@sema-agent/core 5.60.1 → 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.
Files changed (32) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/agents/subagent.d.ts +4 -2
  3. package/dist/agents/subagent.js +9 -9
  4. package/dist/core/governance-codes.d.ts +1 -1
  5. package/dist/core/governance-codes.js +2 -2
  6. package/dist/core/memory-engine/consolidation-driver.d.ts +19 -1
  7. package/dist/core/memory-engine/consolidation-driver.js +75 -3
  8. package/dist/core/memory-engine/consolidation.d.ts +52 -5
  9. package/dist/core/memory-engine/consolidation.js +3 -1
  10. package/dist/core/memory-engine/distiller.d.ts +89 -1
  11. package/dist/core/memory-engine/distiller.js +94 -5
  12. package/dist/core/memory-engine/engine.d.ts +8 -0
  13. package/dist/core/memory-engine/engine.js +51 -8
  14. package/dist/core/memory-engine/index.d.ts +1 -1
  15. package/dist/core/memory-engine/index.js +1 -1
  16. package/dist/core/runner/prepare-task.d.ts +6 -3
  17. package/dist/core/runner/runtask.js +57 -26
  18. package/dist/core/task-notification.d.ts +50 -23
  19. package/dist/core/task-notification.js +20 -4
  20. package/dist/core/types.d.ts +76 -19
  21. package/dist/engine/harness/agent-harness.d.ts +58 -2
  22. package/dist/engine/harness/agent-harness.js +115 -5
  23. package/dist/engine/loop/agent-loop.js +153 -15
  24. package/dist/engine/loop/types.d.ts +32 -0
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +2 -2
  27. package/dist/orchestration/run-workflow-tool.d.ts +7 -2
  28. package/dist/orchestration/run-workflow-tool.js +1 -1
  29. package/dist/tools/monitor.d.ts +3 -3
  30. package/dist/tools/monitor.js +1 -1
  31. package/package.json +1 -1
  32. package/test/export-surface.snapshot.json +7 -1
@@ -252,12 +252,15 @@ export function sanitizeLlmGroups(groups, { candidateIds, maxInputsPerProduct =
252
252
  }
253
253
  return { groups: clean, repairs, claimedIds: [...claimed] };
254
254
  }
255
- export async function mintLlmConsolidationPlan({ candidates, chat, model, baseUrl, maxInputsPerProduct = DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT, contract = MEMORY_DISTILLER_CONTRACT_V1, onProgress = null, }) {
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, typeof x?.entry?.rev === "string" ? x.entry.rev : undefined);
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
  }
@@ -2596,7 +2596,7 @@ export class MemoryEngine {
2596
2596
  if (fresh.epoch !== epoch) {
2597
2597
  throw new ConsolidationRefusedError("memory.consolidation_stale_snapshot", `memory consolidation refused: the ${JSON.stringify(scope)} watermark advanced during the snapshot read — retake the snapshot.`);
2598
2598
  }
2599
- fresh.snapshot = { token: cycleToken, at, epoch, candidates: candidateRevs, eligible: eligibleRevs, marked };
2599
+ fresh.snapshot = { token: cycleToken, at, epoch, candidates: candidateRevs, eligible: eligibleRevs, marked, markedIds: candidates.filter((c) => c.marked).map((c) => c.entry.id) };
2600
2600
  let announce = false;
2601
2601
  if (opts.force !== undefined && fresh.lastForcedRequestId !== opts.force.requestId) {
2602
2602
  fresh.lastForcedRequestId = opts.force.requestId;
@@ -2656,6 +2656,9 @@ export class MemoryEngine {
2656
2656
  else if (p.inputs.length > screened.maxInputsPerProduct)
2657
2657
  reasons.push(`product #${i}: ${p.inputs.length} inputs > maxInputsPerProduct ${screened.maxInputsPerProduct}`);
2658
2658
  }
2659
+ if (proposal.mintExposure !== undefined && proposal.mintExposure !== "partitioned") {
2660
+ reasons.push(`mintExposure ${JSON.stringify(proposal.mintExposure)} is not a member of the closed attestation set {"partitioned"} — omit it (run-level fold) or spell it exactly`);
2661
+ }
2659
2662
  if (proposal.products.length === 0 && intents.length === 0) {
2660
2663
  reasons.push("empty plan: zero products and zero directed intents — a plan with nothing to apply must not settle as a completed run (the vacuous completion would stamp the eligible set into the fingerprint and blind the incremental face)");
2661
2664
  }
@@ -2741,19 +2744,43 @@ export class MemoryEngine {
2741
2744
  }
2742
2745
  const activeSetSize = headers.filter((h) => !superseded.has(h.id) && !exclusions.has(h.id)).length;
2743
2746
  const ceiling = supersessionFuseCeiling(activeSetSize, screened);
2747
+ const attested = proposal.mintExposure === "partitioned";
2748
+ if (attested) {
2749
+ const staleAxes = [];
2750
+ const baseline = snapshot.markedIds !== undefined ? new Set(snapshot.markedIds) : undefined;
2751
+ for (const [id, rev] of Object.entries(snapshot.candidates)) {
2752
+ const h = headerById.get(id);
2753
+ if (h === undefined)
2754
+ staleAxes.push(`served candidate ${id} left the committed listing`);
2755
+ else if (h.rev !== rev)
2756
+ staleAxes.push(`served candidate ${id} moved rev since the snapshot`);
2757
+ else if (exclusions.has(id))
2758
+ staleAxes.push(`served candidate ${id} entered the challenge/latch exclusion set`);
2759
+ else if (h.exposure !== undefined && (baseline === undefined || !baseline.has(id))) {
2760
+ staleAxes.push(baseline === undefined
2761
+ ? `served candidate ${id} is marked and the snapshot row predates the markedIds baseline — no proof the mark predates the mint (conservative arm)`
2762
+ : `served candidate ${id} turned marked since the snapshot`);
2763
+ }
2764
+ }
2765
+ if (staleAxes.length > 0) {
2766
+ throw new ConsolidationRefusedError("memory.consolidation_stale_snapshot", `memory consolidation refused: the attested (exposure-partitioned) mint's clean-arm premise is stale — the ${JSON.stringify(scope)} world moved between snapshot and freeze; retake the snapshot and re-mint`, staleAxes);
2767
+ }
2768
+ }
2744
2769
  let visibleMarked = snapshot.marked;
2745
- if (!visibleMarked) {
2770
+ if (!attested && !visibleMarked) {
2746
2771
  const inputIds = [...new Set(proposal.products.flatMap((p) => p.inputs.map((i2) => i2.id)))];
2747
2772
  const committedInputs = await face.getByIds(inputIds);
2748
2773
  visibleMarked = committedInputs.some((e) => committedOriginOf(e.frontmatter) !== undefined);
2749
2774
  }
2750
- const foldedOrigin = visibleMarked ? { taint: "external", cause: "derived", at } : undefined;
2775
+ const foldedOrigin = !attested && visibleMarked ? { taint: "external", cause: "derived", at } : undefined;
2776
+ const productMarked = (p) => p.inputs.some((i2) => headerById.get(i2.id)?.exposure !== undefined);
2751
2777
  for (let i = 0; i < proposal.products.length; i++) {
2752
2778
  const p = proposal.products[i];
2753
2779
  if (!isInstructionEntry({ ...(p.type !== undefined ? { type: p.type } : {}) }))
2754
2780
  continue;
2755
- if (visibleMarked) {
2756
- wholeReasons.push(`product #${i}: instruction-form product (type ${JSON.stringify(p.type)}) refused — the run's visible set contains marked content (the laundering hard gate, G7/G26; no option opens this arm)`);
2781
+ const hardArm = attested ? productMarked(p) : visibleMarked;
2782
+ if (hardArm) {
2783
+ wholeReasons.push(`product #${i}: instruction-form product (type ${JSON.stringify(p.type)}) refused — ${attested ? "the product's declared inputs contain marked content" : "the run's visible set contains marked content"} (the laundering hard gate, G7/G26; no option opens this arm)`);
2757
2784
  }
2758
2785
  else if (!screened.allowInstructionProducts) {
2759
2786
  wholeReasons.push(`product #${i}: instruction-form product (type ${JSON.stringify(p.type)}) refused — instruction-form consolidation products are refused unconditionally by default (a consolidation must not mint privileged entries out of ordinary notes); the host escape hatch is consolidation.allowInstructionProducts: true`);
@@ -2784,6 +2811,7 @@ export class MemoryEngine {
2784
2811
  }
2785
2812
  const id = uuidv7();
2786
2813
  const slug = deriveProductSlug(p.name, `consolidated-${id.slice(0, 8)}`);
2814
+ const productOrigin = attested ? (productMarked(p) ? { taint: "external", cause: "derived", at } : undefined) : foldedOrigin;
2787
2815
  const entry = {
2788
2816
  id,
2789
2817
  slug,
@@ -2792,7 +2820,7 @@ export class MemoryEngine {
2792
2820
  name: p.name ?? slug,
2793
2821
  ...(p.description !== undefined ? { description: p.description } : {}),
2794
2822
  ...(p.type !== undefined ? { type: p.type } : {}),
2795
- ...(foldedOrigin !== undefined ? { origin: { ...foldedOrigin } } : {}),
2823
+ ...(productOrigin !== undefined ? { origin: { ...productOrigin } } : {}),
2796
2824
  distilled: {
2797
2825
  planId,
2798
2826
  at,
@@ -2917,6 +2945,7 @@ export class MemoryEngine {
2917
2945
  epoch: snapshot.epoch,
2918
2946
  visibleMarked,
2919
2947
  ...(foldedOrigin !== undefined ? { foldedOrigin } : {}),
2948
+ ...(attested ? { mintExposure: "partitioned" } : {}),
2920
2949
  products: assembled,
2921
2950
  productStates: Object.fromEntries(assembled.map((e) => [e.id, "pending"])),
2922
2951
  ...(freezeRefusedInputIds.length > 0 ? { freezeRefusedInputIds } : {}),
@@ -2924,7 +2953,21 @@ export class MemoryEngine {
2924
2953
  intents: intents.map((it) => ({ requestId: it.requestId, state: "pending" })),
2925
2954
  state: "open",
2926
2955
  audit: [
2927
- { at, event: "frozen", requestId: input.requestId, ...(refusedProducts.length > 0 ? { detail: `${refusedProducts.length} product(s) refused at freeze: ${refusedProducts.map((r) => `#${r.index}: ${r.reason}`).join(" | ")}` } : {}) },
2956
+ {
2957
+ at,
2958
+ event: "frozen",
2959
+ requestId: input.requestId,
2960
+ ...((() => {
2961
+ const parts = [];
2962
+ if (attested) {
2963
+ const markedProducts = assembled.filter((e) => e.frontmatter.origin !== undefined).length;
2964
+ parts.push(`attested exposure-partitioned mint: ${markedProducts} marked / ${assembled.length - markedProducts} clean product(s)`);
2965
+ }
2966
+ if (refusedProducts.length > 0)
2967
+ parts.push(`${refusedProducts.length} product(s) refused at freeze: ${refusedProducts.map((r) => `#${r.index}: ${r.reason}`).join(" | ")}`);
2968
+ return parts.length > 0 ? { detail: parts.join("; ") } : {};
2969
+ })()),
2970
+ },
2928
2971
  ],
2929
2972
  };
2930
2973
  writeConsolidationPlan(this.controlDir, plan);
@@ -3492,7 +3535,7 @@ export class MemoryEngine {
3492
3535
  if (read.state === "absent")
3493
3536
  continue;
3494
3537
  const p = read.plan;
3495
- out.push({ planId, scope: p.scope, state: p.state, createdAt: p.createdAt, products: p.products.length, directed: p.directed.length, intents: p.intents.length });
3538
+ out.push({ planId, scope: p.scope, state: p.state, createdAt: p.createdAt, products: p.products.length, markedProducts: p.products.filter((e) => e.frontmatter?.origin !== undefined).length, directed: p.directed.length, intents: p.intents.length });
3496
3539
  }
3497
3540
  return out;
3498
3541
  }
@@ -9,7 +9,7 @@ export { readV2HeaderHints, isInstructionEntry, type V2HeaderHints } from "./hea
9
9
  export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, committedOriginOf, originEquals, ambiguousOriginRepresentation, type ParsedEntryFile } from "./frontmatter.js";
10
10
  export { committedDistilledOf, distilledEquals } from "./frontmatter.js";
11
11
  export { CONSOLIDATION_DEFAULTS, ConsolidationRefusedError, MEMORY_SEARCH_SUPERSEDED_TAG, consolidationTypeEligible, deriveSupersededSet, memorySupersededNote, readIntentCredentials, supersessionFuseCeiling, type ConsolidationGateRead, type ConsolidationGateRow, type ConsolidationIntent, type ConsolidationIntentCredentialRow, type ConsolidationLeaseSeat, type ConsolidationProductProposal, type ConsolidationProposal, type MemoryConsolidationOptions, CONSOLIDATION_RUN_STOP_REASONS, type ConsolidationRunStopReason, } from "./consolidation.js";
12
- export { DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT, LLM_DISTILLER_CONTRACT, LLM_DISTILLER_CONTRACT_DL2, LLM_DISTILLER_CONTRACT_DL3, LLM_DISTILLER_CONTRACTS, MEMORY_DISTILLER_CONTRACT_V1, contractGroupingDiff, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintLlmConsolidationPlan, openAiCompatChatSeat, parseJsonAnswer, planParseRepairs, sanitizeLlmGroups, scheduleUnderFuse, type ConsolidationDistillFn, type ConsolidationDriveCycleRow, type ConsolidationDriveEngine, type ConsolidationDriveResult, type ConsolidationFoldState, type DistillerCandidate, type DistillerChatAnswer, type DistillerChatFn, type DistillerChatRequest, type FuseSchedule, type LlmConsolidationPlan, type LlmConsolidationPlanProduct, type LlmDistillerContract, type MintLlmConsolidationPlanResult, type PlanParseRepairs, type SanitizedLlmGroups, } from "./distiller.js";
12
+ export { DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT, LLM_DISTILLER_CONTRACT, LLM_DISTILLER_CONTRACT_DL2, LLM_DISTILLER_CONTRACT_DL3, LLM_DISTILLER_CONTRACTS, MEMORY_DISTILLER_CONTRACT_V1, contractGroupingDiff, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintExposurePartitionedPlan, mintLlmConsolidationPlan, openAiCompatChatSeat, parseJsonAnswer, planParseRepairs, sanitizeLlmGroups, scheduleUnderFuse, type ConsolidationDistillFn, type ConsolidationDriveCycleRow, type ConsolidationDriveEngine, type ConsolidationDriveResult, type ConsolidationFoldState, type DistillerCandidate, type DistillerChatAnswer, type DistillerChatFn, type DistillerChatRequest, type FuseSchedule, type LlmConsolidationPlan, type LlmConsolidationPlanArm, type LlmConsolidationPlanProduct, type LlmDistillerContract, type MintLlmConsolidationPlanResult, type PlanParseRepairs, type SanitizedLlmGroups, } from "./distiller.js";
13
13
  export { CONSOLIDATION_DRIVER_PLANS_DIR, CONSOLIDATION_DRIVER_RUNS_FILE, archiveDistillerPlan, readConsolidationDriverRun, runMemoryConsolidationDriver, type ConsolidationDriverEngine, type ConsolidationDriverRunRow, type ConsolidationRunReceipt, type RunMemoryConsolidationOptions, } from "./consolidation-driver.js";
14
14
  export type { OriginClearanceRow, OriginClearanceEvent } from "./origin-clearance.js";
15
15
  export { MEMORY_ORIGIN_CAUSES } from "./types.js";
@@ -9,7 +9,7 @@ export { readV2HeaderHints, isInstructionEntry } from "./header-hints.js";
9
9
  export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, committedOriginOf, originEquals, ambiguousOriginRepresentation } from "./frontmatter.js";
10
10
  export { committedDistilledOf, distilledEquals } from "./frontmatter.js";
11
11
  export { CONSOLIDATION_DEFAULTS, ConsolidationRefusedError, MEMORY_SEARCH_SUPERSEDED_TAG, consolidationTypeEligible, deriveSupersededSet, memorySupersededNote, readIntentCredentials, supersessionFuseCeiling, CONSOLIDATION_RUN_STOP_REASONS, } from "./consolidation.js";
12
- export { DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT, LLM_DISTILLER_CONTRACT, LLM_DISTILLER_CONTRACT_DL2, LLM_DISTILLER_CONTRACT_DL3, LLM_DISTILLER_CONTRACTS, MEMORY_DISTILLER_CONTRACT_V1, contractGroupingDiff, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintLlmConsolidationPlan, openAiCompatChatSeat, parseJsonAnswer, planParseRepairs, sanitizeLlmGroups, scheduleUnderFuse, } from "./distiller.js";
12
+ export { DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT, LLM_DISTILLER_CONTRACT, LLM_DISTILLER_CONTRACT_DL2, LLM_DISTILLER_CONTRACT_DL3, LLM_DISTILLER_CONTRACTS, MEMORY_DISTILLER_CONTRACT_V1, contractGroupingDiff, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintExposurePartitionedPlan, mintLlmConsolidationPlan, openAiCompatChatSeat, parseJsonAnswer, planParseRepairs, sanitizeLlmGroups, scheduleUnderFuse, } from "./distiller.js";
13
13
  export { CONSOLIDATION_DRIVER_PLANS_DIR, CONSOLIDATION_DRIVER_RUNS_FILE, archiveDistillerPlan, readConsolidationDriverRun, runMemoryConsolidationDriver, } from "./consolidation-driver.js";
14
14
  export { MEMORY_ORIGIN_CAUSES } from "./types.js";
15
15
  export { memoryBackendContract, assertMemoryBackendSearchEquivalence, } from "./memory-backend-contract.js";
@@ -1490,9 +1490,12 @@ export interface RunInternals {
1490
1490
  * `TaskStream.detach(toolCallId)`; the hands Bash tool threads `signalFor(toolCallId)` into env.exec. */
1491
1491
  detachHub?: import("../tool-detach.js").ToolDetachHub;
1492
1492
  onTaskNotification?: (notification: TaskNotificationPayload,
1493
- /** CC injection priority (design/116 §7, re-anchored 2026-08-05): every priority delivers at
1494
- * the next turn boundary (arrival order, consecutive frames batch); "later" (default) vs
1495
- * "next" now differs only on the park/uplink forwarding path. */
1493
+ /** Injection tier (design/373 the ladder is LIVE): "next" = the running turn's next boundary
1494
+ * (arrival order, consecutive frames batch); "later" = the run's would-otherwise-stop seat
1495
+ * (never folded into work in progress); "now" = class-head + earliest natural boundary on this
1496
+ * lane (interrupt authority belongs to the steer face, never to notifications). Internal
1497
+ * producers declare their tier explicitly (§3.7 census — completion-class lanes are "next");
1498
+ * the parameterless default "later" serves the external verb's omitting callers only. */
1496
1499
  opts?: {
1497
1500
  priority?: import("../task-notification.js").SystemInjectionPriority;
1498
1501
  }) => void;
@@ -50,7 +50,7 @@ import { RunnerSharedToolResultStore } from "../tool-result-store.js";
50
50
  import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
51
51
  import { checkToolPolicyProjection, constraintChainDigest, constraintChainEntryOf, isApprovalSettledBy, refuseOutOfContractDecision, screenApproverAttribution, toolPolicyNameSets } from "../tool-policy.js";
52
52
  import { defaultTaskRegistry } from "../task-registry.js";
53
- import { discloseDroppedPending, isDelegatedAgentTerminal, isSystemInjectionPriority, PendingSessionNotifications, renderTaskNotificationXml, SYSTEM_INJECTION_PRIORITIES, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
53
+ import { discloseDroppedPending, isDelegatedAgentTerminal, isSystemInjectionPriority, isTerminalTaskNotification, PendingSessionNotifications, renderTaskNotificationXml, SYSTEM_INJECTION_PRIORITIES, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
54
54
  import { ToolDetachHub } from "../tool-detach.js";
55
55
  import { createPeerInboundChainRef, createPeerSelfRef } from "../../agents/peer-admission.js";
56
56
  import { workflowSizeGuidelineChangeNotice } from "../../orchestration/workflow-size-guideline.js";
@@ -78,6 +78,7 @@ function nextHumanInputSeq(key) {
78
78
  function sameAcceptedSteerInput(a, b) {
79
79
  return (a.payload === b.payload &&
80
80
  a.trusted === b.trusted &&
81
+ a.priority === b.priority &&
81
82
  a.actor?.id === b.actor?.id &&
82
83
  a.actor?.hostAsserted === b.actor?.hostAsserted &&
83
84
  a.actor?.issuer === b.actor?.issuer);
@@ -1829,6 +1830,11 @@ export class Runner {
1829
1830
  throw steeringError(`inputId "${LEGACY_PENDING_STEER_INPUT_ID}" is reserved for a pre-queue parked steer and cannot be supplied by a caller`, "steering.invalid_content");
1830
1831
  }
1831
1832
  }
1833
+ const priorityIn = options?.priority;
1834
+ if (priorityIn !== undefined && !isSystemInjectionPriority(priorityIn)) {
1835
+ throw steeringError(`priority must be one of ${SYSTEM_INJECTION_PRIORITIES.join("/")} when supplied`, "steering.invalid_content");
1836
+ }
1837
+ const priority = priorityIn ?? "next";
1832
1838
  const actorIn = options?.actor;
1833
1839
  const actor = actorIn === undefined ? undefined : snapshotActorAssertion(actorIn);
1834
1840
  const projected = projectHumanInput({ text, actor, source: "steer" });
@@ -1837,6 +1843,7 @@ export class Runner {
1837
1843
  text,
1838
1844
  trusted,
1839
1845
  inputId: effectiveInputId,
1846
+ ...(priorityIn !== undefined ? { priority } : {}),
1840
1847
  ...(actor !== undefined ? { actor } : {}),
1841
1848
  };
1842
1849
  let payload;
@@ -1872,7 +1879,7 @@ export class Runner {
1872
1879
  throw steeringError("the task is not running");
1873
1880
  payload = trusted ? formatHookFeedback(projected, h.reminderMark) : frameMidTurnUserInput(projected);
1874
1881
  mintsAFrame = payload.trim().length !== 0;
1875
- replay = { payload, trusted, ...(actor !== undefined ? { actor } : {}) };
1882
+ replay = { payload, trusted, priority, ...(actor !== undefined ? { actor } : {}) };
1876
1883
  if (typeof inputId === "string") {
1877
1884
  const prior = acceptedSteerInputs.get(inputId);
1878
1885
  if (prior !== undefined) {
@@ -1885,9 +1892,32 @@ export class Runner {
1885
1892
  return;
1886
1893
  }
1887
1894
  }
1888
- try {
1889
- await h.harness.steer(payload, { provenance: "engine-note", callerAuthored: true, parkRecord, ...(actor !== undefined ? { actor } : {}) });
1895
+ const injectFramed = async () => {
1896
+ const noteOptions = { provenance: "engine-note", callerAuthored: true, parkRecord, ...(actor !== undefined ? { actor } : {}) };
1897
+ if (priority === "later") {
1898
+ await h.harness.followUp(payload, noteOptions);
1899
+ noteAccepted(h);
1900
+ return;
1901
+ }
1902
+ const frame = await h.harness.steer(payload, { ...noteOptions, ...(priority === "now" ? { immediate: true } : {}) });
1890
1903
  noteAccepted(h);
1904
+ if (priority === "now" && frame !== undefined && h.harness.interruptTurn(frame)) {
1905
+ deliverEngineNotice(this.deps.onNotice, {
1906
+ code: "task.turn_interrupted",
1907
+ message: "a caller-provenance steer with priority \"now\" interrupted the running turn: in-flight work was cut at " +
1908
+ "a manufactured boundary (finished tool calls keep their real results; never-started ones settle as " +
1909
+ "interrupted) and the run continues with the steer at the queue head.",
1910
+ detail: {
1911
+ inputId: effectiveInputId,
1912
+ sessionId: h.sessionId,
1913
+ ...(actor?.id !== undefined ? { actorId: actor.id } : {}),
1914
+ ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}),
1915
+ },
1916
+ });
1917
+ }
1918
+ };
1919
+ try {
1920
+ await injectFramed();
1891
1921
  return;
1892
1922
  }
1893
1923
  catch (e) {
@@ -1897,8 +1927,7 @@ export class Runner {
1897
1927
  const birthDeadline = Date.now() + READY_TIMEOUT_MS;
1898
1928
  while (resultValue === undefined && !h.loop.ended && Date.now() < birthDeadline) {
1899
1929
  try {
1900
- await h.harness.steer(payload, { provenance: "engine-note", callerAuthored: true, parkRecord, ...(actor !== undefined ? { actor } : {}) });
1901
- noteAccepted(h);
1930
+ await injectFramed();
1902
1931
  return;
1903
1932
  }
1904
1933
  catch (e2) {
@@ -1938,9 +1967,14 @@ export class Runner {
1938
1967
  if (input.source !== undefined && typeof input.source !== "string") {
1939
1968
  throw notifyError("input.source must be a string when present", "notify.invalid_payload");
1940
1969
  }
1941
- if (opts?.priority !== undefined && !isSystemInjectionPriority(opts.priority)) {
1970
+ const priorityIn = opts?.priority;
1971
+ if (priorityIn !== undefined && !isSystemInjectionPriority(priorityIn)) {
1942
1972
  throw notifyError(`opts.priority must be one of ${SYSTEM_INJECTION_PRIORITIES.join("/")} when present`, "notify.invalid_payload");
1943
1973
  }
1974
+ const priority = priorityIn;
1975
+ if (priority === "now") {
1976
+ throw notifyError('priority "now" (turn interrupt) is not a notification-lane power — it belongs to the steer face; use "next" for earliest-boundary delivery', "notify.invalid_priority");
1977
+ }
1944
1978
  const payload = {
1945
1979
  task_id: input.task_id,
1946
1980
  task_type: "external",
@@ -1954,7 +1988,7 @@ export class Runner {
1954
1988
  if (!h || notifyRef.inject === undefined) {
1955
1989
  throw notifyError("the task is not running", "notify.not_running");
1956
1990
  }
1957
- notifyRef.inject(payload, opts);
1991
+ notifyRef.inject(payload, priority !== undefined ? { priority } : undefined);
1958
1992
  },
1959
1993
  compact: async (opts) => {
1960
1994
  if (resultValue)
@@ -2050,7 +2084,7 @@ export class Runner {
2050
2084
  let descendantAnchorDisclosed = false;
2051
2085
  const parkTaskNotification = (payload, priority) => {
2052
2086
  if (notificationSessionId !== undefined)
2053
- this.pendingSessionNotifications.pend(notificationSessionId, payload);
2087
+ this.pendingSessionNotifications.pend(notificationSessionId, payload, priority);
2054
2088
  if (!isDelegatedAgentTerminal(payload))
2055
2089
  return;
2056
2090
  const uplink = internals?.parentNotify;
@@ -2065,7 +2099,7 @@ export class Runner {
2065
2099
  }
2066
2100
  const rootAnchor = internals?.rootSessionId;
2067
2101
  if (rootAnchor !== undefined && rootAnchor !== notificationSessionId) {
2068
- this.pendingSessionNotifications.pend(rootAnchor, payload);
2102
+ this.pendingSessionNotifications.pend(rootAnchor, payload, priority);
2069
2103
  return;
2070
2104
  }
2071
2105
  if (internals?.parentSessionId !== undefined && !descendantAnchorDisclosed) {
@@ -2074,10 +2108,17 @@ export class Runner {
2074
2108
  }
2075
2109
  };
2076
2110
  const unsubscribeTaskNotifications = taskNotificationQueue.subscribe((item) => {
2077
- queue.push({ type: "task_notification", notification: item.payload, ...notificationIdent() });
2111
+ queue.push({ type: "task_notification", notification: item.payload, priority: item.priority, ...notificationIdent() });
2078
2112
  if (notificationHarness && prepared.batchHaltRef.current === undefined) {
2079
2113
  const xml = renderTaskNotificationXml(item.payload);
2080
- const deliver = notificationHarness.steer(xml, { provenance: "engine-note", enginePayload: item.payload });
2114
+ const noteOptions = {
2115
+ provenance: "engine-note",
2116
+ enginePayload: item.payload,
2117
+ ...(item.payload.task_type !== "external" && isTerminalTaskNotification(item.payload) ? { capPreferred: true } : {}),
2118
+ };
2119
+ const deliver = item.priority === "later"
2120
+ ? notificationHarness.followUp(xml, noteOptions)
2121
+ : notificationHarness.steer(xml, { ...noteOptions, ...(item.priority === "now" ? { immediate: true } : {}) });
2081
2122
  void deliver.then(() => item.onDisposition?.("queued"), () => {
2082
2123
  parkTaskNotification(item.payload, item.priority);
2083
2124
  item.onDisposition?.("parked");
@@ -2090,18 +2131,7 @@ export class Runner {
2090
2131
  });
2091
2132
  const upstreamTaskNotification = internals?.onTaskNotification;
2092
2133
  const deliveredAtTurnOpen = new Set();
2093
- let priorityNowDisclosed = false;
2094
2134
  const injectTaskNotification = (notification, opts) => {
2095
- if (opts?.priority === "now" && !priorityNowDisclosed) {
2096
- priorityNowDisclosed = true;
2097
- deliverEngineNotice(this.deps.onNotice, {
2098
- code: "task.injection_priority_unimplemented",
2099
- message: `a notification was injected with priority "now", which this engine does not implement: all injection ` +
2100
- `priorities deliver at the NEXT turn boundary and none aborts the running turn. The notification is ` +
2101
- `delivered — only the interrupting semantics are absent.`,
2102
- detail: { priority: "now", ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), sessionId: prepared.sessionId },
2103
- });
2104
- }
2105
2135
  if (deliveredAtTurnOpen.has(taskNotificationDedupKey(notification)))
2106
2136
  return Promise.resolve("dropped_duplicate");
2107
2137
  if (!notificationLaneLive) {
@@ -2196,16 +2226,17 @@ export class Runner {
2196
2226
  const pendingIdle = this.pendingSessionNotifications.drain(prepared.sessionId);
2197
2227
  if (pendingIdle !== undefined) {
2198
2228
  for (const payload of discloseDroppedPending(pendingIdle)) {
2229
+ const parkedPriority = pendingIdle.priorities?.get(payload);
2199
2230
  deliveredAtTurnOpen.add(taskNotificationDedupKey(payload));
2200
- queue.push({ type: "task_notification", notification: payload, ...ident() });
2231
+ queue.push({ type: "task_notification", notification: payload, ...(parkedPriority !== undefined ? { priority: parkedPriority } : {}), ...ident() });
2201
2232
  void prepared.harness.nextTurn(renderTaskNotificationXml(payload), { provenance: "engine-note", enginePayload: payload }).catch(() => {
2202
- this.pendingSessionNotifications.pend(prepared.sessionId, payload);
2233
+ this.pendingSessionNotifications.pend(prepared.sessionId, payload, parkedPriority);
2203
2234
  });
2204
2235
  }
2205
2236
  }
2206
2237
  }
2207
2238
  const loopLatch = { ended: false, userInterrupted: false };
2208
- onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark });
2239
+ onReady({ harness: prepared.harness, abortController: prepared.abortController, loop: loopLatch, reminderMark: prepared.reminderMark, sessionId: prepared.sessionId });
2209
2240
  const stats = { turns: 0, tokens: 0, toolCalls: 0, promptTokens: 0, totalInputTokens: 0, cachedTokens: 0, cacheWriteTokens: 0, cacheWriteTokensLong: 0, outputTokens: 0, costMicroUsd: 0 };
2210
2241
  prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
2211
2242
  if (resume &&