You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## Before / after (verity mission `3a902278`, 1882 events)
@@ -52,66 +54,90 @@ The original symptom (74-second freezes on opening verity #1884)
52
54
disappeared after P1-#4..#10 alone. Subsequent items are
53
55
optimisations, not bug fixes.
54
56
57
+
## New measurements from this branch
58
+
59
+
| Item | Measurement |
60
+
| --- | --- |
61
+
|P2-#11/#12 virtualization |`dashboard/tests/control-perf.spec.ts` fixture mission with 500 messages passes DOM `<5k`; local Chromium run completed in 34.0s. |
62
+
|P3-#17 summarization |`inactive_stream_summary_reduces_large_payload_by_ten_x` covers the read-side collapse and asserts the synthetic payload reduction is at least 10x. |
63
+
|P4-#22 negotiated deltas |`text_op_stream_transform_converts_cumulative_delta_to_insert_then_replace` and `text_op_stream_transform_finalizes_before_assistant_message` cover the `cap=text_op` transport conversion path. |
64
+
|P4-#23 canonical rows |`finalized_text_ops_collapse_to_canonical_assistant_row` proves a finalized `text_op` log is replaced by one `assistant_message_canonical` row. |
65
+
|P5-#26 perf CI | Playwright `control @perf keeps large mission within browser budgets` asserts heap `<300MB`, max longtask `<500ms`, and DOM `<5k`. |
66
+
67
+
Validation commands run locally:
68
+
69
+
```bash
70
+
cargo fmt --all --check
71
+
cargo check -q
72
+
cargo test -q inactive_stream_summary --lib
73
+
cargo test -q text_op_stream_transform --lib
74
+
cargo test -q finalized_text_ops_collapse_to_canonical_assistant_row --lib
75
+
cd dashboard && npx tsc --noEmit
76
+
cd dashboard && bun run build
77
+
cd dashboard && PLAYWRIGHT_PORT=3001 PLAYWRIGHT_BASE_URL=http://localhost:3001 bunx playwright test tests/control-perf.spec.ts --project=chromium
|`mission`| When present, the server only emits events whose `mission_id` matches. `status` and `stream_lagged` (connection-scoped) always pass. Omit the param to receive every event the authenticated user can see (used by the mission list and the `?debug=perf` overlay). |
18
+
|`cap`| Optional comma-separated client capabilities. `text_op` asks the transport to convert cumulative `text_delta` events into negotiated CRDT-style `text_op` operations for this connection. Omit it for the cumulative compatibility path. |
18
19
19
20
Each line is one of:
20
21
@@ -26,6 +27,27 @@ Each line is one of:
26
27
-`event: <type>\ndata: <json>\n\n` — a single `AgentEvent`. See the
27
28
type list below.
28
29
30
+
## WebSocket channel
31
+
32
+
`GET /api/control/ws?mission=<uuid>&cap=text_op`
33
+
34
+
The WebSocket stream carries the same JSON `AgentEvent` payloads as SSE,
35
+
including the `type` discriminator. The dashboard attempts WebSocket first
36
+
and falls back to SSE if the upgrade fails.
37
+
38
+
Additional WebSocket-only messages:
39
+
40
+
- Server heartbeat every 15s: `{"seq": N}` where `N` is the latest stored
41
+
event sequence for the filtered mission, or `0` without a mission filter.
42
+
- Client resume request: `{"type":"resume","since_seq": N}`. When the
43
+
socket has a `mission=<uuid>` filter, the server fetches stored events
44
+
with `sequence > N`, converts known rows back into `AgentEvent` shape,
45
+
and sends them before continuing live broadcast delivery.
46
+
47
+
When `mission=<uuid>` is present, WebSocket uses the same per-mission
48
+
broadcast channel as SSE. Connection-scoped `status` and FIDO events still
49
+
come from the global channel.
50
+
29
51
### Event types
30
52
31
53
All events carry `mission_id` (optional for connection-scoped events).
@@ -37,6 +59,7 @@ Listed with the backends that emit them.
37
59
|`user_message`| server |`{id, mission_id, content, queued}`| Echoes the user message back after persisting. |
38
60
|`assistant_message`| server |`{id, mission_id, content, success, cost_cents?, cost_source?, model?, shared_files?}`| One per completed agent turn. **Cumulative content** — the message is the final consolidated text. |
39
61
|`text_delta`| grok, codex |`{mission_id, content, event_id?}`|**Cumulative buffer** — the `content` field contains the *entire* text so far, not the new tokens. Clients must consolidate by replacing, not appending. See "Continuation rule". |
62
+
|`text_op`| negotiated streaming backends |`{mission_id, bubble_id, ops}`| CRDT-style delta stream. `ops` entries are `insert`, `replace`, or `finalize`; clients apply them to a local buffer keyed by `bubble_id`. Backends only emit this when the client advertises support; `text_delta` remains the compatibility path. |
40
63
|`thinking`| grok, codex |`{mission_id, content, done, goal_role?, event_id?}`| Cumulative buffer. `done: true` finalises the current thought; subsequent non-prefix payloads start a new thought. |
41
64
|`tool_call`| all |`{mission_id, tool_call_id, name, args}`| One per tool invocation. |
42
65
|`tool_result`| all |`{mission_id, tool_call_id, name, result}`| Pairs with `tool_call` via `tool_call_id`. |
0 commit comments