@sema-agent/core 7.24.4 → 7.25.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 (107) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/agents/peer-session-drain.d.ts +16 -1
  3. package/dist/agents/peer-session-drain.js +16 -1
  4. package/dist/agents/send-message-tool.d.ts +10 -2
  5. package/dist/agents/send-message-tool.js +18 -7
  6. package/dist/agents/subagent.js +13 -10
  7. package/dist/core/arg-summary.d.ts +0 -7
  8. package/dist/core/arg-summary.js +2 -1
  9. package/dist/core/background-agent-store.d.ts +3 -3
  10. package/dist/core/checkpoint-store.d.ts +1 -0
  11. package/dist/core/delegation-frames.d.ts +5 -0
  12. package/dist/core/gate-fold.js +85 -0
  13. package/dist/core/gate-lanes.js +23 -1
  14. package/dist/core/gate-outcome.d.ts +8 -2
  15. package/dist/core/gate-outcome.js +3 -1
  16. package/dist/core/hooks.d.ts +29 -0
  17. package/dist/core/hooks.js +2 -0
  18. package/dist/core/mailbox-store.d.ts +5 -0
  19. package/dist/core/mailbox-store.js +1 -1
  20. package/dist/core/parent-spec-seat.d.ts +7 -1
  21. package/dist/core/parent-spec-seat.js +2 -0
  22. package/dist/core/permission-rule-consent.js +29 -47
  23. package/dist/core/permission-rule-provider.js +4 -3
  24. package/dist/core/permission-rule-store.d.ts +143 -7
  25. package/dist/core/permission-rule-store.js +165 -15
  26. package/dist/core/runner/contracts.d.ts +36 -2
  27. package/dist/core/runner/gate-exit.d.ts +14 -0
  28. package/dist/core/runner/gate-exit.js +4 -0
  29. package/dist/core/runner/prepare-delegation-surface.d.ts +4 -4
  30. package/dist/core/runner/prepare-delegation-surface.js +2 -1
  31. package/dist/core/runner/prepare-gate-stations.d.ts +4 -0
  32. package/dist/core/runner/prepare-gate-stations.js +7 -4
  33. package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
  34. package/dist/core/runner/prepare-hands-readface.js +25 -10
  35. package/dist/core/runner/prepare-inherited-gate.d.ts +23 -0
  36. package/dist/core/runner/prepare-inherited-gate.js +11 -6
  37. package/dist/core/runner/prepare-policy-chain.d.ts +15 -1
  38. package/dist/core/runner/prepare-policy-chain.js +14 -4
  39. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -2
  40. package/dist/core/runner/prepare-suspend-saga.js +4 -3
  41. package/dist/core/runner/prepare-task.js +10 -9
  42. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
  43. package/dist/core/runner/prepare-wiring-manifest.js +8 -4
  44. package/dist/core/runner/resume-apply.js +2 -0
  45. package/dist/core/runner/run-harness-handlers.js +1 -0
  46. package/dist/core/runner/run-identity-wiring.d.ts +2 -2
  47. package/dist/core/runner/run-leg.js +26 -0
  48. package/dist/core/runner/run-notification-lane.d.ts +2 -2
  49. package/dist/core/runner/run-notification-lane.js +2 -1
  50. package/dist/core/runner/run-settle-and-teardown.js +1 -0
  51. package/dist/core/runner/session-rule-policy.d.ts +3 -4
  52. package/dist/core/runner/session-rule-policy.js +11 -3
  53. package/dist/core/session-policy-store.d.ts +27 -3
  54. package/dist/core/session-policy-store.js +12 -4
  55. package/dist/core/store-contracts/background-agent-store-contract.js +10 -0
  56. package/dist/core/store-contracts/peer-directory-contract.d.ts +23 -0
  57. package/dist/core/store-contracts/peer-directory-contract.js +82 -0
  58. package/dist/core/task-event.d.ts +6 -0
  59. package/dist/core/task-notification.d.ts +79 -4
  60. package/dist/core/task-notification.js +90 -1
  61. package/dist/core/task-registry-agent.js +1 -2
  62. package/dist/core/task-registry-monitor.js +2 -1
  63. package/dist/core/task-registry-shared.d.ts +53 -5
  64. package/dist/core/task-registry-shared.js +13 -2
  65. package/dist/core/task-registry.js +8 -5
  66. package/dist/core/task-spec.d.ts +12 -5
  67. package/dist/core/tool-catalog-entries.js +1 -0
  68. package/dist/core/tool-policy.js +30 -23
  69. package/dist/core/tool-spec.d.ts +1 -1
  70. package/dist/core/wiring-manifest.d.ts +2 -2
  71. package/dist/engine/loop/agent-loop.js +1 -1
  72. package/dist/engine/loop/types.d.ts +4 -0
  73. package/dist/index.d.ts +6 -3
  74. package/dist/index.js +4 -3
  75. package/dist/prompts/coordinator.d.ts +2 -1
  76. package/dist/stores/file/adoption/adopt.js +23 -1
  77. package/dist/stores/file/permission-rule-store.js +3 -1
  78. package/dist/stores/file/session-policy-store.d.ts +31 -0
  79. package/dist/stores/file/session-policy-store.js +87 -7
  80. package/dist/tools/fs/bash-lexer.d.ts +13 -0
  81. package/dist/tools/fs/bash-lexer.js +61 -0
  82. package/dist/tools/fs/bash-program-position.d.ts +36 -29
  83. package/dist/tools/fs/bash-program-position.js +33 -13
  84. package/dist/tools/fs/bash-readonly-classifier.d.ts +26 -313
  85. package/dist/tools/fs/bash-readonly-classifier.js +81 -782
  86. package/dist/tools/fs/fs-bash.d.ts +43 -207
  87. package/dist/tools/fs/fs-bash.js +20 -341
  88. package/dist/tools/fs/index.d.ts +12 -13
  89. package/dist/tools/fs/index.js +3 -4
  90. package/dist/tools/fs/read-deny.d.ts +25 -0
  91. package/dist/tools/fs/read-deny.js +19 -0
  92. package/dist/tools/fs/safety.js +2 -6
  93. package/dist/tools/fs/shell-read-check.d.ts +99 -0
  94. package/dist/tools/fs/shell-read-check.js +229 -0
  95. package/dist/tools/fs/shell-read-establish.d.ts +70 -115
  96. package/dist/tools/fs/shell-read-establish.js +216 -102
  97. package/dist/tools/fs/shell-read-judge.d.ts +231 -0
  98. package/dist/tools/fs/shell-read-judge.js +170 -0
  99. package/dist/tools/fs/shell-read-plan.d.ts +3 -0
  100. package/dist/tools/fs/shell-read-plan.js +88 -27
  101. package/dist/tools/monitor.d.ts +5 -5
  102. package/dist/tools/monitor.js +3 -5
  103. package/dist/tools/task-list.d.ts +4 -0
  104. package/package.json +1 -1
  105. package/test/export-surface.snapshot.json +19 -3
  106. package/wire-changes/core@7.25.0.json +671 -0
  107. package/wire-changes/core@7.25.1.json +64 -0
@@ -20,7 +20,8 @@ export function planShellReads(command, decl, depth = 0) {
20
20
  const shape = { grouped: false, backgrounded: false, strayRedirection: false };
21
21
  if (depth > NESTING_MAX)
22
22
  return { segments: [], unreadable: `command text nested more than ${NESTING_MAX} levels deep`, shape };
23
- const lexed = lexShellText(foldHomeVariableSpelling(command, decl.homeDir), { mode: "lenient" });
23
+ const homeReassigned = /(^|[\s;&|(){}`])(?:export\s+)?HOME\+?=/.test(command);
24
+ const lexed = lexShellText(foldHomeVariableSpelling(command, homeReassigned ? undefined : decl.homeDir), { mode: "lenient" });
24
25
  if ("reject" in lexed)
25
26
  return { segments: [], unreadable: lexed.reject, shape };
26
27
  const nestedPlan = (text) => planShellReads(text, decl, depth + 1);
@@ -140,12 +141,13 @@ export function planShellReads(command, decl, depth = 0) {
140
141
  launcherChain: f.launcherChain,
141
142
  ...(f.verb !== undefined ? { verb: f.verb } : {}),
142
143
  ...(f.reentry !== undefined ? { reentry: f.reentry } : {}),
144
+ ...(f.unreadProgramText !== undefined ? { unreadProgramText: f.unreadProgramText } : {}),
143
145
  declines: f.declines,
144
146
  reads: dedupe(f.reads).map(withBases),
145
147
  redirects: f.redirects.map((r) => (r.target === undefined ? { op: r.op, direction: r.direction } : { op: r.op, direction: r.direction, target: withBases(r.target) })),
146
148
  ...(f.baseMove !== undefined ? { baseMove: { target: typeof f.baseMove.target === "string" ? f.baseMove.target : withBases(f.baseMove.target) } } : {}),
147
149
  bases,
148
- nested: f.nested,
150
+ nested: f.nested.map((n) => rebasePlan(n, decl.cwd, bases)),
149
151
  reach: reaches[si] ?? { andJoined: false, orBroken: false, moveProvable: false },
150
152
  });
151
153
  }
@@ -251,8 +253,24 @@ function segmentFacts(segment, p, decl, nestedPlan) {
251
253
  const controlIndices = new Set(pos.controlWords);
252
254
  const assignmentIndices = new Set(pos.assignments);
253
255
  const chainEnd = pos.program?.index ?? toks.folded.length;
256
+ const launcherValueIndices = new Set();
257
+ for (const l of pos.launchers) {
258
+ const row = LAUNCHER_TABLE.get(programBasename(l.name));
259
+ if (row === undefined)
260
+ continue;
261
+ for (let j = l.index + 1; j < chainEnd; j++) {
262
+ const w = toks.folded[j];
263
+ if (!w.startsWith("-") || w === "-")
264
+ break;
265
+ const arity = row.optionArity.get(w);
266
+ if (arity === undefined)
267
+ break;
268
+ if (arity === 1)
269
+ launcherValueIndices.add(++j);
270
+ }
271
+ }
254
272
  for (let j = 0; j < chainEnd; j++) {
255
- if (launcherIndices.has(j) || controlIndices.has(j) || assignmentIndices.has(j) || j === written)
273
+ if (launcherIndices.has(j) || controlIndices.has(j) || assignmentIndices.has(j) || launcherValueIndices.has(j) || j === written)
256
274
  continue;
257
275
  const word = toks.folded[j];
258
276
  const raw = toks.raw[j];
@@ -267,7 +285,7 @@ function segmentFacts(segment, p, decl, nestedPlan) {
267
285
  reads.push({ spelled: word, raw, provenance: "unlisted_word", expands: "unresolvable" });
268
286
  continue;
269
287
  }
270
- if (assignmentWordPayload(word) !== undefined) {
288
+ if (assignmentWordPayload(word, toks.raw[j]) !== undefined) {
271
289
  pushAssignment(reads, toks, j, "env_assignment");
272
290
  continue;
273
291
  }
@@ -348,7 +366,8 @@ function segmentFacts(segment, p, decl, nestedPlan) {
348
366
  else {
349
367
  unlistedWords(reads, toks, program.index, launcherIndices, "unlisted_word", program);
350
368
  }
351
- if (basename === "sed" && listed) {
369
+ let unreadProgramText;
370
+ if (basename === "sed") {
352
371
  const slot = sedScriptSlotFiles(args);
353
372
  for (const f of slot.reads)
354
373
  reads.push(bare(f, f, "script_slot_file"));
@@ -356,6 +375,9 @@ function segmentFacts(segment, p, decl, nestedPlan) {
356
375
  reads.push(bare(f, f, "write_redirect_target"));
357
376
  for (const c of slot.commands)
358
377
  nested.push(nestedPlan(c));
378
+ if (slot.commands.length > 0 || slot.executesPatternSpace)
379
+ reentry = "shell";
380
+ unreadProgramText = slot.unread;
359
381
  }
360
382
  return {
361
383
  ...base,
@@ -363,6 +385,7 @@ function segmentFacts(segment, p, decl, nestedPlan) {
363
385
  launcherChain,
364
386
  verb: verb,
365
387
  ...(reentry !== undefined ? { reentry } : {}),
388
+ ...(unreadProgramText !== undefined ? { unreadProgramText } : {}),
366
389
  declines,
367
390
  reads,
368
391
  ...(baseMove !== undefined ? { baseMove } : {}),
@@ -371,6 +394,38 @@ function segmentFacts(segment, p, decl, nestedPlan) {
371
394
  loopEnd: false,
372
395
  };
373
396
  }
397
+ function rebasePlan(plan, from, to) {
398
+ if (to !== "unmodelled" && to.length === 1 && to[0] === from)
399
+ return plan;
400
+ const fromSep = from.endsWith("/") || from.endsWith("\\") ? from : `${from}/`;
401
+ const moveBase = (b) => {
402
+ if (to === "unmodelled")
403
+ return [];
404
+ if (b === from)
405
+ return [...to];
406
+ if (b.startsWith(fromSep))
407
+ return to.map((t) => `${t.endsWith("/") || t.endsWith("\\") ? t : `${t}/`}${b.slice(fromSep.length)}`);
408
+ return [b];
409
+ };
410
+ const moveCandidate = (c) => {
411
+ if (c.bases === "unmodelled" || c.bases.length === 0)
412
+ return c;
413
+ if (to === "unmodelled")
414
+ return { ...c, bases: "unmodelled" };
415
+ return { ...c, bases: [...new Set(c.bases.flatMap(moveBase))] };
416
+ };
417
+ return {
418
+ ...plan,
419
+ segments: plan.segments.map((s) => ({
420
+ ...s,
421
+ reads: s.reads.map(moveCandidate),
422
+ redirects: s.redirects.map((r) => (r.target === undefined ? r : { ...r, target: moveCandidate(r.target) })),
423
+ ...(s.baseMove !== undefined ? { baseMove: { target: typeof s.baseMove.target === "string" ? s.baseMove.target : moveCandidate(s.baseMove.target) } } : {}),
424
+ bases: s.bases === "unmodelled" || to === "unmodelled" ? "unmodelled" : [...new Set(s.bases.flatMap(moveBase))],
425
+ nested: s.nested.map((n) => rebasePlan(n, from, to)),
426
+ })),
427
+ };
428
+ }
374
429
  function dedupe(reads) {
375
430
  const seen = new Set();
376
431
  const out = [];
@@ -387,7 +442,7 @@ function bare(raw, folded, provenance) {
387
442
  return { spelled: folded, raw, provenance, expands: expandsOf(raw, folded) };
388
443
  }
389
444
  function expandsOf(raw, folded) {
390
- if (folded.includes(SUBSTITUTION_PLACEHOLDER) || hasUnquotedExpansionMetachar(raw))
445
+ if (folded.includes(SUBSTITUTION_PLACEHOLDER) || hasUnquotedExpansionMetachar(raw) || /[`(]/.test(raw) || raw.includes(")"))
391
446
  return "unresolvable";
392
447
  if (hasUnquotedGlobMetachar(raw))
393
448
  return "glob";
@@ -410,13 +465,13 @@ function redirectFact(r) {
410
465
  }
411
466
  function pushAssignment(reads, toks, k, provenance) {
412
467
  const word = toks.folded[k];
413
- const payload = assignmentWordPayload(word);
468
+ const payload = assignmentWordPayload(word, toks.raw[k]);
414
469
  if (payload === undefined)
415
470
  return;
416
- if (payload.kind === "value")
417
- reads.push(bare(toks.raw[k], payload.value, provenance));
418
- else
471
+ if (payload.kind === "unresolved")
419
472
  reads.push({ spelled: word, raw: toks.raw[k], provenance, expands: "unresolvable" });
473
+ if (payload.value !== undefined && payload.value !== SUBSTITUTION_PLACEHOLDER)
474
+ reads.push(bare(toks.raw[k], payload.value, provenance));
420
475
  }
421
476
  function unlistedWords(reads, toks, from, launcherIndices, provenance, program) {
422
477
  const argv = program !== undefined ? { program: program.basename, from: program.index } : argvProgramOf(toks);
@@ -429,6 +484,8 @@ function unlistedWords(reads, toks, from, launcherIndices, provenance, program)
429
484
  for (let j = from; j < toks.folded.length; j++) {
430
485
  if (launcherIndices.has(j))
431
486
  continue;
487
+ if (noPathOperands && j > argvStart)
488
+ break;
432
489
  if (expandsOf(toks.raw[j], toks.folded[j]) !== "unresolvable")
433
490
  continue;
434
491
  expanding.add(j);
@@ -476,7 +533,7 @@ function unlistedWords(reads, toks, from, launcherIndices, provenance, program)
476
533
  for (const c of named)
477
534
  reads.push(bare(toks.raw[j], c, "option_value"));
478
535
  if (named.length > 0)
479
- positional.push(value);
536
+ positional.push({ word: value, raw: toks.raw[j] });
480
537
  }
481
538
  }
482
539
  continue;
@@ -501,23 +558,27 @@ function unlistedWords(reads, toks, from, launcherIndices, provenance, program)
501
558
  if (m === undefined || m.dashIsStdin === true)
502
559
  stdinOperand = true;
503
560
  else
504
- positional.push(word);
561
+ positional.push({ word, raw });
505
562
  }
506
563
  else
507
- positional.push(word);
564
+ positional.push({ word, raw });
508
565
  }
509
566
  reads.push(bare(raw, word, j === argvStart ? "program_path" : provenance));
510
- if (j < argvStart || !declaredNotPaths)
567
+ if (j < argvStart)
511
568
  pushAssignment(reads, toks, j, "env_assignment");
569
+ else if (!declaredNotPaths)
570
+ pushAssignment(reads, toks, j, provenance);
512
571
  }
513
572
  if (slotVerb !== undefined && argv !== undefined && segmentSelectsRecursiveRead(slotVerb, toks.folded.slice(argvStart + 1))) {
514
- const roots = positional.length > 0 ? positional : stdinOperand ? [] : ["."];
573
+ const roots = positional.length > 0 ? positional : stdinOperand ? [] : [{ word: ".", raw: "." }];
515
574
  for (const root of roots)
516
- reads.push({ spelled: root, raw: root, provenance: "traversal_root", expands: false });
575
+ reads.push({ spelled: root.word, raw: root.raw, provenance: "traversal_root", expands: expandsOf(root.raw, root.word) });
517
576
  }
518
577
  for (let j = from; j < toks.folded.length; j++) {
519
578
  if (launcherIndices.has(j) || j === argvStart)
520
579
  continue;
580
+ if (noPathOperands && j > argvStart)
581
+ break;
521
582
  const raw = toks.raw[j];
522
583
  if (expandsOf(raw, toks.folded[j]) === "glob")
523
584
  reads.push(bare(raw, toks.folded[j], provenance));
@@ -645,7 +706,7 @@ function listedVerbCandidates(name, args, rawArgs, reads) {
645
706
  for (const c of named)
646
707
  push(k, c, "option_value");
647
708
  if (named.length > 0)
648
- positional.push(value);
709
+ positional.push({ word: value, raw: rawArgs[k] });
649
710
  }
650
711
  }
651
712
  continue;
@@ -665,7 +726,7 @@ function listedVerbCandidates(name, args, rawArgs, reads) {
665
726
  stdinOperand = true;
666
727
  continue;
667
728
  }
668
- positional.push(t);
729
+ positional.push({ word: t, raw: rawArgs[k] });
669
730
  continue;
670
731
  }
671
732
  if (inFirstOperandSlot) {
@@ -673,17 +734,17 @@ function listedVerbCandidates(name, args, rawArgs, reads) {
673
734
  for (const c of named)
674
735
  push(k, c, "operand");
675
736
  if (named.length > 0)
676
- positional.push(t);
737
+ positional.push({ word: t, raw: rawArgs[k] });
677
738
  continue;
678
739
  }
679
- positional.push(t);
740
+ positional.push({ word: t, raw: rawArgs[k] });
680
741
  push(k, t, "operand");
681
742
  if (!ASSIGNMENT_VALUE_NOT_PATHS.has(name)) {
682
- const payload = assignmentWordPayload(t);
683
- if (payload?.kind === "value")
684
- push(k, payload.value, "env_assignment");
685
- else if (payload !== undefined)
686
- reads.push({ spelled: t, raw: rawArgs[k], provenance: "env_assignment", expands: "unresolvable" });
743
+ const payload = assignmentWordPayload(t, rawArgs[k]);
744
+ if (payload?.kind === "unresolved")
745
+ reads.push({ spelled: t, raw: rawArgs[k], provenance: "operand", expands: "unresolvable" });
746
+ if (payload?.value !== undefined)
747
+ push(k, payload.value, "operand");
687
748
  }
688
749
  }
689
750
  for (let k = 0; k < args.length; k++) {
@@ -693,8 +754,8 @@ function listedVerbCandidates(name, args, rawArgs, reads) {
693
754
  reads.push(bare(rawArgs[k], args[k], "operand"));
694
755
  }
695
756
  if (segmentSelectsRecursiveRead(name, args)) {
696
- const roots = positional.length > 0 ? positional : stdinOperand ? [] : ["."];
757
+ const roots = positional.length > 0 ? positional : stdinOperand ? [] : [{ word: ".", raw: "." }];
697
758
  for (const root of roots)
698
- reads.push({ spelled: root, raw: root, provenance: "traversal_root", expands: false });
759
+ reads.push({ spelled: root.word, raw: root.raw, provenance: "traversal_root", expands: expandsOf(root.raw, root.word) });
699
760
  }
700
761
  }
@@ -42,7 +42,7 @@ import type { AgentTool, ExecutionEnv } from "../internal/harness-types.js";
42
42
  import { type MonitorTimers, type TaskRegistry } from "../core/task-registry.js";
43
43
  import type { TaskNotificationPayload } from "../core/task-notification.js";
44
44
  import type { ToolResultStore } from "../core/tool-result-store.js";
45
- import type { BashReadonlyRootBoundary } from "./fs/bash-readonly-classifier.js";
45
+ import type { ShellReadAdmission } from "./fs/shell-read-check.js";
46
46
  export interface MonitorToolOptions {
47
47
  registry: TaskRegistry;
48
48
  /** Fallbacks when the runtime ctx carries no identity (direct mounts) — same posture as createBashTool. */
@@ -69,10 +69,10 @@ export interface MonitorToolOptions {
69
69
  * task-registry-monitor.ts's spill helper). Absent ⇒ legal degrade, byte-identical rolling (no spill,
70
70
  * same loss shape as before this design). */
71
71
  toolResultStore?: ToolResultStore;
72
- /** The deployment's shell READ BOUNDARY — the SAME option `createBashTool` takes: this face spawns a
73
- * shell command too, so the seat that judges the WRITTEN spelling needs the leg that re-judges where the
74
- * kernel takes it. BUILT here, so a library mount is not fail-open on one face and closed on the other. */
75
- readBoundary?: BashReadonlyRootBoundary | (() => BashReadonlyRootBoundary | undefined);
72
+ /** The gate's shell read station's pre-spawn ADMISSION — the SAME option `createBashTool` takes: this face
73
+ * spawns a shell command too, so what it runs is what the station judged, re-established before the watch
74
+ * spawns. Absent ⇒ no station judged this mount; nothing is re-established. */
75
+ shellReadAdmission?: ShellReadAdmission;
76
76
  /** RB-465-b (HRD-PTL-3 残件): spec-level retain (`TaskSpec.retainBackgroundProcesses`) — the SECOND
77
77
  * retain lane, OR-composed with the env capability at the teardown sweep. Without this bit the mount
78
78
  * could only see the env lane, so a deployment retaining per spec still got the "session ends it"
@@ -3,7 +3,6 @@ import { defineTool, errorResult } from "../core/tools.js";
3
3
  import { hasBackgroundShell } from "../core/background-shell.js";
4
4
  import { MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, } from "../core/task-registry.js";
5
5
  import { toolFace } from "../core/tool-catalog-entries.js";
6
- import { createShellCanonicalRecheck, MONITOR_ESCAPE_PROSE } from "./fs/fs-bash.js";
7
6
  import { thrownText } from "../core/thrown-value.js";
8
7
  const MONITOR_DESCRIPTION = "Run a shell command in the background and watch its stdout as a stream of events.\n" +
9
8
  "\n" +
@@ -43,7 +42,6 @@ function envBackgroundWallSec(caps, requestedSec) {
43
42
  return Math.min(base, cap);
44
43
  }
45
44
  export function createMonitorTool(env, opts) {
46
- const canonicalRecheck = opts.readBoundary === undefined ? undefined : createShellCanonicalRecheck(env, opts.readBoundary, MONITOR_ESCAPE_PROSE);
47
45
  return defineTool({
48
46
  name: "Monitor",
49
47
  ...toolFace("monitor"),
@@ -70,9 +68,9 @@ export function createMonitorTool(env, opts) {
70
68
  const isPersistent = persistent === true;
71
69
  const timeoutMs = Math.min(MONITOR_MAX_TIMEOUT_MS, Math.max(1, Math.floor(timeout_ms ?? MONITOR_DEFAULT_TIMEOUT_MS)));
72
70
  const spawnTimeoutSec = isPersistent ? undefined : Math.ceil(timeoutMs / 1000) + 5;
73
- const escape = await canonicalRecheck?.(command, ctx.signal);
74
- if (escape !== undefined)
75
- return escape;
71
+ const refused = await opts.shellReadAdmission?.("Monitor", ctx.toolCallId, command, ctx.signal);
72
+ if (refused !== undefined)
73
+ return refused;
76
74
  if (ctx.signal?.aborted === true)
77
75
  return errorResult("Error (Monitor): the call was cancelled before the watch started.");
78
76
  const spawn = await env.spawnBackground(command, {
@@ -55,6 +55,10 @@ export interface TaskListItem {
55
55
  * (a real CC instance on the same directory) can set an id out of order, and then the backends
56
56
  * disagreed: memory replayed its Map's insertion order while file/cc read the id order back off
57
57
  * disk. Ordering is part of the contract, not each backend's incidental iteration order.
58
+ *
59
+ * @contract task_list.partition_is_root_session — a deployment that mounts a TaskListStore per session keys it by the
60
+ * delegation tree's ROOT session: every child of the tree (spawned live, or revived by a host verb after a durable park)
61
+ * reads the one list the leader reads; in-engine children share by construction, a per-call or per-reviver store is the divergence.
58
62
  */
59
63
  export interface TaskListStore {
60
64
  get(id: string): TaskListItem | undefined | Promise<TaskListItem | undefined>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.24.4",
3
+ "version": "7.25.1",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -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": 2402,
4
+ "count": 2410,
5
5
  "exports": {
6
6
  "A2ATaskState": "type",
7
7
  "A2ATaskStateReversal": "type",
@@ -60,6 +60,7 @@
60
60
  "AcquiredSession": "interface",
61
61
  "ActiveWorktreeSession": "interface",
62
62
  "ActorAssertion": "interface",
63
+ "AddResult": "type",
63
64
  "AddWorktreeOptions": "interface",
64
65
  "AdoptFileRuleStoreResult": "type",
65
66
  "AdoptLocalDataRootOptions": "interface",
@@ -793,6 +794,7 @@
793
794
  "MonitorTimers": "interface",
794
795
  "MonitorToolOptions": "interface",
795
796
  "NOTICE_AUDIENCE": "variable",
797
+ "NOTIFICATION_INJECTION_OUTCOMES": "variable",
796
798
  "NO_PERSISTENT_MEMORY_NOTICE": "variable",
797
799
  "NamedToolPolicy": "type",
798
800
  "NamedWorkflowListing": "interface",
@@ -804,6 +806,7 @@
804
806
  "NormalizedReadDenyEntry": "interface",
805
807
  "NotePatch": "interface",
806
808
  "NoticeAudience": "type",
809
+ "NotificationInjectionOutcome": "type",
807
810
  "OFFLOAD_TOOL_NAME": "variable",
808
811
  "ORCHESTRATION_AWARENESS": "variable",
809
812
  "ORCHESTRATION_GUIDANCE": "variable",
@@ -853,6 +856,7 @@
853
856
  "PEER_ADDRESS_PREFIXES": "variable",
854
857
  "PEER_ADMISSION_DEFAULTS": "variable",
855
858
  "PEER_DELIVERY_RECEIPT_STATES": "variable",
859
+ "PEER_DIRECTORY_CONTRACT_SCOPE": "variable",
856
860
  "PEER_DROP_REASONS": "variable",
857
861
  "PEER_HELD_QUEUE_CAP": "variable",
858
862
  "PEER_HELD_REVIEW_CAUSES": "variable",
@@ -922,6 +926,7 @@
922
926
  "PeerDeliveryReceiptState": "type",
923
927
  "PeerDirectory": "interface",
924
928
  "PeerDirectoryAccess": "interface",
929
+ "PeerDirectoryContractHooks": "interface",
925
930
  "PeerDropReason": "type",
926
931
  "PeerHeldEntry": "interface",
927
932
  "PeerHeldQueue": "interface",
@@ -1204,6 +1209,7 @@
1204
1209
  "RuleSyncResponseBody": "interface",
1205
1210
  "RuleSyncState": "interface",
1206
1211
  "RuleTicket": "type",
1212
+ "RuleTightenAddDelta": "interface",
1207
1213
  "RuleTombstone": "interface",
1208
1214
  "RuleWriteDelta": "type",
1209
1215
  "RuleWriteOutcome": "type",
@@ -1625,6 +1631,7 @@
1625
1631
  "ackAdoptionConfig": "function",
1626
1632
  "acquireCcLock": "function",
1627
1633
  "addDotsOf": "function",
1634
+ "addPersistedRule": "function",
1628
1635
  "addWorktree": "function",
1629
1636
  "adjudicateModelRoute": "function",
1630
1637
  "adjudicatePersistedPathRules": "function",
@@ -1670,7 +1677,6 @@
1670
1677
  "autoRunOnRecommendationEffective": "function",
1671
1678
  "backgroundAgentStoreContract": "function",
1672
1679
  "backgroundAgentStoreScopesContract": "function",
1673
- "bashReadBoundaryProbe": "function",
1674
1680
  "bashReversibilityProbe": "function",
1675
1681
  "boundaryPublishVerdict": "function",
1676
1682
  "boundedRedactedSummary": "function",
@@ -1984,6 +1990,7 @@
1984
1990
  "isModelGatedForClass": "function",
1985
1991
  "isModelPriced": "function",
1986
1992
  "isNamespacedCoveringRuleName": "function",
1993
+ "isNotificationInjectionOutcome": "function",
1987
1994
  "isPeerDeliveryReceiptState": "function",
1988
1995
  "isPeerIdleNoticeKind": "function",
1989
1996
  "isPeerSessionId": "function",
@@ -2132,6 +2139,7 @@
2132
2139
  "peekMemoryAnnouncements": "function",
2133
2140
  "peerAdmissionFor": "function",
2134
2141
  "peerAxisToken": "function",
2142
+ "peerDirectoryContract": "function",
2135
2143
  "peerHeldQueueFor": "function",
2136
2144
  "peerHeldQueueIfAny": "function",
2137
2145
  "peerHeldQueueKey": "function",
@@ -2464,6 +2472,7 @@
2464
2472
  "AcquiredSession": "advanced",
2465
2473
  "ActiveWorktreeSession": "internal",
2466
2474
  "ActorAssertion": "advanced",
2475
+ "AddResult": "advanced",
2467
2476
  "AddWorktreeOptions": "advanced",
2468
2477
  "AdoptFileRuleStoreResult": "advanced",
2469
2478
  "AdoptLocalDataRootOptions": "advanced",
@@ -3197,6 +3206,7 @@
3197
3206
  "MonitorTimers": "advanced",
3198
3207
  "MonitorToolOptions": "advanced",
3199
3208
  "NOTICE_AUDIENCE": "advanced",
3209
+ "NOTIFICATION_INJECTION_OUTCOMES": "advanced",
3200
3210
  "NO_PERSISTENT_MEMORY_NOTICE": "advanced",
3201
3211
  "NamedToolPolicy": "advanced",
3202
3212
  "NamedWorkflowListing": "advanced",
@@ -3208,6 +3218,7 @@
3208
3218
  "NormalizedReadDenyEntry": "stable",
3209
3219
  "NotePatch": "stable",
3210
3220
  "NoticeAudience": "advanced",
3221
+ "NotificationInjectionOutcome": "advanced",
3211
3222
  "OFFLOAD_TOOL_NAME": "advanced",
3212
3223
  "ORCHESTRATION_AWARENESS": "advanced",
3213
3224
  "ORCHESTRATION_GUIDANCE": "advanced",
@@ -3257,6 +3268,7 @@
3257
3268
  "PEER_ADDRESS_PREFIXES": "advanced",
3258
3269
  "PEER_ADMISSION_DEFAULTS": "advanced",
3259
3270
  "PEER_DELIVERY_RECEIPT_STATES": "advanced",
3271
+ "PEER_DIRECTORY_CONTRACT_SCOPE": "advanced",
3260
3272
  "PEER_DROP_REASONS": "advanced",
3261
3273
  "PEER_HELD_QUEUE_CAP": "advanced",
3262
3274
  "PEER_HELD_REVIEW_CAUSES": "advanced",
@@ -3326,6 +3338,7 @@
3326
3338
  "PeerDeliveryReceiptState": "advanced",
3327
3339
  "PeerDirectory": "advanced",
3328
3340
  "PeerDirectoryAccess": "advanced",
3341
+ "PeerDirectoryContractHooks": "advanced",
3329
3342
  "PeerDropReason": "advanced",
3330
3343
  "PeerHeldEntry": "advanced",
3331
3344
  "PeerHeldQueue": "advanced",
@@ -3608,6 +3621,7 @@
3608
3621
  "RuleSyncResponseBody": "advanced",
3609
3622
  "RuleSyncState": "advanced",
3610
3623
  "RuleTicket": "advanced",
3624
+ "RuleTightenAddDelta": "advanced",
3611
3625
  "RuleTombstone": "advanced",
3612
3626
  "RuleWriteDelta": "advanced",
3613
3627
  "RuleWriteOutcome": "advanced",
@@ -4029,6 +4043,7 @@
4029
4043
  "ackAdoptionConfig": "advanced",
4030
4044
  "acquireCcLock": "advanced",
4031
4045
  "addDotsOf": "advanced",
4046
+ "addPersistedRule": "advanced",
4032
4047
  "addWorktree": "advanced",
4033
4048
  "adjudicateModelRoute": "advanced",
4034
4049
  "adjudicatePersistedPathRules": "advanced",
@@ -4074,7 +4089,6 @@
4074
4089
  "autoRunOnRecommendationEffective": "internal",
4075
4090
  "backgroundAgentStoreContract": "advanced",
4076
4091
  "backgroundAgentStoreScopesContract": "advanced",
4077
- "bashReadBoundaryProbe": "advanced",
4078
4092
  "bashReversibilityProbe": "advanced",
4079
4093
  "boundaryPublishVerdict": "advanced",
4080
4094
  "boundedRedactedSummary": "advanced",
@@ -4388,6 +4402,7 @@
4388
4402
  "isModelGatedForClass": "advanced",
4389
4403
  "isModelPriced": "advanced",
4390
4404
  "isNamespacedCoveringRuleName": "advanced",
4405
+ "isNotificationInjectionOutcome": "advanced",
4391
4406
  "isPeerDeliveryReceiptState": "advanced",
4392
4407
  "isPeerIdleNoticeKind": "advanced",
4393
4408
  "isPeerSessionId": "advanced",
@@ -4536,6 +4551,7 @@
4536
4551
  "peekMemoryAnnouncements": "advanced",
4537
4552
  "peerAdmissionFor": "advanced",
4538
4553
  "peerAxisToken": "advanced",
4554
+ "peerDirectoryContract": "advanced",
4539
4555
  "peerHeldQueueFor": "advanced",
4540
4556
  "peerHeldQueueIfAny": "advanced",
4541
4557
  "peerHeldQueueKey": "advanced",