@sema-agent/core 7.53.0 → 7.54.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 (244) hide show
  1. package/CHANGELOG.md +277 -1
  2. package/LICENSE +3 -1
  3. package/dist/agents/peer-session-drain.d.ts +0 -1
  4. package/dist/agents/peer-session-drain.js +0 -2
  5. package/dist/agents/send-message-tool.js +3 -4
  6. package/dist/agents/subagent.d.ts +3 -5
  7. package/dist/agents/subagent.js +18 -11
  8. package/dist/agents/tool-filter.d.ts +7 -4
  9. package/dist/agents/tool-filter.js +7 -3
  10. package/dist/core/a2a.js +4 -2
  11. package/dist/core/allow-entry.d.ts +1 -0
  12. package/dist/core/allow-entry.js +1 -0
  13. package/dist/core/approval-object.d.ts +87 -0
  14. package/dist/core/approval-object.js +85 -0
  15. package/dist/core/arg-summary.d.ts +3 -2
  16. package/dist/core/arg-summary.js +17 -11
  17. package/dist/core/ask-origin.d.ts +43 -27
  18. package/dist/core/ask-origin.js +20 -11
  19. package/dist/core/ask-question.d.ts +0 -1
  20. package/dist/core/ask-question.js +4 -67
  21. package/dist/core/ask-unresolvable-notice.d.ts +3 -2
  22. package/dist/core/auto-compaction.d.ts +49 -50
  23. package/dist/core/auto-compaction.js +41 -51
  24. package/dist/core/auto-mode-prompt.d.ts +4 -2
  25. package/dist/core/auto-mode-prompt.js +4 -3
  26. package/dist/core/canonical-json.d.ts +69 -0
  27. package/dist/core/canonical-json.js +130 -8
  28. package/dist/core/checkpoint-requirements.d.ts +13 -7
  29. package/dist/core/checkpoint-requirements.js +4 -1
  30. package/dist/core/checkpoint-store.d.ts +33 -18
  31. package/dist/core/checkpoint-store.js +5 -4
  32. package/dist/core/child-clamp-seats.d.ts +45 -6
  33. package/dist/core/child-clamp-seats.js +56 -4
  34. package/dist/core/consolidate-scope.d.ts +1 -1
  35. package/dist/core/cross-boundary-taint.d.ts +213 -0
  36. package/dist/core/cross-boundary-taint.js +196 -0
  37. package/dist/core/decision-receipt.d.ts +44 -1
  38. package/dist/core/decision-receipt.js +16 -0
  39. package/dist/core/effective-path-target.d.ts +3 -0
  40. package/dist/core/effective-path-target.js +4 -0
  41. package/dist/core/engine-notice.d.ts +34 -2
  42. package/dist/core/engine-notice.js +17 -1
  43. package/dist/core/engine-storage-backend.d.ts +73 -0
  44. package/dist/core/engine-storage-backend.js +1 -0
  45. package/dist/core/file-history-store.d.ts +6 -2
  46. package/dist/core/file-history-store.js +1 -0
  47. package/dist/core/gate-fold.js +10 -2
  48. package/dist/core/gate-lanes.js +30 -21
  49. package/dist/core/gate-outcome.d.ts +7 -3
  50. package/dist/core/governance-codes.d.ts +7 -2
  51. package/dist/core/governance-codes.js +6 -0
  52. package/dist/core/hooks.d.ts +14 -14
  53. package/dist/core/hooks.js +1 -2
  54. package/dist/core/locked-config.d.ts +28 -1
  55. package/dist/core/locked-config.js +30 -3
  56. package/dist/core/lsp.d.ts +1 -1
  57. package/dist/core/mcp-server-spec.d.ts +66 -9
  58. package/dist/core/mcp-server-spec.js +52 -19
  59. package/dist/core/mcp.js +5 -4
  60. package/dist/core/memory-engine/engine.js +2 -1
  61. package/dist/core/memory-engine/export-bundle.d.ts +1 -1
  62. package/dist/core/memory-engine/export-bundle.js +1 -1
  63. package/dist/core/memory-engine/file-backend.d.ts +2 -6
  64. package/dist/core/memory-engine/file-backend.js +1 -12
  65. package/dist/core/memory.d.ts +3 -3
  66. package/dist/core/mode-policy.d.ts +13 -8
  67. package/dist/core/mode-policy.js +3 -2
  68. package/dist/core/mods/kernel/receive.d.ts +2 -1
  69. package/dist/core/mods/kernel/receive.js +7 -2
  70. package/dist/core/parent-spec-seat.d.ts +10 -13
  71. package/dist/core/parent-spec-seat.js +14 -6
  72. package/dist/core/pause-registry.d.ts +35 -1
  73. package/dist/core/pause-registry.js +10 -0
  74. package/dist/core/permission-rule-consent.d.ts +18 -5
  75. package/dist/core/permission-rule-consent.js +13 -8
  76. package/dist/core/permission-rule-model.d.ts +29 -6
  77. package/dist/core/permission-rule-model.js +6 -1
  78. package/dist/core/permission-rule-org.d.ts +37 -0
  79. package/dist/core/permission-rule-org.js +34 -42
  80. package/dist/core/permission-rule-session.js +2 -2
  81. package/dist/core/permission-rule-store.d.ts +8 -0
  82. package/dist/core/permission-rule-store.js +1 -0
  83. package/dist/core/permission-rule-sync.js +3 -2
  84. package/dist/core/read-only-shell.d.ts +1 -1
  85. package/dist/core/read-only-shell.js +30 -9
  86. package/dist/core/remote-env.d.ts +41 -1
  87. package/dist/core/remote-env.js +18 -0
  88. package/dist/core/resume-startup-cause.d.ts +18 -0
  89. package/dist/core/resume-startup-cause.js +12 -0
  90. package/dist/core/runner/approval-feedback.d.ts +46 -0
  91. package/dist/core/runner/approval-feedback.js +41 -0
  92. package/dist/core/runner/compaction-call-options.d.ts +3 -3
  93. package/dist/core/runner/compaction-call-options.js +2 -2
  94. package/dist/core/runner/contracts.d.ts +33 -41
  95. package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
  96. package/dist/core/runner/cross-boundary-lane.js +190 -0
  97. package/dist/core/runner/decide-continuation.d.ts +4 -4
  98. package/dist/core/runner/decide-continuation.js +3 -1
  99. package/dist/core/runner/gate-exit.d.ts +32 -12
  100. package/dist/core/runner/gate-exit.js +75 -33
  101. package/dist/core/runner/inherited-ask-grants.js +2 -2
  102. package/dist/core/runner/person-path-rows.d.ts +2 -2
  103. package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -4
  104. package/dist/core/runner/prepare-acquire-reconcile.js +3 -3
  105. package/dist/core/runner/prepare-ask-lane.js +3 -3
  106. package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
  107. package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
  108. package/dist/core/runner/prepare-config-doors.d.ts +12 -15
  109. package/dist/core/runner/prepare-config-doors.js +8 -3
  110. package/dist/core/runner/prepare-context-lane.js +6 -3
  111. package/dist/core/runner/prepare-delegation-surface.d.ts +3 -2
  112. package/dist/core/runner/prepare-delegation-surface.js +2 -3
  113. package/dist/core/runner/prepare-execution-env.d.ts +1 -2
  114. package/dist/core/runner/prepare-execution-env.js +2 -1
  115. package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
  116. package/dist/core/runner/prepare-gate-stations.js +4 -3
  117. package/dist/core/runner/prepare-hands-readface.d.ts +2 -2
  118. package/dist/core/runner/prepare-hands-readface.js +1 -1
  119. package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
  120. package/dist/core/runner/prepare-inherited-gate.js +1 -0
  121. package/dist/core/runner/prepare-park-ask.d.ts +1 -1
  122. package/dist/core/runner/prepare-park-ask.js +9 -3
  123. package/dist/core/runner/prepare-policy-chain.js +2 -7
  124. package/dist/core/runner/prepare-safety-scan.d.ts +7 -2
  125. package/dist/core/runner/prepare-safety-scan.js +34 -27
  126. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
  127. package/dist/core/runner/prepare-suspend-saga.js +6 -2
  128. package/dist/core/runner/prepare-task.js +13 -10
  129. package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
  130. package/dist/core/runner/prepare-turn-wiring.js +14 -49
  131. package/dist/core/runner/prepare-wiring-manifest.d.ts +2 -2
  132. package/dist/core/runner/prepare-wiring-manifest.js +2 -2
  133. package/dist/core/runner/prepare-workspace-restore.js +2 -2
  134. package/dist/core/runner/resume-admission.js +9 -9
  135. package/dist/core/runner/resume-apply.js +4 -4
  136. package/dist/core/runner/resume-policy-outcome.d.ts +2 -2
  137. package/dist/core/runner/resume-policy-outcome.js +18 -15
  138. package/dist/core/runner/resume-preflight.js +2 -2
  139. package/dist/core/runner/resume-review-outcome.js +3 -3
  140. package/dist/core/runner/run-leg.js +57 -33
  141. package/dist/core/runner/run-settle-and-teardown.js +2 -0
  142. package/dist/core/runner/run-terminal-adoption.js +15 -4
  143. package/dist/core/runner/session-file-state-replay.d.ts +5 -2
  144. package/dist/core/runner/session-file-state-replay.js +3 -2
  145. package/dist/core/runner/stream-steer-verb.js +2 -2
  146. package/dist/core/runner/tool-face-overlay.d.ts +24 -6
  147. package/dist/core/runner/tool-face-overlay.js +24 -15
  148. package/dist/core/runner-deps.d.ts +9 -9
  149. package/dist/core/session-store.d.ts +35 -16
  150. package/dist/core/session-store.js +20 -12
  151. package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
  152. package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
  153. package/dist/core/shell-wrapper-table.d.ts +5 -3
  154. package/dist/core/shell-wrapper-table.js +1 -1
  155. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
  156. package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
  157. package/dist/core/store-contracts/session-store-contract.js +639 -0
  158. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
  159. package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
  160. package/dist/core/task-event.d.ts +6 -4
  161. package/dist/core/task-registry-agent.js +1 -0
  162. package/dist/core/task-registry-shared.d.ts +5 -0
  163. package/dist/core/task-registry.d.ts +7 -0
  164. package/dist/core/task-registry.js +17 -0
  165. package/dist/core/task-result.d.ts +1 -1
  166. package/dist/core/task-spec.d.ts +6 -6
  167. package/dist/core/tighten-task-spec.d.ts +6 -4
  168. package/dist/core/tighten-task-spec.js +2 -2
  169. package/dist/core/tool-catalog-entries.d.ts +1 -1
  170. package/dist/core/tool-catalog-entries.js +52 -52
  171. package/dist/core/tool-face-reader.d.ts +49 -0
  172. package/dist/core/tool-face-reader.js +94 -0
  173. package/dist/core/tool-face.d.ts +17 -8
  174. package/dist/core/tool-face.js +1 -0
  175. package/dist/core/tool-policy.d.ts +64 -23
  176. package/dist/core/tool-policy.js +62 -56
  177. package/dist/core/tool-roster.d.ts +46 -28
  178. package/dist/core/tool-roster.js +91 -14
  179. package/dist/core/tool-spec-members.js +2 -2
  180. package/dist/core/tool-spec.d.ts +20 -4
  181. package/dist/core/tools.js +2 -0
  182. package/dist/core/trace.d.ts +24 -2
  183. package/dist/core/types.d.ts +2 -2
  184. package/dist/core/types.js +1 -1
  185. package/dist/core/untrusted-text.d.ts +7 -0
  186. package/dist/core/untrusted-text.js +3 -8
  187. package/dist/core/user-attachment.d.ts +43 -0
  188. package/dist/core/user-attachment.js +92 -15
  189. package/dist/engine/compaction/compaction.d.ts +2 -2
  190. package/dist/engine/compaction/utils.d.ts +2 -2
  191. package/dist/engine/harness/types.d.ts +7 -4
  192. package/dist/engine/llm/types.d.ts +7 -2
  193. package/dist/engine/llm/types.js +2 -1
  194. package/dist/engine/session/repo-utils.d.ts +5 -0
  195. package/dist/engine/session/repo-utils.js +9 -0
  196. package/dist/engine/session/storage-base.js +2 -2
  197. package/dist/index.d.ts +23 -9
  198. package/dist/index.js +19 -8
  199. package/dist/internal/llm.d.ts +1 -1
  200. package/dist/internal/llm.js +1 -1
  201. package/dist/orchestration/run-workflow-tool.d.ts +3 -0
  202. package/dist/orchestration/run-workflow-tool.js +1 -0
  203. package/dist/orchestration/workflow-governance.d.ts +1 -1
  204. package/dist/orchestration/workflow.d.ts +3 -0
  205. package/dist/orchestration/workflow.js +2 -1
  206. package/dist/prompt-assembly/catalog/schema.d.ts +3 -0
  207. package/dist/prompt-assembly/catalog/schema.js +3 -0
  208. package/dist/prompt-assembly/overlay.js +2 -2
  209. package/dist/prompt-assembly/select.js +2 -2
  210. package/dist/prompts/default.d.ts +1 -1
  211. package/dist/shell/lex.d.ts +87 -8
  212. package/dist/shell/lex.js +283 -49
  213. package/dist/stores/file/index.d.ts +8 -6
  214. package/dist/tools/artifact/artifact-tool.js +10 -0
  215. package/dist/tools/fs/bash-program-position.d.ts +3 -15
  216. package/dist/tools/fs/bash-program-position.js +3 -10
  217. package/dist/tools/fs/bash-readonly-classifier.js +2 -0
  218. package/dist/tools/fs/fs-bash.d.ts +3 -3
  219. package/dist/tools/fs/fs-bash.js +72 -101
  220. package/dist/tools/fs/fs-read.d.ts +12 -0
  221. package/dist/tools/fs/fs-read.js +10 -9
  222. package/dist/tools/fs/fs-shared.d.ts +27 -31
  223. package/dist/tools/fs/fs-shared.js +12 -15
  224. package/dist/tools/fs/fs-write.js +1 -1
  225. package/dist/tools/fs/index.js +2 -2
  226. package/dist/tools/fs/read-deny-home.d.ts +1 -1
  227. package/dist/tools/fs/read-deny-home.js +5 -4
  228. package/dist/tools/fs/read-deny.d.ts +16 -3
  229. package/dist/tools/fs/read-deny.js +12 -9
  230. package/dist/tools/fs/safety.d.ts +7 -6
  231. package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
  232. package/dist/tools/fs/shell-read-bindings.js +15 -16
  233. package/dist/tools/fs/shell-read-device.js +1 -1
  234. package/dist/tools/fs/shell-read-plan.js +36 -47
  235. package/dist/tools/fs/shell-read-word.d.ts +1 -6
  236. package/dist/tools/fs/shell-read-word.js +2 -20
  237. package/dist/tools/fs/shell-reentry-text.js +1 -1
  238. package/dist/tools/web.d.ts +2 -2
  239. package/package.json +4 -1
  240. package/test/export-surface.snapshot.json +5654 -5520
  241. package/wire-changes/core@7.53.0.json +30 -8
  242. package/wire-changes/core@7.54.0.json +2870 -0
  243. package/dist/core/shell-scan.d.ts +0 -60
  244. package/dist/core/shell-scan.js +0 -183
@@ -1,11 +1,13 @@
1
1
  import { CROSS_SESSION_MESSAGE_TAG } from "../agents/cross-session-envelope.js";
2
2
  import { DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, CompactionError, compact, computeFileLists, dryRunSummarizationClamp, estimateContextTokens, estimateTokens, formatFileOperations, formatPersistedOutputRefs, prepareCompaction, shouldCompact, uuidv7, } from "../internal/harness.js";
3
3
  import { fileArgPath } from "./tool-registry.js";
4
+ import { userAttachmentOf } from "../internal/llm.js";
4
5
  import { contextEditFrontier, resolveTriggerWindow } from "./context-edit.js";
5
6
  import { selectCompactionEpoch } from "../prompt-assembly/epoch.js";
6
7
  import { brainToRuntime } from "./runtime.js";
7
8
  import { defuseFenceMarkers, sanitizeUntrustedText } from "./untrusted-text.js";
8
9
  import { createSafeNotifier, observeThenableRejection } from "./safe-notify.js";
10
+ import { thrownText } from "./thrown-value.js";
9
11
  export const DEFAULT_COMPACTION_INSTRUCTIONS = "Preserve verbatim every code, identifier, name, number, file path, error message, credential " +
10
12
  "reference, and explicit user instruction or decision mentioned anywhere in the conversation. " +
11
13
  "When in doubt, keep the specific detail rather than generalizing it.";
@@ -18,10 +20,6 @@ export function sanitizeCompactionInstructions(text) {
18
20
  ? cps.slice(0, COMPACTION_INSTRUCTIONS_MAX_CHARS).join("")
19
21
  : defused;
20
22
  }
21
- const WORKING_FILE_BREAKOUT_TAGS = ["working-file", "summary"];
22
- function escapeWorkingFilePathAttr(path) {
23
- return path.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/[\r\n]+/g, " ");
24
- }
25
23
  const COMPACTION_MANUAL_CANCEL_FLAG = "semaCompactionManualCancel";
26
24
  export function isCompactionManualCancel(err) {
27
25
  return (typeof err === "object" && err !== null && err[COMPACTION_MANUAL_CANCEL_FLAG] === true);
@@ -336,10 +334,15 @@ export async function maybeCompact(opts) {
336
334
  details = res.value.details;
337
335
  }
338
336
  const persistStartAt = summaryEndAt ?? Date.now();
339
- let summaryWithAttachments = summary;
340
- const attachedFiles = [];
341
- const attachedComplete = [];
337
+ let restoreFrames = [];
342
338
  const att = opts.workingFileAttachments;
339
+ const undelivered = (label, reason) => {
340
+ try {
341
+ att?.undelivered?.(label, reason);
342
+ }
343
+ catch {
344
+ }
345
+ };
343
346
  const recentlyRead = att?.recentlyReadFiles?.() ?? [];
344
347
  const rawCandidateFiles = recentlyRead.length > 0 ? recentlyRead : (details?.modifiedFilesByRecency ?? details?.modifiedFiles);
345
348
  const maxFilesForSelection = Math.max(1, att?.maxFiles ?? 3);
@@ -352,6 +355,9 @@ export async function maybeCompact(opts) {
352
355
  const dedupStub = att?.isDedupStubResult;
353
356
  for (let i = firstKeptIdx; i < branch.length; i++) {
354
357
  const entry = branch[i];
358
+ const frame = entry?.type === "message" ? userAttachmentOf(entry.message) : undefined;
359
+ if (frame?.observation !== undefined && (frame.outcome === "attached" || frame.outcome === "partial"))
360
+ keptTailReadPathsRaw.add(frame.observation.key);
355
361
  if (entry.type !== "message" || entry.message.role !== "assistant")
356
362
  continue;
357
363
  const msg = entry.message;
@@ -432,44 +438,13 @@ export async function maybeCompact(opts) {
432
438
  const maxFiles = maxFilesForSelection;
433
439
  const perFileCap = Math.max(200, att.maxCharsPerFile ?? 16_000);
434
440
  const windowShareChars = windowKnown ? Math.floor(window * 0.15) * cpt : Number.POSITIVE_INFINITY;
435
- let remaining = Math.min(maxFiles * perFileCap, windowShareChars);
436
- const blocks = [];
437
- for (const path of candidateFiles.slice(0, maxFiles)) {
438
- if (remaining <= 0)
439
- break;
440
- let content = null;
441
- let withheldPattern;
442
- try {
443
- const r = await att.readFile(path);
444
- if (typeof r === "string")
445
- content = r;
446
- else if (r !== null && typeof r === "object")
447
- withheldPattern = r.withheld.pattern;
448
- }
449
- catch {
450
- content = null;
451
- }
452
- if (withheldPattern !== undefined) {
453
- blocks.push(`<working-file path="${escapeWorkingFilePathAttr(path)}" withheld="content withheld: matches the sensitive-path read deny list (pattern ${escapeWorkingFilePathAttr(withheldPattern)})"/>`);
454
- continue;
455
- }
456
- if (content === null)
457
- continue;
458
- const cap = Math.min(perFileCap, remaining);
459
- const wasClipped = content.length > cap;
460
- const clipped = wasClipped
461
- ? `${content.slice(0, cap)}\n[... ${content.length - cap} more characters truncated — read the file for the rest]`
462
- : content;
463
- remaining -= clipped.length;
464
- blocks.push(`<working-file path="${escapeWorkingFilePathAttr(path)}">\n${sanitizeUntrustedText(clipped, WORKING_FILE_BREAKOUT_TAGS)}\n</working-file>`);
465
- attachedFiles.push({ path, chars: Math.min(content.length, cap), truncated: wasClipped });
466
- if (!wasClipped)
467
- attachedComplete.push({ path, content });
468
- }
469
- if (blocks.length > 0) {
470
- summaryWithAttachments +=
471
- "\n\nCurrent on-disk contents of files touched in the summarized work (re-read at compaction time — trust these over any excerpts in the summary above):\n" +
472
- blocks.join("\n");
441
+ try {
442
+ restoreFrames = await att.restore({ candidates: candidateFiles, maxFiles, perFileChars: perFileCap, totalChars: Math.min(maxFiles * perFileCap, windowShareChars), charsPerToken: cpt, preserveKeys: excludedReadStatePreserveKeys });
443
+ }
444
+ catch (err) {
445
+ restoreFrames = [];
446
+ for (const path of candidateFiles.slice(0, maxFiles))
447
+ undelivered(path, `the restore failed: ${thrownText(err)}`);
473
448
  }
474
449
  }
475
450
  let preChars = 0;
@@ -484,7 +459,7 @@ export async function maybeCompact(opts) {
484
459
  if (opts.signal?.aborted === true) {
485
460
  throw Object.assign(new Error("compaction cancelled before commit — the abort signal fired during the summary call; nothing was persisted"), opts.manualCancelSignal?.aborted === true ? { [COMPACTION_MANUAL_CANCEL_FLAG]: true } : {});
486
461
  }
487
- await opts.session.appendCompaction(summaryWithAttachments, firstKeptEntryId, summaryTokensBefore, {
462
+ await opts.session.appendCompaction(summary, firstKeptEntryId, summaryTokensBefore, {
488
463
  ...(details ?? {}),
489
464
  ...(restatedEpoch !== undefined ? { promptEpoch: restatedEpoch } : {}),
490
465
  ...(restatedListings !== undefined ? { announcedListings: restatedListings } : {}),
@@ -502,6 +477,26 @@ export async function maybeCompact(opts) {
502
477
  catch {
503
478
  }
504
479
  }
480
+ const delivered = [];
481
+ for (const f of restoreFrames) {
482
+ try {
483
+ await opts.session.appendMessage({ role: "user", content: [...f.seed.content], timestamp: Date.now(), provenance: "engine-note", attachment: f.seed.attachment });
484
+ delivered.push(f);
485
+ }
486
+ catch (err) {
487
+ for (const g of restoreFrames.slice(delivered.length))
488
+ undelivered(g.seed.attachment.label, `the transcript did not take the restore frame: ${thrownText(err)}`);
489
+ break;
490
+ }
491
+ }
492
+ try {
493
+ opts.onApplied?.(excludedReadStatePreserveKeys);
494
+ for (const f of delivered)
495
+ f.commit?.();
496
+ }
497
+ catch {
498
+ }
499
+ const attachedFiles = delivered.flatMap(({ seed: { attachment: a } }) => ((a.outcome === "attached" || a.outcome === "partial") && a.dataBlocks.end > a.dataBlocks.start ? [{ path: a.label, chars: a.chars, truncated: a.truncated }] : []));
505
500
  if (opts.gitRestate !== undefined) {
506
501
  try {
507
502
  await opts.gitRestate.land();
@@ -516,11 +511,6 @@ export async function maybeCompact(opts) {
516
511
  catch {
517
512
  }
518
513
  }
519
- try {
520
- opts.onApplied?.(attachedComplete, excludedReadStatePreserveKeys);
521
- }
522
- catch {
523
- }
524
514
  if (ca !== undefined && restatedEpoch !== undefined) {
525
515
  try {
526
516
  ca.apply(restatedEpoch.identity);
@@ -47,14 +47,16 @@ export interface DerivedDenyRule {
47
47
  * The effective READ-deny table as the classifier's deny lines — one line per row, the row's reach in the
48
48
  * engine's own `Read(...)` path-rule grammar.
49
49
  *
50
- * A read-deny row is a run of path SEGMENTS matched anywhere in a path — the entry itself and everything under
50
+ * An UNANCHORED read-deny row is a run of path SEGMENTS matched anywhere in a path — the entry itself and everything under
51
51
  * it — and its `*` never crosses a `/` (read-deny.ts: `.ssh` refuses `~/.ssh/id_rsa` and `/srv/x/.ssh/config`
52
52
  * alike; a `**` segment matches exactly one segment). The rule grammar reaches the path a pattern names and
53
53
  * everything under it, so the line is `//**` + `/` + the row with every run of stars written `*` (one segment,
54
54
  * as in the row): `.ssh` ⇒ `Read(//**\/.ssh)`, `**\/secrets/**` ⇒ `Read(//**\/*\/secrets/*)`, `*` (every
55
55
  * path) ⇒ `Read(//**\/*)`. A row that names no segment at all (`""`, `/`) is not stated. A HOME-ANCHORED row
56
56
  * (`~/<run>`, read-deny.ts `READ_DENY_HOME_WORD`) is the rule grammar's home word with the same reach:
57
- * `~/.sema/settings.json` ⇒ `Read(~/.sema/settings.json)`.
57
+ * `~/.sema/settings.json` ⇒ `Read(~/.sema/settings.json)`; a ROOTED row (`/etc/shadow`, anchored at the filesystem
58
+ * root) is the rule grammar's absolute path: `Read(//etc/shadow)`. Which of the three a row is, is the one judge's
59
+ * answer (`readDenyRowAnchor`), asked here as everywhere a row is read.
58
60
  *
59
61
  * The text of a row can come from a task (a spawned task's author included) and it is spliced into a trusted
60
62
  * prompt, so a row is stated as a rule and nothing else: every character the grammar or the prompt must not
@@ -1,7 +1,7 @@
1
1
  import { AUTO_MODE_DEFAULT_WINDOW_MAX_CHARS, AUTO_MODE_DEFAULT_WINDOW_MAX_ENTRIES, AUTO_MODE_DEFAULTS_SENTINEL } from "./auto-mode-defaults.js";
2
2
  import { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./auto-mode-prompt-assets.js";
3
3
  import { cutEnginePrefix, cutEngineSegments, flattenableUserText } from "./untrusted-text.js";
4
- import { readDenyPatternRun } from "../tools/fs/read-deny.js";
4
+ import { readDenyPatternRun, readDenyRowAnchor } from "../tools/fs/read-deny.js";
5
5
  import { renderContentBlock, renderUserAttachmentView, userAttachmentOf } from "../internal/llm.js";
6
6
  export { AUTO_MODE_DEFAULTS_SENTINEL } from "./auto-mode-defaults.js";
7
7
  function mergeRuleSection(user, defaultsInner) {
@@ -36,12 +36,13 @@ export function readDenyClassifierRules(entries, rules = []) {
36
36
  out.push(line);
37
37
  };
38
38
  for (const { pattern, caseSensitive } of entries) {
39
- const { homeAnchored, run } = readDenyPatternRun(pattern);
39
+ const { run } = readDenyPatternRun(pattern);
40
40
  const segments = run.split("/").filter((seg) => seg.length > 0);
41
41
  if (segments.length === 0)
42
42
  continue;
43
43
  const stated = statedPattern(segments.join("/"));
44
- push({ rule: homeAnchored ? `Read(~/${stated})` : `Read(//**/${stated})`, ...(caseSensitive !== true && /[A-Za-z]/.test(stated) ? { anyCase: true } : {}) });
44
+ const anchor = readDenyRowAnchor(pattern);
45
+ push({ rule: anchor === "home" ? `Read(~/${stated})` : anchor === "root" ? `Read(//${stated})` : `Read(//**/${stated})`, ...(caseSensitive !== true && /[A-Za-z]/.test(stated) ? { anyCase: true } : {}) });
45
46
  }
46
47
  for (const { tool, pattern } of rules) {
47
48
  if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(tool) || pattern.length === 0)
@@ -1,3 +1,4 @@
1
+ import { type ReceiptFault } from "./mods/kernel/receive.js";
1
2
  /**
2
3
  * design/80 D-1 §2 (slice 1a.2) — the engine-only **vetted canonical serializer** used to mint a stable,
3
4
  * opaque {@link boundInputHashOf | boundInputHash} over a pending tool call's arguments.
@@ -60,3 +61,71 @@ export declare const UNCANONICALIZABLE = "<uncanonicalizable>";
60
61
  * The hex digest is fixed-length and opaque — exactly what the operator sees / the D-G signer signs.
61
62
  */
62
63
  export declare function boundInputHashOf(value: unknown): string;
64
+ /**
65
+ * The {@link boundInputHashOf} digest of `value` when an approval can be bound to it ({@link bindableValueOf}), else `undefined` —
66
+ * the digest a license is compared by, one only those bytes have. Over the bindable domain the canonical form is exact (no
67
+ * sentinel token stands for part of it), so the digest is the bytes; outside it there is none to give.
68
+ */
69
+ export declare function exactBoundInputHashOf(value: unknown): string | undefined;
70
+ /**
71
+ * Why a value is outside the bindable domain: its top is not the payload's shape, a refusal of the receipt it is read by, or one of
72
+ * JSON's own five — a number that is not finite, a hole in a list, a named member on a list, an `undefined` list element, one
73
+ * object met twice.
74
+ */
75
+ export type BindingFault = ReceiptFault | {
76
+ readonly kind: "not-container" | "not-finite" | "hole" | "list-member" | "undefined-element" | "shared";
77
+ readonly path: readonly string[];
78
+ };
79
+ /**
80
+ * THE judge of the BINDABLE domain — the values an approval can be bound to, and so the only values with an exact digest: plain
81
+ * JSON data (records, dense lists, strings, finite numbers, booleans, `null`; a record member that is `undefined` is kept as it
82
+ * is, as absent from JSON) under a top of the payload's own shape (`top`: a call's arguments are a RECORD — the default, and the
83
+ * only shape an exact digest is taken of; a question's answers are a LIST), a tree (no object met twice), at most 256 levels of
84
+ * containers. Read by the engine's one receipt of a caller's value in {@link BINDING_RECEIPT} (nothing of the caller's runs: a
85
+ * getter, a hidden or symbol-keyed member, a Proxy, a function, a symbol, a bigint, a Map / Set / Date / binary or any other kind
86
+ * of object, a cycle, nesting past the levels — each refused), then JSON's own five on the receipt's frozen copy. Ok ⇒ that copy.
87
+ */
88
+ export declare function bindableValueOf(value: unknown, top?: BindingTop): {
89
+ readonly ok: true;
90
+ readonly value: unknown;
91
+ } | {
92
+ readonly ok: false;
93
+ readonly fault: BindingFault;
94
+ };
95
+ /** The top a bound payload has: a call's arguments are a record, a question's answers a list. */
96
+ export type BindingTop = "record" | "list";
97
+ /** THE capture of a caller-supplied payload the engine persists and later compares (a durable decide's edit — a record —, a
98
+ * question's answers — a list): the judge's receipt ({@link bindableValueOf}) kept in the form a durable store's JSON round trip
99
+ * hands back ({@link jsonFormOf}). */
100
+ export declare function capturedJsonOf(value: unknown, top?: BindingTop): {
101
+ readonly ok: true;
102
+ readonly value: unknown;
103
+ } | {
104
+ readonly ok: false;
105
+ readonly fault: BindingFault;
106
+ };
107
+ /** One clause naming a binding fault, its path rooted at `args` (`value` is what the judge was given: for a value that is not
108
+ * data, its `typeof` is read at the fault's place). The text holds the value's own member names: contain it to show it. */
109
+ export declare function describeBindingFault(value: unknown, fault: BindingFault): string;
110
+ /**
111
+ * Recursion-depth ceiling. Makes {@link encode} TOTAL against the two ways a naive recursive walk throws:
112
+ * a CIRCULAR reference (infinite recursion → `Maximum call stack size`) and pathological DEEP nesting (a
113
+ * RangeError before the JS stack limit). At/below this depth a deterministic sentinel is emitted instead of
114
+ * recursing — so {@link boundInputHashOf} can never throw. This is load-bearing (MAJOR-4): digests are read after a remote
115
+ * VM is paused (the park's target mint compares one), where a throw would skip the unpause compensation and leak the VM.
116
+ * 256 is far below the engine's stack limit yet far deeper than any real tool-arg payload. It is also the bindable domain's
117
+ * bound ({@link BINDING_RECEIPT}), so no value with an exact digest meets the sentinel.
118
+ */
119
+ export declare const MAX_DEPTH = 256;
120
+ /** Canonical JSON (recursively sorted keys, no whitespace) — the erasure protocol's ONE comparison
121
+ * and hash basis: `selectHash` complains bind to it, and evidence rows are written/compared in this
122
+ * form so a re-serialized import (key order changed by a middle layer) still compares equal. The memory
123
+ * export bundle's section hashes are sha256 over it, and a backend that hashes or compares the same
124
+ * records calls this function — the bytes are the contract, so there is no second copy to keep in step.
125
+ *
126
+ * Not {@link canonicalize}: that one emits a type-tagged, length-prefixed form that is not JSON and is
127
+ * never compared across runtimes; this one emits JSON text for JSON-shaped input (an `undefined` member
128
+ * omitted, an `undefined` / function / symbol array element as `null`, a scalar through `JSON.stringify`,
129
+ * an object read by its own enumerable keys with no `toJSON` call). The two answer different questions
130
+ * and their outputs are not interchangeable. */
131
+ export declare function canonicalJsonStringify(value: unknown): string;
@@ -1,4 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { types } from "node:util";
3
+ import { describeFault, receive } from "./mods/kernel/receive.js";
2
4
  export function canonicalize(value) {
3
5
  try {
4
6
  return encode(value);
@@ -11,7 +13,115 @@ export const UNCANONICALIZABLE = "<uncanonicalizable>";
11
13
  export function boundInputHashOf(value) {
12
14
  return createHash("sha256").update(canonicalize(value), "utf8").digest("hex");
13
15
  }
14
- const MAX_DEPTH = 256;
16
+ export function exactBoundInputHashOf(value) {
17
+ const bound = bindableValueOf(value);
18
+ if (!bound.ok)
19
+ return undefined;
20
+ const form = canonicalize(bound.value);
21
+ return form === UNCANONICALIZABLE ? undefined : createHash("sha256").update(form, "utf8").digest("hex");
22
+ }
23
+ export function bindableValueOf(value, top = "record") {
24
+ const received = receive(value, BINDING_RECEIPT);
25
+ if (!received.ok)
26
+ return received;
27
+ const copy = received.value;
28
+ const topOk = top === "list" ? Array.isArray(copy) : typeof copy === "object" && copy !== null && !Array.isArray(copy);
29
+ if (!topOk)
30
+ return { ok: false, fault: { kind: "not-container", path: [] } };
31
+ const fault = jsonFaultIn(copy, [], new Set());
32
+ return fault === undefined ? { ok: true, value: received.value } : { ok: false, fault };
33
+ }
34
+ export function capturedJsonOf(value, top = "record") {
35
+ const bound = bindableValueOf(value, top);
36
+ return bound.ok ? { ok: true, value: jsonFormOf(bound.value) } : bound;
37
+ }
38
+ function jsonFormOf(value) {
39
+ if (Object.is(value, -0))
40
+ return 0;
41
+ if (typeof value !== "object" || value === null)
42
+ return value;
43
+ if (Array.isArray(value))
44
+ return value.map(jsonFormOf);
45
+ const out = {};
46
+ for (const [key, member] of Object.entries(value))
47
+ if (member !== undefined)
48
+ Object.defineProperty(out, key, { value: jsonFormOf(member), enumerable: true, writable: true, configurable: true });
49
+ return out;
50
+ }
51
+ function jsonFaultIn(v, path, met) {
52
+ if (typeof v === "number")
53
+ return Number.isFinite(v) ? undefined : { kind: "not-finite", path: [...path] };
54
+ if (typeof v !== "object" || v === null)
55
+ return undefined;
56
+ if (met.has(v))
57
+ return { kind: "shared", path: [...path] };
58
+ met.add(v);
59
+ const members = [];
60
+ if (Array.isArray(v)) {
61
+ for (let i = 0; i < v.length; i++) {
62
+ if (!Object.hasOwn(v, i))
63
+ return { kind: "hole", path: [...path, String(i)] };
64
+ if (v[i] === undefined)
65
+ return { kind: "undefined-element", path: [...path, String(i)] };
66
+ members.push([String(i), v[i]]);
67
+ }
68
+ const named = Object.keys(v).find((k) => !isIndexBelow(k, v.length));
69
+ if (named !== undefined)
70
+ return { kind: "list-member", path: [...path, named] };
71
+ }
72
+ else
73
+ for (const entry of Object.entries(v))
74
+ members.push(entry);
75
+ for (const [key, member] of members) {
76
+ path.push(key);
77
+ const fault = jsonFaultIn(member, path, met);
78
+ path.pop();
79
+ if (fault !== undefined)
80
+ return fault;
81
+ }
82
+ return undefined;
83
+ }
84
+ export function describeBindingFault(value, fault) {
85
+ const path = ["args", ...fault.path];
86
+ const at = ` at ${path.join(".")}`;
87
+ switch (fault.kind) {
88
+ case "not-container":
89
+ return `a value that is not a record${at}`;
90
+ case "not-finite":
91
+ return `a number that is not finite${at}`;
92
+ case "hole":
93
+ return `a hole in a list${at}`;
94
+ case "list-member":
95
+ return `a named member on a list${at}`;
96
+ case "undefined-element":
97
+ return `an undefined list element${at}`;
98
+ case "shared":
99
+ return `one object met twice${at} (a shared reference)`;
100
+ case "too-deep":
101
+ return `containers nested deeper than ${MAX_DEPTH} levels`;
102
+ case "not-data":
103
+ return `${kindOfValueAt(value, fault.path)}${at}`;
104
+ default:
105
+ return describeFault({ ...fault, path });
106
+ }
107
+ }
108
+ function kindOfValueAt(value, path) {
109
+ let at = value;
110
+ for (const key of path)
111
+ at = typeof at === "object" && at !== null ? at[key] : undefined;
112
+ if (typeof at === "function" || typeof at === "symbol" || typeof at === "bigint")
113
+ return `a ${typeof at}`;
114
+ return typeof at === "object" && at !== null ? "an object that is not a plain record or list" : "a value that is not data";
115
+ }
116
+ export const MAX_DEPTH = 256;
117
+ const BINDING_RECEIPT = Object.freeze({ accessors: "refuse", depth: MAX_DEPTH, past: "refuse", others: "refuse", freeze: true, isProxy: (v) => types.isProxy(v) });
118
+ const LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
119
+ function segment(text) {
120
+ if (!LONE_SURROGATE.test(text))
121
+ return `${Buffer.byteLength(text, "utf8")}:${text}`;
122
+ const spelled = JSON.stringify(text);
123
+ return `S${Buffer.byteLength(spelled, "utf8")}:${spelled}`;
124
+ }
15
125
  function encode(value, depth = 0) {
16
126
  if (value === null)
17
127
  return "N";
@@ -25,7 +135,7 @@ function encode(value, depth = 0) {
25
135
  case "bigint":
26
136
  return `i:${value.toString()}`;
27
137
  case "string":
28
- return `s${Buffer.byteLength(value, "utf8")}:${value}`;
138
+ return `s${segment(value)}`;
29
139
  case "function":
30
140
  return "fn";
31
141
  case "symbol":
@@ -66,7 +176,7 @@ function encodeOwnKeys(source, depth, skipIndicesBelow = 0) {
66
176
  catch {
67
177
  encoded = "<unreadable-property>";
68
178
  }
69
- out += `${Buffer.byteLength(k, "utf8")}:${k}=${encoded}`;
179
+ out += `${segment(k)}=${encoded}`;
70
180
  }
71
181
  return `${out}}`;
72
182
  }
@@ -91,7 +201,7 @@ function encodeBranded(value, depth) {
91
201
  try {
92
202
  const { buffer, byteOffset, byteLength, elements, family } = viewRange(value);
93
203
  const bytesToken = digestBytes(new Uint8Array(buffer, byteOffset, byteLength));
94
- return `V:${family}:${brand}:${byteLength}:${bytesToken}${encodeOwnKeys(value, depth, elements)}`;
204
+ return `V:${family}:${segment(brand)}:${byteLength}:${bytesToken}${encodeOwnKeys(value, depth, elements)}`;
95
205
  }
96
206
  catch {
97
207
  }
@@ -128,16 +238,16 @@ function encodeBranded(value, depth) {
128
238
  case "RegExp": {
129
239
  const source = String(regexpSourceGetter.call(value));
130
240
  const flags = String(regexpFlagsGetter.call(value));
131
- return `r${Buffer.byteLength(source, "utf8")}:${source}/${flags}${own}`;
241
+ return `r${segment(source)}/${flags}${own}`;
132
242
  }
133
243
  case "Error": {
134
244
  const name = String(value.name);
135
245
  const message = String(value.message);
136
- return `E${Buffer.byteLength(name, "utf8")}:${name}:${Buffer.byteLength(message, "utf8")}:${message}${own}`;
246
+ return `E${segment(name)}:${segment(message)}${own}`;
137
247
  }
138
248
  case "URL": {
139
249
  const href = String(urlHrefGetter.call(value));
140
- return `L${Buffer.byteLength(href, "utf8")}:${href}${own}`;
250
+ return `L${segment(href)}${own}`;
141
251
  }
142
252
  case "Boolean":
143
253
  case "Number":
@@ -158,7 +268,7 @@ function encodeBranded(value, depth) {
158
268
  }
159
269
  catch {
160
270
  }
161
- return `x${Buffer.byteLength(brand, "utf8")}:${brand}${own}`;
271
+ return `x${segment(brand)}${own}`;
162
272
  }
163
273
  function primordialGetter(proto, key) {
164
274
  const get = Object.getOwnPropertyDescriptor(proto, key)?.get;
@@ -236,3 +346,15 @@ function encodeNumber(n) {
236
346
  return "0";
237
347
  return String(n);
238
348
  }
349
+ export function canonicalJsonStringify(value) {
350
+ if (Array.isArray(value))
351
+ return `[${value.map((v) => (v === undefined || typeof v === "function" || typeof v === "symbol" ? "null" : canonicalJsonStringify(v))).join(",")}]`;
352
+ if (value !== null && typeof value === "object") {
353
+ const rec = value;
354
+ const keys = Object.keys(rec)
355
+ .filter((k) => rec[k] !== undefined)
356
+ .sort();
357
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJsonStringify(rec[k])}`).join(",")}}`;
358
+ }
359
+ return JSON.stringify(value);
360
+ }
@@ -6,7 +6,7 @@ import { type StoreFidelity, type Checkpoint, type CheckpointFeature, type Check
6
6
  * had carried beside the one its word now names, and the members added after the table, each a capability a reader
7
7
  * must have. Downstream reads them for display only (`CheckpointError.detail.requires`).
8
8
  */
9
- export declare const REQUIREMENT_WORDS: readonly ["resource", "binding", "token", "orgAdmission", "f012", "realApproval", "face", "requirements_digest", "org_admission_inherited", "org_write_grant", "org_governed", "delegation_provenance", "skill_grants", "skill_card_binding", "hands_coordinates", "hands_home", "path_rule_anchors", "path_rule_installations", "permission_mode", "plan_mode_restore", "persisted_rule_mandate", "deployment_authored", "call_names", "write_face", "denial_limit_fallback", "leg_scope", "tool_ceiling", "tool_lists_judged", "own_tool_lists"];
9
+ export declare const REQUIREMENT_WORDS: readonly ["resource", "binding", "token", "orgAdmission", "f012", "realApproval", "face", "requirements_digest", "org_admission_inherited", "org_write_grant", "org_governed", "delegation_provenance", "skill_grants", "skill_card_binding", "hands_coordinates", "hands_home", "path_rule_anchors", "path_rule_installations", "permission_mode", "plan_mode_restore", "persisted_rule_mandate", "deployment_authored", "call_names", "write_face", "denial_limit_fallback", "leg_scope", "tool_ceiling", "tool_lists_judged", "own_tool_lists", "approval_targets", "cross_boundary"];
10
10
  export type RequirementWord = (typeof REQUIREMENT_WORDS)[number];
11
11
  /** Runtime twin of {@link RequirementWord}. */
12
12
  export declare function isRequirementWord(word: unknown): word is RequirementWord;
@@ -227,6 +227,9 @@ export declare const CHECKPOINT_STATE_REQUIREMENTS: {
227
227
  readonly ownToolLists: {
228
228
  readonly word: "own_tool_lists";
229
229
  };
230
+ readonly crossBoundaryRecorded: {
231
+ readonly word: "cross_boundary";
232
+ };
230
233
  };
231
234
  export declare const PENDING_ACTION_REQUIREMENTS: {
232
235
  readonly tool_approval: {
@@ -308,9 +311,6 @@ export declare const PENDING_ACTION_REQUIREMENTS: {
308
311
  readonly implementationRevision: {
309
312
  readonly digest: "decision-read";
310
313
  };
311
- readonly rosterDigest: {
312
- readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
313
- };
314
314
  readonly family: {
315
315
  readonly digest: "decision-read";
316
316
  };
@@ -320,6 +320,9 @@ export declare const PENDING_ACTION_REQUIREMENTS: {
320
320
  readonly names: {
321
321
  readonly word: "call_names";
322
322
  };
323
+ readonly targets: {
324
+ readonly word: "approval_targets";
325
+ };
323
326
  readonly hasBidiControls: {
324
327
  readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
325
328
  };
@@ -582,6 +585,9 @@ export declare const CHECKPOINT_REQUIREMENTS: {
582
585
  readonly ownToolLists: {
583
586
  readonly word: "own_tool_lists";
584
587
  };
588
+ readonly crossBoundaryRecorded: {
589
+ readonly word: "cross_boundary";
590
+ };
585
591
  };
586
592
  };
587
593
  readonly resourceLedger: {
@@ -808,9 +814,6 @@ export declare const CHECKPOINT_REQUIREMENTS: {
808
814
  readonly implementationRevision: {
809
815
  readonly digest: "decision-read";
810
816
  };
811
- readonly rosterDigest: {
812
- readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
813
- };
814
817
  readonly family: {
815
818
  readonly digest: "decision-read";
816
819
  };
@@ -820,6 +823,9 @@ export declare const CHECKPOINT_REQUIREMENTS: {
820
823
  readonly names: {
821
824
  readonly word: "call_names";
822
825
  };
826
+ readonly targets: {
827
+ readonly word: "approval_targets";
828
+ };
823
829
  readonly hasBidiControls: {
824
830
  readonly ignorable: "shown, never decided (card, listing, transcript or reported count): every read of the name renders, carries transcript or counts";
825
831
  };
@@ -31,6 +31,8 @@ export const REQUIREMENT_WORDS = Object.freeze([
31
31
  "tool_ceiling",
32
32
  "tool_lists_judged",
33
33
  "own_tool_lists",
34
+ "approval_targets",
35
+ "cross_boundary",
34
36
  ]);
35
37
  export function isRequirementWord(word) {
36
38
  return typeof word === "string" && REQUIREMENT_WORDS.includes(word);
@@ -92,6 +94,7 @@ export const CHECKPOINT_STATE_REQUIREMENTS = {
92
94
  toolCeiling: { word: "tool_ceiling" },
93
95
  toolListsJudged: { word: "tool_lists_judged" },
94
96
  ownToolLists: { word: "own_tool_lists" },
97
+ crossBoundaryRecorded: { word: "cross_boundary" },
95
98
  };
96
99
  export const PENDING_ACTION_REQUIREMENTS = {
97
100
  tool_approval: {
@@ -121,10 +124,10 @@ export const PENDING_ACTION_REQUIREMENTS = {
121
124
  contractId: DECISION_READ,
122
125
  shapeDigest: DECISION_READ,
123
126
  implementationRevision: DECISION_READ,
124
- rosterDigest: SHOWN,
125
127
  family: DECISION_READ,
126
128
  pathTarget: DECISION_READ,
127
129
  names: { word: "call_names" },
130
+ targets: { word: "approval_targets" },
128
131
  hasBidiControls: SHOWN,
129
132
  batchToolCallIds: DECISION_READ,
130
133
  completedCallIds: SHOWN,