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
package/dist/config.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { getGlobalAimuxDir, getGlobalConfigPath, getLocalAimuxDir, getConfigPath, getProjectStateDir, } from "./paths.js";
4
+ import { quarantineCorruptFile, writeJsonAtomic } from "./atomic-write.js";
4
5
  const DEFAULT_CONFIG = {
5
6
  defaultTool: "claude",
6
7
  contextMaxEntries: 20,
@@ -103,7 +104,9 @@ export function loadConfig(opts = {}) {
103
104
  const globalRaw = JSON.parse(readFileSync(globalPath, "utf-8"));
104
105
  config = deepMerge(config, globalRaw);
105
106
  }
106
- catch { }
107
+ catch {
108
+ quarantineCorruptFile(globalPath);
109
+ }
107
110
  }
108
111
  // Layer 2: project config
109
112
  const projectPath = getConfigPath();
@@ -112,7 +115,9 @@ export function loadConfig(opts = {}) {
112
115
  const projectRaw = JSON.parse(readFileSync(projectPath, "utf-8"));
113
116
  config = deepMerge(config, projectRaw);
114
117
  }
115
- catch { }
118
+ catch {
119
+ quarantineCorruptFile(projectPath);
120
+ }
116
121
  }
117
122
  return normalizeConfig(config);
118
123
  }
@@ -122,7 +127,7 @@ export function saveConfig(config) {
122
127
  if (!existsSync(dir)) {
123
128
  mkdirSync(dir, { recursive: true });
124
129
  }
125
- writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + "\n");
130
+ writeJsonAtomic(getConfigPath(), config);
126
131
  }
127
132
  /** Save config to global ~/.aimux/config.json */
128
133
  export function saveGlobalConfig(config) {
@@ -130,7 +135,7 @@ export function saveGlobalConfig(config) {
130
135
  if (!existsSync(dir)) {
131
136
  mkdirSync(dir, { recursive: true });
132
137
  }
133
- writeFileSync(getGlobalConfigPath(), JSON.stringify(config, null, 2) + "\n");
138
+ writeJsonAtomic(getGlobalConfigPath(), config);
134
139
  }
135
140
  const GITIGNORE_CONTENTS = `# Runtime-private service/project state (lives in ~/.aimux/projects/)
136
141
  state.json
@@ -192,4 +197,3 @@ function deepMerge(a, b) {
192
197
  }
193
198
  return result;
194
199
  }
195
- //# sourceMappingURL=config.js.map
@@ -1,6 +1,10 @@
1
+ import { homedir } from "node:os";
2
+ import { join, resolve } from "node:path";
1
3
  const PROD_WEB_APP_URL = "https://aimux.app";
2
4
  const PROD_RELAY_URL = "wss://relay.aimux.app";
3
5
  const DEV_WEB_APP_URL = "http://localhost:8081";
6
+ const DEV_HOME = join(homedir(), ".aimux-dev");
7
+ const DEV_DAEMON_PORT = "43191";
4
8
  function cleanUrl(value) {
5
9
  return value.replace(/\/$/, "");
6
10
  }
@@ -8,8 +12,23 @@ function optionalEnv(value) {
8
12
  const trimmed = value?.trim();
9
13
  return trimmed ? trimmed : undefined;
10
14
  }
15
+ function normalizeHome(value) {
16
+ const trimmed = value?.trim();
17
+ if (!trimmed)
18
+ return null;
19
+ if (trimmed === "~")
20
+ return homedir();
21
+ if (trimmed.startsWith("~/"))
22
+ return resolve(homedir(), trimmed.slice(2));
23
+ return resolve(trimmed);
24
+ }
25
+ // Mirrors launcher-env's lane detection: any one dev-lane signal marks the
26
+ // runtime as development, so the label survives a single var being unset.
11
27
  export function isDevelopmentRuntime() {
12
- return process.env.AIMUX_ENV === "development";
28
+ return (process.env.AIMUX_ENV?.trim() === "development" ||
29
+ normalizeHome(process.env.AIMUX_HOME) === DEV_HOME ||
30
+ process.env.AIMUX_DAEMON_PORT?.trim() === DEV_DAEMON_PORT ||
31
+ optionalEnv(process.env.AIMUX_WEB_APP_URL) === DEV_WEB_APP_URL);
13
32
  }
14
33
  export function resolveWebAppUrl(override) {
15
34
  const value = optionalEnv(override) ??
@@ -25,4 +44,3 @@ export const CONNECTION_TARGET_DEFAULTS = {
25
44
  prodRelayUrl: PROD_RELAY_URL,
26
45
  devWebAppUrl: DEV_WEB_APP_URL,
27
46
  };
28
- //# sourceMappingURL=connection-targets.js.map
@@ -209,4 +209,3 @@ function truncate(text, max) {
209
209
  return text;
210
210
  return text.slice(0, max) + "...";
211
211
  }
212
- //# sourceMappingURL=compactor.js.map
@@ -9,6 +9,7 @@ import { algorithmicCompact } from "./compactor.js";
9
9
  import { debugTurn, debugGit, debugContext, debugCompact } from "../debug.js";
10
10
  import { TmuxRuntimeManager } from "../tmux/runtime-manager.js";
11
11
  import { classifyToolPane } from "../tool-output-watchers.js";
12
+ import { parseAgentOutput } from "../agent-output-parser.js";
12
13
  const git = simpleGit();
13
14
  const MAX_LIVE_MD_BYTES = 50 * 1024;
14
15
  const MAX_LIVE_MD_LINES = 200;
@@ -243,7 +244,9 @@ export class ContextWatcher {
243
244
  debugContext("wrote", `${session.id}/live.md`, snapshot.length);
244
245
  const turns = readHistory(session.id, { lastN: 1 });
245
246
  if (promptVisible) {
246
- const snapshotTurn = normalized.split("\n").slice(-80).join("\n").trim();
247
+ const blocks = parseAgentOutput(normalized, { tool: session.command }).blocks;
248
+ const lastResponseBlock = blocks.filter((block) => block.type === "response").at(-1);
249
+ const snapshotTurn = lastResponseBlock?.text.trim() ?? "";
247
250
  if (snapshotTurn) {
248
251
  const lastTurn = turns.at(-1);
249
252
  if (!lastTurn || lastTurn.content !== snapshotTurn) {
@@ -475,4 +478,3 @@ function truncate(text, max) {
475
478
  return text;
476
479
  return text.slice(0, max) + "...";
477
480
  }
478
- //# sourceMappingURL=context-bridge.js.map
@@ -90,4 +90,3 @@ export function readContext(maxEntries = 20) {
90
90
  return "";
91
91
  return selected.join("");
92
92
  }
93
- //# sourceMappingURL=context-file.js.map
@@ -105,4 +105,3 @@ export function readAllHistories(sessionIds, opts) {
105
105
  all.sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));
106
106
  return all;
107
107
  }
108
- //# sourceMappingURL=history.js.map
@@ -3,8 +3,8 @@
3
3
  // Populated by `aimux login` (browser flow). The daemon reads this to connect
4
4
  // to the relay. The token is a long-lived relay-signed JWT — not a Clerk
5
5
  // session token — so it survives daemon restarts and runs for ~90 days.
6
- import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, chmodSync } from "node:fs";
7
- import { dirname } from "node:path";
6
+ import { existsSync, readFileSync, rmSync } from "node:fs";
7
+ import { atomicWrite } from "./atomic-write.js";
8
8
  import { getAuthPath } from "./paths.js";
9
9
  export function loadCredentials() {
10
10
  const path = getAuthPath();
@@ -21,10 +21,7 @@ export function loadCredentials() {
21
21
  }
22
22
  }
23
23
  export function saveCredentials(creds) {
24
- const path = getAuthPath();
25
- mkdirSync(dirname(path), { recursive: true });
26
- writeFileSync(path, `${JSON.stringify(creds, null, 2)}\n`, { mode: 0o600 });
27
- chmodSync(path, 0o600);
24
+ atomicWrite(getAuthPath(), `${JSON.stringify(creds, null, 2)}\n`, { mode: 0o600 });
28
25
  }
29
26
  export function clearCredentials() {
30
27
  const path = getAuthPath();
@@ -46,4 +43,3 @@ export function setRemoteEnabled(enabled) {
46
43
  saveCredentials(next);
47
44
  return next;
48
45
  }
49
- //# sourceMappingURL=credentials.js.map
package/dist/daemon.d.ts CHANGED
@@ -30,6 +30,7 @@ export declare function projectServiceStatus(projectRoot: string): Promise<Proje
30
30
  export declare class AimuxDaemon {
31
31
  private server;
32
32
  private relayClient;
33
+ private readonly pushThrottle;
33
34
  private readonly children;
34
35
  private readonly projectEnsurePromises;
35
36
  private state;
package/dist/daemon.js CHANGED
@@ -9,6 +9,7 @@ import { loadMetadataEndpoint } from "./metadata-store.js";
9
9
  import { requestJson } from "./http-client.js";
10
10
  import { getLoggingConfig, log } from "./debug.js";
11
11
  import { RelayClient } from "./relay-client.js";
12
+ import { MobilePushThrottle } from "./mobile-push-throttle.js";
12
13
  import { loadCredentials, setRemoteEnabled } from "./credentials.js";
13
14
  import { assertRemoteAccessAllowed, parseRemoteActor } from "./remote-access.js";
14
15
  const DEFAULT_DAEMON_PORT = 43190;
@@ -328,6 +329,7 @@ export async function projectServiceStatus(projectRoot) {
328
329
  export class AimuxDaemon {
329
330
  server = null;
330
331
  relayClient = null;
332
+ pushThrottle = new MobilePushThrottle();
331
333
  children = new Map();
332
334
  projectEnsurePromises = new Map();
333
335
  state = loadDaemonState();
@@ -663,6 +665,20 @@ export class AimuxDaemon {
663
665
  if (method === "POST" && pathname === "/relay/disable") {
664
666
  return { status: 200, body: { ok: true, relay: this.disableRelay() } };
665
667
  }
668
+ if (method === "POST" && pathname === "/internal/push") {
669
+ if (actor)
670
+ return { status: 403, body: { ok: false, error: "internal route is loopback-only" } };
671
+ const payload = body;
672
+ if (!payload?.title)
673
+ return { status: 400, body: { ok: false, error: "title is required" } };
674
+ if (this.relayClient?.getStatus().status !== "connected") {
675
+ return { status: 200, body: { ok: true, suppressed: true, reason: "relay_unavailable" } };
676
+ }
677
+ if (!this.pushThrottle.allow(payload))
678
+ return { status: 200, body: { ok: true, suppressed: true } };
679
+ this.relayClient.pushNotification(payload);
680
+ return { status: 200, body: { ok: true } };
681
+ }
666
682
  if (method === "GET" && pathname === "/projects") {
667
683
  const liveById = this.state.projects;
668
684
  const projects = listDesktopProjects().map((project) => ({
@@ -737,4 +753,3 @@ export class AimuxDaemon {
737
753
  send(res, result.status, result.body);
738
754
  }
739
755
  }
740
- //# sourceMappingURL=daemon.js.map
@@ -124,4 +124,3 @@ export function getDashboardCommandSpec(projectRoot) {
124
124
  },
125
125
  };
126
126
  }
127
- //# sourceMappingURL=command-spec.js.map
@@ -110,4 +110,3 @@ export class DashboardFeedbackController {
110
110
  }
111
111
  }
112
112
  }
113
- //# sourceMappingURL=feedback.js.map
@@ -128,6 +128,7 @@ export interface DashboardViewModel {
128
128
  selectedServiceId?: string;
129
129
  selectedTeammates: DashboardSession[];
130
130
  runtimeLabel?: string;
131
+ isDevRuntime?: boolean;
131
132
  mainCheckout: MainCheckoutInfo;
132
133
  worktreeRemoval?: DashboardWorktreeRemovalInfo;
133
134
  operationFailures: DashboardOperationFailure[];
@@ -15,6 +15,7 @@ export class Dashboard {
15
15
  selectedServiceId: undefined,
16
16
  selectedTeammates: [],
17
17
  runtimeLabel: undefined,
18
+ isDevRuntime: false,
18
19
  mainCheckout: { name: "Main Checkout", branch: "" },
19
20
  worktreeRemoval: undefined,
20
21
  operationFailures: [],
@@ -46,4 +47,3 @@ export class Dashboard {
46
47
  this.viewModel.detailsPaneVisible = this.detailsPaneVisible;
47
48
  }
48
49
  }
49
- //# sourceMappingURL=index.js.map
@@ -83,4 +83,3 @@ export function clearDashboardOperationFailures(match) {
83
83
  saveState(state);
84
84
  return changed;
85
85
  }
86
- //# sourceMappingURL=operation-failures.js.map
@@ -52,4 +52,3 @@ export function orderDashboardSessionsForWorktree(sessions, worktreePath, orderS
52
52
  export function orderDashboardServicesForWorktree(services, worktreePath, orderState) {
53
53
  return applyDashboardOrder(services, orderState.serviceOrderByWorktreeKey[dashboardOrderKey(worktreePath)]);
54
54
  }
55
- //# sourceMappingURL=order.js.map
@@ -276,4 +276,3 @@ export class DashboardPendingActions {
276
276
  })();
277
277
  }
278
278
  }
279
- //# sourceMappingURL=pending-actions.js.map
@@ -143,4 +143,3 @@ export function resolveDashboardQuickJumpTarget(worktrees, digits) {
143
143
  }
144
144
  return { kind: "entry", worktree, entry: worktree.entries[entryIndex], entryIndex };
145
145
  }
146
- //# sourceMappingURL=quick-jump.js.map
@@ -19,4 +19,3 @@ export function isLiveDashboardServiceRuntimeEntry(entry) {
19
19
  return true;
20
20
  return hasRuntimeEvidence(entry);
21
21
  }
22
- //# sourceMappingURL=runtime-evidence.js.map
@@ -81,4 +81,3 @@ export async function resumeOfflineSessionWithFeedback(deps, session) {
81
81
  deps.renderDashboard();
82
82
  }
83
83
  }
84
- //# sourceMappingURL=session-actions.js.map
@@ -144,4 +144,3 @@ function resolveWorktreeInfo(worktreePath) {
144
144
  return undefined;
145
145
  return { name, branch: "unknown" };
146
146
  }
147
- //# sourceMappingURL=session-registry.js.map
@@ -11,4 +11,3 @@ export function dashboardCreatedSortKey(entry) {
11
11
  export function sortDashboardEntriesByCreatedAt(entries) {
12
12
  return [...entries].sort((a, b) => dashboardCreatedSortKey(b) - dashboardCreatedSortKey(a));
13
13
  }
14
- //# sourceMappingURL=sort.js.map
@@ -56,4 +56,3 @@ export class DashboardOverlayState {
56
56
  this.version += 1;
57
57
  }
58
58
  }
59
- //# sourceMappingURL=state.js.map
@@ -1,12 +1,18 @@
1
1
  import { isDashboardWindowName } from "../tmux/runtime-manager.js";
2
2
  import { getDashboardCommandSpec } from "./command-spec.js";
3
+ import { getRuntimeOwnerId, TMUX_DASHBOARD_OWNER_OPTION, TMUX_RUNTIME_OWNER_OPTION } from "../runtime-owner.js";
3
4
  function isUsableDashboardTarget(tmux, projectRoot, dashboardBuildStamp, dashboardTarget) {
4
5
  const currentBuildStamp = tmux.getWindowOption(dashboardTarget, "@aimux-dashboard-build");
6
+ const currentOwner = getRuntimeOwnerId();
7
+ const targetRuntimeOwner = tmux.getSessionOption(dashboardTarget.sessionName, TMUX_RUNTIME_OWNER_OPTION);
8
+ const targetDashboardOwner = tmux.getWindowOption(dashboardTarget, TMUX_DASHBOARD_OWNER_OPTION);
5
9
  const targetProjectRoot = tmux.getSessionOption(dashboardTarget.sessionName, "@aimux-project-root");
6
10
  const paneCommand = tmux.displayMessage("#{pane_current_command}", dashboardTarget.windowId);
7
11
  const paneTail = paneCommand === "bash" ? tmux.captureTarget(dashboardTarget, { startLine: -40 }) : "";
8
12
  return (tmux.isWindowAlive(dashboardTarget) &&
9
13
  targetProjectRoot === projectRoot &&
14
+ targetRuntimeOwner === currentOwner &&
15
+ targetDashboardOwner === currentOwner &&
10
16
  currentBuildStamp === dashboardBuildStamp &&
11
17
  paneCommand !== "cat" &&
12
18
  paneCommand !== "tail" &&
@@ -110,11 +116,17 @@ export function resolveDashboardTarget(projectRoot, tmux, options = {}) {
110
116
  const openSessionName = tmux.getOpenSessionName(dashboardSession.sessionName, tmux.isInsideTmux());
111
117
  const dashboardTarget = tmux.ensureDashboardWindow(openSessionName, projectRoot, dashboardCommand);
112
118
  const currentBuildStamp = tmux.getWindowOption(dashboardTarget, "@aimux-dashboard-build");
113
- const shouldRespawn = options.forceReload === true || !tmux.isWindowAlive(dashboardTarget) || currentBuildStamp !== dashboardBuildStamp;
119
+ const currentOwner = getRuntimeOwnerId();
120
+ const currentDashboardOwner = tmux.getWindowOption(dashboardTarget, TMUX_DASHBOARD_OWNER_OPTION);
121
+ const shouldRespawn = options.forceReload === true ||
122
+ !tmux.isWindowAlive(dashboardTarget) ||
123
+ currentBuildStamp !== dashboardBuildStamp ||
124
+ currentDashboardOwner !== currentOwner;
114
125
  if (shouldRespawn) {
115
126
  tmux.respawnWindow(dashboardTarget, dashboardCommand);
116
- tmux.setWindowOption(dashboardTarget, "@aimux-dashboard-build", dashboardBuildStamp);
117
127
  }
128
+ tmux.setWindowOption(dashboardTarget, "@aimux-dashboard-build", dashboardBuildStamp);
129
+ tmux.setWindowOption(dashboardTarget, TMUX_DASHBOARD_OWNER_OPTION, currentOwner);
118
130
  return { dashboardSession, dashboardTarget };
119
131
  }
120
132
  export function openDashboardTarget(projectRoot, tmux, options = {}) {
@@ -125,4 +137,3 @@ export function openDashboardTarget(projectRoot, tmux, options = {}) {
125
137
  });
126
138
  return resolved;
127
139
  }
128
- //# sourceMappingURL=targets.js.map
@@ -1,4 +1,5 @@
1
- import { readFileSync, writeFileSync } from "node:fs";
1
+ import { readFileSync } from "node:fs";
2
+ import { writeJsonAtomic } from "../atomic-write.js";
2
3
  import { dashboardOrderKey, moveDashboardOrder, orderDashboardServicesForWorktree, orderDashboardSessionsForWorktree, orderDashboardWorktreeGroups, } from "./order.js";
3
4
  import { getDashboardClientUiStatePath, getDashboardUiStatePath } from "../paths.js";
4
5
  export class DashboardUiStateStore {
@@ -89,8 +90,8 @@ export class DashboardUiStateStore {
89
90
  this.flatSessionId = flatSession.id;
90
91
  }
91
92
  try {
92
- writeFileSync(getDashboardUiStatePath(), JSON.stringify(sharedSnapshot, null, 2) + "\n");
93
- writeFileSync(getDashboardClientUiStatePath(clientKey), JSON.stringify(clientSnapshot, null, 2) + "\n");
93
+ writeJsonAtomic(getDashboardUiStatePath(), sharedSnapshot);
94
+ writeJsonAtomic(getDashboardClientUiStatePath(clientKey), clientSnapshot);
94
95
  }
95
96
  catch { }
96
97
  }
@@ -183,4 +184,3 @@ function sanitizeOrderMap(value) {
183
184
  function hasOrderEntries(value) {
184
185
  return Object.values(value).some((ids) => ids.length > 0);
185
186
  }
186
- //# sourceMappingURL=ui-state-store.js.map
@@ -538,4 +538,3 @@ export function buildDebugStateReport(options) {
538
538
  export function renderDebugStateReport(report) {
539
539
  return JSON.stringify(report, null, 2);
540
540
  }
541
- //# sourceMappingURL=debug-state.js.map
package/dist/debug.js CHANGED
@@ -242,4 +242,3 @@ export function debugGit(filesChanged, diffLen) {
242
242
  export function closeDebug() {
243
243
  log.info("--- aimux session ended ---", "lifecycle");
244
244
  }
245
- //# sourceMappingURL=debug.js.map
@@ -259,4 +259,3 @@ export function createGithubPrContextPlugin(api) {
259
259
  },
260
260
  };
261
261
  }
262
- //# sourceMappingURL=gh-pr-context.js.map
@@ -104,4 +104,3 @@ export default function createConfiguredTranscriptLengthPlugin(api) {
104
104
  line: pluginConfig.line ?? "top",
105
105
  });
106
106
  }
107
- //# sourceMappingURL=transcript-length.js.map
@@ -188,4 +188,3 @@ export function listSwitchableAgentMenuItems(context, tmux = new TmuxRuntimeMana
188
188
  return a.target.windowIndex - b.target.windowIndex;
189
189
  });
190
190
  }
191
- //# sourceMappingURL=fast-control.js.map
package/dist/hotkeys.js CHANGED
@@ -118,4 +118,3 @@ export class HotkeyHandler {
118
118
  this.clearTimeout();
119
119
  }
120
120
  }
121
- //# sourceMappingURL=hotkeys.js.map
@@ -81,4 +81,3 @@ export async function requestJson(urlString, options = {}) {
81
81
  req.end();
82
82
  });
83
83
  }
84
- //# sourceMappingURL=http-client.js.map
@@ -276,4 +276,3 @@ export function matchKey(event, descriptor) {
276
276
  event.alt === wantAlt &&
277
277
  (event.name === key || event.char === key));
278
278
  }
279
- //# sourceMappingURL=key-parser.js.map
package/dist/last-used.js CHANGED
@@ -1,7 +1,8 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
2
2
  import { getProjectStateDirFor } from "./paths.js";
3
3
  import { join } from "node:path";
4
4
  import { parseRecencyTimestamp } from "./recency.js";
5
+ import { writeJsonAtomic } from "./atomic-write.js";
5
6
  const LAST_USED_VERSION = 1;
6
7
  const MAX_RECENT_IDS = 64;
7
8
  const EMPTY_STATE = {
@@ -66,7 +67,7 @@ export function compareLastUsed(left, right, rankMap) {
66
67
  function persistLastUsedState(projectRoot, state) {
67
68
  const dir = getProjectStateDirFor(projectRoot);
68
69
  mkdirSync(dir, { recursive: true });
69
- writeFileSync(getLastUsedPath(projectRoot), JSON.stringify(state, null, 2));
70
+ writeJsonAtomic(getLastUsedPath(projectRoot), state);
70
71
  }
71
72
  function normalizeLastUsedState(state) {
72
73
  const items = Object.fromEntries(Object.entries(state.items ?? {}).flatMap(([itemId, value]) => typeof value?.lastUsedAt === "string" ? [[itemId, { lastUsedAt: value.lastUsedAt }]] : []));
@@ -90,4 +91,3 @@ function normalizeLastUsedState(state) {
90
91
  function pushRecentId(ids, itemId) {
91
92
  return [itemId, ...ids.filter((entry) => entry !== itemId)].slice(0, MAX_RECENT_IDS);
92
93
  }
93
- //# sourceMappingURL=last-used.js.map
@@ -0,0 +1,4 @@
1
+ type MutableEnv = Record<string, string | undefined>;
2
+ export declare function prepareStableCliEnv(env?: MutableEnv): void;
3
+ export declare function prepareDevCliEnv(env?: MutableEnv): void;
4
+ export {};
@@ -0,0 +1,70 @@
1
+ import { homedir } from "node:os";
2
+ import { resolve, join } from "node:path";
3
+ const PROD_HOME = join(homedir(), ".aimux");
4
+ const DEV_HOME = join(homedir(), ".aimux-dev");
5
+ const PROD_DAEMON_PORT = "43190";
6
+ const DEV_DAEMON_PORT = "43191";
7
+ const PROD_WEB_APP_URL = "https://aimux.app";
8
+ const DEV_WEB_APP_URL = "http://localhost:8081";
9
+ function normalizeHome(value) {
10
+ const trimmed = value?.trim();
11
+ if (!trimmed)
12
+ return null;
13
+ if (trimmed === "~")
14
+ return homedir();
15
+ if (trimmed.startsWith("~/"))
16
+ return resolve(homedir(), trimmed.slice(2));
17
+ return resolve(trimmed);
18
+ }
19
+ function sameHome(value, expected) {
20
+ const normalized = normalizeHome(value);
21
+ return normalized !== null && normalized === expected;
22
+ }
23
+ function sameValue(value, expected) {
24
+ return value?.trim() === expected;
25
+ }
26
+ function blank(value) {
27
+ return !value?.trim();
28
+ }
29
+ function clearSessionScopedEnv(env) {
30
+ delete env.AIMUX_METADATA_ENDPOINT_FILE;
31
+ delete env.AIMUX_SESSION_ID;
32
+ delete env.AIMUX_SHELL_INTEGRATION_SCRIPT;
33
+ delete env.AIMUX_TOOL;
34
+ }
35
+ export function prepareStableCliEnv(env = process.env) {
36
+ const inheritedDevTarget = sameHome(env.AIMUX_HOME, DEV_HOME) ||
37
+ sameValue(env.AIMUX_DAEMON_PORT, DEV_DAEMON_PORT) ||
38
+ sameValue(env.AIMUX_ENV, "development") ||
39
+ sameValue(env.AIMUX_WEB_APP_URL, DEV_WEB_APP_URL);
40
+ if (blank(env.AIMUX_HOME) || sameHome(env.AIMUX_HOME, DEV_HOME))
41
+ env.AIMUX_HOME = PROD_HOME;
42
+ if (blank(env.AIMUX_DAEMON_PORT) || sameValue(env.AIMUX_DAEMON_PORT, DEV_DAEMON_PORT)) {
43
+ env.AIMUX_DAEMON_PORT = PROD_DAEMON_PORT;
44
+ }
45
+ if (blank(env.AIMUX_ENV) || sameValue(env.AIMUX_ENV, "development"))
46
+ env.AIMUX_ENV = "production";
47
+ if (blank(env.AIMUX_WEB_APP_URL) || sameValue(env.AIMUX_WEB_APP_URL, DEV_WEB_APP_URL)) {
48
+ env.AIMUX_WEB_APP_URL = PROD_WEB_APP_URL;
49
+ }
50
+ if (inheritedDevTarget)
51
+ clearSessionScopedEnv(env);
52
+ }
53
+ export function prepareDevCliEnv(env = process.env) {
54
+ const inheritedStableTarget = sameHome(env.AIMUX_HOME, PROD_HOME) ||
55
+ sameValue(env.AIMUX_DAEMON_PORT, PROD_DAEMON_PORT) ||
56
+ sameValue(env.AIMUX_ENV, "production") ||
57
+ sameValue(env.AIMUX_WEB_APP_URL, PROD_WEB_APP_URL);
58
+ if (blank(env.AIMUX_HOME) || sameHome(env.AIMUX_HOME, PROD_HOME))
59
+ env.AIMUX_HOME = DEV_HOME;
60
+ if (blank(env.AIMUX_DAEMON_PORT) || sameValue(env.AIMUX_DAEMON_PORT, PROD_DAEMON_PORT)) {
61
+ env.AIMUX_DAEMON_PORT = DEV_DAEMON_PORT;
62
+ }
63
+ if (blank(env.AIMUX_ENV) || sameValue(env.AIMUX_ENV, "production"))
64
+ env.AIMUX_ENV = "development";
65
+ if (blank(env.AIMUX_WEB_APP_URL) || sameValue(env.AIMUX_WEB_APP_URL, PROD_WEB_APP_URL)) {
66
+ env.AIMUX_WEB_APP_URL = DEV_WEB_APP_URL;
67
+ }
68
+ if (inheritedStableTarget)
69
+ clearSessionScopedEnv(env);
70
+ }
@@ -183,4 +183,3 @@ export function openUrlInBrowser(url) {
183
183
  });
184
184
  child.unref();
185
185
  }
186
- //# sourceMappingURL=local-ui-server.js.map
@@ -117,4 +117,3 @@ export async function runLoginFlow(opts = {}) {
117
117
  function escapeHtml(s) {
118
118
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
119
119
  }
120
- //# sourceMappingURL=login-flow.js.map
package/dist/main.js CHANGED
@@ -20,6 +20,7 @@ import { createThread, listThreadSummaries, markThreadSeen, readMessages, readTh
20
20
  import { sendDirectMessage, sendThreadMessage } from "./orchestration.js";
21
21
  import { runLoginFlow } from "./login-flow.js";
22
22
  import { clearCredentials, loadCredentials, setRemoteEnabled } from "./credentials.js";
23
+ import { takeOverProjectFromOtherOwners } from "./project-takeover.js";
23
24
  import { acceptHandoff, approveReview, acceptTask, assignTask, blockTask, completeHandoff, completeTask, reopenTask, requestTaskChanges, sendHandoff, } from "./orchestration-actions.js";
24
25
  import { readAllTasks, readTask } from "./tasks.js";
25
26
  import { clearNotifications, listNotifications, markNotificationsRead, upsertNotification, unreadNotificationCount, } from "./notifications.js";
@@ -37,6 +38,7 @@ import { persistProjectRuntimeSnapshotsBeforeTmuxStop } from "./multiplexer/serv
37
38
  import { configureLogging, log, resolveLoggingRuntimeConfig } from "./debug.js";
38
39
  import { createRuntimeTopologyStore } from "./runtime-core/topology-store.js";
39
40
  import { listTopologySessionStates } from "./runtime-core/topology-sessions.js";
41
+ import { reconcileOfflineBackendSessionIds } from "./runtime-core/backend-id-reconcile.js";
40
42
  import { listTopologyWorktreeGraveyard, listTopologyWorktreeGraveyardPaths, } from "./runtime-core/topology-worktrees.js";
41
43
  import { buildRuntimeMigrationReport, importRuntimeMigration, renderRuntimeMigrationImportResult, renderRuntimeMigrationReport, renderRuntimeMigrationRollbackResult, rollbackRuntimeMigration, } from "./runtime-migration.js";
42
44
  import { DEFAULT_LOCAL_UI_HOST, DEFAULT_LOCAL_UI_PORT, openUrlInBrowser, startLocalUiServer, } from "./local-ui-server.js";
@@ -561,6 +563,8 @@ program
561
563
  const tmux = new TmuxRuntimeManager();
562
564
  ensureTmuxAvailable(tmux);
563
565
  if (!tool && !opts.resume && !opts.restore) {
566
+ await takeOverProjectFromOtherOwners(projectRoot);
567
+ await ensureDaemonProjectReady(projectRoot);
564
568
  const liveDashboard = findLiveDashboardTarget(projectRoot, tmux);
565
569
  if (liveDashboard) {
566
570
  tmux.openTarget(liveDashboard.dashboardTarget, {
@@ -2790,16 +2794,23 @@ repairCmd
2790
2794
  const tmux = new TmuxRuntimeManager();
2791
2795
  ensureTmuxAvailable(tmux);
2792
2796
  const result = repairTmuxRuntime(tmux, { projectRoot });
2797
+ const backendReconcile = reconcileOfflineBackendSessionIds(projectRoot);
2793
2798
  if (opts.open) {
2794
2799
  const { dashboardTarget } = resolveDashboardTarget(projectRoot, tmux);
2795
2800
  tmux.openTarget(dashboardTarget, { insideTmux: tmux.isInsideTmux(), alreadyResolved: true });
2796
2801
  exitAfterOpen();
2797
2802
  }
2798
2803
  if (opts.json) {
2799
- console.log(JSON.stringify(result, null, 2));
2804
+ console.log(JSON.stringify({ ...result, backendReconcile }, null, 2));
2800
2805
  return;
2801
2806
  }
2802
2807
  console.log(renderTmuxRepairResult(result));
2808
+ if (backendReconcile.reconciled.length > 0) {
2809
+ console.log(`Recovered backend session id for ${backendReconcile.reconciled.length} offline agent(s):`);
2810
+ for (const entry of backendReconcile.reconciled) {
2811
+ console.log(` ${entry.id} -> ${entry.backendSessionId}`);
2812
+ }
2813
+ }
2803
2814
  });
2804
2815
  const metadataCmd = program.command("metadata").description("Push metadata into aimux tmux status integration");
2805
2816
  const metadataTracker = new AgentTracker();
@@ -2927,6 +2938,10 @@ program
2927
2938
  const result = { ok: true, action, sessionId };
2928
2939
  if (payload.session_id) {
2929
2940
  result.backendSessionId = payload.session_id;
2941
+ // No local fallback: this hook is a short-lived CLI process, not the
2942
+ // runtime that owns the session, so it cannot record into topology
2943
+ // directly. A service-down capture gap is closed by reconcile-on-restart.
2944
+ await postLiveProjectServiceJsonOrLocal(projectRoot, "/agents/record-backend-session", { sessionId, backendSessionId: payload.session_id }, () => ({ ok: true })).catch(() => { });
2930
2945
  }
2931
2946
  const setActivity = async (activity) => postLiveProjectServiceJsonOrLocal(projectRoot, "/set-activity", { session: sessionId, activity }, () => metadataTracker.setActivity(sessionId, activity, projectRoot));
2932
2947
  const setAttention = async (attention) => postLiveProjectServiceJsonOrLocal(projectRoot, "/set-attention", { session: sessionId, attention }, () => metadataTracker.setAttention(sessionId, attention, projectRoot));
@@ -3292,4 +3307,3 @@ teamCmd
3292
3307
  }
3293
3308
  });
3294
3309
  program.parse();
3295
- //# sourceMappingURL=main.js.map
@@ -62,4 +62,3 @@ export function wrapCommandWithManagedLaunchEnv(opts) {
62
62
  args: [...envArgs, opts.command, ...opts.args],
63
63
  };
64
64
  }
65
- //# sourceMappingURL=managed-launch-env.js.map