tmux-ide 2.9.0-beta.21 → 2.9.0-beta.26

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 (209) hide show
  1. package/bin/cli.js +46791 -43589
  2. package/package.json +2 -2
  3. package/packages/contracts/src/__tests__/daemon-wire.test.ts +2 -2
  4. package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +35 -0
  5. package/packages/contracts/src/__tests__/issue-error.test.ts +1 -1
  6. package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +3 -1
  7. package/packages/contracts/src/__tests__/pane-stream.test.ts +17 -5
  8. package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +166 -0
  9. package/packages/contracts/src/__tests__/window-links.test.ts +243 -0
  10. package/packages/contracts/src/actions-contract.ts +12 -0
  11. package/packages/contracts/src/daemon-resource-request.test.ts +1 -1
  12. package/packages/contracts/src/daemon-wire.ts +10 -5
  13. package/packages/contracts/src/fleet-client-state.ts +3 -0
  14. package/packages/contracts/src/fleet-lifecycle.ts +5 -0
  15. package/packages/contracts/src/index.ts +2 -0
  16. package/packages/contracts/src/interaction-receipts.ts +39 -0
  17. package/packages/contracts/src/issue-error.ts +1 -1
  18. package/packages/contracts/src/multiplexer-verbs.ts +25 -1
  19. package/packages/contracts/src/pane-stream.ts +51 -8
  20. package/packages/contracts/src/tmux-server-scope.ts +172 -0
  21. package/packages/contracts/src/window-links.ts +76 -0
  22. package/packages/contracts/src/workspace-multiplexer.ts +54 -0
  23. package/packages/core/src/interaction-receipts.ts +4 -0
  24. package/packages/daemon/dist/command-center/actions/command-definitions.js +6 -0
  25. package/packages/daemon/dist/command-center/actions/handlers/workspace-multiplexer.js +2 -0
  26. package/packages/daemon/dist/command-center/actions/registry.js +13 -1
  27. package/packages/daemon/dist/command-center/discovery.js +2 -9
  28. package/packages/daemon/dist/command-center/server.js +21 -1
  29. package/packages/daemon/dist/command-center/tmux-server-native-backing.js +37 -0
  30. package/packages/daemon/dist/command-center/tmux-servers.js +351 -0
  31. package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +64 -8
  32. package/packages/daemon/dist/lib/canonical-daemon.js +5 -2
  33. package/packages/daemon/dist/lib/daemon-embed.js +198 -30
  34. package/packages/daemon/dist/lib/embedded-tmux-server-owners.js +147 -0
  35. package/packages/daemon/dist/lib/fleet-lifecycle-authority.js +31 -11
  36. package/packages/daemon/dist/lib/headless-daemon.js +10 -1
  37. package/packages/daemon/dist/lib/runtime-namespace.js +17 -3
  38. package/packages/daemon/dist/lib/tmux-client-execution.js +59 -0
  39. package/packages/daemon/dist/lib/tmux-named-socket-fence.js +4 -0
  40. package/packages/daemon/dist/lib/tmux-server-generation-runner.js +62 -0
  41. package/packages/daemon/dist/lib/tmux-server-owner.js +285 -0
  42. package/packages/daemon/dist/lib/tmux-server-owners.js +192 -0
  43. package/packages/daemon/dist/lib/tmux-server-proof.js +82 -0
  44. package/packages/daemon/dist/lib/tmux-server-registration.js +85 -0
  45. package/packages/daemon/dist/lib/tmux-server-session-create.js +43 -0
  46. package/packages/daemon/dist/lib/tmux-server-session-open.js +101 -0
  47. package/packages/daemon/dist/lib/tmux-servers-cli.js +134 -0
  48. package/packages/daemon/dist/lib/tmux-session-mutation-fence.js +55 -0
  49. package/packages/daemon/dist/lib/tmux-window-link-guard.js +88 -0
  50. package/packages/daemon/dist/lib/workspace-config-loader.js +3 -0
  51. package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +82 -1
  52. package/packages/daemon/dist/lib/workspace-pane-creation.js +13 -57
  53. package/packages/daemon/dist/lib/workspace-promotion.js +91 -12
  54. package/packages/daemon/dist/lib/workspace-registry.js +4 -0
  55. package/packages/daemon/dist/server/pane-stream-upgrade.js +20 -6
  56. package/packages/daemon/dist/terminal/attachments/native-runtime.js +61 -17
  57. package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +9 -1
  58. package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +11 -2
  59. package/packages/daemon/dist/terminal/mirror/control-channel.js +65 -6
  60. package/packages/daemon/dist/terminal/mirror/mirror-service.js +44 -0
  61. package/packages/daemon/dist/terminal/mirror/session-channel.js +210 -50
  62. package/packages/daemon/dist/terminal/mirror/window-link-authority.js +160 -0
  63. package/packages/daemon/dist/terminal/pane-stream/lease-manager.js +3 -3
  64. package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +40 -6
  65. package/packages/daemon/dist/terminal/protocol/live-session-identity.js +9 -0
  66. package/packages/daemon/dist/terminal/protocol/native-backing-client.js +2 -1
  67. package/packages/daemon/dist/terminal/session-runtime/interaction-receipt-facts.js +9 -0
  68. package/packages/daemon/dist/terminal/session-runtime/registry.js +6 -0
  69. package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +1 -1
  70. package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-resource-changes.js +1 -0
  71. package/packages/daemon/dist/tui/mirror/application-shell-daemon-connection.js +7 -2
  72. package/packages/daemon/dist/tui/mirror/application-shell-server-connection.js +114 -0
  73. package/packages/daemon/dist/tui/mirror/menu-model.js +1 -1
  74. package/packages/daemon/dist/tui/mirror/open-tui-verified-routing.js +34 -4
  75. package/packages/daemon/dist/tui/mirror/open-tui-workspace-runtime-port.js +29 -8
  76. package/packages/daemon/dist/tui/mirror/runtime/application-guided-tour-integration.jsx +119 -0
  77. package/packages/daemon/dist/tui/mirror/runtime/application-guided-tour-owner.js +132 -0
  78. package/packages/daemon/dist/tui/mirror/runtime/application-home-agent-roster.jsx +21 -9
  79. package/packages/daemon/dist/tui/mirror/runtime/application-home-agent-transport.js +63 -1
  80. package/packages/daemon/dist/tui/mirror/runtime/application-home-agents-owner.js +8 -2
  81. package/packages/daemon/dist/tui/mirror/runtime/application-home-agents.js +3 -1
  82. package/packages/daemon/dist/tui/mirror/runtime/application-home-catalog-owner.js +11 -5
  83. package/packages/daemon/dist/tui/mirror/runtime/application-home-catalog.js +140 -75
  84. package/packages/daemon/dist/tui/mirror/runtime/application-home-experience.js +40 -0
  85. package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +132 -0
  86. package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +9 -0
  87. package/packages/daemon/dist/tui/mirror/runtime/application-machine-catalog.js +12 -2
  88. package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +211 -31
  89. package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +91 -37
  90. package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +20 -7
  91. package/packages/daemon/dist/tui/mirror/runtime/application-root-configuration.js +9 -3
  92. package/packages/daemon/dist/tui/mirror/runtime/application-route-connection.js +95 -2
  93. package/packages/daemon/dist/tui/mirror/runtime/application-shell-home.jsx +6 -2
  94. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +158 -2
  95. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace-policy.js +43 -0
  96. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace.jsx +78 -25
  97. package/packages/daemon/dist/tui/mirror/runtime/application-theme-repaint.js +9 -0
  98. package/packages/daemon/dist/tui/mirror/runtime/fleet-lifecycle-client.js +44 -1
  99. package/packages/daemon/dist/tui/mirror/runtime/guided-tour-coach.jsx +54 -0
  100. package/packages/daemon/dist/tui/mirror/runtime/guided-tour-storage.js +67 -0
  101. package/packages/daemon/dist/tui/mirror/runtime/guided-tour.js +113 -0
  102. package/packages/daemon/dist/tui/mirror/runtime/open-tui-generation-host.js +1 -1
  103. package/packages/daemon/dist/tui/mirror/runtime/resize-terminal-pane.js +4 -0
  104. package/packages/daemon/dist/tui/mirror/runtime/select-terminal-pane.js +2 -1
  105. package/packages/daemon/dist/tui/mirror/runtime/semantic-shell-viewport-resize.js +12 -4
  106. package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +6 -3
  107. package/packages/daemon/dist/tui/mirror/workspace/application-command-description.js +11 -2
  108. package/packages/daemon/dist/tui/mirror/workspace/terminal-window-strip.jsx +2 -1
  109. package/packages/daemon/src/command-center/actions/command-definitions.ts +6 -0
  110. package/packages/daemon/src/command-center/actions/errors.ts +4 -0
  111. package/packages/daemon/src/command-center/actions/handlers/workspace-multiplexer.ts +2 -0
  112. package/packages/daemon/src/command-center/actions/registry.ts +14 -0
  113. package/packages/daemon/src/command-center/discovery.ts +2 -10
  114. package/packages/daemon/src/command-center/server.ts +29 -1
  115. package/packages/daemon/src/command-center/tmux-server-native-backing.ts +49 -0
  116. package/packages/daemon/src/command-center/tmux-servers.ts +479 -0
  117. package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +111 -4
  118. package/packages/daemon/src/lib/canonical-daemon.ts +15 -3
  119. package/packages/daemon/src/lib/daemon-embed.ts +241 -28
  120. package/packages/daemon/src/lib/embedded-tmux-server-owners.ts +190 -0
  121. package/packages/daemon/src/lib/fleet-lifecycle-authority.ts +33 -9
  122. package/packages/daemon/src/lib/headless-daemon.ts +18 -1
  123. package/packages/daemon/src/lib/runtime-namespace.ts +24 -3
  124. package/packages/daemon/src/lib/tmux-client-execution.ts +56 -0
  125. package/packages/daemon/src/lib/tmux-named-socket-fence.ts +5 -0
  126. package/packages/daemon/src/lib/tmux-server-generation-runner.ts +81 -0
  127. package/packages/daemon/src/lib/tmux-server-owner.ts +336 -0
  128. package/packages/daemon/src/lib/tmux-server-owners.ts +228 -0
  129. package/packages/daemon/src/lib/tmux-server-proof.ts +95 -0
  130. package/packages/daemon/src/lib/tmux-server-registration.ts +105 -0
  131. package/packages/daemon/src/lib/tmux-server-session-create.ts +52 -0
  132. package/packages/daemon/src/lib/tmux-server-session-open.ts +106 -0
  133. package/packages/daemon/src/lib/tmux-servers-cli.ts +186 -0
  134. package/packages/daemon/src/lib/tmux-session-mutation-fence.ts +62 -0
  135. package/packages/daemon/src/lib/tmux-window-link-guard.ts +112 -0
  136. package/packages/daemon/src/lib/workspace-config-loader.ts +3 -0
  137. package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +121 -1
  138. package/packages/daemon/src/lib/workspace-pane-creation.ts +16 -55
  139. package/packages/daemon/src/lib/workspace-promotion.ts +98 -12
  140. package/packages/daemon/src/lib/workspace-registry.ts +5 -0
  141. package/packages/daemon/src/server/pane-stream-upgrade.ts +25 -5
  142. package/packages/daemon/src/terminal/attachments/native-runtime.ts +70 -16
  143. package/packages/daemon/src/terminal/mirror/control-channel.ts +64 -7
  144. package/packages/daemon/src/terminal/mirror/events.ts +2 -0
  145. package/packages/daemon/src/terminal/mirror/mirror-service.ts +56 -0
  146. package/packages/daemon/src/terminal/mirror/session-channel.ts +245 -50
  147. package/packages/daemon/src/terminal/mirror/window-link-authority.ts +203 -0
  148. package/packages/daemon/src/terminal/pane-stream/lease-manager.ts +3 -3
  149. package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +50 -6
  150. package/packages/daemon/src/terminal/protocol/live-session-identity.ts +14 -0
  151. package/packages/daemon/src/terminal/protocol/native-backing-client.ts +4 -1
  152. package/packages/daemon/src/terminal/session-runtime/interaction-receipt-facts.ts +9 -0
  153. package/packages/daemon/src/terminal/session-runtime/registry.ts +9 -0
  154. package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +2 -2
  155. package/packages/daemon/src/terminal/session-runtime/semantic-mutation-resource-changes.ts +1 -0
  156. package/packages/daemon/src/tui/mirror/application-shell-daemon-connection.ts +28 -2
  157. package/packages/daemon/src/tui/mirror/application-shell-server-connection.ts +154 -0
  158. package/packages/daemon/src/tui/mirror/menu-model.ts +1 -1
  159. package/packages/daemon/src/tui/mirror/open-tui-verified-routing.ts +41 -3
  160. package/packages/daemon/src/tui/mirror/open-tui-workspace-runtime-port.ts +47 -6
  161. package/packages/daemon/src/tui/mirror/runtime/application-entry.ts +2 -1
  162. package/packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions.tsx +10 -4
  163. package/packages/daemon/src/tui/mirror/runtime/application-fleet-tabs.ts +2 -0
  164. package/packages/daemon/src/tui/mirror/runtime/application-guided-tour-integration.tsx +199 -0
  165. package/packages/daemon/src/tui/mirror/runtime/application-guided-tour-owner.ts +162 -0
  166. package/packages/daemon/src/tui/mirror/runtime/application-home-agent-roster.tsx +36 -10
  167. package/packages/daemon/src/tui/mirror/runtime/application-home-agent-transport.ts +69 -0
  168. package/packages/daemon/src/tui/mirror/runtime/application-home-agents-owner.ts +12 -2
  169. package/packages/daemon/src/tui/mirror/runtime/application-home-agents.ts +13 -2
  170. package/packages/daemon/src/tui/mirror/runtime/application-home-catalog-owner.ts +13 -3
  171. package/packages/daemon/src/tui/mirror/runtime/application-home-catalog.ts +172 -100
  172. package/packages/daemon/src/tui/mirror/runtime/application-home-experience.ts +59 -0
  173. package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +175 -0
  174. package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +9 -1
  175. package/packages/daemon/src/tui/mirror/runtime/application-machine-catalog.ts +19 -7
  176. package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +285 -36
  177. package/packages/daemon/src/tui/mirror/runtime/application-machine-overlays.tsx +1 -1
  178. package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +124 -40
  179. package/packages/daemon/src/tui/mirror/runtime/application-palette-preview.tsx +3 -0
  180. package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +18 -6
  181. package/packages/daemon/src/tui/mirror/runtime/application-root-configuration.ts +12 -3
  182. package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +40 -40
  183. package/packages/daemon/src/tui/mirror/runtime/application-route-connection.ts +93 -1
  184. package/packages/daemon/src/tui/mirror/runtime/application-shell-catalog.tsx +4 -0
  185. package/packages/daemon/src/tui/mirror/runtime/application-shell-home.tsx +20 -1
  186. package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +10 -0
  187. package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +177 -2
  188. package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-policy.ts +62 -0
  189. package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace.tsx +87 -30
  190. package/packages/daemon/src/tui/mirror/runtime/application-theme-repaint.ts +19 -0
  191. package/packages/daemon/src/tui/mirror/runtime/fleet-lifecycle-client.ts +55 -0
  192. package/packages/daemon/src/tui/mirror/runtime/guided-tour-coach.tsx +118 -0
  193. package/packages/daemon/src/tui/mirror/runtime/guided-tour-storage.ts +72 -0
  194. package/packages/daemon/src/tui/mirror/runtime/guided-tour.ts +147 -0
  195. package/packages/daemon/src/tui/mirror/runtime/open-tui-generation-host.ts +1 -1
  196. package/packages/daemon/src/tui/mirror/runtime/resize-terminal-pane.ts +3 -0
  197. package/packages/daemon/src/tui/mirror/runtime/select-terminal-pane.ts +3 -0
  198. package/packages/daemon/src/tui/mirror/runtime/semantic-shell-viewport-resize.ts +11 -4
  199. package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +15 -4
  200. package/packages/daemon/src/tui/mirror/workspace/application-command-description.ts +13 -2
  201. package/packages/daemon/src/tui/mirror/workspace/terminal-window-strip.tsx +5 -1
  202. package/packages/daemon-client/package.json +3 -1
  203. package/packages/daemon-client/src/index.ts +2 -0
  204. package/packages/daemon-client/src/pane-stream-client.test.ts +108 -38
  205. package/packages/daemon-client/src/pane-stream-client.ts +26 -0
  206. package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +114 -0
  207. package/packages/daemon-client/src/scoped-tmux-server-transport.ts +385 -0
  208. package/packages/daemon-client/src/tmux-server-client.test.ts +279 -0
  209. package/packages/daemon-client/src/tmux-server-client.ts +287 -0
@@ -1,4 +1,13 @@
1
- import { resolveRuntimeNamespace } from "./runtime-namespace.ts";
1
+ import {
2
+ captureTmuxServerProof,
3
+ captureUnboundTmuxSelectorProof,
4
+ type TmuxServerProof,
5
+ } from "./tmux-server-proof.ts";
6
+ import {
7
+ createPinnedWorkspaceTmuxRunner,
8
+ resolveWorkspacePaneTmuxAuthority,
9
+ } from "./workspace-pane-creation.ts";
10
+ import { resolveRuntimeNamespace, resolveTmuxServerIntent } from "./runtime-namespace.ts";
2
11
  import { compareProductVersions } from "./semver.ts";
3
12
  import { spawn, type ChildProcess } from "node:child_process";
4
13
  import { resolve } from "node:path";
@@ -25,12 +34,15 @@ import {
25
34
  } from "./canonical-daemon.ts";
26
35
 
27
36
  export type CanonicalDaemonBootstrapFailure =
37
+ | "tmux-server-mismatch"
38
+ | "tmux-server-unproven"
28
39
  | "canonical-record-invalid"
29
40
  | "identity-mismatch"
30
41
  | "protocol-mismatch"
31
42
  | "product-version-mismatch";
32
43
 
33
44
  export interface CanonicalDaemonBootstrapOptions {
45
+ readonly tmuxServerIntent?: ReturnType<typeof resolveTmuxServerIntent>;
34
46
  readonly supervisionId?: string;
35
47
  /** The shipped CLI entry which owns `runHeadlessDaemon`. */
36
48
  readonly entryPath: string;
@@ -43,6 +55,9 @@ export interface CanonicalDaemonBootstrapOptions {
43
55
  }
44
56
 
45
57
  export interface CanonicalDaemonBootstrapDependencies {
58
+ readonly serverProof: (
59
+ intent: NonNullable<ReturnType<typeof resolveTmuxServerIntent>>,
60
+ ) => TmuxServerProof | null;
46
61
  readonly inspect: () => CanonicalDaemonInfoState;
47
62
  readonly ownerProvenDead: (
48
63
  state: Exclude<CanonicalDaemonInfoState, { status: "missing" }>,
@@ -135,6 +150,17 @@ function sameCanonicalInstance(left: CanonicalDaemonInfo, right: CanonicalDaemon
135
150
  const defaultDependencies: CanonicalDaemonBootstrapDependencies = {
136
151
  // This adapter owns startup, so legacy permission preparation is explicit
137
152
  // here. Injected inspectors remain isolated from real filesystem mutation.
153
+ serverProof: (intent) => {
154
+ try {
155
+ const authority = { ...resolveWorkspacePaneTmuxAuthority(), socketSelector: intent.selector };
156
+ return (
157
+ captureTmuxServerProof(createPinnedWorkspaceTmuxRunner(authority, { timeoutMs: 1000 })) ??
158
+ captureUnboundTmuxSelectorProof(authority)
159
+ );
160
+ } catch {
161
+ return null;
162
+ }
163
+ },
138
164
  inspect: prepareCanonicalDaemonInfoForBootstrap,
139
165
  ownerProvenDead: isCanonicalDaemonRecordOwnerProvenDead,
140
166
  alive: isCanonicalDaemonAlive,
@@ -164,6 +190,7 @@ async function replaceOlderCanonicalDaemon(
164
190
  info: CanonicalDaemonInfo,
165
191
  timeoutMs: number,
166
192
  expectedProductVersion?: string,
193
+ intent: ReturnType<typeof resolveTmuxServerIntent> = null,
167
194
  ): Promise<void> {
168
195
  if (!needsReplacement(info, expectedProductVersion)) {
169
196
  throw new DaemonBootstrapError(
@@ -172,7 +199,10 @@ async function replaceOlderCanonicalDaemon(
172
199
  { reason: "protocol-mismatch" },
173
200
  );
174
201
  }
175
- const [identity, health] = await Promise.all([deps.identity(info), deps.health(info)]);
202
+ const [identity, health] = await Promise.all([
203
+ deps.identity(info, undefined, Boolean(intent)),
204
+ deps.health(info),
205
+ ]);
176
206
  if (
177
207
  !info.authToken ||
178
208
  !identity ||
@@ -191,6 +221,7 @@ async function replaceOlderCanonicalDaemon(
191
221
  { reason: "identity-mismatch" },
192
222
  );
193
223
  }
224
+ assertServerIntent(deps, info, identity, intent);
194
225
  const latest = deps.inspect();
195
226
  if (
196
227
  latest.status !== "valid" ||
@@ -221,9 +252,35 @@ async function replaceOlderCanonicalDaemon(
221
252
  );
222
253
  }
223
254
 
255
+ function assertServerIntent(
256
+ deps: CanonicalDaemonBootstrapDependencies,
257
+ info: CanonicalDaemonInfo,
258
+ identity: NonNullable<Awaited<ReturnType<typeof probeCanonicalDaemonIdentity>>>,
259
+ intent: ReturnType<typeof resolveTmuxServerIntent>,
260
+ ): void {
261
+ if (!intent) return;
262
+ const proof = deps.serverProof(intent);
263
+ if (info.tmuxServerProofVersion !== 1 || !identity.tmuxServerProof || !proof)
264
+ throw new DaemonBootstrapError(
265
+ "incompatible",
266
+ "Cannot prove the requested tmux server for this daemon. Its server identity is unavailable or unsupported; the existing daemon was left running.",
267
+ { reason: "tmux-server-unproven" },
268
+ );
269
+ if (
270
+ identity.tmuxServerProof.kind !== proof.kind ||
271
+ identity.tmuxServerProof.digest !== proof.digest
272
+ )
273
+ throw new DaemonBootstrapError(
274
+ "incompatible",
275
+ "The existing daemon manages a different tmux server. It was left running; select its server or use a separate daemon namespace.",
276
+ { reason: "tmux-server-mismatch" },
277
+ );
278
+ }
279
+
224
280
  async function probeCanonical(
225
281
  deps: CanonicalDaemonBootstrapDependencies,
226
282
  expectedProductVersion?: string,
283
+ intent: ReturnType<typeof resolveTmuxServerIntent> = null,
227
284
  ): Promise<DaemonBootstrapProbe<CanonicalDaemonInfo, CanonicalDaemonBootstrapFailure>> {
228
285
  const state = deps.inspect();
229
286
  if (state.status === "missing") return { status: "absent-or-stale" };
@@ -244,7 +301,7 @@ async function probeCanonical(
244
301
  return { status: state.info.supervisionId ? "owner-pending" : "absent-or-stale" };
245
302
 
246
303
  const [identity, health] = await Promise.all([
247
- deps.identity(state.info),
304
+ deps.identity(state.info, undefined, Boolean(intent)),
248
305
  deps.health(state.info),
249
306
  ]);
250
307
  // A living elected generation may publish before its accept loop. Preserve
@@ -258,6 +315,7 @@ async function probeCanonical(
258
315
  ) {
259
316
  return { status: "incompatible", reason: "identity-mismatch" };
260
317
  }
318
+ assertServerIntent(deps, state.info, identity, intent);
261
319
  if (
262
320
  state.info.protocolVersion !== DAEMON_WIRE_PROTOCOL_VERSION ||
263
321
  identity.protocolVersion !== state.info.protocolVersion ||
@@ -325,12 +383,24 @@ export function createCanonicalDaemonBootstrapCoordinator(
325
383
  options: CanonicalDaemonBootstrapOptions,
326
384
  dependencies: Partial<CanonicalDaemonBootstrapDependencies> = {},
327
385
  ): DaemonBootstrapCoordinator<CanonicalDaemonInfo, never, CanonicalDaemonBootstrapFailure> {
386
+ options = {
387
+ ...options,
388
+ tmuxServerIntent:
389
+ options.tmuxServerIntent === undefined ? resolveTmuxServerIntent() : options.tmuxServerIntent,
390
+ };
328
391
  const deps = supervisedAdmission(
329
392
  { ...defaultDependencies, ...dependencies },
330
393
  options.supervisionId,
331
394
  );
332
395
  return new DaemonBootstrapCoordinator({
333
- probe: () => probeCanonical(deps, options.expectedProductVersion),
396
+ probe: () =>
397
+ probeCanonical(
398
+ deps,
399
+ options.expectedProductVersion,
400
+ options.tmuxServerIntent === undefined
401
+ ? resolveTmuxServerIntent()
402
+ : options.tmuxServerIntent,
403
+ ),
334
404
  spawn: () => deps.spawnOwner(resolve(options.entryPath), resolve(options.cwd ?? process.cwd())),
335
405
  timeoutMs: options.timeoutMs,
336
406
  onPhaseChanged: options.onPhaseChanged,
@@ -343,6 +413,11 @@ export function ensureCanonicalDaemon(
343
413
  options: CanonicalDaemonBootstrapOptions,
344
414
  dependencies: Partial<CanonicalDaemonBootstrapDependencies> = {},
345
415
  ): Promise<DaemonBootstrapResult<CanonicalDaemonInfo, never>> {
416
+ options = {
417
+ ...options,
418
+ tmuxServerIntent:
419
+ options.tmuxServerIntent === undefined ? resolveTmuxServerIntent() : options.tmuxServerIntent,
420
+ };
346
421
  const deps = supervisedAdmission(
347
422
  { ...defaultDependencies, ...dependencies },
348
423
  options.supervisionId,
@@ -378,6 +453,9 @@ export function ensureCanonicalDaemon(
378
453
  replacing,
379
454
  options.timeoutMs ?? 15_000,
380
455
  options.expectedProductVersion,
456
+ options.tmuxServerIntent === undefined
457
+ ? resolveTmuxServerIntent()
458
+ : options.tmuxServerIntent,
381
459
  );
382
460
  } catch (replacementError) {
383
461
  // A duplicate upgrader can retire the exact owner while this caller is
@@ -402,6 +480,11 @@ export async function retireOutdatedCanonicalDaemon(
402
480
  options: CanonicalDaemonBootstrapOptions,
403
481
  dependencies: Partial<CanonicalDaemonBootstrapDependencies> = {},
404
482
  ): Promise<boolean> {
483
+ options = {
484
+ ...options,
485
+ tmuxServerIntent:
486
+ options.tmuxServerIntent === undefined ? resolveTmuxServerIntent() : options.tmuxServerIntent,
487
+ };
405
488
  const deps = supervisedAdmission(
406
489
  { ...defaultDependencies, ...dependencies },
407
490
  options.supervisionId,
@@ -436,6 +519,7 @@ export async function retireOutdatedCanonicalDaemon(
436
519
  state.info,
437
520
  options.timeoutMs ?? 15_000,
438
521
  options.expectedProductVersion,
522
+ options.tmuxServerIntent === undefined ? resolveTmuxServerIntent() : options.tmuxServerIntent,
439
523
  );
440
524
  return true;
441
525
  } catch (error) {
@@ -448,3 +532,26 @@ export async function retireOutdatedCanonicalDaemon(
448
532
  throw error;
449
533
  }
450
534
  }
535
+
536
+ /** Admission for foreground election winners and explicit takeover paths. */
537
+ export async function assertCanonicalDaemonServerIntent(
538
+ info: CanonicalDaemonInfo,
539
+ intent: ReturnType<typeof resolveTmuxServerIntent> = resolveTmuxServerIntent(),
540
+ dependencies: Partial<CanonicalDaemonBootstrapDependencies> = {},
541
+ ): Promise<void> {
542
+ if (!intent) return;
543
+ const deps = { ...defaultDependencies, ...dependencies };
544
+ const identity = await deps.identity(info, undefined, true);
545
+ if (
546
+ !identity ||
547
+ identity.pid !== info.pid ||
548
+ identity.instanceId !== info.instanceId ||
549
+ identity.startedAt !== info.startedAt
550
+ )
551
+ throw new DaemonBootstrapError(
552
+ "incompatible",
553
+ "Canonical daemon identity could not be verified for the requested tmux server.",
554
+ { reason: "identity-mismatch" },
555
+ );
556
+ assertServerIntent(deps, info, identity, intent);
557
+ }
@@ -644,6 +644,9 @@ export function writeCanonicalDaemonInfo(
644
644
  instanceId: info.instanceId,
645
645
  startedAt: info.startedAt,
646
646
  ...(info.environmentId !== undefined ? { environmentId: info.environmentId } : {}),
647
+ ...(info.tmuxServerProofVersion !== undefined
648
+ ? { tmuxServerProofVersion: info.tmuxServerProofVersion }
649
+ : {}),
647
650
  bindHostname: info.bindHostname,
648
651
  authToken: info.authToken,
649
652
  ...(info.provenance !== undefined ? { provenance: info.provenance } : {}),
@@ -1051,12 +1054,21 @@ export async function probeCanonicalDaemonHealth(
1051
1054
  export async function probeCanonicalDaemonIdentity(
1052
1055
  info: CanonicalDaemonInfo,
1053
1056
  parentSignal?: AbortSignal,
1057
+ includeTmuxServerProof = false,
1054
1058
  ): Promise<DaemonIdentity | null> {
1055
1059
  if (!(await isCanonicalDaemonAlive(info))) return null;
1056
1060
  try {
1057
- const res = await fetch(canonicalDaemonUrl("http", info.bindHostname, info.port, "/identity"), {
1058
- signal: parentSignal ?? timeoutSignal(750),
1059
- });
1061
+ const res = await fetch(
1062
+ canonicalDaemonUrl(
1063
+ "http",
1064
+ info.bindHostname,
1065
+ info.port,
1066
+ includeTmuxServerProof ? "/identity?tmuxServerProof=1" : "/identity",
1067
+ ),
1068
+ {
1069
+ signal: parentSignal ?? timeoutSignal(750),
1070
+ },
1071
+ );
1060
1072
  if (!res.ok) return null;
1061
1073
  const parsed = DaemonIdentitySchema.safeParse(await res.json());
1062
1074
  return parsed.success ? parsed.data : null;