@sema-agent/core 7.6.3 → 7.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +33 -10
  2. package/dist/agents/peer-admission.d.ts +1 -1
  3. package/dist/brain/anthropic.js +8 -2
  4. package/dist/brain/open-responses.js +5 -3
  5. package/dist/brain/openai.js +31 -8
  6. package/dist/brain/reasoning.d.ts +32 -0
  7. package/dist/brain/reasoning.js +18 -0
  8. package/dist/core/auto-mode-defaults.d.ts +16 -0
  9. package/dist/core/auto-mode-defaults.js +1 -0
  10. package/dist/core/auto-mode.d.ts +19 -0
  11. package/dist/core/auto-mode.js +74 -56
  12. package/dist/core/checkpoint-execution-record.d.ts +110 -0
  13. package/dist/core/checkpoint-execution-record.js +49 -0
  14. package/dist/core/checkpoint-store.d.ts +88 -10
  15. package/dist/core/checkpoint-store.js +35 -2
  16. package/dist/core/engine-notice.d.ts +11 -0
  17. package/dist/core/governance-codes.d.ts +1 -1
  18. package/dist/core/governance-codes.js +2 -0
  19. package/dist/core/runner/clock-and-limits.d.ts +117 -0
  20. package/dist/core/runner/clock-and-limits.js +118 -0
  21. package/dist/core/runner/contracts.d.ts +10 -0
  22. package/dist/core/runner/decide-continuation.d.ts +98 -0
  23. package/dist/core/runner/decide-continuation.js +133 -0
  24. package/dist/core/runner/execution-record.d.ts +26 -0
  25. package/dist/core/runner/execution-record.js +19 -0
  26. package/dist/core/runner/git-leg-delivery.d.ts +28 -0
  27. package/dist/core/runner/git-leg-delivery.js +94 -0
  28. package/dist/core/runner/initial-run-state.d.ts +14 -0
  29. package/dist/core/runner/initial-run-state.js +11 -0
  30. package/dist/core/runner/prepare-caps-and-workflow.js +17 -0
  31. package/dist/core/runner/prepare-run-refs.d.ts +0 -16
  32. package/dist/core/runner/prepare-run-refs.js +0 -6
  33. package/dist/core/runner/runtask.d.ts +0 -68
  34. package/dist/core/runner/runtask.js +28 -452
  35. package/dist/core/runner/steer-admission.d.ts +17 -0
  36. package/dist/core/runner/steer-admission.js +17 -0
  37. package/dist/core/runner/tool-end-body.d.ts +71 -0
  38. package/dist/core/runner/tool-end-body.js +74 -0
  39. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +4 -0
  40. package/dist/core/store-contracts/checkpoint-store-contract.js +85 -0
  41. package/dist/core/trace.d.ts +24 -0
  42. package/dist/index.d.ts +5 -4
  43. package/dist/index.js +4 -3
  44. package/dist/stores/file/checkpoint-store.d.ts +7 -0
  45. package/dist/stores/file/checkpoint-store.js +20 -1
  46. package/package.json +1 -1
  47. package/test/export-surface.snapshot.json +37 -1
@@ -0,0 +1,17 @@
1
+ const humanInputOrdinals = new WeakMap();
2
+ export function nextHumanInputSeq(key) {
3
+ let box = humanInputOrdinals.get(key);
4
+ if (box === undefined) {
5
+ box = { n: 0 };
6
+ humanInputOrdinals.set(key, box);
7
+ }
8
+ return ++box.n;
9
+ }
10
+ export function sameAcceptedSteerInput(a, b) {
11
+ return (a.payload === b.payload &&
12
+ a.trusted === b.trusted &&
13
+ a.priority === b.priority &&
14
+ a.actor?.id === b.actor?.id &&
15
+ a.actor?.hostAsserted === b.actor?.hostAsserted &&
16
+ a.actor?.issuer === b.actor?.issuer);
17
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * What the run loop derives from a tool call's END, in every leg that closes one: the `tool_end` frame
3
+ * body (the ONE projection the live loop, the resumed batch and the reconcile-recovered orphan all go
4
+ * through), the `toolResult` transcript message the resume engine closes pending calls with, the thin
5
+ * `response` digest the post-tool-batch observer is handed, and the canonical write-tool → reminder-window
6
+ * family the attachment reducer keys on when a write tool ends.
7
+ *
8
+ * Projections over values the caller already holds, with ONE clock read: `toolResultMsg` stamps the message
9
+ * it mints with `Date.now()` (as it did inside the driver — the transcript's timestamp is minted where the
10
+ * message is). Nothing here reads host state through a callback, awaits, or keeps state between calls.
11
+ * Layer-1 machinery, not a driven lane; the prefix rule it follows is written once, on initial-run-state.ts.
12
+ */
13
+ import { type AgentMessage } from "../../internal/harness.js";
14
+ import type { GateOutcome } from "../gate-outcome.js";
15
+ import { type McpDelivered } from "../mcp-failure.js";
16
+ import type { RecoveredOrphan } from "../session-reconcile.js";
17
+ import type { WriteFamily } from "./turn-attachments.js";
18
+ /** design/134 §3.1b — derive the thin `response` digest from a harness tool result: model-facing text
19
+ * content only (never `details` — the H4 thin-projection discipline), capped with a truncation note. */
20
+ export declare function batchResponseDigest(result: unknown): string | undefined;
21
+ /** The `tool_end` body fields projected from a harness tool result — output/truncated/totalChars via
22
+ * {@link toolOutputFrom} and the CC card via {@link structuredFrom}. Single construction point for BOTH
23
+ * the live loop's frames and the resumed batch's frames (`resolvePendingCall` + the deferred-sibling
24
+ * close): the resumed frames used to carry only `isError`, so a client rendering tool output from frames
25
+ * showed an empty body for every durable-approved call. Same projection = same source as the transcript.
26
+ * Also the one place the gate outcome reaches a frame — see the parameter. */
27
+ export declare function toolEndBodyFrom(result: unknown, isError: boolean,
28
+ /** The gate's record of the pass that admitted or refused this call, supplied by the CALLER of this
29
+ * projection — the live loop reads it off the gate's per-call sideband, the resumed leg off the decide's
30
+ * minted record. Deliberately a parameter and never derived from `result`: a tool's own `details`
31
+ * (which post-tool hooks may also replace) is writable by layers that adjudicate nothing, so reading
32
+ * provenance out of it would let a failing tool claim a person approved it. Omitted ⇒ the call never
33
+ * went through the gate (see the `tool_end.gate` doc, the one home). */
34
+ gate?: GateOutcome,
35
+ /** WHICH call this run's committed durable park is holding ({@link import("./park-commit.js").gatedCallIdOf}), for the frames the
36
+ * abort short-circuits. Same never-derived-from-`result` posture as the outcome above, and for the
37
+ * sharpest version of that reason: this one is an assertion about a DIFFERENT call, so a tool able to
38
+ * author it could point an approval UI at a call nobody is waiting on. Omitted ⇒ no park is holding a
39
+ * call (nothing parked, or the park that did holds none), and the frame then carries no id at all. */
40
+ gatedCallIdOfRun?: string): {
41
+ output?: unknown;
42
+ truncated?: boolean;
43
+ totalChars?: number;
44
+ structured?: unknown;
45
+ errorCode?: string;
46
+ delivered?: McpDelivered;
47
+ gatedCallId?: string;
48
+ gate?: GateOutcome;
49
+ };
50
+ /**
51
+ * scan-1/A1 — the BODY of the synthetic `tool_end` that closes a reconcile-recovered orphan. ONE
52
+ * construction point for BOTH minting legs (the live-abort loop at the end of a run, and the wake/crash
53
+ * leg's replay at run open), so the two can never disagree about the shape of the same event.
54
+ *
55
+ * Both frames used to carry `isError:true` and NOTHING else: a consumer rendering tool output from the
56
+ * event stream showed an EMPTY body for every interrupted call, even though the persisted transcript
57
+ * (which the model reads) carried the full `[INTERRUPTED]` explanation — the two faces of one call
58
+ * disagreed. The projection goes through the same {@link toolEndBodyFrom} every live tool result uses, so
59
+ * `output` = the persisted model-facing text and `errorCode` = the persisted `details.errorKind`
60
+ * (`interrupted_never_started` / `interrupted_outcome_unknown`) — a consumer discriminates on the code
61
+ * instead of prose-matching. No `structured`: the reconcile mints no CC card (no `details.type`), which
62
+ * `structuredFrom`'s allowlist already enforces.
63
+ */
64
+ export declare function reconciledToolEndBody(orphan: Pick<RecoveredOrphan, "text" | "errorKind">): ReturnType<typeof toolEndBodyFrom>;
65
+ /** task #51 F2/F3 — the CANONICAL write-tool → reminder-window mapping (CC qFm :480253-480254
66
+ * `a.name === bD || a.name === hF` = TaskCreate/TaskUpdate; BFm :480217-480219 = TodoWrite). Live
67
+ * deployment-alias resolution happens at the call sites (run-local `writeFamilyOf` over the mounted
68
+ * roster); retired-name normalization is gone (RB-476-A, 5.0.0). */
69
+ export declare function writeFamilyOfCanonical(name: string): WriteFamily | undefined;
70
+ /** A `toolResult` transcript message (text content). Used by the resume engine to close pending calls. */
71
+ export declare function toolResultMsg(toolCallId: string, toolName: string, text: string, isError: boolean): AgentMessage;
@@ -0,0 +1,74 @@
1
+ import {} from "../../internal/harness.js";
2
+ import { MCP_DELIVERY_VERDICTS } from "../mcp-failure.js";
3
+ import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
4
+ import { structuredFrom, toolOutputFrom } from "./tool-output-projection.js";
5
+ const BATCH_RESPONSE_MAX_CHARS = 500;
6
+ export function batchResponseDigest(result) {
7
+ const content = result !== null && typeof result === "object" ? result.content : result;
8
+ if (content === undefined || content === null)
9
+ return undefined;
10
+ let text;
11
+ if (typeof content === "string") {
12
+ text = content;
13
+ }
14
+ else if (Array.isArray(content)) {
15
+ text = content
16
+ .map((c) => (c !== null && typeof c === "object" && typeof c.text === "string" ? c.text : ""))
17
+ .filter((t) => t.length > 0)
18
+ .join("\n");
19
+ }
20
+ else {
21
+ try {
22
+ text = JSON.stringify(content) ?? "";
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ if (text.length === 0)
29
+ return undefined;
30
+ return text.length > BATCH_RESPONSE_MAX_CHARS
31
+ ? `${text.slice(0, BATCH_RESPONSE_MAX_CHARS)}…[+${text.length - BATCH_RESPONSE_MAX_CHARS} chars truncated]`
32
+ : text;
33
+ }
34
+ export function toolEndBodyFrom(result, isError, gate, gatedCallIdOfRun) {
35
+ const o = toolOutputFrom(result);
36
+ const st = structuredFrom(result);
37
+ const det = isError ? result?.details : undefined;
38
+ const codeRaw = det?.code;
39
+ const kindRaw = det?.errorKind;
40
+ const code = typeof codeRaw === "string" ? codeRaw : typeof kindRaw === "string" ? kindRaw : undefined;
41
+ const deliveredRaw = det?.delivered;
42
+ const delivered = typeof deliveredRaw === "string" && MCP_DELIVERY_VERDICTS.includes(deliveredRaw) ? deliveredRaw : undefined;
43
+ const gatedCallId = code === "gate.parked" ? gatedCallIdOfRun : undefined;
44
+ return {
45
+ ...(o !== undefined ? { output: o.output, ...(o.truncated ? { truncated: true } : {}), ...(o.totalChars !== undefined ? { totalChars: o.totalChars } : {}) } : {}),
46
+ ...(st !== undefined ? { structured: st } : {}),
47
+ ...(typeof code === "string" ? { errorCode: code } : {}),
48
+ ...(delivered !== undefined ? { delivered } : {}),
49
+ ...(gatedCallId !== undefined ? { gatedCallId } : {}),
50
+ ...(gate !== undefined ? { gate } : {}),
51
+ };
52
+ }
53
+ export function reconciledToolEndBody(orphan) {
54
+ return toolEndBodyFrom({ content: orphan.text, details: { code: orphan.errorKind } }, true);
55
+ }
56
+ export function writeFamilyOfCanonical(name) {
57
+ if (name === "TaskCreate" || name === "TaskUpdate")
58
+ return "task";
59
+ if (name === "TodoWrite")
60
+ return "todo";
61
+ if (name === TOOL_SEARCH_NAME)
62
+ return "tool_search";
63
+ return undefined;
64
+ }
65
+ export function toolResultMsg(toolCallId, toolName, text, isError) {
66
+ return {
67
+ role: "toolResult",
68
+ toolCallId,
69
+ toolName,
70
+ content: [{ type: "text", text }],
71
+ isError,
72
+ timestamp: Date.now(),
73
+ };
74
+ }
@@ -1,4 +1,5 @@
1
1
  import { type Checkpoint, type CheckpointStore, type ResumeOutcome } from "../checkpoint-store.js";
2
+ import type { GateOutcome } from "../gate-outcome.js";
2
3
  import { type CheckpointFields } from "../pause-registry.js";
3
4
  import { type ContractAssertionRunner } from "./contract-harness.js";
4
5
  /**
@@ -23,6 +24,9 @@ export declare function createCheckpointFixture(over?: Partial<CheckpointFields>
23
24
  export declare const ALLOW: Extract<ResumeOutcome, {
24
25
  gate: "policy_ask";
25
26
  }>;
27
+ export declare const EXECUTED_ALLOW: GateOutcome;
28
+ export declare const EXECUTED_ALLOW_REORDERED: GateOutcome;
29
+ export declare const VETOED_BY_POLICY: GateOutcome;
26
30
  /**
27
31
  * The full cross-backend contract for {@link CheckpointStore} — the SAME assertions for every
28
32
  * backend. Entries are a 1:1 port of the vitest originals (assertion semantics unchanged).
@@ -1,4 +1,5 @@
1
1
  import { strict as assert } from "node:assert";
2
+ import { executionVerdict } from "../checkpoint-execution-record.js";
2
3
  import { MAX_PENDING_STEER_ENTRIES, mintCheckpointToken, readPendingSteerQueue, } from "../checkpoint-store.js";
3
4
  import { checkpointFrom } from "../pause-registry.js";
4
5
  import { beginContract } from "./contract-harness.js";
@@ -36,6 +37,10 @@ export const ALLOW = {
36
37
  boundInputHash: "h0",
37
38
  hostDecision: { decidedBy: "person" },
38
39
  };
40
+ const SETTLED = { kind: "human_allowed", who: { party: "person" }, when: 1_700_000_000_500 };
41
+ export const EXECUTED_ALLOW = Object.freeze({ disposition: Object.freeze({ kind: "allowed" }), settlement: Object.freeze({ ...SETTLED, who: Object.freeze({ ...SETTLED.who }) }), origin: "policy" });
42
+ export const EXECUTED_ALLOW_REORDERED = Object.freeze({ origin: "policy", settlement: Object.freeze({ when: 1_700_000_000_500, who: Object.freeze({ party: "person" }), kind: "human_allowed" }), disposition: Object.freeze({ kind: "allowed" }) });
43
+ export const VETOED_BY_POLICY = Object.freeze({ disposition: Object.freeze({ kind: "denied", deniedBy: "policy" }), settlement: Object.freeze({ ...SETTLED, who: Object.freeze({ ...SETTLED.who }) }), origin: "policy" });
39
44
  export async function checkpointStoreContract(make, runAssertion) {
40
45
  const { run, settle } = beginContract(runAssertion);
41
46
  run("kit prerequisites: reopen/setPendingSteer/listByScope are implemented (REQUIRED by this kit)", async () => {
@@ -43,6 +48,86 @@ export async function checkpointStoreContract(make, runAssertion) {
43
48
  const missing = ["reopen", "setPendingSteer", "listByScope"].filter((m) => typeof probe[m] !== "function");
44
49
  assert.equal(missing.length, 0, `backend does not implement ${missing.join(", ")} — required for the CheckpointStore contract kit`);
45
50
  });
51
+ run("execution declaration: the backend declares execution.outcome === true (a REQUIRED member of the contract)", async () => {
52
+ const probe = make();
53
+ assert.equal(probe.execution?.outcome, true, "backend does not declare `execution = { outcome: true }` — the run refuses the store at its first read");
54
+ assert.equal(typeof probe.recordExecutionOutcome, "function", "backend does not implement recordExecutionOutcome — required by the CheckpointStore contract");
55
+ });
56
+ run("recordExecutionOutcome on a resolved row → `recorded`; the record and its stamp ride the row, the decision winner is untouched, executionVerdict reads executed", async () => {
57
+ const store = make();
58
+ const cp = createCheckpointFixture();
59
+ await store.put(cp.token, cp);
60
+ assert.equal(executionVerdict((await store.get(cp.token))).kind, "unknown");
61
+ await store.resolve(cp.token, cp.scope, ALLOW);
62
+ const before = (await store.get(cp.token));
63
+ assert.equal(executionVerdict(before).kind, "unknown", "a resolved row with no record reads UNKNOWN — never allowed by default");
64
+ assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, VETOED_BY_POLICY), "recorded");
65
+ const after = (await store.get(cp.token));
66
+ assert.deepEqual(after.executionOutcome, VETOED_BY_POLICY);
67
+ assert.equal(typeof after.executionAtMs, "number");
68
+ assert.equal(Number.isFinite(after.executionAtMs), true);
69
+ assert.deepEqual(executionVerdict(after), { kind: "executed", gate: VETOED_BY_POLICY });
70
+ assert.deepEqual(after.resolvedOutcome, before.resolvedOutcome);
71
+ assert.equal(after.status, "resolved");
72
+ assert.equal(after.rev, before.rev);
73
+ });
74
+ run("recordExecutionOutcome is idempotent on the SAME record (structural, key-order-blind) → `already_recorded`; a DIFFERENT record throws checkpoint.execution_outcome_conflict and the row is byte-identical", async () => {
75
+ const store = make();
76
+ const cp = createCheckpointFixture();
77
+ await store.put(cp.token, cp);
78
+ await store.resolve(cp.token, cp.scope, ALLOW);
79
+ assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "recorded");
80
+ const filed = (await store.get(cp.token));
81
+ assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "already_recorded");
82
+ assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW_REORDERED), "already_recorded", "equality is over the record's JSON — key order and freezing are not part of the value");
83
+ assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, structuredClone(EXECUTED_ALLOW)), "already_recorded", "an unfrozen structural twin is the same record");
84
+ await assert.rejects(store.recordExecutionOutcome(cp.token, cp.scope, VETOED_BY_POLICY), (e) => e.code === "checkpoint.execution_outcome_conflict");
85
+ const still = (await store.get(cp.token));
86
+ assert.deepEqual(still.executionOutcome, filed.executionOutcome);
87
+ assert.equal(still.executionAtMs, filed.executionAtMs, "a conflict writes nothing — not even a fresh stamp");
88
+ });
89
+ run("recordExecutionOutcome answers `not_resolved` for a pending and for an expired row, `absent` for a missing row and for a wrong-scope row; none of them write", async () => {
90
+ const store = make();
91
+ const pending = createCheckpointFixture();
92
+ await store.put(pending.token, pending);
93
+ assert.equal(await store.recordExecutionOutcome(pending.token, pending.scope, EXECUTED_ALLOW), "not_resolved");
94
+ assert.equal((await store.get(pending.token)).executionOutcome, undefined);
95
+ assert.equal(await store.recordExecutionOutcome(pending.token, "tenant-b", EXECUTED_ALLOW), "absent", "a wrong-scope row reads exactly like a missing one (the isolation rule)");
96
+ assert.equal(await store.recordExecutionOutcome(mintCheckpointToken(), pending.scope, EXECUTED_ALLOW), "absent");
97
+ const expired = createCheckpointFixture();
98
+ await store.put(expired.token, expired);
99
+ assert.equal(await store.expire(expired.token, expired.scope), true);
100
+ assert.equal(await store.recordExecutionOutcome(expired.token, expired.scope, EXECUTED_ALLOW), "not_resolved");
101
+ assert.equal((await store.get(expired.token)).executionOutcome, undefined);
102
+ });
103
+ run("reopen is refused on a row that carries an execution record (a settled action is not re-openable); the same row reopens before the record lands", async () => {
104
+ const store = make();
105
+ const cp = createCheckpointFixture();
106
+ await store.put(cp.token, cp);
107
+ await store.resolve(cp.token, cp.scope, ALLOW);
108
+ assert.equal(await store.reopen(cp.token, cp.scope, "env_failed"), true);
109
+ assert.equal(await store.resolve(cp.token, cp.scope, ALLOW, { rev: 2 }), true);
110
+ assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "recorded");
111
+ assert.equal(await store.reopen(cp.token, cp.scope, "env_failed"), false);
112
+ assert.equal(await store.reopen(cp.token, cp.scope, "tool_unavailable"), false, "no reason reopens a recorded row");
113
+ const got = (await store.get(cp.token));
114
+ assert.equal(got.status, "resolved");
115
+ assert.equal(got.rev, 3, "a refused reopen bumps nothing");
116
+ assert.equal(got.reopenReason, undefined);
117
+ assert.deepEqual(got.executionOutcome, EXECUTED_ALLOW);
118
+ });
119
+ run("a row filed WITH an execution record (a restored resolved row) keeps it authoritative: get() returns it, reopen is refused, a different record conflicts, the same one is already_recorded", async () => {
120
+ const store = make();
121
+ const cp = createCheckpointFixture({ status: "resolved", rev: 1, executionOutcome: EXECUTED_ALLOW, executionAtMs: 1_700_000_001_000 });
122
+ await store.put(cp.token, cp);
123
+ const back = (await store.get(cp.token));
124
+ assert.deepEqual(back.executionOutcome, EXECUTED_ALLOW);
125
+ assert.equal(back.executionAtMs, 1_700_000_001_000);
126
+ assert.equal(await store.reopen(cp.token, cp.scope, "env_failed"), false, "a restored recorded row is as settled as a live one");
127
+ await assert.rejects(store.recordExecutionOutcome(cp.token, cp.scope, VETOED_BY_POLICY), (e) => e.code === "checkpoint.execution_outcome_conflict");
128
+ assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "already_recorded");
129
+ assert.equal((await store.get(cp.token)).status, "resolved");
130
+ });
46
131
  run("put create-once → already_exists", async () => {
47
132
  const store = make();
48
133
  const cp = createCheckpointFixture();
@@ -511,6 +511,30 @@ export type TraceEvent = {
511
511
  /** The boundary classification the admission asserted. */
512
512
  boundary: "sandbox_internal";
513
513
  ts: number;
514
+ } | {
515
+ /**
516
+ * The auto-mode classifier decided (or declined to decide) one pending ask — one frame per
517
+ * `decide` call, including the breaker-open short-circuit, so a consumer can read the gate's
518
+ * wall-clock cost per tool call and the failure cause behind an ask that reached a person. A
519
+ * TRACE frame rather than a task event: it fires once per gated call, it is an operator's read
520
+ * (why did that ask take four seconds / why did it fall back), and the session-level breaker
521
+ * read face a shell renders is a separate, additive wire face. `ms` is the wall time from the
522
+ * decide call to its verdict (timeout included: a timed-out round reads the deadline).
523
+ */
524
+ kind: "auto_mode.classified";
525
+ version: 1;
526
+ taskId: string;
527
+ /** The gated call the classifier judged. */
528
+ toolCallId: string;
529
+ /** The classifier model (the resolved `classifier` role, or the main model it fell back to). */
530
+ model: string;
531
+ /** Wall-clock milliseconds the gate waited on this decision (integer, ≥ 0). */
532
+ ms: number;
533
+ /** The verdict kind the gate acted on (`allow` / `block` / `unavailable` / `parse_error`). */
534
+ verdict: "allow" | "block" | "unavailable" | "parse_error";
535
+ /** Present iff `verdict === "unavailable"`: `error` / `timeout` / `breaker_open`. */
536
+ cause?: "error" | "timeout" | "breaker_open";
537
+ ts: number;
514
538
  } | {
515
539
  /** C1 — the failover brain served this call from a FALLBACK entry (`createFailoverBrain`): the
516
540
  * primary (and possibly earlier hops) failed cleanly upfront. Without this, same-model gateway
package/dist/index.d.ts CHANGED
@@ -98,7 +98,8 @@ export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY
98
98
  export { deploymentReadFaceClampNotice, resolveReadFace, type ReadFace, type ReadFaceInputs } from "./tools/fs/index.js";
99
99
  export { WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, compileWriteProtection, type WriteProtectedEntry, type WriteProtectedRow, type WriteProtectedKind, type WriteProtectedHit, type WriteProtectionMatcher, } from "./core/write-protect.js";
100
100
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, type ToolResultProvenance, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, type ToolResultStore, type ToolResultSlice, type ToolResultDeletionReport, } from "./core/tool-result-store.js";
101
- export { InMemoryCheckpointStore, CheckpointError, mintCheckpointId, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type ProbeCause, type ProbeCauseOperands, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type TerminalClaimIntent, type TerminalClaimOutcome, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, type CheckpointRow, realApprovalOrgFact, resolveCheckpointStore, } from "./core/checkpoint-store.js";
101
+ export { InMemoryCheckpointStore, CheckpointError, mintCheckpointId, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, type ActorAssertion, type PendingSteerEntry, type PendingSteerInput, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, type RiskDescriptor, type ProbeCause, type ProbeCauseOperands, type CheckpointStore, type CheckpointSummary, type Checkpoint, type CheckpointToken, type CheckpointGate, type CheckpointState, type SerializedCheckpointState, type CheckpointFaultMode, type PendingAction, type ResumeOutcome, type ResolvedOutcome, type ReopenReason, type ResolveExpectation, type TerminalClaimIntent, type TerminalClaimOutcome, type SafetyAxis, type RealApprovalGateBit, type ResourceLedger, type ResourceLimitReason, type PlatformLimitReason, type CheckpointRow, realApprovalOrgFact, resolveCheckpointStore, checkpointStoreExecutionUndeclared, } from "./core/checkpoint-store.js";
102
+ export { EXECUTION_OUTCOME_RECORD_WORDS, EXECUTION_RECORD_LEAVES_ROW_UNRECORDED, isExecutionOutcomeRecordWord, type ExecutionOutcomeRecordWord, type ExecutionRecordTableCoversEveryWord, type ExecutionRecordRowFacts, type ExecutionVerdict, ExecutionOutcomeConflictError, checkpointExecutionRecorded, executionRecordDisposition, sameExecutionRecord, executionVerdict, } from "./core/checkpoint-execution-record.js";
102
103
  export { PAUSE_REGISTRY, type GateKind, type PendingKind, type ResumeGate, isGateKind, resumeGateMatches, type CheckpointPause, type CheckpointFields, pauseOf, checkpointFrom } from "./core/pause-registry.js";
103
104
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, type UsageWindow, type UsageWindowStore, type UsageWindowReading, type UsageWindowRecord, type UsageSlot, type UsageBucketRow, } from "./core/usage-window-store.js";
104
105
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
@@ -152,8 +153,8 @@ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, create
152
153
  export { type AskOrigin, ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPROVAL } from "./core/ask-origin.js";
153
154
  export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, type GateDisposition, type GateOutcome, screenGateOutcome } from "./core/gate-outcome.js";
154
155
  export { type AskCarry } from "./core/hooks.js";
155
- export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitCounts, type DenialLimitFallbackFace, type UnarmedDenialLimitFallback, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
156
- export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS } from "./core/auto-mode-defaults.js";
156
+ export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassified, type AutoModeClassifyFn, type AutoModeClassifyInput, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, type AutoModeDenialTracker, type AutoModeDenialLimitOptions, type DenialLimitCounts, type DenialLimitFallbackFace, type UnarmedDenialLimitFallback, type DenialLimitFallback, type DenialLimitVerdict, } from "./core/auto-mode.js";
157
+ export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_CLASSIFIER_MAX_TOKENS } from "./core/auto-mode-defaults.js";
157
158
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
158
159
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
159
160
  export { AUTO_MODE_ARMING_RECIPE_VERSION, autoModeArmingRecipeOf, sanitizeAutoModeArmingRecipe, foldAutoModeArming, type AutoModeArmingRecipe, type AutoModeArmingFace, type AutoModeArmingFold, type AutoModeRebuildRefusal, } from "./core/auto-mode-arming.js";
@@ -209,7 +210,7 @@ export { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore
209
210
  export { stripEngineMetadata } from "./internal/llm.js";
210
211
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn, type CompiledEventPrompt, type CompiledMessageInjection, type EventDedupe, type EventDefaultPolicy } from "./prompt-assembly/event-registry.js";
211
212
  export type { CompiledSection, ComposedPrompt, PackSectionDeclaration, PromptCacheClass, PromptCarrier, PromptMutability, PromptOwner, PromptPack, PromptRenderCadence, PromptRuntimeFacts, PromptSlot, PromptTrust, SectionRenderInputs, } from "./prompt-assembly/types.js";
212
- export { type ReasoningIntensity, type ReasoningResolution, type ResolvedReasoning, type ReasoningFormat, DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, resolveBinary, reasoningBudgetShare, resolveReasoning, resolveReasoningProfile, type ReasoningTier, type ReasoningProfileFlags, } from "./brain/reasoning.js";
213
+ export { type ReasoningIntensity, type ReasoningResolution, type ResolvedReasoning, type ReasoningFormat, DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, type OffCapabilityModel, resolveReasoningProfile, type ReasoningTier, type ReasoningProfileFlags, } from "./brain/reasoning.js";
213
214
  export { DESIGN_REVIEW_PROMPTS, CODE_REVIEW_PROMPT, SCENARIO_REGISTRY, runScenario, type ScenarioId, type CodeReviewMode, type ScenarioProfile, type RunScenarioOptions, type RunScenarioResult, } from "./scenarios/scenario-registry.js";
214
215
  export { teacherMode, TEACHER_PROFILE, type TeacherModePair, type TeacherProfile, } from "./scenarios/teacher-quickstart.js";
215
216
  export { loadOrchestrationEnv, DEFAULT_REASONING_INTENSITY, type OrchestrationMode, type OrchestrationEnv, } from "./scenarios/env.js";
package/dist/index.js CHANGED
@@ -77,7 +77,8 @@ export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY
77
77
  export { deploymentReadFaceClampNotice, resolveReadFace } from "./tools/fs/index.js";
78
78
  export { WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, compileWriteProtection, } from "./core/write-protect.js";
79
79
  export { InMemoryToolResultStore, OFFLOAD_TOOL_NAME, DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, assertSafeToolResultRef, buildToolResultRef, toolResultContentSegment, MAX_MINTED_TOOL_RESULT_REF_CHARS, assertToolResultProvenanceMatch, normalizeToolResultProvenance, toolResultProvenanceOf, ToolResultRefConflictError, TOOL_RESULT_REF_CONFLICT_CODE, } from "./core/tool-result-store.js";
80
- export { InMemoryCheckpointStore, CheckpointError, mintCheckpointId, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, realApprovalOrgFact, resolveCheckpointStore, } from "./core/checkpoint-store.js";
80
+ export { InMemoryCheckpointStore, CheckpointError, mintCheckpointId, mintCheckpointToken, checkpointVersionOf, CURRENT_CHECKPOINT_VERSION, MAX_SUPPORTED_CHECKPOINT_VERSION, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, debitLedger, remainingBudgetMicroUsd, remainingTokens, winnerFromOutcome, validatePendingSteer, readPendingSteerQueue, appendPendingSteer, MAX_PENDING_STEER_CHARS, MAX_PENDING_STEER_ENTRIES, PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES, PENDING_STEER_FROZEN_FIELDS, ACTOR_ASSERTION_FROZEN_FIELDS, MAX_ACTOR_FIELD_CHARS, MAX_STEER_INPUT_ID_CHARS, LEGACY_PENDING_STEER_INPUT_ID, riskSeverity, buildRiskDescriptor, summarizeCheckpoint, realApprovalOrgFact, resolveCheckpointStore, checkpointStoreExecutionUndeclared, } from "./core/checkpoint-store.js";
81
+ export { EXECUTION_OUTCOME_RECORD_WORDS, EXECUTION_RECORD_LEAVES_ROW_UNRECORDED, isExecutionOutcomeRecordWord, ExecutionOutcomeConflictError, checkpointExecutionRecorded, executionRecordDisposition, sameExecutionRecord, executionVerdict, } from "./core/checkpoint-execution-record.js";
81
82
  export { PAUSE_REGISTRY, isGateKind, resumeGateMatches, pauseOf, checkpointFrom } from "./core/pause-registry.js";
82
83
  export { InMemoryUsageWindowStore, GLOBAL_USAGE_KEY, EMPTY_USAGE_WINDOW_RECORD, chargeUsageRecord, readUsageRecord, usageRetryAfterMs, resolveUsageWindows, } from "./core/usage-window-store.js";
83
84
  export { FileUsageWindowStore } from "./stores/file/usage-window-store.js";
@@ -127,7 +128,7 @@ export { ASK_ORIGINS, isAskOrigin, classifierMayAnswer, ORIGIN_IMPLIES_REAL_APPR
127
128
  export { SETTLEMENT_KINDS, isSettlementKind, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, isDeniedBy, DENIED_BY_MAY_VETO, screenGateOutcome } from "./core/gate-outcome.js";
128
129
  export {} from "./core/hooks.js";
129
130
  export { parseAutoModeResponse, createAutoModeDecider, createAutoModeDenialTracker, denialLimitFallbackMessage, denialLimitSentence, unarmedWindow, } from "./core/auto-mode.js";
130
- export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS } from "./core/auto-mode-defaults.js";
131
+ export { AUTO_MODE_DENIAL_LIMIT_DEFAULTS, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_CLASSIFIER_MAX_TOKENS } from "./core/auto-mode-defaults.js";
131
132
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
132
133
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
133
134
  export { AUTO_MODE_ARMING_RECIPE_VERSION, autoModeArmingRecipeOf, sanitizeAutoModeArmingRecipe, foldAutoModeArming, } from "./core/auto-mode-arming.js";
@@ -167,7 +168,7 @@ export { buildTurnPromptSnapshot, loweringRecordFor, LOWERING_VERSION, PREFIX_IN
167
168
  export { CenterPromptSource, FilePromptArtifactStore, FilePromptSourceStateStore, MemoryPromptArtifactStore, MemoryPromptSourceStateStore, } from "./prompt-assembly/artifact-store.js";
168
169
  export { stripEngineMetadata } from "./internal/llm.js";
169
170
  export { EVENT_PROMPT_REGISTRY, eventDefaultOn } from "./prompt-assembly/event-registry.js";
170
- export { DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, resolveBinary, reasoningBudgetShare, resolveReasoning, resolveReasoningProfile, } from "./brain/reasoning.js";
171
+ export { DEFAULT_EFFORT_LEVELS, REASONING_BUDGET_SHARE, isThinkingLevel, rankOf, resolveEffort, resolveBinary, reasoningBudgetShare, resolveReasoning, thinkingOffExpressible, declaredOffSpelling, resolveReasoningProfile, } from "./brain/reasoning.js";
171
172
  export { DESIGN_REVIEW_PROMPTS, CODE_REVIEW_PROMPT, SCENARIO_REGISTRY, runScenario, } from "./scenarios/scenario-registry.js";
172
173
  export { teacherMode, TEACHER_PROFILE, } from "./scenarios/teacher-quickstart.js";
173
174
  export { loadOrchestrationEnv, DEFAULT_REASONING_INTENSITY, } from "./scenarios/env.js";
@@ -1,4 +1,6 @@
1
+ import { type ExecutionOutcomeRecordWord } from "../../core/checkpoint-execution-record.js";
1
2
  import { type Checkpoint, type PendingSteerInput, type CheckpointFaultMode, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type ReopenReason, type ResolveExpectation, type ResumeOutcome } from "../../core/checkpoint-store.js";
3
+ import type { GateOutcome } from "../../core/gate-outcome.js";
2
4
  export interface FileCheckpointStoreOptions {
3
5
  /** When false, an `appendLine` for a state transition is NOT fsync'd. The checkpoint COMMIT POINT always
4
6
  * fsyncs regardless (its crash-safety depends on it); this governs the `put` and `setPendingSteer`
@@ -25,6 +27,10 @@ export declare class FileCheckpointStore implements CheckpointStore {
25
27
  readonly redecision: {
26
28
  readonly reopen: true;
27
29
  };
30
+ /** The execution-outcome record verb below is the contract's CAS (journaled), and `reopen` honours the record. */
31
+ readonly execution: {
32
+ readonly outcome: true;
33
+ };
28
34
  private readonly fsyncEnabled;
29
35
  private readonly compactEvery;
30
36
  /** RB-134: the directory's ONE authority (map + token mutex + append log), joined not rebuilt. */
@@ -50,6 +56,7 @@ export declare class FileCheckpointStore implements CheckpointStore {
50
56
  put(token: CheckpointToken, cp: Checkpoint): Promise<void>;
51
57
  get(token: CheckpointToken): Promise<Checkpoint | null>;
52
58
  resolve(token: CheckpointToken, scope: string, outcome: ResumeOutcome, expect?: ResolveExpectation): Promise<boolean>;
59
+ recordExecutionOutcome(token: CheckpointToken, scope: string, gate: GateOutcome): Promise<ExecutionOutcomeRecordWord>;
53
60
  reopen(token: CheckpointToken, scope: string, reason: ReopenReason): Promise<boolean>;
54
61
  setPendingSteer(token: CheckpointToken, scope: string, steer: PendingSteerInput): Promise<boolean>;
55
62
  expire(token: CheckpointToken, scope: string): Promise<boolean>;
@@ -1,11 +1,13 @@
1
1
  import { join } from "node:path";
2
2
  import { assertAdoptionBootGate } from "./adoption/marker.js";
3
+ import { checkpointExecutionRecorded, executionRecordDisposition, ExecutionOutcomeConflictError, } from "../../core/checkpoint-execution-record.js";
3
4
  import { appendPendingSteer, CheckpointError, checkpointOccMatches, checkpointRowMatches, summarizeCheckpoint, validatePendingSteer, winnerFromOutcome, } from "../../core/checkpoint-store.js";
4
5
  import { SharedLedgerTable } from "./shared-ledger.js";
5
6
  const CHECKPOINT_LEDGER_EVENT_REGISTRY = {
6
7
  put: true,
7
8
  resolve: true,
8
9
  reopen: true,
10
+ execution: true,
9
11
  expire: true,
10
12
  steer: true,
11
13
  steer_append: true,
@@ -36,6 +38,10 @@ function applyCheckpointEvent(cps, ev) {
36
38
  cp.rev = ev.rev;
37
39
  cp.reopenReason = ev.reason;
38
40
  break;
41
+ case "execution":
42
+ cp.executionOutcome = ev.gate;
43
+ cp.executionAtMs = ev.atMs;
44
+ break;
39
45
  case "expire":
40
46
  cp.status = "expired";
41
47
  break;
@@ -64,6 +70,7 @@ export class FileCheckpointStore {
64
70
  durability = "durable";
65
71
  fidelity = "json";
66
72
  redecision = { reopen: true };
73
+ execution = { outcome: true };
67
74
  fsyncEnabled;
68
75
  compactEvery;
69
76
  ledger;
@@ -126,10 +133,22 @@ export class FileCheckpointStore {
126
133
  return true;
127
134
  });
128
135
  }
136
+ async recordExecutionOutcome(token, scope, gate) {
137
+ return this.withLock(token, () => {
138
+ const cp = this.cps.get(token);
139
+ const disposition = executionRecordDisposition(cp, scope, gate);
140
+ if (disposition === "conflict")
141
+ throw new ExecutionOutcomeConflictError(cp.executionOutcome, gate);
142
+ if (disposition !== "commit")
143
+ return disposition;
144
+ this.commit({ t: "execution", token, gate: structuredClone(gate), atMs: Date.now() }, true);
145
+ return "recorded";
146
+ });
147
+ }
129
148
  async reopen(token, scope, reason) {
130
149
  return this.withLock(token, () => {
131
150
  const cp = this.cps.get(token);
132
- if (!checkpointRowMatches(cp, scope, "resolved")) {
151
+ if (!checkpointRowMatches(cp, scope, "resolved") || checkpointExecutionRecorded(cp)) {
133
152
  return false;
134
153
  }
135
154
  const rev = (cp.rev ?? 0) + 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.6.3",
3
+ "version": "7.7.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",