feat(web,hosted): subagents get a short title as coding agents do, carried on the wire

Merged
DA
8d ago
+1049-96

What

Dean, on the agent tab hover card (#2049): "Subagent cards are unreadable because the name is the whole task. Use a short title. If coding agents already get an automatic title … apply the same title step to subagents on the service, both coder workers and Luke's planning workers, and carry the title on the wire."

This is the service half. #2049 is stacked on top of it and uses these titles on the desktop.

  • One title step. Coding agents, coder workers (an agent's delegate workers), and Luke's planning workers are all named by the same small Haiku call (coder-host/title.ts). It uses the same tidying and the same bound. A deployment without an Anthropic key produces no titles.
    • A subagent is named from its task alone, since it has no plan of its own.
    • The shared instruction now says "Name the task below" instead of "Name the coding task below".
  • Where titles are stored. On the worker's child conversation, in the existing conversations.title column. Nothing read that column for child rows before (it served observed sessions only).
    • The title is filled only while the column is empty (child-titles.ts), the same rule coding agent titles use.
    • It is generated on a detached fiber, so the worker never waits for it.
  • Migration 0079 (renumbered from 0078 after #2068 took that number). Adds a nullable call_id to coding_agent_delegation, so an agent's page can name each worker's title by the call that started it. A child's id is a hash and can't be reversed into a call id.
  • On the wire:
    • codingAgentMessagesAnswerSchema.subagentTitles?: { callId, title }[] lists every titled worker on the agent's own page, not only those in that page, because a title lands after the page that carried the delegation.
    • A worker's own page fills its existing title field. It used to always be null.
    • planWorkSessionSchema.title? is filled when the plan's work is read back and in the live plan.work frame.
    • The JSON Schema goldens were regenerated with LUKE_UPDATE_FIXTURES=1. The only diff is these three additions.
  • Review fixes (Devin):
    • A planning worker is always titled from its first task, read from its child conversation. A later task arriving while the first title call is still out can't take the name.
    • An ended agent's or ended worker's read, with nothing new, now waits for a title that may still land: an untitled worker delegated within the title call's 20s bound. The final page then carries the title. A deployment without a title model never waits. The desktop is unchanged.
  • Planning workers' model and effort: not added.
    • The planning brain sets no reasoning effort, so OpenAI applies an unstated default.
    • Its turns record the bare OpenAI id, not the catalog form the desktop names.
    • Carrying either would mean guessing. Doing it properly needs the planning model to set and record an explicit effort and a catalog model id, as the coder does.

Tests

  • coder-host.test.ts:
    • A worker's title is generated from its delegated task, tidied, written once on its child, and named by call on the agent's page.
    • A failed title call, or a deployment with no title model, leaves the worker untitled.
  • coding-agents-app.test.ts: the worker's title is on its own page, and every titled worker is on the agent's page by call.
  • hosted-brain-host-tools.test.ts: a planning worker is titled once from its first task, on the child conversation of the call that started it. A failed call, or no title model, leaves it untitled.
  • plan-work.test.ts: the plan's work read back carries a worker's title, on the call that started it and on a later call steering the same task.
  • voice-live-brain.test.ts: a title that lands while the turn is followed reaches the live session.

./scripts/check.sh exits 0 locally, and pnpm --filter @luke/web test:store passes (607 tests). This PR's preview had applied the old 0078, so its own preview/conductor/subagent-titles Neon branch was reset from its parent; it is not primary, default, or protected. This touches no desktop renderer code, so there's no Mac UI to verify. verify.sh was not run (Linux VM).

šŸ¤– Generated with Claude Code