SDK 事件
使用一个由你持有的订阅来获取快照并跟踪它的变化。在支持的 daemon 上,建立连接、普通读取和本地目录监听器都不会启动观察。每次观察都会获得一个由服务器签发的新 ID,即使它的过滤条件与另一次观察相同。
跟踪一个 Agent 的状态
const directory = await client.agents.list({
filter: { includeArchived: false },
subscribe: {},
});
directory.subscription.subscribe({
snapshot({ entries }) {
const entry = entries.find(({ agent }) => agent.id === agentId);
console.log(entry?.agent.status);
},
update(message) {
if (message.type !== "agent_update") return;
const update = message.payload;
if (update.kind === "upsert" && update.agent.id === agentId) {
console.log(update.agent.status);
} else if (update.kind === "remove" && update.agentId === agentId) {
console.log("Agent removed from this directory");
}
},
});
// When this view closes:
await directory.subscription.release();
list({ subscribe: {} }) 返回快照、subscriptionId 和 subscription。省略 subscribe 时只返回快照。不要自己提供订阅 ID。订阅会先投递快照再投递更新,重连后会获得新的 ID 和快照。释放订阅不会影响其他观察和底层的 Agent。
client.agents.subscribe() 和 Agent 句柄上的 subscribe() 会为该 API 实例持有的观察添加本地监听器。它们不会请求数据。当多个带过滤条件的视图需要各自的更新时,请使用返回的订阅上的回调。
跟踪时间线事件
const unsubscribe = agent.timeline.subscribe((update) => {
const { event } = update;
if (event.type === "subscription_restored") {
// Live delivery has resumed. Choose whether to fetch missed history.
console.log("Reconnected; events may have been missed");
return;
}
if (event.type === "error") {
console.error("Timeline observation stopped:", event.error);
return;
}
if (event.type === "replacement") {
// Previously fetched history belongs to an old epoch. Fetch the page your UI needs.
void agent.timeline.refetch().then((page) => console.log(page.entries));
return;
}
if (event.type === "timeline" && event.item.type === "assistant_message") {
process.stdout.write(event.item.text);
}
if (event.type === "turn_completed") {
console.log("\nTurn completed");
}
});
在开始需要观察其事件的工作之前,先 await unsubscribe.ready。它会等待初始的实时订阅建立(在只支持广播的主机上则是本地监听器挂载完成);初始历史是一次单独的读取。调用 unsubscribe() 释放需求,或在清理时 await unsubscribe.release()。
重连后,同一个句柄会在后续实时更新之前收到 { agentId, subscriptionId, event: { type: "subscription_restored" } }。这是 SDK 在成员关系确认后(在只支持广播的主机上是本地挂载后)发出的本地通知。订阅会获得一个新的 ID。不会自动获取历史,错过的事件也不会重放。
请为你的使用方选择恢复方式:继续接收实时事件、请求最近一页,或者用你保存的位置调用 timeline.refetch({ direction: "after", cursor: { epoch, seq } })。读取进行期间,实时投递会继续;请按 epoch 和序号缓冲这些事件,或与返回的页面进行对账。需要时,根据 hasNewer 和 endCursor 继续读取后面的页面。实时的 replacement 会使之前的 epoch 失效。显式的历史读取失败时,只会让那次读取 reject,实时订阅仍然有效。
订阅建立失败时会投递 { agentId, event: { type: "error", error } } 并释放该观察。准备好重试时,请建立新的订阅。释放订阅会停止它的回调,并阻止重连后的恢复。
助手消息可能分段到达。需要完整消息时请拼接它们的文本;如果只需要最终回复,请使用 run() 并读取 lastMessage。
一轮的完成由 turn_completed、turn_failed 或 turn_canceled 表示。不要根据 agent_update 转为 idle 来推断一轮已经完成。
获取时间线历史
const page = await agent.timeline.refetch({
direction: "before",
limit: 100,
projection: "projected",
});
for (const entry of page.entries) {
console.log(entry.seqStart, entry.seqEnd, entry.item.type);
}
使用结果中的 startCursor、endCursor、hasOlder 和 hasNewer 翻页,不要自己计算偏移量。
跟踪工作区更新
const directory = await client.workspaces.list({ subscribe: {} });
directory.subscription.subscribe({
snapshot({ entries }) {
console.log(entries);
},
update(message) {
if (message.type === "workspace_update") console.log(message.payload);
},
});
// When this view closes:
await directory.subscription.release();
跟踪 Provider 目录的变化
const unsubscribe = client.providers.subscribe((update) => {
const ready = update.entries.filter((entry) => entry.status === "ready");
console.log(
"Ready providers:",
ready.map((entry) => entry.provider),
);
});
console.log(await client.providers.snapshot());
// When this view closes:
unsubscribe();
Provider 和项目的 subscribe() 调用会请求各自的更新,并在取消订阅时释放该需求。对于初始的项目缓存,请在 await client.projects.list() 期间缓冲更新,拿到快照后再应用它们。
当应用不再需要连接时,调用 client.close()。
旧版 daemon
同样的方法会使用现有的连接和旧的投递行为。旧版的目录订阅共享一个服务器槽位:之后带过滤条件的观察会替换该槽位的过滤条件。句柄有本地 ID 和各自的回调,但独立的服务器端过滤需要更新后的 daemon。释放句柄会解除它的回调;旧版 daemon 可能会继续发送广播。
赞助商
Cloud coding agents
- 成为赞助商