Skip to content

feat(agents): backend slice — definitions, MCP launchers, runtime models#38

Open
ovdmar wants to merge 14 commits into
mainfrom
agent/12-agents-system-6haz23
Open

feat(agents): backend slice — definitions, MCP launchers, runtime models#38
ovdmar wants to merge 14 commits into
mainfrom
agent/12-agents-system-6haz23

Conversation

@ovdmar

@ovdmar ovdmar commented May 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds the backend foundations for a first-class Agents system in Citadel: predefined and custom agent definitions, default agent runtime config, runtime-specific model discovery, and MCP launchers for predefined and custom agents.
  • Adds the plan-registration schema/table as the durable anchor for a later handoff implementation.
  • Merges current origin/main into the branch and keeps the Agents backend slice compatible with the newer scratchpad, PR, quota, diagnostics, and terminal work on main.

Plan

.agents/plans/agents-system.md

What landed

  • Agent definition contracts and storage for predefined editable agents plus user-defined custom agents.
  • Runtime model adapters for claude-code, codex, cursor-agent, and pi.
  • Daemon HTTP routes for agent CRUD, reset, config, and runtime model lookup.
  • MCP tools: launch_implementation_agent, launch_prototype_agent, launch_pm_agent, launch_architect_agent, list_custom_agents, and launch_custom_agent.
  • plan_registrations persistence via DB migration v13 after merging main's v9-v12 migrations.
  • Merge-resolution extractions that keep shared files under the repo size gate: repo routes, plan-registration store methods/tests, and ttyd theme helpers.

Deferred Scope

  • Web cockpit Agents nav/editor/default-runtime UI is still deferred.
  • register_plan and launch_handoff_agent remain stable tool contracts returning not_yet_implemented_in_v1 until the safe file validation and same-workspace handoff path lands.
  • Launcher schemas accept workspace input, but v1 still launches through the existing workspace-creation path.

Test Plan

  • make check passes locally on 2026-05-28 after merging origin/main at 638c565.
  • Focused suites passed for DB, MCP, daemon agents/routes, app wiring, and terminal helpers during merge resolution.
  • Manual QA per the How-to-QA section below.

How to QA

  1. Pull the branch: git checkout agent/12-agents-system-6haz23
  2. Install: pnpm install
  3. Full check: make check
  4. Start dev services: pnpm dev
    • Daemon: http://localhost:4010 or the worktree's resolved port
    • Web cockpit: http://localhost:5173

Suggested manual checks:

  • curl http://localhost:4010/api/agents returns the four predefined agents and the default runtime config.
  • curl http://localhost:4010/api/runtimes/claude-code/models returns either live model discovery or fallback models with a probeError.
  • DELETE /api/agents/implementation returns 409 predefined_agent_cannot_be_deleted.
  • Create a custom agent through POST /api/agents, then launch it through launch_custom_agent via MCP.

Schema-affecting change. First daemon boot after merge applies migration v13 for plan_registrations; existing tables are preserved.

ovdmar and others added 14 commits May 25, 2026 20:11
Adds the dual-approved tech plan for the Agents system (predefined +
custom agent definitions, MCP launchers, plan handoff) and the spec
updates that anchor the implementation. Defines the new "Agent
definition" core term in spec A, the Agents nav surface in B.2, the
launch-time prompt-composition seam in B.3, runtime model discovery
+ default agent runtime in B.6, and the agent launchers + plan
handoff MCP surface in B.7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds AgentDefinitionSchema (+predefined/custom kind), Create/Update
input schemas, LaunchPredefinedAgentInputSchema (shared by all four
predefined launchers), LaunchCustomAgentInputSchema, RegisterPlanInputSchema,
PlanRegistrationSchema, LaunchHandoffAgentInputSchema (with the
predefinedKind XOR customAgentId one-of refinement that prevents typos
silently 404'ing as custom-agent lookups), AgentsConfigSchema for the
global default-runtime knob, and RuntimeModelDescriptor/Response for
the per-runtime model probes. All schemas export inferred types and
are covered by round-trip parse tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the plan_registrations SQLite table (workspace-scoped, FK to
workspaces with ON DELETE CASCADE) and the schema_migrations row for
version 8. Store gains insertPlanRegistration / findPlanRegistration /
listPlanRegistrationsForWorkspace / deletePlanRegistration methods.
PRAGMA foreign_keys=ON preserved.

A regression test asserts the cascade: inserting a registration for
a workspace, deleting the workspace, then listing registrations
returns empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds packages/runtimes/src/models/, mirroring the usage/ adapter pattern.
runtimeModelListers registry exposes per-runtime probes; claude-code
attempts a TUI scrape via tmux-pty.ts (5s timeout, try/finally
killSession cleanup) and degrades to a hardcoded fallback list with a
probeError on any failure. codex/cursor-agent/pi return small hardcoded
defaults — their CLIs don't expose model selection today.

Parser is fixture-driven (fixtures/claude-code-models.txt) so future
TUI-format drift is caught at test time. Real-fixture verification
against a live claude-code session is a follow-up gate before relying
on the parser in production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds ~/.citadel/agents/ storage backing the four predefined agent
definitions (architect, implementation, pm, prototype) plus user-defined
custom agents. Each definition lives in its own JSON file for atomic
writes and to keep concurrent edits from racing on a shared file.

Highlights:
- Idempotent seeding: list() re-reads disk on every call (no in-memory
  cache that could desync across daemons sharing the directory), and
  the seed only writes when a predefined file is missing OR present-but-
  unparseable; well-formed user edits are preserved.
- Boot-safe: when the directory cannot be created, list() returns []
  and state() reports "unavailable" so the daemon does not crashloop
  under systemd Restart=always.
- Predefined IDs are reserved — custom create rejects collisions; remove
  rejects predefined; resetToDefaults rejects custom and restores the
  citadel-authored seed (NOT the user's defaultRuntime).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds apps/daemon/src/agents-routes.ts with the new HTTP surface for
agent definitions and per-runtime model discovery:
- GET/POST/PATCH/DELETE /api/agents (predefined-delete → 409,
  predefined-reset-by-custom-id → 400, etc.)
- POST /api/agents/:id/reset
- GET/PUT /api/agents/config
- GET /api/runtimes/:id/models with a 1-hour TTL cache and ?refresh=1
  bypass (claude-code CLI upgrades happen out-of-band, so
  daemon-lifetime caching would surface stale models)

app.ts gets a single registrar call so it remains thin. To stay under
the 800-LoC file-size gate, also compresses the /api/repos/:repoId/
branches handler (behavior unchanged) and extracts agent contracts to
packages/contracts/src/agents.ts (and IdSchema to id.ts) since the new
schemas pushed index.ts over its budget.

Route tests drive the live express app via node:http (no new dev deps;
supertest avoided to keep the lockfile clean). The 1h-TTL behavior is
exercised with a fake clock that exercises hit/miss/refresh transitions
in order.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ntinel

Registers the new MCP tool surface for the Agents system:
- launch_implementation_agent / launch_prototype_agent / launch_pm_agent
  / launch_architect_agent — predefined launchers
- list_custom_agents, launch_custom_agent — custom launchers
- register_plan, launch_handoff_agent — plan handoff

All eight tools route through the daemon via a new
`agent_launcher_requires_daemon` sentinel (matching the existing per-
family pattern: scratchpad_tool_requires_daemon,
scheduled_agent_run_tool_requires_daemon, session_tool_requires_daemon).
McpToolContext has no fs access and no agentDefinitions handle, so
running list_custom_agents in the snapshot path would silently return
[] and mislead remote callers — daemon-routed is the correct shape.

Definitions live in agent-launcher-tools.ts to keep packages/mcp/src/
index.ts under the 800-line file-size budget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the daemon-side dispatch for the four predefined launchers
(launch_implementation_agent / launch_prototype_agent / launch_pm_agent
/ launch_architect_agent), launch_custom_agent, and list_custom_agents.
Composition is centralized in a single composeAgentLaunchInput seam in
apps/daemon/src/agent-launcher.ts so the system-prompt-prepend path is
testable in isolation — the canary against silently dropping the system
prompt on a future refactor.

System prompt is prepended uniformly across all runtimes under the
`## System` / `## User prompt` headers, then handed to
operations.launchAgent the same way the existing launch_agent path does.
Runtime resolution priority: agent.runtime → agents.config.defaultRuntime
→ "claude-code".

register_plan and launch_handoff_agent dispatch returns
`not_yet_implemented_in_v1` — the realpath/fs validation + plan
resolution layer is the next slice. The MCP tool surface is registered
and the contract is stable, so v2 fills in the dispatch without changing
schemas or caller code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Biome's autoformatter expanded the agent-system registrar call into
multi-line form, pushing apps/daemon/src/app.ts back over the size
gate. Extract the wiring (createAgentDefinitionsStorage + register the
routes) into a `wireAgents(app, asyncRoute, config)` helper in
agents-routes.ts so app.ts has only a single-line call.

Also runs biome --write across the new files to normalize import
ordering, type-only import annotations, and trailing-comma formatting.
No behavior changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-applied after merging origin/main into the agents-system branch.
Single-line collapse on apps/daemon/src/status-monitor-wiring.ts; no
behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Main extracted IdSchema to packages/contracts/src/primitives.ts in
parallel; this branch had extracted it to id.ts. Resolved by:
- Removing this branch's id.ts (main's primitives.ts wins the race)
- Repointing agents.ts to import IdSchema from ./primitives.js
- Keeping this branch's `export * from "./agents.js"` in index.ts

All agents-system tests still pass (59 across contracts, DB cascade,
runtime models, storage CRUD, HTTP routes, MCP layer, launcher
composition).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Another in-flight branch landed (8, 'agent-sessions-auto-resume-backoff')
on main while this PR was open, so plan_registrations now becomes v9.
INSERT OR IGNORE quietly dropped this PR's row on top of the existing
v8 — packages/db/src/migration.test.ts caught the silent-loss case.

Updates:
- migrate.ts: version 8 → 9 for the plan-registrations row
- index.test.ts: schema_migrations ORDER BY version expectation gets
  { version: 9 } appended

This is the migration-version-drift mitigation the tech plan flagged
under "Pre-merge sequencing".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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