agent.libx.js 0.89.3 → 0.89.5

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 CHANGED
@@ -23,6 +23,25 @@ interface SessionMeta {
23
23
  costUsd?: number;
24
24
  /** Sticky: true if any turn's usage was estimated (streamed without provider usage) → cost is approximate. */
25
25
  costEstimated?: boolean;
26
+ /** Per-turn forensic log: timing, outcome, usage, and provider error per turn — for investigating a reported issue. */
27
+ events?: TurnEvent[];
28
+ }
29
+ /** One turn's diagnostics, captured for offline forensics (mirrors what the footer prints, but persisted). */
30
+ interface TurnEvent {
31
+ ts: number;
32
+ durationMs: number;
33
+ model: string;
34
+ finishReason: string;
35
+ steps: number;
36
+ tools: number;
37
+ tokens?: number;
38
+ costUsd?: number;
39
+ estimated?: boolean;
40
+ error?: {
41
+ message: string;
42
+ statusCode?: number;
43
+ code?: string;
44
+ };
26
45
  }
27
46
  interface SessionData {
28
47
  meta: SessionMeta;
@@ -145,6 +164,7 @@ declare function expandMentions(fs: IFilesystem, line: string): Promise<{
145
164
  }>;
146
165
  /** The headless `--output-format json` result object for a turn. */
147
166
  declare function jsonResult(res: RunResult, session: SessionData): {
167
+ error?: any;
148
168
  ok: boolean;
149
169
  finishReason: "error" | "budget" | "stop" | "max_steps" | "timeout" | "loop" | "max_tool_calls" | "aborted";
150
170
  text: string;
package/dist/cli.js CHANGED
@@ -2805,7 +2805,7 @@ var Agent = class _Agent {
2805
2805
  } catch (err2) {
2806
2806
  if (err2?.code === "budget") return kill("budget");
2807
2807
  if (o.signal?.aborted) return kill("aborted");
2808
- log3.error("chat() failed", err2);
2808
+ log3.error(`chat() failed: ${err2?.message ?? err2}`, err2);
2809
2809
  return { text: "", steps, finishReason: "error", messages: this.transcript, usage, usageEstimated, error: err2 };
2810
2810
  }
2811
2811
  if (o.signal?.aborted) return kill("aborted");
@@ -2869,12 +2869,14 @@ var Agent = class _Agent {
2869
2869
  }
2870
2870
  async dispatch(tc) {
2871
2871
  const tool = this.activeTools.find((t) => t.name === tc.function.name);
2872
- if (!tool) return `Error: unknown tool '${tc.function.name}'`;
2873
2872
  let args = {};
2873
+ let earlyError;
2874
+ if (!tool) earlyError = `Error: unknown tool '${tc.function.name}'`;
2874
2875
  try {
2875
2876
  args = tc.function.arguments ? JSON.parse(tc.function.arguments) : {};
2876
2877
  } catch (e) {
2877
- return `Error: invalid JSON arguments for ${tc.function.name}: ${String(e)}`;
2878
+ args = tc.function.arguments;
2879
+ earlyError ??= `Error: invalid JSON arguments for ${tc.function.name}: ${String(e)}`;
2878
2880
  }
2879
2881
  const hooks = this.activeHooks;
2880
2882
  const call = { name: tc.function.name, args };
@@ -2887,6 +2889,12 @@ var Agent = class _Agent {
2887
2889
  return blocked;
2888
2890
  }
2889
2891
  this.options.host?.notify?.({ kind: "tool_use", id: tc.id ?? "", name: tc.function.name, input: args });
2892
+ if (earlyError) {
2893
+ log3.debug(`${tc.function.name} -> ${earlyError}`);
2894
+ await hooks?.postToolUse?.(call, earlyError, meta);
2895
+ this.options.host?.notify?.({ kind: "tool_result", id: tc.id ?? "", output: earlyError, isError: true });
2896
+ return earlyError;
2897
+ }
2890
2898
  let result;
2891
2899
  let threw = false;
2892
2900
  try {
@@ -4639,6 +4647,137 @@ function completePath(listDir, ref) {
4639
4647
 
4640
4648
  // cli/lineEditor.ts
4641
4649
  import { emitKeypressEvents } from "readline";
4650
+
4651
+ // cli/bidi.ts
4652
+ var RTL_RE = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufdff\ufe70-\ufeff]/;
4653
+ var needsBidi = (s) => RTL_RE.test(s);
4654
+ function classify(c) {
4655
+ if (c >= 1425 && c <= 1469) return "NSM";
4656
+ if (c === 1471 || c === 1473 || c === 1474 || c === 1476 || c === 1477 || c === 1479) return "NSM";
4657
+ if (c >= 1424 && c <= 1535 || c >= 64285 && c <= 64335) return "R";
4658
+ if (c >= 1632 && c <= 1641 || c >= 1776 && c <= 1785) return "AN";
4659
+ if (c >= 1536 && c <= 1791 || c >= 1872 && c <= 1919 || c >= 2208 && c <= 2303 || c >= 64336 && c <= 65023 || c >= 65136 && c <= 65279) return "AL";
4660
+ if (c >= 48 && c <= 57) return "EN";
4661
+ if (c === 43 || c === 45) return "ES";
4662
+ if (c === 35 || c === 36 || c === 37 || c >= 162 && c <= 165) return "ET";
4663
+ if (c === 44 || c === 46 || c === 58 || c === 47 || c === 160) return "CS";
4664
+ if (c === 32 || c === 9 || c === 11 || c === 12) return "WS";
4665
+ if (c >= 33 && c <= 47 || c >= 58 && c <= 64 || c >= 91 && c <= 96 || c >= 123 && c <= 126) return "ON";
4666
+ return "L";
4667
+ }
4668
+ function baseLevel(types) {
4669
+ for (const t of types) {
4670
+ if (t === "L") return 0;
4671
+ if (t === "R" || t === "AL") return 1;
4672
+ }
4673
+ return 0;
4674
+ }
4675
+ function resolveWeak(t, baseDir) {
4676
+ const n = t.length;
4677
+ for (let i = 0; i < n; i++) if (t[i] === "NSM") t[i] = i > 0 ? t[i - 1] : baseDir;
4678
+ for (let i = 0; i < n; i++) if (t[i] === "EN") {
4679
+ for (let j = i - 1; j >= 0; j--) if (t[j] === "L" || t[j] === "R" || t[j] === "AL") {
4680
+ if (t[j] === "AL") t[i] = "AN";
4681
+ break;
4682
+ }
4683
+ }
4684
+ for (let i = 0; i < n; i++) if (t[i] === "AL") t[i] = "R";
4685
+ for (let i = 1; i < n - 1; i++) {
4686
+ if (t[i] === "ES" && t[i - 1] === "EN" && t[i + 1] === "EN") t[i] = "EN";
4687
+ else if (t[i] === "CS" && t[i - 1] === "EN" && t[i + 1] === "EN") t[i] = "EN";
4688
+ else if (t[i] === "CS" && t[i - 1] === "AN" && t[i + 1] === "AN") t[i] = "AN";
4689
+ }
4690
+ for (let i = 0; i < n; i++) if (t[i] === "ET") {
4691
+ let j = i;
4692
+ while (j < n && t[j] === "ET") j++;
4693
+ if (i > 0 && t[i - 1] === "EN" || j < n && t[j] === "EN") for (let k = i; k < j; k++) t[k] = "EN";
4694
+ i = j - 1;
4695
+ }
4696
+ for (let i = 0; i < n; i++) if (t[i] === "ES" || t[i] === "ET" || t[i] === "CS") t[i] = "ON";
4697
+ for (let i = 0; i < n; i++) if (t[i] === "EN") {
4698
+ let strong = baseDir;
4699
+ for (let j = i - 1; j >= 0; j--) if (t[j] === "L" || t[j] === "R") {
4700
+ strong = t[j];
4701
+ break;
4702
+ }
4703
+ if (strong === "L") t[i] = "L";
4704
+ }
4705
+ return t;
4706
+ }
4707
+ function resolveNeutral(t, baseDir) {
4708
+ const n = t.length;
4709
+ const strong = (x) => x === "L" ? "L" : x === "R" || x === "EN" || x === "AN" ? "R" : void 0;
4710
+ for (let i = 0; i < n; i++) if (t[i] === "ON" || t[i] === "WS") {
4711
+ let j = i;
4712
+ while (j < n && (t[j] === "ON" || t[j] === "WS")) j++;
4713
+ const prev = i > 0 ? strong(t[i - 1]) : baseDir;
4714
+ const next = j < n ? strong(t[j]) : baseDir;
4715
+ const dir = prev && next && prev === next ? prev : baseDir;
4716
+ for (let k = i; k < j; k++) t[k] = dir;
4717
+ i = j - 1;
4718
+ }
4719
+ return t;
4720
+ }
4721
+ function implicitLevels(t, e) {
4722
+ return t.map((x) => e % 2 === 0 ? x === "R" ? e + 1 : x === "EN" || x === "AN" ? e + 2 : e : x === "L" || x === "EN" || x === "AN" ? e + 1 : e);
4723
+ }
4724
+ function reorder(levels) {
4725
+ const n = levels.length;
4726
+ const order = Array.from({ length: n }, (_, i) => i);
4727
+ let highest = 0, lowestOdd = Infinity;
4728
+ for (const l of levels) {
4729
+ if (l > highest) highest = l;
4730
+ if (l % 2 && l < lowestOdd) lowestOdd = l;
4731
+ }
4732
+ for (let lvl = highest; lvl >= lowestOdd; lvl--) {
4733
+ let i = 0;
4734
+ while (i < n) {
4735
+ if (levels[i] >= lvl) {
4736
+ let j = i;
4737
+ while (j < n && levels[j] >= lvl) j++;
4738
+ for (let a = i, b = j - 1; a < b; a++, b--) {
4739
+ const tmp = order[a];
4740
+ order[a] = order[b];
4741
+ order[b] = tmp;
4742
+ }
4743
+ i = j;
4744
+ } else i++;
4745
+ }
4746
+ }
4747
+ return order;
4748
+ }
4749
+ function bidiLine(line) {
4750
+ const n = line.length;
4751
+ if (n === 0) return { visual: "", caret: [0] };
4752
+ const types = Array.from({ length: n }, (_, i) => classify(line.charCodeAt(i)));
4753
+ const e = baseLevel(types);
4754
+ const baseDir = e % 2 ? "R" : "L";
4755
+ const levels = implicitLevels(resolveNeutral(resolveWeak(types, baseDir), baseDir), e);
4756
+ const order = reorder(levels);
4757
+ const logToVis = new Array(n);
4758
+ for (let v = 0; v < n; v++) logToVis[order[v]] = v;
4759
+ const caret = new Array(n + 1);
4760
+ for (let c = 0; c < n; c++) caret[c] = levels[c] % 2 === 0 ? logToVis[c] : logToVis[c] + 1;
4761
+ caret[n] = levels[n - 1] % 2 === 0 ? logToVis[n - 1] + 1 : logToVis[n - 1];
4762
+ const visual = order.map((li) => line[li]).join("");
4763
+ return { visual, caret };
4764
+ }
4765
+ function applyBidi(buf, cursor) {
4766
+ if (!needsBidi(buf)) return { text: buf, cursor };
4767
+ const lines = buf.split("\n");
4768
+ const out = [];
4769
+ let visCursor = cursor, logBase = 0, visBase = 0;
4770
+ for (const line of lines) {
4771
+ const { visual, caret } = bidiLine(line);
4772
+ out.push(visual);
4773
+ if (cursor >= logBase && cursor <= logBase + line.length) visCursor = visBase + caret[cursor - logBase];
4774
+ logBase += line.length + 1;
4775
+ visBase += visual.length + 1;
4776
+ }
4777
+ return { text: out.join("\n"), cursor: visCursor };
4778
+ }
4779
+
4780
+ // cli/lineEditor.ts
4642
4781
  var visibleWidth = (s) => s.replace(/\x1b\[[0-9;]*m/g, "").length;
4643
4782
  var isPrintable = (str) => !!str && !/[\x00-\x1f\x7f]/.test(str);
4644
4783
  var EditorState = class _EditorState {
@@ -5210,8 +5349,9 @@ function createLineEditor(out) {
5210
5349
  const prompt = s.searching ? dim2(`(${s.searchMiss ? "failing " : ""}reverse-i-search)\`${s.searchQuery}': `) : mode ? COLOR[mode.name](`${mode.pfx} ${mode.name} \u203A `) : vimTag + promptArg;
5211
5350
  if (curRow > 0) out.write(`\x1B[${curRow}A`);
5212
5351
  out.write("\r\x1B[J");
5213
- const viewBuf = mode ? s.buf.slice(mode.pfx.length) : s.buf;
5214
- const viewCursor = mode ? Math.max(0, s.cursor - mode.pfx.length) : s.cursor;
5352
+ const rawBuf = mode ? s.buf.slice(mode.pfx.length) : s.buf;
5353
+ const rawCursor = mode ? Math.max(0, s.cursor - mode.pfx.length) : s.cursor;
5354
+ const { text: viewBuf, cursor: viewCursor } = applyBidi(rawBuf, rawCursor);
5215
5355
  const { rows, cursorRow, cursorCol } = wrapLayout(visibleWidth(prompt), viewBuf, viewCursor, cols);
5216
5356
  const ghost = !mode && !s.searching ? s.ghost() : "";
5217
5357
  const ghostFits = ghost && rows.length === 1 && visibleWidth(prompt) + viewBuf.length + ghost.length < cols;
@@ -5795,6 +5935,22 @@ function resolveModelOrNewest(model) {
5795
5935
  return fallback;
5796
5936
  }
5797
5937
  var ENV_KEY_ALIASES = { google: ["GEMINI_API_KEY"] };
5938
+ function loadInstallEnv() {
5939
+ let dir = dirname3(import.meta.path);
5940
+ for (let i = 0; i < 5 && !existsSync7(join8(dir, "package.json")); i++) dir = dirname3(dir);
5941
+ for (const name of [".env", ".env.local"]) {
5942
+ const file = join8(dir, name);
5943
+ if (!existsSync7(file)) continue;
5944
+ for (const line of readFileSync5(file, "utf8").split("\n")) {
5945
+ const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
5946
+ if (!m || m[1] in process.env) continue;
5947
+ let val = m[2].trim();
5948
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) val = val.slice(1, -1);
5949
+ else val = val.replace(/\s+#.*$/, "").trim();
5950
+ process.env[m[1]] = val;
5951
+ }
5952
+ }
5953
+ }
5798
5954
  function apiKeysFromEnv() {
5799
5955
  const e = process.env, keys = {};
5800
5956
  for (const provider of listProviders()) {
@@ -5986,6 +6142,9 @@ function costOf(pricing, promptTokens = 0, completionTokens = 0) {
5986
6142
  function turnCost(model, usage) {
5987
6143
  return costOf(getModelInfo(model)?.pricing, usage?.promptTokens ?? 0, usage?.completionTokens ?? 0);
5988
6144
  }
6145
+ function errInfo(e) {
6146
+ return { message: String(e?.message ?? e), statusCode: e?.statusCode, code: e?.code };
6147
+ }
5989
6148
  function fmtUsd(n) {
5990
6149
  return n >= 1 ? `$${n.toFixed(2)}` : `$${n.toFixed(4)}`;
5991
6150
  }
@@ -6210,7 +6369,8 @@ function jsonResult(res, session) {
6210
6369
  steps: res.steps,
6211
6370
  tools: res.messages.slice(lastUser).filter((m) => m.role === "tool").length,
6212
6371
  usage: res.usage,
6213
- sessionId: session.meta.id
6372
+ sessionId: session.meta.id,
6373
+ ...res.finishReason === "error" && res.error ? { error: res.error?.message ?? String(res.error) } : {}
6214
6374
  };
6215
6375
  }
6216
6376
  async function readMultiline(readLine) {
@@ -6253,6 +6413,12 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd()) {
6253
6413
  spinner.stop();
6254
6414
  err(red(` error: ${e?.message ?? e}
6255
6415
  `));
6416
+ (session.meta.events ??= []).push({ ts: t0, durationMs: Date.now() - t0, model: agent.options.model, finishReason: "error", steps: 0, tools: 0, error: errInfo(e) });
6417
+ session.meta.updated = Date.now();
6418
+ try {
6419
+ store.save(session);
6420
+ } catch {
6421
+ }
6256
6422
  return { ok: false };
6257
6423
  } finally {
6258
6424
  spinner.stop();
@@ -6267,8 +6433,13 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd()) {
6267
6433
  const lastUser = res.messages.map((m2) => m2.role).lastIndexOf("user");
6268
6434
  const tools = res.messages.slice(lastUser).filter((m2) => m2.role === "tool").length;
6269
6435
  const ok = res.finishReason === "stop";
6270
- err("\n" + (ok ? green(" \u2713 done") : red(` \u2717 ${res.finishReason}`)) + dim(` \xB7 ${res.steps} steps \xB7 ${tools} tools \xB7 ${tok}${secs}s
6436
+ const shortId = session.meta.id.slice(-10);
6437
+ err("\n" + (ok ? green(" \u2713 done") : red(` \u2717 ${res.finishReason}`)) + dim(` \xB7 ${res.steps} steps \xB7 ${tools} tools \xB7 ${tok}${secs}s \xB7 ${shortId}
6271
6438
  `));
6439
+ if (res.finishReason === "error" && res.error) {
6440
+ const e = res.error;
6441
+ err(red(` ${e?.message ?? e}`) + (e?.statusCode ? dim(` (${e.statusCode}${e.code ? " " + e.code : ""})`) : "") + "\n");
6442
+ }
6272
6443
  session.messages = agent.transcript;
6273
6444
  session.meta.turns += 1;
6274
6445
  session.meta.tokens = (session.meta.tokens ?? 0) + (res.usage?.totalTokens ?? 0);
@@ -6276,6 +6447,9 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd()) {
6276
6447
  if (res.usageEstimated) session.meta.costEstimated = true;
6277
6448
  session.meta.updated = Date.now();
6278
6449
  session.meta.model = agent.options.model;
6450
+ const ev = { ts: t0, durationMs: Date.now() - t0, model: agent.options.model, finishReason: res.finishReason, steps: res.steps, tools, tokens: res.usage?.totalTokens, costUsd: cost, estimated: res.usageEstimated };
6451
+ if (res.finishReason === "error" && res.error) ev.error = errInfo(res.error);
6452
+ (session.meta.events ??= []).push(ev);
6279
6453
  if (!session.meta.title) session.meta.title = titleOf(agent.transcript);
6280
6454
  try {
6281
6455
  store.save(session);
@@ -6307,7 +6481,10 @@ function startSession(args, store, agent, cwd) {
6307
6481
  `));
6308
6482
  }
6309
6483
  const now = Date.now();
6310
- return { meta: { id: args.sessionId ?? store.newId(now), created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
6484
+ const id = args.sessionId ?? store.newId(now);
6485
+ if (!args.task) err(dim(` session ${id}
6486
+ `));
6487
+ return { meta: { id, created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
6311
6488
  }
6312
6489
  var AGENTS_MD_TEMPLATE = `# ${"${name}"}
6313
6490
 
@@ -7082,7 +7259,7 @@ ${extra}` : body, checkpoints, cwd);
7082
7259
  }
7083
7260
  });
7084
7261
  }
7085
- const promptStr = bold(cyan("agent \u203A "));
7262
+ const promptStr = bold(cyan("agentx \u203A "));
7086
7263
  const contPrompt = dim(" \u2026 \u203A ");
7087
7264
  const classifyPaste = pastePathClassifier(cwd);
7088
7265
  const releaseStdin = () => {
@@ -7244,6 +7421,7 @@ async function main() {
7244
7421
  if (!id) process.exit(0);
7245
7422
  args.resume = id;
7246
7423
  }
7424
+ loadInstallEnv();
7247
7425
  const apiKeys = { ...cfg.apiKeys, ...apiKeysFromEnv() };
7248
7426
  if (!Object.keys(apiKeys).length) {
7249
7427
  console.error(red("No provider key found. Set ANTHROPIC_API_KEY (or OPENAI_API_KEY / GOOGLE_API_KEY / GROQ_API_KEY), e.g. in .env."));