@xiaoso/dsh-tool-plus 0.1.6 → 0.1.7-beta.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 (119) hide show
  1. package/THIRD_PARTY_NOTICES.md +9 -3
  2. package/lib/{abort-Cwwm4Err.mjs → abort-Caqff3M6.mjs} +2 -2
  3. package/lib/alibaba-coding-plan-DTupMiAi.mjs +73 -0
  4. package/lib/alibaba-token-plan-CHGyWOfX.mjs +46 -0
  5. package/lib/alibaba-token-plan-Dj3OZhEE.mjs +82 -0
  6. package/lib/{amazon-bedrock-TVMpUTRG.mjs → amazon-bedrock-DbFSYXfJ.mjs} +117 -38
  7. package/lib/{anthropic-BL6LFDQS.mjs → anthropic-CTJ3IQVa.mjs} +675 -166
  8. package/lib/anthropic-CXfWlp-x.mjs +105 -0
  9. package/lib/{aws-credentials-B20dliS7.mjs → anthropic-wire-CKuhDwZ1.mjs} +74 -14
  10. package/lib/{api-key-login-CIDnXeYd.mjs → api-key-validation-CGCJFbFr.mjs} +27 -68
  11. package/lib/{azure-openai-responses-BkhsLmN3.mjs → azure-openai-responses-ChFbWaGj.mjs} +27 -16
  12. package/lib/{browser-render-CmgvSNte.mjs → browser-render-9o0Rknop.mjs} +4 -4
  13. package/lib/bun-shim.mjs +71 -2
  14. package/lib/{bundled-references-ChZSPDFw.mjs → bundled-references-DY3EJxZV.mjs} +6 -5
  15. package/lib/cline-pass-Bwg3esu9.mjs +44 -0
  16. package/lib/cloudflare-ai-gateway-BupNXyKK.mjs +35 -0
  17. package/lib/cloudflare-ai-gateway-EWo0VkP5.mjs +44 -0
  18. package/lib/collapse-DOR8sOGf.mjs +2304 -0
  19. package/lib/coreweave-C82HrLpb.mjs +31 -0
  20. package/lib/coreweave-Ct8cF-da.mjs +14 -0
  21. package/lib/{cursor-B6HcmplF.mjs → cursor-B95LzVWV.mjs} +11 -3
  22. package/lib/{cursor-CRpLpaZ3.mjs → cursor-DJV_dcgB.mjs} +887 -1784
  23. package/lib/{cursor-B8m6SqAH.mjs → cursor-LKGjucn2.mjs} +48 -38
  24. package/lib/cursor-proto-B8Wj8bv9.mjs +10513 -0
  25. package/lib/{deterministic-id-D-yIV_eI.mjs → deterministic-id-CUEIMSqR.mjs} +1 -1
  26. package/lib/devin-BMTdx_hx.mjs +316 -0
  27. package/lib/{devin-DUAfvBsW.mjs → devin-DSp0Rw_E.mjs} +172 -86
  28. package/lib/devin-ExGIghmx.mjs +69 -0
  29. package/lib/devin-proto-Br8fdyNl.mjs +2888 -0
  30. package/lib/{dom-CawWi8fa.mjs → dom-DG3sPESc.mjs} +8 -8
  31. package/lib/effort-BdtQMN-_.mjs +23 -0
  32. package/lib/{error-C-f_Wnw4.mjs → error-CORgGg5a.mjs} +275 -154
  33. package/lib/{event-stream-Dh2guZxS.mjs → event-stream-ulcDenZD.mjs} +20 -4
  34. package/lib/{gemini-headers-DKUShltF.mjs → gemini-headers-Co9cVvex.mjs} +2 -2
  35. package/lib/{github-copilot-suDymKrx.mjs → github-copilot-Dstq1D3s.mjs} +69 -29
  36. package/lib/github-copilot-headers-DOh1gt_w.mjs +312 -0
  37. package/lib/gitlab-duo-DSCMrU0m.mjs +11 -0
  38. package/lib/google-CtsKkOQK.mjs +38 -0
  39. package/lib/google-antigravity-z-5KsEci.mjs +252 -0
  40. package/lib/{google-auth-DYJLM792.mjs → google-auth-ieLBaneO.mjs} +4 -4
  41. package/lib/{google-gemini-cli-8BT513Ul.mjs → google-gemini-cli-C6NE5mWz.mjs} +28 -54
  42. package/lib/{google-gemini-cli-D6cSmfvB.mjs → google-gemini-cli-CAIPSl7e.mjs} +23 -29
  43. package/lib/google-oauth-shared-DhMllAkt.mjs +53 -0
  44. package/lib/{google-shared-CVbHK5vZ.mjs → google-shared-B0aNCYEH.mjs} +140 -44
  45. package/lib/{google-validation-Cgbn3tWJ.mjs → google-validation-BEkNs-Bx.mjs} +1 -1
  46. package/lib/{google-vertex-Bui-ypAe.mjs → google-vertex-Bbd1tYNc.mjs} +8 -8
  47. package/lib/hosts-00rjRzUD.mjs +203 -0
  48. package/lib/id-AL5obQzR.mjs +71 -0
  49. package/lib/{idle-iterator-CYAxUkSD.mjs → idle-iterator-DlUkEfzW.mjs} +3 -3
  50. package/lib/index.mjs +587 -5266
  51. package/lib/kilo-ChJL54wq.mjs +81 -0
  52. package/lib/kimi-C2c_Zq8A.mjs +67 -0
  53. package/lib/{launch-BmgEUycu.mjs → launch-DKDH71En.mjs} +1 -1
  54. package/lib/markit-CmRfm4E2.mjs +9885 -0
  55. package/lib/models-DnXMYGcJ.mjs +381604 -0
  56. package/lib/muse-code-Di1VZmB1.mjs +143 -0
  57. package/lib/{ollama-Df9ce-Cp.mjs → ollama-B8ic3sEp.mjs} +15 -30
  58. package/lib/{openai-codex-CAVI3Me-.mjs → openai-codex-DuJNC99x.mjs} +29 -99
  59. package/lib/{openai-codex-responses-BcnOotRB.mjs → openai-codex-responses-BL29nh9K.mjs} +204 -116
  60. package/lib/{openai-completions-Bq4WT47M.mjs → openai-completions-BK2f2dBv.mjs} +152 -82
  61. package/lib/openai-configuration-update-xMYUjaK_.mjs +150 -0
  62. package/lib/{openai-reasoning-fallback-DXsfiSXO.mjs → openai-reasoning-fallback-BdB3RCbj.mjs} +106 -12
  63. package/lib/{openai-responses-Mq_-PjvE.mjs → openai-responses-CGQPlIIy.mjs} +76 -82
  64. package/lib/{perplexity-XgLF73E7.mjs → perplexity-TBjz3VMB.mjs} +52 -9
  65. package/lib/{pkce-BlQikGPu.mjs → pkce-BYjmugmw.mjs} +1 -1
  66. package/lib/protobuf-aab1UGVn.mjs +717 -0
  67. package/lib/{provider-response-C6vcbQyO.mjs → provider-response-SqKSkkUc.mjs} +1 -1
  68. package/lib/{proxy-D9lm7ONA.mjs → proxy-5nWqRL1f.mjs} +45 -5
  69. package/lib/{readability-Cpm-ocau.mjs → readability-uGhqb4GC.mjs} +2 -2
  70. package/lib/reference-CtvM4zLU.mjs +114 -0
  71. package/lib/{request-debug-HJMoJncL.mjs → request-debug-NQN0BjHH.mjs} +1 -1
  72. package/lib/{sanitize-text-CfcXzw5X.mjs → sanitize-text-BCf5KmCf.mjs} +1 -1
  73. package/lib/{scrapers-Cg5w77td.mjs → scrapers-D8oWspgW.mjs} +12 -12
  74. package/lib/{src-BcD0_rQR.mjs → src-BwW51vA5.mjs} +979 -388
  75. package/lib/{sse-debug-CYdjOC96.mjs → sse-debug-B-tkLCPW.mjs} +1 -1
  76. package/lib/{stream-Bjq8wjfr.mjs → stream-B_7qN-bA.mjs} +3802 -2623
  77. package/lib/stream-markup-healing-DKT1lnkW.mjs +267 -0
  78. package/lib/{strict-tool-validation-imVLUcbI.mjs → strict-tool-validation-CBTz1dAA.mjs} +2 -2
  79. package/lib/taxonomy-DSIM99rS.mjs +15392 -0
  80. package/lib/{stream-markup-healing-DZ6bVDxH.mjs → thinking-CiLa2LFB.mjs} +31 -186
  81. package/lib/{turndown-DN7ByzEI.mjs → turndown-DrX1M4WG.mjs} +4 -4
  82. package/lib/{type-guards-B9VVjRDK.mjs → type-guards-CttAANEx.mjs} +1 -1
  83. package/lib/{types-DX_bvuus.mjs → types-PVueGi9S.mjs} +2 -2
  84. package/lib/{utils-C-dSORPa.mjs → utils-CCfhUQen.mjs} +2364 -2676
  85. package/lib/{utils-Cp2Mxf_U.mjs → utils-CUPhpw9A.mjs} +413 -134
  86. package/lib/{utils-CQHPEQ3W.mjs → utils-DeWqNWV8.mjs} +2 -2
  87. package/lib/{vision-guard-mBmtAHx0.mjs → vision-guard-_XwsL1DF.mjs} +14 -29
  88. package/lib/xai-oauth-CXhnz0gO.mjs +210 -0
  89. package/lib/{xiaomi-7mE345KX.mjs → xiaomi-psoUItal.mjs} +8 -37
  90. package/lib/{zai-CeNpMV3W.mjs → zai-ApJ8HBgT.mjs} +8 -71
  91. package/lib/{zip-BK7UHCwX.mjs → zip-Kd602XNe.mjs} +1 -1
  92. package/package.json +11 -11
  93. package/presets/package.json +1 -1
  94. package/presets/tool-plus-ptc/agent.cordis.yml +1 -1
  95. package/presets/tool-plus-standard/agent.cordis.yml +1 -1
  96. package/lib/agent_pb-Rizyt9Zg.mjs +0 -805
  97. package/lib/anthropic-CDmj476p.mjs +0 -262
  98. package/lib/api_server_pb-CDZgLMta.mjs +0 -252
  99. package/lib/callback-server-CFJK-jvK.mjs +0 -398
  100. package/lib/codex-P1dGpX7G.mjs +0 -54
  101. package/lib/device-code-CuXTWYwY.mjs +0 -51
  102. package/lib/devin-BcQwht1W.mjs +0 -114
  103. package/lib/devin-BvMfGevz.mjs +0 -95
  104. package/lib/github-copilot-B3kQl0g0.mjs +0 -104
  105. package/lib/gitlab-duo-BriUTP8k.mjs +0 -155
  106. package/lib/gitlab-duo-workflow-wK5Ljzmc.mjs +0 -91
  107. package/lib/google-CE5tFDod.mjs +0 -30
  108. package/lib/google-antigravity-C1jgNljz.mjs +0 -198
  109. package/lib/google-oauth-shared-BLVTvokk.mjs +0 -147
  110. package/lib/grammar-D-NqNUxQ.mjs +0 -49
  111. package/lib/identity-HNO1Oe9B.mjs +0 -219
  112. package/lib/kimi-DgcUpPtK.mjs +0 -236
  113. package/lib/markit-DtGoRE6M.mjs +0 -1417
  114. package/lib/minimax-code-Y3h5li9N.mjs +0 -53
  115. package/lib/models-DRA2c_8U.mjs +0 -104660
  116. package/lib/opencode-BsVw9rCD.mjs +0 -38
  117. package/lib/to-binary-B1BEBIPC.mjs +0 -6002
  118. package/lib/wafer-BlhWLweO.mjs +0 -17
  119. package/lib/xai-oauth-5pORHUrg.mjs +0 -446
@@ -17,7 +17,51 @@ import { isPromise } from "node:util/types";
17
17
  import inspector from "node:inspector";
18
18
  import { isMainThread } from "node:worker_threads";
19
19
  import * as tls from "node:tls";
20
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/abortable.ts
20
+ //#region src/tools/bash/which.ts
21
+ /**
22
+ * Minimal PATH (+ PATHEXT on Windows) executable lookup — the plugin's single
23
+ * implementation of `Bun.which` semantics on the Node runtime.
24
+ *
25
+ * Two callers, one scanner: DSH code that must resolve executables the way OMP
26
+ * does (procmgr `resolveWindowsShell` for shell discovery, `REJECT_PROMPT_COMMAND`
27
+ * in non-interactive-env) calls `findOnPath` directly, and the bun-shim installs
28
+ * `Bun.which` on top of it — which is also how upstream pi-utils' `$which` lands
29
+ * here (@oh-my-pi/pi-utils src/which.ts:201 calls `Bun.which` on non-darwin).
30
+ * Single implementation — any new caller resolves executables through here,
31
+ * never hand-rolls a PATH walk.
32
+ * @module @xiaoso/dsh-tool-plus/bash/which
33
+ */
34
+ /** Windows default extension order when PATHEXT is unset. */
35
+ const WIN32_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD";
36
+ /** True when `command` carries its own directory component. */
37
+ function hasPathSeparator(command) {
38
+ return command.includes("/") || command.includes("\\");
39
+ }
40
+ /**
41
+ * Resolve `command` against PATH the way `Bun.which` would: absolute/relative
42
+ * candidates are returned as-is when they name an existing file; bare names
43
+ * are looked up in each PATH dir (appending PATHEXT extensions on win32, in
44
+ * order). Returns the first existing regular file, or `undefined`.
45
+ */
46
+ function findOnPath(command, env = process.env) {
47
+ if (command.length === 0) return void 0;
48
+ if (hasPathSeparator(command)) try {
49
+ return fs.statSync(command).isFile() ? path.resolve(command) : void 0;
50
+ } catch {
51
+ return;
52
+ }
53
+ const dirs = (env.PATH ?? "").split(path.delimiter).filter(Boolean);
54
+ const extensions = process.platform === "win32" ? (env.PATHEXT && env.PATHEXT.length > 0 ? env.PATHEXT : WIN32_DEFAULT_PATHEXT).split(";").filter(Boolean) : [];
55
+ const candidates = process.platform === "win32" ? [command, ...extensions.map((ext) => command + ext)] : [command];
56
+ for (const dir of dirs) for (const candidate of candidates) {
57
+ const full = path.join(dir, candidate);
58
+ try {
59
+ if (fs.statSync(full).isFile()) return full;
60
+ } catch {}
61
+ }
62
+ }
63
+ //#endregion
64
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/abortable.ts
21
65
  var AbortError$1 = class extends Error {
22
66
  constructor(signal) {
23
67
  assert(signal.aborted, "Abort signal must be aborted");
@@ -110,7 +154,7 @@ function once(fn) {
110
154
  };
111
155
  }
112
156
  //#endregion
113
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/peek-file.ts
157
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/peek-file.ts
114
158
  /**
115
159
  * Read the first `maxBytes` of a file (offset 0) and pass that slice to `op`.
116
160
  *
@@ -175,7 +219,7 @@ async function peekFile(filePath, maxBytes, op) {
175
219
  }
176
220
  }
177
221
  //#endregion
178
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/binary.ts
222
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/binary.ts
179
223
  /**
180
224
  * Content-based binary/text classification for files that are about to be
181
225
  * decoded as UTF-8 text and shown to a model or user.
@@ -190,7 +234,11 @@ async function peekFile(filePath, maxBytes, op) {
190
234
  * if (await isProbablyBinary(path)) return "[binary file omitted]";
191
235
  * const text = await Bun.file(path).text();
192
236
  */
193
- /** Header window sniffed for the binary heuristic; mirrors git's 8000-byte scan. */
237
+ /**
238
+ * Header window sniffed for the binary heuristic; mirrors git's 8000-byte scan.
239
+ * Exported so callers that already hold the whole file in memory can sniff the
240
+ * identical prefix through {@link isProbablyBinaryHeader} instead of reopening.
241
+ */
194
242
  const BINARY_SNIFF_BYTES = 8192;
195
243
  /**
196
244
  * Classify an in-memory byte header as binary (non-UTF-8-text).
@@ -218,9 +266,23 @@ function isProbablyBinary(filePath, maxBytes = BINARY_SNIFF_BYTES) {
218
266
  return peekFile(filePath, maxBytes, isProbablyBinaryHeader);
219
267
  }
220
268
  //#endregion
221
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/package.json
222
- var version = "17.3.5";
269
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/package.json
270
+ var version = "18.1.17";
223
271
  var engines = { "bun": ">=1.3.14" };
272
+ //#endregion
273
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/fs-error.ts
274
+ function isFsError(err) {
275
+ return err instanceof Error && "code" in err && typeof err.code === "string";
276
+ }
277
+ function isEnoent(err) {
278
+ return isFsError(err) && err.code === "ENOENT";
279
+ }
280
+ function isEnotdir(err) {
281
+ return isFsError(err) && err.code === "ENOTDIR";
282
+ }
283
+ function hasFsCode(err, code) {
284
+ return isFsError(err) && err.code === code;
285
+ }
224
286
  /** Version (e.g. "1.0.0") */
225
287
  const VERSION = version;
226
288
  /** Default User-Agent header string (e.g. "omp/17.2.12") */
@@ -311,7 +373,29 @@ function standardizeMacOSPath(p) {
311
373
  } catch {}
312
374
  return p;
313
375
  }
314
- standardizeMacOSPath(process.cwd());
376
+ let projectDir;
377
+ /** Get the project directory. */
378
+ function getProjectDir() {
379
+ if (projectDir === void 0) try {
380
+ projectDir = standardizeMacOSPath(process.cwd());
381
+ } catch {
382
+ const candidates = [
383
+ process.env.PWD,
384
+ os.homedir(),
385
+ os.tmpdir()
386
+ ];
387
+ for (const candidate of candidates) {
388
+ if (!candidate || !path.isAbsolute(candidate)) continue;
389
+ try {
390
+ process.chdir(candidate);
391
+ projectDir = standardizeMacOSPath(candidate);
392
+ break;
393
+ } catch {}
394
+ }
395
+ if (projectDir === void 0) throw new Error("Unable to determine an accessible working directory");
396
+ }
397
+ return projectDir;
398
+ }
315
399
  /** Get the config directory name relative to home (e.g. ".omp" or PI_CONFIG_DIR override). */
316
400
  function getConfigDirName() {
317
401
  return process.env.PI_CONFIG_DIR || ".omp";
@@ -530,7 +614,7 @@ function getInstallId() {
530
614
  return next;
531
615
  }
532
616
  //#endregion
533
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/env.ts
617
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/env.ts
534
618
  const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
535
619
  /**
536
620
  * Strict shell-identifier shape. Used for dotenv keys we accept into
@@ -557,6 +641,17 @@ function isSafeEnvValue(value) {
557
641
  function isMacosMallocStackLoggingEnvName(name) {
558
642
  return name === "MallocStackLogging" || name === "MallocStackLoggingNoCompact";
559
643
  }
644
+ /**
645
+ * True when running inside a WSL (Windows Subsystem for Linux) distribution.
646
+ *
647
+ * WSL reports `linux` for `process.platform`, so the only reliable signal is
648
+ * the `WSL_DISTRO_NAME`/`WSL_INTEROP` variables the interop layer injects.
649
+ * Callers use this to translate Windows drive paths to their `/mnt/<drive>`
650
+ * mounts and to route clipboard access through `powershell.exe`.
651
+ */
652
+ function isWsl(platform = process.platform, env = process.env) {
653
+ return platform === "linux" && Boolean(env.WSL_DISTRO_NAME || env.WSL_INTEROP);
654
+ }
560
655
  function readLaunchEnv() {
561
656
  if (process.platform === "linux") try {
562
657
  const values = /* @__PURE__ */ new Map();
@@ -628,7 +723,7 @@ function parseEnvFile(filePath) {
628
723
  const homeEnv = parseEnvFile(path.join(os.homedir(), ".env"));
629
724
  const piEnv = parseEnvFile(path.join(getConfigRootDir(), ".env"));
630
725
  const agentEnv = parseEnvFile(path.join(getAgentDir(), ".env"));
631
- const projectEnv = parseEnvFile(path.join(process.cwd(), ".env"));
726
+ const projectEnv = parseEnvFile(path.join(getProjectDir(), ".env"));
632
727
  for (const key of Object.keys(Bun.env)) {
633
728
  const value = Bun.env[key];
634
729
  if (!isSafeEnvName(key) || isMacosMallocStackLoggingEnvName(key) || value === void 0 || !isSafeEnvValue(value)) delete Bun.env[key];
@@ -1057,18 +1152,35 @@ function xxHash64Core(bytes, seed) {
1057
1152
  h ^= h >> 32n;
1058
1153
  return h & MASK64;
1059
1154
  }
1060
- function bunHash(data) {
1155
+ /**
1156
+ * `Bun.hash(input, seed?)` — FNV-1a 32-bit stand-in, seeded form included.
1157
+ *
1158
+ * The seed is load-bearing: pi-utils folds `command` → `cwd` → `PATH` into one
1159
+ * cache key by re-seeding at each step (@oh-my-pi/pi-utils src/which.ts:207-209),
1160
+ * so a shim that drops the seed collapses every command sharing a PATH onto a
1161
+ * single `$which` cache slot (`$which("b")` returning `a`'s path). FNV steps are
1162
+ * bijections mod 2^32, so distinct seeds stay distinct for equal input — exactly
1163
+ * what a cache key needs. Single-argument values are unchanged (no seed → FNV
1164
+ * offset basis).
1165
+ */
1166
+ function bunHash(data, seed) {
1061
1167
  let bytes;
1062
1168
  if (typeof data === "string") bytes = new TextEncoder().encode(data);
1063
1169
  else if (data instanceof ArrayBuffer) bytes = new Uint8Array(data);
1064
1170
  else bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
1065
- let h = 2166136261;
1171
+ let h = seedToBasis(seed);
1066
1172
  for (const b of bytes) {
1067
1173
  h ^= b;
1068
1174
  h = Math.imul(h, 16777619) >>> 0;
1069
1175
  }
1070
1176
  return h >>> 0;
1071
1177
  }
1178
+ /** Seed → FNV-1a offset basis (non-finite numbers fall back to the default). */
1179
+ function seedToBasis(seed) {
1180
+ if (seed === void 0) return 2166136261;
1181
+ if (typeof seed === "bigint") return Number(BigInt.asUintN(32, seed)) >>> 0;
1182
+ return Number.isFinite(seed) ? Math.trunc(seed) >>> 0 : 2166136261;
1183
+ }
1072
1184
  function isWideChar(c) {
1073
1185
  return c >= 4352 && (c <= 4447 || c === 9001 || c === 9002 || c >= 11904 && c <= 42191 && c !== 12351 || c >= 44032 && c <= 55203 || c >= 63744 && c <= 64255 || c >= 65040 && c <= 65049 || c >= 65072 && c <= 65135 || c >= 65280 && c <= 65376 || c >= 65504 && c <= 65510 || c >= 127744 && c <= 129791 || c >= 131072 && c <= 262141);
1074
1186
  }
@@ -1399,6 +1511,13 @@ function bunStripANSI(text) {
1399
1511
  }
1400
1512
  return out;
1401
1513
  }
1514
+ function bunWhich(command, options) {
1515
+ const cwd = options?.cwd;
1516
+ return findOnPath(cwd !== void 0 && !path.isAbsolute(command) && (command.includes("/") || command.includes("\\")) ? path.resolve(cwd, command) : command, options?.PATH !== void 0 ? {
1517
+ ...process.env,
1518
+ PATH: options.PATH
1519
+ } : process.env) ?? null;
1520
+ }
1402
1521
  function installBunShim() {
1403
1522
  patchBase64();
1404
1523
  const g = globalThis;
@@ -1407,6 +1526,7 @@ function installBunShim() {
1407
1526
  file: (p) => new BunFileShim(p),
1408
1527
  write: bunWrite,
1409
1528
  env: process.env,
1529
+ which: bunWhich,
1410
1530
  sleep: bunSleep,
1411
1531
  hash: bunHash,
1412
1532
  randomUUIDv7: bunRandomUUIDv7,
@@ -1518,7 +1638,7 @@ var CookieMap = class {
1518
1638
  }
1519
1639
  };
1520
1640
  //#endregion
1521
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/logger/rotating-file.ts
1641
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/logger/rotating-file.ts
1522
1642
  /** Behavior-compatible reimplementation of winston-daily-rotate-file's used surface. */
1523
1643
  function isAuditEntry(value) {
1524
1644
  if (value === null || typeof value !== "object") return false;
@@ -1633,7 +1753,7 @@ var RotatingFileSink = class {
1633
1753
  }
1634
1754
  };
1635
1755
  //#endregion
1636
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/logger.ts
1756
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/logger.ts
1637
1757
  /**
1638
1758
  * Centralized logger for omp.
1639
1759
  *
@@ -1708,20 +1828,25 @@ function pruneStaleProcessLogs(dir) {
1708
1828
  } catch {}
1709
1829
  continue;
1710
1830
  }
1711
- try {
1712
- const key = `${pidText}:${logMatch[1]}`;
1713
- const staleLogs = staleLogsByProcessDay.get(key) ?? [];
1714
- staleLogs.push({
1715
- path: entryPath,
1716
- mtimeMs: fs.statSync(entryPath).mtimeMs,
1717
- rollover: Number(logMatch[3] ?? 0)
1718
- });
1719
- staleLogsByProcessDay.set(key, staleLogs);
1720
- } catch {}
1831
+ const key = `${pidText}:${logMatch[1]}`;
1832
+ const staleLogs = staleLogsByProcessDay.get(key) ?? [];
1833
+ staleLogs.push({
1834
+ path: entryPath,
1835
+ rollover: Number(logMatch[3] ?? 0)
1836
+ });
1837
+ staleLogsByProcessDay.set(key, staleLogs);
1721
1838
  }
1722
1839
  for (const staleLogs of staleLogsByProcessDay.values()) {
1723
- staleLogs.sort((a, b) => b.mtimeMs - a.mtimeMs || b.rollover - a.rollover || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
1724
- for (const stale of staleLogs.slice(RETAINED_STALE_LOGS_PER_PROCESS_DAY)) try {
1840
+ if (staleLogs.length <= RETAINED_STALE_LOGS_PER_PROCESS_DAY) continue;
1841
+ const ranked = [];
1842
+ for (const stale of staleLogs) try {
1843
+ ranked.push({
1844
+ ...stale,
1845
+ mtimeMs: fs.statSync(stale.path).mtimeMs
1846
+ });
1847
+ } catch {}
1848
+ ranked.sort((a, b) => b.mtimeMs - a.mtimeMs || b.rollover - a.rollover || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
1849
+ for (const stale of ranked.slice(RETAINED_STALE_LOGS_PER_PROCESS_DAY)) try {
1725
1850
  fs.rmSync(stale.path, { force: true });
1726
1851
  } catch {}
1727
1852
  }
@@ -1897,7 +2022,7 @@ function time(op, fn, ...args) {
1897
2022
  }
1898
2023
  }
1899
2024
  //#endregion
1900
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/frontmatter.ts
2025
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/frontmatter.ts
1901
2026
  function stripHtmlComments(content) {
1902
2027
  return content.replace(/<!--[\s\S]*?-->/g, "");
1903
2028
  }
@@ -2047,49 +2172,29 @@ function parseFrontmatter(content, options) {
2047
2172
  };
2048
2173
  }
2049
2174
  }
2050
- //#endregion
2051
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/fs-error.ts
2052
- function isFsError(err) {
2053
- return err instanceof Error && "code" in err && typeof err.code === "string";
2054
- }
2055
- function isEnoent(err) {
2056
- return isFsError(err) && err.code === "ENOENT";
2057
- }
2058
- function isEnotdir(err) {
2059
- return isFsError(err) && err.code === "ENOTDIR";
2060
- }
2061
- function hasFsCode(err, code) {
2062
- return isFsError(err) && err.code === code;
2063
- }
2064
- //#endregion
2065
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/json-parse.ts
2066
- const QUOTE = 34;
2067
- const BACKSLASH = 92;
2068
- const U = 117;
2069
- const SQUOTE = 39;
2175
+ const U$1 = 117;
2176
+ /** Valid chars after `\` in a strict JSON escape: `" \ / b f n r t u`. */
2070
2177
  const VALID_ESCAPE_CHAR = /* @__PURE__ */ new Uint8Array(128);
2071
2178
  for (const ch of "\"\\/bfnrtu") VALID_ESCAPE_CHAR[ch.charCodeAt(0)] = 1;
2072
- (() => {
2073
- const e = [];
2074
- e[8] = "\\b";
2075
- e[9] = "\\t";
2076
- e[10] = "\\n";
2077
- e[12] = "\\f";
2078
- e[13] = "\\r";
2079
- for (let cp = 0; cp <= 31; cp++) e[cp] ??= `\\u${cp.toString(16).padStart(4, "0")}`;
2080
- return e;
2081
- })();
2082
- const HEX4_RE = /^[0-9a-fA-F]{4}$/;
2083
2179
  function isHexDigit(cp) {
2084
2180
  return cp >= 48 && cp <= 57 || (cp | 32) >= 97 && (cp | 32) <= 102;
2085
2181
  }
2182
+ /** JSON insignificant whitespace (RFC 8259 §2). */
2086
2183
  function isWhitespace(cp) {
2087
2184
  return cp === 32 || cp === 9 || cp === 10 || cp === 13;
2088
2185
  }
2089
2186
  function isIdentChar(cp) {
2090
2187
  return cp >= 48 && cp <= 57 || (cp | 32) >= 97 && (cp | 32) <= 122 || cp === 95 || cp === 36;
2091
2188
  }
2092
- /** Bareword literals: standard JSON plus Python `True`/`False`/`None`. */
2189
+ /** First char of a numeric token: sign, dot, or digit. */
2190
+ function isNumberStart(cp) {
2191
+ return cp === 45 || cp === 43 || cp === 46 || cp >= 48 && cp <= 57;
2192
+ }
2193
+ /** Chars that may continue a relaxed numeric token: digits, sign, dot, exponent, radix prefix, hex digits. */
2194
+ function isNumberChar(cp) {
2195
+ return cp >= 48 && cp <= 57 || cp === 45 || cp === 43 || cp === 46 || (cp | 32) >= 97 && (cp | 32) <= 102 || (cp | 32) === 120;
2196
+ }
2197
+ /** Keyword literals: standard JSON plus Python `True`/`False`/`None`. */
2093
2198
  const KEYWORDS = [
2094
2199
  ["true", true],
2095
2200
  ["false", false],
@@ -2109,216 +2214,129 @@ const NON_RECOVERABLE_BAREWORDS = {
2109
2214
  "+Infinity": true,
2110
2215
  undefined: true
2111
2216
  };
2217
+ /** Decode four hex digits at `pos`, or `-1` when fewer than four hex digits are available. */
2218
+ function hex4(s, pos) {
2219
+ if (pos + 4 > s.length) return -1;
2220
+ let value = 0;
2221
+ for (let k = pos; k < pos + 4; k++) {
2222
+ if (!isHexDigit(s.charCodeAt(k))) return -1;
2223
+ value = value << 4 | parseInt(s[k], 16);
2224
+ }
2225
+ return value;
2226
+ }
2112
2227
  /**
2113
- * Sentinel returned by partial-mode value parsing when an atomic value
2114
- * (number / keyword) is incomplete at the streaming edge, so the enclosing
2115
- * object/array rolls back to the last valid prefix instead of committing junk.
2116
- */
2117
- const INCOMPLETE = Symbol("incomplete");
2118
- /**
2119
- * Recursive-descent parser for a forgiving superset of JSON. Beyond strict JSON
2120
- * it accepts, and normalizes, the malformations LLM tool-call bodies leak in
2121
- * practice:
2122
- *
2123
- * - single-quoted strings and unquoted object keys (JSON5);
2124
- * - trailing / stray commas, and `//` + block comments;
2125
- * - Python literals `True` / `False` / `None` and JS `NaN` / `Infinity`;
2126
- * - raw control characters and invalid `\x` escapes inside strings (kept literally);
2127
- * - unescaped quotes inside strings — a quote only closes a string when followed
2128
- * by a value terminator, recovering apostrophes such as `'it's'`;
2129
- * - unquoted string values in object/array value position (strict mode only) —
2130
- * an unrecognized bareword such as `{"paths": packages/foo/*}` is recovered as
2131
- * a string up to the next `,` / `}` / `]` / newline.
2132
- *
2133
- * In `partial` mode an unterminated string/object/array (or a value cut off at
2134
- * end-of-input) is auto-closed with whatever was parsed so far — for streaming.
2135
- * In strict mode, end-of-input mid-value and trailing garbage both throw, so a
2136
- * final parse never silently accepts a half-formed tool call.
2228
+ * Index of the first char at or after `i` that is not whitespace or part of a
2229
+ * `//` line / `/* *\/` block comment. A lone `/` at the end of input is not a
2230
+ * comment and is left in place.
2137
2231
  */
2138
- var RelaxedJson = class {
2139
- #s;
2140
- #n;
2141
- #partial;
2142
- #i = 0;
2143
- constructor(source, partial) {
2144
- this.#s = source;
2145
- this.#n = source.length;
2146
- this.#partial = partial;
2147
- }
2148
- parse() {
2149
- this.#ws();
2150
- if (this.#i >= this.#n) {
2151
- if (this.#partial) return void 0;
2152
- throw new SyntaxError("Unexpected end of JSON input");
2153
- }
2154
- const value = this.#value(false);
2155
- if (value === INCOMPLETE) return void 0;
2156
- this.#ws();
2157
- if (!this.#partial && this.#i < this.#n) throw new SyntaxError(`Unexpected trailing characters at position ${this.#i}`);
2158
- return value;
2159
- }
2160
- #ws() {
2161
- const s = this.#s;
2162
- for (;;) {
2163
- while (this.#i < this.#n && isWhitespace(s.charCodeAt(this.#i))) this.#i++;
2164
- if (this.#i + 1 < this.#n && s.charCodeAt(this.#i) === 47) {
2165
- const next = s.charCodeAt(this.#i + 1);
2166
- if (next === 47) {
2167
- this.#i += 2;
2168
- while (this.#i < this.#n && s.charCodeAt(this.#i) !== 10) this.#i++;
2169
- continue;
2170
- }
2171
- if (next === 42) {
2172
- this.#i += 2;
2173
- while (this.#i + 1 < this.#n && !(s.charCodeAt(this.#i) === 42 && s.charCodeAt(this.#i + 1) === 47)) this.#i++;
2174
- this.#i = Math.min(this.#i + 2, this.#n);
2175
- continue;
2176
- }
2177
- }
2178
- break;
2179
- }
2180
- }
2181
- #value(allowBareword) {
2182
- const s = this.#s;
2183
- const c = s[this.#i];
2184
- if (c === "{") return this.#object();
2185
- if (c === "[") return this.#array();
2186
- if (c === "\"" || c === "'") return this.#string(s.charCodeAt(this.#i));
2187
- const cc = s.charCodeAt(this.#i);
2188
- if (cc === 45 || cc === 43 || cc === 46 || cc >= 48 && cc <= 57) return this.#number();
2189
- return this.#keyword(allowBareword);
2190
- }
2191
- #object() {
2192
- this.#i++;
2193
- const out = {};
2194
- for (;;) {
2195
- this.#ws();
2196
- if (this.#i >= this.#n) {
2197
- if (this.#partial) return out;
2198
- throw new SyntaxError("Unterminated object");
2199
- }
2200
- const c = this.#s[this.#i];
2201
- if (c === "}") {
2202
- this.#i++;
2203
- return out;
2204
- }
2205
- if (c === ",") {
2206
- this.#i++;
2232
+ function skipInsignificant(s, i) {
2233
+ const n = s.length;
2234
+ for (;;) {
2235
+ while (i < n && isWhitespace(s.charCodeAt(i))) i++;
2236
+ if (i + 1 < n && s.charCodeAt(i) === 47) {
2237
+ const next = s.charCodeAt(i + 1);
2238
+ if (next === 47) {
2239
+ i += 2;
2240
+ while (i < n && s.charCodeAt(i) !== 10) i++;
2207
2241
  continue;
2208
2242
  }
2209
- const key = this.#key();
2210
- this.#ws();
2211
- if (this.#i < this.#n && this.#s[this.#i] === ":") this.#i++;
2212
- else if (this.#partial) return out;
2213
- else throw new SyntaxError("Expected ':' in object");
2214
- this.#ws();
2215
- if (this.#i >= this.#n) {
2216
- if (this.#partial) return out;
2217
- throw new SyntaxError("Expected value after ':'");
2218
- }
2219
- const value = this.#value(true);
2220
- if (value === INCOMPLETE) return out;
2221
- out[key] = value;
2222
- this.#ws();
2223
- const d = this.#i < this.#n ? this.#s[this.#i] : "";
2224
- if (d === ",") {
2225
- this.#i++;
2243
+ if (next === 42) {
2244
+ i += 2;
2245
+ while (i + 1 < n && !(s.charCodeAt(i) === 42 && s.charCodeAt(i + 1) === 47)) i++;
2246
+ i = Math.min(i + 2, n);
2226
2247
  continue;
2227
2248
  }
2228
- if (d === "}") {
2229
- this.#i++;
2230
- return out;
2231
- }
2232
- if (this.#partial) return out;
2233
- throw new SyntaxError("Expected ',' or '}' in object");
2234
2249
  }
2250
+ return i;
2251
+ }
2252
+ }
2253
+ /** Reading of the lookahead past a candidate closing quote. */
2254
+ var QuoteLook = /* @__PURE__ */ function(QuoteLook) {
2255
+ /** A value terminator (or end of input) follows: the quote closes. */
2256
+ QuoteLook[QuoteLook["Closes"] = 0] = "Closes";
2257
+ /** Ordinary content follows: the quote is literal (inner-quote recovery). */
2258
+ QuoteLook[QuoteLook["Inner"] = 1] = "Inner";
2259
+ /** A lone `/` at the buffer edge may still grow into a comment and flip this quote from inner to closing. */
2260
+ QuoteLook[QuoteLook["Undecided"] = 2] = "Undecided";
2261
+ return QuoteLook;
2262
+ }(QuoteLook || {});
2263
+ /**
2264
+ * Cursor over the input with the tolerant token readers. `pos` is the current
2265
+ * offset; readers advance it. In `strict` mode a truncated or malformed token
2266
+ * throws `SyntaxError`; the lenient modes report progress or return
2267
+ * `undefined` so the caller can roll back.
2268
+ */
2269
+ var JsonLexer = class {
2270
+ src;
2271
+ mode;
2272
+ pos;
2273
+ constructor(src, mode, pos = 0) {
2274
+ this.src = src;
2275
+ this.mode = mode;
2276
+ this.pos = pos;
2277
+ }
2278
+ get atEnd() {
2279
+ return this.pos >= this.src.length;
2280
+ }
2281
+ /** Char code at the cursor; `NaN` at end of input (so every comparison is false). */
2282
+ peek() {
2283
+ return this.src.charCodeAt(this.pos);
2284
+ }
2285
+ /** Skip whitespace plus `//` line and `/* *\/` block comments. */
2286
+ ws() {
2287
+ this.pos = skipInsignificant(this.src, this.pos);
2235
2288
  }
2236
- #array() {
2237
- this.#i++;
2238
- const out = [];
2239
- for (;;) {
2240
- this.#ws();
2241
- if (this.#i >= this.#n) {
2242
- if (this.#partial) return out;
2243
- throw new SyntaxError("Unterminated array");
2244
- }
2245
- const c = this.#s[this.#i];
2246
- if (c === "]") {
2247
- this.#i++;
2248
- return out;
2249
- }
2250
- if (c === ",") {
2251
- this.#i++;
2252
- continue;
2253
- }
2254
- const value = this.#value(true);
2255
- if (value === INCOMPLETE) return out;
2256
- out.push(value);
2257
- this.#ws();
2258
- const d = this.#i < this.#n ? this.#s[this.#i] : "";
2259
- if (d === ",") {
2260
- this.#i++;
2261
- continue;
2262
- }
2263
- if (d === "]") {
2264
- this.#i++;
2265
- return out;
2266
- }
2267
- if (this.#partial) return out;
2268
- throw new SyntaxError("Expected ',' or ']' in array");
2269
- }
2270
- }
2271
- #key() {
2272
- const c = this.#s[this.#i];
2273
- if (c === "\"" || c === "'") return this.#string(this.#s.charCodeAt(this.#i));
2274
- const start = this.#i;
2275
- while (this.#i < this.#n) {
2276
- const ch = this.#s[this.#i];
2277
- if (ch === ":" || ch === "," || ch === "}" || isWhitespace(this.#s.charCodeAt(this.#i))) break;
2278
- this.#i++;
2279
- }
2280
- if (this.#i === start) {
2281
- if (this.#partial) return "";
2282
- throw new SyntaxError("Expected object key");
2283
- }
2284
- return this.#s.slice(start, this.#i);
2285
- }
2286
- #string(quote) {
2287
- const s = this.#s;
2288
- const n = this.#n;
2289
- let i = this.#i + 1;
2289
+ /**
2290
+ * Read a string starting at the opening `quote`, retaining the information
2291
+ * an incremental consumer needs. Strict mode throws on an unterminated
2292
+ * string; lenient modes consume to the end of input and report progress.
2293
+ */
2294
+ string(quote) {
2295
+ const s = this.src;
2296
+ const n = s.length;
2297
+ let i = this.pos + 1;
2290
2298
  let out = "";
2291
2299
  let runStart = i;
2300
+ let unstableFrom = -1;
2301
+ const lenient = quote === 39 || this.mode === "streaming";
2292
2302
  while (i < n) {
2293
2303
  const cc = s.charCodeAt(i);
2294
- if (cc !== BACKSLASH && cc !== quote) {
2304
+ if (cc !== 92 && cc !== quote) {
2295
2305
  i++;
2296
2306
  continue;
2297
2307
  }
2298
2308
  if (cc === quote) {
2299
- if (!(quote === SQUOTE || this.#partial) || this.#closesString(i + 1)) {
2309
+ const look = lenient ? this.#quoteLookahead(i + 1) : 0;
2310
+ if (look === 0) {
2300
2311
  out += s.slice(runStart, i);
2301
- this.#i = i + 1;
2302
- return out;
2312
+ this.pos = i + 1;
2313
+ return {
2314
+ value: out,
2315
+ stableLen: out.length,
2316
+ complete: true
2317
+ };
2303
2318
  }
2319
+ if (look === 2 && unstableFrom < 0) unstableFrom = out.length + (i - runStart);
2304
2320
  i++;
2305
2321
  continue;
2306
2322
  }
2307
2323
  out += s.slice(runStart, i);
2324
+ const escapeStart = out.length;
2308
2325
  i++;
2309
2326
  if (i >= n) {
2327
+ if (unstableFrom < 0) unstableFrom = escapeStart;
2310
2328
  out += "\\";
2311
2329
  runStart = i;
2312
2330
  break;
2313
2331
  }
2314
2332
  switch (s.charCodeAt(i)) {
2315
- case QUOTE:
2333
+ case 34:
2316
2334
  out += "\"";
2317
2335
  break;
2318
- case SQUOTE:
2336
+ case 39:
2319
2337
  out += "'";
2320
2338
  break;
2321
- case BACKSLASH:
2339
+ case 92:
2322
2340
  out += "\\";
2323
2341
  break;
2324
2342
  case 47:
@@ -2339,12 +2357,16 @@ var RelaxedJson = class {
2339
2357
  case 116:
2340
2358
  out += " ";
2341
2359
  break;
2342
- case U: {
2343
- const hex = s.slice(i + 1, i + 5);
2344
- if (HEX4_RE.test(hex)) {
2345
- out += String.fromCharCode(parseInt(hex, 16));
2360
+ case U$1: {
2361
+ const unit = hex4(s, i + 1);
2362
+ if (unit >= 0) {
2346
2363
  i += 4;
2347
- } else out += "\\u";
2364
+ out += String.fromCharCode(unit);
2365
+ if (unit >= 55296 && unit < 56320 && this.mode !== "strict" && i + 7 > n && unstableFrom < 0) unstableFrom = escapeStart;
2366
+ } else {
2367
+ if (i + 5 > n && this.mode !== "strict" && unstableFrom < 0) unstableFrom = escapeStart;
2368
+ out += "\\u";
2369
+ }
2348
2370
  break;
2349
2371
  }
2350
2372
  default: out += `\\${s[i]}`;
@@ -2352,82 +2374,264 @@ var RelaxedJson = class {
2352
2374
  i++;
2353
2375
  runStart = i;
2354
2376
  }
2355
- out += s.slice(runStart, i);
2356
- if (this.#partial) {
2357
- this.#i = i;
2358
- return out;
2359
- }
2360
- throw new SyntaxError("Unterminated string");
2361
- }
2362
- /** A quote closes a string only when the next non-space char ends a value. */
2363
- #closesString(from) {
2364
- const s = this.#s;
2365
- let k = from;
2366
- while (k < this.#n && isWhitespace(s.charCodeAt(k))) k++;
2367
- if (k >= this.#n) return true;
2368
- const c = s[k];
2369
- return c === "," || c === "}" || c === "]" || c === ":";
2370
- }
2371
- #number() {
2372
- const s = this.#s;
2373
- const start = this.#i;
2374
- while (this.#i < this.#n) {
2375
- const ch = s[this.#i];
2376
- if (ch >= "0" && ch <= "9" || ch === "-" || ch === "+" || ch === "." || ch === "e" || ch === "E" || ch === "x" || ch === "X" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F") this.#i++;
2377
- else break;
2378
- }
2379
- const token = s.slice(start, this.#i);
2380
- const num = Number(token);
2381
- if (Number.isNaN(num)) {
2382
- if (this.#partial) return INCOMPLETE;
2383
- throw new SyntaxError(`Invalid number: ${token}`);
2384
- }
2385
- return num;
2377
+ if (this.mode === "strict") throw new SyntaxError("Unterminated string");
2378
+ out += s.slice(runStart, n);
2379
+ this.pos = i;
2380
+ return {
2381
+ value: out,
2382
+ stableLen: unstableFrom < 0 ? out.length : unstableFrom,
2383
+ complete: false
2384
+ };
2385
+ }
2386
+ /**
2387
+ * Classify the lookahead after a candidate closing quote: a quote closes a
2388
+ * string only when the next significant char (past whitespace and comments)
2389
+ * ends a value.
2390
+ */
2391
+ #quoteLookahead(from) {
2392
+ const s = this.src;
2393
+ const k = skipInsignificant(s, from);
2394
+ if (k >= s.length) return 0;
2395
+ const c = s.charCodeAt(k);
2396
+ if (c === 44 || c === 125 || c === 93 || c === 58) return 0;
2397
+ if (c === 47 && k + 1 === s.length && this.mode !== "strict") return 2;
2398
+ return 1;
2399
+ }
2400
+ /**
2401
+ * Read a numeric token with JS `Number()` semantics (decimal with optional
2402
+ * sign / leading or trailing dot / exponent, plus `0x` hex and `0b` binary).
2403
+ * Non-finite or malformed tokens throw in strict mode and return
2404
+ * `undefined` in lenient modes; the cursor is left past the token either way.
2405
+ */
2406
+ number() {
2407
+ const s = this.src;
2408
+ const start = this.pos;
2409
+ let i = start;
2410
+ while (i < s.length && isNumberChar(s.charCodeAt(i))) i++;
2411
+ this.pos = i;
2412
+ const token = s.slice(start, i);
2413
+ const value = Number(token);
2414
+ if (Number.isFinite(value)) return value;
2415
+ if (this.mode === "strict") throw new SyntaxError(`Invalid number: ${token}`);
2386
2416
  }
2387
- #keyword(allowBareword) {
2388
- const s = this.#s;
2389
- const i = this.#i;
2390
- for (const [word, value] of KEYWORDS) if (s.startsWith(word, i) && !isIdentChar(s.charCodeAt(i + word.length))) {
2391
- this.#i += word.length;
2417
+ /**
2418
+ * Match a keyword literal at the cursor; consumes only on success and
2419
+ * returns `undefined` otherwise. Requires a non-identifier boundary so
2420
+ * `Truex` / `nullish` are not misread as the keyword followed by junk.
2421
+ */
2422
+ keyword() {
2423
+ const s = this.src;
2424
+ for (const [word, value] of KEYWORDS) if (s.startsWith(word, this.pos) && !isIdentChar(s.charCodeAt(this.pos + word.length))) {
2425
+ this.pos += word.length;
2392
2426
  return value;
2393
2427
  }
2394
- if (this.#partial) {
2395
- this.#i = this.#n;
2396
- return INCOMPLETE;
2428
+ }
2429
+ /** Read an unquoted object key: everything up to `:` / `,` / `}` / whitespace. May be empty. */
2430
+ unquotedKey() {
2431
+ const s = this.src;
2432
+ const start = this.pos;
2433
+ let i = start;
2434
+ while (i < s.length) {
2435
+ const cc = s.charCodeAt(i);
2436
+ if (cc === 58 || cc === 44 || cc === 125 || isWhitespace(cc)) break;
2437
+ i++;
2397
2438
  }
2398
- if (allowBareword) return this.#bareword();
2399
- throw new SyntaxError(`Unexpected token at position ${this.#i}`);
2439
+ this.pos = i;
2440
+ return s.slice(start, i);
2400
2441
  }
2401
2442
  /**
2402
- * Strict-mode recovery of an unquoted string value, e.g.
2403
- * `{"paths": packages/foo/*}`: consume until `,` / `}` / `]` / newline and
2404
- * trim trailing whitespace. Recovery still throws — so a final parse never
2405
- * accepts a half-formed or non-finite argument — when the token:
2443
+ * Recover an unquoted string value, e.g. `{"paths": packages/foo/*}`:
2444
+ * consume until `,` / `}` / `]` / newline and trim trailing whitespace.
2445
+ * Recovery still fails — so a final parse never accepts a half-formed or
2446
+ * non-finite argument — when the token:
2406
2447
  * - hits end-of-input before a delimiter (truncated value);
2407
- * - contains a `"`, `{`, `[`, or a key-like `:` — this parser accepts
2408
- * unquoted keys, so a missed comma (`{"a": foo "b": 1}`, `{a: foo b: 1}`)
2409
- * would otherwise silently swallow the following field. A colon followed
2410
- * by `/` or `\` stays literal so URL and Windows-path values recover;
2448
+ * - contains a `"`, `{`, `[`, or a key-like `:` — this grammar accepts
2449
+ * unquoted keys, so a missed comma (`{"a": foo "b": 1}`) would otherwise
2450
+ * silently swallow the following field. A colon followed by `/` or `\`
2451
+ * stays literal so URL and Windows-path values recover;
2411
2452
  * - is a non-finite atom ({@link NON_RECOVERABLE_BAREWORDS}).
2453
+ *
2454
+ * Failure throws in strict mode and returns `undefined` in lenient modes.
2412
2455
  */
2413
- #bareword() {
2414
- const s = this.#s;
2415
- const start = this.#i;
2456
+ bareword() {
2457
+ const s = this.src;
2458
+ const start = this.pos;
2416
2459
  let i = start;
2417
- while (i < this.#n) {
2460
+ while (i < s.length) {
2418
2461
  const cc = s.charCodeAt(i);
2419
2462
  if (cc === 44 || cc === 125 || cc === 93 || cc === 10 || cc === 13) break;
2420
- if (cc === QUOTE || cc === 123 || cc === 91 || cc === 58 && s.charCodeAt(i + 1) !== 47 && s.charCodeAt(i + 1) !== 92) throw new SyntaxError(`Unexpected token at position ${start}`);
2463
+ if (cc === 34 || cc === 123 || cc === 91 || cc === 58 && s.charCodeAt(i + 1) !== 47 && s.charCodeAt(i + 1) !== 92) return this.#unexpected(start);
2421
2464
  i++;
2422
2465
  }
2423
- if (i >= this.#n) throw new SyntaxError(`Unexpected token at position ${start}`);
2466
+ if (i >= s.length) return this.#unexpected(start);
2424
2467
  let end = i;
2425
2468
  while (end > start && isWhitespace(s.charCodeAt(end - 1))) end--;
2426
2469
  const word = s.slice(start, end);
2427
- if (NON_RECOVERABLE_BAREWORDS[word]) throw new SyntaxError(`Unexpected token at position ${start}`);
2428
- this.#i = i;
2470
+ if (NON_RECOVERABLE_BAREWORDS[word]) return this.#unexpected(start);
2471
+ this.pos = i;
2429
2472
  return word;
2430
2473
  }
2474
+ #unexpected(at) {
2475
+ if (this.mode === "strict") throw new SyntaxError(`Unexpected token at position ${at}`);
2476
+ }
2477
+ };
2478
+ //#endregion
2479
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/json-parse.ts
2480
+ const U = 117;
2481
+ (() => {
2482
+ const e = [];
2483
+ e[8] = "\\b";
2484
+ e[9] = "\\t";
2485
+ e[10] = "\\n";
2486
+ e[12] = "\\f";
2487
+ e[13] = "\\r";
2488
+ for (let cp = 0; cp <= 31; cp++) e[cp] ??= `\\u${cp.toString(16).padStart(4, "0")}`;
2489
+ return e;
2490
+ })();
2491
+ /**
2492
+ * Sentinel returned by partial-mode value parsing when an atomic value
2493
+ * (number / keyword) is incomplete at the streaming edge, so the enclosing
2494
+ * object/array rolls back to the last valid prefix instead of committing junk.
2495
+ */
2496
+ const INCOMPLETE = Symbol("incomplete");
2497
+ /**
2498
+ * Recursive-descent tree builder over the tolerant {@link JsonLexer} grammar
2499
+ * (single quotes, unquoted keys, comments, Python literals, stray commas,
2500
+ * literal invalid escapes, apostrophe recovery, bareword values).
2501
+ *
2502
+ * In `partial` mode (lexer mode `streaming`) an unterminated string/object/array
2503
+ * (or a value cut off at end-of-input) is auto-closed with whatever was parsed
2504
+ * so far, and an incomplete trailing atom rolls the enclosing container back
2505
+ * to its last valid prefix. In strict mode, end-of-input mid-value and trailing
2506
+ * garbage both throw, so a final parse never silently accepts a half-formed
2507
+ * tool call.
2508
+ */
2509
+ var RelaxedJson = class {
2510
+ #lex;
2511
+ #partial;
2512
+ constructor(source, partial) {
2513
+ this.#lex = new JsonLexer(source, partial ? "streaming" : "strict");
2514
+ this.#partial = partial;
2515
+ }
2516
+ parse() {
2517
+ const lex = this.#lex;
2518
+ lex.ws();
2519
+ if (lex.atEnd) {
2520
+ if (this.#partial) return void 0;
2521
+ throw new SyntaxError("Unexpected end of JSON input");
2522
+ }
2523
+ const value = this.#value(false);
2524
+ if (value === INCOMPLETE) return void 0;
2525
+ lex.ws();
2526
+ if (!this.#partial && !lex.atEnd) throw new SyntaxError(`Unexpected trailing characters at position ${lex.pos}`);
2527
+ return value;
2528
+ }
2529
+ #value(allowBareword) {
2530
+ const lex = this.#lex;
2531
+ const c = lex.peek();
2532
+ if (c === 123) return this.#object();
2533
+ if (c === 91) return this.#array();
2534
+ if (c === 34 || c === 39) return lex.string(c).value;
2535
+ if (isNumberStart(c)) return lex.number() ?? INCOMPLETE;
2536
+ const keyword = lex.keyword();
2537
+ if (keyword !== void 0) return keyword;
2538
+ if (this.#partial) {
2539
+ lex.pos = lex.src.length;
2540
+ return INCOMPLETE;
2541
+ }
2542
+ if (allowBareword) return lex.bareword();
2543
+ throw new SyntaxError(`Unexpected token at position ${lex.pos}`);
2544
+ }
2545
+ #object() {
2546
+ const lex = this.#lex;
2547
+ lex.pos++;
2548
+ const out = {};
2549
+ for (;;) {
2550
+ lex.ws();
2551
+ if (lex.atEnd) {
2552
+ if (this.#partial) return out;
2553
+ throw new SyntaxError("Unterminated object");
2554
+ }
2555
+ const c = lex.peek();
2556
+ if (c === 125) {
2557
+ lex.pos++;
2558
+ return out;
2559
+ }
2560
+ if (c === 44) {
2561
+ lex.pos++;
2562
+ continue;
2563
+ }
2564
+ const key = this.#key();
2565
+ lex.ws();
2566
+ if (lex.peek() === 58) lex.pos++;
2567
+ else if (this.#partial) return out;
2568
+ else throw new SyntaxError("Expected ':' in object");
2569
+ lex.ws();
2570
+ if (lex.atEnd) {
2571
+ if (this.#partial) return out;
2572
+ throw new SyntaxError("Expected value after ':'");
2573
+ }
2574
+ const value = this.#value(true);
2575
+ if (value === INCOMPLETE) return out;
2576
+ out[key] = value;
2577
+ lex.ws();
2578
+ const d = lex.peek();
2579
+ if (d === 44) {
2580
+ lex.pos++;
2581
+ continue;
2582
+ }
2583
+ if (d === 125) {
2584
+ lex.pos++;
2585
+ return out;
2586
+ }
2587
+ if (this.#partial) return out;
2588
+ throw new SyntaxError("Expected ',' or '}' in object");
2589
+ }
2590
+ }
2591
+ #array() {
2592
+ const lex = this.#lex;
2593
+ lex.pos++;
2594
+ const out = [];
2595
+ for (;;) {
2596
+ lex.ws();
2597
+ if (lex.atEnd) {
2598
+ if (this.#partial) return out;
2599
+ throw new SyntaxError("Unterminated array");
2600
+ }
2601
+ const c = lex.peek();
2602
+ if (c === 93) {
2603
+ lex.pos++;
2604
+ return out;
2605
+ }
2606
+ if (c === 44) {
2607
+ lex.pos++;
2608
+ continue;
2609
+ }
2610
+ const value = this.#value(true);
2611
+ if (value === INCOMPLETE) return out;
2612
+ out.push(value);
2613
+ lex.ws();
2614
+ const d = lex.peek();
2615
+ if (d === 44) {
2616
+ lex.pos++;
2617
+ continue;
2618
+ }
2619
+ if (d === 93) {
2620
+ lex.pos++;
2621
+ return out;
2622
+ }
2623
+ if (this.#partial) return out;
2624
+ throw new SyntaxError("Expected ',' or ']' in array");
2625
+ }
2626
+ }
2627
+ #key() {
2628
+ const lex = this.#lex;
2629
+ const c = lex.peek();
2630
+ if (c === 34 || c === 39) return lex.string(c).value;
2631
+ const key = lex.unquotedKey();
2632
+ if (key.length === 0 && !this.#partial) throw new SyntaxError("Expected object key");
2633
+ return key;
2634
+ }
2431
2635
  };
2432
2636
  /**
2433
2637
  * Final-parse a JSON value, repairing the common LLM malformations
@@ -2535,11 +2739,11 @@ function classifyJsonPrefix(text) {
2535
2739
  i++;
2536
2740
  while (i < n) {
2537
2741
  const c = text.charCodeAt(i);
2538
- if (c === QUOTE) {
2742
+ if (c === 34) {
2539
2743
  i++;
2540
2744
  return 1;
2541
2745
  }
2542
- if (c === BACKSLASH) {
2746
+ if (c === 92) {
2543
2747
  i++;
2544
2748
  if (i >= n) return 0;
2545
2749
  const e = text.charCodeAt(i);
@@ -2625,7 +2829,7 @@ function classifyJsonPrefix(text) {
2625
2829
  break;
2626
2830
  }
2627
2831
  let r;
2628
- if (c === QUOTE) r = scanString();
2832
+ if (c === 34) r = scanString();
2629
2833
  else if (c === 45 || c >= 48 && c <= 57) r = scanNumber();
2630
2834
  else if (c === 116 || c === 102 || c === 110) r = scanKeyword();
2631
2835
  else return "invalid";
@@ -2642,7 +2846,7 @@ function classifyJsonPrefix(text) {
2642
2846
  expect = valueDone();
2643
2847
  break;
2644
2848
  }
2645
- if (c !== QUOTE) return "invalid";
2849
+ if (c !== 34) return "invalid";
2646
2850
  const r = scanString();
2647
2851
  if (r === -1) return "invalid";
2648
2852
  if (r === 0) return "prefix";
@@ -2683,7 +2887,7 @@ function classifyJsonPrefix(text) {
2683
2887
  }
2684
2888
  new Intl.Segmenter();
2685
2889
  //#endregion
2686
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/mime.ts
2890
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/mime.ts
2687
2891
  const DEFAULT_IMAGE_METADATA_HEADER_BYTES = 262144;
2688
2892
  const PNG_MAGIC = Buffer.from([
2689
2893
  137,
@@ -2876,7 +3080,7 @@ function dlopen() {
2876
3080
  return null;
2877
3081
  }
2878
3082
  //#endregion
2879
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/stderr-guard.ts
3083
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/stderr-guard.ts
2880
3084
  /**
2881
3085
  * Terminal stderr guard: keeps unmanaged fd-2 writes off the terminal while a
2882
3086
  * TUI owns the viewport.
@@ -2941,6 +3145,10 @@ function restoreTerminalStderr() {
2941
3145
  }
2942
3146
  const callbackList = [];
2943
3147
  let cleanupStage = "idle";
3148
+ let cleanupPass = 0;
3149
+ let activeCleanupReason;
3150
+ let activeCleanupKeepAlive = false;
3151
+ let activeLatePromises;
2944
3152
  const CLEANUP_DEADLINE_MS = 1e4;
2945
3153
  /**
2946
3154
  * Symbol stamped by the extension-load guard onto the throwing replacement it
@@ -2975,13 +3183,26 @@ function exitProcess(code) {
2975
3183
  }
2976
3184
  let cleanupPromise;
2977
3185
  const fatalRecoveryHintProviders = /* @__PURE__ */ new Set();
3186
+ function invokeCleanup(registration, reason, keepAlive, pass) {
3187
+ if (registration.cancelled || registration.lastPass === pass) return;
3188
+ if (registration.exitOnly && keepAlive) return;
3189
+ registration.lastPass = pass;
3190
+ return registration.callback(reason);
3191
+ }
2978
3192
  /**
2979
3193
  * Internal: runs all registered cleanup callbacks for the given reason.
2980
- * Ensures each callback is invoked at most once. Handles errors and prevents reentrancy.
3194
+ * Ensures each registration is invoked at most once per pass, handles errors,
3195
+ * and prevents reentrancy.
3196
+ *
3197
+ * `keepAlive` marks a manual cleanup that keeps the process running (see
3198
+ * {@link cleanup}). Such a pass returns the stage to `idle`; registrations stay
3199
+ * active for later resources and the eventual real exit. Exit-only callbacks
3200
+ * skip keep-alive passes without consuming their registration. An exit-driven
3201
+ * pass instead settles to `complete` and stays there.
2981
3202
  *
2982
3203
  * Returns a Promise that settles after all cleanups complete or error out.
2983
3204
  */
2984
- function runCleanup(reason) {
3205
+ function runCleanup(reason, keepAlive = false) {
2985
3206
  switch (cleanupStage) {
2986
3207
  case "idle":
2987
3208
  cleanupStage = "running";
@@ -2989,10 +3210,24 @@ function runCleanup(reason) {
2989
3210
  case "running": return cleanupPromise ?? Promise.resolve();
2990
3211
  case "complete": return Promise.resolve();
2991
3212
  }
2992
- const promises = callbackList.toReversed().map((callback) => {
2993
- return Promise.try(() => callback(reason));
3213
+ const pass = ++cleanupPass;
3214
+ activeCleanupReason = reason;
3215
+ activeCleanupKeepAlive = keepAlive;
3216
+ const late = [];
3217
+ activeLatePromises = late;
3218
+ const settle = () => {
3219
+ if (activeLatePromises === late) activeLatePromises = void 0;
3220
+ if (cleanupPass !== pass) return;
3221
+ cleanupStage = keepAlive ? "idle" : "complete";
3222
+ if (keepAlive) {
3223
+ activeCleanupReason = void 0;
3224
+ activeCleanupKeepAlive = false;
3225
+ }
3226
+ };
3227
+ const promises = callbackList.toReversed().map((registration) => {
3228
+ return Promise.try(() => invokeCleanup(registration, reason, keepAlive, pass));
2994
3229
  });
2995
- const cleanupSettled = Promise.allSettled(promises).then((results) => {
3230
+ const cleanupSettled = Promise.allSettled(promises).then(async (results) => {
2996
3231
  for (const result of results) if (result.status === "rejected") {
2997
3232
  const err = result.reason instanceof Error ? result.reason : new Error(String(result.reason));
2998
3233
  error("Cleanup callback failed", {
@@ -3000,17 +3235,20 @@ function runCleanup(reason) {
3000
3235
  stack: err.stack
3001
3236
  });
3002
3237
  }
3003
- cleanupStage = "complete";
3238
+ while (late.length > 0) await Promise.allSettled(late.splice(0));
3239
+ settle();
3004
3240
  });
3005
3241
  const deadline = Promise.withResolvers();
3006
3242
  const deadlineTimer = setTimeout(() => {
3007
3243
  error("Cleanup deadline exceeded; proceeding with exit", { reason });
3008
- cleanupStage = "complete";
3244
+ settle();
3009
3245
  deadline.resolve();
3010
3246
  }, CLEANUP_DEADLINE_MS);
3011
- cleanupPromise = Promise.race([cleanupSettled, deadline.promise]).finally(() => {
3247
+ const passPromise = Promise.race([cleanupSettled, deadline.promise]).finally(() => {
3012
3248
  clearTimeout(deadlineTimer);
3249
+ if (keepAlive && cleanupPass === pass && cleanupPromise === passPromise) cleanupPromise = void 0;
3013
3250
  });
3251
+ cleanupPromise = passPromise;
3014
3252
  return cleanupPromise;
3015
3253
  }
3016
3254
  let inspectorOpened = false;
@@ -3023,21 +3261,86 @@ function classifyBrokenPipe(err) {
3023
3261
  if (err.syscall === "send") return "ipc-send";
3024
3262
  if (err.syscall === "write") return "stdio-write";
3025
3263
  }
3026
- const EXPECTED_CLEANUP = Symbol.for("omp.expectedCleanupError");
3264
+ /** Whether an EPIPE came from an IPC `send()` to an optional worker. */
3265
+ function isIpcSendEpipe(err) {
3266
+ return classifyBrokenPipe(err) === "ipc-send";
3267
+ }
3027
3268
  /**
3028
- * Whether `reason` (or any error in its `cause` chain) was marked via
3029
- * {@link markExpectedCleanupError}. Walks the chain because the unhandled
3030
- * reason is often a wrapper (`AbortError`) with the marked abort reason as
3031
- * its `cause`.
3269
+ * Whether an uncaught error is Bun's asynchronous `ERR_SOCKET_CLOSED` thrown
3270
+ * from inside `node:net` internals with no application frames on the stack.
3271
+ *
3272
+ * Bun ≥1.4 can fire the close callback of an already-closed `node:net` socket
3273
+ * on a fresh stack; the throw bypasses every callsite try/catch and surfaces
3274
+ * here as a process-level uncaughtException. Closing an already-closed socket
3275
+ * is inherently a no-op — the socket owner's own `error`/`close` handlers
3276
+ * still drive recovery — so tearing the session down for it is pure loss.
3277
+ * Only frameless internal stacks qualify: an `ERR_SOCKET_CLOSED` raised
3278
+ * through application code keeps the fatal path.
3032
3279
  */
3033
- function isExpectedCleanupError(reason) {
3280
+ function isInternalSocketClosedError(err) {
3281
+ if (!(err instanceof Error) || !("code" in err) || err.code !== "ERR_SOCKET_CLOSED") return false;
3282
+ const frames = (err.stack ?? "").split("\n").slice(1);
3283
+ if (frames.length === 0) return false;
3284
+ let hasNetFrame = false;
3285
+ return frames.every((frame) => {
3286
+ const trimmed = frame.trim();
3287
+ if (trimmed === "" || trimmed === "at unknown" || trimmed === "at native") return true;
3288
+ if (!/\(node:[^)]*\)$/.test(trimmed) && !trimmed.startsWith("at node:")) return false;
3289
+ hasNetFrame ||= trimmed.includes("node:net:");
3290
+ return true;
3291
+ }) && hasNetFrame;
3292
+ }
3293
+ /**
3294
+ * Detect Bun's advanced-serialization (structured-clone) IPC decode failure.
3295
+ *
3296
+ * When a worker subprocess spawned with `serialization: "advanced"` sends a
3297
+ * malformed or truncated frame, Bun raises the decode failure as a
3298
+ * process-level `uncaughtException` in the *parent* rather than routing it to
3299
+ * the channel's `ipc()` callback (oven-sh/bun#37287). The error is a bare
3300
+ * `TypeError: Unable to deserialize data.` whose only own property is `message`
3301
+ * — it carries no `code`, no `syscall`, and no `stack`. Matching all four traits
3302
+ * keeps unrelated application `TypeError`s (which always carry a populated
3303
+ * multi-frame stack) on the fatal path, so a genuine bug is never silently
3304
+ * swallowed.
3305
+ *
3306
+ * Every advanced-serialization channel in this process is an optional worker
3307
+ * subsystem (TTS, STT, tiny-title, mnemopi embeddings, JS eval), so one
3308
+ * worker's bad frame must fault only that worker — via its own `onExit`/error
3309
+ * path — never tear down the whole session. Callers log-and-continue instead of
3310
+ * taking the fatal path. Mirrors {@link classifyBrokenPipe} for the send side
3311
+ * (#2997, #9158).
3312
+ */
3313
+ function isWorkerIpcDeserializeError(err) {
3314
+ return err instanceof TypeError && err.message === "Unable to deserialize data." && !err.stack && !("code" in err) && !("syscall" in err);
3315
+ }
3316
+ /** Recycle callbacks for the active advanced-serialization worker IPC channels. */
3317
+ const workerIpcFaultHandlers = /* @__PURE__ */ new Set();
3318
+ /** Invoke every registered worker IPC fault handler, isolating handler throws. */
3319
+ function faultWorkerIpcChannels(err) {
3320
+ for (const handler of workerIpcFaultHandlers) try {
3321
+ handler(err);
3322
+ } catch (handlerErr) {
3323
+ warn("Worker IPC fault handler threw", { err: handlerErr });
3324
+ }
3325
+ }
3326
+ const EXPECTED_CLEANUP = Symbol.for("omp.expectedCleanupError");
3327
+ function hasExpectedCleanupMarker(reason) {
3034
3328
  let current = reason;
3035
3329
  for (let depth = 0; depth < 8 && current !== null && typeof current === "object"; depth++) {
3036
- if (current[EXPECTED_CLEANUP] === true) return true;
3037
- current = current.cause;
3330
+ if (Reflect.get(current, EXPECTED_CLEANUP) === true) return true;
3331
+ current = Reflect.get(current, "cause");
3038
3332
  }
3039
3333
  return false;
3040
3334
  }
3335
+ /**
3336
+ * Whether `reason` (or any object in its bounded `cause` chain) was explicitly
3337
+ * marked via {@link markExpectedCleanupError}. Runtime error names and codes
3338
+ * are intentionally insufficient: unmarked `AbortError` and socket failures
3339
+ * can originate from application code and must remain fatal when unhandled.
3340
+ */
3341
+ function isExpectedCleanupError(reason) {
3342
+ return hasExpectedCleanupMarker(reason);
3343
+ }
3041
3344
  /** Interceptors consulted by the global `unhandledRejection` handler before the fatal path. */
3042
3345
  const rejectionInterceptors = /* @__PURE__ */ new Set();
3043
3346
  function escapeFatalHintText(value) {
@@ -3064,15 +3367,16 @@ function formatFatalError(label, err) {
3064
3367
  const stackLines = (err.stack || "").split("\n").slice(1);
3065
3368
  return `\n[${label}] ${name}: ${message}${stackLines.length > 0 ? `\n${stackLines.join("\n")}` : ""}\n`;
3066
3369
  }
3067
- async function exitAfterFatal(label, logMessage, err, reason) {
3370
+ async function exitAfterFatal(output, logMessage, err, reason) {
3068
3371
  const forcedExit = setTimeout(() => exitProcess(1), CLEANUP_DEADLINE_MS);
3069
3372
  try {
3373
+ const cleanup = runCleanup(reason);
3070
3374
  restoreTerminalStderr();
3071
3375
  try {
3072
- fs.writeSync(2, `${formatFatalError(label, err)}${formatFatalRecoveryHints()}`);
3376
+ fs.writeSync(2, output);
3073
3377
  } catch {}
3074
3378
  error(logMessage, { err });
3075
- await runCleanup(reason);
3379
+ await cleanup;
3076
3380
  } finally {
3077
3381
  clearTimeout(forcedExit);
3078
3382
  exitProcess(1);
@@ -3087,12 +3391,26 @@ if (isMainThread) process.on("SIGINT", async () => {
3087
3391
  inspector.open(void 0, void 0, false);
3088
3392
  const url = inspector.url();
3089
3393
  process.stderr.write(`Inspector opened: ${url}\n`);
3090
- }).on("uncaughtException", async (err) => {
3091
- if (isExpectedCleanupError(err)) {
3092
- warn("Ignoring expected cleanup exception", { err });
3394
+ }).on("uncaughtException", async (thrown) => {
3395
+ if (hasExpectedCleanupMarker(thrown)) {
3396
+ warn("Ignoring expected cleanup exception", { err: thrown });
3397
+ return;
3398
+ }
3399
+ const err = thrown instanceof Error ? thrown : new Error(String(thrown));
3400
+ if (isIpcSendEpipe(err)) {
3401
+ warn("Ignoring EPIPE from worker IPC send; optional subsystem will self-recover", { err });
3402
+ return;
3403
+ }
3404
+ if (isWorkerIpcDeserializeError(err)) {
3405
+ warn("Malformed worker IPC frame; faulting active worker subsystems", { err });
3406
+ faultWorkerIpcChannels(err);
3407
+ return;
3408
+ }
3409
+ if (isInternalSocketClosedError(err)) {
3410
+ warn("Ignoring async ERR_SOCKET_CLOSED from node:net internals; socket owner recovers itself", { err });
3093
3411
  return;
3094
3412
  }
3095
- await exitAfterFatal("Uncaught Exception", "Uncaught exception", err, "uncaught_exception");
3413
+ await exitAfterFatal(`${formatFatalError("Uncaught Exception", err)}${formatFatalRecoveryHints()}`, "Uncaught exception", err, "uncaught_exception");
3096
3414
  }).on("unhandledRejection", async (reason) => {
3097
3415
  const err = reason instanceof Error ? reason : new Error(String(reason));
3098
3416
  if (classifyBrokenPipe(err) === "ipc-send") {
@@ -3108,7 +3426,7 @@ if (isMainThread) process.on("SIGINT", async () => {
3108
3426
  } catch (interceptorErr) {
3109
3427
  warn("Unhandled-rejection interceptor threw; continuing with fatal path", { err: interceptorErr });
3110
3428
  }
3111
- await exitAfterFatal("Unhandled Rejection", "Unhandled rejection", err, "unhandled_rejection");
3429
+ await exitAfterFatal(`${formatFatalError("Unhandled Rejection", err)}${formatFatalRecoveryHints()}`, "Unhandled rejection", err, "unhandled_rejection");
3112
3430
  }).on("exit", async () => {
3113
3431
  runCleanup("exit");
3114
3432
  }).on("SIGTERM", async () => {
@@ -3121,8 +3439,72 @@ if (isMainThread) process.on("SIGINT", async () => {
3121
3439
  else process.on("exit", () => {
3122
3440
  runCleanup("exit");
3123
3441
  });
3442
+ /**
3443
+ * Registers a cleanup callback for shutdown, signals, fatal errors, and
3444
+ * repeatable manual cleanup passes.
3445
+ *
3446
+ * Registrations persist across keep-alive {@link cleanup} passes and run at
3447
+ * most once per pass. Set `exitOnly` for resources the continuing process still
3448
+ * holds (open databases, cached handles): keep-alive passes skip the callback
3449
+ * without consuming its registration, while the eventual real exit runs it.
3450
+ *
3451
+ * A callback registered during a running keep-alive pass joins future passes;
3452
+ * normal callbacks also run immediately for the current pass. Registrations
3453
+ * made during a real exit run immediately.
3454
+ *
3455
+ * Returns a function that permanently cancels the registration.
3456
+ */
3457
+ function register(id, callback, options = {}) {
3458
+ const registration = {
3459
+ id,
3460
+ callback,
3461
+ exitOnly: options.exitOnly ?? false,
3462
+ cancelled: false,
3463
+ lastPass: 0
3464
+ };
3465
+ const cancel = () => {
3466
+ registration.cancelled = true;
3467
+ const index = callbackList.indexOf(registration);
3468
+ if (index >= 0) callbackList.splice(index, 1);
3469
+ };
3470
+ const invokeLate = (reason, keepAlive) => {
3471
+ try {
3472
+ const pending = invokeCleanup(registration, reason, keepAlive, cleanupPass);
3473
+ if (!pending) return;
3474
+ const tracked = pending.catch((error$2) => {
3475
+ const err = error$2 instanceof Error ? error$2 : new Error(String(error$2));
3476
+ error("Cleanup callback failed", {
3477
+ err,
3478
+ id,
3479
+ stack: err.stack
3480
+ });
3481
+ });
3482
+ activeLatePromises?.push(tracked);
3483
+ } catch (error$1) {
3484
+ const err = error$1 instanceof Error ? error$1 : new Error(String(error$1));
3485
+ error("Cleanup callback failed", {
3486
+ err,
3487
+ id,
3488
+ stack: err.stack
3489
+ });
3490
+ }
3491
+ };
3492
+ if (cleanupStage === "idle") {
3493
+ callbackList.push(registration);
3494
+ return cancel;
3495
+ }
3496
+ const reason = activeCleanupReason ?? "manual";
3497
+ if (cleanupStage === "running" && activeCleanupKeepAlive) {
3498
+ callbackList.push(registration);
3499
+ if (!registration.exitOnly) invokeLate(reason, true);
3500
+ return cancel;
3501
+ }
3502
+ debug("Cleanup already started; running late callback once", { id });
3503
+ invokeLate(reason, false);
3504
+ return cancel;
3505
+ }
3124
3506
  //#endregion
3125
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/which.ts
3507
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/which.ts
3126
3508
  const XCODE_BINS = /* @__PURE__ */ new Set([
3127
3509
  "clang",
3128
3510
  "clang++",
@@ -3217,7 +3599,7 @@ function darwinWhich(command, options) {
3217
3599
  if (isXcodeBin(command)) return getMacosToolPaths().get(command) ?? null;
3218
3600
  return null;
3219
3601
  }
3220
- const whichFresh = os.platform() === "darwin" ? darwinWhich : Bun.which;
3602
+ const whichFresh = os.platform() === "darwin" ? darwinWhich : (command, options) => Bun.which(command, options);
3221
3603
  function cacheKey$1(command, options) {
3222
3604
  if (!options) return command;
3223
3605
  if (!options.cwd && !options.PATH) return command;
@@ -3252,7 +3634,7 @@ function $which(command, options) {
3252
3634
  return result;
3253
3635
  }
3254
3636
  //#endregion
3255
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/template.ts
3637
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/template.ts
3256
3638
  /** A string that bypasses HTML escaping when interpolated. */
3257
3639
  var SafeString = class {
3258
3640
  #value;
@@ -3703,7 +4085,7 @@ function compile$1(source, options = {}) {
3703
4085
  root,
3704
4086
  data: {
3705
4087
  root,
3706
- ...runtime.data ?? {}
4088
+ ...runtime.data
3707
4089
  }
3708
4090
  };
3709
4091
  return renderNodes(nodes, frame, {
@@ -3741,7 +4123,7 @@ var TemplateEngine = class {
3741
4123
  root,
3742
4124
  data: {
3743
4125
  root,
3744
- ...runtime.data ?? {}
4126
+ ...runtime.data
3745
4127
  }
3746
4128
  };
3747
4129
  return renderNodes(nodes, frame, {
@@ -3754,7 +4136,7 @@ var TemplateEngine = class {
3754
4136
  }
3755
4137
  };
3756
4138
  //#endregion
3757
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/prompt.ts
4139
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/prompt.ts
3758
4140
  const OPENING_XML = /^<([a-z_-]+)(?:\s+[^>]*)?>$/;
3759
4141
  /**
3760
4142
  * Closing XML tag matcher, manual equivalent of `/^<\/([a-z_-]+)>$/` — avoids a
@@ -4197,7 +4579,7 @@ function _usingCtx() {
4197
4579
  };
4198
4580
  }
4199
4581
  //#endregion
4200
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/ptree.ts
4582
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/ptree.ts
4201
4583
  /**
4202
4584
  * Process tree management utilities for Bun subprocesses.
4203
4585
  *
@@ -4206,6 +4588,80 @@ function _usingCtx() {
4206
4588
  * - Cross-platform tree kill for process groups (Windows taskkill, Unix -pid).
4207
4589
  * - Convenience helpers: captureText / execText, AbortSignal, timeouts.
4208
4590
  */
4591
+ const LINUX_SUBREAPER_COMMAND_ENV = "OMP_PTREE_SUBREAPER_COMMAND";
4592
+ const LINUX_SUBREAPER_BUN_BE_BUN_ENV = "OMP_PTREE_SUBREAPER_BUN_BE_BUN";
4593
+ /**
4594
+ * Build the Linux child-subreaper entrypoint.
4595
+ *
4596
+ * @internal Exported so tests can force a missing first libc soname and verify
4597
+ * the loader continues to the next candidate.
4598
+ */
4599
+ function createLinuxSubreaperScript(libcCandidates = ["libc.so.6", "libc.so"]) {
4600
+ return `
4601
+ import { dlopen, FFIType } from "bun:ffi";
4602
+
4603
+ let libc;
4604
+ for (const soname of ${JSON.stringify(libcCandidates)}) {
4605
+ try {
4606
+ libc = dlopen(soname, {
4607
+ prctl: {
4608
+ args: [FFIType.i32, FFIType.u64, FFIType.u64, FFIType.u64, FFIType.u64],
4609
+ returns: FFIType.i32,
4610
+ },
4611
+ waitpid: {
4612
+ args: [FFIType.i32, FFIType.ptr, FFIType.i32],
4613
+ returns: FFIType.i32,
4614
+ },
4615
+ });
4616
+ break;
4617
+ } catch {}
4618
+ }
4619
+ if (!libc) throw new Error("failed to load libc for Linux child supervision");
4620
+
4621
+ if (libc.symbols.prctl(36, 1, 0, 0, 0) !== 0) {
4622
+ throw new Error("failed to become a Linux child subreaper");
4623
+ }
4624
+
4625
+ const commandJson = Bun.env.${LINUX_SUBREAPER_COMMAND_ENV};
4626
+ if (!commandJson) throw new Error("missing supervised command");
4627
+ const callerBunBeBun = Bun.env.${LINUX_SUBREAPER_BUN_BE_BUN_ENV};
4628
+ delete Bun.env.${LINUX_SUBREAPER_COMMAND_ENV};
4629
+ delete Bun.env.${LINUX_SUBREAPER_BUN_BE_BUN_ENV};
4630
+ if (callerBunBeBun === undefined) delete Bun.env.BUN_BE_BUN;
4631
+ else Bun.env.BUN_BE_BUN = callerBunBeBun;
4632
+ const command = JSON.parse(commandJson);
4633
+ const child = Bun.spawn(command, {
4634
+ stdin: "inherit",
4635
+ stdout: "pipe",
4636
+ stderr: "pipe",
4637
+ windowsHide: true,
4638
+ env: Bun.env,
4639
+ });
4640
+
4641
+ async function relay(stream, destination) {
4642
+ const writer = destination.writer();
4643
+ for await (const chunk of stream) writer.write(chunk);
4644
+ await writer.flush();
4645
+ }
4646
+
4647
+ function hasLiveChildren() {
4648
+ let childPid;
4649
+ do {
4650
+ childPid = libc.symbols.waitpid(-1, null, 1);
4651
+ } while (childPid > 0);
4652
+ return childPid === 0;
4653
+ }
4654
+
4655
+ const [exitCode] = await Promise.all([
4656
+ child.exited,
4657
+ relay(child.stdout, Bun.stdout),
4658
+ relay(child.stderr, Bun.stderr),
4659
+ ]);
4660
+ while (hasLiveChildren()) await Bun.sleep(10);
4661
+ process.exit(exitCode ?? 1);
4662
+ `;
4663
+ }
4664
+ const LINUX_SUBREAPER_SCRIPT = createLinuxSubreaperScript();
4209
4665
  /**
4210
4666
  * Base for all exceptions representing child process nonzero exit, killed, or
4211
4667
  * cancellation.
@@ -4266,10 +4722,21 @@ var ChildProcess = class {
4266
4722
  #exitReasonPending;
4267
4723
  #stderrDone;
4268
4724
  #exited;
4725
+ #openPipeReaders = 1;
4726
+ #drainCutoff;
4727
+ #resolveDrainCutoff;
4728
+ #timeoutTimer;
4269
4729
  #stderrStream;
4270
- constructor(proc, exposeStderr, retainFullStderr = exposeStderr) {
4730
+ #terminating;
4731
+ #terminateGroup;
4732
+ #hardKillTree;
4733
+ #windowsRootProcess;
4734
+ constructor(proc, exposeStderr, retainFullStderr = exposeStderr, terminateGroup = false, hardKillTree = false) {
4271
4735
  this.proc = proc;
4272
4736
  this.exposeStderr = exposeStderr;
4737
+ this.#terminateGroup = terminateGroup;
4738
+ this.#hardKillTree = hardKillTree;
4739
+ this.#windowsRootProcess = process.platform === "win32" ? Process.fromPid(proc.pid) ?? void 0 : void 0;
4273
4740
  if (retainFullStderr) this.#stderrChunks = [];
4274
4741
  const dec = new TextDecoder();
4275
4742
  const trim = () => {
@@ -4281,19 +4748,34 @@ var ChildProcess = class {
4281
4748
  this.#stderrStream = teeStream;
4282
4749
  stderrStream = drainStream;
4283
4750
  }
4751
+ const { promise, resolve, reject } = Promise.withResolvers();
4752
+ this.#exited = promise;
4753
+ const drainCutoff = Promise.withResolvers();
4754
+ this.#drainCutoff = drainCutoff.promise;
4755
+ this.#resolveDrainCutoff = drainCutoff.resolve;
4756
+ const pipeCutoff = this.#drainCutoff;
4284
4757
  this.#stderrDone = (async () => {
4758
+ const reader = stderrStream.getReader();
4285
4759
  try {
4286
- for await (const chunk of stderrStream) {
4287
- this.#stderrChunks?.push(chunk);
4288
- this.#stderrTail += dec.decode(chunk, { stream: true });
4760
+ for (;;) {
4761
+ const chunk = await Promise.race([reader.read().then((r) => ({
4762
+ cutoff: false,
4763
+ r
4764
+ })), pipeCutoff.then(() => ({ cutoff: true }))]);
4765
+ if (chunk.cutoff) {
4766
+ await reader.cancel().catch(() => {});
4767
+ break;
4768
+ }
4769
+ if (chunk.r.done) break;
4770
+ this.#stderrChunks?.push(chunk.r.value);
4771
+ this.#stderrTail += dec.decode(chunk.r.value, { stream: true });
4289
4772
  trim();
4290
4773
  }
4291
4774
  } catch {}
4775
+ this.#openPipeReaders--;
4292
4776
  this.#stderrTail += dec.decode();
4293
4777
  trim();
4294
4778
  })();
4295
- const { promise, resolve, reject } = Promise.withResolvers();
4296
- this.#exited = promise;
4297
4779
  proc.exited.catch(() => null).then(async (exitCode) => {
4298
4780
  if (this.#exitReasonPending) {
4299
4781
  this.#exitReason = this.#exitReasonPending;
@@ -4305,6 +4787,11 @@ var ChildProcess = class {
4305
4787
  return;
4306
4788
  }
4307
4789
  await this.#stderrDone;
4790
+ if (this.#exitReasonPending) {
4791
+ this.#exitReason = this.#exitReasonPending;
4792
+ reject(this.#exitReasonPending);
4793
+ return;
4794
+ }
4308
4795
  if (exitCode !== null) {
4309
4796
  this.#exitReason = new NonZeroExitError(exitCode, this.#stderrTail);
4310
4797
  resolve(exitCode);
@@ -4357,36 +4844,131 @@ var ChildProcess = class {
4357
4844
  return this;
4358
4845
  }
4359
4846
  kill(reason, gracefulMs) {
4360
- if (reason && !this.#exitReasonPending) this.#exitReasonPending = reason;
4361
- if (!this.proc.killed) Process.fromPid(this.proc.pid)?.terminate(gracefulMs === void 0 ? void 0 : { gracefulMs })?.catch((e) => void 0);
4847
+ if (reason && !this.#exitReasonPending) {
4848
+ this.#exitReasonPending = reason;
4849
+ if (this.proc.exitCode !== null) this.#exitReason = reason;
4850
+ }
4851
+ if (gracefulMs !== void 0 && gracefulMs < 0 && this.#hardKillTree && this.proc.exitCode === null) {
4852
+ const root = Process.fromPid(this.proc.pid);
4853
+ if (root) {
4854
+ root.killTree(9);
4855
+ this.#terminating = Promise.resolve();
4856
+ return;
4857
+ }
4858
+ }
4859
+ if (this.proc.exitCode !== null && this.#terminateGroup && this.#openPipeReaders > 0 && process.platform !== "win32") {
4860
+ try {
4861
+ process.kill(-this.proc.pid, "SIGKILL");
4862
+ } catch {}
4863
+ this.#terminating = Promise.resolve();
4864
+ return;
4865
+ }
4866
+ if (this.proc.exitCode !== null && this.#windowsRootProcess && this.#openPipeReaders > 0) {
4867
+ this.#windowsRootProcess.killTree();
4868
+ this.#terminating = Promise.resolve();
4869
+ return;
4870
+ }
4871
+ if (!this.proc.killed) {
4872
+ const options = gracefulMs === void 0 ? this.#terminateGroup ? { group: true } : void 0 : {
4873
+ gracefulMs,
4874
+ group: this.#terminateGroup
4875
+ };
4876
+ this.#terminating = (this.#windowsRootProcess ?? Process.fromPid(this.proc.pid))?.terminate(options)?.catch((e) => void 0);
4877
+ }
4878
+ }
4879
+ async #throwIfAborted() {
4880
+ const exitReason = this.exitReason;
4881
+ if (!exitReason?.aborted) return;
4882
+ if (this.#terminating) await this.#terminating;
4883
+ throw exitReason;
4362
4884
  }
4363
4885
  async text() {
4364
- const p = new Response(this.stdout).text();
4886
+ const p = this.#readStream(this.proc.stdout);
4365
4887
  if (this.#nothrow) return p;
4366
4888
  const [text] = await Promise.all([p, this.exitedCleanly]);
4889
+ await this.#throwIfAborted();
4367
4890
  return text;
4368
4891
  }
4369
- async blob() {
4370
- const p = new Response(this.stdout).blob();
4892
+ /**
4893
+ * Read a pipe fully, stopping early only at an explicit command deadline.
4894
+ */
4895
+ async #readStream(stream) {
4896
+ this.#openPipeReaders++;
4897
+ const reader = stream.getReader();
4898
+ const dec = new TextDecoder();
4899
+ let out = "";
4900
+ try {
4901
+ for (;;) {
4902
+ const chunk = await Promise.race([reader.read().then((r) => ({
4903
+ cutoff: false,
4904
+ r
4905
+ })), this.#drainCutoff.then(() => ({ cutoff: true }))]);
4906
+ if (chunk.cutoff) {
4907
+ await reader.cancel().catch(() => {});
4908
+ break;
4909
+ }
4910
+ if (chunk.r.done) break;
4911
+ out += dec.decode(chunk.r.value, { stream: true });
4912
+ }
4913
+ } catch {}
4914
+ this.#openPipeReaders--;
4915
+ return out + dec.decode();
4916
+ }
4917
+ async #readBytes() {
4918
+ const reader = this.proc.stdout.getReader();
4919
+ this.#openPipeReaders++;
4920
+ const chunks = [];
4921
+ let length = 0;
4922
+ try {
4923
+ for (;;) {
4924
+ const chunk = await Promise.race([reader.read().then((r) => ({
4925
+ cutoff: false,
4926
+ r
4927
+ })), this.#drainCutoff.then(() => ({ cutoff: true }))]);
4928
+ if (chunk.cutoff) {
4929
+ await reader.cancel().catch(() => {});
4930
+ break;
4931
+ }
4932
+ if (chunk.r.done) break;
4933
+ chunks.push(chunk.r.value);
4934
+ length += chunk.r.value.byteLength;
4935
+ }
4936
+ } catch {} finally {
4937
+ this.#openPipeReaders--;
4938
+ reader.releaseLock();
4939
+ }
4940
+ const bytes = new Uint8Array(length);
4941
+ let offset = 0;
4942
+ for (const chunk of chunks) {
4943
+ bytes.set(chunk, offset);
4944
+ offset += chunk.byteLength;
4945
+ }
4946
+ return bytes;
4947
+ }
4948
+ async #readOutputBytes(waitForCleanExit = false) {
4949
+ const p = this.#readBytes();
4371
4950
  if (this.#nothrow) return p;
4372
- const [blob] = await Promise.all([p, this.exitedCleanly]);
4373
- return blob;
4951
+ const bytes = waitForCleanExit ? (await Promise.all([p, this.exitedCleanly]))[0] : await p;
4952
+ await this.#throwIfAborted();
4953
+ return bytes;
4954
+ }
4955
+ async blob() {
4956
+ return new Blob([await this.#readOutputBytes(true)]);
4374
4957
  }
4375
4958
  async json() {
4376
- return new Response(this.stdout).json();
4959
+ return JSON.parse(new TextDecoder().decode(await this.#readOutputBytes()));
4377
4960
  }
4378
4961
  async arrayBuffer() {
4379
- return new Response(this.stdout).arrayBuffer();
4962
+ return (await this.#readOutputBytes()).buffer;
4380
4963
  }
4381
4964
  async bytes() {
4382
- const body = await new Response(this.stdout).bytes();
4383
- return body instanceof Uint8Array ? body : new Uint8Array(body);
4965
+ return this.#readOutputBytes();
4384
4966
  }
4385
4967
  async wait(opts) {
4386
4968
  const { allowNonZero = false, allowAbort = false, stderr: stderrMode = "buffer" } = opts ?? {};
4387
4969
  const stderrChunks = this.#stderrChunks;
4388
4970
  if (stderrMode === "full" && !stderrChunks) throw new Error("Full stderr capture must be requested when spawning the process (pass stderr: \"full\")");
4389
- const stdoutP = new Response(this.stdout).text();
4971
+ const stdoutP = this.#readStream(this.proc.stdout);
4390
4972
  const stderrP = stderrMode === "full" && stderrChunks ? this.#stderrDone.then(() => new TextDecoder().decode(Buffer.concat(stderrChunks))) : this.#stderrDone.then(() => this.#stderrTail);
4391
4973
  const [stdout, stderr] = await Promise.all([stdoutP, stderrP]);
4392
4974
  let exitError;
@@ -4396,8 +4978,10 @@ var ChildProcess = class {
4396
4978
  if (err instanceof Exception) exitError = err;
4397
4979
  else throw err;
4398
4980
  }
4981
+ this.#clearTimeout();
4399
4982
  if (!exitError) exitError = this.exitReason;
4400
4983
  if (!exitError && this.exitCode !== null && this.exitCode !== 0) exitError = new NonZeroExitError(this.exitCode, this.#stderrTail);
4984
+ if (exitError?.aborted && this.#terminating) await this.#terminating;
4401
4985
  const exitCode = this.exitCode ?? (exitError && !exitError.aborted ? exitError.exitCode : null);
4402
4986
  const ok = exitCode === 0;
4403
4987
  if (exitError) {
@@ -4417,12 +5001,20 @@ var ChildProcess = class {
4417
5001
  signal.addEventListener("abort", onAbort, { once: true });
4418
5002
  this.#exited.catch(() => {}).finally(() => signal.removeEventListener("abort", onAbort));
4419
5003
  }
5004
+ #clearTimeout() {
5005
+ if (!this.#timeoutTimer) return;
5006
+ clearTimeout(this.#timeoutTimer);
5007
+ this.#timeoutTimer = void 0;
5008
+ }
4420
5009
  attachTimeout(ms) {
4421
5010
  if (ms <= 0 || this.proc.killed) return;
4422
5011
  this.#exited.catch(() => {});
4423
- Promise.race([Bun.sleep(ms).then(() => true), this.proc.exited.then(() => false, () => false)]).then((timedOut) => {
4424
- if (timedOut) this.kill(new TimeoutError(ms, this.#stderrTail));
4425
- });
5012
+ const timer = setTimeout(() => {
5013
+ if (this.proc.exitCode === null || this.#openPipeReaders > 0 && (this.#terminateGroup || this.#windowsRootProcess)) this.kill(new TimeoutError(ms, this.#stderrTail), -1);
5014
+ this.#resolveDrainCutoff();
5015
+ }, ms);
5016
+ timer.unref?.();
5017
+ this.#timeoutTimer = timer;
4426
5018
  }
4427
5019
  [Symbol.dispose]() {
4428
5020
  if (this.proc.exitCode !== null) return;
@@ -4430,14 +5022,27 @@ var ChildProcess = class {
4430
5022
  }
4431
5023
  };
4432
5024
  function spawnInternal(cmd, opts, retainFullStderr) {
4433
- const { timeout = -1, signal, stderr, ...rest } = opts ?? {};
4434
- const cp = new ChildProcess(Bun.spawn(cmd, {
5025
+ const { timeout = -1, signal, stderr, detached, subreaper = false, ...rest } = opts ?? {};
5026
+ const useSubreaper = subreaper && process.platform === "linux";
5027
+ const commandEnv = rest.env ?? Bun.env;
5028
+ const cp = new ChildProcess(Bun.spawn(useSubreaper ? [
5029
+ process.execPath,
5030
+ "-e",
5031
+ LINUX_SUBREAPER_SCRIPT
5032
+ ] : cmd, {
4435
5033
  stdin: "ignore",
4436
5034
  stdout: "pipe",
4437
5035
  stderr: "pipe",
4438
5036
  windowsHide: true,
4439
- ...rest
4440
- }), stderr === "full", retainFullStderr);
5037
+ detached,
5038
+ ...rest,
5039
+ env: useSubreaper ? {
5040
+ ...commandEnv,
5041
+ BUN_BE_BUN: "1",
5042
+ [LINUX_SUBREAPER_COMMAND_ENV]: JSON.stringify(cmd),
5043
+ [LINUX_SUBREAPER_BUN_BE_BUN_ENV]: commandEnv.BUN_BE_BUN
5044
+ } : rest.env
5045
+ }), stderr === "full", retainFullStderr, detached === true, useSubreaper);
4441
5046
  if (signal) cp.attachSignal(signal);
4442
5047
  if (timeout > 0) cp.attachTimeout(timeout);
4443
5048
  return cp;
@@ -4486,7 +5091,7 @@ function combineSignals(...signals) {
4486
5091
  }
4487
5092
  }
4488
5093
  //#endregion
4489
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/snowflake.ts
5094
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/snowflake.ts
4490
5095
  function randu32() {
4491
5096
  return crypto.getRandomValues(/* @__PURE__ */ new Uint32Array(1))[0];
4492
5097
  }
@@ -4565,29 +5170,9 @@ let Snowflake;
4565
5170
  _Snowflake.getDate = getDate;
4566
5171
  })(Snowflake || (Snowflake = {}));
4567
5172
  //#endregion
4568
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/stream.ts
5173
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/stream.ts
4569
5174
  const trailingEvents = /* @__PURE__ */ new WeakSet();
4570
5175
  const LF = 10;
4571
- function parseJsonlChunkCompat(input, beg, end) {
4572
- if (typeof input === "string") {
4573
- const { values, error, read, done } = Bun.JSONL.parseChunk(input);
4574
- return {
4575
- values,
4576
- error,
4577
- read,
4578
- done
4579
- };
4580
- }
4581
- const start = beg ?? 0;
4582
- const stop = end ?? input.length;
4583
- const { values, error, read, done } = Bun.JSONL.parseChunk(input, start, stop);
4584
- return {
4585
- values,
4586
- error,
4587
- read,
4588
- done
4589
- };
4590
- }
4591
5176
  async function* readJsonl(stream, signal) {
4592
5177
  const buffer = new ConcatSink();
4593
5178
  const source = abortableSource(stream, signal);
@@ -4597,7 +5182,7 @@ async function* readJsonl(stream, signal) {
4597
5182
  const tail = buffer.flush();
4598
5183
  if (tail) {
4599
5184
  buffer.clear();
4600
- const { values, error, done } = parseJsonlChunkCompat(tail, 0, tail.length);
5185
+ const { values, error, done } = Bun.JSONL.parseChunk(tail, 0, tail.length);
4601
5186
  if (values.length > 0) yield* values;
4602
5187
  if (error) throw error;
4603
5188
  if (!done) throw new Error("JSONL stream ended unexpectedly");
@@ -4687,8 +5272,14 @@ var ConcatSink = class {
4687
5272
  return text;
4688
5273
  }
4689
5274
  *pullJSONL(chunk, beg, end) {
5275
+ const newline = chunk.indexOf(LF, beg);
5276
+ if (newline === -1 || newline >= end) {
5277
+ if (this.isEmpty) this.reset(chunk.subarray(beg, end));
5278
+ else this.append(chunk.subarray(beg, end));
5279
+ return;
5280
+ }
4690
5281
  if (this.isEmpty) {
4691
- const { values, error, read, done } = parseJsonlChunkCompat(chunk, beg, end);
5282
+ const { values, error, read, done } = Bun.JSONL.parseChunk(chunk, beg, end);
4692
5283
  if (values.length > 0) yield* values;
4693
5284
  if (error) throw error;
4694
5285
  if (done) return;
@@ -4700,7 +5291,7 @@ var ConcatSink = class {
4700
5291
  const space = this.#ensureCapacity(total);
4701
5292
  space.set(chunk.subarray(beg, end), offset);
4702
5293
  this.#length = total;
4703
- const { values, error, read, done } = parseJsonlChunkCompat(space.subarray(0, total), 0, total);
5294
+ const { values, error, read, done } = Bun.JSONL.parseChunk(space, 0, total);
4704
5295
  if (values.length > 0) yield* values;
4705
5296
  if (error) throw error;
4706
5297
  if (done) {
@@ -4859,7 +5450,7 @@ async function* readSseEvents(stream, signal) {
4859
5450
  }
4860
5451
  }
4861
5452
  //#endregion
4862
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/temp.ts
5453
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/temp.ts
4863
5454
  var TempDir = class TempDir {
4864
5455
  #path;
4865
5456
  constructor(path) {
@@ -4956,7 +5547,7 @@ function sleepSync(ms) {
4956
5547
  Atomics.wait(kSleepBuffer, 0, 0, ms);
4957
5548
  }
4958
5549
  //#endregion
4959
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/tls-fetch.ts
5550
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/tls-fetch.ts
4960
5551
  /**
4961
5552
  * `NODE_EXTRA_CA_CERTS` shim for Bun's `fetch`.
4962
5553
  *
@@ -5065,7 +5656,7 @@ function wrapFetchForExtraCa(fetchImpl) {
5065
5656
  }, fetchImpl.preconnect ? { preconnect: fetchImpl.preconnect } : {}, { [EXTRA_CA_FETCH_MARKER]: true });
5066
5657
  }
5067
5658
  //#endregion
5068
- //#region node_modules/.pnpm/@oh-my-pi+pi-utils@17.3.5/node_modules/@oh-my-pi/pi-utils/src/index.ts
5659
+ //#region node_modules/.pnpm/@oh-my-pi+pi-utils@18.1.17/node_modules/@oh-my-pi/pi-utils/src/index.ts
5069
5660
  function isPlainObject(val) {
5070
5661
  return Object.getPrototypeOf(val) === Object.prototype || Array.isArray(val);
5071
5662
  }
@@ -5077,4 +5668,4 @@ function structuredCloneJSON(value) {
5077
5668
  return JSON.parse(JSON.stringify(value));
5078
5669
  }
5079
5670
  //#endregion
5080
- export { peekFile as $, formatAge as A, USER_AGENT as B, error as C, Cookie as D, $ as E, $envpos as F, getDocsRsCacheDir as G, getAgentDir as H, $flag as I, getLogsDir as J, getDocumentConversionCacheDir as K, $pickenv as L, formatCount as M, formatNumber as N, CookieMap as O, $env as P, isProbablyBinary as Q, isBunTestRuntime as R, debug as S, warn as T, getAutoQaDbPath as U, VERSION as V, getDebugLogPath as W, getRemoteDir as X, getPuppeteerDir as Y, getToolsDir as Z, parseStreamingJsonThrottled as _, readSseEvents as a, isEnotdir as b, combineSignals as c, $which as d, once as et, parseImageMetadata as f, parseStreamingJson as g, parseJsonWithRepair as h, readJsonl as i, formatBytes as j, installBunShim as k, exec as l, classifyJsonPrefix as m, wrapFetchForExtraCa as n, readSseJson as o, readImageMetadata as p, getInstallId as q, TempDir as r, Snowflake as s, structuredCloneJSON as t, untilAborted as tt, render as u, hasFsCode as v, time as w, parseFrontmatter as x, isEnoent as y, isTerminalHeadless as z };
5671
+ export { isEnoent as $, formatCount as A, USER_AGENT as B, warn as C, installBunShim as D, CookieMap as E, $flag as F, getDocsRsCacheDir as G, getAgentDir as H, $pickenv as I, getLogsDir as J, getDocumentConversionCacheDir as K, isBunTestRuntime as L, truncate as M, $env as N, formatAge as O, $envpos as P, hasFsCode as Q, isTerminalHeadless as R, time as S, Cookie as T, getAutoQaDbPath as U, VERSION as V, getDebugLogPath as W, getRemoteDir as X, getPuppeteerDir as Y, getToolsDir as Z, parseStreamingJson as _, readSseEvents as a, findOnPath as at, debug as b, combineSignals as c, $which as d, isEnotdir as et, register as f, parseJsonWithRepair as g, classifyJsonPrefix as h, readJsonl as i, untilAborted as it, formatNumber as j, formatBytes as k, exec as l, readImageMetadata as m, wrapFetchForExtraCa as n, peekFile as nt, readSseJson as o, parseImageMetadata as p, getInstallId as q, TempDir as r, once as rt, Snowflake as s, structuredCloneJSON as t, isProbablyBinary as tt, render as u, parseStreamingJsonThrottled as v, $ as w, error as x, parseFrontmatter as y, isWsl as z };