diff --git a/TODO.md b/TODO.md index 3e557def..705f61d2 100644 --- a/TODO.md +++ b/TODO.md @@ -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) diff --git a/todo/chat/_index.md b/todo/chat/_index.md new file mode 100644 index 00000000..877a1762 --- /dev/null +++ b/todo/chat/_index.md @@ -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. diff --git a/todo/chat/show-how-long-a-turn-took-beside-its-cost.md b/todo/chat/show-how-long-a-turn-took-beside-its-cost.md new file mode 100644 index 00000000..b0abe49e --- /dev/null +++ b/todo/chat/show-how-long-a-turn-took-beside-its-cost.md @@ -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. diff --git a/todo/sub-agents/_index.md b/todo/sub-agents/_index.md new file mode 100644 index 00000000..e488c416 --- /dev/null +++ b/todo/sub-agents/_index.md @@ -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. diff --git a/todo/sub-agents/an-api-endpoint-for-spawning-an-agent-with-a.md b/todo/sub-agents/an-api-endpoint-for-spawning-an-agent-with-a.md new file mode 100644 index 00000000..d388c4f0 --- /dev/null +++ b/todo/sub-agents/an-api-endpoint-for-spawning-an-agent-with-a.md @@ -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. diff --git a/todo/sub-agents/spawning-must-respect-the-account-s-own.md b/todo/sub-agents/spawning-must-respect-the-account-s-own.md new file mode 100644 index 00000000..94f55b00 --- /dev/null +++ b/todo/sub-agents/spawning-must-respect-the-account-s-own.md @@ -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.