@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
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { WorkspaceModel } from '../domain/workspace.ts';
|
|
2
2
|
import type { RepositoryManager } from '../domain/repositories.ts';
|
|
3
|
+
import type { ProjectPaneGitHubReviewSummary } from '../mux/project-pane-github-review.ts';
|
|
3
4
|
|
|
4
|
-
interface RuntimeLeftRailRenderLayout {
|
|
5
|
+
export interface RuntimeLeftRailRenderLayout {
|
|
5
6
|
readonly cols: number;
|
|
6
7
|
readonly paneRows: number;
|
|
7
8
|
readonly leftCols: number;
|
|
@@ -27,7 +28,6 @@ interface LeftRailPaneLike<
|
|
|
27
28
|
TConversationRecord,
|
|
28
29
|
TGitSummary,
|
|
29
30
|
TProcessUsage,
|
|
30
|
-
TShortcutBindings,
|
|
31
31
|
TRailViewRows,
|
|
32
32
|
> {
|
|
33
33
|
render(input: {
|
|
@@ -44,12 +44,19 @@ interface LeftRailPaneLike<
|
|
|
44
44
|
projectSelectionEnabled: boolean;
|
|
45
45
|
repositorySelectionEnabled: boolean;
|
|
46
46
|
homeSelectionEnabled: boolean;
|
|
47
|
+
nimSelectionEnabled: boolean;
|
|
48
|
+
tasksSelectionEnabled: boolean;
|
|
49
|
+
showTasksEntry: boolean;
|
|
50
|
+
showGitHubIntegration: boolean;
|
|
51
|
+
visibleGitHubDirectoryIds?: ReadonlySet<string>;
|
|
52
|
+
expandedGitHubDirectoryIds?: ReadonlySet<string>;
|
|
53
|
+
githubReviewByDirectoryId: ReadonlyMap<string, ProjectPaneGitHubReviewSummary>;
|
|
54
|
+
githubSelectionEnabled: boolean;
|
|
55
|
+
activeGitHubProjectId: string | null;
|
|
47
56
|
repositoriesCollapsed: boolean;
|
|
48
57
|
collapsedRepositoryGroupIds: ReadonlySet<string>;
|
|
49
|
-
shortcutsCollapsed: boolean;
|
|
50
58
|
gitSummaryByDirectoryId: ReadonlyMap<string, TGitSummary>;
|
|
51
59
|
processUsageBySessionId: ReadonlyMap<string, TProcessUsage>;
|
|
52
|
-
shortcutBindings: TShortcutBindings;
|
|
53
60
|
loadingGitSummary: TGitSummary;
|
|
54
61
|
}): {
|
|
55
62
|
readonly ansiRows: readonly string[];
|
|
@@ -57,14 +64,27 @@ interface LeftRailPaneLike<
|
|
|
57
64
|
};
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
interface
|
|
67
|
+
export interface RuntimeLeftRailRenderSnapshot<
|
|
68
|
+
TDirectoryRecord,
|
|
69
|
+
TConversationRecord,
|
|
70
|
+
TRepositoryRecord,
|
|
71
|
+
TProcessUsage,
|
|
72
|
+
> {
|
|
73
|
+
readonly repositories: ReadonlyMap<string, TRepositoryRecord>;
|
|
74
|
+
readonly directories: ReadonlyMap<string, TDirectoryRecord>;
|
|
75
|
+
readonly conversations: ReadonlyMap<string, TConversationRecord>;
|
|
76
|
+
readonly orderedConversationIds: readonly string[];
|
|
77
|
+
readonly processUsageBySessionId: ReadonlyMap<string, TProcessUsage>;
|
|
78
|
+
readonly activeConversationId: string | null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface RuntimeLeftRailRenderOptions<
|
|
61
82
|
TDirectoryRecord,
|
|
62
83
|
TConversationRecord,
|
|
63
84
|
TRepositoryRecord,
|
|
64
85
|
TRepositorySnapshot,
|
|
65
86
|
TGitSummary,
|
|
66
87
|
TProcessUsage,
|
|
67
|
-
TShortcutBindings,
|
|
68
88
|
TRailViewRows,
|
|
69
89
|
> {
|
|
70
90
|
readonly leftRailPane: LeftRailPaneLike<
|
|
@@ -75,7 +95,6 @@ interface RuntimeLeftRailRenderOptions<
|
|
|
75
95
|
TConversationRecord,
|
|
76
96
|
TGitSummary,
|
|
77
97
|
TProcessUsage,
|
|
78
|
-
TShortcutBindings,
|
|
79
98
|
TRailViewRows
|
|
80
99
|
>;
|
|
81
100
|
readonly sessionProjectionInstrumentation: SessionProjectionInstrumentationLike<
|
|
@@ -84,76 +103,93 @@ interface RuntimeLeftRailRenderOptions<
|
|
|
84
103
|
>;
|
|
85
104
|
readonly workspace: WorkspaceModel;
|
|
86
105
|
readonly repositoryManager: RepositoryManager<TRepositoryRecord, TRepositorySnapshot>;
|
|
87
|
-
readonly repositories: ReadonlyMap<string, TRepositoryRecord>;
|
|
88
106
|
readonly repositoryAssociationByDirectoryId: ReadonlyMap<string, string>;
|
|
89
107
|
readonly directoryRepositorySnapshotByDirectoryId: ReadonlyMap<string, TRepositorySnapshot>;
|
|
90
|
-
readonly directories: ReadonlyMap<string, TDirectoryRecord>;
|
|
91
|
-
readonly conversations: ReadonlyMap<string, TConversationRecord>;
|
|
92
108
|
readonly gitSummaryByDirectoryId: ReadonlyMap<string, TGitSummary>;
|
|
93
|
-
readonly processUsageBySessionId: () => ReadonlyMap<string, TProcessUsage>;
|
|
94
|
-
readonly shortcutBindings: TShortcutBindings;
|
|
95
109
|
readonly loadingGitSummary: TGitSummary;
|
|
96
|
-
readonly
|
|
97
|
-
readonly
|
|
110
|
+
readonly showGitHubIntegration?: boolean;
|
|
111
|
+
readonly visibleGitHubDirectoryIds?: ReadonlySet<string>;
|
|
112
|
+
readonly expandedGitHubDirectoryIds?: ReadonlySet<string>;
|
|
113
|
+
readonly githubReviewByDirectoryId?: ReadonlyMap<string, ProjectPaneGitHubReviewSummary>;
|
|
114
|
+
readonly showTasksEntry?: boolean;
|
|
98
115
|
}
|
|
99
116
|
|
|
100
|
-
export
|
|
117
|
+
export function renderRuntimeLeftRail<
|
|
101
118
|
TDirectoryRecord,
|
|
102
119
|
TConversationRecord,
|
|
103
120
|
TRepositoryRecord,
|
|
104
121
|
TRepositorySnapshot,
|
|
105
122
|
TGitSummary,
|
|
106
123
|
TProcessUsage,
|
|
107
|
-
TShortcutBindings,
|
|
108
124
|
TRailViewRows,
|
|
109
|
-
>
|
|
110
|
-
|
|
111
|
-
|
|
125
|
+
>(
|
|
126
|
+
options: RuntimeLeftRailRenderOptions<
|
|
127
|
+
TDirectoryRecord,
|
|
128
|
+
TConversationRecord,
|
|
129
|
+
TRepositoryRecord,
|
|
130
|
+
TRepositorySnapshot,
|
|
131
|
+
TGitSummary,
|
|
132
|
+
TProcessUsage,
|
|
133
|
+
TRailViewRows
|
|
134
|
+
>,
|
|
135
|
+
input: {
|
|
136
|
+
readonly layout: RuntimeLeftRailRenderLayout;
|
|
137
|
+
readonly snapshot: RuntimeLeftRailRenderSnapshot<
|
|
112
138
|
TDirectoryRecord,
|
|
113
139
|
TConversationRecord,
|
|
114
140
|
TRepositoryRecord,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
141
|
+
TProcessUsage
|
|
142
|
+
>;
|
|
143
|
+
},
|
|
144
|
+
): {
|
|
145
|
+
readonly ansiRows: readonly string[];
|
|
146
|
+
readonly viewRows: TRailViewRows;
|
|
147
|
+
} {
|
|
148
|
+
options.sessionProjectionInstrumentation.refreshSelectorSnapshot(
|
|
149
|
+
'render',
|
|
150
|
+
input.snapshot.directories,
|
|
151
|
+
input.snapshot.conversations,
|
|
152
|
+
input.snapshot.orderedConversationIds,
|
|
153
|
+
);
|
|
154
|
+
const renderInput = {
|
|
155
|
+
layout: input.layout,
|
|
156
|
+
repositories: input.snapshot.repositories,
|
|
157
|
+
repositoryAssociationByDirectoryId: options.repositoryAssociationByDirectoryId,
|
|
158
|
+
directoryRepositorySnapshotByDirectoryId: options.directoryRepositorySnapshotByDirectoryId,
|
|
159
|
+
directories: input.snapshot.directories,
|
|
160
|
+
conversations: input.snapshot.conversations,
|
|
161
|
+
orderedIds: input.snapshot.orderedConversationIds,
|
|
162
|
+
activeProjectId: options.workspace.activeDirectoryId,
|
|
163
|
+
activeRepositoryId: options.workspace.activeRepositorySelectionId,
|
|
164
|
+
activeConversationId: input.snapshot.activeConversationId,
|
|
165
|
+
projectSelectionEnabled: options.workspace.leftNavSelection.kind === 'project',
|
|
166
|
+
repositorySelectionEnabled: options.workspace.leftNavSelection.kind === 'repository',
|
|
167
|
+
homeSelectionEnabled: options.workspace.leftNavSelection.kind === 'home',
|
|
168
|
+
nimSelectionEnabled: options.workspace.leftNavSelection.kind === 'nim',
|
|
169
|
+
tasksSelectionEnabled: options.workspace.leftNavSelection.kind === 'tasks',
|
|
170
|
+
showTasksEntry: options.showTasksEntry ?? true,
|
|
171
|
+
showGitHubIntegration: options.showGitHubIntegration ?? false,
|
|
172
|
+
githubReviewByDirectoryId: options.githubReviewByDirectoryId ?? new Map(),
|
|
173
|
+
githubSelectionEnabled: options.workspace.leftNavSelection.kind === 'github',
|
|
174
|
+
activeGitHubProjectId:
|
|
175
|
+
options.workspace.leftNavSelection.kind === 'github'
|
|
176
|
+
? options.workspace.leftNavSelection.directoryId
|
|
177
|
+
: null,
|
|
178
|
+
repositoriesCollapsed: options.workspace.repositoriesCollapsed,
|
|
179
|
+
collapsedRepositoryGroupIds: options.repositoryManager.readonlyCollapsedRepositoryGroupIds(),
|
|
180
|
+
gitSummaryByDirectoryId: options.gitSummaryByDirectoryId,
|
|
181
|
+
processUsageBySessionId: input.snapshot.processUsageBySessionId,
|
|
182
|
+
loadingGitSummary: options.loadingGitSummary,
|
|
183
|
+
...(options.visibleGitHubDirectoryIds !== undefined
|
|
184
|
+
? {
|
|
185
|
+
visibleGitHubDirectoryIds: options.visibleGitHubDirectoryIds,
|
|
186
|
+
}
|
|
187
|
+
: {}),
|
|
188
|
+
...(options.expandedGitHubDirectoryIds !== undefined
|
|
189
|
+
? {
|
|
190
|
+
expandedGitHubDirectoryIds: options.expandedGitHubDirectoryIds,
|
|
191
|
+
}
|
|
192
|
+
: {}),
|
|
193
|
+
};
|
|
194
|
+
return options.leftRailPane.render(renderInput);
|
|
159
195
|
}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
|
+
import { startPtySession, type PtyExit } from '../pty/pty_host.ts';
|
|
5
|
+
import type { NimModelRef } from '../../packages/nim-core/src/index.ts';
|
|
6
|
+
import type { RuntimeNimViewModel } from './runtime-nim-session.ts';
|
|
7
|
+
|
|
8
|
+
type NimSessionStatus = RuntimeNimViewModel['status'];
|
|
9
|
+
|
|
10
|
+
interface RuntimeNimCliSessionOptions {
|
|
11
|
+
readonly invocationDirectory: string;
|
|
12
|
+
readonly tenantId: string;
|
|
13
|
+
readonly userId: string;
|
|
14
|
+
readonly markDirty: () => void;
|
|
15
|
+
readonly sessionName: string | null;
|
|
16
|
+
readonly model: NimModelRef;
|
|
17
|
+
readonly useMock: boolean;
|
|
18
|
+
readonly baseUrl?: string;
|
|
19
|
+
readonly maxTranscriptLines?: number;
|
|
20
|
+
readonly harnessScriptPath?: string;
|
|
21
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
22
|
+
readonly startPtySession?: typeof startPtySession;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const PROJECT_ROOT = resolve(MODULE_DIR, '../..');
|
|
27
|
+
const DEFAULT_HARNESS_SCRIPT_PATH = resolve(PROJECT_ROOT, 'scripts/harness.ts');
|
|
28
|
+
const DEFAULT_MAX_TRANSCRIPT_LINES = 200;
|
|
29
|
+
const DEFAULT_COLS = 100;
|
|
30
|
+
const DEFAULT_ROWS = 30;
|
|
31
|
+
|
|
32
|
+
function stripAnsiSequences(value: string): string {
|
|
33
|
+
const ESC = String.fromCharCode(27);
|
|
34
|
+
const BEL = String.fromCharCode(7);
|
|
35
|
+
const oscPattern = new RegExp(`${ESC}\\][^${BEL}]*${BEL}`, 'gu');
|
|
36
|
+
const csiPattern = new RegExp(`${ESC}\\[[0-?]*[ -/]*[@-~]`, 'gu');
|
|
37
|
+
const escPattern = new RegExp(`${ESC}[@-_]`, 'gu');
|
|
38
|
+
return value.replace(oscPattern, '').replace(csiPattern, '').replace(escPattern, '');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isPrintableCharacter(char: string): boolean {
|
|
42
|
+
return char.length === 1 && char >= ' ' && char !== '\u007f';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function providerIdFromModel(model: NimModelRef): string {
|
|
46
|
+
const slash = model.indexOf('/');
|
|
47
|
+
if (slash <= 0) {
|
|
48
|
+
return 'mock';
|
|
49
|
+
}
|
|
50
|
+
return model.slice(0, slash);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface QueueTurnResultLine {
|
|
54
|
+
readonly queued: boolean;
|
|
55
|
+
readonly position?: number;
|
|
56
|
+
readonly reason?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseQueueTurnResultLine(line: string): QueueTurnResultLine | null {
|
|
60
|
+
const trimmed = line.trim();
|
|
61
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
let parsed: unknown;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(trimmed) as unknown;
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const record = parsed as Record<string, unknown>;
|
|
74
|
+
if (typeof record['queued'] !== 'boolean') {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const positionValue = record['position'];
|
|
78
|
+
const reasonValue = record['reason'];
|
|
79
|
+
const position =
|
|
80
|
+
typeof positionValue === 'number' && Number.isInteger(positionValue) && positionValue >= 0
|
|
81
|
+
? positionValue
|
|
82
|
+
: undefined;
|
|
83
|
+
const reason =
|
|
84
|
+
typeof reasonValue === 'string' && reasonValue.trim().length > 0 ? reasonValue : undefined;
|
|
85
|
+
return {
|
|
86
|
+
queued: record['queued'],
|
|
87
|
+
...(position === undefined ? {} : { position }),
|
|
88
|
+
...(reason === undefined ? {} : { reason }),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseQueueDepthLine(line: string): number | null {
|
|
93
|
+
const match = /^queue depth (\d+)$/u.exec(line.trim());
|
|
94
|
+
if (match === null) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const parsed = Number.parseInt(match[1] ?? '', 10);
|
|
98
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizePtyExit(value: unknown): PtyExit | null {
|
|
105
|
+
if (typeof value !== 'object' || value === null) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const candidate = value as { code?: unknown; signal?: unknown };
|
|
109
|
+
if (
|
|
110
|
+
(typeof candidate.code !== 'number' && candidate.code !== null) ||
|
|
111
|
+
(typeof candidate.signal !== 'string' && candidate.signal !== null)
|
|
112
|
+
) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
code: candidate.code,
|
|
117
|
+
signal: typeof candidate.signal === 'string' ? (candidate.signal as NodeJS.Signals) : null,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export class RuntimeNimCliSession {
|
|
122
|
+
private readonly startPtySessionImpl: typeof startPtySession;
|
|
123
|
+
private readonly harnessScriptPath: string;
|
|
124
|
+
private readonly env: NodeJS.ProcessEnv;
|
|
125
|
+
private readonly maxTranscriptLines: number;
|
|
126
|
+
|
|
127
|
+
private started = false;
|
|
128
|
+
private disposed = false;
|
|
129
|
+
private session: ReturnType<typeof startPtySession> | null = null;
|
|
130
|
+
private sessionExitPromise: Promise<PtyExit> | null = null;
|
|
131
|
+
private pendingOutput = '';
|
|
132
|
+
|
|
133
|
+
private sessionId: string | null = null;
|
|
134
|
+
private status: NimSessionStatus = 'idle';
|
|
135
|
+
private uiMode: RuntimeNimViewModel['uiMode'] = 'debug';
|
|
136
|
+
private composerText = '';
|
|
137
|
+
private queuedCount = 0;
|
|
138
|
+
private transcriptLines: string[] = [];
|
|
139
|
+
private activeRunId: string | null = null;
|
|
140
|
+
private pendingDirectRunStarts = 0;
|
|
141
|
+
|
|
142
|
+
constructor(private readonly options: RuntimeNimCliSessionOptions) {
|
|
143
|
+
this.startPtySessionImpl = options.startPtySession ?? startPtySession;
|
|
144
|
+
this.harnessScriptPath = options.harnessScriptPath ?? DEFAULT_HARNESS_SCRIPT_PATH;
|
|
145
|
+
this.env = options.env ?? process.env;
|
|
146
|
+
this.maxTranscriptLines = options.maxTranscriptLines ?? DEFAULT_MAX_TRANSCRIPT_LINES;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public async start(): Promise<void> {
|
|
150
|
+
if (this.started || this.disposed) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.started = true;
|
|
154
|
+
const commandArgs: string[] = [this.harnessScriptPath];
|
|
155
|
+
if (this.options.sessionName !== null) {
|
|
156
|
+
commandArgs.push('--session', this.options.sessionName);
|
|
157
|
+
}
|
|
158
|
+
commandArgs.push(
|
|
159
|
+
'nim',
|
|
160
|
+
'--tenant-id',
|
|
161
|
+
this.options.tenantId,
|
|
162
|
+
'--user-id',
|
|
163
|
+
this.options.userId,
|
|
164
|
+
'--model',
|
|
165
|
+
this.options.model,
|
|
166
|
+
'--ui-mode',
|
|
167
|
+
'debug',
|
|
168
|
+
);
|
|
169
|
+
if (this.options.useMock) {
|
|
170
|
+
commandArgs.push('--mock');
|
|
171
|
+
} else {
|
|
172
|
+
commandArgs.push('--live-anthropic');
|
|
173
|
+
if (typeof this.options.baseUrl === 'string' && this.options.baseUrl.trim().length > 0) {
|
|
174
|
+
commandArgs.push('--base-url', this.options.baseUrl.trim());
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const session = this.startPtySessionImpl({
|
|
178
|
+
command: process.execPath,
|
|
179
|
+
commandArgs,
|
|
180
|
+
cwd: this.options.invocationDirectory,
|
|
181
|
+
env: {
|
|
182
|
+
...this.env,
|
|
183
|
+
HARNESS_INVOKE_CWD: this.options.invocationDirectory,
|
|
184
|
+
},
|
|
185
|
+
initialCols: DEFAULT_COLS,
|
|
186
|
+
initialRows: DEFAULT_ROWS,
|
|
187
|
+
});
|
|
188
|
+
this.session = session;
|
|
189
|
+
this.sessionExitPromise = new Promise((resolveExit) => {
|
|
190
|
+
session.once('exit', (value: unknown) => {
|
|
191
|
+
const normalized = normalizePtyExit(value);
|
|
192
|
+
resolveExit(
|
|
193
|
+
normalized ?? {
|
|
194
|
+
code: 1,
|
|
195
|
+
signal: null,
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
session.on('data', (chunk: Buffer) => {
|
|
201
|
+
this.applyOutputChunk(chunk.toString('utf8'));
|
|
202
|
+
});
|
|
203
|
+
this.options.markDirty();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
public async dispose(): Promise<void> {
|
|
207
|
+
this.disposed = true;
|
|
208
|
+
const session = this.session;
|
|
209
|
+
const exitPromise = this.sessionExitPromise;
|
|
210
|
+
this.session = null;
|
|
211
|
+
this.sessionExitPromise = null;
|
|
212
|
+
if (session === null) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
session.write('/exit\n');
|
|
217
|
+
} catch {
|
|
218
|
+
// Best-effort shutdown only.
|
|
219
|
+
}
|
|
220
|
+
if (exitPromise !== null) {
|
|
221
|
+
const exited = await Promise.race([
|
|
222
|
+
exitPromise.then(() => true),
|
|
223
|
+
delay(500).then(() => false),
|
|
224
|
+
]);
|
|
225
|
+
if (exited) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
session.close();
|
|
231
|
+
} catch {
|
|
232
|
+
// Best-effort shutdown only.
|
|
233
|
+
}
|
|
234
|
+
if (exitPromise !== null) {
|
|
235
|
+
await Promise.race([exitPromise, delay(500)]);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
public snapshot(): RuntimeNimViewModel {
|
|
240
|
+
return {
|
|
241
|
+
sessionId: this.sessionId,
|
|
242
|
+
status: this.status,
|
|
243
|
+
uiMode: this.uiMode,
|
|
244
|
+
composerText: this.composerText,
|
|
245
|
+
queuedCount: this.queuedCount,
|
|
246
|
+
activeRunId: this.activeRunId,
|
|
247
|
+
transcriptLines: this.transcriptLines,
|
|
248
|
+
assistantDraftText: '',
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
public handleInputChunk(text: string): void {
|
|
253
|
+
if (text.length === 0 || this.disposed) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
let skipLf = false;
|
|
257
|
+
for (const char of text) {
|
|
258
|
+
if (skipLf && char === '\n') {
|
|
259
|
+
skipLf = false;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
skipLf = false;
|
|
263
|
+
if (char === '\r' || char === '\n') {
|
|
264
|
+
this.flushComposerAsSend();
|
|
265
|
+
if (char === '\r') {
|
|
266
|
+
skipLf = true;
|
|
267
|
+
}
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (char === '\t') {
|
|
271
|
+
this.flushComposerAsQueue();
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (char === '\u007f' || char === '\b') {
|
|
275
|
+
this.composerText = this.composerText.slice(0, -1);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (!isPrintableCharacter(char)) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
this.composerText += char;
|
|
282
|
+
}
|
|
283
|
+
this.options.markDirty();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private flushComposerAsSend(): void {
|
|
287
|
+
const message = this.composerText.trim();
|
|
288
|
+
this.composerText = '';
|
|
289
|
+
if (message.length === 0 || this.session === null) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.pendingDirectRunStarts += 1;
|
|
293
|
+
this.session.write(`${message}\n`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private flushComposerAsQueue(): void {
|
|
297
|
+
const message = this.composerText.trim();
|
|
298
|
+
this.composerText = '';
|
|
299
|
+
if (message.length === 0 || this.session === null) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
const queueCommand =
|
|
303
|
+
message.startsWith('/queue ') || message === '/queue' ? message : `/queue ${message}`;
|
|
304
|
+
this.session.write(`${queueCommand}\n`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
public handleEscape(): void {
|
|
308
|
+
if (this.disposed) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (this.session !== null) {
|
|
312
|
+
this.session.write('/abort\n');
|
|
313
|
+
}
|
|
314
|
+
this.status = 'idle';
|
|
315
|
+
this.options.markDirty();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
public resize(cols: number, rows: number): void {
|
|
319
|
+
if (this.disposed || this.session === null) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
this.session.resize(Math.max(20, Math.floor(cols)), Math.max(6, Math.floor(rows)));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private applyOutputChunk(text: string): void {
|
|
326
|
+
const stripped = stripAnsiSequences(text).replace(/\r/gu, '');
|
|
327
|
+
if (stripped.length === 0) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
this.pendingOutput += stripped;
|
|
331
|
+
while (true) {
|
|
332
|
+
const newlineIndex = this.pendingOutput.indexOf('\n');
|
|
333
|
+
if (newlineIndex < 0) {
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
const rawLine = this.pendingOutput.slice(0, newlineIndex);
|
|
337
|
+
this.pendingOutput = this.pendingOutput.slice(newlineIndex + 1);
|
|
338
|
+
this.applyOutputLine(rawLine.trim());
|
|
339
|
+
}
|
|
340
|
+
this.options.markDirty();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private applyOutputLine(line: string): void {
|
|
344
|
+
if (line.length === 0) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const readyMatch = /^nim tui ready session=([^\s]+)/u.exec(line);
|
|
348
|
+
if (readyMatch !== null) {
|
|
349
|
+
this.sessionId = readyMatch[1] ?? this.sessionId;
|
|
350
|
+
const providerId = providerIdFromModel(this.options.model);
|
|
351
|
+
this.pushTranscriptLine(
|
|
352
|
+
`[notice] nim subprocess ready model=${this.options.model} provider=${providerId}`,
|
|
353
|
+
);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (line.startsWith('run started ')) {
|
|
357
|
+
const runId = line.slice('run started '.length).trim();
|
|
358
|
+
this.activeRunId = runId.length > 0 ? runId : null;
|
|
359
|
+
this.status = 'thinking';
|
|
360
|
+
if (this.pendingDirectRunStarts > 0) {
|
|
361
|
+
this.pendingDirectRunStarts -= 1;
|
|
362
|
+
} else if (this.queuedCount > 0) {
|
|
363
|
+
this.queuedCount = Math.max(0, this.queuedCount - 1);
|
|
364
|
+
}
|
|
365
|
+
this.pushTranscriptLine(line);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (line.startsWith('run completed ')) {
|
|
369
|
+
this.activeRunId = null;
|
|
370
|
+
this.status = 'idle';
|
|
371
|
+
this.pushTranscriptLine(line);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (line.startsWith('ui mode set to ')) {
|
|
375
|
+
const mode = line.slice('ui mode set to '.length).trim();
|
|
376
|
+
if (mode === 'debug' || mode === 'user') {
|
|
377
|
+
this.uiMode = mode;
|
|
378
|
+
}
|
|
379
|
+
this.pushTranscriptLine(`[notice] ${line}`);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (line.startsWith('new session ') || line.startsWith('resumed session ')) {
|
|
383
|
+
const sessionId = line.split(' ').at(-1) ?? '';
|
|
384
|
+
if (sessionId.length > 0) {
|
|
385
|
+
this.sessionId = sessionId;
|
|
386
|
+
}
|
|
387
|
+
this.queuedCount = 0;
|
|
388
|
+
this.pendingDirectRunStarts = 0;
|
|
389
|
+
this.pushTranscriptLine(`[notice] ${line}`);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const queueDepth = parseQueueDepthLine(line);
|
|
393
|
+
if (queueDepth !== null) {
|
|
394
|
+
this.queuedCount = queueDepth;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (line === 'frame:') {
|
|
398
|
+
this.status = 'responding';
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (line.startsWith('[error]')) {
|
|
402
|
+
this.status = 'idle';
|
|
403
|
+
this.activeRunId = null;
|
|
404
|
+
this.pendingDirectRunStarts = 0;
|
|
405
|
+
}
|
|
406
|
+
const queuedTurnResult = parseQueueTurnResultLine(line);
|
|
407
|
+
if (queuedTurnResult !== null) {
|
|
408
|
+
if (queuedTurnResult.queued) {
|
|
409
|
+
const nextQueuedCount =
|
|
410
|
+
queuedTurnResult.position === undefined
|
|
411
|
+
? this.queuedCount + 1
|
|
412
|
+
: queuedTurnResult.position + 1;
|
|
413
|
+
this.queuedCount = Math.max(this.queuedCount, nextQueuedCount);
|
|
414
|
+
this.pushTranscriptLine(
|
|
415
|
+
`[notice] queued turn position=${String(
|
|
416
|
+
queuedTurnResult.position === undefined
|
|
417
|
+
? this.queuedCount - 1
|
|
418
|
+
: queuedTurnResult.position,
|
|
419
|
+
)}`,
|
|
420
|
+
);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
this.pushTranscriptLine(
|
|
424
|
+
`[notice] queue rejected${queuedTurnResult.reason === undefined ? '' : ` reason=${queuedTurnResult.reason}`}`,
|
|
425
|
+
);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
this.pushTranscriptLine(line);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
private pushTranscriptLine(text: string): void {
|
|
432
|
+
this.transcriptLines.push(text);
|
|
433
|
+
const overflow = this.transcriptLines.length - this.maxTranscriptLines;
|
|
434
|
+
if (overflow > 0) {
|
|
435
|
+
this.transcriptLines.splice(0, overflow);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|