@sema-agent/core 3.0.0 → 4.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 +2345 -0
- package/dist/agents/cascade.d.ts +2 -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.js +27 -19
- package/dist/agents/retain-ledger.d.ts +2 -0
- package/dist/agents/retain-ledger.js +6 -5
- package/dist/agents/roster-store.js +3 -5
- package/dist/agents/subagent.js +66 -64
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +21 -24
- 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/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/hooks.d.ts +3 -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 -3
- 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/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +1 -15
- 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 +20 -53
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +65 -156
- package/dist/core/runner/session-rule-policy.js +4 -1
- 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 +2 -1
- 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 +22 -0
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +40 -57
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.js +2 -1
- package/dist/core/tool-policy.js +10 -5
- 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 +2 -9
- package/dist/core/types.d.ts +15 -12
- package/dist/core/untrusted-egress.js +7 -5
- 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/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 +11 -4
- 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 +3 -1
- package/dist/index.js +2 -1
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/orchestration/run-spec.js +7 -4
- package/dist/orchestration/run-workflow-tool.js +3 -35
- package/dist/orchestration/workflow.d.ts +3 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- 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 +324 -96
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- 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
package/dist/core/stub-env.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ExecutionEnv, ExecutionError, FileError, type FileInfo, type Result } from "../internal/harness.js";
|
|
1
|
+
import { type ExecResult, type ExecutionEnv, ExecutionError, FileError, type FileInfo, type Result } from "../internal/harness.js";
|
|
2
2
|
export declare class StubExecutionEnv implements ExecutionEnv {
|
|
3
3
|
cwd: string;
|
|
4
4
|
constructor(cwd?: string);
|
|
@@ -18,10 +18,6 @@ export declare class StubExecutionEnv implements ExecutionEnv {
|
|
|
18
18
|
remove(path: string): Promise<Result<void, FileError>>;
|
|
19
19
|
createTempDir(): Promise<Result<string, FileError>>;
|
|
20
20
|
createTempFile(): Promise<Result<string, FileError>>;
|
|
21
|
-
exec(): Promise<Result<
|
|
22
|
-
stdout: string;
|
|
23
|
-
stderr: string;
|
|
24
|
-
exitCode: number;
|
|
25
|
-
}, ExecutionError>>;
|
|
21
|
+
exec(): Promise<Result<ExecResult, ExecutionError>>;
|
|
26
22
|
cleanup(): Promise<void>;
|
|
27
23
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const isHighSurrogate = (c) => c >= 0xd800 && c <= 0xdbff;
|
|
2
|
+
export const isLowSurrogate = (c) => c >= 0xdc00 && c <= 0xdfff;
|
|
3
|
+
export function sliceHeadSafe(s, keep) {
|
|
4
|
+
if (keep <= 0)
|
|
5
|
+
return "";
|
|
6
|
+
if (s.length <= keep)
|
|
7
|
+
return s;
|
|
8
|
+
const head = s.slice(0, keep);
|
|
9
|
+
return keep > 0 && isHighSurrogate(head.charCodeAt(keep - 1)) ? head.slice(0, -1) : head;
|
|
10
|
+
}
|
|
11
|
+
export function sliceTailSafe(s, keep) {
|
|
12
|
+
if (keep <= 0)
|
|
13
|
+
return "";
|
|
14
|
+
if (s.length <= keep)
|
|
15
|
+
return s;
|
|
16
|
+
const tail = s.slice(-keep);
|
|
17
|
+
return keep > 0 && isLowSurrogate(tail.charCodeAt(0)) ? tail.slice(1) : tail;
|
|
18
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
2
|
-
import { assertOwnership, defaultMonitorTimers, MONITOR_MAX_TIMEOUT_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_BATCH_WINDOW_MS, MONITOR_MAX_BATCHES_PER_MINUTE, MONITOR_STORM_BURST, MONITOR_STORM_KILL_AFTER_MS, MONITOR_LINE_BUF_CAP, MONITOR_SPILL_CAP_CHARS, TASK_OUTPUT_MAX_CHARS, mintCompletionId, clipMonitorEvent, clipMonitorLine, terminalTaskSummary, accountDroppedBytes, rollSpoolText, statusFromBackground, droppedGapNote, firstDropNote, alreadyTerminalStopNote, clipTaskOutput, sleepPollStep, } from "./task-registry-shared.js";
|
|
2
|
+
import { assertOwnership, defaultMonitorTimers, MONITOR_MAX_TIMEOUT_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_BATCH_WINDOW_MS, MONITOR_MAX_BATCHES_PER_MINUTE, MONITOR_STORM_BURST, MONITOR_STORM_KILL_AFTER_MS, MONITOR_LINE_BUF_CAP, MONITOR_SPILL_CAP_CHARS, TASK_OUTPUT_MAX_CHARS, mintCompletionId, clipMonitorEvent, clipMonitorLine, terminalTaskSummary, accountDroppedBytes, renderSpoolBody, rollSpoolText, statusFromBackground, droppedGapNote, firstDropNote, alreadyTerminalStopNote, clipTaskOutput, sleepPollStep, } from "./task-registry-shared.js";
|
|
3
3
|
import { buildToolResultRef, OFFLOAD_TOOL_NAME } from "./tool-result-store.js";
|
|
4
4
|
export function registerMonitorLane(core, input) {
|
|
5
5
|
assertOwnership(input, "registerMonitor");
|
|
@@ -318,18 +318,13 @@ export async function pollMonitorLane(handle, filter, deadline, signal) {
|
|
|
318
318
|
const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile);
|
|
319
319
|
const running = handle.status === "running";
|
|
320
320
|
const retrieval = running && deadline !== undefined && Date.now() >= deadline ? "timeout" : "success";
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
: spool.
|
|
327
|
-
|
|
328
|
-
: "";
|
|
329
|
-
const drainNote = spool.drainFailed === true ? "[!] the final output drain at stop failed — the tail of the output may be missing\n" : "";
|
|
330
|
-
const stdoutHeading = droppedNote !== "" || drainNote !== "" ? "stdout (re-readable spool — INCOMPLETE, see note above)" : "stdout (full, re-readable)";
|
|
331
|
-
const body = `status: ${displayStatus}\n${rolledNote}${droppedNote}${drainNote}--- ${stdoutHeading} ---\n${shape(spool.stdout) || "(none)"}\n` +
|
|
332
|
-
`--- stderr ---\n${shape(spool.stderr) || "(none)"}`;
|
|
321
|
+
const body = renderSpoolBody(spool, {
|
|
322
|
+
running,
|
|
323
|
+
handleStatus: handle.status,
|
|
324
|
+
shape,
|
|
325
|
+
rolledExtraClause: monitorSpillNote(handle),
|
|
326
|
+
drainNote: spool.drainFailed === true ? "[!] the final output drain at stop failed — the tail of the output may be missing\n" : "",
|
|
327
|
+
});
|
|
333
328
|
return {
|
|
334
329
|
content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
|
|
335
330
|
details: {
|
|
@@ -252,6 +252,28 @@ export declare function accountDroppedBytes(spool: {
|
|
|
252
252
|
truncated?: boolean;
|
|
253
253
|
bytesDroppedBeforeCursor?: number;
|
|
254
254
|
}): void;
|
|
255
|
+
export declare function spoolDropNote(acct: {
|
|
256
|
+
droppedBytes?: number;
|
|
257
|
+
dropUnknown?: true;
|
|
258
|
+
}): string;
|
|
259
|
+
export interface RenderableSpool {
|
|
260
|
+
stdout: string;
|
|
261
|
+
stderr: string;
|
|
262
|
+
rolledChars: number;
|
|
263
|
+
exitCode?: number;
|
|
264
|
+
timedOut?: boolean;
|
|
265
|
+
timeoutSec?: number;
|
|
266
|
+
droppedBytes?: number;
|
|
267
|
+
dropUnknown?: true;
|
|
268
|
+
}
|
|
269
|
+
export interface SpoolBodyOptions {
|
|
270
|
+
running: boolean;
|
|
271
|
+
handleStatus: SemaTaskStatus;
|
|
272
|
+
shape: (text: string) => string;
|
|
273
|
+
rolledExtraClause: string;
|
|
274
|
+
drainNote: string;
|
|
275
|
+
}
|
|
276
|
+
export declare function renderSpoolBody(spool: RenderableSpool, opts: SpoolBodyOptions): string;
|
|
255
277
|
export declare function droppedGapNote(spool: {
|
|
256
278
|
droppedBytes?: number;
|
|
257
279
|
dropUnknown?: true;
|
|
@@ -102,6 +102,22 @@ export function accountDroppedBytes(spool, poll) {
|
|
|
102
102
|
else if (poll.truncated === true)
|
|
103
103
|
spool.dropUnknown = true;
|
|
104
104
|
}
|
|
105
|
+
export function spoolDropNote(acct) {
|
|
106
|
+
if ((acct.droppedBytes ?? 0) > 0)
|
|
107
|
+
return `[!] ${acct.droppedBytes} earlier byte(s) permanently dropped before this window (8MB buffer evicted)\n`;
|
|
108
|
+
if (acct.dropUnknown === true)
|
|
109
|
+
return "[!] an unknown amount of earlier output was permanently dropped before this window (buffer evicted)\n";
|
|
110
|
+
return "";
|
|
111
|
+
}
|
|
112
|
+
export function renderSpoolBody(spool, opts) {
|
|
113
|
+
const timedOutClause = spool.timedOut === true ? ` (timed out after ${spool.timeoutSec !== undefined ? `${spool.timeoutSec}s` : "its background time budget"})` : "";
|
|
114
|
+
const displayStatus = (spool.exitCode !== undefined ? `exited(code ${spool.exitCode})` : opts.running ? "running" : opts.handleStatus) + timedOutClause;
|
|
115
|
+
const rolledNote = spool.rolledChars > 0 ? `[!] ${spool.rolledChars} earlier char(s) dropped from the spool (memory bound)${opts.rolledExtraClause}\n` : "";
|
|
116
|
+
const droppedNote = spoolDropNote(spool);
|
|
117
|
+
const stdoutHeading = droppedNote !== "" || opts.drainNote !== "" ? "stdout (re-readable spool — INCOMPLETE, see note above)" : "stdout (full, re-readable)";
|
|
118
|
+
return (`status: ${displayStatus}\n${rolledNote}${droppedNote}${opts.drainNote}--- ${stdoutHeading} ---\n${opts.shape(spool.stdout) || "(none)"}\n` +
|
|
119
|
+
`--- stderr ---\n${opts.shape(spool.stderr) || "(none)"}`);
|
|
120
|
+
}
|
|
105
121
|
export function droppedGapNote(spool) {
|
|
106
122
|
if ((spool.droppedBytes ?? 0) > 0)
|
|
107
123
|
return ` [!] ${spool.droppedBytes} earlier byte(s) of output were permanently dropped before this window (execution-environment tail buffer evicted) — the event stream and this spool are INCOMPLETE.`;
|
|
@@ -69,6 +69,7 @@ export declare class TaskRegistry {
|
|
|
69
69
|
private sessionReapHooks;
|
|
70
70
|
private bgQuiescenceWatchers;
|
|
71
71
|
private legacyToTaskId;
|
|
72
|
+
private readonly notifier;
|
|
72
73
|
private readonly core;
|
|
73
74
|
durableAgentArmed(id: string): boolean;
|
|
74
75
|
durableAgentRowProbe(id: string): (() => Promise<boolean>) | undefined;
|
|
@@ -4,9 +4,10 @@ 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
12
|
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
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";
|
|
@@ -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."
|
|
@@ -77,6 +103,7 @@ export class TaskRegistry {
|
|
|
77
103
|
sessionReapHooks = new Set();
|
|
78
104
|
bgQuiescenceWatchers = new Map();
|
|
79
105
|
legacyToTaskId = new Map();
|
|
106
|
+
notifier = createSafeNotifier();
|
|
80
107
|
core = (() => {
|
|
81
108
|
const self = this;
|
|
82
109
|
return {
|
|
@@ -407,22 +434,7 @@ export class TaskRegistry {
|
|
|
407
434
|
handle.stoppedBy = handle.stopSource ?? source;
|
|
408
435
|
handle.updatedAt = Date.now();
|
|
409
436
|
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
|
-
}
|
|
437
|
+
await drainBashTailBestEffort(handle);
|
|
426
438
|
const resultText = handle.spool !== undefined ? bashTerminalResult(handle.spool) : "";
|
|
427
439
|
this.notifyTerminalOnce(handle, () => handle.onTerminal?.({
|
|
428
440
|
task_id: handle.id,
|
|
@@ -521,21 +533,13 @@ export class TaskRegistry {
|
|
|
521
533
|
this.settleBackgroundAgent(handle.id, { status: "killed", error: "session released", cycle: handle.reviveCycle ?? 0 });
|
|
522
534
|
if (reapTerminalNote !== undefined && handle.terminalNotified !== true) {
|
|
523
535
|
handle.terminalNotified = true;
|
|
524
|
-
|
|
525
|
-
reapTerminalNote();
|
|
526
|
-
}
|
|
527
|
-
catch {
|
|
528
|
-
}
|
|
536
|
+
this.notifier.notify(() => reapTerminalNote(), "taskRegistry.reapTerminalNote");
|
|
529
537
|
}
|
|
530
538
|
reaped++;
|
|
531
539
|
}
|
|
532
540
|
this.pokeBgQuiescence(sessionId);
|
|
533
541
|
for (const hook of this.sessionReapHooks) {
|
|
534
|
-
|
|
535
|
-
hook(sessionId, scope);
|
|
536
|
-
}
|
|
537
|
-
catch {
|
|
538
|
-
}
|
|
542
|
+
this.notifier.notify(() => hook(sessionId, scope), "taskRegistry.onSessionReap");
|
|
539
543
|
}
|
|
540
544
|
return reaped;
|
|
541
545
|
}
|
|
@@ -585,11 +589,7 @@ export class TaskRegistry {
|
|
|
585
589
|
this.bgQuiescenceWatchers.delete(oldest);
|
|
586
590
|
queueMicrotask(() => {
|
|
587
591
|
for (const victim of victims) {
|
|
588
|
-
|
|
589
|
-
victim("evicted");
|
|
590
|
-
}
|
|
591
|
-
catch {
|
|
592
|
-
}
|
|
592
|
+
this.notifier.notify(() => victim("evicted"), "taskRegistry.bgQuiescence.evicted");
|
|
593
593
|
}
|
|
594
594
|
});
|
|
595
595
|
}
|
|
@@ -617,22 +617,14 @@ export class TaskRegistry {
|
|
|
617
617
|
return;
|
|
618
618
|
this.bgQuiescenceWatchers.delete(owner);
|
|
619
619
|
for (const cb of set) {
|
|
620
|
-
|
|
621
|
-
cb("quiescent");
|
|
622
|
-
}
|
|
623
|
-
catch {
|
|
624
|
-
}
|
|
620
|
+
this.notifier.notify(() => cb("quiescent"), "taskRegistry.bgQuiescence.quiescent");
|
|
625
621
|
}
|
|
626
622
|
}
|
|
627
623
|
notifyTerminalOnce(handle, send) {
|
|
628
624
|
if (handle.terminalNotified === true)
|
|
629
625
|
return;
|
|
630
626
|
handle.terminalNotified = true;
|
|
631
|
-
|
|
632
|
-
send();
|
|
633
|
-
}
|
|
634
|
-
catch {
|
|
635
|
-
}
|
|
627
|
+
this.notifier.notify(() => send(), "taskRegistry.notifyTerminalOnce");
|
|
636
628
|
this.pokeBgQuiescence(handle.owner);
|
|
637
629
|
}
|
|
638
630
|
startBashWatcher(id, onTerminal) {
|
|
@@ -1049,17 +1041,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
1049
1041
|
const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile);
|
|
1050
1042
|
const running = handle.status === "running";
|
|
1051
1043
|
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)"}`;
|
|
1044
|
+
const body = renderSpoolBody(spool, { running, handleStatus: handle.status, shape, rolledExtraClause: "", drainNote: "" });
|
|
1063
1045
|
return {
|
|
1064
1046
|
content: delimitUntrusted(`TaskOutput ${handle.id}`, body),
|
|
1065
1047
|
details: {
|
|
@@ -1089,7 +1071,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
1089
1071
|
};
|
|
1090
1072
|
let stdout = "";
|
|
1091
1073
|
let stderr = "";
|
|
1092
|
-
|
|
1074
|
+
const dropAcct = {};
|
|
1093
1075
|
for (;;) {
|
|
1094
1076
|
const r = await handle.env.pollBackground(handle.shellId);
|
|
1095
1077
|
if (!r.ok) {
|
|
@@ -1107,7 +1089,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
1107
1089
|
}
|
|
1108
1090
|
stdout = roll(stdout + r.value.stdout);
|
|
1109
1091
|
stderr = roll(stderr + r.value.stderr);
|
|
1110
|
-
|
|
1092
|
+
accountDroppedBytes(dropAcct, r.value);
|
|
1111
1093
|
handle.status = statusFromBackground(r.value.status, r.value.exitCode);
|
|
1112
1094
|
if (handle.status === "killed" && handle.stoppedBy === undefined)
|
|
1113
1095
|
handle.stoppedBy = handle.stopSource ?? (r.value.timedOut === true ? "timeout" : "system");
|
|
@@ -1120,7 +1102,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
1120
1102
|
const shape = (text) => clipTaskOutput(rx ? text.split(/\r?\n/).filter((l) => rx.test(l)).join("\n") : text, handle.outputFile);
|
|
1121
1103
|
const timedOutClause = r.value.timedOut === true ? ` (timed out after ${r.value.timeoutSec !== undefined ? `${r.value.timeoutSec}s` : "its background time budget"})` : "";
|
|
1122
1104
|
const displayStatus = (r.value.status === "exited" ? `exited(code ${r.value.exitCode})` : r.value.status) + timedOutClause;
|
|
1123
|
-
const droppedNote = (
|
|
1105
|
+
const droppedNote = spoolDropNote(dropAcct) +
|
|
1124
1106
|
(rolledChars > 0 ? `[!] ${rolledChars} accumulated char(s) dropped from the middle during the blocked wait (memory bound)\n` : "");
|
|
1125
1107
|
const body = `status: ${displayStatus}\n${droppedNote}--- new stdout ---\n${shape(stdout) || "(none)"}\n` +
|
|
1126
1108
|
`--- new stderr ---\n${shape(stderr) || "(none)"}`;
|
|
@@ -1136,7 +1118,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
1136
1118
|
details: {
|
|
1137
1119
|
status: r.value.status,
|
|
1138
1120
|
exitCode: r.value.exitCode,
|
|
1139
|
-
bytesDroppedBeforeCursor:
|
|
1121
|
+
bytesDroppedBeforeCursor: dropAcct.droppedBytes ?? 0,
|
|
1140
1122
|
...(r.value.timedOut === true ? { timedOut: true, ...(r.value.timeoutSec !== undefined ? { timeoutSec: r.value.timeoutSec } : {}) } : {}),
|
|
1141
1123
|
},
|
|
1142
1124
|
},
|
|
@@ -1205,6 +1187,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
|
|
|
1205
1187
|
handle.status = "killed";
|
|
1206
1188
|
handle.updatedAt = Date.now();
|
|
1207
1189
|
mintCompletionId(handle);
|
|
1190
|
+
await drainBashTailBestEffort(handle);
|
|
1208
1191
|
this.notifyTerminalOnce(handle, () => explicitStopBashTerminalNote(handle, false));
|
|
1209
1192
|
return {
|
|
1210
1193
|
content: `Terminated ${handle.id}.`,
|
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;
|
|
@@ -52,7 +52,8 @@ export const TOMBSTONED_TOOLS = {
|
|
|
52
52
|
const MCP_PREFIX = "mcp__";
|
|
53
53
|
const MCP_NAME_SEP = "__";
|
|
54
54
|
export function canonicalToolName(name) {
|
|
55
|
-
const
|
|
55
|
+
const mapped = Object.hasOwn(OLD_TO_NEW, name) ? OLD_TO_NEW[name] : undefined;
|
|
56
|
+
const renamed = typeof mapped === "string" ? mapped : name;
|
|
56
57
|
if (renamed.includes(MCP_NAME_SEP) && !renamed.startsWith(MCP_PREFIX))
|
|
57
58
|
return MCP_PREFIX + renamed;
|
|
58
59
|
return renamed;
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -8,7 +8,7 @@ export function decisionText(d) {
|
|
|
8
8
|
}
|
|
9
9
|
const ALLOW = { action: "allow" };
|
|
10
10
|
function withTimeout(p, ms, onTimeout) {
|
|
11
|
-
if (
|
|
11
|
+
if (ms === undefined) {
|
|
12
12
|
return p;
|
|
13
13
|
}
|
|
14
14
|
return new Promise((resolve, reject) => {
|
|
@@ -25,7 +25,6 @@ function withTimeout(p, ms, onTimeout) {
|
|
|
25
25
|
return;
|
|
26
26
|
settle(resolve, onTimeout());
|
|
27
27
|
}, ms);
|
|
28
|
-
timer.unref?.();
|
|
29
28
|
p.then((v) => settle(resolve, v), (e) => settle(reject, e));
|
|
30
29
|
});
|
|
31
30
|
}
|
|
@@ -51,6 +50,9 @@ export function createAllowDenyPolicy(opts) {
|
|
|
51
50
|
};
|
|
52
51
|
}
|
|
53
52
|
export function createApprovalPolicy(opts) {
|
|
53
|
+
if (opts.approvalTimeoutMs !== undefined && !Number.isFinite(opts.approvalTimeoutMs)) {
|
|
54
|
+
throw new Error(`createApprovalPolicy: approvalTimeoutMs must be a finite number of ms (got ${opts.approvalTimeoutMs}) — omit it to wait indefinitely`);
|
|
55
|
+
}
|
|
54
56
|
const need = new Set(opts.requireApproval.map(canonicalToolName));
|
|
55
57
|
const deny = new Set((opts.deny ?? []).map(canonicalToolName));
|
|
56
58
|
const auto = new Set((opts.autoAllow ?? []).map(canonicalToolName));
|
|
@@ -403,6 +405,8 @@ export function createUnverifiableDeletePolicy(opts) {
|
|
|
403
405
|
function pathSegments(p) {
|
|
404
406
|
return p.split(/[\\/]/).filter(Boolean);
|
|
405
407
|
}
|
|
408
|
+
const CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
|
|
409
|
+
const foldPathCase = (s) => (CASE_INSENSITIVE_FS ? s.toLowerCase() : s);
|
|
406
410
|
function lexicalPath(p, home) {
|
|
407
411
|
const norm = p.normalize("NFC");
|
|
408
412
|
const expanded = norm === "~" ? home : /^~[\\/]/.test(norm) ? join(home, norm.slice(2)) : norm;
|
|
@@ -432,17 +436,18 @@ export function createTranscriptIntegrityPolicy(opts) {
|
|
|
432
436
|
});
|
|
433
437
|
const readAllow = new Set(opts?.readAllow ?? BASH_READONLY_DEFAULT_ALLOW);
|
|
434
438
|
const shellTools = canonicalToolNameSet(opts?.tools);
|
|
435
|
-
const dirSegs = dirs.map(pathSegments);
|
|
439
|
+
const dirSegs = dirs.map((d) => pathSegments(d).map(foldPathCase));
|
|
436
440
|
const inProtectedDir = (p) => {
|
|
437
441
|
const abs = lexicalPath(p, home);
|
|
438
|
-
const segs = pathSegments(abs);
|
|
442
|
+
const segs = pathSegments(abs).map(foldPathCase);
|
|
439
443
|
if (isAbsolute(abs)) {
|
|
440
444
|
return dirSegs.some((d) => d.length <= segs.length && d.every((s, i) => s === segs[i]));
|
|
441
445
|
}
|
|
442
446
|
return dirSegs.some((d) => relativeContinuesDirTail(segs, d));
|
|
443
447
|
};
|
|
444
448
|
const referencesProtectedDir = (cmd) => {
|
|
445
|
-
|
|
449
|
+
const hay = foldPathCase(cmd);
|
|
450
|
+
if (textualForms.some((f) => hay.includes(foldPathCase(f))))
|
|
446
451
|
return true;
|
|
447
452
|
for (const raw of cmd.split(/[\s;&|<>()]+/)) {
|
|
448
453
|
if (raw === "")
|
|
@@ -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;
|
|
@@ -29,6 +29,10 @@ export class InMemoryToolResultStore {
|
|
|
29
29
|
totalChars = 0;
|
|
30
30
|
constructor(opts) {
|
|
31
31
|
this.opts = opts;
|
|
32
|
+
const cap = opts?.maxTotalChars;
|
|
33
|
+
if (cap !== undefined && Number.isNaN(cap)) {
|
|
34
|
+
throw new Error("InMemoryToolResultStore: maxTotalChars must be a number (got NaN) — omit it for an unbounded store");
|
|
35
|
+
}
|
|
32
36
|
}
|
|
33
37
|
put(ref, content) {
|
|
34
38
|
assertSafeToolResultRef(ref);
|
|
@@ -37,7 +41,7 @@ export class InMemoryToolResultStore {
|
|
|
37
41
|
this.map.set(ref, content);
|
|
38
42
|
this.totalChars += content.length;
|
|
39
43
|
const cap = this.opts?.maxTotalChars;
|
|
40
|
-
if (cap !== undefined && cap
|
|
44
|
+
if (cap !== undefined && Number.isFinite(cap)) {
|
|
41
45
|
for (const [oldRef, oldContent] of this.map) {
|
|
42
46
|
if (this.totalChars <= cap || oldRef === ref)
|
|
43
47
|
break;
|
|
@@ -93,6 +97,16 @@ export function isVolatileOffloadStore(store) {
|
|
|
93
97
|
return store instanceof InMemoryToolResultStore;
|
|
94
98
|
}
|
|
95
99
|
export const OFFLOAD_TOOL_NAME = "ReadToolResult";
|
|
100
|
+
export function createOffloadPersist(store, sessionId) {
|
|
101
|
+
return (toolCallId, fullText) => {
|
|
102
|
+
const ref = buildToolResultRef(sessionId, toolCallId);
|
|
103
|
+
void Promise.resolve(store.put(ref, fullText)).catch((err) => {
|
|
104
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
105
|
+
void Promise.resolve(store.put(ref, `[offload LOST at write time: ${cause} — the inline preview is all that survived]`)).catch(() => undefined);
|
|
106
|
+
});
|
|
107
|
+
return ref;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
96
110
|
export function offloadPagebackHint(ref, form, reachableTools) {
|
|
97
111
|
const activate = reachableTools !== undefined && !reachableTools.has(OFFLOAD_TOOL_NAME)
|
|
98
112
|
? `${OFFLOAD_TOOL_NAME} is not active yet — call ${TOOL_SEARCH_NAME} with {"query":"select:${OFFLOAD_TOOL_NAME}"} to activate it, then `
|
|
@@ -144,6 +158,8 @@ export function buildPreview(full, ref, sizes, reachableTools) {
|
|
|
144
158
|
`</persisted-output>`);
|
|
145
159
|
}
|
|
146
160
|
export function withToolResultOffload(tool, store, thresholdChars, sessionId, reachableTools) {
|
|
161
|
+
if (!(Number.isFinite(thresholdChars) && thresholdChars > 0))
|
|
162
|
+
return tool;
|
|
147
163
|
const wrappedExecute = async (toolCallId, params, signal, onUpdate) => {
|
|
148
164
|
const res = await tool.execute(toolCallId, params, signal, onUpdate);
|
|
149
165
|
if (totalTextChars(res.content) <= thresholdChars)
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TaskStatus, ToolEffect } from "./types.js";
|
|
2
2
|
import type { ThinkingLevel } from "../internal/harness-types.js";
|
|
3
|
+
import type { ToolManifestRow } from "../prompt-assembly/tool-catalog.js";
|
|
3
4
|
export interface TraceMechanismsSummary {
|
|
4
5
|
finalizeInjected?: true;
|
|
5
6
|
nudgesSent?: number;
|
|
@@ -42,15 +43,7 @@ export type TraceEvent = {
|
|
|
42
43
|
hash: string;
|
|
43
44
|
contentHash?: string;
|
|
44
45
|
}>;
|
|
45
|
-
tools?:
|
|
46
|
-
wireName: string;
|
|
47
|
-
aliases: string[];
|
|
48
|
-
contractId: string;
|
|
49
|
-
implementationRevision: string;
|
|
50
|
-
cardId: string;
|
|
51
|
-
shapeDigest: string;
|
|
52
|
-
wireSchemaDigest: string;
|
|
53
|
-
}>;
|
|
46
|
+
tools?: ToolManifestRow[];
|
|
54
47
|
totalChars: number;
|
|
55
48
|
snapshot?: {
|
|
56
49
|
cacheIdentity: string;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -57,6 +57,19 @@ export type ToolReturn = string | {
|
|
|
57
57
|
terminate?: boolean;
|
|
58
58
|
isError?: boolean;
|
|
59
59
|
};
|
|
60
|
+
export interface NestedUsage {
|
|
61
|
+
tokens: number;
|
|
62
|
+
turns: number;
|
|
63
|
+
tasks: number;
|
|
64
|
+
costMicroUsd?: number;
|
|
65
|
+
}
|
|
66
|
+
export interface NestedUsageAccum {
|
|
67
|
+
tokens: number;
|
|
68
|
+
turns: number;
|
|
69
|
+
tasks: number;
|
|
70
|
+
costMicroUsd: number;
|
|
71
|
+
anyUnpriced: boolean;
|
|
72
|
+
}
|
|
60
73
|
export interface ToolExecuteContext {
|
|
61
74
|
toolCallId: string;
|
|
62
75
|
signal?: AbortSignal;
|
|
@@ -81,12 +94,7 @@ export interface ToolExecuteContext {
|
|
|
81
94
|
release: () => Promise<void>;
|
|
82
95
|
} | null>;
|
|
83
96
|
parentCwd?: string;
|
|
84
|
-
reportUsage?: (usage:
|
|
85
|
-
tokens: number;
|
|
86
|
-
turns: number;
|
|
87
|
-
tasks: number;
|
|
88
|
-
costMicroUsd?: number;
|
|
89
|
-
}) => void;
|
|
97
|
+
reportUsage?: (usage: NestedUsage) => void;
|
|
90
98
|
model?: Model;
|
|
91
99
|
thinkingLevel?: ThinkingLevel;
|
|
92
100
|
principal?: string;
|
|
@@ -412,12 +420,7 @@ export interface TaskResult {
|
|
|
412
420
|
outputTokens?: number;
|
|
413
421
|
cacheWriteTokensLong?: number;
|
|
414
422
|
costMicroUsd?: number;
|
|
415
|
-
nested?:
|
|
416
|
-
tokens: number;
|
|
417
|
-
turns: number;
|
|
418
|
-
tasks: number;
|
|
419
|
-
costMicroUsd?: number;
|
|
420
|
-
};
|
|
423
|
+
nested?: NestedUsage;
|
|
421
424
|
memory?: {
|
|
422
425
|
tokens: number;
|
|
423
426
|
costMicroUsd: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
2
2
|
import { scrubSecrets } from "./arg-summary.js";
|
|
3
|
+
import { sliceHeadSafe } from "./surrogate-safe-slice.js";
|
|
3
4
|
const URI_SCHEME = String.raw `[A-Za-z][A-Za-z0-9+.\-_]{0,63}`;
|
|
4
5
|
const CREDENTIALS_IN_AUTHORITY = new RegExp(String.raw `(${URI_SCHEME}:\/\/)((?:[^/\s"'@]*@)+)`, "gi");
|
|
5
6
|
const URL_OF_ANY_PROTOCOL = new RegExp(String.raw `${URI_SCHEME}:\/\/[^\s"'\`]+`, "gi");
|
|
@@ -52,16 +53,17 @@ function boundedString(value, max, redact) {
|
|
|
52
53
|
if (s.length <= max)
|
|
53
54
|
return s;
|
|
54
55
|
const totalMarkers = countRedactionMarkers(s);
|
|
55
|
-
const plainSuffix = (
|
|
56
|
-
const enrichedSuffix = (
|
|
57
|
-
const lost = totalMarkers - countRedactionMarkers(
|
|
58
|
-
return lost > 0 ? `…[+${s.length -
|
|
56
|
+
const plainSuffix = (kept) => `…[+${s.length - kept.length} chars]`;
|
|
57
|
+
const enrichedSuffix = (kept) => {
|
|
58
|
+
const lost = totalMarkers - countRedactionMarkers(kept);
|
|
59
|
+
return lost > 0 ? `…[+${s.length - kept.length} chars, ${lost} redaction marker(s) among them]` : plainSuffix(kept);
|
|
59
60
|
};
|
|
60
61
|
const converge = (suffixFor) => {
|
|
61
62
|
let keep = max;
|
|
62
63
|
let out = "";
|
|
63
64
|
for (let i = 0; i < 4; i++) {
|
|
64
|
-
|
|
65
|
+
const kept = sliceHeadSafe(s, keep);
|
|
66
|
+
out = `${kept}${suffixFor(kept)}`;
|
|
65
67
|
if (out.length <= max)
|
|
66
68
|
return out;
|
|
67
69
|
if (keep === 0)
|
|
@@ -3,23 +3,20 @@ export interface WorkflowJournalEntry {
|
|
|
3
3
|
callKey: string;
|
|
4
4
|
result: TaskResult;
|
|
5
5
|
}
|
|
6
|
+
export interface ResumeClaimArgs {
|
|
7
|
+
sourceRunId: string;
|
|
8
|
+
newRunId: string;
|
|
9
|
+
scope: string;
|
|
10
|
+
}
|
|
6
11
|
export interface WorkflowJournalStore {
|
|
7
12
|
load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
|
|
8
13
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
9
14
|
locator?(runId: string, scope: string): string | undefined;
|
|
10
|
-
resumeClaim?(input: {
|
|
11
|
-
sourceRunId: string;
|
|
12
|
-
newRunId: string;
|
|
13
|
-
scope: string;
|
|
14
|
-
}): Promise<{
|
|
15
|
+
resumeClaim?(input: ResumeClaimArgs): Promise<{
|
|
15
16
|
granted: boolean;
|
|
16
17
|
holder?: string;
|
|
17
18
|
}>;
|
|
18
|
-
releaseResumeClaim?(input:
|
|
19
|
-
sourceRunId: string;
|
|
20
|
-
newRunId: string;
|
|
21
|
-
scope: string;
|
|
22
|
-
}): Promise<void>;
|
|
19
|
+
releaseResumeClaim?(input: ResumeClaimArgs): Promise<void>;
|
|
23
20
|
}
|
|
24
21
|
export declare const MAX_JOURNAL_RESULT_BYTES: number;
|
|
25
22
|
export declare function oversizeJournalResult(serialized: string): boolean;
|
|
@@ -31,19 +28,11 @@ export declare class InMemoryWorkflowJournalStore implements WorkflowJournalStor
|
|
|
31
28
|
private readonly runs;
|
|
32
29
|
private readonly resumeClaims;
|
|
33
30
|
private claimsForScope;
|
|
34
|
-
resumeClaim(input: {
|
|
35
|
-
sourceRunId: string;
|
|
36
|
-
newRunId: string;
|
|
37
|
-
scope: string;
|
|
38
|
-
}): Promise<{
|
|
31
|
+
resumeClaim(input: ResumeClaimArgs): Promise<{
|
|
39
32
|
granted: boolean;
|
|
40
33
|
holder?: string;
|
|
41
34
|
}>;
|
|
42
|
-
releaseResumeClaim(input:
|
|
43
|
-
sourceRunId: string;
|
|
44
|
-
newRunId: string;
|
|
45
|
-
scope: string;
|
|
46
|
-
}): Promise<void>;
|
|
35
|
+
releaseResumeClaim(input: ResumeClaimArgs): Promise<void>;
|
|
47
36
|
load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
|
|
48
37
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
49
38
|
}
|