feat: in-app Orchestrator (Phase 2) — multi-agent conductor

Brings the deterministic Han-flow conductor into BooCode: launch any read-only
flow from BooChat or BooCoder, watch each agent stream live in a Paseo-style
run pane, get an evidence-disciplined report — on local Qwen, persisted and
resumable. Read-only enforced hard via qwen --approval-mode plan (orchestrator
tasks fail closed if qwen is unavailable; never fall to write-capable native).

Backend (apps/coder): re-homed conductor defs, flow_runs/flow_steps schema,
flow-runner + dispatcher onTaskTerminal hook, restart-resume, runs routes
(launch/list/get/cancel), user-channel WS. Contracts: two flow_run_* frames.
Web: orchestrator pane kind + OrchestratorPane, Workflow button + slash flows
(BooChat/BooCoder parity), FlowLauncherDialog, "New Orchestrator" in the + and
split menus, runs history + export. Plan: openspec/changes/orchestrator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 14:59:07 +00:00
parent 519b1d2ca1
commit 1937af8df9
118 changed files with 15723 additions and 27 deletions

View File

@@ -288,3 +288,72 @@ CREATE TRIGGER tasks_notify_new
AFTER INSERT ON tasks
FOR EACH ROW
EXECUTE FUNCTION notify_tasks_new();
-- v2.8.0: orchestrator flow_runs + flow_steps — DB-backed scheduler for multi-agent flows.
-- project_id carries no FK (matches tasks.project_id convention, schema.sql:19).
CREATE TABLE IF NOT EXISTS flow_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL,
flow_name TEXT NOT NULL,
band TEXT NOT NULL,
model TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'running',
input JSONB NOT NULL,
report TEXT,
error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
CONSTRAINT flow_runs_band_chk CHECK (band IN ('small', 'medium', 'large')),
CONSTRAINT flow_runs_status_chk CHECK (status IN ('running', 'completed', 'failed', 'cancelled')),
CONSTRAINT flow_runs_input_chk CHECK (input ? 'question')
);
CREATE TABLE IF NOT EXISTS flow_steps (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
run_id UUID NOT NULL REFERENCES flow_runs(id) ON DELETE CASCADE,
step_id TEXT NOT NULL,
kind TEXT NOT NULL,
agent TEXT,
status TEXT NOT NULL DEFAULT 'pending',
task_id UUID REFERENCES tasks(id) ON DELETE SET NULL,
chat_id UUID REFERENCES chats(id) ON DELETE SET NULL,
input TEXT,
output TEXT,
error TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(),
CONSTRAINT flow_steps_kind_chk CHECK (kind IN ('agent', 'code')),
CONSTRAINT flow_steps_status_chk CHECK (status IN ('pending', 'running', 'completed', 'failed', 'skipped', 'cancelled')),
UNIQUE (run_id, step_id)
);
-- Runs history query (GET /api/runs?project_id=).
CREATE INDEX IF NOT EXISTS flow_runs_project_created_idx ON flow_runs(project_id, created_at DESC);
-- Ready-wave derivation + resume scans (flow_steps WHERE run_id = ? AND status = ?).
CREATE INDEX IF NOT EXISTS flow_steps_run_status_idx ON flow_steps(run_id, status);
-- onTaskTerminal callback: look up the step owning a completed task.
CREATE INDEX IF NOT EXISTS flow_steps_task_id_idx ON flow_steps(task_id);
-- v2.8.0 (Phase 4): widen the status CHECKs to include 'cancelled' so the run
-- pane's stop button has a terminal state to record (the plan Notes flagged this
-- gap). Phase 2 applied these tables with the narrower set, so the inline CHECK
-- edits above are no-ops on the existing DB (CREATE TABLE IF NOT EXISTS skips an
-- existing table) — widen via the repo's DROP-IF-EXISTS → guarded-ADD discipline.
-- Pure ADD of a new allowed value, so no row UPDATE is needed (no value renamed).
ALTER TABLE flow_runs DROP CONSTRAINT IF EXISTS flow_runs_status_chk;
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'flow_runs_status_chk') THEN
ALTER TABLE flow_runs ADD CONSTRAINT flow_runs_status_chk
CHECK (status IN ('running', 'completed', 'failed', 'cancelled'));
END IF;
END $$;
ALTER TABLE flow_steps DROP CONSTRAINT IF EXISTS flow_steps_status_chk;
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'flow_steps_status_chk') THEN
ALTER TABLE flow_steps ADD CONSTRAINT flow_steps_status_chk
CHECK (status IN ('pending', 'running', 'completed', 'failed', 'skipped', 'cancelled'));
END IF;
END $$;