tmux-ide 2.9.2 → 2.9.4

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 (267) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +121 -140
  3. package/bin/cli.js +2457 -727
  4. package/package.json +23 -8
  5. package/packages/contracts/src/desktop-host.ts +3 -0
  6. package/packages/contracts/src/saved-machines.ts +19 -0
  7. package/packages/contracts/src/terminal-replica.ts +1 -1
  8. package/packages/core/package.json +3 -2
  9. package/packages/core/src/terminal-delivery.ts +51 -3
  10. package/packages/core/src/terminal-fnv64-wasm.ts +4 -3
  11. package/packages/core/src/terminal-replica-hash-cache.ts +129 -31
  12. package/packages/core/src/terminal-replica-owned-row.ts +27 -0
  13. package/packages/core/src/terminal-replica.ts +13 -28
  14. package/packages/daemon/dist/command-center/daemon-fleet-facts-observer.js +96 -10
  15. package/packages/daemon/dist/command-center/resources/saved-machine-route.js +40 -1
  16. package/packages/daemon/dist/command-center/ws-events.js +5 -24
  17. package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +8 -0
  18. package/packages/daemon/dist/lib/canonical-daemon.js +28 -2
  19. package/packages/daemon/dist/lib/daemon-embed.js +1 -1
  20. package/packages/daemon/dist/lib/daemon-service-manager.js +122 -0
  21. package/packages/daemon/dist/lib/daemon-service-plan.js +127 -0
  22. package/packages/daemon/dist/lib/daemon-service.js +259 -0
  23. package/packages/daemon/dist/lib/install-origin.js +2 -0
  24. package/packages/daemon/dist/lib/local-fleet-request.js +9 -3
  25. package/packages/daemon/dist/lib/log.js +6 -10
  26. package/packages/daemon/dist/lib/managed-installer-update.js +67 -0
  27. package/packages/daemon/dist/lib/{tui-download-lock.js → private-operation-lock.js} +11 -11
  28. package/packages/daemon/dist/lib/remote-daemon-info.js +3 -2
  29. package/packages/daemon/dist/lib/remote-daemon-lifecycle.js +4 -4
  30. package/packages/daemon/dist/lib/remote-tmux-command.js +10 -0
  31. package/packages/daemon/dist/lib/runtime-namespace.js +28 -16
  32. package/packages/daemon/dist/lib/saved-machines.js +40 -2
  33. package/packages/daemon/dist/lib/ssh-daemon-transport.js +24 -9
  34. package/packages/daemon/dist/lib/tmux-server-owner.js +1 -1
  35. package/packages/daemon/dist/lib/tmux-server-registration.js +21 -3
  36. package/packages/daemon/dist/lib/tui-binary.js +2 -2
  37. package/packages/daemon/dist/lib/update.js +18 -0
  38. package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +24 -1
  39. package/packages/daemon/dist/machines.js +40 -4
  40. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
  41. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
  42. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
  43. package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
  44. package/packages/daemon/dist/terminal/attachments/direct-websocket.js +4 -1
  45. package/packages/daemon/dist/terminal/attachments/native-runtime.js +22 -13
  46. package/packages/daemon/dist/terminal/mirror/mirror-service.js +101 -3
  47. package/packages/daemon/dist/terminal/mirror/registered-window-guard.js +62 -0
  48. package/packages/daemon/dist/terminal/mirror/session-channel.js +75 -4
  49. package/packages/daemon/dist/terminal/mirror/shared-window-index.js +45 -0
  50. package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +11 -1
  51. package/packages/daemon/dist/terminal/session-runtime/multiplexer-backend.js +1 -0
  52. package/packages/daemon/dist/terminal/session-runtime/registry.js +56 -7
  53. package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +3 -1
  54. package/packages/daemon/dist/tui/integrations/claude-agent-teams.js +264 -0
  55. package/packages/daemon/dist/tui/mirror/dialog-model.js +2 -35
  56. package/packages/daemon/dist/tui/mirror/runtime/application-daemon-authority-owner.js +8 -0
  57. package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +20 -2
  58. package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +7 -1
  59. package/packages/daemon/dist/tui/mirror/runtime/application-machine-authority.js +59 -9
  60. package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +27 -19
  61. package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +23 -18
  62. package/packages/daemon/dist/tui/mirror/runtime/application-machine-startup.js +31 -2
  63. package/packages/daemon/dist/tui/mirror/runtime/application-palette-search-owner.js +7 -3
  64. package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +3 -0
  65. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-input-ingress.js +146 -2
  66. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +201 -50
  67. package/packages/daemon/dist/tui/mirror/runtime/bounded-performance-record-writer.js +50 -10
  68. package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -5
  69. package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +3 -0
  70. package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +8 -2
  71. package/packages/daemon/dist/tui/mirror/terminal-text.js +35 -0
  72. package/packages/daemon/dist/tui/mirror/workspace/connection-feedback.js +5 -3
  73. package/packages/daemon/src/command-center/daemon-fleet-facts-observer.ts +100 -10
  74. package/packages/daemon/src/command-center/resources/saved-machine-route.ts +48 -1
  75. package/packages/daemon/src/command-center/ws-events.ts +5 -25
  76. package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +12 -0
  77. package/packages/daemon/src/lib/canonical-daemon.ts +44 -2
  78. package/packages/daemon/src/lib/daemon-embed.ts +1 -0
  79. package/packages/daemon/src/lib/daemon-service-manager.ts +152 -0
  80. package/packages/daemon/src/lib/daemon-service-plan.ts +153 -0
  81. package/packages/daemon/src/lib/daemon-service.ts +301 -0
  82. package/packages/daemon/src/lib/install-origin.ts +5 -0
  83. package/packages/daemon/src/lib/local-fleet-request.ts +19 -3
  84. package/packages/daemon/src/lib/log.ts +13 -9
  85. package/packages/daemon/src/lib/managed-installer-update.ts +79 -0
  86. package/packages/daemon/src/lib/{tui-download-lock.ts → private-operation-lock.ts} +15 -11
  87. package/packages/daemon/src/lib/remote-daemon-info.ts +3 -3
  88. package/packages/daemon/src/lib/remote-daemon-lifecycle.ts +4 -4
  89. package/packages/daemon/src/lib/remote-tmux-command.ts +12 -0
  90. package/packages/daemon/src/lib/runtime-namespace.ts +27 -18
  91. package/packages/daemon/src/lib/saved-machines.ts +40 -1
  92. package/packages/daemon/src/lib/ssh-daemon-transport.ts +25 -7
  93. package/packages/daemon/src/lib/tmux-server-owner.ts +1 -0
  94. package/packages/daemon/src/lib/tmux-server-registration.ts +21 -3
  95. package/packages/daemon/src/lib/tui-binary.ts +2 -2
  96. package/packages/daemon/src/lib/update.ts +24 -0
  97. package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +22 -0
  98. package/packages/daemon/src/machines.ts +53 -5
  99. package/packages/daemon/src/terminal/attachments/direct-websocket.ts +8 -2
  100. package/packages/daemon/src/terminal/attachments/native-runtime.ts +28 -13
  101. package/packages/daemon/src/terminal/mirror/mirror-service.ts +105 -3
  102. package/packages/daemon/src/terminal/mirror/registered-window-guard.ts +66 -0
  103. package/packages/daemon/src/terminal/mirror/session-channel.ts +92 -4
  104. package/packages/daemon/src/terminal/mirror/shared-window-index.ts +42 -0
  105. package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +12 -1
  106. package/packages/daemon/src/terminal/session-runtime/multiplexer-backend.ts +2 -0
  107. package/packages/daemon/src/terminal/session-runtime/registry.ts +70 -8
  108. package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +3 -1
  109. package/packages/daemon/src/tui/integrations/claude-agent-teams.ts +334 -0
  110. package/packages/daemon/src/tui/mirror/dialog-model.ts +2 -32
  111. package/packages/daemon/src/tui/mirror/runtime/application-daemon-authority-owner.ts +8 -0
  112. package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +19 -2
  113. package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +6 -1
  114. package/packages/daemon/src/tui/mirror/runtime/application-machine-authority.ts +60 -11
  115. package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +39 -33
  116. package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +31 -23
  117. package/packages/daemon/src/tui/mirror/runtime/application-machine-startup.ts +38 -2
  118. package/packages/daemon/src/tui/mirror/runtime/application-palette-search-owner.ts +7 -4
  119. package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +3 -0
  120. package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +1 -1
  121. package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +47 -46
  122. package/packages/daemon/src/tui/mirror/runtime/application-terminal-input-ingress.ts +165 -2
  123. package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +207 -53
  124. package/packages/daemon/src/tui/mirror/runtime/bounded-performance-record-writer.ts +49 -9
  125. package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +6 -5
  126. package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +4 -0
  127. package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +8 -3
  128. package/packages/daemon/src/tui/mirror/terminal-text.ts +32 -0
  129. package/packages/daemon/src/tui/mirror/workspace/connection-feedback.ts +6 -3
  130. package/packages/daemon-client/src/fleet-connection-status.ts +10 -1
  131. package/packages/daemon-client/src/workspace-client.ts +4 -1
  132. package/skill/SKILL.md +30 -30
  133. package/packages/contracts/src/__tests__/actions-contract.test.ts +0 -119
  134. package/packages/contracts/src/__tests__/agent-graph-overlay.test.ts +0 -428
  135. package/packages/contracts/src/__tests__/app-window-mutation.test.ts +0 -51
  136. package/packages/contracts/src/__tests__/app-window-state.test.ts +0 -34
  137. package/packages/contracts/src/__tests__/application-shell.test.ts +0 -645
  138. package/packages/contracts/src/__tests__/automation-operations.test.ts +0 -78
  139. package/packages/contracts/src/__tests__/causal-cell.test.ts +0 -47
  140. package/packages/contracts/src/__tests__/client-view-state.test.ts +0 -115
  141. package/packages/contracts/src/__tests__/cohesion-fixture.test.ts +0 -106
  142. package/packages/contracts/src/__tests__/commands.test.ts +0 -86
  143. package/packages/contracts/src/__tests__/control.test.ts +0 -154
  144. package/packages/contracts/src/__tests__/daemon-events.test.ts +0 -405
  145. package/packages/contracts/src/__tests__/daemon-resources.test.ts +0 -143
  146. package/packages/contracts/src/__tests__/daemon-wire.test.ts +0 -149
  147. package/packages/contracts/src/__tests__/desktop-host.test.ts +0 -269
  148. package/packages/contracts/src/__tests__/desktop-missions.test.ts +0 -215
  149. package/packages/contracts/src/__tests__/experience-conformance.test.ts +0 -153
  150. package/packages/contracts/src/__tests__/experience-kernel-boundary.test.ts +0 -113
  151. package/packages/contracts/src/__tests__/experience-shell.test.ts +0 -65
  152. package/packages/contracts/src/__tests__/fixtures/daemon-wire-v2.json +0 -35
  153. package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +0 -35
  154. package/packages/contracts/src/__tests__/fixtures/daemon-wire-v4.json +0 -35
  155. package/packages/contracts/src/__tests__/fixtures/scoped-receipt.ts +0 -62
  156. package/packages/contracts/src/__tests__/fleet-agent-graph.test.ts +0 -231
  157. package/packages/contracts/src/__tests__/fleet-catalog.test.ts +0 -270
  158. package/packages/contracts/src/__tests__/fleet-lifecycle.test.ts +0 -62
  159. package/packages/contracts/src/__tests__/focus-overlay.test.ts +0 -110
  160. package/packages/contracts/src/__tests__/ide-config.test.ts +0 -64
  161. package/packages/contracts/src/__tests__/interaction-evidence.test.ts +0 -288
  162. package/packages/contracts/src/__tests__/interaction-receipt-v2.test.ts +0 -72
  163. package/packages/contracts/src/__tests__/issue-error.test.ts +0 -78
  164. package/packages/contracts/src/__tests__/mission-projections.test.ts +0 -186
  165. package/packages/contracts/src/__tests__/missions.test.ts +0 -272
  166. package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +0 -242
  167. package/packages/contracts/src/__tests__/pane-appearance.test.ts +0 -146
  168. package/packages/contracts/src/__tests__/pane-read-result.test.ts +0 -55
  169. package/packages/contracts/src/__tests__/pane-stream.test.ts +0 -536
  170. package/packages/contracts/src/__tests__/pane-widget-marker.test.ts +0 -327
  171. package/packages/contracts/src/__tests__/performance-metrics.test.ts +0 -73
  172. package/packages/contracts/src/__tests__/performance-qualification.test.ts +0 -137
  173. package/packages/contracts/src/__tests__/rich-card-widget.test.ts +0 -29
  174. package/packages/contracts/src/__tests__/session-runtime.test.ts +0 -512
  175. package/packages/contracts/src/__tests__/startup-readiness.test.ts +0 -409
  176. package/packages/contracts/src/__tests__/terminal-attachment-stream.test.ts +0 -72
  177. package/packages/contracts/src/__tests__/terminal-attachments.test.ts +0 -329
  178. package/packages/contracts/src/__tests__/terminal-delivery.test.ts +0 -88
  179. package/packages/contracts/src/__tests__/terminal-runtime-inventory.test.ts +0 -68
  180. package/packages/contracts/src/__tests__/terminals.test.ts +0 -87
  181. package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +0 -166
  182. package/packages/contracts/src/__tests__/transport-contract-loading.test.ts +0 -91
  183. package/packages/contracts/src/__tests__/visual-tokens.test.ts +0 -224
  184. package/packages/contracts/src/__tests__/window-links.test.ts +0 -243
  185. package/packages/contracts/src/__tests__/workspace-changes-resource.test.ts +0 -317
  186. package/packages/contracts/src/__tests__/workspace-changes-view.test.ts +0 -122
  187. package/packages/contracts/src/__tests__/workspace-config.test.ts +0 -294
  188. package/packages/contracts/src/__tests__/workspace-files-resource.test.ts +0 -261
  189. package/packages/contracts/src/__tests__/workspace-files-tree.test.ts +0 -147
  190. package/packages/contracts/src/__tests__/workspace-pane-creation.test.ts +0 -249
  191. package/packages/contracts/src/__tests__/workspace-promotion.test.ts +0 -94
  192. package/packages/contracts/src/__tests__/workspace-resource-identity.test.ts +0 -90
  193. package/packages/contracts/src/daemon-resource-request.test.ts +0 -192
  194. package/packages/contracts/src/native-operation-identity.test.ts +0 -23
  195. package/packages/contracts/src/workspace-admission.test.ts +0 -21
  196. package/packages/contracts/src/workspace-catalog-resource.test.ts +0 -146
  197. package/packages/core/src/agent-provisioning.test.ts +0 -42
  198. package/packages/core/src/application-shell-session.test.ts +0 -105
  199. package/packages/core/src/interaction-evidence.test.ts +0 -134
  200. package/packages/core/src/interaction-flow.test.ts +0 -66
  201. package/packages/core/src/interaction-receipts.test.ts +0 -671
  202. package/packages/core/src/navigator.test.ts +0 -34
  203. package/packages/core/src/optimistic-projection-conformance.test.ts +0 -75
  204. package/packages/core/src/optimistic-projection.test.ts +0 -118
  205. package/packages/core/src/performance-metrics.test.ts +0 -121
  206. package/packages/core/src/performance-qualification.test.ts +0 -283
  207. package/packages/core/src/saved-machines.test.ts +0 -80
  208. package/packages/core/src/terminal-compact-cold.test.ts +0 -87
  209. package/packages/core/src/terminal-conformance.test.ts +0 -48
  210. package/packages/core/src/terminal-delivery-cache-lifetime.test.ts +0 -167
  211. package/packages/core/src/terminal-delivery-encode-cooperative.test.ts +0 -140
  212. package/packages/core/src/terminal-delivery-representation-hash.test.ts +0 -77
  213. package/packages/core/src/terminal-delivery.test.ts +0 -1471
  214. package/packages/core/src/terminal-fnv64-wasm.test.ts +0 -61
  215. package/packages/core/src/terminal-replica-cold.test.ts +0 -31
  216. package/packages/core/src/terminal-replica-cooperative.test.ts +0 -170
  217. package/packages/core/src/terminal-replica-hash-cache.test.ts +0 -283
  218. package/packages/core/src/terminal-replica.test.ts +0 -347
  219. package/packages/core/src/workspace-selection.test.ts +0 -63
  220. package/packages/daemon/dist/__tests__/support.js +0 -33
  221. package/packages/daemon/dist/lib/__tests__/fixtures/workspace-state-writer.js +0 -58
  222. package/packages/daemon/dist/lib/__tests__/installed-recovery-fixture.js +0 -400
  223. package/packages/daemon/dist/terminal/__tests__/MockPtyAdapter.js +0 -182
  224. package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +0 -98
  225. package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +0 -156
  226. package/packages/daemon/src/tui/mirror/__snapshots__/activity-surface-renderer.test.tsx.snap +0 -42
  227. package/packages/daemon/src/tui/mirror/__snapshots__/changes-terminal-surface-renderer.test.tsx.snap +0 -267
  228. package/packages/daemon/src/tui/mirror/__snapshots__/home-files-surface-renderer.test.tsx.snap +0 -267
  229. package/packages/daemon/src/tui/mirror/__snapshots__/missions-surface-renderer.test.tsx.snap +0 -134
  230. package/packages/daemon/src/tui/mirror/__snapshots__/recipes-gallery-renderer.test.tsx.snap +0 -141
  231. package/packages/daemon/src/tui/mirror/__snapshots__/shell-chrome-renderer.test.tsx.snap +0 -134
  232. package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +0 -87
  233. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/agent-terminal-canvas-renderer.test.tsx.snap +0 -348
  234. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/application-shell-renderer.test.tsx.snap +0 -134
  235. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/command-palette-surface-renderer.test.tsx.snap +0 -255
  236. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/pane-frame-renderer.test.tsx.snap +0 -525
  237. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/terminal-pane-header-polish-renderer.test.tsx.snap +0 -31
  238. package/packages/daemon/src/tui/mirror/workspace/__snapshots__/workbench-shell-renderer.test.tsx.snap +0 -134
  239. package/packages/daemon-client/src/application-shell-session.test.ts +0 -196
  240. package/packages/daemon-client/src/automation-client.test.ts +0 -214
  241. package/packages/daemon-client/src/bootstrap-coordinator.test.ts +0 -145
  242. package/packages/daemon-client/src/connection-supervisor.test.ts +0 -109
  243. package/packages/daemon-client/src/direct-application-shell-transport.test.ts +0 -941
  244. package/packages/daemon-client/src/first-latest-coordinator.test.ts +0 -175
  245. package/packages/daemon-client/src/fleet-dial-scheduler.test.ts +0 -114
  246. package/packages/daemon-client/src/fleet-environments.test.ts +0 -40
  247. package/packages/daemon-client/src/generation-bound-store-timers.test.ts +0 -74
  248. package/packages/daemon-client/src/owner-action-client.test.ts +0 -245
  249. package/packages/daemon-client/src/pane-stream-client.test.ts +0 -2841
  250. package/packages/daemon-client/src/pane-stream-clock-calibration.test.ts +0 -207
  251. package/packages/daemon-client/src/pane-stream-loopback-benchmark.test.ts +0 -51
  252. package/packages/daemon-client/src/push-resource-session.test.ts +0 -509
  253. package/packages/daemon-client/src/resource-replica.test.ts +0 -186
  254. package/packages/daemon-client/src/runtime-resource-ledger.test.ts +0 -18
  255. package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +0 -114
  256. package/packages/daemon-client/src/terminal-fast-lane.test.ts +0 -835
  257. package/packages/daemon-client/src/tmux-server-client.test.ts +0 -279
  258. package/packages/daemon-client/src/tmux-server-interaction-events.test.ts +0 -230
  259. package/packages/daemon-client/src/workspace-catalog-v2.test.ts +0 -109
  260. package/packages/daemon-client/src/workspace-client-boundaries.test.ts +0 -59
  261. package/packages/daemon-client/src/workspace-client-operations.test.ts +0 -96
  262. package/packages/daemon-client/src/workspace-client.test.ts +0 -2912
  263. package/packages/daemon-client/src/workspace-event-supervisor.test.ts +0 -1086
  264. package/packages/daemon-client/src/workspace-pane-client.test.ts +0 -80
  265. package/packages/presentation/src/presentation-contract.test.ts +0 -63
  266. package/packages/tmux-bridge/src/runner.test.ts +0 -614
  267. package/packages/tmux-bridge/src/targeting.test.ts +0 -107
@@ -6,21 +6,26 @@ export interface PerformanceRecordSink {
6
6
  export class BoundedPerformanceRecordWriter {
7
7
  readonly #sink: PerformanceRecordSink;
8
8
  readonly #criticalLimit: number;
9
- readonly #pendingCritical = new Map<string, string>();
9
+ readonly #pendingCritical = new Set<string>();
10
+ readonly #pending: { record: string; bytes: number; key?: string }[] = [];
11
+ readonly #pendingByteLimit: number;
12
+ #pendingBytes = 0;
13
+ #flushPromise: Promise<void> | null = null;
14
+ #resolveFlush: (() => void) | null = null;
10
15
  #saturated = false;
11
16
  #failed = false;
12
17
  #droppedRecords = 0;
13
18
 
14
- constructor(sink: PerformanceRecordSink, criticalLimit = 16) {
19
+ constructor(sink: PerformanceRecordSink, criticalLimit = 16, pendingByteLimit = 64 * 1024) {
15
20
  this.#sink = sink;
16
21
  this.#criticalLimit = criticalLimit;
22
+ this.#pendingByteLimit = pendingByteLimit;
17
23
  }
18
24
 
19
25
  write(record: string): boolean {
20
26
  if (this.#failed) return false;
21
27
  if (this.#saturated) {
22
- this.#droppedRecords += 1;
23
- return false;
28
+ return this.#enqueue(record);
24
29
  }
25
30
  return this.#writeAccepted(record);
26
31
  }
@@ -33,22 +38,57 @@ export class BoundedPerformanceRecordWriter {
33
38
  this.#droppedRecords += 1;
34
39
  return false;
35
40
  }
36
- this.#pendingCritical.set(key, record);
37
- return true;
41
+ return this.#enqueue(record, key);
38
42
  }
39
43
 
40
44
  drain(): void {
41
45
  if (this.#failed) return;
42
46
  this.#saturated = false;
43
- for (const [key, record] of this.#pendingCritical) {
44
- this.#pendingCritical.delete(key);
45
- if (!this.#writeAccepted(record)) break;
47
+ while (this.#pending.length > 0 && !this.#saturated && !this.#failed) {
48
+ const next = this.#pending.shift()!;
49
+ this.#pendingBytes -= next.bytes;
50
+ if (next.key !== undefined) this.#pendingCritical.delete(next.key);
51
+ this.#writeAccepted(next.record);
46
52
  }
53
+ if (this.#pending.length === 0) this.#finishFlush();
54
+ }
55
+
56
+ /** Wait until retained records reach the stream; its owner then flushes/end()s it. */
57
+ flush(): Promise<void> {
58
+ if (this.#failed || this.#pending.length === 0) return Promise.resolve();
59
+ this.#flushPromise ??= new Promise((resolve) => {
60
+ this.#resolveFlush = resolve;
61
+ });
62
+ return this.#flushPromise;
47
63
  }
48
64
 
49
65
  fail(): void {
50
66
  this.#failed = true;
51
67
  this.#pendingCritical.clear();
68
+ this.#pending.length = 0;
69
+ this.#pendingBytes = 0;
70
+ this.#finishFlush();
71
+ }
72
+
73
+ #enqueue(record: string, key?: string): boolean {
74
+ const bytes = Buffer.byteLength(record);
75
+ // Ordinary bursts get one extra stream-sized buffer. Critical records have
76
+ // a separate byte reserve as well as their existing key-count limit.
77
+ const limit = this.#pendingByteLimit + (key === undefined ? 0 : 64 * 1024);
78
+ if (this.#pending.length >= 1024 || this.#pendingBytes + bytes > limit) {
79
+ this.#droppedRecords += 1;
80
+ return false;
81
+ }
82
+ if (key !== undefined) this.#pendingCritical.add(key);
83
+ this.#pending.push({ record, bytes, ...(key === undefined ? {} : { key }) });
84
+ this.#pendingBytes += bytes;
85
+ return true;
86
+ }
87
+
88
+ #finishFlush(): void {
89
+ this.#resolveFlush?.();
90
+ this.#resolveFlush = null;
91
+ this.#flushPromise = null;
52
92
  }
53
93
 
54
94
  diagnostics(): Readonly<{
@@ -400,11 +400,12 @@ export class TerminalFastLaneRendererAdapter implements PaneScopedTerminalAdapte
400
400
  identity.acceptedRevision,
401
401
  ]);
402
402
  if (seen.has(key)) continue;
403
- if (seen.size >= 256) this.#droppedCanonicalHostFrames += 1;
404
- else {
405
- seen.add(key);
406
- identities.push(identity);
407
- }
403
+ // Bound retained de-duplication history, not the renderer's lifetime.
404
+ // An evicted identity may be reported again only after another real paint.
405
+ // Pending identities still have their separate per-frame admission limit.
406
+ if (seen.size >= 256) seen.delete(seen.values().next().value!);
407
+ seen.add(key);
408
+ identities.push(identity);
408
409
  }
409
410
  const dropped = this.#droppedCanonicalHostFrames;
410
411
  this.#droppedCanonicalHostFrames = 0;
@@ -27,6 +27,10 @@ export class TerminalPaneInputRouter<Input> {
27
27
  this.#options = options;
28
28
  }
29
29
 
30
+ get selectionVersion(): number {
31
+ return this.#selectionToken;
32
+ }
33
+
30
34
  get focusedPane(): string | null {
31
35
  return this.#focusedPane;
32
36
  }
@@ -28,6 +28,8 @@ export interface OpenTuiTerminalResourceSampler {
28
28
  dispose(): void;
29
29
  }
30
30
 
31
+ // Limit outstanding low-water sampling chains, not completed observations over
32
+ // the lifetime of a renderer. Each chain owns at most one pending timeout.
31
33
  const TERMINAL_RESOURCE_SAMPLE_LIMIT = 512;
32
34
  // The ProductRig idle proof reads after 10.1s. Emit just before that boundary
33
35
  // so the operation-scoped sample cannot race the proof read.
@@ -315,8 +317,7 @@ function createOpenTuiTerminalResourceSampler(
315
317
  }[];
316
318
  }>,
317
319
  ) => {
318
- if (disposed || resourceEpochIdentity === null || ordinal >= TERMINAL_RESOURCE_SAMPLE_LIMIT)
319
- return;
320
+ if (disposed || resourceEpochIdentity === null) return;
320
321
  ordinal += 1;
321
322
  const now = readDiagnosticNow();
322
323
  if (now === null) return;
@@ -474,7 +475,11 @@ function createOpenTuiTerminalResourceSampler(
474
475
  };
475
476
  return {
476
477
  afterFence(identity) {
477
- if (disposed || ordinal >= TERMINAL_RESOURCE_SAMPLE_LIMIT) return;
478
+ if (disposed) return;
479
+ if (pending.size >= TERMINAL_RESOURCE_SAMPLE_LIMIT) {
480
+ recordSamplingFailure();
481
+ return;
482
+ }
478
483
  latestIdentity = identity;
479
484
  let now: number | null;
480
485
  if (resourceEpochIdentity === null) {
@@ -52,3 +52,35 @@ function graphemes(text: string): string[] {
52
52
  );
53
53
  return [...text];
54
54
  }
55
+
56
+ /** PURE — greedy word-wrap for terminal prose (never returns empty for
57
+ * non-empty text; words longer than the width hard-break). */
58
+ export function wrapText(text: string, width: number): string[] {
59
+ if (width <= 0) return [text];
60
+ const out: string[] = [];
61
+ for (const para of text.split("\n")) {
62
+ let line = "";
63
+ for (const word of para.split(/\s+/).filter(Boolean)) {
64
+ if (line.length === 0) {
65
+ let w = word;
66
+ while (w.length > width) {
67
+ out.push(w.slice(0, width));
68
+ w = w.slice(width);
69
+ }
70
+ line = w;
71
+ } else if (line.length + 1 + word.length <= width) {
72
+ line += ` ${word}`;
73
+ } else {
74
+ out.push(line);
75
+ let w = word;
76
+ while (w.length > width) {
77
+ out.push(w.slice(0, width));
78
+ w = w.slice(width);
79
+ }
80
+ line = w;
81
+ }
82
+ }
83
+ if (line.length > 0 || para.length === 0) out.push(line);
84
+ }
85
+ return out.length > 0 ? out : [""];
86
+ }
@@ -167,9 +167,12 @@ export function createApplicationConnectionFeedback(
167
167
  failure,
168
168
  ...(inventory
169
169
  ? {
170
- recovery: missing
171
- ? "A registered session may have been recreated. Choose that session to restore its identity, then retry here. It may be a different session."
172
- : "Terminal identities conflict or cannot be verified. Copy connection details for diagnosis; Retry does not repair identity conflicts.",
170
+ recovery:
171
+ failure.reason === "duplicate-runtime-pane-binding"
172
+ ? "A terminal is shared by multiple workspace entries. Unlink shared tmux windows so each belongs to one session, then select Retry."
173
+ : missing
174
+ ? "A registered session may have been recreated. Choose that session to restore its identity, then retry here. It may be a different session."
175
+ : "Terminal identities conflict or cannot be verified. Copy connection details for diagnosis; Retry does not repair identity conflicts.",
173
176
  }
174
177
  : {}),
175
178
  };
@@ -5,6 +5,9 @@ export type FleetConnectionFailureCode =
5
5
  | "invalid-target"
6
6
  | "invalid-descriptor"
7
7
  | "incompatible"
8
+ | "ssh-authentication"
9
+ | "ssh-host-key"
10
+ | "remote-cli-missing"
8
11
  | "daemon-missing";
9
12
 
10
13
  export interface FleetConnectionStatus {
@@ -26,7 +29,13 @@ export function fleetConnectionMessage(status: FleetConnectionStatus): string {
26
29
  case "invalid-descriptor":
27
30
  return "Remote daemon discovery returned an invalid response. Check the installation.";
28
31
  case "daemon-missing":
29
- return "No running daemon. Start tmux-ide on this machine, then retry.";
32
+ return "No running daemon. Retrying automatically; start tmux-ide on this machine if needed.";
33
+ case "ssh-authentication":
34
+ return "SSH authentication failed. Check the machine's SSH credentials, then retry.";
35
+ case "ssh-host-key":
36
+ return "SSH host key verification failed. Verify the host in your terminal, then retry.";
37
+ case "remote-cli-missing":
38
+ return "Remote tmux-ide command not found. Check its installation and non-interactive SSH PATH.";
30
39
  case "unavailable":
31
40
  return "Connection failed. Check reachability, SSH authentication and host trust.";
32
41
  default:
@@ -1257,7 +1257,10 @@ export function createWorkspaceClient<
1257
1257
  startApplicationShell();
1258
1258
  if (authorityShell !== null) {
1259
1259
  if (!shellMatchesTerminalAuthority(authorityShell, resource))
1260
- requestTerminalMismatchRefresh(authorityShell, resource);
1260
+ // A newer terminal revision has already established topology. Read
1261
+ // its presentation/refusal details instead of refreshing that same
1262
+ // terminal revision again while leaving the shell stale.
1263
+ shellSession?.refresh();
1261
1264
  }
1262
1265
  return true;
1263
1266
  },
package/skill/SKILL.md CHANGED
@@ -2,12 +2,14 @@
2
2
 
3
3
  <!-- tmux-ide-skill-version: 2.6.0 -->
4
4
 
5
- tmux-ide is a **dock around tmux**: one command adds a native chrome to any tmux
6
- session — a fleet of tabs with live agent-status glyphs, ground-truth
7
- working/blocked/done detection, notifications when an agent needs a human, and a
8
- crash-proof restore. It's built _around_ tmux (adopt is additive tmux config, no
9
- wrapper process), and the whole UI is one keystroke away — one interaction
10
- grammar, one theme file. `.tmux-ide/workspace.yml` is optional; adopt works on any session.
5
+ tmux-ide is **the open-source workspace for coding agents**, built on tmux. The
6
+ app (`tmux-ide app`) shows every agent across local and SSH machines with
7
+ ground-truth working/blocked/done status, and drives the live tmux session.
8
+ `tmux-ide adopt` adds the same status to plain tmux clients as tmux chrome: a
9
+ status bar, keys and menus, all additive tmux options with no wrapper process.
10
+ Around both: notifications when an agent needs a human, and crash-proof restore.
11
+ tmux owns every process and pane, so sessions survive tmux-ide. `.tmux-ide/workspace.yml`
12
+ is optional.
11
13
 
12
14
  ## When to use
13
15
 
@@ -206,30 +208,28 @@ Right-click any pane or the bar opens the actions menu at the pointer.
206
208
  | Panels — explorer / changes / config | `prefix e` `g` `v` | `⌥e` `⌥g` `⌥,` |
207
209
 
208
210
  One interaction grammar everywhere: `j`/`k` move, `enter` opens, `/` filters,
209
- `esc` backs out, `?` asks. Bare `tmux-ide` with no project config opens the **home
210
- cockpit** (the fleet home screen). `tmux-ide cheatsheet` prints the full sheet.
211
-
212
- ## The app — `tmux-ide app` (the terminal IDE)
213
-
214
- v2.7 adds a full-screen unified app: tmux stays the engine (PTYs, agents,
215
- persistence); the app is the IDE around it. Launch `tmux-ide app` (bare = home
216
- screen) or `tmux-ide app <session>`. Needs `bun`, or a downloaded binary:
217
- `tmux-ide update --tui-binary`.
218
-
219
- - **Tabs** `F1`–`F4`: Home (fleet, pick a session = set the workspace) ·
220
- Terminal (the session mirrored live — it keeps streaming while you're on
221
- other tabs) · Files (tree + built-in editor: `^s` save, `^z` undo, click to
222
- place the cursor) · Diff (colored working-tree changes, `^e` opens the file
223
- in the editor). `F5` = command palette (fuzzy everything).
224
- - **Mouse-native**: hover highlights; right-click = context menus (split/zoom/
225
- kill panes, layouts, synchronize-panes, kill/rename sessions & windows —
226
- destructive actions confirm); drag pane borders to resize; drag-select text →
227
- clipboard via OSC52 (works through ssh); scrollbars; clickable buttons.
228
- - **tmux parity**: `[⛶]` zoom, window verbs, layout presets, `/` scrollback
229
- search with `n`/`N`, paste-buffer picker. `^q` quits — the session is
230
- untouched, like you were never there.
231
- - State persists across launches (~/.tmux-ide/app-state.json): last tab,
232
- session, open file.
211
+ `esc` backs out, `?` asks. Bare `tmux-ide` with no project config opens the
212
+ **app** (below). `tmux-ide cheatsheet` prints the full sheet.
213
+
214
+ ## The app — `tmux-ide app`
215
+
216
+ The full-screen app: tmux stays the engine (PTYs, agents, persistence); the app
217
+ renders it. Launch `tmux-ide app` (Home) or `tmux-ide app <session>`. Installed
218
+ releases include the runtime; `tmux-ide update --tui-binary` re-downloads it.
219
+
220
+ - **Two surfaces**: `F1` Home (agents across local + SSH machines; `/` search,
221
+ `f` machine filter, `0`/`w`/`a` All/Working/Needs attention, `Enter` opens the
222
+ agent's pane) and `F2` Terminals (the session mirrored live, window tabs, pane
223
+ headers with agent state). There are no Files/Diff/Missions views in 2.9, and
224
+ `app.views` in workspace.yml is validated but not read.
225
+ - **Overlays**: `F5` Commands (new window, New agent…, splits, zoom, Appearance…,
226
+ help) · `F6` Sessions · `F7` Attention · `F8`/`F9` session history/tabs ·
227
+ `F10` sidebar · `Ctrl+G` focus sidebar. `Ctrl+K` in Commands lists every key.
228
+ - **Terminals**: `Ctrl+O`/`Ctrl+T` next pane/window, `Alt+Arrow` resize, drag
229
+ borders; right-click a pane for select text / rename / split / zoom / close
230
+ (confirmed). Shift+click opens links; Shift+drag selects inside mouse apps.
231
+ - `Ctrl+Q` quits — sessions keep running. `tmux-ide app --detachable` hosts the
232
+ app in tmux so `Ctrl+Q` detaches instead.
233
233
 
234
234
  ## .tmux-ide/workspace.yml (optional)
235
235
 
@@ -1,119 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { ActionContractsZ } from "../actions-contract.ts";
3
-
4
- describe("daemon action contract", () => {
5
- it("supports generation-pinned cooperative shutdown", () => {
6
- const instanceId = "9bcf33b0-c837-4a94-b5e8-c0977f54464f";
7
-
8
- expect(
9
- ActionContractsZ["daemon.shutdown"].input.parse({
10
- reason: "takeover",
11
- expectedInstanceId: instanceId,
12
- }),
13
- ).toEqual({ reason: "takeover", expectedInstanceId: instanceId });
14
- expect(
15
- ActionContractsZ["daemon.shutdown"].input.safeParse({
16
- expectedInstanceId: "not-a-uuid",
17
- }).success,
18
- ).toBe(false);
19
- });
20
-
21
- it("accepts only renderer-safe semantic workspace pane creation", () => {
22
- const input = { kind: "terminal", workspaceName: "workspace.alpha" } as const;
23
- expect(ActionContractsZ["workspace.pane.create"].input.parse(input)).toEqual(input);
24
- for (const [field, value] of [
25
- ["cwd", "/tmp/project"],
26
- ["argv", ["sh", "-c", "owned"]],
27
- ["env", { SECRET: "renderer" }],
28
- ["paneId", "%42"],
29
- ["sessionName", "runtime-session"],
30
- ] as const) {
31
- expect(
32
- ActionContractsZ["workspace.pane.create"].input.safeParse({
33
- ...input,
34
- [field]: value,
35
- }).success,
36
- ).toBe(false);
37
- }
38
- });
39
-
40
- it("renames panes through semantic identity only", () => {
41
- const input = {
42
- workspaceName: "workspace.alpha",
43
- scope: "pane",
44
- semanticPaneId: "pane.main",
45
- name: "Build monitor",
46
- } as const;
47
- expect(ActionContractsZ["workspace.rename"].input.parse(input)).toEqual(input);
48
- expect(
49
- ActionContractsZ["workspace.rename"].input.safeParse({
50
- ...input,
51
- semanticPaneId: "%42",
52
- }).success,
53
- ).toBe(false);
54
- });
55
-
56
- it("keeps config-free workspace admission semantic and its result browser-safe", () => {
57
- expect(ActionContractsZ["workspace.open"].input.parse({ projectDir: "/tmp/project" })).toEqual({
58
- projectDir: "/tmp/project",
59
- });
60
- for (const [field, value] of [
61
- ["sessionName", "renderer-session"],
62
- ["paneId", "%42"],
63
- ["command", ["sh", "-c", "owned"]],
64
- ["env", { SECRET: "renderer" }],
65
- ] as const) {
66
- expect(
67
- ActionContractsZ["workspace.open"].input.safeParse({
68
- projectDir: "/tmp/project",
69
- [field]: value,
70
- }).success,
71
- ).toBe(false);
72
- }
73
-
74
- const result = ActionContractsZ["workspace.open"].result.parse({
75
- operationId: "10000000-0000-4000-8000-000000000001",
76
- daemonInstanceId: "20000000-0000-4000-8000-000000000002",
77
- outcome: "created",
78
- resource: {
79
- resourceVersion: 1,
80
- workspaceName: "project-00112233445566778899aabbccddeeff",
81
- initialPaneId: "pane.workspace.00112233445566778899aabbccddeeff",
82
- },
83
- });
84
- expect(JSON.stringify(result)).not.toMatch(/projectDir|sessionName|runtime|tmux|path/u);
85
- });
86
-
87
- it("lets the trusted desktop host select a folder without exposing its path", () => {
88
- const input = {
89
- source: { kind: "host-selection" },
90
- previousWorkspaceName: "workspace.alpha",
91
- } as const;
92
- expect(ActionContractsZ["workspace.open.prepare"].input.parse(input)).toEqual(input);
93
- expect(
94
- ActionContractsZ["workspace.open.prepare"].input.safeParse({
95
- ...input,
96
- source: { kind: "host-selection", projectDir: "/private/project" },
97
- }).success,
98
- ).toBe(false);
99
- });
100
-
101
- it("accepts only semantic pane identities for a swap", () => {
102
- const input = {
103
- workspaceName: "workspace.alpha",
104
- sourceSemanticPaneId: "pane.source",
105
- targetSemanticPaneId: "pane.target",
106
- } as const;
107
- expect(ActionContractsZ["workspace.pane.swap"].input.parse(input)).toEqual(input);
108
- for (const unsafe of [
109
- { sourcePaneId: "%1" },
110
- { targetPaneId: "%2" },
111
- { tmuxCommand: "swap-pane -D" },
112
- { sessionName: "runtime-session" },
113
- ]) {
114
- expect(
115
- ActionContractsZ["workspace.pane.swap"].input.safeParse({ ...input, ...unsafe }).success,
116
- ).toBe(false);
117
- }
118
- });
119
- });