# @cubos/agent-sdk 中文文档

规范地址：https://npmcn.com/package/%40cubos/agent-sdk/

版本：0\.0\.1174337
npm 元数据更新时间：2026-11-07T11:20:39.518Z
中文译文更新时间：2026-11-11T04:24:23.492Z
软件包许可：SEE LICENSE IN LICENSE

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40cubos/agent-sdk
原文对照：https://npmcn.com/package/%40cubos/agent-sdk/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.2 MB，34 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2026-09-08T15:04:50.338Z
- 最新版本发布：2026-11-07T11:20:39.244Z
- 近 30 天下载量：11731（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：113（近 12 个月 113 个）

## 中文简介

Cubos代理谈话的客户端 API. 到处都有跑步.

## 安装

```sh
npm install @cubos/agent-sdk
```

也可以使用：`pnpm add @cubos/agent-sdk`、`yarn add @cubos/agent-sdk`、`bun add @cubos/agent-sdk`

## README

# @cubos/代理服务器

库博斯特工的客户端 两个表面: **对话**,用于您的最终用户应用程序 **管理员**为了你的后端.

零从属. 仅使用 `fetch`, (中文(简体) ), `ReadableStream` 财务报告和审定财务报表 `AbortController`,所以同样的代码运行在浏览器,节点 \\ 18, Bun, Deno和Cloudflare工人.

```sh
bun add @cubos/agent-sdk    # or npm / pnpm / yarn
```

## 代号: 由您的后端铸造, 绝不在客户端中

一个Cubos Agent api key是全租户,而且 **绝对不能到达浏览器**您的后端将它切换成一个短命的符号, 范围可限制给一个用户 :

```ts
import { createAdminClient } from "@cubos/agent-sdk";

// On YOUR server.
const admin = createAdminClient({ baseUrl, apiKey: process.env.CUBOS_API_KEY! });

const { token, expires_at } = await admin.tenant("acme").userTokens.create({
  user_id: session.userId, // from YOUR auth, never from the client
  agent_slugs: ["support"],
  ttl_seconds: 900, // optional; defaults to 3600
});
```

密钥需要 : `user_tokens:write` 赠款。 由此而来的信使只到达了对话的终点,只有该用户自己的对话——无法从客户端扩大它.

`agent_slugs` 列出该符号可能打开对话的代理. `ttl_seconds` 默认为3600,上限为86400. **没有撤销列表**:更喜欢最短的TTL你的应用可以舒适地刷新. 封锁用户( E)`users.block`)在下次请求时,其令牌无效.

## 对话

```ts
import { createUserClient } from "@cubos/agent-sdk";

const client = createUserClient({
  baseUrl: "https://agent.acme.com",
  // Called per request — cache it yourself. On forceRefresh, return a freshly
  // minted token (the SDK only asks for that after a 401).
  getToken: async ({ forceRefresh }) => fetchTokenFromMyBackend({ forceRefresh }),
});

const conversation = await client.createConversation({ agentSlug: "support" });
await client.sendMessage(conversation.id, "My invoice was charged twice");

const subscription = client.subscribe(conversation.id, {
  onMessage: (m) => console.log(m.role, m.content),
  onActivity: (a) => setTyping(a.isProcessing || a.hasPendingTurn),
  onTodos: (todos) => setPlan(todos),
});

subscription.close();
```

如果你已经持有一个符号——一个脚本,一个测试——直接通过作为 `token: "…"` 而不是写一个结束。 静态符号不能被更新, 因此它最终会永远过期; 使用 `getToken` 对于任何真实的东西.

`subscribe` 回填连接上的历史,然后从任何下降后的确切点恢复,所以它提供的是整个对话——不需要将其与 `listMessages`,用于向后呼回漫长的历史.

### 告诉探员屏幕上的内容

```ts
await client.setContext(conversationId, `screen: customers\nfilters: status=active`);
```

只要屏幕动了就叫它——它不写事件,也不开始转弯,所以它 在每次航行,甚至是中转时,都是安全的。 服务器向代理显示 下次一转看对话, 只有当它改变,所以 重复一个不变的上下文并不花费什么.

这是原始路线. `@cubos/agent-sdk-react`是因为 `useConversation({ context })` - 浏览11页 输入任何内容之前的屏幕会发出一个请求,而不是十一个.

应用程序所能达到的替代方案是将上下文前缀在用户的上 留言。 将它花在每一个消息, 把它放在记录,除非每一个 使路径剥出它, 并且只为谁拥有作曲家.

`onActivity` 在同一条溪流上,订单是您可以基于的保证:一个框架说结束的转弯在结束它的答复之前从未交付. 关闭上面的打字指示器, 答案已经掌握在手了 。 (它过去来自对话-元数据流,第二个连接,没有针对日志的订单,它们之间的间隙——一毫秒在本地插座上,几秒通过一个代理——显示一个完全的转折,里面什么都没有. )

对于一个错误交易的长篇对话:每一次重联重播整个日志. 通过一个光标, 溪流从那里开始, 留给历史 :

```ts
const page = await client.listMessagesPage(id, { limit: 50 });
setMessages(page.messages);

// Only what is newer than the page. Nothing is lost in between: the server
// catches up everything past the cursor before going live.
client.subscribe(id, { onMessage: append }, { since: page.latestChangeSeq ?? undefined });

// Scrolled to the top — the page before this one.
if (page.hasOlder) {
  const older = await client.listMessagesPage(id, { before: page.oldestSeq, limit: 50 });
  prepend(older.messages);
}
```

`@cubos/agent-sdk-react` 电线完全接通 `loadOlder` 编号 : `hasOlder`。 。 。 .

### 日志本身,当谈话不够时

对话表面故意隐藏事件日志: 一个聊天抽取消息, 没有 `llm_call` 作行相会. 但隐藏并不等于隐瞒 订阅同时携带,需要日志的应用程序从同一处获得 地点 :

```ts
const page = await client.listEventsPage(id, { limit: 50 });

client.subscribe(
  id,
  {
    onEvent: (e) => append(e), // every row, in stream order
    onMessage: (m) => draw(m), // …and what it became, if you want both
  },
  { since: page.latestChangeSeq ?? undefined },
);
```

一个连接,一个光标,一个赶上. `onEvent` 预测前的火灾 看相框,所以一个应用 摄取两者都不必调和.

不同的是承诺,而不是进入. `Message | Todo | Activity` 实值 - 保管并留在内部; `ConversationEvent` 这是 服务器自有的 DTO , 当服务器运行时移动—— 相同的交易 REST API 已经提供,因为这些行是 它返回。 帮助它建立一个 操作控制台、 审计视图或活动树; 停留在 `Message` 用于建设 一个聊天.

`@cubos/agent-sdk-react` 把它接进 `useConversationEvents`。 。 。 .

### 重新打开是免费的

已经上载的谈话被保存在一个有界限的记忆中,所以打开一个 第二次没有要求的油漆:

```ts
const start = await client.loadHistory(id); // start.fromCache on a repeat
client.subscribe(
  id,
  { onMessage: append, onCursor: (seq) => (cursor = seq) },
  { since: start.latestChangeSeq ?? undefined },
);

// Hand the current state back whenever it moves; the next open resumes from here.
await client.saveHistory(id, { messages, oldestSeq, latestChangeSeq: cursor, hasOlder });
```

**击打不是呆子.** 服务器的事件日志从未重写一行 `content`, (中文(简体) ), `seq` 或者说 `type`,以及每个确实发生的突变——一个暂定事件是: 合并或丢弃的交货收据——通过触发器 发行新版本 `change_seq`。因此订阅缓存光标重放 在应用程序离开时 究竟有什么变化 插入和更新都一样.

过 `cache: null` 关掉它, `cacheMessageLimit` 以改变多少 每次谈话都会保留(默认情况下300个消息;旧信件从 缓存,不是服务器,然后通过呼号返回。 为了活过重载, 执行 `ConversationCache` 超过索引数据库、 AsyncStorage 或 SQLite :

```ts
interface ConversationCache {
  read(key: string): Promise<CachedConversation | null>;
  write(key: string, entry: CachedConversation): Promise<void>;
  clear(key?: string): Promise<void>;
}
```

钥匙已经送到租户和用户的手中——一个持续储存的商店由 设备上的每个会话,两个用户不能互相读取对方的信件 离开它。 视之为不透明. 一个拒绝的商店被当成一个小姐,所以 腐败的缓存会降解为网络,而不是打破聊天.

租户通过象征性方式解决 `GET /me` 在第一个呼叫。 过 `tenant` 在跳过往返的选项中.

### 倾斜

当您想要所有的东西并且不愿管理光标时 :

```ts
for await (const conversation of client.iterateConversations()) { … }

// The 20 most recent messages, newest first.
const recent = [];
for await (const message of client.iterateMessages(id)) {
  recent.push(message);
  if (recent.length === 20) break;
}
```

突出不会获取下一页 .

`pageSize` 计数 **事件**,而不是消息:日志还持有代理工具的呼叫和簿记,因此一个页面可以持有比您要求的更少的信息. 执行者对此负责;一页没有消息,不会结束行走.

### 反应部分

您的应用程序可以让代理回答 **** 组成部分——价格图,a 取出取枣者,取出定单牌来取取取——而不是单独打分. 代理人可能使用的东西 来自 **组件库**a. 管理API的操作作者; 您的应用程序允许打开屏幕可以绘制的 :

```ts
const conversation = await client.createConversation({
  agentSlug: "support",
  componentLibraries: ["support-widgets"],
});
```

`setComponentLibraries(id, slugs)` 随时替换列表 — 发送 完成 ; 空列表将代理人带回平面标记下。 变化 从代理的下一个回合.

分出即是信相界. 构成部分摘要、说明和 向模型显示示例 **逐字记录**,所以写作是快速的 内容和需要一个 api key; 启用库只是线程, 这就是为什么 这个客户端 持有一个短命的最终用户标志 能够做到这一点, 仅此而已.

授权呼叫及其 `list` 对应回复 `tags`,每个 包含启用的库决心,按照代理查看的顺序 已索引。 对照您可以实际渲染的内容检查: 没有渲染器的标记是 一个阻隔 代理将乐于写 和没有画.

每条代理信息都会被分割成 `blocks`,已解析道具 并对照图书馆的计划:

```tsx
{
  message.blocks?.map((block, i) =>
    block.type === "markdown" ? (
      <Markdown key={i}>{block.text}</Markdown>
    ) : (
      <MyComponent key={i} tag={block.tag} {...block.props} />
    ),
  );
}
```

一个组成部分是: **总是自我封闭** 没有 `<Tag>…</Tag>`. 无论如何 应该用道具来显示 周围的标记会留在外面 自有块 :

```jsonc
// leia **isto**
// <PriceChart symbol="PETR4" points={[1,2]} />
[
  { "type": "markdown", "text": "leia **isto**" },
  { "type": "component", "tag": "PriceChart", "props": { "symbol": "PETR4", "points": [1, 2] } }
]
```

值得我们知道:

-   **`blocks` 总是在代理信息.** 无组件的答复是 一个 `markdown` 块,所以你写一个渲染路径,而不是两个.
-   **服务器执行合同.** 没有启用的图书馆服务组件, HTML,或者在信件被写入之前,错过了"计划"的道具被拒绝—— 代理人得到错误并重写。 验证是完整的JSON Schema,不是 浅色类型检查,拒绝时指定失败的路径 (单位:千美元)`points.0.v: "muito" is not of type "number"`),所以代理可以修复.
-   **`content` 仍然是整个消息** 如探员所写,如果你愿意 不如自己来.
-   **块是读取时产生的,从不存储.** 丢弃库不会 重写旧消息,所以保留一个渲染器 任何记录可能仍然 包含.
-   每次对话都启用库, 只有当您的应用程序是渲染器时才启用 . 在频道支持的对话(WhatsApp, Telegram)上进行 外部频道则在频道上声明其库.
-   交互是您的: 代理输出块, 您的组件处理 点击,不管它意味着什么 `sendMessage`。 。 。 .

### 现场对话列表

```ts
const page = await client.listConversations();
client.subscribeToConversations({ onConversation: (c) => upsertAndResort(c) });
```

每当对话活动取得进展时,就再次发出,这就是保持 一张没有投票的聊天列表.

### 图像

```ts
await client.sendImage(id, file, { caption: "is this the right charge?" });

// Up to 10 in one message, so the agent reasons over the set rather than one
// turn per picture. A label names an image for the model, which lets it answer
// about "the receipt" instead of "the second image".
await client.sendImages(
  id,
  [
    { image: receipt, label: "receipt" },
    { image: statement, label: "statement" },
  ],
  { caption: "compare these" },
);
```

png,jpeg,webp和gif,每个可达10MB. 特工在它的模型有视觉时就从原生地看待画面,否则就是从特工倒置的视觉模型中描述出来——无论是哪种方式,都花费一转并作为一个预算.

一个图像像一个 `Message` 谁 `attachments` 说明媒体及其 `content` 是标题. **没有标题的发送是正常的,所以渲染 `attachments` 即使当 `content` 为空.** 字节又回来了 `Blob`,而不是URL,因为信使在信笺中行走——a `<img src>` 指向航道将到达无证:

```ts
const blob = await client.fetchAttachment(conversationId, message.id, attachment.id);
img.src = URL.createObjectURL(blob); // revoke it when the element goes away
```

当反向视觉模型描述一幅图片时,该描述不包含在 是模特在自言自语 屏幕,而不是用户发送的信息.

### 语音信息

```ts
await client.sendAudio(conversation.id, blobFromMediaRecorder);
```

片段由代理商的STT模型在转弯运行前进行转录. 它像一个 `Message` 携带一个 `audio` 附件,与 `content` 持有抄本——直到STT完成为空,这是客户端显示玩家尚未下方文本的窗口. 回放的字节来自 `fetchAttachment` 我也一样.

### 客户工具

没有服务器侧执行的工具:代理机呼叫一个, **轮到它了 暂停**,你的应用程序答案.

```ts
const session = await client.serveClientTools(id, {
  tools: {
    get_location: {
      description: "Where the user is, as their device reports it.",
      inputSchema: { type: "object", properties: {} },
      readOnlyHint: true,
      handler: async () => ({ city: "Fortaleza" }),
    },
  },
});
```

宣布节目,观看谈话 打电话,租赁每一个这样两个 选项卡不运行两次,在慢处理者工作时延长租约,并重试 效果 POST — 一旦副作用出现, 答案就值得多次尝试 已经发生了.

它打开自己的活动流来进行监视. **如果您已经订阅 谈话**,不要支付第二个连接:

```ts
const session = await client.serveClientTools(id, { tools, watch: false });
client.subscribe(id, {
  onOpen: () => session.poke(), // catches up after a (re)connect
  onClientToolCall: () => session.poke(), // and on every new call
  onMessage: append,
});
```

`setClientTools` 宣告时不运行任何内容——在 第一个信息,因为一个工具 从来没有告诉过代理 无法调用在 转过来. `@cubos/agent-sdk-react` 做这一切为你.

## 对话表面

|  |  |
| --- | --- |
| `me()` 编号 : `refreshIdentity()` | 信使的用户、租户和代理人 |
| `listConversations()` 编号 : `iterateConversations()` | 带有光标的一页或全部 |
| `createConversation()` | 打开与代理人的对话 |
| `getConversation()` 编号 : `renameConversation()` 编号 : `archiveConversation()` |  |
| `listMessages()` 编号 : `iterateMessages()` | 历史 |
| `listMessagesPage()` | 历史加光标以呼回和指针 `subscribe({ since })` |
| `loadHistory()` 编号 : `saveHistory()` 编号 : `forgetHistory()` | 对话缓存 |
| `sendMessage()` 编号 : `sendAudio()` | 用户消息,输入或已发 |
| `sendImage()` 编号 : `sendImages()` | 一个图像,或者一个信息中最多10个 |
| `setClientTools()` 编号 : `serveClientTools()` | 声明您的功能,并运行调用 |
| `setComponentLibraries()` 编号 : `listComponentLibraries()` | 代理服务器可能使用的组件库 |
| `fetchAttachment()` | 作为附件的字节 `Blob` |
| `subscribe()` 编号 : `subscribeToConversations()` | 状态 |

他们都接受 `AbortSignal`。 。 。 .

暴露类型`Conversation`, (中文(简体) ), `Message`, (中文(简体) ), `Attachment`, (中文(简体) ), `Block`, (中文(简体) ), `EnabledComponents`, (中文(简体) ), `Todo`, (中文(简体) ), `Activity`)是一个被管理 表面, 窄于服务器的 DTO : 事件日志有数十种类型 给操作员仪表板 将这里的仪表板钉上 内部重塑一个突破性的变化.

## 管理员

以 api key 认证的全 API, 按资源和范围命名 每个租户:

```ts
import { createAdminClient } from "@cubos/agent-sdk";

const admin = createAdminClient({ baseUrl: "https://agent.acme.com", apiKey });

await admin.tenant("acme").agents.list();
await admin.tenant("acme").users.block(userId);
await admin.tenants.list();
await admin.apiKeys.rotate(keyId);
```

类型来自服务器的 OpenAPI 文档(`Schemas["Agent"]`,(......), 在每个路由更改中重新生成——所以客户端自行跟踪服务器. `admin.raw` 是尚未被包裹的路径的逃生舱口.

**仅限服务器侧.** 一个 api key 是全租户的: 它能读写 每一个用户在租户的对话。 使用 `createUserClient` 在浏览器中.

## 错误

SDK抛出的一切 `AgentError`,所以一个 `catch` 涵盖两个 重要案例:

```ts
try {
  await admin.tenant("acme").agents.get("support");
} catch (err) {
  if (err instanceof AgentNetworkError) {
    // Never reached the server: wrong URL, server down, CORS, or the timeout
    // (`err.timedOut`). `err.cause` holds the original failure.
  } else if (err instanceof AgentApiError) {
    // The server answered, and refused.
    if (err.isNotFound) …
    if (err.isConflict) …          // 409
    if (err.isAuthError) …         // 401/403
    if (err.isRetryable) …         // 429 or 5xx
  }
}
```

`AgentApiError` 携带 `status`, (中文(简体) ), `requestId` 和逐字记录 `body` (单位:千美元)`err.json()` 来分析它. `AgentConfigError` 由客户端构建而来 而不是打个电话——通常是 `baseUrl` 没有 `http://`。 。 。 .

每个请求都有30s的超时;用 `timeoutMs`,或 `0` 以禁用. 溪流是豁免的,因为保持开放是重点.

### 费率限额

服务器预算每个实例的最终用户令牌:大约20个代理转出,300个 其他电话每分钟, 加上一个同时流的上限。 拒绝是429 `Retry-After`,以及 **sDK处理它** 它等待广告的间隔 默认情况下重放请求两次. 429表示拒绝,而不是半申请,所以 重放一个 `POST` 这是安全的.

它放弃了,当复试用完,没有 `Retry-After` 或等待超过20秒时——阻断呼叫者 几分钟比现在告诉他们还糟. `maxRetries: 0` 选择退出。 中止期间 等待会取消重试而不是完成它.

api keys 豁免这一切.

瞬间流失败不结束订阅: SDK 重新连接 指数反转并报告通过 `onError`,用于伐木或a "重联"提示.

三个看起来像是沉默的失败得到处理而不是报告。 A 类 **401 (中文(简体) )** 购买一个立即重新连接的硬币刷新——短寿命 在流出超过预期的流水中过期的,不是 错误的配置——只有第二次拒绝才是最终的. A 类 **429个** ——每个用户 在打开的溪流上盖, 一个标签太多到达了—— 等待服务器的 `Retry-After` 并重新连接, 因为盖子清除了任何标签关闭的时刻。 A 类 停止发送的连接 **任何东西**中保留保留备注 放弃并重新开放 `idleTimeoutMs` (30s,是服务器的两倍) 间隔 : 否则半开插座让读者永远等待 没有反应错误 .

一条溪流必须 **在它被信任之前证明自己**。服务器发送一个 每个连接上都有一个框架,所以直到一个连接发出一个——并且再次 只要一结束.. `subscribe` 投票记录相同 每3次通过 `after` 光标, 和对话排行。 在一个健康的网络上,框架土地 第一次民意测验的2分之多 从未有过民意测验 在一个网络上 代理持有一个 `text/event-stream` 回复直到结束( 公司 TLS) 检查,一些抗病毒药物——克罗姆与说话的宿主谈判HTTP/1.1 h2是事实,每一个连接 静静地静静,直到有东西切断它 答复以前只在重新装入一页后才出现;现在它在3s之内出现. 这个 相同的规则涵盖了一个下注和后面的再连接之间的间隔. 一个 光标为两条路径服务, 因此没有发送两次 .

最终结果是什么? 4xx幸存下来, 被删除的对话.. 关闭对善和火灾的订阅 **`onFatal`** (单位:千美元)`onError` 见之也. 这是一个UI必须浮出水面: 一切都会变成,没有东西会 永远到来.

## 还在包裹里

-   **`@cubos/agent-sdk/sse`** ——Server-Sent Eventions的阅读器本身(重接,光标重放,倒放),为消费者直接与API交谈,只想要那块.
-   **`@cubos/agent-sdk-react`** ——与回放钩相同的对话表面,在单独的包中. 胡克:它使无所事事,所以聊天UI保留你的.
-   **`@cubos/agent-sdk-react-dom`** ——少数通用性足以共享的组件,从代理人的平分开始. 只有Web,它本身也风格.

## 常见问题

### @cubos/agent-sdk 是什么？

Cubos代理谈话的客户端 API\. 到处都有跑步\.

### 如何安装 @cubos/agent-sdk？

在项目目录运行 npm install @cubos/agent-sdk，或使用 pnpm add / yarn add / bun add。

### @cubos/agent-sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @cubos/agent-sdk 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @cubos/agent-sdk 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=18。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @cubos/agent-sdk 可以商用吗？

@cubos/agent-sdk 使用 SEE LICENSE IN LICENSE 许可。许可条款写在包内的独立文件中，请阅读该文件确认可否商用。以上为概括，不构成法律意见，请以许可原文为准。

### @cubos/agent-sdk 的下载量有多少？

根据 npm 官方下载统计，@cubos/agent-sdk 在 2026/10/11 – 2026/11/09 这 30 天内被下载 11,731 次，日均约 391 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @cubos/agent-sdk 还在维护吗？

最新版本 0\.0\.1174337 发布于 2026/11/07（3 天前）；至今共发布 113 个版本，其中过去 12 个月发布了 113 个；首次发布于 2026/09/08。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

