快速开始
提供获取 Nuva 用户 token 的函数,再使用 createNuva() 初始化客户端。
import { createNuva, Message } from "@nuva/sdk";
const nuva = createNuva({
getAccessToken: getNuvaAccessToken,
});
const main = await nuva.sessions.getMain();
const agent = nuva.agent({ sessionId: main.id });
const receipt = await agent.send(Message.text("开始广告创作,请先列出需要的产品资料。"));getNuvaAccessToken(): Promise<string> 由应用提供,返回有效的 Nuva 用户 token;获取失败时抛出错误。
| 配置项 | 类型 | 说明 |
|---|---|---|
getAccessToken | () => Promise<string> | 返回当前登录用户的 Nuva 访问 token。获取失败时抛出错误。 |
每个登录用户复用一个客户端实例。退出或切换账号时调用 nuva.close(),然后为新用户创建实例。服务凭证仅配置在后端。
业务对象 API
按业务类型读取和操作数据。每个对象拥有独立 ID;方法返回对应的 Product、Script、Directing Plan 等业务类型。
| SDK 入口 | 对象 | 内容 |
|---|---|---|
nuva.products | Product | 产品 brief、事实、来源和制作要求。 |
nuva.recipes | Recipe | 可复用的创作指导及其版本。 |
nuva.scripts | Script | 创意、叙事、台词和画面意图。 |
nuva.directingPlans | Directing Plan | 落实到具体引用的角色和素材,以及细化的镜头安排。 |
nuva.prompts | Prompt | 实际提交给生成模型的提示文本及其引用关系。 |
nuva.assets | Asset | 上传或生成的图片、视频、音频,以及位置、元数据和来源。 |
nuva.ads | Ad | 一条成片及其完整制作溯源的容器;制作中可尚未有成片。 |
nuva.batches | Batch | 多个 Ad 的集合,用于一起查看和推进。 |
nuva.experiments | Experiment | 实验或探索目标、广告成员、观察、证据和结论。 |
nuva.adMappings | Ad Mapping | 实际投放视频与平台、账户及广告标识的对应关系。 |
读取对象
const script = await nuva.scripts.get(scriptId);
const plan = await nuva.directingPlans.get(planId);
const prompt = await nuva.prompts.get(promptId);
const ad = await nuva.ads.get(adId);
const batch = await nuva.batches.get(batchId);get(id) 读取当前版本。需要读取消息、审核或制作流程中引用的确定版本时,传入 revision:
const script = await nuva.scripts.get(scriptRef.id, {
revision: scriptRef.revision,
});
await agent.send(
Message.create().mention(script).text("修改开场,保留其他内容。")
);消息和业务对象的引用仍包含类型、ID 和版本。读取时使用相应类型的客户端;读取本身不触发 Agent 或生成任务。
制作关系
Script → Directing Plan → 多个 Prompt → 视频素材 → 成片
| 对象 | 在流程中的作用 |
|---|---|
Script | 表达创作意图与内容,可用于不同的导演计划。 |
Directing Plan | 基于脚本,将角色、产品等概念落实为具体引用,并补充动作、构图、运镜和时间安排。 |
Prompt | 由导演计划形成,保存实际提交给模型的提示文本。多个 Prompt 可覆盖不同镜头;同一 Prompt 可生成多个候选素材。 |
Ad | 关联一条选定成片及其脚本、导演计划、提示词、素材和制作来源。 |
Batch | 组织多个 Ad;不替代 Ad 自身的制作关系。 |
Script、Directing Plan 和 Prompt 分别拥有 ID。引用确定的上游版本,保留实际采用的输入。修改脚本后,已有导演计划、提示词和素材仍可追溯到原版本。
独立生成操作可以接收直接输入。有已保存的来源时使用真实引用;没有时保存实际输入。
实验与投放
experiments 保存目标、广告成员、观察和结论。分析可以比较跨实验的广告,也可以从直接选定的广告开始;保留实际使用的证据和时间范围。
广告发布后,通过 adMappings 回传实际视频与平台广告的对应关系,将投放数据连接到正确的 Ad 和制作来源。
产品记录
产品表单通过 nuva.products 直接读写资料,无需先建立会话。
| 方法 | 输入 | 返回值 |
|---|---|---|
products.create(input) | 产品表单数据。 | 已保存的产品记录。 |
products.get(id, options?) | 产品 ID;可通过 options.revision 指定版本。 | 对应版本的 Product。 |
products.update({ base, data }) | 读取到的产品记录 base,以及编辑后的产品数据 data。 | 保存后的产品新版本。 |
const product = await nuva.products.create(productForm);
const current = await nuva.products.get(product.ref.id);
const saved = await nuva.products.update({
base: current,
data: editedBrief,
});产品资料包括 brief、事实及其来源、要求和输入素材。保持未修改事实的来源信息。保存成功后使用返回的记录和新版本。
base.ref.revision 是写入前提。记录已被其他操作更新时返回 REVISION_CONFLICT;保留编辑内容,读取新版本后重新确认修改。
脚本记录与生成
| 方法 | 输入 | 返回值 |
|---|---|---|
scripts.get(id, options?) | 脚本 ID;可通过 options.revision 指定版本。 | 对应版本的 Script。 |
scripts.update({ base, data }) | 读取到的脚本记录 base,以及编辑后的脚本内容 data。 | 保存后的脚本新版本。 |
scripts.generate(request) | 脚本生成请求。 | 新生成并保存的脚本记录。 |
直接修改
const script = await nuva.scripts.get(scriptId);
const revised = await nuva.scripts.update({
base: script,
data: editedScript,
});update() 保存编辑内容并校验版本与可编辑字段;它不会调用模型重新创作。历史版本保持可查询。已有导演计划、提示词和视频不会被这次保存自动改写。
生成后交给 Agent 修改
const script = await nuva.scripts.generate(scriptRequest);
await agent.send(
Message.create()
.mention(script)
.text("把开场改得更自然,保留其他内容。")
);每次新的生成请求返回独立结果。Agent 根据脚本引用读取其内容和已保存的输入。直接保存产品或脚本后,相关会话在继续工作时获得记录更新信息,无需补发一条聊天消息来同步数据。
会话状态与订阅
agent.subscribe(listener) 立即提供当前会话状态,并在消息或状态变化时再次调用 listener。返回值是取消订阅函数。
const unsubscribe = agent.subscribe((state) => {
if (state.status === "loading") return;
if (state.status === "error") {
console.error(state.error);
return;
}
console.log(state.status, state.messages.items);
});
// 不再需要接收更新时调用 unsubscribe()。每次回调收到的 state 包含以下字段:
| 字段 | 值 / 类型 | 说明 |
|---|---|---|
status | loading | error | idle | running | waiting_for_response | 初次加载使用 loading/error。加载完成后直接显示 Agent 执行状态。 |
session | Session | 会话 ID、类型、标题及所属用户;完成加载后可用。 |
messages | Page<MessageView> | 已加载的消息及历史分页游标;完成加载后可用。 |
callIds | readonly string[] | waiting_for_response 状态下正在等待的调用 ID。 |
error | NuvaErrorInfo | 初次加载失败的原因,或已有数据的同步错误。 |
重连或加载更早消息时,保留已有内容与最后已知的执行状态。同步失败通过 error 报告,恢复同步后清除。send/respond 的操作错误由各自的 Promise 返回。
取消订阅只停止本地更新,已开始的服务端工作继续运行。
消息与引用
发送消息
await agent.send(Message.text("继续修改第二条脚本。"));
await agent.send(
Message.create()
.text("修改 ")
.mention(script)
.text(" 的开场,保留其他内容。")
);
await agent.send(
Message.create().replyTo(existingMessage).text("使用这一版。")
);| 方法 | 参数 | 用途 |
|---|---|---|
Message.text() | string | 创建纯文本消息。 |
Message.create().text() | string | 追加文字。 |
.mention() | RecordLink | 在文字中插入记录引用。 |
.attach() | RecordLink | 添加消息附件。 |
.quote() | RecordLink | MessageView, string | 引用记录或消息中的选定文字。 |
.replyTo() | MessageView | 指定被回复的消息。 |
构建方法返回新的消息值。send() 成功后返回已保存消息的引用;Agent 回复通过消息订阅到达。
{
"requestId": "request_21",
"message": {
"sessionId": "work_product_a",
"messageId": "message_12"
}
}记录引用
将业务 API 返回的对象传入 mention() 或 attach()。revision 指定记录版本。
{
"ref": { "kind": "script", "id": "script_1", "revision": "1" },
"label": "脚本 1"
}接收消息
{
"ref": { "sessionId": "work_product_a", "messageId": "message_10" },
"role": "assistant",
"createdAt": "2026-10-12T18:00:00Z",
"state": "complete",
"attachments": [],
"content": [
{ "id": "part_text", "type": "text", "format": "markdown", "text": "脚本已生成。" },
{
"id": "part_script_1",
"type": "resource",
"record": {
"ref": { "kind": "script", "id": "script_1", "revision": "1" },
"label": "脚本 1"
}
}
]
}| 字段 | 说明 |
|---|---|
ref | 来源会话 sessionId 与消息 messageId。转发消息保留原引用。 |
role | user 或 assistant。用户消息的 metadata.user 提供发送者身份。 |
content | 有序内容列表:text、mention、resource、quote、dynamic-tool 或 data-host-call。 |
state | streaming | complete | interrupted | failed。failed 时包含 error。 |
attachments | 消息附件列表。 |
replyTo | 可选的被回复消息引用。 |
按 SDK 返回的顺序显示 messages.items。流式更新会合并到原消息;无需自行拼接网络片段。
客户端工具
问答、许可确认和内容审核通过消息中的 dynamic-tool 交互。使用 toolName 选择处理函数,使用 toolCallId 关联结果。
{
"type": "dynamic-tool",
"toolName": "nuva.review",
"toolCallId": "call_17",
"state": "input-available",
"input": {
"prompt": "请审核这条脚本。批准后将继续细化导演计划。",
"records": [{ "kind": "script", "id": "script_1", "revision": "1" }]
}
}| 状态 | 处理方式 |
|---|---|
input-available | 读取完整 input,显示交互内容并等待用户回应。 |
output-available | 从 output 读取已保存的结果。 |
output-error | 从 errorText 读取失败说明。 |
提交结果
将收到的工具调用和结果传入 respond()。结果必须符合该工具的 outputSchema。
// part 是消息中 state 为 input-available 的工具调用。
const receipt = await agent.respond(part, {
decision: "reject",
text: "先展示产品,再讲困扰。",
});nuva.review 使用 decision: "approve" | "reject" 和 text。无补充说明时传入空字符串。方法在服务端保存结果后返回:
{
"requestId": "request_22",
"call": {
"type": "dynamic-tool",
"toolName": "nuva.review",
"toolCallId": "call_17",
"state": "output-available",
"input": {
"prompt": "请审核这条脚本。批准后将继续细化导演计划。",
"records": [{ "kind": "script", "id": "script_1", "revision": "1" }]
},
"output": { "decision": "reject", "text": "先展示产品,再讲困扰。" }
}
}后续 Agent 回复继续通过消息订阅到达。仍在等待回应的调用可从历史消息或转发消息中提交。
提交执行错误
无法取得有效结果时调用 fail()。用户拒绝审核应作为正常结果传给 respond()。
await agent.fail(part, {
code: "EDITOR_UNAVAILABLE",
message: "脚本编辑器加载失败。",
});返回值包含 requestId 和状态为 output-error 的 call。
Host call
通过 onHostCall() 订阅已注册的网站交互。将 toolName 映射到应用的页面、编辑器或其他操作。
const unsubscribe = agent.onHostCall((part) => {
if (part.type === "dynamic-tool") {
if (part.state !== "input-available") return;
host.receive(part.toolName, part.input, part);
} else {
host.receive(part.data.toolName, part.data.input, part);
}
});
// 不再处理 host call 时调用 unsubscribe()。host.receive 是应用提供的处理函数。需要结果的 dynamic-tool 使用 respond() 或 fail() 回传结果。
不要求结果的 host call 使用 data-host-call。按其 input 执行应用操作,无需回传:
{
"type": "data-host-call",
"id": "host_19",
"data": {
"toolName": "workspace.showScripts",
"input": {
"records": [{ "kind": "script", "id": "script_1", "revision": "1" }]
}
}
}Host call 同时保存在消息内容中。历史渲染只展示保存的内容;自动执行时按调用 ID 去重。
在服务端授权配置中启用对应 UI skill。前端处理函数使用该 skill 中注册的方法名和输入格式。
方法参考
| 方法 | 返回类型 | 用途 |
|---|---|---|
nuva.sessions.getMain() | Promise<Session> | 读取当前用户的主会话。 |
nuva.sessions.list(query?) | Promise<Page<Session>> | 分页读取可访问会话。 |
nuva.sessions.get(sessionId) | Promise<Session> | 读取指定会话。 |
nuva.sessions.history(sessionId, query?) | Promise<Page<MessageView>> | 读取已保存的消息历史。 |
nuva.sessions.getMessage(ref) | Promise<MessageView> | 按 MessageRef 读取消息。 |
nuva.agent({ sessionId }) | AgentHandle | 取得该会话的操作句柄。 |
agent.send(message, options?) | Promise<SendReceipt> | 保存用户消息。 |
agent.respond(part, output, options?) | Promise<ResponseReceipt> | 保存客户端工具结果。 |
agent.fail(part, failure, options?) | Promise<HostFailureReceipt> | 保存客户端工具执行错误。 |
agent.subscribe(listener) | Unsubscribe | 立即读取当前状态,并接收后续状态更新。 |
agent.onHostCall(listener) | Unsubscribe | 接收客户端工具及 host call。 |
agent.loadEarlierMessages() | Promise<void> | 加载更早消息并更新 messages。 |
agent.listPendingCalls(query?) | Promise<Page<DynamicToolUIPart>> | 读取 input-available 状态的调用,包括尚未加载的历史。 |
agent.getHostCall(id) | Promise<DynamicToolUIPart | NotificationHostCall> | 按 toolCallId 或 host call ID 读取调用。 |
agent.refresh() | Promise<void> | 重新同步状态;不会重发操作。 |
nuva.close() | void | 关闭本地连接并清理客户端状态。 |
query 接受可选的 cursor 和 limit。返回页包含 items 和 nextCursor;nextCursor 为 null 时已到末页。options 接受可选的 requestId。
错误处理
操作失败时 Promise 抛出 NuvaError。读取 code、message、retryable 和可选的 requestId。
| 错误码 | 处理方式 |
|---|---|
UNAUTHENTICATED | 检查 token 和应用登录状态。 |
FORBIDDEN | 检查用户授权范围。 |
NOT_FOUND | 核对会话、消息或记录 ID。 |
INVALID_INPUT | 按输入 Schema 修正参数。 |
REVISION_CONFLICT | 读取最新记录,核对后重新提交。 |
CALL_OBSOLETE | 读取当前有效的调用,重新确认待提交内容。 |
CALL_ALREADY_RESOLVED | 读取该调用已保存的结果。 |
REQUEST_ID_CONFLICT | 检查 requestId 是否被用于不同的输入。 |
RATE_LIMITED | 降低调用频率,按 retryable 决定是否重试。 |
UNSUPPORTED_CONTENT | 检查消息类型和 SDK 版本。 |
CONNECTION_ERROR / SERVICE_ERROR | 保留用户输入,检查接受状态后恢复或重试。 |
命令错误中的 acceptance 为 not_accepted 或 unknown。unknown 表示尚不能确认服务端是否已保存操作。
重试同一操作时复用相同 requestId 和输入。缺省 requestId 由 SDK 生成;发生状态不明的错误时,保留错误中的 requestId。send() 或 respond() 返回成功仅表示该次输入已保存,后续生成结果从消息订阅读取。