@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.
- package/package.json +7 -7
- package/src/{agent-tool-runtime.ts → adapter/cloudflare/agent-tool.ts} +1 -1
- package/src/adapter/cloudflare/agent.ts +1682 -0
- package/src/{db/index.ts → adapter/cloudflare/database.ts} +18 -55
- package/src/{runtime-agent.ts → adapter/cloudflare/define-agent.ts} +37 -42
- package/src/adapter/cloudflare/dynamic-worker-observability.ts +1 -1
- package/src/adapter/cloudflare/index.ts +18 -0
- package/src/adapter/cloudflare/resources/runtime-resources.ts +4 -4
- package/src/adapter/cloudflare/sandbox/adapter.ts +1 -1
- package/src/adapter/cloudflare/scheduling/schedule-rule.ts +322 -0
- package/src/adapter/cloudflare/scheduling/zoned-cron.ts +11 -6
- package/src/{db → adapter/cloudflare}/schema.ts +28 -80
- package/src/adapter/cloudflare/subagent/definition.ts +63 -33
- package/src/adapter/cloudflare/subagent/runner.ts +174 -120
- package/src/adapter/cloudflare/subagent/tools.ts +13 -14
- package/src/adapter/cloudflare/universal-agent/preparation.ts +7 -7
- package/src/adapter/cloudflare/universal-agent/tools.ts +10 -12
- package/src/adapter/cloudflare/workspace/git-fs.ts +1 -1
- package/src/adapter/cloudflare/workspace/scoped-workspace.ts +1 -1
- package/src/adapter/cloudflare/workspace/version-control.ts +2 -2
- package/src/config/activation.ts +140 -0
- package/src/{runtime-agent-context.ts → config/agent-context.ts} +3 -3
- package/src/{runtime-assembler.ts → config/assembler.ts} +45 -46
- package/src/{kernel/runtime-assembly.ts → config/assembly-projection.ts} +4 -4
- package/src/{pi/assembly/snapshot.ts → config/assembly-snapshot.ts} +6 -6
- package/src/{runtime-definition.ts → config/definition.ts} +13 -13
- package/src/config/harness-runtime.ts +190 -0
- package/src/config/index.ts +14 -0
- package/src/config/model-guidance.ts +21 -0
- package/src/{pi/runtime-adapter/assembly.ts → config/pinning.ts} +60 -19
- package/src/{kernel/bindings.ts → config/ports.ts} +32 -16
- package/src/{kernel → config}/profile.ts +7 -0
- package/src/{kernel → config}/public-contracts.ts +6 -6
- package/src/{kernel → config}/runtime-load.ts +1 -1
- package/src/{pi/assembly/context.ts → context/assembly.ts} +5 -207
- package/src/{pi/assembly/extensions.ts → context/extension/assembly.ts} +5 -5
- package/src/context/extension/index.ts +3 -0
- package/src/context/index.ts +6 -0
- package/src/{layers/context → context/memory}/budget/gate.ts +21 -5
- package/src/conversation/harness-resume.ts +288 -0
- package/src/conversation/harness-stream.ts +99 -0
- package/src/{pi/runtime-adapter/transcript.ts → conversation/history.ts} +206 -233
- package/src/conversation/index.ts +6 -0
- package/src/{pi → conversation}/message/contract.ts +3 -0
- package/src/{pi → conversation}/message/conversion.ts +75 -0
- package/src/{pi → conversation}/message/index.ts +1 -0
- package/src/{pi → conversation}/message/model-input.ts +1 -1
- package/src/{pi → conversation}/message/projection.ts +113 -30
- package/src/{db/message-ui.repo.ts → conversation/repository.ts} +4 -0
- package/src/conversation/state.ts +196 -0
- package/src/harness/index.ts +7 -0
- package/src/harness/session-harness.ts +562 -0
- package/src/harness/submission-operation.ts +49 -0
- package/src/index.ts +38 -43
- package/src/lib/artifacts.ts +18 -0
- package/src/lib/execution-level.ts +13 -0
- package/src/lib/mcp.ts +1 -1
- package/src/lib/prompt.ts +7 -6
- package/src/lib/token-estimate.ts +8 -0
- package/src/{pi/runtime-adapter/models.ts → models/index.ts} +64 -17
- package/src/{pi/turn/tool-recovery.ts → session/cloudflare/legacy-tool-recovery.ts} +33 -525
- package/src/{pi/session → session/cloudflare}/migration.ts +404 -14
- package/src/{pi/session → session/cloudflare}/storage.ts +8 -36
- package/src/session/index.ts +39 -0
- package/src/skills/springbrand-worker-website/index.ts +1 -1
- package/src/submission/admission.ts +286 -0
- package/src/{kernel/receipts.ts → submission/contracts.ts} +56 -2
- package/src/submission/delivery.ts +212 -0
- package/src/submission/finalization.ts +111 -0
- package/src/submission/index.ts +9 -0
- package/src/{kernel/submission-lifecycle.ts → submission/lifecycle.ts} +41 -28
- package/src/{db/submission.repo.ts → submission/repository.ts} +117 -114
- package/src/submission/steer-delivery.ts +167 -0
- package/src/{db/steer.repo.ts → submission/steer-repository.ts} +27 -5
- package/src/submission/unattended.ts +40 -0
- package/src/telemetry/contract.ts +12 -0
- package/src/telemetry/coordinator.ts +65 -0
- package/src/telemetry/delivery.ts +2 -2
- package/src/telemetry/index.ts +5 -0
- package/src/{model-usage.ts → telemetry/model-usage.ts} +1 -1
- package/src/telemetry/recorder.ts +1 -1
- package/src/telemetry/runtime-telemetry.ts +12 -0
- package/src/{kernel/durable-lifecycle.ts → telemetry/subagent-lifecycle.ts} +1 -1
- package/src/telemetry/turn-events.ts +279 -0
- package/src/tools/README.md +44 -0
- package/src/{pi/tool/ai-adapter.ts → tools/adapters/ai-to-pi.ts} +6 -9
- package/src/{pi/tool → tools/adapters}/declared.ts +6 -2
- package/src/{pi/tool/nested-tools.ts → tools/adapters/pi-to-ai.ts} +2 -2
- package/src/tools/assistant/ask-user.ts +317 -0
- package/src/tools/assistant/candidate.ts +15 -0
- package/src/tools/assistant/followups.ts +34 -0
- package/src/tools/assistant/index.ts +19 -0
- package/src/tools/assistant/memory.ts +73 -0
- package/src/tools/assistant/plan.ts +98 -0
- package/src/{pi/tool → tools/assistant}/time.ts +1 -1
- package/src/{pi/tool/core-host.ts → tools/codemode/adapter.ts} +50 -49
- package/src/{pi/tool/core.ts → tools/codemode/index.ts} +35 -91
- package/src/tools/codemode/workspace-connector.ts +198 -0
- package/src/tools/extension/index.ts +42 -0
- package/src/tools/index.ts +32 -0
- package/src/{pi/tool → tools/mcp}/gateway.ts +19 -19
- package/src/{pi/tool/mcp.ts → tools/mcp/index.ts} +5 -5
- package/src/tools/runtime/approval/index.ts +1 -0
- package/src/{db/approval.repo.ts → tools/runtime/approval/repository.ts} +22 -41
- package/src/tools/runtime/compiler.ts +264 -0
- package/src/tools/runtime/contracts.ts +117 -0
- package/src/tools/runtime/decision-gate.ts +337 -0
- package/src/tools/runtime/human-decisions.ts +127 -0
- package/src/tools/runtime/interaction/index.ts +1 -0
- package/src/{db/interaction.repo.ts → tools/runtime/interaction/repository.ts} +1 -7
- package/src/tools/runtime/result.ts +22 -0
- package/src/tools/runtime/unattended-error.ts +4 -0
- package/src/tools/sandbox/index.ts +228 -0
- package/src/tools/schedule/index.ts +301 -0
- package/src/tools/skill/contracts.ts +20 -0
- package/src/tools/skill/index.ts +99 -0
- package/src/tools/skill/resources.ts +322 -0
- package/src/tools/skill/scripts.ts +168 -0
- package/src/{kernel/submission-authority.ts → tools/subagent/authority.ts} +31 -31
- package/src/{pi/tool/subagent.ts → tools/subagent/index.ts} +33 -36
- package/src/{db/agent-tool.repo.ts → tools/subagent/repository.ts} +1 -1
- package/src/{kernel/subagent-runtime.ts → tools/subagent/runtime.ts} +1 -1
- package/src/tools/subagent/temporary/approvals.ts +123 -0
- package/src/{layers/orchestration/temporary-agent/core.ts → tools/subagent/temporary/coordinator.ts} +41 -49
- package/src/tools/subagent/temporary/core.ts +53 -0
- package/src/{layers/orchestration/temporary-agent → tools/subagent/temporary}/runner.ts +1 -1
- package/src/{layers/orchestration/temporary-agent → tools/subagent/temporary}/workspace.ts +1 -4
- package/src/{pi/tool/web-fetch.ts → tools/web/fetch.ts} +4 -13
- package/src/tools/web/index.ts +2 -0
- package/src/tools/web/search/api.ts +1320 -0
- package/src/tools/web/search/index.ts +2 -0
- package/src/{pi/tool/web-search/web-search.ts → tools/web/search/search.ts} +5 -7
- package/src/{pi/tool/workspace-sandbox.ts → tools/workspace/index.ts} +12 -272
- package/src/{pi/tool/workspace-revision.ts → tools/workspace/revision.ts} +2 -2
- package/src/db/milestone.repo.ts +0 -104
- package/src/db/settlement.repo.ts +0 -104
- package/src/kernel/approval-lifecycle.ts +0 -638
- package/src/kernel/interaction-lifecycle.ts +0 -433
- package/src/kernel/recoverable-chat-agent.ts +0 -1020
- package/src/kernel/state.ts +0 -91
- package/src/pi/assembly/index.ts +0 -19
- package/src/pi/runtime-adapter/execution.ts +0 -1163
- package/src/pi/runtime-adapter/index.ts +0 -293
- package/src/pi/runtime-adapter/recovery.ts +0 -982
- package/src/pi/session/index.ts +0 -24
- package/src/pi/tool/base.ts +0 -550
- package/src/pi/tool/compiler.ts +0 -483
- package/src/pi/tool/index.ts +0 -38
- package/src/pi/tool/schedule.ts +0 -261
- package/src/pi/tool/skill.ts +0 -565
- package/src/pi/tool/web-search/api.ts +0 -1318
- package/src/pi/tool/web-search/index.ts +0 -2
- package/src/pi/tool/workspace-state-connector.ts +0 -146
- package/src/pi/turn/approval.ts +0 -181
- package/src/pi/turn/index.ts +0 -82
- package/src/pi/turn/interaction.ts +0 -181
- package/src/runtime.ts +0 -4760
- package/src/{kernel/runtime-assembly-view.ts → config/assembly-view.ts} +0 -0
- package/src/{kernel → config}/degradation.ts +0 -0
- package/src/{kernel → config}/runtime-config.ts +0 -0
- package/src/{kernel/extensions.ts → context/extension/contracts.ts} +0 -0
- package/src/{db/ext-context.repo.ts → context/extension/repository.ts} +0 -0
- package/src/{pi/runtime-adapter/openrouter-messages.ts → models/openrouter/messages.ts} +0 -0
- package/src/{pi/runtime-adapter → models/openrouter}/test-fixtures/openrouter-tool-search.sse +0 -0
- package/src/{db/submission-admission.repo.ts → submission/admission-repository.ts} +0 -0
- package/src/{db/telemetry-outbox.repo.ts → telemetry/repository.ts} +0 -0
- package/src/{db/runtime-event-outbox.repo.ts → telemetry/runtime-event-repository.ts} +0 -0
- package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/contract.ts +0 -0
- package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/extract/index.ts +0 -0
- package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/fanout/index.ts +0 -0
- package/src/{layers/orchestration/subagents/agent-types → tools/subagent/types}/registry.ts +1 -1
- /package/src/{workspace-versioning.ts → tools/workspace/versioning.ts} +0 -0
|
@@ -1,28 +1,45 @@
|
|
|
1
|
+
import type { ApprovalReceipt } from "../../submission/contracts";
|
|
1
2
|
import type { ToolResultMessage } from "@earendil-works/pi-ai";
|
|
2
3
|
import { isEqual } from "lodash-es";
|
|
3
4
|
import { EXECUTION_LEVELS } from "../../lib/execution-level";
|
|
4
|
-
import {
|
|
5
|
-
decidePiToolApproval,
|
|
6
|
-
type PiApprovalOutcome,
|
|
7
|
-
type PiToolApproval,
|
|
8
|
-
} from "./approval";
|
|
9
|
-
import {
|
|
10
|
-
cancelPiToolInteraction,
|
|
11
|
-
respondPiToolInteraction,
|
|
12
|
-
type PiToolInteraction,
|
|
13
|
-
type PiToolInteractionCancelReason,
|
|
14
|
-
type PiToolInteractionOutcome,
|
|
15
|
-
} from "./interaction";
|
|
16
|
-
|
|
17
5
|
/**
|
|
18
|
-
*
|
|
6
|
+
* 本文件只解码和重放旧 Pi Tool 恢复里程碑,供一次性 Session 迁移使用。
|
|
19
7
|
*
|
|
20
8
|
* @remarks
|
|
21
|
-
*
|
|
9
|
+
* 正常 AgentHarness 执行路径不导入本文件。
|
|
22
10
|
*/
|
|
23
11
|
|
|
24
12
|
// #region 持久恢复契约
|
|
25
13
|
|
|
14
|
+
interface PiToolApproval {
|
|
15
|
+
executionId: string;
|
|
16
|
+
requestId: string;
|
|
17
|
+
source: ApprovalReceipt["source"];
|
|
18
|
+
toolCallId: string;
|
|
19
|
+
toolName: string;
|
|
20
|
+
summary: string;
|
|
21
|
+
executionLevel: ApprovalReceipt["executionLevel"];
|
|
22
|
+
requiredExecutionLevel: ApprovalReceipt["requiredExecutionLevel"];
|
|
23
|
+
inputJson: string;
|
|
24
|
+
createdAt: number;
|
|
25
|
+
status: "pending" | "approved" | "rejected";
|
|
26
|
+
decidedAt?: number;
|
|
27
|
+
reason?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface PiToolInteraction {
|
|
31
|
+
interactionId: string;
|
|
32
|
+
requestId: string;
|
|
33
|
+
toolCallId: string;
|
|
34
|
+
toolName: string;
|
|
35
|
+
inputJson: string;
|
|
36
|
+
createdAt: number;
|
|
37
|
+
status: "pending" | "responded" | "cancelled";
|
|
38
|
+
respondedAt?: number;
|
|
39
|
+
responseJson?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
26
43
|
type RecoveryIdentity = {
|
|
27
44
|
version: 1;
|
|
28
45
|
turnId: string;
|
|
@@ -108,7 +125,7 @@ interface RecoveredTerminal {
|
|
|
108
125
|
/**
|
|
109
126
|
* 表示按顺序重放里程碑后得到的 Pi Tool 恢复状态。
|
|
110
127
|
*
|
|
111
|
-
*
|
|
128
|
+
* Session 迁移用权威 ToolResult 重建持久结算和 transcript。
|
|
112
129
|
*
|
|
113
130
|
* 这些集合是里程碑的派生视图,不应由调用方绕过重放器自行修改。
|
|
114
131
|
*/
|
|
@@ -125,92 +142,6 @@ export interface PiToolRecoveryState {
|
|
|
125
142
|
terminal?: RecoveredTerminal;
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
/**
|
|
129
|
-
* 描述恢复状态机建议 Runtime 执行的唯一下一步。
|
|
130
|
-
*
|
|
131
|
-
* 恢复适配器每次重放后消费一个计划,完成持久化或外部动作后再重放并重新规划。
|
|
132
|
-
*
|
|
133
|
-
* `chargeRecoveryBudget` 明确区分真正的 Tool 重试和等待、提交等可重复维护动作。
|
|
134
|
-
*/
|
|
135
|
-
export type PiToolRecoveryPlan =
|
|
136
|
-
| {
|
|
137
|
-
kind: "parked-approval";
|
|
138
|
-
executionId: string;
|
|
139
|
-
chargeRecoveryBudget: false;
|
|
140
|
-
}
|
|
141
|
-
| {
|
|
142
|
-
kind: "parked-interaction";
|
|
143
|
-
interactionId: string;
|
|
144
|
-
chargeRecoveryBudget: false;
|
|
145
|
-
}
|
|
146
|
-
| {
|
|
147
|
-
kind: "retry-tool";
|
|
148
|
-
toolCallId: string;
|
|
149
|
-
toolName: string;
|
|
150
|
-
input: unknown;
|
|
151
|
-
chargeRecoveryBudget: true;
|
|
152
|
-
}
|
|
153
|
-
| {
|
|
154
|
-
kind: "park-uncertain-tool";
|
|
155
|
-
toolCallId: string;
|
|
156
|
-
toolName: string;
|
|
157
|
-
chargeRecoveryBudget: true;
|
|
158
|
-
}
|
|
159
|
-
| {
|
|
160
|
-
kind: "persist-continuation";
|
|
161
|
-
turnId: string;
|
|
162
|
-
assemblyRevision: string;
|
|
163
|
-
continuationKey: string;
|
|
164
|
-
chargeRecoveryBudget: false;
|
|
165
|
-
}
|
|
166
|
-
| {
|
|
167
|
-
kind: "dispatch-continuation";
|
|
168
|
-
turnId: string;
|
|
169
|
-
assemblyRevision: string;
|
|
170
|
-
continuationKey: string;
|
|
171
|
-
idempotencyKey: string;
|
|
172
|
-
chargeRecoveryBudget: false;
|
|
173
|
-
}
|
|
174
|
-
| {
|
|
175
|
-
kind: "persist-terminal";
|
|
176
|
-
turnId: string;
|
|
177
|
-
assemblyRevision: string;
|
|
178
|
-
outcome: RecoveredTerminal["outcome"];
|
|
179
|
-
message: string | undefined;
|
|
180
|
-
chargeRecoveryBudget: false;
|
|
181
|
-
}
|
|
182
|
-
| {
|
|
183
|
-
kind: "complete";
|
|
184
|
-
outcome: RecoveredTerminal["outcome"];
|
|
185
|
-
chargeRecoveryBudget: false;
|
|
186
|
-
}
|
|
187
|
-
| { kind: "idle"; chargeRecoveryBudget: false };
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* 表示恢复路径应用审批决定后需要追加的里程碑和审批动作。
|
|
191
|
-
*
|
|
192
|
-
* Runtime 收到 approve 或 reject 命令时同时持久化 `milestones` 并处理 `outcome`。
|
|
193
|
-
*
|
|
194
|
-
* 把两者绑定返回可防止审批状态与 ToolResult 或续跑动作只完成一半。
|
|
195
|
-
*/
|
|
196
|
-
export interface RecoveredPiApprovalDecision {
|
|
197
|
-
milestones: PiToolRecoveryMilestone[];
|
|
198
|
-
outcome: PiApprovalOutcome;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* 表示恢复路径应用一次 interaction 响应或取消后需要追加的里程碑和动作。
|
|
203
|
-
*
|
|
204
|
-
* Runtime 收到 respond 或 cancel 命令时同时持久化 `milestones` 并处理 `outcome`。
|
|
205
|
-
*
|
|
206
|
-
* 与审批不同的是,settle 分支的 `tool-result` 里程碑带 `needsContinuation: true`:
|
|
207
|
-
* interaction 没有自己的续跑键,续跑完全依赖既有的 `tool:<id>:settled` 路径。
|
|
208
|
-
*/
|
|
209
|
-
export interface RecoveredPiToolInteractionSettlement {
|
|
210
|
-
milestones: PiToolRecoveryMilestone[];
|
|
211
|
-
outcome: PiToolInteractionOutcome;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
145
|
// #endregion
|
|
215
146
|
|
|
216
147
|
// #region 里程碑编码与校验
|
|
@@ -321,19 +252,6 @@ function isInteraction(value: unknown): value is PiToolInteraction {
|
|
|
321
252
|
);
|
|
322
253
|
}
|
|
323
254
|
|
|
324
|
-
/**
|
|
325
|
-
* 把一个 Pi Tool 恢复里程碑编码成可持久化的 JSON 正文。
|
|
326
|
-
*
|
|
327
|
-
* Runtime 恢复适配器在生成 `append-milestone` 变更时调用它。
|
|
328
|
-
*
|
|
329
|
-
* 编码保持为无包装的版本化 JSON;解码器在信任字段前会重新校验完整协议。
|
|
330
|
-
*/
|
|
331
|
-
export function encodePiToolRecoveryMilestone(
|
|
332
|
-
milestone: PiToolRecoveryMilestone,
|
|
333
|
-
): string {
|
|
334
|
-
return JSON.stringify(milestone);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
255
|
/**
|
|
338
256
|
* 把一条持久正文解码为经过校验的 Pi Tool 恢复里程碑。
|
|
339
257
|
*
|
|
@@ -633,413 +551,3 @@ export function replayPiToolRecovery(
|
|
|
633
551
|
}
|
|
634
552
|
|
|
635
553
|
// #endregion
|
|
636
|
-
|
|
637
|
-
// #region 恢复动作规划
|
|
638
|
-
|
|
639
|
-
// 把一个续跑键的持久阶段转换成下一步恢复计划。
|
|
640
|
-
// 主规划器在审批已决定或 ToolResult 需要继续原 Turn 时调用它。
|
|
641
|
-
// 未记录时先要求持久化 pending,已有 pending 才允许派发,committed 则不再产生动作。
|
|
642
|
-
function continuationPlan(
|
|
643
|
-
state: PiToolRecoveryState,
|
|
644
|
-
continuationKey: string,
|
|
645
|
-
): PiToolRecoveryPlan | null {
|
|
646
|
-
if (!state.turnId || !state.assemblyRevision) {
|
|
647
|
-
return null;
|
|
648
|
-
}
|
|
649
|
-
const phase = state.continuations[continuationKey];
|
|
650
|
-
if (phase === "committed") return null;
|
|
651
|
-
if (phase === "pending") {
|
|
652
|
-
return {
|
|
653
|
-
kind: "dispatch-continuation",
|
|
654
|
-
turnId: state.turnId,
|
|
655
|
-
assemblyRevision: state.assemblyRevision,
|
|
656
|
-
continuationKey,
|
|
657
|
-
idempotencyKey: `${state.turnId}:${continuationKey}`,
|
|
658
|
-
chargeRecoveryBudget: false,
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
return {
|
|
662
|
-
kind: "persist-continuation",
|
|
663
|
-
turnId: state.turnId,
|
|
664
|
-
assemblyRevision: state.assemblyRevision,
|
|
665
|
-
continuationKey,
|
|
666
|
-
chargeRecoveryBudget: false,
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/**
|
|
671
|
-
* 根据已重放状态选择 Pi Tool 恢复的唯一下一步。
|
|
672
|
-
*
|
|
673
|
-
* Runtime 恢复适配器在每次状态变化后调用它,并只执行返回计划对应的一个动作。
|
|
674
|
-
*
|
|
675
|
-
* 优先级固定为终态、已决定审批、待审批、待响应 interaction、已结算 Tool、未结算 Tool,
|
|
676
|
-
* 不能调换到越过终态或用户许可。
|
|
677
|
-
*/
|
|
678
|
-
export function planPiToolRecovery(
|
|
679
|
-
state: PiToolRecoveryState,
|
|
680
|
-
): PiToolRecoveryPlan {
|
|
681
|
-
// 终态优先于任何 Tool 或审批动作,避免已经结束的 Turn 被再次续跑。
|
|
682
|
-
if (state.terminal) {
|
|
683
|
-
if (state.terminal.committed) {
|
|
684
|
-
return {
|
|
685
|
-
kind: "complete",
|
|
686
|
-
outcome: state.terminal.outcome,
|
|
687
|
-
chargeRecoveryBudget: false,
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
if (!state.turnId || !state.assemblyRevision) {
|
|
691
|
-
return { kind: "idle", chargeRecoveryBudget: false };
|
|
692
|
-
}
|
|
693
|
-
return {
|
|
694
|
-
kind: "persist-terminal",
|
|
695
|
-
turnId: state.turnId,
|
|
696
|
-
assemblyRevision: state.assemblyRevision,
|
|
697
|
-
outcome: state.terminal.outcome,
|
|
698
|
-
message: state.terminal.message,
|
|
699
|
-
chargeRecoveryBudget: false,
|
|
700
|
-
};
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// 已决定审批先完成其续跑 outbox,确保决定持久化后只调度一次原 Turn。
|
|
704
|
-
for (const approval of Object.values(state.approvals)) {
|
|
705
|
-
if (approval.status === "pending") continue;
|
|
706
|
-
const key = `approval:${approval.executionId}:${
|
|
707
|
-
approval.status === "approved" ? "approve" : "reject"
|
|
708
|
-
}`;
|
|
709
|
-
const plan = continuationPlan(state, key);
|
|
710
|
-
if (plan) return plan;
|
|
711
|
-
}
|
|
712
|
-
// 仍待用户决定的审批保持 parked,不消耗普通恢复预算,也不执行其 Tool。
|
|
713
|
-
for (const approval of Object.values(state.approvals)) {
|
|
714
|
-
if (approval.status !== "pending") continue;
|
|
715
|
-
return {
|
|
716
|
-
kind: "parked-approval",
|
|
717
|
-
executionId: approval.executionId,
|
|
718
|
-
chargeRecoveryBudget: false,
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
// 仍等客户端投递结果的 interaction 同样保持 parked。
|
|
722
|
-
// 已响应或已取消的 interaction 不在这里出现:它们在写入本记录的同一批里程碑中
|
|
723
|
-
// 一并写了 tool-result,因此直接落到下面「已结算 Tool 补齐续跑」那条既有路径,
|
|
724
|
-
// 不需要 interaction 专属的续跑键。
|
|
725
|
-
for (const interaction of Object.values(state.interactions)) {
|
|
726
|
-
if (interaction.status !== "pending") continue;
|
|
727
|
-
return {
|
|
728
|
-
kind: "parked-interaction",
|
|
729
|
-
interactionId: interaction.interactionId,
|
|
730
|
-
chargeRecoveryBudget: false,
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// 已结算 Tool 先补齐续跑;未结算 Tool 只有声明幂等时才自动重试。
|
|
735
|
-
const tools = Object.values(state.toolCalls);
|
|
736
|
-
const codeModeParents = tools
|
|
737
|
-
.filter((tool) => tool.toolName === "execute")
|
|
738
|
-
.map((tool) => `${tool.toolCallId}:`);
|
|
739
|
-
for (const tool of tools) {
|
|
740
|
-
// 旧版把 Code Mode 内层调用写成独立 Pi attempt,但只有父
|
|
741
|
-
// execute 会产生 settlement。这些带父调用前缀的记录不是可
|
|
742
|
-
// 独立恢复的 Tool;父 execute 才是唯一恢复边界。
|
|
743
|
-
if (codeModeParents.some((prefix) => tool.toolCallId.startsWith(prefix))) {
|
|
744
|
-
continue;
|
|
745
|
-
}
|
|
746
|
-
if (tool.result) {
|
|
747
|
-
const key = `tool:${tool.toolCallId}:settled`;
|
|
748
|
-
const plan = tool.needsContinuation
|
|
749
|
-
? continuationPlan(state, key)
|
|
750
|
-
: null;
|
|
751
|
-
if (plan) return plan;
|
|
752
|
-
continue;
|
|
753
|
-
}
|
|
754
|
-
if (tool.retry === "idempotent") {
|
|
755
|
-
return {
|
|
756
|
-
kind: "retry-tool",
|
|
757
|
-
toolCallId: tool.toolCallId,
|
|
758
|
-
toolName: tool.toolName,
|
|
759
|
-
input: tool.input,
|
|
760
|
-
chargeRecoveryBudget: true,
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
if (tool.retry === "non-idempotent") {
|
|
764
|
-
return {
|
|
765
|
-
kind: "park-uncertain-tool",
|
|
766
|
-
toolCallId: tool.toolCallId,
|
|
767
|
-
toolName: tool.toolName,
|
|
768
|
-
chargeRecoveryBudget: true,
|
|
769
|
-
};
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
return { kind: "idle", chargeRecoveryBudget: false };
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
// #endregion
|
|
777
|
-
|
|
778
|
-
// #region 续跑两阶段提交
|
|
779
|
-
|
|
780
|
-
/**
|
|
781
|
-
* 为一次尚未登记的续跑创建 `pending` 里程碑。
|
|
782
|
-
*
|
|
783
|
-
* Runtime 处理 `persist-continuation` 计划时先调用它,持久化成功后才能重新规划为派发动作。
|
|
784
|
-
*
|
|
785
|
-
* 状态和 Turn revision 必须仍与计划一致;过期或重复计划返回 null,避免写出第二份 outbox 意图。
|
|
786
|
-
*/
|
|
787
|
-
export function stagePiRecoveryContinuation(
|
|
788
|
-
state: PiToolRecoveryState,
|
|
789
|
-
plan: Extract<
|
|
790
|
-
PiToolRecoveryPlan,
|
|
791
|
-
{ kind: "persist-continuation" }
|
|
792
|
-
>,
|
|
793
|
-
timestamp: number,
|
|
794
|
-
): PiToolRecoveryMilestone | null {
|
|
795
|
-
if (
|
|
796
|
-
state.continuations[plan.continuationKey] !== undefined ||
|
|
797
|
-
state.turnId !== plan.turnId ||
|
|
798
|
-
state.assemblyRevision !== plan.assemblyRevision
|
|
799
|
-
) {
|
|
800
|
-
return null;
|
|
801
|
-
}
|
|
802
|
-
return {
|
|
803
|
-
version: 1,
|
|
804
|
-
type: "continuation",
|
|
805
|
-
turnId: plan.turnId,
|
|
806
|
-
assemblyRevision: plan.assemblyRevision,
|
|
807
|
-
continuationKey: plan.continuationKey,
|
|
808
|
-
phase: "pending",
|
|
809
|
-
timestamp,
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
/**
|
|
814
|
-
* 为一次已经登记的续跑创建 `committed` 里程碑。
|
|
815
|
-
*
|
|
816
|
-
* Runtime 确认 `dispatch-continuation` 计划仍有效时调用它,随后把提交记录与对应恢复动作按宿主顺序处理。
|
|
817
|
-
*
|
|
818
|
-
* 只允许 `pending` 且 revision 匹配的计划提交,使重复 wake 或重复调度不会重新打开同一续跑。
|
|
819
|
-
*/
|
|
820
|
-
export function commitPiRecoveryContinuation(
|
|
821
|
-
state: PiToolRecoveryState,
|
|
822
|
-
plan: Extract<
|
|
823
|
-
PiToolRecoveryPlan,
|
|
824
|
-
{ kind: "dispatch-continuation" }
|
|
825
|
-
>,
|
|
826
|
-
timestamp: number,
|
|
827
|
-
): PiToolRecoveryMilestone | null {
|
|
828
|
-
if (
|
|
829
|
-
state.continuations[plan.continuationKey] !== "pending" ||
|
|
830
|
-
state.turnId !== plan.turnId ||
|
|
831
|
-
state.assemblyRevision !== plan.assemblyRevision
|
|
832
|
-
) {
|
|
833
|
-
return null;
|
|
834
|
-
}
|
|
835
|
-
return {
|
|
836
|
-
version: 1,
|
|
837
|
-
type: "continuation",
|
|
838
|
-
turnId: plan.turnId,
|
|
839
|
-
assemblyRevision: plan.assemblyRevision,
|
|
840
|
-
continuationKey: plan.continuationKey,
|
|
841
|
-
phase: "committed",
|
|
842
|
-
timestamp,
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// #endregion
|
|
847
|
-
|
|
848
|
-
// #region 装配迁移
|
|
849
|
-
|
|
850
|
-
/**
|
|
851
|
-
* 为一次停在人机等待期间发生的装配迁移创建里程碑。
|
|
852
|
-
*
|
|
853
|
-
* 宿主换完装配、准备把 Submission 重新 pin 到新装配上时调用它;返回的里程碑
|
|
854
|
-
* 必须与 `pinAssembly` 的写入落在同一个事务里,否则重启后里程碑与 Submission
|
|
855
|
-
* 会各自指向一半的迁移。
|
|
856
|
-
*
|
|
857
|
-
* 已经处在目标 revision(重复调用)或还没有任何里程碑(没有身份要迁移)时返回
|
|
858
|
-
* null:这两种情况下重放本来就得到正确的身份,多写一条只会造出假历史。
|
|
859
|
-
*/
|
|
860
|
-
export function stagePiAssemblyRepin(
|
|
861
|
-
state: PiToolRecoveryState,
|
|
862
|
-
nextAssemblyRevision: string,
|
|
863
|
-
timestamp: number,
|
|
864
|
-
): PiToolRecoveryMilestone | null {
|
|
865
|
-
if (
|
|
866
|
-
!state.turnId ||
|
|
867
|
-
!state.assemblyRevision ||
|
|
868
|
-
state.assemblyRevision === nextAssemblyRevision
|
|
869
|
-
) {
|
|
870
|
-
return null;
|
|
871
|
-
}
|
|
872
|
-
return {
|
|
873
|
-
version: 1,
|
|
874
|
-
turnId: state.turnId,
|
|
875
|
-
assemblyRevision: state.assemblyRevision,
|
|
876
|
-
type: "assembly-repin",
|
|
877
|
-
nextAssemblyRevision,
|
|
878
|
-
timestamp,
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
// #endregion
|
|
883
|
-
|
|
884
|
-
// #region 恢复审批决定
|
|
885
|
-
|
|
886
|
-
/**
|
|
887
|
-
* 在恢复状态上应用一个用户审批决定,并生成需要持久化的里程碑。
|
|
888
|
-
*
|
|
889
|
-
* Runtime 收到冷重建后的 approve 或 reject 命令时调用它,再按返回结果更新审批表和恢复日志。
|
|
890
|
-
*
|
|
891
|
-
* approve 只记录决定并安排续跑;reject 额外写入稳定 ToolResult,且重复决定返回空里程碑。
|
|
892
|
-
*/
|
|
893
|
-
export function applyRecoveredPiApprovalDecision(
|
|
894
|
-
state: PiToolRecoveryState,
|
|
895
|
-
input: {
|
|
896
|
-
executionId: string;
|
|
897
|
-
decision: "approve" | "reject";
|
|
898
|
-
decidedAt: number;
|
|
899
|
-
reason?: string;
|
|
900
|
-
},
|
|
901
|
-
): RecoveredPiApprovalDecision {
|
|
902
|
-
const approval = state.approvals[input.executionId];
|
|
903
|
-
if (!approval) {
|
|
904
|
-
throw new Error(`Unknown SpringBrand Tool approval ${input.executionId}`);
|
|
905
|
-
}
|
|
906
|
-
const transition = decidePiToolApproval(
|
|
907
|
-
approval,
|
|
908
|
-
input.decision === "approve"
|
|
909
|
-
? { decision: "allow_once", decidedAt: input.decidedAt }
|
|
910
|
-
: {
|
|
911
|
-
decision: "deny",
|
|
912
|
-
decidedAt: input.decidedAt,
|
|
913
|
-
reason: input.reason,
|
|
914
|
-
},
|
|
915
|
-
);
|
|
916
|
-
// decidePiToolApproval 已处理的审批只返回 noop;这里不应再追加任何持久记录。
|
|
917
|
-
if (transition.outcome.kind === "noop") {
|
|
918
|
-
return { milestones: [], outcome: transition.outcome };
|
|
919
|
-
}
|
|
920
|
-
if (!state.turnId || !state.assemblyRevision) {
|
|
921
|
-
throw new Error("SpringBrand Tool approval is missing its Turn revision");
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
const identity: RecoveryIdentity = {
|
|
925
|
-
version: 1,
|
|
926
|
-
turnId: state.turnId,
|
|
927
|
-
assemblyRevision: state.assemblyRevision,
|
|
928
|
-
};
|
|
929
|
-
const milestones: PiToolRecoveryMilestone[] = [
|
|
930
|
-
{
|
|
931
|
-
...identity,
|
|
932
|
-
type: "approval",
|
|
933
|
-
approval: transition.approval,
|
|
934
|
-
},
|
|
935
|
-
];
|
|
936
|
-
if (transition.outcome.kind === "denied") {
|
|
937
|
-
// 拒绝直接结算成 ToolResult,且不再安排 Tool settled continuation。
|
|
938
|
-
// 审批决定本身仍有独立 continuation,保持 approve/reject 的恢复调度一致。
|
|
939
|
-
milestones.push({
|
|
940
|
-
...identity,
|
|
941
|
-
type: "tool-result",
|
|
942
|
-
toolResult: transition.outcome.toolResult,
|
|
943
|
-
needsContinuation: false,
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
return { milestones, outcome: transition.outcome };
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
// #endregion
|
|
950
|
-
|
|
951
|
-
// #region 恢复 interaction 结算
|
|
952
|
-
|
|
953
|
-
/**
|
|
954
|
-
* 在恢复状态上记录一条新 park 的 interaction。
|
|
955
|
-
*
|
|
956
|
-
* 执行链进入 park 前调用,返回的里程碑让重启后的重放知道这次 Tool 调用在等客户端,
|
|
957
|
-
* 从而规划成 `parked-interaction` 而不是去重试 Tool。
|
|
958
|
-
*/
|
|
959
|
-
export function recordRecoveredPiToolInteraction(
|
|
960
|
-
state: PiToolRecoveryState,
|
|
961
|
-
interaction: PiToolInteraction,
|
|
962
|
-
): PiToolRecoveryMilestone[] {
|
|
963
|
-
if (!state.turnId || !state.assemblyRevision) {
|
|
964
|
-
throw new Error("SpringBrand Tool interaction is missing its Turn revision");
|
|
965
|
-
}
|
|
966
|
-
return [{
|
|
967
|
-
version: 1,
|
|
968
|
-
turnId: state.turnId,
|
|
969
|
-
assemblyRevision: state.assemblyRevision,
|
|
970
|
-
type: "interaction",
|
|
971
|
-
interaction,
|
|
972
|
-
}];
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
/**
|
|
976
|
-
* 在恢复状态上应用一次客户端响应或取消,并生成需要持久化的里程碑。
|
|
977
|
-
*
|
|
978
|
-
* Runtime 收到 `respondToolInteraction` 或判定取消时调用它,再按返回结果更新表和恢复日志。
|
|
979
|
-
*
|
|
980
|
-
* 两种结局形状相同 —— 都写 interaction 记录 + 一条权威 `tool-result`。重复投递返回空里程碑。
|
|
981
|
-
*/
|
|
982
|
-
export function applyRecoveredPiToolInteractionSettlement(
|
|
983
|
-
state: PiToolRecoveryState,
|
|
984
|
-
input:
|
|
985
|
-
| {
|
|
986
|
-
interactionId: string;
|
|
987
|
-
kind: "respond";
|
|
988
|
-
response: unknown;
|
|
989
|
-
toolResult: ToolResultMessage;
|
|
990
|
-
settledAt: number;
|
|
991
|
-
}
|
|
992
|
-
| {
|
|
993
|
-
interactionId: string;
|
|
994
|
-
kind: "cancel";
|
|
995
|
-
reason: PiToolInteractionCancelReason;
|
|
996
|
-
settledAt: number;
|
|
997
|
-
toolResult?: ToolResultMessage;
|
|
998
|
-
},
|
|
999
|
-
): RecoveredPiToolInteractionSettlement {
|
|
1000
|
-
const interaction = state.interactions[input.interactionId];
|
|
1001
|
-
if (!interaction) {
|
|
1002
|
-
throw new Error(`Unknown SpringBrand Tool interaction ${input.interactionId}`);
|
|
1003
|
-
}
|
|
1004
|
-
const transition = input.kind === "respond"
|
|
1005
|
-
? respondPiToolInteraction(interaction, {
|
|
1006
|
-
response: input.response,
|
|
1007
|
-
toolResult: input.toolResult,
|
|
1008
|
-
respondedAt: input.settledAt,
|
|
1009
|
-
})
|
|
1010
|
-
: cancelPiToolInteraction(interaction, {
|
|
1011
|
-
reason: input.reason,
|
|
1012
|
-
cancelledAt: input.settledAt,
|
|
1013
|
-
...(input.toolResult ? { toolResult: input.toolResult } : {}),
|
|
1014
|
-
});
|
|
1015
|
-
const outcome = transition.outcome;
|
|
1016
|
-
// 已结束的 interaction 只返回 noop;这里不应再追加任何持久记录。
|
|
1017
|
-
if (outcome.kind !== "settle") {
|
|
1018
|
-
return { milestones: [], outcome };
|
|
1019
|
-
}
|
|
1020
|
-
if (!state.turnId || !state.assemblyRevision) {
|
|
1021
|
-
throw new Error("SpringBrand Tool interaction is missing its Turn revision");
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
const identity: RecoveryIdentity = {
|
|
1025
|
-
version: 1,
|
|
1026
|
-
turnId: state.turnId,
|
|
1027
|
-
assemblyRevision: state.assemblyRevision,
|
|
1028
|
-
};
|
|
1029
|
-
return {
|
|
1030
|
-
milestones: [
|
|
1031
|
-
{ ...identity, type: "interaction", interaction: transition.interaction },
|
|
1032
|
-
// needsContinuation 必须为 true:interaction 没有自己的续跑键,
|
|
1033
|
-
// 唤醒原 Turn 全靠 planPiToolRecovery 里 `tool:<id>:settled` 那条既有分支。
|
|
1034
|
-
{
|
|
1035
|
-
...identity,
|
|
1036
|
-
type: "tool-result",
|
|
1037
|
-
toolResult: outcome.toolResult,
|
|
1038
|
-
needsContinuation: true,
|
|
1039
|
-
},
|
|
1040
|
-
],
|
|
1041
|
-
outcome,
|
|
1042
|
-
};
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
// #endregion
|