todo: turn timer in chat, and a sub-agent spawn endpoint bounded by usage limits

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 05:44:47 +00:00
co-authored by Claude Fable 5
parent 6071ded175
commit a34ef422f4
6 changed files with 106 additions and 2 deletions
+4 -2
View File
@@ -16,7 +16,7 @@ the owner's OS user and can never be granted. Indirection there really is accide
## Themes
**73 open, 18 closed**, across 25 themes.
**76 open, 18 closed**, across 27 themes.
| Theme | Open | Closed |
| ------------------------------------------------------------------------------------------ | ---- | ------ |
@@ -24,6 +24,7 @@ the owner's OS user and can never be granted. Indirection there really is accide
| [Speaking into a session from the backend](todo/agent-delivery/) | 2 | 0 |
| [Backburner](todo/backburner/) | 2 | 0 |
| [Background jobs](todo/background-jobs/) | 1 | 0 |
| [Chat interface](todo/chat/) | 1 | 0 |
| [Code editor](todo/code-editor/) | 1 | 0 |
| [File watchers](todo/file-watchers/) | 1 | 0 |
| [Files app — routing](todo/files-app/) | 3 | 0 |
@@ -40,6 +41,7 @@ the owner's OS user and can never be granted. Indirection there really is accide
| [Plugin system — gaps the email extraction found (2026-08-18)](todo/plugin-system/) | 3 | 0 |
| [Prompt library](todo/prompt-library/) | 1 | 0 |
| [Shell — spotlight and the dock](todo/shell/) | 2 | 0 |
| [Sub-agents — agents spawning agents](todo/sub-agents/) | 2 | 0 |
| [Sudo terminal modal](todo/sudo-terminal-modal/) | 1 | 0 |
| [Tooling — the list about the list](todo/tooling/) | 1 | 0 |
| [Transmission](todo/transmission/) | 6 | 0 |
@@ -58,10 +60,10 @@ the owner's OS user and can never be granted. Indirection there really is accide
- `2026-08-21` **No way to see which affordances are reaching your agents, or to turn one off** — [affordances/no-way-to-see-which-affordances-are-reaching](todo/affordances/no-way-to-see-which-affordances-are-reaching.md)
- `2026-08-21` **Generalise it: deliver a turn to a named session, from anywhere in the backend** — [agent-delivery/generalise-it-deliver-a-turn-to-a-named](todo/agent-delivery/generalise-it-deliver-a-turn-to-a-named.md)
- `2026-08-21` **The cost of resuming grows with the transcript** — [agent-delivery/the-cost-of-resuming-grows-with-the-transcript](todo/agent-delivery/the-cost-of-resuming-grows-with-the-transcript.md)
- `2026-08-21` **Show how long a turn took, beside its cost and tokens** — [chat/show-how-long-a-turn-took-beside-its-cost](todo/chat/show-how-long-a-turn-took-beside-its-cost.md)
- `2026-08-21` **Watch a directory, and attach actions to what lands in it** — [file-watchers/watch-a-directory-and-attach-actions-to-what](todo/file-watchers/watch-a-directory-and-attach-actions-to-what.md)
- `2026-08-21` **A way to mark a commit "this one ships", so a release is not just `master`'s tip** — [fleet/a-way-to-mark-a-commit-this-one-ships-so-a](todo/fleet/a-way-to-mark-a-commit-this-one-ships-so-a.md)
- `2026-08-21` **iPhone Action Button → run an Officer task from a locked phone** — [mobile-apps/iphone-action-button-run-an-officer-task-from](todo/mobile-apps/iphone-action-button-run-an-officer-task-from.md)
- `2026-08-21` **The mobile recorder app, and the dispatcher agent behind it** — [mobile-apps/the-mobile-recorder-app-and-the-dispatcher](todo/mobile-apps/the-mobile-recorder-app-and-the-dispatcher.md)
- `2026-08-21` **A plugin's settings routes are reachable by anyone the plugin is granted to** — [multi-user/a-plugin-s-settings-routes-are-reachable-by](todo/multi-user/a-plugin-s-settings-routes-are-reachable-by.md)
- `2026-08-21` **A plugin cannot claim a WebSocket provider** — [plugin-infrastructure/a-plugin-cannot-claim-a-websocket-provider](todo/plugin-infrastructure/a-plugin-cannot-claim-a-websocket-provider.md)
- `2026-08-21` **A store of ready-made prompts, versioned, categorized and tagged** — [prompt-library/a-store-of-ready-made-prompts-versioned](todo/prompt-library/a-store-of-ready-made-prompts-versioned.md)
+7
View File
@@ -0,0 +1,7 @@
---
theme: chat
heading: "Chat interface"
kind: theme
---
The chat surface itself — what a turn shows about itself while and after it runs.
@@ -0,0 +1,25 @@
---
id: chat/show-how-long-a-turn-took-beside-its-cost
title: "Show how long a turn took, beside its cost and tokens"
theme: chat
status: open
created: 2026-08-21
date_source: stated
order: 1
---
**Show how long a turn took, beside its cost and tokens.** Wanted 2026-08-21. A timer running from the
start of each turn, and the elapsed time kept on the settled row — in the same place that already
renders cost and token count, not somewhere new.
That row is `MessageBubble.tsx:264`, and today it reads
`Done · ${message.cost.totalUSD.toFixed(3)} · {inputTokens + outputTokens} tokens`. Duration is the
third number that belongs there and is the only one missing.
Two things already exist and should be reused rather than rebuilt. `formatDuration` is in the same file
(~line 395), and the compaction row beside it already does exactly this job — its header comment says
the elapsed time is "honest for the window that watched it happen, and the settled row carries the
duration", which is the behaviour wanted here. What is missing is the field: `MessageCost` in
`Chat/types.ts:12` carries `totalUSD` and the token counters but no duration, while
`compact:done` carries `durationMs`. So the harness result has to bring it along, or the client has to
time the turn itself and keep it on the settled row.
+9
View File
@@ -0,0 +1,9 @@
---
theme: sub-agents
heading: "Sub-agents — agents spawning agents"
kind: theme
---
An agent asking Officer to run another agent, through a door the owner defines rather than whatever the
harness happens to offer. Related: `todo/agent-delivery/` is the other direction — the backend speaking
into a session that already exists.
@@ -0,0 +1,29 @@
---
id: sub-agents/an-api-endpoint-for-spawning-an-agent-with-a
title: "An API endpoint for spawning an agent with a prompt"
theme: sub-agents
status: open
created: 2026-08-21
date_source: stated
order: 1
---
**An API endpoint for spawning an agent with a prompt.** Wanted 2026-08-21. An agent calls it, names a
prompt, and gets another agent running. A sub-agent system — but **defined by the owner**, which is the
whole point of building one rather than leaning on the harness's own: the shapes, the limits and the
vocabulary are Officer's, so every harness gets the same door and the definition does not change when a
CLI does.
It has more of a starting point than it looks. `startClaudeTurn` already runs a turn with no browser
attached (`nudger.ts:102`, `ws: null`), so the mechanism for "run an agent from the backend" is proven —
what is missing is spawning a NEW one on request rather than feeding a known session, plus the surface
an agent can actually call, which is MCP or an authenticated route.
Open: whether a spawned agent is a Command HQ commander (named, listed, costed — `command_hq_sessions`
already does all three) or something lighter that disappears when it finishes; how its output returns to
the caller, streamed or awaited; and what the caller may set — model, effort, cwd, permission mode — versus
what Officer fixes.
**Bounded by the budget rule in `sub-agents/spawning-must-respect-the-account-s-own`, which is not
optional.** An endpoint that spawns freely is exactly how a weekly limit gets spent by something nobody
was watching.
@@ -0,0 +1,32 @@
---
id: sub-agents/spawning-must-respect-the-account-s-own
title: "Spawning must respect the account's own session and weekly limits"
theme: sub-agents
status: open
created: 2026-08-21
date_source: stated
order: 2
---
**Spawning must respect the account's own session and weekly limits.** Wanted 2026-08-21, as a
condition on the spawn endpoint rather than a later refinement. The failure it exists to prevent is
specific: a sub-agent burns the last of a window, and the chat or session the owner is actually in dies
mid-turn because the credits went somewhere nobody was watching.
**The numbers are already read.** `api/chat/claude-usage.ts` fetches the limit windows from Anthropic's
OAuth usage endpoint using the token the CLI stored at login, and exposes them as `UsageLimit` — a label
already fit to render (`Session`, `Weekly`, `Fable Weekly`) with its percentage. So the backend does not
need new plumbing to know how much room is left; it needs to consult what it has before starting work.
Two facts from that file that shape this, both stated in its own header:
- **The windows belong to the ANTHROPIC ACCOUNT, not to Officer.** They are spent by every `claude` on
the machine — this chat, a terminal, a task runner, a pipeline step. So a budget check has to reason
about a shared pool it does not control, and a reservation Officer makes for itself is a guess.
- **The module is owner-only and structurally so.** It takes no identity at all, deliberately, because
`getOwnerHomeDir` ignores the email it is passed. Any budget rule that has to answer "how much has
THIS user left" needs something that does not exist yet.
Open: what the endpoint does when the room is thin — refuse, queue until the window resets (the reset
time is in the same payload), or downgrade the model; and whether the reserve is a fixed floor or a
share the owner sets.