seedproject-web/agents/console/FRAMEWORK.md
Carlos Arias 6fec3498f2 framework(core): clone tooling + docs (Phase 4)
- FRAMEWORK.md: the host + pluggable-agent contract, the roster, and the
  clone+assign flow
- configure.mjs: ensure a default agents roster on a fresh clone
- .memory/{qa-agent,webdesigner}.md: framework notes pointing at the module layout
Verified: a scratch clone with a different site.config.json yields a different
identity + roster from identical core code.
2026-07-24 08:05:38 -04:00

4.4 KiB

AstroAgent — the standalone agent framework

A generic host that runs pluggable agents, cloneable into any SeedProject site, where each project enables the agents it needs via config. The host knows nothing site-specific; identity comes from config, brand from the per-site brand skill.

Module layout (agents/console/)

File Role
host.mjs The generic runtime — site-agnostic. Shared primitives + route registry + scheduler + core routes (/ping, /auth, /logout).
site.mjs The only place identity is read. Derives name / url / host / model / agentUser / home / role / qaEmail / qaRoutes / agents from site.config.json + astroagent.config.json.
server.mjs Bootstrap: load host → register the enabled agents (with their settings) → host.listen().
agents/<name>.mjs One agent each — in-page-console, project-builder, web-designer, qa. Site-agnostic; identity via site.*, brand via brand/BRAND.md + the brand skill.

Agent module contract

Each agents/<name>.mjs exports a manifest and a register:

export const manifest = {
  name: "qa", title: "QA Agent", class: "runtime",
  description: "…",
  triggers: [ { type:"endpoint", method:"POST", path:"/qa/run" },
              { type:"schedule", settingKey:"heartbeatMin" } ],
  tools: "Read Skill", skills: ["qa"],
  tables: ["cja_qa_runs","cja_qa_findings"],   // migrations this agent needs
  cli: ["qa-start","qa-finish","qa-routes","qa-autofix"], // api/cli/*.php bridges it needs
};

export function register(host, settings = {}) {
  host.route("POST", "/qa/run", (req, res) => { /* uses host.* */ });
  if ((settings.heartbeatMin ?? 60) > 0) host.everyMinutes(settings.heartbeatMin, fn);
}

register receives the host + this agent's per-project settings. It wires routes/triggers and uses only host.* primitives — never globals — so an agent is a self-contained unit.

Host API (host.*)

  • route(method, path, handler) — register an HTTP route (method:"ANY" matches any verb).
  • runClaudeJson({prompt, tools, model}){ok, result} — one-shot Claude.
  • runClaudeStream({prompt, tools, model, resume, onMessage, …}) — streaming Claude (SSE).
  • build(extraEnv), git(args), phpCli(args) — build / git / the DB bridge.
  • everyMinutes(min, fn, {bootDelayMs}) — scheduled trigger (heartbeat).
  • newJob/emit/jobs — SSE job model. readBody/json — HTTP helpers.
  • state.busy — the shared single-flight lock (mutable across agent modules).
  • gitScope, REPO, APP, PREVIEW_DIR, DEFAULT_MODEL, AGENT_TOOLS — framework constants.
  • Extension points agents attach, e.g. host.drainTasks (web-designer) which QA autofix calls.

The roster — assign agents to a project

astroagent.config.json:

"agents": {
  "enabled": ["in-page-console", "project-builder", "web-designer", "qa"],
  "settings": { "qa": { "heartbeatMin": 60, "autofix": true } }
}

server.mjs loads only enabled agents. Disable one → its routes 404 and its triggers stop. A restaurant site might enable ["web-designer","qa","menu-updater"]; a law firm ["web-designer","qa","seo","intake-triage"] — same framework, different roster.

Clone + assign

  1. Clone the base → scripts/new-site.sh (or edit site.config.json + node scripts/configure.mjs) — stamps identity (name, url, agentUser, timezone…). site.mjs picks these up automatically.
  2. Set astroagent.config.json → agents.enabled to the roster this project needs; tune settings.
  3. Provision each enabled agent's dependencies — its tables (migrations), its cli (api/cli/*.php), its skills (.claude/skills/* or MCP). The manifest lists them; node agents/catalog.mjsagents/agents.json, diff it against a clone to find gaps.
  4. Start the console service → the host loads exactly the enabled agents.

Add a new agent

Drop agents/console/agents/<name>.mjs exporting manifest + register, add its name to agents.enabled, provide its migrations/CLI/skills, run node agents/catalog.mjs, restart.

What stays site-specific (never in the core)

site.config.json + astroagent.config.json (identity + roster), the brand skill + brand/BRAND.md (visual rules), the cja_* content, and the built site. The registry (agents/agents.json, generated) unifies pipeline (agents/scripts/*) and runtime (console) agents so one manifest diff covers a whole clone.