@sema-agent/core 7.18.0 → 7.20.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 (137) hide show
  1. package/CHANGELOG.md +276 -2
  2. package/dist/agents/subagent.js +12 -20
  3. package/dist/agents/teacher.js +2 -5
  4. package/dist/agents/verify.js +21 -10
  5. package/dist/brain/anthropic.js +8 -3
  6. package/dist/brain/open-responses.js +12 -4
  7. package/dist/brain/openai.js +7 -2
  8. package/dist/brain/reasoning.d.ts +13 -13
  9. package/dist/brain/reasoning.js +2 -2
  10. package/dist/brain/stream-engine.d.ts +4 -0
  11. package/dist/brain/stream-engine.js +6 -1
  12. package/dist/brain/stream-shared.d.ts +3 -0
  13. package/dist/brain/stream-shared.js +1 -0
  14. package/dist/core/ask-origin.d.ts +16 -0
  15. package/dist/core/ask-origin.js +8 -0
  16. package/dist/core/auto-mode-arming.d.ts +6 -0
  17. package/dist/core/auto-mode-arming.js +5 -5
  18. package/dist/core/auto-mode-defaults.d.ts +43 -15
  19. package/dist/core/auto-mode-defaults.js +4 -1
  20. package/dist/core/auto-mode.d.ts +74 -26
  21. package/dist/core/auto-mode.js +80 -11
  22. package/dist/core/checkpoint-store.d.ts +7 -0
  23. package/dist/core/checkpoint-store.js +2 -2
  24. package/dist/core/child-clamp-seats.d.ts +79 -0
  25. package/dist/core/child-clamp-seats.js +29 -0
  26. package/dist/core/engine-notice.d.ts +89 -41
  27. package/dist/core/fs-write-gate-policy.d.ts +17 -9
  28. package/dist/core/fs-write-gate-policy.js +51 -4
  29. package/dist/core/gate-bounded-await.d.ts +38 -0
  30. package/dist/core/gate-bounded-await.js +53 -0
  31. package/dist/core/gate-fold.js +20 -48
  32. package/dist/core/gate-lanes.js +17 -9
  33. package/dist/core/gate-outcome.d.ts +34 -6
  34. package/dist/core/gate-outcome.js +7 -0
  35. package/dist/core/governance-codes.d.ts +16 -1
  36. package/dist/core/governance-codes.js +18 -0
  37. package/dist/core/hooks.d.ts +8 -8
  38. package/dist/core/memory-engine/engine.d.ts +14 -9
  39. package/dist/core/memory-engine/engine.js +21 -8
  40. package/dist/core/memory-engine/layout.d.ts +31 -0
  41. package/dist/core/memory-engine/layout.js +68 -3
  42. package/dist/core/memory-engine/types.d.ts +24 -0
  43. package/dist/core/memory.d.ts +19 -0
  44. package/dist/core/memory.js +12 -0
  45. package/dist/core/read-root-candidate.d.ts +12 -0
  46. package/dist/core/read-root-candidate.js +24 -0
  47. package/dist/core/remote-env.d.ts +32 -0
  48. package/dist/core/remote-env.js +4 -1
  49. package/dist/core/roles.d.ts +9 -1
  50. package/dist/core/roles.js +4 -0
  51. package/dist/core/runner/advertised-writable-dirs.d.ts +59 -1
  52. package/dist/core/runner/advertised-writable-dirs.js +51 -3
  53. package/dist/core/runner/announce-once-ledger.d.ts +15 -0
  54. package/dist/core/runner/announce-once-ledger.js +8 -0
  55. package/dist/core/runner/classifier-ladder.d.ts +154 -11
  56. package/dist/core/runner/classifier-ladder.js +170 -10
  57. package/dist/core/runner/contracts.d.ts +46 -1
  58. package/dist/core/runner/denial-limit-arms.d.ts +14 -1
  59. package/dist/core/runner/denial-limit-arms.js +7 -6
  60. package/dist/core/runner/env-capability-census.d.ts +30 -0
  61. package/dist/core/runner/env-capability-census.js +38 -0
  62. package/dist/core/runner/forward-sink.d.ts +32 -0
  63. package/dist/core/runner/forward-sink.js +41 -0
  64. package/dist/core/runner/gate-exit.d.ts +4 -0
  65. package/dist/core/runner/gate-exit.js +4 -1
  66. package/dist/core/runner/git-status-frame.d.ts +0 -9
  67. package/dist/core/runner/listing-fold.d.ts +57 -0
  68. package/dist/core/runner/listing-fold.js +75 -0
  69. package/dist/core/runner/prepare-ask-lane.js +1 -0
  70. package/dist/core/runner/prepare-caps-and-workflow.js +20 -31
  71. package/dist/core/runner/prepare-config-doors.d.ts +6 -0
  72. package/dist/core/runner/prepare-config-doors.js +8 -10
  73. package/dist/core/runner/prepare-execution-env.d.ts +1 -1
  74. package/dist/core/runner/prepare-execution-env.js +2 -0
  75. package/dist/core/runner/prepare-gate-stations.d.ts +8 -0
  76. package/dist/core/runner/prepare-gate-stations.js +6 -2
  77. package/dist/core/runner/prepare-hands-readface.d.ts +4 -0
  78. package/dist/core/runner/prepare-hands-readface.js +3 -5
  79. package/dist/core/runner/prepare-listings.d.ts +3 -0
  80. package/dist/core/runner/prepare-listings.js +4 -2
  81. package/dist/core/runner/prepare-memory.js +7 -1
  82. package/dist/core/runner/prepare-policy-chain.js +5 -5
  83. package/dist/core/runner/prepare-project-context.d.ts +5 -1
  84. package/dist/core/runner/prepare-project-context.js +3 -2
  85. package/dist/core/runner/prepare-prompt-inputs.d.ts +2 -2
  86. package/dist/core/runner/prepare-run-refs.d.ts +4 -2
  87. package/dist/core/runner/prepare-run-refs.js +5 -4
  88. package/dist/core/runner/prepare-task.js +8 -6
  89. package/dist/core/runner/resume-apply.js +3 -3
  90. package/dist/core/runner/run-clock-and-content.js +3 -7
  91. package/dist/core/runner/run-harness-handlers.js +10 -10
  92. package/dist/core/runner/run-leg.js +31 -16
  93. package/dist/core/runner/run-settle-and-teardown.js +2 -5
  94. package/dist/core/runner/run-turn-boundary.js +44 -25
  95. package/dist/core/runner/runtask.js +2 -1
  96. package/dist/core/runner/synthetic-tools.d.ts +41 -29
  97. package/dist/core/runner/synthetic-tools.js +25 -40
  98. package/dist/core/runner/task-root-canonical.d.ts +45 -0
  99. package/dist/core/runner/task-root-canonical.js +18 -0
  100. package/dist/core/runner/turn-attachments.d.ts +160 -53
  101. package/dist/core/runner/turn-attachments.js +153 -51
  102. package/dist/core/safe-notify.d.ts +5 -0
  103. package/dist/core/safe-notify.js +24 -2
  104. package/dist/core/sensitive-path-policy.d.ts +84 -17
  105. package/dist/core/sensitive-path-policy.js +37 -19
  106. package/dist/core/skill-listing-budget.d.ts +106 -0
  107. package/dist/core/skill-listing-budget.js +57 -0
  108. package/dist/core/skills-directory.js +17 -10
  109. package/dist/core/task-registry.js +2 -1
  110. package/dist/core/task-spec.d.ts +29 -2
  111. package/dist/core/tool-policy.d.ts +31 -5
  112. package/dist/core/tool-policy.js +45 -32
  113. package/dist/core/tool-spec.d.ts +37 -4
  114. package/dist/core/types.d.ts +1 -1
  115. package/dist/core/write-protect.d.ts +105 -20
  116. package/dist/core/write-protect.js +89 -32
  117. package/dist/index.d.ts +8 -7
  118. package/dist/index.js +6 -5
  119. package/dist/orchestration/workflow-primitives.js +10 -20
  120. package/dist/stores/file/memory-store.d.ts +5 -2
  121. package/dist/stores/file/memory-store.js +8 -4
  122. package/dist/tools/fs/bash-readonly-classifier.d.ts +40 -2
  123. package/dist/tools/fs/bash-readonly-classifier.js +376 -41
  124. package/dist/tools/fs/fs-bash.d.ts +1 -1
  125. package/dist/tools/fs/fs-bash.js +19 -28
  126. package/dist/tools/fs/fs-shared.d.ts +22 -0
  127. package/dist/tools/fs/fs-shared.js +30 -0
  128. package/dist/tools/fs/index.d.ts +1 -1
  129. package/dist/tools/fs/index.js +1 -1
  130. package/dist/tools/fs/read-deny.d.ts +4 -3
  131. package/dist/tools/fs/safety.d.ts +15 -0
  132. package/dist/tools/fs/safety.js +7 -0
  133. package/package.json +2 -1
  134. package/test/export-surface.snapshot.json +31 -5
  135. package/wire-changes/core@7.18.0.json +1273 -0
  136. package/wire-changes/core@7.19.0.json +457 -0
  137. package/wire-changes/core@7.20.0.json +568 -0
@@ -9,11 +9,11 @@ import { hasBackgroundShell } from "../../core/background-shell.js";
9
9
  import { delimitUntrusted } from "../../core/untrusted-text.js";
10
10
  import { MCP_IMAGE_MAX_BASE64 } from "../../core/mcp.js";
11
11
  import { imageMagicMatches, normalizeAbsPathLexically, withinAnyRoot } from "./safety.js";
12
- import { boundaryIsTheSandbox, hasDestroy, isIsolated } from "../../core/remote-env.js";
12
+ import { boundaryIsTheSandbox, canonicalPathIsAuthoritative, hasDestroy, isIsolated } from "../../core/remote-env.js";
13
13
  import { ghRateLimitHint } from "./gh-rate-limit.js";
14
- import { resolveBashTimeoutCaps, bashTimeoutCapsSec, bashTimeoutArgRefusal, bashTimeoutParamDescription, envErrorDetail, bashMaxOutputChars, clipShellOutput, writeShellOverflowFile, createShellOverflowSpoolFence, shellRecoveryHint, CWD_SENTINEL, BASH_READONLY_CONFINEMENT_NOTE, } from "./fs-shared.js";
14
+ import { resolveBashTimeoutCaps, bashTimeoutCapsSec, bashTimeoutArgRefusal, bashTimeoutParamDescription, envErrorDetail, bashMaxOutputChars, clipShellOutput, writeShellOverflowFile, createShellOverflowSpoolFence, shellRecoveryHint, CWD_SENTINEL, stripCwdSentinel, BASH_READONLY_CONFINEMENT_NOTE, } from "./fs-shared.js";
15
15
  import { PROBE_CAUSE_PATH_MAX, inlineUntrusted } from "../../core/untrusted-text.js";
16
- import { BASH_CLASSIFY_DEFAULT_ALLOW, BASH_READONLY_DEFAULT_ALLOW, coarseReadonlyCheck, classifyBoundedReadonlyPollLoopDetailed, classifyCompoundReadonlyDetailed, classifyOutOfRootReadGate, refusedCommandMayRead, classifySimpleCommandReadBoundary, NOT_AUTO_ALLOWED, } from "./bash-readonly-classifier.js";
16
+ import { bashClassifyDefaultAllow, BASH_READONLY_DEFAULT_ALLOW, coarseReadonlyCheck, classifyBoundedReadonlyPollLoopDetailed, classifyCompoundReadonlyDetailed, classifyOutOfRootReadGate, refusedCommandMayRead, classifySimpleCommandReadBoundary, NOT_AUTO_ALLOWED, } from "./bash-readonly-classifier.js";
17
17
  import { toolFace } from "../../core/tool-catalog-entries.js";
18
18
  const RECURSIVE_CAUSE_MAX_PATHS = 3;
19
19
  const RECURSIVE_READ_CAUSE_CODE = "shell.recursive_read_unbounded";
@@ -25,7 +25,7 @@ function operandFamily(paths) {
25
25
  };
26
26
  }
27
27
  export function bashReversibilityProbe(allow, boundary) {
28
- const allowSet = new Set(allow ?? BASH_CLASSIFY_DEFAULT_ALLOW);
28
+ const allowSet = new Set(allow ?? bashClassifyDefaultAllow());
29
29
  return (args) => {
30
30
  const command = shellCommandOf(args);
31
31
  if (command === undefined)
@@ -50,13 +50,17 @@ export function bashReversibilityProbe(allow, boundary) {
50
50
  if (loop.reason === undefined)
51
51
  return { reversible: true };
52
52
  if (boundaryDeclared(loop) || unjudgedRead(command, allowSet, resolved, loop))
53
- return { reversible: false, mandated: true };
54
- return { reversible: false, ...boundaryGate(detailed) };
53
+ return { reversible: false, mandated: true, ...readRootCandidateOf(loop) };
54
+ return { reversible: false, ...boundaryGate(detailed), ...readRootCandidateOf(detailed) };
55
55
  };
56
56
  }
57
57
  function boundaryUnread(verdict) {
58
58
  return boundaryUndecided(verdict) || boundaryUnjudged(verdict);
59
59
  }
60
+ function readRootCandidateOf(verdict) {
61
+ const dir = verdict.outOfRootGrantCandidate;
62
+ return dir !== undefined ? { readRootCandidate: { dir, clearsThisAsk: true } } : {};
63
+ }
60
64
  function boundaryUndecided(verdict) {
61
65
  return verdict.undecidedPaths !== undefined && verdict.undecidedPaths.length > 0;
62
66
  }
@@ -105,7 +109,7 @@ function recursiveReadCause(detailed) {
105
109
  };
106
110
  }
107
111
  export function bashReadBoundaryProbe(boundary) {
108
- const allowSet = new Set(BASH_CLASSIFY_DEFAULT_ALLOW);
112
+ const allowSet = new Set(bashClassifyDefaultAllow());
109
113
  return (args) => {
110
114
  const command = shellCommandOf(args);
111
115
  if (command === undefined)
@@ -134,7 +138,7 @@ export function bashReadBoundaryProbe(boundary) {
134
138
  const loopVerdict = seatVerdict(loop);
135
139
  const mandated = [compoundVerdict, loopVerdict].find((verdict) => verdict?.mandated === true);
136
140
  if (mandated !== undefined)
137
- return mandated;
141
+ return { ...mandated, ...readRootCandidateOf(detailed), ...readRootCandidateOf(loop) };
138
142
  if (readBoundaryMandate(command, allowSet, resolved, detailed).mandated === true)
139
143
  return { reversible: false, mandated: true };
140
144
  return compoundVerdict ?? loopVerdict ?? { reversible: true };
@@ -398,23 +402,6 @@ function createEpilogueFilter(marker) {
398
402
  return out + buf;
399
403
  };
400
404
  }
401
- function readCwdEpilogue(stderr, marker) {
402
- let out = "";
403
- let rest = stderr;
404
- let cwd;
405
- for (;;) {
406
- const open = rest.indexOf(marker);
407
- if (open === -1)
408
- break;
409
- const close = rest.indexOf(marker, open + marker.length);
410
- if (close === -1)
411
- break;
412
- out += rest.slice(0, open);
413
- cwd = rest.slice(open + marker.length, close);
414
- rest = rest.slice(close + marker.length);
415
- }
416
- return { stderr: out + rest, cwd };
417
- }
418
405
  function describeSafely(sink, body) {
419
406
  if (sink === undefined)
420
407
  return;
@@ -578,7 +565,7 @@ async function runShell(env, cwd, toolName, command, timeoutSec, caps, signal, c
578
565
  let { stderr } = res.value;
579
566
  const { stdout, exitCode } = res.value;
580
567
  if (cwdRef) {
581
- const epilogue = readCwdEpilogue(stderr, cwdMarker);
568
+ const epilogue = stripCwdSentinel(stderr, cwdMarker);
582
569
  stderr = epilogue.stderr;
583
570
  if (epilogue.cwd !== undefined) {
584
571
  const captured = epilogue.cwd.trim();
@@ -732,7 +719,7 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
732
719
  const bgRetained = hasBackgroundShell(env) && env.backgroundCapabilities.retainBackgroundProcesses === true;
733
720
  const bgSessionScoped = taskOpts.sessionId !== undefined && taskOpts.taskRegistry !== undefined;
734
721
  const bgEnvIsolatedOwned = hasDestroy(env) && isIsolated(env);
735
- const recheckReaders = new Set(BASH_CLASSIFY_DEFAULT_ALLOW);
722
+ const recheckReaders = new Set(bashClassifyDefaultAllow());
736
723
  let canonicalForRoots;
737
724
  const canonicalBoundaryFor = (roots) => {
738
725
  const key = roots.join("\u0000");
@@ -1066,6 +1053,7 @@ function pathSegmentsFrom(p, from) {
1066
1053
  }
1067
1054
  function createCanonicalReadBoundary(env, readRoots) {
1068
1055
  const lexicalOnly = boundaryIsTheSandbox(env);
1056
+ const resolverIsAuthoritative = canonicalPathIsAuthoritative(env);
1069
1057
  let rootsOnce;
1070
1058
  const canonicalRoots = async (signal) => {
1071
1059
  rootsOnce ??= (async () => {
@@ -1225,8 +1213,11 @@ function createCanonicalReadBoundary(env, readRoots) {
1225
1213
  let unresolvable;
1226
1214
  const judge = async (p) => {
1227
1215
  const real = await resolve(p, signal);
1228
- if (real === undefined)
1216
+ if (real === undefined) {
1217
+ if (resolverIsAuthoritative)
1218
+ unresolvable ??= `"${p}" cannot be resolved to verify where it reads`;
1229
1219
  return;
1220
+ }
1230
1221
  if (!real.exact && real.missing !== true) {
1231
1222
  unresolvable ??= `"${p}" cannot be resolved to verify where it reads`;
1232
1223
  return;
@@ -361,6 +361,28 @@ export declare function shellRecoveryHint(path: string, readOnly: boolean | unde
361
361
  * (NOT NotebookEdit — CC ships it without the trailer). */
362
362
  export declare const FILE_STATE_TRAILER = " (file state is current in your context \u2014 no need to Read it back)";
363
363
  export declare const CWD_SENTINEL = "__cc_cwd_9f2c1b__";
364
+ /**
365
+ * Take the engine's own cwd epilogue out of a finished stderr, and report the working directory the
366
+ * command left behind (the payload of the LAST span).
367
+ *
368
+ * EVERY span, and the LAST one — not the first, and not just one. A command that turns on shell tracing
369
+ * produces more than one (the shell echoes the epilogue's own source, marker and all, before running
370
+ * it), and under a one-pair rule the FIRST pair was that echo, whose payload is the unexpanded
371
+ * `' "$(pwd)" '`; a `cd` that plainly worked was then dropped while the real record stayed in the stderr
372
+ * a person reads. An opener with no close is NOT ours to eat — the command's own text is left exactly
373
+ * as it arrived.
374
+ *
375
+ * TWO CALLERS, ONE RULE, which is why the marker is optional. The foreground result path KNOWS the
376
+ * marker it minted and passes it (exact, unambiguous). A backgrounded command's spool is assembled by a
377
+ * different lane entirely — the shell row outlives the run that launched it and never had the marker —
378
+ * so it passes none and the marker is recovered per span from the sentinel plus its hex suffix. Before
379
+ * this was shared, only the foreground path stripped, and the completion notification handed a person
380
+ * the raw internal marker with the directory inside it.
381
+ */
382
+ export declare function stripCwdSentinel(stderr: string, marker?: string): {
383
+ stderr: string;
384
+ cwd: string | undefined;
385
+ };
364
386
  /** Convert a model-supplied millisecond timeout to our internal seconds, clamped. Exported for the
365
387
  * cc-parity unit (model sees ms, internal stays seconds — no ×1000 error).
366
388
  * ⚠️ This returns the EFFECTIVE budget, not the request — capping at a call site destroys the
@@ -247,6 +247,36 @@ export function shellRecoveryHint(path, readOnly) {
247
247
  }
248
248
  export const FILE_STATE_TRAILER = " (file state is current in your context — no need to Read it back)";
249
249
  export const CWD_SENTINEL = "__cc_cwd_9f2c1b__";
250
+ const CWD_SENTINEL_SUFFIX_CHARS = 16;
251
+ function cwdMarkerAt(text, open) {
252
+ const after = open + CWD_SENTINEL.length;
253
+ const suffix = /^[0-9a-f]{16}/.exec(text.slice(after, after + CWD_SENTINEL_SUFFIX_CHARS));
254
+ return suffix === null ? undefined : CWD_SENTINEL + suffix[0];
255
+ }
256
+ export function stripCwdSentinel(stderr, marker) {
257
+ let out = "";
258
+ let rest = stderr;
259
+ let cwd;
260
+ let from = 0;
261
+ for (;;) {
262
+ const open = rest.indexOf(marker ?? CWD_SENTINEL, from);
263
+ if (open === -1)
264
+ break;
265
+ const span = marker ?? cwdMarkerAt(rest, open);
266
+ if (span === undefined) {
267
+ from = open + CWD_SENTINEL.length;
268
+ continue;
269
+ }
270
+ const close = rest.indexOf(span, open + span.length);
271
+ if (close === -1)
272
+ break;
273
+ out += rest.slice(0, open);
274
+ cwd = rest.slice(open + span.length, close);
275
+ rest = rest.slice(close + span.length);
276
+ from = 0;
277
+ }
278
+ return { stderr: out + rest, cwd };
279
+ }
250
280
  export function msTimeoutToSec(timeoutMs, caps = resolveBashTimeoutCaps()) {
251
281
  const { defaultSec, maxSec } = bashTimeoutCapsSec(caps);
252
282
  if (timeoutMs === undefined || !Number.isFinite(timeoutMs))
@@ -12,7 +12,7 @@ export * from "./bash-readonly-classifier.js";
12
12
  export * from "./fs-bash.js";
13
13
  export * from "./read-deny.js";
14
14
  import { type BashReadonlyRootBoundary } from "./bash-readonly-classifier.js";
15
- export { BASH_CLASSIFY_DEFAULT_ALLOW } from "./bash-readonly-classifier.js";
15
+ export { BASH_CLASSIFY_EXTRA_ALLOW, bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
16
16
  import { type ReadDenyEntry } from "./read-deny.js";
17
17
  import { type ContainmentFace } from "./containment-face.js";
18
18
  export * from "./containment-face.js";
@@ -12,7 +12,7 @@ export * from "./bash-readonly-classifier.js";
12
12
  export * from "./fs-bash.js";
13
13
  export * from "./read-deny.js";
14
14
  import { BASH_READONLY_DEFAULT_ALLOW, } from "./bash-readonly-classifier.js";
15
- export { BASH_CLASSIFY_DEFAULT_ALLOW } from "./bash-readonly-classifier.js";
15
+ export { BASH_CLASSIFY_EXTRA_ALLOW, bashClassifyDefaultAllow } from "./bash-readonly-classifier.js";
16
16
  import { compileReadDeny } from "./read-deny.js";
17
17
  import { deploymentFaceClampNotice, resolveContainmentFace } from "./containment-face.js";
18
18
  export * from "./containment-face.js";
@@ -104,11 +104,12 @@ export interface ReadDenyBuiltinRow {
104
104
  * `.sema`/`.claude` also hold agents, skills, hooks and (under the shell's layout,
105
105
  * `~/.sema/engine-data`) the engine data root with the memory library — material the model reads
106
106
  * and edits as ordinary work, and a directory-wide row would need a data-root passthrough (one more
107
- * rule) to keep it readable. The write face guards the directories (`sensitive-path-policy.ts`): a
108
- * write there is self-authorization; a read of a non-credential file there is debugging. Token
107
+ * rule) to keep it readable. Token
109
108
  * STORES of the same tooling (`.credentials.json`, an agent CLI's `auth.json`, …) are credentials rows, not
110
109
  * agent-config rows: a deployment that switches this tier off to debug its agent configuration must
111
- * not also expose the OAuth stores beside it.
110
+ * not also expose the OAuth stores beside it. These rows are ALSO the opt-in write deny set's
111
+ * agent-configuration block ({@link import("../../core/sensitive-path-policy.js").agentConfigDenyPatterns},
112
+ * which reads them from here; contract sentence there), so a row added here is judged by both faces.
112
113
  */
113
114
  export declare const READ_FACE_BUILTIN_DENY_TABLE: readonly ReadDenyBuiltinRow[];
114
115
  /**
@@ -258,6 +258,21 @@ export declare function isBinaryContent(sample: string): boolean;
258
258
  * test with no verdict of its own.
259
259
  */
260
260
  export declare function isUncPath(path: string): boolean;
261
+ /**
262
+ * True when a WIN-FORM spelling carries a component Win32's own normalization would trim — a trailing dot
263
+ * or space on anything but the `.`/`..` specials (the same client-side strip {@link uncCanonicalKey}
264
+ * folds, stated once and read here rather than re-derived).
265
+ *
266
+ * Such a spelling is an ALIAS: `x. ` and `x` name ONE directory to the OS and two different strings to a
267
+ * lexical comparator. A caller that must reason about a spelling byte-wise — a directory offered to a
268
+ * person as the thing to declare, a common-prefix cut between two operands — cannot pick one of the two
269
+ * readings on the person's behalf, so it withholds. POSIX paths are never aliased this way: a trailing dot
270
+ * or space there is an ordinary filename character.
271
+ *
272
+ * The UNC ANCHOR is exempt for the reason the key minter states: a host's trailing dot is DNS (the FQDN
273
+ * root) and share-name normalization is the server's business; neither is the client-side strip.
274
+ */
275
+ export declare function hasWin32AliasedComponent(p: string): boolean;
261
276
  /** The two spelling families a tree can be in. Read from the ROOT's spelling (the cwd's when a deployment
262
277
  * supplies no root) — never from `process.platform`: the exec env may be remote and of the other family. */
263
278
  export type PathFamily = "win" | "posix";
@@ -314,6 +314,13 @@ function foldUncLexically(p) {
314
314
  const anchor = `\\\\${host}${share !== undefined ? `\\${share}` : ""}`;
315
315
  return { key: out.length > 0 ? `${anchor}\\${out.join("\\")}` : anchor, climbedAboveShare: climbed };
316
316
  }
317
+ export function hasWin32AliasedComponent(p) {
318
+ if (!isWinFormPath(p))
319
+ return false;
320
+ const parts = p.split(/[\\/]+/).filter((seg) => seg.length > 0);
321
+ const tail = p.startsWith("\\\\") || p.startsWith("//") ? parts.slice(2) : parts;
322
+ return tail.some((seg) => seg !== "." && seg !== ".." && /[. ]$/.test(seg));
323
+ }
317
324
  export function pathFamilyOf(bases) {
318
325
  const tree = bases.root ?? bases.cwd;
319
326
  return tree === undefined ? undefined : isWinFormPath(tree) ? "win" : "posix";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.18.0",
3
+ "version": "7.20.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -84,6 +84,7 @@
84
84
  "gate:tool-names": "node scripts/verify-tool-name-tables.mjs",
85
85
  "gate:tool-docs": "node scripts/gen-tool-spec-fields.mjs --check",
86
86
  "gate:backlog-ledger": "node scripts/verify-backlog-ledger.mjs",
87
+ "gate:child-clamp-seats": "node scripts/verify-child-clamp-seats.mjs",
87
88
  "gate:docs-tool-names": "node scripts/verify-docs-tool-names.mjs",
88
89
  "gate:comment-ratio": "vitest run test/comment-ratio-ratchet.test.ts",
89
90
  "gate:negative-controls": "vitest run test/gate-negative-controls.test.ts",
@@ -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": 2373,
4
+ "count": 2386,
5
5
  "exports": {
6
6
  "A2ATaskState": "type",
7
7
  "A2ATaskStateReversal": "type",
@@ -54,6 +54,7 @@
54
54
  "AUTO_MODE_DENIAL_LIMIT_DEFAULTS": "variable",
55
55
  "AUTO_MODE_DENY_SOURCES": "variable",
56
56
  "AUTO_MODE_FALLBACK_CAUSES": "variable",
57
+ "AUTO_MODE_PARSE_FAILURES": "variable",
57
58
  "AUTO_MODE_PERMISSIONS_EXTERNAL": "variable",
58
59
  "AUTO_MODE_UNAVAILABLE_CAUSES": "variable",
59
60
  "AcquiredSession": "interface",
@@ -149,6 +150,7 @@
149
150
  "AutoModeDenySource": "type",
150
151
  "AutoModeFallbackCause": "type",
151
152
  "AutoModeLadderRung": "interface",
153
+ "AutoModeParseFailure": "type",
152
154
  "AutoModeRebuildOptions": "interface",
153
155
  "AutoModeRebuildRefusal": "type",
154
156
  "AutoModeRebuildResult": "type",
@@ -206,7 +208,6 @@
206
208
  "CHALLENGE_LEDGER_MAX_EVENTS": "variable",
207
209
  "CIRCUIT_OPEN_MARKER": "variable",
208
210
  "CLASSIFIER_DENY_CAUSES": "variable",
209
- "CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE": "variable",
210
211
  "COARSE_SHELL_TOOLS": "variable",
211
212
  "CODE_AGENT_PROMPT": "variable",
212
213
  "CODE_REVIEW_PROMPT": "variable",
@@ -269,6 +270,7 @@
269
270
  "CheckpointToken": "type",
270
271
  "CircuitBreakerOptions": "interface",
271
272
  "ClassifierDenyCause": "type",
273
+ "ClassifierSeat": "type",
272
274
  "CleanArmLeakFinding": "interface",
273
275
  "CleanArmLeakVerdict": "interface",
274
276
  "CleanMemoryIndexRow": "interface",
@@ -747,6 +749,7 @@
747
749
  "MemoryNoteRecord": "interface",
748
750
  "MemoryNoteType": "type",
749
751
  "MemoryOriginCause": "type",
752
+ "MemoryProjectionError": "class",
750
753
  "MemoryPromptArtifactStore": "class",
751
754
  "MemoryPromptSourceStateStore": "class",
752
755
  "MemoryReadDetails": "type",
@@ -1081,6 +1084,7 @@
1081
1084
  "ReadOnlyCommandRow": "interface",
1082
1085
  "ReadOnlyFlagArity": "type",
1083
1086
  "ReadOnlyShellVerdict": "type",
1087
+ "ReadRootGrantCandidate": "interface",
1084
1088
  "RealApprovalGateBit": "interface",
1085
1089
  "ReasoningFormat": "type",
1086
1090
  "ReasoningIntensity": "type",
@@ -1536,6 +1540,7 @@
1536
1540
  "WORKFLOW_SUBAGENT_PROMPT": "variable",
1537
1541
  "WORKFLOW_SUBAGENT_PROMPT_SCHEMA": "variable",
1538
1542
  "WORKTREE_PARENT": "variable",
1543
+ "WRITE_EXEMPT_DIR_COVERS_ROOT_CODE": "variable",
1539
1544
  "WRITE_PROTECTED_DEFAULT_TABLE": "variable",
1540
1545
  "WebFetchConfig": "interface",
1541
1546
  "WebSearchConfig": "interface",
@@ -1598,7 +1603,11 @@
1598
1603
  "WriteProtectedHit": "interface",
1599
1604
  "WriteProtectedKind": "type",
1600
1605
  "WriteProtectedRow": "interface",
1606
+ "WriteProtectionCheck": "type",
1607
+ "WriteProtectionCompileOptions": "interface",
1608
+ "WriteProtectionJudgeContext": "interface",
1601
1609
  "WriteProtectionMatcher": "interface",
1610
+ "WriteProtectionVerdict": "type",
1602
1611
  "WriteReceipt": "interface",
1603
1612
  "XARGS_READ_ONLY_TARGETS": "variable",
1604
1613
  "ackAdoptionConfig": "function",
@@ -1679,6 +1688,7 @@
1679
1688
  "callKeyOrdinal": "function",
1680
1689
  "canAccessAgentRecord": "function",
1681
1690
  "canAccessWorkflowRun": "function",
1691
+ "canonicalPathIsAuthoritative": "function",
1682
1692
  "canonicalPeerDisplayName": "function",
1683
1693
  "canonicalWorkflowName": "function",
1684
1694
  "capAggregateMediaBytes": "function",
@@ -1700,6 +1710,8 @@
1700
1710
  "checkpointVersionOf": "function",
1701
1711
  "clampHopChain": "function",
1702
1712
  "classifierMayAnswer": "function",
1713
+ "classifierParseFailureDenyMessage": "function",
1714
+ "classifierSeatModel": "function",
1703
1715
  "classifierUnavailableDenyMessage": "function",
1704
1716
  "classifyCompoundReadonly": "function",
1705
1717
  "classifyCompoundReadonlyDetailed": "function",
@@ -1943,6 +1955,7 @@
1943
1955
  "isAutoModeClassifierFallback": "function",
1944
1956
  "isAutoModeDenySource": "function",
1945
1957
  "isAutoModeFallbackCause": "function",
1958
+ "isAutoModeParseFailure": "function",
1946
1959
  "isAutoModeUnavailableCause": "function",
1947
1960
  "isCanonicalPeerAddress": "function",
1948
1961
  "isClassifierDenyCause": "function",
@@ -2328,7 +2341,7 @@
2328
2341
  "termSet": "function",
2329
2342
  "terminalForTier": "function",
2330
2343
  "terminalProjection": "function",
2331
- "thinkingOffExpressible": "function",
2344
+ "thinkingOffSpelled": "function",
2332
2345
  "tightenTaskSpec": "function",
2333
2346
  "toA2ATaskState": "function",
2334
2347
  "toolCallFaceOf": "function",
@@ -2429,6 +2442,7 @@
2429
2442
  "AUTO_MODE_DENIAL_LIMIT_DEFAULTS": "advanced",
2430
2443
  "AUTO_MODE_DENY_SOURCES": "advanced",
2431
2444
  "AUTO_MODE_FALLBACK_CAUSES": "advanced",
2445
+ "AUTO_MODE_PARSE_FAILURES": "advanced",
2432
2446
  "AUTO_MODE_PERMISSIONS_EXTERNAL": "advanced",
2433
2447
  "AUTO_MODE_UNAVAILABLE_CAUSES": "advanced",
2434
2448
  "AcquiredSession": "advanced",
@@ -2524,6 +2538,7 @@
2524
2538
  "AutoModeDenySource": "advanced",
2525
2539
  "AutoModeFallbackCause": "advanced",
2526
2540
  "AutoModeLadderRung": "advanced",
2541
+ "AutoModeParseFailure": "advanced",
2527
2542
  "AutoModeRebuildOptions": "advanced",
2528
2543
  "AutoModeRebuildRefusal": "advanced",
2529
2544
  "AutoModeRebuildResult": "advanced",
@@ -2581,7 +2596,6 @@
2581
2596
  "CHALLENGE_LEDGER_MAX_EVENTS": "advanced",
2582
2597
  "CIRCUIT_OPEN_MARKER": "advanced",
2583
2598
  "CLASSIFIER_DENY_CAUSES": "advanced",
2584
- "CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE": "advanced",
2585
2599
  "COARSE_SHELL_TOOLS": "stable",
2586
2600
  "CODE_AGENT_PROMPT": "advanced",
2587
2601
  "CODE_REVIEW_PROMPT": "advanced",
@@ -2644,6 +2658,7 @@
2644
2658
  "CheckpointToken": "stable",
2645
2659
  "CircuitBreakerOptions": "advanced",
2646
2660
  "ClassifierDenyCause": "advanced",
2661
+ "ClassifierSeat": "internal",
2647
2662
  "CleanArmLeakFinding": "advanced",
2648
2663
  "CleanArmLeakVerdict": "advanced",
2649
2664
  "CleanMemoryIndexRow": "advanced",
@@ -3122,6 +3137,7 @@
3122
3137
  "MemoryNoteRecord": "stable",
3123
3138
  "MemoryNoteType": "advanced",
3124
3139
  "MemoryOriginCause": "advanced",
3140
+ "MemoryProjectionError": "advanced",
3125
3141
  "MemoryPromptArtifactStore": "advanced",
3126
3142
  "MemoryPromptSourceStateStore": "advanced",
3127
3143
  "MemoryReadDetails": "advanced",
@@ -3456,6 +3472,7 @@
3456
3472
  "ReadOnlyCommandRow": "advanced",
3457
3473
  "ReadOnlyFlagArity": "advanced",
3458
3474
  "ReadOnlyShellVerdict": "advanced",
3475
+ "ReadRootGrantCandidate": "advanced",
3459
3476
  "RealApprovalGateBit": "advanced",
3460
3477
  "ReasoningFormat": "advanced",
3461
3478
  "ReasoningIntensity": "advanced",
@@ -3911,6 +3928,7 @@
3911
3928
  "WORKFLOW_SUBAGENT_PROMPT": "advanced",
3912
3929
  "WORKFLOW_SUBAGENT_PROMPT_SCHEMA": "advanced",
3913
3930
  "WORKTREE_PARENT": "advanced",
3931
+ "WRITE_EXEMPT_DIR_COVERS_ROOT_CODE": "advanced",
3914
3932
  "WRITE_PROTECTED_DEFAULT_TABLE": "advanced",
3915
3933
  "WebFetchConfig": "advanced",
3916
3934
  "WebSearchConfig": "advanced",
@@ -3973,7 +3991,11 @@
3973
3991
  "WriteProtectedHit": "advanced",
3974
3992
  "WriteProtectedKind": "advanced",
3975
3993
  "WriteProtectedRow": "advanced",
3994
+ "WriteProtectionCheck": "advanced",
3995
+ "WriteProtectionCompileOptions": "advanced",
3996
+ "WriteProtectionJudgeContext": "advanced",
3976
3997
  "WriteProtectionMatcher": "advanced",
3998
+ "WriteProtectionVerdict": "advanced",
3977
3999
  "WriteReceipt": "internal",
3978
4000
  "XARGS_READ_ONLY_TARGETS": "advanced",
3979
4001
  "ackAdoptionConfig": "advanced",
@@ -4054,6 +4076,7 @@
4054
4076
  "callKeyOrdinal": "advanced",
4055
4077
  "canAccessAgentRecord": "advanced",
4056
4078
  "canAccessWorkflowRun": "advanced",
4079
+ "canonicalPathIsAuthoritative": "advanced",
4057
4080
  "canonicalPeerDisplayName": "advanced",
4058
4081
  "canonicalWorkflowName": "advanced",
4059
4082
  "capAggregateMediaBytes": "advanced",
@@ -4075,6 +4098,8 @@
4075
4098
  "checkpointVersionOf": "advanced",
4076
4099
  "clampHopChain": "advanced",
4077
4100
  "classifierMayAnswer": "advanced",
4101
+ "classifierParseFailureDenyMessage": "advanced",
4102
+ "classifierSeatModel": "internal",
4078
4103
  "classifierUnavailableDenyMessage": "advanced",
4079
4104
  "classifyCompoundReadonly": "advanced",
4080
4105
  "classifyCompoundReadonlyDetailed": "advanced",
@@ -4318,6 +4343,7 @@
4318
4343
  "isAutoModeClassifierFallback": "advanced",
4319
4344
  "isAutoModeDenySource": "advanced",
4320
4345
  "isAutoModeFallbackCause": "advanced",
4346
+ "isAutoModeParseFailure": "advanced",
4321
4347
  "isAutoModeUnavailableCause": "advanced",
4322
4348
  "isCanonicalPeerAddress": "advanced",
4323
4349
  "isClassifierDenyCause": "advanced",
@@ -4703,7 +4729,7 @@
4703
4729
  "termSet": "stable",
4704
4730
  "terminalForTier": "advanced",
4705
4731
  "terminalProjection": "internal",
4706
- "thinkingOffExpressible": "advanced",
4732
+ "thinkingOffSpelled": "advanced",
4707
4733
  "tightenTaskSpec": "stable",
4708
4734
  "toA2ATaskState": "advanced",
4709
4735
  "toolCallFaceOf": "advanced",