@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.
- package/CHANGELOG.md +2382 -0
- package/dist/agents/cascade.d.ts +3 -1
- package/dist/agents/cascade.js +31 -32
- package/dist/agents/cumulative-stats.d.ts +29 -0
- package/dist/agents/cumulative-stats.js +29 -0
- package/dist/agents/observer.d.ts +4 -0
- package/dist/agents/observer.js +11 -10
- package/dist/agents/repair-loop.d.ts +2 -0
- package/dist/agents/repair-loop.js +48 -24
- package/dist/agents/retain-ledger.d.ts +2 -0
- package/dist/agents/retain-ledger.js +6 -5
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/roster-store.js +3 -5
- package/dist/agents/send-message-tool.d.ts +1 -0
- package/dist/agents/send-message-tool.js +2 -0
- package/dist/agents/subagent.d.ts +3 -1
- package/dist/agents/subagent.js +79 -94
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +21 -24
- package/dist/agents/tool-filter.js +6 -7
- package/dist/agents/verify.d.ts +1 -0
- package/dist/agents/verify.js +15 -14
- package/dist/brain/anthropic.js +24 -21
- package/dist/brain/openai.js +24 -21
- package/dist/brain/stream-engine.js +5 -2
- package/dist/brain/stream-shared.d.ts +1 -0
- package/dist/brain/stream-shared.js +14 -0
- package/dist/brain/terminal-cause.d.ts +4 -0
- package/dist/brain/terminal-cause.js +12 -0
- package/dist/brain/tool-call-id.d.ts +1 -0
- package/dist/brain/tool-call-id.js +3 -0
- package/dist/brain/tool-call-repair.js +2 -1
- package/dist/core/arg-summary.d.ts +21 -1
- package/dist/core/arg-summary.js +61 -14
- package/dist/core/auto-compaction.d.ts +1 -0
- package/dist/core/auto-compaction.js +16 -25
- package/dist/core/background-agent-store.d.ts +22 -22
- package/dist/core/background-agent-store.js +19 -23
- package/dist/core/checkpoint-store.d.ts +6 -13
- package/dist/core/checkpoint-store.js +11 -5
- package/dist/core/file-snapshot-store.d.ts +8 -0
- package/dist/core/file-snapshot-store.js +4 -3
- package/dist/core/fs-write-gate-policy.js +2 -3
- package/dist/core/hooks.d.ts +4 -1
- package/dist/core/hooks.js +8 -18
- package/dist/core/lsp.js +4 -3
- package/dist/core/mailbox-store.d.ts +7 -10
- package/dist/core/mcp.d.ts +7 -10
- package/dist/core/mcp.js +7 -9
- package/dist/core/memory-engine/engine.d.ts +0 -1
- package/dist/core/memory-engine/engine.js +24 -34
- package/dist/core/memory-engine/file-backend.d.ts +1 -2
- package/dist/core/memory-engine/file-backend.js +25 -36
- package/dist/core/memory-engine/layout.d.ts +8 -0
- package/dist/core/memory-engine/layout.js +60 -4
- package/dist/core/memory-engine/scope-contract.js +9 -3
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/permission-rules.js +2 -3
- package/dist/core/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +2 -17
- package/dist/core/runner/assemble-result.d.ts +2 -7
- package/dist/core/runner/assemble-result.js +3 -4
- package/dist/core/runner/compaction-call-options.d.ts +7 -0
- package/dist/core/runner/compaction-call-options.js +30 -0
- package/dist/core/runner/prepare-task.d.ts +6 -23
- package/dist/core/runner/prepare-task.js +36 -61
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +84 -172
- package/dist/core/runner/session-rule-policy.js +7 -5
- package/dist/core/runner/teardown-bounded.d.ts +7 -0
- package/dist/core/runner/teardown-bounded.js +36 -0
- package/dist/core/runner/tool-disclosure.d.ts +2 -5
- package/dist/core/runner/turn-attachments.d.ts +5 -15
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/safe-notify.d.ts +17 -0
- package/dist/core/safe-notify.js +59 -0
- package/dist/core/sensitive-path-policy.js +4 -4
- package/dist/core/session-reconcile.js +1 -2
- package/dist/core/skill-tool-specifier.js +2 -3
- package/dist/core/skills-directory.js +2 -3
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/stub-env.d.ts +2 -6
- package/dist/core/surrogate-safe-slice.d.ts +4 -0
- package/dist/core/surrogate-safe-slice.js +18 -0
- package/dist/core/task-registry-monitor.js +8 -13
- package/dist/core/task-registry-shared.d.ts +23 -1
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +6 -1
- package/dist/core/task-registry.js +52 -87
- package/dist/core/task-tool-shape.d.ts +0 -2
- package/dist/core/task-tool-shape.js +2 -5
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.d.ts +1 -2
- package/dist/core/tool-name-aliases.js +42 -59
- package/dist/core/tool-policy.js +21 -17
- package/dist/core/tool-result-store.d.ts +1 -0
- package/dist/core/tool-result-store.js +17 -1
- package/dist/core/trace.d.ts +9 -9
- package/dist/core/types.d.ts +15 -12
- package/dist/core/untrusted-egress.d.ts +4 -2
- package/dist/core/untrusted-egress.js +28 -14
- package/dist/core/workflow-journal-store.d.ts +9 -20
- package/dist/core/workflow-run-store.d.ts +11 -10
- package/dist/core/workflow-run-store.js +22 -16
- package/dist/engine/compaction/utils.js +4 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
- package/dist/engine/execution-env/node-execution-env.js +1 -1
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.js +13 -33
- package/dist/engine/harness/types.d.ts +29 -67
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +12 -14
- package/dist/engine/loop/types.d.ts +11 -9
- package/dist/engine/session/memory-repo.d.ts +2 -6
- package/dist/engine/session/session.d.ts +5 -30
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -3
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/orchestration/run-spec.js +6 -4
- package/dist/orchestration/run-workflow-tool.js +3 -36
- package/dist/orchestration/workflow-governance.js +2 -1
- package/dist/orchestration/workflow.d.ts +4 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/packs/sema-default.js +1 -3
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/prompts/default.js +1 -3
- package/dist/prompts/simple-sections.d.ts +0 -1
- package/dist/prompts/simple-sections.js +0 -1
- package/dist/stores/file/background-agent-store.d.ts +3 -12
- package/dist/stores/file/background-agent-store.js +3 -41
- package/dist/stores/file/checkpoint-store.js +6 -6
- package/dist/stores/file/mailbox-store.d.ts +2 -6
- package/dist/stores/file/session-policy-store.js +15 -1
- package/dist/stores/file/session-store.d.ts +2 -6
- package/dist/stores/file/workflow-journal-store.d.ts +3 -11
- package/dist/stores/file/workflow-run-store.d.ts +3 -7
- package/dist/stores/file/workflow-run-store.js +4 -17
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -0
- package/dist/tools/fs/bash-readonly-classifier.js +60 -25
- package/dist/tools/fs/fs-bash.js +330 -104
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- package/dist/tools/fs/fs-write.js +0 -1
- package/dist/tools/fs/notebook.d.ts +2 -5
- package/dist/tools/fs/safety.d.ts +3 -0
- package/dist/tools/fs/safety.js +43 -14
- package/dist/tools/fs/search.js +5 -5
- package/dist/tools/monitor.d.ts +1 -0
- package/dist/tools/monitor.js +7 -5
- package/dist/tools/worktree.d.ts +2 -7
- package/dist/tools/worktree.js +3 -1
- 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,
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 = (
|
|
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:
|
|
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
|
|
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
|
|
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.
|
|
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 [
|
package/dist/core/tool-errors.js
CHANGED
|
@@ -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
|
|
8
|
-
|
|
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
|
|
2
|
-
export declare function canonicalToolName(name: string): string;
|
|
1
|
+
export declare const RETIRED_TOOL_NAMES: ReadonlyMap<string, string>;
|
|
@@ -1,59 +1,42 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
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
|
+
]);
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -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 (
|
|
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
|
|
38
|
-
const deny = new Set(
|
|
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 =
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
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;
|