@livx.cc/agentx 0.99.83 → 0.99.85
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 +2 -2
- package/dist/cli.js +90 -47
- package/dist/cli.js.map +1 -1
- package/dist/directives.js +2 -12
- package/dist/directives.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +63 -53
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +7 -11
- package/dist/models.js +8 -12
- package/dist/models.js.map +1 -1
- package/dist/tools.shell.js +10 -1
- package/dist/tools.shell.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ await fs.createDir('/src');
|
|
|
64
64
|
await fs.writeFile('/src/x.ts', 'export const add = (a,b) => a - b;\n');
|
|
65
65
|
|
|
66
66
|
const ai = new AIClient({ apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY } });
|
|
67
|
-
const res = await new Agent({ ai, fs, model: 'anthropic/
|
|
67
|
+
const res = await new Agent({ ai, fs, model: 'anthropic/sonnet' })
|
|
68
68
|
.run('Fix the add bug in /src/x.ts');
|
|
69
69
|
|
|
70
70
|
console.log(res.finishReason, await fs.readFile('/src/x.ts'));
|
|
@@ -77,7 +77,7 @@ console.log(res.finishReason, await fs.readFile('/src/x.ts'));
|
|
|
77
77
|
- **`Edit`** — exact unique-substring replace, with a read-before-edit staleness guard.
|
|
78
78
|
- **`Grep`/`Glob`/`Write`/`MultiEdit`** — structured, typed results straight from the VFS (no `bash` parsing). The selectable tool set the self-evolution loop mutates over.
|
|
79
79
|
- **`TodoWrite`** — a planning scratchpad; **`Task`** — spawn a depth-limited child agent over the VFS (`subagents: true`); **`SlashCommand`** — reusable prompt templates from `<dir>/*.md` (`commandsDir`); plus a real **MCP client** (`src/mcp.client.ts`, node-only — stdio/HTTP JSON-RPC handshake + discovery) that feeds the edge-safe **MCP adapter** (`mcpToolsToAgentTools`), so any MCP server's tools become agent tools.
|
|
80
|
-
- **`WebFetch`/`WebSearch`** — fetch a URL as readable text, or search the web. **Keyless by default** (WebSearch uses DuckDuckGo; `provider: 'auto'` upgrades by key presence — Firecrawl `FIRECRAWL_API_KEY` > Tavily `TAVILY_API_KEY`) and **auto-enabled in the CLI**. Factory-built with an injectable `fetch`, so they stay edge-portable and testable. (In the library they're opt-in by name: `tools: [...,'WebSearch']`.) **`WebSearchAnthropic`** is a separate provider-pinned tool — Anthropic's native search relayed through a cheap model (
|
|
80
|
+
- **`WebFetch`/`WebSearch`** — fetch a URL as readable text, or search the web. **Keyless by default** (WebSearch uses DuckDuckGo; `provider: 'auto'` upgrades by key presence — Firecrawl `FIRECRAWL_API_KEY` > Tavily `TAVILY_API_KEY`) and **auto-enabled in the CLI**. Factory-built with an injectable `fetch`, so they stay edge-portable and testable. (In the library they're opt-in by name: `tools: [...,'WebSearch']`.) **`WebSearchAnthropic`** is a separate provider-pinned tool — Anthropic's native search relayed through a cheap model (the newest Haiku); CLI-default-on when an Anthropic key resolves, API-billed per call, opt out via `anthropicWebSearch: false`.
|
|
81
81
|
- **Oversized-output pagination** — any tool result over a byte ceiling (`maxToolResultBytes`, default 60k) is cropped to page 1 with a marker (refine the query / read further), so one big `Grep`/`Read`/MCP/web result can't blow the context window. In the CLI (**on by default**; `--no-scratch` to disable) the full output instead spills **losslessly** to a **scratch** file and the model recovers specifics via `Grep`/`Read` or **`Ask`** — a cheap, context-isolated peek that returns just the answer (the raw blob never re-enters context).
|
|
82
82
|
|
|
83
83
|
## Agentic subsystems
|
package/dist/cli.js
CHANGED
|
@@ -1000,6 +1000,34 @@ var init_todo = __esm({
|
|
|
1000
1000
|
}
|
|
1001
1001
|
});
|
|
1002
1002
|
|
|
1003
|
+
// src/models.ts
|
|
1004
|
+
import { claudeModelAliases } from "ai.libx.js";
|
|
1005
|
+
function latestClaude(family, provider = "anthropic") {
|
|
1006
|
+
const id = MODEL_ALIASES[family];
|
|
1007
|
+
if (!id) throw new Error(`ai.libx.js catalog has no Claude ${family} model`);
|
|
1008
|
+
return `${provider}/${id}`;
|
|
1009
|
+
}
|
|
1010
|
+
function resolveModelAlias(input) {
|
|
1011
|
+
const slash = input.indexOf("/");
|
|
1012
|
+
const prefix = slash === -1 ? "" : input.slice(0, slash + 1);
|
|
1013
|
+
const rest = slash === -1 ? input : input.slice(slash + 1);
|
|
1014
|
+
if (prefix === "claude-code/") return input;
|
|
1015
|
+
return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);
|
|
1016
|
+
}
|
|
1017
|
+
function modelProvider(model) {
|
|
1018
|
+
const i = model.indexOf("/");
|
|
1019
|
+
if (i !== -1) return model.slice(0, i);
|
|
1020
|
+
if (/^claude-/i.test(model)) return "anthropic";
|
|
1021
|
+
return `?${model}`;
|
|
1022
|
+
}
|
|
1023
|
+
var MODEL_ALIASES;
|
|
1024
|
+
var init_models = __esm({
|
|
1025
|
+
"src/models.ts"() {
|
|
1026
|
+
"use strict";
|
|
1027
|
+
MODEL_ALIASES = { ...claudeModelAliases };
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1003
1031
|
// src/tools.web.ts
|
|
1004
1032
|
function htmlToText(html) {
|
|
1005
1033
|
let s = html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<title[\s\S]*?<\/title>/gi, " ").replace(/<noscript[\s\S]*?<\/noscript>/gi, " ").replace(/<textarea[\s\S]*?<\/textarea>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<\/(p|div|li|h[1-6]|tr|section|article|header|footer|nav)>/gi, "\n").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, " ");
|
|
@@ -1221,7 +1249,7 @@ function makeWebSearchTool(options = {}) {
|
|
|
1221
1249
|
if (provider === "anthropic") {
|
|
1222
1250
|
const akey = options.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
1223
1251
|
if (!akey) return "Error: WebSearchAnthropic requires ANTHROPIC_API_KEY (set in env)";
|
|
1224
|
-
return await anthropicSearch(q2, { key: akey, model: options.model ?? "
|
|
1252
|
+
return await anthropicSearch(q2, { key: akey, model: options.model ?? latestClaude("haiku").slice("anthropic/".length), fetch: doFetch, signal: ctl.signal });
|
|
1225
1253
|
}
|
|
1226
1254
|
if (useFirecrawl) {
|
|
1227
1255
|
if (!fcKey) return "Error: Firecrawl provider selected but FIRECRAWL_API_KEY is not set";
|
|
@@ -1263,6 +1291,7 @@ var init_tools_web = __esm({
|
|
|
1263
1291
|
"src/tools.web.ts"() {
|
|
1264
1292
|
"use strict";
|
|
1265
1293
|
init_logging();
|
|
1294
|
+
init_models();
|
|
1266
1295
|
log4 = forComponent("web");
|
|
1267
1296
|
webFetchTool = makeWebFetchTool();
|
|
1268
1297
|
webSearchTool = makeWebSearchTool();
|
|
@@ -3983,36 +4012,10 @@ ${sections.join("\n\n---\n\n")}`;
|
|
|
3983
4012
|
|
|
3984
4013
|
// src/subagent.ts
|
|
3985
4014
|
init_tools();
|
|
4015
|
+
init_models();
|
|
3986
4016
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
3987
4017
|
import { join as join4 } from "path";
|
|
3988
4018
|
|
|
3989
|
-
// src/models.ts
|
|
3990
|
-
var MODEL_ALIASES = {
|
|
3991
|
-
fable: "claude-fable-5",
|
|
3992
|
-
"fable-5": "claude-fable-5",
|
|
3993
|
-
opus: "claude-opus-5-5",
|
|
3994
|
-
"opus-5-5": "claude-opus-5-5",
|
|
3995
|
-
"opus-5": "claude-opus-5",
|
|
3996
|
-
"opus-4-8": "claude-opus-4-8",
|
|
3997
|
-
"opus-4-7": "claude-opus-4-7",
|
|
3998
|
-
"opus-4-6": "claude-opus-4-6",
|
|
3999
|
-
sonnet: "claude-sonnet-4-6",
|
|
4000
|
-
haiku: "claude-haiku-4-5-20251001"
|
|
4001
|
-
};
|
|
4002
|
-
function resolveModelAlias(input) {
|
|
4003
|
-
const slash = input.indexOf("/");
|
|
4004
|
-
const prefix = slash === -1 ? "" : input.slice(0, slash + 1);
|
|
4005
|
-
const rest = slash === -1 ? input : input.slice(slash + 1);
|
|
4006
|
-
if (prefix === "claude-code/") return input;
|
|
4007
|
-
return prefix + (MODEL_ALIASES[rest.toLowerCase()] ?? rest);
|
|
4008
|
-
}
|
|
4009
|
-
function modelProvider(model) {
|
|
4010
|
-
const i = model.indexOf("/");
|
|
4011
|
-
if (i !== -1) return model.slice(0, i);
|
|
4012
|
-
if (/^claude-/i.test(model)) return "anthropic";
|
|
4013
|
-
return `?${model}`;
|
|
4014
|
-
}
|
|
4015
|
-
|
|
4016
4019
|
// src/permissions.ts
|
|
4017
4020
|
init_tools_structured();
|
|
4018
4021
|
function commandMatches(glob, cmd) {
|
|
@@ -4500,6 +4503,9 @@ ${r.error ? `ERROR: ${r.error}` : r.text || "(no summary)"}${extra}`);
|
|
|
4500
4503
|
};
|
|
4501
4504
|
}
|
|
4502
4505
|
|
|
4506
|
+
// src/Agent.ts
|
|
4507
|
+
init_models();
|
|
4508
|
+
|
|
4503
4509
|
// src/agents.ts
|
|
4504
4510
|
function parseAgentFrontmatter(md) {
|
|
4505
4511
|
const m = md.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
|
@@ -4635,7 +4641,7 @@ var AgentOptions = class {
|
|
|
4635
4641
|
* browser build (which always passes its own fs) never pulls in `node:fs`. Pass `fs` explicitly to
|
|
4636
4642
|
* use any other backend (and to keep full control of the jail). */
|
|
4637
4643
|
fs;
|
|
4638
|
-
model = "
|
|
4644
|
+
model = latestClaude("sonnet");
|
|
4639
4645
|
systemPrompt = "You are a coding agent operating on a virtual filesystem. Use the `bash` tool to explore (ls, grep, find, cat) and the `Read`/`Edit` tools to view and modify files. Always Read a file before Editing it \u2014 and before overwriting it with `Write` or a whole-file `ApplyEdits`: Write replaces the ENTIRE file, so blindly writing over an existing one destroys its unseen contents. If a file already exists, Read it first, then merge your changes (or Edit the specific part). When multiple tool calls are independent (e.g. several Reads or Greps with no dependency between them), issue them together in a single turn rather than one at a time. When the task is complete, reply with a short summary and make no further tool calls.\nRecovery: every turn's file changes are checkpointed. If the user asks to undo/revert/recover edits (including a file you overwrote), tell them to run `/rewind` (or `/undo` for the last turn) to restore the working tree \u2014 checkpoints cover files you edited even in .gitignored dirs. You cannot run `/rewind` yourself; surface it as the recovery path.\nWhen working with a knowledge base or document workspace: use `RepoMap` with scope \"docs\" to see all document headings and summaries (orient first); use `MemorySearch` to find memories by content when you don't know the exact slug; use `Recall` with multiple slugs to batch-load related facts in one call. Prefer targeted loads over reading everything \u2014 orient \u2192 search \u2192 load \u2192 act.";
|
|
4640
4646
|
tools = defaultTools();
|
|
4641
4647
|
/** claude-code/* only: which agentx tools reach it as the in-process `agentx` MCP server (ccBridge.ts). 'memory' =
|
|
@@ -6070,6 +6076,7 @@ function toolKey(name) {
|
|
|
6070
6076
|
}
|
|
6071
6077
|
|
|
6072
6078
|
// src/index.ts
|
|
6079
|
+
init_models();
|
|
6073
6080
|
init_NodeDiskFilesystem();
|
|
6074
6081
|
|
|
6075
6082
|
// src/BodDbFilesystem.ts
|
|
@@ -6778,6 +6785,7 @@ function digestRun(messages, maxChars) {
|
|
|
6778
6785
|
import { MemFilesystem as MemFilesystem2 } from "@livx.cc/wcli/core";
|
|
6779
6786
|
init_llm();
|
|
6780
6787
|
init_logging();
|
|
6788
|
+
init_models();
|
|
6781
6789
|
|
|
6782
6790
|
// node_modules/@bod.ee/voice/src/core/logging.ts
|
|
6783
6791
|
function consoleForComponent(name) {
|
|
@@ -9650,9 +9658,9 @@ var DuplexAgentOptions = class {
|
|
|
9650
9658
|
// mislabels gpt-oss harmony channels under load, leaking raw analysis into the spoken `final` channel
|
|
9651
9659
|
// (and misfiring Hold). 120b is the same price tier (~$0.15/$0.60) — the quality/cost trade is free.
|
|
9652
9660
|
reflexModel = "groq/openai/gpt-oss-120b";
|
|
9653
|
-
actModel = "
|
|
9661
|
+
actModel = latestClaude("sonnet");
|
|
9654
9662
|
/** Premium reasoning model. Set to `false` to disable the Think tier entirely. */
|
|
9655
|
-
thinkModel = "
|
|
9663
|
+
thinkModel = latestClaude("opus");
|
|
9656
9664
|
/** Per-worker providerOptions, derived from the worker's actual model at spawn time (IoC — keeps duplex
|
|
9657
9665
|
* provider-agnostic). Workers override the reflex/main model, so provider-specific options (e.g. cursor's
|
|
9658
9666
|
* cwd/cursorSession) must be recomputed for the worker's model, never inherited from the main template —
|
|
@@ -11230,11 +11238,12 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
11230
11238
|
// src/modelRouter.ts
|
|
11231
11239
|
init_llm();
|
|
11232
11240
|
init_logging();
|
|
11241
|
+
init_models();
|
|
11233
11242
|
var log22 = forComponent("ModelRouter");
|
|
11234
11243
|
var ModelRouterOptions = class {
|
|
11235
11244
|
classifier = jevClassifier();
|
|
11236
11245
|
/** Capability tiers → model id (any provider). basic/standard run the main session; premium is only ever an offload. */
|
|
11237
|
-
models = { basic: "
|
|
11246
|
+
models = { basic: latestClaude("haiku"), standard: latestClaude("sonnet"), premium: latestClaude("opus") };
|
|
11238
11247
|
/** Escalate to a stronger tier when it holds at least this much probability mass (upward bias). */
|
|
11239
11248
|
threshold = 0.25;
|
|
11240
11249
|
/** P(same thread) at or above which the previous premium side-conversation is resumed. */
|
|
@@ -12533,7 +12542,7 @@ Reference files in them by their mount path (the left side).`;
|
|
|
12533
12542
|
ai: o.ai,
|
|
12534
12543
|
jobs: jobRegistry,
|
|
12535
12544
|
fs,
|
|
12536
|
-
model: o.model ?? "
|
|
12545
|
+
model: o.model ?? latestClaude("sonnet"),
|
|
12537
12546
|
// PDF reads (disk mode only — VFS paths aren't real files): poppler's pdftotext when installed.
|
|
12538
12547
|
...!virtual ? { pdfText: pdfTextViaPoppler } : {},
|
|
12539
12548
|
// Anchor cursor to the launch dir (its adapter defaults to TMPDIR otherwise) and forward the
|
|
@@ -12584,7 +12593,7 @@ Windows paths: file tools accept ${cwd} style paths and the same path written ${
|
|
|
12584
12593
|
if (o.anthropicWebSearch !== false && o.anthropicKey && !requested.includes("WebSearchAnthropic")) {
|
|
12585
12594
|
tail.push(makeWebSearchAnthropicTool({ anthropicApiKey: o.anthropicKey }));
|
|
12586
12595
|
}
|
|
12587
|
-
if (scratch) tail.push(makeAskTool({ fs, ai: o.ai, model: o.scratchAskModel ?? o.model ?? "
|
|
12596
|
+
if (scratch) tail.push(makeAskTool({ fs, ai: o.ai, model: o.scratchAskModel ?? o.model ?? latestClaude("sonnet"), dir: scratchDir }));
|
|
12588
12597
|
tail.push(makeNotifyTool());
|
|
12589
12598
|
if (!realShell.length) return [...base, ...tail];
|
|
12590
12599
|
const filtered = base.filter((t) => t.name !== "bash");
|
|
@@ -16407,7 +16416,7 @@ function parseArgs(argv) {
|
|
|
16407
16416
|
if (a.seed && !a.boddb) throw new Error("--seed only applies with --boddb (it seeds the database from cwd on first run)");
|
|
16408
16417
|
if (a.worktree && (a.vfs || a.boddb)) throw new Error("--worktree is a real-disk concept \u2014 incompatible with --vfs/--boddb");
|
|
16409
16418
|
if (a.duplex && (a.task || a.print)) throw new Error("--duplex is interactive-only (a conversational mode) \u2014 drop the task/-p");
|
|
16410
|
-
if (a.inputFormat && (!a.duplex || a.outputFormat !== "stream-json")) throw new Error("--input-format stream-json
|
|
16419
|
+
if (a.inputFormat && (!a.duplex && !a.print || a.outputFormat !== "stream-json")) throw new Error("--input-format stream-json needs --output-format stream-json and --duplex (or --voice) or -p");
|
|
16411
16420
|
if (a.duplex && a.plan) throw new Error("--plan is not supported in --duplex (workers are non-interactive; a plan could never be approved)");
|
|
16412
16421
|
if (a.voiceSystemPrompt && !a.duplex) throw new Error("--voice-system-prompt only applies with --duplex");
|
|
16413
16422
|
if (a.voiceModel && !a.duplex) throw new Error("--voice-model only applies with --duplex");
|
|
@@ -16425,7 +16434,7 @@ var HELP = `agentx \u2014 agent runtime CLI
|
|
|
16425
16434
|
agentx -c "<task>" continue the most recent session
|
|
16426
16435
|
|
|
16427
16436
|
Flags:
|
|
16428
|
-
-m, --model <id> model (default auto: per-turn haiku/sonnet + opus offload when Jev is configured, else anthropic
|
|
16437
|
+
-m, --model <id> model (default auto: per-turn haiku/sonnet + opus offload when Jev is configured, else the newest anthropic Sonnet); bare -m or -m list = list all models
|
|
16429
16438
|
-C, --cwd <dir> working dir / project root (default .)
|
|
16430
16439
|
-c, --continue resume the most recent session in this dir
|
|
16431
16440
|
-r, --resume [id] resume a session \u2014 with an id, or bare for an interactive picker
|
|
@@ -16457,7 +16466,7 @@ Flags:
|
|
|
16457
16466
|
with SONIOX_API_KEY + CARTESIA_API_KEY(+VOICE_ID) set: real voice I/O \u2014 mic in,
|
|
16458
16467
|
spoken replies out (echo-cancelled; speak over it to interrupt)
|
|
16459
16468
|
--voice-model <id> with --duplex: the fast voice model (default groq/openai/gpt-oss-120b)
|
|
16460
|
-
--think-model <id> with --duplex: the premium deep-reasoning model (default anthropic
|
|
16469
|
+
--think-model <id> with --duplex: the premium deep-reasoning model (default: the newest anthropic Opus)
|
|
16461
16470
|
--no-think with --duplex: disable the Think tier (Act handles everything)
|
|
16462
16471
|
--voice-system-prompt <text> with --duplex: appended to the voice (reflex) prompt only (--append-system-prompt \u2192 workers)
|
|
16463
16472
|
--add-dir <path> mount another directory into the workspace (repeatable; disk mode only)
|
|
@@ -16472,6 +16481,8 @@ Flags:
|
|
|
16472
16481
|
--timeout <sec> wall-clock kill-switch (default 120)
|
|
16473
16482
|
--input-format stream-json with --duplex|--voice + --output-format stream-json: headless duplex for a host app
|
|
16474
16483
|
(NDJSON turns/barge-ins on stdin, reflex text + worker speech + task events on stdout)
|
|
16484
|
+
with -p: the first {"type":"user","text"} line is the prompt; later ones STEER the running turn
|
|
16485
|
+
(unconsumed steers come back as the result's \`unsteered\`)
|
|
16475
16486
|
--output-format <f> headless output: text (default) | json (one result object) | stream-json (NDJSON events: {type:text|thinking}\u2026 then {type:result})
|
|
16476
16487
|
-v, --version print version and exit
|
|
16477
16488
|
-h, --help
|
|
@@ -16506,8 +16517,8 @@ REPL shortcuts: Shift+Tab cycles permission posture (ask \u2192 accept-edits \u2
|
|
|
16506
16517
|
REPL steer/stash: type while a turn is running \u2192 Enter STEERS it (the text reaches the model at its next step, alongside the next tool result). Alt+S (or Ctrl+S) with text stashes it for AFTER the turn instead; on an empty prompt pops the next stashed entry for editing.
|
|
16507
16518
|
REPL editing (emacs/readline): Ctrl-A/E line start/end \xB7 Ctrl-B/F char \xB7 Alt-B/F or Alt/Ctrl-\u2190/\u2192 word \xB7 Ctrl-W kill word \xB7 Ctrl-U/K kill to start/end \xB7 Ctrl-Y yank \xB7 Ctrl-_ undo \xB7 Alt-D kill word fwd \xB7 Ctrl-L clear screen. Set editorMode:'vim' (or /config) for modal vim editing.
|
|
16508
16519
|
REPL paste: large/multi-line pastes collapse to a [Pasted #N "\u2026" +N lines] preview (expands on send); a pasted image/file path attaches as [Image]/[File]; Ctrl-V or /paste grabs a clipboard image (macOS).`;
|
|
16509
|
-
function newestModel() {
|
|
16510
|
-
return listModels().slice().sort((a, b) => (getModelInfo(b)?.releaseDate ?? "").localeCompare(getModelInfo(a)?.releaseDate ?? ""))[0] ?? "";
|
|
16520
|
+
function newestModel(provider) {
|
|
16521
|
+
return listModels(provider).slice().sort((a, b) => (getModelInfo(b)?.releaseDate ?? "").localeCompare(getModelInfo(a)?.releaseDate ?? ""))[0] ?? "";
|
|
16511
16522
|
}
|
|
16512
16523
|
function printModels() {
|
|
16513
16524
|
const byProvider = /* @__PURE__ */ new Map();
|
|
@@ -16531,7 +16542,8 @@ function resolveModelOrNewest(model) {
|
|
|
16531
16542
|
const want = model ?? new AgentOptions().model;
|
|
16532
16543
|
const resolved = resolveModel(want);
|
|
16533
16544
|
if (isModelSupported(resolved)) return resolved;
|
|
16534
|
-
const
|
|
16545
|
+
const provider = getProviderFromModel(want) ?? void 0;
|
|
16546
|
+
const fallback = newestModel(provider && listModels(provider).length ? provider : void 0);
|
|
16535
16547
|
if (!fallback) return want;
|
|
16536
16548
|
err(yellow(` \u26A0 model "${want}" not available \u2014 using ${fallback}
|
|
16537
16549
|
`));
|
|
@@ -16601,7 +16613,7 @@ function duplexModels(o) {
|
|
|
16601
16613
|
let reflexModel = asked ? resolveModelOrNewest(asked) : void 0;
|
|
16602
16614
|
const reflex = reflexModel ?? dflt.reflexModel;
|
|
16603
16615
|
if (!modelUsable(reflex, o.apiKeys)) {
|
|
16604
|
-
const to = anthropic ? "
|
|
16616
|
+
const to = anthropic ? latestClaude("haiku") : cc ? "claude-code/haiku" : o.actModel.startsWith("cursor/") ? void 0 : o.actModel;
|
|
16605
16617
|
if (to) reflexModel = to;
|
|
16606
16618
|
notes.push(`voice model ${reflex} can't run here (${why(reflex)})${to ? ` \u2014 using ${to}` : ""}`);
|
|
16607
16619
|
}
|
|
@@ -17058,7 +17070,7 @@ async function evaluateGoal(ai, condition, transcript, log35) {
|
|
|
17058
17070
|
}).join("\n---\n");
|
|
17059
17071
|
try {
|
|
17060
17072
|
const r = await ai.chat({
|
|
17061
|
-
model: "
|
|
17073
|
+
model: latestClaude("haiku"),
|
|
17062
17074
|
stream: false,
|
|
17063
17075
|
messages: [
|
|
17064
17076
|
{ role: "system", content: 'You judge whether a goal condition has been met based on conversation transcript. Respond ONLY with JSON: {"met": boolean, "reason": "one sentence"}. Be strict \u2014 only met:true if there is clear evidence in the transcript.' },
|
|
@@ -17998,7 +18010,7 @@ async function repl(args, ai, cfg, cwd) {
|
|
|
17998
18010
|
err(green(" \u2713 think tier disabled\n"));
|
|
17999
18011
|
return;
|
|
18000
18012
|
}
|
|
18001
|
-
const picked2 = await pickModel(dx.options.thinkModel === false ? "
|
|
18013
|
+
const picked2 = await pickModel(dx.options.thinkModel === false ? latestClaude("opus") : String(dx.options.thinkModel));
|
|
18002
18014
|
if (picked2) applyThinkModel(picked2);
|
|
18003
18015
|
return;
|
|
18004
18016
|
}
|
|
@@ -18639,7 +18651,7 @@ ${task}`;
|
|
|
18639
18651
|
return;
|
|
18640
18652
|
}
|
|
18641
18653
|
const current = dx.options.thinkModel === false ? void 0 : dx.options.thinkModel;
|
|
18642
|
-
const picked = await pickModel(current ?? "
|
|
18654
|
+
const picked = await pickModel(current ?? latestClaude("opus"));
|
|
18643
18655
|
if (picked) applyThinkModel(picked);
|
|
18644
18656
|
else err(dim(` think ${dx.options.thinkModel === false ? "off" : dx.options.thinkModel}
|
|
18645
18657
|
`));
|
|
@@ -18995,7 +19007,7 @@ ${task}`;
|
|
|
18995
19007
|
await fs2.writeFile(p, [
|
|
18996
19008
|
"---",
|
|
18997
19009
|
`description: What "${name}" is for \u2014 the Task tool picks agents by this line`,
|
|
18998
|
-
"# model: anthropic/
|
|
19010
|
+
"# model: anthropic/haiku # optional per-agent model override",
|
|
18999
19011
|
"# tools: Read, Grep, Glob # optional tool allowlist (omit = all tools)",
|
|
19000
19012
|
"---",
|
|
19001
19013
|
"",
|
|
@@ -20016,6 +20028,33 @@ async function duplexServe(args, ai, cfg, cwd) {
|
|
|
20016
20028
|
persist();
|
|
20017
20029
|
await closeMcp(mounted);
|
|
20018
20030
|
}
|
|
20031
|
+
function readStdinSteers() {
|
|
20032
|
+
const early = [];
|
|
20033
|
+
let sink, got;
|
|
20034
|
+
const first = new Promise((res) => {
|
|
20035
|
+
got = res;
|
|
20036
|
+
});
|
|
20037
|
+
const rl = createInterface({ input: process.stdin });
|
|
20038
|
+
rl.on("line", (line) => {
|
|
20039
|
+
let m;
|
|
20040
|
+
try {
|
|
20041
|
+
m = JSON.parse(line);
|
|
20042
|
+
} catch {
|
|
20043
|
+
return;
|
|
20044
|
+
}
|
|
20045
|
+
if (m?.type !== "user" || typeof m.text !== "string" || !m.text.trim()) return;
|
|
20046
|
+
if (got) {
|
|
20047
|
+
got(m.text);
|
|
20048
|
+
got = void 0;
|
|
20049
|
+
} else if (sink) sink(m.text);
|
|
20050
|
+
else early.push(m.text);
|
|
20051
|
+
});
|
|
20052
|
+
rl.on("close", () => got?.(""));
|
|
20053
|
+
return { first, steer: (to) => {
|
|
20054
|
+
sink = to;
|
|
20055
|
+
early.splice(0).forEach(to);
|
|
20056
|
+
} };
|
|
20057
|
+
}
|
|
20019
20058
|
function readAllStdin() {
|
|
20020
20059
|
return new Promise((res) => {
|
|
20021
20060
|
let data = "";
|
|
@@ -20049,7 +20088,9 @@ async function main() {
|
|
|
20049
20088
|
process.exit(installUpdate() ? 0 : 1);
|
|
20050
20089
|
}
|
|
20051
20090
|
if (args.debug) process.env.DEBUG ||= "*";
|
|
20052
|
-
|
|
20091
|
+
const steers = args.print && args.inputFormat && !args.task ? readStdinSteers() : void 0;
|
|
20092
|
+
if (steers) args.task = (await steers.first).trim();
|
|
20093
|
+
else if (args.print && !args.task && !process.stdin.isTTY) args.task = (await readAllStdin()).trim();
|
|
20053
20094
|
if (args.print && !args.task) {
|
|
20054
20095
|
console.error(red(" -p: empty prompt (no task argument and nothing on stdin)"));
|
|
20055
20096
|
process.exit(2);
|
|
@@ -20139,6 +20180,7 @@ async function main() {
|
|
|
20139
20180
|
const store = new SessionStore(cwd);
|
|
20140
20181
|
const session = startSession(args, store, agent, cwd);
|
|
20141
20182
|
process.once("SIGINT", () => activeTurn?.abort());
|
|
20183
|
+
steers?.steer((text) => agent.inject(text));
|
|
20142
20184
|
const { ok, res } = await runTurn(agent, store, session, args.task, void 0, cwd, autoRouteSend(args, agent, () => session, (m) => providerOptionsFor(m, cwd, cfg.mcpServers, mounted.map((x) => x.name))));
|
|
20143
20185
|
if (cfg.reflectOnFailure && !ok && res?.finishReason !== "needs_input" && res && agent.options.memoryDir) {
|
|
20144
20186
|
const _fsBase = agent.options.fs.getCwd() === "/" ? "" : agent.options.fs.getCwd();
|
|
@@ -20148,7 +20190,8 @@ async function main() {
|
|
|
20148
20190
|
}
|
|
20149
20191
|
await closeMcp(mounted);
|
|
20150
20192
|
if (args.outputFormat === "json" || args.outputFormat === "stream-json") {
|
|
20151
|
-
const
|
|
20193
|
+
const left = steers ? agent.drainPendingSteers() : [];
|
|
20194
|
+
const obj = { ...res ? jsonResult(res, session) : { ok: false, finishReason: "error", sessionId: session.meta.id }, ...left.length ? { unsteered: left } : {} };
|
|
20152
20195
|
process.stdout.write(JSON.stringify(args.outputFormat === "stream-json" ? { type: "result", ...obj } : obj) + "\n");
|
|
20153
20196
|
}
|
|
20154
20197
|
worktreeCleanup?.();
|