@armadra/agent 0.10.0 → 0.12.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 (82) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/CHANGELOG.zh-CN.md +31 -0
  3. package/README.md +5 -0
  4. package/README.zh-CN.md +4 -0
  5. package/dist/acp.d.ts +2 -2
  6. package/dist/acp.js +2 -2
  7. package/dist/bundle/ama.cjs +1730 -446
  8. package/dist/cli/compose-agents.js +8 -0
  9. package/dist/cli/compose.d.ts +3 -0
  10. package/dist/cli/compose.js +1 -0
  11. package/dist/drivers/acp/client.d.ts +54 -5
  12. package/dist/drivers/acp/client.js +96 -9
  13. package/dist/drivers/acp/types.d.ts +69 -3
  14. package/dist/drivers/acp/types.js +30 -1
  15. package/dist/drivers/agents.js +1 -1
  16. package/dist/drivers/host-runners.d.ts +26 -1
  17. package/dist/drivers/host-runners.js +147 -0
  18. package/dist/hooks/matcher.d.ts +2 -2
  19. package/dist/hooks/matcher.js +3 -3
  20. package/dist/host/api-impl.d.ts +3 -0
  21. package/dist/host/api-impl.js +7 -2
  22. package/dist/host/types.d.ts +37 -1
  23. package/dist/host/types.js +5 -0
  24. package/dist/host.d.ts +1 -1
  25. package/dist/host.js +1 -1
  26. package/dist/i18n/catalog.d.ts +5 -0
  27. package/dist/i18n/messages/approval.d.ts +5 -0
  28. package/dist/i18n/messages/approval.js +5 -0
  29. package/dist/i18n/messages/drivers.d.ts +2 -0
  30. package/dist/i18n/messages/drivers.js +2 -0
  31. package/dist/i18n/messages/interactive-view.d.ts +5 -0
  32. package/dist/i18n/messages/interactive-view.js +5 -0
  33. package/dist/i18n/messages/interactive.d.ts +4 -0
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/modes/commands-core.d.ts +7 -0
  37. package/dist/modes/commands-core.js +23 -5
  38. package/dist/modes/interactive/approval-dialog.js +17 -4
  39. package/dist/modes/interactive/commands.d.ts +2 -0
  40. package/dist/modes/interactive/commands.js +5 -1
  41. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  42. package/dist/modes/interactive/interactive-mode.js +14 -6
  43. package/dist/modes/interactive/tool-summary.d.ts +2 -0
  44. package/dist/modes/interactive/tool-summary.js +27 -0
  45. package/dist/modes/rpc/commands.js +4 -4
  46. package/dist/permissions/auto-safe.d.ts +3 -0
  47. package/dist/permissions/auto-safe.js +10 -3
  48. package/dist/permissions/classifier.js +1 -1
  49. package/dist/permissions/network-class.d.ts +28 -0
  50. package/dist/permissions/network-class.js +91 -0
  51. package/dist/permissions/pipeline.d.ts +1 -1
  52. package/dist/permissions/pipeline.js +24 -7
  53. package/dist/permissions/preview.js +3 -0
  54. package/dist/permissions/readonly-bash.d.ts +2 -2
  55. package/dist/permissions/readonly-bash.js +2 -2
  56. package/dist/permissions/rules.d.ts +1 -0
  57. package/dist/permissions/rules.js +5 -1
  58. package/dist/tools/presets.d.ts +8 -1
  59. package/dist/tools/presets.js +12 -3
  60. package/dist/tools/registry.d.ts +3 -1
  61. package/dist/tools/registry.js +4 -2
  62. package/dist/tools/task-ctl.d.ts +9 -0
  63. package/dist/tools/task-ctl.js +22 -3
  64. package/dist/tools/types.d.ts +4 -1
  65. package/dist/tools/web/fetch.d.ts +48 -0
  66. package/dist/tools/web/fetch.js +307 -0
  67. package/dist/tools/web/html-to-markdown.d.ts +31 -0
  68. package/dist/tools/web/html-to-markdown.js +522 -0
  69. package/dist/tools/web/url-guard.d.ts +59 -0
  70. package/dist/tools/web/url-guard.js +221 -0
  71. package/docs/en/guides/permissions.md +33 -11
  72. package/docs/en/reference/acp.md +7 -3
  73. package/docs/en/reference/host-api.md +61 -19
  74. package/docs/en/reference/rpc.md +7 -7
  75. package/docs/guides/agents.md +18 -1
  76. package/docs/guides/hooks.md +86 -8
  77. package/docs/guides/permissions.md +33 -11
  78. package/docs/guides/sandbox.md +6 -0
  79. package/docs/reference/acp.md +7 -3
  80. package/docs/reference/host-api.md +61 -19
  81. package/docs/reference/rpc.md +7 -7
  82. 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.10.0" : readPackageVersion();
44
+ AMA_VERSION = true ? "0.12.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);
@@ -15116,6 +15493,9 @@ var init_jsonl = __esm({
15116
15493
  });
15117
15494
 
15118
15495
  // src/drivers/acp/types.ts
15496
+ function isTerminalAuthMethod(method) {
15497
+ return method.type === "terminal";
15498
+ }
15119
15499
  var ACP_PROTOCOL_VERSION, ACP_METHODS, RPC_ERRORS, ACP_META_KEY;
15120
15500
  var init_types7 = __esm({
15121
15501
  "src/drivers/acp/types.ts"() {
@@ -15123,8 +15503,10 @@ var init_types7 = __esm({
15123
15503
  ACP_PROTOCOL_VERSION = 1;
15124
15504
  ACP_METHODS = {
15125
15505
  initialize: "initialize",
15126
- /** ama 不实现(只给 terminal 型认证方法,规范要求这类方法不经 authenticate)。 */
15506
+ /** 客户端 → Agent:agent 型认证方法经它完成;terminal 型不经它(规范)。 */
15127
15507
  authenticate: "authenticate",
15508
+ /** 客户端 → Agent:登出当前账号(Agent 声明 `agentCapabilities.auth.logout` 才可用)。 */
15509
+ logout: "logout",
15128
15510
  sessionNew: "session/new",
15129
15511
  sessionLoad: "session/load",
15130
15512
  sessionResume: "session/resume",
@@ -15137,6 +15519,8 @@ var init_types7 = __esm({
15137
15519
  sessionUpdate: "session/update",
15138
15520
  requestPermission: "session/request_permission",
15139
15521
  elicitationCreate: "elicitation/create",
15522
+ /** Agent → 客户端通知:URL 型 elicitation 已在带外完成。 */
15523
+ elicitationComplete: "elicitation/complete",
15140
15524
  /** 协议级取消(双向通知,`requestId` 指对端发来的、尚未答复的请求)。 */
15141
15525
  cancelRequest: "$/cancel_request"
15142
15526
  };
@@ -16134,10 +16518,18 @@ function safeFileName(name) {
16134
16518
  const cleaned = name.replace(/[^A-Za-z0-9._-]/g, "_");
16135
16519
  return cleaned === "" ? "output" : cleaned.slice(0, 120);
16136
16520
  }
16137
- var import_node_os4, import_node_path24, DEFAULT_MAX_LINES, DEFAULT_MAX_BYTES, RESULT_NOTE_RESERVE, GREP_MAX_LINE_CHARS, HEAD_TAIL_RATIO;
16521
+ function writeFullOutput(outputDir, fileName, content) {
16522
+ const dir = resolveOutputDir(outputDir);
16523
+ (0, import_node_fs22.mkdirSync)(dir, { recursive: true });
16524
+ const target = (0, import_node_path24.join)(dir, safeFileName(fileName));
16525
+ (0, import_node_fs22.writeFileSync)(target, content);
16526
+ return target;
16527
+ }
16528
+ 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
16529
  var init_truncate = __esm({
16139
16530
  "src/tools/truncate.ts"() {
16140
16531
  "use strict";
16532
+ import_node_fs22 = require("node:fs");
16141
16533
  import_node_os4 = require("node:os");
16142
16534
  import_node_path24 = require("node:path");
16143
16535
  DEFAULT_MAX_LINES = 2e3;
@@ -16777,11 +17169,11 @@ async function readImage(abs, shown2, ctx, options) {
16777
17169
  [${fit.note}]`, details };
16778
17170
  const note = fit.resized !== void 0 && fit.size !== void 0 ? `
16779
17171
  [Resized to ${fit.size.width}x${fit.size.height} to fit the attachment limit.]` : "";
16780
- const blocks = [
17172
+ const blocks2 = [
16781
17173
  { type: "text", text: caption + note },
16782
17174
  internImage({ type: "image", data: fit.buf.toString("base64"), mimeType: fit.mimeType })
16783
17175
  ];
16784
- return { content: blocks, details };
17176
+ return { content: blocks2, details };
16785
17177
  }
16786
17178
  async function executeRead(input2, ctx, options = {}) {
16787
17179
  const abs = resolvePath(input2.path, ctx.cwd);
@@ -17251,7 +17643,7 @@ function gitBashCandidates(env) {
17251
17643
  function resolveShell(deps = {}) {
17252
17644
  const platform = deps.platform ?? process.platform;
17253
17645
  const env = deps.env ?? process.env;
17254
- const exists5 = deps.exists ?? import_node_fs22.existsSync;
17646
+ const exists5 = deps.exists ?? import_node_fs23.existsSync;
17255
17647
  const custom = env["AMA_SHELL"]?.trim();
17256
17648
  if (custom) return { shell: custom, kind: shellKindOf(custom) };
17257
17649
  if (platform === "win32") {
@@ -17291,11 +17683,11 @@ function buildToolEnv(base4, info2) {
17291
17683
  set("AMA_DEPTH", String(info2.depth));
17292
17684
  return env;
17293
17685
  }
17294
- var import_node_fs22, POWERSHELL_ARGS, INJECTED_ENV_VARS;
17686
+ var import_node_fs23, POWERSHELL_ARGS, INJECTED_ENV_VARS;
17295
17687
  var init_shell = __esm({
17296
17688
  "src/tools/shell.ts"() {
17297
17689
  "use strict";
17298
- import_node_fs22 = require("node:fs");
17690
+ import_node_fs23 = require("node:fs");
17299
17691
  POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-Command"];
17300
17692
  INJECTED_ENV_VARS = [
17301
17693
  "AMA_SESSION_ID",
@@ -17410,11 +17802,11 @@ var init_process_tree = __esm({
17410
17802
  function sanitizeOutput(text) {
17411
17803
  return text.replace(/\r\n/g, "\n").replace(ANSI, "").replace(/\r/g, "\n").replace(CONTROL, "");
17412
17804
  }
17413
- var import_node_fs23, import_node_path28, import_node_string_decoder2, ANSI, CONTROL, OutputAccumulator;
17805
+ var import_node_fs24, import_node_path28, import_node_string_decoder2, ANSI, CONTROL, OutputAccumulator;
17414
17806
  var init_output_accumulator = __esm({
17415
17807
  "src/tools/output-accumulator.ts"() {
17416
17808
  "use strict";
17417
- import_node_fs23 = require("node:fs");
17809
+ import_node_fs24 = require("node:fs");
17418
17810
  import_node_path28 = require("node:path");
17419
17811
  import_node_string_decoder2 = require("node:string_decoder");
17420
17812
  init_truncate();
@@ -17456,7 +17848,7 @@ var init_output_accumulator = __esm({
17456
17848
  this.tailText = sliceTailBytes(this.tailText, this.maxBytes * 2);
17457
17849
  }
17458
17850
  if (this.spilledTo !== void 0) {
17459
- (0, import_node_fs23.appendFileSync)(this.spilledTo, text);
17851
+ (0, import_node_fs24.appendFileSync)(this.spilledTo, text);
17460
17852
  return;
17461
17853
  }
17462
17854
  this.chunks.push(text);
@@ -17465,8 +17857,8 @@ var init_output_accumulator = __esm({
17465
17857
  }
17466
17858
  spill() {
17467
17859
  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(""));
17860
+ (0, import_node_fs24.mkdirSync)((0, import_node_path28.dirname)(target), { recursive: true });
17861
+ (0, import_node_fs24.writeFileSync)(target, this.chunks.join(""));
17470
17862
  this.spilledTo = target;
17471
17863
  this.chunks = [];
17472
17864
  this.memoryBytes = 0;
@@ -17524,11 +17916,11 @@ async function disposeSessionJobs(sessionId) {
17524
17916
  registry2.delete(sessionId);
17525
17917
  await jobs.disposeAll();
17526
17918
  }
17527
- var import_node_fs24, import_node_path29, BackgroundJobs, registry2;
17919
+ var import_node_fs25, import_node_path29, BackgroundJobs, registry2;
17528
17920
  var init_background_jobs = __esm({
17529
17921
  "src/tools/background-jobs.ts"() {
17530
17922
  "use strict";
17531
- import_node_fs24 = require("node:fs");
17923
+ import_node_fs25 = require("node:fs");
17532
17924
  import_node_path29 = require("node:path");
17533
17925
  init_process_tree();
17534
17926
  init_truncate();
@@ -17537,13 +17929,13 @@ var init_background_jobs = __esm({
17537
17929
  listeners = /* @__PURE__ */ new Set();
17538
17930
  seq = 0;
17539
17931
  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");
17932
+ (0, import_node_fs25.mkdirSync)((0, import_node_path29.dirname)(options.outputPath), { recursive: true });
17933
+ const fd = (0, import_node_fs25.openSync)(options.outputPath, "a");
17542
17934
  let child;
17543
17935
  try {
17544
17936
  child = options.spawn(fd);
17545
17937
  } finally {
17546
- (0, import_node_fs24.closeSync)(fd);
17938
+ (0, import_node_fs25.closeSync)(fd);
17547
17939
  }
17548
17940
  const id = `bg${++this.seq}`;
17549
17941
  const job = {
@@ -17624,8 +18016,8 @@ var init_background_jobs = __esm({
17624
18016
  if (entry === void 0) return void 0;
17625
18017
  let text = "";
17626
18018
  try {
17627
- if ((0, import_node_fs24.statSync)(entry.job.outputPath).size > 0)
17628
- text = (0, import_node_fs24.readFileSync)(entry.job.outputPath, "utf8");
18019
+ if ((0, import_node_fs25.statSync)(entry.job.outputPath).size > 0)
18020
+ text = (0, import_node_fs25.readFileSync)(entry.job.outputPath, "utf8");
17629
18021
  } catch {
17630
18022
  }
17631
18023
  return { ...truncateTail(text, options), job: entry.job };
@@ -17671,7 +18063,7 @@ var init_background_jobs = __esm({
17671
18063
  });
17672
18064
 
17673
18065
  // src/sandbox/wrap.ts
17674
- function resolveWritable(dirs, realpath4 = import_node_fs25.realpathSync) {
18066
+ function resolveWritable(dirs, realpath4 = import_node_fs26.realpathSync) {
17675
18067
  const out = [];
17676
18068
  for (const dir of dirs) {
17677
18069
  let real2;
@@ -17686,12 +18078,12 @@ function resolveWritable(dirs, realpath4 = import_node_fs25.realpathSync) {
17686
18078
  }
17687
18079
  function isDirectory(path) {
17688
18080
  try {
17689
- return (0, import_node_fs25.statSync)(path).isDirectory();
18081
+ return (0, import_node_fs26.statSync)(path).isDirectory();
17690
18082
  } catch {
17691
18083
  return false;
17692
18084
  }
17693
18085
  }
17694
- function resolveExtras(policy, missingOk, realpath4 = import_node_fs25.realpathSync, isDir4 = isDirectory) {
18086
+ function resolveExtras(policy, missingOk, realpath4 = import_node_fs26.realpathSync, isDir4 = isDirectory) {
17695
18087
  const readOnly = [];
17696
18088
  for (const path of policy.readOnly ?? []) {
17697
18089
  let real2;
@@ -17763,11 +18155,11 @@ function wrapCommand(status, command3, args, policy, realpath4, isDir4) {
17763
18155
  };
17764
18156
  }
17765
18157
  }
17766
- var import_node_fs25, import_node_path30;
18158
+ var import_node_fs26, import_node_path30;
17767
18159
  var init_wrap = __esm({
17768
18160
  "src/sandbox/wrap.ts"() {
17769
18161
  "use strict";
17770
- import_node_fs25 = require("node:fs");
18162
+ import_node_fs26 = require("node:fs");
17771
18163
  import_node_path30 = require("node:path");
17772
18164
  init_profile2();
17773
18165
  }
@@ -18281,7 +18673,7 @@ async function executeGrep(input2, ctx) {
18281
18673
  }
18282
18674
  const files = await candidateFiles(root, rootIsFile, input2.glob, ctx.signal);
18283
18675
  if (input2.filesOnly === true) return grepFilesOnly(files, regex, limit, ctx);
18284
- const blocks = [];
18676
+ const blocks2 = [];
18285
18677
  let matches2 = 0;
18286
18678
  let matchedFiles = 0;
18287
18679
  let limited = false;
@@ -18298,7 +18690,7 @@ async function executeGrep(input2, ctx) {
18298
18690
  }
18299
18691
  const shown2 = displayPath(batch[j], ctx.cwd);
18300
18692
  const block = formatFileHits(shown2, hits, context, limit - matches2);
18301
- blocks.push(block.text.join("\n"));
18693
+ blocks2.push(block.text.join("\n"));
18302
18694
  matches2 += block.used;
18303
18695
  matchedFiles++;
18304
18696
  if (block.used < hits.hits.length) {
@@ -18308,7 +18700,7 @@ async function executeGrep(input2, ctx) {
18308
18700
  }
18309
18701
  }
18310
18702
  if (matches2 === 0) return { content: "No matches found", details: { matches: 0, files: 0 } };
18311
- const joined = blocks.join(context > 0 ? "\n--\n" : "\n");
18703
+ const joined = blocks2.join(context > 0 ? "\n--\n" : "\n");
18312
18704
  const maxBytes = toolOutputBytes(ctx.maxResultChars);
18313
18705
  const cut = truncateHead(joined, { maxLines: Number.MAX_SAFE_INTEGER, maxBytes });
18314
18706
  let content = cut.content;
@@ -18764,9 +19156,9 @@ ${tail}`, truncated: true };
18764
19156
  }
18765
19157
  function writeTaskOutput(dir, name, text) {
18766
19158
  try {
18767
- (0, import_node_fs26.mkdirSync)(dir, { recursive: true });
19159
+ (0, import_node_fs27.mkdirSync)(dir, { recursive: true });
18768
19160
  const file = (0, import_node_path34.join)(dir, name);
18769
- (0, import_node_fs26.writeFileSync)(file, text);
19161
+ (0, import_node_fs27.writeFileSync)(file, text);
18770
19162
  return file;
18771
19163
  } catch {
18772
19164
  return void 0;
@@ -18803,11 +19195,11 @@ function backgroundedText(reason, afterMs) {
18803
19195
  function waitDetachedText(taskId) {
18804
19196
  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
19197
  }
18806
- var import_node_fs26, import_node_path34, TASK_RESULT_LIMIT_BYTES, HEAD_SHARE, MAX_TURNS_NOTE, NOTIFY_HINT, TASK_NOTIFICATION_RULE;
19198
+ var import_node_fs27, import_node_path34, TASK_RESULT_LIMIT_BYTES, HEAD_SHARE, MAX_TURNS_NOTE, NOTIFY_HINT, TASK_NOTIFICATION_RULE;
18807
19199
  var init_result = __esm({
18808
19200
  "src/agents/result.ts"() {
18809
19201
  "use strict";
18810
- import_node_fs26 = require("node:fs");
19202
+ import_node_fs27 = require("node:fs");
18811
19203
  import_node_path34 = require("node:path");
18812
19204
  TASK_RESULT_LIMIT_BYTES = 50 * 1024;
18813
19205
  HEAD_SHARE = 0.7;
@@ -18987,6 +19379,9 @@ var init_task_control = __esm({
18987
19379
  });
18988
19380
 
18989
19381
  // src/tools/task-ctl.ts
19382
+ function bindTaskCtlWaitCap(tool, capMs) {
19383
+ if (tool !== void 0) waitCaps.set(tool, capMs);
19384
+ }
18990
19385
  function fail4(message) {
18991
19386
  return { content: message, isError: true };
18992
19387
  }
@@ -19013,7 +19408,7 @@ async function send(input2, ctx) {
19013
19408
  return { content: result.text, isError: result.isError };
19014
19409
  }
19015
19410
  function createTaskCtlTool() {
19016
- return {
19411
+ const tool = {
19017
19412
  name: TASK_CTL_TOOL,
19018
19413
  label: "TaskCtl",
19019
19414
  description: "Manage sub-agent tasks started by task: list them, wait for one, stop it, read its output, or send it a follow-up message (runs in background).",
@@ -19064,14 +19459,17 @@ ${body}` };
19064
19459
  };
19065
19460
  }
19066
19461
  case "wait": {
19067
- const timeout = Math.min(Math.max(input2.timeoutMs ?? DEFAULT_WAIT_MS, 0), MAX_WAIT_MS);
19462
+ const cap = Math.min(waitCaps.get(tool) ?? MAX_WAIT_MS, MAX_WAIT_MS);
19463
+ const wanted = Math.min(Math.max(input2.timeoutMs ?? DEFAULT_WAIT_MS, 0), MAX_WAIT_MS);
19464
+ const timeout = Math.min(wanted, cap);
19068
19465
  const signal = control.detachSignal(taskId);
19069
19466
  const result = await control.wait(taskId, timeout, { signal });
19070
19467
  if (result === void 0 && signal.aborted) return { content: waitDetachedText(taskId) };
19071
19468
  if (result === void 0) {
19072
19469
  const turns = control.get(taskId)?.turns ?? 0;
19470
+ const capped2 = wanted > timeout ? ` Waits are capped at ${Math.round(timeout / 1e3)}s here; do not block on it: keep working, a <task-notification> follows when it finishes.` : "";
19073
19471
  return {
19074
- content: `Task ${taskId} is still running (${turns} turns so far). Wait again or stop it.`
19472
+ content: `Task ${taskId} is still running (${turns} turns so far). Wait again or stop it.${capped2}`
19075
19473
  };
19076
19474
  }
19077
19475
  return {
@@ -19085,8 +19483,9 @@ ${result.text}`,
19085
19483
  }
19086
19484
  }
19087
19485
  };
19486
+ return tool;
19088
19487
  }
19089
- var TASK_CTL_TOOL, DEFAULT_WAIT_MS, MAX_WAIT_MS;
19488
+ var TASK_CTL_TOOL, DEFAULT_WAIT_MS, MAX_WAIT_MS, COORDINATOR_WAIT_MAX_MS, waitCaps;
19090
19489
  var init_task_ctl = __esm({
19091
19490
  "src/tools/task-ctl.ts"() {
19092
19491
  "use strict";
@@ -19096,6 +19495,749 @@ var init_task_ctl = __esm({
19096
19495
  TASK_CTL_TOOL = "task_ctl";
19097
19496
  DEFAULT_WAIT_MS = 3e4;
19098
19497
  MAX_WAIT_MS = 6e5;
19498
+ COORDINATOR_WAIT_MAX_MS = 5e3;
19499
+ waitCaps = /* @__PURE__ */ new WeakMap();
19500
+ }
19501
+ });
19502
+
19503
+ // src/tools/web/html-to-markdown.ts
19504
+ function decodeEntities(text) {
19505
+ return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]*);?/gi, (whole, body) => {
19506
+ if (body[0] === "#") {
19507
+ const code = body[1] === "x" || body[1] === "X" ? parseInt(body.slice(2), 16) : Number(body.slice(1));
19508
+ if (!Number.isFinite(code) || code <= 0 || code > 1114111 || code >= 55296 && code <= 57343)
19509
+ return "�";
19510
+ return String.fromCodePoint(code);
19511
+ }
19512
+ const hit = ENTITIES[body] ?? ENTITIES[body.toLowerCase()];
19513
+ return hit ?? whole;
19514
+ });
19515
+ }
19516
+ function parseAttrs(text) {
19517
+ const attrs = {};
19518
+ for (const m of text.matchAll(ATTR_RE)) {
19519
+ const name = m[1].toLowerCase();
19520
+ if (!(name in attrs)) attrs[name] = decodeEntities(m[2] ?? m[3] ?? m[4] ?? "");
19521
+ }
19522
+ return attrs;
19523
+ }
19524
+ function parseHtml(html) {
19525
+ const root = { tag: "#root", attrs: {}, children: [] };
19526
+ let cur = root;
19527
+ const open3 = (tag) => {
19528
+ for (let n = cur; n !== void 0; n = n.parent) if (n.tag === tag) return n;
19529
+ return void 0;
19530
+ };
19531
+ const closeTo = (node) => {
19532
+ cur = node.parent ?? root;
19533
+ };
19534
+ const closeOpen = (tags, stop) => {
19535
+ for (let n = cur; n !== void 0 && n !== root; n = n.parent) {
19536
+ if (tags.includes(n.tag)) return closeTo(n);
19537
+ if (stop.has(n.tag)) return;
19538
+ }
19539
+ };
19540
+ let i = 0;
19541
+ while (i < html.length) {
19542
+ const lt = html.indexOf("<", i);
19543
+ if (lt === -1) {
19544
+ cur.children.push(html.slice(i));
19545
+ break;
19546
+ }
19547
+ if (lt > i) cur.children.push(html.slice(i, lt));
19548
+ if (html.startsWith("<!--", lt)) {
19549
+ const end = html.indexOf("-->", lt + 4);
19550
+ i = end === -1 ? html.length : end + 3;
19551
+ continue;
19552
+ }
19553
+ if (html[lt + 1] === "!" || html[lt + 1] === "?") {
19554
+ const end = html.indexOf(">", lt);
19555
+ i = end === -1 ? html.length : end + 1;
19556
+ continue;
19557
+ }
19558
+ const m = /^<(\/?)([a-zA-Z][a-zA-Z0-9:-]*)((?:[^>"']|"[^"]*"|'[^']*')*)>/.exec(
19559
+ html.slice(lt, lt + 8192)
19560
+ );
19561
+ if (m === null) {
19562
+ cur.children.push("<");
19563
+ i = lt + 1;
19564
+ continue;
19565
+ }
19566
+ i = lt + m[0].length;
19567
+ const tag = m[2].toLowerCase();
19568
+ if (m[1] === "/") {
19569
+ const node2 = open3(tag);
19570
+ if (node2 !== void 0) closeTo(node2);
19571
+ continue;
19572
+ }
19573
+ if (CLOSES_P.has(tag)) closeOpen(["p"], BLOCK);
19574
+ const siblings = AUTO_CLOSE[tag];
19575
+ if (siblings !== void 0) closeOpen(siblings, SCOPE);
19576
+ const node = { tag, attrs: parseAttrs(m[3]), children: [], parent: cur };
19577
+ cur.children.push(node);
19578
+ if (RAW_TEXT.has(tag)) {
19579
+ const end = html.toLowerCase().indexOf(`</${tag}`, i);
19580
+ const body = html.slice(i, end === -1 ? html.length : end);
19581
+ node.children.push(body);
19582
+ const close = end === -1 ? -1 : html.indexOf(">", end);
19583
+ i = close === -1 ? html.length : close + 1;
19584
+ continue;
19585
+ }
19586
+ if (!VOID.has(tag) && !/\/\s*$/.test(m[3])) cur = node;
19587
+ }
19588
+ return root;
19589
+ }
19590
+ function find(node, test) {
19591
+ for (const child of node.children) {
19592
+ if (typeof child === "string") continue;
19593
+ if (test(child)) return child;
19594
+ if (DROP.has(child.tag)) continue;
19595
+ const hit = find(child, test);
19596
+ if (hit !== void 0) return hit;
19597
+ }
19598
+ return void 0;
19599
+ }
19600
+ function rawText(node) {
19601
+ return node.children.map((c) => typeof c === "string" ? c : rawText(c)).join("");
19602
+ }
19603
+ function resolveHref(href, base4) {
19604
+ const value = href.trim();
19605
+ if (value === "" || /^(javascript|data|vbscript):/i.test(value)) return void 0;
19606
+ if (value.startsWith("#")) return void 0;
19607
+ try {
19608
+ return base4 === void 0 ? new URL(value).href : new URL(value, base4).href;
19609
+ } catch {
19610
+ return void 0;
19611
+ }
19612
+ }
19613
+ function collapse(text) {
19614
+ return decodeEntities(text).replace(/[\s\u00a0]+/g, " ");
19615
+ }
19616
+ function inline(node, ctx) {
19617
+ let out = "";
19618
+ for (const child of node.children) {
19619
+ if (typeof child === "string") out += collapse(child);
19620
+ else out += renderInline(child, ctx);
19621
+ }
19622
+ return out;
19623
+ }
19624
+ function wrap(mark, text) {
19625
+ const trimmed = text.trim();
19626
+ if (trimmed === "") return text;
19627
+ const lead = text.startsWith(" ") ? " " : "";
19628
+ const trail = text.endsWith(" ") ? " " : "";
19629
+ return `${lead}${mark}${trimmed}${mark}${trail}`;
19630
+ }
19631
+ function renderInline(node, ctx) {
19632
+ if (DROP.has(node.tag)) return "";
19633
+ switch (node.tag) {
19634
+ case "br":
19635
+ return "\n";
19636
+ case "img": {
19637
+ const alt = (node.attrs["alt"] ?? "").trim();
19638
+ return alt === "" ? "" : collapse(alt);
19639
+ }
19640
+ case "a": {
19641
+ const text = inline(node, ctx);
19642
+ const href = resolveHref(node.attrs["href"] ?? "", ctx.base);
19643
+ if (href === void 0 || text.trim() === "") return text;
19644
+ return `[${text.trim().replace(/[[\]]/g, "\\$&")}](${href.replace(/[()\s]/g, encodeURIComponent)})`;
19645
+ }
19646
+ case "code":
19647
+ case "kbd":
19648
+ case "samp":
19649
+ case "tt": {
19650
+ const text = decodeEntities(rawText(node)).replace(/\s+/g, " ");
19651
+ if (text.trim() === "") return text;
19652
+ const fence2 = text.includes("`") ? "``" : "`";
19653
+ return `${fence2}${text}${fence2}`;
19654
+ }
19655
+ case "strong":
19656
+ case "b":
19657
+ return wrap("**", inline(node, ctx));
19658
+ case "em":
19659
+ case "i":
19660
+ return wrap("*", inline(node, ctx));
19661
+ case "del":
19662
+ case "s":
19663
+ return wrap("~~", inline(node, ctx));
19664
+ default:
19665
+ return BLOCK.has(node.tag) ? `
19666
+
19667
+ ${blocks(node, ctx)}
19668
+
19669
+ ` : inline(node, ctx);
19670
+ }
19671
+ }
19672
+ function blocks(node, ctx) {
19673
+ const parts = [];
19674
+ let run7 = "";
19675
+ const flush = () => {
19676
+ const text = run7.split("\n").map((l) => l.replace(/ {2,}/g, " ").trim()).join("\n").trim();
19677
+ if (text !== "") parts.push(text);
19678
+ run7 = "";
19679
+ };
19680
+ for (const child of node.children) {
19681
+ if (typeof child === "string") {
19682
+ run7 += collapse(child);
19683
+ continue;
19684
+ }
19685
+ if (DROP.has(child.tag)) continue;
19686
+ if (BLOCK.has(child.tag)) {
19687
+ flush();
19688
+ const text = renderBlock(child, ctx);
19689
+ if (text.trim() !== "") parts.push(text);
19690
+ } else run7 += renderInline(child, ctx);
19691
+ }
19692
+ flush();
19693
+ return parts.join("\n\n");
19694
+ }
19695
+ function prefixLines(text, first, rest) {
19696
+ return text.split("\n").map(
19697
+ (line2, i) => line2 === "" ? i === 0 ? first.trimEnd() : "" : `${i === 0 ? first : rest}${line2}`
19698
+ ).join("\n");
19699
+ }
19700
+ function renderList(node, ctx) {
19701
+ const ordered = node.tag === "ol";
19702
+ let n = Number(node.attrs["start"] ?? "1");
19703
+ if (!Number.isFinite(n)) n = 1;
19704
+ const items = [];
19705
+ const inner = { ...ctx, listDepth: ctx.listDepth + 1 };
19706
+ for (const child of node.children) {
19707
+ if (typeof child === "string" || DROP.has(child.tag)) continue;
19708
+ const body = child.tag === "li" ? blocks(child, inner) : renderBlock(child, inner);
19709
+ if (body.trim() === "") continue;
19710
+ const marker = ordered ? `${n++}. ` : "- ";
19711
+ items.push(prefixLines(body.replace(/\n{2,}/g, "\n"), marker, " ".repeat(marker.length)));
19712
+ }
19713
+ return items.join("\n");
19714
+ }
19715
+ function cellText(node, ctx) {
19716
+ return blocks(node, ctx).replace(/\s*\n+\s*/g, " ").replace(/\|/g, "\\|").trim();
19717
+ }
19718
+ function renderTable(node, ctx) {
19719
+ const rows = [];
19720
+ const walk4 = (n) => {
19721
+ for (const child of n.children) {
19722
+ if (typeof child === "string" || DROP.has(child.tag)) continue;
19723
+ if (child.tag === "tr") {
19724
+ const cells = child.children.filter(
19725
+ (c) => typeof c !== "string" && (c.tag === "td" || c.tag === "th")
19726
+ );
19727
+ if (cells.length > 0)
19728
+ rows.push({
19729
+ cells: cells.map((c) => cellText(c, ctx)),
19730
+ header: n.tag === "thead" || cells.every((c) => c.tag === "th")
19731
+ });
19732
+ } else if (child.tag !== "table") walk4(child);
19733
+ }
19734
+ };
19735
+ walk4(node);
19736
+ if (rows.length === 0) return "";
19737
+ const width2 = Math.max(...rows.map((r) => r.cells.length));
19738
+ const line2 = (cells) => `| ${[...cells, ...new Array(width2 - cells.length).fill("")].join(" | ")} |`;
19739
+ const headerIndex = rows[0]?.header === true ? 0 : -1;
19740
+ const head = headerIndex === 0 ? rows.shift().cells : new Array(width2).fill("");
19741
+ return [line2(head), `|${" --- |".repeat(width2)}`, ...rows.map((r) => line2(r.cells))].join("\n");
19742
+ }
19743
+ function renderBlock(node, ctx) {
19744
+ switch (node.tag) {
19745
+ case "h1":
19746
+ case "h2":
19747
+ case "h3":
19748
+ case "h4":
19749
+ case "h5":
19750
+ case "h6": {
19751
+ const text = inline(node, ctx).replace(/\s+/g, " ").trim();
19752
+ return text === "" ? "" : `${"#".repeat(Number(node.tag[1]))} ${text}`;
19753
+ }
19754
+ case "hr":
19755
+ return "---";
19756
+ case "pre": {
19757
+ const code = find(node, (n) => n.tag === "code");
19758
+ const cls = `${code?.attrs["class"] ?? ""} ${node.attrs["class"] ?? ""}`;
19759
+ const lang = /(?:^|\s)(?:language|lang)-([\w+#-]+)/.exec(cls)?.[1] ?? "";
19760
+ const text = decodeEntities(rawText(node)).replace(/^\n/, "").replace(/\s+$/, "");
19761
+ const fence2 = text.includes("```") ? "~~~~" : "```";
19762
+ return `${fence2}${lang}
19763
+ ${text}
19764
+ ${fence2}`;
19765
+ }
19766
+ case "blockquote":
19767
+ return prefixLines(blocks(node, ctx), "> ", "> ");
19768
+ case "ul":
19769
+ case "ol":
19770
+ return renderList(node, ctx);
19771
+ case "table":
19772
+ return renderTable(node, ctx);
19773
+ default:
19774
+ return blocks(node, ctx);
19775
+ }
19776
+ }
19777
+ function pageTitle(root) {
19778
+ const title = find(root, (n) => n.tag === "title") ?? findIn(root, "title");
19779
+ const text = title !== void 0 ? collapse(rawText(title)).trim() : "";
19780
+ if (text !== "") return text;
19781
+ const h1 = find(root, (n) => n.tag === "h1");
19782
+ const heading = h1 !== void 0 ? collapse(rawText(h1)).trim() : "";
19783
+ return heading === "" ? void 0 : heading;
19784
+ }
19785
+ function findIn(node, tag) {
19786
+ for (const child of node.children) {
19787
+ if (typeof child === "string") continue;
19788
+ if (child.tag === tag) return child;
19789
+ const hit = findIn(child, tag);
19790
+ if (hit !== void 0) return hit;
19791
+ }
19792
+ return void 0;
19793
+ }
19794
+ function htmlToMarkdown(html, baseUrl) {
19795
+ const root = parseHtml(html);
19796
+ let base4;
19797
+ try {
19798
+ const declared = findIn(root, "base")?.attrs["href"];
19799
+ base4 = baseUrl === void 0 ? void 0 : new URL(declared ?? "", baseUrl);
19800
+ } catch {
19801
+ base4 = baseUrl === void 0 ? void 0 : new URL(baseUrl);
19802
+ }
19803
+ 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;
19804
+ const markdown = blocks(main2, { base: base4, listDepth: 0 }).replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
19805
+ const title = pageTitle(root);
19806
+ return title === void 0 ? { markdown } : { title, markdown };
19807
+ }
19808
+ function sniffMetaCharset(head) {
19809
+ const text = head.slice(0, 2048);
19810
+ const direct = /<meta[^>]+charset\s*=\s*["']?\s*([\w.:-]+)/i.exec(text);
19811
+ return direct?.[1]?.toLowerCase();
19812
+ }
19813
+ var VOID, RAW_TEXT, DROP, BLOCK, AUTO_CLOSE, CLOSES_P, SCOPE, ENTITIES, ATTR_RE;
19814
+ var init_html_to_markdown = __esm({
19815
+ "src/tools/web/html-to-markdown.ts"() {
19816
+ "use strict";
19817
+ VOID = /* @__PURE__ */ new Set([
19818
+ "area",
19819
+ "base",
19820
+ "br",
19821
+ "col",
19822
+ "embed",
19823
+ "hr",
19824
+ "img",
19825
+ "input",
19826
+ "link",
19827
+ "meta",
19828
+ "param",
19829
+ "source",
19830
+ "track",
19831
+ "wbr"
19832
+ ]);
19833
+ RAW_TEXT = /* @__PURE__ */ new Set(["script", "style", "textarea", "title", "xmp", "noscript"]);
19834
+ DROP = /* @__PURE__ */ new Set([
19835
+ "script",
19836
+ "style",
19837
+ "noscript",
19838
+ "svg",
19839
+ "math",
19840
+ "template",
19841
+ "iframe",
19842
+ "nav",
19843
+ "footer",
19844
+ "aside",
19845
+ "form",
19846
+ "button",
19847
+ "select",
19848
+ "textarea",
19849
+ "head",
19850
+ "title",
19851
+ "canvas",
19852
+ "object",
19853
+ "dialog"
19854
+ ]);
19855
+ BLOCK = /* @__PURE__ */ new Set([
19856
+ "address",
19857
+ "article",
19858
+ "blockquote",
19859
+ "body",
19860
+ "center",
19861
+ "dd",
19862
+ "details",
19863
+ "div",
19864
+ "dl",
19865
+ "dt",
19866
+ "figcaption",
19867
+ "figure",
19868
+ "h1",
19869
+ "h2",
19870
+ "h3",
19871
+ "h4",
19872
+ "h5",
19873
+ "h6",
19874
+ "header",
19875
+ "hr",
19876
+ "html",
19877
+ "li",
19878
+ "main",
19879
+ "ol",
19880
+ "p",
19881
+ "pre",
19882
+ "section",
19883
+ "summary",
19884
+ "table",
19885
+ "tbody",
19886
+ "td",
19887
+ "tfoot",
19888
+ "th",
19889
+ "thead",
19890
+ "tr",
19891
+ "ul"
19892
+ ]);
19893
+ AUTO_CLOSE = {
19894
+ li: ["li"],
19895
+ dt: ["dt", "dd"],
19896
+ dd: ["dt", "dd"],
19897
+ tr: ["tr"],
19898
+ td: ["td", "th"],
19899
+ th: ["td", "th"],
19900
+ option: ["option"]
19901
+ };
19902
+ CLOSES_P = /* @__PURE__ */ new Set([
19903
+ "p",
19904
+ ...[...BLOCK].filter((t) => !["li", "td", "th", "dd", "dt"].includes(t))
19905
+ ]);
19906
+ SCOPE = /* @__PURE__ */ new Set(["ul", "ol", "dl", "table", "html", "body"]);
19907
+ ENTITIES = {
19908
+ amp: "&",
19909
+ lt: "<",
19910
+ gt: ">",
19911
+ quot: '"',
19912
+ apos: "'",
19913
+ nbsp: " ",
19914
+ copy: "©",
19915
+ reg: "®",
19916
+ trade: "™",
19917
+ hellip: "…",
19918
+ mdash: "—",
19919
+ ndash: "–",
19920
+ lsquo: "‘",
19921
+ rsquo: "’",
19922
+ ldquo: "“",
19923
+ rdquo: "”",
19924
+ laquo: "«",
19925
+ raquo: "»",
19926
+ middot: "·",
19927
+ bull: "•",
19928
+ times: "×",
19929
+ divide: "÷",
19930
+ deg: "°",
19931
+ euro: "€",
19932
+ pound: "£",
19933
+ yen: "¥",
19934
+ cent: "¢",
19935
+ sect: "§",
19936
+ para: "¶",
19937
+ larr: "←",
19938
+ rarr: "→",
19939
+ uarr: "↑",
19940
+ darr: "↓",
19941
+ harr: "↔",
19942
+ rArr: "⇒",
19943
+ lArr: "⇐",
19944
+ le: "≤",
19945
+ ge: "≥",
19946
+ ne: "≠",
19947
+ plusmn: "±",
19948
+ minus: "−",
19949
+ shy: "",
19950
+ zwj: "",
19951
+ zwnj: "",
19952
+ thinsp: " ",
19953
+ ensp: " ",
19954
+ emsp: " ",
19955
+ check: "✓"
19956
+ };
19957
+ ATTR_RE = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
19958
+ }
19959
+ });
19960
+
19961
+ // src/tools/web/fetch.ts
19962
+ function fail5(content, details) {
19963
+ return { content, isError: true, ...details !== void 0 ? { details } : {} };
19964
+ }
19965
+ function readBody(res, limit) {
19966
+ const encoding = String(res.headers["content-encoding"] ?? "").trim().toLowerCase();
19967
+ let stream5 = res;
19968
+ if (encoding === "gzip" || encoding === "x-gzip") stream5 = res.pipe((0, import_node_zlib.createGunzip)());
19969
+ else if (encoding === "br") stream5 = res.pipe((0, import_node_zlib.createBrotliDecompress)());
19970
+ else if (encoding === "deflate") stream5 = res.pipe((0, import_node_zlib.createInflate)());
19971
+ return new Promise((resolve41, reject2) => {
19972
+ const chunks2 = [];
19973
+ let size = 0;
19974
+ let done = false;
19975
+ const finish2 = (cut) => {
19976
+ if (done) return;
19977
+ done = true;
19978
+ resolve41({ body: Buffer.concat(chunks2, Math.min(size, limit)).subarray(0, limit), cut });
19979
+ };
19980
+ stream5.on("data", (chunk) => {
19981
+ if (done) return;
19982
+ chunks2.push(chunk);
19983
+ size += chunk.length;
19984
+ if (size > limit) {
19985
+ finish2(true);
19986
+ res.destroy();
19987
+ if (stream5 !== res) stream5.destroy();
19988
+ }
19989
+ });
19990
+ stream5.on("end", () => finish2(false));
19991
+ stream5.on("error", (err) => done ? void 0 : (done = true, reject2(err)));
19992
+ res.on("error", (err) => done ? void 0 : (done = true, reject2(err)));
19993
+ });
19994
+ }
19995
+ function decodeText(body, contentType, html) {
19996
+ const declared = /charset\s*=\s*"?([\w.:-]+)/i.exec(contentType)?.[1];
19997
+ const sniffed = html ? sniffMetaCharset(body.subarray(0, 2048).toString("latin1")) : void 0;
19998
+ for (const label2 of [declared, sniffed, "utf-8"]) {
19999
+ if (label2 === void 0) continue;
20000
+ try {
20001
+ return new TextDecoder(label2).decode(body);
20002
+ } catch {
20003
+ }
20004
+ }
20005
+ return body.toString("utf8");
20006
+ }
20007
+ function formatOf(contentType, body) {
20008
+ const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
20009
+ if (type === "text/html" || type === "application/xhtml+xml") return "html";
20010
+ if (type === "application/json" || type.endsWith("+json")) return "json";
20011
+ if (type.startsWith("text/") || /^application\/(xml|javascript|ecmascript|x-yaml|yaml|toml)$/.test(type) || type.endsWith("+xml"))
20012
+ return "text";
20013
+ if (type === "") {
20014
+ const head = body.subarray(0, 1024);
20015
+ if (head.includes(0)) return "binary";
20016
+ return /^\s*<(!doctype html|html)/i.test(head.toString("latin1")) ? "html" : "text";
20017
+ }
20018
+ return "binary";
20019
+ }
20020
+ function convert(body, contentType, finalUrl, raw) {
20021
+ const kind = formatOf(contentType, body);
20022
+ if (kind === "binary") {
20023
+ const type = contentType.split(";")[0]?.trim() || "unknown type";
20024
+ return {
20025
+ format: "binary",
20026
+ text: `Binary content (${type}, ${body.length} bytes); not parsed.`
20027
+ };
20028
+ }
20029
+ const text = decodeText(body, contentType, kind === "html");
20030
+ if (raw) return { format: "text", text };
20031
+ if (kind === "html") {
20032
+ const page2 = htmlToMarkdown(text, finalUrl);
20033
+ return page2.title === void 0 ? { format: "markdown", text: page2.markdown } : { format: "markdown", title: page2.title, text: page2.markdown };
20034
+ }
20035
+ if (kind === "json") {
20036
+ try {
20037
+ return { format: "json", text: JSON.stringify(JSON.parse(text), null, 2) };
20038
+ } catch {
20039
+ return { format: "text", text };
20040
+ }
20041
+ }
20042
+ return { format: "text", text };
20043
+ }
20044
+ function createWebFetchTool(options = {}) {
20045
+ const request = options.request ?? defaultRequest;
20046
+ const totalMs = options.totalTimeoutMs ?? FETCH_TOTAL_TIMEOUT_MS;
20047
+ const firstByteMs = options.firstByteTimeoutMs ?? FETCH_FIRST_BYTE_TIMEOUT_MS;
20048
+ const maxBody = options.maxBodyBytes ?? FETCH_MAX_BODY_BYTES;
20049
+ async function get(url, hostname, signal) {
20050
+ const resolved = await resolvePublicAddresses(hostname, options.lookup);
20051
+ if (!resolved.ok) throw new FetchError(resolved.reason);
20052
+ return new Promise((resolve41, reject2) => {
20053
+ const timer = setTimeout(() => {
20054
+ req.destroy(new FetchError(`no response within ${firstByteMs / 1e3}s`));
20055
+ }, firstByteMs);
20056
+ const req = request(
20057
+ url,
20058
+ {
20059
+ method: "GET",
20060
+ agent: false,
20061
+ lookup: pinnedLookup(resolved.addresses),
20062
+ headers: {
20063
+ Accept: FETCH_ACCEPT,
20064
+ "Accept-Encoding": "gzip, deflate, br",
20065
+ "User-Agent": `ama/${AMA_VERSION}`
20066
+ },
20067
+ signal
20068
+ },
20069
+ (res) => {
20070
+ clearTimeout(timer);
20071
+ resolve41(res);
20072
+ }
20073
+ );
20074
+ req.on("error", (err) => {
20075
+ clearTimeout(timer);
20076
+ reject2(err);
20077
+ });
20078
+ req.end();
20079
+ });
20080
+ }
20081
+ async function fetchFollowing(start, hostname, signal) {
20082
+ let url = start;
20083
+ let host = hostname;
20084
+ for (let hop = 0; ; hop++) {
20085
+ const res = await get(url, host, signal);
20086
+ const status = res.statusCode ?? 0;
20087
+ const location = res.headers.location;
20088
+ if (status >= 300 && status < 400 && typeof location === "string") {
20089
+ res.resume();
20090
+ let next;
20091
+ try {
20092
+ next = new URL(location, url);
20093
+ } catch {
20094
+ throw new FetchError(`invalid redirect location ${JSON.stringify(location)}`);
20095
+ }
20096
+ if (redirectPolicy(url, next) === "cross-host") return { redirect: next.href, status };
20097
+ const checked = checkFetchUrl(next.href);
20098
+ if (!checked.ok)
20099
+ throw new FetchError(`redirect to ${next.href} refused: ${checked.reason}`);
20100
+ if (hop + 1 > MAX_SAME_HOST_REDIRECTS)
20101
+ throw new FetchError(`more than ${MAX_SAME_HOST_REDIRECTS} redirects`);
20102
+ url = checked.url;
20103
+ host = checked.hostname;
20104
+ continue;
20105
+ }
20106
+ const { body, cut } = await readBody(res, maxBody);
20107
+ return { res, finalUrl: url.href, body, bodyCut: cut };
20108
+ }
20109
+ }
20110
+ async function execute2(input2, ctx) {
20111
+ const checked = checkFetchUrl(input2.url);
20112
+ if (!checked.ok) return fail5(`Cannot fetch ${String(input2.url)}: ${checked.reason}`);
20113
+ const offset = input2.offset ?? 0;
20114
+ const asked = input2.maxChars ?? DEFAULT_FETCH_CHARS;
20115
+ if (!Number.isInteger(offset) || offset < 0) return fail5("offset must be an integer ≥ 0");
20116
+ if (!Number.isInteger(asked) || asked < 1) return fail5("maxChars must be an integer ≥ 1");
20117
+ const sessionCap = ctx.maxResultChars === void 0 ? Infinity : ctx.maxResultChars - RESULT_NOTE_RESERVE;
20118
+ const limit = Math.max(1e3, Math.min(asked, MAX_FETCH_CHARS, sessionCap));
20119
+ const controller = new AbortController();
20120
+ const timer = setTimeout(
20121
+ () => controller.abort(new FetchError(`timed out after ${totalMs / 1e3}s`)),
20122
+ totalMs
20123
+ );
20124
+ const signal = AbortSignal.any([ctx.signal, controller.signal]);
20125
+ let fetched;
20126
+ try {
20127
+ fetched = await fetchFollowing(checked.url, checked.hostname, signal);
20128
+ } catch (err) {
20129
+ const reason = controller.signal.aborted && controller.signal.reason instanceof FetchError ? controller.signal.reason.message : err instanceof Error ? err.message : String(err);
20130
+ return fail5(`Cannot fetch ${checked.url.href}: ${reason}`);
20131
+ } finally {
20132
+ clearTimeout(timer);
20133
+ }
20134
+ if ("redirect" in fetched) {
20135
+ return {
20136
+ content: `Redirected to ${fetched.redirect}; call web_fetch with it if appropriate.`,
20137
+ details: {
20138
+ status: fetched.status,
20139
+ contentType: "",
20140
+ bytes: 0,
20141
+ truncated: false,
20142
+ finalUrl: fetched.redirect
20143
+ }
20144
+ };
20145
+ }
20146
+ const { res, finalUrl, body, bodyCut } = fetched;
20147
+ const status = res.statusCode ?? 0;
20148
+ const contentType = String(res.headers["content-type"] ?? "");
20149
+ const page2 = convert(body, contentType, finalUrl, input2.raw === true);
20150
+ const base4 = {
20151
+ status,
20152
+ contentType,
20153
+ bytes: body.length,
20154
+ truncated: false,
20155
+ finalUrl,
20156
+ format: page2.format,
20157
+ ...page2.title !== void 0 ? { title: page2.title } : {}
20158
+ };
20159
+ if (status < 200 || status >= 300) {
20160
+ const snippet = page2.text.slice(0, ERROR_BODY_CHARS);
20161
+ return fail5(
20162
+ `HTTP ${status}${res.statusMessage ? ` ${res.statusMessage}` : ""} from ${finalUrl}${snippet.trim() === "" ? "" : `
20163
+
20164
+ ${snippet}`}`,
20165
+ base4
20166
+ );
20167
+ }
20168
+ const full = page2.title === void 0 ? page2.text : `# ${page2.title}
20169
+
20170
+ ${page2.text}`;
20171
+ if (offset > 0 && offset >= full.length)
20172
+ return fail5(`offset ${offset} is past the end of the content (${full.length} chars)`, base4);
20173
+ const slice = full.slice(offset, offset + limit);
20174
+ const end = offset + slice.length;
20175
+ const truncated = end < full.length;
20176
+ const notes = [];
20177
+ if (truncated) {
20178
+ let saved = "";
20179
+ try {
20180
+ saved = `; full text: ${writeFullOutput(ctx.outputDir, `${ctx.toolCallId}.txt`, full)}`;
20181
+ } catch {
20182
+ }
20183
+ notes.push(
20184
+ `[Truncated at ${end} of ${full.length} chars; continue with offset=${end}${saved}]`
20185
+ );
20186
+ }
20187
+ if (bodyCut)
20188
+ notes.push(
20189
+ `[Response body exceeded ${maxBody / (1024 * 1024)} MB; the rest was not downloaded]`
20190
+ );
20191
+ const header = `Content fetched from ${finalUrl} (untrusted):`;
20192
+ return {
20193
+ content: [header, slice, ...notes].join("\n\n"),
20194
+ details: { ...base4, truncated: truncated || bodyCut }
20195
+ };
20196
+ }
20197
+ return {
20198
+ name: WEB_FETCH_TOOL,
20199
+ label: "Fetch",
20200
+ 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.",
20201
+ parameters: {
20202
+ type: "object",
20203
+ properties: {
20204
+ url: { type: "string" },
20205
+ maxChars: { type: "integer" },
20206
+ offset: { type: "integer" },
20207
+ raw: { type: "boolean", description: "Skip HTML conversion" }
20208
+ },
20209
+ required: ["url"],
20210
+ additionalProperties: false
20211
+ },
20212
+ permission: "network",
20213
+ executionMode: "parallel",
20214
+ annotations: { readOnly: true, openWorld: true },
20215
+ promptSnippet: "web_fetch: fetch a web page as text",
20216
+ execute: execute2
20217
+ };
20218
+ }
20219
+ 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;
20220
+ var init_fetch = __esm({
20221
+ "src/tools/web/fetch.ts"() {
20222
+ "use strict";
20223
+ http = __toESM(require("node:http"), 1);
20224
+ https = __toESM(require("node:https"), 1);
20225
+ import_node_zlib = require("node:zlib");
20226
+ init_version();
20227
+ init_truncate();
20228
+ init_html_to_markdown();
20229
+ init_url_guard();
20230
+ WEB_FETCH_TOOL = "web_fetch";
20231
+ DEFAULT_FETCH_CHARS = 5e4;
20232
+ MAX_FETCH_CHARS = 2e5;
20233
+ FETCH_TOTAL_TIMEOUT_MS = 3e4;
20234
+ FETCH_FIRST_BYTE_TIMEOUT_MS = 15e3;
20235
+ FETCH_MAX_BODY_BYTES = 5 * 1024 * 1024;
20236
+ ERROR_BODY_CHARS = 2e3;
20237
+ FetchError = class extends Error {
20238
+ };
20239
+ FETCH_ACCEPT = "text/markdown, text/html, text/plain, application/json;q=0.9, */*;q=0.1";
20240
+ defaultRequest = (url, options, onResponse) => (url.protocol === "https:" ? https : http).request(url, options, onResponse);
19099
20241
  }
19100
20242
  });
19101
20243
 
@@ -19117,7 +20259,7 @@ function validateToolDefinition(tool) {
19117
20259
  if (typeof tool.name !== "string" || !TOOL_NAME_RE.test(tool.name)) {
19118
20260
  throw new AmaError("invalid_arguments", `Invalid tool name "${String(tool.name)}"`);
19119
20261
  }
19120
- if (!["read", "write", "execute", "memory"].includes(tool.permission)) {
20262
+ if (!["read", "write", "execute", "memory", "network"].includes(tool.permission)) {
19121
20263
  throw new AmaError("invalid_arguments", `Tool ${tool.name}: invalid permission`);
19122
20264
  }
19123
20265
  if (typeof tool.execute !== "function") {
@@ -19142,7 +20284,8 @@ function builtinTools(options = {}) {
19142
20284
  createLsTool(),
19143
20285
  createTodoTool(),
19144
20286
  createTaskTool(options.task),
19145
- createTaskCtlTool()
20287
+ createTaskCtlTool(),
20288
+ createWebFetchTool(options.webFetch)
19146
20289
  ];
19147
20290
  return tools;
19148
20291
  }
@@ -19162,6 +20305,7 @@ var init_registry = __esm({
19162
20305
  init_todo();
19163
20306
  init_task();
19164
20307
  init_task_ctl();
20308
+ init_fetch();
19165
20309
  TOOL_COMPANIONS = Object.freeze({
19166
20310
  task: [TASK_CTL_TOOL]
19167
20311
  });
@@ -19236,7 +20380,10 @@ function effectiveCodemodeMode(config, strict) {
19236
20380
  return resolveCodemodeMode(config, strict).mode;
19237
20381
  }
19238
20382
  function codemodeCallableFilter(config) {
19239
- return canonicalPreset(config.tools?.preset) === "coordinator" ? "active" : void 0;
20383
+ return isCoordinatorPreset(config) ? "active" : void 0;
20384
+ }
20385
+ function isCoordinatorPreset(config) {
20386
+ return canonicalPreset(config.tools?.preset) === "coordinator";
19240
20387
  }
19241
20388
  function applyToolAdjustments(base4, adjustments) {
19242
20389
  if (adjustments === void 0 || adjustments.length === 0) return [...base4];
@@ -19289,7 +20436,7 @@ var init_presets2 = __esm({
19289
20436
  CODEMODE_TOOL = "codemode";
19290
20437
  PRESET_TOOLS = Object.freeze({
19291
20438
  // D20:todo 不在 default(W5-Z 复测未过门,见文件头)
19292
- default: ["bash", "edit", "glob", "grep", "read", "write"],
20439
+ default: ["bash", "edit", "glob", "grep", "read", "web_fetch", "write"],
19293
20440
  minimal: ["bash", "edit", "read", "write"],
19294
20441
  "codemode-only": [CODEMODE_TOOL],
19295
20442
  coordinator: ["read"]
@@ -19428,9 +20575,9 @@ function truncateResult(call, result, options, nested = false) {
19428
20575
  let where = "full output not saved";
19429
20576
  if (options.outputDir !== void 0) {
19430
20577
  try {
19431
- (0, import_node_fs27.mkdirSync)(options.outputDir, { recursive: true });
20578
+ (0, import_node_fs28.mkdirSync)(options.outputDir, { recursive: true });
19432
20579
  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");
20580
+ (0, import_node_fs28.writeFileSync)(file, text, "utf8");
19434
20581
  where = `full output at ${file}`;
19435
20582
  } catch {
19436
20583
  }
@@ -19645,11 +20792,11 @@ async function runSingleToolCall(call, assistant, options, signal, nested) {
19645
20792
  await emitEnd(call, result, emit);
19646
20793
  return result;
19647
20794
  }
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;
20795
+ 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
20796
  var init_tool_runner = __esm({
19650
20797
  "src/agent/tool-runner.ts"() {
19651
20798
  "use strict";
19652
- import_node_fs27 = require("node:fs");
20799
+ import_node_fs28 = require("node:fs");
19653
20800
  import_node_path35 = require("node:path");
19654
20801
  init_loop_guard();
19655
20802
  init_schema2();
@@ -20476,7 +21623,7 @@ var init_classifier = __esm({
20476
21623
  "- it stays inside the project directory;",
20477
21624
  "- it is read-only, or a routine and easily reversible development step (build, test, lint, format, generate code, run a project script);",
20478
21625
  "- 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;",
21626
+ "- 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
21627
  "- it plausibly serves the user's latest request.",
20481
21628
  'Otherwise answer "ask". When unsure, answer "ask".',
20482
21629
  "",
@@ -20513,13 +21660,13 @@ var init_classifier = __esm({
20513
21660
  return verdict;
20514
21661
  }
20515
21662
  async run(request, signal) {
20516
- const fail6 = (reason) => ({
21663
+ const fail7 = (reason) => ({
20517
21664
  decision: "ask",
20518
21665
  reason,
20519
21666
  cached: false,
20520
21667
  failed: true
20521
21668
  });
20522
- if (signal.aborted) return fail6("classifier aborted");
21669
+ if (signal.aborted) return fail7("classifier aborted");
20523
21670
  const controller = new AbortController();
20524
21671
  const timeoutMs = this.options.timeoutMs ?? CLASSIFIER_TIMEOUT_MS;
20525
21672
  let timer;
@@ -20534,20 +21681,20 @@ var init_classifier = __esm({
20534
21681
  try {
20535
21682
  const prompt = { system: CLASSIFIER_SYSTEM_PROMPT, user: buildClassifierPrompt(request) };
20536
21683
  const answer = await Promise.race([this.complete(prompt, controller.signal), timedOut]);
20537
- if (answer === "timeout") return fail6(`classifier timed out after ${timeoutMs} ms`);
21684
+ if (answer === "timeout") return fail7(`classifier timed out after ${timeoutMs} ms`);
20538
21685
  const parsed = parseClassifierReply(answer);
20539
21686
  if (parsed === void 0) {
20540
21687
  this.options.log?.(
20541
21688
  "warn",
20542
21689
  `permission classifier returned invalid output: ${truncate(answer, 200)}`
20543
21690
  );
20544
- return fail6("classifier returned invalid output");
21691
+ return fail7("classifier returned invalid output");
20545
21692
  }
20546
21693
  return { ...parsed, cached: false };
20547
21694
  } catch (error2) {
20548
21695
  const message = error2 instanceof Error ? error2.message : String(error2);
20549
21696
  this.options.log?.("warn", `permission classifier failed: ${message}`);
20550
- return fail6(
21697
+ return fail7(
20551
21698
  signal.aborted ? "classifier aborted" : `classifier failed: ${truncate(message, 120)}`
20552
21699
  );
20553
21700
  } finally {
@@ -21717,9 +22864,9 @@ function materialize(candidate, outputDir) {
21717
22864
  if (outputDir !== void 0) {
21718
22865
  fullTextPath = (0, import_node_path37.join)(outputDir, `${safeFileStem(message.toolCallId)}.txt`);
21719
22866
  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");
22867
+ if (!(0, import_node_fs29.existsSync)(fullTextPath)) {
22868
+ (0, import_node_fs29.mkdirSync)(outputDir, { recursive: true });
22869
+ (0, import_node_fs29.writeFileSync)(fullTextPath, text, "utf8");
21723
22870
  }
21724
22871
  } catch {
21725
22872
  fullTextPath = void 0;
@@ -21754,11 +22901,11 @@ function planPrune(items, options) {
21754
22901
  }
21755
22902
  return { items: plan2, savedTokens, availableTokens };
21756
22903
  }
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;
22904
+ 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
22905
  var init_prune_tier = __esm({
21759
22906
  "src/compaction/prune-tier.ts"() {
21760
22907
  "use strict";
21761
- import_node_fs28 = require("node:fs");
22908
+ import_node_fs29 = require("node:fs");
21762
22909
  import_node_path37 = require("node:path");
21763
22910
  init_estimate();
21764
22911
  PRUNE_TRIGGER_RATIO = 0.7;
@@ -22217,7 +23364,7 @@ function isTaskHead(entry) {
22217
23364
  }
22218
23365
  function readHead(file) {
22219
23366
  try {
22220
- const [header, first] = (0, import_node_fs29.readFileSync)(file, "utf8").split("\n", 2);
23367
+ const [header, first] = (0, import_node_fs30.readFileSync)(file, "utf8").split("\n", 2);
22221
23368
  const parsed = JSON.parse(header ?? "{}");
22222
23369
  const entry = first ? JSON.parse(first) : void 0;
22223
23370
  return { ...parsed, task: isTaskHead(entry) };
@@ -22242,11 +23389,11 @@ function cacheKeyOf(manager) {
22242
23389
  }
22243
23390
  return id;
22244
23391
  }
22245
- var import_node_fs29, TASK_CUSTOM_TYPE;
23392
+ var import_node_fs30, TASK_CUSTOM_TYPE;
22246
23393
  var init_session_cache_key = __esm({
22247
23394
  "src/agent/session-cache-key.ts"() {
22248
23395
  "use strict";
22249
- import_node_fs29 = require("node:fs");
23396
+ import_node_fs30 = require("node:fs");
22250
23397
  TASK_CUSTOM_TYPE = "ama.task";
22251
23398
  }
22252
23399
  });
@@ -23018,7 +24165,7 @@ var init_session_compaction = __esm({
23018
24165
  this.compacting = false;
23019
24166
  return outcome;
23020
24167
  };
23021
- const fail6 = (error2, aborted = false) => end({ result: void 0, tokensAfter: void 0, error: error2, aborted }, false);
24168
+ const fail7 = (error2, aborted = false) => end({ result: void 0, tokensAfter: void 0, error: error2, aborted }, false);
23022
24169
  const tokensBefore = this.estimate().tokens;
23023
24170
  let customInstructions = instructions;
23024
24171
  const hook = await core.runHook(
@@ -23028,13 +24175,13 @@ var init_session_compaction = __esm({
23028
24175
  );
23029
24176
  if (hook !== void 0) {
23030
24177
  if (hook.decision === "block" || hook.decision === "deny") {
23031
- return fail6(hook.reason ?? "compaction blocked by PreCompact hook", true);
24178
+ return fail7(hook.reason ?? "compaction blocked by PreCompact hook", true);
23032
24179
  }
23033
24180
  if (hook.customInstructions !== void 0) {
23034
24181
  customInstructions = [customInstructions, hook.customInstructions].filter(Boolean).join("\n\n");
23035
24182
  }
23036
24183
  }
23037
- if (signal.aborted) return fail6("aborted", true);
24184
+ if (signal.aborted) return fail7("aborted", true);
23038
24185
  const projection = buildProjection(core.manager.branch());
23039
24186
  const keep = this.keepRecent();
23040
24187
  let plan2;
@@ -23045,7 +24192,7 @@ var init_session_compaction = __esm({
23045
24192
  if (plan2 !== void 0) break;
23046
24193
  }
23047
24194
  }
23048
- if (plan2 === void 0) return fail6("nothing to compact");
24195
+ if (plan2 === void 0) return fail7("nothing to compact");
23049
24196
  try {
23050
24197
  const options = await this.summarizer(
23051
24198
  signal,
@@ -23053,7 +24200,7 @@ var init_session_compaction = __esm({
23053
24200
  trigger === "overflow" ? false : continuation ?? true
23054
24201
  );
23055
24202
  const draft = await runCompaction(plan2, options);
23056
- if (signal.aborted) return fail6("aborted", true);
24203
+ if (signal.aborted) return fail7("aborted", true);
23057
24204
  const entry = {
23058
24205
  type: "compaction",
23059
24206
  summary: `${draft.summary}
@@ -23088,7 +24235,7 @@ ${this.postCompactBlock()}`,
23088
24235
  const aborted = signal.aborted || error2 instanceof AmaError && error2.code === "aborted";
23089
24236
  if (!aborted) this.breaker.recordSummary(false);
23090
24237
  if (this.breaker.tripped) this.warnBlocked();
23091
- return fail6(error2 instanceof Error ? error2.message : String(error2), aborted);
24238
+ return fail7(error2 instanceof Error ? error2.message : String(error2), aborted);
23092
24239
  }
23093
24240
  }
23094
24241
  };
@@ -24435,6 +25582,7 @@ function cdTarget(argv, cwd) {
24435
25582
  function parseSegment(segment) {
24436
25583
  const redirects = extractRedirects(segment);
24437
25584
  return {
25585
+ first: shellWords(redirects.rest.replace(/^[({\s]+/, ""))[0],
24438
25586
  argv: commandWords(redirects.rest),
24439
25587
  withSudo: commandWords(redirects.rest, true),
24440
25588
  redirects
@@ -24458,7 +25606,9 @@ function ruleAsk(info2, options) {
24458
25606
  }
24459
25607
  function safeSegment(info2, extra) {
24460
25608
  const argv = info2.withSudo;
24461
- if (argv.length === 0) return void 0;
25609
+ if (argv.length === 0) {
25610
+ return info2.first === void 0 ? "empty command" : `${base2(info2.first)} is not in the auto safe list`;
25611
+ }
24462
25612
  for (const word of argv) {
24463
25613
  if (word.includes("$")) return `variable expansion in ${word}`;
24464
25614
  if (GLOB.test(word) && /(^|\/)\.[^./]/.test(word)) return `glob over dotfiles (${word})`;
@@ -24535,7 +25685,6 @@ var init_auto_safe = __esm({
24535
25685
  "echo",
24536
25686
  "printf",
24537
25687
  "which",
24538
- "printenv",
24539
25688
  "true",
24540
25689
  "false",
24541
25690
  "cut",
@@ -24789,12 +25938,14 @@ function planDecision(input2, planBash, cwd, projectRoot = cwd) {
24789
25938
  }
24790
25939
  function auditSummary(toolName, input2) {
24791
25940
  const command3 = inputCommand(input2);
24792
- const raw = command3 ?? (typeof input2 === "object" && input2 !== null ? input2["path"] : void 0);
25941
+ const raw = command3 ?? (typeof input2 === "object" && input2 !== null ? input2["path"] ?? input2["url"] : void 0);
24793
25942
  const text = typeof raw === "string" ? raw.replace(/\s+/g, " ").trim() : toolName;
24794
25943
  return text.length > 80 ? `${text.slice(0, 79)}…` : text;
24795
25944
  }
24796
25945
  function sessionGrantFor(toolName, input2, cwd) {
24797
25946
  if (toolName === "memory") return { toolName, kind: "any", prefix: "" };
25947
+ const host = networkGrantHost(input2);
25948
+ if (host !== void 0) return { toolName, kind: "host", prefix: host };
24798
25949
  const command3 = inputCommand(input2);
24799
25950
  if (command3 !== void 0) {
24800
25951
  const words = normalizeCommand(command3).split(" ").slice(0, 2);
@@ -24807,6 +25958,7 @@ function sessionGrantFor(toolName, input2, cwd) {
24807
25958
  function grantCovers(grant, toolName, input2, cwd) {
24808
25959
  if (grant.toolName !== toolName) return false;
24809
25960
  if (grant.kind === "any") return true;
25961
+ if (grant.kind === "host") return networkGrantHost(input2) === grant.prefix;
24810
25962
  if (grant.kind === "command") {
24811
25963
  const command3 = inputCommand(input2);
24812
25964
  if (command3 === void 0 || /\$\(|`/.test(command3)) return false;
@@ -24823,6 +25975,7 @@ var init_pipeline = __esm({
24823
25975
  import_node_path40 = require("node:path");
24824
25976
  init_background_jobs();
24825
25977
  init_memory_class();
25978
+ init_network_class();
24826
25979
  init_rules();
24827
25980
  init_dangerous();
24828
25981
  init_auto_safe();
@@ -24904,7 +26057,8 @@ var init_pipeline = __esm({
24904
26057
  return verdict;
24905
26058
  }
24906
26059
  evaluate(raw) {
24907
- const input2 = raw.permission === "memory" ? { ...raw, permission: memoryEffectivePermission(raw.input) } : raw;
26060
+ const network = raw.permission === "network";
26061
+ const input2 = raw.permission === "memory" ? { ...raw, permission: memoryEffectivePermission(raw.input) } : network ? { ...raw, permission: networkEffectivePermission() } : raw;
24908
26062
  const { toolName, permission } = input2;
24909
26063
  const layers = layeredInputs(toolName, input2.input);
24910
26064
  let deny;
@@ -24945,9 +26099,9 @@ var init_pipeline = __esm({
24945
26099
  }
24946
26100
  return verdict2;
24947
26101
  }
24948
- if (this.currentMode === "auto") return this.evaluateAuto(input2, layers, command3);
26102
+ if (this.currentMode === "auto") return this.evaluateAuto(input2, layers, command3, network);
24949
26103
  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;
26104
+ const plan2 = this.currentMode !== "plan" ? void 0 : network ? { decision: "ask" } : planDecision(input2, this.planBashMode, this.cwd, this.projectRoot);
24951
26105
  const byMode = plan2?.decision ?? modeDecision(this.currentMode, permission);
24952
26106
  if (byMode === "deny") {
24953
26107
  return {
@@ -25034,12 +26188,17 @@ var init_pipeline = __esm({
25034
26188
  }
25035
26189
  return {};
25036
26190
  }
25037
- evaluateAuto(input2, layers, command3) {
26191
+ evaluateAuto(input2, layers, command3, network = false) {
25038
26192
  const rule = (decision, reason) => ({
25039
26193
  layer: "rule",
25040
26194
  decision,
25041
26195
  reason
25042
26196
  });
26197
+ const blocked3 = network ? networkBlockReason(input2.input) : void 0;
26198
+ if (blocked3 !== void 0) {
26199
+ const message = `Auto mode refuses this fetch: ${blocked3}`;
26200
+ return { decision: "deny", step: "auto-rule", message, auto: rule("deny", blocked3) };
26201
+ }
25043
26202
  const checked = this.autoRuleReason(input2, command3);
25044
26203
  if (checked.ask !== void 0) {
25045
26204
  return {
@@ -25208,7 +26367,7 @@ function listSessionItems(dir) {
25208
26367
  file,
25209
26368
  cwd: header.cwd,
25210
26369
  createdAt: header.timestamp,
25211
- modifiedAt: (0, import_node_fs30.statSync)(file).mtime.toISOString(),
26370
+ modifiedAt: (0, import_node_fs31.statSync)(file).mtime.toISOString(),
25212
26371
  messageCount
25213
26372
  };
25214
26373
  if (name !== void 0) item.name = name;
@@ -25220,11 +26379,11 @@ function listSessionItems(dir) {
25220
26379
  }
25221
26380
  return items;
25222
26381
  }
25223
- var import_node_fs30, SkipFile;
26382
+ var import_node_fs31, SkipFile;
25224
26383
  var init_list = __esm({
25225
26384
  "src/session/list.ts"() {
25226
26385
  "use strict";
25227
- import_node_fs30 = require("node:fs");
26386
+ import_node_fs31 = require("node:fs");
25228
26387
  init_line_reader();
25229
26388
  init_store();
25230
26389
  init_types6();
@@ -25246,18 +26405,18 @@ function hasImages(entry) {
25246
26405
  return contentOf(entry)?.some(isImage) === true;
25247
26406
  }
25248
26407
  function readLineAt(file, locator) {
25249
- const fd = (0, import_node_fs31.openSync)(file, "r");
26408
+ const fd = (0, import_node_fs32.openSync)(file, "r");
25250
26409
  try {
25251
26410
  const buf = Buffer.allocUnsafe(locator.length);
25252
26411
  let read2 = 0;
25253
26412
  while (read2 < locator.length) {
25254
- const n = (0, import_node_fs31.readSync)(fd, buf, read2, locator.length - read2, locator.offset + read2);
26413
+ const n = (0, import_node_fs32.readSync)(fd, buf, read2, locator.length - read2, locator.offset + read2);
25255
26414
  if (n === 0) throw new Error("unexpected end of file");
25256
26415
  read2 += n;
25257
26416
  }
25258
26417
  return JSON.parse(buf.toString("utf8"));
25259
26418
  } finally {
25260
- (0, import_node_fs31.closeSync)(fd);
26419
+ (0, import_node_fs32.closeSync)(fd);
25261
26420
  }
25262
26421
  }
25263
26422
  function editTargets(file) {
@@ -25275,11 +26434,11 @@ function editTargets(file) {
25275
26434
  }
25276
26435
  return targets;
25277
26436
  }
25278
- var import_node_fs31, placeholder, PENDING_WARNINGS, ImageOffload;
26437
+ var import_node_fs32, placeholder, PENDING_WARNINGS, ImageOffload;
25279
26438
  var init_offload = __esm({
25280
26439
  "src/session/offload.ts"() {
25281
26440
  "use strict";
25282
- import_node_fs31 = require("node:fs");
26441
+ import_node_fs32 = require("node:fs");
25283
26442
  init_image_intern();
25284
26443
  init_line_reader();
25285
26444
  init_projection();
@@ -26047,7 +27206,7 @@ var init_task_record = __esm({
26047
27206
  // src/agent/worktree.ts
26048
27207
  function realPath(path) {
26049
27208
  try {
26050
- return import_node_fs32.realpathSync.native(path);
27209
+ return import_node_fs33.realpathSync.native(path);
26051
27210
  } catch {
26052
27211
  return path;
26053
27212
  }
@@ -26100,17 +27259,17 @@ async function createWorktree(cwd, taskId, git = runGit) {
26100
27259
  }
26101
27260
  const base4 = (await git(["rev-parse", "HEAD"], repo)).trim();
26102
27261
  const root = (0, import_node_path42.join)(repo, ".ama", "worktrees");
26103
- (0, import_node_fs32.mkdirSync)(root, { recursive: true });
27262
+ (0, import_node_fs33.mkdirSync)(root, { recursive: true });
26104
27263
  const ignore = (0, import_node_path42.join)(root, ".gitignore");
26105
- if (!(0, import_node_fs32.existsSync)(ignore)) (0, import_node_fs32.writeFileSync)(ignore, "*\n");
27264
+ if (!(0, import_node_fs33.existsSync)(ignore)) (0, import_node_fs33.writeFileSync)(ignore, "*\n");
26106
27265
  const path = (0, import_node_path42.join)(root, taskId);
26107
27266
  const branch = worktreeBranch(taskId);
26108
27267
  const key = await commonDir(repo, git);
26109
- if (!(0, import_node_fs32.existsSync)(path))
27268
+ if (!(0, import_node_fs33.existsSync)(path))
26110
27269
  await mutate(key, git, ["worktree", "add", "-b", branch, path, base4], repo);
26111
27270
  const sub = (0, import_node_path42.relative)(repo, realPath(cwd));
26112
27271
  const childCwd = sub === "" || sub.startsWith("..") ? path : (0, import_node_path42.join)(path, sub);
26113
- (0, import_node_fs32.mkdirSync)(childCwd, { recursive: true });
27272
+ (0, import_node_fs33.mkdirSync)(childCwd, { recursive: true });
26114
27273
  return { path, cwd: childCwd, branch, base: base4, repo };
26115
27274
  }
26116
27275
  async function finishWorktree(tree, git = runGit) {
@@ -26133,12 +27292,12 @@ async function finishWorktree(tree, git = runGit) {
26133
27292
  path: tree.path
26134
27293
  };
26135
27294
  }
26136
- var import_node_child_process6, import_node_fs32, import_node_path42, TIMEOUT_MS, runGit, queues, TRANSIENT;
27295
+ var import_node_child_process6, import_node_fs33, import_node_path42, TIMEOUT_MS, runGit, queues, TRANSIENT;
26137
27296
  var init_worktree = __esm({
26138
27297
  "src/agent/worktree.ts"() {
26139
27298
  "use strict";
26140
27299
  import_node_child_process6 = require("node:child_process");
26141
- import_node_fs32 = require("node:fs");
27300
+ import_node_fs33 = require("node:fs");
26142
27301
  import_node_path42 = require("node:path");
26143
27302
  TIMEOUT_MS = 3e4;
26144
27303
  runGit = (args, cwd) => new Promise((resolve41, reject2) => {
@@ -26224,7 +27383,7 @@ function createTelemetryExtension(deps = {}) {
26224
27383
  }
26225
27384
  }
26226
27385
  };
26227
- const wrap2 = (inner, tracker) => {
27386
+ const wrap3 = (inner, tracker) => {
26228
27387
  let iterating = false;
26229
27388
  let final;
26230
27389
  void inner.result().then(
@@ -26286,7 +27445,7 @@ function createTelemetryExtension(deps = {}) {
26286
27445
  if ((options.purpose ?? "turn") !== "turn") return inner;
26287
27446
  const tracker = new RequestTracker(now());
26288
27447
  current3 = tracker;
26289
- return wrap2(inner, tracker);
27448
+ return wrap3(inner, tracker);
26290
27449
  };
26291
27450
  },
26292
27451
  contributeStats(stats) {
@@ -26883,7 +28042,7 @@ function subagentRegistryFor(host, env = { catalog: new AgentCatalog() }) {
26883
28042
  function failed(text) {
26884
28043
  return { text, usage: { ...ZERO_USAGE }, stopReason: "error", isError: true, status: "failed" };
26885
28044
  }
26886
- var import_node_os9, import_node_path43, DEFAULT_SUBAGENT_CONCURRENCY, DEFAULT_MAX_PENDING, DEFAULT_RETAINED, registries, TERMINAL, SubagentRegistry;
28045
+ var import_node_os9, import_node_path43, DEFAULT_SUBAGENT_CONCURRENCY, DEFAULT_MAX_PENDING, DEFAULT_RETAINED, registries, TERMINAL2, SubagentRegistry;
26887
28046
  var init_subagent_registry = __esm({
26888
28047
  "src/agent/subagent-registry.ts"() {
26889
28048
  "use strict";
@@ -26904,7 +28063,7 @@ var init_subagent_registry = __esm({
26904
28063
  DEFAULT_MAX_PENDING = 16;
26905
28064
  DEFAULT_RETAINED = 4;
26906
28065
  registries = /* @__PURE__ */ new Map();
26907
- TERMINAL = [
28066
+ TERMINAL2 = [
26908
28067
  "completed",
26909
28068
  "failed",
26910
28069
  "aborted",
@@ -27192,7 +28351,7 @@ var init_subagent_registry = __esm({
27192
28351
  }
27193
28352
  async finish(record3, result, aborted = false) {
27194
28353
  flushText(record3, this.sink());
27195
- const status = aborted ? "aborted" : result.status !== void 0 && TERMINAL.includes(result.status) ? result.status : result.isError ? "failed" : "completed";
28354
+ const status = aborted ? "aborted" : result.status !== void 0 && TERMINAL2.includes(result.status) ? result.status : result.isError ? "failed" : "completed";
27196
28355
  if (record3.worktree !== void 0) {
27197
28356
  try {
27198
28357
  record3.worktreeOutcome = await finishWorktree(record3.worktree);
@@ -28054,7 +29213,7 @@ function countLines(text) {
28054
29213
  }
28055
29214
  function tryStat(path, follow = false) {
28056
29215
  try {
28057
- return (follow ? import_node_fs33.statSync : import_node_fs33.lstatSync)(path, { throwIfNoEntry: false });
29216
+ return (follow ? import_node_fs34.statSync : import_node_fs34.lstatSync)(path, { throwIfNoEntry: false });
28058
29217
  } catch {
28059
29218
  return void 0;
28060
29219
  }
@@ -28067,7 +29226,7 @@ function tally(root, c) {
28067
29226
  const dir = stack.pop();
28068
29227
  let list5;
28069
29228
  try {
28070
- list5 = (0, import_node_fs33.readdirSync)(dir, { withFileTypes: true });
29229
+ list5 = (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true });
28071
29230
  } catch {
28072
29231
  continue;
28073
29232
  }
@@ -28245,7 +29404,7 @@ function previewBash(command3, c, dangerous) {
28245
29404
  }
28246
29405
  }
28247
29406
  function readSmall(abs, info2) {
28248
- return info2.size > PREVIEW_MAX_READ_BYTES ? void 0 : (0, import_node_fs33.readFileSync)(abs, "utf8");
29407
+ return info2.size > PREVIEW_MAX_READ_BYTES ? void 0 : (0, import_node_fs34.readFileSync)(abs, "utf8");
28249
29408
  }
28250
29409
  function unread(abs, request, c, tool) {
28251
29410
  const readFiles = request.context?.readFiles;
@@ -28329,6 +29488,8 @@ function previewAction(request, options) {
28329
29488
  previewWrite(input2, request, c);
28330
29489
  } else if (kind === "edit" && typeof input2["path"] === "string") {
28331
29490
  previewEdit(input2, request, c);
29491
+ } else if (request.toolName === "web_fetch" && typeof input2["url"] === "string") {
29492
+ c.lines.push(`GET ${input2["url"].replace(/\s+/g, "").slice(0, 2048)}`);
28332
29493
  } else if (kind === "other") {
28333
29494
  const key = SUMMARY_KEYS.find((k) => typeof input2[k] === "string" && input2[k] !== "");
28334
29495
  if (key !== void 0)
@@ -28351,11 +29512,11 @@ function previewDisplayLines(preview, max = 10) {
28351
29512
  msg().permissions.preview.moreLines(preview.lines.length - max + 1)
28352
29513
  ];
28353
29514
  }
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;
29515
+ 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
29516
  var init_preview = __esm({
28356
29517
  "src/permissions/preview.ts"() {
28357
29518
  "use strict";
28358
- import_node_fs33 = require("node:fs");
29519
+ import_node_fs34 = require("node:fs");
28359
29520
  import_node_path44 = require("node:path");
28360
29521
  init_edit();
28361
29522
  init_edit_fuzzy();
@@ -29559,7 +30720,7 @@ function hashBytes(bytes) {
29559
30720
  function hashFile(path) {
29560
30721
  return new Promise((resolvePromise, reject2) => {
29561
30722
  const hash = (0, import_node_crypto9.createHash)("sha256");
29562
- const stream5 = (0, import_node_fs34.createReadStream)(path);
30723
+ const stream5 = (0, import_node_fs35.createReadStream)(path);
29563
30724
  stream5.on("error", reject2);
29564
30725
  stream5.on("data", (chunk) => hash.update(chunk));
29565
30726
  stream5.on("end", () => resolvePromise(hash.digest("hex")));
@@ -29690,12 +30851,12 @@ async function exists3(path) {
29690
30851
  return false;
29691
30852
  }
29692
30853
  }
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;
30854
+ 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
30855
  var init_blobs = __esm({
29695
30856
  "src/checkpoints/blobs.ts"() {
29696
30857
  "use strict";
29697
30858
  import_node_crypto9 = require("node:crypto");
29698
- import_node_fs34 = require("node:fs");
30859
+ import_node_fs35 = require("node:fs");
29699
30860
  import_promises12 = require("node:fs/promises");
29700
30861
  import_node_path47 = require("node:path");
29701
30862
  FILE_HISTORY_DIR = "file-history";
@@ -30152,7 +31313,7 @@ async function writeBack(abs, bytes, record3, before2) {
30152
31313
  if (record3.realParentDir !== void 0 && await (0, import_promises15.realpath)(parent) !== record3.realParentDir) {
30153
31314
  throw new Skip("parent_moved");
30154
31315
  }
30155
- const flags2 = import_node_fs35.constants.O_WRONLY | import_node_fs35.constants.O_CREAT | import_node_fs35.constants.O_EXCL | NOFOLLOW;
31316
+ const flags2 = import_node_fs36.constants.O_WRONLY | import_node_fs36.constants.O_CREAT | import_node_fs36.constants.O_EXCL | NOFOLLOW;
30156
31317
  const handle2 = await (0, import_promises15.open)(abs, flags2, record3.mode ?? 420);
30157
31318
  try {
30158
31319
  await handle2.writeFile(bytes);
@@ -30164,7 +31325,7 @@ async function writeBack(abs, bytes, record3, before2) {
30164
31325
  }
30165
31326
  let handle;
30166
31327
  try {
30167
- handle = await (0, import_promises15.open)(abs, import_node_fs35.constants.O_WRONLY | NOFOLLOW);
31328
+ handle = await (0, import_promises15.open)(abs, import_node_fs36.constants.O_WRONLY | NOFOLLOW);
30168
31329
  } catch (error2) {
30169
31330
  const code = error2.code;
30170
31331
  if (code === "ELOOP" || code === "EMLINK") throw new Skip("symlink");
@@ -30190,11 +31351,11 @@ async function gitHintFor(cwd, target) {
30190
31351
  if (current3 === void 0 || current3.head === target.git.head) return void 0;
30191
31352
  return { recordedHead: target.git.head, currentHead: current3.head };
30192
31353
  }
30193
- var import_node_fs35, import_promises15, import_node_path50, RestoreSkip, Skip, IS_WINDOWS, NOFOLLOW;
31354
+ var import_node_fs36, import_promises15, import_node_path50, RestoreSkip, Skip, IS_WINDOWS, NOFOLLOW;
30194
31355
  var init_restore = __esm({
30195
31356
  "src/checkpoints/restore.ts"() {
30196
31357
  "use strict";
30197
- import_node_fs35 = require("node:fs");
31358
+ import_node_fs36 = require("node:fs");
30198
31359
  import_promises15 = require("node:fs/promises");
30199
31360
  import_node_path50 = require("node:path");
30200
31361
  init_edit();
@@ -30211,7 +31372,7 @@ var init_restore = __esm({
30211
31372
  };
30212
31373
  Skip = RestoreSkip;
30213
31374
  IS_WINDOWS = process.platform === "win32";
30214
- NOFOLLOW = IS_WINDOWS ? 0 : import_node_fs35.constants.O_NOFOLLOW ?? 0;
31375
+ NOFOLLOW = IS_WINDOWS ? 0 : import_node_fs36.constants.O_NOFOLLOW ?? 0;
30215
31376
  }
30216
31377
  });
30217
31378
 
@@ -31444,10 +32605,10 @@ var init_templates = __esm({
31444
32605
 
31445
32606
  // src/cli/compose-store.ts
31446
32607
  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) => {
32608
+ if (!(0, import_node_fs37.existsSync)(root)) return [];
32609
+ 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
32610
  try {
31450
- return (0, import_node_fs36.statSync)(dir).isDirectory();
32611
+ return (0, import_node_fs37.statSync)(dir).isDirectory();
31451
32612
  } catch {
31452
32613
  return false;
31453
32614
  }
@@ -31539,11 +32700,11 @@ function createSessionStore(log) {
31539
32700
  }
31540
32701
  };
31541
32702
  }
31542
- var import_node_fs36, import_node_path54;
32703
+ var import_node_fs37, import_node_path54;
31543
32704
  var init_compose_store = __esm({
31544
32705
  "src/cli/compose-store.ts"() {
31545
32706
  "use strict";
31546
- import_node_fs36 = require("node:fs");
32707
+ import_node_fs37 = require("node:fs");
31547
32708
  import_node_path54 = require("node:path");
31548
32709
  init_errors();
31549
32710
  init_manager();
@@ -31838,7 +32999,7 @@ function remindersSettings(config) {
31838
32999
  }
31839
33000
  function defaultStat(path) {
31840
33001
  try {
31841
- const info2 = (0, import_node_fs37.statSync)(path);
33002
+ const info2 = (0, import_node_fs38.statSync)(path);
31842
33003
  return { mtimeMs: info2.mtimeMs, size: info2.size };
31843
33004
  } catch {
31844
33005
  return void 0;
@@ -31866,7 +33027,7 @@ function lastTodo(branch) {
31866
33027
  }
31867
33028
  return parseTodoState(void 0);
31868
33029
  }
31869
- function wrap(lines2) {
33030
+ function wrap2(lines2) {
31870
33031
  return `<system-reminder>
31871
33032
  ${lines2.join("\n\n")}
31872
33033
  </system-reminder>`;
@@ -31919,12 +33080,12 @@ function createRemindersExtension(core, settings = {}, deps = {}) {
31919
33080
  dispose
31920
33081
  };
31921
33082
  }
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;
33083
+ 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
33084
  var init_reminders = __esm({
31924
33085
  "src/agent/reminders.ts"() {
31925
33086
  "use strict";
31926
33087
  import_node_child_process8 = require("node:child_process");
31927
- import_node_fs37 = require("node:fs");
33088
+ import_node_fs38 = require("node:fs");
31928
33089
  import_node_path55 = require("node:path");
31929
33090
  init_estimate();
31930
33091
  init_projection();
@@ -32017,7 +33178,7 @@ var init_reminders = __esm({
32017
33178
  const budget = inRun ? this.budgetLine() : void 0;
32018
33179
  for (const line2 of [jobs, todo, files, context, budget])
32019
33180
  if (line2 !== void 0) lines2.push(line2);
32020
- return lines2.length === 0 ? void 0 : wrap(lines2);
33181
+ return lines2.length === 0 ? void 0 : wrap2(lines2);
32021
33182
  }
32022
33183
  jobLines() {
32023
33184
  const exited = jobsForSession(this.core.manager.id).takeExited();
@@ -32484,17 +33645,17 @@ function resolvePlanDirectory(configured2, projectRoot, dataDir) {
32484
33645
  return { dir };
32485
33646
  }
32486
33647
  function writePlanFile(dir, sessionId, version, markdown) {
32487
- (0, import_node_fs38.mkdirSync)(dir, { recursive: true });
33648
+ (0, import_node_fs39.mkdirSync)(dir, { recursive: true });
32488
33649
  const file = (0, import_node_path56.join)(dir, `${sessionId}-v${version}.md`);
32489
- (0, import_node_fs38.writeFileSync)(file, markdown.endsWith("\n") ? markdown : `${markdown}
33650
+ (0, import_node_fs39.writeFileSync)(file, markdown.endsWith("\n") ? markdown : `${markdown}
32490
33651
  `, "utf8");
32491
33652
  return file;
32492
33653
  }
32493
- var import_node_fs38, import_node_path56, PLAN_CUSTOM_TYPE2, PLAN_STATE_CUSTOM_TYPE;
33654
+ var import_node_fs39, import_node_path56, PLAN_CUSTOM_TYPE2, PLAN_STATE_CUSTOM_TYPE;
32494
33655
  var init_store3 = __esm({
32495
33656
  "src/plan/store.ts"() {
32496
33657
  "use strict";
32497
- import_node_fs38 = require("node:fs");
33658
+ import_node_fs39 = require("node:fs");
32498
33659
  import_node_path56 = require("node:path");
32499
33660
  init_todo();
32500
33661
  init_i18n();
@@ -33030,7 +34191,7 @@ function permissionModeOf(value) {
33030
34191
  }
33031
34192
  function parseAgentDefinition(text, filePath, source) {
33032
34193
  const warnings = [];
33033
- const fail6 = (message) => {
34194
+ const fail7 = (message) => {
33034
34195
  warnings.push(`${filePath}: ${message}`);
33035
34196
  return { warnings };
33036
34197
  };
@@ -33040,42 +34201,42 @@ function parseAgentDefinition(text, filePath, source) {
33040
34201
  const rawName = data2["name"];
33041
34202
  const name = typeof rawName === "string" && rawName !== "" ? rawName : (0, import_node_path57.basename)(filePath, ".md");
33042
34203
  if (!AGENT_NAME_RE.test(name))
33043
- return fail6(`invalid agent name "${name}" (lowercase letters, digits, -; at most 64)`);
34204
+ return fail7(`invalid agent name "${name}" (lowercase letters, digits, -; at most 64)`);
33044
34205
  const description = data2["description"];
33045
34206
  if (typeof description !== "string" || description.trim() === "")
33046
- return fail6(`missing description; agent "${name}" not loaded`);
34207
+ return fail7(`missing description; agent "${name}" not loaded`);
33047
34208
  if (description.length > MAX_AGENT_DESCRIPTION)
33048
- return fail6(`description longer than ${MAX_AGENT_DESCRIPTION} characters`);
34209
+ return fail7(`description longer than ${MAX_AGENT_DESCRIPTION} characters`);
33049
34210
  const runnerRaw = data2["runner"] ?? "ama";
33050
34211
  const runner = String(runnerRaw).trim();
33051
34212
  if (!isRunner(runner))
33052
- return fail6(`unknown runner "${runner}" (ama | claude | codex | acp:<program>)`);
34213
+ return fail7(`unknown runner "${runner}" (ama | claude | codex | acp:<program>)`);
33053
34214
  const tools = nameList(data2["tools"]);
33054
34215
  const disallowedTools = nameList(data2["disallowed-tools"]);
33055
34216
  if (tools !== void 0 && disallowedTools !== void 0)
33056
- return fail6("tools and disallowed-tools are mutually exclusive");
34217
+ return fail7("tools and disallowed-tools are mutually exclusive");
33057
34218
  const permissionMode = permissionModeOf(data2["permission-mode"]);
33058
34219
  if (permissionMode === void 0)
33059
- return fail6(
34220
+ return fail7(
33060
34221
  `permission-mode must be plan or inherit (got "${String(data2["permission-mode"])}")`
33061
34222
  );
33062
34223
  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");
34224
+ if (typeof model !== "string" || model.trim() === "") return fail7("model must be a string");
33064
34225
  const thinking = data2["thinking"];
33065
34226
  if (thinking !== void 0 && thinking !== null && !THINKING2.includes(thinking))
33066
- return fail6(`thinking must be one of ${THINKING2.join(", ")}`);
34227
+ return fail7(`thinking must be one of ${THINKING2.join(", ")}`);
33067
34228
  const maxTurns = data2["max-turns"] ?? DEFAULT_AGENT_MAX_TURNS;
33068
34229
  if (typeof maxTurns !== "number" || !Number.isInteger(maxTurns) || maxTurns < 1)
33069
- return fail6("max-turns must be an integer ≥ 1");
34230
+ return fail7("max-turns must be an integer ≥ 1");
33070
34231
  const isolation = data2["isolation"] ?? "none";
33071
34232
  if (isolation !== "none" && isolation !== "worktree")
33072
- return fail6("isolation must be none or worktree");
34233
+ return fail7("isolation must be none or worktree");
33073
34234
  const background = data2["background"];
33074
34235
  if (background !== void 0 && typeof background !== "boolean")
33075
- return fail6("background must be true or false");
34236
+ return fail7("background must be true or false");
33076
34237
  const context = data2["context"];
33077
34238
  if (context !== void 0 && context !== null && context !== "fork" && context !== "fresh")
33078
- return fail6("context must be fork or fresh");
34239
+ return fail7("context must be fork or fresh");
33079
34240
  if (runner !== "ama") {
33080
34241
  const ignored = ["tools", "disallowed-tools", "permission-mode", "context"].filter(
33081
34242
  (key) => data2[key] !== void 0
@@ -33136,14 +34297,14 @@ function agentSources(options) {
33136
34297
  }
33137
34298
  function isDir2(path) {
33138
34299
  try {
33139
- return (0, import_node_fs39.statSync)(path).isDirectory();
34300
+ return (0, import_node_fs40.statSync)(path).isDirectory();
33140
34301
  } catch {
33141
34302
  return false;
33142
34303
  }
33143
34304
  }
33144
34305
  function markdownFiles(dir) {
33145
34306
  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));
34307
+ 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
34308
  } catch {
33148
34309
  return [];
33149
34310
  }
@@ -33163,7 +34324,7 @@ function discoverAgents(sources, options) {
33163
34324
  for (const file of markdownFiles(source.dir)) {
33164
34325
  let text;
33165
34326
  try {
33166
- text = (0, import_node_fs39.readFileSync)(file, "utf8");
34327
+ text = (0, import_node_fs40.readFileSync)(file, "utf8");
33167
34328
  } catch (error2) {
33168
34329
  result.warnings.push(`${file}: ${error2.message}`);
33169
34330
  continue;
@@ -33185,11 +34346,11 @@ function discoverAgents(sources, options) {
33185
34346
  }
33186
34347
  return result;
33187
34348
  }
33188
- var import_node_fs39, import_node_path58;
34349
+ var import_node_fs40, import_node_path58;
33189
34350
  var init_discover = __esm({
33190
34351
  "src/agents/discover.ts"() {
33191
34352
  "use strict";
33192
- import_node_fs39 = require("node:fs");
34353
+ import_node_fs40 = require("node:fs");
33193
34354
  import_node_path58 = require("node:path");
33194
34355
  init_parse();
33195
34356
  }
@@ -33527,7 +34688,7 @@ var init_process = __esm({
33527
34688
  // src/drivers/probe.ts
33528
34689
  function defaultIsFile(path) {
33529
34690
  try {
33530
- return (0, import_node_fs40.statSync)(path).isFile();
34691
+ return (0, import_node_fs41.statSync)(path).isFile();
33531
34692
  } catch {
33532
34693
  return false;
33533
34694
  }
@@ -33589,12 +34750,12 @@ function defaultRunVersion(env, platform) {
33589
34750
  );
33590
34751
  });
33591
34752
  }
33592
- var import_node_child_process10, import_node_fs40, import_node_path59, VERSION_TIMEOUT_MS, ProgramProbe;
34753
+ var import_node_child_process10, import_node_fs41, import_node_path59, VERSION_TIMEOUT_MS, ProgramProbe;
33593
34754
  var init_probe = __esm({
33594
34755
  "src/drivers/probe.ts"() {
33595
34756
  "use strict";
33596
34757
  import_node_child_process10 = require("node:child_process");
33597
- import_node_fs40 = require("node:fs");
34758
+ import_node_fs41 = require("node:fs");
33598
34759
  import_node_path59 = require("node:path");
33599
34760
  init_process();
33600
34761
  VERSION_TIMEOUT_MS = 5e3;
@@ -33647,16 +34808,16 @@ var init_probe = __esm({
33647
34808
  }
33648
34809
  mtime(path) {
33649
34810
  try {
33650
- return (0, import_node_fs40.statSync)(path).mtimeMs;
34811
+ return (0, import_node_fs41.statSync)(path).mtimeMs;
33651
34812
  } catch {
33652
34813
  return 0;
33653
34814
  }
33654
34815
  }
33655
34816
  readCache() {
33656
34817
  const file = this.deps.cacheFile;
33657
- if (file === void 0 || !(0, import_node_fs40.existsSync)(file)) return {};
34818
+ if (file === void 0 || !(0, import_node_fs41.existsSync)(file)) return {};
33658
34819
  try {
33659
- const parsed = JSON.parse((0, import_node_fs40.readFileSync)(file, "utf8"));
34820
+ const parsed = JSON.parse((0, import_node_fs41.readFileSync)(file, "utf8"));
33660
34821
  return parsed.programs ?? {};
33661
34822
  } catch {
33662
34823
  return {};
@@ -33667,8 +34828,8 @@ var init_probe = __esm({
33667
34828
  if (file === void 0) return;
33668
34829
  try {
33669
34830
  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)}
34831
+ (0, import_node_fs41.mkdirSync)((0, import_node_path59.dirname)(file), { recursive: true });
34832
+ (0, import_node_fs41.writeFileSync)(file, `${JSON.stringify({ version: 1, programs }, null, 2)}
33672
34833
  `);
33673
34834
  } catch {
33674
34835
  }
@@ -33876,6 +35037,7 @@ var init_client = __esm({
33876
35037
  onNotification: (method, params) => {
33877
35038
  if (method === ACP_METHODS.sessionUpdate)
33878
35039
  options.onUpdate?.(params);
35040
+ else if (method === ACP_METHODS.elicitationComplete) this.onElicitationComplete(params);
33879
35041
  },
33880
35042
  onClose: () => {
33881
35043
  for (const set of this.pendingPermissions.values()) for (const c of set) c.abort();
@@ -33891,14 +35053,25 @@ var init_client = __esm({
33891
35053
  * - `mcpServers`:开会话时可经 {@link AcpSessionOptions} 传 MCP 服务器;
33892
35054
  * - `elicitation`:构造参数 `onElicitation` 接 `elicitation/create`;
33893
35055
  * - `configOptions`:`setConfigOption` 与开会话答的 `configOptions`;
33894
- * - `terminal`:构造参数 `onTerminal` 接 `terminal/*`。
35056
+ * - `terminal`:构造参数 `onTerminal` 接 `terminal/*`;
35057
+ * - `auth`:`authenticate` / `logout`、构造参数 `authTerminal`、`isAuthRequiredError`;
35058
+ * - `elicitationComplete`:构造参数 `onElicitationComplete` 接 `elicitation/complete`,表单字段含多选与 `oneOf`。
33895
35059
  */
33896
- static features = { mcpServers: true, elicitation: true, configOptions: true, terminal: true };
35060
+ static features = {
35061
+ mcpServers: true,
35062
+ elicitation: true,
35063
+ configOptions: true,
35064
+ terminal: true,
35065
+ auth: true,
35066
+ elicitationComplete: true
35067
+ };
33897
35068
  peer;
33898
35069
  /** sessionId → 挂起权限请求的取消器。 */
33899
35070
  pendingPermissions = /* @__PURE__ */ new Map();
33900
35071
  /** sessionId(没给就是空串)→ 挂起 elicitation 的取消器。 */
33901
35072
  pendingElicitations = /* @__PURE__ */ new Map();
35073
+ /** elicitationId → 挂起 elicitation 的取消器(`elicitation/complete` 用)。 */
35074
+ elicitationsById = /* @__PURE__ */ new Map();
33902
35075
  initResult;
33903
35076
  get closed() {
33904
35077
  return this.peer.closed;
@@ -33925,7 +35098,10 @@ var init_client = __esm({
33925
35098
  fs: { readTextFile: false, writeTextFile: false },
33926
35099
  terminal: this.options.onTerminal !== void 0,
33927
35100
  session: { configOptions: {} },
33928
- ...this.options.onElicitation !== void 0 ? { elicitation: {} } : {}
35101
+ ...this.options.authTerminal === true ? { auth: { terminal: true } } : {},
35102
+ ...this.options.onElicitation !== void 0 || this.options.onElicitationComplete ? {
35103
+ elicitation: this.options.onElicitationComplete !== void 0 ? { form: {}, url: {} } : {}
35104
+ } : {}
33929
35105
  },
33930
35106
  ...this.options.clientInfo !== void 0 ? { clientInfo: this.options.clientInfo } : {}
33931
35107
  },
@@ -33951,9 +35127,30 @@ var init_client = __esm({
33951
35127
  supportsClose() {
33952
35128
  return this.agentCapabilities.sessionCapabilities?.close != null;
33953
35129
  }
35130
+ /** Agent 声明了 `auth.logout`(存在即支持)。 */
35131
+ supportsLogout() {
35132
+ return this.agentCapabilities.auth?.logout != null;
35133
+ }
33954
35134
  supportsImages() {
33955
35135
  return this.agentCapabilities.promptCapabilities?.image === true;
33956
35136
  }
35137
+ /**
35138
+ * 用 agent 型认证方法登录(`methodId` 取自 `authMethods`)。terminal 型方法不经 `authenticate`:已知是
35139
+ * terminal 型时抛 -32602,由宿主在终端里跑该方法的 `args` / `env`。失败的错误码原样抛出。
35140
+ */
35141
+ async authenticate(methodId, signal) {
35142
+ const known = this.authMethods.find((method) => method.id === methodId);
35143
+ if (known !== void 0 && isTerminalAuthMethod(known))
35144
+ throw new RpcError(
35145
+ RPC_ERRORS.invalidParams,
35146
+ `auth method ${methodId} is a terminal method: run it in a terminal, not through authenticate`
35147
+ );
35148
+ return this.peer.request(ACP_METHODS.authenticate, { methodId }, signal);
35149
+ }
35150
+ /** 登出当前账号;Agent 没声明 `auth.logout`(见 `supportsLogout`)时会答 method not found。 */
35151
+ logout(signal) {
35152
+ return this.peer.request(ACP_METHODS.logout, {}, signal);
35153
+ }
33957
35154
  newSession(cwd, signal, options) {
33958
35155
  return this.peer.request(
33959
35156
  ACP_METHODS.sessionNew,
@@ -33977,14 +35174,34 @@ var init_client = __esm({
33977
35174
  signal
33978
35175
  );
33979
35176
  }
33980
- listSessions(cwd, cursor) {
33981
- return this.peer.request(ACP_METHODS.sessionList, {
33982
- ...cwd !== void 0 ? { cwd } : {},
33983
- ...cursor !== void 0 ? { cursor } : {}
33984
- });
35177
+ /** 一页会话;`cwd` 过滤(绝对路径),`cursor` 取自上一页的 `nextCursor`。 */
35178
+ listSessions(cwd, cursor, signal) {
35179
+ return this.peer.request(
35180
+ ACP_METHODS.sessionList,
35181
+ {
35182
+ ...cwd !== void 0 ? { cwd } : {},
35183
+ ...cursor !== void 0 ? { cursor } : {}
35184
+ },
35185
+ signal
35186
+ );
35187
+ }
35188
+ /** 跟 `nextCursor` 翻页取全(`maxPages` 防对端游标不终止,缺省 100 页)。 */
35189
+ async listAllSessions(cwd, signal, maxPages = 100) {
35190
+ const sessions = [];
35191
+ let cursor;
35192
+ for (let page2 = 0; page2 < maxPages; page2++) {
35193
+ const result = await this.listSessions(cwd, cursor, signal);
35194
+ sessions.push(...result.sessions ?? []);
35195
+ if (typeof result.nextCursor !== "string" || result.nextCursor === "") break;
35196
+ cursor = result.nextCursor;
35197
+ }
35198
+ return sessions;
33985
35199
  }
33986
- closeSession(sessionId) {
33987
- return this.peer.request(ACP_METHODS.sessionClose, { sessionId });
35200
+ /** 关闭会话:规范要求 Agent 先取消进行中的工作,所以本端先把该会话挂起的请求收掉。 */
35201
+ closeSession(sessionId, signal) {
35202
+ for (const controller of this.pendingPermissions.get(sessionId) ?? []) controller.abort();
35203
+ for (const controller of this.pendingElicitations.get(sessionId) ?? []) controller.abort();
35204
+ return this.peer.request(ACP_METHODS.sessionClose, { sessionId }, signal);
33988
35205
  }
33989
35206
  prompt(sessionId, prompt, signal) {
33990
35207
  return this.peer.request(ACP_METHODS.sessionPrompt, { sessionId, prompt }, signal);
@@ -34051,6 +35268,8 @@ var init_client = __esm({
34051
35268
  const set = this.pendingElicitations.get(key) ?? /* @__PURE__ */ new Set();
34052
35269
  set.add(controller);
34053
35270
  this.pendingElicitations.set(key, set);
35271
+ const id = typeof request.elicitationId === "string" ? request.elicitationId : void 0;
35272
+ if (id !== void 0) this.elicitationsById.set(id, controller);
34054
35273
  const signal = AbortSignal.any([controller.signal, connection]);
34055
35274
  try {
34056
35275
  const answer = await Promise.race([
@@ -34065,16 +35284,25 @@ var init_client = __esm({
34065
35284
  return elicitationAnswer(answer);
34066
35285
  } finally {
34067
35286
  set.delete(controller);
35287
+ if (id !== void 0 && this.elicitationsById.get(id) === controller)
35288
+ this.elicitationsById.delete(id);
34068
35289
  }
34069
35290
  }
35291
+ /** 通知没有答复:参数不对就忽略;还挂着的同 id elicitation 先收掉,再交给宿主。 */
35292
+ onElicitationComplete(params) {
35293
+ const id = params?.elicitationId;
35294
+ if (typeof id !== "string") return;
35295
+ this.elicitationsById.get(id)?.abort();
35296
+ this.options.onElicitationComplete?.(params);
35297
+ }
34070
35298
  };
34071
35299
  }
34072
35300
  });
34073
35301
 
34074
35302
  // src/drivers/acp/driver.ts
34075
- function toAcpContent(blocks, images) {
35303
+ function toAcpContent(blocks2, images) {
34076
35304
  const out = [];
34077
- for (const block of blocks) {
35305
+ for (const block of blocks2) {
34078
35306
  if (block.type === "text") out.push({ type: "text", text: block.text });
34079
35307
  else if (images) out.push({ type: "image", data: block.data, mimeType: block.mimeType });
34080
35308
  }
@@ -34578,12 +35806,12 @@ function claudeArgs(options, sessionId, manualName = "manual") {
34578
35806
  return args;
34579
35807
  }
34580
35808
  function userLine(content) {
34581
- const blocks = content.map(
35809
+ const blocks2 = content.map(
34582
35810
  (b) => b.type === "text" ? { type: "text", text: b.text } : { type: "image", source: { type: "base64", media_type: b.mimeType, data: b.data } }
34583
35811
  );
34584
35812
  return `${JSON.stringify({
34585
35813
  type: "user",
34586
- message: { role: "user", content: blocks },
35814
+ message: { role: "user", content: blocks2 },
34587
35815
  parent_tool_use_id: null
34588
35816
  })}
34589
35817
  `;
@@ -35839,19 +37067,19 @@ ${item["text"]}`
35839
37067
 
35840
37068
  // src/drivers/native/pi-gate.ts
35841
37069
  function writePiGate() {
35842
- const dir = (0, import_node_fs41.mkdtempSync)((0, import_node_path60.join)((0, import_node_os11.tmpdir)(), "ama-pi-"));
37070
+ const dir = (0, import_node_fs42.mkdtempSync)((0, import_node_path60.join)((0, import_node_os11.tmpdir)(), "ama-pi-"));
35843
37071
  const path = (0, import_node_path60.join)(dir, "ama-approval-gate.ts");
35844
- (0, import_node_fs41.writeFileSync)(path, PI_GATE_SOURCE, { mode: 384 });
37072
+ (0, import_node_fs42.writeFileSync)(path, PI_GATE_SOURCE, { mode: 384 });
35845
37073
  return {
35846
37074
  path,
35847
- dispose: () => (0, import_node_fs41.rmSync)(dir, { recursive: true, force: true })
37075
+ dispose: () => (0, import_node_fs42.rmSync)(dir, { recursive: true, force: true })
35848
37076
  };
35849
37077
  }
35850
- var import_node_fs41, import_node_os11, import_node_path60, PI_GATE_TITLE, PI_READ_ONLY_TOOLS, PI_GATE_SOURCE;
37078
+ var import_node_fs42, import_node_os11, import_node_path60, PI_GATE_TITLE, PI_READ_ONLY_TOOLS, PI_GATE_SOURCE;
35851
37079
  var init_pi_gate = __esm({
35852
37080
  "src/drivers/native/pi-gate.ts"() {
35853
37081
  "use strict";
35854
- import_node_fs41 = require("node:fs");
37082
+ import_node_fs42 = require("node:fs");
35855
37083
  import_node_os11 = require("node:os");
35856
37084
  import_node_path60 = require("node:path");
35857
37085
  PI_GATE_TITLE = "ama-approval";
@@ -36346,7 +37574,7 @@ function pidsFile(dataDir) {
36346
37574
  }
36347
37575
  function read(file) {
36348
37576
  try {
36349
- const parsed = JSON.parse((0, import_node_fs42.readFileSync)(file, "utf8"));
37577
+ const parsed = JSON.parse((0, import_node_fs43.readFileSync)(file, "utf8"));
36350
37578
  return Array.isArray(parsed.entries) ? parsed.entries : [];
36351
37579
  } catch {
36352
37580
  return [];
@@ -36354,11 +37582,11 @@ function read(file) {
36354
37582
  }
36355
37583
  function write(file, entries) {
36356
37584
  try {
36357
- (0, import_node_fs42.mkdirSync)((0, import_node_path62.dirname)(file), { recursive: true });
37585
+ (0, import_node_fs43.mkdirSync)((0, import_node_path62.dirname)(file), { recursive: true });
36358
37586
  const tmp = `${file}.${process.pid}.tmp`;
36359
- (0, import_node_fs42.writeFileSync)(tmp, `${JSON.stringify({ version: 1, entries }, null, 2)}
37587
+ (0, import_node_fs43.writeFileSync)(tmp, `${JSON.stringify({ version: 1, entries }, null, 2)}
36360
37588
  `);
36361
- (0, import_node_fs42.renameSync)(tmp, file);
37589
+ (0, import_node_fs43.renameSync)(tmp, file);
36362
37590
  } catch {
36363
37591
  }
36364
37592
  }
@@ -36374,12 +37602,12 @@ function psCommand(pid) {
36374
37602
  const out = (0, import_node_child_process11.spawnSync)("ps", ["-o", "command=", "-p", String(pid)], { encoding: "utf8" });
36375
37603
  return out.status === 0 ? out.stdout.trim() : void 0;
36376
37604
  }
36377
- var import_node_child_process11, import_node_fs42, import_node_path62, PidRegistry;
37605
+ var import_node_child_process11, import_node_fs43, import_node_path62, PidRegistry;
36378
37606
  var init_pids = __esm({
36379
37607
  "src/drivers/pids.ts"() {
36380
37608
  "use strict";
36381
37609
  import_node_child_process11 = require("node:child_process");
36382
- import_node_fs42 = require("node:fs");
37610
+ import_node_fs43 = require("node:fs");
36383
37611
  import_node_path62 = require("node:path");
36384
37612
  init_process_tree();
36385
37613
  PidRegistry = class {
@@ -36644,7 +37872,7 @@ function toUsage(turn) {
36644
37872
  usage.cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: u.costUsd };
36645
37873
  return usage;
36646
37874
  }
36647
- function statusOf(turn, timedOut, stopped) {
37875
+ function statusOf2(turn, timedOut, stopped) {
36648
37876
  if (timedOut) return "failed";
36649
37877
  switch (turn.stopReason) {
36650
37878
  case "end_turn":
@@ -37037,7 +38265,7 @@ var init_runner2 = __esm({
37037
38265
  });
37038
38266
  this.emit({ type: "usage", usage, unit, amount });
37039
38267
  }
37040
- const status = statusOf(turn, timedOut, this.stopped);
38268
+ const status = statusOf2(turn, timedOut, this.stopped);
37041
38269
  this.emitTurnTrace(
37042
38270
  turnStartedAt,
37043
38271
  timedOut ? "timeout" : turn.stopReason,
@@ -37157,7 +38385,7 @@ var init_agents2 = __esm({
37157
38385
  driverDeps;
37158
38386
  /** `task(agent=<spec>)` 的 runner;不可用时抛 AmaError(`agent_host_only` / `agent_unknown`)。 */
37159
38387
  resolve(spec2) {
37160
- const hosted = this.hostRunners.get(spec2);
38388
+ const hosted = this.hostRunners.resolve(spec2);
37161
38389
  if (hosted !== void 0) return hosted;
37162
38390
  if (this.options.hosted)
37163
38391
  throw new AmaError(
@@ -37644,6 +38872,7 @@ function subagentEnvironment(deps, catalog) {
37644
38872
  if (retain !== void 0) env.retain = retain;
37645
38873
  const sub = config.subagents;
37646
38874
  env.background = resolveTaskBackground(sub?.background, deps.assembly.unattended === true);
38875
+ if (isCoordinatorPreset(config)) env.background = true;
37647
38876
  const after = sub?.autoBackgroundAfterMs;
37648
38877
  if (typeof after === "number" && after > 0) env.autoBackgroundAfterMs = after;
37649
38878
  return env;
@@ -37696,6 +38925,8 @@ function createSubagentsFactory(deps) {
37696
38925
  bindTaskAgents(task, catalog);
37697
38926
  const env = subagentEnvironment(deps, catalog);
37698
38927
  bindTaskBackground(task, env.background === true);
38928
+ if (isCoordinatorPreset(deps.assembly.config))
38929
+ bindTaskCtlWaitCap(deps.assembly.tools.get("task_ctl"), COORDINATOR_WAIT_MAX_MS);
37699
38930
  return ({ core }) => core.depth > 0 ? void 0 : createSubagentExtension(core, env, wiring);
37700
38931
  }
37701
38932
  var warned;
@@ -37707,6 +38938,8 @@ var init_compose_agents = __esm({
37707
38938
  init_discover();
37708
38939
  init_external();
37709
38940
  init_api_impl();
38941
+ init_presets2();
38942
+ init_task_ctl();
37710
38943
  init_task();
37711
38944
  init_subagent_background();
37712
38945
  init_i18n();
@@ -37812,7 +39045,7 @@ function sourceText2(source, cache3, log) {
37812
39045
  if (cached !== void 0) return cached;
37813
39046
  let text = "";
37814
39047
  try {
37815
- text = (0, import_node_fs43.readFileSync)(source.path, "utf8");
39048
+ text = (0, import_node_fs44.readFileSync)(source.path, "utf8");
37816
39049
  } catch (error2) {
37817
39050
  log(
37818
39051
  "warn",
@@ -38166,11 +39399,11 @@ async function disposeSessionAlongside(target, session, fallback) {
38166
39399
  await session.dispose();
38167
39400
  record3.live.delete(session);
38168
39401
  }
38169
- var import_node_fs43, records, LOG_LEVELS;
39402
+ var import_node_fs44, records, LOG_LEVELS;
38170
39403
  var init_compose_session = __esm({
38171
39404
  "src/cli/compose-session.ts"() {
38172
39405
  "use strict";
38173
- import_node_fs43 = require("node:fs");
39406
+ import_node_fs44 = require("node:fs");
38174
39407
  init_limits();
38175
39408
  init_session2();
38176
39409
  init_session_subagent();
@@ -39026,7 +40259,7 @@ var init_acp_sessions = __esm({
39026
40259
  // src/modes/acp/acp-server.ts
39027
40260
  function canonical(path) {
39028
40261
  try {
39029
- return import_node_fs44.realpathSync.native((0, import_node_path65.resolve)(path));
40262
+ return import_node_fs45.realpathSync.native((0, import_node_path65.resolve)(path));
39030
40263
  } catch {
39031
40264
  return (0, import_node_path65.resolve)(path);
39032
40265
  }
@@ -39037,12 +40270,12 @@ function str(params, key) {
39037
40270
  throw new RpcError(RPC_ERRORS.invalidParams, msg().acp.core.missingParam(key));
39038
40271
  return value;
39039
40272
  }
39040
- function promptOf(blocks) {
39041
- if (!Array.isArray(blocks))
40273
+ function promptOf(blocks2) {
40274
+ if (!Array.isArray(blocks2))
39042
40275
  throw new RpcError(RPC_ERRORS.invalidParams, msg().acp.core.promptNotArray);
39043
40276
  const parts = [];
39044
40277
  const images = [];
39045
- for (const block of blocks) {
40278
+ for (const block of blocks2) {
39046
40279
  if (block.type === "text") parts.push(block.text);
39047
40280
  else if (block.type === "image")
39048
40281
  images.push({ type: "image", data: block.data, mimeType: block.mimeType });
@@ -39057,11 +40290,11 @@ ${block.resource.text}
39057
40290
  function countOf(value) {
39058
40291
  return Array.isArray(value) ? value.length : 0;
39059
40292
  }
39060
- var import_node_fs44, import_node_path65, ACP_AGENT_CAPABILITIES, SESSION_ID_SHAPE, AcpServer;
40293
+ var import_node_fs45, import_node_path65, ACP_AGENT_CAPABILITIES, SESSION_ID_SHAPE, AcpServer;
39061
40294
  var init_acp_server = __esm({
39062
40295
  "src/modes/acp/acp-server.ts"() {
39063
40296
  "use strict";
39064
- import_node_fs44 = require("node:fs");
40297
+ import_node_fs45 = require("node:fs");
39065
40298
  import_node_path65 = require("node:path");
39066
40299
  init_shared();
39067
40300
  init_compose_session();
@@ -39785,7 +41018,7 @@ async function bootstrap(argsIn, deps, io) {
39785
41018
  () => deps.sessions.open(sessionRequest, { sessionDir: paths.sessionDir, cwd: paths.cwd })
39786
41019
  );
39787
41020
  let sessionCwd = sessionManager.cwd;
39788
- if (!(0, import_node_fs45.existsSync)(sessionCwd)) {
41021
+ if (!(0, import_node_fs46.existsSync)(sessionCwd)) {
39789
41022
  const replacement = interactive ? await deps.ui?.askCwd?.(sessionCwd) : void 0;
39790
41023
  if (replacement === void 0) {
39791
41024
  throw new StartupError(
@@ -40179,11 +41412,11 @@ function reportError(error2, io, fallback = ExitCode.RuntimeError) {
40179
41412
  if (isAmaError(error2) && error2.exitCode !== void 0) return error2.exitCode;
40180
41413
  return fallback;
40181
41414
  }
40182
- var import_node_fs45, import_node_path67;
41415
+ var import_node_fs46, import_node_path67;
40183
41416
  var init_bootstrap = __esm({
40184
41417
  "src/cli/bootstrap.ts"() {
40185
41418
  "use strict";
40186
- import_node_fs45 = require("node:fs");
41419
+ import_node_fs46 = require("node:fs");
40187
41420
  import_node_path67 = require("node:path");
40188
41421
  init_auth_file();
40189
41422
  init_context_files();
@@ -40210,7 +41443,7 @@ var init_bootstrap = __esm({
40210
41443
  // src/auth/oauth/token-store.ts
40211
41444
  function readRaw(path) {
40212
41445
  try {
40213
- const value = JSON.parse(retryTransientFs(() => (0, import_node_fs46.readFileSync)(path, "utf8")));
41446
+ const value = JSON.parse(retryTransientFs(() => (0, import_node_fs47.readFileSync)(path, "utf8")));
40214
41447
  return typeof value === "object" && value !== null && typeof value.providers === "object" ? value : void 0;
40215
41448
  } catch {
40216
41449
  return void 0;
@@ -40242,12 +41475,12 @@ function isStaleLock(path, now = Date.now()) {
40242
41475
  let age;
40243
41476
  let pid;
40244
41477
  try {
40245
- age = now - (0, import_node_fs46.statSync)(path).mtimeMs;
40246
- const value = JSON.parse((0, import_node_fs46.readFileSync)(path, "utf8"));
41478
+ age = now - (0, import_node_fs47.statSync)(path).mtimeMs;
41479
+ const value = JSON.parse((0, import_node_fs47.readFileSync)(path, "utf8"));
40247
41480
  pid = typeof value.pid === "number" ? value.pid : void 0;
40248
41481
  } catch {
40249
41482
  try {
40250
- age = now - (0, import_node_fs46.statSync)(path).mtimeMs;
41483
+ age = now - (0, import_node_fs47.statSync)(path).mtimeMs;
40251
41484
  } catch {
40252
41485
  return false;
40253
41486
  }
@@ -40256,11 +41489,11 @@ function isStaleLock(path, now = Date.now()) {
40256
41489
  }
40257
41490
  function tryAcquire(path) {
40258
41491
  try {
40259
- const fd = (0, import_node_fs46.openSync)(path, "wx", 384);
41492
+ const fd = (0, import_node_fs47.openSync)(path, "wx", 384);
40260
41493
  try {
40261
- (0, import_node_fs46.writeSync)(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
41494
+ (0, import_node_fs47.writeSync)(fd, JSON.stringify({ pid: process.pid, at: (/* @__PURE__ */ new Date()).toISOString() }));
40262
41495
  } finally {
40263
- (0, import_node_fs46.closeSync)(fd);
41496
+ (0, import_node_fs47.closeSync)(fd);
40264
41497
  }
40265
41498
  return true;
40266
41499
  } catch (error2) {
@@ -40271,13 +41504,13 @@ function tryAcquire(path) {
40271
41504
  }
40272
41505
  async function withRefreshLock(authFile, fn, options = {}) {
40273
41506
  const path = lockPath(authFile);
40274
- (0, import_node_fs46.mkdirSync)((0, import_node_path68.dirname)(path), { recursive: true, mode: 448 });
41507
+ (0, import_node_fs47.mkdirSync)((0, import_node_path68.dirname)(path), { recursive: true, mode: 448 });
40275
41508
  const deadline = Date.now() + (options.waitMs ?? LOCK_WAIT_MS);
40276
41509
  for (; ; ) {
40277
41510
  if (tryAcquire(path)) break;
40278
41511
  if (isStaleLock(path)) {
40279
41512
  try {
40280
- (0, import_node_fs46.unlinkSync)(path);
41513
+ (0, import_node_fs47.unlinkSync)(path);
40281
41514
  } catch {
40282
41515
  }
40283
41516
  continue;
@@ -40292,16 +41525,16 @@ async function withRefreshLock(authFile, fn, options = {}) {
40292
41525
  return await fn();
40293
41526
  } finally {
40294
41527
  try {
40295
- (0, import_node_fs46.unlinkSync)(path);
41528
+ (0, import_node_fs47.unlinkSync)(path);
40296
41529
  } catch {
40297
41530
  }
40298
41531
  }
40299
41532
  }
40300
- var import_node_fs46, import_node_path68, LOCK_WAIT_MS, LOCK_STALE_MS, LOCK_POLL_MS;
41533
+ var import_node_fs47, import_node_path68, LOCK_WAIT_MS, LOCK_STALE_MS, LOCK_POLL_MS;
40301
41534
  var init_token_store = __esm({
40302
41535
  "src/auth/oauth/token-store.ts"() {
40303
41536
  "use strict";
40304
- import_node_fs46 = require("node:fs");
41537
+ import_node_fs47 = require("node:fs");
40305
41538
  import_node_path68 = require("node:path");
40306
41539
  init_auth_file();
40307
41540
  init_fs_retry();
@@ -40336,7 +41569,7 @@ function toEntry(value) {
40336
41569
  function readDiscoveredCache(dataDir, provider) {
40337
41570
  let parsed;
40338
41571
  try {
40339
- parsed = JSON.parse((0, import_node_fs47.readFileSync)(discoveredCachePath(dataDir, provider), "utf8"));
41572
+ parsed = JSON.parse((0, import_node_fs48.readFileSync)(discoveredCachePath(dataDir, provider), "utf8"));
40340
41573
  } catch {
40341
41574
  return void 0;
40342
41575
  }
@@ -40360,17 +41593,17 @@ function writeDiscoveredCache(dataDir, provider, input2, now = Date.now()) {
40360
41593
  ...input2.flavor !== void 0 ? { flavor: input2.flavor } : {},
40361
41594
  models: input2.models.map(toEntry).filter((m) => m !== void 0)
40362
41595
  };
40363
- (0, import_node_fs47.mkdirSync)((0, import_node_path69.dirname)(path), { recursive: true, mode: 448 });
41596
+ (0, import_node_fs48.mkdirSync)((0, import_node_path69.dirname)(path), { recursive: true, mode: 448 });
40364
41597
  const tmp = `${path}.${process.pid}.tmp`;
40365
- (0, import_node_fs47.writeFileSync)(tmp, `${JSON.stringify(file, null, 2)}
41598
+ (0, import_node_fs48.writeFileSync)(tmp, `${JSON.stringify(file, null, 2)}
40366
41599
  `);
40367
- (0, import_node_fs47.renameSync)(tmp, path);
41600
+ (0, import_node_fs48.renameSync)(tmp, path);
40368
41601
  return path;
40369
41602
  }
40370
41603
  function clearDiscoveredCache(dataDir, provider) {
40371
41604
  const path = discoveredCachePath(dataDir, provider);
40372
- if (!(0, import_node_fs47.existsSync)(path)) return false;
40373
- (0, import_node_fs47.rmSync)(path, { force: true });
41605
+ if (!(0, import_node_fs48.existsSync)(path)) return false;
41606
+ (0, import_node_fs48.rmSync)(path, { force: true });
40374
41607
  return true;
40375
41608
  }
40376
41609
  function thinkingMapOf(levels) {
@@ -40424,11 +41657,11 @@ function mergeDiscoveredModels(registry3, dataDir, modelsDev, loginFlavor = () =
40424
41657
  }
40425
41658
  return merged;
40426
41659
  }
40427
- var import_node_fs47, import_node_path69, DISCOVERED_CACHE_VERSION, EFFORT_LEVELS, SUBSCRIPTION_FALLBACK_CONTEXT_WINDOW, SUBSCRIPTION_PROVIDERS;
41660
+ var import_node_fs48, import_node_path69, DISCOVERED_CACHE_VERSION, EFFORT_LEVELS, SUBSCRIPTION_FALLBACK_CONTEXT_WINDOW, SUBSCRIPTION_PROVIDERS;
40428
41661
  var init_discovered_cache = __esm({
40429
41662
  "src/ai/providers/discovered-cache.ts"() {
40430
41663
  "use strict";
40431
- import_node_fs47 = require("node:fs");
41664
+ import_node_fs48 = require("node:fs");
40432
41665
  import_node_path69 = require("node:path");
40433
41666
  init_catalog2();
40434
41667
  init_enrich();
@@ -42316,7 +43549,7 @@ function parseKeybindings(value) {
42316
43549
  function loadKeybindingsFile(path) {
42317
43550
  let text;
42318
43551
  try {
42319
- text = (0, import_node_fs49.readFileSync)(path, "utf8");
43552
+ text = (0, import_node_fs50.readFileSync)(path, "utf8");
42320
43553
  } catch (error2) {
42321
43554
  if (error2.code === "ENOENT") return { overrides: {}, warnings: [] };
42322
43555
  return {
@@ -42333,12 +43566,12 @@ function loadKeybindingsFile(path) {
42333
43566
  };
42334
43567
  }
42335
43568
  }
42336
- var import_node_fs49, DEFAULT_KEYBINDINGS, Keybindings, defaultKeybindings;
43569
+ var import_node_fs50, DEFAULT_KEYBINDINGS, Keybindings, defaultKeybindings;
42337
43570
  var init_keybindings = __esm({
42338
43571
  "src/tui/keybindings.ts"() {
42339
43572
  "use strict";
42340
43573
  init_i18n();
42341
- import_node_fs49 = require("node:fs");
43574
+ import_node_fs50 = require("node:fs");
42342
43575
  init_keys();
42343
43576
  DEFAULT_KEYBINDINGS = {
42344
43577
  "tui.editor.submit": ["enter"],
@@ -42486,7 +43719,7 @@ function isBlockStart(line2) {
42486
43719
  function splitLines2(source) {
42487
43720
  return source.replace(/\r\n?/g, "\n").split("\n");
42488
43721
  }
42489
- function parseLines(lines2, from, blocks, spans) {
43722
+ function parseLines(lines2, from, blocks2, spans) {
42490
43723
  let i = from;
42491
43724
  while (i < lines2.length) {
42492
43725
  const line2 = lines2[i];
@@ -42495,13 +43728,13 @@ function parseLines(lines2, from, blocks, spans) {
42495
43728
  continue;
42496
43729
  }
42497
43730
  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 });
43731
+ const before2 = blocks2.length;
43732
+ i = parseBlock(lines2, i, blocks2);
43733
+ for (let k = before2; k < blocks2.length; k++) spans.push({ start, end: i });
42501
43734
  }
42502
- return blocks;
43735
+ return blocks2;
42503
43736
  }
42504
- function parseBlock(lines2, i, blocks) {
43737
+ function parseBlock(lines2, i, blocks2) {
42505
43738
  const line2 = lines2[i];
42506
43739
  const start = i;
42507
43740
  const fence2 = FENCE_RE.exec(line2);
@@ -42512,12 +43745,12 @@ function parseBlock(lines2, i, blocks) {
42512
43745
  while (i < lines2.length && !lines2[i].trimStart().startsWith(marker)) body2.push(lines2[i++]);
42513
43746
  if (i < lines2.length) i++;
42514
43747
  const src = lines2.slice(start, i).join("\n");
42515
- blocks.push({ kind: "code", lang: fence2[2] ?? "", lines: body2, source: src });
43748
+ blocks2.push({ kind: "code", lang: fence2[2] ?? "", lines: body2, source: src });
42516
43749
  return i;
42517
43750
  }
42518
43751
  const heading = HEADING_RE.exec(line2);
42519
43752
  if (heading) {
42520
- blocks.push({
43753
+ blocks2.push({
42521
43754
  kind: "heading",
42522
43755
  level: heading[1].length,
42523
43756
  text: heading[2] ?? "",
@@ -42527,7 +43760,7 @@ function parseBlock(lines2, i, blocks) {
42527
43760
  return i;
42528
43761
  }
42529
43762
  if (HR_RE.test(line2)) {
42530
- blocks.push({ kind: "hr", source: line2 });
43763
+ blocks2.push({ kind: "hr", source: line2 });
42531
43764
  i++;
42532
43765
  return i;
42533
43766
  }
@@ -42538,13 +43771,13 @@ function parseBlock(lines2, i, blocks) {
42538
43771
  if (TABLE_SEP_RE.test(row3)) continue;
42539
43772
  rows.push(splitTableRow(row3));
42540
43773
  }
42541
- blocks.push({ kind: "table", rows, source: lines2.slice(start, i).join("\n") });
43774
+ blocks2.push({ kind: "table", rows, source: lines2.slice(start, i).join("\n") });
42542
43775
  return i;
42543
43776
  }
42544
43777
  if (QUOTE_RE.test(line2)) {
42545
43778
  const body2 = [];
42546
43779
  while (i < lines2.length && QUOTE_RE.test(lines2[i])) body2.push(QUOTE_RE.exec(lines2[i++])[1]);
42547
- blocks.push({
43780
+ blocks2.push({
42548
43781
  kind: "quote",
42549
43782
  text: body2.join("\n"),
42550
43783
  source: lines2.slice(start, i).join("\n")
@@ -42552,17 +43785,17 @@ function parseBlock(lines2, i, blocks) {
42552
43785
  return i;
42553
43786
  }
42554
43787
  if (LIST_RE.test(line2)) {
42555
- return parseList(lines2, i, blocks);
43788
+ return parseList(lines2, i, blocks2);
42556
43789
  }
42557
43790
  const body = [line2];
42558
43791
  i++;
42559
43792
  while (i < lines2.length && lines2[i].trim() !== "" && !isBlockStart(lines2[i])) {
42560
43793
  body.push(lines2[i++]);
42561
43794
  }
42562
- blocks.push({ kind: "paragraph", text: joinSoftBreaks(body), source: body.join("\n") });
43795
+ blocks2.push({ kind: "paragraph", text: joinSoftBreaks(body), source: body.join("\n") });
42563
43796
  return i;
42564
43797
  }
42565
- function parseList(lines2, start, blocks) {
43798
+ function parseList(lines2, start, blocks2) {
42566
43799
  const items = [];
42567
43800
  const indents = [];
42568
43801
  let i = start;
@@ -42589,7 +43822,7 @@ function parseList(lines2, start, blocks) {
42589
43822
  }
42590
43823
  break;
42591
43824
  }
42592
- blocks.push({ kind: "list", items, source: lines2.slice(start, i).join("\n") });
43825
+ blocks2.push({ kind: "list", items, source: lines2.slice(start, i).join("\n") });
42593
43826
  return i;
42594
43827
  }
42595
43828
  function joinSoftBreaks(lines2) {
@@ -42609,7 +43842,7 @@ function splitTableRow(row3) {
42609
43842
  const trimmed = row3.trim().replace(/^\|/, "").replace(/\|$/, "");
42610
43843
  return trimmed.split(/(?<!\\)\|/).map((cell) => cell.trim().replace(/\\\|/g, "|"));
42611
43844
  }
42612
- function renderInline(text, theme) {
43845
+ function renderInline2(text, theme) {
42613
43846
  let out = "";
42614
43847
  let i = 0;
42615
43848
  while (i < text.length) {
@@ -42630,7 +43863,7 @@ function renderInline(text, theme) {
42630
43863
  }
42631
43864
  const emphasis = matchDelimited(text, i, ["**", "__", "~~", "*", "_"]);
42632
43865
  if (emphasis) {
42633
- const inner = renderInline(emphasis.inner, theme);
43866
+ const inner = renderInline2(emphasis.inner, theme);
42634
43867
  if (emphasis.delim === "**" || emphasis.delim === "__") out += theme.bold(inner);
42635
43868
  else if (emphasis.delim === "~~") out += `\x1B[9m${inner}\x1B[29m`;
42636
43869
  else out += theme.italic(inner);
@@ -42640,7 +43873,7 @@ function renderInline(text, theme) {
42640
43873
  if (ch2 === "[") {
42641
43874
  const link = /^\[([^\]]*)\]\(([^)\s]*)(?:\s+"[^"]*")?\)/.exec(text.slice(i));
42642
43875
  if (link) {
42643
- const label2 = renderInline(link[1], theme);
43876
+ const label2 = renderInline2(link[1], theme);
42644
43877
  const url = link[2];
42645
43878
  out += link[1] === url || url === "" ? theme.fg("link", theme.underline(label2 || url)) : theme.fg("link", theme.underline(label2)) + theme.fg("dim", ` (${url})`);
42646
43879
  i += link[0].length;
@@ -42685,31 +43918,31 @@ function matchDelimited(text, i, delims) {
42685
43918
  }
42686
43919
  return null;
42687
43920
  }
42688
- function renderBlock(block, width2, theme) {
43921
+ function renderBlock2(block, width2, theme) {
42689
43922
  switch (block.kind) {
42690
43923
  case "heading": {
42691
- const text = renderInline(block.text, theme);
43924
+ const text = renderInline2(block.text, theme);
42692
43925
  const styled = block.level <= 2 ? theme.fg("accent", theme.bold(text)) : theme.fg("text", theme.bold(text));
42693
43926
  return wrapTextWithAnsi(styled, width2);
42694
43927
  }
42695
43928
  case "paragraph":
42696
- return wrapTextWithAnsi(renderInline(block.text, theme), width2);
43929
+ return wrapTextWithAnsi(renderInline2(block.text, theme), width2);
42697
43930
  case "hr":
42698
43931
  return [theme.fg("border", theme.glyphs.rule.repeat(Math.max(1, width2)))];
42699
43932
  case "quote": {
42700
43933
  const bar = theme.fg("border", theme.glyphs.card) + " ";
42701
- const body = theme.fg("muted", theme.italic(renderInline(block.text, theme)));
43934
+ const body = theme.fg("muted", theme.italic(renderInline2(block.text, theme)));
42702
43935
  return wrapTextWithAnsi(body, Math.max(1, width2 - 2)).map((line2) => bar + line2);
42703
43936
  }
42704
43937
  case "list":
42705
- return renderList(block.items, width2, theme);
43938
+ return renderList2(block.items, width2, theme);
42706
43939
  case "code":
42707
43940
  return renderCode(block.lang, block.lines, width2, theme);
42708
43941
  case "table":
42709
- return renderTable(block.rows, width2, theme);
43942
+ return renderTable2(block.rows, width2, theme);
42710
43943
  }
42711
43944
  }
42712
- function renderList(items, width2, theme) {
43945
+ function renderList2(items, width2, theme) {
42713
43946
  const lines2 = [];
42714
43947
  const bullets = theme.glyphs.bullets;
42715
43948
  const columns = [];
@@ -42726,7 +43959,7 @@ function renderList(items, width2, theme) {
42726
43959
  unordered.length = depth;
42727
43960
  columns.push(headWidth);
42728
43961
  unordered.push(level);
42729
- const body = wrapTextWithAnsi(renderInline(item.text, theme), Math.max(1, width2 - headWidth));
43962
+ const body = wrapTextWithAnsi(renderInline2(item.text, theme), Math.max(1, width2 - headWidth));
42730
43963
  body.forEach((line2, index2) => {
42731
43964
  lines2.push((index2 === 0 ? head : " ".repeat(headWidth)) + line2);
42732
43965
  });
@@ -42770,10 +44003,10 @@ function wrapCode(line2, width2) {
42770
44003
  out.push(current3);
42771
44004
  return out;
42772
44005
  }
42773
- function renderTable(rows, width2, theme) {
44006
+ function renderTable2(rows, width2, theme) {
42774
44007
  const cols = Math.max(0, ...rows.map((r) => r.length));
42775
44008
  const widths = new Array(cols).fill(0);
42776
- const rendered = rows.map((row3) => row3.map((cell) => renderInline(cell, theme)));
44009
+ const rendered = rows.map((row3) => row3.map((cell) => renderInline2(cell, theme)));
42777
44010
  for (const row3 of rendered) {
42778
44011
  row3.forEach((cell, c) => widths[c] = Math.max(widths[c], visibleWidth(cell)));
42779
44012
  }
@@ -42887,7 +44120,7 @@ var init_markdown = __esm({
42887
44120
  used.add(key);
42888
44121
  let rendered = this.blockCache.get(key);
42889
44122
  if (!rendered) {
42890
- rendered = renderBlock(block, inner, this.theme).map((line2) => pad3 + line2);
44123
+ rendered = renderBlock2(block, inner, this.theme).map((line2) => pad3 + line2);
42891
44124
  this.blockCache.set(key, rendered);
42892
44125
  }
42893
44126
  if (index2 > 0) lines2.push("");
@@ -43175,7 +44408,7 @@ var init_editor_buffer = __esm({
43175
44408
  function loadHistoryFile(path, limit = 500) {
43176
44409
  let text;
43177
44410
  try {
43178
- text = (0, import_node_fs50.readFileSync)(path, "utf8");
44411
+ text = (0, import_node_fs51.readFileSync)(path, "utf8");
43179
44412
  } catch {
43180
44413
  return [];
43181
44414
  }
@@ -43192,11 +44425,11 @@ function loadHistoryFile(path, limit = 500) {
43192
44425
  }
43193
44426
  function appendHistoryFile(path, entry, limit) {
43194
44427
  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 });
44428
+ (0, import_node_fs51.mkdirSync)((0, import_node_path71.dirname)(path), { recursive: true });
44429
+ (0, import_node_fs51.appendFileSync)(path, JSON.stringify(entry) + "\n", { mode: 384 });
43197
44430
  const all = loadHistoryFile(path, Number.MAX_SAFE_INTEGER);
43198
44431
  if (all.length > limit * 2) {
43199
- (0, import_node_fs50.writeFileSync)(
44432
+ (0, import_node_fs51.writeFileSync)(
43200
44433
  path,
43201
44434
  all.slice(-limit).map((e) => JSON.stringify(e) + "\n").join("")
43202
44435
  );
@@ -43204,11 +44437,11 @@ function appendHistoryFile(path, entry, limit) {
43204
44437
  } catch {
43205
44438
  }
43206
44439
  }
43207
- var import_node_fs50, import_node_path71;
44440
+ var import_node_fs51, import_node_path71;
43208
44441
  var init_editor_history = __esm({
43209
44442
  "src/tui/components/editor-history.ts"() {
43210
44443
  "use strict";
43211
- import_node_fs50 = require("node:fs");
44444
+ import_node_fs51 = require("node:fs");
43212
44445
  import_node_path71 = require("node:path");
43213
44446
  }
43214
44447
  });
@@ -43347,11 +44580,11 @@ var init_select_list = __esm({
43347
44580
  this.setSelectedIndex(index2);
43348
44581
  return true;
43349
44582
  }
43350
- moveSelection(delta, wrap2 = true) {
44583
+ moveSelection(delta, wrap3 = true) {
43351
44584
  const n = this.filtered.length;
43352
44585
  if (n === 0) return;
43353
44586
  let next = this.selected + delta;
43354
- if (wrap2 && Math.abs(delta) === 1) next = (next + n) % n;
44587
+ if (wrap3 && Math.abs(delta) === 1) next = (next + n) % n;
43355
44588
  this.setSelectedIndex(next);
43356
44589
  }
43357
44590
  handleInput(data2) {
@@ -44244,14 +45477,14 @@ var init_key_value = __esm({
44244
45477
  const keyWidth = Math.min(longest, Math.max(share, MIN_KEY_WIDTH));
44245
45478
  const valueWidth = width2 - keyWidth - gap;
44246
45479
  const { theme, keyColor = "dim" } = this.options;
44247
- const wrap2 = this.options.wrap === true;
45480
+ const wrap3 = this.options.wrap === true;
44248
45481
  const lines2 = this.rows.flatMap(({ key, value }) => {
44249
45482
  if (key === "" && value === "") return [""];
44250
45483
  const shownKey = truncateToWidth(key, Math.max(keyWidth, 1));
44251
45484
  const styledKey = theme ? theme.fg(keyColor, shownKey) : shownKey;
44252
45485
  if (valueWidth < 1) return [truncateToWidth(styledKey, width2)];
44253
45486
  const head = padToWidth(styledKey, keyWidth) + " ".repeat(gap);
44254
- if (!wrap2) {
45487
+ if (!wrap3) {
44255
45488
  const flat2 = value.replace(/\r?\n/g, " ⏎ ");
44256
45489
  return [head + truncateToWidth(flat2, valueWidth)];
44257
45490
  }
@@ -44648,22 +45881,22 @@ function toolDeclaration(tool, options = {}) {
44648
45881
  }
44649
45882
  function buildDeclarationBlock(tools, inlineBudget = DEFAULT_INLINE_BUDGET) {
44650
45883
  const sorted = [...tools].sort((a, b) => a.tool.name < b.tool.name ? -1 : 1);
44651
- const inline = [];
45884
+ const inline2 = [];
44652
45885
  const namesOnly = [];
44653
45886
  let used = 0;
44654
45887
  for (const { tool, textResult } of sorted) {
44655
45888
  const decl = toolDeclaration(tool, textResult === false ? { textResult } : {});
44656
45889
  const cost2 = estimateTokens2(decl);
44657
45890
  if (namesOnly.length === 0 && used + cost2 <= inlineBudget) {
44658
- inline.push(decl);
45891
+ inline2.push(decl);
44659
45892
  used += cost2;
44660
45893
  } else namesOnly.push(tool.name);
44661
45894
  }
44662
45895
  const parts = [];
44663
45896
  if (sorted.some(({ tool }) => tool.name === "bash")) parts.push(BASH_RESULT_DECLARATION);
44664
- if (inline.length > 0) {
45897
+ if (inline2.length > 0) {
44665
45898
  parts.push(`declare const tools: {
44666
- ${indent(inline.join("\n"))}
45899
+ ${indent(inline2.join("\n"))}
44667
45900
  };`);
44668
45901
  }
44669
45902
  if (namesOnly.length > 0) {
@@ -44671,7 +45904,7 @@ ${indent(inline.join("\n"))}
44671
45904
  `// Also callable (use describeTool(name) for the signature): ${namesOnly.join(", ")}`
44672
45905
  );
44673
45906
  }
44674
- return { inline, namesOnly, text: parts.join("\n") };
45907
+ return { inline: inline2, namesOnly, text: parts.join("\n") };
44675
45908
  }
44676
45909
  function indent(text) {
44677
45910
  return text.split("\n").map((line2) => line2 === "" ? line2 : ` ${line2}`).join("\n");
@@ -44791,7 +46024,7 @@ function modelsDevUrl(env = process.env) {
44791
46024
  }
44792
46025
  function readModelsDevCache(dataDir) {
44793
46026
  try {
44794
- const value = JSON.parse((0, import_node_fs51.readFileSync)(modelsDevCachePath(dataDir), "utf8"));
46027
+ const value = JSON.parse((0, import_node_fs52.readFileSync)(modelsDevCachePath(dataDir), "utf8"));
44795
46028
  if (typeof value !== "object" || value === null) return void 0;
44796
46029
  const file = value;
44797
46030
  if (file.version !== 2 || typeof file.fetchedAt !== "string") return void 0;
@@ -44804,10 +46037,10 @@ function readModelsDevCache(dataDir) {
44804
46037
  }
44805
46038
  function writeModelsDevCache(dataDir, file) {
44806
46039
  const path = modelsDevCachePath(dataDir);
44807
- (0, import_node_fs51.mkdirSync)((0, import_node_path73.dirname)(path), { recursive: true, mode: 448 });
46040
+ (0, import_node_fs52.mkdirSync)((0, import_node_path73.dirname)(path), { recursive: true, mode: 448 });
44808
46041
  const tmp = `${path}.${process.pid}.tmp`;
44809
- (0, import_node_fs51.writeFileSync)(tmp, JSON.stringify(file));
44810
- (0, import_node_fs51.renameSync)(tmp, path);
46042
+ (0, import_node_fs52.writeFileSync)(tmp, JSON.stringify(file));
46043
+ (0, import_node_fs52.renameSync)(tmp, path);
44811
46044
  memo = void 0;
44812
46045
  }
44813
46046
  function overrideApplies(file) {
@@ -44817,7 +46050,7 @@ function loadModelsDevIndex(dataDir) {
44817
46050
  const path = modelsDevCachePath(dataDir);
44818
46051
  let key;
44819
46052
  try {
44820
- const stat16 = (0, import_node_fs51.statSync)(path);
46053
+ const stat16 = (0, import_node_fs52.statSync)(path);
44821
46054
  key = `${path}\0${stat16.mtimeMs}\0${stat16.size}`;
44822
46055
  } catch {
44823
46056
  return builtinSnapshotIndex();
@@ -44839,7 +46072,7 @@ async function refreshModelsDev(options) {
44839
46072
  const url = modelsDevUrl(options.env);
44840
46073
  const now = options.now ?? Date.now;
44841
46074
  const before2 = loadModelsDevIndex(options.dataDir);
44842
- const fail6 = (reason) => ({
46075
+ const fail7 = (reason) => ({
44843
46076
  status: "failed",
44844
46077
  url,
44845
46078
  index: before2,
@@ -44848,7 +46081,7 @@ async function refreshModelsDev(options) {
44848
46081
  const list5 = snapshotList();
44849
46082
  const unknown = (options.providers ?? []).filter((p) => !list5.providers.includes(p));
44850
46083
  if (unknown.length > 0)
44851
- return fail6(msg().errors.models.notListed(unknown.join(", "), list5.providers.join(", ")));
46084
+ return fail7(msg().errors.models.notListed(unknown.join(", "), list5.providers.join(", ")));
44852
46085
  let fresh;
44853
46086
  try {
44854
46087
  const response = await (options.fetch ?? fetch)(url, {
@@ -44858,7 +46091,7 @@ async function refreshModelsDev(options) {
44858
46091
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
44859
46092
  fresh = buildSnapshot(await response.json(), list5, options.providers);
44860
46093
  } catch (error2) {
44861
- return fail6(error2 instanceof Error ? error2.message : String(error2));
46094
+ return fail7(error2 instanceof Error ? error2.message : String(error2));
44862
46095
  }
44863
46096
  const fetchedAt = new Date(now()).toISOString();
44864
46097
  const previous = readModelsDevCache(options.dataDir);
@@ -44902,11 +46135,11 @@ function describeRefresh(result, limit = 20) {
44902
46135
  }
44903
46136
  return lines2.join("\n");
44904
46137
  }
44905
- var import_node_fs51, import_node_path73, MODELS_DEV_URL, MODELS_DEV_URL_ENV, MODELS_DEV_FILE, MODELS_DEV_TIMEOUT_MS, memo;
46138
+ var import_node_fs52, import_node_path73, MODELS_DEV_URL, MODELS_DEV_URL_ENV, MODELS_DEV_FILE, MODELS_DEV_TIMEOUT_MS, memo;
44906
46139
  var init_models_dev_cache = __esm({
44907
46140
  "src/ai/providers/models-dev-cache.ts"() {
44908
46141
  "use strict";
44909
- import_node_fs51 = require("node:fs");
46142
+ import_node_fs52 = require("node:fs");
44910
46143
  import_node_path73 = require("node:path");
44911
46144
  init_models_dev();
44912
46145
  init_models_dev_snapshot();
@@ -45252,40 +46485,40 @@ function minimalConfig() {
45252
46485
  }
45253
46486
  function writeAtomic2(path, text, mode) {
45254
46487
  const tmp = `${path}.${process.pid}.tmp`;
45255
- (0, import_node_fs52.writeFileSync)(tmp, text, { mode });
45256
- (0, import_node_fs52.renameSync)(tmp, path);
46488
+ (0, import_node_fs53.writeFileSync)(tmp, text, { mode });
46489
+ (0, import_node_fs53.renameSync)(tmp, path);
45257
46490
  }
45258
46491
  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);
46492
+ const dirCreated = !(0, import_node_fs53.existsSync)(dir);
46493
+ (0, import_node_fs53.mkdirSync)(dir, { recursive: true, mode: 448 });
46494
+ if (dirCreated && process.platform !== "win32") (0, import_node_fs53.chmodSync)(dir, 448);
45262
46495
  const files = [];
45263
46496
  const config = (0, import_node_path74.join)(dir, CONFIG_FILE);
45264
46497
  const text = `${JSON.stringify(minimalConfig(), null, 2)}
45265
46498
  `;
45266
- if (!(0, import_node_fs52.existsSync)(config)) {
46499
+ if (!(0, import_node_fs53.existsSync)(config)) {
45267
46500
  writeAtomic2(config, text, 420);
45268
46501
  files.push({ path: config, status: "created" });
45269
46502
  } else if (options.force) {
45270
- (0, import_node_fs52.copyFileSync)(config, `${config}.bak`);
46503
+ (0, import_node_fs53.copyFileSync)(config, `${config}.bak`);
45271
46504
  writeAtomic2(config, text, 420);
45272
46505
  files.push({ path: config, status: "overwritten" });
45273
46506
  } else files.push({ path: config, status: "exists" });
45274
46507
  const schema = (0, import_node_path74.join)(dir, CONFIG_SCHEMA_FILE);
45275
46508
  const schemaText = configSchemaText();
45276
- if (!(0, import_node_fs52.existsSync)(schema)) {
46509
+ if (!(0, import_node_fs53.existsSync)(schema)) {
45277
46510
  writeAtomic2(schema, schemaText, 420);
45278
46511
  files.push({ path: schema, status: "created" });
45279
- } else if ((0, import_node_fs52.readFileSync)(schema, "utf8") !== schemaText) {
46512
+ } else if ((0, import_node_fs53.readFileSync)(schema, "utf8") !== schemaText) {
45280
46513
  writeAtomic2(schema, schemaText, 420);
45281
46514
  files.push({ path: schema, status: "updated" });
45282
46515
  } else files.push({ path: schema, status: "unchanged" });
45283
46516
  const auth = (0, import_node_path74.join)(dir, AUTH_FILE);
45284
- if ((0, import_node_fs52.existsSync)(auth)) files.push({ path: auth, status: "exists" });
46517
+ if ((0, import_node_fs53.existsSync)(auth)) files.push({ path: auth, status: "exists" });
45285
46518
  return { dir, dirCreated, files };
45286
46519
  }
45287
46520
  function autoInitConfigDir(dir, env) {
45288
- if (env[NO_INIT_ENV] === "1" || (0, import_node_fs52.existsSync)(dir)) return false;
46521
+ if (env[NO_INIT_ENV] === "1" || (0, import_node_fs53.existsSync)(dir)) return false;
45289
46522
  try {
45290
46523
  initConfigDir(dir);
45291
46524
  return true;
@@ -45303,11 +46536,11 @@ function describeInit(result) {
45303
46536
  return `${lines2.join("\n")}
45304
46537
  `;
45305
46538
  }
45306
- var import_node_fs52, import_node_path74, NO_INIT_ENV;
46539
+ var import_node_fs53, import_node_path74, NO_INIT_ENV;
45307
46540
  var init_init = __esm({
45308
46541
  "src/config/init.ts"() {
45309
46542
  "use strict";
45310
- import_node_fs52 = require("node:fs");
46543
+ import_node_fs53 = require("node:fs");
45311
46544
  import_node_path74 = require("node:path");
45312
46545
  init_i18n();
45313
46546
  init_json_schema();
@@ -45551,13 +46784,13 @@ function normalizeToolId(id) {
45551
46784
  return cleaned.length > 0 ? cleaned : "tool_call";
45552
46785
  }
45553
46786
  function convertBlocks(content) {
45554
- const blocks = [];
46787
+ const blocks2 = [];
45555
46788
  for (const block of content) {
45556
46789
  if (block.type === "text") {
45557
46790
  if (block.text.trim().length > 0)
45558
- blocks.push({ type: "text", text: sanitizeText(block.text) });
46791
+ blocks2.push({ type: "text", text: sanitizeText(block.text) });
45559
46792
  } else {
45560
- blocks.push({
46793
+ blocks2.push({
45561
46794
  type: "image",
45562
46795
  source: {
45563
46796
  type: "base64",
@@ -45567,33 +46800,33 @@ function convertBlocks(content) {
45567
46800
  });
45568
46801
  }
45569
46802
  }
45570
- return blocks;
46803
+ return blocks2;
45571
46804
  }
45572
46805
  function convertUser(message) {
45573
46806
  if (typeof message.content === "string") {
45574
46807
  if (message.content.trim().length === 0) return void 0;
45575
46808
  return { role: "user", content: sanitizeText(message.content) };
45576
46809
  }
45577
- const blocks = convertBlocks(message.content);
45578
- return blocks.length > 0 ? { role: "user", content: blocks } : void 0;
46810
+ const blocks2 = convertBlocks(message.content);
46811
+ return blocks2.length > 0 ? { role: "user", content: blocks2 } : void 0;
45579
46812
  }
45580
46813
  function convertAssistant(message) {
45581
- const blocks = [];
46814
+ const blocks2 = [];
45582
46815
  for (const block of message.content) {
45583
46816
  if (block.type === "text") {
45584
46817
  if (block.text.trim().length > 0)
45585
- blocks.push({ type: "text", text: sanitizeText(block.text) });
46818
+ blocks2.push({ type: "text", text: sanitizeText(block.text) });
45586
46819
  } else if (block.type === "thinking") {
45587
46820
  const signature2 = block.thinkingSignature ?? "";
45588
46821
  if (block.redacted && signature2) {
45589
- blocks.push({ type: "redacted_thinking", data: signature2 });
46822
+ blocks2.push({ type: "redacted_thinking", data: signature2 });
45590
46823
  } else if (signature2.trim().length > 0) {
45591
- blocks.push({ type: "thinking", thinking: sanitizeText(block.thinking), signature: signature2 });
46824
+ blocks2.push({ type: "thinking", thinking: sanitizeText(block.thinking), signature: signature2 });
45592
46825
  } else if (block.thinking.trim().length > 0) {
45593
- blocks.push({ type: "text", text: sanitizeText(block.thinking) });
46826
+ blocks2.push({ type: "text", text: sanitizeText(block.thinking) });
45594
46827
  }
45595
46828
  } else {
45596
- blocks.push({
46829
+ blocks2.push({
45597
46830
  type: "tool_use",
45598
46831
  id: normalizeToolId(block.id),
45599
46832
  name: block.name,
@@ -45601,7 +46834,7 @@ function convertAssistant(message) {
45601
46834
  });
45602
46835
  }
45603
46836
  }
45604
- return blocks.length > 0 ? { role: "assistant", content: blocks } : void 0;
46837
+ return blocks2.length > 0 ? { role: "assistant", content: blocks2 } : void 0;
45605
46838
  }
45606
46839
  function convertToolResult(message) {
45607
46840
  const content = typeof message.content === "string" || !message.content.some((b) => b.type === "image") ? sanitizeText(contentText2(message.content)) : convertBlocks(message.content);
@@ -46464,8 +47697,8 @@ function convertToolResults(model, results, compat) {
46464
47697
  const images = [];
46465
47698
  for (const result of results) {
46466
47699
  const text = contentText2(result.content);
46467
- const blocks = typeof result.content === "string" ? [] : result.content;
46468
- const hasImages2 = blocks.some((b) => b.type === "image");
47700
+ const blocks2 = typeof result.content === "string" ? [] : result.content;
47701
+ const hasImages2 = blocks2.some((b) => b.type === "image");
46469
47702
  const message = {
46470
47703
  role: "tool",
46471
47704
  tool_call_id: normalizeCallId(model, result.toolCallId),
@@ -46476,7 +47709,7 @@ function convertToolResults(model, results, compat) {
46476
47709
  if (compat.requiresToolResultName) message["name"] = result.toolName;
46477
47710
  messages.push(message);
46478
47711
  if (hasImages2 && model.input.includes("image")) {
46479
- for (const block of blocks) if (block.type === "image") images.push(imagePart(block));
47712
+ for (const block of blocks2) if (block.type === "image") images.push(imagePart(block));
46480
47713
  }
46481
47714
  }
46482
47715
  return { messages, images };
@@ -46726,9 +47959,9 @@ function imagePart2(block) {
46726
47959
  };
46727
47960
  }
46728
47961
  function convertUser4(message) {
46729
- const blocks = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
47962
+ const blocks2 = typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content;
46730
47963
  const content = [];
46731
- for (const block of blocks) {
47964
+ for (const block of blocks2) {
46732
47965
  if (block.type === "image") content.push(imagePart2(block));
46733
47966
  else if (block.text.length > 0) {
46734
47967
  content.push({ type: "input_text", text: sanitizeText(block.text) });
@@ -47982,12 +49215,12 @@ function handleChunk2(chunk, state, tracker) {
47982
49215
  }
47983
49216
  const usage = obj6(chunk["usageMetadata"]);
47984
49217
  if (usage) output.usage = parseGoogleUsage(usage);
47985
- const blocked2 = str7(obj6(chunk["promptFeedback"])?.["blockReason"]);
49218
+ const blocked3 = str7(obj6(chunk["promptFeedback"])?.["blockReason"]);
47986
49219
  const candidates2 = Array.isArray(chunk["candidates"]) ? chunk["candidates"] : [];
47987
49220
  const candidate = obj6(candidates2[0]);
47988
- if (!candidate && blocked2) {
47989
- output.rawStopReason = blocked2;
47990
- throw new ProviderStopError(`Prompt blocked: ${blocked2}`);
49221
+ if (!candidate && blocked3) {
49222
+ output.rawStopReason = blocked3;
49223
+ throw new ProviderStopError(`Prompt blocked: ${blocked3}`);
47991
49224
  }
47992
49225
  const parts = obj6(candidate?.["content"])?.["parts"];
47993
49226
  if (Array.isArray(parts)) {
@@ -48198,7 +49431,7 @@ function defaultConfigDir(env = process.env) {
48198
49431
  function readAuthFile2(path, onWarning) {
48199
49432
  let raw;
48200
49433
  try {
48201
- raw = (0, import_node_fs53.readFileSync)(path, "utf8");
49434
+ raw = (0, import_node_fs54.readFileSync)(path, "utf8");
48202
49435
  } catch (error2) {
48203
49436
  const code = error2.code;
48204
49437
  if (code !== "ENOENT") onWarning?.(`cannot read auth file ${path}`);
@@ -48206,7 +49439,7 @@ function readAuthFile2(path, onWarning) {
48206
49439
  }
48207
49440
  if (process.platform !== "win32") {
48208
49441
  try {
48209
- const mode = (0, import_node_fs53.statSync)(path).mode & 511;
49442
+ const mode = (0, import_node_fs54.statSync)(path).mode & 511;
48210
49443
  if ((mode & 63) !== 0) {
48211
49444
  onWarning?.(
48212
49445
  `auth file ${path} has permissions ${mode.toString(8).padStart(4, "0")}; expected 0600`
@@ -48240,12 +49473,12 @@ function expandEnvRefs(value, env) {
48240
49473
  );
48241
49474
  return missing || out.length === 0 ? void 0 : out;
48242
49475
  }
48243
- var import_node_child_process13, import_node_fs53, import_node_os12, import_node_path75, ApiKeyResolver;
49476
+ var import_node_child_process13, import_node_fs54, import_node_os12, import_node_path75, ApiKeyResolver;
48244
49477
  var init_auth2 = __esm({
48245
49478
  "src/ai/providers/auth.ts"() {
48246
49479
  "use strict";
48247
49480
  import_node_child_process13 = require("node:child_process");
48248
- import_node_fs53 = require("node:fs");
49481
+ import_node_fs54 = require("node:fs");
48249
49482
  import_node_os12 = require("node:os");
48250
49483
  import_node_path75 = require("node:path");
48251
49484
  init_live();
@@ -49033,25 +50266,25 @@ var init_settings_registry = __esm({
49033
50266
 
49034
50267
  // src/config/write.ts
49035
50268
  function writeConfigFile(path, config, options = {}) {
49036
- (0, import_node_fs54.mkdirSync)((0, import_node_path77.dirname)(path), { recursive: true });
50269
+ (0, import_node_fs55.mkdirSync)((0, import_node_path77.dirname)(path), { recursive: true });
49037
50270
  let mode;
49038
50271
  try {
49039
- mode = (0, import_node_fs54.statSync)(path).mode & 511;
50272
+ mode = (0, import_node_fs55.statSync)(path).mode & 511;
49040
50273
  } catch {
49041
50274
  mode = void 0;
49042
50275
  }
49043
- if (mode !== void 0 && options.backup !== false) (0, import_node_fs54.copyFileSync)(path, `${path}.bak`);
50276
+ if (mode !== void 0 && options.backup !== false) (0, import_node_fs55.copyFileSync)(path, `${path}.bak`);
49044
50277
  const tmp = `${path}.${process.pid}.tmp`;
49045
- (0, import_node_fs54.writeFileSync)(tmp, `${JSON.stringify(config, null, 2)}
50278
+ (0, import_node_fs55.writeFileSync)(tmp, `${JSON.stringify(config, null, 2)}
49046
50279
  `, { mode: mode ?? 420 });
49047
- if (mode !== void 0) (0, import_node_fs54.chmodSync)(tmp, mode);
49048
- (0, import_node_fs54.renameSync)(tmp, path);
50280
+ if (mode !== void 0) (0, import_node_fs55.chmodSync)(tmp, mode);
50281
+ (0, import_node_fs55.renameSync)(tmp, path);
49049
50282
  }
49050
- var import_node_fs54, import_node_path77;
50283
+ var import_node_fs55, import_node_path77;
49051
50284
  var init_write2 = __esm({
49052
50285
  "src/config/write.ts"() {
49053
50286
  "use strict";
49054
- import_node_fs54 = require("node:fs");
50287
+ import_node_fs55 = require("node:fs");
49055
50288
  import_node_path77 = require("node:path");
49056
50289
  }
49057
50290
  });
@@ -49250,7 +50483,7 @@ function parseEnum(key, text, options) {
49250
50483
  function readTarget(path, scope) {
49251
50484
  let text;
49252
50485
  try {
49253
- text = (0, import_node_fs55.readFileSync)(path, "utf8");
50486
+ text = (0, import_node_fs56.readFileSync)(path, "utf8");
49254
50487
  } catch (error2) {
49255
50488
  if (error2.code === "ENOENT")
49256
50489
  return scope === "user" ? minimalConfig() : { version: CONFIG_FILE_VERSION };
@@ -49332,11 +50565,11 @@ function formatSettingValue(value) {
49332
50565
  if (typeof value === "string") return value;
49333
50566
  return JSON.stringify(value);
49334
50567
  }
49335
- var import_node_fs55, ConfigEditError, same, ABOVE, TRUE, FALSE, UNSET;
50568
+ var import_node_fs56, ConfigEditError, same, ABOVE, TRUE, FALSE, UNSET;
49336
50569
  var init_edit2 = __esm({
49337
50570
  "src/config/edit.ts"() {
49338
50571
  "use strict";
49339
- import_node_fs55 = require("node:fs");
50572
+ import_node_fs56 = require("node:fs");
49340
50573
  init_errors();
49341
50574
  init_i18n();
49342
50575
  init_init();
@@ -49416,7 +50649,7 @@ async function readClipboardImage(dataDir, deps = {}) {
49416
50649
  await (0, import_promises22.mkdir)(dir, { recursive: true });
49417
50650
  const stamp = new Date((deps.now ?? Date.now)()).toISOString().replace(/[:.]/g, "-");
49418
50651
  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`);
50652
+ for (let n = 1; (0, import_node_fs62.existsSync)(file); n++) file = (0, import_node_path80.join)(dir, `${stamp}-${n}.png`);
49420
50653
  let anyTool = false;
49421
50654
  for (const attempt of attempts(platform, file, env)) {
49422
50655
  const options = attempt.via === "file" && attempt.env !== void 0 ? { env: attempt.env } : {};
@@ -49456,11 +50689,11 @@ async function gcClipboardImages(dataDir, options = {}) {
49456
50689
  }
49457
50690
  return removed;
49458
50691
  }
49459
- var import_node_fs61, import_promises22, import_node_path80, CLIPBOARD_DIR, CLIPBOARD_MAX_AGE_DAYS, OSASCRIPT, POWERSHELL;
50692
+ var import_node_fs62, import_promises22, import_node_path80, CLIPBOARD_DIR, CLIPBOARD_MAX_AGE_DAYS, OSASCRIPT, POWERSHELL;
49460
50693
  var init_clipboard_image = __esm({
49461
50694
  "src/tools/clipboard-image.ts"() {
49462
50695
  "use strict";
49463
- import_node_fs61 = require("node:fs");
50696
+ import_node_fs62 = require("node:fs");
49464
50697
  import_promises22 = require("node:fs/promises");
49465
50698
  import_node_path80 = require("node:path");
49466
50699
  init_image_file();
@@ -51175,6 +52408,8 @@ function toolSummary(name, args, cwd) {
51175
52408
  }
51176
52409
  case "task":
51177
52410
  return flat(str8("description") ?? str8("prompt") ?? "");
52411
+ case "web_fetch":
52412
+ return flat((str8("url") ?? "").replace(/^https?:\/\//i, ""));
51178
52413
  default:
51179
52414
  if (path !== void 0) return displayPath2(path, cwd);
51180
52415
  for (const key of [
@@ -51302,10 +52537,28 @@ function resultSummary(input2, theme) {
51302
52537
  ] : [];
51303
52538
  return muted([m.done, duration, ...tokens].join(" · "));
51304
52539
  }
52540
+ case "web_fetch": {
52541
+ const finalUrl = details["finalUrl"];
52542
+ if (num9(details["bytes"]) === 0 && typeof finalUrl === "string" && lines2[0]?.startsWith("Redirected"))
52543
+ return muted(m.fetchRedirect(finalUrl));
52544
+ const title = typeof details["title"] === "string" ? flat(details["title"], 60) : void 0;
52545
+ const format = typeof details["format"] === "string" ? details["format"] : "";
52546
+ return muted(m.fetched(title, formatSize(num9(details["bytes"]) ?? 0), format));
52547
+ }
51305
52548
  default:
51306
52549
  return muted(count2 === 0 ? m.done : m.outputLines(count2));
51307
52550
  }
51308
52551
  }
52552
+ function fetchHost(toolName, input2) {
52553
+ if (toolName !== "web_fetch") return void 0;
52554
+ const raw = record(input2)["url"];
52555
+ if (typeof raw !== "string") return void 0;
52556
+ try {
52557
+ return new URL(raw.trim()).hostname.replace(/^\[|\]$/g, "").toLowerCase() || void 0;
52558
+ } catch {
52559
+ return void 0;
52560
+ }
52561
+ }
51309
52562
  var import_node_path85;
51310
52563
  var init_tool_summary = __esm({
51311
52564
  "src/modes/interactive/tool-summary.ts"() {
@@ -51314,6 +52567,7 @@ var init_tool_summary = __esm({
51314
52567
  import_node_path85 = require("node:path");
51315
52568
  init_tui2();
51316
52569
  init_status_bar();
52570
+ init_truncate();
51317
52571
  }
51318
52572
  });
51319
52573
 
@@ -52128,7 +53382,7 @@ function childLoader(read2 = readTraceInput) {
52128
53382
  const load2 = ((file) => {
52129
53383
  let stamp = "";
52130
53384
  try {
52131
- const st = (0, import_node_fs64.statSync)(file);
53385
+ const st = (0, import_node_fs65.statSync)(file);
52132
53386
  stamp = `${st.mtimeMs}:${st.size}`;
52133
53387
  } catch {
52134
53388
  stamp = "missing";
@@ -52172,11 +53426,11 @@ function traceText(session, taskId) {
52172
53426
  `${msg().trace.taskTitle(node.taskId, node.agent)}${summary2}`
52173
53427
  );
52174
53428
  }
52175
- var import_node_fs64, LiveTracker, lineTraceCommand;
53429
+ var import_node_fs65, LiveTracker, lineTraceCommand;
52176
53430
  var init_session3 = __esm({
52177
53431
  "src/trace/session.ts"() {
52178
53432
  "use strict";
52179
- import_node_fs64 = require("node:fs");
53433
+ import_node_fs65 = require("node:fs");
52180
53434
  init_session2();
52181
53435
  init_i18n();
52182
53436
  init_scan();
@@ -52359,7 +53613,7 @@ function ancestorDirs(cwd) {
52359
53613
  let dir = (0, import_node_path89.resolve)(cwd);
52360
53614
  for (; ; ) {
52361
53615
  out.push(dir);
52362
- if ((0, import_node_fs66.existsSync)((0, import_node_path89.join)(dir, ".git"))) return out;
53616
+ if ((0, import_node_fs67.existsSync)((0, import_node_path89.join)(dir, ".git"))) return out;
52363
53617
  const parent = (0, import_node_path89.dirname)(dir);
52364
53618
  if (parent === dir) return out;
52365
53619
  dir = parent;
@@ -52421,7 +53675,7 @@ async function isDir3(path) {
52421
53675
  async function findSkillFiles(dir, depth = 0) {
52422
53676
  if (depth > MAX_DEPTH3) return [];
52423
53677
  const own = (0, import_node_path89.join)(dir, SKILL_FILE);
52424
- if ((0, import_node_fs66.existsSync)(own)) return [own];
53678
+ if ((0, import_node_fs67.existsSync)(own)) return [own];
52425
53679
  let names;
52426
53680
  try {
52427
53681
  names = await (0, import_promises24.readdir)(dir);
@@ -52473,11 +53727,11 @@ async function discoverSkills(sources, options) {
52473
53727
  }
52474
53728
  return result;
52475
53729
  }
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;
53730
+ 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
53731
  var init_discover2 = __esm({
52478
53732
  "src/skills/discover.ts"() {
52479
53733
  "use strict";
52480
- import_node_fs66 = require("node:fs");
53734
+ import_node_fs67 = require("node:fs");
52481
53735
  import_promises24 = require("node:fs/promises");
52482
53736
  import_node_path89 = require("node:path");
52483
53737
  init_frontmatter2();
@@ -52601,7 +53855,7 @@ function resolveSandboxEntry(moduleUrl = __ama_import_meta_url) {
52601
53855
  candidates2.push((0, import_node_path90.join)(here, "sandbox-entry.js"));
52602
53856
  candidates2.push((0, import_node_path90.join)(here, "sandbox-entry.ts"));
52603
53857
  for (const candidate of candidates2) {
52604
- if ((0, import_node_fs67.existsSync)(candidate)) return (0, import_node_fs67.realpathSync)(candidate);
53858
+ if ((0, import_node_fs68.existsSync)(candidate)) return (0, import_node_fs68.realpathSync)(candidate);
52605
53859
  }
52606
53860
  return void 0;
52607
53861
  }
@@ -52833,12 +54087,12 @@ async function runSandbox(request) {
52833
54087
  child.stdin.end();
52834
54088
  return result;
52835
54089
  }
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;
54090
+ 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
54091
  var init_host_side = __esm({
52838
54092
  "src/codemode/host-side.ts"() {
52839
54093
  "use strict";
52840
54094
  import_node_child_process16 = require("node:child_process");
52841
- import_node_fs67 = require("node:fs");
54095
+ import_node_fs68 = require("node:fs");
52842
54096
  import_node_path90 = require("node:path");
52843
54097
  import_node_url2 = require("node:url");
52844
54098
  init_detect();
@@ -53004,9 +54258,9 @@ ${output.slice(-half)}`;
53004
54258
  function saveFullOutput(ctx, text) {
53005
54259
  try {
53006
54260
  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 });
54261
+ (0, import_node_fs69.mkdirSync)(dir, { recursive: true });
53008
54262
  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");
54263
+ (0, import_node_fs69.writeFileSync)(file, text, "utf8");
53010
54264
  return file;
53011
54265
  } catch {
53012
54266
  return void 0;
@@ -53158,11 +54412,11 @@ function codemodeToolFactory(overrides = {}) {
53158
54412
  return createCodemodeTool(options);
53159
54413
  };
53160
54414
  }
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;
54415
+ 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
54416
  var init_tool = __esm({
53163
54417
  "src/codemode/tool.ts"() {
53164
54418
  "use strict";
53165
- import_node_fs68 = require("node:fs");
54419
+ import_node_fs69 = require("node:fs");
53166
54420
  import_node_os14 = require("node:os");
53167
54421
  import_node_path91 = require("node:path");
53168
54422
  init_presets2();
@@ -53188,7 +54442,7 @@ var init_tool = __esm({
53188
54442
  });
53189
54443
 
53190
54444
  // src/memory/tool.ts
53191
- function fail5(input2, code, message) {
54445
+ function fail6(input2, code, message) {
53192
54446
  const details = { command: String(input2.command ?? ""), code };
53193
54447
  if (typeof input2.path === "string") details.path = input2.path;
53194
54448
  return { content: message, isError: true, details };
@@ -53220,11 +54474,11 @@ function createMemoryTool(runtime) {
53220
54474
  const write2 = command3 !== "view";
53221
54475
  if (ctx.depth > 0) {
53222
54476
  if (runtime.subagents === "off")
53223
- return fail5(input2, "subagent", "subagents cannot access memory");
53224
- if (write2) return fail5(input2, "subagent", "subagents cannot modify memory");
54477
+ return fail6(input2, "subagent", "subagents cannot access memory");
54478
+ if (write2) return fail6(input2, "subagent", "subagents cannot modify memory");
53225
54479
  }
53226
54480
  if (write2 && !runtime.writesEnabled)
53227
- return fail5(input2, "disabled", "memory writes are turned off for this session");
54481
+ return fail6(input2, "disabled", "memory writes are turned off for this session");
53228
54482
  const store = runtime.store;
53229
54483
  try {
53230
54484
  switch (command3) {
@@ -53244,7 +54498,7 @@ function createMemoryTool(runtime) {
53244
54498
  };
53245
54499
  }
53246
54500
  default:
53247
- return fail5(
54501
+ return fail6(
53248
54502
  input2,
53249
54503
  "invalid_command",
53250
54504
  'command must be "view", "create", "str_replace" or "delete"'
@@ -53252,7 +54506,7 @@ function createMemoryTool(runtime) {
53252
54506
  }
53253
54507
  } catch (error2) {
53254
54508
  if (error2 instanceof MemoryError || error2 instanceof MemoryLockError)
53255
- return fail5(input2, error2.code, error2.message);
54509
+ return fail6(input2, error2.code, error2.message);
53256
54510
  throw error2;
53257
54511
  }
53258
54512
  }
@@ -53276,7 +54530,7 @@ var init_tool2 = __esm({
53276
54530
  // src/cli/codemode-notice.ts
53277
54531
  function readNotices(path) {
53278
54532
  try {
53279
- const value = JSON.parse((0, import_node_fs69.readFileSync)(path, "utf8"));
54533
+ const value = JSON.parse((0, import_node_fs70.readFileSync)(path, "utf8"));
53280
54534
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
53281
54535
  const shown2 = value[KEY];
53282
54536
  return {
@@ -53300,20 +54554,20 @@ function takeCodemodeNotice(input2) {
53300
54554
  const shown2 = notices[KEY] ?? [];
53301
54555
  if (shown2.includes(input2.configDir)) return void 0;
53302
54556
  try {
53303
- (0, import_node_fs69.mkdirSync)(input2.dataDir, { recursive: true });
54557
+ (0, import_node_fs70.mkdirSync)(input2.dataDir, { recursive: true });
53304
54558
  const tmp = `${path}.${process.pid}.tmp`;
53305
- (0, import_node_fs69.writeFileSync)(tmp, `${JSON.stringify({ ...notices, [KEY]: [...shown2, input2.configDir] })}
54559
+ (0, import_node_fs70.writeFileSync)(tmp, `${JSON.stringify({ ...notices, [KEY]: [...shown2, input2.configDir] })}
53306
54560
  `);
53307
- (0, import_node_fs69.renameSync)(tmp, path);
54561
+ (0, import_node_fs70.renameSync)(tmp, path);
53308
54562
  } catch {
53309
54563
  }
53310
54564
  return codemodeNoticeText(input2.capability);
53311
54565
  }
53312
- var import_node_fs69, import_node_path92, NOTICES_FILE, KEY;
54566
+ var import_node_fs70, import_node_path92, NOTICES_FILE, KEY;
53313
54567
  var init_codemode_notice = __esm({
53314
54568
  "src/cli/codemode-notice.ts"() {
53315
54569
  "use strict";
53316
- import_node_fs69 = require("node:fs");
54570
+ import_node_fs70 = require("node:fs");
53317
54571
  import_node_path92 = require("node:path");
53318
54572
  init_presets2();
53319
54573
  init_i18n();
@@ -53330,7 +54584,7 @@ function readEntries(dataDir, now) {
53330
54584
  const out = /* @__PURE__ */ new Map();
53331
54585
  let parsed;
53332
54586
  try {
53333
- parsed = JSON.parse((0, import_node_fs70.readFileSync)(maxTokensCachePath(dataDir), "utf8"));
54587
+ parsed = JSON.parse((0, import_node_fs71.readFileSync)(maxTokensCachePath(dataDir), "utf8"));
53334
54588
  } catch {
53335
54589
  return out;
53336
54590
  }
@@ -53364,11 +54618,11 @@ function recordMaxTokensCap(dataDir, key, cap, now = Date.now) {
53364
54618
  const path = maxTokensCachePath(dataDir);
53365
54619
  const tmp = `${path}.${process.pid}.tmp`;
53366
54620
  try {
53367
- (0, import_node_fs70.mkdirSync)((0, import_node_path93.dirname)(path), { recursive: true, mode: 448 });
54621
+ (0, import_node_fs71.mkdirSync)((0, import_node_path93.dirname)(path), { recursive: true, mode: 448 });
53368
54622
  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)}
54623
+ (0, import_node_fs71.writeFileSync)(tmp, `${JSON.stringify({ version: MAX_TOKENS_CACHE_VERSION, caps }, null, 2)}
53370
54624
  `);
53371
- (0, import_node_fs70.renameSync)(tmp, path);
54625
+ (0, import_node_fs71.renameSync)(tmp, path);
53372
54626
  } catch {
53373
54627
  }
53374
54628
  }
@@ -53384,11 +54638,11 @@ function attachMaxTokensCache(dataDir, now = Date.now) {
53384
54638
  attached2.set(dataDir, detach);
53385
54639
  return detach;
53386
54640
  }
53387
- var import_node_fs70, import_node_path93, MAX_TOKENS_CACHE_VERSION, MAX_TOKENS_CACHE_TTL_MS, attached2;
54641
+ var import_node_fs71, import_node_path93, MAX_TOKENS_CACHE_VERSION, MAX_TOKENS_CACHE_TTL_MS, attached2;
53388
54642
  var init_max_tokens_cache = __esm({
53389
54643
  "src/ai/providers/max-tokens-cache.ts"() {
53390
54644
  "use strict";
53391
- import_node_fs70 = require("node:fs");
54645
+ import_node_fs71 = require("node:fs");
53392
54646
  import_node_path93 = require("node:path");
53393
54647
  init_max_tokens();
53394
54648
  MAX_TOKENS_CACHE_VERSION = 1;
@@ -53542,7 +54796,7 @@ function findImageRefs(text, cwd) {
53542
54796
  token = token.replace(/\\ /g, " ").replace(/[,\uFF0C\u3002;\uFF1B)\uFF09]+$/, "");
53543
54797
  if (token === "" || imageMimeFromPath(token) === void 0) continue;
53544
54798
  const path = expand(token, cwd);
53545
- if (!explicit && !(0, import_node_fs71.existsSync)(path)) continue;
54799
+ if (!explicit && !(0, import_node_fs72.existsSync)(path)) continue;
53546
54800
  if (seen.has(path)) continue;
53547
54801
  seen.add(path);
53548
54802
  out.push({ path, explicit });
@@ -53571,9 +54825,9 @@ async function loadPromptImages(refs, model, options = {}) {
53571
54825
  ...model !== void 0 ? { maxBase64Bytes: imageLimits(model).perImageBase64 } : {},
53572
54826
  ...options
53573
54827
  };
53574
- const blocks = [];
53575
- for (const ref of wanted) blocks.push((await loadLocalized(ref.path, fit)).block);
53576
- return blocks;
54828
+ const blocks2 = [];
54829
+ for (const ref of wanted) blocks2.push((await loadLocalized(ref.path, fit)).block);
54830
+ return blocks2;
53577
54831
  }
53578
54832
  async function promptImages(text, extra, cwd, model, options = {}) {
53579
54833
  const refs = [
@@ -53601,11 +54855,11 @@ async function loadLocalized(path, options) {
53601
54855
  throw error2;
53602
54856
  }
53603
54857
  }
53604
- var import_node_fs71, import_node_os15, import_node_path94, TOKEN;
54858
+ var import_node_fs72, import_node_os15, import_node_path94, TOKEN;
53605
54859
  var init_image_input = __esm({
53606
54860
  "src/modes/image-input.ts"() {
53607
54861
  "use strict";
53608
- import_node_fs71 = require("node:fs");
54862
+ import_node_fs72 = require("node:fs");
53609
54863
  import_node_os15 = require("node:os");
53610
54864
  import_node_path94 = require("node:path");
53611
54865
  init_channels();
@@ -54404,7 +55658,7 @@ function trustChoices() {
54404
55658
  }
54405
55659
  function isDirectory2(path) {
54406
55660
  try {
54407
- return (0, import_node_fs72.statSync)(path).isDirectory();
55661
+ return (0, import_node_fs73.statSync)(path).isDirectory();
54408
55662
  } catch {
54409
55663
  return false;
54410
55664
  }
@@ -54459,7 +55713,7 @@ function createStartupUi(options = {}) {
54459
55713
  validate: (text) => {
54460
55714
  if (text === "") return m.cwdEmpty;
54461
55715
  const abs = (0, import_node_path95.resolve)(text);
54462
- if (!(0, import_node_fs72.existsSync)(abs)) return m.cwdMissing(abs);
55716
+ if (!(0, import_node_fs73.existsSync)(abs)) return m.cwdMissing(abs);
54463
55717
  if (!isDirectory2(abs)) return m.cwdNotDir(abs);
54464
55718
  return void 0;
54465
55719
  }
@@ -54467,12 +55721,12 @@ function createStartupUi(options = {}) {
54467
55721
  }
54468
55722
  };
54469
55723
  }
54470
- var import_node_fs72, import_node_path95;
55724
+ var import_node_fs73, import_node_path95;
54471
55725
  var init_startup_ui = __esm({
54472
55726
  "src/modes/interactive/startup-ui.ts"() {
54473
55727
  "use strict";
54474
55728
  init_i18n();
54475
- import_node_fs72 = require("node:fs");
55729
+ import_node_fs73 = require("node:fs");
54476
55730
  import_node_path95 = require("node:path");
54477
55731
  init_tui2();
54478
55732
  init_model_items();
@@ -54913,8 +56167,8 @@ var init_startup_screen = __esm({
54913
56167
  });
54914
56168
 
54915
56169
  // src/modes/interactive/panels.ts
54916
- function keyValue(rows, theme, wrap2 = false) {
54917
- return new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap2 ? { wrap: true } : {} });
56170
+ function keyValue(rows, theme, wrap3 = false) {
56171
+ return new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap3 ? { wrap: true } : {} });
54918
56172
  }
54919
56173
  function cacheSection(session, theme, now) {
54920
56174
  return [
@@ -55212,7 +56466,7 @@ function contextPanel(session, theme) {
55212
56466
  const m = msg().report.contextReport;
55213
56467
  const snap = contextSnapshot(session);
55214
56468
  const { breakdown } = snap;
55215
- const table3 = (rows, wrap2 = false) => new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap2 ? { wrap: true } : {} });
56469
+ const table3 = (rows, wrap3 = false) => new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...wrap3 ? { wrap: true } : {} });
55216
56470
  const largest = largestRows(breakdown);
55217
56471
  const parts = [
55218
56472
  table3(headerRows(snap)),
@@ -55271,14 +56525,20 @@ function command(name, key, args) {
55271
56525
  });
55272
56526
  return info2;
55273
56527
  }
56528
+ function availableCommands2(runtime, session, list5 = BUILTIN_COMMANDS) {
56529
+ const active = new Set(session.getTools().map((t) => t.name));
56530
+ const registered = new Set(runtime.tools.list());
56531
+ const has = (requires) => requires === "memory" ? registered.has("memory") : requires === "task" ? active.has("task") || active.has("codemode") && registered.has("task") : true;
56532
+ return list5.filter((c) => has(c.requires));
56533
+ }
55274
56534
  function parseSlash(line2) {
55275
56535
  const m = /^\/([A-Za-z?][\w-]*)(?:\s+([\s\S]*))?$/.exec(line2.trim());
55276
56536
  if (m === null) return void 0;
55277
56537
  const raw = m[1].toLowerCase();
55278
56538
  return { name: ALIASES[raw] ?? raw, args: (m[2] ?? "").trim() };
55279
56539
  }
55280
- function helpText2() {
55281
- const lines2 = BUILTIN_COMMANDS.map(
56540
+ function helpText2(ctx) {
56541
+ const lines2 = availableCommands2(ctx.runtime, ctx.session()).map(
55282
56542
  (c) => `/${c.name}${c.args !== void 0 ? ` ${c.args}` : ""} ${c.description}`
55283
56543
  );
55284
56544
  const m = msg().report.commands;
@@ -55325,7 +56585,7 @@ async function runSlashCommand(line2, ctx) {
55325
56585
  const session = ctx.session();
55326
56586
  switch (name) {
55327
56587
  case "help":
55328
- return { kind: "handled", message: helpText2() };
56588
+ return { kind: "handled", message: helpText2(ctx) };
55329
56589
  case "exit":
55330
56590
  return { kind: "exit" };
55331
56591
  case "new": {
@@ -55497,7 +56757,9 @@ var init_commands_core = __esm({
55497
56757
  command("cache", "cache", "[warm off|streaming|idle | fingerprint]"),
55498
56758
  command("statusline", "statusline", "[full|compact]"),
55499
56759
  command("plan", "plan", { key: "planArgs" }),
55500
- command("tasks", "tasks", "[id] | stop <id> | bg [id]"),
56760
+ Object.assign(command("tasks", "tasks", "[id] | stop <id> | bg [id]"), {
56761
+ requires: "task"
56762
+ }),
55501
56763
  command("agents", "agents"),
55502
56764
  command("paste", "paste"),
55503
56765
  command("interrupt", "interrupt", { key: "interruptArgs" }),
@@ -55505,7 +56767,9 @@ var init_commands_core = __esm({
55505
56767
  // [W6-C0] 第六波(W6-S / W6-T1 / W6-M 实现)
55506
56768
  command("config", "config", "[key=value]"),
55507
56769
  command("trace", "trace", { key: "traceArgs" }),
55508
- command("memory", "memory", { key: "memoryArgs" })
56770
+ Object.assign(command("memory", "memory", { key: "memoryArgs" }), {
56771
+ requires: "memory"
56772
+ })
55509
56773
  ];
55510
56774
  ALIASES = { quit: "exit", q: "exit", "?": "help" };
55511
56775
  }
@@ -55605,9 +56869,9 @@ async function availableModels(runtime) {
55605
56869
  }
55606
56870
  return out;
55607
56871
  }
55608
- function commands(runtime) {
56872
+ function commands(runtime, session) {
55609
56873
  return [
55610
- ...BUILTIN_COMMANDS.map((c) => ({
56874
+ ...availableCommands2(runtime, session).map((c) => ({
55611
56875
  name: c.name,
55612
56876
  description: c.description,
55613
56877
  source: "builtin"
@@ -55817,7 +57081,7 @@ var init_commands = __esm({
55817
57081
  ctx.session().setPermissionMode(p.mode);
55818
57082
  return { mode: p.mode };
55819
57083
  },
55820
- get_commands: async (_p, ctx) => ({ commands: commands(ctx.runtime) }),
57084
+ get_commands: async (_p, ctx) => ({ commands: commands(ctx.runtime, ctx.session()) }),
55821
57085
  get_skills: async (_p, ctx) => ({ skills: ctx.runtime.resources.skills }),
55822
57086
  // [W5-F] 计划 / 任务命令(docs/history/wave5-plan.md §6.5、§7.5)
55823
57087
  plan_response: async (p, ctx) => {
@@ -56312,8 +57576,8 @@ var init_config_panel = __esm({
56312
57576
  const dynamic = value.value === void 0 ? settingDynamicDefault(row3.id) : void 0;
56313
57577
  const text = dynamic === void 0 ? doc : `${doc} · ${dynamic}`;
56314
57578
  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))];
57579
+ const blocked3 = spec2 === void 0 ? void 0 : blockedReason(spec2, value, this.scope);
57580
+ const lines2 = blocked3 === void 0 ? [] : [theme.fg("warning", truncateToWidth(blocked3, width2))];
56317
57581
  if (text !== "") lines2.push(...wrapTextWithAnsi(theme.fg("dim", text), width2));
56318
57582
  return lines2.slice(0, 3);
56319
57583
  }
@@ -57356,8 +58620,8 @@ var init_config_ui = __esm({
57356
58620
  const spec2 = settingSpec(key);
57357
58621
  if (panel === void 0 || spec2 === void 0) return;
57358
58622
  const current3 = getConfigValue(readSnapshot(this.input()), key);
57359
- const blocked2 = blockedReason(spec2, current3, scope);
57360
- if (blocked2 !== void 0) return panel.setNotice(blocked2, "warning");
58623
+ const blocked3 = blockedReason(spec2, current3, scope);
58624
+ if (blocked3 !== void 0) return panel.setNotice(blocked3, "warning");
57361
58625
  const value = current3.value;
57362
58626
  switch (spec2.kind) {
57363
58627
  case "bool":
@@ -57647,27 +58911,27 @@ function runEditor(command3, file) {
57647
58911
  return result.status;
57648
58912
  }
57649
58913
  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-"));
58914
+ const dir = (0, import_node_fs74.mkdtempSync)((0, import_node_path97.join)((0, import_node_os16.tmpdir)(), "ama-edit-"));
57651
58915
  const file = (0, import_node_path97.join)(dir, name);
57652
- (0, import_node_fs73.writeFileSync)(file, text);
58916
+ (0, import_node_fs74.writeFileSync)(file, text);
57653
58917
  deps.suspend();
57654
58918
  try {
57655
58919
  const code = (deps.run ?? runEditor)(editorCommand(deps.env, deps.platform), file);
57656
58920
  if (code !== 0) return void 0;
57657
- return (0, import_node_fs73.readFileSync)(file, "utf8");
58921
+ return (0, import_node_fs74.readFileSync)(file, "utf8");
57658
58922
  } catch {
57659
58923
  return void 0;
57660
58924
  } finally {
57661
58925
  deps.resume();
57662
- (0, import_node_fs73.rmSync)(dir, { recursive: true, force: true });
58926
+ (0, import_node_fs74.rmSync)(dir, { recursive: true, force: true });
57663
58927
  }
57664
58928
  }
57665
- var import_node_child_process17, import_node_fs73, import_node_os16, import_node_path97;
58929
+ var import_node_child_process17, import_node_fs74, import_node_os16, import_node_path97;
57666
58930
  var init_external_editor = __esm({
57667
58931
  "src/modes/interactive/external-editor.ts"() {
57668
58932
  "use strict";
57669
58933
  import_node_child_process17 = require("node:child_process");
57670
- import_node_fs73 = require("node:fs");
58934
+ import_node_fs74 = require("node:fs");
57671
58935
  import_node_os16 = require("node:os");
57672
58936
  import_node_path97 = require("node:path");
57673
58937
  }
@@ -58631,6 +59895,10 @@ function describeRequest(request, theme, options = {}) {
58631
59895
  const { shown: shown2, hidden } = lines(input2["command"], COMMAND_LINES);
58632
59896
  out.push(...shown2.map((l, i) => theme.fg("code", (i === 0 ? "$ " : " ") + l)));
58633
59897
  if (hidden > 0) out.push(more(hidden));
59898
+ } else if (request.toolName === "web_fetch" && typeof input2["url"] === "string") {
59899
+ const { shown: shown2, hidden } = lines(input2["url"].replace(/(.{72})/g, "$1\n"), COMMAND_LINES);
59900
+ out.push(...shown2.map((l) => theme.fg("code", l)));
59901
+ if (hidden > 0) out.push(more(hidden));
58634
59902
  } else if (request.toolName === "write" && typeof input2["content"] === "string") {
58635
59903
  const count2 = lines(input2["content"], Number.MAX_SAFE_INTEGER).shown.length;
58636
59904
  out.push(
@@ -58699,8 +59967,10 @@ function approvalOutcomeText(request, outcome, taskAgent) {
58699
59967
  switch (outcome) {
58700
59968
  case "allow":
58701
59969
  return m.allow(what);
58702
- case "allow_session":
58703
- return m.allowSession(what);
59970
+ case "allow_session": {
59971
+ const host = fetchHost(request.toolName, request.input);
59972
+ return host === void 0 ? m.allowSession(what) : m.allowSessionHost(what, host);
59973
+ }
58704
59974
  case "deny":
58705
59975
  return m.deny(what);
58706
59976
  case "cancelled":
@@ -58790,10 +60060,12 @@ var init_approval_dialog = __esm({
58790
60060
  const { theme } = this.host;
58791
60061
  const g = theme.glyphs;
58792
60062
  const keyWidth = compact ? 1 : Math.max(...APPROVAL_OPTIONS.map((o) => o.keys.length));
60063
+ const host = fetchHost(this.request.toolName, this.request.input);
58793
60064
  return APPROVAL_OPTIONS.map((option2, i) => {
58794
60065
  const selected = i === this.selected;
58795
60066
  const keys = compact ? option2.keys.slice(0, 1) : option2.keys;
58796
- const left = `${selected ? g.prompt : " "} ${i + 1}. ${option2.label}`;
60067
+ const text = option2.decision === "allow_session" && host !== void 0 ? msg().approval.option.allowSessionHost(host) : option2.label;
60068
+ const left = `${selected ? g.prompt : " "} ${i + 1}. ${text}`;
58797
60069
  const right = keys.padEnd(keyWidth);
58798
60070
  const room = Math.max(1, width2 - visibleWidth(right) - 1);
58799
60071
  const label2 = selected ? theme.fg("accent", theme.bold(left)) : left;
@@ -61212,6 +62484,9 @@ function command2(name, key) {
61212
62484
  }
61213
62485
  };
61214
62486
  }
62487
+ function completionCommands(runtime, session) {
62488
+ return availableCommands2(runtime, session, ALL_COMMANDS);
62489
+ }
61215
62490
  function keyHints() {
61216
62491
  return msg().interactive.commands.keyHints;
61217
62492
  }
@@ -64336,7 +65611,7 @@ var init_program_status = __esm({
64336
65611
  });
64337
65612
 
64338
65613
  // src/modes/interactive/program-status-ui.ts
64339
- function blocked(kind, text) {
65614
+ function blocked2(kind, text) {
64340
65615
  return { state: "blocked", kind, msg: text };
64341
65616
  }
64342
65617
  function programStatusUi(deps) {
@@ -64492,9 +65767,9 @@ var init_program_status_ui = __esm({
64492
65767
  /** 当前根记录(测试也用)。 */
64493
65768
  root() {
64494
65769
  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);
65770
+ if (pending !== void 0) return blocked2("permission", pending.msg);
65771
+ if (this.plan !== void 0) return blocked2("question", this.plan);
65772
+ if (this.auth !== void 0) return blocked2("auth", this.auth);
64498
65773
  if (this.running)
64499
65774
  return { state: "working", ...this.activity !== void 0 ? { msg: this.activity } : {} };
64500
65775
  return this.outcome ?? { state: "idle" };
@@ -64618,7 +65893,7 @@ function runInteractiveMode(runtime, context, options = {}) {
64618
65893
  const { hint, bar: status } = area;
64619
65894
  const historyFile = options.historyFile === false ? void 0 : options.historyFile ?? (0, import_node_path101.join)(runtime.paths.dataDir, "history");
64620
65895
  const completion = new InteractiveCompletion({
64621
- commands: () => ALL_COMMANDS,
65896
+ commands: () => completionCommands(runtime, session),
64622
65897
  prompts: () => runtime.resources.prompts,
64623
65898
  skills: () => runtime.resources.skills,
64624
65899
  cwd: () => session.state.cwd,
@@ -64645,6 +65920,7 @@ function runInteractiveMode(runtime, context, options = {}) {
64645
65920
  tui.addChild(status);
64646
65921
  tui.addChild(area.quota);
64647
65922
  let finished = false;
65923
+ const hintTimers = options.hintTimers ?? realHintTimers;
64648
65924
  let hintTimer;
64649
65925
  let resolveExit = () => void 0;
64650
65926
  const render = () => tui.requestRender();
@@ -64654,14 +65930,13 @@ function runInteractiveMode(runtime, context, options = {}) {
64654
65930
  };
64655
65931
  const showHint = (text, ms = HINT_MS) => {
64656
65932
  hint.setText(text === "" ? "" : theme.fg("dim", text));
64657
- if (hintTimer !== void 0) clearTimeout(hintTimer);
65933
+ if (hintTimer !== void 0) hintTimers.clear(hintTimer);
64658
65934
  hintTimer = void 0;
64659
65935
  if (text !== "") {
64660
- hintTimer = setTimeout(() => {
65936
+ hintTimer = hintTimers.set(() => {
64661
65937
  hint.setText("");
64662
65938
  render();
64663
65939
  }, ms);
64664
- hintTimer.unref?.();
64665
65940
  }
64666
65941
  render();
64667
65942
  };
@@ -64914,7 +66189,7 @@ function runInteractiveMode(runtime, context, options = {}) {
64914
66189
  if (finished) return;
64915
66190
  finished = true;
64916
66191
  logoAnimation?.finish();
64917
- if (hintTimer !== void 0) clearTimeout(hintTimer);
66192
+ if (hintTimer !== void 0) hintTimers.clear(hintTimer);
64918
66193
  loader.stop();
64919
66194
  area.dispose();
64920
66195
  osc7501.exit();
@@ -64980,7 +66255,7 @@ function runInteractiveMode(runtime, context, options = {}) {
64980
66255
  }
64981
66256
  });
64982
66257
  }
64983
- var import_node_path101, HINT_MS;
66258
+ var import_node_path101, HINT_MS, realHintTimers;
64984
66259
  var init_interactive_mode = __esm({
64985
66260
  "src/modes/interactive/interactive-mode.ts"() {
64986
66261
  "use strict";
@@ -65019,6 +66294,14 @@ var init_interactive_mode = __esm({
65019
66294
  init_tool_view();
65020
66295
  init_task_background();
65021
66296
  HINT_MS = 2500;
66297
+ realHintTimers = {
66298
+ set: (fn, ms) => {
66299
+ const handle = setTimeout(fn, ms);
66300
+ handle.unref?.();
66301
+ return handle;
66302
+ },
66303
+ clear: (handle) => clearTimeout(handle)
66304
+ };
65022
66305
  }
65023
66306
  });
65024
66307
 
@@ -65061,7 +66344,7 @@ function choose(answer, values) {
65061
66344
  }
65062
66345
  function isDirectory3(path) {
65063
66346
  try {
65064
- return (0, import_node_fs74.statSync)(path).isDirectory();
66347
+ return (0, import_node_fs75.statSync)(path).isDirectory();
65065
66348
  } catch {
65066
66349
  return false;
65067
66350
  }
@@ -65106,7 +66389,7 @@ function createTextStartupUi(io) {
65106
66389
  const answer = (await ask([m.cwdMissing(missing)], m.cwdAnswer))?.trim();
65107
66390
  if (answer === void 0 || answer === "") return void 0;
65108
66391
  const abs = (0, import_node_path102.resolve)(answer);
65109
- if (!(0, import_node_fs74.existsSync)(abs) || !isDirectory3(abs)) {
66392
+ if (!(0, import_node_fs75.existsSync)(abs) || !isDirectory3(abs)) {
65110
66393
  io.write(m.notDirectory(abs));
65111
66394
  return void 0;
65112
66395
  }
@@ -65114,11 +66397,11 @@ function createTextStartupUi(io) {
65114
66397
  }
65115
66398
  };
65116
66399
  }
65117
- var import_node_fs74, import_node_path102;
66400
+ var import_node_fs75, import_node_path102;
65118
66401
  var init_startup_ui_text = __esm({
65119
66402
  "src/modes/startup-ui-text.ts"() {
65120
66403
  "use strict";
65121
- import_node_fs74 = require("node:fs");
66404
+ import_node_fs75 = require("node:fs");
65122
66405
  import_node_path102 = require("node:path");
65123
66406
  init_i18n();
65124
66407
  }
@@ -65155,6 +66438,7 @@ function createTools(input2, options, state) {
65155
66438
  };
65156
66439
  const builtins = builtinTools({
65157
66440
  bash,
66441
+ ...options.webFetch !== void 0 ? { webFetch: options.webFetch } : {},
65158
66442
  read: {
65159
66443
  supportsImages: (ctx2) => modelAcceptsImages(state, ctx2.model),
65160
66444
  imageOptions: (ctx2) => imageFitOptionsFor(state.providers, ctx2.model, input2.config.images)
@@ -65564,7 +66848,7 @@ var init_memory3 = __esm({
65564
66848
  });
65565
66849
 
65566
66850
  // src/cli/main.ts
65567
- var import_node_fs75 = require("node:fs");
66851
+ var import_node_fs76 = require("node:fs");
65568
66852
  var import_node_path104 = require("node:path");
65569
66853
  var import_node_url3 = require("node:url");
65570
66854
  init_version();
@@ -66101,7 +67385,7 @@ init_token_client();
66101
67385
 
66102
67386
  // src/auth/chatgpt/host-id.ts
66103
67387
  var import_node_crypto19 = require("node:crypto");
66104
- var import_node_fs48 = require("node:fs");
67388
+ var import_node_fs49 = require("node:fs");
66105
67389
  var import_node_path70 = require("node:path");
66106
67390
  var HOST_ID_FILE = "chatgpt-host.json";
66107
67391
  function hostIdPath(dataDir) {
@@ -66110,14 +67394,14 @@ function hostIdPath(dataDir) {
66110
67394
  function readOrCreateHostId(dataDir) {
66111
67395
  const path = hostIdPath(dataDir);
66112
67396
  try {
66113
- const value = JSON.parse((0, import_node_fs48.readFileSync)(path, "utf8"));
67397
+ const value = JSON.parse((0, import_node_fs49.readFileSync)(path, "utf8"));
66114
67398
  if (typeof value.hostId === "string" && /^urn:uuid:[0-9a-f-]{36}$/i.test(value.hostId))
66115
67399
  return value.hostId;
66116
67400
  } catch {
66117
67401
  }
66118
67402
  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)}
67403
+ (0, import_node_fs49.mkdirSync)((0, import_node_path70.dirname)(path), { recursive: true, mode: 448 });
67404
+ (0, import_node_fs49.writeFileSync)(path, `${JSON.stringify({ version: 1, hostId }, null, 2)}
66121
67405
  `, { mode: 384 });
66122
67406
  return hostId;
66123
67407
  }
@@ -66630,7 +67914,7 @@ init_merge();
66630
67914
  init_key_docs();
66631
67915
  init_paths();
66632
67916
  var import_node_child_process14 = require("node:child_process");
66633
- var import_node_fs57 = require("node:fs");
67917
+ var import_node_fs58 = require("node:fs");
66634
67918
  var import_node_path79 = require("node:path");
66635
67919
  init_models_dev_cache();
66636
67920
  init_init();
@@ -66692,7 +67976,7 @@ init_ansi();
66692
67976
  // src/cli/subcommands/config-set.ts
66693
67977
  init_i18n();
66694
67978
  init_edit2();
66695
- var import_node_fs56 = require("node:fs");
67979
+ var import_node_fs57 = require("node:fs");
66696
67980
  var import_node_path78 = require("node:path");
66697
67981
  init_init();
66698
67982
  init_paths();
@@ -66822,7 +68106,7 @@ async function runConfigEdit(action, argv, io, deps = {}) {
66822
68106
  if (key === "permission.mode" && value === "full-auto" && scope === "user") {
66823
68107
  if (!await confirmBypass(io, flags2.has("yes"), deps)) return ExitCode.Config;
66824
68108
  }
66825
- if (scope === "user" && !(0, import_node_fs56.existsSync)((0, import_node_path78.join)(input2.configDir, CONFIG_FILE)))
68109
+ if (scope === "user" && !(0, import_node_fs57.existsSync)((0, import_node_path78.join)(input2.configDir, CONFIG_FILE)))
66826
68110
  initConfigDir(input2.configDir);
66827
68111
  report(io, setConfigValue({ ...input2, scope, key, value }), value);
66828
68112
  return ExitCode.Ok;
@@ -66835,7 +68119,7 @@ function showPaths(io) {
66835
68119
  const configDir = resolveConfigDir({ env: io.env });
66836
68120
  const dataDir = resolveDataDir({ env: io.env });
66837
68121
  const m = msg().subcommands.configShow;
66838
- const mark = (path) => `${path}${(0, import_node_fs57.existsSync)(path) ? "" : m.missing}`;
68122
+ const mark = (path) => `${path}${(0, import_node_fs58.existsSync)(path) ? "" : m.missing}`;
66839
68123
  const width2 = Math.max(...[m.configDir, m.dataDir, m.projectLevel].map(visibleWidth)) + 2;
66840
68124
  const label2 = (text) => padToWidth(text, width2);
66841
68125
  const lines2 = [
@@ -66856,7 +68140,7 @@ function showPaths(io) {
66856
68140
  function editConfig(io) {
66857
68141
  const configDir = resolveConfigDir({ env: io.env });
66858
68142
  const path = (0, import_node_path79.join)(configDir, CONFIG_FILE);
66859
- if (!(0, import_node_fs57.existsSync)(path)) initConfigDir(configDir);
68143
+ if (!(0, import_node_fs58.existsSync)(path)) initConfigDir(configDir);
66860
68144
  const editor = (io.env["VISUAL"] ?? io.env["EDITOR"] ?? "").trim();
66861
68145
  if (editor === "") {
66862
68146
  io.stdout(msg().subcommands.configShow.noEditor(path));
@@ -67148,7 +68432,7 @@ async function runConfig(argv, io, deps) {
67148
68432
  }
67149
68433
 
67150
68434
  // src/cli/subcommands/doctor.ts
67151
- var import_node_fs58 = require("node:fs");
68435
+ var import_node_fs59 = require("node:fs");
67152
68436
  init_blobs();
67153
68437
  init_shadow_git();
67154
68438
  init_gc();
@@ -67167,7 +68451,7 @@ init_args();
67167
68451
  init_exit_codes();
67168
68452
 
67169
68453
  // src/cli/proxy.ts
67170
- var http = __toESM(require("node:http"), 1);
68454
+ var http2 = __toESM(require("node:http"), 1);
67171
68455
  init_i18n();
67172
68456
  function pick5(env, name) {
67173
68457
  const value = env[name.toLowerCase()] ?? env[name.toUpperCase()];
@@ -67175,18 +68459,18 @@ function pick5(env, name) {
67175
68459
  }
67176
68460
  function readProxyEnv(env) {
67177
68461
  const out = {};
67178
- const https = pick5(env, "https_proxy");
68462
+ const https2 = pick5(env, "https_proxy");
67179
68463
  const plain = pick5(env, "http_proxy");
67180
68464
  const no = pick5(env, "no_proxy");
67181
- if (https !== void 0) out.httpsProxy = https;
68465
+ if (https2 !== void 0) out.httpsProxy = https2;
67182
68466
  if (plain !== void 0) out.httpProxy = plain;
67183
68467
  if (no !== void 0) out.noProxy = no;
67184
68468
  return out;
67185
68469
  }
67186
68470
  function defaultProxyRuntime() {
67187
- const fn = http.setGlobalProxyFromEnv;
68471
+ const fn = http2.setGlobalProxyFromEnv;
67188
68472
  return {
67189
- setGlobalProxyFromEnv: typeof fn === "function" ? fn.bind(http) : void 0,
68473
+ setGlobalProxyFromEnv: typeof fn === "function" ? fn.bind(http2) : void 0,
67190
68474
  execArgv: process.execArgv,
67191
68475
  version: process.versions.node
67192
68476
  };
@@ -67521,7 +68805,7 @@ async function runDoctor(argv, io, deps) {
67521
68805
  const m = msg().report.doctor;
67522
68806
  report2.section(m.dirsTitle);
67523
68807
  report2.item(m.configDir(level.configDir));
67524
- report2.item(m.dataDir(level.dataDir, (0, import_node_fs58.existsSync)(level.dataDir)));
68808
+ report2.item(m.dataDir(level.dataDir, (0, import_node_fs59.existsSync)(level.dataDir)));
67525
68809
  await fileHistoryLine(report2, level.dataDir);
67526
68810
  report2.section(m.layersTitle);
67527
68811
  report2.item(m.builtinDefaults);
@@ -67749,7 +69033,7 @@ init_discovered_cache();
67749
69033
  init_catalog2();
67750
69034
  init_models_dev_cache();
67751
69035
  init_models_dev();
67752
- var import_node_fs59 = require("node:fs");
69036
+ var import_node_fs60 = require("node:fs");
67753
69037
  init_load();
67754
69038
  init_write2();
67755
69039
  init_args();
@@ -68112,7 +69396,7 @@ function writeEntries(ctx, provider, entries, notes) {
68112
69396
  return 0;
68113
69397
  }
68114
69398
  models.push(...added);
68115
- const backup = (0, import_node_fs59.existsSync)(path);
69399
+ const backup = (0, import_node_fs60.existsSync)(path);
68116
69400
  writeConfigFile(path, config, { backup: true });
68117
69401
  io.stdout(m.written(path, provider.id, added.length, kept, backup));
68118
69402
  if (notes.noTools.size > 0) io.stdout(m.skippedNoTools([...notes.noTools].join(", ")));
@@ -68537,7 +69821,7 @@ async function runModels(argv, io, deps) {
68537
69821
  }
68538
69822
 
68539
69823
  // src/cli/subcommands/providers.ts
68540
- var import_node_fs60 = require("node:fs");
69824
+ var import_node_fs61 = require("node:fs");
68541
69825
  init_models_dev_cache();
68542
69826
  init_models_dev();
68543
69827
  init_auth_file();
@@ -68884,7 +70168,7 @@ function yesNo(value) {
68884
70168
  const t = msg().subcommands.providersPlan;
68885
70169
  return value === void 0 ? "?" : value ? t.yes : t.no;
68886
70170
  }
68887
- function renderTable2(plans2) {
70171
+ function renderTable3(plans2) {
68888
70172
  const h = msg().subcommands.providersPlan.head;
68889
70173
  const head = [
68890
70174
  h.model,
@@ -69140,7 +70424,7 @@ async function add(ctx, id, refresh) {
69140
70424
  });
69141
70425
  if (result.cancelled !== void 0) return result.cancelled;
69142
70426
  io.stdout(`
69143
- ${renderTable2(result.plans)}`);
70427
+ ${renderTable3(result.plans)}`);
69144
70428
  if (refresh) {
69145
70429
  const gone = [...existingIds].filter((m2) => !listed2.some((l) => l.id === m2));
69146
70430
  if (gone.length > 0) io.stdout(m.goneUpstream(gone));
@@ -69194,7 +70478,7 @@ ${renderTable2(result.plans)}`);
69194
70478
  const providers = config.providers ??= {};
69195
70479
  providers[id] = merged.config;
69196
70480
  if (defaultPick !== void 0) config.defaultModel = `${id}/${defaultPick.id}`;
69197
- const backup = (0, import_node_fs60.existsSync)(level.userConfigPath);
70481
+ const backup = (0, import_node_fs61.existsSync)(level.userConfigPath);
69198
70482
  writeConfigFile(level.userConfigPath, config, { backup: true });
69199
70483
  if (key.store !== void 0) setAuthKey(level.authFile, id, key.store);
69200
70484
  io.stdout(m.written(level.userConfigPath, backup));
@@ -69319,7 +70603,7 @@ init_reuse();
69319
70603
  init_clipboard_image();
69320
70604
 
69321
70605
  // src/cli/subcommands/sessions-export.ts
69322
- var import_node_fs62 = require("node:fs");
70606
+ var import_node_fs63 = require("node:fs");
69323
70607
  var import_node_path81 = require("node:path");
69324
70608
  init_paths();
69325
70609
 
@@ -69551,7 +70835,7 @@ async function runSessionsExport(argv, io) {
69551
70835
  return ExitCode.Ok;
69552
70836
  }
69553
70837
  const target = (0, import_node_path81.resolve)(io.cwd, expandHome(output, { env: io.env }));
69554
- (0, import_node_fs62.writeFileSync)(target, text, { mode: 384 });
70838
+ (0, import_node_fs63.writeFileSync)(target, text, { mode: 384 });
69555
70839
  io.stderr(msg().subcommands.sessionsExport.exported(target));
69556
70840
  return ExitCode.Ok;
69557
70841
  }
@@ -69883,7 +71167,7 @@ function aggregateStats(summaries, options = {}) {
69883
71167
  }
69884
71168
 
69885
71169
  // src/session/stats-index.ts
69886
- var import_node_fs63 = require("node:fs");
71170
+ var import_node_fs64 = require("node:fs");
69887
71171
  var import_node_path82 = require("node:path");
69888
71172
 
69889
71173
  // src/session/stats-scan.ts
@@ -70097,7 +71381,7 @@ function collectSummaries(files, options = {}) {
70097
71381
  let index2 = { version: INDEX_VERSION, zone, files: {} };
70098
71382
  if (options.indexFile !== void 0) {
70099
71383
  try {
70100
- const loaded = JSON.parse((0, import_node_fs63.readFileSync)(options.indexFile, "utf8"));
71384
+ const loaded = JSON.parse((0, import_node_fs64.readFileSync)(options.indexFile, "utf8"));
70101
71385
  if (loaded.version === INDEX_VERSION && loaded.zone === zone && typeof loaded.files === "object")
70102
71386
  index2 = loaded;
70103
71387
  } catch {
@@ -70108,7 +71392,7 @@ function collectSummaries(files, options = {}) {
70108
71392
  for (const file of files) {
70109
71393
  let stat16;
70110
71394
  try {
70111
- stat16 = (0, import_node_fs63.statSync)(file);
71395
+ stat16 = (0, import_node_fs64.statSync)(file);
70112
71396
  } catch {
70113
71397
  result.invalid++;
70114
71398
  continue;
@@ -70138,10 +71422,10 @@ function collectSummaries(files, options = {}) {
70138
71422
  }
70139
71423
  if (dirty && options.indexFile !== void 0) {
70140
71424
  try {
70141
- (0, import_node_fs63.mkdirSync)((0, import_node_path82.dirname)(options.indexFile), { recursive: true, mode: 448 });
71425
+ (0, import_node_fs64.mkdirSync)((0, import_node_path82.dirname)(options.indexFile), { recursive: true, mode: 448 });
70142
71426
  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);
71427
+ (0, import_node_fs64.writeFileSync)(tmp, JSON.stringify(index2), { mode: 384 });
71428
+ (0, import_node_fs64.renameSync)(tmp, options.indexFile);
70145
71429
  } catch {
70146
71430
  }
70147
71431
  }
@@ -70427,7 +71711,7 @@ async function runSessionsSearch(argv, io) {
70427
71711
 
70428
71712
  // src/cli/subcommands/sessions-trace.ts
70429
71713
  var import_node_child_process15 = require("node:child_process");
70430
- var import_node_fs65 = require("node:fs");
71714
+ var import_node_fs66 = require("node:fs");
70431
71715
  var import_node_os13 = require("node:os");
70432
71716
  var import_node_path86 = require("node:path");
70433
71717
  init_paths();
@@ -70835,9 +72119,9 @@ function openCommand(platform, file) {
70835
72119
  return { command: "xdg-open", args: [file] };
70836
72120
  }
70837
72121
  function spawnOpen({ command: command3, args }) {
70838
- return new Promise((done, fail6) => {
72122
+ return new Promise((done, fail7) => {
70839
72123
  const child = (0, import_node_child_process15.spawn)(command3, args, { detached: true, stdio: "ignore" });
70840
- child.once("error", fail6);
72124
+ child.once("error", fail7);
70841
72125
  child.once("spawn", () => {
70842
72126
  child.unref();
70843
72127
  done();
@@ -70866,7 +72150,7 @@ function splitHtmlFlag(argv) {
70866
72150
  }
70867
72151
  function locate(io, target, dirFlag) {
70868
72152
  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;
72153
+ if (/[\\/]|\.jsonl$/.test(target) && (0, import_node_fs66.existsSync)(asPath)) return asPath;
70870
72154
  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
72155
  return findSessionFileReadOnly(root, target, io.cwd);
70872
72156
  }
@@ -70939,7 +72223,7 @@ async function runSessionsTrace(argv, io, deps = {}) {
70939
72223
  io.stdout(text);
70940
72224
  return ExitCode.Ok;
70941
72225
  }
70942
- (0, import_node_fs65.writeFileSync)(outPath, text, { mode: 384 });
72226
+ (0, import_node_fs66.writeFileSync)(outPath, text, { mode: 384 });
70943
72227
  io.stderr(m.wrote(outPath));
70944
72228
  if (open3) {
70945
72229
  try {
@@ -71140,7 +72424,7 @@ function readStdinDefault() {
71140
72424
  }
71141
72425
  function stdinKindDefault() {
71142
72426
  try {
71143
- const stat16 = (0, import_node_fs75.fstatSync)(0);
72427
+ const stat16 = (0, import_node_fs76.fstatSync)(0);
71144
72428
  if (stat16.isFile()) return "file";
71145
72429
  if (stat16.isFIFO()) return "fifo";
71146
72430
  if (stat16.isSocket()) return "socket";
@@ -71200,7 +72484,7 @@ function defaultIo() {
71200
72484
  }
71201
72485
  function peekUserLanguage(env) {
71202
72486
  try {
71203
- const raw = (0, import_node_fs75.readFileSync)((0, import_node_path104.join)(resolveConfigDir({ env }), "config.json"), "utf8");
72487
+ const raw = (0, import_node_fs76.readFileSync)((0, import_node_path104.join)(resolveConfigDir({ env }), "config.json"), "utf8");
71204
72488
  const value = JSON.parse(raw).ui?.language;
71205
72489
  return value === "auto" || value === "zh" || value === "en" ? value : void 0;
71206
72490
  } catch {
@@ -71288,7 +72572,7 @@ function isDirectRun() {
71288
72572
  const entry = process.argv[1];
71289
72573
  if (entry === void 0) return false;
71290
72574
  try {
71291
- return (0, import_node_fs75.realpathSync)(entry) === (0, import_node_fs75.realpathSync)((0, import_node_url3.fileURLToPath)(__ama_import_meta_url));
72575
+ return (0, import_node_fs76.realpathSync)(entry) === (0, import_node_fs76.realpathSync)((0, import_node_url3.fileURLToPath)(__ama_import_meta_url));
71292
72576
  } catch {
71293
72577
  return false;
71294
72578
  }