@sema-agent/core 3.0.0 → 5.0.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 (153) hide show
  1. package/CHANGELOG.md +2382 -0
  2. package/dist/agents/cascade.d.ts +3 -1
  3. package/dist/agents/cascade.js +31 -32
  4. package/dist/agents/cumulative-stats.d.ts +29 -0
  5. package/dist/agents/cumulative-stats.js +29 -0
  6. package/dist/agents/observer.d.ts +4 -0
  7. package/dist/agents/observer.js +11 -10
  8. package/dist/agents/repair-loop.d.ts +2 -0
  9. package/dist/agents/repair-loop.js +48 -24
  10. package/dist/agents/retain-ledger.d.ts +2 -0
  11. package/dist/agents/retain-ledger.js +6 -5
  12. package/dist/agents/roster-store.d.ts +1 -0
  13. package/dist/agents/roster-store.js +3 -5
  14. package/dist/agents/send-message-tool.d.ts +1 -0
  15. package/dist/agents/send-message-tool.js +2 -0
  16. package/dist/agents/subagent.d.ts +3 -1
  17. package/dist/agents/subagent.js +79 -94
  18. package/dist/agents/teacher.d.ts +1 -9
  19. package/dist/agents/teacher.js +21 -24
  20. package/dist/agents/tool-filter.js +6 -7
  21. package/dist/agents/verify.d.ts +1 -0
  22. package/dist/agents/verify.js +15 -14
  23. package/dist/brain/anthropic.js +24 -21
  24. package/dist/brain/openai.js +24 -21
  25. package/dist/brain/stream-engine.js +5 -2
  26. package/dist/brain/stream-shared.d.ts +1 -0
  27. package/dist/brain/stream-shared.js +14 -0
  28. package/dist/brain/terminal-cause.d.ts +4 -0
  29. package/dist/brain/terminal-cause.js +12 -0
  30. package/dist/brain/tool-call-id.d.ts +1 -0
  31. package/dist/brain/tool-call-id.js +3 -0
  32. package/dist/brain/tool-call-repair.js +2 -1
  33. package/dist/core/arg-summary.d.ts +21 -1
  34. package/dist/core/arg-summary.js +61 -14
  35. package/dist/core/auto-compaction.d.ts +1 -0
  36. package/dist/core/auto-compaction.js +16 -25
  37. package/dist/core/background-agent-store.d.ts +22 -22
  38. package/dist/core/background-agent-store.js +19 -23
  39. package/dist/core/checkpoint-store.d.ts +6 -13
  40. package/dist/core/checkpoint-store.js +11 -5
  41. package/dist/core/file-snapshot-store.d.ts +8 -0
  42. package/dist/core/file-snapshot-store.js +4 -3
  43. package/dist/core/fs-write-gate-policy.js +2 -3
  44. package/dist/core/hooks.d.ts +4 -1
  45. package/dist/core/hooks.js +8 -18
  46. package/dist/core/lsp.js +4 -3
  47. package/dist/core/mailbox-store.d.ts +7 -10
  48. package/dist/core/mcp.d.ts +7 -10
  49. package/dist/core/mcp.js +7 -9
  50. package/dist/core/memory-engine/engine.d.ts +0 -1
  51. package/dist/core/memory-engine/engine.js +24 -34
  52. package/dist/core/memory-engine/file-backend.d.ts +1 -2
  53. package/dist/core/memory-engine/file-backend.js +25 -36
  54. package/dist/core/memory-engine/layout.d.ts +8 -0
  55. package/dist/core/memory-engine/layout.js +60 -4
  56. package/dist/core/memory-engine/scope-contract.js +9 -3
  57. package/dist/core/memory-engine/types.d.ts +1 -1
  58. package/dist/core/permission-rules.js +2 -3
  59. package/dist/core/runner/active-skill-scope.d.ts +0 -1
  60. package/dist/core/runner/active-skill-scope.js +2 -17
  61. package/dist/core/runner/assemble-result.d.ts +2 -7
  62. package/dist/core/runner/assemble-result.js +3 -4
  63. package/dist/core/runner/compaction-call-options.d.ts +7 -0
  64. package/dist/core/runner/compaction-call-options.js +30 -0
  65. package/dist/core/runner/prepare-task.d.ts +6 -23
  66. package/dist/core/runner/prepare-task.js +36 -61
  67. package/dist/core/runner/runtask.d.ts +1 -0
  68. package/dist/core/runner/runtask.js +84 -172
  69. package/dist/core/runner/session-rule-policy.js +7 -5
  70. package/dist/core/runner/teardown-bounded.d.ts +7 -0
  71. package/dist/core/runner/teardown-bounded.js +36 -0
  72. package/dist/core/runner/tool-disclosure.d.ts +2 -5
  73. package/dist/core/runner/turn-attachments.d.ts +5 -15
  74. package/dist/core/runner/turn-attachments.js +1 -1
  75. package/dist/core/safe-notify.d.ts +17 -0
  76. package/dist/core/safe-notify.js +59 -0
  77. package/dist/core/sensitive-path-policy.js +4 -4
  78. package/dist/core/session-reconcile.js +1 -2
  79. package/dist/core/skill-tool-specifier.js +2 -3
  80. package/dist/core/skills-directory.js +2 -3
  81. package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
  82. package/dist/core/stub-env.d.ts +2 -6
  83. package/dist/core/surrogate-safe-slice.d.ts +4 -0
  84. package/dist/core/surrogate-safe-slice.js +18 -0
  85. package/dist/core/task-registry-monitor.js +8 -13
  86. package/dist/core/task-registry-shared.d.ts +23 -1
  87. package/dist/core/task-registry-shared.js +16 -0
  88. package/dist/core/task-registry.d.ts +6 -1
  89. package/dist/core/task-registry.js +52 -87
  90. package/dist/core/task-tool-shape.d.ts +0 -2
  91. package/dist/core/task-tool-shape.js +2 -5
  92. package/dist/core/tool-errors.js +5 -4
  93. package/dist/core/tool-name-aliases.d.ts +1 -2
  94. package/dist/core/tool-name-aliases.js +42 -59
  95. package/dist/core/tool-policy.js +21 -17
  96. package/dist/core/tool-result-store.d.ts +1 -0
  97. package/dist/core/tool-result-store.js +17 -1
  98. package/dist/core/trace.d.ts +9 -9
  99. package/dist/core/types.d.ts +15 -12
  100. package/dist/core/untrusted-egress.d.ts +4 -2
  101. package/dist/core/untrusted-egress.js +28 -14
  102. package/dist/core/workflow-journal-store.d.ts +9 -20
  103. package/dist/core/workflow-run-store.d.ts +11 -10
  104. package/dist/core/workflow-run-store.js +22 -16
  105. package/dist/engine/compaction/utils.js +4 -2
  106. package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
  107. package/dist/engine/execution-env/node-execution-env.js +1 -1
  108. package/dist/engine/harness/agent-harness.d.ts +1 -0
  109. package/dist/engine/harness/agent-harness.js +13 -33
  110. package/dist/engine/harness/types.d.ts +29 -67
  111. package/dist/engine/llm/types.d.ts +1 -1
  112. package/dist/engine/loop/agent-loop.js +12 -14
  113. package/dist/engine/loop/types.d.ts +11 -9
  114. package/dist/engine/session/memory-repo.d.ts +2 -6
  115. package/dist/engine/session/session.d.ts +5 -30
  116. package/dist/index.d.ts +6 -3
  117. package/dist/index.js +4 -3
  118. package/dist/internal/harness-types.d.ts +3 -1
  119. package/dist/orchestration/run-spec.js +6 -4
  120. package/dist/orchestration/run-workflow-tool.js +3 -36
  121. package/dist/orchestration/workflow-governance.js +2 -1
  122. package/dist/orchestration/workflow.d.ts +4 -11
  123. package/dist/orchestration/workflow.js +164 -204
  124. package/dist/prompt-assembly/packs/sema-default.js +1 -3
  125. package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
  126. package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
  127. package/dist/prompts/default.js +1 -3
  128. package/dist/prompts/simple-sections.d.ts +0 -1
  129. package/dist/prompts/simple-sections.js +0 -1
  130. package/dist/stores/file/background-agent-store.d.ts +3 -12
  131. package/dist/stores/file/background-agent-store.js +3 -41
  132. package/dist/stores/file/checkpoint-store.js +6 -6
  133. package/dist/stores/file/mailbox-store.d.ts +2 -6
  134. package/dist/stores/file/session-policy-store.js +15 -1
  135. package/dist/stores/file/session-store.d.ts +2 -6
  136. package/dist/stores/file/workflow-journal-store.d.ts +3 -11
  137. package/dist/stores/file/workflow-run-store.d.ts +3 -7
  138. package/dist/stores/file/workflow-run-store.js +4 -17
  139. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -0
  140. package/dist/tools/fs/bash-readonly-classifier.js +60 -25
  141. package/dist/tools/fs/fs-bash.js +330 -104
  142. package/dist/tools/fs/fs-pdf.d.ts +2 -12
  143. package/dist/tools/fs/fs-shared.js +2 -2
  144. package/dist/tools/fs/fs-write.js +0 -1
  145. package/dist/tools/fs/notebook.d.ts +2 -5
  146. package/dist/tools/fs/safety.d.ts +3 -0
  147. package/dist/tools/fs/safety.js +43 -14
  148. package/dist/tools/fs/search.js +5 -5
  149. package/dist/tools/monitor.d.ts +1 -0
  150. package/dist/tools/monitor.js +7 -5
  151. package/dist/tools/worktree.d.ts +2 -7
  152. package/dist/tools/worktree.js +3 -1
  153. package/package.json +6 -2
@@ -4,11 +4,12 @@ import { hasBackgroundShell } from "./background-shell.js";
4
4
  import { shutdownDebug } from "./shutdown-debug.js";
5
5
  import { canAccessAgentRecord, } from "./background-agent-store.js";
6
6
  import { delimitUntrusted } from "./untrusted-text.js";
7
+ import { createSafeNotifier } from "./safe-notify.js";
7
8
  import { registerMonitorLane, pollMonitorLane, stopMonitorLane } from "./task-registry-monitor.js";
8
9
  import { registerWorkflowLane, pollWorkflowLane, stopWorkflowLane } from "./task-registry-workflow.js";
9
- import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, droppedGapNote, alreadyTerminalStopNote, terminalTaskSummary, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
10
+ import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, renderSpoolBody, spoolDropNote, droppedGapNote, alreadyTerminalStopNote, terminalTaskSummary, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
10
11
  export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
11
- import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_ALIASES, TASK_STOP_ALIASES, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
12
+ import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
12
13
  import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
13
14
  export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
14
15
  const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
@@ -47,6 +48,31 @@ function bashTerminalResult(spool) {
47
48
  const err = spool.stderr.slice(-2_000);
48
49
  return err.length > 0 ? `${out}${out.length > 0 ? "\n" : ""}--- stderr (tail) ---\n${err}` : out;
49
50
  }
51
+ async function drainBashTailBestEffort(handle) {
52
+ if (handle.outputFile === undefined)
53
+ return;
54
+ try {
55
+ const drained = await handle.env.pollBackground(handle.shellId);
56
+ if (drained.ok && handle.spool !== undefined)
57
+ accountDroppedBytes(handle.spool, drained.value);
58
+ if (drained.ok && (drained.value.stdout.length > 0 || drained.value.stderr.length > 0)) {
59
+ if (handle.spool !== undefined) {
60
+ const spool = handle.spool;
61
+ const cap = 2 * TASK_OUTPUT_MAX_CHARS;
62
+ spool.stdout = rollSpoolText(spool, spool.stdout + drained.value.stdout, cap);
63
+ spool.stderr = rollSpoolText(spool, spool.stderr + drained.value.stderr, cap);
64
+ const ap = await handle.env.appendFile(handle.outputFile, drained.value.stdout + drained.value.stderr);
65
+ if (!ap.ok)
66
+ spool.mirrorFailed = true;
67
+ }
68
+ else {
69
+ await handle.env.appendFile(handle.outputFile, drained.value.stdout + drained.value.stderr);
70
+ }
71
+ }
72
+ }
73
+ catch {
74
+ }
75
+ }
50
76
  function bashMirrorGapNote(handle) {
51
77
  return handle.outputFile !== undefined && handle.spool?.mirrorFailed === true
52
78
  ? " [!] The output file is INCOMPLETE (one or more writes to it failed) — trust the result text / TaskOutput over the file."
@@ -76,7 +102,10 @@ export class TaskRegistry {
76
102
  handles = new Map();
77
103
  sessionReapHooks = new Set();
78
104
  bgQuiescenceWatchers = new Map();
79
- legacyToTaskId = new Map();
105
+ notifier = createSafeNotifier();
106
+ notifierFailureCounts() {
107
+ return this.notifier.failedSites().map((site) => ({ site, count: this.notifier.failuresAt(site) }));
108
+ }
80
109
  core = (() => {
81
110
  const self = this;
82
111
  return {
@@ -199,7 +228,7 @@ export class TaskRegistry {
199
228
  const prefix = TASK_PREFIX[type];
200
229
  for (let i = 0; i < 32; i++) {
201
230
  const id = `${prefix}${randomBytes(8).toString("hex")}`;
202
- if (!this.handles.has(id) && !this.legacyToTaskId.has(id))
231
+ if (!this.handles.has(id))
203
232
  return id;
204
233
  }
205
234
  throw new Error(`TaskRegistry: exhausted task id mint retries for ${type}`);
@@ -226,7 +255,6 @@ export class TaskRegistry {
226
255
  ...(input.onTerminal !== undefined ? { onTerminal: input.onTerminal } : {}),
227
256
  ...(input.outputFile !== undefined ? { outputFile: input.outputFile } : {}),
228
257
  });
229
- this.legacyToTaskId.set(String(input.shellId), id);
230
258
  if (input.onTerminal)
231
259
  this.startBashWatcher(id, input.onTerminal);
232
260
  return id;
@@ -263,6 +291,7 @@ export class TaskRegistry {
263
291
  ...(hb.parentTaskId !== undefined ? { parentTaskId: hb.parentTaskId } : {}),
264
292
  ...(hb.parentSessionId !== undefined ? { parentSessionId: hb.parentSessionId } : {}),
265
293
  ...(hb.rootSessionId !== undefined ? { rootSessionId: hb.rootSessionId } : {}),
294
+ createdAt: h.createdAt,
266
295
  };
267
296
  }
268
297
  markStopSource(id, source) {
@@ -291,17 +320,9 @@ export class TaskRegistry {
291
320
  return marked;
292
321
  }
293
322
  markStopSourceByShellId(shellId, source, env) {
294
- const sid = String(shellId);
295
- const taskId = this.legacyToTaskId.get(sid);
296
- if (taskId !== undefined) {
297
- const handle = this.handles.get(taskId);
298
- if (handle && handle.type === "background_bash" && (env === undefined || handle.env === env)) {
299
- this.markStopSource(taskId, source);
300
- return;
301
- }
302
- }
303
323
  if (env === undefined)
304
324
  return;
325
+ const sid = String(shellId);
305
326
  for (const [id, handle] of this.handles) {
306
327
  if (handle.type === "background_bash" && String(handle.shellId) === sid && handle.env === env) {
307
328
  this.markStopSource(id, source);
@@ -322,9 +343,6 @@ export class TaskRegistry {
322
343
  }
323
344
  return true;
324
345
  };
325
- const taskId = this.legacyToTaskId.get(sid);
326
- if (taskId !== undefined && clear(taskId))
327
- return;
328
346
  if (env === undefined)
329
347
  return;
330
348
  for (const [id, handle] of this.handles) {
@@ -407,22 +425,7 @@ export class TaskRegistry {
407
425
  handle.stoppedBy = handle.stopSource ?? source;
408
426
  handle.updatedAt = Date.now();
409
427
  mintCompletionId(handle);
410
- if (handle.outputFile !== undefined) {
411
- try {
412
- const drained = await handle.env.pollBackground(handle.shellId);
413
- if (drained.ok && (drained.value.stdout.length > 0 || drained.value.stderr.length > 0)) {
414
- const spool = (handle.spool ??= { stdout: "", stderr: "", rolledChars: 0 });
415
- const cap = 2 * TASK_OUTPUT_MAX_CHARS;
416
- spool.stdout = rollSpoolText(spool, spool.stdout + drained.value.stdout, cap);
417
- spool.stderr = rollSpoolText(spool, spool.stderr + drained.value.stderr, cap);
418
- const ap = await handle.env.appendFile(handle.outputFile, drained.value.stdout + drained.value.stderr);
419
- if (!ap.ok)
420
- spool.mirrorFailed = true;
421
- }
422
- }
423
- catch {
424
- }
425
- }
428
+ await drainBashTailBestEffort(handle);
426
429
  const resultText = handle.spool !== undefined ? bashTerminalResult(handle.spool) : "";
427
430
  this.notifyTerminalOnce(handle, () => handle.onTerminal?.({
428
431
  task_id: handle.id,
@@ -521,21 +524,13 @@ export class TaskRegistry {
521
524
  this.settleBackgroundAgent(handle.id, { status: "killed", error: "session released", cycle: handle.reviveCycle ?? 0 });
522
525
  if (reapTerminalNote !== undefined && handle.terminalNotified !== true) {
523
526
  handle.terminalNotified = true;
524
- try {
525
- reapTerminalNote();
526
- }
527
- catch {
528
- }
527
+ this.notifier.notify(() => reapTerminalNote(), "taskRegistry.reapTerminalNote");
529
528
  }
530
529
  reaped++;
531
530
  }
532
531
  this.pokeBgQuiescence(sessionId);
533
532
  for (const hook of this.sessionReapHooks) {
534
- try {
535
- hook(sessionId, scope);
536
- }
537
- catch {
538
- }
533
+ this.notifier.notify(() => hook(sessionId, scope), "taskRegistry.onSessionReap");
539
534
  }
540
535
  return reaped;
541
536
  }
@@ -585,11 +580,7 @@ export class TaskRegistry {
585
580
  this.bgQuiescenceWatchers.delete(oldest);
586
581
  queueMicrotask(() => {
587
582
  for (const victim of victims) {
588
- try {
589
- victim("evicted");
590
- }
591
- catch {
592
- }
583
+ this.notifier.notify(() => victim("evicted"), "taskRegistry.bgQuiescence.evicted");
593
584
  }
594
585
  });
595
586
  }
@@ -617,22 +608,14 @@ export class TaskRegistry {
617
608
  return;
618
609
  this.bgQuiescenceWatchers.delete(owner);
619
610
  for (const cb of set) {
620
- try {
621
- cb("quiescent");
622
- }
623
- catch {
624
- }
611
+ this.notifier.notify(() => cb("quiescent"), "taskRegistry.bgQuiescence.quiescent");
625
612
  }
626
613
  }
627
614
  notifyTerminalOnce(handle, send) {
628
615
  if (handle.terminalNotified === true)
629
616
  return;
630
617
  handle.terminalNotified = true;
631
- try {
632
- send();
633
- }
634
- catch {
635
- }
618
+ this.notifier.notify(() => send(), "taskRegistry.notifyTerminalOnce");
636
619
  this.pokeBgQuiescence(handle.owner);
637
620
  }
638
621
  startBashWatcher(id, onTerminal) {
@@ -966,11 +949,8 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
966
949
  if (now - handle.updatedAt < terminalTtlMs)
967
950
  continue;
968
951
  this.handles.delete(id);
969
- if (handle.type === "background_bash") {
970
- this.legacyToTaskId.delete(String(handle.shellId));
971
- if (handle.watcher !== undefined)
972
- clearInterval(handle.watcher);
973
- }
952
+ if (handle.type === "background_bash" && handle.watcher !== undefined)
953
+ clearInterval(handle.watcher);
974
954
  if (handle.type === "monitor" && handle.watcher !== undefined) {
975
955
  handle.timers.clearInterval(handle.watcher);
976
956
  handle.watcher = undefined;
@@ -1007,7 +987,6 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1007
987
  this.handles.delete(id);
1008
988
  if (handle.status === "running" && handle.owner !== undefined)
1009
989
  evictedRunningOwners.add(handle.owner);
1010
- this.legacyToTaskId.delete(String(handle.shellId));
1011
990
  if (handle.watcher !== undefined)
1012
991
  clearInterval(handle.watcher);
1013
992
  deleted++;
@@ -1017,10 +996,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1017
996
  return deleted;
1018
997
  }
1019
998
  resolveInternal(id, access) {
1020
- const taskId = this.handles.has(id) ? id : this.legacyToTaskId.get(id);
1021
- if (!taskId)
1022
- return undefined;
1023
- const handle = this.handles.get(taskId);
999
+ const handle = this.handles.get(id);
1024
1000
  if (!handle || !canAccess(handle, access))
1025
1001
  return undefined;
1026
1002
  return handle;
@@ -1049,17 +1025,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1049
1025
  const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile);
1050
1026
  const running = handle.status === "running";
1051
1027
  const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
1052
- const timedOutClause = spool.timedOut === true ? ` (timed out after ${spool.timeoutSec !== undefined ? `${spool.timeoutSec}s` : "its background time budget"})` : "";
1053
- const displayStatus = (spool.exitCode !== undefined ? `exited(code ${spool.exitCode})` : running ? "running" : handle.status) + timedOutClause;
1054
- const rolledNote = spool.rolledChars > 0 ? `[!] ${spool.rolledChars} earlier char(s) dropped from the spool (memory bound)\n` : "";
1055
- const droppedNote = (spool.droppedBytes ?? 0) > 0
1056
- ? `[!] ${spool.droppedBytes} earlier byte(s) permanently dropped before this window (8MB buffer evicted)\n`
1057
- : spool.dropUnknown === true
1058
- ? "[!] an unknown amount of earlier output was permanently dropped before this window (buffer evicted)\n"
1059
- : "";
1060
- const stdoutHeading = droppedNote !== "" ? "stdout (re-readable spool — INCOMPLETE, see note above)" : "stdout (full, re-readable)";
1061
- const body = `status: ${displayStatus}\n${rolledNote}${droppedNote}--- ${stdoutHeading} ---\n${shape(spool.stdout) || "(none)"}\n` +
1062
- `--- stderr ---\n${shape(spool.stderr) || "(none)"}`;
1028
+ const body = renderSpoolBody(spool, { running, handleStatus: handle.status, shape, rolledExtraClause: "", drainNote: "" });
1063
1029
  return {
1064
1030
  content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
1065
1031
  details: {
@@ -1089,7 +1055,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1089
1055
  };
1090
1056
  let stdout = "";
1091
1057
  let stderr = "";
1092
- let dropped = 0;
1058
+ const dropAcct = {};
1093
1059
  for (;;) {
1094
1060
  const r = await handle.env.pollBackground(handle.shellId);
1095
1061
  if (!r.ok) {
@@ -1107,7 +1073,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1107
1073
  }
1108
1074
  stdout = roll(stdout + r.value.stdout);
1109
1075
  stderr = roll(stderr + r.value.stderr);
1110
- dropped += r.value.bytesDroppedBeforeCursor ?? 0;
1076
+ accountDroppedBytes(dropAcct, r.value);
1111
1077
  handle.status = statusFromBackground(r.value.status, r.value.exitCode);
1112
1078
  if (handle.status === "killed" && handle.stoppedBy === undefined)
1113
1079
  handle.stoppedBy = handle.stopSource ?? (r.value.timedOut === true ? "timeout" : "system");
@@ -1120,7 +1086,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1120
1086
  const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile);
1121
1087
  const timedOutClause = r.value.timedOut === true ? ` (timed out after ${r.value.timeoutSec !== undefined ? `${r.value.timeoutSec}s` : "its background time budget"})` : "";
1122
1088
  const displayStatus = (r.value.status === "exited" ? `exited(code ${r.value.exitCode})` : r.value.status) + timedOutClause;
1123
- const droppedNote = (dropped > 0 ? `[!] ${dropped} earlier byte(s) permanently dropped before this window (8MB buffer evicted)\n` : "") +
1089
+ const droppedNote = spoolDropNote(dropAcct) +
1124
1090
  (rolledChars > 0 ? `[!] ${rolledChars} accumulated char(s) dropped from the middle during the blocked wait (memory bound)\n` : "");
1125
1091
  const body = `status: ${displayStatus}\n${droppedNote}--- new stdout ---\n${shape(stdout) || "(none)"}\n` +
1126
1092
  `--- new stderr ---\n${shape(stderr) || "(none)"}`;
@@ -1136,7 +1102,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1136
1102
  details: {
1137
1103
  status: r.value.status,
1138
1104
  exitCode: r.value.exitCode,
1139
- bytesDroppedBeforeCursor: dropped,
1105
+ bytesDroppedBeforeCursor: dropAcct.droppedBytes ?? 0,
1140
1106
  ...(r.value.timedOut === true ? { timedOut: true, ...(r.value.timeoutSec !== undefined ? { timeoutSec: r.value.timeoutSec } : {}) } : {}),
1141
1107
  },
1142
1108
  },
@@ -1205,6 +1171,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1205
1171
  handle.status = "killed";
1206
1172
  handle.updatedAt = Date.now();
1207
1173
  mintCompletionId(handle);
1174
+ await drainBashTailBestEffort(handle);
1208
1175
  this.notifyTerminalOnce(handle, () => explicitStopBashTerminalNote(handle, false));
1209
1176
  return {
1210
1177
  content: `Terminated ${handle.id}.`,
@@ -1233,14 +1200,13 @@ export function createTaskOutputTool(opts) {
1233
1200
  const caps = { ...REGISTRY_TASK_TOOL_CAPS, notification: opts.notificationWired !== false };
1234
1201
  return defineTool({
1235
1202
  name: TASK_OUTPUT_TOOL_NAME,
1236
- aliases: [...TASK_OUTPUT_ALIASES],
1237
1203
  contract: TASK_OUTPUT_CONTRACT,
1238
1204
  description: composeTaskOutputDescription(caps),
1239
1205
  parameters: composeTaskOutputParams(caps),
1240
1206
  effect: "read",
1241
1207
  execute: async (rawArgs, ctx) => {
1242
1208
  const args = rawArgs;
1243
- const id = resolveTaskIdArg(args.task_id, args.bash_id, args.runId);
1209
+ const id = resolveTaskIdArg(args.task_id);
1244
1210
  if (!id)
1245
1211
  return errorResult(TASK_OUTPUT_MISSING_ID_MESSAGE);
1246
1212
  const requestedMs = typeof args.timeout === "number" ? args.timeout : undefined;
@@ -1283,14 +1249,13 @@ export function createTaskOutputTool(opts) {
1283
1249
  export function createTaskStopTool(opts) {
1284
1250
  return defineTool({
1285
1251
  name: TASK_STOP_TOOL_NAME,
1286
- aliases: [...TASK_STOP_ALIASES],
1287
1252
  contract: TASK_STOP_CONTRACT,
1288
1253
  description: composeTaskStopDescription(REGISTRY_TASK_TOOL_CAPS),
1289
1254
  parameters: TASK_STOP_PARAMS,
1290
1255
  effect: "write",
1291
1256
  execute: async (rawArgs, ctx) => {
1292
1257
  const args = rawArgs;
1293
- const id = resolveTaskIdArg(args.task_id, args.shell_id, args.runId);
1258
+ const id = resolveTaskIdArg(args.task_id, args.shell_id);
1294
1259
  if (!id)
1295
1260
  return errorResult(TASK_STOP_MISSING_ID_MESSAGE);
1296
1261
  const r = await opts.registry.stopTask(id, { owner: ctx.taskId ?? opts.owner, scope: ctx.principal ?? opts.scope, ...((ctx.sessionId ?? opts.sessionId) !== undefined ? { sessionId: ctx.sessionId ?? opts.sessionId } : {}) }, { workflowStore: opts.workflowStore, agentStore: opts.agentStore });
@@ -1,8 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  export declare const TASK_OUTPUT_TOOL_NAME = "TaskOutput";
3
3
  export declare const TASK_STOP_TOOL_NAME = "TaskStop";
4
- export declare const TASK_OUTPUT_ALIASES: readonly ["BashOutput", "AgentOutputTool", "BashOutputTool", "AgentOutput", "WorkflowStatus"];
5
- export declare const TASK_STOP_ALIASES: readonly ["KillShell", "KillBash"];
6
4
  export declare const TASK_OUTPUT_CONTRACT: {
7
5
  readonly contractId: "core.task_output@1";
8
6
  readonly implementationRevision: "1";
@@ -1,8 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  export const TASK_OUTPUT_TOOL_NAME = "TaskOutput";
3
3
  export const TASK_STOP_TOOL_NAME = "TaskStop";
4
- export const TASK_OUTPUT_ALIASES = ["BashOutput", "AgentOutputTool", "BashOutputTool", "AgentOutput", "WorkflowStatus"];
5
- export const TASK_STOP_ALIASES = ["KillShell", "KillBash"];
6
4
  export const TASK_OUTPUT_CONTRACT = { contractId: "core.task_output@1", implementationRevision: "1" };
7
5
  export const TASK_STOP_CONTRACT = { contractId: "core.task_stop@1", implementationRevision: "1" };
8
6
  export const TASK_OUTPUT_MISSING_ID_MESSAGE = "Error (TaskOutput): Missing required parameter: task_id";
@@ -50,9 +48,8 @@ export function composeTaskOutputDescription(caps) {
50
48
  "poll captures any final output. ");
51
49
  }
52
50
  if (!caps.lanes)
53
- parts.push("Optional `filter` is a regex keeping only matching lines. ");
54
- parts.push("Legacy bash_id and runId arguments are accepted for compatibility.");
55
- return parts.join("");
51
+ parts.push("Optional `filter` is a regex keeping only matching lines.");
52
+ return parts.join("").trimEnd();
56
53
  }
57
54
  export function composeTaskStopDescription(caps) {
58
55
  return [
@@ -1,14 +1,15 @@
1
1
  import { Value } from "typebox/value";
2
2
  const MAX_ERROR_CHARS = 10_000;
3
3
  const HALF = MAX_ERROR_CHARS / 2;
4
+ import { isHighSurrogate, isLowSurrogate, sliceHeadSafe, sliceTailSafe } from "./surrogate-safe-slice.js";
4
5
  export function truncateError(s) {
5
6
  if (s.length <= MAX_ERROR_CHARS)
6
7
  return s;
7
- const removed = s.length - MAX_ERROR_CHARS;
8
- return `${s.slice(0, HALF)}\n\n... [${removed} characters truncated] ...\n\n${s.slice(s.length - HALF)}`;
8
+ const head = sliceHeadSafe(s, HALF);
9
+ const tail = sliceTailSafe(s, HALF);
10
+ const removed = s.length - head.length - tail.length;
11
+ return `${head}\n\n... [${removed} characters truncated] ...\n\n${tail}`;
9
12
  }
10
- const isHighSurrogate = (c) => c >= 0xd800 && c <= 0xdbff;
11
- const isLowSurrogate = (c) => c >= 0xdc00 && c <= 0xdfff;
12
13
  export function clipWithFilePointer(s, limit, fullOutputPath) {
13
14
  if (s.length <= limit)
14
15
  return s;
@@ -1,2 +1 @@
1
- export declare const TOMBSTONED_TOOLS: Readonly<Record<string, string>>;
2
- export declare function canonicalToolName(name: string): string;
1
+ export declare const RETIRED_TOOL_NAMES: ReadonlyMap<string, string>;
@@ -1,59 +1,42 @@
1
- const OLD_TO_NEW = {
2
- recall: "Recall",
3
- remember: "Remember",
4
- read_file: "Read",
5
- edit_file: "Edit",
6
- write_file: "Write",
7
- bash: "Bash",
8
- bash_readonly: "Bash",
9
- glob: "Glob",
10
- grep: "Grep",
11
- web_fetch: "WebFetch",
12
- tool_search: "ToolSearch",
13
- skill: "Skill",
14
- present_plan: "ExitPlanMode",
15
- enter_plan_mode: "EnterPlanMode",
16
- spawn_subagent: "Agent",
17
- CronCancel: "CronDelete",
18
- lsp: "LSP",
19
- submit_output: "StructuredOutput",
20
- repo_map: "RepoMap",
21
- read_tool_result: "ReadToolResult",
22
- report_blocked: "ReportBlocked",
23
- declare_done: "DeclareDone",
24
- run_workflow: "Workflow",
25
- RunWorkflow: "Workflow",
26
- Task: "Agent",
27
- BashOutput: "TaskOutput",
28
- AgentOutputTool: "TaskOutput",
29
- BashOutputTool: "TaskOutput",
30
- AgentOutput: "TaskOutput",
31
- KillShell: "TaskStop",
32
- KillBash: "TaskStop",
33
- ListMcpResources: "ListMcpResourcesTool",
34
- ReadMcpResource: "ReadMcpResourceTool",
35
- ReadMcpResourceDir: "ReadMcpResourceDirTool",
36
- MultiEdit: "Edit",
37
- Fork: "Agent",
38
- WorkflowStatus: "TaskOutput",
39
- };
40
- const MEMORY_TOOLS_TOMBSTONE = "The Remember/Recall memory tools were removed (design/138): long-term memory is now file-based. " +
41
- "Durable notes live in the memory directory shown in your # Memory instruction (MEMORY.md index + topic files) — " +
42
- "use your ordinary file tools (Read/Write/Edit) on those files to record or look up memories. " +
43
- "If no # Memory section is present, memory is not enabled for this task; do not retry this tool.";
44
- export const TOMBSTONED_TOOLS = {
45
- Sleep: 'Sleep was removed. To pause and resume later, schedule a self-contained re-wake with CronCreate({ schedule: { kind: "delay", delaySec: … }, prompt: "<what to do on wake>" }) and then end your turn. To wait for an external condition or a long-running command, use Monitor. Do not poll in a loop.',
46
- Fork: 'Fork was removed as a standalone tool. Use Agent with subagent_type: "fork" instead — it spawns the same context-inheriting child (pass your directive as \'prompt\'; run_in_background is supported).',
47
- Remember: MEMORY_TOOLS_TOMBSTONE,
48
- remember: MEMORY_TOOLS_TOMBSTONE,
49
- Recall: MEMORY_TOOLS_TOMBSTONE,
50
- recall: MEMORY_TOOLS_TOMBSTONE,
51
- };
52
- const MCP_PREFIX = "mcp__";
53
- const MCP_NAME_SEP = "__";
54
- export function canonicalToolName(name) {
55
- const renamed = OLD_TO_NEW[name] ?? name;
56
- if (renamed.includes(MCP_NAME_SEP) && !renamed.startsWith(MCP_PREFIX))
57
- return MCP_PREFIX + renamed;
58
- return renamed;
59
- }
1
+ export const RETIRED_TOOL_NAMES = new Map([
2
+ ["read_file", 'renamed "Read"'],
3
+ ["edit_file", 'renamed "Edit"'],
4
+ ["write_file", 'renamed "Write"'],
5
+ ["bash", 'renamed "Bash"'],
6
+ ["bash_readonly", 'folded into "Bash" (read-only posture rides handsReadOnly/shellGate, not a tool name)'],
7
+ ["glob", 'renamed "Glob"'],
8
+ ["grep", 'renamed "Grep"'],
9
+ ["web_fetch", 'renamed "WebFetch"'],
10
+ ["tool_search", 'renamed "ToolSearch"'],
11
+ ["skill", 'renamed "Skill"'],
12
+ ["present_plan", 'renamed "ExitPlanMode"'],
13
+ ["enter_plan_mode", 'renamed "EnterPlanMode"'],
14
+ ["spawn_subagent", 'renamed "Agent"'],
15
+ ["CronCancel", 'renamed "CronDelete"'],
16
+ ["lsp", 'renamed "LSP"'],
17
+ ["submit_output", 'renamed "StructuredOutput"'],
18
+ ["repo_map", 'renamed "RepoMap"'],
19
+ ["read_tool_result", 'renamed "ReadToolResult"'],
20
+ ["report_blocked", 'renamed "ReportBlocked"'],
21
+ ["declare_done", 'renamed "DeclareDone"'],
22
+ ["run_workflow", 'renamed "Workflow"'],
23
+ ["RunWorkflow", 'renamed "Workflow"'],
24
+ ["Task", 'renamed "Agent" (a deployment-defined tool named "Task" is fine — this fires only when the name matches nothing in the roster)'],
25
+ ["BashOutput", 'renamed "TaskOutput"'],
26
+ ["AgentOutputTool", 'renamed "TaskOutput"'],
27
+ ["BashOutputTool", 'renamed "TaskOutput"'],
28
+ ["AgentOutput", 'renamed "TaskOutput"'],
29
+ ["WorkflowStatus", 'folded into "TaskOutput" (poll by runId)'],
30
+ ["KillShell", 'renamed "TaskStop"'],
31
+ ["KillBash", 'renamed "TaskStop"'],
32
+ ["ListMcpResources", 'renamed "ListMcpResourcesTool"'],
33
+ ["ReadMcpResource", 'renamed "ReadMcpResourceTool"'],
34
+ ["ReadMcpResourceDir", 'renamed "ReadMcpResourceDirTool"'],
35
+ ["MultiEdit", 'removed — "Edit" accepts the edits[] shape'],
36
+ ["Fork", 'removed — use Agent with subagent_type: "fork"'],
37
+ ["Sleep", "removed — schedule a re-wake with CronCreate({schedule:{kind:\"delay\"}}) or wait with Monitor"],
38
+ ["Remember", "removed — memory is file-based; write the memory directory with the ordinary file tools"],
39
+ ["remember", "removed — memory is file-based; write the memory directory with the ordinary file tools"],
40
+ ["Recall", "removed memory is file-based; read the memory directory with the ordinary file tools"],
41
+ ["recall", "removed memory is file-based; read the memory directory with the ordinary file tools"],
42
+ ]);
@@ -2,13 +2,12 @@ import { homedir } from "node:os";
2
2
  import { isAbsolute, join, normalize as normalizePath, sep } from "node:path";
3
3
  import { BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName } from "../tools/fs/index.js";
4
4
  import { writeTargetPath } from "../tools/fs/safety.js";
5
- import { canonicalToolName } from "./tool-name-aliases.js";
6
5
  export function decisionText(d) {
7
6
  return d.message ?? d.reason;
8
7
  }
9
8
  const ALLOW = { action: "allow" };
10
9
  function withTimeout(p, ms, onTimeout) {
11
- if (!ms || ms <= 0) {
10
+ if (ms === undefined) {
12
11
  return p;
13
12
  }
14
13
  return new Promise((resolve, reject) => {
@@ -25,7 +24,6 @@ function withTimeout(p, ms, onTimeout) {
25
24
  return;
26
25
  settle(resolve, onTimeout());
27
26
  }, ms);
28
- timer.unref?.();
29
27
  p.then((v) => settle(resolve, v), (e) => settle(reject, e));
30
28
  });
31
29
  }
@@ -34,12 +32,12 @@ export function toolPolicyNameSets(p) {
34
32
  return Array.isArray(sets) ? sets : [];
35
33
  }
36
34
  export function createAllowDenyPolicy(opts) {
37
- const allow = opts.allow ? new Set(opts.allow.map(canonicalToolName)) : undefined;
38
- const deny = new Set((opts.deny ?? []).map(canonicalToolName));
35
+ const allow = opts.allow ? new Set(opts.allow) : undefined;
36
+ const deny = new Set(opts.deny ?? []);
39
37
  return {
40
38
  nameSets: [{ ...(opts.allow ? { allow: [...opts.allow] } : {}), ...(opts.deny ? { deny: [...opts.deny] } : {}) }],
41
39
  check(req) {
42
- const toolName = canonicalToolName(req.toolName);
40
+ const toolName = req.toolName;
43
41
  if (deny.has(toolName)) {
44
42
  return { action: "deny", reason: `tool "${req.toolName}" is denied by policy` };
45
43
  }
@@ -51,9 +49,12 @@ export function createAllowDenyPolicy(opts) {
51
49
  };
52
50
  }
53
51
  export function createApprovalPolicy(opts) {
54
- const need = new Set(opts.requireApproval.map(canonicalToolName));
55
- const deny = new Set((opts.deny ?? []).map(canonicalToolName));
56
- const auto = new Set((opts.autoAllow ?? []).map(canonicalToolName));
52
+ if (opts.approvalTimeoutMs !== undefined && !Number.isFinite(opts.approvalTimeoutMs)) {
53
+ throw new Error(`createApprovalPolicy: approvalTimeoutMs must be a finite number of ms (got ${opts.approvalTimeoutMs}) omit it to wait indefinitely`);
54
+ }
55
+ const need = new Set(opts.requireApproval);
56
+ const deny = new Set(opts.deny ?? []);
57
+ const auto = new Set(opts.autoAllow ?? []);
57
58
  return {
58
59
  nameSets: [
59
60
  {
@@ -63,7 +64,7 @@ export function createApprovalPolicy(opts) {
63
64
  },
64
65
  ],
65
66
  async check(req, signal) {
66
- const toolName = canonicalToolName(req.toolName);
67
+ const toolName = req.toolName;
67
68
  if (deny.has(toolName)) {
68
69
  return { action: "deny", reason: `tool "${req.toolName}" is denied by policy` };
69
70
  }
@@ -121,7 +122,7 @@ export function combinePolicies(...policies) {
121
122
  }
122
123
  export const COARSE_SHELL_TOOLS = ["Bash", "Monitor"];
123
124
  function canonicalToolNameSet(tools) {
124
- return new Set((tools ?? COARSE_SHELL_TOOLS).map(canonicalToolName));
125
+ return new Set(tools ?? COARSE_SHELL_TOOLS);
125
126
  }
126
127
  export function createCoarseCommandNamePolicy(opts) {
127
128
  const allow = opts.allow ? new Set(opts.allow) : undefined;
@@ -133,7 +134,7 @@ export function createCoarseCommandNamePolicy(opts) {
133
134
  : { action: "ask", message: reason, decisionReason: "rule" };
134
135
  return {
135
136
  check(req) {
136
- if (!shellTools.has(canonicalToolName(req.toolName)))
137
+ if (!shellTools.has(req.toolName))
137
138
  return ALLOW;
138
139
  const command = req.args?.command;
139
140
  if (typeof command !== "string") {
@@ -381,7 +382,7 @@ export function createUnverifiableDeletePolicy(opts) {
381
382
  const shellTools = canonicalToolNameSet(opts?.tools);
382
383
  return {
383
384
  check(req) {
384
- if (!shellTools.has(canonicalToolName(req.toolName)))
385
+ if (!shellTools.has(req.toolName))
385
386
  return ALLOW;
386
387
  const command = req.args?.command;
387
388
  if (typeof command !== "string")
@@ -403,6 +404,8 @@ export function createUnverifiableDeletePolicy(opts) {
403
404
  function pathSegments(p) {
404
405
  return p.split(/[\\/]/).filter(Boolean);
405
406
  }
407
+ const CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
408
+ const foldPathCase = (s) => (CASE_INSENSITIVE_FS ? s.toLowerCase() : s);
406
409
  function lexicalPath(p, home) {
407
410
  const norm = p.normalize("NFC");
408
411
  const expanded = norm === "~" ? home : /^~[\\/]/.test(norm) ? join(home, norm.slice(2)) : norm;
@@ -432,17 +435,18 @@ export function createTranscriptIntegrityPolicy(opts) {
432
435
  });
433
436
  const readAllow = new Set(opts?.readAllow ?? BASH_READONLY_DEFAULT_ALLOW);
434
437
  const shellTools = canonicalToolNameSet(opts?.tools);
435
- const dirSegs = dirs.map(pathSegments);
438
+ const dirSegs = dirs.map((d) => pathSegments(d).map(foldPathCase));
436
439
  const inProtectedDir = (p) => {
437
440
  const abs = lexicalPath(p, home);
438
- const segs = pathSegments(abs);
441
+ const segs = pathSegments(abs).map(foldPathCase);
439
442
  if (isAbsolute(abs)) {
440
443
  return dirSegs.some((d) => d.length <= segs.length && d.every((s, i) => s === segs[i]));
441
444
  }
442
445
  return dirSegs.some((d) => relativeContinuesDirTail(segs, d));
443
446
  };
444
447
  const referencesProtectedDir = (cmd) => {
445
- if (textualForms.some((f) => cmd.includes(f)))
448
+ const hay = foldPathCase(cmd);
449
+ if (textualForms.some((f) => hay.includes(foldPathCase(f))))
446
450
  return true;
447
451
  for (const raw of cmd.split(/[\s;&|<>()]+/)) {
448
452
  if (raw === "")
@@ -471,7 +475,7 @@ export function createTranscriptIntegrityPolicy(opts) {
471
475
  });
472
476
  return {
473
477
  check(req) {
474
- const toolName = canonicalToolName(req.toolName);
478
+ const toolName = req.toolName;
475
479
  if (shellTools.has(toolName)) {
476
480
  const command = req.args?.command;
477
481
  if (typeof command !== "string")
@@ -45,6 +45,7 @@ export declare class ScopedToolResultStore implements ToolResultStore {
45
45
  }
46
46
  export declare function isVolatileOffloadStore(store: ToolResultStore): boolean;
47
47
  export declare const OFFLOAD_TOOL_NAME = "ReadToolResult";
48
+ export declare function createOffloadPersist(store: ToolResultStore, sessionId: string): (toolCallId: string, fullText: string) => string;
48
49
  export declare function offloadPagebackHint(ref: string, form: "preview" | "cleared", reachableTools?: ReadonlySet<string>): string;
49
50
  export declare const PERSISTED_OUTPUT_PREFIX = "<persisted-output ref=";
50
51
  export declare const DEFAULT_TOOL_RESULT_THRESHOLD_CHARS = 20000;