@jmoyers/harness 0.1.11 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. package/README.md +31 -39
  2. package/package.json +31 -11
  3. package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
  4. package/packages/harness-ai/src/stream-text.ts +13 -91
  5. package/packages/harness-ui/src/frame-primitives.ts +158 -0
  6. package/packages/harness-ui/src/index.ts +18 -0
  7. package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
  8. package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
  9. package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
  10. package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
  11. package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
  12. package/packages/harness-ui/src/interaction/input.ts +420 -0
  13. package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
  14. package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
  15. package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
  16. package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
  17. package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
  18. package/packages/harness-ui/src/kit.ts +476 -0
  19. package/packages/harness-ui/src/layout.ts +238 -0
  20. package/packages/harness-ui/src/modal-manager.ts +222 -0
  21. package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
  22. package/packages/harness-ui/src/surface.ts +252 -0
  23. package/packages/harness-ui/src/text-layout.ts +210 -0
  24. package/packages/nim-core/src/contracts.ts +239 -0
  25. package/packages/nim-core/src/event-store.ts +299 -0
  26. package/packages/nim-core/src/events.ts +53 -0
  27. package/packages/nim-core/src/index.ts +9 -0
  28. package/packages/nim-core/src/provider-router.ts +129 -0
  29. package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
  30. package/packages/nim-core/src/runtime-factory.ts +49 -0
  31. package/packages/nim-core/src/runtime.ts +1797 -0
  32. package/packages/nim-core/src/session-store.ts +516 -0
  33. package/packages/nim-core/src/telemetry.ts +48 -0
  34. package/packages/nim-test-tui/src/index.ts +150 -0
  35. package/packages/nim-ui-core/src/index.ts +1 -0
  36. package/packages/nim-ui-core/src/projection.ts +87 -0
  37. package/scripts/codex-live-mux-runtime.ts +2 -3872
  38. package/scripts/control-plane-daemon.ts +11 -0
  39. package/scripts/harness-bin.js +5 -0
  40. package/scripts/harness-commands.ts +300 -0
  41. package/scripts/harness-runtime.ts +82 -0
  42. package/scripts/harness.ts +33 -3019
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/gateway/runtime.ts +1872 -0
  46. package/src/cli/parsing/flags.ts +23 -0
  47. package/src/cli/parsing/session.ts +42 -0
  48. package/src/cli/runtime/context.ts +193 -0
  49. package/src/cli/runtime-app/application.ts +392 -0
  50. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  51. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  52. package/src/cli/workflows/runtime.ts +965 -0
  53. package/src/clients/tui/left-rail-interactions.ts +519 -0
  54. package/src/clients/tui/main-pane-interactions.ts +509 -0
  55. package/src/clients/tui/modal-input-routing.ts +71 -0
  56. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  57. package/src/clients/web/synced-selectors.ts +132 -0
  58. package/src/codex/live-session.ts +82 -29
  59. package/src/config/config-core.ts +348 -8
  60. package/src/config/harness.config.template.jsonc +33 -0
  61. package/src/control-plane/agent-realtime-api.ts +82 -427
  62. package/src/control-plane/session-summary.ts +10 -81
  63. package/src/control-plane/status/reducer-base.ts +12 -12
  64. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  65. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  66. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  67. package/src/control-plane/stream-client.ts +12 -2
  68. package/src/control-plane/stream-command-parser.ts +83 -143
  69. package/src/control-plane/stream-protocol.ts +53 -37
  70. package/src/control-plane/stream-server-command.ts +376 -69
  71. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  72. package/src/control-plane/stream-server.ts +864 -70
  73. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  74. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  75. package/src/core/state/observed-stream-cursor.ts +43 -0
  76. package/src/core/state/synced-observed-state.ts +273 -0
  77. package/src/core/store/harness-synced-store.ts +81 -0
  78. package/src/diff/budget.ts +136 -0
  79. package/src/diff/build.ts +289 -0
  80. package/src/diff/chunker.ts +146 -0
  81. package/src/diff/git-invoke.ts +315 -0
  82. package/src/diff/git-parse.ts +472 -0
  83. package/src/diff/hash.ts +70 -0
  84. package/src/diff/index.ts +24 -0
  85. package/src/diff/normalize.ts +134 -0
  86. package/src/diff/types.ts +178 -0
  87. package/src/diff-ui/args.ts +346 -0
  88. package/src/diff-ui/commands.ts +123 -0
  89. package/src/diff-ui/finder.ts +94 -0
  90. package/src/diff-ui/highlight.ts +127 -0
  91. package/src/diff-ui/index.ts +2 -0
  92. package/src/diff-ui/model.ts +141 -0
  93. package/src/diff-ui/pager.ts +412 -0
  94. package/src/diff-ui/render.ts +337 -0
  95. package/src/diff-ui/runtime.ts +379 -0
  96. package/src/diff-ui/state.ts +224 -0
  97. package/src/diff-ui/types.ts +236 -0
  98. package/src/domain/workspace.ts +68 -5
  99. package/src/mux/control-plane-op-queue.ts +93 -7
  100. package/src/mux/conversation-rail.ts +28 -71
  101. package/src/mux/dual-pane-core.ts +13 -13
  102. package/src/mux/harness-core-ui.ts +313 -42
  103. package/src/mux/input-shortcuts.ts +13 -131
  104. package/src/mux/keybinding-catalog.ts +340 -0
  105. package/src/mux/keybinding-registry.ts +103 -0
  106. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  107. package/src/mux/live-mux/command-menu.ts +167 -4
  108. package/src/mux/live-mux/conversation-state.ts +13 -0
  109. package/src/mux/live-mux/directory-resolution.ts +1 -1
  110. package/src/mux/live-mux/git-snapshot.ts +33 -2
  111. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  112. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  113. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  114. package/src/mux/live-mux/input-forwarding.ts +59 -2
  115. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  116. package/src/mux/live-mux/left-nav.ts +35 -0
  117. package/src/mux/live-mux/link-click.ts +292 -0
  118. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  119. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  120. package/src/mux/live-mux/modal-input-reducers.ts +77 -12
  121. package/src/mux/live-mux/modal-overlays.ts +168 -34
  122. package/src/mux/live-mux/modal-pointer.ts +3 -7
  123. package/src/mux/live-mux/modal-prompt-handlers.ts +23 -2
  124. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  125. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  126. package/src/mux/live-mux/pointer-routing.ts +5 -2
  127. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  128. package/src/mux/live-mux/rail-layout.ts +33 -30
  129. package/src/mux/live-mux/release-notes.ts +383 -0
  130. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  131. package/src/mux/live-mux/repository-folding.ts +3 -0
  132. package/src/mux/live-mux/selection.ts +0 -4
  133. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  134. package/src/mux/project-pane-github-review.ts +271 -0
  135. package/src/mux/render-frame.ts +4 -0
  136. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  137. package/src/mux/task-composer.ts +21 -14
  138. package/src/mux/task-focused-pane.ts +118 -117
  139. package/src/mux/task-screen-keybindings.ts +10 -101
  140. package/src/mux/workspace-rail-model.ts +270 -104
  141. package/src/mux/workspace-rail.ts +45 -22
  142. package/src/pty/session-broker.ts +1 -1
  143. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  144. package/src/services/control-plane.ts +50 -32
  145. package/src/services/conversation-lifecycle.ts +118 -87
  146. package/src/services/conversation-startup-hydration.ts +20 -12
  147. package/src/services/directory-hydration.ts +21 -16
  148. package/src/services/event-persistence.ts +7 -0
  149. package/src/services/left-rail-pointer-handler.ts +329 -0
  150. package/src/services/mux-ui-state-persistence.ts +5 -1
  151. package/src/services/recording.ts +34 -26
  152. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  153. package/src/services/runtime-control-actions.ts +79 -61
  154. package/src/services/runtime-control-plane-ops.ts +122 -83
  155. package/src/services/runtime-conversation-actions.ts +40 -26
  156. package/src/services/runtime-conversation-activation.ts +73 -46
  157. package/src/services/runtime-conversation-starter.ts +53 -45
  158. package/src/services/runtime-conversation-title-edit.ts +91 -80
  159. package/src/services/runtime-envelope-handler.ts +107 -105
  160. package/src/services/runtime-git-state.ts +42 -29
  161. package/src/services/runtime-layout-resize.ts +3 -1
  162. package/src/services/runtime-left-rail-render.ts +99 -63
  163. package/src/services/runtime-nim-cli-session.ts +438 -0
  164. package/src/services/runtime-nim-session.ts +705 -0
  165. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  166. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  167. package/src/services/runtime-process-wiring.ts +29 -36
  168. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  169. package/src/services/runtime-render-flush.ts +63 -70
  170. package/src/services/runtime-render-lifecycle.ts +65 -64
  171. package/src/services/runtime-render-orchestrator.ts +55 -45
  172. package/src/services/runtime-render-pipeline.ts +106 -103
  173. package/src/services/runtime-render-state.ts +62 -49
  174. package/src/services/runtime-repository-actions.ts +97 -72
  175. package/src/services/runtime-right-pane-render.ts +80 -53
  176. package/src/services/runtime-shutdown.ts +38 -35
  177. package/src/services/runtime-stream-subscriptions.ts +35 -27
  178. package/src/services/runtime-task-composer-persistence.ts +71 -59
  179. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  180. package/src/services/runtime-task-editor-actions.ts +46 -29
  181. package/src/services/runtime-task-pane-actions.ts +220 -134
  182. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  183. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  184. package/src/services/runtime-workspace-observed-events.ts +33 -184
  185. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  186. package/src/services/session-diagnostics-store.ts +217 -0
  187. package/src/services/startup-background-resume.ts +26 -21
  188. package/src/services/startup-orchestrator.ts +16 -13
  189. package/src/services/startup-paint-tracker.ts +29 -21
  190. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  191. package/src/services/startup-settled-gate.ts +25 -15
  192. package/src/services/startup-shutdown.ts +18 -22
  193. package/src/services/startup-state-hydration.ts +44 -34
  194. package/src/services/startup-visibility.ts +12 -4
  195. package/src/services/task-pane-selection-actions.ts +89 -72
  196. package/src/services/task-planning-hydration.ts +24 -18
  197. package/src/services/task-planning-observed-events.ts +50 -52
  198. package/src/services/workspace-observed-events.ts +66 -63
  199. package/src/storage/storage-lifecycle-core.ts +438 -0
  200. package/src/store/control-plane-store-normalize.ts +33 -242
  201. package/src/store/control-plane-store-types.ts +1 -35
  202. package/src/store/control-plane-store.ts +360 -56
  203. package/src/store/event-store.ts +366 -8
  204. package/src/terminal/snapshot-oracle.ts +207 -94
  205. package/src/ui/mux-theme.ts +112 -8
  206. package/src/ui/panes/home-gridfire.ts +40 -31
  207. package/src/ui/panes/home.ts +10 -2
  208. package/src/ui/panes/nim.ts +315 -0
  209. package/src/mux/live-mux/actions-task.ts +0 -115
  210. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  211. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -85
  212. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  213. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  214. package/src/services/runtime-directory-actions.ts +0 -164
  215. package/src/services/runtime-input-pipeline.ts +0 -50
  216. package/src/services/runtime-input-router.ts +0 -195
  217. package/src/services/runtime-main-pane-input.ts +0 -230
  218. package/src/services/runtime-modal-input.ts +0 -137
  219. package/src/services/runtime-navigation-input.ts +0 -197
  220. package/src/services/runtime-rail-input.ts +0 -279
  221. package/src/services/runtime-task-pane.ts +0 -62
  222. package/src/services/runtime-workspace-actions.ts +0 -158
  223. package/src/ui/conversation-input-forwarder.ts +0 -114
  224. package/src/ui/conversation-selection-input.ts +0 -103
  225. package/src/ui/global-shortcut-input.ts +0 -89
  226. package/src/ui/input.ts +0 -269
  227. package/src/ui/kit.ts +0 -509
  228. package/src/ui/left-nav-input.ts +0 -80
  229. package/src/ui/left-rail-pointer-input.ts +0 -148
  230. package/src/ui/modals/manager.ts +0 -218
  231. package/src/ui/repository-fold-input.ts +0 -91
  232. package/src/ui/surface.ts +0 -224
@@ -0,0 +1,519 @@
1
+ import {
2
+ detectMuxGlobalShortcut,
3
+ type ResolvedMuxShortcutBindings,
4
+ } from '../../mux/input-shortcuts.ts';
5
+ import { handleGlobalShortcut } from '../../mux/live-mux/global-shortcut-handlers.ts';
6
+ import {
7
+ activateLeftNavTarget,
8
+ cycleLeftNavSelection,
9
+ } from '../../mux/live-mux/left-nav-activation.ts';
10
+ import { visibleLeftNavTargets } from '../../mux/live-mux/left-nav.ts';
11
+ import {
12
+ firstDirectoryForRepositoryGroup as firstDirectoryForRepositoryGroupFn,
13
+ reduceRepositoryFoldChordInput,
14
+ repositoryTreeArrowAction,
15
+ } from '../../mux/live-mux/repository-folding.ts';
16
+ import type { buildWorkspaceRailViewRows } from '../../mux/workspace-rail-model.ts';
17
+ import type { WorkspaceModel } from '../../domain/workspace.ts';
18
+ import { LeftRailPointerHandler } from '../../services/left-rail-pointer-handler.ts';
19
+ import { GlobalShortcutInput } from '../../../packages/harness-ui/src/interaction/global-shortcut-input.ts';
20
+ import { LeftNavInput } from '../../../packages/harness-ui/src/interaction/left-nav-input.ts';
21
+ import {
22
+ RailPointerInput,
23
+ type HandlePointerClickInput,
24
+ } from '../../../packages/harness-ui/src/interaction/rail-pointer-input.ts';
25
+ import { RepositoryFoldInput } from '../../../packages/harness-ui/src/interaction/repository-fold-input.ts';
26
+
27
+ interface DirectoryRecordLike {
28
+ readonly directoryId: string;
29
+ }
30
+
31
+ interface ConversationRecordLike {
32
+ readonly directoryId: string | null;
33
+ readonly agentType: string;
34
+ }
35
+
36
+ interface ConversationLookup {
37
+ readonly activeConversationId: string | null;
38
+ has(sessionId: string): boolean;
39
+ directoryIdOf(sessionId: string): string | null;
40
+ }
41
+
42
+ interface DirectoryLookup {
43
+ hasDirectory(directoryId: string): boolean;
44
+ }
45
+
46
+ interface RepositoryManagerLike {
47
+ repositoryGroupIdForDirectory(directoryId: string, fallbackGroupId: string): string;
48
+ collapseRepositoryGroup(repositoryGroupId: string, repositoriesCollapsed: boolean): void;
49
+ expandRepositoryGroup(repositoryGroupId: string, repositoriesCollapsed: boolean): void;
50
+ toggleRepositoryGroup(repositoryGroupId: string, repositoriesCollapsed: boolean): void;
51
+ collapseAllRepositoryGroups(): true;
52
+ expandAllRepositoryGroups(): false;
53
+ }
54
+
55
+ interface QueueControlPlaneOps {
56
+ queueControlPlaneOp(task: () => Promise<void>, label: string): void;
57
+ queueLatestControlPlaneOp(
58
+ key: string,
59
+ task: (options: { readonly signal: AbortSignal }) => Promise<void>,
60
+ label: string,
61
+ ): void;
62
+ }
63
+
64
+ interface ConversationLifecycleActions {
65
+ activateConversation(
66
+ sessionId: string,
67
+ options?: {
68
+ readonly signal?: AbortSignal;
69
+ },
70
+ ): Promise<void>;
71
+ openOrCreateCritiqueConversationInDirectory(directoryId: string): Promise<void>;
72
+ takeoverConversation(sessionId: string): Promise<void>;
73
+ beginConversationTitleEdit(conversationId: string): void;
74
+ stopConversationTitleEdit(persistPending: boolean): void;
75
+ }
76
+
77
+ interface RuntimeDirectoryActions {
78
+ archiveConversation(sessionId: string): Promise<void>;
79
+ closeDirectory(directoryId: string): Promise<void>;
80
+ }
81
+
82
+ interface RuntimeRepositoryActions {
83
+ openRepositoryPromptForCreate(): void;
84
+ openRepositoryPromptForEdit(repositoryId: string): void;
85
+ archiveRepositoryById(repositoryId: string): Promise<void>;
86
+ }
87
+
88
+ interface RuntimeControlActions {
89
+ toggleGatewayProfiler(): Promise<void>;
90
+ toggleGatewayStatusTimeline(): Promise<void>;
91
+ toggleGatewayRenderTrace(conversationId: string | null): Promise<void>;
92
+ refreshAllConversationTitles(): Promise<void>;
93
+ interruptConversation(sessionId: string): Promise<void>;
94
+ }
95
+
96
+ interface TuiLeftRailInteractionNavigationActions {
97
+ enterHomePane(): void;
98
+ enterNimPane?(): void;
99
+ enterProjectPane(directoryId: string): void;
100
+ enterTasksPane?(): void;
101
+ resolveDirectoryForAction(): string | null;
102
+ openNewThreadPrompt(directoryId: string): void;
103
+ toggleCommandMenu(): void;
104
+ requestStop(): void;
105
+ markDirty(): void;
106
+ queuePersistMuxUiState(): void;
107
+ resetFrameCache(): void;
108
+ releaseViewportPinForSelection(): void;
109
+ }
110
+
111
+ export interface TuiLeftRailInteractionOptions<
112
+ TShortcutBindings extends ResolvedMuxShortcutBindings | undefined,
113
+ TDirectoryRecord extends DirectoryRecordLike,
114
+ TConversationRecord extends ConversationRecordLike,
115
+ > {
116
+ readonly workspace: WorkspaceModel;
117
+ readonly railViewState: {
118
+ readLatestRows(): ReturnType<typeof buildWorkspaceRailViewRows>;
119
+ };
120
+ readonly directories: ReadonlyMap<string, TDirectoryRecord>;
121
+ readonly conversationRecords: ReadonlyMap<string, TConversationRecord>;
122
+ readonly repositories: ReadonlyMap<string, unknown>;
123
+ readonly conversationLookup: ConversationLookup;
124
+ readonly directoryLookup: DirectoryLookup;
125
+ readonly repositoryManager: RepositoryManagerLike;
126
+ readonly repositoryGroupFallbackId: string;
127
+ readonly queueControlPlaneOps: QueueControlPlaneOps;
128
+ readonly conversationLifecycle: ConversationLifecycleActions;
129
+ readonly runtimeDirectoryActions: RuntimeDirectoryActions;
130
+ readonly runtimeRepositoryActions: RuntimeRepositoryActions;
131
+ readonly runtimeControlActions: RuntimeControlActions;
132
+ readonly navigation: TuiLeftRailInteractionNavigationActions;
133
+ readonly shortcutBindings: TShortcutBindings;
134
+ readonly showTasksEntry: boolean;
135
+ readonly nowMs?: () => number;
136
+ readonly repositoryToggleChordTimeoutMs?: number;
137
+ readonly repositoryCollapseAllChordPrefix?: Buffer;
138
+ readonly conversationTitleEditDoubleClickWindowMs?: number;
139
+ }
140
+
141
+ export interface TuiLeftRailInteractions {
142
+ readonly handleRepositoryFoldInput: (input: Buffer) => boolean;
143
+ readonly handleGlobalShortcutInput: (input: Buffer) => boolean;
144
+ readonly leftRailPointerInput: {
145
+ handlePointerClick(input: HandlePointerClickInput): boolean;
146
+ };
147
+ }
148
+
149
+ const DEFAULT_CONVERSATION_TITLE_EDIT_DOUBLE_CLICK_WINDOW_MS = 350;
150
+ const DEFAULT_REPOSITORY_TOGGLE_CHORD_TIMEOUT_MS = 1250;
151
+ const DEFAULT_REPOSITORY_COLLAPSE_ALL_CHORD_PREFIX = Buffer.from([0x0b]);
152
+
153
+ export function createTuiLeftRailInteractions<
154
+ TShortcutBindings extends ResolvedMuxShortcutBindings | undefined,
155
+ TDirectoryRecord extends DirectoryRecordLike,
156
+ TConversationRecord extends ConversationRecordLike,
157
+ >(
158
+ options: TuiLeftRailInteractionOptions<TShortcutBindings, TDirectoryRecord, TConversationRecord>,
159
+ ): TuiLeftRailInteractions {
160
+ const nowMs = options.nowMs ?? (() => Date.now());
161
+ const conversationTitleEditDoubleClickWindowMs =
162
+ options.conversationTitleEditDoubleClickWindowMs ??
163
+ DEFAULT_CONVERSATION_TITLE_EDIT_DOUBLE_CLICK_WINDOW_MS;
164
+ const repositoryToggleChordTimeoutMs =
165
+ options.repositoryToggleChordTimeoutMs ?? DEFAULT_REPOSITORY_TOGGLE_CHORD_TIMEOUT_MS;
166
+ const repositoryCollapseAllChordPrefix =
167
+ options.repositoryCollapseAllChordPrefix ?? DEFAULT_REPOSITORY_COLLAPSE_ALL_CHORD_PREFIX;
168
+
169
+ const selectLeftNavRepository = options.workspace.selectLeftNavRepository.bind(options.workspace);
170
+ const selectLeftNavConversation = options.workspace.selectLeftNavConversation.bind(
171
+ options.workspace,
172
+ );
173
+ const repositoryGroupIdForDirectory = (directoryId: string): string => {
174
+ return options.repositoryManager.repositoryGroupIdForDirectory(
175
+ directoryId,
176
+ options.repositoryGroupFallbackId,
177
+ );
178
+ };
179
+
180
+ const firstDirectoryForRepositoryGroup = (repositoryGroupId: string): string | null => {
181
+ return firstDirectoryForRepositoryGroupFn(
182
+ options.directories,
183
+ repositoryGroupIdForDirectory,
184
+ repositoryGroupId,
185
+ );
186
+ };
187
+
188
+ const collapseRepositoryGroup = (repositoryGroupId: string): void => {
189
+ options.repositoryManager.collapseRepositoryGroup(
190
+ repositoryGroupId,
191
+ options.workspace.repositoriesCollapsed,
192
+ );
193
+ };
194
+ const expandRepositoryGroup = (repositoryGroupId: string): void => {
195
+ options.repositoryManager.expandRepositoryGroup(
196
+ repositoryGroupId,
197
+ options.workspace.repositoriesCollapsed,
198
+ );
199
+ };
200
+ const toggleRepositoryGroup = (repositoryGroupId: string): void => {
201
+ options.repositoryManager.toggleRepositoryGroup(
202
+ repositoryGroupId,
203
+ options.workspace.repositoriesCollapsed,
204
+ );
205
+ };
206
+ const collapseAllRepositoryGroups = (): void => {
207
+ options.workspace.repositoriesCollapsed =
208
+ options.repositoryManager.collapseAllRepositoryGroups();
209
+ options.navigation.queuePersistMuxUiState();
210
+ };
211
+ const expandAllRepositoryGroups = (): void => {
212
+ options.workspace.repositoriesCollapsed = options.repositoryManager.expandAllRepositoryGroups();
213
+ options.navigation.queuePersistMuxUiState();
214
+ };
215
+
216
+ const openAddDirectoryPromptFromRail = (): void => {
217
+ options.workspace.repositoryPrompt = null;
218
+ options.workspace.apiKeyPrompt = null;
219
+ options.workspace.addDirectoryPrompt = {
220
+ value: '',
221
+ error: null,
222
+ };
223
+ };
224
+
225
+ const leftNavInput = new LeftNavInput(
226
+ {
227
+ railViewState: options.railViewState,
228
+ currentSelection: () => options.workspace.leftNavSelection,
229
+ },
230
+ {
231
+ enterHomePane: options.navigation.enterHomePane,
232
+ ...(options.navigation.enterNimPane === undefined
233
+ ? {}
234
+ : {
235
+ enterNimPane: options.navigation.enterNimPane,
236
+ }),
237
+ firstDirectoryForRepositoryGroup,
238
+ enterProjectPane: options.navigation.enterProjectPane,
239
+ setMainPaneProjectMode: () => {
240
+ options.workspace.mainPaneMode = 'project';
241
+ },
242
+ selectLeftNavRepository,
243
+ selectLeftNavConversation,
244
+ markDirty: options.navigation.markDirty,
245
+ directoriesHas: (directoryId) => options.directoryLookup.hasDirectory(directoryId),
246
+ conversationDirectoryId: (sessionId) => options.conversationLookup.directoryIdOf(sessionId),
247
+ queueControlPlaneOp: options.queueControlPlaneOps.queueControlPlaneOp,
248
+ queueLatestControlPlaneOp: options.queueControlPlaneOps.queueLatestControlPlaneOp,
249
+ activateConversation: async (sessionId, activateOptions) => {
250
+ await options.conversationLifecycle.activateConversation(sessionId, activateOptions);
251
+ },
252
+ conversationsHas: (sessionId) => options.conversationLookup.has(sessionId),
253
+ ...(options.showTasksEntry
254
+ ? {
255
+ enterTasksPane: options.navigation.enterTasksPane,
256
+ }
257
+ : {}),
258
+ },
259
+ {
260
+ visibleTargets: visibleLeftNavTargets,
261
+ activateTarget: activateLeftNavTarget,
262
+ cycleSelection: cycleLeftNavSelection,
263
+ },
264
+ );
265
+
266
+ const repositoryFoldInput = new RepositoryFoldInput(
267
+ {
268
+ leftNavSelection: () => options.workspace.leftNavSelection,
269
+ repositoryToggleChordPrefixAtMs: () => options.workspace.repositoryToggleChordPrefixAtMs,
270
+ setRepositoryToggleChordPrefixAtMs: (value) => {
271
+ options.workspace.repositoryToggleChordPrefixAtMs = value;
272
+ },
273
+ conversations: () => options.conversationRecords,
274
+ repositoryGroupIdForDirectory,
275
+ nowMs,
276
+ },
277
+ {
278
+ collapseRepositoryGroup,
279
+ expandRepositoryGroup,
280
+ collapseAllRepositoryGroups,
281
+ expandAllRepositoryGroups,
282
+ selectLeftNavRepository,
283
+ markDirty: options.navigation.markDirty,
284
+ },
285
+ {
286
+ chordTimeoutMs: repositoryToggleChordTimeoutMs,
287
+ collapseAllChordPrefix: repositoryCollapseAllChordPrefix,
288
+ },
289
+ {
290
+ reduceRepositoryFoldChordInput,
291
+ repositoryTreeArrowAction,
292
+ },
293
+ );
294
+
295
+ const globalShortcutInput = new GlobalShortcutInput(
296
+ options.shortcutBindings,
297
+ {
298
+ mainPaneMode: () => options.workspace.mainPaneMode,
299
+ activeConversationId: () => options.conversationLookup.activeConversationId,
300
+ activeConversationAgentType: () => {
301
+ const activeConversationId = options.conversationLookup.activeConversationId;
302
+ if (activeConversationId === null) {
303
+ return null;
304
+ }
305
+ return options.conversationRecords.get(activeConversationId)?.agentType ?? null;
306
+ },
307
+ conversationsHas: (sessionId) => options.conversationLookup.has(sessionId),
308
+ activeDirectoryId: () => options.workspace.activeDirectoryId,
309
+ directoryExists: (directoryId) => options.directoryLookup.hasDirectory(directoryId),
310
+ },
311
+ {
312
+ requestStop: options.navigation.requestStop,
313
+ resolveDirectoryForAction: options.navigation.resolveDirectoryForAction,
314
+ openNewThreadPrompt: options.navigation.openNewThreadPrompt,
315
+ toggleCommandMenu: options.navigation.toggleCommandMenu,
316
+ openOrCreateCritiqueConversationInDirectory: async (directoryId) => {
317
+ await options.conversationLifecycle.openOrCreateCritiqueConversationInDirectory(
318
+ directoryId,
319
+ );
320
+ },
321
+ toggleGatewayProfile: async () => {
322
+ await options.runtimeControlActions.toggleGatewayProfiler();
323
+ },
324
+ toggleGatewayStatusTimeline: async () => {
325
+ await options.runtimeControlActions.toggleGatewayStatusTimeline();
326
+ },
327
+ toggleGatewayRenderTrace: async (conversationId) => {
328
+ await options.runtimeControlActions.toggleGatewayRenderTrace(conversationId);
329
+ },
330
+ queueControlPlaneOp: options.queueControlPlaneOps.queueControlPlaneOp,
331
+ archiveConversation: async (sessionId) => {
332
+ await options.runtimeDirectoryActions.archiveConversation(sessionId);
333
+ },
334
+ refreshAllConversationTitles: async () => {
335
+ await options.runtimeControlActions.refreshAllConversationTitles();
336
+ },
337
+ interruptConversation: async (sessionId) => {
338
+ await options.runtimeControlActions.interruptConversation(sessionId);
339
+ },
340
+ takeoverConversation: async (sessionId) => {
341
+ await options.conversationLifecycle.takeoverConversation(sessionId);
342
+ },
343
+ openAddDirectoryPrompt: () => {
344
+ openAddDirectoryPromptFromRail();
345
+ options.navigation.markDirty();
346
+ },
347
+ closeDirectory: async (directoryId) => {
348
+ await options.runtimeDirectoryActions.closeDirectory(directoryId);
349
+ },
350
+ cycleLeftNavSelection: (direction) => {
351
+ leftNavInput.cycleSelection(direction);
352
+ },
353
+ },
354
+ {
355
+ detectShortcut: detectMuxGlobalShortcut,
356
+ handleShortcut: (input) =>
357
+ handleGlobalShortcut({
358
+ ...input,
359
+ toggleDebugBar: () => {
360
+ options.workspace.showDebugBar = !options.workspace.showDebugBar;
361
+ options.navigation.queuePersistMuxUiState();
362
+ options.navigation.markDirty();
363
+ },
364
+ }),
365
+ },
366
+ );
367
+
368
+ const leftRailPointerHandler = new LeftRailPointerHandler(
369
+ {
370
+ railViewState: options.railViewState,
371
+ conversationTitleEditConversationId: () =>
372
+ options.workspace.conversationTitleEdit?.conversationId ?? null,
373
+ activeConversationId: () => options.conversationLookup.activeConversationId,
374
+ repositoriesCollapsed: () => options.workspace.repositoriesCollapsed,
375
+ resolveDirectoryForAction: options.navigation.resolveDirectoryForAction,
376
+ previousConversationClickState: () => options.workspace.conversationTitleEditClickState,
377
+ nowMs,
378
+ isConversationPaneActive: () => options.workspace.mainPaneMode === 'conversation',
379
+ directoriesHas: (directoryId) => options.directoryLookup.hasDirectory(directoryId),
380
+ },
381
+ {
382
+ clearConversationTitleEditClickState: () => {
383
+ options.workspace.conversationTitleEditClickState = null;
384
+ },
385
+ openNewThreadPrompt: options.navigation.openNewThreadPrompt,
386
+ queueArchiveConversation: (conversationId) => {
387
+ options.queueControlPlaneOps.queueControlPlaneOp(async () => {
388
+ await options.runtimeDirectoryActions.archiveConversation(conversationId);
389
+ }, 'mouse-archive-conversation');
390
+ },
391
+ openAddDirectoryPrompt: openAddDirectoryPromptFromRail,
392
+ openRepositoryPromptForCreate: () => {
393
+ options.runtimeRepositoryActions.openRepositoryPromptForCreate();
394
+ },
395
+ repositoryExists: (repositoryId) => options.repositories.has(repositoryId),
396
+ openRepositoryPromptForEdit: (repositoryId) => {
397
+ options.runtimeRepositoryActions.openRepositoryPromptForEdit(repositoryId);
398
+ },
399
+ queueArchiveRepository: (repositoryId) => {
400
+ options.queueControlPlaneOps.queueControlPlaneOp(async () => {
401
+ await options.runtimeRepositoryActions.archiveRepositoryById(repositoryId);
402
+ }, 'mouse-archive-repository');
403
+ },
404
+ toggleRepositoryGroup,
405
+ selectLeftNavRepository,
406
+ expandAllRepositoryGroups,
407
+ collapseAllRepositoryGroups,
408
+ enterHomePane: options.navigation.enterHomePane,
409
+ ...(options.navigation.enterNimPane === undefined
410
+ ? {}
411
+ : {
412
+ enterNimPane: options.navigation.enterNimPane,
413
+ }),
414
+ ...(options.showTasksEntry
415
+ ? {
416
+ enterTasksPane: options.navigation.enterTasksPane,
417
+ }
418
+ : {}),
419
+ queueCloseDirectory: (directoryId) => {
420
+ options.queueControlPlaneOps.queueControlPlaneOp(async () => {
421
+ await options.runtimeDirectoryActions.closeDirectory(directoryId);
422
+ }, 'mouse-close-directory');
423
+ },
424
+ toggleShortcutsCollapsed: () => {
425
+ options.workspace.shortcutsCollapsed = !options.workspace.shortcutsCollapsed;
426
+ options.navigation.queuePersistMuxUiState();
427
+ },
428
+ setConversationClickState: (next) => {
429
+ options.workspace.conversationTitleEditClickState = next;
430
+ },
431
+ ensureConversationPaneActive: (conversationId) => {
432
+ options.workspace.mainPaneMode = 'conversation';
433
+ options.workspace.selectLeftNavConversation(conversationId);
434
+ options.workspace.projectPaneSnapshot = null;
435
+ options.workspace.projectPaneScrollTop = 0;
436
+ options.navigation.resetFrameCache();
437
+ },
438
+ beginConversationTitleEdit: (conversationId) => {
439
+ options.conversationLifecycle.beginConversationTitleEdit(conversationId);
440
+ },
441
+ queueActivateConversation: (conversationId) => {
442
+ options.queueControlPlaneOps.queueLatestControlPlaneOp(
443
+ 'left-nav:activate-conversation',
444
+ async ({ signal }) => {
445
+ if (signal.aborted) {
446
+ return;
447
+ }
448
+ await options.conversationLifecycle.activateConversation(conversationId, {
449
+ signal,
450
+ });
451
+ },
452
+ 'mouse-activate-conversation',
453
+ );
454
+ },
455
+ queueActivateConversationAndEdit: (conversationId) => {
456
+ options.queueControlPlaneOps.queueLatestControlPlaneOp(
457
+ 'left-nav:activate-conversation',
458
+ async ({ signal }) => {
459
+ if (signal.aborted) {
460
+ return;
461
+ }
462
+ await options.conversationLifecycle.activateConversation(conversationId, {
463
+ signal,
464
+ });
465
+ if (signal.aborted) {
466
+ return;
467
+ }
468
+ options.conversationLifecycle.beginConversationTitleEdit(conversationId);
469
+ },
470
+ 'mouse-activate-edit-conversation',
471
+ );
472
+ },
473
+ enterProjectPane: options.navigation.enterProjectPane,
474
+ markDirty: options.navigation.markDirty,
475
+ },
476
+ {
477
+ conversationTitleEditDoubleClickWindowMs,
478
+ },
479
+ );
480
+
481
+ const leftRailPointerInput = new RailPointerInput(
482
+ leftRailPointerHandler,
483
+ leftRailPointerHandler,
484
+ {
485
+ hasActiveEdit: () => options.workspace.conversationTitleEdit !== null,
486
+ shouldKeepActiveEdit: (hit) =>
487
+ leftRailPointerHandler.shouldKeepConversationTitleEditActive(hit),
488
+ stopActiveEdit: () => {
489
+ options.conversationLifecycle.stopConversationTitleEdit(true);
490
+ },
491
+ },
492
+ {
493
+ hasSelection: () =>
494
+ options.workspace.selection !== null || options.workspace.selectionDrag !== null,
495
+ clearSelection: () => {
496
+ options.workspace.selection = null;
497
+ options.workspace.selectionDrag = null;
498
+ options.navigation.releaseViewportPinForSelection();
499
+ },
500
+ },
501
+ );
502
+
503
+ const handleRepositoryFoldInput = (input: Buffer): boolean => {
504
+ return (
505
+ repositoryFoldInput.handleRepositoryFoldChords(input) ||
506
+ repositoryFoldInput.handleRepositoryTreeArrow(input)
507
+ );
508
+ };
509
+
510
+ const handleGlobalShortcutInput = (input: Buffer): boolean => {
511
+ return globalShortcutInput.handleInput(input);
512
+ };
513
+
514
+ return {
515
+ handleRepositoryFoldInput,
516
+ handleGlobalShortcutInput,
517
+ leftRailPointerInput,
518
+ };
519
+ }