aimux-cli 0.1.18 → 0.1.20

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 (304) hide show
  1. package/README.md +13 -4
  2. package/bin/aimux +4 -0
  3. package/bin/aimux-dev +2 -6
  4. package/dist/agent-events.js +0 -1
  5. package/dist/agent-output-parser-audit.d.ts +23 -0
  6. package/dist/agent-output-parser-audit.js +187 -0
  7. package/dist/agent-output-parser-contract.d.ts +9 -0
  8. package/dist/agent-output-parser-contract.js +33 -0
  9. package/dist/agent-output-parser-fixtures.d.ts +15 -0
  10. package/dist/agent-output-parser-fixtures.js +593 -0
  11. package/dist/agent-output-parser-harness.d.ts +21 -0
  12. package/dist/agent-output-parser-harness.js +43 -0
  13. package/dist/agent-output-parser-test-utils.d.ts +1 -0
  14. package/dist/agent-output-parser-test-utils.js +7 -0
  15. package/dist/agent-output-parser.js +215 -36
  16. package/dist/agent-prompt-delivery.js +0 -1
  17. package/dist/agent-tracker.js +0 -1
  18. package/dist/agent-watcher.js +0 -1
  19. package/dist/alert-display.js +0 -1
  20. package/dist/atomic-write.d.ts +15 -0
  21. package/dist/atomic-write.js +69 -5
  22. package/dist/attachment-store.d.ts +7 -0
  23. package/dist/attachment-store.js +64 -6
  24. package/dist/backend-session-discovery.d.ts +17 -0
  25. package/dist/backend-session-discovery.js +57 -0
  26. package/dist/builtin-metadata-watchers.js +0 -1
  27. package/dist/claude-hooks.js +0 -1
  28. package/dist/config.js +9 -5
  29. package/dist/connection-targets.js +20 -2
  30. package/dist/context/compactor.js +0 -1
  31. package/dist/context/context-bridge.js +4 -2
  32. package/dist/context/context-file.js +0 -1
  33. package/dist/context/history.js +0 -1
  34. package/dist/credentials.js +3 -7
  35. package/dist/daemon.d.ts +1 -0
  36. package/dist/daemon.js +16 -1
  37. package/dist/dashboard/command-spec.js +0 -1
  38. package/dist/dashboard/feedback.js +0 -1
  39. package/dist/dashboard/index.d.ts +1 -0
  40. package/dist/dashboard/index.js +1 -1
  41. package/dist/dashboard/operation-failures.js +0 -1
  42. package/dist/dashboard/order.js +0 -1
  43. package/dist/dashboard/pending-actions.js +0 -1
  44. package/dist/dashboard/quick-jump.js +0 -1
  45. package/dist/dashboard/runtime-evidence.js +0 -1
  46. package/dist/dashboard/session-actions.js +0 -1
  47. package/dist/dashboard/session-registry.js +0 -1
  48. package/dist/dashboard/sort.js +0 -1
  49. package/dist/dashboard/state.js +0 -1
  50. package/dist/dashboard/targets.js +14 -3
  51. package/dist/dashboard/ui-state-store.js +4 -4
  52. package/dist/debug-state.js +0 -1
  53. package/dist/debug.js +0 -1
  54. package/dist/default-plugins/gh-pr-context.js +0 -1
  55. package/dist/default-plugins/transcript-length.js +0 -1
  56. package/dist/fast-control.js +0 -1
  57. package/dist/hotkeys.js +0 -1
  58. package/dist/http-client.js +0 -1
  59. package/dist/key-parser.js +0 -1
  60. package/dist/last-used.js +3 -3
  61. package/dist/launcher-env.d.ts +4 -0
  62. package/dist/launcher-env.js +70 -0
  63. package/dist/local-ui-server.js +0 -1
  64. package/dist/login-flow.js +0 -1
  65. package/dist/main.js +16 -2
  66. package/dist/managed-launch-env.js +0 -1
  67. package/dist/metadata-server.d.ts +13 -2
  68. package/dist/metadata-server.js +60 -5
  69. package/dist/metadata-store.js +4 -4
  70. package/dist/mobile-push-bridge.d.ts +8 -0
  71. package/dist/mobile-push-bridge.js +22 -0
  72. package/dist/mobile-push-throttle.d.ts +23 -0
  73. package/dist/mobile-push-throttle.js +53 -0
  74. package/dist/multiplexer/agent-io-methods.js +0 -1
  75. package/dist/multiplexer/archives.js +0 -1
  76. package/dist/multiplexer/dashboard-actions-methods.js +0 -1
  77. package/dist/multiplexer/dashboard-control.js +0 -1
  78. package/dist/multiplexer/dashboard-interaction.js +0 -1
  79. package/dist/multiplexer/dashboard-model.js +3 -3
  80. package/dist/multiplexer/dashboard-ops.d.ts +3 -2
  81. package/dist/multiplexer/dashboard-ops.js +2 -3
  82. package/dist/multiplexer/dashboard-state-methods.js +0 -1
  83. package/dist/multiplexer/dashboard-tail-methods.d.ts +3 -2
  84. package/dist/multiplexer/dashboard-tail-methods.js +2 -3
  85. package/dist/multiplexer/dashboard-view-methods.js +2 -1
  86. package/dist/multiplexer/graveyard-view-model.js +0 -1
  87. package/dist/multiplexer/index.d.ts +1 -1
  88. package/dist/multiplexer/index.js +4 -5
  89. package/dist/multiplexer/navigation.js +0 -1
  90. package/dist/multiplexer/notifications.js +0 -1
  91. package/dist/multiplexer/persistence-methods.js +2 -2
  92. package/dist/multiplexer/runtime-lifecycle-methods.js +6 -3
  93. package/dist/multiplexer/runtime-state.js +13 -2
  94. package/dist/multiplexer/runtime-sync.js +0 -1
  95. package/dist/multiplexer/service-state-snapshot.js +4 -3
  96. package/dist/multiplexer/services.js +5 -5
  97. package/dist/multiplexer/session-capture.js +0 -1
  98. package/dist/multiplexer/session-launch.d.ts +1 -1
  99. package/dist/multiplexer/session-launch.js +18 -7
  100. package/dist/multiplexer/session-runtime-core.js +9 -3
  101. package/dist/multiplexer/subscreens.js +0 -1
  102. package/dist/multiplexer/tool-picker.d.ts +2 -1
  103. package/dist/multiplexer/tool-picker.js +29 -22
  104. package/dist/multiplexer/worktree-graveyard.js +0 -1
  105. package/dist/multiplexer/worktrees.js +0 -1
  106. package/dist/notification-context.js +0 -1
  107. package/dist/notifications.js +0 -1
  108. package/dist/notify.d.ts +1 -1
  109. package/dist/notify.js +8 -6
  110. package/dist/orchestration-actions.js +0 -1
  111. package/dist/orchestration-routing.js +0 -1
  112. package/dist/orchestration.js +0 -1
  113. package/dist/osc-notifications.js +0 -1
  114. package/dist/paths.js +50 -5
  115. package/dist/pending-actions.js +0 -1
  116. package/dist/plugin-runtime.js +0 -1
  117. package/dist/project-events.js +0 -1
  118. package/dist/project-scanner.js +0 -1
  119. package/dist/project-service-manifest.js +0 -1
  120. package/dist/project-takeover.d.ts +1 -0
  121. package/dist/project-takeover.js +117 -0
  122. package/dist/recency.js +0 -1
  123. package/dist/recorder.js +0 -1
  124. package/dist/relay-client.d.ts +10 -0
  125. package/dist/relay-client.js +5 -1
  126. package/dist/remote-access.js +0 -1
  127. package/dist/runtime-core/backend-id-reconcile.d.ts +13 -0
  128. package/dist/runtime-core/backend-id-reconcile.js +23 -0
  129. package/dist/runtime-core/exchange-derived.js +0 -1
  130. package/dist/runtime-core/exchange-import.js +0 -1
  131. package/dist/runtime-core/exchange-store.js +3 -9
  132. package/dist/runtime-core/topology-services.js +0 -1
  133. package/dist/runtime-core/topology-sessions.js +0 -1
  134. package/dist/runtime-core/topology-store.js +3 -9
  135. package/dist/runtime-core/topology-worktrees.js +0 -1
  136. package/dist/runtime-migration.js +0 -1
  137. package/dist/runtime-owner.d.ts +3 -0
  138. package/dist/runtime-owner.js +10 -0
  139. package/dist/session-bootstrap.js +0 -1
  140. package/dist/session-runtime.js +0 -1
  141. package/dist/session-semantics.js +0 -1
  142. package/dist/shell-args.d.ts +13 -0
  143. package/dist/shell-args.js +25 -1
  144. package/dist/shell-hooks.d.ts +1 -0
  145. package/dist/shell-hooks.js +1 -1
  146. package/dist/shell-state.js +0 -1
  147. package/dist/status-detector.js +0 -1
  148. package/dist/statusline-model.js +0 -1
  149. package/dist/task-workflow.js +0 -1
  150. package/dist/tasks.js +0 -1
  151. package/dist/team.js +4 -4
  152. package/dist/terminal-host.js +0 -1
  153. package/dist/threads.js +0 -1
  154. package/dist/tmux/doctor.js +0 -1
  155. package/dist/tmux/inbox-popup.js +0 -1
  156. package/dist/tmux/runtime-manager.js +2 -1
  157. package/dist/tmux/session-transport.js +0 -1
  158. package/dist/tmux/statusline-cache.js +0 -1
  159. package/dist/tmux/statusline.js +0 -1
  160. package/dist/tmux/switcher.js +0 -1
  161. package/dist/tmux/window-open.js +0 -1
  162. package/dist/tool-output-watchers.js +0 -1
  163. package/dist/tui/render/box.js +0 -1
  164. package/dist/tui/render/text.js +0 -1
  165. package/dist/tui/screens/dashboard-renderers.js +6 -7
  166. package/dist/tui/screens/overlay-renderers.js +0 -1
  167. package/dist/tui/screens/subscreen-renderers.js +0 -1
  168. package/dist/vitest.setup.d.ts +1 -0
  169. package/dist/vitest.setup.js +9 -0
  170. package/dist/workflow.js +0 -1
  171. package/dist/worktree.js +0 -1
  172. package/dist-ui/_expo/static/css/web-8782287775683e5a944b821b854d0f60.css +1 -0
  173. package/dist-ui/_expo/static/js/web/{entry-477c745b2adc79367a4380ecf07d9ff6.js → entry-90d00d223eefabe5cc21e4329b274fa5.js} +260 -252
  174. package/dist-ui/index.html +2 -2
  175. package/package.json +5 -2
  176. package/dist/agent-events.js.map +0 -1
  177. package/dist/agent-output-parser.js.map +0 -1
  178. package/dist/agent-prompt-delivery.js.map +0 -1
  179. package/dist/agent-tracker.js.map +0 -1
  180. package/dist/agent-watcher.js.map +0 -1
  181. package/dist/alert-display.js.map +0 -1
  182. package/dist/atomic-write.js.map +0 -1
  183. package/dist/attachment-store.js.map +0 -1
  184. package/dist/builtin-metadata-watchers.js.map +0 -1
  185. package/dist/claude-hooks.js.map +0 -1
  186. package/dist/config.js.map +0 -1
  187. package/dist/connection-targets.js.map +0 -1
  188. package/dist/context/compactor.js.map +0 -1
  189. package/dist/context/context-bridge.js.map +0 -1
  190. package/dist/context/context-file.js.map +0 -1
  191. package/dist/context/history.js.map +0 -1
  192. package/dist/credentials.js.map +0 -1
  193. package/dist/daemon.js.map +0 -1
  194. package/dist/dashboard/command-spec.js.map +0 -1
  195. package/dist/dashboard/feedback.js.map +0 -1
  196. package/dist/dashboard/index.js.map +0 -1
  197. package/dist/dashboard/operation-failures.js.map +0 -1
  198. package/dist/dashboard/order.js.map +0 -1
  199. package/dist/dashboard/pending-actions.js.map +0 -1
  200. package/dist/dashboard/quick-jump.js.map +0 -1
  201. package/dist/dashboard/runtime-evidence.js.map +0 -1
  202. package/dist/dashboard/session-actions.js.map +0 -1
  203. package/dist/dashboard/session-registry.js.map +0 -1
  204. package/dist/dashboard/sort.js.map +0 -1
  205. package/dist/dashboard/state.js.map +0 -1
  206. package/dist/dashboard/targets.js.map +0 -1
  207. package/dist/dashboard/ui-state-store.js.map +0 -1
  208. package/dist/debug-state.js.map +0 -1
  209. package/dist/debug.js.map +0 -1
  210. package/dist/default-plugins/gh-pr-context.js.map +0 -1
  211. package/dist/default-plugins/transcript-length.js.map +0 -1
  212. package/dist/fast-control.js.map +0 -1
  213. package/dist/hotkeys.js.map +0 -1
  214. package/dist/http-client.js.map +0 -1
  215. package/dist/key-parser.js.map +0 -1
  216. package/dist/last-used.js.map +0 -1
  217. package/dist/local-ui-server.js.map +0 -1
  218. package/dist/login-flow.js.map +0 -1
  219. package/dist/main.js.map +0 -1
  220. package/dist/managed-launch-env.js.map +0 -1
  221. package/dist/metadata-server.js.map +0 -1
  222. package/dist/metadata-store.js.map +0 -1
  223. package/dist/multiplexer/agent-io-methods.js.map +0 -1
  224. package/dist/multiplexer/archives.js.map +0 -1
  225. package/dist/multiplexer/dashboard-actions-methods.js.map +0 -1
  226. package/dist/multiplexer/dashboard-control.js.map +0 -1
  227. package/dist/multiplexer/dashboard-interaction.js.map +0 -1
  228. package/dist/multiplexer/dashboard-model.js.map +0 -1
  229. package/dist/multiplexer/dashboard-ops.js.map +0 -1
  230. package/dist/multiplexer/dashboard-state-methods.js.map +0 -1
  231. package/dist/multiplexer/dashboard-tail-methods.js.map +0 -1
  232. package/dist/multiplexer/dashboard-view-methods.js.map +0 -1
  233. package/dist/multiplexer/graveyard-view-model.js.map +0 -1
  234. package/dist/multiplexer/index.js.map +0 -1
  235. package/dist/multiplexer/navigation.js.map +0 -1
  236. package/dist/multiplexer/notifications.js.map +0 -1
  237. package/dist/multiplexer/persistence-methods.js.map +0 -1
  238. package/dist/multiplexer/runtime-lifecycle-methods.js.map +0 -1
  239. package/dist/multiplexer/runtime-state.js.map +0 -1
  240. package/dist/multiplexer/runtime-sync.js.map +0 -1
  241. package/dist/multiplexer/service-state-snapshot.js.map +0 -1
  242. package/dist/multiplexer/services.js.map +0 -1
  243. package/dist/multiplexer/session-capture.js.map +0 -1
  244. package/dist/multiplexer/session-launch.js.map +0 -1
  245. package/dist/multiplexer/session-runtime-core.js.map +0 -1
  246. package/dist/multiplexer/subscreens.js.map +0 -1
  247. package/dist/multiplexer/tool-picker.js.map +0 -1
  248. package/dist/multiplexer/worktree-graveyard.js.map +0 -1
  249. package/dist/multiplexer/worktrees.js.map +0 -1
  250. package/dist/notification-context.js.map +0 -1
  251. package/dist/notifications.js.map +0 -1
  252. package/dist/notify.js.map +0 -1
  253. package/dist/orchestration-actions.js.map +0 -1
  254. package/dist/orchestration-routing.js.map +0 -1
  255. package/dist/orchestration.js.map +0 -1
  256. package/dist/osc-notifications.js.map +0 -1
  257. package/dist/paths.js.map +0 -1
  258. package/dist/pending-actions.js.map +0 -1
  259. package/dist/plugin-runtime.js.map +0 -1
  260. package/dist/project-events.js.map +0 -1
  261. package/dist/project-scanner.js.map +0 -1
  262. package/dist/project-service-manifest.js.map +0 -1
  263. package/dist/recency.js.map +0 -1
  264. package/dist/recorder.js.map +0 -1
  265. package/dist/relay-client.js.map +0 -1
  266. package/dist/remote-access.js.map +0 -1
  267. package/dist/runtime-core/exchange-derived.js.map +0 -1
  268. package/dist/runtime-core/exchange-import.js.map +0 -1
  269. package/dist/runtime-core/exchange-store.js.map +0 -1
  270. package/dist/runtime-core/topology-services.js.map +0 -1
  271. package/dist/runtime-core/topology-sessions.js.map +0 -1
  272. package/dist/runtime-core/topology-store.js.map +0 -1
  273. package/dist/runtime-core/topology-worktrees.js.map +0 -1
  274. package/dist/runtime-migration.js.map +0 -1
  275. package/dist/session-bootstrap.js.map +0 -1
  276. package/dist/session-runtime.js.map +0 -1
  277. package/dist/session-semantics.js.map +0 -1
  278. package/dist/shell-args.js.map +0 -1
  279. package/dist/shell-hooks.js.map +0 -1
  280. package/dist/shell-state.js.map +0 -1
  281. package/dist/status-detector.js.map +0 -1
  282. package/dist/statusline-model.js.map +0 -1
  283. package/dist/task-workflow.js.map +0 -1
  284. package/dist/tasks.js.map +0 -1
  285. package/dist/team.js.map +0 -1
  286. package/dist/terminal-host.js.map +0 -1
  287. package/dist/threads.js.map +0 -1
  288. package/dist/tmux/doctor.js.map +0 -1
  289. package/dist/tmux/inbox-popup.js.map +0 -1
  290. package/dist/tmux/runtime-manager.js.map +0 -1
  291. package/dist/tmux/session-transport.js.map +0 -1
  292. package/dist/tmux/statusline-cache.js.map +0 -1
  293. package/dist/tmux/statusline.js.map +0 -1
  294. package/dist/tmux/switcher.js.map +0 -1
  295. package/dist/tmux/window-open.js.map +0 -1
  296. package/dist/tool-output-watchers.js.map +0 -1
  297. package/dist/tui/render/box.js.map +0 -1
  298. package/dist/tui/render/text.js.map +0 -1
  299. package/dist/tui/screens/dashboard-renderers.js.map +0 -1
  300. package/dist/tui/screens/overlay-renderers.js.map +0 -1
  301. package/dist/tui/screens/subscreen-renderers.js.map +0 -1
  302. package/dist/workflow.js.map +0 -1
  303. package/dist/worktree.js.map +0 -1
  304. package/dist-ui/_expo/static/css/web-30453ede1678c16acb08b97e83e8646d.css +0 -1
@@ -1,6 +1,7 @@
1
1
  import { type SessionAlertDisplayContext } from "./alert-display.js";
2
2
  import { type MessageKind } from "./threads.js";
3
3
  import { type TaskLifecycleResult } from "./orchestration-actions.js";
4
+ import type { LaunchOverride } from "./shell-args.js";
4
5
  import type { ParsedAgentOutput } from "./agent-output-parser.js";
5
6
  import { ProjectEventBus } from "./project-events.js";
6
7
  interface MetadataServerOptions {
@@ -204,7 +205,7 @@ interface MetadataServerOptions {
204
205
  sessionId?: string;
205
206
  worktreePath?: string;
206
207
  open?: boolean;
207
- extraArgs?: string[];
208
+ launchOverride?: LaunchOverride;
208
209
  }) => Promise<{
209
210
  sessionId: string;
210
211
  }> | {
@@ -242,7 +243,7 @@ interface MetadataServerOptions {
242
243
  instruction?: string;
243
244
  worktreePath?: string;
244
245
  open?: boolean;
245
- extraArgs?: string[];
246
+ launchOverride?: LaunchOverride;
246
247
  }) => Promise<{
247
248
  sessionId: string;
248
249
  threadId: string;
@@ -297,6 +298,16 @@ interface MetadataServerOptions {
297
298
  status: "graveyard";
298
299
  previousStatus: "running" | "offline";
299
300
  };
301
+ recordBackendSessionId?: (input: {
302
+ sessionId: string;
303
+ backendSessionId: string;
304
+ }) => Promise<{
305
+ sessionId: string;
306
+ backendSessionId: string;
307
+ }> | {
308
+ sessionId: string;
309
+ backendSessionId: string;
310
+ };
300
311
  sendAgentInput?: (input: {
301
312
  sessionId: string;
302
313
  text: string;
@@ -3,6 +3,7 @@ import { createHash, randomUUID } from "node:crypto";
3
3
  import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { getDashboardClientUiStatePath, getPlansDir, getProjectId, getProjectStateDir } from "./paths.js";
6
+ import { writeJsonAtomic } from "./atomic-write.js";
6
7
  import { updateSessionMetadata, clearSessionLogs, saveMetadataEndpoint, loadMetadataState, } from "./metadata-store.js";
7
8
  import { contextualizeAlertInput, mergeDisplayContext, metadataDisplayContext, } from "./alert-display.js";
8
9
  import { notifyAlert } from "./notify.js";
@@ -16,7 +17,7 @@ import { readAllTasks, readTask } from "./tasks.js";
16
17
  import { buildWorkflowEntries } from "./workflow.js";
17
18
  import { markLastUsed } from "./last-used.js";
18
19
  import { formatRelativeRecency } from "./recency.js";
19
- import { getAttachment, getAttachmentContent } from "./attachment-store.js";
20
+ import { createUploadedAttachment, getAttachment, getAttachmentContent, getAttachmentRecord, } from "./attachment-store.js";
20
21
  import { ProjectEventBus } from "./project-events.js";
21
22
  import { getProjectServiceManifest } from "./project-service-manifest.js";
22
23
  import { applyShellStateTransition } from "./shell-state.js";
@@ -78,7 +79,7 @@ function persistDashboardClientPreference(clientSession, update) {
78
79
  }
79
80
  catch { }
80
81
  update(snapshot);
81
- writeFileSync(path, JSON.stringify(snapshot, null, 2) + "\n");
82
+ writeJsonAtomic(path, snapshot);
82
83
  }
83
84
  function persistDashboardReturnSelection(tmux, projectRoot, currentClientSession, currentWindowId) {
84
85
  persistDashboardClientPreference(currentClientSession, (snapshot) => {
@@ -177,6 +178,16 @@ function sendBytes(res, status, body, mimeType) {
177
178
  res.setHeader("connection", "close");
178
179
  res.end(body);
179
180
  }
181
+ function formatAgentInputWithAttachments(text, attachments) {
182
+ const trimmedText = text.trim();
183
+ if (attachments.length === 0)
184
+ return text;
185
+ const body = trimmedText || "Please review the attached image file(s).";
186
+ const attachmentLines = attachments.map((attachment) => {
187
+ return `- ${attachment.filename} (${attachment.mimeType}, ${attachment.sizeBytes} bytes): ${attachment.contentPath}`;
188
+ });
189
+ return `${body}\n\nAttached image files:\n${attachmentLines.join("\n")}`;
190
+ }
180
191
  function sendSseEvent(res, event, data) {
181
192
  res.write(`event: ${event}\n`);
182
193
  res.write(`data: ${JSON.stringify(data)}\n\n`);
@@ -1937,6 +1948,17 @@ export class MetadataServer {
1937
1948
  send(res, 200, { ok: true, ...result });
1938
1949
  return;
1939
1950
  }
1951
+ if (req.method === "POST" && url.pathname === "/agents/record-backend-session") {
1952
+ const body = (await readJson(req));
1953
+ if (!this.options.lifecycle?.recordBackendSessionId) {
1954
+ send(res, 501, { ok: false, error: "backend session recording not supported by this service" });
1955
+ return;
1956
+ }
1957
+ const result = await this.options.lifecycle.recordBackendSessionId(body);
1958
+ this.options.onChange?.();
1959
+ send(res, 200, { ok: true, ...result });
1960
+ return;
1961
+ }
1940
1962
  if (req.method === "POST" && url.pathname === "/agents/interrupt") {
1941
1963
  const body = (await readJson(req));
1942
1964
  if (!this.options.lifecycle?.interruptAgent) {
@@ -1989,19 +2011,53 @@ export class MetadataServer {
1989
2011
  return;
1990
2012
  }
1991
2013
  const text = typeof body.text === "string" ? body.text : "";
1992
- if (!text.trim()) {
2014
+ const attachmentIds = Array.isArray(body.attachmentIds)
2015
+ ? body.attachmentIds
2016
+ .filter((id) => typeof id === "string")
2017
+ .map((id) => id.trim())
2018
+ .filter(Boolean)
2019
+ : [];
2020
+ if (!text.trim() && attachmentIds.length === 0) {
1993
2021
  send(res, 400, { ok: false, error: "text is required" });
1994
2022
  return;
1995
2023
  }
2024
+ const attachments = attachmentIds.map((id) => getAttachmentRecord(id));
2025
+ const missingAttachmentId = attachmentIds.find((_, index) => attachments[index] === null);
2026
+ if (missingAttachmentId) {
2027
+ send(res, 400, { ok: false, error: `attachment not found: ${missingAttachmentId}` });
2028
+ return;
2029
+ }
1996
2030
  if (!this.options.lifecycle?.sendAgentInput) {
1997
2031
  send(res, 501, { ok: false, error: "agent input not supported by this service" });
1998
2032
  return;
1999
2033
  }
2000
- const result = await this.options.lifecycle.sendAgentInput({ sessionId, text });
2034
+ const formattedText = formatAgentInputWithAttachments(text, attachments.filter((entry) => !!entry));
2035
+ const result = await this.options.lifecycle.sendAgentInput({ sessionId, text: formattedText });
2001
2036
  this.options.onChange?.();
2002
2037
  send(res, 200, { ok: true, ...result });
2003
2038
  return;
2004
2039
  }
2040
+ if (req.method === "POST" && url.pathname === "/attachments") {
2041
+ const body = (await readJson(req));
2042
+ if (typeof body.filename !== "string" ||
2043
+ typeof body.mimeType !== "string" ||
2044
+ typeof body.dataBase64 !== "string") {
2045
+ send(res, 400, { ok: false, error: "filename, mimeType, and dataBase64 are required" });
2046
+ return;
2047
+ }
2048
+ try {
2049
+ const attachment = createUploadedAttachment({
2050
+ filename: body.filename,
2051
+ mimeType: body.mimeType,
2052
+ dataBase64: body.dataBase64,
2053
+ });
2054
+ send(res, 200, { ok: true, attachment });
2055
+ }
2056
+ catch (error) {
2057
+ send(res, 400, { ok: false, error: error instanceof Error ? error.message : "invalid attachment" });
2058
+ }
2059
+ return;
2060
+ }
2005
2061
  const attachmentContentMatch = url.pathname.match(/^\/attachments\/([^/]+)\/content$/);
2006
2062
  if (req.method === "GET" && attachmentContentMatch) {
2007
2063
  const content = getAttachmentContent(decodeURIComponent(attachmentContentMatch[1] || ""));
@@ -2254,4 +2310,3 @@ export class MetadataServer {
2254
2310
  send(res, 404, { ok: false, error: "not found" });
2255
2311
  }
2256
2312
  }
2257
- //# sourceMappingURL=metadata-server.js.map
@@ -1,6 +1,6 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
- import { writeJsonAtomic } from "./atomic-write.js";
3
+ import { quarantineCorruptFile, writeJsonAtomic, writeTextAtomic } from "./atomic-write.js";
4
4
  import { getProjectStateDir, getProjectStateDirFor } from "./paths.js";
5
5
  function ensureParent(path) {
6
6
  mkdirSync(dirname(path), { recursive: true });
@@ -21,6 +21,7 @@ function loadJson(path, fallback) {
21
21
  return JSON.parse(readFileSync(path, "utf-8"));
22
22
  }
23
23
  catch {
24
+ quarantineCorruptFile(path);
24
25
  return fallback;
25
26
  }
26
27
  }
@@ -94,7 +95,7 @@ export function saveMetadataEndpoint(endpoint, projectRoot) {
94
95
  saveJson(endpointPathFor(projectRoot), endpoint);
95
96
  const textPath = endpointTextPathFor(projectRoot);
96
97
  ensureParent(textPath);
97
- writeFileSync(textPath, `http://${endpoint.host}:${endpoint.port}\n`);
98
+ writeTextAtomic(textPath, `http://${endpoint.host}:${endpoint.port}\n`);
98
99
  }
99
100
  export function removeMetadataEndpoint(projectRoot) {
100
101
  try {
@@ -110,4 +111,3 @@ export function removeMetadataEndpoint(projectRoot) {
110
111
  }
111
112
  catch { }
112
113
  }
113
- //# sourceMappingURL=metadata-store.js.map
@@ -0,0 +1,8 @@
1
+ import type { AlertEvent } from "./project-events.js";
2
+ /**
3
+ * Forwards an alert that already passed desktop notification gating to the
4
+ * daemon, which relays it to the owner's mobile devices. Fire-and-forget: the
5
+ * daemon owns the single relay connection, so the project service hands off and
6
+ * never blocks the alert path on push delivery.
7
+ */
8
+ export declare function forwardAlertToMobilePush(event: AlertEvent): void;
@@ -0,0 +1,22 @@
1
+ import { requestDaemonJson } from "./daemon.js";
2
+ /**
3
+ * Forwards an alert that already passed desktop notification gating to the
4
+ * daemon, which relays it to the owner's mobile devices. Fire-and-forget: the
5
+ * daemon owns the single relay connection, so the project service hands off and
6
+ * never blocks the alert path on push delivery.
7
+ */
8
+ export function forwardAlertToMobilePush(event) {
9
+ void requestDaemonJson("/internal/push", {
10
+ method: "POST",
11
+ body: JSON.stringify({
12
+ title: event.title || "aimux",
13
+ body: event.message || event.sessionId || event.kind,
14
+ kind: event.kind,
15
+ sessionId: event.sessionId,
16
+ projectId: event.projectId,
17
+ projectRoot: process.cwd(),
18
+ dedupeKey: event.dedupeKey,
19
+ }),
20
+ headers: { "content-type": "application/json" },
21
+ }).catch(() => { });
22
+ }
@@ -0,0 +1,23 @@
1
+ export interface ThrottleInput {
2
+ dedupeKey?: string;
3
+ sessionId?: string;
4
+ kind?: string;
5
+ title?: string;
6
+ body?: string;
7
+ }
8
+ /**
9
+ * In-memory guard for outbound mobile pushes. Collapses identical alerts
10
+ * re-emitted within a TTL window (chatty idle/needs_input polling) and caps the
11
+ * push rate per session so one runaway agent cannot flood the device.
12
+ */
13
+ export declare class MobilePushThrottle {
14
+ private readonly dedupeTtlMs;
15
+ private readonly sessionLimit;
16
+ private readonly sessionWindowMs;
17
+ private readonly now;
18
+ private readonly lastByKey;
19
+ private readonly sessionHits;
20
+ constructor(dedupeTtlMs?: number, sessionLimit?: number, sessionWindowMs?: number, now?: () => number);
21
+ allow(input: ThrottleInput): boolean;
22
+ private prune;
23
+ }
@@ -0,0 +1,53 @@
1
+ const DEDUPE_TTL_MS = 60_000;
2
+ const SESSION_WINDOW_MS = 60_000;
3
+ const SESSION_LIMIT = 5;
4
+ /**
5
+ * In-memory guard for outbound mobile pushes. Collapses identical alerts
6
+ * re-emitted within a TTL window (chatty idle/needs_input polling) and caps the
7
+ * push rate per session so one runaway agent cannot flood the device.
8
+ */
9
+ export class MobilePushThrottle {
10
+ dedupeTtlMs;
11
+ sessionLimit;
12
+ sessionWindowMs;
13
+ now;
14
+ lastByKey = new Map();
15
+ sessionHits = new Map();
16
+ constructor(dedupeTtlMs = DEDUPE_TTL_MS, sessionLimit = SESSION_LIMIT, sessionWindowMs = SESSION_WINDOW_MS, now = () => Date.now()) {
17
+ this.dedupeTtlMs = dedupeTtlMs;
18
+ this.sessionLimit = sessionLimit;
19
+ this.sessionWindowMs = sessionWindowMs;
20
+ this.now = now;
21
+ }
22
+ allow(input) {
23
+ const ts = this.now();
24
+ this.prune(ts);
25
+ const key = input.dedupeKey?.trim() || [input.sessionId, input.kind, input.title, input.body].map((p) => p ?? "").join("|");
26
+ const last = this.lastByKey.get(key);
27
+ if (last !== undefined && ts - last < this.dedupeTtlMs)
28
+ return false;
29
+ const session = input.sessionId?.trim() || "_global";
30
+ const hits = (this.sessionHits.get(session) ?? []).filter((t) => ts - t < this.sessionWindowMs);
31
+ if (hits.length >= this.sessionLimit) {
32
+ this.sessionHits.set(session, hits);
33
+ return false;
34
+ }
35
+ hits.push(ts);
36
+ this.sessionHits.set(session, hits);
37
+ this.lastByKey.set(key, ts);
38
+ return true;
39
+ }
40
+ prune(ts) {
41
+ for (const [key, t] of this.lastByKey) {
42
+ if (ts - t >= this.dedupeTtlMs)
43
+ this.lastByKey.delete(key);
44
+ }
45
+ for (const [session, hits] of this.sessionHits) {
46
+ const fresh = hits.filter((t) => ts - t < this.sessionWindowMs);
47
+ if (fresh.length === 0)
48
+ this.sessionHits.delete(session);
49
+ else
50
+ this.sessionHits.set(session, fresh);
51
+ }
52
+ }
53
+ }
@@ -150,4 +150,3 @@ export const agentIoMethods = {
150
150
  return readAgentOutputImpl(this, sessionId, startLine);
151
151
  },
152
152
  };
153
- //# sourceMappingURL=agent-io-methods.js.map
@@ -384,4 +384,3 @@ export function openPlanInEditor(host, path) {
384
384
  host.renderDashboardErrorOverlay();
385
385
  }
386
386
  }
387
- //# sourceMappingURL=archives.js.map
@@ -184,4 +184,3 @@ export const dashboardActionMethods = {
184
184
  handleToolOptionsKeyImpl(this, data);
185
185
  },
186
186
  };
187
- //# sourceMappingURL=dashboard-actions-methods.js.map
@@ -673,4 +673,3 @@ export function handleOrchestrationRoutePickerKey(host, data) {
673
673
  showOrchestrationInput(host, mode, target);
674
674
  }
675
675
  }
676
- //# sourceMappingURL=dashboard-control.js.map
@@ -870,4 +870,3 @@ export const dashboardInteractionMethods = {
870
870
  this.renderDashboard();
871
871
  },
872
872
  };
873
- //# sourceMappingURL=dashboard-interaction.js.map
@@ -819,7 +819,7 @@ export async function startProjectServices(host) {
819
819
  targetSessionId: input.sessionId,
820
820
  targetWorktreePath: input.worktreePath,
821
821
  open: input.open ?? false,
822
- extraArgs: input.extraArgs,
822
+ launchOverride: input.launchOverride,
823
823
  }), input.sessionId
824
824
  ? buildMetadataPendingSessionSeed({
825
825
  sessionId: input.sessionId,
@@ -862,7 +862,7 @@ export async function startProjectServices(host) {
862
862
  instruction: input.instruction,
863
863
  targetWorktreePath: input.worktreePath,
864
864
  open: input.open ?? false,
865
- extraArgs: input.extraArgs,
865
+ launchOverride: input.launchOverride,
866
866
  }), input.targetSessionId
867
867
  ? buildMetadataPendingSessionSeed({
868
868
  sessionId: input.targetSessionId,
@@ -876,6 +876,7 @@ export async function startProjectServices(host) {
876
876
  renameAgent: (input) => host.renameAgent(input.sessionId, input.label),
877
877
  migrateAgent: (input) => withMetadataSessionPending(host, input.sessionId, "migrating", () => host.migrateAgent(input.sessionId, input.worktreePath), findDashboardSessionSeed(host, input.sessionId)),
878
878
  killAgent: (input) => withMetadataSessionPending(host, input.sessionId, "graveyarding", () => host.sendAgentToGraveyard(input.sessionId), findDashboardSessionSeed(host, input.sessionId)),
879
+ recordBackendSessionId: (input) => host.recordSessionBackendSessionId(input.sessionId, input.backendSessionId),
879
880
  sendAgentInput: (input) => host.sendAgentInput(input.sessionId, input.text),
880
881
  readAgentOutput: (input) => host.readAgentOutput(input.sessionId, input.startLine),
881
882
  },
@@ -935,4 +936,3 @@ async function ensureDashboardControlPlane(host) {
935
936
  host.dashboardServiceRecovery = null;
936
937
  }
937
938
  }
938
- //# sourceMappingURL=dashboard-model.js.map
@@ -1,12 +1,13 @@
1
1
  import type { DashboardService, DashboardSession } from "../dashboard/index.js";
2
2
  import type { PendingServiceActionKind, PendingSessionActionKind } from "../pending-actions.js";
3
+ import type { LaunchOverride } from "../shell-args.js";
3
4
  type DashboardOpsHost = any;
4
5
  export declare function runDashboardOperation<T>(host: DashboardOpsHost, title: string, lines: string[], work: () => Promise<T> | T, errorTitle?: string): Promise<T | undefined>;
5
6
  export declare function spawnDashboardAgentWithFeedback(host: DashboardOpsHost, input: {
6
7
  sessionId: string;
7
8
  tool: string;
8
9
  worktreePath?: string;
9
- extraArgs?: string[];
10
+ launchOverride?: LaunchOverride;
10
11
  }): Promise<void>;
11
12
  export declare function forkDashboardAgentWithFeedback(host: DashboardOpsHost, input: {
12
13
  sourceSessionId: string;
@@ -14,7 +15,7 @@ export declare function forkDashboardAgentWithFeedback(host: DashboardOpsHost, i
14
15
  tool: string;
15
16
  instruction?: string;
16
17
  worktreePath?: string;
17
- extraArgs?: string[];
18
+ launchOverride?: LaunchOverride;
18
19
  }): Promise<void>;
19
20
  export declare function setPendingDashboardSessionAction(host: DashboardOpsHost, sessionId: string, kind: PendingSessionActionKind | null, opts?: {
20
21
  sessionSeed?: DashboardSession;
@@ -300,7 +300,7 @@ export async function spawnDashboardAgentWithFeedback(host, input) {
300
300
  tool: input.tool,
301
301
  sessionId: input.sessionId,
302
302
  worktreePath: input.worktreePath,
303
- extraArgs: input.extraArgs,
303
+ launchOverride: input.launchOverride,
304
304
  open: false,
305
305
  }, { timeoutMs: 10_000 });
306
306
  },
@@ -330,7 +330,7 @@ export async function forkDashboardAgentWithFeedback(host, input) {
330
330
  tool: input.tool,
331
331
  instruction: input.instruction,
332
332
  worktreePath: input.worktreePath,
333
- extraArgs: input.extraArgs,
333
+ launchOverride: input.launchOverride,
334
334
  open: false,
335
335
  }, { timeoutMs: 10_000 });
336
336
  },
@@ -778,4 +778,3 @@ export async function migrateSessionWithFeedback(host, session, targetPath, targ
778
778
  errorTitle: `Failed to migrate "${label}"`,
779
779
  });
780
780
  }
781
- //# sourceMappingURL=dashboard-ops.js.map
@@ -225,4 +225,3 @@ export const dashboardStateMethods = {
225
225
  await startProjectServicesImpl(this);
226
226
  },
227
227
  };
228
- //# sourceMappingURL=dashboard-state-methods.js.map
@@ -3,6 +3,7 @@ import type { Multiplexer, SessionState } from "./index.js";
3
3
  import { dashboardSessionActionDeps as dashboardSessionActionDepsImpl } from "./dashboard-ops.js";
4
4
  import type { PendingServiceActionKind, PendingSessionActionKind } from "../pending-actions.js";
5
5
  import type { SessionRuntime } from "../session-runtime.js";
6
+ import type { LaunchOverride } from "../shell-args.js";
6
7
  export type DashboardTailMethods = {
7
8
  forkAgent(this: Multiplexer, opts: {
8
9
  sourceSessionId: string;
@@ -11,7 +12,7 @@ export type DashboardTailMethods = {
11
12
  instruction?: string;
12
13
  targetWorktreePath?: string;
13
14
  open?: boolean;
14
- extraArgs?: string[];
15
+ launchOverride?: LaunchOverride;
15
16
  }): Promise<{
16
17
  sessionId: string;
17
18
  threadId: string;
@@ -21,7 +22,7 @@ export type DashboardTailMethods = {
21
22
  targetSessionId?: string;
22
23
  targetWorktreePath?: string;
23
24
  open?: boolean;
24
- extraArgs?: string[];
25
+ launchOverride?: LaunchOverride;
25
26
  }): Promise<{
26
27
  sessionId: string;
27
28
  }>;
@@ -55,7 +55,7 @@ function refreshLifecycleViews(host) {
55
55
  }
56
56
  export const dashboardTailMethods = {
57
57
  async forkAgent(opts) {
58
- const result = await this.forkSessionFromSource(opts.sourceSessionId, opts.targetToolConfigKey, opts.targetSessionId, opts.instruction, opts.targetWorktreePath, opts.extraArgs ?? []);
58
+ const result = await this.forkSessionFromSource(opts.sourceSessionId, opts.targetToolConfigKey, opts.targetSessionId, opts.instruction, opts.targetWorktreePath, opts.launchOverride);
59
59
  if (!result) {
60
60
  throw new Error(`Unable to fork agent "${opts.sourceSessionId}"`);
61
61
  }
@@ -71,7 +71,7 @@ export const dashboardTailMethods = {
71
71
  throw new Error(`Unknown tool config: ${opts.toolConfigKey}`);
72
72
  }
73
73
  const sessionId = opts.targetSessionId ?? this.generateDashboardSessionId?.(tool.command);
74
- const transport = this.createSession(tool.command, [...tool.args, ...(opts.extraArgs ?? [])], tool.preambleFlag, opts.toolConfigKey, undefined, tool.sessionIdFlag, opts.targetWorktreePath, undefined, sessionId, !opts.open);
74
+ const transport = this.createSession(opts.launchOverride?.command ?? tool.command, opts.launchOverride?.args ?? tool.args, tool.preambleFlag, opts.toolConfigKey, undefined, tool.sessionIdFlag, opts.targetWorktreePath, undefined, sessionId, !opts.open, false, undefined, opts.launchOverride?.env);
75
75
  if (opts.open) {
76
76
  this.openLiveTmuxWindowForEntry({ id: transport.id });
77
77
  }
@@ -378,4 +378,3 @@ export const dashboardTailMethods = {
378
378
  return orderDashboardSessionsByVisualWorktree(allDash, worktreePaths, mainRepoPath);
379
379
  },
380
380
  };
381
- //# sourceMappingURL=dashboard-tail-methods.js.map
@@ -2,6 +2,7 @@ import { closeNotificationPanel as closeNotificationPanelImpl, handleNotificatio
2
2
  import { beginWorktreeRemoval as beginWorktreeRemovalImpl, finishWorktreeRemoval as finishWorktreeRemovalImpl, handleWorktreeInputKey as handleWorktreeInputKeyImpl, handleWorktreeRemoveConfirmKey as handleWorktreeRemoveConfirmKeyImpl, handleWorktreeListKey as handleWorktreeListKeyImpl, renderWorktreeInput as renderWorktreeInputImpl, renderWorktreeList as renderWorktreeListImpl, renderWorktreeRemoveConfirm as renderWorktreeRemoveConfirmImpl, showWorktreeCreatePrompt as showWorktreeCreatePromptImpl, showWorktreeList as showWorktreeListImpl, } from "./worktrees.js";
3
3
  import { createService as createServiceImpl, removeOfflineService as removeOfflineServiceImpl, resumeOfflineService as resumeOfflineServiceImpl, resumeOfflineServiceById as resumeOfflineServiceByIdImpl, serviceLabelForCommand as serviceLabelForCommandImpl, stopService as stopServiceImpl, } from "./services.js";
4
4
  import { derivedStatusLabel } from "../dashboard/index.js";
5
+ import { isDevelopmentRuntime } from "../connection-targets.js";
5
6
  import { selectDashboardTeammates } from "../dashboard/session-registry.js";
6
7
  import { hasRuntimeEvidence, isAttachableDashboardSessionEntry } from "../dashboard/runtime-evidence.js";
7
8
  export const dashboardViewMethods = {
@@ -113,6 +114,7 @@ export const dashboardViewMethods = {
113
114
  selectedServiceId: selectedService,
114
115
  selectedTeammates: selectDashboardTeammates(dashTeammates, selectedSessionEntry),
115
116
  runtimeLabel: "tmux",
117
+ isDevRuntime: isDevelopmentRuntime(),
116
118
  mainCheckout: mainCheckoutInfo,
117
119
  operationFailures: this.dashboardOperationFailuresCache ?? [],
118
120
  worktreeRemoval: this.worktreeRemovalJob
@@ -214,4 +216,3 @@ export const dashboardViewMethods = {
214
216
  handleWorktreeListKeyImpl(this, data);
215
217
  },
216
218
  };
217
- //# sourceMappingURL=dashboard-view-methods.js.map
@@ -221,4 +221,3 @@ function compareRecencyOrCreated(left, leftLastUsedAt, right, rightLastUsedAt) {
221
221
  const rightCreated = parseRecencyTimestamp(right.createdAt) ?? 0;
222
222
  return rightCreated - leftCreated || left.id.localeCompare(right.id);
223
223
  }
224
- //# sourceMappingURL=graveyard-view-model.js.map
@@ -185,7 +185,7 @@ export declare class Multiplexer {
185
185
  * Starts fresh sessions but with context from the previous conversation.
186
186
  */
187
187
  restoreSessions(toolFilter?: string): Promise<number>;
188
- createSession(command: string, args: string[], preambleFlag?: string[], toolConfigKey?: string, extraPreamble?: string, sessionIdFlag?: string[], worktreePath?: string, backendSessionIdOverride?: string, sessionIdOverride?: string, detachedInTmux?: boolean, suppressStartupPreamble?: boolean, team?: SessionTeamMetadata): SessionTransport;
188
+ createSession(command: string, args: string[], preambleFlag?: string[], toolConfigKey?: string, extraPreamble?: string, sessionIdFlag?: string[], worktreePath?: string, backendSessionIdOverride?: string, sessionIdOverride?: string, detachedInTmux?: boolean, suppressStartupPreamble?: boolean, team?: SessionTeamMetadata, launchEnv?: Record<string, string>): SessionTransport;
189
189
  recordSessionBackendSessionId(sessionId: string, backendSessionId: string): {
190
190
  sessionId: string;
191
191
  backendSessionId: string;
@@ -317,8 +317,8 @@ export class Multiplexer {
317
317
  async restoreSessions(toolFilter) {
318
318
  return restoreSessionsImpl(this, toolFilter);
319
319
  }
320
- createSession(command, args, preambleFlag, toolConfigKey, extraPreamble, sessionIdFlag, worktreePath, backendSessionIdOverride, sessionIdOverride, detachedInTmux = false, suppressStartupPreamble = false, team) {
321
- return createSessionImpl(this, command, args, preambleFlag, toolConfigKey, extraPreamble, sessionIdFlag, worktreePath, backendSessionIdOverride, sessionIdOverride, detachedInTmux, suppressStartupPreamble, team);
320
+ createSession(command, args, preambleFlag, toolConfigKey, extraPreamble, sessionIdFlag, worktreePath, backendSessionIdOverride, sessionIdOverride, detachedInTmux = false, suppressStartupPreamble = false, team, launchEnv) {
321
+ return createSessionImpl(this, command, args, preambleFlag, toolConfigKey, extraPreamble, sessionIdFlag, worktreePath, backendSessionIdOverride, sessionIdOverride, detachedInTmux, suppressStartupPreamble, team, launchEnv);
322
322
  }
323
323
  recordSessionBackendSessionId(sessionId, backendSessionId) {
324
324
  return runtimeLifecycleMethods.recordSessionBackendSessionId.call(this, sessionId, backendSessionId);
@@ -348,7 +348,7 @@ export class Multiplexer {
348
348
  handleAction(action) {
349
349
  handleActionImpl(this, action);
350
350
  }
351
- async forkSessionFromSource(sourceSessionId, targetToolConfigKey, requestedTargetSessionId, instruction, targetWorktreePath, extraArgs = []) {
351
+ async forkSessionFromSource(sourceSessionId, targetToolConfigKey, requestedTargetSessionId, instruction, targetWorktreePath, launchOverride) {
352
352
  const sourceSession = this.sessions.find((session) => session.id === sourceSessionId);
353
353
  if (!sourceSession) {
354
354
  this.showDashboardError("Cannot fork missing session", [`Source session ${sourceSessionId} not found.`]);
@@ -402,7 +402,7 @@ export class Multiplexer {
402
402
  ]
403
403
  .filter(Boolean)
404
404
  .join("\n\n");
405
- const transport = this.createSession(toolCfg.command, [...toolCfg.args, ...extraArgs], toolCfg.preambleFlag, targetToolConfigKey, extraPreamble, toolCfg.sessionIdFlag, targetWorktree, undefined, targetSessionId, !toolCfg.preambleFlag);
405
+ const transport = this.createSession(launchOverride?.command ?? toolCfg.command, launchOverride?.args ?? toolCfg.args, toolCfg.preambleFlag, targetToolConfigKey, extraPreamble, toolCfg.sessionIdFlag, targetWorktree, undefined, targetSessionId, !toolCfg.preambleFlag, false, undefined, launchOverride?.env);
406
406
  this.agentTracker.emit(sourceSessionId, {
407
407
  kind: "status",
408
408
  message: `Forked ${targetSessionId} from this session`,
@@ -460,4 +460,3 @@ export class Multiplexer {
460
460
  }
461
461
  }
462
462
  Object.assign(Multiplexer.prototype, dashboardInteractionMethods, dashboardViewMethods, dashboardActionMethods, dashboardTailMethods, persistenceMethods, dashboardStateMethods, agentIoMethods, runtimeLifecycleMethods);
463
- //# sourceMappingURL=index.js.map
@@ -225,4 +225,3 @@ export function handleMigratePickerKey(host, data) {
225
225
  host.focusSession(host.activeIndex);
226
226
  }
227
227
  }
228
- //# sourceMappingURL=navigation.js.map
@@ -361,4 +361,3 @@ export function handleNotificationsKey(host, data) {
361
361
  void openSelectedNotification(host);
362
362
  }
363
363
  }
364
- //# sourceMappingURL=notifications.js.map
@@ -5,6 +5,7 @@ import { addDashboardOperationFailure, clearDashboardOperationFailures, listDash
5
5
  import { composeDashboardWorktreeGroups } from "./dashboard-model.js";
6
6
  import { loadDaemonInfo } from "../daemon.js";
7
7
  import { getProjectStateDir, getStatePath } from "../paths.js";
8
+ import { writeJsonAtomic } from "../atomic-write.js";
8
9
  import { loadMetadataState } from "../metadata-store.js";
9
10
  import { createRuntimeExchangeStore } from "../runtime-core/exchange-store.js";
10
11
  import { renderCurrentDashboardView as renderCurrentDashboardViewImpl } from "./runtime-state.js";
@@ -855,7 +856,7 @@ function removePersistedServicesForWorktree(path) {
855
856
  const nextServices = (state.services ?? []).filter((service) => service.worktreePath !== path);
856
857
  if (nextServices.length === (state.services ?? []).length)
857
858
  return;
858
- writeFileSync(statePath, JSON.stringify({ ...state, services: nextServices }, null, 2) + "\n");
859
+ writeJsonAtomic(statePath, { ...state, services: nextServices });
859
860
  }
860
861
  catch { }
861
862
  }
@@ -874,4 +875,3 @@ function markLifecycleUsed(host, itemId) {
874
875
  }
875
876
  catch { }
876
877
  }
877
- //# sourceMappingURL=persistence-methods.js.map
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { closeDebug, debug } from "../debug.js";
5
5
  import { loadConfig } from "../config.js";
6
6
  import { getStatePath } from "../paths.js";
7
+ import { quarantineCorruptFile, writeJsonAtomic } from "../atomic-write.js";
7
8
  import { buildAimuxAgentInstructions } from "../session-bootstrap.js";
8
9
  import { listTopologySessionStates, saveRuntimeTopologySessions } from "../runtime-core/topology-sessions.js";
9
10
  import { adjustAfterRemove as adjustAfterRemoveImpl, buildLiveServiceStates as buildLiveServiceStatesImpl, evictZombieSession as evictZombieSessionImpl, graveyardSession as graveyardSessionImpl, isSessionRuntimeLive as isSessionRuntimeLiveImpl, loadOfflineServices as loadOfflineServicesImpl, loadOfflineTopologySessions as loadOfflineTopologySessionsImpl, restoreTmuxSessionsFromTopology as restoreTmuxSessionsFromTopologyImpl, recordSessionBackendSessionId as recordSessionBackendSessionIdImpl, resumeOfflineSession as resumeOfflineSessionImpl, startHeartbeat as startHeartbeatImpl, startProjectServiceRefresh as startProjectServiceRefreshImpl, startStatusRefresh as startStatusRefreshImpl, stopHeartbeat as stopHeartbeatImpl, stopProjectServiceRefresh as stopProjectServiceRefreshImpl, stopSessionToOffline as stopSessionToOfflineImpl, stopStatusRefresh as stopStatusRefreshImpl, syncSessionsFromTopology as syncSessionsFromTopologyImpl, } from "./runtime-state.js";
@@ -92,6 +93,7 @@ export function loadStateStatic() {
92
93
  };
93
94
  }
94
95
  catch {
96
+ quarantineCorruptFile(statePath);
95
97
  return null;
96
98
  }
97
99
  }
@@ -261,7 +263,9 @@ export const runtimeLifecycleMethods = {
261
263
  });
262
264
  mergedServices = [...otherServices, ...myServices];
263
265
  }
264
- catch { }
266
+ catch {
267
+ quarantineCorruptFile(statePath);
268
+ }
265
269
  }
266
270
  saveRuntimeTopologySessions({ sessions: mergedSessions });
267
271
  unpreservedExitedIds.clear();
@@ -270,7 +274,7 @@ export const runtimeLifecycleMethods = {
270
274
  cwd: process.cwd(),
271
275
  services: mergedServices,
272
276
  };
273
- writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n");
277
+ writeJsonAtomic(statePath, state);
274
278
  this.invalidateDesktopStateSnapshot();
275
279
  },
276
280
  teardown() {
@@ -324,4 +328,3 @@ export const runtimeLifecycleMethods = {
324
328
  process.exit(0);
325
329
  },
326
330
  };
327
- //# sourceMappingURL=runtime-lifecycle-methods.js.map