@sema-agent/core 5.60.1 → 5.62.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 +125 -0
- package/dist/agents/subagent.d.ts +4 -2
- package/dist/agents/subagent.js +9 -9
- package/dist/brain/open-responses.js +8 -3
- package/dist/brain/openai.js +4 -4
- package/dist/brain/stream-engine.d.ts +13 -2
- package/dist/brain/stream-engine.js +3 -3
- package/dist/core/auto-mode-prompt-assets.js +1 -1
- package/dist/core/checkpoint-store.d.ts +36 -4
- package/dist/core/checkpoint-store.js +1 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -2
- package/dist/core/hooks.d.ts +83 -4
- package/dist/core/hooks.js +3 -3
- 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 +8 -0
- 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/park-selfcheck.js +2 -0
- package/dist/core/pricing.d.ts +24 -0
- package/dist/core/pricing.js +18 -0
- package/dist/core/runner/prepare-config-doors.d.ts +34 -0
- package/dist/core/runner/prepare-config-doors.js +55 -0
- package/dist/core/runner/prepare-task.d.ts +52 -10
- package/dist/core/runner/prepare-task.js +77 -42
- package/dist/core/runner/runtask.d.ts +7 -0
- package/dist/core/runner/runtask.js +254 -38
- package/dist/core/runner/turn-attachments.d.ts +137 -5
- package/dist/core/runner/turn-attachments.js +25 -2
- package/dist/core/store-contracts/checkpoint-store-contract.js +19 -0
- package/dist/core/task-notification.d.ts +50 -23
- package/dist/core/task-notification.js +20 -4
- package/dist/core/tool-errors.d.ts +2 -1
- package/dist/core/tool-policy.d.ts +27 -0
- package/dist/core/types.d.ts +214 -31
- package/dist/core/untrusted-text.d.ts +5 -4
- package/dist/core/untrusted-text.js +8 -0
- package/dist/core/usage-window-store.d.ts +109 -8
- package/dist/core/usage-window-store.js +79 -12
- 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 +2 -2
- package/dist/index.js +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +9 -4
- package/dist/orchestration/run-workflow-tool.js +1 -1
- package/dist/orchestration/workflow.d.ts +2 -2
- package/dist/prompt-assembly/event-registry.js +2 -0
- package/dist/server/http.d.ts +1 -1
- package/dist/stores/file/usage-window-store.d.ts +1 -1
- package/dist/stores/file/usage-window-store.js +27 -6
- package/dist/tools/loop-tick.js +1 -1
- package/dist/tools/monitor.d.ts +3 -3
- package/dist/tools/monitor.js +1 -1
- package/dist/tools/scheduler-tools.js +9 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +7 -1
package/dist/core/hooks.js
CHANGED
|
@@ -841,7 +841,7 @@ export async function runToolGate(input) {
|
|
|
841
841
|
? { origin: orgAskOrigin !== undefined ? `org_${orgAskOrigin}` : "policy" }
|
|
842
842
|
: undefined;
|
|
843
843
|
if (suspendAsk && decision.action === "ask") {
|
|
844
|
-
const suspended = await suspendAsk(req, currentInput, safety, undefined, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined, decision.action === "ask" ? decision.segmentCoverage : undefined);
|
|
844
|
+
const suspended = await suspendAsk(req, currentInput, safety, undefined, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined, decision.action === "ask" ? decision.segmentCoverage : undefined, decision.action === "ask" ? decision.matchedAskRule : undefined);
|
|
845
845
|
if (suspended) {
|
|
846
846
|
if ("parkFailed" in suspended)
|
|
847
847
|
parkFailed = suspended.parkFailed;
|
|
@@ -860,7 +860,7 @@ export async function runToolGate(input) {
|
|
|
860
860
|
req.args = outcome.presentedInput;
|
|
861
861
|
}
|
|
862
862
|
if (suspendAsk && outcome.parkDeclined && parkFailed === undefined) {
|
|
863
|
-
const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined, decision.action === "ask" ? decision.segmentCoverage : undefined);
|
|
863
|
+
const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined, decision.action === "ask" ? decision.segmentCoverage : undefined, decision.action === "ask" ? decision.matchedAskRule : undefined);
|
|
864
864
|
if (suspended) {
|
|
865
865
|
if ("parkFailed" in suspended)
|
|
866
866
|
parkFailed = suspended.parkFailed;
|
|
@@ -892,7 +892,7 @@ export async function runToolGate(input) {
|
|
|
892
892
|
askDenyResolution = resolved.resolution;
|
|
893
893
|
decision = resolved;
|
|
894
894
|
if (resolved.action === "deny" && resolved.approverUnavailable === true && suspendAsk && parkFailed === undefined) {
|
|
895
|
-
const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(askBeforeResolve), askBeforeResolve.action === "ask" ? askBeforeResolve.persistedRuleShadowed : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.decisionReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeCause : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.segmentCoverage : undefined);
|
|
895
|
+
const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(askBeforeResolve), askBeforeResolve.action === "ask" ? askBeforeResolve.persistedRuleShadowed : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.decisionReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeCause : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.segmentCoverage : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.matchedAskRule : undefined);
|
|
896
896
|
if (suspended) {
|
|
897
897
|
if ("parkFailed" in suspended)
|
|
898
898
|
parkFailed = suspended.parkFailed;
|
|
@@ -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 {};
|
|
@@ -265,6 +265,8 @@ function planShapeInvalid(raw, planId) {
|
|
|
265
265
|
if (freezeRefused !== undefined && (!Array.isArray(freezeRefused) || !freezeRefused.every((x) => typeof x === "string"))) {
|
|
266
266
|
return "malformed freezeRefusedInputIds";
|
|
267
267
|
}
|
|
268
|
+
if (p["mintExposure"] !== undefined && p["mintExposure"] !== "partitioned")
|
|
269
|
+
return "malformed mintExposure echo";
|
|
268
270
|
if ((p["v"] === 2) !== (freezeRefused !== undefined)) {
|
|
269
271
|
return p["v"] === 2 ? "v2 plan without its freezeRefusedInputIds member" : "freezeRefusedInputIds on a v1 plan";
|
|
270
272
|
}
|
|
@@ -360,4 +362,4 @@ export class ConsolidationRefusedError extends Error {
|
|
|
360
362
|
export function productAddPatch(product) {
|
|
361
363
|
return { op: "add", id: product.id, entry: product, guard: "absent" };
|
|
362
364
|
}
|
|
363
|
-
export const CONSOLIDATION_RUN_STOP_REASONS = ["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed"];
|
|
365
|
+
export const CONSOLIDATION_RUN_STOP_REASONS = ["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed", "mint_invalidated"];
|
|
@@ -64,6 +64,17 @@ export declare const LLM_DISTILLER_CONTRACTS: Record<string, LlmDistillerContrac
|
|
|
64
64
|
* archived). The shipping rule this alias exists to make auditable: a change to the product prompt
|
|
65
65
|
* is a NEW `version` (pd-2, …) and must produce rig readings BEFORE it ships — the contract-hash
|
|
66
66
|
* pin in the test suite reds on any silent byte drift under an unchanged version id.
|
|
67
|
+
*
|
|
68
|
+
* STANDING OBLIGATION on pd-2 (design/376-C1, ruled — mandatory, not optional): the next contract
|
|
69
|
+
* version MUST carry, as its FIRST A/B item, the root-elimination arm for the shared-grouping
|
|
70
|
+
* influence channel — the clean arm's writing pass without the grouping-authored `intent` (the
|
|
71
|
+
* template's own "(none stated)" arm) and without the `key` name fallback, which empties the one
|
|
72
|
+
* byte path an unattributed candidate has into a clean product and retires most of the D-1b guard
|
|
73
|
+
* machinery. The A/B must publish an EXPLICIT continuation verdict, one of three states — switch
|
|
74
|
+
* (root elimination ships) / hold (guard form stays, with readings) / redirect — it may not be
|
|
75
|
+
* left hanging. The exposure-partitioned topology itself deliberately did NOT bump this version
|
|
76
|
+
* (the candidate-set composition is topology, not wording; the wording axis stays single-variable
|
|
77
|
+
* for exactly this A/B).
|
|
67
78
|
*/
|
|
68
79
|
export declare const MEMORY_DISTILLER_CONTRACT_V1: LlmDistillerContract;
|
|
69
80
|
/**
|
|
@@ -223,6 +234,20 @@ export interface LlmConsolidationPlanProduct {
|
|
|
223
234
|
* alone, exactly as they always did (the archived-readings compatibility arm). */
|
|
224
235
|
inputRevs?: Record<string, string>;
|
|
225
236
|
}
|
|
237
|
+
/** design/376-C1 §2.2 — one arm's mint accounting inside an exposure-partitioned plan (usage
|
|
238
|
+
* "两臂合计+分列": the merged plan's top-level `minting` carries the totals, these rows carry the
|
|
239
|
+
* per-arm split PLUS each arm's verbatim grouping prompt — the clean arm's prompt bytes are the
|
|
240
|
+
* C1-G5 audit face: any reader can mechanically verify zero marked-entry bytes reached it). */
|
|
241
|
+
export interface LlmConsolidationPlanArm {
|
|
242
|
+
arm: "clean" | "marked";
|
|
243
|
+
candidateCount: number;
|
|
244
|
+
calls: number;
|
|
245
|
+
promptTokens: number;
|
|
246
|
+
completionTokens: number;
|
|
247
|
+
retries: number;
|
|
248
|
+
truncations: number;
|
|
249
|
+
groupingPrompt: string;
|
|
250
|
+
}
|
|
226
251
|
export interface LlmConsolidationPlan {
|
|
227
252
|
kind: "llm-consolidation-plan";
|
|
228
253
|
contractVersion: string;
|
|
@@ -233,6 +258,27 @@ export interface LlmConsolidationPlan {
|
|
|
233
258
|
maxInputsPerProduct: number;
|
|
234
259
|
candidateCount: number;
|
|
235
260
|
products: LlmConsolidationPlanProduct[];
|
|
261
|
+
/** design/376-C1 D-2 — present ⇔ this plan was minted by {@link mintExposurePartitionedPlan}
|
|
262
|
+
* (clean/marked candidates partitioned into physically separate call chains). The DRIVE threads
|
|
263
|
+
* this onto the committed proposal, where it is the engine's per-product-fold attestation.
|
|
264
|
+
* Absent on every plan minted before the field existed — those replay UN-attested (run-level
|
|
265
|
+
* fold, the safe default), which is exactly right for prompts composed over the whole library. */
|
|
266
|
+
mintExposure?: "partitioned";
|
|
267
|
+
/** design/376-C1 D-1b — the mint's served-world roster: every candidate actually RENDERED into
|
|
268
|
+
* some arm's grouping call, with the rev and marked flag it was served at. The driver's
|
|
269
|
+
* cross-cycle invalidation predicate compares each row against the fresh snapshot
|
|
270
|
+
* (offered ∧ same rev ∧ same marked, or departed-with-own-fold-evidence) — any other state
|
|
271
|
+
* means the world the prompts were composed over no longer stands. A single withheld marked
|
|
272
|
+
* row (markedCount = 1, no marked arm) is deliberately NOT in the roster: no call ever saw it. */
|
|
273
|
+
servedCandidates?: Array<{
|
|
274
|
+
id: string;
|
|
275
|
+
rev: string;
|
|
276
|
+
marked: boolean;
|
|
277
|
+
}>;
|
|
278
|
+
/** design/376-C1 A-5 disclosure — how many marked candidates were withheld from the CLEAN arm's
|
|
279
|
+
* prompts (= the marked candidate count when the partition engaged). Present ⇔ partitioned
|
|
280
|
+
* mint over a library with at least one marked row. */
|
|
281
|
+
withheldFromCleanArm?: number;
|
|
236
282
|
minting: {
|
|
237
283
|
calls: number;
|
|
238
284
|
groupingAttempts: number;
|
|
@@ -264,6 +310,10 @@ export interface LlmConsolidationPlan {
|
|
|
264
310
|
model: string | null;
|
|
265
311
|
answer: string;
|
|
266
312
|
}>;
|
|
313
|
+
/** Per-arm split of a partitioned mint (see {@link LlmConsolidationPlanArm}); absent on
|
|
314
|
+
* single-call-chain plans, INCLUDING the zero-marked degenerate of the partitioned mint
|
|
315
|
+
* (whose bytes must stay identical to the plain mint's, modulo the attestation echo). */
|
|
316
|
+
arms?: LlmConsolidationPlanArm[];
|
|
267
317
|
};
|
|
268
318
|
}
|
|
269
319
|
export type MintLlmConsolidationPlanResult = {
|
|
@@ -280,7 +330,7 @@ export type MintLlmConsolidationPlanResult = {
|
|
|
280
330
|
* it answered is returned inside the plan so the run can be re-read, re-scored and disputed from
|
|
281
331
|
* the archive alone.
|
|
282
332
|
*/
|
|
283
|
-
export declare function mintLlmConsolidationPlan({ candidates, chat, model, baseUrl, maxInputsPerProduct, contract, onProgress, }: {
|
|
333
|
+
export declare function mintLlmConsolidationPlan({ candidates, chat, model, baseUrl, maxInputsPerProduct, contract, onProgress, armLabel, }: {
|
|
284
334
|
candidates: readonly DistillerCandidate[];
|
|
285
335
|
chat: DistillerChatFn;
|
|
286
336
|
/** The requested model id, recorded into the plan (audit anchor). Alias spellings refuse. */
|
|
@@ -298,7 +348,44 @@ export declare function mintLlmConsolidationPlan({ candidates, chat, model, base
|
|
|
298
348
|
done: number;
|
|
299
349
|
of: number;
|
|
300
350
|
}) => void) | null;
|
|
351
|
+
/** design/376-C1 — the partitioned mint's TRANSCRIPT arm tag (`grouping.clean#1` /
|
|
352
|
+
* `writing.marked#1:key#1` label forms). Prompt bytes are untouched — this names calls in the
|
|
353
|
+
* archive only, and stays compatible with `planParseRepairs`' `startsWith("grouping")` phase
|
|
354
|
+
* split and its `#N` retry grammar. Absent ⇒ the historical label forms, byte-identical. */
|
|
355
|
+
armLabel?: "clean" | "marked";
|
|
301
356
|
}): Promise<MintLlmConsolidationPlanResult>;
|
|
357
|
+
/** ONE rev sentinel for every face that RECORDS or RE-CHECKS a candidate's rev (the mint roster
|
|
358
|
+
* below, the driver's cross-cycle roster re-check, the replay's offered map). The snapshot
|
|
359
|
+
* interface is structural — a host-supplied engine can hand entries whose rev is not a string at
|
|
360
|
+
* runtime — and the recording and re-checking sides MUST agree on the fallback, or a rev-less
|
|
361
|
+
* candidate compares unequal to its own roster row and self-invalidates the mint it was just
|
|
362
|
+
* served into (a paid mint discarded on its own cycle, re-minted and re-discarded forever). */
|
|
363
|
+
export declare const normalizedCandidateRev: (rev: unknown) => string;
|
|
364
|
+
/**
|
|
365
|
+
* design/376-C1 §2.2 — mint an EXPOSURE-PARTITIONED fold plan: candidates are bisected on the
|
|
366
|
+
* snapshot's per-candidate `marked` flag and each subset is minted through its own physically
|
|
367
|
+
* separate call chain, so no clean product's authoring chain (grouping intent included) is ever
|
|
368
|
+
* served a marked entry's bytes. This is what makes the engine's per-product fold SOUND — the
|
|
369
|
+
* attestation the plan carries (`mintExposure: "partitioned"`) is the freeze's gate, and this
|
|
370
|
+
* function is the single source the product driver and every bench adapter share.
|
|
371
|
+
*
|
|
372
|
+
* - CLEAN arm: `mintLlmConsolidationPlan` over the clean subset — contract, templates, retries,
|
|
373
|
+
* sampling all byte-identical to the plain mint (dl-3 恒等; only the candidate-set composition
|
|
374
|
+
* moves, which is topology, not wording — no prompt-contract version bump, D-10/A-10).
|
|
375
|
+
* - MARKED arm: the same contract over the marked subset, minted only at `markedCount ≥ 2`
|
|
376
|
+
* (design C1-B): a single marked row has no group to fold and simply stays where it is — an
|
|
377
|
+
* index handle row loses no discoverability by standing still. Withheld rows are disclosed
|
|
378
|
+
* (`withheldFromCleanArm`) and kept OUT of the served roster (no call saw them).
|
|
379
|
+
* - ZERO-marked degenerate: exactly ONE arm over exactly the full set with the HISTORICAL
|
|
380
|
+
* transcript labels — byte-identical to the plain mint modulo the attestation echo and the
|
|
381
|
+
* roster (C1-G1's identity claim).
|
|
382
|
+
* - Either arm failing fails the WHOLE mint (one mint, one verdict; both arms' calls ride the
|
|
383
|
+
* failure transcript). This widens nothing: under the run-level mint a hostile marked row
|
|
384
|
+
* already sat inside the single grouping call and could fail it whole.
|
|
385
|
+
* - Cross-arm group structure is impossible by construction: each arm's `sanitizeLlmGroups`
|
|
386
|
+
* valid map is built over that arm's own 1..N numbering.
|
|
387
|
+
*/
|
|
388
|
+
export declare function mintExposurePartitionedPlan(args: Parameters<typeof mintLlmConsolidationPlan>[0]): Promise<MintLlmConsolidationPlanResult>;
|
|
302
389
|
/** The distiller signature the drive consumes: `(candidates, snapshot, scope) -> proposal`.
|
|
303
390
|
* At runtime only `entry.id` is load-bearing for the replay filter — test/bench stubs that hand
|
|
304
391
|
* partial entry shapes are tolerated by the reads, the type states the engine's real snapshot.
|
|
@@ -321,6 +408,7 @@ export declare function llmPlanDistiller(plan: {
|
|
|
321
408
|
products?: ReadonlyArray<Pick<LlmConsolidationPlanProduct, "name" | "description" | "type" | "body" | "inputIds"> & {
|
|
322
409
|
inputRevs?: Record<string, string>;
|
|
323
410
|
}>;
|
|
411
|
+
mintExposure?: "partitioned";
|
|
324
412
|
} | undefined | null): ConsolidationDistillFn;
|
|
325
413
|
export interface FuseSchedule<P> {
|
|
326
414
|
schedule: "next-fit-decreasing";
|
|
@@ -252,12 +252,15 @@ export function sanitizeLlmGroups(groups, { candidateIds, maxInputsPerProduct =
|
|
|
252
252
|
}
|
|
253
253
|
return { groups: clean, repairs, claimedIds: [...claimed] };
|
|
254
254
|
}
|
|
255
|
-
|
|
255
|
+
function assertMintConfig(chat, model) {
|
|
256
256
|
if (typeof chat !== "function")
|
|
257
257
|
throw distillerConfigRefusal("mintLlmConsolidationPlan needs a chat function (the explicit model seat) — a distiller that silently skips would publish an empty plan as a model's answer.");
|
|
258
258
|
if (typeof model !== "string" || model.trim() === "" || isAliasModelId(model)) {
|
|
259
259
|
throw distillerConfigRefusal(`mintLlmConsolidationPlan needs an EXPLICIT model id, got ${JSON.stringify(model)} — an alias can be re-pointed upstream and would move the run with no trace in the plan.`);
|
|
260
260
|
}
|
|
261
|
+
}
|
|
262
|
+
export async function mintLlmConsolidationPlan({ candidates, chat, model, baseUrl, maxInputsPerProduct = DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT, contract = MEMORY_DISTILLER_CONTRACT_V1, onProgress = null, armLabel, }) {
|
|
263
|
+
assertMintConfig(chat, model);
|
|
261
264
|
const rows = candidates.map((c, i) => ({ n: i + 1, id: c?.entry?.id, rev: c?.entry?.rev, text: renderCandidate(i + 1, c) }));
|
|
262
265
|
const missingId = rows.filter((r) => typeof r.id !== "string");
|
|
263
266
|
if (missingId.length > 0)
|
|
@@ -277,7 +280,7 @@ export async function mintLlmConsolidationPlan({ candidates, chat, model, baseUr
|
|
|
277
280
|
for (let attempt = 0; attempt <= contract.retries && groups === null; attempt++) {
|
|
278
281
|
groupingAttempts += 1;
|
|
279
282
|
const user = attempt === 0 ? groupingUser : `${groupingUser}\n\nYour previous answer could not be read: ${lastError}. Answer with one JSON object and nothing else.`;
|
|
280
|
-
const out = await record(`grouping#${attempt + 1}`, () => chat({ system: contract.grouping.system, user, maxTokens: contract.sampling.maxTokens.grouping, temperature: contract.sampling.temperature }));
|
|
283
|
+
const out = await record(`grouping${armLabel !== undefined ? `.${armLabel}` : ""}#${attempt + 1}`, () => chat({ system: contract.grouping.system, user, maxTokens: contract.sampling.maxTokens.grouping, temperature: contract.sampling.temperature }));
|
|
281
284
|
if (out.finishReason === "length") {
|
|
282
285
|
lastError = "the answer was cut off at the token limit";
|
|
283
286
|
continue;
|
|
@@ -312,7 +315,7 @@ export async function mintLlmConsolidationPlan({ candidates, chat, model, baseUr
|
|
|
312
315
|
let err = null;
|
|
313
316
|
for (let attempt = 0; attempt <= contract.retries && written === null; attempt++) {
|
|
314
317
|
const u = attempt === 0 ? user : `${user}\n\nYour previous answer could not be read: ${err}. Answer with one JSON object and nothing else.`;
|
|
315
|
-
const out = await record(`writing#${i + 1}:${g.key}#${attempt + 1}`, () => chat({ system: contract.writing.system, user: u, maxTokens: contract.sampling.maxTokens.writing, temperature: contract.sampling.temperature }));
|
|
318
|
+
const out = await record(`writing${armLabel !== undefined ? `.${armLabel}` : ""}#${i + 1}:${g.key}#${attempt + 1}`, () => chat({ system: contract.writing.system, user: u, maxTokens: contract.sampling.maxTokens.writing, temperature: contract.sampling.temperature }));
|
|
316
319
|
if (out.finishReason === "length") {
|
|
317
320
|
err = "the answer was cut off at the token limit";
|
|
318
321
|
continue;
|
|
@@ -372,17 +375,103 @@ export async function mintLlmConsolidationPlan({ candidates, chat, model, baseUr
|
|
|
372
375
|
},
|
|
373
376
|
};
|
|
374
377
|
}
|
|
378
|
+
export const normalizedCandidateRev = (rev) => (typeof rev === "string" ? rev : "");
|
|
379
|
+
const rosterOf = (list) => list.map((c) => ({ id: c.entry.id, rev: normalizedCandidateRev(c.entry.rev), marked: c.marked === true }));
|
|
380
|
+
const armUsageOf = (arm, plan, candidateCount) => ({
|
|
381
|
+
arm,
|
|
382
|
+
candidateCount,
|
|
383
|
+
calls: plan.minting.calls,
|
|
384
|
+
promptTokens: plan.minting.usage.promptTokens,
|
|
385
|
+
completionTokens: plan.minting.usage.completionTokens,
|
|
386
|
+
retries: plan.minting.parseRepairs.retries,
|
|
387
|
+
truncations: plan.minting.parseRepairs.lengthCapped.length,
|
|
388
|
+
groupingPrompt: plan.minting.prompts.grouping,
|
|
389
|
+
});
|
|
390
|
+
export async function mintExposurePartitionedPlan(args) {
|
|
391
|
+
assertMintConfig(args.chat, args.model);
|
|
392
|
+
const { candidates, ...rest } = args;
|
|
393
|
+
const clean = (candidates ?? []).filter((c) => c?.marked !== true);
|
|
394
|
+
const marked = (candidates ?? []).filter((c) => c?.marked === true);
|
|
395
|
+
if (marked.length === 0) {
|
|
396
|
+
const res = await mintLlmConsolidationPlan({ ...rest, candidates: clean });
|
|
397
|
+
if (!res.ok)
|
|
398
|
+
return res;
|
|
399
|
+
return { ok: true, plan: { ...res.plan, mintExposure: "partitioned", servedCandidates: rosterOf(clean) } };
|
|
400
|
+
}
|
|
401
|
+
const cleanRes = clean.length > 0 ? await mintLlmConsolidationPlan({ ...rest, candidates: clean, armLabel: "clean" }) : undefined;
|
|
402
|
+
if (cleanRes !== undefined && !cleanRes.ok) {
|
|
403
|
+
return { ok: false, reason: `clean arm: ${cleanRes.reason}`, calls: cleanRes.calls };
|
|
404
|
+
}
|
|
405
|
+
const markedRes = marked.length >= 2 ? await mintLlmConsolidationPlan({ ...rest, candidates: marked, armLabel: "marked" }) : undefined;
|
|
406
|
+
if (markedRes !== undefined && !markedRes.ok) {
|
|
407
|
+
return { ok: false, reason: `marked arm: ${markedRes.reason}`, calls: [...(cleanRes?.plan.minting.transcript ?? []), ...markedRes.calls] };
|
|
408
|
+
}
|
|
409
|
+
const arms = [
|
|
410
|
+
...(cleanRes !== undefined ? [{ res: cleanRes.plan, tag: "clean", count: clean.length }] : []),
|
|
411
|
+
...(markedRes !== undefined ? [{ res: markedRes.plan, tag: "marked", count: marked.length }] : []),
|
|
412
|
+
];
|
|
413
|
+
const served = [...(cleanRes !== undefined ? clean : []), ...(markedRes !== undefined ? marked : [])];
|
|
414
|
+
const transcript = arms.flatMap((a) => a.res.minting.transcript);
|
|
415
|
+
const sumRepairs = (k) => arms.reduce((n, a) => n + a.res.minting.repairs[k], 0);
|
|
416
|
+
const anchor = arms[0]?.res;
|
|
417
|
+
return {
|
|
418
|
+
ok: true,
|
|
419
|
+
plan: {
|
|
420
|
+
kind: "llm-consolidation-plan",
|
|
421
|
+
contractVersion: anchor?.contractVersion ?? (rest.contract ?? MEMORY_DISTILLER_CONTRACT_V1).version,
|
|
422
|
+
model: rest.model,
|
|
423
|
+
...(rest.baseUrl !== undefined ? { baseUrl: rest.baseUrl } : {}),
|
|
424
|
+
mintedAt: anchor?.mintedAt ?? new Date().toISOString(),
|
|
425
|
+
maxInputsPerProduct: anchor?.maxInputsPerProduct ?? rest.maxInputsPerProduct ?? DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT,
|
|
426
|
+
candidateCount: (candidates ?? []).length,
|
|
427
|
+
products: arms.flatMap((a) => a.res.products),
|
|
428
|
+
mintExposure: "partitioned",
|
|
429
|
+
servedCandidates: rosterOf(served),
|
|
430
|
+
withheldFromCleanArm: marked.length,
|
|
431
|
+
minting: {
|
|
432
|
+
calls: transcript.length,
|
|
433
|
+
groupingAttempts: arms.reduce((n, a) => n + a.res.minting.groupingAttempts, 0),
|
|
434
|
+
groupingParseRepairs: arms.reduce((n, a) => n + a.res.minting.groupingParseRepairs, 0),
|
|
435
|
+
parseRepairs: planParseRepairs({ minting: { transcript } }),
|
|
436
|
+
repairs: {
|
|
437
|
+
hallucinatedMembers: sumRepairs("hallucinatedMembers"),
|
|
438
|
+
duplicateMembers: sumRepairs("duplicateMembers"),
|
|
439
|
+
emptyGroups: sumRepairs("emptyGroups"),
|
|
440
|
+
oversizeGroups: arms.flatMap((a) => a.res.minting.repairs.oversizeGroups),
|
|
441
|
+
nonIntegerMembers: sumRepairs("nonIntegerMembers"),
|
|
442
|
+
},
|
|
443
|
+
writeFailures: arms.flatMap((a) => a.res.minting.writeFailures),
|
|
444
|
+
usage: {
|
|
445
|
+
promptTokens: arms.reduce((n, a) => n + a.res.minting.usage.promptTokens, 0),
|
|
446
|
+
completionTokens: arms.reduce((n, a) => n + a.res.minting.usage.completionTokens, 0),
|
|
447
|
+
},
|
|
448
|
+
prompts: {
|
|
449
|
+
grouping: anchor?.minting.prompts.grouping ?? "",
|
|
450
|
+
writingTemplate: (rest.contract ?? MEMORY_DISTILLER_CONTRACT_V1).writing.instructions,
|
|
451
|
+
systems: {
|
|
452
|
+
grouping: (rest.contract ?? MEMORY_DISTILLER_CONTRACT_V1).grouping.system,
|
|
453
|
+
writing: (rest.contract ?? MEMORY_DISTILLER_CONTRACT_V1).writing.system,
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
transcript,
|
|
457
|
+
...(arms.length > 0 ? { arms: arms.map((a) => armUsageOf(a.tag, a.res, a.count)) } : {}),
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
}
|
|
375
462
|
export function llmPlanDistiller(plan) {
|
|
376
463
|
const products = plan?.products ?? [];
|
|
464
|
+
const mintExposure = plan?.mintExposure === "partitioned" ? "partitioned" : undefined;
|
|
377
465
|
return (candidates, _snapshot, scope) => {
|
|
378
466
|
const offered = new Map();
|
|
379
467
|
for (const x of (candidates ?? [])) {
|
|
380
468
|
const id = x?.entry?.id ?? x?.id;
|
|
381
469
|
if (typeof id === "string")
|
|
382
|
-
offered.set(id,
|
|
470
|
+
offered.set(id, normalizedCandidateRev(x?.entry?.rev));
|
|
383
471
|
}
|
|
384
472
|
return {
|
|
385
473
|
scope,
|
|
474
|
+
...(mintExposure !== undefined ? { mintExposure } : {}),
|
|
386
475
|
products: products.map((p) => {
|
|
387
476
|
const ids = p.inputIds ?? [];
|
|
388
477
|
const live = ids.filter((id) => offered.has(id));
|
|
@@ -527,7 +616,7 @@ export async function driveConsolidationToFixpoint(engine, scope, opts) {
|
|
|
527
616
|
let receipt;
|
|
528
617
|
let commitError = null;
|
|
529
618
|
try {
|
|
530
|
-
receipt = await engine.commitConsolidationPlan(snapshot.cycleToken, { scope, products: plan.picked }, { requestId: `${requestIdPrefix}-c${cycle}` });
|
|
619
|
+
receipt = await engine.commitConsolidationPlan(snapshot.cycleToken, { scope, products: plan.picked, ...(proposal.mintExposure !== undefined ? { mintExposure: proposal.mintExposure } : {}) }, { requestId: `${requestIdPrefix}-c${cycle}` });
|
|
531
620
|
}
|
|
532
621
|
catch (err) {
|
|
533
622
|
commitError = err;
|
|
@@ -475,6 +475,10 @@ export interface ConsolidationCommitReceipt {
|
|
|
475
475
|
settled: string[];
|
|
476
476
|
pending: string[];
|
|
477
477
|
};
|
|
478
|
+
/** True ⇔ the RUN-LEVEL single-value fold applied to this plan (design/376-C1 D-6: an
|
|
479
|
+
* un-attested freeze over a marked served set). An attested (exposure-partitioned) plan
|
|
480
|
+
* answers false here even when some of its products carry origin — per-product outcomes live
|
|
481
|
+
* in the plan summary's `markedProducts` and in each product's own committed bytes. */
|
|
478
482
|
foldedOrigin: boolean;
|
|
479
483
|
notices: EngineNotice[];
|
|
480
484
|
}
|
|
@@ -510,6 +514,10 @@ export interface ConsolidationPlanSummary {
|
|
|
510
514
|
state?: ConsolidationPlanFile["state"];
|
|
511
515
|
createdAt?: number;
|
|
512
516
|
products?: number;
|
|
517
|
+
/** design/376-C1 D-6 (additive) — how many of the plan's frozen products carry an origin marker
|
|
518
|
+
* in their own bytes. Counts BOTH fold forms truthfully: a run-level plan's single value rides
|
|
519
|
+
* every product, an attested plan marks exactly the products whose declared inputs were marked. */
|
|
520
|
+
markedProducts?: number;
|
|
513
521
|
directed?: number;
|
|
514
522
|
intents?: number;
|
|
515
523
|
}
|