@armadra/agent 0.7.4 → 0.7.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.
@@ -41,7 +41,7 @@ var AMA_VERSION;
41
41
  var init_version = __esm({
42
42
  "src/version.ts"() {
43
43
  "use strict";
44
- AMA_VERSION = true ? "0.7.4" : readPackageVersion();
44
+ AMA_VERSION = true ? "0.7.5" : readPackageVersion();
45
45
  }
46
46
  });
47
47
 
@@ -10284,8 +10284,9 @@ function readSessionLines(file, options = {}) {
10284
10284
  forEachLineSync(file, (buf, index2, last, offset) => {
10285
10285
  if (last) unterminated = true;
10286
10286
  if (isBlankLine(buf)) return;
10287
+ let line2;
10287
10288
  try {
10288
- lines2.push(JSON.parse(buf.toString("utf8")));
10289
+ line2 = JSON.parse(buf.toString("utf8"));
10289
10290
  } catch (error2) {
10290
10291
  if (last) {
10291
10292
  repairedTail = true;
@@ -10296,6 +10297,8 @@ function readSessionLines(file, options = {}) {
10296
10297
  cause: error2
10297
10298
  });
10298
10299
  }
10300
+ options.onLine?.(line2, { offset, length: buf.length });
10301
+ lines2.push(line2);
10299
10302
  });
10300
10303
  } catch (error2) {
10301
10304
  if (error2 instanceof AmaError) throw error2;
@@ -10304,19 +10307,20 @@ function readSessionLines(file, options = {}) {
10304
10307
  if (!repairedTail && unterminated && options.repair === true) {
10305
10308
  appendRaw(file, "\n");
10306
10309
  }
10307
- return { lines: lines2, repairedTail };
10310
+ if (options.onLine === void 0) return { lines: lines2, repairedTail };
10311
+ return { lines: lines2, repairedTail, bytes: (0, import_node_fs10.statSync)(file).size };
10308
10312
  }
10309
10313
  function appendRaw(file, text) {
10310
10314
  const fd = (0, import_node_fs10.openSync)(file, "a");
10311
10315
  try {
10312
- (0, import_node_fs10.writeSync)(fd, text);
10316
+ return (0, import_node_fs10.writeSync)(fd, text);
10313
10317
  } finally {
10314
10318
  (0, import_node_fs10.closeSync)(fd);
10315
10319
  }
10316
10320
  }
10317
10321
  function appendLines(file, lines2) {
10318
- if (lines2.length === 0) return;
10319
- appendRaw(file, lines2.map((line2) => JSON.stringify(line2)).join("\n") + "\n");
10322
+ if (lines2.length === 0) return 0;
10323
+ return appendRaw(file, lines2.map((line2) => JSON.stringify(line2)).join("\n") + "\n");
10320
10324
  }
10321
10325
  function writeNewSessionFile(file, lines2) {
10322
10326
  (0, import_node_fs10.mkdirSync)((0, import_node_path13.dirname)(file), { recursive: true, mode: 448 });
@@ -10324,8 +10328,10 @@ function writeNewSessionFile(file, lines2) {
10324
10328
  throw new AmaError("session_exists", `session file already exists: ${file}`);
10325
10329
  }
10326
10330
  const tmp = `${file}.${process.pid}.tmp`;
10327
- (0, import_node_fs10.writeFileSync)(tmp, lines2.map((line2) => JSON.stringify(line2)).join("\n") + "\n", { mode: 384 });
10331
+ const texts2 = lines2.map((line2) => JSON.stringify(line2));
10332
+ (0, import_node_fs10.writeFileSync)(tmp, texts2.join("\n") + "\n", { mode: 384 });
10328
10333
  (0, import_node_fs10.renameSync)(tmp, file);
10334
+ return texts2.map((text) => Buffer.byteLength(text));
10329
10335
  }
10330
10336
  function installExitHandler() {
10331
10337
  if (exitHandlerInstalled) return;
@@ -10788,12 +10794,26 @@ var init_suggest = __esm({
10788
10794
 
10789
10795
  // src/ai/json-body.ts
10790
10796
  function isPlain(value) {
10797
+ plainScans.count++;
10791
10798
  for (let i = 0; i < value.length; i += CHECK_CHUNK) {
10792
10799
  const chunk = value.slice(i, i + CHECK_CHUNK);
10793
10800
  if (JSON.stringify(chunk).length !== chunk.length + 2) return false;
10794
10801
  }
10795
10802
  return true;
10796
10803
  }
10804
+ function largeString(prefix, key, text) {
10805
+ if (text.length < LARGE_STRING_BYTES || !isShortPlain(prefix)) return prefix + text;
10806
+ return new LargeString(prefix, key, text);
10807
+ }
10808
+ function plainLargeBytes(value) {
10809
+ if (!isShortPlain(value.prefix)) return void 0;
10810
+ const known = plainBytes.get(value.key);
10811
+ if (known !== void 0 && known.length === value.text.length) return known.bytes;
10812
+ if (!isPlain(value.text)) return void 0;
10813
+ const bytes = Buffer.byteLength(value.text, "utf8");
10814
+ plainBytes.set(value.key, { length: value.text.length, bytes });
10815
+ return bytes;
10816
+ }
10797
10817
  function isContainer(value) {
10798
10818
  if (typeof value !== "object" || value === null) return false;
10799
10819
  if (typeof value.toJSON === "function") return false;
@@ -10803,6 +10823,7 @@ function isContainer(value) {
10803
10823
  }
10804
10824
  function collectLarge(value, marked, depth) {
10805
10825
  if (isLargeString(value)) return true;
10826
+ if (value instanceof LargeString) return value.text.length >= LARGE_STRING_BYTES;
10806
10827
  if (depth >= MAX_DEPTH || !isContainer(value)) return false;
10807
10828
  let found = false;
10808
10829
  if (Array.isArray(value)) {
@@ -10835,6 +10856,13 @@ function writeEntry(key, value, marked, out, prefix, depth) {
10835
10856
  }
10836
10857
  return true;
10837
10858
  }
10859
+ if (value instanceof LargeString && value.text.length >= LARGE_STRING_BYTES) {
10860
+ const bytes = plainLargeBytes(value);
10861
+ out.text(prefix);
10862
+ if (bytes !== void 0) out.large(value.text, value.prefix, bytes);
10863
+ else out.text(JSON.stringify(value.toJSON()));
10864
+ return true;
10865
+ }
10838
10866
  if (depth < MAX_DEPTH && isContainer(value) && marked.has(value)) {
10839
10867
  out.text(prefix);
10840
10868
  writeContainer(value, marked, out, depth);
@@ -10868,20 +10896,16 @@ function jsonParts(body) {
10868
10896
  const marked = /* @__PURE__ */ new Set();
10869
10897
  if (!collectLarge(body, marked, 0)) return void 0;
10870
10898
  const out = new Pieces();
10871
- if (isLargeString(body)) {
10872
- if (!isPlain(body)) return void 0;
10873
- out.large(body);
10874
- } else writeContainer(body, marked, out, 0);
10899
+ writeEntry("", body, marked, out, "", 0);
10875
10900
  const parts = out.finish();
10876
- let length = 0;
10877
- for (const part of parts) length += Buffer.byteLength(part, "utf8");
10878
- return { parts, length };
10901
+ return { parts, length: out.bytes };
10879
10902
  }
10880
10903
  function jsonFetchBody(body) {
10881
10904
  const split = jsonParts(body);
10882
10905
  if (split === void 0) return { body: JSON.stringify(body) };
10883
10906
  const { parts } = split;
10884
10907
  let next = 0;
10908
+ let offset = 0;
10885
10909
  const stream5 = new ReadableStream({
10886
10910
  pull(controller) {
10887
10911
  const part = parts[next];
@@ -10889,8 +10913,15 @@ function jsonFetchBody(body) {
10889
10913
  controller.close();
10890
10914
  return;
10891
10915
  }
10892
- parts[next++] = "";
10893
- controller.enqueue(Buffer.from(part, "utf8"));
10916
+ let end = Math.min(part.length, offset + STREAM_CHUNK_CHARS);
10917
+ const last = part.charCodeAt(end - 1);
10918
+ if (end < part.length && last >= 55296 && last <= 56319) end--;
10919
+ const piece = offset === 0 && end === part.length ? part : part.slice(offset, end);
10920
+ if (end === part.length) {
10921
+ parts[next++] = "";
10922
+ offset = 0;
10923
+ } else offset = end;
10924
+ controller.enqueue(Buffer.from(piece, "utf8"));
10894
10925
  },
10895
10926
  cancel() {
10896
10927
  parts.length = 0;
@@ -10898,31 +10929,55 @@ function jsonFetchBody(body) {
10898
10929
  });
10899
10930
  return { body: stream5, contentLength: split.length };
10900
10931
  }
10901
- var LARGE_STRING_BYTES, CHECK_CHUNK, MAX_DEPTH, isLargeString, Pieces;
10932
+ var LARGE_STRING_BYTES, CHECK_CHUNK, isShortPlain, plainScans, LargeString, plainBytes, MAX_DEPTH, isLargeString, Pieces, STREAM_CHUNK_CHARS;
10902
10933
  var init_json_body = __esm({
10903
10934
  "src/ai/json-body.ts"() {
10904
10935
  "use strict";
10905
10936
  LARGE_STRING_BYTES = 64 * 1024;
10906
10937
  CHECK_CHUNK = 16 * 1024;
10938
+ isShortPlain = (value) => JSON.stringify(value).length === value.length + 2;
10939
+ plainScans = { count: 0 };
10940
+ LargeString = class {
10941
+ constructor(prefix, key, text) {
10942
+ this.prefix = prefix;
10943
+ this.key = key;
10944
+ this.text = text;
10945
+ }
10946
+ prefix;
10947
+ key;
10948
+ text;
10949
+ toJSON() {
10950
+ return this.prefix + this.text;
10951
+ }
10952
+ };
10953
+ plainBytes = /* @__PURE__ */ new WeakMap();
10907
10954
  MAX_DEPTH = 64;
10908
10955
  isLargeString = (value) => typeof value === "string" && value.length >= LARGE_STRING_BYTES;
10909
10956
  Pieces = class {
10910
10957
  parts = [];
10958
+ /** 已推入 `parts` 的 UTF-8 字节数。 */
10959
+ bytes = 0;
10911
10960
  pending = "";
10912
10961
  text(chunk) {
10913
10962
  this.pending += chunk;
10914
10963
  }
10915
- /** 大字符串原样写入(引号进相邻的小片段)。 */
10916
- large(value) {
10917
- this.parts.push(`${this.pending}"`, value);
10964
+ /** 大字符串原样写入(引号与 `head` 进前一个小片段);`bytes` 已知时不再数一遍。 */
10965
+ large(value, head = "", bytes = Buffer.byteLength(value, "utf8")) {
10966
+ const before2 = `${this.pending}"${head}`;
10967
+ this.parts.push(before2, value);
10968
+ this.bytes += Buffer.byteLength(before2, "utf8") + bytes;
10918
10969
  this.pending = '"';
10919
10970
  }
10920
10971
  finish() {
10921
- if (this.pending.length > 0) this.parts.push(this.pending);
10972
+ if (this.pending.length > 0) {
10973
+ this.parts.push(this.pending);
10974
+ this.bytes += Buffer.byteLength(this.pending, "utf8");
10975
+ }
10922
10976
  this.pending = "";
10923
10977
  return this.parts;
10924
10978
  }
10925
10979
  };
10980
+ STREAM_CHUNK_CHARS = 256 * 1024;
10926
10981
  }
10927
10982
  });
10928
10983
 
@@ -16177,121 +16232,150 @@ var init_edit_fuzzy = __esm({
16177
16232
  });
16178
16233
 
16179
16234
  // src/tools/read-lines.ts
16180
- function readHead(abs, bytes) {
16181
- const fd = (0, import_node_fs22.openSync)(abs, "r");
16235
+ async function readHeadAsync(abs, bytes) {
16236
+ const handle = await (0, import_promises3.open)(abs, "r");
16182
16237
  try {
16183
16238
  const buf = Buffer.alloc(bytes);
16184
16239
  let got = 0;
16185
16240
  while (got < bytes) {
16186
- const n = (0, import_node_fs22.readSync)(fd, buf, got, bytes - got, null);
16187
- if (n === 0) break;
16188
- got += n;
16241
+ const { bytesRead } = await handle.read(buf, got, bytes - got, null);
16242
+ if (bytesRead === 0) break;
16243
+ got += bytesRead;
16189
16244
  }
16190
16245
  return buf.subarray(0, got);
16191
16246
  } finally {
16192
- (0, import_node_fs22.closeSync)(fd);
16247
+ await handle.close();
16193
16248
  }
16194
16249
  }
16195
- function readLineWindow(abs, offset, limit, maxBytes, chunkBytes = CHUNK_BYTES) {
16250
+ function scanner(offset, limit, maxBytes) {
16196
16251
  const first = offset - 1;
16197
- const stop = limit === void 0 ? Infinity : first + limit;
16198
- const lineCap = maxBytes + LINE_SLACK;
16199
- const lines2 = [];
16200
- let truncatedBy;
16201
- let collected = 0;
16202
- let index2 = 0;
16203
- let parts = [];
16204
- let partBytes = 0;
16205
- let pending = false;
16206
- let skipLF = false;
16207
- let bom = false;
16208
- const collecting = () => truncatedBy === void 0 && index2 >= first && index2 < stop;
16209
- const take = (buf, from, to) => {
16210
- if (to <= from || partBytes >= lineCap) return;
16211
- const end = Math.min(to, from + lineCap - partBytes);
16212
- parts.push(Buffer.from(buf.subarray(from, end)));
16213
- partBytes += end - from;
16214
- };
16215
- const finish2 = () => {
16216
- if (collecting()) {
16217
- const line2 = (parts.length === 1 ? parts[0] : Buffer.concat(parts)).toString("utf8");
16218
- lines2.push(line2);
16219
- collected += Buffer.byteLength(line2) + 1;
16220
- if (collected > maxBytes) truncatedBy = "bytes";
16221
- }
16222
- if (partBytes > 0) parts = [];
16223
- partBytes = 0;
16224
- pending = false;
16225
- index2++;
16226
- };
16252
+ return new LineScanner(first, limit === void 0 ? Infinity : first + limit, maxBytes);
16253
+ }
16254
+ async function readLineWindowAsync(abs, offset, limit, maxBytes, chunkBytes = CHUNK_BYTES) {
16255
+ const scan = scanner(offset, limit, maxBytes);
16227
16256
  const size = Math.max(3, chunkBytes);
16228
- const fd = (0, import_node_fs22.openSync)(abs, "r");
16257
+ const handle = await (0, import_promises3.open)(abs, "r");
16229
16258
  try {
16230
16259
  const buf = Buffer.alloc(size);
16231
16260
  let carry = 0;
16232
- let firstChunk = true;
16233
16261
  for (; ; ) {
16234
- const n = (0, import_node_fs22.readSync)(fd, buf, carry, size - carry, null);
16262
+ const { bytesRead: n } = await handle.read(buf, carry, size - carry, null);
16235
16263
  const end = carry + n;
16236
- carry = 0;
16237
- if (n === 0 && end === 0) break;
16238
- let pos = 0;
16239
- if (firstChunk) {
16240
- if (end < 3 && n > 0) {
16241
- carry = end;
16242
- continue;
16243
- }
16244
- firstChunk = false;
16245
- if (end >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
16246
- bom = true;
16247
- pos = 3;
16248
- }
16249
- }
16250
- if (skipLF && pos < end) {
16251
- if (buf[pos] === LF2) pos++;
16252
- skipLF = false;
16253
- }
16254
- let nextLF = buf.indexOf(LF2, pos);
16255
- let nextCR = buf.indexOf(CR2, pos);
16256
- while (pos < end) {
16257
- if (nextLF !== -1 && nextLF < pos) nextLF = buf.indexOf(LF2, pos);
16258
- if (nextCR !== -1 && nextCR < pos) nextCR = buf.indexOf(CR2, pos);
16259
- const lf = nextLF === -1 || nextLF >= end ? end : nextLF;
16260
- const cr = nextCR === -1 || nextCR >= end ? end : nextCR;
16261
- const at = Math.min(lf, cr);
16262
- if (at > pos) {
16263
- pending = true;
16264
- if (collecting()) take(buf, pos, at);
16265
- }
16266
- if (at === end) break;
16267
- finish2();
16268
- pos = at + 1;
16269
- if (at === cr) {
16270
- if (pos < end) {
16271
- if (buf[pos] === LF2) pos++;
16272
- } else {
16273
- skipLF = true;
16274
- }
16275
- }
16276
- }
16264
+ if (end === 0) break;
16265
+ carry = scan.feed(buf, end, n === 0);
16277
16266
  if (n === 0) break;
16278
16267
  }
16279
- if (pending) finish2();
16280
16268
  } finally {
16281
- (0, import_node_fs22.closeSync)(fd);
16269
+ await handle.close();
16282
16270
  }
16283
- return { lines: lines2, totalLines: index2, ...truncatedBy ? { truncatedBy } : {}, bom };
16271
+ return scan.finish();
16284
16272
  }
16285
- var import_node_fs22, STREAM_READ_THRESHOLD, CHUNK_BYTES, LINE_SLACK, LF2, CR2;
16273
+ var import_promises3, STREAM_READ_THRESHOLD, CHUNK_BYTES, LINE_SLACK, LF2, CR2, LineScanner;
16286
16274
  var init_read_lines = __esm({
16287
16275
  "src/tools/read-lines.ts"() {
16288
16276
  "use strict";
16289
- import_node_fs22 = require("node:fs");
16277
+ import_promises3 = require("node:fs/promises");
16290
16278
  STREAM_READ_THRESHOLD = 1024 * 1024;
16291
16279
  CHUNK_BYTES = 64 * 1024;
16292
16280
  LINE_SLACK = 64;
16293
16281
  LF2 = 10;
16294
16282
  CR2 = 13;
16283
+ LineScanner = class {
16284
+ constructor(first, stop, maxBytes) {
16285
+ this.first = first;
16286
+ this.stop = stop;
16287
+ this.maxBytes = maxBytes;
16288
+ this.lineCap = maxBytes + LINE_SLACK;
16289
+ }
16290
+ first;
16291
+ stop;
16292
+ maxBytes;
16293
+ lineCap;
16294
+ lines = [];
16295
+ truncatedBy;
16296
+ collected = 0;
16297
+ index = 0;
16298
+ // 已完成的行数
16299
+ parts = [];
16300
+ partBytes = 0;
16301
+ pending = false;
16302
+ // 最后一个终止符之后已有字节
16303
+ skipLF = false;
16304
+ // 上一块以 `\r` 结尾:下一块开头的 `\n` 属于同一终止符
16305
+ bom = false;
16306
+ firstChunk = true;
16307
+ /**
16308
+ * 处理 `buf[0, end)`;`eof` 表示这是最后一块。返回调用方下次读取前应保留在 `buf` 开头的字节数
16309
+ * (只在首块不足 3 字节、还判断不了 BOM 时非零)。
16310
+ */
16311
+ feed(buf, end, eof) {
16312
+ let pos = 0;
16313
+ if (this.firstChunk) {
16314
+ if (end < 3 && !eof) return end;
16315
+ this.firstChunk = false;
16316
+ if (end >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191) {
16317
+ this.bom = true;
16318
+ pos = 3;
16319
+ }
16320
+ }
16321
+ if (this.skipLF && pos < end) {
16322
+ if (buf[pos] === LF2) pos++;
16323
+ this.skipLF = false;
16324
+ }
16325
+ let nextLF = buf.indexOf(LF2, pos);
16326
+ let nextCR = buf.indexOf(CR2, pos);
16327
+ while (pos < end) {
16328
+ if (nextLF !== -1 && nextLF < pos) nextLF = buf.indexOf(LF2, pos);
16329
+ if (nextCR !== -1 && nextCR < pos) nextCR = buf.indexOf(CR2, pos);
16330
+ const lf = nextLF === -1 || nextLF >= end ? end : nextLF;
16331
+ const cr = nextCR === -1 || nextCR >= end ? end : nextCR;
16332
+ const at = Math.min(lf, cr);
16333
+ if (at > pos) {
16334
+ this.pending = true;
16335
+ if (this.collecting()) this.take(buf, pos, at);
16336
+ }
16337
+ if (at === end) break;
16338
+ this.endLine();
16339
+ pos = at + 1;
16340
+ if (at === cr) {
16341
+ if (pos < end) {
16342
+ if (buf[pos] === LF2) pos++;
16343
+ } else {
16344
+ this.skipLF = true;
16345
+ }
16346
+ }
16347
+ }
16348
+ return 0;
16349
+ }
16350
+ /** 文件读完:收尾最后一行并给出结果。 */
16351
+ finish() {
16352
+ if (this.pending) this.endLine();
16353
+ const { lines: lines2, truncatedBy, bom } = this;
16354
+ return { lines: lines2, totalLines: this.index, ...truncatedBy ? { truncatedBy } : {}, bom };
16355
+ }
16356
+ collecting() {
16357
+ return this.truncatedBy === void 0 && this.index >= this.first && this.index < this.stop;
16358
+ }
16359
+ take(buf, from, to) {
16360
+ if (to <= from || this.partBytes >= this.lineCap) return;
16361
+ const end = Math.min(to, from + this.lineCap - this.partBytes);
16362
+ this.parts.push(Buffer.from(buf.subarray(from, end)));
16363
+ this.partBytes += end - from;
16364
+ }
16365
+ endLine() {
16366
+ if (this.collecting()) {
16367
+ const { parts } = this;
16368
+ const line2 = (parts.length === 1 ? parts[0] : Buffer.concat(parts)).toString("utf8");
16369
+ this.lines.push(line2);
16370
+ this.collected += Buffer.byteLength(line2) + 1;
16371
+ if (this.collected > this.maxBytes) this.truncatedBy = "bytes";
16372
+ }
16373
+ if (this.partBytes > 0) this.parts = [];
16374
+ this.partBytes = 0;
16375
+ this.pending = false;
16376
+ this.index++;
16377
+ }
16378
+ };
16295
16379
  }
16296
16380
  });
16297
16381
 
@@ -16363,7 +16447,7 @@ async function resizeImage(input2, target, deps = {}) {
16363
16447
  const mimeType = format === "jpeg" ? "image/jpeg" : "image/png";
16364
16448
  const long = target.size !== void 0 ? Math.max(target.size.width, target.size.height) : 0;
16365
16449
  const start = long > 0 ? Math.min(target.maxEdge, long) : target.maxEdge;
16366
- const dir = await (0, import_promises3.mkdtemp)((0, import_node_path25.join)(deps.tmpDir ?? (0, import_node_os5.tmpdir)(), "ama-resize-"));
16450
+ const dir = await (0, import_promises4.mkdtemp)((0, import_node_path25.join)(deps.tmpDir ?? (0, import_node_os5.tmpdir)(), "ama-resize-"));
16367
16451
  try {
16368
16452
  for (const tool of resizeTools(deps.platform ?? process.platform)) {
16369
16453
  let edge = start;
@@ -16374,7 +16458,7 @@ async function resizeImage(input2, target, deps = {}) {
16374
16458
  if (result.missing === true || result.code !== 0) break;
16375
16459
  let buf;
16376
16460
  try {
16377
- buf = await (0, import_promises3.readFile)(output);
16461
+ buf = await (0, import_promises4.readFile)(output);
16378
16462
  } catch {
16379
16463
  break;
16380
16464
  }
@@ -16390,15 +16474,15 @@ async function resizeImage(input2, target, deps = {}) {
16390
16474
  }
16391
16475
  return { ok: false, reason: "no_tool" };
16392
16476
  } finally {
16393
- await (0, import_promises3.rm)(dir, { recursive: true, force: true }).catch(() => void 0);
16477
+ await (0, import_promises4.rm)(dir, { recursive: true, force: true }).catch(() => void 0);
16394
16478
  }
16395
16479
  }
16396
- var import_node_child_process3, import_promises3, import_node_os5, import_node_path25, runCommand, MAX_ATTEMPTS, SHRINK, MIN_EDGE;
16480
+ var import_node_child_process3, import_promises4, import_node_os5, import_node_path25, runCommand, MAX_ATTEMPTS, SHRINK, MIN_EDGE;
16397
16481
  var init_image_resize = __esm({
16398
16482
  "src/tools/image-resize.ts"() {
16399
16483
  "use strict";
16400
16484
  import_node_child_process3 = require("node:child_process");
16401
- import_promises3 = require("node:fs/promises");
16485
+ import_promises4 = require("node:fs/promises");
16402
16486
  import_node_os5 = require("node:os");
16403
16487
  import_node_path25 = require("node:path");
16404
16488
  init_image_limits();
@@ -16570,7 +16654,7 @@ function fitError(path, fit, limit, bytes, resizeOff) {
16570
16654
  async function loadImageFile(path, options = {}) {
16571
16655
  let info2;
16572
16656
  try {
16573
- info2 = await (0, import_promises4.stat)(path);
16657
+ info2 = await (0, import_promises5.stat)(path);
16574
16658
  } catch {
16575
16659
  throw imageError(`Image not found: ${path}`, { reason: "missing", path });
16576
16660
  }
@@ -16586,7 +16670,7 @@ async function loadImageFile(path, options = {}) {
16586
16670
  sizeMb
16587
16671
  });
16588
16672
  }
16589
- const buf = await (0, import_promises4.readFile)(path);
16673
+ const buf = await (0, import_promises5.readFile)(path);
16590
16674
  const mimeType = sniffImageMime(buf) ?? imageMimeFromPath(path);
16591
16675
  if (mimeType === void 0)
16592
16676
  throw imageError(`Unsupported image (PNG / JPEG / GIF / WebP): ${path}`, {
@@ -16603,11 +16687,11 @@ async function loadImageFile(path, options = {}) {
16603
16687
  ...fit.size ? { size: fit.size } : {}
16604
16688
  };
16605
16689
  }
16606
- var import_promises4, import_node_path26, MAX_IMAGE_FILE_BYTES, IMAGE_EXTENSIONS, HINT_TEXT;
16690
+ var import_promises5, import_node_path26, MAX_IMAGE_FILE_BYTES, IMAGE_EXTENSIONS, HINT_TEXT;
16607
16691
  var init_image_file = __esm({
16608
16692
  "src/tools/image-file.ts"() {
16609
16693
  "use strict";
16610
- import_promises4 = require("node:fs/promises");
16694
+ import_promises5 = require("node:fs/promises");
16611
16695
  import_node_path26 = require("node:path");
16612
16696
  init_errors();
16613
16697
  init_image_intern();
@@ -16654,7 +16738,7 @@ function numberLines(lines2, firstLine3) {
16654
16738
  return lines2.map((line2, i) => `${String(firstLine3 + i).padStart(6)} ${line2}`).join("\n");
16655
16739
  }
16656
16740
  async function readImage(abs, shown2, ctx, options) {
16657
- const buf = await (0, import_promises5.readFile)(abs);
16741
+ const buf = await (0, import_promises6.readFile)(abs);
16658
16742
  const mimeType = sniffImageMime(buf) ?? imageMimeFromPath(abs) ?? "application/octet-stream";
16659
16743
  const size = imageSize(buf, mimeType);
16660
16744
  const dims = size ? `${size.width}x${size.height}, ` : "";
@@ -16685,7 +16769,7 @@ async function executeRead(input2, ctx, options = {}) {
16685
16769
  const shown2 = displayPath(abs, ctx.cwd);
16686
16770
  let info2;
16687
16771
  try {
16688
- info2 = await (0, import_promises5.stat)(abs);
16772
+ info2 = await (0, import_promises6.stat)(abs);
16689
16773
  } catch {
16690
16774
  return error(`File not found: ${shown2}`);
16691
16775
  }
@@ -16698,9 +16782,9 @@ async function executeRead(input2, ctx, options = {}) {
16698
16782
  let totalLines;
16699
16783
  let selected;
16700
16784
  if (info2.size > (options.streamThreshold ?? STREAM_READ_THRESHOLD)) {
16701
- if (isBinary(readHead(abs, SNIFF_BYTES))) return error(binaryMessage(shown2));
16785
+ if (isBinary(await readHeadAsync(abs, SNIFF_BYTES))) return error(binaryMessage(shown2));
16702
16786
  const want = Math.min(input2.limit ?? Infinity, DEFAULT_MAX_LINES + 1);
16703
- const win = readLineWindow(
16787
+ const win = await readLineWindowAsync(
16704
16788
  abs,
16705
16789
  badOffset ? 1 : offset,
16706
16790
  badOffset || badLimit ? 0 : want,
@@ -16709,7 +16793,7 @@ async function executeRead(input2, ctx, options = {}) {
16709
16793
  totalLines = win.totalLines;
16710
16794
  selected = win.lines;
16711
16795
  } else {
16712
- const buf = await (0, import_promises5.readFile)(abs);
16796
+ const buf = await (0, import_promises6.readFile)(abs);
16713
16797
  if (isBinary(buf)) return error(binaryMessage(shown2));
16714
16798
  const { text } = splitBom(buf.toString("utf8"));
16715
16799
  const normalized = normalizeToLF(text);
@@ -16770,11 +16854,11 @@ function createReadTool(options = {}) {
16770
16854
  execute: (input2, ctx) => executeRead(input2, ctx, options)
16771
16855
  };
16772
16856
  }
16773
- var import_promises5, SNIFF_BYTES, binaryMessage;
16857
+ var import_promises6, SNIFF_BYTES, binaryMessage;
16774
16858
  var init_read = __esm({
16775
16859
  "src/tools/read.ts"() {
16776
16860
  "use strict";
16777
- import_promises5 = require("node:fs/promises");
16861
+ import_promises6 = require("node:fs/promises");
16778
16862
  init_image_intern();
16779
16863
  init_paths2();
16780
16864
  init_truncate();
@@ -16943,7 +17027,7 @@ async function executeEdit(input2, ctx) {
16943
17027
  const shown2 = displayPath(abs, ctx.cwd);
16944
17028
  return withFileMutex(abs, async () => {
16945
17029
  try {
16946
- const info2 = await (0, import_promises6.stat)(abs);
17030
+ const info2 = await (0, import_promises7.stat)(abs);
16947
17031
  if (!info2.isFile()) return { content: `${shown2} is not a regular file`, isError: true };
16948
17032
  } catch {
16949
17033
  return { content: `File not found: ${shown2}`, isError: true };
@@ -16951,7 +17035,7 @@ async function executeEdit(input2, ctx) {
16951
17035
  if (!ctx.readFiles.has(abs)) {
16952
17036
  return { content: `Read ${shown2} with the read tool before editing it.`, isError: true };
16953
17037
  }
16954
- const raw = await (0, import_promises6.readFile)(abs, "utf8");
17038
+ const raw = await (0, import_promises7.readFile)(abs, "utf8");
16955
17039
  const { bom, text } = splitBom(raw);
16956
17040
  const ending = detectLineEnding(text);
16957
17041
  const content = normalizeToLF(text);
@@ -16964,7 +17048,7 @@ async function executeEdit(input2, ctx) {
16964
17048
  }
16965
17049
  const output = bom + restoreLineEndings(plan2.result, ending);
16966
17050
  await beforeWrite(ctx, abs);
16967
- await (0, import_promises6.writeFile)(abs, output, "utf8");
17051
+ await (0, import_promises7.writeFile)(abs, output, "utf8");
16968
17052
  ctx.checkpoint?.afterWrite(abs, output);
16969
17053
  const details = {
16970
17054
  path: abs,
@@ -17014,11 +17098,11 @@ function createEditTool() {
17014
17098
  execute: executeEdit
17015
17099
  };
17016
17100
  }
17017
- var import_promises6, EditError;
17101
+ var import_promises7, EditError;
17018
17102
  var init_edit = __esm({
17019
17103
  "src/tools/edit.ts"() {
17020
17104
  "use strict";
17021
- import_promises6 = require("node:fs/promises");
17105
+ import_promises7 = require("node:fs/promises");
17022
17106
  init_types8();
17023
17107
  init_paths2();
17024
17108
  init_file_mutex();
@@ -17031,7 +17115,7 @@ var init_edit = __esm({
17031
17115
  // src/tools/write.ts
17032
17116
  async function exists2(path) {
17033
17117
  try {
17034
- const info2 = await (0, import_promises7.stat)(path);
17118
+ const info2 = await (0, import_promises8.stat)(path);
17035
17119
  return info2.isDirectory() ? "dir" : "file";
17036
17120
  } catch {
17037
17121
  return false;
@@ -17072,12 +17156,12 @@ async function executeWrite(input2, ctx) {
17072
17156
  isError: true
17073
17157
  };
17074
17158
  }
17075
- original = await (0, import_promises7.readFile)(abs, "utf8");
17159
+ original = await (0, import_promises8.readFile)(abs, "utf8");
17076
17160
  output = conformToOriginal(original, input2.content);
17077
17161
  }
17078
17162
  await beforeWrite(ctx, abs);
17079
- await (0, import_promises7.mkdir)((0, import_node_path27.dirname)(abs), { recursive: true });
17080
- await (0, import_promises7.writeFile)(abs, output, "utf8");
17163
+ await (0, import_promises8.mkdir)((0, import_node_path27.dirname)(abs), { recursive: true });
17164
+ await (0, import_promises8.writeFile)(abs, output, "utf8");
17081
17165
  ctx.checkpoint?.afterWrite(abs, output);
17082
17166
  ctx.markRead(abs);
17083
17167
  const details = {
@@ -17113,11 +17197,11 @@ function createWriteTool() {
17113
17197
  execute: executeWrite
17114
17198
  };
17115
17199
  }
17116
- var import_promises7, import_node_path27;
17200
+ var import_promises8, import_node_path27;
17117
17201
  var init_write = __esm({
17118
17202
  "src/tools/write.ts"() {
17119
17203
  "use strict";
17120
- import_promises7 = require("node:fs/promises");
17204
+ import_promises8 = require("node:fs/promises");
17121
17205
  import_node_path27 = require("node:path");
17122
17206
  init_paths2();
17123
17207
  init_file_mutex();
@@ -17148,7 +17232,7 @@ function gitBashCandidates(env) {
17148
17232
  function resolveShell(deps = {}) {
17149
17233
  const platform = deps.platform ?? process.platform;
17150
17234
  const env = deps.env ?? process.env;
17151
- const exists5 = deps.exists ?? import_node_fs23.existsSync;
17235
+ const exists5 = deps.exists ?? import_node_fs22.existsSync;
17152
17236
  const custom = env["AMA_SHELL"]?.trim();
17153
17237
  if (custom) return { shell: custom, kind: shellKindOf(custom) };
17154
17238
  if (platform === "win32") {
@@ -17188,11 +17272,11 @@ function buildToolEnv(base4, info2) {
17188
17272
  set("AMA_DEPTH", String(info2.depth));
17189
17273
  return env;
17190
17274
  }
17191
- var import_node_fs23, POWERSHELL_ARGS, INJECTED_ENV_VARS;
17275
+ var import_node_fs22, POWERSHELL_ARGS, INJECTED_ENV_VARS;
17192
17276
  var init_shell = __esm({
17193
17277
  "src/tools/shell.ts"() {
17194
17278
  "use strict";
17195
- import_node_fs23 = require("node:fs");
17279
+ import_node_fs22 = require("node:fs");
17196
17280
  POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-Command"];
17197
17281
  INJECTED_ENV_VARS = [
17198
17282
  "AMA_SESSION_ID",
@@ -17307,11 +17391,11 @@ var init_process_tree = __esm({
17307
17391
  function sanitizeOutput(text) {
17308
17392
  return text.replace(/\r\n/g, "\n").replace(ANSI, "").replace(/\r/g, "\n").replace(CONTROL, "");
17309
17393
  }
17310
- var import_node_fs24, import_node_path28, import_node_string_decoder2, ANSI, CONTROL, OutputAccumulator;
17394
+ var import_node_fs23, import_node_path28, import_node_string_decoder2, ANSI, CONTROL, OutputAccumulator;
17311
17395
  var init_output_accumulator = __esm({
17312
17396
  "src/tools/output-accumulator.ts"() {
17313
17397
  "use strict";
17314
- import_node_fs24 = require("node:fs");
17398
+ import_node_fs23 = require("node:fs");
17315
17399
  import_node_path28 = require("node:path");
17316
17400
  import_node_string_decoder2 = require("node:string_decoder");
17317
17401
  init_truncate();
@@ -17353,7 +17437,7 @@ var init_output_accumulator = __esm({
17353
17437
  this.tailText = sliceTailBytes(this.tailText, this.maxBytes * 2);
17354
17438
  }
17355
17439
  if (this.spilledTo !== void 0) {
17356
- (0, import_node_fs24.appendFileSync)(this.spilledTo, text);
17440
+ (0, import_node_fs23.appendFileSync)(this.spilledTo, text);
17357
17441
  return;
17358
17442
  }
17359
17443
  this.chunks.push(text);
@@ -17362,8 +17446,8 @@ var init_output_accumulator = __esm({
17362
17446
  }
17363
17447
  spill() {
17364
17448
  const target = this.options.spillPath();
17365
- (0, import_node_fs24.mkdirSync)((0, import_node_path28.dirname)(target), { recursive: true });
17366
- (0, import_node_fs24.writeFileSync)(target, this.chunks.join(""));
17449
+ (0, import_node_fs23.mkdirSync)((0, import_node_path28.dirname)(target), { recursive: true });
17450
+ (0, import_node_fs23.writeFileSync)(target, this.chunks.join(""));
17367
17451
  this.spilledTo = target;
17368
17452
  this.chunks = [];
17369
17453
  this.memoryBytes = 0;
@@ -17421,11 +17505,11 @@ async function disposeSessionJobs(sessionId) {
17421
17505
  registry2.delete(sessionId);
17422
17506
  await jobs.disposeAll();
17423
17507
  }
17424
- var import_node_fs25, import_node_path29, BackgroundJobs, registry2;
17508
+ var import_node_fs24, import_node_path29, BackgroundJobs, registry2;
17425
17509
  var init_background_jobs = __esm({
17426
17510
  "src/tools/background-jobs.ts"() {
17427
17511
  "use strict";
17428
- import_node_fs25 = require("node:fs");
17512
+ import_node_fs24 = require("node:fs");
17429
17513
  import_node_path29 = require("node:path");
17430
17514
  init_process_tree();
17431
17515
  init_truncate();
@@ -17434,13 +17518,13 @@ var init_background_jobs = __esm({
17434
17518
  listeners = /* @__PURE__ */ new Set();
17435
17519
  seq = 0;
17436
17520
  start(command3, options) {
17437
- (0, import_node_fs25.mkdirSync)((0, import_node_path29.dirname)(options.outputPath), { recursive: true });
17438
- const fd = (0, import_node_fs25.openSync)(options.outputPath, "a");
17521
+ (0, import_node_fs24.mkdirSync)((0, import_node_path29.dirname)(options.outputPath), { recursive: true });
17522
+ const fd = (0, import_node_fs24.openSync)(options.outputPath, "a");
17439
17523
  let child;
17440
17524
  try {
17441
17525
  child = options.spawn(fd);
17442
17526
  } finally {
17443
- (0, import_node_fs25.closeSync)(fd);
17527
+ (0, import_node_fs24.closeSync)(fd);
17444
17528
  }
17445
17529
  const id = `bg${++this.seq}`;
17446
17530
  const job = {
@@ -17521,8 +17605,8 @@ var init_background_jobs = __esm({
17521
17605
  if (entry === void 0) return void 0;
17522
17606
  let text = "";
17523
17607
  try {
17524
- if ((0, import_node_fs25.statSync)(entry.job.outputPath).size > 0)
17525
- text = (0, import_node_fs25.readFileSync)(entry.job.outputPath, "utf8");
17608
+ if ((0, import_node_fs24.statSync)(entry.job.outputPath).size > 0)
17609
+ text = (0, import_node_fs24.readFileSync)(entry.job.outputPath, "utf8");
17526
17610
  } catch {
17527
17611
  }
17528
17612
  return { ...truncateTail(text), job: entry.job };
@@ -17568,7 +17652,7 @@ var init_background_jobs = __esm({
17568
17652
  });
17569
17653
 
17570
17654
  // src/sandbox/wrap.ts
17571
- function resolveWritable(dirs, realpath4 = import_node_fs26.realpathSync) {
17655
+ function resolveWritable(dirs, realpath4 = import_node_fs25.realpathSync) {
17572
17656
  const out = [];
17573
17657
  for (const dir of dirs) {
17574
17658
  let real2;
@@ -17583,12 +17667,12 @@ function resolveWritable(dirs, realpath4 = import_node_fs26.realpathSync) {
17583
17667
  }
17584
17668
  function isDirectory(path) {
17585
17669
  try {
17586
- return (0, import_node_fs26.statSync)(path).isDirectory();
17670
+ return (0, import_node_fs25.statSync)(path).isDirectory();
17587
17671
  } catch {
17588
17672
  return false;
17589
17673
  }
17590
17674
  }
17591
- function resolveExtras(policy, missingOk, realpath4 = import_node_fs26.realpathSync, isDir4 = isDirectory) {
17675
+ function resolveExtras(policy, missingOk, realpath4 = import_node_fs25.realpathSync, isDir4 = isDirectory) {
17592
17676
  const readOnly = [];
17593
17677
  for (const path of policy.readOnly ?? []) {
17594
17678
  let real2;
@@ -17660,11 +17744,11 @@ function wrapCommand(status, command3, args, policy, realpath4, isDir4) {
17660
17744
  };
17661
17745
  }
17662
17746
  }
17663
- var import_node_fs26, import_node_path30;
17747
+ var import_node_fs25, import_node_path30;
17664
17748
  var init_wrap = __esm({
17665
17749
  "src/sandbox/wrap.ts"() {
17666
17750
  "use strict";
17667
- import_node_fs26 = require("node:fs");
17751
+ import_node_fs25 = require("node:fs");
17668
17752
  import_node_path30 = require("node:path");
17669
17753
  init_profile2();
17670
17754
  }
@@ -17827,7 +17911,7 @@ async function executeBash(input2, ctx, options = {}) {
17827
17911
  }
17828
17912
  const cwd = resolvePath(input2.cwd ?? ".", ctx.cwd);
17829
17913
  try {
17830
- if (!(await (0, import_promises8.stat)(cwd)).isDirectory())
17914
+ if (!(await (0, import_promises9.stat)(cwd)).isDirectory())
17831
17915
  return fail2(`Working directory is not a directory: ${cwd}`);
17832
17916
  } catch {
17833
17917
  return fail2(`Working directory does not exist: ${cwd}`);
@@ -18077,12 +18161,12 @@ function createBashTool(options = {}) {
18077
18161
  execute: (input2, ctx) => executeBash(input2, ctx, options)
18078
18162
  };
18079
18163
  }
18080
- var import_node_child_process5, import_promises8, import_node_path32, DEFAULT_BASH_TIMEOUT_MS, MAX_BASH_TIMEOUT_MS, UPDATE_THROTTLE_MS, STDIO_DRAIN_MS, DEFAULT_JOB_WAIT_MS;
18164
+ var import_node_child_process5, import_promises9, import_node_path32, DEFAULT_BASH_TIMEOUT_MS, MAX_BASH_TIMEOUT_MS, UPDATE_THROTTLE_MS, STDIO_DRAIN_MS, DEFAULT_JOB_WAIT_MS;
18081
18165
  var init_bash2 = __esm({
18082
18166
  "src/tools/bash.ts"() {
18083
18167
  "use strict";
18084
18168
  import_node_child_process5 = require("node:child_process");
18085
- import_promises8 = require("node:fs/promises");
18169
+ import_promises9 = require("node:fs/promises");
18086
18170
  import_node_path32 = require("node:path");
18087
18171
  init_paths2();
18088
18172
  init_shell();
@@ -18109,9 +18193,9 @@ function compilePattern(input2) {
18109
18193
  }
18110
18194
  async function searchFile(abs, regex, firstOnly = false) {
18111
18195
  try {
18112
- const info2 = await (0, import_promises9.stat)(abs);
18196
+ const info2 = await (0, import_promises10.stat)(abs);
18113
18197
  if (!info2.isFile() || info2.size > GREP_MAX_FILE_BYTES) return void 0;
18114
- const buf = await (0, import_promises9.readFile)(abs);
18198
+ const buf = await (0, import_promises10.readFile)(abs);
18115
18199
  if (isBinary(buf)) return void 0;
18116
18200
  const lines2 = buf.toString("utf8").replace(/\r\n/g, "\n").split("\n");
18117
18201
  if (lines2[lines2.length - 1] === "") lines2.pop();
@@ -18172,7 +18256,7 @@ async function executeGrep(input2, ctx) {
18172
18256
  const root = resolvePath(input2.path ?? ".", ctx.cwd);
18173
18257
  let rootIsFile;
18174
18258
  try {
18175
- rootIsFile = (await (0, import_promises9.stat)(root)).isFile();
18259
+ rootIsFile = (await (0, import_promises10.stat)(root)).isFile();
18176
18260
  } catch {
18177
18261
  return { content: `Path not found: ${displayPath(root, ctx.cwd)}`, isError: true };
18178
18262
  }
@@ -18277,11 +18361,11 @@ function createGrepTool() {
18277
18361
  execute: executeGrep
18278
18362
  };
18279
18363
  }
18280
- var import_promises9, DEFAULT_GREP_LIMIT, GREP_MAX_FILE_BYTES, GREP_CONCURRENCY, MAX_CONTEXT;
18364
+ var import_promises10, DEFAULT_GREP_LIMIT, GREP_MAX_FILE_BYTES, GREP_CONCURRENCY, MAX_CONTEXT;
18281
18365
  var init_grep = __esm({
18282
18366
  "src/tools/grep.ts"() {
18283
18367
  "use strict";
18284
- import_promises9 = require("node:fs/promises");
18368
+ import_promises10 = require("node:fs/promises");
18285
18369
  init_paths2();
18286
18370
  init_ignore();
18287
18371
  init_glob();
@@ -18298,16 +18382,16 @@ var init_grep = __esm({
18298
18382
  async function describe(dir, name) {
18299
18383
  const abs = (0, import_node_path33.join)(dir, name);
18300
18384
  try {
18301
- const info2 = await (0, import_promises10.lstat)(abs);
18385
+ const info2 = await (0, import_promises11.lstat)(abs);
18302
18386
  if (info2.isSymbolicLink()) {
18303
18387
  let target = "?";
18304
18388
  try {
18305
- target = await (0, import_promises10.readlink)(abs);
18389
+ target = await (0, import_promises11.readlink)(abs);
18306
18390
  } catch {
18307
18391
  }
18308
18392
  let pointsToDir = false;
18309
18393
  try {
18310
- pointsToDir = (await (0, import_promises10.stat)(abs)).isDirectory();
18394
+ pointsToDir = (await (0, import_promises11.stat)(abs)).isDirectory();
18311
18395
  } catch {
18312
18396
  }
18313
18397
  return { name: pointsToDir ? `${name}/` : name, type: "symlink", target };
@@ -18333,9 +18417,9 @@ async function executeLs(input2, ctx) {
18333
18417
  const shown2 = displayPath(dir, ctx.cwd);
18334
18418
  let names;
18335
18419
  try {
18336
- if (!(await (0, import_promises10.stat)(dir)).isDirectory())
18420
+ if (!(await (0, import_promises11.stat)(dir)).isDirectory())
18337
18421
  return { content: `${shown2} is not a directory`, isError: true };
18338
- names = await (0, import_promises10.readdir)(dir);
18422
+ names = await (0, import_promises11.readdir)(dir);
18339
18423
  } catch {
18340
18424
  return { content: `Directory not found: ${shown2}`, isError: true };
18341
18425
  }
@@ -18370,11 +18454,11 @@ function createLsTool() {
18370
18454
  execute: executeLs
18371
18455
  };
18372
18456
  }
18373
- var import_promises10, import_node_path33, DEFAULT_LS_LIMIT;
18457
+ var import_promises11, import_node_path33, DEFAULT_LS_LIMIT;
18374
18458
  var init_ls = __esm({
18375
18459
  "src/tools/ls.ts"() {
18376
18460
  "use strict";
18377
- import_promises10 = require("node:fs/promises");
18461
+ import_promises11 = require("node:fs/promises");
18378
18462
  import_node_path33 = require("node:path");
18379
18463
  init_paths2();
18380
18464
  init_truncate();
@@ -18661,9 +18745,9 @@ ${tail}`, truncated: true };
18661
18745
  }
18662
18746
  function writeTaskOutput(dir, name, text) {
18663
18747
  try {
18664
- (0, import_node_fs27.mkdirSync)(dir, { recursive: true });
18748
+ (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
18665
18749
  const file = (0, import_node_path34.join)(dir, name);
18666
- (0, import_node_fs27.writeFileSync)(file, text);
18750
+ (0, import_node_fs26.writeFileSync)(file, text);
18667
18751
  return file;
18668
18752
  } catch {
18669
18753
  return void 0;
@@ -18700,11 +18784,11 @@ function backgroundedText(reason, afterMs) {
18700
18784
  function waitDetachedText(taskId) {
18701
18785
  return `Task ${taskId} is still running and was moved to the background by the user; do not wait again — a <task-notification> arrives when it finishes.`;
18702
18786
  }
18703
- var import_node_fs27, import_node_path34, TASK_RESULT_LIMIT_BYTES, HEAD_SHARE, MAX_TURNS_NOTE, NOTIFY_HINT, TASK_NOTIFICATION_RULE;
18787
+ var import_node_fs26, import_node_path34, TASK_RESULT_LIMIT_BYTES, HEAD_SHARE, MAX_TURNS_NOTE, NOTIFY_HINT, TASK_NOTIFICATION_RULE;
18704
18788
  var init_result = __esm({
18705
18789
  "src/agents/result.ts"() {
18706
18790
  "use strict";
18707
- import_node_fs27 = require("node:fs");
18791
+ import_node_fs26 = require("node:fs");
18708
18792
  import_node_path34 = require("node:path");
18709
18793
  TASK_RESULT_LIMIT_BYTES = 50 * 1024;
18710
18794
  HEAD_SHARE = 0.7;
@@ -19303,9 +19387,9 @@ function truncateResult(call, result, options, nested = false) {
19303
19387
  let where = "full output not saved";
19304
19388
  if (options.outputDir !== void 0) {
19305
19389
  try {
19306
- (0, import_node_fs28.mkdirSync)(options.outputDir, { recursive: true });
19390
+ (0, import_node_fs27.mkdirSync)(options.outputDir, { recursive: true });
19307
19391
  const file = (0, import_node_path35.join)(options.outputDir, `${call.id.replace(/[^A-Za-z0-9_-]/g, "_")}.txt`);
19308
- (0, import_node_fs28.writeFileSync)(file, text, "utf8");
19392
+ (0, import_node_fs27.writeFileSync)(file, text, "utf8");
19309
19393
  where = `full output at ${file}`;
19310
19394
  } catch {
19311
19395
  }
@@ -19520,11 +19604,11 @@ async function runSingleToolCall(call, assistant, options, signal, nested) {
19520
19604
  await emitEnd(call, result, emit);
19521
19605
  return result;
19522
19606
  }
19523
- var import_node_fs28, import_node_path35, ABORTED_TOOL_TEXT, DEFAULT_MAX_TOOL_RESULT_CHARS, DEFAULT_ABORT_GRACE_MS, NESTED_MAX_RESULT_CHARS, autoDecisions, deniedCalls, batchSuffixSources;
19607
+ var import_node_fs27, import_node_path35, ABORTED_TOOL_TEXT, DEFAULT_MAX_TOOL_RESULT_CHARS, DEFAULT_ABORT_GRACE_MS, NESTED_MAX_RESULT_CHARS, autoDecisions, deniedCalls, batchSuffixSources;
19524
19608
  var init_tool_runner = __esm({
19525
19609
  "src/agent/tool-runner.ts"() {
19526
19610
  "use strict";
19527
- import_node_fs28 = require("node:fs");
19611
+ import_node_fs27 = require("node:fs");
19528
19612
  import_node_path35 = require("node:path");
19529
19613
  init_loop_guard();
19530
19614
  init_schema2();
@@ -21577,9 +21661,9 @@ function materialize(candidate, outputDir) {
21577
21661
  if (outputDir !== void 0) {
21578
21662
  fullTextPath = (0, import_node_path37.join)(outputDir, `${safeFileStem(message.toolCallId)}.txt`);
21579
21663
  try {
21580
- if (!(0, import_node_fs29.existsSync)(fullTextPath)) {
21581
- (0, import_node_fs29.mkdirSync)(outputDir, { recursive: true });
21582
- (0, import_node_fs29.writeFileSync)(fullTextPath, text, "utf8");
21664
+ if (!(0, import_node_fs28.existsSync)(fullTextPath)) {
21665
+ (0, import_node_fs28.mkdirSync)(outputDir, { recursive: true });
21666
+ (0, import_node_fs28.writeFileSync)(fullTextPath, text, "utf8");
21583
21667
  }
21584
21668
  } catch {
21585
21669
  fullTextPath = void 0;
@@ -21614,11 +21698,11 @@ function planPrune(items, options) {
21614
21698
  }
21615
21699
  return { items: plan2, savedTokens, availableTokens };
21616
21700
  }
21617
- var import_node_fs29, import_node_path37, PRUNE_TRIGGER_RATIO, PRUNE_TARGET_RATIO, PRUNE_KEEP_RESULTS, PRUNE_PROTECT_TOKENS, PRUNE_PROTECT_RATIO, PRUNE_CLEAR_AT_LEAST, PRUNE_CLEAR_AT_LEAST_RATIO, PRUNE_CLEAR_AT_LEAST_CAP_RATIO, PRUNE_MIN_TOKENS, REPLACEMENT_TOKENS;
21701
+ var import_node_fs28, import_node_path37, PRUNE_TRIGGER_RATIO, PRUNE_TARGET_RATIO, PRUNE_KEEP_RESULTS, PRUNE_PROTECT_TOKENS, PRUNE_PROTECT_RATIO, PRUNE_CLEAR_AT_LEAST, PRUNE_CLEAR_AT_LEAST_RATIO, PRUNE_CLEAR_AT_LEAST_CAP_RATIO, PRUNE_MIN_TOKENS, REPLACEMENT_TOKENS;
21618
21702
  var init_prune_tier = __esm({
21619
21703
  "src/compaction/prune-tier.ts"() {
21620
21704
  "use strict";
21621
- import_node_fs29 = require("node:fs");
21705
+ import_node_fs28 = require("node:fs");
21622
21706
  import_node_path37 = require("node:path");
21623
21707
  init_estimate();
21624
21708
  PRUNE_TRIGGER_RATIO = 0.7;
@@ -22075,9 +22159,9 @@ function isTaskHead(entry) {
22075
22159
  const data2 = entry.data;
22076
22160
  return data2?.context !== "fork";
22077
22161
  }
22078
- function readHead2(file) {
22162
+ function readHead(file) {
22079
22163
  try {
22080
- const [header, first] = (0, import_node_fs30.readFileSync)(file, "utf8").split("\n", 2);
22164
+ const [header, first] = (0, import_node_fs29.readFileSync)(file, "utf8").split("\n", 2);
22081
22165
  const parsed = JSON.parse(header ?? "{}");
22082
22166
  const entry = first ? JSON.parse(first) : void 0;
22083
22167
  return { ...parsed, task: isTaskHead(entry) };
@@ -22095,18 +22179,18 @@ function cacheKeyOf(manager) {
22095
22179
  let id = manager.id;
22096
22180
  let file = header.parentSession;
22097
22181
  for (let depth = 0; depth < 8 && file !== void 0; depth++) {
22098
- const next = readHead2(file);
22182
+ const next = readHead(file);
22099
22183
  if (next.id === void 0 || next.task) break;
22100
22184
  id = next.id;
22101
22185
  file = next.parentSession;
22102
22186
  }
22103
22187
  return id;
22104
22188
  }
22105
- var import_node_fs30, TASK_CUSTOM_TYPE;
22189
+ var import_node_fs29, TASK_CUSTOM_TYPE;
22106
22190
  var init_session_cache_key = __esm({
22107
22191
  "src/agent/session-cache-key.ts"() {
22108
22192
  "use strict";
22109
- import_node_fs30 = require("node:fs");
22193
+ import_node_fs29 = require("node:fs");
22110
22194
  TASK_CUSTOM_TYPE = "ama.task";
22111
22195
  }
22112
22196
  });
@@ -25068,7 +25152,7 @@ function listSessionItems(dir) {
25068
25152
  file,
25069
25153
  cwd: header.cwd,
25070
25154
  createdAt: header.timestamp,
25071
- modifiedAt: (0, import_node_fs31.statSync)(file).mtime.toISOString(),
25155
+ modifiedAt: (0, import_node_fs30.statSync)(file).mtime.toISOString(),
25072
25156
  messageCount
25073
25157
  };
25074
25158
  if (name !== void 0) item.name = name;
@@ -25080,11 +25164,11 @@ function listSessionItems(dir) {
25080
25164
  }
25081
25165
  return items;
25082
25166
  }
25083
- var import_node_fs31, SkipFile;
25167
+ var import_node_fs30, SkipFile;
25084
25168
  var init_list = __esm({
25085
25169
  "src/session/list.ts"() {
25086
25170
  "use strict";
25087
- import_node_fs31 = require("node:fs");
25171
+ import_node_fs30 = require("node:fs");
25088
25172
  init_line_reader();
25089
25173
  init_store();
25090
25174
  init_types6();
@@ -25093,6 +25177,163 @@ var init_list = __esm({
25093
25177
  }
25094
25178
  });
25095
25179
 
25180
+ // src/session/offload.ts
25181
+ function isImage(block) {
25182
+ return typeof block === "object" && block !== null && block.type === "image" && typeof block.data === "string";
25183
+ }
25184
+ function contentOf(entry) {
25185
+ if (entry.type !== "message") return void 0;
25186
+ const content = entry.message.content;
25187
+ return Array.isArray(content) ? content : void 0;
25188
+ }
25189
+ function hasImages(entry) {
25190
+ return contentOf(entry)?.some(isImage) === true;
25191
+ }
25192
+ function readLineAt(file, locator) {
25193
+ const fd = (0, import_node_fs31.openSync)(file, "r");
25194
+ try {
25195
+ const buf = Buffer.allocUnsafe(locator.length);
25196
+ let read2 = 0;
25197
+ while (read2 < locator.length) {
25198
+ const n = (0, import_node_fs31.readSync)(fd, buf, read2, locator.length - read2, locator.offset + read2);
25199
+ if (n === 0) throw new Error("unexpected end of file");
25200
+ read2 += n;
25201
+ }
25202
+ return JSON.parse(buf.toString("utf8"));
25203
+ } finally {
25204
+ (0, import_node_fs31.closeSync)(fd);
25205
+ }
25206
+ }
25207
+ function editTargets(file) {
25208
+ const targets = /* @__PURE__ */ new Set();
25209
+ try {
25210
+ forEachLineSync(file, (line2) => {
25211
+ if (lineTypeOf(line2)?.type !== "context_edit") return;
25212
+ try {
25213
+ const id = JSON.parse(line2.toString("utf8")).targetId;
25214
+ if (typeof id === "string") targets.add(id);
25215
+ } catch {
25216
+ }
25217
+ });
25218
+ } catch {
25219
+ }
25220
+ return targets;
25221
+ }
25222
+ var import_node_fs31, placeholder, ImageOffload;
25223
+ var init_offload = __esm({
25224
+ "src/session/offload.ts"() {
25225
+ "use strict";
25226
+ import_node_fs31 = require("node:fs");
25227
+ init_image_intern();
25228
+ init_line_reader();
25229
+ init_projection();
25230
+ placeholder = (block) => ({ ...block, data: "" });
25231
+ ImageOffload = class {
25232
+ constructor(warn2 = () => {
25233
+ }) {
25234
+ this.warn = warn2;
25235
+ }
25236
+ warn;
25237
+ /** 含图 message 条目的行位置(只有已落盘的条目才有)。 */
25238
+ locators = /* @__PURE__ */ new Map();
25239
+ offloaded = /* @__PURE__ */ new Set();
25240
+ /** 记下条目的行位置;不含图片的条目忽略。 */
25241
+ track(entry, locator) {
25242
+ if (hasImages(entry)) this.locators.set(entry.id, locator);
25243
+ }
25244
+ /** 读文件时逐行调用:记下行位置;`strip` 时就地换成占位(刚解析的对象,别处没有引用)。 */
25245
+ load(line2, locator, strip) {
25246
+ if (typeof line2.id !== "string" || !hasImages(line2)) return;
25247
+ this.locators.set(line2.id, locator);
25248
+ if (!strip) return;
25249
+ const content = contentOf(line2);
25250
+ content.forEach((block, i) => {
25251
+ if (isImage(block)) content[i] = placeholder(block);
25252
+ });
25253
+ this.offloaded.add(line2.id);
25254
+ }
25255
+ isOffloaded(id) {
25256
+ return this.offloaded.has(id);
25257
+ }
25258
+ /** 已卸载的条目数(测试与诊断用)。 */
25259
+ get size() {
25260
+ return this.offloaded.size;
25261
+ }
25262
+ /** 把条目里的图片换成占位;没有行位置(未落盘、不含图)或已卸载时返回 false。 */
25263
+ offload(entry) {
25264
+ if (entry.type !== "message" || this.offloaded.has(entry.id)) return false;
25265
+ const content = contentOf(entry);
25266
+ if (content === void 0 || !this.locators.has(entry.id)) return false;
25267
+ entry.message = {
25268
+ ...entry.message,
25269
+ content: content.map((block) => isImage(block) ? placeholder(block) : block)
25270
+ };
25271
+ this.offloaded.add(entry.id);
25272
+ return true;
25273
+ }
25274
+ /** 就地放回图片;读不回时返回 false(占位保留,之后不再卸载 / 回读这一条)。 */
25275
+ hydrate(file, entry) {
25276
+ if (!this.offloaded.has(entry.id)) return true;
25277
+ this.offloaded.delete(entry.id);
25278
+ const source = this.read(file, entry);
25279
+ const content = contentOf(entry);
25280
+ if (source === void 0 || content === void 0) return false;
25281
+ const originals = contentOf(source) ?? [];
25282
+ content.forEach((block, i) => {
25283
+ const original = originals[i];
25284
+ if (isImage(block) && block.data === "" && isImage(original)) {
25285
+ content[i] = internImage(original);
25286
+ }
25287
+ });
25288
+ return true;
25289
+ }
25290
+ /** 原文:已卸载的条目从文件读一份新副本(图片驻留),否则原样返回。读不回时返回占位版本。 */
25291
+ original(file, entry) {
25292
+ if (!this.offloaded.has(entry.id)) return entry;
25293
+ const copy = this.read(file, entry);
25294
+ if (copy === void 0) return entry;
25295
+ const content = contentOf(copy) ?? [];
25296
+ content.forEach((block, i) => {
25297
+ if (isImage(block)) content[i] = internImage(block);
25298
+ });
25299
+ return copy;
25300
+ }
25301
+ /** 按活动分支对齐:分支上被生效编辑改写的含图条目卸载,其余已卸载的回读。 */
25302
+ sync(file, branch, index2) {
25303
+ const edits = collectContextEdits(branch);
25304
+ const onBranch = /* @__PURE__ */ new Set();
25305
+ for (const entry of branch) if (edits.has(entry.id)) onBranch.add(entry.id);
25306
+ for (const id of [...this.offloaded]) {
25307
+ const entry = index2.get(id);
25308
+ if (entry !== void 0 && !onBranch.has(id)) this.hydrate(file, entry);
25309
+ }
25310
+ for (const id of onBranch) {
25311
+ const entry = index2.get(id);
25312
+ if (entry !== void 0) this.offload(entry);
25313
+ }
25314
+ }
25315
+ /** 从文件读回条目(新对象);不成功时告警、忘掉行位置并返回 undefined。 */
25316
+ read(file, entry) {
25317
+ const locator = this.locators.get(entry.id);
25318
+ try {
25319
+ if (locator === void 0) throw new Error("no line locator");
25320
+ const line2 = readLineAt(file, locator);
25321
+ const content = line2.id === entry.id ? contentOf(line2) : void 0;
25322
+ if (content === void 0 || content.length !== contentOf(entry)?.length) {
25323
+ throw new Error("line does not match the entry");
25324
+ }
25325
+ return line2;
25326
+ } catch (error2) {
25327
+ this.locators.delete(entry.id);
25328
+ this.offloaded.delete(entry.id);
25329
+ this.warn(`cannot read session entry ${entry.id} back from ${file}: ${String(error2)}`);
25330
+ return void 0;
25331
+ }
25332
+ }
25333
+ };
25334
+ }
25335
+ });
25336
+
25096
25337
  // src/session/manager.ts
25097
25338
  function newEntryId(taken) {
25098
25339
  for (; ; ) {
@@ -25111,6 +25352,7 @@ var init_manager = __esm({
25111
25352
  init_version();
25112
25353
  init_list();
25113
25354
  init_migrate();
25355
+ init_offload();
25114
25356
  init_store();
25115
25357
  init_tree();
25116
25358
  init_types6();
@@ -25124,7 +25366,11 @@ var init_manager = __esm({
25124
25366
  storage;
25125
25367
  closed = false;
25126
25368
  now;
25127
- constructor(header, entries, storage, now, leafId) {
25369
+ images;
25370
+ warn;
25371
+ /** 已知的会话文件字节数(新行的行首偏移)。 */
25372
+ fileBytes = 0;
25373
+ constructor(header, entries, storage, now, leafId, warn2, images) {
25128
25374
  this._header = header;
25129
25375
  this.id = header.id;
25130
25376
  this.cwd = header.cwd;
@@ -25134,6 +25380,8 @@ var init_manager = __esm({
25134
25380
  this.leaf = leafId === void 0 || leafId !== null && !this.index.has(leafId) ? last : leafId;
25135
25381
  this.storage = storage;
25136
25382
  this.now = now;
25383
+ this.warn = warn2;
25384
+ this.images = images ?? new ImageOffload(warn2);
25137
25385
  }
25138
25386
  static newHeader(cwd, options, now) {
25139
25387
  const header = {
@@ -25154,7 +25402,14 @@ var init_manager = __esm({
25154
25402
  /** `dir` 是会话文件所在目录(通常是 `sessionDirForCwd(sessionsRoot, cwd)`)。 */
25155
25403
  static create(dir, cwd, options = {}) {
25156
25404
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
25157
- return new _SessionManager(this.newHeader(cwd, options, now()), [], { kind: "lazy", dir }, now);
25405
+ return new _SessionManager(
25406
+ this.newHeader(cwd, options, now()),
25407
+ [],
25408
+ { kind: "lazy", dir },
25409
+ now,
25410
+ void 0,
25411
+ options.warn
25412
+ );
25158
25413
  }
25159
25414
  static createForCwd(sessionsRoot, cwd, options = {}) {
25160
25415
  return this.create(sessionDirForCwd(sessionsRoot, cwd), cwd, options);
@@ -25162,16 +25417,29 @@ var init_manager = __esm({
25162
25417
  static open(file, options = {}) {
25163
25418
  const lock = acquireLock(file);
25164
25419
  try {
25165
- const { lines: lines2 } = readSessionLines(file, { repair: true });
25420
+ const images = new ImageOffload(options.warn);
25421
+ const targets = editTargets(file);
25422
+ const { lines: lines2, bytes } = readSessionLines(file, {
25423
+ repair: true,
25424
+ onLine: (line2, locator) => {
25425
+ const entry = line2;
25426
+ images.load(entry, locator, targets.has(entry.id));
25427
+ }
25428
+ });
25166
25429
  const { header, entries, leafId } = migrateSessionLines(lines2, file);
25167
25430
  internSessionImages(entries);
25168
- return new _SessionManager(
25431
+ const manager = new _SessionManager(
25169
25432
  header,
25170
25433
  entries,
25171
25434
  { kind: "file", file, lock },
25172
25435
  options.now ?? (() => /* @__PURE__ */ new Date()),
25173
- leafId
25436
+ leafId,
25437
+ options.warn,
25438
+ images
25174
25439
  );
25440
+ manager.fileBytes = bytes ?? 0;
25441
+ manager.syncImages();
25442
+ return manager;
25175
25443
  } catch (error2) {
25176
25444
  lock.release();
25177
25445
  throw error2;
@@ -25212,10 +25480,18 @@ var init_manager = __esm({
25212
25480
  parentId: this.leaf,
25213
25481
  timestamp: this.now().toISOString()
25214
25482
  };
25215
- if (this.storage.kind === "file") appendLines(this.storage.file, [entry]);
25483
+ if (this.storage.kind === "file") {
25484
+ const written = appendLines(this.storage.file, [entry]);
25485
+ this.images.track(entry, { offset: this.fileBytes, length: written - 1 });
25486
+ this.fileBytes += written;
25487
+ }
25216
25488
  this._entries.push(entry);
25217
25489
  this.index.set(entry.id, entry);
25218
25490
  this.leaf = entry.id;
25491
+ if (entry.type === "context_edit" && this.storage.kind === "file") {
25492
+ const target = this.index.get(entry.targetId);
25493
+ if (target !== void 0 && this.branch().includes(target)) this.images.offload(target);
25494
+ }
25219
25495
  return entry;
25220
25496
  }
25221
25497
  setLeaf(id) {
@@ -25223,7 +25499,24 @@ var init_manager = __esm({
25223
25499
  throw new AmaError("invalid_arguments", `no such session entry: ${id}`);
25224
25500
  }
25225
25501
  this.leaf = id;
25226
- if (this.storage.kind === "file") appendLines(this.storage.file, [this.leafLine()]);
25502
+ if (this.storage.kind === "file") {
25503
+ this.fileBytes += appendLines(this.storage.file, [this.leafLine()]);
25504
+ }
25505
+ this.syncImages();
25506
+ }
25507
+ /** 按当前活动分支卸载 / 回读图片(只对已落盘的会话)。 */
25508
+ syncImages() {
25509
+ if (this.storage.kind === "file")
25510
+ this.images.sync(this.storage.file, this.branch(), this.index);
25511
+ }
25512
+ /** 原文条目(已卸载的从文件回读一份副本)。 */
25513
+ original(entry) {
25514
+ if (this.storage.kind !== "file" || !this.images.isOffloaded(entry.id)) return entry;
25515
+ return this.images.original(this.storage.file, entry);
25516
+ }
25517
+ /** 已卸载图片的条目数(测试与诊断用)。 */
25518
+ offloadedCount() {
25519
+ return this.images.size;
25227
25520
  }
25228
25521
  leafLine() {
25229
25522
  return { type: "leaf", id: this.leaf, timestamp: this.now().toISOString() };
@@ -25232,10 +25525,9 @@ var init_manager = __esm({
25232
25525
  return pathToRoot(this.index, leafId === void 0 ? this.leaf : leafId);
25233
25526
  }
25234
25527
  getEntries(since) {
25235
- if (since === void 0) return { entries: [...this._entries], leafId: this.leaf };
25236
- const at = this._entries.findIndex((entry) => entry.id === since);
25528
+ const at = since === void 0 ? -1 : this._entries.findIndex((entry) => entry.id === since);
25237
25529
  return {
25238
- entries: at < 0 ? [...this._entries] : this._entries.slice(at + 1),
25530
+ entries: this._entries.slice(at + 1).map((entry) => this.original(entry)),
25239
25531
  leafId: this.leaf
25240
25532
  };
25241
25533
  }
@@ -25254,7 +25546,10 @@ var init_manager = __esm({
25254
25546
  if (!this.index.has(entryId)) {
25255
25547
  throw new AmaError("invalid_arguments", `no such session entry: ${entryId}`);
25256
25548
  }
25257
- const copied = this.branch(entryId).map((entry) => structuredClone(entry));
25549
+ const copied = this.branch(entryId).map((entry) => {
25550
+ const original = this.original(entry);
25551
+ return original === entry ? structuredClone(entry) : original;
25552
+ });
25258
25553
  if (forkOptions.head !== void 0) {
25259
25554
  const head = {
25260
25555
  ...forkOptions.head,
@@ -25273,7 +25568,7 @@ var init_manager = __esm({
25273
25568
  if (this.storage.kind === "memory") storage = { kind: "memory" };
25274
25569
  else if (this.storage.kind === "lazy") storage = { kind: "lazy", dir: this.storage.dir };
25275
25570
  else storage = { kind: "lazy", dir: (0, import_node_path41.dirname)(this.storage.file) };
25276
- const forked = new _SessionManager(header, copied, storage, this.now);
25571
+ const forked = new _SessionManager(header, copied, storage, this.now, void 0, this.warn);
25277
25572
  if (parentFile !== void 0) forked.flush();
25278
25573
  return forked;
25279
25574
  }
@@ -25287,11 +25582,18 @@ var init_manager = __esm({
25287
25582
  if (this.storage.kind === "file") return this.storage.file;
25288
25583
  const file = (0, import_node_path41.join)(this.storage.dir, sessionFileName(new Date(this._header.timestamp), this.id));
25289
25584
  const moved = this.leaf !== (this._entries.at(-1)?.id ?? null);
25290
- writeNewSessionFile(file, [
25585
+ const lengths = writeNewSessionFile(file, [
25291
25586
  this._header,
25292
25587
  ...this._entries,
25293
25588
  ...moved ? [this.leafLine()] : []
25294
25589
  ]);
25590
+ let offset = 0;
25591
+ lengths.forEach((length, i) => {
25592
+ const entry = i === 0 ? void 0 : this._entries[i - 1];
25593
+ if (entry !== void 0) this.images.track(entry, { offset, length });
25594
+ offset += length + 1;
25595
+ });
25596
+ this.fileBytes = offset;
25295
25597
  let lock;
25296
25598
  try {
25297
25599
  lock = acquireLock(file);
@@ -25299,6 +25601,7 @@ var init_manager = __esm({
25299
25601
  lock = void 0;
25300
25602
  }
25301
25603
  this.storage = { kind: "file", file, lock };
25604
+ this.syncImages();
25302
25605
  return file;
25303
25606
  }
25304
25607
  isPersisted() {
@@ -29168,16 +29471,16 @@ async function putBlob(dataDir, bytes, hash = hashBytes(bytes)) {
29168
29471
  const target = blobPath(dataDir, hash);
29169
29472
  if (await exists3(target)) {
29170
29473
  const now = /* @__PURE__ */ new Date();
29171
- await (0, import_promises11.utimes)(target, now, now).catch(() => void 0);
29474
+ await (0, import_promises12.utimes)(target, now, now).catch(() => void 0);
29172
29475
  return hash;
29173
29476
  }
29174
- await (0, import_promises11.mkdir)((0, import_node_path47.dirname)(target), { recursive: true, mode: 448 });
29477
+ await (0, import_promises12.mkdir)((0, import_node_path47.dirname)(target), { recursive: true, mode: 448 });
29175
29478
  const tmp = `${target}.tmp-${process.pid}-${tmpCounter++}`;
29176
29479
  try {
29177
- await (0, import_promises11.writeFile)(tmp, bytes, { mode: 384 });
29178
- await (0, import_promises11.rename)(tmp, target);
29480
+ await (0, import_promises12.writeFile)(tmp, bytes, { mode: 384 });
29481
+ await (0, import_promises12.rename)(tmp, target);
29179
29482
  } catch (error2) {
29180
- await (0, import_promises11.rm)(tmp, { force: true });
29483
+ await (0, import_promises12.rm)(tmp, { force: true });
29181
29484
  if (await exists3(target)) return hash;
29182
29485
  throw error2;
29183
29486
  }
@@ -29185,7 +29488,7 @@ async function putBlob(dataDir, bytes, hash = hashBytes(bytes)) {
29185
29488
  }
29186
29489
  async function readBlob(dataDir, hash) {
29187
29490
  try {
29188
- return await (0, import_promises11.readFile)(blobPath(dataDir, hash));
29491
+ return await (0, import_promises12.readFile)(blobPath(dataDir, hash));
29189
29492
  } catch (error2) {
29190
29493
  if (isNotFound(error2)) return void 0;
29191
29494
  throw error2;
@@ -29193,7 +29496,7 @@ async function readBlob(dataDir, hash) {
29193
29496
  }
29194
29497
  async function realParentOf(absolutePath) {
29195
29498
  try {
29196
- return await (0, import_promises11.realpath)((0, import_node_path47.dirname)(absolutePath));
29499
+ return await (0, import_promises12.realpath)((0, import_node_path47.dirname)(absolutePath));
29197
29500
  } catch (error2) {
29198
29501
  if (isNotFound(error2)) return void 0;
29199
29502
  throw error2;
@@ -29204,7 +29507,7 @@ async function recordFile(absolutePath, options) {
29204
29507
  const max = options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
29205
29508
  let info2;
29206
29509
  try {
29207
- info2 = await (0, import_promises11.lstat)(absolutePath);
29510
+ info2 = await (0, import_promises12.lstat)(absolutePath);
29208
29511
  } catch (error2) {
29209
29512
  if (!isNotFound(error2)) throw error2;
29210
29513
  const realParentDir2 = await realParentOf(absolutePath);
@@ -29218,7 +29521,7 @@ async function recordFile(absolutePath, options) {
29218
29521
  const realParentDir = await realParentOf(absolutePath);
29219
29522
  const base4 = { mode, size: info2.size, ...realParentDir !== void 0 ? { realParentDir } : {} };
29220
29523
  if (info2.size > max) return { record: { blob: null, ...base4, skipped: "too_large" }, at };
29221
- const bytes = await (0, import_promises11.readFile)(absolutePath);
29524
+ const bytes = await (0, import_promises12.readFile)(absolutePath);
29222
29525
  if (bytes.length > max)
29223
29526
  return { record: { blob: null, ...base4, size: bytes.length, skipped: "too_large" }, at };
29224
29527
  const blob = await putBlob(options.dataDir, bytes);
@@ -29245,7 +29548,7 @@ async function listBlobs(dataDir, includeTemp = false) {
29245
29548
  const root = blobsDir(dataDir);
29246
29549
  let shards;
29247
29550
  try {
29248
- shards = await (0, import_promises11.readdir)(root);
29551
+ shards = await (0, import_promises12.readdir)(root);
29249
29552
  } catch (error2) {
29250
29553
  if (isNotFound(error2)) return [];
29251
29554
  throw error2;
@@ -29255,7 +29558,7 @@ async function listBlobs(dataDir, includeTemp = false) {
29255
29558
  if (!/^[0-9a-f]{2}$/.test(shard)) continue;
29256
29559
  let names;
29257
29560
  try {
29258
- names = await (0, import_promises11.readdir)((0, import_node_path47.join)(root, shard));
29561
+ names = await (0, import_promises12.readdir)((0, import_node_path47.join)(root, shard));
29259
29562
  } catch {
29260
29563
  continue;
29261
29564
  }
@@ -29264,7 +29567,7 @@ async function listBlobs(dataDir, includeTemp = false) {
29264
29567
  if (!isBlob && !(includeTemp && name.includes(".tmp-"))) continue;
29265
29568
  const path = (0, import_node_path47.join)(root, shard, name);
29266
29569
  try {
29267
- const info2 = await (0, import_promises11.stat)(path);
29570
+ const info2 = await (0, import_promises12.stat)(path);
29268
29571
  if (!info2.isFile()) continue;
29269
29572
  out.push({
29270
29573
  path,
@@ -29283,19 +29586,19 @@ function isNotFound(error2) {
29283
29586
  }
29284
29587
  async function exists3(path) {
29285
29588
  try {
29286
- await (0, import_promises11.stat)(path);
29589
+ await (0, import_promises12.stat)(path);
29287
29590
  return true;
29288
29591
  } catch {
29289
29592
  return false;
29290
29593
  }
29291
29594
  }
29292
- var import_node_crypto9, import_node_fs34, import_promises11, import_node_path47, FILE_HISTORY_DIR, BLOBS_DIR, DEFAULT_MAX_FILE_BYTES, SHA256_HEX, tmpCounter;
29595
+ var import_node_crypto9, import_node_fs34, import_promises12, import_node_path47, FILE_HISTORY_DIR, BLOBS_DIR, DEFAULT_MAX_FILE_BYTES, SHA256_HEX, tmpCounter;
29293
29596
  var init_blobs = __esm({
29294
29597
  "src/checkpoints/blobs.ts"() {
29295
29598
  "use strict";
29296
29599
  import_node_crypto9 = require("node:crypto");
29297
29600
  import_node_fs34 = require("node:fs");
29298
- import_promises11 = require("node:fs/promises");
29601
+ import_promises12 = require("node:fs/promises");
29299
29602
  import_node_path47 = require("node:path");
29300
29603
  FILE_HISTORY_DIR = "file-history";
29301
29604
  BLOBS_DIR = "blobs";
@@ -29311,7 +29614,7 @@ function rootsFile(dataDir) {
29311
29614
  }
29312
29615
  async function readSessionRoots(dataDir) {
29313
29616
  try {
29314
- const value = JSON.parse(await (0, import_promises12.readFile)(rootsFile(dataDir), "utf8"));
29617
+ const value = JSON.parse(await (0, import_promises13.readFile)(rootsFile(dataDir), "utf8"));
29315
29618
  const roots = value?.roots;
29316
29619
  return Array.isArray(roots) ? roots.filter((r) => typeof r === "string") : [];
29317
29620
  } catch {
@@ -29323,18 +29626,18 @@ async function registerSessionRoot(dataDir, sessionsRoot) {
29323
29626
  const roots = await readSessionRoots(dataDir);
29324
29627
  if (roots.includes(root)) return;
29325
29628
  const dir = fileHistoryDir(dataDir);
29326
- await (0, import_promises12.mkdir)(dir, { recursive: true, mode: 448 });
29629
+ await (0, import_promises13.mkdir)(dir, { recursive: true, mode: 448 });
29327
29630
  const tmp = `${rootsFile(dataDir)}.tmp-${process.pid}`;
29328
- await (0, import_promises12.writeFile)(tmp, `${JSON.stringify({ roots: [...roots, root] }, null, 2)}
29631
+ await (0, import_promises13.writeFile)(tmp, `${JSON.stringify({ roots: [...roots, root] }, null, 2)}
29329
29632
  `, {
29330
29633
  mode: 384
29331
29634
  });
29332
- await (0, import_promises12.rename)(tmp, rootsFile(dataDir));
29635
+ await (0, import_promises13.rename)(tmp, rootsFile(dataDir));
29333
29636
  }
29334
29637
  async function* sessionFiles(dir) {
29335
29638
  let entries;
29336
29639
  try {
29337
- entries = await (0, import_promises12.readdir)(dir, { withFileTypes: true });
29640
+ entries = await (0, import_promises13.readdir)(dir, { withFileTypes: true });
29338
29641
  } catch (error2) {
29339
29642
  if (isNotFound(error2)) return;
29340
29643
  throw error2;
@@ -29356,7 +29659,7 @@ async function collectBlobRefs(sessionRoots) {
29356
29659
  for await (const file of sessionFiles(abs)) {
29357
29660
  let text;
29358
29661
  try {
29359
- text = await (0, import_promises12.readFile)(file, "utf8");
29662
+ text = await (0, import_promises13.readFile)(file, "utf8");
29360
29663
  } catch (error2) {
29361
29664
  if (isNotFound(error2)) continue;
29362
29665
  throw error2;
@@ -29389,7 +29692,7 @@ async function gcBlobs(options) {
29389
29692
  if (old && unreferenced) {
29390
29693
  if (options.dryRun !== true) {
29391
29694
  try {
29392
- await (0, import_promises12.rm)(blob.path, { force: true });
29695
+ await (0, import_promises13.rm)(blob.path, { force: true });
29393
29696
  } catch {
29394
29697
  continue;
29395
29698
  }
@@ -29414,11 +29717,11 @@ function formatBytes2(bytes) {
29414
29717
  }
29415
29718
  return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unit]}`;
29416
29719
  }
29417
- var import_promises12, import_node_path48, GC_MIN_AGE_MS, ROOTS_FILE, HASH_IN_LINE;
29720
+ var import_promises13, import_node_path48, GC_MIN_AGE_MS, ROOTS_FILE, HASH_IN_LINE;
29418
29721
  var init_gc = __esm({
29419
29722
  "src/checkpoints/gc.ts"() {
29420
29723
  "use strict";
29421
- import_promises12 = require("node:fs/promises");
29724
+ import_promises13 = require("node:fs/promises");
29422
29725
  import_node_path48 = require("node:path");
29423
29726
  init_blobs();
29424
29727
  init_types10();
@@ -29547,7 +29850,7 @@ var init_replay = __esm({
29547
29850
  // src/checkpoints/git-head.ts
29548
29851
  async function readText2(path) {
29549
29852
  try {
29550
- return await (0, import_promises13.readFile)(path, "utf8");
29853
+ return await (0, import_promises14.readFile)(path, "utf8");
29551
29854
  } catch {
29552
29855
  return void 0;
29553
29856
  }
@@ -29557,7 +29860,7 @@ async function findGitDir(cwd) {
29557
29860
  for (; ; ) {
29558
29861
  const candidate = (0, import_node_path49.join)(dir, ".git");
29559
29862
  try {
29560
- const info2 = await (0, import_promises13.stat)(candidate);
29863
+ const info2 = await (0, import_promises14.stat)(candidate);
29561
29864
  if (info2.isDirectory()) return candidate;
29562
29865
  if (info2.isFile()) {
29563
29866
  const text = await readText2(candidate) ?? "";
@@ -29605,11 +29908,11 @@ async function readGitHead(cwd) {
29605
29908
  const branch = ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : void 0;
29606
29909
  return { head: sha, ...branch !== void 0 ? { branch } : {} };
29607
29910
  }
29608
- var import_promises13, import_node_path49, SHA;
29911
+ var import_promises14, import_node_path49, SHA;
29609
29912
  var init_git_head = __esm({
29610
29913
  "src/checkpoints/git-head.ts"() {
29611
29914
  "use strict";
29612
- import_promises13 = require("node:fs/promises");
29915
+ import_promises14 = require("node:fs/promises");
29613
29916
  import_node_path49 = require("node:path");
29614
29917
  SHA = /^[0-9a-f]{40}([0-9a-f]{24})?$/;
29615
29918
  }
@@ -29629,7 +29932,7 @@ function emptyCodeResult() {
29629
29932
  }
29630
29933
  async function lstatOrUndefined(path) {
29631
29934
  try {
29632
- return await (0, import_promises14.lstat)(path);
29935
+ return await (0, import_promises15.lstat)(path);
29633
29936
  } catch (error2) {
29634
29937
  if (isNotFound(error2)) return void 0;
29635
29938
  throw error2;
@@ -29718,7 +30021,7 @@ async function restoreFile(abs, record3, options) {
29718
30021
  await checkParent(abs, record3);
29719
30022
  const targetBytes = record3.blob === null ? void 0 : await (options.load ?? ((r) => readBlob(options.dataDir, r.blob)))(record3);
29720
30023
  if (record3.blob !== null && targetBytes === void 0) throw new Skip("backup_missing");
29721
- const currentBytes = info2 !== void 0 && info2.size <= options.maxDiff ? await (0, import_promises14.readFile)(abs) : void 0;
30024
+ const currentBytes = info2 !== void 0 && info2.size <= options.maxDiff ? await (0, import_promises15.readFile)(abs) : void 0;
29722
30025
  const counted = info2 !== void 0 && currentBytes === void 0 ? { insertions: 0, deletions: 0 } : countLineChanges(toText(currentBytes), toText(targetBytes));
29723
30026
  const outcome = { deleted: record3.blob === null, conflict, ...counted };
29724
30027
  if (options.dryRun === true) return outcome;
@@ -29742,17 +30045,17 @@ async function removeRegular(abs, before2) {
29742
30045
  if (now.nlink > 1) throw new Skip("hardlink");
29743
30046
  if (now.dev !== before2.dev || now.ino !== before2.ino)
29744
30047
  throw new Error(msg().session.checkpoints.replacedDuringRestore);
29745
- await (0, import_promises14.unlink)(abs);
30048
+ await (0, import_promises15.unlink)(abs);
29746
30049
  }
29747
30050
  async function writeBack(abs, bytes, record3, before2) {
29748
30051
  if (before2 === void 0) {
29749
30052
  const parent = (0, import_node_path50.dirname)(abs);
29750
- await (0, import_promises14.mkdir)(parent, { recursive: true });
29751
- if (record3.realParentDir !== void 0 && await (0, import_promises14.realpath)(parent) !== record3.realParentDir) {
30053
+ await (0, import_promises15.mkdir)(parent, { recursive: true });
30054
+ if (record3.realParentDir !== void 0 && await (0, import_promises15.realpath)(parent) !== record3.realParentDir) {
29752
30055
  throw new Skip("parent_moved");
29753
30056
  }
29754
30057
  const flags2 = import_node_fs35.constants.O_WRONLY | import_node_fs35.constants.O_CREAT | import_node_fs35.constants.O_EXCL | NOFOLLOW;
29755
- const handle2 = await (0, import_promises14.open)(abs, flags2, record3.mode ?? 420);
30058
+ const handle2 = await (0, import_promises15.open)(abs, flags2, record3.mode ?? 420);
29756
30059
  try {
29757
30060
  await handle2.writeFile(bytes);
29758
30061
  if (record3.mode !== void 0) await handle2.chmod(record3.mode);
@@ -29763,7 +30066,7 @@ async function writeBack(abs, bytes, record3, before2) {
29763
30066
  }
29764
30067
  let handle;
29765
30068
  try {
29766
- handle = await (0, import_promises14.open)(abs, import_node_fs35.constants.O_WRONLY | NOFOLLOW);
30069
+ handle = await (0, import_promises15.open)(abs, import_node_fs35.constants.O_WRONLY | NOFOLLOW);
29767
30070
  } catch (error2) {
29768
30071
  const code = error2.code;
29769
30072
  if (code === "ELOOP" || code === "EMLINK") throw new Skip("symlink");
@@ -29771,7 +30074,7 @@ async function writeBack(abs, bytes, record3, before2) {
29771
30074
  }
29772
30075
  try {
29773
30076
  const opened = await handle.stat();
29774
- const now = await (0, import_promises14.lstat)(abs);
30077
+ const now = await (0, import_promises15.lstat)(abs);
29775
30078
  if (!opened.isFile() || opened.dev !== before2.dev || opened.ino !== before2.ino || now.dev !== opened.dev || now.ino !== opened.ino) {
29776
30079
  throw new Error(msg().session.checkpoints.replacedDuringRestore);
29777
30080
  }
@@ -29789,12 +30092,12 @@ async function gitHintFor(cwd, target) {
29789
30092
  if (current3 === void 0 || current3.head === target.git.head) return void 0;
29790
30093
  return { recordedHead: target.git.head, currentHead: current3.head };
29791
30094
  }
29792
- var import_node_fs35, import_promises14, import_node_path50, RestoreSkip, Skip, IS_WINDOWS, NOFOLLOW;
30095
+ var import_node_fs35, import_promises15, import_node_path50, RestoreSkip, Skip, IS_WINDOWS, NOFOLLOW;
29793
30096
  var init_restore = __esm({
29794
30097
  "src/checkpoints/restore.ts"() {
29795
30098
  "use strict";
29796
30099
  import_node_fs35 = require("node:fs");
29797
- import_promises14 = require("node:fs/promises");
30100
+ import_promises15 = require("node:fs/promises");
29798
30101
  import_node_path50 = require("node:path");
29799
30102
  init_edit();
29800
30103
  init_blobs();
@@ -29845,7 +30148,7 @@ async function shadowUsage(dataDir) {
29845
30148
  const usage = { repos: 0, bytes: 0 };
29846
30149
  let names;
29847
30150
  try {
29848
- names = await (0, import_promises15.readdir)(shadowRoot(dataDir));
30151
+ names = await (0, import_promises16.readdir)(shadowRoot(dataDir));
29849
30152
  } catch {
29850
30153
  return usage;
29851
30154
  }
@@ -29861,7 +30164,7 @@ async function duBytes(dir) {
29861
30164
  let total = 0;
29862
30165
  let entries;
29863
30166
  try {
29864
- entries = await (0, import_promises15.readdir)(dir, { withFileTypes: true });
30167
+ entries = await (0, import_promises16.readdir)(dir, { withFileTypes: true });
29865
30168
  } catch {
29866
30169
  return 0;
29867
30170
  }
@@ -29870,7 +30173,7 @@ async function duBytes(dir) {
29870
30173
  if (entry.isDirectory()) total += await duBytes(full);
29871
30174
  else if (entry.isFile()) {
29872
30175
  try {
29873
- total += (await (0, import_promises15.stat)(full)).size;
30176
+ total += (await (0, import_promises16.stat)(full)).size;
29874
30177
  } catch {
29875
30178
  }
29876
30179
  }
@@ -29954,7 +30257,7 @@ function excludeLine(path) {
29954
30257
  async function removeStaleIndexes(dir) {
29955
30258
  let names;
29956
30259
  try {
29957
- names = await (0, import_promises15.readdir)(dir);
30260
+ names = await (0, import_promises16.readdir)(dir);
29958
30261
  } catch {
29959
30262
  return;
29960
30263
  }
@@ -29963,7 +30266,7 @@ async function removeStaleIndexes(dir) {
29963
30266
  if (match === null) continue;
29964
30267
  const pid = Number(match[1]);
29965
30268
  if (pid === process.pid || isAlive(pid)) continue;
29966
- await (0, import_promises15.rm)((0, import_node_path51.join)(dir, name), { force: true }).catch(() => void 0);
30269
+ await (0, import_promises16.rm)((0, import_node_path51.join)(dir, name), { force: true }).catch(() => void 0);
29967
30270
  }
29968
30271
  }
29969
30272
  function isAlive(pid) {
@@ -29976,7 +30279,7 @@ function isAlive(pid) {
29976
30279
  }
29977
30280
  async function exists4(path) {
29978
30281
  try {
29979
- await (0, import_promises15.stat)(path);
30282
+ await (0, import_promises16.stat)(path);
29980
30283
  return true;
29981
30284
  } catch {
29982
30285
  return false;
@@ -29984,18 +30287,18 @@ async function exists4(path) {
29984
30287
  }
29985
30288
  async function readSmall2(path) {
29986
30289
  try {
29987
- return (await (0, import_promises15.readFile)(path, "utf8")).trim();
30290
+ return (await (0, import_promises16.readFile)(path, "utf8")).trim();
29988
30291
  } catch {
29989
30292
  return void 0;
29990
30293
  }
29991
30294
  }
29992
- var import_node_child_process7, import_node_crypto10, import_promises15, import_node_os10, import_node_path51, SHADOW_DIR, SHADOW_MAX_FILES, SHADOW_MAX_SNAPSHOT_MS, GIT_TIMEOUT_MS, CONFIG_VERSION, IDENTITY, ATTRIBUTES, GitMissingError, MODE_ABSENT, MODE_SYMLINK, MODE_GITLINK, MODE_EXECUTABLE, queues2, ShadowRepo;
30295
+ var import_node_child_process7, import_node_crypto10, import_promises16, import_node_os10, import_node_path51, SHADOW_DIR, SHADOW_MAX_FILES, SHADOW_MAX_SNAPSHOT_MS, GIT_TIMEOUT_MS, CONFIG_VERSION, IDENTITY, ATTRIBUTES, GitMissingError, MODE_ABSENT, MODE_SYMLINK, MODE_GITLINK, MODE_EXECUTABLE, queues2, ShadowRepo;
29993
30296
  var init_shadow_git = __esm({
29994
30297
  "src/checkpoints/shadow-git.ts"() {
29995
30298
  "use strict";
29996
30299
  import_node_child_process7 = require("node:child_process");
29997
30300
  import_node_crypto10 = require("node:crypto");
29998
- import_promises15 = require("node:fs/promises");
30301
+ import_promises16 = require("node:fs/promises");
29999
30302
  import_node_os10 = require("node:os");
30000
30303
  import_node_path51 = require("node:path");
30001
30304
  init_blobs();
@@ -30079,9 +30382,9 @@ var init_shadow_git = __esm({
30079
30382
  }
30080
30383
  async doInit() {
30081
30384
  const hooks = (0, import_node_path51.join)(this.dir, "ama-hooks");
30082
- await (0, import_promises15.mkdir)(hooks, { recursive: true, mode: 448 });
30083
- await (0, import_promises15.writeFile)((0, import_node_path51.join)(this.dir, "ama-empty"), "", { flag: "a", mode: 384 });
30084
- await (0, import_promises15.writeFile)((0, import_node_path51.join)(this.dir, "ama-cwd"), `${this.cwd}
30385
+ await (0, import_promises16.mkdir)(hooks, { recursive: true, mode: 448 });
30386
+ await (0, import_promises16.writeFile)((0, import_node_path51.join)(this.dir, "ama-empty"), "", { flag: "a", mode: 384 });
30387
+ await (0, import_promises16.writeFile)((0, import_node_path51.join)(this.dir, "ama-cwd"), `${this.cwd}
30085
30388
  `, { mode: 384 });
30086
30389
  const version = await readSmall2((0, import_node_path51.join)(this.dir, "ama-config-version"));
30087
30390
  if (version !== CONFIG_VERSION) {
@@ -30112,9 +30415,9 @@ var init_shadow_git = __esm({
30112
30415
  ["user.useConfigOnly", "true"]
30113
30416
  ];
30114
30417
  for (const [key, value] of config) await this.run(["config", key, value]);
30115
- await (0, import_promises15.mkdir)((0, import_node_path51.join)(this.dir, "info"), { recursive: true });
30116
- await (0, import_promises15.writeFile)((0, import_node_path51.join)(this.dir, "info", "attributes"), ATTRIBUTES);
30117
- await (0, import_promises15.writeFile)((0, import_node_path51.join)(this.dir, "ama-config-version"), CONFIG_VERSION);
30418
+ await (0, import_promises16.mkdir)((0, import_node_path51.join)(this.dir, "info"), { recursive: true });
30419
+ await (0, import_promises16.writeFile)((0, import_node_path51.join)(this.dir, "info", "attributes"), ATTRIBUTES);
30420
+ await (0, import_promises16.writeFile)((0, import_node_path51.join)(this.dir, "ama-config-version"), CONFIG_VERSION);
30118
30421
  }
30119
30422
  await removeStaleIndexes(this.dir);
30120
30423
  }
@@ -30133,8 +30436,8 @@ var init_shadow_git = __esm({
30133
30436
  } catch (error2) {
30134
30437
  if (error2 instanceof GitMissingError) throw error2;
30135
30438
  }
30136
- await (0, import_promises15.mkdir)((0, import_node_path51.join)(this.dir, "info"), { recursive: true });
30137
- await (0, import_promises15.writeFile)((0, import_node_path51.join)(this.dir, "info", "exclude"), `${lines2.join("\n")}
30439
+ await (0, import_promises16.mkdir)((0, import_node_path51.join)(this.dir, "info"), { recursive: true });
30440
+ await (0, import_promises16.writeFile)((0, import_node_path51.join)(this.dir, "info", "exclude"), `${lines2.join("\n")}
30138
30441
  `);
30139
30442
  }
30140
30443
  /** 把工作区收进索引并写树,返回树 id(调用前已 writeExcludes)。 */
@@ -30291,7 +30594,7 @@ async function restoreFromShadow(options) {
30291
30594
  changes.filter((c) => isRegularMode(c.oldMode)).map((c) => c.oldOid),
30292
30595
  maxBytes
30293
30596
  );
30294
- const realCwd = await (0, import_promises16.realpath)(options.cwd);
30597
+ const realCwd = await (0, import_promises17.realpath)(options.cwd);
30295
30598
  const latest = latestCheckpoint(options.state);
30296
30599
  const result = emptyCodeResult();
30297
30600
  const touched = [];
@@ -30342,7 +30645,7 @@ function isRegularMode(mode) {
30342
30645
  async function targetRecord(change, abs, realCwd, blobs) {
30343
30646
  const slash = change.path.lastIndexOf("/");
30344
30647
  const realParentDir = slash < 0 ? realCwd : (0, import_node_path52.join)(realCwd, ...change.path.slice(0, slash).split("/"));
30345
- const info2 = await (0, import_promises16.lstat)(abs).catch(() => void 0);
30648
+ const info2 = await (0, import_promises17.lstat)(abs).catch(() => void 0);
30346
30649
  if (change.newMode === MODE_ABSENT && info2 !== void 0) {
30347
30650
  return void 0;
30348
30651
  }
@@ -30375,11 +30678,11 @@ function merge(into, from) {
30375
30678
  into.insertions += from.insertions;
30376
30679
  into.deletions += from.deletions;
30377
30680
  }
30378
- var import_promises16, import_node_path52, IS_WINDOWS2;
30681
+ var import_promises17, import_node_path52, IS_WINDOWS2;
30379
30682
  var init_shadow_restore = __esm({
30380
30683
  "src/checkpoints/shadow-restore.ts"() {
30381
30684
  "use strict";
30382
- import_promises16 = require("node:fs/promises");
30685
+ import_promises17 = require("node:fs/promises");
30383
30686
  import_node_path52 = require("node:path");
30384
30687
  init_blobs();
30385
30688
  init_replay();
@@ -30394,11 +30697,11 @@ var init_shadow_restore = __esm({
30394
30697
  function describe2(error2) {
30395
30698
  return error2 instanceof Error ? error2.message : String(error2);
30396
30699
  }
30397
- var import_promises17, MTIME_GRANULARITY_MS, CheckpointTracker;
30700
+ var import_promises18, MTIME_GRANULARITY_MS, CheckpointTracker;
30398
30701
  var init_tracker = __esm({
30399
30702
  "src/checkpoints/tracker.ts"() {
30400
30703
  "use strict";
30401
- import_promises17 = require("node:fs/promises");
30704
+ import_promises18 = require("node:fs/promises");
30402
30705
  init_git_head();
30403
30706
  init_blobs();
30404
30707
  init_replay();
@@ -30510,7 +30813,7 @@ var init_tracker = __esm({
30510
30813
  const at = this.now();
30511
30814
  let info2;
30512
30815
  try {
30513
- info2 = await (0, import_promises17.lstat)(absolutePath);
30816
+ info2 = await (0, import_promises18.lstat)(absolutePath);
30514
30817
  } catch (error2) {
30515
30818
  if (!isNotFound(error2)) throw error2;
30516
30819
  if (prev !== void 0 && prev.record.blob === null && prev.record.skipped === void 0)
@@ -30541,7 +30844,7 @@ var init_tracker = __esm({
30541
30844
  return prev;
30542
30845
  return { record: { blob: null, ...base4, skipped: "too_large" }, at };
30543
30846
  }
30544
- const bytes = await (0, import_promises17.readFile)(absolutePath);
30847
+ const bytes = await (0, import_promises18.readFile)(absolutePath);
30545
30848
  const hash = hashBytes(bytes);
30546
30849
  if (p !== void 0 && p.blob === hash && p.mode === mode && p.realParentDir === realParentDir) {
30547
30850
  return { record: p, at };
@@ -30863,7 +31166,7 @@ function formatSkillInvocation(skill, body, args) {
30863
31166
 
30864
31167
  ${args}`;
30865
31168
  }
30866
- async function expandSkillCommand(text, skills, read2 = (p) => (0, import_promises18.readFile)(p, "utf8")) {
31169
+ async function expandSkillCommand(text, skills, read2 = (p) => (0, import_promises19.readFile)(p, "utf8")) {
30867
31170
  const cmd = parseSkillCommand(text);
30868
31171
  if (!cmd) return void 0;
30869
31172
  const skill = skills.find((s2) => s2.name === cmd.name);
@@ -30871,11 +31174,11 @@ async function expandSkillCommand(text, skills, read2 = (p) => (0, import_promis
30871
31174
  const body = skillBody(await read2(skill.location));
30872
31175
  return { kind: "expanded", text: formatSkillInvocation(skill, body, cmd.args), skill };
30873
31176
  }
30874
- var import_promises18;
31177
+ var import_promises19;
30875
31178
  var init_expand = __esm({
30876
31179
  "src/skills/expand.ts"() {
30877
31180
  "use strict";
30878
- import_promises18 = require("node:fs/promises");
31181
+ import_promises19 = require("node:fs/promises");
30879
31182
  init_frontmatter2();
30880
31183
  init_index_prompt();
30881
31184
  }
@@ -30901,8 +31204,8 @@ async function discoverPromptTemplates(sources, options) {
30901
31204
  for (const source of sources) {
30902
31205
  let names;
30903
31206
  try {
30904
- if (!(await (0, import_promises19.stat)(source.dir)).isDirectory()) continue;
30905
- names = await (0, import_promises19.readdir)(source.dir);
31207
+ if (!(await (0, import_promises20.stat)(source.dir)).isDirectory()) continue;
31208
+ names = await (0, import_promises20.readdir)(source.dir);
30906
31209
  } catch {
30907
31210
  continue;
30908
31211
  }
@@ -30924,8 +31227,8 @@ async function discoverPromptTemplates(sources, options) {
30924
31227
  }
30925
31228
  let text;
30926
31229
  try {
30927
- if (!(await (0, import_promises19.stat)(path)).isFile()) continue;
30928
- text = await (0, import_promises19.readFile)(path, "utf8");
31230
+ if (!(await (0, import_promises20.stat)(path)).isFile()) continue;
31231
+ text = await (0, import_promises20.readFile)(path, "utf8");
30929
31232
  } catch {
30930
31233
  continue;
30931
31234
  }
@@ -31012,7 +31315,7 @@ function parsePromptCommand(text) {
31012
31315
  const rawArgs = (m[2] ?? "").trim();
31013
31316
  return { name: m[1], args: parseCommandArgs(rawArgs), rawArgs };
31014
31317
  }
31015
- async function expandPromptCommand(text, templates, read2 = (p) => (0, import_promises19.readFile)(p, "utf8")) {
31318
+ async function expandPromptCommand(text, templates, read2 = (p) => (0, import_promises20.readFile)(p, "utf8")) {
31016
31319
  const cmd = parsePromptCommand(text);
31017
31320
  if (!cmd) return void 0;
31018
31321
  const template2 = templates.find((t) => t.name === cmd.name);
@@ -31020,11 +31323,11 @@ async function expandPromptCommand(text, templates, read2 = (p) => (0, import_pr
31020
31323
  const body = parseFrontmatter(await read2(template2.path)).body.replace(/^\n+/, "");
31021
31324
  return { text: expandTemplate(body, cmd.args), template: template2 };
31022
31325
  }
31023
- var import_promises19, import_node_path53, TEMPLATE_NAME_RE, PLACEHOLDER;
31326
+ var import_promises20, import_node_path53, TEMPLATE_NAME_RE, PLACEHOLDER;
31024
31327
  var init_templates = __esm({
31025
31328
  "src/skills/templates.ts"() {
31026
31329
  "use strict";
31027
- import_promises19 = require("node:fs/promises");
31330
+ import_promises20 = require("node:fs/promises");
31028
31331
  import_node_path53 = require("node:path");
31029
31332
  init_frontmatter2();
31030
31333
  TEMPLATE_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/i;
@@ -31256,8 +31559,8 @@ function candidates(items) {
31256
31559
  }
31257
31560
  return out;
31258
31561
  }
31259
- function budgetReplacement(content, placeholder = IMAGE_OMITTED_FOR_BUDGET) {
31260
- return content.map((block) => block.type === "text" ? block.text : placeholder).filter((text) => text !== "").join("\n");
31562
+ function budgetReplacement(content, placeholder2 = IMAGE_OMITTED_FOR_BUDGET) {
31563
+ return content.map((block) => block.type === "text" ? block.text : placeholder2).filter((text) => text !== "").join("\n");
31261
31564
  }
31262
31565
  function base64ImageSize(block) {
31263
31566
  const cached = sizeCache.get(block);
@@ -31272,10 +31575,10 @@ function planImageBudget(items, budgetBytes, options = {}) {
31272
31575
  const all = candidates(items);
31273
31576
  const plan2 = [];
31274
31577
  const drop = (c, cause) => {
31275
- const placeholder = cause === "per_image" ? IMAGE_OMITTED_TOO_LARGE : IMAGE_OMITTED_FOR_BUDGET;
31578
+ const placeholder2 = cause === "per_image" ? IMAGE_OMITTED_TOO_LARGE : IMAGE_OMITTED_FOR_BUDGET;
31276
31579
  plan2.push({
31277
31580
  targetId: c.targetId,
31278
- replacement: budgetReplacement(c.content, placeholder),
31581
+ replacement: budgetReplacement(c.content, placeholder2),
31279
31582
  cause,
31280
31583
  images: c.images.length,
31281
31584
  bytes: c.bytes
@@ -31809,7 +32112,7 @@ var init_done_markers = __esm({
31809
32112
  function extractProposedPlan(text) {
31810
32113
  const lines2 = text.split(/\r?\n/);
31811
32114
  let fence2;
31812
- let open2;
32115
+ let open3;
31813
32116
  let last;
31814
32117
  for (const [i, line2] of lines2.entries()) {
31815
32118
  const marker = FENCE.exec(line2)?.[1];
@@ -31820,10 +32123,10 @@ function extractProposedPlan(text) {
31820
32123
  }
31821
32124
  if (fence2 !== void 0) continue;
31822
32125
  const trimmed = line2.trim();
31823
- if (trimmed === PLAN_OPEN_TAG) open2 = i;
31824
- else if (trimmed === PLAN_CLOSE_TAG && open2 !== void 0) {
31825
- last = { from: open2 + 1, to: i };
31826
- open2 = void 0;
32126
+ if (trimmed === PLAN_OPEN_TAG) open3 = i;
32127
+ else if (trimmed === PLAN_CLOSE_TAG && open3 !== void 0) {
32128
+ last = { from: open3 + 1, to: i };
32129
+ open3 = void 0;
31827
32130
  }
31828
32131
  }
31829
32132
  if (last === void 0) return void 0;
@@ -37196,7 +37499,7 @@ function permissionModes(currentModeId) {
37196
37499
  }))
37197
37500
  };
37198
37501
  }
37199
- function contentOf(content) {
37502
+ function contentOf2(content) {
37200
37503
  if (typeof content === "string") return content === "" ? [] : [{ type: "text", text: content }];
37201
37504
  const out = [];
37202
37505
  for (const block of content) {
@@ -37418,7 +37721,7 @@ var init_acp_events = __esm({
37418
37721
  for (const message of messages) {
37419
37722
  if (!("role" in message)) continue;
37420
37723
  if (message.role === "user") {
37421
- for (const block of contentOf(message.content))
37724
+ for (const block of contentOf2(message.content))
37422
37725
  this.emit({ sessionUpdate: "user_message_chunk", content: block });
37423
37726
  } else if (message.role === "assistant") {
37424
37727
  for (const block of message.content) {
@@ -37825,10 +38128,11 @@ function pageSessions(items, cursor, pageSize = LIST_PAGE_SIZE) {
37825
38128
  const last = all[start + pageSize - 1];
37826
38129
  return start + pageSize < all.length && last !== void 0 ? { sessions, nextCursor: encodeCursor(last.updatedAt, last.sessionId) } : { sessions };
37827
38130
  }
37828
- async function promptWhenIdle(session, job) {
38131
+ async function promptWhenIdle(session, job, onStart) {
37829
38132
  for (; ; ) {
37830
38133
  await session.waitForIdle();
37831
38134
  if (job.cancelRequested) return;
38135
+ onStart?.();
37832
38136
  try {
37833
38137
  await session.prompt(job.text, job.images.length > 0 ? { images: job.images } : {});
37834
38138
  return;
@@ -37837,10 +38141,36 @@ async function promptWhenIdle(session, job) {
37837
38141
  }
37838
38142
  }
37839
38143
  }
37840
- var LIST_PAGE_SIZE, TITLE_LIMIT, ZERO_USAGE2, PromptQueue;
38144
+ async function idleNow(session) {
38145
+ return await Promise.race([session.waitForIdle(), Promise.resolve(BUSY)]) !== BUSY;
38146
+ }
38147
+ async function quiesce(session, control, rounds = 50) {
38148
+ for (const task of control?.list() ?? [])
38149
+ if (task.status === "running") await control?.stop(task.taskId).catch(() => void 0);
38150
+ for (let i = 0; i < rounds; i++) {
38151
+ await session.abort();
38152
+ await new Promise((resolve41) => setImmediate(resolve41));
38153
+ if (await idleNow(session)) return true;
38154
+ }
38155
+ return false;
38156
+ }
38157
+ async function disposeQuiet(runtime, session, fallback, control, retries = 3) {
38158
+ for (let i = 0; ; i++) {
38159
+ try {
38160
+ await disposeSessionAlongside(runtime, session, fallback);
38161
+ break;
38162
+ } catch (error2) {
38163
+ if (i >= retries || !(error2 instanceof AmaError && error2.code === "busy")) throw error2;
38164
+ await quiesce(session, control);
38165
+ }
38166
+ }
38167
+ await session.abort();
38168
+ }
38169
+ var LIST_PAGE_SIZE, TITLE_LIMIT, ZERO_USAGE2, PromptQueue, BUSY;
37841
38170
  var init_acp_sessions = __esm({
37842
38171
  "src/modes/acp/acp-sessions.ts"() {
37843
38172
  "use strict";
38173
+ init_compose_session();
37844
38174
  init_types7();
37845
38175
  init_jsonrpc();
37846
38176
  init_errors();
@@ -37908,6 +38238,7 @@ var init_acp_sessions = __esm({
37908
38238
  });
37909
38239
  }
37910
38240
  };
38241
+ BUSY = /* @__PURE__ */ Symbol("busy");
37911
38242
  }
37912
38243
  });
37913
38244
 
@@ -37955,6 +38286,7 @@ var init_acp_server = __esm({
37955
38286
  init_compose_session();
37956
38287
  init_compose_store();
37957
38288
  init_session2();
38289
+ init_task_control();
37958
38290
  init_jsonrpc();
37959
38291
  init_types7();
37960
38292
  init_modes();
@@ -38052,13 +38384,14 @@ var init_acp_server = __esm({
38052
38384
  if (abort)
38053
38385
  await this.session().abort().catch(() => void 0);
38054
38386
  await this.queue.settled();
38055
- for (const session of this.sessions()) await session.waitForIdle().catch(() => void 0);
38387
+ for (const s2 of this.sessions())
38388
+ if (s2 instanceof AgentSessionImpl) await quiesce(s2, taskControl(s2.state.sessionId));
38056
38389
  const foreground = this.session();
38057
38390
  for (const entry of this.pool.values()) entry.unsubscribe();
38058
38391
  for (const session of this.sessions()) {
38059
38392
  if (session === foreground || !(session instanceof AgentSessionImpl)) continue;
38060
38393
  if (!(foreground instanceof AgentSessionImpl)) continue;
38061
- await disposeSessionAlongside(this.runtime, session, foreground).catch(() => void 0);
38394
+ await disposeQuiet(this.runtime, session, foreground).catch(() => void 0);
38062
38395
  }
38063
38396
  this.pool.clear();
38064
38397
  this.standby = void 0;
@@ -38194,12 +38527,9 @@ var init_acp_server = __esm({
38194
38527
  if (entry === void 0) return {};
38195
38528
  this.pool.delete(id);
38196
38529
  this.queue.cancelQueued(id);
38197
- const running = this.queue.running;
38198
- if (running?.sessionId === id) {
38199
- running.cancelRequested = true;
38200
- await entry.session.abort().catch(() => void 0);
38201
- await this.queue.settled(id);
38202
- }
38530
+ if (this.queue.running?.sessionId === id) this.queue.running.cancelRequested = true;
38531
+ await quiesce(entry.session, taskControl(id));
38532
+ await this.queue.settled(id);
38203
38533
  entry.unsubscribe();
38204
38534
  let fallback = this.session();
38205
38535
  if (fallback === entry.session) {
@@ -38211,7 +38541,7 @@ var init_acp_server = __esm({
38211
38541
  }
38212
38542
  }
38213
38543
  if (fallback instanceof AgentSessionImpl)
38214
- await disposeSessionAlongside(this.runtime, entry.session, fallback);
38544
+ await disposeQuiet(this.runtime, entry.session, fallback, taskControl(id));
38215
38545
  return {};
38216
38546
  }
38217
38547
  async prompt(params, signal) {
@@ -38250,15 +38580,15 @@ var init_acp_server = __esm({
38250
38580
  const { session } = entry;
38251
38581
  setForegroundSession(this.runtime, session);
38252
38582
  if (this.runtime.permission.mode !== entry.mode) session.setPermissionMode(entry.mode);
38253
- const before2 = session.getStats().tokens;
38583
+ let before2 = session.getStats().tokens;
38254
38584
  let failure2;
38255
38585
  try {
38256
- await promptWhenIdle(session, job);
38586
+ await promptWhenIdle(session, job, () => void (before2 = session.getStats().tokens));
38257
38587
  } catch (error2) {
38258
38588
  failure2 = error2;
38259
38589
  }
38260
- await this.peer.flush();
38261
38590
  const after = session.getStats().tokens;
38591
+ await this.peer.flush();
38262
38592
  const usage = {
38263
38593
  inputTokens: after.input - before2.input,
38264
38594
  outputTokens: after.output - before2.output,
@@ -38668,7 +38998,7 @@ async function bootstrap(argsIn, deps, io) {
38668
38998
  request = { kind: "resume", id };
38669
38999
  }
38670
39000
  const sessionRequest = request;
38671
- const sessionManager = await step(
39001
+ let sessionManager = await step(
38672
39002
  ExitCode.Session,
38673
39003
  m().steps.session,
38674
39004
  () => deps.sessions.open(sessionRequest, { sessionDir: paths.sessionDir, cwd: paths.cwd })
@@ -38893,6 +39223,8 @@ async function bootstrap(argsIn, deps, io) {
38893
39223
  uiBroker: () => uiBroker,
38894
39224
  onSessionReplaced: (next) => {
38895
39225
  session = next;
39226
+ sessionManager = next.manager ?? sessionManager;
39227
+ assembly.sessionManager = sessionManager;
38896
39228
  },
38897
39229
  sessionStartContext: () => sessionStartContext,
38898
39230
  unattended,
@@ -38922,7 +39254,7 @@ async function bootstrap(argsIn, deps, io) {
38922
39254
  await events.emit("session_shutdown", {});
38923
39255
  await hooks.run("SessionEnd", { reason }).catch(() => void 0);
38924
39256
  await disposeHost(host, (e) => warn2(m().hostDisposeFailed(String(e))));
38925
- await (session ?? active).dispose();
39257
+ await session?.dispose();
38926
39258
  })();
38927
39259
  return disposed2;
38928
39260
  };
@@ -38955,8 +39287,13 @@ async function bootstrap(argsIn, deps, io) {
38955
39287
  providers,
38956
39288
  model,
38957
39289
  thinkingLevel,
38958
- sessionManager,
38959
- session: active,
39290
+ // [#165] 跟随当前会话(ACP 关掉启动会话后它可被回收)
39291
+ get sessionManager() {
39292
+ return sessionManager;
39293
+ },
39294
+ get session() {
39295
+ return session;
39296
+ },
38960
39297
  hooks,
38961
39298
  host,
38962
39299
  permission,
@@ -43445,18 +43782,18 @@ async function confirmContinue(options = {}) {
43445
43782
  });
43446
43783
  return index2 === 0;
43447
43784
  }
43448
- const rl = (0, import_promises20.createInterface)({ input: input2, output: options.output ?? process.stderr });
43785
+ const rl = (0, import_promises21.createInterface)({ input: input2, output: options.output ?? process.stderr });
43449
43786
  try {
43450
43787
  return /^y(es)?$/i.test((await rl.question(m.textQuestion(question))).trim());
43451
43788
  } finally {
43452
43789
  rl.close();
43453
43790
  }
43454
43791
  }
43455
- var import_promises20, HIDE_CURSOR2, SHOW_CURSOR2;
43792
+ var import_promises21, HIDE_CURSOR2, SHOW_CURSOR2;
43456
43793
  var init_choice_prompt = __esm({
43457
43794
  "src/cli/choice-prompt.ts"() {
43458
43795
  "use strict";
43459
- import_promises20 = require("node:readline/promises");
43796
+ import_promises21 = require("node:readline/promises");
43460
43797
  init_tui2();
43461
43798
  init_i18n();
43462
43799
  HIDE_CURSOR2 = "\x1B[?25l";
@@ -44312,7 +44649,7 @@ ${error2.message}`)?.[0].toLowerCase();
44312
44649
  }
44313
44650
  function stripInto(value, removed) {
44314
44651
  if (Array.isArray(value)) return value.map((item) => stripInto(item, removed));
44315
- if (typeof value !== "object" || value === null) return value;
44652
+ if (typeof value !== "object" || value === null || value instanceof LargeString) return value;
44316
44653
  const out = {};
44317
44654
  for (const [key, inner] of Object.entries(value)) {
44318
44655
  if (key === "cache_control") removed.count++;
@@ -44351,6 +44688,7 @@ var init_cache_params = __esm({
44351
44688
  "src/ai/apis/cache-params.ts"() {
44352
44689
  "use strict";
44353
44690
  init_http();
44691
+ init_json_body();
44354
44692
  init_i18n();
44355
44693
  DEFAULT_BASE_URLS = {
44356
44694
  "anthropic-messages": "https://api.anthropic.com",
@@ -44433,7 +44771,11 @@ function convertBlocks(content) {
44433
44771
  } else {
44434
44772
  blocks.push({
44435
44773
  type: "image",
44436
- source: { type: "base64", media_type: block.mimeType, data: block.data }
44774
+ source: {
44775
+ type: "base64",
44776
+ media_type: block.mimeType,
44777
+ data: largeString("", block, block.data)
44778
+ }
44437
44779
  });
44438
44780
  }
44439
44781
  }
@@ -44607,6 +44949,7 @@ var init_anthropic_request = __esm({
44607
44949
  "src/ai/apis/anthropic-request.ts"() {
44608
44950
  "use strict";
44609
44951
  init_context();
44952
+ init_json_body();
44610
44953
  init_thinking();
44611
44954
  init_anthropic_compat();
44612
44955
  init_cache_params();
@@ -44656,7 +44999,7 @@ function validSignature(signature2) {
44656
44999
  return signature2 !== void 0 && signature2.length > 0 && signature2.length % 4 === 0 && BASE64.test(signature2);
44657
45000
  }
44658
45001
  function inlineData(block) {
44659
- return { inlineData: { mimeType: block.mimeType, data: block.data } };
45002
+ return { inlineData: { mimeType: block.mimeType, data: largeString("", block, block.data) } };
44660
45003
  }
44661
45004
  function convertUser2(message) {
44662
45005
  if (typeof message.content === "string") {
@@ -44827,6 +45170,7 @@ var init_google_request = __esm({
44827
45170
  "src/ai/apis/google-request.ts"() {
44828
45171
  "use strict";
44829
45172
  init_context();
45173
+ init_json_body();
44830
45174
  init_thinking();
44831
45175
  GOOGLE_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
44832
45176
  GOOGLE_COMPAT_KEYS = [
@@ -45274,7 +45618,8 @@ var init_chatgpt_backend = __esm({
45274
45618
 
45275
45619
  // src/ai/apis/openai-request.ts
45276
45620
  function imagePart(block) {
45277
- return { type: "image_url", image_url: { url: `data:${block.mimeType};base64,${block.data}` } };
45621
+ const url = largeString(`data:${block.mimeType};base64,`, block, block.data);
45622
+ return { type: "image_url", image_url: { url } };
45278
45623
  }
45279
45624
  function convertUser3(message) {
45280
45625
  if (typeof message.content === "string") {
@@ -45325,17 +45670,17 @@ function convertToolResults(model, results, compat) {
45325
45670
  for (const result of results) {
45326
45671
  const text = contentText2(result.content);
45327
45672
  const blocks = typeof result.content === "string" ? [] : result.content;
45328
- const hasImages = blocks.some((b) => b.type === "image");
45673
+ const hasImages2 = blocks.some((b) => b.type === "image");
45329
45674
  const message = {
45330
45675
  role: "tool",
45331
45676
  tool_call_id: normalizeCallId(model, result.toolCallId),
45332
45677
  content: sanitizeText(
45333
- text.length > 0 ? text : hasImages ? "(see attached image)" : "(no output)"
45678
+ text.length > 0 ? text : hasImages2 ? "(see attached image)" : "(no output)"
45334
45679
  )
45335
45680
  };
45336
45681
  if (compat.requiresToolResultName) message["name"] = result.toolName;
45337
45682
  messages.push(message);
45338
- if (hasImages && model.input.includes("image")) {
45683
+ if (hasImages2 && model.input.includes("image")) {
45339
45684
  for (const block of blocks) if (block.type === "image") images.push(imagePart(block));
45340
45685
  }
45341
45686
  }
@@ -45527,6 +45872,7 @@ var init_openai_request = __esm({
45527
45872
  init_thinking();
45528
45873
  init_anthropic_request();
45529
45874
  init_cache_params();
45875
+ init_json_body();
45530
45876
  init_openai_compat();
45531
45877
  REASONING_FIELDS = ["reasoning_content", "reasoning", "reasoning_text"];
45532
45878
  ASSISTANT_BRIDGE_TEXT = "Tool results received.";
@@ -45581,7 +45927,7 @@ function imagePart2(block) {
45581
45927
  return {
45582
45928
  type: "input_image",
45583
45929
  detail: "auto",
45584
- image_url: `data:${block.mimeType};base64,${block.data}`
45930
+ image_url: largeString(`data:${block.mimeType};base64,`, block, block.data)
45585
45931
  };
45586
45932
  }
45587
45933
  function convertUser4(message) {
@@ -45726,6 +46072,7 @@ var init_openai_responses_request = __esm({
45726
46072
  "src/ai/apis/openai-responses-request.ts"() {
45727
46073
  "use strict";
45728
46074
  init_context();
46075
+ init_json_body();
45729
46076
  init_thinking();
45730
46077
  init_cache_params();
45731
46078
  init_chatgpt_backend();
@@ -47647,8 +47994,8 @@ var init_registry2 = __esm({
47647
47994
  const id = trimmed.slice(slash + 1);
47648
47995
  const model = provider.models.find((m) => m.id === id);
47649
47996
  if (model) return { ok: true, model, provider };
47650
- const open2 = this.relayed.has(provider.id) || this.openTables.has(provider.id);
47651
- const relayed = provider.models.length === 0 || open2;
47997
+ const open3 = this.relayed.has(provider.id) || this.openTables.has(provider.id);
47998
+ const relayed = provider.models.length === 0 || open3;
47652
47999
  if (synthesize && relayed && id.length > 0) {
47653
48000
  const known = provider.channels?.some((c) => c.name === channel) ? channel : void 0;
47654
48001
  const synthesized = this.synthesize(provider, id, known);
@@ -48253,7 +48600,7 @@ function attempts(platform, file, env) {
48253
48600
  }
48254
48601
  async function isPng(file) {
48255
48602
  try {
48256
- return sniffImageMime(await (0, import_promises21.readFile)(file)) === "image/png";
48603
+ return sniffImageMime(await (0, import_promises22.readFile)(file)) === "image/png";
48257
48604
  } catch {
48258
48605
  return false;
48259
48606
  }
@@ -48263,7 +48610,7 @@ async function readClipboardImage(dataDir, deps = {}) {
48263
48610
  const env = deps.env ?? process.env;
48264
48611
  const run7 = deps.run ?? runCommand;
48265
48612
  const dir = (0, import_node_path78.join)(dataDir, CLIPBOARD_DIR);
48266
- await (0, import_promises21.mkdir)(dir, { recursive: true });
48613
+ await (0, import_promises22.mkdir)(dir, { recursive: true });
48267
48614
  const stamp = new Date((deps.now ?? Date.now)()).toISOString().replace(/[:.]/g, "-");
48268
48615
  let file = (0, import_node_path78.join)(dir, `${stamp}.png`);
48269
48616
  for (let n = 1; (0, import_node_fs60.existsSync)(file); n++) file = (0, import_node_path78.join)(dir, `${stamp}-${n}.png`);
@@ -48276,19 +48623,19 @@ async function readClipboardImage(dataDir, deps = {}) {
48276
48623
  if (result.code !== 0) continue;
48277
48624
  if (attempt.via === "stdout") {
48278
48625
  if (sniffImageMime(result.stdout) !== "image/png") continue;
48279
- await (0, import_promises21.writeFile)(file, result.stdout);
48626
+ await (0, import_promises22.writeFile)(file, result.stdout);
48280
48627
  }
48281
48628
  if (await isPng(file)) return { ok: true, path: file, tool: attempt.tool };
48282
- await (0, import_promises21.rm)(file, { force: true });
48629
+ await (0, import_promises22.rm)(file, { force: true });
48283
48630
  }
48284
- await (0, import_promises21.rm)(file, { force: true });
48631
+ await (0, import_promises22.rm)(file, { force: true });
48285
48632
  return { ok: false, reason: anyTool ? "no_image" : "no_tool" };
48286
48633
  }
48287
48634
  async function gcClipboardImages(dataDir, options = {}) {
48288
48635
  const dir = (0, import_node_path78.join)(dataDir, CLIPBOARD_DIR);
48289
48636
  let names;
48290
48637
  try {
48291
- names = await (0, import_promises21.readdir)(dir);
48638
+ names = await (0, import_promises22.readdir)(dir);
48292
48639
  } catch {
48293
48640
  return [];
48294
48641
  }
@@ -48297,21 +48644,21 @@ async function gcClipboardImages(dataDir, options = {}) {
48297
48644
  for (const name of names) {
48298
48645
  const file = (0, import_node_path78.join)(dir, name);
48299
48646
  try {
48300
- const info2 = await (0, import_promises21.stat)(file);
48647
+ const info2 = await (0, import_promises22.stat)(file);
48301
48648
  if (!info2.isFile() || info2.mtimeMs >= cutoff) continue;
48302
- if (options.dryRun !== true) await (0, import_promises21.rm)(file, { force: true });
48649
+ if (options.dryRun !== true) await (0, import_promises22.rm)(file, { force: true });
48303
48650
  removed.push(file);
48304
48651
  } catch {
48305
48652
  }
48306
48653
  }
48307
48654
  return removed;
48308
48655
  }
48309
- var import_node_fs60, import_promises21, import_node_path78, CLIPBOARD_DIR, CLIPBOARD_MAX_AGE_DAYS, OSASCRIPT, POWERSHELL;
48656
+ var import_node_fs60, import_promises22, import_node_path78, CLIPBOARD_DIR, CLIPBOARD_MAX_AGE_DAYS, OSASCRIPT, POWERSHELL;
48310
48657
  var init_clipboard_image = __esm({
48311
48658
  "src/tools/clipboard-image.ts"() {
48312
48659
  "use strict";
48313
48660
  import_node_fs60 = require("node:fs");
48314
- import_promises21 = require("node:fs/promises");
48661
+ import_promises22 = require("node:fs/promises");
48315
48662
  import_node_path78 = require("node:path");
48316
48663
  init_image_file();
48317
48664
  init_image_resize();
@@ -50456,8 +50803,8 @@ function barText(row3, width2, theme) {
50456
50803
  const color = colors[rank3];
50457
50804
  body += color === void 0 || plain ? ch2 : theme.fg(color, ch2);
50458
50805
  }
50459
- const [open2, close] = plain ? ["[", "]"] : ["▕", "▏"];
50460
- return theme.fg("border", open2) + body + theme.fg("border", close);
50806
+ const [open3, close] = plain ? ["[", "]"] : ["▕", "▏"];
50807
+ return theme.fg("border", open3) + body + theme.fg("border", close);
50461
50808
  }
50462
50809
  function labelPart(row3, ctx) {
50463
50810
  const g = ctx.theme.glyphs;
@@ -51115,11 +51462,11 @@ ${skill.body}`;
51115
51462
  }
51116
51463
  async function ensureFile(path, text) {
51117
51464
  try {
51118
- if (await (0, import_promises22.readFile)(path, "utf8") === text) return;
51465
+ if (await (0, import_promises23.readFile)(path, "utf8") === text) return;
51119
51466
  } catch {
51120
51467
  }
51121
- await (0, import_promises22.mkdir)((0, import_node_path86.dirname)(path), { recursive: true });
51122
- await (0, import_promises22.writeFile)(path, text, "utf8");
51468
+ await (0, import_promises23.mkdir)((0, import_node_path86.dirname)(path), { recursive: true });
51469
+ await (0, import_promises23.writeFile)(path, text, "utf8");
51123
51470
  }
51124
51471
  async function withBuiltinSkills(skills, dataDir, builtins = BUILTIN_SKILLS) {
51125
51472
  const out = [...skills];
@@ -51148,11 +51495,11 @@ async function withBuiltinSkills(skills, dataDir, builtins = BUILTIN_SKILLS) {
51148
51495
  }
51149
51496
  return { skills: out, warnings };
51150
51497
  }
51151
- var import_promises22, import_node_path86, BUILTIN_SKILLS_DIR, AMA_DOCS_BODY, BUILTIN_SKILLS;
51498
+ var import_promises23, import_node_path86, BUILTIN_SKILLS_DIR, AMA_DOCS_BODY, BUILTIN_SKILLS;
51152
51499
  var init_builtin3 = __esm({
51153
51500
  "src/skills/builtin.ts"() {
51154
51501
  "use strict";
51155
- import_promises22 = require("node:fs/promises");
51502
+ import_promises23 = require("node:fs/promises");
51156
51503
  import_node_path86 = require("node:path");
51157
51504
  BUILTIN_SKILLS_DIR = "builtin";
51158
51505
  AMA_DOCS_BODY = `# ama quick reference
@@ -51263,7 +51610,7 @@ function parseSkill(text, location, scope) {
51263
51610
  }
51264
51611
  async function isDir3(path) {
51265
51612
  try {
51266
- return (await (0, import_promises23.stat)(path)).isDirectory();
51613
+ return (await (0, import_promises24.stat)(path)).isDirectory();
51267
51614
  } catch {
51268
51615
  return false;
51269
51616
  }
@@ -51274,7 +51621,7 @@ async function findSkillFiles(dir, depth = 0) {
51274
51621
  if ((0, import_node_fs65.existsSync)(own)) return [own];
51275
51622
  let names;
51276
51623
  try {
51277
- names = await (0, import_promises23.readdir)(dir);
51624
+ names = await (0, import_promises24.readdir)(dir);
51278
51625
  } catch {
51279
51626
  return [];
51280
51627
  }
@@ -51302,7 +51649,7 @@ async function discoverSkills(sources, options) {
51302
51649
  for (const file of await findSkillFiles(source.dir)) {
51303
51650
  let text;
51304
51651
  try {
51305
- text = await (0, import_promises23.readFile)(file, "utf8");
51652
+ text = await (0, import_promises24.readFile)(file, "utf8");
51306
51653
  } catch (err) {
51307
51654
  result.warnings.push(`${file}: ${err.message}`);
51308
51655
  continue;
@@ -51323,12 +51670,12 @@ async function discoverSkills(sources, options) {
51323
51670
  }
51324
51671
  return result;
51325
51672
  }
51326
- var import_node_fs65, import_promises23, import_node_path87, SKILL_FILE, SKILL_NAME_RE, MAX_SKILL_NAME, MAX_SKILL_DESCRIPTION, MAX_DEPTH3, SKIP_DIRS;
51673
+ var import_node_fs65, import_promises24, import_node_path87, SKILL_FILE, SKILL_NAME_RE, MAX_SKILL_NAME, MAX_SKILL_DESCRIPTION, MAX_DEPTH3, SKIP_DIRS;
51327
51674
  var init_discover2 = __esm({
51328
51675
  "src/skills/discover.ts"() {
51329
51676
  "use strict";
51330
51677
  import_node_fs65 = require("node:fs");
51331
- import_promises23 = require("node:fs/promises");
51678
+ import_promises24 = require("node:fs/promises");
51332
51679
  import_node_path87 = require("node:path");
51333
51680
  init_frontmatter2();
51334
51681
  SKILL_FILE = "SKILL.md";
@@ -60456,9 +60803,9 @@ function parseLeftRight(text) {
60456
60803
  async function hasUpstream(cwd, branch) {
60457
60804
  const gitDir = await findGitDir(cwd);
60458
60805
  if (gitDir === void 0) return false;
60459
- const common = (await (0, import_promises24.readFile)((0, import_node_path95.join)(gitDir, "commondir"), "utf8").catch(() => void 0))?.trim();
60806
+ const common = (await (0, import_promises25.readFile)((0, import_node_path95.join)(gitDir, "commondir"), "utf8").catch(() => void 0))?.trim();
60460
60807
  const dir = common === void 0 ? gitDir : (0, import_node_path95.isAbsolute)(common) ? common : (0, import_node_path95.resolve)(gitDir, common);
60461
- const text = await (0, import_promises24.readFile)((0, import_node_path95.join)(dir, "config"), "utf8").catch(() => "");
60808
+ const text = await (0, import_promises25.readFile)((0, import_node_path95.join)(dir, "config"), "utf8").catch(() => "");
60462
60809
  let inBranch = false;
60463
60810
  for (const raw of text.split("\n")) {
60464
60811
  const line2 = raw.trim();
@@ -60471,7 +60818,7 @@ async function hasUpstream(cwd, branch) {
60471
60818
  }
60472
60819
  return false;
60473
60820
  }
60474
- async function readHead3(cwd) {
60821
+ async function readHead2(cwd) {
60475
60822
  const head = await readGitHead(cwd);
60476
60823
  if (head !== void 0) {
60477
60824
  return {
@@ -60481,16 +60828,16 @@ async function readHead3(cwd) {
60481
60828
  }
60482
60829
  const gitDir = await findGitDir(cwd);
60483
60830
  if (gitDir === void 0) return void 0;
60484
- const text = await (0, import_promises24.readFile)((0, import_node_path95.join)(gitDir, "HEAD"), "utf8").catch(() => void 0);
60831
+ const text = await (0, import_promises25.readFile)((0, import_node_path95.join)(gitDir, "HEAD"), "utf8").catch(() => void 0);
60485
60832
  const match = /^ref:\s*refs\/heads\/(\S+)\s*$/.exec(text ?? "");
60486
60833
  return match?.[1] !== void 0 ? { branch: match[1] } : void 0;
60487
60834
  }
60488
- var import_node_child_process18, import_promises24, import_node_path95, GIT_NUMSTAT_INTERVAL_MS, GIT_NUMSTAT_TIMEOUT_MS, sameInfo, GitInfoWatcher;
60835
+ var import_node_child_process18, import_promises25, import_node_path95, GIT_NUMSTAT_INTERVAL_MS, GIT_NUMSTAT_TIMEOUT_MS, sameInfo, GitInfoWatcher;
60489
60836
  var init_info = __esm({
60490
60837
  "src/git/info.ts"() {
60491
60838
  "use strict";
60492
60839
  import_node_child_process18 = require("node:child_process");
60493
- import_promises24 = require("node:fs/promises");
60840
+ import_promises25 = require("node:fs/promises");
60494
60841
  import_node_path95 = require("node:path");
60495
60842
  init_git_head();
60496
60843
  GIT_NUMSTAT_INTERVAL_MS = 1e4;
@@ -60545,7 +60892,7 @@ var init_info = __esm({
60545
60892
  const run7 = async () => {
60546
60893
  do {
60547
60894
  this.headAgain = false;
60548
- const head = await readHead3(this.cwd).catch(() => void 0);
60895
+ const head = await readHead2(this.cwd).catch(() => void 0);
60549
60896
  this.update(head);
60550
60897
  } while (this.headAgain && !this.disposed);
60551
60898
  this.headReading = void 0;
@@ -62644,8 +62991,8 @@ var init_run_indicator = __esm({
62644
62991
  get busy() {
62645
62992
  return this.running || this.compacting;
62646
62993
  }
62647
- setApproval(open2) {
62648
- this.approval = open2;
62994
+ setApproval(open3) {
62995
+ this.approval = open3;
62649
62996
  this.sync();
62650
62997
  }
62651
62998
  /** 会话切换后清零。 */
@@ -69698,9 +70045,9 @@ async function runSessionsTrace(argv, io, deps = {}) {
69698
70045
  });
69699
70046
  }
69700
70047
  const outFlag = split.file ?? values.get("output");
69701
- const open2 = flags2.has("open");
70048
+ const open3 = flags2.has("open");
69702
70049
  let outPath = outFlag === void 0 ? void 0 : (0, import_node_path84.resolve)(io.cwd, expandHome(outFlag, { env: io.env }));
69703
- if (outPath === void 0 && open2)
70050
+ if (outPath === void 0 && open3)
69704
70051
  outPath = (0, import_node_path84.join)((0, import_node_os12.tmpdir)(), `ama-trace-${read2.header.id}.${wantsJson ? "json" : "html"}`);
69705
70052
  if (outPath === void 0) {
69706
70053
  io.stdout(text);
@@ -69708,7 +70055,7 @@ async function runSessionsTrace(argv, io, deps = {}) {
69708
70055
  }
69709
70056
  (0, import_node_fs64.writeFileSync)(outPath, text, { mode: 384 });
69710
70057
  io.stderr(m.wrote(outPath));
69711
- if (open2) {
70058
+ if (open3) {
69712
70059
  try {
69713
70060
  await (deps.open ?? spawnOpen)(openCommand(deps.platform ?? process.platform, outPath));
69714
70061
  } catch (error2) {