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,24 @@
1
- import { runtimeTmuxArgs } from "./runtime-namespace.ts";
1
+ import { createNativeTmuxSessionCreator } from "./tmux-server-session-create.ts";
2
+ import { createTmuxSessionMutationFence } from "./tmux-session-mutation-fence.ts";
3
+ import { createNativeTmuxSessionOpener } from "./tmux-server-session-open.ts";
4
+ import {
5
+ createServerGenerationFencedTmuxRunner,
6
+ createServerGenerationFencedTmuxAsyncRunner,
7
+ } from "./tmux-server-generation-runner.ts";
8
+ import { createNativeTmuxServerCatalog } from "./tmux-server-owner.ts";
9
+ import { createEmbeddedTmuxServerOwners } from "./embedded-tmux-server-owners.ts";
10
+ import {
11
+ captureTmuxServerProofAsync,
12
+ captureTmuxServerProof,
13
+ captureUnboundTmuxSelectorProof,
14
+ type TmuxServerProof,
15
+ } from "./tmux-server-proof.ts";
16
+ import { assertCanonicalDaemonServerIntent } from "./canonical-daemon-bootstrap.ts";
17
+ import {
18
+ runtimeTmuxArgs,
19
+ resolveTmuxServerIntent,
20
+ resolveRuntimeNamespace,
21
+ } from "./runtime-namespace.ts";
2
22
  import { createFleetPreviewCapture } from "../command-center/resources/fleet-preview-route.ts";
3
23
  import { mountTerminalNativeBackingRoute } from "../command-center/resources/terminal-native-backing-route.ts";
4
24
  import { startOwnedEmbeddedDaemon } from "./embedded-daemon-lifecycle.ts";
@@ -34,7 +54,6 @@ import {
34
54
  import {
35
55
  classifySessionInspectionError,
36
56
  DaemonSessionMonitor,
37
- execTmuxAsync,
38
57
  isConfirmedMissingTmuxTarget,
39
58
  parseDaemonMonitorPanes,
40
59
  readPortProcessFactsAsync,
@@ -221,14 +240,6 @@ function tmux(...args: string[]): string {
221
240
  }).trim();
222
241
  }
223
242
 
224
- function tmuxSilent(...args: string[]): string {
225
- try {
226
- return tmux(...args);
227
- } catch {
228
- return "";
229
- }
230
- }
231
-
232
243
  function assertTmuxSession(
233
244
  sessionName: string,
234
245
  runTmux: (args: readonly string[]) => string = (args) => tmux(...args),
@@ -549,6 +560,7 @@ function sameCanonicalInstance(left: CanonicalDaemonInfo, right: CanonicalDaemon
549
560
  async function requestValidatedDaemonShutdown(
550
561
  info: CanonicalDaemonInfo,
551
562
  deadline: TakeoverDeadline,
563
+ intent: ReturnType<typeof resolveTmuxServerIntent>,
552
564
  ): Promise<void> {
553
565
  if (info.supervisionId)
554
566
  throw new DaemonStartupError(
@@ -573,6 +585,8 @@ async function requestValidatedDaemonShutdown(
573
585
  "canonical_takeover_identity_mismatch",
574
586
  );
575
587
  }
588
+ await assertCanonicalDaemonServerIntent(info, intent);
589
+ assertTakeoverDeadline(deadline, "Tmux server intent proof exceeded the takeover deadline");
576
590
  const health = await probeCanonicalDaemonHealth(info, deadline.signal);
577
591
  assertTakeoverDeadline(
578
592
  deadline,
@@ -757,6 +771,7 @@ async function startHttpServer({
757
771
  localBypassToken,
758
772
  silent,
759
773
  readProjectAuth,
774
+ tmuxServerProof,
760
775
  daemonIdentity,
761
776
  workspacePaneCreationBackend,
762
777
  workspaceOpenBackend,
@@ -774,7 +789,9 @@ async function startHttpServer({
774
789
  catalogFleet,
775
790
  fleetPreviewCapture,
776
791
  sessionRuntimeRegistry,
792
+ serverOwners,
777
793
  }: {
794
+ serverOwners: Awaited<ReturnType<typeof createEmbeddedTmuxServerOwners>>;
778
795
  sessionName: string;
779
796
  requestedPort: number;
780
797
  bindHostname: string;
@@ -783,6 +800,7 @@ async function startHttpServer({
783
800
  localBypassToken?: string | null;
784
801
  silent?: boolean;
785
802
  readProjectAuth?: boolean;
803
+ tmuxServerProof: () => Promise<TmuxServerProof | null>;
786
804
  daemonIdentity: {
787
805
  productVersion: string;
788
806
  instanceId: string;
@@ -839,6 +857,7 @@ async function startHttpServer({
839
857
  const authService = new AuthService(authConfig.secret);
840
858
 
841
859
  const app = createApp({
860
+ tmuxServerOwners: serverOwners.owners,
842
861
  authService,
843
862
  authConfig,
844
863
  remoteAccess: {
@@ -848,6 +867,7 @@ async function startHttpServer({
848
867
  ownerToken: localBypassToken ?? null,
849
868
  },
850
869
  daemonIdentity,
870
+ tmuxServerProof,
851
871
  workspacePaneCreationBackend,
852
872
  workspaceOpenBackend,
853
873
  workspaceOpenHandoffBackend,
@@ -914,7 +934,13 @@ async function startHttpServer({
914
934
  (create ? getTerminalAttachmentRuntime() : peekTerminalAttachmentRuntime())?.admission ??
915
935
  null,
916
936
  );
917
- const paneStreamBoundary = attachPaneStreamWebSocket(server, paneStreamRuntime.coordinator);
937
+ const paneStreamBoundary = attachPaneStreamWebSocket(
938
+ server,
939
+ paneStreamRuntime.coordinator,
940
+ PANE_STREAM_REDEEM_PATH,
941
+ () => serverOwners.isDefaultCurrent(),
942
+ );
943
+ serverOwners.attach(server);
918
944
 
919
945
  try {
920
946
  await new Promise<void>((resolve, reject) => {
@@ -987,6 +1013,7 @@ export async function startEmbeddedDaemon(
987
1013
  async function startEmbeddedDaemonGeneration(
988
1014
  opts: EmbeddedDaemonOptions,
989
1015
  ): Promise<EmbeddedDaemonHandle> {
1016
+ const tmuxServerIntent = resolveTmuxServerIntent();
990
1017
  const sessionName = opts.sessionName ?? EMBEDDED_SESSION_NAME;
991
1018
  const sessionless = opts.sessionName == null;
992
1019
  const appSettings = readAppSettings();
@@ -1026,7 +1053,7 @@ async function startEmbeddedDaemonGeneration(
1026
1053
  if (state.status === "valid" && (await isCanonicalDaemonAlive(state.info))) {
1027
1054
  const takeoverDeadline = createTakeoverDeadline(takeoverTimeoutMs());
1028
1055
  try {
1029
- await requestValidatedDaemonShutdown(state.info, takeoverDeadline);
1056
+ await requestValidatedDaemonShutdown(state.info, takeoverDeadline, tmuxServerIntent);
1030
1057
  claim = await acquireCanonicalDaemonClaimAfterTakeover(state.info, takeoverDeadline);
1031
1058
  } finally {
1032
1059
  takeoverDeadline.dispose();
@@ -1084,7 +1111,22 @@ async function startEmbeddedDaemonGeneration(
1084
1111
  // caller's ambient TMUX/PATH happens to select.
1085
1112
  const tmuxAuthority = resolveWorkspacePaneTmuxAuthority();
1086
1113
  const catalogTmuxRunner = createPinnedWorkspaceTmuxRunner(tmuxAuthority);
1087
- const fleetFactsTmuxRunner = createPinnedWorkspaceTmuxAsyncRunner(tmuxAuthority);
1114
+ const initialTmuxProof = captureTmuxServerProof(catalogTmuxRunner);
1115
+ const initialNativeIdentityParts = initialTmuxProof
1116
+ ? catalogTmuxRunner(["display-message", "-p", "#{pid}\t#{start_time}"]).split("\t")
1117
+ : null;
1118
+ const initialNativeServerIdentity = initialNativeIdentityParts
1119
+ ? { pid: initialNativeIdentityParts[0]!, startTime: initialNativeIdentityParts[1]! }
1120
+ : undefined;
1121
+ const sessionMutationFence = createTmuxSessionMutationFence();
1122
+ const nativeGenerationTmuxRunner = sessionMutationFence.wrap(
1123
+ initialNativeServerIdentity
1124
+ ? createServerGenerationFencedTmuxRunner(tmuxAuthority, initialNativeServerIdentity)
1125
+ : catalogTmuxRunner,
1126
+ );
1127
+ const fleetFactsTmuxRunner = initialNativeServerIdentity
1128
+ ? createServerGenerationFencedTmuxAsyncRunner(tmuxAuthority, initialNativeServerIdentity)
1129
+ : createPinnedWorkspaceTmuxAsyncRunner(tmuxAuthority);
1088
1130
  const tmuxAuthorityReplaced = createTmuxAuthorityReplacementProbe(
1089
1131
  tmuxAuthority,
1090
1132
  catalogTmuxRunner,
@@ -1110,8 +1152,8 @@ async function startEmbeddedDaemonGeneration(
1110
1152
  const workspaceRegistry = getDefaultWorkspaceRegistry();
1111
1153
  await workspaceRegistry.load(() => readWorkspaceRegistrySessionInventory(registryTmuxRunner));
1112
1154
  const paneSourceCredentials = new PaneSourceCredentialAuthority({
1113
- run: (args) => tmuxSilent(...args),
1114
- runAsync: (args, signal) => execTmuxAsync(args, signal),
1155
+ run: (args) => nativeGenerationTmuxRunner(args),
1156
+ runAsync: (args, signal) => fleetFactsTmuxRunner(args, signal),
1115
1157
  });
1116
1158
  const legacySession = process.env.TMUX_IDE_SESSION;
1117
1159
  if (legacySession && !workspaceRegistry.has(legacySession)) {
@@ -1153,16 +1195,19 @@ async function startEmbeddedDaemonGeneration(
1153
1195
  daemonInstanceId: instanceId,
1154
1196
  registry: workspaceRegistry,
1155
1197
  tmuxAuthority,
1198
+ io: { runTmux: nativeGenerationTmuxRunner },
1156
1199
  });
1157
1200
  const workspaceOpen = new WorkspaceOpenAuthority({
1158
1201
  daemonInstanceId: instanceId,
1159
1202
  registry: workspaceRegistry,
1160
1203
  tmuxAuthority,
1204
+ io: { runTmux: nativeGenerationTmuxRunner },
1161
1205
  });
1162
1206
  const workspacePromotion = new WorkspacePromotionAuthority({
1163
1207
  daemonInstanceId: instanceId,
1164
1208
  registry: workspaceRegistry,
1165
1209
  tmuxAuthority,
1210
+ io: { runTmux: fleetFactsTmuxRunner },
1166
1211
  });
1167
1212
  if (opts.restoreTmuxWorkspaces) {
1168
1213
  for (const workspace of workspaceRegistry.list()) {
@@ -1178,7 +1223,7 @@ async function startEmbeddedDaemonGeneration(
1178
1223
  productVersion,
1179
1224
  startedAt,
1180
1225
  registry: workspaceRegistry,
1181
- runTmux: catalogTmuxRunner,
1226
+ runTmux: nativeGenerationTmuxRunner,
1182
1227
  readFleet: () => readAdoptedFleet(workspaceRegistry, catalogTmuxRunner),
1183
1228
  });
1184
1229
  const appWindowMutation = new AppWindowMutationAuthority({
@@ -1189,13 +1234,22 @@ async function startEmbeddedDaemonGeneration(
1189
1234
  daemonInstanceId: instanceId,
1190
1235
  registry: workspaceRegistry,
1191
1236
  tmuxAuthority,
1237
+ ...(initialTmuxProof
1238
+ ? {
1239
+ io: {
1240
+ runTmux: nativeGenerationTmuxRunner,
1241
+ },
1242
+ }
1243
+ : {}),
1192
1244
  });
1193
1245
  let sessionRuntimeRegistry: SessionRuntimeRegistry | null = null;
1194
1246
  let workspaceOpenHandoff: WorkspaceOpenHandoffCoordinator | null = null;
1195
1247
  let terminalInventoryRuntime: WorkspaceTerminalInventoryRuntime | null = null;
1248
+ let sessionMonitor: DaemonSessionMonitor | null = null;
1196
1249
  const externalInteractionObserver = new TmuxExternalInteractionObserver({
1197
1250
  daemonInstanceId: instanceId,
1198
1251
  internalReadOwnerToken: localBypassToken,
1252
+ io: { runTmux: fleetFactsTmuxRunner },
1199
1253
  registry: workspaceRegistry,
1200
1254
  tmuxAuthority,
1201
1255
  // A gap cannot reconstruct historical interactions. Refresh inventory
@@ -1238,6 +1292,23 @@ async function startEmbeddedDaemonGeneration(
1238
1292
  });
1239
1293
  let terminalAttachmentRuntime: NativeTerminalAttachmentRuntime | null = null;
1240
1294
  let paneStreamRuntime: PaneStreamRuntime | null = null;
1295
+ let serverOwners: Awaited<ReturnType<typeof createEmbeddedTmuxServerOwners>> | null = null;
1296
+ const defaultSessionCreator = createNativeTmuxSessionCreator({
1297
+ generation: instanceId,
1298
+ registry: workspaceRegistry,
1299
+ run: nativeGenerationTmuxRunner,
1300
+ assertOpen: () => {
1301
+ if (serverOwners?.defaultRetired) throw new Error("Default tmux owner is retired");
1302
+ },
1303
+ });
1304
+ const defaultSessionOpener = createNativeTmuxSessionOpener({
1305
+ generation: instanceId,
1306
+ registry: workspaceRegistry,
1307
+ run: fleetFactsTmuxRunner,
1308
+ assertOpen: () => {
1309
+ if (serverOwners?.defaultRetired) throw new Error("Default tmux owner is retired");
1310
+ },
1311
+ });
1241
1312
  let runtimeTraceStream: ReturnType<typeof createWriteStream> | null = null;
1242
1313
  const closeRuntimeTraceStream = async (): Promise<void> => {
1243
1314
  externalInteractionObserver.setDiagnostics(null);
@@ -1259,6 +1330,19 @@ async function startEmbeddedDaemonGeneration(
1259
1330
  workspaceMultiplexer.readPane(operationId, intent);
1260
1331
  return;
1261
1332
  }
1333
+ if (
1334
+ intent.verb === "workspace.window.link.select" ||
1335
+ intent.verb === "workspace.window.link.unlink" ||
1336
+ intent.verb === "workspace.pane.select"
1337
+ ) {
1338
+ return workspaceMultiplexer.mutateWindowLink(
1339
+ { operationId, expectedDaemonInstanceId: instanceId, intent },
1340
+ (session, action) => {
1341
+ if (!sessionRuntimeRegistry) throw new Error("Session runtime unavailable");
1342
+ return sessionRuntimeRegistry.executeWindowLinkAction(session, action);
1343
+ },
1344
+ );
1345
+ }
1262
1346
  return workspaceMultiplexer.mutate(
1263
1347
  { operationId, expectedDaemonInstanceId: instanceId, intent },
1264
1348
  timing,
@@ -1326,8 +1410,10 @@ async function startEmbeddedDaemonGeneration(
1326
1410
  publishResourceChange: (change) => broadcastResourceChanged(change, instanceId),
1327
1411
  },
1328
1412
  mirror: {
1413
+ nativeServerIdentity: initialNativeServerIdentity,
1329
1414
  executable: tmuxAuthority.executablePath,
1330
- resolveSocketPath: () => catalogTmuxRunner(["display-message", "-p", "#{socket_path}"]),
1415
+ resolveSocketPath: () =>
1416
+ nativeGenerationTmuxRunner(["display-message", "-p", "#{socket_path}"]),
1331
1417
  internalReadHookEmission: (runtimePaneId, marker) =>
1332
1418
  externalInteractionObserver.internalReadHookEmission(runtimePaneId, marker),
1333
1419
  ...(selector.kind === "path" ? { socketPath: selector.path } : {}),
@@ -1403,6 +1489,7 @@ async function startEmbeddedDaemonGeneration(
1403
1489
  sessionRuntimeRegistry,
1404
1490
  tmuxAuthority: {
1405
1491
  executablePath: tmuxAuthority.executablePath,
1492
+ nativeServerIdentity: initialNativeServerIdentity,
1406
1493
  socketSelector: tmuxAuthority.socketSelector,
1407
1494
  trustedCwd: dir,
1408
1495
  ...(tmuxAuthority.socketSelector.kind === "name"
@@ -1454,8 +1541,93 @@ async function startEmbeddedDaemonGeneration(
1454
1541
  paneSourceCredentials.resolve(credential, resolvedSession, claimedSource),
1455
1542
  });
1456
1543
  await externalInteractionObserver.start();
1544
+ serverOwners = await createEmbeddedTmuxServerOwners({
1545
+ defaultAuthority: tmuxAuthority,
1546
+ defaultGeneration: instanceId,
1547
+ expectedDefaultProofDigest: initialTmuxProof?.digest ?? null,
1548
+ stateDirectory: resolveRuntimeNamespace().runtimeDir,
1549
+ webSocketBaseUrl: canonicalDaemonUrl("ws", bindHostname, port),
1550
+ defaultOwner: {
1551
+ catalog: createNativeTmuxServerCatalog(workspaceRegistry, fleetFactsTmuxRunner),
1552
+ openSession: async (liveSessionId) => {
1553
+ await createNativeTmuxServerCatalog(workspaceRegistry, fleetFactsTmuxRunner)();
1554
+ const result = await defaultSessionOpener.openSession(liveSessionId);
1555
+ terminalInventoryRuntime!.invalidate();
1556
+ return result;
1557
+ },
1558
+ workspaceRegistry,
1559
+ multiplexerBackend: orderedMultiplexerBackend,
1560
+ createSession: defaultSessionCreator.createSession,
1561
+ createSessionPane: async (liveSessionId, request) => {
1562
+ const selectedSession = workspaceRegistry.get(
1563
+ request.intent.workspaceName,
1564
+ )?.sessionName;
1565
+ if (!selectedSession) throw new Error("Selected workspace is unavailable");
1566
+ const result = await sessionMutationFence.execute({
1567
+ liveSessionId,
1568
+ sessionName: selectedSession,
1569
+ run: fleetFactsTmuxRunner,
1570
+ mutate: () => workspacePaneCreation.create(request),
1571
+ });
1572
+ terminalInventoryRuntime!.invalidate();
1573
+ return result;
1574
+ },
1575
+ mutateSession: async (liveSessionId, request, authenticatedHostClientId) => {
1576
+ const selectedSession = workspaceRegistry.get(
1577
+ request.intent.workspaceName,
1578
+ )?.sessionName;
1579
+ if (!selectedSession) throw new Error("Selected workspace is unavailable");
1580
+ return sessionMutationFence.execute({
1581
+ liveSessionId,
1582
+ sessionName: selectedSession,
1583
+ run: fleetFactsTmuxRunner,
1584
+ mutate: () =>
1585
+ orderedMultiplexerBackend.mutate(
1586
+ request,
1587
+ authenticatedHostClientId,
1588
+ undefined,
1589
+ true,
1590
+ ),
1591
+ });
1592
+ },
1593
+ sessionRuntimeRegistry,
1594
+ terminalInventoryRuntime,
1595
+ paneStreamRuntime,
1596
+ dispose: async () => {
1597
+ sessionMonitor?.stop();
1598
+ paneSourceCredentials.dispose();
1599
+ const transportResults = await Promise.allSettled([
1600
+ Promise.resolve().then(() => terminalAttachmentRuntime?.dispose()),
1601
+ Promise.resolve().then(() => paneStreamRuntime!.dispose()),
1602
+ ]);
1603
+ const authorityResults = await Promise.allSettled([
1604
+ Promise.resolve().then(() => terminalInventoryRuntime!.dispose()),
1605
+ Promise.resolve().then(() => workspacePaneCreation.dispose()),
1606
+ Promise.resolve().then(() => workspaceOpen.dispose()),
1607
+ Promise.resolve().then(() => workspaceOpenHandoff?.dispose()),
1608
+ Promise.resolve().then(() =>
1609
+ Promise.all([
1610
+ workspacePromotion.dispose(),
1611
+ defaultSessionOpener.dispose(),
1612
+ defaultSessionCreator.dispose(),
1613
+ ]).then(() => {}),
1614
+ ),
1615
+ Promise.resolve().then(() => appWindowMutation.dispose()),
1616
+ Promise.resolve().then(() => workspaceMultiplexer.dispose()),
1617
+ Promise.resolve().then(() => externalInteractionObserver.dispose()),
1618
+ ]);
1619
+ await sessionRuntimeRegistry!.dispose();
1620
+ const failures = [...transportResults, ...authorityResults].flatMap((result) =>
1621
+ result.status === "rejected" ? [result.reason] : [],
1622
+ );
1623
+ if (failures.length)
1624
+ throw new AggregateError(failures, "Default tmux owner retirement failed");
1625
+ },
1626
+ },
1627
+ });
1457
1628
  setFleetFactsTmuxRunner(fleetFactsTmuxRunner);
1458
1629
  startedServer = await startHttpServer({
1630
+ serverOwners,
1459
1631
  sessionName,
1460
1632
  requestedPort: port,
1461
1633
  bindHostname,
@@ -1465,6 +1637,13 @@ async function startEmbeddedDaemonGeneration(
1465
1637
  silent: opts.silent,
1466
1638
  readProjectAuth: !sessionless,
1467
1639
  daemonIdentity: { productVersion, instanceId, startedAt, environmentId },
1640
+ tmuxServerProof: async () => {
1641
+ const deadline = AbortSignal.timeout(500);
1642
+ return (
1643
+ (await captureTmuxServerProofAsync((args) => fleetFactsTmuxRunner(args, deadline))) ??
1644
+ captureUnboundTmuxSelectorProof(tmuxAuthority)
1645
+ );
1646
+ },
1468
1647
  workspacePaneCreationBackend: workspacePaneCreation,
1469
1648
  workspaceOpenBackend: workspaceOpen,
1470
1649
  workspaceOpenHandoffBackend: workspaceOpenHandoff,
@@ -1485,6 +1664,7 @@ async function startEmbeddedDaemonGeneration(
1485
1664
  sessionRuntimeRegistry,
1486
1665
  });
1487
1666
  } catch (error) {
1667
+ await serverOwners?.dispose();
1488
1668
  setFleetFactsTmuxRunner(null);
1489
1669
  await Promise.allSettled([
1490
1670
  Promise.resolve().then(() => terminalAttachmentRuntime?.dispose()),
@@ -1493,7 +1673,11 @@ async function startEmbeddedDaemonGeneration(
1493
1673
  workspacePaneCreation.dispose(),
1494
1674
  workspaceOpen.dispose(),
1495
1675
  Promise.resolve().then(() => workspaceOpenHandoff?.dispose()),
1496
- workspacePromotion.dispose(),
1676
+ Promise.all([
1677
+ workspacePromotion.dispose(),
1678
+ defaultSessionOpener.dispose(),
1679
+ defaultSessionCreator.dispose(),
1680
+ ]).then(() => {}),
1497
1681
  appWindowMutation.dispose(),
1498
1682
  workspaceMultiplexer.dispose(),
1499
1683
  externalInteractionObserver.dispose(),
@@ -1526,6 +1710,7 @@ async function startEmbeddedDaemonGeneration(
1526
1710
  );
1527
1711
  };
1528
1712
  const abortStartedServer = async (): Promise<void> => {
1713
+ await serverOwners?.dispose();
1529
1714
  setFleetFactsTmuxRunner(null);
1530
1715
  const terminalFailures = [
1531
1716
  ...(await retireTerminalAttachmentTransport(
@@ -1540,7 +1725,13 @@ async function startEmbeddedDaemonGeneration(
1540
1725
  const workspaceOpenHandoffDisposal = Promise.resolve().then(() =>
1541
1726
  workspaceOpenHandoff?.dispose(),
1542
1727
  );
1543
- const workspacePromotionDisposal = Promise.resolve().then(() => workspacePromotion.dispose());
1728
+ const workspacePromotionDisposal = Promise.resolve().then(() =>
1729
+ Promise.all([
1730
+ workspacePromotion.dispose(),
1731
+ defaultSessionOpener.dispose(),
1732
+ defaultSessionCreator.dispose(),
1733
+ ]).then(() => {}),
1734
+ );
1544
1735
  const appWindowMutationDisposal = Promise.resolve().then(() => appWindowMutation.dispose());
1545
1736
  const workspaceMultiplexerDisposal = Promise.resolve().then(() =>
1546
1737
  workspaceMultiplexer.dispose(),
@@ -1600,6 +1791,7 @@ async function startEmbeddedDaemonGeneration(
1600
1791
  environmentId,
1601
1792
  bindHostname,
1602
1793
  authToken: localBypassToken,
1794
+ tmuxServerProofVersion: 1,
1603
1795
  provenance,
1604
1796
  },
1605
1797
  claim,
@@ -1658,14 +1850,14 @@ async function startEmbeddedDaemonGeneration(
1658
1850
  },
1659
1851
  });
1660
1852
 
1661
- const sessionMonitor = sessionless
1853
+ sessionMonitor = sessionless
1662
1854
  ? null
1663
1855
  : new DaemonSessionMonitor({
1664
1856
  sessionName,
1665
1857
  backend: {
1666
1858
  inspectSession: async (candidate, signal) => {
1667
1859
  try {
1668
- await execTmuxAsync(["has-session", "-t", candidate], signal);
1860
+ await fleetFactsTmuxRunner(["has-session", "-t", candidate], signal);
1669
1861
  return "yes";
1670
1862
  } catch (error) {
1671
1863
  if (signal.aborted) return "unknown";
@@ -1685,14 +1877,14 @@ async function startEmbeddedDaemonGeneration(
1685
1877
  },
1686
1878
  hasClients: async (signal) => {
1687
1879
  try {
1688
- return (await execTmuxAsync(["list-clients"], signal)).length > 0;
1880
+ return (await fleetFactsTmuxRunner(["list-clients"], signal)).length > 0;
1689
1881
  } catch {
1690
1882
  return null;
1691
1883
  }
1692
1884
  },
1693
1885
  listPanes: async (candidate, signal) => {
1694
1886
  try {
1695
- const raw = await execTmuxAsync(
1887
+ const raw = await fleetFactsTmuxRunner(
1696
1888
  [
1697
1889
  "list-panes",
1698
1890
  "-t",
@@ -1716,21 +1908,21 @@ async function startEmbeddedDaemonGeneration(
1716
1908
  },
1717
1909
  setPaneOption: async (paneId, option, value, signal) => {
1718
1910
  try {
1719
- await execTmuxAsync(["set-option", "-pt", paneId, option, value], signal);
1911
+ await fleetFactsTmuxRunner(["set-option", "-pt", paneId, option, value], signal);
1720
1912
  } catch (error) {
1721
1913
  if (!isConfirmedMissingTmuxTarget(error)) throw error;
1722
1914
  }
1723
1915
  },
1724
1916
  setPaneTitle: async (paneId, title, signal) => {
1725
1917
  try {
1726
- await execTmuxAsync(["select-pane", "-t", paneId, "-T", title], signal);
1918
+ await fleetFactsTmuxRunner(["select-pane", "-t", paneId, "-T", title], signal);
1727
1919
  } catch (error) {
1728
1920
  if (!isConfirmedMissingTmuxTarget(error)) throw error;
1729
1921
  }
1730
1922
  },
1731
1923
  refreshClients: async (signal) => {
1732
1924
  try {
1733
- await execTmuxAsync(["refresh-client", "-S"], signal);
1925
+ await fleetFactsTmuxRunner(["refresh-client", "-S"], signal);
1734
1926
  } catch (error) {
1735
1927
  if (!isConfirmedMissingTmuxTarget(error)) throw error;
1736
1928
  }
@@ -1744,7 +1936,21 @@ async function startEmbeddedDaemonGeneration(
1744
1936
  const wsUrl = canonicalDaemonUrl("ws", bindHostname, port, "/ws/events");
1745
1937
 
1746
1938
  const handle: EmbeddedDaemonHandle = {
1747
- tmuxAuthorityReplaced,
1939
+ tmuxAuthorityReplaced: async () => {
1940
+ if (serverOwners?.defaultRetired) return false;
1941
+ const replaced = await tmuxAuthorityReplaced();
1942
+ // A native server replacement cannot restart unrelated server owners.
1943
+ // Legacy default-only callers retain the established restart adapter.
1944
+ if (
1945
+ replaced &&
1946
+ serverOwners &&
1947
+ (serverOwners.owners.list().length > 1 || serverOwners.defaultRetired)
1948
+ ) {
1949
+ await serverOwners.owners.refresh();
1950
+ return false;
1951
+ }
1952
+ return replaced;
1953
+ },
1748
1954
  instanceId,
1749
1955
  pid: process.pid,
1750
1956
  port,
@@ -1769,6 +1975,7 @@ async function startEmbeddedDaemonGeneration(
1769
1975
  await capture(() => setActivationBackend(null));
1770
1976
  await capture(() => sessionMonitor?.stop());
1771
1977
  paneSourceCredentials.dispose();
1978
+ await capture(() => serverOwners?.dispose());
1772
1979
 
1773
1980
  // Retire direct-ticket admission, live PTYs, and the direct upgrade
1774
1981
  // listener before touching the legacy WS or HTTP surfaces.
@@ -1783,7 +1990,13 @@ async function startEmbeddedDaemonGeneration(
1783
1990
  await capture(() => workspacePaneCreation.dispose());
1784
1991
  await capture(() => workspaceOpen.dispose());
1785
1992
  await capture(() => workspaceOpenHandoff?.dispose());
1786
- await capture(() => workspacePromotion.dispose());
1993
+ await capture(() =>
1994
+ Promise.all([
1995
+ workspacePromotion.dispose(),
1996
+ defaultSessionOpener.dispose(),
1997
+ defaultSessionCreator.dispose(),
1998
+ ]).then(() => {}),
1999
+ );
1787
2000
  await capture(() => appWindowMutation.dispose());
1788
2001
  await capture(() => workspaceMultiplexer.dispose());
1789
2002
  await capture(() => externalInteractionObserver.dispose());