agent.libx.js 0.89.4 → 0.89.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/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 {
@@ -3830,6 +3838,11 @@ async function buildAgent(o) {
3830
3838
  const jailedDisk = new JailedFilesystem(disk);
3831
3839
  jailedDisk.setCwd(cwd);
3832
3840
  const virtual = o.sandbox || !!o.boddb;
3841
+ const isCursor = (o.model ?? "").startsWith("cursor/");
3842
+ if (virtual && isCursor)
3843
+ throw new Error(
3844
+ "cursor/* models cannot run in --sandbox/--boddb: the Cursor agent runs its own real-disk tools and bypasses the VFS jail. Use disk mode (default)."
3845
+ );
3833
3846
  let fs = jailedDisk;
3834
3847
  if (o.sandbox) {
3835
3848
  const mem = new MemFilesystem2();
@@ -3891,6 +3904,10 @@ Reference files in them by their mount path (the left side).`;
3891
3904
  ai: o.ai,
3892
3905
  fs,
3893
3906
  model: o.model ?? "anthropic/claude-sonnet-4-6",
3907
+ // Anchor cursor to the launch dir (its adapter defaults to TMPDIR otherwise). Gated to cursor:
3908
+ // openai/google adapters Object.assign providerOptions into the request body, so a blanket cwd
3909
+ // would corrupt those calls.
3910
+ ...isCursor ? { providerOptions: { cwd } } : {},
3894
3911
  ...(() => {
3895
3912
  const now = /* @__PURE__ */ new Date();
3896
3913
  const platformNames = { darwin: "macOS", linux: "Linux", win32: "Windows" };
@@ -4641,7 +4658,7 @@ function completePath(listDir, ref) {
4641
4658
  import { emitKeypressEvents } from "readline";
4642
4659
 
4643
4660
  // cli/bidi.ts
4644
- var RTL_RE = /[֐-׿؀-ۿ܀-ݏݐ-ݿࢠ-ࣿיִ-﷿ﹰ-]/;
4661
+ var RTL_RE = /[\u0590-\u05ff\u0600-\u06ff\u0750-\u077f\u08a0-\u08ff\ufb1d-\ufdff\ufe70-\ufeff]/;
4645
4662
  var needsBidi = (s) => RTL_RE.test(s);
4646
4663
  function classify(c) {
4647
4664
  if (c >= 1425 && c <= 1469) return "NSM";
@@ -5927,6 +5944,22 @@ function resolveModelOrNewest(model) {
5927
5944
  return fallback;
5928
5945
  }
5929
5946
  var ENV_KEY_ALIASES = { google: ["GEMINI_API_KEY"] };
5947
+ function loadInstallEnv() {
5948
+ let dir = dirname3(import.meta.path);
5949
+ for (let i = 0; i < 5 && !existsSync7(join8(dir, "package.json")); i++) dir = dirname3(dir);
5950
+ for (const name of [".env", ".env.local"]) {
5951
+ const file = join8(dir, name);
5952
+ if (!existsSync7(file)) continue;
5953
+ for (const line of readFileSync5(file, "utf8").split("\n")) {
5954
+ const m = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
5955
+ if (!m || m[1] in process.env) continue;
5956
+ let val = m[2].trim();
5957
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) val = val.slice(1, -1);
5958
+ else val = val.replace(/\s+#.*$/, "").trim();
5959
+ process.env[m[1]] = val;
5960
+ }
5961
+ }
5962
+ }
5930
5963
  function apiKeysFromEnv() {
5931
5964
  const e = process.env, keys = {};
5932
5965
  for (const provider of listProviders()) {
@@ -5956,7 +5989,10 @@ function makeHost(format = "text", opts) {
5956
5989
  }
5957
5990
  if (e.kind === "thinking_delta") {
5958
5991
  if (streamJson) process.stdout.write(JSON.stringify({ type: "thinking", text: e.message }) + "\n");
5959
- else if (!cleanStdout) process.stderr.write(dim(e.message));
5992
+ else if (!cleanStdout) {
5993
+ if (md && md.pending()) process.stdout.write(md.flush() + "\n");
5994
+ process.stderr.write(dim(e.message));
5995
+ }
5960
5996
  return;
5961
5997
  }
5962
5998
  if (md && md.pending()) process.stdout.write(md.flush() + "\n");
@@ -6118,6 +6154,9 @@ function costOf(pricing, promptTokens = 0, completionTokens = 0) {
6118
6154
  function turnCost(model, usage) {
6119
6155
  return costOf(getModelInfo(model)?.pricing, usage?.promptTokens ?? 0, usage?.completionTokens ?? 0);
6120
6156
  }
6157
+ function errInfo(e) {
6158
+ return { message: String(e?.message ?? e), statusCode: e?.statusCode, code: e?.code };
6159
+ }
6121
6160
  function fmtUsd(n) {
6122
6161
  return n >= 1 ? `$${n.toFixed(2)}` : `$${n.toFixed(4)}`;
6123
6162
  }
@@ -6342,7 +6381,8 @@ function jsonResult(res, session) {
6342
6381
  steps: res.steps,
6343
6382
  tools: res.messages.slice(lastUser).filter((m) => m.role === "tool").length,
6344
6383
  usage: res.usage,
6345
- sessionId: session.meta.id
6384
+ sessionId: session.meta.id,
6385
+ ...res.finishReason === "error" && res.error ? { error: res.error?.message ?? String(res.error) } : {}
6346
6386
  };
6347
6387
  }
6348
6388
  async function readMultiline(readLine) {
@@ -6385,6 +6425,12 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd()) {
6385
6425
  spinner.stop();
6386
6426
  err(red(` error: ${e?.message ?? e}
6387
6427
  `));
6428
+ (session.meta.events ??= []).push({ ts: t0, durationMs: Date.now() - t0, model: agent.options.model, finishReason: "error", steps: 0, tools: 0, error: errInfo(e) });
6429
+ session.meta.updated = Date.now();
6430
+ try {
6431
+ store.save(session);
6432
+ } catch {
6433
+ }
6388
6434
  return { ok: false };
6389
6435
  } finally {
6390
6436
  spinner.stop();
@@ -6399,8 +6445,13 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd()) {
6399
6445
  const lastUser = res.messages.map((m2) => m2.role).lastIndexOf("user");
6400
6446
  const tools = res.messages.slice(lastUser).filter((m2) => m2.role === "tool").length;
6401
6447
  const ok = res.finishReason === "stop";
6402
- err("\n" + (ok ? green(" \u2713 done") : red(` \u2717 ${res.finishReason}`)) + dim(` \xB7 ${res.steps} steps \xB7 ${tools} tools \xB7 ${tok}${secs}s
6448
+ const shortId = session.meta.id.slice(-10);
6449
+ err("\n" + (ok ? green(" \u2713 done") : red(` \u2717 ${res.finishReason}`)) + dim(` \xB7 ${res.steps} steps \xB7 ${tools} tools \xB7 ${tok}${secs}s \xB7 ${shortId}
6403
6450
  `));
6451
+ if (res.finishReason === "error" && res.error) {
6452
+ const e = res.error;
6453
+ err(red(` ${e?.message ?? e}`) + (e?.statusCode ? dim(` (${e.statusCode}${e.code ? " " + e.code : ""})`) : "") + "\n");
6454
+ }
6404
6455
  session.messages = agent.transcript;
6405
6456
  session.meta.turns += 1;
6406
6457
  session.meta.tokens = (session.meta.tokens ?? 0) + (res.usage?.totalTokens ?? 0);
@@ -6408,6 +6459,9 @@ async function runTurn(agent, store, session, task, cp, cwd = process.cwd()) {
6408
6459
  if (res.usageEstimated) session.meta.costEstimated = true;
6409
6460
  session.meta.updated = Date.now();
6410
6461
  session.meta.model = agent.options.model;
6462
+ 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 };
6463
+ if (res.finishReason === "error" && res.error) ev.error = errInfo(res.error);
6464
+ (session.meta.events ??= []).push(ev);
6411
6465
  if (!session.meta.title) session.meta.title = titleOf(agent.transcript);
6412
6466
  try {
6413
6467
  store.save(session);
@@ -6439,7 +6493,10 @@ function startSession(args, store, agent, cwd) {
6439
6493
  `));
6440
6494
  }
6441
6495
  const now = Date.now();
6442
- return { meta: { id: args.sessionId ?? store.newId(now), created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
6496
+ const id = args.sessionId ?? store.newId(now);
6497
+ if (!args.task) err(dim(` session ${id}
6498
+ `));
6499
+ return { meta: { id, created: now, updated: now, cwd, model: agent.options.model, turns: 0, title: "" }, messages: [] };
6443
6500
  }
6444
6501
  var AGENTS_MD_TEMPLATE = `# ${"${name}"}
6445
6502
 
@@ -7214,7 +7271,7 @@ ${extra}` : body, checkpoints, cwd);
7214
7271
  }
7215
7272
  });
7216
7273
  }
7217
- const promptStr = bold(cyan("agent \u203A "));
7274
+ const promptStr = bold(cyan("agentx \u203A "));
7218
7275
  const contPrompt = dim(" \u2026 \u203A ");
7219
7276
  const classifyPaste = pastePathClassifier(cwd);
7220
7277
  const releaseStdin = () => {
@@ -7376,6 +7433,7 @@ async function main() {
7376
7433
  if (!id) process.exit(0);
7377
7434
  args.resume = id;
7378
7435
  }
7436
+ loadInstallEnv();
7379
7437
  const apiKeys = { ...cfg.apiKeys, ...apiKeysFromEnv() };
7380
7438
  if (!Object.keys(apiKeys).length) {
7381
7439
  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."));