mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
fix(story-mode): the rest of the PR review: lookups, polling, frame budget, linear links
From review round 7 (PR review): - With no hook path (normal after a server restart), the transcript was looked up by conversation id only; it now also tries the Codeman id and a recovered session's `restored-<fragment>` transcript, as last-response does, and keeps what it found briefly (a lookup reads every project folder; a miss is kept only 4 s). - A session Story Mode cannot draw is not polled, and a page with nothing drawn yet is polled like a finished one (every 15 s), not every 4 s. - Helper steps: one request per helper is drawn, the newest; a failed refetch never writes over steps already shown. - The Director's cut toggle, a skin change and the fonts arriving redraw within the frame budget, not every panel in one task. - A bare name under an `ls` header finds its folder in one pass over the lines; a search per link over the text before it was quadratic (about 1.5 s for 700 links on agent output). - A page of many questions still keeps to 12 panels: questions fold last. - Opening Story Mode while it is open never starts a second runtime; the status announcer carries data-i18n-skip; the turn key in the ETag is clipped. - The crafted-input timing test runs at sizes where the old code took 6 to 29 s and the new one under 10 ms, with a 1.5 s bound. - Docs: `older`, `origin` (`scheduled`), which route answers a 500; mobile.css's stale comment; an orphaned doc comment. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e7ce3b8e56
commit
42d1c75a19
10 files changed
+191
-41
No files matched your search
@@ -1056,16 +1056,16 @@ What Story Mode reads (the opt-in header button, App Settings → Header buttons
|
||||
|
||||
### Routes
|
||||
|
||||
- `GET /api/sessions/:id/story[?turn=<id>]`: one turn as a page. `data` is `{ supported: true, turns, page }`: `turns` lists up to 60 turns, oldest first (`{ id, at, prompt, origin, complete, interrupted, superseded?, toolCalls }`), and `page` is the newest turn, or the one `turn=<id>` names, or `null` when there is none (an unknown id included). `older: true` means the transcript is longer than the part that was read, so older turns are not listed.
|
||||
- `GET /api/sessions/:id/story[?turn=<id>]`: one turn as a page. `data` is `{ supported: true, turns, page }`: `turns` lists up to 60 turns, oldest first (`{ id, at, prompt, origin, complete, interrupted, superseded?, toolCalls }`), and `page` is the newest turn, or the one `turn=<id>` names, or `null` when there is none (an unknown id included). `older: true` means older turns exist that `turns` leaves out: before the part of the transcript that was read, or past the 60 listed.
|
||||
- `GET /api/sessions/:id/story/helpers/:agentId`: one helper (an Agent call, `agentId` from its event's `helper.agentId`) drawn from its own transcript, `<conversation>/subagents/agent-<agentId>.jsonl` beside the session's. `data` is `{ supported: true, helper: { agentId, type?, description?, finished? }, page }`, the same page shape, or `page: null` while the helper has written nothing yet. `agentId` must match `[A-Za-z0-9_-]{1,64}` (`400` otherwise).
|
||||
|
||||
Both answer `{ supported: false, reason }` for a session they cannot draw: its CLI writes no Claude Code transcript (registry `capabilities.transcript` is not `claude-jsonl`), or it is remote or Docker, whose transcript is not on this host. Both are read-only. The session resolves like every session route (ownership via `findSessionOrFail`; another user's session is `404`), and the file read is the session's own transcript, never one the request names: the path the Claude hook reported when it really lies (symlinks resolved) in a Claude projects folder (the session's relocated `CLAUDE_CONFIG_DIR`, or the server's), else `<conversation id>.jsonl` under that folder. Only a regular file is read, through one non-blocking handle, so a FIFO or a device in its place fails at once. An unchanged file is a `304` decided from a stat, before anything is read. Only its tail is read (8 MB, widened in steps to 64 MB while that tail holds no prompt to start the turn); file contents, original files and images never leave the server. A transcript that cannot be read is a `500` with `OPERATION_FAILED`.
|
||||
Both answer `{ supported: false, reason }` for a session they cannot draw: its CLI writes no Claude Code transcript (registry `capabilities.transcript` is not `claude-jsonl`), or it is remote or Docker, whose transcript is not on this host. Both are read-only. The session resolves like every session route (ownership via `findSessionOrFail`; another user's session is `404`), and the file read is the session's own transcript, never one the request names: the path the Claude hook reported when it really lies (symlinks resolved) in a Claude projects folder (the session's relocated `CLAUDE_CONFIG_DIR`, or the server's), else `<conversation id>.jsonl` under that folder. Only a regular file is read, through one non-blocking handle, so a FIFO or a device in its place fails at once. An unchanged file is a `304` decided from a stat, before anything is read. Only its tail is read (8 MB, widened in steps to 64 MB while that tail holds no prompt to start the turn); file contents, original files and images never leave the server. On the page route, a transcript that cannot be read is a `500` with `OPERATION_FAILED`; the helper route answers `page: null` for a helper transcript it cannot read (one not written yet, or gone).
|
||||
|
||||
Responses carry a weak `ETag` with `Cache-Control: no-cache`; a matching `If-None-Match` gets a `304` without the page being rebuilt, which keeps a live view's frequent refetches cheap.
|
||||
|
||||
### Turns: where they start and end
|
||||
|
||||
A turn is one prompt and everything Claude did for it. `origin` says who sent the prompt: `human`, `task` (a background job reporting back), `peer` (another session) or `system`.
|
||||
A turn is one prompt and everything Claude did for it. `origin` says who sent the prompt: `human`, `task` (a background job reporting back), `peer` (another session), `scheduled` (a scheduled task's run) or `system`.
|
||||
|
||||
- **Start.** A user line with a prompt starts a turn. A prompt that arrives while Claude works (typed, a background job's report, a message from another session) is written by current Claude Code builds as a `queued_command` attachment, not a user line; it joins the running turn. A prompt written twice under one `promptId` (a typed `/compact`, then its command record) is one turn. A slash command is a prompt event with tool `Command`.
|
||||
- **End.** At `turn_duration`; at the Stop hook's `stop_hook_summary` (unless it prevented the stop) in sessions that never write `turn_duration`; at an interrupt; for a local slash command (`/compact`, `/model`), at its own output (`<local-command-stdout>`, kept as the prompt event's `output`). A turn still open when the next prompt starts has ended too, and is marked `superseded`.
|
||||
|
||||
Reference in new issue
Block a user