@armadra/agent 0.9.3 → 0.11.0

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/CHANGELOG.zh-CN.md +22 -0
  3. package/README.md +5 -0
  4. package/README.zh-CN.md +4 -0
  5. package/dist/bundle/ama.cjs +1606 -423
  6. package/dist/cli/compose.d.ts +3 -0
  7. package/dist/cli/compose.js +1 -0
  8. package/dist/drivers/agents.js +1 -1
  9. package/dist/drivers/host-runners.d.ts +26 -1
  10. package/dist/drivers/host-runners.js +147 -0
  11. package/dist/hooks/matcher.d.ts +2 -2
  12. package/dist/hooks/matcher.js +3 -3
  13. package/dist/host/api-impl.d.ts +3 -0
  14. package/dist/host/api-impl.js +7 -2
  15. package/dist/host/types.d.ts +37 -1
  16. package/dist/host/types.js +5 -0
  17. package/dist/host.d.ts +1 -1
  18. package/dist/host.js +1 -1
  19. package/dist/i18n/catalog.d.ts +5 -0
  20. package/dist/i18n/messages/approval.d.ts +5 -0
  21. package/dist/i18n/messages/approval.js +5 -0
  22. package/dist/i18n/messages/drivers.d.ts +2 -0
  23. package/dist/i18n/messages/drivers.js +2 -0
  24. package/dist/i18n/messages/interactive-view.d.ts +5 -0
  25. package/dist/i18n/messages/interactive-view.js +5 -0
  26. package/dist/i18n/messages/interactive.d.ts +4 -0
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +1 -1
  29. package/dist/modes/commands-core.d.ts +7 -0
  30. package/dist/modes/commands-core.js +23 -5
  31. package/dist/modes/interactive/approval-dialog.js +17 -4
  32. package/dist/modes/interactive/commands.d.ts +2 -0
  33. package/dist/modes/interactive/commands.js +5 -1
  34. package/dist/modes/interactive/interactive-mode.js +2 -2
  35. package/dist/modes/interactive/tool-summary.d.ts +2 -0
  36. package/dist/modes/interactive/tool-summary.js +27 -0
  37. package/dist/modes/rpc/commands.js +4 -4
  38. package/dist/permissions/classifier.js +1 -1
  39. package/dist/permissions/network-class.d.ts +28 -0
  40. package/dist/permissions/network-class.js +91 -0
  41. package/dist/permissions/pipeline.d.ts +1 -1
  42. package/dist/permissions/pipeline.js +24 -7
  43. package/dist/permissions/preview.js +3 -0
  44. package/dist/permissions/rules.d.ts +1 -0
  45. package/dist/permissions/rules.js +5 -1
  46. package/dist/tools/presets.d.ts +1 -1
  47. package/dist/tools/presets.js +2 -2
  48. package/dist/tools/registry.d.ts +3 -1
  49. package/dist/tools/registry.js +4 -2
  50. package/dist/tools/types.d.ts +4 -1
  51. package/dist/tools/web/fetch.d.ts +48 -0
  52. package/dist/tools/web/fetch.js +307 -0
  53. package/dist/tools/web/html-to-markdown.d.ts +31 -0
  54. package/dist/tools/web/html-to-markdown.js +522 -0
  55. package/dist/tools/web/url-guard.d.ts +59 -0
  56. package/dist/tools/web/url-guard.js +221 -0
  57. package/docs/en/guides/permissions.md +23 -1
  58. package/docs/en/reference/host-api.md +61 -19
  59. package/docs/en/reference/rpc.md +7 -7
  60. package/docs/guides/agents.md +14 -0
  61. package/docs/guides/hooks.md +86 -8
  62. package/docs/guides/permissions.md +23 -1
  63. package/docs/guides/sandbox.md +6 -0
  64. package/docs/reference/host-api.md +61 -19
  65. package/docs/reference/rpc.md +7 -7
  66. package/package.json +1 -1
@@ -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.9.3" : readPackageVersion();
44
+ AMA_VERSION = true ? "0.11.0" : readPackageVersion();
45
45
  }
46
46
  });
47
47
 
@@ -1794,6 +1794,9 @@ var init_interactive_view = __esm({
1794
1794
  codemode: (calls, lines2) => `${plural(calls, "inner call")} · script printed ${plural(lines2, "line")}`,
1795
1795
  done: "done",
1796
1796
  outputLines: (n) => `${plural(n, "line")} of output`,
1797
+ /** web_fetch:`<标题> · <大小> · markdown|text|json|binary`。 */
1798
+ fetched: (title, size, format) => [title, size, format].filter((p) => p !== void 0 && p !== "").join(" · "),
1799
+ fetchRedirect: (url) => `redirected to ${url} (not followed)`,
1797
1800
  awaiting: (glyph) => `${glyph} awaiting approval`,
1798
1801
  subagentRunning: (elapsed) => `subagent · running ${elapsed}`,
1799
1802
  running: (elapsed) => `running · ${elapsed}`,
@@ -1863,6 +1866,8 @@ var init_interactive_view = __esm({
1863
1866
  codemode: (calls, lines2) => `${calls} 个内层调用 · 脚本输出 ${lines2} 行`,
1864
1867
  done: "完成",
1865
1868
  outputLines: (n) => `${n} 行输出`,
1869
+ fetched: (title, size, format) => [title, size, format].filter((p) => p !== void 0 && p !== "").join(" · "),
1870
+ fetchRedirect: (url) => `重定向到 ${url}(未跟随)`,
1866
1871
  awaiting: (glyph) => `${glyph} 等待确认`,
1867
1872
  subagentRunning: (elapsed) => `子 Agent · 运行中 ${elapsed}`,
1868
1873
  running: (elapsed) => `运行中 · ${elapsed}`,
@@ -2199,6 +2204,8 @@ var init_approval = __esm({
2199
2204
  option: {
2200
2205
  allow: "Allow",
2201
2206
  allowSession: "Allow for this session",
2207
+ /** web_fetch:本会话记住的是主机。 */
2208
+ allowSessionHost: (host) => `Allow ${host} for this session`,
2202
2209
  deny: "Deny"
2203
2210
  },
2204
2211
  hint: (arrows) => `${arrows} select · Enter confirm · v full input`,
@@ -2207,6 +2214,7 @@ var init_approval = __esm({
2207
2214
  outcome: {
2208
2215
  allow: (what) => `Allowed ${what}`,
2209
2216
  allowSession: (what) => `Allowed ${what} (similar calls won't ask again this session)`,
2217
+ allowSessionHost: (what, host) => `Allowed ${what} (${host} won't ask again this session)`,
2210
2218
  deny: (what) => `Denied ${what}`,
2211
2219
  cancelled: (what) => `Approval cancelled (timeout or interrupt): ${what}`
2212
2220
  },
@@ -2244,6 +2252,7 @@ var init_approval = __esm({
2244
2252
  option: {
2245
2253
  allow: "允许",
2246
2254
  allowSession: "本会话允许同类",
2255
+ allowSessionHost: (host) => `本会话允许 ${host}`,
2247
2256
  deny: "拒绝"
2248
2257
  },
2249
2258
  hint: (arrows) => `${arrows} 选择 · Enter 确认 · v 完整输入`,
@@ -2251,6 +2260,7 @@ var init_approval = __esm({
2251
2260
  outcome: {
2252
2261
  allow: (what) => `已允许 ${what}`,
2253
2262
  allowSession: (what) => `已允许 ${what}(本会话同类不再询问)`,
2263
+ allowSessionHost: (what, host) => `已允许 ${what}(本会话 ${host} 不再询问)`,
2254
2264
  deny: (what) => `已拒绝 ${what}`,
2255
2265
  cancelled: (what) => `审批已取消(超时或中断):${what}`
2256
2266
  },
@@ -4281,6 +4291,7 @@ var init_drivers = __esm({
4281
4291
  brokerNeedsAsk: "approvals.setBroker: broker needs ask()",
4282
4292
  warmingNeedsFunction: "cache.onWarmingDecision: needs a function",
4283
4293
  runnerNeedsIdStart: "runners.provide: needs a runner with id and start",
4294
+ runnerDoneNeedsFunction: "runners.onDone: needs a function",
4284
4295
  notAdapter: (label2) => `${label2}: not a host adapter module (missing hostApi / create export)`,
4285
4296
  versionMismatch: (label2, found, needed) => `${label2}: host adapter hostApi=${found}, ama needs ${needed}`,
4286
4297
  noCreate: (label2) => `${label2}: missing create(api) function`,
@@ -4340,6 +4351,7 @@ var init_drivers = __esm({
4340
4351
  brokerNeedsAsk: "approvals.setBroker:broker 需要 ask()",
4341
4352
  warmingNeedsFunction: "cache.onWarmingDecision:需要函数",
4342
4353
  runnerNeedsIdStart: "runners.provide:需要带 id 与 start 的 runner",
4354
+ runnerDoneNeedsFunction: "runners.onDone:需要函数",
4343
4355
  notAdapter: (label2) => `${label2}: 不是宿主适配器模块(缺少 hostApi / create 导出)`,
4344
4356
  versionMismatch: (label2, found, needed) => `${label2}: 宿主适配器 hostApi=${found},ama 需要 ${needed}`,
4345
4357
  noCreate: (label2) => `${label2}: 缺少 create(api) 函数`,
@@ -6419,11 +6431,11 @@ function parseArgs(argv) {
6419
6431
  continue;
6420
6432
  }
6421
6433
  let name;
6422
- let inline;
6434
+ let inline2;
6423
6435
  if (token.startsWith("--")) {
6424
6436
  const eq = token.indexOf("=");
6425
6437
  name = eq === -1 ? token.slice(2) : token.slice(2, eq);
6426
- inline = eq === -1 ? void 0 : token.slice(eq + 1);
6438
+ inline2 = eq === -1 ? void 0 : token.slice(eq + 1);
6427
6439
  } else {
6428
6440
  const alias = FLAG_ALIASES[token];
6429
6441
  if (alias === void 0) throw new UsageError(msg().cli.args.unknownOption(token));
@@ -6432,9 +6444,9 @@ function parseArgs(argv) {
6432
6444
  if (name === "resume") {
6433
6445
  args.resume = true;
6434
6446
  const next = argv[i + 1];
6435
- if (inline !== void 0) {
6436
- if (inline === "") throw new UsageError(msg().cli.args.emptyResumeId);
6437
- args.resumeId = inline;
6447
+ if (inline2 !== void 0) {
6448
+ if (inline2 === "") throw new UsageError(msg().cli.args.emptyResumeId);
6449
+ args.resumeId = inline2;
6438
6450
  } else if (next !== void 0 && SESSION_ID_LIKE.test(next)) {
6439
6451
  args.resumeId = next;
6440
6452
  i++;
@@ -6442,7 +6454,7 @@ function parseArgs(argv) {
6442
6454
  continue;
6443
6455
  }
6444
6456
  if (VALUE_OPTIONS.has(name)) {
6445
- let value = inline;
6457
+ let value = inline2;
6446
6458
  if (value === void 0) {
6447
6459
  value = argv[i + 1];
6448
6460
  if (value === void 0) throw new UsageError(msg().cli.args.needsValue(name));
@@ -6451,7 +6463,7 @@ function parseArgs(argv) {
6451
6463
  applyValue(args, name, value);
6452
6464
  continue;
6453
6465
  }
6454
- if (inline !== void 0) throw new UsageError(msg().cli.args.noValue(name));
6466
+ if (inline2 !== void 0) throw new UsageError(msg().cli.args.noValue(name));
6455
6467
  if (!applyFlag(args, name)) throw new UsageError(msg().cli.args.unknownOption(token));
6456
6468
  }
6457
6469
  if (args.positionals.length > 0) args.prompt = args.positionals.join(" ");
@@ -8614,6 +8626,237 @@ var init_memory_class = __esm({
8614
8626
  }
8615
8627
  });
8616
8628
 
8629
+ // src/tools/web/url-guard.ts
8630
+ function ipv6Groups(address) {
8631
+ let text = address.replace(/^\[|\]$/g, "").replace(/%.*$/, "");
8632
+ const v4 = /(\d+\.\d+\.\d+\.\d+)$/.exec(text);
8633
+ if (v4 !== null) {
8634
+ const parts = v4[1].split(".").map(Number);
8635
+ if (parts.some((p) => p > 255)) return void 0;
8636
+ const [a, b, c, d] = parts;
8637
+ text = `${text.slice(0, v4.index)}${(a << 8 | b).toString(16)}:${(c << 8 | d).toString(16)}`;
8638
+ }
8639
+ const halves = text.split("::");
8640
+ if (halves.length > 2) return void 0;
8641
+ const parse3 = (part) => part === "" ? [] : part.split(":").map((g) => /^[0-9a-f]{1,4}$/i.test(g) ? parseInt(g, 16) : NaN);
8642
+ const head = parse3(halves[0]);
8643
+ const tail = halves.length === 2 ? parse3(halves[1]) : [];
8644
+ const fill = 8 - head.length - tail.length;
8645
+ if (halves.length === 1 ? fill !== 0 : fill < 1) return void 0;
8646
+ const groups = [...head, ...new Array(halves.length === 2 ? fill : 0).fill(0), ...tail];
8647
+ return groups.some((g) => Number.isNaN(g)) ? void 0 : groups;
8648
+ }
8649
+ function v4FromGroups(hi, lo) {
8650
+ return `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
8651
+ }
8652
+ function embeddedIPv4(address) {
8653
+ const g = ipv6Groups(address);
8654
+ if (g === void 0) return void 0;
8655
+ const zeros = (from, to) => g.slice(from, to).every((x) => x === 0);
8656
+ if (zeros(0, 5) && (g[5] === 65535 || g[5] === 0 && g[6] >= 256))
8657
+ return v4FromGroups(g[6], g[7]);
8658
+ if (g[0] === 100 && g[1] === 65435 && zeros(2, 6))
8659
+ return v4FromGroups(g[6], g[7]);
8660
+ if (g[0] === 8194) return v4FromGroups(g[1], g[2]);
8661
+ return void 0;
8662
+ }
8663
+ function blockedAddressReason(address) {
8664
+ const bare = address.replace(/^\[|\]$/g, "").replace(/%.*$/, "");
8665
+ const family = (0, import_node_net.isIP)(bare);
8666
+ if (family === 0) return `${address} is not an IP address`;
8667
+ const lower = bare.toLowerCase();
8668
+ if (METADATA.has(lower)) return `${bare} is a cloud metadata address`;
8669
+ if (family === 4) {
8670
+ if (bare === "255.255.255.255" || blocked.check(bare, "ipv4"))
8671
+ return `${bare} is a private, loopback, link-local or reserved address`;
8672
+ return void 0;
8673
+ }
8674
+ const inner = embeddedIPv4(lower);
8675
+ if (inner !== void 0) {
8676
+ const reason = blockedAddressReason(inner);
8677
+ if (reason !== void 0) return `${bare} embeds ${inner}: ${reason}`;
8678
+ }
8679
+ if (blocked.check(lower, "ipv6"))
8680
+ return `${bare} is a private, loopback, link-local or multicast address`;
8681
+ return void 0;
8682
+ }
8683
+ function bareHostname(url) {
8684
+ return url.hostname.replace(/^\[|\]$/g, "").replace(/\.$/, "").toLowerCase();
8685
+ }
8686
+ function checkFetchUrl(raw) {
8687
+ if (typeof raw !== "string" || raw.trim() === "") return { ok: false, reason: "url is empty" };
8688
+ const text = raw.trim();
8689
+ if (text.length > MAX_URL_LENGTH)
8690
+ return { ok: false, reason: `URL is longer than ${MAX_URL_LENGTH} characters` };
8691
+ let url;
8692
+ try {
8693
+ url = new URL(text);
8694
+ } catch {
8695
+ return { ok: false, reason: "not a valid absolute URL" };
8696
+ }
8697
+ if (url.protocol !== "http:" && url.protocol !== "https:")
8698
+ return { ok: false, reason: `only http and https URLs are supported (got ${url.protocol})` };
8699
+ if (url.username !== "" || url.password !== "")
8700
+ return { ok: false, reason: "URLs with credentials (user:pass@) are not allowed" };
8701
+ if (url.search.length > MAX_QUERY_LENGTH + 1)
8702
+ return { ok: false, reason: `query string is longer than ${MAX_QUERY_LENGTH} characters` };
8703
+ const hostname = bareHostname(url);
8704
+ if ((0, import_node_net.isIP)(hostname) !== 0) {
8705
+ const reason = blockedAddressReason(hostname);
8706
+ return reason === void 0 ? { ok: true, url, hostname } : { ok: false, reason };
8707
+ }
8708
+ if (!hostname.includes(".") || hostname === "localhost" || hostname.endsWith(".localhost"))
8709
+ return {
8710
+ ok: false,
8711
+ reason: `${hostname} is a local host name; web_fetch only reaches public hosts`
8712
+ };
8713
+ return { ok: true, url, hostname };
8714
+ }
8715
+ async function resolvePublicAddresses(hostname, lookup = systemLookup) {
8716
+ const family = (0, import_node_net.isIP)(hostname);
8717
+ if (family !== 0) {
8718
+ const reason = blockedAddressReason(hostname);
8719
+ return reason === void 0 ? { ok: true, addresses: [{ address: hostname, family }] } : { ok: false, reason };
8720
+ }
8721
+ let addresses;
8722
+ try {
8723
+ addresses = await lookup(hostname);
8724
+ } catch (err) {
8725
+ const code = err.code;
8726
+ return { ok: false, reason: `cannot resolve ${hostname}${code ? ` (${code})` : ""}` };
8727
+ }
8728
+ if (addresses.length === 0) return { ok: false, reason: `cannot resolve ${hostname}` };
8729
+ for (const { address } of addresses) {
8730
+ const reason = blockedAddressReason(address);
8731
+ if (reason !== void 0) return { ok: false, reason: `${hostname} resolves to ${reason}` };
8732
+ }
8733
+ return { ok: true, addresses };
8734
+ }
8735
+ function pinnedLookup(addresses) {
8736
+ return (_hostname, options, callback) => {
8737
+ const cb = typeof options === "function" ? options : callback;
8738
+ const all = typeof options === "object" && options !== null && options.all;
8739
+ const first = addresses[0];
8740
+ if (all === true) cb(null, [...addresses]);
8741
+ else cb(null, first.address, first.family);
8742
+ };
8743
+ }
8744
+ function redirectPolicy(from, to) {
8745
+ if (bareHostname(from) !== bareHostname(to)) return "cross-host";
8746
+ if (from.protocol === "http:" && to.protocol === "https:") return "follow";
8747
+ return from.protocol === to.protocol && portOf(from) === portOf(to) ? "follow" : "cross-host";
8748
+ }
8749
+ function portOf(url) {
8750
+ return url.port !== "" ? url.port : url.protocol === "https:" ? "443" : "80";
8751
+ }
8752
+ var import_node_dns, import_node_net, MAX_URL_LENGTH, MAX_QUERY_LENGTH, MAX_SAME_HOST_REDIRECTS, V4_BLOCKED, V6_BLOCKED, METADATA, blocked, systemLookup;
8753
+ var init_url_guard = __esm({
8754
+ "src/tools/web/url-guard.ts"() {
8755
+ "use strict";
8756
+ import_node_dns = require("node:dns");
8757
+ import_node_net = require("node:net");
8758
+ MAX_URL_LENGTH = 2048;
8759
+ MAX_QUERY_LENGTH = 1024;
8760
+ MAX_SAME_HOST_REDIRECTS = 5;
8761
+ V4_BLOCKED = [
8762
+ ["0.0.0.0", 8],
8763
+ ["10.0.0.0", 8],
8764
+ ["100.64.0.0", 10],
8765
+ ["127.0.0.0", 8],
8766
+ ["169.254.0.0", 16],
8767
+ ["172.16.0.0", 12],
8768
+ ["192.0.0.0", 24],
8769
+ ["192.0.2.0", 24],
8770
+ ["192.168.0.0", 16],
8771
+ ["198.18.0.0", 15],
8772
+ ["198.51.100.0", 24],
8773
+ ["203.0.113.0", 24],
8774
+ ["224.0.0.0", 4],
8775
+ ["240.0.0.0", 4]
8776
+ ];
8777
+ V6_BLOCKED = [
8778
+ ["::", 96],
8779
+ ["::1", 128],
8780
+ ["100::", 64],
8781
+ ["2001:db8::", 32],
8782
+ ["fc00::", 7],
8783
+ ["fe80::", 10],
8784
+ ["fec0::", 10],
8785
+ ["ff00::", 8]
8786
+ ];
8787
+ METADATA = /* @__PURE__ */ new Set(["169.254.169.254", "100.100.100.200", "fd00:ec2::254"]);
8788
+ blocked = new import_node_net.BlockList();
8789
+ for (const [net, prefix] of V4_BLOCKED) blocked.addSubnet(net, prefix, "ipv4");
8790
+ for (const [net, prefix] of V6_BLOCKED) blocked.addSubnet(net, prefix, "ipv6");
8791
+ systemLookup = (hostname) => new Promise((resolve41, reject2) => {
8792
+ (0, import_node_dns.lookup)(hostname, { all: true, verbatim: true }, (err, addresses) => {
8793
+ if (err) reject2(err);
8794
+ else resolve41(addresses);
8795
+ });
8796
+ });
8797
+ }
8798
+ });
8799
+
8800
+ // src/permissions/network-class.ts
8801
+ function networkEffectivePermission() {
8802
+ return "execute";
8803
+ }
8804
+ function inputUrl(input2) {
8805
+ const raw = typeof input2 === "object" && input2 !== null ? input2["url"] : void 0;
8806
+ if (typeof raw !== "string") return void 0;
8807
+ try {
8808
+ const url = new URL(raw.trim());
8809
+ return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
8810
+ } catch {
8811
+ return void 0;
8812
+ }
8813
+ }
8814
+ function urlSubject(url) {
8815
+ return `${bareHostname(url)}${url.pathname === "" ? "/" : url.pathname}`;
8816
+ }
8817
+ function globRegExp(pattern) {
8818
+ let body = "";
8819
+ for (let i = 0; i < pattern.length; i++) {
8820
+ const ch2 = pattern[i];
8821
+ if (ch2 === "*" && pattern[i + 1] === "*") {
8822
+ body += ".*";
8823
+ i++;
8824
+ } else if (ch2 === "*") body += "[^/]*";
8825
+ else if (ch2 === "?") body += "[^/]";
8826
+ else body += ch2.replace(/[.+^${}()|[\]\\]/g, "\\$&");
8827
+ }
8828
+ return new RegExp(`^${body}$`, "i");
8829
+ }
8830
+ function networkRuleMatches(pattern, input2) {
8831
+ const url = inputUrl(input2);
8832
+ if (url === void 0) return false;
8833
+ const p = pattern.trim().replace(/^https?:\/\//i, "").toLowerCase();
8834
+ if (p === "*" || p === "**") return true;
8835
+ const host = bareHostname(url);
8836
+ if (!p.includes("/")) {
8837
+ if (p.includes("*") || p.includes("?")) return globRegExp(p).test(host);
8838
+ const bare = p.replace(/\.$/, "");
8839
+ return host === bare || host.endsWith(`.${bare}`);
8840
+ }
8841
+ return globRegExp(p).test(urlSubject(url));
8842
+ }
8843
+ function networkGrantHost(input2) {
8844
+ const url = inputUrl(input2);
8845
+ return url === void 0 ? void 0 : bareHostname(url);
8846
+ }
8847
+ function networkBlockReason(input2) {
8848
+ const raw = typeof input2 === "object" && input2 !== null ? input2["url"] : void 0;
8849
+ if (typeof raw !== "string") return "url is missing";
8850
+ const checked = checkFetchUrl(raw);
8851
+ return checked.ok ? void 0 : checked.reason;
8852
+ }
8853
+ var init_network_class = __esm({
8854
+ "src/permissions/network-class.ts"() {
8855
+ "use strict";
8856
+ init_url_guard();
8857
+ }
8858
+ });
8859
+
8617
8860
  // src/permissions/rules.ts
8618
8861
  function parseRule(raw, effect, source) {
8619
8862
  const text = raw.trim();
@@ -8712,7 +8955,8 @@ function matchesSubject(rule, toolName, input2, cwd, command3) {
8712
8955
  if (toolName === "memory") return memoryRuleMatches(rule.pattern, input2);
8713
8956
  if (command3 !== void 0) return wildcardToRegExp(rule.pattern).test(command3);
8714
8957
  const abs = inputPath(toolName, input2, cwd);
8715
- return abs !== void 0 && pathMatches(rule.pattern, abs, cwd);
8958
+ if (abs !== void 0) return pathMatches(rule.pattern, abs, cwd);
8959
+ return networkRuleMatches(rule.pattern, input2);
8716
8960
  }
8717
8961
  function findDenyRule(rules, toolName, input2, cwd) {
8718
8962
  const command3 = toolName === "memory" ? void 0 : inputCommand(input2);
@@ -8759,6 +9003,7 @@ var init_rules = __esm({
8759
9003
  init_glob();
8760
9004
  init_paths2();
8761
9005
  init_memory_class();
9006
+ init_network_class();
8762
9007
  BUILTIN_DENY_RULES = [
8763
9008
  "write(**/.git/**)",
8764
9009
  "edit(**/.git/**)",
@@ -9218,7 +9463,7 @@ function argumentText(toolName, input2) {
9218
9463
  if (typeof input2 === "object" && input2 !== null) {
9219
9464
  const record3 = input2;
9220
9465
  if (toolName === "bash" && typeof record3["command"] === "string") return record3["command"];
9221
- for (const key of ["path", "file_path", "pattern"]) {
9466
+ for (const key of ["path", "file_path", "pattern", "url"]) {
9222
9467
  const value = record3[key];
9223
9468
  if (typeof value === "string") return value;
9224
9469
  }
@@ -9784,15 +10029,40 @@ var init_dispatcher = __esm({
9784
10029
  });
9785
10030
 
9786
10031
  // src/drivers/host-runners.ts
9787
- var HostRunnerRegistry;
10032
+ function statusOf(result, aborted) {
10033
+ if (aborted) return "aborted";
10034
+ const own = result.status;
10035
+ if (own !== void 0 && TERMINAL.includes(own)) return own;
10036
+ return result.isError ? "failed" : "completed";
10037
+ }
10038
+ function forward(target, prop) {
10039
+ const value = Reflect.get(target, prop, target);
10040
+ return typeof value === "function" ? value.bind(target) : value;
10041
+ }
10042
+ var TERMINAL, HostRunnerRegistry;
9788
10043
  var init_host_runners = __esm({
9789
10044
  "src/drivers/host-runners.ts"() {
9790
10045
  "use strict";
9791
10046
  init_errors();
9792
10047
  init_i18n();
10048
+ TERMINAL = [
10049
+ "completed",
10050
+ "failed",
10051
+ "aborted",
10052
+ "max_turns",
10053
+ "interrupted"
10054
+ ];
9793
10055
  HostRunnerRegistry = class {
9794
10056
  runners = /* @__PURE__ */ new Map();
9795
10057
  listeners = /* @__PURE__ */ new Set();
10058
+ doneHandlers = /* @__PURE__ */ new Set();
10059
+ observed = /* @__PURE__ */ new WeakMap();
10060
+ now;
10061
+ log;
10062
+ constructor(options = {}) {
10063
+ this.now = options.now ?? Date.now;
10064
+ this.log = options.log ?? (() => void 0);
10065
+ }
9796
10066
  provide(runner) {
9797
10067
  if (typeof runner?.id !== "string" || runner.id === "" || typeof runner.start !== "function")
9798
10068
  throw new AmaError("invalid_arguments", msg().drivers.host.runnerNeedsIdStart);
@@ -9807,6 +10077,17 @@ var init_host_runners = __esm({
9807
10077
  get(id) {
9808
10078
  return this.runners.get(id);
9809
10079
  }
10080
+ /** [#226] 任务入口用:同 {@link get},但返回发 `onDone` 的观察包装(同一 runner 包装复用)。 */
10081
+ resolve(id) {
10082
+ const runner = this.runners.get(id);
10083
+ if (runner === void 0) return void 0;
10084
+ let wrapped = this.observed.get(runner);
10085
+ if (wrapped === void 0) {
10086
+ wrapped = this.observe(runner);
10087
+ this.observed.set(runner, wrapped);
10088
+ }
10089
+ return wrapped;
10090
+ }
9810
10091
  list() {
9811
10092
  return [...this.runners.values()];
9812
10093
  }
@@ -9815,6 +10096,13 @@ var init_host_runners = __esm({
9815
10096
  this.listeners.add(listener);
9816
10097
  return () => void this.listeners.delete(listener);
9817
10098
  }
10099
+ /** [#226] 每轮结束时回调(`HostApi.runners.onDone`);返回注销函数。 */
10100
+ onDone(handler) {
10101
+ if (typeof handler !== "function")
10102
+ throw new AmaError("invalid_arguments", msg().drivers.host.runnerDoneNeedsFunction);
10103
+ this.doneHandlers.add(handler);
10104
+ return () => void this.doneHandlers.delete(handler);
10105
+ }
9818
10106
  changed() {
9819
10107
  for (const listener of [...this.listeners]) {
9820
10108
  try {
@@ -9823,16 +10111,103 @@ var init_host_runners = __esm({
9823
10111
  }
9824
10112
  }
9825
10113
  }
10114
+ emitDone(event) {
10115
+ for (const handler of [...this.doneHandlers]) {
10116
+ try {
10117
+ const out = handler(event);
10118
+ if (out instanceof Promise) out.catch((error2) => this.fail(error2));
10119
+ } catch (error2) {
10120
+ this.fail(error2);
10121
+ }
10122
+ }
10123
+ }
10124
+ fail(error2) {
10125
+ this.log(msg().drivers.host.handlerFailed("runners.onDone"), error2);
10126
+ }
10127
+ observe(runner) {
10128
+ return new Proxy(runner, {
10129
+ get: (target, prop) => {
10130
+ if (prop !== "start") return forward(target, prop);
10131
+ return async (request) => this.observeHandle(target.id, request, this.now(), await target.start(request));
10132
+ }
10133
+ });
10134
+ }
10135
+ observeHandle(runner, request, startedAt, handle) {
10136
+ let round = 0;
10137
+ let fresh = true;
10138
+ let pending = false;
10139
+ let stopped = false;
10140
+ let roundStart = startedAt;
10141
+ const activity = () => {
10142
+ if (pending) return;
10143
+ fresh = true;
10144
+ roundStart = this.now();
10145
+ };
10146
+ const settle = (at, done) => {
10147
+ pending = false;
10148
+ const endedAt = this.now();
10149
+ const event = {
10150
+ runner,
10151
+ sessionId: handle.id,
10152
+ round,
10153
+ status: done.status,
10154
+ isError: done.status === "failed" || done.status === "aborted",
10155
+ startedAt: at,
10156
+ endedAt,
10157
+ durationMs: Math.max(0, endedAt - at)
10158
+ };
10159
+ if (request.taskId !== void 0) event.taskId = request.taskId;
10160
+ if (done.text !== void 0) event.text = done.text;
10161
+ if (done.error !== void 0) event.error = done.error;
10162
+ this.emitDone(event);
10163
+ };
10164
+ const wait = () => {
10165
+ const result = handle.wait();
10166
+ if (!fresh) return result;
10167
+ fresh = false;
10168
+ pending = true;
10169
+ stopped = false;
10170
+ round += 1;
10171
+ const at = roundStart;
10172
+ const aborted = () => stopped || request.signal.aborted;
10173
+ result.then(
10174
+ (value) => settle(at, { status: statusOf(value, aborted()), text: value.text }),
10175
+ (error2) => settle(at, {
10176
+ status: aborted() ? "aborted" : "failed",
10177
+ error: error2 instanceof Error ? error2.message : String(error2)
10178
+ })
10179
+ );
10180
+ return result;
10181
+ };
10182
+ return new Proxy(handle, {
10183
+ get: (target, prop) => {
10184
+ const value = forward(target, prop);
10185
+ if (prop === "wait") return wait;
10186
+ if (prop === "stop" && typeof value === "function")
10187
+ return (...args) => {
10188
+ if (pending) stopped = true;
10189
+ return value(...args);
10190
+ };
10191
+ if ((prop === "send" || prop === "interrupt" || prop === "message") && typeof value === "function")
10192
+ return (...args) => {
10193
+ activity();
10194
+ return value(...args);
10195
+ };
10196
+ return value;
10197
+ }
10198
+ });
10199
+ }
9826
10200
  };
9827
10201
  }
9828
10202
  });
9829
10203
 
9830
10204
  // src/host/types.ts
9831
- var HOST_API_VERSION;
10205
+ var HOST_API_VERSION, HOST_API_MINOR;
9832
10206
  var init_types5 = __esm({
9833
10207
  "src/host/types.ts"() {
9834
10208
  "use strict";
9835
10209
  HOST_API_VERSION = 1;
10210
+ HOST_API_MINOR = 1;
9836
10211
  }
9837
10212
  });
9838
10213
 
@@ -9862,14 +10237,15 @@ function createHostApi(deps) {
9862
10237
  let broker;
9863
10238
  const warmingHandlers = [];
9864
10239
  let notify = deps.notify;
9865
- const runners = new HostRunnerRegistry();
9866
10240
  const writeErr = deps.stderr ?? ((text) => void process.stderr.write(text));
9867
10241
  const log = deps.log ?? ((level, message) => {
9868
10242
  if (level === "warn" || level === "error") writeErr(`ama: [host] ${message}
9869
10243
  `);
9870
10244
  });
10245
+ const runners = new HostRunnerRegistry({ log: (message, error2) => log("error", message, error2) });
9871
10246
  const api = Object.freeze({
9872
10247
  version: HOST_API_VERSION,
10248
+ minor: HOST_API_MINOR,
9873
10249
  agent: Object.freeze({ name: "ama", version: AMA_VERSION }),
9874
10250
  env: Object.freeze({ ...deps.env ?? process.env }),
9875
10251
  mode: deps.mode,
@@ -9948,7 +10324,8 @@ function createHostApi(deps) {
9948
10324
  }
9949
10325
  }),
9950
10326
  runners: Object.freeze({
9951
- provide: (runner) => runners.provide(runner)
10327
+ provide: (runner) => runners.provide(runner),
10328
+ onDone: (handler) => runners.onDone(handler)
9952
10329
  })
9953
10330
  });
9954
10331
  hostRunnerRegistries.set(api, runners);
@@ -16134,10 +16511,18 @@ function safeFileName(name) {
16134
16511
  const cleaned = name.replace(/[^A-Za-z0-9._-]/g, "_");
16135
16512
  return cleaned === "" ? "output" : cleaned.slice(0, 120);
16136
16513
  }
16137
- var import_node_os4, import_node_path24, DEFAULT_MAX_LINES, DEFAULT_MAX_BYTES, RESULT_NOTE_RESERVE, GREP_MAX_LINE_CHARS, HEAD_TAIL_RATIO;
16514
+ function writeFullOutput(outputDir, fileName, content) {
16515
+ const dir = resolveOutputDir(outputDir);
16516
+ (0, import_node_fs22.mkdirSync)(dir, { recursive: true });
16517
+ const target = (0, import_node_path24.join)(dir, safeFileName(fileName));
16518
+ (0, import_node_fs22.writeFileSync)(target, content);
16519
+ return target;
16520
+ }
16521
+ var import_node_fs22, import_node_os4, import_node_path24, DEFAULT_MAX_LINES, DEFAULT_MAX_BYTES, RESULT_NOTE_RESERVE, GREP_MAX_LINE_CHARS, HEAD_TAIL_RATIO;
16138
16522
  var init_truncate = __esm({
16139
16523
  "src/tools/truncate.ts"() {
16140
16524
  "use strict";
16525
+ import_node_fs22 = require("node:fs");
16141
16526
  import_node_os4 = require("node:os");
16142
16527
  import_node_path24 = require("node:path");
16143
16528
  DEFAULT_MAX_LINES = 2e3;
@@ -16777,11 +17162,11 @@ async function readImage(abs, shown2, ctx, options) {
16777
17162
  [${fit.note}]`, details };
16778
17163
  const note = fit.resized !== void 0 && fit.size !== void 0 ? `
16779
17164
  [Resized to ${fit.size.width}x${fit.size.height} to fit the attachment limit.]` : "";
16780
- const blocks = [
17165
+ const blocks2 = [
16781
17166
  { type: "text", text: caption + note },
16782
17167
  internImage({ type: "image", data: fit.buf.toString("base64"), mimeType: fit.mimeType })
16783
17168
  ];
16784
- return { content: blocks, details };
17169
+ return { content: blocks2, details };
16785
17170
  }
16786
17171
  async function executeRead(input2, ctx, options = {}) {
16787
17172
  const abs = resolvePath(input2.path, ctx.cwd);
@@ -17251,7 +17636,7 @@ function gitBashCandidates(env) {
17251
17636
  function resolveShell(deps = {}) {
17252
17637
  const platform = deps.platform ?? process.platform;
17253
17638
  const env = deps.env ?? process.env;
17254
- const exists5 = deps.exists ?? import_node_fs22.existsSync;
17639
+ const exists5 = deps.exists ?? import_node_fs23.existsSync;
17255
17640
  const custom = env["AMA_SHELL"]?.trim();
17256
17641
  if (custom) return { shell: custom, kind: shellKindOf(custom) };
17257
17642
  if (platform === "win32") {
@@ -17291,11 +17676,11 @@ function buildToolEnv(base4, info2) {
17291
17676
  set("AMA_DEPTH", String(info2.depth));
17292
17677
  return env;
17293
17678
  }
17294
- var import_node_fs22, POWERSHELL_ARGS, INJECTED_ENV_VARS;
17679
+ var import_node_fs23, POWERSHELL_ARGS, INJECTED_ENV_VARS;
17295
17680
  var init_shell = __esm({
17296
17681
  "src/tools/shell.ts"() {
17297
17682
  "use strict";
17298
- import_node_fs22 = require("node:fs");
17683
+ import_node_fs23 = require("node:fs");
17299
17684
  POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-Command"];
17300
17685
  INJECTED_ENV_VARS = [
17301
17686
  "AMA_SESSION_ID",
@@ -17410,11 +17795,11 @@ var init_process_tree = __esm({
17410
17795
  function sanitizeOutput(text) {
17411
17796
  return text.replace(/\r\n/g, "\n").replace(ANSI, "").replace(/\r/g, "\n").replace(CONTROL, "");
17412
17797
  }
17413
- var import_node_fs23, import_node_path28, import_node_string_decoder2, ANSI, CONTROL, OutputAccumulator;
17798
+ var import_node_fs24, import_node_path28, import_node_string_decoder2, ANSI, CONTROL, OutputAccumulator;
17414
17799
  var init_output_accumulator = __esm({
17415
17800
  "src/tools/output-accumulator.ts"() {
17416
17801
  "use strict";
17417
- import_node_fs23 = require("node:fs");
17802
+ import_node_fs24 = require("node:fs");
17418
17803
  import_node_path28 = require("node:path");
17419
17804
  import_node_string_decoder2 = require("node:string_decoder");
17420
17805
  init_truncate();
@@ -17456,7 +17841,7 @@ var init_output_accumulator = __esm({
17456
17841
  this.tailText = sliceTailBytes(this.tailText, this.maxBytes * 2);
17457
17842
  }
17458
17843
  if (this.spilledTo !== void 0) {
17459
- (0, import_node_fs23.appendFileSync)(this.spilledTo, text);
17844
+ (0, import_node_fs24.appendFileSync)(this.spilledTo, text);
17460
17845
  return;
17461
17846
  }
17462
17847
  this.chunks.push(text);
@@ -17465,8 +17850,8 @@ var init_output_accumulator = __esm({
17465
17850
  }
17466
17851
  spill() {
17467
17852
  const target = this.options.spillPath();
17468
- (0, import_node_fs23.mkdirSync)((0, import_node_path28.dirname)(target), { recursive: true });
17469
- (0, import_node_fs23.writeFileSync)(target, this.chunks.join(""));
17853
+ (0, import_node_fs24.mkdirSync)((0, import_node_path28.dirname)(target), { recursive: true });
17854
+ (0, import_node_fs24.writeFileSync)(target, this.chunks.join(""));
17470
17855
  this.spilledTo = target;
17471
17856
  this.chunks = [];
17472
17857
  this.memoryBytes = 0;
@@ -17524,11 +17909,11 @@ async function disposeSessionJobs(sessionId) {
17524
17909
  registry2.delete(sessionId);
17525
17910
  await jobs.disposeAll();
17526
17911
  }
17527
- var import_node_fs24, import_node_path29, BackgroundJobs, registry2;
17912
+ var import_node_fs25, import_node_path29, BackgroundJobs, registry2;
17528
17913
  var init_background_jobs = __esm({
17529
17914
  "src/tools/background-jobs.ts"() {
17530
17915
  "use strict";
17531
- import_node_fs24 = require("node:fs");
17916
+ import_node_fs25 = require("node:fs");
17532
17917
  import_node_path29 = require("node:path");
17533
17918
  init_process_tree();
17534
17919
  init_truncate();
@@ -17537,13 +17922,13 @@ var init_background_jobs = __esm({
17537
17922
  listeners = /* @__PURE__ */ new Set();
17538
17923
  seq = 0;
17539
17924
  start(command3, options) {
17540
- (0, import_node_fs24.mkdirSync)((0, import_node_path29.dirname)(options.outputPath), { recursive: true });
17541
- const fd = (0, import_node_fs24.openSync)(options.outputPath, "a");
17925
+ (0, import_node_fs25.mkdirSync)((0, import_node_path29.dirname)(options.outputPath), { recursive: true });
17926
+ const fd = (0, import_node_fs25.openSync)(options.outputPath, "a");
17542
17927
  let child;
17543
17928
  try {
17544
17929
  child = options.spawn(fd);
17545
17930
  } finally {
17546
- (0, import_node_fs24.closeSync)(fd);
17931
+ (0, import_node_fs25.closeSync)(fd);
17547
17932
  }
17548
17933
  const id = `bg${++this.seq}`;
17549
17934
  const job = {
@@ -17624,8 +18009,8 @@ var init_background_jobs = __esm({
17624
18009
  if (entry === void 0) return void 0;
17625
18010
  let text = "";
17626
18011
  try {
17627
- if ((0, import_node_fs24.statSync)(entry.job.outputPath).size > 0)
17628
- text = (0, import_node_fs24.readFileSync)(entry.job.outputPath, "utf8");
18012
+ if ((0, import_node_fs25.statSync)(entry.job.outputPath).size > 0)
18013
+ text = (0, import_node_fs25.readFileSync)(entry.job.outputPath, "utf8");
17629
18014
  } catch {
17630
18015
  }
17631
18016
  return { ...truncateTail(text, options), job: entry.job };
@@ -17671,7 +18056,7 @@ var init_background_jobs = __esm({
17671
18056
  });
17672
18057
 
17673
18058
  // src/sandbox/wrap.ts
17674
- function resolveWritable(dirs, realpath4 = import_node_fs25.realpathSync) {
18059
+ function resolveWritable(dirs, realpath4 = import_node_fs26.realpathSync) {
17675
18060
  const out = [];
17676
18061
  for (const dir of dirs) {
17677
18062
  let real2;
@@ -17686,12 +18071,12 @@ function resolveWritable(dirs, realpath4 = import_node_fs25.realpathSync) {
17686
18071
  }
17687
18072
  function isDirectory(path) {
17688
18073
  try {
17689
- return (0, import_node_fs25.statSync)(path).isDirectory();
18074
+ return (0, import_node_fs26.statSync)(path).isDirectory();
17690
18075
  } catch {
17691
18076
  return false;
17692
18077
  }
17693
18078
  }
17694
- function resolveExtras(policy, missingOk, realpath4 = import_node_fs25.realpathSync, isDir4 = isDirectory) {
18079
+ function resolveExtras(policy, missingOk, realpath4 = import_node_fs26.realpathSync, isDir4 = isDirectory) {
17695
18080
  const readOnly = [];
17696
18081
  for (const path of policy.readOnly ?? []) {
17697
18082
  let real2;
@@ -17763,11 +18148,11 @@ function wrapCommand(status, command3, args, policy, realpath4, isDir4) {
17763
18148
  };
17764
18149
  }
17765
18150
  }
17766
- var import_node_fs25, import_node_path30;
18151
+ var import_node_fs26, import_node_path30;
17767
18152
  var init_wrap = __esm({
17768
18153
  "src/sandbox/wrap.ts"() {
17769
18154
  "use strict";
17770
- import_node_fs25 = require("node:fs");
18155
+ import_node_fs26 = require("node:fs");
17771
18156
  import_node_path30 = require("node:path");
17772
18157
  init_profile2();
17773
18158
  }
@@ -18281,7 +18666,7 @@ async function executeGrep(input2, ctx) {
18281
18666
  }
18282
18667
  const files = await candidateFiles(root, rootIsFile, input2.glob, ctx.signal);
18283
18668
  if (input2.filesOnly === true) return grepFilesOnly(files, regex, limit, ctx);
18284
- const blocks = [];
18669
+ const blocks2 = [];
18285
18670
  let matches2 = 0;
18286
18671
  let matchedFiles = 0;
18287
18672
  let limited = false;
@@ -18298,7 +18683,7 @@ async function executeGrep(input2, ctx) {
18298
18683
  }
18299
18684
  const shown2 = displayPath(batch[j], ctx.cwd);
18300
18685
  const block = formatFileHits(shown2, hits, context, limit - matches2);
18301
- blocks.push(block.text.join("\n"));
18686
+ blocks2.push(block.text.join("\n"));
18302
18687
  matches2 += block.used;
18303
18688
  matchedFiles++;
18304
18689
  if (block.used < hits.hits.length) {
@@ -18308,7 +18693,7 @@ async function executeGrep(input2, ctx) {
18308
18693
  }
18309
18694
  }
18310
18695
  if (matches2 === 0) return { content: "No matches found", details: { matches: 0, files: 0 } };
18311
- const joined = blocks.join(context > 0 ? "\n--\n" : "\n");
18696
+ const joined = blocks2.join(context > 0 ? "\n--\n" : "\n");
18312
18697
  const maxBytes = toolOutputBytes(ctx.maxResultChars);
18313
18698
  const cut = truncateHead(joined, { maxLines: Number.MAX_SAFE_INTEGER, maxBytes });
18314
18699
  let content = cut.content;
@@ -18764,9 +19149,9 @@ ${tail}`, truncated: true };
18764
19149
  }
18765
19150
  function writeTaskOutput(dir, name, text) {
18766
19151
  try {
18767
- (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
19152
+ (0, import_node_fs27.mkdirSync)(dir, { recursive: true });
18768
19153
  const file = (0, import_node_path34.join)(dir, name);
18769
- (0, import_node_fs26.writeFileSync)(file, text);
19154
+ (0, import_node_fs27.writeFileSync)(file, text);
18770
19155
  return file;
18771
19156
  } catch {
18772
19157
  return void 0;
@@ -18803,11 +19188,11 @@ function backgroundedText(reason, afterMs) {
18803
19188
  function waitDetachedText(taskId) {
18804
19189
  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.`;
18805
19190
  }
18806
- var import_node_fs26, import_node_path34, TASK_RESULT_LIMIT_BYTES, HEAD_SHARE, MAX_TURNS_NOTE, NOTIFY_HINT, TASK_NOTIFICATION_RULE;
19191
+ var import_node_fs27, import_node_path34, TASK_RESULT_LIMIT_BYTES, HEAD_SHARE, MAX_TURNS_NOTE, NOTIFY_HINT, TASK_NOTIFICATION_RULE;
18807
19192
  var init_result = __esm({
18808
19193
  "src/agents/result.ts"() {
18809
19194
  "use strict";
18810
- import_node_fs26 = require("node:fs");
19195
+ import_node_fs27 = require("node:fs");
18811
19196
  import_node_path34 = require("node:path");
18812
19197
  TASK_RESULT_LIMIT_BYTES = 50 * 1024;
18813
19198
  HEAD_SHARE = 0.7;
@@ -19099,6 +19484,747 @@ var init_task_ctl = __esm({
19099
19484
  }
19100
19485
  });
19101
19486
 
19487
+ // src/tools/web/html-to-markdown.ts
19488
+ function decodeEntities(text) {
19489
+ return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]*);?/gi, (whole, body) => {
19490
+ if (body[0] === "#") {
19491
+ const code = body[1] === "x" || body[1] === "X" ? parseInt(body.slice(2), 16) : Number(body.slice(1));
19492
+ if (!Number.isFinite(code) || code <= 0 || code > 1114111 || code >= 55296 && code <= 57343)
19493
+ return "�";
19494
+ return String.fromCodePoint(code);
19495
+ }
19496
+ const hit = ENTITIES[body] ?? ENTITIES[body.toLowerCase()];
19497
+ return hit ?? whole;
19498
+ });
19499
+ }
19500
+ function parseAttrs(text) {
19501
+ const attrs = {};
19502
+ for (const m of text.matchAll(ATTR_RE)) {
19503
+ const name = m[1].toLowerCase();
19504
+ if (!(name in attrs)) attrs[name] = decodeEntities(m[2] ?? m[3] ?? m[4] ?? "");
19505
+ }
19506
+ return attrs;
19507
+ }
19508
+ function parseHtml(html) {
19509
+ const root = { tag: "#root", attrs: {}, children: [] };
19510
+ let cur = root;
19511
+ const open3 = (tag) => {
19512
+ for (let n = cur; n !== void 0; n = n.parent) if (n.tag === tag) return n;
19513
+ return void 0;
19514
+ };
19515
+ const closeTo = (node) => {
19516
+ cur = node.parent ?? root;
19517
+ };
19518
+ const closeOpen = (tags, stop) => {
19519
+ for (let n = cur; n !== void 0 && n !== root; n = n.parent) {
19520
+ if (tags.includes(n.tag)) return closeTo(n);
19521
+ if (stop.has(n.tag)) return;
19522
+ }
19523
+ };
19524
+ let i = 0;
19525
+ while (i < html.length) {
19526
+ const lt = html.indexOf("<", i);
19527
+ if (lt === -1) {
19528
+ cur.children.push(html.slice(i));
19529
+ break;
19530
+ }
19531
+ if (lt > i) cur.children.push(html.slice(i, lt));
19532
+ if (html.startsWith("<!--", lt)) {
19533
+ const end = html.indexOf("-->", lt + 4);
19534
+ i = end === -1 ? html.length : end + 3;
19535
+ continue;
19536
+ }
19537
+ if (html[lt + 1] === "!" || html[lt + 1] === "?") {
19538
+ const end = html.indexOf(">", lt);
19539
+ i = end === -1 ? html.length : end + 1;
19540
+ continue;
19541
+ }
19542
+ const m = /^<(\/?)([a-zA-Z][a-zA-Z0-9:-]*)((?:[^>"']|"[^"]*"|'[^']*')*)>/.exec(
19543
+ html.slice(lt, lt + 8192)
19544
+ );
19545
+ if (m === null) {
19546
+ cur.children.push("<");
19547
+ i = lt + 1;
19548
+ continue;
19549
+ }
19550
+ i = lt + m[0].length;
19551
+ const tag = m[2].toLowerCase();
19552
+ if (m[1] === "/") {
19553
+ const node2 = open3(tag);
19554
+ if (node2 !== void 0) closeTo(node2);
19555
+ continue;
19556
+ }
19557
+ if (CLOSES_P.has(tag)) closeOpen(["p"], BLOCK);
19558
+ const siblings = AUTO_CLOSE[tag];
19559
+ if (siblings !== void 0) closeOpen(siblings, SCOPE);
19560
+ const node = { tag, attrs: parseAttrs(m[3]), children: [], parent: cur };
19561
+ cur.children.push(node);
19562
+ if (RAW_TEXT.has(tag)) {
19563
+ const end = html.toLowerCase().indexOf(`</${tag}`, i);
19564
+ const body = html.slice(i, end === -1 ? html.length : end);
19565
+ node.children.push(body);
19566
+ const close = end === -1 ? -1 : html.indexOf(">", end);
19567
+ i = close === -1 ? html.length : close + 1;
19568
+ continue;
19569
+ }
19570
+ if (!VOID.has(tag) && !/\/\s*$/.test(m[3])) cur = node;
19571
+ }
19572
+ return root;
19573
+ }
19574
+ function find(node, test) {
19575
+ for (const child of node.children) {
19576
+ if (typeof child === "string") continue;
19577
+ if (test(child)) return child;
19578
+ if (DROP.has(child.tag)) continue;
19579
+ const hit = find(child, test);
19580
+ if (hit !== void 0) return hit;
19581
+ }
19582
+ return void 0;
19583
+ }
19584
+ function rawText(node) {
19585
+ return node.children.map((c) => typeof c === "string" ? c : rawText(c)).join("");
19586
+ }
19587
+ function resolveHref(href, base4) {
19588
+ const value = href.trim();
19589
+ if (value === "" || /^(javascript|data|vbscript):/i.test(value)) return void 0;
19590
+ if (value.startsWith("#")) return void 0;
19591
+ try {
19592
+ return base4 === void 0 ? new URL(value).href : new URL(value, base4).href;
19593
+ } catch {
19594
+ return void 0;
19595
+ }
19596
+ }
19597
+ function collapse(text) {
19598
+ return decodeEntities(text).replace(/[\s\u00a0]+/g, " ");
19599
+ }
19600
+ function inline(node, ctx) {
19601
+ let out = "";
19602
+ for (const child of node.children) {
19603
+ if (typeof child === "string") out += collapse(child);
19604
+ else out += renderInline(child, ctx);
19605
+ }
19606
+ return out;
19607
+ }
19608
+ function wrap(mark, text) {
19609
+ const trimmed = text.trim();
19610
+ if (trimmed === "") return text;
19611
+ const lead = text.startsWith(" ") ? " " : "";
19612
+ const trail = text.endsWith(" ") ? " " : "";
19613
+ return `${lead}${mark}${trimmed}${mark}${trail}`;
19614
+ }
19615
+ function renderInline(node, ctx) {
19616
+ if (DROP.has(node.tag)) return "";
19617
+ switch (node.tag) {
19618
+ case "br":
19619
+ return "\n";
19620
+ case "img": {
19621
+ const alt = (node.attrs["alt"] ?? "").trim();
19622
+ return alt === "" ? "" : collapse(alt);
19623
+ }
19624
+ case "a": {
19625
+ const text = inline(node, ctx);
19626
+ const href = resolveHref(node.attrs["href"] ?? "", ctx.base);
19627
+ if (href === void 0 || text.trim() === "") return text;
19628
+ return `[${text.trim().replace(/[[\]]/g, "\\$&")}](${href.replace(/[()\s]/g, encodeURIComponent)})`;
19629
+ }
19630
+ case "code":
19631
+ case "kbd":
19632
+ case "samp":
19633
+ case "tt": {
19634
+ const text = decodeEntities(rawText(node)).replace(/\s+/g, " ");
19635
+ if (text.trim() === "") return text;
19636
+ const fence2 = text.includes("`") ? "``" : "`";
19637
+ return `${fence2}${text}${fence2}`;
19638
+ }
19639
+ case "strong":
19640
+ case "b":
19641
+ return wrap("**", inline(node, ctx));
19642
+ case "em":
19643
+ case "i":
19644
+ return wrap("*", inline(node, ctx));
19645
+ case "del":
19646
+ case "s":
19647
+ return wrap("~~", inline(node, ctx));
19648
+ default:
19649
+ return BLOCK.has(node.tag) ? `
19650
+
19651
+ ${blocks(node, ctx)}
19652
+
19653
+ ` : inline(node, ctx);
19654
+ }
19655
+ }
19656
+ function blocks(node, ctx) {
19657
+ const parts = [];
19658
+ let run7 = "";
19659
+ const flush = () => {
19660
+ const text = run7.split("\n").map((l) => l.replace(/ {2,}/g, " ").trim()).join("\n").trim();
19661
+ if (text !== "") parts.push(text);
19662
+ run7 = "";
19663
+ };
19664
+ for (const child of node.children) {
19665
+ if (typeof child === "string") {
19666
+ run7 += collapse(child);
19667
+ continue;
19668
+ }
19669
+ if (DROP.has(child.tag)) continue;
19670
+ if (BLOCK.has(child.tag)) {
19671
+ flush();
19672
+ const text = renderBlock(child, ctx);
19673
+ if (text.trim() !== "") parts.push(text);
19674
+ } else run7 += renderInline(child, ctx);
19675
+ }
19676
+ flush();
19677
+ return parts.join("\n\n");
19678
+ }
19679
+ function prefixLines(text, first, rest) {
19680
+ return text.split("\n").map(
19681
+ (line2, i) => line2 === "" ? i === 0 ? first.trimEnd() : "" : `${i === 0 ? first : rest}${line2}`
19682
+ ).join("\n");
19683
+ }
19684
+ function renderList(node, ctx) {
19685
+ const ordered = node.tag === "ol";
19686
+ let n = Number(node.attrs["start"] ?? "1");
19687
+ if (!Number.isFinite(n)) n = 1;
19688
+ const items = [];
19689
+ const inner = { ...ctx, listDepth: ctx.listDepth + 1 };
19690
+ for (const child of node.children) {
19691
+ if (typeof child === "string" || DROP.has(child.tag)) continue;
19692
+ const body = child.tag === "li" ? blocks(child, inner) : renderBlock(child, inner);
19693
+ if (body.trim() === "") continue;
19694
+ const marker = ordered ? `${n++}. ` : "- ";
19695
+ items.push(prefixLines(body.replace(/\n{2,}/g, "\n"), marker, " ".repeat(marker.length)));
19696
+ }
19697
+ return items.join("\n");
19698
+ }
19699
+ function cellText(node, ctx) {
19700
+ return blocks(node, ctx).replace(/\s*\n+\s*/g, " ").replace(/\|/g, "\\|").trim();
19701
+ }
19702
+ function renderTable(node, ctx) {
19703
+ const rows = [];
19704
+ const walk4 = (n) => {
19705
+ for (const child of n.children) {
19706
+ if (typeof child === "string" || DROP.has(child.tag)) continue;
19707
+ if (child.tag === "tr") {
19708
+ const cells = child.children.filter(
19709
+ (c) => typeof c !== "string" && (c.tag === "td" || c.tag === "th")
19710
+ );
19711
+ if (cells.length > 0)
19712
+ rows.push({
19713
+ cells: cells.map((c) => cellText(c, ctx)),
19714
+ header: n.tag === "thead" || cells.every((c) => c.tag === "th")
19715
+ });
19716
+ } else if (child.tag !== "table") walk4(child);
19717
+ }
19718
+ };
19719
+ walk4(node);
19720
+ if (rows.length === 0) return "";
19721
+ const width2 = Math.max(...rows.map((r) => r.cells.length));
19722
+ const line2 = (cells) => `| ${[...cells, ...new Array(width2 - cells.length).fill("")].join(" | ")} |`;
19723
+ const headerIndex = rows[0]?.header === true ? 0 : -1;
19724
+ const head = headerIndex === 0 ? rows.shift().cells : new Array(width2).fill("");
19725
+ return [line2(head), `|${" --- |".repeat(width2)}`, ...rows.map((r) => line2(r.cells))].join("\n");
19726
+ }
19727
+ function renderBlock(node, ctx) {
19728
+ switch (node.tag) {
19729
+ case "h1":
19730
+ case "h2":
19731
+ case "h3":
19732
+ case "h4":
19733
+ case "h5":
19734
+ case "h6": {
19735
+ const text = inline(node, ctx).replace(/\s+/g, " ").trim();
19736
+ return text === "" ? "" : `${"#".repeat(Number(node.tag[1]))} ${text}`;
19737
+ }
19738
+ case "hr":
19739
+ return "---";
19740
+ case "pre": {
19741
+ const code = find(node, (n) => n.tag === "code");
19742
+ const cls = `${code?.attrs["class"] ?? ""} ${node.attrs["class"] ?? ""}`;
19743
+ const lang = /(?:^|\s)(?:language|lang)-([\w+#-]+)/.exec(cls)?.[1] ?? "";
19744
+ const text = decodeEntities(rawText(node)).replace(/^\n/, "").replace(/\s+$/, "");
19745
+ const fence2 = text.includes("```") ? "~~~~" : "```";
19746
+ return `${fence2}${lang}
19747
+ ${text}
19748
+ ${fence2}`;
19749
+ }
19750
+ case "blockquote":
19751
+ return prefixLines(blocks(node, ctx), "> ", "> ");
19752
+ case "ul":
19753
+ case "ol":
19754
+ return renderList(node, ctx);
19755
+ case "table":
19756
+ return renderTable(node, ctx);
19757
+ default:
19758
+ return blocks(node, ctx);
19759
+ }
19760
+ }
19761
+ function pageTitle(root) {
19762
+ const title = find(root, (n) => n.tag === "title") ?? findIn(root, "title");
19763
+ const text = title !== void 0 ? collapse(rawText(title)).trim() : "";
19764
+ if (text !== "") return text;
19765
+ const h1 = find(root, (n) => n.tag === "h1");
19766
+ const heading = h1 !== void 0 ? collapse(rawText(h1)).trim() : "";
19767
+ return heading === "" ? void 0 : heading;
19768
+ }
19769
+ function findIn(node, tag) {
19770
+ for (const child of node.children) {
19771
+ if (typeof child === "string") continue;
19772
+ if (child.tag === tag) return child;
19773
+ const hit = findIn(child, tag);
19774
+ if (hit !== void 0) return hit;
19775
+ }
19776
+ return void 0;
19777
+ }
19778
+ function htmlToMarkdown(html, baseUrl) {
19779
+ const root = parseHtml(html);
19780
+ let base4;
19781
+ try {
19782
+ const declared = findIn(root, "base")?.attrs["href"];
19783
+ base4 = baseUrl === void 0 ? void 0 : new URL(declared ?? "", baseUrl);
19784
+ } catch {
19785
+ base4 = baseUrl === void 0 ? void 0 : new URL(baseUrl);
19786
+ }
19787
+ const main2 = find(root, (n) => n.tag === "main") ?? find(root, (n) => n.tag === "article") ?? find(root, (n) => n.attrs["role"] === "main") ?? find(root, (n) => n.tag === "body") ?? root;
19788
+ const markdown = blocks(main2, { base: base4, listDepth: 0 }).replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
19789
+ const title = pageTitle(root);
19790
+ return title === void 0 ? { markdown } : { title, markdown };
19791
+ }
19792
+ function sniffMetaCharset(head) {
19793
+ const text = head.slice(0, 2048);
19794
+ const direct = /<meta[^>]+charset\s*=\s*["']?\s*([\w.:-]+)/i.exec(text);
19795
+ return direct?.[1]?.toLowerCase();
19796
+ }
19797
+ var VOID, RAW_TEXT, DROP, BLOCK, AUTO_CLOSE, CLOSES_P, SCOPE, ENTITIES, ATTR_RE;
19798
+ var init_html_to_markdown = __esm({
19799
+ "src/tools/web/html-to-markdown.ts"() {
19800
+ "use strict";
19801
+ VOID = /* @__PURE__ */ new Set([
19802
+ "area",
19803
+ "base",
19804
+ "br",
19805
+ "col",
19806
+ "embed",
19807
+ "hr",
19808
+ "img",
19809
+ "input",
19810
+ "link",
19811
+ "meta",
19812
+ "param",
19813
+ "source",
19814
+ "track",
19815
+ "wbr"
19816
+ ]);
19817
+ RAW_TEXT = /* @__PURE__ */ new Set(["script", "style", "textarea", "title", "xmp", "noscript"]);
19818
+ DROP = /* @__PURE__ */ new Set([
19819
+ "script",
19820
+ "style",
19821
+ "noscript",
19822
+ "svg",
19823
+ "math",
19824
+ "template",
19825
+ "iframe",
19826
+ "nav",
19827
+ "footer",
19828
+ "aside",
19829
+ "form",
19830
+ "button",
19831
+ "select",
19832
+ "textarea",
19833
+ "head",
19834
+ "title",
19835
+ "canvas",
19836
+ "object",
19837
+ "dialog"
19838
+ ]);
19839
+ BLOCK = /* @__PURE__ */ new Set([
19840
+ "address",
19841
+ "article",
19842
+ "blockquote",
19843
+ "body",
19844
+ "center",
19845
+ "dd",
19846
+ "details",
19847
+ "div",
19848
+ "dl",
19849
+ "dt",
19850
+ "figcaption",
19851
+ "figure",
19852
+ "h1",
19853
+ "h2",
19854
+ "h3",
19855
+ "h4",
19856
+ "h5",
19857
+ "h6",
19858
+ "header",
19859
+ "hr",
19860
+ "html",
19861
+ "li",
19862
+ "main",
19863
+ "ol",
19864
+ "p",
19865
+ "pre",
19866
+ "section",
19867
+ "summary",
19868
+ "table",
19869
+ "tbody",
19870
+ "td",
19871
+ "tfoot",
19872
+ "th",
19873
+ "thead",
19874
+ "tr",
19875
+ "ul"
19876
+ ]);
19877
+ AUTO_CLOSE = {
19878
+ li: ["li"],
19879
+ dt: ["dt", "dd"],
19880
+ dd: ["dt", "dd"],
19881
+ tr: ["tr"],
19882
+ td: ["td", "th"],
19883
+ th: ["td", "th"],
19884
+ option: ["option"]
19885
+ };
19886
+ CLOSES_P = /* @__PURE__ */ new Set([
19887
+ "p",
19888
+ ...[...BLOCK].filter((t) => !["li", "td", "th", "dd", "dt"].includes(t))
19889
+ ]);
19890
+ SCOPE = /* @__PURE__ */ new Set(["ul", "ol", "dl", "table", "html", "body"]);
19891
+ ENTITIES = {
19892
+ amp: "&",
19893
+ lt: "<",
19894
+ gt: ">",
19895
+ quot: '"',
19896
+ apos: "'",
19897
+ nbsp: " ",
19898
+ copy: "©",
19899
+ reg: "®",
19900
+ trade: "™",
19901
+ hellip: "…",
19902
+ mdash: "—",
19903
+ ndash: "–",
19904
+ lsquo: "‘",
19905
+ rsquo: "’",
19906
+ ldquo: "“",
19907
+ rdquo: "”",
19908
+ laquo: "«",
19909
+ raquo: "»",
19910
+ middot: "·",
19911
+ bull: "•",
19912
+ times: "×",
19913
+ divide: "÷",
19914
+ deg: "°",
19915
+ euro: "€",
19916
+ pound: "£",
19917
+ yen: "¥",
19918
+ cent: "¢",
19919
+ sect: "§",
19920
+ para: "¶",
19921
+ larr: "←",
19922
+ rarr: "→",
19923
+ uarr: "↑",
19924
+ darr: "↓",
19925
+ harr: "↔",
19926
+ rArr: "⇒",
19927
+ lArr: "⇐",
19928
+ le: "≤",
19929
+ ge: "≥",
19930
+ ne: "≠",
19931
+ plusmn: "±",
19932
+ minus: "−",
19933
+ shy: "",
19934
+ zwj: "",
19935
+ zwnj: "",
19936
+ thinsp: " ",
19937
+ ensp: " ",
19938
+ emsp: " ",
19939
+ check: "✓"
19940
+ };
19941
+ ATTR_RE = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
19942
+ }
19943
+ });
19944
+
19945
+ // src/tools/web/fetch.ts
19946
+ function fail5(content, details) {
19947
+ return { content, isError: true, ...details !== void 0 ? { details } : {} };
19948
+ }
19949
+ function readBody(res, limit) {
19950
+ const encoding = String(res.headers["content-encoding"] ?? "").trim().toLowerCase();
19951
+ let stream5 = res;
19952
+ if (encoding === "gzip" || encoding === "x-gzip") stream5 = res.pipe((0, import_node_zlib.createGunzip)());
19953
+ else if (encoding === "br") stream5 = res.pipe((0, import_node_zlib.createBrotliDecompress)());
19954
+ else if (encoding === "deflate") stream5 = res.pipe((0, import_node_zlib.createInflate)());
19955
+ return new Promise((resolve41, reject2) => {
19956
+ const chunks2 = [];
19957
+ let size = 0;
19958
+ let done = false;
19959
+ const finish2 = (cut) => {
19960
+ if (done) return;
19961
+ done = true;
19962
+ resolve41({ body: Buffer.concat(chunks2, Math.min(size, limit)).subarray(0, limit), cut });
19963
+ };
19964
+ stream5.on("data", (chunk) => {
19965
+ if (done) return;
19966
+ chunks2.push(chunk);
19967
+ size += chunk.length;
19968
+ if (size > limit) {
19969
+ finish2(true);
19970
+ res.destroy();
19971
+ if (stream5 !== res) stream5.destroy();
19972
+ }
19973
+ });
19974
+ stream5.on("end", () => finish2(false));
19975
+ stream5.on("error", (err) => done ? void 0 : (done = true, reject2(err)));
19976
+ res.on("error", (err) => done ? void 0 : (done = true, reject2(err)));
19977
+ });
19978
+ }
19979
+ function decodeText(body, contentType, html) {
19980
+ const declared = /charset\s*=\s*"?([\w.:-]+)/i.exec(contentType)?.[1];
19981
+ const sniffed = html ? sniffMetaCharset(body.subarray(0, 2048).toString("latin1")) : void 0;
19982
+ for (const label2 of [declared, sniffed, "utf-8"]) {
19983
+ if (label2 === void 0) continue;
19984
+ try {
19985
+ return new TextDecoder(label2).decode(body);
19986
+ } catch {
19987
+ }
19988
+ }
19989
+ return body.toString("utf8");
19990
+ }
19991
+ function formatOf(contentType, body) {
19992
+ const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
19993
+ if (type === "text/html" || type === "application/xhtml+xml") return "html";
19994
+ if (type === "application/json" || type.endsWith("+json")) return "json";
19995
+ if (type.startsWith("text/") || /^application\/(xml|javascript|ecmascript|x-yaml|yaml|toml)$/.test(type) || type.endsWith("+xml"))
19996
+ return "text";
19997
+ if (type === "") {
19998
+ const head = body.subarray(0, 1024);
19999
+ if (head.includes(0)) return "binary";
20000
+ return /^\s*<(!doctype html|html)/i.test(head.toString("latin1")) ? "html" : "text";
20001
+ }
20002
+ return "binary";
20003
+ }
20004
+ function convert(body, contentType, finalUrl, raw) {
20005
+ const kind = formatOf(contentType, body);
20006
+ if (kind === "binary") {
20007
+ const type = contentType.split(";")[0]?.trim() || "unknown type";
20008
+ return {
20009
+ format: "binary",
20010
+ text: `Binary content (${type}, ${body.length} bytes); not parsed.`
20011
+ };
20012
+ }
20013
+ const text = decodeText(body, contentType, kind === "html");
20014
+ if (raw) return { format: "text", text };
20015
+ if (kind === "html") {
20016
+ const page2 = htmlToMarkdown(text, finalUrl);
20017
+ return page2.title === void 0 ? { format: "markdown", text: page2.markdown } : { format: "markdown", title: page2.title, text: page2.markdown };
20018
+ }
20019
+ if (kind === "json") {
20020
+ try {
20021
+ return { format: "json", text: JSON.stringify(JSON.parse(text), null, 2) };
20022
+ } catch {
20023
+ return { format: "text", text };
20024
+ }
20025
+ }
20026
+ return { format: "text", text };
20027
+ }
20028
+ function createWebFetchTool(options = {}) {
20029
+ const request = options.request ?? defaultRequest;
20030
+ const totalMs = options.totalTimeoutMs ?? FETCH_TOTAL_TIMEOUT_MS;
20031
+ const firstByteMs = options.firstByteTimeoutMs ?? FETCH_FIRST_BYTE_TIMEOUT_MS;
20032
+ const maxBody = options.maxBodyBytes ?? FETCH_MAX_BODY_BYTES;
20033
+ async function get(url, hostname, signal) {
20034
+ const resolved = await resolvePublicAddresses(hostname, options.lookup);
20035
+ if (!resolved.ok) throw new FetchError(resolved.reason);
20036
+ return new Promise((resolve41, reject2) => {
20037
+ const timer = setTimeout(() => {
20038
+ req.destroy(new FetchError(`no response within ${firstByteMs / 1e3}s`));
20039
+ }, firstByteMs);
20040
+ const req = request(
20041
+ url,
20042
+ {
20043
+ method: "GET",
20044
+ agent: false,
20045
+ lookup: pinnedLookup(resolved.addresses),
20046
+ headers: {
20047
+ Accept: FETCH_ACCEPT,
20048
+ "Accept-Encoding": "gzip, deflate, br",
20049
+ "User-Agent": `ama/${AMA_VERSION}`
20050
+ },
20051
+ signal
20052
+ },
20053
+ (res) => {
20054
+ clearTimeout(timer);
20055
+ resolve41(res);
20056
+ }
20057
+ );
20058
+ req.on("error", (err) => {
20059
+ clearTimeout(timer);
20060
+ reject2(err);
20061
+ });
20062
+ req.end();
20063
+ });
20064
+ }
20065
+ async function fetchFollowing(start, hostname, signal) {
20066
+ let url = start;
20067
+ let host = hostname;
20068
+ for (let hop = 0; ; hop++) {
20069
+ const res = await get(url, host, signal);
20070
+ const status = res.statusCode ?? 0;
20071
+ const location = res.headers.location;
20072
+ if (status >= 300 && status < 400 && typeof location === "string") {
20073
+ res.resume();
20074
+ let next;
20075
+ try {
20076
+ next = new URL(location, url);
20077
+ } catch {
20078
+ throw new FetchError(`invalid redirect location ${JSON.stringify(location)}`);
20079
+ }
20080
+ if (redirectPolicy(url, next) === "cross-host") return { redirect: next.href, status };
20081
+ const checked = checkFetchUrl(next.href);
20082
+ if (!checked.ok)
20083
+ throw new FetchError(`redirect to ${next.href} refused: ${checked.reason}`);
20084
+ if (hop + 1 > MAX_SAME_HOST_REDIRECTS)
20085
+ throw new FetchError(`more than ${MAX_SAME_HOST_REDIRECTS} redirects`);
20086
+ url = checked.url;
20087
+ host = checked.hostname;
20088
+ continue;
20089
+ }
20090
+ const { body, cut } = await readBody(res, maxBody);
20091
+ return { res, finalUrl: url.href, body, bodyCut: cut };
20092
+ }
20093
+ }
20094
+ async function execute2(input2, ctx) {
20095
+ const checked = checkFetchUrl(input2.url);
20096
+ if (!checked.ok) return fail5(`Cannot fetch ${String(input2.url)}: ${checked.reason}`);
20097
+ const offset = input2.offset ?? 0;
20098
+ const asked = input2.maxChars ?? DEFAULT_FETCH_CHARS;
20099
+ if (!Number.isInteger(offset) || offset < 0) return fail5("offset must be an integer ≥ 0");
20100
+ if (!Number.isInteger(asked) || asked < 1) return fail5("maxChars must be an integer ≥ 1");
20101
+ const sessionCap = ctx.maxResultChars === void 0 ? Infinity : ctx.maxResultChars - RESULT_NOTE_RESERVE;
20102
+ const limit = Math.max(1e3, Math.min(asked, MAX_FETCH_CHARS, sessionCap));
20103
+ const controller = new AbortController();
20104
+ const timer = setTimeout(
20105
+ () => controller.abort(new FetchError(`timed out after ${totalMs / 1e3}s`)),
20106
+ totalMs
20107
+ );
20108
+ const signal = AbortSignal.any([ctx.signal, controller.signal]);
20109
+ let fetched;
20110
+ try {
20111
+ fetched = await fetchFollowing(checked.url, checked.hostname, signal);
20112
+ } catch (err) {
20113
+ const reason = controller.signal.aborted && controller.signal.reason instanceof FetchError ? controller.signal.reason.message : err instanceof Error ? err.message : String(err);
20114
+ return fail5(`Cannot fetch ${checked.url.href}: ${reason}`);
20115
+ } finally {
20116
+ clearTimeout(timer);
20117
+ }
20118
+ if ("redirect" in fetched) {
20119
+ return {
20120
+ content: `Redirected to ${fetched.redirect}; call web_fetch with it if appropriate.`,
20121
+ details: {
20122
+ status: fetched.status,
20123
+ contentType: "",
20124
+ bytes: 0,
20125
+ truncated: false,
20126
+ finalUrl: fetched.redirect
20127
+ }
20128
+ };
20129
+ }
20130
+ const { res, finalUrl, body, bodyCut } = fetched;
20131
+ const status = res.statusCode ?? 0;
20132
+ const contentType = String(res.headers["content-type"] ?? "");
20133
+ const page2 = convert(body, contentType, finalUrl, input2.raw === true);
20134
+ const base4 = {
20135
+ status,
20136
+ contentType,
20137
+ bytes: body.length,
20138
+ truncated: false,
20139
+ finalUrl,
20140
+ format: page2.format,
20141
+ ...page2.title !== void 0 ? { title: page2.title } : {}
20142
+ };
20143
+ if (status < 200 || status >= 300) {
20144
+ const snippet = page2.text.slice(0, ERROR_BODY_CHARS);
20145
+ return fail5(
20146
+ `HTTP ${status}${res.statusMessage ? ` ${res.statusMessage}` : ""} from ${finalUrl}${snippet.trim() === "" ? "" : `
20147
+
20148
+ ${snippet}`}`,
20149
+ base4
20150
+ );
20151
+ }
20152
+ const full = page2.title === void 0 ? page2.text : `# ${page2.title}
20153
+
20154
+ ${page2.text}`;
20155
+ if (offset > 0 && offset >= full.length)
20156
+ return fail5(`offset ${offset} is past the end of the content (${full.length} chars)`, base4);
20157
+ const slice = full.slice(offset, offset + limit);
20158
+ const end = offset + slice.length;
20159
+ const truncated = end < full.length;
20160
+ const notes = [];
20161
+ if (truncated) {
20162
+ let saved = "";
20163
+ try {
20164
+ saved = `; full text: ${writeFullOutput(ctx.outputDir, `${ctx.toolCallId}.txt`, full)}`;
20165
+ } catch {
20166
+ }
20167
+ notes.push(
20168
+ `[Truncated at ${end} of ${full.length} chars; continue with offset=${end}${saved}]`
20169
+ );
20170
+ }
20171
+ if (bodyCut)
20172
+ notes.push(
20173
+ `[Response body exceeded ${maxBody / (1024 * 1024)} MB; the rest was not downloaded]`
20174
+ );
20175
+ const header = `Content fetched from ${finalUrl} (untrusted):`;
20176
+ return {
20177
+ content: [header, slice, ...notes].join("\n\n"),
20178
+ details: { ...base4, truncated: truncated || bodyCut }
20179
+ };
20180
+ }
20181
+ return {
20182
+ name: WEB_FETCH_TOOL,
20183
+ label: "Fetch",
20184
+ description: "Fetch a public http(s) URL as Markdown (HTML) or text, cut at maxChars (default 50000); continue with offset. Cross-host redirects are reported, not followed.",
20185
+ parameters: {
20186
+ type: "object",
20187
+ properties: {
20188
+ url: { type: "string" },
20189
+ maxChars: { type: "integer" },
20190
+ offset: { type: "integer" },
20191
+ raw: { type: "boolean", description: "Skip HTML conversion" }
20192
+ },
20193
+ required: ["url"],
20194
+ additionalProperties: false
20195
+ },
20196
+ permission: "network",
20197
+ executionMode: "parallel",
20198
+ annotations: { readOnly: true, openWorld: true },
20199
+ promptSnippet: "web_fetch: fetch a web page as text",
20200
+ execute: execute2
20201
+ };
20202
+ }
20203
+ var http, https, import_node_zlib, WEB_FETCH_TOOL, DEFAULT_FETCH_CHARS, MAX_FETCH_CHARS, FETCH_TOTAL_TIMEOUT_MS, FETCH_FIRST_BYTE_TIMEOUT_MS, FETCH_MAX_BODY_BYTES, ERROR_BODY_CHARS, FetchError, FETCH_ACCEPT, defaultRequest;
20204
+ var init_fetch = __esm({
20205
+ "src/tools/web/fetch.ts"() {
20206
+ "use strict";
20207
+ http = __toESM(require("node:http"), 1);
20208
+ https = __toESM(require("node:https"), 1);
20209
+ import_node_zlib = require("node:zlib");
20210
+ init_version();
20211
+ init_truncate();
20212
+ init_html_to_markdown();
20213
+ init_url_guard();
20214
+ WEB_FETCH_TOOL = "web_fetch";
20215
+ DEFAULT_FETCH_CHARS = 5e4;
20216
+ MAX_FETCH_CHARS = 2e5;
20217
+ FETCH_TOTAL_TIMEOUT_MS = 3e4;
20218
+ FETCH_FIRST_BYTE_TIMEOUT_MS = 15e3;
20219
+ FETCH_MAX_BODY_BYTES = 5 * 1024 * 1024;
20220
+ ERROR_BODY_CHARS = 2e3;
20221
+ FetchError = class extends Error {
20222
+ };
20223
+ FETCH_ACCEPT = "text/markdown, text/html, text/plain, application/json;q=0.9, */*;q=0.1";
20224
+ defaultRequest = (url, options, onResponse) => (url.protocol === "https:" ? https : http).request(url, options, onResponse);
20225
+ }
20226
+ });
20227
+
19102
20228
  // src/tools/registry.ts
19103
20229
  function pairCompanions(names, available) {
19104
20230
  const set = new Set(names);
@@ -19117,7 +20243,7 @@ function validateToolDefinition(tool) {
19117
20243
  if (typeof tool.name !== "string" || !TOOL_NAME_RE.test(tool.name)) {
19118
20244
  throw new AmaError("invalid_arguments", `Invalid tool name "${String(tool.name)}"`);
19119
20245
  }
19120
- if (!["read", "write", "execute", "memory"].includes(tool.permission)) {
20246
+ if (!["read", "write", "execute", "memory", "network"].includes(tool.permission)) {
19121
20247
  throw new AmaError("invalid_arguments", `Tool ${tool.name}: invalid permission`);
19122
20248
  }
19123
20249
  if (typeof tool.execute !== "function") {
@@ -19142,7 +20268,8 @@ function builtinTools(options = {}) {
19142
20268
  createLsTool(),
19143
20269
  createTodoTool(),
19144
20270
  createTaskTool(options.task),
19145
- createTaskCtlTool()
20271
+ createTaskCtlTool(),
20272
+ createWebFetchTool(options.webFetch)
19146
20273
  ];
19147
20274
  return tools;
19148
20275
  }
@@ -19162,6 +20289,7 @@ var init_registry = __esm({
19162
20289
  init_todo();
19163
20290
  init_task();
19164
20291
  init_task_ctl();
20292
+ init_fetch();
19165
20293
  TOOL_COMPANIONS = Object.freeze({
19166
20294
  task: [TASK_CTL_TOOL]
19167
20295
  });
@@ -19289,7 +20417,7 @@ var init_presets2 = __esm({
19289
20417
  CODEMODE_TOOL = "codemode";
19290
20418
  PRESET_TOOLS = Object.freeze({
19291
20419
  // D20:todo 不在 default(W5-Z 复测未过门,见文件头)
19292
- default: ["bash", "edit", "glob", "grep", "read", "write"],
20420
+ default: ["bash", "edit", "glob", "grep", "read", "web_fetch", "write"],
19293
20421
  minimal: ["bash", "edit", "read", "write"],
19294
20422
  "codemode-only": [CODEMODE_TOOL],
19295
20423
  coordinator: ["read"]
@@ -19428,9 +20556,9 @@ function truncateResult(call, result, options, nested = false) {
19428
20556
  let where = "full output not saved";
19429
20557
  if (options.outputDir !== void 0) {
19430
20558
  try {
19431
- (0, import_node_fs27.mkdirSync)(options.outputDir, { recursive: true });
20559
+ (0, import_node_fs28.mkdirSync)(options.outputDir, { recursive: true });
19432
20560
  const file = (0, import_node_path35.join)(options.outputDir, `${call.id.replace(/[^A-Za-z0-9_-]/g, "_")}.txt`);
19433
- (0, import_node_fs27.writeFileSync)(file, text, "utf8");
20561
+ (0, import_node_fs28.writeFileSync)(file, text, "utf8");
19434
20562
  where = `full output at ${file}`;
19435
20563
  } catch {
19436
20564
  }
@@ -19645,11 +20773,11 @@ async function runSingleToolCall(call, assistant, options, signal, nested) {
19645
20773
  await emitEnd(call, result, emit);
19646
20774
  return result;
19647
20775
  }
19648
- 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;
20776
+ 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;
19649
20777
  var init_tool_runner = __esm({
19650
20778
  "src/agent/tool-runner.ts"() {
19651
20779
  "use strict";
19652
- import_node_fs27 = require("node:fs");
20780
+ import_node_fs28 = require("node:fs");
19653
20781
  import_node_path35 = require("node:path");
19654
20782
  init_loop_guard();
19655
20783
  init_schema2();
@@ -20476,7 +21604,7 @@ var init_classifier = __esm({
20476
21604
  "- it stays inside the project directory;",
20477
21605
  "- it is read-only, or a routine and easily reversible development step (build, test, lint, format, generate code, run a project script);",
20478
21606
  "- it does not read or change secrets, credentials, keys or VCS internals;",
20479
- "- it does not use the network, install software, publish, deploy, push, or delete data the user may need;",
21607
+ "- it does not use the network, install software, publish, deploy, push, or delete data the user may need (a web_fetch of a public documentation or package-registry page is routine; answer ask when its query string looks like a key or a long random token);",
20480
21608
  "- it plausibly serves the user's latest request.",
20481
21609
  'Otherwise answer "ask". When unsure, answer "ask".',
20482
21610
  "",
@@ -20513,13 +21641,13 @@ var init_classifier = __esm({
20513
21641
  return verdict;
20514
21642
  }
20515
21643
  async run(request, signal) {
20516
- const fail6 = (reason) => ({
21644
+ const fail7 = (reason) => ({
20517
21645
  decision: "ask",
20518
21646
  reason,
20519
21647
  cached: false,
20520
21648
  failed: true
20521
21649
  });
20522
- if (signal.aborted) return fail6("classifier aborted");
21650
+ if (signal.aborted) return fail7("classifier aborted");
20523
21651
  const controller = new AbortController();
20524
21652
  const timeoutMs = this.options.timeoutMs ?? CLASSIFIER_TIMEOUT_MS;
20525
21653
  let timer;
@@ -20534,20 +21662,20 @@ var init_classifier = __esm({
20534
21662
  try {
20535
21663
  const prompt = { system: CLASSIFIER_SYSTEM_PROMPT, user: buildClassifierPrompt(request) };
20536
21664
  const answer = await Promise.race([this.complete(prompt, controller.signal), timedOut]);
20537
- if (answer === "timeout") return fail6(`classifier timed out after ${timeoutMs} ms`);
21665
+ if (answer === "timeout") return fail7(`classifier timed out after ${timeoutMs} ms`);
20538
21666
  const parsed = parseClassifierReply(answer);
20539
21667
  if (parsed === void 0) {
20540
21668
  this.options.log?.(
20541
21669
  "warn",
20542
21670
  `permission classifier returned invalid output: ${truncate(answer, 200)}`
20543
21671
  );
20544
- return fail6("classifier returned invalid output");
21672
+ return fail7("classifier returned invalid output");
20545
21673
  }
20546
21674
  return { ...parsed, cached: false };
20547
21675
  } catch (error2) {
20548
21676
  const message = error2 instanceof Error ? error2.message : String(error2);
20549
21677
  this.options.log?.("warn", `permission classifier failed: ${message}`);
20550
- return fail6(
21678
+ return fail7(
20551
21679
  signal.aborted ? "classifier aborted" : `classifier failed: ${truncate(message, 120)}`
20552
21680
  );
20553
21681
  } finally {
@@ -21717,9 +22845,9 @@ function materialize(candidate, outputDir) {
21717
22845
  if (outputDir !== void 0) {
21718
22846
  fullTextPath = (0, import_node_path37.join)(outputDir, `${safeFileStem(message.toolCallId)}.txt`);
21719
22847
  try {
21720
- if (!(0, import_node_fs28.existsSync)(fullTextPath)) {
21721
- (0, import_node_fs28.mkdirSync)(outputDir, { recursive: true });
21722
- (0, import_node_fs28.writeFileSync)(fullTextPath, text, "utf8");
22848
+ if (!(0, import_node_fs29.existsSync)(fullTextPath)) {
22849
+ (0, import_node_fs29.mkdirSync)(outputDir, { recursive: true });
22850
+ (0, import_node_fs29.writeFileSync)(fullTextPath, text, "utf8");
21723
22851
  }
21724
22852
  } catch {
21725
22853
  fullTextPath = void 0;
@@ -21754,11 +22882,11 @@ function planPrune(items, options) {
21754
22882
  }
21755
22883
  return { items: plan2, savedTokens, availableTokens };
21756
22884
  }
21757
- 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;
22885
+ 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;
21758
22886
  var init_prune_tier = __esm({
21759
22887
  "src/compaction/prune-tier.ts"() {
21760
22888
  "use strict";
21761
- import_node_fs28 = require("node:fs");
22889
+ import_node_fs29 = require("node:fs");
21762
22890
  import_node_path37 = require("node:path");
21763
22891
  init_estimate();
21764
22892
  PRUNE_TRIGGER_RATIO = 0.7;
@@ -22217,7 +23345,7 @@ function isTaskHead(entry) {
22217
23345
  }
22218
23346
  function readHead(file) {
22219
23347
  try {
22220
- const [header, first] = (0, import_node_fs29.readFileSync)(file, "utf8").split("\n", 2);
23348
+ const [header, first] = (0, import_node_fs30.readFileSync)(file, "utf8").split("\n", 2);
22221
23349
  const parsed = JSON.parse(header ?? "{}");
22222
23350
  const entry = first ? JSON.parse(first) : void 0;
22223
23351
  return { ...parsed, task: isTaskHead(entry) };
@@ -22242,11 +23370,11 @@ function cacheKeyOf(manager) {
22242
23370
  }
22243
23371
  return id;
22244
23372
  }
22245
- var import_node_fs29, TASK_CUSTOM_TYPE;
23373
+ var import_node_fs30, TASK_CUSTOM_TYPE;
22246
23374
  var init_session_cache_key = __esm({
22247
23375
  "src/agent/session-cache-key.ts"() {
22248
23376
  "use strict";
22249
- import_node_fs29 = require("node:fs");
23377
+ import_node_fs30 = require("node:fs");
22250
23378
  TASK_CUSTOM_TYPE = "ama.task";
22251
23379
  }
22252
23380
  });
@@ -23018,7 +24146,7 @@ var init_session_compaction = __esm({
23018
24146
  this.compacting = false;
23019
24147
  return outcome;
23020
24148
  };
23021
- const fail6 = (error2, aborted = false) => end({ result: void 0, tokensAfter: void 0, error: error2, aborted }, false);
24149
+ const fail7 = (error2, aborted = false) => end({ result: void 0, tokensAfter: void 0, error: error2, aborted }, false);
23022
24150
  const tokensBefore = this.estimate().tokens;
23023
24151
  let customInstructions = instructions;
23024
24152
  const hook = await core.runHook(
@@ -23028,13 +24156,13 @@ var init_session_compaction = __esm({
23028
24156
  );
23029
24157
  if (hook !== void 0) {
23030
24158
  if (hook.decision === "block" || hook.decision === "deny") {
23031
- return fail6(hook.reason ?? "compaction blocked by PreCompact hook", true);
24159
+ return fail7(hook.reason ?? "compaction blocked by PreCompact hook", true);
23032
24160
  }
23033
24161
  if (hook.customInstructions !== void 0) {
23034
24162
  customInstructions = [customInstructions, hook.customInstructions].filter(Boolean).join("\n\n");
23035
24163
  }
23036
24164
  }
23037
- if (signal.aborted) return fail6("aborted", true);
24165
+ if (signal.aborted) return fail7("aborted", true);
23038
24166
  const projection = buildProjection(core.manager.branch());
23039
24167
  const keep = this.keepRecent();
23040
24168
  let plan2;
@@ -23045,7 +24173,7 @@ var init_session_compaction = __esm({
23045
24173
  if (plan2 !== void 0) break;
23046
24174
  }
23047
24175
  }
23048
- if (plan2 === void 0) return fail6("nothing to compact");
24176
+ if (plan2 === void 0) return fail7("nothing to compact");
23049
24177
  try {
23050
24178
  const options = await this.summarizer(
23051
24179
  signal,
@@ -23053,7 +24181,7 @@ var init_session_compaction = __esm({
23053
24181
  trigger === "overflow" ? false : continuation ?? true
23054
24182
  );
23055
24183
  const draft = await runCompaction(plan2, options);
23056
- if (signal.aborted) return fail6("aborted", true);
24184
+ if (signal.aborted) return fail7("aborted", true);
23057
24185
  const entry = {
23058
24186
  type: "compaction",
23059
24187
  summary: `${draft.summary}
@@ -23088,7 +24216,7 @@ ${this.postCompactBlock()}`,
23088
24216
  const aborted = signal.aborted || error2 instanceof AmaError && error2.code === "aborted";
23089
24217
  if (!aborted) this.breaker.recordSummary(false);
23090
24218
  if (this.breaker.tripped) this.warnBlocked();
23091
- return fail6(error2 instanceof Error ? error2.message : String(error2), aborted);
24219
+ return fail7(error2 instanceof Error ? error2.message : String(error2), aborted);
23092
24220
  }
23093
24221
  }
23094
24222
  };
@@ -24789,12 +25917,14 @@ function planDecision(input2, planBash, cwd, projectRoot = cwd) {
24789
25917
  }
24790
25918
  function auditSummary(toolName, input2) {
24791
25919
  const command3 = inputCommand(input2);
24792
- const raw = command3 ?? (typeof input2 === "object" && input2 !== null ? input2["path"] : void 0);
25920
+ const raw = command3 ?? (typeof input2 === "object" && input2 !== null ? input2["path"] ?? input2["url"] : void 0);
24793
25921
  const text = typeof raw === "string" ? raw.replace(/\s+/g, " ").trim() : toolName;
24794
25922
  return text.length > 80 ? `${text.slice(0, 79)}…` : text;
24795
25923
  }
24796
25924
  function sessionGrantFor(toolName, input2, cwd) {
24797
25925
  if (toolName === "memory") return { toolName, kind: "any", prefix: "" };
25926
+ const host = networkGrantHost(input2);
25927
+ if (host !== void 0) return { toolName, kind: "host", prefix: host };
24798
25928
  const command3 = inputCommand(input2);
24799
25929
  if (command3 !== void 0) {
24800
25930
  const words = normalizeCommand(command3).split(" ").slice(0, 2);
@@ -24807,6 +25937,7 @@ function sessionGrantFor(toolName, input2, cwd) {
24807
25937
  function grantCovers(grant, toolName, input2, cwd) {
24808
25938
  if (grant.toolName !== toolName) return false;
24809
25939
  if (grant.kind === "any") return true;
25940
+ if (grant.kind === "host") return networkGrantHost(input2) === grant.prefix;
24810
25941
  if (grant.kind === "command") {
24811
25942
  const command3 = inputCommand(input2);
24812
25943
  if (command3 === void 0 || /\$\(|`/.test(command3)) return false;
@@ -24823,6 +25954,7 @@ var init_pipeline = __esm({
24823
25954
  import_node_path40 = require("node:path");
24824
25955
  init_background_jobs();
24825
25956
  init_memory_class();
25957
+ init_network_class();
24826
25958
  init_rules();
24827
25959
  init_dangerous();
24828
25960
  init_auto_safe();
@@ -24904,7 +26036,8 @@ var init_pipeline = __esm({
24904
26036
  return verdict;
24905
26037
  }
24906
26038
  evaluate(raw) {
24907
- const input2 = raw.permission === "memory" ? { ...raw, permission: memoryEffectivePermission(raw.input) } : raw;
26039
+ const network = raw.permission === "network";
26040
+ const input2 = raw.permission === "memory" ? { ...raw, permission: memoryEffectivePermission(raw.input) } : network ? { ...raw, permission: networkEffectivePermission() } : raw;
24908
26041
  const { toolName, permission } = input2;
24909
26042
  const layers = layeredInputs(toolName, input2.input);
24910
26043
  let deny;
@@ -24945,9 +26078,9 @@ var init_pipeline = __esm({
24945
26078
  }
24946
26079
  return verdict2;
24947
26080
  }
24948
- if (this.currentMode === "auto") return this.evaluateAuto(input2, layers, command3);
26081
+ if (this.currentMode === "auto") return this.evaluateAuto(input2, layers, command3, network);
24949
26082
  if (this.currentMode === "allowlist") return this.evaluateAllowlist(input2, layers);
24950
- const plan2 = this.currentMode === "plan" ? planDecision(input2, this.planBashMode, this.cwd, this.projectRoot) : void 0;
26083
+ const plan2 = this.currentMode !== "plan" ? void 0 : network ? { decision: "ask" } : planDecision(input2, this.planBashMode, this.cwd, this.projectRoot);
24951
26084
  const byMode = plan2?.decision ?? modeDecision(this.currentMode, permission);
24952
26085
  if (byMode === "deny") {
24953
26086
  return {
@@ -25034,12 +26167,17 @@ var init_pipeline = __esm({
25034
26167
  }
25035
26168
  return {};
25036
26169
  }
25037
- evaluateAuto(input2, layers, command3) {
26170
+ evaluateAuto(input2, layers, command3, network = false) {
25038
26171
  const rule = (decision, reason) => ({
25039
26172
  layer: "rule",
25040
26173
  decision,
25041
26174
  reason
25042
26175
  });
26176
+ const blocked3 = network ? networkBlockReason(input2.input) : void 0;
26177
+ if (blocked3 !== void 0) {
26178
+ const message = `Auto mode refuses this fetch: ${blocked3}`;
26179
+ return { decision: "deny", step: "auto-rule", message, auto: rule("deny", blocked3) };
26180
+ }
25043
26181
  const checked = this.autoRuleReason(input2, command3);
25044
26182
  if (checked.ask !== void 0) {
25045
26183
  return {
@@ -25208,7 +26346,7 @@ function listSessionItems(dir) {
25208
26346
  file,
25209
26347
  cwd: header.cwd,
25210
26348
  createdAt: header.timestamp,
25211
- modifiedAt: (0, import_node_fs30.statSync)(file).mtime.toISOString(),
26349
+ modifiedAt: (0, import_node_fs31.statSync)(file).mtime.toISOString(),
25212
26350
  messageCount
25213
26351
  };
25214
26352
  if (name !== void 0) item.name = name;
@@ -25220,11 +26358,11 @@ function listSessionItems(dir) {
25220
26358
  }
25221
26359
  return items;
25222
26360
  }
25223
- var import_node_fs30, SkipFile;
26361
+ var import_node_fs31, SkipFile;
25224
26362
  var init_list = __esm({
25225
26363
  "src/session/list.ts"() {
25226
26364
  "use strict";
25227
- import_node_fs30 = require("node:fs");
26365
+ import_node_fs31 = require("node:fs");
25228
26366
  init_line_reader();
25229
26367
  init_store();
25230
26368
  init_types6();
@@ -25246,18 +26384,18 @@ function hasImages(entry) {
25246
26384
  return contentOf(entry)?.some(isImage) === true;
25247
26385
  }
25248
26386
  function readLineAt(file, locator) {
25249
- const fd = (0, import_node_fs31.openSync)(file, "r");
26387
+ const fd = (0, import_node_fs32.openSync)(file, "r");
25250
26388
  try {
25251
26389
  const buf = Buffer.allocUnsafe(locator.length);
25252
26390
  let read2 = 0;
25253
26391
  while (read2 < locator.length) {
25254
- const n = (0, import_node_fs31.readSync)(fd, buf, read2, locator.length - read2, locator.offset + read2);
26392
+ const n = (0, import_node_fs32.readSync)(fd, buf, read2, locator.length - read2, locator.offset + read2);
25255
26393
  if (n === 0) throw new Error("unexpected end of file");
25256
26394
  read2 += n;
25257
26395
  }
25258
26396
  return JSON.parse(buf.toString("utf8"));
25259
26397
  } finally {
25260
- (0, import_node_fs31.closeSync)(fd);
26398
+ (0, import_node_fs32.closeSync)(fd);
25261
26399
  }
25262
26400
  }
25263
26401
  function editTargets(file) {
@@ -25275,11 +26413,11 @@ function editTargets(file) {
25275
26413
  }
25276
26414
  return targets;
25277
26415
  }
25278
- var import_node_fs31, placeholder, PENDING_WARNINGS, ImageOffload;
26416
+ var import_node_fs32, placeholder, PENDING_WARNINGS, ImageOffload;
25279
26417
  var init_offload = __esm({
25280
26418
  "src/session/offload.ts"() {
25281
26419
  "use strict";
25282
- import_node_fs31 = require("node:fs");
26420
+ import_node_fs32 = require("node:fs");
25283
26421
  init_image_intern();
25284
26422
  init_line_reader();
25285
26423
  init_projection();
@@ -26047,7 +27185,7 @@ var init_task_record = __esm({
26047
27185
  // src/agent/worktree.ts
26048
27186
  function realPath(path) {
26049
27187
  try {
26050
- return import_node_fs32.realpathSync.native(path);
27188
+ return import_node_fs33.realpathSync.native(path);
26051
27189
  } catch {
26052
27190
  return path;
26053
27191
  }
@@ -26100,17 +27238,17 @@ async function createWorktree(cwd, taskId, git = runGit) {
26100
27238
  }
26101
27239
  const base4 = (await git(["rev-parse", "HEAD"], repo)).trim();
26102
27240
  const root = (0, import_node_path42.join)(repo, ".ama", "worktrees");
26103
- (0, import_node_fs32.mkdirSync)(root, { recursive: true });
27241
+ (0, import_node_fs33.mkdirSync)(root, { recursive: true });
26104
27242
  const ignore = (0, import_node_path42.join)(root, ".gitignore");
26105
- if (!(0, import_node_fs32.existsSync)(ignore)) (0, import_node_fs32.writeFileSync)(ignore, "*\n");
27243
+ if (!(0, import_node_fs33.existsSync)(ignore)) (0, import_node_fs33.writeFileSync)(ignore, "*\n");
26106
27244
  const path = (0, import_node_path42.join)(root, taskId);
26107
27245
  const branch = worktreeBranch(taskId);
26108
27246
  const key = await commonDir(repo, git);
26109
- if (!(0, import_node_fs32.existsSync)(path))
27247
+ if (!(0, import_node_fs33.existsSync)(path))
26110
27248
  await mutate(key, git, ["worktree", "add", "-b", branch, path, base4], repo);
26111
27249
  const sub = (0, import_node_path42.relative)(repo, realPath(cwd));
26112
27250
  const childCwd = sub === "" || sub.startsWith("..") ? path : (0, import_node_path42.join)(path, sub);
26113
- (0, import_node_fs32.mkdirSync)(childCwd, { recursive: true });
27251
+ (0, import_node_fs33.mkdirSync)(childCwd, { recursive: true });
26114
27252
  return { path, cwd: childCwd, branch, base: base4, repo };
26115
27253
  }
26116
27254
  async function finishWorktree(tree, git = runGit) {
@@ -26133,12 +27271,12 @@ async function finishWorktree(tree, git = runGit) {
26133
27271
  path: tree.path
26134
27272
  };
26135
27273
  }
26136
- var import_node_child_process6, import_node_fs32, import_node_path42, TIMEOUT_MS, runGit, queues, TRANSIENT;
27274
+ var import_node_child_process6, import_node_fs33, import_node_path42, TIMEOUT_MS, runGit, queues, TRANSIENT;
26137
27275
  var init_worktree = __esm({
26138
27276
  "src/agent/worktree.ts"() {
26139
27277
  "use strict";
26140
27278
  import_node_child_process6 = require("node:child_process");
26141
- import_node_fs32 = require("node:fs");
27279
+ import_node_fs33 = require("node:fs");
26142
27280
  import_node_path42 = require("node:path");
26143
27281
  TIMEOUT_MS = 3e4;
26144
27282
  runGit = (args, cwd) => new Promise((resolve41, reject2) => {
@@ -26224,7 +27362,7 @@ function createTelemetryExtension(deps = {}) {
26224
27362
  }
26225
27363
  }
26226
27364
  };
26227
- const wrap2 = (inner, tracker) => {
27365
+ const wrap3 = (inner, tracker) => {
26228
27366
  let iterating = false;
26229
27367
  let final;
26230
27368
  void inner.result().then(
@@ -26286,7 +27424,7 @@ function createTelemetryExtension(deps = {}) {
26286
27424
  if ((options.purpose ?? "turn") !== "turn") return inner;
26287
27425
  const tracker = new RequestTracker(now());
26288
27426
  current3 = tracker;
26289
- return wrap2(inner, tracker);
27427
+ return wrap3(inner, tracker);
26290
27428
  };
26291
27429
  },
26292
27430
  contributeStats(stats) {
@@ -26883,7 +28021,7 @@ function subagentRegistryFor(host, env = { catalog: new AgentCatalog() }) {
26883
28021
  function failed(text) {
26884
28022
  return { text, usage: { ...ZERO_USAGE }, stopReason: "error", isError: true, status: "failed" };
26885
28023
  }
26886
- var import_node_os9, import_node_path43, DEFAULT_SUBAGENT_CONCURRENCY, DEFAULT_MAX_PENDING, DEFAULT_RETAINED, registries, TERMINAL, SubagentRegistry;
28024
+ var import_node_os9, import_node_path43, DEFAULT_SUBAGENT_CONCURRENCY, DEFAULT_MAX_PENDING, DEFAULT_RETAINED, registries, TERMINAL2, SubagentRegistry;
26887
28025
  var init_subagent_registry = __esm({
26888
28026
  "src/agent/subagent-registry.ts"() {
26889
28027
  "use strict";
@@ -26904,7 +28042,7 @@ var init_subagent_registry = __esm({
26904
28042
  DEFAULT_MAX_PENDING = 16;
26905
28043
  DEFAULT_RETAINED = 4;
26906
28044
  registries = /* @__PURE__ */ new Map();
26907
- TERMINAL = [
28045
+ TERMINAL2 = [
26908
28046
  "completed",
26909
28047
  "failed",
26910
28048
  "aborted",
@@ -27192,7 +28330,7 @@ var init_subagent_registry = __esm({
27192
28330
  }
27193
28331
  async finish(record3, result, aborted = false) {
27194
28332
  flushText(record3, this.sink());
27195
- const status = aborted ? "aborted" : result.status !== void 0 && TERMINAL.includes(result.status) ? result.status : result.isError ? "failed" : "completed";
28333
+ const status = aborted ? "aborted" : result.status !== void 0 && TERMINAL2.includes(result.status) ? result.status : result.isError ? "failed" : "completed";
27196
28334
  if (record3.worktree !== void 0) {
27197
28335
  try {
27198
28336
  record3.worktreeOutcome = await finishWorktree(record3.worktree);
@@ -28054,7 +29192,7 @@ function countLines(text) {
28054
29192
  }
28055
29193
  function tryStat(path, follow = false) {
28056
29194
  try {
28057
- return (follow ? import_node_fs33.statSync : import_node_fs33.lstatSync)(path, { throwIfNoEntry: false });
29195
+ return (follow ? import_node_fs34.statSync : import_node_fs34.lstatSync)(path, { throwIfNoEntry: false });
28058
29196
  } catch {
28059
29197
  return void 0;
28060
29198
  }
@@ -28067,7 +29205,7 @@ function tally(root, c) {
28067
29205
  const dir = stack.pop();
28068
29206
  let list5;
28069
29207
  try {
28070
- list5 = (0, import_node_fs33.readdirSync)(dir, { withFileTypes: true });
29208
+ list5 = (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true });
28071
29209
  } catch {
28072
29210
  continue;
28073
29211
  }
@@ -28245,7 +29383,7 @@ function previewBash(command3, c, dangerous) {
28245
29383
  }
28246
29384
  }
28247
29385
  function readSmall(abs, info2) {
28248
- return info2.size > PREVIEW_MAX_READ_BYTES ? void 0 : (0, import_node_fs33.readFileSync)(abs, "utf8");
29386
+ return info2.size > PREVIEW_MAX_READ_BYTES ? void 0 : (0, import_node_fs34.readFileSync)(abs, "utf8");
28249
29387
  }
28250
29388
  function unread(abs, request, c, tool) {
28251
29389
  const readFiles = request.context?.readFiles;
@@ -28329,6 +29467,8 @@ function previewAction(request, options) {
28329
29467
  previewWrite(input2, request, c);
28330
29468
  } else if (kind === "edit" && typeof input2["path"] === "string") {
28331
29469
  previewEdit(input2, request, c);
29470
+ } else if (request.toolName === "web_fetch" && typeof input2["url"] === "string") {
29471
+ c.lines.push(`GET ${input2["url"].replace(/\s+/g, "").slice(0, 2048)}`);
28332
29472
  } else if (kind === "other") {
28333
29473
  const key = SUMMARY_KEYS.find((k) => typeof input2[k] === "string" && input2[k] !== "");
28334
29474
  if (key !== void 0)
@@ -28351,11 +29491,11 @@ function previewDisplayLines(preview, max = 10) {
28351
29491
  msg().permissions.preview.moreLines(preview.lines.length - max + 1)
28352
29492
  ];
28353
29493
  }
28354
- var import_node_fs33, import_node_path44, PREVIEW_MAX_ENTRIES, PREVIEW_BUDGET_MS, PREVIEW_MAX_READ_BYTES, PREVIEW_MAX_TARGETS, RANK, NULL_DEVICES, UNEXPANDED, Collector2, GIT_VALUE_OPTS, SUMMARY_KEYS;
29494
+ var import_node_fs34, import_node_path44, PREVIEW_MAX_ENTRIES, PREVIEW_BUDGET_MS, PREVIEW_MAX_READ_BYTES, PREVIEW_MAX_TARGETS, RANK, NULL_DEVICES, UNEXPANDED, Collector2, GIT_VALUE_OPTS, SUMMARY_KEYS;
28355
29495
  var init_preview = __esm({
28356
29496
  "src/permissions/preview.ts"() {
28357
29497
  "use strict";
28358
- import_node_fs33 = require("node:fs");
29498
+ import_node_fs34 = require("node:fs");
28359
29499
  import_node_path44 = require("node:path");
28360
29500
  init_edit();
28361
29501
  init_edit_fuzzy();
@@ -29559,7 +30699,7 @@ function hashBytes(bytes) {
29559
30699
  function hashFile(path) {
29560
30700
  return new Promise((resolvePromise, reject2) => {
29561
30701
  const hash = (0, import_node_crypto9.createHash)("sha256");
29562
- const stream5 = (0, import_node_fs34.createReadStream)(path);
30702
+ const stream5 = (0, import_node_fs35.createReadStream)(path);
29563
30703
  stream5.on("error", reject2);
29564
30704
  stream5.on("data", (chunk) => hash.update(chunk));
29565
30705
  stream5.on("end", () => resolvePromise(hash.digest("hex")));
@@ -29690,12 +30830,12 @@ async function exists3(path) {
29690
30830
  return false;
29691
30831
  }
29692
30832
  }
29693
- var import_node_crypto9, import_node_fs34, import_promises12, import_node_path47, FILE_HISTORY_DIR, BLOBS_DIR, DEFAULT_MAX_FILE_BYTES, SHA256_HEX, tmpCounter;
30833
+ var import_node_crypto9, import_node_fs35, import_promises12, import_node_path47, FILE_HISTORY_DIR, BLOBS_DIR, DEFAULT_MAX_FILE_BYTES, SHA256_HEX, tmpCounter;
29694
30834
  var init_blobs = __esm({
29695
30835
  "src/checkpoints/blobs.ts"() {
29696
30836
  "use strict";
29697
30837
  import_node_crypto9 = require("node:crypto");
29698
- import_node_fs34 = require("node:fs");
30838
+ import_node_fs35 = require("node:fs");
29699
30839
  import_promises12 = require("node:fs/promises");
29700
30840
  import_node_path47 = require("node:path");
29701
30841
  FILE_HISTORY_DIR = "file-history";
@@ -30152,7 +31292,7 @@ async function writeBack(abs, bytes, record3, before2) {
30152
31292
  if (record3.realParentDir !== void 0 && await (0, import_promises15.realpath)(parent) !== record3.realParentDir) {
30153
31293
  throw new Skip("parent_moved");
30154
31294
  }
30155
- const flags2 = import_node_fs35.constants.O_WRONLY | import_node_fs35.constants.O_CREAT | import_node_fs35.constants.O_EXCL | NOFOLLOW;
31295
+ const flags2 = import_node_fs36.constants.O_WRONLY | import_node_fs36.constants.O_CREAT | import_node_fs36.constants.O_EXCL | NOFOLLOW;
30156
31296
  const handle2 = await (0, import_promises15.open)(abs, flags2, record3.mode ?? 420);
30157
31297
  try {
30158
31298
  await handle2.writeFile(bytes);
@@ -30164,7 +31304,7 @@ async function writeBack(abs, bytes, record3, before2) {
30164
31304
  }
30165
31305
  let handle;
30166
31306
  try {
30167
- handle = await (0, import_promises15.open)(abs, import_node_fs35.constants.O_WRONLY | NOFOLLOW);
31307
+ handle = await (0, import_promises15.open)(abs, import_node_fs36.constants.O_WRONLY | NOFOLLOW);
30168
31308
  } catch (error2) {
30169
31309
  const code = error2.code;
30170
31310
  if (code === "ELOOP" || code === "EMLINK") throw new Skip("symlink");
@@ -30190,11 +31330,11 @@ async function gitHintFor(cwd, target) {
30190
31330
  if (current3 === void 0 || current3.head === target.git.head) return void 0;
30191
31331
  return { recordedHead: target.git.head, currentHead: current3.head };
30192
31332
  }
30193
- var import_node_fs35, import_promises15, import_node_path50, RestoreSkip, Skip, IS_WINDOWS, NOFOLLOW;
31333
+ var import_node_fs36, import_promises15, import_node_path50, RestoreSkip, Skip, IS_WINDOWS, NOFOLLOW;
30194
31334
  var init_restore = __esm({
30195
31335
  "src/checkpoints/restore.ts"() {
30196
31336
  "use strict";
30197
- import_node_fs35 = require("node:fs");
31337
+ import_node_fs36 = require("node:fs");
30198
31338
  import_promises15 = require("node:fs/promises");
30199
31339
  import_node_path50 = require("node:path");
30200
31340
  init_edit();
@@ -30211,7 +31351,7 @@ var init_restore = __esm({
30211
31351
  };
30212
31352
  Skip = RestoreSkip;
30213
31353
  IS_WINDOWS = process.platform === "win32";
30214
- NOFOLLOW = IS_WINDOWS ? 0 : import_node_fs35.constants.O_NOFOLLOW ?? 0;
31354
+ NOFOLLOW = IS_WINDOWS ? 0 : import_node_fs36.constants.O_NOFOLLOW ?? 0;
30215
31355
  }
30216
31356
  });
30217
31357
 
@@ -31444,10 +32584,10 @@ var init_templates = __esm({
31444
32584
 
31445
32585
  // src/cli/compose-store.ts
31446
32586
  function cwdDirs(root) {
31447
- if (!(0, import_node_fs36.existsSync)(root)) return [];
31448
- return (0, import_node_fs36.readdirSync)(root).filter((name) => name !== TRASH_DIR_NAME).map((name) => (0, import_node_path54.join)(root, name)).filter((dir) => {
32587
+ if (!(0, import_node_fs37.existsSync)(root)) return [];
32588
+ return (0, import_node_fs37.readdirSync)(root).filter((name) => name !== TRASH_DIR_NAME).map((name) => (0, import_node_path54.join)(root, name)).filter((dir) => {
31449
32589
  try {
31450
- return (0, import_node_fs36.statSync)(dir).isDirectory();
32590
+ return (0, import_node_fs37.statSync)(dir).isDirectory();
31451
32591
  } catch {
31452
32592
  return false;
31453
32593
  }
@@ -31539,11 +32679,11 @@ function createSessionStore(log) {
31539
32679
  }
31540
32680
  };
31541
32681
  }
31542
- var import_node_fs36, import_node_path54;
32682
+ var import_node_fs37, import_node_path54;
31543
32683
  var init_compose_store = __esm({
31544
32684
  "src/cli/compose-store.ts"() {
31545
32685
  "use strict";
31546
- import_node_fs36 = require("node:fs");
32686
+ import_node_fs37 = require("node:fs");
31547
32687
  import_node_path54 = require("node:path");
31548
32688
  init_errors();
31549
32689
  init_manager();
@@ -31838,7 +32978,7 @@ function remindersSettings(config) {
31838
32978
  }
31839
32979
  function defaultStat(path) {
31840
32980
  try {
31841
- const info2 = (0, import_node_fs37.statSync)(path);
32981
+ const info2 = (0, import_node_fs38.statSync)(path);
31842
32982
  return { mtimeMs: info2.mtimeMs, size: info2.size };
31843
32983
  } catch {
31844
32984
  return void 0;
@@ -31866,7 +33006,7 @@ function lastTodo(branch) {
31866
33006
  }
31867
33007
  return parseTodoState(void 0);
31868
33008
  }
31869
- function wrap(lines2) {
33009
+ function wrap2(lines2) {
31870
33010
  return `<system-reminder>
31871
33011
  ${lines2.join("\n\n")}
31872
33012
  </system-reminder>`;
@@ -31919,12 +33059,12 @@ function createRemindersExtension(core, settings = {}, deps = {}) {
31919
33059
  dispose
31920
33060
  };
31921
33061
  }
31922
- var import_node_child_process8, import_node_fs37, import_node_path55, REMINDER_CUSTOM_TYPE, DEFAULT_TODO_REMINDER_TURNS, CONTEXT_REMINDER_RATIOS, BUDGET_REMINDER_RATIO, MAX_CHANGED_FILES, FILE_TOOLS, Reminders;
33062
+ var import_node_child_process8, import_node_fs38, import_node_path55, REMINDER_CUSTOM_TYPE, DEFAULT_TODO_REMINDER_TURNS, CONTEXT_REMINDER_RATIOS, BUDGET_REMINDER_RATIO, MAX_CHANGED_FILES, FILE_TOOLS, Reminders;
31923
33063
  var init_reminders = __esm({
31924
33064
  "src/agent/reminders.ts"() {
31925
33065
  "use strict";
31926
33066
  import_node_child_process8 = require("node:child_process");
31927
- import_node_fs37 = require("node:fs");
33067
+ import_node_fs38 = require("node:fs");
31928
33068
  import_node_path55 = require("node:path");
31929
33069
  init_estimate();
31930
33070
  init_projection();
@@ -32017,7 +33157,7 @@ var init_reminders = __esm({
32017
33157
  const budget = inRun ? this.budgetLine() : void 0;
32018
33158
  for (const line2 of [jobs, todo, files, context, budget])
32019
33159
  if (line2 !== void 0) lines2.push(line2);
32020
- return lines2.length === 0 ? void 0 : wrap(lines2);
33160
+ return lines2.length === 0 ? void 0 : wrap2(lines2);
32021
33161
  }
32022
33162
  jobLines() {
32023
33163
  const exited = jobsForSession(this.core.manager.id).takeExited();
@@ -32484,17 +33624,17 @@ function resolvePlanDirectory(configured2, projectRoot, dataDir) {
32484
33624
  return { dir };
32485
33625
  }
32486
33626
  function writePlanFile(dir, sessionId, version, markdown) {
32487
- (0, import_node_fs38.mkdirSync)(dir, { recursive: true });
33627
+ (0, import_node_fs39.mkdirSync)(dir, { recursive: true });
32488
33628
  const file = (0, import_node_path56.join)(dir, `${sessionId}-v${version}.md`);
32489
- (0, import_node_fs38.writeFileSync)(file, markdown.endsWith("\n") ? markdown : `${markdown}
33629
+ (0, import_node_fs39.writeFileSync)(file, markdown.endsWith("\n") ? markdown : `${markdown}
32490
33630
  `, "utf8");
32491
33631
  return file;
32492
33632
  }
32493
- var import_node_fs38, import_node_path56, PLAN_CUSTOM_TYPE2, PLAN_STATE_CUSTOM_TYPE;
33633
+ var import_node_fs39, import_node_path56, PLAN_CUSTOM_TYPE2, PLAN_STATE_CUSTOM_TYPE;
32494
33634
  var init_store3 = __esm({
32495
33635
  "src/plan/store.ts"() {
32496
33636
  "use strict";
32497
- import_node_fs38 = require("node:fs");
33637
+ import_node_fs39 = require("node:fs");
32498
33638
  import_node_path56 = require("node:path");
32499
33639
  init_todo();
32500
33640
  init_i18n();
@@ -33030,7 +34170,7 @@ function permissionModeOf(value) {
33030
34170
  }
33031
34171
  function parseAgentDefinition(text, filePath, source) {
33032
34172
  const warnings = [];
33033
- const fail6 = (message) => {
34173
+ const fail7 = (message) => {
33034
34174
  warnings.push(`${filePath}: ${message}`);
33035
34175
  return { warnings };
33036
34176
  };
@@ -33040,42 +34180,42 @@ function parseAgentDefinition(text, filePath, source) {
33040
34180
  const rawName = data2["name"];
33041
34181
  const name = typeof rawName === "string" && rawName !== "" ? rawName : (0, import_node_path57.basename)(filePath, ".md");
33042
34182
  if (!AGENT_NAME_RE.test(name))
33043
- return fail6(`invalid agent name "${name}" (lowercase letters, digits, -; at most 64)`);
34183
+ return fail7(`invalid agent name "${name}" (lowercase letters, digits, -; at most 64)`);
33044
34184
  const description = data2["description"];
33045
34185
  if (typeof description !== "string" || description.trim() === "")
33046
- return fail6(`missing description; agent "${name}" not loaded`);
34186
+ return fail7(`missing description; agent "${name}" not loaded`);
33047
34187
  if (description.length > MAX_AGENT_DESCRIPTION)
33048
- return fail6(`description longer than ${MAX_AGENT_DESCRIPTION} characters`);
34188
+ return fail7(`description longer than ${MAX_AGENT_DESCRIPTION} characters`);
33049
34189
  const runnerRaw = data2["runner"] ?? "ama";
33050
34190
  const runner = String(runnerRaw).trim();
33051
34191
  if (!isRunner(runner))
33052
- return fail6(`unknown runner "${runner}" (ama | claude | codex | acp:<program>)`);
34192
+ return fail7(`unknown runner "${runner}" (ama | claude | codex | acp:<program>)`);
33053
34193
  const tools = nameList(data2["tools"]);
33054
34194
  const disallowedTools = nameList(data2["disallowed-tools"]);
33055
34195
  if (tools !== void 0 && disallowedTools !== void 0)
33056
- return fail6("tools and disallowed-tools are mutually exclusive");
34196
+ return fail7("tools and disallowed-tools are mutually exclusive");
33057
34197
  const permissionMode = permissionModeOf(data2["permission-mode"]);
33058
34198
  if (permissionMode === void 0)
33059
- return fail6(
34199
+ return fail7(
33060
34200
  `permission-mode must be plan or inherit (got "${String(data2["permission-mode"])}")`
33061
34201
  );
33062
34202
  const model = data2["model"] === void 0 || data2["model"] === null ? "inherit" : data2["model"];
33063
- if (typeof model !== "string" || model.trim() === "") return fail6("model must be a string");
34203
+ if (typeof model !== "string" || model.trim() === "") return fail7("model must be a string");
33064
34204
  const thinking = data2["thinking"];
33065
34205
  if (thinking !== void 0 && thinking !== null && !THINKING2.includes(thinking))
33066
- return fail6(`thinking must be one of ${THINKING2.join(", ")}`);
34206
+ return fail7(`thinking must be one of ${THINKING2.join(", ")}`);
33067
34207
  const maxTurns = data2["max-turns"] ?? DEFAULT_AGENT_MAX_TURNS;
33068
34208
  if (typeof maxTurns !== "number" || !Number.isInteger(maxTurns) || maxTurns < 1)
33069
- return fail6("max-turns must be an integer ≥ 1");
34209
+ return fail7("max-turns must be an integer ≥ 1");
33070
34210
  const isolation = data2["isolation"] ?? "none";
33071
34211
  if (isolation !== "none" && isolation !== "worktree")
33072
- return fail6("isolation must be none or worktree");
34212
+ return fail7("isolation must be none or worktree");
33073
34213
  const background = data2["background"];
33074
34214
  if (background !== void 0 && typeof background !== "boolean")
33075
- return fail6("background must be true or false");
34215
+ return fail7("background must be true or false");
33076
34216
  const context = data2["context"];
33077
34217
  if (context !== void 0 && context !== null && context !== "fork" && context !== "fresh")
33078
- return fail6("context must be fork or fresh");
34218
+ return fail7("context must be fork or fresh");
33079
34219
  if (runner !== "ama") {
33080
34220
  const ignored = ["tools", "disallowed-tools", "permission-mode", "context"].filter(
33081
34221
  (key) => data2[key] !== void 0
@@ -33136,14 +34276,14 @@ function agentSources(options) {
33136
34276
  }
33137
34277
  function isDir2(path) {
33138
34278
  try {
33139
- return (0, import_node_fs39.statSync)(path).isDirectory();
34279
+ return (0, import_node_fs40.statSync)(path).isDirectory();
33140
34280
  } catch {
33141
34281
  return false;
33142
34282
  }
33143
34283
  }
33144
34284
  function markdownFiles(dir) {
33145
34285
  try {
33146
- return (0, import_node_fs39.readdirSync)(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort().map((name) => (0, import_node_path58.join)(dir, name));
34286
+ return (0, import_node_fs40.readdirSync)(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort().map((name) => (0, import_node_path58.join)(dir, name));
33147
34287
  } catch {
33148
34288
  return [];
33149
34289
  }
@@ -33163,7 +34303,7 @@ function discoverAgents(sources, options) {
33163
34303
  for (const file of markdownFiles(source.dir)) {
33164
34304
  let text;
33165
34305
  try {
33166
- text = (0, import_node_fs39.readFileSync)(file, "utf8");
34306
+ text = (0, import_node_fs40.readFileSync)(file, "utf8");
33167
34307
  } catch (error2) {
33168
34308
  result.warnings.push(`${file}: ${error2.message}`);
33169
34309
  continue;
@@ -33185,11 +34325,11 @@ function discoverAgents(sources, options) {
33185
34325
  }
33186
34326
  return result;
33187
34327
  }
33188
- var import_node_fs39, import_node_path58;
34328
+ var import_node_fs40, import_node_path58;
33189
34329
  var init_discover = __esm({
33190
34330
  "src/agents/discover.ts"() {
33191
34331
  "use strict";
33192
- import_node_fs39 = require("node:fs");
34332
+ import_node_fs40 = require("node:fs");
33193
34333
  import_node_path58 = require("node:path");
33194
34334
  init_parse();
33195
34335
  }
@@ -33527,7 +34667,7 @@ var init_process = __esm({
33527
34667
  // src/drivers/probe.ts
33528
34668
  function defaultIsFile(path) {
33529
34669
  try {
33530
- return (0, import_node_fs40.statSync)(path).isFile();
34670
+ return (0, import_node_fs41.statSync)(path).isFile();
33531
34671
  } catch {
33532
34672
  return false;
33533
34673
  }
@@ -33589,12 +34729,12 @@ function defaultRunVersion(env, platform) {
33589
34729
  );
33590
34730
  });
33591
34731
  }
33592
- var import_node_child_process10, import_node_fs40, import_node_path59, VERSION_TIMEOUT_MS, ProgramProbe;
34732
+ var import_node_child_process10, import_node_fs41, import_node_path59, VERSION_TIMEOUT_MS, ProgramProbe;
33593
34733
  var init_probe = __esm({
33594
34734
  "src/drivers/probe.ts"() {
33595
34735
  "use strict";
33596
34736
  import_node_child_process10 = require("node:child_process");
33597
- import_node_fs40 = require("node:fs");
34737
+ import_node_fs41 = require("node:fs");
33598
34738
  import_node_path59 = require("node:path");
33599
34739
  init_process();
33600
34740
  VERSION_TIMEOUT_MS = 5e3;
@@ -33647,16 +34787,16 @@ var init_probe = __esm({
33647
34787
  }
33648
34788
  mtime(path) {
33649
34789
  try {
33650
- return (0, import_node_fs40.statSync)(path).mtimeMs;
34790
+ return (0, import_node_fs41.statSync)(path).mtimeMs;
33651
34791
  } catch {
33652
34792
  return 0;
33653
34793
  }
33654
34794
  }
33655
34795
  readCache() {
33656
34796
  const file = this.deps.cacheFile;
33657
- if (file === void 0 || !(0, import_node_fs40.existsSync)(file)) return {};
34797
+ if (file === void 0 || !(0, import_node_fs41.existsSync)(file)) return {};
33658
34798
  try {
33659
- const parsed = JSON.parse((0, import_node_fs40.readFileSync)(file, "utf8"));
34799
+ const parsed = JSON.parse((0, import_node_fs41.readFileSync)(file, "utf8"));
33660
34800
  return parsed.programs ?? {};
33661
34801
  } catch {
33662
34802
  return {};
@@ -33667,8 +34807,8 @@ var init_probe = __esm({
33667
34807
  if (file === void 0) return;
33668
34808
  try {
33669
34809
  const programs = { ...this.readCache(), [path]: entry };
33670
- (0, import_node_fs40.mkdirSync)((0, import_node_path59.dirname)(file), { recursive: true });
33671
- (0, import_node_fs40.writeFileSync)(file, `${JSON.stringify({ version: 1, programs }, null, 2)}
34810
+ (0, import_node_fs41.mkdirSync)((0, import_node_path59.dirname)(file), { recursive: true });
34811
+ (0, import_node_fs41.writeFileSync)(file, `${JSON.stringify({ version: 1, programs }, null, 2)}
33672
34812
  `);
33673
34813
  } catch {
33674
34814
  }
@@ -34072,9 +35212,9 @@ var init_client = __esm({
34072
35212
  });
34073
35213
 
34074
35214
  // src/drivers/acp/driver.ts
34075
- function toAcpContent(blocks, images) {
35215
+ function toAcpContent(blocks2, images) {
34076
35216
  const out = [];
34077
- for (const block of blocks) {
35217
+ for (const block of blocks2) {
34078
35218
  if (block.type === "text") out.push({ type: "text", text: block.text });
34079
35219
  else if (images) out.push({ type: "image", data: block.data, mimeType: block.mimeType });
34080
35220
  }
@@ -34578,12 +35718,12 @@ function claudeArgs(options, sessionId, manualName = "manual") {
34578
35718
  return args;
34579
35719
  }
34580
35720
  function userLine(content) {
34581
- const blocks = content.map(
35721
+ const blocks2 = content.map(
34582
35722
  (b) => b.type === "text" ? { type: "text", text: b.text } : { type: "image", source: { type: "base64", media_type: b.mimeType, data: b.data } }
34583
35723
  );
34584
35724
  return `${JSON.stringify({
34585
35725
  type: "user",
34586
- message: { role: "user", content: blocks },
35726
+ message: { role: "user", content: blocks2 },
34587
35727
  parent_tool_use_id: null
34588
35728
  })}
34589
35729
  `;
@@ -35839,19 +36979,19 @@ ${item["text"]}`
35839
36979
 
35840
36980
  // src/drivers/native/pi-gate.ts
35841
36981
  function writePiGate() {
35842
- const dir = (0, import_node_fs41.mkdtempSync)((0, import_node_path60.join)((0, import_node_os11.tmpdir)(), "ama-pi-"));
36982
+ const dir = (0, import_node_fs42.mkdtempSync)((0, import_node_path60.join)((0, import_node_os11.tmpdir)(), "ama-pi-"));
35843
36983
  const path = (0, import_node_path60.join)(dir, "ama-approval-gate.ts");
35844
- (0, import_node_fs41.writeFileSync)(path, PI_GATE_SOURCE, { mode: 384 });
36984
+ (0, import_node_fs42.writeFileSync)(path, PI_GATE_SOURCE, { mode: 384 });
35845
36985
  return {
35846
36986
  path,
35847
- dispose: () => (0, import_node_fs41.rmSync)(dir, { recursive: true, force: true })
36987
+ dispose: () => (0, import_node_fs42.rmSync)(dir, { recursive: true, force: true })
35848
36988
  };
35849
36989
  }
35850
- var import_node_fs41, import_node_os11, import_node_path60, PI_GATE_TITLE, PI_READ_ONLY_TOOLS, PI_GATE_SOURCE;
36990
+ var import_node_fs42, import_node_os11, import_node_path60, PI_GATE_TITLE, PI_READ_ONLY_TOOLS, PI_GATE_SOURCE;
35851
36991
  var init_pi_gate = __esm({
35852
36992
  "src/drivers/native/pi-gate.ts"() {
35853
36993
  "use strict";
35854
- import_node_fs41 = require("node:fs");
36994
+ import_node_fs42 = require("node:fs");
35855
36995
  import_node_os11 = require("node:os");
35856
36996
  import_node_path60 = require("node:path");
35857
36997
  PI_GATE_TITLE = "ama-approval";
@@ -36346,7 +37486,7 @@ function pidsFile(dataDir) {
36346
37486
  }
36347
37487
  function read(file) {
36348
37488
  try {
36349
- const parsed = JSON.parse((0, import_node_fs42.readFileSync)(file, "utf8"));
37489
+ const parsed = JSON.parse((0, import_node_fs43.readFileSync)(file, "utf8"));
36350
37490
  return Array.isArray(parsed.entries) ? parsed.entries : [];
36351
37491
  } catch {
36352
37492
  return [];
@@ -36354,11 +37494,11 @@ function read(file) {
36354
37494
  }
36355
37495
  function write(file, entries) {
36356
37496
  try {
36357
- (0, import_node_fs42.mkdirSync)((0, import_node_path62.dirname)(file), { recursive: true });
37497
+ (0, import_node_fs43.mkdirSync)((0, import_node_path62.dirname)(file), { recursive: true });
36358
37498
  const tmp = `${file}.${process.pid}.tmp`;
36359
- (0, import_node_fs42.writeFileSync)(tmp, `${JSON.stringify({ version: 1, entries }, null, 2)}
37499
+ (0, import_node_fs43.writeFileSync)(tmp, `${JSON.stringify({ version: 1, entries }, null, 2)}
36360
37500
  `);
36361
- (0, import_node_fs42.renameSync)(tmp, file);
37501
+ (0, import_node_fs43.renameSync)(tmp, file);
36362
37502
  } catch {
36363
37503
  }
36364
37504
  }
@@ -36374,12 +37514,12 @@ function psCommand(pid) {
36374
37514
  const out = (0, import_node_child_process11.spawnSync)("ps", ["-o", "command=", "-p", String(pid)], { encoding: "utf8" });
36375
37515
  return out.status === 0 ? out.stdout.trim() : void 0;
36376
37516
  }
36377
- var import_node_child_process11, import_node_fs42, import_node_path62, PidRegistry;
37517
+ var import_node_child_process11, import_node_fs43, import_node_path62, PidRegistry;
36378
37518
  var init_pids = __esm({
36379
37519
  "src/drivers/pids.ts"() {
36380
37520
  "use strict";
36381
37521
  import_node_child_process11 = require("node:child_process");
36382
- import_node_fs42 = require("node:fs");
37522
+ import_node_fs43 = require("node:fs");
36383
37523
  import_node_path62 = require("node:path");
36384
37524
  init_process_tree();
36385
37525
  PidRegistry = class {
@@ -36644,7 +37784,7 @@ function toUsage(turn) {
36644
37784
  usage.cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: u.costUsd };
36645
37785
  return usage;
36646
37786
  }
36647
- function statusOf(turn, timedOut, stopped) {
37787
+ function statusOf2(turn, timedOut, stopped) {
36648
37788
  if (timedOut) return "failed";
36649
37789
  switch (turn.stopReason) {
36650
37790
  case "end_turn":
@@ -37037,7 +38177,7 @@ var init_runner2 = __esm({
37037
38177
  });
37038
38178
  this.emit({ type: "usage", usage, unit, amount });
37039
38179
  }
37040
- const status = statusOf(turn, timedOut, this.stopped);
38180
+ const status = statusOf2(turn, timedOut, this.stopped);
37041
38181
  this.emitTurnTrace(
37042
38182
  turnStartedAt,
37043
38183
  timedOut ? "timeout" : turn.stopReason,
@@ -37157,7 +38297,7 @@ var init_agents2 = __esm({
37157
38297
  driverDeps;
37158
38298
  /** `task(agent=<spec>)` 的 runner;不可用时抛 AmaError(`agent_host_only` / `agent_unknown`)。 */
37159
38299
  resolve(spec2) {
37160
- const hosted = this.hostRunners.get(spec2);
38300
+ const hosted = this.hostRunners.resolve(spec2);
37161
38301
  if (hosted !== void 0) return hosted;
37162
38302
  if (this.options.hosted)
37163
38303
  throw new AmaError(
@@ -37812,7 +38952,7 @@ function sourceText2(source, cache3, log) {
37812
38952
  if (cached !== void 0) return cached;
37813
38953
  let text = "";
37814
38954
  try {
37815
- text = (0, import_node_fs43.readFileSync)(source.path, "utf8");
38955
+ text = (0, import_node_fs44.readFileSync)(source.path, "utf8");
37816
38956
  } catch (error2) {
37817
38957
  log(
37818
38958
  "warn",
@@ -38166,11 +39306,11 @@ async function disposeSessionAlongside(target, session, fallback) {
38166
39306
  await session.dispose();
38167
39307
  record3.live.delete(session);
38168
39308
  }
38169
- var import_node_fs43, records, LOG_LEVELS;
39309
+ var import_node_fs44, records, LOG_LEVELS;
38170
39310
  var init_compose_session = __esm({
38171
39311
  "src/cli/compose-session.ts"() {
38172
39312
  "use strict";
38173
- import_node_fs43 = require("node:fs");
39313
+ import_node_fs44 = require("node:fs");
38174
39314
  init_limits();
38175
39315
  init_session2();
38176
39316
  init_session_subagent();
@@ -39026,7 +40166,7 @@ var init_acp_sessions = __esm({
39026
40166
  // src/modes/acp/acp-server.ts
39027
40167
  function canonical(path) {
39028
40168
  try {
39029
- return import_node_fs44.realpathSync.native((0, import_node_path65.resolve)(path));
40169
+ return import_node_fs45.realpathSync.native((0, import_node_path65.resolve)(path));
39030
40170
  } catch {
39031
40171
  return (0, import_node_path65.resolve)(path);
39032
40172
  }
@@ -39037,12 +40177,12 @@ function str(params, key) {
39037
40177
  throw new RpcError(RPC_ERRORS.invalidParams, msg().acp.core.missingParam(key));
39038
40178
  return value;
39039
40179
  }
39040
- function promptOf(blocks) {
39041
- if (!Array.isArray(blocks))
40180
+ function promptOf(blocks2) {
40181
+ if (!Array.isArray(blocks2))
39042
40182
  throw new RpcError(RPC_ERRORS.invalidParams, msg().acp.core.promptNotArray);
39043
40183
  const parts = [];
39044
40184
  const images = [];
39045
- for (const block of blocks) {
40185
+ for (const block of blocks2) {
39046
40186
  if (block.type === "text") parts.push(block.text);
39047
40187
  else if (block.type === "image")
39048
40188
  images.push({ type: "image", data: block.data, mimeType: block.mimeType });
@@ -39057,11 +40197,11 @@ ${block.resource.text}
39057
40197
  function countOf(value) {
39058
40198
  return Array.isArray(value) ? value.length : 0;
39059
40199
  }
39060
- var import_node_fs44, import_node_path65, ACP_AGENT_CAPABILITIES, SESSION_ID_SHAPE, AcpServer;
40200
+ var import_node_fs45, import_node_path65, ACP_AGENT_CAPABILITIES, SESSION_ID_SHAPE, AcpServer;
39061
40201
  var init_acp_server = __esm({
39062
40202
  "src/modes/acp/acp-server.ts"() {
39063
40203
  "use strict";
39064
- import_node_fs44 = require("node:fs");
40204
+ import_node_fs45 = require("node:fs");
39065
40205
  import_node_path65 = require("node:path");
39066
40206
  init_shared();
39067
40207
  init_compose_session();
@@ -39785,7 +40925,7 @@ async function bootstrap(argsIn, deps, io) {
39785
40925
  () => deps.sessions.open(sessionRequest, { sessionDir: paths.sessionDir, cwd: paths.cwd })
39786
40926
  );
39787
40927
  let sessionCwd = sessionManager.cwd;
39788
- if (!(0, import_node_fs45.existsSync)(sessionCwd)) {
40928
+ if (!(0, import_node_fs46.existsSync)(sessionCwd)) {
39789
40929
  const replacement = interactive ? await deps.ui?.askCwd?.(sessionCwd) : void 0;
39790
40930
  if (replacement === void 0) {
39791
40931
  throw new StartupError(
@@ -40179,11 +41319,11 @@ function reportError(error2, io, fallback = ExitCode.RuntimeError) {
40179
41319
  if (isAmaError(error2) && error2.exitCode !== void 0) return error2.exitCode;
40180
41320
  return fallback;
40181
41321
  }
40182
- var import_node_fs45, import_node_path67;
41322
+ var import_node_fs46, import_node_path67;
40183
41323
  var init_bootstrap = __esm({
40184
41324
  "src/cli/bootstrap.ts"() {
40185
41325
  "use strict";
40186
- import_node_fs45 = require("node:fs");
41326
+ import_node_fs46 = require("node:fs");
40187
41327
  import_node_path67 = require("node:path");
40188
41328
  init_auth_file();
40189
41329
  init_context_files();
@@ -40210,7 +41350,7 @@ var init_bootstrap = __esm({
40210
41350
  // src/auth/oauth/token-store.ts
40211
41351
  function readRaw(path) {
40212
41352
  try {
40213
- const value = JSON.parse(retryTransientFs(() => (0, import_node_fs46.readFileSync)(path, "utf8")));
41353
+ const value = JSON.parse(retryTransientFs(() => (0, import_node_fs47.readFileSync)(path, "utf8")));
40214
41354
  return typeof value === "object" && value !== null && typeof value.providers === "object" ? value : void 0;
40215
41355
  } catch {
40216
41356
  return void 0;
@@ -40242,12 +41382,12 @@ function isStaleLock(path, now = Date.now()) {
40242
41382
  let age;
40243
41383
  let pid;
40244
41384
  try {
40245
- age = now - (0, import_node_fs46.statSync)(path).mtimeMs;
40246
- const value = JSON.parse((0, import_node_fs46.readFileSync)(path, "utf8"));
41385
+ age = now - (0, import_node_fs47.statSync)(path).mtimeMs;
41386
+ const value = JSON.parse((0, import_node_fs47.readFileSync)(path, "utf8"));
40247
41387
  pid = typeof value.pid === "number" ? value.pid : void 0;
40248
41388
  } catch {
40249
41389
  try {
40250
- age = now - (0, import_node_fs46.statSync)(path).mtimeMs;
41390
+ age = now - (0, import_node_fs47.statSync)(path).mtimeMs;
40251
41391
  } catch {
40252
41392
  return false;
40253
41393
  }
@@ -40256,11 +41396,11 @@ function isStaleLock(path, now = Date.now()) {
40256
41396
  }
40257
41397
  function tryAcquire(path) {
40258
41398
  try {
40259
- const fd = (0, import_node_fs46.openSync)(path, "wx", 384);
41399
+ const fd = (0, import_node_fs47.openSync)(path, "wx", 384);
40260
41400
  try {
40261
- (0, import_node_fs46.writeSync)(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
41401
+ (0, import_node_fs47.writeSync)(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
40262
41402
  } finally {
40263
- (0, import_node_fs46.closeSync)(fd);
41403
+ (0, import_node_fs47.closeSync)(fd);
40264
41404
  }
40265
41405
  return true;
40266
41406
  } catch (error2) {
@@ -40271,13 +41411,13 @@ function tryAcquire(path) {
40271
41411
  }
40272
41412
  async function withRefreshLock(authFile, fn, options = {}) {
40273
41413
  const path = lockPath(authFile);
40274
- (0, import_node_fs46.mkdirSync)((0, import_node_path68.dirname)(path), { recursive: true, mode: 448 });
41414
+ (0, import_node_fs47.mkdirSync)((0, import_node_path68.dirname)(path), { recursive: true, mode: 448 });
40275
41415
  const deadline = Date.now() + (options.waitMs ?? LOCK_WAIT_MS);
40276
41416
  for (; ; ) {
40277
41417
  if (tryAcquire(path)) break;
40278
41418
  if (isStaleLock(path)) {
40279
41419
  try {
40280
- (0, import_node_fs46.unlinkSync)(path);
41420
+ (0, import_node_fs47.unlinkSync)(path);
40281
41421
  } catch {
40282
41422
  }
40283
41423
  continue;
@@ -40292,16 +41432,16 @@ async function withRefreshLock(authFile, fn, options = {}) {
40292
41432
  return await fn();
40293
41433
  } finally {
40294
41434
  try {
40295
- (0, import_node_fs46.unlinkSync)(path);
41435
+ (0, import_node_fs47.unlinkSync)(path);
40296
41436
  } catch {
40297
41437
  }
40298
41438
  }
40299
41439
  }
40300
- var import_node_fs46, import_node_path68, LOCK_WAIT_MS, LOCK_STALE_MS, LOCK_POLL_MS;
41440
+ var import_node_fs47, import_node_path68, LOCK_WAIT_MS, LOCK_STALE_MS, LOCK_POLL_MS;
40301
41441
  var init_token_store = __esm({
40302
41442
  "src/auth/oauth/token-store.ts"() {
40303
41443
  "use strict";
40304
- import_node_fs46 = require("node:fs");
41444
+ import_node_fs47 = require("node:fs");
40305
41445
  import_node_path68 = require("node:path");
40306
41446
  init_auth_file();
40307
41447
  init_fs_retry();
@@ -40336,7 +41476,7 @@ function toEntry(value) {
40336
41476
  function readDiscoveredCache(dataDir, provider) {
40337
41477
  let parsed;
40338
41478
  try {
40339
- parsed = JSON.parse((0, import_node_fs47.readFileSync)(discoveredCachePath(dataDir, provider), "utf8"));
41479
+ parsed = JSON.parse((0, import_node_fs48.readFileSync)(discoveredCachePath(dataDir, provider), "utf8"));
40340
41480
  } catch {
40341
41481
  return void 0;
40342
41482
  }
@@ -40360,17 +41500,17 @@ function writeDiscoveredCache(dataDir, provider, input2, now = Date.now()) {
40360
41500
  ...input2.flavor !== void 0 ? { flavor: input2.flavor } : {},
40361
41501
  models: input2.models.map(toEntry).filter((m) => m !== void 0)
40362
41502
  };
40363
- (0, import_node_fs47.mkdirSync)((0, import_node_path69.dirname)(path), { recursive: true, mode: 448 });
41503
+ (0, import_node_fs48.mkdirSync)((0, import_node_path69.dirname)(path), { recursive: true, mode: 448 });
40364
41504
  const tmp = `${path}.${process.pid}.tmp`;
40365
- (0, import_node_fs47.writeFileSync)(tmp, `${JSON.stringify(file, null, 2)}
41505
+ (0, import_node_fs48.writeFileSync)(tmp, `${JSON.stringify(file, null, 2)}
40366
41506
  `);
40367
- (0, import_node_fs47.renameSync)(tmp, path);
41507
+ (0, import_node_fs48.renameSync)(tmp, path);
40368
41508
  return path;
40369
41509
  }
40370
41510
  function clearDiscoveredCache(dataDir, provider) {
40371
41511
  const path = discoveredCachePath(dataDir, provider);
40372
- if (!(0, import_node_fs47.existsSync)(path)) return false;
40373
- (0, import_node_fs47.rmSync)(path, { force: true });
41512
+ if (!(0, import_node_fs48.existsSync)(path)) return false;
41513
+ (0, import_node_fs48.rmSync)(path, { force: true });
40374
41514
  return true;
40375
41515
  }
40376
41516
  function thinkingMapOf(levels) {
@@ -40424,11 +41564,11 @@ function mergeDiscoveredModels(registry3, dataDir, modelsDev, loginFlavor = () =
40424
41564
  }
40425
41565
  return merged;
40426
41566
  }
40427
- var import_node_fs47, import_node_path69, DISCOVERED_CACHE_VERSION, EFFORT_LEVELS, SUBSCRIPTION_FALLBACK_CONTEXT_WINDOW, SUBSCRIPTION_PROVIDERS;
41567
+ var import_node_fs48, import_node_path69, DISCOVERED_CACHE_VERSION, EFFORT_LEVELS, SUBSCRIPTION_FALLBACK_CONTEXT_WINDOW, SUBSCRIPTION_PROVIDERS;
40428
41568
  var init_discovered_cache = __esm({
40429
41569
  "src/ai/providers/discovered-cache.ts"() {
40430
41570
  "use strict";
40431
- import_node_fs47 = require("node:fs");
41571
+ import_node_fs48 = require("node:fs");
40432
41572
  import_node_path69 = require("node:path");
40433
41573
  init_catalog2();
40434
41574
  init_enrich();
@@ -42316,7 +43456,7 @@ function parseKeybindings(value) {
42316
43456
  function loadKeybindingsFile(path) {
42317
43457
  let text;
42318
43458
  try {
42319
- text = (0, import_node_fs49.readFileSync)(path, "utf8");
43459
+ text = (0, import_node_fs50.readFileSync)(path, "utf8");
42320
43460
  } catch (error2) {
42321
43461
  if (error2.code === "ENOENT") return { overrides: {}, warnings: [] };
42322
43462
  return {
@@ -42333,12 +43473,12 @@ function loadKeybindingsFile(path) {
42333
43473
  };
42334
43474
  }
42335
43475
  }
42336
- var import_node_fs49, DEFAULT_KEYBINDINGS, Keybindings, defaultKeybindings;
43476
+ var import_node_fs50, DEFAULT_KEYBINDINGS, Keybindings, defaultKeybindings;
42337
43477
  var init_keybindings = __esm({
42338
43478
  "src/tui/keybindings.ts"() {
42339
43479
  "use strict";
42340
43480
  init_i18n();
42341
- import_node_fs49 = require("node:fs");
43481
+ import_node_fs50 = require("node:fs");
42342
43482
  init_keys();
42343
43483
  DEFAULT_KEYBINDINGS = {
42344
43484
  "tui.editor.submit": ["enter"],
@@ -42486,7 +43626,7 @@ function isBlockStart(line2) {
42486
43626
  function splitLines2(source) {
42487
43627
  return source.replace(/\r\n?/g, "\n").split("\n");
42488
43628
  }
42489
- function parseLines(lines2, from, blocks, spans) {
43629
+ function parseLines(lines2, from, blocks2, spans) {
42490
43630
  let i = from;
42491
43631
  while (i < lines2.length) {
42492
43632
  const line2 = lines2[i];
@@ -42495,13 +43635,13 @@ function parseLines(lines2, from, blocks, spans) {
42495
43635
  continue;
42496
43636
  }
42497
43637
  const start = i;
42498
- const before2 = blocks.length;
42499
- i = parseBlock(lines2, i, blocks);
42500
- for (let k = before2; k < blocks.length; k++) spans.push({ start, end: i });
43638
+ const before2 = blocks2.length;
43639
+ i = parseBlock(lines2, i, blocks2);
43640
+ for (let k = before2; k < blocks2.length; k++) spans.push({ start, end: i });
42501
43641
  }
42502
- return blocks;
43642
+ return blocks2;
42503
43643
  }
42504
- function parseBlock(lines2, i, blocks) {
43644
+ function parseBlock(lines2, i, blocks2) {
42505
43645
  const line2 = lines2[i];
42506
43646
  const start = i;
42507
43647
  const fence2 = FENCE_RE.exec(line2);
@@ -42512,12 +43652,12 @@ function parseBlock(lines2, i, blocks) {
42512
43652
  while (i < lines2.length && !lines2[i].trimStart().startsWith(marker)) body2.push(lines2[i++]);
42513
43653
  if (i < lines2.length) i++;
42514
43654
  const src = lines2.slice(start, i).join("\n");
42515
- blocks.push({ kind: "code", lang: fence2[2] ?? "", lines: body2, source: src });
43655
+ blocks2.push({ kind: "code", lang: fence2[2] ?? "", lines: body2, source: src });
42516
43656
  return i;
42517
43657
  }
42518
43658
  const heading = HEADING_RE.exec(line2);
42519
43659
  if (heading) {
42520
- blocks.push({
43660
+ blocks2.push({
42521
43661
  kind: "heading",
42522
43662
  level: heading[1].length,
42523
43663
  text: heading[2] ?? "",
@@ -42527,7 +43667,7 @@ function parseBlock(lines2, i, blocks) {
42527
43667
  return i;
42528
43668
  }
42529
43669
  if (HR_RE.test(line2)) {
42530
- blocks.push({ kind: "hr", source: line2 });
43670
+ blocks2.push({ kind: "hr", source: line2 });
42531
43671
  i++;
42532
43672
  return i;
42533
43673
  }
@@ -42538,13 +43678,13 @@ function parseBlock(lines2, i, blocks) {
42538
43678
  if (TABLE_SEP_RE.test(row3)) continue;
42539
43679
  rows.push(splitTableRow(row3));
42540
43680
  }
42541
- blocks.push({ kind: "table", rows, source: lines2.slice(start, i).join("\n") });
43681
+ blocks2.push({ kind: "table", rows, source: lines2.slice(start, i).join("\n") });
42542
43682
  return i;
42543
43683
  }
42544
43684
  if (QUOTE_RE.test(line2)) {
42545
43685
  const body2 = [];
42546
43686
  while (i < lines2.length && QUOTE_RE.test(lines2[i])) body2.push(QUOTE_RE.exec(lines2[i++])[1]);
42547
- blocks.push({
43687
+ blocks2.push({
42548
43688
  kind: "quote",
42549
43689
  text: body2.join("\n"),
42550
43690
  source: lines2.slice(start, i).join("\n")
@@ -42552,17 +43692,17 @@ function parseBlock(lines2, i, blocks) {
42552
43692
  return i;
42553
43693
  }
42554
43694
  if (LIST_RE.test(line2)) {
42555
- return parseList(lines2, i, blocks);
43695
+ return parseList(lines2, i, blocks2);
42556
43696
  }
42557
43697
  const body = [line2];
42558
43698
  i++;
42559
43699
  while (i < lines2.length && lines2[i].trim() !== "" && !isBlockStart(lines2[i])) {
42560
43700
  body.push(lines2[i++]);
42561
43701
  }
42562
- blocks.push({ kind: "paragraph", text: joinSoftBreaks(body), source: body.join("\n") });
43702
+ blocks2.push({ kind: "paragraph", text: joinSoftBreaks(body), source: body.join("\n") });
42563
43703
  return i;
42564
43704
  }
42565
- function parseList(lines2, start, blocks) {
43705
+ function parseList(lines2, start, blocks2) {
42566
43706
  const items = [];
42567
43707
  const indents = [];
42568
43708
  let i = start;
@@ -42589,7 +43729,7 @@ function parseList(lines2, start, blocks) {
42589
43729
  }
42590
43730
  break;
42591
43731
  }
42592
- blocks.push({ kind: "list", items, source: lines2.slice(start, i).join("\n") });
43732
+ blocks2.push({ kind: "list", items, source: lines2.slice(start, i).join("\n") });
42593
43733
  return i;
42594
43734
  }
42595
43735
  function joinSoftBreaks(lines2) {
@@ -42609,7 +43749,7 @@ function splitTableRow(row3) {
42609
43749
  const trimmed = row3.trim().replace(/^\|/, "").replace(/\|$/, "");
42610
43750
  return trimmed.split(/(?<!\\)\|/).map((cell) => cell.trim().replace(/\\\|/g, "|"));
42611
43751
  }
42612
- function renderInline(text, theme) {
43752
+ function renderInline2(text, theme) {
42613
43753
  let out = "";
42614
43754
  let i = 0;
42615
43755
  while (i < text.length) {
@@ -42630,7 +43770,7 @@ function renderInline(text, theme) {
42630
43770
  }
42631
43771
  const emphasis = matchDelimited(text, i, ["**", "__", "~~", "*", "_"]);
42632
43772
  if (emphasis) {
42633
- const inner = renderInline(emphasis.inner, theme);
43773
+ const inner = renderInline2(emphasis.inner, theme);
42634
43774
  if (emphasis.delim === "**" || emphasis.delim === "__") out += theme.bold(inner);
42635
43775
  else if (emphasis.delim === "~~") out += `\x1B[9m${inner}\x1B[29m`;
42636
43776
  else out += theme.italic(inner);
@@ -42640,7 +43780,7 @@ function renderInline(text, theme) {
42640
43780
  if (ch2 === "[") {
42641
43781
  const link = /^\[([^\]]*)\]\(([^)\s]*)(?:\s+"[^"]*")?\)/.exec(text.slice(i));
42642
43782
  if (link) {
42643
- const label2 = renderInline(link[1], theme);
43783
+ const label2 = renderInline2(link[1], theme);
42644
43784
  const url = link[2];
42645
43785
  out += link[1] === url || url === "" ? theme.fg("link", theme.underline(label2 || url)) : theme.fg("link", theme.underline(label2)) + theme.fg("dim", ` (${url})`);
42646
43786
  i += link[0].length;
@@ -42685,31 +43825,31 @@ function matchDelimited(text, i, delims) {
42685
43825
  }
42686
43826
  return null;
42687
43827
  }
42688
- function renderBlock(block, width2, theme) {
43828
+ function renderBlock2(block, width2, theme) {
42689
43829
  switch (block.kind) {
42690
43830
  case "heading": {
42691
- const text = renderInline(block.text, theme);
43831
+ const text = renderInline2(block.text, theme);
42692
43832
  const styled = block.level <= 2 ? theme.fg("accent", theme.bold(text)) : theme.fg("text", theme.bold(text));
42693
43833
  return wrapTextWithAnsi(styled, width2);
42694
43834
  }
42695
43835
  case "paragraph":
42696
- return wrapTextWithAnsi(renderInline(block.text, theme), width2);
43836
+ return wrapTextWithAnsi(renderInline2(block.text, theme), width2);
42697
43837
  case "hr":
42698
43838
  return [theme.fg("border", theme.glyphs.rule.repeat(Math.max(1, width2)))];
42699
43839
  case "quote": {
42700
43840
  const bar = theme.fg("border", theme.glyphs.card) + " ";
42701
- const body = theme.fg("muted", theme.italic(renderInline(block.text, theme)));
43841
+ const body = theme.fg("muted", theme.italic(renderInline2(block.text, theme)));
42702
43842
  return wrapTextWithAnsi(body, Math.max(1, width2 - 2)).map((line2) => bar + line2);
42703
43843
  }
42704
43844
  case "list":
42705
- return renderList(block.items, width2, theme);
43845
+ return renderList2(block.items, width2, theme);
42706
43846
  case "code":
42707
43847
  return renderCode(block.lang, block.lines, width2, theme);
42708
43848
  case "table":
42709
- return renderTable(block.rows, width2, theme);
43849
+ return renderTable2(block.rows, width2, theme);
42710
43850
  }
42711
43851
  }
42712
- function renderList(items, width2, theme) {
43852
+ function renderList2(items, width2, theme) {
42713
43853
  const lines2 = [];
42714
43854
  const bullets = theme.glyphs.bullets;
42715
43855
  const columns = [];
@@ -42726,7 +43866,7 @@ function renderList(items, width2, theme) {
42726
43866
  unordered.length = depth;
42727
43867
  columns.push(headWidth);
42728
43868
  unordered.push(level);
42729
- const body = wrapTextWithAnsi(renderInline(item.text, theme), Math.max(1, width2 - headWidth));
43869
+ const body = wrapTextWithAnsi(renderInline2(item.text, theme), Math.max(1, width2 - headWidth));
42730
43870
  body.forEach((line2, index2) => {
42731
43871
  lines2.push((index2 === 0 ? head : " ".repeat(headWidth)) + line2);
42732
43872
  });
@@ -42770,10 +43910,10 @@ function wrapCode(line2, width2) {
42770
43910
  out.push(current3);
42771
43911
  return out;
42772
43912
  }
42773
- function renderTable(rows, width2, theme) {
43913
+ function renderTable2(rows, width2, theme) {
42774
43914
  const cols = Math.max(0, ...rows.map((r) => r.length));
42775
43915
  const widths = new Array(cols).fill(0);
42776
- const rendered = rows.map((row3) => row3.map((cell) => renderInline(cell, theme)));
43916
+ const rendered = rows.map((row3) => row3.map((cell) => renderInline2(cell, theme)));
42777
43917
  for (const row3 of rendered) {
42778
43918
  row3.forEach((cell, c) => widths[c] = Math.max(widths[c], visibleWidth(cell)));
42779
43919
  }
@@ -42887,7 +44027,7 @@ var init_markdown = __esm({
42887
44027
  used.add(key);
42888
44028
  let rendered = this.blockCache.get(key);
42889
44029
  if (!rendered) {
42890
- rendered = renderBlock(block, inner, this.theme).map((line2) => pad3 + line2);
44030
+ rendered = renderBlock2(block, inner, this.theme).map((line2) => pad3 + line2);
42891
44031
  this.blockCache.set(key, rendered);
42892
44032
  }
42893
44033
  if (index2 > 0) lines2.push("");
@@ -43175,7 +44315,7 @@ var init_editor_buffer = __esm({
43175
44315
  function loadHistoryFile(path, limit = 500) {
43176
44316
  let text;
43177
44317
  try {
43178
- text = (0, import_node_fs50.readFileSync)(path, "utf8");
44318
+ text = (0, import_node_fs51.readFileSync)(path, "utf8");
43179
44319
  } catch {
43180
44320
  return [];
43181
44321
  }
@@ -43192,11 +44332,11 @@ function loadHistoryFile(path, limit = 500) {
43192
44332
  }
43193
44333
  function appendHistoryFile(path, entry, limit) {
43194
44334
  try {
43195
- (0, import_node_fs50.mkdirSync)((0, import_node_path71.dirname)(path), { recursive: true });
43196
- (0, import_node_fs50.appendFileSync)(path, JSON.stringify(entry) + "\n", { mode: 384 });
44335
+ (0, import_node_fs51.mkdirSync)((0, import_node_path71.dirname)(path), { recursive: true });
44336
+ (0, import_node_fs51.appendFileSync)(path, JSON.stringify(entry) + "\n", { mode: 384 });
43197
44337
  const all = loadHistoryFile(path, Number.MAX_SAFE_INTEGER);
43198
44338
  if (all.length > limit * 2) {
43199
- (0, import_node_fs50.writeFileSync)(
44339
+ (0, import_node_fs51.writeFileSync)(
43200
44340
  path,
43201
44341
  all.slice(-limit).map((e) => JSON.stringify(e) + "\n").join("")
43202
44342
  );
@@ -43204,11 +44344,11 @@ function appendHistoryFile(path, entry, limit) {
43204
44344
  } catch {
43205
44345
  }
43206
44346
  }
43207
- var import_node_fs50, import_node_path71;
44347
+ var import_node_fs51, import_node_path71;
43208
44348
  var init_editor_history = __esm({
43209
44349
  "src/tui/components/editor-history.ts"() {
43210
44350
  "use strict";
43211
- import_node_fs50 = require("node:fs");
44351
+ import_node_fs51 = require("node:fs");
43212
44352
  import_node_path71 = require("node:path");
43213
44353
  }
43214
44354
  });
@@ -43347,11 +44487,11 @@ var init_select_list = __esm({
43347
44487
  this.setSelectedIndex(index2);
43348
44488
  return true;
43349
44489
  }
43350
- moveSelection(delta, wrap2 = true) {
44490
+ moveSelection(delta, wrap3 = true) {
43351
44491
  const n = this.filtered.length;
43352
44492
  if (n === 0) return;
43353
44493
  let next = this.selected + delta;
43354
- if (wrap2 && Math.abs(delta) === 1) next = (next + n) % n;
44494
+ if (wrap3 && Math.abs(delta) === 1) next = (next + n) % n;
43355
44495
  this.setSelectedIndex(next);
43356
44496
  }
43357
44497
  handleInput(data2) {
@@ -44244,14 +45384,14 @@ var init_key_value = __esm({
44244
45384
  const keyWidth = Math.min(longest, Math.max(share, MIN_KEY_WIDTH));
44245
45385
  const valueWidth = width2 - keyWidth - gap;
44246
45386
  const { theme, keyColor = "dim" } = this.options;
44247
- const wrap2 = this.options.wrap === true;
45387
+ const wrap3 = this.options.wrap === true;
44248
45388
  const lines2 = this.rows.flatMap(({ key, value }) => {
44249
45389
  if (key === "" && value === "") return [""];
44250
45390
  const shownKey = truncateToWidth(key, Math.max(keyWidth, 1));
44251
45391
  const styledKey = theme ? theme.fg(keyColor, shownKey) : shownKey;
44252
45392
  if (valueWidth < 1) return [truncateToWidth(styledKey, width2)];
44253
45393
  const head = padToWidth(styledKey, keyWidth) + " ".repeat(gap);
44254
- if (!wrap2) {
45394
+ if (!wrap3) {
44255
45395
  const flat2 = value.replace(/\r?\n/g, " ⏎ ");
44256
45396
  return [head + truncateToWidth(flat2, valueWidth)];
44257
45397
  }
@@ -44648,22 +45788,22 @@ function toolDeclaration(tool, options = {}) {
44648
45788
  }
44649
45789
  function buildDeclarationBlock(tools, inlineBudget = DEFAULT_INLINE_BUDGET) {
44650
45790
  const sorted = [...tools].sort((a, b) => a.tool.name < b.tool.name ? -1 : 1);
44651
- const inline = [];
45791
+ const inline2 = [];
44652
45792
  const namesOnly = [];
44653
45793
  let used = 0;
44654
45794
  for (const { tool, textResult } of sorted) {
44655
45795
  const decl = toolDeclaration(tool, textResult === false ? { textResult } : {});
44656
45796
  const cost2 = estimateTokens2(decl);
44657
45797
  if (namesOnly.length === 0 && used + cost2 <= inlineBudget) {
44658
- inline.push(decl);
45798
+ inline2.push(decl);
44659
45799
  used += cost2;
44660
45800
  } else namesOnly.push(tool.name);
44661
45801
  }
44662
45802
  const parts = [];
44663
45803
  if (sorted.some(({ tool }) => tool.name === "bash")) parts.push(BASH_RESULT_DECLARATION);
44664
- if (inline.length > 0) {
45804
+ if (inline2.length > 0) {
44665
45805
  parts.push(`declare const tools: {
44666
- ${indent(inline.join("\n"))}
45806
+ ${indent(inline2.join("\n"))}
44667
45807
  };`);
44668
45808
  }
44669
45809
  if (namesOnly.length > 0) {
@@ -44671,7 +45811,7 @@ ${indent(inline.join("\n"))}
44671
45811
  `// Also callable (use describeTool(name) for the signature): ${namesOnly.join(", ")}`
44672
45812
  );
44673
45813
  }
44674
- return { inline, namesOnly, text: parts.join("\n") };
45814
+ return { inline: inline2, namesOnly, text: parts.join("\n") };
44675
45815
  }
44676
45816
  function indent(text) {
44677
45817
  return text.split("\n").map((line2) => line2 === "" ? line2 : ` ${line2}`).join("\n");
@@ -44791,7 +45931,7 @@ function modelsDevUrl(env = process.env) {
44791
45931
  }
44792
45932
  function readModelsDevCache(dataDir) {
44793
45933
  try {
44794
- const value = JSON.parse((0, import_node_fs51.readFileSync)(modelsDevCachePath(dataDir), "utf8"));
45934
+ const value = JSON.parse((0, import_node_fs52.readFileSync)(modelsDevCachePath(dataDir), "utf8"));
44795
45935
  if (typeof value !== "object" || value === null) return void 0;
44796
45936
  const file = value;
44797
45937
  if (file.version !== 2 || typeof file.fetchedAt !== "string") return void 0;
@@ -44804,10 +45944,10 @@ function readModelsDevCache(dataDir) {
44804
45944
  }
44805
45945
  function writeModelsDevCache(dataDir, file) {
44806
45946
  const path = modelsDevCachePath(dataDir);
44807
- (0, import_node_fs51.mkdirSync)((0, import_node_path73.dirname)(path), { recursive: true, mode: 448 });
45947
+ (0, import_node_fs52.mkdirSync)((0, import_node_path73.dirname)(path), { recursive: true, mode: 448 });
44808
45948
  const tmp = `${path}.${process.pid}.tmp`;
44809
- (0, import_node_fs51.writeFileSync)(tmp, JSON.stringify(file));
44810
- (0, import_node_fs51.renameSync)(tmp, path);
45949
+ (0, import_node_fs52.writeFileSync)(tmp, JSON.stringify(file));
45950
+ (0, import_node_fs52.renameSync)(tmp, path);
44811
45951
  memo = void 0;
44812
45952
  }
44813
45953
  function overrideApplies(file) {
@@ -44817,7 +45957,7 @@ function loadModelsDevIndex(dataDir) {
44817
45957
  const path = modelsDevCachePath(dataDir);
44818
45958
  let key;
44819
45959
  try {
44820
- const stat16 = (0, import_node_fs51.statSync)(path);
45960
+ const stat16 = (0, import_node_fs52.statSync)(path);
44821
45961
  key = `${path}\0${stat16.mtimeMs}\0${stat16.size}`;
44822
45962
  } catch {
44823
45963
  return builtinSnapshotIndex();
@@ -44839,7 +45979,7 @@ async function refreshModelsDev(options) {
44839
45979
  const url = modelsDevUrl(options.env);
44840
45980
  const now = options.now ?? Date.now;
44841
45981
  const before2 = loadModelsDevIndex(options.dataDir);
44842
- const fail6 = (reason) => ({
45982
+ const fail7 = (reason) => ({
44843
45983
  status: "failed",
44844
45984
  url,
44845
45985
  index: before2,
@@ -44848,7 +45988,7 @@ async function refreshModelsDev(options) {
44848
45988
  const list5 = snapshotList();
44849
45989
  const unknown = (options.providers ?? []).filter((p) => !list5.providers.includes(p));
44850
45990
  if (unknown.length > 0)
44851
- return fail6(msg().errors.models.notListed(unknown.join(", "), list5.providers.join(", ")));
45991
+ return fail7(msg().errors.models.notListed(unknown.join(", "), list5.providers.join(", ")));
44852
45992
  let fresh;
44853
45993
  try {
44854
45994
  const response = await (options.fetch ?? fetch)(url, {
@@ -44858,7 +45998,7 @@ async function refreshModelsDev(options) {
44858
45998
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
44859
45999
  fresh = buildSnapshot(await response.json(), list5, options.providers);
44860
46000
  } catch (error2) {
44861
- return fail6(error2 instanceof Error ? error2.message : String(error2));
46001
+ return fail7(error2 instanceof Error ? error2.message : String(error2));
44862
46002
  }
44863
46003
  const fetchedAt = new Date(now()).toISOString();
44864
46004
  const previous = readModelsDevCache(options.dataDir);
@@ -44902,11 +46042,11 @@ function describeRefresh(result, limit = 20) {
44902
46042
  }
44903
46043
  return lines2.join("\n");
44904
46044
  }
44905
- var import_node_fs51, import_node_path73, MODELS_DEV_URL, MODELS_DEV_URL_ENV, MODELS_DEV_FILE, MODELS_DEV_TIMEOUT_MS, memo;
46045
+ var import_node_fs52, import_node_path73, MODELS_DEV_URL, MODELS_DEV_URL_ENV, MODELS_DEV_FILE, MODELS_DEV_TIMEOUT_MS, memo;
44906
46046
  var init_models_dev_cache = __esm({
44907
46047
  "src/ai/providers/models-dev-cache.ts"() {
44908
46048
  "use strict";
44909
- import_node_fs51 = require("node:fs");
46049
+ import_node_fs52 = require("node:fs");
44910
46050
  import_node_path73 = require("node:path");
44911
46051
  init_models_dev();
44912
46052
  init_models_dev_snapshot();
@@ -45252,40 +46392,40 @@ function minimalConfig() {
45252
46392
  }
45253
46393
  function writeAtomic2(path, text, mode) {
45254
46394
  const tmp = `${path}.${process.pid}.tmp`;
45255
- (0, import_node_fs52.writeFileSync)(tmp, text, { mode });
45256
- (0, import_node_fs52.renameSync)(tmp, path);
46395
+ (0, import_node_fs53.writeFileSync)(tmp, text, { mode });
46396
+ (0, import_node_fs53.renameSync)(tmp, path);
45257
46397
  }
45258
46398
  function initConfigDir(dir, options = {}) {
45259
- const dirCreated = !(0, import_node_fs52.existsSync)(dir);
45260
- (0, import_node_fs52.mkdirSync)(dir, { recursive: true, mode: 448 });
45261
- if (dirCreated && process.platform !== "win32") (0, import_node_fs52.chmodSync)(dir, 448);
46399
+ const dirCreated = !(0, import_node_fs53.existsSync)(dir);
46400
+ (0, import_node_fs53.mkdirSync)(dir, { recursive: true, mode: 448 });
46401
+ if (dirCreated && process.platform !== "win32") (0, import_node_fs53.chmodSync)(dir, 448);
45262
46402
  const files = [];
45263
46403
  const config = (0, import_node_path74.join)(dir, CONFIG_FILE);
45264
46404
  const text = `${JSON.stringify(minimalConfig(), null, 2)}
45265
46405
  `;
45266
- if (!(0, import_node_fs52.existsSync)(config)) {
46406
+ if (!(0, import_node_fs53.existsSync)(config)) {
45267
46407
  writeAtomic2(config, text, 420);
45268
46408
  files.push({ path: config, status: "created" });
45269
46409
  } else if (options.force) {
45270
- (0, import_node_fs52.copyFileSync)(config, `${config}.bak`);
46410
+ (0, import_node_fs53.copyFileSync)(config, `${config}.bak`);
45271
46411
  writeAtomic2(config, text, 420);
45272
46412
  files.push({ path: config, status: "overwritten" });
45273
46413
  } else files.push({ path: config, status: "exists" });
45274
46414
  const schema = (0, import_node_path74.join)(dir, CONFIG_SCHEMA_FILE);
45275
46415
  const schemaText = configSchemaText();
45276
- if (!(0, import_node_fs52.existsSync)(schema)) {
46416
+ if (!(0, import_node_fs53.existsSync)(schema)) {
45277
46417
  writeAtomic2(schema, schemaText, 420);
45278
46418
  files.push({ path: schema, status: "created" });
45279
- } else if ((0, import_node_fs52.readFileSync)(schema, "utf8") !== schemaText) {
46419
+ } else if ((0, import_node_fs53.readFileSync)(schema, "utf8") !== schemaText) {
45280
46420
  writeAtomic2(schema, schemaText, 420);
45281
46421
  files.push({ path: schema, status: "updated" });
45282
46422
  } else files.push({ path: schema, status: "unchanged" });
45283
46423
  const auth = (0, import_node_path74.join)(dir, AUTH_FILE);
45284
- if ((0, import_node_fs52.existsSync)(auth)) files.push({ path: auth, status: "exists" });
46424
+ if ((0, import_node_fs53.existsSync)(auth)) files.push({ path: auth, status: "exists" });
45285
46425
  return { dir, dirCreated, files };
45286
46426
  }
45287
46427
  function autoInitConfigDir(dir, env) {
45288
- if (env[NO_INIT_ENV] === "1" || (0, import_node_fs52.existsSync)(dir)) return false;
46428
+ if (env[NO_INIT_ENV] === "1" || (0, import_node_fs53.existsSync)(dir)) return false;
45289
46429
  try {
45290
46430
  initConfigDir(dir);
45291
46431
  return true;
@@ -45303,11 +46443,11 @@ function describeInit(result) {
45303
46443
  return `${lines2.join("\n")}
45304
46444
  `;
45305
46445
  }
45306
- var import_node_fs52, import_node_path74, NO_INIT_ENV;
46446
+ var import_node_fs53, import_node_path74, NO_INIT_ENV;
45307
46447
  var init_init = __esm({
45308
46448
  "src/config/init.ts"() {
45309
46449
  "use strict";
45310
- import_node_fs52 = require("node:fs");
46450
+ import_node_fs53 = require("node:fs");
45311
46451
  import_node_path74 = require("node:path");
45312
46452
  init_i18n();
45313
46453
  init_json_schema();
@@ -45551,13 +46691,13 @@ function normalizeToolId(id) {
45551
46691
  return cleaned.length > 0 ? cleaned : "tool_call";
45552
46692
  }
45553
46693
  function convertBlocks(content) {
45554
- const blocks = [];
46694
+ const blocks2 = [];
45555
46695
  for (const block of content) {
45556
46696
  if (block.type === "text") {
45557
46697
  if (block.text.trim().length > 0)
45558
- blocks.push({ type: "text", text: sanitizeText(block.text) });
46698
+ blocks2.push({ type: "text", text: sanitizeText(block.text) });
45559
46699
  } else {
45560
- blocks.push({
46700
+ blocks2.push({
45561
46701
  type: "image",
45562
46702
  source: {
45563
46703
  type: "base64",
@@ -45567,33 +46707,33 @@ function convertBlocks(content) {
45567
46707
  });
45568
46708
  }
45569
46709
  }
45570
- return blocks;
46710
+ return blocks2;
45571
46711
  }
45572
46712
  function convertUser(message) {
45573
46713
  if (typeof message.content === "string") {
45574
46714
  if (message.content.trim().length === 0) return void 0;
45575
46715
  return { role: "user", content: sanitizeText(message.content) };
45576
46716
  }
45577
- const blocks = convertBlocks(message.content);
45578
- return blocks.length > 0 ? { role: "user", content: blocks } : void 0;
46717
+ const blocks2 = convertBlocks(message.content);
46718
+ return blocks2.length > 0 ? { role: "user", content: blocks2 } : void 0;
45579
46719
  }
45580
46720
  function convertAssistant(message) {
45581
- const blocks = [];
46721
+ const blocks2 = [];
45582
46722
  for (const block of message.content) {
45583
46723
  if (block.type === "text") {
45584
46724
  if (block.text.trim().length > 0)
45585
- blocks.push({ type: "text", text: sanitizeText(block.text) });
46725
+ blocks2.push({ type: "text", text: sanitizeText(block.text) });
45586
46726
  } else if (block.type === "thinking") {
45587
46727
  const signature2 = block.thinkingSignature ?? "";
45588
46728
  if (block.redacted && signature2) {
45589
- blocks.push({ type: "redacted_thinking", data: signature2 });
46729
+ blocks2.push({ type: "redacted_thinking", data: signature2 });
45590
46730
  } else if (signature2.trim().length > 0) {
45591
- blocks.push({ type: "thinking", thinking: sanitizeText(block.thinking), signature: signature2 });
46731
+ blocks2.push({ type: "thinking", thinking: sanitizeText(block.thinking), signature: signature2 });
45592
46732
  } else if (block.thinking.trim().length > 0) {
45593
- blocks.push({ type: "text", text: sanitizeText(block.thinking) });
46733
+ blocks2.push({ type: "text", text: sanitizeText(block.thinking) });
45594
46734
  }
45595
46735
  } else {
45596
- blocks.push({
46736
+ blocks2.push({
45597
46737
  type: "tool_use",
45598
46738
  id: normalizeToolId(block.id),
45599
46739
  name: block.name,
@@ -45601,7 +46741,7 @@ function convertAssistant(message) {
45601
46741
  });
45602
46742
  }
45603
46743
  }
45604
- return blocks.length > 0 ? { role: "assistant", content: blocks } : void 0;
46744
+ return blocks2.length > 0 ? { role: "assistant", content: blocks2 } : void 0;
45605
46745
  }
45606
46746
  function convertToolResult(message) {
45607
46747
  const content = typeof message.content === "string" || !message.content.some((b) => b.type === "image") ? sanitizeText(contentText2(message.content)) : convertBlocks(message.content);
@@ -46464,8 +47604,8 @@ function convertToolResults(model, results, compat) {
46464
47604
  const images = [];
46465
47605
  for (const result of results) {
46466
47606
  const text = contentText2(result.content);
46467
- const blocks = typeof result.content === "string" ? [] : result.content;
46468
- const hasImages2 = blocks.some((b) => b.type === "image");
47607
+ const blocks2 = typeof result.content === "string" ? [] : result.content;
47608
+ const hasImages2 = blocks2.some((b) => b.type === "image");
46469
47609
  const message = {
46470
47610
  role: "tool",
46471
47611
  tool_call_id: normalizeCallId(model, result.toolCallId),
@@ -46476,7 +47616,7 @@ function convertToolResults(model, results, compat) {
46476
47616
  if (compat.requiresToolResultName) message["name"] = result.toolName;
46477
47617
  messages.push(message);
46478
47618
  if (hasImages2 && model.input.includes("image")) {
46479
- for (const block of blocks) if (block.type === "image") images.push(imagePart(block));
47619
+ for (const block of blocks2) if (block.type === "image") images.push(imagePart(block));
46480
47620
  }
46481
47621
  }
46482
47622
  return { messages, images };
@@ -46726,9 +47866,9 @@ function imagePart2(block) {
46726
47866
  };
46727
47867
  }
46728
47868
  function convertUser4(message) {
46729
- const blocks = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
47869
+ const blocks2 = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
46730
47870
  const content = [];
46731
- for (const block of blocks) {
47871
+ for (const block of blocks2) {
46732
47872
  if (block.type === "image") content.push(imagePart2(block));
46733
47873
  else if (block.text.length > 0) {
46734
47874
  content.push({ type: "input_text", text: sanitizeText(block.text) });
@@ -47982,12 +49122,12 @@ function handleChunk2(chunk, state, tracker) {
47982
49122
  }
47983
49123
  const usage = obj6(chunk["usageMetadata"]);
47984
49124
  if (usage) output.usage = parseGoogleUsage(usage);
47985
- const blocked2 = str7(obj6(chunk["promptFeedback"])?.["blockReason"]);
49125
+ const blocked3 = str7(obj6(chunk["promptFeedback"])?.["blockReason"]);
47986
49126
  const candidates2 = Array.isArray(chunk["candidates"]) ? chunk["candidates"] : [];
47987
49127
  const candidate = obj6(candidates2[0]);
47988
- if (!candidate && blocked2) {
47989
- output.rawStopReason = blocked2;
47990
- throw new ProviderStopError(`Prompt blocked: ${blocked2}`);
49128
+ if (!candidate && blocked3) {
49129
+ output.rawStopReason = blocked3;
49130
+ throw new ProviderStopError(`Prompt blocked: ${blocked3}`);
47991
49131
  }
47992
49132
  const parts = obj6(candidate?.["content"])?.["parts"];
47993
49133
  if (Array.isArray(parts)) {
@@ -48198,7 +49338,7 @@ function defaultConfigDir(env = process.env) {
48198
49338
  function readAuthFile2(path, onWarning) {
48199
49339
  let raw;
48200
49340
  try {
48201
- raw = (0, import_node_fs53.readFileSync)(path, "utf8");
49341
+ raw = (0, import_node_fs54.readFileSync)(path, "utf8");
48202
49342
  } catch (error2) {
48203
49343
  const code = error2.code;
48204
49344
  if (code !== "ENOENT") onWarning?.(`cannot read auth file ${path}`);
@@ -48206,7 +49346,7 @@ function readAuthFile2(path, onWarning) {
48206
49346
  }
48207
49347
  if (process.platform !== "win32") {
48208
49348
  try {
48209
- const mode = (0, import_node_fs53.statSync)(path).mode & 511;
49349
+ const mode = (0, import_node_fs54.statSync)(path).mode & 511;
48210
49350
  if ((mode & 63) !== 0) {
48211
49351
  onWarning?.(
48212
49352
  `auth file ${path} has permissions ${mode.toString(8).padStart(4, "0")}; expected 0600`
@@ -48240,12 +49380,12 @@ function expandEnvRefs(value, env) {
48240
49380
  );
48241
49381
  return missing || out.length === 0 ? void 0 : out;
48242
49382
  }
48243
- var import_node_child_process13, import_node_fs53, import_node_os12, import_node_path75, ApiKeyResolver;
49383
+ var import_node_child_process13, import_node_fs54, import_node_os12, import_node_path75, ApiKeyResolver;
48244
49384
  var init_auth2 = __esm({
48245
49385
  "src/ai/providers/auth.ts"() {
48246
49386
  "use strict";
48247
49387
  import_node_child_process13 = require("node:child_process");
48248
- import_node_fs53 = require("node:fs");
49388
+ import_node_fs54 = require("node:fs");
48249
49389
  import_node_os12 = require("node:os");
48250
49390
  import_node_path75 = require("node:path");
48251
49391
  init_live();
@@ -49033,25 +50173,25 @@ var init_settings_registry = __esm({
49033
50173
 
49034
50174
  // src/config/write.ts
49035
50175
  function writeConfigFile(path, config, options = {}) {
49036
- (0, import_node_fs54.mkdirSync)((0, import_node_path77.dirname)(path), { recursive: true });
50176
+ (0, import_node_fs55.mkdirSync)((0, import_node_path77.dirname)(path), { recursive: true });
49037
50177
  let mode;
49038
50178
  try {
49039
- mode = (0, import_node_fs54.statSync)(path).mode & 511;
50179
+ mode = (0, import_node_fs55.statSync)(path).mode & 511;
49040
50180
  } catch {
49041
50181
  mode = void 0;
49042
50182
  }
49043
- if (mode !== void 0 && options.backup !== false) (0, import_node_fs54.copyFileSync)(path, `${path}.bak`);
50183
+ if (mode !== void 0 && options.backup !== false) (0, import_node_fs55.copyFileSync)(path, `${path}.bak`);
49044
50184
  const tmp = `${path}.${process.pid}.tmp`;
49045
- (0, import_node_fs54.writeFileSync)(tmp, `${JSON.stringify(config, null, 2)}
50185
+ (0, import_node_fs55.writeFileSync)(tmp, `${JSON.stringify(config, null, 2)}
49046
50186
  `, { mode: mode ?? 420 });
49047
- if (mode !== void 0) (0, import_node_fs54.chmodSync)(tmp, mode);
49048
- (0, import_node_fs54.renameSync)(tmp, path);
50187
+ if (mode !== void 0) (0, import_node_fs55.chmodSync)(tmp, mode);
50188
+ (0, import_node_fs55.renameSync)(tmp, path);
49049
50189
  }
49050
- var import_node_fs54, import_node_path77;
50190
+ var import_node_fs55, import_node_path77;
49051
50191
  var init_write2 = __esm({
49052
50192
  "src/config/write.ts"() {
49053
50193
  "use strict";
49054
- import_node_fs54 = require("node:fs");
50194
+ import_node_fs55 = require("node:fs");
49055
50195
  import_node_path77 = require("node:path");
49056
50196
  }
49057
50197
  });
@@ -49250,7 +50390,7 @@ function parseEnum(key, text, options) {
49250
50390
  function readTarget(path, scope) {
49251
50391
  let text;
49252
50392
  try {
49253
- text = (0, import_node_fs55.readFileSync)(path, "utf8");
50393
+ text = (0, import_node_fs56.readFileSync)(path, "utf8");
49254
50394
  } catch (error2) {
49255
50395
  if (error2.code === "ENOENT")
49256
50396
  return scope === "user" ? minimalConfig() : { version: CONFIG_FILE_VERSION };
@@ -49332,11 +50472,11 @@ function formatSettingValue(value) {
49332
50472
  if (typeof value === "string") return value;
49333
50473
  return JSON.stringify(value);
49334
50474
  }
49335
- var import_node_fs55, ConfigEditError, same, ABOVE, TRUE, FALSE, UNSET;
50475
+ var import_node_fs56, ConfigEditError, same, ABOVE, TRUE, FALSE, UNSET;
49336
50476
  var init_edit2 = __esm({
49337
50477
  "src/config/edit.ts"() {
49338
50478
  "use strict";
49339
- import_node_fs55 = require("node:fs");
50479
+ import_node_fs56 = require("node:fs");
49340
50480
  init_errors();
49341
50481
  init_i18n();
49342
50482
  init_init();
@@ -49416,7 +50556,7 @@ async function readClipboardImage(dataDir, deps = {}) {
49416
50556
  await (0, import_promises22.mkdir)(dir, { recursive: true });
49417
50557
  const stamp = new Date((deps.now ?? Date.now)()).toISOString().replace(/[:.]/g, "-");
49418
50558
  let file = (0, import_node_path80.join)(dir, `${stamp}.png`);
49419
- for (let n = 1; (0, import_node_fs61.existsSync)(file); n++) file = (0, import_node_path80.join)(dir, `${stamp}-${n}.png`);
50559
+ for (let n = 1; (0, import_node_fs62.existsSync)(file); n++) file = (0, import_node_path80.join)(dir, `${stamp}-${n}.png`);
49420
50560
  let anyTool = false;
49421
50561
  for (const attempt of attempts(platform, file, env)) {
49422
50562
  const options = attempt.via === "file" && attempt.env !== void 0 ? { env: attempt.env } : {};
@@ -49456,11 +50596,11 @@ async function gcClipboardImages(dataDir, options = {}) {
49456
50596
  }
49457
50597
  return removed;
49458
50598
  }
49459
- var import_node_fs61, import_promises22, import_node_path80, CLIPBOARD_DIR, CLIPBOARD_MAX_AGE_DAYS, OSASCRIPT, POWERSHELL;
50599
+ var import_node_fs62, import_promises22, import_node_path80, CLIPBOARD_DIR, CLIPBOARD_MAX_AGE_DAYS, OSASCRIPT, POWERSHELL;
49460
50600
  var init_clipboard_image = __esm({
49461
50601
  "src/tools/clipboard-image.ts"() {
49462
50602
  "use strict";
49463
- import_node_fs61 = require("node:fs");
50603
+ import_node_fs62 = require("node:fs");
49464
50604
  import_promises22 = require("node:fs/promises");
49465
50605
  import_node_path80 = require("node:path");
49466
50606
  init_image_file();
@@ -51175,6 +52315,8 @@ function toolSummary(name, args, cwd) {
51175
52315
  }
51176
52316
  case "task":
51177
52317
  return flat(str8("description") ?? str8("prompt") ?? "");
52318
+ case "web_fetch":
52319
+ return flat((str8("url") ?? "").replace(/^https?:\/\//i, ""));
51178
52320
  default:
51179
52321
  if (path !== void 0) return displayPath2(path, cwd);
51180
52322
  for (const key of [
@@ -51302,10 +52444,28 @@ function resultSummary(input2, theme) {
51302
52444
  ] : [];
51303
52445
  return muted([m.done, duration, ...tokens].join(" · "));
51304
52446
  }
52447
+ case "web_fetch": {
52448
+ const finalUrl = details["finalUrl"];
52449
+ if (num9(details["bytes"]) === 0 && typeof finalUrl === "string" && lines2[0]?.startsWith("Redirected"))
52450
+ return muted(m.fetchRedirect(finalUrl));
52451
+ const title = typeof details["title"] === "string" ? flat(details["title"], 60) : void 0;
52452
+ const format = typeof details["format"] === "string" ? details["format"] : "";
52453
+ return muted(m.fetched(title, formatSize(num9(details["bytes"]) ?? 0), format));
52454
+ }
51305
52455
  default:
51306
52456
  return muted(count2 === 0 ? m.done : m.outputLines(count2));
51307
52457
  }
51308
52458
  }
52459
+ function fetchHost(toolName, input2) {
52460
+ if (toolName !== "web_fetch") return void 0;
52461
+ const raw = record(input2)["url"];
52462
+ if (typeof raw !== "string") return void 0;
52463
+ try {
52464
+ return new URL(raw.trim()).hostname.replace(/^\[|\]$/g, "").toLowerCase() || void 0;
52465
+ } catch {
52466
+ return void 0;
52467
+ }
52468
+ }
51309
52469
  var import_node_path85;
51310
52470
  var init_tool_summary = __esm({
51311
52471
  "src/modes/interactive/tool-summary.ts"() {
@@ -51314,6 +52474,7 @@ var init_tool_summary = __esm({
51314
52474
  import_node_path85 = require("node:path");
51315
52475
  init_tui2();
51316
52476
  init_status_bar();
52477
+ init_truncate();
51317
52478
  }
51318
52479
  });
51319
52480
 
@@ -52128,7 +53289,7 @@ function childLoader(read2 = readTraceInput) {
52128
53289
  const load2 = ((file) => {
52129
53290
  let stamp = "";
52130
53291
  try {
52131
- const st = (0, import_node_fs64.statSync)(file);
53292
+ const st = (0, import_node_fs65.statSync)(file);
52132
53293
  stamp = `${st.mtimeMs}:${st.size}`;
52133
53294
  } catch {
52134
53295
  stamp = "missing";
@@ -52172,11 +53333,11 @@ function traceText(session, taskId) {
52172
53333
  `${msg().trace.taskTitle(node.taskId, node.agent)}${summary2}`
52173
53334
  );
52174
53335
  }
52175
- var import_node_fs64, LiveTracker, lineTraceCommand;
53336
+ var import_node_fs65, LiveTracker, lineTraceCommand;
52176
53337
  var init_session3 = __esm({
52177
53338
  "src/trace/session.ts"() {
52178
53339
  "use strict";
52179
- import_node_fs64 = require("node:fs");
53340
+ import_node_fs65 = require("node:fs");
52180
53341
  init_session2();
52181
53342
  init_i18n();
52182
53343
  init_scan();
@@ -52359,7 +53520,7 @@ function ancestorDirs(cwd) {
52359
53520
  let dir = (0, import_node_path89.resolve)(cwd);
52360
53521
  for (; ; ) {
52361
53522
  out.push(dir);
52362
- if ((0, import_node_fs66.existsSync)((0, import_node_path89.join)(dir, ".git"))) return out;
53523
+ if ((0, import_node_fs67.existsSync)((0, import_node_path89.join)(dir, ".git"))) return out;
52363
53524
  const parent = (0, import_node_path89.dirname)(dir);
52364
53525
  if (parent === dir) return out;
52365
53526
  dir = parent;
@@ -52421,7 +53582,7 @@ async function isDir3(path) {
52421
53582
  async function findSkillFiles(dir, depth = 0) {
52422
53583
  if (depth > MAX_DEPTH3) return [];
52423
53584
  const own = (0, import_node_path89.join)(dir, SKILL_FILE);
52424
- if ((0, import_node_fs66.existsSync)(own)) return [own];
53585
+ if ((0, import_node_fs67.existsSync)(own)) return [own];
52425
53586
  let names;
52426
53587
  try {
52427
53588
  names = await (0, import_promises24.readdir)(dir);
@@ -52473,11 +53634,11 @@ async function discoverSkills(sources, options) {
52473
53634
  }
52474
53635
  return result;
52475
53636
  }
52476
- var import_node_fs66, import_promises24, import_node_path89, SKILL_FILE, SKILL_NAME_RE, MAX_SKILL_NAME, MAX_SKILL_DESCRIPTION, MAX_DEPTH3, SKIP_DIRS;
53637
+ var import_node_fs67, import_promises24, import_node_path89, SKILL_FILE, SKILL_NAME_RE, MAX_SKILL_NAME, MAX_SKILL_DESCRIPTION, MAX_DEPTH3, SKIP_DIRS;
52477
53638
  var init_discover2 = __esm({
52478
53639
  "src/skills/discover.ts"() {
52479
53640
  "use strict";
52480
- import_node_fs66 = require("node:fs");
53641
+ import_node_fs67 = require("node:fs");
52481
53642
  import_promises24 = require("node:fs/promises");
52482
53643
  import_node_path89 = require("node:path");
52483
53644
  init_frontmatter2();
@@ -52601,7 +53762,7 @@ function resolveSandboxEntry(moduleUrl = __ama_import_meta_url) {
52601
53762
  candidates2.push((0, import_node_path90.join)(here, "sandbox-entry.js"));
52602
53763
  candidates2.push((0, import_node_path90.join)(here, "sandbox-entry.ts"));
52603
53764
  for (const candidate of candidates2) {
52604
- if ((0, import_node_fs67.existsSync)(candidate)) return (0, import_node_fs67.realpathSync)(candidate);
53765
+ if ((0, import_node_fs68.existsSync)(candidate)) return (0, import_node_fs68.realpathSync)(candidate);
52605
53766
  }
52606
53767
  return void 0;
52607
53768
  }
@@ -52833,12 +53994,12 @@ async function runSandbox(request) {
52833
53994
  child.stdin.end();
52834
53995
  return result;
52835
53996
  }
52836
- var import_node_child_process16, import_node_fs67, import_node_path90, import_node_url2, MAX_COLLECTED_OUTPUT_CHARS, ABORT_GRACE_MS, DEFAULT_CODEMODE_HEAP_MB, CODEMODE_OS_POLICY;
53997
+ var import_node_child_process16, import_node_fs68, import_node_path90, import_node_url2, MAX_COLLECTED_OUTPUT_CHARS, ABORT_GRACE_MS, DEFAULT_CODEMODE_HEAP_MB, CODEMODE_OS_POLICY;
52837
53998
  var init_host_side = __esm({
52838
53999
  "src/codemode/host-side.ts"() {
52839
54000
  "use strict";
52840
54001
  import_node_child_process16 = require("node:child_process");
52841
- import_node_fs67 = require("node:fs");
54002
+ import_node_fs68 = require("node:fs");
52842
54003
  import_node_path90 = require("node:path");
52843
54004
  import_node_url2 = require("node:url");
52844
54005
  init_detect();
@@ -53004,9 +54165,9 @@ ${output.slice(-half)}`;
53004
54165
  function saveFullOutput(ctx, text) {
53005
54166
  try {
53006
54167
  const dir = ctx.outputDir ?? (0, import_node_path91.join)((0, import_node_os14.tmpdir)(), "ama-codemode");
53007
- (0, import_node_fs68.mkdirSync)(dir, { recursive: true });
54168
+ (0, import_node_fs69.mkdirSync)(dir, { recursive: true });
53008
54169
  const file = (0, import_node_path91.join)(dir, `${ctx.toolCallId.replace(/[^A-Za-z0-9_-]/g, "_")}.txt`);
53009
- (0, import_node_fs68.writeFileSync)(file, text, "utf8");
54170
+ (0, import_node_fs69.writeFileSync)(file, text, "utf8");
53010
54171
  return file;
53011
54172
  } catch {
53012
54173
  return void 0;
@@ -53158,11 +54319,11 @@ function codemodeToolFactory(overrides = {}) {
53158
54319
  return createCodemodeTool(options);
53159
54320
  };
53160
54321
  }
53161
- var import_node_fs68, import_node_os14, import_node_path91, CODEMODE_TOOL_NAME2, DEFAULT_MAX_TOOL_RESULT_CHARS2, CODEMODE_EXAMPLE, SCRIPT_ONLY_GLOBALS, TAIL_CHARS, CODEMODE_ONLY_GUIDELINE;
54322
+ var import_node_fs69, import_node_os14, import_node_path91, CODEMODE_TOOL_NAME2, DEFAULT_MAX_TOOL_RESULT_CHARS2, CODEMODE_EXAMPLE, SCRIPT_ONLY_GLOBALS, TAIL_CHARS, CODEMODE_ONLY_GUIDELINE;
53162
54323
  var init_tool = __esm({
53163
54324
  "src/codemode/tool.ts"() {
53164
54325
  "use strict";
53165
- import_node_fs68 = require("node:fs");
54326
+ import_node_fs69 = require("node:fs");
53166
54327
  import_node_os14 = require("node:os");
53167
54328
  import_node_path91 = require("node:path");
53168
54329
  init_presets2();
@@ -53188,7 +54349,7 @@ var init_tool = __esm({
53188
54349
  });
53189
54350
 
53190
54351
  // src/memory/tool.ts
53191
- function fail5(input2, code, message) {
54352
+ function fail6(input2, code, message) {
53192
54353
  const details = { command: String(input2.command ?? ""), code };
53193
54354
  if (typeof input2.path === "string") details.path = input2.path;
53194
54355
  return { content: message, isError: true, details };
@@ -53220,11 +54381,11 @@ function createMemoryTool(runtime) {
53220
54381
  const write2 = command3 !== "view";
53221
54382
  if (ctx.depth > 0) {
53222
54383
  if (runtime.subagents === "off")
53223
- return fail5(input2, "subagent", "subagents cannot access memory");
53224
- if (write2) return fail5(input2, "subagent", "subagents cannot modify memory");
54384
+ return fail6(input2, "subagent", "subagents cannot access memory");
54385
+ if (write2) return fail6(input2, "subagent", "subagents cannot modify memory");
53225
54386
  }
53226
54387
  if (write2 && !runtime.writesEnabled)
53227
- return fail5(input2, "disabled", "memory writes are turned off for this session");
54388
+ return fail6(input2, "disabled", "memory writes are turned off for this session");
53228
54389
  const store = runtime.store;
53229
54390
  try {
53230
54391
  switch (command3) {
@@ -53244,7 +54405,7 @@ function createMemoryTool(runtime) {
53244
54405
  };
53245
54406
  }
53246
54407
  default:
53247
- return fail5(
54408
+ return fail6(
53248
54409
  input2,
53249
54410
  "invalid_command",
53250
54411
  'command must be "view", "create", "str_replace" or "delete"'
@@ -53252,7 +54413,7 @@ function createMemoryTool(runtime) {
53252
54413
  }
53253
54414
  } catch (error2) {
53254
54415
  if (error2 instanceof MemoryError || error2 instanceof MemoryLockError)
53255
- return fail5(input2, error2.code, error2.message);
54416
+ return fail6(input2, error2.code, error2.message);
53256
54417
  throw error2;
53257
54418
  }
53258
54419
  }
@@ -53276,7 +54437,7 @@ var init_tool2 = __esm({
53276
54437
  // src/cli/codemode-notice.ts
53277
54438
  function readNotices(path) {
53278
54439
  try {
53279
- const value = JSON.parse((0, import_node_fs69.readFileSync)(path, "utf8"));
54440
+ const value = JSON.parse((0, import_node_fs70.readFileSync)(path, "utf8"));
53280
54441
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
53281
54442
  const shown2 = value[KEY];
53282
54443
  return {
@@ -53300,20 +54461,20 @@ function takeCodemodeNotice(input2) {
53300
54461
  const shown2 = notices[KEY] ?? [];
53301
54462
  if (shown2.includes(input2.configDir)) return void 0;
53302
54463
  try {
53303
- (0, import_node_fs69.mkdirSync)(input2.dataDir, { recursive: true });
54464
+ (0, import_node_fs70.mkdirSync)(input2.dataDir, { recursive: true });
53304
54465
  const tmp = `${path}.${process.pid}.tmp`;
53305
- (0, import_node_fs69.writeFileSync)(tmp, `${JSON.stringify({ ...notices, [KEY]: [...shown2, input2.configDir] })}
54466
+ (0, import_node_fs70.writeFileSync)(tmp, `${JSON.stringify({ ...notices, [KEY]: [...shown2, input2.configDir] })}
53306
54467
  `);
53307
- (0, import_node_fs69.renameSync)(tmp, path);
54468
+ (0, import_node_fs70.renameSync)(tmp, path);
53308
54469
  } catch {
53309
54470
  }
53310
54471
  return codemodeNoticeText(input2.capability);
53311
54472
  }
53312
- var import_node_fs69, import_node_path92, NOTICES_FILE, KEY;
54473
+ var import_node_fs70, import_node_path92, NOTICES_FILE, KEY;
53313
54474
  var init_codemode_notice = __esm({
53314
54475
  "src/cli/codemode-notice.ts"() {
53315
54476
  "use strict";
53316
- import_node_fs69 = require("node:fs");
54477
+ import_node_fs70 = require("node:fs");
53317
54478
  import_node_path92 = require("node:path");
53318
54479
  init_presets2();
53319
54480
  init_i18n();
@@ -53330,7 +54491,7 @@ function readEntries(dataDir, now) {
53330
54491
  const out = /* @__PURE__ */ new Map();
53331
54492
  let parsed;
53332
54493
  try {
53333
- parsed = JSON.parse((0, import_node_fs70.readFileSync)(maxTokensCachePath(dataDir), "utf8"));
54494
+ parsed = JSON.parse((0, import_node_fs71.readFileSync)(maxTokensCachePath(dataDir), "utf8"));
53334
54495
  } catch {
53335
54496
  return out;
53336
54497
  }
@@ -53364,11 +54525,11 @@ function recordMaxTokensCap(dataDir, key, cap, now = Date.now) {
53364
54525
  const path = maxTokensCachePath(dataDir);
53365
54526
  const tmp = `${path}.${process.pid}.tmp`;
53366
54527
  try {
53367
- (0, import_node_fs70.mkdirSync)((0, import_node_path93.dirname)(path), { recursive: true, mode: 448 });
54528
+ (0, import_node_fs71.mkdirSync)((0, import_node_path93.dirname)(path), { recursive: true, mode: 448 });
53368
54529
  const caps = Object.fromEntries([...entries].sort(([a], [b]) => a < b ? -1 : 1));
53369
- (0, import_node_fs70.writeFileSync)(tmp, `${JSON.stringify({ version: MAX_TOKENS_CACHE_VERSION, caps }, null, 2)}
54530
+ (0, import_node_fs71.writeFileSync)(tmp, `${JSON.stringify({ version: MAX_TOKENS_CACHE_VERSION, caps }, null, 2)}
53370
54531
  `);
53371
- (0, import_node_fs70.renameSync)(tmp, path);
54532
+ (0, import_node_fs71.renameSync)(tmp, path);
53372
54533
  } catch {
53373
54534
  }
53374
54535
  }
@@ -53384,11 +54545,11 @@ function attachMaxTokensCache(dataDir, now = Date.now) {
53384
54545
  attached2.set(dataDir, detach);
53385
54546
  return detach;
53386
54547
  }
53387
- var import_node_fs70, import_node_path93, MAX_TOKENS_CACHE_VERSION, MAX_TOKENS_CACHE_TTL_MS, attached2;
54548
+ var import_node_fs71, import_node_path93, MAX_TOKENS_CACHE_VERSION, MAX_TOKENS_CACHE_TTL_MS, attached2;
53388
54549
  var init_max_tokens_cache = __esm({
53389
54550
  "src/ai/providers/max-tokens-cache.ts"() {
53390
54551
  "use strict";
53391
- import_node_fs70 = require("node:fs");
54552
+ import_node_fs71 = require("node:fs");
53392
54553
  import_node_path93 = require("node:path");
53393
54554
  init_max_tokens();
53394
54555
  MAX_TOKENS_CACHE_VERSION = 1;
@@ -53542,7 +54703,7 @@ function findImageRefs(text, cwd) {
53542
54703
  token = token.replace(/\\ /g, " ").replace(/[,\uFF0C\u3002;\uFF1B)\uFF09]+$/, "");
53543
54704
  if (token === "" || imageMimeFromPath(token) === void 0) continue;
53544
54705
  const path = expand(token, cwd);
53545
- if (!explicit && !(0, import_node_fs71.existsSync)(path)) continue;
54706
+ if (!explicit && !(0, import_node_fs72.existsSync)(path)) continue;
53546
54707
  if (seen.has(path)) continue;
53547
54708
  seen.add(path);
53548
54709
  out.push({ path, explicit });
@@ -53571,9 +54732,9 @@ async function loadPromptImages(refs, model, options = {}) {
53571
54732
  ...model !== void 0 ? { maxBase64Bytes: imageLimits(model).perImageBase64 } : {},
53572
54733
  ...options
53573
54734
  };
53574
- const blocks = [];
53575
- for (const ref of wanted) blocks.push((await loadLocalized(ref.path, fit)).block);
53576
- return blocks;
54735
+ const blocks2 = [];
54736
+ for (const ref of wanted) blocks2.push((await loadLocalized(ref.path, fit)).block);
54737
+ return blocks2;
53577
54738
  }
53578
54739
  async function promptImages(text, extra, cwd, model, options = {}) {
53579
54740
  const refs = [
@@ -53601,11 +54762,11 @@ async function loadLocalized(path, options) {
53601
54762
  throw error2;
53602
54763
  }
53603
54764
  }
53604
- var import_node_fs71, import_node_os15, import_node_path94, TOKEN;
54765
+ var import_node_fs72, import_node_os15, import_node_path94, TOKEN;
53605
54766
  var init_image_input = __esm({
53606
54767
  "src/modes/image-input.ts"() {
53607
54768
  "use strict";
53608
- import_node_fs71 = require("node:fs");
54769
+ import_node_fs72 = require("node:fs");
53609
54770
  import_node_os15 = require("node:os");
53610
54771
  import_node_path94 = require("node:path");
53611
54772
  init_channels();
@@ -54404,7 +55565,7 @@ function trustChoices() {
54404
55565
  }
54405
55566
  function isDirectory2(path) {
54406
55567
  try {
54407
- return (0, import_node_fs72.statSync)(path).isDirectory();
55568
+ return (0, import_node_fs73.statSync)(path).isDirectory();
54408
55569
  } catch {
54409
55570
  return false;
54410
55571
  }
@@ -54459,7 +55620,7 @@ function createStartupUi(options = {}) {
54459
55620
  validate: (text) => {
54460
55621
  if (text === "") return m.cwdEmpty;
54461
55622
  const abs = (0, import_node_path95.resolve)(text);
54462
- if (!(0, import_node_fs72.existsSync)(abs)) return m.cwdMissing(abs);
55623
+ if (!(0, import_node_fs73.existsSync)(abs)) return m.cwdMissing(abs);
54463
55624
  if (!isDirectory2(abs)) return m.cwdNotDir(abs);
54464
55625
  return void 0;
54465
55626
  }
@@ -54467,12 +55628,12 @@ function createStartupUi(options = {}) {
54467
55628
  }
54468
55629
  };
54469
55630
  }
54470
- var import_node_fs72, import_node_path95;
55631
+ var import_node_fs73, import_node_path95;
54471
55632
  var init_startup_ui = __esm({
54472
55633
  "src/modes/interactive/startup-ui.ts"() {
54473
55634
  "use strict";
54474
55635
  init_i18n();
54475
- import_node_fs72 = require("node:fs");
55636
+ import_node_fs73 = require("node:fs");
54476
55637
  import_node_path95 = require("node:path");
54477
55638
  init_tui2();
54478
55639
  init_model_items();
@@ -54913,8 +56074,8 @@ var init_startup_screen = __esm({
54913
56074
  });
54914
56075
 
54915
56076
  // src/modes/interactive/panels.ts
54916
- function keyValue(rows, theme, wrap2 = false) {
54917
- return new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap2 ? { wrap: true } : {} });
56077
+ function keyValue(rows, theme, wrap3 = false) {
56078
+ return new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap3 ? { wrap: true } : {} });
54918
56079
  }
54919
56080
  function cacheSection(session, theme, now) {
54920
56081
  return [
@@ -55212,7 +56373,7 @@ function contextPanel(session, theme) {
55212
56373
  const m = msg().report.contextReport;
55213
56374
  const snap = contextSnapshot(session);
55214
56375
  const { breakdown } = snap;
55215
- const table3 = (rows, wrap2 = false) => new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap2 ? { wrap: true } : {} });
56376
+ const table3 = (rows, wrap3 = false) => new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap3 ? { wrap: true } : {} });
55216
56377
  const largest = largestRows(breakdown);
55217
56378
  const parts = [
55218
56379
  table3(headerRows(snap)),
@@ -55271,14 +56432,20 @@ function command(name, key, args) {
55271
56432
  });
55272
56433
  return info2;
55273
56434
  }
56435
+ function availableCommands2(runtime, session, list5 = BUILTIN_COMMANDS) {
56436
+ const active = new Set(session.getTools().map((t) => t.name));
56437
+ const registered = new Set(runtime.tools.list());
56438
+ const has = (requires) => requires === "memory" ? registered.has("memory") : requires === "task" ? active.has("task") || active.has("codemode") && registered.has("task") : true;
56439
+ return list5.filter((c) => has(c.requires));
56440
+ }
55274
56441
  function parseSlash(line2) {
55275
56442
  const m = /^\/([A-Za-z?][\w-]*)(?:\s+([\s\S]*))?$/.exec(line2.trim());
55276
56443
  if (m === null) return void 0;
55277
56444
  const raw = m[1].toLowerCase();
55278
56445
  return { name: ALIASES[raw] ?? raw, args: (m[2] ?? "").trim() };
55279
56446
  }
55280
- function helpText2() {
55281
- const lines2 = BUILTIN_COMMANDS.map(
56447
+ function helpText2(ctx) {
56448
+ const lines2 = availableCommands2(ctx.runtime, ctx.session()).map(
55282
56449
  (c) => `/${c.name}${c.args !== void 0 ? ` ${c.args}` : ""} ${c.description}`
55283
56450
  );
55284
56451
  const m = msg().report.commands;
@@ -55325,7 +56492,7 @@ async function runSlashCommand(line2, ctx) {
55325
56492
  const session = ctx.session();
55326
56493
  switch (name) {
55327
56494
  case "help":
55328
- return { kind: "handled", message: helpText2() };
56495
+ return { kind: "handled", message: helpText2(ctx) };
55329
56496
  case "exit":
55330
56497
  return { kind: "exit" };
55331
56498
  case "new": {
@@ -55497,7 +56664,9 @@ var init_commands_core = __esm({
55497
56664
  command("cache", "cache", "[warm off|streaming|idle | fingerprint]"),
55498
56665
  command("statusline", "statusline", "[full|compact]"),
55499
56666
  command("plan", "plan", { key: "planArgs" }),
55500
- command("tasks", "tasks", "[id] | stop <id> | bg [id]"),
56667
+ Object.assign(command("tasks", "tasks", "[id] | stop <id> | bg [id]"), {
56668
+ requires: "task"
56669
+ }),
55501
56670
  command("agents", "agents"),
55502
56671
  command("paste", "paste"),
55503
56672
  command("interrupt", "interrupt", { key: "interruptArgs" }),
@@ -55505,7 +56674,9 @@ var init_commands_core = __esm({
55505
56674
  // [W6-C0] 第六波(W6-S / W6-T1 / W6-M 实现)
55506
56675
  command("config", "config", "[key=value]"),
55507
56676
  command("trace", "trace", { key: "traceArgs" }),
55508
- command("memory", "memory", { key: "memoryArgs" })
56677
+ Object.assign(command("memory", "memory", { key: "memoryArgs" }), {
56678
+ requires: "memory"
56679
+ })
55509
56680
  ];
55510
56681
  ALIASES = { quit: "exit", q: "exit", "?": "help" };
55511
56682
  }
@@ -55605,9 +56776,9 @@ async function availableModels(runtime) {
55605
56776
  }
55606
56777
  return out;
55607
56778
  }
55608
- function commands(runtime) {
56779
+ function commands(runtime, session) {
55609
56780
  return [
55610
- ...BUILTIN_COMMANDS.map((c) => ({
56781
+ ...availableCommands2(runtime, session).map((c) => ({
55611
56782
  name: c.name,
55612
56783
  description: c.description,
55613
56784
  source: "builtin"
@@ -55817,7 +56988,7 @@ var init_commands = __esm({
55817
56988
  ctx.session().setPermissionMode(p.mode);
55818
56989
  return { mode: p.mode };
55819
56990
  },
55820
- get_commands: async (_p, ctx) => ({ commands: commands(ctx.runtime) }),
56991
+ get_commands: async (_p, ctx) => ({ commands: commands(ctx.runtime, ctx.session()) }),
55821
56992
  get_skills: async (_p, ctx) => ({ skills: ctx.runtime.resources.skills }),
55822
56993
  // [W5-F] 计划 / 任务命令(docs/history/wave5-plan.md §6.5、§7.5)
55823
56994
  plan_response: async (p, ctx) => {
@@ -56312,8 +57483,8 @@ var init_config_panel = __esm({
56312
57483
  const dynamic = value.value === void 0 ? settingDynamicDefault(row3.id) : void 0;
56313
57484
  const text = dynamic === void 0 ? doc : `${doc} · ${dynamic}`;
56314
57485
  const spec2 = settingsRegistry().find((x) => x.key === row3.id);
56315
- const blocked2 = spec2 === void 0 ? void 0 : blockedReason(spec2, value, this.scope);
56316
- const lines2 = blocked2 === void 0 ? [] : [theme.fg("warning", truncateToWidth(blocked2, width2))];
57486
+ const blocked3 = spec2 === void 0 ? void 0 : blockedReason(spec2, value, this.scope);
57487
+ const lines2 = blocked3 === void 0 ? [] : [theme.fg("warning", truncateToWidth(blocked3, width2))];
56317
57488
  if (text !== "") lines2.push(...wrapTextWithAnsi(theme.fg("dim", text), width2));
56318
57489
  return lines2.slice(0, 3);
56319
57490
  }
@@ -57356,8 +58527,8 @@ var init_config_ui = __esm({
57356
58527
  const spec2 = settingSpec(key);
57357
58528
  if (panel === void 0 || spec2 === void 0) return;
57358
58529
  const current3 = getConfigValue(readSnapshot(this.input()), key);
57359
- const blocked2 = blockedReason(spec2, current3, scope);
57360
- if (blocked2 !== void 0) return panel.setNotice(blocked2, "warning");
58530
+ const blocked3 = blockedReason(spec2, current3, scope);
58531
+ if (blocked3 !== void 0) return panel.setNotice(blocked3, "warning");
57361
58532
  const value = current3.value;
57362
58533
  switch (spec2.kind) {
57363
58534
  case "bool":
@@ -57647,27 +58818,27 @@ function runEditor(command3, file) {
57647
58818
  return result.status;
57648
58819
  }
57649
58820
  async function editExternally(text, name, deps) {
57650
- const dir = (0, import_node_fs73.mkdtempSync)((0, import_node_path97.join)((0, import_node_os16.tmpdir)(), "ama-edit-"));
58821
+ const dir = (0, import_node_fs74.mkdtempSync)((0, import_node_path97.join)((0, import_node_os16.tmpdir)(), "ama-edit-"));
57651
58822
  const file = (0, import_node_path97.join)(dir, name);
57652
- (0, import_node_fs73.writeFileSync)(file, text);
58823
+ (0, import_node_fs74.writeFileSync)(file, text);
57653
58824
  deps.suspend();
57654
58825
  try {
57655
58826
  const code = (deps.run ?? runEditor)(editorCommand(deps.env, deps.platform), file);
57656
58827
  if (code !== 0) return void 0;
57657
- return (0, import_node_fs73.readFileSync)(file, "utf8");
58828
+ return (0, import_node_fs74.readFileSync)(file, "utf8");
57658
58829
  } catch {
57659
58830
  return void 0;
57660
58831
  } finally {
57661
58832
  deps.resume();
57662
- (0, import_node_fs73.rmSync)(dir, { recursive: true, force: true });
58833
+ (0, import_node_fs74.rmSync)(dir, { recursive: true, force: true });
57663
58834
  }
57664
58835
  }
57665
- var import_node_child_process17, import_node_fs73, import_node_os16, import_node_path97;
58836
+ var import_node_child_process17, import_node_fs74, import_node_os16, import_node_path97;
57666
58837
  var init_external_editor = __esm({
57667
58838
  "src/modes/interactive/external-editor.ts"() {
57668
58839
  "use strict";
57669
58840
  import_node_child_process17 = require("node:child_process");
57670
- import_node_fs73 = require("node:fs");
58841
+ import_node_fs74 = require("node:fs");
57671
58842
  import_node_os16 = require("node:os");
57672
58843
  import_node_path97 = require("node:path");
57673
58844
  }
@@ -58631,6 +59802,10 @@ function describeRequest(request, theme, options = {}) {
58631
59802
  const { shown: shown2, hidden } = lines(input2["command"], COMMAND_LINES);
58632
59803
  out.push(...shown2.map((l, i) => theme.fg("code", (i === 0 ? "$ " : " ") + l)));
58633
59804
  if (hidden > 0) out.push(more(hidden));
59805
+ } else if (request.toolName === "web_fetch" && typeof input2["url"] === "string") {
59806
+ const { shown: shown2, hidden } = lines(input2["url"].replace(/(.{72})/g, "$1\n"), COMMAND_LINES);
59807
+ out.push(...shown2.map((l) => theme.fg("code", l)));
59808
+ if (hidden > 0) out.push(more(hidden));
58634
59809
  } else if (request.toolName === "write" && typeof input2["content"] === "string") {
58635
59810
  const count2 = lines(input2["content"], Number.MAX_SAFE_INTEGER).shown.length;
58636
59811
  out.push(
@@ -58699,8 +59874,10 @@ function approvalOutcomeText(request, outcome, taskAgent) {
58699
59874
  switch (outcome) {
58700
59875
  case "allow":
58701
59876
  return m.allow(what);
58702
- case "allow_session":
58703
- return m.allowSession(what);
59877
+ case "allow_session": {
59878
+ const host = fetchHost(request.toolName, request.input);
59879
+ return host === void 0 ? m.allowSession(what) : m.allowSessionHost(what, host);
59880
+ }
58704
59881
  case "deny":
58705
59882
  return m.deny(what);
58706
59883
  case "cancelled":
@@ -58790,10 +59967,12 @@ var init_approval_dialog = __esm({
58790
59967
  const { theme } = this.host;
58791
59968
  const g = theme.glyphs;
58792
59969
  const keyWidth = compact ? 1 : Math.max(...APPROVAL_OPTIONS.map((o) => o.keys.length));
59970
+ const host = fetchHost(this.request.toolName, this.request.input);
58793
59971
  return APPROVAL_OPTIONS.map((option2, i) => {
58794
59972
  const selected = i === this.selected;
58795
59973
  const keys = compact ? option2.keys.slice(0, 1) : option2.keys;
58796
- const left = `${selected ? g.prompt : " "} ${i + 1}. ${option2.label}`;
59974
+ const text = option2.decision === "allow_session" && host !== void 0 ? msg().approval.option.allowSessionHost(host) : option2.label;
59975
+ const left = `${selected ? g.prompt : " "} ${i + 1}. ${text}`;
58797
59976
  const right = keys.padEnd(keyWidth);
58798
59977
  const room = Math.max(1, width2 - visibleWidth(right) - 1);
58799
59978
  const label2 = selected ? theme.fg("accent", theme.bold(left)) : left;
@@ -61212,6 +62391,9 @@ function command2(name, key) {
61212
62391
  }
61213
62392
  };
61214
62393
  }
62394
+ function completionCommands(runtime, session) {
62395
+ return availableCommands2(runtime, session, ALL_COMMANDS);
62396
+ }
61215
62397
  function keyHints() {
61216
62398
  return msg().interactive.commands.keyHints;
61217
62399
  }
@@ -64336,7 +65518,7 @@ var init_program_status = __esm({
64336
65518
  });
64337
65519
 
64338
65520
  // src/modes/interactive/program-status-ui.ts
64339
- function blocked(kind, text) {
65521
+ function blocked2(kind, text) {
64340
65522
  return { state: "blocked", kind, msg: text };
64341
65523
  }
64342
65524
  function programStatusUi(deps) {
@@ -64492,9 +65674,9 @@ var init_program_status_ui = __esm({
64492
65674
  /** 当前根记录(测试也用)。 */
64493
65675
  root() {
64494
65676
  const pending = [...this.approvals.values()].at(-1);
64495
- if (pending !== void 0) return blocked("permission", pending.msg);
64496
- if (this.plan !== void 0) return blocked("question", this.plan);
64497
- if (this.auth !== void 0) return blocked("auth", this.auth);
65677
+ if (pending !== void 0) return blocked2("permission", pending.msg);
65678
+ if (this.plan !== void 0) return blocked2("question", this.plan);
65679
+ if (this.auth !== void 0) return blocked2("auth", this.auth);
64498
65680
  if (this.running)
64499
65681
  return { state: "working", ...this.activity !== void 0 ? { msg: this.activity } : {} };
64500
65682
  return this.outcome ?? { state: "idle" };
@@ -64618,7 +65800,7 @@ function runInteractiveMode(runtime, context, options = {}) {
64618
65800
  const { hint, bar: status } = area;
64619
65801
  const historyFile = options.historyFile === false ? void 0 : options.historyFile ?? (0, import_node_path101.join)(runtime.paths.dataDir, "history");
64620
65802
  const completion = new InteractiveCompletion({
64621
- commands: () => ALL_COMMANDS,
65803
+ commands: () => completionCommands(runtime, session),
64622
65804
  prompts: () => runtime.resources.prompts,
64623
65805
  skills: () => runtime.resources.skills,
64624
65806
  cwd: () => session.state.cwd,
@@ -65061,7 +66243,7 @@ function choose(answer, values) {
65061
66243
  }
65062
66244
  function isDirectory3(path) {
65063
66245
  try {
65064
- return (0, import_node_fs74.statSync)(path).isDirectory();
66246
+ return (0, import_node_fs75.statSync)(path).isDirectory();
65065
66247
  } catch {
65066
66248
  return false;
65067
66249
  }
@@ -65106,7 +66288,7 @@ function createTextStartupUi(io) {
65106
66288
  const answer = (await ask([m.cwdMissing(missing)], m.cwdAnswer))?.trim();
65107
66289
  if (answer === void 0 || answer === "") return void 0;
65108
66290
  const abs = (0, import_node_path102.resolve)(answer);
65109
- if (!(0, import_node_fs74.existsSync)(abs) || !isDirectory3(abs)) {
66291
+ if (!(0, import_node_fs75.existsSync)(abs) || !isDirectory3(abs)) {
65110
66292
  io.write(m.notDirectory(abs));
65111
66293
  return void 0;
65112
66294
  }
@@ -65114,11 +66296,11 @@ function createTextStartupUi(io) {
65114
66296
  }
65115
66297
  };
65116
66298
  }
65117
- var import_node_fs74, import_node_path102;
66299
+ var import_node_fs75, import_node_path102;
65118
66300
  var init_startup_ui_text = __esm({
65119
66301
  "src/modes/startup-ui-text.ts"() {
65120
66302
  "use strict";
65121
- import_node_fs74 = require("node:fs");
66303
+ import_node_fs75 = require("node:fs");
65122
66304
  import_node_path102 = require("node:path");
65123
66305
  init_i18n();
65124
66306
  }
@@ -65155,6 +66337,7 @@ function createTools(input2, options, state) {
65155
66337
  };
65156
66338
  const builtins = builtinTools({
65157
66339
  bash,
66340
+ ...options.webFetch !== void 0 ? { webFetch: options.webFetch } : {},
65158
66341
  read: {
65159
66342
  supportsImages: (ctx2) => modelAcceptsImages(state, ctx2.model),
65160
66343
  imageOptions: (ctx2) => imageFitOptionsFor(state.providers, ctx2.model, input2.config.images)
@@ -65564,7 +66747,7 @@ var init_memory3 = __esm({
65564
66747
  });
65565
66748
 
65566
66749
  // src/cli/main.ts
65567
- var import_node_fs75 = require("node:fs");
66750
+ var import_node_fs76 = require("node:fs");
65568
66751
  var import_node_path104 = require("node:path");
65569
66752
  var import_node_url3 = require("node:url");
65570
66753
  init_version();
@@ -66101,7 +67284,7 @@ init_token_client();
66101
67284
 
66102
67285
  // src/auth/chatgpt/host-id.ts
66103
67286
  var import_node_crypto19 = require("node:crypto");
66104
- var import_node_fs48 = require("node:fs");
67287
+ var import_node_fs49 = require("node:fs");
66105
67288
  var import_node_path70 = require("node:path");
66106
67289
  var HOST_ID_FILE = "chatgpt-host.json";
66107
67290
  function hostIdPath(dataDir) {
@@ -66110,14 +67293,14 @@ function hostIdPath(dataDir) {
66110
67293
  function readOrCreateHostId(dataDir) {
66111
67294
  const path = hostIdPath(dataDir);
66112
67295
  try {
66113
- const value = JSON.parse((0, import_node_fs48.readFileSync)(path, "utf8"));
67296
+ const value = JSON.parse((0, import_node_fs49.readFileSync)(path, "utf8"));
66114
67297
  if (typeof value.hostId === "string" && /^urn:uuid:[0-9a-f-]{36}$/i.test(value.hostId))
66115
67298
  return value.hostId;
66116
67299
  } catch {
66117
67300
  }
66118
67301
  const hostId = `urn:uuid:${(0, import_node_crypto19.randomUUID)()}`;
66119
- (0, import_node_fs48.mkdirSync)((0, import_node_path70.dirname)(path), { recursive: true, mode: 448 });
66120
- (0, import_node_fs48.writeFileSync)(path, `${JSON.stringify({ version: 1, hostId }, null, 2)}
67302
+ (0, import_node_fs49.mkdirSync)((0, import_node_path70.dirname)(path), { recursive: true, mode: 448 });
67303
+ (0, import_node_fs49.writeFileSync)(path, `${JSON.stringify({ version: 1, hostId }, null, 2)}
66121
67304
  `, { mode: 384 });
66122
67305
  return hostId;
66123
67306
  }
@@ -66630,7 +67813,7 @@ init_merge();
66630
67813
  init_key_docs();
66631
67814
  init_paths();
66632
67815
  var import_node_child_process14 = require("node:child_process");
66633
- var import_node_fs57 = require("node:fs");
67816
+ var import_node_fs58 = require("node:fs");
66634
67817
  var import_node_path79 = require("node:path");
66635
67818
  init_models_dev_cache();
66636
67819
  init_init();
@@ -66692,7 +67875,7 @@ init_ansi();
66692
67875
  // src/cli/subcommands/config-set.ts
66693
67876
  init_i18n();
66694
67877
  init_edit2();
66695
- var import_node_fs56 = require("node:fs");
67878
+ var import_node_fs57 = require("node:fs");
66696
67879
  var import_node_path78 = require("node:path");
66697
67880
  init_init();
66698
67881
  init_paths();
@@ -66822,7 +68005,7 @@ async function runConfigEdit(action, argv, io, deps = {}) {
66822
68005
  if (key === "permission.mode" && value === "full-auto" && scope === "user") {
66823
68006
  if (!await confirmBypass(io, flags2.has("yes"), deps)) return ExitCode.Config;
66824
68007
  }
66825
- if (scope === "user" && !(0, import_node_fs56.existsSync)((0, import_node_path78.join)(input2.configDir, CONFIG_FILE)))
68008
+ if (scope === "user" && !(0, import_node_fs57.existsSync)((0, import_node_path78.join)(input2.configDir, CONFIG_FILE)))
66826
68009
  initConfigDir(input2.configDir);
66827
68010
  report(io, setConfigValue({ ...input2, scope, key, value }), value);
66828
68011
  return ExitCode.Ok;
@@ -66835,7 +68018,7 @@ function showPaths(io) {
66835
68018
  const configDir = resolveConfigDir({ env: io.env });
66836
68019
  const dataDir = resolveDataDir({ env: io.env });
66837
68020
  const m = msg().subcommands.configShow;
66838
- const mark = (path) => `${path}${(0, import_node_fs57.existsSync)(path) ? "" : m.missing}`;
68021
+ const mark = (path) => `${path}${(0, import_node_fs58.existsSync)(path) ? "" : m.missing}`;
66839
68022
  const width2 = Math.max(...[m.configDir, m.dataDir, m.projectLevel].map(visibleWidth)) + 2;
66840
68023
  const label2 = (text) => padToWidth(text, width2);
66841
68024
  const lines2 = [
@@ -66856,7 +68039,7 @@ function showPaths(io) {
66856
68039
  function editConfig(io) {
66857
68040
  const configDir = resolveConfigDir({ env: io.env });
66858
68041
  const path = (0, import_node_path79.join)(configDir, CONFIG_FILE);
66859
- if (!(0, import_node_fs57.existsSync)(path)) initConfigDir(configDir);
68042
+ if (!(0, import_node_fs58.existsSync)(path)) initConfigDir(configDir);
66860
68043
  const editor = (io.env["VISUAL"] ?? io.env["EDITOR"] ?? "").trim();
66861
68044
  if (editor === "") {
66862
68045
  io.stdout(msg().subcommands.configShow.noEditor(path));
@@ -67148,7 +68331,7 @@ async function runConfig(argv, io, deps) {
67148
68331
  }
67149
68332
 
67150
68333
  // src/cli/subcommands/doctor.ts
67151
- var import_node_fs58 = require("node:fs");
68334
+ var import_node_fs59 = require("node:fs");
67152
68335
  init_blobs();
67153
68336
  init_shadow_git();
67154
68337
  init_gc();
@@ -67167,7 +68350,7 @@ init_args();
67167
68350
  init_exit_codes();
67168
68351
 
67169
68352
  // src/cli/proxy.ts
67170
- var http = __toESM(require("node:http"), 1);
68353
+ var http2 = __toESM(require("node:http"), 1);
67171
68354
  init_i18n();
67172
68355
  function pick5(env, name) {
67173
68356
  const value = env[name.toLowerCase()] ?? env[name.toUpperCase()];
@@ -67175,18 +68358,18 @@ function pick5(env, name) {
67175
68358
  }
67176
68359
  function readProxyEnv(env) {
67177
68360
  const out = {};
67178
- const https = pick5(env, "https_proxy");
68361
+ const https2 = pick5(env, "https_proxy");
67179
68362
  const plain = pick5(env, "http_proxy");
67180
68363
  const no = pick5(env, "no_proxy");
67181
- if (https !== void 0) out.httpsProxy = https;
68364
+ if (https2 !== void 0) out.httpsProxy = https2;
67182
68365
  if (plain !== void 0) out.httpProxy = plain;
67183
68366
  if (no !== void 0) out.noProxy = no;
67184
68367
  return out;
67185
68368
  }
67186
68369
  function defaultProxyRuntime() {
67187
- const fn = http.setGlobalProxyFromEnv;
68370
+ const fn = http2.setGlobalProxyFromEnv;
67188
68371
  return {
67189
- setGlobalProxyFromEnv: typeof fn === "function" ? fn.bind(http) : void 0,
68372
+ setGlobalProxyFromEnv: typeof fn === "function" ? fn.bind(http2) : void 0,
67190
68373
  execArgv: process.execArgv,
67191
68374
  version: process.versions.node
67192
68375
  };
@@ -67521,7 +68704,7 @@ async function runDoctor(argv, io, deps) {
67521
68704
  const m = msg().report.doctor;
67522
68705
  report2.section(m.dirsTitle);
67523
68706
  report2.item(m.configDir(level.configDir));
67524
- report2.item(m.dataDir(level.dataDir, (0, import_node_fs58.existsSync)(level.dataDir)));
68707
+ report2.item(m.dataDir(level.dataDir, (0, import_node_fs59.existsSync)(level.dataDir)));
67525
68708
  await fileHistoryLine(report2, level.dataDir);
67526
68709
  report2.section(m.layersTitle);
67527
68710
  report2.item(m.builtinDefaults);
@@ -67749,7 +68932,7 @@ init_discovered_cache();
67749
68932
  init_catalog2();
67750
68933
  init_models_dev_cache();
67751
68934
  init_models_dev();
67752
- var import_node_fs59 = require("node:fs");
68935
+ var import_node_fs60 = require("node:fs");
67753
68936
  init_load();
67754
68937
  init_write2();
67755
68938
  init_args();
@@ -68112,7 +69295,7 @@ function writeEntries(ctx, provider, entries, notes) {
68112
69295
  return 0;
68113
69296
  }
68114
69297
  models.push(...added);
68115
- const backup = (0, import_node_fs59.existsSync)(path);
69298
+ const backup = (0, import_node_fs60.existsSync)(path);
68116
69299
  writeConfigFile(path, config, { backup: true });
68117
69300
  io.stdout(m.written(path, provider.id, added.length, kept, backup));
68118
69301
  if (notes.noTools.size > 0) io.stdout(m.skippedNoTools([...notes.noTools].join(", ")));
@@ -68537,7 +69720,7 @@ async function runModels(argv, io, deps) {
68537
69720
  }
68538
69721
 
68539
69722
  // src/cli/subcommands/providers.ts
68540
- var import_node_fs60 = require("node:fs");
69723
+ var import_node_fs61 = require("node:fs");
68541
69724
  init_models_dev_cache();
68542
69725
  init_models_dev();
68543
69726
  init_auth_file();
@@ -68884,7 +70067,7 @@ function yesNo(value) {
68884
70067
  const t = msg().subcommands.providersPlan;
68885
70068
  return value === void 0 ? "?" : value ? t.yes : t.no;
68886
70069
  }
68887
- function renderTable2(plans2) {
70070
+ function renderTable3(plans2) {
68888
70071
  const h = msg().subcommands.providersPlan.head;
68889
70072
  const head = [
68890
70073
  h.model,
@@ -69140,7 +70323,7 @@ async function add(ctx, id, refresh) {
69140
70323
  });
69141
70324
  if (result.cancelled !== void 0) return result.cancelled;
69142
70325
  io.stdout(`
69143
- ${renderTable2(result.plans)}`);
70326
+ ${renderTable3(result.plans)}`);
69144
70327
  if (refresh) {
69145
70328
  const gone = [...existingIds].filter((m2) => !listed2.some((l) => l.id === m2));
69146
70329
  if (gone.length > 0) io.stdout(m.goneUpstream(gone));
@@ -69194,7 +70377,7 @@ ${renderTable2(result.plans)}`);
69194
70377
  const providers = config.providers ??= {};
69195
70378
  providers[id] = merged.config;
69196
70379
  if (defaultPick !== void 0) config.defaultModel = `${id}/${defaultPick.id}`;
69197
- const backup = (0, import_node_fs60.existsSync)(level.userConfigPath);
70380
+ const backup = (0, import_node_fs61.existsSync)(level.userConfigPath);
69198
70381
  writeConfigFile(level.userConfigPath, config, { backup: true });
69199
70382
  if (key.store !== void 0) setAuthKey(level.authFile, id, key.store);
69200
70383
  io.stdout(m.written(level.userConfigPath, backup));
@@ -69319,7 +70502,7 @@ init_reuse();
69319
70502
  init_clipboard_image();
69320
70503
 
69321
70504
  // src/cli/subcommands/sessions-export.ts
69322
- var import_node_fs62 = require("node:fs");
70505
+ var import_node_fs63 = require("node:fs");
69323
70506
  var import_node_path81 = require("node:path");
69324
70507
  init_paths();
69325
70508
 
@@ -69551,7 +70734,7 @@ async function runSessionsExport(argv, io) {
69551
70734
  return ExitCode.Ok;
69552
70735
  }
69553
70736
  const target = (0, import_node_path81.resolve)(io.cwd, expandHome(output, { env: io.env }));
69554
- (0, import_node_fs62.writeFileSync)(target, text, { mode: 384 });
70737
+ (0, import_node_fs63.writeFileSync)(target, text, { mode: 384 });
69555
70738
  io.stderr(msg().subcommands.sessionsExport.exported(target));
69556
70739
  return ExitCode.Ok;
69557
70740
  }
@@ -69883,7 +71066,7 @@ function aggregateStats(summaries, options = {}) {
69883
71066
  }
69884
71067
 
69885
71068
  // src/session/stats-index.ts
69886
- var import_node_fs63 = require("node:fs");
71069
+ var import_node_fs64 = require("node:fs");
69887
71070
  var import_node_path82 = require("node:path");
69888
71071
 
69889
71072
  // src/session/stats-scan.ts
@@ -70097,7 +71280,7 @@ function collectSummaries(files, options = {}) {
70097
71280
  let index2 = { version: INDEX_VERSION, zone, files: {} };
70098
71281
  if (options.indexFile !== void 0) {
70099
71282
  try {
70100
- const loaded = JSON.parse((0, import_node_fs63.readFileSync)(options.indexFile, "utf8"));
71283
+ const loaded = JSON.parse((0, import_node_fs64.readFileSync)(options.indexFile, "utf8"));
70101
71284
  if (loaded.version === INDEX_VERSION && loaded.zone === zone && typeof loaded.files === "object")
70102
71285
  index2 = loaded;
70103
71286
  } catch {
@@ -70108,7 +71291,7 @@ function collectSummaries(files, options = {}) {
70108
71291
  for (const file of files) {
70109
71292
  let stat16;
70110
71293
  try {
70111
- stat16 = (0, import_node_fs63.statSync)(file);
71294
+ stat16 = (0, import_node_fs64.statSync)(file);
70112
71295
  } catch {
70113
71296
  result.invalid++;
70114
71297
  continue;
@@ -70138,10 +71321,10 @@ function collectSummaries(files, options = {}) {
70138
71321
  }
70139
71322
  if (dirty && options.indexFile !== void 0) {
70140
71323
  try {
70141
- (0, import_node_fs63.mkdirSync)((0, import_node_path82.dirname)(options.indexFile), { recursive: true, mode: 448 });
71324
+ (0, import_node_fs64.mkdirSync)((0, import_node_path82.dirname)(options.indexFile), { recursive: true, mode: 448 });
70142
71325
  const tmp = `${options.indexFile}.${process.pid}.tmp`;
70143
- (0, import_node_fs63.writeFileSync)(tmp, JSON.stringify(index2), { mode: 384 });
70144
- (0, import_node_fs63.renameSync)(tmp, options.indexFile);
71326
+ (0, import_node_fs64.writeFileSync)(tmp, JSON.stringify(index2), { mode: 384 });
71327
+ (0, import_node_fs64.renameSync)(tmp, options.indexFile);
70145
71328
  } catch {
70146
71329
  }
70147
71330
  }
@@ -70427,7 +71610,7 @@ async function runSessionsSearch(argv, io) {
70427
71610
 
70428
71611
  // src/cli/subcommands/sessions-trace.ts
70429
71612
  var import_node_child_process15 = require("node:child_process");
70430
- var import_node_fs65 = require("node:fs");
71613
+ var import_node_fs66 = require("node:fs");
70431
71614
  var import_node_os13 = require("node:os");
70432
71615
  var import_node_path86 = require("node:path");
70433
71616
  init_paths();
@@ -70835,9 +72018,9 @@ function openCommand(platform, file) {
70835
72018
  return { command: "xdg-open", args: [file] };
70836
72019
  }
70837
72020
  function spawnOpen({ command: command3, args }) {
70838
- return new Promise((done, fail6) => {
72021
+ return new Promise((done, fail7) => {
70839
72022
  const child = (0, import_node_child_process15.spawn)(command3, args, { detached: true, stdio: "ignore" });
70840
- child.once("error", fail6);
72023
+ child.once("error", fail7);
70841
72024
  child.once("spawn", () => {
70842
72025
  child.unref();
70843
72026
  done();
@@ -70866,7 +72049,7 @@ function splitHtmlFlag(argv) {
70866
72049
  }
70867
72050
  function locate(io, target, dirFlag) {
70868
72051
  const asPath = (0, import_node_path86.resolve)(io.cwd, expandHome(target, { env: io.env }));
70869
- if (/[\\/]|\.jsonl$/.test(target) && (0, import_node_fs65.existsSync)(asPath)) return asPath;
72052
+ if (/[\\/]|\.jsonl$/.test(target) && (0, import_node_fs66.existsSync)(asPath)) return asPath;
70870
72053
  const root = dirFlag !== void 0 ? (0, import_node_path86.resolve)(io.cwd, expandHome(dirFlag, { env: io.env })) : (0, import_node_path86.join)(resolveDataDir({ env: io.env }), "sessions");
70871
72054
  return findSessionFileReadOnly(root, target, io.cwd);
70872
72055
  }
@@ -70939,7 +72122,7 @@ async function runSessionsTrace(argv, io, deps = {}) {
70939
72122
  io.stdout(text);
70940
72123
  return ExitCode.Ok;
70941
72124
  }
70942
- (0, import_node_fs65.writeFileSync)(outPath, text, { mode: 384 });
72125
+ (0, import_node_fs66.writeFileSync)(outPath, text, { mode: 384 });
70943
72126
  io.stderr(m.wrote(outPath));
70944
72127
  if (open3) {
70945
72128
  try {
@@ -71140,7 +72323,7 @@ function readStdinDefault() {
71140
72323
  }
71141
72324
  function stdinKindDefault() {
71142
72325
  try {
71143
- const stat16 = (0, import_node_fs75.fstatSync)(0);
72326
+ const stat16 = (0, import_node_fs76.fstatSync)(0);
71144
72327
  if (stat16.isFile()) return "file";
71145
72328
  if (stat16.isFIFO()) return "fifo";
71146
72329
  if (stat16.isSocket()) return "socket";
@@ -71200,7 +72383,7 @@ function defaultIo() {
71200
72383
  }
71201
72384
  function peekUserLanguage(env) {
71202
72385
  try {
71203
- const raw = (0, import_node_fs75.readFileSync)((0, import_node_path104.join)(resolveConfigDir({ env }), "config.json"), "utf8");
72386
+ const raw = (0, import_node_fs76.readFileSync)((0, import_node_path104.join)(resolveConfigDir({ env }), "config.json"), "utf8");
71204
72387
  const value = JSON.parse(raw).ui?.language;
71205
72388
  return value === "auto" || value === "zh" || value === "en" ? value : void 0;
71206
72389
  } catch {
@@ -71288,7 +72471,7 @@ function isDirectRun() {
71288
72471
  const entry = process.argv[1];
71289
72472
  if (entry === void 0) return false;
71290
72473
  try {
71291
- return (0, import_node_fs75.realpathSync)(entry) === (0, import_node_fs75.realpathSync)((0, import_node_url3.fileURLToPath)(__ama_import_meta_url));
72474
+ return (0, import_node_fs76.realpathSync)(entry) === (0, import_node_fs76.realpathSync)((0, import_node_url3.fileURLToPath)(__ama_import_meta_url));
71292
72475
  } catch {
71293
72476
  return false;
71294
72477
  }