@springbrand/agent-runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +28 -0
- package/src/db/approval.repo.ts +291 -0
- package/src/db/ext-context.repo.ts +34 -0
- package/src/db/index.ts +83 -0
- package/src/db/message-ui.repo.ts +39 -0
- package/src/db/milestone.repo.ts +96 -0
- package/src/db/runtime-event-outbox.repo.ts +89 -0
- package/src/db/schema.ts +164 -0
- package/src/db/settlement.repo.ts +104 -0
- package/src/db/steer.repo.ts +73 -0
- package/src/db/submission.repo.ts +323 -0
- package/src/index.ts +133 -0
- package/src/kernel/approval-lifecycle.ts +552 -0
- package/src/kernel/bindings.ts +898 -0
- package/src/kernel/degradation.ts +15 -0
- package/src/kernel/extensions.ts +108 -0
- package/src/kernel/profile.ts +116 -0
- package/src/kernel/public-contracts.ts +17 -0
- package/src/kernel/receipts.ts +124 -0
- package/src/kernel/recoverable-chat-agent.ts +899 -0
- package/src/kernel/state.ts +76 -0
- package/src/kernel/submission-lifecycle.ts +600 -0
- package/src/layers/context/budget/gate.ts +88 -0
- package/src/layers/orchestration/subagents/agent-types/contract.ts +78 -0
- package/src/layers/orchestration/subagents/agent-types/extract/index.ts +47 -0
- package/src/layers/orchestration/subagents/agent-types/fanout/index.ts +53 -0
- package/src/layers/orchestration/subagents/agent-types/registry.ts +16 -0
- package/src/layers/orchestration/temporary-agent/core.ts +152 -0
- package/src/layers/orchestration/temporary-agent/runner.ts +133 -0
- package/src/layers/orchestration/temporary-agent/workspace.ts +154 -0
- package/src/lib/artifacts.ts +54 -0
- package/src/lib/egress.ts +44 -0
- package/src/lib/execution-level.ts +27 -0
- package/src/lib/extension-name.ts +18 -0
- package/src/lib/host-actions.ts +57 -0
- package/src/lib/mcp.ts +86 -0
- package/src/lib/model-catalog.ts +7 -0
- package/src/lib/prompt.ts +139 -0
- package/src/lib/telemetry-dev.ts +44 -0
- package/src/pi/assembly/context.ts +510 -0
- package/src/pi/assembly/extensions.ts +661 -0
- package/src/pi/assembly/index.ts +19 -0
- package/src/pi/assembly/snapshot.ts +200 -0
- package/src/pi/message/contract.ts +8 -0
- package/src/pi/message/conversion.ts +73 -0
- package/src/pi/message/index.ts +3 -0
- package/src/pi/message/projection.ts +604 -0
- package/src/pi/runtime-adapter/assembly.ts +552 -0
- package/src/pi/runtime-adapter/execution.ts +683 -0
- package/src/pi/runtime-adapter/index.ts +232 -0
- package/src/pi/runtime-adapter/models.ts +243 -0
- package/src/pi/runtime-adapter/recovery.ts +805 -0
- package/src/pi/runtime-adapter/transcript.ts +825 -0
- package/src/pi/session/index.ts +24 -0
- package/src/pi/session/storage.ts +353 -0
- package/src/pi/tool/ai-adapter.ts +100 -0
- package/src/pi/tool/base.ts +110 -0
- package/src/pi/tool/compiler.ts +444 -0
- package/src/pi/tool/core-host.ts +48 -0
- package/src/pi/tool/core.ts +251 -0
- package/src/pi/tool/index.ts +32 -0
- package/src/pi/tool/mcp.ts +319 -0
- package/src/pi/tool/schedule.ts +198 -0
- package/src/pi/tool/skill.ts +455 -0
- package/src/pi/tool/subagent.ts +148 -0
- package/src/pi/tool/web-search/api.ts +1292 -0
- package/src/pi/tool/web-search/index.ts +2 -0
- package/src/pi/tool/web-search/web-search.ts +127 -0
- package/src/pi/tool/workspace-sandbox.ts +664 -0
- package/src/pi/turn/approval.ts +181 -0
- package/src/pi/turn/index.ts +62 -0
- package/src/pi/turn/tool-recovery.ts +792 -0
- package/src/plugins.ts +1024 -0
- package/src/runtime-agent.ts +654 -0
- package/src/runtime.ts +2880 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
import {
|
|
2
|
+
callable,
|
|
3
|
+
type Agent,
|
|
4
|
+
type AgentToolRunStatus,
|
|
5
|
+
type ChatCapableAgentClass,
|
|
6
|
+
type RunAgentToolOptions,
|
|
7
|
+
} from "agents";
|
|
8
|
+
import type { UIMessage } from "ai";
|
|
9
|
+
import type { PiCanonicalTranscriptSnapshot } from "./pi/runtime-adapter";
|
|
10
|
+
import type {
|
|
11
|
+
ApprovalDecision,
|
|
12
|
+
ApprovalReceipt,
|
|
13
|
+
MessageDelivery,
|
|
14
|
+
MessageDispatchReceipt,
|
|
15
|
+
SubmissionReceipt,
|
|
16
|
+
} from "./kernel/receipts";
|
|
17
|
+
import type { ExecutionLevel } from "./lib/execution-level";
|
|
18
|
+
import type { RuntimeState } from "./kernel/state";
|
|
19
|
+
import type {
|
|
20
|
+
TemporaryAgentApprovalDecision,
|
|
21
|
+
TemporaryAgentApprovalRequest,
|
|
22
|
+
TemporaryAgentExecutor,
|
|
23
|
+
TemporaryAgentRequest,
|
|
24
|
+
TemporaryAgentRunContext,
|
|
25
|
+
} from "./layers/orchestration/temporary-agent/core";
|
|
26
|
+
import type { AgentConfig } from "./plugins";
|
|
27
|
+
import { AgentRuntimeKernel } from "./runtime";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 本文件把应用提供的 `createConfig` 接到 Cloudflare Agent 生命周期。
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* 核心术语见包入口 `index.ts`。
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// #region Agent 身份与公开契约
|
|
37
|
+
|
|
38
|
+
const RESERVED_IDENTIFIERS = new Set([
|
|
39
|
+
"arguments",
|
|
40
|
+
"await",
|
|
41
|
+
"break",
|
|
42
|
+
"case",
|
|
43
|
+
"catch",
|
|
44
|
+
"class",
|
|
45
|
+
"const",
|
|
46
|
+
"continue",
|
|
47
|
+
"debugger",
|
|
48
|
+
"default",
|
|
49
|
+
"delete",
|
|
50
|
+
"do",
|
|
51
|
+
"else",
|
|
52
|
+
"enum",
|
|
53
|
+
"eval",
|
|
54
|
+
"export",
|
|
55
|
+
"extends",
|
|
56
|
+
"false",
|
|
57
|
+
"finally",
|
|
58
|
+
"for",
|
|
59
|
+
"function",
|
|
60
|
+
"if",
|
|
61
|
+
"implements",
|
|
62
|
+
"import",
|
|
63
|
+
"in",
|
|
64
|
+
"instanceof",
|
|
65
|
+
"interface",
|
|
66
|
+
"let",
|
|
67
|
+
"new",
|
|
68
|
+
"null",
|
|
69
|
+
"package",
|
|
70
|
+
"private",
|
|
71
|
+
"protected",
|
|
72
|
+
"public",
|
|
73
|
+
"return",
|
|
74
|
+
"static",
|
|
75
|
+
"super",
|
|
76
|
+
"switch",
|
|
77
|
+
"this",
|
|
78
|
+
"throw",
|
|
79
|
+
"true",
|
|
80
|
+
"try",
|
|
81
|
+
"typeof",
|
|
82
|
+
"var",
|
|
83
|
+
"void",
|
|
84
|
+
"while",
|
|
85
|
+
"with",
|
|
86
|
+
"yield",
|
|
87
|
+
]);
|
|
88
|
+
const JAVASCRIPT_IDENTIFIER =
|
|
89
|
+
/^[$_\p{ID_Start}][$\u200C\u200D\p{ID_Continue}]*$/u;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 描述 Agent 寻址路径中的一级父子节点。
|
|
93
|
+
*
|
|
94
|
+
* @remarks
|
|
95
|
+
* Cloudflare Agents SDK 用 `parentPath` 暴露根节点到直接父节点的路径。
|
|
96
|
+
*
|
|
97
|
+
* 调用方通常只读取,不应自行构造后写回 SDK。
|
|
98
|
+
*/
|
|
99
|
+
export interface RuntimeAgentPathStep {
|
|
100
|
+
className: string;
|
|
101
|
+
name: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 表示一次 Agent Tool 执行返回给 Runtime 的稳定字段。
|
|
106
|
+
*
|
|
107
|
+
* @remarks
|
|
108
|
+
* Subagent 端口在调用 `RuntimeAgentConfigContext.runAgentTool` 后读取它。
|
|
109
|
+
*
|
|
110
|
+
* 这里只保留 Runtime 需要的结果,避免把 SDK 内部运行对象暴露给应用。
|
|
111
|
+
*/
|
|
112
|
+
export interface RuntimeAgentToolResult {
|
|
113
|
+
status: string;
|
|
114
|
+
runId: string;
|
|
115
|
+
output?: unknown;
|
|
116
|
+
summary?: string;
|
|
117
|
+
error?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 这是应用创建 Config 时唯一可以接触的 Agent 实例能力。
|
|
122
|
+
*
|
|
123
|
+
* 身份通过实时 getter 暴露。
|
|
124
|
+
*
|
|
125
|
+
* 待确认:原注释把 getter 的必要性归因于 facet 元数据在 Durable Object
|
|
126
|
+
* 构造后恢复,但当前 SDK 会在用户 `onStart` 前完成恢复。
|
|
127
|
+
*/
|
|
128
|
+
export interface RuntimeAgentConfigContext<
|
|
129
|
+
Env extends Cloudflare.Env = Cloudflare.Env,
|
|
130
|
+
> {
|
|
131
|
+
/** 读取当前 Durable Object 的状态句柄。 */
|
|
132
|
+
readonly ctx: DurableObjectState;
|
|
133
|
+
/** 读取当前 Worker 的环境绑定。 */
|
|
134
|
+
readonly env: Env;
|
|
135
|
+
/** 读取当前 Agent 实例名称。 */
|
|
136
|
+
readonly name: string;
|
|
137
|
+
/** 读取从根节点到直接父节点的 Agent 路径。 */
|
|
138
|
+
readonly parentPath: readonly RuntimeAgentPathStep[];
|
|
139
|
+
/**
|
|
140
|
+
* 告诉生成类本次配置最终解析到了哪个 Runtime key。
|
|
141
|
+
*
|
|
142
|
+
* @remarks
|
|
143
|
+
* `createConfig` 在完成业务寻址后调用,同一次创建只能报告一个非空值。
|
|
144
|
+
*
|
|
145
|
+
* 生成类用该 key 合并重复加载;它只是运行时选择,不会写回业务配置。
|
|
146
|
+
*/
|
|
147
|
+
setResolvedRuntimeKey(runtimeKey: string): void;
|
|
148
|
+
/**
|
|
149
|
+
* 请求重新装配一次 Runtime,让业务侧刚写入的配置立刻生效。
|
|
150
|
+
*
|
|
151
|
+
* @remarks
|
|
152
|
+
* 给「运行中改变自身可用能力」的 Tool 使用:先把变更写进持久层,再调用它,
|
|
153
|
+
* 新的 Snapshot 会按同一条解析链重新装配。不要在 Plugin 的 loader 里调用,
|
|
154
|
+
* 那会让装配递归。
|
|
155
|
+
*/
|
|
156
|
+
reloadRuntime(): Promise<void>;
|
|
157
|
+
/**
|
|
158
|
+
* 通过当前 Agent 调用一个受 SDK 管理的子 Agent Tool。
|
|
159
|
+
*
|
|
160
|
+
* @remarks
|
|
161
|
+
* subagent 端口在父 Agent 需要保留子运行状态、事件和取消边界时调用。
|
|
162
|
+
*
|
|
163
|
+
* 调用必须经过当前 Agent 实例,才能使用 Cloudflare Agents SDK 的受管子运行登记。
|
|
164
|
+
*/
|
|
165
|
+
runAgentTool(
|
|
166
|
+
agentClass: ChatCapableAgentClass,
|
|
167
|
+
options: RunAgentToolOptions<unknown>,
|
|
168
|
+
): Promise<RuntimeAgentToolResult>;
|
|
169
|
+
/**
|
|
170
|
+
* 清理符合年龄和状态条件的 SDK 子运行记录。
|
|
171
|
+
*
|
|
172
|
+
* @remarks
|
|
173
|
+
* subagent 清理逻辑在需要回收已保留运行时调用。
|
|
174
|
+
*
|
|
175
|
+
* 必须显式提供年龄和状态筛选,避免无边界地删除仍需查询的子运行。
|
|
176
|
+
*/
|
|
177
|
+
clearAgentToolRuns(options: {
|
|
178
|
+
olderThan: number;
|
|
179
|
+
status: AgentToolRunStatus[];
|
|
180
|
+
}): Promise<void>;
|
|
181
|
+
/** 读取当前已装配 User Agent 的执行档位。 */
|
|
182
|
+
executionLevel(): ExecutionLevel;
|
|
183
|
+
/**
|
|
184
|
+
* 在当前 Session Runtime 中执行一个仅对本次调用存活的临时 Agent。
|
|
185
|
+
*
|
|
186
|
+
* @remarks
|
|
187
|
+
* temporary-agent 端口在 Tool 需要一次性委派时调用。
|
|
188
|
+
*
|
|
189
|
+
* 它复用当前 Session 的 Runtime 和取消信号,但不创建持久 facet。
|
|
190
|
+
*/
|
|
191
|
+
runTemporaryAgent(
|
|
192
|
+
request: TemporaryAgentRequest,
|
|
193
|
+
context: TemporaryAgentRunContext,
|
|
194
|
+
execute: TemporaryAgentExecutor,
|
|
195
|
+
): Promise<string>;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 告诉 Runtime 如何定义 Agent 类并创建配置。
|
|
200
|
+
*
|
|
201
|
+
* @remarks
|
|
202
|
+
* Worker 入口把它传给 `defineRuntimeAgent`。
|
|
203
|
+
*
|
|
204
|
+
* `createConfig` 只负责业务寻址和 Plugin 创建,原子装配由 Runtime 完成。
|
|
205
|
+
*/
|
|
206
|
+
export interface RuntimeAgentDefinition<
|
|
207
|
+
Env extends Cloudflare.Env = Cloudflare.Env,
|
|
208
|
+
> {
|
|
209
|
+
/**
|
|
210
|
+
* 必须等于 Worker 导出名。
|
|
211
|
+
*
|
|
212
|
+
* 顶层 Agent 还必须与 Wrangler `class_name` 一致。
|
|
213
|
+
*
|
|
214
|
+
* 纯子 facet 不需要自己的顶层绑定。
|
|
215
|
+
*
|
|
216
|
+
* Agents SDK 使用构造器名称解析 facet 回调。
|
|
217
|
+
*/
|
|
218
|
+
className: string;
|
|
219
|
+
/**
|
|
220
|
+
* 为当前 Agent 实例创建一次完整的 Plugin 配置。
|
|
221
|
+
*
|
|
222
|
+
* @remarks
|
|
223
|
+
* 生成类在首次执行需求和显式切换 Runtime key 时调用。
|
|
224
|
+
*
|
|
225
|
+
* 返回值尚未生效,只有 Runtime 完成校验和提交后才会替换当前 Snapshot。
|
|
226
|
+
*/
|
|
227
|
+
createConfig(
|
|
228
|
+
context: RuntimeAgentConfigContext<Env>,
|
|
229
|
+
runtimeKey?: string,
|
|
230
|
+
): Promise<AgentConfig>;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 这是生成 Runtime Agent 的稳定公开控制面。
|
|
235
|
+
*
|
|
236
|
+
* Pi 与 Cloudflare Agent 生命周期钩子和 Kernel 实现方法有意不进入这个 API。
|
|
237
|
+
*/
|
|
238
|
+
export interface RuntimeAgentControls {
|
|
239
|
+
/**
|
|
240
|
+
* 确保当前 Runtime 已加载,或按给定 key 显式重载。
|
|
241
|
+
*
|
|
242
|
+
* @remarks
|
|
243
|
+
* RPC 调用方在业务配置变更后调用;`force` 用于 key 没变但配置内容已变的情况。
|
|
244
|
+
*
|
|
245
|
+
* 实现与首次启动共用同一串行入口,避免并发重载安装不同的 Snapshot。
|
|
246
|
+
*/
|
|
247
|
+
reloadRuntime(
|
|
248
|
+
runtimeKey?: string,
|
|
249
|
+
options?: { force?: boolean },
|
|
250
|
+
): Promise<void>;
|
|
251
|
+
/**
|
|
252
|
+
* 确保 Runtime 已加载,再请求 Kernel 刷新当前 Session Context。
|
|
253
|
+
*
|
|
254
|
+
* @remarks
|
|
255
|
+
* 业务侧在外部 Context 变更后调用,例如 Inbox 需要让已存在的聊天立即重读内容。
|
|
256
|
+
*
|
|
257
|
+
* 待确认:当前 `refreshContext` 只检查 Snapshot 已安装,没有重新读取 Context 或刷新缓存。
|
|
258
|
+
*/
|
|
259
|
+
refreshMemoryContext(): Promise<void>;
|
|
260
|
+
/**
|
|
261
|
+
* 读取 canonical transcript 的浏览器投影。
|
|
262
|
+
*
|
|
263
|
+
* @remarks
|
|
264
|
+
* UI 或 fork 流程需要当前消息历史时调用。
|
|
265
|
+
*
|
|
266
|
+
* 返回值由 Pi transcript 统一生成,调用方不应另建第二份权威历史。
|
|
267
|
+
*/
|
|
268
|
+
getMessages(): Promise<UIMessage[]>;
|
|
269
|
+
/**
|
|
270
|
+
* 把一组既有消息一次性导入 canonical transcript。
|
|
271
|
+
*
|
|
272
|
+
* @remarks
|
|
273
|
+
* 只供真实 fork 或 import 流程在新 Session 初始化时调用。
|
|
274
|
+
*
|
|
275
|
+
* 普通发送必须经过 `submitPrompt`,否则会绕过 admission 和回执状态。
|
|
276
|
+
*/
|
|
277
|
+
getCanonicalSnapshot(
|
|
278
|
+
throughMessageId: string,
|
|
279
|
+
): Promise<PiCanonicalTranscriptSnapshot>;
|
|
280
|
+
/** Import a server-authored canonical transcript snapshot into a new Session. */
|
|
281
|
+
addCanonicalSnapshot(snapshot: PiCanonicalTranscriptSnapshot): Promise<void>;
|
|
282
|
+
/** 读取一条仍待决定的审批,供 Host 可信处理 `allow_level`。 */
|
|
283
|
+
getPendingApproval(executionId: string): ApprovalReceipt | null;
|
|
284
|
+
/** 对当前待审批执行应用一次决定。 */
|
|
285
|
+
decideApproval(
|
|
286
|
+
executionId: string,
|
|
287
|
+
decision: ApprovalDecision,
|
|
288
|
+
): Promise<{ ok: boolean }>;
|
|
289
|
+
/**
|
|
290
|
+
* 登记一条受管临时 Agent 发起的审批请求。
|
|
291
|
+
*
|
|
292
|
+
* @remarks
|
|
293
|
+
* temporary-agent runner 在子执行调用需审批的 Tool 时调用并等待结果。
|
|
294
|
+
*
|
|
295
|
+
* Kernel 按 execution id 保留唯一等待项并广播给 UI,重复 id 会立即失败。
|
|
296
|
+
*/
|
|
297
|
+
requestTemporaryAgentApproval(
|
|
298
|
+
request: TemporaryAgentApprovalRequest,
|
|
299
|
+
): Promise<TemporaryAgentApprovalDecision>;
|
|
300
|
+
/**
|
|
301
|
+
* 移除已不再等待的临时 Agent 审批请求。
|
|
302
|
+
*
|
|
303
|
+
* @remarks
|
|
304
|
+
* temporary-agent runner 在子执行取消或提前结束时调用。
|
|
305
|
+
*
|
|
306
|
+
* 取消会以拒绝结果解除原等待,并刷新 UI;找不到 id 时返回 `ok: false`。
|
|
307
|
+
*/
|
|
308
|
+
cancelTemporaryAgentApproval(
|
|
309
|
+
executionId: string,
|
|
310
|
+
reason?: string,
|
|
311
|
+
): Promise<{ ok: boolean }>;
|
|
312
|
+
/**
|
|
313
|
+
* 提交一个用户提示,并返回可查询的回执。
|
|
314
|
+
*
|
|
315
|
+
* @remarks
|
|
316
|
+
* RPC 调用方需要程序化启动 Turn 时调用,可传入幂等 key 复用已接纳提交。
|
|
317
|
+
*
|
|
318
|
+
* 方法在 admission 后立即返回,完成 Promise 交给 Durable Object `waitUntil`,调用方用回执另行查询。
|
|
319
|
+
*/
|
|
320
|
+
submitPrompt(
|
|
321
|
+
text: string,
|
|
322
|
+
opts?: { idempotencyKey?: string },
|
|
323
|
+
): Promise<SubmissionReceipt>;
|
|
324
|
+
dispatchMessage(
|
|
325
|
+
message: UIMessage,
|
|
326
|
+
delivery: MessageDelivery,
|
|
327
|
+
): Promise<MessageDispatchReceipt>;
|
|
328
|
+
steerQueuedSubmission(
|
|
329
|
+
submissionId: string,
|
|
330
|
+
): Promise<MessageDispatchReceipt>;
|
|
331
|
+
/**
|
|
332
|
+
* 等待 Session 稳定后提交提示,并在准入后返回当前回执。
|
|
333
|
+
*
|
|
334
|
+
* @remarks
|
|
335
|
+
* schedule 端口在 Cloudflare Agents SDK 唤醒对应回调后调用。
|
|
336
|
+
*
|
|
337
|
+
* 它与普通提示共用 admission,但最多等待当前实现的 30 秒稳定期,超时返回 `skipped`。
|
|
338
|
+
*/
|
|
339
|
+
submitScheduledPrompt(
|
|
340
|
+
prompt: string,
|
|
341
|
+
opts?: { idempotencyKey?: string },
|
|
342
|
+
): Promise<SubmissionReceipt>;
|
|
343
|
+
/**
|
|
344
|
+
* 按提交编号查询当前处理状态。
|
|
345
|
+
*
|
|
346
|
+
* @remarks
|
|
347
|
+
* RPC 调用方在 `submitPrompt` 返回后跟踪结果时调用。
|
|
348
|
+
*
|
|
349
|
+
* 结果来自持久化的 submission 生命周期,不存在的编号返回 `null`。
|
|
350
|
+
*/
|
|
351
|
+
getSubmission(submissionId: string): Promise<SubmissionReceipt | null>;
|
|
352
|
+
/**
|
|
353
|
+
* 按提交编号取消尚未结束的请求。
|
|
354
|
+
*
|
|
355
|
+
* @remarks
|
|
356
|
+
* RPC 调用方已知 submission id 时调用,可附带面向记录的原因。
|
|
357
|
+
*
|
|
358
|
+
* Kernel 把取消交给 submission 生命周期,由它区分等待、运行和已终止状态。
|
|
359
|
+
*/
|
|
360
|
+
cancelSubmissionById(
|
|
361
|
+
submissionId: string,
|
|
362
|
+
reason?: string,
|
|
363
|
+
): Promise<{ ok: boolean }>;
|
|
364
|
+
/**
|
|
365
|
+
* 停止当前或指定请求的 Turn。
|
|
366
|
+
*
|
|
367
|
+
* @remarks
|
|
368
|
+
* UI 或 RPC 调用方在用户主动停止生成时调用;省略 request id 表示当前 Turn。
|
|
369
|
+
*
|
|
370
|
+
* Kernel 通过 submission 生命周期向当前 Pi 执行传递中止,不直接修改 transcript。
|
|
371
|
+
*/
|
|
372
|
+
stopTurn(
|
|
373
|
+
requestId?: string,
|
|
374
|
+
reason?: string,
|
|
375
|
+
): Promise<{ ok: boolean }>;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 表示 `defineRuntimeAgent` 生成类的实例。
|
|
380
|
+
*
|
|
381
|
+
* @remarks
|
|
382
|
+
* RPC 调用方使用这个交叉类型访问 Cloudflare Agent 能力和稳定控制面。
|
|
383
|
+
*/
|
|
384
|
+
export type RuntimeAgentInstance<
|
|
385
|
+
Env extends Cloudflare.Env = Cloudflare.Env,
|
|
386
|
+
> = Agent<Env, RuntimeState> & RuntimeAgentControls;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 表示 `defineRuntimeAgent` 返回的 Durable Object 类构造器。
|
|
390
|
+
*
|
|
391
|
+
* @remarks
|
|
392
|
+
* Worker 入口导出该类,Cloudflare Runtime 负责实例化它。
|
|
393
|
+
*/
|
|
394
|
+
export interface RuntimeAgentClass<
|
|
395
|
+
Env extends Cloudflare.Env = Cloudflare.Env,
|
|
396
|
+
> {
|
|
397
|
+
new (ctx: DurableObjectState, env: Env): RuntimeAgentInstance<Env>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// #endregion
|
|
401
|
+
|
|
402
|
+
// #region 生成类的构造与生命周期
|
|
403
|
+
|
|
404
|
+
// 作用:确认导出类名是可以安全创建的 JavaScript 标识符。
|
|
405
|
+
// 调用:`defineRuntimeAgent` 在生成类之前调用一次。
|
|
406
|
+
// 原因:动态计算类名时提前拒绝关键字,避免部署后才出现寻址失败。
|
|
407
|
+
function assertClassName(className: string): void {
|
|
408
|
+
if (
|
|
409
|
+
!JAVASCRIPT_IDENTIFIER.test(className) ||
|
|
410
|
+
RESERVED_IDENTIFIERS.has(className)
|
|
411
|
+
) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
`Invalid Runtime Agent className "${className}": expected a non-reserved JavaScript identifier`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* 生成一个不向应用暴露 Kernel 继承细节的 Cloudflare Agent 类。
|
|
420
|
+
*
|
|
421
|
+
* @remarks
|
|
422
|
+
* Worker 入口调用一次,并把返回类作为稳定导出。
|
|
423
|
+
*
|
|
424
|
+
* 生成类把首次加载、并发去重、按 key 重载和失败重试收口到一个入口。
|
|
425
|
+
*/
|
|
426
|
+
export function defineRuntimeAgent<
|
|
427
|
+
Env extends Cloudflare.Env = Cloudflare.Env,
|
|
428
|
+
>(
|
|
429
|
+
definition: RuntimeAgentDefinition<Env>,
|
|
430
|
+
): RuntimeAgentClass<Env> {
|
|
431
|
+
assertClassName(definition.className);
|
|
432
|
+
|
|
433
|
+
const GeneratedRuntimeAgent = {
|
|
434
|
+
[definition.className]: class extends AgentRuntimeKernel<Env> {
|
|
435
|
+
private hasLoadedRuntime = false;
|
|
436
|
+
private loadedRuntimeKey?: string;
|
|
437
|
+
private loading?: Promise<void>;
|
|
438
|
+
|
|
439
|
+
// 作用:在 Kernel 真正消费 RuntimeSnapshot 前确保配置已完整提交。
|
|
440
|
+
// 调用:Submission 准入、恢复、审批续跑与 Runtime Workspace 路径调用。
|
|
441
|
+
// 原因:Session 本地读取不应支付 Config、Plugin、MCP 和 Pi 装配成本。
|
|
442
|
+
protected ensureRuntimeReady(): Promise<void> {
|
|
443
|
+
return this.ensureConfig();
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// 作用:加载默认 Runtime,或切换到调用方指定的 key。
|
|
447
|
+
// 调用:RPC 控制面在业务配置需要刷新时调用。
|
|
448
|
+
// 原因:复用 `ensureConfig`,让手动重载遵守和首次启动相同的原子规则。
|
|
449
|
+
async reloadRuntime(
|
|
450
|
+
runtimeKey?: string,
|
|
451
|
+
options?: { force?: boolean },
|
|
452
|
+
): Promise<void> {
|
|
453
|
+
await this.ensureConfig(runtimeKey, options?.force === true);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// 作用:确保 Runtime 可用后请求 Kernel 刷新 Session Context。
|
|
457
|
+
// 调用:Inbox 等 RPC 调用方在外部 Context 变更后调用。
|
|
458
|
+
// 待确认:当前 Kernel 的 `refreshContext` 只检查 Snapshot,并未执行实际刷新。
|
|
459
|
+
async refreshMemoryContext(): Promise<void> {
|
|
460
|
+
await this.ensureRuntimeReady();
|
|
461
|
+
await this.refreshContext();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
dispatchMessage(
|
|
465
|
+
message: UIMessage,
|
|
466
|
+
delivery: MessageDelivery,
|
|
467
|
+
): Promise<MessageDispatchReceipt> {
|
|
468
|
+
return super.dispatchMessage(message, delivery);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
steerQueuedSubmission(
|
|
472
|
+
submissionId: string,
|
|
473
|
+
): Promise<MessageDispatchReceipt> {
|
|
474
|
+
return super.steerQueuedSubmission(submissionId);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
cancelSubmissionById(
|
|
478
|
+
submissionId: string,
|
|
479
|
+
reason?: string,
|
|
480
|
+
): Promise<{ ok: boolean }> {
|
|
481
|
+
return super.cancelSubmissionById(submissionId, reason);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
stopTurn(
|
|
485
|
+
requestId?: string,
|
|
486
|
+
reason?: string,
|
|
487
|
+
): Promise<{ ok: boolean }> {
|
|
488
|
+
return super.stopTurn(requestId, reason);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// 作用:把首次加载和按 key 重载合并成一个串行入口。
|
|
492
|
+
// 调用:Runtime readiness、Session 配置和重载都会调用。
|
|
493
|
+
// 原因:共享同一个 Promise 可合并并发首加载,显式切 key 则等待前序完成后再判断。
|
|
494
|
+
private ensureConfig(
|
|
495
|
+
requestedRuntimeKey?: string,
|
|
496
|
+
force = false,
|
|
497
|
+
fromQueue = false,
|
|
498
|
+
): Promise<void> {
|
|
499
|
+
if (this.loading && !fromQueue) {
|
|
500
|
+
if (!requestedRuntimeKey && !force) return this.loading;
|
|
501
|
+
|
|
502
|
+
// 显式 key 或强制刷新不能被正在进行的其他加载吞掉,必须等它结束后重新核对。
|
|
503
|
+
const queued = this.loading
|
|
504
|
+
.catch(() => undefined)
|
|
505
|
+
.then(() => {
|
|
506
|
+
if (
|
|
507
|
+
!force &&
|
|
508
|
+
this.hasLoadedRuntime &&
|
|
509
|
+
this.loadedRuntimeKey === requestedRuntimeKey
|
|
510
|
+
) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
return this.ensureConfig(
|
|
514
|
+
requestedRuntimeKey,
|
|
515
|
+
force,
|
|
516
|
+
true,
|
|
517
|
+
);
|
|
518
|
+
});
|
|
519
|
+
let tracked: Promise<void>;
|
|
520
|
+
tracked = queued.finally(() => {
|
|
521
|
+
if (this.loading === tracked) this.loading = undefined;
|
|
522
|
+
});
|
|
523
|
+
this.loading = tracked;
|
|
524
|
+
return tracked;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (
|
|
528
|
+
!force &&
|
|
529
|
+
this.hasLoadedRuntime &&
|
|
530
|
+
(!requestedRuntimeKey ||
|
|
531
|
+
requestedRuntimeKey === this.loadedRuntimeKey)
|
|
532
|
+
) {
|
|
533
|
+
return Promise.resolve();
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// 先创建局部 Promise,再赋给 `loading`,失败时也能在 finally 中放开重试。
|
|
537
|
+
const pending = (async () => {
|
|
538
|
+
this.beginRuntimeLoad();
|
|
539
|
+
let resolvedRuntimeKey: string | undefined;
|
|
540
|
+
const agent = this;
|
|
541
|
+
const config = await definition.createConfig(
|
|
542
|
+
{
|
|
543
|
+
ctx: this.ctx,
|
|
544
|
+
env: this.env,
|
|
545
|
+
// 作用:在创建配置时读取当前 Agent 实例名。
|
|
546
|
+
// 调用:应用的 `createConfig` 在解析 Session 身份时读取。
|
|
547
|
+
// 原因:使用实时 getter,不在上下文创建时复制 SDK 身份值。
|
|
548
|
+
get name() {
|
|
549
|
+
return agent.name;
|
|
550
|
+
},
|
|
551
|
+
// 作用:在创建配置时读取根到直接父节点的路径。
|
|
552
|
+
// 调用:应用的 `createConfig` 在解析 facet 归属时读取。
|
|
553
|
+
// 原因:路径由 Agents SDK 维护,Runtime 只转发而不另存副本。
|
|
554
|
+
get parentPath() {
|
|
555
|
+
return agent.parentPath;
|
|
556
|
+
},
|
|
557
|
+
// 作用:记住本次配置解析出的 Runtime key。
|
|
558
|
+
// 调用:应用的 `createConfig` 在完成业务寻址后调用。
|
|
559
|
+
// 原因:只接受一个非空结果,防止同一次装配在两个业务身份之间摇摆。
|
|
560
|
+
setResolvedRuntimeKey(runtimeKey) {
|
|
561
|
+
const normalized = runtimeKey.trim();
|
|
562
|
+
if (!normalized) {
|
|
563
|
+
throw new Error(
|
|
564
|
+
"Resolved Runtime key must not be empty",
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
if (
|
|
568
|
+
resolvedRuntimeKey &&
|
|
569
|
+
resolvedRuntimeKey !== normalized
|
|
570
|
+
) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
"Resolved Runtime key cannot change during Config creation",
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
resolvedRuntimeKey = normalized;
|
|
576
|
+
},
|
|
577
|
+
// 作用:强制按当前业务数据重新装配 Runtime。
|
|
578
|
+
// 调用:会修改自身配置的 Tool 在持久化成功后调用。
|
|
579
|
+
// 原因:配置内容可能已变而 key 不变,因此不能复用已加载标记。
|
|
580
|
+
reloadRuntime: () => agent.ensureConfig(undefined, true),
|
|
581
|
+
// 作用:通过当前 Agent 启动一个受 SDK 管理的子运行。
|
|
582
|
+
// 调用:subagent 端口在委派 Agent Tool 时调用。
|
|
583
|
+
// 原因:保留 Agents SDK 的运行登记、事件、取消和清理边界。
|
|
584
|
+
runAgentTool: async (agentClass, options) =>
|
|
585
|
+
agent.runAgentTool(
|
|
586
|
+
agentClass,
|
|
587
|
+
options as never,
|
|
588
|
+
) as Promise<RuntimeAgentToolResult>,
|
|
589
|
+
// 作用:清理符合明确策略的已保留子运行。
|
|
590
|
+
// 调用:subagent 端口在执行运行保留期回收时调用。
|
|
591
|
+
// 原因:清理仍由 Agents SDK 执行,Runtime 不越过 SDK 直接修改子运行存储。
|
|
592
|
+
clearAgentToolRuns: (options) =>
|
|
593
|
+
agent.clearAgentToolRuns(options),
|
|
594
|
+
// 作用:返回当前已装配 User Agent 的执行档位。
|
|
595
|
+
// 调用:Plugin 在装配临时 Agent 审批桥时调用。
|
|
596
|
+
// 原因:只读 Profile,不再维护 Session 覆盖状态。
|
|
597
|
+
executionLevel: () => agent.executionLevel(),
|
|
598
|
+
// 作用:在当前 Session 中执行一次性临时 Agent。
|
|
599
|
+
// 调用:temporary-agent 端口在 Tool 需要调用内委派时调用。
|
|
600
|
+
// 原因:交给 Kernel 统一管理重名、取消和审批,不创建持久 facet。
|
|
601
|
+
runTemporaryAgent: (request, runContext, execute) =>
|
|
602
|
+
agent.runTemporaryAgent(request, runContext, execute),
|
|
603
|
+
},
|
|
604
|
+
requestedRuntimeKey,
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
// 只有原子提交成功后才更新已加载标记和 key。
|
|
608
|
+
await this.initConfig(config);
|
|
609
|
+
this.hasLoadedRuntime = true;
|
|
610
|
+
this.loadedRuntimeKey =
|
|
611
|
+
resolvedRuntimeKey ?? requestedRuntimeKey;
|
|
612
|
+
this.completeRuntimeLoad();
|
|
613
|
+
})().catch((error) => {
|
|
614
|
+
this.failRuntimeLoad();
|
|
615
|
+
throw error;
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
if (fromQueue) return pending;
|
|
619
|
+
|
|
620
|
+
// 无论成功失败都清掉进行中标记,后续调用才可以重试或切 key。
|
|
621
|
+
let tracked: Promise<void>;
|
|
622
|
+
tracked = pending.finally(() => {
|
|
623
|
+
if (this.loading === tracked) this.loading = undefined;
|
|
624
|
+
});
|
|
625
|
+
this.loading = tracked;
|
|
626
|
+
return tracked;
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
}[definition.className];
|
|
630
|
+
|
|
631
|
+
if (GeneratedRuntimeAgent.name !== definition.className) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
`Runtime Agent class name mismatch: expected "${definition.className}", got "${GeneratedRuntimeAgent.name}"`,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Decorator syntax renames this computed class during Worker bundling.
|
|
638
|
+
// Apply the same decorator function after class-name inference instead.
|
|
639
|
+
const callableContext = {} as ClassMethodDecoratorContext;
|
|
640
|
+
callable()(GeneratedRuntimeAgent.prototype.dispatchMessage, callableContext);
|
|
641
|
+
callable()(
|
|
642
|
+
GeneratedRuntimeAgent.prototype.steerQueuedSubmission,
|
|
643
|
+
callableContext,
|
|
644
|
+
);
|
|
645
|
+
callable()(
|
|
646
|
+
GeneratedRuntimeAgent.prototype.cancelSubmissionById,
|
|
647
|
+
callableContext,
|
|
648
|
+
);
|
|
649
|
+
callable()(GeneratedRuntimeAgent.prototype.stopTurn, callableContext);
|
|
650
|
+
|
|
651
|
+
return GeneratedRuntimeAgent as RuntimeAgentClass<Env>;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// #endregion
|