@sema-agent/core 5.6.0 → 5.8.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 +55 -0
- package/dist/agents/cascade.d.ts +1 -1
- package/dist/agents/subagent.d.ts +4 -7
- package/dist/agents/subagent.js +10 -19
- package/dist/agents/teacher.js +3 -3
- package/dist/agents/team.d.ts +1 -1
- package/dist/agents/team.js +1 -1
- package/dist/agents/verify.js +1 -1
- package/dist/brain/anthropic.js +24 -35
- package/dist/brain/openai.js +22 -33
- package/dist/brain/stream-engine.d.ts +0 -2
- package/dist/brain/stream-engine.js +5 -47
- package/dist/brain/stream-shared.d.ts +0 -10
- package/dist/brain/stream-shared.js +0 -23
- package/dist/brain/terminal-cause.d.ts +0 -1
- package/dist/brain/terminal-cause.js +0 -3
- package/dist/brain/timeout.d.ts +4 -2
- package/dist/brain/timeout.js +4 -14
- package/dist/config/catalog.d.ts +3 -11
- package/dist/config/catalog.js +30 -69
- package/dist/config/defaults.d.ts +0 -3
- package/dist/config/defaults.js +0 -3
- package/dist/core/auto-compaction.d.ts +2 -11
- package/dist/core/auto-compaction.js +3 -67
- package/dist/core/checkpoint-store.d.ts +15 -10
- package/dist/core/checkpoint-store.js +13 -10
- package/dist/core/memory-engine/file-backend.js +20 -17
- package/dist/core/memory-engine/layout.d.ts +5 -0
- package/dist/core/memory-engine/layout.js +72 -47
- package/dist/core/runner/assemble-result.d.ts +2 -8
- package/dist/core/runner/assemble-result.js +15 -16
- package/dist/core/runner/prepare-task.d.ts +26 -6
- package/dist/core/runner/prepare-task.js +203 -141
- package/dist/core/runner/runtask.d.ts +0 -2
- package/dist/core/runner/runtask.js +267 -253
- package/dist/core/secret-env.d.ts +2 -0
- package/dist/core/secret-env.js +16 -4
- package/dist/core/session-policy-store.d.ts +5 -0
- package/dist/core/session-policy-store.js +3 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/task-notification.d.ts +1 -0
- package/dist/core/task-notification.js +3 -0
- package/dist/core/task-registry-agent.d.ts +2 -0
- package/dist/core/task-registry-agent.js +33 -1
- package/dist/core/task-registry.d.ts +2 -0
- package/dist/core/task-registry.js +9 -1
- package/dist/core/tool-errors.js +10 -2
- package/dist/core/trace.d.ts +1 -8
- package/dist/core/types.d.ts +24 -30
- package/dist/core/usage-window-store.d.ts +39 -0
- package/dist/core/usage-window-store.js +115 -0
- package/dist/engine/execution-env/node-execution-env.js +0 -22
- package/dist/engine/harness/agent-harness.d.ts +0 -7
- package/dist/engine/harness/agent-harness.js +13 -53
- package/dist/engine/harness/types.d.ts +2 -15
- package/dist/engine/llm/types.d.ts +1 -2
- package/dist/engine/loop/agent-loop.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +5 -171
- package/dist/engine/loop/types.d.ts +0 -18
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8 -4
- package/dist/orchestration/goal.js +1 -5
- package/dist/orchestration/run-workflow-tool.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow-governance.d.ts +6 -6
- package/dist/orchestration/workflow-governance.js +33 -47
- package/dist/prompt-assembly/event-registry.js +2 -3
- package/dist/stores/cc/lockfile.js +9 -11
- package/dist/stores/cc/mailbox-store.js +49 -38
- package/dist/stores/file/index.d.ts +3 -0
- package/dist/stores/file/index.js +4 -0
- package/dist/stores/file/usage-window-store.d.ts +9 -0
- package/dist/stores/file/usage-window-store.js +86 -0
- package/dist/tools/fs/fs-bash.d.ts +1 -10
- package/dist/tools/fs/fs-bash.js +20 -60
- package/dist/tools/fs/index.d.ts +0 -2
- package/dist/tools/fs/index.js +1 -2
- package/package.json +1 -4
- package/dist/bin/sema-tb.d.ts +0 -31
- package/dist/bin/sema-tb.js +0 -448
- package/dist/bin/tb-env.d.ts +0 -2
- package/dist/bin/tb-env.js +0 -17
- package/dist/brain/walltime.d.ts +0 -1
- package/dist/brain/walltime.js +0 -1
- package/dist/core/runner/call-cap.d.ts +0 -67
- package/dist/core/runner/call-cap.js +0 -145
- package/dist/core/runner/cut-kill.d.ts +0 -10
- package/dist/core/runner/cut-kill.js +0 -37
package/dist/tools/fs/fs-bash.js
CHANGED
|
@@ -231,61 +231,27 @@ function msTimeoutToRequestedSec(timeoutMs) {
|
|
|
231
231
|
return undefined;
|
|
232
232
|
return Math.max(1, Math.round(timeoutMs / 1000));
|
|
233
233
|
}
|
|
234
|
-
async function runShell(env, cwd, toolName, command, timeoutSec, caps, signal, cwdRef, detach,
|
|
234
|
+
async function runShell(env, cwd, toolName, command, timeoutSec, caps, signal, cwdRef, detach, readOnly, containmentRoots) {
|
|
235
235
|
const requestedSec = Math.max(1, Math.floor(timeoutSec ?? caps.defaultSec));
|
|
236
236
|
let timeout = Math.min(caps.maxSec, requestedSec);
|
|
237
237
|
const cappedByMaxTimeout = requestedSec > caps.maxSec;
|
|
238
|
-
let clampedByDeadline = false;
|
|
239
|
-
let deadlineExhausted = false;
|
|
240
|
-
if (execClamp) {
|
|
241
|
-
const deadline = execClamp.deadlineMs();
|
|
242
|
-
if (deadline !== undefined) {
|
|
243
|
-
const rawRemainSec = Math.floor((deadline - Date.now()) / 1000);
|
|
244
|
-
const maxSec = Math.max(1, rawRemainSec);
|
|
245
|
-
if (maxSec < timeout) {
|
|
246
|
-
execClamp.onClamp?.(requestedSec, maxSec);
|
|
247
|
-
timeout = maxSec;
|
|
248
|
-
clampedByDeadline = true;
|
|
249
|
-
deadlineExhausted = rawRemainSec < 1;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
238
|
const effectiveCwd = cwdRef ? cwdRef.current : cwd;
|
|
254
239
|
const toRun = cwdRef
|
|
255
240
|
? `${command}\n__cc_rc=$?; printf '%s%s%s' '${CWD_SENTINEL}' "$(pwd)" '${CWD_SENTINEL}' 1>&2; exit $__cc_rc`
|
|
256
241
|
: command;
|
|
257
242
|
const execEnv = cwdRef ? { env: { PWD: effectiveCwd } } : undefined;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
onDetachAdopted: () => {
|
|
271
|
-
deregisterKill?.();
|
|
272
|
-
deregisterKill = undefined;
|
|
273
|
-
},
|
|
274
|
-
autoBackgroundOnTimeout: detach.autoBackgroundEligible === true && !clampedByDeadline,
|
|
275
|
-
}
|
|
276
|
-
: {}),
|
|
277
|
-
...(registerKillHandle !== undefined && toolCallId !== undefined
|
|
278
|
-
? {
|
|
279
|
-
onSpawn: (handle) => {
|
|
280
|
-
deregisterKill = registerKillHandle(toolCallId, handle);
|
|
281
|
-
},
|
|
282
|
-
}
|
|
283
|
-
: {}),
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
finally {
|
|
287
|
-
deregisterKill?.();
|
|
288
|
-
}
|
|
243
|
+
const res = await env.exec(toRun, {
|
|
244
|
+
cwd: effectiveCwd,
|
|
245
|
+
timeout,
|
|
246
|
+
abortSignal: signal,
|
|
247
|
+
...execEnv,
|
|
248
|
+
...(detach
|
|
249
|
+
? {
|
|
250
|
+
detachSignal: detach.signal,
|
|
251
|
+
autoBackgroundOnTimeout: detach.autoBackgroundEligible === true,
|
|
252
|
+
}
|
|
253
|
+
: {}),
|
|
254
|
+
});
|
|
289
255
|
if (!res.ok) {
|
|
290
256
|
if (res.error.code === "spawn_error" || res.error.code === "shell_unavailable") {
|
|
291
257
|
const diagnosis = await diagnoseDeadCwd(env, toolName, effectiveCwd, cwd, cwdRef);
|
|
@@ -321,13 +287,9 @@ async function runShell(env, cwd, toolName, command, timeoutSec, caps, signal, c
|
|
|
321
287
|
const pStdout = clipShellOutput(rawStdout);
|
|
322
288
|
const pStderr = clipShellOutput(rawStderr);
|
|
323
289
|
const headline = res.error.code === "timeout"
|
|
324
|
-
?
|
|
325
|
-
?
|
|
326
|
-
|
|
327
|
-
: `Command was cut off after ${timeout}s — NOT its requested ${requestedSec}s timeout: the task is near its wall-clock deadline, so the foreground time budget was clamped to the ${timeout}s remaining. The process was killed; output produced before the cutoff is shown below. Do NOT retry this foreground command — the next attempt gets even less time. Use run_in_background (exempt from this clamp) if the work must continue, or write out your results now.`
|
|
328
|
-
: cappedByMaxTimeout
|
|
329
|
-
? `Command timed out after ${timeout}s — requested ${requestedSec}s, capped at ${caps.maxSec}s (engine ceiling: requests above ${caps.maxSec}s are reduced to it). The process was killed; output produced before the cutoff is shown below. Re-running with a larger timeout gets the same ${caps.maxSec}s ceiling — use run_in_background for work that needs longer, a narrower command, or resume from the partial progress below.`
|
|
330
|
-
: `Command timed out after ${timeout}s (timeout limit: ${timeout}s). The process was killed; output produced before the cutoff is shown below. Re-running the same command will likely time out again — consider run_in_background for long commands, a narrower command, or resuming from the partial progress below.`
|
|
290
|
+
? cappedByMaxTimeout
|
|
291
|
+
? `Command timed out after ${timeout}s — requested ${requestedSec}s, capped at ${caps.maxSec}s (engine ceiling: requests above ${caps.maxSec}s are reduced to it). The process was killed; output produced before the cutoff is shown below. Re-running with a larger timeout gets the same ${caps.maxSec}s ceiling — use run_in_background for work that needs longer, a narrower command, or resume from the partial progress below.`
|
|
292
|
+
: `Command timed out after ${timeout}s (timeout limit: ${timeout}s). The process was killed; output produced before the cutoff is shown below. Re-running the same command will likely time out again — consider run_in_background for long commands, a narrower command, or resuming from the partial progress below.`
|
|
331
293
|
: res.error.code === "aborted"
|
|
332
294
|
? `Command was interrupted (aborted) before completion. Output produced before the interrupt is shown below.`
|
|
333
295
|
: `Command started but its completion could not be observed — a host-side output-stream callback failed mid-run and the process was terminated (${res.error.message}). Output captured before the cut is shown below; its effects up to that point may have landed. Verify before assuming it needs a full re-run.`;
|
|
@@ -350,9 +312,7 @@ async function runShell(env, cwd, toolName, command, timeoutSec, caps, signal, c
|
|
|
350
312
|
? {
|
|
351
313
|
timedOut: true,
|
|
352
314
|
timeoutSec: timeout,
|
|
353
|
-
...(
|
|
354
|
-
...(clampedByDeadline ? { clampedByDeadline: true } : {}),
|
|
355
|
-
...(cappedByMaxTimeout ? { cappedToMaxTimeoutSec: caps.maxSec } : {}),
|
|
315
|
+
...(cappedByMaxTimeout ? { requestedTimeoutSec: requestedSec, cappedToMaxTimeoutSec: caps.maxSec } : {}),
|
|
356
316
|
}
|
|
357
317
|
: res.error.code === "aborted"
|
|
358
318
|
? { aborted: true }
|
|
@@ -696,7 +656,7 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
|
|
|
696
656
|
: undefined;
|
|
697
657
|
let res;
|
|
698
658
|
try {
|
|
699
|
-
res = await runShell(env, rootCanonical, "Bash", command, msTimeoutToRequestedSec(timeout), timeoutCapsSecView, ctx.signal, cwdRef, detachChain,
|
|
659
|
+
res = await runShell(env, rootCanonical, "Bash", command, msTimeoutToRequestedSec(timeout), timeoutCapsSecView, ctx.signal, cwdRef, detachChain, false, [rootCanonical, ...(taskOpts.additionalRoots ?? [])]);
|
|
700
660
|
}
|
|
701
661
|
finally {
|
|
702
662
|
taskOpts.detachHub?.gc(ctx.toolCallId);
|
|
@@ -941,7 +901,7 @@ function createCanonicalReadBoundary(env, readRoots) {
|
|
|
941
901
|
},
|
|
942
902
|
};
|
|
943
903
|
}
|
|
944
|
-
export function createBashReadonlyTool(env, rootCanonical, allow,
|
|
904
|
+
export function createBashReadonlyTool(env, rootCanonical, allow, opts) {
|
|
945
905
|
const timeoutCaps = resolveBashTimeoutCaps(opts);
|
|
946
906
|
const timeoutCapsSecView = bashTimeoutCapsSec(timeoutCaps);
|
|
947
907
|
const sample = [...allow].slice(0, 6).join(", ");
|
|
@@ -996,7 +956,7 @@ export function createBashReadonlyTool(env, rootCanonical, allow, execClamp, opt
|
|
|
996
956
|
return errorResult(`Error (Bash): a path this command reads resolves through a symlink to ${quoted}, outside the allowed directories for this session: ${readRoots.join(", ")} — not auto-allowed. ` +
|
|
997
957
|
"bash_readonly is confined to the workspace roots; it has no approval path, so the call is refused rather than escalated.", { code: "readonly_out_of_root", paths: resolved.escaping });
|
|
998
958
|
}
|
|
999
|
-
return await runShell(env, rootCanonical, "Bash", command, msTimeoutToRequestedSec(timeout), timeoutCapsSecView, ctx.signal, undefined, undefined,
|
|
959
|
+
return await runShell(env, rootCanonical, "Bash", command, msTimeoutToRequestedSec(timeout), timeoutCapsSecView, ctx.signal, undefined, undefined, true);
|
|
1000
960
|
},
|
|
1001
961
|
});
|
|
1002
962
|
}
|
package/dist/tools/fs/index.d.ts
CHANGED
|
@@ -11,7 +11,6 @@ export * from "./fs-search-tools.js";
|
|
|
11
11
|
export * from "./bash-readonly-classifier.js";
|
|
12
12
|
export * from "./fs-bash.js";
|
|
13
13
|
import { type CwdRef, type ReadImageDownsamplerOption } from "./fs-shared.js";
|
|
14
|
-
import { type ExecClampOption } from "./fs-bash.js";
|
|
15
14
|
export interface HandsToolkitOptions {
|
|
16
15
|
additionalRoots?: readonly string[];
|
|
17
16
|
includeShell?: boolean;
|
|
@@ -28,7 +27,6 @@ export interface HandsToolkitOptions {
|
|
|
28
27
|
priority?: "now" | "next" | "later";
|
|
29
28
|
}) => void;
|
|
30
29
|
detachHub?: import("../../core/tool-detach.js").ToolDetachHub;
|
|
31
|
-
execClamp?: ExecClampOption;
|
|
32
30
|
bashDefaultTimeoutMs?: number;
|
|
33
31
|
bashMaxTimeoutMs?: number;
|
|
34
32
|
oneShot?: boolean;
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -35,7 +35,7 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
35
35
|
tools.push(createGrepTool(env, rootCanonical, additionalRoots), createGlobTool(env, rootCanonical, additionalRoots), createRepoMapTool(env, rootCanonical, additionalRoots));
|
|
36
36
|
if (includeShell) {
|
|
37
37
|
tools.push(readOnly
|
|
38
|
-
? createBashReadonlyTool(env, rootCanonical, new Set(bashReadonlyAllow ?? BASH_READONLY_DEFAULT_ALLOW),
|
|
38
|
+
? createBashReadonlyTool(env, rootCanonical, new Set(bashReadonlyAllow ?? BASH_READONLY_DEFAULT_ALLOW), {
|
|
39
39
|
...(opts.bashDefaultTimeoutMs !== undefined ? { bashDefaultTimeoutMs: opts.bashDefaultTimeoutMs } : {}),
|
|
40
40
|
...(opts.bashMaxTimeoutMs !== undefined ? { bashMaxTimeoutMs: opts.bashMaxTimeoutMs } : {}),
|
|
41
41
|
...(additionalRoots !== undefined ? { additionalRoots } : {}),
|
|
@@ -47,7 +47,6 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
47
47
|
...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}),
|
|
48
48
|
onTaskNotification: opts.taskNotification,
|
|
49
49
|
detachHub: opts.detachHub,
|
|
50
|
-
execClamp: opts.execClamp,
|
|
51
50
|
...(opts.autoBackgroundOnTimeout !== undefined ? { autoBackgroundOnTimeout: opts.autoBackgroundOnTimeout } : {}),
|
|
52
51
|
...(opts.oneShot !== undefined ? { oneShot: opts.oneShot } : {}),
|
|
53
52
|
...(additionalRoots !== undefined ? { additionalRoots } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "Stateless, task-oriented AI agent core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -20,9 +20,6 @@
|
|
|
20
20
|
],
|
|
21
21
|
"main": "./dist/index.js",
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
|
-
"bin": {
|
|
24
|
-
"sema-tb": "dist/bin/sema-tb.js"
|
|
25
|
-
},
|
|
26
23
|
"exports": {
|
|
27
24
|
".": {
|
|
28
25
|
"types": "./dist/index.d.ts",
|
package/dist/bin/sema-tb.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { type Model } from "../index.js";
|
|
3
|
-
interface Args {
|
|
4
|
-
model: string;
|
|
5
|
-
objectiveFile: string | undefined;
|
|
6
|
-
jsonl: string | undefined;
|
|
7
|
-
}
|
|
8
|
-
export type ParsedArgs = {
|
|
9
|
-
args: Args;
|
|
10
|
-
} | {
|
|
11
|
-
help: true;
|
|
12
|
-
} | {
|
|
13
|
-
usageError: string;
|
|
14
|
-
};
|
|
15
|
-
export declare function parseArgs(argv: string[], env?: Record<string, string | undefined>): ParsedArgs;
|
|
16
|
-
export type InstructionResolution = {
|
|
17
|
-
instruction: string;
|
|
18
|
-
} | {
|
|
19
|
-
error: string;
|
|
20
|
-
};
|
|
21
|
-
export declare function resolveInstruction(args: Pick<Args, "objectiveFile">, readInput: (source: string | 0) => string): InstructionResolution;
|
|
22
|
-
type TbCost = Model["cost"];
|
|
23
|
-
export type TbCostResolution = {
|
|
24
|
-
cost: TbCost;
|
|
25
|
-
source: "tb-cost-env" | "model-family" | "provider-default" | "disabled";
|
|
26
|
-
warning?: string;
|
|
27
|
-
} | {
|
|
28
|
-
error: string;
|
|
29
|
-
};
|
|
30
|
-
export declare function resolveTbCost(modelId: string, provider: string, baseUrlOverridden: boolean, tbCostEnv: string | undefined): TbCostResolution;
|
|
31
|
-
export {};
|
package/dist/bin/sema-tb.js
DELETED
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, realpathSync, createWriteStream } from "node:fs";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { Runner, NodeExecutionEnv, createOpenAIBrain, createAnthropicBrain, CODE_SYSTEM_PROMPT, assembleCodeTools, createWebFetchSummarizer, } from "../index.js";
|
|
5
|
-
import { positiveIntFromEnv, tbModelInput } from "./tb-env.js";
|
|
6
|
-
import { inferMaxTokensField } from "../brain/openai.js";
|
|
7
|
-
function usage(code = 2) {
|
|
8
|
-
const out = code === 0 ? process.stdout : process.stderr;
|
|
9
|
-
out.write("Usage: sema-tb --model <provider/model> [--objective-file <path>] [--jsonl <path>]\n" +
|
|
10
|
-
" Instruction legs: --objective-file <path> (preferred for real task text) | stdin (default when the flag is absent).\n" +
|
|
11
|
-
" WARNING: argv is visible to every process in the sandbox (ps / pgrep -f / /proc/*/cmdline), so instructions ride\n" +
|
|
12
|
-
" --objective-file or stdin; the retired --message argv leg is refused (process-list exposure).\n" +
|
|
13
|
-
"Env: <PROVIDER>_API_KEY / <PROVIDER>_BASE_URL, TB_MAX_TURNS, TB_TIMEOUT_SEC, TB_FULLBODY, TB_TODOREMIND, TB_BGTASKS, TB_SUMMARIZE,\n" +
|
|
14
|
-
" TB_FINALVERIFY, TB_CALLCAP, TB_FINALIZE, TB_REASONING, TB_JSONL, TB_CONTEXT_WINDOW, TB_MAX_OUTPUT_TOKENS, TB_TASKLIST, TB_VISION\n" +
|
|
15
|
-
' TB_COST="input,output[,cacheRead[,cacheWrite]]" — USD per MTok; explicit pricing override (omitted\n' +
|
|
16
|
-
" trailing segments default to 0). Without it, pricing resolves by MODEL FAMILY (deepseek/claude/gpt-o*)\n" +
|
|
17
|
-
" first, then the wire-prefix table; unknown model on an overridden BASE_URL ⇒ cost accounting disabled.\n");
|
|
18
|
-
process.exit(code);
|
|
19
|
-
}
|
|
20
|
-
export function parseArgs(argv, env = process.env) {
|
|
21
|
-
let model;
|
|
22
|
-
let objectiveFile;
|
|
23
|
-
let jsonl;
|
|
24
|
-
for (let i = 0; i < argv.length; i++) {
|
|
25
|
-
const a = argv[i];
|
|
26
|
-
if (a === "--message") {
|
|
27
|
-
return { usageError: "--message is retired (argv is visible to every sandbox process — the exposure once made a model's pgrep cleanup kill its own agent). Use --objective-file <path> or pipe the instruction on stdin." };
|
|
28
|
-
}
|
|
29
|
-
if (a === "--model" || a === "--objective-file" || a === "--jsonl") {
|
|
30
|
-
const v = argv[++i];
|
|
31
|
-
if (v === undefined)
|
|
32
|
-
return { usageError: `missing value for ${a}` };
|
|
33
|
-
if (a === "--model")
|
|
34
|
-
model = v;
|
|
35
|
-
else if (a === "--objective-file")
|
|
36
|
-
objectiveFile = v;
|
|
37
|
-
else
|
|
38
|
-
jsonl = v;
|
|
39
|
-
}
|
|
40
|
-
else if (a === "--help" || a === "-h")
|
|
41
|
-
return { help: true };
|
|
42
|
-
else
|
|
43
|
-
return { usageError: `unknown argument: ${a}` };
|
|
44
|
-
}
|
|
45
|
-
if (!model || !model.includes("/")) {
|
|
46
|
-
return { usageError: "--model must be <provider/model> (e.g. deepseek/deepseek-v4-flash)" };
|
|
47
|
-
}
|
|
48
|
-
return { args: { model, objectiveFile, jsonl: jsonl ?? env.TB_JSONL } };
|
|
49
|
-
}
|
|
50
|
-
export function resolveInstruction(args, readInput) {
|
|
51
|
-
const leg = args.objectiveFile !== undefined ? `--objective-file ${args.objectiveFile}` : "stdin (no --objective-file)";
|
|
52
|
-
let raw;
|
|
53
|
-
try {
|
|
54
|
-
raw = readInput(args.objectiveFile ?? 0);
|
|
55
|
-
}
|
|
56
|
-
catch (e) {
|
|
57
|
-
return { error: `cannot read instruction from ${leg}: ${e instanceof Error ? e.message : String(e)}` };
|
|
58
|
-
}
|
|
59
|
-
const instruction = raw.trim();
|
|
60
|
-
if (!instruction)
|
|
61
|
-
return { error: `empty instruction (${leg})` };
|
|
62
|
-
return { instruction };
|
|
63
|
-
}
|
|
64
|
-
const envPrefix = (provider) => provider.toUpperCase().replace(/-/g, "_");
|
|
65
|
-
const PROVIDER_DEFAULTS = {
|
|
66
|
-
deepseek: {
|
|
67
|
-
baseUrl: "https://api.deepseek.com",
|
|
68
|
-
contextWindow: 200_000,
|
|
69
|
-
maxTokens: 32_000,
|
|
70
|
-
charsPerToken: 3,
|
|
71
|
-
reasoning: true,
|
|
72
|
-
cost: { input: 0.28, output: 0.42, cacheRead: 0.028, cacheWrite: 0 },
|
|
73
|
-
},
|
|
74
|
-
openai: {
|
|
75
|
-
baseUrl: "https://api.openai.com/v1",
|
|
76
|
-
contextWindow: 200_000,
|
|
77
|
-
maxTokens: 16_384,
|
|
78
|
-
charsPerToken: 3,
|
|
79
|
-
reasoning: false,
|
|
80
|
-
cost: { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
|
|
81
|
-
},
|
|
82
|
-
anthropic: {
|
|
83
|
-
baseUrl: "https://api.anthropic.com",
|
|
84
|
-
contextWindow: 200_000,
|
|
85
|
-
maxTokens: 32_000,
|
|
86
|
-
charsPerToken: 3,
|
|
87
|
-
reasoning: true,
|
|
88
|
-
cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
92
|
-
function parseTbCostEnv(raw) {
|
|
93
|
-
const parts = raw.split(",").map((p) => p.trim());
|
|
94
|
-
if (parts.length > 4)
|
|
95
|
-
return { error: `expected at most 4 comma-separated values, got ${parts.length}` };
|
|
96
|
-
const nums = [];
|
|
97
|
-
for (const p of parts) {
|
|
98
|
-
if (!/^(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/.test(p)) {
|
|
99
|
-
return { error: `${JSON.stringify(p)} is not a non-negative number` };
|
|
100
|
-
}
|
|
101
|
-
const n = Number(p);
|
|
102
|
-
if (!Number.isFinite(n))
|
|
103
|
-
return { error: `${JSON.stringify(p)} is not finite` };
|
|
104
|
-
nums.push(n);
|
|
105
|
-
}
|
|
106
|
-
return { input: nums[0] ?? 0, output: nums[1] ?? 0, cacheRead: nums[2] ?? 0, cacheWrite: nums[3] ?? 0 };
|
|
107
|
-
}
|
|
108
|
-
export function resolveTbCost(modelId, provider, baseUrlOverridden, tbCostEnv) {
|
|
109
|
-
const raw = tbCostEnv?.trim();
|
|
110
|
-
if (raw) {
|
|
111
|
-
const parsed = parseTbCostEnv(raw);
|
|
112
|
-
if ("error" in parsed) {
|
|
113
|
-
return {
|
|
114
|
-
error: `sema-tb: invalid TB_COST ${JSON.stringify(tbCostEnv)}: ${parsed.error} ` +
|
|
115
|
-
`(expected "input,output[,cacheRead[,cacheWrite]]" in USD per MTok)`,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
return { cost: parsed, source: "tb-cost-env" };
|
|
119
|
-
}
|
|
120
|
-
const id = modelId.toLowerCase();
|
|
121
|
-
const family = id.includes("deepseek")
|
|
122
|
-
? "deepseek"
|
|
123
|
-
: id.startsWith("claude")
|
|
124
|
-
? "anthropic"
|
|
125
|
-
: id.startsWith("gpt") || id.startsWith("o1") || id.startsWith("o3") || id.startsWith("o4")
|
|
126
|
-
? "openai"
|
|
127
|
-
: undefined;
|
|
128
|
-
if (family)
|
|
129
|
-
return { cost: { ...PROVIDER_DEFAULTS[family].cost }, source: "model-family" };
|
|
130
|
-
if (!baseUrlOverridden) {
|
|
131
|
-
const d = PROVIDER_DEFAULTS[provider];
|
|
132
|
-
return { cost: d ? { ...d.cost } : { ...ZERO_COST }, source: "provider-default" };
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
cost: { ...ZERO_COST },
|
|
136
|
-
source: "disabled",
|
|
137
|
-
warning: `sema-tb: unknown pricing for model ${modelId} on overridden endpoint — ` +
|
|
138
|
-
`cost accounting disabled; set TB_COST to enable\n`,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
function buildModelAndBrain(providerModel) {
|
|
142
|
-
const [provider, ...rest] = providerModel.split("/");
|
|
143
|
-
const modelId = rest.join("/");
|
|
144
|
-
const prefix = envPrefix(provider);
|
|
145
|
-
const apiKeyEnv = `${prefix}_API_KEY`;
|
|
146
|
-
const apiKey = process.env[apiKeyEnv] ??
|
|
147
|
-
(provider === "anthropic" ? process.env.ANTHROPIC_AUTH_TOKEN : undefined);
|
|
148
|
-
const baseUrlOverride = process.env[`${prefix}_BASE_URL`]?.trim();
|
|
149
|
-
const d = PROVIDER_DEFAULTS[provider];
|
|
150
|
-
if (!d && !baseUrlOverride) {
|
|
151
|
-
process.stderr.write(`sema-tb: unknown provider "${provider}" and no ${prefix}_BASE_URL set — cannot resolve an endpoint.\n`);
|
|
152
|
-
process.exit(2);
|
|
153
|
-
}
|
|
154
|
-
if (!apiKey) {
|
|
155
|
-
process.stderr.write(`sema-tb: missing ${apiKeyEnv} in the environment.\n`);
|
|
156
|
-
process.exit(2);
|
|
157
|
-
}
|
|
158
|
-
const costRes = resolveTbCost(modelId, provider, Boolean(baseUrlOverride), process.env.TB_COST);
|
|
159
|
-
if ("error" in costRes) {
|
|
160
|
-
process.stderr.write(`${costRes.error}\n`);
|
|
161
|
-
process.exit(2);
|
|
162
|
-
}
|
|
163
|
-
if (costRes.warning)
|
|
164
|
-
process.stderr.write(costRes.warning);
|
|
165
|
-
const isAnthropic = provider === "anthropic";
|
|
166
|
-
const contextWindowOverride = positiveIntFromEnv("TB_CONTEXT_WINDOW");
|
|
167
|
-
const maxOutputTokensOverride = positiveIntFromEnv("TB_MAX_OUTPUT_TOKENS");
|
|
168
|
-
const model = {
|
|
169
|
-
id: modelId,
|
|
170
|
-
name: providerModel,
|
|
171
|
-
api: isAnthropic ? "anthropic-messages" : "openai-completions",
|
|
172
|
-
provider,
|
|
173
|
-
baseUrl: baseUrlOverride || d.baseUrl,
|
|
174
|
-
reasoning: process.env.TB_REASONING === "0" ? false : process.env.TB_REASONING === "1" ? true : (d?.reasoning ?? false),
|
|
175
|
-
input: tbModelInput(),
|
|
176
|
-
cost: costRes.cost,
|
|
177
|
-
contextWindow: contextWindowOverride ?? d?.contextWindow ?? 200_000,
|
|
178
|
-
maxTokens: maxOutputTokensOverride ?? d?.maxTokens ?? 32_000,
|
|
179
|
-
charsPerToken: d?.charsPerToken ?? 3,
|
|
180
|
-
...(isAnthropic ? {} : { compat: { maxTokensField: inferMaxTokensField(modelId) } }),
|
|
181
|
-
};
|
|
182
|
-
const brain = isAnthropic ? createAnthropicBrain({ apiKey }) : createOpenAIBrain({ apiKey });
|
|
183
|
-
return { model, brain, apiKeyEnv };
|
|
184
|
-
}
|
|
185
|
-
const T0 = Date.now();
|
|
186
|
-
const elapsed = () => `[+${((Date.now() - T0) / 1000).toFixed(1)}s]`;
|
|
187
|
-
const line = (s) => void process.stdout.write(`${elapsed()} ${s}\n`);
|
|
188
|
-
function clipLine(s, cap = 200) {
|
|
189
|
-
const one = s.replace(/\s*\n\s*/g, " ⏎ ");
|
|
190
|
-
return one.length > cap ? one.slice(0, cap) + `…(+${one.length - cap})` : one;
|
|
191
|
-
}
|
|
192
|
-
function summarizeArgs(args, cap = 220) {
|
|
193
|
-
let s;
|
|
194
|
-
try {
|
|
195
|
-
s = JSON.stringify(args) ?? "";
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
s = String(args);
|
|
199
|
-
}
|
|
200
|
-
s = s.replace(/\s*\n\s*/g, " ");
|
|
201
|
-
return s.length > cap ? s.slice(0, cap) + `…(+${s.length - cap})` : s;
|
|
202
|
-
}
|
|
203
|
-
class TextLineBuffer {
|
|
204
|
-
prefix;
|
|
205
|
-
buf = "";
|
|
206
|
-
constructor(prefix = "[text]") {
|
|
207
|
-
this.prefix = prefix;
|
|
208
|
-
}
|
|
209
|
-
push(delta) {
|
|
210
|
-
this.buf += delta;
|
|
211
|
-
let nl;
|
|
212
|
-
while ((nl = this.buf.indexOf("\n")) >= 0) {
|
|
213
|
-
const row = this.buf.slice(0, nl);
|
|
214
|
-
this.buf = this.buf.slice(nl + 1);
|
|
215
|
-
if (row.trim().length > 0)
|
|
216
|
-
line(`${this.prefix} ${row}`);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
flush() {
|
|
220
|
-
if (this.buf.trim().length > 0)
|
|
221
|
-
line(`${this.prefix} ${this.buf}`);
|
|
222
|
-
this.buf = "";
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
function intFromEnv(name, fallback) {
|
|
226
|
-
const raw = process.env[name];
|
|
227
|
-
if (!raw)
|
|
228
|
-
return fallback;
|
|
229
|
-
const n = Number.parseInt(raw, 10);
|
|
230
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
231
|
-
}
|
|
232
|
-
async function main() {
|
|
233
|
-
process.title = "sema-tb-agent";
|
|
234
|
-
const parsed = parseArgs(process.argv.slice(2));
|
|
235
|
-
if ("help" in parsed)
|
|
236
|
-
usage(0);
|
|
237
|
-
if ("usageError" in parsed) {
|
|
238
|
-
process.stderr.write(`sema-tb: ${parsed.usageError}\n`);
|
|
239
|
-
usage();
|
|
240
|
-
}
|
|
241
|
-
const args = parsed.args;
|
|
242
|
-
const resolved = resolveInstruction(args, (source) => readFileSync(source, "utf8"));
|
|
243
|
-
if ("error" in resolved) {
|
|
244
|
-
process.stderr.write(`sema-tb: ${resolved.error}\n`);
|
|
245
|
-
process.exit(2);
|
|
246
|
-
}
|
|
247
|
-
const instruction = resolved.instruction;
|
|
248
|
-
const { model, brain } = buildModelAndBrain(args.model);
|
|
249
|
-
const cwd = process.cwd();
|
|
250
|
-
const maxTurns = intFromEnv("TB_MAX_TURNS", 500);
|
|
251
|
-
const timeoutSec = intFromEnv("TB_TIMEOUT_SEC", 3000);
|
|
252
|
-
let jsonl;
|
|
253
|
-
if (args.jsonl) {
|
|
254
|
-
try {
|
|
255
|
-
jsonl = createWriteStream(args.jsonl, { flags: "w" });
|
|
256
|
-
jsonl.on("error", () => void (jsonl = undefined));
|
|
257
|
-
}
|
|
258
|
-
catch {
|
|
259
|
-
jsonl = undefined;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
const sink = (ch, payload) => {
|
|
263
|
-
if (!jsonl)
|
|
264
|
-
return;
|
|
265
|
-
try {
|
|
266
|
-
jsonl.write(`${JSON.stringify({ ch, tMs: Date.now() - T0, ...payload })}\n`);
|
|
267
|
-
}
|
|
268
|
-
catch {
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
line(`[init] model=${args.model} baseUrl=${model.baseUrl} cwd=${cwd} maxTurns=${maxTurns} timeoutSec=${timeoutSec} fullBody=${process.env.TB_FULLBODY !== "0"}${args.jsonl ? ` jsonl=${args.jsonl}` : ""}`);
|
|
272
|
-
let degradedTo;
|
|
273
|
-
let lastServedModel;
|
|
274
|
-
const tracer = (e) => {
|
|
275
|
-
sink("trace", e);
|
|
276
|
-
switch (e.kind) {
|
|
277
|
-
case "brain.call":
|
|
278
|
-
lastServedModel = e.model;
|
|
279
|
-
line(`[call] model=${e.model} in=${e.promptTokens} tot=${e.totalInputTokens} out=${e.completionTokens} cache=${e.cacheRead} latMs=${e.latencyMs}` +
|
|
280
|
-
(e.firstTokenMs !== undefined ? ` ttftMs=${e.firstTokenMs}` : "") +
|
|
281
|
-
(e.stopReason !== undefined ? ` stop=${e.stopReason}` : "") +
|
|
282
|
-
(e.callCap !== undefined ? ` cap=${e.callCap}${e.capThinkingSkipped ? "(think-skip)" : ""}` : "") +
|
|
283
|
-
(e.costMicroUsd !== undefined ? ` costMicroUsd=${e.costMicroUsd}` : ""));
|
|
284
|
-
break;
|
|
285
|
-
case "task.degraded":
|
|
286
|
-
degradedTo = e.to ?? "unknown";
|
|
287
|
-
line(`[degraded] ${summarizeArgs(e)}`);
|
|
288
|
-
break;
|
|
289
|
-
case "prompt.assembled":
|
|
290
|
-
line(`[prompt] constitution=${e.constitution} blocks=${e.blocks.map((b) => b.id).join(",")} totalChars=${e.totalChars}`);
|
|
291
|
-
break;
|
|
292
|
-
case "brain.failover":
|
|
293
|
-
line(`[failover] servedIndex=${e.servedIndex}/${e.total - 1}${e.errorCode !== undefined ? ` errorCode=${e.errorCode}` : ""}`);
|
|
294
|
-
break;
|
|
295
|
-
case "breaker.transition":
|
|
296
|
-
line(`[breaker] key=${e.key} phase=${e.phase} failures=${e.failures}`);
|
|
297
|
-
break;
|
|
298
|
-
case "brain.retry":
|
|
299
|
-
line(`[retry] attempt=${e.attempt} phase=${e.phase}`);
|
|
300
|
-
break;
|
|
301
|
-
case "loop.recovery":
|
|
302
|
-
line(`[recovery] ${e.reason}`);
|
|
303
|
-
break;
|
|
304
|
-
case "compaction.suppressed":
|
|
305
|
-
line(`[compaction-suppressed] est=${e.estTokens} floor=${e.floor}`);
|
|
306
|
-
break;
|
|
307
|
-
default:
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
const runner = new Runner({
|
|
312
|
-
brain,
|
|
313
|
-
executionEnv: new NodeExecutionEnv({ cwd, retainBackgroundProcesses: true }),
|
|
314
|
-
rootPath: cwd,
|
|
315
|
-
tracer,
|
|
316
|
-
});
|
|
317
|
-
const fullBody = process.env.TB_FULLBODY !== "0";
|
|
318
|
-
const stream = runner.runTaskStream({
|
|
319
|
-
objective: instruction,
|
|
320
|
-
model,
|
|
321
|
-
onAsk: "allow",
|
|
322
|
-
streamingToolExecution: true,
|
|
323
|
-
limits: {
|
|
324
|
-
maxTurns,
|
|
325
|
-
timeoutSec,
|
|
326
|
-
...(process.env.TB_CALLCAP === "0" ? { callCapByDeadline: false } : {}),
|
|
327
|
-
...(process.env.TB_FINALIZE === "0" ? { gracefulFinalize: false } : {}),
|
|
328
|
-
},
|
|
329
|
-
...(process.env.TB_FINALVERIFY === "0" ? {} : { finalVerification: true }),
|
|
330
|
-
...(process.env.TB_TODOREMIND === "0" && process.env.TB_BGTASKS !== "1"
|
|
331
|
-
? {}
|
|
332
|
-
: {
|
|
333
|
-
attachments: {
|
|
334
|
-
...(process.env.TB_TODOREMIND === "0" ? {} : { todoReminder: true }),
|
|
335
|
-
...(process.env.TB_TODOREMIND === "off" ? { todoReminderMode: "off" } : {}),
|
|
336
|
-
...(process.env.TB_BGTASKS === "1" ? { backgroundTasks: true } : {}),
|
|
337
|
-
},
|
|
338
|
-
}),
|
|
339
|
-
...(fullBody
|
|
340
|
-
? {
|
|
341
|
-
systemPrompt: CODE_SYSTEM_PROMPT,
|
|
342
|
-
tools: assembleCodeTools({
|
|
343
|
-
...(process.env.TB_SUMMARIZE === "0" ? {} : { webFetch: { summarize: createWebFetchSummarizer(brain, model) } }),
|
|
344
|
-
...(process.env.TB_TASKLIST === "0" || process.env.TB_TASKLIST === "false" ? { taskList: false } : {}),
|
|
345
|
-
}),
|
|
346
|
-
}
|
|
347
|
-
: {}),
|
|
348
|
-
});
|
|
349
|
-
const text = new TextLineBuffer();
|
|
350
|
-
const reason = new TextLineBuffer("[reason]");
|
|
351
|
-
let turns = 0;
|
|
352
|
-
try {
|
|
353
|
-
for await (const ev of stream) {
|
|
354
|
-
sink("event", ev);
|
|
355
|
-
switch (ev.type) {
|
|
356
|
-
case "text_delta":
|
|
357
|
-
text.push(ev.delta);
|
|
358
|
-
break;
|
|
359
|
-
case "reasoning_delta":
|
|
360
|
-
reason.push(ev.delta);
|
|
361
|
-
break;
|
|
362
|
-
case "tool_start":
|
|
363
|
-
text.flush();
|
|
364
|
-
reason.flush();
|
|
365
|
-
line(`[tool] ${ev.toolName} ${summarizeArgs(ev.args)}`);
|
|
366
|
-
break;
|
|
367
|
-
case "tool_end": {
|
|
368
|
-
const st = ev.structured;
|
|
369
|
-
let detail = "";
|
|
370
|
-
if (st?.type === "bash" && typeof st.exitCode === "number") {
|
|
371
|
-
detail = ` exit=${st.exitCode}`;
|
|
372
|
-
if (st.exitCode !== 0 && typeof st.stderr === "string" && st.stderr.trim()) {
|
|
373
|
-
detail += ` stderr=${JSON.stringify(clipLine(st.stderr, 160))}`;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
else if (ev.isError && ev.output !== undefined) {
|
|
377
|
-
detail = ` err=${JSON.stringify(clipLine(typeof ev.output === "string" ? ev.output : JSON.stringify(ev.output), 160))}`;
|
|
378
|
-
}
|
|
379
|
-
line(`[tool_end] ${ev.toolName} ${ev.isError ? "ERROR" : "ok"}${detail}`);
|
|
380
|
-
break;
|
|
381
|
-
}
|
|
382
|
-
case "steering_injected":
|
|
383
|
-
line(`[steer] source=${ev.source} ${JSON.stringify(clipLine(ev.preview, 160))}`);
|
|
384
|
-
break;
|
|
385
|
-
case "status":
|
|
386
|
-
line(`[status] ${summarizeArgs(ev, 160)}`);
|
|
387
|
-
break;
|
|
388
|
-
case "turn_end": {
|
|
389
|
-
text.flush();
|
|
390
|
-
reason.flush();
|
|
391
|
-
turns++;
|
|
392
|
-
const u = ev.usage;
|
|
393
|
-
line(`[turn] ${turns}${u ? ` in=${u.inputTokens} tot=${u.totalInputTokens} out=${u.outputTokens} cache=${u.cacheReadTokens}` : ""}` +
|
|
394
|
-
(ev.usageMissing ? " usageMissing=1" : "") +
|
|
395
|
-
(ev.stopReason !== undefined ? ` stop=${ev.stopReason}` : ""));
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
case "compacted":
|
|
399
|
-
line(`[compacted] trigger=${ev.trigger} tokensBefore=${ev.tokensBefore}` +
|
|
400
|
-
(ev.tokensAfter !== undefined ? ` tokensAfter=${ev.tokensAfter}` : "") +
|
|
401
|
-
(ev.durationMs !== undefined ? ` durationMs=${ev.durationMs}` : ""));
|
|
402
|
-
break;
|
|
403
|
-
default:
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
catch (e) {
|
|
409
|
-
process.stderr.write(`sema-tb: event stream error: ${e instanceof Error ? e.message : String(e)}\n`);
|
|
410
|
-
}
|
|
411
|
-
text.flush();
|
|
412
|
-
reason.flush();
|
|
413
|
-
const r = await stream.result();
|
|
414
|
-
const mech = r.stats.mechanisms;
|
|
415
|
-
line(`[done] status=${r.status} turns=${r.stats.turns} toolCalls=${r.stats.toolCalls ?? 0} ` +
|
|
416
|
-
`tokens=${r.stats.tokens} costMicroUsd=${r.stats.costMicroUsd ?? 0}` +
|
|
417
|
-
` model=${lastServedModel ?? model.id} degraded=${degradedTo ?? "no"}` +
|
|
418
|
-
(r.stats.cacheHitRate !== undefined ? ` cacheHitRate=${r.stats.cacheHitRate.toFixed(3)}` : "") +
|
|
419
|
-
(mech ? ` mech=${mech.finalizeInjected ? "finalize" : ""}${mech.nudgesSent ? `+nudges:${mech.nudgesSent}` : ""}${mech.capShrinks ? `+capShrinks:${mech.capShrinks}` : ""}${mech.callCutoffs ? `+callCutoffs:${mech.callCutoffs}` : ""}${mech.toolClamps ? `+toolClamps:${mech.toolClamps}` : ""}` : "") +
|
|
420
|
-
(r.errorCode ? ` errorCode=${r.errorCode}` : "") +
|
|
421
|
-
(r.errorMessage ? ` error=${JSON.stringify(r.errorMessage.slice(0, 300))}` : "") +
|
|
422
|
-
(r.blockedReason ? ` blocked=${JSON.stringify(r.blockedReason.slice(0, 300))}` : ""));
|
|
423
|
-
if (r.result)
|
|
424
|
-
for (const rl of r.result.split("\n"))
|
|
425
|
-
line(`[result] ${rl}`);
|
|
426
|
-
sink("event", { type: "done", status: r.status, stats: r.stats, errorCode: r.errorCode });
|
|
427
|
-
if (jsonl)
|
|
428
|
-
await new Promise((res) => jsonl.end(res));
|
|
429
|
-
runner.sessions.dispose();
|
|
430
|
-
process.exit(0);
|
|
431
|
-
}
|
|
432
|
-
function invokedAsScript() {
|
|
433
|
-
const argv1 = process.argv[1];
|
|
434
|
-
if (!argv1)
|
|
435
|
-
return true;
|
|
436
|
-
try {
|
|
437
|
-
return pathToFileURL(realpathSync(argv1)).href === import.meta.url;
|
|
438
|
-
}
|
|
439
|
-
catch {
|
|
440
|
-
return true;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (invokedAsScript()) {
|
|
444
|
-
main().catch((e) => {
|
|
445
|
-
process.stderr.write(`sema-tb: fatal: ${e instanceof Error ? (e.stack ?? e.message) : String(e)}\n`);
|
|
446
|
-
process.exit(1);
|
|
447
|
-
});
|
|
448
|
-
}
|
package/dist/bin/tb-env.d.ts
DELETED