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