@jmoyers/harness 0.1.10 → 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.
- package/README.md +31 -35
- package/package.json +31 -11
- package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
- package/packages/harness-ai/src/stream-text.ts +13 -91
- package/packages/harness-ui/src/frame-primitives.ts +158 -0
- package/packages/harness-ui/src/index.ts +18 -0
- package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
- package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
- package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
- package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
- package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
- package/packages/harness-ui/src/interaction/input.ts +420 -0
- package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
- package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
- package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
- package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
- package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
- package/packages/harness-ui/src/kit.ts +476 -0
- package/packages/harness-ui/src/layout.ts +238 -0
- package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
- package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
- package/packages/harness-ui/src/surface.ts +252 -0
- package/packages/harness-ui/src/text-layout.ts +210 -0
- package/packages/nim-core/src/contracts.ts +239 -0
- package/packages/nim-core/src/event-store.ts +299 -0
- package/packages/nim-core/src/events.ts +53 -0
- package/packages/nim-core/src/index.ts +9 -0
- package/packages/nim-core/src/provider-router.ts +129 -0
- package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
- package/packages/nim-core/src/runtime-factory.ts +49 -0
- package/packages/nim-core/src/runtime.ts +1797 -0
- package/packages/nim-core/src/session-store.ts +516 -0
- package/packages/nim-core/src/telemetry.ts +48 -0
- package/packages/nim-test-tui/src/index.ts +150 -0
- package/packages/nim-ui-core/src/index.ts +1 -0
- package/packages/nim-ui-core/src/projection.ts +87 -0
- package/scripts/codex-live-mux-runtime.ts +2 -3721
- package/scripts/control-plane-daemon.ts +24 -2
- package/scripts/harness-bin.js +5 -0
- package/scripts/harness-commands.ts +300 -0
- package/scripts/harness-runtime.ts +82 -0
- package/scripts/harness.ts +33 -3007
- package/scripts/nim-tui-smoke.ts +748 -0
- package/src/cli/auth/runtime.ts +948 -0
- package/src/cli/default-gateway-pointer.ts +193 -0
- package/src/cli/gateway/runtime.ts +1872 -0
- package/src/cli/parsing/flags.ts +23 -0
- package/src/cli/parsing/session.ts +42 -0
- package/src/cli/runtime/context.ts +193 -0
- package/src/cli/runtime-app/application.ts +392 -0
- package/src/cli/runtime-infra/gateway-control.ts +729 -0
- package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
- package/src/cli/workflows/runtime.ts +965 -0
- package/src/clients/tui/left-rail-interactions.ts +519 -0
- package/src/clients/tui/main-pane-interactions.ts +509 -0
- package/src/clients/tui/modal-input-routing.ts +71 -0
- package/src/clients/tui/render-snapshot-adapter.ts +88 -0
- package/src/clients/web/synced-selectors.ts +132 -0
- package/src/codex/live-session.ts +82 -29
- package/src/config/config-core.ts +361 -10
- package/src/config/harness-paths.ts +4 -7
- package/src/config/harness-runtime-migration.ts +142 -19
- package/src/config/harness.config.template.jsonc +33 -0
- package/src/config/secrets-core.ts +92 -4
- package/src/control-plane/agent-realtime-api.ts +82 -427
- package/src/control-plane/prompt/thread-title-namer.ts +49 -23
- package/src/control-plane/session-summary.ts +10 -81
- package/src/control-plane/status/reducer-base.ts +12 -12
- package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
- package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
- package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
- package/src/control-plane/stream-client.ts +12 -2
- package/src/control-plane/stream-command-parser.ts +83 -143
- package/src/control-plane/stream-protocol.ts +53 -37
- package/src/control-plane/stream-server-background.ts +18 -2
- package/src/control-plane/stream-server-command.ts +376 -69
- package/src/control-plane/stream-server-session-runtime.ts +3 -2
- package/src/control-plane/stream-server.ts +943 -80
- package/src/control-plane/stream-session-runtime-types.ts +41 -0
- package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
- package/src/core/state/observed-stream-cursor.ts +43 -0
- package/src/core/state/synced-observed-state.ts +273 -0
- package/src/core/store/harness-synced-store.ts +81 -0
- package/src/diff/budget.ts +136 -0
- package/src/diff/build.ts +289 -0
- package/src/diff/chunker.ts +146 -0
- package/src/diff/git-invoke.ts +315 -0
- package/src/diff/git-parse.ts +472 -0
- package/src/diff/hash.ts +70 -0
- package/src/diff/index.ts +24 -0
- package/src/diff/normalize.ts +134 -0
- package/src/diff/types.ts +178 -0
- package/src/diff-ui/args.ts +346 -0
- package/src/diff-ui/commands.ts +123 -0
- package/src/diff-ui/finder.ts +94 -0
- package/src/diff-ui/highlight.ts +127 -0
- package/src/diff-ui/index.ts +2 -0
- package/src/diff-ui/model.ts +141 -0
- package/src/diff-ui/pager.ts +412 -0
- package/src/diff-ui/render.ts +337 -0
- package/src/diff-ui/runtime.ts +379 -0
- package/src/diff-ui/state.ts +224 -0
- package/src/diff-ui/types.ts +236 -0
- package/src/domain/conversations.ts +11 -7
- package/src/domain/workspace.ts +76 -4
- package/src/mux/control-plane-op-queue.ts +93 -7
- package/src/mux/conversation-rail.ts +28 -71
- package/src/mux/dual-pane-core.ts +13 -13
- package/src/mux/harness-core-ui.ts +313 -42
- package/src/mux/input-shortcuts.ts +22 -112
- package/src/mux/keybinding-catalog.ts +340 -0
- package/src/mux/keybinding-registry.ts +103 -0
- package/src/mux/live-mux/command-menu-open-in.ts +280 -0
- package/src/mux/live-mux/command-menu.ts +167 -4
- package/src/mux/live-mux/conversation-state.ts +13 -0
- package/src/mux/live-mux/directory-resolution.ts +1 -1
- package/src/mux/live-mux/git-parsing.ts +16 -0
- package/src/mux/live-mux/git-snapshot.ts +33 -2
- package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
- package/src/mux/live-mux/home-pane-drop.ts +1 -1
- package/src/mux/live-mux/home-pane-pointer.ts +10 -0
- package/src/mux/live-mux/input-forwarding.ts +59 -2
- package/src/mux/live-mux/left-nav-activation.ts +124 -7
- package/src/mux/live-mux/left-nav.ts +35 -0
- package/src/mux/live-mux/link-click.ts +292 -0
- package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
- package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
- package/src/mux/live-mux/modal-input-reducers.ts +106 -8
- package/src/mux/live-mux/modal-overlays.ts +210 -31
- package/src/mux/live-mux/modal-pointer.ts +3 -7
- package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
- package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
- package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
- package/src/mux/live-mux/pointer-routing.ts +5 -2
- package/src/mux/live-mux/project-pane-pointer.ts +8 -0
- package/src/mux/live-mux/rail-layout.ts +33 -30
- package/src/mux/live-mux/release-notes.ts +383 -0
- package/src/mux/live-mux/render-trace-analysis.ts +52 -7
- package/src/mux/live-mux/repository-folding.ts +3 -0
- package/src/mux/live-mux/selection.ts +0 -4
- package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
- package/src/mux/project-pane-github-review.ts +271 -0
- package/src/mux/render-frame.ts +4 -0
- package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
- package/src/mux/task-composer.ts +21 -14
- package/src/mux/task-focused-pane.ts +118 -117
- package/src/mux/task-screen-keybindings.ts +19 -82
- package/src/mux/workspace-rail-model.ts +270 -104
- package/src/mux/workspace-rail.ts +45 -22
- package/src/pty/session-broker.ts +1 -1
- package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
- package/src/services/control-plane.ts +50 -32
- package/src/services/conversation-lifecycle.ts +118 -87
- package/src/services/conversation-startup-hydration.ts +20 -12
- package/src/services/directory-hydration.ts +21 -16
- package/src/services/event-persistence.ts +7 -0
- package/src/services/left-rail-pointer-handler.ts +329 -0
- package/src/services/mux-ui-state-persistence.ts +5 -1
- package/src/services/recording.ts +34 -26
- package/src/services/runtime-command-menu-agent-tools.ts +1 -1
- package/src/services/runtime-control-actions.ts +79 -61
- package/src/services/runtime-control-plane-ops.ts +122 -83
- package/src/services/runtime-conversation-actions.ts +40 -26
- package/src/services/runtime-conversation-activation.ts +82 -30
- package/src/services/runtime-conversation-starter.ts +80 -48
- package/src/services/runtime-conversation-title-edit.ts +91 -80
- package/src/services/runtime-envelope-handler.ts +107 -105
- package/src/services/runtime-git-state.ts +42 -29
- package/src/services/runtime-layout-resize.ts +3 -1
- package/src/services/runtime-left-rail-render.ts +99 -63
- package/src/services/runtime-nim-cli-session.ts +438 -0
- package/src/services/runtime-nim-session.ts +705 -0
- package/src/services/runtime-nim-tool-bridge.ts +141 -0
- package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
- package/src/services/runtime-process-wiring.ts +29 -36
- package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
- package/src/services/runtime-render-flush.ts +63 -70
- package/src/services/runtime-render-lifecycle.ts +65 -64
- package/src/services/runtime-render-orchestrator.ts +55 -45
- package/src/services/runtime-render-pipeline.ts +106 -103
- package/src/services/runtime-render-state.ts +62 -49
- package/src/services/runtime-repository-actions.ts +97 -70
- package/src/services/runtime-right-pane-render.ts +80 -53
- package/src/services/runtime-shutdown.ts +38 -35
- package/src/services/runtime-stream-subscriptions.ts +35 -27
- package/src/services/runtime-task-composer-persistence.ts +71 -59
- package/src/services/runtime-task-composer-snapshot.ts +14 -0
- package/src/services/runtime-task-editor-actions.ts +46 -29
- package/src/services/runtime-task-pane-actions.ts +220 -134
- package/src/services/runtime-task-pane-shortcuts.ts +323 -123
- package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
- package/src/services/runtime-workspace-observed-events.ts +33 -184
- package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
- package/src/services/session-diagnostics-store.ts +217 -0
- package/src/services/startup-background-resume.ts +26 -21
- package/src/services/startup-orchestrator.ts +16 -13
- package/src/services/startup-paint-tracker.ts +29 -21
- package/src/services/startup-persisted-conversation-queue.ts +19 -13
- package/src/services/startup-settled-gate.ts +25 -15
- package/src/services/startup-shutdown.ts +18 -22
- package/src/services/startup-state-hydration.ts +44 -34
- package/src/services/startup-visibility.ts +12 -4
- package/src/services/task-pane-selection-actions.ts +89 -72
- package/src/services/task-planning-hydration.ts +24 -18
- package/src/services/task-planning-observed-events.ts +50 -52
- package/src/services/workspace-observed-events.ts +66 -63
- package/src/storage/storage-lifecycle-core.ts +438 -0
- package/src/store/control-plane-store-normalize.ts +33 -242
- package/src/store/control-plane-store-types.ts +1 -35
- package/src/store/control-plane-store.ts +396 -56
- package/src/store/event-store.ts +397 -3
- package/src/terminal/snapshot-oracle.ts +207 -94
- package/src/ui/mux-theme.ts +112 -8
- package/src/ui/panes/home-gridfire.ts +40 -31
- package/src/ui/panes/home.ts +10 -2
- package/src/ui/panes/nim.ts +315 -0
- package/src/mux/live-mux/actions-task.ts +0 -115
- package/src/mux/live-mux/left-rail-actions.ts +0 -118
- package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
- package/src/mux/live-mux/left-rail-pointer.ts +0 -74
- package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
- package/src/services/runtime-directory-actions.ts +0 -164
- package/src/services/runtime-input-pipeline.ts +0 -50
- package/src/services/runtime-input-router.ts +0 -189
- package/src/services/runtime-main-pane-input.ts +0 -230
- package/src/services/runtime-modal-input.ts +0 -119
- package/src/services/runtime-navigation-input.ts +0 -197
- package/src/services/runtime-rail-input.ts +0 -278
- package/src/services/runtime-task-pane.ts +0 -62
- package/src/services/runtime-workspace-actions.ts +0 -158
- package/src/ui/conversation-input-forwarder.ts +0 -114
- package/src/ui/conversation-selection-input.ts +0 -103
- package/src/ui/global-shortcut-input.ts +0 -89
- package/src/ui/input.ts +0 -238
- package/src/ui/kit.ts +0 -509
- package/src/ui/left-nav-input.ts +0 -80
- package/src/ui/left-rail-pointer-input.ts +0 -148
- package/src/ui/repository-fold-input.ts +0 -91
- package/src/ui/surface.ts +0 -224
|
@@ -9,7 +9,7 @@ interface RepositoryRecordLike {
|
|
|
9
9
|
readonly archivedAt: string | null;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
interface TaskPaneSelectionActionsOptions<TTaskRecord extends TaskRecordLike> {
|
|
12
|
+
export interface TaskPaneSelectionActionsOptions<TTaskRecord extends TaskRecordLike> {
|
|
13
13
|
readonly workspace: WorkspaceModel;
|
|
14
14
|
readonly taskRecordById: (taskId: string) => TTaskRecord | undefined;
|
|
15
15
|
readonly hasTask: (taskId: string) => boolean;
|
|
@@ -21,133 +21,150 @@ interface TaskPaneSelectionActionsOptions<TTaskRecord extends TaskRecordLike> {
|
|
|
21
21
|
readonly markDirty: () => void;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export
|
|
25
|
-
|
|
24
|
+
export interface TaskPaneSelectionActions {
|
|
25
|
+
syncTaskPaneSelectionFocus(): void;
|
|
26
|
+
syncTaskPaneSelection(): void;
|
|
27
|
+
syncTaskPaneRepositorySelection(): void;
|
|
28
|
+
focusDraftComposer(): void;
|
|
29
|
+
focusTaskComposer(taskId: string): void;
|
|
30
|
+
selectTaskById(taskId: string): void;
|
|
31
|
+
selectRepositoryById(repositoryId: string): void;
|
|
32
|
+
}
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
export function createTaskPaneSelectionActions<TTaskRecord extends TaskRecordLike>(
|
|
35
|
+
options: TaskPaneSelectionActionsOptions<TTaskRecord>,
|
|
36
|
+
): TaskPaneSelectionActions {
|
|
37
|
+
function syncTaskPaneSelectionFocus(): void {
|
|
28
38
|
const hasTaskSelection =
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
options.workspace.taskPaneSelectedTaskId !== null &&
|
|
40
|
+
options.hasTask(options.workspace.taskPaneSelectedTaskId);
|
|
31
41
|
const hasRepositorySelection =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
42
|
+
options.workspace.taskPaneSelectedRepositoryId !== null &&
|
|
43
|
+
options.hasRepository(options.workspace.taskPaneSelectedRepositoryId);
|
|
44
|
+
if (options.workspace.taskPaneSelectionFocus === 'task' && hasTaskSelection) {
|
|
35
45
|
return;
|
|
36
46
|
}
|
|
37
|
-
if (
|
|
47
|
+
if (options.workspace.taskPaneSelectionFocus === 'repository' && hasRepositorySelection) {
|
|
38
48
|
return;
|
|
39
49
|
}
|
|
40
50
|
if (hasTaskSelection) {
|
|
41
|
-
|
|
51
|
+
options.workspace.taskPaneSelectionFocus = 'task';
|
|
42
52
|
return;
|
|
43
53
|
}
|
|
44
54
|
if (hasRepositorySelection) {
|
|
45
|
-
|
|
55
|
+
options.workspace.taskPaneSelectionFocus = 'repository';
|
|
46
56
|
return;
|
|
47
57
|
}
|
|
48
|
-
|
|
58
|
+
options.workspace.taskPaneSelectionFocus = 'task';
|
|
49
59
|
}
|
|
50
60
|
|
|
51
|
-
syncTaskPaneSelection(): void {
|
|
52
|
-
const scopedTaskIds = new Set(
|
|
53
|
-
this.options.selectedRepositoryTasks().map((task) => task.taskId),
|
|
54
|
-
);
|
|
61
|
+
function syncTaskPaneSelection(): void {
|
|
62
|
+
const scopedTaskIds = new Set(options.selectedRepositoryTasks().map((task) => task.taskId));
|
|
55
63
|
if (
|
|
56
|
-
|
|
57
|
-
!scopedTaskIds.has(
|
|
64
|
+
options.workspace.taskPaneSelectedTaskId !== null &&
|
|
65
|
+
!scopedTaskIds.has(options.workspace.taskPaneSelectedTaskId)
|
|
58
66
|
) {
|
|
59
|
-
|
|
67
|
+
options.workspace.taskPaneSelectedTaskId = null;
|
|
60
68
|
}
|
|
61
|
-
if (
|
|
62
|
-
const scopedTasks =
|
|
63
|
-
|
|
69
|
+
if (options.workspace.taskPaneSelectedTaskId === null) {
|
|
70
|
+
const scopedTasks = options.selectedRepositoryTasks();
|
|
71
|
+
options.workspace.taskPaneSelectedTaskId = scopedTasks[0]?.taskId ?? null;
|
|
64
72
|
}
|
|
65
|
-
|
|
73
|
+
syncTaskPaneSelectionFocus();
|
|
66
74
|
if (
|
|
67
|
-
|
|
68
|
-
!scopedTaskIds.has(
|
|
75
|
+
options.workspace.taskEditorTarget.kind === 'task' &&
|
|
76
|
+
!scopedTaskIds.has(options.workspace.taskEditorTarget.taskId)
|
|
69
77
|
) {
|
|
70
|
-
|
|
78
|
+
focusDraftComposer();
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
|
|
74
|
-
syncTaskPaneRepositorySelection(): void {
|
|
75
|
-
if (
|
|
76
|
-
const selectedRepository =
|
|
77
|
-
|
|
82
|
+
function syncTaskPaneRepositorySelection(): void {
|
|
83
|
+
if (options.workspace.taskPaneSelectedRepositoryId !== null) {
|
|
84
|
+
const selectedRepository = options.repositoryById(
|
|
85
|
+
options.workspace.taskPaneSelectedRepositoryId,
|
|
78
86
|
);
|
|
79
87
|
if (selectedRepository === undefined || selectedRepository.archivedAt !== null) {
|
|
80
|
-
|
|
88
|
+
options.workspace.taskPaneSelectedRepositoryId = null;
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
this.options.activeRepositoryIds()[0] ?? null;
|
|
91
|
+
if (options.workspace.taskPaneSelectedRepositoryId === null) {
|
|
92
|
+
options.workspace.taskPaneSelectedRepositoryId = options.activeRepositoryIds()[0] ?? null;
|
|
86
93
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
options.workspace.taskRepositoryDropdownOpen = false;
|
|
95
|
+
syncTaskPaneSelectionFocus();
|
|
96
|
+
syncTaskPaneSelection();
|
|
90
97
|
}
|
|
91
98
|
|
|
92
|
-
focusDraftComposer(): void {
|
|
93
|
-
if (
|
|
94
|
-
|
|
99
|
+
function focusDraftComposer(): void {
|
|
100
|
+
if (options.workspace.taskEditorTarget.kind === 'task') {
|
|
101
|
+
options.flushTaskComposerPersist(options.workspace.taskEditorTarget.taskId);
|
|
95
102
|
}
|
|
96
|
-
|
|
103
|
+
options.workspace.taskEditorTarget = {
|
|
97
104
|
kind: 'draft',
|
|
98
105
|
};
|
|
99
|
-
|
|
100
|
-
|
|
106
|
+
options.workspace.taskPaneSelectionFocus = 'task';
|
|
107
|
+
options.markDirty();
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
focusTaskComposer(taskId: string): void {
|
|
104
|
-
if (!
|
|
110
|
+
function focusTaskComposer(taskId: string): void {
|
|
111
|
+
if (!options.hasTask(taskId)) {
|
|
105
112
|
return;
|
|
106
113
|
}
|
|
107
114
|
if (
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
options.workspace.taskEditorTarget.kind === 'task' &&
|
|
116
|
+
options.workspace.taskEditorTarget.taskId !== taskId
|
|
110
117
|
) {
|
|
111
|
-
|
|
118
|
+
options.flushTaskComposerPersist(options.workspace.taskEditorTarget.taskId);
|
|
112
119
|
}
|
|
113
|
-
|
|
120
|
+
options.workspace.taskEditorTarget = {
|
|
114
121
|
kind: 'task',
|
|
115
122
|
taskId,
|
|
116
123
|
};
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
options.workspace.taskPaneSelectedTaskId = taskId;
|
|
125
|
+
options.workspace.taskPaneSelectionFocus = 'task';
|
|
126
|
+
options.workspace.taskPaneNotice = null;
|
|
127
|
+
options.markDirty();
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
selectTaskById(taskId: string): void {
|
|
124
|
-
const taskRecord =
|
|
130
|
+
function selectTaskById(taskId: string): void {
|
|
131
|
+
const taskRecord = options.taskRecordById(taskId);
|
|
125
132
|
if (taskRecord === undefined) {
|
|
126
133
|
return;
|
|
127
134
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (taskRecord.repositoryId !== null &&
|
|
131
|
-
|
|
135
|
+
options.workspace.taskPaneSelectedTaskId = taskId;
|
|
136
|
+
options.workspace.taskPaneSelectionFocus = 'task';
|
|
137
|
+
if (taskRecord.repositoryId !== null && options.hasRepository(taskRecord.repositoryId)) {
|
|
138
|
+
options.workspace.taskPaneSelectedRepositoryId = taskRecord.repositoryId;
|
|
132
139
|
}
|
|
133
|
-
|
|
140
|
+
focusTaskComposer(taskId);
|
|
134
141
|
}
|
|
135
142
|
|
|
136
|
-
selectRepositoryById(repositoryId: string): void {
|
|
137
|
-
if (!
|
|
143
|
+
function selectRepositoryById(repositoryId: string): void {
|
|
144
|
+
if (!options.hasRepository(repositoryId)) {
|
|
138
145
|
return;
|
|
139
146
|
}
|
|
140
|
-
if (
|
|
141
|
-
|
|
147
|
+
if (options.workspace.taskEditorTarget.kind === 'task') {
|
|
148
|
+
options.flushTaskComposerPersist(options.workspace.taskEditorTarget.taskId);
|
|
142
149
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
options.workspace.taskPaneSelectedRepositoryId = repositoryId;
|
|
151
|
+
options.workspace.taskRepositoryDropdownOpen = false;
|
|
152
|
+
options.workspace.taskPaneSelectionFocus = 'repository';
|
|
153
|
+
options.workspace.taskEditorTarget = {
|
|
147
154
|
kind: 'draft',
|
|
148
155
|
};
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
156
|
+
syncTaskPaneSelection();
|
|
157
|
+
options.workspace.taskPaneNotice = null;
|
|
158
|
+
options.markDirty();
|
|
152
159
|
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
syncTaskPaneSelectionFocus,
|
|
163
|
+
syncTaskPaneSelection,
|
|
164
|
+
syncTaskPaneRepositorySelection,
|
|
165
|
+
focusDraftComposer,
|
|
166
|
+
focusTaskComposer,
|
|
167
|
+
selectTaskById,
|
|
168
|
+
selectRepositoryById,
|
|
169
|
+
};
|
|
153
170
|
}
|
|
@@ -14,7 +14,7 @@ interface TaskPlanningHydrationServiceControlPlane<
|
|
|
14
14
|
listTasks(limit: number): Promise<readonly TTaskRecord[]>;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
interface TaskPlanningHydrationServiceOptions<
|
|
17
|
+
export interface TaskPlanningHydrationServiceOptions<
|
|
18
18
|
TRepositoryRecord extends RepositoryRecordLike,
|
|
19
19
|
TTaskRecord extends TaskRecordLike,
|
|
20
20
|
> {
|
|
@@ -32,27 +32,33 @@ interface TaskPlanningHydrationServiceOptions<
|
|
|
32
32
|
readonly taskLimit: number;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export
|
|
35
|
+
export interface TaskPlanningHydrationService {
|
|
36
|
+
hydrate(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function createTaskPlanningHydrationService<
|
|
36
40
|
TRepositoryRecord extends RepositoryRecordLike,
|
|
37
41
|
TTaskRecord extends TaskRecordLike,
|
|
38
|
-
>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for (const repository of await this.options.controlPlaneService.listRepositories()) {
|
|
46
|
-
this.options.setRepository(repository);
|
|
42
|
+
>(
|
|
43
|
+
options: TaskPlanningHydrationServiceOptions<TRepositoryRecord, TTaskRecord>,
|
|
44
|
+
): TaskPlanningHydrationService {
|
|
45
|
+
async function hydrate(): Promise<void> {
|
|
46
|
+
options.clearRepositories();
|
|
47
|
+
for (const repository of await options.controlPlaneService.listRepositories()) {
|
|
48
|
+
options.setRepository(repository);
|
|
47
49
|
}
|
|
48
|
-
|
|
50
|
+
options.syncTaskPaneRepositorySelection();
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
for (const task of await
|
|
52
|
-
|
|
52
|
+
options.clearTasks();
|
|
53
|
+
for (const task of await options.controlPlaneService.listTasks(options.taskLimit)) {
|
|
54
|
+
options.setTask(task);
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
options.syncTaskPaneSelection();
|
|
57
|
+
options.syncTaskPaneRepositorySelection();
|
|
58
|
+
options.markDirty();
|
|
57
59
|
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
hydrate,
|
|
63
|
+
};
|
|
58
64
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { StreamObservedEvent } from '../control-plane/stream-protocol.ts';
|
|
2
|
-
|
|
3
1
|
interface RepositoryRecordLike {
|
|
4
2
|
readonly repositoryId: string;
|
|
5
3
|
readonly archivedAt: string | null;
|
|
@@ -9,13 +7,29 @@ interface TaskRecordLike {
|
|
|
9
7
|
readonly taskId: string;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
|
-
interface
|
|
10
|
+
interface TaskPlanningSyncedProjectionState<
|
|
11
|
+
TRepositoryRecord extends RepositoryRecordLike,
|
|
12
|
+
TTaskRecord extends TaskRecordLike,
|
|
13
|
+
> {
|
|
14
|
+
readonly repositoriesById: Readonly<Record<string, TRepositoryRecord>>;
|
|
15
|
+
readonly tasksById: Readonly<Record<string, TTaskRecord>>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface TaskPlanningSyncedProjectionInput<
|
|
19
|
+
TRepositoryRecord extends RepositoryRecordLike,
|
|
20
|
+
TTaskRecord extends TaskRecordLike,
|
|
21
|
+
> {
|
|
22
|
+
readonly changed: boolean;
|
|
23
|
+
readonly state: TaskPlanningSyncedProjectionState<TRepositoryRecord, TTaskRecord>;
|
|
24
|
+
readonly removedTaskIds: readonly string[];
|
|
25
|
+
readonly upsertedRepositoryIds: readonly string[];
|
|
26
|
+
readonly upsertedTaskIds: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface TaskPlanningSyncedProjectionOptions<
|
|
13
30
|
TRepositoryRecord extends RepositoryRecordLike,
|
|
14
31
|
TTaskRecord extends TaskRecordLike,
|
|
15
32
|
> {
|
|
16
|
-
readonly parseRepositoryRecord: (value: unknown) => TRepositoryRecord | null;
|
|
17
|
-
readonly parseTaskRecord: (value: unknown) => TTaskRecord | null;
|
|
18
|
-
readonly getRepository: (repositoryId: string) => TRepositoryRecord | undefined;
|
|
19
33
|
readonly setRepository: (repositoryId: string, repository: TRepositoryRecord) => void;
|
|
20
34
|
readonly setTask: (task: TTaskRecord) => void;
|
|
21
35
|
readonly deleteTask: (taskId: string) => boolean;
|
|
@@ -24,66 +38,50 @@ interface TaskPlanningObservedEventsOptions<
|
|
|
24
38
|
readonly markDirty: () => void;
|
|
25
39
|
}
|
|
26
40
|
|
|
27
|
-
export class
|
|
41
|
+
export class TaskPlanningSyncedProjection<
|
|
28
42
|
TRepositoryRecord extends RepositoryRecordLike,
|
|
29
43
|
TTaskRecord extends TaskRecordLike,
|
|
30
44
|
> {
|
|
31
45
|
constructor(
|
|
32
|
-
private readonly options:
|
|
46
|
+
private readonly options: TaskPlanningSyncedProjectionOptions<TRepositoryRecord, TTaskRecord>,
|
|
33
47
|
) {}
|
|
34
48
|
|
|
35
|
-
apply(
|
|
36
|
-
if (
|
|
37
|
-
const repository = this.options.parseRepositoryRecord(observed.repository);
|
|
38
|
-
if (repository !== null) {
|
|
39
|
-
this.options.setRepository(repository.repositoryId, repository);
|
|
40
|
-
this.options.syncTaskPaneRepositorySelection();
|
|
41
|
-
this.options.markDirty();
|
|
42
|
-
}
|
|
49
|
+
apply(reduction: TaskPlanningSyncedProjectionInput<TRepositoryRecord, TTaskRecord>): void {
|
|
50
|
+
if (!reduction.changed) {
|
|
43
51
|
return;
|
|
44
52
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
this.options.syncTaskPaneRepositorySelection();
|
|
53
|
-
this.options.markDirty();
|
|
53
|
+
|
|
54
|
+
let repositoriesChanged = false;
|
|
55
|
+
for (const repositoryId of reduction.upsertedRepositoryIds) {
|
|
56
|
+
const repository = reduction.state.repositoriesById[repositoryId];
|
|
57
|
+
if (repository === undefined) {
|
|
58
|
+
continue;
|
|
54
59
|
}
|
|
55
|
-
|
|
60
|
+
this.options.setRepository(repositoryId, repository);
|
|
61
|
+
repositoriesChanged = true;
|
|
56
62
|
}
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.options.setTask(task);
|
|
61
|
-
this.options.syncTaskPaneSelection();
|
|
62
|
-
this.options.markDirty();
|
|
63
|
-
}
|
|
64
|
-
return;
|
|
63
|
+
if (repositoriesChanged) {
|
|
64
|
+
this.options.syncTaskPaneRepositorySelection();
|
|
65
|
+
this.options.markDirty();
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
|
|
68
|
+
let tasksChanged = false;
|
|
69
|
+
for (const taskId of reduction.removedTaskIds) {
|
|
70
|
+
if (this.options.deleteTask(taskId)) {
|
|
71
|
+
tasksChanged = true;
|
|
70
72
|
}
|
|
71
|
-
return;
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (task === null) {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
this.options.setTask(task);
|
|
81
|
-
changed = true;
|
|
82
|
-
}
|
|
83
|
-
if (changed) {
|
|
84
|
-
this.options.syncTaskPaneSelection();
|
|
85
|
-
this.options.markDirty();
|
|
74
|
+
for (const taskId of reduction.upsertedTaskIds) {
|
|
75
|
+
const task = reduction.state.tasksById[taskId];
|
|
76
|
+
if (task === undefined) {
|
|
77
|
+
continue;
|
|
86
78
|
}
|
|
79
|
+
this.options.setTask(task);
|
|
80
|
+
tasksChanged = true;
|
|
81
|
+
}
|
|
82
|
+
if (tasksChanged) {
|
|
83
|
+
this.options.syncTaskPaneSelection();
|
|
84
|
+
this.options.markDirty();
|
|
87
85
|
}
|
|
88
86
|
}
|
|
89
87
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type { StreamObservedEvent } from '../control-plane/stream-protocol.ts';
|
|
2
|
-
|
|
3
1
|
interface DirectoryRecordLike {
|
|
4
2
|
readonly directoryId: string;
|
|
5
3
|
}
|
|
@@ -15,99 +13,104 @@ interface WorkspaceObservedApplyResult {
|
|
|
15
13
|
readonly removedDirectoryIds: readonly string[];
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
interface
|
|
16
|
+
interface WorkspaceSyncedProjectionState<
|
|
17
|
+
TDirectoryRecord extends DirectoryRecordLike,
|
|
18
|
+
TConversationRecord extends ConversationRecordLike,
|
|
19
|
+
> {
|
|
20
|
+
readonly directoriesById: Readonly<Record<string, TDirectoryRecord>>;
|
|
21
|
+
readonly conversationsById: Readonly<Record<string, TConversationRecord>>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface WorkspaceSyncedProjectionInput<
|
|
25
|
+
TDirectoryRecord extends DirectoryRecordLike,
|
|
26
|
+
TConversationRecord extends ConversationRecordLike,
|
|
27
|
+
> {
|
|
28
|
+
readonly changed: boolean;
|
|
29
|
+
readonly state: WorkspaceSyncedProjectionState<TDirectoryRecord, TConversationRecord>;
|
|
30
|
+
readonly removedConversationIds: readonly string[];
|
|
31
|
+
readonly removedDirectoryIds: readonly string[];
|
|
32
|
+
readonly upsertedDirectoryIds: readonly string[];
|
|
33
|
+
readonly upsertedConversationIds: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface WorkspaceSyncedProjectionOptions<
|
|
19
37
|
TDirectoryRecord extends DirectoryRecordLike,
|
|
20
38
|
TConversationRecord extends ConversationRecordLike,
|
|
21
39
|
> {
|
|
22
|
-
readonly parseDirectoryRecord: (value: unknown) => TDirectoryRecord | null;
|
|
23
|
-
readonly parseConversationRecord: (value: unknown) => TConversationRecord | null;
|
|
24
40
|
readonly setDirectory: (directoryId: string, directory: TDirectoryRecord) => void;
|
|
25
41
|
readonly deleteDirectory: (directoryId: string) => boolean;
|
|
26
42
|
readonly deleteDirectoryGitState: (directoryId: string) => void;
|
|
27
43
|
readonly syncGitStateWithDirectories: () => void;
|
|
28
44
|
readonly upsertConversationFromPersistedRecord: (record: TConversationRecord) => void;
|
|
29
45
|
readonly removeConversation: (sessionId: string) => boolean;
|
|
30
|
-
readonly orderedConversationIds: () => readonly string[];
|
|
31
|
-
readonly conversationDirectoryId: (sessionId: string) => string | null;
|
|
32
46
|
}
|
|
33
47
|
|
|
34
|
-
export class
|
|
48
|
+
export class WorkspaceSyncedProjection<
|
|
35
49
|
TDirectoryRecord extends DirectoryRecordLike,
|
|
36
50
|
TConversationRecord extends ConversationRecordLike,
|
|
37
51
|
> {
|
|
38
52
|
constructor(
|
|
39
|
-
private readonly options:
|
|
53
|
+
private readonly options: WorkspaceSyncedProjectionOptions<
|
|
54
|
+
TDirectoryRecord,
|
|
55
|
+
TConversationRecord
|
|
56
|
+
>,
|
|
40
57
|
) {}
|
|
41
58
|
|
|
42
|
-
apply(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
changed: false,
|
|
48
|
-
removedConversationIds: [],
|
|
49
|
-
removedDirectoryIds: [],
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
this.options.setDirectory(directory.directoryId, directory);
|
|
53
|
-
this.options.syncGitStateWithDirectories();
|
|
59
|
+
apply(
|
|
60
|
+
reduction: WorkspaceSyncedProjectionInput<TDirectoryRecord, TConversationRecord>,
|
|
61
|
+
): WorkspaceObservedApplyResult {
|
|
62
|
+
if (!reduction.changed) {
|
|
54
63
|
return {
|
|
55
|
-
changed:
|
|
64
|
+
changed: false,
|
|
56
65
|
removedConversationIds: [],
|
|
57
66
|
removedDirectoryIds: [],
|
|
58
67
|
};
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
+
let changed = false;
|
|
71
|
+
const removedConversationIds: string[] = [];
|
|
72
|
+
const removedDirectoryIds: string[] = [];
|
|
73
|
+
|
|
74
|
+
for (const directoryId of reduction.upsertedDirectoryIds) {
|
|
75
|
+
const directory = reduction.state.directoriesById[directoryId];
|
|
76
|
+
if (directory === undefined) {
|
|
77
|
+
continue;
|
|
70
78
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this.options.syncGitStateWithDirectories();
|
|
74
|
-
return {
|
|
75
|
-
changed: removedDirectory || removedConversationIds.length > 0,
|
|
76
|
-
removedConversationIds,
|
|
77
|
-
removedDirectoryIds: removedDirectory ? [observed.directoryId] : [],
|
|
78
|
-
};
|
|
79
|
+
this.options.setDirectory(directoryId, directory);
|
|
80
|
+
changed = true;
|
|
79
81
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
changed: false,
|
|
86
|
-
removedConversationIds: [],
|
|
87
|
-
removedDirectoryIds: [],
|
|
88
|
-
};
|
|
82
|
+
for (const conversationId of reduction.upsertedConversationIds) {
|
|
83
|
+
const conversation = reduction.state.conversationsById[conversationId];
|
|
84
|
+
if (conversation === undefined) {
|
|
85
|
+
continue;
|
|
89
86
|
}
|
|
90
87
|
this.options.upsertConversationFromPersistedRecord(conversation);
|
|
91
|
-
|
|
92
|
-
changed: true,
|
|
93
|
-
removedConversationIds: [],
|
|
94
|
-
removedDirectoryIds: [],
|
|
95
|
-
};
|
|
88
|
+
changed = true;
|
|
96
89
|
}
|
|
97
90
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
91
|
+
for (const sessionId of reduction.removedConversationIds) {
|
|
92
|
+
if (!this.options.removeConversation(sessionId)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
removedConversationIds.push(sessionId);
|
|
96
|
+
changed = true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const directoryId of reduction.removedDirectoryIds) {
|
|
100
|
+
if (this.options.deleteDirectory(directoryId)) {
|
|
101
|
+
removedDirectoryIds.push(directoryId);
|
|
102
|
+
changed = true;
|
|
103
|
+
}
|
|
104
|
+
this.options.deleteDirectoryGitState(directoryId);
|
|
105
|
+
}
|
|
106
|
+
if (reduction.upsertedDirectoryIds.length > 0 || reduction.removedDirectoryIds.length > 0) {
|
|
107
|
+
this.options.syncGitStateWithDirectories();
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
return {
|
|
108
|
-
changed
|
|
109
|
-
removedConversationIds
|
|
110
|
-
removedDirectoryIds
|
|
111
|
+
changed,
|
|
112
|
+
removedConversationIds,
|
|
113
|
+
removedDirectoryIds,
|
|
111
114
|
};
|
|
112
115
|
}
|
|
113
116
|
}
|