@sema-agent/core 7.20.1 → 7.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/dist/core/ask-origin.d.ts +95 -8
  3. package/dist/core/ask-origin.js +37 -5
  4. package/dist/core/gate-bounded-await.d.ts +6 -1
  5. package/dist/core/gate-bounded-await.js +13 -3
  6. package/dist/core/gate-fold.js +18 -5
  7. package/dist/core/gate-lanes.js +6 -12
  8. package/dist/core/memory-engine/consolidation-driver.d.ts +3 -0
  9. package/dist/core/memory-engine/consolidation-driver.js +20 -1
  10. package/dist/core/memory-engine/consolidation.d.ts +7 -0
  11. package/dist/core/memory-engine/consolidation.js +4 -1
  12. package/dist/core/memory-engine/distiller.d.ts +26 -1
  13. package/dist/core/memory-engine/distiller.js +9 -2
  14. package/dist/core/path-identity.d.ts +13 -0
  15. package/dist/core/path-identity.js +14 -0
  16. package/dist/core/runner/env-capability-census.d.ts +3 -1
  17. package/dist/core/runner/env-capability-census.js +24 -7
  18. package/dist/core/runner/gate-exit.d.ts +7 -0
  19. package/dist/core/runner/inherited-ask-grants.d.ts +1 -0
  20. package/dist/core/runner/inherited-ask-grants.js +5 -2
  21. package/dist/core/runner/memory-consolidation-driver.d.ts +73 -2
  22. package/dist/core/runner/memory-consolidation-driver.js +57 -0
  23. package/dist/core/runner/prepare-config-doors.js +2 -0
  24. package/dist/core/runner/prepare-policy-chain.d.ts +1 -0
  25. package/dist/core/runner/prepare-policy-chain.js +52 -20
  26. package/dist/core/runner/prepare-wiring-manifest.js +2 -0
  27. package/dist/core/runner/run-settle-and-teardown.js +5 -0
  28. package/dist/core/runner-deps.d.ts +21 -0
  29. package/dist/core/sensitive-path-policy.js +13 -0
  30. package/dist/core/wiring-manifest.d.ts +16 -0
  31. package/dist/core/wiring-manifest.js +1 -0
  32. package/dist/core/write-protect.d.ts +14 -2
  33. package/dist/core/write-protect.js +36 -13
  34. package/dist/engine/execution-env/node-execution-env.d.ts +10 -0
  35. package/dist/engine/execution-env/node-execution-env.js +29 -1
  36. package/dist/engine/harness/types.d.ts +10 -0
  37. package/dist/index.d.ts +1 -1
  38. package/dist/internal/harness-types.d.ts +1 -1
  39. package/dist/tools/fs/bash-readonly-classifier.js +35 -6
  40. package/dist/tools/fs/read-deny.d.ts +4 -2
  41. package/dist/tools/fs/read-deny.js +5 -0
  42. package/dist/tools/fs/safety.d.ts +12 -0
  43. package/dist/tools/fs/safety.js +43 -38
  44. package/package.json +2 -1
  45. package/test/export-surface.snapshot.json +3 -1
  46. package/wire-changes/core@7.21.0.json +452 -0
  47. package/wire-changes/core@7.21.1.json +41 -0
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
3
3
  import { pathTargetValue, protectivePathTargetOf } from "../../core/tool-registry.js";
4
+ import { withGateTimeout, DEFAULT_GATE_AWAIT_TIMEOUT_MS } from "../../core/gate-bounded-await.js";
4
5
  export function fileArgPath(args) {
5
6
  const a = args;
6
7
  const fp = a?.file_path;
@@ -291,29 +292,6 @@ function stripWin32ExtendedPrefix(p) {
291
292
  }
292
293
  return unc ? "\\\\" + rest.slice(unc[0].length) : rest;
293
294
  }
294
- function foldUncLexically(p) {
295
- const body = p.slice(2);
296
- const segs = body.split(/[\\/]+/).filter((s) => s !== "");
297
- const host = segs.shift() ?? "";
298
- const share = segs.shift();
299
- const out = [];
300
- let climbed = false;
301
- for (const seg of segs) {
302
- if (seg === ".")
303
- continue;
304
- if (seg === "..") {
305
- if (out.length === 0)
306
- climbed = true;
307
- else
308
- out.pop();
309
- continue;
310
- }
311
- const stripped = seg.replace(/[. ]+$/, "");
312
- out.push(stripped.length > 0 ? stripped : seg);
313
- }
314
- const anchor = `\\\\${host}${share !== undefined ? `\\${share}` : ""}`;
315
- return { key: out.length > 0 ? `${anchor}\\${out.join("\\")}` : anchor, climbedAboveShare: climbed };
316
- }
317
295
  export function hasWin32AliasedComponent(p) {
318
296
  if (!isWinFormPath(p))
319
297
  return false;
@@ -325,7 +303,7 @@ export function pathFamilyOf(bases) {
325
303
  const tree = bases.root ?? bases.cwd;
326
304
  return tree === undefined ? undefined : isWinFormPath(tree) ? "win" : "posix";
327
305
  }
328
- function isUncSpelling(p) {
306
+ export function isUncSpelling(p) {
329
307
  return /^[\\/]{2}[^\\/]+[\\/]+[^\\/]/.test(p);
330
308
  }
331
309
  export function isAbsoluteForFamily(family, p) {
@@ -511,16 +489,26 @@ export async function canonicalizeTarget(env, path, signal, family, baseCwd) {
511
489
  if (!screened.ok) {
512
490
  return { ok: false, message: `path "${clipSpelling(path)}" ${WIN32_NAMESPACE_REFUSAL_TEXT[screened.refuse]}; refused (${screened.refuse}).`, refused: screened.refuse };
513
491
  }
514
- const unprefixed = screened.spelled;
515
- if (family === "win" && isUncSpelling(unprefixed)) {
516
- const folded = foldUncLexically(unprefixed);
517
- if (folded.climbedAboveShare) {
518
- return { ok: false, message: `path "${path}" climbs above its UNC share root via ".." (\\\\host\\share is the root of a network path); refused.` };
519
- }
520
- return { ok: true, key: folded.key };
521
- }
492
+ const unprefixed = nativeUncSpellingOf(family, screened.spelled);
522
493
  const spelled = family === "posix" && unprefixed.startsWith("//") ? unprefixed.replace(/^\/+/, "/") : unprefixed;
523
494
  const target = baseCwd && !isAbsoluteForFamily(family, spelled) ? joinForFamily(family, baseCwd, spelled) : spelled;
495
+ let thrownByProbe;
496
+ let seenThrow = false;
497
+ const probe = probeRealTarget(env, path, target, family, signal).catch((e) => {
498
+ thrownByProbe = e;
499
+ seenThrow = true;
500
+ throw e;
501
+ });
502
+ try {
503
+ return await withGateTimeout(probe, DEFAULT_GATE_AWAIT_TIMEOUT_MS, signal, "pathCanonicalization");
504
+ }
505
+ catch (err) {
506
+ if (seenThrow && err === thrownByProbe)
507
+ throw err;
508
+ return { ok: false, message: `cannot resolve path "${path}": ${err instanceof Error ? err.message : String(err)}` };
509
+ }
510
+ }
511
+ async function probeRealTarget(env, path, target, family, signal) {
524
512
  const absR = await env.absolutePath(target, signal);
525
513
  if (!absR.ok)
526
514
  return { ok: false, message: `cannot resolve path "${path}": ${absR.error.message}` };
@@ -531,7 +519,7 @@ export async function canonicalizeTarget(env, path, signal, family, baseCwd) {
531
519
  return { ok: false, message: `cannot determine whether "${path}" exists: ${exists.error.message}`, unresolvedSymlink: true };
532
520
  }
533
521
  if (!exists.value) {
534
- const newPath = await canonicalizeNewPath(env, abs, signal);
522
+ const newPath = await canonicalizeNewPath(env, abs, family, signal);
535
523
  if (!newPath.ok) {
536
524
  return { ok: false, message: newPath.message, unresolvedSymlink: true };
537
525
  }
@@ -545,7 +533,7 @@ export async function canonicalizeTarget(env, path, signal, family, baseCwd) {
545
533
  return { ok: false, message: `cannot determine whether "${path}" is a symlink: ${info.error.message}`, unresolvedSymlink: true };
546
534
  }
547
535
  if (info.value.kind !== "symlink") {
548
- const viaAncestors = await canonicalizeNewPath(env, abs, signal, { skipLeaf: true });
536
+ const viaAncestors = await canonicalizeNewPath(env, abs, family, signal, { skipLeaf: true });
549
537
  if (!viaAncestors.ok) {
550
538
  return { ok: false, message: viaAncestors.message, unresolvedSymlink: true };
551
539
  }
@@ -574,13 +562,26 @@ function parentDir(abs) {
574
562
  const head = abs.slice(0, i);
575
563
  return /^[A-Za-z]:$/.test(head) ? head + abs[i] : head;
576
564
  }
577
- async function canonicalizeNewPath(env, abs, signal, opts) {
565
+ function uncAnchorIndex(parts) {
566
+ let seen = 0;
567
+ for (let i = 0; i < parts.length; i++) {
568
+ if (parts[i] === "")
569
+ continue;
570
+ seen++;
571
+ if (seen === 2)
572
+ return i;
573
+ }
574
+ return parts.length;
575
+ }
576
+ async function canonicalizeNewPath(env, abs, family, signal, opts) {
578
577
  const sep = nativeSepOf(abs);
579
- const parts = isWinFormPath(abs) ? abs.split(/[\\/]/) : abs.split("/");
578
+ const winForm = family === "win" || isWinFormPath(abs);
579
+ const parts = winForm ? abs.split(/[\\/]/) : abs.split("/");
580
580
  const tail = [];
581
+ const floor = family === "win" && isUncSpelling(abs) ? uncAnchorIndex(parts) : 1;
581
582
  if (opts?.skipLeaf === true && parts.length > 1)
582
583
  tail.push(parts.pop());
583
- while (parts.length > 1) {
584
+ while (parts.length > floor) {
584
585
  const candidate = parts.join(sep) || sep;
585
586
  const ex = await env.exists(candidate, signal);
586
587
  if (!ex.ok) {
@@ -591,7 +592,11 @@ async function canonicalizeNewPath(env, abs, signal, opts) {
591
592
  if (!canon.ok) {
592
593
  return { ok: false, message: `cannot canonicalize existing ancestor "${candidate}": ${canon.error.message}` };
593
594
  }
594
- return { ok: true, key: [canon.value, ...tail.reverse()].join(nativeSepOf(canon.value)) };
595
+ const tailSegments = tail.reverse();
596
+ const canonSep = nativeSepOf(canon.value);
597
+ if (tailSegments.length === 0)
598
+ return { ok: true, key: canon.value };
599
+ return { ok: true, key: [canon.value.replace(family === "win" ? /[\\/]+$/ : /\/+$/, ""), ...tailSegments].join(canonSep) };
595
600
  }
596
601
  tail.push(parts.pop());
597
602
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.20.1",
3
+ "version": "7.21.1",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -88,6 +88,7 @@
88
88
  "gate:docs-tool-names": "node scripts/verify-docs-tool-names.mjs",
89
89
  "gate:comment-ratio": "vitest run test/comment-ratio-ratchet.test.ts",
90
90
  "gate:negative-controls": "vitest run test/gate-negative-controls.test.ts",
91
+ "gate:memory-ga": "node scripts/gate-memory-distillation-ga.mjs",
91
92
  "gate:all": "node scripts/gate-all.mjs",
92
93
  "car:gate": "node scripts/car-gate.mjs"
93
94
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_comment": "design/87 L3 — frozen public export surface of src/index.ts (name -> kind). DO NOT edit by hand to silence a red test. A removed/changed entry = a SemVer-BREAKING change; bump MAJOR and update this fixture in the SAME commit (design/87 §4.2 / §5.2). Regenerate via REGEN in test/export-surface.test.ts.",
3
3
  "_tierComment": "#435 v1 — machine-readable layering of the public surface: stable = demonstrated by README.md / src/examples; internal = an `Internal`-marked name or a runner/engine deep-subtree declaration (the model seam src/engine/llm is excluded — it is the BYOM contract, not an engine internal); advanced = a supported export the front door does not walk you through. THIS IS AN INITIAL HEURISTIC, derived mechanically and expected to be refined ticket by ticket: no human reviewed these 1700+ entries one by one, and nothing here claims otherwise. Known bias: a short or English-word export name (ok, err, Result, Usage) can match ordinary prose in README.md and land `stable` on a coincidence. Every export MUST carry a tier — a new export with no row fails the gate in export-surface.test.ts.",
4
- "count": 2389,
4
+ "count": 2390,
5
5
  "exports": {
6
6
  "A2ATaskState": "type",
7
7
  "A2ATaskStateReversal": "type",
@@ -1369,6 +1369,7 @@
1369
1369
  "SummarizableFinding": "interface",
1370
1370
  "SwappableDepSeat": "type",
1371
1371
  "SwappableDeps": "type",
1372
+ "SymlinkChain": "interface",
1372
1373
  "SyncMemoryScopeOptions": "interface",
1373
1374
  "SyntheticContinuationReason": "type",
1374
1375
  "SystemInjection": "interface",
@@ -3760,6 +3761,7 @@
3760
3761
  "SummarizableFinding": "advanced",
3761
3762
  "SwappableDepSeat": "advanced",
3762
3763
  "SwappableDeps": "advanced",
3764
+ "SymlinkChain": "internal",
3763
3765
  "SyncMemoryScopeOptions": "advanced",
3764
3766
  "SyntheticContinuationReason": "advanced",
3765
3767
  "SystemInjection": "advanced",
@@ -0,0 +1,452 @@
1
+ {
2
+ "repo": "core",
3
+ "version": "7.21.0",
4
+ "from": "6f1349738f7d9d23f81326f1fadcd09fbdca3283",
5
+ "to": "v7.21.0",
6
+ "added": [
7
+ {
8
+ "name": "SymlinkChain",
9
+ "kind": "interface",
10
+ "tier": "internal",
11
+ "at": [
12
+ "src/engine/harness/types.ts:400",
13
+ "src/index.ts:511",
14
+ "src/internal/harness-types.ts:33"
15
+ ],
16
+ "criteriaCovered": true,
17
+ "breaking": false
18
+ }
19
+ ],
20
+ "removed": [],
21
+ "changed": [],
22
+ "narrowed": [
23
+ {
24
+ "id": "F2",
25
+ "source": "behaviorFlips.summary",
26
+ "text": "NARROWING (allow -> ask), roots face, unlisted programs (#860; main session): a `NAME=value` word offers its VALUE as a path candidate BESIDE the whole word, judged where the value points. ONE arm read by both boundary faces (`collectSegmentBoundaryFindings`, `src/tools/fs/bash-readonly-classifier.ts:3219-3223`) and by the refused-whole word scan (`unlistedSegmentJudgedInside`, `:1099-1112` and `:1140`), including the LEADING environment words bash strips before choosing the program (`:1164`, `:1178`, `:1183`; launcher scan re-headed from the FIRST token at `:4417`). Who needs it: every roots-face deployment — `dd if=/etc/passwd` read outside the roots at ZERO asks under the default doctrine, because the whole word `if=/etc/passwd` contains a separator and resolved to `./if=/etc/passwd`, inside. Who is hurt: roots-face runs of `make PREFIX=/usr/local`, `docker run -e HOME=/root ...` and every other `NAME=<outside path>` spelling now pay one mandated ask (headless: refused). Compensation: path-less and in-roots values stay at zero asks on both faces, and the open face vouches for a plain value (only the deny half speaks there)."
27
+ },
28
+ {
29
+ "id": "F3",
30
+ "source": "behaviorFlips.summary",
31
+ "text": "NARROWING (allow -> ask), BOTH faces, the two spellings the folded text cannot settle (#860, second half): a value carrying a tilde (`assignmentWordPayload`, `src/tools/fs/bash-readonly-classifier.ts:2971-2978`; `value.includes(\"~\")` at `:2976`) and the APPEND form `NAME+=value` (`:2975`, regex `ASSIGNMENT_OR_APPEND_WORD` at `:2969`) are reported UNRESOLVED and ask on both faces, never guessed — bash expands a tilde in such a word only where it is unquoted, and the folded word no longer says which. STATED OVER-FIRE, measured on the merge tree: a LISTED reader handed `NAME=<outside path>` as a POSITIONAL operand (`cat a=/etc/passwd`) now asks on the roots face too (the card carries a `readRootCandidate` way out); the open face vouches for it. Who is hurt: those two spellings on an unlisted program, plus that one listed-reader shape. Compensation: the fail-closed direction, and the program-position reader is deliberately NOT widened to `+=` (a quoted `'A+=x'` is a command NAME to bash and reading it as an assignment would skip the real program — pinned as a negative control)."
32
+ },
33
+ {
34
+ "id": "F4",
35
+ "source": "behaviorFlips.summary",
36
+ "text": "NARROWING (a stored yes no longer answers it) (#850 / C-R52 / DV-817-1; car-850): the write-protection demotion becomes a member of the MANDATE family — `PERSISTED_RULE_MANDATES` (`src/core/ask-origin.ts:298`, now a registered closed set in `docs/CLOSED-SETS.md` with two total tables behind `AssertAllKeysHandled` fences at `:325` and `:358`), with the two new words `write_protection` and `write_protection_unresolved` placed on the ladder at `persistedRuleMandateOf` (`:398-403`). A matching persisted allow rule is DISCLOSED as shadowed (`persistedRuleShadowed` plus the word's own sentence from `MANDATE_DISCLOSURE`, `:345-357`) instead of clearing the ask. Who needs it: upstream parity — the reference CLI runs the write-protection judge BEFORE the general allow rules, so one stored 'allow this from now on' covering the path silently retired every later write onto the protected table. Who is hurt: a deployment whose store answers `allow` for a write tool (core's own DSL mints no such rule — `parseRuleText` refuses an allow on a path tool; an imported foreign settings store can hold one); those writes ask again, once per write. Compensation: an armed classifier and a person still clear it, and the durable park stays budgetable (`safetyAxisOf` is unchanged — this is not an irreversibility mandate)."
37
+ },
38
+ {
39
+ "id": "F5",
40
+ "source": "behaviorFlips.summary",
41
+ "text": "NARROWING (the same escape through a different door) (#850): the judge now ALSO runs over an ask another layer already raised (`src/core/gate-fold.ts:360-361`, `writeProtectionStandingAsk`) and merges the mandate ALONE — `decision !== undefined && !writeProtectionStandingAsk` at `:381` is what keeps the standing ask's action, message, provenance and disclosures BYTE-IDENTICAL. Who needs it: a deployment whose write face asks FIRST (`FsWriteGatePolicyOptions.defaultWrite: \"ask\"`, or any policy that asks) never reached the judge at all, so the standing rule cleared that question with the table never consulted. Who is hurt: those deployments' protected writes now ask again per write. Compensation: the pin is byte-equality against the same run with no judge mounted (G2.d), so nothing but the mandate word is merged."
42
+ },
43
+ {
44
+ "id": "F6",
45
+ "source": "behaviorFlips.summary",
46
+ "text": "ADDITIVE word + NARROWING on the automatic lanes, and one LOUD failure (#850): the judge's OTHER non-clear verdict carries `write_protection_unresolved` (`src/core/ask-origin.ts:398-401`; the pass cells `GatePass.writeProtected` / `GatePass.writeProtectionUnresolved` at `src/core/runner/gate-exit.ts:355-361`, written at `gate-fold.ts:378-379` through the two verdict predicates `writeProtectionMandated` / `writeProtectionUnresolved`, `src/core/write-protect.ts:730/:736`, contract sentence `write_protection.verdict_is_the_mandate`). The ACTION stays the owning arm's (a surviving allow is still denied; an ask another layer raised is untouched). TWO COLUMNS, not one word: `MANDATE_LANE_ANSWERS` (`ask-origin.ts:316-323`) gives this word `classifier: false` (the table's answer for this call is UNKNOWN) and `sandboxAdmission: true` (containment holds the effect whether or not the target is on the table). A judge that TIMES OUT or THROWS produces the same verdict AND reaches `RunnerDeps.onError(phase:\"hook\")` naming the judge, the tool and the callId (`gate-fold.ts:366-375` -> `traceHookCrash`, `gate-exit.ts:197` -> `notifyHookError`, `prepare-ask-lane.ts:342-344`). Who needs it: the cost of F4 is paid in AUTONOMY, so a broken judge must not silently stop every covered write from being handled automatically. Who is hurt: a deployment whose judge is SLOW keeps its writes behind a person or an approver seat on the ask-first path too."
47
+ },
48
+ {
49
+ "id": "F7",
50
+ "source": "behaviorFlips.summary",
51
+ "text": "NARROWING (allow -> ask), one alias shape, every deployment whose environment declares the new seat (#851 / C-R53 / DV-817-3; car-851): the write-protection judge judges every hop of the target's LEAF symlink chain (`src/core/write-protect.ts:673-680`), consulted LAST and ONLY for a terminal that names no row and is not inside the data root; a hop is judged as a SPELLING (`judgeSpelled` at `:614` — the data-root passthrough and the three readings apply) and a hit reports `via: \"chain\"` (`:679`) with its own ask sentence (`writeProtectionTightenDecision`, `:705-707`). Who needs it: `plainname -> <home>/.ssh/newfile` where `<home>/.ssh` is itself a symlink to an unguarded directory was cleared by both judges (measured on 7.20.0 by the black-box acceptance: zero asks, `disposition.kind: \"allowed\"`), while the reference CLI (2.1.259) judges every variant a path has and asks there. Who is hurt: such writes ask once where they execute (headless: refused). Compensation: the exemption by IDENTITY outranks a hop and ends the judgment; a 7.20.0 spelling or target hit keeps its own word and its byte-identical sentence; an environment that declares no chain is byte-identical."
52
+ },
53
+ {
54
+ "id": "F8",
55
+ "source": "behaviorFlips.summary",
56
+ "text": "NARROWING (allow -> deny), the same shape on the sensitive-path deny set (#851): `createSensitivePathPolicy().check` judges each hop as a WRITTEN spelling (both readings, no data-root passthrough — that exemption compares canonical keys and ends the judgment by identity) and refuses naming the pattern (`src/core/sensitive-path-policy.ts:470-484`; reached only when `relative === null`, i.e. the terminal is not inside the data root). Who needs it: deployments that mount the recommended deny set (the server does). Who is hurt: an aliased write whose chain crosses a guarded row is refused rather than executed, on every lane including headless. Compensation: pinned as CURRENT reach and not as a promise — the deny set still does not judge the SPELLING itself, so the direct `~/.ssh/x` under a symlinked `~/.ssh` stays allowed by this set (the table asks) while `plainname -> ~/.ssh/x` is refused; a follow-up ticket carries the spelling arm."
57
+ },
58
+ {
59
+ "id": "F14",
60
+ "source": "behaviorFlips.summary",
61
+ "text": "SECURITY / NARROWING (allow -> refuse), win-family trees only (#840 / C-R54; car-840): the UNC lexical arm of `canonicalizeTarget` is deleted (`foldUncLexically` with it), so a win-form `\\\\host\\share\\...` spelling takes the ordinary resolution path — resolve, follow the symlink chain, canonicalize the deepest existing ancestor (`src/tools/fs/safety.ts:1132` onwards; the probing half `probeRealTarget` at `:1240`). Who needs it: on a UNC-rooted workspace a link INSIDE the share was never followed, so a target spelled in-root whose real destination is another share (or a guarded directory) was admitted by root containment, the sensitive-path deny set, the fs write gate, the session `allowDirs` and a skill manifest's `allowPaths` alike — measured on the previous tree, all four fences. Who is hurt: such a spelling is now refused by whichever fence is wired (headless: the write/read is refused rather than executed); a UNC-rooted workspace whose files are ordinary files is unchanged. Compensation: non-win trees are byte-identical spelling for spelling, and the negative-control matrix was re-run against the previous tree."
62
+ }
63
+ ],
64
+ "closedSetMembers": [
65
+ {
66
+ "set": "GateWaitFailure",
67
+ "at": [
68
+ "src/core/gate-bounded-await.ts:24"
69
+ ],
70
+ "newSet": true,
71
+ "added": [
72
+ "aborted",
73
+ "timeout"
74
+ ],
75
+ "removed": [],
76
+ "breaking": false,
77
+ "criteriaCovered": false
78
+ },
79
+ {
80
+ "set": "PERSISTED_RULE_MANDATES",
81
+ "at": [
82
+ "src/core/ask-origin.ts:298"
83
+ ],
84
+ "newSet": true,
85
+ "added": [
86
+ "operator_always",
87
+ "probe_mandate",
88
+ "probe_unanswered",
89
+ "tool_marks",
90
+ "write_protection",
91
+ "write_protection_unresolved"
92
+ ],
93
+ "removed": [],
94
+ "breaking": false,
95
+ "criteriaCovered": false
96
+ }
97
+ ],
98
+ "closedSetsUnreadable": [],
99
+ "typeKeys": [
100
+ {
101
+ "type": "ExecutionEnv",
102
+ "at": [
103
+ "src/engine/harness/types.ts:599"
104
+ ],
105
+ "added": [
106
+ "canonicalChain"
107
+ ],
108
+ "removed": [],
109
+ "breaking": false,
110
+ "criteriaCovered": true
111
+ },
112
+ {
113
+ "type": "RemoteExecutionEnv",
114
+ "at": [
115
+ "src/core/remote-env.ts:313"
116
+ ],
117
+ "added": [
118
+ "canonicalChain"
119
+ ],
120
+ "removed": [],
121
+ "breaking": false,
122
+ "criteriaCovered": false
123
+ },
124
+ {
125
+ "type": "RunMemoryConsolidationOptions",
126
+ "at": [
127
+ "src/core/memory-engine/consolidation-driver.ts:234"
128
+ ],
129
+ "added": [
130
+ "onNotice"
131
+ ],
132
+ "removed": [],
133
+ "breaking": false,
134
+ "criteriaCovered": false
135
+ },
136
+ {
137
+ "type": "RunnerDeps",
138
+ "at": [
139
+ "src/core/runner-deps.ts:104"
140
+ ],
141
+ "added": [
142
+ "autoRunOnRecommendation"
143
+ ],
144
+ "removed": [],
145
+ "breaking": false,
146
+ "criteriaCovered": true
147
+ },
148
+ {
149
+ "type": "WiringFacts",
150
+ "at": [
151
+ "src/core/wiring-manifest.ts:378"
152
+ ],
153
+ "added": [
154
+ "autoConsolidationOnRecommendation"
155
+ ],
156
+ "removed": [],
157
+ "breaking": false,
158
+ "criteriaCovered": false
159
+ },
160
+ {
161
+ "type": "WiringManifest",
162
+ "at": [
163
+ "src/core/wiring-manifest.ts:164"
164
+ ],
165
+ "added": [
166
+ "autoConsolidation"
167
+ ],
168
+ "removed": [],
169
+ "breaking": false,
170
+ "criteriaCovered": true
171
+ }
172
+ ],
173
+ "declarationMoves": [],
174
+ "mintMoves": [],
175
+ "criteriaObserves": [
176
+ {
177
+ "kind": "engineNoticeCode",
178
+ "name": "config.execution_env_capability_invalid",
179
+ "assertions": [
180
+ "G4.a"
181
+ ]
182
+ },
183
+ {
184
+ "kind": "engineNoticeCode",
185
+ "name": "memory.consolidation_recommended",
186
+ "assertions": [
187
+ "G5.c"
188
+ ]
189
+ },
190
+ {
191
+ "kind": "taskEventField",
192
+ "name": "tool_end.errorCode",
193
+ "assertions": [
194
+ "G7.b"
195
+ ]
196
+ },
197
+ {
198
+ "kind": "taskEventField",
199
+ "name": "tool_end.isError",
200
+ "assertions": [
201
+ "G7.a"
202
+ ]
203
+ },
204
+ {
205
+ "kind": "taskEventField",
206
+ "name": "tool_end.structured",
207
+ "assertions": [
208
+ "G7.e"
209
+ ]
210
+ },
211
+ {
212
+ "kind": "typeMember",
213
+ "name": "AskRequest.toolName",
214
+ "assertions": [
215
+ "G2.d"
216
+ ]
217
+ },
218
+ {
219
+ "kind": "typeMember",
220
+ "name": "BashReadonlyRootBoundary.denyMatch",
221
+ "assertions": [
222
+ "G1.b"
223
+ ]
224
+ },
225
+ {
226
+ "kind": "typeMember",
227
+ "name": "BashReadonlyRootBoundary.face",
228
+ "assertions": [
229
+ "G1.a",
230
+ "G1.c",
231
+ "G1.e"
232
+ ]
233
+ },
234
+ {
235
+ "kind": "typeMember",
236
+ "name": "CompoundReadonlyVerdict.outOfRootPaths",
237
+ "assertions": [
238
+ "G1.d"
239
+ ]
240
+ },
241
+ {
242
+ "kind": "typeMember",
243
+ "name": "ExecutionEnv.canonicalChain",
244
+ "assertions": [
245
+ "G3.b",
246
+ "G3.f"
247
+ ]
248
+ },
249
+ {
250
+ "kind": "typeMember",
251
+ "name": "ExecutionEnv.canonicalPath",
252
+ "assertions": [
253
+ "G7.d"
254
+ ]
255
+ },
256
+ {
257
+ "kind": "typeMember",
258
+ "name": "FsWriteGatePolicyOptions.defaultWrite",
259
+ "assertions": [
260
+ "G2.c"
261
+ ]
262
+ },
263
+ {
264
+ "kind": "typeMember",
265
+ "name": "FsWriteGatePolicyOptions.exemptDirs",
266
+ "assertions": [
267
+ "G7.f"
268
+ ]
269
+ },
270
+ {
271
+ "kind": "typeMember",
272
+ "name": "GateDisposition.classifier",
273
+ "assertions": [
274
+ "G8.a"
275
+ ]
276
+ },
277
+ {
278
+ "kind": "typeMember",
279
+ "name": "GateDisposition.deniedBy",
280
+ "assertions": [
281
+ "G2.e",
282
+ "G3.c"
283
+ ]
284
+ },
285
+ {
286
+ "kind": "typeMember",
287
+ "name": "GateDisposition.kind",
288
+ "assertions": [
289
+ "G8.d"
290
+ ]
291
+ },
292
+ {
293
+ "kind": "typeMember",
294
+ "name": "GateOutcome.settlement",
295
+ "assertions": [
296
+ "G8.b"
297
+ ]
298
+ },
299
+ {
300
+ "kind": "typeMember",
301
+ "name": "LlmConsolidationPlan.minting",
302
+ "assertions": [
303
+ "G6.a"
304
+ ]
305
+ },
306
+ {
307
+ "kind": "typeMember",
308
+ "name": "LlmConsolidationPlan.minting.writingChannel",
309
+ "assertions": [
310
+ "G6.b"
311
+ ]
312
+ },
313
+ {
314
+ "kind": "typeMember",
315
+ "name": "PermissionResult.persistedRuleShadowed",
316
+ "assertions": [
317
+ "G2.a"
318
+ ]
319
+ },
320
+ {
321
+ "kind": "typeMember",
322
+ "name": "ReadDenyMatcher.matchTarget",
323
+ "assertions": [
324
+ "G3.e"
325
+ ]
326
+ },
327
+ {
328
+ "kind": "typeMember",
329
+ "name": "RemoteExecutionEnv.capabilities",
330
+ "assertions": [
331
+ "G4.b"
332
+ ]
333
+ },
334
+ {
335
+ "kind": "typeMember",
336
+ "name": "ReversibilityVerdict.mandated",
337
+ "assertions": [
338
+ "G1.f"
339
+ ]
340
+ },
341
+ {
342
+ "kind": "typeMember",
343
+ "name": "ReversibilityVerdict.readRootCandidate",
344
+ "assertions": [
345
+ "G7.c"
346
+ ]
347
+ },
348
+ {
349
+ "kind": "typeMember",
350
+ "name": "RunnerDeps.autoRunOnRecommendation",
351
+ "assertions": [
352
+ "G5.b"
353
+ ]
354
+ },
355
+ {
356
+ "kind": "typeMember",
357
+ "name": "RunnerDeps.memoryConsolidation",
358
+ "assertions": [
359
+ "G5.d"
360
+ ]
361
+ },
362
+ {
363
+ "kind": "typeMember",
364
+ "name": "RunnerDeps.memoryConsolidationDriver",
365
+ "assertions": [
366
+ "G5.e"
367
+ ]
368
+ },
369
+ {
370
+ "kind": "typeMember",
371
+ "name": "RunnerDeps.onError",
372
+ "assertions": [
373
+ "G2.f"
374
+ ]
375
+ },
376
+ {
377
+ "kind": "typeMember",
378
+ "name": "RunnerDeps.writeProtectedPaths",
379
+ "assertions": [
380
+ "G2.g"
381
+ ]
382
+ },
383
+ {
384
+ "kind": "typeMember",
385
+ "name": "Settlement.kind",
386
+ "assertions": [
387
+ "G2.b"
388
+ ]
389
+ },
390
+ {
391
+ "kind": "typeMember",
392
+ "name": "Settlement.who.approver",
393
+ "assertions": [
394
+ "G8.c"
395
+ ]
396
+ },
397
+ {
398
+ "kind": "typeMember",
399
+ "name": "SymlinkChain.hops",
400
+ "assertions": [
401
+ "G3.d"
402
+ ]
403
+ },
404
+ {
405
+ "kind": "typeMember",
406
+ "name": "TaskResult.stats.humanReview",
407
+ "assertions": [
408
+ "G8.e"
409
+ ]
410
+ },
411
+ {
412
+ "kind": "typeMember",
413
+ "name": "WiringManifest.autoConsolidation",
414
+ "assertions": [
415
+ "G5.a"
416
+ ]
417
+ },
418
+ {
419
+ "kind": "typeMember",
420
+ "name": "WriteProtectionVerdict.via",
421
+ "assertions": [
422
+ "G3.a"
423
+ ]
424
+ }
425
+ ],
426
+ "consumersHint": {
427
+ "cli": [
428
+ "ExecutionEnv",
429
+ "RemoteExecutionEnv",
430
+ "RunnerDeps",
431
+ "WiringFacts",
432
+ "WiringManifest"
433
+ ],
434
+ "server": [
435
+ "ExecutionEnv",
436
+ "RemoteExecutionEnv",
437
+ "RunMemoryConsolidationOptions",
438
+ "RunnerDeps",
439
+ "WiringFacts",
440
+ "WiringManifest"
441
+ ],
442
+ "sdk": [
443
+ "ExecutionEnv",
444
+ "RunnerDeps",
445
+ "WiringManifest"
446
+ ],
447
+ "note": "scanned cli@0f132216 server@6e121caf sdk@6209e5e — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it"
448
+ },
449
+ "breakingCount": 0,
450
+ "breakingItems": [],
451
+ "toResolvedAt": "abb48087842215283113c231f31d255691a3b8dc"
452
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "repo": "core",
3
+ "version": "7.21.1",
4
+ "from": "d1999920883607f4063bb8e311f0cc98633218c4",
5
+ "to": "v7.21.1",
6
+ "added": [],
7
+ "removed": [],
8
+ "changed": [],
9
+ "narrowed": [],
10
+ "closedSetMembers": [],
11
+ "closedSetsUnreadable": [],
12
+ "typeKeys": [],
13
+ "declarationMoves": [],
14
+ "mintMoves": [],
15
+ "criteriaObserves": [
16
+ {
17
+ "kind": "typeMember",
18
+ "name": "RunnerDeps.autoRunOnRecommendation",
19
+ "assertions": [
20
+ "G1.b",
21
+ "G1.c"
22
+ ]
23
+ },
24
+ {
25
+ "kind": "typeMember",
26
+ "name": "WiringManifest.autoConsolidation",
27
+ "assertions": [
28
+ "G1.a"
29
+ ]
30
+ }
31
+ ],
32
+ "consumersHint": {
33
+ "cli": [],
34
+ "server": [],
35
+ "sdk": [],
36
+ "note": "scanned cli@0f132216 server@6e121caf sdk@6209e5e — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it"
37
+ },
38
+ "breakingCount": 0,
39
+ "breakingItems": [],
40
+ "toResolvedAt": "33bdf10459de6fbf3a9ede670468f73f95203628"
41
+ }