@livx.cc/agentx 0.99.73 → 0.99.75

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/index.d.ts CHANGED
@@ -1,15 +1,15 @@
1
- import { a as AgentOptions, H as Hooks, n as RunResult, A as Agent, g as PermissionPolicy } from './Agent-CSFGRC-I.js';
2
- export { C as ChatFragment, b as Classification, c as Classifier, d as CompactResult, D as DEFAULT_MUTATING, e as Decision, M as ModelRouter, f as ModelRouterOptions, P as PermissionOptions, h as PermissionRule, i as PreToolUseDecision, R as ReasoningEffort, j as RecordingHooks, k as RecordingLifecycle, l as RouteDecision, m as RouterState, T as Tier, o as ToolUse, p as ToolUseMeta, q as claudeCodeDisallowedTools, r as composeHooks, s as estimateTokens, t as jevAvailable, u as jevClassifier, v as pickTier, w as planMode, x as reasoningToChatFragment } from './Agent-CSFGRC-I.js';
1
+ import { a as AgentOptions, H as Hooks, n as RunResult, A as Agent, g as PermissionPolicy } from './Agent-BBlKI8uD.js';
2
+ export { C as ChatFragment, b as Classification, c as Classifier, d as CompactResult, D as DEFAULT_MUTATING, e as Decision, M as ModelRouter, f as ModelRouterOptions, P as PermissionOptions, h as PermissionRule, i as PreToolUseDecision, R as ReasoningEffort, j as RecordingHooks, k as RecordingLifecycle, l as RouteDecision, m as RouterState, T as Tier, o as ToolUse, p as ToolUseMeta, q as claudeCodeDisallowedTools, r as composeHooks, s as estimateTokens, t as jevAvailable, u as jevClassifier, v as pickTier, w as planMode, x as reasoningToChatFragment } from './Agent-BBlKI8uD.js';
3
3
  export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
4
4
  import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
5
5
  export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
6
6
  import { BodDB } from '@bod.ee/db';
7
- import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, r as ToolCall, H as HostBridge, U as UserQuestion, o as MessageContent } from './tools-wVQBhYwm.js';
8
- export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, J as JobCtx, g as JobHandle, h as JobInfo, i as JobKind, j as JobNotice, k as JobRegistry, l as JobRegistryOptions, n as JobTrackOptions, M as Message, m as RegistryJobStatus, R as Role, S as SandboxJobRegistry, p as StreamChunk, T as TodoItem, q as Tool, s as ToolContext, t as bashTool, u as clearImageRefs, v as contentBytes, w as contentText, x as defaultTools, y as editTool, z as elideImages, B as elideInlineImages, D as elideStaleImages, E as exitSessionTool, F as expandImagesForSend, G as formatJobNotice, K as imagePart, L as imageRefPart, N as imageRefResult, O as makeContext, P as makeJobTools, Q as messageHasImage, V as messageHasImageRef, W as messageHasInlineImage, X as mintImageRef, Y as parseImageRef, Z as readTool, _ as remintImageRefs, $ as sendBytes, a0 as sendBytesPer, a1 as toWireTools, a2 as todoWriteTool, a3 as toolImageRef, a4 as toolRegistry, a5 as toolsByName } from './tools-wVQBhYwm.js';
7
+ import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, r as ToolCall, H as HostBridge, U as UserQuestion, o as MessageContent } from './tools-lC3jn60o.js';
8
+ export { c as ContentPart, d as HostEvent, I as IMAGE_ELIDE_STUB, e as IMAGE_REF_SCHEME, f as ImageRef, J as JobCtx, g as JobHandle, h as JobInfo, i as JobKind, j as JobNotice, k as JobRegistry, l as JobRegistryOptions, n as JobTrackOptions, M as Message, m as RegistryJobStatus, R as Role, S as SandboxJobRegistry, p as StreamChunk, T as TodoItem, q as Tool, s as ToolContext, t as bashTool, u as clearImageRefs, v as contentBytes, w as contentText, x as defaultTools, y as editTool, z as elideImages, B as elideInlineImages, D as elideStaleImages, E as exitSessionTool, F as expandImagesForSend, G as formatJobNotice, K as imagePart, L as imageRefPart, N as imageRefResult, O as makeContext, P as makeJobTools, Q as messageHasImage, V as messageHasImageRef, W as messageHasInlineImage, X as mintImageRef, Y as parseImageRef, Z as readTool, _ as remintImageRefs, $ as sendBytes, a0 as sendBytesPer, a1 as toWireTools, a2 as todoWriteTool, a3 as toolImageRef, a4 as toolRegistry, a5 as toolsByName } from './tools-lC3jn60o.js';
9
9
  import { ShellJobRegistry } from './tools.shell.js';
10
10
  import { SpokenSplitter } from '@bod.ee/voice';
11
11
  export { AudioSink, AudioSource, AuthProvider, CartesiaTTS, CartesiaTTSOptions, STT_SAMPLE_RATE, SonioxSTT, SonioxSTTOptions, SttLike, TTS_SAMPLE_RATE, TtsLike, VoiceEngine, VoiceEngineOptions, VoiceState, resolveAuth } from '@bod.ee/voice';
12
- export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-8_lBApgs.js';
12
+ export { M as McpCall, a as McpImage, b as McpRoute, c as McpRouteResolver, d as McpToolResult, e as McpToolSearchOptions, f as McpToolSpec, g as MountedMcpLike, h as buildMcpCatalog, m as makeLazyMcpToolSearch, i as makeMcpToolSearch, j as makeMcpToolSearchFromMounted, k as mcpToolToAgentTool, l as mcpToolsToAgentTools } from './mcp-BhTxN2I7.js';
13
13
  import * as libx_js_src_modules_log from 'libx.js/src/modules/log';
14
14
  export { log } from 'libx.js/src/modules/log';
15
15
 
package/dist/index.js CHANGED
@@ -19,134 +19,14 @@ var init_logging = __esm({
19
19
  });
20
20
 
21
21
  // src/imageWire.ts
22
- import { readFileSync, writeFileSync, statSync, mkdtempSync, rmSync } from "fs";
23
- import { spawnSync } from "child_process";
24
- import { createHash } from "crypto";
25
- import { tmpdir } from "os";
26
- import { join } from "path";
27
- import { PNG } from "pngjs";
28
- import jpeg from "jpeg-js";
29
- function imageSize(b) {
30
- const u16 = (i) => b[i] << 8 | b[i + 1];
31
- const le16 = (i) => b[i] | b[i + 1] << 8;
32
- const u32 = (i) => (b[i] << 24 >>> 0) + (b[i + 1] << 16) + (b[i + 2] << 8) + b[i + 3];
33
- if (b[0] === 137 && b[1] === 80 && b.length >= 24) return { w: u32(16), h: u32(20) };
34
- if (b[0] === 71 && b[1] === 73 && b.length >= 10) return { w: le16(6), h: le16(8) };
35
- if (b[0] === 255 && b[1] === 216) {
36
- for (let i = 2; i + 9 < b.length; ) {
37
- if (b[i] !== 255) return null;
38
- const m = b[i + 1];
39
- if (m >= 192 && m <= 207 && m !== 196 && m !== 200 && m !== 204) return { w: u16(i + 7), h: u16(i + 5) };
40
- i += 2 + u16(i + 2);
41
- }
42
- return null;
43
- }
44
- if (b.length >= 30 && String.fromCharCode(...b.subarray(8, 12)) === "WEBP") {
45
- const kind = String.fromCharCode(...b.subarray(12, 16));
46
- if (kind === "VP8X") return { w: 1 + (b[24] | b[25] << 8 | b[26] << 16), h: 1 + (b[27] | b[28] << 8 | b[29] << 16) };
47
- if (kind === "VP8 ") return { w: le16(26) & 16383, h: le16(28) & 16383 };
48
- if (kind === "VP8L") {
49
- const v = b[21] | b[22] << 8 | b[23] << 16 | b[24] << 24;
50
- return { w: (v & 16383) + 1, h: (v >> 14 & 16383) + 1 };
51
- }
52
- }
53
- return null;
54
- }
55
- function imageNote(what, bytes, dims, why) {
56
- return `[image ${what}${dims ? `, ${dims.w}x${dims.h}` : ""}, ${fmtBytes(bytes)} \u2014 not sent: ${why}]`;
57
- }
58
- function downscaleJs(buf, maxSide) {
59
- let src;
60
- try {
61
- if (buf[0] === 137 && buf[1] === 80) src = PNG.sync.read(buf);
62
- else if (buf[0] === 255 && buf[1] === 216) src = jpeg.decode(buf, { useTArray: true, maxMemoryUsageInMB: 1024 });
63
- else return null;
64
- } catch (e) {
65
- log2.warn("pure-JS image decode failed", e);
66
- return null;
67
- }
68
- const { width: sw, height: sh, data: s } = src;
69
- const k = Math.min(1, maxSide / Math.max(sw, sh));
70
- const w = Math.max(1, Math.round(sw * k)), h = Math.max(1, Math.round(sh * k));
71
- const d = new Uint8Array(w * h * 4);
72
- for (let y = 0; y < h; y++) {
73
- const y0 = Math.floor(y * sh / h), y1 = Math.max(y0 + 1, Math.floor((y + 1) * sh / h));
74
- for (let x = 0; x < w; x++) {
75
- const x0 = Math.floor(x * sw / w), x1 = Math.max(x0 + 1, Math.floor((x + 1) * sw / w));
76
- let r = 0, g = 0, b = 0, a = 0;
77
- for (let yy = y0; yy < y1; yy++) for (let xx = x0; xx < x1; xx++) {
78
- const i = (yy * sw + xx) * 4, al = s[i + 3];
79
- r += s[i] * al;
80
- g += s[i + 1] * al;
81
- b += s[i + 2] * al;
82
- a += al;
83
- }
84
- const o = (y * w + x) * 4, n = (y1 - y0) * (x1 - x0), bg = 255 * (n - a / 255);
85
- d[o] = (r / 255 + bg) / n;
86
- d[o + 1] = (g / 255 + bg) / n;
87
- d[o + 2] = (b / 255 + bg) / n;
88
- d[o + 3] = 255;
89
- }
90
- }
91
- let out = Buffer.alloc(0);
92
- for (const q of [80, 60, 40]) {
93
- out = Buffer.from(jpeg.encode({ width: w, height: h, data: d }, q).data);
94
- if (out.length <= IMAGE_MAX_BYTES) break;
95
- }
96
- return out;
97
- }
98
- function downscale(path, maxSide) {
99
- const dir = mkdtempSync(join(tmpdir(), "agentx-img-"));
100
- const out = join(dir, "x.jpg");
101
- try {
102
- const tries = [
103
- ["sips", ["-Z", String(maxSide), "-s", "format", "jpeg", "-s", "formatOptions", "80", path, "--out", out]],
104
- ["magick", [path, "-resize", `${maxSide}x${maxSide}>`, "-quality", "80", out]],
105
- ["convert", [path, "-resize", `${maxSide}x${maxSide}>`, "-quality", "80", out]]
106
- ];
107
- for (const [cmd, args] of tries) {
108
- const r = spawnSync(cmd, args, { stdio: "ignore", timeout: 15e3 });
109
- if (r.status !== 0) continue;
110
- try {
111
- const o = readFileSync(out);
112
- if (o.length <= IMAGE_MAX_BYTES) return o;
113
- break;
114
- } catch {
115
- }
116
- }
117
- const js = downscaleJs(readFileSync(path), maxSide);
118
- if (!js) log2.warn(`could not downscale ${path} (tried sips/magick/convert/pure JS)`);
119
- return js;
120
- } finally {
121
- rmSync(dir, { recursive: true, force: true });
122
- }
123
- }
22
+ import { readFileSync, statSync } from "fs";
23
+ import { imageWireOf, inlineImageForWire, scrubBase64Text, imageSize, imageNote, IMAGE_MAX_SIDE, IMAGE_MAX_BYTES } from "ai.libx.js";
124
24
  function remember(id, w) {
125
25
  cache.delete(id);
126
26
  cache.set(id, w);
127
27
  if (cache.size > CACHE_MAX) cache.delete(cache.keys().next().value);
128
28
  return w;
129
29
  }
130
- function wireOf(key, buf, mime, label, file) {
131
- const dims = imageSize(buf);
132
- const fits = buf.length <= IMAGE_MAX_BYTES && (!dims || Math.max(dims.w, dims.h) <= IMAGE_MAX_SIDE);
133
- let small = null;
134
- if (!fits) {
135
- if (file) small = downscale(file, IMAGE_MAX_SIDE);
136
- else {
137
- const dir = mkdtempSync(join(tmpdir(), "agentx-img-in-"));
138
- try {
139
- const f = join(dir, `in.${mime.split("/")[1] || "img"}`);
140
- writeFileSync(f, buf);
141
- small = downscale(f, IMAGE_MAX_SIDE);
142
- } finally {
143
- rmSync(dir, { recursive: true, force: true });
144
- }
145
- }
146
- }
147
- const url = fits ? `data:${mime};base64,${buf.toString("base64")}` : small && small.length <= IMAGE_MAX_BYTES ? `data:image/jpeg;base64,${small.toString("base64")}` : null;
148
- return { key, url, note: imageNote(label, buf.length, dims, url ? "this model can't see images" : "too large and it couldn't be downscaled") };
149
- }
150
30
  function imageForWire(path, mime, vision) {
151
31
  let st;
152
32
  try {
@@ -157,48 +37,30 @@ function imageForWire(path, mime, vision) {
157
37
  }
158
38
  const key = `${st.mtimeMs}:${st.size}`;
159
39
  let hit = cache.get(path);
160
- if (!hit || hit.key !== key) hit = remember(path, wireOf(key, readFileSync(path), mime, path, path));
40
+ if (!hit || hit.key !== key) hit = remember(path, { key, ...imageWireOf(readFileSync(path), mime, path, path) });
161
41
  return vision && hit.url ? { url: hit.url } : { note: hit.note };
162
42
  }
163
- function inlineImageForWire(url, vision) {
164
- const comma = url.indexOf(",");
165
- const mime = url.slice(5, url.indexOf(";"));
166
- if (vision && url.length < 3e5) {
167
- const b64 = url.slice(comma + 1);
168
- const dims = imageSize(Buffer.from(b64.slice(0, 4096), "base64")) ?? imageSize(Buffer.from(b64, "base64"));
169
- if (!dims || Math.max(dims.w, dims.h) <= IMAGE_MAX_SIDE) return { url };
170
- }
171
- const id = `inline:${createHash("sha1").update(url).digest("base64")}`;
172
- let hit = cache.get(id);
173
- if (!hit) {
174
- const buf = Buffer.from(url.slice(comma + 1), "base64");
175
- hit = remember(id, wireOf(id, buf, mime, `(${mime})`));
176
- }
177
- return vision && hit.url ? { url: hit.url } : { note: hit.note };
43
+ function imageTokens(dims) {
44
+ return dims ? Math.min(IMAGE_TOKENS_MAX, Math.max(1, Math.ceil(dims.w * dims.h / 750))) : IMAGE_TOKENS_MAX;
178
45
  }
179
- function imageWireBytes(path, mime, vision) {
180
- const r = imageForWire(path, mime, vision);
181
- return !r ? 0 : "url" in r ? r.url.length : r.note.length;
46
+ function wiredImageWeight(r) {
47
+ if ("note" in r) return r.note.length;
48
+ const b64 = r.url.slice(r.url.indexOf(",") + 1);
49
+ return 4 * imageTokens(imageSize(Buffer.from(b64.slice(0, 4096), "base64")));
182
50
  }
183
- function scrubBase64Text(s) {
184
- if (s.length < 2e3) return s;
185
- const out = s.replace(DATA_URI, (m) => imageNote(m.slice(5, m.indexOf(";")), Math.floor((m.length - m.indexOf(",") - 1) * 3 / 4), null, "base64 is never sent as text")).replace(B64_RUN, (m) => !/[+/]/.test(m) || !/\d/.test(m) ? m : `[base64 data, ${fmtBytes(Math.floor(m.length * 3 / 4))} \u2014 not sent: base64 is never sent as text]`);
186
- if (out !== s) log2.warn(`scrubbed base64 from request text (${s.length} \u2192 ${out.length} chars)`);
187
- return out;
51
+ function imageWireWeight(path, mime, vision) {
52
+ const r = imageForWire(path, mime, vision);
53
+ return r ? wiredImageWeight(r) : 0;
188
54
  }
189
- var log2, IMAGE_MAX_SIDE, IMAGE_MAX_BYTES, fmtBytes, cache, CACHE_MAX, DATA_URI, B64_RUN;
55
+ var log2, cache, CACHE_MAX, IMAGE_TOKENS_MAX;
190
56
  var init_imageWire = __esm({
191
57
  "src/imageWire.ts"() {
192
58
  "use strict";
193
59
  init_logging();
194
60
  log2 = forComponent("imageWire");
195
- IMAGE_MAX_SIDE = 1568;
196
- IMAGE_MAX_BYTES = 1e6;
197
- fmtBytes = (n) => n >= 1e6 ? `${(n / 1e6).toFixed(1)} MB` : n >= 1e3 ? `${Math.round(n / 1e3)} KB` : `${n} B`;
198
61
  cache = /* @__PURE__ */ new Map();
199
62
  CACHE_MAX = 8;
200
- DATA_URI = /data:image\/[\w.+-]+;base64,[A-Za-z0-9+/=]+/g;
201
- B64_RUN = /[A-Za-z0-9+/=]{2000,}/g;
63
+ IMAGE_TOKENS_MAX = 1600;
202
64
  }
203
65
  });
204
66
 
@@ -396,7 +258,7 @@ function sendBytesPer(messages, keepRecent = 1, vision = true, stub = IMAGE_ELID
396
258
  }
397
259
  function wireBytes(content, kept, vision, stub) {
398
260
  const tool = toolImageRef(content);
399
- if (tool) return kept ? imageWireBytes(tool.path, tool.mime, vision) || stub.length : stub.length;
261
+ if (tool) return kept ? imageWireWeight(tool.path, tool.mime, vision) || stub.length : stub.length;
400
262
  if (content == null || typeof content === "string") return contentBytes(content);
401
263
  if (kept) {
402
264
  let n2 = 0;
@@ -404,7 +266,7 @@ function wireBytes(content, kept, vision, stub) {
404
266
  const url = p.type === "image_url" ? p.image_url?.url ?? "" : "";
405
267
  const ref = url ? parseImageRef(url) : null;
406
268
  const inline = url.startsWith("data:") ? inlineImageForWire(url, vision) : null;
407
- n2 += ref ? imageWireBytes(ref.path, ref.mime, vision) : inline ? ("url" in inline ? inline.url : inline.note).length : contentBytes([p]);
269
+ n2 += ref ? imageWireWeight(ref.path, ref.mime, vision) : inline ? wiredImageWeight(inline) : contentBytes([p]);
408
270
  }
409
271
  return n2;
410
272
  }
@@ -3642,8 +3504,8 @@ ${sections.join("\n\n---\n\n")}`;
3642
3504
 
3643
3505
  // src/subagent.ts
3644
3506
  init_tools();
3645
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
3646
- import { join as join4 } from "path";
3507
+ import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
3508
+ import { join as join3 } from "path";
3647
3509
 
3648
3510
  // src/models.ts
3649
3511
  var MODEL_ALIASES = {
@@ -3840,9 +3702,9 @@ function composeHooks(...list) {
3840
3702
  init_OverlayFilesystem();
3841
3703
 
3842
3704
  // src/worktree.ts
3843
- import { spawnSync as spawnSync2 } from "child_process";
3844
- import { symlinkSync, mkdirSync, statSync as statSync3, readFileSync as readFileSync3, rmSync as rmSync2 } from "fs";
3845
- import { join as join2 } from "path";
3705
+ import { spawnSync } from "child_process";
3706
+ import { symlinkSync, mkdirSync, statSync as statSync3, readFileSync as readFileSync3, rmSync } from "fs";
3707
+ import { join } from "path";
3846
3708
  var VALID_SEGMENT = /^[a-zA-Z0-9._-]+$/;
3847
3709
  var MAX_SLUG_LENGTH = 64;
3848
3710
  var GIT_NO_PROMPT_ENV = { GIT_TERMINAL_PROMPT: "0", GIT_ASKPASS: "" };
@@ -3861,13 +3723,13 @@ function worktreeBranchName(slug2) {
3861
3723
  return `worktree-${flattenSlug(slug2)}`;
3862
3724
  }
3863
3725
  function worktreesDir(repoRoot) {
3864
- return join2(repoRoot, ".claude", "worktrees");
3726
+ return join(repoRoot, ".claude", "worktrees");
3865
3727
  }
3866
3728
  function worktreePathFor(repoRoot, slug2) {
3867
- return join2(worktreesDir(repoRoot), flattenSlug(slug2));
3729
+ return join(worktreesDir(repoRoot), flattenSlug(slug2));
3868
3730
  }
3869
3731
  function git(args, cwd) {
3870
- const r = spawnSync2("git", args, {
3732
+ const r = spawnSync("git", args, {
3871
3733
  cwd,
3872
3734
  stdio: ["ignore", "pipe", "pipe"],
3873
3735
  env: { ...process.env, ...GIT_NO_PROMPT_ENV }
@@ -3880,14 +3742,14 @@ function findGitRoot(from) {
3880
3742
  }
3881
3743
  function readWorktreeHead(worktreePath) {
3882
3744
  try {
3883
- const gitFile = readFileSync3(join2(worktreePath, ".git"), "utf-8").trim();
3745
+ const gitFile = readFileSync3(join(worktreePath, ".git"), "utf-8").trim();
3884
3746
  const m = gitFile.match(/^gitdir:\s*(.+)$/);
3885
3747
  if (!m) return null;
3886
- const headPath = join2(m[1], "HEAD");
3748
+ const headPath = join(m[1], "HEAD");
3887
3749
  const head = readFileSync3(headPath, "utf-8").trim();
3888
3750
  const refMatch = head.match(/^ref:\s*(.+)$/);
3889
3751
  if (!refMatch) return head.length === 40 ? head : null;
3890
- const refPath = join2(m[1], "..", refMatch[1]);
3752
+ const refPath = join(m[1], "..", refMatch[1]);
3891
3753
  try {
3892
3754
  return readFileSync3(refPath, "utf-8").trim();
3893
3755
  } catch {
@@ -3922,8 +3784,8 @@ function getOrCreateWorktree(repoRoot, slug2) {
3922
3784
  }
3923
3785
  function symlinkDirs(repoRoot, worktreePath, dirs) {
3924
3786
  for (const dir of dirs) {
3925
- const src = join2(repoRoot, dir);
3926
- const dst = join2(worktreePath, dir);
3787
+ const src = join(repoRoot, dir);
3788
+ const dst = join(worktreePath, dir);
3927
3789
  try {
3928
3790
  statSync3(src);
3929
3791
  } catch {
@@ -3951,7 +3813,7 @@ function cleanupWorktree(repoRoot, slug2, opts) {
3951
3813
  }
3952
3814
  for (const dir of ["node_modules", ".bun"]) {
3953
3815
  try {
3954
- rmSync2(join2(worktreePath, dir));
3816
+ rmSync(join(worktreePath, dir));
3955
3817
  } catch {
3956
3818
  }
3957
3819
  }
@@ -4063,7 +3925,7 @@ function traceFileFor(dir, label, depth) {
4063
3925
  try {
4064
3926
  mkdirSync2(dir, { recursive: true });
4065
3927
  const id = `${Date.now().toString(36)}-${(traceSeq++).toString(36)}`;
4066
- return join4(dir, `${id}-d${depth}-${sanitizeSlug(label) || "sub"}.json`);
3928
+ return join3(dir, `${id}-d${depth}-${sanitizeSlug(label) || "sub"}.json`);
4067
3929
  } catch (e) {
4068
3930
  log7.warn(`subagent trace dir unavailable (${dir}) \u2014 running without a trace: ${e instanceof Error ? e.message : String(e)}`);
4069
3931
  return void 0;
@@ -4071,7 +3933,7 @@ function traceFileFor(dir, label, depth) {
4071
3933
  }
4072
3934
  function flushTrace(file, meta, messages, status) {
4073
3935
  try {
4074
- writeFileSync2(file, JSON.stringify({ ...meta, status, updated: Date.now(), messages }, null, 2));
3936
+ writeFileSync(file, JSON.stringify({ ...meta, status, updated: Date.now(), messages }, null, 2));
4075
3937
  } catch (e) {
4076
3938
  log7.warn(`subagent trace flush failed (${file}): ${e instanceof Error ? e.message : String(e)}`);
4077
3939
  }
@@ -5005,6 +4867,7 @@ var Agent = class _Agent {
5005
4867
  let steps = 0;
5006
4868
  const usage = { promptTokens: 0, completionTokens: 0, totalTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
5007
4869
  let usageEstimated = false;
4870
+ let spent = 0, prevPrompt = -1, stepPrompt = 0;
5008
4871
  const start = Date.now();
5009
4872
  this.parkedMs = 0;
5010
4873
  this.ctx.needsInput = void 0;
@@ -5015,12 +4878,12 @@ var Agent = class _Agent {
5015
4878
  let closureNudged = false;
5016
4879
  let lastProgressAt = start;
5017
4880
  const kill = (finishReason) => {
5018
- log8.warn(`kill-switch: ${finishReason} (steps=${steps}, tokens=${usage.totalTokens}, budgetTokens=${Math.round(usage.totalTokens - 0.9 * usage.cacheReadTokens)}, ms=${Date.now() - start - this.parkedMs}${finishReason === "timeout" ? `, idle=${Date.now() - lastProgressAt - this.parkedMs}ms` : ""}${this.parkedMs ? ` +${this.parkedMs} parked` : ""})`);
4881
+ log8.warn(`kill-switch: ${finishReason} (steps=${steps}, tokens=${usage.totalTokens}, budgetTokens=${Math.round(spent)}, ms=${Date.now() - start - this.parkedMs}${finishReason === "timeout" ? `, idle=${Date.now() - lastProgressAt - this.parkedMs}ms` : ""}${this.parkedMs ? ` +${this.parkedMs} parked` : ""})`);
5019
4882
  this.jobs.killAll(`run ended: ${finishReason}`, true);
5020
4883
  const claims = this.survivingClaims();
5021
- return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...claims.length ? { fabricatedToolClaims: claims } : {} };
4884
+ return { text: lastAssistantText(this.transcript), steps, finishReason, messages: this.transcript, usage, usageEstimated, ...finishReason === "budget" ? { budget: { spent: Math.round(spent), limit: o.maxTokens, stepPrompt } } : {}, ...claims.length ? { fabricatedToolClaims: claims } : {} };
5022
4885
  };
5023
- const overBudget = () => !!o.maxTokens && usage.totalTokens - 0.9 * usage.cacheReadTokens >= o.maxTokens;
4886
+ const overBudget = () => !!o.maxTokens && spent >= o.maxTokens;
5024
4887
  const canNudge = () => steps < o.maxSteps && !overBudget();
5025
4888
  while (true) {
5026
4889
  if (o.signal?.aborted) return kill("aborted");
@@ -5174,6 +5037,14 @@ var Agent = class _Agent {
5174
5037
  usage.totalTokens += res.usage.totalTokens ?? 0;
5175
5038
  usage.cacheCreationTokens += res.usage.cacheCreationTokens ?? 0;
5176
5039
  usage.cacheReadTokens += res.usage.cacheReadTokens ?? 0;
5040
+ const prompt = res.usage.promptTokens ?? 0, cacheRead = res.usage.cacheReadTokens ?? 0;
5041
+ if (prevPrompt < 0) {
5042
+ const u = sent.map((m) => m.role).lastIndexOf("user");
5043
+ prevPrompt = u > 0 ? estimateTokens(sent.slice(0, u)) : 0;
5044
+ }
5045
+ const reused = Math.min(prompt, Math.max(cacheRead, prevPrompt));
5046
+ spent += (res.usage.totalTokens ?? 0) - 0.9 * reused;
5047
+ prevPrompt = stepPrompt = prompt;
5177
5048
  }
5178
5049
  const delegatedTools = res.delegatedTools ?? [];
5179
5050
  if (delegatedTools.length) {