@livx.cc/agentx 0.99.40 → 0.99.42
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/README.md +1 -0
- package/dist/{Agent-D4-CLnxC.d.ts → Agent-DydgLoT-.d.ts} +5 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +156 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +17 -5
- package/dist/index.js +177 -56
- package/dist/index.js.map +1 -1
- package/dist/{mcp-aONOXRNk.d.ts → mcp-D5eN0JyQ.d.ts} +1 -1
- package/dist/mcp.client.d.ts +2 -2
- package/dist/models.d.ts +15 -1
- package/dist/models.js +7 -0
- package/dist/models.js.map +1 -1
- package/dist/{tools-BWzPQVy0.d.ts → tools-S-ArIR3o.d.ts} +14 -2
- package/dist/tools.shell.d.ts +14 -2
- package/dist/tools.shell.js +80 -2
- package/dist/tools.shell.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { a as AgentOptions, H as Hooks, i as RunResult, A as Agent } from './Agent-
|
|
2
|
-
export { C as ChatFragment, b as CompactResult, D as DEFAULT_MUTATING, c as Decision, P as PermissionOptions, d as PermissionPolicy, e as PermissionRule, f as PreToolUseDecision, R as ReasoningEffort, g as RecordingHooks, h as RecordingLifecycle, T as ToolUse, j as ToolUseMeta, k as composeHooks, l as estimateTokens, p as planMode, r as reasoningToChatFragment } from './Agent-
|
|
1
|
+
import { a as AgentOptions, H as Hooks, i as RunResult, A as Agent } from './Agent-DydgLoT-.js';
|
|
2
|
+
export { C as ChatFragment, b as CompactResult, D as DEFAULT_MUTATING, c as Decision, P as PermissionOptions, d as PermissionPolicy, e as PermissionRule, f as PreToolUseDecision, R as ReasoningEffort, g as RecordingHooks, h as RecordingLifecycle, T as ToolUse, j as ToolUseMeta, k as composeHooks, l as estimateTokens, p as planMode, r as reasoningToChatFragment } from './Agent-DydgLoT-.js';
|
|
3
3
|
export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
|
|
4
4
|
import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
|
|
5
5
|
export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
|
|
6
6
|
import { BodDB } from '@bod.ee/db';
|
|
7
|
-
import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, j as ToolCall, H as HostBridge, U as UserQuestion, g as MessageContent } from './tools-
|
|
8
|
-
export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, M as Message, R as Role, S as SandboxJobRegistry, h as StreamChunk, T as TodoItem, i as Tool, k as ToolContext, l as bashTool, m as clearImageRefs, n as contentBytes, o as contentText, p as defaultTools, q as editTool, r as elideImages, s as elideInlineImages, t as elideStaleImages, u as exitSessionTool, v as expandImagesForSend, w as imagePart, x as imageRefPart, y as imageRefResult, z as makeContext, B as makeJobTools, D as messageHasImage, E as messageHasImageRef, F as messageHasInlineImage, G as mintImageRef, J as parseImageRef, K as readTool, L as remintImageRefs, N as sendBytes, O as sendBytesPer, P as toWireTools, Q as todoWriteTool, V as toolImageRef, W as toolRegistry, X as toolsByName } from './tools-
|
|
7
|
+
import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, j as ToolCall, H as HostBridge, U as UserQuestion, g as MessageContent } from './tools-S-ArIR3o.js';
|
|
8
|
+
export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, M as Message, R as Role, S as SandboxJobRegistry, h as StreamChunk, T as TodoItem, i as Tool, k as ToolContext, l as bashTool, m as clearImageRefs, n as contentBytes, o as contentText, p as defaultTools, q as editTool, r as elideImages, s as elideInlineImages, t as elideStaleImages, u as exitSessionTool, v as expandImagesForSend, w as imagePart, x as imageRefPart, y as imageRefResult, z as makeContext, B as makeJobTools, D as messageHasImage, E as messageHasImageRef, F as messageHasInlineImage, G as mintImageRef, J as parseImageRef, K as readTool, L as remintImageRefs, N as sendBytes, O as sendBytesPer, P as toWireTools, Q as todoWriteTool, V as toolImageRef, W as toolRegistry, X as toolsByName } from './tools-S-ArIR3o.js';
|
|
9
9
|
import { ShellJobRegistry } from './tools.shell.js';
|
|
10
10
|
import { SpokenSplitter } from '@bod.ee/voice';
|
|
11
11
|
export { AudioSink, AudioSource, AuthProvider, CartesiaTTS, CartesiaTTSOptions, STT_SAMPLE_RATE, SonioxSTT, SonioxSTTOptions, SttLike, TTS_SAMPLE_RATE, TtsLike, VoiceEngine, VoiceEngineOptions, VoiceState, resolveAuth } from '@bod.ee/voice';
|
|
12
|
-
export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-
|
|
12
|
+
export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-D5eN0JyQ.js';
|
|
13
13
|
import * as libx_js_src_modules_log from 'libx.js/src/modules/log';
|
|
14
14
|
export { log } from 'libx.js/src/modules/log';
|
|
15
15
|
|
|
@@ -1286,6 +1286,18 @@ interface TaskToolOptions {
|
|
|
1286
1286
|
* (the child's signal is aborted) and therefore honestly declarable as the tool's `maxDurationMs`.
|
|
1287
1287
|
*/
|
|
1288
1288
|
maxDurationMs?: number;
|
|
1289
|
+
/** The parent's toolbelt — the child INHERITS it by default (otherwise it falls back to the jailed
|
|
1290
|
+
* `defaultTools()` and loses everything the host injected: a real shell, Write/Grep, MCP tools).
|
|
1291
|
+
* An agentType def's `tools` allowlist still narrows it (and can name one of these by name). */
|
|
1292
|
+
tools?: AgentTool[];
|
|
1293
|
+
/** The parent's system prompt — inherited by a plain (untyped) child so it keeps the host's persona
|
|
1294
|
+
* instead of reverting to agentx's generic built-in one. An agentType def's body overrides it. */
|
|
1295
|
+
systemPrompt?: string;
|
|
1296
|
+
/** Provider-specific options for the CHILD's model, recomputed per child (IoC — same seam duplex uses
|
|
1297
|
+
* for its workers). The child's model may differ from the parent's (explicit `model` / agentType def),
|
|
1298
|
+
* and provider options are NOT portable across providers (cursor `cwd`/`cursorSession` on anthropic
|
|
1299
|
+
* is a hard 400), so they are derived, never blindly inherited. */
|
|
1300
|
+
providerOptionsFor?: (model: string) => Record<string, unknown> | undefined;
|
|
1289
1301
|
/** Origin-anchored real-disk dir for child transcript traces (see `AgentOptions.traceDir`). Passed
|
|
1290
1302
|
* down UNCHANGED to nested children so every level writes to the same origin `.agent`. */
|
|
1291
1303
|
traceDir?: string;
|
package/dist/index.js
CHANGED
|
@@ -1363,11 +1363,12 @@ function toolRegistry() {
|
|
|
1363
1363
|
const all = [bashTool, readTool, editTool, grepTool, globTool, writeTool, multiEditTool, applyEditsTool, repoMapTool, reviewTool(), todoWriteTool, webFetchTool, webSearchTool, webSearchAnthropicTool];
|
|
1364
1364
|
return Object.fromEntries(all.map((t) => [t.name, t]));
|
|
1365
1365
|
}
|
|
1366
|
-
function toolsByName(names) {
|
|
1366
|
+
function toolsByName(names, available) {
|
|
1367
1367
|
const reg = toolRegistry();
|
|
1368
|
+
const extra = Object.fromEntries((available ?? []).map((t) => [t.name, t]));
|
|
1368
1369
|
return names.map((n) => {
|
|
1369
|
-
const t = reg[n];
|
|
1370
|
-
if (!t) throw new Error(`unknown tool '${n}'. Known: ${Object.keys(reg).join(", ")}`);
|
|
1370
|
+
const t = extra[n] ?? reg[n];
|
|
1371
|
+
if (!t) throw new Error(`unknown tool '${n}'. Known: ${[.../* @__PURE__ */ new Set([...Object.keys(extra), ...Object.keys(reg)])].join(", ")}`);
|
|
1371
1372
|
return t;
|
|
1372
1373
|
});
|
|
1373
1374
|
}
|
|
@@ -1995,8 +1996,26 @@ function makeRealShellTool(options) {
|
|
|
1995
1996
|
};
|
|
1996
1997
|
return {
|
|
1997
1998
|
name: "Shell",
|
|
1999
|
+
// Rebind for an isolated child agent (git worktree): same policy/env/timeouts, new cwd. The
|
|
2000
|
+
// `registry` is deliberately dropped — it is bound to the PARENT's cwd (and, in hosts like
|
|
2001
|
+
// shraga-ee, to the parent's session), so a background job started from the child would run
|
|
2002
|
+
// outside the child's isolation and report into the parent. Background stays a parent capability
|
|
2003
|
+
// (`makeShellJobTools`' companions drop out of an isolated child entirely — see their `withCwd`).
|
|
2004
|
+
// Only `run` is rebuilt: everything else is carried over from THIS instance, because a host may
|
|
2005
|
+
// have mutated the tool AFTER construction (shraga-ee renames `Shell` -> `Bash` and its system
|
|
2006
|
+
// prompt says `Bash` everywhere). Rebuilding from `options` alone silently dropped that rename,
|
|
2007
|
+
// so a worktree child advertised `Shell` while being told to call `Bash` — an unknown-tool error
|
|
2008
|
+
// on its first call, and a hard failure for an agentType def whose allowlist names `Bash`.
|
|
2009
|
+
// `description` is the ONE thing taken from the rebound tool instead: it is derived from the
|
|
2010
|
+
// options and states whether `background:true` works — the rebind drops the registry, so carrying
|
|
2011
|
+
// the parent's text over would advertise a background capability the child does not have. (The
|
|
2012
|
+
// spread also collapses the getter below to a plain value, which is why this override is explicit.)
|
|
2013
|
+
withCwd(cwd) {
|
|
2014
|
+
const rebound = makeRealShellTool({ ...options, cwd, registry: void 0 });
|
|
2015
|
+
return { ...this, description: rebound.description, run: rebound.run, withCwd: rebound.withCwd };
|
|
2016
|
+
},
|
|
1998
2017
|
get description() {
|
|
1999
|
-
return
|
|
2018
|
+
return "Run a shell command via /bin/sh in the working directory. Executes any installed binary \u2014 ls, cat, grep, git, bun, node, curl, scripts, etc. Returns combined stdout+stderr; non-zero exits are prefixed `[exit N]`. Runs non-interactively with no terminal (stdin is /dev/null): commands that prompt for input fail fast rather than hang \u2014 for privileged actions use a non-interactive flag (e.g. `sudo -n`), or ask the user to run the command themselves. " + (options.registry?.adopt ? `A command still running after ${defaultTimeoutMs}ms is NOT killed \u2014 it is handed to a background job and you get its id, so you can keep checking on it (\`[still running]\`). Pass \`timeoutMs\` (max ${maxTimeoutMs}) to wait longer in the foreground, and always bound network commands yourself (e.g. \`curl -m 10\`). ` : `Each command is killed after ${defaultTimeoutMs}ms (result \`[exit 124]\` with whatever output it produced) \u2014 pass \`timeoutMs\` (max ${maxTimeoutMs}) for a legitimately slower command, and always bound network commands yourself (e.g. \`curl -m 10\`). `) + backgroundDoc();
|
|
2000
2019
|
},
|
|
2001
2020
|
// Declared so transports above the agent loop can size their own deadline with headroom (see AgentTool.maxDurationMs).
|
|
2002
2021
|
maxDurationMs: maxTimeoutMs,
|
|
@@ -2035,9 +2054,12 @@ function makeRealShellTool(options) {
|
|
|
2035
2054
|
else ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
2036
2055
|
}
|
|
2037
2056
|
let timedOut = false;
|
|
2038
|
-
|
|
2057
|
+
let onDeadline = () => {
|
|
2039
2058
|
timedOut = true;
|
|
2040
2059
|
ctl.abort();
|
|
2060
|
+
};
|
|
2061
|
+
const timer = setTimeout(() => {
|
|
2062
|
+
onDeadline();
|
|
2041
2063
|
}, timeoutMs);
|
|
2042
2064
|
let pend = "";
|
|
2043
2065
|
let flushTimer = null;
|
|
@@ -2068,6 +2090,18 @@ function makeRealShellTool(options) {
|
|
|
2068
2090
|
}
|
|
2069
2091
|
if (ctl.signal.aborted) killGroup(proc, "SIGKILL");
|
|
2070
2092
|
else ctl.signal.addEventListener("abort", () => killGroup(proc, "SIGKILL"), { once: true });
|
|
2093
|
+
const registry = options.registry;
|
|
2094
|
+
if (registry?.adopt) {
|
|
2095
|
+
onDeadline = () => {
|
|
2096
|
+
timedOut = true;
|
|
2097
|
+
if (settled) return;
|
|
2098
|
+
flushEmit(ctx);
|
|
2099
|
+
proc.stdout?.off?.("data", collect);
|
|
2100
|
+
proc.stderr?.off?.("data", collect);
|
|
2101
|
+
const id = registry.adopt(cmd, proc, out);
|
|
2102
|
+
finish(handoffFor(timeoutMs, id, clean(out), !!registry.notifiesOnExit));
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2071
2105
|
const collect = (chunk) => {
|
|
2072
2106
|
const s = typeof chunk === "string" ? chunk : chunk?.toString?.("utf8") ?? "";
|
|
2073
2107
|
out += s;
|
|
@@ -2100,6 +2134,12 @@ function makeRealShellTool(options) {
|
|
|
2100
2134
|
}
|
|
2101
2135
|
};
|
|
2102
2136
|
}
|
|
2137
|
+
function handoffFor(timeoutMs, id, body, notifies) {
|
|
2138
|
+
const head = `[still running] exceeded ${timeoutMs}ms, so it was handed to background job ${id} \u2014 NOT killed, it is still going. Check on it with ShellOutput({id:"${id}"}) / ShellStatus({id:"${id}"}), stop it with ShellKill({id:"${id}"}). ` + (notifies ? "Its completion will be reported to you, so you may continue with other work meanwhile." : "Nothing will tell you when it finishes \u2014 poll it yourself before you rely on its result.");
|
|
2139
|
+
return body ? `${head}
|
|
2140
|
+
Output so far:
|
|
2141
|
+
${body}` : head;
|
|
2142
|
+
}
|
|
2103
2143
|
function reasonFor(timedOut, timeoutMs, body) {
|
|
2104
2144
|
const head = timedOut ? `[exit 124] timed out after ${timeoutMs}ms (killed). Re-running this command unchanged will time out again. Either re-run it with background:true (returns a job id immediately; poll with ShellOutput/ShellStatus, stop with ShellKill) or narrow it so it can finish \u2014 bound it, scope it, or ask for less.` : "[exit 130] cancelled (killed)";
|
|
2105
2145
|
return body ? `${head}
|
|
@@ -2108,8 +2148,10 @@ ${body}` : head;
|
|
|
2108
2148
|
}
|
|
2109
2149
|
function makeShellJobTools(registry) {
|
|
2110
2150
|
const idParam = { type: "object", properties: { id: { type: "string", description: "the job id from Shell({background:true})" } } };
|
|
2151
|
+
const dropOnRebind = { withCwd: () => void 0 };
|
|
2111
2152
|
return [
|
|
2112
2153
|
{
|
|
2154
|
+
...dropOnRebind,
|
|
2113
2155
|
name: "ShellOutput",
|
|
2114
2156
|
description: "Read the accumulated output (tail) of a background Shell job by id.",
|
|
2115
2157
|
parameters: { type: "object", required: ["id"], properties: { id: { type: "string" } } },
|
|
@@ -2122,6 +2164,7 @@ ${clean(out) || "(no output yet)"}`;
|
|
|
2122
2164
|
}
|
|
2123
2165
|
},
|
|
2124
2166
|
{
|
|
2167
|
+
...dropOnRebind,
|
|
2125
2168
|
name: "ShellStatus",
|
|
2126
2169
|
description: "Status of a background Shell job (running/exited/killed + exit code). Omit `id` to list all jobs.",
|
|
2127
2170
|
parameters: idParam,
|
|
@@ -2135,6 +2178,7 @@ ${clean(out) || "(no output yet)"}`;
|
|
|
2135
2178
|
}
|
|
2136
2179
|
},
|
|
2137
2180
|
{
|
|
2181
|
+
...dropOnRebind,
|
|
2138
2182
|
name: "ShellKill",
|
|
2139
2183
|
description: "Stop a running background Shell job by id (SIGTERM).",
|
|
2140
2184
|
parameters: { type: "object", required: ["id"], properties: { id: { type: "string" } } },
|
|
@@ -2235,6 +2279,41 @@ var init_tools_shell = __esm({
|
|
|
2235
2279
|
list() {
|
|
2236
2280
|
return [...this.jobs].map(([id, j]) => ({ id, command: j.command, status: j.status }));
|
|
2237
2281
|
}
|
|
2282
|
+
/**
|
|
2283
|
+
* Take over an ALREADY-RUNNING child as a background job. A foreground command that outruns its
|
|
2284
|
+
* timeout is not necessarily a hung command — killing it throws away work that was nearly done and,
|
|
2285
|
+
* worse, a launcher that spawned its own detached worker leaves that worker running with nothing
|
|
2286
|
+
* tracking it. Adopting hands the model a handle instead: the command keeps going, its completion
|
|
2287
|
+
* is reported like any other job, and `seed` carries the output produced before the handoff.
|
|
2288
|
+
*/
|
|
2289
|
+
adopt(command, proc, seed = "") {
|
|
2290
|
+
const id = `job-${++this.seq}`;
|
|
2291
|
+
const max = this.cfg.maxBuffer ?? 256 * 1024;
|
|
2292
|
+
const job = { command, buf: seed.slice(-max), status: "running", proc };
|
|
2293
|
+
const append = (chunk) => {
|
|
2294
|
+
const s = typeof chunk === "string" ? chunk : chunk?.toString?.("utf8") ?? "";
|
|
2295
|
+
job.buf = (job.buf + s).slice(-max);
|
|
2296
|
+
};
|
|
2297
|
+
proc.stdout?.on("data", append);
|
|
2298
|
+
proc.stderr?.on("data", append);
|
|
2299
|
+
proc.on("error", (err) => {
|
|
2300
|
+
if (job.status === "running") {
|
|
2301
|
+
job.status = "error";
|
|
2302
|
+
append(`
|
|
2303
|
+
[error] ${err?.message ?? err}`);
|
|
2304
|
+
this.notifyExit(id, job);
|
|
2305
|
+
}
|
|
2306
|
+
});
|
|
2307
|
+
proc.on("close", (code) => {
|
|
2308
|
+
if (job.status === "running") {
|
|
2309
|
+
job.status = "exited";
|
|
2310
|
+
job.exitCode = code ?? void 0;
|
|
2311
|
+
this.notifyExit(id, job);
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
this.jobs.set(id, job);
|
|
2315
|
+
return id;
|
|
2316
|
+
}
|
|
2238
2317
|
kill(id) {
|
|
2239
2318
|
const j = this.jobs.get(id);
|
|
2240
2319
|
if (!j) return false;
|
|
@@ -2946,6 +3025,53 @@ ${sections.join("\n\n---\n\n")}`;
|
|
|
2946
3025
|
init_tools();
|
|
2947
3026
|
import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
2948
3027
|
import { join as join3 } from "path";
|
|
3028
|
+
|
|
3029
|
+
// src/models.ts
|
|
3030
|
+
var MODEL_ALIASES = {
|
|
3031
|
+
fable: "claude-fable-5",
|
|
3032
|
+
"fable-5": "claude-fable-5",
|
|
3033
|
+
opus: "claude-opus-4-8",
|
|
3034
|
+
"opus-4-8": "claude-opus-4-8",
|
|
3035
|
+
"opus-4-7": "claude-opus-4-7",
|
|
3036
|
+
"opus-4-6": "claude-opus-4-6",
|
|
3037
|
+
sonnet: "claude-sonnet-4-6",
|
|
3038
|
+
haiku: "claude-haiku-4-5-20251001"
|
|
3039
|
+
};
|
|
3040
|
+
function resolveModelAlias(input) {
|
|
3041
|
+
const slash = input.indexOf("/");
|
|
3042
|
+
const prefix = slash === -1 ? "" : input.slice(0, slash + 1);
|
|
3043
|
+
const rest = slash === -1 ? input : input.slice(slash + 1);
|
|
3044
|
+
return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);
|
|
3045
|
+
}
|
|
3046
|
+
function modelProvider(model) {
|
|
3047
|
+
const i = model.indexOf("/");
|
|
3048
|
+
if (i !== -1) return model.slice(0, i);
|
|
3049
|
+
if (/^claude-/i.test(model)) return "anthropic";
|
|
3050
|
+
return `?${model}`;
|
|
3051
|
+
}
|
|
3052
|
+
function modelShortLabel(model) {
|
|
3053
|
+
if (!model) return "default";
|
|
3054
|
+
const id = model.split("/").pop();
|
|
3055
|
+
return id.replace(/^claude-/, "").replace(/-\d.*$/, "");
|
|
3056
|
+
}
|
|
3057
|
+
function resolveModelSwitch(opts) {
|
|
3058
|
+
const { requested, current, prior, allowed } = opts;
|
|
3059
|
+
if (!requested) return { model: current, denied: false };
|
|
3060
|
+
if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {
|
|
3061
|
+
return { model: current, denied: true };
|
|
3062
|
+
}
|
|
3063
|
+
const fmt = opts.format ?? ((from, to) => `_[switching from ${from} to ${to}]_
|
|
3064
|
+
|
|
3065
|
+
`);
|
|
3066
|
+
const changed = prior !== void 0 && modelShortLabel(prior) !== modelShortLabel(requested);
|
|
3067
|
+
return {
|
|
3068
|
+
model: requested,
|
|
3069
|
+
denied: false,
|
|
3070
|
+
notice: changed ? fmt(modelShortLabel(prior), modelShortLabel(requested)) : void 0
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
// src/subagent.ts
|
|
2949
3075
|
init_OverlayFilesystem();
|
|
2950
3076
|
|
|
2951
3077
|
// src/worktree.ts
|
|
@@ -3154,6 +3280,14 @@ function createWorktreeFs(cwd, label, index) {
|
|
|
3154
3280
|
function releaseWorktree(handle) {
|
|
3155
3281
|
return cleanupWorktree(handle.repoRoot, handle.slug, { force: true });
|
|
3156
3282
|
}
|
|
3283
|
+
function rebindTools(tools, root) {
|
|
3284
|
+
if (!tools || !root) return tools;
|
|
3285
|
+
return tools.flatMap((t) => {
|
|
3286
|
+
if (!t.withCwd) return [t];
|
|
3287
|
+
const rebound = t.withCwd(root);
|
|
3288
|
+
return rebound ? [rebound] : [];
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3157
3291
|
function worktreePromptPrefix(branch) {
|
|
3158
3292
|
return `You are running in an isolated git worktree on branch "${branch}". When you finish making changes, commit them with a clear message before replying with your summary. Your worktree will be removed after you finish \u2014 only committed work survives (on the branch).
|
|
3159
3293
|
|
|
@@ -3221,22 +3355,30 @@ async function runChildTraced(args) {
|
|
|
3221
3355
|
await opts.hooks?.onSubagentStop?.(result, { label, agentType });
|
|
3222
3356
|
return { res, result };
|
|
3223
3357
|
}
|
|
3224
|
-
function childOptionsFor(opts, fs, depth, maxDepth, agentType) {
|
|
3358
|
+
function childOptionsFor(opts, fs, depth, maxDepth, agentType, model, root) {
|
|
3225
3359
|
let def;
|
|
3226
3360
|
if (agentType) {
|
|
3227
3361
|
def = (opts.agents ?? []).find((a) => a.name === agentType);
|
|
3228
3362
|
if (!def) return `no subagent type '${agentType}'. Available: ${(opts.agents ?? []).map((a) => a.name).join(", ") || "(none defined)"}`;
|
|
3229
3363
|
}
|
|
3230
|
-
const
|
|
3364
|
+
const childModel = resolveModelAlias(model || def?.model || opts.model);
|
|
3365
|
+
const childOpts = { ai: opts.ai, fs, model: childModel, subagents: true, depth: depth + 1, maxDepth, traceDir: opts.traceDir };
|
|
3231
3366
|
if (opts.maxSteps != null) childOpts.maxSteps = opts.maxSteps;
|
|
3232
|
-
|
|
3367
|
+
const childPrompt = def?.systemPrompt ?? opts.systemPrompt;
|
|
3368
|
+
if (childPrompt) childOpts.systemPrompt = childPrompt;
|
|
3369
|
+
const inherited = rebindTools(opts.tools, root);
|
|
3233
3370
|
if (def?.tools?.length) {
|
|
3234
3371
|
try {
|
|
3235
|
-
childOpts.tools = toolsByName(def.tools);
|
|
3372
|
+
childOpts.tools = toolsByName(def.tools, inherited);
|
|
3236
3373
|
} catch (e) {
|
|
3237
3374
|
return `subagent '${agentType}' declares an unknown tool \u2014 ${e.message}`;
|
|
3238
3375
|
}
|
|
3376
|
+
} else if (inherited?.length) {
|
|
3377
|
+
childOpts.tools = inherited;
|
|
3239
3378
|
}
|
|
3379
|
+
const po = opts.providerOptionsFor?.(childModel);
|
|
3380
|
+
if (po) childOpts.providerOptions = po;
|
|
3381
|
+
if (opts.hooks?.preToolUse) childOpts.hooks = { preToolUse: opts.hooks.preToolUse.bind(opts.hooks) };
|
|
3240
3382
|
return childOpts;
|
|
3241
3383
|
}
|
|
3242
3384
|
function makeTaskTool(opts) {
|
|
@@ -3244,7 +3386,7 @@ function makeTaskTool(opts) {
|
|
|
3244
3386
|
const maxDepth = opts.maxDepth ?? 2;
|
|
3245
3387
|
return {
|
|
3246
3388
|
name: "Task",
|
|
3247
|
-
description: 'Delegate a self-contained sub-task to a child agent over the same filesystem. It runs autonomously with its own step budget and a wall-clock deadline, and returns a concise summary \u2014 use to isolate context-heavy work (broad search, a scoped refactor). Provide a short `description` and a full `prompt`. If it is interrupted or fails, the result is a STATUS HINT with a pointer to its full transcript (Read it to recover what it did, then decide whether to continue or restart) \u2014 do not assume an incomplete sub-task succeeded. Set `background: true` to run it detached (returns a job id to poll with JobOutput while you keep working); its file edits are overlay-isolated and commit when it finishes
|
|
3389
|
+
description: 'Delegate a self-contained sub-task to a child agent over the same filesystem. It runs autonomously with its own step budget and a wall-clock deadline, and returns a concise summary \u2014 use to isolate context-heavy work (broad search, a scoped refactor). Provide a short `description` and a full `prompt`. If it is interrupted or fails, the result is a STATUS HINT with a pointer to its full transcript (Read it to recover what it did, then decide whether to continue or restart) \u2014 do not assume an incomplete sub-task succeeded. Set `background: true` to run it detached (returns a job id to poll with JobOutput while you keep working); its VFS file edits are overlay-isolated and commit when it finishes \u2014 but a child that uses the real `Shell` writes straight to the working tree (an overlay cannot isolate a real process), so pair shell-heavy work with `isolation: "worktree"`. Set `isolation: "worktree"` to run in a real git worktree (needed when the child uses real shell commands that must see/modify actual files \u2014 its shell is re-bound to the worktree \u2014 keeping any name your host gave it \u2014 and background shell jobs, including inspecting or killing MINE, are unavailable there) \u2014 slower (~200ms setup), auto-cleaned if no changes. The child inherits YOUR tools and system prompt by default (and is subject to the same tool-use gate); pass `model` to run this one sub-task on a different model (e.g. a cheap one for bulk search, a stronger one for hard reasoning) \u2014 it overrides the agentType default.',
|
|
3248
3390
|
parameters: {
|
|
3249
3391
|
type: "object",
|
|
3250
3392
|
required: ["description", "prompt"],
|
|
@@ -3252,6 +3394,7 @@ function makeTaskTool(opts) {
|
|
|
3252
3394
|
description: { type: "string", description: "a short (3-5 word) label for the sub-task" },
|
|
3253
3395
|
prompt: { type: "string", description: "the full instructions the child agent should carry out" },
|
|
3254
3396
|
agentType: { type: "string", description: "optional named subagent type (its persona, model, and scoped tools) \u2014 see the catalog" },
|
|
3397
|
+
model: { type: "string", description: 'optional model for THIS sub-task (alias like "haiku"/"opus"/"sonnet", or a full id). Overrides the agentType default; otherwise the child runs on your model.' },
|
|
3255
3398
|
background: { type: "boolean", description: "run detached (overlay-isolated, commits on finish); returns a job id to poll with JobOutput instead of blocking" },
|
|
3256
3399
|
isolation: { type: "string", enum: ["overlay", "worktree"], description: 'isolation mode: "overlay" (default, VFS layer) or "worktree" (real git worktree for shell-heavy work)' }
|
|
3257
3400
|
}
|
|
@@ -3262,7 +3405,7 @@ function makeTaskTool(opts) {
|
|
|
3262
3405
|
maxDurationMs: opts.maxDurationMs ?? SUBAGENT_MAX_DURATION_MS,
|
|
3263
3406
|
maxDurationEnforced: true,
|
|
3264
3407
|
// the wall clock below ABORTS the child at this bound — it cannot be overrun, so the transport needs slack, not a doubling
|
|
3265
|
-
async run({ description, prompt, agentType, background, isolation }, ctx) {
|
|
3408
|
+
async run({ description, prompt, agentType, model, background, isolation }, ctx) {
|
|
3266
3409
|
if (depth >= maxDepth) {
|
|
3267
3410
|
return `Error: Task depth limit reached (maxDepth ${maxDepth}). Cannot spawn another child agent \u2014 do this work directly instead.`;
|
|
3268
3411
|
}
|
|
@@ -3285,7 +3428,7 @@ function makeTaskTool(opts) {
|
|
|
3285
3428
|
fs2 = overlay;
|
|
3286
3429
|
commit = () => overlay.commit();
|
|
3287
3430
|
}
|
|
3288
|
-
const childOpts2 = childOptionsFor(opts, fs2, depth, maxDepth, agentType);
|
|
3431
|
+
const childOpts2 = childOptionsFor(opts, fs2, depth, maxDepth, agentType, model, wtHandle2?.worktreePath);
|
|
3289
3432
|
if (typeof childOpts2 === "string") throw new Error(childOpts2);
|
|
3290
3433
|
const childPrompt = wtHandle2 ? worktreePromptPrefix(wtHandle2.branch) + String(prompt ?? "") : String(prompt ?? "");
|
|
3291
3434
|
const { res, result } = await runChildTraced({ opts, childOpts: childOpts2, prompt: childPrompt, label, agentType, childDepth: depth + 1, signal });
|
|
@@ -3315,7 +3458,7 @@ function makeTaskTool(opts) {
|
|
|
3315
3458
|
} else {
|
|
3316
3459
|
fs = opts.fs;
|
|
3317
3460
|
}
|
|
3318
|
-
const childOpts = childOptionsFor(opts, fs, depth, maxDepth, agentType);
|
|
3461
|
+
const childOpts = childOptionsFor(opts, fs, depth, maxDepth, agentType, model, wtHandle?.worktreePath);
|
|
3319
3462
|
if (typeof childOpts === "string") {
|
|
3320
3463
|
wtHandle && releaseWorktree(wtHandle);
|
|
3321
3464
|
return `Error: ${childOpts}`;
|
|
@@ -3344,7 +3487,7 @@ function makeTaskBatchTool(opts) {
|
|
|
3344
3487
|
const maxParallel = opts.maxParallel ?? 4;
|
|
3345
3488
|
return {
|
|
3346
3489
|
name: "TaskBatch",
|
|
3347
|
-
description: 'Delegate SEVERAL independent sub-tasks to child agents that run concurrently; returns all their summaries. Each child is write-isolated (its file edits are merged back in array order). Use for parallel fan-out (review/search/scoped refactors across files). Provide `tasks: [{ description, prompt, agentType? }]
|
|
3490
|
+
description: 'Delegate SEVERAL independent sub-tasks to child agents that run concurrently; returns all their summaries. Each child is write-isolated at the VFS level (its file edits are merged back in array order); real `Shell` commands are NOT \u2014 use `isolation: "worktree"` for those. Use for parallel fan-out (review/search/scoped refactors across files). Provide `tasks: [{ description, prompt, agentType?, model? }]` \u2014 each child inherits your tools and system prompt, and `model` runs that one child on a different model. Set `isolation: "worktree"` to run each child in its own git worktree (needed for real shell commands) \u2014 slower, auto-cleaned if no changes.',
|
|
3348
3491
|
parameters: {
|
|
3349
3492
|
type: "object",
|
|
3350
3493
|
required: ["tasks"],
|
|
@@ -3358,7 +3501,8 @@ function makeTaskBatchTool(opts) {
|
|
|
3358
3501
|
properties: {
|
|
3359
3502
|
description: { type: "string", description: "a short (3-5 word) label" },
|
|
3360
3503
|
prompt: { type: "string", description: "the full instructions for this child" },
|
|
3361
|
-
agentType: { type: "string", description: "optional named subagent type" }
|
|
3504
|
+
agentType: { type: "string", description: "optional named subagent type" },
|
|
3505
|
+
model: { type: "string", description: "optional model for this child (alias or full id); overrides the agentType default" }
|
|
3362
3506
|
}
|
|
3363
3507
|
}
|
|
3364
3508
|
},
|
|
@@ -3391,7 +3535,7 @@ function makeTaskBatchTool(opts) {
|
|
|
3391
3535
|
overlay = new OverlayFilesystem(opts.fs);
|
|
3392
3536
|
fs = overlay;
|
|
3393
3537
|
}
|
|
3394
|
-
const childOpts = childOptionsFor(opts, fs, depth, maxDepth, t?.agentType);
|
|
3538
|
+
const childOpts = childOptionsFor(opts, fs, depth, maxDepth, t?.agentType, t?.model, wtHandle?.worktreePath);
|
|
3395
3539
|
if (typeof childOpts === "string") return { label, error: childOpts, ok: false };
|
|
3396
3540
|
try {
|
|
3397
3541
|
const childPrompt = wtHandle ? worktreePromptPrefix(wtHandle.branch) + String(t?.prompt ?? "") : String(t?.prompt ?? "");
|
|
@@ -3818,6 +3962,10 @@ var AgentOptions = class {
|
|
|
3818
3962
|
autoTest;
|
|
3819
3963
|
/** Provider-specific options forwarded to ai.chat() (e.g. cursor mcpServers, cwd). */
|
|
3820
3964
|
providerOptions;
|
|
3965
|
+
/** Derive `providerOptions` for a DELEGATED child whose model may differ from this agent's
|
|
3966
|
+
* (`Task({ model })` / an agentType def). Default: inherit `providerOptions` only when the child's
|
|
3967
|
+
* model is on the SAME provider — cursor's `cwd`/`cursorSession` on an anthropic child is a 400. */
|
|
3968
|
+
providerOptionsFor;
|
|
3821
3969
|
/** Prompt caching (providers that support it, e.g. Anthropic): cache tools/system/conversation
|
|
3822
3970
|
* prefix across the loop's steps — reads cost 0.1x, writes 1.25x. A multi-step agent loop
|
|
3823
3971
|
* re-sends its whole prefix every step, so this is a large net cost cut. Default on. */
|
|
@@ -4041,7 +4189,19 @@ var Agent = class _Agent {
|
|
|
4041
4189
|
agents = loaded.agents;
|
|
4042
4190
|
if (loaded.catalog) systemPrompt += "\n\n" + loaded.catalog;
|
|
4043
4191
|
}
|
|
4044
|
-
const taskOpts = {
|
|
4192
|
+
const taskOpts = {
|
|
4193
|
+
ai: o.ai,
|
|
4194
|
+
model: o.model,
|
|
4195
|
+
fs,
|
|
4196
|
+
depth: o.depth,
|
|
4197
|
+
maxDepth: o.maxDepth,
|
|
4198
|
+
agents,
|
|
4199
|
+
hooks: o.hooks,
|
|
4200
|
+
traceDir: o.traceDir,
|
|
4201
|
+
tools: [...o.tools, ...this.injectedTools],
|
|
4202
|
+
systemPrompt: o.systemPrompt,
|
|
4203
|
+
providerOptionsFor: o.providerOptionsFor ?? ((m) => modelProvider(m) === modelProvider(o.model) ? o.providerOptions : void 0)
|
|
4204
|
+
};
|
|
4045
4205
|
tools = [...tools, makeTaskTool(taskOpts), makeTaskBatchTool(taskOpts)];
|
|
4046
4206
|
}
|
|
4047
4207
|
if (o.checkpoints) tools = [...tools, ...checkpointTools()];
|
|
@@ -4906,45 +5066,6 @@ function lastAssistantText(messages) {
|
|
|
4906
5066
|
return "";
|
|
4907
5067
|
}
|
|
4908
5068
|
|
|
4909
|
-
// src/models.ts
|
|
4910
|
-
var MODEL_ALIASES = {
|
|
4911
|
-
fable: "claude-fable-5",
|
|
4912
|
-
"fable-5": "claude-fable-5",
|
|
4913
|
-
opus: "claude-opus-4-8",
|
|
4914
|
-
"opus-4-8": "claude-opus-4-8",
|
|
4915
|
-
"opus-4-7": "claude-opus-4-7",
|
|
4916
|
-
"opus-4-6": "claude-opus-4-6",
|
|
4917
|
-
sonnet: "claude-sonnet-4-6",
|
|
4918
|
-
haiku: "claude-haiku-4-5-20251001"
|
|
4919
|
-
};
|
|
4920
|
-
function resolveModelAlias(input) {
|
|
4921
|
-
const slash = input.indexOf("/");
|
|
4922
|
-
const prefix = slash === -1 ? "" : input.slice(0, slash + 1);
|
|
4923
|
-
const rest = slash === -1 ? input : input.slice(slash + 1);
|
|
4924
|
-
return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);
|
|
4925
|
-
}
|
|
4926
|
-
function modelShortLabel(model) {
|
|
4927
|
-
if (!model) return "default";
|
|
4928
|
-
const id = model.split("/").pop();
|
|
4929
|
-
return id.replace(/^claude-/, "").replace(/-\d.*$/, "");
|
|
4930
|
-
}
|
|
4931
|
-
function resolveModelSwitch(opts) {
|
|
4932
|
-
const { requested, current, prior, allowed } = opts;
|
|
4933
|
-
if (!requested) return { model: current, denied: false };
|
|
4934
|
-
if (allowed && !allowed.some((a) => modelShortLabel(a) === modelShortLabel(requested))) {
|
|
4935
|
-
return { model: current, denied: true };
|
|
4936
|
-
}
|
|
4937
|
-
const fmt = opts.format ?? ((from, to) => `_[switching from ${from} to ${to}]_
|
|
4938
|
-
|
|
4939
|
-
`);
|
|
4940
|
-
const changed = prior !== void 0 && modelShortLabel(prior) !== modelShortLabel(requested);
|
|
4941
|
-
return {
|
|
4942
|
-
model: requested,
|
|
4943
|
-
denied: false,
|
|
4944
|
-
notice: changed ? fmt(modelShortLabel(prior), modelShortLabel(requested)) : void 0
|
|
4945
|
-
};
|
|
4946
|
-
}
|
|
4947
|
-
|
|
4948
5069
|
// src/index.ts
|
|
4949
5070
|
init_NodeDiskFilesystem();
|
|
4950
5071
|
|