@bastani/atomic 0.9.5-alpha.5 → 0.9.5-alpha.7

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 (216) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/package.json +1 -1
  5. package/dist/builtin/subagents/package.json +1 -1
  6. package/dist/builtin/web-access/package.json +1 -1
  7. package/dist/builtin/workflows/CHANGELOG.md +6 -0
  8. package/dist/builtin/workflows/package.json +1 -1
  9. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +8 -0
  10. package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +2 -0
  11. package/dist/builtin/workflows/src/runs/foreground/stage-tool-execution-buffer.ts +47 -0
  12. package/dist/builtin/workflows/src/tui/stage-chat-view-pending-tools.ts +9 -0
  13. package/dist/builtin/workflows/src/tui/stage-chat-view-render-settings.ts +27 -0
  14. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +5 -6
  15. package/dist/bun/split-loader.d.ts +2 -0
  16. package/dist/bun/split-loader.d.ts.map +1 -0
  17. package/dist/bun/split-loader.js +37 -0
  18. package/dist/bun/split-loader.js.map +1 -0
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +6 -4
  21. package/dist/config.js.map +1 -1
  22. package/dist/core/agent-session-auto-compaction.d.ts +5 -4
  23. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  24. package/dist/core/agent-session-auto-compaction.js +15 -9
  25. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  26. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  27. package/dist/core/agent-session-extension-bindings.js +1 -1
  28. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  29. package/dist/core/agent-session-methods.d.ts +1 -1
  30. package/dist/core/agent-session-methods.d.ts.map +1 -1
  31. package/dist/core/agent-session-methods.js.map +1 -1
  32. package/dist/core/builtin-packages.d.ts.map +1 -1
  33. package/dist/core/builtin-packages.js +8 -4
  34. package/dist/core/builtin-packages.js.map +1 -1
  35. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  36. package/dist/core/copilot-model-catalog.js +3 -2
  37. package/dist/core/copilot-model-catalog.js.map +1 -1
  38. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  39. package/dist/core/extensions/loader-core.js +6 -0
  40. package/dist/core/extensions/loader-core.js.map +1 -1
  41. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  42. package/dist/core/extensions/loader-virtual-modules.js +8 -3
  43. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  44. package/dist/core/model-resolver-scope.d.ts +9 -0
  45. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  46. package/dist/core/model-resolver-scope.js +12 -4
  47. package/dist/core/model-resolver-scope.js.map +1 -1
  48. package/dist/core/model-resolver.d.ts +3 -2
  49. package/dist/core/model-resolver.d.ts.map +1 -1
  50. package/dist/core/model-resolver.js +1 -1
  51. package/dist/core/model-resolver.js.map +1 -1
  52. package/dist/core/openai-responses-payload-sanitizer.d.ts +6 -0
  53. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -0
  54. package/dist/core/openai-responses-payload-sanitizer.js +59 -0
  55. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -0
  56. package/dist/core/package-manager-auto-resources.d.ts +2 -2
  57. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  58. package/dist/core/package-manager-auto-resources.js +32 -52
  59. package/dist/core/package-manager-auto-resources.js.map +1 -1
  60. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  61. package/dist/core/package-manager-resolver.js +23 -14
  62. package/dist/core/package-manager-resolver.js.map +1 -1
  63. package/dist/core/package-manager-resource-collector.d.ts +3 -3
  64. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  65. package/dist/core/package-manager-resource-collector.js +76 -73
  66. package/dist/core/package-manager-resource-collector.js.map +1 -1
  67. package/dist/core/package-manager-resource-files.d.ts +9 -8
  68. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  69. package/dist/core/package-manager-resource-files.js +92 -81
  70. package/dist/core/package-manager-resource-files.js.map +1 -1
  71. package/dist/core/prompt-templates-async.d.ts +3 -0
  72. package/dist/core/prompt-templates-async.d.ts.map +1 -0
  73. package/dist/core/prompt-templates-async.js +118 -0
  74. package/dist/core/prompt-templates-async.js.map +1 -0
  75. package/dist/core/provider-context-usage.d.ts +11 -0
  76. package/dist/core/provider-context-usage.d.ts.map +1 -0
  77. package/dist/core/provider-context-usage.js +58 -0
  78. package/dist/core/provider-context-usage.js.map +1 -0
  79. package/dist/core/resource-loader-assets.d.ts +3 -3
  80. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  81. package/dist/core/resource-loader-assets.js +66 -83
  82. package/dist/core/resource-loader-assets.js.map +1 -1
  83. package/dist/core/resource-loader-core.d.ts +1 -1
  84. package/dist/core/resource-loader-core.d.ts.map +1 -1
  85. package/dist/core/resource-loader-core.js +5 -5
  86. package/dist/core/resource-loader-core.js.map +1 -1
  87. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  88. package/dist/core/resource-loader-extensions.js +4 -0
  89. package/dist/core/resource-loader-extensions.js.map +1 -1
  90. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  91. package/dist/core/resource-loader-reload.js +54 -7
  92. package/dist/core/resource-loader-reload.js.map +1 -1
  93. package/dist/core/resource-loader-types.d.ts +7 -1
  94. package/dist/core/resource-loader-types.d.ts.map +1 -1
  95. package/dist/core/resource-loader-types.js.map +1 -1
  96. package/dist/core/sdk.d.ts.map +1 -1
  97. package/dist/core/sdk.js +6 -4
  98. package/dist/core/sdk.js.map +1 -1
  99. package/dist/core/settings-manager-core.d.ts.map +1 -1
  100. package/dist/core/settings-manager-core.js +3 -2
  101. package/dist/core/settings-manager-core.js.map +1 -1
  102. package/dist/core/settings-storage.d.ts.map +1 -1
  103. package/dist/core/settings-storage.js +2 -1
  104. package/dist/core/settings-storage.js.map +1 -1
  105. package/dist/core/skills-async.d.ts +3 -0
  106. package/dist/core/skills-async.d.ts.map +1 -0
  107. package/dist/core/skills-async.js +269 -0
  108. package/dist/core/skills-async.js.map +1 -0
  109. package/dist/core/trust-manager.d.ts +9 -0
  110. package/dist/core/trust-manager.d.ts.map +1 -1
  111. package/dist/core/trust-manager.js +44 -2
  112. package/dist/core/trust-manager.js.map +1 -1
  113. package/dist/index.d.ts +1 -1
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +1 -1
  116. package/dist/index.js.map +1 -1
  117. package/dist/main-deferred-startup.d.ts +21 -0
  118. package/dist/main-deferred-startup.d.ts.map +1 -0
  119. package/dist/main-deferred-startup.js +23 -0
  120. package/dist/main-deferred-startup.js.map +1 -0
  121. package/dist/main.d.ts.map +1 -1
  122. package/dist/main.js +28 -26
  123. package/dist/main.js.map +1 -1
  124. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  125. package/dist/modes/interactive/interactive-agent-events.js +10 -1
  126. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  127. package/dist/modes/interactive/interactive-deferred-startup.d.ts +8 -1
  128. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  129. package/dist/modes/interactive/interactive-deferred-startup.js +58 -11
  130. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  131. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  132. package/dist/modes/interactive/interactive-editor-actions.js +13 -13
  133. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  134. package/dist/modes/interactive/interactive-extension-runtime.d.ts.map +1 -1
  135. package/dist/modes/interactive/interactive-extension-runtime.js +16 -0
  136. package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
  137. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  138. package/dist/modes/interactive/interactive-input-handling.js +48 -0
  139. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  140. package/dist/modes/interactive/interactive-mode-base.d.ts +5 -0
  141. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  142. package/dist/modes/interactive/interactive-mode-base.js +5 -0
  143. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  144. package/dist/modes/interactive/interactive-mode-deps.d.ts +2 -2
  145. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  146. package/dist/modes/interactive/interactive-mode-deps.js +2 -2
  147. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  148. package/dist/modes/interactive/interactive-mode-surface.d.ts +18 -6
  149. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  150. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  151. package/dist/modes/interactive/interactive-mode-types.d.ts +5 -1
  152. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  153. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  154. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  155. package/dist/modes/interactive/interactive-model-routing.js +3 -3
  156. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  157. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
  158. package/dist/modes/interactive/interactive-onboarding.js +1 -0
  159. package/dist/modes/interactive/interactive-onboarding.js.map +1 -1
  160. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  161. package/dist/modes/interactive/interactive-process-lifecycle.js +43 -6
  162. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  163. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  164. package/dist/modes/interactive/interactive-render-chat.js +57 -0
  165. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  166. package/dist/modes/interactive/interactive-resource-disclosure.d.ts.map +1 -1
  167. package/dist/modes/interactive/interactive-resource-disclosure.js +3 -2
  168. package/dist/modes/interactive/interactive-resource-disclosure.js.map +1 -1
  169. package/dist/modes/interactive/interactive-resource-rendering.d.ts.map +1 -1
  170. package/dist/modes/interactive/interactive-resource-rendering.js +10 -8
  171. package/dist/modes/interactive/interactive-resource-rendering.js.map +1 -1
  172. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  173. package/dist/modes/interactive/interactive-session-routing.js +2 -2
  174. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  175. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  176. package/dist/modes/interactive/interactive-session-runtime.js +1 -1
  177. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  178. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  179. package/dist/modes/interactive/interactive-startup.js +59 -52
  180. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  181. package/dist/modes/interactive/theme/theme-loading.d.ts +1 -0
  182. package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
  183. package/dist/modes/interactive/theme/theme-loading.js +3 -0
  184. package/dist/modes/interactive/theme/theme-loading.js.map +1 -1
  185. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  186. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  187. package/dist/modes/interactive/theme/theme.js +1 -1
  188. package/dist/modes/interactive/theme/theme.js.map +1 -1
  189. package/dist/utils/child-process.d.ts +8 -1
  190. package/dist/utils/child-process.d.ts.map +1 -1
  191. package/dist/utils/child-process.js +39 -6
  192. package/dist/utils/child-process.js.map +1 -1
  193. package/dist/utils/event-loop.d.ts +7 -0
  194. package/dist/utils/event-loop.d.ts.map +1 -0
  195. package/dist/utils/event-loop.js +14 -0
  196. package/dist/utils/event-loop.js.map +1 -0
  197. package/dist/utils/json.d.ts +4 -0
  198. package/dist/utils/json.d.ts.map +1 -1
  199. package/dist/utils/json.js +8 -0
  200. package/dist/utils/json.js.map +1 -1
  201. package/dist/utils/module-require.d.ts +11 -0
  202. package/dist/utils/module-require.d.ts.map +1 -1
  203. package/dist/utils/module-require.js +14 -1
  204. package/dist/utils/module-require.js.map +1 -1
  205. package/dist/utils/split-launcher.d.ts +38 -0
  206. package/dist/utils/split-launcher.d.ts.map +1 -0
  207. package/dist/utils/split-launcher.js +63 -0
  208. package/dist/utils/split-launcher.js.map +1 -0
  209. package/docs/compaction.md +3 -0
  210. package/docs/keybindings.md +2 -2
  211. package/docs/providers.md +1 -1
  212. package/docs/settings.md +5 -1
  213. package/docs/usage.md +1 -1
  214. package/docs/workflows.md +1 -1
  215. package/npm-shrinkwrap.json +23 -23
  216. package/package.json +4 -4
@@ -3,6 +3,8 @@ import { basename } from "node:path";
3
3
  import crossSpawn from "cross-spawn";
4
4
  const EXIT_STDIO_IDLE_GRACE_MS = 100;
5
5
  const EXIT_STDIO_ACTIVE_DRAIN_HARD_CAP_MS = 5_000;
6
+ const WINDOWS_EXIT_POLL_INTERVAL_MS = 50;
7
+ const WINDOWS_EXIT_CODE_GRACE_MS = 1_000;
6
8
  const WINDOWS_SHELL_COMMANDS = new Set(["bun", "npm", "npx", "pnpm", "yarn", "yarnpkg", "corepack"]);
7
9
  export function shouldUseWindowsShell(command) {
8
10
  if (process.platform !== "win32")
@@ -37,7 +39,7 @@ function isWindowsProcessAlive(pid) {
37
39
  * after the grace elapses. A longer active-drain hard cap is armed once on `exit`
38
40
  * so an endlessly noisy descendant cannot keep the wait pending forever.
39
41
  */
40
- export function waitForChildProcess(child) {
42
+ export function waitForChildProcess(child, options = {}) {
41
43
  return new Promise((resolve, reject) => {
42
44
  let settled = false;
43
45
  let exited = false;
@@ -47,6 +49,11 @@ export function waitForChildProcess(child) {
47
49
  let stdoutEnded = child.stdout === null;
48
50
  let stderrEnded = child.stderr === null;
49
51
  let windowsExitPoll;
52
+ let windowsExitCodeGraceTimer;
53
+ const platform = options.platform ?? process.platform;
54
+ const processAlive = options.isWindowsProcessAlive ?? isWindowsProcessAlive;
55
+ const windowsExitPollIntervalMs = options.windowsExitPollIntervalMs ?? WINDOWS_EXIT_POLL_INTERVAL_MS;
56
+ const windowsExitCodeGraceMs = options.windowsExitCodeGraceMs ?? WINDOWS_EXIT_CODE_GRACE_MS;
50
57
  const cleanup = () => {
51
58
  if (windowsExitPoll) {
52
59
  clearInterval(windowsExitPoll);
@@ -60,6 +67,10 @@ export function waitForChildProcess(child) {
60
67
  clearTimeout(postExitActiveDrainHardCapTimer);
61
68
  postExitActiveDrainHardCapTimer = undefined;
62
69
  }
70
+ if (windowsExitCodeGraceTimer) {
71
+ clearTimeout(windowsExitCodeGraceTimer);
72
+ windowsExitCodeGraceTimer = undefined;
73
+ }
63
74
  child.removeListener("error", onError);
64
75
  child.removeListener("exit", onExit);
65
76
  child.removeListener("close", onClose);
@@ -116,6 +127,10 @@ export function waitForChildProcess(child) {
116
127
  reject(err);
117
128
  };
118
129
  const onExit = (code) => {
130
+ if (windowsExitCodeGraceTimer) {
131
+ clearTimeout(windowsExitCodeGraceTimer);
132
+ windowsExitCodeGraceTimer = undefined;
133
+ }
119
134
  exited = true;
120
135
  exitCode = code;
121
136
  maybeFinalizeAfterExit();
@@ -124,18 +139,36 @@ export function waitForChildProcess(child) {
124
139
  armIdleTimer();
125
140
  }
126
141
  };
127
- const pollWindowsExit = () => {
128
- if (process.platform !== "win32" || !child.pid || exited || settled)
142
+ const armWindowsExitCodeGraceTimer = () => {
143
+ if (windowsExitCodeGraceTimer)
129
144
  return;
130
- if (!isWindowsProcessAlive(child.pid)) {
145
+ if (windowsExitPoll) {
146
+ clearInterval(windowsExitPoll);
147
+ windowsExitPoll = undefined;
148
+ }
149
+ windowsExitCodeGraceTimer = setTimeout(() => {
150
+ windowsExitCodeGraceTimer = undefined;
131
151
  onExit(child.exitCode ?? 0);
152
+ }, windowsExitCodeGraceMs);
153
+ };
154
+ const pollWindowsExit = () => {
155
+ if (platform !== "win32" || !child.pid || exited || settled)
156
+ return;
157
+ if (!processAlive(child.pid)) {
158
+ if (child.exitCode !== null) {
159
+ onExit(child.exitCode);
160
+ }
161
+ else {
162
+ armWindowsExitCodeGraceTimer();
163
+ }
132
164
  }
133
165
  };
134
166
  const onClose = (code) => {
135
167
  finalize(code);
136
168
  };
137
- if (process.platform === "win32" && child.pid)
138
- windowsExitPoll = setInterval(pollWindowsExit, 50);
169
+ if (platform === "win32" && child.pid) {
170
+ windowsExitPoll = setInterval(pollWindowsExit, windowsExitPollIntervalMs);
171
+ }
139
172
  child.stdout?.once("end", onStdoutEnd);
140
173
  child.stderr?.once("end", onStderrEnd);
141
174
  child.stdout?.on("data", onData);
@@ -1 +1 @@
1
- {"version":3,"file":"child-process.js","sourceRoot":"","sources":["../../src/utils/child-process.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ChildProcess,\n\ttype ChildProcessByStdio,\n\tspawn as nodeSpawn,\n\tspawnSync as nodeSpawnSync,\n\ttype SpawnOptions,\n\ttype SpawnOptionsWithStdioTuple,\n\ttype SpawnSyncOptionsWithStringEncoding,\n\ttype SpawnSyncReturns,\n\ttype StdioNull,\n\ttype StdioPipe,\n} from \"node:child_process\";\nimport { basename } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport crossSpawn from \"cross-spawn\";\n\nconst EXIT_STDIO_IDLE_GRACE_MS = 100;\nconst EXIT_STDIO_ACTIVE_DRAIN_HARD_CAP_MS = 5_000;\nconst WINDOWS_SHELL_COMMANDS = new Set([\"bun\", \"npm\", \"npx\", \"pnpm\", \"yarn\", \"yarnpkg\", \"corepack\"]);\n\nexport function shouldUseWindowsShell(command: string): boolean {\n\tif (process.platform !== \"win32\") return false;\n\tconst commandName = basename(command).toLowerCase();\n\treturn commandName.endsWith(\".cmd\") || commandName.endsWith(\".bat\") || WINDOWS_SHELL_COMMANDS.has(commandName);\n}\n\nexport function spawnProcess(\n\tcommand: string,\n\targs: string[],\n\toptions: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioPipe>,\n): ChildProcessByStdio<null, Readable, Readable>;\nexport function spawnProcess(command: string, args: string[], options: SpawnOptions): ChildProcess;\nexport function spawnProcess(command: string, args: string[], options: SpawnOptions): ChildProcess {\n\treturn process.platform === \"win32\" ? crossSpawn(command, args, options) : nodeSpawn(command, args, options);\n}\n\nexport function spawnProcessSync(\n\tcommand: string,\n\targs: string[],\n\toptions: SpawnSyncOptionsWithStringEncoding,\n): SpawnSyncReturns<string> {\n\treturn process.platform === \"win32\"\n\t\t? crossSpawn.sync(command, args, options)\n\t\t: nodeSpawnSync(command, args, options);\n}\n\nfunction isWindowsProcessAlive(pid: number): boolean {\n\tconst result = nodeSpawnSync(\"tasklist\", [\"/FI\", `PID eq ${pid}`, \"/NH\"], { encoding: \"utf-8\", windowsHide: true });\n\tif (result.status !== 0) return true;\n\treturn new RegExp(`\\\\b${pid}\\\\b`).test(result.stdout);\n}\n\n/**\n * Wait for a child process to terminate without hanging on inherited stdio handles.\n *\n * A short-lived child can `exit` while a detached descendant keeps its stdout/stderr\n * pipe open. We must not resolve and destroy the streams on a short fixed deadline\n * measured from `exit`, or finite post-exit output still being written past that\n * deadline is silently lost (earendil-works/pi#5303). Instead, after `exit` we wait\n * for the pipes to fall idle: the grace timer is re-armed on every chunk, so finite\n * post-exit writers drain while active, while a quiet inherited handle (e.g. a\n * Windows daemonized descendant that never lets `close` fire) releases promptly\n * after the grace elapses. A longer active-drain hard cap is armed once on `exit`\n * so an endlessly noisy descendant cannot keep the wait pending forever.\n */\nexport function waitForChildProcess(child: ChildProcess): Promise<number | null> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet exited = false;\n\t\tlet exitCode: number | null = null;\n\t\tlet postExitIdleTimer: NodeJS.Timeout | undefined;\n\t\tlet postExitActiveDrainHardCapTimer: NodeJS.Timeout | undefined;\n\t\tlet stdoutEnded = child.stdout === null;\n\t\tlet stderrEnded = child.stderr === null;\n\t\tlet windowsExitPoll: NodeJS.Timeout | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (windowsExitPoll) {\n\t\t\t\tclearInterval(windowsExitPoll);\n\t\t\t\twindowsExitPoll = undefined;\n\t\t\t}\n\t\t\tif (postExitIdleTimer) {\n\t\t\t\tclearTimeout(postExitIdleTimer);\n\t\t\t\tpostExitIdleTimer = undefined;\n\t\t\t}\n\t\t\tif (postExitActiveDrainHardCapTimer) {\n\t\t\t\tclearTimeout(postExitActiveDrainHardCapTimer);\n\t\t\t\tpostExitActiveDrainHardCapTimer = undefined;\n\t\t\t}\n\t\t\tchild.removeListener(\"error\", onError);\n\t\t\tchild.removeListener(\"exit\", onExit);\n\t\t\tchild.removeListener(\"close\", onClose);\n\t\t\tchild.stdout?.removeListener(\"end\", onStdoutEnd);\n\t\t\tchild.stderr?.removeListener(\"end\", onStderrEnd);\n\t\t\tchild.stdout?.removeListener(\"data\", onData);\n\t\t\tchild.stderr?.removeListener(\"data\", onData);\n\t\t};\n\n\t\tconst finalize = (code: number | null) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tchild.stdout?.destroy();\n\t\t\tchild.stderr?.destroy();\n\t\t\tresolve(code);\n\t\t};\n\n\t\tconst maybeFinalizeAfterExit = () => {\n\t\t\tif (!exited || settled) return;\n\t\t\tif (stdoutEnded && stderrEnded) {\n\t\t\t\tfinalize(exitCode);\n\t\t\t}\n\t\t};\n\n\t\tconst armIdleTimer = () => {\n\t\t\tif (postExitIdleTimer) clearTimeout(postExitIdleTimer);\n\t\t\tpostExitIdleTimer = setTimeout(() => finalize(exitCode), EXIT_STDIO_IDLE_GRACE_MS);\n\t\t};\n\n\t\tconst armActiveDrainHardCapTimer = () => {\n\t\t\tif (postExitActiveDrainHardCapTimer) return;\n\t\t\tpostExitActiveDrainHardCapTimer = setTimeout(\n\t\t\t\t() => finalize(exitCode),\n\t\t\t\tEXIT_STDIO_ACTIVE_DRAIN_HARD_CAP_MS,\n\t\t\t);\n\t\t};\n\n\t\tconst onData = () => {\n\t\t\t// Output is still arriving after exit; keep extending the idle grace\n\t\t\t// so active finite writers can drain without truncating the tail.\n\t\t\tif (exited && !settled) armIdleTimer();\n\t\t};\n\n\t\tconst onStdoutEnd = () => {\n\t\t\tstdoutEnded = true;\n\t\t\tmaybeFinalizeAfterExit();\n\t\t};\n\n\t\tconst onStderrEnd = () => {\n\t\t\tstderrEnded = true;\n\t\t\tmaybeFinalizeAfterExit();\n\t\t};\n\n\t\tconst onError = (err: Error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(err);\n\t\t};\n\n\t\tconst onExit = (code: number | null) => {\n\t\t\texited = true;\n\t\t\texitCode = code;\n\t\t\tmaybeFinalizeAfterExit();\n\t\t\tif (!settled) {\n\t\t\t\tarmActiveDrainHardCapTimer();\n\t\t\t\tarmIdleTimer();\n\t\t\t}\n\t\t};\n\n\t\tconst pollWindowsExit = () => {\n\t\t\tif (process.platform !== \"win32\" || !child.pid || exited || settled) return;\n\t\t\tif (!isWindowsProcessAlive(child.pid)) {\n\t\t\t\tonExit(child.exitCode ?? 0);\n\t\t\t}\n\t\t};\n\n\t\tconst onClose = (code: number | null) => {\n\t\t\tfinalize(code);\n\t\t};\n\n\t\tif (process.platform === \"win32\" && child.pid) windowsExitPoll = setInterval(pollWindowsExit, 50);\n\t\tchild.stdout?.once(\"end\", onStdoutEnd);\n\t\tchild.stderr?.once(\"end\", onStderrEnd);\n\t\tchild.stdout?.on(\"data\", onData);\n\t\tchild.stderr?.on(\"data\", onData);\n\t\tchild.once(\"error\", onError);\n\t\tchild.once(\"exit\", onExit);\n\t\tchild.once(\"close\", onClose);\n\t});\n}\n"]}
1
+ {"version":3,"file":"child-process.js","sourceRoot":"","sources":["../../src/utils/child-process.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ChildProcess,\n\ttype ChildProcessByStdio,\n\tspawn as nodeSpawn,\n\tspawnSync as nodeSpawnSync,\n\ttype SpawnOptions,\n\ttype SpawnOptionsWithStdioTuple,\n\ttype SpawnSyncOptionsWithStringEncoding,\n\ttype SpawnSyncReturns,\n\ttype StdioNull,\n\ttype StdioPipe,\n} from \"node:child_process\";\nimport { basename } from \"node:path\";\nimport type { Readable } from \"node:stream\";\nimport crossSpawn from \"cross-spawn\";\n\nconst EXIT_STDIO_IDLE_GRACE_MS = 100;\nconst EXIT_STDIO_ACTIVE_DRAIN_HARD_CAP_MS = 5_000;\nconst WINDOWS_EXIT_POLL_INTERVAL_MS = 50;\nconst WINDOWS_EXIT_CODE_GRACE_MS = 1_000;\n\ntype WaitForChildProcessOptions = {\n\tplatform?: NodeJS.Platform;\n\tisWindowsProcessAlive?: (pid: number) => boolean;\n\twindowsExitPollIntervalMs?: number;\n\twindowsExitCodeGraceMs?: number;\n};\n\nconst WINDOWS_SHELL_COMMANDS = new Set([\"bun\", \"npm\", \"npx\", \"pnpm\", \"yarn\", \"yarnpkg\", \"corepack\"]);\n\nexport function shouldUseWindowsShell(command: string): boolean {\n\tif (process.platform !== \"win32\") return false;\n\tconst commandName = basename(command).toLowerCase();\n\treturn commandName.endsWith(\".cmd\") || commandName.endsWith(\".bat\") || WINDOWS_SHELL_COMMANDS.has(commandName);\n}\n\nexport function spawnProcess(\n\tcommand: string,\n\targs: string[],\n\toptions: SpawnOptionsWithStdioTuple<StdioNull, StdioPipe, StdioPipe>,\n): ChildProcessByStdio<null, Readable, Readable>;\nexport function spawnProcess(command: string, args: string[], options: SpawnOptions): ChildProcess;\nexport function spawnProcess(command: string, args: string[], options: SpawnOptions): ChildProcess {\n\treturn process.platform === \"win32\" ? crossSpawn(command, args, options) : nodeSpawn(command, args, options);\n}\n\nexport function spawnProcessSync(\n\tcommand: string,\n\targs: string[],\n\toptions: SpawnSyncOptionsWithStringEncoding,\n): SpawnSyncReturns<string> {\n\treturn process.platform === \"win32\"\n\t\t? crossSpawn.sync(command, args, options)\n\t\t: nodeSpawnSync(command, args, options);\n}\n\nfunction isWindowsProcessAlive(pid: number): boolean {\n\tconst result = nodeSpawnSync(\"tasklist\", [\"/FI\", `PID eq ${pid}`, \"/NH\"], { encoding: \"utf-8\", windowsHide: true });\n\tif (result.status !== 0) return true;\n\treturn new RegExp(`\\\\b${pid}\\\\b`).test(result.stdout);\n}\n\n/**\n * Wait for a child process to terminate without hanging on inherited stdio handles.\n *\n * A short-lived child can `exit` while a detached descendant keeps its stdout/stderr\n * pipe open. We must not resolve and destroy the streams on a short fixed deadline\n * measured from `exit`, or finite post-exit output still being written past that\n * deadline is silently lost (earendil-works/pi#5303). Instead, after `exit` we wait\n * for the pipes to fall idle: the grace timer is re-armed on every chunk, so finite\n * post-exit writers drain while active, while a quiet inherited handle (e.g. a\n * Windows daemonized descendant that never lets `close` fire) releases promptly\n * after the grace elapses. A longer active-drain hard cap is armed once on `exit`\n * so an endlessly noisy descendant cannot keep the wait pending forever.\n */\nexport function waitForChildProcess(child: ChildProcess, options: WaitForChildProcessOptions = {}): Promise<number | null> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet exited = false;\n\t\tlet exitCode: number | null = null;\n\t\tlet postExitIdleTimer: NodeJS.Timeout | undefined;\n\t\tlet postExitActiveDrainHardCapTimer: NodeJS.Timeout | undefined;\n\t\tlet stdoutEnded = child.stdout === null;\n\t\tlet stderrEnded = child.stderr === null;\n\t\tlet windowsExitPoll: NodeJS.Timeout | undefined;\n\t\tlet windowsExitCodeGraceTimer: NodeJS.Timeout | undefined;\n\t\tconst platform = options.platform ?? process.platform;\n\t\tconst processAlive = options.isWindowsProcessAlive ?? isWindowsProcessAlive;\n\t\tconst windowsExitPollIntervalMs = options.windowsExitPollIntervalMs ?? WINDOWS_EXIT_POLL_INTERVAL_MS;\n\t\tconst windowsExitCodeGraceMs = options.windowsExitCodeGraceMs ?? WINDOWS_EXIT_CODE_GRACE_MS;\n\n\t\tconst cleanup = () => {\n\t\t\tif (windowsExitPoll) {\n\t\t\t\tclearInterval(windowsExitPoll);\n\t\t\t\twindowsExitPoll = undefined;\n\t\t\t}\n\t\t\tif (postExitIdleTimer) {\n\t\t\t\tclearTimeout(postExitIdleTimer);\n\t\t\t\tpostExitIdleTimer = undefined;\n\t\t\t}\n\t\t\tif (postExitActiveDrainHardCapTimer) {\n\t\t\t\tclearTimeout(postExitActiveDrainHardCapTimer);\n\t\t\t\tpostExitActiveDrainHardCapTimer = undefined;\n\t\t\t}\n\t\t\tif (windowsExitCodeGraceTimer) {\n\t\t\t\tclearTimeout(windowsExitCodeGraceTimer);\n\t\t\t\twindowsExitCodeGraceTimer = undefined;\n\t\t\t}\n\t\t\tchild.removeListener(\"error\", onError);\n\t\t\tchild.removeListener(\"exit\", onExit);\n\t\t\tchild.removeListener(\"close\", onClose);\n\t\t\tchild.stdout?.removeListener(\"end\", onStdoutEnd);\n\t\t\tchild.stderr?.removeListener(\"end\", onStderrEnd);\n\t\t\tchild.stdout?.removeListener(\"data\", onData);\n\t\t\tchild.stderr?.removeListener(\"data\", onData);\n\t\t};\n\n\t\tconst finalize = (code: number | null) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\tchild.stdout?.destroy();\n\t\t\tchild.stderr?.destroy();\n\t\t\tresolve(code);\n\t\t};\n\n\t\tconst maybeFinalizeAfterExit = () => {\n\t\t\tif (!exited || settled) return;\n\t\t\tif (stdoutEnded && stderrEnded) {\n\t\t\t\tfinalize(exitCode);\n\t\t\t}\n\t\t};\n\n\t\tconst armIdleTimer = () => {\n\t\t\tif (postExitIdleTimer) clearTimeout(postExitIdleTimer);\n\t\t\tpostExitIdleTimer = setTimeout(() => finalize(exitCode), EXIT_STDIO_IDLE_GRACE_MS);\n\t\t};\n\n\t\tconst armActiveDrainHardCapTimer = () => {\n\t\t\tif (postExitActiveDrainHardCapTimer) return;\n\t\t\tpostExitActiveDrainHardCapTimer = setTimeout(\n\t\t\t\t() => finalize(exitCode),\n\t\t\t\tEXIT_STDIO_ACTIVE_DRAIN_HARD_CAP_MS,\n\t\t\t);\n\t\t};\n\n\t\tconst onData = () => {\n\t\t\t// Output is still arriving after exit; keep extending the idle grace\n\t\t\t// so active finite writers can drain without truncating the tail.\n\t\t\tif (exited && !settled) armIdleTimer();\n\t\t};\n\n\t\tconst onStdoutEnd = () => {\n\t\t\tstdoutEnded = true;\n\t\t\tmaybeFinalizeAfterExit();\n\t\t};\n\n\t\tconst onStderrEnd = () => {\n\t\t\tstderrEnded = true;\n\t\t\tmaybeFinalizeAfterExit();\n\t\t};\n\n\t\tconst onError = (err: Error) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(err);\n\t\t};\n\n\t\tconst onExit = (code: number | null) => {\n\t\t\tif (windowsExitCodeGraceTimer) {\n\t\t\t\tclearTimeout(windowsExitCodeGraceTimer);\n\t\t\t\twindowsExitCodeGraceTimer = undefined;\n\t\t\t}\n\t\t\texited = true;\n\t\t\texitCode = code;\n\t\t\tmaybeFinalizeAfterExit();\n\t\t\tif (!settled) {\n\t\t\t\tarmActiveDrainHardCapTimer();\n\t\t\t\tarmIdleTimer();\n\t\t\t}\n\t\t};\n\n\t\tconst armWindowsExitCodeGraceTimer = () => {\n\t\t\tif (windowsExitCodeGraceTimer) return;\n\t\t\tif (windowsExitPoll) {\n\t\t\t\tclearInterval(windowsExitPoll);\n\t\t\t\twindowsExitPoll = undefined;\n\t\t\t}\n\t\t\twindowsExitCodeGraceTimer = setTimeout(() => {\n\t\t\t\twindowsExitCodeGraceTimer = undefined;\n\t\t\t\tonExit(child.exitCode ?? 0);\n\t\t\t}, windowsExitCodeGraceMs);\n\t\t};\n\n\t\tconst pollWindowsExit = () => {\n\t\t\tif (platform !== \"win32\" || !child.pid || exited || settled) return;\n\t\t\tif (!processAlive(child.pid)) {\n\t\t\t\tif (child.exitCode !== null) {\n\t\t\t\t\tonExit(child.exitCode);\n\t\t\t\t} else {\n\t\t\t\t\tarmWindowsExitCodeGraceTimer();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tconst onClose = (code: number | null) => {\n\t\t\tfinalize(code);\n\t\t};\n\n\t\tif (platform === \"win32\" && child.pid) {\n\t\t\twindowsExitPoll = setInterval(pollWindowsExit, windowsExitPollIntervalMs);\n\t\t}\n\t\tchild.stdout?.once(\"end\", onStdoutEnd);\n\t\tchild.stderr?.once(\"end\", onStderrEnd);\n\t\tchild.stdout?.on(\"data\", onData);\n\t\tchild.stderr?.on(\"data\", onData);\n\t\tchild.once(\"error\", onError);\n\t\tchild.once(\"exit\", onExit);\n\t\tchild.once(\"close\", onClose);\n\t});\n}\n"]}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Yield to the macrotask queue so terminal input, timers, and render work can
3
+ * run between otherwise synchronous startup chunks.
4
+ */
5
+ export declare function yieldToEventLoop(): Promise<void>;
6
+ export declare function yieldToEventLoopIfSlow(startedAt: number, thresholdMs?: number): Promise<void>;
7
+ //# sourceMappingURL=event-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-loop.d.ts","sourceRoot":"","sources":["../../src/utils/event-loop.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEtD;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F","sourcesContent":["import { setImmediate as waitForImmediate } from \"node:timers/promises\";\n\n/**\n * Yield to the macrotask queue so terminal input, timers, and render work can\n * run between otherwise synchronous startup chunks.\n */\nexport async function yieldToEventLoop(): Promise<void> {\n\tawait waitForImmediate();\n}\n\nexport async function yieldToEventLoopIfSlow(startedAt: number, thresholdMs = 16): Promise<void> {\n\tif (Date.now() - startedAt >= thresholdMs) {\n\t\tawait yieldToEventLoop();\n\t}\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import { setImmediate as waitForImmediate } from "node:timers/promises";
2
+ /**
3
+ * Yield to the macrotask queue so terminal input, timers, and render work can
4
+ * run between otherwise synchronous startup chunks.
5
+ */
6
+ export async function yieldToEventLoop() {
7
+ await waitForImmediate();
8
+ }
9
+ export async function yieldToEventLoopIfSlow(startedAt, thresholdMs = 16) {
10
+ if (Date.now() - startedAt >= thresholdMs) {
11
+ await yieldToEventLoop();
12
+ }
13
+ }
14
+ //# sourceMappingURL=event-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-loop.js","sourceRoot":"","sources":["../../src/utils/event-loop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACrC,MAAM,gBAAgB,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,SAAiB,EAAE,WAAW,GAAG,EAAE;IAC/E,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,WAAW,EAAE,CAAC;QAC3C,MAAM,gBAAgB,EAAE,CAAC;IAC1B,CAAC;AACF,CAAC","sourcesContent":["import { setImmediate as waitForImmediate } from \"node:timers/promises\";\n\n/**\n * Yield to the macrotask queue so terminal input, timers, and render work can\n * run between otherwise synchronous startup chunks.\n */\nexport async function yieldToEventLoop(): Promise<void> {\n\tawait waitForImmediate();\n}\n\nexport async function yieldToEventLoopIfSlow(startedAt: number, thresholdMs = 16): Promise<void> {\n\tif (Date.now() - startedAt >= thresholdMs) {\n\t\tawait yieldToEventLoop();\n\t}\n}\n"]}
@@ -1,3 +1,7 @@
1
1
  /** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */
2
2
  export declare function stripJsonComments(input: string): string;
3
+ /** Strip one leading UTF-8 BOM (U+FEFF) before JSON parsing. */
4
+ export declare function stripJsonBom(input: string): string;
5
+ /** Parse JSON read from disk, accepting files that start with a UTF-8 BOM. */
6
+ export declare function parseJsonFileContent(input: string): unknown;
3
7
  //# sourceMappingURL=json.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD","sourcesContent":["/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */\nexport function stripJsonComments(input: string): string {\n\treturn input\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|\\/\\/[^\\n]*/g, (m) => (m[0] === '\"' ? m : \"\"))\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|,(\\s*[}\\]])/g, (m, tail) => tail ?? (m[0] === '\"' ? m : \"\"));\n}\n"]}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,8EAA8E;AAC9E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D","sourcesContent":["/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */\nexport function stripJsonComments(input: string): string {\n\treturn input\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|\\/\\/[^\\n]*/g, (m) => (m[0] === '\"' ? m : \"\"))\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|,(\\s*[}\\]])/g, (m, tail) => tail ?? (m[0] === '\"' ? m : \"\"));\n}\n\n/** Strip one leading UTF-8 BOM (U+FEFF) before JSON parsing. */\nexport function stripJsonBom(input: string): string {\n\treturn input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;\n}\n\n/** Parse JSON read from disk, accepting files that start with a UTF-8 BOM. */\nexport function parseJsonFileContent(input: string): unknown {\n\treturn JSON.parse(stripJsonBom(input));\n}\n"]}
@@ -4,4 +4,12 @@ export function stripJsonComments(input) {
4
4
  .replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, (m) => (m[0] === '"' ? m : ""))
5
5
  .replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (m, tail) => tail ?? (m[0] === '"' ? m : ""));
6
6
  }
7
+ /** Strip one leading UTF-8 BOM (U+FEFF) before JSON parsing. */
8
+ export function stripJsonBom(input) {
9
+ return input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
10
+ }
11
+ /** Parse JSON read from disk, accepting files that start with a UTF-8 BOM. */
12
+ export function parseJsonFileContent(input) {
13
+ return JSON.parse(stripJsonBom(input));
14
+ }
7
15
  //# sourceMappingURL=json.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC9C,OAAO,KAAK;SACV,OAAO,CAAC,+BAA+B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACxE,OAAO,CAAC,gCAAgC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */\nexport function stripJsonComments(input: string): string {\n\treturn input\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|\\/\\/[^\\n]*/g, (m) => (m[0] === '\"' ? m : \"\"))\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|,(\\s*[}\\]])/g, (m, tail) => tail ?? (m[0] === '\"' ? m : \"\"));\n}\n"]}
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC9C,OAAO,KAAK;SACV,OAAO,CAAC,+BAA+B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACxE,OAAO,CAAC,gCAAgC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,YAAY,CAAC,KAAa;IACzC,OAAO,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAChE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAAC,KAAa;IACjD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AACxC,CAAC","sourcesContent":["/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */\nexport function stripJsonComments(input: string): string {\n\treturn input\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|\\/\\/[^\\n]*/g, (m) => (m[0] === '\"' ? m : \"\"))\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|,(\\s*[}\\]])/g, (m, tail) => tail ?? (m[0] === '\"' ? m : \"\"));\n}\n\n/** Strip one leading UTF-8 BOM (U+FEFF) before JSON parsing. */\nexport function stripJsonBom(input: string): string {\n\treturn input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;\n}\n\n/** Parse JSON read from disk, accepting files that start with a UTF-8 BOM. */\nexport function parseJsonFileContent(input: string): unknown {\n\treturn JSON.parse(stripJsonBom(input));\n}\n"]}
@@ -8,4 +8,15 @@
8
8
  * shipped next to the binary.
9
9
  */
10
10
  export declare function createModuleRequire(moduleUrl: string): NodeJS.Require;
11
+ /**
12
+ * A resolution base URL safe for the current runtime.
13
+ *
14
+ * In compiled binaries (monolithic bun or the Atomic split launcher) the
15
+ * bundled `import.meta.url` is the build machine's source URL — a foreign-OS
16
+ * `file://` URL that `fileURLToPath` rejects on other platforms (notably a
17
+ * macOS `file:///Users/...` URL on Windows). Any API that decodes the base
18
+ * (`createRequire`, jiti's `createJiti`) then throws `ERR_INVALID_FILE_URL_PATH`.
19
+ * Anchor to the executable instead, which is always a valid local file URL.
20
+ */
21
+ export declare function resolutionBaseUrl(moduleUrl: string): string;
11
22
  //# sourceMappingURL=module-require.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module-require.d.ts","sourceRoot":"","sources":["../../src/utils/module-require.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAErE","sourcesContent":["import { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { isBunBinary } from \"../config.ts\";\n\n/**\n * createRequire anchored so bare-specifier resolution works in every runtime.\n *\n * In the compiled binary the bundle is CJS (bytecode), where import.meta.url\n * is rewritten to the original build-machine source path; requiring through it\n * would resolve node_modules against paths that do not exist on user machines.\n * Anchor to the executable instead so resolution walks from the node_modules\n * shipped next to the binary.\n */\nexport function createModuleRequire(moduleUrl: string): NodeJS.Require {\n\treturn createRequire(isBunBinary ? pathToFileURL(process.execPath).href : moduleUrl);\n}\n"]}
1
+ {"version":3,"file":"module-require.d.ts","sourceRoot":"","sources":["../../src/utils/module-require.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D","sourcesContent":["import { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { isBunBinary } from \"../config.ts\";\n\n/**\n * createRequire anchored so bare-specifier resolution works in every runtime.\n *\n * In the compiled binary the bundle is CJS (bytecode), where import.meta.url\n * is rewritten to the original build-machine source path; requiring through it\n * would resolve node_modules against paths that do not exist on user machines.\n * Anchor to the executable instead so resolution walks from the node_modules\n * shipped next to the binary.\n */\nexport function createModuleRequire(moduleUrl: string): NodeJS.Require {\n\treturn createRequire(resolutionBaseUrl(moduleUrl));\n}\n\n/**\n * A resolution base URL safe for the current runtime.\n *\n * In compiled binaries (monolithic bun or the Atomic split launcher) the\n * bundled `import.meta.url` is the build machine's source URL — a foreign-OS\n * `file://` URL that `fileURLToPath` rejects on other platforms (notably a\n * macOS `file:///Users/...` URL on Windows). Any API that decodes the base\n * (`createRequire`, jiti's `createJiti`) then throws `ERR_INVALID_FILE_URL_PATH`.\n * Anchor to the executable instead, which is always a valid local file URL.\n */\nexport function resolutionBaseUrl(moduleUrl: string): string {\n\treturn isBunBinary ? pathToFileURL(process.execPath).href : moduleUrl;\n}\n"]}
@@ -11,6 +11,19 @@ import { isBunBinary } from "../config.js";
11
11
  * shipped next to the binary.
12
12
  */
13
13
  export function createModuleRequire(moduleUrl) {
14
- return createRequire(isBunBinary ? pathToFileURL(process.execPath).href : moduleUrl);
14
+ return createRequire(resolutionBaseUrl(moduleUrl));
15
+ }
16
+ /**
17
+ * A resolution base URL safe for the current runtime.
18
+ *
19
+ * In compiled binaries (monolithic bun or the Atomic split launcher) the
20
+ * bundled `import.meta.url` is the build machine's source URL — a foreign-OS
21
+ * `file://` URL that `fileURLToPath` rejects on other platforms (notably a
22
+ * macOS `file:///Users/...` URL on Windows). Any API that decodes the base
23
+ * (`createRequire`, jiti's `createJiti`) then throws `ERR_INVALID_FILE_URL_PATH`.
24
+ * Anchor to the executable instead, which is always a valid local file URL.
25
+ */
26
+ export function resolutionBaseUrl(moduleUrl) {
27
+ return isBunBinary ? pathToFileURL(process.execPath).href : moduleUrl;
15
28
  }
16
29
  //# sourceMappingURL=module-require.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"module-require.js","sourceRoot":"","sources":["../../src/utils/module-require.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACpD,OAAO,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACtF,CAAC","sourcesContent":["import { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { isBunBinary } from \"../config.ts\";\n\n/**\n * createRequire anchored so bare-specifier resolution works in every runtime.\n *\n * In the compiled binary the bundle is CJS (bytecode), where import.meta.url\n * is rewritten to the original build-machine source path; requiring through it\n * would resolve node_modules against paths that do not exist on user machines.\n * Anchor to the executable instead so resolution walks from the node_modules\n * shipped next to the binary.\n */\nexport function createModuleRequire(moduleUrl: string): NodeJS.Require {\n\treturn createRequire(isBunBinary ? pathToFileURL(process.execPath).href : moduleUrl);\n}\n"]}
1
+ {"version":3,"file":"module-require.js","sourceRoot":"","sources":["../../src/utils/module-require.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACpD,OAAO,aAAa,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;AACpD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IAClD,OAAO,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACvE,CAAC","sourcesContent":["import { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport { isBunBinary } from \"../config.ts\";\n\n/**\n * createRequire anchored so bare-specifier resolution works in every runtime.\n *\n * In the compiled binary the bundle is CJS (bytecode), where import.meta.url\n * is rewritten to the original build-machine source path; requiring through it\n * would resolve node_modules against paths that do not exist on user machines.\n * Anchor to the executable instead so resolution walks from the node_modules\n * shipped next to the binary.\n */\nexport function createModuleRequire(moduleUrl: string): NodeJS.Require {\n\treturn createRequire(resolutionBaseUrl(moduleUrl));\n}\n\n/**\n * A resolution base URL safe for the current runtime.\n *\n * In compiled binaries (monolithic bun or the Atomic split launcher) the\n * bundled `import.meta.url` is the build machine's source URL — a foreign-OS\n * `file://` URL that `fileURLToPath` rejects on other platforms (notably a\n * macOS `file:///Users/...` URL on Windows). Any API that decodes the base\n * (`createRequire`, jiti's `createJiti`) then throws `ERR_INVALID_FILE_URL_PATH`.\n * Anchor to the executable instead, which is always a valid local file URL.\n */\nexport function resolutionBaseUrl(moduleUrl: string): string {\n\treturn isBunBinary ? pathToFileURL(process.execPath).href : moduleUrl;\n}\n"]}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Split-launcher / foreign-build `import.meta.url` handling.
3
+ *
4
+ * Atomic's release binaries use a split launcher: a tiny compiled `atomic` /
5
+ * `atomic.exe` executable sets `ATOMIC_CODING_AGENT=true` and then dynamically
6
+ * imports the bundled sidecar `app.js` (plus raw-TS built-in extensions) from
7
+ * disk next to the executable.
8
+ *
9
+ * Unlike pi's single monolithic `bun --compile` binary — where `import.meta.url`
10
+ * is always a `$bunfs://` virtual-FS URL — the split sidecar bundle carries the
11
+ * build machine's source path baked in at bundle time (e.g. a macOS
12
+ * `file:///Users/.../app.js` URL). On Windows, `fileURLToPath()` rejects that
13
+ * URL with `ERR_INVALID_FILE_URL_PATH` ("File URL path must be an absolute
14
+ * path") because `/Users/...` has no drive letter, so every site that decodes
15
+ * `import.meta.url` to a filesystem path must tolerate it.
16
+ *
17
+ * These helpers centralize that tolerance: decode normally, and only when the
18
+ * decode fails under the split launcher fall back to a path resolved relative to
19
+ * the launcher executable (where the sidecar bundle and its assets actually
20
+ * live). This mirrors pi's convention of a single detection point rather than
21
+ * scattered ad-hoc guards.
22
+ */
23
+ export declare function isSplitLauncherRuntime(): boolean;
24
+ /** Directory containing the launcher executable and its sidecar bundle/assets. */
25
+ export declare function splitLauncherDir(): string;
26
+ /**
27
+ * Resolve a module's directory from its `import.meta.url`, tolerating the
28
+ * split-launcher case where the bundled URL is a foreign-OS build path.
29
+ * `execRelativeSegments` locate the equivalent directory next to the launcher.
30
+ */
31
+ export declare function moduleDirFromMetaUrl(metaUrl: string, ...execRelativeSegments: string[]): string;
32
+ /**
33
+ * Resolve a module's filename from its `import.meta.url`, tolerating the
34
+ * split-launcher case. `execRelativeFile` locates the equivalent file next to
35
+ * the launcher (used as a stable resolution base, e.g. for `createRequire`).
36
+ */
37
+ export declare function moduleFileFromMetaUrl(metaUrl: string, execRelativeFile: string): string;
38
+ //# sourceMappingURL=split-launcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-launcher.d.ts","sourceRoot":"","sources":["../../src/utils/split-launcher.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAKhD;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,oBAAoB,EAAE,MAAM,EAAE,GAAG,MAAM,CAO/F;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAOvF","sourcesContent":["import { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Split-launcher / foreign-build `import.meta.url` handling.\n *\n * Atomic's release binaries use a split launcher: a tiny compiled `atomic` /\n * `atomic.exe` executable sets `ATOMIC_CODING_AGENT=true` and then dynamically\n * imports the bundled sidecar `app.js` (plus raw-TS built-in extensions) from\n * disk next to the executable.\n *\n * Unlike pi's single monolithic `bun --compile` binary — where `import.meta.url`\n * is always a `$bunfs://` virtual-FS URL — the split sidecar bundle carries the\n * build machine's source path baked in at bundle time (e.g. a macOS\n * `file:///Users/.../app.js` URL). On Windows, `fileURLToPath()` rejects that\n * URL with `ERR_INVALID_FILE_URL_PATH` (\"File URL path must be an absolute\n * path\") because `/Users/...` has no drive letter, so every site that decodes\n * `import.meta.url` to a filesystem path must tolerate it.\n *\n * These helpers centralize that tolerance: decode normally, and only when the\n * decode fails under the split launcher fall back to a path resolved relative to\n * the launcher executable (where the sidecar bundle and its assets actually\n * live). This mirrors pi's convention of a single detection point rather than\n * scattered ad-hoc guards.\n */\nexport function isSplitLauncherRuntime(): boolean {\n\treturn (\n\t\tprocess.env.ATOMIC_CODING_AGENT === \"true\" &&\n\t\t/(?:^|[\\\\/])atomic(?:\\.exe)?$/i.test(process.execPath)\n\t);\n}\n\n/** Directory containing the launcher executable and its sidecar bundle/assets. */\nexport function splitLauncherDir(): string {\n\treturn dirname(process.execPath);\n}\n\n/**\n * Resolve a module's directory from its `import.meta.url`, tolerating the\n * split-launcher case where the bundled URL is a foreign-OS build path.\n * `execRelativeSegments` locate the equivalent directory next to the launcher.\n */\nexport function moduleDirFromMetaUrl(metaUrl: string, ...execRelativeSegments: string[]): string {\n\ttry {\n\t\treturn dirname(fileURLToPath(metaUrl));\n\t} catch (error) {\n\t\tif (isSplitLauncherRuntime()) return join(splitLauncherDir(), ...execRelativeSegments);\n\t\tthrow error;\n\t}\n}\n\n/**\n * Resolve a module's filename from its `import.meta.url`, tolerating the\n * split-launcher case. `execRelativeFile` locates the equivalent file next to\n * the launcher (used as a stable resolution base, e.g. for `createRequire`).\n */\nexport function moduleFileFromMetaUrl(metaUrl: string, execRelativeFile: string): string {\n\ttry {\n\t\treturn fileURLToPath(metaUrl);\n\t} catch (error) {\n\t\tif (isSplitLauncherRuntime()) return join(splitLauncherDir(), execRelativeFile);\n\t\tthrow error;\n\t}\n}\n"]}
@@ -0,0 +1,63 @@
1
+ import { dirname, join } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ /**
4
+ * Split-launcher / foreign-build `import.meta.url` handling.
5
+ *
6
+ * Atomic's release binaries use a split launcher: a tiny compiled `atomic` /
7
+ * `atomic.exe` executable sets `ATOMIC_CODING_AGENT=true` and then dynamically
8
+ * imports the bundled sidecar `app.js` (plus raw-TS built-in extensions) from
9
+ * disk next to the executable.
10
+ *
11
+ * Unlike pi's single monolithic `bun --compile` binary — where `import.meta.url`
12
+ * is always a `$bunfs://` virtual-FS URL — the split sidecar bundle carries the
13
+ * build machine's source path baked in at bundle time (e.g. a macOS
14
+ * `file:///Users/.../app.js` URL). On Windows, `fileURLToPath()` rejects that
15
+ * URL with `ERR_INVALID_FILE_URL_PATH` ("File URL path must be an absolute
16
+ * path") because `/Users/...` has no drive letter, so every site that decodes
17
+ * `import.meta.url` to a filesystem path must tolerate it.
18
+ *
19
+ * These helpers centralize that tolerance: decode normally, and only when the
20
+ * decode fails under the split launcher fall back to a path resolved relative to
21
+ * the launcher executable (where the sidecar bundle and its assets actually
22
+ * live). This mirrors pi's convention of a single detection point rather than
23
+ * scattered ad-hoc guards.
24
+ */
25
+ export function isSplitLauncherRuntime() {
26
+ return (process.env.ATOMIC_CODING_AGENT === "true" &&
27
+ /(?:^|[\\/])atomic(?:\.exe)?$/i.test(process.execPath));
28
+ }
29
+ /** Directory containing the launcher executable and its sidecar bundle/assets. */
30
+ export function splitLauncherDir() {
31
+ return dirname(process.execPath);
32
+ }
33
+ /**
34
+ * Resolve a module's directory from its `import.meta.url`, tolerating the
35
+ * split-launcher case where the bundled URL is a foreign-OS build path.
36
+ * `execRelativeSegments` locate the equivalent directory next to the launcher.
37
+ */
38
+ export function moduleDirFromMetaUrl(metaUrl, ...execRelativeSegments) {
39
+ try {
40
+ return dirname(fileURLToPath(metaUrl));
41
+ }
42
+ catch (error) {
43
+ if (isSplitLauncherRuntime())
44
+ return join(splitLauncherDir(), ...execRelativeSegments);
45
+ throw error;
46
+ }
47
+ }
48
+ /**
49
+ * Resolve a module's filename from its `import.meta.url`, tolerating the
50
+ * split-launcher case. `execRelativeFile` locates the equivalent file next to
51
+ * the launcher (used as a stable resolution base, e.g. for `createRequire`).
52
+ */
53
+ export function moduleFileFromMetaUrl(metaUrl, execRelativeFile) {
54
+ try {
55
+ return fileURLToPath(metaUrl);
56
+ }
57
+ catch (error) {
58
+ if (isSplitLauncherRuntime())
59
+ return join(splitLauncherDir(), execRelativeFile);
60
+ throw error;
61
+ }
62
+ }
63
+ //# sourceMappingURL=split-launcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split-launcher.js","sourceRoot":"","sources":["../../src/utils/split-launcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,sBAAsB;IACrC,OAAO,CACN,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,MAAM;QAC1C,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CACtD,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,gBAAgB;IAC/B,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,GAAG,oBAA8B;IACtF,IAAI,CAAC;QACJ,OAAO,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,sBAAsB,EAAE;YAAE,OAAO,IAAI,CAAC,gBAAgB,EAAE,EAAE,GAAG,oBAAoB,CAAC,CAAC;QACvF,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,gBAAwB;IAC9E,IAAI,CAAC;QACJ,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,sBAAsB,EAAE;YAAE,OAAO,IAAI,CAAC,gBAAgB,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAChF,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC","sourcesContent":["import { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Split-launcher / foreign-build `import.meta.url` handling.\n *\n * Atomic's release binaries use a split launcher: a tiny compiled `atomic` /\n * `atomic.exe` executable sets `ATOMIC_CODING_AGENT=true` and then dynamically\n * imports the bundled sidecar `app.js` (plus raw-TS built-in extensions) from\n * disk next to the executable.\n *\n * Unlike pi's single monolithic `bun --compile` binary — where `import.meta.url`\n * is always a `$bunfs://` virtual-FS URL — the split sidecar bundle carries the\n * build machine's source path baked in at bundle time (e.g. a macOS\n * `file:///Users/.../app.js` URL). On Windows, `fileURLToPath()` rejects that\n * URL with `ERR_INVALID_FILE_URL_PATH` (\"File URL path must be an absolute\n * path\") because `/Users/...` has no drive letter, so every site that decodes\n * `import.meta.url` to a filesystem path must tolerate it.\n *\n * These helpers centralize that tolerance: decode normally, and only when the\n * decode fails under the split launcher fall back to a path resolved relative to\n * the launcher executable (where the sidecar bundle and its assets actually\n * live). This mirrors pi's convention of a single detection point rather than\n * scattered ad-hoc guards.\n */\nexport function isSplitLauncherRuntime(): boolean {\n\treturn (\n\t\tprocess.env.ATOMIC_CODING_AGENT === \"true\" &&\n\t\t/(?:^|[\\\\/])atomic(?:\\.exe)?$/i.test(process.execPath)\n\t);\n}\n\n/** Directory containing the launcher executable and its sidecar bundle/assets. */\nexport function splitLauncherDir(): string {\n\treturn dirname(process.execPath);\n}\n\n/**\n * Resolve a module's directory from its `import.meta.url`, tolerating the\n * split-launcher case where the bundled URL is a foreign-OS build path.\n * `execRelativeSegments` locate the equivalent directory next to the launcher.\n */\nexport function moduleDirFromMetaUrl(metaUrl: string, ...execRelativeSegments: string[]): string {\n\ttry {\n\t\treturn dirname(fileURLToPath(metaUrl));\n\t} catch (error) {\n\t\tif (isSplitLauncherRuntime()) return join(splitLauncherDir(), ...execRelativeSegments);\n\t\tthrow error;\n\t}\n}\n\n/**\n * Resolve a module's filename from its `import.meta.url`, tolerating the\n * split-launcher case. `execRelativeFile` locates the equivalent file next to\n * the launcher (used as a stable resolution base, e.g. for `createRequire`).\n */\nexport function moduleFileFromMetaUrl(metaUrl: string, execRelativeFile: string): string {\n\ttry {\n\t\treturn fileURLToPath(metaUrl);\n\t} catch (error) {\n\t\tif (isSplitLauncherRuntime()) return join(splitLauncherDir(), execRelativeFile);\n\t\tthrow error;\n\t}\n}\n"]}
@@ -13,6 +13,7 @@ Atomic's compaction design and terminology are informed by Morph's Context Compa
13
13
  - [`packages/coding-agent/src/core/compaction/branch-summarization.ts`](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/src/core/compaction/branch-summarization.ts) - Branch summarization
14
14
  - [`packages/coding-agent/src/core/compaction/utils.ts`](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/src/core/compaction/utils.ts) - Shared utilities (file tracking, serialization)
15
15
  - [`packages/coding-agent/src/core/session-manager.ts`](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/src/core/session-manager.ts) - Entry types (`ContextCompactionEntry`, `BranchSummaryEntry`) and active-context rebuild logic
16
+ - [`packages/coding-agent/src/core/provider-context-usage.ts`](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/src/core/provider-context-usage.ts) - Provider-bound usage scrub that keeps post-compaction token budgeting based on the compacted prompt
16
17
  - [`packages/coding-agent/src/core/extensions/session-events.ts`](https://github.com/bastani-inc/atomic/blob/main/packages/coding-agent/src/core/extensions/session-events.ts) - Compaction extension event payloads
17
18
 
18
19
  For TypeScript definitions in your project, inspect `node_modules/@bastani/atomic/dist/`.
@@ -69,6 +70,8 @@ Atomic records those targets in an append-only `context_compaction` entry. When
69
70
 
70
71
  The raw session JSONL remains append-only. Deleted objects stay available in the stored session file and backup snapshot; they are only omitted from future active LLM context on that branch.
71
72
 
73
+ Provider-bound context is cloned one more time before each LLM request. Retained assistant messages from before the latest `context_compaction` keep their historical usage in the durable session JSONL, but Atomic zeroes that usage in the request-only clone so provider token-budget estimators do not treat the compacted context as if it still contained the old, larger prompt. The first assistant response after compaction then supplies fresh usage for subsequent turns.
74
+
72
75
  ### Compaction Parameters
73
76
 
74
77
  Atomic uses three effective parameters for each context-compaction run. They are available to extension hooks as `event.parameters`, copied into `event.preparation.parameters`, and returned on `event.result.parameters` after a successful compaction.
@@ -82,8 +82,8 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1
82
82
 
83
83
  | Keybinding id | Default | Description |
84
84
  |--------|---------|-------------|
85
- | `app.interrupt` | `escape` | Cancel / abort |
86
- | `app.clear` | `ctrl+c` | Clear editor |
85
+ | `app.interrupt` | `escape` | Cancel / abort the running agent, bash command, compaction, or retry |
86
+ | `app.clear` | `ctrl+c` | Interrupt the running agent when busy; otherwise clear the editor (press twice to exit) |
87
87
  | `app.exit` | `ctrl+d` | Exit (when editor empty) |
88
88
  | `app.suspend` | `ctrl+z` (none on Windows) | Suspend to background |
89
89
  | `app.editor.external` | `ctrl+g` | Open in external editor (`$VISUAL` or `$EDITOR`) |
package/docs/providers.md CHANGED
@@ -38,7 +38,7 @@ Anthropic subscription auth is active for Claude Pro/Max accounts. Third-party h
38
38
  ### GitHub Copilot
39
39
 
40
40
  - Press Enter for github.com, or enter your GitHub Enterprise Server domain
41
- - When using `COPILOT_GITHUB_TOKEN` instead of `/login`, Atomic uses the token's `proxy-ep` when present, honors `COPILOT_API_TARGET` or `GITHUB_COPILOT_BASE_URL` overrides, derives `copilot-api.<tenant>.ghe.com` from `GITHUB_SERVER_URL=*.ghe.com`, derives `https://api.enterprise.githubcopilot.com` from other non-`github.com` server URLs, and otherwise falls back to the public Copilot routing hub `https://api.githubcopilot.com` instead of the account-specific individual endpoint.
41
+ - When using `COPILOT_GITHUB_TOKEN` instead of `/login`, Atomic uses the token's `proxy-ep` when present, honors `COPILOT_API_TARGET` or `GITHUB_COPILOT_BASE_URL` overrides, derives the tenant-specific GHE routing host from `GITHUB_SERVER_URL=*.ghe.com`, derives `https://api.enterprise.githubcopilot.com` from other non-`github.com` server URLs, and otherwise falls back to the public Copilot routing hub `https://api.githubcopilot.com` instead of the account-specific individual endpoint.
42
42
  - If you get "model not supported", enable it in VS Code: Copilot Chat → model selector → select model → "Enable"
43
43
  - GitHub Copilot models are populated dynamically from Copilot's live CAPI `/models` catalog when Copilot auth is available. Atomic synthesizes only picker-enabled, non-disabled `chat` entries with plain ids (for example `github-copilot/claude-sonnet-5` and `github-copilot/mai-code-1-flash-picker`); namespaced enterprise deployments containing `/` are skipped rather than exposed as `github-copilot/*` models. Models that advertise long-context limits, such as `github-copilot/gpt-5.5`, `github-copilot/claude-opus-4.8`, and `github-copilot/gemini-3.1-pro-preview`, expose an opt-in long-context choice through `--context-window`, the `/model` selection flow, per-model `defaultContextWindows`, SDK, and RPC controls. The long-context option advertises the model's full context window (for example `1m` or `1.05m` — GitHub's `max_context_window_tokens`), matching how the native `openai/*` and `anthropic/*` providers report these models and what the chat footer shows. GitHub's lower server-side prompt cap (`max_prompt_tokens`, for example `936k` or `922k`) is retained internally as the effective input budget that drives compaction thresholds and overflow recovery, and GitHub's live output cap (`max_output_tokens`) replaces Atomic's bundled `maxTokens` fallback for provider requests. If CAPI advertises `capabilities.supports.reasoning_effort` as an array, Atomic also gates `/model` and thinking-level cycling to only those live levels for both dynamic Copilot models and bundled `pi-ai` Copilot models; budget-only or boolean-only reasoning metadata leaves the existing thinking map untouched. Active interactive sessions refresh from this metadata as soon as the catalog is applied, so a startup fallback model does not keep stale reasoning levels until restart. This lets Atomic display the branded context window, request the catalog-advertised output budget, and avoid offering unsupported Copilot reasoning levels.
44
44
  - Selecting long context sets Atomic's displayed window to the model's full capacity while compaction triggers against the effective prompt-token budget, and makes Copilot requests include `X-GitHub-Api-Version: 2026-06-01`. Atomic does not send a body field, `contextTier`, or model-id variant; GitHub automatically applies the server-side `long_context` tier when prompt tokens exceed the default budget.
package/docs/settings.md CHANGED
@@ -21,6 +21,10 @@ If no extension or saved decision applies, `defaultProjectTrust` controls the fa
21
21
 
22
22
  Use `/trust` in interactive mode to save a project trust decision for future sessions, including trust for the immediate parent folder. It writes `~/.atomic/agent/trust.json` only; the current session is not reloaded, so restart Atomic for changes to take effect.
23
23
 
24
+ If a bare directory starts without trust-gated inputs, Atomic may run the interactive session as implicitly trusted. Inert state directories such as `.atomic/todos/` and `.atomic/sessions/` do not require trust and do not disable deferred resource startup. On the normal interactive TTY fast path, Atomic paints the shell and makes the input editor responsive before scanning bundled extension packages, skills, prompts, themes, context files, and system-prompt files. Deferred loading uses async filesystem discovery and async file reads with cooperative yields, so visible typing, Enter, Ctrl+C, rendering, and the normal prompt spinner remain responsive when a submitted prompt needs extension-dependent tools and prompt templates. Startup does not show a resource-loading spinner before the user submits a prompt. When resources finish loading, Atomic shows the normal resources disclosure so newly added skills, prompts, themes, and extensions are visible. If trust-requiring config appears later, Atomic prompts again on the next launch until you explicitly save a persistent trust decision; the only automatic persistence of implicit startup trust is the existing `/reload` flow after reload discovers trust-requiring resources in an already-trusted session.
25
+
26
+ Settings and trust JSON files may start with a UTF-8 BOM, as commonly written by older Windows tools; Atomic strips that leading marker before parsing.
27
+
24
28
  ## All Settings
25
29
 
26
30
  ### Model & Thinking
@@ -240,7 +244,7 @@ When multiple sources specify a session directory, precedence is `--session-dir`
240
244
 
241
245
  | Setting | Type | Default | Description |
242
246
  |---------|------|---------|-------------|
243
- | `enabledModels` | string[] | - | Model patterns for CTRL+P cycling (same format as `--models` CLI flag) |
247
+ | `enabledModels` | string[] | - | Model patterns for CTRL+P cycling (same format as `--models` CLI flag). In interactive TTY startup, these patterns are resolved again after deferred extension/resource loading so extension-provided providers can match without blocking first paint. |
244
248
  | `defaultContextWindow` | number \| string | model default | Optional global fallback context window for models that expose selectable context windows. Accepts raw token counts or compact labels such as `400k` and `1m`. Unsupported values are ignored for models that do not support them. |
245
249
  | `defaultContextWindows` | object | `{}` | Per-model preferred context windows keyed as `provider/modelId`. The interactive `/model` context picker writes this setting so a Copilot-specific prompt cap such as `936k` does not leak into Anthropic, Cursor, or other providers. |
246
250
 
package/docs/usage.md CHANGED
@@ -64,7 +64,7 @@ You can submit messages while the agent is still working:
64
64
 
65
65
  - **Enter** queues a steering message, delivered after the current assistant turn finishes executing its tool calls.
66
66
  - **ALT+Enter** queues a follow-up message, delivered after the agent finishes all work.
67
- - **Escape** aborts and restores queued messages to the editor.
67
+ - **Escape** or **Ctrl+C** aborts the running agent and restores queued messages to the editor. When idle, Ctrl+C clears the editor (press twice to exit).
68
68
  - **ALT+Up** retrieves queued messages back to the editor.
69
69
 
70
70
  On Windows Terminal, ALT+Enter is fullscreen by default. Remap it as described in [Terminal setup](/terminal-setup) if you want Atomic to receive the shortcut.
package/docs/workflows.md CHANGED
@@ -892,7 +892,7 @@ In non-interactive (`-p`, `--print`, or `--mode json`) sessions, named workflow
892
892
  /workflow reload
893
893
  ```
894
894
 
895
- Use `connect` for the workflow graph. Use `attach` when you want a chat pane for a specific stage. Attached stage chats capture mouse/trackpad wheel events by default so scrolling stays inside the active stage transcript or prompt instead of falling through to terminal/main-chat scrollback. Press `ctrl+t` inside an attached stage chat to toggle **copy mode**: copy mode disables workflow-chat mouse reporting so normal terminal/tmux text selection can work; press `ctrl+t` again to leave copy mode and restore transcript or prompt scrolling. While copy mode is on, wheel/trackpad gestures are handled by the terminal/tmux and may scroll terminal scrollback, so leave copy mode before using the wheel again. Use `interrupt`, `pause`, and `resume` for resumable live work; `resume` on a non-paused run reopens the saved snapshot or overlay. Use `kill` only when the run should be terminated; killed runs are retained in live history/status for read-only inspection. Use `/workflow reload` after adding, editing, installing, or removing workflow resources or package manifest workflow entries and you want Atomic to rediscover them in-process. `/workflow status` lists all retained active and terminal top-level runs by default; implementation-owned nested child runs are flattened into their parent workflow rather than listed separately. `/workflow status --all` is retained as a compatibility alias.
895
+ Use `connect` for the workflow graph. Use `attach` when you want a chat pane for a specific stage. Attached stage chats capture mouse/trackpad wheel events by default so scrolling stays inside the active stage transcript or prompt instead of falling through to terminal/main-chat scrollback. Live `subagent` tool calls in stage chats use the same single, parallel, and chain progress widgets as main chat, including after exiting and re-attaching to an in-flight stage; press Ctrl+O (the `app.tools.expand` binding) to expand live detail for every child, including current tool activity and artifact paths. Press `ctrl+t` inside an attached stage chat to toggle **copy mode**: copy mode disables workflow-chat mouse reporting so normal terminal/tmux text selection can work; press `ctrl+t` again to leave copy mode and restore transcript or prompt scrolling. While copy mode is on, wheel/trackpad gestures are handled by the terminal/tmux and may scroll terminal scrollback, so leave copy mode before using the wheel again. Use `interrupt`, `pause`, and `resume` for resumable live work; `resume` on a non-paused run reopens the saved snapshot or overlay. Use `kill` only when the run should be terminated; killed runs are retained in live history/status for read-only inspection. Use `/workflow reload` after adding, editing, installing, or removing workflow resources or package manifest workflow entries and you want Atomic to rediscover them in-process. `/workflow status` lists all retained active and terminal top-level runs by default; implementation-owned nested child runs are flattened into their parent workflow rather than listed separately. `/workflow status --all` is retained as a compatibility alias.
896
896
 
897
897
  <p align="center"><img src="images/workflow-graph.png" alt="Workflow Graph Viewer" width="600" /></p>
898
898