@livx.cc/agentx 0.99.18 → 0.99.20
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/dist/cli.d.ts +1 -0
- package/dist/cli.js +140 -31
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -150,6 +150,7 @@ declare function makeHost(format?: 'text' | 'json' | 'stream-json', opts?: {
|
|
|
150
150
|
stream?: boolean;
|
|
151
151
|
}): HostBridge & {
|
|
152
152
|
flushText: () => void;
|
|
153
|
+
takeToolCount: () => number;
|
|
153
154
|
};
|
|
154
155
|
/** Hooks that render tool activity to stderr: a preToolUse header, and for edits a colorized diff.
|
|
155
156
|
* `opts.background` marks the agent as a BACKGROUND one (duplex workers): its chrome lands async on
|
package/dist/cli.js
CHANGED
|
@@ -9894,11 +9894,15 @@ function lastToken(line) {
|
|
|
9894
9894
|
const m = line.match(/(\S*)$/);
|
|
9895
9895
|
return m ? m[1] : "";
|
|
9896
9896
|
}
|
|
9897
|
+
function atLineStart(line, token) {
|
|
9898
|
+
return line.slice(0, line.length - token.length).trim() === "";
|
|
9899
|
+
}
|
|
9897
9900
|
function completeLine(line, ctx) {
|
|
9898
9901
|
const token = lastToken(line);
|
|
9899
|
-
if (token.startsWith("/")
|
|
9902
|
+
if (token.startsWith("/")) {
|
|
9903
|
+
const pool = atLineStart(line, token) ? ctx.commands : ctx.agentCommands ?? ctx.commands;
|
|
9900
9904
|
const want = token.slice(1);
|
|
9901
|
-
const hits = rank(
|
|
9905
|
+
const hits = rank(pool.filter((c) => fuzzy(want, c)), want).map((c) => "/" + c);
|
|
9902
9906
|
return [hits, token];
|
|
9903
9907
|
}
|
|
9904
9908
|
if (line.startsWith("!") && line.length > 1 && ctx.bangHistory?.length) {
|
|
@@ -10156,19 +10160,112 @@ function renderMdLine(line, inFence, p, cols = 80, fenceLang) {
|
|
|
10156
10160
|
function renderMdBlock(text, p, cols = 80) {
|
|
10157
10161
|
let inFence = false, fenceLang;
|
|
10158
10162
|
const out = [];
|
|
10163
|
+
let table = [];
|
|
10164
|
+
const flush = () => {
|
|
10165
|
+
if (table.length) {
|
|
10166
|
+
out.push(...renderTableLines(table, p, cols));
|
|
10167
|
+
table = [];
|
|
10168
|
+
}
|
|
10169
|
+
};
|
|
10159
10170
|
for (const line of text.split("\n")) {
|
|
10171
|
+
if (!inFence && isTableRow(line)) {
|
|
10172
|
+
table.push(line);
|
|
10173
|
+
continue;
|
|
10174
|
+
}
|
|
10175
|
+
flush();
|
|
10160
10176
|
const r = renderMdLine(line, inFence, p, cols, fenceLang);
|
|
10161
10177
|
inFence = r.inFence;
|
|
10162
10178
|
fenceLang = r.fenceLang;
|
|
10163
10179
|
out.push(r.inFence || /^\s*```/.test(line) ? r.out : wrapAnsi(r.out, cols, hangIndent(line)));
|
|
10164
10180
|
}
|
|
10181
|
+
flush();
|
|
10165
10182
|
return out.join("\n");
|
|
10166
10183
|
}
|
|
10184
|
+
var T_PAD = " ";
|
|
10185
|
+
var T_GAP = 3;
|
|
10186
|
+
var SGR_CLOSES = {
|
|
10187
|
+
"0": /./,
|
|
10188
|
+
"22": /^[12]$/,
|
|
10189
|
+
"23": /^3$/,
|
|
10190
|
+
"24": /^4$/,
|
|
10191
|
+
"27": /^7$/,
|
|
10192
|
+
"29": /^9$/,
|
|
10193
|
+
"39": /^(3[0-7]|9[0-7])$/,
|
|
10194
|
+
"49": /^(4[0-7]|10[0-7])$/
|
|
10195
|
+
};
|
|
10196
|
+
function wrapCell(styled, w) {
|
|
10197
|
+
const segs = wrapAnsi(styled, w).split("\r\n");
|
|
10198
|
+
if (segs.length < 2 || !styled.includes("\x1B")) return segs;
|
|
10199
|
+
const tok = new RegExp(ANSI_TOKEN.source, "g");
|
|
10200
|
+
let open = [];
|
|
10201
|
+
return segs.map((seg) => {
|
|
10202
|
+
const prefix = open.join("");
|
|
10203
|
+
for (const m of seg.match(tok) ?? []) {
|
|
10204
|
+
const code = /^\x1b\[([0-9;]*)m$/.exec(m)?.[1];
|
|
10205
|
+
if (code === void 0) continue;
|
|
10206
|
+
const closes = SGR_CLOSES[code || "0"];
|
|
10207
|
+
if (closes) open = open.filter((o) => !closes.test(/^\x1b\[([0-9;]*)m$/.exec(o)[1]));
|
|
10208
|
+
else open.push(m);
|
|
10209
|
+
}
|
|
10210
|
+
return prefix + seg + (open.length ? "\x1B[0m" : "");
|
|
10211
|
+
});
|
|
10212
|
+
}
|
|
10213
|
+
var T_MINCOL = 2;
|
|
10214
|
+
function fitWidths(natural, avail) {
|
|
10215
|
+
const total = natural.reduce((a, b) => a + b, 0);
|
|
10216
|
+
if (total <= avail || !natural.length) return natural;
|
|
10217
|
+
const room = Math.max(T_MINCOL * natural.length, avail);
|
|
10218
|
+
let cap = 1;
|
|
10219
|
+
for (let c = 1; c <= Math.max(...natural); c++) {
|
|
10220
|
+
if (natural.reduce((a, w2) => a + Math.min(w2, c), 0) <= room) cap = c;
|
|
10221
|
+
else break;
|
|
10222
|
+
}
|
|
10223
|
+
const w = natural.map((n) => Math.min(n, cap));
|
|
10224
|
+
let left = room - w.reduce((a, b) => a + b, 0);
|
|
10225
|
+
for (let i = 0; i < w.length && left > 0; i++) {
|
|
10226
|
+
const take = Math.min(left, natural[i] - w[i]);
|
|
10227
|
+
w[i] += take;
|
|
10228
|
+
left -= take;
|
|
10229
|
+
}
|
|
10230
|
+
return w;
|
|
10231
|
+
}
|
|
10232
|
+
function renderTableLines(rows, p, cols = 80) {
|
|
10233
|
+
const sepIdx = rows.findIndex((r, i) => i > 0 && r.includes("-") && /^\s*\|[\s:|-]+\|\s*$/.test(r));
|
|
10234
|
+
if (sepIdx < 0) return rows.map((r) => renderMdLine(r, false, p, cols).out);
|
|
10235
|
+
const cells = (r) => r.trim().replace(/^\||\|$/g, "").split("|").map((c) => c.trim());
|
|
10236
|
+
const data = rows.filter((_, i) => i !== sepIdx).map(cells);
|
|
10237
|
+
const ncol = Math.max(...data.map((r) => r.length));
|
|
10238
|
+
const natural = Array.from({ length: ncol }, (_, i) => Math.max(...data.map((r) => displayText(r[i] ?? "").length)));
|
|
10239
|
+
const widths = fitWidths(natural, cols - T_PAD.length - T_GAP * (ncol - 1));
|
|
10240
|
+
const grid = data.map((row, ri) => widths.map((w, ci) => {
|
|
10241
|
+
const c = row[ci] ?? "";
|
|
10242
|
+
const styled = ri === 0 ? p.bold(displayText(vis(c))) : mdInline(vis(c), p);
|
|
10243
|
+
return wrapCell(styled, w);
|
|
10244
|
+
}));
|
|
10245
|
+
const airy = grid.slice(1).some((row) => row.some((c) => c.length > 1));
|
|
10246
|
+
const out = [];
|
|
10247
|
+
grid.forEach((col, ri) => {
|
|
10248
|
+
const header = ri === 0;
|
|
10249
|
+
if (airy && ri > 1) out.push("");
|
|
10250
|
+
const height = Math.max(...col.map((c) => c.length));
|
|
10251
|
+
for (let li = 0; li < height; li++) {
|
|
10252
|
+
out.push((T_PAD + widths.map((w, ci) => {
|
|
10253
|
+
const seg = col[ci][li] ?? "";
|
|
10254
|
+
return seg + " ".repeat(Math.max(0, w - visWidth(seg)));
|
|
10255
|
+
}).join(" ".repeat(T_GAP))).replace(/\s+$/, ""));
|
|
10256
|
+
}
|
|
10257
|
+
if (header) out.push(T_PAD + p.dim("\u2500".repeat(widths.reduce((a, b) => a + b, 0) + T_GAP * (ncol - 1))));
|
|
10258
|
+
});
|
|
10259
|
+
return out;
|
|
10260
|
+
}
|
|
10167
10261
|
function isTableRow(line) {
|
|
10168
10262
|
return /^\s*\|.*\|\s*$/.test(line);
|
|
10169
10263
|
}
|
|
10170
10264
|
var ANSI_TOKEN = /\x1b\[[0-9;]*m|\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/;
|
|
10171
10265
|
var WRAP_TOKENS = new RegExp(`${ANSI_TOKEN.source}|[ \\t]+|[^\\s\\x1b]+`, "g");
|
|
10266
|
+
function visWidth(s) {
|
|
10267
|
+
return s.replace(new RegExp(ANSI_TOKEN.source, "g"), "").length;
|
|
10268
|
+
}
|
|
10172
10269
|
function wrapAnsi(s, cols, indent = "") {
|
|
10173
10270
|
if (cols <= 1 || s.replace(new RegExp(ANSI_TOKEN.source, "g"), "").length <= cols) return s;
|
|
10174
10271
|
if (indent.length >= cols) indent = "";
|
|
@@ -10235,29 +10332,11 @@ var MarkdownStream = class {
|
|
|
10235
10332
|
this.cols = Math.max(1, cols);
|
|
10236
10333
|
if (this.buf) this.lineRows = Math.max(1, Math.ceil(this.buf.length / this.cols));
|
|
10237
10334
|
}
|
|
10238
|
-
/** Render the held table block as aligned columns and reset the buffer.
|
|
10239
|
-
* separator row it isn't a real table — emit the rows verbatim (rendered as normal lines) instead. */
|
|
10335
|
+
/** Render the held table block as aligned columns (shared with renderMdBlock) and reset the buffer. */
|
|
10240
10336
|
flushTable() {
|
|
10241
10337
|
const rows = this.tableBuf;
|
|
10242
10338
|
this.tableBuf = [];
|
|
10243
|
-
|
|
10244
|
-
if (sepIdx < 0) return rows.map((r) => renderMdLine(r, false, this.p, this.cols).out + "\r\n").join("");
|
|
10245
|
-
const cells = (r) => r.trim().replace(/^\||\|$/g, "").split("|").map((c) => c.trim());
|
|
10246
|
-
const data = rows.filter((_, i) => i !== sepIdx).map(cells);
|
|
10247
|
-
const ncol = Math.max(...data.map((r) => r.length));
|
|
10248
|
-
const widths = Array.from({ length: ncol }, (_, i) => Math.max(...data.map((r) => displayText(r[i] ?? "").length)));
|
|
10249
|
-
const out = [];
|
|
10250
|
-
data.forEach((row, ri) => {
|
|
10251
|
-
const header = ri === 0;
|
|
10252
|
-
const line = " " + widths.map((w, ci) => {
|
|
10253
|
-
const c = row[ci] ?? "";
|
|
10254
|
-
const styled = header ? this.p.bold(displayText(vis(c))) : mdInline(vis(c), this.p);
|
|
10255
|
-
return styled + " ".repeat(Math.max(0, w - displayText(c).length));
|
|
10256
|
-
}).join(" ").replace(/\s+$/, "");
|
|
10257
|
-
out.push(line);
|
|
10258
|
-
if (header) out.push(" " + this.p.dim("\u2500".repeat(widths.reduce((a, b) => a + b, 0) + 3 * (ncol - 1))));
|
|
10259
|
-
});
|
|
10260
|
-
return out.map((l) => l + "\r\n").join("");
|
|
10339
|
+
return renderTableLines(rows, this.p, this.cols).map((l) => l + "\r\n").join("");
|
|
10261
10340
|
}
|
|
10262
10341
|
feed(text) {
|
|
10263
10342
|
this.buf += text;
|
|
@@ -10484,6 +10563,14 @@ var EditorState = class _EditorState {
|
|
|
10484
10563
|
for (const [ph, full] of this.pastes) if (out.includes(ph)) out = out.split(ph).join(full);
|
|
10485
10564
|
return out;
|
|
10486
10565
|
}
|
|
10566
|
+
/**
|
|
10567
|
+
* A `/cmd` the REPL will DISPATCH — i.e. the line's first word. A slash token elsewhere in the
|
|
10568
|
+
* prompt is prose the model reads, so it completes like an `@path`: no accept-and-run, no
|
|
10569
|
+
* exact-match row. Kept in sync with the REPL's whole-line `/` dispatch via `atLineStart`.
|
|
10570
|
+
*/
|
|
10571
|
+
get runnable() {
|
|
10572
|
+
return this.token.startsWith("/") && atLineStart(this.buf.slice(0, this.cursor), this.token);
|
|
10573
|
+
}
|
|
10487
10574
|
/** Recompute suggestions from the line up to the cursor. */
|
|
10488
10575
|
refresh() {
|
|
10489
10576
|
const r = this.suggest(this.buf.slice(0, this.cursor));
|
|
@@ -10491,7 +10578,7 @@ var EditorState = class _EditorState {
|
|
|
10491
10578
|
this.token = r.token;
|
|
10492
10579
|
this.describe = r.describe;
|
|
10493
10580
|
const exact = r.hits.length === 1 && r.hits[0] === r.token;
|
|
10494
|
-
this.menuOpen = r.hits.length > 0 && (!exact ||
|
|
10581
|
+
this.menuOpen = r.hits.length > 0 && (!exact || this.runnable);
|
|
10495
10582
|
if (this.sel >= r.hits.length) this.sel = 0;
|
|
10496
10583
|
}
|
|
10497
10584
|
// ── Editor undo (Ctrl-_): snapshot before every mutation, pop to restore ──
|
|
@@ -10687,13 +10774,22 @@ var EditorState = class _EditorState {
|
|
|
10687
10774
|
this.hits = [];
|
|
10688
10775
|
this.sel = 0;
|
|
10689
10776
|
}
|
|
10690
|
-
/**
|
|
10777
|
+
/**
|
|
10778
|
+
* Replace the trailing token with the highlighted hit; re-suggest (e.g. `@src/` opens its children).
|
|
10779
|
+
* A finished hit gets a trailing space so the next word is typed straight away (CC/shell parity).
|
|
10780
|
+
* Two exceptions: a DIRECTORY hit (`@src/`) is a waypoint, not a word — a space there would end the
|
|
10781
|
+
* mention mid-path; and an existing following space isn't doubled. A spaced dir rides the quoted
|
|
10782
|
+
* form (`@"my dir/"`), so the `/` test must look past a trailing quote.
|
|
10783
|
+
*/
|
|
10691
10784
|
accept() {
|
|
10692
10785
|
if (!this.menuOpen || !this.hits.length) return false;
|
|
10693
10786
|
const hit = this.hits[this.sel] ?? this.hits[0];
|
|
10694
10787
|
const start = this.cursor - this.token.length;
|
|
10695
|
-
|
|
10696
|
-
|
|
10788
|
+
const rest = this.buf.slice(this.cursor);
|
|
10789
|
+
const isDir = hit.replace(/"$/, "").endsWith("/");
|
|
10790
|
+
const pad = isDir || rest.startsWith(" ") ? "" : " ";
|
|
10791
|
+
this.buf = this.buf.slice(0, start) + hit + pad + rest;
|
|
10792
|
+
this.cursor = start + hit.length + pad.length;
|
|
10697
10793
|
this.refresh();
|
|
10698
10794
|
return true;
|
|
10699
10795
|
}
|
|
@@ -10907,9 +11003,9 @@ function applyKey(s, key, str) {
|
|
|
10907
11003
|
case "return":
|
|
10908
11004
|
case "enter":
|
|
10909
11005
|
if (s.menuOpen) {
|
|
10910
|
-
const
|
|
11006
|
+
const run = s.runnable;
|
|
10911
11007
|
s.accept();
|
|
10912
|
-
return
|
|
11008
|
+
return run ? "submit" : "none";
|
|
10913
11009
|
}
|
|
10914
11010
|
if (s.buf.slice(0, s.cursor).endsWith("\\")) {
|
|
10915
11011
|
s.backspace();
|
|
@@ -12137,8 +12233,17 @@ function makeHost(format = "text", opts) {
|
|
|
12137
12233
|
}
|
|
12138
12234
|
};
|
|
12139
12235
|
const activitySeen = /* @__PURE__ */ new Set();
|
|
12236
|
+
const turnActivityIds = /* @__PURE__ */ new Set();
|
|
12237
|
+
let anonActivityCount = 0;
|
|
12140
12238
|
return {
|
|
12141
12239
|
flushText,
|
|
12240
|
+
/** Read + reset this turn's self-executing-backend tool tally (see turnActivityIds). */
|
|
12241
|
+
takeToolCount: () => {
|
|
12242
|
+
const n = turnActivityIds.size + anonActivityCount;
|
|
12243
|
+
turnActivityIds.clear();
|
|
12244
|
+
anonActivityCount = 0;
|
|
12245
|
+
return n;
|
|
12246
|
+
},
|
|
12142
12247
|
notify(e) {
|
|
12143
12248
|
if (e.kind === "turn_start") {
|
|
12144
12249
|
activitySeen.clear();
|
|
@@ -12171,6 +12276,8 @@ function makeHost(format = "text", opts) {
|
|
|
12171
12276
|
}
|
|
12172
12277
|
if (e.kind === "tool_activity") {
|
|
12173
12278
|
const a = e;
|
|
12279
|
+
if (a.id) turnActivityIds.add(a.id);
|
|
12280
|
+
else if (a.status === "running") anonActivityCount++;
|
|
12174
12281
|
flushLookupGroup();
|
|
12175
12282
|
closeReasonLine();
|
|
12176
12283
|
flushText();
|
|
@@ -12869,6 +12976,7 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd(), sen
|
|
|
12869
12976
|
spinner.turnStart = t0;
|
|
12870
12977
|
spinner.start(sendFn ? "voice\u2026" : void 0);
|
|
12871
12978
|
const msgsBefore = agent.transcript.length;
|
|
12979
|
+
agent.options.host?.takeToolCount?.();
|
|
12872
12980
|
const host = agent.options.host;
|
|
12873
12981
|
const origNotify = host?.notify?.bind(host);
|
|
12874
12982
|
if (host && origNotify) host.notify = (e) => {
|
|
@@ -12910,7 +13018,7 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd(), sen
|
|
|
12910
13018
|
const m = res.usageEstimated ? "~" : "";
|
|
12911
13019
|
const tok = res.usage?.totalTokens ? `${m}${(res.usage.totalTokens / 1e3).toFixed(1)}k tok \xB7 ${cost > 0 ? m + fmtUsd(cost) + " \xB7 " : ""}` : "";
|
|
12912
13020
|
const lastUser = res.messages.map((m2) => m2.role).lastIndexOf("user");
|
|
12913
|
-
const tools = res.messages.slice(lastUser).filter((m2) => m2.role === "tool").length;
|
|
13021
|
+
const tools = res.messages.slice(lastUser).filter((m2) => m2.role === "tool").length + (agent.options.host?.takeToolCount?.() ?? 0);
|
|
12914
13022
|
const ok = res.finishReason === "stop";
|
|
12915
13023
|
const shortId = session.meta.id.replace(/^\d{8}-/, "");
|
|
12916
13024
|
const didWork = agent.transcript.slice(msgsBefore).some((m2) => m2.role === "assistant" || m2.role === "tool");
|
|
@@ -14751,10 +14859,11 @@ ${task}`;
|
|
|
14751
14859
|
return null;
|
|
14752
14860
|
}
|
|
14753
14861
|
};
|
|
14754
|
-
const
|
|
14862
|
+
const agentCommandNames = () => [...cmds.map((c) => c.name), ...skills.map((s) => s.name)];
|
|
14863
|
+
const commandNames = () => [...Object.keys(builtins).filter((k) => k !== "quit"), ...agentCommandNames()];
|
|
14755
14864
|
const describe = (hit) => hit.startsWith("/") ? builtins[hit.slice(1)]?.desc ?? cmds.find((c) => c.name === hit.slice(1))?.description ?? skills.find((s) => s.name === hit.slice(1))?.description : void 0;
|
|
14756
14865
|
const suggest = (lineToCursor) => {
|
|
14757
|
-
const [hits, token] = completeLine(lineToCursor, { commands: commandNames(), listDir, bangHistory: history });
|
|
14866
|
+
const [hits, token] = completeLine(lineToCursor, { commands: commandNames(), agentCommands: agentCommandNames(), listDir, bangHistory: history });
|
|
14758
14867
|
return { hits, token, describe };
|
|
14759
14868
|
};
|
|
14760
14869
|
const editor = createLineEditor(process.stderr);
|