@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.
- package/README.md +31 -39
- 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/packages/harness-ui/src/modal-manager.ts +222 -0
- 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 -3872
- package/scripts/control-plane-daemon.ts +11 -0
- 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 -3019
- package/scripts/nim-tui-smoke.ts +748 -0
- package/src/cli/auth/runtime.ts +948 -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 +348 -8
- package/src/config/harness.config.template.jsonc +33 -0
- package/src/control-plane/agent-realtime-api.ts +82 -427
- 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-command.ts +376 -69
- package/src/control-plane/stream-server-session-runtime.ts +3 -2
- package/src/control-plane/stream-server.ts +864 -70
- 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/workspace.ts +68 -5
- 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 +13 -131
- 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-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 +77 -12
- package/src/mux/live-mux/modal-overlays.ts +168 -34
- package/src/mux/live-mux/modal-pointer.ts +3 -7
- package/src/mux/live-mux/modal-prompt-handlers.ts +23 -2
- 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 +10 -101
- 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 +73 -46
- package/src/services/runtime-conversation-starter.ts +53 -45
- 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 -72
- 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 +360 -56
- package/src/store/event-store.ts +366 -8
- 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 -85
- 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 -195
- package/src/services/runtime-main-pane-input.ts +0 -230
- package/src/services/runtime-modal-input.ts +0 -137
- package/src/services/runtime-navigation-input.ts +0 -197
- package/src/services/runtime-rail-input.ts +0 -279
- 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 -269
- 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/modals/manager.ts +0 -218
- package/src/ui/repository-fold-input.ts +0 -91
- package/src/ui/surface.ts +0 -224
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
} from '../store/control-plane-store.ts';
|
|
21
21
|
import type { TerminalBufferTail, TerminalSnapshotFrame } from '../terminal/snapshot-oracle.ts';
|
|
22
22
|
import type { PtyExit } from '../pty/pty_host.ts';
|
|
23
|
+
import type { StartSessionRuntimeInput } from './stream-session-runtime-types.ts';
|
|
23
24
|
|
|
24
25
|
const DEFAULT_TENANT_ID = 'tenant-local';
|
|
25
26
|
const DEFAULT_USER_ID = 'user-local';
|
|
@@ -54,21 +55,6 @@ interface SessionControllerState extends StreamSessionController {
|
|
|
54
55
|
connectionId: string;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
interface StartSessionRuntimeInput {
|
|
58
|
-
readonly sessionId: string;
|
|
59
|
-
readonly args: readonly string[];
|
|
60
|
-
readonly initialCols: number;
|
|
61
|
-
readonly initialRows: number;
|
|
62
|
-
readonly env?: Record<string, string>;
|
|
63
|
-
readonly cwd?: string;
|
|
64
|
-
readonly tenantId?: string;
|
|
65
|
-
readonly userId?: string;
|
|
66
|
-
readonly workspaceId?: string;
|
|
67
|
-
readonly worktreeId?: string;
|
|
68
|
-
readonly terminalForegroundHex?: string;
|
|
69
|
-
readonly terminalBackgroundHex?: string;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
58
|
interface LiveSessionLike {
|
|
73
59
|
attach(
|
|
74
60
|
handlers: {
|
|
@@ -206,9 +192,8 @@ interface ExecuteCommandContext {
|
|
|
206
192
|
workspaceId: string;
|
|
207
193
|
repositoryId?: string;
|
|
208
194
|
projectId?: string;
|
|
209
|
-
title
|
|
210
|
-
|
|
211
|
-
linear?: Record<string, unknown>;
|
|
195
|
+
title?: string | null;
|
|
196
|
+
body: string;
|
|
212
197
|
}): ControlPlaneTaskRecord;
|
|
213
198
|
getTask(taskId: string): ControlPlaneTaskRecord | null;
|
|
214
199
|
listTasks(query: {
|
|
@@ -224,11 +209,10 @@ interface ExecuteCommandContext {
|
|
|
224
209
|
updateTask(
|
|
225
210
|
taskId: string,
|
|
226
211
|
input: {
|
|
227
|
-
title?: string;
|
|
228
|
-
|
|
212
|
+
title?: string | null;
|
|
213
|
+
body?: string;
|
|
229
214
|
repositoryId?: string | null;
|
|
230
215
|
projectId?: string | null;
|
|
231
|
-
linear?: Record<string, unknown> | null;
|
|
232
216
|
},
|
|
233
217
|
): ControlPlaneTaskRecord | null;
|
|
234
218
|
deleteTask(taskId: string): void;
|
|
@@ -391,10 +375,46 @@ interface ExecuteCommandContext {
|
|
|
391
375
|
baseBranch: string;
|
|
392
376
|
state: 'open' | 'closed';
|
|
393
377
|
isDraft: boolean;
|
|
378
|
+
mergedAt: string | null;
|
|
394
379
|
updatedAt: string;
|
|
395
380
|
createdAt: string;
|
|
396
381
|
closedAt: string | null;
|
|
397
382
|
} | null>;
|
|
383
|
+
findPullRequestForBranch(input: { owner: string; repo: string; headBranch: string }): Promise<{
|
|
384
|
+
number: number;
|
|
385
|
+
title: string;
|
|
386
|
+
url: string;
|
|
387
|
+
authorLogin: string | null;
|
|
388
|
+
headBranch: string;
|
|
389
|
+
headSha: string;
|
|
390
|
+
baseBranch: string;
|
|
391
|
+
state: 'open' | 'closed';
|
|
392
|
+
isDraft: boolean;
|
|
393
|
+
mergedAt: string | null;
|
|
394
|
+
updatedAt: string;
|
|
395
|
+
createdAt: string;
|
|
396
|
+
closedAt: string | null;
|
|
397
|
+
} | null>;
|
|
398
|
+
listPullRequestReviewThreads(input: {
|
|
399
|
+
owner: string;
|
|
400
|
+
repo: string;
|
|
401
|
+
pullNumber: number;
|
|
402
|
+
}): Promise<
|
|
403
|
+
readonly {
|
|
404
|
+
threadId: string;
|
|
405
|
+
isResolved: boolean;
|
|
406
|
+
isOutdated: boolean;
|
|
407
|
+
resolvedByLogin: string | null;
|
|
408
|
+
comments: readonly {
|
|
409
|
+
commentId: string;
|
|
410
|
+
authorLogin: string | null;
|
|
411
|
+
body: string;
|
|
412
|
+
url: string | null;
|
|
413
|
+
createdAt: string;
|
|
414
|
+
updatedAt: string;
|
|
415
|
+
}[];
|
|
416
|
+
}[]
|
|
417
|
+
>;
|
|
398
418
|
createPullRequest(input: {
|
|
399
419
|
owner: string;
|
|
400
420
|
repo: string;
|
|
@@ -413,11 +433,127 @@ interface ExecuteCommandContext {
|
|
|
413
433
|
baseBranch: string;
|
|
414
434
|
state: 'open' | 'closed';
|
|
415
435
|
isDraft: boolean;
|
|
436
|
+
mergedAt: string | null;
|
|
416
437
|
updatedAt: string;
|
|
417
438
|
createdAt: string;
|
|
418
439
|
closedAt: string | null;
|
|
419
440
|
}>;
|
|
420
441
|
};
|
|
442
|
+
getGitHubProjectReviewCache(input: { repositoryId: string; branchName: string }): {
|
|
443
|
+
repositoryId: string;
|
|
444
|
+
branchName: string;
|
|
445
|
+
pr: {
|
|
446
|
+
number: number;
|
|
447
|
+
title: string;
|
|
448
|
+
url: string;
|
|
449
|
+
authorLogin: string | null;
|
|
450
|
+
headBranch: string;
|
|
451
|
+
headSha: string;
|
|
452
|
+
baseBranch: string;
|
|
453
|
+
state: 'draft' | 'open' | 'merged' | 'closed';
|
|
454
|
+
isDraft: boolean;
|
|
455
|
+
mergedAt: string | null;
|
|
456
|
+
closedAt: string | null;
|
|
457
|
+
updatedAt: string;
|
|
458
|
+
createdAt: string;
|
|
459
|
+
} | null;
|
|
460
|
+
openThreads: readonly {
|
|
461
|
+
threadId: string;
|
|
462
|
+
isResolved: boolean;
|
|
463
|
+
isOutdated: boolean;
|
|
464
|
+
resolvedByLogin: string | null;
|
|
465
|
+
comments: readonly {
|
|
466
|
+
commentId: string;
|
|
467
|
+
authorLogin: string | null;
|
|
468
|
+
body: string;
|
|
469
|
+
url: string | null;
|
|
470
|
+
createdAt: string;
|
|
471
|
+
updatedAt: string;
|
|
472
|
+
}[];
|
|
473
|
+
}[];
|
|
474
|
+
resolvedThreads: readonly {
|
|
475
|
+
threadId: string;
|
|
476
|
+
isResolved: boolean;
|
|
477
|
+
isOutdated: boolean;
|
|
478
|
+
resolvedByLogin: string | null;
|
|
479
|
+
comments: readonly {
|
|
480
|
+
commentId: string;
|
|
481
|
+
authorLogin: string | null;
|
|
482
|
+
body: string;
|
|
483
|
+
url: string | null;
|
|
484
|
+
createdAt: string;
|
|
485
|
+
updatedAt: string;
|
|
486
|
+
}[];
|
|
487
|
+
}[];
|
|
488
|
+
fetchedAtMs: number;
|
|
489
|
+
} | null;
|
|
490
|
+
refreshGitHubProjectReviewCache(input: {
|
|
491
|
+
repositoryId: string;
|
|
492
|
+
owner: string;
|
|
493
|
+
repo: string;
|
|
494
|
+
branchName: string;
|
|
495
|
+
forceRefresh?: boolean;
|
|
496
|
+
}): Promise<{
|
|
497
|
+
repositoryId: string;
|
|
498
|
+
branchName: string;
|
|
499
|
+
pr: {
|
|
500
|
+
number: number;
|
|
501
|
+
title: string;
|
|
502
|
+
url: string;
|
|
503
|
+
authorLogin: string | null;
|
|
504
|
+
headBranch: string;
|
|
505
|
+
headSha: string;
|
|
506
|
+
baseBranch: string;
|
|
507
|
+
state: 'draft' | 'open' | 'merged' | 'closed';
|
|
508
|
+
isDraft: boolean;
|
|
509
|
+
mergedAt: string | null;
|
|
510
|
+
closedAt: string | null;
|
|
511
|
+
updatedAt: string;
|
|
512
|
+
createdAt: string;
|
|
513
|
+
} | null;
|
|
514
|
+
openThreads: readonly {
|
|
515
|
+
threadId: string;
|
|
516
|
+
isResolved: boolean;
|
|
517
|
+
isOutdated: boolean;
|
|
518
|
+
resolvedByLogin: string | null;
|
|
519
|
+
comments: readonly {
|
|
520
|
+
commentId: string;
|
|
521
|
+
authorLogin: string | null;
|
|
522
|
+
body: string;
|
|
523
|
+
url: string | null;
|
|
524
|
+
createdAt: string;
|
|
525
|
+
updatedAt: string;
|
|
526
|
+
}[];
|
|
527
|
+
}[];
|
|
528
|
+
resolvedThreads: readonly {
|
|
529
|
+
threadId: string;
|
|
530
|
+
isResolved: boolean;
|
|
531
|
+
isOutdated: boolean;
|
|
532
|
+
resolvedByLogin: string | null;
|
|
533
|
+
comments: readonly {
|
|
534
|
+
commentId: string;
|
|
535
|
+
authorLogin: string | null;
|
|
536
|
+
body: string;
|
|
537
|
+
url: string | null;
|
|
538
|
+
createdAt: string;
|
|
539
|
+
updatedAt: string;
|
|
540
|
+
}[];
|
|
541
|
+
}[];
|
|
542
|
+
fetchedAtMs: number;
|
|
543
|
+
}>;
|
|
544
|
+
readonly linear: {
|
|
545
|
+
enabled: boolean;
|
|
546
|
+
};
|
|
547
|
+
readonly linearApi: {
|
|
548
|
+
issueByIdentifier(input: { identifier: string }): Promise<{
|
|
549
|
+
identifier: string;
|
|
550
|
+
title: string;
|
|
551
|
+
description: string | null;
|
|
552
|
+
url: string | null;
|
|
553
|
+
stateName: string | null;
|
|
554
|
+
teamKey: string | null;
|
|
555
|
+
} | null>;
|
|
556
|
+
};
|
|
421
557
|
readonly streamCursor: number;
|
|
422
558
|
refreshConversationTitle(conversationId: string): Promise<{
|
|
423
559
|
conversation: ControlPlaneConversationRecord;
|
|
@@ -564,6 +700,40 @@ function parseGitHubOwnerRepo(remoteUrl: string): { owner: string; repo: string
|
|
|
564
700
|
return null;
|
|
565
701
|
}
|
|
566
702
|
|
|
703
|
+
function parseLinearIssueIdentifierFromUrl(issueUrl: string): {
|
|
704
|
+
identifier: string;
|
|
705
|
+
} | null {
|
|
706
|
+
const trimmed = issueUrl.trim();
|
|
707
|
+
if (trimmed.length === 0) {
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
let parsedUrl: URL;
|
|
711
|
+
try {
|
|
712
|
+
parsedUrl = new URL(trimmed);
|
|
713
|
+
} catch {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
if (parsedUrl.protocol !== 'https:') {
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
const hostname = parsedUrl.hostname.toLowerCase();
|
|
720
|
+
if (!(hostname === 'linear.app' || hostname.endsWith('.linear.app'))) {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
723
|
+
const segments = parsedUrl.pathname.split('/').filter((segment) => segment.length > 0);
|
|
724
|
+
const issueSegmentIndex = segments.findIndex((segment) => segment.toLowerCase() === 'issue');
|
|
725
|
+
if (issueSegmentIndex < 0 || issueSegmentIndex + 1 >= segments.length) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const identifier = segments[issueSegmentIndex + 1]?.trim().toUpperCase() ?? '';
|
|
729
|
+
if (!/^[A-Z][A-Z0-9]*-\d+$/u.test(identifier)) {
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
return {
|
|
733
|
+
identifier,
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
567
737
|
function resolveTrackedBranch(input: {
|
|
568
738
|
strategy: 'pinned-then-current' | 'current-only' | 'pinned-only';
|
|
569
739
|
pinnedBranch: string | null;
|
|
@@ -647,10 +817,29 @@ function ciRollupFromJobs(
|
|
|
647
817
|
return 'neutral';
|
|
648
818
|
}
|
|
649
819
|
|
|
820
|
+
function githubLifecycleStateFromPullRequest(input: {
|
|
821
|
+
state: 'open' | 'closed';
|
|
822
|
+
isDraft: boolean;
|
|
823
|
+
mergedAt: string | null;
|
|
824
|
+
}): 'draft' | 'open' | 'merged' | 'closed' {
|
|
825
|
+
if (input.state === 'open' && input.isDraft) {
|
|
826
|
+
return 'draft';
|
|
827
|
+
}
|
|
828
|
+
if (input.state === 'open') {
|
|
829
|
+
return 'open';
|
|
830
|
+
}
|
|
831
|
+
if (input.mergedAt !== null) {
|
|
832
|
+
return 'merged';
|
|
833
|
+
}
|
|
834
|
+
return 'closed';
|
|
835
|
+
}
|
|
836
|
+
|
|
650
837
|
export const streamServerCommandTestInternals = {
|
|
651
838
|
parseGitHubOwnerRepo,
|
|
839
|
+
parseLinearIssueIdentifierFromUrl,
|
|
652
840
|
resolveTrackedBranch,
|
|
653
841
|
ciRollupFromJobs,
|
|
842
|
+
githubLifecycleStateFromPullRequest,
|
|
654
843
|
};
|
|
655
844
|
|
|
656
845
|
export async function executeStreamServerCommand(
|
|
@@ -1161,6 +1350,99 @@ export async function executeStreamServerCommand(
|
|
|
1161
1350
|
};
|
|
1162
1351
|
}
|
|
1163
1352
|
|
|
1353
|
+
if (command.type === 'github.project-review') {
|
|
1354
|
+
const resolved = resolveProjectGitHubContext(command.directoryId);
|
|
1355
|
+
const storedPr =
|
|
1356
|
+
resolved.repository === null || resolved.trackedBranch === null
|
|
1357
|
+
? null
|
|
1358
|
+
: (ctx.stateStore.listGitHubPullRequests({
|
|
1359
|
+
repositoryId: resolved.repository.repositoryId,
|
|
1360
|
+
headBranch: resolved.trackedBranch,
|
|
1361
|
+
limit: 1,
|
|
1362
|
+
})[0] ?? null);
|
|
1363
|
+
const storedPrView =
|
|
1364
|
+
storedPr === null
|
|
1365
|
+
? null
|
|
1366
|
+
: {
|
|
1367
|
+
prRecordId: storedPr.prRecordId,
|
|
1368
|
+
number: storedPr.number,
|
|
1369
|
+
title: storedPr.title,
|
|
1370
|
+
url: storedPr.url,
|
|
1371
|
+
authorLogin: storedPr.authorLogin,
|
|
1372
|
+
headBranch: storedPr.headBranch,
|
|
1373
|
+
headSha: storedPr.headSha,
|
|
1374
|
+
baseBranch: storedPr.baseBranch,
|
|
1375
|
+
state: githubLifecycleStateFromPullRequest({
|
|
1376
|
+
state: storedPr.state,
|
|
1377
|
+
isDraft: storedPr.isDraft,
|
|
1378
|
+
mergedAt: null,
|
|
1379
|
+
}),
|
|
1380
|
+
isDraft: storedPr.isDraft,
|
|
1381
|
+
mergedAt: null,
|
|
1382
|
+
closedAt: storedPr.closedAt,
|
|
1383
|
+
ciRollup: storedPr.ciRollup,
|
|
1384
|
+
updatedAt: storedPr.updatedAt,
|
|
1385
|
+
createdAt: storedPr.createdAt,
|
|
1386
|
+
observedAt: storedPr.observedAt,
|
|
1387
|
+
};
|
|
1388
|
+
if (
|
|
1389
|
+
resolved.repository === null ||
|
|
1390
|
+
resolved.trackedBranch === null ||
|
|
1391
|
+
resolved.ownerRepo === null ||
|
|
1392
|
+
!ctx.github.enabled
|
|
1393
|
+
) {
|
|
1394
|
+
return {
|
|
1395
|
+
directoryId: resolved.directory.directoryId,
|
|
1396
|
+
repositoryId: resolved.repository?.repositoryId ?? null,
|
|
1397
|
+
branchName: resolved.trackedBranch,
|
|
1398
|
+
branchSource: resolved.trackedBranchSource,
|
|
1399
|
+
pr: storedPrView,
|
|
1400
|
+
openThreads: [],
|
|
1401
|
+
resolvedThreads: [],
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
const reviewCache =
|
|
1405
|
+
command.forceRefresh === true
|
|
1406
|
+
? await ctx.refreshGitHubProjectReviewCache({
|
|
1407
|
+
repositoryId: resolved.repository.repositoryId,
|
|
1408
|
+
owner: resolved.ownerRepo.owner,
|
|
1409
|
+
repo: resolved.ownerRepo.repo,
|
|
1410
|
+
branchName: resolved.trackedBranch,
|
|
1411
|
+
forceRefresh: true,
|
|
1412
|
+
})
|
|
1413
|
+
: ctx.getGitHubProjectReviewCache({
|
|
1414
|
+
repositoryId: resolved.repository.repositoryId,
|
|
1415
|
+
branchName: resolved.trackedBranch,
|
|
1416
|
+
});
|
|
1417
|
+
if (reviewCache === null) {
|
|
1418
|
+
return {
|
|
1419
|
+
directoryId: resolved.directory.directoryId,
|
|
1420
|
+
repositoryId: resolved.repository.repositoryId,
|
|
1421
|
+
branchName: resolved.trackedBranch,
|
|
1422
|
+
branchSource: resolved.trackedBranchSource,
|
|
1423
|
+
pr: storedPrView,
|
|
1424
|
+
openThreads: [],
|
|
1425
|
+
resolvedThreads: [],
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
const reviewPr =
|
|
1429
|
+
reviewCache.pr === null
|
|
1430
|
+
? storedPrView
|
|
1431
|
+
: {
|
|
1432
|
+
...reviewCache.pr,
|
|
1433
|
+
ciRollup: storedPrView?.ciRollup ?? null,
|
|
1434
|
+
};
|
|
1435
|
+
return {
|
|
1436
|
+
directoryId: resolved.directory.directoryId,
|
|
1437
|
+
repositoryId: resolved.repository.repositoryId,
|
|
1438
|
+
branchName: resolved.trackedBranch,
|
|
1439
|
+
branchSource: resolved.trackedBranchSource,
|
|
1440
|
+
pr: reviewPr,
|
|
1441
|
+
openThreads: reviewCache.openThreads,
|
|
1442
|
+
resolvedThreads: reviewCache.resolvedThreads,
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1164
1446
|
if (command.type === 'github.pr-list') {
|
|
1165
1447
|
const prs = ctx.stateStore.listGitHubPullRequests({
|
|
1166
1448
|
...(command.tenantId === undefined ? {} : { tenantId: command.tenantId }),
|
|
@@ -1301,6 +1583,66 @@ export async function executeStreamServerCommand(
|
|
|
1301
1583
|
};
|
|
1302
1584
|
}
|
|
1303
1585
|
|
|
1586
|
+
if (command.type === 'linear.issue.import') {
|
|
1587
|
+
if (!ctx.linear.enabled) {
|
|
1588
|
+
throw new Error('linear integration is disabled');
|
|
1589
|
+
}
|
|
1590
|
+
const parsedLinearIssue = parseLinearIssueIdentifierFromUrl(command.url);
|
|
1591
|
+
if (parsedLinearIssue === null) {
|
|
1592
|
+
throw new Error('linear issue url required');
|
|
1593
|
+
}
|
|
1594
|
+
const issue = await ctx.linearApi.issueByIdentifier({
|
|
1595
|
+
identifier: parsedLinearIssue.identifier,
|
|
1596
|
+
});
|
|
1597
|
+
if (issue === null) {
|
|
1598
|
+
throw new Error(`linear issue not found: ${parsedLinearIssue.identifier}`);
|
|
1599
|
+
}
|
|
1600
|
+
if (command.repositoryId === undefined && command.projectId === undefined) {
|
|
1601
|
+
throw new Error('linear issue import requires repositoryId or projectId');
|
|
1602
|
+
}
|
|
1603
|
+
const task = ctx.stateStore.createTask({
|
|
1604
|
+
taskId: `task-${randomUUID()}`,
|
|
1605
|
+
tenantId: command.tenantId ?? DEFAULT_TENANT_ID,
|
|
1606
|
+
userId: command.userId ?? DEFAULT_USER_ID,
|
|
1607
|
+
workspaceId: command.workspaceId ?? DEFAULT_WORKSPACE_ID,
|
|
1608
|
+
...(command.repositoryId === undefined ? {} : { repositoryId: command.repositoryId }),
|
|
1609
|
+
...(command.projectId === undefined ? {} : { projectId: command.projectId }),
|
|
1610
|
+
title: `${issue.identifier}: ${issue.title}`.trim(),
|
|
1611
|
+
body: [
|
|
1612
|
+
issue.url ?? command.url.trim(),
|
|
1613
|
+
issue.stateName === null ? null : `state: ${issue.stateName}`,
|
|
1614
|
+
issue.teamKey === null ? null : `team: ${issue.teamKey}`,
|
|
1615
|
+
issue.description,
|
|
1616
|
+
]
|
|
1617
|
+
.filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)
|
|
1618
|
+
.join('\n'),
|
|
1619
|
+
});
|
|
1620
|
+
ctx.publishObservedEvent(
|
|
1621
|
+
{
|
|
1622
|
+
tenantId: task.tenantId,
|
|
1623
|
+
userId: task.userId,
|
|
1624
|
+
workspaceId: task.workspaceId,
|
|
1625
|
+
directoryId: null,
|
|
1626
|
+
conversationId: null,
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
type: 'task-created',
|
|
1630
|
+
task: ctx.taskRecord(task),
|
|
1631
|
+
},
|
|
1632
|
+
);
|
|
1633
|
+
return {
|
|
1634
|
+
imported: true,
|
|
1635
|
+
issue: {
|
|
1636
|
+
identifier: issue.identifier,
|
|
1637
|
+
title: issue.title,
|
|
1638
|
+
url: issue.url ?? command.url.trim(),
|
|
1639
|
+
stateName: issue.stateName,
|
|
1640
|
+
teamKey: issue.teamKey,
|
|
1641
|
+
},
|
|
1642
|
+
task: ctx.taskRecord(task),
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1304
1646
|
if (command.type === 'conversation.create') {
|
|
1305
1647
|
const conversation = ctx.stateStore.createConversation({
|
|
1306
1648
|
conversationId: command.conversationId ?? `conversation-${randomUUID()}`,
|
|
@@ -1597,42 +1939,24 @@ export async function executeStreamServerCommand(
|
|
|
1597
1939
|
workspaceId: string;
|
|
1598
1940
|
repositoryId?: string;
|
|
1599
1941
|
projectId?: string;
|
|
1600
|
-
title
|
|
1601
|
-
|
|
1602
|
-
linear?: {
|
|
1603
|
-
issueId?: string | null;
|
|
1604
|
-
identifier?: string | null;
|
|
1605
|
-
url?: string | null;
|
|
1606
|
-
teamId?: string | null;
|
|
1607
|
-
projectId?: string | null;
|
|
1608
|
-
projectMilestoneId?: string | null;
|
|
1609
|
-
cycleId?: string | null;
|
|
1610
|
-
stateId?: string | null;
|
|
1611
|
-
assigneeId?: string | null;
|
|
1612
|
-
priority?: number | null;
|
|
1613
|
-
estimate?: number | null;
|
|
1614
|
-
dueDate?: string | null;
|
|
1615
|
-
labelIds?: readonly string[] | null;
|
|
1616
|
-
};
|
|
1942
|
+
title?: string | null;
|
|
1943
|
+
body: string;
|
|
1617
1944
|
} = {
|
|
1618
1945
|
taskId: command.taskId ?? `task-${randomUUID()}`,
|
|
1619
1946
|
tenantId: command.tenantId ?? DEFAULT_TENANT_ID,
|
|
1620
1947
|
userId: command.userId ?? DEFAULT_USER_ID,
|
|
1621
1948
|
workspaceId: command.workspaceId ?? DEFAULT_WORKSPACE_ID,
|
|
1622
|
-
|
|
1949
|
+
body: command.body,
|
|
1623
1950
|
};
|
|
1951
|
+
if (command.title !== undefined) {
|
|
1952
|
+
input.title = command.title;
|
|
1953
|
+
}
|
|
1624
1954
|
if (command.repositoryId !== undefined) {
|
|
1625
1955
|
input.repositoryId = command.repositoryId;
|
|
1626
1956
|
}
|
|
1627
1957
|
if (command.projectId !== undefined) {
|
|
1628
1958
|
input.projectId = command.projectId;
|
|
1629
1959
|
}
|
|
1630
|
-
if (command.description !== undefined) {
|
|
1631
|
-
input.description = command.description;
|
|
1632
|
-
}
|
|
1633
|
-
if (command.linear !== undefined) {
|
|
1634
|
-
input.linear = command.linear;
|
|
1635
|
-
}
|
|
1636
1960
|
const task = ctx.stateStore.createTask(input);
|
|
1637
1961
|
ctx.publishObservedEvent(
|
|
1638
1962
|
{
|
|
@@ -1705,31 +2029,16 @@ export async function executeStreamServerCommand(
|
|
|
1705
2029
|
|
|
1706
2030
|
if (command.type === 'task.update') {
|
|
1707
2031
|
const update: {
|
|
1708
|
-
title?: string;
|
|
1709
|
-
|
|
2032
|
+
title?: string | null;
|
|
2033
|
+
body?: string;
|
|
1710
2034
|
repositoryId?: string | null;
|
|
1711
2035
|
projectId?: string | null;
|
|
1712
|
-
linear?: {
|
|
1713
|
-
issueId?: string | null;
|
|
1714
|
-
identifier?: string | null;
|
|
1715
|
-
url?: string | null;
|
|
1716
|
-
teamId?: string | null;
|
|
1717
|
-
projectId?: string | null;
|
|
1718
|
-
projectMilestoneId?: string | null;
|
|
1719
|
-
cycleId?: string | null;
|
|
1720
|
-
stateId?: string | null;
|
|
1721
|
-
assigneeId?: string | null;
|
|
1722
|
-
priority?: number | null;
|
|
1723
|
-
estimate?: number | null;
|
|
1724
|
-
dueDate?: string | null;
|
|
1725
|
-
labelIds?: readonly string[] | null;
|
|
1726
|
-
} | null;
|
|
1727
2036
|
} = {};
|
|
1728
2037
|
if (command.title !== undefined) {
|
|
1729
2038
|
update.title = command.title;
|
|
1730
2039
|
}
|
|
1731
|
-
if (command.
|
|
1732
|
-
update.
|
|
2040
|
+
if (command.body !== undefined) {
|
|
2041
|
+
update.body = command.body;
|
|
1733
2042
|
}
|
|
1734
2043
|
if (command.repositoryId !== undefined) {
|
|
1735
2044
|
update.repositoryId = command.repositoryId;
|
|
@@ -1737,9 +2046,6 @@ export async function executeStreamServerCommand(
|
|
|
1737
2046
|
if (command.projectId !== undefined) {
|
|
1738
2047
|
update.projectId = command.projectId;
|
|
1739
2048
|
}
|
|
1740
|
-
if (command.linear !== undefined) {
|
|
1741
|
-
update.linear = command.linear;
|
|
1742
|
-
}
|
|
1743
2049
|
const updated = ctx.stateStore.updateTask(command.taskId, update);
|
|
1744
2050
|
if (updated === null) {
|
|
1745
2051
|
throw new Error(`task not found: ${command.taskId}`);
|
|
@@ -2494,13 +2800,14 @@ export async function executeStreamServerCommand(
|
|
|
2494
2800
|
const attachmentId = state.session.attach(
|
|
2495
2801
|
{
|
|
2496
2802
|
onData: (event) => {
|
|
2803
|
+
const chunkBase64 = event.chunk.toString('base64');
|
|
2497
2804
|
ctx.sendToConnection(
|
|
2498
2805
|
connection.id,
|
|
2499
2806
|
{
|
|
2500
2807
|
kind: 'pty.output',
|
|
2501
2808
|
sessionId: command.sessionId,
|
|
2502
2809
|
cursor: event.cursor,
|
|
2503
|
-
chunkBase64
|
|
2810
|
+
chunkBase64,
|
|
2504
2811
|
},
|
|
2505
2812
|
command.sessionId,
|
|
2506
2813
|
);
|
|
@@ -2514,7 +2821,7 @@ export async function executeStreamServerCommand(
|
|
|
2514
2821
|
type: 'session-output',
|
|
2515
2822
|
sessionId: command.sessionId,
|
|
2516
2823
|
outputCursor: event.cursor,
|
|
2517
|
-
chunkBase64
|
|
2824
|
+
chunkBase64,
|
|
2518
2825
|
ts: new Date().toISOString(),
|
|
2519
2826
|
directoryId: sessionState.directoryId,
|
|
2520
2827
|
conversationId: sessionState.id,
|
|
@@ -530,7 +530,8 @@ export function handleSessionEvent(
|
|
|
530
530
|
|
|
531
531
|
const mapped = mapSessionEvent(event);
|
|
532
532
|
if (mapped !== null && event.type !== 'terminal-output') {
|
|
533
|
-
const
|
|
533
|
+
const nowIso = new Date().toISOString();
|
|
534
|
+
const observedAt = mapped.type === 'session-exit' ? nowIso : mapped.record.ts;
|
|
534
535
|
for (const connectionId of sessionState.eventSubscriberConnectionIds) {
|
|
535
536
|
ctx.sendToConnection(
|
|
536
537
|
connectionId,
|
|
@@ -546,7 +547,7 @@ export function handleSessionEvent(
|
|
|
546
547
|
type: 'session-event',
|
|
547
548
|
sessionId,
|
|
548
549
|
event: mapped,
|
|
549
|
-
ts:
|
|
550
|
+
ts: nowIso,
|
|
550
551
|
directoryId: sessionState.directoryId,
|
|
551
552
|
conversationId: sessionState.id,
|
|
552
553
|
});
|