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:
@@ -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 $$;
|
||||
|
||||
Reference in New Issue
Block a user