@sema-agent/core 5.60.0 → 5.61.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.
- package/CHANGELOG.md +79 -0
- package/dist/agents/subagent.d.ts +4 -2
- package/dist/agents/subagent.js +9 -9
- package/dist/core/checkpoint-store.d.ts +56 -6
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -2
- package/dist/core/mcp.d.ts +4 -5
- package/dist/core/memory-engine/consolidation-driver.d.ts +19 -1
- package/dist/core/memory-engine/consolidation-driver.js +75 -3
- package/dist/core/memory-engine/consolidation.d.ts +52 -5
- package/dist/core/memory-engine/consolidation.js +3 -1
- package/dist/core/memory-engine/distiller.d.ts +89 -1
- package/dist/core/memory-engine/distiller.js +94 -5
- package/dist/core/memory-engine/engine.d.ts +13 -6
- package/dist/core/memory-engine/engine.js +51 -8
- package/dist/core/memory-engine/index.d.ts +1 -1
- package/dist/core/memory-engine/index.js +1 -1
- package/dist/core/runner/prepare-task.d.ts +6 -3
- package/dist/core/runner/prepare-task.js +1 -0
- package/dist/core/runner/runtask.js +113 -31
- package/dist/core/task-notification.d.ts +50 -23
- package/dist/core/task-notification.js +20 -4
- package/dist/core/tool-policy.d.ts +8 -2
- package/dist/core/types.d.ts +76 -19
- package/dist/engine/harness/agent-harness.d.ts +58 -2
- package/dist/engine/harness/agent-harness.js +115 -5
- package/dist/engine/loop/agent-loop.js +153 -15
- package/dist/engine/loop/types.d.ts +32 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +7 -2
- package/dist/orchestration/run-workflow-tool.js +1 -1
- package/dist/tools/monitor.d.ts +3 -3
- package/dist/tools/monitor.js +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.61.0 — 2026-08-25
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Injection-priority ladder is LIVE (design/373 S1+S2)**: `now`/`next`/`later` now carry three
|
|
7
|
+
distinct delivery semantics (CC 2.1.223 form). `next` = the running turn's next boundary (the
|
|
8
|
+
previous behavior of every frame — byte-identical for `next`); `later` = the run's
|
|
9
|
+
would-otherwise-stop seat (its own closing turn; non-natural endings re-pend per session, the next
|
|
10
|
+
run's turn-open delivers); `now` = `next`'s guarantee plus a manufactured boundary. A
|
|
11
|
+
caller-provenance `TaskStream.steer(text, { priority: "now" })` CUTS the in-flight turn at a
|
|
12
|
+
reconciled boundary (finished tools keep real results; never-started ones settle as paired
|
|
13
|
+
`[INTERRUPTED]` results with `details.errorKind: "interrupted_never_started"`; the CC-verbatim
|
|
14
|
+
interruption marker lands) and the run CONTINUES; every real cut emits the new
|
|
15
|
+
`task.turn_interrupted` notice (audience `user`) and counts toward `maxTurns`. Interrupt authority
|
|
16
|
+
is the steer face's alone.
|
|
17
|
+
- `TaskStream.steer` gains `options.priority` (default `"next"`); the replay identity (`inputId`)
|
|
18
|
+
gains the normalized tier as an axis (same id+text at a different tier ⇒ `steering.duplicate_input_id`).
|
|
19
|
+
- `task_notification` wire events gain an additive `priority` field (live + turn-open lanes; absent =
|
|
20
|
+
pre-373 producer or tier-unknown park — never read absence as `later`).
|
|
21
|
+
- `isTerminalTaskNotification` package-root export; engine-note backlog cap gains terminal preference
|
|
22
|
+
(#445): at a full cap an INTERNAL-lane terminal frame displaces the oldest event batch (which parks
|
|
23
|
+
losslessly) instead of parking behind it — external frames of every status stay ordinary
|
|
24
|
+
displaceable notes (trust cut at the marking site).
|
|
25
|
+
- Engine-layer seams: per-turn AbortController + `interruptTurn(frame)` (consumption guard; run
|
|
26
|
+
signal wins), pre-request immediate re-check, final commit point (a frame arriving during the
|
|
27
|
+
stop-gate await window is served by the same run), `AgentHarness.steer` returns the minted frame.
|
|
28
|
+
- **Product-grade origin folding behind a host-code attestation gate (design/376-C1)**:
|
|
29
|
+
`freezeConsolidationPlan` grows the attested arm — `proposal.mintExposure: "partitioned"` (closed
|
|
30
|
+
single-member set; non-members refuse structurally at the commit door) switches the fold from the
|
|
31
|
+
run-level single value to a per-product committed-exposure re-read over declared inputs (any marked
|
|
32
|
+
input ⇒ external/derived, all-clean ⇒ no origin); every UN-attested plan — every pre-C1 caller and
|
|
33
|
+
every foreign minter — keeps the run-level blanket byte-identical. `mintExposurePartitionedPlan`
|
|
34
|
+
+ `LlmConsolidationPlanArm` exported: the exposure-partitioned mint bisects candidates on the
|
|
35
|
+
engine-minted marked flag (clean arm byte-identical to the historical chain; marked arm mints only
|
|
36
|
+
at markedCount ≥ 2; a lone marked row stands as its handle row, withheld from every call); the
|
|
37
|
+
attestation threads verbatim distiller → driver → commit. D-1b two-layer world-movement guard:
|
|
38
|
+
the engine re-checks every served candidate on four axes (rev moved / left listing / challenge-latch
|
|
39
|
+
/ turned marked) and refuses the whole plan `_stale_snapshot`; the driver's cross-cycle roster guard
|
|
40
|
+
discards a stale mint cache and settles under the NEW stop reason `mint_invalidated`
|
|
41
|
+
(closed-set add; committed cycles stand add-only). Plan rows gain `markedProducts` (additive);
|
|
42
|
+
snapshot gate rows record `markedIds` (additive); the instruction-form gate grows the D-5 three-arm
|
|
43
|
+
form (attested all-clean products follow C-3's clean arm — default refusal + the
|
|
44
|
+
`allowInstructionProducts` hatch, a named WIDENING vs the pre-C1 marked-set blanket; a marked-input
|
|
45
|
+
product keeps the unconditional hard arm).
|
|
46
|
+
|
|
47
|
+
### BREAKING
|
|
48
|
+
- **B1** — `notify()` frames with `priority: "later"` (INCLUDING the parameterless default) moved
|
|
49
|
+
from next-boundary delivery to the run's closing seat / the next run's turn-open. Honestly noted:
|
|
50
|
+
in the flat era `later` was silently inert (no runtime signal said so) — the engine version is the
|
|
51
|
+
only discriminator. Callers needing boundary delivery must pass `priority: "next"` explicitly.
|
|
52
|
+
- **B2** — `notify(…, { priority: "now" })` is now a typed refusal (`notify.invalid_priority`);
|
|
53
|
+
previously it delivered with a one-per-run disclosure notice.
|
|
54
|
+
- **B3** — the `task.injection_priority_unimplemented` notice code is RETIRED (catalog + audience row
|
|
55
|
+
+ word table). Consumers must judge ladder support by version, never by the code's absence.
|
|
56
|
+
- Closed-set adds, named: `ENGINE_NOTICE_CODES` +`task.turn_interrupted`; notify error family
|
|
57
|
+
+`notify.invalid_priority`; consolidation run stop reasons +`mint_invalidated`. Internal producer
|
|
58
|
+
tiers re-seated: subagent terminal frames ×9 and monitor (both shapes) `later`→`next` (#445
|
|
59
|
+
ALIGNED), workflow terminals now explicit `next`.
|
|
60
|
+
|
|
61
|
+
## 5.60.1 — 2026-08-25
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
- **Resume-leg usage-governance holes (#449, P0/P1)**: a resume no longer burns a full model turn
|
|
65
|
+
when the usage window is exhausted — an owed row (approval/review decision, wake, parked steer,
|
|
66
|
+
bg snapshot) refuses PRE-CAS with the new `resume.usage_window_exhausted` CheckpointError
|
|
67
|
+
(`detail.retryAfterMs`; the row stays pending, zero consumption, the same token redeems after the
|
|
68
|
+
window); a bare resource_limit continue re-suspends at turn 0 through the same platform-limit leg
|
|
69
|
+
the turn boundary uses (fresh checkpoint, new `resumeAfterMs`, zero model calls). The resume
|
|
70
|
+
ledger key now falls back to the checkpoint-pinned principal (`resource_limit` mints stamp
|
|
71
|
+
`cp.principal`) — a caller that omits principal no longer silently charges the GLOBAL bucket and
|
|
72
|
+
collects a fresh quota; supplying a DIFFERENT principal refuses loudly
|
|
73
|
+
(`resume.principal_mismatch`, pre-CAS, row pending). NARROWING, named: mismatched-principal
|
|
74
|
+
resumes previously proceeded under the wrong identity.
|
|
75
|
+
- **covering-ask audit arm (#450, core half)**: the prepare-time policy-name audit gains ask-slot
|
|
76
|
+
coverage pins (the exemption for `mcp__<server>__*` spellings now proves it speaks the truth
|
|
77
|
+
against a live in-engine ask lane); `isNamespacedCoveringRuleName`/`namespacedRuleNameCovers`
|
|
78
|
+
root-exported (additive) so out-of-engine name-set publishers can self-check the covering grammar
|
|
79
|
+
(the fifth-publisher fail-open shape stays pinned as-is until the server-side consumer lands its
|
|
80
|
+
fix). Export surface 1758 → 1760.
|
|
81
|
+
|
|
3
82
|
## 5.60.0 — 2026-08-25
|
|
4
83
|
|
|
5
84
|
### Added
|
|
@@ -511,8 +511,10 @@ export interface SubagentToolOptions {
|
|
|
511
511
|
/**
|
|
512
512
|
* design/115 P3 — background sub-agents (CC `run_in_background`). When set, the tool exposes the
|
|
513
513
|
* `run_in_background` parameter: the call returns an `a*` task_id immediately, the child runs
|
|
514
|
-
* asynchronously, and completion fires ONE task-notification ("
|
|
515
|
-
*
|
|
514
|
+
* asynchronously, and completion fires ONE task-notification ("next" priority — design/373 §3.7 census
|
|
515
|
+
* re-seat, upstream-anchored: every background-task terminal is explicitly "next" there; the earlier
|
|
516
|
+
* "later doesn't derail active work" reading was the flat era's — a completion must reach a busy model
|
|
517
|
+
* at the boundary, not at the run's closing seat). Poll/stop via TaskOutput/TaskStop. Absent ⇒ synchronous only.
|
|
516
518
|
*/
|
|
517
519
|
background?: {
|
|
518
520
|
registry: import("../core/task-registry.js").TaskRegistry;
|
package/dist/agents/subagent.js
CHANGED
|
@@ -775,7 +775,7 @@ export function createSubagentResume(deps) {
|
|
|
775
775
|
}
|
|
776
776
|
else if (ownsTerminalFacesRevive) {
|
|
777
777
|
try {
|
|
778
|
-
deps.notify?.(resumeFrame, { priority: "
|
|
778
|
+
deps.notify?.(resumeFrame, { priority: "next" });
|
|
779
779
|
}
|
|
780
780
|
catch {
|
|
781
781
|
}
|
|
@@ -829,7 +829,7 @@ export function createSubagentResume(deps) {
|
|
|
829
829
|
...(abort.signal.aborted ? {} : { error: msgRevive.slice(0, REPORT_FIELD_MAX) }),
|
|
830
830
|
...(!abort.signal.aborted && errCodeReviveReject !== undefined ? { errorCode: errCodeReviveReject } : {}),
|
|
831
831
|
...(completionIdRejectRevive !== undefined ? { completionId: completionIdRejectRevive } : {}),
|
|
832
|
-
}, { priority: "
|
|
832
|
+
}, { priority: "next" });
|
|
833
833
|
}
|
|
834
834
|
catch {
|
|
835
835
|
}
|
|
@@ -862,7 +862,7 @@ function parkCompletionNotify(deps) {
|
|
|
862
862
|
...payload,
|
|
863
863
|
summary: `${payload.summary} (delivered after ${pendingCount} background ${pendingCount === 1 ? "child" : "children"} settled)`,
|
|
864
864
|
}
|
|
865
|
-
: payload, { priority: "
|
|
865
|
+
: payload, { priority: "next" });
|
|
866
866
|
}
|
|
867
867
|
catch {
|
|
868
868
|
}
|
|
@@ -2387,7 +2387,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2387
2387
|
sessionId: forkedId,
|
|
2388
2388
|
resumable: false,
|
|
2389
2389
|
...(completionIdReap !== undefined ? { completionId: completionIdReap } : {}),
|
|
2390
|
-
}, { priority: "
|
|
2390
|
+
}, { priority: "next" }), "subagent.reapTerminalNotify");
|
|
2391
2391
|
});
|
|
2392
2392
|
if (agentName !== undefined) {
|
|
2393
2393
|
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, sessionId: forkedId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...((typeof childModel === "string" ? resolveModelDisplayLabel(childModel) : childModel?.id) !== undefined ? { model: typeof childModel === "string" ? childModel : childModel?.id } : {}), ...(modelFallback !== undefined ? { modelFallback } : {}), ...((ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}), ...(sessionScopedBg ? { sessionScoped: true } : {}), createdAt: Date.now() }, (err) => opts.onObserverError?.(err, { site: "roster.recordSpawn" }));
|
|
@@ -2588,7 +2588,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2588
2588
|
resumable: resumableFork,
|
|
2589
2589
|
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
2590
2590
|
...(completionIdFork !== undefined ? { completionId: completionIdFork } : {}),
|
|
2591
|
-
}, { priority: "
|
|
2591
|
+
}, { priority: "next" });
|
|
2592
2592
|
}
|
|
2593
2593
|
catch {
|
|
2594
2594
|
}
|
|
@@ -2639,7 +2639,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2639
2639
|
...(settledBg === "failed" ? { error: msgFork.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
2640
2640
|
...(settledBg === "failed" && errCodeForkReject !== undefined ? { errorCode: errCodeForkReject } : {}),
|
|
2641
2641
|
...(completionIdForkReject !== undefined ? { completionId: completionIdForkReject } : {}),
|
|
2642
|
-
}, { priority: "
|
|
2642
|
+
}, { priority: "next" });
|
|
2643
2643
|
}
|
|
2644
2644
|
catch {
|
|
2645
2645
|
}
|
|
@@ -2967,7 +2967,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2967
2967
|
sessionId: bgChildSessionId,
|
|
2968
2968
|
resumable: false,
|
|
2969
2969
|
...(completionIdReap !== undefined ? { completionId: completionIdReap } : {}),
|
|
2970
|
-
}, { priority: "
|
|
2970
|
+
}, { priority: "next" }), "subagent.reapTerminalNotify");
|
|
2971
2971
|
});
|
|
2972
2972
|
const bgDurableProbe = bg.registry.durableAgentRowProbe(taskId);
|
|
2973
2973
|
const bgRowConfirmed = async () => (bgDurableProbe ? await bgDurableProbe().catch(() => false) : false);
|
|
@@ -3438,7 +3438,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3438
3438
|
}
|
|
3439
3439
|
else if (ownsTerminalFaces) {
|
|
3440
3440
|
try {
|
|
3441
|
-
notify?.(completionFrame, { priority: "
|
|
3441
|
+
notify?.(completionFrame, { priority: "next" });
|
|
3442
3442
|
}
|
|
3443
3443
|
catch {
|
|
3444
3444
|
}
|
|
@@ -3508,7 +3508,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3508
3508
|
...(settled === "failed" ? { error: msg.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
3509
3509
|
...(settled === "failed" && errCodeBgReject !== undefined ? { errorCode: errCodeBgReject } : {}),
|
|
3510
3510
|
...(completionIdBgReject !== undefined ? { completionId: completionIdBgReject } : {}),
|
|
3511
|
-
}, { priority: "
|
|
3511
|
+
}, { priority: "next" });
|
|
3512
3512
|
}
|
|
3513
3513
|
catch {
|
|
3514
3514
|
}
|
|
@@ -1263,7 +1263,7 @@ export declare const FACE_CHECKPOINT_VERSION = 9;
|
|
|
1263
1263
|
* (binding human/irreversible_ask), v4 (a pre-164 row, refused only when it carries the retired
|
|
1264
1264
|
* wall-clock allocation), v5 (token-allocation-bearing resource/approval), v6 (org-admission freeze
|
|
1265
1265
|
* state), v7 (F-012 constraint-chain / delegation-provenance enforcement state), v8 (a
|
|
1266
|
-
* non-budgetable `realApproval` gate bit with its org-origin resume semantics)
|
|
1266
|
+
* non-budgetable `realApproval` gate bit with its org-origin resume semantics), v9 (the
|
|
1267
1267
|
* design/199 read-face section: resolved face + deny-set additions, folded stricter-wins on
|
|
1268
1268
|
* resume). */
|
|
1269
1269
|
export declare const MAX_SUPPORTED_CHECKPOINT_VERSION = 9;
|
|
@@ -1551,11 +1551,21 @@ export interface Checkpoint {
|
|
|
1551
1551
|
sourceTaskId?: string;
|
|
1552
1552
|
/**
|
|
1553
1553
|
* design S1d (source-tag persistence): the issuing task's authenticated end-user {@link
|
|
1554
|
-
* import("./types.js").TaskSpec.principal} (design/62), stamped at the APPROVAL suspend mint
|
|
1555
|
-
*
|
|
1556
|
-
* import("./tool-policy.js").AskRequest}.principal. Caller-set
|
|
1557
|
-
* {@link sourceTaskId} it is **
|
|
1558
|
-
* reads it
|
|
1554
|
+
* import("./types.js").TaskSpec.principal} (design/62), stamped at the APPROVAL suspend mint — and, since
|
|
1555
|
+
* #449 G2, at the `resource_limit` mint too — so an aggregating inbox can attribute a paused checkpoint
|
|
1556
|
+
* per-user — the durable analog of {@link import("./tool-policy.js").AskRequest}.principal. Caller-set
|
|
1557
|
+
* (never a tool/worker arg), and like {@link sourceTaskId} it is **NEVER a gate input**: no gate / CAS /
|
|
1558
|
+
* winner path reads it. The RESUME ENTRY however does read it as the row's identity record (#449 G2):
|
|
1559
|
+
* a resume config that omits `principal` inherits this value for the resumed leg (usage-ledger bucket,
|
|
1560
|
+
* later mints' scope derivation, ask attribution — a dropped field must not silently move the leg onto
|
|
1561
|
+
* the SHARED usage bucket), and a resume config carrying a DIFFERENT non-empty principal is refused
|
|
1562
|
+
* pre-CAS (`resume.principal_mismatch`). Absent ⇒ no principal (a legacy checkpoint, an unauthenticated
|
|
1563
|
+
* task, or a gate kind that predates the stamp). Rolling-deployment note (#449 G2 residual): the field
|
|
1564
|
+
* is NOT version-gated — an older worker accepts a principal-bearing row and keeps its historic
|
|
1565
|
+
* behavior (no fallback, no mismatch refusal); the identity-continuity guarantee holds once the fleet
|
|
1566
|
+
* is on enforcing workers. Version-gating it is a recorded design candidate: the lattice's newest rung
|
|
1567
|
+
* (design/199) encodes "v>=9 ⟺ face section present", so ANY new version above it must rewrite that
|
|
1568
|
+
* guard's window form and add its own presence invariant — a lattice-wide change, not a stamp.
|
|
1559
1569
|
*/
|
|
1560
1570
|
principal?: string;
|
|
1561
1571
|
/**
|
|
@@ -1812,6 +1822,23 @@ export declare class CheckpointError extends Error {
|
|
|
1812
1822
|
* thinner (or different) ancestor chain than it suspended with. Rejected pre-CAS (the checkpoint
|
|
1813
1823
|
* stays `pending`); re-resume with the full original chain. */
|
|
1814
1824
|
| "resume.parent_constraint_mismatch"
|
|
1825
|
+
/** #449 G1 (codex r2, pre-CAS entrance): a deployment usage-governance window for this resume's
|
|
1826
|
+
* ledger key (the config's principal, else the row's recorded one, else the shared bucket) is
|
|
1827
|
+
* EXHAUSTED, and the row still OWES a delivery a re-mint cannot carry (an approval/review
|
|
1828
|
+
* decision, a wake, parked steers, a background-task obituary snapshot). Refused BEFORE the CAS,
|
|
1829
|
+
* so nothing is consumed and nothing is unpinned — the SAME token with the SAME decision is
|
|
1830
|
+
* redeemable once the window frees (`detail.retryAfterMs` carries the wait). A bare
|
|
1831
|
+
* `resource_limit` continue with nothing parked does NOT take this refusal: it proceeds and
|
|
1832
|
+
* re-suspends at turn 0 into a FRESH checkpoint carrying the new wait hint. */
|
|
1833
|
+
| "resume.usage_window_exhausted"
|
|
1834
|
+
/** #449 G2 (identity continuity): the resume config carries a NON-EMPTY `principal` that differs from
|
|
1835
|
+
* the {@link Checkpoint.principal} recorded at the suspend mint. `ResumeTaskConfig`'s contract is
|
|
1836
|
+
* "the same TaskSpec the run suspended with", and the principal keys real lanes on the resumed leg
|
|
1837
|
+
* (the usage-governance ledger bucket, later mints' scope derivation, session-rule reads, ask
|
|
1838
|
+
* attribution) — silently running the leg under a different identity is the bad-value direction this
|
|
1839
|
+
* refusal exists to close. Rejected PRE-CAS (the checkpoint stays `pending`); re-resume with the
|
|
1840
|
+
* original principal, or omit the field to inherit the recorded one. */
|
|
1841
|
+
| "resume.principal_mismatch"
|
|
1815
1842
|
/** F-012 L3: the resume carried an approver EDIT (`updatedInput`) and a FROZEN inherited ancestor
|
|
1816
1843
|
* constraint (the persisted projection chain) — or the deployment's resume-edit policy — DENIES the
|
|
1817
1844
|
* edited args. Refused PRE-CAS with fresh-redecision semantics: the checkpoint stays `pending` and
|
|
@@ -1839,6 +1866,9 @@ export declare class CheckpointError extends Error {
|
|
|
1839
1866
|
* discriminant. */
|
|
1840
1867
|
readonly detail?: {
|
|
1841
1868
|
field?: "boundCallId" | "boundInputHash" | "answer" | "settledBy" | "approver";
|
|
1869
|
+
/** #449 G1 — on `resume.usage_window_exhausted` ONLY: how long until the binding window frees
|
|
1870
|
+
* (the same wait a suspended row carries as `gate.resumeAfterMs`). Absent on every other code. */
|
|
1871
|
+
retryAfterMs?: number;
|
|
1842
1872
|
/** WHICH pre-CAS refusal arm fired, where one `code` covers several (requested 2026-08-10: a
|
|
1843
1873
|
* deployment retry policy needs to tell "a newer worker can redeem this row" from "this row is
|
|
1844
1874
|
* damaged/caller-declared and no worker ever will" — blanket-retriable and gate-shape heuristics were
|
|
@@ -1941,6 +1971,23 @@ export declare class CheckpointError extends Error {
|
|
|
1941
1971
|
* thinner (or different) ancestor chain than it suspended with. Rejected pre-CAS (the checkpoint
|
|
1942
1972
|
* stays `pending`); re-resume with the full original chain. */
|
|
1943
1973
|
| "resume.parent_constraint_mismatch"
|
|
1974
|
+
/** #449 G1 (codex r2, pre-CAS entrance): a deployment usage-governance window for this resume's
|
|
1975
|
+
* ledger key (the config's principal, else the row's recorded one, else the shared bucket) is
|
|
1976
|
+
* EXHAUSTED, and the row still OWES a delivery a re-mint cannot carry (an approval/review
|
|
1977
|
+
* decision, a wake, parked steers, a background-task obituary snapshot). Refused BEFORE the CAS,
|
|
1978
|
+
* so nothing is consumed and nothing is unpinned — the SAME token with the SAME decision is
|
|
1979
|
+
* redeemable once the window frees (`detail.retryAfterMs` carries the wait). A bare
|
|
1980
|
+
* `resource_limit` continue with nothing parked does NOT take this refusal: it proceeds and
|
|
1981
|
+
* re-suspends at turn 0 into a FRESH checkpoint carrying the new wait hint. */
|
|
1982
|
+
| "resume.usage_window_exhausted"
|
|
1983
|
+
/** #449 G2 (identity continuity): the resume config carries a NON-EMPTY `principal` that differs from
|
|
1984
|
+
* the {@link Checkpoint.principal} recorded at the suspend mint. `ResumeTaskConfig`'s contract is
|
|
1985
|
+
* "the same TaskSpec the run suspended with", and the principal keys real lanes on the resumed leg
|
|
1986
|
+
* (the usage-governance ledger bucket, later mints' scope derivation, session-rule reads, ask
|
|
1987
|
+
* attribution) — silently running the leg under a different identity is the bad-value direction this
|
|
1988
|
+
* refusal exists to close. Rejected PRE-CAS (the checkpoint stays `pending`); re-resume with the
|
|
1989
|
+
* original principal, or omit the field to inherit the recorded one. */
|
|
1990
|
+
| "resume.principal_mismatch"
|
|
1944
1991
|
/** F-012 L3: the resume carried an approver EDIT (`updatedInput`) and a FROZEN inherited ancestor
|
|
1945
1992
|
* constraint (the persisted projection chain) — or the deployment's resume-edit policy — DENIES the
|
|
1946
1993
|
* edited args. Refused PRE-CAS with fresh-redecision semantics: the checkpoint stays `pending` and
|
|
@@ -1968,6 +2015,9 @@ export declare class CheckpointError extends Error {
|
|
|
1968
2015
|
* discriminant. */
|
|
1969
2016
|
detail?: {
|
|
1970
2017
|
field?: "boundCallId" | "boundInputHash" | "answer" | "settledBy" | "approver";
|
|
2018
|
+
/** #449 G1 — on `resume.usage_window_exhausted` ONLY: how long until the binding window frees
|
|
2019
|
+
* (the same wait a suspended row carries as `gate.resumeAfterMs`). Absent on every other code. */
|
|
2020
|
+
retryAfterMs?: number;
|
|
1971
2021
|
/** WHICH pre-CAS refusal arm fired, where one `code` covers several (requested 2026-08-10: a
|
|
1972
2022
|
* deployment retry policy needs to tell "a newer worker can redeem this row" from "this row is
|
|
1973
2023
|
* damaged/caller-declared and no worker ever will" — blanket-retriable and gate-shape heuristics were
|
|
@@ -93,7 +93,7 @@ export type NoticeAudience = "user" | "operator";
|
|
|
93
93
|
* src/ for notice mint shapes and names any code that is minted but unregistered, or registered but
|
|
94
94
|
* no longer minted.
|
|
95
95
|
*/
|
|
96
|
-
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.read_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "delegation.transcript_integrity", "mcp.revocation_probe_failed", "workflow.governance_key_stripped", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "route.fallback_to_primary", "route.base_url_changed_key_unchanged", "task.user_steer_undrained", "task.user_followup_undrained", "task.
|
|
96
|
+
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.read_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "delegation.transcript_integrity", "mcp.revocation_probe_failed", "workflow.governance_key_stripped", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "route.fallback_to_primary", "route.base_url_changed_key_unchanged", "task.user_steer_undrained", "task.user_followup_undrained", "task.turn_interrupted", "tool_result.offload_put_failed"];
|
|
97
97
|
/** A code this engine mints (see {@link ENGINE_NOTICE_CODES}). NOT the type of
|
|
98
98
|
* `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
|
|
99
99
|
* sink is a supported shape, and narrowing that field would break it. */
|
|
@@ -113,7 +113,7 @@ export const ENGINE_NOTICE_CODES = [
|
|
|
113
113
|
"route.base_url_changed_key_unchanged",
|
|
114
114
|
"task.user_steer_undrained",
|
|
115
115
|
"task.user_followup_undrained",
|
|
116
|
-
"task.
|
|
116
|
+
"task.turn_interrupted",
|
|
117
117
|
"tool_result.offload_put_failed",
|
|
118
118
|
];
|
|
119
119
|
const NOTICE_AUDIENCE_TABLE = {
|
|
@@ -125,6 +125,7 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
125
125
|
"memory.hold_disposed": "user",
|
|
126
126
|
"task.user_steer_undrained": "user",
|
|
127
127
|
"task.user_followup_undrained": "user",
|
|
128
|
+
"task.turn_interrupted": "user",
|
|
128
129
|
"config.autocompact_window_clamped": "operator",
|
|
129
130
|
"config.env_timeout_discarded": "operator",
|
|
130
131
|
"config.materialize_env_discarded": "operator",
|
|
@@ -144,7 +145,6 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
144
145
|
"memory.consolidation_refused": "operator",
|
|
145
146
|
"route.fallback_to_primary": "operator",
|
|
146
147
|
"route.base_url_changed_key_unchanged": "operator",
|
|
147
|
-
"task.injection_priority_unimplemented": "operator",
|
|
148
148
|
"tool_result.offload_put_failed": "operator",
|
|
149
149
|
};
|
|
150
150
|
export const NOTICE_AUDIENCE = NOTICE_AUDIENCE_TABLE;
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -177,11 +177,10 @@ export interface MaterializedMcp {
|
|
|
177
177
|
* fails the WHOLE model request (provider 400), bricking the task for every healthy tool
|
|
178
178
|
* (CC 2.1.216 ships the same intake prune, `tengu_mcp_drop_invalid_tool_schemas`).
|
|
179
179
|
*
|
|
180
|
-
* HONEST REACH (pinned in mcp-dropped-tools.test.ts):
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* transport/SDK revision delivers schemas the client does not fully validate.
|
|
180
|
+
* HONEST REACH (pinned in mcp-dropped-tools.test.ts): listings arrive through the lenient reader
|
|
181
|
+
* (`listToolsLenient` — raw request, no SDK-client zod pass), so a string-root or otherwise odd
|
|
182
|
+
* schema DOES reach this gate and is judged per tool; healthy siblings survive. This per-tool gate
|
|
183
|
+
* is therefore the PRIMARY adjudicator for schema shape, not a fallback behind SDK validation.
|
|
185
184
|
*
|
|
186
185
|
* The drop is NEVER silent, two faces (CC parity — its `mcp_dropped_tools_delta` system reminder):
|
|
187
186
|
* - operator: prepare-task forwards each entry to `onError(phase:"mcp")` alongside `warnings`;
|
|
@@ -56,11 +56,24 @@ export interface ConsolidationDriverRunRow {
|
|
|
56
56
|
outcome?: ConsolidationRunStopReason;
|
|
57
57
|
settledAt?: number;
|
|
58
58
|
stopDetail?: string;
|
|
59
|
-
/** The minted plan's replayable half (mint ONCE per run; each cycle filters, never re-mints).
|
|
59
|
+
/** The minted plan's replayable half (mint ONCE per run; each cycle filters, never re-mints).
|
|
60
|
+
* design/376-C1: `mintExposure` + `servedCandidates` ride the cache — the attestation threads
|
|
61
|
+
* onto every cycle's proposal, the roster feeds the cross-cycle invalidation predicate. Rows
|
|
62
|
+
* cached before the fields existed replay UN-attested with no roster check (run-level fold —
|
|
63
|
+
* exactly the treatment their whole-library prompts earned). */
|
|
60
64
|
planCache?: {
|
|
61
65
|
maxInputsPerProduct: number;
|
|
62
66
|
products: LlmConsolidationPlanProduct[];
|
|
67
|
+
mintExposure?: "partitioned";
|
|
68
|
+
servedCandidates?: Array<{
|
|
69
|
+
id: string;
|
|
70
|
+
rev: string;
|
|
71
|
+
marked: boolean;
|
|
72
|
+
}>;
|
|
63
73
|
};
|
|
74
|
+
/** design/376-C1 A-5 disclosure — marked candidates withheld from the clean arm's prompts at
|
|
75
|
+
* mint time (present ⇔ the partition engaged over a marked library). */
|
|
76
|
+
withheldFromCleanArm?: number;
|
|
64
77
|
/** The archived mint transcript's file name under {@link CONSOLIDATION_DRIVER_PLANS_DIR}. */
|
|
65
78
|
planArchive?: string;
|
|
66
79
|
/** COMMITTED cycles only (the store moved): a refused/fuse-blocked/zero-delta cycle row does
|
|
@@ -181,6 +194,11 @@ export interface ConsolidationRunReceipt {
|
|
|
181
194
|
name: string;
|
|
182
195
|
targets: number;
|
|
183
196
|
}>;
|
|
197
|
+
/** design/376-C1 A-5 disclosure — marked candidates withheld from the clean arm's prompts
|
|
198
|
+
* (present ⇔ the partitioned mint engaged over a marked library). A host reading a clean
|
|
199
|
+
* product's silence about some fact key should know the current value may sit in a withheld
|
|
200
|
+
* (marked, handle-faced) row. */
|
|
201
|
+
withheldFromCleanArm?: number;
|
|
184
202
|
planArchive?: string;
|
|
185
203
|
/** Advisory notices minted for this run (the `_incomplete` disclosure when not converged). */
|
|
186
204
|
notices: EngineNotice[];
|
|
@@ -4,7 +4,7 @@ import { atomicWriteFileSync, ensureDirExists, lockedStrictUpdate, readStrictSid
|
|
|
4
4
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
5
5
|
import { CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS, screenConsolidationOptions, supersessionFuseCeiling, } from "./consolidation.js";
|
|
6
6
|
import { memoryConsolidationIncompleteNotice } from "./engine.js";
|
|
7
|
-
import { CONSOLIDATION_DRIVE_ABORT, CONSOLIDATION_SNAPSHOT_PARKED, MEMORY_DISTILLER_CONTRACT_V1, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintLlmConsolidationPlan, } from "./distiller.js";
|
|
7
|
+
import { CONSOLIDATION_DRIVE_ABORT, CONSOLIDATION_SNAPSHOT_PARKED, MEMORY_DISTILLER_CONTRACT_V1, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintExposurePartitionedPlan, mintLlmConsolidationPlan, normalizedCandidateRev, } from "./distiller.js";
|
|
8
8
|
export const CONSOLIDATION_DRIVER_RUNS_FILE = "distiller-runs.json";
|
|
9
9
|
export const CONSOLIDATION_DRIVER_PLANS_DIR = "distiller-plans";
|
|
10
10
|
function coerceRunsFile(raw) {
|
|
@@ -71,6 +71,15 @@ class DriverMintFailure extends Error {
|
|
|
71
71
|
this.detail = detail;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
class MintInvalidated extends Error {
|
|
75
|
+
detail;
|
|
76
|
+
residue;
|
|
77
|
+
constructor(detail, residue = []) {
|
|
78
|
+
super(detail);
|
|
79
|
+
this.detail = detail;
|
|
80
|
+
this.residue = residue;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
74
83
|
const codeOf = (err) => {
|
|
75
84
|
const c = err?.code;
|
|
76
85
|
return typeof c === "string" ? c : undefined;
|
|
@@ -173,6 +182,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
173
182
|
repairs: { ...run.repairs },
|
|
174
183
|
writeFailures: [...run.writeFailures],
|
|
175
184
|
residue: run.residue ?? [],
|
|
185
|
+
...(run.withheldFromCleanArm !== undefined ? { withheldFromCleanArm: run.withheldFromCleanArm } : {}),
|
|
176
186
|
...(run.planArchive !== undefined ? { planArchive: run.planArchive } : {}),
|
|
177
187
|
notices,
|
|
178
188
|
...(announceFailures.length > 0 ? { announceFailures: [...announceFailures] } : {}),
|
|
@@ -187,7 +197,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
187
197
|
if (run.planCache === undefined) {
|
|
188
198
|
let minted;
|
|
189
199
|
try {
|
|
190
|
-
minted = await
|
|
200
|
+
minted = await mintExposurePartitionedPlan({
|
|
191
201
|
candidates,
|
|
192
202
|
chat: opts.chat,
|
|
193
203
|
model: opts.model,
|
|
@@ -230,10 +240,50 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
230
240
|
throw new DriverMintFailure(`the mint needed structural repairs on ${dropped} of ${total} member claim(s) (${(ratio * 100).toFixed(1)}% > mintRepairBudget ${(mintRepairBudget * 100).toFixed(1)}%) — a plan repaired this much does not represent the model's intent, and committing it would fold entries the harness chose, not the model (hallucinated ${plan.minting.repairs.hallucinatedMembers}, duplicate ${plan.minting.repairs.duplicateMembers}, nonInteger ${plan.minting.repairs.nonIntegerMembers}; emptied groups ${plan.minting.repairs.emptyGroups}, oversize ${plan.minting.repairs.oversizeGroups.length})`);
|
|
231
241
|
}
|
|
232
242
|
run.planArchive = distillerPlanArchiveName(run.runId, run.attempt);
|
|
233
|
-
run.planCache = {
|
|
243
|
+
run.planCache = {
|
|
244
|
+
maxInputsPerProduct: plan.maxInputsPerProduct,
|
|
245
|
+
products: plan.products,
|
|
246
|
+
...(plan.mintExposure !== undefined ? { mintExposure: plan.mintExposure } : {}),
|
|
247
|
+
...(plan.servedCandidates !== undefined ? { servedCandidates: plan.servedCandidates } : {}),
|
|
248
|
+
};
|
|
249
|
+
if (plan.withheldFromCleanArm !== undefined)
|
|
250
|
+
run.withheldFromCleanArm = plan.withheldFromCleanArm;
|
|
234
251
|
writeDriverRun(controlDir, run);
|
|
235
252
|
archiveDistillerPlan(controlDir, run.runId, plan, run.attempt);
|
|
236
253
|
}
|
|
254
|
+
const roster = run.planCache?.servedCandidates;
|
|
255
|
+
if (roster !== undefined && roster.length > 0) {
|
|
256
|
+
const offeredNow = new Map();
|
|
257
|
+
for (const c of candidates) {
|
|
258
|
+
if (typeof c?.entry?.id === "string")
|
|
259
|
+
offeredNow.set(c.entry.id, { rev: normalizedCandidateRev(c.entry.rev), marked: c.marked === true });
|
|
260
|
+
}
|
|
261
|
+
let ownFold;
|
|
262
|
+
const violations = [];
|
|
263
|
+
for (const rowR of roster) {
|
|
264
|
+
const off = offeredNow.get(rowR.id);
|
|
265
|
+
if (off !== undefined) {
|
|
266
|
+
if (off.rev !== rowR.rev)
|
|
267
|
+
violations.push(`${rowR.id}: rev moved since the mint`);
|
|
268
|
+
else if (off.marked !== rowR.marked)
|
|
269
|
+
violations.push(`${rowR.id}: marked flag flipped since the mint (${String(rowR.marked)} -> ${String(off.marked)})`);
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
ownFold ??= collectRunFoldEvidence(engine, scope, run.requestId);
|
|
273
|
+
if (ownFold.unreadable !== undefined)
|
|
274
|
+
continue;
|
|
275
|
+
if (!ownFold.targets.has(rowR.id))
|
|
276
|
+
violations.push(`${rowR.id}: left the candidate set without a fold this run's plans attest (deleted, excluded, or folded by another writer)`);
|
|
277
|
+
}
|
|
278
|
+
if (violations.length > 0) {
|
|
279
|
+
const residue = (run.planCache?.products ?? []).flatMap((p) => {
|
|
280
|
+
const live = (p.inputIds ?? []).filter((id) => offeredNow.has(id));
|
|
281
|
+
return live.length > 0 ? [{ name: p.name, targets: live.length }] : [];
|
|
282
|
+
});
|
|
283
|
+
delete run.planCache;
|
|
284
|
+
throw new MintInvalidated(`the mint's served-world roster failed the cross-cycle re-check (${violations.length} row(s)): ${violations.slice(0, 5).join("; ")}${violations.length > 5 ? "; …" : ""} — the cached plan is discarded; committed cycles stand and a fresh run re-mints over the current library`, residue);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
237
287
|
const offered = new Map();
|
|
238
288
|
for (const c of candidates)
|
|
239
289
|
if (typeof c?.entry?.id === "string")
|
|
@@ -304,6 +354,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
304
354
|
}
|
|
305
355
|
}
|
|
306
356
|
};
|
|
357
|
+
const cyclesSeen = [];
|
|
307
358
|
let drive;
|
|
308
359
|
try {
|
|
309
360
|
drive = await driveConsolidationToFixpoint(engine, scope, {
|
|
@@ -315,6 +366,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
315
366
|
maxCycles: opts.maxCycles ?? 64,
|
|
316
367
|
takeSnapshot,
|
|
317
368
|
onCycle: (row) => {
|
|
369
|
+
cyclesSeen.push(row);
|
|
318
370
|
if (row.committed.products > 0 || row.committed.superseded > 0)
|
|
319
371
|
run.cyclesDone += 1;
|
|
320
372
|
run.entriesSuperseded = (run.entriesSuperseded ?? 0) + row.committed.superseded;
|
|
@@ -330,6 +382,17 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
330
382
|
announce([`memory consolidation driver run ${run.runId} for scope ${inlineUntrusted(scope, 80)} FAILED before any write: ${inlineUntrusted(err.detail, 300)}`]);
|
|
331
383
|
return settle("driver_failed", undefined, err.detail);
|
|
332
384
|
}
|
|
385
|
+
if (err instanceof MintInvalidated) {
|
|
386
|
+
announce([`memory consolidation driver run ${run.runId} for scope ${inlineUntrusted(scope, 80)} MINT INVALIDATED: ${inlineUntrusted(err.detail, 300)}`]);
|
|
387
|
+
if (run.residue === undefined && err.residue.length > 0)
|
|
388
|
+
run.residue = err.residue;
|
|
389
|
+
const partial = {
|
|
390
|
+
cycles: cyclesSeen,
|
|
391
|
+
productsCommitted: cyclesSeen.reduce((a, c) => a + c.committed.products, 0),
|
|
392
|
+
entriesSuperseded: cyclesSeen.reduce((a, c) => a + c.committed.superseded, 0),
|
|
393
|
+
};
|
|
394
|
+
return settle("mint_invalidated", partial, err.detail);
|
|
395
|
+
}
|
|
333
396
|
throw err;
|
|
334
397
|
}
|
|
335
398
|
const writeResidue = run.writeFailures.map((w) => ({ name: w.key, targets: w.members }));
|
|
@@ -372,6 +435,15 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
372
435
|
}
|
|
373
436
|
if (driverResidue.length > 0)
|
|
374
437
|
run.residue = [...(drive.stop?.fuseBlocked ?? []), ...driverResidue];
|
|
438
|
+
const plannedAtStop = (run.planCache?.products ?? []).some((p) => (p.inputIds?.length ?? 0) > 0);
|
|
439
|
+
if (plannedAtStop) {
|
|
440
|
+
const evidence = collectRunFoldEvidence(engine, scope, run.requestId);
|
|
441
|
+
if (evidence.unreadable !== undefined) {
|
|
442
|
+
announce([
|
|
443
|
+
`memory consolidation driver run ${run.runId} for scope ${inlineUntrusted(scope, 80)}: plan ${evidence.unreadable} — one of THIS run's own cycles — answered ${evidence.unreadableState} (control-plane damage on run-owned evidence, not a transient race); the run stops non-converged with the damage disclosed, and a fresh run re-mints over the current bytes`,
|
|
444
|
+
]);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
375
447
|
const stop = drive.stop;
|
|
376
448
|
if (stop?.fuseBlocked !== undefined)
|
|
377
449
|
return settle("fuse_residue", drive, stop.reason);
|
|
@@ -247,7 +247,13 @@ export interface ConsolidationGateRow {
|
|
|
247
247
|
* cycle token; a newer snapshot replaces it, and commit refuses a token that no longer
|
|
248
248
|
* matches). `candidates`: the served (incremental) id → rev set — the edge-target legality
|
|
249
249
|
* domain; `eligible`: the FULL eligible id → rev map at snapshot time (the next fingerprint on
|
|
250
|
-
* completion); `marked`: any served candidate carried a committed external-origin marker
|
|
250
|
+
* completion); `marked`: any served candidate carried a committed external-origin marker;
|
|
251
|
+
* `markedIds` (additive, design/376-C1 D-1b): WHICH served candidates carried it — the
|
|
252
|
+
* attested freeze's world-movement baseline (a candidate marked NOW but absent here turned
|
|
253
|
+
* marked after the snapshot ⇒ the mint's clean-arm premise is stale). Absent on rows written
|
|
254
|
+
* before the field existed: an attested freeze over such a row takes the conservative arm
|
|
255
|
+
* (ANY currently-marked served candidate refuses stale — no baseline means no proof the mark
|
|
256
|
+
* predates the mint, and refusing to re-mint beats guessing). */
|
|
251
257
|
snapshot?: {
|
|
252
258
|
token: string;
|
|
253
259
|
at: number;
|
|
@@ -255,6 +261,7 @@ export interface ConsolidationGateRow {
|
|
|
255
261
|
candidates: Record<string, string>;
|
|
256
262
|
eligible: Record<string, string>;
|
|
257
263
|
marked: boolean;
|
|
264
|
+
markedIds?: string[];
|
|
258
265
|
};
|
|
259
266
|
}
|
|
260
267
|
interface ConsolidationGateFile {
|
|
@@ -326,7 +333,10 @@ export type ConsolidationIntent = {
|
|
|
326
333
|
type?: string;
|
|
327
334
|
};
|
|
328
335
|
/** §1.1 — one proposed product (driver output = DATA, zero carriage authority: origin/distilled
|
|
329
|
-
* in a proposal do not exist as fields — the engine computes both at freeze).
|
|
336
|
+
* in a proposal do not exist as fields — the engine computes both at freeze). The design/376-C1
|
|
337
|
+
* attestation does not breach this invariant: `mintExposure` on the PROPOSAL declares how the
|
|
338
|
+
* prompts were composed, it carries no origin value and can never set one — the engine still
|
|
339
|
+
* computes every origin itself (per product under attestation, run-level otherwise). */
|
|
330
340
|
export interface ConsolidationProductProposal {
|
|
331
341
|
name?: string;
|
|
332
342
|
description?: string;
|
|
@@ -342,6 +352,25 @@ export interface ConsolidationProposal {
|
|
|
342
352
|
scope: string;
|
|
343
353
|
products: ConsolidationProductProposal[];
|
|
344
354
|
intents?: ConsolidationIntent[];
|
|
355
|
+
/**
|
|
356
|
+
* design/376-C1 D-2 — the HOST-CODE attestation about prompt composition: `"partitioned"`
|
|
357
|
+
* declares that the proposal was minted under exposure-partitioned calls, i.e. every all-clean
|
|
358
|
+
* declared product's authoring chain was served NO byte of any committed-origin (marked) entry
|
|
359
|
+
* (the clean arm's grouping/writing calls saw the clean subset only — mechanical isolation,
|
|
360
|
+
* auditable from the archived transcript). The freeze folds origin PER PRODUCT only under this
|
|
361
|
+
* attestation; absent (or any other spelling — refused structurally, closed set), the freeze
|
|
362
|
+
* keeps the run-level single-value fold: any marked candidate in the served set marks EVERY
|
|
363
|
+
* product (today's safe default, and the permanent default for foreign minters).
|
|
364
|
+
*
|
|
365
|
+
* Trust plane, stated in full: this is a host-code attestation, NOT driver/model output — the
|
|
366
|
+
* drive re-assembles the proposal it commits (`{ scope, products, mintExposure }`), so model
|
|
367
|
+
* output can never populate it. Attesting falsely is a HOST defect outside the threat model,
|
|
368
|
+
* the same plane as a mis-declared `ToolSpec.contentOrigin` — and unlike most host mistakes it
|
|
369
|
+
* stays auditable after the fact: the archived clean-arm grouping prompt must contain zero
|
|
370
|
+
* marked-entry bytes, a mechanical check any reader can re-run against any plan that claims
|
|
371
|
+
* this attestation.
|
|
372
|
+
*/
|
|
373
|
+
mintExposure?: "partitioned";
|
|
345
374
|
}
|
|
346
375
|
export type ConsolidationDirectedState = "pending" | "applied" | "satisfied" | "conflict";
|
|
347
376
|
export interface ConsolidationDirectedPatch {
|
|
@@ -379,7 +408,15 @@ export interface ConsolidationPlanFile {
|
|
|
379
408
|
/** r3-1 — the ENGINE-VISIBLE-SET fold fact this run froze under (authority = what the engine
|
|
380
409
|
* served, never the proposal's declaration). */
|
|
381
410
|
visibleMarked: boolean;
|
|
411
|
+
/** The RUN-LEVEL single-value fold, when it applied (design/376-C1 D-6): present ⇔ this plan
|
|
412
|
+
* froze UN-attested over a marked served set, and this one value rides every product. An
|
|
413
|
+
* attested (per-product) plan omits it — each product's origin lives in its own frozen bytes
|
|
414
|
+
* below, and a single plan-level value would misdescribe a mixed plan. */
|
|
382
415
|
foldedOrigin?: MemoryEntryOrigin;
|
|
416
|
+
/** design/376-C1 D-6 — the proposal's accepted attestation, echoed for audit (informational:
|
|
417
|
+
* apply is byte-faithful to `products` and never re-reads this; a reader that predates the
|
|
418
|
+
* field ignores it and replays correctly, which is why the plan version does NOT bump). */
|
|
419
|
+
mintExposure?: "partitioned";
|
|
383
420
|
/** Frozen products — FULL entry bytes (id/frontmatter incl. distilled + origin/body/rev). */
|
|
384
421
|
products: MemoryEntry[];
|
|
385
422
|
productStates: Record<string, "pending" | "applied" | "conflict">;
|
|
@@ -479,16 +516,26 @@ export declare function productAddPatch(product: MemoryEntry): NotePatch;
|
|
|
479
516
|
* inputs edited mid-run) settles here TOO — committed cycles stand
|
|
480
517
|
* (add-only, no rollback), the residue is named, and a fresh run re-mints
|
|
481
518
|
* over the current bytes. The clean `converged` label never covers a
|
|
482
|
-
* driver-side residue
|
|
519
|
+
* driver-side residue;
|
|
520
|
+
* - `mint_invalidated` — design/376-C1 D-1b (driver layer): the mint's served-world ROSTER
|
|
521
|
+
* failed the cross-cycle re-check — a served candidate moved rev, flipped
|
|
522
|
+
* its marked flag, or left the candidate set without a fold this run's own
|
|
523
|
+
* plans attest. The cached plan is discarded (its prompts were composed
|
|
524
|
+
* over a world that no longer stands — replaying it could publish clean
|
|
525
|
+
* products whose authoring chain silently absorbed the moved row's
|
|
526
|
+
* influence); committed cycles stand (add-only, no rollback), and the host
|
|
527
|
+
* re-runs to re-mint over the current bytes (immediately or next window —
|
|
528
|
+
* host policy).
|
|
483
529
|
* Extending this set is a consumer-visible closed-set expansion (release-note duty: every
|
|
484
|
-
* downstream that pinned the enumeration becomes a named party
|
|
530
|
+
* downstream that pinned the enumeration becomes a named party — `mint_invalidated` was the
|
|
531
|
+
* design/376-C1 add, named on its release).
|
|
485
532
|
*
|
|
486
533
|
* The type DERIVES from the array (never `satisfies` beside it): the array exists precisely for
|
|
487
534
|
* downstream enumeration, and two independent declarations would let a new union arm skip the
|
|
488
535
|
* array with no compile error — the derived form makes an edit to either side a type error at
|
|
489
536
|
* the other.
|
|
490
537
|
*/
|
|
491
|
-
export declare const CONSOLIDATION_RUN_STOP_REASONS: readonly ["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed"];
|
|
538
|
+
export declare const CONSOLIDATION_RUN_STOP_REASONS: readonly ["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed", "mint_invalidated"];
|
|
492
539
|
/** See the doc on {@link CONSOLIDATION_RUN_STOP_REASONS} — the single source this derives from. */
|
|
493
540
|
export type ConsolidationRunStopReason = (typeof CONSOLIDATION_RUN_STOP_REASONS)[number];
|
|
494
541
|
export {};
|