@springbrand/agent-runtime 0.1.3-alpha.0 → 0.1.3-alpha.10
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 +12 -3
- package/src/adapter/cloudflare/index.ts +56 -0
- package/src/adapter/cloudflare/resources/runtime-resources.ts +89 -0
- package/src/adapter/cloudflare/sandbox/adapter.ts +1513 -0
- package/src/adapter/cloudflare/sandbox/id.ts +23 -0
- package/src/adapter/cloudflare/sandbox/policy.ts +15 -0
- package/src/adapter/cloudflare/subagent/definition.ts +574 -0
- package/src/adapter/cloudflare/subagent/runner.ts +175 -0
- package/src/adapter/cloudflare/subagent/tools.ts +254 -0
- package/src/adapter/cloudflare/universal-agent/hooks.ts +35 -0
- package/src/adapter/cloudflare/universal-agent/preparation.ts +277 -0
- package/src/adapter/cloudflare/universal-agent/tools.ts +80 -0
- package/src/adapter/cloudflare/workspace/git-fs.ts +178 -0
- package/src/adapter/cloudflare/workspace/publisher.ts +31 -0
- package/src/adapter/cloudflare/workspace/scoped-workspace.ts +376 -0
- package/src/adapter/cloudflare/workspace/version-control.ts +374 -0
- package/src/agent-tool-runtime.ts +152 -0
- package/src/db/agent-tool.repo.ts +27 -0
- package/src/db/index.ts +33 -0
- package/src/db/interaction.repo.ts +185 -0
- package/src/db/schema.ts +25 -1
- package/src/db/submission.repo.ts +63 -1
- package/src/index.ts +61 -27
- package/src/kernel/approval-lifecycle.ts +41 -6
- package/src/kernel/bindings.ts +99 -12
- package/src/kernel/extensions.ts +1 -1
- package/src/kernel/interaction-lifecycle.ts +395 -0
- package/src/kernel/profile.ts +3 -4
- package/src/kernel/public-contracts.ts +2 -0
- package/src/kernel/recoverable-chat-agent.ts +104 -6
- package/src/kernel/runtime-assembly-view.ts +37 -0
- package/src/kernel/runtime-assembly.ts +41 -0
- package/src/kernel/runtime-config.ts +4 -0
- package/src/kernel/runtime-load.ts +191 -0
- package/src/kernel/state.ts +12 -1
- package/src/kernel/submission-lifecycle.ts +33 -2
- package/src/layers/orchestration/temporary-agent/core.ts +12 -1
- package/src/layers/orchestration/temporary-agent/runner.ts +1 -67
- package/src/lib/mcp.ts +7 -3
- package/src/lib/prompt.ts +1 -1
- package/src/lib/telemetry-dev.ts +7 -4
- package/src/pi/assembly/context.ts +4 -6
- package/src/pi/assembly/extensions.ts +11 -22
- package/src/pi/assembly/snapshot.ts +17 -9
- package/src/pi/message/contract.ts +7 -0
- package/src/pi/message/conversion.ts +9 -1
- package/src/pi/runtime-adapter/assembly.ts +42 -97
- package/src/pi/runtime-adapter/execution.ts +216 -21
- package/src/pi/runtime-adapter/index.ts +24 -8
- package/src/pi/runtime-adapter/models.ts +382 -35
- package/src/pi/runtime-adapter/recovery.ts +188 -1
- package/src/pi/runtime-adapter/transcript.ts +61 -3
- package/src/pi/tool/ai-adapter.ts +58 -1
- package/src/pi/tool/base.ts +190 -12
- package/src/pi/tool/compiler.ts +39 -33
- package/src/pi/tool/core-host.ts +28 -30
- package/src/pi/tool/core.ts +37 -124
- package/src/pi/tool/gateway.ts +54 -0
- package/src/pi/tool/index.ts +2 -0
- package/src/pi/tool/mcp.ts +98 -70
- package/src/pi/tool/schedule.ts +86 -20
- package/src/pi/tool/skill.ts +126 -420
- package/src/pi/tool/subagent.ts +14 -2
- package/src/pi/tool/web-fetch.ts +281 -0
- package/src/pi/tool/web-search/api.ts +34 -18
- package/src/pi/tool/web-search/web-search.ts +0 -1
- package/src/pi/tool/workspace-revision.ts +64 -0
- package/src/pi/tool/workspace-sandbox.ts +105 -263
- package/src/pi/turn/index.ts +20 -0
- package/src/pi/turn/interaction.ts +181 -0
- package/src/pi/turn/tool-recovery.ts +244 -1
- package/src/runtime-agent-context.ts +112 -0
- package/src/runtime-agent.ts +568 -321
- package/src/runtime-assembler.ts +797 -0
- package/src/runtime-definition.ts +175 -0
- package/src/runtime.ts +840 -208
- package/src/tool-registry.ts +143 -0
- package/src/workspace-versioning.ts +46 -0
- package/src/plugins.ts +0 -1033
package/src/plugins.ts
DELETED
|
@@ -1,1033 +0,0 @@
|
|
|
1
|
-
import type { SkillSource } from "agents/skills";
|
|
2
|
-
import type {
|
|
3
|
-
RuntimeMemoryPort,
|
|
4
|
-
RuntimePlatformPort,
|
|
5
|
-
RuntimeProviderPort,
|
|
6
|
-
RuntimeSandboxPort,
|
|
7
|
-
RuntimeSchedulePort,
|
|
8
|
-
RuntimeSubagentPort,
|
|
9
|
-
RuntimeTurnEventsPort,
|
|
10
|
-
RuntimeBindings,
|
|
11
|
-
RuntimeSkillScriptPolicy,
|
|
12
|
-
WorkspacePort,
|
|
13
|
-
} from "./kernel/bindings";
|
|
14
|
-
import {
|
|
15
|
-
freezeRuntimeProfile,
|
|
16
|
-
type RuntimeDenyPolicy,
|
|
17
|
-
type RuntimeMcpServer,
|
|
18
|
-
type RuntimeMemoryProfile,
|
|
19
|
-
type RuntimeProfile,
|
|
20
|
-
type ThinkingEffort,
|
|
21
|
-
} from "./kernel/profile";
|
|
22
|
-
import type { RuntimeExtensionConfig } from "./kernel/extensions";
|
|
23
|
-
import type { RuntimeDegradation } from "./kernel/degradation";
|
|
24
|
-
import {
|
|
25
|
-
EXECUTION_LEVELS,
|
|
26
|
-
type ExecutionLevel,
|
|
27
|
-
} from "./lib/execution-level";
|
|
28
|
-
import {
|
|
29
|
-
createPiRuntimeAssembly,
|
|
30
|
-
type PiRuntimeAssembly,
|
|
31
|
-
} from "./pi/assembly/snapshot";
|
|
32
|
-
import type { PiToolCandidate } from "./pi/tool/compiler";
|
|
33
|
-
import { basePiToolCandidates } from "./pi/tool/base";
|
|
34
|
-
import { createWebSearch } from "./pi/tool/web-search";
|
|
35
|
-
import { resolvePiModel } from "./pi/runtime-adapter/models";
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 本文件实现 Plugin 的准备、贡献、校验和原子候选装配。
|
|
39
|
-
*
|
|
40
|
-
* @remarks
|
|
41
|
-
* 核心术语见包入口 `index.ts`,这里不重复定义。
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
// #region Plugin 公开契约
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* 标明一个 Plugin 负责哪一类能力。
|
|
48
|
-
*
|
|
49
|
-
* @remarks
|
|
50
|
-
* 应用创建 Plugin 时必须选择其中一个值。
|
|
51
|
-
*
|
|
52
|
-
* Runtime 用它拒绝重复能力并确定贡献顺序,所以不要把它当作自由文本。
|
|
53
|
-
*/
|
|
54
|
-
export type PluginKind =
|
|
55
|
-
| "scope"
|
|
56
|
-
| "profile"
|
|
57
|
-
| "provider"
|
|
58
|
-
| "platform"
|
|
59
|
-
| "workspace"
|
|
60
|
-
| "sandbox"
|
|
61
|
-
| "memory"
|
|
62
|
-
| "tool"
|
|
63
|
-
| "skill"
|
|
64
|
-
| "connector"
|
|
65
|
-
| "extension"
|
|
66
|
-
| "schedule"
|
|
67
|
-
| "subagent"
|
|
68
|
-
| "turn-events";
|
|
69
|
-
|
|
70
|
-
export type { RuntimeDegradation } from "./kernel/degradation";
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* 把 loader 读取到的值和本次降级信息一起返回。
|
|
74
|
-
*
|
|
75
|
-
* @remarks
|
|
76
|
-
* `definePlugin` 在准备阶段读取它。
|
|
77
|
-
*
|
|
78
|
-
* 值与诊断一起返回,可以避免 loader 直接修改 Runtime。
|
|
79
|
-
*/
|
|
80
|
-
export interface PluginLoadResult<T> {
|
|
81
|
-
readonly value: T;
|
|
82
|
-
readonly degradations: readonly RuntimeDegradation[];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 表示一个尚未准备的 Runtime Plugin。
|
|
87
|
-
*
|
|
88
|
-
* @remarks
|
|
89
|
-
* 应用把它放进 `AgentConfig.plugins`。
|
|
90
|
-
*
|
|
91
|
-
* Runtime 只调用 `prepare`,不会接触 Plugin 捕获的业务依赖。
|
|
92
|
-
*/
|
|
93
|
-
export interface AgentPlugin {
|
|
94
|
-
readonly kind: PluginKind;
|
|
95
|
-
/**
|
|
96
|
-
* 读取这个 Plugin 的配置,并生成一个还未贡献的准备结果。
|
|
97
|
-
*
|
|
98
|
-
* @remarks
|
|
99
|
-
* Runtime 在生成候选结果时调用,应用只需把 Plugin 放进 `AgentConfig`。
|
|
100
|
-
*
|
|
101
|
-
* 读取与贡献分开,是为了并行读取外部数据后再按稳定顺序合并。
|
|
102
|
-
*/
|
|
103
|
-
prepare(): Promise<PreparedPlugin>;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 表示 loader 已完成、但还没有写入候选结果的 Plugin。
|
|
108
|
-
*
|
|
109
|
-
* @remarks
|
|
110
|
-
* Runtime 按固定 kind 顺序调用 `contribute`。
|
|
111
|
-
*
|
|
112
|
-
* 准备和贡献分开,才能并行读取数据又保持确定的合并顺序。
|
|
113
|
-
*/
|
|
114
|
-
export interface PreparedPlugin {
|
|
115
|
-
readonly kind: PluginKind;
|
|
116
|
-
readonly degradations: readonly RuntimeDegradation[];
|
|
117
|
-
/**
|
|
118
|
-
* 把已读取的值写进 Runtime 提供的受限候选上下文。
|
|
119
|
-
*
|
|
120
|
-
* @remarks
|
|
121
|
-
* Runtime 在全部 loader 完成后,按 `PluginKind` 固定顺序调用。
|
|
122
|
-
*
|
|
123
|
-
* 这一阶段只能使用受限上下文,不能直接安装 Snapshot。
|
|
124
|
-
*/
|
|
125
|
-
contribute(
|
|
126
|
-
ctx: RuntimeContributionContext,
|
|
127
|
-
): void | Promise<void>;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 描述一个 Plugin 应该怎样读取数据并贡献能力。
|
|
132
|
-
*
|
|
133
|
-
* @remarks
|
|
134
|
-
* 应用把该对象传给 `definePlugin`。
|
|
135
|
-
*
|
|
136
|
-
* `loader` 负责读取,`contribute` 负责组装,二者不要互相越权。
|
|
137
|
-
*/
|
|
138
|
-
export interface AgentPluginSpec<T> {
|
|
139
|
-
readonly kind: PluginKind;
|
|
140
|
-
/**
|
|
141
|
-
* 在准备阶段读取本 Plugin 所需的数据。
|
|
142
|
-
*
|
|
143
|
-
* @remarks
|
|
144
|
-
* `definePlugin` 包装出的 `prepare` 会在 Runtime 装配开始时调用它。
|
|
145
|
-
*
|
|
146
|
-
* 它只返回数据和降级信息,不应直接修改正在运行的 Runtime。
|
|
147
|
-
*/
|
|
148
|
-
loader(): Promise<PluginLoadResult<T>>;
|
|
149
|
-
/**
|
|
150
|
-
* 在贡献阶段把已读取的数据交给 Runtime。
|
|
151
|
-
*
|
|
152
|
-
* @remarks
|
|
153
|
-
* Runtime 准备完所有 Plugin 后,通过 `PreparedPlugin.contribute` 调用它。
|
|
154
|
-
*
|
|
155
|
-
* 它必须通过 `RuntimeContributionContext` 写入候选结果,以便统一检查重复与跨能力约束。
|
|
156
|
-
*/
|
|
157
|
-
contribute(
|
|
158
|
-
loaded: T,
|
|
159
|
-
ctx: RuntimeContributionContext,
|
|
160
|
-
): void | Promise<void>;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* 汇总一次 Runtime 装配要使用的全部 Plugin。
|
|
165
|
-
*
|
|
166
|
-
* @remarks
|
|
167
|
-
* `RuntimeAgentDefinition.createConfig` 在首次加载或显式重载时返回它。
|
|
168
|
-
*
|
|
169
|
-
* 它只保存可信函数对象,不用于持久化或跨进程序列化。
|
|
170
|
-
*/
|
|
171
|
-
export interface AgentConfig {
|
|
172
|
-
readonly plugins: readonly AgentPlugin[];
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* 提供 Runtime 核心运行参数。
|
|
177
|
-
*
|
|
178
|
-
* @remarks
|
|
179
|
-
* `profile` Plugin 在贡献阶段调用 `configureProfile` 时传入。
|
|
180
|
-
*
|
|
181
|
-
* 这里只放业务可配置字段,冻结和最终结构由 Runtime 负责。
|
|
182
|
-
*/
|
|
183
|
-
export interface RuntimeProfileContribution {
|
|
184
|
-
readonly model: string;
|
|
185
|
-
readonly thinking: ThinkingEffort;
|
|
186
|
-
readonly systemPrompt?: string;
|
|
187
|
-
readonly executionLevel: ExecutionLevel;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* 限定 Plugin 可以写入候选 Runtime 的内容。
|
|
192
|
-
*
|
|
193
|
-
* @remarks
|
|
194
|
-
* Plugin 的 `contribute` 回调使用这些方法。
|
|
195
|
-
*
|
|
196
|
-
* Runtime 只暴露绑定和增量添加操作,避免 Plugin 取得 Builder 的可变引用。
|
|
197
|
-
*/
|
|
198
|
-
export interface RuntimeContributionContext {
|
|
199
|
-
/**
|
|
200
|
-
* 写入唯一的模型、提示词、审批和权限配置。
|
|
201
|
-
*
|
|
202
|
-
* @remarks
|
|
203
|
-
* profile Plugin 在贡献阶段调用一次。
|
|
204
|
-
*
|
|
205
|
-
* Runtime 拒绝第二份配置,避免候选结果同时有两个运行参数来源。
|
|
206
|
-
*/
|
|
207
|
-
configureProfile(profile: RuntimeProfileContribution): void;
|
|
208
|
-
/**
|
|
209
|
-
* 写入部署提供的模型端点与凭据。
|
|
210
|
-
*
|
|
211
|
-
* @remarks
|
|
212
|
-
* provider Plugin 在贡献阶段调用一次。
|
|
213
|
-
*
|
|
214
|
-
* Runtime 保留单一 Provider 选择,并在生成候选结果时检查默认模型、端点和凭据。
|
|
215
|
-
*/
|
|
216
|
-
bindProvider(provider: RuntimeProviderPort): void;
|
|
217
|
-
/**
|
|
218
|
-
* 写入唯一的 Cloudflare 平台端口。
|
|
219
|
-
*
|
|
220
|
-
* @remarks
|
|
221
|
-
* platform Plugin 在贡献阶段调用一次。
|
|
222
|
-
*
|
|
223
|
-
* Loader 和出口函数作为一个整体绑定,不允许从多个平台配置静默拼接。
|
|
224
|
-
*/
|
|
225
|
-
bindPlatform(platform: RuntimePlatformPort): void;
|
|
226
|
-
/**
|
|
227
|
-
* 写入可选的工作区端口。
|
|
228
|
-
*
|
|
229
|
-
* @remarks
|
|
230
|
-
* workspace Plugin 在工作区可用时调用一次。
|
|
231
|
-
*
|
|
232
|
-
* Runtime 只允许一个当前工作区;未绑定时依赖它的能力必须降级或在校验时失败。
|
|
233
|
-
*/
|
|
234
|
-
bindWorkspace(workspace: WorkspacePort): void;
|
|
235
|
-
/**
|
|
236
|
-
* 写入可选的 Linux Sandbox 执行端口。
|
|
237
|
-
*
|
|
238
|
-
* @remarks
|
|
239
|
-
* sandbox Plugin 在部署和 Agent 配置都启用该能力时调用。
|
|
240
|
-
*
|
|
241
|
-
* Sandbox 必须和持久工作区成对出现,这个跨能力约束在提交前统一校验。
|
|
242
|
-
*/
|
|
243
|
-
bindSandbox(
|
|
244
|
-
sandbox: RuntimeSandboxPort,
|
|
245
|
-
): void;
|
|
246
|
-
/**
|
|
247
|
-
* 写入内存参数,并在启用时提供内存读写端口。
|
|
248
|
-
*
|
|
249
|
-
* @remarks
|
|
250
|
-
* memory Plugin 在贡献阶段调用一次,即使功能关闭也要传入 profile。
|
|
251
|
-
*
|
|
252
|
-
* 独立记录“已配置”状态,才能区分未贡献与明确关闭。
|
|
253
|
-
*/
|
|
254
|
-
configureMemory(
|
|
255
|
-
profile: RuntimeMemoryProfile,
|
|
256
|
-
port?: RuntimeMemoryPort,
|
|
257
|
-
): void;
|
|
258
|
-
/**
|
|
259
|
-
* 添加一个已经过装配期授权、并保留审批元数据的 Pi Tool 候选。
|
|
260
|
-
*
|
|
261
|
-
* @remarks
|
|
262
|
-
* tool Plugin 为当前 Agent 真正可见的 Tool 逐个调用。
|
|
263
|
-
*
|
|
264
|
-
* Runtime 在装配期拒绝空名和重名,避免 Pi 开始 Turn 后才发现能力冲突。
|
|
265
|
-
*/
|
|
266
|
-
addPiTool(candidate: PiToolCandidate): void;
|
|
267
|
-
/**
|
|
268
|
-
* 写入唯一的 Turn 期拒绝清单。
|
|
269
|
-
*
|
|
270
|
-
* @remarks
|
|
271
|
-
* tool Plugin 在完成实际可见能力挑选后调用一次。
|
|
272
|
-
*
|
|
273
|
-
* 能力的有无在注入前已经决定,这里只保留 Turn 期仍需执行的拒绝规则。
|
|
274
|
-
*/
|
|
275
|
-
configureDenyPolicy(
|
|
276
|
-
policy?: RuntimeDenyPolicy,
|
|
277
|
-
): void;
|
|
278
|
-
/**
|
|
279
|
-
* 添加一个已授权 Skill 的持久化 catalog、内容来源及脚本权限。
|
|
280
|
-
*
|
|
281
|
-
* @remarks
|
|
282
|
-
* skill Plugin 为已解析的外部 Skill 逐个调用。
|
|
283
|
-
*
|
|
284
|
-
* catalog、来源与权限一起登记,同时和其他 Skill 来源共享同一名称空间。
|
|
285
|
-
*/
|
|
286
|
-
addSkillSource(
|
|
287
|
-
name: string,
|
|
288
|
-
description: string,
|
|
289
|
-
source: SkillSource,
|
|
290
|
-
script?: Partial<RuntimeSkillScriptPolicy>,
|
|
291
|
-
): void;
|
|
292
|
-
/**
|
|
293
|
-
* 按名称添加一个 MCP Connector。
|
|
294
|
-
*
|
|
295
|
-
* @remarks
|
|
296
|
-
* connector Plugin 为当前 Agent 已授权的连接逐个调用。
|
|
297
|
-
*
|
|
298
|
-
* Runtime 在装配期统一规范化名称和 URL,并拒绝空值或重名。
|
|
299
|
-
*/
|
|
300
|
-
addConnector(server: RuntimeMcpServer): void;
|
|
301
|
-
/**
|
|
302
|
-
* 按名称添加一个已验证的 Runtime Extension。
|
|
303
|
-
*
|
|
304
|
-
* @remarks
|
|
305
|
-
* extension Plugin 为当前部署的扩展逐个调用。
|
|
306
|
-
*
|
|
307
|
-
* 登记名必须和 manifest 名一致,否则配置选择可能指向错误的执行对象。
|
|
308
|
-
*/
|
|
309
|
-
addExtension(name: string, extension: RuntimeExtensionConfig): void;
|
|
310
|
-
/**
|
|
311
|
-
* 写入唯一的定时任务端口。
|
|
312
|
-
*
|
|
313
|
-
* @remarks
|
|
314
|
-
* schedule Plugin 在贡献阶段调用一次。
|
|
315
|
-
*
|
|
316
|
-
* 本端口只描述回到当前 Session 的调用边界;Cloudflare 的实际唤醒和回调路由 Host 与 Agents SDK 管理。
|
|
317
|
-
*/
|
|
318
|
-
bindSchedule(schedule: RuntimeSchedulePort): void;
|
|
319
|
-
/**
|
|
320
|
-
* 标记一个 Subagent 类型在本次 Runtime 中启用。
|
|
321
|
-
*
|
|
322
|
-
* @remarks
|
|
323
|
-
* subagent Plugin 通过部署检查后为每种可用类型调用。
|
|
324
|
-
*
|
|
325
|
-
* Runtime 只把已启用名称交给 Pi,并在提交前确认执行端口存在。
|
|
326
|
-
*/
|
|
327
|
-
enableSubagent(name: string): void;
|
|
328
|
-
/**
|
|
329
|
-
* 写入唯一的 Subagent 执行端口。
|
|
330
|
-
*
|
|
331
|
-
* @remarks
|
|
332
|
-
* subagent Plugin 在至少启用一种类型时调用一次。
|
|
333
|
-
*
|
|
334
|
-
* 类型选择和实际执行能力分开登记,缺少端口时由统一校验拒绝候选结果。
|
|
335
|
-
*/
|
|
336
|
-
bindSubagents(subagents: RuntimeSubagentPort): void;
|
|
337
|
-
/**
|
|
338
|
-
* 写入唯一的 Turn 完成事件端口。
|
|
339
|
-
*
|
|
340
|
-
* @remarks
|
|
341
|
-
* turn-events Plugin 在贡献阶段调用一次。
|
|
342
|
-
*
|
|
343
|
-
* 单一端口保证应用投影只有一个顺序来源,避免同一完成事件被重复写入。
|
|
344
|
-
*/
|
|
345
|
-
bindTurnEvents(turnEvents: RuntimeTurnEventsPort): void;
|
|
346
|
-
/**
|
|
347
|
-
* 添加一个最终提交前必须通过的异步检查。
|
|
348
|
-
*
|
|
349
|
-
* @remarks
|
|
350
|
-
* 会在 loader 执行期间过期的 scope Plugin 在贡献时添加它。
|
|
351
|
-
*
|
|
352
|
-
* Runtime 延迟到全部候选内容就绪后再运行 guard,防止旧业务条件被提交。
|
|
353
|
-
*/
|
|
354
|
-
addCommitGuard(guard: () => Promise<void>): void;
|
|
355
|
-
/**
|
|
356
|
-
* 记录一项不阻止启动的能力降级。
|
|
357
|
-
*
|
|
358
|
-
* @remarks
|
|
359
|
-
* Runtime 在贡献每个 PreparedPlugin 前,把它的降级信息逐项写入。
|
|
360
|
-
*
|
|
361
|
-
* 记录时会复制并冻结顶层对象,避免已提交诊断被 Plugin 后续改写。
|
|
362
|
-
*/
|
|
363
|
-
reportDegradation(degradation: RuntimeDegradation): void;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* 把 loader 与贡献回调包装成 Runtime 可准备的 Plugin。
|
|
368
|
-
*
|
|
369
|
-
* @remarks
|
|
370
|
-
* 应用在创建 `AgentConfig` 时调用它,不需要手写 `prepare`。
|
|
371
|
-
*
|
|
372
|
-
* 包装层会冻结准备结果,避免读取完成后被调用方改写。
|
|
373
|
-
*/
|
|
374
|
-
export function definePlugin<T>(
|
|
375
|
-
spec: AgentPluginSpec<T>,
|
|
376
|
-
): AgentPlugin {
|
|
377
|
-
return Object.freeze({
|
|
378
|
-
kind: spec.kind,
|
|
379
|
-
|
|
380
|
-
// 作用:读取本 Plugin 的数据,并把结果变成不可变的准备对象。
|
|
381
|
-
// 调用:Runtime 在候选装配开始时调用,应用不应直接调用。
|
|
382
|
-
// 原因:此处只准备数据,不贡献能力,才能安全地与其他 loader 并行。
|
|
383
|
-
async prepare(): Promise<PreparedPlugin> {
|
|
384
|
-
const loaded = await spec.loader();
|
|
385
|
-
const degradations = Object.freeze(
|
|
386
|
-
loaded.degradations.map((degradation) =>
|
|
387
|
-
Object.freeze({ ...degradation }),
|
|
388
|
-
),
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
return Object.freeze({
|
|
392
|
-
kind: spec.kind,
|
|
393
|
-
degradations,
|
|
394
|
-
contribute: (ctx: RuntimeContributionContext) =>
|
|
395
|
-
spec.contribute(loaded.value, ctx),
|
|
396
|
-
});
|
|
397
|
-
},
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// #endregion
|
|
402
|
-
|
|
403
|
-
// #region 内部候选契约与装配顺序
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* 保存一次已经完成校验、可供 Kernel 使用的 Runtime 结果。
|
|
407
|
-
*
|
|
408
|
-
* @internal
|
|
409
|
-
*/
|
|
410
|
-
export interface RuntimeSnapshot {
|
|
411
|
-
readonly profile: RuntimeProfile;
|
|
412
|
-
readonly bindings: RuntimeBindings;
|
|
413
|
-
readonly pi: PiRuntimeAssembly;
|
|
414
|
-
readonly degradations: readonly RuntimeDegradation[];
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* 保存待提交的 Snapshot 和提交前检查。
|
|
419
|
-
*
|
|
420
|
-
* @internal
|
|
421
|
-
*/
|
|
422
|
-
export interface RuntimeCandidate {
|
|
423
|
-
readonly snapshot: RuntimeSnapshot;
|
|
424
|
-
readonly commitGuards: readonly (() => Promise<void>)[];
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const PLUGIN_ORDER: readonly PluginKind[] = Object.freeze([
|
|
428
|
-
"scope",
|
|
429
|
-
"profile",
|
|
430
|
-
"provider",
|
|
431
|
-
"platform",
|
|
432
|
-
"workspace",
|
|
433
|
-
"sandbox",
|
|
434
|
-
"memory",
|
|
435
|
-
"tool",
|
|
436
|
-
"skill",
|
|
437
|
-
"connector",
|
|
438
|
-
"extension",
|
|
439
|
-
"schedule",
|
|
440
|
-
"subagent",
|
|
441
|
-
"turn-events",
|
|
442
|
-
]);
|
|
443
|
-
|
|
444
|
-
const REQUIRED_PLUGIN_KINDS: readonly PluginKind[] = Object.freeze([
|
|
445
|
-
"profile",
|
|
446
|
-
"provider",
|
|
447
|
-
"platform",
|
|
448
|
-
]);
|
|
449
|
-
|
|
450
|
-
const DISABLED_MEMORY: RuntimeMemoryProfile = Object.freeze({
|
|
451
|
-
enabled: false,
|
|
452
|
-
memoryTokens: 2_000,
|
|
453
|
-
preferencesTokens: 500,
|
|
454
|
-
compactAfterTokens: 100_000,
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// 作用:把外部名称整理成非空字符串。
|
|
458
|
-
// 调用:Builder 在添加具名 Tool、Skill、Connector 或 Extension 时调用。
|
|
459
|
-
// 原因:统一在入口拒绝空名称,后续 Map 和 Set 才有稳定键值。
|
|
460
|
-
function requiredName(value: string, label: string): string {
|
|
461
|
-
const name = value.trim();
|
|
462
|
-
if (!name) throw new Error(`${label} name must not be empty`);
|
|
463
|
-
return name;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// 作用:检查内存预算都是可用的非负数字。
|
|
467
|
-
// 调用:Builder 在生成候选 Snapshot 前调用。
|
|
468
|
-
// 原因:把检查放在提交前,失败时旧 Snapshot 仍保持不变。
|
|
469
|
-
function assertMemoryProfile(profile: RuntimeMemoryProfile): void {
|
|
470
|
-
for (const [name, value] of Object.entries(profile)) {
|
|
471
|
-
if (name === "enabled") continue;
|
|
472
|
-
if (
|
|
473
|
-
typeof value !== "number" ||
|
|
474
|
-
!Number.isFinite(value) ||
|
|
475
|
-
value < 0
|
|
476
|
-
) {
|
|
477
|
-
throw new Error(`Runtime memory ${name} must be a non-negative number`);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// #endregion
|
|
483
|
-
|
|
484
|
-
// #region RuntimeBuilder 贡献接口
|
|
485
|
-
|
|
486
|
-
class RuntimeBuilder implements RuntimeContributionContext {
|
|
487
|
-
private profile?: RuntimeProfileContribution;
|
|
488
|
-
private provider?: RuntimeProviderPort;
|
|
489
|
-
private platform?: RuntimePlatformPort;
|
|
490
|
-
private workspace?: WorkspacePort;
|
|
491
|
-
private sandbox?: RuntimeSandboxPort;
|
|
492
|
-
private memoryProfile: RuntimeMemoryProfile = DISABLED_MEMORY;
|
|
493
|
-
private memory?: RuntimeMemoryPort;
|
|
494
|
-
private memoryConfigured = false;
|
|
495
|
-
private denyPolicy?: RuntimeDenyPolicy;
|
|
496
|
-
private denyPolicyConfigured = false;
|
|
497
|
-
private readonly piTools = new Map<string, PiToolCandidate>();
|
|
498
|
-
private readonly skillSources = new Map<
|
|
499
|
-
string,
|
|
500
|
-
{
|
|
501
|
-
name: string;
|
|
502
|
-
description: string;
|
|
503
|
-
source: SkillSource;
|
|
504
|
-
script: RuntimeSkillScriptPolicy;
|
|
505
|
-
}
|
|
506
|
-
>();
|
|
507
|
-
private readonly skillDefinitions = new Set<string>();
|
|
508
|
-
private readonly connectors = new Map<string, RuntimeMcpServer>();
|
|
509
|
-
private readonly extensions = new Map<string, RuntimeExtensionConfig>();
|
|
510
|
-
private schedule?: RuntimeSchedulePort;
|
|
511
|
-
private readonly enabledSubagents = new Set<string>();
|
|
512
|
-
private subagents?: RuntimeSubagentPort;
|
|
513
|
-
private turnEvents?: RuntimeTurnEventsPort;
|
|
514
|
-
private readonly commitGuards: Array<() => Promise<void>> = [];
|
|
515
|
-
private readonly degradations: RuntimeDegradation[] = [];
|
|
516
|
-
|
|
517
|
-
// 作用:给 Plugin 一组受限的候选写入方法。
|
|
518
|
-
// 调用:候选装配开始后创建一次,再交给每个 contribute 回调。
|
|
519
|
-
// 原因:返回冻结门面,防止 Plugin 保留或修改 Builder 自身状态。
|
|
520
|
-
contributionContext(): RuntimeContributionContext {
|
|
521
|
-
return Object.freeze({
|
|
522
|
-
configureProfile: this.configureProfile.bind(this),
|
|
523
|
-
bindProvider: this.bindProvider.bind(this),
|
|
524
|
-
bindPlatform: this.bindPlatform.bind(this),
|
|
525
|
-
bindWorkspace: this.bindWorkspace.bind(this),
|
|
526
|
-
bindSandbox: this.bindSandbox.bind(this),
|
|
527
|
-
configureMemory: this.configureMemory.bind(this),
|
|
528
|
-
addPiTool: this.addPiTool.bind(this),
|
|
529
|
-
configureDenyPolicy:
|
|
530
|
-
this.configureDenyPolicy.bind(this),
|
|
531
|
-
addSkillSource: this.addSkillSource.bind(this),
|
|
532
|
-
addConnector: this.addConnector.bind(this),
|
|
533
|
-
addExtension: this.addExtension.bind(this),
|
|
534
|
-
bindSchedule: this.bindSchedule.bind(this),
|
|
535
|
-
enableSubagent: this.enableSubagent.bind(this),
|
|
536
|
-
bindSubagents: this.bindSubagents.bind(this),
|
|
537
|
-
bindTurnEvents: this.bindTurnEvents.bind(this),
|
|
538
|
-
addCommitGuard: this.addCommitGuard.bind(this),
|
|
539
|
-
reportDegradation:
|
|
540
|
-
this.reportDegradation.bind(this),
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// 作用:记录本次装配唯一的运行参数。
|
|
545
|
-
// 调用:profile Plugin 在贡献阶段调用一次。
|
|
546
|
-
// 原因:重复配置通常意味着装配歧义,因此立即拒绝。
|
|
547
|
-
configureProfile(profile: RuntimeProfileContribution): void {
|
|
548
|
-
if (this.profile) {
|
|
549
|
-
throw new Error("Runtime profile was contributed more than once");
|
|
550
|
-
}
|
|
551
|
-
if (!EXECUTION_LEVELS.includes(profile.executionLevel)) {
|
|
552
|
-
throw new Error("Runtime execution level is invalid");
|
|
553
|
-
}
|
|
554
|
-
this.profile = profile;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// 作用:记录本次装配唯一的模型 Provider。
|
|
558
|
-
// 调用:provider Plugin 在贡献阶段调用一次。
|
|
559
|
-
// 原因:默认模型和端点必须从同一份配置校验,不能静默合并。
|
|
560
|
-
bindProvider(provider: RuntimeProviderPort): void {
|
|
561
|
-
if (this.provider) {
|
|
562
|
-
throw new Error("Runtime Provider was contributed more than once");
|
|
563
|
-
}
|
|
564
|
-
this.provider = provider;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// 作用:记录本次装配唯一的平台能力。
|
|
568
|
-
// 调用:platform Plugin 在贡献阶段调用一次。
|
|
569
|
-
// 原因:Loader 与出口端口必须来自同一个平台配置,不能静默拼接。
|
|
570
|
-
bindPlatform(platform: RuntimePlatformPort): void {
|
|
571
|
-
if (this.platform) {
|
|
572
|
-
throw new Error("Runtime platform was contributed more than once");
|
|
573
|
-
}
|
|
574
|
-
this.platform = platform;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
// 作用:记录本次装配可选的工作区。
|
|
578
|
-
// 调用:workspace Plugin 在成功加载工作区后调用。
|
|
579
|
-
// 原因:工作区只有一个当前目录语义,重复绑定会产生不确定行为。
|
|
580
|
-
bindWorkspace(workspace: WorkspacePort): void {
|
|
581
|
-
if (this.workspace) {
|
|
582
|
-
throw new Error("Runtime workspace was contributed more than once");
|
|
583
|
-
}
|
|
584
|
-
this.workspace = workspace;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// 作用:记录本次候选唯一的 Linux Sandbox 执行端口。
|
|
588
|
-
// 调用:sandbox Plugin 在部署与 Agent 双开关都通过后调用。
|
|
589
|
-
// 原因:一个 Chat 只能指向一个由 Host 选定的 Sandbox,重复绑定必须失败。
|
|
590
|
-
bindSandbox(
|
|
591
|
-
sandbox: RuntimeSandboxPort,
|
|
592
|
-
): void {
|
|
593
|
-
if (this.sandbox) {
|
|
594
|
-
throw new Error("Runtime Sandbox was contributed more than once");
|
|
595
|
-
}
|
|
596
|
-
this.sandbox = sandbox;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
// 作用:记录内存预算和可选读写端口。
|
|
600
|
-
// 调用:memory Plugin 在贡献阶段调用一次。
|
|
601
|
-
// 原因:即使内存关闭也要记录“已经配置”,以便拒绝第二份冲突配置。
|
|
602
|
-
configureMemory(
|
|
603
|
-
profile: RuntimeMemoryProfile,
|
|
604
|
-
port?: RuntimeMemoryPort,
|
|
605
|
-
): void {
|
|
606
|
-
if (this.memoryConfigured) {
|
|
607
|
-
throw new Error("Runtime memory was contributed more than once");
|
|
608
|
-
}
|
|
609
|
-
this.memoryConfigured = true;
|
|
610
|
-
this.memoryProfile = profile;
|
|
611
|
-
this.memory = port;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// 作用:把一个已授权的 Pi Tool 加入候选 Agent 输入。
|
|
615
|
-
// 调用:tool Plugin 在装配期为实际可见的能力逐个调用。
|
|
616
|
-
// 原因:Pi Tool 自带名称,同名能力必须在进入 Agent 前失败。
|
|
617
|
-
addPiTool(candidate: PiToolCandidate): void {
|
|
618
|
-
const name = requiredName(candidate.tool.name, "Pi Tool");
|
|
619
|
-
if (!EXECUTION_LEVELS.includes(candidate.requiredExecutionLevel)) {
|
|
620
|
-
throw new Error(`Pi Tool ${name} execution level is invalid`);
|
|
621
|
-
}
|
|
622
|
-
if (this.piTools.has(name)) {
|
|
623
|
-
throw new Error(`Duplicate Runtime Pi Tool: ${name}`);
|
|
624
|
-
}
|
|
625
|
-
this.piTools.set(name, candidate);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// 作用:记录 Turn 期拒绝清单。
|
|
629
|
-
// 调用:tool Plugin 完成 Action 挑选后调用一次。
|
|
630
|
-
// 原因:空策略也是有效配置,所以用独立标记判断是否重复贡献。
|
|
631
|
-
configureDenyPolicy(
|
|
632
|
-
policy?: RuntimeDenyPolicy,
|
|
633
|
-
): void {
|
|
634
|
-
if (this.denyPolicyConfigured) {
|
|
635
|
-
throw new Error(
|
|
636
|
-
"Runtime deny policy was contributed more than once",
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
this.denyPolicyConfigured = true;
|
|
640
|
-
this.denyPolicy = policy;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
// 作用:加入一个 Skill 的持久化 catalog、内容来源及其独立脚本权限。
|
|
644
|
-
// 调用:skill Plugin 为已绑定的 Resource 逐个调用。
|
|
645
|
-
// 原因:catalog、来源和权限一起登记,装配无需读取来源且权限不会在 Skill 之间漂移。
|
|
646
|
-
addSkillSource(
|
|
647
|
-
name: string,
|
|
648
|
-
description: string,
|
|
649
|
-
source: SkillSource,
|
|
650
|
-
script: Partial<RuntimeSkillScriptPolicy> = {},
|
|
651
|
-
): void {
|
|
652
|
-
const normalized = this.addSkillDefinition(name);
|
|
653
|
-
const normalizedDescription = requiredName(
|
|
654
|
-
description,
|
|
655
|
-
`Runtime Skill "${normalized}" description`,
|
|
656
|
-
);
|
|
657
|
-
const network = script.network ?? "none";
|
|
658
|
-
if (network !== "none" && network !== "full") {
|
|
659
|
-
throw new Error(
|
|
660
|
-
`Runtime Skill "${normalized}" has invalid network permission`,
|
|
661
|
-
);
|
|
662
|
-
}
|
|
663
|
-
const workspace = script.workspace ?? "none";
|
|
664
|
-
if (
|
|
665
|
-
workspace !== "none" &&
|
|
666
|
-
workspace !== "read" &&
|
|
667
|
-
workspace !== "read-write"
|
|
668
|
-
) {
|
|
669
|
-
throw new Error(
|
|
670
|
-
`Runtime Skill "${normalized}" has invalid workspace permission`,
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
|
-
const tools = [
|
|
674
|
-
...new Set(
|
|
675
|
-
(script.tools ?? []).map((tool) =>
|
|
676
|
-
requiredName(tool, `Runtime Skill "${normalized}" Tool`),
|
|
677
|
-
),
|
|
678
|
-
),
|
|
679
|
-
];
|
|
680
|
-
this.skillSources.set(normalized, Object.freeze({
|
|
681
|
-
name: normalized,
|
|
682
|
-
description: normalizedDescription,
|
|
683
|
-
source,
|
|
684
|
-
script: Object.freeze({
|
|
685
|
-
network,
|
|
686
|
-
workspace,
|
|
687
|
-
tools: Object.freeze(tools),
|
|
688
|
-
}),
|
|
689
|
-
}));
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// 作用:把一个 MCP Connector 加入候选连接表。
|
|
693
|
-
// 调用:connector Plugin 为每个已授权连接逐个调用。
|
|
694
|
-
// 原因:名称和 URL 在提交前规范化,避免运行时才发现空地址或重名。
|
|
695
|
-
addConnector(server: RuntimeMcpServer): void {
|
|
696
|
-
const name = requiredName(server.name, "Connector");
|
|
697
|
-
if (this.connectors.has(name)) {
|
|
698
|
-
throw new Error(`Duplicate Runtime Connector: ${name}`);
|
|
699
|
-
}
|
|
700
|
-
if (!server.url.trim()) {
|
|
701
|
-
throw new Error(`Runtime Connector "${name}" URL must not be empty`);
|
|
702
|
-
}
|
|
703
|
-
this.connectors.set(name, Object.freeze({
|
|
704
|
-
name,
|
|
705
|
-
url: server.url.trim(),
|
|
706
|
-
}));
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// 作用:把一个 Runtime Extension 加入候选扩展表。
|
|
710
|
-
// 调用:extension Plugin 为每个已部署扩展逐个调用。
|
|
711
|
-
// 原因:注册名必须等于 manifest 名,防止配置选择和实际执行对象错位。
|
|
712
|
-
addExtension(name: string, extension: RuntimeExtensionConfig): void {
|
|
713
|
-
const normalized = requiredName(name, "Extension");
|
|
714
|
-
if (this.extensions.has(normalized)) {
|
|
715
|
-
throw new Error(`Duplicate Runtime Extension: ${normalized}`);
|
|
716
|
-
}
|
|
717
|
-
if (extension.manifest.name !== normalized) {
|
|
718
|
-
throw new Error(
|
|
719
|
-
`Runtime Extension name mismatch: expected "${normalized}", got "${extension.manifest.name}"`,
|
|
720
|
-
);
|
|
721
|
-
}
|
|
722
|
-
this.extensions.set(normalized, extension);
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
// 作用:记录定时任务调用端口。
|
|
726
|
-
// 调用:schedule Plugin 在贡献阶段调用一次。
|
|
727
|
-
// 原因:定时任务必须回到当前会话所有者,不能选择多个路由。
|
|
728
|
-
bindSchedule(schedule: RuntimeSchedulePort): void {
|
|
729
|
-
if (this.schedule) {
|
|
730
|
-
throw new Error("Runtime Schedule was contributed more than once");
|
|
731
|
-
}
|
|
732
|
-
this.schedule = schedule;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
// 作用:标记一个 Subagent 类型在本次运行中可用。
|
|
736
|
-
// 调用:subagent Plugin 完成部署检查后逐个调用。
|
|
737
|
-
// 原因:只记录已部署类型,避免模型拿到无法执行的名称。
|
|
738
|
-
enableSubagent(name: string): void {
|
|
739
|
-
this.enabledSubagents.add(requiredName(name, "Subagent"));
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// 作用:记录 Subagent 的实际执行端口。
|
|
743
|
-
// 调用:subagent Plugin 至少启用一种类型时调用一次。
|
|
744
|
-
// 原因:启用列表与执行能力分开校验,缺端口时在提交前失败。
|
|
745
|
-
bindSubagents(subagents: RuntimeSubagentPort): void {
|
|
746
|
-
if (this.subagents) {
|
|
747
|
-
throw new Error("Runtime Subagents were contributed more than once");
|
|
748
|
-
}
|
|
749
|
-
this.subagents = subagents;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// 作用:记录 Turn 完成后的应用投影端口。
|
|
753
|
-
// 调用:turn-events Plugin 在贡献阶段调用一次。
|
|
754
|
-
// 原因:完成事件只有一个顺序来源,重复绑定可能重复写业务状态。
|
|
755
|
-
bindTurnEvents(turnEvents: RuntimeTurnEventsPort): void {
|
|
756
|
-
if (this.turnEvents) {
|
|
757
|
-
throw new Error("Runtime Turn Events were contributed more than once");
|
|
758
|
-
}
|
|
759
|
-
this.turnEvents = turnEvents;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// 作用:登记一个提交前必须再次通过的业务检查。
|
|
763
|
-
// 调用:容易在加载期间过期的 scope Plugin 添加检查时调用。
|
|
764
|
-
// 原因:检查延迟到最终 commit 前,可阻止过期候选覆盖当前 Runtime。
|
|
765
|
-
addCommitGuard(guard: () => Promise<void>): void {
|
|
766
|
-
this.commitGuards.push(guard);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// 作用:把一项可继续运行的能力缺失加入诊断。
|
|
770
|
-
// 调用:Runtime 汇总每个 PreparedPlugin 的降级信息时调用。
|
|
771
|
-
// 原因:复制并冻结对象,避免 Plugin 在提交后改写诊断。
|
|
772
|
-
reportDegradation(degradation: RuntimeDegradation): void {
|
|
773
|
-
this.degradations.push(Object.freeze({ ...degradation }));
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// #endregion
|
|
777
|
-
|
|
778
|
-
// #region RuntimeBuilder 校验与候选生成
|
|
779
|
-
|
|
780
|
-
// 作用:校验全部贡献,并生成一个不可变候选 Runtime。
|
|
781
|
-
// 调用:所有 Plugin 按固定顺序完成 contribute 后调用一次。
|
|
782
|
-
// 原因:所有交叉约束都在这里通过后才返回候选,旧 Snapshot 不会半更新。
|
|
783
|
-
build(): RuntimeCandidate {
|
|
784
|
-
if (!this.profile) {
|
|
785
|
-
throw new Error("Runtime profile Plugin did not configure a profile");
|
|
786
|
-
}
|
|
787
|
-
if (!this.platform) {
|
|
788
|
-
throw new Error("Runtime platform Plugin did not bind a platform");
|
|
789
|
-
}
|
|
790
|
-
if (!this.provider) {
|
|
791
|
-
throw new Error("Runtime provider Plugin did not bind a provider");
|
|
792
|
-
}
|
|
793
|
-
if (!this.profile.model.trim()) {
|
|
794
|
-
throw new Error("Runtime model must not be empty");
|
|
795
|
-
}
|
|
796
|
-
if (
|
|
797
|
-
!this.provider.defaultModel.trim() ||
|
|
798
|
-
this.provider.endpoints.length === 0 ||
|
|
799
|
-
!this.provider.endpoints.some((endpoint) =>
|
|
800
|
-
endpoint.models.includes(this.provider!.defaultModel)
|
|
801
|
-
) ||
|
|
802
|
-
this.provider.endpoints.some((endpoint) =>
|
|
803
|
-
!endpoint.apiKey.trim() ||
|
|
804
|
-
!endpoint.baseURL.trim() ||
|
|
805
|
-
endpoint.models.length === 0
|
|
806
|
-
)
|
|
807
|
-
) {
|
|
808
|
-
throw new Error("No usable Provider credential is configured");
|
|
809
|
-
}
|
|
810
|
-
if (!this.platform.loader) {
|
|
811
|
-
throw new Error("Runtime platform loader is required");
|
|
812
|
-
}
|
|
813
|
-
if (typeof this.platform.outbound !== "function") {
|
|
814
|
-
throw new Error("Runtime platform outbound port is required");
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
assertMemoryProfile(this.memoryProfile);
|
|
818
|
-
if (this.memoryProfile.enabled && (!this.workspace || !this.memory)) {
|
|
819
|
-
throw new Error(
|
|
820
|
-
"Enabled Runtime memory requires Workspace and Memory ports",
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
if (this.sandbox && !this.workspace) {
|
|
824
|
-
throw new Error(
|
|
825
|
-
"Runtime Sandbox requires a persistent Workspace port",
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
if (this.enabledSubagents.size > 0 && !this.subagents) {
|
|
829
|
-
throw new Error(
|
|
830
|
-
"Enabled Runtime Subagents require a Subagent port",
|
|
831
|
-
);
|
|
832
|
-
}
|
|
833
|
-
const profile = freezeRuntimeProfile({
|
|
834
|
-
model: this.profile.model.trim(),
|
|
835
|
-
thinking: this.profile.thinking,
|
|
836
|
-
systemPrompt: this.profile.systemPrompt,
|
|
837
|
-
denyPolicy: this.denyPolicy,
|
|
838
|
-
enabledSubagents: [...this.enabledSubagents],
|
|
839
|
-
mcpServers: [...this.connectors.values()],
|
|
840
|
-
executionLevel: this.profile.executionLevel,
|
|
841
|
-
memory: this.memoryProfile,
|
|
842
|
-
});
|
|
843
|
-
const provider: RuntimeProviderPort = Object.freeze({
|
|
844
|
-
defaultModel: this.provider.defaultModel,
|
|
845
|
-
endpoints: Object.freeze(this.provider.endpoints.map(
|
|
846
|
-
(endpoint) => Object.freeze({
|
|
847
|
-
...endpoint,
|
|
848
|
-
...(endpoint.headers
|
|
849
|
-
? { headers: Object.freeze({ ...endpoint.headers }) }
|
|
850
|
-
: {}),
|
|
851
|
-
models: Object.freeze([...endpoint.models]),
|
|
852
|
-
}),
|
|
853
|
-
)),
|
|
854
|
-
});
|
|
855
|
-
const endpoints = provider.endpoints.filter((endpoint) =>
|
|
856
|
-
endpoint.models.includes(profile.model)
|
|
857
|
-
);
|
|
858
|
-
if (endpoints.length !== 1) {
|
|
859
|
-
throw new Error(
|
|
860
|
-
endpoints.length === 0
|
|
861
|
-
? `Model is not configured by this deployment: ${profile.model}`
|
|
862
|
-
: `Model is configured by multiple endpoints: ${profile.model}`,
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
|
-
const resolvedModel = resolvePiModel(provider, profile.model);
|
|
866
|
-
const endpoint = endpoints[0]!;
|
|
867
|
-
const webSearch = endpoint.protocol === "openrouter-chat"
|
|
868
|
-
? undefined
|
|
869
|
-
: createWebSearch({
|
|
870
|
-
endpoint,
|
|
871
|
-
model: profile.model,
|
|
872
|
-
maxTokens: resolvedModel.maxTokens,
|
|
873
|
-
reasoning: resolvedModel.reasoning,
|
|
874
|
-
});
|
|
875
|
-
if (!webSearch) {
|
|
876
|
-
this.reportDegradation({
|
|
877
|
-
capability: "web_search",
|
|
878
|
-
reason: "unavailable",
|
|
879
|
-
detail: "Native web search is unavailable for openrouter-chat",
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
for (const candidate of basePiToolCandidates(webSearch)) {
|
|
883
|
-
this.addPiTool(candidate);
|
|
884
|
-
}
|
|
885
|
-
const bindings: RuntimeBindings = Object.freeze({
|
|
886
|
-
provider,
|
|
887
|
-
platform: Object.freeze({ ...this.platform }),
|
|
888
|
-
...(this.workspace ? { workspace: this.workspace } : {}),
|
|
889
|
-
...(this.memoryProfile.enabled && this.memory
|
|
890
|
-
? { memory: this.memory }
|
|
891
|
-
: {}),
|
|
892
|
-
skills: Object.freeze({
|
|
893
|
-
sources: Object.freeze([
|
|
894
|
-
...this.skillSources.values(),
|
|
895
|
-
]),
|
|
896
|
-
}),
|
|
897
|
-
...(this.turnEvents ? { turnEvents: this.turnEvents } : {}),
|
|
898
|
-
});
|
|
899
|
-
const snapshot: RuntimeSnapshot = Object.freeze({
|
|
900
|
-
profile,
|
|
901
|
-
bindings,
|
|
902
|
-
pi: createPiRuntimeAssembly({
|
|
903
|
-
profile,
|
|
904
|
-
provider,
|
|
905
|
-
toolCandidates: [...this.piTools.values()],
|
|
906
|
-
mcpServers: profile.mcpServers,
|
|
907
|
-
extensions: [...this.extensions.values()],
|
|
908
|
-
}),
|
|
909
|
-
degradations: Object.freeze([
|
|
910
|
-
...this.degradations,
|
|
911
|
-
]),
|
|
912
|
-
});
|
|
913
|
-
|
|
914
|
-
return Object.freeze({
|
|
915
|
-
snapshot,
|
|
916
|
-
commitGuards: Object.freeze([
|
|
917
|
-
...this.commitGuards,
|
|
918
|
-
]),
|
|
919
|
-
});
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// 作用:登记一个 Skill 名称并返回规范化结果。
|
|
923
|
-
// 调用:Host 文档和外部 Skill 来源写入前调用。
|
|
924
|
-
// 原因:所有 Skill 来源共享一个命名空间,重名必须在装配时暴露。
|
|
925
|
-
private addSkillDefinition(name: string): string {
|
|
926
|
-
const normalized = requiredName(name, "Skill");
|
|
927
|
-
if (this.skillDefinitions.has(normalized)) {
|
|
928
|
-
throw new Error(`Duplicate Runtime Skill: ${normalized}`);
|
|
929
|
-
}
|
|
930
|
-
this.skillDefinitions.add(normalized);
|
|
931
|
-
return normalized;
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
// 作用:检查 Plugin kind 合法、唯一,并包含所有必需能力。
|
|
936
|
-
// 调用:任何 loader 启动前由 `prepareRuntimeCandidate` 调用。
|
|
937
|
-
// 原因:先校验结构可避免为注定失败的配置执行外部读取。
|
|
938
|
-
function validatePluginKinds(
|
|
939
|
-
plugins: readonly AgentPlugin[],
|
|
940
|
-
): void {
|
|
941
|
-
const kinds = new Set<PluginKind>();
|
|
942
|
-
const knownKinds = new Set(PLUGIN_ORDER);
|
|
943
|
-
|
|
944
|
-
for (const plugin of plugins) {
|
|
945
|
-
if (!knownKinds.has(plugin.kind)) {
|
|
946
|
-
throw new Error(`Unknown Agent Plugin kind: ${plugin.kind}`);
|
|
947
|
-
}
|
|
948
|
-
if (kinds.has(plugin.kind)) {
|
|
949
|
-
throw new Error(
|
|
950
|
-
`Duplicate Agent Plugin kind: ${plugin.kind}`,
|
|
951
|
-
);
|
|
952
|
-
}
|
|
953
|
-
kinds.add(plugin.kind);
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
for (const required of REQUIRED_PLUGIN_KINDS) {
|
|
957
|
-
if (!kinds.has(required)) {
|
|
958
|
-
throw new Error(
|
|
959
|
-
`Missing required Agent Plugin: ${required}`,
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
* 在不改动当前 Runtime 的前提下生成一个隔离候选。
|
|
967
|
-
*
|
|
968
|
-
* @remarks
|
|
969
|
-
* `AgentRuntimeKernel` 在初始化或重载时调用它。
|
|
970
|
-
*
|
|
971
|
-
* loader 并行执行,贡献按固定 kind 顺序执行,任何失败都不会产生 commit。
|
|
972
|
-
*
|
|
973
|
-
* 该函数有意不从包入口导出,提交生命周期只能由 Kernel 控制。
|
|
974
|
-
*
|
|
975
|
-
* @internal
|
|
976
|
-
*/
|
|
977
|
-
export async function prepareRuntimeCandidate(
|
|
978
|
-
config: AgentConfig,
|
|
979
|
-
): Promise<RuntimeCandidate> {
|
|
980
|
-
validatePluginKinds(config.plugins);
|
|
981
|
-
|
|
982
|
-
const prepared = await Promise.all(
|
|
983
|
-
config.plugins.map(async (plugin) => {
|
|
984
|
-
const result = await plugin.prepare();
|
|
985
|
-
if (result.kind !== plugin.kind) {
|
|
986
|
-
throw new Error(
|
|
987
|
-
`Prepared Agent Plugin kind mismatch: expected ${plugin.kind}, got ${result.kind}`,
|
|
988
|
-
);
|
|
989
|
-
}
|
|
990
|
-
return result;
|
|
991
|
-
}),
|
|
992
|
-
);
|
|
993
|
-
const byKind = new Map(
|
|
994
|
-
prepared.map((plugin) => [plugin.kind, plugin]),
|
|
995
|
-
);
|
|
996
|
-
const builder = new RuntimeBuilder();
|
|
997
|
-
const contributionContext =
|
|
998
|
-
builder.contributionContext();
|
|
999
|
-
|
|
1000
|
-
for (const kind of PLUGIN_ORDER) {
|
|
1001
|
-
const plugin = byKind.get(kind);
|
|
1002
|
-
if (!plugin) continue;
|
|
1003
|
-
for (const degradation of plugin.degradations) {
|
|
1004
|
-
builder.reportDegradation(degradation);
|
|
1005
|
-
}
|
|
1006
|
-
await plugin.contribute(contributionContext);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
return builder.build();
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
/**
|
|
1013
|
-
* 完成候选装配、提交前检查,并在最后一步安装 Snapshot。
|
|
1014
|
-
*
|
|
1015
|
-
* @remarks
|
|
1016
|
-
* `AgentRuntimeKernel.initConfig` 在首次加载或显式重载时调用它。
|
|
1017
|
-
*
|
|
1018
|
-
* `commit` 只在全部 loader、贡献、校验和 guard 成功后调用一次。
|
|
1019
|
-
*
|
|
1020
|
-
* @internal
|
|
1021
|
-
*/
|
|
1022
|
-
export async function initializeRuntimeConfig(
|
|
1023
|
-
config: AgentConfig,
|
|
1024
|
-
commit: (snapshot: RuntimeSnapshot) => void,
|
|
1025
|
-
): Promise<void> {
|
|
1026
|
-
const candidate = await prepareRuntimeCandidate(config);
|
|
1027
|
-
for (const guard of candidate.commitGuards) {
|
|
1028
|
-
await guard();
|
|
1029
|
-
}
|
|
1030
|
-
commit(candidate.snapshot);
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
// #endregion
|