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
@@ -0,0 +1,117 @@
1
+ import { existsSync, readFileSync, rmSync } from "node:fs";
2
+ import { execFileSync } from "node:child_process";
3
+ import { homedir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { getGlobalAimuxDir, getProjectIdFor } from "./paths.js";
6
+ import { writeJsonAtomic } from "./atomic-write.js";
7
+ import { requestJson } from "./http-client.js";
8
+ import { log } from "./debug.js";
9
+ function isPidAlive(pid) {
10
+ try {
11
+ process.kill(pid, 0);
12
+ return true;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ function knownOwners() {
19
+ return [
20
+ { home: join(homedir(), ".aimux"), port: 43190 },
21
+ { home: join(homedir(), ".aimux-dev"), port: 43191 },
22
+ ];
23
+ }
24
+ function readJson(path) {
25
+ if (!existsSync(path))
26
+ return null;
27
+ try {
28
+ return JSON.parse(readFileSync(path, "utf-8"));
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ function writeJson(path, value) {
35
+ writeJsonAtomic(path, value);
36
+ }
37
+ function isAimuxProjectServiceProcess(pid) {
38
+ try {
39
+ const args = execFileSync("ps", ["-o", "args=", "-p", String(pid)], {
40
+ encoding: "utf8",
41
+ stdio: ["ignore", "pipe", "ignore"],
42
+ });
43
+ return args.includes("__project-service-internal");
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ async function requestOtherOwnerStop(owner, projectRoot) {
50
+ const info = readJson(join(owner.home, "daemon", "daemon.json"));
51
+ const port = info?.port ?? owner.port;
52
+ if (!info?.pid || !isPidAlive(info.pid))
53
+ return false;
54
+ try {
55
+ const { status, json } = await requestJson(`http://127.0.0.1:${port}/projects/stop`, {
56
+ method: "POST",
57
+ headers: { "content-type": "application/json" },
58
+ body: { projectRoot },
59
+ timeoutMs: 1500,
60
+ });
61
+ return status >= 200 && status < 300 && json?.ok !== false;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ function cleanOtherOwnerProjectState(owner, projectId) {
68
+ const statePath = join(owner.home, "daemon", "state.json");
69
+ const state = readJson(statePath);
70
+ const entry = state?.projects?.[projectId];
71
+ if (entry?.pid && isPidAlive(entry.pid)) {
72
+ if (isAimuxProjectServiceProcess(entry.pid)) {
73
+ try {
74
+ process.kill(entry.pid, "SIGTERM");
75
+ }
76
+ catch { }
77
+ }
78
+ else {
79
+ log.warn("skipping stale takeover pid with unverified identity", "daemon", {
80
+ ownerHome: owner.home,
81
+ projectId,
82
+ projectRoot: entry.projectRoot,
83
+ pid: entry.pid,
84
+ });
85
+ }
86
+ }
87
+ if (state?.projects?.[projectId]) {
88
+ delete state.projects[projectId];
89
+ writeJson(statePath, state);
90
+ }
91
+ const projectStateDir = join(owner.home, "projects", projectId);
92
+ for (const file of ["metadata-api.json", "metadata-api.txt", "host.json"]) {
93
+ try {
94
+ rmSync(join(projectStateDir, file), { force: true });
95
+ }
96
+ catch { }
97
+ }
98
+ }
99
+ export async function takeOverProjectFromOtherOwners(projectRoot) {
100
+ const currentHome = resolve(getGlobalAimuxDir());
101
+ const projectId = getProjectIdFor(projectRoot);
102
+ for (const owner of knownOwners()) {
103
+ if (resolve(owner.home) === currentHome)
104
+ continue;
105
+ try {
106
+ await requestOtherOwnerStop(owner, projectRoot);
107
+ cleanOtherOwnerProjectState(owner, projectId);
108
+ }
109
+ catch (error) {
110
+ log.warn("project takeover cleanup failed for alternate owner", "daemon", {
111
+ ownerHome: owner.home,
112
+ projectRoot,
113
+ error: error instanceof Error ? error.message : String(error),
114
+ });
115
+ }
116
+ }
117
+ }
package/dist/recency.js CHANGED
@@ -31,4 +31,3 @@ export function formatRelativeRecency(value, now = Date.now()) {
31
31
  const years = Math.floor(days / 365);
32
32
  return `${years}y ago`;
33
33
  }
34
- //# sourceMappingURL=recency.js.map
package/dist/recorder.js CHANGED
@@ -127,4 +127,3 @@ export class Recorder {
127
127
  this.txtStream.end();
128
128
  }
129
129
  }
130
- //# sourceMappingURL=recorder.js.map
@@ -1,4 +1,13 @@
1
1
  import type { AimuxDaemon } from "./daemon.js";
2
+ export interface RelayNotificationPush {
3
+ title: string;
4
+ body: string;
5
+ kind?: string;
6
+ sessionId?: string;
7
+ projectId?: string;
8
+ projectRoot?: string;
9
+ dedupeKey?: string;
10
+ }
2
11
  export type RelayConnectionStatus = "connected" | "connecting" | "reconnecting" | "disconnected" | "auth_failed";
3
12
  export interface RelayStatusSnapshot {
4
13
  status: RelayConnectionStatus;
@@ -22,6 +31,7 @@ export declare class RelayClient {
22
31
  constructor(relayUrl: string, token: string, daemon: AimuxDaemon);
23
32
  getStatus(): RelayStatusSnapshot;
24
33
  connect(): void;
34
+ pushNotification(notification: RelayNotificationPush): void;
25
35
  disconnect(): void;
26
36
  private handleMessage;
27
37
  private sendRaw;
@@ -100,6 +100,11 @@ export class RelayClient {
100
100
  catch { }
101
101
  });
102
102
  }
103
+ pushNotification(notification) {
104
+ if (!notification.title)
105
+ return;
106
+ this.sendRaw(JSON.stringify({ type: "notification_push", notification }));
107
+ }
103
108
  disconnect() {
104
109
  this.stopped = true;
105
110
  this.status = "disconnected";
@@ -188,4 +193,3 @@ export class RelayClient {
188
193
  this.retryMs = Math.min(this.retryMs * 2, MAX_RETRY_MS);
189
194
  }
190
195
  }
191
- //# sourceMappingURL=relay-client.js.map
@@ -88,4 +88,3 @@ export function assertRemoteAccessAllowed(actor, method, pathname, searchParams)
88
88
  }
89
89
  return { ok: false, status: 403, error: "shared guests can only read shared session output" };
90
90
  }
91
- //# sourceMappingURL=remote-access.js.map
@@ -0,0 +1,13 @@
1
+ export interface BackendIdReconcileResult {
2
+ reconciled: Array<{
3
+ id: string;
4
+ backendSessionId: string;
5
+ }>;
6
+ }
7
+ /**
8
+ * Backfill missing backend session ids for offline sessions from each tool's
9
+ * on-disk session store, so a crash that lost the id before capture does not
10
+ * leave the agent unresumable. Idempotent: sessions that already have an id,
11
+ * or whose id cannot be found on disk, are left untouched.
12
+ */
13
+ export declare function reconcileOfflineBackendSessionIds(projectRoot?: string): BackendIdReconcileResult;
@@ -0,0 +1,23 @@
1
+ import { discoverBackendSessionId } from "../backend-session-discovery.js";
2
+ import { getRepoRoot } from "../paths.js";
3
+ import { listTopologySessionStates, upsertTopologySession } from "./topology-sessions.js";
4
+ /**
5
+ * Backfill missing backend session ids for offline sessions from each tool's
6
+ * on-disk session store, so a crash that lost the id before capture does not
7
+ * leave the agent unresumable. Idempotent: sessions that already have an id,
8
+ * or whose id cannot be found on disk, are left untouched.
9
+ */
10
+ export function reconcileOfflineBackendSessionIds(projectRoot = getRepoRoot()) {
11
+ const reconciled = [];
12
+ for (const session of listTopologySessionStates({ statuses: ["offline"] })) {
13
+ if (session.backendSessionId)
14
+ continue;
15
+ const cwd = session.worktreePath ?? projectRoot;
16
+ const discovered = discoverBackendSessionId(session.toolConfigKey, cwd);
17
+ if (!discovered)
18
+ continue;
19
+ upsertTopologySession({ ...session, backendSessionId: discovered }, "offline", { projectRoot });
20
+ reconciled.push({ id: session.id, backendSessionId: discovered });
21
+ }
22
+ return { reconciled };
23
+ }
@@ -151,4 +151,3 @@ export function deriveRuntimeExchangeIndexes(exchange) {
151
151
  inbox: preserveAcknowledgedInbox(nextInbox, exchange.inbox),
152
152
  };
153
153
  }
154
- //# sourceMappingURL=exchange-derived.js.map
@@ -315,4 +315,3 @@ export function importRuntimeExchangeFromLegacyFiles(input = {}) {
315
315
  attachments: readLegacyAttachments(),
316
316
  });
317
317
  }
318
- //# sourceMappingURL=exchange-import.js.map
@@ -1,6 +1,7 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { parse, stringify } from "yaml";
4
+ import { atomicWrite } from "../atomic-write.js";
4
5
  import { getRuntimeExchangePath } from "../paths.js";
5
6
  export const RUNTIME_EXCHANGE_VERSION = 1;
6
7
  const UPDATE_LOCK_TIMEOUT_MS = 5_000;
@@ -376,14 +377,8 @@ export class RuntimeExchangeStore {
376
377
  return coerceRuntimeExchange(parsed);
377
378
  }
378
379
  write(exchange) {
379
- mkdirSync(dirname(this.path), { recursive: true });
380
380
  const normalized = coerceRuntimeExchange(exchange);
381
- const tmpPath = `${this.path}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
382
- writeFileSync(tmpPath, stringify(normalized, {
383
- lineWidth: 120,
384
- sortMapEntries: false,
385
- }));
386
- renameSync(tmpPath, this.path);
381
+ atomicWrite(this.path, stringify(normalized, { lineWidth: 120, sortMapEntries: false }));
387
382
  return normalized;
388
383
  }
389
384
  acquireUpdateLock() {
@@ -450,4 +445,3 @@ export class RuntimeExchangeStore {
450
445
  export function createRuntimeExchangeStore(path) {
451
446
  return new RuntimeExchangeStore(path);
452
447
  }
453
- //# sourceMappingURL=exchange-store.js.map
@@ -168,4 +168,3 @@ export function removeTopologyServicesForWorktree(worktreePath, input) {
168
168
  });
169
169
  return removed;
170
170
  }
171
- //# sourceMappingURL=topology-services.js.map
@@ -236,4 +236,3 @@ export function resurrectTopologySession(sessionId, input) {
236
236
  });
237
237
  return restored;
238
238
  }
239
- //# sourceMappingURL=topology-sessions.js.map
@@ -1,6 +1,7 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { parse, stringify } from "yaml";
4
+ import { atomicWrite } from "../atomic-write.js";
4
5
  import { getRuntimeTopologyPath } from "../paths.js";
5
6
  export const RUNTIME_TOPOLOGY_VERSION = 1;
6
7
  const UPDATE_LOCK_TIMEOUT_MS = 5_000;
@@ -370,14 +371,8 @@ export class RuntimeTopologyStore {
370
371
  return coerceRuntimeTopology(parsed);
371
372
  }
372
373
  write(topology) {
373
- mkdirSync(dirname(this.path), { recursive: true });
374
374
  const normalized = coerceRuntimeTopology(topology);
375
- const tmpPath = `${this.path}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
376
- writeFileSync(tmpPath, stringify(normalized, {
377
- lineWidth: 120,
378
- sortMapEntries: false,
379
- }));
380
- renameSync(tmpPath, this.path);
375
+ atomicWrite(this.path, stringify(normalized, { lineWidth: 120, sortMapEntries: false }));
381
376
  return normalized;
382
377
  }
383
378
  acquireUpdateLock() {
@@ -417,4 +412,3 @@ export class RuntimeTopologyStore {
417
412
  export function createRuntimeTopologyStore(path) {
418
413
  return new RuntimeTopologyStore(path);
419
414
  }
420
- //# sourceMappingURL=topology-store.js.map
@@ -197,4 +197,3 @@ export function removeTopologyWorktree(path, input) {
197
197
  });
198
198
  return removed;
199
199
  }
200
- //# sourceMappingURL=topology-worktrees.js.map
@@ -396,4 +396,3 @@ export function renderRuntimeMigrationRollbackResult(manifest) {
396
396
  removedCopiedDirs: manifest.copiedDirs,
397
397
  }, null, 2);
398
398
  }
399
- //# sourceMappingURL=runtime-migration.js.map
@@ -0,0 +1,3 @@
1
+ export declare const TMUX_RUNTIME_OWNER_OPTION = "@aimux-runtime-owner";
2
+ export declare const TMUX_DASHBOARD_OWNER_OPTION = "@aimux-dashboard-owner";
3
+ export declare function getRuntimeOwnerId(): string;
@@ -0,0 +1,10 @@
1
+ import { getGlobalAimuxDir } from "./paths.js";
2
+ const DEFAULT_DAEMON_PORT = "43190";
3
+ export const TMUX_RUNTIME_OWNER_OPTION = "@aimux-runtime-owner";
4
+ export const TMUX_DASHBOARD_OWNER_OPTION = "@aimux-dashboard-owner";
5
+ export function getRuntimeOwnerId() {
6
+ return JSON.stringify({
7
+ home: getGlobalAimuxDir(),
8
+ port: process.env.AIMUX_DAEMON_PORT?.trim() || DEFAULT_DAEMON_PORT,
9
+ });
10
+ }
@@ -373,4 +373,3 @@ export function getToolResumeArgs(toolCfg, backendSessionId) {
373
373
  }
374
374
  return toolCfg.resumeArgs.map((arg) => arg.replace("{sessionId}", backendSessionId));
375
375
  }
376
- //# sourceMappingURL=session-bootstrap.js.map
@@ -54,4 +54,3 @@ export class SessionRuntime {
54
54
  this.transport.destroy();
55
55
  }
56
56
  }
57
- //# sourceMappingURL=session-runtime.js.map
@@ -219,4 +219,3 @@ export function sessionSemanticAttentionScore(semantic) {
219
219
  export function sessionSemanticCompactHint(semantic) {
220
220
  return semantic.presentation.compactHint;
221
221
  }
222
- //# sourceMappingURL=session-semantics.js.map
@@ -1 +1,14 @@
1
+ export interface LaunchOverride {
2
+ /** Binary to exec. May differ from the tool's configured command. */
3
+ command: string;
4
+ /** Full argument list (replaces the tool's default args, not appended). */
5
+ args: string[];
6
+ /** Extra env vars merged into the managed launch environment. */
7
+ env?: Record<string, string>;
8
+ }
9
+ /**
10
+ * Parse a full command line where leading NAME=VALUE tokens are environment
11
+ * assignments, the next token is the command, and the rest are its args.
12
+ */
13
+ export declare function parseLaunchCommandLine(input: string): LaunchOverride;
1
14
  export declare function parseShellArgs(input: string): string[];
@@ -1,3 +1,28 @@
1
+ const ENV_ASSIGNMENT = /^([A-Za-z_][A-Za-z0-9_]*)=([\s\S]*)$/;
2
+ /**
3
+ * Parse a full command line where leading NAME=VALUE tokens are environment
4
+ * assignments, the next token is the command, and the rest are its args.
5
+ */
6
+ export function parseLaunchCommandLine(input) {
7
+ const tokens = parseShellArgs(input);
8
+ const env = {};
9
+ let i = 0;
10
+ for (; i < tokens.length; i++) {
11
+ const match = ENV_ASSIGNMENT.exec(tokens[i]);
12
+ if (!match)
13
+ break;
14
+ env[match[1]] = match[2];
15
+ }
16
+ const command = tokens[i];
17
+ if (!command) {
18
+ throw new Error("no command to launch");
19
+ }
20
+ return {
21
+ command,
22
+ args: tokens.slice(i + 1),
23
+ env: Object.keys(env).length ? env : undefined,
24
+ };
25
+ }
1
26
  export function parseShellArgs(input) {
2
27
  const args = [];
3
28
  let current = "";
@@ -54,4 +79,3 @@ export function parseShellArgs(input) {
54
79
  }
55
80
  return args;
56
81
  }
57
- //# sourceMappingURL=shell-args.js.map
@@ -14,6 +14,7 @@ export declare function wrapCommandWithShellIntegration(opts: {
14
14
  args: string[];
15
15
  shellPath?: string;
16
16
  env?: NodeJS.ProcessEnv;
17
+ extraEnv?: Record<string, string>;
17
18
  }): {
18
19
  command: string;
19
20
  args: string[];
@@ -163,6 +163,7 @@ export function prepareShellIntegration(projectRoot, shellPath = process.env.SHE
163
163
  export function wrapCommandWithShellIntegration(opts) {
164
164
  const prepared = prepareShellIntegration(opts.projectRoot, opts.shellPath);
165
165
  const envArgs = [
166
+ ...Object.entries(opts.extraEnv ?? {}).map(([key, value]) => `${key}=${value}`),
166
167
  `AIMUX_SESSION_ID=${opts.sessionId}`,
167
168
  `AIMUX_TOOL=${opts.tool}`,
168
169
  `AIMUX_METADATA_ENDPOINT_FILE=${join(getProjectStateDirFor(opts.projectRoot), "metadata-api.txt")}`,
@@ -195,4 +196,3 @@ export function wrapInteractiveShellWithIntegration(opts) {
195
196
  env: opts.env,
196
197
  });
197
198
  }
198
- //# sourceMappingURL=shell-hooks.js.map
@@ -73,4 +73,3 @@ export function applyShellStateTransition(input) {
73
73
  command,
74
74
  };
75
75
  }
76
- //# sourceMappingURL=shell-state.js.map
@@ -64,4 +64,3 @@ export class StatusDetector {
64
64
  }
65
65
  }
66
66
  }
67
- //# sourceMappingURL=status-detector.js.map
@@ -241,4 +241,3 @@ export function resolveExactSessionMetadata(data, projectRoot, currentSession, c
241
241
  return undefined;
242
242
  return data.metadata?.[activeSessionId];
243
243
  }
244
- //# sourceMappingURL=statusline-model.js.map
@@ -121,4 +121,3 @@ export async function requestReview(agentSessionId, agentRole, diff, summary) {
121
121
  await writeTask(reviewTask);
122
122
  return reviewTask;
123
123
  }
124
- //# sourceMappingURL=task-workflow.js.map
package/dist/tasks.js CHANGED
@@ -93,4 +93,3 @@ export function listPendingReviews(role) {
93
93
  export function listTasksForRole(role) {
94
94
  return readAllTasks().filter((task) => task.assignee === role && task.status !== "done" && task.status !== "failed");
95
95
  }
96
- //# sourceMappingURL=tasks.js.map
package/dist/team.js CHANGED
@@ -1,5 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
1
+ import { existsSync, readFileSync, mkdirSync } from "node:fs";
2
2
  import { getLocalAimuxDir, getProjectTeamPath, getGlobalTeamPath, getGlobalAimuxDir } from "./paths.js";
3
+ import { writeJsonAtomic } from "./atomic-write.js";
3
4
  export function isTeammateSession(session) {
4
5
  return Boolean(session?.team?.parentSessionId);
5
6
  }
@@ -91,7 +92,7 @@ export function saveTeamConfig(config) {
91
92
  if (!existsSync(dir)) {
92
93
  mkdirSync(dir, { recursive: true });
93
94
  }
94
- writeFileSync(getProjectTeamPath(), JSON.stringify(config, null, 2) + "\n");
95
+ writeJsonAtomic(getProjectTeamPath(), config);
95
96
  }
96
97
  /**
97
98
  * Save team config at the global level (~/.aimux/team.json).
@@ -101,7 +102,7 @@ export function saveGlobalTeamConfig(config) {
101
102
  if (!existsSync(dir)) {
102
103
  mkdirSync(dir, { recursive: true });
103
104
  }
104
- writeFileSync(getGlobalTeamPath(), JSON.stringify(config, null, 2) + "\n");
105
+ writeJsonAtomic(getGlobalTeamPath(), config);
105
106
  }
106
107
  /**
107
108
  * Build a role-specific preamble string for injection into an agent's system prompt.
@@ -128,4 +129,3 @@ export function buildRolePreamble(role, config) {
128
129
  }
129
130
  return lines.join("\n");
130
131
  }
131
- //# sourceMappingURL=team.js.map
@@ -49,4 +49,3 @@ export class TerminalHost {
49
49
  catch { }
50
50
  }
51
51
  }
52
- //# sourceMappingURL=terminal-host.js.map
package/dist/threads.js CHANGED
@@ -188,4 +188,3 @@ export function listThreadSummaries(participantId) {
188
188
  };
189
189
  });
190
190
  }
191
- //# sourceMappingURL=threads.js.map
@@ -249,4 +249,3 @@ export function renderTmuxRepairResult(result) {
249
249
  ` dashboard target: ${result.dashboardSessionName}:${result.dashboardWindowId}`,
250
250
  ].join("\n");
251
251
  }
252
- //# sourceMappingURL=doctor.js.map
@@ -286,4 +286,3 @@ export async function runTmuxInboxPopup(options) {
286
286
  process.stdin.on("data", onData);
287
287
  });
288
288
  }
289
- //# sourceMappingURL=inbox-popup.js.map
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
  import { loadConfig } from "../config.js";
8
8
  import { debug, log } from "../debug.js";
9
9
  import { getProjectStateDirFor } from "../paths.js";
10
+ import { getRuntimeOwnerId, TMUX_RUNTIME_OWNER_OPTION } from "../runtime-owner.js";
10
11
  export function isDashboardWindowName(name) {
11
12
  return name === "dashboard" || name.startsWith("dashboard-");
12
13
  }
@@ -677,6 +678,7 @@ export class TmuxRuntimeManager {
677
678
  const statuslineCommand = this.getStatuslineCommandSpec();
678
679
  const projectStateDir = getProjectStateDirFor(projectRoot);
679
680
  this.exec(["set-option", "-t", sessionName, "@aimux-project-root", projectRoot]);
681
+ this.exec(["set-option", "-t", sessionName, TMUX_RUNTIME_OWNER_OPTION, getRuntimeOwnerId()]);
680
682
  this.exec(["set-option", "-t", sessionName, "prefix", MANAGED_TMUX_SESSION_OPTIONS.prefix]);
681
683
  this.exec(["set-option", "-t", sessionName, "prefix2", MANAGED_TMUX_SESSION_OPTIONS.prefix2]);
682
684
  this.exec(["set-option", "-t", sessionName, "mouse", MANAGED_TMUX_SESSION_OPTIONS.mouse]);
@@ -970,4 +972,3 @@ export class TmuxRuntimeManager {
970
972
  function shellQuote(value) {
971
973
  return `'${value.replace(/'/g, `'"'"'`)}'`;
972
974
  }
973
- //# sourceMappingURL=runtime-manager.js.map
@@ -119,4 +119,3 @@ export class TmuxSessionTransport {
119
119
  listener(code);
120
120
  }
121
121
  }
122
- //# sourceMappingURL=session-transport.js.map
@@ -25,4 +25,3 @@ export function invalidateTmuxStatuslineArtifacts(projectRoot) {
25
25
  catch { }
26
26
  }
27
27
  }
28
- //# sourceMappingURL=statusline-cache.js.map
@@ -252,4 +252,3 @@ export function renderTmuxStatuslineFromData(data, projectRoot, line, options =
252
252
  export function renderTmuxStatusline(projectRoot, line, options = {}) {
253
253
  return renderTmuxStatuslineFromData(loadStatusline(projectRoot), projectRoot, line, options);
254
254
  }
255
- //# sourceMappingURL=statusline.js.map
@@ -112,4 +112,3 @@ export async function runTmuxSwitcher(options) {
112
112
  process.stdin.on("data", onData);
113
113
  });
114
114
  }
115
- //# sourceMappingURL=switcher.js.map
@@ -67,4 +67,3 @@ export function openManagedServiceWindow(tmux, projectRoot, serviceId) {
67
67
  selectLinkedOrOpenTarget(tmux, match.target);
68
68
  return match.target;
69
69
  }
70
- //# sourceMappingURL=window-open.js.map
@@ -82,4 +82,3 @@ export function classifyToolPane(tool, text) {
82
82
  const promptVisible = !updatePromptVisible && tracksPromptReadiness(tool) && hasToolInputPrompt(tool, text, lastLine);
83
83
  return { promptVisible, errorVisible, interruptedVisible, updatePromptVisible, blockedMessage };
84
84
  }
85
- //# sourceMappingURL=tool-output-watchers.js.map
@@ -36,4 +36,3 @@ export function renderOverlayBox(lines, cols, rows, style = "blue") {
36
36
  output += "\x1b8";
37
37
  return output;
38
38
  }
39
- //# sourceMappingURL=box.js.map
@@ -89,4 +89,3 @@ export function composeTwoPane(left, right, cols) {
89
89
  }
90
90
  return out;
91
91
  }
92
- //# sourceMappingURL=text.js.map
@@ -422,12 +422,12 @@ export function renderDashboardFrame(state, cols, rows) {
422
422
  lines.push("");
423
423
  return lines.slice(0, height);
424
424
  };
425
- const header = [
426
- "",
427
- centerInBlock(`\x1b[1maimux\x1b[0m agent multiplexer${state.runtimeLabel ? ` \x1b[32m● ${state.runtimeLabel}\x1b[0m` : ""}`),
428
- "─".repeat(Math.max(0, cols)),
429
- "",
430
- ];
425
+ const devBadge = state.isDevRuntime ? "\x1b[1;30;43m DEV \x1b[0m " : "";
426
+ const title = `${devBadge}\x1b[1maimux\x1b[0m — agent multiplexer${state.runtimeLabel ? ` \x1b[32m● ${state.runtimeLabel}\x1b[0m` : ""}`;
427
+ const divider = state.isDevRuntime
428
+ ? `\x1b[33m${"─".repeat(Math.max(0, cols))}\x1b[0m`
429
+ : "".repeat(Math.max(0, cols));
430
+ const header = ["", centerInBlock(title), divider, ""];
431
431
  const content = [];
432
432
  const operationFailures = state.operationFailures ?? [];
433
433
  if (operationFailures.length > 0) {
@@ -491,4 +491,3 @@ export function renderDashboardFrame(state, cols, rows) {
491
491
  scrollOffset,
492
492
  };
493
493
  }
494
- //# sourceMappingURL=dashboard-renderers.js.map
@@ -348,4 +348,3 @@ export function renderMigratePickerOverlay(ctx) {
348
348
  if (output)
349
349
  process.stdout.write(output);
350
350
  }
351
- //# sourceMappingURL=overlay-renderers.js.map
@@ -524,4 +524,3 @@ export function renderPlanDetails(ctx, width, height) {
524
524
  lines.push("");
525
525
  return lines.slice(0, height);
526
526
  }
527
- //# sourceMappingURL=subscreen-renderers.js.map
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterAll } from "vitest";
5
+ const aimuxTestHome = mkdtempSync(join(tmpdir(), "aimux-vitest-home-"));
6
+ process.env.AIMUX_HOME = aimuxTestHome;
7
+ afterAll(() => {
8
+ rmSync(aimuxTestHome, { recursive: true, force: true });
9
+ });
package/dist/workflow.js CHANGED
@@ -108,4 +108,3 @@ export function describeWorkflowNextAction(entry, currentParticipant = "user") {
108
108
  return "inspect blocked thread";
109
109
  return "open thread";
110
110
  }
111
- //# sourceMappingURL=workflow.js.map
package/dist/worktree.js CHANGED
@@ -180,4 +180,3 @@ export function createWorktree(name, cwd) {
180
180
  });
181
181
  return targetPath;
182
182
  }
183
- //# sourceMappingURL=worktree.js.map