@overdeck/contracts 0.30.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/dist/index.mjs ADDED
@@ -0,0 +1,4199 @@
1
+ import { Effect, Schema } from "effect";
2
+ import * as Rpc from "effect/unstable/rpc/Rpc";
3
+ import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
4
+ //#region src/types.ts
5
+ const IssueId = Schema.String;
6
+ const AgentId = Schema.String;
7
+ const SequenceNumber = Schema.Number;
8
+ const AgentStatus = Schema.Literals([
9
+ "starting",
10
+ "running",
11
+ "stopped",
12
+ "error",
13
+ "unknown"
14
+ ]);
15
+ const Role = Schema.Literals([
16
+ "plan",
17
+ "work",
18
+ "review",
19
+ "test",
20
+ "ship",
21
+ "flywheel",
22
+ "strike",
23
+ "sequencer"
24
+ ]);
25
+ const AgentResolution = Schema.Literals([
26
+ "working",
27
+ "done",
28
+ "needs_input",
29
+ "stuck",
30
+ "completed",
31
+ "unclear",
32
+ "abandoned",
33
+ "api_error"
34
+ ]);
35
+ const ReviewStatusValue = Schema.Literals([
36
+ "pending",
37
+ "reviewing",
38
+ "passed",
39
+ "failed",
40
+ "blocked"
41
+ ]);
42
+ const TestStatusValue = Schema.Literals([
43
+ "pending",
44
+ "testing",
45
+ "passed",
46
+ "failed",
47
+ "skipped",
48
+ "dispatch_failed"
49
+ ]);
50
+ const UatStatusValue = Schema.Literals([
51
+ "pending",
52
+ "testing",
53
+ "passed",
54
+ "failed"
55
+ ]);
56
+ const MergeStatusValue = Schema.Literals([
57
+ "pending",
58
+ "queued",
59
+ "merging",
60
+ "verifying",
61
+ "merged",
62
+ "failed"
63
+ ]);
64
+ const VerificationStatusValue = Schema.Literals([
65
+ "pending",
66
+ "running",
67
+ "passed",
68
+ "failed",
69
+ "skipped"
70
+ ]);
71
+ const KNOWN_HARNESSES = new Set([
72
+ "claude-code",
73
+ "pi",
74
+ "codex"
75
+ ]);
76
+ /**
77
+ * Normalize a snapshot's runtime field to a known Harness value.
78
+ * Unknown or missing values fall back to 'claude-code' (the default harness).
79
+ */
80
+ function getHarness(snapshot) {
81
+ const raw = snapshot?.runtime;
82
+ if (raw && KNOWN_HARNESSES.has(raw)) return raw;
83
+ return "claude-code";
84
+ }
85
+ const Activity = Schema.Literals([
86
+ "working",
87
+ "thinking",
88
+ "waiting",
89
+ "idle",
90
+ "stopped"
91
+ ]);
92
+ const WaitingReason = Schema.Literals([
93
+ "tool_permission",
94
+ "user_question",
95
+ "disambiguation",
96
+ "other"
97
+ ]);
98
+ const ClaudeChannelPermissionBehavior = Schema.Literals(["allow", "deny"]);
99
+ const ThinkingState = Schema.Struct({
100
+ since: Schema.String,
101
+ lastToolAt: Schema.String
102
+ });
103
+ const WaitingState = Schema.Struct({
104
+ reason: WaitingReason,
105
+ startedAt: Schema.String,
106
+ message: Schema.optional(Schema.String),
107
+ notificationSent: Schema.optional(Schema.Boolean)
108
+ });
109
+ const CHANNEL_REPLY_KIND_VALUES = [
110
+ "status",
111
+ "done",
112
+ "needs_input"
113
+ ];
114
+ const MAX_CHANNEL_REPLY_SUMMARY_LENGTH = 4 * 1024;
115
+ const MAX_CHANNEL_REPLY_ARTIFACT_REFS = 20;
116
+ const MAX_CHANNEL_REPLY_ARTIFACT_URI_LENGTH = 512;
117
+ const MAX_CHANNEL_REPLY_ARTIFACT_LABEL_LENGTH = 512;
118
+ const CHANNEL_REPLY_ARTIFACT_URI_PATTERN = /^(?:file:\/\/|https:\/\/|\/)/;
119
+ const ChannelReplyKind = Schema.Literals(CHANNEL_REPLY_KIND_VALUES);
120
+ function isChannelReplyKind(value) {
121
+ return typeof value === "string" && CHANNEL_REPLY_KIND_VALUES.includes(value);
122
+ }
123
+ const ChannelReplyArtifactRef = Schema.Struct({
124
+ uri: Schema.String,
125
+ label: Schema.optional(Schema.String)
126
+ });
127
+ const AgentChannelReply = Schema.Struct({
128
+ kind: ChannelReplyKind,
129
+ summary: Schema.String,
130
+ artifactRefs: Schema.Array(ChannelReplyArtifactRef),
131
+ reportedAt: Schema.String
132
+ });
133
+ function normalizeChannelReplyPayload(payload, fieldPrefix = "channel_reply") {
134
+ if (payload === null || typeof payload !== "object") throw new Error(`${fieldPrefix} payload must be an object`);
135
+ const source = payload;
136
+ if (!isChannelReplyKind(source.kind)) throw new Error(`${fieldPrefix}.kind must be one of: ${CHANNEL_REPLY_KIND_VALUES.join(", ")}`);
137
+ if (typeof source.summary !== "string") throw new Error(`${fieldPrefix}.summary must be a non-empty string`);
138
+ const summary = source.summary.trim();
139
+ if (summary.length === 0) throw new Error(`${fieldPrefix}.summary must be a non-empty string`);
140
+ if (summary.length > 4096) throw new Error(`${fieldPrefix}.summary must be at most ${MAX_CHANNEL_REPLY_SUMMARY_LENGTH} characters`);
141
+ if (source.artifactRefs !== void 0 && !Array.isArray(source.artifactRefs)) throw new Error(`${fieldPrefix}.artifactRefs must be an array when provided`);
142
+ const rawArtifactRefs = source.artifactRefs ?? [];
143
+ if (rawArtifactRefs.length > 20) throw new Error(`${fieldPrefix}.artifactRefs must contain at most 20 entries`);
144
+ const artifactRefs = rawArtifactRefs.map((item, index) => {
145
+ if (item === null || typeof item !== "object") throw new Error(`${fieldPrefix}.artifactRefs[${index}] must be an object`);
146
+ const ref = item;
147
+ if (typeof ref.uri !== "string") throw new Error(`${fieldPrefix}.artifactRefs[${index}].uri must be a non-empty string`);
148
+ const uri = ref.uri.trim();
149
+ if (uri.length === 0) throw new Error(`${fieldPrefix}.artifactRefs[${index}].uri must be a non-empty string`);
150
+ if (uri.length > 512) throw new Error(`${fieldPrefix}.artifactRefs[${index}].uri must be at most 512 characters`);
151
+ if (!CHANNEL_REPLY_ARTIFACT_URI_PATTERN.test(uri)) throw new Error(`${fieldPrefix}.artifactRefs[${index}].uri must start with file://, https://, or /`);
152
+ if (ref.label !== void 0 && typeof ref.label !== "string") throw new Error(`${fieldPrefix}.artifactRefs[${index}].label must be a string when provided`);
153
+ const label = typeof ref.label === "string" ? ref.label.trim() : void 0;
154
+ if (label !== void 0 && label.length > 512) throw new Error(`${fieldPrefix}.artifactRefs[${index}].label must be at most 512 characters`);
155
+ return {
156
+ uri,
157
+ ...label ? { label } : {}
158
+ };
159
+ });
160
+ return {
161
+ kind: source.kind,
162
+ summary,
163
+ artifactRefs
164
+ };
165
+ }
166
+ const AgentRuntimeSnapshot = Schema.Struct({
167
+ id: AgentId,
168
+ activity: Activity,
169
+ lastActivity: Schema.String,
170
+ currentTool: Schema.optional(Schema.String),
171
+ thinking: Schema.optional(ThinkingState),
172
+ waiting: Schema.optional(WaitingState),
173
+ claudeSessionId: Schema.optional(Schema.String),
174
+ sessionModel: Schema.optional(Schema.String),
175
+ sessionHarness: Schema.optional(Schema.String),
176
+ model: Schema.optional(Schema.String),
177
+ lastMessageAt: Schema.optional(Schema.String),
178
+ channelReply: Schema.optional(AgentChannelReply),
179
+ resolution: Schema.optional(AgentResolution),
180
+ resolutionCount: Schema.optional(Schema.Number),
181
+ resolutionUpdatedAt: Schema.optional(Schema.String),
182
+ currentIssue: Schema.optional(IssueId),
183
+ contextSaturatedAt: Schema.optional(Schema.String),
184
+ updatedAtSequence: SequenceNumber
185
+ });
186
+ const ChannelPermissionRequestSnapshot = Schema.Struct({
187
+ requestId: Schema.String,
188
+ agentId: AgentId,
189
+ issueId: Schema.optional(IssueId),
190
+ toolName: Schema.String,
191
+ description: Schema.String,
192
+ inputPreview: Schema.String,
193
+ createdAt: Schema.String
194
+ });
195
+ const AgentSnapshot = Schema.Struct({
196
+ id: AgentId,
197
+ issueId: IssueId,
198
+ workspace: Schema.optional(Schema.String),
199
+ runtime: Schema.optional(Schema.String),
200
+ model: Schema.optional(Schema.String),
201
+ status: AgentStatus,
202
+ startedAt: Schema.optional(Schema.String),
203
+ lastActivity: Schema.optional(Schema.String),
204
+ branch: Schema.optional(Schema.String),
205
+ costSoFar: Schema.optional(Schema.Number),
206
+ sessionId: Schema.optional(Schema.String),
207
+ role: Schema.optional(Role),
208
+ phase: Schema.optional(Schema.String),
209
+ workType: Schema.optional(Schema.String),
210
+ roleRunHead: Schema.optional(Schema.String),
211
+ hasLiveTmuxSession: Schema.optional(Schema.Boolean),
212
+ stoppedByUser: Schema.optional(Schema.Boolean),
213
+ paused: Schema.optional(Schema.Boolean),
214
+ pausedReason: Schema.optional(Schema.String),
215
+ pausedAt: Schema.optional(Schema.String),
216
+ troubled: Schema.optional(Schema.Boolean),
217
+ troubledAt: Schema.optional(Schema.String),
218
+ consecutiveFailures: Schema.optional(Schema.Number),
219
+ firstFailureInRunAt: Schema.optional(Schema.String),
220
+ lastFailureAt: Schema.optional(Schema.String),
221
+ lastFailureReason: Schema.optional(Schema.String),
222
+ lastFailureNextRetryAt: Schema.optional(Schema.String),
223
+ runtimeState: Schema.optional(Schema.String),
224
+ hasPendingQuestion: Schema.optional(Schema.Boolean),
225
+ pendingQuestionCount: Schema.optional(Schema.Number),
226
+ pendingQuestionPrompt: Schema.optional(Schema.String),
227
+ pendingQuestionReason: Schema.optional(Schema.String),
228
+ pendingInputCount: Schema.optional(Schema.Number),
229
+ pendingInputKinds: Schema.optional(Schema.Array(Schema.String)),
230
+ pendingAskUserQuestion: Schema.optional(Schema.Struct({
231
+ toolUseId: Schema.String,
232
+ askedAt: Schema.String,
233
+ questions: Schema.Array(Schema.Struct({
234
+ question: Schema.String,
235
+ header: Schema.optional(Schema.String),
236
+ multiSelect: Schema.optional(Schema.Boolean),
237
+ options: Schema.Array(Schema.Struct({
238
+ label: Schema.String,
239
+ description: Schema.optional(Schema.String)
240
+ }))
241
+ }))
242
+ })),
243
+ resolution: Schema.optional(AgentResolution),
244
+ resolutionCount: Schema.optional(Schema.Number),
245
+ runtimeSnapshotSequence: Schema.optional(SequenceNumber)
246
+ });
247
+ const ReviewStatusSnapshot = Schema.Struct({
248
+ issueId: IssueId,
249
+ reviewStatus: Schema.optional(ReviewStatusValue),
250
+ testStatus: Schema.optional(TestStatusValue),
251
+ uatStatus: Schema.optional(UatStatusValue),
252
+ uatNotes: Schema.optional(Schema.String),
253
+ mergeStatus: Schema.optional(MergeStatusValue),
254
+ verificationStatus: Schema.optional(VerificationStatusValue),
255
+ verificationNotes: Schema.optional(Schema.String),
256
+ verificationCycleCount: Schema.optional(Schema.Number),
257
+ readyForMerge: Schema.optional(Schema.Boolean),
258
+ updatedAt: Schema.optional(Schema.String),
259
+ prUrl: Schema.optional(Schema.String),
260
+ /** Persistent stuck flag — set by divergence guard, cleared by /unstick */
261
+ stuck: Schema.optional(Schema.Boolean),
262
+ stuckReason: Schema.optional(Schema.String),
263
+ stuckAt: Schema.optional(Schema.String),
264
+ stuckDetails: Schema.optional(Schema.String),
265
+ /** Commit SHA at which review passed; deacon uses this to detect new pushes after review */
266
+ reviewedAtCommit: Schema.optional(Schema.String),
267
+ /** Commit SHA at which the pre-review verification gate last passed */
268
+ lastVerifiedCommit: Schema.optional(Schema.String),
269
+ /** Current merge pipeline step (granular visibility for the merge step tracker) */
270
+ mergeStep: Schema.optional(Schema.String),
271
+ /** PAN-699: timestamp when review agents were dispatched */
272
+ reviewSpawnedAt: Schema.optional(Schema.String),
273
+ /** PAN-699: number of test-agent dispatch retries */
274
+ testRetryCount: Schema.optional(Schema.Number),
275
+ /** PAN-794: parallel-review re-dispatch retry counter (current recovery cycle) */
276
+ reviewRetryCount: Schema.optional(Schema.Number),
277
+ /** PAN-796: review auto-requeue count (circuit breaker threshold) */
278
+ autoRequeueCount: Schema.optional(Schema.Number),
279
+ /** PAN-794: ISO timestamp marking the start of the current recovery cycle */
280
+ recoveryStartedAt: Schema.optional(Schema.String),
281
+ /** Human-requested patrol opt-out — when true, Deacon ignores this issue. */
282
+ deaconIgnored: Schema.optional(Schema.Boolean),
283
+ deaconIgnoredAt: Schema.optional(Schema.String),
284
+ deaconIgnoredReason: Schema.optional(Schema.String),
285
+ /** PAN-1691: per-issue auto-merge routing key. undefined = project default, true = auto-merge (fast lane), false = hold for UAT. */
286
+ autoMerge: Schema.optional(Schema.Boolean),
287
+ /** Active review orchestrator tmux session name (e.g. agent-pan-540-review). */
288
+ reviewCoordinatorSessionName: Schema.optional(Schema.String),
289
+ /** Active review sub-role tmux session names (e.g. agent-pan-540-review-correctness). Discovered at emission time. */
290
+ reviewSessionNames: Schema.optional(Schema.Array(Schema.String)),
291
+ /** Per-role review completion status (keyed by role: 'correctness' | 'security' | ...) */
292
+ reviewSubStatuses: Schema.optional(Schema.Record(Schema.String, Schema.Literals(["running", "done"]))),
293
+ /** PAN-905: queue position in the merge queue */
294
+ queuePosition: Schema.optional(Schema.Number),
295
+ /** PAN-905: currently active specialist (e.g. 'merge-agent') */
296
+ activeSpecialist: Schema.optional(Schema.String),
297
+ /** PAN-905: number of merge retries attempted */
298
+ mergeRetryCount: Schema.optional(Schema.Number),
299
+ /** PAN-905: free-form notes about the merge attempt */
300
+ mergeNotes: Schema.optional(Schema.String),
301
+ /** PAN-905: GitHub-native merge blocker reasons */
302
+ blockerReasons: Schema.optional(Schema.Array(Schema.Struct({
303
+ type: Schema.Literals([
304
+ "failing_checks",
305
+ "merge_conflict",
306
+ "unresolved_conversations",
307
+ "changes_requested",
308
+ "draft_pr",
309
+ "not_mergeable"
310
+ ]),
311
+ summary: Schema.String,
312
+ details: Schema.optional(Schema.String),
313
+ detectedAt: Schema.String
314
+ })))
315
+ });
316
+ const TurnDiffFileChange = Schema.Struct({
317
+ path: Schema.String,
318
+ kind: Schema.optional(Schema.String),
319
+ additions: Schema.optional(Schema.Number),
320
+ deletions: Schema.optional(Schema.Number)
321
+ });
322
+ const TurnDiffSummary = Schema.Struct({
323
+ turnId: Schema.String,
324
+ completedAt: Schema.String,
325
+ status: Schema.optional(Schema.String),
326
+ files: Schema.Array(TurnDiffFileChange),
327
+ checkpointRef: Schema.optional(Schema.String),
328
+ assistantMessageId: Schema.optional(Schema.String),
329
+ checkpointTurnCount: Schema.optional(Schema.Number)
330
+ });
331
+ const ScanProgressSnapshot = Schema.Struct({
332
+ active: Schema.Boolean,
333
+ mode: Schema.Literals([
334
+ "targeted",
335
+ "watched",
336
+ "system"
337
+ ]),
338
+ dirs: Schema.Array(Schema.String),
339
+ dirsProcessed: Schema.Number,
340
+ dirsTotal: Schema.Number,
341
+ sessionsFound: Schema.Number,
342
+ elapsedMs: Schema.Number,
343
+ inserted: Schema.Number,
344
+ updated: Schema.Number,
345
+ skipped: Schema.Number,
346
+ errors: Schema.Number,
347
+ durationMs: Schema.Number
348
+ });
349
+ const EnrichStatsSnapshot = Schema.Struct({
350
+ processed: Schema.Number,
351
+ totalCost: Schema.Number,
352
+ failures: Schema.Number,
353
+ durationMs: Schema.Number
354
+ });
355
+ const EnrichProgressSnapshot = Schema.Struct({
356
+ sessionId: Schema.Number,
357
+ level: Schema.Number,
358
+ model: Schema.String,
359
+ cost: Schema.Number,
360
+ success: Schema.Boolean,
361
+ error: Schema.optional(Schema.String),
362
+ timestamp: Schema.String
363
+ });
364
+ const EmbedProgressSnapshot = Schema.Struct({
365
+ sessionId: Schema.Number,
366
+ model: Schema.String,
367
+ success: Schema.Boolean,
368
+ error: Schema.optional(Schema.String),
369
+ timestamp: Schema.String
370
+ });
371
+ const DashboardSnapshot = Schema.Struct({
372
+ sequence: SequenceNumber,
373
+ agents: Schema.Array(AgentSnapshot),
374
+ specialists: Schema.Array(Schema.Unknown),
375
+ reviewStatuses: Schema.Array(ReviewStatusSnapshot),
376
+ issues: Schema.Array(Schema.Unknown),
377
+ resources: Schema.optional(Schema.Unknown),
378
+ memory: Schema.optional(Schema.Unknown),
379
+ agentRuntimeById: Schema.optional(Schema.Record(Schema.String, AgentRuntimeSnapshot)),
380
+ channelPermissionRequests: Schema.optional(Schema.Array(ChannelPermissionRequestSnapshot)),
381
+ scanProgress: Schema.optional(Schema.NullOr(ScanProgressSnapshot)),
382
+ enrichStats: Schema.optional(Schema.NullOr(EnrichStatsSnapshot)),
383
+ enrichProgressBySessionId: Schema.optional(Schema.Record(Schema.String, EnrichProgressSnapshot)),
384
+ embedProgressBySessionId: Schema.optional(Schema.Record(Schema.String, EmbedProgressSnapshot)),
385
+ timestamp: Schema.String
386
+ });
387
+ const WorkspaceDetail = Schema.Struct({
388
+ workspace: Schema.Unknown,
389
+ reviewStatus: Schema.Unknown,
390
+ planning: Schema.Unknown,
391
+ costs: Schema.Unknown,
392
+ agentOutput: Schema.Array(Schema.String)
393
+ });
394
+ const ResourceStats = Schema.Struct({ containers: Schema.Array(Schema.Struct({
395
+ name: Schema.String,
396
+ cpu: Schema.optional(Schema.String),
397
+ mem: Schema.optional(Schema.String),
398
+ status: Schema.optional(Schema.String)
399
+ })) });
400
+ const SessionNodePresence = Schema.Literals([
401
+ "active",
402
+ "idle",
403
+ "suspended",
404
+ "ended"
405
+ ]);
406
+ const SessionNodeType = Schema.Literals([
407
+ "planning",
408
+ "work",
409
+ "strike",
410
+ "review",
411
+ "reviewer",
412
+ "test",
413
+ "ship",
414
+ "merge",
415
+ "legacy"
416
+ ]);
417
+ const ReviewerRoundSummary = Schema.Struct({
418
+ round: Schema.Number,
419
+ status: Schema.optional(Schema.String),
420
+ reviewResult: Schema.optional(Schema.String),
421
+ startedAt: Schema.optional(Schema.String),
422
+ endedAt: Schema.optional(Schema.String),
423
+ durationSec: Schema.optional(Schema.Number),
424
+ cost: Schema.optional(Schema.Number),
425
+ findings: Schema.optional(Schema.Number),
426
+ summary: Schema.optional(Schema.String)
427
+ });
428
+ const ReviewerRoundMetadata = Schema.Struct({
429
+ roundCount: Schema.Number,
430
+ latestRound: Schema.Number,
431
+ latestStatus: Schema.optional(Schema.String),
432
+ latestReviewResult: Schema.optional(Schema.String),
433
+ history: Schema.Array(ReviewerRoundSummary)
434
+ });
435
+ const SessionNode = Schema.Struct({
436
+ type: SessionNodeType,
437
+ role: Schema.optional(Schema.String),
438
+ sessionId: Schema.String,
439
+ tmuxSession: Schema.optional(Schema.String),
440
+ model: Schema.String,
441
+ harness: Schema.optional(Schema.String),
442
+ startedAt: Schema.String,
443
+ endedAt: Schema.optional(Schema.String),
444
+ duration: Schema.NullOr(Schema.Number),
445
+ status: AgentStatus,
446
+ hasJsonl: Schema.optional(Schema.Boolean),
447
+ transcript: Schema.optional(Schema.String),
448
+ presence: SessionNodePresence,
449
+ awaitingInput: Schema.optional(Schema.Boolean),
450
+ awaitingInputPrompt: Schema.optional(Schema.String),
451
+ awaitingInputReason: Schema.optional(Schema.String),
452
+ roundMetadata: Schema.optional(ReviewerRoundMetadata),
453
+ deliveryMethod: Schema.optional(Schema.Literals([
454
+ "auto",
455
+ "channels",
456
+ "tmux"
457
+ ])),
458
+ paused: Schema.optional(Schema.Boolean),
459
+ pausedReason: Schema.optional(Schema.String),
460
+ pausedAt: Schema.optional(Schema.String)
461
+ });
462
+ const FeatureNode = Schema.Struct({
463
+ issueId: IssueId,
464
+ title: Schema.String,
465
+ sessions: Schema.Array(SessionNode)
466
+ });
467
+ const ProjectSessionTree = Schema.Struct({
468
+ projectKey: Schema.String,
469
+ features: Schema.Array(FeatureNode)
470
+ });
471
+ /** Snapshot of a discovered session for RPC responses and frontend display */
472
+ const DiscoveredSessionSnapshot = Schema.Struct({
473
+ id: Schema.Number,
474
+ jsonlPath: Schema.String,
475
+ sessionId: Schema.optional(Schema.String),
476
+ workspacePath: Schema.optional(Schema.String),
477
+ workspaceHash: Schema.optional(Schema.String),
478
+ messageCount: Schema.Number,
479
+ firstTs: Schema.optional(Schema.String),
480
+ lastTs: Schema.optional(Schema.String),
481
+ modelsUsed: Schema.Array(Schema.String),
482
+ primaryModel: Schema.optional(Schema.String),
483
+ tokenInput: Schema.Number,
484
+ tokenOutput: Schema.Number,
485
+ estimatedCost: Schema.Number,
486
+ toolsUsed: Schema.Array(Schema.String),
487
+ filesTouched: Schema.Array(Schema.String),
488
+ tags: Schema.Array(Schema.String),
489
+ summary: Schema.optional(Schema.String),
490
+ summaryDetailed: Schema.optional(Schema.String),
491
+ enrichmentLevel: Schema.Number,
492
+ enrichmentModel: Schema.optional(Schema.String),
493
+ enrichedAt: Schema.optional(Schema.String),
494
+ enrichmentFailed: Schema.Boolean,
495
+ overdeckManaged: Schema.Boolean,
496
+ panIssueId: Schema.optional(Schema.String),
497
+ panAgentId: Schema.optional(Schema.String),
498
+ scannedAt: Schema.String
499
+ });
500
+ /** Filter parameters for conversation search */
501
+ const ConversationFilter = Schema.Struct({
502
+ workspacePath: Schema.optional(Schema.String),
503
+ primaryModel: Schema.optional(Schema.String),
504
+ managed: Schema.optional(Schema.Boolean),
505
+ unmanaged: Schema.optional(Schema.Boolean),
506
+ since: Schema.optional(Schema.String),
507
+ before: Schema.optional(Schema.String),
508
+ after: Schema.optional(Schema.String),
509
+ minCost: Schema.optional(Schema.Number),
510
+ maxCost: Schema.optional(Schema.Number),
511
+ minMessages: Schema.optional(Schema.Number),
512
+ tags: Schema.optional(Schema.Array(Schema.String)),
513
+ tools: Schema.optional(Schema.Array(Schema.String)),
514
+ files: Schema.optional(Schema.Array(Schema.String)),
515
+ issueId: Schema.optional(Schema.String),
516
+ enrichmentLevel: Schema.optional(Schema.Number),
517
+ enriched: Schema.optional(Schema.Boolean),
518
+ notEnriched: Schema.optional(Schema.Boolean),
519
+ query: Schema.optional(Schema.String),
520
+ semantic: Schema.optional(Schema.Boolean),
521
+ similarTo: Schema.optional(Schema.Number),
522
+ limit: Schema.optional(Schema.Number),
523
+ offset: Schema.optional(Schema.Number),
524
+ format: Schema.optional(Schema.Literals([
525
+ "table",
526
+ "json",
527
+ "brief",
528
+ "ids"
529
+ ]))
530
+ });
531
+ /** Aggregate cost breakdown for conversations */
532
+ const ConversationCostSummary = Schema.Struct({
533
+ groupBy: Schema.Literals([
534
+ "workspace",
535
+ "model",
536
+ "day",
537
+ "month"
538
+ ]),
539
+ entries: Schema.Array(Schema.Struct({
540
+ key: Schema.String,
541
+ totalCost: Schema.Number,
542
+ sessionCount: Schema.Number,
543
+ totalTokensIn: Schema.Number,
544
+ totalTokensOut: Schema.Number
545
+ })),
546
+ grandTotal: Schema.Number,
547
+ totalTokensIn: Schema.Number,
548
+ totalTokensOut: Schema.Number
549
+ });
550
+ /** Scan result summary */
551
+ const ScanResult = Schema.Struct({
552
+ inserted: Schema.Number,
553
+ updated: Schema.Number,
554
+ skipped: Schema.Number,
555
+ errors: Schema.Number,
556
+ durationMs: Schema.Number
557
+ });
558
+ //#endregion
559
+ //#region src/memory.ts
560
+ const MemoryIdentity = Schema.Struct({
561
+ projectId: Schema.String,
562
+ workspaceId: Schema.String,
563
+ issueId: IssueId,
564
+ runId: Schema.String,
565
+ sessionId: Schema.String,
566
+ agentRole: Role,
567
+ agentHarness: Schema.String
568
+ });
569
+ const MemoryTokenUsage = Schema.Struct({
570
+ prompt: Schema.Number,
571
+ completion: Schema.Number,
572
+ total: Schema.Number
573
+ });
574
+ const MemoryObservation = Schema.Struct({
575
+ id: Schema.String,
576
+ timestamp: Schema.String,
577
+ projectId: Schema.String,
578
+ workspaceId: Schema.String,
579
+ issueId: IssueId,
580
+ runId: Schema.String,
581
+ sessionId: Schema.String,
582
+ agentRole: Role,
583
+ agentHarness: Schema.String,
584
+ gitBranch: Schema.String,
585
+ sourceTranscriptOffset: Schema.Number,
586
+ actionStatus: Schema.NullOr(Schema.String),
587
+ narrative: Schema.String,
588
+ summary: Schema.String,
589
+ files: Schema.Array(Schema.String),
590
+ tags: Schema.Array(Schema.String),
591
+ tokens: MemoryTokenUsage,
592
+ model: Schema.String
593
+ });
594
+ const MemoryStatusPhase = Schema.Literals([
595
+ "exploring",
596
+ "planning",
597
+ "building",
598
+ "verifying",
599
+ "cleaning",
600
+ "shipping"
601
+ ]);
602
+ const MemoryStatus = Schema.Struct({
603
+ name: Schema.String,
604
+ headline: Schema.String,
605
+ summary: Schema.String,
606
+ goal: Schema.NullOr(Schema.String),
607
+ phase: MemoryStatusPhase,
608
+ accomplished: Schema.Array(Schema.String),
609
+ decided: Schema.Array(Schema.String),
610
+ open: Schema.Array(Schema.String),
611
+ nextSteps: Schema.Array(Schema.String),
612
+ confidence: Schema.Number,
613
+ workingSet: Schema.Array(Schema.String),
614
+ tags: Schema.Array(Schema.String)
615
+ });
616
+ const PendingTurn = Schema.Struct({
617
+ id: Schema.String,
618
+ createdAt: Schema.String,
619
+ identity: MemoryIdentity,
620
+ trigger: Schema.Literals([
621
+ "stop-hook",
622
+ "poller",
623
+ "reconciliation",
624
+ "manual"
625
+ ]),
626
+ transcriptPath: Schema.String,
627
+ fromOffset: Schema.Number,
628
+ toOffset: Schema.Number,
629
+ lastFullLineOffset: Schema.Number,
630
+ eventsConsumed: Schema.Number,
631
+ compressedText: Schema.String
632
+ });
633
+ const ResetMarkerScope = Schema.Literals([
634
+ "project",
635
+ "workspace",
636
+ "issue",
637
+ "session"
638
+ ]);
639
+ const ResetMarker = Schema.Struct({
640
+ id: Schema.String,
641
+ scope: ResetMarkerScope,
642
+ scopeId: Schema.String,
643
+ fromTimestamp: Schema.String,
644
+ reason: Schema.String,
645
+ createdAt: Schema.String
646
+ });
647
+ const RagDecisionSource = Schema.Struct({
648
+ id: Schema.String,
649
+ docType: Schema.Literals([
650
+ "observation",
651
+ "summary",
652
+ "status",
653
+ "sibling"
654
+ ]),
655
+ scope: Schema.String,
656
+ score: Schema.Number,
657
+ tokens: Schema.Number
658
+ });
659
+ const RagDecision = Schema.Struct({
660
+ id: Schema.String,
661
+ timestamp: Schema.String,
662
+ identity: MemoryIdentity,
663
+ surface: Schema.Literals(["spawn", "user-prompt"]),
664
+ outcome: Schema.Literals([
665
+ "injected",
666
+ "skipped",
667
+ "no-hits",
668
+ "expansion-failed",
669
+ "context-too-large",
670
+ "budget-truncated"
671
+ ]),
672
+ query: Schema.String,
673
+ expandedTerms: Schema.Array(Schema.String),
674
+ allocations: Schema.Struct({
675
+ status: Schema.Number,
676
+ observations: Schema.Number,
677
+ summaries: Schema.Number,
678
+ sibling: Schema.Number
679
+ }),
680
+ sources: Schema.Array(RagDecisionSource),
681
+ reason: Schema.NullOr(Schema.String)
682
+ });
683
+ const ExtractionProviderTarget = Schema.Struct({
684
+ provider: Schema.String,
685
+ model: Schema.String
686
+ });
687
+ const ExtractionProviderConfig = Schema.Struct({
688
+ provider: Schema.String,
689
+ model: Schema.String,
690
+ providerEnvVar: Schema.optional(Schema.String),
691
+ modelEnvVar: Schema.optional(Schema.String),
692
+ perDayCostCapUsd: Schema.Number,
693
+ fallbackChain: Schema.Array(ExtractionProviderTarget)
694
+ });
695
+ //#endregion
696
+ //#region src/events.ts
697
+ /** App-level liveness frame for the /ws/rpc domain-events stream. */
698
+ const SystemHeartbeatEvent = Schema.Struct({
699
+ type: Schema.Literal("system.heartbeat"),
700
+ timestamp: Schema.String,
701
+ payload: Schema.Struct({ ts: Schema.Number })
702
+ });
703
+ /** Replaces socket.io `agents:changed` (event: 'started') */
704
+ const AgentStartedEvent = Schema.Struct({
705
+ type: Schema.Literal("agent.started"),
706
+ sequence: SequenceNumber,
707
+ timestamp: Schema.String,
708
+ payload: Schema.Struct({
709
+ agentId: AgentId,
710
+ issueId: IssueId,
711
+ agent: AgentSnapshot
712
+ })
713
+ });
714
+ /** Replaces socket.io `agents:changed` (event: 'stopped') */
715
+ const AgentStoppedEvent = Schema.Struct({
716
+ type: Schema.Literal("agent.stopped"),
717
+ sequence: SequenceNumber,
718
+ timestamp: Schema.String,
719
+ payload: Schema.Struct({
720
+ agentId: AgentId,
721
+ issueId: IssueId,
722
+ sessionId: Schema.optional(Schema.String)
723
+ })
724
+ });
725
+ const AgentHeartbeatDeadEvent = Schema.Struct({
726
+ type: Schema.Literal("agent.heartbeat_dead"),
727
+ sequence: SequenceNumber,
728
+ timestamp: Schema.String,
729
+ payload: Schema.Struct({
730
+ agentId: AgentId,
731
+ issueId: Schema.optional(IssueId),
732
+ sessionId: Schema.optional(Schema.String)
733
+ })
734
+ });
735
+ /** Role lifecycle — work agent completed implementation and is ready for review. */
736
+ const WorkCompletedEvent = Schema.Struct({
737
+ type: Schema.Literal("work.completed"),
738
+ sequence: SequenceNumber,
739
+ timestamp: Schema.String,
740
+ payload: Schema.Struct({
741
+ issueId: IssueId,
742
+ agentId: Schema.optional(AgentId)
743
+ })
744
+ });
745
+ /** Role lifecycle — generic agent completion signal, normalized by Cloister by role. */
746
+ const AgentCompletedEvent = Schema.Struct({
747
+ type: Schema.Literal("agent.completed"),
748
+ sequence: SequenceNumber,
749
+ timestamp: Schema.String,
750
+ payload: Schema.Struct({
751
+ issueId: IssueId,
752
+ agentId: Schema.optional(AgentId),
753
+ role: Schema.optional(Role)
754
+ })
755
+ });
756
+ /** Role lifecycle — review approved the branch and testing should start. */
757
+ const ReviewApprovedEvent = Schema.Struct({
758
+ type: Schema.Literal("review.approved"),
759
+ sequence: SequenceNumber,
760
+ timestamp: Schema.String,
761
+ payload: Schema.Struct({ issueId: IssueId })
762
+ });
763
+ /** Role lifecycle — tests passed and shipping should prepare the branch. */
764
+ const TestPassedEvent = Schema.Struct({
765
+ type: Schema.Literal("test.passed"),
766
+ sequence: SequenceNumber,
767
+ timestamp: Schema.String,
768
+ payload: Schema.Struct({ issueId: IssueId })
769
+ });
770
+ /** Replaces socket.io `godview:status-change` */
771
+ const AgentStatusChangedEvent = Schema.Struct({
772
+ type: Schema.Literal("agent.status_changed"),
773
+ sequence: SequenceNumber,
774
+ timestamp: Schema.String,
775
+ payload: Schema.Struct({
776
+ agentId: AgentId,
777
+ issueId: Schema.optional(IssueId),
778
+ status: AgentStatus,
779
+ previousStatus: Schema.optional(AgentStatus),
780
+ hasLiveTmuxSession: Schema.optional(Schema.Boolean),
781
+ stoppedByUser: Schema.optional(Schema.Boolean),
782
+ stoppedByPause: Schema.optional(Schema.Boolean),
783
+ paused: Schema.optional(Schema.Boolean),
784
+ pausedReason: Schema.optional(Schema.NullOr(Schema.String)),
785
+ pausedAt: Schema.optional(Schema.NullOr(Schema.String)),
786
+ troubled: Schema.optional(Schema.Boolean),
787
+ troubledAt: Schema.optional(Schema.NullOr(Schema.String)),
788
+ consecutiveFailures: Schema.optional(Schema.Number),
789
+ firstFailureInRunAt: Schema.optional(Schema.NullOr(Schema.String)),
790
+ lastFailureAt: Schema.optional(Schema.NullOr(Schema.String)),
791
+ lastFailureReason: Schema.optional(Schema.NullOr(Schema.String)),
792
+ lastFailureNextRetryAt: Schema.optional(Schema.NullOr(Schema.String)),
793
+ kickoffDelivered: Schema.optional(Schema.Boolean),
794
+ hostOverride: Schema.optional(Schema.Boolean),
795
+ role: Schema.optional(Role),
796
+ model: Schema.optional(Schema.String),
797
+ workspace: Schema.optional(Schema.String),
798
+ sessionId: Schema.optional(Schema.String),
799
+ lastActivity: Schema.optional(Schema.String),
800
+ lastResumeAt: Schema.optional(Schema.String),
801
+ stoppedAt: Schema.optional(Schema.String),
802
+ branch: Schema.optional(Schema.String),
803
+ costSoFar: Schema.optional(Schema.Number),
804
+ phase: Schema.optional(Schema.String),
805
+ workType: Schema.optional(Schema.String),
806
+ roleRunHead: Schema.optional(Schema.String),
807
+ flywheelRunId: Schema.optional(Schema.String),
808
+ reviewSubRole: Schema.optional(Schema.String),
809
+ reviewRunId: Schema.optional(Schema.String),
810
+ reviewOutputPath: Schema.optional(Schema.String),
811
+ reviewSynthesisAgentId: Schema.optional(Schema.String),
812
+ reviewDeadlineAt: Schema.optional(Schema.String),
813
+ reviewMonitorSignaled: Schema.optional(Schema.String),
814
+ reviewRetryAttempt: Schema.optional(Schema.Number),
815
+ inspectSubRole: Schema.optional(Schema.String),
816
+ deliveryMethod: Schema.optional(Schema.String),
817
+ supervisorEnabled: Schema.optional(Schema.Boolean),
818
+ channelsEnabled: Schema.optional(Schema.Boolean)
819
+ })
820
+ });
821
+ /** Replaces socket.io `godview:agent-output` */
822
+ const AgentOutputReceivedEvent = Schema.Struct({
823
+ type: Schema.Literal("agent.output_received"),
824
+ sequence: SequenceNumber,
825
+ timestamp: Schema.String,
826
+ payload: Schema.Struct({
827
+ agentId: AgentId,
828
+ lines: Schema.Array(Schema.String)
829
+ })
830
+ });
831
+ /** New — agent enrichment fields updated (PAN-440) */
832
+ const AgentEnrichmentChangedEvent = Schema.Struct({
833
+ type: Schema.Literal("agent.enrichment_changed"),
834
+ sequence: SequenceNumber,
835
+ timestamp: Schema.String,
836
+ payload: Schema.Struct({
837
+ agentId: AgentId,
838
+ role: Schema.optional(Role),
839
+ hasPendingQuestion: Schema.optional(Schema.Boolean),
840
+ pendingQuestionCount: Schema.optional(Schema.Number),
841
+ pendingQuestionPrompt: Schema.optional(Schema.String),
842
+ pendingQuestionReason: Schema.optional(Schema.String),
843
+ pendingInputCount: Schema.optional(Schema.Number),
844
+ pendingInputKinds: Schema.optional(Schema.Array(Schema.String)),
845
+ pendingAskUserQuestion: Schema.optional(Schema.Struct({
846
+ toolUseId: Schema.String,
847
+ askedAt: Schema.String,
848
+ questions: Schema.Array(Schema.Struct({
849
+ question: Schema.String,
850
+ header: Schema.optional(Schema.String),
851
+ multiSelect: Schema.optional(Schema.Boolean),
852
+ options: Schema.Array(Schema.Struct({
853
+ label: Schema.String,
854
+ description: Schema.optional(Schema.String)
855
+ }))
856
+ }))
857
+ })),
858
+ resolution: Schema.optional(AgentResolution),
859
+ resolutionCount: Schema.optional(Schema.Number)
860
+ })
861
+ });
862
+ /** New — agent created in database */
863
+ const AgentCreatedEvent = Schema.Struct({
864
+ type: Schema.Literal("agent.created"),
865
+ sequence: SequenceNumber,
866
+ timestamp: Schema.String,
867
+ payload: Schema.Struct({
868
+ agentId: AgentId,
869
+ issueId: IssueId,
870
+ agent: AgentSnapshot
871
+ })
872
+ });
873
+ const AgentActivityChangedEvent = Schema.Struct({
874
+ type: Schema.Literal("agent.activity_changed"),
875
+ sequence: SequenceNumber,
876
+ timestamp: Schema.String,
877
+ payload: Schema.Struct({
878
+ agentId: AgentId,
879
+ activity: Activity,
880
+ currentTool: Schema.optional(Schema.String)
881
+ })
882
+ });
883
+ const AgentThinkingStartedEvent = Schema.Struct({
884
+ type: Schema.Literal("agent.thinking_started"),
885
+ sequence: SequenceNumber,
886
+ timestamp: Schema.String,
887
+ payload: Schema.Struct({
888
+ agentId: AgentId,
889
+ lastToolAt: Schema.String
890
+ })
891
+ });
892
+ const AgentThinkingStoppedEvent = Schema.Struct({
893
+ type: Schema.Literal("agent.thinking_stopped"),
894
+ sequence: SequenceNumber,
895
+ timestamp: Schema.String,
896
+ payload: Schema.Struct({
897
+ agentId: AgentId,
898
+ resolvedBy: Schema.Literals([
899
+ "tool",
900
+ "waiting",
901
+ "idle",
902
+ "stopped"
903
+ ])
904
+ })
905
+ });
906
+ const AgentWaitingStartedEvent = Schema.Struct({
907
+ type: Schema.Literal("agent.waiting_started"),
908
+ sequence: SequenceNumber,
909
+ timestamp: Schema.String,
910
+ payload: Schema.Struct({
911
+ agentId: AgentId,
912
+ reason: WaitingReason,
913
+ message: Schema.optional(Schema.String)
914
+ })
915
+ });
916
+ const AgentWaitingClearedEvent = Schema.Struct({
917
+ type: Schema.Literal("agent.waiting_cleared"),
918
+ sequence: SequenceNumber,
919
+ timestamp: Schema.String,
920
+ payload: Schema.Struct({
921
+ agentId: AgentId,
922
+ clearedBy: Schema.Literals([
923
+ "user_response",
924
+ "timeout",
925
+ "stopped",
926
+ "tool_resumed"
927
+ ])
928
+ })
929
+ });
930
+ const AgentPermissionRequestedEvent = Schema.Struct({
931
+ type: Schema.Literal("agent.permission_requested"),
932
+ sequence: SequenceNumber,
933
+ timestamp: Schema.String,
934
+ payload: ChannelPermissionRequestSnapshot
935
+ });
936
+ const AgentPermissionResolvedEvent = Schema.Struct({
937
+ type: Schema.Literal("agent.permission_resolved"),
938
+ sequence: SequenceNumber,
939
+ timestamp: Schema.String,
940
+ payload: Schema.Struct({
941
+ requestId: Schema.String,
942
+ agentId: AgentId,
943
+ issueId: Schema.optional(IssueId),
944
+ behavior: ClaudeChannelPermissionBehavior
945
+ })
946
+ });
947
+ const AgentMessageReceivedEvent = Schema.Struct({
948
+ type: Schema.Literal("agent.message_received"),
949
+ sequence: SequenceNumber,
950
+ timestamp: Schema.String,
951
+ payload: Schema.Struct({
952
+ agentId: AgentId,
953
+ direction: Schema.Literals(["to_agent", "from_agent"]),
954
+ source: Schema.Literals([
955
+ "user",
956
+ "cloister",
957
+ "specialist",
958
+ "automated"
959
+ ])
960
+ })
961
+ });
962
+ const AgentChannelReplyEvent = Schema.Struct({
963
+ type: Schema.Literal("agent.channel_reply"),
964
+ sequence: SequenceNumber,
965
+ timestamp: Schema.String,
966
+ payload: Schema.Struct({
967
+ agentId: AgentId,
968
+ reply: AgentChannelReply
969
+ })
970
+ });
971
+ const AgentModelSetEvent = Schema.Struct({
972
+ type: Schema.Literal("agent.model_set"),
973
+ sequence: SequenceNumber,
974
+ timestamp: Schema.String,
975
+ payload: Schema.Struct({
976
+ agentId: AgentId,
977
+ model: Schema.String,
978
+ claudeSessionId: Schema.optional(Schema.String),
979
+ sessionModel: Schema.optional(Schema.String),
980
+ sessionHarness: Schema.optional(Schema.String)
981
+ })
982
+ });
983
+ const AgentCurrentIssueSetEvent = Schema.Struct({
984
+ type: Schema.Literal("agent.current_issue_set"),
985
+ sequence: SequenceNumber,
986
+ timestamp: Schema.String,
987
+ payload: Schema.Struct({
988
+ agentId: AgentId,
989
+ currentIssue: Schema.optional(IssueId)
990
+ })
991
+ });
992
+ const AgentContextSaturationChangedEvent = Schema.Struct({
993
+ type: Schema.Literal("agent.context_saturation_changed"),
994
+ sequence: SequenceNumber,
995
+ timestamp: Schema.String,
996
+ payload: Schema.Struct({
997
+ agentId: AgentId,
998
+ contextSaturatedAt: Schema.optional(Schema.String)
999
+ })
1000
+ });
1001
+ const AgentResolutionChangedEvent = Schema.Struct({
1002
+ type: Schema.Literal("agent.resolution_changed"),
1003
+ sequence: SequenceNumber,
1004
+ timestamp: Schema.String,
1005
+ payload: Schema.Struct({
1006
+ agentId: AgentId,
1007
+ resolution: AgentResolution,
1008
+ resolutionCount: Schema.Number
1009
+ })
1010
+ });
1011
+ /**
1012
+ * Bootstrap-only event emitted by AgentStateService when it seeds a runtime
1013
+ * snapshot during bootstrap (reconstruction from sources, PAN-1920). Not
1014
+ * emitted by hooks.
1015
+ */
1016
+ const AgentStateRestoredEvent = Schema.Struct({
1017
+ type: Schema.Literal("agent.state_restored"),
1018
+ sequence: SequenceNumber,
1019
+ timestamp: Schema.String,
1020
+ payload: Schema.Struct({
1021
+ agentId: AgentId,
1022
+ snapshot: AgentRuntimeSnapshot
1023
+ })
1024
+ });
1025
+ /** Emitted when a turn diff checkpoint is captured and diff computed */
1026
+ const AgentTurnDiffCompletedEvent = Schema.Struct({
1027
+ type: Schema.Literal("agent.turn_diff_completed"),
1028
+ sequence: SequenceNumber,
1029
+ timestamp: Schema.String,
1030
+ payload: Schema.Struct({
1031
+ agentId: AgentId,
1032
+ turnId: Schema.String,
1033
+ completedAt: Schema.String,
1034
+ files: Schema.Array(Schema.Struct({
1035
+ path: Schema.String,
1036
+ kind: Schema.optional(Schema.String),
1037
+ additions: Schema.optional(Schema.Number),
1038
+ deletions: Schema.optional(Schema.Number)
1039
+ })),
1040
+ checkpointRef: Schema.optional(Schema.String),
1041
+ assistantMessageId: Schema.optional(Schema.String),
1042
+ checkpointTurnCount: Schema.optional(Schema.Number)
1043
+ })
1044
+ });
1045
+ /** Replaces socket.io `planning:started` */
1046
+ const PlanningStartedEvent = Schema.Struct({
1047
+ type: Schema.Literal("planning.started"),
1048
+ sequence: SequenceNumber,
1049
+ timestamp: Schema.String,
1050
+ payload: Schema.Struct({
1051
+ issueId: IssueId,
1052
+ sessionName: Schema.String,
1053
+ harness: Schema.optional(Schema.String)
1054
+ })
1055
+ });
1056
+ /** Replaces socket.io `planning:failed` */
1057
+ const PlanningFailedEvent = Schema.Struct({
1058
+ type: Schema.Literal("planning.failed"),
1059
+ sequence: SequenceNumber,
1060
+ timestamp: Schema.String,
1061
+ payload: Schema.Struct({
1062
+ issueId: IssueId,
1063
+ error: Schema.String
1064
+ })
1065
+ });
1066
+ /** Replaces socket.io `planning:sync` */
1067
+ const PlanningSyncEvent = Schema.Struct({
1068
+ type: Schema.Literal("planning.sync"),
1069
+ sequence: SequenceNumber,
1070
+ timestamp: Schema.String,
1071
+ payload: Schema.Struct({
1072
+ issueId: IssueId,
1073
+ status: Schema.String,
1074
+ progress: Schema.optional(Schema.Number),
1075
+ message: Schema.optional(Schema.String)
1076
+ })
1077
+ });
1078
+ /** Replaces socket.io `plan:item-status-changed` */
1079
+ const PlanItemStatusChangedEvent = Schema.Struct({
1080
+ type: Schema.Literal("plan.item_status_changed"),
1081
+ sequence: SequenceNumber,
1082
+ timestamp: Schema.String,
1083
+ payload: Schema.Struct({
1084
+ issueId: IssueId,
1085
+ itemId: Schema.String,
1086
+ status: Schema.String
1087
+ })
1088
+ });
1089
+ /** Replaces socket.io `plan:subitem-status-changed` */
1090
+ const PlanSubitemStatusChangedEvent = Schema.Struct({
1091
+ type: Schema.Literal("plan.subitem_status_changed"),
1092
+ sequence: SequenceNumber,
1093
+ timestamp: Schema.String,
1094
+ payload: Schema.Struct({
1095
+ issueId: IssueId,
1096
+ itemId: Schema.String,
1097
+ subItemId: Schema.String,
1098
+ status: Schema.String
1099
+ })
1100
+ });
1101
+ /** Replaces socket.io `plan:items-unblocked` */
1102
+ const PlanItemsUnblockedEvent = Schema.Struct({
1103
+ type: Schema.Literal("plan.items_unblocked"),
1104
+ sequence: SequenceNumber,
1105
+ timestamp: Schema.String,
1106
+ payload: Schema.Struct({
1107
+ issueId: IssueId,
1108
+ items: Schema.Array(Schema.String)
1109
+ })
1110
+ });
1111
+ /** Replaces socket.io `pipeline:status` */
1112
+ const PipelineStatusChangedEvent = Schema.Struct({
1113
+ type: Schema.Literal("pipeline.status_changed"),
1114
+ sequence: SequenceNumber,
1115
+ timestamp: Schema.String,
1116
+ payload: Schema.Struct({
1117
+ issueId: IssueId,
1118
+ status: ReviewStatusSnapshot
1119
+ })
1120
+ });
1121
+ /** Replaces socket.io `merge:ready` */
1122
+ const MergeReadyEvent = Schema.Struct({
1123
+ type: Schema.Literal("merge.ready"),
1124
+ sequence: SequenceNumber,
1125
+ timestamp: Schema.String,
1126
+ payload: Schema.Struct({ issueId: IssueId })
1127
+ });
1128
+ /** New — review status changed */
1129
+ const ReviewStatusChangedEvent = Schema.Struct({
1130
+ type: Schema.Literal("review.status_changed"),
1131
+ sequence: SequenceNumber,
1132
+ timestamp: Schema.String,
1133
+ payload: Schema.Struct({
1134
+ issueId: IssueId,
1135
+ status: ReviewStatusSnapshot
1136
+ })
1137
+ });
1138
+ /** New — review specialist dispatched */
1139
+ const PipelineReviewStartedEvent = Schema.Struct({
1140
+ type: Schema.Literal("pipeline.review-started"),
1141
+ sequence: SequenceNumber,
1142
+ timestamp: Schema.String,
1143
+ payload: Schema.Struct({ issueId: IssueId })
1144
+ });
1145
+ /** New — review specialist finished (passed or failed) */
1146
+ const PipelineReviewCompletedEvent = Schema.Struct({
1147
+ type: Schema.Literal("pipeline.review-completed"),
1148
+ sequence: SequenceNumber,
1149
+ timestamp: Schema.String,
1150
+ payload: Schema.Struct({
1151
+ issueId: IssueId,
1152
+ passed: Schema.Boolean
1153
+ })
1154
+ });
1155
+ /** New — test specialist dispatched */
1156
+ const PipelineTestStartedEvent = Schema.Struct({
1157
+ type: Schema.Literal("pipeline.test-started"),
1158
+ sequence: SequenceNumber,
1159
+ timestamp: Schema.String,
1160
+ payload: Schema.Struct({ issueId: IssueId })
1161
+ });
1162
+ /** New — test specialist finished (passed or failed) */
1163
+ const PipelineTestCompletedEvent = Schema.Struct({
1164
+ type: Schema.Literal("pipeline.test-completed"),
1165
+ sequence: SequenceNumber,
1166
+ timestamp: Schema.String,
1167
+ payload: Schema.Struct({
1168
+ issueId: IssueId,
1169
+ passed: Schema.Boolean
1170
+ })
1171
+ });
1172
+ const OperatorInterventionEvent = Schema.Struct({
1173
+ type: Schema.Literal("operator.intervention"),
1174
+ sequence: SequenceNumber,
1175
+ timestamp: Schema.String,
1176
+ payload: Schema.Struct({
1177
+ issueId: IssueId,
1178
+ kind: Schema.Literals([
1179
+ "tell",
1180
+ "pause",
1181
+ "restart",
1182
+ "manual_edit",
1183
+ "deep_wipe",
1184
+ "unpause",
1185
+ "untroubled"
1186
+ ]),
1187
+ source: Schema.String
1188
+ })
1189
+ });
1190
+ const SubstrateBugFiledEvent = Schema.Struct({
1191
+ type: Schema.Literal("substrate.bug_filed"),
1192
+ sequence: SequenceNumber,
1193
+ timestamp: Schema.String,
1194
+ payload: Schema.Struct({
1195
+ issueId: IssueId,
1196
+ runId: Schema.optional(Schema.String),
1197
+ filedBy: Schema.Literals(["agent", "operator"]),
1198
+ discoveredIn: Schema.optional(IssueId),
1199
+ severity: Schema.Literals([
1200
+ "P0",
1201
+ "P1",
1202
+ "P2"
1203
+ ])
1204
+ })
1205
+ });
1206
+ /**
1207
+ * PAN-915 — reviewer session received a new prompt (spawn or resume of a
1208
+ * canonical PAN-830 session). Drives event-driven `reviewSubStatuses[role] =
1209
+ * 'running'` and tracking of `reviewSessionNames` without polling tmux.
1210
+ */
1211
+ const ReviewReviewerStartedEvent = Schema.Struct({
1212
+ type: Schema.Literal("review.reviewer_started"),
1213
+ sequence: SequenceNumber,
1214
+ timestamp: Schema.String,
1215
+ payload: Schema.Struct({
1216
+ issueId: IssueId,
1217
+ role: Schema.String,
1218
+ sessionName: Schema.String
1219
+ })
1220
+ });
1221
+ /**
1222
+ * PAN-915 — reviewer wrote its output file (round complete for that role).
1223
+ * Updates `reviewSubStatuses[role] = 'done'` event-driven.
1224
+ */
1225
+ const ReviewReviewerCompletedEvent = Schema.Struct({
1226
+ type: Schema.Literal("review.reviewer_completed"),
1227
+ sequence: SequenceNumber,
1228
+ timestamp: Schema.String,
1229
+ payload: Schema.Struct({
1230
+ issueId: IssueId,
1231
+ role: Schema.String
1232
+ })
1233
+ });
1234
+ /**
1235
+ * Review specialist timeout telemetry. Emitted once per timed-out reviewer wait
1236
+ * attempt so operators can distinguish transient auto-retries from terminal
1237
+ * review failures.
1238
+ */
1239
+ const ReviewSpecialistTimedOutEvent = Schema.Struct({
1240
+ type: Schema.Literal("review.specialist.timed_out"),
1241
+ sequence: SequenceNumber,
1242
+ timestamp: Schema.String,
1243
+ payload: Schema.Struct({
1244
+ issueId: IssueId,
1245
+ role: Schema.String,
1246
+ sessionName: Schema.String,
1247
+ attempt: Schema.Number,
1248
+ maxRetries: Schema.Number,
1249
+ willRetry: Schema.Boolean
1250
+ })
1251
+ });
1252
+ /**
1253
+ * PAN-915 — review coordinator session spawned. Surfaces in the dashboard so
1254
+ * the kanban card can show "review in progress" the instant the coordinator
1255
+ * starts, not after the first reviewer finishes.
1256
+ */
1257
+ const ReviewCoordinatorStartedEvent = Schema.Struct({
1258
+ type: Schema.Literal("review.coordinator_started"),
1259
+ sequence: SequenceNumber,
1260
+ timestamp: Schema.String,
1261
+ payload: Schema.Struct({
1262
+ issueId: IssueId,
1263
+ sessionName: Schema.String
1264
+ })
1265
+ });
1266
+ /** Review coordinator died before writing a terminal exit marker. */
1267
+ const ReviewCoordinatorDiedEvent = Schema.Struct({
1268
+ type: Schema.Literal("review.coordinator.died"),
1269
+ sequence: SequenceNumber,
1270
+ timestamp: Schema.String,
1271
+ payload: Schema.Struct({
1272
+ issueId: IssueId,
1273
+ sessionName: Schema.String,
1274
+ reason: Schema.String
1275
+ })
1276
+ });
1277
+ const SpecialistLifecycleState = Schema.Literals([
1278
+ "active",
1279
+ "sleeping",
1280
+ "uninitialized"
1281
+ ]);
1282
+ /** New — role-backed specialist became active */
1283
+ const SpecialistStartedEvent = Schema.Struct({
1284
+ type: Schema.Literal("specialist.started"),
1285
+ sequence: SequenceNumber,
1286
+ timestamp: Schema.String,
1287
+ payload: Schema.Struct({
1288
+ name: Role,
1289
+ state: SpecialistLifecycleState,
1290
+ isRunning: Schema.Boolean,
1291
+ currentIssue: Schema.optional(Schema.String),
1292
+ lastWake: Schema.optional(Schema.String)
1293
+ })
1294
+ });
1295
+ /** New — role-backed specialist completed work */
1296
+ const SpecialistCompletedEvent = Schema.Struct({
1297
+ type: Schema.Literal("specialist.completed"),
1298
+ sequence: SequenceNumber,
1299
+ timestamp: Schema.String,
1300
+ payload: Schema.Struct({
1301
+ name: Role,
1302
+ issueId: Schema.optional(IssueId)
1303
+ })
1304
+ });
1305
+ /** New — role-backed specialist failed */
1306
+ const SpecialistFailedEvent = Schema.Struct({
1307
+ type: Schema.Literal("specialist.failed"),
1308
+ sequence: SequenceNumber,
1309
+ timestamp: Schema.String,
1310
+ payload: Schema.Struct({
1311
+ name: Role,
1312
+ issueId: Schema.optional(IssueId),
1313
+ error: Schema.String
1314
+ })
1315
+ });
1316
+ /** Replaces socket.io `resources:updated` */
1317
+ const ResourcesUpdatedEvent = Schema.Struct({
1318
+ type: Schema.Literal("resources.updated"),
1319
+ sequence: SequenceNumber,
1320
+ timestamp: Schema.String,
1321
+ payload: Schema.Struct({ resources: ResourceStats })
1322
+ });
1323
+ const SystemHealthSeverityChangedEvent = Schema.Struct({
1324
+ type: Schema.Literal("system.health_severity_changed"),
1325
+ sequence: SequenceNumber,
1326
+ timestamp: Schema.String,
1327
+ payload: Schema.Struct({
1328
+ previousSeverity: Schema.String,
1329
+ severity: Schema.String,
1330
+ reasons: Schema.Array(Schema.String),
1331
+ leakedSpecialistCount: Schema.Number
1332
+ })
1333
+ });
1334
+ /** Replaces socket.io `issues:snapshot` */
1335
+ const IssuesSnapshotEvent = Schema.Struct({
1336
+ type: Schema.Literal("issues.snapshot"),
1337
+ sequence: SequenceNumber,
1338
+ timestamp: Schema.String,
1339
+ payload: Schema.Struct({ issues: Schema.Array(Schema.Unknown) })
1340
+ });
1341
+ /** Replaces socket.io `issues:updated` */
1342
+ const IssuesUpdatedEvent = Schema.Struct({
1343
+ type: Schema.Literal("issues.updated"),
1344
+ sequence: SequenceNumber,
1345
+ timestamp: Schema.String,
1346
+ payload: Schema.Struct({ issueId: Schema.optional(IssueId) })
1347
+ });
1348
+ /** Patch a single issue's status in the read model without a full snapshot refresh. */
1349
+ const IssueStatusChangedEvent = Schema.Struct({
1350
+ type: Schema.Literal("issue.statusChanged"),
1351
+ sequence: SequenceNumber,
1352
+ timestamp: Schema.String,
1353
+ payload: Schema.Struct({
1354
+ issueId: IssueId,
1355
+ status: Schema.String,
1356
+ canonicalStatus: Schema.String,
1357
+ labels: Schema.optional(Schema.Array(Schema.String))
1358
+ })
1359
+ });
1360
+ /** Replaces socket.io `godview:activity` */
1361
+ const ActivityUpdatedEvent = Schema.Struct({
1362
+ type: Schema.Literal("activity.updated"),
1363
+ sequence: SequenceNumber,
1364
+ timestamp: Schema.String,
1365
+ payload: Schema.Struct({ events: Schema.Array(Schema.Unknown) })
1366
+ });
1367
+ /** Individual activity log entry — emitted by merge-agent, cloister, specialists (PAN-520) */
1368
+ const ActivityEntryEvent = Schema.Struct({
1369
+ type: Schema.Literal("activity.entry"),
1370
+ sequence: SequenceNumber,
1371
+ timestamp: Schema.String,
1372
+ payload: Schema.Struct({
1373
+ id: Schema.String,
1374
+ source: Schema.String,
1375
+ level: Schema.String,
1376
+ message: Schema.String,
1377
+ details: Schema.optional(Schema.String),
1378
+ issueId: Schema.optional(IssueId),
1379
+ /** Dashboard route the feed navigates to on click (e.g. /conv/<name>, /flywheel). */
1380
+ link: Schema.optional(Schema.String)
1381
+ })
1382
+ });
1383
+ /** Detailed activity log — auto-generated from domain state changes */
1384
+ const ActivityDetailedEvent = Schema.Struct({
1385
+ type: Schema.Literal("activity.detailed"),
1386
+ sequence: SequenceNumber,
1387
+ timestamp: Schema.String,
1388
+ payload: Schema.Struct({
1389
+ id: Schema.String,
1390
+ source: Schema.String,
1391
+ level: Schema.String,
1392
+ message: Schema.String,
1393
+ details: Schema.optional(Schema.String),
1394
+ issueId: Schema.optional(IssueId),
1395
+ triggeringEvent: Schema.optional(Schema.String)
1396
+ })
1397
+ });
1398
+ /** TTS activity log — upleveled utterances for text-to-speech */
1399
+ const ActivityTtsEvent = Schema.Struct({
1400
+ type: Schema.Literal("activity.tts"),
1401
+ sequence: SequenceNumber,
1402
+ timestamp: Schema.String,
1403
+ payload: Schema.Struct({
1404
+ id: Schema.String,
1405
+ utterance: Schema.String,
1406
+ priority: Schema.optional(Schema.Number),
1407
+ issueId: Schema.optional(IssueId),
1408
+ source: Schema.optional(Schema.String),
1409
+ eventType: Schema.optional(Schema.String)
1410
+ })
1411
+ });
1412
+ /** Dashboard is restarting (post-merge deploy, pan restart, etc.) (PAN-520) */
1413
+ const DashboardLifecycleStartedEvent = Schema.Struct({
1414
+ type: Schema.Literal("dashboard.lifecycle_started"),
1415
+ sequence: SequenceNumber,
1416
+ timestamp: Schema.String,
1417
+ payload: Schema.Struct({
1418
+ reason: Schema.String,
1419
+ issueId: Schema.optional(IssueId),
1420
+ trigger: Schema.String
1421
+ })
1422
+ });
1423
+ /** Dashboard restarted successfully after a lifecycle event (PAN-520) */
1424
+ const DashboardLifecycleCompletedEvent = Schema.Struct({
1425
+ type: Schema.Literal("dashboard.lifecycle_completed"),
1426
+ sequence: SequenceNumber,
1427
+ timestamp: Schema.String,
1428
+ payload: Schema.Struct({
1429
+ reason: Schema.String,
1430
+ issueId: Schema.optional(IssueId),
1431
+ durationMs: Schema.Number
1432
+ })
1433
+ });
1434
+ /** Dashboard restart failed (PAN-520) */
1435
+ const DashboardLifecycleFailedEvent = Schema.Struct({
1436
+ type: Schema.Literal("dashboard.lifecycle_failed"),
1437
+ sequence: SequenceNumber,
1438
+ timestamp: Schema.String,
1439
+ payload: Schema.Struct({
1440
+ reason: Schema.String,
1441
+ issueId: Schema.optional(IssueId),
1442
+ error: Schema.String
1443
+ })
1444
+ });
1445
+ /** Replaces socket.io `shadow:inference-update` */
1446
+ const ShadowInferenceUpdateEvent = Schema.Struct({
1447
+ type: Schema.Literal("shadow.inference_update"),
1448
+ sequence: SequenceNumber,
1449
+ timestamp: Schema.String,
1450
+ payload: Schema.Struct({
1451
+ issueId: IssueId,
1452
+ content: Schema.String
1453
+ })
1454
+ });
1455
+ /** New — workspace worktree created (before planning.started) */
1456
+ const WorkspaceCreatedEvent = Schema.Struct({
1457
+ type: Schema.Literal("workspace.created"),
1458
+ sequence: SequenceNumber,
1459
+ timestamp: Schema.String,
1460
+ payload: Schema.Struct({
1461
+ issueId: IssueId,
1462
+ workspacePath: Schema.String
1463
+ })
1464
+ });
1465
+ /** New — deep-wipe started (transitional state for UI spinner) */
1466
+ const WorkspaceWipeStartedEvent = Schema.Struct({
1467
+ type: Schema.Literal("workspace.wipe_started"),
1468
+ sequence: SequenceNumber,
1469
+ timestamp: Schema.String,
1470
+ payload: Schema.Struct({ issueId: IssueId })
1471
+ });
1472
+ /** New — deep-wipe completed, workspace fully destroyed */
1473
+ const WorkspaceDestroyedEvent = Schema.Struct({
1474
+ type: Schema.Literal("workspace.destroyed"),
1475
+ sequence: SequenceNumber,
1476
+ timestamp: Schema.String,
1477
+ payload: Schema.Struct({ issueId: IssueId })
1478
+ });
1479
+ /** New — cleanup-workspace completed, workspace directory removed */
1480
+ const WorkspaceDeletedEvent = Schema.Struct({
1481
+ type: Schema.Literal("workspace.deleted"),
1482
+ sequence: SequenceNumber,
1483
+ timestamp: Schema.String,
1484
+ payload: Schema.Struct({ issueId: IssueId })
1485
+ });
1486
+ /** New — planning aborted, workspace returned to idle state */
1487
+ const WorkspaceAbortedEvent = Schema.Struct({
1488
+ type: Schema.Literal("workspace.aborted"),
1489
+ sequence: SequenceNumber,
1490
+ timestamp: Schema.String,
1491
+ payload: Schema.Struct({
1492
+ issueId: IssueId,
1493
+ sessionName: Schema.optional(Schema.String)
1494
+ })
1495
+ });
1496
+ const MemoryObservationCreatedEvent = Schema.Struct({
1497
+ type: Schema.Literal("memory.observation_created"),
1498
+ sequence: SequenceNumber,
1499
+ timestamp: Schema.String,
1500
+ payload: Schema.Struct({ observation: MemoryObservation })
1501
+ });
1502
+ const MemoryStatusUpdatedEvent = Schema.Struct({
1503
+ type: Schema.Literal("memory.status_updated"),
1504
+ sequence: SequenceNumber,
1505
+ timestamp: Schema.String,
1506
+ payload: Schema.Struct({
1507
+ identity: Schema.Struct({
1508
+ projectId: Schema.String,
1509
+ workspaceId: Schema.String,
1510
+ issueId: IssueId
1511
+ }),
1512
+ status: MemoryStatus,
1513
+ previousStatus: Schema.optional(MemoryStatus)
1514
+ })
1515
+ });
1516
+ const MemoryRollupTriggeredEvent = Schema.Struct({
1517
+ type: Schema.Literal("memory.rollup_triggered"),
1518
+ sequence: SequenceNumber,
1519
+ timestamp: Schema.String,
1520
+ payload: Schema.Struct({
1521
+ projectId: Schema.String,
1522
+ workspaceId: Schema.String,
1523
+ issueId: IssueId,
1524
+ pendingCount: Schema.Number,
1525
+ turnIds: Schema.Array(Schema.String),
1526
+ threshold: Schema.Number
1527
+ })
1528
+ });
1529
+ const MemoryResetMarkerCreatedEvent = Schema.Struct({
1530
+ type: Schema.Literal("memory.reset_marker_created"),
1531
+ sequence: SequenceNumber,
1532
+ timestamp: Schema.String,
1533
+ payload: Schema.Struct({ marker: ResetMarker })
1534
+ });
1535
+ const MemoryHealthChangedEvent = Schema.Struct({
1536
+ type: Schema.Literal("memory.health_changed"),
1537
+ sequence: SequenceNumber,
1538
+ timestamp: Schema.String,
1539
+ payload: Schema.Struct({
1540
+ projectId: Schema.String,
1541
+ issueId: IssueId,
1542
+ status: Schema.Literals([
1543
+ "healthy",
1544
+ "degraded",
1545
+ "failing"
1546
+ ]),
1547
+ reason: Schema.NullOr(Schema.String),
1548
+ detail: Schema.optional(Schema.String),
1549
+ ragDecision: Schema.optional(RagDecision)
1550
+ })
1551
+ });
1552
+ /** New — cost event recorded in the store */
1553
+ const CostEventRecordedEvent = Schema.Struct({
1554
+ type: Schema.Literal("cost.event_recorded"),
1555
+ sequence: SequenceNumber,
1556
+ timestamp: Schema.String,
1557
+ payload: Schema.Struct({
1558
+ agentId: AgentId,
1559
+ issueId: IssueId,
1560
+ cost: Schema.Number,
1561
+ inputTokens: Schema.Number,
1562
+ outputTokens: Schema.Number
1563
+ })
1564
+ });
1565
+ /** Emitted (in-memory only, not persisted) when a Overdeck-native compaction starts or completes. */
1566
+ const ConversationCompactingChangedEvent = Schema.Struct({
1567
+ type: Schema.Literal("conversation.compacting_changed"),
1568
+ sequence: SequenceNumber,
1569
+ timestamp: Schema.String,
1570
+ payload: Schema.Struct({
1571
+ conversationName: Schema.String,
1572
+ compacting: Schema.Boolean
1573
+ })
1574
+ });
1575
+ /** Emitted (in-memory only) when a new conversation row is created, so the
1576
+ * sidebar list can refresh immediately instead of waiting for its poll tick. */
1577
+ const ConversationCreatedEvent = Schema.Struct({
1578
+ type: Schema.Literal("conversation.created"),
1579
+ sequence: SequenceNumber,
1580
+ timestamp: Schema.String,
1581
+ payload: Schema.Struct({ conversationName: Schema.String })
1582
+ });
1583
+ /** Emitted (in-memory only) when a PermissionRequest hook fires or resolves for a conversation. */
1584
+ const ConversationPermissionChangedEvent = Schema.Struct({
1585
+ type: Schema.Literal("conversation.permission_changed"),
1586
+ sequence: SequenceNumber,
1587
+ timestamp: Schema.String,
1588
+ payload: Schema.Struct({
1589
+ conversationName: Schema.String,
1590
+ waiting: Schema.Boolean,
1591
+ toolName: Schema.optional(Schema.String)
1592
+ })
1593
+ });
1594
+ /** Scan started */
1595
+ const ScanStartedEvent = Schema.Struct({
1596
+ type: Schema.Literal("scan.started"),
1597
+ sequence: SequenceNumber,
1598
+ timestamp: Schema.String,
1599
+ payload: Schema.Struct({
1600
+ mode: Schema.Literals([
1601
+ "targeted",
1602
+ "watched",
1603
+ "system"
1604
+ ]),
1605
+ dirs: Schema.Array(Schema.String)
1606
+ })
1607
+ });
1608
+ /** Scan progress tick */
1609
+ const ScanProgressEvent = Schema.Struct({
1610
+ type: Schema.Literal("scan.progress"),
1611
+ sequence: SequenceNumber,
1612
+ timestamp: Schema.String,
1613
+ payload: Schema.Struct({
1614
+ dirsProcessed: Schema.Number,
1615
+ dirsTotal: Schema.Number,
1616
+ sessionsFound: Schema.Number,
1617
+ elapsedMs: Schema.Number
1618
+ })
1619
+ });
1620
+ /** Scan completed */
1621
+ const ScanCompleteEvent = Schema.Struct({
1622
+ type: Schema.Literal("scan.complete"),
1623
+ sequence: SequenceNumber,
1624
+ timestamp: Schema.String,
1625
+ payload: Schema.Struct({
1626
+ inserted: Schema.Number,
1627
+ updated: Schema.Number,
1628
+ skipped: Schema.Number,
1629
+ errors: Schema.Number,
1630
+ durationMs: Schema.Number
1631
+ })
1632
+ });
1633
+ /** Per-session enrichment progress */
1634
+ const EnrichProgressEvent = Schema.Struct({
1635
+ type: Schema.Literal("enrich.progress"),
1636
+ sequence: SequenceNumber,
1637
+ timestamp: Schema.String,
1638
+ payload: Schema.Struct({
1639
+ sessionId: Schema.Number,
1640
+ level: Schema.Number,
1641
+ model: Schema.String,
1642
+ cost: Schema.Number,
1643
+ success: Schema.Boolean,
1644
+ error: Schema.optional(Schema.String)
1645
+ })
1646
+ });
1647
+ /** Enrichment batch completed */
1648
+ const EnrichCompleteEvent = Schema.Struct({
1649
+ type: Schema.Literal("enrich.complete"),
1650
+ sequence: SequenceNumber,
1651
+ timestamp: Schema.String,
1652
+ payload: Schema.Struct({
1653
+ processed: Schema.Number,
1654
+ totalCost: Schema.Number,
1655
+ failures: Schema.Number,
1656
+ durationMs: Schema.Number
1657
+ })
1658
+ });
1659
+ /** Per-session embedding progress */
1660
+ const EmbedProgressEvent = Schema.Struct({
1661
+ type: Schema.Literal("embed.progress"),
1662
+ sequence: SequenceNumber,
1663
+ timestamp: Schema.String,
1664
+ payload: Schema.Struct({
1665
+ sessionId: Schema.Number,
1666
+ model: Schema.String,
1667
+ success: Schema.Boolean,
1668
+ error: Schema.optional(Schema.String)
1669
+ })
1670
+ });
1671
+ /** All domain events — the shape streamed via subscribeDomainEvents RPC */
1672
+ const DomainEvent = Schema.Union([
1673
+ SystemHeartbeatEvent,
1674
+ AgentCreatedEvent,
1675
+ AgentEnrichmentChangedEvent,
1676
+ AgentStartedEvent,
1677
+ AgentStoppedEvent,
1678
+ AgentHeartbeatDeadEvent,
1679
+ WorkCompletedEvent,
1680
+ AgentCompletedEvent,
1681
+ ReviewApprovedEvent,
1682
+ TestPassedEvent,
1683
+ AgentStatusChangedEvent,
1684
+ AgentOutputReceivedEvent,
1685
+ AgentActivityChangedEvent,
1686
+ AgentThinkingStartedEvent,
1687
+ AgentThinkingStoppedEvent,
1688
+ AgentWaitingStartedEvent,
1689
+ AgentWaitingClearedEvent,
1690
+ AgentPermissionRequestedEvent,
1691
+ AgentPermissionResolvedEvent,
1692
+ AgentMessageReceivedEvent,
1693
+ AgentChannelReplyEvent,
1694
+ AgentModelSetEvent,
1695
+ AgentCurrentIssueSetEvent,
1696
+ AgentContextSaturationChangedEvent,
1697
+ AgentResolutionChangedEvent,
1698
+ AgentStateRestoredEvent,
1699
+ AgentTurnDiffCompletedEvent,
1700
+ PlanningStartedEvent,
1701
+ PlanningFailedEvent,
1702
+ PlanningSyncEvent,
1703
+ PlanItemStatusChangedEvent,
1704
+ PlanSubitemStatusChangedEvent,
1705
+ PlanItemsUnblockedEvent,
1706
+ PipelineStatusChangedEvent,
1707
+ MergeReadyEvent,
1708
+ ReviewStatusChangedEvent,
1709
+ PipelineReviewStartedEvent,
1710
+ PipelineReviewCompletedEvent,
1711
+ PipelineTestStartedEvent,
1712
+ PipelineTestCompletedEvent,
1713
+ OperatorInterventionEvent,
1714
+ SubstrateBugFiledEvent,
1715
+ ReviewReviewerStartedEvent,
1716
+ ReviewReviewerCompletedEvent,
1717
+ ReviewSpecialistTimedOutEvent,
1718
+ ReviewCoordinatorStartedEvent,
1719
+ ReviewCoordinatorDiedEvent,
1720
+ SpecialistStartedEvent,
1721
+ SpecialistCompletedEvent,
1722
+ SpecialistFailedEvent,
1723
+ ResourcesUpdatedEvent,
1724
+ SystemHealthSeverityChangedEvent,
1725
+ IssuesSnapshotEvent,
1726
+ IssuesUpdatedEvent,
1727
+ IssueStatusChangedEvent,
1728
+ ActivityUpdatedEvent,
1729
+ ActivityEntryEvent,
1730
+ ActivityDetailedEvent,
1731
+ ActivityTtsEvent,
1732
+ ShadowInferenceUpdateEvent,
1733
+ MemoryObservationCreatedEvent,
1734
+ MemoryStatusUpdatedEvent,
1735
+ MemoryRollupTriggeredEvent,
1736
+ MemoryResetMarkerCreatedEvent,
1737
+ MemoryHealthChangedEvent,
1738
+ CostEventRecordedEvent,
1739
+ WorkspaceCreatedEvent,
1740
+ WorkspaceWipeStartedEvent,
1741
+ WorkspaceDestroyedEvent,
1742
+ WorkspaceDeletedEvent,
1743
+ WorkspaceAbortedEvent,
1744
+ DashboardLifecycleStartedEvent,
1745
+ DashboardLifecycleCompletedEvent,
1746
+ DashboardLifecycleFailedEvent,
1747
+ ConversationCompactingChangedEvent,
1748
+ ConversationCreatedEvent,
1749
+ ConversationPermissionChangedEvent,
1750
+ ScanStartedEvent,
1751
+ ScanProgressEvent,
1752
+ ScanCompleteEvent,
1753
+ EnrichProgressEvent,
1754
+ EnrichCompleteEvent,
1755
+ EmbedProgressEvent
1756
+ ]);
1757
+ //#endregion
1758
+ //#region src/editor.ts
1759
+ const EDITORS = [
1760
+ {
1761
+ id: "cursor",
1762
+ label: "Cursor",
1763
+ command: "cursor",
1764
+ supportsGoto: true
1765
+ },
1766
+ {
1767
+ id: "windsurf",
1768
+ label: "Windsurf",
1769
+ command: "windsurf",
1770
+ supportsGoto: true
1771
+ },
1772
+ {
1773
+ id: "trae",
1774
+ label: "Trae",
1775
+ command: "trae",
1776
+ supportsGoto: true
1777
+ },
1778
+ {
1779
+ id: "kiro",
1780
+ label: "Kiro",
1781
+ command: "kiro",
1782
+ supportsGoto: true
1783
+ },
1784
+ {
1785
+ id: "vscode",
1786
+ label: "VS Code",
1787
+ command: "code",
1788
+ supportsGoto: true
1789
+ },
1790
+ {
1791
+ id: "vscode-insiders",
1792
+ label: "VS Code Insiders",
1793
+ command: "code-insiders",
1794
+ supportsGoto: true
1795
+ },
1796
+ {
1797
+ id: "vscodium",
1798
+ label: "VSCodium",
1799
+ command: "codium",
1800
+ supportsGoto: true
1801
+ },
1802
+ {
1803
+ id: "zed",
1804
+ label: "Zed",
1805
+ command: "zed",
1806
+ supportsGoto: false
1807
+ },
1808
+ {
1809
+ id: "antigravity",
1810
+ label: "Antigravity",
1811
+ command: "agy",
1812
+ supportsGoto: false
1813
+ },
1814
+ {
1815
+ id: "idea",
1816
+ label: "IntelliJ IDEA",
1817
+ command: "idea",
1818
+ supportsGoto: true
1819
+ },
1820
+ {
1821
+ id: "aqua",
1822
+ label: "Aqua",
1823
+ command: "aqua",
1824
+ supportsGoto: true
1825
+ },
1826
+ {
1827
+ id: "clion",
1828
+ label: "CLion",
1829
+ command: "clion",
1830
+ supportsGoto: true
1831
+ },
1832
+ {
1833
+ id: "datagrip",
1834
+ label: "DataGrip",
1835
+ command: "datagrip",
1836
+ supportsGoto: true
1837
+ },
1838
+ {
1839
+ id: "dataspell",
1840
+ label: "DataSpell",
1841
+ command: "dataspell",
1842
+ supportsGoto: true
1843
+ },
1844
+ {
1845
+ id: "goland",
1846
+ label: "GoLand",
1847
+ command: "goland",
1848
+ supportsGoto: true
1849
+ },
1850
+ {
1851
+ id: "phpstorm",
1852
+ label: "PhpStorm",
1853
+ command: "phpstorm",
1854
+ supportsGoto: true
1855
+ },
1856
+ {
1857
+ id: "pycharm",
1858
+ label: "PyCharm",
1859
+ command: "pycharm",
1860
+ supportsGoto: true
1861
+ },
1862
+ {
1863
+ id: "rider",
1864
+ label: "Rider",
1865
+ command: "rider",
1866
+ supportsGoto: true
1867
+ },
1868
+ {
1869
+ id: "rubymine",
1870
+ label: "RubyMine",
1871
+ command: "rubymine",
1872
+ supportsGoto: true
1873
+ },
1874
+ {
1875
+ id: "rustrover",
1876
+ label: "RustRover",
1877
+ command: "rustrover",
1878
+ supportsGoto: true
1879
+ },
1880
+ {
1881
+ id: "webstorm",
1882
+ label: "WebStorm",
1883
+ command: "webstorm",
1884
+ supportsGoto: true
1885
+ },
1886
+ {
1887
+ id: "file-manager",
1888
+ label: "File Manager",
1889
+ command: null,
1890
+ supportsGoto: false
1891
+ }
1892
+ ];
1893
+ const EditorIdSchema = Schema.Literals([
1894
+ "cursor",
1895
+ "windsurf",
1896
+ "trae",
1897
+ "kiro",
1898
+ "vscode",
1899
+ "vscode-insiders",
1900
+ "vscodium",
1901
+ "zed",
1902
+ "antigravity",
1903
+ "idea",
1904
+ "aqua",
1905
+ "clion",
1906
+ "datagrip",
1907
+ "dataspell",
1908
+ "goland",
1909
+ "phpstorm",
1910
+ "pycharm",
1911
+ "rider",
1912
+ "rubymine",
1913
+ "rustrover",
1914
+ "webstorm",
1915
+ "file-manager"
1916
+ ]);
1917
+ const OpenInEditorInput = Schema.Struct({
1918
+ cwd: Schema.String,
1919
+ editor: EditorIdSchema
1920
+ });
1921
+ //#endregion
1922
+ //#region src/flywheel.ts
1923
+ const FlywheelRunId = Schema.String.check(Schema.isPattern(/^RUN-\d+$/));
1924
+ const FlywheelHttpUrl = Schema.String.check(Schema.isPattern(/^https?:\/\/\S+$/i));
1925
+ const FlywheelHarness = Schema.Literals([
1926
+ "claude-code",
1927
+ "pi",
1928
+ "codex"
1929
+ ]);
1930
+ const FlywheelEffort = Schema.Literals([
1931
+ "low",
1932
+ "medium",
1933
+ "high",
1934
+ "xhigh",
1935
+ "max"
1936
+ ]);
1937
+ const FlywheelOrchestrator = Schema.Struct({
1938
+ harness: FlywheelHarness,
1939
+ model: Schema.String,
1940
+ effort: FlywheelEffort,
1941
+ ctxPercent: Schema.Number
1942
+ });
1943
+ const FlywheelHeadline = Schema.Struct({
1944
+ bugsFixed: Schema.Number,
1945
+ swarmItemsMerged: Schema.Number,
1946
+ swarmItemsTotal: Schema.Number,
1947
+ prsMerged: Schema.Number,
1948
+ awaitingUat: Schema.Number
1949
+ });
1950
+ /**
1951
+ * Pipeline verbs and the merge-queue contract (PAN-1736):
1952
+ *
1953
+ * `computeMergeQueue` treats an item as AT THE MERGE GATE when its verb is
1954
+ * "shipping" OR "merging" — both are natural emissions for a merge-ready
1955
+ * issue, and orchestrators have used each. Emitting any OTHER verb for an
1956
+ * issue that is ready_for_merge in the review DB silently drops it from the
1957
+ * merge queue and from UAT batch assembly (PAN-1737). RUN-18 shipped an
1958
+ * empty queue with five ready issues exactly this way.
1959
+ *
1960
+ * If you add a verb that can describe a merge-ready issue, extend
1961
+ * MERGE_GATE_VERBS in src/lib/flywheel-merge-order.ts in the same change.
1962
+ */
1963
+ const FlywheelPipelineVerb = Schema.Literals([
1964
+ "planning",
1965
+ "working",
1966
+ "reviewing",
1967
+ "testing",
1968
+ "shipping",
1969
+ "merging",
1970
+ "blocked",
1971
+ "parked"
1972
+ ]);
1973
+ const FlywheelPipelineStatus = Schema.Literals([
1974
+ "queued",
1975
+ "running",
1976
+ "blocked",
1977
+ "passed",
1978
+ "failed",
1979
+ "merged",
1980
+ "parked"
1981
+ ]);
1982
+ const FlywheelPipelineItem = Schema.Struct({
1983
+ issueId: Schema.String,
1984
+ title: Schema.String,
1985
+ verb: FlywheelPipelineVerb,
1986
+ status: FlywheelPipelineStatus,
1987
+ progressPercent: Schema.optional(Schema.Number),
1988
+ agentId: Schema.optional(Schema.String),
1989
+ pr: Schema.optional(Schema.Number),
1990
+ mergeOrder: Schema.optional(Schema.Number),
1991
+ conflictsWith: Schema.optional(Schema.Array(Schema.String))
1992
+ });
1993
+ const FlywheelSubstrateBugStatus = Schema.Literals([
1994
+ "filed",
1995
+ "fixed",
1996
+ "workaround"
1997
+ ]);
1998
+ const FlywheelSubstrateBug = Schema.Struct({
1999
+ issueId: Schema.String,
2000
+ title: Schema.String,
2001
+ status: FlywheelSubstrateBugStatus,
2002
+ commitSha: Schema.optional(Schema.String),
2003
+ url: Schema.optional(FlywheelHttpUrl)
2004
+ });
2005
+ const FlywheelAgentStatus = Schema.Literals([
2006
+ "starting",
2007
+ "running",
2008
+ "waiting",
2009
+ "idle",
2010
+ "stopped",
2011
+ "error"
2012
+ ]);
2013
+ const FlywheelAgent = Schema.Struct({
2014
+ id: Schema.String,
2015
+ label: Schema.String,
2016
+ status: FlywheelAgentStatus,
2017
+ issueId: Schema.optional(Schema.String),
2018
+ role: Schema.optional(Schema.String),
2019
+ model: Schema.optional(Schema.String),
2020
+ ctxPercent: Schema.optional(Schema.Number),
2021
+ currentAction: Schema.optional(Schema.String)
2022
+ });
2023
+ const FlywheelParkedItem = Schema.Struct({
2024
+ issueId: Schema.String,
2025
+ title: Schema.String,
2026
+ reason: Schema.String,
2027
+ parkedAt: Schema.optional(Schema.String)
2028
+ });
2029
+ const FlywheelSuggestionAction = Schema.Literals([
2030
+ "start",
2031
+ "resume",
2032
+ "plan",
2033
+ "review",
2034
+ "merge",
2035
+ "unblock",
2036
+ "park",
2037
+ "investigate",
2038
+ "wait"
2039
+ ]);
2040
+ const FlywheelSuggestionPriority = Schema.Literals([
2041
+ "urgent",
2042
+ "high",
2043
+ "medium",
2044
+ "low"
2045
+ ]);
2046
+ const FlywheelSuggestion = Schema.Struct({
2047
+ action: FlywheelSuggestionAction,
2048
+ issueId: Schema.optional(Schema.String),
2049
+ rationale: Schema.String,
2050
+ priority: FlywheelSuggestionPriority
2051
+ });
2052
+ const FlywheelSystemStatus = Schema.Struct({
2053
+ mainHead: Schema.String,
2054
+ ramUsedMb: Schema.Number,
2055
+ ramTotalMb: Schema.Number,
2056
+ swapUsedMb: Schema.Number,
2057
+ swapTotalMb: Schema.Number,
2058
+ agentsActive: Schema.Number,
2059
+ agentsCap: Schema.Number
2060
+ });
2061
+ const FlywheelStatus = Schema.Struct({
2062
+ runId: FlywheelRunId,
2063
+ startedAt: Schema.String,
2064
+ elapsedMs: Schema.Number,
2065
+ orchestrator: FlywheelOrchestrator,
2066
+ headline: FlywheelHeadline,
2067
+ activePipeline: Schema.Array(FlywheelPipelineItem),
2068
+ substrateBugs: Schema.Array(FlywheelSubstrateBug),
2069
+ agents: Schema.Array(FlywheelAgent),
2070
+ parked: Schema.Array(FlywheelParkedItem),
2071
+ suggestions: Schema.Array(FlywheelSuggestion).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
2072
+ system: FlywheelSystemStatus,
2073
+ openQuestions: Schema.Array(Schema.String),
2074
+ ticks: Schema.Number,
2075
+ lastTickAt: Schema.String
2076
+ });
2077
+ //#endregion
2078
+ //#region src/rpc.ts
2079
+ const WS_METHODS = {
2080
+ scanConversations: "pan.scanConversations",
2081
+ searchConversations: "pan.searchConversations",
2082
+ listDiscoveredSessions: "pan.listDiscoveredSessions",
2083
+ getDiscoveredSession: "pan.getDiscoveredSession",
2084
+ enrichSessions: "pan.enrichSessions",
2085
+ embedSessions: "pan.embedSessions",
2086
+ getConversationCost: "pan.getConversationCost",
2087
+ getConversationCostByWorkspace: "pan.getConversationCostByWorkspace",
2088
+ getConversationStats: "pan.getConversationStats",
2089
+ subscribeDomainEvents: "pan.subscribeDomainEvents",
2090
+ subscribeIssueEvents: "pan.subscribeIssueEvents",
2091
+ subscribeTerminal: "pan.subscribeTerminal",
2092
+ subscribeAgentOutput: "pan.subscribeAgentOutput",
2093
+ subscribeConversationMessages: "pan.subscribeConversationMessages",
2094
+ subscribeProjectSessionTree: "pan.subscribeProjectSessionTree",
2095
+ subscribeFlywheelStatus: "pan.subscribeFlywheelStatus",
2096
+ getSnapshot: "pan.getSnapshot",
2097
+ replayEvents: "pan.replayEvents",
2098
+ getWorkspaceDetail: "pan.getWorkspaceDetail",
2099
+ readWorkspaceFile: "pan.readWorkspaceFile",
2100
+ resolveFilePathExists: "pan.resolveFilePathExists",
2101
+ terminalOpen: "pan.terminalOpen",
2102
+ terminalWrite: "pan.terminalWrite",
2103
+ terminalResize: "pan.terminalResize",
2104
+ terminalClose: "pan.terminalClose",
2105
+ startPlanning: "pan.startPlanning",
2106
+ startAgent: "pan.startAgent",
2107
+ deepWipe: "pan.deepWipe",
2108
+ sendTerminalInput: "pan.sendTerminalInput",
2109
+ resizeTerminal: "pan.resizeTerminal",
2110
+ shellOpenInEditor: "pan.shellOpenInEditor",
2111
+ getAvailableEditors: "pan.getAvailableEditors"
2112
+ };
2113
+ var PanRpcError = class extends Schema.TaggedErrorClass()("PanRpcError", {
2114
+ message: Schema.String,
2115
+ code: Schema.optional(Schema.String)
2116
+ }) {};
2117
+ const TerminalOutput = Schema.Struct({
2118
+ sessionName: Schema.String,
2119
+ data: Schema.String
2120
+ });
2121
+ const AgentOutput = Schema.Struct({
2122
+ agentId: Schema.String,
2123
+ line: Schema.String
2124
+ });
2125
+ const ProposedPlan = Schema.Struct({
2126
+ id: Schema.String,
2127
+ plan: Schema.String,
2128
+ planFilePath: Schema.optional(Schema.String),
2129
+ status: Schema.Literals([
2130
+ "pending",
2131
+ "approved",
2132
+ "rejected"
2133
+ ]),
2134
+ createdAt: Schema.String,
2135
+ resolvedAt: Schema.optional(Schema.String)
2136
+ });
2137
+ const CompactBoundary = Schema.Struct({
2138
+ id: Schema.String,
2139
+ timestamp: Schema.String,
2140
+ trigger: Schema.optional(Schema.String),
2141
+ preTokens: Schema.optional(Schema.Number),
2142
+ model: Schema.optional(Schema.String)
2143
+ });
2144
+ const ContextUsage = Schema.Struct({
2145
+ activeBytes: Schema.Number,
2146
+ estimatedTokens: Schema.Number,
2147
+ contextWindow: Schema.Number,
2148
+ percentUsed: Schema.Number,
2149
+ /**
2150
+ * Optional breakdown of the last assistant turn — surfaced by the click
2151
+ * popover on the ContextWindowMeter so users can see WHY the meter says
2152
+ * what it does. Computed by `computeContextUsage()` from JSONL `usage`.
2153
+ */
2154
+ lastInputTokens: Schema.optional(Schema.Number),
2155
+ lastCacheReadTokens: Schema.optional(Schema.Number),
2156
+ lastCacheCreationTokens: Schema.optional(Schema.Number),
2157
+ /** Highest input_tokens ever observed in this window — triggers 1M detection. */
2158
+ maxObservedInputTokens: Schema.optional(Schema.Number),
2159
+ /** Model the most recent assistant turn ran under (from JSONL). */
2160
+ lastModel: Schema.optional(Schema.NullOr(Schema.String)),
2161
+ /** ISO timestamp of the most recent assistant turn. */
2162
+ lastTurnAt: Schema.optional(Schema.NullOr(Schema.String))
2163
+ });
2164
+ const ChatMessage = Schema.Struct({
2165
+ id: Schema.String,
2166
+ role: Schema.Literals([
2167
+ "user",
2168
+ "assistant",
2169
+ "system"
2170
+ ]),
2171
+ text: Schema.String,
2172
+ turnId: Schema.optional(Schema.String),
2173
+ createdAt: Schema.String,
2174
+ completedAt: Schema.optional(Schema.String),
2175
+ streaming: Schema.optional(Schema.Boolean),
2176
+ sequence: Schema.optional(Schema.Number)
2177
+ });
2178
+ const WorkLogEntry = Schema.Struct({
2179
+ id: Schema.String,
2180
+ createdAt: Schema.String,
2181
+ label: Schema.String,
2182
+ detail: Schema.optional(Schema.String),
2183
+ /** Tool result output (populated when tool_result is received). */
2184
+ result: Schema.optional(Schema.String),
2185
+ command: Schema.optional(Schema.String),
2186
+ changedFiles: Schema.optional(Schema.Array(Schema.String)),
2187
+ tone: Schema.Literals([
2188
+ "thinking",
2189
+ "tool",
2190
+ "info",
2191
+ "error"
2192
+ ]),
2193
+ toolTitle: Schema.optional(Schema.String),
2194
+ /** Raw tool_use input dict — drives per-tool expanded rendering (PAN-1459). */
2195
+ toolInput: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
2196
+ sequence: Schema.optional(Schema.Number)
2197
+ });
2198
+ const ConversationEvent = Schema.Union([Schema.Struct({
2199
+ kind: Schema.Literal("messages"),
2200
+ messages: Schema.Array(ChatMessage),
2201
+ workLog: Schema.Array(WorkLogEntry),
2202
+ streaming: Schema.Boolean,
2203
+ snapshot: Schema.optional(Schema.Boolean),
2204
+ proposedPlan: Schema.optional(ProposedPlan),
2205
+ compactBoundaries: Schema.optional(Schema.Array(CompactBoundary)),
2206
+ contextUsage: Schema.optional(Schema.NullOr(ContextUsage))
2207
+ }), Schema.Struct({ kind: Schema.Literal("discovering") })]);
2208
+ const SessionTreeDelta = Schema.Struct({
2209
+ kind: Schema.Literals([
2210
+ "session_added",
2211
+ "session_removed",
2212
+ "presence_changed",
2213
+ "status_changed"
2214
+ ]),
2215
+ issueId: Schema.String,
2216
+ sessionId: Schema.String,
2217
+ presence: Schema.optional(SessionNodePresence),
2218
+ status: Schema.optional(AgentStatus),
2219
+ timestamp: Schema.String
2220
+ });
2221
+ /** 1. Subscribe to the live domain event stream (stream) */
2222
+ const SubscribeDomainEventsRpc = Rpc.make(WS_METHODS.subscribeDomainEvents, {
2223
+ payload: Schema.Struct({}),
2224
+ success: DomainEvent,
2225
+ stream: true
2226
+ });
2227
+ /** 1b. Subscribe to the live domain event stream for one issue (stream) */
2228
+ const SubscribeIssueEventsRpc = Rpc.make(WS_METHODS.subscribeIssueEvents, {
2229
+ payload: Schema.Struct({ issueId: IssueId }),
2230
+ success: DomainEvent,
2231
+ stream: true
2232
+ });
2233
+ /** 2. Subscribe to raw terminal output for a tmux session (stream) */
2234
+ const SubscribeTerminalRpc = Rpc.make(WS_METHODS.subscribeTerminal, {
2235
+ payload: Schema.Struct({
2236
+ sessionName: Schema.String,
2237
+ cols: Schema.Number,
2238
+ rows: Schema.Number
2239
+ }),
2240
+ success: TerminalOutput,
2241
+ error: PanRpcError,
2242
+ stream: true
2243
+ });
2244
+ /** 3. Subscribe to buffered agent stdout (stream) */
2245
+ const SubscribeAgentOutputRpc = Rpc.make(WS_METHODS.subscribeAgentOutput, {
2246
+ payload: Schema.Struct({ agentId: Schema.String }),
2247
+ success: AgentOutput,
2248
+ error: PanRpcError,
2249
+ stream: true
2250
+ });
2251
+ /** 4. Get current dashboard snapshot (unary) */
2252
+ const GetSnapshotRpc = Rpc.make(WS_METHODS.getSnapshot, {
2253
+ payload: Schema.Struct({}),
2254
+ success: DashboardSnapshot,
2255
+ error: PanRpcError
2256
+ });
2257
+ /** 5. Replay events since a given sequence number (unary) */
2258
+ const ReplayEventsRpc = Rpc.make(WS_METHODS.replayEvents, {
2259
+ payload: Schema.Struct({ fromSequence: SequenceNumber }),
2260
+ success: Schema.Array(DomainEvent),
2261
+ error: PanRpcError
2262
+ });
2263
+ /** 6. Open a terminal session / attach PTY (unary) */
2264
+ const TerminalOpenRpc = Rpc.make(WS_METHODS.terminalOpen, {
2265
+ payload: Schema.Struct({
2266
+ sessionName: Schema.String,
2267
+ cols: Schema.Number,
2268
+ rows: Schema.Number
2269
+ }),
2270
+ success: Schema.Struct({ sessionName: Schema.String }),
2271
+ error: PanRpcError
2272
+ });
2273
+ /** 7. Write data to a terminal session (unary) */
2274
+ const TerminalWriteRpc = Rpc.make(WS_METHODS.terminalWrite, {
2275
+ payload: Schema.Struct({
2276
+ sessionName: Schema.String,
2277
+ data: Schema.String
2278
+ }),
2279
+ error: PanRpcError
2280
+ });
2281
+ /** 8. Resize a terminal session (unary) */
2282
+ const TerminalResizeRpc = Rpc.make(WS_METHODS.terminalResize, {
2283
+ payload: Schema.Struct({
2284
+ sessionName: Schema.String,
2285
+ cols: Schema.Number,
2286
+ rows: Schema.Number
2287
+ }),
2288
+ error: PanRpcError
2289
+ });
2290
+ /** 9. Close a terminal session (unary) */
2291
+ const TerminalCloseRpc = Rpc.make(WS_METHODS.terminalClose, {
2292
+ payload: Schema.Struct({ sessionName: Schema.String }),
2293
+ error: PanRpcError
2294
+ });
2295
+ /** 10. Get batched workspace detail (unary) — replaces 5 separate HTTP calls */
2296
+ const GetWorkspaceDetailRpc = Rpc.make(WS_METHODS.getWorkspaceDetail, {
2297
+ payload: Schema.Struct({ issueId: IssueId }),
2298
+ success: WorkspaceDetail,
2299
+ error: PanRpcError
2300
+ });
2301
+ const ReadWorkspaceFileInput = Schema.Struct({
2302
+ issueId: IssueId,
2303
+ relativePath: Schema.String,
2304
+ line: Schema.optional(Schema.Number),
2305
+ contextLines: Schema.optional(Schema.Number)
2306
+ });
2307
+ const ReadWorkspaceFileResult = Schema.Struct({
2308
+ text: Schema.String,
2309
+ lang: Schema.String,
2310
+ truncated: Schema.Boolean,
2311
+ totalLines: Schema.Number
2312
+ });
2313
+ /** 10b. Read a workspace file for inline previews (unary) */
2314
+ const ReadWorkspaceFileRpc = Rpc.make(WS_METHODS.readWorkspaceFile, {
2315
+ payload: ReadWorkspaceFileInput,
2316
+ success: ReadWorkspaceFileResult,
2317
+ error: PanRpcError
2318
+ });
2319
+ /**
2320
+ * 10c. Resolve whether a file path candidate exists on disk (PAN-1457).
2321
+ *
2322
+ * Used by MarkdownFileLink to decide whether a path-like token in chat
2323
+ * markdown should render as a clickable chip. Returns exists=false for
2324
+ * phantom paths (`conv/2209`, `users/foo`) and exists=true with kind for
2325
+ * real files and directories (including bare directory references like
2326
+ * `src/components/Foo` that the regex heuristic alone cannot validate).
2327
+ *
2328
+ * Security:
2329
+ * - Pure stat — never reads file contents, never enumerates directories.
2330
+ * - Relative paths resolve under cwd; absolute paths are statted directly.
2331
+ * - Caller is responsible for sending only paths surfaced from their own
2332
+ * conversation context; the resolver does not gate by issueId because
2333
+ * chat happens outside any single workspace.
2334
+ */
2335
+ const ResolveFilePathExistsInput = Schema.Struct({
2336
+ cwd: Schema.String,
2337
+ path: Schema.String
2338
+ });
2339
+ const ResolveFilePathExistsResult = Schema.Struct({
2340
+ exists: Schema.Boolean,
2341
+ kind: Schema.NullOr(Schema.Literals(["file", "dir"]))
2342
+ });
2343
+ const ResolveFilePathExistsRpc = Rpc.make(WS_METHODS.resolveFilePathExists, {
2344
+ payload: ResolveFilePathExistsInput,
2345
+ success: ResolveFilePathExistsResult,
2346
+ error: PanRpcError
2347
+ });
2348
+ /** 11. Start planning for an issue (command) */
2349
+ const StartPlanningRpc = Rpc.make(WS_METHODS.startPlanning, {
2350
+ payload: Schema.Struct({
2351
+ issueId: IssueId,
2352
+ options: Schema.optional(Schema.Unknown)
2353
+ }),
2354
+ success: Schema.Struct({ queued: Schema.Boolean }),
2355
+ error: PanRpcError
2356
+ });
2357
+ /** 12. Start a work agent for an issue (command) */
2358
+ const StartAgentRpc = Rpc.make(WS_METHODS.startAgent, {
2359
+ payload: Schema.Struct({
2360
+ issueId: IssueId,
2361
+ options: Schema.optional(Schema.Unknown)
2362
+ }),
2363
+ success: Schema.Struct({ agentId: Schema.String }),
2364
+ error: PanRpcError
2365
+ });
2366
+ /** 13. Deep-wipe a workspace (destructive command — requires explicit confirmation) */
2367
+ const DeepWipeRpc = Rpc.make(WS_METHODS.deepWipe, {
2368
+ payload: Schema.Struct({
2369
+ issueId: IssueId,
2370
+ deleteWorkspace: Schema.optional(Schema.Boolean)
2371
+ }),
2372
+ success: Schema.Struct({ wiped: Schema.Boolean }),
2373
+ error: PanRpcError
2374
+ });
2375
+ /** 14. Send input to a terminal session (command) */
2376
+ const SendTerminalInputRpc = Rpc.make(WS_METHODS.sendTerminalInput, {
2377
+ payload: Schema.Struct({
2378
+ sessionName: Schema.String,
2379
+ data: Schema.String
2380
+ }),
2381
+ error: PanRpcError
2382
+ });
2383
+ /** 15. Resize a terminal session (command) */
2384
+ const ResizeTerminalRpc = Rpc.make(WS_METHODS.resizeTerminal, {
2385
+ payload: Schema.Struct({
2386
+ sessionName: Schema.String,
2387
+ cols: Schema.Number,
2388
+ rows: Schema.Number
2389
+ }),
2390
+ error: PanRpcError
2391
+ });
2392
+ /** 16. Subscribe to structured conversation messages from a JSONL session file (stream, PAN-451) */
2393
+ const SubscribeConversationMessagesRpc = Rpc.make(WS_METHODS.subscribeConversationMessages, {
2394
+ payload: Schema.Struct({ conversationName: Schema.String }),
2395
+ success: ConversationEvent,
2396
+ error: PanRpcError,
2397
+ stream: true
2398
+ });
2399
+ /** 17. Subscribe to live session tree deltas for a project (stream, PAN-821) */
2400
+ const SubscribeProjectSessionTreeRpc = Rpc.make(WS_METHODS.subscribeProjectSessionTree, {
2401
+ payload: Schema.Struct({ projectKey: Schema.String }),
2402
+ success: SessionTreeDelta,
2403
+ error: PanRpcError,
2404
+ stream: true
2405
+ });
2406
+ /** 18. Subscribe to latest Flywheel status snapshots (stream) */
2407
+ const SubscribeFlywheelStatusRpc = Rpc.make(WS_METHODS.subscribeFlywheelStatus, {
2408
+ payload: Schema.Struct({}),
2409
+ success: Schema.NullOr(FlywheelStatus),
2410
+ error: PanRpcError,
2411
+ stream: true
2412
+ });
2413
+ /** 19. Open a workspace in an editor (PAN-966) */
2414
+ const ShellOpenInEditorRpc = Rpc.make(WS_METHODS.shellOpenInEditor, {
2415
+ payload: OpenInEditorInput,
2416
+ error: PanRpcError
2417
+ });
2418
+ /** 19. Get available (installed) editors (PAN-966) */
2419
+ const GetAvailableEditorsRpc = Rpc.make(WS_METHODS.getAvailableEditors, {
2420
+ success: Schema.Struct({
2421
+ editors: Schema.Array(EditorIdSchema),
2422
+ /**
2423
+ * Default cwd to open when no project/workspace is selected — the
2424
+ * "devroot" fallback. `~/Projects` if it exists, else `$HOME`.
2425
+ */
2426
+ defaultCwd: Schema.String
2427
+ }),
2428
+ error: PanRpcError
2429
+ });
2430
+ const DiscoveredSessionListResult = Schema.Struct({
2431
+ sessions: Schema.Array(DiscoveredSessionSnapshot),
2432
+ count: Schema.Number,
2433
+ total: Schema.Number
2434
+ });
2435
+ const DiscoveredSessionSearchResult = Schema.Struct({
2436
+ sessions: Schema.Array(DiscoveredSessionSnapshot),
2437
+ total: Schema.Number,
2438
+ mode: Schema.String,
2439
+ durationMs: Schema.Number,
2440
+ error: Schema.optional(Schema.String)
2441
+ });
2442
+ const DiscoveredSessionStatsResult = Schema.Struct({
2443
+ total: Schema.Number,
2444
+ enriched: Schema.Number,
2445
+ embedded: Schema.Number,
2446
+ managedCount: Schema.Number,
2447
+ embeddingModels: Schema.optional(Schema.Array(Schema.Struct({
2448
+ model: Schema.String,
2449
+ embedded: Schema.Number
2450
+ })))
2451
+ });
2452
+ const ConversationCostTotals = Schema.Struct({
2453
+ sessionCount: Schema.Number,
2454
+ totalCost: Schema.Number,
2455
+ totalTokensIn: Schema.Number,
2456
+ totalTokensOut: Schema.Number
2457
+ });
2458
+ /** Scan conversations (trigger discovery) */
2459
+ const ScanConversationsRpc = Rpc.make(WS_METHODS.scanConversations, {
2460
+ payload: Schema.Struct({
2461
+ mode: Schema.Literals([
2462
+ "targeted",
2463
+ "watched",
2464
+ "system"
2465
+ ]),
2466
+ dirs: Schema.optional(Schema.Array(Schema.String)),
2467
+ dryRun: Schema.optional(Schema.Boolean)
2468
+ }),
2469
+ success: ScanResult,
2470
+ error: PanRpcError
2471
+ });
2472
+ /** Search discovered sessions with filters + optional FTS query */
2473
+ const SearchConversationsRpc = Rpc.make(WS_METHODS.searchConversations, {
2474
+ payload: ConversationFilter,
2475
+ success: DiscoveredSessionSearchResult,
2476
+ error: PanRpcError
2477
+ });
2478
+ /** List discovered sessions (recent, with optional managed/unmanaged filter) */
2479
+ const ListDiscoveredSessionsRpc = Rpc.make(WS_METHODS.listDiscoveredSessions, {
2480
+ payload: ConversationFilter,
2481
+ success: DiscoveredSessionListResult,
2482
+ error: PanRpcError
2483
+ });
2484
+ /** Get a single discovered session by ID */
2485
+ const GetDiscoveredSessionRpc = Rpc.make(WS_METHODS.getDiscoveredSession, {
2486
+ payload: Schema.Struct({ id: Schema.Number }),
2487
+ success: DiscoveredSessionSnapshot,
2488
+ error: PanRpcError
2489
+ });
2490
+ /** Enrich sessions by ID or filter */
2491
+ const EnrichSessionsRpc = Rpc.make(WS_METHODS.enrichSessions, {
2492
+ payload: Schema.Struct({
2493
+ ids: Schema.optional(Schema.Array(Schema.Number)),
2494
+ filter: Schema.optional(ConversationFilter),
2495
+ level: Schema.Literals([
2496
+ 1,
2497
+ 2,
2498
+ 3
2499
+ ]),
2500
+ model: Schema.optional(Schema.String),
2501
+ fullTranscript: Schema.optional(Schema.Boolean),
2502
+ customPrompt: Schema.optional(Schema.String),
2503
+ upgrade: Schema.optional(Schema.Boolean),
2504
+ limit: Schema.optional(Schema.Number),
2505
+ confirmed: Schema.optional(Schema.Boolean),
2506
+ force: Schema.optional(Schema.Boolean)
2507
+ }),
2508
+ success: Schema.Struct({
2509
+ processed: Schema.Number,
2510
+ totalCost: Schema.Number,
2511
+ failures: Schema.Number
2512
+ }),
2513
+ error: PanRpcError
2514
+ });
2515
+ /** Generate or update embeddings for enriched sessions */
2516
+ const EmbedSessionsRpc = Rpc.make(WS_METHODS.embedSessions, {
2517
+ payload: Schema.Struct({
2518
+ ids: Schema.optional(Schema.Array(Schema.Number)),
2519
+ regenerate: Schema.optional(Schema.Boolean)
2520
+ }),
2521
+ success: Schema.Struct({
2522
+ total: Schema.Number,
2523
+ embedded: Schema.Number,
2524
+ model: Schema.String
2525
+ }),
2526
+ error: PanRpcError
2527
+ });
2528
+ /** Aggregate cost totals for discovered sessions */
2529
+ const GetConversationCostRpc = Rpc.make(WS_METHODS.getConversationCost, {
2530
+ payload: ConversationFilter,
2531
+ success: ConversationCostTotals,
2532
+ error: PanRpcError
2533
+ });
2534
+ /** Aggregate workspace cost totals for the full filtered corpus */
2535
+ const GetConversationCostByWorkspaceRpc = Rpc.make(WS_METHODS.getConversationCostByWorkspace, {
2536
+ payload: ConversationFilter,
2537
+ success: ConversationCostSummary,
2538
+ error: PanRpcError
2539
+ });
2540
+ /** Discovery index statistics */
2541
+ const GetConversationStatsRpc = Rpc.make(WS_METHODS.getConversationStats, {
2542
+ payload: Schema.Struct({}),
2543
+ success: DiscoveredSessionStatsResult,
2544
+ error: PanRpcError
2545
+ });
2546
+ /** All Overdeck WebSocket RPC methods */
2547
+ const PanRpcGroup = RpcGroup.make(SubscribeDomainEventsRpc, SubscribeIssueEventsRpc, SubscribeTerminalRpc, SubscribeAgentOutputRpc, GetSnapshotRpc, ReplayEventsRpc, GetWorkspaceDetailRpc, ReadWorkspaceFileRpc, ResolveFilePathExistsRpc, TerminalOpenRpc, TerminalWriteRpc, TerminalResizeRpc, TerminalCloseRpc, StartPlanningRpc, StartAgentRpc, DeepWipeRpc, SendTerminalInputRpc, ResizeTerminalRpc, SubscribeConversationMessagesRpc, SubscribeProjectSessionTreeRpc, SubscribeFlywheelStatusRpc, ShellOpenInEditorRpc, GetAvailableEditorsRpc, ScanConversationsRpc, SearchConversationsRpc, ListDiscoveredSessionsRpc, GetDiscoveredSessionRpc, EnrichSessionsRpc, EmbedSessionsRpc, GetConversationCostRpc, GetConversationCostByWorkspaceRpc, GetConversationStatsRpc);
2548
+ //#endregion
2549
+ //#region src/event-reducers.ts
2550
+ const INITIAL_READ_MODEL_STATE = {
2551
+ sequence: 0,
2552
+ agentsById: {},
2553
+ agentRuntimeById: {},
2554
+ reviewStatusByIssueId: {},
2555
+ resources: null,
2556
+ agentOutputById: {},
2557
+ issuesRaw: [],
2558
+ recentActivity: [],
2559
+ detailedActivity: [],
2560
+ ttsActivity: [],
2561
+ shadowInferenceByIssueId: {},
2562
+ turnDiffSummariesByAgentId: {},
2563
+ channelPermissionRequestsById: {},
2564
+ channelPermissionRequestIdsByAgentId: {},
2565
+ resolvedChannelPermissionDecisionsById: {},
2566
+ resolvedChannelPermissionDecisionIdsByAgentId: {},
2567
+ conversationsCompactingByName: {},
2568
+ conversationsAwaitingPermissionByName: {},
2569
+ conversationsListRevision: 0,
2570
+ observationsByIssueId: {},
2571
+ statusByIssueId: {},
2572
+ rollupsByIssueId: {},
2573
+ resetMarkersByScopeId: {},
2574
+ healthByIssueId: {},
2575
+ scanProgress: null,
2576
+ enrichStats: null,
2577
+ enrichProgressBySessionId: {},
2578
+ embedProgressBySessionId: {},
2579
+ agentIdBySessionId: {},
2580
+ dashboardLifecycle: {
2581
+ active: false,
2582
+ reason: null,
2583
+ issueId: null,
2584
+ trigger: null,
2585
+ startedAt: null,
2586
+ completedAt: null,
2587
+ failedAt: null,
2588
+ error: null
2589
+ }
2590
+ };
2591
+ const MAX_AGENT_OUTPUT_LINES = 200;
2592
+ const MAX_ACTIVITY_ENTRIES = 50;
2593
+ const MAX_DETAILED_ENTRIES = 200;
2594
+ const MAX_TTS_ENTRIES = 50;
2595
+ const MAX_SESSION_PROGRESS_ENTRIES = 100;
2596
+ const DEFAULT_MAX_TURN_DIFF_SUMMARIES_PER_AGENT = 200;
2597
+ const DEFAULT_MAX_MEMORY_OBSERVATIONS_PER_ISSUE = 50;
2598
+ function getMaxMemoryObservationsPerIssue() {
2599
+ const raw = globalThis.process?.env?.OVERDECK_MEMORY_OBSERVATION_LIMIT;
2600
+ const parsed = raw ? Number.parseInt(raw, 10) : NaN;
2601
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 50;
2602
+ }
2603
+ function trimMemoryObservations(observations) {
2604
+ const max = getMaxMemoryObservationsPerIssue();
2605
+ return observations.length > max ? observations.slice(-max) : observations;
2606
+ }
2607
+ function getMaxTurnDiffSummariesPerAgent() {
2608
+ const raw = globalThis.process?.env?.OVERDECK_TURN_DIFF_SUMMARY_LIMIT;
2609
+ const parsed = raw ? Number.parseInt(raw, 10) : NaN;
2610
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 200;
2611
+ }
2612
+ function isTerminalTurnDiffSummaryStatus(status) {
2613
+ return status === "stopped" || status === "done" || status === "archived" || status === "closed";
2614
+ }
2615
+ function trimTurnDiffSummaries(summaries) {
2616
+ const max = getMaxTurnDiffSummariesPerAgent();
2617
+ return summaries.length > max ? summaries.slice(-max) : summaries;
2618
+ }
2619
+ function upsertBoundedSessionProgress(bySessionId, progress) {
2620
+ const existing = bySessionId[progress.sessionId];
2621
+ const entries = Object.values(bySessionId);
2622
+ if (existing || entries.length < MAX_SESSION_PROGRESS_ENTRIES) return {
2623
+ ...bySessionId,
2624
+ [progress.sessionId]: progress
2625
+ };
2626
+ const keep = entries.sort((a, b) => a.timestamp.localeCompare(b.timestamp)).slice(-(MAX_SESSION_PROGRESS_ENTRIES - 1));
2627
+ const next = {};
2628
+ for (const entry of [...keep, progress]) next[entry.sessionId] = entry;
2629
+ return next;
2630
+ }
2631
+ function omitTurnDiffSummariesForAgent(turnDiffSummariesByAgentId, agentId) {
2632
+ const { [agentId]: _removed, ...rest } = turnDiffSummariesByAgentId ?? {};
2633
+ return rest;
2634
+ }
2635
+ /** Remove all entries in agentIdBySessionId that point to the given agentId. */
2636
+ function removeAgentFromSessionIndex(agentIdBySessionId, agentId) {
2637
+ const next = {};
2638
+ for (const [sessionId, id] of Object.entries(agentIdBySessionId ?? {})) if (id !== agentId) next[sessionId] = id;
2639
+ return next;
2640
+ }
2641
+ function defaultRuntimeSnapshot(agentId, timestamp, sequence) {
2642
+ return {
2643
+ id: agentId,
2644
+ activity: "idle",
2645
+ lastActivity: timestamp,
2646
+ updatedAtSequence: sequence
2647
+ };
2648
+ }
2649
+ /**
2650
+ * Bump runtimeSnapshotSequence on the corresponding AgentSnapshot (if present)
2651
+ * so low-frequency subscribers can cheaply detect a runtime change. No-op if
2652
+ * the agent has no lifecycle snapshot yet (a runtime event arrived before
2653
+ * agent.created — unusual but defensive).
2654
+ */
2655
+ function bumpRuntimeSnapshotSequence(agentsById, agentId, sequence) {
2656
+ const agent = agentsById[agentId];
2657
+ if (!agent) return agentsById;
2658
+ return {
2659
+ ...agentsById,
2660
+ [agentId]: {
2661
+ ...agent,
2662
+ runtimeSnapshotSequence: sequence
2663
+ }
2664
+ };
2665
+ }
2666
+ function syncSnapshot(state, snapshot) {
2667
+ const agentsById = {};
2668
+ for (const agent of snapshot.agents) agentsById[agent.id] = agent;
2669
+ const reviewStatusByIssueId = {};
2670
+ for (const rs of snapshot.reviewStatuses) reviewStatusByIssueId[rs.issueId] = rs;
2671
+ const channelPermissionRequestsById = {};
2672
+ const channelPermissionRequestIdsByAgentId = {};
2673
+ for (const request of snapshot.channelPermissionRequests ?? []) {
2674
+ channelPermissionRequestsById[request.requestId] = request;
2675
+ const existing = channelPermissionRequestIdsByAgentId[request.agentId] ?? [];
2676
+ channelPermissionRequestIdsByAgentId[request.agentId] = [...existing, request.requestId];
2677
+ }
2678
+ const memory = snapshot.memory;
2679
+ const agentIdBySessionId = {};
2680
+ for (const agent of snapshot.agents) if (agent.sessionId) agentIdBySessionId[agent.sessionId] = agent.id;
2681
+ for (const [agentId, runtime] of Object.entries(snapshot.agentRuntimeById ?? {})) if (runtime.claudeSessionId) agentIdBySessionId[runtime.claudeSessionId] = agentId;
2682
+ return {
2683
+ ...state,
2684
+ sequence: snapshot.sequence,
2685
+ agentsById,
2686
+ reviewStatusByIssueId,
2687
+ agentRuntimeById: snapshot.agentRuntimeById ?? state.agentRuntimeById,
2688
+ channelPermissionRequestsById,
2689
+ channelPermissionRequestIdsByAgentId,
2690
+ resolvedChannelPermissionDecisionsById: {},
2691
+ resolvedChannelPermissionDecisionIdsByAgentId: {},
2692
+ resources: snapshot.resources ?? null,
2693
+ issuesRaw: snapshot.issues ?? state.issuesRaw,
2694
+ observationsByIssueId: memory?.observationsByIssueId ?? state.observationsByIssueId,
2695
+ statusByIssueId: memory?.statusByIssueId ?? state.statusByIssueId,
2696
+ rollupsByIssueId: memory?.rollupsByIssueId ?? state.rollupsByIssueId,
2697
+ resetMarkersByScopeId: memory?.resetMarkersByScopeId ?? state.resetMarkersByScopeId,
2698
+ healthByIssueId: memory?.healthByIssueId ?? state.healthByIssueId,
2699
+ scanProgress: snapshot.scanProgress ?? null,
2700
+ enrichStats: snapshot.enrichStats ?? null,
2701
+ enrichProgressBySessionId: snapshot.enrichProgressBySessionId ?? {},
2702
+ embedProgressBySessionId: snapshot.embedProgressBySessionId ?? {},
2703
+ agentIdBySessionId
2704
+ };
2705
+ }
2706
+ function applyEvent(state, event) {
2707
+ switch (event.type) {
2708
+ case "agent.created": {
2709
+ const existing = state.agentsById[event.payload.agentId];
2710
+ const agent = event.payload.agent;
2711
+ const nextAgentIdBySessionId = agent.sessionId ? {
2712
+ ...state.agentIdBySessionId,
2713
+ [agent.sessionId]: agent.id
2714
+ } : state.agentIdBySessionId;
2715
+ return {
2716
+ ...state,
2717
+ sequence: Math.max(state.sequence, event.sequence),
2718
+ agentsById: {
2719
+ ...state.agentsById,
2720
+ [event.payload.agentId]: existing ? {
2721
+ ...existing,
2722
+ ...agent
2723
+ } : agent
2724
+ },
2725
+ agentIdBySessionId: nextAgentIdBySessionId
2726
+ };
2727
+ }
2728
+ case "agent.started": {
2729
+ const agent = event.payload.agent;
2730
+ const nextAgentIdBySessionId = agent.sessionId ? {
2731
+ ...state.agentIdBySessionId,
2732
+ [agent.sessionId]: agent.id
2733
+ } : state.agentIdBySessionId;
2734
+ return {
2735
+ ...state,
2736
+ sequence: Math.max(state.sequence, event.sequence),
2737
+ agentsById: {
2738
+ ...state.agentsById,
2739
+ [event.payload.agentId]: agent
2740
+ },
2741
+ agentIdBySessionId: nextAgentIdBySessionId
2742
+ };
2743
+ }
2744
+ case "agent.enrichment_changed": {
2745
+ const agent = state.agentsById[event.payload.agentId];
2746
+ if (!agent) return {
2747
+ ...state,
2748
+ sequence: Math.max(state.sequence, event.sequence)
2749
+ };
2750
+ return {
2751
+ ...state,
2752
+ sequence: Math.max(state.sequence, event.sequence),
2753
+ agentsById: {
2754
+ ...state.agentsById,
2755
+ [event.payload.agentId]: {
2756
+ ...agent,
2757
+ role: event.payload.role ?? agent.role,
2758
+ hasPendingQuestion: event.payload.hasPendingQuestion ?? false,
2759
+ pendingQuestionCount: event.payload.pendingQuestionCount ?? 0,
2760
+ pendingQuestionPrompt: event.payload.pendingQuestionPrompt,
2761
+ pendingQuestionReason: event.payload.pendingQuestionReason,
2762
+ pendingInputCount: event.payload.pendingInputCount,
2763
+ pendingInputKinds: event.payload.pendingInputKinds,
2764
+ pendingAskUserQuestion: event.payload.pendingAskUserQuestion,
2765
+ resolution: event.payload.resolution,
2766
+ resolutionCount: event.payload.resolutionCount
2767
+ }
2768
+ }
2769
+ };
2770
+ }
2771
+ case "agent.stopped": {
2772
+ const { [event.payload.agentId]: _removed, ...rest } = state.agentsById;
2773
+ const runtimeById = state.agentRuntimeById ?? {};
2774
+ const prevRuntime = runtimeById[event.payload.agentId];
2775
+ const nextRuntimeById = prevRuntime ? {
2776
+ ...runtimeById,
2777
+ [event.payload.agentId]: {
2778
+ ...prevRuntime,
2779
+ activity: "stopped",
2780
+ currentTool: void 0,
2781
+ thinking: void 0,
2782
+ waiting: void 0,
2783
+ channelReply: void 0,
2784
+ lastActivity: event.timestamp,
2785
+ updatedAtSequence: event.sequence
2786
+ }
2787
+ } : runtimeById;
2788
+ const permissionRequestIdsByAgentId = state.channelPermissionRequestIdsByAgentId ?? {};
2789
+ const pendingIds = permissionRequestIdsByAgentId[event.payload.agentId] ?? [];
2790
+ const nextPermissionRequestsById = { ...state.channelPermissionRequestsById };
2791
+ for (const requestId of pendingIds) delete nextPermissionRequestsById[requestId];
2792
+ const { [event.payload.agentId]: _removedPendingIds, ...restPendingIds } = permissionRequestIdsByAgentId;
2793
+ const resolvedDecisionIdsByAgentId = state.resolvedChannelPermissionDecisionIdsByAgentId ?? {};
2794
+ const resolvedIds = resolvedDecisionIdsByAgentId[event.payload.agentId] ?? [];
2795
+ const nextResolvedDecisionsById = { ...state.resolvedChannelPermissionDecisionsById ?? {} };
2796
+ for (const requestId of resolvedIds) delete nextResolvedDecisionsById[requestId];
2797
+ const { [event.payload.agentId]: _removedResolvedIds, ...restResolvedIds } = resolvedDecisionIdsByAgentId;
2798
+ return {
2799
+ ...state,
2800
+ sequence: Math.max(state.sequence, event.sequence),
2801
+ agentsById: rest,
2802
+ agentRuntimeById: nextRuntimeById,
2803
+ channelPermissionRequestsById: nextPermissionRequestsById,
2804
+ channelPermissionRequestIdsByAgentId: restPendingIds,
2805
+ resolvedChannelPermissionDecisionsById: nextResolvedDecisionsById,
2806
+ resolvedChannelPermissionDecisionIdsByAgentId: restResolvedIds,
2807
+ turnDiffSummariesByAgentId: omitTurnDiffSummariesForAgent(state.turnDiffSummariesByAgentId, event.payload.agentId),
2808
+ agentIdBySessionId: removeAgentFromSessionIndex(state.agentIdBySessionId, event.payload.agentId)
2809
+ };
2810
+ }
2811
+ case "agent.status_changed": {
2812
+ const agent = state.agentsById[event.payload.agentId];
2813
+ if (!agent) return {
2814
+ ...state,
2815
+ sequence: Math.max(state.sequence, event.sequence)
2816
+ };
2817
+ const nextTurnDiffSummariesByAgentId = isTerminalTurnDiffSummaryStatus(event.payload.status) ? omitTurnDiffSummariesForAgent(state.turnDiffSummariesByAgentId, event.payload.agentId) : state.turnDiffSummariesByAgentId;
2818
+ const nextAgent = (() => {
2819
+ const base = {
2820
+ ...agent,
2821
+ status: event.payload.status
2822
+ };
2823
+ for (const field of [
2824
+ "hasLiveTmuxSession",
2825
+ "stoppedByUser",
2826
+ "stoppedByPause",
2827
+ "paused",
2828
+ "pausedReason",
2829
+ "pausedAt",
2830
+ "troubled",
2831
+ "troubledAt",
2832
+ "consecutiveFailures",
2833
+ "firstFailureInRunAt",
2834
+ "lastFailureAt",
2835
+ "lastFailureReason",
2836
+ "lastFailureNextRetryAt",
2837
+ "kickoffDelivered",
2838
+ "hostOverride",
2839
+ "role",
2840
+ "model",
2841
+ "workspace",
2842
+ "sessionId",
2843
+ "lastActivity",
2844
+ "lastResumeAt",
2845
+ "stoppedAt",
2846
+ "branch",
2847
+ "costSoFar",
2848
+ "phase",
2849
+ "workType",
2850
+ "roleRunHead",
2851
+ "flywheelRunId",
2852
+ "reviewSubRole",
2853
+ "reviewRunId",
2854
+ "reviewOutputPath",
2855
+ "reviewSynthesisAgentId",
2856
+ "reviewDeadlineAt",
2857
+ "reviewMonitorSignaled",
2858
+ "reviewRetryAttempt",
2859
+ "inspectSubRole",
2860
+ "deliveryMethod",
2861
+ "supervisorEnabled",
2862
+ "channelsEnabled"
2863
+ ]) if (field in event.payload) {
2864
+ const value = event.payload[field];
2865
+ if (value === null || value === void 0) delete base[field];
2866
+ else base[field] = value;
2867
+ }
2868
+ if (event.payload.status !== "running" && event.payload.status !== "starting") {
2869
+ delete base["hasPendingQuestion"];
2870
+ delete base["pendingQuestionCount"];
2871
+ delete base["pendingQuestionPrompt"];
2872
+ delete base["pendingQuestionReason"];
2873
+ delete base["pendingInputCount"];
2874
+ delete base["pendingInputKinds"];
2875
+ delete base["pendingAskUserQuestion"];
2876
+ }
2877
+ return base;
2878
+ })();
2879
+ return {
2880
+ ...state,
2881
+ sequence: Math.max(state.sequence, event.sequence),
2882
+ agentsById: {
2883
+ ...state.agentsById,
2884
+ [event.payload.agentId]: nextAgent
2885
+ },
2886
+ turnDiffSummariesByAgentId: nextTurnDiffSummariesByAgentId
2887
+ };
2888
+ }
2889
+ case "agent.heartbeat_dead": {
2890
+ const agent = state.agentsById[event.payload.agentId];
2891
+ if (!agent) return {
2892
+ ...state,
2893
+ sequence: Math.max(state.sequence, event.sequence)
2894
+ };
2895
+ const runtimeById = state.agentRuntimeById ?? {};
2896
+ const prevRuntime = runtimeById[event.payload.agentId];
2897
+ const nextRuntimeById = prevRuntime ? {
2898
+ ...runtimeById,
2899
+ [event.payload.agentId]: {
2900
+ ...prevRuntime,
2901
+ activity: "stopped",
2902
+ currentTool: void 0,
2903
+ thinking: void 0,
2904
+ waiting: void 0,
2905
+ channelReply: void 0,
2906
+ lastActivity: event.timestamp,
2907
+ updatedAtSequence: event.sequence
2908
+ }
2909
+ } : runtimeById;
2910
+ return {
2911
+ ...state,
2912
+ sequence: Math.max(state.sequence, event.sequence),
2913
+ agentsById: {
2914
+ ...state.agentsById,
2915
+ [event.payload.agentId]: {
2916
+ ...agent,
2917
+ status: "error"
2918
+ }
2919
+ },
2920
+ agentRuntimeById: nextRuntimeById
2921
+ };
2922
+ }
2923
+ case "agent.output_received": {
2924
+ const updated = [...state.agentOutputById[event.payload.agentId] ?? [], ...event.payload.lines].slice(-MAX_AGENT_OUTPUT_LINES);
2925
+ return {
2926
+ ...state,
2927
+ sequence: Math.max(state.sequence, event.sequence),
2928
+ agentOutputById: {
2929
+ ...state.agentOutputById,
2930
+ [event.payload.agentId]: updated
2931
+ }
2932
+ };
2933
+ }
2934
+ case "pipeline.status_changed":
2935
+ case "review.status_changed": return {
2936
+ ...state,
2937
+ sequence: Math.max(state.sequence, event.sequence),
2938
+ reviewStatusByIssueId: {
2939
+ ...state.reviewStatusByIssueId,
2940
+ [event.payload.issueId]: event.payload.status
2941
+ }
2942
+ };
2943
+ case "review.reviewer_started": {
2944
+ const { issueId, role, sessionName } = event.payload;
2945
+ const existing = state.reviewStatusByIssueId[issueId];
2946
+ const prevSubs = existing?.reviewSubStatuses ?? {};
2947
+ const prevNames = existing?.reviewSessionNames ?? [];
2948
+ const nextNames = prevNames.includes(sessionName) ? prevNames : [...prevNames, sessionName];
2949
+ const nextStatus = {
2950
+ ...existing ?? { issueId },
2951
+ reviewSubStatuses: {
2952
+ ...prevSubs,
2953
+ [role]: "running"
2954
+ },
2955
+ reviewSessionNames: nextNames
2956
+ };
2957
+ return {
2958
+ ...state,
2959
+ sequence: Math.max(state.sequence, event.sequence),
2960
+ reviewStatusByIssueId: {
2961
+ ...state.reviewStatusByIssueId,
2962
+ [issueId]: nextStatus
2963
+ }
2964
+ };
2965
+ }
2966
+ case "review.reviewer_completed": {
2967
+ const { issueId, role } = event.payload;
2968
+ const existing = state.reviewStatusByIssueId[issueId];
2969
+ if (!existing) return {
2970
+ ...state,
2971
+ sequence: Math.max(state.sequence, event.sequence)
2972
+ };
2973
+ const prevSubs = existing.reviewSubStatuses ?? {};
2974
+ const nextStatus = {
2975
+ ...existing,
2976
+ reviewSubStatuses: {
2977
+ ...prevSubs,
2978
+ [role]: "done"
2979
+ }
2980
+ };
2981
+ return {
2982
+ ...state,
2983
+ sequence: Math.max(state.sequence, event.sequence),
2984
+ reviewStatusByIssueId: {
2985
+ ...state.reviewStatusByIssueId,
2986
+ [issueId]: nextStatus
2987
+ }
2988
+ };
2989
+ }
2990
+ case "review.specialist.timed_out": return {
2991
+ ...state,
2992
+ sequence: Math.max(state.sequence, event.sequence)
2993
+ };
2994
+ case "review.coordinator_started": {
2995
+ const { issueId, sessionName } = event.payload;
2996
+ const nextStatus = {
2997
+ ...state.reviewStatusByIssueId[issueId] ?? { issueId },
2998
+ reviewCoordinatorSessionName: sessionName
2999
+ };
3000
+ return {
3001
+ ...state,
3002
+ sequence: Math.max(state.sequence, event.sequence),
3003
+ reviewStatusByIssueId: {
3004
+ ...state.reviewStatusByIssueId,
3005
+ [issueId]: nextStatus
3006
+ }
3007
+ };
3008
+ }
3009
+ case "review.coordinator.died": return {
3010
+ ...state,
3011
+ sequence: Math.max(state.sequence, event.sequence)
3012
+ };
3013
+ case "pipeline.review-started":
3014
+ case "pipeline.review-completed":
3015
+ case "pipeline.test-started":
3016
+ case "pipeline.test-completed": return {
3017
+ ...state,
3018
+ sequence: Math.max(state.sequence, event.sequence)
3019
+ };
3020
+ case "merge.ready": return {
3021
+ ...state,
3022
+ sequence: Math.max(state.sequence, event.sequence)
3023
+ };
3024
+ case "specialist.started":
3025
+ case "specialist.completed":
3026
+ case "specialist.failed": return {
3027
+ ...state,
3028
+ sequence: Math.max(state.sequence, event.sequence)
3029
+ };
3030
+ case "resources.updated": return {
3031
+ ...state,
3032
+ sequence: Math.max(state.sequence, event.sequence),
3033
+ resources: event.payload.resources
3034
+ };
3035
+ case "issues.snapshot": return {
3036
+ ...state,
3037
+ sequence: Math.max(state.sequence, event.sequence),
3038
+ issuesRaw: event.payload.issues
3039
+ };
3040
+ case "issues.updated": return {
3041
+ ...state,
3042
+ sequence: Math.max(state.sequence, event.sequence)
3043
+ };
3044
+ case "issue.statusChanged": {
3045
+ const { issueId, status, canonicalStatus, labels } = event.payload;
3046
+ const updatedIssues = state.issuesRaw.map((issue) => {
3047
+ if (issue["identifier"] === issueId || issue["id"] === issueId) {
3048
+ const patch = {
3049
+ ...issue,
3050
+ status,
3051
+ canonicalStatus,
3052
+ state: canonicalStatus
3053
+ };
3054
+ if (labels) patch.labels = labels;
3055
+ return patch;
3056
+ }
3057
+ return issue;
3058
+ });
3059
+ return {
3060
+ ...state,
3061
+ sequence: Math.max(state.sequence, event.sequence),
3062
+ issuesRaw: updatedIssues
3063
+ };
3064
+ }
3065
+ case "activity.updated": return {
3066
+ ...state,
3067
+ sequence: Math.max(state.sequence, event.sequence),
3068
+ recentActivity: event.payload.events.slice(0, MAX_ACTIVITY_ENTRIES)
3069
+ };
3070
+ case "shadow.inference_update": return {
3071
+ ...state,
3072
+ sequence: Math.max(state.sequence, event.sequence),
3073
+ shadowInferenceByIssueId: {
3074
+ ...state.shadowInferenceByIssueId,
3075
+ [event.payload.issueId]: event.payload.content
3076
+ }
3077
+ };
3078
+ case "planning.started": {
3079
+ const sessionName = event.payload.sessionName;
3080
+ const issueId = event.payload.issueId;
3081
+ if (sessionName) return {
3082
+ ...state,
3083
+ sequence: Math.max(state.sequence, event.sequence),
3084
+ agentsById: {
3085
+ ...state.agentsById,
3086
+ [sessionName]: {
3087
+ ...state.agentsById[sessionName],
3088
+ id: sessionName,
3089
+ issueId,
3090
+ status: "running",
3091
+ startedAt: event.timestamp,
3092
+ runtime: event.payload.harness ?? "claude-code",
3093
+ role: "plan"
3094
+ }
3095
+ }
3096
+ };
3097
+ return {
3098
+ ...state,
3099
+ sequence: Math.max(state.sequence, event.sequence)
3100
+ };
3101
+ }
3102
+ case "workspace.created": return {
3103
+ ...state,
3104
+ sequence: Math.max(state.sequence, event.sequence)
3105
+ };
3106
+ case "workspace.wipe_started": {
3107
+ const { issueId } = event.payload;
3108
+ const updatedIssues = state.issuesRaw.map((issue) => {
3109
+ if (issue["identifier"] === issueId || issue["id"] === issueId) return {
3110
+ ...issue,
3111
+ canonicalStatus: "wiping",
3112
+ state: "wiping"
3113
+ };
3114
+ return issue;
3115
+ });
3116
+ return {
3117
+ ...state,
3118
+ sequence: Math.max(state.sequence, event.sequence),
3119
+ issuesRaw: updatedIssues
3120
+ };
3121
+ }
3122
+ case "workspace.destroyed":
3123
+ case "workspace.deleted": {
3124
+ const { issueId } = event.payload;
3125
+ const removedAgentIds = Object.entries(state.agentsById).filter(([, agent]) => agent.issueId === issueId).map(([agentId]) => agentId);
3126
+ const updatedAgents = Object.fromEntries(Object.entries(state.agentsById).filter(([, agent]) => agent.issueId !== issueId));
3127
+ let nextTurnDiffSummariesByAgentId = state.turnDiffSummariesByAgentId;
3128
+ let nextAgentIdBySessionId = state.agentIdBySessionId;
3129
+ for (const agentId of removedAgentIds) {
3130
+ nextTurnDiffSummariesByAgentId = omitTurnDiffSummariesForAgent(nextTurnDiffSummariesByAgentId, agentId);
3131
+ nextAgentIdBySessionId = removeAgentFromSessionIndex(nextAgentIdBySessionId, agentId);
3132
+ }
3133
+ const updatedIssues = state.issuesRaw.map((issue) => {
3134
+ if (issue["identifier"] === issueId || issue["id"] === issueId) return {
3135
+ ...issue,
3136
+ status: "Todo",
3137
+ canonicalStatus: "todo",
3138
+ state: "todo"
3139
+ };
3140
+ return issue;
3141
+ });
3142
+ return {
3143
+ ...state,
3144
+ sequence: Math.max(state.sequence, event.sequence),
3145
+ agentsById: updatedAgents,
3146
+ turnDiffSummariesByAgentId: nextTurnDiffSummariesByAgentId,
3147
+ agentIdBySessionId: nextAgentIdBySessionId,
3148
+ issuesRaw: updatedIssues
3149
+ };
3150
+ }
3151
+ case "workspace.aborted": {
3152
+ const { issueId, sessionName } = event.payload;
3153
+ let updatedAgents;
3154
+ let nextTurnDiffSummariesByAgentId = state.turnDiffSummariesByAgentId;
3155
+ let nextAgentIdBySessionId = state.agentIdBySessionId;
3156
+ if (sessionName) {
3157
+ const { [sessionName]: _removed, ...rest } = state.agentsById;
3158
+ updatedAgents = rest;
3159
+ nextTurnDiffSummariesByAgentId = omitTurnDiffSummariesForAgent(nextTurnDiffSummariesByAgentId, sessionName);
3160
+ nextAgentIdBySessionId = removeAgentFromSessionIndex(nextAgentIdBySessionId, sessionName);
3161
+ } else {
3162
+ const removedAgentIds = Object.entries(state.agentsById).filter(([, agent]) => agent.issueId === issueId).map(([agentId]) => agentId);
3163
+ updatedAgents = Object.fromEntries(Object.entries(state.agentsById).filter(([, agent]) => agent.issueId !== issueId));
3164
+ for (const agentId of removedAgentIds) {
3165
+ nextTurnDiffSummariesByAgentId = omitTurnDiffSummariesForAgent(nextTurnDiffSummariesByAgentId, agentId);
3166
+ nextAgentIdBySessionId = removeAgentFromSessionIndex(nextAgentIdBySessionId, agentId);
3167
+ }
3168
+ }
3169
+ return {
3170
+ ...state,
3171
+ sequence: Math.max(state.sequence, event.sequence),
3172
+ agentsById: updatedAgents,
3173
+ turnDiffSummariesByAgentId: nextTurnDiffSummariesByAgentId,
3174
+ agentIdBySessionId: nextAgentIdBySessionId
3175
+ };
3176
+ }
3177
+ case "planning.failed":
3178
+ case "planning.sync":
3179
+ case "plan.item_status_changed":
3180
+ case "plan.subitem_status_changed":
3181
+ case "plan.items_unblocked":
3182
+ case "operator.intervention":
3183
+ case "substrate.bug_filed":
3184
+ case "cost.event_recorded": return {
3185
+ ...state,
3186
+ sequence: Math.max(state.sequence, event.sequence)
3187
+ };
3188
+ case "memory.observation_created": {
3189
+ const observation = event.payload.observation;
3190
+ const existing = state.observationsByIssueId[observation.issueId] ?? [];
3191
+ const index = existing.findIndex((entry) => entry.id === observation.id);
3192
+ const updated = trimMemoryObservations(index === -1 ? [...existing, observation] : existing.map((entry, entryIndex) => entryIndex === index ? observation : entry));
3193
+ return {
3194
+ ...state,
3195
+ sequence: Math.max(state.sequence, event.sequence),
3196
+ observationsByIssueId: {
3197
+ ...state.observationsByIssueId,
3198
+ [observation.issueId]: updated
3199
+ }
3200
+ };
3201
+ }
3202
+ case "memory.status_updated": {
3203
+ const { identity, status } = event.payload;
3204
+ return {
3205
+ ...state,
3206
+ sequence: Math.max(state.sequence, event.sequence),
3207
+ statusByIssueId: {
3208
+ ...state.statusByIssueId,
3209
+ [identity.issueId]: status
3210
+ }
3211
+ };
3212
+ }
3213
+ case "memory.rollup_triggered": {
3214
+ const trigger = {
3215
+ projectId: event.payload.projectId,
3216
+ workspaceId: event.payload.workspaceId,
3217
+ issueId: event.payload.issueId,
3218
+ pendingTurns: [],
3219
+ pendingCount: event.payload.pendingCount,
3220
+ threshold: event.payload.threshold,
3221
+ triggeredAt: event.timestamp
3222
+ };
3223
+ const updated = [...state.rollupsByIssueId[event.payload.issueId] ?? [], trigger].slice(-10);
3224
+ return {
3225
+ ...state,
3226
+ sequence: Math.max(state.sequence, event.sequence),
3227
+ rollupsByIssueId: {
3228
+ ...state.rollupsByIssueId,
3229
+ [event.payload.issueId]: updated
3230
+ }
3231
+ };
3232
+ }
3233
+ case "memory.reset_marker_created": {
3234
+ const { marker } = event.payload;
3235
+ const key = `${marker.scope}:${marker.scopeId}`;
3236
+ const existing = state.resetMarkersByScopeId[key] ?? [];
3237
+ const index = existing.findIndex((entry) => entry.id === marker.id);
3238
+ const updated = index === -1 ? [...existing, marker] : existing.map((entry, entryIndex) => entryIndex === index ? marker : entry);
3239
+ return {
3240
+ ...state,
3241
+ sequence: Math.max(state.sequence, event.sequence),
3242
+ resetMarkersByScopeId: {
3243
+ ...state.resetMarkersByScopeId,
3244
+ [key]: updated
3245
+ }
3246
+ };
3247
+ }
3248
+ case "memory.health_changed": {
3249
+ const health = {
3250
+ projectId: event.payload.projectId,
3251
+ issueId: event.payload.issueId,
3252
+ status: event.payload.status,
3253
+ reason: event.payload.reason,
3254
+ ...event.payload.detail !== void 0 ? { detail: event.payload.detail } : {},
3255
+ ...event.payload.ragDecision !== void 0 ? { ragDecision: event.payload.ragDecision } : {},
3256
+ updatedAt: event.timestamp
3257
+ };
3258
+ return {
3259
+ ...state,
3260
+ sequence: Math.max(state.sequence, event.sequence),
3261
+ healthByIssueId: {
3262
+ ...state.healthByIssueId,
3263
+ [event.payload.issueId]: health
3264
+ }
3265
+ };
3266
+ }
3267
+ case "agent.activity_changed": {
3268
+ const { agentId, activity, currentTool } = event.payload;
3269
+ const prev = state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence);
3270
+ const next = {
3271
+ ...prev,
3272
+ activity,
3273
+ currentTool: activity === "working" ? currentTool : void 0,
3274
+ thinking: activity === "thinking" ? prev.thinking : void 0,
3275
+ waiting: activity === "waiting" ? prev.waiting : void 0,
3276
+ channelReply: activity === "working" ? void 0 : prev.channelReply,
3277
+ lastActivity: event.timestamp,
3278
+ updatedAtSequence: event.sequence
3279
+ };
3280
+ return {
3281
+ ...state,
3282
+ sequence: Math.max(state.sequence, event.sequence),
3283
+ agentRuntimeById: {
3284
+ ...state.agentRuntimeById,
3285
+ [agentId]: next
3286
+ },
3287
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3288
+ };
3289
+ }
3290
+ case "agent.thinking_started": {
3291
+ const { agentId, lastToolAt } = event.payload;
3292
+ const next = {
3293
+ ...state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence),
3294
+ activity: "thinking",
3295
+ currentTool: void 0,
3296
+ thinking: {
3297
+ since: event.timestamp,
3298
+ lastToolAt
3299
+ },
3300
+ waiting: void 0,
3301
+ channelReply: void 0,
3302
+ lastActivity: event.timestamp,
3303
+ updatedAtSequence: event.sequence
3304
+ };
3305
+ return {
3306
+ ...state,
3307
+ sequence: Math.max(state.sequence, event.sequence),
3308
+ agentRuntimeById: {
3309
+ ...state.agentRuntimeById,
3310
+ [agentId]: next
3311
+ },
3312
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3313
+ };
3314
+ }
3315
+ case "agent.thinking_stopped": {
3316
+ const { agentId } = event.payload;
3317
+ const prev = state.agentRuntimeById[agentId];
3318
+ if (!prev) return {
3319
+ ...state,
3320
+ sequence: Math.max(state.sequence, event.sequence)
3321
+ };
3322
+ const next = {
3323
+ ...prev,
3324
+ thinking: void 0,
3325
+ lastActivity: event.timestamp,
3326
+ updatedAtSequence: event.sequence
3327
+ };
3328
+ return {
3329
+ ...state,
3330
+ sequence: Math.max(state.sequence, event.sequence),
3331
+ agentRuntimeById: {
3332
+ ...state.agentRuntimeById,
3333
+ [agentId]: next
3334
+ },
3335
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3336
+ };
3337
+ }
3338
+ case "agent.waiting_started": {
3339
+ const { agentId, reason, message } = event.payload;
3340
+ const next = {
3341
+ ...state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence),
3342
+ activity: "waiting",
3343
+ currentTool: void 0,
3344
+ thinking: void 0,
3345
+ waiting: {
3346
+ reason,
3347
+ startedAt: event.timestamp,
3348
+ message
3349
+ },
3350
+ lastActivity: event.timestamp,
3351
+ updatedAtSequence: event.sequence
3352
+ };
3353
+ return {
3354
+ ...state,
3355
+ sequence: Math.max(state.sequence, event.sequence),
3356
+ agentRuntimeById: {
3357
+ ...state.agentRuntimeById,
3358
+ [agentId]: next
3359
+ },
3360
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3361
+ };
3362
+ }
3363
+ case "agent.waiting_cleared": {
3364
+ const { agentId } = event.payload;
3365
+ const prev = state.agentRuntimeById[agentId];
3366
+ if (!prev) return {
3367
+ ...state,
3368
+ sequence: Math.max(state.sequence, event.sequence)
3369
+ };
3370
+ const next = {
3371
+ ...prev,
3372
+ waiting: void 0,
3373
+ lastActivity: event.timestamp,
3374
+ updatedAtSequence: event.sequence
3375
+ };
3376
+ return {
3377
+ ...state,
3378
+ sequence: Math.max(state.sequence, event.sequence),
3379
+ agentRuntimeById: {
3380
+ ...state.agentRuntimeById,
3381
+ [agentId]: next
3382
+ },
3383
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3384
+ };
3385
+ }
3386
+ case "agent.permission_requested": {
3387
+ const request = event.payload;
3388
+ const permissionRequestIdsByAgentId = state.channelPermissionRequestIdsByAgentId ?? {};
3389
+ const resolvedDecisionsById = state.resolvedChannelPermissionDecisionsById ?? {};
3390
+ const resolvedDecisionIdsByAgentId = state.resolvedChannelPermissionDecisionIdsByAgentId ?? {};
3391
+ const nextPendingIds = permissionRequestIdsByAgentId[request.agentId] ?? [];
3392
+ const resolvedDecision = resolvedDecisionsById[request.requestId];
3393
+ const nextResolvedDecisionsById = { ...resolvedDecisionsById };
3394
+ const nextResolvedIdsByAgentId = { ...resolvedDecisionIdsByAgentId };
3395
+ if (resolvedDecision) {
3396
+ delete nextResolvedDecisionsById[request.requestId];
3397
+ const filteredResolvedIds = (nextResolvedIdsByAgentId[resolvedDecision.agentId] ?? []).filter((id) => id !== request.requestId);
3398
+ if (filteredResolvedIds.length > 0) nextResolvedIdsByAgentId[resolvedDecision.agentId] = filteredResolvedIds;
3399
+ else delete nextResolvedIdsByAgentId[resolvedDecision.agentId];
3400
+ }
3401
+ return {
3402
+ ...state,
3403
+ sequence: Math.max(state.sequence, event.sequence),
3404
+ channelPermissionRequestsById: {
3405
+ ...state.channelPermissionRequestsById,
3406
+ [request.requestId]: request
3407
+ },
3408
+ channelPermissionRequestIdsByAgentId: {
3409
+ ...permissionRequestIdsByAgentId,
3410
+ [request.agentId]: nextPendingIds.includes(request.requestId) ? nextPendingIds : [...nextPendingIds, request.requestId]
3411
+ },
3412
+ resolvedChannelPermissionDecisionsById: nextResolvedDecisionsById,
3413
+ resolvedChannelPermissionDecisionIdsByAgentId: nextResolvedIdsByAgentId
3414
+ };
3415
+ }
3416
+ case "agent.permission_resolved": {
3417
+ const { [event.payload.requestId]: _removed, ...rest } = state.channelPermissionRequestsById;
3418
+ const nextPendingIdsByAgentId = { ...state.channelPermissionRequestIdsByAgentId ?? {} };
3419
+ const filteredPendingIds = (nextPendingIdsByAgentId[event.payload.agentId] ?? []).filter((id) => id !== event.payload.requestId);
3420
+ if (filteredPendingIds.length > 0) nextPendingIdsByAgentId[event.payload.agentId] = filteredPendingIds;
3421
+ else delete nextPendingIdsByAgentId[event.payload.agentId];
3422
+ const resolvedDecisionIdsByAgentId = state.resolvedChannelPermissionDecisionIdsByAgentId ?? {};
3423
+ const nextResolvedIds = resolvedDecisionIdsByAgentId[event.payload.agentId] ?? [];
3424
+ return {
3425
+ ...state,
3426
+ sequence: Math.max(state.sequence, event.sequence),
3427
+ channelPermissionRequestsById: rest,
3428
+ channelPermissionRequestIdsByAgentId: nextPendingIdsByAgentId,
3429
+ resolvedChannelPermissionDecisionsById: {
3430
+ ...state.resolvedChannelPermissionDecisionsById ?? {},
3431
+ [event.payload.requestId]: {
3432
+ requestId: event.payload.requestId,
3433
+ agentId: event.payload.agentId,
3434
+ issueId: event.payload.issueId,
3435
+ behavior: event.payload.behavior
3436
+ }
3437
+ },
3438
+ resolvedChannelPermissionDecisionIdsByAgentId: {
3439
+ ...resolvedDecisionIdsByAgentId,
3440
+ [event.payload.agentId]: nextResolvedIds.includes(event.payload.requestId) ? nextResolvedIds : [...nextResolvedIds, event.payload.requestId]
3441
+ }
3442
+ };
3443
+ }
3444
+ case "agent.message_received": {
3445
+ const { agentId, direction } = event.payload;
3446
+ const prev = state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence);
3447
+ const next = {
3448
+ ...prev,
3449
+ lastMessageAt: event.timestamp,
3450
+ channelReply: direction === "to_agent" ? void 0 : prev.channelReply,
3451
+ lastActivity: event.timestamp,
3452
+ updatedAtSequence: event.sequence
3453
+ };
3454
+ return {
3455
+ ...state,
3456
+ sequence: Math.max(state.sequence, event.sequence),
3457
+ agentRuntimeById: {
3458
+ ...state.agentRuntimeById,
3459
+ [agentId]: next
3460
+ },
3461
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3462
+ };
3463
+ }
3464
+ case "agent.channel_reply": {
3465
+ const { agentId, reply } = event.payload;
3466
+ const prev = state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence);
3467
+ const next = {
3468
+ ...prev,
3469
+ channelReply: {
3470
+ ...reply,
3471
+ reportedAt: event.timestamp
3472
+ },
3473
+ resolution: reply.kind === "done" ? "done" : reply.kind === "needs_input" ? "needs_input" : prev.resolution,
3474
+ resolutionUpdatedAt: reply.kind === "done" || reply.kind === "needs_input" ? event.timestamp : prev.resolutionUpdatedAt,
3475
+ lastActivity: event.timestamp,
3476
+ updatedAtSequence: event.sequence
3477
+ };
3478
+ return {
3479
+ ...state,
3480
+ sequence: Math.max(state.sequence, event.sequence),
3481
+ agentRuntimeById: {
3482
+ ...state.agentRuntimeById,
3483
+ [agentId]: next
3484
+ },
3485
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3486
+ };
3487
+ }
3488
+ case "agent.model_set": {
3489
+ const { agentId, model, claudeSessionId, sessionModel, sessionHarness } = event.payload;
3490
+ const prev = state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence);
3491
+ const next = {
3492
+ ...prev,
3493
+ model,
3494
+ claudeSessionId: claudeSessionId ?? prev.claudeSessionId,
3495
+ sessionModel: sessionModel ?? prev.sessionModel,
3496
+ sessionHarness: sessionHarness ?? prev.sessionHarness,
3497
+ lastActivity: event.timestamp,
3498
+ updatedAtSequence: event.sequence
3499
+ };
3500
+ let nextAgentIdBySessionId = state.agentIdBySessionId;
3501
+ if (claudeSessionId && claudeSessionId !== prev.claudeSessionId) {
3502
+ nextAgentIdBySessionId = {
3503
+ ...state.agentIdBySessionId,
3504
+ [claudeSessionId]: agentId
3505
+ };
3506
+ if (prev.claudeSessionId && state.agentIdBySessionId[prev.claudeSessionId] === agentId) {
3507
+ const { [prev.claudeSessionId]: _removed, ...rest } = state.agentIdBySessionId;
3508
+ nextAgentIdBySessionId = {
3509
+ ...rest,
3510
+ [claudeSessionId]: agentId
3511
+ };
3512
+ }
3513
+ }
3514
+ return {
3515
+ ...state,
3516
+ sequence: Math.max(state.sequence, event.sequence),
3517
+ agentRuntimeById: {
3518
+ ...state.agentRuntimeById,
3519
+ [agentId]: next
3520
+ },
3521
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence),
3522
+ agentIdBySessionId: nextAgentIdBySessionId
3523
+ };
3524
+ }
3525
+ case "agent.current_issue_set": {
3526
+ const { agentId, currentIssue } = event.payload;
3527
+ const next = {
3528
+ ...state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence),
3529
+ currentIssue,
3530
+ lastActivity: event.timestamp,
3531
+ updatedAtSequence: event.sequence
3532
+ };
3533
+ return {
3534
+ ...state,
3535
+ sequence: Math.max(state.sequence, event.sequence),
3536
+ agentRuntimeById: {
3537
+ ...state.agentRuntimeById,
3538
+ [agentId]: next
3539
+ },
3540
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3541
+ };
3542
+ }
3543
+ case "agent.context_saturation_changed": {
3544
+ const { agentId, contextSaturatedAt } = event.payload;
3545
+ const next = {
3546
+ ...state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence),
3547
+ contextSaturatedAt,
3548
+ updatedAtSequence: event.sequence
3549
+ };
3550
+ return {
3551
+ ...state,
3552
+ sequence: Math.max(state.sequence, event.sequence),
3553
+ agentRuntimeById: {
3554
+ ...state.agentRuntimeById,
3555
+ [agentId]: next
3556
+ },
3557
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3558
+ };
3559
+ }
3560
+ case "agent.resolution_changed": {
3561
+ const { agentId, resolution, resolutionCount } = event.payload;
3562
+ const next = {
3563
+ ...state.agentRuntimeById[agentId] ?? defaultRuntimeSnapshot(agentId, event.timestamp, event.sequence),
3564
+ resolution,
3565
+ resolutionCount,
3566
+ resolutionUpdatedAt: event.timestamp,
3567
+ lastActivity: event.timestamp,
3568
+ updatedAtSequence: event.sequence
3569
+ };
3570
+ return {
3571
+ ...state,
3572
+ sequence: Math.max(state.sequence, event.sequence),
3573
+ agentRuntimeById: {
3574
+ ...state.agentRuntimeById,
3575
+ [agentId]: next
3576
+ },
3577
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3578
+ };
3579
+ }
3580
+ case "agent.state_restored": {
3581
+ const { agentId, snapshot } = event.payload;
3582
+ const next = {
3583
+ ...snapshot,
3584
+ updatedAtSequence: event.sequence
3585
+ };
3586
+ return {
3587
+ ...state,
3588
+ sequence: Math.max(state.sequence, event.sequence),
3589
+ agentRuntimeById: {
3590
+ ...state.agentRuntimeById,
3591
+ [agentId]: next
3592
+ },
3593
+ agentsById: bumpRuntimeSnapshotSequence(state.agentsById, agentId, event.sequence)
3594
+ };
3595
+ }
3596
+ case "activity.entry": {
3597
+ const entry = event.payload;
3598
+ const updated = [{
3599
+ id: entry.id,
3600
+ timestamp: event.timestamp,
3601
+ ...entry
3602
+ }, ...state.recentActivity].slice(0, MAX_ACTIVITY_ENTRIES);
3603
+ return {
3604
+ ...state,
3605
+ sequence: Math.max(state.sequence, event.sequence),
3606
+ recentActivity: updated
3607
+ };
3608
+ }
3609
+ case "activity.detailed": {
3610
+ const entry = event.payload;
3611
+ const updated = [{
3612
+ id: entry.id,
3613
+ timestamp: event.timestamp,
3614
+ ...entry
3615
+ }, ...state.detailedActivity].slice(0, MAX_DETAILED_ENTRIES);
3616
+ return {
3617
+ ...state,
3618
+ sequence: Math.max(state.sequence, event.sequence),
3619
+ detailedActivity: updated
3620
+ };
3621
+ }
3622
+ case "activity.tts": {
3623
+ const entry = event.payload;
3624
+ const updated = [{
3625
+ id: entry.id,
3626
+ timestamp: event.timestamp,
3627
+ ...entry
3628
+ }, ...state.ttsActivity].slice(0, MAX_TTS_ENTRIES);
3629
+ return {
3630
+ ...state,
3631
+ sequence: Math.max(state.sequence, event.sequence),
3632
+ ttsActivity: updated
3633
+ };
3634
+ }
3635
+ case "dashboard.lifecycle_started": {
3636
+ const { reason, issueId, trigger } = event.payload;
3637
+ return {
3638
+ ...state,
3639
+ sequence: Math.max(state.sequence, event.sequence),
3640
+ dashboardLifecycle: {
3641
+ active: true,
3642
+ reason,
3643
+ issueId,
3644
+ trigger,
3645
+ startedAt: event.timestamp,
3646
+ completedAt: null,
3647
+ failedAt: null,
3648
+ error: null
3649
+ }
3650
+ };
3651
+ }
3652
+ case "dashboard.lifecycle_completed": {
3653
+ const { reason, issueId } = event.payload;
3654
+ return {
3655
+ ...state,
3656
+ sequence: Math.max(state.sequence, event.sequence),
3657
+ dashboardLifecycle: {
3658
+ ...state.dashboardLifecycle,
3659
+ active: false,
3660
+ reason: reason ?? state.dashboardLifecycle.reason,
3661
+ issueId: issueId ?? state.dashboardLifecycle.issueId,
3662
+ completedAt: event.timestamp
3663
+ }
3664
+ };
3665
+ }
3666
+ case "dashboard.lifecycle_failed": {
3667
+ const { reason, issueId, error } = event.payload;
3668
+ return {
3669
+ ...state,
3670
+ sequence: Math.max(state.sequence, event.sequence),
3671
+ dashboardLifecycle: {
3672
+ ...state.dashboardLifecycle,
3673
+ active: false,
3674
+ reason: reason ?? state.dashboardLifecycle.reason,
3675
+ issueId: issueId ?? state.dashboardLifecycle.issueId,
3676
+ failedAt: event.timestamp,
3677
+ error
3678
+ }
3679
+ };
3680
+ }
3681
+ case "agent.turn_diff_completed": {
3682
+ const p = event.payload;
3683
+ const existing = state.turnDiffSummariesByAgentId[p.agentId] ?? [];
3684
+ const summary = {
3685
+ turnId: p.turnId,
3686
+ completedAt: p.completedAt,
3687
+ files: p.files,
3688
+ checkpointRef: p.checkpointRef,
3689
+ assistantMessageId: p.assistantMessageId ?? void 0,
3690
+ checkpointTurnCount: p.checkpointTurnCount
3691
+ };
3692
+ const idx = existing.findIndex((s) => s.turnId === p.turnId);
3693
+ const updated = trimTurnDiffSummaries(idx >= 0 ? existing.map((s, i) => i === idx ? summary : s) : [...existing, summary]);
3694
+ return {
3695
+ ...state,
3696
+ sequence: Math.max(state.sequence, event.sequence),
3697
+ turnDiffSummariesByAgentId: {
3698
+ ...state.turnDiffSummariesByAgentId,
3699
+ [p.agentId]: updated
3700
+ }
3701
+ };
3702
+ }
3703
+ case "conversation.compacting_changed": {
3704
+ const { conversationName, compacting } = event.payload;
3705
+ if (!compacting) {
3706
+ const { [conversationName]: _removed, ...rest } = state.conversationsCompactingByName;
3707
+ return {
3708
+ ...state,
3709
+ conversationsCompactingByName: rest
3710
+ };
3711
+ }
3712
+ return {
3713
+ ...state,
3714
+ conversationsCompactingByName: {
3715
+ ...state.conversationsCompactingByName,
3716
+ [conversationName]: true
3717
+ }
3718
+ };
3719
+ }
3720
+ case "conversation.created": return {
3721
+ ...state,
3722
+ conversationsListRevision: state.conversationsListRevision + 1
3723
+ };
3724
+ case "conversation.permission_changed": {
3725
+ const { conversationName, waiting } = event.payload;
3726
+ if (!waiting) {
3727
+ const { [conversationName]: _removed, ...rest } = state.conversationsAwaitingPermissionByName;
3728
+ return {
3729
+ ...state,
3730
+ conversationsAwaitingPermissionByName: rest
3731
+ };
3732
+ }
3733
+ return {
3734
+ ...state,
3735
+ conversationsAwaitingPermissionByName: {
3736
+ ...state.conversationsAwaitingPermissionByName,
3737
+ [conversationName]: true
3738
+ }
3739
+ };
3740
+ }
3741
+ case "scan.started": {
3742
+ const { mode, dirs } = event.payload;
3743
+ return {
3744
+ ...state,
3745
+ sequence: Math.max(state.sequence, event.sequence),
3746
+ scanProgress: {
3747
+ active: true,
3748
+ mode,
3749
+ dirs,
3750
+ dirsProcessed: 0,
3751
+ dirsTotal: 0,
3752
+ sessionsFound: 0,
3753
+ elapsedMs: 0,
3754
+ inserted: 0,
3755
+ updated: 0,
3756
+ skipped: 0,
3757
+ errors: 0,
3758
+ durationMs: 0
3759
+ }
3760
+ };
3761
+ }
3762
+ case "scan.progress": {
3763
+ if (!state.scanProgress) return {
3764
+ ...state,
3765
+ sequence: Math.max(state.sequence, event.sequence)
3766
+ };
3767
+ const { dirsProcessed, dirsTotal, sessionsFound, elapsedMs } = event.payload;
3768
+ return {
3769
+ ...state,
3770
+ sequence: Math.max(state.sequence, event.sequence),
3771
+ scanProgress: {
3772
+ ...state.scanProgress,
3773
+ dirsProcessed,
3774
+ dirsTotal,
3775
+ sessionsFound,
3776
+ elapsedMs
3777
+ }
3778
+ };
3779
+ }
3780
+ case "scan.complete": {
3781
+ if (!state.scanProgress) return {
3782
+ ...state,
3783
+ sequence: Math.max(state.sequence, event.sequence)
3784
+ };
3785
+ const { inserted, updated, skipped, errors, durationMs } = event.payload;
3786
+ return {
3787
+ ...state,
3788
+ sequence: Math.max(state.sequence, event.sequence),
3789
+ scanProgress: {
3790
+ ...state.scanProgress,
3791
+ active: false,
3792
+ inserted,
3793
+ updated,
3794
+ skipped,
3795
+ errors,
3796
+ durationMs
3797
+ }
3798
+ };
3799
+ }
3800
+ case "enrich.progress": {
3801
+ const { sessionId, level, model, cost, success, error } = event.payload;
3802
+ const prev = state.enrichStats ?? {
3803
+ processed: 0,
3804
+ totalCost: 0,
3805
+ failures: 0,
3806
+ durationMs: 0
3807
+ };
3808
+ const progress = {
3809
+ sessionId,
3810
+ level,
3811
+ model,
3812
+ cost,
3813
+ success,
3814
+ error,
3815
+ timestamp: event.timestamp
3816
+ };
3817
+ return {
3818
+ ...state,
3819
+ sequence: Math.max(state.sequence, event.sequence),
3820
+ enrichStats: {
3821
+ processed: prev.processed + 1,
3822
+ totalCost: prev.totalCost + (success ? cost : 0),
3823
+ failures: prev.failures + (success ? 0 : 1),
3824
+ durationMs: 0
3825
+ },
3826
+ enrichProgressBySessionId: upsertBoundedSessionProgress(state.enrichProgressBySessionId, progress)
3827
+ };
3828
+ }
3829
+ case "enrich.complete": {
3830
+ const { processed, totalCost, failures, durationMs } = event.payload;
3831
+ return {
3832
+ ...state,
3833
+ sequence: Math.max(state.sequence, event.sequence),
3834
+ enrichStats: {
3835
+ processed,
3836
+ totalCost,
3837
+ failures,
3838
+ durationMs
3839
+ },
3840
+ enrichProgressBySessionId: {}
3841
+ };
3842
+ }
3843
+ case "embed.progress": {
3844
+ const { sessionId, model, success, error } = event.payload;
3845
+ const progress = {
3846
+ sessionId,
3847
+ model,
3848
+ success,
3849
+ error,
3850
+ timestamp: event.timestamp
3851
+ };
3852
+ return {
3853
+ ...state,
3854
+ sequence: Math.max(state.sequence, event.sequence),
3855
+ embedProgressBySessionId: upsertBoundedSessionProgress(state.embedProgressBySessionId, progress)
3856
+ };
3857
+ }
3858
+ default: return state;
3859
+ }
3860
+ }
3861
+ function applyEvents(state, events) {
3862
+ return events.reduce(applyEvent, state);
3863
+ }
3864
+ //#endregion
3865
+ //#region src/flywheel-stats.ts
3866
+ const FlywheelStatsCriterionStatus = Schema.Literals([
3867
+ "green",
3868
+ "yellow",
3869
+ "red",
3870
+ "insufficient_data"
3871
+ ]);
3872
+ const FlywheelStatsTrend = Schema.Literals([
3873
+ "up",
3874
+ "down",
3875
+ "flat"
3876
+ ]);
3877
+ const FlywheelStatsCriterionValueObject = Schema.Record(Schema.String, Schema.Json);
3878
+ const FlywheelStatsCriterionValue = Schema.Union([Schema.Number, FlywheelStatsCriterionValueObject]);
3879
+ const FlywheelStatsCriterion = Schema.Struct({
3880
+ label: Schema.String,
3881
+ value: FlywheelStatsCriterionValue,
3882
+ target: FlywheelStatsCriterionValue,
3883
+ status: FlywheelStatsCriterionStatus,
3884
+ trend: Schema.optional(FlywheelStatsTrend),
3885
+ sampleSize: Schema.Number,
3886
+ dataSufficient: Schema.Boolean
3887
+ });
3888
+ const FlywheelStatsCriteria = Schema.Struct({
3889
+ c1_bugRate: FlywheelStatsCriterion,
3890
+ c2_p0Bugs: FlywheelStatsCriterion,
3891
+ c3_passRate: FlywheelStatsCriterion,
3892
+ c4_mttr: FlywheelStatsCriterion,
3893
+ c5_intervention: FlywheelStatsCriterion,
3894
+ c6_timeConsistency: FlywheelStatsCriterion,
3895
+ c7_flake: FlywheelStatsCriterion
3896
+ });
3897
+ const FlywheelStats = Schema.Struct({
3898
+ window: Schema.String,
3899
+ generatedAt: Schema.String,
3900
+ criteria: FlywheelStatsCriteria
3901
+ });
3902
+ const decodeFlywheelStats = Schema.decodeUnknownSync(FlywheelStats);
3903
+ const encodeFlywheelStats = Schema.encodeSync(FlywheelStats);
3904
+ //#endregion
3905
+ //#region src/context-layers.ts
3906
+ const CONTEXT_LAYER_KINDS = [
3907
+ "global",
3908
+ "project",
3909
+ "workspace"
3910
+ ];
3911
+ const CONTEXT_PREVIEW_HARNESSES = [
3912
+ "claude-code",
3913
+ "pi",
3914
+ "codex"
3915
+ ];
3916
+ const ContextLayerKind = Schema.Literals(CONTEXT_LAYER_KINDS);
3917
+ const ContextPreviewHarness = Schema.Literals(CONTEXT_PREVIEW_HARNESSES);
3918
+ const ContextProjectSummary = Schema.Struct({
3919
+ projectKey: Schema.String,
3920
+ name: Schema.String,
3921
+ path: Schema.String,
3922
+ issuePrefix: Schema.optional(Schema.String),
3923
+ tracker: Schema.optional(Schema.Literals([
3924
+ "linear",
3925
+ "github",
3926
+ "gitlab",
3927
+ "rally"
3928
+ ])),
3929
+ workspaceRoot: Schema.optional(Schema.String)
3930
+ });
3931
+ const ContextWorkspaceSummary = Schema.Struct({
3932
+ projectKey: Schema.String,
3933
+ path: Schema.String,
3934
+ name: Schema.String,
3935
+ issueId: Schema.optional(Schema.String),
3936
+ branch: Schema.optional(Schema.String)
3937
+ });
3938
+ const ContextLayerBase = {
3939
+ file: Schema.String,
3940
+ exists: Schema.Boolean,
3941
+ content: Schema.String,
3942
+ editable: Schema.Boolean
3943
+ };
3944
+ const GlobalContextLayerRecord = Schema.Struct({
3945
+ kind: Schema.Literal("global"),
3946
+ ...ContextLayerBase
3947
+ });
3948
+ const ProjectContextLayerRecord = Schema.Struct({
3949
+ kind: Schema.Literal("project"),
3950
+ projectKey: Schema.String,
3951
+ ...ContextLayerBase
3952
+ });
3953
+ const WorkspaceContextLayerRecord = Schema.Struct({
3954
+ kind: Schema.Literal("workspace"),
3955
+ projectKey: Schema.String,
3956
+ workspacePath: Schema.String,
3957
+ ...ContextLayerBase
3958
+ });
3959
+ const ContextEditableLayerRecord = Schema.Union([
3960
+ GlobalContextLayerRecord,
3961
+ ProjectContextLayerRecord,
3962
+ WorkspaceContextLayerRecord
3963
+ ]);
3964
+ const GlobalContextLayerTarget = Schema.Struct({ kind: Schema.Literal("global") });
3965
+ const ProjectContextLayerTarget = Schema.Struct({
3966
+ kind: Schema.Literal("project"),
3967
+ projectKey: Schema.String
3968
+ });
3969
+ const WorkspaceContextLayerTarget = Schema.Struct({
3970
+ kind: Schema.Literal("workspace"),
3971
+ projectKey: Schema.String,
3972
+ workspacePath: Schema.String
3973
+ });
3974
+ const ContextLayerTarget = Schema.Union([
3975
+ GlobalContextLayerTarget,
3976
+ ProjectContextLayerTarget,
3977
+ WorkspaceContextLayerTarget
3978
+ ]);
3979
+ const ContextLayerDraft = Schema.Struct({
3980
+ target: ContextLayerTarget,
3981
+ content: Schema.String
3982
+ });
3983
+ /**
3984
+ * A rendered output file `pan sync` writes a Overdeck-managed region into
3985
+ * (e.g. ~/.claude/CLAUDE.md, a project's CLAUDE.md or AGENTS.md). Distinct from
3986
+ * the editable *layer source* files — this describes the injection *target* so
3987
+ * the dashboard can show the user exactly where context lands and reassure them
3988
+ * their own content is preserved.
3989
+ */
3990
+ const ContextSyncTarget = Schema.Struct({
3991
+ harness: ContextPreviewHarness,
3992
+ layerKind: Schema.Literals(["global", "project"]),
3993
+ projectKey: Schema.optional(Schema.String),
3994
+ /** Short human label, e.g. "Claude Code · global". */
3995
+ label: Schema.String,
3996
+ /** Resolved absolute path of the target file. */
3997
+ path: Schema.String,
3998
+ exists: Schema.Boolean,
3999
+ /** True when the file already contains a Overdeck-managed region. */
4000
+ hasManagedRegion: Schema.Boolean,
4001
+ /** True when the file has hand-authored content outside the managed region. */
4002
+ hasUserContent: Schema.Boolean
4003
+ });
4004
+ const ContextLayersResponse = Schema.Struct({
4005
+ operation: Schema.Literal("load"),
4006
+ projects: Schema.Array(ContextProjectSummary),
4007
+ workspaces: Schema.Array(ContextWorkspaceSummary),
4008
+ layers: Schema.Array(ContextEditableLayerRecord),
4009
+ /** Injection targets `pan sync` writes managed regions into. */
4010
+ targets: Schema.Array(ContextSyncTarget)
4011
+ });
4012
+ const ContextPreviewRequest = Schema.Struct({
4013
+ operation: Schema.Literal("preview"),
4014
+ selectedLayer: ContextLayerTarget,
4015
+ drafts: Schema.Array(ContextLayerDraft)
4016
+ });
4017
+ const ContextHarnessPreviews = Schema.Struct({
4018
+ "claude-code": Schema.String,
4019
+ pi: Schema.String,
4020
+ codex: Schema.String,
4021
+ fullPrompt: Schema.String
4022
+ });
4023
+ const ContextPreviewDiagnostic = Schema.Struct({
4024
+ level: Schema.Literals([
4025
+ "info",
4026
+ "warning",
4027
+ "error"
4028
+ ]),
4029
+ message: Schema.String,
4030
+ layer: Schema.optional(ContextLayerTarget)
4031
+ });
4032
+ const ContextPreviewResponse = Schema.Struct({
4033
+ operation: Schema.Literal("preview"),
4034
+ previews: ContextHarnessPreviews,
4035
+ diagnostics: Schema.Array(ContextPreviewDiagnostic)
4036
+ });
4037
+ const ContextLayerSaveRequest = Schema.Struct({
4038
+ operation: Schema.Literal("save"),
4039
+ target: ContextLayerTarget,
4040
+ content: Schema.String
4041
+ });
4042
+ const ContextLayerSaveResponse = Schema.Struct({
4043
+ operation: Schema.Literal("save"),
4044
+ layer: ContextEditableLayerRecord,
4045
+ savedAt: Schema.String
4046
+ });
4047
+ const ContextSyncRequest = Schema.Struct({ operation: Schema.Literal("sync") });
4048
+ const ContextSyncResponse = Schema.Struct({
4049
+ operation: Schema.Literal("sync"),
4050
+ success: Schema.Boolean,
4051
+ exitCode: Schema.optional(Schema.Number),
4052
+ stdout: Schema.String,
4053
+ stderr: Schema.String,
4054
+ syncedAt: Schema.String
4055
+ });
4056
+ //#endregion
4057
+ //#region src/artifacts.ts
4058
+ const ArtifactId = Schema.String;
4059
+ const ArtifactSlug = Schema.String;
4060
+ const ArtifactHash = Schema.String;
4061
+ const ArtifactAgentRole = Schema.Literals([
4062
+ "plan",
4063
+ "work",
4064
+ "review",
4065
+ "test",
4066
+ "ship",
4067
+ "flywheel",
4068
+ "user"
4069
+ ]);
4070
+ const ArtifactAgentHarness = Schema.Literals([
4071
+ "claude-code",
4072
+ "pi",
4073
+ "codex",
4074
+ "user"
4075
+ ]);
4076
+ const ArtifactLifecycleState = Schema.Literals([
4077
+ "published",
4078
+ "pending_changes",
4079
+ "unshared"
4080
+ ]);
4081
+ const ArtifactValidationCode = Schema.Literals([
4082
+ "invalid_file_type",
4083
+ "not_a_file",
4084
+ "size_limit_exceeded",
4085
+ "forbidden_asset_url",
4086
+ "secret_detected",
4087
+ "secret_allowed",
4088
+ "high_entropy_string",
4089
+ "inline_event_handler",
4090
+ "missing_image_alt"
4091
+ ]);
4092
+ const ArtifactValidationFinding = Schema.Struct({
4093
+ code: ArtifactValidationCode,
4094
+ message: Schema.String,
4095
+ line: Schema.optional(Schema.Number),
4096
+ column: Schema.optional(Schema.Number),
4097
+ rule: Schema.optional(Schema.String),
4098
+ strict: Schema.optional(Schema.Boolean)
4099
+ });
4100
+ const ArtifactValidationResult = Schema.Struct({
4101
+ ok: Schema.Boolean,
4102
+ filePath: Schema.optional(Schema.String),
4103
+ size: Schema.Number,
4104
+ hash: ArtifactHash,
4105
+ strict: Schema.Boolean,
4106
+ errors: Schema.Array(ArtifactValidationFinding),
4107
+ warnings: Schema.Array(ArtifactValidationFinding)
4108
+ });
4109
+ const ArtifactMetadata = Schema.Struct({
4110
+ artifactId: ArtifactId,
4111
+ slug: ArtifactSlug,
4112
+ issueId: Schema.optional(Schema.NullOr(Schema.String)),
4113
+ workspaceId: Schema.optional(Schema.NullOr(Schema.String)),
4114
+ agentRole: Schema.optional(Schema.NullOr(ArtifactAgentRole)),
4115
+ agentHarness: Schema.optional(Schema.NullOr(ArtifactAgentHarness)),
4116
+ runId: Schema.optional(Schema.NullOr(Schema.String)),
4117
+ sessionId: Schema.optional(Schema.NullOr(Schema.String)),
4118
+ filePath: Schema.String,
4119
+ currentHash: ArtifactHash,
4120
+ lastPublishedHash: Schema.optional(Schema.NullOr(ArtifactHash)),
4121
+ supersedes: Schema.optional(Schema.NullOr(ArtifactId)),
4122
+ title: Schema.optional(Schema.NullOr(Schema.String)),
4123
+ description: Schema.optional(Schema.NullOr(Schema.String)),
4124
+ createdAt: Schema.String,
4125
+ publishedAt: Schema.optional(Schema.NullOr(Schema.String)),
4126
+ unsharedAt: Schema.optional(Schema.NullOr(Schema.String))
4127
+ });
4128
+ const ArtifactUrls = Schema.Struct({
4129
+ wrapperUrl: Schema.String,
4130
+ rawUrl: Schema.String
4131
+ });
4132
+ const ArtifactStatusResponse = Schema.Struct({
4133
+ artifact: Schema.optional(ArtifactMetadata),
4134
+ filePath: Schema.String,
4135
+ currentHash: ArtifactHash,
4136
+ lastPublishedHash: Schema.optional(Schema.NullOr(ArtifactHash)),
4137
+ pendingChanges: Schema.Boolean,
4138
+ validation: Schema.optional(ArtifactValidationResult)
4139
+ });
4140
+ const ArtifactCreateResponse = Schema.Struct({
4141
+ artifact: ArtifactMetadata,
4142
+ urls: ArtifactUrls,
4143
+ validation: ArtifactValidationResult,
4144
+ published: Schema.Boolean
4145
+ });
4146
+ const ArtifactPublishResponse = Schema.Struct({
4147
+ artifact: ArtifactMetadata,
4148
+ urls: ArtifactUrls,
4149
+ validation: ArtifactValidationResult,
4150
+ published: Schema.Boolean,
4151
+ pendingChanges: Schema.Boolean
4152
+ });
4153
+ const ArtifactUnshareResponse = Schema.Struct({
4154
+ artifact: ArtifactMetadata,
4155
+ unshared: Schema.Boolean
4156
+ });
4157
+ const ArtifactListEntry = Schema.Struct({
4158
+ artifact: ArtifactMetadata,
4159
+ urls: ArtifactUrls,
4160
+ status: ArtifactLifecycleState,
4161
+ pendingChanges: Schema.Boolean,
4162
+ thumbnailUrl: Schema.optional(Schema.String)
4163
+ });
4164
+ const ArtifactListResponse = Schema.Struct({ artifacts: Schema.Array(ArtifactListEntry) });
4165
+ const ArtifactDetailResponse = Schema.Struct({
4166
+ artifact: ArtifactMetadata,
4167
+ urls: ArtifactUrls,
4168
+ status: ArtifactLifecycleState,
4169
+ pendingChanges: Schema.Boolean
4170
+ });
4171
+ const WorkspaceArtifactsResponse = Schema.Struct({
4172
+ issueId: Schema.optional(Schema.String),
4173
+ workspaceId: Schema.optional(Schema.String),
4174
+ artifacts: Schema.Array(ArtifactListEntry)
4175
+ });
4176
+ //#endregion
4177
+ //#region src/feature-registry.ts
4178
+ const FeatureRegistryStatus = Schema.Literals([
4179
+ "active",
4180
+ "archived",
4181
+ "merged",
4182
+ "deferred"
4183
+ ]);
4184
+ const FeatureRegistryEntry = Schema.Struct({
4185
+ featureId: Schema.String,
4186
+ featureName: Schema.String,
4187
+ description: Schema.NullOr(Schema.String),
4188
+ owningWorkspaceId: Schema.NullOr(Schema.String),
4189
+ owningIssueId: Schema.NullOr(Schema.String),
4190
+ owningAgentId: Schema.NullOr(Schema.String),
4191
+ status: FeatureRegistryStatus,
4192
+ createdAt: Schema.String,
4193
+ updatedAt: Schema.String,
4194
+ tags: Schema.Array(Schema.String)
4195
+ });
4196
+ //#endregion
4197
+ export { Activity, ActivityDetailedEvent, ActivityEntryEvent, ActivityTtsEvent, ActivityUpdatedEvent, AgentActivityChangedEvent, AgentChannelReply, AgentChannelReplyEvent, AgentCompletedEvent, AgentContextSaturationChangedEvent, AgentCreatedEvent, AgentCurrentIssueSetEvent, AgentEnrichmentChangedEvent, AgentHeartbeatDeadEvent, AgentId, AgentMessageReceivedEvent, AgentModelSetEvent, AgentOutput, AgentOutputReceivedEvent, AgentPermissionRequestedEvent, AgentPermissionResolvedEvent, AgentResolution, AgentResolutionChangedEvent, AgentRuntimeSnapshot, AgentSnapshot, AgentStartedEvent, AgentStateRestoredEvent, AgentStatus, AgentStatusChangedEvent, AgentStoppedEvent, AgentThinkingStartedEvent, AgentThinkingStoppedEvent, AgentTurnDiffCompletedEvent, AgentWaitingClearedEvent, AgentWaitingStartedEvent, ArtifactAgentHarness, ArtifactAgentRole, ArtifactCreateResponse, ArtifactDetailResponse, ArtifactHash, ArtifactId, ArtifactLifecycleState, ArtifactListEntry, ArtifactListResponse, ArtifactMetadata, ArtifactPublishResponse, ArtifactSlug, ArtifactStatusResponse, ArtifactUnshareResponse, ArtifactUrls, ArtifactValidationCode, ArtifactValidationFinding, ArtifactValidationResult, CHANNEL_REPLY_KIND_VALUES, CONTEXT_LAYER_KINDS, CONTEXT_PREVIEW_HARNESSES, ChannelPermissionRequestSnapshot, ChannelReplyArtifactRef, ChannelReplyKind, ChatMessage, ClaudeChannelPermissionBehavior, CompactBoundary, ContextEditableLayerRecord, ContextHarnessPreviews, ContextLayerDraft, ContextLayerKind, ContextLayerSaveRequest, ContextLayerSaveResponse, ContextLayerTarget, ContextLayersResponse, ContextPreviewDiagnostic, ContextPreviewHarness, ContextPreviewRequest, ContextPreviewResponse, ContextProjectSummary, ContextSyncRequest, ContextSyncResponse, ContextSyncTarget, ContextUsage, ContextWorkspaceSummary, ConversationCompactingChangedEvent, ConversationCostSummary, ConversationCreatedEvent, ConversationEvent, ConversationFilter, ConversationPermissionChangedEvent, CostEventRecordedEvent, DEFAULT_MAX_MEMORY_OBSERVATIONS_PER_ISSUE, DEFAULT_MAX_TURN_DIFF_SUMMARIES_PER_AGENT, DashboardLifecycleCompletedEvent, DashboardLifecycleFailedEvent, DashboardLifecycleStartedEvent, DashboardSnapshot, DeepWipeRpc, DiscoveredSessionSnapshot, DomainEvent, EDITORS, EditorIdSchema, EmbedProgressEvent, EmbedProgressSnapshot, EmbedSessionsRpc, EnrichCompleteEvent, EnrichProgressEvent, EnrichProgressSnapshot, EnrichSessionsRpc, EnrichStatsSnapshot, ExtractionProviderConfig, ExtractionProviderTarget, FeatureNode, FeatureRegistryEntry, FeatureRegistryStatus, FlywheelAgent, FlywheelAgentStatus, FlywheelEffort, FlywheelHarness, FlywheelHeadline, FlywheelHttpUrl, FlywheelOrchestrator, FlywheelParkedItem, FlywheelPipelineItem, FlywheelPipelineStatus, FlywheelPipelineVerb, FlywheelRunId, FlywheelStats, FlywheelStatsCriteria, FlywheelStatsCriterion, FlywheelStatsCriterionStatus, FlywheelStatsCriterionValue, FlywheelStatsCriterionValueObject, FlywheelStatsTrend, FlywheelStatus, FlywheelSubstrateBug, FlywheelSubstrateBugStatus, FlywheelSuggestion, FlywheelSuggestionAction, FlywheelSuggestionPriority, FlywheelSystemStatus, GetAvailableEditorsRpc, GetConversationCostByWorkspaceRpc, GetConversationCostRpc, GetConversationStatsRpc, GetDiscoveredSessionRpc, GetSnapshotRpc, GetWorkspaceDetailRpc, GlobalContextLayerRecord, GlobalContextLayerTarget, INITIAL_READ_MODEL_STATE, IssueId, IssueStatusChangedEvent, IssuesSnapshotEvent, IssuesUpdatedEvent, ListDiscoveredSessionsRpc, MAX_CHANNEL_REPLY_ARTIFACT_LABEL_LENGTH, MAX_CHANNEL_REPLY_ARTIFACT_REFS, MAX_CHANNEL_REPLY_ARTIFACT_URI_LENGTH, MAX_CHANNEL_REPLY_SUMMARY_LENGTH, MemoryHealthChangedEvent, MemoryIdentity, MemoryObservation, MemoryObservationCreatedEvent, MemoryResetMarkerCreatedEvent, MemoryRollupTriggeredEvent, MemoryStatus, MemoryStatusPhase, MemoryStatusUpdatedEvent, MemoryTokenUsage, MergeReadyEvent, MergeStatusValue, OpenInEditorInput, OperatorInterventionEvent, PanRpcError, PanRpcGroup, PendingTurn, PipelineReviewCompletedEvent, PipelineReviewStartedEvent, PipelineStatusChangedEvent, PipelineTestCompletedEvent, PipelineTestStartedEvent, PlanItemStatusChangedEvent, PlanItemsUnblockedEvent, PlanSubitemStatusChangedEvent, PlanningFailedEvent, PlanningStartedEvent, PlanningSyncEvent, ProjectContextLayerRecord, ProjectContextLayerTarget, ProjectSessionTree, ProposedPlan, RagDecision, RagDecisionSource, ReadWorkspaceFileInput, ReadWorkspaceFileResult, ReadWorkspaceFileRpc, ReplayEventsRpc, ResetMarker, ResetMarkerScope, ResizeTerminalRpc, ResolveFilePathExistsInput, ResolveFilePathExistsResult, ResolveFilePathExistsRpc, ResourceStats, ResourcesUpdatedEvent, ReviewApprovedEvent, ReviewCoordinatorDiedEvent, ReviewCoordinatorStartedEvent, ReviewReviewerCompletedEvent, ReviewReviewerStartedEvent, ReviewSpecialistTimedOutEvent, ReviewStatusChangedEvent, ReviewStatusSnapshot, ReviewStatusValue, ReviewerRoundMetadata, ReviewerRoundSummary, Role, ScanCompleteEvent, ScanConversationsRpc, ScanProgressEvent, ScanProgressSnapshot, ScanResult, ScanStartedEvent, SearchConversationsRpc, SendTerminalInputRpc, SequenceNumber, SessionNode, SessionNodePresence, SessionNodeType, SessionTreeDelta, ShadowInferenceUpdateEvent, ShellOpenInEditorRpc, SpecialistCompletedEvent, SpecialistFailedEvent, SpecialistStartedEvent, StartAgentRpc, StartPlanningRpc, SubscribeAgentOutputRpc, SubscribeConversationMessagesRpc, SubscribeDomainEventsRpc, SubscribeFlywheelStatusRpc, SubscribeIssueEventsRpc, SubscribeProjectSessionTreeRpc, SubscribeTerminalRpc, SubstrateBugFiledEvent, SystemHealthSeverityChangedEvent, SystemHeartbeatEvent, TerminalCloseRpc, TerminalOpenRpc, TerminalOutput, TerminalResizeRpc, TerminalWriteRpc, TestPassedEvent, TestStatusValue, ThinkingState, TurnDiffFileChange, TurnDiffSummary, UatStatusValue, VerificationStatusValue, WS_METHODS, WaitingReason, WaitingState, WorkCompletedEvent, WorkLogEntry, WorkspaceAbortedEvent, WorkspaceArtifactsResponse, WorkspaceContextLayerRecord, WorkspaceContextLayerTarget, WorkspaceCreatedEvent, WorkspaceDeletedEvent, WorkspaceDestroyedEvent, WorkspaceDetail, WorkspaceWipeStartedEvent, applyEvent, applyEvents, decodeFlywheelStats, encodeFlywheelStats, getHarness, getMaxMemoryObservationsPerIssue, getMaxTurnDiffSummariesPerAgent, isChannelReplyKind, isTerminalTurnDiffSummaryStatus, normalizeChannelReplyPayload, omitTurnDiffSummariesForAgent, syncSnapshot, trimMemoryObservations, trimTurnDiffSummaries };
4198
+
4199
+ //# sourceMappingURL=index.mjs.map