ctx.sessions 让小工具创建并驱动自己的会话。典型用途:外部平台 Bot、后台任务处理、多 Agent 编排。
隔离边界:小工具只能访问自己创建的 Session,读不到用户的普通对话,也碰不到别的小工具的会话。
完整回路
创建会话
const session = await ctx.sessions.create({
containerId: 'inbox', // 容器投放
title: 'Chat with Alice',
activity: 'interactive', // 或 'background'
permissionMode: 'ask', // interactive 默认 ask;background 默认 acceptCalls
initialMessage: {
text: 'Hello from the bot.',
idempotencyKey: 'welcome-alice-2026-01-01',
},
});
投放位置二选一,不能混用:
containerId— 投放到自己的容器(小工具默认选择)。space: { spaceId }— 投放到普通 Space 会话列表,看起来像用户自己建的,所有权仍属小工具。
activity 的实际差别:background 会话完成或等待时不弹系统通知,只在容器入口显示红点提醒,权限默认 acceptCalls,适合无人值守任务;interactive 是正常聊天会话。
context: 'caller' 只能在 Agent 工具的 execute() 内使用,让新会话继承调用方的 cwd、模型、策略和 Space 上下文,适合从当前对话 fork 一条支线。在工具调用之外使用会抛错。
带 initialMessage 的 create() 失败时不会留下幽灵会话。
发消息
send() 在单个会话内严格 FIFO:
const receipt = await ctx.sessions.send(session.sessionId, {
text: 'What is the weather?',
idempotencyKey: 'msg-123', // 必填
});
if (receipt.state === 'rejected') {
// 队列满,等 receipt.retryAfterMs 后重试,不要立刻重灌
}
idempotencyKey 必填,最长 512 字符。应该用外部来源的稳定消息 ID,不要用随机 UUID——重复发送时会返回原始回执而不是新建一轮。
限制:文本 10 万字符;附件每条 10 个、单个 20MB、总计 20MB。
拿结果:三个 API 各司其职
| API | 用途 |
|---|---|
waitForTurn(sessionId, turnId) | 当前操作需要这一轮的确切最终结果,请求-响应式编排 |
onDidReceiveEvent(cb) | 跨多个会话、多轮的长期观察,Bot 场景 |
listEvents({ sessionId, after }) | 只用于历史回溯和断线恢复 |
const result = await ctx.sessions.waitForTurn(
sessionId, receipt.turnId, { timeoutMs: 60_000 },
);
if (result.state === 'completed') console.log(result.outputText);
if (result.state === 'failed') console.error(result.code);
if (result.state === 'timeout') console.log('仍在运行');
超时默认 60 秒,钳制在 1–600 秒。超时只结束这次等待,不会取消那一轮执行。
事件类型中 assistant.delta 是实时流式片段,不持久化;断线恢复必须依赖 assistant.message 或 turn.completed。事件保留 7 天,每个小工具上限 10000 条。
禁止 sleep + 轮询 listEvents() 的写法,该用 waitForTurn()。
编排范式:Planner → Worker → Writer
多 Agent 编排的标准形态:主工具在一次调用里拆解任务、并行开子会话、等待全部结果、汇总输出。
const results = await Promise.all(tasks.map(async (task, i) => {
const s = await ctx.sessions.create({
containerId: 'workers',
activity: 'background',
context: 'caller',
initialMessage: { text: task, idempotencyKey: `job-${jobId}-${i}` },
});
const r = await ctx.sessions.waitForTurn(s.sessionId, s.turnId, { timeoutMs: 300_000 });
return r.state === 'completed' ? r.outputText : `任务 ${i} 失败`;
}));
用 background 让子会话不打扰用户,用户仍可在容器里点进去观察每个子会话的完整过程。
配额
| 限制 | 数值 |
|---|---|
| 单会话未完成轮次 | 20 |
| 单小工具未完成轮次 | 200 |
| 队列满重试间隔 | 1000 ms |
| 事件保留数 / 时长 | 10000 条 / 7 天 |
常见错误
- 漏声明
permissions.sessions或contributes.sessionContainers。 - 传了未在 manifest 声明的
containerId。 - 省略
idempotencyKey,外部 webhook 每次重投都新建一轮。 - 在工具调用之外用
context: 'caller'。 - 以为
assistant.delta会持久化。 - 不处理
send()的rejected队列满状态。 - 每条外部消息都新建会话,而不是一个联系人复用一个会话。