@sema-agent/core 2.13.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 +6 -1
- package/dist/agents/cascade.js +76 -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 +110 -3
- package/dist/agents/retain-ledger.d.ts +20 -2
- package/dist/agents/retain-ledger.js +38 -8
- package/dist/agents/roster-store.d.ts +4 -0
- package/dist/agents/roster-store.js +20 -1
- package/dist/agents/subagent.js +66 -64
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +23 -24
- package/dist/agents/team.js +7 -4
- package/dist/agents/verify.d.ts +15 -2
- package/dist/agents/verify.js +39 -3
- package/dist/bin/sema-tb.js +2 -2
- package/dist/brain/anthropic.js +24 -21
- package/dist/brain/circuit-breaker.d.ts +1 -0
- package/dist/brain/circuit-breaker.js +26 -9
- package/dist/brain/degrading.js +13 -1
- package/dist/brain/failover.js +2 -0
- package/dist/brain/openai.js +24 -21
- package/dist/brain/repetition.js +10 -1
- package/dist/brain/routing.js +5 -1
- package/dist/brain/status-sink.js +5 -1
- 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 +34 -15
- package/dist/core/auto-compaction.js +33 -32
- 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 +7 -13
- package/dist/core/checkpoint-store.js +39 -15
- package/dist/core/consolidate-scope.js +4 -3
- package/dist/core/context-edit.js +3 -0
- package/dist/core/context-guard.js +3 -0
- package/dist/core/file-snapshot-store.d.ts +8 -0
- package/dist/core/file-snapshot-store.js +4 -3
- package/dist/core/git-worktree-env.d.ts +6 -1
- package/dist/core/git-worktree-env.js +12 -1
- package/dist/core/hooks.d.ts +4 -1
- package/dist/core/hooks.js +35 -7
- package/dist/core/lsp-diagnostics.js +13 -7
- package/dist/core/lsp-protocol.d.ts +1 -1
- package/dist/core/lsp-protocol.js +20 -8
- package/dist/core/lsp-session.d.ts +12 -2
- package/dist/core/lsp-session.js +114 -49
- package/dist/core/lsp.d.ts +13 -1
- package/dist/core/lsp.js +65 -9
- 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/data-plane.js +4 -0
- package/dist/core/memory-engine/engine.d.ts +2 -2
- package/dist/core/memory-engine/engine.js +96 -31
- package/dist/core/memory-engine/file-backend.d.ts +2 -2
- package/dist/core/memory-engine/file-backend.js +38 -39
- package/dist/core/memory-engine/layout.d.ts +8 -0
- package/dist/core/memory-engine/layout.js +71 -4
- package/dist/core/memory-engine/scope-contract.js +9 -3
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/memory.js +3 -0
- package/dist/core/permission-rules.d.ts +2 -1
- package/dist/core/permission-rules.js +24 -3
- 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 +3 -8
- package/dist/core/runner/assemble-result.js +3 -5
- 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 +31 -53
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +86 -178
- 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/runner/usage-accounting.d.ts +6 -0
- package/dist/core/runner/usage-accounting.js +9 -8
- package/dist/core/safe-notify.d.ts +17 -0
- package/dist/core/safe-notify.js +59 -0
- package/dist/core/secret-env.js +5 -1
- package/dist/core/sensitive-path-policy.js +11 -4
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/store-contracts/session-repo-contract.js +40 -0
- 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 +15 -14
- 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 +72 -19
- 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 +3 -9
- package/dist/core/types.d.ts +16 -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/compaction.d.ts +1 -0
- package/dist/engine/compaction/compaction.js +14 -5
- 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/lsp/frame-decoder.js +6 -3
- package/dist/engine/lsp/node-lsp-manager.d.ts +2 -0
- package/dist/engine/lsp/node-lsp-manager.js +103 -26
- package/dist/engine/lsp/stdio-lsp-transport.js +16 -7
- 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/internal/harness.d.ts +1 -1
- package/dist/internal/harness.js +1 -1
- package/dist/orchestration/run-spec.js +7 -4
- package/dist/orchestration/run-workflow-tool.js +3 -35
- package/dist/orchestration/workflow-governance.js +43 -9
- package/dist/orchestration/workflow-sandbox-conformance.js +85 -51
- package/dist/orchestration/workflow-script-store.js +34 -4
- package/dist/orchestration/workflow.d.ts +3 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/artifact-store.d.ts +2 -0
- package/dist/prompt-assembly/artifact-store.js +39 -24
- package/dist/prompt-assembly/assemble.js +38 -7
- package/dist/prompt-assembly/epoch.js +19 -2
- package/dist/prompt-assembly/event-registry.js +3 -3
- package/dist/prompt-assembly/explain.js +1 -1
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/tool-catalog.js +16 -1
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/prompt-assembly/turn-snapshot.js +4 -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/fs-atomic.d.ts +3 -0
- package/dist/stores/file/fs-atomic.js +53 -8
- package/dist/stores/file/index.d.ts +1 -0
- package/dist/stores/file/index.js +7 -0
- 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 +4 -6
- package/dist/stores/file/session-store.js +28 -1
- 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 +2 -0
- package/dist/tools/fs/bash-readonly-classifier.js +73 -21
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +407 -91
- 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/loop-tick.js +1 -1
- package/dist/tools/monitor.d.ts +1 -0
- package/dist/tools/monitor.js +57 -9
- package/dist/tools/worktree.d.ts +4 -7
- package/dist/tools/worktree.js +235 -143
- package/package.json +9 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ImageContent } from "../../internal/llm.js";
|
|
1
2
|
export declare function isNotebookPath(p: string): boolean;
|
|
2
3
|
export declare const NOTEBOOK_CELL_OUTPUT_POINTER_CHARS = 10000;
|
|
3
4
|
export declare const NOTEBOOK_OUTPUT_TEXT_CLIP = 10000;
|
|
@@ -31,11 +32,7 @@ export interface NotebookRender {
|
|
|
31
32
|
blocks: Array<{
|
|
32
33
|
type: "text";
|
|
33
34
|
text: string;
|
|
34
|
-
} |
|
|
35
|
-
type: "image";
|
|
36
|
-
data: string;
|
|
37
|
-
mimeType: string;
|
|
38
|
-
}>;
|
|
35
|
+
} | ImageContent>;
|
|
39
36
|
textChars: number;
|
|
40
37
|
imageChars: number;
|
|
41
38
|
}
|
|
@@ -22,8 +22,11 @@ export interface FsViolation {
|
|
|
22
22
|
roots?: readonly string[];
|
|
23
23
|
message: string;
|
|
24
24
|
}
|
|
25
|
+
export declare function isAbsolutePathForm(p: string): boolean;
|
|
26
|
+
export declare function isWinFormPath(p: string): boolean;
|
|
25
27
|
export declare function isBlockedDevicePath(key: string): boolean;
|
|
26
28
|
export declare function normalizeAbsPathLexically(p: string): string;
|
|
29
|
+
export declare function foldRedundantPathSegments(p: string): string;
|
|
27
30
|
export declare function hasBinaryExtension(path: string): boolean;
|
|
28
31
|
export declare function imageMimeForRead(path: string): string | undefined;
|
|
29
32
|
export declare function imageMagicMatches(bytes: Uint8Array, mimeType: string): boolean;
|
package/dist/tools/fs/safety.js
CHANGED
|
@@ -18,20 +18,20 @@ export function sha256(content) {
|
|
|
18
18
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
19
19
|
}
|
|
20
20
|
const SEP = "/";
|
|
21
|
-
function isAbsolutePathForm(p) {
|
|
22
|
-
return p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p);
|
|
21
|
+
export function isAbsolutePathForm(p) {
|
|
22
|
+
return p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p) || p.startsWith("\\\\");
|
|
23
23
|
}
|
|
24
24
|
function normalizeForCompare(p) {
|
|
25
|
-
let n = p.replace(/\\/g, "/");
|
|
25
|
+
let n = isWinFormPath(p) ? p.replace(/\\/g, "/") : p;
|
|
26
26
|
if (/^[A-Za-z]:(\/|$)/.test(n))
|
|
27
27
|
n = n[0].toLowerCase() + n.slice(1);
|
|
28
28
|
return n;
|
|
29
29
|
}
|
|
30
30
|
function nativeSepOf(p) {
|
|
31
|
-
return p.includes("\\") ? "\\" : "/";
|
|
31
|
+
return isWinFormPath(p) && p.includes("\\") ? "\\" : "/";
|
|
32
32
|
}
|
|
33
33
|
function lastSepIndex(p) {
|
|
34
|
-
return Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
|
|
34
|
+
return isWinFormPath(p) ? Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\")) : p.lastIndexOf("/");
|
|
35
35
|
}
|
|
36
36
|
const BLOCKED_DEVICE_PATHS = new Set([
|
|
37
37
|
"/dev/zero", "/dev/random", "/dev/urandom", "/dev/full",
|
|
@@ -46,7 +46,7 @@ function isProcSensitiveFile(key) {
|
|
|
46
46
|
return key.startsWith("/proc/") && PROC_SENSITIVE_SUFFIXES.some((suf) => key.endsWith(suf));
|
|
47
47
|
}
|
|
48
48
|
const WIN_RESERVED_RE = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.[^\\/]*)?$/i;
|
|
49
|
-
function isWinFormPath(p) {
|
|
49
|
+
export function isWinFormPath(p) {
|
|
50
50
|
return /^[A-Za-z]:[\\/]/.test(p) || p.startsWith("\\\\") || (!p.startsWith("/") && p.includes("\\"));
|
|
51
51
|
}
|
|
52
52
|
function isWinReservedDeviceKey(key) {
|
|
@@ -60,19 +60,48 @@ export function isBlockedDevicePath(key) {
|
|
|
60
60
|
return BLOCKED_DEVICE_PATHS.has(key) || isProcStdioFd(key) || isProcSensitiveFile(key) || isWinReservedDeviceKey(key);
|
|
61
61
|
}
|
|
62
62
|
export function normalizeAbsPathLexically(p) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
return foldSegmentsLexically(p, true);
|
|
64
|
+
}
|
|
65
|
+
export function foldRedundantPathSegments(p) {
|
|
66
|
+
return foldSegmentsLexically(p, false);
|
|
67
|
+
}
|
|
68
|
+
function foldSegmentsLexically(p, foldParents) {
|
|
69
|
+
const isUnc = p.startsWith("\\\\");
|
|
70
|
+
const drive = /^[A-Za-z]:/.exec(p)?.[0] ?? "";
|
|
71
|
+
const winForm = isUnc || drive !== "" || (!p.startsWith("/") && p.includes("\\"));
|
|
72
|
+
const sep = winForm && p.includes("\\") ? "\\" : "/";
|
|
73
|
+
const splitter = winForm ? /[\\/]+/ : /\/+/;
|
|
74
|
+
let prefix = drive;
|
|
75
|
+
let rest = p.slice(drive.length + (isUnc ? 2 : 0));
|
|
76
|
+
let abs = rest.startsWith("/") || (winForm && rest.startsWith("\\"));
|
|
77
|
+
if (isUnc) {
|
|
78
|
+
const segs = rest.split(splitter).filter((seg) => seg !== "");
|
|
79
|
+
const host = segs.shift() ?? "";
|
|
80
|
+
const share = segs.shift();
|
|
81
|
+
prefix = sep + sep + host + (share === undefined ? "" : sep + share);
|
|
82
|
+
rest = segs.join(sep);
|
|
83
|
+
abs = true;
|
|
84
|
+
}
|
|
85
|
+
const outSegs = [];
|
|
86
|
+
for (const seg of rest.split(splitter)) {
|
|
67
87
|
if (seg === "" || seg === ".")
|
|
68
88
|
continue;
|
|
69
89
|
if (seg === "..") {
|
|
70
|
-
|
|
90
|
+
if (!foldParents) {
|
|
91
|
+
outSegs.push(seg);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (outSegs.length > 0 && outSegs[outSegs.length - 1] !== "..")
|
|
95
|
+
outSegs.pop();
|
|
96
|
+
else if (!abs)
|
|
97
|
+
outSegs.push("..");
|
|
71
98
|
continue;
|
|
72
99
|
}
|
|
73
|
-
|
|
100
|
+
outSegs.push(seg);
|
|
74
101
|
}
|
|
75
|
-
|
|
102
|
+
if (outSegs.length === 0)
|
|
103
|
+
return abs ? prefix + sep : prefix === "" ? "." : prefix;
|
|
104
|
+
return prefix + (abs ? sep : "") + outSegs.join(sep);
|
|
76
105
|
}
|
|
77
106
|
const BINARY_EXTENSIONS = new Set([
|
|
78
107
|
".exe", ".dll", ".so", ".dylib", ".o", ".a", ".obj", ".lib", ".bin", ".class", ".pyc", ".pyo", ".wasm",
|
|
@@ -229,7 +258,7 @@ function parentDir(abs) {
|
|
|
229
258
|
}
|
|
230
259
|
async function canonicalizeNewPath(env, abs, signal) {
|
|
231
260
|
const sep = nativeSepOf(abs);
|
|
232
|
-
const parts = abs.split(/[\\/]/);
|
|
261
|
+
const parts = isWinFormPath(abs) ? abs.split(/[\\/]/) : abs.split("/");
|
|
233
262
|
const tail = [];
|
|
234
263
|
while (parts.length > 1) {
|
|
235
264
|
const candidate = parts.join(sep) || sep;
|
package/dist/tools/fs/search.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { delimitUntrusted } from "../../core/untrusted-text.js";
|
|
2
|
-
import { hasBinaryExtension } from "./safety.js";
|
|
2
|
+
import { hasBinaryExtension, isAbsolutePathForm } from "./safety.js";
|
|
3
3
|
const WALK_MAX_FILES = 5000;
|
|
4
4
|
const WALK_MAX_DEPTH = 32;
|
|
5
5
|
const GREP_DEFAULT_CAP = 250;
|
|
@@ -51,7 +51,7 @@ export function splitGlobParam(glob) {
|
|
|
51
51
|
return out;
|
|
52
52
|
}
|
|
53
53
|
export function splitAbsoluteGlobPattern(pattern) {
|
|
54
|
-
if (!(pattern
|
|
54
|
+
if (!isAbsolutePathForm(pattern))
|
|
55
55
|
return undefined;
|
|
56
56
|
const m = /[*?[{]/.exec(pattern);
|
|
57
57
|
if (!m) {
|
|
@@ -758,7 +758,7 @@ export async function jsGrep(env, root, p, signal, guards) {
|
|
|
758
758
|
}
|
|
759
759
|
const ignore = await buildIgnore(env, root, signal);
|
|
760
760
|
const rootPrefix = root.replace(/[\\/]+$/, "") + (root.includes("\\") ? "\\" : "/");
|
|
761
|
-
const start = p.path ? (
|
|
761
|
+
const start = p.path ? (isAbsolutePathForm(p.path) ? p.path : `${rootPrefix}${p.path}`) : root;
|
|
762
762
|
let walked;
|
|
763
763
|
if (p.path) {
|
|
764
764
|
const info = await env.fileInfo(start, signal);
|
|
@@ -992,7 +992,7 @@ async function sortRgFilesByMtime(env, root, stdout, signal) {
|
|
|
992
992
|
const rootPrefix = root.replace(/[\\/]+$/, "") + (root.includes("\\") ? "\\" : "/");
|
|
993
993
|
const toAbs = (p) => {
|
|
994
994
|
const stripped = p.startsWith("./") ? p.slice(2) : p;
|
|
995
|
-
return
|
|
995
|
+
return isAbsolutePathForm(stripped) ? stripped : `${rootPrefix}${stripped}`;
|
|
996
996
|
};
|
|
997
997
|
const infos = await Promise.all(paths.map((p) => env.fileInfo(toAbs(p), signal)));
|
|
998
998
|
const withMtime = paths.map((p, i) => {
|
|
@@ -1104,7 +1104,7 @@ export async function runGlobDetailed(env, root, pattern, opts = {}, signal) {
|
|
|
1104
1104
|
const t0 = Date.now();
|
|
1105
1105
|
const baseIgnore = await buildIgnore(env, root, signal);
|
|
1106
1106
|
const rootPrefix = root.replace(/[\\/]+$/, "") + (root.includes("\\") ? "\\" : "/");
|
|
1107
|
-
const start = opts.path ? (
|
|
1107
|
+
const start = opts.path ? (isAbsolutePathForm(opts.path) ? opts.path : `${rootPrefix}${opts.path}`) : root;
|
|
1108
1108
|
if (opts.path !== undefined) {
|
|
1109
1109
|
const probe = await env.fileInfo(start, signal);
|
|
1110
1110
|
if (!probe.ok && probe.error.code === "not_found") {
|
package/dist/tools/loop-tick.js
CHANGED
|
@@ -49,7 +49,7 @@ Read and analyze freely — understanding the state of things has no blast radiu
|
|
|
49
49
|
`;
|
|
50
50
|
const DYNAMIC_APPENDIX = `
|
|
51
51
|
|
|
52
|
-
If a Monitor is armed (
|
|
52
|
+
If a Monitor is armed (its task_id was reported in the receipt when you started it), keep \`delaySeconds\` at 1200–1800s — the Monitor is the wake signal and this is only the fallback heartbeat. If you were woken by a \`<task-notification>\`, handle the event before deciding whether to re-arm. To stop the loop, call ${SCHEDULE_WAKEUP_TOOL_NAME} with \`stop: true\` and TaskStop the monitor (its task id is in that start receipt, and in the \`<task-id>\` of every notification the watch sends).`;
|
|
53
53
|
function pushNotificationAppendix(persistentVariant) {
|
|
54
54
|
const r = persistentVariant
|
|
55
55
|
? "newly blocked on a decision you won't make alone, you're ending the loop"
|
package/dist/tools/monitor.d.ts
CHANGED
|
@@ -17,5 +17,6 @@ export interface MonitorToolOptions {
|
|
|
17
17
|
batchWindowMs?: number;
|
|
18
18
|
maxBatchesPerMinute?: number;
|
|
19
19
|
toolResultStore?: ToolResultStore;
|
|
20
|
+
retainBackgroundProcesses?: boolean;
|
|
20
21
|
}
|
|
21
22
|
export declare function createMonitorTool(env: ExecutionEnv, opts: MonitorToolOptions): AgentTool;
|
package/dist/tools/monitor.js
CHANGED
|
@@ -29,6 +29,16 @@ const MONITOR_DESCRIPTION = "Run a shell command in the background and watch its
|
|
|
29
29
|
'- Wrong: `until grep -q "BUILD SUCCEEDED" build.log; do sleep 5; done` — if the build fails, this never exits and you wait until the watch times out.\n' +
|
|
30
30
|
'- Right: `until grep -qE "BUILD (SUCCEEDED|FAILED)" build.log; do sleep 5; done` — every terminal state exits.\n' +
|
|
31
31
|
"Before arming, ask: *if this process crashed right now, would my filter emit anything?* If not, widen it.";
|
|
32
|
+
function envBackgroundWallSec(caps, requestedSec) {
|
|
33
|
+
const positive = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
|
|
34
|
+
const cap = positive(caps.maxBgTimeoutSec);
|
|
35
|
+
const base = requestedSec ?? positive(caps.defaultBgTimeoutSec);
|
|
36
|
+
if (base === undefined)
|
|
37
|
+
return cap;
|
|
38
|
+
if (cap === undefined)
|
|
39
|
+
return base;
|
|
40
|
+
return Math.min(base, cap);
|
|
41
|
+
}
|
|
32
42
|
export function createMonitorTool(env, opts) {
|
|
33
43
|
return defineTool({
|
|
34
44
|
name: "Monitor",
|
|
@@ -56,9 +66,10 @@ export function createMonitorTool(env, opts) {
|
|
|
56
66
|
}
|
|
57
67
|
const isPersistent = persistent === true;
|
|
58
68
|
const timeoutMs = Math.min(MONITOR_MAX_TIMEOUT_MS, Math.max(1, Math.floor(timeout_ms ?? MONITOR_DEFAULT_TIMEOUT_MS)));
|
|
69
|
+
const spawnTimeoutSec = isPersistent ? undefined : Math.ceil(timeoutMs / 1000) + 5;
|
|
59
70
|
const spawn = await env.spawnBackground(command, {
|
|
60
71
|
...(opts.cwdRef !== undefined ? { cwd: opts.cwdRef.current } : {}),
|
|
61
|
-
...(
|
|
72
|
+
...(spawnTimeoutSec !== undefined ? { timeout: spawnTimeoutSec } : {}),
|
|
62
73
|
});
|
|
63
74
|
if (!spawn.ok)
|
|
64
75
|
return errorResult(`Error (Monitor): ${spawn.error.message}`);
|
|
@@ -87,18 +98,52 @@ export function createMonitorTool(env, opts) {
|
|
|
87
98
|
});
|
|
88
99
|
}
|
|
89
100
|
catch (e) {
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
const why = e instanceof Error ? e.message : String(e);
|
|
102
|
+
let killDetail;
|
|
103
|
+
try {
|
|
104
|
+
const kill = await env.killBackground(spawn.value.shellId);
|
|
105
|
+
if (!kill.ok)
|
|
106
|
+
killDetail = `${kill.error.code}: ${kill.error.message}`;
|
|
107
|
+
}
|
|
108
|
+
catch (killErr) {
|
|
109
|
+
killDetail = killErr instanceof Error ? killErr.message : String(killErr);
|
|
110
|
+
}
|
|
111
|
+
return errorResult(killDetail === undefined
|
|
112
|
+
? `Error (Monitor): the watch process was started but could not be registered (${why}); it has been terminated.`
|
|
113
|
+
: `Error (Monitor): the watch process was started but could not be registered (${why}), and it could NOT be terminated either (${killDetail}). ` +
|
|
114
|
+
`It may still be running as background shell ${spawn.value.shellId}; no task id was minted, so TaskOutput/TaskStop cannot reach it — ` +
|
|
115
|
+
"stop it out of band (the environment's own background time budget and its teardown sweep are the remaining backstops).");
|
|
92
116
|
}
|
|
93
|
-
const bgTimeout = env.backgroundCapabilities
|
|
117
|
+
const bgTimeout = envBackgroundWallSec(env.backgroundCapabilities, spawnTimeoutSec);
|
|
94
118
|
const wasClamped = timeout_ms !== undefined && timeout_ms !== timeoutMs;
|
|
95
119
|
const clampNote = !isPersistent && wasClamped ? ` (clamped to ${timeoutMs}ms from your requested ${timeout_ms}ms)` : "";
|
|
96
120
|
const timeoutIgnoredNote = isPersistent && timeout_ms !== undefined ? ` (the timeout_ms you passed is ignored because persistent is true)` : "";
|
|
121
|
+
const envWallMs = bgTimeout !== undefined ? bgTimeout * 1000 : undefined;
|
|
122
|
+
const envWallWins = !isPersistent && envWallMs !== undefined && envWallMs < timeoutMs;
|
|
123
|
+
const persistentBudget = bgTimeout !== undefined
|
|
124
|
+
? `the execution environment kills background processes after ${bgTimeout}s`
|
|
125
|
+
: `the execution environment's background time budget still applies`;
|
|
126
|
+
const retainedByEnv = env.backgroundCapabilities.retainBackgroundProcesses === true;
|
|
127
|
+
const retainedBySpec = opts.retainBackgroundProcesses === true;
|
|
128
|
+
const retained = retainedByEnv || retainedBySpec;
|
|
129
|
+
const persistentAnchor = sessionScoped
|
|
130
|
+
? retained
|
|
131
|
+
? ". This run retains background processes (environment capability or task spec), so at the run teardown the watch is marked retained and " +
|
|
132
|
+
"survives the session's release: only your TaskStop, the command's own exit, or that budget end it."
|
|
133
|
+
: "; it runs until then, until you stop it, or until the session ends."
|
|
134
|
+
: retained
|
|
135
|
+
? ". This mount carries no session identity, so the watch is NOT session-resident despite persistent: true; and because this " +
|
|
136
|
+
"run retains background processes (environment capability or task spec) it survives the session's release rather than being settled with the run — " +
|
|
137
|
+
"nothing but your TaskStop, the command's own exit, or that budget will end it, and no later turn can reach it " +
|
|
138
|
+
"(stop it explicitly with TaskStop before you finish)."
|
|
139
|
+
: ". This mount carries no session identity, so the watch is NOT session-resident despite persistent: true — " +
|
|
140
|
+
"it belongs to this task alone and the run teardown settles it, so do not count on it outliving this turn " +
|
|
141
|
+
"(stop it explicitly with TaskStop when you are done).";
|
|
97
142
|
const lifetime = isPersistent
|
|
98
|
-
?
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
143
|
+
? `It is persistent: no watch timeout of its own${timeoutIgnoredNote}, but ${persistentBudget}${persistentAnchor}`
|
|
144
|
+
: envWallWins
|
|
145
|
+
? `It will be killed after ${envWallMs}ms if still running — the execution environment's background time budget (${bgTimeout}s) is shorter than the ${timeoutMs}ms watch timeout${clampNote}, and the shorter of the two wins.`
|
|
146
|
+
: `It will be killed after ${timeoutMs}ms${clampNote} if still running.`;
|
|
102
147
|
const content = onNotify !== undefined
|
|
103
148
|
? `Monitoring in background; task_id=${taskId}. Each stdout line becomes a notification event (lines within ` +
|
|
104
149
|
`~200ms are batched); you'll get a final notification with the exit code when it ends — do not poll. ` +
|
|
@@ -111,8 +156,11 @@ export function createMonitorTool(env, opts) {
|
|
|
111
156
|
type: "monitor-start",
|
|
112
157
|
taskId,
|
|
113
158
|
persistent: isPersistent,
|
|
159
|
+
...(bgTimeout !== undefined ? { envBudgetSec: bgTimeout } : {}),
|
|
160
|
+
...(envWallWins && envWallMs !== undefined ? { effectiveTimeoutMs: envWallMs } : {}),
|
|
114
161
|
...(isPersistent
|
|
115
|
-
?
|
|
162
|
+
?
|
|
163
|
+
{ timeoutIgnored: timeout_ms !== undefined, sessionScoped, ...(retainedByEnv ? { retainedByEnv: true } : {}), ...(retainedBySpec ? { retainedBySpec: true } : {}) }
|
|
116
164
|
: { timeoutMs, ...(timeout_ms !== undefined ? { requestedTimeoutMs: timeout_ms } : {}), wasClamped }),
|
|
117
165
|
},
|
|
118
166
|
};
|
package/dist/tools/worktree.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import type { AgentTool, ExecutionEnv } from "../internal/harness-types.js";
|
|
1
|
+
import type { ActiveWorktreeSession, AgentTool, ExecutionEnv } from "../internal/harness-types.js";
|
|
2
2
|
import type { CwdRef } from "./fs/index.js";
|
|
3
|
-
export
|
|
4
|
-
worktreeDir: string;
|
|
5
|
-
originalCwd: string;
|
|
6
|
-
baseSha: string;
|
|
7
|
-
entered?: boolean;
|
|
8
|
-
}
|
|
3
|
+
export type { ActiveWorktreeSession };
|
|
9
4
|
export interface WorktreeSessionRef {
|
|
10
5
|
current?: ActiveWorktreeSession;
|
|
6
|
+
exiting?: boolean;
|
|
7
|
+
entering?: boolean;
|
|
11
8
|
}
|
|
12
9
|
export interface WorktreeToolsOptions {
|
|
13
10
|
repoRoot: string;
|