1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
# Dispatch — tasks (live progress)
> **Live status + roadmap only.** Completed milestones are summarized, not
> narrated. Old blow-by-blow history is pruned — it lives in git (`git log`).
> Keep this lean and current; do not let it re-accrete a step-by-step changelog.
## Status (current)
`tsc -b` EXIT 0 · biome clean · **546 vitest + 89 bun = 635 tests**.
Built and verified live (full-fidelity: every feature is a manifest-loaded
extension through the host):
- **kernel** — contracts (ABI), bus, `runTurn` turn loop, extension host.
- **core extensions** — storage-sqlite, auth-apikey, provider-openai-compat
(OpenCode Go), conversation-store, session-orchestrator, transport-http,
credential-store; tool extension `read_file`.
- **observability** — structured Logger/Span ABI + journal-sink → out-of-process
collector → trace-store (`bun:sqlite`); host-bin supervises the collector;
nested turn→step→{prompt, provider.request, ttft, decode} spans; D5 verbatim
provider capture (self-redacted); `trace-replay` record/replay lib + fixtures.
- **CLI** — one-shot HTTP client (`bun packages/cli/src/main.ts`); `GET /models`,
`--cwd`, `--conversation`.
- **web frontend** — SEPARATE repo `../dispatch-web`. Slice 1 (surface system)
shipped via `ui-contract` + `surface-registry` + `transport-ws` +
`surface-loaded-extensions`. Slice 2 (browser chat) in progress there.
## How to run
```bash
KEY1=$(grep DISPATCH_API_KEY_OPENCODE1 .env | cut -d= -f2)
PORT=4567 DISPATCH_API_KEY="$KEY1" bun packages/host-bin/src/main.ts # boots app + collector
curl -s -X POST localhost:4567/chat -H 'content-type: application/json' \
-d '{"conversationId":"c1","message":"Say hello in 3 words."}' # field = conversationId
```
Process cleanup uses the `[x]` bracket trick (ORCHESTRATOR §8) — leaked
server/collector procs poison the next run's counts.
## Foundation (done — summarized; details in git)
- **MVP + multi-turn:** curl → transport-http → session-orchestrator →
host/registry → provider → OpenCode Go → AgentEvents → NDJSON;
`conversationId` threads history.
- **Post-MVP:** auth→provider seam; `read_file` tool (live tool-dispatch loop);
`getHostAPI()` hygiene; `tabId → conversationId` rename.
- **Observability Phase A/B:** the substrate + collector/store + supervision +
replay fixtures (see bullet list above).
- **CLI MVP:** credential-store + transport-contract + cli; model catalog; cwd
threading; multi-turn.
- **FE Slice 1:** the surface system across both repos (live WS probe verified).
- **FE Slice 2 backend prereqs:** `@dispatch/wire` split; per-chunk `seq` cursor;
read endpoint `GET /conversations/:id?sinceSeq=`; WS chat-deltas (transport-ws);
turn-lifecycle events (`turn-start`/`done`/`turn-sealed`); step grouping
(`stepId` on tool chunks/events); live stream metrics (`step-complete` +
`usage`/`done` token/timing — "Pass 1"); CORS.
## Metrics — token + timing (current milestone)
- [x] **Pass 1 — live stream metrics** (done): `step-complete` event +
`usage`(stepId) + `done`(durationMs + aggregate usage).
- [x] **Observability spans** (done): turn & step span-close stamp all four
`Usage` fields (added cacheRead/cacheWrite; normalized `usage_*` → `usage.*`).
- [x] **Pass 2 — persisted replay metrics** (done, was deferred): `StepMetrics`/
`TurnMetrics` wire types; conversation-store `appendMetrics`/`loadMetrics`
(separate key space, turn-append order); session-orchestrator accumulates
per-step+turn metrics from the event stream and persists after seal;
transport-http `GET /conversations/:id/metrics` → `ConversationMetricsResponse`.
`@dispatch/wire` + `@dispatch/transport-contract` → `0.4.0`. Commit `6db12ff`.
- [x] **Live-verified end-to-end** (against flash): live `step-complete`/`done`
metrics ↔ persisted `GET /conversations/:id/metrics` byte-match (aggregate +
per-step `stepId` + ttft/decode/genTotal + durationMs); journal turn/step spans
carry dotted `usage.*` incl. `usage.cacheReadTokens` (the #2 fix).
- [x] **FE courier handoff** written: `frontend-metrics-pass2-handoff.md` (in
this repo; user couriers to `../dispatch-web`; ORCHESTRATOR §7).
## Open items
- **dedup / storage growth (deferred):** trace-body de-dup (D5 volume control +
`prefix.fingerprint`) + rotation/compression/retention
(`notes/observability-design.md` §6, D9). `cacheReadTokens` is the cheap dedup
signal; thin/fat split already built.
- **D8 `prompt.assembly` segments:** deferred-by-design (await the context-filter
chain).
## Roadmap
1. **CLI** — done.
2. **Web frontend** (in progress, SEPARATE repo `../dispatch-web`; Svelte +
DaisyUI, same methodology). Slice 2 = browser chat MVP consuming the
wire/transport-contract + metrics. Cross-repo contract changes are couriered
via the user (ORCHESTRATOR §7); `lsp references` does not span repos.
3. **dedup / storage growth** — after the frontend (see Open items).
|