@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.
Files changed (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +145 -0
  3. package/native/ptyd/Cargo.lock +16 -0
  4. package/native/ptyd/Cargo.toml +7 -0
  5. package/native/ptyd/src/main.rs +257 -0
  6. package/package.json +90 -0
  7. package/scripts/build-ptyd.sh +73 -0
  8. package/scripts/control-plane-daemon.ts +277 -0
  9. package/scripts/cursor-hook-relay.ts +82 -0
  10. package/scripts/harness-animate.ts +469 -0
  11. package/scripts/harness-bin.js +77 -0
  12. package/scripts/harness-core.ts +1 -0
  13. package/scripts/harness-inspector.ts +439 -0
  14. package/scripts/harness.ts +2493 -0
  15. package/src/adapters/agent-session-state.ts +390 -0
  16. package/src/cli/gateway-record.ts +173 -0
  17. package/src/codex/live-session.ts +872 -0
  18. package/src/config/config-core.ts +1359 -0
  19. package/src/config/secrets-core.ts +170 -0
  20. package/src/control-plane/agent-realtime-api.ts +2441 -0
  21. package/src/control-plane/codex-session-stream.ts +392 -0
  22. package/src/control-plane/codex-telemetry.ts +1325 -0
  23. package/src/control-plane/lifecycle-hooks.ts +706 -0
  24. package/src/control-plane/session-summary.ts +380 -0
  25. package/src/control-plane/status/agent-status-reducer.ts +21 -0
  26. package/src/control-plane/status/reducer-base.ts +170 -0
  27. package/src/control-plane/status/reducers/claude-status-reducer.ts +37 -0
  28. package/src/control-plane/status/reducers/codex-status-reducer.ts +48 -0
  29. package/src/control-plane/status/reducers/critique-status-reducer.ts +15 -0
  30. package/src/control-plane/status/reducers/cursor-status-reducer.ts +37 -0
  31. package/src/control-plane/status/reducers/terminal-status-reducer.ts +15 -0
  32. package/src/control-plane/status/session-status-engine.ts +76 -0
  33. package/src/control-plane/stream-client.ts +396 -0
  34. package/src/control-plane/stream-command-parser.ts +1673 -0
  35. package/src/control-plane/stream-protocol.ts +1808 -0
  36. package/src/control-plane/stream-server-background.ts +486 -0
  37. package/src/control-plane/stream-server-command.ts +2557 -0
  38. package/src/control-plane/stream-server-connection.ts +234 -0
  39. package/src/control-plane/stream-server-observed-filter.ts +112 -0
  40. package/src/control-plane/stream-server-session-runtime.ts +566 -0
  41. package/src/control-plane/stream-server-state-store.ts +15 -0
  42. package/src/control-plane/stream-server.ts +3192 -0
  43. package/src/cursor/managed-hooks.ts +282 -0
  44. package/src/domain/conversations.ts +414 -0
  45. package/src/domain/directories.ts +78 -0
  46. package/src/domain/repositories.ts +123 -0
  47. package/src/domain/tasks.ts +148 -0
  48. package/src/domain/workspace.ts +156 -0
  49. package/src/events/normalized-events.ts +124 -0
  50. package/src/mux/ansi-integrity.ts +103 -0
  51. package/src/mux/control-plane-op-queue.ts +212 -0
  52. package/src/mux/conversation-rail.ts +339 -0
  53. package/src/mux/double-click.ts +78 -0
  54. package/src/mux/dual-pane-core.ts +435 -0
  55. package/src/mux/harness-core-ui.ts +817 -0
  56. package/src/mux/input-shortcuts.ts +667 -0
  57. package/src/mux/live-mux/actions-conversation.ts +344 -0
  58. package/src/mux/live-mux/actions-repository.ts +246 -0
  59. package/src/mux/live-mux/actions-task.ts +115 -0
  60. package/src/mux/live-mux/args.ts +142 -0
  61. package/src/mux/live-mux/command-menu.ts +298 -0
  62. package/src/mux/live-mux/control-plane-records.ts +546 -0
  63. package/src/mux/live-mux/conversation-state.ts +188 -0
  64. package/src/mux/live-mux/directory-resolution.ts +34 -0
  65. package/src/mux/live-mux/event-mapping.ts +96 -0
  66. package/src/mux/live-mux/gateway-profiler.ts +152 -0
  67. package/src/mux/live-mux/gateway-render-trace.ts +177 -0
  68. package/src/mux/live-mux/gateway-status-timeline.ts +166 -0
  69. package/src/mux/live-mux/git-parsing.ts +131 -0
  70. package/src/mux/live-mux/git-snapshot.ts +263 -0
  71. package/src/mux/live-mux/git-state.ts +136 -0
  72. package/src/mux/live-mux/global-shortcut-handlers.ts +143 -0
  73. package/src/mux/live-mux/home-pane-actions.ts +58 -0
  74. package/src/mux/live-mux/home-pane-drop.ts +44 -0
  75. package/src/mux/live-mux/home-pane-entity-click.ts +96 -0
  76. package/src/mux/live-mux/home-pane-pointer.ts +96 -0
  77. package/src/mux/live-mux/input-forwarding.ts +112 -0
  78. package/src/mux/live-mux/layout.ts +30 -0
  79. package/src/mux/live-mux/left-nav-activation.ts +103 -0
  80. package/src/mux/live-mux/left-nav.ts +85 -0
  81. package/src/mux/live-mux/left-rail-actions.ts +118 -0
  82. package/src/mux/live-mux/left-rail-conversation-click.ts +82 -0
  83. package/src/mux/live-mux/left-rail-pointer.ts +74 -0
  84. package/src/mux/live-mux/modal-command-menu-handler.ts +101 -0
  85. package/src/mux/live-mux/modal-conversation-handlers.ts +217 -0
  86. package/src/mux/live-mux/modal-input-reducers.ts +94 -0
  87. package/src/mux/live-mux/modal-overlays.ts +287 -0
  88. package/src/mux/live-mux/modal-pointer.ts +70 -0
  89. package/src/mux/live-mux/modal-prompt-handlers.ts +187 -0
  90. package/src/mux/live-mux/modal-task-editor-handler.ts +156 -0
  91. package/src/mux/live-mux/observed-stream.ts +87 -0
  92. package/src/mux/live-mux/palette-parsing.ts +128 -0
  93. package/src/mux/live-mux/pointer-routing.ts +108 -0
  94. package/src/mux/live-mux/process-usage.ts +53 -0
  95. package/src/mux/live-mux/project-pane-pointer.ts +44 -0
  96. package/src/mux/live-mux/rail-layout.ts +244 -0
  97. package/src/mux/live-mux/render-trace-analysis.ts +213 -0
  98. package/src/mux/live-mux/render-trace-state.ts +84 -0
  99. package/src/mux/live-mux/repository-folding.ts +207 -0
  100. package/src/mux/live-mux/runtime-shutdown.ts +51 -0
  101. package/src/mux/live-mux/selection.ts +411 -0
  102. package/src/mux/live-mux/startup-utils.ts +187 -0
  103. package/src/mux/live-mux/status-timeline-state.ts +82 -0
  104. package/src/mux/live-mux/task-pane-shortcuts.ts +206 -0
  105. package/src/mux/live-mux/terminal-palette.ts +79 -0
  106. package/src/mux/new-thread-prompt.ts +165 -0
  107. package/src/mux/project-tree.ts +295 -0
  108. package/src/mux/render-frame.ts +113 -0
  109. package/src/mux/runtime-wiring.ts +185 -0
  110. package/src/mux/selector-index.ts +160 -0
  111. package/src/mux/startup-sequencer.ts +238 -0
  112. package/src/mux/task-composer.ts +289 -0
  113. package/src/mux/task-focused-pane.ts +417 -0
  114. package/src/mux/task-screen-keybindings.ts +539 -0
  115. package/src/mux/terminal-input-modes.ts +35 -0
  116. package/src/mux/workspace-path.ts +55 -0
  117. package/src/mux/workspace-rail-model.ts +701 -0
  118. package/src/mux/workspace-rail.ts +247 -0
  119. package/src/perf/perf-core.ts +307 -0
  120. package/src/pty/pty_host.ts +217 -0
  121. package/src/pty/session-broker.ts +158 -0
  122. package/src/recording/terminal-recording.ts +383 -0
  123. package/src/services/control-plane.ts +567 -0
  124. package/src/services/conversation-lifecycle.ts +176 -0
  125. package/src/services/conversation-startup-hydration.ts +47 -0
  126. package/src/services/directory-hydration.ts +49 -0
  127. package/src/services/event-persistence.ts +104 -0
  128. package/src/services/mux-ui-state-persistence.ts +82 -0
  129. package/src/services/output-load-sampler.ts +231 -0
  130. package/src/services/process-usage-refresh.ts +88 -0
  131. package/src/services/recording.ts +75 -0
  132. package/src/services/render-trace-recorder.ts +177 -0
  133. package/src/services/runtime-control-actions.ts +123 -0
  134. package/src/services/runtime-control-plane-ops.ts +131 -0
  135. package/src/services/runtime-conversation-actions.ts +113 -0
  136. package/src/services/runtime-conversation-activation.ts +78 -0
  137. package/src/services/runtime-conversation-starter.ts +171 -0
  138. package/src/services/runtime-conversation-title-edit.ts +149 -0
  139. package/src/services/runtime-directory-actions.ts +164 -0
  140. package/src/services/runtime-envelope-handler.ts +198 -0
  141. package/src/services/runtime-git-state.ts +92 -0
  142. package/src/services/runtime-input-pipeline.ts +50 -0
  143. package/src/services/runtime-input-router.ts +202 -0
  144. package/src/services/runtime-layout-resize.ts +236 -0
  145. package/src/services/runtime-left-rail-render.ts +159 -0
  146. package/src/services/runtime-main-pane-input.ts +230 -0
  147. package/src/services/runtime-modal-input.ts +119 -0
  148. package/src/services/runtime-navigation-input.ts +207 -0
  149. package/src/services/runtime-process-wiring.ts +68 -0
  150. package/src/services/runtime-rail-input.ts +287 -0
  151. package/src/services/runtime-render-flush.ts +146 -0
  152. package/src/services/runtime-render-lifecycle.ts +104 -0
  153. package/src/services/runtime-render-orchestrator.ts +108 -0
  154. package/src/services/runtime-render-pipeline.ts +167 -0
  155. package/src/services/runtime-render-state.ts +72 -0
  156. package/src/services/runtime-repository-actions.ts +197 -0
  157. package/src/services/runtime-right-pane-render.ts +132 -0
  158. package/src/services/runtime-shutdown.ts +79 -0
  159. package/src/services/runtime-stream-subscriptions.ts +56 -0
  160. package/src/services/runtime-task-composer-persistence.ts +139 -0
  161. package/src/services/runtime-task-editor-actions.ts +83 -0
  162. package/src/services/runtime-task-pane-actions.ts +198 -0
  163. package/src/services/runtime-task-pane-shortcuts.ts +189 -0
  164. package/src/services/runtime-task-pane.ts +62 -0
  165. package/src/services/runtime-workspace-actions.ts +153 -0
  166. package/src/services/runtime-workspace-observed-events.ts +190 -0
  167. package/src/services/session-projection-instrumentation.ts +190 -0
  168. package/src/services/startup-background-probe.ts +91 -0
  169. package/src/services/startup-background-resume.ts +65 -0
  170. package/src/services/startup-orchestrator.ts +166 -0
  171. package/src/services/startup-output-tracker.ts +54 -0
  172. package/src/services/startup-paint-tracker.ts +115 -0
  173. package/src/services/startup-persisted-conversation-queue.ts +45 -0
  174. package/src/services/startup-settled-gate.ts +67 -0
  175. package/src/services/startup-shutdown.ts +53 -0
  176. package/src/services/startup-span-tracker.ts +77 -0
  177. package/src/services/startup-state-hydration.ts +94 -0
  178. package/src/services/startup-visibility.ts +35 -0
  179. package/src/services/status-timeline-recorder.ts +144 -0
  180. package/src/services/task-pane-selection-actions.ts +153 -0
  181. package/src/services/task-planning-hydration.ts +58 -0
  182. package/src/services/task-planning-observed-events.ts +89 -0
  183. package/src/services/workspace-observed-events.ts +113 -0
  184. package/src/store/control-plane-store-normalize.ts +760 -0
  185. package/src/store/control-plane-store-types.ts +224 -0
  186. package/src/store/control-plane-store.ts +2951 -0
  187. package/src/store/event-store.ts +253 -0
  188. package/src/store/sqlite.ts +81 -0
  189. package/src/terminal/compat-matrix.ts +345 -0
  190. package/src/terminal/differential-checkpoints.ts +132 -0
  191. package/src/terminal/parity-suite.ts +441 -0
  192. package/src/terminal/snapshot-oracle.ts +1840 -0
  193. package/src/ui/conversation-input-forwarder.ts +114 -0
  194. package/src/ui/conversation-selection-input.ts +103 -0
  195. package/src/ui/debug-footer-notice.ts +39 -0
  196. package/src/ui/global-shortcut-input.ts +126 -0
  197. package/src/ui/input-preflight.ts +68 -0
  198. package/src/ui/input-token-router.ts +312 -0
  199. package/src/ui/input.ts +238 -0
  200. package/src/ui/kit.ts +509 -0
  201. package/src/ui/left-nav-input.ts +80 -0
  202. package/src/ui/left-rail-pointer-input.ts +148 -0
  203. package/src/ui/main-pane-pointer-input.ts +150 -0
  204. package/src/ui/modals/manager.ts +192 -0
  205. package/src/ui/mux-theme.ts +529 -0
  206. package/src/ui/panes/conversation.ts +19 -0
  207. package/src/ui/panes/home-gridfire.ts +302 -0
  208. package/src/ui/panes/home.ts +109 -0
  209. package/src/ui/panes/left-rail.ts +12 -0
  210. package/src/ui/panes/project.ts +44 -0
  211. package/src/ui/pointer-routing-input.ts +158 -0
  212. package/src/ui/repository-fold-input.ts +91 -0
  213. package/src/ui/screen.ts +210 -0
  214. package/src/ui/surface.ts +224 -0
@@ -0,0 +1,234 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import {
3
+ consumeJsonLines,
4
+ parseClientEnvelope,
5
+ type StreamClientEnvelope,
6
+ type StreamCommand,
7
+ type StreamSignal,
8
+ } from './stream-protocol.ts';
9
+ import { recordPerfEvent, startPerfSpan } from '../perf/perf-core.ts';
10
+
11
+ interface ConnectionState {
12
+ id: string;
13
+ socket: {
14
+ destroy(): void;
15
+ on(event: 'data', listener: (chunk: Buffer) => void): void;
16
+ on(event: 'drain' | 'error' | 'close', listener: () => void): void;
17
+ };
18
+ remainder: string;
19
+ authenticated: boolean;
20
+ attachedSessionIds: Set<string>;
21
+ eventSessionIds: Set<string>;
22
+ streamSubscriptionIds: Set<string>;
23
+ queuedPayloads: Array<{
24
+ payload: string;
25
+ bytes: number;
26
+ diagnosticSessionId: string | null;
27
+ }>;
28
+ queuedPayloadBytes: number;
29
+ writeBlocked: boolean;
30
+ }
31
+
32
+ interface StreamServerConnectionContext {
33
+ readonly authToken: string | null;
34
+ readonly connections: Map<string, ConnectionState>;
35
+ flushConnectionWrites(connectionId: string): void;
36
+ cleanupConnection(connectionId: string): void;
37
+ handleSocketData(connection: ConnectionState, chunk: Buffer): void;
38
+ handleClientEnvelope(connection: ConnectionState, envelope: StreamClientEnvelope): void;
39
+ handleAuth(connection: ConnectionState, token: string): void;
40
+ handleCommand(connection: ConnectionState, commandId: string, command: StreamCommand): void;
41
+ handleInput(connectionId: string, sessionId: string, dataBase64: string): void;
42
+ handleResize(connectionId: string, sessionId: string, cols: number, rows: number): void;
43
+ handleSignal(connectionId: string, sessionId: string, signal: StreamSignal): void;
44
+ executeCommand(
45
+ connection: ConnectionState,
46
+ command: StreamCommand,
47
+ ): Record<string, unknown> | Promise<Record<string, unknown>>;
48
+ sendToConnection(connectionId: string, envelope: Record<string, unknown>): void;
49
+ }
50
+
51
+ export function handleConnection(
52
+ ctx: StreamServerConnectionContext,
53
+ socket: ConnectionState['socket'],
54
+ ): void {
55
+ const connectionId = `connection-${randomUUID()}`;
56
+ const state: ConnectionState = {
57
+ id: connectionId,
58
+ socket,
59
+ remainder: '',
60
+ authenticated: ctx.authToken === null,
61
+ attachedSessionIds: new Set<string>(),
62
+ eventSessionIds: new Set<string>(),
63
+ streamSubscriptionIds: new Set<string>(),
64
+ queuedPayloads: [],
65
+ queuedPayloadBytes: 0,
66
+ writeBlocked: false,
67
+ };
68
+
69
+ ctx.connections.set(connectionId, state);
70
+ recordPerfEvent('control-plane.server.connection.open', {
71
+ role: 'server',
72
+ connectionId,
73
+ authRequired: ctx.authToken !== null,
74
+ });
75
+
76
+ socket.on('data', (chunk: Buffer) => {
77
+ ctx.handleSocketData(state, chunk);
78
+ });
79
+
80
+ socket.on('drain', () => {
81
+ state.writeBlocked = false;
82
+ ctx.flushConnectionWrites(connectionId);
83
+ });
84
+
85
+ socket.on('error', () => {
86
+ ctx.cleanupConnection(connectionId);
87
+ });
88
+
89
+ socket.on('close', () => {
90
+ ctx.cleanupConnection(connectionId);
91
+ });
92
+ }
93
+
94
+ export function handleSocketData(
95
+ ctx: StreamServerConnectionContext,
96
+ connection: ConnectionState,
97
+ chunk: Buffer,
98
+ ): void {
99
+ const combined = `${connection.remainder}${chunk.toString('utf8')}`;
100
+ const consumed = consumeJsonLines(combined);
101
+ connection.remainder = consumed.remainder;
102
+
103
+ for (const message of consumed.messages) {
104
+ const parsed = parseClientEnvelope(message);
105
+ if (parsed === null) {
106
+ continue;
107
+ }
108
+ ctx.handleClientEnvelope(connection, parsed);
109
+ }
110
+ }
111
+
112
+ export function handleClientEnvelope(
113
+ ctx: StreamServerConnectionContext,
114
+ connection: ConnectionState,
115
+ envelope: StreamClientEnvelope,
116
+ ): void {
117
+ if (envelope.kind === 'auth') {
118
+ ctx.handleAuth(connection, envelope.token);
119
+ return;
120
+ }
121
+
122
+ if (!connection.authenticated) {
123
+ ctx.sendToConnection(connection.id, {
124
+ kind: 'auth.error',
125
+ error: 'authentication required',
126
+ });
127
+ recordPerfEvent('control-plane.server.auth.required', {
128
+ role: 'server',
129
+ connectionId: connection.id,
130
+ });
131
+ connection.socket.destroy();
132
+ return;
133
+ }
134
+
135
+ if (envelope.kind === 'command') {
136
+ ctx.handleCommand(connection, envelope.commandId, envelope.command);
137
+ return;
138
+ }
139
+
140
+ if (envelope.kind === 'pty.input') {
141
+ ctx.handleInput(connection.id, envelope.sessionId, envelope.dataBase64);
142
+ return;
143
+ }
144
+
145
+ if (envelope.kind === 'pty.resize') {
146
+ ctx.handleResize(connection.id, envelope.sessionId, envelope.cols, envelope.rows);
147
+ return;
148
+ }
149
+
150
+ ctx.handleSignal(connection.id, envelope.sessionId, envelope.signal);
151
+ }
152
+
153
+ export function handleAuth(
154
+ ctx: StreamServerConnectionContext,
155
+ connection: ConnectionState,
156
+ token: string,
157
+ ): void {
158
+ if (ctx.authToken === null) {
159
+ connection.authenticated = true;
160
+ ctx.sendToConnection(connection.id, {
161
+ kind: 'auth.ok',
162
+ });
163
+ recordPerfEvent('control-plane.server.auth.ok', {
164
+ role: 'server',
165
+ connectionId: connection.id,
166
+ authRequired: false,
167
+ });
168
+ return;
169
+ }
170
+
171
+ if (token !== ctx.authToken) {
172
+ ctx.sendToConnection(connection.id, {
173
+ kind: 'auth.error',
174
+ error: 'invalid auth token',
175
+ });
176
+ recordPerfEvent('control-plane.server.auth.failed', {
177
+ role: 'server',
178
+ connectionId: connection.id,
179
+ });
180
+ connection.socket.destroy();
181
+ return;
182
+ }
183
+
184
+ connection.authenticated = true;
185
+ ctx.sendToConnection(connection.id, {
186
+ kind: 'auth.ok',
187
+ });
188
+ recordPerfEvent('control-plane.server.auth.ok', {
189
+ role: 'server',
190
+ connectionId: connection.id,
191
+ authRequired: true,
192
+ });
193
+ }
194
+
195
+ export function handleCommand(
196
+ ctx: StreamServerConnectionContext,
197
+ connection: ConnectionState,
198
+ commandId: string,
199
+ command: StreamCommand,
200
+ ): void {
201
+ ctx.sendToConnection(connection.id, {
202
+ kind: 'command.accepted',
203
+ commandId,
204
+ });
205
+
206
+ const commandSpan = startPerfSpan('control-plane.server.command', {
207
+ role: 'server',
208
+ type: command.type,
209
+ });
210
+ Promise.resolve(ctx.executeCommand(connection, command))
211
+ .then((result) => {
212
+ commandSpan.end({
213
+ type: command.type,
214
+ status: 'completed',
215
+ });
216
+ ctx.sendToConnection(connection.id, {
217
+ kind: 'command.completed',
218
+ commandId,
219
+ result,
220
+ });
221
+ })
222
+ .catch((error: unknown) => {
223
+ commandSpan.end({
224
+ type: command.type,
225
+ status: 'failed',
226
+ message: String(error),
227
+ });
228
+ ctx.sendToConnection(connection.id, {
229
+ kind: 'command.failed',
230
+ commandId,
231
+ error: String(error),
232
+ });
233
+ });
234
+ }
@@ -0,0 +1,112 @@
1
+ import type { StreamObservedEvent } from './stream-protocol.ts';
2
+
3
+ interface StreamSubscriptionFilter {
4
+ tenantId?: string;
5
+ userId?: string;
6
+ workspaceId?: string;
7
+ repositoryId?: string;
8
+ taskId?: string;
9
+ directoryId?: string;
10
+ conversationId?: string;
11
+ includeOutput: boolean;
12
+ }
13
+
14
+ interface StreamObservedScope {
15
+ tenantId: string;
16
+ userId: string;
17
+ workspaceId: string;
18
+ directoryId: string | null;
19
+ conversationId: string | null;
20
+ }
21
+
22
+ interface FilterContext {
23
+ eventIncludesRepositoryId(event: StreamObservedEvent, repositoryId: string): boolean;
24
+ eventIncludesTaskId(event: StreamObservedEvent, taskId: string): boolean;
25
+ }
26
+
27
+ export function eventIncludesRepositoryId(
28
+ event: StreamObservedEvent,
29
+ repositoryId: string,
30
+ ): boolean {
31
+ if (event.type === 'directory-git-updated') {
32
+ return event.repositoryId === repositoryId;
33
+ }
34
+ if (event.type === 'repository-upserted' || event.type === 'repository-updated') {
35
+ return event.repository['repositoryId'] === repositoryId;
36
+ }
37
+ if (event.type === 'repository-archived') {
38
+ return event.repositoryId === repositoryId;
39
+ }
40
+ if (event.type === 'task-created' || event.type === 'task-updated') {
41
+ return event.task['repositoryId'] === repositoryId;
42
+ }
43
+ if (event.type === 'task-reordered') {
44
+ for (const task of event.tasks) {
45
+ if (task['repositoryId'] === repositoryId) {
46
+ return true;
47
+ }
48
+ }
49
+ return false;
50
+ }
51
+ if (event.type === 'github-pr-upserted') {
52
+ return event.pr['repositoryId'] === repositoryId;
53
+ }
54
+ if (event.type === 'github-pr-closed' || event.type === 'github-pr-jobs-updated') {
55
+ return event.repositoryId === repositoryId;
56
+ }
57
+ return false;
58
+ }
59
+
60
+ export function eventIncludesTaskId(event: StreamObservedEvent, taskId: string): boolean {
61
+ if (event.type === 'task-created' || event.type === 'task-updated') {
62
+ return event.task['taskId'] === taskId;
63
+ }
64
+ if (event.type === 'task-deleted') {
65
+ return event.taskId === taskId;
66
+ }
67
+ if (event.type === 'task-reordered') {
68
+ for (const task of event.tasks) {
69
+ if (task['taskId'] === taskId) {
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+ return false;
76
+ }
77
+
78
+ export function matchesObservedFilter(
79
+ ctx: FilterContext,
80
+ scope: StreamObservedScope,
81
+ event: StreamObservedEvent,
82
+ filter: StreamSubscriptionFilter,
83
+ ): boolean {
84
+ if (!filter.includeOutput && event.type === 'session-output') {
85
+ return false;
86
+ }
87
+ if (filter.tenantId !== undefined && scope.tenantId !== filter.tenantId) {
88
+ return false;
89
+ }
90
+ if (filter.userId !== undefined && scope.userId !== filter.userId) {
91
+ return false;
92
+ }
93
+ if (filter.workspaceId !== undefined && scope.workspaceId !== filter.workspaceId) {
94
+ return false;
95
+ }
96
+ if (
97
+ filter.repositoryId !== undefined &&
98
+ !ctx.eventIncludesRepositoryId(event, filter.repositoryId)
99
+ ) {
100
+ return false;
101
+ }
102
+ if (filter.taskId !== undefined && !ctx.eventIncludesTaskId(event, filter.taskId)) {
103
+ return false;
104
+ }
105
+ if (filter.directoryId !== undefined && scope.directoryId !== filter.directoryId) {
106
+ return false;
107
+ }
108
+ if (filter.conversationId !== undefined && scope.conversationId !== filter.conversationId) {
109
+ return false;
110
+ }
111
+ return true;
112
+ }