@jmoyers/harness 0.1.0
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/LICENSE +21 -0
- package/README.md +145 -0
- package/native/ptyd/Cargo.lock +16 -0
- package/native/ptyd/Cargo.toml +7 -0
- package/native/ptyd/src/main.rs +257 -0
- package/package.json +90 -0
- package/scripts/build-ptyd.sh +73 -0
- package/scripts/control-plane-daemon.ts +277 -0
- package/scripts/cursor-hook-relay.ts +82 -0
- package/scripts/harness-animate.ts +469 -0
- package/scripts/harness-bin.js +77 -0
- package/scripts/harness-core.ts +1 -0
- package/scripts/harness-inspector.ts +439 -0
- package/scripts/harness.ts +2493 -0
- package/src/adapters/agent-session-state.ts +390 -0
- package/src/cli/gateway-record.ts +173 -0
- package/src/codex/live-session.ts +872 -0
- package/src/config/config-core.ts +1359 -0
- package/src/config/secrets-core.ts +170 -0
- package/src/control-plane/agent-realtime-api.ts +2441 -0
- package/src/control-plane/codex-session-stream.ts +392 -0
- package/src/control-plane/codex-telemetry.ts +1325 -0
- package/src/control-plane/lifecycle-hooks.ts +706 -0
- package/src/control-plane/session-summary.ts +380 -0
- package/src/control-plane/status/agent-status-reducer.ts +21 -0
- package/src/control-plane/status/reducer-base.ts +170 -0
- package/src/control-plane/status/reducers/claude-status-reducer.ts +37 -0
- package/src/control-plane/status/reducers/codex-status-reducer.ts +48 -0
- package/src/control-plane/status/reducers/critique-status-reducer.ts +15 -0
- package/src/control-plane/status/reducers/cursor-status-reducer.ts +37 -0
- package/src/control-plane/status/reducers/terminal-status-reducer.ts +15 -0
- package/src/control-plane/status/session-status-engine.ts +76 -0
- package/src/control-plane/stream-client.ts +396 -0
- package/src/control-plane/stream-command-parser.ts +1673 -0
- package/src/control-plane/stream-protocol.ts +1808 -0
- package/src/control-plane/stream-server-background.ts +486 -0
- package/src/control-plane/stream-server-command.ts +2557 -0
- package/src/control-plane/stream-server-connection.ts +234 -0
- package/src/control-plane/stream-server-observed-filter.ts +112 -0
- package/src/control-plane/stream-server-session-runtime.ts +566 -0
- package/src/control-plane/stream-server-state-store.ts +15 -0
- package/src/control-plane/stream-server.ts +3192 -0
- package/src/cursor/managed-hooks.ts +282 -0
- package/src/domain/conversations.ts +414 -0
- package/src/domain/directories.ts +78 -0
- package/src/domain/repositories.ts +123 -0
- package/src/domain/tasks.ts +148 -0
- package/src/domain/workspace.ts +156 -0
- package/src/events/normalized-events.ts +124 -0
- package/src/mux/ansi-integrity.ts +103 -0
- package/src/mux/control-plane-op-queue.ts +212 -0
- package/src/mux/conversation-rail.ts +339 -0
- package/src/mux/double-click.ts +78 -0
- package/src/mux/dual-pane-core.ts +435 -0
- package/src/mux/harness-core-ui.ts +817 -0
- package/src/mux/input-shortcuts.ts +667 -0
- package/src/mux/live-mux/actions-conversation.ts +344 -0
- package/src/mux/live-mux/actions-repository.ts +246 -0
- package/src/mux/live-mux/actions-task.ts +115 -0
- package/src/mux/live-mux/args.ts +142 -0
- package/src/mux/live-mux/command-menu.ts +298 -0
- package/src/mux/live-mux/control-plane-records.ts +546 -0
- package/src/mux/live-mux/conversation-state.ts +188 -0
- package/src/mux/live-mux/directory-resolution.ts +34 -0
- package/src/mux/live-mux/event-mapping.ts +96 -0
- package/src/mux/live-mux/gateway-profiler.ts +152 -0
- package/src/mux/live-mux/gateway-render-trace.ts +177 -0
- package/src/mux/live-mux/gateway-status-timeline.ts +166 -0
- package/src/mux/live-mux/git-parsing.ts +131 -0
- package/src/mux/live-mux/git-snapshot.ts +263 -0
- package/src/mux/live-mux/git-state.ts +136 -0
- package/src/mux/live-mux/global-shortcut-handlers.ts +143 -0
- package/src/mux/live-mux/home-pane-actions.ts +58 -0
- package/src/mux/live-mux/home-pane-drop.ts +44 -0
- package/src/mux/live-mux/home-pane-entity-click.ts +96 -0
- package/src/mux/live-mux/home-pane-pointer.ts +96 -0
- package/src/mux/live-mux/input-forwarding.ts +112 -0
- package/src/mux/live-mux/layout.ts +30 -0
- package/src/mux/live-mux/left-nav-activation.ts +103 -0
- package/src/mux/live-mux/left-nav.ts +85 -0
- package/src/mux/live-mux/left-rail-actions.ts +118 -0
- package/src/mux/live-mux/left-rail-conversation-click.ts +82 -0
- package/src/mux/live-mux/left-rail-pointer.ts +74 -0
- package/src/mux/live-mux/modal-command-menu-handler.ts +101 -0
- package/src/mux/live-mux/modal-conversation-handlers.ts +217 -0
- package/src/mux/live-mux/modal-input-reducers.ts +94 -0
- package/src/mux/live-mux/modal-overlays.ts +287 -0
- package/src/mux/live-mux/modal-pointer.ts +70 -0
- package/src/mux/live-mux/modal-prompt-handlers.ts +187 -0
- package/src/mux/live-mux/modal-task-editor-handler.ts +156 -0
- package/src/mux/live-mux/observed-stream.ts +87 -0
- package/src/mux/live-mux/palette-parsing.ts +128 -0
- package/src/mux/live-mux/pointer-routing.ts +108 -0
- package/src/mux/live-mux/process-usage.ts +53 -0
- package/src/mux/live-mux/project-pane-pointer.ts +44 -0
- package/src/mux/live-mux/rail-layout.ts +244 -0
- package/src/mux/live-mux/render-trace-analysis.ts +213 -0
- package/src/mux/live-mux/render-trace-state.ts +84 -0
- package/src/mux/live-mux/repository-folding.ts +207 -0
- package/src/mux/live-mux/runtime-shutdown.ts +51 -0
- package/src/mux/live-mux/selection.ts +411 -0
- package/src/mux/live-mux/startup-utils.ts +187 -0
- package/src/mux/live-mux/status-timeline-state.ts +82 -0
- package/src/mux/live-mux/task-pane-shortcuts.ts +206 -0
- package/src/mux/live-mux/terminal-palette.ts +79 -0
- package/src/mux/new-thread-prompt.ts +165 -0
- package/src/mux/project-tree.ts +295 -0
- package/src/mux/render-frame.ts +113 -0
- package/src/mux/runtime-wiring.ts +185 -0
- package/src/mux/selector-index.ts +160 -0
- package/src/mux/startup-sequencer.ts +238 -0
- package/src/mux/task-composer.ts +289 -0
- package/src/mux/task-focused-pane.ts +417 -0
- package/src/mux/task-screen-keybindings.ts +539 -0
- package/src/mux/terminal-input-modes.ts +35 -0
- package/src/mux/workspace-path.ts +55 -0
- package/src/mux/workspace-rail-model.ts +701 -0
- package/src/mux/workspace-rail.ts +247 -0
- package/src/perf/perf-core.ts +307 -0
- package/src/pty/pty_host.ts +217 -0
- package/src/pty/session-broker.ts +158 -0
- package/src/recording/terminal-recording.ts +383 -0
- package/src/services/control-plane.ts +567 -0
- package/src/services/conversation-lifecycle.ts +176 -0
- package/src/services/conversation-startup-hydration.ts +47 -0
- package/src/services/directory-hydration.ts +49 -0
- package/src/services/event-persistence.ts +104 -0
- package/src/services/mux-ui-state-persistence.ts +82 -0
- package/src/services/output-load-sampler.ts +231 -0
- package/src/services/process-usage-refresh.ts +88 -0
- package/src/services/recording.ts +75 -0
- package/src/services/render-trace-recorder.ts +177 -0
- package/src/services/runtime-control-actions.ts +123 -0
- package/src/services/runtime-control-plane-ops.ts +131 -0
- package/src/services/runtime-conversation-actions.ts +113 -0
- package/src/services/runtime-conversation-activation.ts +78 -0
- package/src/services/runtime-conversation-starter.ts +171 -0
- package/src/services/runtime-conversation-title-edit.ts +149 -0
- package/src/services/runtime-directory-actions.ts +164 -0
- package/src/services/runtime-envelope-handler.ts +198 -0
- package/src/services/runtime-git-state.ts +92 -0
- package/src/services/runtime-input-pipeline.ts +50 -0
- package/src/services/runtime-input-router.ts +202 -0
- package/src/services/runtime-layout-resize.ts +236 -0
- package/src/services/runtime-left-rail-render.ts +159 -0
- package/src/services/runtime-main-pane-input.ts +230 -0
- package/src/services/runtime-modal-input.ts +119 -0
- package/src/services/runtime-navigation-input.ts +207 -0
- package/src/services/runtime-process-wiring.ts +68 -0
- package/src/services/runtime-rail-input.ts +287 -0
- package/src/services/runtime-render-flush.ts +146 -0
- package/src/services/runtime-render-lifecycle.ts +104 -0
- package/src/services/runtime-render-orchestrator.ts +108 -0
- package/src/services/runtime-render-pipeline.ts +167 -0
- package/src/services/runtime-render-state.ts +72 -0
- package/src/services/runtime-repository-actions.ts +197 -0
- package/src/services/runtime-right-pane-render.ts +132 -0
- package/src/services/runtime-shutdown.ts +79 -0
- package/src/services/runtime-stream-subscriptions.ts +56 -0
- package/src/services/runtime-task-composer-persistence.ts +139 -0
- package/src/services/runtime-task-editor-actions.ts +83 -0
- package/src/services/runtime-task-pane-actions.ts +198 -0
- package/src/services/runtime-task-pane-shortcuts.ts +189 -0
- package/src/services/runtime-task-pane.ts +62 -0
- package/src/services/runtime-workspace-actions.ts +153 -0
- package/src/services/runtime-workspace-observed-events.ts +190 -0
- package/src/services/session-projection-instrumentation.ts +190 -0
- package/src/services/startup-background-probe.ts +91 -0
- package/src/services/startup-background-resume.ts +65 -0
- package/src/services/startup-orchestrator.ts +166 -0
- package/src/services/startup-output-tracker.ts +54 -0
- package/src/services/startup-paint-tracker.ts +115 -0
- package/src/services/startup-persisted-conversation-queue.ts +45 -0
- package/src/services/startup-settled-gate.ts +67 -0
- package/src/services/startup-shutdown.ts +53 -0
- package/src/services/startup-span-tracker.ts +77 -0
- package/src/services/startup-state-hydration.ts +94 -0
- package/src/services/startup-visibility.ts +35 -0
- package/src/services/status-timeline-recorder.ts +144 -0
- package/src/services/task-pane-selection-actions.ts +153 -0
- package/src/services/task-planning-hydration.ts +58 -0
- package/src/services/task-planning-observed-events.ts +89 -0
- package/src/services/workspace-observed-events.ts +113 -0
- package/src/store/control-plane-store-normalize.ts +760 -0
- package/src/store/control-plane-store-types.ts +224 -0
- package/src/store/control-plane-store.ts +2951 -0
- package/src/store/event-store.ts +253 -0
- package/src/store/sqlite.ts +81 -0
- package/src/terminal/compat-matrix.ts +345 -0
- package/src/terminal/differential-checkpoints.ts +132 -0
- package/src/terminal/parity-suite.ts +441 -0
- package/src/terminal/snapshot-oracle.ts +1840 -0
- package/src/ui/conversation-input-forwarder.ts +114 -0
- package/src/ui/conversation-selection-input.ts +103 -0
- package/src/ui/debug-footer-notice.ts +39 -0
- package/src/ui/global-shortcut-input.ts +126 -0
- package/src/ui/input-preflight.ts +68 -0
- package/src/ui/input-token-router.ts +312 -0
- package/src/ui/input.ts +238 -0
- package/src/ui/kit.ts +509 -0
- package/src/ui/left-nav-input.ts +80 -0
- package/src/ui/left-rail-pointer-input.ts +148 -0
- package/src/ui/main-pane-pointer-input.ts +150 -0
- package/src/ui/modals/manager.ts +192 -0
- package/src/ui/mux-theme.ts +529 -0
- package/src/ui/panes/conversation.ts +19 -0
- package/src/ui/panes/home-gridfire.ts +302 -0
- package/src/ui/panes/home.ts +109 -0
- package/src/ui/panes/left-rail.ts +12 -0
- package/src/ui/panes/project.ts +44 -0
- package/src/ui/pointer-routing-input.ts +158 -0
- package/src/ui/repository-fold-input.ts +91 -0
- package/src/ui/screen.ts +210 -0
- package/src/ui/surface.ts +224 -0
|
@@ -0,0 +1,1808 @@
|
|
|
1
|
+
import type { PtyExit } from '../pty/pty_host.ts';
|
|
2
|
+
import { parseStreamCommand } from './stream-command-parser.ts';
|
|
3
|
+
|
|
4
|
+
export type StreamSignal = 'interrupt' | 'eof' | 'terminate';
|
|
5
|
+
export type StreamSessionRuntimeStatus = 'running' | 'needs-input' | 'completed' | 'exited';
|
|
6
|
+
export type StreamSessionListSort = 'attention-first' | 'started-desc' | 'started-asc';
|
|
7
|
+
export type StreamTelemetrySource = 'otlp-log' | 'otlp-metric' | 'otlp-trace' | 'history';
|
|
8
|
+
export type StreamTelemetryStatusHint = 'running' | 'completed' | 'needs-input';
|
|
9
|
+
export type StreamSessionControllerType = 'human' | 'agent' | 'automation';
|
|
10
|
+
export type StreamSessionDisplayPhase = 'needs-action' | 'starting' | 'working' | 'idle' | 'exited';
|
|
11
|
+
|
|
12
|
+
export interface StreamSessionController {
|
|
13
|
+
controllerId: string;
|
|
14
|
+
controllerType: StreamSessionControllerType;
|
|
15
|
+
controllerLabel: string | null;
|
|
16
|
+
claimedAt: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface StreamTelemetrySummary {
|
|
20
|
+
source: StreamTelemetrySource;
|
|
21
|
+
eventName: string | null;
|
|
22
|
+
severity: string | null;
|
|
23
|
+
summary: string | null;
|
|
24
|
+
observedAt: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface StreamSessionStatusModel {
|
|
28
|
+
runtimeStatus: StreamSessionRuntimeStatus;
|
|
29
|
+
phase: StreamSessionDisplayPhase;
|
|
30
|
+
glyph: '▲' | '◔' | '◆' | '○' | '■';
|
|
31
|
+
badge: 'NEED' | 'RUN ' | 'DONE' | 'EXIT';
|
|
32
|
+
detailText: string;
|
|
33
|
+
attentionReason: string | null;
|
|
34
|
+
lastKnownWork: string | null;
|
|
35
|
+
lastKnownWorkAt: string | null;
|
|
36
|
+
phaseHint: 'needs-action' | 'working' | 'idle' | null;
|
|
37
|
+
observedAt: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface StreamSessionKeyEventRecord {
|
|
41
|
+
source: StreamTelemetrySource;
|
|
42
|
+
eventName: string | null;
|
|
43
|
+
severity: string | null;
|
|
44
|
+
summary: string | null;
|
|
45
|
+
observedAt: string;
|
|
46
|
+
statusHint: StreamTelemetryStatusHint | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface DirectoryUpsertCommand {
|
|
50
|
+
type: 'directory.upsert';
|
|
51
|
+
directoryId?: string;
|
|
52
|
+
tenantId?: string;
|
|
53
|
+
userId?: string;
|
|
54
|
+
workspaceId?: string;
|
|
55
|
+
path: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface DirectoryListCommand {
|
|
59
|
+
type: 'directory.list';
|
|
60
|
+
tenantId?: string;
|
|
61
|
+
userId?: string;
|
|
62
|
+
workspaceId?: string;
|
|
63
|
+
includeArchived?: boolean;
|
|
64
|
+
limit?: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface DirectoryArchiveCommand {
|
|
68
|
+
type: 'directory.archive';
|
|
69
|
+
directoryId: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface DirectoryGitStatusListCommand {
|
|
73
|
+
type: 'directory.git-status';
|
|
74
|
+
tenantId?: string;
|
|
75
|
+
userId?: string;
|
|
76
|
+
workspaceId?: string;
|
|
77
|
+
directoryId?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface ConversationCreateCommand {
|
|
81
|
+
type: 'conversation.create';
|
|
82
|
+
conversationId?: string;
|
|
83
|
+
directoryId: string;
|
|
84
|
+
title: string;
|
|
85
|
+
agentType: string;
|
|
86
|
+
adapterState?: Record<string, unknown>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface ConversationListCommand {
|
|
90
|
+
type: 'conversation.list';
|
|
91
|
+
directoryId?: string;
|
|
92
|
+
tenantId?: string;
|
|
93
|
+
userId?: string;
|
|
94
|
+
workspaceId?: string;
|
|
95
|
+
includeArchived?: boolean;
|
|
96
|
+
limit?: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
interface ConversationArchiveCommand {
|
|
100
|
+
type: 'conversation.archive';
|
|
101
|
+
conversationId: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface ConversationUpdateCommand {
|
|
105
|
+
type: 'conversation.update';
|
|
106
|
+
conversationId: string;
|
|
107
|
+
title: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface ConversationDeleteCommand {
|
|
111
|
+
type: 'conversation.delete';
|
|
112
|
+
conversationId: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type StreamTaskStatus = 'draft' | 'ready' | 'in-progress' | 'completed';
|
|
116
|
+
type StreamTaskScopeKind = 'global' | 'repository' | 'project';
|
|
117
|
+
type StreamTaskLinearPriority = 0 | 1 | 2 | 3 | 4;
|
|
118
|
+
type StreamProjectTaskFocusMode = 'balanced' | 'own-only';
|
|
119
|
+
type StreamProjectThreadSpawnMode = 'new-thread' | 'reuse-thread';
|
|
120
|
+
type StreamAutomationPolicyScope = 'global' | 'repository' | 'project';
|
|
121
|
+
|
|
122
|
+
interface StreamTaskLinearInput {
|
|
123
|
+
issueId?: string | null;
|
|
124
|
+
identifier?: string | null;
|
|
125
|
+
url?: string | null;
|
|
126
|
+
teamId?: string | null;
|
|
127
|
+
projectId?: string | null;
|
|
128
|
+
projectMilestoneId?: string | null;
|
|
129
|
+
cycleId?: string | null;
|
|
130
|
+
stateId?: string | null;
|
|
131
|
+
assigneeId?: string | null;
|
|
132
|
+
priority?: StreamTaskLinearPriority | null;
|
|
133
|
+
estimate?: number | null;
|
|
134
|
+
dueDate?: string | null;
|
|
135
|
+
labelIds?: readonly string[] | null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
interface RepositoryUpsertCommand {
|
|
139
|
+
type: 'repository.upsert';
|
|
140
|
+
repositoryId?: string;
|
|
141
|
+
tenantId?: string;
|
|
142
|
+
userId?: string;
|
|
143
|
+
workspaceId?: string;
|
|
144
|
+
name: string;
|
|
145
|
+
remoteUrl: string;
|
|
146
|
+
defaultBranch?: string;
|
|
147
|
+
metadata?: Record<string, unknown>;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface RepositoryGetCommand {
|
|
151
|
+
type: 'repository.get';
|
|
152
|
+
repositoryId: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface RepositoryListCommand {
|
|
156
|
+
type: 'repository.list';
|
|
157
|
+
tenantId?: string;
|
|
158
|
+
userId?: string;
|
|
159
|
+
workspaceId?: string;
|
|
160
|
+
includeArchived?: boolean;
|
|
161
|
+
limit?: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
interface RepositoryUpdateCommand {
|
|
165
|
+
type: 'repository.update';
|
|
166
|
+
repositoryId: string;
|
|
167
|
+
name?: string;
|
|
168
|
+
remoteUrl?: string;
|
|
169
|
+
defaultBranch?: string;
|
|
170
|
+
metadata?: Record<string, unknown>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
interface RepositoryArchiveCommand {
|
|
174
|
+
type: 'repository.archive';
|
|
175
|
+
repositoryId: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface TaskCreateCommand {
|
|
179
|
+
type: 'task.create';
|
|
180
|
+
taskId?: string;
|
|
181
|
+
tenantId?: string;
|
|
182
|
+
userId?: string;
|
|
183
|
+
workspaceId?: string;
|
|
184
|
+
repositoryId?: string;
|
|
185
|
+
projectId?: string;
|
|
186
|
+
title: string;
|
|
187
|
+
description?: string;
|
|
188
|
+
linear?: StreamTaskLinearInput;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
interface TaskGetCommand {
|
|
192
|
+
type: 'task.get';
|
|
193
|
+
taskId: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface TaskListCommand {
|
|
197
|
+
type: 'task.list';
|
|
198
|
+
tenantId?: string;
|
|
199
|
+
userId?: string;
|
|
200
|
+
workspaceId?: string;
|
|
201
|
+
repositoryId?: string;
|
|
202
|
+
projectId?: string;
|
|
203
|
+
scopeKind?: StreamTaskScopeKind;
|
|
204
|
+
status?: StreamTaskStatus;
|
|
205
|
+
limit?: number;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
interface TaskUpdateCommand {
|
|
209
|
+
type: 'task.update';
|
|
210
|
+
taskId: string;
|
|
211
|
+
title?: string;
|
|
212
|
+
description?: string;
|
|
213
|
+
repositoryId?: string | null;
|
|
214
|
+
projectId?: string | null;
|
|
215
|
+
linear?: StreamTaskLinearInput | null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
interface TaskDeleteCommand {
|
|
219
|
+
type: 'task.delete';
|
|
220
|
+
taskId: string;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
interface TaskClaimCommand {
|
|
224
|
+
type: 'task.claim';
|
|
225
|
+
taskId: string;
|
|
226
|
+
controllerId: string;
|
|
227
|
+
directoryId?: string;
|
|
228
|
+
branchName?: string;
|
|
229
|
+
baseBranch?: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
interface TaskPullCommand {
|
|
233
|
+
type: 'task.pull';
|
|
234
|
+
tenantId?: string;
|
|
235
|
+
userId?: string;
|
|
236
|
+
workspaceId?: string;
|
|
237
|
+
controllerId: string;
|
|
238
|
+
directoryId?: string;
|
|
239
|
+
repositoryId?: string;
|
|
240
|
+
branchName?: string;
|
|
241
|
+
baseBranch?: string;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
interface TaskCompleteCommand {
|
|
245
|
+
type: 'task.complete';
|
|
246
|
+
taskId: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
interface TaskQueueCommand {
|
|
250
|
+
type: 'task.queue';
|
|
251
|
+
taskId: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
interface TaskReadyCommand {
|
|
255
|
+
type: 'task.ready';
|
|
256
|
+
taskId: string;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
interface TaskDraftCommand {
|
|
260
|
+
type: 'task.draft';
|
|
261
|
+
taskId: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface TaskReorderCommand {
|
|
265
|
+
type: 'task.reorder';
|
|
266
|
+
tenantId: string;
|
|
267
|
+
userId: string;
|
|
268
|
+
workspaceId: string;
|
|
269
|
+
orderedTaskIds: string[];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
interface ProjectSettingsGetCommand {
|
|
273
|
+
type: 'project.settings-get';
|
|
274
|
+
directoryId: string;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
interface ProjectSettingsUpdateCommand {
|
|
278
|
+
type: 'project.settings-update';
|
|
279
|
+
directoryId: string;
|
|
280
|
+
pinnedBranch?: string | null;
|
|
281
|
+
taskFocusMode?: StreamProjectTaskFocusMode;
|
|
282
|
+
threadSpawnMode?: StreamProjectThreadSpawnMode;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
interface ProjectStatusCommand {
|
|
286
|
+
type: 'project.status';
|
|
287
|
+
directoryId: string;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
interface AutomationPolicyGetCommand {
|
|
291
|
+
type: 'automation.policy-get';
|
|
292
|
+
tenantId?: string;
|
|
293
|
+
userId?: string;
|
|
294
|
+
workspaceId?: string;
|
|
295
|
+
scope: StreamAutomationPolicyScope;
|
|
296
|
+
scopeId?: string;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
interface AutomationPolicySetCommand {
|
|
300
|
+
type: 'automation.policy-set';
|
|
301
|
+
tenantId?: string;
|
|
302
|
+
userId?: string;
|
|
303
|
+
workspaceId?: string;
|
|
304
|
+
scope: StreamAutomationPolicyScope;
|
|
305
|
+
scopeId?: string;
|
|
306
|
+
automationEnabled?: boolean;
|
|
307
|
+
frozen?: boolean;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
interface GitHubProjectPrCommand {
|
|
311
|
+
type: 'github.project-pr';
|
|
312
|
+
directoryId: string;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
interface GitHubPrListCommand {
|
|
316
|
+
type: 'github.pr-list';
|
|
317
|
+
tenantId?: string;
|
|
318
|
+
userId?: string;
|
|
319
|
+
workspaceId?: string;
|
|
320
|
+
repositoryId?: string;
|
|
321
|
+
directoryId?: string;
|
|
322
|
+
headBranch?: string;
|
|
323
|
+
state?: 'open' | 'closed';
|
|
324
|
+
limit?: number;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
interface GitHubPrCreateCommand {
|
|
328
|
+
type: 'github.pr-create';
|
|
329
|
+
directoryId: string;
|
|
330
|
+
title?: string;
|
|
331
|
+
body?: string;
|
|
332
|
+
baseBranch?: string;
|
|
333
|
+
headBranch?: string;
|
|
334
|
+
draft?: boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
interface GitHubPrJobsListCommand {
|
|
338
|
+
type: 'github.pr-jobs-list';
|
|
339
|
+
tenantId?: string;
|
|
340
|
+
userId?: string;
|
|
341
|
+
workspaceId?: string;
|
|
342
|
+
repositoryId?: string;
|
|
343
|
+
prRecordId?: string;
|
|
344
|
+
limit?: number;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
interface GitHubRepoMyPrsUrlCommand {
|
|
348
|
+
type: 'github.repo-my-prs-url';
|
|
349
|
+
repositoryId: string;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface StreamSubscribeCommand {
|
|
353
|
+
type: 'stream.subscribe';
|
|
354
|
+
tenantId?: string;
|
|
355
|
+
userId?: string;
|
|
356
|
+
workspaceId?: string;
|
|
357
|
+
repositoryId?: string;
|
|
358
|
+
taskId?: string;
|
|
359
|
+
directoryId?: string;
|
|
360
|
+
conversationId?: string;
|
|
361
|
+
includeOutput?: boolean;
|
|
362
|
+
afterCursor?: number;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
interface StreamUnsubscribeCommand {
|
|
366
|
+
type: 'stream.unsubscribe';
|
|
367
|
+
subscriptionId: string;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
interface SessionListCommand {
|
|
371
|
+
type: 'session.list';
|
|
372
|
+
tenantId?: string;
|
|
373
|
+
userId?: string;
|
|
374
|
+
workspaceId?: string;
|
|
375
|
+
worktreeId?: string;
|
|
376
|
+
status?: StreamSessionRuntimeStatus;
|
|
377
|
+
live?: boolean;
|
|
378
|
+
sort?: StreamSessionListSort;
|
|
379
|
+
limit?: number;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
interface AttentionListCommand {
|
|
383
|
+
type: 'attention.list';
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
interface SessionStatusCommand {
|
|
387
|
+
type: 'session.status';
|
|
388
|
+
sessionId: string;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
interface SessionSnapshotCommand {
|
|
392
|
+
type: 'session.snapshot';
|
|
393
|
+
sessionId: string;
|
|
394
|
+
tailLines?: number;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
interface SessionRespondCommand {
|
|
398
|
+
type: 'session.respond';
|
|
399
|
+
sessionId: string;
|
|
400
|
+
text: string;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
interface SessionClaimCommand {
|
|
404
|
+
type: 'session.claim';
|
|
405
|
+
sessionId: string;
|
|
406
|
+
controllerId: string;
|
|
407
|
+
controllerType: StreamSessionControllerType;
|
|
408
|
+
controllerLabel?: string;
|
|
409
|
+
reason?: string;
|
|
410
|
+
takeover?: boolean;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
interface SessionReleaseCommand {
|
|
414
|
+
type: 'session.release';
|
|
415
|
+
sessionId: string;
|
|
416
|
+
reason?: string;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
interface SessionInterruptCommand {
|
|
420
|
+
type: 'session.interrupt';
|
|
421
|
+
sessionId: string;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
interface SessionRemoveCommand {
|
|
425
|
+
type: 'session.remove';
|
|
426
|
+
sessionId: string;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
interface PtyStartCommand {
|
|
430
|
+
type: 'pty.start';
|
|
431
|
+
sessionId: string;
|
|
432
|
+
args: string[];
|
|
433
|
+
env?: Record<string, string>;
|
|
434
|
+
cwd?: string;
|
|
435
|
+
initialCols: number;
|
|
436
|
+
initialRows: number;
|
|
437
|
+
terminalForegroundHex?: string;
|
|
438
|
+
terminalBackgroundHex?: string;
|
|
439
|
+
tenantId?: string;
|
|
440
|
+
userId?: string;
|
|
441
|
+
workspaceId?: string;
|
|
442
|
+
worktreeId?: string;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
interface PtyAttachCommand {
|
|
446
|
+
type: 'pty.attach';
|
|
447
|
+
sessionId: string;
|
|
448
|
+
sinceCursor?: number;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
interface PtyDetachCommand {
|
|
452
|
+
type: 'pty.detach';
|
|
453
|
+
sessionId: string;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
interface PtySubscribeEventsCommand {
|
|
457
|
+
type: 'pty.subscribe-events';
|
|
458
|
+
sessionId: string;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
interface PtyUnsubscribeEventsCommand {
|
|
462
|
+
type: 'pty.unsubscribe-events';
|
|
463
|
+
sessionId: string;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
interface PtyCloseCommand {
|
|
467
|
+
type: 'pty.close';
|
|
468
|
+
sessionId: string;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export type StreamCommand =
|
|
472
|
+
| DirectoryUpsertCommand
|
|
473
|
+
| DirectoryListCommand
|
|
474
|
+
| DirectoryArchiveCommand
|
|
475
|
+
| DirectoryGitStatusListCommand
|
|
476
|
+
| ConversationCreateCommand
|
|
477
|
+
| ConversationListCommand
|
|
478
|
+
| ConversationArchiveCommand
|
|
479
|
+
| ConversationUpdateCommand
|
|
480
|
+
| ConversationDeleteCommand
|
|
481
|
+
| RepositoryUpsertCommand
|
|
482
|
+
| RepositoryGetCommand
|
|
483
|
+
| RepositoryListCommand
|
|
484
|
+
| RepositoryUpdateCommand
|
|
485
|
+
| RepositoryArchiveCommand
|
|
486
|
+
| TaskCreateCommand
|
|
487
|
+
| TaskGetCommand
|
|
488
|
+
| TaskListCommand
|
|
489
|
+
| TaskUpdateCommand
|
|
490
|
+
| TaskDeleteCommand
|
|
491
|
+
| TaskClaimCommand
|
|
492
|
+
| TaskPullCommand
|
|
493
|
+
| TaskCompleteCommand
|
|
494
|
+
| TaskQueueCommand
|
|
495
|
+
| TaskReadyCommand
|
|
496
|
+
| TaskDraftCommand
|
|
497
|
+
| TaskReorderCommand
|
|
498
|
+
| ProjectSettingsGetCommand
|
|
499
|
+
| ProjectSettingsUpdateCommand
|
|
500
|
+
| ProjectStatusCommand
|
|
501
|
+
| AutomationPolicyGetCommand
|
|
502
|
+
| AutomationPolicySetCommand
|
|
503
|
+
| GitHubProjectPrCommand
|
|
504
|
+
| GitHubPrListCommand
|
|
505
|
+
| GitHubPrCreateCommand
|
|
506
|
+
| GitHubPrJobsListCommand
|
|
507
|
+
| GitHubRepoMyPrsUrlCommand
|
|
508
|
+
| StreamSubscribeCommand
|
|
509
|
+
| StreamUnsubscribeCommand
|
|
510
|
+
| SessionListCommand
|
|
511
|
+
| AttentionListCommand
|
|
512
|
+
| SessionStatusCommand
|
|
513
|
+
| SessionSnapshotCommand
|
|
514
|
+
| SessionRespondCommand
|
|
515
|
+
| SessionClaimCommand
|
|
516
|
+
| SessionReleaseCommand
|
|
517
|
+
| SessionInterruptCommand
|
|
518
|
+
| SessionRemoveCommand
|
|
519
|
+
| PtyStartCommand
|
|
520
|
+
| PtyAttachCommand
|
|
521
|
+
| PtyDetachCommand
|
|
522
|
+
| PtySubscribeEventsCommand
|
|
523
|
+
| PtyUnsubscribeEventsCommand
|
|
524
|
+
| PtyCloseCommand;
|
|
525
|
+
|
|
526
|
+
export interface StreamCommandEnvelope {
|
|
527
|
+
kind: 'command';
|
|
528
|
+
commandId: string;
|
|
529
|
+
command: StreamCommand;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
interface StreamAuthEnvelope {
|
|
533
|
+
kind: 'auth';
|
|
534
|
+
token: string;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
interface StreamInputEnvelope {
|
|
538
|
+
kind: 'pty.input';
|
|
539
|
+
sessionId: string;
|
|
540
|
+
dataBase64: string;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
interface StreamResizeEnvelope {
|
|
544
|
+
kind: 'pty.resize';
|
|
545
|
+
sessionId: string;
|
|
546
|
+
cols: number;
|
|
547
|
+
rows: number;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
interface StreamSignalEnvelope {
|
|
551
|
+
kind: 'pty.signal';
|
|
552
|
+
sessionId: string;
|
|
553
|
+
signal: StreamSignal;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export type StreamClientEnvelope =
|
|
557
|
+
| StreamAuthEnvelope
|
|
558
|
+
| StreamCommandEnvelope
|
|
559
|
+
| StreamInputEnvelope
|
|
560
|
+
| StreamResizeEnvelope
|
|
561
|
+
| StreamSignalEnvelope;
|
|
562
|
+
|
|
563
|
+
interface StreamNotifyRecord {
|
|
564
|
+
ts: string;
|
|
565
|
+
payload: Record<string, unknown>;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
export type StreamSessionEvent =
|
|
569
|
+
| {
|
|
570
|
+
type: 'notify';
|
|
571
|
+
record: StreamNotifyRecord;
|
|
572
|
+
}
|
|
573
|
+
| {
|
|
574
|
+
type: 'session-exit';
|
|
575
|
+
exit: PtyExit;
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
export type StreamObservedEvent =
|
|
579
|
+
| {
|
|
580
|
+
type: 'directory-upserted';
|
|
581
|
+
directory: Record<string, unknown>;
|
|
582
|
+
}
|
|
583
|
+
| {
|
|
584
|
+
type: 'directory-archived';
|
|
585
|
+
directoryId: string;
|
|
586
|
+
ts: string;
|
|
587
|
+
}
|
|
588
|
+
| {
|
|
589
|
+
type: 'directory-git-updated';
|
|
590
|
+
directoryId: string;
|
|
591
|
+
summary: {
|
|
592
|
+
branch: string;
|
|
593
|
+
changedFiles: number;
|
|
594
|
+
additions: number;
|
|
595
|
+
deletions: number;
|
|
596
|
+
};
|
|
597
|
+
repositorySnapshot: {
|
|
598
|
+
normalizedRemoteUrl: string | null;
|
|
599
|
+
commitCount: number | null;
|
|
600
|
+
lastCommitAt: string | null;
|
|
601
|
+
shortCommitHash: string | null;
|
|
602
|
+
inferredName: string | null;
|
|
603
|
+
defaultBranch: string | null;
|
|
604
|
+
};
|
|
605
|
+
repositoryId: string | null;
|
|
606
|
+
repository: Record<string, unknown> | null;
|
|
607
|
+
observedAt: string;
|
|
608
|
+
}
|
|
609
|
+
| {
|
|
610
|
+
type: 'conversation-created';
|
|
611
|
+
conversation: Record<string, unknown>;
|
|
612
|
+
}
|
|
613
|
+
| {
|
|
614
|
+
type: 'conversation-updated';
|
|
615
|
+
conversation: Record<string, unknown>;
|
|
616
|
+
}
|
|
617
|
+
| {
|
|
618
|
+
type: 'conversation-archived';
|
|
619
|
+
conversationId: string;
|
|
620
|
+
ts: string;
|
|
621
|
+
}
|
|
622
|
+
| {
|
|
623
|
+
type: 'conversation-deleted';
|
|
624
|
+
conversationId: string;
|
|
625
|
+
ts: string;
|
|
626
|
+
}
|
|
627
|
+
| {
|
|
628
|
+
type: 'repository-upserted';
|
|
629
|
+
repository: Record<string, unknown>;
|
|
630
|
+
}
|
|
631
|
+
| {
|
|
632
|
+
type: 'repository-updated';
|
|
633
|
+
repository: Record<string, unknown>;
|
|
634
|
+
}
|
|
635
|
+
| {
|
|
636
|
+
type: 'repository-archived';
|
|
637
|
+
repositoryId: string;
|
|
638
|
+
ts: string;
|
|
639
|
+
}
|
|
640
|
+
| {
|
|
641
|
+
type: 'task-created';
|
|
642
|
+
task: Record<string, unknown>;
|
|
643
|
+
}
|
|
644
|
+
| {
|
|
645
|
+
type: 'task-updated';
|
|
646
|
+
task: Record<string, unknown>;
|
|
647
|
+
}
|
|
648
|
+
| {
|
|
649
|
+
type: 'task-deleted';
|
|
650
|
+
taskId: string;
|
|
651
|
+
ts: string;
|
|
652
|
+
}
|
|
653
|
+
| {
|
|
654
|
+
type: 'task-reordered';
|
|
655
|
+
tasks: Record<string, unknown>[];
|
|
656
|
+
ts: string;
|
|
657
|
+
}
|
|
658
|
+
| {
|
|
659
|
+
type: 'github-pr-upserted';
|
|
660
|
+
pr: Record<string, unknown>;
|
|
661
|
+
}
|
|
662
|
+
| {
|
|
663
|
+
type: 'github-pr-closed';
|
|
664
|
+
prRecordId: string;
|
|
665
|
+
repositoryId: string;
|
|
666
|
+
ts: string;
|
|
667
|
+
}
|
|
668
|
+
| {
|
|
669
|
+
type: 'github-pr-jobs-updated';
|
|
670
|
+
prRecordId: string;
|
|
671
|
+
repositoryId: string;
|
|
672
|
+
ciRollup: 'pending' | 'success' | 'failure' | 'cancelled' | 'neutral' | 'none';
|
|
673
|
+
jobs: Record<string, unknown>[];
|
|
674
|
+
ts: string;
|
|
675
|
+
}
|
|
676
|
+
| {
|
|
677
|
+
type: 'session-status';
|
|
678
|
+
sessionId: string;
|
|
679
|
+
status: StreamSessionRuntimeStatus;
|
|
680
|
+
attentionReason: string | null;
|
|
681
|
+
statusModel: StreamSessionStatusModel | null;
|
|
682
|
+
live: boolean;
|
|
683
|
+
ts: string;
|
|
684
|
+
directoryId: string | null;
|
|
685
|
+
conversationId: string | null;
|
|
686
|
+
telemetry: StreamTelemetrySummary | null;
|
|
687
|
+
controller: StreamSessionController | null;
|
|
688
|
+
}
|
|
689
|
+
| {
|
|
690
|
+
type: 'session-event';
|
|
691
|
+
sessionId: string;
|
|
692
|
+
event: StreamSessionEvent;
|
|
693
|
+
ts: string;
|
|
694
|
+
directoryId: string | null;
|
|
695
|
+
conversationId: string | null;
|
|
696
|
+
}
|
|
697
|
+
| {
|
|
698
|
+
type: 'session-key-event';
|
|
699
|
+
sessionId: string;
|
|
700
|
+
keyEvent: StreamSessionKeyEventRecord;
|
|
701
|
+
ts: string;
|
|
702
|
+
directoryId: string | null;
|
|
703
|
+
conversationId: string | null;
|
|
704
|
+
}
|
|
705
|
+
| {
|
|
706
|
+
type: 'session-control';
|
|
707
|
+
sessionId: string;
|
|
708
|
+
action: 'claimed' | 'released' | 'taken-over';
|
|
709
|
+
controller: StreamSessionController | null;
|
|
710
|
+
previousController: StreamSessionController | null;
|
|
711
|
+
reason: string | null;
|
|
712
|
+
ts: string;
|
|
713
|
+
directoryId: string | null;
|
|
714
|
+
conversationId: string | null;
|
|
715
|
+
}
|
|
716
|
+
| {
|
|
717
|
+
type: 'session-output';
|
|
718
|
+
sessionId: string;
|
|
719
|
+
outputCursor: number;
|
|
720
|
+
chunkBase64: string;
|
|
721
|
+
ts: string;
|
|
722
|
+
directoryId: string | null;
|
|
723
|
+
conversationId: string | null;
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
interface StreamCommandAcceptedEnvelope {
|
|
727
|
+
kind: 'command.accepted';
|
|
728
|
+
commandId: string;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
interface StreamAuthOkEnvelope {
|
|
732
|
+
kind: 'auth.ok';
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
interface StreamAuthErrorEnvelope {
|
|
736
|
+
kind: 'auth.error';
|
|
737
|
+
error: string;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
interface StreamCommandCompletedEnvelope {
|
|
741
|
+
kind: 'command.completed';
|
|
742
|
+
commandId: string;
|
|
743
|
+
result: Record<string, unknown>;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
interface StreamCommandFailedEnvelope {
|
|
747
|
+
kind: 'command.failed';
|
|
748
|
+
commandId: string;
|
|
749
|
+
error: string;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
interface StreamPtyOutputEnvelope {
|
|
753
|
+
kind: 'pty.output';
|
|
754
|
+
sessionId: string;
|
|
755
|
+
cursor: number;
|
|
756
|
+
chunkBase64: string;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
interface StreamPtyExitEnvelope {
|
|
760
|
+
kind: 'pty.exit';
|
|
761
|
+
sessionId: string;
|
|
762
|
+
exit: PtyExit;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
interface StreamPtyEventEnvelope {
|
|
766
|
+
kind: 'pty.event';
|
|
767
|
+
sessionId: string;
|
|
768
|
+
event: StreamSessionEvent;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
interface StreamObservedEventEnvelope {
|
|
772
|
+
kind: 'stream.event';
|
|
773
|
+
subscriptionId: string;
|
|
774
|
+
cursor: number;
|
|
775
|
+
event: StreamObservedEvent;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
export type StreamServerEnvelope =
|
|
779
|
+
| StreamAuthOkEnvelope
|
|
780
|
+
| StreamAuthErrorEnvelope
|
|
781
|
+
| StreamCommandAcceptedEnvelope
|
|
782
|
+
| StreamCommandCompletedEnvelope
|
|
783
|
+
| StreamCommandFailedEnvelope
|
|
784
|
+
| StreamPtyOutputEnvelope
|
|
785
|
+
| StreamPtyExitEnvelope
|
|
786
|
+
| StreamPtyEventEnvelope
|
|
787
|
+
| StreamObservedEventEnvelope;
|
|
788
|
+
|
|
789
|
+
interface ConsumedJsonLines {
|
|
790
|
+
messages: unknown[];
|
|
791
|
+
remainder: string;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export function encodeStreamEnvelope(
|
|
795
|
+
envelope: StreamClientEnvelope | StreamServerEnvelope,
|
|
796
|
+
): string {
|
|
797
|
+
return `${JSON.stringify(envelope)}\n`;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
export function consumeJsonLines(buffer: string): ConsumedJsonLines {
|
|
801
|
+
const lines = buffer.split('\n');
|
|
802
|
+
const remainder = lines.pop() as string;
|
|
803
|
+
const messages: unknown[] = [];
|
|
804
|
+
|
|
805
|
+
for (const line of lines) {
|
|
806
|
+
const trimmed = line.trim();
|
|
807
|
+
if (trimmed.length === 0) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
try {
|
|
811
|
+
messages.push(JSON.parse(trimmed));
|
|
812
|
+
} catch {
|
|
813
|
+
// Invalid lines are ignored so malformed peers cannot break stream processing.
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return {
|
|
818
|
+
messages,
|
|
819
|
+
remainder,
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
824
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
827
|
+
return value as Record<string, unknown>;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function readString(value: unknown): string | null {
|
|
831
|
+
return typeof value === 'string' ? value : null;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function readNumber(value: unknown): number | null {
|
|
835
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function readBoolean(value: unknown): boolean | null {
|
|
839
|
+
return typeof value === 'boolean' ? value : null;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function readSignalName(value: unknown): NodeJS.Signals | null {
|
|
843
|
+
if (typeof value !== 'string') {
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
if (!/^SIG[A-Z0-9]+(?:_[A-Z0-9]+)*$/.test(value)) {
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
return value as NodeJS.Signals;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
export function parseClientEnvelope(value: unknown): StreamClientEnvelope | null {
|
|
853
|
+
const record = asRecord(value);
|
|
854
|
+
if (record === null) {
|
|
855
|
+
return null;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const kind = readString(record['kind']);
|
|
859
|
+
if (kind === null) {
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (kind === 'auth') {
|
|
864
|
+
const token = readString(record['token']);
|
|
865
|
+
if (token === null) {
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
return {
|
|
869
|
+
kind,
|
|
870
|
+
token,
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (kind === 'command') {
|
|
875
|
+
const commandId = readString(record['commandId']);
|
|
876
|
+
const command = parseStreamCommand(record['command']);
|
|
877
|
+
if (commandId === null || command === null) {
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
return {
|
|
882
|
+
kind,
|
|
883
|
+
commandId,
|
|
884
|
+
command,
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
const sessionId = readString(record['sessionId']);
|
|
889
|
+
if (sessionId === null) {
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (kind === 'pty.input') {
|
|
894
|
+
const dataBase64 = readString(record['dataBase64']);
|
|
895
|
+
if (dataBase64 === null) {
|
|
896
|
+
return null;
|
|
897
|
+
}
|
|
898
|
+
return {
|
|
899
|
+
kind,
|
|
900
|
+
sessionId,
|
|
901
|
+
dataBase64,
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
if (kind === 'pty.resize') {
|
|
906
|
+
const cols = readNumber(record['cols']);
|
|
907
|
+
const rows = readNumber(record['rows']);
|
|
908
|
+
if (cols === null || rows === null) {
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
return {
|
|
912
|
+
kind,
|
|
913
|
+
sessionId,
|
|
914
|
+
cols,
|
|
915
|
+
rows,
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (kind === 'pty.signal') {
|
|
920
|
+
const signal = readString(record['signal']);
|
|
921
|
+
if (signal !== 'interrupt' && signal !== 'eof' && signal !== 'terminate') {
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
return {
|
|
925
|
+
kind,
|
|
926
|
+
sessionId,
|
|
927
|
+
signal,
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function parseStreamSessionEvent(value: unknown): StreamSessionEvent | null {
|
|
935
|
+
const record = asRecord(value);
|
|
936
|
+
if (record === null) {
|
|
937
|
+
return null;
|
|
938
|
+
}
|
|
939
|
+
const type = readString(record['type']);
|
|
940
|
+
if (type === null) {
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (type === 'session-exit') {
|
|
945
|
+
const exitRecord = asRecord(record['exit']);
|
|
946
|
+
if (exitRecord === null) {
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
const code = exitRecord['code'];
|
|
950
|
+
const signal = exitRecord['signal'];
|
|
951
|
+
const normalizedCode = code === null ? null : readNumber(code);
|
|
952
|
+
const normalizedSignal = signal === null ? null : readSignalName(signal);
|
|
953
|
+
if (normalizedCode === null && code !== null) {
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
if (normalizedSignal === null && signal !== null) {
|
|
957
|
+
return null;
|
|
958
|
+
}
|
|
959
|
+
return {
|
|
960
|
+
type,
|
|
961
|
+
exit: {
|
|
962
|
+
code: normalizedCode,
|
|
963
|
+
signal: normalizedSignal,
|
|
964
|
+
},
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
if (type === 'notify') {
|
|
969
|
+
const notifyRecord = asRecord(record['record']);
|
|
970
|
+
if (notifyRecord === null) {
|
|
971
|
+
return null;
|
|
972
|
+
}
|
|
973
|
+
const ts = readString(notifyRecord['ts']);
|
|
974
|
+
const payload = asRecord(notifyRecord['payload']);
|
|
975
|
+
if (ts === null || payload === null) {
|
|
976
|
+
return null;
|
|
977
|
+
}
|
|
978
|
+
return {
|
|
979
|
+
type: 'notify',
|
|
980
|
+
record: {
|
|
981
|
+
ts,
|
|
982
|
+
payload,
|
|
983
|
+
},
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
return null;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
function parseTelemetrySource(value: unknown): StreamTelemetrySource | null {
|
|
991
|
+
if (
|
|
992
|
+
value === 'otlp-log' ||
|
|
993
|
+
value === 'otlp-metric' ||
|
|
994
|
+
value === 'otlp-trace' ||
|
|
995
|
+
value === 'history'
|
|
996
|
+
) {
|
|
997
|
+
return value;
|
|
998
|
+
}
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function parseSessionControllerType(value: unknown): StreamSessionControllerType | null {
|
|
1003
|
+
if (value === 'human' || value === 'agent' || value === 'automation') {
|
|
1004
|
+
return value;
|
|
1005
|
+
}
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
function parseSessionController(value: unknown): StreamSessionController | null | undefined {
|
|
1010
|
+
if (value === undefined) {
|
|
1011
|
+
return undefined;
|
|
1012
|
+
}
|
|
1013
|
+
if (value === null) {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
const record = asRecord(value);
|
|
1017
|
+
if (record === null) {
|
|
1018
|
+
return undefined;
|
|
1019
|
+
}
|
|
1020
|
+
const controllerId = readString(record['controllerId']);
|
|
1021
|
+
const controllerType = parseSessionControllerType(record['controllerType']);
|
|
1022
|
+
const controllerLabel =
|
|
1023
|
+
record['controllerLabel'] === null ? null : readString(record['controllerLabel']);
|
|
1024
|
+
const claimedAt = readString(record['claimedAt']);
|
|
1025
|
+
if (
|
|
1026
|
+
controllerId === null ||
|
|
1027
|
+
controllerType === null ||
|
|
1028
|
+
(controllerLabel === null && record['controllerLabel'] !== null) ||
|
|
1029
|
+
claimedAt === null
|
|
1030
|
+
) {
|
|
1031
|
+
return undefined;
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
controllerId,
|
|
1035
|
+
controllerType,
|
|
1036
|
+
controllerLabel,
|
|
1037
|
+
claimedAt,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function parseTelemetryStatusHint(value: unknown): StreamTelemetryStatusHint | null | undefined {
|
|
1042
|
+
if (value === undefined) {
|
|
1043
|
+
return undefined;
|
|
1044
|
+
}
|
|
1045
|
+
if (value === null) {
|
|
1046
|
+
return null;
|
|
1047
|
+
}
|
|
1048
|
+
if (value === 'running' || value === 'completed' || value === 'needs-input') {
|
|
1049
|
+
return value;
|
|
1050
|
+
}
|
|
1051
|
+
return undefined;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
function parseTelemetrySummary(value: unknown): StreamTelemetrySummary | null | undefined {
|
|
1055
|
+
if (value === undefined) {
|
|
1056
|
+
return undefined;
|
|
1057
|
+
}
|
|
1058
|
+
if (value === null) {
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
const record = asRecord(value);
|
|
1062
|
+
if (record === null) {
|
|
1063
|
+
return undefined;
|
|
1064
|
+
}
|
|
1065
|
+
const source = parseTelemetrySource(record['source']);
|
|
1066
|
+
const eventName = record['eventName'] === null ? null : readString(record['eventName']);
|
|
1067
|
+
const severity = record['severity'] === null ? null : readString(record['severity']);
|
|
1068
|
+
const summary = record['summary'] === null ? null : readString(record['summary']);
|
|
1069
|
+
const observedAt = readString(record['observedAt']);
|
|
1070
|
+
if (
|
|
1071
|
+
source === null ||
|
|
1072
|
+
(eventName === null && record['eventName'] !== null) ||
|
|
1073
|
+
(severity === null && record['severity'] !== null) ||
|
|
1074
|
+
(summary === null && record['summary'] !== null) ||
|
|
1075
|
+
observedAt === null
|
|
1076
|
+
) {
|
|
1077
|
+
return undefined;
|
|
1078
|
+
}
|
|
1079
|
+
return {
|
|
1080
|
+
source,
|
|
1081
|
+
eventName,
|
|
1082
|
+
severity,
|
|
1083
|
+
summary,
|
|
1084
|
+
observedAt,
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function parseSessionStatusModel(value: unknown): StreamSessionStatusModel | null | undefined {
|
|
1089
|
+
if (value === undefined) {
|
|
1090
|
+
return undefined;
|
|
1091
|
+
}
|
|
1092
|
+
if (value === null) {
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
const record = asRecord(value);
|
|
1096
|
+
if (record === null) {
|
|
1097
|
+
return undefined;
|
|
1098
|
+
}
|
|
1099
|
+
const runtimeStatus = readString(record['runtimeStatus']);
|
|
1100
|
+
const phase = readString(record['phase']);
|
|
1101
|
+
const glyph = readString(record['glyph']);
|
|
1102
|
+
const badge = readString(record['badge']);
|
|
1103
|
+
const detailText = readString(record['detailText']);
|
|
1104
|
+
const attentionReason =
|
|
1105
|
+
record['attentionReason'] === null ? null : readString(record['attentionReason']);
|
|
1106
|
+
const lastKnownWork =
|
|
1107
|
+
record['lastKnownWork'] === null ? null : readString(record['lastKnownWork']);
|
|
1108
|
+
const lastKnownWorkAt =
|
|
1109
|
+
record['lastKnownWorkAt'] === null ? null : readString(record['lastKnownWorkAt']);
|
|
1110
|
+
const phaseHint = record['phaseHint'] === null ? null : readString(record['phaseHint']);
|
|
1111
|
+
const observedAt = readString(record['observedAt']);
|
|
1112
|
+
if (
|
|
1113
|
+
runtimeStatus === null ||
|
|
1114
|
+
(runtimeStatus !== 'running' &&
|
|
1115
|
+
runtimeStatus !== 'needs-input' &&
|
|
1116
|
+
runtimeStatus !== 'completed' &&
|
|
1117
|
+
runtimeStatus !== 'exited') ||
|
|
1118
|
+
phase === null ||
|
|
1119
|
+
(phase !== 'needs-action' &&
|
|
1120
|
+
phase !== 'starting' &&
|
|
1121
|
+
phase !== 'working' &&
|
|
1122
|
+
phase !== 'idle' &&
|
|
1123
|
+
phase !== 'exited') ||
|
|
1124
|
+
glyph === null ||
|
|
1125
|
+
(glyph !== '▲' && glyph !== '◔' && glyph !== '◆' && glyph !== '○' && glyph !== '■') ||
|
|
1126
|
+
badge === null ||
|
|
1127
|
+
(badge !== 'NEED' && badge !== 'RUN ' && badge !== 'DONE' && badge !== 'EXIT') ||
|
|
1128
|
+
detailText === null ||
|
|
1129
|
+
(attentionReason === null && record['attentionReason'] !== null) ||
|
|
1130
|
+
(lastKnownWork === null && record['lastKnownWork'] !== null) ||
|
|
1131
|
+
(lastKnownWorkAt === null && record['lastKnownWorkAt'] !== null) ||
|
|
1132
|
+
(phaseHint === null && record['phaseHint'] !== null) ||
|
|
1133
|
+
(phaseHint !== null &&
|
|
1134
|
+
phaseHint !== 'needs-action' &&
|
|
1135
|
+
phaseHint !== 'working' &&
|
|
1136
|
+
phaseHint !== 'idle') ||
|
|
1137
|
+
observedAt === null
|
|
1138
|
+
) {
|
|
1139
|
+
return undefined;
|
|
1140
|
+
}
|
|
1141
|
+
return {
|
|
1142
|
+
runtimeStatus,
|
|
1143
|
+
phase,
|
|
1144
|
+
glyph,
|
|
1145
|
+
badge,
|
|
1146
|
+
detailText,
|
|
1147
|
+
attentionReason,
|
|
1148
|
+
lastKnownWork,
|
|
1149
|
+
lastKnownWorkAt,
|
|
1150
|
+
phaseHint,
|
|
1151
|
+
observedAt,
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
function parseSessionKeyEventRecord(value: unknown): StreamSessionKeyEventRecord | null {
|
|
1156
|
+
const record = asRecord(value);
|
|
1157
|
+
if (record === null) {
|
|
1158
|
+
return null;
|
|
1159
|
+
}
|
|
1160
|
+
const source = parseTelemetrySource(record['source']);
|
|
1161
|
+
const eventName = record['eventName'] === null ? null : readString(record['eventName']);
|
|
1162
|
+
const severity = record['severity'] === null ? null : readString(record['severity']);
|
|
1163
|
+
const summary = record['summary'] === null ? null : readString(record['summary']);
|
|
1164
|
+
const observedAt = readString(record['observedAt']);
|
|
1165
|
+
const statusHint = parseTelemetryStatusHint(record['statusHint']);
|
|
1166
|
+
if (
|
|
1167
|
+
source === null ||
|
|
1168
|
+
(eventName === null && record['eventName'] !== null) ||
|
|
1169
|
+
(severity === null && record['severity'] !== null) ||
|
|
1170
|
+
(summary === null && record['summary'] !== null) ||
|
|
1171
|
+
observedAt === null ||
|
|
1172
|
+
statusHint === undefined
|
|
1173
|
+
) {
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
return {
|
|
1177
|
+
source,
|
|
1178
|
+
eventName,
|
|
1179
|
+
severity,
|
|
1180
|
+
summary,
|
|
1181
|
+
observedAt,
|
|
1182
|
+
statusHint,
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function parseStreamObservedEvent(value: unknown): StreamObservedEvent | null {
|
|
1187
|
+
const record = asRecord(value);
|
|
1188
|
+
if (record === null) {
|
|
1189
|
+
return null;
|
|
1190
|
+
}
|
|
1191
|
+
const type = readString(record['type']);
|
|
1192
|
+
if (type === null) {
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
if (type === 'directory-upserted') {
|
|
1197
|
+
const directory = asRecord(record['directory']);
|
|
1198
|
+
if (directory === null) {
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
return {
|
|
1202
|
+
type,
|
|
1203
|
+
directory,
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
if (type === 'directory-archived') {
|
|
1208
|
+
const directoryId = readString(record['directoryId']);
|
|
1209
|
+
const ts = readString(record['ts']);
|
|
1210
|
+
if (directoryId === null || ts === null) {
|
|
1211
|
+
return null;
|
|
1212
|
+
}
|
|
1213
|
+
return {
|
|
1214
|
+
type,
|
|
1215
|
+
directoryId,
|
|
1216
|
+
ts,
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
if (type === 'directory-git-updated') {
|
|
1221
|
+
const directoryId = readString(record['directoryId']);
|
|
1222
|
+
const summaryRecord = asRecord(record['summary']);
|
|
1223
|
+
const repositorySnapshotRecord = asRecord(record['repositorySnapshot']);
|
|
1224
|
+
const repositoryId =
|
|
1225
|
+
record['repositoryId'] === null ? null : readString(record['repositoryId']);
|
|
1226
|
+
const repository = record['repository'] === null ? null : asRecord(record['repository']);
|
|
1227
|
+
const observedAt = readString(record['observedAt']);
|
|
1228
|
+
if (
|
|
1229
|
+
directoryId === null ||
|
|
1230
|
+
summaryRecord === null ||
|
|
1231
|
+
repositorySnapshotRecord === null ||
|
|
1232
|
+
(repositoryId === null && record['repositoryId'] !== null) ||
|
|
1233
|
+
(repository === null && record['repository'] !== null) ||
|
|
1234
|
+
observedAt === null
|
|
1235
|
+
) {
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
const branch = readString(summaryRecord['branch']);
|
|
1239
|
+
const changedFiles = readNumber(summaryRecord['changedFiles']);
|
|
1240
|
+
const additions = readNumber(summaryRecord['additions']);
|
|
1241
|
+
const deletions = readNumber(summaryRecord['deletions']);
|
|
1242
|
+
if (branch === null || changedFiles === null || additions === null || deletions === null) {
|
|
1243
|
+
return null;
|
|
1244
|
+
}
|
|
1245
|
+
const normalizedRemoteUrl =
|
|
1246
|
+
repositorySnapshotRecord['normalizedRemoteUrl'] === null
|
|
1247
|
+
? null
|
|
1248
|
+
: readString(repositorySnapshotRecord['normalizedRemoteUrl']);
|
|
1249
|
+
const commitCount =
|
|
1250
|
+
repositorySnapshotRecord['commitCount'] === null
|
|
1251
|
+
? null
|
|
1252
|
+
: readNumber(repositorySnapshotRecord['commitCount']);
|
|
1253
|
+
const lastCommitAt =
|
|
1254
|
+
repositorySnapshotRecord['lastCommitAt'] === null
|
|
1255
|
+
? null
|
|
1256
|
+
: readString(repositorySnapshotRecord['lastCommitAt']);
|
|
1257
|
+
const shortCommitHash =
|
|
1258
|
+
repositorySnapshotRecord['shortCommitHash'] === null
|
|
1259
|
+
? null
|
|
1260
|
+
: readString(repositorySnapshotRecord['shortCommitHash']);
|
|
1261
|
+
const inferredName =
|
|
1262
|
+
repositorySnapshotRecord['inferredName'] === null
|
|
1263
|
+
? null
|
|
1264
|
+
: readString(repositorySnapshotRecord['inferredName']);
|
|
1265
|
+
const defaultBranch =
|
|
1266
|
+
repositorySnapshotRecord['defaultBranch'] === null
|
|
1267
|
+
? null
|
|
1268
|
+
: readString(repositorySnapshotRecord['defaultBranch']);
|
|
1269
|
+
if (
|
|
1270
|
+
(normalizedRemoteUrl === null && repositorySnapshotRecord['normalizedRemoteUrl'] !== null) ||
|
|
1271
|
+
(commitCount === null && repositorySnapshotRecord['commitCount'] !== null) ||
|
|
1272
|
+
(lastCommitAt === null && repositorySnapshotRecord['lastCommitAt'] !== null) ||
|
|
1273
|
+
(shortCommitHash === null && repositorySnapshotRecord['shortCommitHash'] !== null) ||
|
|
1274
|
+
(inferredName === null && repositorySnapshotRecord['inferredName'] !== null) ||
|
|
1275
|
+
(defaultBranch === null && repositorySnapshotRecord['defaultBranch'] !== null)
|
|
1276
|
+
) {
|
|
1277
|
+
return null;
|
|
1278
|
+
}
|
|
1279
|
+
return {
|
|
1280
|
+
type,
|
|
1281
|
+
directoryId,
|
|
1282
|
+
summary: {
|
|
1283
|
+
branch,
|
|
1284
|
+
changedFiles,
|
|
1285
|
+
additions,
|
|
1286
|
+
deletions,
|
|
1287
|
+
},
|
|
1288
|
+
repositorySnapshot: {
|
|
1289
|
+
normalizedRemoteUrl,
|
|
1290
|
+
commitCount,
|
|
1291
|
+
lastCommitAt,
|
|
1292
|
+
shortCommitHash,
|
|
1293
|
+
inferredName,
|
|
1294
|
+
defaultBranch,
|
|
1295
|
+
},
|
|
1296
|
+
repositoryId,
|
|
1297
|
+
repository,
|
|
1298
|
+
observedAt,
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if (type === 'conversation-created') {
|
|
1303
|
+
const conversation = asRecord(record['conversation']);
|
|
1304
|
+
if (conversation === null) {
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
return {
|
|
1308
|
+
type,
|
|
1309
|
+
conversation,
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
if (type === 'conversation-updated') {
|
|
1314
|
+
const conversation = asRecord(record['conversation']);
|
|
1315
|
+
if (conversation === null) {
|
|
1316
|
+
return null;
|
|
1317
|
+
}
|
|
1318
|
+
return {
|
|
1319
|
+
type,
|
|
1320
|
+
conversation,
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
if (type === 'conversation-archived') {
|
|
1325
|
+
const conversationId = readString(record['conversationId']);
|
|
1326
|
+
const ts = readString(record['ts']);
|
|
1327
|
+
if (conversationId === null || ts === null) {
|
|
1328
|
+
return null;
|
|
1329
|
+
}
|
|
1330
|
+
return {
|
|
1331
|
+
type,
|
|
1332
|
+
conversationId,
|
|
1333
|
+
ts,
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
if (type === 'conversation-deleted') {
|
|
1338
|
+
const conversationId = readString(record['conversationId']);
|
|
1339
|
+
const ts = readString(record['ts']);
|
|
1340
|
+
if (conversationId === null || ts === null) {
|
|
1341
|
+
return null;
|
|
1342
|
+
}
|
|
1343
|
+
return {
|
|
1344
|
+
type,
|
|
1345
|
+
conversationId,
|
|
1346
|
+
ts,
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
if (type === 'repository-upserted') {
|
|
1351
|
+
const repository = asRecord(record['repository']);
|
|
1352
|
+
if (repository === null) {
|
|
1353
|
+
return null;
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
type,
|
|
1357
|
+
repository,
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
if (type === 'repository-updated') {
|
|
1362
|
+
const repository = asRecord(record['repository']);
|
|
1363
|
+
if (repository === null) {
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
return {
|
|
1367
|
+
type,
|
|
1368
|
+
repository,
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
if (type === 'repository-archived') {
|
|
1373
|
+
const repositoryId = readString(record['repositoryId']);
|
|
1374
|
+
const ts = readString(record['ts']);
|
|
1375
|
+
if (repositoryId === null || ts === null) {
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
return {
|
|
1379
|
+
type,
|
|
1380
|
+
repositoryId,
|
|
1381
|
+
ts,
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
if (type === 'task-created') {
|
|
1386
|
+
const task = asRecord(record['task']);
|
|
1387
|
+
if (task === null) {
|
|
1388
|
+
return null;
|
|
1389
|
+
}
|
|
1390
|
+
return {
|
|
1391
|
+
type,
|
|
1392
|
+
task,
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
if (type === 'task-updated') {
|
|
1397
|
+
const task = asRecord(record['task']);
|
|
1398
|
+
if (task === null) {
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
return {
|
|
1402
|
+
type,
|
|
1403
|
+
task,
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
if (type === 'task-deleted') {
|
|
1408
|
+
const taskId = readString(record['taskId']);
|
|
1409
|
+
const ts = readString(record['ts']);
|
|
1410
|
+
if (taskId === null || ts === null) {
|
|
1411
|
+
return null;
|
|
1412
|
+
}
|
|
1413
|
+
return {
|
|
1414
|
+
type,
|
|
1415
|
+
taskId,
|
|
1416
|
+
ts,
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
if (type === 'task-reordered') {
|
|
1421
|
+
const tasksValue = record['tasks'];
|
|
1422
|
+
if (!Array.isArray(tasksValue)) {
|
|
1423
|
+
return null;
|
|
1424
|
+
}
|
|
1425
|
+
const tasks: Record<string, unknown>[] = [];
|
|
1426
|
+
for (const entry of tasksValue) {
|
|
1427
|
+
const normalized = asRecord(entry);
|
|
1428
|
+
if (normalized === null) {
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
tasks.push(normalized);
|
|
1432
|
+
}
|
|
1433
|
+
const ts = readString(record['ts']);
|
|
1434
|
+
if (ts === null) {
|
|
1435
|
+
return null;
|
|
1436
|
+
}
|
|
1437
|
+
return {
|
|
1438
|
+
type,
|
|
1439
|
+
tasks,
|
|
1440
|
+
ts,
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
if (type === 'github-pr-upserted') {
|
|
1445
|
+
const pr = asRecord(record['pr']);
|
|
1446
|
+
if (pr === null) {
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
return {
|
|
1450
|
+
type,
|
|
1451
|
+
pr,
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
if (type === 'github-pr-closed') {
|
|
1456
|
+
const prRecordId = readString(record['prRecordId']);
|
|
1457
|
+
const repositoryId = readString(record['repositoryId']);
|
|
1458
|
+
const ts = readString(record['ts']);
|
|
1459
|
+
if (prRecordId === null || repositoryId === null || ts === null) {
|
|
1460
|
+
return null;
|
|
1461
|
+
}
|
|
1462
|
+
return {
|
|
1463
|
+
type,
|
|
1464
|
+
prRecordId,
|
|
1465
|
+
repositoryId,
|
|
1466
|
+
ts,
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
if (type === 'github-pr-jobs-updated') {
|
|
1471
|
+
const prRecordId = readString(record['prRecordId']);
|
|
1472
|
+
const repositoryId = readString(record['repositoryId']);
|
|
1473
|
+
const ciRollup = readString(record['ciRollup']);
|
|
1474
|
+
const jobsRaw = record['jobs'];
|
|
1475
|
+
const ts = readString(record['ts']);
|
|
1476
|
+
if (
|
|
1477
|
+
prRecordId === null ||
|
|
1478
|
+
repositoryId === null ||
|
|
1479
|
+
(ciRollup !== 'pending' &&
|
|
1480
|
+
ciRollup !== 'success' &&
|
|
1481
|
+
ciRollup !== 'failure' &&
|
|
1482
|
+
ciRollup !== 'cancelled' &&
|
|
1483
|
+
ciRollup !== 'neutral' &&
|
|
1484
|
+
ciRollup !== 'none') ||
|
|
1485
|
+
!Array.isArray(jobsRaw) ||
|
|
1486
|
+
ts === null
|
|
1487
|
+
) {
|
|
1488
|
+
return null;
|
|
1489
|
+
}
|
|
1490
|
+
const jobs: Record<string, unknown>[] = [];
|
|
1491
|
+
for (const entry of jobsRaw) {
|
|
1492
|
+
const job = asRecord(entry);
|
|
1493
|
+
if (job === null) {
|
|
1494
|
+
return null;
|
|
1495
|
+
}
|
|
1496
|
+
jobs.push(job);
|
|
1497
|
+
}
|
|
1498
|
+
return {
|
|
1499
|
+
type,
|
|
1500
|
+
prRecordId,
|
|
1501
|
+
repositoryId,
|
|
1502
|
+
ciRollup,
|
|
1503
|
+
jobs,
|
|
1504
|
+
ts,
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
if (type === 'session-status') {
|
|
1509
|
+
const sessionId = readString(record['sessionId']);
|
|
1510
|
+
const status = readString(record['status']);
|
|
1511
|
+
const attentionReason = readString(record['attentionReason']);
|
|
1512
|
+
const statusModel = parseSessionStatusModel(record['statusModel']);
|
|
1513
|
+
const live = readBoolean(record['live']);
|
|
1514
|
+
const ts = readString(record['ts']);
|
|
1515
|
+
const directoryId = readString(record['directoryId']);
|
|
1516
|
+
const conversationId = readString(record['conversationId']);
|
|
1517
|
+
const telemetry = parseTelemetrySummary(record['telemetry']);
|
|
1518
|
+
const controller = parseSessionController(record['controller']);
|
|
1519
|
+
if (
|
|
1520
|
+
sessionId === null ||
|
|
1521
|
+
status === null ||
|
|
1522
|
+
statusModel === undefined ||
|
|
1523
|
+
live === null ||
|
|
1524
|
+
ts === null ||
|
|
1525
|
+
(record['attentionReason'] !== null && attentionReason === null) ||
|
|
1526
|
+
(record['directoryId'] !== null && directoryId === null) ||
|
|
1527
|
+
(record['conversationId'] !== null && conversationId === null) ||
|
|
1528
|
+
(record['telemetry'] !== undefined && telemetry === undefined) ||
|
|
1529
|
+
(record['controller'] !== undefined && controller === undefined)
|
|
1530
|
+
) {
|
|
1531
|
+
return null;
|
|
1532
|
+
}
|
|
1533
|
+
if (
|
|
1534
|
+
status !== 'running' &&
|
|
1535
|
+
status !== 'needs-input' &&
|
|
1536
|
+
status !== 'completed' &&
|
|
1537
|
+
status !== 'exited'
|
|
1538
|
+
) {
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
return {
|
|
1542
|
+
type,
|
|
1543
|
+
sessionId,
|
|
1544
|
+
status,
|
|
1545
|
+
attentionReason: record['attentionReason'] === null ? null : attentionReason,
|
|
1546
|
+
statusModel,
|
|
1547
|
+
live,
|
|
1548
|
+
ts,
|
|
1549
|
+
directoryId: record['directoryId'] === null ? null : directoryId,
|
|
1550
|
+
conversationId: record['conversationId'] === null ? null : conversationId,
|
|
1551
|
+
telemetry: telemetry ?? null,
|
|
1552
|
+
controller: controller ?? null,
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
if (type === 'session-event') {
|
|
1557
|
+
const sessionId = readString(record['sessionId']);
|
|
1558
|
+
const event = parseStreamSessionEvent(record['event']);
|
|
1559
|
+
const ts = readString(record['ts']);
|
|
1560
|
+
const directoryId = readString(record['directoryId']);
|
|
1561
|
+
const conversationId = readString(record['conversationId']);
|
|
1562
|
+
if (
|
|
1563
|
+
sessionId === null ||
|
|
1564
|
+
event === null ||
|
|
1565
|
+
ts === null ||
|
|
1566
|
+
(record['directoryId'] !== null && directoryId === null) ||
|
|
1567
|
+
(record['conversationId'] !== null && conversationId === null)
|
|
1568
|
+
) {
|
|
1569
|
+
return null;
|
|
1570
|
+
}
|
|
1571
|
+
return {
|
|
1572
|
+
type,
|
|
1573
|
+
sessionId,
|
|
1574
|
+
event,
|
|
1575
|
+
ts,
|
|
1576
|
+
directoryId: record['directoryId'] === null ? null : directoryId,
|
|
1577
|
+
conversationId: record['conversationId'] === null ? null : conversationId,
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
if (type === 'session-key-event') {
|
|
1582
|
+
const sessionId = readString(record['sessionId']);
|
|
1583
|
+
const keyEvent = parseSessionKeyEventRecord(record['keyEvent']);
|
|
1584
|
+
const ts = readString(record['ts']);
|
|
1585
|
+
const directoryId = readString(record['directoryId']);
|
|
1586
|
+
const conversationId = readString(record['conversationId']);
|
|
1587
|
+
if (
|
|
1588
|
+
sessionId === null ||
|
|
1589
|
+
keyEvent === null ||
|
|
1590
|
+
ts === null ||
|
|
1591
|
+
(record['directoryId'] !== null && directoryId === null) ||
|
|
1592
|
+
(record['conversationId'] !== null && conversationId === null)
|
|
1593
|
+
) {
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
type,
|
|
1598
|
+
sessionId,
|
|
1599
|
+
keyEvent,
|
|
1600
|
+
ts,
|
|
1601
|
+
directoryId: record['directoryId'] === null ? null : directoryId,
|
|
1602
|
+
conversationId: record['conversationId'] === null ? null : conversationId,
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
if (type === 'session-control') {
|
|
1607
|
+
const sessionId = readString(record['sessionId']);
|
|
1608
|
+
const action = readString(record['action']);
|
|
1609
|
+
const controller = parseSessionController(record['controller']);
|
|
1610
|
+
const previousController = parseSessionController(record['previousController']);
|
|
1611
|
+
const reason = record['reason'] === null ? null : readString(record['reason']);
|
|
1612
|
+
const ts = readString(record['ts']);
|
|
1613
|
+
const directoryId = readString(record['directoryId']);
|
|
1614
|
+
const conversationId = readString(record['conversationId']);
|
|
1615
|
+
if (
|
|
1616
|
+
sessionId === null ||
|
|
1617
|
+
(action !== 'claimed' && action !== 'released' && action !== 'taken-over') ||
|
|
1618
|
+
controller === undefined ||
|
|
1619
|
+
previousController === undefined ||
|
|
1620
|
+
(reason === null && record['reason'] !== null) ||
|
|
1621
|
+
ts === null ||
|
|
1622
|
+
(record['directoryId'] !== null && directoryId === null) ||
|
|
1623
|
+
(record['conversationId'] !== null && conversationId === null)
|
|
1624
|
+
) {
|
|
1625
|
+
return null;
|
|
1626
|
+
}
|
|
1627
|
+
return {
|
|
1628
|
+
type,
|
|
1629
|
+
sessionId,
|
|
1630
|
+
action,
|
|
1631
|
+
controller,
|
|
1632
|
+
previousController,
|
|
1633
|
+
reason,
|
|
1634
|
+
ts,
|
|
1635
|
+
directoryId: record['directoryId'] === null ? null : directoryId,
|
|
1636
|
+
conversationId: record['conversationId'] === null ? null : conversationId,
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
if (type === 'session-output') {
|
|
1641
|
+
const sessionId = readString(record['sessionId']);
|
|
1642
|
+
const outputCursor = readNumber(record['outputCursor']);
|
|
1643
|
+
const chunkBase64 = readString(record['chunkBase64']);
|
|
1644
|
+
const ts = readString(record['ts']);
|
|
1645
|
+
const directoryId = readString(record['directoryId']);
|
|
1646
|
+
const conversationId = readString(record['conversationId']);
|
|
1647
|
+
if (
|
|
1648
|
+
sessionId === null ||
|
|
1649
|
+
outputCursor === null ||
|
|
1650
|
+
chunkBase64 === null ||
|
|
1651
|
+
ts === null ||
|
|
1652
|
+
(record['directoryId'] !== null && directoryId === null) ||
|
|
1653
|
+
(record['conversationId'] !== null && conversationId === null)
|
|
1654
|
+
) {
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
return {
|
|
1658
|
+
type,
|
|
1659
|
+
sessionId,
|
|
1660
|
+
outputCursor,
|
|
1661
|
+
chunkBase64,
|
|
1662
|
+
ts,
|
|
1663
|
+
directoryId: record['directoryId'] === null ? null : directoryId,
|
|
1664
|
+
conversationId: record['conversationId'] === null ? null : conversationId,
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
return null;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
export function parseServerEnvelope(value: unknown): StreamServerEnvelope | null {
|
|
1672
|
+
const record = asRecord(value);
|
|
1673
|
+
if (record === null) {
|
|
1674
|
+
return null;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
const kind = readString(record['kind']);
|
|
1678
|
+
if (kind === null) {
|
|
1679
|
+
return null;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
if (kind === 'auth.ok') {
|
|
1683
|
+
return {
|
|
1684
|
+
kind,
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
if (kind === 'auth.error') {
|
|
1689
|
+
const error = readString(record['error']);
|
|
1690
|
+
if (error === null) {
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1693
|
+
return {
|
|
1694
|
+
kind,
|
|
1695
|
+
error,
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
if (kind === 'command.accepted') {
|
|
1700
|
+
const commandId = readString(record['commandId']);
|
|
1701
|
+
if (commandId === null) {
|
|
1702
|
+
return null;
|
|
1703
|
+
}
|
|
1704
|
+
return {
|
|
1705
|
+
kind,
|
|
1706
|
+
commandId,
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
if (kind === 'command.completed') {
|
|
1711
|
+
const commandId = readString(record['commandId']);
|
|
1712
|
+
const result = asRecord(record['result']);
|
|
1713
|
+
if (commandId === null || result === null) {
|
|
1714
|
+
return null;
|
|
1715
|
+
}
|
|
1716
|
+
return {
|
|
1717
|
+
kind,
|
|
1718
|
+
commandId,
|
|
1719
|
+
result,
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
if (kind === 'command.failed') {
|
|
1724
|
+
const commandId = readString(record['commandId']);
|
|
1725
|
+
const error = readString(record['error']);
|
|
1726
|
+
if (commandId === null || error === null) {
|
|
1727
|
+
return null;
|
|
1728
|
+
}
|
|
1729
|
+
return {
|
|
1730
|
+
kind,
|
|
1731
|
+
commandId,
|
|
1732
|
+
error,
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
if (kind === 'stream.event') {
|
|
1737
|
+
const subscriptionId = readString(record['subscriptionId']);
|
|
1738
|
+
const cursor = readNumber(record['cursor']);
|
|
1739
|
+
const event = parseStreamObservedEvent(record['event']);
|
|
1740
|
+
if (subscriptionId === null || cursor === null || event === null) {
|
|
1741
|
+
return null;
|
|
1742
|
+
}
|
|
1743
|
+
return {
|
|
1744
|
+
kind,
|
|
1745
|
+
subscriptionId,
|
|
1746
|
+
cursor,
|
|
1747
|
+
event,
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
const sessionId = readString(record['sessionId']);
|
|
1752
|
+
if (sessionId === null) {
|
|
1753
|
+
return null;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
if (kind === 'pty.output') {
|
|
1757
|
+
const cursor = readNumber(record['cursor']);
|
|
1758
|
+
const chunkBase64 = readString(record['chunkBase64']);
|
|
1759
|
+
if (cursor === null || chunkBase64 === null) {
|
|
1760
|
+
return null;
|
|
1761
|
+
}
|
|
1762
|
+
return {
|
|
1763
|
+
kind,
|
|
1764
|
+
sessionId,
|
|
1765
|
+
cursor,
|
|
1766
|
+
chunkBase64,
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
if (kind === 'pty.exit') {
|
|
1771
|
+
const exitRecord = asRecord(record['exit']);
|
|
1772
|
+
if (exitRecord === null) {
|
|
1773
|
+
return null;
|
|
1774
|
+
}
|
|
1775
|
+
const code = exitRecord['code'];
|
|
1776
|
+
const signal = exitRecord['signal'];
|
|
1777
|
+
const normalizedCode = code === null ? null : readNumber(code);
|
|
1778
|
+
const normalizedSignal = signal === null ? null : readSignalName(signal);
|
|
1779
|
+
if (normalizedCode === null && code !== null) {
|
|
1780
|
+
return null;
|
|
1781
|
+
}
|
|
1782
|
+
if (normalizedSignal === null && signal !== null) {
|
|
1783
|
+
return null;
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
kind,
|
|
1787
|
+
sessionId,
|
|
1788
|
+
exit: {
|
|
1789
|
+
code: normalizedCode,
|
|
1790
|
+
signal: normalizedSignal,
|
|
1791
|
+
},
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
if (kind === 'pty.event') {
|
|
1796
|
+
const event = parseStreamSessionEvent(record['event']);
|
|
1797
|
+
if (event === null) {
|
|
1798
|
+
return null;
|
|
1799
|
+
}
|
|
1800
|
+
return {
|
|
1801
|
+
kind,
|
|
1802
|
+
sessionId,
|
|
1803
|
+
event,
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
return null;
|
|
1808
|
+
}
|