@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,16 +1,17 @@
1
+ import { sha256Hex } from "../lib/artifacts";
1
2
  import type {
2
3
  RuntimeBindings,
3
4
  RuntimeGatewaySession,
4
5
  RuntimePlatformPort,
5
- } from "../../kernel/bindings";
6
- import type { RuntimeDegradation } from "../../kernel/degradation";
6
+ } from "./ports";
7
+ import type { RuntimeDegradation } from "./degradation";
7
8
  import type {
8
9
  RuntimeExtensionPermissions,
9
- } from "../../kernel/extensions";
10
- import type { RuntimeProfile } from "../../kernel/profile";
11
- import type { ExecutionLevel } from "../../lib/execution-level";
12
- import type { RuntimeSnapshot } from "../../runtime-assembler";
13
- import type { FinalizedPiToolSurface, PiRuntimeAssembly } from "../assembly";
10
+ } from "../context/extension/contracts";
11
+ import type { RuntimeProfile } from "./profile";
12
+ import type { ExecutionLevel } from "../lib/execution-level";
13
+ import type { RuntimeSnapshot } from "./assembler";
14
+ import type { FinalizedPiToolSurface, PiRuntimeAssembly } from "../context";
14
15
  import {
15
16
  assemblePiSystemContext,
16
17
  assemblePiExtensions,
@@ -18,14 +19,35 @@ import {
18
19
  type PiExtensionAssembly,
19
20
  type PiExtensionContextContribution,
20
21
  type PiLoadedExtension,
21
- } from "../assembly";
22
+ } from "../context";
22
23
  import {
23
24
  createPiGatewayToolCandidates,
24
25
  createPiMcpToolCandidates,
25
26
  listExtensionsPiToolCandidate,
26
27
  type PiMcpHost,
27
28
  type PiToolCandidate,
28
- } from "../tool";
29
+ } from "../tools";
30
+
31
+ export interface RuntimeAssemblyPin {
32
+ readonly revision: string;
33
+ readonly descriptor: string;
34
+ }
35
+
36
+ /** A saved pin is reusable only with its original Runtime key and intact descriptor. */
37
+ export async function validateRuntimeAssemblyPin(
38
+ runtimeKey: string,
39
+ pin: RuntimeAssemblyPin,
40
+ ): Promise<boolean> {
41
+ if (!runtimeKey) {
42
+ throw new Error("Cannot safely recover a pinned Submission without its original Runtime key");
43
+ }
44
+ if (!pin.revision && !pin.descriptor) return false;
45
+ if (!pin.revision || !pin.descriptor) throw new Error("Submission assembly pin is incomplete");
46
+ if (pin.revision !== await sha256Hex(pin.descriptor)) {
47
+ throw new Error("Pinned Runtime assembly descriptor is invalid");
48
+ }
49
+ return true;
50
+ }
29
51
 
30
52
  /**
31
53
  * 本文件负责准备 Pi 的扩展与工具,并为每次提交固定可恢复的运行描述。
@@ -85,7 +107,7 @@ interface PreparedPiAssembly {
85
107
  * 提供准备 Pi 所需的已校验 Runtime 快照和 Host 端口。
86
108
  *
87
109
  * @remarks
88
- * `PiRuntimeAdapter.prepare` 在加载 Runtime 配置时传入它。
110
+ * `HarnessRuntimeConfig.prepare` 在加载 Runtime 配置时传入它。
89
111
  *
90
112
  * 扩展绑定仍由 Host 创建,因为 Cloudflare 绑定属于 Worker 的能力边界。
91
113
  *
@@ -128,13 +150,18 @@ export interface PreparedPiRuntime {
128
150
  * 提供把一次提交固定到当前 Runtime 所需的值。
129
151
  *
130
152
  * @remarks
131
- * Runtime 在写入提交前的准入阶段把它交给 `PiRuntimeAdapter.pin`。
153
+ * Runtime 在写入提交前的准入阶段把它交给 `HarnessRuntimeConfig.pin`。
132
154
  *
133
155
  * 扩展没有可读上下文时不需要读取函数,所以该端口保持可选。
134
156
  */
135
157
  export interface PinPiRuntimeOptions {
136
158
  readonly prepared: PreparedPiRuntime;
137
159
  readonly baseRevision: string;
160
+ /**
161
+ * Whether this Submission may inherit automatic hot and cold Memory context.
162
+ * Scheduled task input is intentionally isolated from those sources.
163
+ */
164
+ readonly includeMemory?: boolean;
138
165
  /**
139
166
  * 读取一个扩展要写入固定系统提示词的上下文。
140
167
  *
@@ -165,7 +192,7 @@ export interface PinnedPiRuntime {
165
192
  * 描述一条已存提交可以恢复到哪份 Pi 配置。
166
193
  *
167
194
  * @remarks
168
- * `PreparedPiTurnAdapter` 在重建 Turn 前读取这个 JSON 结构。
195
+ * `HarnessRuntimeConfig.harnessInputs` 在打开或恢复 operation 前读取这个 JSON 结构。
169
196
  *
170
197
  * 基础版本作为准入时元数据保留,创建 Turn 时不与当前 Runtime 比较。
171
198
  */
@@ -227,9 +254,20 @@ function describeTools(candidates: readonly PiToolCandidate[]) {
227
254
  description: candidate.tool.description ?? candidate.tool.label,
228
255
  parameters: candidate.tool.parameters,
229
256
  requiredExecutionLevel: candidate.requiredExecutionLevel,
257
+ ...(candidate.confirmation ? { confirmation: true } : {}),
230
258
  ...(candidate.alwaysRequiresApproval
231
259
  ? { alwaysRequiresApproval: true }
232
260
  : {}),
261
+ ...(candidate.unattendedExecution
262
+ ? {
263
+ unattendedExecution: {
264
+ capability: candidate.unattendedExecution.capability,
265
+ operation: candidate.unattendedExecution.operation,
266
+ objectConstrained:
267
+ candidate.unattendedExecution.objectForInput !== undefined,
268
+ },
269
+ }
270
+ : {}),
233
271
  ...(candidate.deferLoading ? { deferLoading: true } : {}),
234
272
  ...(candidate.source ? { source: candidate.source } : {}),
235
273
  retry: piToolRetryPolicy(candidate),
@@ -282,7 +320,7 @@ function describeRuntime(
282
320
  class PreparedRuntime implements PreparedPiRuntime {
283
321
  // 把公开版本数据与受身份保护的内部准备状态放在同一个句柄里。
284
322
  // 只有 preparePiRuntime 会构造它,并在返回前冻结实例。
285
- // owner 使用对象身份区分 PiRuntimeAdapter,避免不同适配器的句柄被混用。
323
+ // owner 使用对象身份区分 HarnessRuntimeConfig,避免不同适配器的句柄被混用。
286
324
  constructor(
287
325
  readonly revisionDescriptor: string,
288
326
  readonly degradations: readonly RuntimeDegradation[],
@@ -305,7 +343,7 @@ class PreparedRuntime implements PreparedPiRuntime {
305
343
  }
306
344
 
307
345
  /**
308
- * 为创建它的 PiRuntimeAdapter 打开准备句柄。
346
+ * 为创建它的 HarnessRuntimeConfig 打开准备句柄。
309
347
  *
310
348
  * @remarks
311
349
  * 固定配置和创建 Turn 都会先调用它,再读取快照、装配结果或工具候选。
@@ -326,7 +364,7 @@ export function readPreparedPiRuntime(
326
364
  * 读取已存的固定描述。
327
365
  *
328
366
  * @remarks
329
- * `PreparedPiTurnAdapter` 在新建或恢复 Pi Turn 前调用它。
367
+ * `HarnessRuntimeConfig.harnessInputs` 在新建或恢复 Pi operation 前调用它。
330
368
  *
331
369
  * Prepared Runtime 提供当前执行能力,descriptor 仅保留 Submission 固定的 Turn 字段。
332
370
  */
@@ -415,7 +453,7 @@ async function preparePiAssembly(
415
453
  * 为一份已校验的 Runtime 快照准备扩展和工具状态。
416
454
  *
417
455
  * @remarks
418
- * `PiRuntimeAdapter.prepare` 在 Runtime 配置阶段、快照生效前调用它。
456
+ * `HarnessRuntimeConfig.prepare` 在 Runtime 配置阶段、快照生效前调用它。
419
457
  *
420
458
  * 冻结句柄会保存最终候选列表,对外只提供版本描述和降级结果。
421
459
  */
@@ -459,11 +497,14 @@ export async function pinPiRuntime(
459
497
  ): Promise<PinnedPiRuntime> {
460
498
  const state = readPreparedPiRuntime(options.prepared, owner);
461
499
  const { snapshot, assembly } = state;
500
+ const includeMemory = options.includeMemory !== false;
462
501
  const context = await assemblePiSystemContext({
463
502
  baseSystemPrompt: snapshot.pi.systemPrompt,
464
- memory: snapshot.profile.memory,
465
- memoryPort: snapshot.bindings.memory,
466
- workspace: snapshot.bindings.workspace,
503
+ memory: includeMemory
504
+ ? snapshot.profile.memory
505
+ : { ...snapshot.profile.memory, enabled: false },
506
+ memoryPort: includeMemory ? snapshot.bindings.memory : undefined,
507
+ workspace: includeMemory ? snapshot.bindings.workspace : undefined,
467
508
  skills: snapshot.bindings.skills.sources,
468
509
  extensionContexts: assembly.context,
469
510
  readExtensionContext: options.readExtensionContext,
@@ -1,15 +1,18 @@
1
+ export type { RuntimeSchedulePreparation } from "../submission/contracts";
2
+ import type { RuntimeModelInputModality } from "./profile";
1
3
  import type { SkillSource } from "agents/skills";
2
- import type { ScheduleSpec } from "./receipts";
3
- import type { RuntimeActivityProjection } from "./state";
4
+ import type { ScheduleSpec, RuntimeSchedulePreparation } from "../submission/contracts";
5
+ import type { RuntimeActivityProjection } from "../conversation/state";
4
6
  import type { ExecutionLevel } from "../lib/execution-level";
5
7
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
6
- import type { PiToolCandidate } from "../pi/tool/compiler";
8
+ import type { PiToolCandidate } from "../tools/runtime/contracts";
7
9
  import type {
8
10
  RuntimeEventConfirmation,
9
11
  RuntimeLifecycleFact,
10
12
  RuntimeSubagentUsageEvent,
11
13
  RuntimeSubagentUsageMetrics,
12
- } from "./submission-authority";
14
+ } from "../tools/subagent/authority";
15
+ export type { RuntimeModelInputModality } from "./profile";
13
16
  export type {
14
17
  RuntimeEventConfirmation,
15
18
  RuntimeLifecycleFact,
@@ -19,7 +22,7 @@ export type {
19
22
  RuntimeSubmissionAdmissionHook,
20
23
  RuntimeSubmissionAdmissionInput,
21
24
  RuntimeSubmissionAdmissionResult,
22
- } from "./submission-authority";
25
+ } from "../tools/subagent/authority";
23
26
 
24
27
  // #region Workspace 端口
25
28
 
@@ -624,7 +627,7 @@ export interface RuntimeGatewayPort {
624
627
  }
625
628
 
626
629
  /** Safe catalog from an external Host; credentials and SDK clients stay there. */
627
- export type RuntimeMcpHost = import("../pi/tool/mcp").PiMcpHost & {
630
+ export type RuntimeMcpHost = import("../tools/mcp").PiMcpHost & {
628
631
  readonly servers: readonly import("./profile").RuntimeMcpServer[];
629
632
  };
630
633
 
@@ -667,16 +670,25 @@ export interface RuntimeScheduleAgentSummary {
667
670
  * 让 Runtime 通过 Host 管理当前用户的定时任务。
668
671
  *
669
672
  * @remarks
670
- * Schedule Plugin 在装配时把它交给基础工具,模型在用户要求创建、查询或取消时调用。
673
+ * Host 提供准备与提交操作;Runtime 在现有持久化审批中固定候选并等待用户确认。
671
674
  *
672
- * 任务归属和持久化留在 Host,Runtime 只传递 `ScheduleSpec` 和窄结果。
675
+ * 任务事实、候选版本、授权检查和保存幂等由 Host 持有,不存放凭据。
673
676
  */
677
+ export interface RuntimeScheduleConfirmationPort {
678
+ /** Read-only preparation; bind the full candidate and authorization to this stable intent. */
679
+ prepare(request: RuntimeScheduleUpdate & { intentId: string; id?: string }): Promise<RuntimeSchedulePreparation>;
680
+ /** Atomically check candidate/version, live authorization and quotas. Replay must return the same task. */
681
+ commit(prepared: RuntimeSchedulePreparation, context: { intentId: string; signal: AbortSignal }): Promise<{ id: string }>;
682
+ }
683
+
674
684
  export interface RuntimeSchedulePort {
685
+ /** Required for chat creation/editing. Legacy management callers retain their CRUD contract. */
686
+ confirmation?: RuntimeScheduleConfirmationPort;
675
687
  /**
676
688
  * 创建一条定时任务并返回它的 ID。
677
689
  *
678
690
  * @remarks
679
- * `schedule` 工具在把用户的时间与提示词组成 `ScheduleSpec` 后调用。
691
+ * 保留给既有宿主管理调用者;聊天 `schedule` 只经 confirmation.commit 保存。
680
692
  *
681
693
  * Host 在该边界内绑定用户与会话,Runtime 不携带业务归属 ID。
682
694
  */
@@ -827,7 +839,7 @@ export interface RuntimeModelUsageEvent {
827
839
  * 结算事件刻意不带参数,因为它只用来记账;开始事件必须带,因为它唯一的用处
828
840
  * 就是让 Host 立刻知道「正在写的是什么」——只有工具名的话,「write 开始了」
829
841
  * 和「一个网页正在生成」之间的差别就丢了,而后者才是盯着屏幕的人要看的。
830
- * 参数本来就随 Tool 输入里程碑落库,这里没有多暴露任何东西。
842
+ * 参数本来就随 Pi Tool 调用写入 Session,这里没有多暴露任何东西。
831
843
  */
832
844
  export interface RuntimeToolStartEvent {
833
845
  readonly submissionId: string;
@@ -942,12 +954,6 @@ export type RuntimeModelProtocol =
942
954
  | "google-generative-ai"
943
955
  | "openai-codex-responses";
944
956
 
945
- export type RuntimeModelInputModality =
946
- | "text"
947
- | "image"
948
- | "file"
949
- | "audio"
950
- | "video";
951
957
 
952
958
  /**
953
959
  * 描述一个已解析凭据的模型 API 端点。
@@ -1024,6 +1030,16 @@ export interface RuntimePlatformPort {
1024
1030
  requiredExecutionLevel: ExecutionLevel;
1025
1031
  signal: AbortSignal;
1026
1032
  }): Promise<void>;
1033
+ /**
1034
+ * Match a pinned scheduled-task grant against the concrete Tool operation
1035
+ * and recheck the Host-owned live account authorization.
1036
+ *
1037
+ * This callback must fail closed by throwing a user-safe, specific reason.
1038
+ * It is called before dispatch and again at the effect boundary so a grant
1039
+ * revocation cannot be hidden by an earlier decision.
1040
+ */
1041
+ /** @deprecated Scheduled execution is authorized locally by execution level. */
1042
+ authorizeUnattendedExecution?(request: unknown): Promise<void>;
1027
1043
  }
1028
1044
 
1029
1045
  /**
@@ -1,5 +1,12 @@
1
1
  import type { ExecutionLevel } from "../lib/execution-level";
2
2
 
3
+ export type RuntimeModelInputModality =
4
+ | "text"
5
+ | "image"
6
+ | "file"
7
+ | "audio"
8
+ | "video";
9
+
3
10
  /**
4
11
  * 指定模型在一次 Turn 中可以使用的推理强度。
5
12
  *
@@ -11,13 +11,13 @@
11
11
  * @packageDocumentation
12
12
  */
13
13
  export * from "./profile";
14
- export * from "./runtime-assembly-view";
14
+ export * from "./assembly-view";
15
15
  export * from "./runtime-config";
16
- export * from "../pi/message";
17
- export * from "./receipts";
18
- export * from "./state";
16
+ export * from "../conversation/message";
17
+ export * from "../submission/contracts";
18
+ export * from "../conversation/state";
19
19
  export * from "../lib/execution-level";
20
- export type { WorkspaceRevision, WorkspaceRevisionManifest } from "../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 "./submission-authority";
30
+ } from "../tools/subagent/authority";
@@ -1,4 +1,4 @@
1
- import type { RuntimeLoadPhase, RuntimeLoadState } from "./state";
1
+ import type { RuntimeLoadPhase, RuntimeLoadState } from "../conversation/state";
2
2
 
3
3
  /** Hard ceiling for one Host-owned Runtime definition phase. */
4
4
  export const RUNTIME_LOAD_TIMEOUT_MS = 180_000;
@@ -1,212 +1,12 @@
1
- import { summarizeModelUsage } from "../../model-usage";
2
- import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/pi-agent-core/harness/context";
3
- import {
4
- buildSessionContext,
5
- compact as compactPiTranscript,
6
- DEFAULT_COMPACTION_SETTINGS,
7
- estimateContextTokens,
8
- estimateTokens,
9
- prepareCompaction,
10
- type AgentMessage,
11
- type CompactionEntry,
12
- type Entry,
13
- } from "@earendil-works/pi-agent-core";
14
- import type {
15
- AssistantMessage,
16
- Model,
17
- Models,
18
- RetryPolicy,
19
- Usage,
20
- } from "@earendil-works/pi-ai";
21
- import { estimateStringTokens } from "agents/experimental/memory/utils";
22
- import type { RuntimeMemoryPort } from "../../kernel/bindings";
23
- import type { RuntimeDegradation } from "../../kernel/degradation";
24
- import type { RuntimeMemoryProfile } from "../../kernel/profile";
25
- import type { PiExtensionContextContribution } from "./extensions";
26
-
27
- /**
28
- * 压缩这一次 LLM 调用的重试策略。
29
- *
30
- * 为什么必须显式传:pi 的 `retryAssistantCall` 是 opt-in 的 —— d.ts 原话
31
- * 「When `policy` is undefined or disabled, the first response is returned
32
- * unchanged」。升到 0.83 不传这个参数,行为与 0.80.6 完全一样(零重试)。
33
- *
34
- * 为什么压缩值得重试:压缩失败不是「少一次摘要」,是**下一轮直接撞 provider
35
- * 的上下文窗口上限、整个 turn 挂掉**。而它跑在 Workers 里,瞬时网络错误是常态。
36
- *
37
- * 为什么只有 2 次、退避从 500ms 起:压缩发生在 turn 的关键路径上,DO 的墙钟
38
- * 是真金白银(等 LLM 不占 CPU 配额但占用户的等待)。2 次退避总计约 1.5s,
39
- * 够穿过瞬时抖动,又不至于把一次失败拖成用户可感知的卡顿。
40
- */
41
- const COMPACTION_RETRY: RetryPolicy = {
42
- enabled: true,
43
- maxRetries: 2,
44
- baseDelayMs: 500,
45
- };
1
+ import { estimateStringTokens } from "../lib/token-estimate";
2
+ import type { RuntimeMemoryPort } from "../config/ports";
3
+ import type { RuntimeDegradation } from "../config/degradation";
4
+ import type { RuntimeMemoryProfile } from "../config/profile";
5
+ import type { PiExtensionContextContribution } from "./extension/assembly";
46
6
 
47
7
  const COLD_MEMORY_INDEX = "/userspace/memories/MEMORY.md";
48
8
  const TRUNCATION_MARKER = "\n[truncated to memory budget]";
49
9
 
50
- function compactionUsage(responses: readonly AssistantMessage[], requested: Model<any>) {
51
- const [first, ...remaining] = responses;
52
- return first ? { modelUsage: summarizeModelUsage(requested, [first, ...remaining]) } : {};
53
- }
54
-
55
- // #region Transcript compaction
56
-
57
- /**
58
- * 把当前 Pi 会话分支压缩成下一轮可以直接使用的消息。
59
- *
60
- * @remarks
61
- * Transcript 在每轮开始前传入当前分支、模型和 token 阈值,并在返回压缩条目后自行追加到 Session。
62
- *
63
- * 这里先用 Pi 的 Session 管线重建上下文,任何失败都退回未压缩消息,以避免压缩失败损坏持久化会话。
64
- *
65
- * 核心术语见包入口 `index.ts`。
66
- */
67
- export async function compactPiContext(input: Readonly<{
68
- branch: readonly Entry[];
69
- compactAfterTokens: number;
70
- force?: boolean;
71
- models: Models;
72
- model: Model<any>;
73
- signal?: AbortSignal;
74
- }>): Promise<{
75
- messages: AgentMessage[];
76
- compactionEntry?: Omit<
77
- CompactionEntry,
78
- "id" | "parentId" | "seq" | "timestamp"
79
- >;
80
- modelUsage?: {
81
- api: string;
82
- provider: string;
83
- model: string;
84
- responseModel?: string;
85
- stopReason?: string;
86
- usage: Usage;
87
- };
88
- didCompact: boolean;
89
- degraded: boolean;
90
- }> {
91
- const responses: AssistantMessage[] = [];
92
- const branch = [...input.branch];
93
- const context = input.signal ? withAbortSignal(input.signal, BACKGROUND_CONTEXT) : BACKGROUND_CONTEXT;
94
- let currentMessages = branch.flatMap((entry) =>
95
- entry.type === "message" ? [entry.message] : []
96
- );
97
- try {
98
- currentMessages = await buildSessionContext(branch, undefined, context);
99
- const settings = DEFAULT_COMPACTION_SETTINGS;
100
- if (
101
- !settings.enabled ||
102
- (!input.force && estimateContextTokens(currentMessages).tokens <
103
- input.compactAfterTokens)
104
- ) {
105
- return {
106
- messages: currentMessages,
107
- didCompact: false,
108
- degraded: false,
109
- };
110
- }
111
-
112
- const preparation = prepareCompaction(branch, settings);
113
- if (!preparation.ok) {
114
- return {
115
- messages: currentMessages,
116
- didCompact: false,
117
- degraded: true,
118
- ...compactionUsage(responses, input.model),
119
- };
120
- }
121
- if (!preparation.value) {
122
- return {
123
- messages: currentMessages,
124
- didCompact: false,
125
- degraded: false,
126
- };
127
- }
128
-
129
- const models = {
130
- completeSimple: async (
131
- model: Model<any>,
132
- context: Parameters<Models["completeSimple"]>[1],
133
- options?: Parameters<Models["completeSimple"]>[2],
134
- ) => {
135
- const response = await input.models.completeSimple(model, context, options);
136
- responses.push(response);
137
- input.signal?.throwIfAborted();
138
- return response;
139
- },
140
- } as Models;
141
- const compacted = await compactPiTranscript(
142
- preparation.value,
143
- models,
144
- input.model,
145
- undefined,
146
- undefined,
147
- COMPACTION_RETRY,
148
- undefined,
149
- context,
150
- );
151
- if (!compacted.ok) {
152
- return {
153
- messages: currentMessages,
154
- didCompact: false,
155
- degraded: true,
156
- ...compactionUsage(responses, input.model),
157
- };
158
- }
159
-
160
- const compactionEntry = {
161
- type: "compaction",
162
- fromHook: false,
163
- ...compacted.value,
164
- } satisfies Omit<
165
- CompactionEntry,
166
- "id" | "parentId" | "seq" | "timestamp"
167
- >;
168
- const previewEntry: CompactionEntry = {
169
- ...compactionEntry,
170
- id: `compaction-preview:${branch[branch.length - 1]?.id ?? "root"}`,
171
- parentId: branch[branch.length - 1]?.id ?? null,
172
- timestamp: Date.now(),
173
- seq: (branch.at(-1)?.seq ?? 0) + 1,
174
- };
175
- const messages = await buildSessionContext(
176
- [...branch, previewEntry], undefined, context,
177
- );
178
- if (
179
- input.force &&
180
- messages.reduce((total, message) => total + estimateTokens(message), 0) >=
181
- currentMessages.reduce(
182
- (total, message) => total + estimateTokens(message),
183
- 0,
184
- )
185
- ) {
186
- return {
187
- messages: currentMessages,
188
- didCompact: false,
189
- degraded: true,
190
- ...compactionUsage(responses, input.model),
191
- };
192
- }
193
- return {
194
- messages,
195
- compactionEntry,
196
- ...compactionUsage(responses, input.model),
197
- didCompact: true,
198
- degraded: false,
199
- };
200
- } catch {
201
- return {
202
- messages: currentMessages,
203
- didCompact: false,
204
- degraded: true,
205
- ...compactionUsage(responses, input.model),
206
- };
207
- }
208
- }
209
-
210
10
  // 作用:把一段上下文裁到指定 token 预算内。
211
11
  // 调用:内存和 Extension 上下文在拼入 system prompt 前调用。
212
12
  // 原因:二分搜索每次都用同一个 token 估算器验证,改成字符数截断会突破真实预算。
@@ -246,8 +46,6 @@ function renderBlock(
246
46
  return `${separator}\n${label} (${description})\n${separator}\n${content}`;
247
47
  }
248
48
 
249
- // #endregion
250
-
251
49
  // #region System context sources
252
50
 
253
51
  // 作用:同时收集当前 Session 内存和用户管理的冷内存索引。
@@ -8,12 +8,12 @@ import type {
8
8
  LoadedRuntimeExtension,
9
9
  RuntimeExtensionConfig,
10
10
  RuntimeExtensionPermissions,
11
- } from "../../kernel/extensions";
12
- import type { RuntimeDegradation } from "../../kernel/degradation";
13
- import { withRuntimeLoadTimeout } from "../../kernel/runtime-load";
11
+ } from "./contracts";
12
+ import type { RuntimeDegradation } from "../../config/degradation";
13
+ import { withRuntimeLoadTimeout } from "../../config/runtime-load";
14
14
  import { sanitizeExtensionName } from "../../lib/extension-name";
15
- import type { PiToolCandidate } from "../tool/compiler";
16
- import { createPiDeclaredToolCandidate } from "../tool/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;
@@ -0,0 +1,3 @@
1
+ export * from "./assembly";
2
+ export * from "./contracts";
3
+ export * from "./repository";
@@ -0,0 +1,6 @@
1
+ export * from "./assembly";
2
+ export * from "./extension/assembly";
3
+ export * from "./extension/contracts";
4
+ export * from "./extension/repository";
5
+ export * from "./memory/budget/gate";
6
+ export * from "../config/assembly-snapshot";
@@ -1,4 +1,5 @@
1
1
  import {
2
+ artifactId,
2
3
  type ArtifactRef,
3
4
  serializeOutput,
4
5
  sha256Hex,
@@ -29,7 +30,7 @@ import {
29
30
  /**
30
31
  * 持久化单个字符串叶子的上限。
31
32
  *
32
- * MUST ≥ 单页 read 的上限(见 `workspace-sandbox.ts` 的 `READ_PAGE_MAX_CHARS`),
33
+ * MUST ≥ 单页 read 的上限(见 `tools/workspace/index.ts` 的 `READ_PAGE_MAX_CHARS`),
33
34
  * 否则持久记录会比模型当轮看到的内容还少,会话恢复后出现凭空缺页。
34
35
  */
35
36
  export const STORAGE_LEAF_MAX_CHARS = 64 * 1024;
@@ -44,8 +45,8 @@ export const SPILL_THRESHOLD = 32 * 1024;
44
45
  /**
45
46
  * 历史里一条旧 Tool 结果重放多少。
46
47
  *
47
- * 这条最紧,因为它对每个 Turn 的**全部**历史消息生效(见
48
- * `pi/runtime-adapter/execution.ts` 的 `projectToolResultsForModel`):
48
+ * 这条最紧,因为 `projectToolOutputForModel` 会把它应用到每个 Turn
49
+ * 重放的全部历史 Tool 结果:
49
50
  * 放宽 1 KB 就是 N 条历史 × 1 KB。
50
51
  */
51
52
  export const MODEL_REPLAY_THRESHOLD = 16 * 1024;
@@ -161,11 +162,26 @@ export async function spillDurableToolOutput(
161
162
  text,
162
163
  ext === "json" ? "application/json" : "text/plain",
163
164
  );
165
+ if (!options.workspace.stat || !options.workspace.readFileBytes) {
166
+ throw new Error("Workspace artifact verification is unavailable");
167
+ }
168
+ const stat = await options.workspace.stat(path);
169
+ const expectedBytes = new TextEncoder().encode(text).byteLength;
170
+ if (!stat || stat.type !== "file" || stat.size !== expectedBytes) {
171
+ throw new Error("Workspace artifact failed size verification");
172
+ }
173
+ const stored = await options.workspace.readFileBytes(path);
174
+ if (!stored || new TextDecoder().decode(stored) !== text) {
175
+ throw new Error("Workspace artifact failed content verification");
176
+ }
164
177
  return {
165
178
  kind: "artifact_ref",
179
+ artifactId: artifactId(hash),
166
180
  path,
167
- bytes: new TextEncoder().encode(text).byteLength,
168
- hash: hash.slice(0, 16),
181
+ bytes: expectedBytes,
182
+ hash,
183
+ workspaceRevision: stat.contentVersion ?? `${stat.updatedAt}:${stat.size}`,
184
+ availability: "available",
169
185
  preview: text.slice(0, PREVIEW_CHARS),
170
186
  // 出口指令必须是可执行的:只说“去 read 这个路径”会诱导模型整读,而整读一个
171
187
  // 刚刚因为过大被外置的文件毫无意义。这里明确要求分页,并告知 read 会返回