Level-up analyzes an agent's brain + recent run outcomes (or a
whole team's aggregate state), calls Gemini 2.5 Flash for structured
JSON proposals, and persists them as pending level_up_proposals
rows. Reviewer approves a subset via /apply; the applier commits
only those items.
Migration 0052 adds level_up_proposals (id, workspace_id, agent_id
XOR team_id via CHECK constraint, status, payload JSONB,
applied_items[], model, created_by, approved_by, created_at,
applied_at) + workspace/pending/agent/team indexes.
Rust surface:
- cm_db::repo::level_up::{insert, get, list_pending, mark_applied,
mark_rejected}
- cm_api::level_up::{propose_agent, propose_team, apply}
Item kinds handled by apply():
identity_refinement → UPDATE agents.system_prompt
skill_add → agent_skills_ext INSERT
skill_candidate → workspace-scoped skills INSERT
(deterministic id per (workspace, name))
brain_consolidation → set_agent_md on the brain (unlike
brain_seed::ingest, this overwrites)
roster_change / mcp_bundle_change — logged as
"not auto-applied, human runs
team-wizard" (structural changes need
human review of side effects).
API:
- POST /api/claws/{id}/level-up → { proposal_id }
- POST /api/teams/{id}/level-up → { proposal_id }
- GET /api/level-up-proposals → pending list
- GET /api/level-up-proposals/{id}
- POST /api/level-up-proposals/{id}/apply { approved_item_ids }
- POST /api/level-up-proposals/{id}/reject
Uses Gemini 2.5 Flash with response_mime_type: "application/json"
so the model returns structured JSON directly (no ```json fence
stripping needed). Configurable via CLAWMATES_LEVEL_UP_MODEL.
Follow-ups:
- Frontend diff-review UI (pick items, approve/reject)
- roster_change / mcp_bundle_change appliers (currently manual)
- Anthropic + OpenAI proposer variants
- Promote workspace-scoped skills to builtin via a curator flow
Co-Authored-By: Claude Opus 4.7 <[email protected]>
75 lines
3.1 KiB
SQL
75 lines
3.1 KiB
SQL
-- Slice 8.5 — level-up proposals.
|
|
--
|
|
-- A proposal is an LLM-generated diff of "current state → suggested
|
|
-- state" for either a specific agent or a whole team. Human approves
|
|
-- (all or per-item) via a diff-review UI; the applier commits the
|
|
-- approved subset.
|
|
--
|
|
-- Proposal payload (JSONB) shape — kept schemaless because proposal
|
|
-- kinds evolve. Documented shapes:
|
|
--
|
|
-- agent proposal:
|
|
-- {
|
|
-- "kind": "agent",
|
|
-- "current": { "system_prompt": "…", "skills": ["…"] },
|
|
-- "suggested_items": [
|
|
-- { "id": "u1", "kind": "brain_consolidation",
|
|
-- "rationale": "…", "brain_md_diff": "…" },
|
|
-- { "id": "u2", "kind": "identity_refinement",
|
|
--- "rationale": "…", "new_system_prompt": "…" },
|
|
-- { "id": "u3", "kind": "skill_add", "skill_id": "…",
|
|
-- "rationale": "…" },
|
|
-- { "id": "u4", "kind": "skill_candidate",
|
|
-- "rationale": "…", "draft": { "name": "…",
|
|
-- "description": "…",
|
|
-- "when_to_use": "…",
|
|
-- "body": "…",
|
|
-- "tags": ["…"] } }
|
|
-- ]
|
|
-- }
|
|
--
|
|
-- team proposal — same shape plus:
|
|
-- { "id": "u5", "kind": "roster_change",
|
|
-- "op": "add"|"drop"|"rename", "slot": "…", "rationale": "…" }
|
|
-- { "id": "u6", "kind": "mcp_bundle_change",
|
|
-- "op": "add"|"drop", "bundle": "…", "rationale": "…" }
|
|
--
|
|
-- `applied_items` is the array of `id`s that the reviewer approved;
|
|
-- the applier only touches those.
|
|
|
|
CREATE TABLE level_up_proposals (
|
|
id UUID PRIMARY KEY,
|
|
workspace_id UUID NOT NULL REFERENCES workspaces(id) ON DELETE CASCADE,
|
|
-- Exactly one of these is set; enforced by the CHECK below.
|
|
agent_id UUID REFERENCES agents(id) ON DELETE CASCADE,
|
|
team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
|
|
-- 'pending' | 'applied' | 'rejected' | 'partial' (applied a subset)
|
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
-- Full proposal JSONB (see header comment for shape).
|
|
payload JSONB NOT NULL,
|
|
-- Ids from payload.suggested_items[] that reviewer approved.
|
|
applied_items TEXT[] NOT NULL DEFAULT '{}',
|
|
-- Model used for the LLM analysis pass (audit trail).
|
|
model TEXT,
|
|
created_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
approved_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
applied_at TIMESTAMPTZ,
|
|
CONSTRAINT level_up_target_one CHECK (
|
|
(agent_id IS NOT NULL AND team_id IS NULL) OR
|
|
(agent_id IS NULL AND team_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
CREATE INDEX level_up_workspace_idx
|
|
ON level_up_proposals (workspace_id, created_at DESC);
|
|
CREATE INDEX level_up_pending_idx
|
|
ON level_up_proposals (status, created_at DESC)
|
|
WHERE status = 'pending';
|
|
CREATE INDEX level_up_agent_idx
|
|
ON level_up_proposals (agent_id, created_at DESC)
|
|
WHERE agent_id IS NOT NULL;
|
|
CREATE INDEX level_up_team_idx
|
|
ON level_up_proposals (team_id, created_at DESC)
|
|
WHERE team_id IS NOT NULL;
|