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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/package.json +7 -7
  2. package/src/{agent-tool-runtime.ts → adapter/cloudflare/agent-tool.ts} +1 -1
  3. package/src/adapter/cloudflare/agent.ts +1682 -0
  4. package/src/{db/index.ts → adapter/cloudflare/database.ts} +18 -55
  5. package/src/{runtime-agent.ts → adapter/cloudflare/define-agent.ts} +37 -42
  6. package/src/adapter/cloudflare/dynamic-worker-observability.ts +1 -1
  7. package/src/adapter/cloudflare/index.ts +18 -0
  8. package/src/adapter/cloudflare/resources/runtime-resources.ts +4 -4
  9. package/src/adapter/cloudflare/sandbox/adapter.ts +1 -1
  10. package/src/adapter/cloudflare/scheduling/schedule-rule.ts +322 -0
  11. package/src/adapter/cloudflare/scheduling/zoned-cron.ts +11 -6
  12. package/src/{db → adapter/cloudflare}/schema.ts +28 -80
  13. package/src/adapter/cloudflare/subagent/definition.ts +63 -33
  14. package/src/adapter/cloudflare/subagent/runner.ts +174 -120
  15. package/src/adapter/cloudflare/subagent/tools.ts +13 -14
  16. package/src/adapter/cloudflare/universal-agent/preparation.ts +7 -7
  17. package/src/adapter/cloudflare/universal-agent/tools.ts +10 -12
  18. package/src/adapter/cloudflare/workspace/git-fs.ts +1 -1
  19. package/src/adapter/cloudflare/workspace/scoped-workspace.ts +1 -1
  20. package/src/adapter/cloudflare/workspace/version-control.ts +2 -2
  21. package/src/config/activation.ts +140 -0
  22. package/src/{runtime-agent-context.ts → config/agent-context.ts} +3 -3
  23. package/src/{runtime-assembler.ts → config/assembler.ts} +45 -46
  24. package/src/{kernel/runtime-assembly.ts → config/assembly-projection.ts} +4 -4
  25. package/src/{pi/assembly/snapshot.ts → config/assembly-snapshot.ts} +6 -6
  26. package/src/{runtime-definition.ts → config/definition.ts} +13 -13
  27. package/src/config/harness-runtime.ts +190 -0
  28. package/src/config/index.ts +14 -0
  29. package/src/config/model-guidance.ts +21 -0
  30. package/src/{pi/runtime-adapter/assembly.ts → config/pinning.ts} +60 -19
  31. package/src/{kernel/bindings.ts → config/ports.ts} +32 -16
  32. package/src/{kernel → config}/profile.ts +7 -0
  33. package/src/{kernel → config}/public-contracts.ts +6 -6
  34. package/src/{kernel → config}/runtime-load.ts +1 -1
  35. package/src/{pi/assembly/context.ts → context/assembly.ts} +5 -207
  36. package/src/{pi/assembly/extensions.ts → context/extension/assembly.ts} +5 -5
  37. package/src/context/extension/index.ts +3 -0
  38. package/src/context/index.ts +6 -0
  39. package/src/{layers/context → context/memory}/budget/gate.ts +21 -5
  40. package/src/conversation/harness-resume.ts +288 -0
  41. package/src/conversation/harness-stream.ts +99 -0
  42. package/src/{pi/runtime-adapter/transcript.ts → conversation/history.ts} +206 -233
  43. package/src/conversation/index.ts +6 -0
  44. package/src/{pi → conversation}/message/contract.ts +3 -0
  45. package/src/{pi → conversation}/message/conversion.ts +75 -0
  46. package/src/{pi → conversation}/message/index.ts +1 -0
  47. package/src/{pi → conversation}/message/model-input.ts +1 -1
  48. package/src/{pi → conversation}/message/projection.ts +113 -30
  49. package/src/{db/message-ui.repo.ts → conversation/repository.ts} +4 -0
  50. package/src/conversation/state.ts +196 -0
  51. package/src/harness/index.ts +7 -0
  52. package/src/harness/session-harness.ts +562 -0
  53. package/src/harness/submission-operation.ts +49 -0
  54. package/src/index.ts +38 -43
  55. package/src/lib/artifacts.ts +18 -0
  56. package/src/lib/execution-level.ts +13 -0
  57. package/src/lib/mcp.ts +1 -1
  58. package/src/lib/prompt.ts +7 -6
  59. package/src/lib/token-estimate.ts +8 -0
  60. package/src/{pi/runtime-adapter/models.ts → models/index.ts} +64 -17
  61. package/src/{pi/turn/tool-recovery.ts → session/cloudflare/legacy-tool-recovery.ts} +33 -525
  62. package/src/{pi/session → session/cloudflare}/migration.ts +404 -14
  63. package/src/{pi/session → session/cloudflare}/storage.ts +8 -36
  64. package/src/session/index.ts +39 -0
  65. package/src/skills/springbrand-worker-website/index.ts +1 -1
  66. package/src/submission/admission.ts +286 -0
  67. package/src/{kernel/receipts.ts → submission/contracts.ts} +56 -2
  68. package/src/submission/delivery.ts +212 -0
  69. package/src/submission/finalization.ts +111 -0
  70. package/src/submission/index.ts +9 -0
  71. package/src/{kernel/submission-lifecycle.ts → submission/lifecycle.ts} +41 -28
  72. package/src/{db/submission.repo.ts → submission/repository.ts} +117 -114
  73. package/src/submission/steer-delivery.ts +167 -0
  74. package/src/{db/steer.repo.ts → submission/steer-repository.ts} +27 -5
  75. package/src/submission/unattended.ts +40 -0
  76. package/src/telemetry/contract.ts +12 -0
  77. package/src/telemetry/coordinator.ts +65 -0
  78. package/src/telemetry/delivery.ts +2 -2
  79. package/src/telemetry/index.ts +5 -0
  80. package/src/{model-usage.ts → telemetry/model-usage.ts} +1 -1
  81. package/src/telemetry/recorder.ts +1 -1
  82. package/src/telemetry/runtime-telemetry.ts +12 -0
  83. package/src/{kernel/durable-lifecycle.ts → telemetry/subagent-lifecycle.ts} +1 -1
  84. package/src/telemetry/turn-events.ts +279 -0
  85. package/src/tools/README.md +44 -0
  86. package/src/{pi/tool/ai-adapter.ts → tools/adapters/ai-to-pi.ts} +6 -9
  87. package/src/{pi/tool → tools/adapters}/declared.ts +6 -2
  88. package/src/{pi/tool/nested-tools.ts → tools/adapters/pi-to-ai.ts} +2 -2
  89. package/src/tools/assistant/ask-user.ts +317 -0
  90. package/src/tools/assistant/candidate.ts +15 -0
  91. package/src/tools/assistant/followups.ts +34 -0
  92. package/src/tools/assistant/index.ts +19 -0
  93. package/src/tools/assistant/memory.ts +73 -0
  94. package/src/tools/assistant/plan.ts +98 -0
  95. package/src/{pi/tool → tools/assistant}/time.ts +1 -1
  96. package/src/{pi/tool/core-host.ts → tools/codemode/adapter.ts} +50 -49
  97. package/src/{pi/tool/core.ts → tools/codemode/index.ts} +35 -91
  98. package/src/tools/codemode/workspace-connector.ts +198 -0
  99. package/src/tools/extension/index.ts +42 -0
  100. package/src/tools/index.ts +32 -0
  101. package/src/{pi/tool → tools/mcp}/gateway.ts +19 -19
  102. package/src/{pi/tool/mcp.ts → tools/mcp/index.ts} +5 -5
  103. package/src/tools/runtime/approval/index.ts +1 -0
  104. package/src/{db/approval.repo.ts → tools/runtime/approval/repository.ts} +22 -41
  105. package/src/tools/runtime/compiler.ts +264 -0
  106. package/src/tools/runtime/contracts.ts +117 -0
  107. package/src/tools/runtime/decision-gate.ts +337 -0
  108. package/src/tools/runtime/human-decisions.ts +127 -0
  109. package/src/tools/runtime/interaction/index.ts +1 -0
  110. package/src/{db/interaction.repo.ts → tools/runtime/interaction/repository.ts} +1 -7
  111. package/src/tools/runtime/result.ts +22 -0
  112. package/src/tools/runtime/unattended-error.ts +4 -0
  113. package/src/tools/sandbox/index.ts +228 -0
  114. package/src/tools/schedule/index.ts +301 -0
  115. package/src/tools/skill/contracts.ts +20 -0
  116. package/src/tools/skill/index.ts +99 -0
  117. package/src/tools/skill/resources.ts +322 -0
  118. package/src/tools/skill/scripts.ts +168 -0
  119. package/src/{kernel/submission-authority.ts → tools/subagent/authority.ts} +31 -31
  120. package/src/{pi/tool/subagent.ts → tools/subagent/index.ts} +33 -36
  121. package/src/{db/agent-tool.repo.ts → tools/subagent/repository.ts} +1 -1
  122. package/src/{kernel/subagent-runtime.ts → tools/subagent/runtime.ts} +1 -1
  123. package/src/tools/subagent/temporary/approvals.ts +123 -0
  124. package/src/{layers/orchestration/temporary-agent/core.ts → tools/subagent/temporary/coordinator.ts} +41 -49
  125. package/src/tools/subagent/temporary/core.ts +53 -0
  126. package/src/{layers/orchestration/temporary-agent → tools/subagent/temporary}/runner.ts +1 -1
  127. package/src/{layers/orchestration/temporary-agent → tools/subagent/temporary}/workspace.ts +1 -4
  128. package/src/{pi/tool/web-fetch.ts → tools/web/fetch.ts} +4 -13
  129. package/src/tools/web/index.ts +2 -0
  130. package/src/tools/web/search/api.ts +1320 -0
  131. package/src/tools/web/search/index.ts +2 -0
  132. package/src/{pi/tool/web-search/web-search.ts → tools/web/search/search.ts} +5 -7
  133. package/src/{pi/tool/workspace-sandbox.ts → tools/workspace/index.ts} +12 -272
  134. package/src/{pi/tool/workspace-revision.ts → tools/workspace/revision.ts} +2 -2
  135. package/src/db/milestone.repo.ts +0 -104
  136. package/src/db/settlement.repo.ts +0 -104
  137. package/src/kernel/approval-lifecycle.ts +0 -638
  138. package/src/kernel/interaction-lifecycle.ts +0 -433
  139. package/src/kernel/recoverable-chat-agent.ts +0 -1020
  140. package/src/kernel/state.ts +0 -91
  141. package/src/pi/assembly/index.ts +0 -19
  142. package/src/pi/runtime-adapter/execution.ts +0 -1163
  143. package/src/pi/runtime-adapter/index.ts +0 -293
  144. package/src/pi/runtime-adapter/recovery.ts +0 -982
  145. package/src/pi/session/index.ts +0 -24
  146. package/src/pi/tool/base.ts +0 -550
  147. package/src/pi/tool/compiler.ts +0 -483
  148. package/src/pi/tool/index.ts +0 -38
  149. package/src/pi/tool/schedule.ts +0 -261
  150. package/src/pi/tool/skill.ts +0 -565
  151. package/src/pi/tool/web-search/api.ts +0 -1318
  152. package/src/pi/tool/web-search/index.ts +0 -2
  153. package/src/pi/tool/workspace-state-connector.ts +0 -146
  154. package/src/pi/turn/approval.ts +0 -181
  155. package/src/pi/turn/index.ts +0 -82
  156. package/src/pi/turn/interaction.ts +0 -181
  157. package/src/runtime.ts +0 -4760
  158. package/src/{kernel/runtime-assembly-view.ts → config/assembly-view.ts} +0 -0
  159. package/src/{kernel → config}/degradation.ts +0 -0
  160. package/src/{kernel → config}/runtime-config.ts +0 -0
  161. package/src/{kernel/extensions.ts → context/extension/contracts.ts} +0 -0
  162. package/src/{db/ext-context.repo.ts → context/extension/repository.ts} +0 -0
  163. package/src/{pi/runtime-adapter/openrouter-messages.ts → models/openrouter/messages.ts} +0 -0
  164. package/src/{pi/runtime-adapter → models/openrouter}/test-fixtures/openrouter-tool-search.sse +0 -0
  165. package/src/{db/submission-admission.repo.ts → submission/admission-repository.ts} +0 -0
  166. package/src/{db/telemetry-outbox.repo.ts → telemetry/repository.ts} +0 -0
  167. package/src/{db/runtime-event-outbox.repo.ts → telemetry/runtime-event-repository.ts} +0 -0
  168. package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/contract.ts +0 -0
  169. package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/extract/index.ts +0 -0
  170. package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/fanout/index.ts +0 -0
  171. package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/registry.ts +1 -1
  172. /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> {
@@ -114,7 +114,7 @@ interface RuntimeAgentDefinitionBase<
114
114
  /** Prepare the optional telemetry consumer for this concrete Agent facet. */
115
115
  readonly telemetry?: (
116
116
  context: RuntimeAgentPlanningContext<Env, Command, Change>,
117
- ) => AgentTelemetryBinding | undefined;
117
+ ) => AgentTelemetryBinding | undefined | Promise<AgentTelemetryBinding | undefined>;
118
118
  }
119
119
 
120
120
  export interface ReadonlyRuntimeAgentDefinition<
@@ -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
  *
@@ -271,7 +262,12 @@ export interface RuntimeAgentControls {
271
262
  */
272
263
  submitScheduledPrompt(
273
264
  prompt: string,
274
- opts?: { idempotencyKey?: string },
265
+ opts?: {
266
+ idempotencyKey?: string;
267
+ scheduledMaxExecutionLevel?: import("../../lib/execution-level").ExecutionLevel;
268
+ /** @deprecated Retained for rolling upgrades; ignored by Runtime. */
269
+ unattendedExecutionGrant?: import("../../submission").RuntimeUnattendedExecutionGrant;
270
+ },
275
271
  ): Promise<SubmissionReceipt>;
276
272
  /**
277
273
  * 按提交编号查询当前处理状态。
@@ -318,7 +314,7 @@ export interface RuntimeAgentConfigControls<Command, Change> {
318
314
  /** Type-only Host extension seam implemented by AgentRuntimeKernel. */
319
315
  declare class RuntimeAgentHostSeam {
320
316
  protected prepareToolInteractionResponse(
321
- input: import("./runtime").ToolInteractionResponsePreparation,
317
+ input: import("./agent").ToolInteractionResponsePreparation,
322
318
  ): Promise<unknown | null>;
323
319
  }
324
320
 
@@ -415,14 +411,14 @@ export function defineRuntimeAgent<
415
411
  | MutableRuntimeAgentDefinition<Env, Config, Command, Change>
416
412
  | ReadonlyRuntimeAgentDefinition<Env, Config>,
417
413
  ): RuntimeAgentClass<Env, Command, Change> {
418
- const resolveDefinitionTelemetry = (
414
+ const resolveDefinitionTelemetry = async (
419
415
  context: RuntimeAgentPlanningContext<Env, Command, Change>,
420
- ): AgentTelemetryBinding | undefined => {
416
+ ): Promise<AgentTelemetryBinding | undefined> => {
421
417
  const telemetry = definition.telemetry;
422
418
  if (!telemetry) return undefined;
423
- return (telemetry as (
419
+ return await (telemetry as (
424
420
  context: RuntimeAgentPlanningContext<Env, Command, Change>,
425
- ) => AgentTelemetryBinding | undefined)(context);
421
+ ) => AgentTelemetryBinding | undefined | Promise<AgentTelemetryBinding | undefined>)(context);
426
422
  };
427
423
 
428
424
  const GeneratedRuntimeAgent = {
@@ -449,6 +445,10 @@ export function defineRuntimeAgent<
449
445
  return this.ensureConfig();
450
446
  }
451
447
 
448
+ protected override ensureRuntimeVersion(runtimeKey: string): Promise<void> {
449
+ return this.ensureConfig(runtimeKey);
450
+ }
451
+
452
452
  protected async prepareRuntimeForSubmission(submissionId: string): Promise<void> {
453
453
  const beforeSubmission = definition.beforeSubmission as RuntimeAgentDefinitionBase<Env, Config, Command, Change>["beforeSubmission"];
454
454
  try {
@@ -502,14 +502,6 @@ export function defineRuntimeAgent<
502
502
  }
503
503
  }
504
504
 
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
505
  async getRuntimeAssembly(): Promise<RuntimeAssemblyView> {
514
506
  await this.ensureRuntimeReady();
515
507
  return this.readRuntimeAssembly();
@@ -648,7 +640,7 @@ export function defineRuntimeAgent<
648
640
  let resources = loaded.resources;
649
641
  let commitGuard = loaded.commitGuard;
650
642
  if (!this.telemetryResolved) {
651
- this.resolvedTelemetry = resolveDefinitionTelemetry(context);
643
+ this.resolvedTelemetry = await resolveDefinitionTelemetry(context);
652
644
  this.telemetryResolved = true;
653
645
  }
654
646
  const telemetry = this.resolvedTelemetry;
@@ -666,7 +658,10 @@ export function defineRuntimeAgent<
666
658
  ...(telemetry ? { telemetry } : {}),
667
659
  });
668
660
 
669
- await this.initCandidate(candidate);
661
+ await this.initCandidate({
662
+ ...candidate,
663
+ runtimeKey: resolvedRuntimeKey,
664
+ });
670
665
  this.hasLoadedRuntime = true;
671
666
  this.loadedRuntimeKey = resolvedRuntimeKey;
672
667
  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 = {
@@ -23,8 +23,26 @@ export {
23
23
  export {
24
24
  isValidTimeZone,
25
25
  nextCronOccurrence,
26
+ wallTimeInstants,
26
27
  zoneOffsetMs,
27
28
  } from "./scheduling/zoned-cron";
29
+ export {
30
+ DEFAULT_LATENESS_WINDOW_MS,
31
+ isWithinLatenessWindow,
32
+ nextScheduledOccurrence,
33
+ resolveScheduleOccurrences,
34
+ toScheduleRuleSpec,
35
+ validateScheduleRuleSpec,
36
+ type LocalScheduleTrigger,
37
+ type ScheduledOccurrence,
38
+ type ScheduleOccurrence,
39
+ type ScheduleOccurrenceResolution,
40
+ type ScheduleRuleError,
41
+ type ScheduleRuleErrorCode,
42
+ type ScheduleRuleSpec,
43
+ type ScheduleSkipReason,
44
+ type SkippedOccurrence,
45
+ } from "./scheduling/schedule-rule";
28
46
 
29
47
  export {
30
48
  CloudflareSandboxAdapter,
@@ -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,
@@ -0,0 +1,322 @@
1
+ import type {
2
+ LocalScheduleRule,
3
+ ScheduleSpec,
4
+ } from "../../../submission/contracts";
5
+ import { isValidTimeZone, wallTimeInstants, zoneOffsetMs } from "./zoned-cron";
6
+
7
+ const DAY_MS = 86_400_000;
8
+ const HOUR_MS = 3_600_000;
9
+ const MINUTE_MS = 60_000;
10
+ /** 扫描上限:每日/每周规则一定在 7 天内出现,这里只兜底防止坏输入空转。 */
11
+ const MAX_SCAN_DAYS = 366 * 5;
12
+
13
+ /**
14
+ * 宿主允许在计划时点之后多久仍可启动该轮次。宿主可覆盖,
15
+ * 通用模块只把它当作可参数化的迟到窗口,不承载套餐或任务策略。
16
+ */
17
+ export const DEFAULT_LATENESS_WINDOW_MS = 5 * MINUTE_MS;
18
+
19
+ export type ScheduleSkipReason = "dst_missing_local_time";
20
+
21
+ /** 一个真实可执行的本地时间轮次。 */
22
+ export interface ScheduledOccurrence {
23
+ kind: "scheduled";
24
+ /** 跨重算、重启都稳定的轮次身份,供宿主做幂等键。 */
25
+ occurrenceId: string;
26
+ /** 计划执行的 UTC 瞬时。 */
27
+ scheduledAt: number;
28
+ /** 任务时区下的本地日期 `YYYY-MM-DD`。 */
29
+ localDate: string;
30
+ /** 任务时区下的本地钟点 `HH:mm`。 */
31
+ localTime: string;
32
+ /** 含边界的最迟可启动瞬时 = `scheduledAt + latenessWindowMs`。 */
33
+ latestStartAt: number;
34
+ }
35
+
36
+ /** 因夏令时缺失钟点而被跳过的轮次;没有可执行瞬时,只留身份与原因。 */
37
+ export interface SkippedOccurrence {
38
+ kind: "skipped";
39
+ occurrenceId: string;
40
+ localDate: string;
41
+ localTime: string;
42
+ reason: ScheduleSkipReason;
43
+ }
44
+
45
+ export type ScheduleOccurrence = ScheduledOccurrence | SkippedOccurrence;
46
+
47
+ /** 一条按任务时区解释的当地时间规则及其生效窗口。 */
48
+ export interface ScheduleRuleSpec {
49
+ rule: LocalScheduleRule;
50
+ /** IANA 时区名,如 `Asia/Shanghai`;绝不退化成固定 UTC offset。 */
51
+ timeZone: string;
52
+ /** 含当天的最早生效本地日期 `YYYY-MM-DD`。 */
53
+ startDate?: string;
54
+ /** 含当天的最后生效本地日期 `YYYY-MM-DD`。 */
55
+ endDate?: string;
56
+ /** 覆盖默认迟到窗口。 */
57
+ latenessWindowMs?: number;
58
+ }
59
+
60
+ /** `ScheduleSpec.trigger` 里 `local` 变体的形状。 */
61
+ export type LocalScheduleTrigger = Extract<
62
+ ScheduleSpec["trigger"],
63
+ { kind: "local" }
64
+ >;
65
+
66
+ /**
67
+ * 把合同的 `local` 触发条件投影成时间规则计算规格。
68
+ *
69
+ * 宿主与路由共用同一映射,避免各自拼一份 `startDate`/`endDate`,改合同字段时漏改。
70
+ */
71
+ export function toScheduleRuleSpec(
72
+ trigger: LocalScheduleTrigger,
73
+ timeZone: string,
74
+ ): ScheduleRuleSpec {
75
+ return {
76
+ rule: trigger.rule,
77
+ timeZone,
78
+ ...(trigger.startDate ? { startDate: trigger.startDate } : {}),
79
+ ...(trigger.endDate ? { endDate: trigger.endDate } : {}),
80
+ };
81
+ }
82
+
83
+ export type ScheduleRuleErrorCode =
84
+ | "invalid_rule"
85
+ | "invalid_time_zone"
86
+ | "invalid_date"
87
+ | "invalid_time"
88
+ | "empty_weekdays"
89
+ | "invalid_weekday"
90
+ | "invalid_window"
91
+ | "invalid_limit"
92
+ | "no_future_occurrence";
93
+
94
+ export interface ScheduleRuleError {
95
+ code: ScheduleRuleErrorCode;
96
+ message: string;
97
+ }
98
+
99
+ export type ScheduleOccurrenceResolution =
100
+ | { ok: true; occurrences: ScheduleOccurrence[] }
101
+ | { ok: false; error: ScheduleRuleError };
102
+
103
+ interface LocalClock {
104
+ hour: number;
105
+ minute: number;
106
+ }
107
+
108
+ /** 校验规则形状与时区,不判断是否存在未来时点。 */
109
+ export function validateScheduleRuleSpec(
110
+ spec: ScheduleRuleSpec,
111
+ ): ScheduleRuleError | null {
112
+ if (typeof spec.timeZone !== "string" || !isValidTimeZone(spec.timeZone)) {
113
+ return { code: "invalid_time_zone", message: `未知时区:${spec.timeZone}` };
114
+ }
115
+ const rule = spec.rule as unknown;
116
+ if (!isRecord(rule) ||
117
+ (rule.kind !== "once" && rule.kind !== "daily" && rule.kind !== "weekly")) {
118
+ return { code: "invalid_rule", message: "不支持的当地时间规则" };
119
+ }
120
+ const time = typeof rule.time === "string" ? parseTime(rule.time) : null;
121
+ if (!time) {
122
+ return { code: "invalid_time", message: `非法钟点:${String(rule.time ?? "")}` };
123
+ }
124
+ if (rule.kind === "once" &&
125
+ (typeof rule.date !== "string" || parseDate(rule.date) === null)) {
126
+ return { code: "invalid_date", message: `非法日期:${String(rule.date ?? "")}` };
127
+ }
128
+ if (rule.kind === "weekly") {
129
+ if (!Array.isArray(rule.weekdays) || rule.weekdays.length === 0) {
130
+ if (Array.isArray(rule.weekdays)) {
131
+ return { code: "empty_weekdays", message: "每周规则至少需要一个星期" };
132
+ }
133
+ return { code: "invalid_weekday", message: "星期必须是数字数组" };
134
+ }
135
+ if (
136
+ rule.weekdays.some(
137
+ (weekday) => !Number.isInteger(weekday) || weekday < 0 || weekday > 6,
138
+ )
139
+ ) {
140
+ return {
141
+ code: "invalid_weekday",
142
+ message: `非法星期:${rule.weekdays.join(",")}`,
143
+ };
144
+ }
145
+ }
146
+ const start = optionalDate(spec.startDate);
147
+ if (spec.startDate !== undefined && start === null) {
148
+ return { code: "invalid_date", message: `非法开始日期:${spec.startDate}` };
149
+ }
150
+ const end = optionalDate(spec.endDate);
151
+ if (spec.endDate !== undefined && end === null) {
152
+ return { code: "invalid_date", message: `非法结束日期:${spec.endDate}` };
153
+ }
154
+ if (start !== null && end !== null && end < start) {
155
+ return { code: "invalid_window", message: "结束日期不能早于开始日期" };
156
+ }
157
+ return null;
158
+ }
159
+
160
+ /**
161
+ * 从 `after`(UTC 瞬时,不含)起枚举轮次,含被跳过的缺失钟点。
162
+ *
163
+ * 预览、下一轮计算和恢复枚举都走这里,保证三者语义一致;输出为空时返回可识别的
164
+ * `no_future_occurrence`,而不是伪造一个执行时间。
165
+ */
166
+ export function resolveScheduleOccurrences(
167
+ spec: ScheduleRuleSpec,
168
+ options: { after: number; limit: number },
169
+ ): ScheduleOccurrenceResolution {
170
+ const error = validateScheduleRuleSpec(spec);
171
+ if (error) return { ok: false, error };
172
+ if (!Number.isSafeInteger(options.limit) || options.limit <= 0) {
173
+ return {
174
+ ok: false,
175
+ error: { code: "invalid_limit", message: "轮次数量必须是正整数" },
176
+ };
177
+ }
178
+ const occurrences = enumerate(spec, options.after, options.limit);
179
+ if (occurrences.length === 0) {
180
+ return {
181
+ ok: false,
182
+ error: {
183
+ code: "no_future_occurrence",
184
+ message: "该规则在给定时间之后没有有效执行时点",
185
+ },
186
+ };
187
+ }
188
+ return { ok: true, occurrences };
189
+ }
190
+
191
+ /** 下一个可执行瞬时;缺失钟点被跳过,不伪造、不顺延补跑。 */
192
+ export function nextScheduledOccurrence(
193
+ spec: ScheduleRuleSpec,
194
+ after: number,
195
+ ): ScheduledOccurrence | null {
196
+ if (validateScheduleRuleSpec(spec) !== null) return null;
197
+ for (let limit = 1; limit <= 512; limit *= 2) {
198
+ const occurrences = enumerate(spec, after, limit);
199
+ const scheduled = occurrences.find(
200
+ (occurrence): occurrence is ScheduledOccurrence =>
201
+ occurrence.kind === "scheduled",
202
+ );
203
+ if (scheduled) return scheduled;
204
+ if (occurrences.length < limit) return null;
205
+ }
206
+ return null;
207
+ }
208
+
209
+ /** 含边界判断某个已计划轮次是否仍在迟到窗口内。 */
210
+ export function isWithinLatenessWindow(
211
+ occurrence: ScheduledOccurrence,
212
+ now: number,
213
+ ): boolean {
214
+ return now >= occurrence.scheduledAt && now <= occurrence.latestStartAt;
215
+ }
216
+
217
+ function enumerate(
218
+ spec: ScheduleRuleSpec,
219
+ after: number,
220
+ limit: number,
221
+ ): ScheduleOccurrence[] {
222
+ const time = parseTime(spec.rule.time);
223
+ if (!time) return [];
224
+ const windowMs = spec.latenessWindowMs ?? DEFAULT_LATENESS_WINDOW_MS;
225
+ const wallAfter = after + zoneOffsetMs(after, spec.timeZone);
226
+ const dayOfAfter = Math.floor(wallAfter / DAY_MS) * DAY_MS;
227
+ const startDay = optionalDate(spec.startDate) ?? dayOfAfter;
228
+ const endDay = optionalDate(spec.endDate);
229
+ const occurrences: ScheduleOccurrence[] = [];
230
+
231
+ const onceDay = spec.rule.kind === "once" ? parseDate(spec.rule.date) : null;
232
+ // 一次性规则直接跳到目标日期,不能让重复规则的扫描保险上限拒绝远期任务。
233
+ let day = Math.max(startDay, dayOfAfter, onceDay ?? Number.NEGATIVE_INFINITY);
234
+ const maxScanDays = onceDay === null ? MAX_SCAN_DAYS : 0;
235
+ for (let scanned = 0; scanned <= maxScanDays; scanned += 1, day += DAY_MS) {
236
+ if (endDay !== null && day > endDay) break;
237
+ if (!matchesRule(spec.rule, day)) continue;
238
+
239
+ const wall = day + time.hour * HOUR_MS + time.minute * MINUTE_MS;
240
+ const localDate = formatDate(day);
241
+ const localTime = formatTime(time);
242
+ const occurrenceId = `${localDate}T${localTime}`;
243
+ const instants = wallTimeInstants(wall, spec.timeZone);
244
+ if (instants.length === 0) {
245
+ // 拨快抹掉的钟点:没有可执行瞬时,只在游标之后才作为跳过轮次报告。
246
+ if (wall <= wallAfter) continue;
247
+ occurrences.push({
248
+ kind: "skipped",
249
+ occurrenceId,
250
+ localDate,
251
+ localTime,
252
+ reason: "dst_missing_local_time",
253
+ });
254
+ } else {
255
+ const scheduledAt = instants[0];
256
+ if (scheduledAt <= after) continue;
257
+ occurrences.push({
258
+ kind: "scheduled",
259
+ occurrenceId,
260
+ scheduledAt,
261
+ localDate,
262
+ localTime,
263
+ latestStartAt: scheduledAt + windowMs,
264
+ });
265
+ }
266
+ if (occurrences.length >= limit) break;
267
+ }
268
+ return occurrences;
269
+ }
270
+
271
+ function matchesRule(rule: LocalScheduleRule, day: number): boolean {
272
+ switch (rule.kind) {
273
+ case "once":
274
+ return day === parseDate(rule.date);
275
+ case "daily":
276
+ return true;
277
+ case "weekly":
278
+ return rule.weekdays.includes(new Date(day).getUTCDay());
279
+ }
280
+ }
281
+
282
+ function parseTime(value: string): LocalClock | null {
283
+ const match = /^([01]\d|2[0-3]):([0-5]\d)$/.exec(value);
284
+ if (!match) return null;
285
+ return { hour: Number(match[1]), minute: Number(match[2]) };
286
+ }
287
+
288
+ function formatTime({ hour, minute }: LocalClock): string {
289
+ return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`;
290
+ }
291
+
292
+ /** `YYYY-MM-DD` → UTC 零点毫秒;非法日历日期返回 null。 */
293
+ function parseDate(value: string): number | null {
294
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
295
+ if (!match) return null;
296
+ const year = Number(match[1]);
297
+ const month = Number(match[2]);
298
+ const day = Number(match[3]);
299
+ const ms = Date.UTC(year, month - 1, day);
300
+ const date = new Date(ms);
301
+ if (
302
+ date.getUTCFullYear() !== year ||
303
+ date.getUTCMonth() !== month - 1 ||
304
+ date.getUTCDate() !== day
305
+ ) {
306
+ return null;
307
+ }
308
+ return ms;
309
+ }
310
+
311
+ function optionalDate(value: string | undefined): number | null {
312
+ if (value === undefined) return null;
313
+ return parseDate(value);
314
+ }
315
+
316
+ function formatDate(day: number): string {
317
+ return new Date(day).toISOString().slice(0, 10);
318
+ }
319
+
320
+ function isRecord(value: unknown): value is Record<string, unknown> {
321
+ return typeof value === "object" && value !== null && !Array.isArray(value);
322
+ }