@bastani/atomic 0.9.16-alpha.6 → 0.9.16-alpha.8

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 (214) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +4 -4
  4. package/dist/builtin/intercom/broker/broker.ts +617 -22
  5. package/dist/builtin/intercom/broker/client-message-validation.ts +9 -0
  6. package/dist/builtin/intercom/broker/client.ts +200 -10
  7. package/dist/builtin/intercom/broker/pending-send-registry.ts +4 -0
  8. package/dist/builtin/intercom/broker/send-handler.ts +90 -36
  9. package/dist/builtin/intercom/broker/send-signature.ts +20 -8
  10. package/dist/builtin/intercom/group.ts +85 -0
  11. package/dist/builtin/intercom/index.bundle.mjs +680 -53
  12. package/dist/builtin/intercom/package.json +1 -1
  13. package/dist/builtin/intercom/session-target.ts +54 -0
  14. package/dist/builtin/intercom/skills/intercom/SKILL.md +18 -3
  15. package/dist/builtin/intercom/source-ownership.ts +31 -0
  16. package/dist/builtin/intercom/types.ts +115 -0
  17. package/dist/builtin/mcp/index.bundle.mjs +109 -35
  18. package/dist/builtin/mcp/package.json +1 -1
  19. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  20. package/dist/builtin/subagents/package.json +1 -1
  21. package/dist/builtin/subagents/skills/qlty/SKILL.md +123 -0
  22. package/dist/builtin/subagents/skills/qlty/references/coding-with-ai-agents.md +64 -0
  23. package/dist/builtin/subagents/skills/qlty/references/commands.md +291 -0
  24. package/dist/builtin/subagents/skills/qlty/references/plugins-and-extensions.md +228 -0
  25. package/dist/builtin/subagents/skills/qlty/references/quickstart.md +110 -0
  26. package/dist/builtin/subagents/src/extension/index.bundle.mjs +173 -51
  27. package/dist/builtin/web-access/index.bundle.mjs +109 -35
  28. package/dist/builtin/web-access/package.json +1 -1
  29. package/dist/builtin/workflows/CHANGELOG.md +23 -0
  30. package/dist/builtin/workflows/README.md +40 -37
  31. package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
  32. package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +137 -133
  33. package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
  34. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  35. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  36. package/dist/builtin/workflows/builtin/chunk-5n10x7j2.js +69730 -0
  37. package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
  38. package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
  39. package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
  40. package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
  41. package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
  42. package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
  43. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  44. package/dist/builtin/workflows/builtin/chunk-cdtd3m3w.js +529 -0
  45. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  46. package/dist/builtin/workflows/builtin/chunk-hdpj1dkw.js +2447 -0
  47. package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
  48. package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
  49. package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
  50. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  51. package/dist/builtin/workflows/builtin/chunk-v04c5qmd.js +1207 -0
  52. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  53. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  54. package/dist/builtin/workflows/builtin/chunk-zf28603f.js +1080 -0
  55. package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
  56. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
  57. package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
  58. package/dist/builtin/workflows/builtin/goal.js +16 -0
  59. package/dist/builtin/workflows/builtin/index.js +49 -0
  60. package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
  61. package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
  62. package/dist/builtin/workflows/builtin/ralph.js +16 -0
  63. package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
  64. package/dist/builtin/workflows/builtin/steering-context.js +7 -0
  65. package/dist/builtin/workflows/builtin/tournament.js +13 -0
  66. package/dist/builtin/workflows/package.json +10 -7
  67. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  68. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1250 -428
  69. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +956 -427
  70. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
  71. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
  72. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  73. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
  74. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  75. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  76. package/dist/core/agent-session-custom-message-commit.js +12 -5
  77. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  78. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  79. package/dist/core/agent-session-persistent-custom-messages.js +2 -0
  80. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  81. package/dist/core/agent-session.d.ts.map +1 -1
  82. package/dist/core/agent-session.js +2 -1
  83. package/dist/core/agent-session.js.map +1 -1
  84. package/dist/core/auth-storage.d.ts +2 -0
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +4 -0
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/builtin-install-layout.d.ts +1 -1
  89. package/dist/core/builtin-install-layout.d.ts.map +1 -1
  90. package/dist/core/builtin-install-layout.js +1 -1
  91. package/dist/core/builtin-install-layout.js.map +1 -1
  92. package/dist/core/codex-fast-mode.d.ts +10 -7
  93. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  94. package/dist/core/codex-fast-mode.js +31 -13
  95. package/dist/core/codex-fast-mode.js.map +1 -1
  96. package/dist/core/extensions/context-types.d.ts +35 -0
  97. package/dist/core/extensions/context-types.d.ts.map +1 -1
  98. package/dist/core/extensions/context-types.js.map +1 -1
  99. package/dist/core/extensions/index.d.ts +1 -1
  100. package/dist/core/extensions/index.d.ts.map +1 -1
  101. package/dist/core/extensions/index.js.map +1 -1
  102. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  104. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  105. package/dist/core/model-runtime.d.ts +2 -0
  106. package/dist/core/model-runtime.d.ts.map +1 -1
  107. package/dist/core/model-runtime.js +4 -0
  108. package/dist/core/model-runtime.js.map +1 -1
  109. package/dist/core/runtime-credentials.d.ts +1 -0
  110. package/dist/core/runtime-credentials.d.ts.map +1 -1
  111. package/dist/core/runtime-credentials.js +8 -0
  112. package/dist/core/runtime-credentials.js.map +1 -1
  113. package/dist/core/sdk.d.ts.map +1 -1
  114. package/dist/core/sdk.js +5 -5
  115. package/dist/core/sdk.js.map +1 -1
  116. package/dist/core/session-manager-storage.d.ts +2 -0
  117. package/dist/core/session-manager-storage.d.ts.map +1 -1
  118. package/dist/core/session-manager-storage.js +4 -2
  119. package/dist/core/session-manager-storage.js.map +1 -1
  120. package/dist/core/slash-commands.js +1 -1
  121. package/dist/core/slash-commands.js.map +1 -1
  122. package/dist/core/workflow-stage-admission.d.ts +6 -1
  123. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  124. package/dist/core/workflow-stage-admission.js +8 -9
  125. package/dist/core/workflow-stage-admission.js.map +1 -1
  126. package/dist/index.d.ts +0 -1
  127. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/footer.js +1 -1
  129. package/dist/modes/interactive/components/footer.js.map +1 -1
  130. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  131. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  132. package/dist/modes/interactive/interactive-startup.js +1 -1
  133. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  134. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  135. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  136. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  137. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  138. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  139. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  140. package/dist/modes/rpc/rpc-client-api.js +33 -5
  141. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  142. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  144. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  145. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  146. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-types.js.map +1 -1
  148. package/docs/intercom.md +15 -6
  149. package/docs/packages.md +2 -2
  150. package/docs/providers.md +8 -3
  151. package/docs/quickstart.md +3 -2
  152. package/docs/rpc.md +21 -4
  153. package/docs/settings.md +6 -4
  154. package/docs/skills.md +4 -0
  155. package/docs/subagents.md +1 -1
  156. package/docs/usage.md +1 -1
  157. package/docs/workflows.md +112 -239
  158. package/npm-shrinkwrap.json +32 -32
  159. package/package.json +9 -13
  160. package/dist/builtin/workflows/ambient.d.ts +0 -61
  161. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  162. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  163. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  164. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  165. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  166. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  167. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  168. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  169. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  170. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  171. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  172. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  173. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  174. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  175. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  176. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  177. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  178. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -211
  179. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  180. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  181. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  182. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  183. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  184. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  185. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  186. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  187. package/dist/builtin/workflows/builtin/index.ts +0 -16
  188. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  189. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  190. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  191. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  192. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  193. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  194. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  195. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  196. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  197. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  198. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  199. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  200. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  201. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  202. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  203. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -100
  204. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -455
  205. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  206. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  207. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  208. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  209. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  210. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  211. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  212. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  213. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  214. package/dist/builtin/workflows/builtin/verification-usage.ts +0 -44
@@ -0,0 +1,4110 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-5wgwscd0.js";
4
+
5
+ // src/utils/child-process.ts
6
+ import {
7
+ spawn as nodeSpawn,
8
+ spawnSync as nodeSpawnSync
9
+ } from "node:child_process";
10
+ import { basename } from "node:path";
11
+ import crossSpawn from "cross-spawn";
12
+
13
+ // src/utils/agent-attribution.ts
14
+ var ATOMIC_AI_AGENT = "atomic";
15
+
16
+ // src/utils/child-process.ts
17
+ var EXIT_STDIO_IDLE_GRACE_MS = 100;
18
+ var EXIT_STDIO_ACTIVE_DRAIN_HARD_CAP_MS = 5000;
19
+ var WINDOWS_EXIT_POLL_INTERVAL_MS = 50;
20
+ var WINDOWS_EXIT_CODE_GRACE_MS = 1000;
21
+ function createChildProcessEnvironment(overrides, baseEnv = process.env) {
22
+ return { ...baseEnv, ...overrides, AI_AGENT: ATOMIC_AI_AGENT };
23
+ }
24
+ var WINDOWS_SHELL_COMMANDS = new Set(["bun", "npm", "npx", "pnpm", "yarn", "yarnpkg", "corepack"]);
25
+ function shouldUseWindowsShell(command) {
26
+ if (process.platform !== "win32")
27
+ return false;
28
+ const commandName = basename(command).toLowerCase();
29
+ return commandName.endsWith(".cmd") || commandName.endsWith(".bat") || WINDOWS_SHELL_COMMANDS.has(commandName);
30
+ }
31
+ function spawnProcess(command, args, options) {
32
+ const childOptions = {
33
+ ...options,
34
+ env: options.env ? createChildProcessEnvironment(undefined, options.env) : createChildProcessEnvironment()
35
+ };
36
+ return process.platform === "win32" ? crossSpawn(command, args, childOptions) : nodeSpawn(command, args, childOptions);
37
+ }
38
+ function spawnProcessSync(command, args, options) {
39
+ const childOptions = {
40
+ ...options,
41
+ env: options.env ? createChildProcessEnvironment(undefined, options.env) : createChildProcessEnvironment()
42
+ };
43
+ return process.platform === "win32" ? crossSpawn.sync(command, args, childOptions) : nodeSpawnSync(command, args, childOptions);
44
+ }
45
+ function isWindowsProcessAlive(pid) {
46
+ const result = nodeSpawnSync("tasklist", ["/FI", `PID eq ${pid}`, "/NH"], {
47
+ encoding: "utf-8",
48
+ windowsHide: true,
49
+ env: createChildProcessEnvironment()
50
+ });
51
+ if (result.status !== 0)
52
+ return true;
53
+ return new RegExp(`\\b${pid}\\b`).test(result.stdout);
54
+ }
55
+ function waitForChildProcess(child, options = {}) {
56
+ return new Promise((resolve, reject) => {
57
+ let settled = false;
58
+ let exited = false;
59
+ let exitCode = null;
60
+ let postExitIdleTimer;
61
+ let postExitActiveDrainHardCapTimer;
62
+ let stdoutEnded = child.stdout === null;
63
+ let stderrEnded = child.stderr === null;
64
+ let windowsExitPoll;
65
+ let windowsExitCodeGraceTimer;
66
+ const platform = options.platform ?? process.platform;
67
+ const processAlive = options.isWindowsProcessAlive ?? isWindowsProcessAlive;
68
+ const windowsExitPollIntervalMs = options.windowsExitPollIntervalMs ?? WINDOWS_EXIT_POLL_INTERVAL_MS;
69
+ const windowsExitCodeGraceMs = options.windowsExitCodeGraceMs ?? WINDOWS_EXIT_CODE_GRACE_MS;
70
+ const cleanup = () => {
71
+ if (windowsExitPoll) {
72
+ clearInterval(windowsExitPoll);
73
+ windowsExitPoll = undefined;
74
+ }
75
+ if (postExitIdleTimer) {
76
+ clearTimeout(postExitIdleTimer);
77
+ postExitIdleTimer = undefined;
78
+ }
79
+ if (postExitActiveDrainHardCapTimer) {
80
+ clearTimeout(postExitActiveDrainHardCapTimer);
81
+ postExitActiveDrainHardCapTimer = undefined;
82
+ }
83
+ if (windowsExitCodeGraceTimer) {
84
+ clearTimeout(windowsExitCodeGraceTimer);
85
+ windowsExitCodeGraceTimer = undefined;
86
+ }
87
+ child.removeListener("error", onError);
88
+ child.removeListener("exit", onExit);
89
+ child.removeListener("close", onClose);
90
+ child.stdout?.removeListener("end", onStdoutEnd);
91
+ child.stderr?.removeListener("end", onStderrEnd);
92
+ child.stdout?.removeListener("data", onData);
93
+ child.stderr?.removeListener("data", onData);
94
+ };
95
+ const finalize = (code) => {
96
+ if (settled)
97
+ return;
98
+ settled = true;
99
+ cleanup();
100
+ child.stdout?.destroy();
101
+ child.stderr?.destroy();
102
+ resolve(code);
103
+ };
104
+ const maybeFinalizeAfterExit = () => {
105
+ if (!exited || settled)
106
+ return;
107
+ if (stdoutEnded && stderrEnded) {
108
+ finalize(exitCode);
109
+ }
110
+ };
111
+ const armIdleTimer = () => {
112
+ if (postExitIdleTimer)
113
+ clearTimeout(postExitIdleTimer);
114
+ postExitIdleTimer = setTimeout(() => finalize(exitCode), EXIT_STDIO_IDLE_GRACE_MS);
115
+ };
116
+ const armActiveDrainHardCapTimer = () => {
117
+ if (postExitActiveDrainHardCapTimer)
118
+ return;
119
+ postExitActiveDrainHardCapTimer = setTimeout(() => finalize(exitCode), EXIT_STDIO_ACTIVE_DRAIN_HARD_CAP_MS);
120
+ };
121
+ const onData = () => {
122
+ if (exited && !settled)
123
+ armIdleTimer();
124
+ };
125
+ const onStdoutEnd = () => {
126
+ stdoutEnded = true;
127
+ maybeFinalizeAfterExit();
128
+ };
129
+ const onStderrEnd = () => {
130
+ stderrEnded = true;
131
+ maybeFinalizeAfterExit();
132
+ };
133
+ const onError = (err) => {
134
+ if (settled)
135
+ return;
136
+ settled = true;
137
+ cleanup();
138
+ reject(err);
139
+ };
140
+ const onExit = (code) => {
141
+ if (windowsExitCodeGraceTimer) {
142
+ clearTimeout(windowsExitCodeGraceTimer);
143
+ windowsExitCodeGraceTimer = undefined;
144
+ }
145
+ exited = true;
146
+ exitCode = code;
147
+ maybeFinalizeAfterExit();
148
+ if (!settled) {
149
+ armActiveDrainHardCapTimer();
150
+ armIdleTimer();
151
+ }
152
+ };
153
+ const armWindowsExitCodeGraceTimer = () => {
154
+ if (windowsExitCodeGraceTimer)
155
+ return;
156
+ if (windowsExitPoll) {
157
+ clearInterval(windowsExitPoll);
158
+ windowsExitPoll = undefined;
159
+ }
160
+ windowsExitCodeGraceTimer = setTimeout(() => {
161
+ windowsExitCodeGraceTimer = undefined;
162
+ onExit(child.exitCode ?? 0);
163
+ }, windowsExitCodeGraceMs);
164
+ };
165
+ const pollWindowsExit = () => {
166
+ if (platform !== "win32" || !child.pid || exited || settled)
167
+ return;
168
+ if (!processAlive(child.pid)) {
169
+ if (child.exitCode !== null) {
170
+ onExit(child.exitCode);
171
+ } else {
172
+ armWindowsExitCodeGraceTimer();
173
+ }
174
+ }
175
+ };
176
+ const onClose = (code) => {
177
+ finalize(code);
178
+ };
179
+ if (platform === "win32" && child.pid) {
180
+ windowsExitPoll = setInterval(pollWindowsExit, windowsExitPollIntervalMs);
181
+ }
182
+ child.stdout?.once("end", onStdoutEnd);
183
+ child.stderr?.once("end", onStderrEnd);
184
+ child.stdout?.on("data", onData);
185
+ child.stderr?.on("data", onData);
186
+ child.once("error", onError);
187
+ child.once("exit", onExit);
188
+ child.once("close", onClose);
189
+ });
190
+ }
191
+
192
+ // src/utils/paths.ts
193
+ import { realpathSync } from "node:fs";
194
+ import { homedir } from "node:os";
195
+ import { isAbsolute, join, resolve as nodeResolvePath, relative, sep } from "node:path";
196
+ import { fileURLToPath } from "node:url";
197
+ var UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
198
+ function getHomeDir() {
199
+ if (process.platform === "win32") {
200
+ if (process.env.USERPROFILE)
201
+ return process.env.USERPROFILE;
202
+ if (process.env.HOMEDRIVE && process.env.HOMEPATH)
203
+ return `${process.env.HOMEDRIVE}${process.env.HOMEPATH}`;
204
+ if (process.env.HOME)
205
+ return process.env.HOME;
206
+ return homedir();
207
+ }
208
+ return process.env.HOME || process.env.USERPROFILE || homedir();
209
+ }
210
+ function canonicalizePath(path) {
211
+ try {
212
+ return realpathSync(path);
213
+ } catch {
214
+ return path;
215
+ }
216
+ }
217
+ function isLocalPath(value) {
218
+ const trimmed = value.trim();
219
+ if (trimmed.startsWith("npm:") || trimmed.startsWith("git:") || trimmed.startsWith("github:") || trimmed.startsWith("http:") || trimmed.startsWith("https:") || trimmed.startsWith("ssh:")) {
220
+ return false;
221
+ }
222
+ return true;
223
+ }
224
+ function normalizeWindowsShellPath(filePath) {
225
+ if (!filePath.startsWith("/") || filePath.startsWith("//") || filePath.includes("\\"))
226
+ return filePath;
227
+ const match = filePath.match(/^\/(?:mnt\/|cygdrive\/)?([a-z])(?:\/(.*))?$/i);
228
+ if (!match)
229
+ return filePath;
230
+ const suffix = match[2]?.replaceAll("/", "\\");
231
+ return `${match[1].toUpperCase()}:\\${suffix ?? ""}`;
232
+ }
233
+ function normalizePath(input, options = {}) {
234
+ let normalized = options.trim ? input.trim() : input;
235
+ if (options.normalizeUnicodeSpaces) {
236
+ normalized = normalized.replace(UNICODE_SPACES, " ");
237
+ }
238
+ if (options.stripAtPrefix && normalized.startsWith("@")) {
239
+ normalized = normalized.slice(1);
240
+ }
241
+ if (process.platform === "win32") {
242
+ normalized = normalizeWindowsShellPath(normalized);
243
+ }
244
+ if (options.expandTilde ?? true) {
245
+ const home = options.homeDir ?? getHomeDir();
246
+ if (normalized === "~")
247
+ return home;
248
+ if (normalized.startsWith("~/") || process.platform === "win32" && normalized.startsWith("~\\")) {
249
+ return join(home, normalized.slice(2));
250
+ }
251
+ }
252
+ if (/^file:\/\//.test(normalized)) {
253
+ decodeURI(normalized);
254
+ return fileURLToPath(normalized);
255
+ }
256
+ return normalized;
257
+ }
258
+ function resolvePath(input, baseDir = process.cwd(), options = {}) {
259
+ const normalized = normalizePath(input, options);
260
+ const normalizedBaseDir = normalizePath(baseDir);
261
+ return isAbsolute(normalized) ? nodeResolvePath(normalized) : nodeResolvePath(normalizedBaseDir, normalized);
262
+ }
263
+ function getCwdRelativePath(filePath, cwd) {
264
+ const resolvedCwd = resolvePath(cwd);
265
+ const resolvedPath = resolvePath(filePath, resolvedCwd);
266
+ const relativePath = relative(resolvedCwd, resolvedPath);
267
+ const isInsideCwd = relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
268
+ return isInsideCwd ? relativePath || "." : undefined;
269
+ }
270
+ function formatPathRelativeToCwdOrAbsolute(filePath, cwd) {
271
+ const absolutePath = resolvePath(filePath, cwd);
272
+ return (getCwdRelativePath(absolutePath, cwd) ?? absolutePath).split(sep).join("/");
273
+ }
274
+ function markPathIgnoredByCloudSync(path) {
275
+ const attrs = process.platform === "darwin" ? ["com.dropbox.ignored", "com.apple.fileprovider.ignore#P"] : process.platform === "linux" ? ["user.com.dropbox.ignored"] : [];
276
+ for (const attr of attrs) {
277
+ if (process.platform === "darwin") {
278
+ spawnProcessSync("xattr", ["-w", attr, "1", path], { encoding: "utf-8", stdio: "ignore" });
279
+ } else {
280
+ spawnProcessSync("setfattr", ["-n", attr, "-v", "1", path], { encoding: "utf-8", stdio: "ignore" });
281
+ }
282
+ }
283
+ }
284
+
285
+ // src/config.ts
286
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
287
+ import { dirname as dirname4, join as join5, resolve as resolve2 } from "path";
288
+
289
+ // src/config-package-identity.ts
290
+ import { existsSync, readFileSync } from "fs";
291
+ import { dirname, join as join2 } from "path";
292
+
293
+ // src/core/builtin-install-layout.ts
294
+ var BUILTIN_PACKAGE_DIR_NAMES = ["workflows", "subagents", "mcp", "web-access", "intercom"];
295
+ var SOURCE_EXTENSION_ENTRIES = {
296
+ workflows: "src/extension/index.ts",
297
+ subagents: "src/extension/index.ts",
298
+ mcp: "index.ts",
299
+ "web-access": "index.ts",
300
+ intercom: "index.ts"
301
+ };
302
+ var INSTALLED_EXTENSION_ENTRIES = {
303
+ workflows: "src/extension/index.bundle.mjs",
304
+ subagents: "src/extension/index.bundle.mjs",
305
+ mcp: "index.bundle.mjs",
306
+ "web-access": "index.bundle.mjs",
307
+ intercom: "index.bundle.mjs"
308
+ };
309
+ function requiredEntriesForBuiltin(dirName) {
310
+ return [INSTALLED_EXTENSION_ENTRIES[dirName], SOURCE_EXTENSION_ENTRIES[dirName]];
311
+ }
312
+
313
+ // src/utils/text.ts
314
+ function splitBom(content) {
315
+ return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
316
+ }
317
+ function stripBom(content) {
318
+ return splitBom(content).text;
319
+ }
320
+
321
+ // src/config-package-identity.ts
322
+ var COMPANION_BUILTIN_PACKAGE_NAMES = BUILTIN_PACKAGE_DIR_NAMES.map((dirName) => `@bastani/${dirName}`);
323
+ function isCompanionBuiltinPackageName(name) {
324
+ return name !== undefined && COMPANION_BUILTIN_PACKAGE_NAMES.includes(name);
325
+ }
326
+ function packageJsonDefinesAppIdentity(pkg) {
327
+ if (pkg.name === "@bastani/atomic" || pkg.name === "@mariozechner/pi")
328
+ return true;
329
+ return pkg.atomicConfig !== undefined || pkg.piConfig !== undefined;
330
+ }
331
+ function resolvePackageDirFrom(startDir) {
332
+ let dir = startDir;
333
+ let firstPackageDir;
334
+ while (dir !== dirname(dir)) {
335
+ const packageJsonPath = join2(dir, "package.json");
336
+ if (existsSync(packageJsonPath)) {
337
+ firstPackageDir ??= dir;
338
+ if (shouldUsePackageDir(readPackageIdentity(packageJsonPath))) {
339
+ return dir;
340
+ }
341
+ }
342
+ dir = dirname(dir);
343
+ }
344
+ return firstPackageDir ?? startDir;
345
+ }
346
+ function shouldUsePackageDir(pkg) {
347
+ if (packageJsonDefinesAppIdentity(pkg))
348
+ return true;
349
+ return !isCompanionBuiltinPackageName(pkg.name);
350
+ }
351
+ function isJsonValue(value) {
352
+ if (value === null || typeof value === "boolean" || typeof value === "string")
353
+ return true;
354
+ if (typeof value === "number")
355
+ return Number.isFinite(value);
356
+ if (Array.isArray(value))
357
+ return value.every(isJsonValue);
358
+ if (typeof value !== "object" || Object.getPrototypeOf(value) !== Object.prototype)
359
+ return false;
360
+ return Object.values(value).every(isJsonValue);
361
+ }
362
+ function isJsonObject(value) {
363
+ return value !== null && typeof value === "object" && !Array.isArray(value);
364
+ }
365
+ function readPackageIdentity(packageJsonPath) {
366
+ try {
367
+ const parsed = JSON.parse(stripBom(readFileSync(packageJsonPath, "utf-8")));
368
+ if (!isJsonValue(parsed) || !isJsonObject(parsed))
369
+ return {};
370
+ const name = parsed.name;
371
+ const atomicConfig = parsed.atomicConfig;
372
+ const piConfig = parsed.piConfig;
373
+ return {
374
+ ...typeof name === "string" ? { name } : {},
375
+ ...atomicConfig !== undefined && isJsonObject(atomicConfig) ? { atomicConfig } : {},
376
+ ...piConfig !== undefined && isJsonObject(piConfig) ? { piConfig } : {}
377
+ };
378
+ } catch {}
379
+ return {};
380
+ }
381
+
382
+ // src/utils/split-launcher.ts
383
+ import { dirname as dirname2, join as join3 } from "node:path";
384
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
385
+ function isSplitLauncherRuntime() {
386
+ return process.env.ATOMIC_CODING_AGENT === "true" && /(?:^|[\\/])atomic(?:\.exe)?$/i.test(process.execPath);
387
+ }
388
+ function splitLauncherDir() {
389
+ return dirname2(process.execPath);
390
+ }
391
+ function moduleDirFromMetaUrl(metaUrl, ...execRelativeSegments) {
392
+ try {
393
+ return dirname2(fileURLToPath2(metaUrl));
394
+ } catch (error) {
395
+ if (isSplitLauncherRuntime())
396
+ return join3(splitLauncherDir(), ...execRelativeSegments);
397
+ throw error;
398
+ }
399
+ }
400
+ function moduleFileFromMetaUrl(metaUrl, execRelativeFile) {
401
+ try {
402
+ return fileURLToPath2(metaUrl);
403
+ } catch (error) {
404
+ if (isSplitLauncherRuntime())
405
+ return join3(splitLauncherDir(), execRelativeFile);
406
+ throw error;
407
+ }
408
+ }
409
+
410
+ // src/config-self-update.ts
411
+ import { accessSync, constants, existsSync as existsSync2, realpathSync as realpathSync2 } from "fs";
412
+ import { homedir as homedir2 } from "os";
413
+ import { basename as basename2, dirname as dirname3, join as join4, resolve, sep as sep2, win32 } from "path";
414
+ function normalizeSelfUpdateTarget(target) {
415
+ if (typeof target === "string")
416
+ return { packageName: target, installSpec: target };
417
+ return { packageName: target.packageName, installSpec: target.installSpec ?? target.packageName };
418
+ }
419
+ function makeSelfUpdateCommand(installStep, uninstallStep) {
420
+ if (!uninstallStep)
421
+ return installStep;
422
+ return {
423
+ ...installStep,
424
+ display: `${uninstallStep.display} && ${installStep.display}`,
425
+ steps: [uninstallStep, installStep]
426
+ };
427
+ }
428
+ function makeSelfUpdateCommandStep(command, args) {
429
+ return {
430
+ command,
431
+ args,
432
+ display: [command, ...args].map((arg) => /\s/.test(arg) ? `"${arg}"` : arg).join(" ")
433
+ };
434
+ }
435
+ function detectInstallMethodForRuntime(runtime) {
436
+ if (runtime.isBunBinary) {
437
+ return "bun-binary";
438
+ }
439
+ const resolvedPath = `${runtime.moduleDir}\x00${process.execPath || ""}`.toLowerCase().replace(/\\/g, "/");
440
+ if (resolvedPath.includes("/pnpm/") || resolvedPath.includes("/.pnpm/")) {
441
+ return "pnpm";
442
+ }
443
+ if (resolvedPath.includes("/yarn/") || resolvedPath.includes("/.yarn/")) {
444
+ return "yarn";
445
+ }
446
+ if (resolvedPath.includes("/install/global/node_modules/")) {
447
+ return "bun";
448
+ }
449
+ if (resolvedPath.includes("/npm/") || resolvedPath.includes("/node_modules/")) {
450
+ return "npm";
451
+ }
452
+ return "unknown";
453
+ }
454
+ function getInferredNpmInstall(runtime) {
455
+ const packageDir = runtime.getPackageDir();
456
+ const path = process.platform === "win32" || packageDir.includes("\\") ? win32 : { basename: basename2, dirname: dirname3 };
457
+ const parent = path.dirname(packageDir);
458
+ let root;
459
+ if (path.basename(parent).startsWith("@") && path.basename(path.dirname(parent)) === "node_modules") {
460
+ root = path.dirname(parent);
461
+ } else if (path.basename(parent) === "node_modules") {
462
+ root = parent;
463
+ }
464
+ if (!root)
465
+ return;
466
+ const rootParent = path.dirname(root);
467
+ if (path.basename(rootParent) === "lib")
468
+ return { root, prefix: path.dirname(rootParent) };
469
+ return;
470
+ }
471
+ function getSelfUpdateCommandForMethod(runtime, method, installedPackageName, updatePackageName = installedPackageName, npmCommand, installSpec = updatePackageName) {
472
+ switch (method) {
473
+ case "bun-binary":
474
+ return;
475
+ case "pnpm": {
476
+ const match = readCommandOutput("pnpm", ["root", "-g"]) ? undefined : /^(.*[\\/]global[\\/][^\\/]+)[\\/]\.pnpm[\\/]/.exec(runtime.getPackageDir());
477
+ const binDirArgs = match ? [`--config.global-bin-dir=${process.env.PNPM_HOME || dirname3(dirname3(match[1]))}`] : [];
478
+ return makeSelfUpdateCommand(makeSelfUpdateCommandStep("pnpm", [
479
+ "install",
480
+ "-g",
481
+ "--ignore-scripts",
482
+ "--config.minimumReleaseAge=0",
483
+ ...binDirArgs,
484
+ installSpec
485
+ ]), updatePackageName === installedPackageName ? undefined : makeSelfUpdateCommandStep("pnpm", ["remove", "-g", ...binDirArgs, installedPackageName]));
486
+ }
487
+ case "yarn":
488
+ return makeSelfUpdateCommand(makeSelfUpdateCommandStep("yarn", ["global", "add", "--ignore-scripts", installSpec]), updatePackageName === installedPackageName ? undefined : makeSelfUpdateCommandStep("yarn", ["global", "remove", installedPackageName]));
489
+ case "bun":
490
+ return makeSelfUpdateCommand(makeSelfUpdateCommandStep("bun", [
491
+ "install",
492
+ "-g",
493
+ "--ignore-scripts",
494
+ "--minimum-release-age=0",
495
+ installSpec
496
+ ]), updatePackageName === installedPackageName ? undefined : makeSelfUpdateCommandStep("bun", ["uninstall", "-g", installedPackageName]));
497
+ case "npm": {
498
+ const [command = "npm", ...npmArgs] = npmCommand ?? [];
499
+ const inferred = npmCommand?.length ? undefined : getInferredNpmInstall(runtime);
500
+ const prefixArgs = [...npmArgs, ...inferred ? ["--prefix", inferred.prefix] : []];
501
+ const installStep = makeSelfUpdateCommandStep(command, [
502
+ ...prefixArgs,
503
+ "install",
504
+ "-g",
505
+ "--ignore-scripts",
506
+ "--min-release-age=0",
507
+ installSpec
508
+ ]);
509
+ const uninstallStep = updatePackageName === installedPackageName ? undefined : makeSelfUpdateCommandStep(command, [...prefixArgs, "uninstall", "-g", installedPackageName]);
510
+ return makeSelfUpdateCommand(installStep, uninstallStep);
511
+ }
512
+ case "unknown":
513
+ return;
514
+ }
515
+ }
516
+ function readCommandOutput(command, args, options = {}) {
517
+ const result = spawnProcessSync(command, args, {
518
+ encoding: "utf-8",
519
+ stdio: ["ignore", "pipe", "pipe"],
520
+ env: process.env,
521
+ shell: shouldUseWindowsShell(command)
522
+ });
523
+ if (result.status === 0)
524
+ return result.stdout.trim() || undefined;
525
+ if (options.requireSuccess) {
526
+ const reason = result.error?.message || result.stderr.trim() || `exit code ${result.status ?? "unknown"}`;
527
+ throw new Error(`Failed to run ${[command, ...args].join(" ")}: ${reason}`);
528
+ }
529
+ return;
530
+ }
531
+ function getGlobalPackageRoots(runtime, method, _packageName, npmCommand) {
532
+ switch (method) {
533
+ case "npm": {
534
+ const configured = !!npmCommand?.length;
535
+ const [command = "npm", ...npmArgs] = npmCommand ?? [];
536
+ if (configured && command === "bun") {
537
+ const bunBin = readCommandOutput(command, [...npmArgs, "pm", "bin", "-g"], {
538
+ requireSuccess: true
539
+ });
540
+ const roots = [join4(homedir2(), ".bun", "install", "global", "node_modules")];
541
+ if (bunBin) {
542
+ roots.push(join4(dirname3(bunBin), "install", "global", "node_modules"));
543
+ }
544
+ return roots;
545
+ }
546
+ const root = readCommandOutput(command, [...npmArgs, "root", "-g"], {
547
+ requireSuccess: configured
548
+ });
549
+ const inferred = configured ? undefined : getInferredNpmInstall(runtime);
550
+ return [root, inferred?.root].filter((x) => !!x);
551
+ }
552
+ case "pnpm": {
553
+ const root = readCommandOutput("pnpm", ["root", "-g"]);
554
+ if (root)
555
+ return [root, dirname3(root)];
556
+ const match = /^(.*[\\/]global[\\/][^\\/]+)[\\/]\.pnpm[\\/]/.exec(runtime.getPackageDir());
557
+ return match ? [match[1]] : [];
558
+ }
559
+ case "yarn": {
560
+ const dir = readCommandOutput("yarn", ["global", "dir"]);
561
+ return dir ? [dir, join4(dir, "node_modules")] : [];
562
+ }
563
+ case "bun": {
564
+ const bunBin = readCommandOutput("bun", ["pm", "bin", "-g"]);
565
+ const roots = [join4(homedir2(), ".bun", "install", "global", "node_modules")];
566
+ if (bunBin) {
567
+ roots.push(join4(dirname3(bunBin), "install", "global", "node_modules"));
568
+ }
569
+ return roots;
570
+ }
571
+ case "bun-binary":
572
+ case "unknown":
573
+ return [];
574
+ }
575
+ }
576
+ function normalizeExistingPathForComparison(path, resolveSymlinks) {
577
+ const resolvedPath = resolve(path);
578
+ if (!existsSync2(resolvedPath)) {
579
+ return;
580
+ }
581
+ let normalizedPath = resolvedPath;
582
+ if (resolveSymlinks) {
583
+ try {
584
+ normalizedPath = realpathSync2(resolvedPath);
585
+ } catch {
586
+ return;
587
+ }
588
+ }
589
+ if (process.platform === "win32") {
590
+ normalizedPath = normalizedPath.toLowerCase();
591
+ }
592
+ return normalizedPath;
593
+ }
594
+ function getPathComparisonCandidates(path) {
595
+ return Array.from(new Set([normalizeExistingPathForComparison(path, false), normalizeExistingPathForComparison(path, true)].filter((candidate) => !!candidate)));
596
+ }
597
+ function getEntrypointPackageDir() {
598
+ const entrypoint = process.argv[1];
599
+ if (!entrypoint)
600
+ return;
601
+ let dir = dirname3(entrypoint);
602
+ while (dir !== dirname3(dir)) {
603
+ if (existsSync2(join4(dir, "package.json"))) {
604
+ return dir;
605
+ }
606
+ dir = dirname3(dir);
607
+ }
608
+ return;
609
+ }
610
+ function isSelfUpdatePathWritable(runtime) {
611
+ const packageDir = runtime.getPackageDir();
612
+ try {
613
+ accessSync(packageDir, constants.W_OK);
614
+ accessSync(dirname3(packageDir), constants.W_OK);
615
+ return true;
616
+ } catch {
617
+ return false;
618
+ }
619
+ }
620
+ function isManagedByGlobalPackageManager(runtime, method, packageName, npmCommand) {
621
+ const packageDirs = [runtime.getPackageDir(), getEntrypointPackageDir()].filter((dir) => !!dir);
622
+ const packageDirCandidates = packageDirs.flatMap((dir) => getPathComparisonCandidates(dir));
623
+ return getGlobalPackageRoots(runtime, method, packageName, npmCommand).some((root) => {
624
+ return getPathComparisonCandidates(root).some((normalizedRoot) => {
625
+ const rootPrefix = normalizedRoot.endsWith(sep2) ? normalizedRoot : `${normalizedRoot}${sep2}`;
626
+ return packageDirCandidates.some((packageDir) => packageDir.startsWith(rootPrefix));
627
+ });
628
+ });
629
+ }
630
+ function getSelfUpdateCommandForRuntime(runtime, packageName, npmCommand, updateTarget = packageName) {
631
+ const method = detectInstallMethodForRuntime(runtime);
632
+ const target = normalizeSelfUpdateTarget(updateTarget);
633
+ const command = getSelfUpdateCommandForMethod(runtime, method, packageName, target.packageName, npmCommand, target.installSpec);
634
+ if (!command || !isManagedByGlobalPackageManager(runtime, method, packageName, npmCommand) || !isSelfUpdatePathWritable(runtime)) {
635
+ return;
636
+ }
637
+ return command;
638
+ }
639
+ function getSelfUpdateUnavailableInstructionForRuntime(runtime, packageName, npmCommand, updateTarget = packageName) {
640
+ const method = detectInstallMethodForRuntime(runtime);
641
+ if (method === "bun-binary") {
642
+ return `Download from: https://github.com/bastani-inc/atomic/releases/latest`;
643
+ }
644
+ const target = normalizeSelfUpdateTarget(updateTarget);
645
+ const command = getSelfUpdateCommandForMethod(runtime, method, packageName, target.packageName, npmCommand, target.installSpec);
646
+ if (command) {
647
+ if (isManagedByGlobalPackageManager(runtime, method, packageName, npmCommand) && !isSelfUpdatePathWritable(runtime)) {
648
+ return `This installation is managed by a global ${method} install, but the install path is not writable. Update it yourself with: ${command.display}`;
649
+ }
650
+ return `This installation is not managed by a global ${method} install. Update it with the package manager, wrapper, or source checkout that provides it.`;
651
+ }
652
+ return `Update ${target.installSpec} using the package manager, wrapper, or source checkout that provides this installation.`;
653
+ }
654
+
655
+ // src/config.ts
656
+ var __filename2 = moduleFileFromMetaUrl(import.meta.url, "app.js");
657
+ var __dirname2 = dirname4(__filename2);
658
+ var bunFsMarkers = ["$bunfs", "~BUN", "%7EBUN"];
659
+ var isBunBinary = isSplitLauncherRuntime() || [import.meta.url, process.argv[1] ?? ""].some((candidate) => bunFsMarkers.some((marker) => candidate.includes(marker)));
660
+ var isBundledBuild = process.env.ATOMIC_BUNDLED_BUILD === "1";
661
+ var isBunRuntime = !!process.versions.bun;
662
+ function selfUpdateRuntime() {
663
+ return {
664
+ isBunBinary,
665
+ isBunRuntime,
666
+ moduleDir: __dirname2,
667
+ getPackageDir
668
+ };
669
+ }
670
+ function detectInstallMethod() {
671
+ return detectInstallMethodForRuntime(selfUpdateRuntime());
672
+ }
673
+ function getSelfUpdateCommand(packageName, npmCommand, updateTarget = { packageName, installSpec: packageName }) {
674
+ return getSelfUpdateCommandForRuntime(selfUpdateRuntime(), packageName, npmCommand, updateTarget);
675
+ }
676
+ function getSelfUpdateUnavailableInstruction(packageName, npmCommand, updateTarget = { packageName, installSpec: packageName }) {
677
+ return getSelfUpdateUnavailableInstructionForRuntime(selfUpdateRuntime(), packageName, npmCommand, updateTarget);
678
+ }
679
+ function getPackageDir() {
680
+ const envDir = process.env.ATOMIC_PACKAGE_DIR ?? process.env.PI_PACKAGE_DIR;
681
+ if (envDir) {
682
+ return normalizePath(envDir);
683
+ }
684
+ if (isBunBinary) {
685
+ return dirname4(process.execPath);
686
+ }
687
+ return resolvePackageDirFrom(__dirname2);
688
+ }
689
+ function getModuleAssetRoot() {
690
+ const hasPackageDirOverride = !!(process.env.ATOMIC_PACKAGE_DIR || process.env.PI_PACKAGE_DIR);
691
+ if (!isBundledBuild && !hasPackageDirOverride) {
692
+ return __dirname2;
693
+ }
694
+ const packageDir = getPackageDir();
695
+ return join5(packageDir, existsSync3(join5(packageDir, "src")) ? "src" : "dist");
696
+ }
697
+ function getThemesDir() {
698
+ if (isBunBinary) {
699
+ return join5(getPackageDir(), "theme");
700
+ }
701
+ return join5(getModuleAssetRoot(), "modes", "interactive", "theme");
702
+ }
703
+ function getExportTemplateDir() {
704
+ if (isBunBinary) {
705
+ return join5(getPackageDir(), "export-html");
706
+ }
707
+ return join5(getModuleAssetRoot(), "core", "export-html");
708
+ }
709
+ function getPackageJsonPath() {
710
+ return join5(getPackageDir(), "package.json");
711
+ }
712
+ function getReadmePath() {
713
+ return resolve2(join5(getPackageDir(), "README.md"));
714
+ }
715
+ function getDocsPath() {
716
+ return resolve2(join5(getPackageDir(), "docs"));
717
+ }
718
+ function getExamplesPath() {
719
+ return resolve2(join5(getPackageDir(), "examples"));
720
+ }
721
+ function getChangelogPath() {
722
+ return resolve2(join5(getPackageDir(), "CHANGELOG.md"));
723
+ }
724
+ function getInteractiveAssetsDir() {
725
+ if (isBunBinary) {
726
+ return join5(getPackageDir(), "assets");
727
+ }
728
+ return join5(getModuleAssetRoot(), "modes", "interactive", "assets");
729
+ }
730
+ function getBundledInteractiveAssetPath(name) {
731
+ return join5(getInteractiveAssetsDir(), name);
732
+ }
733
+ var pkg = {};
734
+ try {
735
+ pkg = JSON.parse(stripBom(readFileSync2(getPackageJsonPath(), "utf-8")));
736
+ } catch (e) {
737
+ const err = e;
738
+ if (err.code !== "ENOENT")
739
+ throw e;
740
+ }
741
+ function appNameFromPackageName(packageName) {
742
+ const localName = packageName?.split("/").pop()?.trim();
743
+ return localName && localName.length > 0 ? localName : undefined;
744
+ }
745
+ function readAppConfig(packageJson, appName) {
746
+ if (appName) {
747
+ const appConfig = packageJson[`${appName}Config`];
748
+ if (appConfig && typeof appConfig === "object" && !Array.isArray(appConfig)) {
749
+ return appConfig;
750
+ }
751
+ }
752
+ return packageJson.piConfig;
753
+ }
754
+ var PACKAGE_NAME = pkg.name || "@bastani/atomic";
755
+ var packageAppName = appNameFromPackageName(PACKAGE_NAME);
756
+ var appConfig = readAppConfig(pkg, packageAppName);
757
+ var APP_NAME = appConfig?.name || packageAppName || "pi";
758
+ var APP_TITLE = appConfig?.name !== undefined || APP_NAME !== "pi" ? APP_NAME : "π";
759
+ var CONFIG_DIR_NAME = appConfig?.configDir || (APP_NAME === "pi" ? ".pi" : `.${APP_NAME}`);
760
+ var LEGACY_CONFIG_DIR_NAME = ".pi";
761
+ var CONFIG_DIR_NAMES = CONFIG_DIR_NAME === LEGACY_CONFIG_DIR_NAME ? [CONFIG_DIR_NAME] : [CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAME];
762
+ var VERSION = pkg.version || "0.0.0";
763
+ var CHANGELOG_URL = appConfig?.changelogUrl?.trim() || undefined;
764
+ var ENV_PREFIX = APP_NAME.toUpperCase();
765
+ var LEGACY_ENV_PREFIX = "PI";
766
+ var ENV_AGENT_DIR = `${ENV_PREFIX}_CODING_AGENT_DIR`;
767
+ var ENV_SESSION_DIR = `${ENV_PREFIX}_CODING_AGENT_SESSION_DIR`;
768
+ var ENV_PACKAGE_DIR = `${ENV_PREFIX}_PACKAGE_DIR`;
769
+ var ENV_OFFLINE = `${ENV_PREFIX}_OFFLINE`;
770
+ var ENV_SKIP_VERSION_CHECK = `${ENV_PREFIX}_SKIP_VERSION_CHECK`;
771
+ var ENV_STARTUP_BENCHMARK = `${ENV_PREFIX}_STARTUP_BENCHMARK`;
772
+ var ENV_TELEMETRY = `${ENV_PREFIX}_TELEMETRY`;
773
+ var ENV_SHARE_VIEWER_URL = `${ENV_PREFIX}_SHARE_VIEWER_URL`;
774
+ var ENV_CLEAR_ON_SHRINK = `${ENV_PREFIX}_CLEAR_ON_SHRINK`;
775
+ var ENV_HARDWARE_CURSOR = `${ENV_PREFIX}_HARDWARE_CURSOR`;
776
+ var ENV_TIMING = `${ENV_PREFIX}_TIMING`;
777
+ var ENV_CODEX_FAST_MODE = `${ENV_PREFIX}_CODEX_FAST_MODE`;
778
+ var WORKFLOW_STAGE_SUBAGENT_GUARD_ENV = `${ENV_PREFIX}_WORKFLOW_STAGE_SUBAGENT_GUARD`;
779
+ function parseCodexFastModeEnvBoolean(value) {
780
+ switch (value?.trim().toLowerCase()) {
781
+ case "1":
782
+ case "true":
783
+ case "enabled":
784
+ case "on":
785
+ return true;
786
+ case "0":
787
+ case "false":
788
+ case "disabled":
789
+ case "off":
790
+ return false;
791
+ default:
792
+ return;
793
+ }
794
+ }
795
+ function serializeCodexFastModeEnvironmentSettings(settings) {
796
+ return `chat=${settings.chat ? "1" : "0"};workflow=${settings.workflow ? "1" : "0"}`;
797
+ }
798
+ function parseCodexFastModeEnvironmentSettings(value) {
799
+ if (!value)
800
+ return;
801
+ const settings = {};
802
+ for (const part of value.split(/[;,]/)) {
803
+ const separatorIndex = part.indexOf("=");
804
+ if (separatorIndex === -1)
805
+ continue;
806
+ const key = part.slice(0, separatorIndex).trim();
807
+ const parsedValue = parseCodexFastModeEnvBoolean(part.slice(separatorIndex + 1));
808
+ if (parsedValue === undefined)
809
+ continue;
810
+ if (key === "chat")
811
+ settings.chat = parsedValue;
812
+ if (key === "workflow")
813
+ settings.workflow = parsedValue;
814
+ }
815
+ return settings.chat !== undefined || settings.workflow !== undefined ? settings : undefined;
816
+ }
817
+ function getCodexFastModeEnvironmentSettings() {
818
+ return parseCodexFastModeEnvironmentSettings(getEnvValue(ENV_CODEX_FAST_MODE));
819
+ }
820
+ function setCodexFastModeEnvironmentSettings(settings) {
821
+ setEnvValue(ENV_CODEX_FAST_MODE, serializeCodexFastModeEnvironmentSettings(settings));
822
+ }
823
+ function getEnvNames(name) {
824
+ if (ENV_PREFIX === LEGACY_ENV_PREFIX || !name.startsWith(`${ENV_PREFIX}_`))
825
+ return [name];
826
+ return [name, `${LEGACY_ENV_PREFIX}_${name.slice(ENV_PREFIX.length + 1)}`];
827
+ }
828
+ function getEnvValue(name) {
829
+ for (const candidate of getEnvNames(name)) {
830
+ const value = process.env[candidate];
831
+ if (value !== undefined)
832
+ return value;
833
+ }
834
+ return;
835
+ }
836
+ function hasEnvValue(name) {
837
+ return getEnvValue(name) !== undefined;
838
+ }
839
+ function setEnvValue(name, value) {
840
+ process.env[name] = value;
841
+ }
842
+ function expandTildePath(path) {
843
+ return normalizePath(path);
844
+ }
845
+ var DEFAULT_SHARE_VIEWER_URL = "https://pi.dev/session/";
846
+ function getShareViewerUrl(gistId) {
847
+ const baseUrl = getEnvValue(ENV_SHARE_VIEWER_URL) || DEFAULT_SHARE_VIEWER_URL;
848
+ return `${baseUrl}#${gistId}`;
849
+ }
850
+ function getAgentDir() {
851
+ const envDir = getEnvValue(ENV_AGENT_DIR);
852
+ if (envDir) {
853
+ return expandTildePath(envDir);
854
+ }
855
+ return join5(getHomeDir(), CONFIG_DIR_NAME, "agent");
856
+ }
857
+ function getLegacyAgentDir() {
858
+ return join5(getHomeDir(), LEGACY_CONFIG_DIR_NAME, "agent");
859
+ }
860
+ function getAgentDirs() {
861
+ const primary = getAgentDir();
862
+ if (hasEnvValue(ENV_AGENT_DIR) || CONFIG_DIR_NAME === LEGACY_CONFIG_DIR_NAME) {
863
+ return [primary];
864
+ }
865
+ const legacy = getLegacyAgentDir();
866
+ return legacy === primary ? [primary] : [primary, legacy];
867
+ }
868
+ function getUserConfigDirs() {
869
+ return CONFIG_DIR_NAMES.map((name) => join5(getHomeDir(), name));
870
+ }
871
+ function getProjectConfigDirs(cwd) {
872
+ return CONFIG_DIR_NAMES.map((name) => join5(cwd, name));
873
+ }
874
+ function getUserConfigPaths(...segments) {
875
+ return getUserConfigDirs().map((dir) => join5(dir, ...segments));
876
+ }
877
+ function getAgentConfigPaths(...segments) {
878
+ return getAgentDirs().map((dir) => join5(dir, ...segments));
879
+ }
880
+ function getProjectConfigPaths(cwd, ...segments) {
881
+ return getProjectConfigDirs(cwd).map((dir) => join5(dir, ...segments));
882
+ }
883
+ function getCustomThemesDir() {
884
+ return join5(getAgentDir(), "themes");
885
+ }
886
+ function getAuthPath() {
887
+ return join5(getAgentDir(), "auth.json");
888
+ }
889
+ function getSettingsPath() {
890
+ return join5(getAgentDir(), "settings.json");
891
+ }
892
+ function getBinDir() {
893
+ return join5(getAgentDir(), "bin");
894
+ }
895
+ function getExtensionTranspileCacheDir() {
896
+ return join5(getAgentDir(), "cache", "jiti", VERSION);
897
+ }
898
+ function getSessionsDir() {
899
+ return join5(getAgentDir(), "sessions");
900
+ }
901
+ function getDebugLogPath() {
902
+ return join5(getAgentDir(), `${APP_NAME}-debug.log`);
903
+ }
904
+
905
+ // src/core/messages.ts
906
+ var BRANCH_SUMMARY_PREFIX = `The following is a summary of a branch that this conversation came back from:
907
+
908
+ <summary>
909
+ `;
910
+ var BRANCH_SUMMARY_SUFFIX = `</summary>`;
911
+ function normalizeMessageContent(message) {
912
+ switch (message.role) {
913
+ case "user":
914
+ case "assistant":
915
+ case "toolResult":
916
+ case "custom":
917
+ return message.content == null ? { ...message, content: [] } : message;
918
+ default:
919
+ return message;
920
+ }
921
+ }
922
+ function userLikeContentBlockIsLlmVisible(block) {
923
+ if (!block || typeof block !== "object")
924
+ return false;
925
+ const candidate = block;
926
+ if (candidate.type === "image")
927
+ return true;
928
+ if (candidate.type === "text")
929
+ return typeof candidate.text === "string" && candidate.text.trim().length > 0;
930
+ return typeof candidate.type === "string" && candidate.type.trim().length > 0;
931
+ }
932
+ function userLikeContentIsLlmVisible(content, deletedBlockIndexes = new Set) {
933
+ if (typeof content === "string")
934
+ return content.trim().length > 0;
935
+ if (!Array.isArray(content))
936
+ return false;
937
+ return content.some((block, index) => !deletedBlockIndexes.has(index) && userLikeContentBlockIsLlmVisible(block));
938
+ }
939
+ function messageStartsLlmUserTurn(message, deletedBlockIndexes = new Set) {
940
+ switch (message.role) {
941
+ case "user":
942
+ return userLikeContentIsLlmVisible(message.content, deletedBlockIndexes);
943
+ case "custom":
944
+ return message.excludeFromContext !== true && userLikeContentIsLlmVisible(message.content, deletedBlockIndexes);
945
+ case "branchSummary":
946
+ return typeof message.summary === "string" && message.summary.length > 0;
947
+ case "bashExecution":
948
+ return message.excludeFromContext !== true;
949
+ default:
950
+ return false;
951
+ }
952
+ }
953
+ function messageIsLlmVisible(message, deletedBlockIndexes = new Set) {
954
+ if (message.role === "assistant" || message.role === "toolResult")
955
+ return true;
956
+ return messageStartsLlmUserTurn(message, deletedBlockIndexes);
957
+ }
958
+ function filterUserLikeContentBlocks(content) {
959
+ return content.filter(userLikeContentBlockIsLlmVisible);
960
+ }
961
+ function normalizeRawRedactedThinking(message) {
962
+ if (message.role !== "assistant" || !Array.isArray(message.content))
963
+ return message;
964
+ let changed = false;
965
+ const content = message.content.map((block) => {
966
+ if (!block || typeof block !== "object")
967
+ return block;
968
+ const candidate = block;
969
+ if (candidate.type !== "redacted_thinking" || typeof candidate.data !== "string")
970
+ return block;
971
+ changed = true;
972
+ return {
973
+ type: "thinking",
974
+ thinking: "",
975
+ thinkingSignature: candidate.data,
976
+ redacted: true
977
+ };
978
+ });
979
+ return changed ? { ...message, content } : message;
980
+ }
981
+ function bashExecutionToText(msg) {
982
+ let text = `Ran \`${msg.command}\`
983
+ `;
984
+ if (msg.output) {
985
+ text += `\`\`\`
986
+ ${msg.output}
987
+ \`\`\``;
988
+ } else {
989
+ text += "(no output)";
990
+ }
991
+ if (msg.cancelled) {
992
+ text += `
993
+
994
+ (command cancelled)`;
995
+ } else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {
996
+ text += `
997
+
998
+ Command exited with code ${msg.exitCode}`;
999
+ }
1000
+ if (msg.truncated && msg.fullOutputPath) {
1001
+ text += `
1002
+
1003
+ [Output truncated. Full output: ${msg.fullOutputPath}]`;
1004
+ }
1005
+ return text;
1006
+ }
1007
+ var VERBATIM_COMPACTION_PREFIX = `The earlier conversation was compacted. Below is the verbatim transcript of the retained lines; elided spans are marked "(filtered N lines)".
1008
+
1009
+ `;
1010
+ var verbatimCompactionMessages = new WeakSet;
1011
+ function isVerbatimCompactionMessage(message) {
1012
+ return verbatimCompactionMessages.has(message);
1013
+ }
1014
+ function createVerbatimCompactionMessage(compactedText, tokensBefore, timestamp, details, retainedTail) {
1015
+ const content = [{ type: "text", text: VERBATIM_COMPACTION_PREFIX + compactedText }];
1016
+ for (const block of retainedTail ?? []) {
1017
+ const last = content[content.length - 1];
1018
+ if (block.type === "text" && last.type === "text")
1019
+ last.text += block.text;
1020
+ else
1021
+ content.push({ ...block });
1022
+ }
1023
+ const message = createCustomMessage("compaction", content, true, details ?? { tokensBefore }, timestamp);
1024
+ verbatimCompactionMessages.add(message);
1025
+ return message;
1026
+ }
1027
+ function createBranchSummaryMessage(summary, fromId, timestamp) {
1028
+ return {
1029
+ role: "branchSummary",
1030
+ summary,
1031
+ fromId,
1032
+ timestamp: new Date(timestamp).getTime()
1033
+ };
1034
+ }
1035
+ function createCustomMessage(customType, content, display, details, timestamp, excludeFromContext, stageAdmissionKey) {
1036
+ const message = {
1037
+ role: "custom",
1038
+ customType,
1039
+ content: content ?? [],
1040
+ display,
1041
+ details,
1042
+ ...stageAdmissionKey === undefined ? {} : { stageAdmissionKey },
1043
+ timestamp: new Date(timestamp).getTime()
1044
+ };
1045
+ if (excludeFromContext === true)
1046
+ message.excludeFromContext = true;
1047
+ return message;
1048
+ }
1049
+ function collectAssistantToolCalls(message) {
1050
+ const content = message.content;
1051
+ const calls = [];
1052
+ if (!Array.isArray(content))
1053
+ return calls;
1054
+ for (const block of content) {
1055
+ if (!block || typeof block !== "object")
1056
+ continue;
1057
+ const candidate = block;
1058
+ if (candidate.type === "toolCall" && typeof candidate.id === "string" && typeof candidate.name === "string") {
1059
+ calls.push({ id: candidate.id, name: candidate.name });
1060
+ }
1061
+ }
1062
+ return calls;
1063
+ }
1064
+ function getToolResultCallId(message) {
1065
+ if (message.role !== "toolResult")
1066
+ return;
1067
+ const toolCallId = message.toolCallId;
1068
+ return typeof toolCallId === "string" ? toolCallId : undefined;
1069
+ }
1070
+ function repairOrphanToolResults(messages, options = {}) {
1071
+ let allowedToolCallIds;
1072
+ let answeredToolCallIds;
1073
+ let pendingToolCalls = [];
1074
+ let pendingTimestamp = 0;
1075
+ let changed = false;
1076
+ const repaired = [];
1077
+ const flushUnanswered = () => {
1078
+ for (const call of pendingToolCalls) {
1079
+ if (answeredToolCallIds?.has(call.id))
1080
+ continue;
1081
+ const interrupted = {
1082
+ role: "toolResult",
1083
+ toolCallId: call.id,
1084
+ toolName: call.name,
1085
+ content: [
1086
+ {
1087
+ type: "text",
1088
+ text: "Tool execution was interrupted after it started; its result is unavailable. Inspect side effects before deciding whether to retry it."
1089
+ }
1090
+ ],
1091
+ isError: true,
1092
+ timestamp: pendingTimestamp
1093
+ };
1094
+ repaired.push(interrupted);
1095
+ changed = true;
1096
+ }
1097
+ allowedToolCallIds = undefined;
1098
+ answeredToolCallIds = undefined;
1099
+ pendingToolCalls = [];
1100
+ };
1101
+ for (const message of messages) {
1102
+ if (message.role === "assistant") {
1103
+ flushUnanswered();
1104
+ const announcedToolCalls = collectAssistantToolCalls(message);
1105
+ pendingToolCalls = message.stopReason === "toolUse" ? announcedToolCalls : [];
1106
+ allowedToolCallIds = new Set(announcedToolCalls.map((call) => call.id));
1107
+ answeredToolCallIds = new Set;
1108
+ pendingTimestamp = message.timestamp;
1109
+ repaired.push(message);
1110
+ continue;
1111
+ }
1112
+ if (message.role === "toolResult") {
1113
+ const toolCallId = getToolResultCallId(message);
1114
+ if (toolCallId && allowedToolCallIds?.has(toolCallId) && answeredToolCallIds?.has(toolCallId) === false) {
1115
+ answeredToolCallIds.add(toolCallId);
1116
+ repaired.push(message);
1117
+ continue;
1118
+ }
1119
+ changed = true;
1120
+ continue;
1121
+ }
1122
+ flushUnanswered();
1123
+ repaired.push(message);
1124
+ }
1125
+ if (options.repairTrailing)
1126
+ flushUnanswered();
1127
+ return changed ? repaired : messages;
1128
+ }
1129
+ function convertToLlm(messages) {
1130
+ const converted = messages.map((rawMessage) => {
1131
+ const m = normalizeRawRedactedThinking(normalizeMessageContent(rawMessage));
1132
+ switch (m.role) {
1133
+ case "bashExecution":
1134
+ if (!messageStartsLlmUserTurn(m))
1135
+ return;
1136
+ return {
1137
+ role: "user",
1138
+ content: [{ type: "text", text: bashExecutionToText(m) }],
1139
+ timestamp: m.timestamp
1140
+ };
1141
+ case "custom": {
1142
+ if (!messageStartsLlmUserTurn(m))
1143
+ return;
1144
+ const content = typeof m.content === "string" ? [{ type: "text", text: m.content }] : filterUserLikeContentBlocks(m.content);
1145
+ return { role: "user", content, timestamp: m.timestamp };
1146
+ }
1147
+ case "branchSummary":
1148
+ if (!messageStartsLlmUserTurn(m))
1149
+ return;
1150
+ return {
1151
+ role: "user",
1152
+ content: [{ type: "text", text: BRANCH_SUMMARY_PREFIX + m.summary + BRANCH_SUMMARY_SUFFIX }],
1153
+ timestamp: m.timestamp
1154
+ };
1155
+ case "user": {
1156
+ if (!messageStartsLlmUserTurn(m))
1157
+ return;
1158
+ if (!Array.isArray(m.content))
1159
+ return m;
1160
+ return { ...m, content: filterUserLikeContentBlocks(m.content) };
1161
+ }
1162
+ case "assistant":
1163
+ case "toolResult":
1164
+ return m;
1165
+ case "compactionSummary":
1166
+ return;
1167
+ default: {
1168
+ const _exhaustiveCheck = m;
1169
+ return;
1170
+ }
1171
+ }
1172
+ }).filter((m) => m !== undefined);
1173
+ return repairOrphanToolResults(converted);
1174
+ }
1175
+
1176
+ // src/core/compaction/transcript-serialization.ts
1177
+ var FILTERED_MARKER_RE = /^\(filtered (\d+) lines\)$/;
1178
+ var LINE_NUMBER_SEPARATOR = "→";
1179
+ var ROLE_HEADER_RE = /^\[(User|Assistant|Assistant thinking|Assistant tool calls|Tool result)\]: /;
1180
+ var KEEP_CONTEXT_OPEN_LINE_RE = /^<keepContext>$/i;
1181
+ var KEEP_CONTEXT_CLOSE_LINE_RE = /^<\/keepContext>$/i;
1182
+ var TOOL_RESULT_MAX_CHARS = 16000;
1183
+ function filteredMarker(lineCount) {
1184
+ return `(filtered ${lineCount} lines)`;
1185
+ }
1186
+ function truncateToolResult(text) {
1187
+ if (text.length <= TOOL_RESULT_MAX_CHARS)
1188
+ return text;
1189
+ return `${text.slice(0, TOOL_RESULT_MAX_CHARS)}
1190
+
1191
+ [... ${text.length - TOOL_RESULT_MAX_CHARS} more characters truncated]`;
1192
+ }
1193
+ function lastChunk(chunks) {
1194
+ return chunks.length > 0 ? chunks[chunks.length - 1] : undefined;
1195
+ }
1196
+ function appendText(chunks, text) {
1197
+ if (text.length === 0)
1198
+ return;
1199
+ const last = lastChunk(chunks);
1200
+ if (last !== undefined && last.type === "text")
1201
+ last.text += text;
1202
+ else
1203
+ chunks.push({ type: "text", text });
1204
+ }
1205
+ function hasContent(chunks) {
1206
+ return chunks.some((chunk) => chunk.type !== "text" || chunk.text.length > 0);
1207
+ }
1208
+ function serializeContentBlocks(content, mode) {
1209
+ if (typeof content === "string")
1210
+ return content.length > 0 ? [{ type: "text", text: content }] : [];
1211
+ const chunks = [];
1212
+ for (const block of content) {
1213
+ if (block.type === "text") {
1214
+ appendText(chunks, block.text);
1215
+ continue;
1216
+ }
1217
+ if (block.type !== "image")
1218
+ continue;
1219
+ if (mode === "retained") {
1220
+ if (chunks.length > 0)
1221
+ appendText(chunks, `
1222
+ `);
1223
+ chunks.push({ ...block });
1224
+ continue;
1225
+ }
1226
+ const last = lastChunk(chunks);
1227
+ const needsBreak = last !== undefined && last.type === "text" && !last.text.endsWith(`
1228
+ `);
1229
+ appendText(chunks, `${needsBreak ? `
1230
+ ` : ""}[image]
1231
+ `);
1232
+ }
1233
+ const trailing = lastChunk(chunks);
1234
+ if (trailing !== undefined && trailing.type === "text" && trailing.text.endsWith(`
1235
+ `)) {
1236
+ trailing.text = trailing.text.slice(0, -1);
1237
+ }
1238
+ return chunks;
1239
+ }
1240
+ function serializeTranscriptChunks(messages, mode) {
1241
+ const chunks = [];
1242
+ const pushSection = (header, section) => {
1243
+ if (!hasContent(section))
1244
+ return;
1245
+ appendText(chunks, `${chunks.length > 0 ? `
1246
+
1247
+ ` : ""}${header}`);
1248
+ for (const chunk of section) {
1249
+ if (chunk.type === "text")
1250
+ appendText(chunks, chunk.text);
1251
+ else
1252
+ chunks.push(chunk);
1253
+ }
1254
+ };
1255
+ for (const message of messages) {
1256
+ if (message.role === "user") {
1257
+ pushSection("[User]: ", serializeContentBlocks(message.content, mode));
1258
+ continue;
1259
+ }
1260
+ if (message.role === "assistant") {
1261
+ const text = [];
1262
+ const thinking = [];
1263
+ const toolCalls = [];
1264
+ for (const block of message.content) {
1265
+ if (block.type === "text")
1266
+ text.push(block.text);
1267
+ else if (block.type === "thinking")
1268
+ thinking.push(block.thinking);
1269
+ else if (block.type === "toolCall") {
1270
+ const args = Object.entries(block.arguments).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(", ");
1271
+ toolCalls.push(`${block.name}(${args})`);
1272
+ }
1273
+ }
1274
+ if (thinking.length > 0)
1275
+ pushSection("[Assistant thinking]: ", [{ type: "text", text: thinking.join(`
1276
+ `) }]);
1277
+ if (text.length > 0)
1278
+ pushSection("[Assistant]: ", [{ type: "text", text: text.join(`
1279
+ `) }]);
1280
+ if (toolCalls.length > 0)
1281
+ pushSection("[Assistant tool calls]: ", [{ type: "text", text: toolCalls.join("; ") }]);
1282
+ continue;
1283
+ }
1284
+ if (message.role === "toolResult") {
1285
+ const section = serializeContentBlocks(message.content, mode);
1286
+ if (mode === "compacted") {
1287
+ for (const chunk of section) {
1288
+ if (chunk.type === "text")
1289
+ chunk.text = truncateToolResult(chunk.text);
1290
+ }
1291
+ }
1292
+ pushSection("[Tool result]: ", section);
1293
+ }
1294
+ }
1295
+ return chunks;
1296
+ }
1297
+ function serializeConversationForCompaction(messages) {
1298
+ return serializeTranscriptChunks(messages, "compacted").map((chunk) => chunk.type === "text" ? chunk.text : "").join("");
1299
+ }
1300
+ function serializeRetainedTranscript(messages) {
1301
+ return serializeTranscriptChunks(messages, "retained");
1302
+ }
1303
+ function keepContextMarker(line) {
1304
+ const body = line.replace(ROLE_HEADER_RE, "").trim();
1305
+ if (KEEP_CONTEXT_OPEN_LINE_RE.test(body))
1306
+ return "open";
1307
+ if (KEEP_CONTEXT_CLOSE_LINE_RE.test(body))
1308
+ return "close";
1309
+ return;
1310
+ }
1311
+ var UNTRUSTED_MARKER_ROLE = "Tool result";
1312
+ function roleMayProtect(role) {
1313
+ return role !== UNTRUSTED_MARKER_ROLE;
1314
+ }
1315
+ function keepContextLineNumbers(lines) {
1316
+ const protectedLines = new Set;
1317
+ let openIndex;
1318
+ let role;
1319
+ const protectThrough = (endIndex) => {
1320
+ if (openIndex === undefined)
1321
+ return;
1322
+ for (let line = openIndex;line <= endIndex; line++)
1323
+ protectedLines.add(line + 1);
1324
+ openIndex = undefined;
1325
+ };
1326
+ for (let index = 0;index < lines.length; index++) {
1327
+ const line = lines[index];
1328
+ const header = ROLE_HEADER_RE.exec(line);
1329
+ if (header) {
1330
+ protectThrough(index - 1);
1331
+ role = header[1];
1332
+ }
1333
+ if (!roleMayProtect(role))
1334
+ continue;
1335
+ const marker = keepContextMarker(line);
1336
+ if (marker === "open") {
1337
+ if (openIndex === undefined)
1338
+ openIndex = index;
1339
+ continue;
1340
+ }
1341
+ if (marker === "close")
1342
+ protectThrough(index);
1343
+ }
1344
+ protectThrough(lines.length - 1);
1345
+ return protectedLines;
1346
+ }
1347
+ function createNumberedRegion(text, protectedLineNumbers) {
1348
+ const lines = text.split(`
1349
+ `);
1350
+ const headerLineNumbers = new Set;
1351
+ const priorMarkerNs = new Map;
1352
+ for (let index = 0;index < lines.length; index++) {
1353
+ const lineNumber = index + 1;
1354
+ if (ROLE_HEADER_RE.test(lines[index]))
1355
+ headerLineNumbers.add(lineNumber);
1356
+ const markerMatch = FILTERED_MARKER_RE.exec(lines[index]);
1357
+ if (markerMatch)
1358
+ priorMarkerNs.set(lineNumber, Number(markerMatch[1]));
1359
+ }
1360
+ const detected = keepContextLineNumbers(lines);
1361
+ const merged = protectedLineNumbers === undefined ? detected : new Set([...protectedLineNumbers, ...detected]);
1362
+ return {
1363
+ __brand: "NumberedRegion",
1364
+ lines,
1365
+ headerLineNumbers,
1366
+ priorMarkerNs,
1367
+ protectedLineNumbers: merged.size > 0 ? merged : undefined,
1368
+ tokenEstimate: Math.ceil(text.length / 4)
1369
+ };
1370
+ }
1371
+ function numberRegionLines(region, start = 1, end = region.lines.length) {
1372
+ const first = Math.max(1, Math.trunc(start));
1373
+ const last = Math.min(region.lines.length, Math.trunc(end));
1374
+ if (first > last)
1375
+ return "";
1376
+ return region.lines.slice(first - 1, last).map((line, index) => `${first + index}${LINE_NUMBER_SEPARATOR}${line}`).join(`
1377
+ `);
1378
+ }
1379
+
1380
+ // src/core/session-entry-normalization.ts
1381
+ function normalizeDerivedSessionEntries(entries) {
1382
+ return entries.map((entry) => {
1383
+ if (entry.type !== "message")
1384
+ return entry;
1385
+ const message = normalizeMessageContent(entry.message);
1386
+ return message === entry.message ? entry : { ...entry, message };
1387
+ });
1388
+ }
1389
+
1390
+ // src/core/session-manager-history.ts
1391
+ function contextMessageFromEntry(entry) {
1392
+ if (entry.type === "message")
1393
+ return normalizeMessageContent(entry.message);
1394
+ if (entry.type === "custom_message") {
1395
+ return createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp, entry.excludeFromContext, entry.stageAdmissionKey);
1396
+ }
1397
+ if (entry.type === "branch_summary" && typeof entry.summary === "string" && entry.summary.length > 0) {
1398
+ return createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);
1399
+ }
1400
+ return;
1401
+ }
1402
+ function serializeKeptTail(entries) {
1403
+ const messages = [];
1404
+ for (const entry of entries) {
1405
+ const message = contextMessageFromEntry(entry);
1406
+ if (message)
1407
+ messages.push(message);
1408
+ }
1409
+ if (messages.length === 0)
1410
+ return [];
1411
+ return serializeRetainedTranscript(convertToLlm(messages));
1412
+ }
1413
+ function getLatestCompactionBoundaryEntry(entries) {
1414
+ for (let i = entries.length - 1;i >= 0; i--) {
1415
+ const entry = entries[i];
1416
+ if (entry.type !== "compaction")
1417
+ continue;
1418
+ const details = entry.details;
1419
+ if (details?.strategy === "verbatim-lines")
1420
+ return entry;
1421
+ }
1422
+ return null;
1423
+ }
1424
+ function sessionEntryToContextMessages(entry) {
1425
+ if (entry.type === "message")
1426
+ return [normalizeMessageContent(entry.message)];
1427
+ if (entry.type === "custom_message") {
1428
+ return [
1429
+ createCustomMessage(entry.customType, entry.content, entry.display, entry.details, entry.timestamp, entry.excludeFromContext, entry.stageAdmissionKey)
1430
+ ];
1431
+ }
1432
+ if (entry.type === "branch_summary" && entry.summary.length > 0) {
1433
+ return [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)];
1434
+ }
1435
+ if (entry.type === "compaction") {
1436
+ const details = entry.details;
1437
+ if (details?.strategy === "verbatim-lines") {
1438
+ return [
1439
+ createVerbatimCompactionMessage(entry.summary, entry.tokensBefore, entry.timestamp, entry.details)
1440
+ ];
1441
+ }
1442
+ }
1443
+ return [];
1444
+ }
1445
+ function buildContextEntries(entries, leafId, byId = new Map(entries.map((entry) => [entry.id, entry]))) {
1446
+ if (leafId === null)
1447
+ return [];
1448
+ const leaf = leafId ? byId.get(leafId) : entries[entries.length - 1];
1449
+ if (!leaf)
1450
+ return [];
1451
+ const path = normalizeDerivedSessionEntries(getBranchPath(leaf.id, byId));
1452
+ const boundary = getLatestCompactionBoundaryEntry(path);
1453
+ if (!boundary)
1454
+ return path;
1455
+ const boundaryIndex = path.findIndex((entry) => entry.id === boundary.id);
1456
+ const firstKeptIndex = path.findIndex((entry, index) => index < boundaryIndex && entry.id === boundary.firstKeptEntryId);
1457
+ return [
1458
+ boundary,
1459
+ ...firstKeptIndex >= 0 ? path.slice(firstKeptIndex, boundaryIndex) : [],
1460
+ ...path.slice(boundaryIndex + 1)
1461
+ ];
1462
+ }
1463
+ function buildSessionContext(entries, leafId, byId) {
1464
+ if (!byId) {
1465
+ byId = new Map;
1466
+ for (const entry of entries) {
1467
+ byId.set(entry.id, entry);
1468
+ }
1469
+ }
1470
+ let leaf;
1471
+ if (leafId === null) {
1472
+ return { messages: [], thinkingLevel: "off", model: null };
1473
+ }
1474
+ if (leafId) {
1475
+ leaf = byId.get(leafId);
1476
+ }
1477
+ if (!leaf) {
1478
+ leaf = entries[entries.length - 1];
1479
+ }
1480
+ if (!leaf) {
1481
+ return { messages: [], thinkingLevel: "off", model: null };
1482
+ }
1483
+ const path = normalizeDerivedSessionEntries(getBranchPath(leaf.id, byId));
1484
+ let thinkingLevel = "off";
1485
+ let model = null;
1486
+ for (const entry of path) {
1487
+ if (entry.type === "thinking_level_change") {
1488
+ thinkingLevel = entry.thinkingLevel;
1489
+ } else if (entry.type === "model_change") {
1490
+ model = { provider: entry.provider, modelId: entry.modelId };
1491
+ } else if (entry.type === "message" && entry.message.role === "assistant") {
1492
+ model = { provider: entry.message.provider, modelId: entry.message.model };
1493
+ }
1494
+ }
1495
+ const messages = [];
1496
+ const appendMessage = (entry) => {
1497
+ const message = contextMessageFromEntry(entry);
1498
+ if (message)
1499
+ messages.push(message);
1500
+ };
1501
+ const boundary = getLatestCompactionBoundaryEntry(path);
1502
+ if (!boundary) {
1503
+ for (const entry of path)
1504
+ appendMessage(entry);
1505
+ return { messages, thinkingLevel, model };
1506
+ }
1507
+ const boundaryIndex = path.findIndex((entry) => entry.id === boundary.id);
1508
+ const firstKeptIndex = path.findIndex((entry, index) => index < boundaryIndex && entry.id === boundary.firstKeptEntryId);
1509
+ const keptTail = firstKeptIndex >= 0 ? serializeKeptTail(path.slice(firstKeptIndex, boundaryIndex)) : [];
1510
+ const separator = keptTail.length > 0 && boundary.summary.length > 0 ? [{ type: "text", text: `
1511
+
1512
+ ` }] : [];
1513
+ messages.push(createVerbatimCompactionMessage(boundary.summary, boundary.tokensBefore, boundary.timestamp, boundary.details, [
1514
+ ...separator,
1515
+ ...keptTail
1516
+ ]));
1517
+ for (let i = boundaryIndex + 1;i < path.length; i++)
1518
+ appendMessage(path[i]);
1519
+ return { messages, thinkingLevel, model };
1520
+ }
1521
+ function buildSessionIndex(fileEntries) {
1522
+ const byId = new Map;
1523
+ const labelsById = new Map;
1524
+ const labelTimestampsById = new Map;
1525
+ let leafId = null;
1526
+ for (const entry of fileEntries) {
1527
+ if (entry.type === "session")
1528
+ continue;
1529
+ byId.set(entry.id, entry);
1530
+ leafId = entry.id;
1531
+ if (entry.type === "label") {
1532
+ if (entry.label) {
1533
+ labelsById.set(entry.targetId, entry.label);
1534
+ labelTimestampsById.set(entry.targetId, entry.timestamp);
1535
+ } else {
1536
+ labelsById.delete(entry.targetId);
1537
+ labelTimestampsById.delete(entry.targetId);
1538
+ }
1539
+ }
1540
+ }
1541
+ return { byId, labelsById, labelTimestampsById, leafId };
1542
+ }
1543
+ function getBranchPath(fromId, byId) {
1544
+ const path = [];
1545
+ let current = fromId ? byId.get(fromId) : undefined;
1546
+ while (current) {
1547
+ path.push(current);
1548
+ current = current.parentId ? byId.get(current.parentId) : undefined;
1549
+ }
1550
+ path.reverse();
1551
+ return path;
1552
+ }
1553
+ function buildSessionTree(entries, labelsById, labelTimestampsById) {
1554
+ const nodeMap = new Map;
1555
+ const roots = [];
1556
+ for (const entry of entries) {
1557
+ const label = labelsById.get(entry.id);
1558
+ const labelTimestamp = labelTimestampsById.get(entry.id);
1559
+ nodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });
1560
+ }
1561
+ for (const entry of entries) {
1562
+ const node = nodeMap.get(entry.id);
1563
+ if (entry.parentId === null || entry.parentId === entry.id) {
1564
+ roots.push(node);
1565
+ } else {
1566
+ const parent = nodeMap.get(entry.parentId);
1567
+ if (parent) {
1568
+ parent.children.push(node);
1569
+ } else {
1570
+ roots.push(node);
1571
+ }
1572
+ }
1573
+ }
1574
+ const stack = [...roots];
1575
+ while (stack.length > 0) {
1576
+ const node = stack.pop();
1577
+ node.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());
1578
+ stack.push(...node.children);
1579
+ }
1580
+ return roots;
1581
+ }
1582
+
1583
+ // src/core/session-manager-classification.ts
1584
+ var WORKFLOW_SESSION_METADATA_ENV = "ATOMIC_WORKFLOW_SESSION_METADATA";
1585
+ function isNonEmptyString(value) {
1586
+ return typeof value === "string" && value.trim().length > 0;
1587
+ }
1588
+ function validSessionWorkflowMetadata(value) {
1589
+ if (value === null || typeof value !== "object" || Array.isArray(value))
1590
+ return;
1591
+ const metadata = value;
1592
+ if (!isNonEmptyString(metadata.runId) || !isNonEmptyString(metadata.stageId) || !isNonEmptyString(metadata.stageName)) {
1593
+ return;
1594
+ }
1595
+ return value;
1596
+ }
1597
+ function classifiedWorkflowMetadata(header) {
1598
+ if (header?.internal !== true)
1599
+ return;
1600
+ return validSessionWorkflowMetadata(header.workflow);
1601
+ }
1602
+ function isClassifiedWorkflowSession(header) {
1603
+ return classifiedWorkflowMetadata(header) !== undefined;
1604
+ }
1605
+ function workflowSessionMetadataFromEnv(env = process.env) {
1606
+ const serialized = env[WORKFLOW_SESSION_METADATA_ENV];
1607
+ if (!serialized)
1608
+ return;
1609
+ try {
1610
+ return validSessionWorkflowMetadata(JSON.parse(serialized));
1611
+ } catch {
1612
+ return;
1613
+ }
1614
+ }
1615
+
1616
+ // src/core/session-manager-types.ts
1617
+ var CURRENT_SESSION_VERSION = 3;
1618
+
1619
+ // src/core/session-manager-validation.ts
1620
+ import { uuidv7 } from "@bastani/pi-ai";
1621
+ import { randomUUID } from "crypto";
1622
+ function createSessionId() {
1623
+ return uuidv7();
1624
+ }
1625
+ function assertValidSessionId(id) {
1626
+ if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(id)) {
1627
+ throw new Error("Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character");
1628
+ }
1629
+ }
1630
+ function generateId(byId) {
1631
+ for (let i = 0;i < 100; i++) {
1632
+ const id = randomUUID().slice(0, 8);
1633
+ if (!byId.has(id))
1634
+ return id;
1635
+ }
1636
+ return randomUUID();
1637
+ }
1638
+
1639
+ // src/core/session-manager-migrations.ts
1640
+ function migrateV1ToV2(entries) {
1641
+ const ids = new Set;
1642
+ let prevId = null;
1643
+ for (const entry of entries) {
1644
+ if (entry.type === "session") {
1645
+ entry.version = 2;
1646
+ continue;
1647
+ }
1648
+ entry.id = generateId(ids);
1649
+ entry.parentId = prevId;
1650
+ prevId = entry.id;
1651
+ if (entry.type === "compaction") {
1652
+ const comp = entry;
1653
+ if (typeof comp.firstKeptEntryIndex === "number") {
1654
+ const targetEntry = entries[comp.firstKeptEntryIndex];
1655
+ if (targetEntry && targetEntry.type !== "session") {
1656
+ comp.firstKeptEntryId = targetEntry.id;
1657
+ }
1658
+ delete comp.firstKeptEntryIndex;
1659
+ }
1660
+ }
1661
+ }
1662
+ }
1663
+ function migrateV2ToV3(entries) {
1664
+ for (const entry of entries) {
1665
+ if (entry.type === "session") {
1666
+ entry.version = 3;
1667
+ continue;
1668
+ }
1669
+ if (entry.type === "message") {
1670
+ const msgEntry = entry;
1671
+ if (msgEntry.message && msgEntry.message.role === "hookMessage") {
1672
+ msgEntry.message.role = "custom";
1673
+ }
1674
+ }
1675
+ }
1676
+ }
1677
+ function migrateToCurrentVersion(entries) {
1678
+ const header = entries.find((e) => e.type === "session");
1679
+ const version = header?.version ?? 1;
1680
+ if (version >= CURRENT_SESSION_VERSION)
1681
+ return false;
1682
+ if (version < 2)
1683
+ migrateV1ToV2(entries);
1684
+ if (version < 3)
1685
+ migrateV2ToV3(entries);
1686
+ return true;
1687
+ }
1688
+ function migrateSessionEntries(entries) {
1689
+ migrateToCurrentVersion(entries);
1690
+ }
1691
+ function parseSessionEntries(content) {
1692
+ const entries = [];
1693
+ const lines = content.trim().split(`
1694
+ `);
1695
+ for (const line of lines) {
1696
+ if (!line.trim())
1697
+ continue;
1698
+ try {
1699
+ const entry = JSON.parse(line);
1700
+ entries.push(entry);
1701
+ } catch {}
1702
+ }
1703
+ return entries;
1704
+ }
1705
+
1706
+ // src/core/session-manager-core.ts
1707
+ import { existsSync as existsSync7, statSync as statSync2 } from "fs";
1708
+ import { resolve as resolve3 } from "path";
1709
+
1710
+ // src/core/session-manager-archive.ts
1711
+ import { join as join9 } from "path";
1712
+
1713
+ // src/core/session-manager-entries.ts
1714
+ import { join as join6 } from "path";
1715
+ function entryBase(byId, parentId) {
1716
+ return {
1717
+ id: generateId(byId),
1718
+ parentId,
1719
+ timestamp: new Date().toISOString()
1720
+ };
1721
+ }
1722
+ function createSessionHeader(id, cwd, timestamp = new Date().toISOString(), parentSession, internal, workflow) {
1723
+ const header = {
1724
+ type: "session",
1725
+ version: CURRENT_SESSION_VERSION,
1726
+ id,
1727
+ timestamp,
1728
+ cwd
1729
+ };
1730
+ if (parentSession !== undefined)
1731
+ header.parentSession = parentSession;
1732
+ const validWorkflow = internal === true ? validSessionWorkflowMetadata(workflow) : undefined;
1733
+ if (validWorkflow) {
1734
+ header.internal = true;
1735
+ header.workflow = validWorkflow;
1736
+ }
1737
+ return header;
1738
+ }
1739
+ function createSessionFilePath(sessionDir, timestamp, sessionId) {
1740
+ const fileTimestamp = timestamp.replace(/[:.]/g, "-");
1741
+ return join6(sessionDir, `${fileTimestamp}_${sessionId}.jsonl`);
1742
+ }
1743
+ function createMessageEntry(message, byId, parentId) {
1744
+ return {
1745
+ type: "message",
1746
+ ...entryBase(byId, parentId),
1747
+ message
1748
+ };
1749
+ }
1750
+ function createThinkingLevelChangeEntry(thinkingLevel, byId, parentId) {
1751
+ return {
1752
+ type: "thinking_level_change",
1753
+ ...entryBase(byId, parentId),
1754
+ thinkingLevel
1755
+ };
1756
+ }
1757
+ function createModelChangeEntry(provider, modelId, byId, parentId) {
1758
+ return {
1759
+ type: "model_change",
1760
+ ...entryBase(byId, parentId),
1761
+ provider,
1762
+ modelId
1763
+ };
1764
+ }
1765
+ function createCompactionEntry(compactedText, firstKeptEntryId, tokensBefore, details, usage, byId, parentId) {
1766
+ return {
1767
+ type: "compaction",
1768
+ ...entryBase(byId, parentId),
1769
+ summary: compactedText,
1770
+ firstKeptEntryId,
1771
+ tokensBefore,
1772
+ details,
1773
+ usage,
1774
+ fromHook: details.rung === "extension" || undefined
1775
+ };
1776
+ }
1777
+ function createCustomEntry(customType, data, byId, parentId) {
1778
+ return {
1779
+ type: "custom",
1780
+ customType,
1781
+ data,
1782
+ ...entryBase(byId, parentId)
1783
+ };
1784
+ }
1785
+ function createSessionInfoEntry(name, byId, parentId) {
1786
+ return {
1787
+ type: "session_info",
1788
+ ...entryBase(byId, parentId),
1789
+ name: name.trim()
1790
+ };
1791
+ }
1792
+ function createSessionSummaryEntry(summary, summarizedThroughId, usage, byId, parentId) {
1793
+ return {
1794
+ type: "session_summary",
1795
+ ...entryBase(byId, parentId),
1796
+ summary: summary.trim(),
1797
+ summarizedThroughId,
1798
+ usage
1799
+ };
1800
+ }
1801
+ function getLastConversationMessageId(entries) {
1802
+ for (let i = entries.length - 1;i >= 0; i--) {
1803
+ const entry = entries[i];
1804
+ if (entry.type !== "message")
1805
+ continue;
1806
+ const role = entry.message.role;
1807
+ if (role === "user" || role === "assistant")
1808
+ return entry.id;
1809
+ }
1810
+ return;
1811
+ }
1812
+ function getLatestSessionSummary(entries) {
1813
+ for (let i = entries.length - 1;i >= 0; i--) {
1814
+ const entry = entries[i];
1815
+ if (entry.type === "branch_summary")
1816
+ return;
1817
+ if (entry.type === "session_summary")
1818
+ return entry;
1819
+ }
1820
+ return;
1821
+ }
1822
+ function getLatestSessionName(entries) {
1823
+ for (let i = entries.length - 1;i >= 0; i--) {
1824
+ const entry = entries[i];
1825
+ if (entry.type === "session_info") {
1826
+ const name = entry.name?.trim() || undefined;
1827
+ return { hasName: true, ...name !== undefined ? { name } : {} };
1828
+ }
1829
+ }
1830
+ return { hasName: false };
1831
+ }
1832
+ function createCustomMessageEntry(customType, content, display, details, excludeFromContext, protectedReconciliation, byId, parentId, stageAdmissionKey) {
1833
+ return {
1834
+ type: "custom_message",
1835
+ customType,
1836
+ content,
1837
+ display,
1838
+ details,
1839
+ ...excludeFromContext === true ? { excludeFromContext: true } : {},
1840
+ ...stageAdmissionKey === undefined ? {} : { stageAdmissionKey },
1841
+ ...protectedReconciliation === undefined ? {} : { protectedReconciliation },
1842
+ ...entryBase(byId, parentId)
1843
+ };
1844
+ }
1845
+ function createLabelEntry(targetId, label, byId, parentId, timestamp) {
1846
+ return {
1847
+ type: "label",
1848
+ ...entryBase(byId, parentId),
1849
+ ...timestamp !== undefined ? { timestamp } : {},
1850
+ targetId,
1851
+ label
1852
+ };
1853
+ }
1854
+ function createBranchSummaryEntry(branchFromId, fromId, summary, details, usage, fromHook, byId) {
1855
+ return {
1856
+ type: "branch_summary",
1857
+ ...entryBase(byId, branchFromId),
1858
+ fromId,
1859
+ summary,
1860
+ details,
1861
+ usage,
1862
+ fromHook
1863
+ };
1864
+ }
1865
+ function getEntriesWithoutHeader(fileEntries) {
1866
+ return fileEntries.filter((entry) => entry.type !== "session");
1867
+ }
1868
+
1869
+ // src/core/session-manager-paths.ts
1870
+ import { existsSync as existsSync4, mkdirSync } from "fs";
1871
+ import { join as join7 } from "path";
1872
+ function getDefaultSessionDirPath(cwd, agentDir = getAgentDir()) {
1873
+ const resolvedCwd = resolvePath(cwd);
1874
+ const resolvedAgentDir = resolvePath(agentDir);
1875
+ const safePath = `--${resolvedCwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
1876
+ return join7(resolvedAgentDir, "sessions", safePath);
1877
+ }
1878
+ function getDefaultSessionDir(cwd, agentDir = getAgentDir()) {
1879
+ const sessionDir = getDefaultSessionDirPath(cwd, agentDir);
1880
+ if (!existsSync4(sessionDir)) {
1881
+ mkdirSync(sessionDir, { recursive: true });
1882
+ }
1883
+ return sessionDir;
1884
+ }
1885
+
1886
+ // src/core/session-manager-storage.ts
1887
+ import {
1888
+ appendFileSync,
1889
+ closeSync,
1890
+ existsSync as existsSync5,
1891
+ mkdirSync as mkdirSync2,
1892
+ openSync,
1893
+ readdirSync,
1894
+ readSync,
1895
+ statSync,
1896
+ truncateSync,
1897
+ unlinkSync,
1898
+ writeFileSync
1899
+ } from "fs";
1900
+ import { join as join8 } from "path";
1901
+ import { StringDecoder } from "string_decoder";
1902
+ var SESSION_READ_BUFFER_SIZE = 1024 * 1024;
1903
+ var HEADER_READ_BUFFER_SIZE = 64 * 1024;
1904
+ function parseSessionEntryLine(line) {
1905
+ if (!line.trim())
1906
+ return null;
1907
+ try {
1908
+ return JSON.parse(line);
1909
+ } catch {
1910
+ return null;
1911
+ }
1912
+ }
1913
+ function loadEntriesFromFile(filePath) {
1914
+ const resolvedFilePath = normalizePath(filePath);
1915
+ if (!existsSync5(resolvedFilePath))
1916
+ return [];
1917
+ const entries = [];
1918
+ let pending = "";
1919
+ const fd = openSync(resolvedFilePath, "r");
1920
+ try {
1921
+ const decoder = new StringDecoder("utf8");
1922
+ const buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);
1923
+ while (true) {
1924
+ const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
1925
+ if (bytesRead === 0)
1926
+ break;
1927
+ pending += decoder.write(buffer.subarray(0, bytesRead));
1928
+ let lineStart = 0;
1929
+ let newlineIndex = pending.indexOf(`
1930
+ `, lineStart);
1931
+ while (newlineIndex !== -1) {
1932
+ const entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));
1933
+ if (entry)
1934
+ entries.push(entry);
1935
+ lineStart = newlineIndex + 1;
1936
+ newlineIndex = pending.indexOf(`
1937
+ `, lineStart);
1938
+ }
1939
+ pending = pending.slice(lineStart);
1940
+ }
1941
+ pending += decoder.end();
1942
+ const finalEntry = parseSessionEntryLine(pending);
1943
+ if (finalEntry)
1944
+ entries.push(finalEntry);
1945
+ } finally {
1946
+ closeSync(fd);
1947
+ }
1948
+ if (entries.length === 0)
1949
+ return entries;
1950
+ const header = entries[0];
1951
+ if (header.type !== "session" || !("id" in header) || typeof header.id !== "string") {
1952
+ return [];
1953
+ }
1954
+ if (pending)
1955
+ appendFileSync(resolvedFilePath, `
1956
+ `);
1957
+ return entries;
1958
+ }
1959
+ function readSessionHeader(filePath) {
1960
+ try {
1961
+ const fd = openSync(filePath, "r");
1962
+ try {
1963
+ const decoder = new StringDecoder("utf8");
1964
+ const buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);
1965
+ let pending = "";
1966
+ let foundNewline = false;
1967
+ while (true) {
1968
+ const bytesRead = readSync(fd, buffer, 0, buffer.length, null);
1969
+ if (bytesRead === 0)
1970
+ break;
1971
+ pending += decoder.write(buffer.subarray(0, bytesRead));
1972
+ const newlineIndex = pending.indexOf(`
1973
+ `);
1974
+ if (newlineIndex !== -1) {
1975
+ pending = pending.slice(0, newlineIndex);
1976
+ foundNewline = true;
1977
+ break;
1978
+ }
1979
+ }
1980
+ if (!foundNewline) {
1981
+ pending += decoder.end();
1982
+ }
1983
+ const firstLine = pending.split(`
1984
+ `)[0];
1985
+ if (!firstLine)
1986
+ return null;
1987
+ const header = JSON.parse(firstLine);
1988
+ if (header.type !== "session" || typeof header.id !== "string") {
1989
+ return null;
1990
+ }
1991
+ return header;
1992
+ } finally {
1993
+ closeSync(fd);
1994
+ }
1995
+ } catch {
1996
+ return null;
1997
+ }
1998
+ }
1999
+ function isInternalHeader(header) {
2000
+ return isClassifiedWorkflowSession(header);
2001
+ }
2002
+ function getSessionHeaderCwd(header) {
2003
+ const cwd = header.cwd;
2004
+ return typeof cwd === "string" ? cwd : undefined;
2005
+ }
2006
+ function sessionCwdMatches(cwd, resolvedCwd) {
2007
+ return cwd !== undefined && cwd !== "" && resolvePath(cwd) === resolvedCwd;
2008
+ }
2009
+ function findMostRecentSession(sessionDir, cwd, includeInternal = false) {
2010
+ const resolvedSessionDir = normalizePath(sessionDir);
2011
+ const resolvedCwd = cwd ? resolvePath(cwd) : undefined;
2012
+ try {
2013
+ const files = readdirSync(resolvedSessionDir).filter((f) => f.endsWith(".jsonl")).map((f) => join8(resolvedSessionDir, f)).map((path) => ({ path, header: readSessionHeader(path) })).filter((file) => file.header !== null && (!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) && (includeInternal || !isInternalHeader(file.header))).map(({ path }) => ({ path, mtime: statSync(path).mtime })).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
2014
+ return files[0]?.path || null;
2015
+ } catch {
2016
+ return null;
2017
+ }
2018
+ }
2019
+ function serializeSessionEntries(entries) {
2020
+ return `${entries.map((e) => JSON.stringify(e)).join(`
2021
+ `)}
2022
+ `;
2023
+ }
2024
+ function writeSessionEntries(filePath, entries) {
2025
+ writeFileSync(filePath, serializeSessionEntries(entries));
2026
+ }
2027
+ function appendSessionPayload(filePath, payload) {
2028
+ const existed = existsSync5(filePath);
2029
+ const offset = existed ? statSync(filePath).size : 0;
2030
+ try {
2031
+ appendFileSync(filePath, payload);
2032
+ } catch (writeError) {
2033
+ try {
2034
+ if (existed)
2035
+ truncateSync(filePath, offset);
2036
+ else if (existsSync5(filePath))
2037
+ unlinkSync(filePath);
2038
+ } catch (rollbackError) {
2039
+ throw new AggregateError([writeError, rollbackError], "Session append and rollback failed");
2040
+ }
2041
+ throw writeError;
2042
+ }
2043
+ }
2044
+ function appendSessionEntry(filePath, entry) {
2045
+ appendSessionPayload(filePath, `${JSON.stringify(entry)}
2046
+ `);
2047
+ }
2048
+ function appendSessionEntries(filePath, entries) {
2049
+ if (entries.length === 0)
2050
+ return;
2051
+ appendSessionPayload(filePath, serializeSessionEntries(entries));
2052
+ }
2053
+ function hasAssistantMessage(entries) {
2054
+ return entries.some((entry) => entry.type === "message" && entry.message.role === "assistant");
2055
+ }
2056
+ function persistAppendedEntry(filePath, entries, entry, flushed) {
2057
+ if (flushed)
2058
+ appendSessionEntry(filePath, entry);
2059
+ else if (!hasAssistantMessage(entries))
2060
+ return false;
2061
+ else
2062
+ appendSessionEntries(filePath, entries);
2063
+ return true;
2064
+ }
2065
+ function ensureDirectory(dir) {
2066
+ if (!existsSync5(dir)) {
2067
+ mkdirSync2(dir, { recursive: true });
2068
+ }
2069
+ }
2070
+
2071
+ // src/core/session-manager-archive.ts
2072
+ function createBackupSnapshot(sessionFile, entries, label = "compact") {
2073
+ if (!sessionFile)
2074
+ return;
2075
+ const safeLabel = label.replace(/[^a-z0-9_-]/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "backup";
2076
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
2077
+ const backupPath = `${sessionFile}.${timestamp}.${safeLabel}.bak`;
2078
+ writeSessionEntries(backupPath, entries);
2079
+ return backupPath;
2080
+ }
2081
+ function collectLabelsForPath(pathEntryIds, labelsById, labelTimestampsById) {
2082
+ const labelsToWrite = [];
2083
+ for (const [targetId, label] of labelsById) {
2084
+ if (pathEntryIds.has(targetId)) {
2085
+ labelsToWrite.push({ targetId, label, timestamp: labelTimestampsById.get(targetId) });
2086
+ }
2087
+ }
2088
+ return labelsToWrite;
2089
+ }
2090
+ function buildPersistedLabelEntries(pathEntryIds, lastEntryId, labelsToWrite) {
2091
+ let parentId = lastEntryId;
2092
+ const labelEntries = [];
2093
+ for (const { targetId, label, timestamp: labelTimestamp } of labelsToWrite) {
2094
+ const labelEntry = createLabelEntry(targetId, label, new Set(pathEntryIds), parentId, labelTimestamp);
2095
+ pathEntryIds.add(labelEntry.id);
2096
+ labelEntries.push(labelEntry);
2097
+ parentId = labelEntry.id;
2098
+ }
2099
+ return labelEntries;
2100
+ }
2101
+ function buildInMemoryLabelEntries(pathEntryIds, lastEntryId, labelsToWrite) {
2102
+ const labelEntries = [];
2103
+ let parentId = lastEntryId;
2104
+ for (const { targetId, label, timestamp: labelTimestamp } of labelsToWrite) {
2105
+ const labelEntry = {
2106
+ type: "label",
2107
+ id: generateId(new Set([...pathEntryIds, ...labelEntries.map((e) => e.id)])),
2108
+ parentId,
2109
+ timestamp: labelTimestamp,
2110
+ targetId,
2111
+ label
2112
+ };
2113
+ labelEntries.push(labelEntry);
2114
+ parentId = labelEntry.id;
2115
+ }
2116
+ return labelEntries;
2117
+ }
2118
+ function createBranchedSessionState(input) {
2119
+ if (input.path.length === 0) {
2120
+ throw new Error(`Entry ${input.leafId} not found`);
2121
+ }
2122
+ const pathWithoutLabels = input.path.filter((entry) => entry.type !== "label");
2123
+ const newSessionId = createSessionId();
2124
+ const timestamp = new Date().toISOString();
2125
+ const newSessionFile = createSessionFilePath(input.sessionDir, timestamp, newSessionId);
2126
+ const header = createSessionHeader(newSessionId, input.cwd, timestamp, input.persist ? input.previousSessionFile : undefined, input.workflow !== undefined, input.workflow);
2127
+ const pathEntryIds = new Set(pathWithoutLabels.map((entry) => entry.id));
2128
+ const labelsToWrite = collectLabelsForPath(pathEntryIds, input.labelsById, input.labelTimestampsById);
2129
+ const lastEntryId = pathWithoutLabels[pathWithoutLabels.length - 1]?.id || null;
2130
+ const labelEntries = input.persist ? buildPersistedLabelEntries(pathEntryIds, lastEntryId, labelsToWrite) : buildInMemoryLabelEntries(pathEntryIds, lastEntryId, labelsToWrite);
2131
+ const fileEntries = [header, ...pathWithoutLabels, ...labelEntries];
2132
+ if (!input.persist) {
2133
+ return { sessionId: newSessionId, sessionFile: undefined, fileEntries, shouldRewriteFile: false };
2134
+ }
2135
+ const shouldRewriteFile = hasAssistantMessage(fileEntries);
2136
+ return {
2137
+ sessionId: newSessionId,
2138
+ sessionFile: newSessionFile,
2139
+ fileEntries,
2140
+ shouldRewriteFile,
2141
+ flushed: shouldRewriteFile
2142
+ };
2143
+ }
2144
+ function forkSessionFromFile(sourcePath, targetCwd, sessionDir, options) {
2145
+ const resolvedSourcePath = resolvePath(sourcePath);
2146
+ const resolvedTargetCwd = resolvePath(targetCwd);
2147
+ const sourceEntries = loadEntriesFromFile(resolvedSourcePath);
2148
+ if (sourceEntries.length === 0) {
2149
+ throw new Error(`Cannot fork: source session file is empty or invalid: ${resolvedSourcePath}`);
2150
+ }
2151
+ const sourceHeader = sourceEntries.find((entry) => entry.type === "session");
2152
+ if (!sourceHeader) {
2153
+ throw new Error(`Cannot fork: source session has no header: ${resolvedSourcePath}`);
2154
+ }
2155
+ const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(resolvedTargetCwd);
2156
+ ensureDirectory(dir);
2157
+ if (options?.id !== undefined) {
2158
+ assertValidSessionId(options.id);
2159
+ }
2160
+ const newSessionId = options?.id ?? createSessionId();
2161
+ const timestamp = new Date().toISOString();
2162
+ const newSessionFile = join9(dir, `${timestamp.replace(/[:.]/g, "-")}_${newSessionId}.jsonl`);
2163
+ const newHeader = createSessionHeader(newSessionId, resolvedTargetCwd, timestamp, resolvedSourcePath, options?.internal, options?.workflow);
2164
+ appendSessionEntry(newSessionFile, newHeader);
2165
+ for (const entry of sourceEntries) {
2166
+ if (entry.type !== "session") {
2167
+ appendSessionEntry(newSessionFile, entry);
2168
+ }
2169
+ }
2170
+ return { cwd: resolvedTargetCwd, sessionDir: dir, sessionFile: newSessionFile };
2171
+ }
2172
+
2173
+ // src/core/session-manager-list.ts
2174
+ import { existsSync as existsSync6 } from "fs";
2175
+ import { readdir, readFile, stat } from "fs/promises";
2176
+ import { join as join10 } from "path";
2177
+
2178
+ // src/utils/event-loop.ts
2179
+ import { setImmediate as waitForImmediate } from "node:timers/promises";
2180
+ async function yieldToEventLoop() {
2181
+ await waitForImmediate();
2182
+ }
2183
+ async function yieldToEventLoopIfSlow(startedAt, thresholdMs = 16) {
2184
+ if (Date.now() - startedAt >= thresholdMs) {
2185
+ await yieldToEventLoop();
2186
+ }
2187
+ }
2188
+
2189
+ // src/core/session-manager-list.ts
2190
+ function isMessageWithContent(message) {
2191
+ return typeof message.role === "string" && "content" in message;
2192
+ }
2193
+ function extractTextContent(message) {
2194
+ const content = message.content;
2195
+ if (typeof content === "string") {
2196
+ return content;
2197
+ }
2198
+ return content.filter((block) => block.type === "text").map((block) => block.text).join(" ");
2199
+ }
2200
+ function getLastActivityTime(entries) {
2201
+ let lastActivityTime;
2202
+ for (const entry of entries) {
2203
+ if (entry.type !== "message")
2204
+ continue;
2205
+ const message = entry.message;
2206
+ if (!isMessageWithContent(message))
2207
+ continue;
2208
+ if (message.role !== "user" && message.role !== "assistant")
2209
+ continue;
2210
+ const msgTimestamp = message.timestamp;
2211
+ if (typeof msgTimestamp === "number") {
2212
+ lastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);
2213
+ continue;
2214
+ }
2215
+ const entryTimestamp = entry.timestamp;
2216
+ if (typeof entryTimestamp === "string") {
2217
+ const t = new Date(entryTimestamp).getTime();
2218
+ if (!Number.isNaN(t)) {
2219
+ lastActivityTime = Math.max(lastActivityTime ?? 0, t);
2220
+ }
2221
+ }
2222
+ }
2223
+ return lastActivityTime;
2224
+ }
2225
+ function getSessionModifiedDate(entries, header, statsMtime) {
2226
+ const lastActivityTime = getLastActivityTime(entries);
2227
+ if (typeof lastActivityTime === "number" && lastActivityTime > 0) {
2228
+ return new Date(lastActivityTime);
2229
+ }
2230
+ const headerTime = typeof header.timestamp === "string" ? new Date(header.timestamp).getTime() : NaN;
2231
+ return !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;
2232
+ }
2233
+ var COOPERATIVE_PARSE_CONTENT_BYTES = 512 * 1024;
2234
+ var PARSE_YIELD_EVERY_LINES = 2000;
2235
+ var LIST_FILE_BATCH_SIZE = 24;
2236
+ async function parseSessionEntriesCooperatively(content) {
2237
+ const entries = [];
2238
+ const lines = content.trim().split(`
2239
+ `);
2240
+ let startedAt = Date.now();
2241
+ for (let index = 0;index < lines.length; index += 1) {
2242
+ const line = lines[index];
2243
+ if (line.trim()) {
2244
+ try {
2245
+ entries.push(JSON.parse(line));
2246
+ } catch {}
2247
+ }
2248
+ if ((index + 1) % PARSE_YIELD_EVERY_LINES === 0) {
2249
+ await yieldToEventLoopIfSlow(startedAt);
2250
+ startedAt = Date.now();
2251
+ }
2252
+ }
2253
+ return entries;
2254
+ }
2255
+ async function mapSessionFilesCooperatively(files, includeInternal, onFileDone) {
2256
+ const results = [];
2257
+ for (let offset = 0;offset < files.length; offset += LIST_FILE_BATCH_SIZE) {
2258
+ const batch = files.slice(offset, offset + LIST_FILE_BATCH_SIZE);
2259
+ const startedAt = Date.now();
2260
+ const batchResults = await Promise.all(batch.map(async (file) => {
2261
+ if (!includeInternal && isInternalHeader(readSessionHeader(file))) {
2262
+ onFileDone();
2263
+ return null;
2264
+ }
2265
+ const info = await buildSessionInfo(file);
2266
+ onFileDone();
2267
+ return info;
2268
+ }));
2269
+ for (const info of batchResults)
2270
+ results.push(info);
2271
+ await yieldToEventLoopIfSlow(startedAt);
2272
+ }
2273
+ return results;
2274
+ }
2275
+ async function buildSessionInfo(filePath) {
2276
+ try {
2277
+ const content = await readFile(filePath, "utf8");
2278
+ const entries = content.length > COOPERATIVE_PARSE_CONTENT_BYTES ? await parseSessionEntriesCooperatively(content) : parseSessionEntries(content);
2279
+ if (entries.length === 0)
2280
+ return null;
2281
+ const header = entries[0];
2282
+ if (header.type !== "session")
2283
+ return null;
2284
+ const stats = await stat(filePath);
2285
+ let messageCount = 0;
2286
+ let firstMessage = "";
2287
+ const allMessages = [];
2288
+ let name;
2289
+ let hasName = false;
2290
+ for (const entry of entries) {
2291
+ if (entry.type === "session_info") {
2292
+ const infoEntry = entry;
2293
+ name = infoEntry.name?.trim() || undefined;
2294
+ hasName = true;
2295
+ }
2296
+ if (entry.type !== "message")
2297
+ continue;
2298
+ messageCount++;
2299
+ const message = entry.message;
2300
+ if (!isMessageWithContent(message))
2301
+ continue;
2302
+ if (message.role !== "user" && message.role !== "assistant")
2303
+ continue;
2304
+ const textContent = extractTextContent(message);
2305
+ if (!textContent)
2306
+ continue;
2307
+ allMessages.push(textContent);
2308
+ if (!firstMessage && message.role === "user") {
2309
+ firstMessage = textContent;
2310
+ }
2311
+ }
2312
+ const cwd = typeof header.cwd === "string" ? header.cwd : "";
2313
+ const parentSessionPath = header.parentSession;
2314
+ const workflow = classifiedWorkflowMetadata(header);
2315
+ const internal = workflow ? true : undefined;
2316
+ const modified = getSessionModifiedDate(entries, header, stats.mtime);
2317
+ const summaryEntry = getLatestSessionSummary(entries);
2318
+ const summary = summaryEntry?.summarizedThroughId === getLastConversationMessageId(entries) ? summaryEntry?.summary : undefined;
2319
+ return {
2320
+ path: filePath,
2321
+ id: header.id,
2322
+ cwd,
2323
+ name,
2324
+ ...hasName ? { hasName: true } : {},
2325
+ parentSessionPath,
2326
+ ...internal ? { internal } : {},
2327
+ ...workflow ? { workflow } : {},
2328
+ created: new Date(header.timestamp),
2329
+ modified,
2330
+ messageCount,
2331
+ firstMessage: firstMessage || "(no messages)",
2332
+ summary,
2333
+ allMessagesText: allMessages.join(" ")
2334
+ };
2335
+ } catch {
2336
+ return null;
2337
+ }
2338
+ }
2339
+ async function listSessionsFromDir(dir, onProgress, progressOffset = 0, progressTotal, includeInternal = false) {
2340
+ const sessions = [];
2341
+ if (!existsSync6(dir)) {
2342
+ return sessions;
2343
+ }
2344
+ try {
2345
+ const dirEntries = await readdir(dir);
2346
+ const files = dirEntries.filter((f) => f.endsWith(".jsonl")).map((f) => join10(dir, f));
2347
+ const total = progressTotal ?? files.length;
2348
+ let loaded = 0;
2349
+ const results = await mapSessionFilesCooperatively(files, includeInternal, () => {
2350
+ loaded++;
2351
+ onProgress?.(progressOffset + loaded, total);
2352
+ });
2353
+ for (const info of results) {
2354
+ if (info && (includeInternal || !info.internal)) {
2355
+ sessions.push(info);
2356
+ }
2357
+ }
2358
+ } catch {}
2359
+ return sessions;
2360
+ }
2361
+ async function listProjectSessions(cwd, sessionDir, onProgress, includeInternal = false) {
2362
+ const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
2363
+ const filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);
2364
+ const resolvedCwd = resolvePath(cwd);
2365
+ const sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter((session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd));
2366
+ sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
2367
+ return sessions;
2368
+ }
2369
+ async function listAllSessions(sessionDirOrOnProgress, onProgress, includeInternal = false) {
2370
+ const customSessionDir = typeof sessionDirOrOnProgress === "string" ? normalizePath(sessionDirOrOnProgress) : undefined;
2371
+ const progress = typeof sessionDirOrOnProgress === "function" ? sessionDirOrOnProgress : onProgress;
2372
+ if (customSessionDir) {
2373
+ const sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);
2374
+ sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
2375
+ return sessions;
2376
+ }
2377
+ const sessionsDir = getSessionsDir();
2378
+ try {
2379
+ if (!existsSync6(sessionsDir)) {
2380
+ return [];
2381
+ }
2382
+ const entries = await readdir(sessionsDir, { withFileTypes: true });
2383
+ const dirs = entries.filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map((entry) => join10(sessionsDir, entry.name));
2384
+ let totalFiles = 0;
2385
+ const dirFiles = [];
2386
+ for (const dir of dirs) {
2387
+ try {
2388
+ const files = (await readdir(dir)).filter((f) => f.endsWith(".jsonl"));
2389
+ dirFiles.push(files.map((f) => join10(dir, f)));
2390
+ totalFiles += files.length;
2391
+ } catch {
2392
+ dirFiles.push([]);
2393
+ }
2394
+ }
2395
+ let loaded = 0;
2396
+ const sessions = [];
2397
+ const allFiles = dirFiles.flat();
2398
+ const results = await mapSessionFilesCooperatively(allFiles, includeInternal, () => {
2399
+ loaded++;
2400
+ progress?.(loaded, totalFiles);
2401
+ });
2402
+ for (const info of results) {
2403
+ if (info && (includeInternal || !info.internal)) {
2404
+ sessions.push(info);
2405
+ }
2406
+ }
2407
+ sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
2408
+ return sessions;
2409
+ } catch {
2410
+ return [];
2411
+ }
2412
+ }
2413
+
2414
+ // src/core/session-manager-core.ts
2415
+ class SessionManager {
2416
+ sessionId = "";
2417
+ sessionFile;
2418
+ sessionDir;
2419
+ cwd;
2420
+ persist;
2421
+ flushed = false;
2422
+ fileEntries = [];
2423
+ byId = new Map;
2424
+ labelsById = new Map;
2425
+ labelTimestampsById = new Map;
2426
+ leafId = null;
2427
+ constructor(cwd, sessionDir, sessionFile, persist, newSessionOptions, preloadedFileEntries) {
2428
+ this.cwd = resolvePath(cwd);
2429
+ this.sessionDir = normalizePath(sessionDir);
2430
+ this.persist = persist;
2431
+ if (persist && this.sessionDir) {
2432
+ ensureDirectory(this.sessionDir);
2433
+ }
2434
+ if (sessionFile) {
2435
+ this._setSessionFile(sessionFile, preloadedFileEntries);
2436
+ } else {
2437
+ this.newSession(newSessionOptions);
2438
+ }
2439
+ }
2440
+ setSessionFile(sessionFile) {
2441
+ this._setSessionFile(sessionFile);
2442
+ }
2443
+ _setSessionFile(sessionFile, preloadedFileEntries) {
2444
+ this.sessionFile = resolvePath(sessionFile);
2445
+ if (existsSync7(this.sessionFile)) {
2446
+ this.fileEntries = preloadedFileEntries ?? loadEntriesFromFile(this.sessionFile);
2447
+ if (this.fileEntries.length === 0) {
2448
+ const explicitPath = this.sessionFile;
2449
+ if (statSync2(explicitPath).size > 0) {
2450
+ throw new Error(`Session file is not a valid ${APP_TITLE} session: ${explicitPath}`);
2451
+ }
2452
+ this.newSession();
2453
+ this.sessionFile = explicitPath;
2454
+ this._rewriteFile();
2455
+ this.flushed = true;
2456
+ return;
2457
+ }
2458
+ const header = this.fileEntries.find((entry) => entry.type === "session");
2459
+ this.sessionId = header?.id ?? createSessionId();
2460
+ if (migrateToCurrentVersion(this.fileEntries)) {
2461
+ this._rewriteFile();
2462
+ }
2463
+ this._buildIndex();
2464
+ this.flushed = true;
2465
+ } else {
2466
+ const explicitPath = this.sessionFile;
2467
+ this.newSession();
2468
+ this.sessionFile = explicitPath;
2469
+ }
2470
+ }
2471
+ newSession(options) {
2472
+ if (options?.id !== undefined) {
2473
+ assertValidSessionId(options.id);
2474
+ }
2475
+ this.sessionId = options?.id ?? createSessionId();
2476
+ const timestamp = new Date().toISOString();
2477
+ const header = createSessionHeader(this.sessionId, this.cwd, timestamp, options?.parentSession, options?.internal, options?.workflow);
2478
+ this.fileEntries = [header];
2479
+ this.byId.clear();
2480
+ this.labelsById.clear();
2481
+ this.labelTimestampsById.clear();
2482
+ this.leafId = null;
2483
+ this.flushed = false;
2484
+ if (this.persist) {
2485
+ this.sessionFile = createSessionFilePath(this.getSessionDir(), timestamp, this.sessionId);
2486
+ }
2487
+ return this.sessionFile;
2488
+ }
2489
+ markSessionInternal(workflow) {
2490
+ const header = this.fileEntries.find((entry) => entry.type === "session");
2491
+ if (!header || classifiedWorkflowMetadata(header))
2492
+ return;
2493
+ const validWorkflow = validSessionWorkflowMetadata(workflow);
2494
+ if (!validWorkflow)
2495
+ return;
2496
+ header.internal = true;
2497
+ header.workflow = validWorkflow;
2498
+ if (this.flushed)
2499
+ this._rewriteFile();
2500
+ }
2501
+ _buildIndex() {
2502
+ const index = buildSessionIndex(this.fileEntries);
2503
+ this.byId = index.byId;
2504
+ this.labelsById = index.labelsById;
2505
+ this.labelTimestampsById = index.labelTimestampsById;
2506
+ this.leafId = index.leafId;
2507
+ }
2508
+ _rewriteFile() {
2509
+ if (!this.persist || !this.sessionFile)
2510
+ return;
2511
+ writeSessionEntries(this.sessionFile, this.fileEntries);
2512
+ }
2513
+ isPersisted() {
2514
+ return this.persist;
2515
+ }
2516
+ flush() {
2517
+ if (!this.persist || !this.sessionFile)
2518
+ return;
2519
+ this._rewriteFile();
2520
+ this.flushed = true;
2521
+ }
2522
+ getCwd() {
2523
+ return this.cwd;
2524
+ }
2525
+ getSessionDir() {
2526
+ return this.sessionDir;
2527
+ }
2528
+ usesDefaultSessionDir() {
2529
+ return this.sessionDir === getDefaultSessionDirPath(this.cwd);
2530
+ }
2531
+ getSessionId() {
2532
+ return this.sessionId;
2533
+ }
2534
+ getSessionFile() {
2535
+ return this.sessionFile;
2536
+ }
2537
+ _persist(entry) {
2538
+ if (!this.persist || !this.sessionFile)
2539
+ return;
2540
+ this.flushed = persistAppendedEntry(this.sessionFile, this.fileEntries, entry, this.flushed);
2541
+ }
2542
+ _appendEntry(entry) {
2543
+ const previousLeafId = this.leafId;
2544
+ this.fileEntries.push(entry);
2545
+ this.byId.set(entry.id, entry);
2546
+ this.leafId = entry.id;
2547
+ try {
2548
+ this._persist(entry);
2549
+ } catch (error) {
2550
+ this.fileEntries.pop();
2551
+ this.byId.delete(entry.id);
2552
+ this.leafId = previousLeafId;
2553
+ throw error;
2554
+ }
2555
+ }
2556
+ appendMessage(message) {
2557
+ const entry = createMessageEntry(message, this.byId, this.leafId);
2558
+ this._appendEntry(entry);
2559
+ return entry.id;
2560
+ }
2561
+ appendThinkingLevelChange(thinkingLevel) {
2562
+ const entry = createThinkingLevelChangeEntry(thinkingLevel, this.byId, this.leafId);
2563
+ this._appendEntry(entry);
2564
+ return entry.id;
2565
+ }
2566
+ appendModelChange(provider, modelId) {
2567
+ const entry = createModelChangeEntry(provider, modelId, this.byId, this.leafId);
2568
+ this._appendEntry(entry);
2569
+ return entry.id;
2570
+ }
2571
+ appendCompaction(compactedText, firstKeptEntryId, tokensBefore, details, usage) {
2572
+ if (firstKeptEntryId !== null && !this.byId.has(firstKeptEntryId))
2573
+ throw new Error(`Entry ${firstKeptEntryId} not found`);
2574
+ const entry = createCompactionEntry(compactedText, firstKeptEntryId, tokensBefore, details, usage, this.byId, this.leafId);
2575
+ this._appendEntry(entry);
2576
+ return entry.id;
2577
+ }
2578
+ writeBackupSnapshot(label = "compact") {
2579
+ if (!this.persist)
2580
+ return;
2581
+ return createBackupSnapshot(this.sessionFile, this.fileEntries, label);
2582
+ }
2583
+ appendCustomEntry(customType, data) {
2584
+ const entry = createCustomEntry(customType, data, this.byId, this.leafId);
2585
+ this._appendEntry(entry);
2586
+ return entry.id;
2587
+ }
2588
+ appendSessionInfo(name) {
2589
+ const entry = createSessionInfoEntry(name, this.byId, this.leafId);
2590
+ this._appendEntry(entry);
2591
+ return entry.id;
2592
+ }
2593
+ appendSessionSummary(summary, summarizedThroughId, usage) {
2594
+ if (!this.byId.has(summarizedThroughId))
2595
+ throw new Error(`Entry ${summarizedThroughId} not found`);
2596
+ const entry = createSessionSummaryEntry(summary, summarizedThroughId, usage, this.byId, this.leafId);
2597
+ this._appendEntry(entry);
2598
+ return entry.id;
2599
+ }
2600
+ getSessionName() {
2601
+ return this.getSessionNameState().name;
2602
+ }
2603
+ getSessionNameState() {
2604
+ return getLatestSessionName(this.getEntries());
2605
+ }
2606
+ appendCustomMessageEntry(customType, content, display, details, excludeFromContext, protectedReconciliation, stageAdmissionKey) {
2607
+ const entry = createCustomMessageEntry(customType, content, display, details, excludeFromContext, protectedReconciliation, this.byId, this.leafId, stageAdmissionKey);
2608
+ this._appendEntry(entry);
2609
+ return entry.id;
2610
+ }
2611
+ getLeafId() {
2612
+ return this.leafId;
2613
+ }
2614
+ getLeafEntry() {
2615
+ return this.leafId ? this.byId.get(this.leafId) : undefined;
2616
+ }
2617
+ getEntry(id) {
2618
+ return this.byId.get(id);
2619
+ }
2620
+ getChildren(parentId) {
2621
+ const children = [];
2622
+ for (const entry of this.byId.values()) {
2623
+ if (entry.parentId === parentId) {
2624
+ children.push(entry);
2625
+ }
2626
+ }
2627
+ return children;
2628
+ }
2629
+ getLabel(id) {
2630
+ return this.labelsById.get(id);
2631
+ }
2632
+ appendLabelChange(targetId, label) {
2633
+ if (!this.byId.has(targetId)) {
2634
+ throw new Error(`Entry ${targetId} not found`);
2635
+ }
2636
+ const entry = createLabelEntry(targetId, label, this.byId, this.leafId);
2637
+ this._appendEntry(entry);
2638
+ if (label) {
2639
+ this.labelsById.set(targetId, label);
2640
+ this.labelTimestampsById.set(targetId, entry.timestamp);
2641
+ } else {
2642
+ this.labelsById.delete(targetId);
2643
+ this.labelTimestampsById.delete(targetId);
2644
+ }
2645
+ return entry.id;
2646
+ }
2647
+ getBranch(fromId) {
2648
+ return getBranchPath(fromId ?? this.leafId, this.byId);
2649
+ }
2650
+ buildSessionContext() {
2651
+ return buildSessionContext(this.getEntries(), this.leafId, this.byId);
2652
+ }
2653
+ getHeader() {
2654
+ const header = this.fileEntries.find((entry) => entry.type === "session");
2655
+ return header ? header : null;
2656
+ }
2657
+ getEntries() {
2658
+ return getEntriesWithoutHeader(this.fileEntries);
2659
+ }
2660
+ getTree() {
2661
+ return buildSessionTree(this.getEntries(), this.labelsById, this.labelTimestampsById);
2662
+ }
2663
+ branch(branchFromId) {
2664
+ if (!this.byId.has(branchFromId)) {
2665
+ throw new Error(`Entry ${branchFromId} not found`);
2666
+ }
2667
+ this.leafId = branchFromId;
2668
+ }
2669
+ resetLeaf() {
2670
+ this.leafId = null;
2671
+ }
2672
+ branchWithSummary(branchFromId, summary, details, fromHook, usage, fromId = this.leafId ?? "root") {
2673
+ if (branchFromId !== null && !this.byId.has(branchFromId)) {
2674
+ throw new Error(`Entry ${branchFromId} not found`);
2675
+ }
2676
+ this.leafId = branchFromId;
2677
+ const entry = createBranchSummaryEntry(branchFromId, fromId, summary, details, usage, fromHook, this.byId);
2678
+ this._appendEntry(entry);
2679
+ return entry.id;
2680
+ }
2681
+ createBranchedSession(leafId) {
2682
+ const result = createBranchedSessionState({
2683
+ leafId,
2684
+ persist: this.persist,
2685
+ sessionDir: this.getSessionDir(),
2686
+ cwd: this.cwd,
2687
+ previousSessionFile: this.sessionFile,
2688
+ workflow: classifiedWorkflowMetadata(this.getHeader()),
2689
+ path: this.getBranch(leafId),
2690
+ labelsById: this.labelsById,
2691
+ labelTimestampsById: this.labelTimestampsById
2692
+ });
2693
+ this.fileEntries = result.fileEntries;
2694
+ this.sessionId = result.sessionId;
2695
+ if (result.sessionFile)
2696
+ this.sessionFile = result.sessionFile;
2697
+ this._buildIndex();
2698
+ if (this.persist) {
2699
+ if (result.shouldRewriteFile)
2700
+ this._rewriteFile();
2701
+ this.flushed = result.flushed ?? false;
2702
+ return result.sessionFile;
2703
+ }
2704
+ return;
2705
+ }
2706
+ static create(cwd, sessionDir, options) {
2707
+ const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
2708
+ return new SessionManager(cwd, dir, undefined, true, options);
2709
+ }
2710
+ static open(path, sessionDir, cwdOverride) {
2711
+ const resolvedPath = resolvePath(path);
2712
+ const entries = loadEntriesFromFile(resolvedPath);
2713
+ const header = entries.find((entry) => entry.type === "session");
2714
+ const cwd = cwdOverride ?? header?.cwd ?? process.cwd();
2715
+ const dir = sessionDir ? normalizePath(sessionDir) : resolve3(resolvedPath, "..");
2716
+ return new SessionManager(cwd, dir, resolvedPath, true, undefined, entries);
2717
+ }
2718
+ static continueRecent(cwd, sessionDir, options) {
2719
+ const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
2720
+ const filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);
2721
+ const mostRecent = findMostRecentSession(dir, filterCwd ? cwd : undefined, options?.includeInternal === true);
2722
+ return new SessionManager(cwd, dir, mostRecent ?? undefined, true);
2723
+ }
2724
+ static inMemory(cwd = process.cwd(), options) {
2725
+ return new SessionManager(cwd, "", undefined, false, options);
2726
+ }
2727
+ static forkFrom(sourcePath, targetCwd, sessionDir, options) {
2728
+ const forked = forkSessionFromFile(sourcePath, targetCwd, sessionDir, options);
2729
+ return new SessionManager(forked.cwd, forked.sessionDir, forked.sessionFile, true);
2730
+ }
2731
+ static async list(cwd, sessionDir, onProgress, options) {
2732
+ return listProjectSessions(cwd, sessionDir, onProgress, options?.includeInternal === true);
2733
+ }
2734
+ static async listAll(sessionDirOrOnProgress, onProgress, options) {
2735
+ return listAllSessions(sessionDirOrOnProgress, onProgress, options?.includeInternal === true);
2736
+ }
2737
+ }
2738
+ // src/utils/fs-watch.ts
2739
+ import { realpathSync as realpathSync3, watch } from "node:fs";
2740
+ var FS_WATCH_RETRY_DELAY_MS = 5000;
2741
+ var SAFE_FS_WATCH_CANONICALIZATION_FAILED = "ERR_SAFE_FS_WATCH_CANONICALIZATION_FAILED";
2742
+ var SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH = "ERR_SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH";
2743
+ function closeWatcher(watcher) {
2744
+ if (!watcher) {
2745
+ return;
2746
+ }
2747
+ try {
2748
+ watcher.close();
2749
+ } catch {}
2750
+ }
2751
+ function createSafeFsWatchPathError(code, watchedPath, message, resolvedPath, cause) {
2752
+ const error = new Error(message, cause ? { cause } : undefined);
2753
+ error.name = "SafeFsWatchPathError";
2754
+ error.code = code;
2755
+ error.watchedPath = watchedPath;
2756
+ if (resolvedPath !== undefined) {
2757
+ error.resolvedPath = resolvedPath;
2758
+ }
2759
+ return error;
2760
+ }
2761
+ function isUnsafeWindowsShortPath(path, platform = process.platform) {
2762
+ if (platform !== "win32") {
2763
+ return false;
2764
+ }
2765
+ return path.split(/[\\/]+/).some((component) => /~\d+(?:\.|$)/i.test(component));
2766
+ }
2767
+ function isSafeFsWatchPathError(error) {
2768
+ if (typeof error !== "object" || error === null || !("code" in error)) {
2769
+ return false;
2770
+ }
2771
+ const code = error.code;
2772
+ return code === SAFE_FS_WATCH_CANONICALIZATION_FAILED || code === SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH;
2773
+ }
2774
+ function resolveNativeWatchPath(path, options = {}) {
2775
+ const platform = options.platform ?? process.platform;
2776
+ if (platform !== "win32") {
2777
+ return { path };
2778
+ }
2779
+ const resolvePath2 = options.realpathSyncNative ?? realpathSync3.native;
2780
+ let resolvedPath;
2781
+ try {
2782
+ resolvedPath = resolvePath2(path);
2783
+ } catch (error) {
2784
+ return {
2785
+ error: createSafeFsWatchPathError(SAFE_FS_WATCH_CANONICALIZATION_FAILED, path, `Cannot canonicalize Windows fs.watch path '${path}'.`, undefined, error instanceof Error ? error : undefined)
2786
+ };
2787
+ }
2788
+ if (isUnsafeWindowsShortPath(resolvedPath, platform)) {
2789
+ return {
2790
+ error: createSafeFsWatchPathError(SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH, path, `Refusing native fs.watch for unsafe Windows short-name path '${resolvedPath}'.`, resolvedPath)
2791
+ };
2792
+ }
2793
+ return { path: resolvedPath };
2794
+ }
2795
+ function watchWithErrorHandler(path, listener, onError, options = {}) {
2796
+ const resolved = resolveNativeWatchPath(path, options);
2797
+ if ("error" in resolved) {
2798
+ onError(resolved.error);
2799
+ return null;
2800
+ }
2801
+ try {
2802
+ const watchPath = options.watch ?? watch;
2803
+ const watcher = watchPath(resolved.path, listener);
2804
+ watcher.on("error", onError);
2805
+ return watcher;
2806
+ } catch (error) {
2807
+ onError(error instanceof Error ? error : new Error(String(error)));
2808
+ return null;
2809
+ }
2810
+ }
2811
+
2812
+ // src/modes/interactive/theme/theme-class.ts
2813
+ import chalk from "chalk";
2814
+
2815
+ // src/modes/interactive/theme/color-utils.ts
2816
+ function detectColorMode() {
2817
+ const colorterm = process.env.COLORTERM;
2818
+ if (colorterm === "truecolor" || colorterm === "24bit") {
2819
+ return "truecolor";
2820
+ }
2821
+ if (process.env.WT_SESSION) {
2822
+ return "truecolor";
2823
+ }
2824
+ const term = process.env.TERM || "";
2825
+ if (term === "dumb" || term === "" || term === "linux") {
2826
+ return "256color";
2827
+ }
2828
+ if (process.env.TERM_PROGRAM === "Apple_Terminal") {
2829
+ return "256color";
2830
+ }
2831
+ if (term === "screen" || term.startsWith("screen-") || term.startsWith("screen.")) {
2832
+ return "256color";
2833
+ }
2834
+ return "truecolor";
2835
+ }
2836
+ function hexToRgb(hex) {
2837
+ const cleaned = hex.replace("#", "");
2838
+ if (cleaned.length !== 6) {
2839
+ throw new Error(`Invalid hex color: ${hex}`);
2840
+ }
2841
+ const r = parseInt(cleaned.substring(0, 2), 16);
2842
+ const g = parseInt(cleaned.substring(2, 4), 16);
2843
+ const b = parseInt(cleaned.substring(4, 6), 16);
2844
+ if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
2845
+ throw new Error(`Invalid hex color: ${hex}`);
2846
+ }
2847
+ return { r, g, b };
2848
+ }
2849
+ var CUBE_VALUES = [0, 95, 135, 175, 215, 255];
2850
+ var GRAY_VALUES = Array.from({ length: 24 }, (_, i) => 8 + i * 10);
2851
+ function findClosestCubeIndex(value) {
2852
+ let minDist = Infinity;
2853
+ let minIdx = 0;
2854
+ for (let i = 0;i < CUBE_VALUES.length; i++) {
2855
+ const dist = Math.abs(value - CUBE_VALUES[i]);
2856
+ if (dist < minDist) {
2857
+ minDist = dist;
2858
+ minIdx = i;
2859
+ }
2860
+ }
2861
+ return minIdx;
2862
+ }
2863
+ function findClosestGrayIndex(gray) {
2864
+ let minDist = Infinity;
2865
+ let minIdx = 0;
2866
+ for (let i = 0;i < GRAY_VALUES.length; i++) {
2867
+ const dist = Math.abs(gray - GRAY_VALUES[i]);
2868
+ if (dist < minDist) {
2869
+ minDist = dist;
2870
+ minIdx = i;
2871
+ }
2872
+ }
2873
+ return minIdx;
2874
+ }
2875
+ function colorDistance(r1, g1, b1, r2, g2, b2) {
2876
+ const dr = r1 - r2;
2877
+ const dg = g1 - g2;
2878
+ const db = b1 - b2;
2879
+ return dr * dr * 0.299 + dg * dg * 0.587 + db * db * 0.114;
2880
+ }
2881
+ function rgbTo256(r, g, b) {
2882
+ const rIdx = findClosestCubeIndex(r);
2883
+ const gIdx = findClosestCubeIndex(g);
2884
+ const bIdx = findClosestCubeIndex(b);
2885
+ const cubeR = CUBE_VALUES[rIdx];
2886
+ const cubeG = CUBE_VALUES[gIdx];
2887
+ const cubeB = CUBE_VALUES[bIdx];
2888
+ const cubeIndex = 16 + 36 * rIdx + 6 * gIdx + bIdx;
2889
+ const cubeDist = colorDistance(r, g, b, cubeR, cubeG, cubeB);
2890
+ const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
2891
+ const grayIdx = findClosestGrayIndex(gray);
2892
+ const grayValue = GRAY_VALUES[grayIdx];
2893
+ const grayIndex = 232 + grayIdx;
2894
+ const grayDist = colorDistance(r, g, b, grayValue, grayValue, grayValue);
2895
+ const maxC = Math.max(r, g, b);
2896
+ const minC = Math.min(r, g, b);
2897
+ const spread = maxC - minC;
2898
+ if (spread < 10 && grayDist < cubeDist) {
2899
+ return grayIndex;
2900
+ }
2901
+ return cubeIndex;
2902
+ }
2903
+ function hexTo256(hex) {
2904
+ const { r, g, b } = hexToRgb(hex);
2905
+ return rgbTo256(r, g, b);
2906
+ }
2907
+ function fgAnsi(color, mode) {
2908
+ if (color === "")
2909
+ return "\x1B[39m";
2910
+ if (typeof color === "number")
2911
+ return `\x1B[38;5;${color}m`;
2912
+ if (color.startsWith("#")) {
2913
+ if (mode === "truecolor") {
2914
+ const { r, g, b } = hexToRgb(color);
2915
+ return `\x1B[38;2;${r};${g};${b}m`;
2916
+ } else {
2917
+ const index = hexTo256(color);
2918
+ return `\x1B[38;5;${index}m`;
2919
+ }
2920
+ }
2921
+ throw new Error(`Invalid color value: ${color}`);
2922
+ }
2923
+ function bgAnsi(color, mode) {
2924
+ if (color === "")
2925
+ return "\x1B[49m";
2926
+ if (typeof color === "number")
2927
+ return `\x1B[48;5;${color}m`;
2928
+ if (color.startsWith("#")) {
2929
+ if (mode === "truecolor") {
2930
+ const { r, g, b } = hexToRgb(color);
2931
+ return `\x1B[48;2;${r};${g};${b}m`;
2932
+ } else {
2933
+ const index = hexTo256(color);
2934
+ return `\x1B[48;5;${index}m`;
2935
+ }
2936
+ }
2937
+ throw new Error(`Invalid color value: ${color}`);
2938
+ }
2939
+ function resolveVarRefs(value, vars, visited = new Set) {
2940
+ if (typeof value === "number" || value === "" || value.startsWith("#")) {
2941
+ return value;
2942
+ }
2943
+ if (visited.has(value)) {
2944
+ throw new Error(`Circular variable reference detected: ${value}`);
2945
+ }
2946
+ if (!(value in vars)) {
2947
+ throw new Error(`Variable reference not found: ${value}`);
2948
+ }
2949
+ visited.add(value);
2950
+ return resolveVarRefs(vars[value], vars, visited);
2951
+ }
2952
+ function resolveThemeColors(colors, vars = {}) {
2953
+ const resolved = {};
2954
+ for (const [key, value] of Object.entries(colors)) {
2955
+ resolved[key] = resolveVarRefs(value, vars);
2956
+ }
2957
+ return resolved;
2958
+ }
2959
+ function ansi256ToHex(index) {
2960
+ const basicColors = [
2961
+ "#000000",
2962
+ "#800000",
2963
+ "#008000",
2964
+ "#808000",
2965
+ "#000080",
2966
+ "#800080",
2967
+ "#008080",
2968
+ "#c0c0c0",
2969
+ "#808080",
2970
+ "#ff0000",
2971
+ "#00ff00",
2972
+ "#ffff00",
2973
+ "#0000ff",
2974
+ "#ff00ff",
2975
+ "#00ffff",
2976
+ "#ffffff"
2977
+ ];
2978
+ if (index < 16) {
2979
+ return basicColors[index];
2980
+ }
2981
+ if (index < 232) {
2982
+ const cubeIndex = index - 16;
2983
+ const r = Math.floor(cubeIndex / 36);
2984
+ const g = Math.floor(cubeIndex % 36 / 6);
2985
+ const b = cubeIndex % 6;
2986
+ const toHex = (n) => (n === 0 ? 0 : 55 + n * 40).toString(16).padStart(2, "0");
2987
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
2988
+ }
2989
+ const gray = 8 + (index - 232) * 10;
2990
+ const grayHex = gray.toString(16).padStart(2, "0");
2991
+ return `#${grayHex}${grayHex}${grayHex}`;
2992
+ }
2993
+
2994
+ // src/modes/interactive/theme/theme-class.ts
2995
+ class Theme {
2996
+ name;
2997
+ sourcePath;
2998
+ sourceInfo;
2999
+ fgColors;
3000
+ bgColors;
3001
+ mode;
3002
+ workingIndicatorColors;
3003
+ constructor(fgColors, bgColors, mode, options = {}) {
3004
+ this.name = options.name;
3005
+ this.sourcePath = options.sourcePath;
3006
+ this.sourceInfo = options.sourceInfo;
3007
+ this.mode = mode;
3008
+ this.fgColors = new Map;
3009
+ const foregrounds = {
3010
+ ...fgColors,
3011
+ searchMatchText: fgColors.searchMatchText ?? fgColors.text
3012
+ };
3013
+ for (const [key, value] of Object.entries(foregrounds)) {
3014
+ this.fgColors.set(key, fgAnsi(value, mode));
3015
+ }
3016
+ this.bgColors = new Map;
3017
+ const backgrounds = {
3018
+ ...bgColors,
3019
+ scrollbarThumb: bgColors.scrollbarThumb ?? bgColors.selectedBg,
3020
+ searchMatchBg: bgColors.searchMatchBg ?? bgColors.selectedBg
3021
+ };
3022
+ for (const [key, value] of Object.entries(backgrounds)) {
3023
+ this.bgColors.set(key, bgAnsi(value, mode));
3024
+ }
3025
+ this.workingIndicatorColors = new Map;
3026
+ for (const [key, value] of Object.entries(options.workingIndicator ?? {})) {
3027
+ this.workingIndicatorColors.set(key, fgAnsi(value, mode));
3028
+ }
3029
+ }
3030
+ fg(color, text) {
3031
+ const ansi = this.fgColors.get(color);
3032
+ if (!ansi)
3033
+ throw new Error(`Unknown theme color: ${color}`);
3034
+ return `${ansi}${text}\x1B[39m`;
3035
+ }
3036
+ bg(color, text) {
3037
+ const ansi = this.bgColors.get(color);
3038
+ if (!ansi)
3039
+ throw new Error(`Unknown theme background color: ${color}`);
3040
+ return `${ansi}${text}\x1B[49m`;
3041
+ }
3042
+ bold(text) {
3043
+ return chalk.bold(text);
3044
+ }
3045
+ italic(text) {
3046
+ return chalk.italic(text);
3047
+ }
3048
+ underline(text) {
3049
+ return chalk.underline(text);
3050
+ }
3051
+ inverse(text) {
3052
+ return chalk.inverse(text);
3053
+ }
3054
+ strikethrough(text) {
3055
+ return chalk.strikethrough(text);
3056
+ }
3057
+ getFgAnsi(color) {
3058
+ const ansi = this.fgColors.get(color);
3059
+ if (!ansi)
3060
+ throw new Error(`Unknown theme color: ${color}`);
3061
+ return ansi;
3062
+ }
3063
+ getWorkingIndicatorAnsi(tone) {
3064
+ return this.workingIndicatorColors.get(tone);
3065
+ }
3066
+ getBgAnsi(color) {
3067
+ const ansi = this.bgColors.get(color);
3068
+ if (!ansi)
3069
+ throw new Error(`Unknown theme background color: ${color}`);
3070
+ return ansi;
3071
+ }
3072
+ getColorMode() {
3073
+ return this.mode;
3074
+ }
3075
+ getThinkingBorderColor(level) {
3076
+ switch (level) {
3077
+ case "off":
3078
+ return (str) => this.fg("thinkingOff", str);
3079
+ case "minimal":
3080
+ return (str) => this.fg("thinkingMinimal", str);
3081
+ case "low":
3082
+ return (str) => this.fg("thinkingLow", str);
3083
+ case "medium":
3084
+ return (str) => this.fg("thinkingMedium", str);
3085
+ case "high":
3086
+ return (str) => this.fg("thinkingHigh", str);
3087
+ case "xhigh":
3088
+ return (str) => this.fg("thinkingXhigh", str);
3089
+ case "max":
3090
+ return (str) => this.fg("thinkingXhigh", str);
3091
+ default:
3092
+ return (str) => this.fg("thinkingOff", str);
3093
+ }
3094
+ }
3095
+ getBashModeBorderColor() {
3096
+ return (str) => this.fg("bashMode", str);
3097
+ }
3098
+ }
3099
+
3100
+ // src/modes/interactive/theme/global-theme.ts
3101
+ import * as fs2 from "node:fs";
3102
+ import * as path2 from "node:path";
3103
+
3104
+ // src/modes/interactive/theme/terminal-detection.ts
3105
+ function parseAutoThemeSetting(themeSetting) {
3106
+ if (!themeSetting)
3107
+ return;
3108
+ const slashIndex = themeSetting.indexOf("/");
3109
+ if (slashIndex === -1 || themeSetting.indexOf("/", slashIndex + 1) !== -1) {
3110
+ return;
3111
+ }
3112
+ const lightTheme = themeSetting.slice(0, slashIndex).trim();
3113
+ const darkTheme = themeSetting.slice(slashIndex + 1).trim();
3114
+ if (!lightTheme || !darkTheme) {
3115
+ return;
3116
+ }
3117
+ return { lightTheme, darkTheme };
3118
+ }
3119
+ function resolveThemeSetting(themeSetting, terminalTheme) {
3120
+ const autoTheme = parseAutoThemeSetting(themeSetting);
3121
+ if (autoTheme) {
3122
+ return terminalTheme === "light" ? autoTheme.lightTheme : autoTheme.darkTheme;
3123
+ }
3124
+ if (themeSetting?.includes("/"))
3125
+ return;
3126
+ if (typeof themeSetting === "string")
3127
+ return themeSetting;
3128
+ return;
3129
+ }
3130
+ function getColorFgBgBackgroundIndex(colorfgbg) {
3131
+ const parts = colorfgbg.split(";");
3132
+ for (let i = parts.length - 1;i >= 0; i--) {
3133
+ const bg = parseInt(parts[i].trim(), 10);
3134
+ if (Number.isInteger(bg) && bg >= 0 && bg <= 255) {
3135
+ return bg;
3136
+ }
3137
+ }
3138
+ return;
3139
+ }
3140
+ function getRgbColorLuminance({ r, g, b }) {
3141
+ const toLinear = (channel) => {
3142
+ const value = channel / 255;
3143
+ return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
3144
+ };
3145
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
3146
+ }
3147
+ function getAnsiColorLuminance(index) {
3148
+ return getRgbColorLuminance(hexToRgb(ansi256ToHex(index)));
3149
+ }
3150
+ function getThemeForRgbColor(rgb) {
3151
+ return getRgbColorLuminance(rgb) >= 0.5 ? "light" : "dark";
3152
+ }
3153
+ function detectTerminalBackgroundFromEnv(options = {}) {
3154
+ const env = options.env ?? process.env;
3155
+ const colorfgbg = env.COLORFGBG || "";
3156
+ const bg = getColorFgBgBackgroundIndex(colorfgbg);
3157
+ if (bg !== undefined) {
3158
+ return {
3159
+ theme: getAnsiColorLuminance(bg) >= 0.5 ? "light" : "dark",
3160
+ source: "COLORFGBG",
3161
+ detail: `background color index ${bg}`,
3162
+ confidence: "high"
3163
+ };
3164
+ }
3165
+ return {
3166
+ theme: "dark",
3167
+ source: "fallback",
3168
+ detail: "no terminal background hint found",
3169
+ confidence: "low"
3170
+ };
3171
+ }
3172
+ async function detectTerminalBackgroundTheme({
3173
+ ui,
3174
+ timeoutMs,
3175
+ env
3176
+ }) {
3177
+ try {
3178
+ const rgb = await ui.queryTerminalBackgroundColor({ timeoutMs });
3179
+ if (rgb) {
3180
+ return {
3181
+ theme: getThemeForRgbColor(rgb),
3182
+ source: "terminal background",
3183
+ detail: `OSC 11 background rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,
3184
+ confidence: "high"
3185
+ };
3186
+ }
3187
+ } catch {}
3188
+ return detectTerminalBackgroundFromEnv({ env });
3189
+ }
3190
+ async function detectTerminalThemeForAuto({
3191
+ ui,
3192
+ timeoutMs,
3193
+ env
3194
+ }) {
3195
+ let colorSchemePromise;
3196
+ try {
3197
+ colorSchemePromise = ui.queryTerminalColorScheme({ timeoutMs });
3198
+ } catch {}
3199
+ const backgroundThemePromise = detectTerminalBackgroundTheme({ ui, timeoutMs, env });
3200
+ try {
3201
+ const colorScheme = await colorSchemePromise;
3202
+ if (colorScheme)
3203
+ return colorScheme;
3204
+ } catch {}
3205
+ return (await backgroundThemePromise).theme;
3206
+ }
3207
+ function getDefaultTheme() {
3208
+ return detectTerminalBackgroundFromEnv().theme;
3209
+ }
3210
+
3211
+ // src/modes/interactive/theme/theme-loading.ts
3212
+ import * as fs from "node:fs";
3213
+ import * as path from "node:path";
3214
+
3215
+ // src/modes/interactive/theme/theme-schema.ts
3216
+ import { Type } from "typebox";
3217
+ import { Compile } from "typebox/compile";
3218
+ var ColorValueSchema = Type.Union([
3219
+ Type.String(),
3220
+ Type.Integer({ minimum: 0, maximum: 255 })
3221
+ ]);
3222
+ var ThemeJsonSchema = Type.Object({
3223
+ $schema: Type.Optional(Type.String()),
3224
+ name: Type.String(),
3225
+ vars: Type.Optional(Type.Record(Type.String(), ColorValueSchema)),
3226
+ colors: Type.Object({
3227
+ accent: ColorValueSchema,
3228
+ border: ColorValueSchema,
3229
+ borderAccent: ColorValueSchema,
3230
+ borderMuted: ColorValueSchema,
3231
+ success: ColorValueSchema,
3232
+ error: ColorValueSchema,
3233
+ warning: ColorValueSchema,
3234
+ muted: ColorValueSchema,
3235
+ dim: ColorValueSchema,
3236
+ text: ColorValueSchema,
3237
+ thinkingText: ColorValueSchema,
3238
+ selectedBg: ColorValueSchema,
3239
+ scrollbarThumb: Type.Optional(ColorValueSchema),
3240
+ searchMatchBg: Type.Optional(ColorValueSchema),
3241
+ searchMatchText: Type.Optional(ColorValueSchema),
3242
+ userMessageBg: ColorValueSchema,
3243
+ userMessageText: ColorValueSchema,
3244
+ customMessageBg: ColorValueSchema,
3245
+ customMessageText: ColorValueSchema,
3246
+ customMessageLabel: ColorValueSchema,
3247
+ toolPendingBg: ColorValueSchema,
3248
+ toolSuccessBg: ColorValueSchema,
3249
+ toolErrorBg: ColorValueSchema,
3250
+ toolTitle: ColorValueSchema,
3251
+ toolOutput: ColorValueSchema,
3252
+ mdHeading: ColorValueSchema,
3253
+ mdLink: ColorValueSchema,
3254
+ mdLinkUrl: ColorValueSchema,
3255
+ mdCode: ColorValueSchema,
3256
+ mdCodeBlock: ColorValueSchema,
3257
+ mdCodeBlockBorder: ColorValueSchema,
3258
+ mdQuote: ColorValueSchema,
3259
+ mdQuoteBorder: ColorValueSchema,
3260
+ mdHr: ColorValueSchema,
3261
+ mdListBullet: ColorValueSchema,
3262
+ toolDiffAdded: ColorValueSchema,
3263
+ toolDiffRemoved: ColorValueSchema,
3264
+ toolDiffContext: ColorValueSchema,
3265
+ syntaxComment: ColorValueSchema,
3266
+ syntaxKeyword: ColorValueSchema,
3267
+ syntaxFunction: ColorValueSchema,
3268
+ syntaxVariable: ColorValueSchema,
3269
+ syntaxString: ColorValueSchema,
3270
+ syntaxNumber: ColorValueSchema,
3271
+ syntaxType: ColorValueSchema,
3272
+ syntaxOperator: ColorValueSchema,
3273
+ syntaxPunctuation: ColorValueSchema,
3274
+ thinkingOff: ColorValueSchema,
3275
+ thinkingMinimal: ColorValueSchema,
3276
+ thinkingLow: ColorValueSchema,
3277
+ thinkingMedium: ColorValueSchema,
3278
+ thinkingHigh: ColorValueSchema,
3279
+ thinkingXhigh: ColorValueSchema,
3280
+ bashMode: ColorValueSchema
3281
+ }),
3282
+ workingIndicator: Type.Optional(Type.Object({
3283
+ dark: Type.Optional(ColorValueSchema),
3284
+ lift: Type.Optional(ColorValueSchema),
3285
+ muted: Type.Optional(ColorValueSchema),
3286
+ accent: Type.Optional(ColorValueSchema),
3287
+ bright: Type.Optional(ColorValueSchema),
3288
+ peak: Type.Optional(ColorValueSchema)
3289
+ })),
3290
+ export: Type.Optional(Type.Object({
3291
+ pageBg: Type.Optional(ColorValueSchema),
3292
+ cardBg: Type.Optional(ColorValueSchema),
3293
+ infoBg: Type.Optional(ColorValueSchema)
3294
+ }))
3295
+ });
3296
+ var validateThemeJson = Compile(ThemeJsonSchema);
3297
+
3298
+ // src/modes/interactive/theme/theme-parse.ts
3299
+ function assertThemeNameIsValid(name) {
3300
+ if (name.includes("/")) {
3301
+ throw new Error(`Invalid theme name "${name}": theme names cannot contain "/" because it is reserved for automatic light/dark theme settings.`);
3302
+ }
3303
+ }
3304
+ function parseThemeJson(label, json) {
3305
+ if (!validateThemeJson.Check(json)) {
3306
+ const errors = Array.from(validateThemeJson.Errors(json));
3307
+ const missingColors = new Set;
3308
+ const otherErrors = [];
3309
+ for (const error of errors) {
3310
+ if (error.keyword === "required" && error.instancePath === "/colors") {
3311
+ const requiredProperties = error.params.requiredProperties;
3312
+ for (const requiredProperty of requiredProperties ?? []) {
3313
+ missingColors.add(requiredProperty);
3314
+ }
3315
+ continue;
3316
+ }
3317
+ const path = error.instancePath || "/";
3318
+ otherErrors.push(` - ${path}: ${error.message}`);
3319
+ }
3320
+ let errorMessage = `Invalid theme "${label}":
3321
+ `;
3322
+ if (missingColors.size > 0) {
3323
+ errorMessage += `
3324
+ Missing required color tokens:
3325
+ `;
3326
+ errorMessage += Array.from(missingColors).sort().map((color) => ` - ${color}`).join(`
3327
+ `);
3328
+ errorMessage += `
3329
+
3330
+ Please add these colors to your theme's "colors" object.`;
3331
+ errorMessage += `
3332
+ See the built-in themes (dark.json, light.json) for reference values.`;
3333
+ }
3334
+ if (otherErrors.length > 0) {
3335
+ errorMessage += `
3336
+
3337
+ Other errors:
3338
+ ${otherErrors.join(`
3339
+ `)}`;
3340
+ }
3341
+ throw new Error(errorMessage);
3342
+ }
3343
+ const themeJson = json;
3344
+ assertThemeNameIsValid(themeJson.name);
3345
+ return themeJson;
3346
+ }
3347
+ function parseThemeJsonContent(label, content) {
3348
+ let json;
3349
+ try {
3350
+ json = JSON.parse(stripBom(content));
3351
+ } catch (error) {
3352
+ throw new Error(`Failed to parse theme ${label}: ${error}`);
3353
+ }
3354
+ return parseThemeJson(label, json);
3355
+ }
3356
+
3357
+ // src/modes/interactive/theme/theme-loading.ts
3358
+ var BUILTIN_THEMES;
3359
+ var registeredThemes = new Map;
3360
+ function getBuiltinThemes() {
3361
+ if (!BUILTIN_THEMES) {
3362
+ const themesDir = getThemesDir();
3363
+ const themes = {};
3364
+ for (const file of fs.readdirSync(themesDir).sort()) {
3365
+ if (!file.endsWith(".json") || file === "theme-schema.json") {
3366
+ continue;
3367
+ }
3368
+ const themePath = path.join(themesDir, file);
3369
+ const themeJson = parseThemeJsonContent(themePath, fs.readFileSync(themePath, "utf-8"));
3370
+ themes[themeJson.name] = themeJson;
3371
+ }
3372
+ BUILTIN_THEMES = themes;
3373
+ }
3374
+ return BUILTIN_THEMES;
3375
+ }
3376
+ function getAvailableThemes() {
3377
+ return getAvailableThemesWithPaths().map(({ name }) => name);
3378
+ }
3379
+ function getAvailableThemesWithPaths() {
3380
+ const themesDir = getThemesDir();
3381
+ const result = [];
3382
+ const seen = new Set;
3383
+ const addTheme = (info) => {
3384
+ if (seen.has(info.name))
3385
+ return;
3386
+ seen.add(info.name);
3387
+ result.push(info);
3388
+ };
3389
+ for (const name of Object.keys(getBuiltinThemes())) {
3390
+ addTheme({ name, path: path.join(themesDir, `${name}.json`) });
3391
+ }
3392
+ for (const info of getCustomThemeInfos()) {
3393
+ addTheme(info);
3394
+ }
3395
+ for (const [name, theme] of registeredThemes.entries()) {
3396
+ addTheme({ name, path: theme.sourcePath });
3397
+ }
3398
+ return result.sort((a, b) => a.name.localeCompare(b.name));
3399
+ }
3400
+ function getCustomThemeInfos() {
3401
+ const customThemesDir = getCustomThemesDir();
3402
+ const result = [];
3403
+ if (!fs.existsSync(customThemesDir)) {
3404
+ return result;
3405
+ }
3406
+ for (const file of fs.readdirSync(customThemesDir)) {
3407
+ if (!file.endsWith(".json")) {
3408
+ continue;
3409
+ }
3410
+ const themePath = path.join(customThemesDir, file);
3411
+ try {
3412
+ const customTheme = loadThemeFromPath(themePath);
3413
+ if (customTheme.name) {
3414
+ result.push({ name: customTheme.name, path: themePath });
3415
+ }
3416
+ } catch {}
3417
+ }
3418
+ return result;
3419
+ }
3420
+ function loadThemeJson(name) {
3421
+ const builtinThemes = getBuiltinThemes();
3422
+ if (name in builtinThemes) {
3423
+ return builtinThemes[name];
3424
+ }
3425
+ const registeredTheme = registeredThemes.get(name);
3426
+ if (registeredTheme?.sourcePath) {
3427
+ const content2 = fs.readFileSync(registeredTheme.sourcePath, "utf-8");
3428
+ return parseThemeJsonContent(registeredTheme.sourcePath, content2);
3429
+ }
3430
+ if (registeredTheme) {
3431
+ throw new Error(`Theme "${name}" does not have a source path for export`);
3432
+ }
3433
+ const customThemesDir = getCustomThemesDir();
3434
+ const themePath = path.join(customThemesDir, `${name}.json`);
3435
+ if (!fs.existsSync(themePath)) {
3436
+ throw new Error(`Theme not found: ${name}`);
3437
+ }
3438
+ const content = fs.readFileSync(themePath, "utf-8");
3439
+ return parseThemeJsonContent(name, content);
3440
+ }
3441
+ function createTheme(themeJson, mode, sourcePath) {
3442
+ const colorMode = mode ?? detectColorMode();
3443
+ const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
3444
+ const workingIndicator = themeJson.workingIndicator ? resolveThemeColors(themeJson.workingIndicator, themeJson.vars) : undefined;
3445
+ const fgColors = {};
3446
+ const bgColors = {};
3447
+ const bgColorKeys = new Set([
3448
+ "selectedBg",
3449
+ "scrollbarThumb",
3450
+ "searchMatchBg",
3451
+ "userMessageBg",
3452
+ "customMessageBg",
3453
+ "toolPendingBg",
3454
+ "toolSuccessBg",
3455
+ "toolErrorBg"
3456
+ ]);
3457
+ for (const [key, value] of Object.entries(resolvedColors)) {
3458
+ if (bgColorKeys.has(key)) {
3459
+ bgColors[key] = value;
3460
+ } else {
3461
+ fgColors[key] = value;
3462
+ }
3463
+ }
3464
+ return new Theme(fgColors, bgColors, colorMode, {
3465
+ name: themeJson.name,
3466
+ sourcePath,
3467
+ workingIndicator
3468
+ });
3469
+ }
3470
+ function loadThemeFromPath(themePath, mode) {
3471
+ const content = fs.readFileSync(themePath, "utf-8");
3472
+ return loadThemeFromContent(themePath, content, mode);
3473
+ }
3474
+ function loadThemeFromContent(themePath, content, mode) {
3475
+ const themeJson = parseThemeJsonContent(themePath, content);
3476
+ return createTheme(themeJson, mode, themePath);
3477
+ }
3478
+ function loadTheme(name, mode) {
3479
+ const registeredTheme = registeredThemes.get(name);
3480
+ if (registeredTheme) {
3481
+ return registeredTheme;
3482
+ }
3483
+ const themeJson = loadThemeJson(name);
3484
+ return createTheme(themeJson, mode);
3485
+ }
3486
+ function getThemeByName(name) {
3487
+ try {
3488
+ return loadTheme(name);
3489
+ } catch {
3490
+ return;
3491
+ }
3492
+ }
3493
+ function setRegisteredThemes(themes) {
3494
+ registeredThemes.clear();
3495
+ for (const theme of themes) {
3496
+ if (theme.name) {
3497
+ assertThemeNameIsValid(theme.name);
3498
+ registeredThemes.set(theme.name, theme);
3499
+ }
3500
+ }
3501
+ }
3502
+ function setRegisteredTheme(name, theme) {
3503
+ registeredThemes.set(name, theme);
3504
+ }
3505
+
3506
+ // src/modes/interactive/theme/global-theme.ts
3507
+ var THEME_KEY = Symbol.for("@bastani/atomic:theme");
3508
+ var THEME_KEY_OLD = Symbol.for("@bastani/atomic:theme");
3509
+ var theme = new Proxy({}, {
3510
+ get(_target, prop) {
3511
+ const t = globalThis[THEME_KEY];
3512
+ if (!t)
3513
+ throw new Error("Theme not initialized. Call initTheme() first.");
3514
+ return t[prop];
3515
+ }
3516
+ });
3517
+ function setGlobalTheme(t) {
3518
+ globalThis[THEME_KEY] = t;
3519
+ globalThis[THEME_KEY_OLD] = t;
3520
+ }
3521
+ var currentThemeName;
3522
+ var themeWatcher;
3523
+ var themeReloadTimer;
3524
+ var themeWatchFilePath;
3525
+ var themeWatchFileListener;
3526
+ var onThemeChangeCallback;
3527
+ function getCurrentThemeName() {
3528
+ return currentThemeName;
3529
+ }
3530
+ function initTheme(themeName, enableWatcher = false) {
3531
+ const name = themeName ?? getDefaultTheme();
3532
+ currentThemeName = name;
3533
+ try {
3534
+ setGlobalTheme(loadTheme(name));
3535
+ if (enableWatcher) {
3536
+ startThemeWatcher();
3537
+ }
3538
+ } catch (_error) {
3539
+ currentThemeName = "dark";
3540
+ setGlobalTheme(loadTheme("dark"));
3541
+ }
3542
+ }
3543
+ function setTheme(name, enableWatcher = false) {
3544
+ currentThemeName = name;
3545
+ try {
3546
+ setGlobalTheme(loadTheme(name));
3547
+ if (enableWatcher) {
3548
+ startThemeWatcher();
3549
+ }
3550
+ if (onThemeChangeCallback) {
3551
+ onThemeChangeCallback();
3552
+ }
3553
+ return { success: true };
3554
+ } catch (error) {
3555
+ currentThemeName = "dark";
3556
+ setGlobalTheme(loadTheme("dark"));
3557
+ return {
3558
+ success: false,
3559
+ error: error instanceof Error ? error.message : String(error)
3560
+ };
3561
+ }
3562
+ }
3563
+ function setThemeInstance(themeInstance) {
3564
+ setGlobalTheme(themeInstance);
3565
+ currentThemeName = "<in-memory>";
3566
+ stopThemeWatcher();
3567
+ if (onThemeChangeCallback) {
3568
+ onThemeChangeCallback();
3569
+ }
3570
+ }
3571
+ function onThemeChange(callback) {
3572
+ onThemeChangeCallback = callback;
3573
+ }
3574
+ function startThemeWatcher() {
3575
+ stopThemeWatcher();
3576
+ if (!currentThemeName || currentThemeName in getBuiltinThemes()) {
3577
+ return;
3578
+ }
3579
+ const customThemesDir = getCustomThemesDir();
3580
+ const watchedThemeName = currentThemeName;
3581
+ const watchedFileName = `${watchedThemeName}.json`;
3582
+ const themeFile = path2.join(customThemesDir, watchedFileName);
3583
+ if (!fs2.existsSync(themeFile)) {
3584
+ return;
3585
+ }
3586
+ const scheduleReload = () => {
3587
+ if (themeReloadTimer) {
3588
+ clearTimeout(themeReloadTimer);
3589
+ }
3590
+ themeReloadTimer = setTimeout(() => {
3591
+ themeReloadTimer = undefined;
3592
+ if (currentThemeName !== watchedThemeName) {
3593
+ return;
3594
+ }
3595
+ if (!fs2.existsSync(themeFile)) {
3596
+ return;
3597
+ }
3598
+ try {
3599
+ const reloadedTheme = loadThemeFromPath(themeFile);
3600
+ setRegisteredTheme(watchedThemeName, reloadedTheme);
3601
+ setGlobalTheme(reloadedTheme);
3602
+ if (onThemeChangeCallback) {
3603
+ onThemeChangeCallback();
3604
+ }
3605
+ } catch (_error) {}
3606
+ }, 100);
3607
+ };
3608
+ const startPollingFallback = () => {
3609
+ if (themeWatchFilePath && themeWatchFileListener) {
3610
+ return;
3611
+ }
3612
+ themeWatchFilePath = themeFile;
3613
+ themeWatchFileListener = (current, previous) => {
3614
+ if (current.mtimeMs !== previous.mtimeMs || current.ctimeMs !== previous.ctimeMs || current.size !== previous.size) {
3615
+ scheduleReload();
3616
+ }
3617
+ };
3618
+ fs2.watchFile(themeFile, { interval: 1000 }, themeWatchFileListener);
3619
+ };
3620
+ themeWatcher = watchWithErrorHandler(customThemesDir, (_eventType, filename) => {
3621
+ if (currentThemeName !== watchedThemeName) {
3622
+ return;
3623
+ }
3624
+ if (!filename) {
3625
+ scheduleReload();
3626
+ return;
3627
+ }
3628
+ if (filename !== watchedFileName) {
3629
+ return;
3630
+ }
3631
+ scheduleReload();
3632
+ }, (error) => {
3633
+ closeWatcher(themeWatcher);
3634
+ themeWatcher = undefined;
3635
+ if (isSafeFsWatchPathError(error)) {
3636
+ startPollingFallback();
3637
+ }
3638
+ }) ?? undefined;
3639
+ }
3640
+ function stopThemeWatcher() {
3641
+ if (themeReloadTimer) {
3642
+ clearTimeout(themeReloadTimer);
3643
+ themeReloadTimer = undefined;
3644
+ }
3645
+ closeWatcher(themeWatcher);
3646
+ themeWatcher = undefined;
3647
+ if (themeWatchFilePath && themeWatchFileListener) {
3648
+ fs2.unwatchFile(themeWatchFilePath, themeWatchFileListener);
3649
+ }
3650
+ themeWatchFilePath = undefined;
3651
+ themeWatchFileListener = undefined;
3652
+ }
3653
+
3654
+ // src/modes/interactive/theme/export-colors.ts
3655
+ function getResolvedThemeColors(themeName) {
3656
+ const name = themeName ?? getCurrentThemeName() ?? getDefaultTheme();
3657
+ const isLight = isLightTheme(name);
3658
+ const themeJson = loadThemeJson(name);
3659
+ const resolved = resolveThemeColors(themeJson.colors, themeJson.vars);
3660
+ const defaultText = isLight ? "#000000" : "#e5e5e7";
3661
+ const cssColors = {};
3662
+ for (const [key, value] of Object.entries(resolved)) {
3663
+ if (typeof value === "number") {
3664
+ cssColors[key] = ansi256ToHex(value);
3665
+ } else if (value === "") {
3666
+ cssColors[key] = defaultText;
3667
+ } else {
3668
+ cssColors[key] = value;
3669
+ }
3670
+ }
3671
+ return cssColors;
3672
+ }
3673
+ function isLightTheme(themeName) {
3674
+ return themeName === "light" || themeName === "catppuccin-latte";
3675
+ }
3676
+ function getThemeExportColors(themeName) {
3677
+ const name = themeName ?? getCurrentThemeName() ?? getDefaultTheme();
3678
+ try {
3679
+ const themeJson = loadThemeJson(name);
3680
+ const exportSection = themeJson.export;
3681
+ if (!exportSection)
3682
+ return {};
3683
+ const vars = themeJson.vars ?? {};
3684
+ const resolve4 = (value) => {
3685
+ if (value === undefined)
3686
+ return;
3687
+ const resolved = resolveVarRefs(value, vars);
3688
+ if (typeof resolved === "number")
3689
+ return ansi256ToHex(resolved);
3690
+ if (resolved === "")
3691
+ return;
3692
+ return resolved;
3693
+ };
3694
+ return {
3695
+ pageBg: resolve4(exportSection.pageBg),
3696
+ cardBg: resolve4(exportSection.cardBg),
3697
+ infoBg: resolve4(exportSection.infoBg)
3698
+ };
3699
+ } catch {
3700
+ return {};
3701
+ }
3702
+ }
3703
+
3704
+ // src/modes/interactive/theme/tui-theme.ts
3705
+ import chalk2 from "chalk";
3706
+
3707
+ // src/utils/syntax-highlight.ts
3708
+ import hljs from "highlight.js/lib/core.js";
3709
+ import bash from "highlight.js/lib/languages/bash.js";
3710
+ import c from "highlight.js/lib/languages/c.js";
3711
+ import cpp from "highlight.js/lib/languages/cpp.js";
3712
+ import csharp from "highlight.js/lib/languages/csharp.js";
3713
+ import dart from "highlight.js/lib/languages/dart.js";
3714
+ import go from "highlight.js/lib/languages/go.js";
3715
+ import groovy from "highlight.js/lib/languages/groovy.js";
3716
+ import java from "highlight.js/lib/languages/java.js";
3717
+ import javascript from "highlight.js/lib/languages/javascript.js";
3718
+ import kotlin from "highlight.js/lib/languages/kotlin.js";
3719
+ import lua from "highlight.js/lib/languages/lua.js";
3720
+ import nix from "highlight.js/lib/languages/nix.js";
3721
+ import perl from "highlight.js/lib/languages/perl.js";
3722
+ import php from "highlight.js/lib/languages/php.js";
3723
+ import python from "highlight.js/lib/languages/python.js";
3724
+ import ruby from "highlight.js/lib/languages/ruby.js";
3725
+ import rust from "highlight.js/lib/languages/rust.js";
3726
+ import scala from "highlight.js/lib/languages/scala.js";
3727
+ import swift from "highlight.js/lib/languages/swift.js";
3728
+ import typescript from "highlight.js/lib/languages/typescript.js";
3729
+
3730
+ // src/utils/html.ts
3731
+ function decodeCodePoint(codePoint) {
3732
+ if (!Number.isInteger(codePoint) || codePoint < 0 || codePoint > 1114111) {
3733
+ return;
3734
+ }
3735
+ return String.fromCodePoint(codePoint);
3736
+ }
3737
+ function decodeHtmlEntity(entity) {
3738
+ switch (entity) {
3739
+ case "amp":
3740
+ return "&";
3741
+ case "lt":
3742
+ return "<";
3743
+ case "gt":
3744
+ return ">";
3745
+ case "quot":
3746
+ return '"';
3747
+ case "apos":
3748
+ return "'";
3749
+ }
3750
+ if (entity.startsWith("#x") || entity.startsWith("#X")) {
3751
+ return decodeCodePoint(Number.parseInt(entity.slice(2), 16));
3752
+ }
3753
+ if (entity.startsWith("#")) {
3754
+ return decodeCodePoint(Number.parseInt(entity.slice(1), 10));
3755
+ }
3756
+ return;
3757
+ }
3758
+ function decodeHtmlEntityAt(html, index) {
3759
+ const semicolonIndex = html.indexOf(";", index + 1);
3760
+ if (semicolonIndex === -1 || semicolonIndex - index > 16) {
3761
+ return;
3762
+ }
3763
+ const entity = html.slice(index + 1, semicolonIndex);
3764
+ const decoded = decodeHtmlEntity(entity);
3765
+ if (decoded === undefined) {
3766
+ return;
3767
+ }
3768
+ return { text: decoded, length: semicolonIndex - index + 1 };
3769
+ }
3770
+
3771
+ // src/utils/syntax-highlight.ts
3772
+ var eagerLanguages = {
3773
+ python,
3774
+ java,
3775
+ go,
3776
+ javascript,
3777
+ cpp,
3778
+ typescript,
3779
+ php,
3780
+ ruby,
3781
+ c,
3782
+ csharp,
3783
+ nix,
3784
+ bash,
3785
+ rust,
3786
+ scala,
3787
+ kotlin,
3788
+ swift,
3789
+ dart,
3790
+ groovy,
3791
+ perl,
3792
+ lua
3793
+ };
3794
+ for (const [name, language] of Object.entries(eagerLanguages)) {
3795
+ hljs.registerLanguage(name, language);
3796
+ }
3797
+ var allLanguagesPromise;
3798
+ function loadAllHighlightLanguages() {
3799
+ if (!allLanguagesPromise) {
3800
+ allLanguagesPromise = new Promise((resolve4) => {
3801
+ setImmediate(() => {
3802
+ import("highlight.js/lib/index.js").then(() => resolve4(), () => {
3803
+ resolve4();
3804
+ });
3805
+ });
3806
+ });
3807
+ }
3808
+ return allLanguagesPromise;
3809
+ }
3810
+ var SPAN_CLOSE = "</span>";
3811
+ var HIGHLIGHT_CLASS_PREFIX = "hljs-";
3812
+ function getScopeFromSpanTag(tag) {
3813
+ const match = /\sclass\s*=\s*(?:"([^"]*)"|'([^']*)')/.exec(tag);
3814
+ const classValue = match?.[1] ?? match?.[2];
3815
+ if (!classValue) {
3816
+ return;
3817
+ }
3818
+ for (const className of classValue.split(/\s+/)) {
3819
+ if (className.startsWith(HIGHLIGHT_CLASS_PREFIX)) {
3820
+ return className.slice(HIGHLIGHT_CLASS_PREFIX.length);
3821
+ }
3822
+ }
3823
+ return;
3824
+ }
3825
+ function getScopeFormatter(scope, theme2) {
3826
+ const exact = theme2[scope];
3827
+ if (exact) {
3828
+ return exact;
3829
+ }
3830
+ const dotIndex = scope.indexOf(".");
3831
+ if (dotIndex !== -1) {
3832
+ const prefixFormatter = theme2[scope.slice(0, dotIndex)];
3833
+ if (prefixFormatter) {
3834
+ return prefixFormatter;
3835
+ }
3836
+ }
3837
+ const dashIndex = scope.indexOf("-");
3838
+ if (dashIndex !== -1) {
3839
+ const prefixFormatter = theme2[scope.slice(0, dashIndex)];
3840
+ if (prefixFormatter) {
3841
+ return prefixFormatter;
3842
+ }
3843
+ }
3844
+ return;
3845
+ }
3846
+ function getActiveFormatter(scopes, theme2) {
3847
+ for (let i = scopes.length - 1;i >= 0; i--) {
3848
+ const scope = scopes[i];
3849
+ if (!scope) {
3850
+ continue;
3851
+ }
3852
+ const formatter = getScopeFormatter(scope, theme2);
3853
+ if (formatter) {
3854
+ return formatter;
3855
+ }
3856
+ }
3857
+ return theme2.default;
3858
+ }
3859
+ function isSpanOpenTagStart(html, index) {
3860
+ if (!html.startsWith("<span", index)) {
3861
+ return false;
3862
+ }
3863
+ const nextChar = html[index + "<span".length];
3864
+ return nextChar === ">" || nextChar === " " || nextChar === "\t" || nextChar === `
3865
+ ` || nextChar === "\r";
3866
+ }
3867
+ function renderHighlightedHtml(html, theme2 = {}) {
3868
+ let output = "";
3869
+ let textBuffer = "";
3870
+ const scopes = [];
3871
+ const flushText = () => {
3872
+ if (!textBuffer) {
3873
+ return;
3874
+ }
3875
+ const formatter = getActiveFormatter(scopes, theme2);
3876
+ output += formatter ? formatter(textBuffer) : textBuffer;
3877
+ textBuffer = "";
3878
+ };
3879
+ let index = 0;
3880
+ while (index < html.length) {
3881
+ if (isSpanOpenTagStart(html, index)) {
3882
+ const tagEndIndex = html.indexOf(">", index + 5);
3883
+ if (tagEndIndex !== -1) {
3884
+ flushText();
3885
+ const tag = html.slice(index, tagEndIndex + 1);
3886
+ const scope = getScopeFromSpanTag(tag);
3887
+ scopes.push(scope);
3888
+ index = tagEndIndex + 1;
3889
+ continue;
3890
+ }
3891
+ }
3892
+ if (html.startsWith(SPAN_CLOSE, index)) {
3893
+ flushText();
3894
+ if (scopes.length > 0) {
3895
+ scopes.pop();
3896
+ }
3897
+ index += SPAN_CLOSE.length;
3898
+ continue;
3899
+ }
3900
+ if (html[index] === "&") {
3901
+ const decoded = decodeHtmlEntityAt(html, index);
3902
+ if (decoded) {
3903
+ textBuffer += decoded.text;
3904
+ index += decoded.length;
3905
+ continue;
3906
+ }
3907
+ }
3908
+ textBuffer += html[index];
3909
+ index++;
3910
+ }
3911
+ flushText();
3912
+ return output;
3913
+ }
3914
+ function highlight(code, options = {}) {
3915
+ const html = options.language ? hljs.highlight(code, {
3916
+ language: options.language,
3917
+ ignoreIllegals: options.ignoreIllegals
3918
+ }).value : hljs.highlightAuto(code, options.languageSubset).value;
3919
+ return renderHighlightedHtml(html, options.theme);
3920
+ }
3921
+ function supportsLanguage(name) {
3922
+ return hljs.getLanguage(name) !== undefined;
3923
+ }
3924
+
3925
+ // src/modes/interactive/theme/tui-theme.ts
3926
+ var cachedHighlightThemeFor;
3927
+ var cachedCliHighlightTheme;
3928
+ function buildCliHighlightTheme(t) {
3929
+ return {
3930
+ keyword: (s) => t.fg("syntaxKeyword", s),
3931
+ built_in: (s) => t.fg("syntaxType", s),
3932
+ literal: (s) => t.fg("syntaxNumber", s),
3933
+ number: (s) => t.fg("syntaxNumber", s),
3934
+ regexp: (s) => t.fg("syntaxString", s),
3935
+ string: (s) => t.fg("syntaxString", s),
3936
+ comment: (s) => t.fg("syntaxComment", s),
3937
+ doctag: (s) => t.fg("syntaxComment", s),
3938
+ meta: (s) => t.fg("muted", s),
3939
+ function: (s) => t.fg("syntaxFunction", s),
3940
+ title: (s) => t.fg("syntaxFunction", s),
3941
+ class: (s) => t.fg("syntaxType", s),
3942
+ type: (s) => t.fg("syntaxType", s),
3943
+ tag: (s) => t.fg("syntaxPunctuation", s),
3944
+ name: (s) => t.fg("syntaxKeyword", s),
3945
+ attr: (s) => t.fg("syntaxVariable", s),
3946
+ variable: (s) => t.fg("syntaxVariable", s),
3947
+ params: (s) => t.fg("syntaxVariable", s),
3948
+ operator: (s) => t.fg("syntaxOperator", s),
3949
+ punctuation: (s) => t.fg("syntaxPunctuation", s),
3950
+ emphasis: (s) => t.italic(s),
3951
+ strong: (s) => t.bold(s),
3952
+ link: (s) => t.underline(s),
3953
+ addition: (s) => t.fg("toolDiffAdded", s),
3954
+ deletion: (s) => t.fg("toolDiffRemoved", s)
3955
+ };
3956
+ }
3957
+ function getCliHighlightTheme(t) {
3958
+ if (cachedHighlightThemeFor !== t || !cachedCliHighlightTheme) {
3959
+ cachedHighlightThemeFor = t;
3960
+ cachedCliHighlightTheme = buildCliHighlightTheme(t);
3961
+ }
3962
+ return cachedCliHighlightTheme;
3963
+ }
3964
+ function highlightCode(code, lang) {
3965
+ const validLang = lang && supportsLanguage(lang) ? lang : undefined;
3966
+ if (!validLang) {
3967
+ return code.split(`
3968
+ `).map((line) => theme.fg("mdCodeBlock", line));
3969
+ }
3970
+ const opts = {
3971
+ language: validLang,
3972
+ ignoreIllegals: true,
3973
+ theme: getCliHighlightTheme(theme)
3974
+ };
3975
+ try {
3976
+ return highlight(code, opts).split(`
3977
+ `);
3978
+ } catch {
3979
+ return code.split(`
3980
+ `);
3981
+ }
3982
+ }
3983
+ function getLanguageFromPath(filePath) {
3984
+ const ext = filePath.split(".").pop()?.toLowerCase();
3985
+ if (!ext)
3986
+ return;
3987
+ const extToLang = {
3988
+ ts: "typescript",
3989
+ tsx: "typescript",
3990
+ js: "javascript",
3991
+ jsx: "javascript",
3992
+ mjs: "javascript",
3993
+ cjs: "javascript",
3994
+ py: "python",
3995
+ rb: "ruby",
3996
+ rs: "rust",
3997
+ go: "go",
3998
+ java: "java",
3999
+ kt: "kotlin",
4000
+ swift: "swift",
4001
+ c: "c",
4002
+ h: "c",
4003
+ cpp: "cpp",
4004
+ cc: "cpp",
4005
+ cxx: "cpp",
4006
+ hpp: "cpp",
4007
+ cs: "csharp",
4008
+ php: "php",
4009
+ sh: "bash",
4010
+ bash: "bash",
4011
+ zsh: "bash",
4012
+ fish: "fish",
4013
+ ps1: "powershell",
4014
+ sql: "sql",
4015
+ html: "html",
4016
+ htm: "html",
4017
+ css: "css",
4018
+ scss: "scss",
4019
+ sass: "sass",
4020
+ less: "less",
4021
+ json: "json",
4022
+ yaml: "yaml",
4023
+ yml: "yaml",
4024
+ toml: "toml",
4025
+ xml: "xml",
4026
+ md: "markdown",
4027
+ markdown: "markdown",
4028
+ dockerfile: "dockerfile",
4029
+ makefile: "makefile",
4030
+ cmake: "cmake",
4031
+ lua: "lua",
4032
+ perl: "perl",
4033
+ r: "r",
4034
+ scala: "scala",
4035
+ clj: "clojure",
4036
+ ex: "elixir",
4037
+ exs: "elixir",
4038
+ erl: "erlang",
4039
+ hs: "haskell",
4040
+ ml: "ocaml",
4041
+ vim: "vim",
4042
+ graphql: "graphql",
4043
+ proto: "protobuf",
4044
+ tf: "hcl",
4045
+ hcl: "hcl"
4046
+ };
4047
+ return extToLang[ext];
4048
+ }
4049
+ function getMarkdownTheme() {
4050
+ return {
4051
+ heading: (text) => theme.fg("mdHeading", text),
4052
+ link: (text) => theme.fg("mdLink", text),
4053
+ linkUrl: (text) => theme.fg("mdLinkUrl", text),
4054
+ code: (text) => theme.fg("mdCode", text),
4055
+ codeBlock: (text) => theme.fg("mdCodeBlock", text),
4056
+ codeBlockBorder: (text) => theme.fg("mdCodeBlockBorder", text),
4057
+ quote: (text) => theme.fg("mdQuote", text),
4058
+ quoteBorder: (text) => theme.fg("mdQuoteBorder", text),
4059
+ hr: (text) => theme.fg("mdHr", text),
4060
+ listBullet: (text) => theme.fg("mdListBullet", text),
4061
+ bold: (text) => theme.bold(text),
4062
+ italic: (text) => theme.italic(text),
4063
+ underline: (text) => theme.underline(text),
4064
+ strikethrough: (text) => chalk2.strikethrough(text),
4065
+ highlightCode: (code, lang) => {
4066
+ const validLang = lang && supportsLanguage(lang) ? lang : undefined;
4067
+ if (!validLang) {
4068
+ return code.split(`
4069
+ `).map((line) => theme.fg("mdCodeBlock", line));
4070
+ }
4071
+ const opts = {
4072
+ language: validLang,
4073
+ ignoreIllegals: true,
4074
+ theme: getCliHighlightTheme(theme)
4075
+ };
4076
+ try {
4077
+ return highlight(code, opts).split(`
4078
+ `);
4079
+ } catch {
4080
+ return code.split(`
4081
+ `).map((line) => theme.fg("mdCodeBlock", line));
4082
+ }
4083
+ }
4084
+ };
4085
+ }
4086
+ function getSelectListTheme() {
4087
+ return {
4088
+ selectedPrefix: (text) => theme.fg("accent", text),
4089
+ selectedText: (text) => theme.fg("accent", text),
4090
+ description: (text) => theme.fg("muted", text),
4091
+ scrollInfo: (text) => theme.fg("muted", text),
4092
+ noMatch: (text) => theme.fg("muted", text)
4093
+ };
4094
+ }
4095
+ function getEditorTheme() {
4096
+ return {
4097
+ borderColor: (text) => theme.fg("borderMuted", text),
4098
+ selectList: getSelectListTheme()
4099
+ };
4100
+ }
4101
+ function getSettingsListTheme() {
4102
+ return {
4103
+ label: (text, selected) => selected ? theme.fg("accent", text) : text,
4104
+ value: (text, selected) => selected ? theme.fg("accent", text) : theme.fg("muted", text),
4105
+ description: (text) => theme.fg("dim", text),
4106
+ cursor: theme.fg("accent", "→ "),
4107
+ hint: (text) => theme.fg("dim", text)
4108
+ };
4109
+ }
4110
+ export { requiredEntriesForBuiltin, stripBom, createChildProcessEnvironment, spawnProcess, spawnProcessSync, waitForChildProcess, getHomeDir, canonicalizePath, isLocalPath, normalizePath, resolvePath, getCwdRelativePath, formatPathRelativeToCwdOrAbsolute, markPathIgnoredByCloudSync, moduleDirFromMetaUrl, isBunBinary, isBundledBuild, detectInstallMethod, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, getPackageDir, getThemesDir, getExportTemplateDir, getPackageJsonPath, getReadmePath, getDocsPath, getExamplesPath, getChangelogPath, getInteractiveAssetsDir, getBundledInteractiveAssetPath, PACKAGE_NAME, APP_NAME, APP_TITLE, CONFIG_DIR_NAME, LEGACY_CONFIG_DIR_NAME, CONFIG_DIR_NAMES, VERSION, CHANGELOG_URL, LEGACY_ENV_PREFIX, ENV_AGENT_DIR, ENV_SESSION_DIR, ENV_PACKAGE_DIR, ENV_OFFLINE, ENV_SKIP_VERSION_CHECK, ENV_STARTUP_BENCHMARK, ENV_TELEMETRY, ENV_SHARE_VIEWER_URL, ENV_CLEAR_ON_SHRINK, ENV_HARDWARE_CURSOR, ENV_TIMING, ENV_CODEX_FAST_MODE, WORKFLOW_STAGE_SUBAGENT_GUARD_ENV, getCodexFastModeEnvironmentSettings, setCodexFastModeEnvironmentSettings, getEnvNames, getEnvValue, hasEnvValue, setEnvValue, expandTildePath, getShareViewerUrl, getAgentDir, getLegacyAgentDir, getAgentDirs, getUserConfigDirs, getProjectConfigDirs, getUserConfigPaths, getAgentConfigPaths, getProjectConfigPaths, getAuthPath, getSettingsPath, getBinDir, getExtensionTranspileCacheDir, getDebugLogPath, normalizeMessageContent, userLikeContentBlockIsLlmVisible, messageStartsLlmUserTurn, messageIsLlmVisible, VERBATIM_COMPACTION_PREFIX, isVerbatimCompactionMessage, createVerbatimCompactionMessage, createBranchSummaryMessage, createCustomMessage, repairOrphanToolResults, convertToLlm, filteredMarker, serializeConversationForCompaction, createNumberedRegion, numberRegionLines, normalizeDerivedSessionEntries, getLatestCompactionBoundaryEntry, sessionEntryToContextMessages, buildContextEntries, buildSessionContext, WORKFLOW_SESSION_METADATA_ENV, workflowSessionMetadataFromEnv, CURRENT_SESSION_VERSION, assertValidSessionId, getLastConversationMessageId, getLatestSessionSummary, getDefaultSessionDir, yieldToEventLoop, yieldToEventLoopIfSlow, migrateSessionEntries, parseSessionEntries, SessionManager, hexToRgb, fgAnsi, ansi256ToHex, FS_WATCH_RETRY_DELAY_MS, SAFE_FS_WATCH_CANONICALIZATION_FAILED, SAFE_FS_WATCH_UNSAFE_WINDOWS_SHORT_PATH, closeWatcher, isUnsafeWindowsShortPath, isSafeFsWatchPathError, resolveNativeWatchPath, watchWithErrorHandler, parseAutoThemeSetting, resolveThemeSetting, detectTerminalBackgroundFromEnv, detectTerminalBackgroundTheme, detectTerminalThemeForAuto, Theme, getAvailableThemes, getAvailableThemesWithPaths, loadThemeFromContent, getThemeByName, setRegisteredThemes, theme, initTheme, setTheme, setThemeInstance, onThemeChange, stopThemeWatcher, getResolvedThemeColors, getThemeExportColors, loadAllHighlightLanguages, highlightCode, getLanguageFromPath, getMarkdownTheme, getSelectListTheme, getEditorTheme, getSettingsListTheme };