@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.
Files changed (79) hide show
  1. package/package.json +12 -3
  2. package/src/adapter/cloudflare/index.ts +56 -0
  3. package/src/adapter/cloudflare/resources/runtime-resources.ts +89 -0
  4. package/src/adapter/cloudflare/sandbox/adapter.ts +1513 -0
  5. package/src/adapter/cloudflare/sandbox/id.ts +23 -0
  6. package/src/adapter/cloudflare/sandbox/policy.ts +15 -0
  7. package/src/adapter/cloudflare/subagent/definition.ts +574 -0
  8. package/src/adapter/cloudflare/subagent/runner.ts +175 -0
  9. package/src/adapter/cloudflare/subagent/tools.ts +254 -0
  10. package/src/adapter/cloudflare/universal-agent/hooks.ts +35 -0
  11. package/src/adapter/cloudflare/universal-agent/preparation.ts +277 -0
  12. package/src/adapter/cloudflare/universal-agent/tools.ts +80 -0
  13. package/src/adapter/cloudflare/workspace/git-fs.ts +178 -0
  14. package/src/adapter/cloudflare/workspace/publisher.ts +31 -0
  15. package/src/adapter/cloudflare/workspace/scoped-workspace.ts +376 -0
  16. package/src/adapter/cloudflare/workspace/version-control.ts +374 -0
  17. package/src/agent-tool-runtime.ts +152 -0
  18. package/src/db/agent-tool.repo.ts +27 -0
  19. package/src/db/index.ts +33 -0
  20. package/src/db/interaction.repo.ts +185 -0
  21. package/src/db/schema.ts +25 -1
  22. package/src/db/submission.repo.ts +63 -1
  23. package/src/index.ts +61 -27
  24. package/src/kernel/approval-lifecycle.ts +41 -6
  25. package/src/kernel/bindings.ts +99 -12
  26. package/src/kernel/extensions.ts +1 -1
  27. package/src/kernel/interaction-lifecycle.ts +395 -0
  28. package/src/kernel/profile.ts +3 -4
  29. package/src/kernel/public-contracts.ts +2 -0
  30. package/src/kernel/recoverable-chat-agent.ts +104 -6
  31. package/src/kernel/runtime-assembly-view.ts +37 -0
  32. package/src/kernel/runtime-assembly.ts +41 -0
  33. package/src/kernel/runtime-config.ts +4 -0
  34. package/src/kernel/runtime-load.ts +191 -0
  35. package/src/kernel/state.ts +12 -1
  36. package/src/kernel/submission-lifecycle.ts +33 -2
  37. package/src/layers/orchestration/temporary-agent/core.ts +12 -1
  38. package/src/layers/orchestration/temporary-agent/runner.ts +1 -67
  39. package/src/lib/mcp.ts +7 -3
  40. package/src/lib/prompt.ts +1 -1
  41. package/src/lib/telemetry-dev.ts +7 -4
  42. package/src/pi/assembly/context.ts +4 -6
  43. package/src/pi/assembly/extensions.ts +11 -22
  44. package/src/pi/assembly/snapshot.ts +17 -9
  45. package/src/pi/message/contract.ts +7 -0
  46. package/src/pi/message/conversion.ts +9 -1
  47. package/src/pi/runtime-adapter/assembly.ts +42 -97
  48. package/src/pi/runtime-adapter/execution.ts +216 -21
  49. package/src/pi/runtime-adapter/index.ts +24 -8
  50. package/src/pi/runtime-adapter/models.ts +382 -35
  51. package/src/pi/runtime-adapter/recovery.ts +188 -1
  52. package/src/pi/runtime-adapter/transcript.ts +61 -3
  53. package/src/pi/tool/ai-adapter.ts +58 -1
  54. package/src/pi/tool/base.ts +190 -12
  55. package/src/pi/tool/compiler.ts +39 -33
  56. package/src/pi/tool/core-host.ts +28 -30
  57. package/src/pi/tool/core.ts +37 -124
  58. package/src/pi/tool/gateway.ts +54 -0
  59. package/src/pi/tool/index.ts +2 -0
  60. package/src/pi/tool/mcp.ts +98 -70
  61. package/src/pi/tool/schedule.ts +86 -20
  62. package/src/pi/tool/skill.ts +126 -420
  63. package/src/pi/tool/subagent.ts +14 -2
  64. package/src/pi/tool/web-fetch.ts +281 -0
  65. package/src/pi/tool/web-search/api.ts +34 -18
  66. package/src/pi/tool/web-search/web-search.ts +0 -1
  67. package/src/pi/tool/workspace-revision.ts +64 -0
  68. package/src/pi/tool/workspace-sandbox.ts +105 -263
  69. package/src/pi/turn/index.ts +20 -0
  70. package/src/pi/turn/interaction.ts +181 -0
  71. package/src/pi/turn/tool-recovery.ts +244 -1
  72. package/src/runtime-agent-context.ts +112 -0
  73. package/src/runtime-agent.ts +568 -321
  74. package/src/runtime-assembler.ts +797 -0
  75. package/src/runtime-definition.ts +175 -0
  76. package/src/runtime.ts +840 -208
  77. package/src/tool-registry.ts +143 -0
  78. package/src/workspace-versioning.ts +46 -0
  79. package/src/plugins.ts +0 -1033
@@ -6,7 +6,6 @@ import type {
6
6
  import type { ApprovalReceipt } from "../../kernel/receipts";
7
7
  import { boundDurableToolOutput } from "../../layers/context/budget/gate";
8
8
  import {
9
- EXECUTION_LEVELS,
10
9
  type ExecutionLevel,
11
10
  } from "../../lib/execution-level";
12
11
 
@@ -14,19 +13,51 @@ import {
14
13
 
15
14
  // #region Public contracts
16
15
 
17
- /** 描述一个尚未经过去重、禁用规则和结算包装的 Pi 工具。 */
16
+ /**
17
+ * 声明一个 Tool 的结果由客户端投递,而不是由 `execute()` 产出。
18
+ *
19
+ * 带这个字段的 Tool 会在执行链里 park 住,等宿主经 `respondToolInteraction` 把响应送进来,
20
+ * 再用 `settle` 把响应映射成这次 `toolCallId` 的权威 ToolResult。它的 `execute` 不会被调用。
21
+ *
22
+ * 这是 AI SDK 里「没有 execute 的 user-interaction tool」的等价物;差别是我们 park 住整个 Turn,
23
+ * 而不是让 tool part 悬空到下一次请求 —— 我们的 transcript 是服务端权威的,悬空的 tool_use
24
+ * 会让下一次模型调用非法。
25
+ */
26
+ export interface PiToolInteractionSpec {
27
+ /** 校验客户端响应体;不通过则拒绝投递,park 保持不变。 */
28
+ readonly validateResponse: (response: unknown) => boolean;
29
+ /** 把通过校验的响应映射成 ToolResult;缺省是原样回传。 */
30
+ readonly settle?: (
31
+ input: unknown,
32
+ response: unknown,
33
+ ) => AgentToolResult<unknown>;
34
+ }
35
+
36
+ /** 描述一个尚未进入最终 Tool Surface 和结算包装的 Pi 工具。 */
18
37
  export interface PiToolCandidate {
19
38
  readonly owner: string;
20
- readonly authorized: boolean;
21
39
  readonly tool: AgentTool<any, any>;
40
+ /** Conservative maximum used in the stable Runtime descriptor. */
22
41
  readonly requiredExecutionLevel: ExecutionLevel;
42
+ /** Trusted parameter-level policy, evaluated before approval or dispatch. */
43
+ readonly requiredExecutionLevelForInput?: (
44
+ input: unknown,
45
+ ) => ExecutionLevel | Promise<ExecutionLevel>;
46
+ /**
47
+ * 无条件需要人来决定:不看执行档位矩阵,每次调用都 park 出审批。
48
+ *
49
+ * 档位差是「够不够格自己跑」,不是「该不该问人」。花用户钱、改变账户
50
+ * 状态这类工具,一旦 Agent 档位被 `allow_level` 永久拉高就会静默执行,
51
+ * 那是失效不是降级。`deny` 仍然优先,被 deny 的工具根本不会注入。
52
+ */
53
+ readonly alwaysRequiresApproval?: boolean;
23
54
  readonly summary?: string;
24
55
  readonly source?: ApprovalReceipt["source"];
56
+ readonly interaction?: PiToolInteractionSpec;
25
57
  }
26
58
 
27
59
  /** 控制候选 Pi 工具如何被编译为可执行工具集。 */
28
60
  export interface CompilePiToolsOptions {
29
- readonly deny?: readonly string[];
30
61
  readonly governance?: PiToolGovernance;
31
62
  readonly settle: (
32
63
  call: SettledPiToolCall,
@@ -248,7 +279,7 @@ export function createPiToolGovernance(
248
279
  }
249
280
 
250
281
  // 为一个候选工具包上输出限额、持久化结算、重试治理和遥测。
251
- // `compilePiTools()` 对通过授权和 deny 筛选的每个候选项调用。
282
+ // `compilePiTools()` 对最终 Tool Surface 中的每个候选项调用。
252
283
  // 一个共享包装边界可确保所有工具无论成功还是失败都经过 settle,不能由各工具自行选择是否持久化。
253
284
  function governedTool(
254
285
  candidate: PiToolCandidate,
@@ -402,11 +433,9 @@ function governedTool(
402
433
  }
403
434
 
404
435
  /**
405
- * 把已授权且未被禁用的 Pi 工具候选项编译成可执行工具集。
436
+ * 把已经 Tool Surface 最终化的候选项包成可执行工具集。
406
437
  *
407
- * Runtime 组装层在启动前用它验证工具集,`PiTurnAdapter` 在每个 Turn 及手动重试时用它创建执行包装。
408
- *
409
- * 工具名是模型调用和结算的唯一键,所以冲突必须在组装时失败,不能用后来者静默覆盖。
438
+ * `PiTurnAdapter` 在每个 Turn 及手动重试时用它创建执行治理包装。
410
439
  */
411
440
  export function compilePiTools(
412
441
  candidates: readonly PiToolCandidate[],
@@ -415,30 +444,7 @@ export function compilePiTools(
415
444
  if (!options?.settle) {
416
445
  throw new Error("Pi Tool settlement port is required");
417
446
  }
418
- const deny = new Set(options.deny ?? []);
419
- const tools: AgentTool<any, any>[] = [];
420
- const owners = new Map<string, string>();
421
-
422
- for (const candidate of candidates) {
423
- if (!EXECUTION_LEVELS.includes(candidate.requiredExecutionLevel)) {
424
- throw new Error(
425
- `Pi Tool "${candidate.tool.name}" has invalid requiredExecutionLevel`,
426
- );
427
- }
428
- if (!candidate.authorized || deny.has(candidate.tool.name)) continue;
429
-
430
- const existingOwner = owners.get(candidate.tool.name);
431
- if (existingOwner !== undefined) {
432
- throw new Error(
433
- `duplicate tool key "${candidate.tool.name}" (${candidate.owner} collides with ${existingOwner})`,
434
- );
435
- }
436
-
437
- owners.set(candidate.tool.name, candidate.owner);
438
- tools.push(governedTool(candidate, options));
439
- }
440
-
441
- return tools;
447
+ return candidates.map((candidate) => governedTool(candidate, options));
442
448
  }
443
449
 
444
450
  // #endregion
@@ -1,52 +1,50 @@
1
- import {
2
- createCodemodeRuntime,
3
- DynamicWorkerExecutor,
4
- truncateResult,
5
- } from "@cloudflare/codemode";
6
1
  import {
7
2
  createWorkspaceStateBackend,
8
3
  type WorkspaceFsLike,
9
4
  } from "@cloudflare/shell";
10
- import { StateConnector } from "@cloudflare/shell/workers";
11
- import type { WorkspacePort } from "../../kernel/bindings";
12
- import { codeExecutionPiToolCandidate } from "./core";
13
- import type { PiToolCandidate } from "./compiler";
5
+ import { createExecuteRuntime } from "@cloudflare/think/tools/execute";
6
+ import type {
7
+ RuntimeCodeExecutionPort,
8
+ WorkspacePort,
9
+ } from "../../kernel/bindings";
14
10
 
15
11
  // 本文件沿用 `../../index.ts` 入口定义的 Workspace、Port 和 Tool Candidate 术语。
16
12
 
17
13
  const CODEMODE_SANDBOX_TIMEOUT_MS = 55_000;
18
14
 
19
15
  /**
20
- * 把宿主的 Worker Loader、出站网络和 Workspace 组装成 Pi 代码执行工具候选项。
16
+ * 把宿主的 Worker Loader、出站网络和 Workspace 组装成代码执行 Port。
21
17
  *
22
18
  * Worker 宿主在具备 Durable Object state 和完整平台绑定时调用,然后把返回值交给 Runtime 工具组装。
23
19
  *
24
- * Cloudflare Codemode 把持久化 Runtime 和无状态 Dynamic Worker executor 分开;这里必须用 StateConnector 显式注入已限定范围的 Workspace,并在返回模型前用官方 `truncateResult` 限制结果。
20
+ * Think 的独立 execute factory 接受显式宿主参数,不要求 Agent 继承 Think;这里只借它组装 Codemode Runtime、Dynamic Worker executor 和已限定范围的 Workspace state connector。
25
21
  */
26
- export function workspaceCodeExecutionPiToolCandidate(options: {
22
+ export function createWorkspaceCodeExecutionPort(options: {
27
23
  readonly ctx: DurableObjectState;
28
24
  readonly loader: WorkerLoader;
29
25
  readonly outbound: Fetcher;
30
26
  readonly workspace: WorkspacePort;
31
- }): PiToolCandidate {
32
- const runtime = createCodemodeRuntime({
27
+ }): RuntimeCodeExecutionPort {
28
+ const { tool } = createExecuteRuntime({
33
29
  ctx: options.ctx,
34
- executor: new DynamicWorkerExecutor({
35
- loader: options.loader,
36
- globalOutbound: options.outbound,
37
- // 先于外层 60s 截止结束,给 Runtime RPC 结算和 Worker 释放留出时间。
38
- timeout: CODEMODE_SANDBOX_TIMEOUT_MS,
39
- }),
40
- connectors: [
41
- new StateConnector(
42
- options.ctx,
43
- createWorkspaceStateBackend(
44
- options.workspace as unknown as WorkspaceFsLike,
45
- ),
46
- ),
47
- ],
30
+ loader: options.loader,
31
+ globalOutbound: options.outbound,
32
+ // 先于外层 60s 截止结束,给 Runtime RPC 结算和 Worker 释放留出时间。
33
+ timeout: CODEMODE_SANDBOX_TIMEOUT_MS,
34
+ state: createWorkspaceStateBackend(
35
+ options.workspace as unknown as WorkspaceFsLike,
36
+ ),
48
37
  name: "execute",
49
- transformResult: truncateResult,
50
38
  });
51
- return codeExecutionPiToolCandidate(runtime);
39
+ const execute = tool.execute;
40
+ if (typeof execute !== "function") {
41
+ throw new Error("Think createExecuteRuntime returned a non-executable tool");
42
+ }
43
+ return {
44
+ execute: (input) => Promise.resolve(execute(input, {
45
+ toolCallId: "execute",
46
+ messages: [],
47
+ context: undefined,
48
+ })),
49
+ };
52
50
  }
@@ -1,20 +1,21 @@
1
+ import { createQuickActionTools } from "@cloudflare/think/tools/browser";
1
2
  import type {
2
3
  AgentTool,
3
4
  AgentToolResult,
4
5
  } from "@earendil-works/pi-agent-core";
5
6
  import { Type } from "@earendil-works/pi-ai";
6
- import type { CodemodeRuntimeHandle } from "@cloudflare/codemode";
7
- import {
8
- browserExtract,
9
- browserLinks,
10
- browserMarkdown,
11
- browserScrape,
12
- type QuickActionPage,
13
- } from "agents/browser";
14
- import type { RuntimeBrowserPort } from "../../kernel/bindings";
7
+ import type {
8
+ RuntimeBrowserPort,
9
+ RuntimeCodeExecutionPort,
10
+ } from "../../kernel/bindings";
15
11
  import { serializeOutput } from "../../lib/artifacts";
16
12
  import type { PiLoadedExtension } from "../assembly/extensions";
13
+ import { aiToolToPi } from "./ai-adapter";
17
14
  import type { PiToolCandidate } from "./compiler";
15
+ import {
16
+ toolRegistryFromPiCandidates,
17
+ type ToolRegistry,
18
+ } from "../../tool-registry";
18
19
 
19
20
  // 本文件沿用 `../../index.ts` 入口定义的 Extension、Port 和 Tool Candidate 术语。
20
21
 
@@ -30,128 +31,32 @@ function result(details: unknown): AgentToolResult<unknown> {
30
31
 
31
32
  // #region Browser Quick Actions
32
33
 
33
- const pageParameters = {
34
- url: Type.Optional(Type.String({ format: "uri" })),
35
- html: Type.Optional(Type.String({ minLength: 1 })),
34
+ const BROWSER_TOOL_LABELS: Readonly<Record<string, string>> = {
35
+ browser_markdown: "Read web page",
36
+ browser_extract: "Extract web data",
37
+ browser_links: "List web links",
38
+ browser_scrape: "Scrape web elements",
36
39
  };
37
- const browserPageParameters = Type.Object(pageParameters);
38
- const browserExtractParameters = Type.Object({
39
- ...pageParameters,
40
- prompt: Type.Optional(Type.String({ minLength: 1 })),
41
- schema: Type.Optional(Type.Unknown()),
42
- });
43
- const browserScrapeParameters = Type.Object({
44
- ...pageParameters,
45
- selectors: Type.Array(Type.String({ minLength: 1 }), {
46
- minItems: 1,
47
- }),
48
- });
49
-
50
- // 把工具输入收窄为 Browser Quick Action 接受的页面来源。
51
- // 四个 Browser Quick Action execute 方法在调用 Agents SDK 前共用它。
52
- // 实际运行时再拒绝空输入,而同时给出 url 和 html 时优先 url,不能把不完整来源传给宿主绑定。
53
- function page(input: {
54
- url?: string;
55
- html?: string;
56
- }): QuickActionPage {
57
- if (input.url) return { url: input.url };
58
- if (input.html) return { html: input.html };
59
- throw new Error("Provide either 'url' or 'html'");
60
- }
61
40
 
62
41
  /**
63
42
  * 为 Cloudflare Browser Run 的四个一次性 Quick Action 创建 Pi 工具候选项。
64
43
  *
65
44
  * Runtime 在宿主提供 Browser port 时调用,模型分别用它读取 Markdown、抽取数据、列出链接或按选择器抓取。
66
45
  *
67
- * Cloudflare 官方将 Quick Actions 定位为只需 browser binding 的无状态单次操作,所以这里直接调用四个宿主函数,不引入额外会话或重试状态。
46
+ * Cloudflare 官方将 Quick Actions 定位为只需 browser binding 的无状态单次操作,这里直接复用官方工厂。
68
47
  */
69
48
  export function browserQuickActionPiToolCandidates(
70
49
  browser: RuntimeBrowserPort,
71
50
  ): PiToolCandidate[] {
72
- const markdown: AgentTool<typeof browserPageParameters> = {
73
- name: "browser_markdown",
74
- label: "Read web page",
75
- description:
76
- "Load a web page (or render raw HTML) and return its content as Markdown. Best for reading articles, docs, or any page as text.",
77
- parameters: browserPageParameters,
78
- // 把 URL 或 HTML 读成 Markdown。
79
- // Pi 工具循环在模型选择 `browser_markdown` 时调用,调用前允许 Turn 取消。
80
- // 取消检查必须放在 Browser binding 调用前,避免已终止 Turn 继续发起页面请求。
81
- async execute(_toolCallId, input, signal) {
82
- signal?.throwIfAborted();
83
- return result(await browserMarkdown(browser, page(input)));
84
- },
85
- };
86
- const extract: AgentTool<typeof browserExtractParameters> = {
87
- name: "browser_extract",
88
- label: "Extract web data",
89
- description:
90
- "Extract structured data from a web page using AI. Describe what you want in 'prompt'. Passing a JSON Schema in 'schema' is strongly recommended — without one the extractor often fails to produce JSON.",
91
- parameters: browserExtractParameters,
92
- // 按自然语言提示或 JSON Schema 从页面抽取数据。
93
- // Pi 工具循环在模型选择 `browser_extract` 时调用,至少需要 prompt 或 schema 之一。
94
- // schema 必须转成 Browser Run 期望的 `json_schema` response_format,而空抽取要求必须在发起请求前失败。
95
- async execute(
96
- _toolCallId,
97
- { prompt, schema, ...input },
98
- signal,
99
- ) {
100
- signal?.throwIfAborted();
101
- if (!prompt && schema === undefined) {
102
- throw new Error("Provide either 'prompt' or 'schema'");
103
- }
104
- return result(await browserExtract(browser, {
105
- ...page(input),
106
- prompt,
107
- response_format: schema === undefined
108
- ? undefined
109
- : { type: "json_schema", schema },
110
- }));
111
- },
112
- };
113
- const links: AgentTool<typeof browserPageParameters> = {
114
- name: "browser_links",
115
- label: "List web links",
116
- description:
117
- "Return every link found on a web page (including ones not visible). Useful for discovering pages to follow.",
118
- parameters: browserPageParameters,
119
- // 列出 URL 或 HTML 页面中的链接。
120
- // Pi 工具循环在模型选择 `browser_links` 时调用,调用前允许 Turn 取消。
121
- // 保留 Agents SDK 的原始结果形状,让统一 result 边界负责文本投影而不在此处二次整形。
122
- async execute(_toolCallId, input, signal) {
123
- signal?.throwIfAborted();
124
- return result(await browserLinks(browser, page(input)));
125
- },
126
- };
127
- const scrape: AgentTool<typeof browserScrapeParameters> = {
128
- name: "browser_scrape",
129
- label: "Scrape web elements",
130
- description:
131
- "Scrape specific elements from a web page by CSS selector. Returns the matched elements' text, HTML, and attributes.",
132
- parameters: browserScrapeParameters,
133
- // 按 CSS 选择器抓取 URL 或 HTML 页面的指定元素。
134
- // Pi 工具循环在模型选择 `browser_scrape` 时调用,并把简化的字符串列表传入。
135
- // Browser Run 需要 `{ selector }` 对象列表,因此这个适配只在边界做一次形状转换。
136
- async execute(
137
- _toolCallId,
138
- { selectors, ...input },
139
- signal,
140
- ) {
141
- signal?.throwIfAborted();
142
- return result(await browserScrape(browser, {
143
- ...page(input),
144
- elements: selectors.map((selector) => ({ selector })),
145
- }));
146
- },
147
- };
148
-
149
- return [markdown, extract, links, scrape].map((tool) => ({
150
- owner: "core:browser",
151
- authorized: true,
152
- requiredExecutionLevel: "low",
153
- tool,
154
- }));
51
+ return Object.entries(createQuickActionTools({ browser })).map(
52
+ ([name, tool]) => ({
53
+ owner: "core:browser",
54
+ requiredExecutionLevel: "low",
55
+ tool: aiToolToPi(name, tool, {
56
+ label: BROWSER_TOOL_LABELS[name] ?? name,
57
+ }),
58
+ }),
59
+ );
155
60
  }
156
61
 
157
62
  // #endregion
@@ -188,7 +93,6 @@ export function listExtensionsPiToolCandidate(
188
93
  };
189
94
  return {
190
95
  owner: "core:extensions",
191
- authorized: true,
192
96
  requiredExecutionLevel: "low",
193
97
  tool,
194
98
  };
@@ -210,12 +114,13 @@ const CODEMODE_EXECUTE_TIMEOUT_MS = 60_000;
210
114
  /**
211
115
  * 把 Cloudflare Codemode Runtime handle 包装为高风险 Pi 代码执行工具候选项。
212
116
  *
213
- * Worker 宿主用 `workspaceCodeExecutionPiToolCandidate()` 组装好持久化 Codemode Runtime 后调用,模型再通过 `execute` 运行代码。
117
+ * Tool Surface 收到 Host 已组装的 Code Execution Port 后调用,
118
+ * 模型再通过 `execute` 运行代码。
214
119
  *
215
120
  * Codemode 代码可访问出站网络和已配置 connector,因此必须保留 high 档和 codemode source 标记,不能降级成普通本地计算工具。
216
121
  */
217
122
  export function codeExecutionPiToolCandidate(
218
- runtime: Pick<CodemodeRuntimeHandle, "execute">,
123
+ runtime: RuntimeCodeExecutionPort,
219
124
  ): PiToolCandidate {
220
125
  const tool: AgentTool<typeof executeParameters> = {
221
126
  name: "execute",
@@ -267,7 +172,6 @@ export function codeExecutionPiToolCandidate(
267
172
  };
268
173
  return {
269
174
  owner: "core:codemode",
270
- authorized: true,
271
175
  requiredExecutionLevel: "high",
272
176
  source: "codemode",
273
177
  summary: "Run JavaScript with network and configured connector access",
@@ -275,4 +179,13 @@ export function codeExecutionPiToolCandidate(
275
179
  };
276
180
  }
277
181
 
182
+ /** 从 Codemode Runtime Port 生成 `execute` Tool。 */
183
+ export function createCodeExecutionTool(
184
+ runtime: RuntimeCodeExecutionPort,
185
+ ): ToolRegistry {
186
+ return toolRegistryFromPiCandidates([
187
+ codeExecutionPiToolCandidate(runtime),
188
+ ]);
189
+ }
190
+
278
191
  // #endregion
@@ -0,0 +1,54 @@
1
+ import type { RuntimeGatewaySession } from "../../kernel/bindings";
2
+ import type { PiToolCandidate } from "./compiler";
3
+ import { createPiMcpToolCandidate } from "./mcp";
4
+
5
+ const GATEWAY_TOOLS = [
6
+ "search_capabilities",
7
+ "execute_capability",
8
+ ] as const;
9
+
10
+ /** Builds the two platform Gateway tools from one authenticated logical MCP Session. */
11
+ export function createPiGatewayToolCandidates(
12
+ session: RuntimeGatewaySession,
13
+ ): PiToolCandidate[] {
14
+ const tools = new Map(session.tools.map((tool) => [tool.name, tool]));
15
+ if (
16
+ tools.size !== GATEWAY_TOOLS.length ||
17
+ GATEWAY_TOOLS.some((name) => !tools.has(name))
18
+ ) {
19
+ throw new Error("Connector Gateway returned an invalid MCP catalog");
20
+ }
21
+
22
+ return GATEWAY_TOOLS.map((name) => {
23
+ const tool = tools.get(name)!;
24
+ const execute = name === "execute_capability";
25
+ return createPiMcpToolCandidate(
26
+ { ...tool, serverId: "gateway" },
27
+ (input, signal) => session.callTool(name, input, signal),
28
+ {
29
+ owner: "gateway",
30
+ modelName: name,
31
+ requiredExecutionLevel: execute ? "high" : "safe",
32
+ ...(execute
33
+ ? {
34
+ requiredExecutionLevelForInput: async (input: unknown) => {
35
+ const reference = input && typeof input === "object" &&
36
+ !Array.isArray(input)
37
+ ? (input as Record<string, unknown>).name
38
+ : undefined;
39
+ if (typeof reference !== "string" || !reference) {
40
+ throw new Error(
41
+ "Gateway Capability Reference is not verified",
42
+ );
43
+ }
44
+ return (await session.resolveCapabilityRisk(reference)) ===
45
+ "none"
46
+ ? "safe" as const
47
+ : "high" as const;
48
+ },
49
+ }
50
+ : {}),
51
+ },
52
+ );
53
+ });
54
+ }
@@ -24,9 +24,11 @@ export * from "./base";
24
24
  export * from "./compiler";
25
25
  export * from "./core";
26
26
  export * from "./core-host";
27
+ export * from "./gateway";
27
28
  export * from "./mcp";
28
29
  export * from "./schedule";
29
30
  export * from "./skill";
30
31
  export * from "./subagent";
31
32
  export * from "./workspace-sandbox";
33
+ export * from "./workspace-revision";
32
34
  export * from "./web-search";