open-multi-agent-kit 0.98.4 → 0.98.5
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/CHANGELOG.md +12 -0
- package/README.md +3 -2
- package/dist/commands/run-command.d.ts +2 -1
- package/dist/commands/run-command.d.ts.map +1 -1
- package/dist/commands/run-command.js +9 -1
- package/dist/commands/run-command.js.map +1 -1
- package/dist/commands/verified-run-cli.d.ts +6 -0
- package/dist/commands/verified-run-cli.d.ts.map +1 -0
- package/dist/commands/verified-run-cli.js +190 -0
- package/dist/commands/verified-run-cli.js.map +1 -0
- package/dist/core/agent-session-services.d.ts +8 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +41 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -8
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +61 -41
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/prompt-settlement.d.ts +4 -3
- package/dist/core/prompt-settlement.d.ts.map +1 -1
- package/dist/core/prompt-settlement.js.map +1 -1
- package/dist/core/run-budget-policy.d.ts +18 -0
- package/dist/core/run-budget-policy.d.ts.map +1 -0
- package/dist/core/run-budget-policy.js +52 -0
- package/dist/core/run-budget-policy.js.map +1 -0
- package/dist/core/run-budget.d.ts +33 -0
- package/dist/core/run-budget.d.ts.map +1 -0
- package/dist/core/run-budget.js +89 -0
- package/dist/core/run-budget.js.map +1 -0
- package/dist/core/run-execution-api.d.ts +15 -0
- package/dist/core/run-execution-api.d.ts.map +1 -0
- package/dist/core/run-execution-api.js +7 -0
- package/dist/core/run-execution-api.js.map +1 -0
- package/dist/core/run-journal.d.ts.map +1 -1
- package/dist/core/run-journal.js +30 -208
- package/dist/core/run-journal.js.map +1 -1
- package/dist/core/session-bash-service.d.ts +2 -2
- package/dist/core/session-bash-service.d.ts.map +1 -1
- package/dist/core/session-bash-service.js +21 -9
- package/dist/core/session-bash-service.js.map +1 -1
- package/dist/core/session-failure-cause.d.ts.map +1 -1
- package/dist/core/session-failure-cause.js +5 -0
- package/dist/core/session-failure-cause.js.map +1 -1
- package/dist/core/session-prompt-lifecycle.d.ts +25 -0
- package/dist/core/session-prompt-lifecycle.d.ts.map +1 -0
- package/dist/core/session-prompt-lifecycle.js +87 -0
- package/dist/core/session-prompt-lifecycle.js.map +1 -0
- package/dist/core/session-run-budget.d.ts +28 -0
- package/dist/core/session-run-budget.d.ts.map +1 -0
- package/dist/core/session-run-budget.js +127 -0
- package/dist/core/session-run-budget.js.map +1 -0
- package/dist/core/session-run-termination.d.ts.map +1 -1
- package/dist/core/session-run-termination.js +14 -3
- package/dist/core/session-run-termination.js.map +1 -1
- package/dist/core/session-termination-types.d.ts +97 -0
- package/dist/core/session-termination-types.d.ts.map +1 -0
- package/dist/core/session-termination-types.js +26 -0
- package/dist/core/session-termination-types.js.map +1 -0
- package/dist/core/session-termination.d.ts +3 -97
- package/dist/core/session-termination.d.ts.map +1 -1
- package/dist/core/session-termination.js +24 -28
- package/dist/core/session-termination.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/subagent-lane-launcher.d.ts +3 -2
- package/dist/core/subagent-lane-launcher.d.ts.map +1 -1
- package/dist/core/subagent-lane-launcher.js +24 -13
- package/dist/core/subagent-lane-launcher.js.map +1 -1
- package/dist/core/verified-run/broker.d.ts +26 -0
- package/dist/core/verified-run/broker.d.ts.map +1 -0
- package/dist/core/verified-run/broker.js +210 -0
- package/dist/core/verified-run/broker.js.map +1 -0
- package/dist/core/verified-run/candidate.d.ts +24 -0
- package/dist/core/verified-run/candidate.d.ts.map +1 -0
- package/dist/core/verified-run/candidate.js +167 -0
- package/dist/core/verified-run/candidate.js.map +1 -0
- package/dist/core/verified-run/check-receipt.d.ts +18 -0
- package/dist/core/verified-run/check-receipt.d.ts.map +1 -0
- package/dist/core/verified-run/check-receipt.js +92 -0
- package/dist/core/verified-run/check-receipt.js.map +1 -0
- package/dist/core/verified-run/coordinator.d.ts +39 -0
- package/dist/core/verified-run/coordinator.d.ts.map +1 -0
- package/dist/core/verified-run/coordinator.js +190 -0
- package/dist/core/verified-run/coordinator.js.map +1 -0
- package/dist/core/verified-run/dag-candidates.d.ts +8 -0
- package/dist/core/verified-run/dag-candidates.d.ts.map +1 -0
- package/dist/core/verified-run/dag-candidates.js +65 -0
- package/dist/core/verified-run/dag-candidates.js.map +1 -0
- package/dist/core/verified-run/dag-phase.d.ts +7 -0
- package/dist/core/verified-run/dag-phase.d.ts.map +1 -0
- package/dist/core/verified-run/dag-phase.js +92 -0
- package/dist/core/verified-run/dag-phase.js.map +1 -0
- package/dist/core/verified-run/dag-projection.d.ts +7 -0
- package/dist/core/verified-run/dag-projection.d.ts.map +1 -0
- package/dist/core/verified-run/dag-projection.js +90 -0
- package/dist/core/verified-run/dag-projection.js.map +1 -0
- package/dist/core/verified-run/dag-recovery.d.ts +13 -0
- package/dist/core/verified-run/dag-recovery.d.ts.map +1 -0
- package/dist/core/verified-run/dag-recovery.js +98 -0
- package/dist/core/verified-run/dag-recovery.js.map +1 -0
- package/dist/core/verified-run/dag-retry-projection.d.ts +9 -0
- package/dist/core/verified-run/dag-retry-projection.d.ts.map +1 -0
- package/dist/core/verified-run/dag-retry-projection.js +43 -0
- package/dist/core/verified-run/dag-retry-projection.js.map +1 -0
- package/dist/core/verified-run/dag-types.d.ts +47 -0
- package/dist/core/verified-run/dag-types.d.ts.map +1 -0
- package/dist/core/verified-run/dag-types.js +2 -0
- package/dist/core/verified-run/dag-types.js.map +1 -0
- package/dist/core/verified-run/event-parser.d.ts +3 -0
- package/dist/core/verified-run/event-parser.d.ts.map +1 -0
- package/dist/core/verified-run/event-parser.js +153 -0
- package/dist/core/verified-run/event-parser.js.map +1 -0
- package/dist/core/verified-run/events.d.ts +4 -0
- package/dist/core/verified-run/events.d.ts.map +1 -0
- package/dist/core/verified-run/events.js +3 -0
- package/dist/core/verified-run/events.js.map +1 -0
- package/dist/core/verified-run/evidence-binding.d.ts +18 -0
- package/dist/core/verified-run/evidence-binding.d.ts.map +1 -0
- package/dist/core/verified-run/evidence-binding.js +81 -0
- package/dist/core/verified-run/evidence-binding.js.map +1 -0
- package/dist/core/verified-run/evidence.d.ts +27 -0
- package/dist/core/verified-run/evidence.d.ts.map +1 -0
- package/dist/core/verified-run/evidence.js +100 -0
- package/dist/core/verified-run/evidence.js.map +1 -0
- package/dist/core/verified-run/journal.d.ts +32 -0
- package/dist/core/verified-run/journal.d.ts.map +1 -0
- package/dist/core/verified-run/journal.js +103 -0
- package/dist/core/verified-run/journal.js.map +1 -0
- package/dist/core/verified-run/namespace-identity.d.ts +11 -0
- package/dist/core/verified-run/namespace-identity.d.ts.map +1 -0
- package/dist/core/verified-run/namespace-identity.js +77 -0
- package/dist/core/verified-run/namespace-identity.js.map +1 -0
- package/dist/core/verified-run/owned-execution.d.ts +21 -0
- package/dist/core/verified-run/owned-execution.d.ts.map +1 -0
- package/dist/core/verified-run/owned-execution.js +43 -0
- package/dist/core/verified-run/owned-execution.js.map +1 -0
- package/dist/core/verified-run/phase-context.d.ts +9 -0
- package/dist/core/verified-run/phase-context.d.ts.map +1 -0
- package/dist/core/verified-run/phase-context.js +2 -0
- package/dist/core/verified-run/phase-context.js.map +1 -0
- package/dist/core/verified-run/process-gate.d.ts +5 -0
- package/dist/core/verified-run/process-gate.d.ts.map +1 -0
- package/dist/core/verified-run/process-gate.js +31 -0
- package/dist/core/verified-run/process-gate.js.map +1 -0
- package/dist/core/verified-run/projection.d.ts +4 -0
- package/dist/core/verified-run/projection.d.ts.map +1 -0
- package/dist/core/verified-run/projection.js +219 -0
- package/dist/core/verified-run/projection.js.map +1 -0
- package/dist/core/verified-run/recovery-clock.d.ts +19 -0
- package/dist/core/verified-run/recovery-clock.d.ts.map +1 -0
- package/dist/core/verified-run/recovery-clock.js +69 -0
- package/dist/core/verified-run/recovery-clock.js.map +1 -0
- package/dist/core/verified-run/recovery-command.d.ts +11 -0
- package/dist/core/verified-run/recovery-command.d.ts.map +1 -0
- package/dist/core/verified-run/recovery-command.js +73 -0
- package/dist/core/verified-run/recovery-command.js.map +1 -0
- package/dist/core/verified-run/recovery-projection.d.ts +12 -0
- package/dist/core/verified-run/recovery-projection.d.ts.map +1 -0
- package/dist/core/verified-run/recovery-projection.js +88 -0
- package/dist/core/verified-run/recovery-projection.js.map +1 -0
- package/dist/core/verified-run/recovery.d.ts +12 -0
- package/dist/core/verified-run/recovery.d.ts.map +1 -0
- package/dist/core/verified-run/recovery.js +115 -0
- package/dist/core/verified-run/recovery.js.map +1 -0
- package/dist/core/verified-run/run-types.d.ts +103 -0
- package/dist/core/verified-run/run-types.d.ts.map +1 -0
- package/dist/core/verified-run/run-types.js +2 -0
- package/dist/core/verified-run/run-types.js.map +1 -0
- package/dist/core/verified-run/scripted-writer.d.ts +15 -0
- package/dist/core/verified-run/scripted-writer.d.ts.map +1 -0
- package/dist/core/verified-run/scripted-writer.js +94 -0
- package/dist/core/verified-run/scripted-writer.js.map +1 -0
- package/dist/core/verified-run/session-port.d.ts +27 -0
- package/dist/core/verified-run/session-port.d.ts.map +1 -0
- package/dist/core/verified-run/session-port.js +2 -0
- package/dist/core/verified-run/session-port.js.map +1 -0
- package/dist/core/verified-run/storage.d.ts +15 -0
- package/dist/core/verified-run/storage.d.ts.map +1 -0
- package/dist/core/verified-run/storage.js +96 -0
- package/dist/core/verified-run/storage.js.map +1 -0
- package/dist/core/verified-run/verification-phase.d.ts +5 -0
- package/dist/core/verified-run/verification-phase.d.ts.map +1 -0
- package/dist/core/verified-run/verification-phase.js +56 -0
- package/dist/core/verified-run/verification-phase.js.map +1 -0
- package/dist/core/verified-run/work-recovery.d.ts +8 -0
- package/dist/core/verified-run/work-recovery.d.ts.map +1 -0
- package/dist/core/verified-run/work-recovery.js +44 -0
- package/dist/core/verified-run/work-recovery.js.map +1 -0
- package/dist/core/verified-run/writer-completion.d.ts +4 -0
- package/dist/core/verified-run/writer-completion.d.ts.map +1 -0
- package/dist/core/verified-run/writer-completion.js +27 -0
- package/dist/core/verified-run/writer-completion.js.map +1 -0
- package/dist/core/verified-run/writer-phase.d.ts +8 -0
- package/dist/core/verified-run/writer-phase.d.ts.map +1 -0
- package/dist/core/verified-run/writer-phase.js +76 -0
- package/dist/core/verified-run/writer-phase.js.map +1 -0
- package/dist/core/verified-run/writer-projection.d.ts +11 -0
- package/dist/core/verified-run/writer-projection.d.ts.map +1 -0
- package/dist/core/verified-run/writer-projection.js +70 -0
- package/dist/core/verified-run/writer-projection.js.map +1 -0
- package/dist/core/verified-run/writer-recovery.d.ts +16 -0
- package/dist/core/verified-run/writer-recovery.d.ts.map +1 -0
- package/dist/core/verified-run/writer-recovery.js +107 -0
- package/dist/core/verified-run/writer-recovery.js.map +1 -0
- package/dist/core/workload-permit-pool.d.ts +1 -3
- package/dist/core/workload-permit-pool.d.ts.map +1 -1
- package/dist/core/workload-permit-pool.js +11 -11
- package/dist/core/workload-permit-pool.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -8
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/session-failure.d.ts +13 -0
- package/dist/modes/interactive/components/session-failure.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-failure.js +81 -0
- package/dist/modes/interactive/components/session-failure.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +45 -31
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tui-diagnostics.d.ts +36 -0
- package/dist/modes/interactive/tui-diagnostics.d.ts.map +1 -0
- package/dist/modes/interactive/tui-diagnostics.js +105 -0
- package/dist/modes/interactive/tui-diagnostics.js.map +1 -0
- package/dist/modes/interactive/tui-runtime-info.d.ts +20 -0
- package/dist/modes/interactive/tui-runtime-info.d.ts.map +1 -0
- package/dist/modes/interactive/tui-runtime-info.js +76 -0
- package/dist/modes/interactive/tui-runtime-info.js.map +1 -0
- package/docs/development.md +10 -0
- package/docs/keybindings.md +1 -1
- package/docs/release-audit-0.98.5.md +84 -0
- package/docs/run-protocol.md +33 -1
- package/docs/runtime-algorithms.md +26 -0
- package/docs/sdk.md +193 -0
- package/docs/usage.md +54 -0
- package/docs/verified-run-testing.md +417 -0
- package/docs/verified-run.md +388 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +18 -18
- package/package.json +6 -6
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type SandboxOutcome } from "./broker.ts";
|
|
2
|
+
import type { VerifiedRunJournal } from "./journal.ts";
|
|
3
|
+
export interface OwnedRunCommand {
|
|
4
|
+
readonly role: "writer" | "verifier";
|
|
5
|
+
readonly argv: readonly string[];
|
|
6
|
+
readonly workspace: string;
|
|
7
|
+
readonly deadline: number;
|
|
8
|
+
readonly claimId: string | null;
|
|
9
|
+
}
|
|
10
|
+
export interface OwnedRunResult {
|
|
11
|
+
readonly executionId: string;
|
|
12
|
+
readonly result: SandboxOutcome;
|
|
13
|
+
readonly timeoutMs: number;
|
|
14
|
+
}
|
|
15
|
+
/** The mandatory durable intent boundary is shared by command and AgentSession writers. */
|
|
16
|
+
export declare function executeRunCommand(journal: VerifiedRunJournal, request: OwnedRunCommand, policy: {
|
|
17
|
+
readonly cleanupMs: number;
|
|
18
|
+
readonly maxOutputBytes: number;
|
|
19
|
+
readonly signal?: AbortSignal;
|
|
20
|
+
}): Promise<OwnedRunResult>;
|
|
21
|
+
//# sourceMappingURL=owned-execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owned-execution.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/owned-execution.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIvD,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AACD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,2FAA2F;AAC3F,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE;IACP,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B,GACC,OAAO,CAAC,cAAc,CAAC,CAgCzB","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { performance } from \"node:perf_hooks\";\nimport { executeSandbox, type SandboxOutcome } from \"./broker.ts\";\nimport type { VerifiedRunJournal } from \"./journal.ts\";\nimport type { NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { VerifiedRunError } from \"./storage.ts\";\n\nexport interface OwnedRunCommand {\n\treadonly role: \"writer\" | \"verifier\";\n\treadonly argv: readonly string[];\n\treadonly workspace: string;\n\treadonly deadline: number;\n\treadonly claimId: string | null;\n}\nexport interface OwnedRunResult {\n\treadonly executionId: string;\n\treadonly result: SandboxOutcome;\n\treadonly timeoutMs: number;\n}\n\n/** The mandatory durable intent boundary is shared by command and AgentSession writers. */\nexport async function executeRunCommand(\n\tjournal: VerifiedRunJournal,\n\trequest: OwnedRunCommand,\n\tpolicy: {\n\t\treadonly cleanupMs: number;\n\t\treadonly maxOutputBytes: number;\n\t\treadonly signal?: AbortSignal;\n\t},\n): Promise<OwnedRunResult> {\n\tif (Math.floor(request.deadline - performance.now()) <= 0) throw new VerifiedRunError(\"deadline\");\n\tif (policy.signal?.aborted) throw new VerifiedRunError(\"cancelled\");\n\tconst generation = journal.state.generation;\n\tconst executionId = randomUUID();\n\tjournal.append({ kind: \"dispatch\", executionId, role: request.role, claimId: request.claimId });\n\tconst timeoutMs = Math.floor(request.deadline - performance.now());\n\tif (timeoutMs <= 0 || policy.signal?.aborted) {\n\t\tconst failure = policy.signal?.aborted ? \"cancelled\" : \"deadline\";\n\t\tjournal.append({ kind: \"exited\", executionId, failure });\n\t\tthrow new VerifiedRunError(failure);\n\t}\n\tconst result = await executeSandbox({\n\t\tworkspace: request.workspace,\n\t\targv: request.argv,\n\t\twritable: request.role === \"writer\",\n\t\ttimeoutMs,\n\t\tcleanupMs: policy.cleanupMs,\n\t\tmaxOutputBytes: policy.maxOutputBytes,\n\t\t...(policy.signal ? { signal: policy.signal } : {}),\n\t\t...(journal.state.budget\n\t\t\t? {\n\t\t\t\t\tonReady: (identity: NamespaceIdentity) => {\n\t\t\t\t\t\tif (journal.state.generation !== generation) throw new VerifiedRunError(\"stale_generation\");\n\t\t\t\t\t\tjournal.append({ kind: \"process_ready\", executionId, identity });\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t});\n\tif (journal.state.generation !== generation) throw new VerifiedRunError(\"stale_generation\");\n\tjournal.append({ kind: \"exited\", executionId, failure: request.role === \"writer\" ? result.failure : null });\n\treturn { executionId, result, timeoutMs };\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { performance } from "node:perf_hooks";
|
|
3
|
+
import { executeSandbox } from "./broker.js";
|
|
4
|
+
import { VerifiedRunError } from "./storage.js";
|
|
5
|
+
/** The mandatory durable intent boundary is shared by command and AgentSession writers. */
|
|
6
|
+
export async function executeRunCommand(journal, request, policy) {
|
|
7
|
+
if (Math.floor(request.deadline - performance.now()) <= 0)
|
|
8
|
+
throw new VerifiedRunError("deadline");
|
|
9
|
+
if (policy.signal?.aborted)
|
|
10
|
+
throw new VerifiedRunError("cancelled");
|
|
11
|
+
const generation = journal.state.generation;
|
|
12
|
+
const executionId = randomUUID();
|
|
13
|
+
journal.append({ kind: "dispatch", executionId, role: request.role, claimId: request.claimId });
|
|
14
|
+
const timeoutMs = Math.floor(request.deadline - performance.now());
|
|
15
|
+
if (timeoutMs <= 0 || policy.signal?.aborted) {
|
|
16
|
+
const failure = policy.signal?.aborted ? "cancelled" : "deadline";
|
|
17
|
+
journal.append({ kind: "exited", executionId, failure });
|
|
18
|
+
throw new VerifiedRunError(failure);
|
|
19
|
+
}
|
|
20
|
+
const result = await executeSandbox({
|
|
21
|
+
workspace: request.workspace,
|
|
22
|
+
argv: request.argv,
|
|
23
|
+
writable: request.role === "writer",
|
|
24
|
+
timeoutMs,
|
|
25
|
+
cleanupMs: policy.cleanupMs,
|
|
26
|
+
maxOutputBytes: policy.maxOutputBytes,
|
|
27
|
+
...(policy.signal ? { signal: policy.signal } : {}),
|
|
28
|
+
...(journal.state.budget
|
|
29
|
+
? {
|
|
30
|
+
onReady: (identity) => {
|
|
31
|
+
if (journal.state.generation !== generation)
|
|
32
|
+
throw new VerifiedRunError("stale_generation");
|
|
33
|
+
journal.append({ kind: "process_ready", executionId, identity });
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
: {}),
|
|
37
|
+
});
|
|
38
|
+
if (journal.state.generation !== generation)
|
|
39
|
+
throw new VerifiedRunError("stale_generation");
|
|
40
|
+
journal.append({ kind: "exited", executionId, failure: request.role === "writer" ? result.failure : null });
|
|
41
|
+
return { executionId, result, timeoutMs };
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=owned-execution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owned-execution.js","sourceRoot":"","sources":["../../../src/core/verified-run/owned-execution.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { performance } from \"node:perf_hooks\";\nimport { executeSandbox, type SandboxOutcome } from \"./broker.ts\";\nimport type { VerifiedRunJournal } from \"./journal.ts\";\nimport type { NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { VerifiedRunError } from \"./storage.ts\";\n\nexport interface OwnedRunCommand {\n\treadonly role: \"writer\" | \"verifier\";\n\treadonly argv: readonly string[];\n\treadonly workspace: string;\n\treadonly deadline: number;\n\treadonly claimId: string | null;\n}\nexport interface OwnedRunResult {\n\treadonly executionId: string;\n\treadonly result: SandboxOutcome;\n\treadonly timeoutMs: number;\n}\n\n/** The mandatory durable intent boundary is shared by command and AgentSession writers. */\nexport async function executeRunCommand(\n\tjournal: VerifiedRunJournal,\n\trequest: OwnedRunCommand,\n\tpolicy: {\n\t\treadonly cleanupMs: number;\n\t\treadonly maxOutputBytes: number;\n\t\treadonly signal?: AbortSignal;\n\t},\n): Promise<OwnedRunResult> {\n\tif (Math.floor(request.deadline - performance.now()) <= 0) throw new VerifiedRunError(\"deadline\");\n\tif (policy.signal?.aborted) throw new VerifiedRunError(\"cancelled\");\n\tconst generation = journal.state.generation;\n\tconst executionId = randomUUID();\n\tjournal.append({ kind: \"dispatch\", executionId, role: request.role, claimId: request.claimId });\n\tconst timeoutMs = Math.floor(request.deadline - performance.now());\n\tif (timeoutMs <= 0 || policy.signal?.aborted) {\n\t\tconst failure = policy.signal?.aborted ? \"cancelled\" : \"deadline\";\n\t\tjournal.append({ kind: \"exited\", executionId, failure });\n\t\tthrow new VerifiedRunError(failure);\n\t}\n\tconst result = await executeSandbox({\n\t\tworkspace: request.workspace,\n\t\targv: request.argv,\n\t\twritable: request.role === \"writer\",\n\t\ttimeoutMs,\n\t\tcleanupMs: policy.cleanupMs,\n\t\tmaxOutputBytes: policy.maxOutputBytes,\n\t\t...(policy.signal ? { signal: policy.signal } : {}),\n\t\t...(journal.state.budget\n\t\t\t? {\n\t\t\t\t\tonReady: (identity: NamespaceIdentity) => {\n\t\t\t\t\t\tif (journal.state.generation !== generation) throw new VerifiedRunError(\"stale_generation\");\n\t\t\t\t\t\tjournal.append({ kind: \"process_ready\", executionId, identity });\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t});\n\tif (journal.state.generation !== generation) throw new VerifiedRunError(\"stale_generation\");\n\tjournal.append({ kind: \"exited\", executionId, failure: request.role === \"writer\" ? result.failure : null });\n\treturn { executionId, result, timeoutMs };\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunContract } from "omk-protocol";
|
|
2
|
+
import type { VerifiedRunJournal } from "./journal.ts";
|
|
3
|
+
export interface RunPhaseContext {
|
|
4
|
+
readonly contract: RunContract;
|
|
5
|
+
readonly runPath: string;
|
|
6
|
+
readonly journal: VerifiedRunJournal;
|
|
7
|
+
readonly signal?: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=phase-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-context.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/phase-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC9B","sourcesContent":["import type { RunContract } from \"omk-protocol\";\nimport type { VerifiedRunJournal } from \"./journal.ts\";\n\nexport interface RunPhaseContext {\n\treadonly contract: RunContract;\n\treadonly runPath: string;\n\treadonly journal: VerifiedRunJournal;\n\treadonly signal?: AbortSignal;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-context.js","sourceRoot":"","sources":["../../../src/core/verified-run/phase-context.ts"],"names":[],"mappings":"","sourcesContent":["import type { RunContract } from \"omk-protocol\";\nimport type { VerifiedRunJournal } from \"./journal.ts\";\n\nexport interface RunPhaseContext {\n\treadonly contract: RunContract;\n\treadonly runPath: string;\n\treadonly journal: VerifiedRunJournal;\n\treadonly signal?: AbortSignal;\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type NamespaceIdentity } from "./namespace-identity.ts";
|
|
2
|
+
export declare const PROCESS_GATE_ARGV: readonly ["/bin/sh", "-c", "IFS= read -r gate && [ \"$gate\" = go ] && exec \"$@\"", "omk-process-gate"];
|
|
3
|
+
/** This JSON comes only from bwrap's private info FD, before any candidate code is released. */
|
|
4
|
+
export declare function identityFromSandboxInfo(bytes: string): NamespaceIdentity;
|
|
5
|
+
//# sourceMappingURL=process-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-gate.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/process-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG3F,eAAO,MAAM,iBAAiB,0GAKpB,CAAC;AAEX,gGAAgG;AAChG,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAoBxE","sourcesContent":["import { captureNamespaceIdentity, type NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { VerifiedRunError } from \"./storage.ts\";\n\nexport const PROCESS_GATE_ARGV = [\n\t\"/bin/sh\",\n\t\"-c\",\n\t'IFS= read -r gate && [ \"$gate\" = go ] && exec \"$@\"',\n\t\"omk-process-gate\",\n] as const;\n\n/** This JSON comes only from bwrap's private info FD, before any candidate code is released. */\nexport function identityFromSandboxInfo(bytes: string): NamespaceIdentity {\n\tlet info: unknown;\n\ttry {\n\t\tinfo = JSON.parse(bytes);\n\t} catch {\n\t\tthrow new VerifiedRunError(\"process_identity\");\n\t}\n\tif (\n\t\ttypeof info !== \"object\" ||\n\t\tinfo === null ||\n\t\t!(\"child-pid\" in info) ||\n\t\ttypeof info[\"child-pid\"] !== \"number\" ||\n\t\t!(\"pid-namespace\" in info) ||\n\t\ttypeof info[\"pid-namespace\"] !== \"number\" ||\n\t\t!Number.isSafeInteger(info[\"pid-namespace\"])\n\t)\n\t\tthrow new VerifiedRunError(\"process_identity\");\n\tconst identity = captureNamespaceIdentity(info[\"child-pid\"]);\n\tif (identity.namespace !== `pid:[${info[\"pid-namespace\"]}]`) throw new VerifiedRunError(\"process_identity\");\n\treturn identity;\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { captureNamespaceIdentity } from "./namespace-identity.js";
|
|
2
|
+
import { VerifiedRunError } from "./storage.js";
|
|
3
|
+
export const PROCESS_GATE_ARGV = [
|
|
4
|
+
"/bin/sh",
|
|
5
|
+
"-c",
|
|
6
|
+
'IFS= read -r gate && [ "$gate" = go ] && exec "$@"',
|
|
7
|
+
"omk-process-gate",
|
|
8
|
+
];
|
|
9
|
+
/** This JSON comes only from bwrap's private info FD, before any candidate code is released. */
|
|
10
|
+
export function identityFromSandboxInfo(bytes) {
|
|
11
|
+
let info;
|
|
12
|
+
try {
|
|
13
|
+
info = JSON.parse(bytes);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new VerifiedRunError("process_identity");
|
|
17
|
+
}
|
|
18
|
+
if (typeof info !== "object" ||
|
|
19
|
+
info === null ||
|
|
20
|
+
!("child-pid" in info) ||
|
|
21
|
+
typeof info["child-pid"] !== "number" ||
|
|
22
|
+
!("pid-namespace" in info) ||
|
|
23
|
+
typeof info["pid-namespace"] !== "number" ||
|
|
24
|
+
!Number.isSafeInteger(info["pid-namespace"]))
|
|
25
|
+
throw new VerifiedRunError("process_identity");
|
|
26
|
+
const identity = captureNamespaceIdentity(info["child-pid"]);
|
|
27
|
+
if (identity.namespace !== `pid:[${info["pid-namespace"]}]`)
|
|
28
|
+
throw new VerifiedRunError("process_identity");
|
|
29
|
+
return identity;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=process-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-gate.js","sourceRoot":"","sources":["../../../src/core/verified-run/process-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAA0B,MAAM,yBAAyB,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,SAAS;IACT,IAAI;IACJ,oDAAoD;IACpD,kBAAkB;CACT,CAAC;AAEX,gGAAgG;AAChG,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAqB;IACzE,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACJ,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACR,MAAM,IAAI,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;IAChD,CAAC;IACD,IACC,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,KAAK,IAAI;QACb,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC;QACtB,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,QAAQ;QACrC,CAAC,CAAC,eAAe,IAAI,IAAI,CAAC;QAC1B,OAAO,IAAI,CAAC,eAAe,CAAC,KAAK,QAAQ;QACzC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAE5C,MAAM,IAAI,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,wBAAwB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,IAAI,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,eAAe,CAAC,GAAG;QAAE,MAAM,IAAI,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;IAC5G,OAAO,QAAQ,CAAC;AAAA,CAChB","sourcesContent":["import { captureNamespaceIdentity, type NamespaceIdentity } from \"./namespace-identity.ts\";\nimport { VerifiedRunError } from \"./storage.ts\";\n\nexport const PROCESS_GATE_ARGV = [\n\t\"/bin/sh\",\n\t\"-c\",\n\t'IFS= read -r gate && [ \"$gate\" = go ] && exec \"$@\"',\n\t\"omk-process-gate\",\n] as const;\n\n/** This JSON comes only from bwrap's private info FD, before any candidate code is released. */\nexport function identityFromSandboxInfo(bytes: string): NamespaceIdentity {\n\tlet info: unknown;\n\ttry {\n\t\tinfo = JSON.parse(bytes);\n\t} catch {\n\t\tthrow new VerifiedRunError(\"process_identity\");\n\t}\n\tif (\n\t\ttypeof info !== \"object\" ||\n\t\tinfo === null ||\n\t\t!(\"child-pid\" in info) ||\n\t\ttypeof info[\"child-pid\"] !== \"number\" ||\n\t\t!(\"pid-namespace\" in info) ||\n\t\ttypeof info[\"pid-namespace\"] !== \"number\" ||\n\t\t!Number.isSafeInteger(info[\"pid-namespace\"])\n\t)\n\t\tthrow new VerifiedRunError(\"process_identity\");\n\tconst identity = captureNamespaceIdentity(info[\"child-pid\"]);\n\tif (identity.namespace !== `pid:[${info[\"pid-namespace\"]}]`) throw new VerifiedRunError(\"process_identity\");\n\treturn identity;\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RunEvent, RunProjection } from "./run-types.ts";
|
|
2
|
+
/** Deterministic replay only. Recovery observations are supplied by the trusted host adapter. */
|
|
3
|
+
export declare function projectRun(events: readonly RunEvent[]): RunProjection;
|
|
4
|
+
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/projection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAmB,MAAM,gBAAgB,CAAC;AAI/E,iGAAiG;AACjG,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,aAAa,CAmOrE","sourcesContent":["import { reduceDagEvent } from \"./dag-projection.ts\";\nimport type { RunTaskProjection } from \"./dag-types.ts\";\nimport { reduceRecoveryEvent } from \"./recovery-projection.ts\";\nimport type { RunEvent, RunProjection, WriterReduction } from \"./run-types.ts\";\nimport { VerifiedRunError } from \"./storage.ts\";\nimport { acceptWriterDispatch, reduceWriterEvent } from \"./writer-projection.ts\";\n\n/** Deterministic replay only. Recovery observations are supplied by the trusted host adapter. */\nexport function projectRun(events: readonly RunEvent[]): RunProjection {\n\tconst first = events[0];\n\tif (first?.kind !== \"created\" || first.contract.runId !== first.command.runId)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\tconst context: WriterReduction = {\n\t\tcontract: first.contract,\n\t\twriterFinished: false,\n\t\twriterStarted: false,\n\t\tproducerStarted: false,\n\t\twriterCommands: 0,\n\t\trequestBaseline: 0,\n\t\trequests: new Set(),\n\t\tstate: {\n\t\t\trunId: first.contract.runId,\n\t\t\trevision: 1,\n\t\t\tgeneration: 1,\n\t\t\texecution: \"ready\",\n\t\t\tsettlement: \"open\",\n\t\t\tverification: \"not_requested\",\n\t\t\tapplication: \"not_requested\",\n\t\t\tcandidateDigest: null,\n\t\t\tinputDigest: null,\n\t\t\treceiptDigest: null,\n\t\t\tfailure: null,\n\t\t\tactiveExecutionIds: [],\n\t\t\twriterOpen: false,\n\t\t\tmodelRequests: 0,\n\t\t\ttasks:\n\t\t\t\tfirst.contract.profile === \"linux-command-dag-v1\"\n\t\t\t\t\t? first.contract.writer.tasks.map(\n\t\t\t\t\t\t\t(task): RunTaskProjection => ({\n\t\t\t\t\t\t\t\ttaskId: task.id,\n\t\t\t\t\t\t\t\tattempt: 0,\n\t\t\t\t\t\t\t\tgeneration: 1,\n\t\t\t\t\t\t\t\tstatus: \"pending\",\n\t\t\t\t\t\t\t\tinputDigest: null,\n\t\t\t\t\t\t\t\toutputDigest: null,\n\t\t\t\t\t\t\t\tfailure: null,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t: [],\n\t\t\tbudget: null,\n\t\t\tenvironmentDigest: null,\n\t\t\tverificationDeadlineMs: null,\n\t\t\tlastClockMs: null,\n\t\t\tprocesses: [],\n\t\t},\n\t};\n\tconst dispatched = new Set<string>();\n\tconst checked = new Set<string>();\n\tconst commands = new Set([first.command.commandId]);\n\tlet activeRole: \"writer\" | \"verifier\" = \"writer\";\n\tfor (const event of events.slice(1)) {\n\t\tconst state = context.state;\n\t\tif (state.execution === \"failed\" || state.receiptDigest) throw new VerifiedRunError(\"integrity\");\n\t\tswitch (event.kind) {\n\t\t\tcase \"created\":\n\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\tcase \"budget_anchored\": {\n\t\t\t\tconst budget = event.budget;\n\t\t\t\tconst limits = first.contract.budget;\n\t\t\t\tif (\n\t\t\t\t\tstate.revision !== 1 ||\n\t\t\t\t\tstate.budget ||\n\t\t\t\t\tbudget.workDeadlineMs - budget.startedMs !== limits.workMs ||\n\t\t\t\t\tbudget.verifyCapMs - budget.workDeadlineMs !== limits.verifyMs ||\n\t\t\t\t\tbudget.cleanupDeadlineMs - budget.verifyCapMs !== limits.cleanupMs\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tbudget,\n\t\t\t\t\tenvironmentDigest: event.environmentDigest,\n\t\t\t\t\tlastClockMs: budget.startedMs,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"input_checkpoint\":\n\t\t\t\tif (\n\t\t\t\t\tstate.revision !== 2 ||\n\t\t\t\t\t!state.budget ||\n\t\t\t\t\tstate.inputDigest ||\n\t\t\t\t\tevent.digest !== first.contract.workspace.baseDigest\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = { ...state, inputDigest: event.digest };\n\t\t\t\tbreak;\n\t\t\tcase \"task_started\":\n\t\t\tcase \"task_finished\":\n\t\t\tcase \"tasks_paused\":\n\t\t\t\treduceDagEvent(context, event);\n\t\t\t\tbreak;\n\t\t\tcase \"writer_opened\":\n\t\t\tcase \"model_request\":\n\t\t\tcase \"writer_closed\":\n\t\t\t\treduceWriterEvent(context, event);\n\t\t\t\tbreak;\n\t\t\tcase \"dispatch\": {\n\t\t\t\tif (state.failure || state.activeExecutionIds.length || dispatched.has(event.executionId))\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tif (event.role === \"writer\") {\n\t\t\t\t\tacceptWriterDispatch(context, event);\n\t\t\t\t} else {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!context.writerFinished ||\n\t\t\t\t\t\tstate.writerOpen ||\n\t\t\t\t\t\t!state.candidateDigest ||\n\t\t\t\t\t\tevent.claimId === null ||\n\t\t\t\t\t\t!first.contract.checks.some((check) => check.claimId === event.claimId) ||\n\t\t\t\t\t\tchecked.has(event.claimId)\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\t\tchecked.add(event.claimId);\n\t\t\t\t}\n\t\t\t\tdispatched.add(event.executionId);\n\t\t\t\tactiveRole = event.role;\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\texecution: \"running\",\n\t\t\t\t\tsettlement: \"draining\",\n\t\t\t\t\tactiveExecutionIds: [event.executionId],\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"process_ready\":\n\t\t\t\tif (\n\t\t\t\t\t!state.budget ||\n\t\t\t\t\tstate.activeExecutionIds[0] !== event.executionId ||\n\t\t\t\t\tevent.identity.bootId !== state.budget.bootId ||\n\t\t\t\t\tstate.processes.some((item) => item.executionId === event.executionId)\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tprocesses: [...state.processes, { executionId: event.executionId, identity: event.identity }],\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tcase \"exited\":\n\t\t\t\tif (\n\t\t\t\t\tstate.activeExecutionIds[0] !== event.executionId ||\n\t\t\t\t\t(state.budget &&\n\t\t\t\t\t\tevent.failure === null &&\n\t\t\t\t\t\t!state.processes.some((item) => item.executionId === event.executionId))\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tif (activeRole === \"writer\") context.writerFinished = event.failure === null;\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tsettlement: state.writerOpen ? \"open\" : \"settled\",\n\t\t\t\t\tactiveExecutionIds: [],\n\t\t\t\t\tfailure: event.failure,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tcase \"candidate\": {\n\t\t\t\tif (\n\t\t\t\t\t!context.writerFinished ||\n\t\t\t\t\tstate.writerOpen ||\n\t\t\t\t\tstate.failure ||\n\t\t\t\t\tstate.activeExecutionIds.length ||\n\t\t\t\t\tstate.candidateDigest ||\n\t\t\t\t\tstate.tasks.some((task) => task.status !== \"succeeded\" || task.generation !== state.generation)\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tif (state.budget) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tevent.observedMs === undefined ||\n\t\t\t\t\t\tevent.verificationDeadlineMs === undefined ||\n\t\t\t\t\t\tevent.observedMs < state.budget.startedMs ||\n\t\t\t\t\t\tevent.observedMs > state.budget.workDeadlineMs ||\n\t\t\t\t\t\tevent.verificationDeadlineMs !==\n\t\t\t\t\t\t\tMath.min(event.observedMs + first.contract.budget.verifyMs, state.budget.verifyCapMs)\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\t} else if (event.observedMs !== undefined || event.verificationDeadlineMs !== undefined)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tcandidateDigest: event.digest,\n\t\t\t\t\tverificationDeadlineMs: event.verificationDeadlineMs ?? null,\n\t\t\t\t\tlastClockMs: event.observedMs ?? null,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"resumed\":\n\t\t\tcase \"tasks_retried\":\n\t\t\tcase \"writer_restarted\":\n\t\t\t\treduceRecoveryEvent(context, event, { commands, checked, activeRole });\n\t\t\t\tbreak;\n\t\t\tcase \"evaluated\":\n\t\t\t\tif (\n\t\t\t\t\t!state.candidateDigest ||\n\t\t\t\t\tstate.writerOpen ||\n\t\t\t\t\tstate.activeExecutionIds.length ||\n\t\t\t\t\tchecked.size !== first.contract.checks.length ||\n\t\t\t\t\t(event.verified && state.failure)\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\treceiptDigest: event.receiptDigest,\n\t\t\t\t\texecution: \"succeeded\",\n\t\t\t\t\tverification: event.verified ? \"verified\" : \"violated\",\n\t\t\t\t\tapplication: event.verified ? \"candidate_ready\" : \"not_requested\",\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tcase \"failed\":\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\texecution: \"failed\",\n\t\t\t\t\tfailure: event.code,\n\t\t\t\t\tverification: \"inconclusive\",\n\t\t\t\t\tsettlement: state.activeExecutionIds.length || state.writerOpen ? \"quarantined\" : \"settled\",\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = event;\n\t\t\t\tthrow new VerifiedRunError(String(exhaustive));\n\t\t\t}\n\t\t}\n\t\tcontext.state = { ...context.state, revision: state.revision + 1 };\n\t}\n\treturn Object.freeze({\n\t\t...context.state,\n\t\tactiveExecutionIds: Object.freeze([...context.state.activeExecutionIds]),\n\t\tprocesses: Object.freeze(context.state.processes.map((item) => Object.freeze(item))),\n\t\ttasks: Object.freeze(context.state.tasks.map((task) => Object.freeze(task))),\n\t});\n}\n"]}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { reduceDagEvent } from "./dag-projection.js";
|
|
2
|
+
import { reduceRecoveryEvent } from "./recovery-projection.js";
|
|
3
|
+
import { VerifiedRunError } from "./storage.js";
|
|
4
|
+
import { acceptWriterDispatch, reduceWriterEvent } from "./writer-projection.js";
|
|
5
|
+
/** Deterministic replay only. Recovery observations are supplied by the trusted host adapter. */
|
|
6
|
+
export function projectRun(events) {
|
|
7
|
+
const first = events[0];
|
|
8
|
+
if (first?.kind !== "created" || first.contract.runId !== first.command.runId)
|
|
9
|
+
throw new VerifiedRunError("integrity");
|
|
10
|
+
const context = {
|
|
11
|
+
contract: first.contract,
|
|
12
|
+
writerFinished: false,
|
|
13
|
+
writerStarted: false,
|
|
14
|
+
producerStarted: false,
|
|
15
|
+
writerCommands: 0,
|
|
16
|
+
requestBaseline: 0,
|
|
17
|
+
requests: new Set(),
|
|
18
|
+
state: {
|
|
19
|
+
runId: first.contract.runId,
|
|
20
|
+
revision: 1,
|
|
21
|
+
generation: 1,
|
|
22
|
+
execution: "ready",
|
|
23
|
+
settlement: "open",
|
|
24
|
+
verification: "not_requested",
|
|
25
|
+
application: "not_requested",
|
|
26
|
+
candidateDigest: null,
|
|
27
|
+
inputDigest: null,
|
|
28
|
+
receiptDigest: null,
|
|
29
|
+
failure: null,
|
|
30
|
+
activeExecutionIds: [],
|
|
31
|
+
writerOpen: false,
|
|
32
|
+
modelRequests: 0,
|
|
33
|
+
tasks: first.contract.profile === "linux-command-dag-v1"
|
|
34
|
+
? first.contract.writer.tasks.map((task) => ({
|
|
35
|
+
taskId: task.id,
|
|
36
|
+
attempt: 0,
|
|
37
|
+
generation: 1,
|
|
38
|
+
status: "pending",
|
|
39
|
+
inputDigest: null,
|
|
40
|
+
outputDigest: null,
|
|
41
|
+
failure: null,
|
|
42
|
+
}))
|
|
43
|
+
: [],
|
|
44
|
+
budget: null,
|
|
45
|
+
environmentDigest: null,
|
|
46
|
+
verificationDeadlineMs: null,
|
|
47
|
+
lastClockMs: null,
|
|
48
|
+
processes: [],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
const dispatched = new Set();
|
|
52
|
+
const checked = new Set();
|
|
53
|
+
const commands = new Set([first.command.commandId]);
|
|
54
|
+
let activeRole = "writer";
|
|
55
|
+
for (const event of events.slice(1)) {
|
|
56
|
+
const state = context.state;
|
|
57
|
+
if (state.execution === "failed" || state.receiptDigest)
|
|
58
|
+
throw new VerifiedRunError("integrity");
|
|
59
|
+
switch (event.kind) {
|
|
60
|
+
case "created":
|
|
61
|
+
throw new VerifiedRunError("integrity");
|
|
62
|
+
case "budget_anchored": {
|
|
63
|
+
const budget = event.budget;
|
|
64
|
+
const limits = first.contract.budget;
|
|
65
|
+
if (state.revision !== 1 ||
|
|
66
|
+
state.budget ||
|
|
67
|
+
budget.workDeadlineMs - budget.startedMs !== limits.workMs ||
|
|
68
|
+
budget.verifyCapMs - budget.workDeadlineMs !== limits.verifyMs ||
|
|
69
|
+
budget.cleanupDeadlineMs - budget.verifyCapMs !== limits.cleanupMs)
|
|
70
|
+
throw new VerifiedRunError("integrity");
|
|
71
|
+
context.state = {
|
|
72
|
+
...state,
|
|
73
|
+
budget,
|
|
74
|
+
environmentDigest: event.environmentDigest,
|
|
75
|
+
lastClockMs: budget.startedMs,
|
|
76
|
+
};
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case "input_checkpoint":
|
|
80
|
+
if (state.revision !== 2 ||
|
|
81
|
+
!state.budget ||
|
|
82
|
+
state.inputDigest ||
|
|
83
|
+
event.digest !== first.contract.workspace.baseDigest)
|
|
84
|
+
throw new VerifiedRunError("integrity");
|
|
85
|
+
context.state = { ...state, inputDigest: event.digest };
|
|
86
|
+
break;
|
|
87
|
+
case "task_started":
|
|
88
|
+
case "task_finished":
|
|
89
|
+
case "tasks_paused":
|
|
90
|
+
reduceDagEvent(context, event);
|
|
91
|
+
break;
|
|
92
|
+
case "writer_opened":
|
|
93
|
+
case "model_request":
|
|
94
|
+
case "writer_closed":
|
|
95
|
+
reduceWriterEvent(context, event);
|
|
96
|
+
break;
|
|
97
|
+
case "dispatch": {
|
|
98
|
+
if (state.failure || state.activeExecutionIds.length || dispatched.has(event.executionId))
|
|
99
|
+
throw new VerifiedRunError("integrity");
|
|
100
|
+
if (event.role === "writer") {
|
|
101
|
+
acceptWriterDispatch(context, event);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
if (!context.writerFinished ||
|
|
105
|
+
state.writerOpen ||
|
|
106
|
+
!state.candidateDigest ||
|
|
107
|
+
event.claimId === null ||
|
|
108
|
+
!first.contract.checks.some((check) => check.claimId === event.claimId) ||
|
|
109
|
+
checked.has(event.claimId))
|
|
110
|
+
throw new VerifiedRunError("integrity");
|
|
111
|
+
checked.add(event.claimId);
|
|
112
|
+
}
|
|
113
|
+
dispatched.add(event.executionId);
|
|
114
|
+
activeRole = event.role;
|
|
115
|
+
context.state = {
|
|
116
|
+
...state,
|
|
117
|
+
execution: "running",
|
|
118
|
+
settlement: "draining",
|
|
119
|
+
activeExecutionIds: [event.executionId],
|
|
120
|
+
};
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "process_ready":
|
|
124
|
+
if (!state.budget ||
|
|
125
|
+
state.activeExecutionIds[0] !== event.executionId ||
|
|
126
|
+
event.identity.bootId !== state.budget.bootId ||
|
|
127
|
+
state.processes.some((item) => item.executionId === event.executionId))
|
|
128
|
+
throw new VerifiedRunError("integrity");
|
|
129
|
+
context.state = {
|
|
130
|
+
...state,
|
|
131
|
+
processes: [...state.processes, { executionId: event.executionId, identity: event.identity }],
|
|
132
|
+
};
|
|
133
|
+
break;
|
|
134
|
+
case "exited":
|
|
135
|
+
if (state.activeExecutionIds[0] !== event.executionId ||
|
|
136
|
+
(state.budget &&
|
|
137
|
+
event.failure === null &&
|
|
138
|
+
!state.processes.some((item) => item.executionId === event.executionId)))
|
|
139
|
+
throw new VerifiedRunError("integrity");
|
|
140
|
+
if (activeRole === "writer")
|
|
141
|
+
context.writerFinished = event.failure === null;
|
|
142
|
+
context.state = {
|
|
143
|
+
...state,
|
|
144
|
+
settlement: state.writerOpen ? "open" : "settled",
|
|
145
|
+
activeExecutionIds: [],
|
|
146
|
+
failure: event.failure,
|
|
147
|
+
};
|
|
148
|
+
break;
|
|
149
|
+
case "candidate": {
|
|
150
|
+
if (!context.writerFinished ||
|
|
151
|
+
state.writerOpen ||
|
|
152
|
+
state.failure ||
|
|
153
|
+
state.activeExecutionIds.length ||
|
|
154
|
+
state.candidateDigest ||
|
|
155
|
+
state.tasks.some((task) => task.status !== "succeeded" || task.generation !== state.generation))
|
|
156
|
+
throw new VerifiedRunError("integrity");
|
|
157
|
+
if (state.budget) {
|
|
158
|
+
if (event.observedMs === undefined ||
|
|
159
|
+
event.verificationDeadlineMs === undefined ||
|
|
160
|
+
event.observedMs < state.budget.startedMs ||
|
|
161
|
+
event.observedMs > state.budget.workDeadlineMs ||
|
|
162
|
+
event.verificationDeadlineMs !==
|
|
163
|
+
Math.min(event.observedMs + first.contract.budget.verifyMs, state.budget.verifyCapMs))
|
|
164
|
+
throw new VerifiedRunError("integrity");
|
|
165
|
+
}
|
|
166
|
+
else if (event.observedMs !== undefined || event.verificationDeadlineMs !== undefined)
|
|
167
|
+
throw new VerifiedRunError("integrity");
|
|
168
|
+
context.state = {
|
|
169
|
+
...state,
|
|
170
|
+
candidateDigest: event.digest,
|
|
171
|
+
verificationDeadlineMs: event.verificationDeadlineMs ?? null,
|
|
172
|
+
lastClockMs: event.observedMs ?? null,
|
|
173
|
+
};
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case "resumed":
|
|
177
|
+
case "tasks_retried":
|
|
178
|
+
case "writer_restarted":
|
|
179
|
+
reduceRecoveryEvent(context, event, { commands, checked, activeRole });
|
|
180
|
+
break;
|
|
181
|
+
case "evaluated":
|
|
182
|
+
if (!state.candidateDigest ||
|
|
183
|
+
state.writerOpen ||
|
|
184
|
+
state.activeExecutionIds.length ||
|
|
185
|
+
checked.size !== first.contract.checks.length ||
|
|
186
|
+
(event.verified && state.failure))
|
|
187
|
+
throw new VerifiedRunError("integrity");
|
|
188
|
+
context.state = {
|
|
189
|
+
...state,
|
|
190
|
+
receiptDigest: event.receiptDigest,
|
|
191
|
+
execution: "succeeded",
|
|
192
|
+
verification: event.verified ? "verified" : "violated",
|
|
193
|
+
application: event.verified ? "candidate_ready" : "not_requested",
|
|
194
|
+
};
|
|
195
|
+
break;
|
|
196
|
+
case "failed":
|
|
197
|
+
context.state = {
|
|
198
|
+
...state,
|
|
199
|
+
execution: "failed",
|
|
200
|
+
failure: event.code,
|
|
201
|
+
verification: "inconclusive",
|
|
202
|
+
settlement: state.activeExecutionIds.length || state.writerOpen ? "quarantined" : "settled",
|
|
203
|
+
};
|
|
204
|
+
break;
|
|
205
|
+
default: {
|
|
206
|
+
const exhaustive = event;
|
|
207
|
+
throw new VerifiedRunError(String(exhaustive));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
context.state = { ...context.state, revision: state.revision + 1 };
|
|
211
|
+
}
|
|
212
|
+
return Object.freeze({
|
|
213
|
+
...context.state,
|
|
214
|
+
activeExecutionIds: Object.freeze([...context.state.activeExecutionIds]),
|
|
215
|
+
processes: Object.freeze(context.state.processes.map((item) => Object.freeze(item))),
|
|
216
|
+
tasks: Object.freeze(context.state.tasks.map((task) => Object.freeze(task))),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../../../src/core/verified-run/projection.ts"],"names":[],"mappings":"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","sourcesContent":["import { reduceDagEvent } from \"./dag-projection.ts\";\nimport type { RunTaskProjection } from \"./dag-types.ts\";\nimport { reduceRecoveryEvent } from \"./recovery-projection.ts\";\nimport type { RunEvent, RunProjection, WriterReduction } from \"./run-types.ts\";\nimport { VerifiedRunError } from \"./storage.ts\";\nimport { acceptWriterDispatch, reduceWriterEvent } from \"./writer-projection.ts\";\n\n/** Deterministic replay only. Recovery observations are supplied by the trusted host adapter. */\nexport function projectRun(events: readonly RunEvent[]): RunProjection {\n\tconst first = events[0];\n\tif (first?.kind !== \"created\" || first.contract.runId !== first.command.runId)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\tconst context: WriterReduction = {\n\t\tcontract: first.contract,\n\t\twriterFinished: false,\n\t\twriterStarted: false,\n\t\tproducerStarted: false,\n\t\twriterCommands: 0,\n\t\trequestBaseline: 0,\n\t\trequests: new Set(),\n\t\tstate: {\n\t\t\trunId: first.contract.runId,\n\t\t\trevision: 1,\n\t\t\tgeneration: 1,\n\t\t\texecution: \"ready\",\n\t\t\tsettlement: \"open\",\n\t\t\tverification: \"not_requested\",\n\t\t\tapplication: \"not_requested\",\n\t\t\tcandidateDigest: null,\n\t\t\tinputDigest: null,\n\t\t\treceiptDigest: null,\n\t\t\tfailure: null,\n\t\t\tactiveExecutionIds: [],\n\t\t\twriterOpen: false,\n\t\t\tmodelRequests: 0,\n\t\t\ttasks:\n\t\t\t\tfirst.contract.profile === \"linux-command-dag-v1\"\n\t\t\t\t\t? first.contract.writer.tasks.map(\n\t\t\t\t\t\t\t(task): RunTaskProjection => ({\n\t\t\t\t\t\t\t\ttaskId: task.id,\n\t\t\t\t\t\t\t\tattempt: 0,\n\t\t\t\t\t\t\t\tgeneration: 1,\n\t\t\t\t\t\t\t\tstatus: \"pending\",\n\t\t\t\t\t\t\t\tinputDigest: null,\n\t\t\t\t\t\t\t\toutputDigest: null,\n\t\t\t\t\t\t\t\tfailure: null,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t: [],\n\t\t\tbudget: null,\n\t\t\tenvironmentDigest: null,\n\t\t\tverificationDeadlineMs: null,\n\t\t\tlastClockMs: null,\n\t\t\tprocesses: [],\n\t\t},\n\t};\n\tconst dispatched = new Set<string>();\n\tconst checked = new Set<string>();\n\tconst commands = new Set([first.command.commandId]);\n\tlet activeRole: \"writer\" | \"verifier\" = \"writer\";\n\tfor (const event of events.slice(1)) {\n\t\tconst state = context.state;\n\t\tif (state.execution === \"failed\" || state.receiptDigest) throw new VerifiedRunError(\"integrity\");\n\t\tswitch (event.kind) {\n\t\t\tcase \"created\":\n\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\tcase \"budget_anchored\": {\n\t\t\t\tconst budget = event.budget;\n\t\t\t\tconst limits = first.contract.budget;\n\t\t\t\tif (\n\t\t\t\t\tstate.revision !== 1 ||\n\t\t\t\t\tstate.budget ||\n\t\t\t\t\tbudget.workDeadlineMs - budget.startedMs !== limits.workMs ||\n\t\t\t\t\tbudget.verifyCapMs - budget.workDeadlineMs !== limits.verifyMs ||\n\t\t\t\t\tbudget.cleanupDeadlineMs - budget.verifyCapMs !== limits.cleanupMs\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tbudget,\n\t\t\t\t\tenvironmentDigest: event.environmentDigest,\n\t\t\t\t\tlastClockMs: budget.startedMs,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"input_checkpoint\":\n\t\t\t\tif (\n\t\t\t\t\tstate.revision !== 2 ||\n\t\t\t\t\t!state.budget ||\n\t\t\t\t\tstate.inputDigest ||\n\t\t\t\t\tevent.digest !== first.contract.workspace.baseDigest\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = { ...state, inputDigest: event.digest };\n\t\t\t\tbreak;\n\t\t\tcase \"task_started\":\n\t\t\tcase \"task_finished\":\n\t\t\tcase \"tasks_paused\":\n\t\t\t\treduceDagEvent(context, event);\n\t\t\t\tbreak;\n\t\t\tcase \"writer_opened\":\n\t\t\tcase \"model_request\":\n\t\t\tcase \"writer_closed\":\n\t\t\t\treduceWriterEvent(context, event);\n\t\t\t\tbreak;\n\t\t\tcase \"dispatch\": {\n\t\t\t\tif (state.failure || state.activeExecutionIds.length || dispatched.has(event.executionId))\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tif (event.role === \"writer\") {\n\t\t\t\t\tacceptWriterDispatch(context, event);\n\t\t\t\t} else {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!context.writerFinished ||\n\t\t\t\t\t\tstate.writerOpen ||\n\t\t\t\t\t\t!state.candidateDigest ||\n\t\t\t\t\t\tevent.claimId === null ||\n\t\t\t\t\t\t!first.contract.checks.some((check) => check.claimId === event.claimId) ||\n\t\t\t\t\t\tchecked.has(event.claimId)\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\t\tchecked.add(event.claimId);\n\t\t\t\t}\n\t\t\t\tdispatched.add(event.executionId);\n\t\t\t\tactiveRole = event.role;\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\texecution: \"running\",\n\t\t\t\t\tsettlement: \"draining\",\n\t\t\t\t\tactiveExecutionIds: [event.executionId],\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"process_ready\":\n\t\t\t\tif (\n\t\t\t\t\t!state.budget ||\n\t\t\t\t\tstate.activeExecutionIds[0] !== event.executionId ||\n\t\t\t\t\tevent.identity.bootId !== state.budget.bootId ||\n\t\t\t\t\tstate.processes.some((item) => item.executionId === event.executionId)\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tprocesses: [...state.processes, { executionId: event.executionId, identity: event.identity }],\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tcase \"exited\":\n\t\t\t\tif (\n\t\t\t\t\tstate.activeExecutionIds[0] !== event.executionId ||\n\t\t\t\t\t(state.budget &&\n\t\t\t\t\t\tevent.failure === null &&\n\t\t\t\t\t\t!state.processes.some((item) => item.executionId === event.executionId))\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tif (activeRole === \"writer\") context.writerFinished = event.failure === null;\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tsettlement: state.writerOpen ? \"open\" : \"settled\",\n\t\t\t\t\tactiveExecutionIds: [],\n\t\t\t\t\tfailure: event.failure,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tcase \"candidate\": {\n\t\t\t\tif (\n\t\t\t\t\t!context.writerFinished ||\n\t\t\t\t\tstate.writerOpen ||\n\t\t\t\t\tstate.failure ||\n\t\t\t\t\tstate.activeExecutionIds.length ||\n\t\t\t\t\tstate.candidateDigest ||\n\t\t\t\t\tstate.tasks.some((task) => task.status !== \"succeeded\" || task.generation !== state.generation)\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tif (state.budget) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tevent.observedMs === undefined ||\n\t\t\t\t\t\tevent.verificationDeadlineMs === undefined ||\n\t\t\t\t\t\tevent.observedMs < state.budget.startedMs ||\n\t\t\t\t\t\tevent.observedMs > state.budget.workDeadlineMs ||\n\t\t\t\t\t\tevent.verificationDeadlineMs !==\n\t\t\t\t\t\t\tMath.min(event.observedMs + first.contract.budget.verifyMs, state.budget.verifyCapMs)\n\t\t\t\t\t)\n\t\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\t} else if (event.observedMs !== undefined || event.verificationDeadlineMs !== undefined)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\tcandidateDigest: event.digest,\n\t\t\t\t\tverificationDeadlineMs: event.verificationDeadlineMs ?? null,\n\t\t\t\t\tlastClockMs: event.observedMs ?? null,\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"resumed\":\n\t\t\tcase \"tasks_retried\":\n\t\t\tcase \"writer_restarted\":\n\t\t\t\treduceRecoveryEvent(context, event, { commands, checked, activeRole });\n\t\t\t\tbreak;\n\t\t\tcase \"evaluated\":\n\t\t\t\tif (\n\t\t\t\t\t!state.candidateDigest ||\n\t\t\t\t\tstate.writerOpen ||\n\t\t\t\t\tstate.activeExecutionIds.length ||\n\t\t\t\t\tchecked.size !== first.contract.checks.length ||\n\t\t\t\t\t(event.verified && state.failure)\n\t\t\t\t)\n\t\t\t\t\tthrow new VerifiedRunError(\"integrity\");\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\treceiptDigest: event.receiptDigest,\n\t\t\t\t\texecution: \"succeeded\",\n\t\t\t\t\tverification: event.verified ? \"verified\" : \"violated\",\n\t\t\t\t\tapplication: event.verified ? \"candidate_ready\" : \"not_requested\",\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tcase \"failed\":\n\t\t\t\tcontext.state = {\n\t\t\t\t\t...state,\n\t\t\t\t\texecution: \"failed\",\n\t\t\t\t\tfailure: event.code,\n\t\t\t\t\tverification: \"inconclusive\",\n\t\t\t\t\tsettlement: state.activeExecutionIds.length || state.writerOpen ? \"quarantined\" : \"settled\",\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = event;\n\t\t\t\tthrow new VerifiedRunError(String(exhaustive));\n\t\t\t}\n\t\t}\n\t\tcontext.state = { ...context.state, revision: state.revision + 1 };\n\t}\n\treturn Object.freeze({\n\t\t...context.state,\n\t\tactiveExecutionIds: Object.freeze([...context.state.activeExecutionIds]),\n\t\tprocesses: Object.freeze(context.state.processes.map((item) => Object.freeze(item))),\n\t\ttasks: Object.freeze(context.state.tasks.map((task) => Object.freeze(task))),\n\t});\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RunPhaseBudget } from "omk-protocol";
|
|
2
|
+
export interface RunClock {
|
|
3
|
+
readonly bootId: string;
|
|
4
|
+
readonly nowMs: number;
|
|
5
|
+
}
|
|
6
|
+
export interface RecoveryBudget {
|
|
7
|
+
readonly bootId: string;
|
|
8
|
+
readonly startedMs: number;
|
|
9
|
+
readonly workDeadlineMs: number;
|
|
10
|
+
readonly verifyCapMs: number;
|
|
11
|
+
readonly cleanupDeadlineMs: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function readRunClock(): RunClock;
|
|
14
|
+
export declare function anchorRunBudget(limits: RunPhaseBudget, clock: RunClock): RecoveryBudget;
|
|
15
|
+
export declare function parseRecoveryBudget(raw: unknown): RecoveryBudget;
|
|
16
|
+
export declare function remainingRunTime(budget: RecoveryBudget, deadlineMs: number, clock?: RunClock): number;
|
|
17
|
+
export declare const remainingVerification: typeof remainingRunTime;
|
|
18
|
+
export declare function localVerificationDeadline(budget: RecoveryBudget, deadlineMs: number): number;
|
|
19
|
+
//# sourceMappingURL=recovery-clock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-clock.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/recovery-clock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AACD,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACnC;AAGD,wBAAgB,YAAY,IAAI,QAAQ,CAOvC;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,QAAQ,GAAG,cAAc,CAQvF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAgChE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,WAAiB,GAAG,MAAM,CAM3G;AAED,eAAO,MAAM,qBAAqB,yBAAmB,CAAC;AAEtD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAI5F","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { uptime } from \"node:os\";\nimport { performance } from \"node:perf_hooks\";\nimport type { RunPhaseBudget } from \"omk-protocol\";\nimport { VerifiedRunError } from \"./storage.ts\";\n\nexport interface RunClock {\n\treadonly bootId: string;\n\treadonly nowMs: number;\n}\nexport interface RecoveryBudget {\n\treadonly bootId: string;\n\treadonly startedMs: number;\n\treadonly workDeadlineMs: number;\n\treadonly verifyCapMs: number;\n\treadonly cleanupDeadlineMs: number;\n}\nconst BOOT_ID = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;\n\nexport function readRunClock(): RunClock {\n\tif (process.platform !== \"linux\") throw new VerifiedRunError(\"clock_unavailable\");\n\tconst bootId = readFileSync(\"/proc/sys/kernel/random/boot_id\", \"utf8\").trim();\n\tconst nowMs = Math.floor(uptime() * 1000);\n\tif (!BOOT_ID.test(bootId) || !Number.isSafeInteger(nowMs) || nowMs < 0)\n\t\tthrow new VerifiedRunError(\"clock_unavailable\");\n\treturn Object.freeze({ bootId, nowMs });\n}\n\nexport function anchorRunBudget(limits: RunPhaseBudget, clock: RunClock): RecoveryBudget {\n\treturn parseRecoveryBudget({\n\t\tbootId: clock.bootId,\n\t\tstartedMs: clock.nowMs,\n\t\tworkDeadlineMs: clock.nowMs + limits.workMs,\n\t\tverifyCapMs: clock.nowMs + limits.workMs + limits.verifyMs,\n\t\tcleanupDeadlineMs: clock.nowMs + limits.workMs + limits.verifyMs + limits.cleanupMs,\n\t});\n}\n\nexport function parseRecoveryBudget(raw: unknown): RecoveryBudget {\n\tif (\n\t\ttypeof raw !== \"object\" ||\n\t\traw === null ||\n\t\t!(\"bootId\" in raw) ||\n\t\ttypeof raw.bootId !== \"string\" ||\n\t\t!BOOT_ID.test(raw.bootId) ||\n\t\t!(\"startedMs\" in raw) ||\n\t\ttypeof raw.startedMs !== \"number\" ||\n\t\t!(\"workDeadlineMs\" in raw) ||\n\t\ttypeof raw.workDeadlineMs !== \"number\" ||\n\t\t!(\"verifyCapMs\" in raw) ||\n\t\ttypeof raw.verifyCapMs !== \"number\" ||\n\t\t!(\"cleanupDeadlineMs\" in raw) ||\n\t\ttypeof raw.cleanupDeadlineMs !== \"number\"\n\t)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\tconst values = [raw.startedMs, raw.workDeadlineMs, raw.verifyCapMs, raw.cleanupDeadlineMs];\n\tif (\n\t\tvalues.some((value) => !Number.isSafeInteger(value) || value < 0) ||\n\t\traw.workDeadlineMs <= raw.startedMs ||\n\t\traw.verifyCapMs <= raw.workDeadlineMs ||\n\t\traw.cleanupDeadlineMs <= raw.verifyCapMs\n\t)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\treturn Object.freeze({\n\t\tbootId: raw.bootId,\n\t\tstartedMs: raw.startedMs,\n\t\tworkDeadlineMs: raw.workDeadlineMs,\n\t\tverifyCapMs: raw.verifyCapMs,\n\t\tcleanupDeadlineMs: raw.cleanupDeadlineMs,\n\t});\n}\n\nexport function remainingRunTime(budget: RecoveryBudget, deadlineMs: number, clock = readRunClock()): number {\n\tif (clock.bootId !== budget.bootId) throw new VerifiedRunError(\"clock_changed\");\n\tif (clock.nowMs < budget.startedMs) throw new VerifiedRunError(\"clock_rollback\");\n\tif (!Number.isSafeInteger(deadlineMs) || deadlineMs <= budget.startedMs || deadlineMs > budget.verifyCapMs)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\treturn Math.max(0, deadlineMs - clock.nowMs);\n}\n\nexport const remainingVerification = remainingRunTime;\n\nexport function localVerificationDeadline(budget: RecoveryBudget, deadlineMs: number): number {\n\tconst remaining = remainingVerification(budget, deadlineMs);\n\tif (remaining <= 0) throw new VerifiedRunError(\"deadline\");\n\treturn performance.now() + remaining;\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { uptime } from "node:os";
|
|
3
|
+
import { performance } from "node:perf_hooks";
|
|
4
|
+
import { VerifiedRunError } from "./storage.js";
|
|
5
|
+
const BOOT_ID = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;
|
|
6
|
+
export function readRunClock() {
|
|
7
|
+
if (process.platform !== "linux")
|
|
8
|
+
throw new VerifiedRunError("clock_unavailable");
|
|
9
|
+
const bootId = readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim();
|
|
10
|
+
const nowMs = Math.floor(uptime() * 1000);
|
|
11
|
+
if (!BOOT_ID.test(bootId) || !Number.isSafeInteger(nowMs) || nowMs < 0)
|
|
12
|
+
throw new VerifiedRunError("clock_unavailable");
|
|
13
|
+
return Object.freeze({ bootId, nowMs });
|
|
14
|
+
}
|
|
15
|
+
export function anchorRunBudget(limits, clock) {
|
|
16
|
+
return parseRecoveryBudget({
|
|
17
|
+
bootId: clock.bootId,
|
|
18
|
+
startedMs: clock.nowMs,
|
|
19
|
+
workDeadlineMs: clock.nowMs + limits.workMs,
|
|
20
|
+
verifyCapMs: clock.nowMs + limits.workMs + limits.verifyMs,
|
|
21
|
+
cleanupDeadlineMs: clock.nowMs + limits.workMs + limits.verifyMs + limits.cleanupMs,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function parseRecoveryBudget(raw) {
|
|
25
|
+
if (typeof raw !== "object" ||
|
|
26
|
+
raw === null ||
|
|
27
|
+
!("bootId" in raw) ||
|
|
28
|
+
typeof raw.bootId !== "string" ||
|
|
29
|
+
!BOOT_ID.test(raw.bootId) ||
|
|
30
|
+
!("startedMs" in raw) ||
|
|
31
|
+
typeof raw.startedMs !== "number" ||
|
|
32
|
+
!("workDeadlineMs" in raw) ||
|
|
33
|
+
typeof raw.workDeadlineMs !== "number" ||
|
|
34
|
+
!("verifyCapMs" in raw) ||
|
|
35
|
+
typeof raw.verifyCapMs !== "number" ||
|
|
36
|
+
!("cleanupDeadlineMs" in raw) ||
|
|
37
|
+
typeof raw.cleanupDeadlineMs !== "number")
|
|
38
|
+
throw new VerifiedRunError("integrity");
|
|
39
|
+
const values = [raw.startedMs, raw.workDeadlineMs, raw.verifyCapMs, raw.cleanupDeadlineMs];
|
|
40
|
+
if (values.some((value) => !Number.isSafeInteger(value) || value < 0) ||
|
|
41
|
+
raw.workDeadlineMs <= raw.startedMs ||
|
|
42
|
+
raw.verifyCapMs <= raw.workDeadlineMs ||
|
|
43
|
+
raw.cleanupDeadlineMs <= raw.verifyCapMs)
|
|
44
|
+
throw new VerifiedRunError("integrity");
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
bootId: raw.bootId,
|
|
47
|
+
startedMs: raw.startedMs,
|
|
48
|
+
workDeadlineMs: raw.workDeadlineMs,
|
|
49
|
+
verifyCapMs: raw.verifyCapMs,
|
|
50
|
+
cleanupDeadlineMs: raw.cleanupDeadlineMs,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export function remainingRunTime(budget, deadlineMs, clock = readRunClock()) {
|
|
54
|
+
if (clock.bootId !== budget.bootId)
|
|
55
|
+
throw new VerifiedRunError("clock_changed");
|
|
56
|
+
if (clock.nowMs < budget.startedMs)
|
|
57
|
+
throw new VerifiedRunError("clock_rollback");
|
|
58
|
+
if (!Number.isSafeInteger(deadlineMs) || deadlineMs <= budget.startedMs || deadlineMs > budget.verifyCapMs)
|
|
59
|
+
throw new VerifiedRunError("integrity");
|
|
60
|
+
return Math.max(0, deadlineMs - clock.nowMs);
|
|
61
|
+
}
|
|
62
|
+
export const remainingVerification = remainingRunTime;
|
|
63
|
+
export function localVerificationDeadline(budget, deadlineMs) {
|
|
64
|
+
const remaining = remainingVerification(budget, deadlineMs);
|
|
65
|
+
if (remaining <= 0)
|
|
66
|
+
throw new VerifiedRunError("deadline");
|
|
67
|
+
return performance.now() + remaining;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=recovery-clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-clock.js","sourceRoot":"","sources":["../../../src/core/verified-run/recovery-clock.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { uptime } from \"node:os\";\nimport { performance } from \"node:perf_hooks\";\nimport type { RunPhaseBudget } from \"omk-protocol\";\nimport { VerifiedRunError } from \"./storage.ts\";\n\nexport interface RunClock {\n\treadonly bootId: string;\n\treadonly nowMs: number;\n}\nexport interface RecoveryBudget {\n\treadonly bootId: string;\n\treadonly startedMs: number;\n\treadonly workDeadlineMs: number;\n\treadonly verifyCapMs: number;\n\treadonly cleanupDeadlineMs: number;\n}\nconst BOOT_ID = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;\n\nexport function readRunClock(): RunClock {\n\tif (process.platform !== \"linux\") throw new VerifiedRunError(\"clock_unavailable\");\n\tconst bootId = readFileSync(\"/proc/sys/kernel/random/boot_id\", \"utf8\").trim();\n\tconst nowMs = Math.floor(uptime() * 1000);\n\tif (!BOOT_ID.test(bootId) || !Number.isSafeInteger(nowMs) || nowMs < 0)\n\t\tthrow new VerifiedRunError(\"clock_unavailable\");\n\treturn Object.freeze({ bootId, nowMs });\n}\n\nexport function anchorRunBudget(limits: RunPhaseBudget, clock: RunClock): RecoveryBudget {\n\treturn parseRecoveryBudget({\n\t\tbootId: clock.bootId,\n\t\tstartedMs: clock.nowMs,\n\t\tworkDeadlineMs: clock.nowMs + limits.workMs,\n\t\tverifyCapMs: clock.nowMs + limits.workMs + limits.verifyMs,\n\t\tcleanupDeadlineMs: clock.nowMs + limits.workMs + limits.verifyMs + limits.cleanupMs,\n\t});\n}\n\nexport function parseRecoveryBudget(raw: unknown): RecoveryBudget {\n\tif (\n\t\ttypeof raw !== \"object\" ||\n\t\traw === null ||\n\t\t!(\"bootId\" in raw) ||\n\t\ttypeof raw.bootId !== \"string\" ||\n\t\t!BOOT_ID.test(raw.bootId) ||\n\t\t!(\"startedMs\" in raw) ||\n\t\ttypeof raw.startedMs !== \"number\" ||\n\t\t!(\"workDeadlineMs\" in raw) ||\n\t\ttypeof raw.workDeadlineMs !== \"number\" ||\n\t\t!(\"verifyCapMs\" in raw) ||\n\t\ttypeof raw.verifyCapMs !== \"number\" ||\n\t\t!(\"cleanupDeadlineMs\" in raw) ||\n\t\ttypeof raw.cleanupDeadlineMs !== \"number\"\n\t)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\tconst values = [raw.startedMs, raw.workDeadlineMs, raw.verifyCapMs, raw.cleanupDeadlineMs];\n\tif (\n\t\tvalues.some((value) => !Number.isSafeInteger(value) || value < 0) ||\n\t\traw.workDeadlineMs <= raw.startedMs ||\n\t\traw.verifyCapMs <= raw.workDeadlineMs ||\n\t\traw.cleanupDeadlineMs <= raw.verifyCapMs\n\t)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\treturn Object.freeze({\n\t\tbootId: raw.bootId,\n\t\tstartedMs: raw.startedMs,\n\t\tworkDeadlineMs: raw.workDeadlineMs,\n\t\tverifyCapMs: raw.verifyCapMs,\n\t\tcleanupDeadlineMs: raw.cleanupDeadlineMs,\n\t});\n}\n\nexport function remainingRunTime(budget: RecoveryBudget, deadlineMs: number, clock = readRunClock()): number {\n\tif (clock.bootId !== budget.bootId) throw new VerifiedRunError(\"clock_changed\");\n\tif (clock.nowMs < budget.startedMs) throw new VerifiedRunError(\"clock_rollback\");\n\tif (!Number.isSafeInteger(deadlineMs) || deadlineMs <= budget.startedMs || deadlineMs > budget.verifyCapMs)\n\t\tthrow new VerifiedRunError(\"integrity\");\n\treturn Math.max(0, deadlineMs - clock.nowMs);\n}\n\nexport const remainingVerification = remainingRunTime;\n\nexport function localVerificationDeadline(budget: RecoveryBudget, deadlineMs: number): number {\n\tconst remaining = remainingVerification(budget, deadlineMs);\n\tif (remaining <= 0) throw new VerifiedRunError(\"deadline\");\n\treturn performance.now() + remaining;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RunResumeCommand, RunTaskRetryCommand, RunWriterRestartCommand } from "omk-protocol";
|
|
2
|
+
import { type JournalSnapshot, VerifiedRunJournal } from "./journal.ts";
|
|
3
|
+
import type { RunProjection } from "./run-types.ts";
|
|
4
|
+
export type RecoveryCommand = RunResumeCommand | RunWriterRestartCommand | RunTaskRetryCommand;
|
|
5
|
+
export declare function requireRunJournal(runPath: string): JournalSnapshot;
|
|
6
|
+
/** One lease/CAS/idempotency boundary for all explicit recovery actions. */
|
|
7
|
+
export declare function withRecoveryLease(runPath: string, command: RecoveryCommand, execute: (input: {
|
|
8
|
+
readonly snapshot: JournalSnapshot;
|
|
9
|
+
readonly journal: VerifiedRunJournal;
|
|
10
|
+
}) => Promise<RunProjection>): Promise<RunProjection>;
|
|
11
|
+
//# sourceMappingURL=recovery-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery-command.d.ts","sourceRoot":"","sources":["../../../src/core/verified-run/recovery-command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEnG,OAAO,EAAE,KAAK,eAAe,EAA+B,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACrG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGpD,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,uBAAuB,GAAG,mBAAmB,CAAC;AAC/F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAIlE;AA4CD,4EAA4E;AAC5E,wBAAsB,iBAAiB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,CAAC,KAAK,EAAE;IAChB,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;CACrC,KAAK,OAAO,CAAC,aAAa,CAAC,GAC1B,OAAO,CAAC,aAAa,CAAC,CAsBxB","sourcesContent":["import type { RunResumeCommand, RunTaskRetryCommand, RunWriterRestartCommand } from \"omk-protocol\";\nimport { acquireSessionOwnerLeaseSync } from \"../session-owner-lease.ts\";\nimport { type JournalSnapshot, journalPath, readRunJournal, VerifiedRunJournal } from \"./journal.ts\";\nimport type { RunProjection } from \"./run-types.ts\";\nimport { digestObject, VerifiedRunError } from \"./storage.ts\";\n\nexport type RecoveryCommand = RunResumeCommand | RunWriterRestartCommand | RunTaskRetryCommand;\nexport function requireRunJournal(runPath: string): JournalSnapshot {\n\tconst journal = readRunJournal(runPath);\n\tif (!journal) throw new VerifiedRunError(\"missing_run\");\n\treturn journal;\n}\n\nfunction commandDisposition(journal: JournalSnapshot, command: RecoveryCommand): \"new\" | \"duplicate\" {\n\tconst first = journal.records[0]?.event;\n\tif (\n\t\tfirst?.kind !== \"created\" ||\n\t\tfirst.contract.runId !== command.runId ||\n\t\tdigestObject(first.contract) !== command.contractDigest\n\t)\n\t\tthrow new VerifiedRunError(\"command_conflict\");\n\tif (first.command.commandId === command.commandId) throw new VerifiedRunError(\"command_conflict\");\n\tconst previous = journal.records.find(\n\t\t({ event }) =>\n\t\t\t(event.kind === \"resumed\" || event.kind === \"writer_restarted\" || event.kind === \"tasks_retried\") &&\n\t\t\tevent.command.commandId === command.commandId,\n\t)?.event;\n\tif (previous?.kind === \"resumed\" || previous?.kind === \"writer_restarted\" || previous?.kind === \"tasks_retried\") {\n\t\tif (digestObject(previous.command) !== digestObject(command)) throw new VerifiedRunError(\"command_conflict\");\n\t\treturn \"duplicate\";\n\t}\n\tif (command.expectedRevision !== journal.state.revision || command.expectedGeneration !== journal.state.generation)\n\t\tthrow new VerifiedRunError(\"stale_revision\");\n\tswitch (command.kind) {\n\t\tcase \"resume\":\n\t\t\tif (command.candidateDigest !== journal.state.candidateDigest)\n\t\t\t\tthrow new VerifiedRunError(\"candidate_mismatch\");\n\t\t\tbreak;\n\t\tcase \"retry_tasks\":\n\t\tcase \"restart_writer\":\n\t\t\tif (!journal.state.inputDigest) throw new VerifiedRunError(\"input_checkpoint_missing\");\n\t\t\tif (\n\t\t\t\tcommand.baseDigest !== journal.state.inputDigest ||\n\t\t\t\tcommand.baseDigest !== first.contract.workspace.baseDigest\n\t\t\t)\n\t\t\t\tthrow new VerifiedRunError(\"input_mismatch\");\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = command;\n\t\t\tthrow new VerifiedRunError(String(exhaustive));\n\t\t}\n\t}\n\treturn \"new\";\n}\n\n/** One lease/CAS/idempotency boundary for all explicit recovery actions. */\nexport async function withRecoveryLease(\n\trunPath: string,\n\tcommand: RecoveryCommand,\n\texecute: (input: {\n\t\treadonly snapshot: JournalSnapshot;\n\t\treadonly journal: VerifiedRunJournal;\n\t}) => Promise<RunProjection>,\n): Promise<RunProjection> {\n\tconst initial = requireRunJournal(runPath);\n\tif (commandDisposition(initial, command) === \"duplicate\") return initial.state;\n\tconst owner = acquireSessionOwnerLeaseSync(journalPath(runPath));\n\ttry {\n\t\tconst snapshot = requireRunJournal(runPath);\n\t\tif (commandDisposition(snapshot, command) === \"duplicate\") return snapshot.state;\n\t\tconst journal = new VerifiedRunJournal(runPath, owner);\n\t\ttry {\n\t\t\treturn await execute({ snapshot, journal });\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\terror instanceof VerifiedRunError &&\n\t\t\t\tjournal.state.generation > snapshot.state.generation &&\n\t\t\t\t!journal.state.receiptDigest\n\t\t\t)\n\t\t\t\treturn journal.append({ kind: \"failed\", code: error.code });\n\t\t\tthrow error;\n\t\t}\n\t} finally {\n\t\towner.release();\n\t}\n}\n"]}
|