@springbrand/agent-runtime 0.2.0-alpha.72 → 0.3.0-alpha.2

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 (141) hide show
  1. package/package.json +3 -3
  2. package/src/{agent-tool-runtime.ts → adapter/cloudflare/agent-tool.ts} +1 -1
  3. package/src/adapter/cloudflare/agent.ts +1592 -0
  4. package/src/{db/index.ts → adapter/cloudflare/database.ts} +18 -55
  5. package/src/{runtime-agent.ts → adapter/cloudflare/define-agent.ts} +25 -35
  6. package/src/adapter/cloudflare/dynamic-worker-observability.ts +1 -1
  7. package/src/adapter/cloudflare/resources/runtime-resources.ts +4 -4
  8. package/src/adapter/cloudflare/sandbox/adapter.ts +1 -1
  9. package/src/{db → adapter/cloudflare}/schema.ts +16 -83
  10. package/src/adapter/cloudflare/subagent/definition.ts +63 -33
  11. package/src/adapter/cloudflare/subagent/runner.ts +174 -120
  12. package/src/adapter/cloudflare/subagent/tools.ts +13 -14
  13. package/src/adapter/cloudflare/universal-agent/preparation.ts +7 -7
  14. package/src/adapter/cloudflare/universal-agent/tools.ts +10 -10
  15. package/src/adapter/cloudflare/workspace/git-fs.ts +1 -1
  16. package/src/adapter/cloudflare/workspace/scoped-workspace.ts +1 -1
  17. package/src/adapter/cloudflare/workspace/version-control.ts +2 -2
  18. package/src/config/activation.ts +140 -0
  19. package/src/{runtime-agent-context.ts → config/agent-context.ts} +3 -3
  20. package/src/{runtime-assembler.ts → config/assembler.ts} +20 -19
  21. package/src/{kernel/runtime-assembly.ts → config/assembly-projection.ts} +4 -4
  22. package/src/{pi/assembly/snapshot.ts → config/assembly-snapshot.ts} +6 -6
  23. package/src/{runtime-definition.ts → config/definition.ts} +13 -13
  24. package/src/config/harness-runtime.ts +182 -0
  25. package/src/config/index.ts +14 -0
  26. package/src/{pi/runtime-adapter/assembly.ts → config/pinning.ts} +38 -16
  27. package/src/{kernel/bindings.ts → config/ports.ts} +9 -13
  28. package/src/{kernel → config}/profile.ts +7 -0
  29. package/src/{kernel → config}/public-contracts.ts +6 -6
  30. package/src/{kernel → config}/runtime-load.ts +1 -1
  31. package/src/{pi/assembly/context.ts → context/assembly.ts} +4 -206
  32. package/src/{pi/assembly/extensions.ts → context/extension/assembly.ts} +5 -5
  33. package/src/context/extension/index.ts +3 -0
  34. package/src/context/index.ts +6 -0
  35. package/src/{layers/context → context/memory}/budget/gate.ts +2 -2
  36. package/src/conversation/harness-resume.ts +288 -0
  37. package/src/conversation/harness-stream.ts +98 -0
  38. package/src/{pi/runtime-adapter/transcript.ts → conversation/history.ts} +206 -233
  39. package/src/conversation/index.ts +6 -0
  40. package/src/{pi → conversation}/message/contract.ts +3 -0
  41. package/src/{pi → conversation}/message/conversion.ts +75 -0
  42. package/src/{pi → conversation}/message/index.ts +1 -0
  43. package/src/{pi → conversation}/message/model-input.ts +1 -1
  44. package/src/{pi → conversation}/message/projection.ts +76 -10
  45. package/src/{db/message-ui.repo.ts → conversation/repository.ts} +4 -0
  46. package/src/conversation/state.ts +175 -0
  47. package/src/harness/index.ts +7 -0
  48. package/src/harness/session-harness.ts +481 -0
  49. package/src/harness/submission-operation.ts +49 -0
  50. package/src/index.ts +38 -43
  51. package/src/lib/mcp.ts +1 -1
  52. package/src/{pi/runtime-adapter/models.ts → models/index.ts} +64 -18
  53. package/src/{pi/turn/tool-recovery.ts → session/cloudflare/legacy-tool-recovery.ts} +33 -525
  54. package/src/{pi/session → session/cloudflare}/migration.ts +334 -8
  55. package/src/{pi/session → session/cloudflare}/storage.ts +8 -36
  56. package/src/session/index.ts +39 -0
  57. package/src/submission/admission.ts +275 -0
  58. package/src/{kernel/receipts.ts → submission/contracts.ts} +14 -0
  59. package/src/submission/delivery.ts +212 -0
  60. package/src/submission/finalization.ts +111 -0
  61. package/src/submission/index.ts +9 -0
  62. package/src/{kernel/submission-lifecycle.ts → submission/lifecycle.ts} +41 -28
  63. package/src/{db/submission.repo.ts → submission/repository.ts} +62 -119
  64. package/src/submission/steer-delivery.ts +167 -0
  65. package/src/{db/steer.repo.ts → submission/steer-repository.ts} +27 -5
  66. package/src/telemetry/contract.ts +12 -0
  67. package/src/telemetry/coordinator.ts +65 -0
  68. package/src/telemetry/delivery.ts +2 -2
  69. package/src/telemetry/index.ts +5 -0
  70. package/src/{model-usage.ts → telemetry/model-usage.ts} +1 -1
  71. package/src/telemetry/recorder.ts +1 -1
  72. package/src/telemetry/runtime-telemetry.ts +12 -0
  73. package/src/{kernel/durable-lifecycle.ts → telemetry/subagent-lifecycle.ts} +1 -1
  74. package/src/telemetry/turn-events.ts +279 -0
  75. package/src/{pi/tool → tools}/ai-adapter.ts +2 -2
  76. package/src/tools/approval/index.ts +1 -0
  77. package/src/{db/approval.repo.ts → tools/approval/repository.ts} +10 -30
  78. package/src/{pi/tool → tools}/base.ts +14 -30
  79. package/src/tools/compiler.ts +327 -0
  80. package/src/{pi/tool → tools}/core-host.ts +3 -3
  81. package/src/{pi/tool → tools}/core.ts +3 -3
  82. package/src/tools/decision-gate.ts +227 -0
  83. package/src/{pi/tool → tools}/gateway.ts +1 -1
  84. package/src/tools/human-decisions.ts +127 -0
  85. package/src/tools/index.ts +30 -0
  86. package/src/tools/interaction/index.ts +1 -0
  87. package/src/{pi/tool → tools}/mcp.ts +1 -1
  88. package/src/{pi/tool → tools}/schedule.ts +3 -3
  89. package/src/{pi/tool → tools}/skill.ts +4 -4
  90. package/src/{kernel → tools}/subagent-runtime.ts +1 -1
  91. package/src/{pi/tool → tools}/subagent.ts +4 -4
  92. package/src/tools/temporary-agent/approvals.ts +126 -0
  93. package/src/{layers/orchestration/temporary-agent/core.ts → tools/temporary-agent/coordinator.ts} +41 -49
  94. package/src/tools/temporary-agent/core.ts +53 -0
  95. package/src/{layers/orchestration → tools}/temporary-agent/runner.ts +1 -1
  96. package/src/{layers/orchestration → tools}/temporary-agent/workspace.ts +1 -1
  97. package/src/{pi/tool → tools}/web-search/api.ts +1 -1
  98. package/src/{pi/tool → tools}/workspace-revision.ts +2 -2
  99. package/src/{pi/tool → tools}/workspace-sandbox.ts +2 -2
  100. package/src/{pi/tool → tools}/workspace-state-connector.ts +1 -1
  101. package/src/db/milestone.repo.ts +0 -104
  102. package/src/db/settlement.repo.ts +0 -104
  103. package/src/kernel/approval-lifecycle.ts +0 -638
  104. package/src/kernel/interaction-lifecycle.ts +0 -433
  105. package/src/kernel/recoverable-chat-agent.ts +0 -1020
  106. package/src/kernel/state.ts +0 -91
  107. package/src/pi/assembly/index.ts +0 -19
  108. package/src/pi/runtime-adapter/execution.ts +0 -1163
  109. package/src/pi/runtime-adapter/index.ts +0 -293
  110. package/src/pi/runtime-adapter/recovery.ts +0 -982
  111. package/src/pi/session/index.ts +0 -24
  112. package/src/pi/tool/compiler.ts +0 -483
  113. package/src/pi/tool/index.ts +0 -38
  114. package/src/pi/turn/approval.ts +0 -181
  115. package/src/pi/turn/index.ts +0 -82
  116. package/src/pi/turn/interaction.ts +0 -181
  117. package/src/runtime.ts +0 -4760
  118. /package/src/{kernel/runtime-assembly-view.ts → config/assembly-view.ts} +0 -0
  119. /package/src/{kernel → config}/degradation.ts +0 -0
  120. /package/src/{kernel → config}/runtime-config.ts +0 -0
  121. /package/src/{kernel/extensions.ts → context/extension/contracts.ts} +0 -0
  122. /package/src/{db/ext-context.repo.ts → context/extension/repository.ts} +0 -0
  123. /package/src/{pi/runtime-adapter/openrouter-messages.ts → models/openrouter/messages.ts} +0 -0
  124. /package/src/{pi/runtime-adapter → models/openrouter}/test-fixtures/openrouter-tool-search.sse +0 -0
  125. /package/src/{db/submission-admission.repo.ts → submission/admission-repository.ts} +0 -0
  126. /package/src/{db/telemetry-outbox.repo.ts → telemetry/repository.ts} +0 -0
  127. /package/src/{db/runtime-event-outbox.repo.ts → telemetry/runtime-event-repository.ts} +0 -0
  128. /package/src/{pi/tool → tools}/declared.ts +0 -0
  129. /package/src/{db/interaction.repo.ts → tools/interaction/repository.ts} +0 -0
  130. /package/src/{pi/tool → tools}/nested-tools.ts +0 -0
  131. /package/src/{kernel/submission-authority.ts → tools/subagent-authority.ts} +0 -0
  132. /package/src/{db/agent-tool.repo.ts → tools/subagent-run-repository.ts} +0 -0
  133. /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/contract.ts +0 -0
  134. /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/extract/index.ts +0 -0
  135. /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/fanout/index.ts +0 -0
  136. /package/src/{layers/orchestration/subagents/agent-types → tools/subagent-types}/registry.ts +0 -0
  137. /package/src/{pi/tool → tools}/time.ts +0 -0
  138. /package/src/{pi/tool → tools}/web-fetch.ts +0 -0
  139. /package/src/{pi/tool → tools}/web-search/index.ts +0 -0
  140. /package/src/{pi/tool → tools}/web-search/web-search.ts +0 -0
  141. /package/src/{workspace-versioning.ts → tools/workspace-versioning.ts} +0 -0
@@ -1,26 +1,28 @@
1
1
  import type { SqlTaggedTemplate } from "agents/chat";
2
2
 
3
3
  import { initializeSchema } from "./schema";
4
- import { SubmissionRepository } from "./submission.repo";
5
- import { ApprovalRepository } from "./approval.repo";
6
- import { ToolInteractionRepository } from "./interaction.repo";
7
- import { ToolSettlementRepository } from "./settlement.repo";
8
- import { RecoveryMilestoneRepository } from "./milestone.repo";
9
- import { ExtContextRepository } from "./ext-context.repo";
10
- import { MessageUiRepository } from "./message-ui.repo";
11
- import { SteerRepository } from "./steer.repo";
12
- import { RuntimeEventOutboxRepository } from "./runtime-event-outbox.repo";
13
- import { TelemetryOutboxRepository } from "./telemetry-outbox.repo";
14
- import { AgentToolRepository } from "./agent-tool.repo";
15
- import { SubmissionAdmissionRepository } from "./submission-admission.repo";
4
+ import {
5
+ SubmissionAdmissionRepository,
6
+ SubmissionRepository,
7
+ SteerRepository,
8
+ } from "../../submission";
9
+ import {
10
+ AgentToolRepository,
11
+ ApprovalRepository,
12
+ ToolInteractionRepository,
13
+ } from "../../tools";
14
+ import { ExtContextRepository } from "../../context";
15
+ import { MessageUiRepository } from "../../conversation";
16
+ import {
17
+ RuntimeEventOutboxRepository,
18
+ TelemetryOutboxRepository,
19
+ } from "../../telemetry";
16
20
 
17
21
  // 数据库术语以这里为准。
18
22
  // Submission 是一次可持久化的用户提交,从排队到终态都用同一个 submissionId 跟踪。
19
23
  // Approval 是某次工具调用的持久化审批记录,executionId 是对外决策时的主键。
20
24
  // Tool Interaction 是「结果由客户端提供」的工具调用的 park 记录,interactionId 是主键;
21
- // 它只回答还等不等得到响应,答案的权威副本仍是 Settlement。
22
- // Settlement 是工具调用的最终结果,同一 Submission 内用 toolCallId 防止重复落盘。
23
- // Recovery Milestone 是按 seq 重放的恢复事实,milestoneKey 用来去重。
25
+ // 它保存人的回答,Harness Session 保存工具执行位置和最终 ToolResult。
24
26
  // Extension Context 是扩展按 label 保存的文本,不随清空聊天记录删除。
25
27
  // Message UI 是会话消息的用户界面补充数据,它由 Transcript 而不是 Turn 生命周期管理。
26
28
  // Agent Tool Run 是 Agents SDK 派发的子运行,唯一一张不归 Runtime 所有的表:SDK 建它、写它、清它,
@@ -28,26 +30,10 @@ import { SubmissionAdmissionRepository } from "./submission-admission.repo";
28
30
  // 是因为 SDK 没有公开父级侧的「还有没有子运行在跑」读法,而复制一份必然产生第二个真相源。
29
31
  // Repository 只把 Runtime 语义翻译成参数化 SQL,所有表都位于当前 Agent 实例自己的 Durable Object SQLite 存储中。
30
32
  // Transaction 指 Cloudflare transactionSync 包住的同步操作,回调抛错时整体回滚。
31
- export * from "./schema";
32
- export * from "./submission.repo";
33
- export * from "./approval.repo";
34
- export * from "./interaction.repo";
35
- export * from "./settlement.repo";
36
- export * from "./milestone.repo";
37
- export * from "./ext-context.repo";
38
- export * from "./message-ui.repo";
39
- export * from "./steer.repo";
40
- export * from "./runtime-event-outbox.repo";
41
- export * from "./telemetry-outbox.repo";
42
- export * from "./agent-tool.repo";
43
- export * from "./submission-admission.repo";
44
-
45
33
  export class RuntimeDatabase {
46
34
  readonly submissions: SubmissionRepository;
47
35
  readonly approvals: ApprovalRepository;
48
36
  readonly interactions: ToolInteractionRepository;
49
- readonly settlements: ToolSettlementRepository;
50
- readonly milestones: RecoveryMilestoneRepository;
51
37
  readonly extContext: ExtContextRepository;
52
38
  readonly messageUi: MessageUiRepository;
53
39
  readonly steers: SteerRepository;
@@ -63,11 +49,9 @@ export class RuntimeDatabase {
63
49
  private readonly sql: SqlTaggedTemplate,
64
50
  private readonly transact: <T>(fn: () => T) => T,
65
51
  ) {
66
- this.submissions = new SubmissionRepository(sql);
52
+ this.submissions = new SubmissionRepository(sql, transact);
67
53
  this.approvals = new ApprovalRepository(sql);
68
54
  this.interactions = new ToolInteractionRepository(sql);
69
- this.settlements = new ToolSettlementRepository(sql);
70
- this.milestones = new RecoveryMilestoneRepository(sql);
71
55
  this.extContext = new ExtContextRepository(sql);
72
56
  this.messageUi = new MessageUiRepository(sql);
73
57
  this.steers = new SteerRepository(sql);
@@ -77,25 +61,6 @@ export class RuntimeDatabase {
77
61
  this.submissionAdmissions = new SubmissionAdmissionRepository(sql);
78
62
  }
79
63
 
80
- /**
81
- * 有未完成 Submission,且每一条都停在人机等待上。
82
- *
83
- * 换装配的忙碌闸门调用它。「停在等人」有两种形态:待决审批,和等客户端
84
- * 投递结果的 Tool Interaction —— 两者都意味着没有模型请求在飞、没有工具
85
- * 在执行,因此换掉能力语义是安全的。
86
- *
87
- * 注意它不是「存在待处理的人机等待」:只要有一条 Submission 正在真的执行,
88
- * 整个判断就必须为假,否则会把执行中的 Turn 切成两套能力语义。
89
- */
90
- everyUnfinishedSubmissionParked(): boolean {
91
- const unfinished = this.submissions.listUnfinishedIds();
92
- if (unfinished.length === 0) return false;
93
- return unfinished.every((submissionId) =>
94
- this.approvals.listPendingForSubmission(submissionId).length > 0 ||
95
- this.interactions.listPendingForSubmission(submissionId).length > 0
96
- );
97
- }
98
-
99
64
  // 在一个同步 SQLite 事务里运行一组读写。
100
65
  // Submission、Approval、Transcript 等需要一起成功或一起回滚时调用,回调不得返回 Promise。
101
66
  // 这里直接保留 transactionSync 的抛错回滚语义;待确认:当前泛型签名无法在类型层阻止 async 回调。
@@ -118,8 +83,6 @@ export class RuntimeDatabase {
118
83
  this.sql`DELETE FROM pi_submissions`;
119
84
  this.sql`DELETE FROM pi_approvals`;
120
85
  this.sql`DELETE FROM pi_tool_interactions`;
121
- this.sql`DELETE FROM pi_tool_settlements`;
122
- this.sql`DELETE FROM pi_recovery_milestones`;
123
86
  this.sql`DELETE FROM pi_telemetry_outbox`;
124
87
  }
125
88
  }
@@ -5,53 +5,53 @@ import {
5
5
  type ChatCapableAgentClass,
6
6
  } from "agents";
7
7
  import type { UIMessage } from "ai";
8
- import type { PiCanonicalTranscriptSnapshot } from "./pi/runtime-adapter";
8
+ import type { PiCanonicalTranscriptSnapshot } from "../../conversation";
9
9
  import type {
10
10
  ApprovalDecision,
11
11
  ApprovalReceipt,
12
12
  MessageDelivery,
13
13
  MessageDispatchReceipt,
14
14
  SubmissionReceipt,
15
- } from "./kernel/receipts";
16
- import type { RuntimeState } from "./kernel/state";
15
+ } from "../../submission";
16
+ import type { RuntimeState } from "../../conversation";
17
17
  import type {
18
18
  TemporaryAgentApprovalDecision,
19
19
  TemporaryAgentApprovalRequest,
20
20
  TemporaryAgentLaunch,
21
- } from "./layers/orchestration/temporary-agent/core";
22
- import { TEMPORARY_AGENT_LAUNCH_KEY } from "./layers/orchestration/temporary-agent/core";
21
+ } from "../../tools";
22
+ import { TEMPORARY_AGENT_LAUNCH_KEY } from "../../tools";
23
23
  import {
24
24
  temporaryAgentExtensionIsSafe,
25
25
  temporaryAgentToolAllowed,
26
- } from "./layers/orchestration/temporary-agent/runner";
26
+ } from "../../tools";
27
27
  import {
28
28
  EXECUTION_LEVELS,
29
29
  requiresExecutionApproval,
30
30
  type ExecutionLevel,
31
- } from "./lib/execution-level";
31
+ } from "../../lib/execution-level";
32
32
  import {
33
33
  assembleRuntimeSnapshot,
34
34
  type RuntimeCandidate,
35
- } from "./runtime-assembler";
35
+ } from "../../config";
36
36
  import type {
37
37
  ResolvedResources,
38
38
  ToolAssemblyResult,
39
- } from "./runtime-definition";
40
- import type { RuntimeAssemblyView } from "./kernel/runtime-assembly-view";
41
- import type { RuntimeConfigUpdateResult } from "./kernel/runtime-config";
39
+ } from "../../config";
40
+ import type { RuntimeAssemblyView } from "../../config";
41
+ import type { RuntimeConfigUpdateResult } from "../../config";
42
42
  import {
43
43
  logRuntimeLoadFailure,
44
44
  RUNTIME_LOAD_TIMEOUT_MS,
45
45
  withRuntimeLoadTimeout,
46
- } from "./kernel/runtime-load";
47
- import { AgentToolRuntimeKernel } from "./agent-tool-runtime";
46
+ } from "../../config";
47
+ import { AgentToolRuntimeKernel } from "./agent-tool";
48
48
  import type {
49
49
  RuntimeAgentConfigContext,
50
50
  RuntimeAgentPlanningContext,
51
51
  RuntimeAgentRole,
52
52
  RuntimeAgentToolResult,
53
- } from "./runtime-agent-context";
54
- import type { AgentTelemetryBinding } from "./telemetry/contract";
53
+ } from "../../config";
54
+ import type { AgentTelemetryBinding } from "../../telemetry";
55
55
 
56
56
  /**
57
57
  * 本文件把应用提供的 Config Definition 与 Planner 接到 Cloudflare Agent 生命周期。
@@ -70,7 +70,7 @@ export type {
70
70
  RuntimeAgentRole,
71
71
  RuntimeAgentToolResult,
72
72
  RuntimeAssemblyContext,
73
- } from "./runtime-agent-context";
73
+ } from "../../config";
74
74
 
75
75
  /** Host 一次性准备的完整 Runtime 加载结果。 */
76
76
  export interface LoadedRuntime<Config> {
@@ -163,15 +163,6 @@ export interface RuntimeAgentControls {
163
163
  runtimeKey?: string,
164
164
  options?: { force?: boolean },
165
165
  ): Promise<void>;
166
- /**
167
- * 确保 Runtime 已加载,再请求 Kernel 刷新当前 Session Context。
168
- *
169
- * @remarks
170
- * 业务侧在外部 Context 变更后调用,例如 Inbox 需要让已存在的聊天立即重读内容。
171
- *
172
- * 待确认:当前 `refreshContext` 只检查 Snapshot 已安装,没有重新读取 Context 或刷新缓存。
173
- */
174
- refreshMemoryContext(): Promise<void>;
175
166
  /**
176
167
  * Read the Runtime Snapshot currently installed on this Session facet.
177
168
  *
@@ -318,7 +309,7 @@ export interface RuntimeAgentConfigControls<Command, Change> {
318
309
  /** Type-only Host extension seam implemented by AgentRuntimeKernel. */
319
310
  declare class RuntimeAgentHostSeam {
320
311
  protected prepareToolInteractionResponse(
321
- input: import("./runtime").ToolInteractionResponsePreparation,
312
+ input: import("./agent").ToolInteractionResponsePreparation,
322
313
  ): Promise<unknown | null>;
323
314
  }
324
315
 
@@ -449,6 +440,10 @@ export function defineRuntimeAgent<
449
440
  return this.ensureConfig();
450
441
  }
451
442
 
443
+ protected override ensureRuntimeVersion(runtimeKey: string): Promise<void> {
444
+ return this.ensureConfig(runtimeKey);
445
+ }
446
+
452
447
  protected async prepareRuntimeForSubmission(submissionId: string): Promise<void> {
453
448
  const beforeSubmission = definition.beforeSubmission as RuntimeAgentDefinitionBase<Env, Config, Command, Change>["beforeSubmission"];
454
449
  try {
@@ -502,14 +497,6 @@ export function defineRuntimeAgent<
502
497
  }
503
498
  }
504
499
 
505
- // 作用:确保 Runtime 可用后请求 Kernel 刷新 Session Context。
506
- // 调用:Inbox 等 RPC 调用方在外部 Context 变更后调用。
507
- // 待确认:当前 Kernel 的 `refreshContext` 只检查 Snapshot,并未执行实际刷新。
508
- async refreshMemoryContext(): Promise<void> {
509
- await this.ensureRuntimeReady();
510
- await this.refreshContext();
511
- }
512
-
513
500
  async getRuntimeAssembly(): Promise<RuntimeAssemblyView> {
514
501
  await this.ensureRuntimeReady();
515
502
  return this.readRuntimeAssembly();
@@ -666,7 +653,10 @@ export function defineRuntimeAgent<
666
653
  ...(telemetry ? { telemetry } : {}),
667
654
  });
668
655
 
669
- await this.initCandidate(candidate);
656
+ await this.initCandidate({
657
+ ...candidate,
658
+ runtimeKey: resolvedRuntimeKey,
659
+ });
670
660
  this.hasLoadedRuntime = true;
671
661
  this.loadedRuntimeKey = resolvedRuntimeKey;
672
662
  this.runtimeLoad.complete();
@@ -1,4 +1,4 @@
1
- import type { RuntimePlatformPort } from "../../kernel/bindings";
1
+ import type { RuntimePlatformPort } from "../../config/ports";
2
2
  import { WorkerEntrypoint } from "cloudflare:workers";
3
3
 
4
4
  export type DynamicWorkerObservation = {
@@ -1,7 +1,7 @@
1
- import type { RuntimeDegradation } from "../../../kernel/degradation";
2
- import type { RuntimeExtensionConfig } from "../../../kernel/extensions";
3
- import { withRuntimeLoadTimeout } from "../../../kernel/runtime-load";
4
- import type { RuntimeExtensionContribution } from "../../../runtime-definition";
1
+ import type { RuntimeDegradation } from "../../../config/degradation";
2
+ import type { RuntimeExtensionConfig } from "../../../context/extension/contracts";
3
+ import { withRuntimeLoadTimeout } from "../../../config/runtime-load";
4
+ import type { RuntimeExtensionContribution } from "../../../config/definition";
5
5
  export { createR2SkillSource } from "./r2-skill-source";
6
6
 
7
7
  export interface CloudflareRuntimeExtensionSource {
@@ -24,7 +24,7 @@ import {
24
24
  type SandboxSyncFailure,
25
25
  type SandboxSyncResult,
26
26
  type WorkspacePort,
27
- } from "../../../kernel/bindings";
27
+ } from "../../../config/ports";
28
28
  import type {
29
29
  WorkspaceFileVersion,
30
30
  WorkspacePublishPort,
@@ -4,8 +4,7 @@ import type { SqlTaggedTemplate } from "agents/chat";
4
4
  // 创建当前 Runtime 需要但尚不存在的 SQLite 表。
5
5
  // AgentRuntimeKernel 构造时通过 RuntimeDatabase.initializeSchema 调用,重复启动依赖 IF NOT EXISTS 保留现有表。
6
6
  // pi_approvals 有一次显式的 risk → 四档升级;步骤均可重入,中断后下次初始化会继续。
7
- // pi_tool_interactions 是 client-settled tool 的 pending 索引:结果的权威副本仍在 pi_tool_settlements,
8
- // 这张表只回答「这次工具调用还等不等得到客户端响应」。
7
+ // pi_tool_interactions 只保存宿主交互事实;Tool 结果由 Pi Session 唯一持久化。
9
8
  export function initializeSchema(sql: SqlTaggedTemplate): void {
10
9
  sql`CREATE TABLE IF NOT EXISTS pi_message_ui (
11
10
  message_id TEXT PRIMARY KEY,
@@ -23,31 +22,21 @@ export function initializeSchema(sql: SqlTaggedTemplate): void {
23
22
  assembly_revision TEXT NOT NULL,
24
23
  assembly_descriptor TEXT NOT NULL,
25
24
  assistant_message_id TEXT NOT NULL,
26
- abort_reason TEXT,
27
- recovery_error_count INTEGER NOT NULL DEFAULT 0,
28
- recovery_reason TEXT,
29
- context_overflow_retried INTEGER NOT NULL DEFAULT 0
25
+ abort_reason TEXT
30
26
  )`;
31
27
  const submissionColumns = new Set(
32
28
  sql<{ name: string }>`PRAGMA table_info(pi_submissions)`
33
29
  .map((column) => column.name),
34
30
  );
31
+ if (!submissionColumns.has("runtime_key")) {
32
+ sql`ALTER TABLE pi_submissions ADD COLUMN runtime_key TEXT NOT NULL DEFAULT ''`;
33
+ }
35
34
  if (!submissionColumns.has("run_id")) {
36
35
  sql`ALTER TABLE pi_submissions ADD COLUMN run_id TEXT`;
37
36
  }
38
37
  if (!submissionColumns.has("admission_retryable")) {
39
38
  sql`ALTER TABLE pi_submissions ADD COLUMN admission_retryable INTEGER`;
40
39
  }
41
- // 跨执行片累计的模型回合数。让步把一个 Turn 切成多片后,每片的回合计数都从 0 开始,
42
- // 只有把总数落到持久层,全局预算才拦得住「一直让步、永不收敛」的工具循环。
43
- if (!submissionColumns.has("model_turns")) {
44
- sql`ALTER TABLE pi_submissions
45
- ADD COLUMN model_turns INTEGER NOT NULL DEFAULT 0`;
46
- }
47
- // 当前让步片的标识。用来丢弃重复到期的续跑 alarm,并让看门狗判断这一轮有没有推进。
48
- if (!submissionColumns.has("continuation_id")) {
49
- sql`ALTER TABLE pi_submissions ADD COLUMN continuation_id TEXT`;
50
- }
51
40
  sql`CREATE UNIQUE INDEX IF NOT EXISTS pi_submissions_run_id
52
41
  ON pi_submissions(run_id) WHERE run_id IS NOT NULL`;
53
42
  if (!submissionColumns.has("queued_input_json")) {
@@ -62,17 +51,6 @@ export function initializeSchema(sql: SqlTaggedTemplate): void {
62
51
  if (!submissionColumns.has("regenerate_message_id")) {
63
52
  sql`ALTER TABLE pi_submissions ADD COLUMN regenerate_message_id TEXT`;
64
53
  }
65
- if (!submissionColumns.has("recovery_error_count")) {
66
- sql`ALTER TABLE pi_submissions
67
- ADD COLUMN recovery_error_count INTEGER NOT NULL DEFAULT 0`;
68
- }
69
- if (!submissionColumns.has("recovery_reason")) {
70
- sql`ALTER TABLE pi_submissions ADD COLUMN recovery_reason TEXT`;
71
- }
72
- if (!submissionColumns.has("context_overflow_retried")) {
73
- sql`ALTER TABLE pi_submissions
74
- ADD COLUMN context_overflow_retried INTEGER NOT NULL DEFAULT 0`;
75
- }
76
54
  sql`CREATE UNIQUE INDEX IF NOT EXISTS pi_submissions_one_running
77
55
  ON pi_submissions(status)
78
56
  WHERE status = 'running'`;
@@ -80,20 +58,21 @@ export function initializeSchema(sql: SqlTaggedTemplate): void {
80
58
  steer_id TEXT PRIMARY KEY,
81
59
  submission_id TEXT NOT NULL,
82
60
  message_id TEXT NOT NULL UNIQUE,
61
+ harness_entry_id TEXT,
83
62
  canonical_json TEXT NOT NULL,
84
63
  ui_message_json TEXT,
85
64
  created_at INTEGER NOT NULL
86
65
  )`;
87
- sql`CREATE TABLE IF NOT EXISTS pi_tool_settlements (
88
- submission_id TEXT NOT NULL,
89
- tool_call_id TEXT NOT NULL,
90
- tool_name TEXT NOT NULL,
91
- args TEXT NOT NULL,
92
- result TEXT NOT NULL,
93
- is_error INTEGER NOT NULL,
94
- created_at INTEGER NOT NULL,
95
- PRIMARY KEY (submission_id, tool_call_id)
96
- )`;
66
+ const steerColumns = new Set(
67
+ sql<{ name: string }>`PRAGMA table_info(pi_pending_steers)`
68
+ .map((column) => column.name),
69
+ );
70
+ if (!steerColumns.has("harness_entry_id")) {
71
+ sql`ALTER TABLE pi_pending_steers ADD COLUMN harness_entry_id TEXT`;
72
+ }
73
+ sql`CREATE UNIQUE INDEX IF NOT EXISTS pi_pending_steers_harness_entry_id
74
+ ON pi_pending_steers(harness_entry_id)
75
+ WHERE harness_entry_id IS NOT NULL`;
97
76
  sql`CREATE TABLE IF NOT EXISTS pi_turn_event_outbox (
98
77
  event_id TEXT PRIMARY KEY,
99
78
  body TEXT NOT NULL,
@@ -211,52 +190,6 @@ export function initializeSchema(sql: SqlTaggedTemplate): void {
211
190
  responded_at INTEGER,
212
191
  UNIQUE (submission_id, tool_call_id)
213
192
  )`;
214
- sql`CREATE TABLE IF NOT EXISTS pi_recovery_milestones (
215
- submission_id TEXT NOT NULL,
216
- seq INTEGER NOT NULL,
217
- milestone_key TEXT NOT NULL,
218
- body TEXT NOT NULL,
219
- PRIMARY KEY (submission_id, seq),
220
- UNIQUE (submission_id, milestone_key)
221
- )`;
222
- sql`UPDATE pi_recovery_milestones
223
- SET body = json_remove(
224
- json_set(
225
- body,
226
- '$.approval.executionLevel', 'safe',
227
- '$.approval.requiredExecutionLevel',
228
- COALESCE(json_extract(body, '$.approval.risk'), 'safe')
229
- ),
230
- '$.approval.risk'
231
- )
232
- WHERE json_valid(body)
233
- AND json_extract(body, '$.version') = 1
234
- AND json_extract(body, '$.type') = 'approval'
235
- AND json_type(body, '$.turnId') = 'text'
236
- AND json_type(body, '$.assemblyRevision') = 'text'
237
- AND json_type(body, '$.approval') = 'object'
238
- AND json_type(body, '$.approval.executionId') = 'text'
239
- AND json_type(body, '$.approval.requestId') = 'text'
240
- AND json_extract(body, '$.approval.source') IN (
241
- 'action', 'codemode', 'temporary-agent'
242
- )
243
- AND json_type(body, '$.approval.toolCallId') = 'text'
244
- AND json_type(body, '$.approval.toolName') = 'text'
245
- AND json_type(body, '$.approval.summary') = 'text'
246
- AND json_type(body, '$.approval.inputJson') = 'text'
247
- AND json_type(body, '$.approval.createdAt') IN ('integer', 'real')
248
- AND json_extract(body, '$.approval.status') IN (
249
- 'pending', 'approved', 'rejected'
250
- )
251
- AND (
252
- json_type(body, '$.approval.risk') IS NULL
253
- OR json_type(body, '$.approval.risk') = 'null'
254
- OR json_extract(body, '$.approval.risk') IN (
255
- 'low', 'medium', 'high'
256
- )
257
- )
258
- AND json_type(body, '$.approval.executionLevel') IS NULL
259
- AND json_type(body, '$.approval.requiredExecutionLevel') IS NULL`;
260
193
  sql`CREATE TABLE IF NOT EXISTS pi_extension_context (
261
194
  label TEXT PRIMARY KEY,
262
195
  content TEXT NOT NULL,
@@ -7,29 +7,31 @@ import {
7
7
  type FiberRecoveryContext,
8
8
  type FiberRecoveryResult,
9
9
  } from "agents";
10
- import type { AgentEvent } from "@earendil-works/pi-agent-core";
10
+ import type { HarnessEvent } from "@earendil-works/pi-agent-core";
11
+ import type { UIMessageChunk } from "ai";
11
12
  import {
12
13
  AGENT_TYPES,
13
- } from "../../../layers/orchestration/subagents/agent-types/registry";
14
- import type { AgentType } from "../../../layers/orchestration/subagents/agent-types/contract";
14
+ } from "../../../tools/subagent-types/registry";
15
+ import type { AgentType } from "../../../tools/subagent-types/contract";
15
16
  import type {
16
17
  RuntimeProviderPort,
17
18
  WorkspacePort,
18
- } from "../../../kernel/bindings";
19
- import { PiChunkEncoder } from "../../../pi/message";
19
+ } from "../../../config/ports";
20
+ import { HarnessStreamProjection } from "../../../conversation/harness-stream";
20
21
  import {
21
22
  createPiModels,
22
23
  resolvePiModel,
23
- withProviderRetry,
24
- } from "../../../pi/runtime-adapter/models";
24
+ withProviderBoundary,
25
+ } from "../../../models";
25
26
  import { runCloudflareSubAgent } from "./runner";
27
+ import { openCloudflareSession } from "../../../session";
26
28
  import {
27
29
  addSubagentUsage,
28
30
  decodeSubagentCompletion,
29
31
  emptySubagentUsage,
30
32
  encodeSubagentCompletion,
31
- } from "../../../kernel/subagent-runtime";
32
- import type { RuntimeSubagentUsageMetrics } from "../../../kernel/submission-authority";
33
+ } from "../../../tools/subagent-runtime";
34
+ import type { RuntimeSubagentUsageMetrics } from "../../../tools/subagent-authority";
33
35
  import { createCloudflareSubAgentTools } from "./tools";
34
36
 
35
37
  type RunStatus =
@@ -92,24 +94,24 @@ function stringifyJson(value: unknown): string {
92
94
  }
93
95
 
94
96
  function progressFor(
95
- event: AgentEvent,
97
+ event: HarnessEvent,
96
98
  ): Omit<AgentToolProgressSnapshot, "at"> | null {
97
- if (event.type === "agent_start") {
99
+ if (event.type === "run_start" || event.type === "run_resume") {
98
100
  return { phase: "running", message: "SubAgent started" };
99
101
  }
100
- if (event.type === "tool_execution_start") {
102
+ if (event.type === "tool_start") {
101
103
  return {
102
104
  phase: "tool",
103
105
  message: `Running ${event.toolName}`,
104
106
  };
105
107
  }
106
- if (event.type === "tool_execution_update") {
108
+ if (event.type === "tool_update") {
107
109
  return {
108
110
  phase: "tool",
109
111
  message: `${event.toolName} is still running`,
110
112
  };
111
113
  }
112
- if (event.type === "tool_execution_end") {
114
+ if (event.type === "tool_end") {
113
115
  return {
114
116
  phase: event.isError ? "tool-error" : "tool",
115
117
  message: event.isError
@@ -362,6 +364,15 @@ export abstract class CloudflareSubAgent<
362
364
  `;
363
365
 
364
366
  const usage = this.readUsage(runId);
367
+ let streamSequence = 0;
368
+ let projection: HarnessStreamProjection | undefined;
369
+ const publish = (chunks: readonly UIMessageChunk[]) => {
370
+ for (const chunk of chunks) {
371
+ this.appendChunk(runId, stringifyJson({
372
+ v: 1, messageId: runId, sequence: streamSequence++, chunk,
373
+ }));
374
+ }
375
+ };
365
376
  try {
366
377
  const { provider, workspace, loader, outbound } =
367
378
  await this.prepareExecution();
@@ -387,16 +398,19 @@ export abstract class CloudflareSubAgent<
387
398
  );
388
399
  },
389
400
  });
390
- let streamSequence = 0;
391
- const encoder = new PiChunkEncoder({
401
+ projection = new HarnessStreamProjection({
392
402
  messageId: runId,
403
+ turnId: runId,
393
404
  startedAt: this.readRun(runId)?.started_at ?? Date.now(),
394
405
  });
395
406
  const output = await runCloudflareSubAgent({
396
407
  type,
397
408
  input,
398
409
  model,
399
- streamFn: withProviderRetry(models.streamSimple.bind(models)),
410
+ models,
411
+ openSession: () => openCloudflareSession(this.ctx.storage),
412
+ operationId: runId,
413
+ streamFn: withProviderBoundary(models.streamSimple.bind(models)),
400
414
  tools,
401
415
  signal,
402
416
  onEvent: (event) => {
@@ -404,20 +418,19 @@ export abstract class CloudflareSubAgent<
404
418
  addSubagentUsage(usage, event.message.usage);
405
419
  this.persistUsage(runId, usage);
406
420
  }
407
- for (const chunk of encoder.encode(event)) {
408
- this.appendChunk(runId, stringifyJson({
409
- v: 1,
410
- messageId: runId,
411
- sequence: streamSequence++,
412
- chunk,
413
- }));
414
- }
421
+ publish(projection!.event(event));
415
422
  const progress = progressFor(event);
416
423
  if (progress) this.persistProgress(runId, progress);
417
424
  },
418
425
  });
426
+ publish(projection.finish({ status: "completed", endedAt: Date.now() }));
419
427
  this.completeRun(runId, output, usage);
420
428
  } catch (error) {
429
+ if (projection) publish(projection.finish({
430
+ status: signal.aborted ? "aborted" : "failed",
431
+ endedAt: Date.now(),
432
+ error: { code: signal.aborted ? "aborted" : "failed", message: errorMessage(error) },
433
+ }));
421
434
  this.failRun(
422
435
  runId,
423
436
  signal.aborted ? "aborted" : "error",
@@ -611,16 +624,33 @@ export abstract class CloudflareSubAgent<
611
624
  typeof context.metadata?.runId === "string"
612
625
  ? context.metadata.runId
613
626
  : context.idempotencyKey;
614
- if (runId) {
615
- this.failRun(
616
- runId,
617
- "error",
618
- "SubAgent run was interrupted before completion",
619
- );
627
+ if (!runId) {
628
+ return { status: "interrupted", reason: "SubAgent run ID is missing" };
629
+ }
630
+ const row = this.readRun(runId);
631
+ if (!row) {
632
+ return { status: "interrupted", reason: "SubAgent run is missing" };
633
+ }
634
+ if (!TERMINAL.has(row.status)) {
635
+ const type = this.resolveType(row.agent_type);
636
+ const input = type.inputSchema.parse(parseJson(row.input_json));
637
+ await this.executeRun(runId, type, input, new AbortController().signal);
638
+ }
639
+ const recovered = this.readRun(runId);
640
+ if (recovered?.status === "completed") {
641
+ return { status: "completed", snapshot: { runId } };
642
+ }
643
+ if (recovered?.status === "aborted") {
644
+ return {
645
+ status: "aborted",
646
+ reason: recovered.error_message ?? "SubAgent run aborted",
647
+ snapshot: { runId },
648
+ };
620
649
  }
621
650
  return {
622
- status: "interrupted",
623
- reason: "SubAgent run was interrupted before completion",
651
+ status: "error",
652
+ error: recovered?.error_message ?? "SubAgent recovery failed",
653
+ snapshot: { runId },
624
654
  };
625
655
  }
626
656
  }