Skip to content

Wire declarative capabilities (surfaces/runtime/workflow/tools) through create + update — the missing authoring path (#51) #141

Description

@serge-ivo

Context

Research (2026-08-03) confirmed the Coder agent is already fully capability-driven at runtime — every consumer (coding routes, CodingSessionWorkflow, browser-runner, console Coding tab, agent-do tools, MCP) branches on agentCapabilities() (surfaces/runtime/workflow/tools), not the "coder" slug. The lone slug === "coder" check in lib/agent-capabilities.ts:300 is a legacy fallback, never consulted once config.capabilities is present. A D1 row with the right config.capabilities behaves identically to Coder with zero platform code changes — already proven by repo-chat (migrations 0032/0050) and Coder's own GitHub tools (0054), all defined as pure data.

The connector/tool layer is likewise sufficient: the #84 epic is complete (unified registry, 7 connectors, consent gate, auto-exposed to runtime+API+MCP), and the coding delegation tools (list_coding_repos/read_terminal/send_to_cli) are auto-injected on the coding surface.

The one missing piece is the authoring path. Capabilities can only be set today via SQL migrations:

  • POST /agents accepts name/slug/description/category/model/personality/goal — not capabilities.
  • PUT /agents/:id allows 8 columns — not surfaces/runtime/workflow/tools.
  • PUT /agents/:id/capabilities accepts only customSurfaces.
  • The formal AgentDefinition/sanitizeAgentDefinition validator was built then removed as unwired dead code (commit f471ee1, PR feat(agents): formal declarative agent-definition schema (#51) #62) — "wiring deferred to the next PR that never came."

Scope (this issue)

Wire the declarative power fields through the existing owner-only capability routes + create:

  1. sanitizeDeclaredCapabilities(input) in lib/agent-capabilities.ts — validate surfaces[] (∈ KNOWN_SURFACES), runtime (∈ {browser,coding,null}), workflow (∈ {JOB_APPLY,CODING_SESSION,INSURANCE_QUOTES,null}), tools[] (via existing sanitizeToolList). Re-introduce KNOWN_RUNTIMES/KNOWN_WORKFLOWS as validation sets. Drop unknowns silently (same philosophy as the read-side filter). Composes existing sanitizers — do not re-validate customSurfaces/settingsSchema (their dedicated routes own those).
  2. Extend PUT /agents/:id/capabilities to also accept + merge surfaces/runtime/workflow/tools (keep customSurfaces handling as-is), and GET /agents/:id/capabilities to return them.
  3. Extend POST /agents with an optional sanitized capabilities object at creation.
  4. Console: a capabilities editor in AgentDetail.tsx (surfaces multiselect, runtime select, workflow select, tools multiselect from TOOL_CATALOG), reusing the same GET/PUT pattern as the existing customSurfaces/settingsSchema editors.

Critical assessment / risks

  • Closed-enum validation is the safety boundary. surfaces/runtime/workflow are closed unions — a creator can only pick capabilities the platform already implements. No arbitrary code, no new blast radius. This is why wiring them is safe now while customSurfaces (which loads a code bundle into the console origin) stays a separate, more-guarded path.
  • Replicating Coder needs NO new vocabularycoding/coding/CODING_SESSION are known values. A genuinely new agent type (novel surface + its own workflow) still needs a monorepo PR to extend the enums + add the workflow binding — tracked by [3P] Tier-1: declarative agent schema + open tool catalog + shared runtime #51, explicitly out of scope here.
  • Trust gating is deferred, not solved. Declaring runtime:"coding"/workflow:"CODING_SESSION" lets an agent's Pilot drive a CLI/browser on a subscriber's machine (consent-based: the subscriber runs pags up, Pro-gated — same risk profile as Coder itself). Acceptable only because agent creation is operator-only today (epic Epic: first-party exemplary agents → mature the platform → open to third parties (demand-gated) #58 is demand-gated/blocked). Before third-party creators may self-declare runtime/workflow, the trust model ([Epic] Open browser-agent creation to creators — BLOCKED on a trust model #74/[browser][trust] Trust & permission model for browser agents (design prerequisite) #75) must land — see the companion issue.
  • Coherence is intentionally not over-constrained. We validate each field against its enum independently and allow partial combos (repo-chat is a valid surface:repo, runtime:null, workflow:null). We do NOT force surfaces:["coding"] ⇒ runtime:"coding". lintAgentClaims already warns on capability/description mismatch at create.
  • No AgentDefinition mega-schema resurrection. feat(agents): formal declarative agent-definition schema (#51) #62 died because it was a validator with no consumer. This issue wires the existing per-field sanitizers into the existing routes — the minimum that makes capabilities authorable — rather than a speculative full-definition struct.

Acceptance criteria

  • sanitizeDeclaredCapabilities unit-tested (valid pass-through, unknown surface/runtime/workflow dropped, tools capped/deduped, junk → undefined fields).
  • POST /agents with capabilities persists a valid config.capabilities; GET/PUT /agents/:id/capabilities round-trip surfaces/runtime/workflow/tools + preserve customSurfaces/settingsSchema.
  • Creating an agent with {surfaces:["coding"],runtime:"coding",workflow:"CODING_SESSION"} via API alone yields a working Coder-equivalent (coding tab, session, tools) with no code change.
  • Console capabilities editor reads/writes the fields.
  • Owner/admin-gated; unknown enum values rejected.

Refs: #51, #58, #62 (removed dead code), #84 (connectors, done).

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend / Worker / API workenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions