@brimveyn/aimux 1.21.0 → 1.22.2

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 (220) hide show
  1. package/README.md +27 -25
  2. package/package.json +2 -2
  3. package/src/app-runtime/auto-commit-driver.ts +22 -27
  4. package/src/app-runtime/backend-attach-runtime.ts +43 -36
  5. package/src/app-runtime/backend-runtime-events.ts +107 -88
  6. package/src/app-runtime/editor-actions.ts +201 -0
  7. package/src/app-runtime/git-actions.ts +197 -0
  8. package/src/app-runtime/measure-ref.ts +19 -0
  9. package/src/app-runtime/navigation-actions.ts +213 -0
  10. package/src/app-runtime/project-actions.ts +188 -0
  11. package/src/app-runtime/prompt-injection.ts +123 -0
  12. package/src/app-runtime/selection.ts +29 -0
  13. package/src/app-runtime/settings-actions.ts +156 -0
  14. package/src/app-runtime/setup-actions.ts +237 -0
  15. package/src/app-runtime/side-effect-context.ts +33 -0
  16. package/src/app-runtime/side-effects.ts +302 -1541
  17. package/src/app-runtime/snippet-actions.ts +3 -2
  18. package/src/app-runtime/tab-actions.ts +218 -0
  19. package/src/app-runtime/tab-runtime-timeouts.ts +1 -1
  20. package/src/app-runtime/use-auto-commit-driver.ts +14 -13
  21. package/src/app-runtime/use-backend-runtime.ts +12 -11
  22. package/src/app-runtime/use-directory-search.ts +5 -4
  23. package/src/app-runtime/use-mouse-handlers.ts +2 -1
  24. package/src/app-runtime/{use-workspace-autosave.ts → use-project-autosave.ts} +4 -4
  25. package/src/app-runtime/use-renderer-bindings.ts +2 -1
  26. package/src/app-runtime/use-setup-runner.ts +96 -0
  27. package/src/app-runtime/use-terminal-resize.ts +18 -14
  28. package/src/app-runtime/workspace-actions.ts +366 -0
  29. package/src/app-runtime/workspace-activity.ts +138 -0
  30. package/src/app-runtime/workspace-naming.ts +90 -0
  31. package/src/app.tsx +93 -48
  32. package/src/assets/sounds/bell.wav +0 -0
  33. package/src/assets/sounds/ding.wav +0 -0
  34. package/src/assets/sounds/plane.wav +0 -0
  35. package/src/cli/client/daemon-client.ts +2 -2
  36. package/src/cli/client/project-resolver.ts +112 -0
  37. package/src/cli/commands/project/close.ts +32 -0
  38. package/src/cli/commands/project/create.ts +93 -0
  39. package/src/cli/commands/project/list.ts +25 -0
  40. package/src/cli/commands/project/show.ts +32 -0
  41. package/src/cli/commands/{workspace → project}/switch.ts +19 -19
  42. package/src/cli/commands/tab/await.ts +2 -2
  43. package/src/cli/commands/tab/close.ts +3 -3
  44. package/src/cli/commands/tab/create.ts +74 -73
  45. package/src/cli/commands/tab/focus.ts +3 -3
  46. package/src/cli/commands/tab/list.ts +6 -6
  47. package/src/cli/commands/tab/run.ts +2 -2
  48. package/src/cli/commands/tab/send.ts +2 -2
  49. package/src/cli/commands/tab/snapshot.ts +2 -2
  50. package/src/cli/commands/tab/tail.ts +2 -2
  51. package/src/cli/commands/tab/wait.ts +2 -2
  52. package/src/cli/commands/worker/await.ts +3 -3
  53. package/src/cli/commands/worker/doctor.ts +30 -30
  54. package/src/cli/commands/worker/list.ts +15 -15
  55. package/src/cli/commands/worker/prompt.ts +3 -3
  56. package/src/cli/commands/worker/run.ts +23 -23
  57. package/src/cli/commands/worker/shared.ts +53 -59
  58. package/src/cli/commands/worker/stop.ts +29 -29
  59. package/src/cli/commands/worker/submit.ts +3 -3
  60. package/src/cli/commands/{worktree → workspace}/create-core.ts +26 -26
  61. package/src/cli/commands/workspace/create.ts +30 -68
  62. package/src/cli/commands/workspace/list.ts +52 -9
  63. package/src/cli/commands/workspace/remove.ts +81 -0
  64. package/src/cli/completion/plan.ts +2 -2
  65. package/src/cli/completion/sources.ts +18 -18
  66. package/src/cli/context.ts +13 -13
  67. package/src/cli/flags.ts +30 -10
  68. package/src/cli/index.ts +26 -16
  69. package/src/cli/output.ts +1 -1
  70. package/src/cli/registry.ts +12 -12
  71. package/src/config/loader.ts +16 -5
  72. package/src/config.ts +70 -112
  73. package/src/daemon/catalog-writer.ts +67 -67
  74. package/src/daemon/daemon.ts +205 -201
  75. package/src/daemon/session-manager.ts +39 -39
  76. package/src/daemon/session-registry.ts +14 -14
  77. package/src/git/divergence.ts +33 -2
  78. package/src/git/git-poller.ts +1 -1
  79. package/src/git/git-status.ts +2 -2
  80. package/src/git/{move-worktree.ts → move-workspace.ts} +5 -5
  81. package/src/git/pr-status.ts +1 -1
  82. package/src/git/repo-discovery.ts +1 -1
  83. package/src/git/use-repo-discovery.ts +1 -1
  84. package/src/git/workspace-branch-poller.ts +54 -0
  85. package/src/git/workspace-divergence-poller.ts +65 -0
  86. package/src/git/worktree.ts +29 -0
  87. package/src/index.tsx +4 -4
  88. package/src/input/keymap/help-entries.ts +7 -5
  89. package/src/input/modes/bridge.ts +13 -12
  90. package/src/input/modes/handlers/shared.ts +1 -1
  91. package/src/input/modes/transitions.ts +34 -21
  92. package/src/input/modes/types.ts +48 -31
  93. package/src/ipc/manager-protocol.ts +44 -44
  94. package/src/ipc/protocol.ts +192 -166
  95. package/src/platform/play-sound.ts +184 -0
  96. package/src/platform/project-search.ts +8 -8
  97. package/src/platform/worktree-paths.ts +8 -6
  98. package/src/pty/assistant-question-extractor.ts +1 -1
  99. package/src/pty/assistant-status-detection-loop.ts +79 -55
  100. package/src/pty/assistant-status-detector.ts +40 -15
  101. package/src/pty/command-registry.ts +81 -1
  102. package/src/restart-terminal-manager.ts +1 -1
  103. package/src/services/ai-usage/provider.ts +9 -2
  104. package/src/services/ai-usage/spawn.ts +19 -6
  105. package/src/session-backend/bootstrap.ts +4 -4
  106. package/src/session-backend/local-session-backend.ts +78 -75
  107. package/src/session-backend/remote-session-backend.ts +64 -52
  108. package/src/session-backend/types.ts +43 -32
  109. package/src/settings/live.ts +90 -0
  110. package/src/settings/search.ts +43 -0
  111. package/src/settings/sections/about.ts +49 -0
  112. package/src/settings/sections/appearance.ts +54 -0
  113. package/src/settings/sections/automation.ts +105 -0
  114. package/src/settings/sections/commands.ts +72 -0
  115. package/src/settings/sections/editor.ts +56 -0
  116. package/src/settings/sections/experimental.ts +57 -0
  117. package/src/settings/sections/git.ts +112 -0
  118. package/src/settings/sections/index.ts +95 -0
  119. package/src/settings/sections/integrations.ts +23 -0
  120. package/src/settings/sections/layout.ts +72 -0
  121. package/src/settings/sections/notifications.ts +99 -0
  122. package/src/settings/sections/setup.ts +54 -0
  123. package/src/settings/sections/status-bar.ts +63 -0
  124. package/src/settings/settings-store.ts +207 -0
  125. package/src/settings/types.ts +108 -0
  126. package/src/snippets/run-shell-var.ts +48 -22
  127. package/src/state/actions.ts +342 -0
  128. package/src/state/app-store.ts +2 -1
  129. package/src/state/bars.ts +1 -1
  130. package/src/state/dispatch-ref.ts +1 -1
  131. package/src/state/layout-resize.ts +2 -2
  132. package/src/state/project-catalog.ts +275 -0
  133. package/src/state/project-data.ts +143 -0
  134. package/src/state/{session-persistence.ts → project-persistence.ts} +74 -68
  135. package/src/state/project-save.ts +46 -0
  136. package/src/state/project-workspaces.ts +392 -0
  137. package/src/state/reducers/auto-commit-state.ts +11 -10
  138. package/src/state/reducers/git-commit-modal-state.ts +122 -0
  139. package/src/state/reducers/git-mode-state.ts +1 -1
  140. package/src/state/reducers/git-panel-state.ts +10 -6
  141. package/src/state/reducers/modal-state.ts +290 -681
  142. package/src/state/reducers/multi-repo-state.ts +3 -6
  143. package/src/state/reducers/project-state.ts +312 -0
  144. package/src/state/reducers/settings-state.ts +80 -0
  145. package/src/state/reducers/tab-state.ts +107 -60
  146. package/src/state/reducers/ui-state.ts +4 -3
  147. package/src/state/selectors.ts +49 -37
  148. package/src/state/store.ts +57 -24
  149. package/src/state/types.ts +244 -405
  150. package/src/state/validation.ts +14 -12
  151. package/src/terminal-manager/manager-client.ts +32 -32
  152. package/src/terminal-manager/terminal-manager.ts +24 -24
  153. package/src/ui/components/git/git-panel.tsx +2 -2
  154. package/src/ui/components/git/git-view.tsx +12 -12
  155. package/src/ui/components/git/image-diff/image-diff-view.tsx +1 -1
  156. package/src/ui/components/git/pane/git-pane-widget.tsx +7 -7
  157. package/src/ui/components/layout/sidebar/project-list.tsx +444 -0
  158. package/src/ui/components/layout/sidebar/tab-item.tsx +27 -12
  159. package/src/ui/components/layout/sidebar/use-sidebar-auto-scroll.ts +1 -1
  160. package/src/ui/components/layout/sidebar/workspace-row.tsx +253 -0
  161. package/src/ui/components/layout/status-bar.tsx +6 -2
  162. package/src/ui/components/layout/terminal-pane.tsx +34 -29
  163. package/src/ui/components/layout/top-tab-bar.tsx +46 -36
  164. package/src/ui/components/modals/git/git-commit-modal.tsx +8 -8
  165. package/src/ui/components/modals/{sessions/create-session-modal.tsx → projects/create-project-modal.tsx} +12 -12
  166. package/src/ui/components/modals/{sessions/session-name-modal.tsx → projects/project-name-modal.tsx} +2 -2
  167. package/src/ui/components/modals/{sessions/session-picker-modal.tsx → projects/project-picker-modal.tsx} +33 -35
  168. package/src/ui/components/modals/settings/settings-search-modal.tsx +70 -0
  169. package/src/ui/components/modals/shared/form.tsx +34 -22
  170. package/src/ui/components/modals/shared/picker.tsx +10 -10
  171. package/src/ui/components/modals/shared/{worktree-delete-confirm.tsx → workspace-delete-confirm.tsx} +9 -9
  172. package/src/ui/components/modals/tabs/new-tab-modal.tsx +30 -324
  173. package/src/ui/components/modals/workspace/create-workspace-modal.tsx +101 -0
  174. package/src/ui/components/modals/{worktree/worktree-move-confirm-modal.tsx → workspace/workspace-move-confirm-modal.tsx} +5 -5
  175. package/src/ui/components/modals/{worktree/worktree-move-modal.tsx → workspace/workspace-move-modal.tsx} +29 -27
  176. package/src/ui/components/primitives/input-field.tsx +7 -3
  177. package/src/ui/components/primitives/surface.tsx +3 -0
  178. package/src/ui/components/settings/row-value.tsx +74 -0
  179. package/src/ui/components/settings/settings-row.tsx +78 -0
  180. package/src/ui/components/settings/settings-view.tsx +169 -0
  181. package/src/ui/components/setup/setup-widget.tsx +152 -0
  182. package/src/ui/flash/build-labels.ts +27 -27
  183. package/src/ui/hooks/use-activity-sprite.ts +73 -0
  184. package/src/ui/hooks/use-scroll-active-into-view.ts +26 -0
  185. package/src/ui/{session-ordering.ts → project-ordering.ts} +4 -4
  186. package/src/ui/root.tsx +139 -121
  187. package/src/ui/status-bar-model.ts +48 -34
  188. package/src/ui/terminal-graphics/kitty.ts +28 -2
  189. package/src/ui/terminal-graphics/sprites/done@330/0.png +0 -0
  190. package/src/ui/terminal-graphics/sprites/done@330/1.png +0 -0
  191. package/src/ui/terminal-graphics/sprites/idle@1000/0.png +0 -0
  192. package/src/ui/terminal-graphics/sprites/idle@1000/1.png +0 -0
  193. package/src/ui/terminal-graphics/sprites/waiting@170/0.png +0 -0
  194. package/src/ui/terminal-graphics/sprites/waiting@170/1.png +0 -0
  195. package/src/ui/terminal-graphics/sprites/working@150/0.png +0 -0
  196. package/src/ui/terminal-graphics/sprites/working@150/1.png +0 -0
  197. package/src/ui/terminal-graphics/sprites/working@150/2.png +0 -0
  198. package/src/ui/terminal-graphics/sprites/working@150/3.png +0 -0
  199. package/src/ui/terminal-graphics/sprites.ts +246 -0
  200. package/src/ui/truncate.ts +7 -0
  201. package/src/ui/widgets/registry.tsx +6 -3
  202. package/src/ui/widgets/widget-context-menu.ts +26 -10
  203. package/src/update.ts +2 -2
  204. package/src/app-runtime/session-actions.ts +0 -187
  205. package/src/cli/client/workspace-resolver.ts +0 -110
  206. package/src/cli/commands/workspace/close.ts +0 -32
  207. package/src/cli/commands/workspace/show.ts +0 -32
  208. package/src/cli/commands/worktree/create.ts +0 -55
  209. package/src/cli/commands/worktree/list.ts +0 -68
  210. package/src/cli/commands/worktree/remove.ts +0 -81
  211. package/src/git/worktree-branch-poller.ts +0 -54
  212. package/src/git/worktree-divergence-poller.ts +0 -59
  213. package/src/state/reducers/session-state.ts +0 -262
  214. package/src/state/session-catalog.ts +0 -143
  215. package/src/state/session-worktrees.ts +0 -260
  216. package/src/state/workspace-save.ts +0 -43
  217. package/src/ui/components/layout/sidebar/use-top-tab-bar-auto-scroll.ts +0 -30
  218. package/src/ui/components/layout/sidebar/workspace-list.tsx +0 -418
  219. package/src/ui/components/layout/sidebar/worktree-row.tsx +0 -149
  220. /package/src/ui/{components/git/image-diff → terminal-graphics}/dimensions.ts +0 -0
@@ -1,17 +1,17 @@
1
1
  import type {
2
2
  AssistantId,
3
+ ProjectSnapshotV1,
4
+ ProjectStatus,
3
5
  QuestionKind,
4
- SessionStatus,
5
6
  TabActivity,
6
7
  TabSession,
7
8
  TabStatus,
8
9
  TerminalModeState,
9
10
  TerminalSnapshot,
10
- WorkspaceSnapshotV1,
11
- WorktreeRecord,
11
+ WorkspaceRecord,
12
12
  } from '../state/types'
13
13
 
14
- import { isWorkspaceSnapshotV1, isWorktreeRecord } from '../state/validation'
14
+ import { isProjectSnapshotV1, isWorkspaceRecord } from '../state/validation'
15
15
 
16
16
  // v9: scroll position is owned entirely by the backend emulator. Dropped the
17
17
  // per-tab scroll `intent`/`intents` from resize messages and removed the
@@ -22,13 +22,13 @@ import { isWorkspaceSnapshotV1, isWorktreeRecord } from '../state/validation'
22
22
  // v11: additive — exposes `listTabs` (read-only enumeration without `attach`),
23
23
  // `attach.thin` (skip server-side resize so a headless CLI can attach
24
24
  // alongside a UI without resizing PTYs), and `createTab` cols/rows=0 fallback
25
- // to the session's last attached size. All three are gated behind
25
+ // to the project's last attached size. All three are gated behind
26
26
  // capabilities; MIN stays at 10 so a v10 UI keeps talking to a v11 daemon
27
27
  // (and vice-versa).
28
28
  //
29
- // v12: additive — workspace lifecycle requests (`createWorkspace`,
30
- // `switchWorkspace`, `closeWorkspace`, `announceWorkspaceSwitched`) and
31
- // worktree record requests (`addWorktreeRecord`, `removeWorktreeRecord`),
29
+ // v12: additive — project lifecycle requests (`createProject`,
30
+ // `switchProject`, `closeProject`, `announceProjectSwitched`) and
31
+ // workspace record requests (`addWorkspaceRecord`, `removeWorkspaceRecord`),
32
32
  // plus matching broadcast events. All capability-gated; MIN stays at 10.
33
33
  //
34
34
  // v13: additive — agent-orchestration signals. Two new broadcast events,
@@ -41,13 +41,19 @@ import { isWorkspaceSnapshotV1, isWorktreeRecord } from '../state/validation'
41
41
  // v14: additive — tab metadata synchronization for manual and automatic
42
42
  // renames. The new request/event are capability-gated; MIN stays at 10.
43
43
  //
44
- // v15: additive — workspace-scoped `workerName` metadata on create, attach,
44
+ // v15: additive — project-scoped `workerName` metadata on create, attach,
45
45
  // and list results. This powers stable name-or-id orchestration selectors.
46
46
  //
47
47
  // v16: breaking release boundary — the agent-first worker control plane and
48
48
  // its metadata guarantees are now required. Old app/daemon pairs must not mix.
49
- export const IPC_PROTOCOL_MIN_VERSION = 16
50
- export const IPC_PROTOCOL_VERSION = 16
49
+ //
50
+ // v18: additive an optional `workspaceId` on the `tabStatus` and
51
+ // `tabTurnComplete` broadcasts. It needs no capability: a client that receives
52
+ // it attributes the tab to a workspace row, and one that doesn't (or a v17
53
+ // daemon that never sends it) simply has no workspace-level indicator. MIN
54
+ // stays at 17 so a running v17 daemon keeps serving until it is restarted.
55
+ export const IPC_PROTOCOL_MIN_VERSION = 17
56
+ export const IPC_PROTOCOL_VERSION = 18
51
57
 
52
58
  /**
53
59
  * Capability advertised by a daemon that knows how to drain + handoff its
@@ -72,7 +78,7 @@ export const IPC_CAPABILITY_THIN_ATTACH = 'thinAttach'
72
78
 
73
79
  /**
74
80
  * Capability gating `createTab` with `cols: 0` or `rows: 0` meaning "use the
75
- * session's last attached size". Older daemons would propagate 0 straight to
81
+ * project's last attached size". Older daemons would propagate 0 straight to
76
82
  * the TM and spawn a zero-sized PTY, so the client only sends zeroes when
77
83
  * this capability is advertised.
78
84
  */
@@ -80,7 +86,7 @@ export const IPC_CAPABILITY_CREATE_TAB_SIZE_FALLBACK = 'createTabSizeFallback'
80
86
 
81
87
  /**
82
88
  * Capability gating the `tabAdded` server event. When advertised, the daemon
83
- * fans a `tabAdded` event out to every socket attached to the session after
89
+ * fans a `tabAdded` event out to every socket attached to the project after
84
90
  * a successful `createTab` — that's how a UI process learns about tabs
85
91
  * created by a sibling CLI invocation. Pre-cap UIs simply never see the
86
92
  * event and continue to discover tabs only via the next `attachResult`.
@@ -88,30 +94,30 @@ export const IPC_CAPABILITY_CREATE_TAB_SIZE_FALLBACK = 'createTabSizeFallback'
88
94
  export const IPC_CAPABILITY_TAB_LIFECYCLE_EVENTS = 'tabLifecycleEvents'
89
95
 
90
96
  /**
91
- * Capability gating the optional `worktreeId` on `createTab` payloads. With
92
- * it, the CLI can spawn a tab inside a specific worktree of the active
93
- * session so the UI groups it correctly. Older daemons ignore the field
97
+ * Capability gating the optional `workspaceId` on `createTab` payloads. With
98
+ * it, the CLI can spawn a tab inside a specific workspace of the active
99
+ * project so the UI groups it correctly. Older daemons ignore the field
94
100
  * (the parser accepts it but the TM has no knob for it pre-bump).
95
101
  */
96
- export const IPC_CAPABILITY_CREATE_TAB_WORKTREE_ID = 'createTabWorktreeId'
102
+ export const IPC_CAPABILITY_CREATE_TAB_WORKSPACE_ID = 'createTabWorkspaceId'
97
103
 
98
104
  /**
99
- * v12 — capability gating headless workspace lifecycle requests
100
- * (`createWorkspace`, `switchWorkspace`, `closeWorkspace`,
101
- * `announceWorkspaceSwitched`) and their `workspace*Requested` /
102
- * `workspaceSwitched` broadcasts. When a UI is attached the daemon relays
105
+ * v12 — capability gating headless project lifecycle requests
106
+ * (`createProject`, `switchProject`, `closeProject`,
107
+ * `announceProjectSwitched`) and their `project*Requested` /
108
+ * `projectSwitched` broadcasts. When a UI is attached the daemon relays
103
109
  * the request as an event so the UI's reducer stays authoritative for the
104
- * live workspace snapshot; when no UI is attached the daemon mutates the
110
+ * live project snapshot; when no UI is attached the daemon mutates the
105
111
  * catalog directly.
106
112
  */
107
- export const IPC_CAPABILITY_WORKSPACE_LIFECYCLE = 'workspaceLifecycle'
113
+ export const IPC_CAPABILITY_PROJECT_LIFECYCLE = 'projectLifecycle'
108
114
 
109
115
  /**
110
- * v12 — capability gating `addWorktreeRecord` / `removeWorktreeRecord` and
111
- * their `worktreeAdded` / `worktreeRemoved` broadcasts. Same UI-vs-headless
112
- * fanout as workspaceLifecycle.
116
+ * v12 — capability gating `addWorkspaceRecord` / `removeWorkspaceRecord` and
117
+ * their `workspaceAdded` / `workspaceRemoved` broadcasts. Same UI-vs-headless
118
+ * fanout as projectLifecycle.
113
119
  */
114
- export const IPC_CAPABILITY_WORKTREE_LIFECYCLE_EVENTS = 'worktreeLifecycleEvents'
120
+ export const IPC_CAPABILITY_WORKSPACE_LIFECYCLE_EVENTS = 'workspaceLifecycleEvents'
115
121
  export const IPC_CAPABILITY_WORKER_METADATA = 'workerMetadata'
116
122
 
117
123
  /**
@@ -166,9 +172,9 @@ export const IPC_PROTOCOL_CAPABILITIES: readonly string[] = [
166
172
  IPC_CAPABILITY_THIN_ATTACH,
167
173
  IPC_CAPABILITY_CREATE_TAB_SIZE_FALLBACK,
168
174
  IPC_CAPABILITY_TAB_LIFECYCLE_EVENTS,
169
- IPC_CAPABILITY_CREATE_TAB_WORKTREE_ID,
170
- IPC_CAPABILITY_WORKSPACE_LIFECYCLE,
171
- IPC_CAPABILITY_WORKTREE_LIFECYCLE_EVENTS,
175
+ IPC_CAPABILITY_CREATE_TAB_WORKSPACE_ID,
176
+ IPC_CAPABILITY_PROJECT_LIFECYCLE,
177
+ IPC_CAPABILITY_WORKSPACE_LIFECYCLE_EVENTS,
172
178
  IPC_CAPABILITY_TAB_TAIL,
173
179
  IPC_CAPABILITY_TURN_LIFECYCLE,
174
180
  IPC_CAPABILITY_QUESTION_EVENTS,
@@ -209,15 +215,15 @@ export interface ProtocolHelloResult {
209
215
 
210
216
  export interface AttachRequest {
211
217
  protocolVersion: number
212
- sessionId: string
218
+ projectId: string
213
219
  cols: number
214
220
  rows: number
215
- workspaceSnapshot?: WorkspaceSnapshotV1
221
+ projectSnapshot?: ProjectSnapshotV1
216
222
  /**
217
223
  * v11 / capability `thinAttach`. When true, the daemon does not call
218
- * `manager.resize` on the session — the headless attacher (CLI) does not
224
+ * `manager.resize` on the project — the headless attacher (CLI) does not
219
225
  * own a viewport, so it must not clobber the dimensions a UI process is
220
- * driving for the same session.
226
+ * driving for the same project.
221
227
  *
222
228
  * Pre-v11 daemons ignore the field; the gate exists so the client knows
223
229
  * to fall through to a full attach (with a sentinel size) when the daemon
@@ -229,7 +235,7 @@ export interface AttachRequest {
229
235
  /**
230
236
  * Slim per-tab summary returned by `listTabs`. Subset of {@link TabSession}
231
237
  * with the buffer/viewport/terminalModes intentionally omitted so the request
232
- * stays cheap even on sessions with many tabs.
238
+ * stays cheap even on projects with many tabs.
233
239
  */
234
240
  export interface TabSessionSummary {
235
241
  id: string
@@ -238,7 +244,7 @@ export interface TabSessionSummary {
238
244
  status: TabStatus
239
245
  activity?: TabActivity
240
246
  command: string
241
- worktreeId?: string
247
+ workspaceId?: string
242
248
  workerName?: string
243
249
  /**
244
250
  * v13 / capability `listTabsLastLine`. The tab's last non-blank rendered
@@ -258,12 +264,12 @@ export interface AttachResult {
258
264
  tabs: TabSession[]
259
265
  activeTabId: string | null
260
266
  /**
261
- * Snapshot of every known session's status at attach time. Applied by
262
- * the client atomically with `hydrate-workspace` so chips render the
267
+ * Snapshot of every known project's status at attach time. Applied by
268
+ * the client atomically with `hydrate-project` so chips render the
263
269
  * right state immediately, without the per-event race where a separate
264
- * `sessionStatus` event could arrive before the session was in state.
270
+ * `projectStatus` event could arrive before the project was in state.
265
271
  */
266
- initialSessionStatuses: { sessionId: string; status: SessionStatus }[]
272
+ initialProjectStatuses: { projectId: string; status: ProjectStatus }[]
267
273
  }
268
274
 
269
275
  export type ClientRequest =
@@ -282,14 +288,14 @@ export type ClientRequest =
282
288
  rows: number
283
289
  cwd?: string
284
290
  /**
285
- * v11 / capability `createTabWorktreeId`. Lets the CLI spawn a tab
286
- * inside a specific worktree so the UI groups it correctly. The
291
+ * v11 / capability `createTabWorkspaceId`. Lets the CLI spawn a tab
292
+ * inside a specific workspace so the UI groups it correctly. The
287
293
  * field is parsed unconditionally — older daemons accepted no such
288
294
  * field, so this is a true additive on the wire — but the daemon
289
295
  * only forwards it to the TM when its own capability is in play.
290
296
  */
291
- worktreeId?: string
292
- /** v15 / capability `workerMetadata`: stable workspace-scoped handle. */
297
+ workspaceId?: string
298
+ /** v15 / capability `workerMetadata`: stable project-scoped handle. */
293
299
  workerName?: string
294
300
  /** True only when the creator did not provide an explicit title. */
295
301
  autoRenameCandidate?: boolean
@@ -318,51 +324,51 @@ export type ClientRequest =
318
324
  // spawned successor binary can bind the canonical socket path while the
319
325
  // terminal-manager (and every PTY) keeps running.
320
326
  | { id: string; type: 'prepareReexec'; payload: { reason?: string } }
321
- // Capability-gated on `listTabs`. Read-only enumeration of a session's
322
- // tabs — does NOT attach. Lets a headless CLI inspect a session without
327
+ // Capability-gated on `listTabs`. Read-only enumeration of a project's
328
+ // tabs — does NOT attach. Lets a headless CLI inspect a project without
323
329
  // implicit `manager.resize` or `setBroadcastEnabled` side effects.
324
- | { id: string; type: 'listTabs'; payload: { sessionId: string } }
325
- // v12 / capability `workspaceLifecycle`. Ask the daemon to create a new
326
- // workspace in the catalog. When any UI socket is attached the daemon
327
- // broadcasts `workspaceCreateRequested` so the UI's reducer performs the
328
- // create (owning the live workspace snapshot); when no UI is attached the
330
+ | { id: string; type: 'listTabs'; payload: { projectId: string } }
331
+ // v12 / capability `projectLifecycle`. Ask the daemon to create a new
332
+ // project in the catalog. When any UI socket is attached the daemon
333
+ // broadcasts `projectCreateRequested` so the UI's reducer performs the
334
+ // create (owning the live project snapshot); when no UI is attached the
329
335
  // daemon writes the catalog directly.
330
336
  | {
331
337
  id: string
332
- type: 'createWorkspace'
338
+ type: 'createProject'
333
339
  payload: { name: string; projectPath?: string; switch?: boolean }
334
340
  }
335
- // v12 / capability `workspaceLifecycle`. Ask the daemon to switch to
336
- // another workspace. Broadcast → `workspaceSwitchRequested`; headless
341
+ // v12 / capability `projectLifecycle`. Ask the daemon to switch to
342
+ // another project. Broadcast → `projectSwitchRequested`; headless
337
343
  // path bumps `lastOpenedAt` on the target so the next UI boot picks it.
338
- | { id: string; type: 'switchWorkspace'; payload: { targetSessionId: string } }
339
- // v12 / capability `workspaceLifecycle`. Ask the daemon to remove a
340
- // workspace. Broadcast → `workspaceCloseRequested`; headless deletes the
344
+ | { id: string; type: 'switchProject'; payload: { targetProjectId: string } }
345
+ // v12 / capability `projectLifecycle`. Ask the daemon to remove a
346
+ // project. Broadcast → `projectCloseRequested`; headless deletes the
341
347
  // catalog entry directly.
342
348
  | {
343
349
  id: string
344
- type: 'closeWorkspace'
345
- payload: { targetSessionId: string }
350
+ type: 'closeProject'
351
+ payload: { targetProjectId: string }
346
352
  }
347
- // v12 / capability `workspaceLifecycle`. UI announces that its own
348
- // `handleSwitchSessionEffect` has finished; the daemon relays as a
349
- // `workspaceSwitched` broadcast so a `--wait`ing CLI can exit.
350
- | { id: string; type: 'announceWorkspaceSwitched'; payload: { sessionId: string } }
351
- // v12 / capability `worktreeLifecycleEvents`. Append a worktree record to
352
- // a session in the catalog. UI-attached path relays as `worktreeAdded`;
353
+ // v12 / capability `projectLifecycle`. UI announces that its own
354
+ // `handleSwitchProjectEffect` has finished; the daemon relays as a
355
+ // `projectSwitched` broadcast so a `--wait`ing CLI can exit.
356
+ | { id: string; type: 'announceProjectSwitched'; payload: { projectId: string } }
357
+ // v12 / capability `workspaceLifecycleEvents`. Append a workspace record to
358
+ // a project in the catalog. UI-attached path relays as `workspaceAdded`;
353
359
  // headless path writes the catalog directly.
354
360
  | {
355
361
  id: string
356
- type: 'addWorktreeRecord'
357
- payload: { sessionId: string; worktree: WorktreeRecord }
362
+ type: 'addWorkspaceRecord'
363
+ payload: { projectId: string; workspace: WorkspaceRecord }
358
364
  }
359
- // v12 / capability `worktreeLifecycleEvents`. Remove a worktree record.
360
- // UI-attached path relays as `worktreeRemoved`; headless path writes the
365
+ // v12 / capability `workspaceLifecycleEvents`. Remove a workspace record.
366
+ // UI-attached path relays as `workspaceRemoved`; headless path writes the
361
367
  // catalog directly.
362
368
  | {
363
369
  id: string
364
- type: 'removeWorktreeRecord'
365
- payload: { sessionId: string; worktreeId: string }
370
+ type: 'removeWorkspaceRecord'
371
+ payload: { projectId: string; workspaceId: string }
366
372
  }
367
373
 
368
374
  export type ServerResponse =
@@ -390,13 +396,22 @@ export type ServerEvent =
390
396
  }
391
397
  | { type: 'tabExit'; payload: { tabId: string; exitCode: number } }
392
398
  | { type: 'tabError'; payload: { tabId: string; message: string } }
393
- | { type: 'tabStatus'; payload: { sessionId: string; tabId: string; status: TabActivity } }
399
+ // v18: `workspaceId` is the tab's workspace when the daemon knows one. A
400
+ // client only holds tabs for the project it is attached to, so this is the
401
+ // only way it can attribute a *foreign* project's tab to a workspace row.
402
+ | {
403
+ type: 'tabStatus'
404
+ payload: { projectId: string; tabId: string; status: TabActivity; workspaceId?: string }
405
+ }
394
406
  // v13 / capability `turnLifecycle`. Authoritative end-of-turn: broadcast
395
407
  // once a tab's `idle` activity has held continuously for the settle window.
396
408
  // Edge-triggered — re-armed only after the tab leaves `idle` again — so a
397
409
  // driver gets exactly one per turn. `idleMs` is how long idle had held when
398
410
  // the event fired.
399
- | { type: 'tabTurnComplete'; payload: { sessionId: string; tabId: string; idleMs: number } }
411
+ | {
412
+ type: 'tabTurnComplete'
413
+ payload: { projectId: string; tabId: string; idleMs: number; workspaceId?: string }
414
+ }
400
415
  // v13 / capability `questionEvents`. Broadcast when a tab transitions into
401
416
  // `waiting-input`. `prompt` is the captured tail text (authoritative);
402
417
  // `options` is a best-effort per-CLI parse of the choice list and may be
@@ -404,73 +419,73 @@ export type ServerEvent =
404
419
  | {
405
420
  type: 'tabQuestion'
406
421
  payload: {
407
- sessionId: string
422
+ projectId: string
408
423
  tabId: string
409
424
  kind: QuestionKind
410
425
  prompt: string
411
426
  options?: string[]
412
427
  }
413
428
  }
414
- | { type: 'sessionStatus'; payload: { sessionId: string; status: SessionStatus } }
429
+ | { type: 'projectStatus'; payload: { projectId: string; status: ProjectStatus } }
415
430
  // Capability-gated on `tabLifecycleEvents`. Broadcast after a successful
416
- // `createTab` so every UI/CLI client attached to the same session learns
431
+ // `createTab` so every UI/CLI client attached to the same project learns
417
432
  // about the new tab without having to re-attach. The tab's `viewport` is
418
433
  // intentionally omitted — the very next `tabRender` event carries it.
419
434
  | {
420
435
  type: 'tabAdded'
421
- payload: { sessionId: string; tab: TabSession }
436
+ payload: { projectId: string; tab: TabSession }
422
437
  }
423
438
  | {
424
439
  type: 'tabMetadataUpdated'
425
440
  payload: {
426
- sessionId: string
441
+ projectId: string
427
442
  tabId: string
428
443
  title?: string
429
444
  autoRenameStatus?: 'eligible' | 'attempted'
430
445
  }
431
446
  }
432
- // v12 / capability `workspaceLifecycle`. Broadcast to every socket when a
433
- // CLI issues `createWorkspace` while a UI is attached — the UI runs its
434
- // create-session handler so the live workspace snapshot is preserved.
435
- // Broadcast is NOT session-scoped; the UI may be attached to a different
436
- // session than the one being created.
447
+ // v12 / capability `projectLifecycle`. Broadcast to every socket when a
448
+ // CLI issues `createProject` while a UI is attached — the UI runs its
449
+ // create-project handler so the live project snapshot is preserved.
450
+ // Broadcast is NOT project-scoped; the UI may be attached to a different
451
+ // project than the one being created.
437
452
  | {
438
- type: 'workspaceCreateRequested'
453
+ type: 'projectCreateRequested'
439
454
  payload: { name: string; projectPath?: string; switch?: boolean }
440
455
  }
441
- // v12 / capability `workspaceLifecycle`. Broadcast when a CLI issues
442
- // `switchWorkspace`; UI runs `handleSwitchSessionEffect` and then emits
443
- // `announceWorkspaceSwitched` when done.
456
+ // v12 / capability `projectLifecycle`. Broadcast when a CLI issues
457
+ // `switchProject`; UI runs `handleSwitchProjectEffect` and then emits
458
+ // `announceProjectSwitched` when done.
444
459
  | {
445
- type: 'workspaceSwitchRequested'
446
- payload: { targetSessionId: string }
460
+ type: 'projectSwitchRequested'
461
+ payload: { targetProjectId: string }
447
462
  }
448
- // v12 / capability `workspaceLifecycle`. Broadcast when a CLI issues
449
- // `closeWorkspace`; UI runs `handleDeleteSessionEffect`.
463
+ // v12 / capability `projectLifecycle`. Broadcast when a CLI issues
464
+ // `closeProject`; UI runs `handleDeleteProjectEffect`.
450
465
  | {
451
- type: 'workspaceCloseRequested'
452
- payload: { targetSessionId: string }
466
+ type: 'projectCloseRequested'
467
+ payload: { targetProjectId: string }
453
468
  }
454
- // v12 / capability `workspaceLifecycle`. Daemon relay of the UI's
455
- // `announceWorkspaceSwitched` so a `--wait`ing CLI can exit.
469
+ // v12 / capability `projectLifecycle`. Daemon relay of the UI's
470
+ // `announceProjectSwitched` so a `--wait`ing CLI can exit.
456
471
  | {
457
- type: 'workspaceSwitched'
458
- payload: { sessionId: string }
472
+ type: 'projectSwitched'
473
+ payload: { projectId: string }
459
474
  }
460
- // v12 / capability `worktreeLifecycleEvents`. Broadcast after a CLI
461
- // `addWorktreeRecord` while a UI is attached; the UI reducer appends the
475
+ // v12 / capability `workspaceLifecycleEvents`. Broadcast after a CLI
476
+ // `addWorkspaceRecord` while a UI is attached; the UI reducer appends the
462
477
  // record and persists the catalog.
463
478
  | {
464
- type: 'worktreeAdded'
465
- payload: { sessionId: string; worktree: WorktreeRecord }
479
+ type: 'workspaceAdded'
480
+ payload: { projectId: string; workspace: WorkspaceRecord }
466
481
  }
467
- // v12 / capability `worktreeLifecycleEvents`. Broadcast after a CLI
468
- // `removeWorktreeRecord` while a UI is attached; the UI reducer removes
482
+ // v12 / capability `workspaceLifecycleEvents`. Broadcast after a CLI
483
+ // `removeWorkspaceRecord` while a UI is attached; the UI reducer removes
469
484
  // the record (and closes any tabs pinned to it via the existing
470
- // worktree-removal side-effect).
485
+ // workspace-removal side-effect).
471
486
  | {
472
- type: 'worktreeRemoved'
473
- payload: { sessionId: string; worktreeId: string }
487
+ type: 'workspaceRemoved'
488
+ payload: { projectId: string; workspaceId: string }
474
489
  }
475
490
 
476
491
  export type IpcMessage = ClientRequest | ServerResponse | ServerEvent
@@ -516,7 +531,7 @@ function isTabActivity(value: unknown): value is TabActivity {
516
531
  return value === 'working' || value === 'waiting-input' || value === 'idle'
517
532
  }
518
533
 
519
- function isSessionStatus(value: unknown): value is SessionStatus {
534
+ function isProjectStatus(value: unknown): value is ProjectStatus {
520
535
  return (
521
536
  isObjectRecord(value) &&
522
537
  typeof value.working === 'boolean' &&
@@ -608,7 +623,7 @@ function isTabSessionSummary(value: unknown): value is TabSessionSummary {
608
623
  value.activity === 'waiting-input' ||
609
624
  value.activity === 'idle') &&
610
625
  isString(value.command) &&
611
- (value.worktreeId === undefined || isString(value.worktreeId)) &&
626
+ (value.workspaceId === undefined || isString(value.workspaceId)) &&
612
627
  (value.workerName === undefined || isString(value.workerName)) &&
613
628
  (value.lastLine === undefined || isString(value.lastLine))
614
629
  )
@@ -652,7 +667,7 @@ function isTabSession(value: unknown): value is TabSession {
652
667
  (value.viewport === undefined || isTerminalSnapshot(value.viewport)) &&
653
668
  (value.errorMessage === undefined || isString(value.errorMessage)) &&
654
669
  (value.exitCode === undefined || isFiniteNumber(value.exitCode)) &&
655
- (value.worktreeId === undefined || isString(value.worktreeId))
670
+ (value.workspaceId === undefined || isString(value.workspaceId))
656
671
  )
657
672
  }
658
673
 
@@ -663,9 +678,9 @@ function isAttachResult(value: unknown): value is AttachResult {
663
678
  Array.isArray(value.tabs) &&
664
679
  value.tabs.every(isTabSession) &&
665
680
  isNullableString(value.activeTabId) &&
666
- Array.isArray(value.initialSessionStatuses) &&
667
- value.initialSessionStatuses.every(
668
- (entry) => isObjectRecord(entry) && isString(entry.sessionId) && isSessionStatus(entry.status)
681
+ Array.isArray(value.initialProjectStatuses) &&
682
+ value.initialProjectStatuses.every(
683
+ (entry) => isObjectRecord(entry) && isString(entry.projectId) && isProjectStatus(entry.status)
669
684
  )
670
685
  )
671
686
  }
@@ -717,13 +732,13 @@ export function parseClientRequest(value: unknown): ClientRequest {
717
732
  isFiniteNumber(value.payload.protocolVersion),
718
733
  'attach.protocolVersion must be a number'
719
734
  )
720
- assert(isString(value.payload.sessionId), 'attach.sessionId must be a string')
735
+ assert(isString(value.payload.projectId), 'attach.projectId must be a string')
721
736
  assert(isFiniteNumber(value.payload.cols), 'attach.cols must be a number')
722
737
  assert(isFiniteNumber(value.payload.rows), 'attach.rows must be a number')
723
738
  assert(
724
- value.payload.workspaceSnapshot === undefined ||
725
- isWorkspaceSnapshotV1(value.payload.workspaceSnapshot),
726
- 'attach.workspaceSnapshot must be a valid workspace snapshot'
739
+ value.payload.projectSnapshot === undefined ||
740
+ isProjectSnapshotV1(value.payload.projectSnapshot),
741
+ 'attach.projectSnapshot must be a valid project snapshot'
727
742
  )
728
743
  assert(
729
744
  value.payload.thin === undefined || typeof value.payload.thin === 'boolean',
@@ -731,7 +746,7 @@ export function parseClientRequest(value: unknown): ClientRequest {
731
746
  )
732
747
  return value as ClientRequest
733
748
  case 'listTabs':
734
- assert(isString(value.payload.sessionId), 'listTabs.sessionId must be a string')
749
+ assert(isString(value.payload.projectId), 'listTabs.projectId must be a string')
735
750
  return value as ClientRequest
736
751
  case 'createTab':
737
752
  assert(isString(value.payload.tabId), 'createTab.tabId must be a string')
@@ -752,8 +767,8 @@ export function parseClientRequest(value: unknown): ClientRequest {
752
767
  'createTab.cwd must be a string'
753
768
  )
754
769
  assert(
755
- value.payload.worktreeId === undefined || isString(value.payload.worktreeId),
756
- 'createTab.worktreeId must be a string when present'
770
+ value.payload.workspaceId === undefined || isString(value.payload.workspaceId),
771
+ 'createTab.workspaceId must be a string when present'
757
772
  )
758
773
  assert(
759
774
  value.payload.workerName === undefined || isString(value.payload.workerName),
@@ -807,48 +822,51 @@ export function parseClientRequest(value: unknown): ClientRequest {
807
822
  'prepareReexec.reason must be a string when present'
808
823
  )
809
824
  return value as ClientRequest
810
- case 'createWorkspace':
825
+ case 'createProject':
811
826
  assert(
812
827
  isString(value.payload.name) && value.payload.name.length > 0,
813
- 'createWorkspace.name must be a non-empty string'
828
+ 'createProject.name must be a non-empty string'
814
829
  )
815
830
  assert(
816
831
  value.payload.projectPath === undefined || isString(value.payload.projectPath),
817
- 'createWorkspace.projectPath must be a string when present'
832
+ 'createProject.projectPath must be a string when present'
818
833
  )
819
834
  assert(
820
835
  value.payload.switch === undefined || typeof value.payload.switch === 'boolean',
821
- 'createWorkspace.switch must be a boolean when present'
836
+ 'createProject.switch must be a boolean when present'
822
837
  )
823
838
  return value as ClientRequest
824
- case 'switchWorkspace':
839
+ case 'switchProject':
825
840
  assert(
826
- isString(value.payload.targetSessionId),
827
- 'switchWorkspace.targetSessionId must be a string'
841
+ isString(value.payload.targetProjectId),
842
+ 'switchProject.targetProjectId must be a string'
828
843
  )
829
844
  return value as ClientRequest
830
- case 'closeWorkspace':
845
+ case 'closeProject':
831
846
  assert(
832
- isString(value.payload.targetSessionId),
833
- 'closeWorkspace.targetSessionId must be a string'
847
+ isString(value.payload.targetProjectId),
848
+ 'closeProject.targetProjectId must be a string'
834
849
  )
835
850
  return value as ClientRequest
836
- case 'announceWorkspaceSwitched':
851
+ case 'announceProjectSwitched':
837
852
  assert(
838
- isString(value.payload.sessionId),
839
- 'announceWorkspaceSwitched.sessionId must be a string'
853
+ isString(value.payload.projectId),
854
+ 'announceProjectSwitched.projectId must be a string'
840
855
  )
841
856
  return value as ClientRequest
842
- case 'addWorktreeRecord':
843
- assert(isString(value.payload.sessionId), 'addWorktreeRecord.sessionId must be a string')
857
+ case 'addWorkspaceRecord':
858
+ assert(isString(value.payload.projectId), 'addWorkspaceRecord.projectId must be a string')
844
859
  assert(
845
- isWorktreeRecord(value.payload.worktree),
846
- 'addWorktreeRecord.worktree must be a WorktreeRecord'
860
+ isWorkspaceRecord(value.payload.workspace),
861
+ 'addWorkspaceRecord.workspace must be a WorkspaceRecord'
847
862
  )
848
863
  return value as ClientRequest
849
- case 'removeWorktreeRecord':
850
- assert(isString(value.payload.sessionId), 'removeWorktreeRecord.sessionId must be a string')
851
- assert(isString(value.payload.worktreeId), 'removeWorktreeRecord.worktreeId must be a string')
864
+ case 'removeWorkspaceRecord':
865
+ assert(isString(value.payload.projectId), 'removeWorkspaceRecord.projectId must be a string')
866
+ assert(
867
+ isString(value.payload.workspaceId),
868
+ 'removeWorkspaceRecord.workspaceId must be a string'
869
+ )
852
870
  return value as ClientRequest
853
871
  default:
854
872
  throw new IpcProtocolError(`Unknown IPC request type: ${String(value.type)}`)
@@ -906,17 +924,25 @@ export function parseServerMessage(value: unknown): ServerResponse | ServerEvent
906
924
  assert(isString(value.payload.message), 'tabError.message must be a string')
907
925
  return value as ServerEvent
908
926
  case 'tabStatus':
909
- assert(isString(value.payload.sessionId), 'tabStatus.sessionId must be a string')
927
+ assert(isString(value.payload.projectId), 'tabStatus.projectId must be a string')
910
928
  assert(isString(value.payload.tabId), 'tabStatus.tabId must be a string')
911
929
  assert(isTabActivity(value.payload.status), 'tabStatus.status is invalid')
930
+ assert(
931
+ value.payload.workspaceId === undefined || isString(value.payload.workspaceId),
932
+ 'tabStatus.workspaceId must be a string when present'
933
+ )
912
934
  return value as ServerEvent
913
935
  case 'tabTurnComplete':
914
- assert(isString(value.payload.sessionId), 'tabTurnComplete.sessionId must be a string')
936
+ assert(isString(value.payload.projectId), 'tabTurnComplete.projectId must be a string')
915
937
  assert(isString(value.payload.tabId), 'tabTurnComplete.tabId must be a string')
916
938
  assert(isFiniteNumber(value.payload.idleMs), 'tabTurnComplete.idleMs must be a number')
939
+ assert(
940
+ value.payload.workspaceId === undefined || isString(value.payload.workspaceId),
941
+ 'tabTurnComplete.workspaceId must be a string when present'
942
+ )
917
943
  return value as ServerEvent
918
944
  case 'tabQuestion':
919
- assert(isString(value.payload.sessionId), 'tabQuestion.sessionId must be a string')
945
+ assert(isString(value.payload.projectId), 'tabQuestion.projectId must be a string')
920
946
  assert(isString(value.payload.tabId), 'tabQuestion.tabId must be a string')
921
947
  assert(isQuestionKind(value.payload.kind), 'tabQuestion.kind is invalid')
922
948
  assert(isString(value.payload.prompt), 'tabQuestion.prompt must be a string')
@@ -926,11 +952,11 @@ export function parseServerMessage(value: unknown): ServerResponse | ServerEvent
926
952
  )
927
953
  return value as ServerEvent
928
954
  case 'tabAdded':
929
- assert(isString(value.payload.sessionId), 'tabAdded.sessionId must be a string')
955
+ assert(isString(value.payload.projectId), 'tabAdded.projectId must be a string')
930
956
  assert(isTabSession(value.payload.tab), 'tabAdded.tab is invalid')
931
957
  return value as ServerEvent
932
958
  case 'tabMetadataUpdated':
933
- assert(isString(value.payload.sessionId), 'tabMetadataUpdated.sessionId must be a string')
959
+ assert(isString(value.payload.projectId), 'tabMetadataUpdated.projectId must be a string')
934
960
  assert(isString(value.payload.tabId), 'tabMetadataUpdated.tabId must be a string')
935
961
  assert(
936
962
  value.payload.title === undefined || isString(value.payload.title),
@@ -943,49 +969,49 @@ export function parseServerMessage(value: unknown): ServerResponse | ServerEvent
943
969
  'tabMetadataUpdated.autoRenameStatus is invalid'
944
970
  )
945
971
  return value as ServerEvent
946
- case 'sessionStatus':
947
- assert(isString(value.payload.sessionId), 'sessionStatus.sessionId must be a string')
948
- assert(isSessionStatus(value.payload.status), 'sessionStatus.status is invalid')
972
+ case 'projectStatus':
973
+ assert(isString(value.payload.projectId), 'projectStatus.projectId must be a string')
974
+ assert(isProjectStatus(value.payload.status), 'projectStatus.status is invalid')
949
975
  return value as ServerEvent
950
- case 'workspaceCreateRequested':
976
+ case 'projectCreateRequested':
951
977
  assert(
952
978
  isString(value.payload.name) && value.payload.name.length > 0,
953
- 'workspaceCreateRequested.name must be a non-empty string'
979
+ 'projectCreateRequested.name must be a non-empty string'
954
980
  )
955
981
  assert(
956
982
  value.payload.projectPath === undefined || isString(value.payload.projectPath),
957
- 'workspaceCreateRequested.projectPath must be a string when present'
983
+ 'projectCreateRequested.projectPath must be a string when present'
958
984
  )
959
985
  assert(
960
986
  value.payload.switch === undefined || typeof value.payload.switch === 'boolean',
961
- 'workspaceCreateRequested.switch must be a boolean when present'
987
+ 'projectCreateRequested.switch must be a boolean when present'
962
988
  )
963
989
  return value as ServerEvent
964
- case 'workspaceSwitchRequested':
990
+ case 'projectSwitchRequested':
965
991
  assert(
966
- isString(value.payload.targetSessionId),
967
- 'workspaceSwitchRequested.targetSessionId must be a string'
992
+ isString(value.payload.targetProjectId),
993
+ 'projectSwitchRequested.targetProjectId must be a string'
968
994
  )
969
995
  return value as ServerEvent
970
- case 'workspaceCloseRequested':
996
+ case 'projectCloseRequested':
971
997
  assert(
972
- isString(value.payload.targetSessionId),
973
- 'workspaceCloseRequested.targetSessionId must be a string'
998
+ isString(value.payload.targetProjectId),
999
+ 'projectCloseRequested.targetProjectId must be a string'
974
1000
  )
975
1001
  return value as ServerEvent
976
- case 'workspaceSwitched':
977
- assert(isString(value.payload.sessionId), 'workspaceSwitched.sessionId must be a string')
1002
+ case 'projectSwitched':
1003
+ assert(isString(value.payload.projectId), 'projectSwitched.projectId must be a string')
978
1004
  return value as ServerEvent
979
- case 'worktreeAdded':
980
- assert(isString(value.payload.sessionId), 'worktreeAdded.sessionId must be a string')
1005
+ case 'workspaceAdded':
1006
+ assert(isString(value.payload.projectId), 'workspaceAdded.projectId must be a string')
981
1007
  assert(
982
- isWorktreeRecord(value.payload.worktree),
983
- 'worktreeAdded.worktree must be a WorktreeRecord'
1008
+ isWorkspaceRecord(value.payload.workspace),
1009
+ 'workspaceAdded.workspace must be a WorkspaceRecord'
984
1010
  )
985
1011
  return value as ServerEvent
986
- case 'worktreeRemoved':
987
- assert(isString(value.payload.sessionId), 'worktreeRemoved.sessionId must be a string')
988
- assert(isString(value.payload.worktreeId), 'worktreeRemoved.worktreeId must be a string')
1012
+ case 'workspaceRemoved':
1013
+ assert(isString(value.payload.projectId), 'workspaceRemoved.projectId must be a string')
1014
+ assert(isString(value.payload.workspaceId), 'workspaceRemoved.workspaceId must be a string')
989
1015
  return value as ServerEvent
990
1016
  default:
991
1017
  throw new IpcProtocolError(`Unknown IPC response type: ${String(value.type)}`)