Cache task events on disk per server; fix performance audit findings

This commit is contained in:
Ian Gulliver
2026-10-23 12:49:43 -07:00
parent dc2fcc6075
commit d6be98f9d8
13 changed files with 448 additions and 250 deletions
+1
View File
@@ -30,6 +30,7 @@ NDJSON both ways: one JSON object per line on stdin and stdout. Non-JSON stdout
- Newer CLIs also send `command_lifecycle` lines (`command_uuid`, `state` such as `completed`). byre drops them, as it does `keep_alive`.
- The `init` event arrives with the first turn, not at launch, so the session ID is unknown until the first message is sent.
- While a tool runs, `tool_progress` lines with `"heartbeat": true`, the tool's `elapsed_time_seconds`, and a `parent_tool_use_id` naming the tool call arrive every 30 seconds. A Bash call also brings `system` `task_started` and `task_notification` lines around it.
- `system` lines with subtype `status` (`"status": "requesting"` as each model request starts) and `thinking_tokens` (a running `estimated_tokens` while the model thinks, several a second) are progress only. byre stores neither them nor `tool_progress`; the other lines it doesn't handle are kept as `agent.vendor` events.
- Subagents (the `Agent` tool, listed in `init` tools as `Task`): `system` `task_started` (`task_type` `local_agent`, `tool_use_id` naming the `Agent` call, `subagent_type`, `description`, `prompt`, `is_backgrounded`), `task_progress` (`description` of what it is doing), `task_updated` (by `task_id` only), and `task_notification` (`status`, `summary`). `assistant` and `user` lines from inside the subagent carry `parent_tool_use_id` set to the `Agent` call's ID, plus `agent_id`, `subagent_type` and `task_description`. A foreground subagent (`run_in_background: false`) sends only its prompt this way, as a `user` text block, and its final report arrives as the `Agent` call's `tool_result`. A background one (the default) streams all its assistant and tool-result lines; the `Agent` call's `tool_result` comes back at once saying it launched, the turn can end with its own `result` while the subagent runs, and when the subagent finishes the CLI starts another turn by itself, ending in a second `result` carrying `origin.kind` `task-notification`.
## Control channel
+2
View File
@@ -48,6 +48,8 @@ The update UI must not offer Skip or turning automatic updates off: on a Mac run
CloudKit has no unique constraints, so two Macs can each create a record for the same thing; duplicate records are expected. See `docs/operations.md`, "CloudKit schema", before changing the SwiftData models. The store is named `byre.store` because the app isn't sandboxed, so SwiftData's default `default.store` would be shared with every other unsandboxed app. Deleting the local files while the app isn't running makes the next launch download the records again.
Each task's events are cached on disk per server, under `run.fc.byre.mac/events/<server host>/` in Application Support, so a launch fetches only the events newer than the cached ones. Events never change, so the cache is keyed on the one thing that rewrites them: the `schema` fingerprint in `/api/status` (the server's applied migrations), and the whole server's cache is dropped when it differs. A task's file goes when the task closes, and a file that stops decoding is cut at its last good line, with the rest fetched again. A server restart replaces the Store while the old one still shows the transcripts, so each connection's cache opens with a generation and takes appends only from feeds loaded under the current one; a file is only ever extended by a writer that read it first.
## Gaps in SwiftUI
Places where SwiftUI alone may fall short, each checked side by side against its fallback; the fallback is used only where the check failed: