refactor: delete the per-task Luke

Merged
DA
5d ago
+2716-19213

PR 7 of the Unified agents rollout (STAG-322). It deletes everything that served only the per-task Luke, the planner brain each task ran in its own plan conversation, and keeps everything coding agents use. Every agent is now a coding agent on /eve/coder, and a call speaks into one.

Held: don't merge this until the developer says so; see "Merge conditions" below.

Merge conditions

  • Desktop first. The latest desktop release, v0.18.0, predates the whole rollout. Its calls send no agentId, and it reads plans.transcript, plans.work, messages.task and messages.taskWorker, all of which this PR removes. So this PR may deploy only once a desktop release that includes #4300 and later is out and in use.
  • With 7b. It merges together with the data PR 7b (see "For 7b" below).

What users stop seeing

Their data stays in the database until 7b; only the reads and views go.

  • Luke-era calls without an agent. A task's calls from before calls spoke into agents (voice_sessions rows with no agent) are no longer drawn; every call shows in its agent's tab now.
  • Code Luke showed. The Code tab reads only coding agents' show_code calls, so code a task's own Luke put on screen no longer shows.
  • Luke's Work turns. The Work tab and its read are gone, and so are its live plan.work frames.
  • Browser tabs Luke opened. The cloud tab store lists only tabs whose conversation is a coding agent's, so Luke's rows stop showing.

Deleted

The planner's eve app and service

  • apps/luke/ (28 files).
  • The luke service and the /eve/v1/(.*) rewrite in vercel.json.
  • luke in function-rewrites.ts, vercel-ignore.sh, repository-checks.sh, knip.json, the effect-edges record, and the lockfile.
  • The preview probe now checks /eve/coder/v1/health.

The brain host

  • brain-host/brain.ts, host.ts, model.ts, plan-edit.ts, production.ts, task-title.ts and chat.ts, with their tests.
  • tasks/brain-tool-set.ts and tasks/brain-transcript.ts.
  • testing/eve-scripted-model.ts.
  • The package exports for all of these.

Roles and tools

  • AGENT_ROLE.BRAIN and BRAIN_WORKER.
  • SANDBOX_POLICY.READ_SHALLOW_LAZY.
  • run_in_repository: repository-shell.ts's lazy checkout and RUN_IN_REPOSITORY_TOOL.
  • The brain's tool declarations: READ_AGENT_TOOL, SHOW_PAGE_TOOL, and show_page's inline-html input (it now takes path alone).
  • HOSTED_TOOL_SET and EVE_MOUNT.TASKS.
  • AGENT_TOOL.BROWSER loses BRAIN and BRAIN_WORKER, and the brain's browser wiring from #4318 goes with brain.ts, host.ts and apps/luke/runtime.ts.

The task conversation

  • openTaskConversation, readTaskOfConversation, findTaskOfConversation, attachTaskConversation and setConversation.
  • StoredTask.conversationId and TaskHeader.conversationId.
  • The task reach in recorded-session.ts: conversationOwnerOf and findConversationOwner.
  • The task readTranscript.
  • taskFollowedBy.
  • findTaskWorkspace.

Routes and live reads

  • plans.stop, plans.transcript and plans.work.
  • messages.task and messages.taskWorker.
  • The live taskConversation and taskWorkerTranscript reads.
  • Their contract, client, gateway and host plumbing.
  • The plan.draft frame, PLAN_DRAFT, and the host's draft handling.
  • The plan.work frame and everything behind it: the live brain's work projection and its reads of subagent sessions (tasks/task-work.ts, tasks/child-models.ts, childTitlesByCall, delegationJournalsOf); the voice package's route for the frame; and the host's showWork, view.work, voiceWindowState and windowAppState, which only kept that work from the voice and call windows. Nothing in packages/panel read view.work.
  • The panel's Work model (work-model.ts) and the planning workers in the side panel.

Voice

  • call-backend.ts collapses to the one coder backend; callBackendOf and the explore branch go.
  • A create without agentId is refused at decode, and opening.ts, live-exchange.ts and voice-session-record.ts are agent-only.

Chat and comments

  • A Luke link in a post's words is now a plain reference: it mints no member and tells no one. The Luke arms of ChatMemberMention, ChatMemberSubject, chat-ask, chat-notify and the composer's offer list are removed.
  • PlanCommentWriterRef's LUKE arm is removed. An @Luke in a thread hands it to no one.
  • The panel's Luke picker row and LUKE_AWAY are removed.
  • A shown-code note now names the agent that showed it and links to that agent's turn.

Docs

  • docs/TASKS.md, PRIVACY.md, apps/web/server/README.md, packages/service/README.md, CONTRIBUTING.md and packages/live/AGENTS.md now describe agents only.
  • AGENTS.md only loses its apps/luke lines.
  • PRIVACY.md says a task's delete stamps the conversation its own Luke kept along with its agents'.

Kept, and the test that covers each

KeptWhyCovered by
brain-host/brain-ask.ts (the asks intake)It is how a call's spoken ask reaches an agentbrain-ask.test.ts, voice/live-brain.test.ts, voice/voice-service-exchange.test.ts
brain-host/relay.tsThe coder relay journals every turn through itbrain-host-relay.test.ts, relay-journal-batch.test.ts, relay-pictures.test.ts, coder-host.test.ts
turn-events.ts (projectTurnEvents)It is how the voice follows an agent's spoken turnagents/turn-events.test.ts, voice/live-brain.test.ts
brain-host/seed.ts, context.ts (seed and standing context)A retired agent session reopens with a seed, and turn messages carry contextcoder-host/coder-host.test.ts ("a session opened afresh after the agent's last one retired … is seeded", and the turnMessage tests)
brain-host/door.ts, auth.ts, conversation.tsThe coder door's admission and deployment actorbrain-host-access.test.ts, coder-host/stop.test.ts, coder-host/coder-agent.test.ts, eve-deployment-secret.test.ts
brain-host/eve-sessions.ts (EVE_MOUNT = { CODER })Every eve clienteve-sessions.test.ts, brain-ask.test.ts, accounts/account-delete.test.ts
MEMBER_KIND.LUKE ("planner")Old chat-member rows still decode; nothing mints a new one (LUKE_HANDLE already went with #4333's handle retirement)chat-store.test.ts (a former Luke's channel scope and roster), coding-agents-app.test.ts (a Luke link mints none)
PLAN_COMMENT_ACTOR.LUKE, MENTION_KIND.LUKEOld comments and links still readapps/web/tests/plan-comments.test.ts (a thread mentioning Luke hands it to no one), mention-link.test.ts, chat-mentions.test.ts
CONVERSATION_KIND.TASK = "plan" and task.conversation_id (now @deprecated, read only by a task's delete)Old rows stand until 7btask-store.test.ts ("a plan's delete stamps its agents' conversations and the one its own Luke kept…", "the conversation a plan's own Luke kept, and a worker's delegated from it, are reached by no one…"), apps/web/tests/session-sandbox-cleanup.test.ts
task-record-live's calls part (spoken: 0)Desktops already shipped still decode ittask-record-live.test.ts

For 7b (the data PR, cut from this branch)

Planner chat members. chat_message.author_member_id is NOT NULL with ON DELETE CASCADE to chat_member, and so are dm_a_member_id and dm_b_member_id. Deleting a member deletes its messages and DMs, so this PR keeps the rows and only stops minting them.

Production, read-only:

  • 9 planner members, all with a task_id, in one workspace.
  • 0 messages written by them, and 0 DMs.
  • 11 messages that mention them.
  • 1 plan_comment with actor luke, and 1 with asked_actor luke.

So 7b can delete these rows behind a NOT EXISTS guard on any message or DM they wrote. After that it can drop the planner literal, then chat_member.task_id, then chat_member_task, in that order.

Retention gap: closed here. deleteTask reads task.conversation_id under the row lock it already takes, and stamps that conversation along with the agents'. That keeps deleting a task correct between 7 and 7b. Only the agents' conversations are handed to beforeDelete, which retires eve sessions, because Luke's ran on the luke service this PR deletes; the purge removes the stamped conversation and its session sandboxes as it does any other. 7b's stamping only needs the plan conversations left over: those no task names any more (a conversation a task moved off earlier), and, when it drops the column, those the column still names.

Access to old plan conversations

namedByWork no longer counts task.conversation_id. Left alone, the own-conversation clause of reachableConversation would treat an old plan conversation, and the workers delegated from one, as private to their opener: the opener would keep reach after leaving the workspace, and every other member would lose it. So the own clause now leaves out kind plan and any conversation whose parent is a plan, and nobody reaches them (test in task-store.test.ts). No route reads them any more.

The abandoned-turn sweep's writer

maintenance-app.ts now builds the sweep's writer over CODER_TOOL_SET instead of the deleted HOSTED_TOOL_SET. The sweep only settles a turn's end (settledLost → the writer's turnEnded). That path reads the turn row and journal without validating them against the tool set, and settles any unanswered call with the unknown-outcome envelope. The tool set matters only at construction, where every tool in it must admit that envelope. CODER_TOOL_SET is exactly the set the coder host's own writer runs on (coder-host/production.ts), so a coding agent's turn is settled as its own writer would settle it. A leftover plan turn needs nothing from the set, and the database's sweep tests already run it over tools: {} (abandoned-turns.test.ts). Main used the brain's HOSTED_TOOL_SET for every conversation, coding agents' included.

AGENTS.md rewording, needs approval

AGENTS.md is otherwise pure removals of lines and words that name deleted files. Two lines had to be reworded so the sentence still reads:

  1. The two lines host through, composed with `Effect.runSync` as it loads for the same reason and `apps/luke/host.ts` is. Not become one: host through, composed with `Effect.runSync` as it loads. Not. The reason they pointed at lived in the deleted apps/luke passage.
  2. `packages/service/src/agents/brain-host/production.ts`'s `spend` is becomes the same line with coder-host/production.ts. The coder host's spend is the same AI SDK middleware seam, so "two promise-shaped foreign boundaries" stays true.

Suggested, not made: line 133's comment # offline brain eval, scripted model, by hand names no deleted file, but test:agent now runs only the coding-agent eval. "offline coding-agent eval" would be accurate.

Checks

  • tsc --noEmit passes in every package and app, each run on its own: the turbo typecheck is killed for memory in this sandbox, so it was run package by package.
  • The vitest suites pass in database, features, voice, ui, host, panel, service and web.
  • pnpm lint (biome and oxlint) is clean in the changed files.
  • node scripts/check.mjs --without typecheck passes apart from the desktop's app-menu.test.ts, which needs an Electron binary this sandbox lacks. pnpm discarded-effect passes when run on its own.
  • apps/web/server/function-externals.json drops node:dns/promises from four bundles, which no longer load the brain's tools.

Made with Luke