Skip to content

feat: add experimental orchestrator chat - #29383

Draft
kylecarbs wants to merge 3 commits into
mainfrom
feat/chat-orchestrator
Draft

kylecarbs wants to merge 3 commits into
mainfrom
feat/chat-orchestrator

Conversation

@kylecarbs

@kylecarbs kylecarbs commented Sep 15, 2026

Copy link
Copy Markdown
Member

Adds the chat-orchestrator experiment: a single persistent chat per user with no workspace tools that can use external MCP tools, spawn independent chats, and list or read the user's existing chats. When the experiment is on, the /agents sidebar shows a pinned "Orchestrator" entry above the chat list.

How it works

  • Identity: new chat_mode enum value orchestrator. The chat ID is deterministic per owner (codersdk.OrchestratorChatID), so a concurrent create collides on the primary key instead of producing two orchestrators.
  • Addressing: no dedicated endpoints. A middleware on /chats/{chat} rewrites the literal orchestrator segment to the caller's orchestrator chat ID before ExtractChatParam, so every existing chat route (GET, PATCH, messages, stream, ...) works at /api/v2/chats/orchestrator/... and 404s until the chat exists. Creation is POST /api/v2/chats with orchestrator: true (409 if it already exists).
  • Lazy creation: the state machine requires every chat to start with a user message, so /agents/orchestrator renders a create form until the first message, then redirects to the chat.
  • Tools: orchestrator turns skip all workspace built-ins (read_file, execute, create_workspace, ...), workspace MCP tools, and the subagent tools. They receive external MCP tools plus spawn_chat (creates an independent root chat owned by the user, which shows up in the sidebar and can create its own workspace), list_chats (owned root chats with status, summaries, workspace, paging, search), and read_chat (chat details plus latest assistant response; owner-only).
  • Guards: orchestrator chats are excluded from GET /api/v2/chats, and PATCH / message requests reject workspace_id and plan_mode for them. Chat.mode is now exposed in the SDK so the UI hides the workspace picker and plan toggle.
  • Prompt: orchestrator chats use a dedicated system prompt (plus any admin custom prompt) instead of the workspace-centric default.

Testing

  • TestOrchestratorChat (coderd): lifecycle, singleton, list exclusion, guards, tool set, and spawn_chat / list_chats end to end against the fake model.
  • TestOrchestratorToolsOwnershipBoundary (chatd): read_chat / list_chats never expose another user's chats.
  • dbauthz method coverage, migrations, and existing chatd / chat handler tests pass. Frontend: ChatsSidebar experiment gating tests, chats.test.ts mutation coverage, pnpm check, pnpm lint.

Left TODO(chat-orchestrator) comments in coderd/x/chatd/ARCHITECTURE.md for the human author to document the new endpoint and tool set.

Implementation plan

Orchestrator chat experiment

Goal

Add an experimental "Orchestrator" chat: a single persistent chat per user
that has no workspace tools but does have external MCP tools, can spawn new
chats, and can list and read the user's existing chats. It is pinned at the
top of the /agents sidebar as "Orchestrator" behind the
chat-orchestrator experiment.

Decisions

  • Identity: new chat_mode enum value orchestrator. Modes already
    drive tool assembly (explore, computer_use), so this reuses the
    existing per-mode switching in prepareGeneration and
    buildSystemPrompt. "One per user" is enforced with a deterministic
    per-owner chat ID (chatd.OrchestratorChatID) so concurrent creates
    collide on the primary key. A partial unique index was rejected: the new
    enum label cannot be referenced in the migration transaction that adds
    it, and an enum-to-text cast is not immutable.
  • Creation: the state machine requires every chat to be created with an
    initial user message, so the orchestrator is created lazily on the user's
    first message via POST /api/v2/chats with orchestrator: true.
  • Addressing: the literal orchestrator path segment is rewritten to the
    caller's deterministic orchestrator chat ID before ExtractChatParam, so
    every existing /chats/{chat}/... route works for it with no dedicated
    handlers. GET /api/v2/chats/orchestrator 404s until it is created.
  • Listing: orchestrator chats are excluded from GetChats so they do
    not appear as regular sidebar rows; the sidebar renders a fixed entry.
  • Tools: orchestrator turns get no built-in workspace tools
    (read_file, execute, create_workspace, ...), no workspace MCP
    tools, no subagent (spawn_agent) tools. They get external MCP tools,
    personal skills, the advisor (if that experiment is on), and three
    orchestrator tools:
    • spawn_chat: creates an independent root chat owned by the user
      (appears in the sidebar like any other chat and can create its own
      workspace).
    • list_chats: lists the user's root chats with status, title,
      summary, last turn summary, workspace, timestamps; supports search
      and paging.
    • read_chat: returns a chat's details plus its latest assistant
      response.
  • Guards: workspace attach and plan mode are rejected for orchestrator
    chats (PATCH, messages), and Chat.mode is exposed in the SDK so the UI
    can hide the workspace picker and plan toggle.
  • Prompt: orchestrator chats use a dedicated system prompt instead of
    the deployment default (which is workspace-centric).

Steps

  1. codersdk: ExperimentChatOrchestrator, ChatMode type + Chat.Mode,
    CreateChatRequest.Orchestrator, OrchestratorChatAlias,
    OrchestratorChatID.
  2. DB: migration, GetOrchestratorChatByOwnerID query, GetChats
    exclusion, dbauthz wrapper + test, make gen.
  3. chatd: isOrchestratorMode, orchestrator tools file, tool assembly and
    prompt branches, CreateChat prompt selection, guard in
    applyRequestedMCPServerIDs untouched.
  4. coderd: orchestrator flag in postChats, alias middleware on
    /chats/{chat}, guards in patchChat / postChatMessages, make gen.
  5. Frontend: chatMode in generated types, API client + queries,
    /agents/orchestrator route + page, sidebar entry, hide workspace/plan
    controls for orchestrator chats, tests.
  6. Tests: chatd internal tests for tool assembly, coderd handler tests,
    sidebar test.

Generated by Coder Agents on behalf of @kylecarbs.

Add the chat-orchestrator experiment: a single persistent per-user chat
with no workspace tools that can use external MCP tools, spawn
independent chats, and list or read the user's existing chats.

- New chat_mode value 'orchestrator' and GET/POST /api/v2/chats/orchestrator
  with a deterministic per-owner chat ID so creation is idempotent.
- Orchestrator turns skip workspace built-ins, workspace MCP tools, and
  subagent tools; they receive spawn_chat, list_chats, and read_chat.
- Orchestrator chats are excluded from the chat list and reject workspace
  and plan mode updates. Chat.mode is exposed in the SDK.
- Sidebar shows a pinned "Orchestrator" entry and /agents/orchestrator
  lazily creates the chat on the first message.
@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

Replace the dedicated GET/POST /chats/orchestrator handlers with two
smaller pieces: an `orchestrator` flag on CreateChatRequest and a
middleware that rewrites the literal "orchestrator" path segment to the
caller's deterministic orchestrator chat ID before ExtractChatParam.
Every existing /chats/{chat} route now works for the orchestrator.

Also regenerates docs after dropping a struct comment.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant