@springbrand/agent-runtime 0.3.0-alpha.8 → 0.3.0-alpha.9

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 (87) hide show
  1. package/package.json +2 -2
  2. package/src/adapter/cloudflare/subagent/definition.ts +4 -4
  3. package/src/adapter/cloudflare/subagent/runner.ts +1 -1
  4. package/src/adapter/cloudflare/subagent/tools.ts +2 -2
  5. package/src/adapter/cloudflare/universal-agent/preparation.ts +3 -3
  6. package/src/adapter/cloudflare/universal-agent/tools.ts +4 -6
  7. package/src/adapter/cloudflare/workspace/version-control.ts +1 -1
  8. package/src/config/agent-context.ts +1 -1
  9. package/src/config/assembler.ts +17 -31
  10. package/src/config/assembly-snapshot.ts +1 -1
  11. package/src/config/definition.ts +1 -1
  12. package/src/config/model-guidance.ts +1 -1
  13. package/src/config/ports.ts +3 -3
  14. package/src/config/public-contracts.ts +2 -2
  15. package/src/context/extension/assembly.ts +2 -2
  16. package/src/context/memory/budget/gate.ts +1 -1
  17. package/src/conversation/state.ts +1 -1
  18. package/src/index.ts +7 -7
  19. package/src/lib/prompt.ts +3 -5
  20. package/src/models/index.ts +1 -0
  21. package/src/session/cloudflare/migration.ts +29 -8
  22. package/src/skills/springbrand-worker-website/index.ts +1 -1
  23. package/src/submission/delivery.ts +1 -1
  24. package/src/submission/finalization.ts +1 -1
  25. package/src/telemetry/coordinator.ts +1 -1
  26. package/src/telemetry/subagent-lifecycle.ts +1 -1
  27. package/src/telemetry/turn-events.ts +1 -1
  28. package/src/tools/README.md +44 -0
  29. package/src/tools/{ai-adapter.ts → adapters/ai-to-pi.ts} +6 -9
  30. package/src/tools/{declared.ts → adapters/declared.ts} +2 -2
  31. package/src/tools/{nested-tools.ts → adapters/pi-to-ai.ts} +2 -2
  32. package/src/tools/assistant/ask-user.ts +317 -0
  33. package/src/tools/assistant/candidate.ts +15 -0
  34. package/src/tools/assistant/followups.ts +34 -0
  35. package/src/tools/assistant/index.ts +19 -0
  36. package/src/tools/assistant/memory.ts +73 -0
  37. package/src/tools/assistant/plan.ts +98 -0
  38. package/src/tools/{time.ts → assistant/time.ts} +1 -1
  39. package/src/tools/{core-host.ts → codemode/adapter.ts} +50 -49
  40. package/src/tools/{core.ts → codemode/index.ts} +36 -92
  41. package/src/tools/codemode/workspace-connector.ts +198 -0
  42. package/src/tools/extension/index.ts +42 -0
  43. package/src/tools/index.ts +28 -26
  44. package/src/tools/{gateway.ts → mcp/gateway.ts} +19 -19
  45. package/src/tools/{mcp.ts → mcp/index.ts} +5 -5
  46. package/src/tools/{approval → runtime/approval}/repository.ts +3 -9
  47. package/src/tools/{compiler.ts → runtime/compiler.ts} +24 -157
  48. package/src/tools/runtime/contracts.ts +117 -0
  49. package/src/tools/{decision-gate.ts → runtime/decision-gate.ts} +6 -9
  50. package/src/tools/{human-decisions.ts → runtime/human-decisions.ts} +4 -4
  51. package/src/tools/{interaction → runtime/interaction}/repository.ts +1 -7
  52. package/src/tools/runtime/result.ts +22 -0
  53. package/src/tools/sandbox/index.ts +228 -0
  54. package/src/tools/{schedule.ts → schedule/index.ts} +50 -49
  55. package/src/tools/skill/contracts.ts +20 -0
  56. package/src/tools/skill/index.ts +99 -0
  57. package/src/tools/skill/resources.ts +322 -0
  58. package/src/tools/skill/scripts.ts +168 -0
  59. package/src/tools/{subagent-authority.ts → subagent/authority.ts} +31 -31
  60. package/src/tools/{subagent.ts → subagent/index.ts} +34 -37
  61. package/src/tools/{subagent-run-repository.ts → subagent/repository.ts} +1 -1
  62. package/src/tools/{subagent-runtime.ts → subagent/runtime.ts} +1 -1
  63. package/src/tools/{temporary-agent → subagent/temporary}/approvals.ts +4 -7
  64. package/src/tools/{temporary-agent → subagent/temporary}/core.ts +1 -1
  65. package/src/tools/{temporary-agent → subagent/temporary}/runner.ts +1 -1
  66. package/src/tools/{temporary-agent → subagent/temporary}/workspace.ts +1 -4
  67. package/src/tools/{web-fetch.ts → web/fetch.ts} +4 -13
  68. package/src/tools/web/index.ts +2 -0
  69. package/src/tools/web/search/api.ts +1320 -0
  70. package/src/tools/web/search/index.ts +2 -0
  71. package/src/tools/{web-search/web-search.ts → web/search/search.ts} +5 -7
  72. package/src/tools/{workspace-sandbox.ts → workspace/index.ts} +12 -272
  73. package/src/tools/{workspace-revision.ts → workspace/revision.ts} +3 -3
  74. package/src/tools/base.ts +0 -542
  75. package/src/tools/skill.ts +0 -565
  76. package/src/tools/web-search/api.ts +0 -1318
  77. package/src/tools/web-search/index.ts +0 -2
  78. package/src/tools/workspace-state-connector.ts +0 -146
  79. package/src/tools/{approval → runtime/approval}/index.ts +0 -0
  80. package/src/tools/{interaction → runtime/interaction}/index.ts +0 -0
  81. package/src/tools/{unattended-error.ts → runtime/unattended-error.ts} +0 -0
  82. package/src/tools/{temporary-agent → subagent/temporary}/coordinator.ts +0 -0
  83. package/src/tools/{subagent-types → subagent/types}/contract.ts +0 -0
  84. package/src/tools/{subagent-types → subagent/types}/extract/index.ts +0 -0
  85. package/src/tools/{subagent-types → subagent/types}/fanout/index.ts +0 -0
  86. package/src/tools/{subagent-types → subagent/types}/registry.ts +1 -1
  87. /package/src/tools/{workspace-versioning.ts → workspace/versioning.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springbrand/agent-runtime",
3
- "version": "0.3.0-alpha.8",
3
+ "version": "0.3.0-alpha.9",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -13,7 +13,7 @@
13
13
  ".": "./src/index.ts",
14
14
  "./adapter/cloudflare": "./src/adapter/cloudflare/index.ts",
15
15
  "./contracts": "./src/config/public-contracts.ts",
16
- "./declared-tool": "./src/tools/declared.ts",
16
+ "./declared-tool": "./src/tools/adapters/declared.ts",
17
17
  "./models": "./src/lib/model-catalog.ts"
18
18
  },
19
19
  "dependencies": {
@@ -11,8 +11,8 @@ import type { HarnessEvent } from "@earendil-works/pi-agent-core";
11
11
  import type { UIMessageChunk } from "ai";
12
12
  import {
13
13
  AGENT_TYPES,
14
- } from "../../../tools/subagent-types/registry";
15
- import type { AgentType } from "../../../tools/subagent-types/contract";
14
+ } from "../../../tools/subagent/types/registry";
15
+ import type { AgentType } from "../../../tools/subagent/types/contract";
16
16
  import type {
17
17
  RuntimeProviderPort,
18
18
  WorkspacePort,
@@ -30,8 +30,8 @@ import {
30
30
  decodeSubagentCompletion,
31
31
  emptySubagentUsage,
32
32
  encodeSubagentCompletion,
33
- } from "../../../tools/subagent-runtime";
34
- import type { RuntimeSubagentUsageMetrics } from "../../../tools/subagent-authority";
33
+ } from "../../../tools/subagent/runtime";
34
+ import type { RuntimeSubagentUsageMetrics } from "../../../tools/subagent/authority";
35
35
  import { createCloudflareSubAgentTools } from "./tools";
36
36
 
37
37
  type RunStatus =
@@ -12,7 +12,7 @@ import {
12
12
  import type { Api, AssistantMessage, Model, Models } from "@earendil-works/pi-ai";
13
13
  import { z } from "zod";
14
14
  import { assembleSubagentPrompt } from "../../../lib/prompt";
15
- import type { AgentType } from "../../../tools/subagent-types/contract";
15
+ import type { AgentType } from "../../../tools/subagent/types/contract";
16
16
  import { structuredOutputStream } from "../../../models";
17
17
  import {
18
18
  openSessionHarness,
@@ -15,13 +15,13 @@ import type {
15
15
  } from "@earendil-works/pi-agent-core";
16
16
  import { Type } from "@earendil-works/pi-ai";
17
17
  import type { WorkspacePort } from "../../../config/ports";
18
- import type { AgentType } from "../../../tools/subagent-types/contract";
18
+ import type { AgentType } from "../../../tools/subagent/types/contract";
19
19
  import {
20
20
  compileHarnessTools,
21
21
  type CompileHarnessToolsOptions,
22
22
  type PiToolCandidate,
23
23
  } from "../../../tools";
24
- import { workspacePiToolCandidates } from "../../../tools/workspace-sandbox";
24
+ import { workspacePiToolCandidates } from "../../../tools/workspace";
25
25
 
26
26
  const READONLY_WORKSPACE_TOOLS = new Set([
27
27
  "read",
@@ -9,16 +9,16 @@ import type {
9
9
  RuntimeSubagentPort,
10
10
  WorkspacePort,
11
11
  } from "../../../config/ports";
12
- import { decodeSubagentCompletion } from "../../../tools/subagent-runtime";
12
+ import { decodeSubagentCompletion } from "../../../tools/subagent/runtime";
13
13
  import type { RuntimeDegradation } from "../../../config/degradation";
14
14
  import type { RuntimeMemoryProfile } from "../../../config/profile";
15
- import { AGENT_TYPES } from "../../../tools/subagent-types/registry";
15
+ import { AGENT_TYPES } from "../../../tools/subagent/types/registry";
16
16
  import type { RuntimeAgentConfigContext } from "../../../config/agent-context";
17
17
  import {
18
18
  createBrowserExecutionFactory,
19
19
  createWorkspaceCodeExecutionFactory,
20
20
  type RuntimeBrowserBinding,
21
- } from "../../../tools/core-host";
21
+ } from "../../../tools/codemode/adapter";
22
22
  import {
23
23
  createCloudflareSandboxAdapter,
24
24
  type SandboxAdmission,
@@ -7,15 +7,13 @@ import type {
7
7
  } from "../../../config/ports";
8
8
  import type { RuntimeDegradation } from "../../../config/degradation";
9
9
  import type { RuntimeMemoryProfile } from "../../../config/profile";
10
- import type { WorkspaceRevisionRestorePort } from "../../../tools/workspace-versioning";
10
+ import type { WorkspaceRevisionRestorePort } from "../../../tools/workspace/versioning";
11
11
  import { schedulePiToolCandidates } from "../../../tools/schedule";
12
- import { workspaceRevisionPiToolCandidate } from "../../../tools/workspace-revision";
13
- import {
14
- sandboxPiToolCandidates,
15
- } from "../../../tools/workspace-sandbox";
12
+ import { workspaceRevisionPiToolCandidate } from "../../../tools/workspace/revision";
13
+ import { sandboxPiToolCandidates } from "../../../tools/sandbox";
16
14
  import type { RuntimeCodeExecutionFactory } from "../../../config/ports";
17
15
  import type { ToolAssemblyResult } from "../../../config/definition";
18
- import type { PiToolCandidate } from "../../../tools/compiler";
16
+ import type { PiToolCandidate } from "../../../tools/runtime/contracts";
19
17
 
20
18
  export function assembleUniversalAgentTools(options: {
21
19
  hostTools?: readonly PiToolCandidate[];
@@ -5,7 +5,7 @@ import {
5
5
  type WorkspaceRevision,
6
6
  type WorkspaceRevisionManifest,
7
7
  type WorkspaceVersionPort,
8
- } from "../../../tools/workspace-versioning";
8
+ } from "../../../tools/workspace/versioning";
9
9
  import { createWorkspaceGitFs } from "./git-fs";
10
10
 
11
11
  const PAGE_SIZE = 256;
@@ -8,7 +8,7 @@ import type { RuntimeConfigUpdateResult } from "./runtime-config";
8
8
  import type {
9
9
  TemporaryAgentRequest,
10
10
  TemporaryAgentRunContext,
11
- } from "../tools/temporary-agent/core";
11
+ } from "../tools/subagent/temporary/core";
12
12
 
13
13
  /**
14
14
  * 描述 Agent 寻址路径中的一级父子节点。
@@ -37,10 +37,7 @@ import {
37
37
  type PiRuntimeAssembly,
38
38
  } from "./assembly-snapshot";
39
39
  import type { AgentTool } from "@earendil-works/pi-agent-core";
40
- import type {
41
- PiToolCandidate,
42
- ToolExposureMode,
43
- } from "../tools/compiler";
40
+ import type { PiToolCandidate, ToolExposureMode } from "../tools/runtime/contracts";
44
41
  import type {
45
42
  AssembleRuntimeSnapshotInput,
46
43
  RuntimeSettings,
@@ -51,17 +48,11 @@ import type {
51
48
  RuntimeToolSurfacePolicy,
52
49
  } from "./definition";
53
50
  import type { RuntimeCodeExecutionFactory } from "./ports";
54
- import {
55
- basePiToolCandidates,
56
- memoryPiToolCandidate,
57
- } from "../tools/base";
58
- import {
59
- BROWSER_EXECUTE_TOOL_NAME,
60
- browserExecutionPiToolCandidate,
61
- codeExecutionPiToolCandidate,
62
- } from "../tools/core";
51
+ import { basePiToolCandidates } from "../tools/assistant";
52
+ import { memoryPiToolCandidate } from "../tools/assistant/memory";
53
+ import { BROWSER_EXECUTE_TOOL_NAME, browserExecutionPiToolCandidate, codeExecutionPiToolCandidate } from "../tools/codemode";
63
54
  import { skillPiToolCandidates } from "../tools/skill";
64
- import { createWebSearch, WEB_SEARCH_MODEL } from "../tools/web-search";
55
+ import { createWebSearch, WEB_SEARCH_MODEL } from "../tools/web/search";
65
56
  import { subagentPiToolCandidates } from "../tools/subagent";
66
57
  import { resolvePiModel } from "../models";
67
58
  import {
@@ -305,17 +296,12 @@ async function createToolSurface(
305
296
  typeof candidate.tool.execute === "function",
306
297
  );
307
298
  const codeExecutionCandidates = Object.freeze([...mergeable]);
308
- const codeExecutionToolNames = new Set(
309
- codeExecutionCandidates.map(({ tool }) => tool.name),
310
- );
311
299
  const codeExecution = input.codeExecution.create(
312
300
  codeExecutionCandidates,
313
301
  );
314
- const topLevelOnly = directVisible.filter(
315
- ({ tool }) => !codeExecutionToolNames.has(tool.name),
316
- );
302
+ const directToolNames = new Set(directVisible.map(({ tool }) => tool.name));
317
303
  const routeList = (deferred: boolean) => {
318
- const matches = topLevelOnly.filter((candidate) =>
304
+ const matches = directVisible.filter((candidate) =>
319
305
  (defersTool?.(candidate.tool.name) === true) === deferred
320
306
  );
321
307
  return matches.length > 0
@@ -327,8 +313,9 @@ async function createToolSurface(
327
313
  };
328
314
  const routeGuidance = [
329
315
  "## Tool routing",
330
- "Top-level deferred Tools are not callable through `tools.*`. Use Provider Tool Search with their exact name, then call them directly.",
316
+ "For top-level deferred Tools, use Provider Tool Search with their exact name, then call them directly.",
331
317
  "Top-level immediate Tools are already available outside execute; call them directly.",
318
+ "Only Code Mode Tools listed below are callable inside execute through tools.<name>. Tools marked execute-only are not registered as top-level Tools.",
332
319
  "",
333
320
  "## Top-level deferred Tools",
334
321
  routeList(true),
@@ -336,21 +323,20 @@ async function createToolSurface(
336
323
  "## Top-level immediate Tools",
337
324
  routeList(false),
338
325
  "",
326
+ "## Code Mode Tools",
327
+ codeExecutionCandidates.length > 0
328
+ ? codeExecutionCandidates.map(({ tool }) =>
329
+ `- \`tools.${tool.name}\` — ${directToolNames.has(tool.name) ? "also available as a top-level Tool" : "execute-only"}`
330
+ ).join("\n")
331
+ : "- None.",
332
+ "",
339
333
  ].join("\n");
340
- const materializeGuidance = codeExecutionCandidates.some(
341
- ({ tool }) => tool.name === "materialize_skill_resource",
342
- )
343
- ? "Skill resource copying is available only inside execute through " +
344
- "`tools.materialize_skill_resource({ name, path, destination })`; " +
345
- "no Direct Tool is registered for it. " +
346
- "Copy multiple resources sequentially in one execute so durable replay preserves call order.\n\n"
347
- : "";
348
334
  return Object.freeze({
349
335
  candidates: Object.freeze([
350
336
  classify(codeExecutionPiToolCandidate({
351
337
  ...codeExecution,
352
338
  description:
353
- routeGuidance + "\n" + materializeGuidance + codeExecution.description,
339
+ routeGuidance + "\n" + codeExecution.description,
354
340
  })),
355
341
  ...directVisible.map(classify),
356
342
  ]),
@@ -18,7 +18,7 @@ import type {
18
18
  import type { RuntimeProviderPort } from "./ports";
19
19
  import { assembleSystemPrompt } from "../lib/prompt";
20
20
  import { resolvePiModel } from "../models";
21
- import type { PiToolCandidate } from "../tools/compiler";
21
+ import type { PiToolCandidate } from "../tools/runtime/contracts";
22
22
 
23
23
  /**
24
24
  * 收集完动态 Extension/MCP 候选后,一次性生成最终 Tool Surface。
@@ -17,7 +17,7 @@ import type {
17
17
  } from "./profile";
18
18
  import type { RuntimeExtensionConfig } from "../context/extension/contracts";
19
19
  import type { RuntimeDegradation } from "./degradation";
20
- import type { PiToolCandidate } from "../tools/compiler";
20
+ import type { PiToolCandidate } from "../tools/runtime/contracts";
21
21
  import type {
22
22
  RuntimeAgentContext,
23
23
  RuntimeAgentPlanningContext,
@@ -1,5 +1,5 @@
1
1
  import type { Context } from "@earendil-works/pi-ai";
2
- import type { PiToolCandidate } from "../tools/compiler";
2
+ import type { PiToolCandidate } from "../tools/runtime/contracts";
3
3
 
4
4
  /** Only descriptions and guidance may vary inside a pinned Submission. */
5
5
  export async function refreshModelGuidance(
@@ -5,13 +5,13 @@ import type { ScheduleSpec, RuntimeSchedulePreparation } from "../submission/con
5
5
  import type { RuntimeActivityProjection } from "../conversation/state";
6
6
  import type { ExecutionLevel } from "../lib/execution-level";
7
7
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
8
- import type { PiToolCandidate } from "../tools/compiler";
8
+ import type { PiToolCandidate } from "../tools/runtime/contracts";
9
9
  import type {
10
10
  RuntimeEventConfirmation,
11
11
  RuntimeLifecycleFact,
12
12
  RuntimeSubagentUsageEvent,
13
13
  RuntimeSubagentUsageMetrics,
14
- } from "../tools/subagent-authority";
14
+ } from "../tools/subagent/authority";
15
15
  export type { RuntimeModelInputModality } from "./profile";
16
16
  export type {
17
17
  RuntimeEventConfirmation,
@@ -22,7 +22,7 @@ export type {
22
22
  RuntimeSubmissionAdmissionHook,
23
23
  RuntimeSubmissionAdmissionInput,
24
24
  RuntimeSubmissionAdmissionResult,
25
- } from "../tools/subagent-authority";
25
+ } from "../tools/subagent/authority";
26
26
 
27
27
  // #region Workspace 端口
28
28
 
@@ -17,7 +17,7 @@ export * from "../conversation/message";
17
17
  export * from "../submission/contracts";
18
18
  export * from "../conversation/state";
19
19
  export * from "../lib/execution-level";
20
- export type { WorkspaceRevision, WorkspaceRevisionManifest } from "../tools/workspace-versioning";
20
+ export type { WorkspaceRevision, WorkspaceRevisionManifest } from "../tools/workspace/versioning";
21
21
  export type {
22
22
  RuntimeEventConfirmation,
23
23
  RuntimeLifecycleFact,
@@ -27,4 +27,4 @@ export type {
27
27
  RuntimeSubmissionAdmissionHook,
28
28
  RuntimeSubmissionAdmissionInput,
29
29
  RuntimeSubmissionAdmissionResult,
30
- } from "../tools/subagent-authority";
30
+ } from "../tools/subagent/authority";
@@ -12,8 +12,8 @@ import type {
12
12
  import type { RuntimeDegradation } from "../../config/degradation";
13
13
  import { withRuntimeLoadTimeout } from "../../config/runtime-load";
14
14
  import { sanitizeExtensionName } from "../../lib/extension-name";
15
- import type { PiToolCandidate } from "../../tools/compiler";
16
- import { createPiDeclaredToolCandidate } from "../../tools/declared";
15
+ import type { PiToolCandidate } from "../../tools/runtime/contracts";
16
+ import { createPiDeclaredToolCandidate } from "../../tools/adapters/declared";
17
17
 
18
18
  export interface PiExtensionContextContribution {
19
19
  readonly extensionName: string;
@@ -29,7 +29,7 @@ import {
29
29
  /**
30
30
  * 持久化单个字符串叶子的上限。
31
31
  *
32
- * MUST ≥ 单页 read 的上限(见 `workspace-sandbox.ts` 的 `READ_PAGE_MAX_CHARS`),
32
+ * MUST ≥ 单页 read 的上限(见 `tools/workspace/index.ts` 的 `READ_PAGE_MAX_CHARS`),
33
33
  * 否则持久记录会比模型当轮看到的内容还少,会话恢复后出现凭空缺页。
34
34
  */
35
35
  export const STORAGE_LEAF_MAX_CHARS = 64 * 1024;
@@ -1,6 +1,6 @@
1
1
  import type { ApprovalReceipt } from "../submission/contracts";
2
2
  import type { StoredSubmission } from "../submission/repository";
3
- import type { PendingApprovalSummary } from "../tools/approval/repository";
3
+ import type { PendingApprovalSummary } from "../tools/runtime/approval/repository";
4
4
  import type { PiCanonicalUserInput } from "./message/contract";
5
5
 
6
6
  export type RuntimeLoadPhase = "config" | "assembly" | "mcp" | "pi";
package/src/index.ts CHANGED
@@ -51,7 +51,7 @@ export type { RuntimeAssemblyView } from "./config/assembly-view";
51
51
  export * from "./adapter/cloudflare/define-agent";
52
52
  export type { ToolInteractionResponsePreparation } from "./adapter/cloudflare/agent";
53
53
  export * from "./conversation/state";
54
- export * from "./tools/workspace-versioning";
54
+ export * from "./tools/workspace/versioning";
55
55
  export * from "./lib/execution-level";
56
56
  export type {
57
57
  RuntimeAssemblyInput,
@@ -84,10 +84,10 @@ export type {
84
84
  } from "./lib/host-actions";
85
85
  export {
86
86
  AGENT_TYPES,
87
- } from "./tools/subagent-types/registry";
87
+ } from "./tools/subagent/types/registry";
88
88
  export type {
89
89
  AgentType,
90
- } from "./tools/subagent-types/contract";
90
+ } from "./tools/subagent/types/contract";
91
91
  export type {
92
92
  PiToolCandidate,
93
93
  ToolExposureMode,
@@ -129,15 +129,15 @@ export type {
129
129
  TemporaryAgentLaunch,
130
130
  TemporaryAgentRequest,
131
131
  TemporaryAgentRunContext,
132
- } from "./tools/temporary-agent/core";
133
- export { TEMPORARY_AGENT_LAUNCH_KEY } from "./tools/temporary-agent/core";
132
+ } from "./tools/subagent/temporary/core";
133
+ export { TEMPORARY_AGENT_LAUNCH_KEY } from "./tools/subagent/temporary/core";
134
134
  export {
135
135
  temporaryAgentExtensionIsSafe,
136
136
  temporaryAgentToolAllowed,
137
- } from "./tools/temporary-agent/runner";
137
+ } from "./tools/subagent/temporary/runner";
138
138
  export {
139
139
  createTemporaryAgentWorkspace,
140
- } from "./tools/temporary-agent/workspace";
140
+ } from "./tools/subagent/temporary/workspace";
141
141
  export {
142
142
  createPiModels,
143
143
  modelRequestUrl,
package/src/lib/prompt.ts CHANGED
@@ -17,9 +17,8 @@ export const PERSONA =
17
17
  // Names the stable runtime environment.
18
18
  export const RUNTIME =
19
19
  "Runtime: this agent runs on Cloudflare Workers. When execute is present, its Code Mode Dynamic Worker is your " +
20
- "instrument — code there runs with outbound network access (fetch), your " +
21
- "workspace filesystem (state.*), and the tools.* methods listed in its own description. Use it for raw or customized HTTP " +
22
- "requests, parsing a payload, hitting several known endpoints, or computing over a file. Write plain " +
20
+ "instrument — code there runs with your workspace filesystem (state.*) and the tools.* methods listed in its own description. " +
21
+ "Use it for tool composition, parsing a payload, or computing over a file. Write plain " +
23
22
  "JavaScript — the sandbox evaluates " +
24
23
  "it directly, so TypeScript type annotations (`: number`, `as Type`) are a syntax error, and there " +
25
24
  "is no Python interpreter or package manager to invoke. Use the JS/Workers equivalent of what you " +
@@ -58,8 +57,7 @@ export const TOOLS =
58
57
  "use state.* for workspace file operations, tools.* for other host capabilities, and sequential calls for repeated or durable operations. " +
59
58
  "Top-level-only Tools remain Direct even when called repeatedly. " +
60
59
  "For web tasks, use web_search for web discovery, current facts, cited research, " +
61
- "and public URL analysis. For web access specifically, use execute Code Mode only for raw or customized network requests, structured " +
62
- "API calls, or when web_search cannot retrieve the required content; do not use execute for ordinary web " +
60
+ "and public URL analysis; do not use execute for ordinary web " +
63
61
  "searches. When execute is absent, use the actually exposed Direct Tools. When sandbox_* tools are present, use that isolated Linux environment for Python/Node, " +
64
62
  "package managers, system commands, builds, tests and background processes; its filesystem is temporary, " +
65
63
  "persistent inputs are copied in automatically on first use, /userspace is read-only, and only explicitly " +
@@ -790,6 +790,7 @@ function configuredModel(
790
790
  ? {
791
791
  compat: {
792
792
  ...anthropicMessagesCompat,
793
+ // OpenRouter rejects Anthropic's mid-conversation effort messages and block binding.
793
794
  supportsMidConvoEffort: false,
794
795
  supportsEagerToolInputStreaming: false,
795
796
  supportsStrictTools:
@@ -716,6 +716,20 @@ export function migrateLegacyPiSession(
716
716
  .map((row) => [row.message_id, row.submission_id] as const)
717
717
  : [],
718
718
  );
719
+ const submissionStatuses = new Map(
720
+ hasTable(sql, "pi_submissions")
721
+ ? sql
722
+ .exec<{ submission_id: string; status: string }>(
723
+ "SELECT submission_id, status FROM pi_submissions",
724
+ )
725
+ .toArray()
726
+ .map((row) => [row.submission_id, row.status] as const)
727
+ : [],
728
+ );
729
+ const terminalSubmission = (submissionId: string): boolean =>
730
+ LEGACY_TERMINAL_SUBMISSION_STATUSES.has(
731
+ submissionStatuses.get(submissionId) ?? "",
732
+ );
719
733
  const results = new Map<string, ToolResultMessage>();
720
734
  for (const entry of converted.values()) {
721
735
  if (entry.type !== "message" || entry.message.role !== "toolResult")
@@ -797,9 +811,6 @@ export function migrateLegacyPiSession(
797
811
  const knownSubmissions = new Set(
798
812
  submissions.map((row) => row.submission_id),
799
813
  );
800
- const submissionStatuses = new Map(
801
- submissions.map((row) => [row.submission_id, row.status]),
802
- );
803
814
  for (const submission of submissions) {
804
815
  if (submission.status !== "pending" || !submission.queued_input_json)
805
816
  continue;
@@ -862,8 +873,12 @@ export function migrateLegacyPiSession(
862
873
  .canonicalToolResults) {
863
874
  const key = `${row.submission_id}:${result.toolCallId}`;
864
875
  const previous = results.get(key);
865
- if (previous) assertSameToolResult(previous, result);
866
- results.set(key, result);
876
+ if (previous && !terminalSubmission(row.submission_id)) {
877
+ assertSameToolResult(previous, result);
878
+ }
879
+ if (!previous || !terminalSubmission(row.submission_id)) {
880
+ results.set(key, result);
881
+ }
867
882
  }
868
883
  }
869
884
  }
@@ -875,8 +890,12 @@ export function migrateLegacyPiSession(
875
890
  if (call.result) {
876
891
  const key = `${submissionId}:${call.toolCallId}`;
877
892
  const previous = results.get(key);
878
- if (previous) assertSameToolResult(previous, call.result);
879
- results.set(key, call.result);
893
+ if (previous && !terminalSubmission(submissionId)) {
894
+ assertSameToolResult(previous, call.result);
895
+ }
896
+ if (!previous || !terminalSubmission(submissionId)) {
897
+ results.set(key, call.result);
898
+ }
880
899
  }
881
900
  }
882
901
  }
@@ -944,7 +963,9 @@ export function migrateLegacyPiSession(
944
963
  const recorded = results.get(
945
964
  `${row.submission_id}:${row.tool_call_id}`,
946
965
  );
947
- if (recorded) assertSameToolResult(recorded, expected);
966
+ if (recorded && !terminalSubmission(row.submission_id)) {
967
+ assertSameToolResult(recorded, expected);
968
+ }
948
969
  }
949
970
  }
950
971
  // The old table remains an inert backup. Failure rolls back DDL, data and the version together.
@@ -163,7 +163,7 @@ export const SPRINGBRAND_WORKER_WEBSITE_SKILL = {
163
163
  "5. Call `get_browser_console_logs` for the home page and for every primary internal page. Check navigation responses, missing assets, exceptions, CSP or CORS failures, and client fetch failures.",
164
164
  "6. Open the Website with the available browser evidence tools. For a multi-page Website, follow real links from the home page to every primary internal page and verify navigation back to the home page. Check at least the normal desktop viewport and one narrow viewport; capture and inspect a screenshot when visual appearance matters.",
165
165
  "7. Fix every failure, redeploy, and repeat the same checks. A clean home page does not prove a multi-page Website works.",
166
- "8. After browser verification succeeds, call `save_creation` exactly once with the existing entry HTML Working Copy path. For the recommended layout, that is `/creations/<website-name>/public/index.html`.",
166
+ "8. After browser verification succeeds, call `save_creation` exactly once with a user-visible title and the existing entry HTML Working Copy path. For the recommended layout, that path is `/creations/<website-name>/public/index.html`.",
167
167
  "9. Do not call the Website complete until `save_creation` returns an `artifactId`. Do not save referenced assets as separate Creations.",
168
168
  "",
169
169
  "## Publication contract",
@@ -2,7 +2,7 @@ import type { UIMessage } from "ai";
2
2
  import type { PiCanonicalUserInput } from "../conversation/message/contract";
3
3
  import type { RuntimeTelemetryCoordinator } from "../telemetry/coordinator";
4
4
  import type { RuntimeTurnEvents } from "../telemetry/turn-events";
5
- import type { HarnessHumanDecisions } from "../tools/human-decisions";
5
+ import type { HarnessHumanDecisions } from "../tools/runtime/human-decisions";
6
6
  import type { SubmitMessageOptions } from "./admission";
7
7
  import {
8
8
  projectSubmissionReceipt,
@@ -4,7 +4,7 @@ import type { ConversationHistory } from "../conversation/history";
4
4
  import type { MessageUiRepository } from "../conversation/repository";
5
5
  import type { RuntimeTelemetryCoordinator } from "../telemetry/coordinator";
6
6
  import type { RuntimeTurnEvents } from "../telemetry/turn-events";
7
- import type { HarnessHumanDecisions } from "../tools/human-decisions";
7
+ import type { HarnessHumanDecisions } from "../tools/runtime/human-decisions";
8
8
  import { USER_STOP_REASON } from "./contracts";
9
9
  import type { SubmissionOutcome } from "./lifecycle";
10
10
  import { isTerminalSubmissionStatus, type StoredSubmission, type SubmissionRepository } from "./repository";
@@ -5,7 +5,7 @@ import { TelemetryRecorder } from "./recorder";
5
5
  import { RuntimeTelemetry } from "./runtime-telemetry";
6
6
  import type { AgentTelemetryBinding } from "./contract";
7
7
  import type { PiGenerationLifecycleEvent } from "../models";
8
- import type { TemporaryAgentLifecycleEvent } from "../tools/temporary-agent/coordinator";
8
+ import type { TemporaryAgentLifecycleEvent } from "../tools/subagent/temporary/coordinator";
9
9
 
10
10
  const TELEMETRY_COLD_BUFFER_LIMIT = 1_000;
11
11
 
@@ -2,7 +2,7 @@ import type {
2
2
  RuntimeEventConfirmation,
3
3
  RuntimeLifecycleFact,
4
4
  RuntimeSubagentUsageEvent,
5
- } from "../tools/subagent-authority";
5
+ } from "../tools/subagent/authority";
6
6
 
7
7
  export interface RuntimeLifecycleSink {
8
8
  save(
@@ -7,7 +7,7 @@ import type {
7
7
  RuntimeTurnEventsPort,
8
8
  } from "../config/ports";
9
9
  import type { StoredSubmission } from "../submission/repository";
10
- import { decodeSubagentCompletion, isZeroSubagentUsage, subagentTerminalStatus, type RuntimeSubagentRunMetadata } from "../tools/subagent-runtime";
10
+ import { decodeSubagentCompletion, isZeroSubagentUsage, subagentTerminalStatus, type RuntimeSubagentRunMetadata } from "../tools/subagent/runtime";
11
11
  import type { RuntimeTelemetryCoordinator } from "./coordinator";
12
12
  import { summarizeModelUsage } from "./model-usage";
13
13
  import type { RuntimeEventOutboxRepository } from "./runtime-event-repository";
@@ -0,0 +1,44 @@
1
+ # Runtime Tools
2
+
3
+ 工具按功能归组。根 `index.ts` 只维护包内统一导出;功能目录的 `index.ts` 定义或组合该功能的 Pi Tool。
4
+
5
+ | 目录 | 职责 |
6
+ | --- | --- |
7
+ | `runtime/` | 公共工具契约、Harness 编译、执行决策、审批与客户端交互结算 |
8
+ | `adapters/` | AI SDK ↔ Pi 转换,以及外部工具声明到 Pi Candidate 的适配 |
9
+ | `codemode/` | `execute` 和 `browser_execute` 的 Pi Tool 定义、Cloudflare 执行适配、Workspace Connector |
10
+ | `assistant/` | 用户问答、计划、后续建议、记忆与时间工具 |
11
+ | `workspace/` | 持久 Workspace 文件操作、写入串行化与版本操作工具 |
12
+ | `sandbox/` | 临时 Linux Sandbox 的命令、进程和显式文件发布工具 |
13
+ | `skill/` | Skill 工具组合、资源读取与复制、脚本运行与输出投影 |
14
+ | `subagent/` | 子 Agent 工具、权限、运行记录、类型和临时 Agent 协调 |
15
+ | `schedule/` | 定时任务的 Pi Tool 定义 |
16
+ | `web/` | 网页获取与 Provider-native 搜索 |
17
+ | `mcp/` | MCP Tool 接入及使用 MCP Session 的 Connector Gateway |
18
+ | `extension/` | 已加载 Extension 的发现工具;Extension 装载仍归 `context/extension/` |
19
+
20
+ ## Code Mode
21
+
22
+ ```text
23
+ codemode/index.ts Pi Tool 定义、执行级别与调用截止
24
+ |
25
+ v
26
+ RuntimeCodeExecutionPort 内部执行接口
27
+ |
28
+ v
29
+ codemode/adapter.ts Cloudflare 执行器组装、描述与结果适配
30
+ |
31
+ +-- workspace-connector.ts state.* 方法、发现与校验
32
+ +-- ../adapters/pi-to-ai.ts 现有 Pi 工具到 Cloudflare ToolSet 的转换
33
+ ```
34
+
35
+ `pi-to-ai.ts` 也被 Skill 脚本执行使用,因此放在共用 `adapters/` 中。工具声明仍以 Pi 为唯一事实源。
36
+
37
+ ## 修改位置
38
+
39
+ - 修改某个工具:进入对应功能目录,测试与实现放在一起。
40
+ - 修改所有工具共用的执行策略:进入 `runtime/`,不要复制到每个工具。
41
+ - 修改外部协议转换:进入 `adapters/`,不新增另一份工具定义。
42
+ - 小功能可以只有 `index.ts`,不强制拆分 Adapter、Service 或 Repository。
43
+ - `runtime/result.ts` 只共享结果包装与进度报告;`assistant/candidate.ts` 只共享助手工具已有的元数据默认值。
44
+ - 包的公共导出名及 `@springbrand/agent-runtime/declared-tool` 子路径保持不变。内部路径变更需要同步 Vitest 测试池配置。
@@ -1,13 +1,10 @@
1
1
  // 本文件把 ai-sdk Tool 转成 Pi AgentTool。
2
2
  // `@cloudflare/think`、Agents Skills 和 Workspace 等上游工厂生成 ai-sdk Tool 时调用这个边界。
3
- // 转换只保留在这里,才能让后续治理和结算始终看到同一种 AgentTool 结果;核心术语见 `../../index.ts` 入口说明。
3
+ // 转换只保留在这里,才能让后续治理和结算始终看到同一种 AgentTool 结果;核心术语见 `src/index.ts` 入口说明。
4
4
 
5
- import type {
6
- AgentTool,
7
- AgentToolResult,
8
- } from "@earendil-works/pi-agent-core";
5
+ import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core";
9
6
  import { asSchema, type Tool as AiTool } from "ai";
10
- import { serializeOutput } from "../lib/artifacts";
7
+ import { serializeOutput } from "../../lib/artifacts";
11
8
 
12
9
  /** 控制一个 ai-sdk Tool 如何投影到 Pi AgentTool。 */
13
10
  export interface AiToolAdapterOptions {
@@ -141,9 +138,9 @@ export function aiToolToPi(
141
138
  .toModelOutput;
142
139
  const content = typeof toModelOutput === "function"
143
140
  ? modelContent(await (toModelOutput as (input: {
144
- input: unknown;
145
- output: unknown;
146
- }) => unknown)({ input: params, output: raw }))
141
+ input: unknown;
142
+ output: unknown;
143
+ }) => unknown)({ input: params, output: raw }))
147
144
  : null;
148
145
  return {
149
146
  content: content ?? [{
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool, AgentToolResult } from "@earendil-works/pi-agent-core";
2
- import type { PiToolCandidate } from "./compiler";
2
+ import type { PiToolCandidate } from "../runtime/contracts";
3
3
 
4
4
  /**
5
5
  * 一个由 Runtime 之外声明、由 Runtime 之外执行的工具。
@@ -15,7 +15,7 @@ import type { PiToolCandidate } from "./compiler";
15
15
  * 本模块只引入类型,因此可经 `@springbrand/agent-runtime/declared-tool` 单独导入:
16
16
  * 声明方在 Node 下也能装配自己的工具,不必为此加载 Workers 运行时。
17
17
  *
18
- * `Pi`、`Tool Candidate` 与 `Runtime` 等核心术语见 `../../index.ts`。
18
+ * `Pi`、`Tool Candidate` 与 `Runtime` 等核心术语见 `src/index.ts`。
19
19
  */
20
20
  export interface PiDeclaredTool {
21
21
  /** 声明方自己的工具名,用于结果详情与诊断,不一定是模型看到的名字。 */