@springbrand/agent-runtime 0.2.0-alpha.32 → 0.2.0-alpha.36

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/agent-runtime",
3
- "version": "0.2.0-alpha.32",
3
+ "version": "0.2.0-alpha.36",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -13,7 +13,6 @@ export {
13
13
  } from "./universal-agent/preparation";
14
14
  export {
15
15
  assembleUniversalAgentTools,
16
- selectTools,
17
16
  } from "./universal-agent/tools";
18
17
  export { createUniversalAgentHooks } from "./universal-agent/hooks";
19
18
 
@@ -8,31 +8,18 @@ import type {
8
8
  import type { RuntimeDegradation } from "../../../kernel/degradation";
9
9
  import type { RuntimeMemoryProfile } from "../../../kernel/profile";
10
10
  import type { WorkspaceRevisionRestorePort } from "../../../workspace-versioning";
11
- import { createScheduleTools } from "../../../pi/tool/schedule";
12
- import { createWorkspaceRevisionTools } from "../../../pi/tool/workspace-revision";
11
+ import { schedulePiToolCandidates } from "../../../pi/tool/schedule";
12
+ import { workspaceRevisionPiToolCandidate } from "../../../pi/tool/workspace-revision";
13
13
  import {
14
- createSandboxTools,
15
- createWorkspaceTools,
14
+ sandboxPiToolCandidates,
15
+ workspacePiToolCandidates,
16
16
  } from "../../../pi/tool/workspace-sandbox";
17
- import {
18
- mergeToolRegistries,
19
- type RuntimeCodeExecutionFactory,
20
- type ToolAssemblyResult,
21
- type ToolRegistry,
22
- } from "../../../tool-registry";
23
-
24
- export function selectTools(
25
- tools: ToolRegistry,
26
- names: Iterable<string>,
27
- ): ToolRegistry {
28
- const selected = new Set(names);
29
- return Object.fromEntries(
30
- Object.entries(tools).filter(([name]) => selected.has(name)),
31
- );
32
- }
17
+ import type { RuntimeCodeExecutionFactory } from "../../../kernel/bindings";
18
+ import type { ToolAssemblyResult } from "../../../runtime-definition";
19
+ import type { PiToolCandidate } from "../../../pi/tool/compiler";
33
20
 
34
21
  export function assembleUniversalAgentTools(options: {
35
- hostTools?: ToolRegistry;
22
+ hostTools?: readonly PiToolCandidate[];
36
23
  workspace?: WorkspacePort;
37
24
  workspaceRevisions?: WorkspaceRevisionRestorePort;
38
25
  codeExecution?: RuntimeCodeExecutionFactory;
@@ -45,15 +32,15 @@ export function assembleUniversalAgentTools(options: {
45
32
  degradations?: readonly RuntimeDegradation[];
46
33
  dispose?: () => Promise<void>;
47
34
  }): ToolAssemblyResult {
48
- const tools = mergeToolRegistries(
49
- options.hostTools ?? {},
50
- options.workspace ? createWorkspaceTools(options.workspace) : {},
51
- options.workspaceRevisions
52
- ? createWorkspaceRevisionTools(options.workspaceRevisions)
53
- : {},
54
- options.sandbox ? createSandboxTools(options.sandbox) : {},
55
- options.schedule ? createScheduleTools(options.schedule) : {},
56
- );
35
+ const tools = [
36
+ ...(options.hostTools ?? []),
37
+ ...(options.workspace ? workspacePiToolCandidates(options.workspace) : []),
38
+ ...(options.workspaceRevisions
39
+ ? [workspaceRevisionPiToolCandidate(options.workspaceRevisions)]
40
+ : []),
41
+ ...(options.sandbox ? sandboxPiToolCandidates(options.sandbox) : []),
42
+ ...(options.schedule ? schedulePiToolCandidates(options.schedule) : []),
43
+ ];
57
44
 
58
45
  return {
59
46
  tools,
package/src/index.ts CHANGED
@@ -23,7 +23,7 @@
23
23
  * - `canonical transcript` 是 Pi 持久化和恢复 Turn 时使用的权威消息历史。
24
24
  * - `admission` 是提示进入 Turn 前的接纳、去重和状态登记流程。
25
25
  * - `defineRuntimeAgent.load` 返回 config 和已加载 Resource。
26
- * - `defineRuntimeAgent.tools` Host 第一层 Tool 注册表声明。
26
+ * - `defineRuntimeAgent.tools` 返回 Host Pi Tool Candidate 装配结果。
27
27
  * - `assembleRuntimeSnapshot` 消费该输入与 hooks,产出候选 Snapshot。
28
28
  * - `Port` 是 Runtime 调用外部能力时依赖的最小接口(Definition 作者不直接装配)。
29
29
  * - `RuntimeBindings` 是本次装配选中的 Port 和可执行对象集合。
@@ -61,11 +61,8 @@ export type {
61
61
  RuntimeSkillContribution,
62
62
  RuntimeToolSurfacePolicy,
63
63
  } from "./runtime-assembler";
64
- export { assembleRuntimeSnapshot, toolRegistryPiToolCandidates } from "./runtime-assembler";
64
+ export { assembleRuntimeSnapshot } from "./runtime-assembler";
65
65
  export type {
66
- PlatformToolContext,
67
- PlatformToolRegistry,
68
- PlatformToolSpec,
69
66
  ProfileOverrides,
70
67
  ResolvedConnectors,
71
68
  ResolvedResources,
@@ -74,20 +71,8 @@ export type {
74
71
  RuntimeSettings,
75
72
  RuntimeToolBindings,
76
73
  ToolAssemblyResult,
77
- ToolContext,
78
- ToolRegistry,
79
- ToolSpec,
80
74
  } from "./runtime-definition";
81
- export {
82
- emptyPlatformToolRegistry,
83
- emptyToolRegistry,
84
- mergeToolRegistries,
85
- normalizeToolAssembly,
86
- piCandidateToToolSpec,
87
- toolRegistryFromPiCandidates,
88
- } from "./tool-registry";
89
75
  export type { ModelOption } from "./lib/model-catalog";
90
- export type { RuntimeCodeExecutionFactory } from "./tool-registry";
91
76
  export {
92
77
  assembleSubagentPrompt,
93
78
  assembleSystemPrompt,
@@ -118,26 +103,14 @@ export type {
118
103
  PiDeclaredToolCallContext,
119
104
  PiDeclaredToolPolicy,
120
105
  } from "./pi/tool";
106
+ export { memoryPiToolCandidate } from "./pi/tool";
107
+ export { schedulePiToolCandidates } from "./pi/tool";
121
108
  export {
122
- createMemoryTools,
123
- memoryPiToolCandidate,
124
- } from "./pi/tool";
125
- export {
126
- createScheduleTools,
127
- schedulePiToolCandidates,
128
- } from "./pi/tool";
129
- export {
130
- createSandboxTools,
131
- createWorkspaceRevisionTools,
132
- createWorkspaceTools,
133
109
  sandboxPiToolCandidates,
134
110
  workspaceRevisionPiToolCandidate,
135
111
  workspacePiToolCandidates,
136
112
  } from "./pi/tool";
137
- export {
138
- createSubagentTools,
139
- subagentPiToolCandidates,
140
- } from "./pi/tool";
113
+ export { subagentPiToolCandidates } from "./pi/tool";
141
114
  export { skillPiToolCandidates } from "./pi/tool";
142
115
  export type { PiSkillBinding } from "./pi/tool";
143
116
  export {
@@ -2,7 +2,8 @@ import type { SkillSource } from "agents/skills";
2
2
  import type { ScheduleSpec } from "./receipts";
3
3
  import type { RuntimeActivityProjection } from "./state";
4
4
  import type { ExecutionLevel } from "../lib/execution-level";
5
- import type { ToolRegistry } from "./tool-surface";
5
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
6
+ import type { PiToolCandidate } from "../pi/tool/compiler";
6
7
  import type {
7
8
  RuntimeEventConfirmation,
8
9
  RuntimeLifecycleFact,
@@ -213,7 +214,7 @@ export interface WorkspacePort {
213
214
  */
214
215
  export interface RuntimeCodeExecutionPort {
215
216
  readonly description: string;
216
- execute(input: { code: string }): Promise<unknown>;
217
+ execute(input: { code: string }): Promise<AgentToolResult<unknown>>;
217
218
  }
218
219
 
219
220
  /**
@@ -1022,12 +1023,11 @@ export interface RuntimeSkillSourceBinding {
1022
1023
  * 延迟到最终 Tool Surface 完成后再创建 Code Mode 执行能力。
1023
1024
  *
1024
1025
  * @remarks
1025
- * 定义在这里而不是 `tool-registry.ts`:它的返回类型是本文件的
1026
- * `RuntimeCodeExecutionPort`,而 `RuntimeBindings` 又要引用这个工厂 —— 放在
1027
- * tool-registry 会让两个模块互相 import。`tool-registry.ts` 继续对外导出它。
1026
+ * 工厂与返回 Port 同住 Host 绑定边界;输入直接使用最终 Pi Tool candidates,
1027
+ * 不再经过 Registry 或另一份 Tool 元数据协议。
1028
1028
  */
1029
1029
  export interface RuntimeCodeExecutionFactory {
1030
- create(tools: ToolRegistry): RuntimeCodeExecutionPort;
1030
+ create(candidates: readonly PiToolCandidate[]): RuntimeCodeExecutionPort;
1031
1031
  }
1032
1032
 
1033
1033
  export interface RuntimeBindings {
@@ -12,7 +12,7 @@ import type {
12
12
  PiToolInteraction,
13
13
  PiToolInteractionCancelReason,
14
14
  } from "../pi/runtime-adapter";
15
- import type { ToolResultMessage } from "@earendil-works/pi-ai";
15
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
16
16
 
17
17
  // #region 类型约定
18
18
 
@@ -43,10 +43,10 @@ export interface InteractionRecord extends PiToolInteraction {
43
43
  * `content` / `details` 由 Tool 自己的 `settle` 映射产出 —— 本流程不解释响应体语义。
44
44
  * 取消不带载荷,由纯函数层用固定文案兜底。
45
45
  */
46
- export interface InteractionSettlementPayload {
47
- readonly content: ToolResultMessage["content"];
48
- readonly details: unknown;
49
- }
46
+ export type InteractionSettlementPayload = Omit<
47
+ AgentToolResult<unknown>,
48
+ "terminate"
49
+ >;
50
50
 
51
51
  // 宿主用这些依赖把 interaction 状态机接入数据库、Pi 恢复计算和 Turn 续跑。
52
52
  interface InteractionLifecycleOptions<
@@ -3,7 +3,6 @@ import {
3
3
  type ExtensionManifest,
4
4
  type ExtensionPermissions,
5
5
  } from "@cloudflare/think/extensions";
6
- import type { AgentTool } from "@earendil-works/pi-agent-core";
7
6
  import { asSchema } from "ai";
8
7
  import type {
9
8
  LoadedRuntimeExtension,
@@ -14,6 +13,7 @@ import type { RuntimeDegradation } from "../../kernel/degradation";
14
13
  import { withRuntimeLoadTimeout } from "../../kernel/runtime-load";
15
14
  import { sanitizeExtensionName } from "../../lib/extension-name";
16
15
  import type { PiToolCandidate } from "../tool/compiler";
16
+ import { createPiDeclaredToolCandidate } from "../tool/declared";
17
17
 
18
18
  export interface PiExtensionContextContribution {
19
19
  readonly extensionName: string;
@@ -563,45 +563,30 @@ export async function assemblePiExtensions({
563
563
  const toolName = `${prefix}_${descriptor.name}`;
564
564
  toolNames.push(toolName);
565
565
  const description = `[${name}] ${descriptor.description}`;
566
- // 作用:把 Extension 的原始执行结果包装成 Pi Agent Core 的 Tool 结果。
567
- // 调用:编译后的共享 Tool 调度器在模型发起该 Extension Tool 时调用。
568
- // 原因:所有 Extension 结果在这个边界统一变成 text content,同时在 details 保留未损失的原值。
569
- const execute: AgentTool<any, {
570
- extension: string;
571
- result: unknown;
572
- }>["execute"] = async (_toolCallId, params, signal) => {
573
- const result = await loaded.execute(
566
+ const candidate = createPiDeclaredToolCandidate(
567
+ {
568
+ name: descriptor.name,
569
+ title: `${name}: ${descriptor.name}`,
570
+ description,
571
+ inputSchema: descriptor.inputSchema,
572
+ },
573
+ (input, { signal }) => loaded.execute(
574
574
  descriptor.name,
575
- params as Readonly<Record<string, unknown>>,
575
+ input,
576
576
  signal,
577
- );
578
- return {
579
- content: [{
580
- type: "text" as const,
581
- text: resultText(result),
582
- }],
583
- details: {
584
- extension: name,
585
- result,
586
- },
587
- };
588
- };
589
- const tool: AgentTool<any, {
590
- extension: string;
591
- result: unknown;
592
- }> = Object.freeze({
593
- name: toolName,
594
- label: `${name}: ${descriptor.name}`,
595
- description,
596
- parameters: descriptor.inputSchema as AgentTool["parameters"],
597
- execute,
598
- });
599
- candidates.push(Object.freeze({
600
- owner: `extension:${name}@${extension.manifest.version}`,
601
- requiredExecutionLevel,
602
- summary: description,
603
- tool,
604
- }));
577
+ ),
578
+ {
579
+ owner: `extension:${name}@${extension.manifest.version}`,
580
+ modelName: toolName,
581
+ requiredExecutionLevel,
582
+ summary: description,
583
+ project: (result) => ({
584
+ content: [{ type: "text", text: resultText(result) }],
585
+ details: { extension: name, result },
586
+ }),
587
+ },
588
+ );
589
+ candidates.push(Object.freeze({ ...candidate, requiredExecutionLevel }));
605
590
  }
606
591
  loadedExtensions.push(Object.freeze({
607
592
  name,
@@ -23,7 +23,13 @@ import type { PiToolCandidate } from "../tool/compiler";
23
23
  export interface PiToolSurface {
24
24
  finalize(
25
25
  candidates: readonly PiToolCandidate[],
26
- ): readonly PiToolCandidate[];
26
+ ): FinalizedPiToolSurface;
27
+ }
28
+
29
+ /** Internal finalization output; not a second Tool protocol. */
30
+ export interface FinalizedPiToolSurface {
31
+ readonly candidates: readonly PiToolCandidate[];
32
+ readonly codeExecutionCandidates: readonly PiToolCandidate[];
27
33
  }
28
34
 
29
35
  /** Immutable inputs consumed directly by Pi Agent Core. */
@@ -10,7 +10,7 @@ import type {
10
10
  import type { RuntimeProfile } from "../../kernel/profile";
11
11
  import type { ExecutionLevel } from "../../lib/execution-level";
12
12
  import type { RuntimeSnapshot } from "../../runtime-assembler";
13
- import type { PiRuntimeAssembly } from "../assembly";
13
+ import type { FinalizedPiToolSurface, PiRuntimeAssembly } from "../assembly";
14
14
  import {
15
15
  assemblePiSystemContext,
16
16
  assemblePiExtensions,
@@ -77,7 +77,7 @@ interface PreparedPiAssembly {
77
77
  readonly loaded: readonly PiLoadedExtension[];
78
78
  readonly context: readonly PiExtensionContextContribution[];
79
79
  readonly degradations: PiExtensionAssembly["degradations"];
80
- readonly candidates: readonly PiToolCandidate[];
80
+ readonly toolSurface: FinalizedPiToolSurface;
81
81
  }
82
82
 
83
83
  /**
@@ -193,6 +193,7 @@ export interface PreparedPiRuntimeState {
193
193
  readonly snapshot: RuntimeSnapshot;
194
194
  readonly assembly: PreparedPiAssembly;
195
195
  readonly candidates: readonly PiToolCandidate[];
196
+ readonly codeExecutionCandidates: readonly PiToolCandidate[];
196
197
  }
197
198
 
198
199
  // #endregion
@@ -411,21 +412,22 @@ async function preparePiAssembly(
411
412
  });
412
413
  },
413
414
  });
415
+ const toolSurface = snapshot.pi.toolSurface.finalize([
416
+ ...extensions.candidates,
417
+ listExtensionsPiToolCandidate(extensions.loaded),
418
+ ...(options.gatewaySession
419
+ ? createPiGatewayToolCandidates(options.gatewaySession)
420
+ : []),
421
+ ...createPiMcpToolCandidates(
422
+ options.mcpHost,
423
+ snapshot.profile.mcpServers,
424
+ ),
425
+ ]);
414
426
  return Object.freeze({
415
427
  loaded: extensions.loaded,
416
428
  context: extensions.context,
417
429
  degradations: extensions.degradations,
418
- candidates: snapshot.pi.toolSurface.finalize([
419
- ...extensions.candidates,
420
- listExtensionsPiToolCandidate(extensions.loaded),
421
- ...(options.gatewaySession
422
- ? createPiGatewayToolCandidates(options.gatewaySession)
423
- : []),
424
- ...createPiMcpToolCandidates(
425
- options.mcpHost,
426
- snapshot.profile.mcpServers,
427
- ),
428
- ]),
430
+ toolSurface,
429
431
  });
430
432
  }
431
433
 
@@ -446,7 +448,7 @@ export async function preparePiRuntime(
446
448
  owner: object,
447
449
  ): Promise<PreparedPiRuntime> {
448
450
  const assembly = await preparePiAssembly(options);
449
- const candidates = assembly.candidates;
451
+ const candidates = assembly.toolSurface.candidates;
450
452
  return Object.freeze(new PreparedRuntime(
451
453
  describeRuntime(options.snapshot, candidates, assembly.loaded),
452
454
  Object.freeze([
@@ -460,6 +462,9 @@ export async function preparePiRuntime(
460
462
  snapshot: options.snapshot,
461
463
  assembly,
462
464
  candidates: Object.freeze([...candidates]),
465
+ codeExecutionCandidates: Object.freeze([
466
+ ...assembly.toolSurface.codeExecutionCandidates,
467
+ ]),
463
468
  }),
464
469
  ));
465
470
  }
@@ -49,7 +49,6 @@ import {
49
49
  import { EXECUTION_LEVELS } from "../../lib/execution-level";
50
50
  import { projectToolOutputForModel } from "../../layers/context/budget/gate";
51
51
  import type { SpillWorkspace } from "../../lib/artifacts";
52
- import { toolRegistryFromPiCandidates } from "../../tool-registry";
53
52
 
54
53
  // #region Single-run Pi bridge
55
54
 
@@ -816,7 +815,7 @@ export class PreparedPiTurnAdapter {
816
815
  },
817
816
  });
818
817
  this.candidates = state.candidates.map((candidate) => {
819
- if (!candidate.codeExecutionTools) return bindCandidate(candidate);
818
+ if (candidate.tool.name !== "execute") return bindCandidate(candidate);
820
819
  const factory = state.snapshot.bindings.codeExecution;
821
820
  if (!factory) {
822
821
  throw new Error("Prepared Code Mode Tool requires a Runtime factory");
@@ -827,14 +826,12 @@ export class PreparedPiTurnAdapter {
827
826
  ...candidate.tool,
828
827
  execute: (toolCallId, input, signal, onUpdate) => {
829
828
  const runtimeCandidate = codeExecutionPiToolCandidate(
830
- factory.create(toolRegistryFromPiCandidates(
831
- candidate.codeExecutionTools!.map((inner) =>
832
- // Code Mode owns replay of its connector calls. Pi persists
833
- // only the parent execute attempt/result; recording an inner
834
- // input without a Pi settlement creates an orphan recovery
835
- // action that cannot be found on the direct Tool surface.
836
- bindCandidate(inner, toolCallId, false)
837
- ),
829
+ factory.create(state.codeExecutionCandidates.map((inner) =>
830
+ // Code Mode owns replay of its connector calls. Pi persists
831
+ // only the parent execute attempt/result; recording an inner
832
+ // input without a Pi settlement creates an orphan recovery
833
+ // action that cannot be found on the direct Tool surface.
834
+ bindCandidate(inner, toolCallId, false)
838
835
  )),
839
836
  );
840
837
  return runtimeCandidate.tool.execute(
@@ -15,6 +15,11 @@ import {
15
15
  type PiToolRecoveryState,
16
16
  } from "../turn";
17
17
  import type { ToolResultMessage } from "@earendil-works/pi-ai";
18
+ import {
19
+ createToolResultMessage,
20
+ type AgentToolResult,
21
+ } from "@earendil-works/pi-agent-core";
22
+ import { isEqual } from "lodash-es";
18
23
  import {
19
24
  aiSdkRecoveryCodec,
20
25
  shouldCreditStreamProgress,
@@ -32,15 +37,8 @@ export interface PiRecoveryIdentity {
32
37
  }
33
38
 
34
39
  export interface PiRecoveredToolSettlement {
35
- readonly toolCallId: string;
36
- readonly toolName: string;
37
40
  readonly args: unknown;
38
- readonly result: {
39
- readonly content: ToolResultMessage["content"];
40
- readonly details: unknown;
41
- };
42
- readonly isError: boolean;
43
- readonly createdAt: number;
41
+ readonly message: ToolResultMessage;
44
42
  }
45
43
 
46
44
  export type PiRecoveryCommand =
@@ -75,10 +73,7 @@ export type PiRecoveryCommand =
75
73
  | {
76
74
  readonly kind: "respond";
77
75
  readonly response: unknown;
78
- readonly result: {
79
- readonly content: ToolResultMessage["content"];
80
- readonly details: unknown;
81
- };
76
+ readonly result: Omit<AgentToolResult<unknown>, "terminate">;
82
77
  }
83
78
  | {
84
79
  readonly kind: "cancel";
@@ -89,10 +84,7 @@ export type PiRecoveryCommand =
89
84
  readonly kind: "record-tool-result";
90
85
  readonly toolCallId: string;
91
86
  readonly toolName: string;
92
- readonly result: {
93
- readonly content: ToolResultMessage["content"];
94
- readonly details: unknown;
95
- };
87
+ readonly result: AgentToolResult<unknown>;
96
88
  readonly isError: boolean;
97
89
  readonly timestamp: number;
98
90
  readonly needsContinuation?: boolean;
@@ -218,17 +210,10 @@ function recoveredToolSettlements(
218
210
  (item) => item.toolCallId === toolResult.toolCallId,
219
211
  );
220
212
  return {
221
- toolCallId: toolResult.toolCallId,
222
- toolName: toolResult.toolName,
223
213
  args:
224
214
  tool?.input ??
225
215
  (approval ? JSON.parse(approval.inputJson) : {}),
226
- result: {
227
- content: toolResult.content,
228
- details: toolResult.details,
229
- },
230
- isError: toolResult.isError,
231
- createdAt: toolResult.timestamp,
216
+ message: toolResult,
232
217
  };
233
218
  });
234
219
  }
@@ -248,24 +233,19 @@ function approvalExecutionId(continuationKey: string): string | null {
248
233
  // 记录必须已经存在 —— 拿不到 toolCallId/toolName 就无法把结果绑回原调用,这时失败关闭而不是编一个 id。
249
234
  function interactionToolResult(
250
235
  interaction: PiToolInteraction | undefined,
251
- result: {
252
- readonly content: ToolResultMessage["content"];
253
- readonly details: unknown;
254
- },
236
+ result: Omit<AgentToolResult<unknown>, "terminate">,
255
237
  timestamp: number,
256
238
  ): ToolResultMessage {
257
239
  if (!interaction) {
258
240
  throw new Error("SpringBrand Tool interaction is missing for its response");
259
241
  }
260
- return {
261
- role: "toolResult",
242
+ return createToolResultMessage({
262
243
  toolCallId: interaction.toolCallId,
263
244
  toolName: interaction.toolName,
264
- content: result.content,
265
- details: result.details,
245
+ result,
266
246
  isError: false,
267
247
  timestamp,
268
- };
248
+ });
269
249
  }
270
250
 
271
251
  // 把纯恢复计划翻译成持久化操作和 Runtime 下一步动作。
@@ -450,7 +430,7 @@ export function decidePiRecovery(
450
430
  if (
451
431
  existing.toolName !== record.toolName ||
452
432
  existing.retry !== record.retry ||
453
- JSON.stringify(existing.input) !== JSON.stringify(record.input)
433
+ !isEqual(existing.input, record.input)
454
434
  ) {
455
435
  throw new Error(
456
436
  `Conflicting SpringBrand Tool input for ${record.toolCallId}`,
@@ -608,18 +588,16 @@ export function decidePiRecovery(
608
588
  }
609
589
  if (input.command.kind === "record-tool-result") {
610
590
  const command = input.command;
611
- const toolResult: ToolResultMessage = {
612
- role: "toolResult",
591
+ const toolResult = createToolResultMessage({
613
592
  toolCallId: command.toolCallId,
614
593
  toolName: command.toolName,
615
- content: command.result.content,
616
- details: command.result.details,
594
+ result: command.result,
617
595
  isError: command.isError,
618
596
  timestamp: command.timestamp,
619
- };
597
+ });
620
598
  const existing = state.toolCalls[command.toolCallId]?.result;
621
599
  if (existing) {
622
- if (JSON.stringify(existing) !== JSON.stringify(toolResult)) {
600
+ if (!isEqual(existing, toolResult)) {
623
601
  throw new Error(
624
602
  `Conflicting SpringBrand Tool result for ${command.toolCallId}`,
625
603
  );
@@ -344,19 +344,11 @@ export class PiRuntimeTranscript {
344
344
  settlement: PiRecoveredToolSettlement,
345
345
  ): boolean {
346
346
  return this.append(
347
- `${submissionId}:tool:${settlement.toolCallId}`,
348
- {
349
- role: "toolResult",
350
- toolCallId: settlement.toolCallId,
351
- toolName: settlement.toolName,
352
- content: settlement.result.content,
353
- details: settlement.result.details,
354
- isError: settlement.isError,
355
- timestamp: settlement.createdAt,
356
- },
347
+ `${submissionId}:tool:${settlement.message.toolCallId}`,
348
+ settlement.message,
357
349
  {
358
350
  submissionId,
359
- createdAt: settlement.createdAt,
351
+ createdAt: settlement.message.timestamp,
360
352
  },
361
353
  );
362
354
  }
@@ -384,8 +376,8 @@ export class PiRuntimeTranscript {
384
376
  }
385
377
  return [...settlements].sort(
386
378
  (left, right) =>
387
- (callOrder.get(left.toolCallId) ?? Number.MAX_SAFE_INTEGER) -
388
- (callOrder.get(right.toolCallId) ?? Number.MAX_SAFE_INTEGER),
379
+ (callOrder.get(left.message.toolCallId) ?? Number.MAX_SAFE_INTEGER) -
380
+ (callOrder.get(right.message.toolCallId) ?? Number.MAX_SAFE_INTEGER),
389
381
  );
390
382
  }
391
383
 
@@ -8,10 +8,6 @@ import type { RuntimeMemoryPort } from "../../kernel/bindings";
8
8
  import type { RuntimeMemoryProfile } from "../../kernel/profile";
9
9
  import { serializeOutput } from "../../lib/artifacts";
10
10
  import type { PiToolCandidate } from "./compiler";
11
- import {
12
- toolRegistryFromPiCandidates,
13
- type ToolRegistry,
14
- } from "../../tool-registry";
15
11
  import { webSearchPiToolCandidate } from "./web-search";
16
12
  import type { WebSearch } from "./web-search/api";
17
13
 
@@ -368,14 +364,3 @@ export function memoryPiToolCandidate(
368
364
  }
369
365
 
370
366
  /** 从 Memory Port 生成 `set_context` Tool。 */
371
- export function createMemoryTools(
372
- memory: RuntimeMemoryPort,
373
- profile: Pick<
374
- RuntimeMemoryProfile,
375
- "memoryTokens" | "preferencesTokens"
376
- >,
377
- ): ToolRegistry {
378
- return toolRegistryFromPiCandidates([
379
- memoryPiToolCandidate(memory, profile),
380
- ]);
381
- }
@@ -39,7 +39,7 @@ export interface PiToolInteractionSpec {
39
39
  readonly settle?: (
40
40
  input: unknown,
41
41
  response: unknown,
42
- ) => AgentToolResult<unknown>;
42
+ ) => Omit<AgentToolResult<unknown>, "terminate">;
43
43
  }
44
44
 
45
45
  /** 描述一个尚未进入最终 Tool Surface 和结算包装的 Pi 工具。 */
@@ -52,8 +52,6 @@ export interface PiToolCandidate {
52
52
  readonly direct?: true;
53
53
  /** Offer this Tool only through Code Mode, never as a top-level Tool. */
54
54
  readonly codeExecutionOnly?: true;
55
- /** @internal Tools also callable through this Code Mode candidate. */
56
- readonly codeExecutionTools?: readonly PiToolCandidate[];
57
55
  /** Conservative maximum used in the stable Runtime descriptor. */
58
56
  readonly requiredExecutionLevel: ExecutionLevel;
59
57
  /** Trusted parameter-level policy, evaluated before approval or dispatch. */