agent.libx.js 0.94.5 → 0.94.6
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 -1
- package/dist/cli.js +36 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +16 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ agentx --resume <id> "…" # resume a specific session
|
|
|
111
111
|
- **Any provider** — set `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GOOGLE_API_KEY` / `GROQ_API_KEY`; choose with `-m provider/model`.
|
|
112
112
|
- **@-file mentions & headless JSON** — reference files inline in a prompt with `@path` (e.g. `explain @src/Agent.ts`); script with `-p --output-format json` to get one machine-readable result object on stdout (activity stays on stderr).
|
|
113
113
|
- **Tab-completion** — `Tab` completes `/<command>` names and `@<path>` file/dir references (descends subdirs, dotfiles hidden unless typed) straight from the working tree.
|
|
114
|
-
- **Duplex mode** — `agentx --duplex` runs the full standard REPL (slash commands, sessions, postures, rewind, MCP) with the three-tier engine driving turns: a fast voice model (`--voice-model`, default `groq/openai/gpt-oss-120b`) answers every line instantly and delegates real work to background workers built with the same wiring as a normal run (fs mode, permissions, MCP); worker activity shows as dim chrome and results are re-voiced when ready. Switch any tier live with `/model` (opens a reflex/act/think picker), or the `/voice-model` · `/think-model` shortcuts. `/tasks` lists background tasks and cancels a running one from a picker (Esc mid-turn cancels the foreground turn; Esc again at the idle prompt cancels running workers).
|
|
114
|
+
- **Duplex mode** — `agentx --duplex` runs the full standard REPL (slash commands, sessions, postures, rewind, MCP) with the three-tier engine driving turns: a fast voice model (`--voice-model`, default `groq/openai/gpt-oss-120b`) answers every line instantly and delegates real work to background workers built with the same wiring as a normal run (fs mode, permissions, MCP); worker activity shows as dim chrome and results are re-voiced when ready. Switch any tier live with `/model` (opens a reflex/act/think picker), or the `/voice-model` · `/think-model` shortcuts. `/tasks` lists background tasks, inspects a task's live output tail, and cancels a running one from a picker (Esc mid-turn cancels the foreground turn; Esc again at the idle prompt cancels running workers).
|
|
115
115
|
- **MCP servers** — declare `mcpServers: { name: { command, args } | { url } }` in config and they're auto-mounted at startup (in parallel, with an optional `mountTimeoutMs` deadline so one slow/dead server never blocks the rest): the client does the JSON-RPC handshake (stdio or HTTP) + `tools/list`, and the discovered tools appear as `mcp__<name>__<tool>` in `/tools` (inspect with `/mcp`). A bad server is logged and skipped, never blocking the agent. For large tool sets, **deferred mode** (`makeMcpToolSearch` / `mountMcpDeferred`) exposes just two bounded tools (`ToolSearch` + `McpCall`) instead of N defs — dodging the provider tool-cap and improving selection accuracy. **`mountMcpCatalog`** goes further: a cached, hash-keyed catalog + lazy connect means a turn that uses no MCP tool opens **zero** connections, and one that uses a tool connects exactly that server — latency scales with tools-used, not servers-configured. A down server is **negative-cached** (`failureCooldownMs`) so it never re-floors a later turn at the deadline. For zero turn-path latency even on a cold process, call **`warmMcpCatalog`** at boot + on a timer (off-turn discovery) and mount with **`{ discover: 'cache-only' }`** — the turn then never synchronously connects: it serves the warmed catalog and discovers any miss in the background.
|
|
116
116
|
|
|
117
117
|
## 🧬 It improves itself
|
package/dist/cli.js
CHANGED
|
@@ -4257,22 +4257,30 @@ ${recent}` : brief) + verify;
|
|
|
4257
4257
|
const controller = new AbortController();
|
|
4258
4258
|
const base = tierOpts?.hooks ?? o.actOptions?.hooks;
|
|
4259
4259
|
const report = o.progressUpdates ? this.progressReporter(id) : void 0;
|
|
4260
|
-
const
|
|
4260
|
+
const tail = [];
|
|
4261
|
+
const pushTail = (line) => {
|
|
4262
|
+
tail.push(line.slice(0, 200));
|
|
4263
|
+
if (tail.length > 120) tail.splice(0, tail.length - 120);
|
|
4264
|
+
};
|
|
4265
|
+
const hooks = {
|
|
4261
4266
|
...base,
|
|
4262
4267
|
preToolUse: async (call, meta) => {
|
|
4263
4268
|
const d = await base?.preToolUse?.(call, meta);
|
|
4264
|
-
|
|
4269
|
+
pushTail(`\u2699 ${describeCall(call)}`);
|
|
4270
|
+
report?.pre(call);
|
|
4265
4271
|
return d;
|
|
4266
4272
|
},
|
|
4267
4273
|
postToolUse: async (call, result, meta) => {
|
|
4268
4274
|
await base?.postToolUse?.(call, result, meta);
|
|
4269
|
-
|
|
4275
|
+
const last = result?.trim().split("\n").filter(Boolean).pop();
|
|
4276
|
+
if (last) pushTail(` \u21B3 ${last}`);
|
|
4277
|
+
report?.post(call);
|
|
4270
4278
|
},
|
|
4271
4279
|
onToolOutput: (call, chunk, meta) => {
|
|
4272
4280
|
base?.onToolOutput?.(call, chunk, meta);
|
|
4273
|
-
report
|
|
4281
|
+
report?.output(chunk);
|
|
4274
4282
|
}
|
|
4275
|
-
}
|
|
4283
|
+
};
|
|
4276
4284
|
const relayAsk = async (q2) => {
|
|
4277
4285
|
const opts = q2.options?.length ? ` Options: ${q2.options.map((x) => x.label).join(", ")}.` : "";
|
|
4278
4286
|
const a = await this.parkQuestion(id, `${q2.question}${opts}`);
|
|
@@ -4294,7 +4302,7 @@ ${recent}` : brief) + verify;
|
|
|
4294
4302
|
// shared with the checker so a cancel tears down both
|
|
4295
4303
|
};
|
|
4296
4304
|
const promise = new Agent(agentOpts).run(briefText).then((res) => this.maybeVerify(id, briefText, res, tier, agentOpts)).then((res) => this.onWorkerSettled(id, res)).catch((err2) => this.onWorkerFailed(id, err2));
|
|
4297
|
-
this.tasks.set(id, { id, label, status: "running", controller, promise });
|
|
4305
|
+
this.tasks.set(id, { id, label, status: "running", controller, promise, tail });
|
|
4298
4306
|
}
|
|
4299
4307
|
/** Fresh-context check of a successful Act task: a NEW agent (same model/fs/tools, but NO shared
|
|
4300
4308
|
* conversation context) re-reads the file state against the brief and fixes any gap. The fix lands
|
|
@@ -4413,6 +4421,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
4413
4421
|
return this.failTask(rec, msg);
|
|
4414
4422
|
}
|
|
4415
4423
|
rec.status = "done";
|
|
4424
|
+
rec.result = res.text;
|
|
4416
4425
|
log7.verbose(`task ${id} done (${res.steps} steps)`);
|
|
4417
4426
|
this.notify("task_done", `task ${id} (${rec.label}) completed`, {
|
|
4418
4427
|
id,
|
|
@@ -4430,6 +4439,7 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
4430
4439
|
failTask(rec, msg) {
|
|
4431
4440
|
this.dropAsk(rec.id);
|
|
4432
4441
|
rec.status = "error";
|
|
4442
|
+
rec.result = msg;
|
|
4433
4443
|
log7.warn(`task ${rec.id} failed: ${msg}`);
|
|
4434
4444
|
this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`);
|
|
4435
4445
|
this.queueRevoice(`[task ${rec.id} failed] ${msg}`);
|
|
@@ -10077,7 +10087,7 @@ ${extra}` : body);
|
|
|
10077
10087
|
`));
|
|
10078
10088
|
}
|
|
10079
10089
|
}, tasks: {
|
|
10080
|
-
desc: "background tasks \u2014 /tasks [cancel <id>], or alone for a picker (\u21B5
|
|
10090
|
+
desc: "background tasks \u2014 /tasks [cancel <id>], or alone for a picker (\u21B5 inspects output; running tasks can be cancelled)",
|
|
10081
10091
|
run: async (a) => {
|
|
10082
10092
|
const all = [...dx.tasks.values()];
|
|
10083
10093
|
if (!all.length) {
|
|
@@ -10094,15 +10104,29 @@ ${extra}` : body);
|
|
|
10094
10104
|
return;
|
|
10095
10105
|
}
|
|
10096
10106
|
const mark = (s) => s === "running" ? cyan("\u25D4 running") : s === "done" ? green("\u2713 done") : s === "cancelled" ? yellow("\u2298 cancelled") : red(`\u2717 ${s}`);
|
|
10097
|
-
|
|
10098
|
-
|
|
10107
|
+
const inspect = (t2) => {
|
|
10108
|
+
err(` ${t2.id} ${mark(t2.status)} ${dim(t2.label)}
|
|
10099
10109
|
`);
|
|
10110
|
+
for (const l of t2.tail.slice(-20)) err(dim(` ${l}
|
|
10111
|
+
`));
|
|
10112
|
+
if (t2.result) err(dim(` \u29BF ${t2.result.split("\n")[0].slice(0, 160)}
|
|
10113
|
+
`));
|
|
10114
|
+
if (!t2.tail.length && !t2.result) err(dim(" (no activity yet)\n"));
|
|
10115
|
+
};
|
|
10116
|
+
if (!process.stderr.isTTY || !process.stdin.isTTY) {
|
|
10117
|
+
for (const t2 of all) inspect(t2);
|
|
10100
10118
|
return;
|
|
10101
10119
|
}
|
|
10102
|
-
const items = all.map((
|
|
10103
|
-
const id = await selectMenu(process.stderr, { title: "Background tasks \xB7 \u21B5
|
|
10104
|
-
if (id)
|
|
10120
|
+
const items = all.map((t2) => ({ label: `${t2.id} ${t2.label.slice(0, 60)}`, value: t2.id, desc: mark(t2.status) + " \xB7 \u21B5 inspect" }));
|
|
10121
|
+
const id = await selectMenu(process.stderr, { title: "Background tasks \xB7 \u21B5 inspect \xB7 esc close", items });
|
|
10122
|
+
if (!id) return;
|
|
10123
|
+
const t = dx.tasks.get(String(id));
|
|
10124
|
+
inspect(t);
|
|
10125
|
+
if (t.status === "running") {
|
|
10126
|
+
const v = await selectMenu(process.stderr, { title: `Cancel ${t.id}?`, items: [{ label: "Keep running", value: "keep" }, { label: "Cancel the task", value: "cancel" }], current: "keep" });
|
|
10127
|
+
if (v === "cancel") err(dim(` ${dx.cancelTask(t.id)}
|
|
10105
10128
|
`));
|
|
10129
|
+
}
|
|
10106
10130
|
}
|
|
10107
10131
|
} } : {},
|
|
10108
10132
|
reasoning: {
|