@schlessera/brain-ui-server 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/activity/digest.d.ts +29 -0
  2. package/dist/activity/digest.d.ts.map +1 -0
  3. package/dist/activity/digest.js +61 -0
  4. package/dist/activity/digest.js.map +1 -0
  5. package/dist/activity/notify.d.ts +62 -0
  6. package/dist/activity/notify.d.ts.map +1 -0
  7. package/dist/activity/notify.js +171 -0
  8. package/dist/activity/notify.js.map +1 -0
  9. package/dist/activity/push-sender.d.ts +63 -0
  10. package/dist/activity/push-sender.d.ts.map +1 -0
  11. package/dist/activity/push-sender.js +131 -0
  12. package/dist/activity/push-sender.js.map +1 -0
  13. package/dist/activity/query.d.ts +16 -0
  14. package/dist/activity/query.d.ts.map +1 -0
  15. package/dist/activity/query.js +128 -0
  16. package/dist/activity/query.js.map +1 -0
  17. package/dist/activity/recorder.d.ts +48 -0
  18. package/dist/activity/recorder.d.ts.map +1 -0
  19. package/dist/activity/recorder.js +205 -0
  20. package/dist/activity/recorder.js.map +1 -0
  21. package/dist/activity/runtime.d.ts +31 -0
  22. package/dist/activity/runtime.d.ts.map +1 -0
  23. package/dist/activity/runtime.js +109 -0
  24. package/dist/activity/runtime.js.map +1 -0
  25. package/dist/activity/span-sink.d.ts +22 -0
  26. package/dist/activity/span-sink.d.ts.map +1 -0
  27. package/dist/activity/span-sink.js +138 -0
  28. package/dist/activity/span-sink.js.map +1 -0
  29. package/dist/activity/store.d.ts +207 -0
  30. package/dist/activity/store.d.ts.map +1 -0
  31. package/dist/activity/store.js +440 -0
  32. package/dist/activity/store.js.map +1 -0
  33. package/dist/activity/stream.d.ts +45 -0
  34. package/dist/activity/stream.d.ts.map +1 -0
  35. package/dist/activity/stream.js +312 -0
  36. package/dist/activity/stream.js.map +1 -0
  37. package/dist/app.d.ts.map +1 -1
  38. package/dist/app.js +16 -0
  39. package/dist/app.js.map +1 -1
  40. package/dist/cron/scheduler.d.ts.map +1 -1
  41. package/dist/cron/scheduler.js +20 -5
  42. package/dist/cron/scheduler.js.map +1 -1
  43. package/dist/db/client.d.ts.map +1 -1
  44. package/dist/db/client.js +32 -6
  45. package/dist/db/client.js.map +1 -1
  46. package/dist/index.d.ts +4 -0
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +7 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/routes/activity.d.ts +20 -0
  51. package/dist/routes/activity.d.ts.map +1 -0
  52. package/dist/routes/activity.js +300 -0
  53. package/dist/routes/activity.js.map +1 -0
  54. package/dist/routes/push.d.ts +6 -0
  55. package/dist/routes/push.d.ts.map +1 -0
  56. package/dist/routes/push.js +73 -0
  57. package/dist/routes/push.js.map +1 -0
  58. package/dist/routes/sessions.d.ts.map +1 -1
  59. package/dist/routes/sessions.js +20 -4
  60. package/dist/routes/sessions.js.map +1 -1
  61. package/dist/ws/bridge.d.ts +2 -1
  62. package/dist/ws/bridge.d.ts.map +1 -1
  63. package/dist/ws/bridge.js +27 -2
  64. package/dist/ws/bridge.js.map +1 -1
  65. package/dist/ws/connection.d.ts.map +1 -1
  66. package/dist/ws/connection.js +9 -1
  67. package/dist/ws/connection.js.map +1 -1
  68. package/dist/ws/dispatch.d.ts.map +1 -1
  69. package/dist/ws/dispatch.js +11 -0
  70. package/dist/ws/dispatch.js.map +1 -1
  71. package/dist/ws/host.d.ts +16 -0
  72. package/dist/ws/host.d.ts.map +1 -1
  73. package/dist/ws/host.js +2 -0
  74. package/dist/ws/host.js.map +1 -1
  75. package/dist/ws/run-session.d.ts.map +1 -1
  76. package/dist/ws/run-session.js +21 -1
  77. package/dist/ws/run-session.js.map +1 -1
  78. package/migrations/007_activity.sql +114 -0
  79. package/migrations/008_push_subscriptions.sql +24 -0
  80. package/package.json +5 -3
  81. package/src/activity/digest.ts +92 -0
  82. package/src/activity/notify.ts +285 -0
  83. package/src/activity/push-sender.ts +215 -0
  84. package/src/activity/query.ts +160 -0
  85. package/src/activity/recorder.ts +261 -0
  86. package/src/activity/runtime.ts +141 -0
  87. package/src/activity/span-sink.ts +152 -0
  88. package/src/activity/store.ts +774 -0
  89. package/src/activity/stream.ts +366 -0
  90. package/src/app.ts +17 -0
  91. package/src/cron/scheduler.ts +28 -8
  92. package/src/db/client.ts +31 -6
  93. package/src/index.ts +22 -0
  94. package/src/routes/activity.ts +363 -0
  95. package/src/routes/push.ts +90 -0
  96. package/src/routes/sessions.ts +24 -4
  97. package/src/ws/bridge.ts +29 -2
  98. package/src/ws/connection.ts +9 -1
  99. package/src/ws/dispatch.ts +13 -0
  100. package/src/ws/host.ts +18 -0
  101. package/src/ws/run-session.ts +28 -1
@@ -0,0 +1,440 @@
1
+ export const SPAN_OUTCOMES = [
2
+ "success",
3
+ "error",
4
+ "timeout",
5
+ "cancelled",
6
+ "denied",
7
+ "interrupted",
8
+ ];
9
+ /** The one snake→camel mapper for rollup rows — every reader shares it. */
10
+ export function rowToRunRollup(r) {
11
+ return {
12
+ runId: r.run_id,
13
+ origin: r.origin,
14
+ name: r.name,
15
+ sessionId: r.session_id,
16
+ jobName: r.job_name,
17
+ startedAt: r.started_at,
18
+ endedAt: r.ended_at,
19
+ outcome: r.outcome,
20
+ durationMs: r.duration_ms,
21
+ spanCount: r.span_count,
22
+ inputTokens: r.input_tokens,
23
+ outputTokens: r.output_tokens,
24
+ cacheReadTokens: r.cache_read_tokens,
25
+ cacheCreationTokens: r.cache_creation_tokens,
26
+ costUsd: r.cost_usd,
27
+ failureReason: r.failure_reason,
28
+ detailPruned: r.detail_pruned === 1,
29
+ };
30
+ }
31
+ /** A single event payload is capped so no delta can approach the WS frame cap. */
32
+ export const MAX_EVENT_PAYLOAD_BYTES = 16_384;
33
+ export const TRUNCATION_MARKER = "…[truncated]";
34
+ export function createActivityStore(db, options = {}) {
35
+ const writer = options.writer ?? `pid:${process.pid}:${Date.now()}`;
36
+ // bun:sqlite transactions: `.immediate` takes the write lock up front, so a
37
+ // concurrent writer waits (busy_timeout) instead of failing mid-upgrade.
38
+ const inWrite = (fn) => db.transaction(fn).immediate();
39
+ function nextSeq(runId) {
40
+ const row = db
41
+ .query("SELECT COALESCE(MAX(seq), 0) AS hi FROM activity_changes WHERE run_id = ?")
42
+ .get(runId);
43
+ return row.hi + 1;
44
+ }
45
+ function logChange(runId, seq, spanId, eventIndex) {
46
+ db.query("INSERT INTO activity_changes (run_id, seq, span_id, event_index) VALUES (?, ?, ?, ?)").run(runId, seq, spanId, eventIndex ?? null);
47
+ }
48
+ function rowToSpan(r) {
49
+ return {
50
+ spanId: r.span_id,
51
+ runId: r.run_id,
52
+ parentSpanId: r.parent_span_id,
53
+ name: r.name,
54
+ kind: r.kind,
55
+ origin: r.origin,
56
+ sessionId: r.session_id,
57
+ jobName: r.job_name,
58
+ attrs: safeParse(r.attrs) ?? {},
59
+ startedAt: r.started_at,
60
+ waitUntil: r.wait_until,
61
+ endedAt: r.ended_at,
62
+ outcome: r.outcome,
63
+ outcomeReason: r.outcome_reason,
64
+ usage: {
65
+ inputTokens: r.input_tokens ?? undefined,
66
+ outputTokens: r.output_tokens ?? undefined,
67
+ cacheReadTokens: r.cache_read_tokens ?? undefined,
68
+ cacheCreationTokens: r.cache_creation_tokens ?? undefined,
69
+ costUsd: r.cost_usd ?? undefined,
70
+ model: r.model ?? undefined,
71
+ },
72
+ writer: r.writer,
73
+ lastHeartbeatAt: r.last_heartbeat_at,
74
+ };
75
+ }
76
+ function rowToEvent(r) {
77
+ const parsed = safeParse(r.payload);
78
+ return {
79
+ spanId: r.span_id,
80
+ eventIndex: r.event_index,
81
+ ts: r.ts,
82
+ eventType: r.event_type,
83
+ payload: parsed?.v,
84
+ truncated: parsed?.truncated === true,
85
+ };
86
+ }
87
+ function getSpanRaw(spanId) {
88
+ const r = db.query("SELECT * FROM activity_spans WHERE span_id = ?").get(spanId);
89
+ return r ? rowToSpan(r) : null;
90
+ }
91
+ function runHighWaterSeqRaw(runId) {
92
+ const row = db
93
+ .query("SELECT COALESCE(MAX(seq), 0) AS hi FROM activity_changes WHERE run_id = ?")
94
+ .get(runId);
95
+ return row.hi;
96
+ }
97
+ function endSpanInTx(spanId, input) {
98
+ const existing = db
99
+ .query("SELECT run_id, outcome, attrs FROM activity_spans WHERE span_id = ?")
100
+ .get(spanId);
101
+ if (!existing || existing.outcome !== null)
102
+ return false;
103
+ const endedAt = input.endedAt ?? Date.now();
104
+ const attrs = input.attrs
105
+ ? JSON.stringify({ ...(safeParse(existing.attrs) ?? {}), ...input.attrs })
106
+ : existing.attrs;
107
+ const u = input.usage ?? {};
108
+ db.query(`UPDATE activity_spans SET outcome = ?, outcome_reason = ?, ended_at = ?, attrs = ?,
109
+ input_tokens = COALESCE(?, input_tokens),
110
+ output_tokens = COALESCE(?, output_tokens),
111
+ cache_read_tokens = COALESCE(?, cache_read_tokens),
112
+ cache_creation_tokens = COALESCE(?, cache_creation_tokens),
113
+ cost_usd = COALESCE(?, cost_usd),
114
+ model = COALESCE(?, model)
115
+ WHERE span_id = ?`).run(input.outcome, input.reason ?? null, endedAt, attrs, u.inputTokens ?? null, u.outputTokens ?? null, u.cacheReadTokens ?? null, u.cacheCreationTokens ?? null, u.costUsd ?? null, u.model ?? null, spanId);
116
+ logChange(existing.run_id, nextSeq(existing.run_id), spanId);
117
+ return true;
118
+ }
119
+ /** Close every open span of the run (children first) and roll it up. */
120
+ function closeRunInTx(runId, outcome, reason) {
121
+ const open = db
122
+ .query("SELECT span_id FROM activity_spans WHERE run_id = ? AND outcome IS NULL ORDER BY started_at DESC")
123
+ .all(runId);
124
+ let closed = 0;
125
+ for (const { span_id } of open) {
126
+ if (endSpanInTx(span_id, { outcome, reason }))
127
+ closed++;
128
+ }
129
+ if (closed > 0)
130
+ rollupRunInTx(runId);
131
+ return closed;
132
+ }
133
+ function rollupRunInTx(runId) {
134
+ const spans = db
135
+ .query("SELECT * FROM activity_spans WHERE run_id = ? ORDER BY started_at")
136
+ .all(runId)
137
+ .map(rowToSpan);
138
+ if (spans.length === 0)
139
+ return;
140
+ const root = spans.find((s) => s.parentSpanId === null) ?? spans[0];
141
+ // Aggregation scope: ROOT spans only. Result-level accounting (the
142
+ // SDK's modelUsage) already includes subagent consumption — summing the
143
+ // tree would double-count every fan-out. Verified empirically (plan U3
144
+ // smoke test).
145
+ const failure = spans.find((s) => (s.outcome === "error" || s.outcome === "timeout") && s.outcomeReason != null)?.outcomeReason ?? (root.outcome === "interrupted" ? "interrupted" : null);
146
+ db.query(`INSERT INTO activity_run_rollups
147
+ (run_id, origin, name, session_id, job_name, started_at, ended_at, outcome,
148
+ duration_ms, span_count, input_tokens, output_tokens, cache_read_tokens,
149
+ cache_creation_tokens, cost_usd, failure_reason, detail_pruned)
150
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
151
+ ON CONFLICT(run_id) DO UPDATE SET
152
+ ended_at = excluded.ended_at, outcome = excluded.outcome,
153
+ duration_ms = excluded.duration_ms, span_count = excluded.span_count,
154
+ input_tokens = excluded.input_tokens, output_tokens = excluded.output_tokens,
155
+ cache_read_tokens = excluded.cache_read_tokens,
156
+ cache_creation_tokens = excluded.cache_creation_tokens,
157
+ cost_usd = excluded.cost_usd, failure_reason = excluded.failure_reason`).run(runId, root.origin, root.name, root.sessionId, root.jobName, root.startedAt, root.endedAt, root.outcome, root.endedAt !== null ? root.endedAt - root.startedAt : null, spans.length, root.usage.inputTokens ?? null, root.usage.outputTokens ?? null, root.usage.cacheReadTokens ?? null, root.usage.cacheCreationTokens ?? null, root.usage.costUsd ?? null, failure);
158
+ }
159
+ return {
160
+ writer,
161
+ startSpan(input) {
162
+ return inWrite(() => {
163
+ const startedAt = input.startedAt ?? Date.now();
164
+ db.query(`INSERT INTO activity_spans
165
+ (span_id, run_id, parent_span_id, name, kind, origin, session_id, job_name,
166
+ attrs, started_at, writer, last_heartbeat_at)
167
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.spanId, input.runId, input.parentSpanId ?? null, input.name, input.kind, input.origin, input.sessionId ?? null, input.jobName ?? null, input.attrs ? JSON.stringify(input.attrs) : null, startedAt, writer, input.parentSpanId ? null : startedAt);
168
+ logChange(input.runId, nextSeq(input.runId), input.spanId);
169
+ return getSpanRaw(input.spanId);
170
+ });
171
+ },
172
+ endSpan(spanId, input) {
173
+ return inWrite(() => endSpanInTx(spanId, input));
174
+ },
175
+ patchSpan(spanId, patch) {
176
+ return inWrite(() => {
177
+ const existing = db
178
+ .query("SELECT run_id, outcome, attrs FROM activity_spans WHERE span_id = ?")
179
+ .get(spanId);
180
+ if (!existing || existing.outcome !== null)
181
+ return false;
182
+ const attrs = patch.attrs
183
+ ? JSON.stringify({ ...(safeParse(existing.attrs) ?? {}), ...patch.attrs })
184
+ : existing.attrs;
185
+ const u = patch.usage ?? {};
186
+ db.query(`UPDATE activity_spans SET attrs = ?, wait_until = COALESCE(?, wait_until),
187
+ input_tokens = COALESCE(?, input_tokens),
188
+ output_tokens = COALESCE(?, output_tokens),
189
+ cache_read_tokens = COALESCE(?, cache_read_tokens),
190
+ cache_creation_tokens = COALESCE(?, cache_creation_tokens),
191
+ cost_usd = COALESCE(?, cost_usd),
192
+ model = COALESCE(?, model)
193
+ WHERE span_id = ?`).run(attrs, patch.waitUntil ?? null, u.inputTokens ?? null, u.outputTokens ?? null, u.cacheReadTokens ?? null, u.cacheCreationTokens ?? null, u.costUsd ?? null, u.model ?? null, spanId);
194
+ logChange(existing.run_id, nextSeq(existing.run_id), spanId);
195
+ return true;
196
+ });
197
+ },
198
+ appendEvent(spanId, eventType, payload, ts) {
199
+ return inWrite(() => {
200
+ const span = db
201
+ .query("SELECT run_id FROM activity_spans WHERE span_id = ?")
202
+ .get(spanId);
203
+ if (!span)
204
+ return null;
205
+ const next = db
206
+ .query("SELECT COALESCE(MAX(event_index), -1) + 1 AS idx FROM activity_events WHERE span_id = ?")
207
+ .get(spanId);
208
+ const stored = capPayload(payload);
209
+ db.query("INSERT INTO activity_events (span_id, event_index, ts, event_type, payload) VALUES (?, ?, ?, ?, ?)").run(spanId, next.idx, ts ?? Date.now(), eventType, JSON.stringify(stored));
210
+ logChange(span.run_id, nextSeq(span.run_id), spanId, next.idx);
211
+ const r = db
212
+ .query("SELECT * FROM activity_events WHERE span_id = ? AND event_index = ?")
213
+ .get(spanId, next.idx);
214
+ return rowToEvent(r);
215
+ });
216
+ },
217
+ heartbeat(spanId, at) {
218
+ inWrite(() => {
219
+ // Deliberately NOT change-logged: a heartbeat is liveness metadata,
220
+ // not activity the client needs a delta for.
221
+ db.query("UPDATE activity_spans SET last_heartbeat_at = ? WHERE span_id = ? AND outcome IS NULL").run(at ?? Date.now(), spanId);
222
+ });
223
+ },
224
+ cascadeClose(runId, outcome, reason) {
225
+ return inWrite(() => closeRunInTx(runId, outcome, reason));
226
+ },
227
+ sweepOwnOrphans() {
228
+ // "Own" means this PROCESS IDENTITY's rows from a previous life. The
229
+ // writer stamp includes the start time, so rows written by the
230
+ // current instance never match a fresh store's sweep... but a boot
231
+ // sweep runs before any spans are written, so sweeping by pid-prefix
232
+ // alone would be wrong across pid reuse. Sweep every session-origin
233
+ // open span instead: only THIS server writes session spans, and at
234
+ // boot none of ours can legitimately be open.
235
+ //
236
+ // Candidates are read OUTSIDE the write transaction — the common case
237
+ // finds nothing and must not take the exclusive lock for it. The close
238
+ // loop re-checks open-ness inside the transaction (endSpanInTx is
239
+ // write-once), so a race just skips.
240
+ const open = db
241
+ .query("SELECT DISTINCT run_id FROM activity_spans WHERE outcome IS NULL AND origin = 'session'")
242
+ .all();
243
+ if (open.length === 0)
244
+ return 0;
245
+ return inWrite(() => {
246
+ let closed = 0;
247
+ for (const { run_id } of open) {
248
+ closed += closeRunInTx(run_id, "interrupted", "server restarted");
249
+ }
250
+ return closed;
251
+ });
252
+ },
253
+ sweepStale(staleAfterMs, now) {
254
+ const cutoff = (now ?? Date.now()) - staleAfterMs;
255
+ // Staleness is judged on ROOT heartbeat age — never span age — so a
256
+ // legitimately long, quiet run with a live writer is never killed.
257
+ // Candidate read outside the write transaction, same as sweepOwnOrphans.
258
+ const staleRoots = db
259
+ .query(`SELECT span_id, run_id FROM activity_spans
260
+ WHERE outcome IS NULL AND parent_span_id IS NULL
261
+ AND writer != ? AND COALESCE(last_heartbeat_at, started_at) < ?`)
262
+ .all(writer, cutoff);
263
+ if (staleRoots.length === 0)
264
+ return 0;
265
+ return inWrite(() => {
266
+ let closed = 0;
267
+ for (const { run_id } of staleRoots) {
268
+ closed += closeRunInTx(run_id, "interrupted", "writer went silent");
269
+ }
270
+ return closed;
271
+ });
272
+ },
273
+ findStuck(thresholdMs, now) {
274
+ const cutoff = (now ?? Date.now()) - thresholdMs;
275
+ return db
276
+ .query("SELECT * FROM activity_spans WHERE outcome IS NULL AND parent_span_id IS NULL AND started_at < ?")
277
+ .all(cutoff).map(rowToSpan);
278
+ },
279
+ changesSince(changeCursor, limit = 500) {
280
+ // Set-based: one join per change kind instead of a lookup per row. A
281
+ // change whose span/event row was pruned drops out via the join, same
282
+ // as the old per-row miss.
283
+ const spanRows = db
284
+ .query(`SELECT c.change_id, c.run_id, c.seq, s.*
285
+ FROM activity_changes c
286
+ JOIN activity_spans s ON s.span_id = c.span_id
287
+ WHERE c.change_id > ? AND c.event_index IS NULL
288
+ ORDER BY c.change_id LIMIT ?`)
289
+ .all(changeCursor, limit);
290
+ const eventRows = db
291
+ .query(`SELECT c.change_id, c.run_id, c.seq, e.*
292
+ FROM activity_changes c
293
+ JOIN activity_events e
294
+ ON e.span_id = c.span_id AND e.event_index = c.event_index
295
+ WHERE c.change_id > ? AND c.event_index IS NOT NULL
296
+ ORDER BY c.change_id LIMIT ?`)
297
+ .all(changeCursor, limit);
298
+ const changes = [
299
+ ...spanRows.map((r) => ({
300
+ changeId: r.change_id,
301
+ runId: r.run_id,
302
+ seq: r.seq,
303
+ kind: "span",
304
+ span: rowToSpan(r),
305
+ })),
306
+ ...eventRows.map((r) => ({
307
+ changeId: r.change_id,
308
+ runId: r.run_id,
309
+ seq: r.seq,
310
+ kind: "event",
311
+ event: rowToEvent(r),
312
+ })),
313
+ ];
314
+ changes.sort((a, b) => a.changeId - b.changeId);
315
+ return changes.length > limit ? changes.slice(0, limit) : changes;
316
+ },
317
+ terminalRootChangesSince(changeCursor, limit = 500) {
318
+ const rows = db
319
+ .query(`SELECT MAX(c.change_id) AS change_id, s.*
320
+ FROM activity_changes c
321
+ JOIN activity_spans s ON s.span_id = c.span_id
322
+ WHERE c.change_id > ? AND c.event_index IS NULL
323
+ AND s.parent_span_id IS NULL AND s.outcome IS NOT NULL
324
+ GROUP BY s.span_id
325
+ ORDER BY change_id LIMIT ?`)
326
+ .all(changeCursor, limit);
327
+ return rows.map((r) => ({ changeId: r.change_id, span: rowToSpan(r) }));
328
+ },
329
+ latestChangeCursor() {
330
+ const row = db
331
+ .query("SELECT COALESCE(MAX(change_id), 0) AS hi FROM activity_changes")
332
+ .get();
333
+ return row.hi;
334
+ },
335
+ snapshotRun(runId) {
336
+ // A read transaction so spans, events, high-water seq, and the global
337
+ // cursor are one consistent picture — the poller baseline is pinned to
338
+ // exactly this moment (no gap between snapshot and first delta).
339
+ return db.transaction(() => {
340
+ const spans = db
341
+ .query("SELECT * FROM activity_spans WHERE run_id = ? ORDER BY started_at")
342
+ .all(runId).map(rowToSpan);
343
+ if (spans.length === 0)
344
+ return null;
345
+ const events = db
346
+ .query(`SELECT e.* FROM activity_events e
347
+ JOIN activity_spans s ON s.span_id = e.span_id
348
+ WHERE s.run_id = ? ORDER BY e.ts, e.event_index`)
349
+ .all(runId).map(rowToEvent);
350
+ const cursor = db
351
+ .query("SELECT COALESCE(MAX(change_id), 0) AS hi FROM activity_changes")
352
+ .get();
353
+ return {
354
+ runId,
355
+ spans,
356
+ events,
357
+ highWaterSeq: runHighWaterSeqRaw(runId),
358
+ changeCursor: cursor.hi,
359
+ };
360
+ })();
361
+ },
362
+ runHighWaterSeq: runHighWaterSeqRaw,
363
+ getSpan: getSpanRaw,
364
+ openRootSpans() {
365
+ return db
366
+ .query("SELECT * FROM activity_spans WHERE outcome IS NULL AND parent_span_id IS NULL ORDER BY started_at DESC")
367
+ .all().map(rowToSpan);
368
+ },
369
+ rollupRun(runId) {
370
+ inWrite(() => rollupRunInTx(runId));
371
+ },
372
+ prune(options) {
373
+ const now = options.now ?? Date.now();
374
+ const batch = options.batch ?? 50;
375
+ // The digest floor gates pruning, but a dead digest job must not freeze
376
+ // it forever: the hard ceiling prunes regardless (marking the rollup so
377
+ // the coverage gap is visible).
378
+ const floor = Math.max(options.digestFloorAt, 0);
379
+ const ceiling = now - options.hardCeilingMs;
380
+ return inWrite(() => {
381
+ // Candidates come from the rollups (they exist for every finished
382
+ // run — that terminal write is what made the run prunable), guarded
383
+ // against any run that still has an open span.
384
+ const candidates = db
385
+ .query(`SELECT run_id, ended_at AS ended FROM activity_run_rollups r
386
+ WHERE detail_pruned = 0 AND ended_at IS NOT NULL
387
+ AND (ended_at < ? OR ended_at < ?)
388
+ AND NOT EXISTS (
389
+ SELECT 1 FROM activity_spans s
390
+ WHERE s.run_id = r.run_id AND s.outcome IS NULL
391
+ )
392
+ LIMIT ?`)
393
+ .all(floor, ceiling, batch);
394
+ let spansDeleted = 0;
395
+ for (const { run_id, ended } of candidates) {
396
+ db.query("UPDATE activity_run_rollups SET detail_pruned = 1 WHERE run_id = ?").run(run_id);
397
+ if (ended >= floor) {
398
+ // Pruned by the hard ceiling alone — the digest never covered
399
+ // this run; make the coverage gap visible on the rollup.
400
+ db.query("UPDATE activity_run_rollups SET failure_reason = COALESCE(failure_reason, 'digest coverage gap') WHERE run_id = ?").run(run_id);
401
+ }
402
+ db.query(`DELETE FROM activity_events WHERE span_id IN
403
+ (SELECT span_id FROM activity_spans WHERE run_id = ?)`).run(run_id);
404
+ const res = db.query("DELETE FROM activity_spans WHERE run_id = ?").run(run_id);
405
+ spansDeleted += res.changes;
406
+ db.query("DELETE FROM activity_changes WHERE run_id = ?").run(run_id);
407
+ }
408
+ // The change log only serves live polling; rows this deep in the past
409
+ // are unreachable by any live cursor. Keep runs with open spans.
410
+ db.query(`DELETE FROM activity_changes WHERE change_id < (
411
+ SELECT COALESCE(MAX(change_id), 0) FROM activity_changes
412
+ ) - 100000 AND run_id NOT IN (
413
+ SELECT DISTINCT run_id FROM activity_spans WHERE outcome IS NULL
414
+ )`).run();
415
+ return { runsPruned: candidates.length, spansDeleted };
416
+ });
417
+ },
418
+ };
419
+ }
420
+ function safeParse(text) {
421
+ if (!text)
422
+ return undefined;
423
+ try {
424
+ return JSON.parse(text);
425
+ }
426
+ catch {
427
+ return undefined;
428
+ }
429
+ }
430
+ /** Cap a payload's serialized size, storing an explicit truncation marker. */
431
+ function capPayload(payload) {
432
+ const json = JSON.stringify(payload ?? null);
433
+ if (json.length <= MAX_EVENT_PAYLOAD_BYTES)
434
+ return { v: payload ?? null };
435
+ if (typeof payload === "string") {
436
+ return { v: payload.slice(0, MAX_EVENT_PAYLOAD_BYTES) + TRUNCATION_MARKER, truncated: true };
437
+ }
438
+ return { v: json.slice(0, MAX_EVENT_PAYLOAD_BYTES) + TRUNCATION_MARKER, truncated: true };
439
+ }
440
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/activity/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Live activity streaming: view-scoped subscriptions, snapshot-then-delta.
3
+ *
4
+ * All emission is change-log driven — one code path for the server's own
5
+ * writes and a foreign writer's (the cron wrapper). A write on this process
6
+ * calls `pump()` directly (near-zero latency); foreign writes are picked up
7
+ * by a fast poll interval that runs ONLY while at least one subscription is
8
+ * live. The pump reads `changesSince(cursor)` and fans each committed change
9
+ * out as an `activity_delta` to matching subscribers, so ordering is the
10
+ * change log's and a delta can never precede its commit (persist-then-emit).
11
+ *
12
+ * The subscribe handler answers with a snapshot whose per-run high-water seq
13
+ * and pump baseline come from ONE store read transaction — there is no gap
14
+ * window between snapshot and first delta, and no duplication the client's
15
+ * seq-discard rule cannot handle.
16
+ *
17
+ * This is deliberately the only place broadcast is per-connection filtered;
18
+ * every other frame keeps the host's broadcast-to-all semantics.
19
+ */
20
+ import type { ActivitySpan, ActivitySpanEvent, ClientActivitySubscribe, ClientActivityUnsubscribe } from "@schlessera/brain-ui-sdk/protocol";
21
+ import type { Logger } from "@opentelemetry/api-logs";
22
+ import type { WSContext } from "../ws/clients.js";
23
+ import type { ActivityStore, SpanEventRow, SpanRow } from "./store.js";
24
+ /** Store row → wire span. Lifts the typed fields the protocol promotes
25
+ * (toolName, subagent enrichment) out of the naming/attr conventions. */
26
+ export declare function toWireSpan(span: SpanRow): ActivitySpan;
27
+ export declare function toWireEvent(event: SpanEventRow): ActivitySpanEvent;
28
+ export interface ActivityStream {
29
+ handleSubscribe(ws: WSContext, msg: ClientActivitySubscribe): void;
30
+ handleUnsubscribe(ws: WSContext, msg: ClientActivityUnsubscribe): void;
31
+ /** Forget a closed socket. */
32
+ dropConnection(ws: WSContext): void;
33
+ /** Drain new committed changes to subscribers. Reentrancy-safe. */
34
+ pump(): void;
35
+ /** Whether any connection currently watches the given session's activity. */
36
+ isWatched(scope: {
37
+ sessionId?: string;
38
+ runId?: string;
39
+ }): boolean;
40
+ /** Number of live subscriptions (all connections). */
41
+ subscriptionCount(): number;
42
+ close(): void;
43
+ }
44
+ export declare function createActivityStream(store: ActivityStore, log?: Logger): ActivityStream;
45
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/activity/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,EACvB,yBAAyB,EAG1B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,KAAK,EAAkB,aAAa,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiBvF;0EAC0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CA8BtD;AAgBD,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,iBAAiB,CASlE;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACnE,iBAAiB,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,yBAAyB,GAAG,IAAI,CAAC;IACvE,8BAA8B;IAC9B,cAAc,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAAC;IACpC,mEAAmE;IACnE,IAAI,IAAI,IAAI,CAAC;IACb,6EAA6E;IAC7E,SAAS,CAAC,KAAK,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;IAClE,sDAAsD;IACtD,iBAAiB,IAAI,MAAM,CAAC;IAC5B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAwOvF"}