API reference
See Gateway preview versus local port-forward fallback and sandbox policy for the shared visual model.
See Direct or confirmed launch, DAG dispatch, collective assembly and recovery for the shared visual model.
See Durable event ordering, SSE replay and terminal draining for the shared visual model.
The Agentweaver backend is the single source of truth for run lifecycle, streaming, review, and merge. Every client is a thin layer over these endpoints.
- Base path:
/api - Authentication: bearer API key on authenticated API requests
- Event ordering: use
sequence, nottimestamp
Authentication
Send the API key on API requests unless the endpoint is explicitly public (/, /health, /auth/entra/*, or /api/server/info):
Authorization: Bearer <api-key>Keys map to the user accountable for the runs they submit. You can configure multiple keys through Auth:Keys, or one key through Auth:ApiKey and Auth:User.
{
"Auth": {
"Keys": [
{ "Token": "dev-local-key", "User": "local-developer" }
]
}
}A request without recognized credentials returns 401 Unauthorized. A request for a run the caller cannot access returns 403 Forbidden (or 404 Not Found on existence-hiding artifact routes). When no credentials are configured, authenticated API requests are unauthorized.
A run with a persisted project_id inherits authorization from that project. Viewer can use read, stream, history, graph, metrics, workspace, file, and preview-list endpoints; Contributor and Owner can also use run mutations such as review, approval, steering, retry, archive, cancellation, and sandbox preview control. The server resolves the project from the stored run record, never from caller input. GitHub App capabilities do not grant project access. Runs with no project_id retain submitting-user ownership. The trusted internal service identity is denied on ordinary run read and mutation routes; only explicitly opted-in, run-bound callbacks such as agent-initiated preview creation accept it.
Endpoints
Runs
Project-scoped run endpoints use the authorization boundary of the run's persisted project: viewers may inspect a run and contributors or owners may operate it. Older runs without a project remain submitting-user scoped. There is no username-based administrative override.
| Method | Path | Purpose |
|---|---|---|
GET | / | Health banner (Agentweaver API) |
GET | /api/runs/{id} | Get current run state |
POST | /api/runs/{id}/archive | Archive a run |
DELETE | /api/runs/{id} | Cancel (if active) and delete a run record |
POST | /api/runs/{id}/cancel | Cancel a run's live work but keep the record |
GET | /api/runs/{id}/stream | Stream ordered run events over SSE |
GET | /api/runs/{id}/events | Return persisted run events |
POST | /api/runs/{id}/review | Record an approve or decline decision |
POST | /api/runs/{id}/shell-approvals | Approve a pending destructive shell command |
POST | /api/runs/{id}/shell-denials | Deny a pending destructive shell command |
GET | /api/runs/{id}/history | Replay persisted session events for terminal runs |
GET | /api/runs/{id}/graph | Get the workflow graph descriptor for rendering the run topology |
POST | /api/runs/{id}/commit | Commit worktree changes and merge into originating branch |
POST | /api/runs/{id}/request-changes | Request a revision cycle: agent rewrites in place |
POST | /api/runs/{id}/retry | Retry a failed run as a new linked run |
GET | /api/runs/{id}/workspace | List workspace files with change status and line counts |
GET | /api/runs/{id}/files | List changed files (flat, with filter) |
GET | /api/runs/{id}/files/{**path} | Get diff or content for a specific file |
POST | /api/runs/{id}/tool-approvals | Approve a pending tool call |
POST | /api/runs/{id}/tool-denials | Deny a pending tool call |
POST | /api/runs/{id}/questions/{requestId}/answer | Answer a pending ask_question request |
POST | /api/runs/{id}/auto-approve | Toggle the per-run auto-approve-tools option |
POST | /api/runs/{id}/autopilot | Toggle the coordinator Autopilot option |
POST | /api/runs/{runId}/sandbox/port-forward | Start a sandbox pod port-forward |
GET | /api/runs/{runId}/sandbox/port-forward | List sandbox port-forwards for a run |
DELETE | /api/runs/{runId}/sandbox/port-forward/{sessionId} | Stop a sandbox port-forward |
Assistant
Assistant endpoints back the Sessions feature (see The Assistant and Sessions — Getting Started): a chat-driven, top-level (not project-scoped) conversation that calls MCP tools on the caller's behalf. A session is stored as a run record (agent_name: "Operator") so it's deleted via the generic DELETE /api/runs/{id} above — there is no separate Assistant delete endpoint.
| Method | Path | Purpose |
|---|---|---|
POST | /api/assistant/runs | Start a new session, optionally running an opening turn |
GET | /api/assistant/runs | List the caller's own sessions, newest first |
POST | /api/assistant/runs/{id}/messages | Send the next message into an existing session |
Projects
| Method | Path | Purpose |
|---|---|---|
POST | /api/projects | Create a project (blank or from GitHub) |
GET | /api/server/info | Get server metadata, including the optional Repo App install URL when configured |
GET | /api/projects | List all projects |
GET | /api/projects/{id} | Get a project by id |
PATCH | /api/projects/{id} | Rename a project |
PUT | /api/projects/{id}/provider-settings | Update provider and model defaults |
DELETE | /api/projects/{id} | Delete a project (record only; cancels active runs) |
GET | /api/projects/{id}/runs | List runs for a project |
POST | /api/projects/{id}/runs | Deprecated direct run submission; returns 410 Gone |
POST | /api/projects/{id}/orchestrations | Start a coordinator orchestration |
AI execution context
Before an AI-guarded action, call POST /api/ai/execution-context with the project or run scope and an operation from the OpenAPI request schema enum. The returned execution_key is bound to that action and must be sent in If-Model-Provider-Key to the matching guarded endpoint. For POST /api/projects/{id}/orchestrations, use the orchestration operation.
The API rejects operation labels outside the published enum. Clients must not substitute another operation if the endpoint's documented operation is rejected; treat that as an API contract error and stop the affected flow safely.
Run summary objects returned by GET /api/projects/{id}/runs include a result field ("no_changes" or null). When result is "no_changes", the agent found no file changes to commit; the review and merge gates are skipped. Each summary also includes coordinator_status: for a coordinator run (agent_name: "Coordinator", no parent) this is the current work-plan orchestration status (dispatching, awaiting_assembly, assembling, in_review, complete, assembly_blocked, assembly_failed, assembly_declined); it is null for normal runs. A companion coordinator_status_reason (the coordinator run's result, scoped to coordinator rows) carries the human-readable terminal/failure detail so the UI can render "Failed: <reason>". Children are excluded from this list. The UI should render coordinator_status (plus coordinator_status_reason) for coordinator rows so a long-running assembly does not show as a bare in_progress and a terminal failure does not show as an unexplained failed. The standalone project-scoped workflow-run detail endpoint (GET /api/projects/{id}/runs/{workflowRunId}) has been removed with the retired standalone run pages. Use project-role-authorized /api/runs/{id} and child run endpoints for embedded coordinator panels.
Run control state is durable. Shell approvals/denials, tool approval requests, run-scoped/always allow policies, child-to-parent approval inheritance, ask_question answers, auto-approve, and autopilot are replayed from persisted run events. That means approval, answer, and toggle requests may land on any API replica and still be observed by the worker that owns the run.
GitHub repository selection codes
These endpoints are the pre-project handoff for GitHub-backed project creation. They require an authenticated human Entra subject and use only that caller's current Repo App authorization.
| Method | Path | Purpose |
|---|---|---|
GET | /api/github/repository-selections | List up to 200 bounded, metadata-only repositories available to the caller |
POST | /api/github/repository-selections | Verify one selected browse result and mint a short-lived opaque selection code |
GET returns:
{
"repositories": [
{
"full_name": "octo/example",
"owner_login": "octo",
"private": true,
"default_branch": "main",
"pushed_at": "2026-08-28T00:00:00+00:00"
}
],
"installations": [
{
"account_login": "octo",
"account_type": "user",
"repository_selection": "selected",
"management_url": "https://github.com/settings/installations/123"
}
]
}Repository enumeration uses the caller's Repo App user access token against the user-installation repository endpoint. The visible list is therefore limited to repositories available to both the caller and the App installation. installations contains GitHub-provided navigation metadata for personal and organization installations, including whether each installation grants all or selected repositories. management_url is validated against the configured GitHub web origin.
The response intentionally has no repository permission map, clone URL, raw installation ID, credential data, provider error, or assertion that public metadata proves operational access. POST accepts { "full_name": "octo/example" } only as a user selection instruction. The server rechecks that name against the caller's bounded Repo App browse result, then returns:
{ "selection_code": "opaque-43-character-base64url-value", "expires_at": "2026-08-28T00:05:00+00:00" }The code is cryptographically random, stored only as a digest, caller-bound, valid for five minutes, credential-kind-bound, and atomically single-use. Entra codes are bound to the exact Repo App authorization used to issue them. A code is not a general GitHub credential. Missing, revoked, malformed, expired, reused, or cross-subject codes fail closed and do not disclose repository scope. Browser responses contain no GitHub repository IDs, installation or authorization IDs, tokens, secrets, or permission maps. These endpoints return 409 with one of human_entra_subject_required, github_binding_unavailable, or github_capability_unavailable; malformed selection input returns 400.
The GitHub branch of POST /api/projects accepts only repository_selection_code as repository authority. It atomically consumes the code for the authenticated caller, verifies the active Repo App authorization is still usable, then resolves clone metadata server-side. It rejects client-supplied repository URLs, identifiers, owner/name, installation IDs, tokens, and permission maps. Project Settings uses the same browse + selection-code flow when attaching an existing repository to a blank-origin project through POST /api/projects/{id}/github/repository/connection.
Memory
Memory is scoped to projects. MemoryContextCompiler serializes selected decisions, memories, and session fields into a single JSON data envelope marked as untrusted; stored text is never emitted as prompt headings or executable instructions. Cross-team memory and active architectural/scope decisions compile only after approval by a project owner or a run-authenticated Coordinator. Export writes to .squad/decisions.md, .squad/agents/{name}/history.md, .agentweaver/context/boundaries.md, and .agentweaver/context/patterns.md.
Memory and decision responses expose sourceKind, sourceIdentity, sourceRunId, and trustState; approved records also expose approvedBy and approvedAt. Existing rows migrate as sourceKind: "legacy" and trustState: "legacy" and therefore do not compile until explicitly approved. This fail-closed migration avoids treating historical rows with unknown provenance as trusted policy.
Agent loopback writes authenticate with the normal internal API key plus a run-scoped capability. Only a SHA-256 digest of the short-lived capability is stored in the shared database, so validation works across API replicas without persisting the bearer token. The API resolves the project and agent from the verified run and rejects a client-supplied agent_name that does not match. Human API callers continue to use project authorization; only project owners and verified Coordinator runs may promote memory, merge/reject inbox entries, or create/update active decisions.
Decision Inbox
| Method | Path | Purpose |
|---|---|---|
POST | /api/projects/{id}/decisions/inbox | Submit a decision or learning to the inbox |
GET | /api/projects/{id}/decisions/inbox | List inbox entries (?agent=, ?type=, ?status=) |
POST | /api/projects/{id}/decisions/inbox/{entryId}/merge | Merge a pending entry into decisions |
POST | /api/projects/{id}/decisions/inbox/{entryId}/promote | Alias for merge/promote |
POST | /api/projects/{id}/decisions/inbox/{entryId}/reject | Reject a pending entry |
Decisions
| Method | Path | Purpose |
|---|---|---|
POST | /api/projects/{id}/decisions | Create a decision directly |
GET | /api/projects/{id}/decisions | List decisions (?type=, ?agent=) |
GET | /api/projects/{id}/decisions/{decisionId} | Get a single decision |
POST | /api/projects/{id}/decisions/{decisionId}/approve | Approve a legacy or pending-trust decision for compilation |
PUT | /api/projects/{id}/decisions/{decisionId} | Update decision status/content |
Agent Memory
| Method | Path | Purpose |
|---|---|---|
POST | /api/projects/{id}/agents/{name}/memory | Add a memory entry for an agent |
GET | /api/projects/{id}/agents/{name}/memory | List agent memories (?type=, ?importance=) |
GET | /api/projects/{id}/agents/{name}/memory/{memId} | Get a single memory entry |
POST | /api/projects/{id}/agents/{name}/memory/{memId}/promote | Approve memory for cross-agent compilation |
GET | /api/projects/{id}/memory | Cross-agent memory search (?type=, ?tags=) |
Sessions
| Method | Path | Purpose |
|---|---|---|
POST | /api/projects/{id}/sessions | Start a new session (auto-ends existing) |
GET | /api/projects/{id}/sessions/current | Get current open session |
PUT | /api/projects/{id}/sessions/current | Update focus, summary, or end session |
GET | /api/projects/{id}/sessions | List sessions |
PATCH | /api/projects/{id}/sessions/{sessionId} | Update a specific session |
Export / Import
| Method | Path | Purpose |
|---|---|---|
POST | /api/projects/{id}/memory/export | Export DB memory → .squad/ + .agentweaver/context/ |
POST | /api/projects/{id}/memory/import | Import .squad/decisions/inbox/*.md → DB |
GitHub capability authorization
| Method | Path | Purpose |
|---|---|---|
POST | /api/auth/github/repo-app/authorizations | Begin an Entra-user-bound Repo App authorization; returns an authorization URL and opaque transaction ID |
POST | /api/auth/github/repo-app/authorizations/handoff | Begin an MCP-safe Repo App browser handoff; returns only an opaque transaction ID, browser URL, and expiry |
GET | /auth/github/repo-app/handoff/{transactionId} | Redeem an MCP browser URL only from the initiating user's authenticated Entra browser session; issues the callback cookie and redirects to GitHub |
GET | /auth/github/repo-app/callback | Complete the Repo App browser callback with its one-time callback cookie |
GET | /api/auth/github/repo-app/authorizations/{transactionId} | Return only the initiating subject's safe transaction status |
POST | /api/auth/github/repo-app/authorization/refresh | Refresh the caller's Repo App authorization without changing its grant identity |
DELETE | /api/auth/github/repo-app/authorization | Revoke the caller's Repo App authorization and write a credential tombstone |
Project Copilot App authorization has equivalent project-scoped endpoints at /api/projects/{id}/github/copilot/authorizations/handoff and /auth/github/copilot-app/handoff/{transactionId}. Handoff URLs require the same initiating Entra browser session through callback completion; the transaction ID alone cannot issue a callback cookie or authorize a GitHub account.
Team casting
Team and casting endpoints are project-scoped and use the project owner check before exposing or changing rosters, charters, proposals, or sync state. A caller who does not own the project cannot manage that project's team.
| Method | Path | Purpose |
|---|---|---|
GET | /api/casting/templates | List available scenario groupings (team templates) |
GET | /api/projects/{id}/casting/universes | List allowed universe names |
GET | /api/catalog/roles | List all available role definitions |
POST | /api/projects/{id}/casting/proposals | Create a casting proposal |
GET | /api/projects/{id}/casting/proposals | List active proposals for a project |
GET | /api/projects/{id}/casting/proposals/{proposalId} | Get a proposal |
PATCH | /api/projects/{id}/casting/proposals/{proposalId} | Amend a proposal |
POST | /api/projects/{id}/casting/proposals/{proposalId}/confirm | Confirm a proposal and create the team |
DELETE | /api/projects/{id}/casting/proposals/{proposalId} | Reject a proposal |
GET | /api/projects/{id}/team | Get team roster and layout metadata |
GET | /api/projects/{id}/team/members/{name}/charter | Get a member's charter |
PUT | /api/projects/{id}/team/members/{name}/charter | Replace a member's charter |
GET | /api/projects/{id}/team/members/{name}/history | Get agent interaction history |
POST | /api/projects/{id}/team/members | Add a team member |
DELETE | /api/projects/{id}/team/members/{name} | Retire a team member |
PATCH | /api/projects/{id}/team/members/{name} | Re-role a team member |
GET | /api/projects/{projectId}/team/sync | Get pending .squad/ changes and change set hash |
POST | /api/projects/{projectId}/team/sync | Commit pending .squad/ changes |
Team member objects include is_built_in: true for Scribe, Ralph, and Rai (case-insensitive). Built-in agents cannot be removed, re-roled, or directly run. Attempting to start a run with a built-in agent name returns 400 Bad Request.
Blueprints
| Method | Path | Purpose |
|---|---|---|
GET | /api/blueprints | List predefined blueprints |
POST | /api/blueprints/generate | Accept a durable Blueprint-generation job |
GET | /api/blueprints/generation-jobs/{jobId} | Read authorized generation status |
GET | /api/blueprints/generation-jobs/{jobId}/result | Read the immutable generated Blueprint artifact |
POST | /api/blueprints/generation-jobs/{jobId}/cancel | Cancel queued or running generation |
POST | /api/blueprints/generation-jobs/{jobId}/retry | Retry failed or cancelled generation |
POST | /api/blueprints/suggest | Analyze a GitHub repository and recommend a catalog blueprint |
POST | /api/blueprints/validate | Validate an inline blueprint |
Suggested blueprint analysis accepts { "repository": "owner/repo" }, returns recommended_blueprint, rationale, confidence, signals, and fallback, and gracefully returns fallback: true when GitHub analysis is unavailable. See Repository blueprint suggestions.
Backlog, board, and workflow setup
Backlog, board, review-policy, and workflow endpoints are project-scoped and require ownership of the containing project. Backlog tasks do not introduce separate cross-user privileges; callers manage only tasks in projects they own.
| Method | Path | Purpose |
|---|---|---|
GET | /api/projects/{id}/workspace/files | List project workspace files for decomposition |
POST | /api/projects/{id}/backlog/decompose | Decompose workspace files into backlog tasks |
POST | /api/projects/{projectId}/backlog/tasks | Create a backlog task |
PATCH | /api/projects/{projectId}/backlog/tasks/{taskId} | Edit a backlog task title/description |
DELETE | /api/projects/{projectId}/backlog/tasks/{taskId} | Delete a backlog task |
POST | /api/projects/{projectId}/backlog/tasks/{taskId}/ready | Move a task to ready |
POST | /api/projects/{projectId}/backlog/ready-all | Move all eligible tasks to ready |
POST | /api/projects/{projectId}/backlog/tasks/{taskId}/backlog | Move a task back to backlog |
POST | /api/projects/{projectId}/backlog/tasks/{taskId}/reorder | Reorder a backlog task |
POST | /api/projects/{projectId}/backlog/tasks/{taskId}/archive | Archive a backlog task |
GET | /api/projects/{projectId}/board | Get the board state |
GET | /api/projects/{projectId}/workflow-stages | Get workflow stages |
GET | /api/projects/{projectId}/backlog/settings | Get backlog pickup settings |
PUT | /api/projects/{projectId}/backlog/settings | Update backlog pickup settings |
GET | /api/projects/{projectId}/review-policies | List review policies |
POST | /api/projects/{projectId}/review-policies/sync | Reload review policies from disk |
GET | /api/projects/{projectId}/workflows | List workflows, including structured trigger metadata |
GET | /api/projects/{projectId}/workflows/{workflowId} | Get one workflow, including all triggers |
GET | /api/projects/{projectId}/workflows/{workflowId}/trigger | Get the workflow's trigger configs as structured JSON |
PUT | /api/projects/{projectId}/workflows/{workflowId}/trigger | Create/replace one trigger by type |
PATCH | /api/projects/{projectId}/workflows/{workflowId}/trigger | Partially update one trigger by type |
DELETE | /api/projects/{projectId}/workflows/{workflowId}/trigger | Clear all triggers, or one type with ?type= |
POST | /api/projects/{projectId}/workflow-events | Fire a named workflow event manually |
GitHub App webhook receiver
GitHub delivers repository events only to the Repo App's App-level receiver; do not configure per-project webhook URLs or provisioning routes. The API verifies the webhook signature before parsing and routing the delivery.
| Method | Path | Purpose |
|---|---|---|
POST | /api/github/webhooks/repo-app | Receive an HMAC-signed Repo App webhook delivery |
Workflow trigger objects use the existing top-level trigger fields (type, interval, day_of_week, day_of_month, time_of_day, event_name) plus an optional if predicate array for event triggers. The array is implicitly ANDed; compound logic uses nested or / not wrapper predicates. The current JSON predicate vocabulary is:
hasLabel: { label }isNotLabeledWith: { label }baseBranch: { branch }reviewState: { state }wherestateisapproved,changes_requested, orcommentedref: { branch, matchMode }wherematchModeisequalsorprefixcategory: { name }commentMatches: { pattern }
Example trigger payload:
{
"type": "event",
"event_name": "github.pull_request.opened",
"if": [
{
"or": [
{ "baseBranch": { "branch": "main" } },
{ "baseBranch": { "branch": "release/v1" } }
]
}
]
}| GET | /api/projects/{projectId}/review-policies/{policyName} | Get a review policy | | PUT | /api/projects/{projectId}/review-policies/active | Set the active review policy | | GET | /api/projects/{projectId}/workflows | List workflow definitions | | POST | /api/projects/{projectId}/workflows/sync | Reload workflow definitions from disk | | GET | /api/projects/{projectId}/workflows/{workflowId} | Get a workflow definition | | GET | /api/projects/{projectId}/workflows/{workflowId}/trigger | Get a workflow's structured trigger config | | PUT | /api/projects/{projectId}/workflows/{workflowId}/trigger | Create or replace one workflow trigger type | | PATCH | /api/projects/{projectId}/workflows/{workflowId}/trigger | Partially update one workflow trigger type | | DELETE | /api/projects/{projectId}/workflows/{workflowId}/trigger | Clear all triggers or one trigger type | | PUT | /api/projects/{projectId}/workflows/default | Set the default workflow | | PUT | /api/projects/{projectId}/backlog/tasks/{taskId}/workflow-override | Set a task workflow override | | GET | /api/projects/{projectId}/workflows/{workflowId}/graph | Get a workflow graph | | GET | /api/projects/{projectId}/workflows/{workflowId}/yaml | Get workflow YAML | | PUT | /api/projects/{projectId}/workflows/{workflowId} | Replace a workflow definition | | POST | /api/projects/{projectId}/workflows/generate | Accept a durable workflow-generation job (Idempotency-Key required) | | GET | /api/projects/{projectId}/workflows/generation-jobs/{jobId} | Read authorized workflow-generation status | | GET | /api/projects/{projectId}/workflows/generation-jobs/{jobId}/result | Read immutable workflow YAML, version, and graph | | POST | /api/projects/{projectId}/workflows/generation-jobs/{jobId}/cancel | Cancel queued or running generation | | POST | /api/projects/{projectId}/workflows/generation-jobs/{jobId}/retry | Retry cancelled or retryable failed generation |
Workflow trigger configuration
Workflow list and detail responses expose the complete triggers array. The legacy trigger field remains as an alias for the first trigger so existing clients continue to deserialize unchanged. The dedicated trigger CRUD endpoints expose the same shapes without requiring callers to rewrite the whole workflow YAML.
GET /api/projects/{projectId}/workflows/{workflowId}/trigger
PUT /api/projects/{projectId}/workflows/{workflowId}/trigger
PATCH /api/projects/{projectId}/workflows/{workflowId}/trigger
DELETE /api/projects/{projectId}/workflows/{workflowId}/trigger- Every response returns
{ "trigger": <WorkflowTriggerDto|null>, "triggers": [...] }. PUTaccepts aWorkflowTriggerDtoand upserts that trigger type without removing other types.PATCHpartially updates the trigger named bytype. When the workflow has multiple triggers,typeis required; a single-trigger workflow retains the legacy type-optional behavior.DELETE ?type=scheduleor?type=eventremoves only that type.DELETEwithout atypepreserves its legacy behavior and clears all triggers.
The JSON contract is intentionally close to workflow YAML:
- top-level trigger keys keep their existing snake_case names such as
event_name,day_of_week,day_of_month, andtime_of_day; - nested predicate objects use camelCase keys such as
hasLabel,baseBranch,commentMatches, andmatchMode.
Schedule trigger body
{
"type": "schedule",
"interval": "weekly",
"day_of_week": "monday",
"time_of_day": "09:00"
}Monthly schedules replace day_of_week with day_of_month (1-28). Schedule triggers reject an if block.
Event trigger body
{
"type": "event",
"event_name": "github.pull_request.opened",
"if": [
{
"or": [
{ "baseBranch": { "branch": "main" } },
{ "baseBranch": { "branch": "release/v1" } }
]
},
{
"not": {
"hasLabel": { "label": "blocked" }
}
}
]
}Sibling entries in if are ANDed by default. Compound logic uses nested or and not wrapper predicates.
Supported predicate payloads:
| Predicate | JSON shape | Supported GitHub event types |
|---|---|---|
hasLabel | { "hasLabel": { "label": "bug" } } | issues, pull_request |
isNotLabeledWith | { "isNotLabeledWith": { "label": "blocked" } } | issues, pull_request |
baseBranch | { "baseBranch": { "branch": "main" } } | pull_request |
reviewState | { "reviewState": { "state": "approved" } } | pull_request_review |
ref | { "ref": { "branch": "refs/heads/main", "matchMode": "equals" } } | push |
category | { "category": { "name": "Ideas" } } | discussion |
commentMatches | { "commentMatches": { "pattern": "^/agentweaver:triage$" } } | issue_comment |
or | { "or": [ ...predicates... ] } | same as its children |
not | { "not": { ...predicate... } } | same as its child |
The curated GitHub event shortlist is issues, issue_comment, pull_request, pull_request_review, push, release, and discussion. release currently has no event-specific predicates beyond the event name itself.
commentMatches accepts a fixed saved pattern only. The backend validates it against a safe regex subset, executes it with the non-backtracking engine plus a hard timeout, and exposes only the boolean match outcome to the rest of the workflow-firing pipeline.
Workspace, diagnostics, and metrics
| Method | Path | Purpose |
|---|---|---|
GET | /health | Public liveness probe |
GET | /api/health | API liveness probe |
GET | /api/diagnostics | Get API diagnostics (SQLite/disk/workflow/heartbeat) |
GET | /api/diagnostics/cluster | Get cluster diagnostics (pods, quota, component health, pending runs) |
GET | /api/diagnostics/heartbeat | Get diagnostics heartbeat |
GET | /api/projects/{id}/diagnostics | Get project diagnostics |
GET | /api/projects/{id}/workspace/refs | List workspace refs |
GET | /api/projects/{id}/workspace | List project workspace files |
GET | /api/projects/{id}/workspace/files/{**path} | Read a project workspace file |
GET | /api/projects/{id}/dashboard | Get project dashboard summary plus compatibility throughput / leaderboard fields |
GET | /api/projects/{id}/metrics | Get App Insights-backed throughput and leaderboard widgets |
GET | /api/overview | Get global overview metrics |
GET | /api/runs/{id}/token-breakdown | Get per-agent token and AI-credit data for a run |
GET | /api/metrics/runs/{runId}/traces | Get Application Insights agent and LLM spans for a run |
GET /api/diagnostics/cluster
Returns a ClusterDiagnosticsDto with the current state of the Kubernetes cluster as seen by the Agentweaver API. Requires authentication. Returns 404 Not Found when cluster diagnostics are not available (e.g. non-AKS deployment).
Five component health checks run concurrently with a 5-second individual timeout each:
| Check name | What it tests |
|---|---|
postgresql | Postgres connectivity |
key_vault | Azure Key Vault CSI delivery of the required mcp-api-key. critical: secret 'mcp-api-key' not found means API authentication and worker loopback calls cannot run. |
agent_pod_quota | Effective admission headroom in the sandbox namespace, computed from the tighter of the pods and SandboxClaim object quotas. |
warm_pool | Warm-pool agent-sandbox availability |
kubernetes_api | Kubernetes API server reachability |
Response 200 OK — a ClusterDiagnosticsDto:
{
"checks": [
{ "name": "postgresql", "status": "pass", "detail": null, "duration_ms": 12 },
{ "name": "agent_pod_quota", "status": "warn", "detail": "4 additional agent pod starts available before quota exhaustion (limited by pods; pods 196/200, sandboxclaims 188/200 used)", "duration_ms": 45 }
],
"active_agent_pods": [
{ "pod_name": "agent-host-abc123", "run_id": "f36800fd-...", "node": "katapool-vm-1", "started_at": "2026-06-27T17:55:00Z" }
],
"orphaned_agent_pods": [],
"pending_capacity_runs": [
{ "coordinator_run_id": "coord-...", "subtask_id": 7, "pending_since": "2026-06-27T17:58:30Z", "retry_count": 3 }
]
}| Field | Type | Notes |
|---|---|---|
checks | DetailedHealthCheckDto[] | One entry per check. Its status is healthy, warning, critical, or unknown. |
active_agent_pods | AgentPodInfoDto[] | Pods currently running with a matching active run. |
orphaned_agent_pods | AgentPodInfoDto[] | Pods running with no matching active run (candidates for next reaper sweep). |
pending_capacity_runs | PendingCapacityRunDto[] | Legacy / back-compat. Subtasks recorded in the historical PendingCapacity status; empty for new runs (Kubernetes now owns scheduling, issue #217). |
See Cluster diagnostics reference for the full DTO schema and field descriptions.
GET /api/diagnostics/heartbeat — automation_name
The heartbeat tick records returned by GET /api/diagnostics/heartbeat include an automation_name field on each TickRecordDto:
{
"tick_records": [
{
"automation_name": "Coordinator Heartbeat",
"acted_count": 2,
"error_count": 0,
"duration_ms": 340,
"recorded_at": "2026-06-27T18:00:00Z"
}
]
}The Heartbeat page Recent Activity table shows this as the first column (Automation). Possible values are "Coordinator Heartbeat" and "Checkpoint GC".
GET /
Returns the plain text banner Agentweaver API.
GET /api/runs/{id}
Persisted project-scoped runs inherit their stored project's access: Viewer permits inspection; Contributor permits run control, review, approval, questions and steering; Owner permits project administration. Authorization uses persisted ProjectId, not caller-supplied context or the submitting-user string. Legacy runs without a project retain submitting-user ownership; dangling project references fail closed. Unauthorized ordinary-run SSE access returns 404 to hide existence.
Response 200 OK:
{
"run_id": "f36800fd-f2f8-418c-958e-aae3e4921ba6",
"status": "awaiting_review",
"model_source": "github-copilot",
"started_at": "2026-06-07T21:09:45.7526712+00:00",
"ended_at": "2026-06-07T21:09:52.103+00:00",
"step_count": 4,
"tree_hash": "a1b2c3d4e5f6...",
"diff": "diff --git a/a.txt b/a.txt\n..."
}Unknown ids return 404 Not Found. Status values are pending, in_progress, awaiting_review, merging, merged, declined, merge_failed, failed, and completed. completed is reached when the agent turn produced no file changes (no review gate is entered on that path).
For a coordinator run (agent_name: "Coordinator", no parent), the response also carries coordinator_status: the current work-plan orchestration status (dispatching, awaiting_assembly, assembling, in_review, complete, assembly_blocked, assembly_failed, assembly_declined). It is null for normal runs and for coordinator runs that have no work plan yet. Because a coordinator run stays in_progress while it dispatches children and runs collective assembly, coordinator_status is what the UI should render (for example "Awaiting assembly" or "Failed: <result>") instead of the bare status. On a terminal assembly failure the result — also surfaced as coordinator_status_reason on this response (scoped to coordinator runs) — carries the human-readable reason (for example assembly_blocked: <reason>, assembly_merge_failed: <reason>, assembly_error: <message>).
Coordinator run detail also includes coordinator_steerable (boolean). The backend sets it for coordinator runs whose RunStatus is in_progress or awaiting_review, so the UI can keep Message coordinator and steering controls enabled while the collective assembly review gate is parked (apps/Agentweaver.Api/Contracts/Dtos.cs:178, apps/Agentweaver.Api/Endpoints/RunEndpoints.cs:185, apps/Agentweaver.Api/Coordinator/CoordinatorSteeringService.cs:348).
The response also carries auto_approve_tools and autopilot (booleans) reflecting the effective per-run option state. Active runs include live toggles; after runtime cleanup, the values fall back to the persisted launch policy. Both are false unless explicitly enabled. The frontend uses these to render the toggle controls; see POST /api/runs/{id}/auto-approve and POST /api/runs/{id}/autopilot.
POST /api/runs/{id}/archive
Archives a run for the owner. Response 200 OK:
{ "run_id": "f36800fd-...", "archived_at": "2026-06-07T21:20:00+00:00" }DELETE /api/runs/{id}
Cancels and deletes a run record. For any non-terminal run, the shared cancellation path runs first (EndpointHelpers.CancelRunWorkAsync): the live MAF workflow is abandoned — which also stops any child subtask runs a coordinator is driving — the worktree is torn down best-effort, and the run is forced to a terminal Failed state. The run row is then removed and its in-memory stream entry is dropped. Runs already in a terminal state (Merged, Declined, MergeFailed, Failed, Completed) are deleted directly with no cancellation work.
Response 204 No Content.
Authorization:
- Human platform administrators may delete any run, including one whose persisted project no longer exists. Dedicated internal-service credentials cannot delete runs.
- The submitting user may delete their own personal session created by the Assistant endpoints even if its incidental project no longer exists or the user's project role was revoked. Sessions are recognized only when the first durable event is the server-authored sequence-1
run.startedmarker with the run's matchingrunId,agentName: "Operator", andkind: "operator"values. - Other project-owned runs require current project Contributor access.
Errors: 400 invalid run id; 404 run not found; 403 caller lacks deletion authority; 500 fetch or delete failed.
POST /api/runs/{id}/cancel
Cancels a run's live work but keeps the run record so the user can still inspect it. Runs the same shared cancellation path as DELETE — abandon the workflow (stopping coordinator child runs), best-effort worktree cleanup, force to terminal Failed, and complete the event stream — without deleting the row. This is what the Stop action on the Orchestrations list uses.
For a non-terminal run, response 200 OK:
{ "run_id": "f36800fd-...", "status": "failed", "cancelled": true, "already_terminal": false }An already-terminal run has no live work to cancel: the endpoint reports the current state without acting, response 200 OK:
{ "run_id": "f36800fd-...", "status": "completed", "cancelled": false, "already_terminal": true }Errors: 400 invalid run id; 404 run not found; 403 caller is not the run owner; 500 fetch failed.
GET /api/runs/{id}/stream
Streams the run's events over SSE. Requires valid authentication and Viewer access to the run's persisted project (legacy run ownership otherwise) — an unauthorized caller receives 404 (no existence leak). Each frame carries the per-run sequence as the SSE id and the event payload as data:
id: 3
event: agent.message.delta
data: {"delta":"Hello","messageId":"msg-001"}
id: 4
event: run.completed
data: {"result":"no_changes"}
event: done
data: {}The stream ends with a synthetic done frame (no id) after the terminal event.
Set Last-Event-ID to the last per-run sequence received. SQLite uses SqliteRunEventStream; PostgreSQL uses EfRunEventStream. SSE serves a retained local entry when available, otherwise durable replay-and-tail, including another producer's events. Restart or local-entry eviction does not erase persisted history.
Durable subscribers drain the full loaded replay batch before terminating, including persisted diagnostics after a terminal event. coordinator.assembly_blocked is not terminal. A done frame ends a connection, not necessarily a parked or human-gated run.
Response headers:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-alive
POST /api/runs/{id}/review
Records a human review decision. Submitting a decision requires Contributor access to the run's persisted project, or legacy run ownership. Insufficient access returns 403 Forbidden.
Request:
{ "approved": true }Primary path (normal operation)
In normal operation the API hands the decision to the background MAF workflow and returns immediately:
- Approve —
200 OK,status: "merging". The merge runs asynchronously inside the workflow. Watch the SSE stream forreview.approvedfollowed by eithermerge.completedormerge.failedto learn the outcome. - Decline —
200 OK,status: "declined". The workflow terminates;review.declinedis emitted on the stream.
{ "run_id": "...", "status": "merging", "merge_result": null }
{ "run_id": "...", "status": "declined", "merge_result": null }Idempotent re-POST
If the run has already reached a matching terminal state, the endpoint returns the current state rather than an error:
- Re-approving an already-
mergedrun returns200 OKwithstatus: "merged"and the storedmerge_result. - Re-declining an already-
declinedrun returns200 OKwithstatus: "declined".
Error responses
| Status | Condition |
|---|---|
403 Forbidden | The caller does not own the run |
404 Not Found | No run found for the given id |
409 Conflict | The run is not in awaiting_review status (and the decision does not match an already-terminal state), or the review decision was already consumed by a concurrent POST |
A 409 from a duplicate or concurrent POST has no body. A 409 from a wrong-status run includes an error message:
{ "error": "Run is in status 'in_progress' and cannot be reviewed." }Direct fallback path (post-restart recovery)
After a server restart, if no workflow checkpoint is available to resume, the endpoint executes the merge or decline synchronously and returns the final outcome directly:
Merge succeeds —
200 OK, statusmerged. The run's worktree branch is merged into the originating branch.merge_resultismerged:{commit-hash}. If the originating branch is currently checked out and the tree is clean, the branch ref is advanced and the working tree is updated via a hard reset. If it is not checked out, only the branch ref is advanced. On success the worktree is torn down: its physical directory is deleted first, the admin entry is pruned, then the branch is removed.Blocked (retriable) —
409 Conflict, statusawaiting_review. No git mutations occurred. The run stays at the review gate and can be approved again once the condition is resolved. Causes include: uncommitted changes to tracked files, staged changes in the index, untracked files that would be overwritten by the merge, a merge or rebase already in progress in the working tree, the repository lock being held by another concurrent request, or a concurrent approve that already won the CAS gate. Body:json{ "error": "there are uncommitted changes to tracked files", "status": "awaiting_review" }Terminal conflict —
200 OK, statusmerge_failed. The originating branch has diverged with conflicts that require human resolution, or the tree hash stored at review time no longer matches the worktree branch. The originating branch is unchanged and the worktree is preserved.merge_resultisconflict:{reason}.Decline —
200 OK, statusdeclined,merge_result: null.
{ "run_id": "...", "status": "merged", "merge_result": "merged:34c09ee..." }
{ "run_id": "...", "status": "merge_failed", "merge_result": "conflict:The originating branch has diverged..." }
{ "run_id": "...", "status": "declined", "merge_result": null }See events.md for the event types emitted on the stream for each outcome.
POST /api/runs/{id}/shell-approvals
Approves a pending shell command. Use the commandHash from the shell.approval_required event as command_hash.
Request:
{ "command_hash": "sha256:..." }Response 200 OK { "run_id", "command_hash", "approved": true }.
POST /api/runs/{id}/shell-denials
Denies a pending shell command.
Request:
{ "command_hash": "sha256:..." }Response 200 OK { "run_id", "command_hash", "denied": true }.
GET /api/runs/{id}/events
Returns persisted run events ordered by sequence. Optional after, limit, and type query parameters provide bounded server-side retrieval. See Run events for the complete request, validation, ordering, and response contract.
GET /api/runs/{id}/history
Replays persisted Copilot SDK session events for a terminal run. The session is identified by agentweaver-run-{runId}. Returns a JSON array of run events in stream order. Only available for terminal runs. Returns 404 if the run is not terminal or the session is not found.
GET /api/runs/{id}/graph
Returns the workflow graph descriptor for the run, describing the node/edge topology so a client can render the live workflow without hardcoding it. The descriptor is built from the same code that wires the MAF workflow (no runtime reflection). Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise) Bearer auth. Coordinator runs (parent_run_id == null, driven by the built-in Coordinator agent, with a persisted work plan) return the coordinator variant (see below); child runs (parent_run_id != null) return the child variant; all others return the full variant.
Response 200 OK — a GraphDescriptor:
{
"graph_id": "agentweaver-workflow-full",
"variant": "full",
"start_node_id": "agent",
"nodes": [
{ "id": "agent", "label": "Agent", "role": "agent", "kind": "live", "node_type": "agent", "child_graph_ref": null }
],
"edges": [
{ "from": "agent", "to": "rai", "cardinality": "direct", "loopback": false }
]
}variant:"full"|"child"|"coordinator".nodes[].id: the logical node id (matches the step key inworkflow.stepevents).kind:"live"|"planned".child_graph_ref: optional reference to a nested graph.nodes[].node_type: self-declared category that drives the frontend's rendered shape/size — one of"agent"(an AI agent turn),"action"(a deterministic system op),"gate"(a human-in-the-loop decision/approval),"terminal"(a workflow endpoint/checkpoint), or"subtask"(a coordinator fan-out child reference). Required on every node.edges[].cardinality:"direct"|"fanout"|"fanin".loopback:truewhen the edge targets an ancestor (a revision cycle back-edge).
Coordinator variant
Coordinator graph descriptors combine work-plan topology with persisted status. Nodes may include status, status_reason, and terminal_stage; subtask state also arrives through coordinator.topology. Selected-workflow assembly gates become kind: "live" when reached, even though their stable IDs start with planned:assembly-. Failure projection uses the terminal stage so failure-scribe does not mark never-run gates as executed. Delegated plans leave skipped nodes planned with delegated status.
Leaf subtasks connect to the first selected gate (or merge if none); the gates form a chain followed by merge and Scribe. Each selected gate has a coordinator loopback, excluded from forward degree calculations. Fixed gate lists are examples for a particular workflow, not a universal RAI-only pipeline.
- Node
coordinator(node_type: "agent",role: "coordinator",kind: "live"). - One node per subtask, id
plan:subtask-{id}(node_type: "subtask",role: "subtask",kind: "live"). Subtask nodes carry rich display fields as OPTIONAL snake_case properties (omitted when null):agent,model,phase,isolation,child_run_id. Once the subtask's child run is dispatched,child_graph_refisrun:{childRunId}so the client can expand the child's own graph viaGET /api/runs/{childRunId}/graph; it isnulluntil dispatched. - Assembly nodes are resolved from the selected workflow. Stable
planned:assembly-*IDs become live when reached; optional status/reason/terminal-stage fields describe persisted execution. Merge and Scribe follow the gates. - Edges connect Coordinator to root subtasks, prerequisite to dependent subtasks, and each leaf to the first selected assembly gate (or merge). Gates chain into merge and Scribe. Every selected gate has a Coordinator loopback; forward degree/cardinality excludes those direct loopbacks.
{
"graph_id": "coordinator:run_abc",
"variant": "coordinator",
"start_node_id": "coordinator",
"nodes": [
{ "id": "coordinator", "label": "Coordinator", "role": "coordinator", "kind": "live", "node_type": "agent", "child_graph_ref": null },
{ "id": "plan:subtask-1", "label": "Build API", "role": "subtask", "kind": "live", "node_type": "subtask", "child_graph_ref": "run:run_child1", "agent": "morpheus", "model": "gpt-5.3-codex", "phase": "execution", "isolation": "worktree", "child_run_id": "run_child1" }
],
"edges": [
{ "from": "coordinator", "to": "plan:subtask-1", "cardinality": "fanout", "loopback": false }
]
}The same descriptor is emitted once at run start as a run.workflow_graph event on the stream (see events.md).
POST /api/runs/{id}/commit
Commits any remaining uncommitted worktree changes and immediately merges the worktree branch into the originating branch. The run must be in awaiting_review. Uses CAS AwaitingReview → Committing → Merging to prevent concurrent commits.
Response:
200 OK{ run_id, status: "merged", merge_result: "merged:{hash}" }on success200 OK{ run_id, status: "merge_failed", merge_result: "conflict:{reason}", conflicting_files: [...] }on conflict409 Conflict{ error, status: "awaiting_review" }on retriable block (dirty working tree, concurrent request, etc.)409 Conflict{ error }if the run is not inawaiting_review
POST /api/runs/{id}/request-changes
Requests a revision cycle. The agent is given the reviewer's comment and re-runs on the same worktree without creating a new branch. The run returns to in_progress.
Request body:
{ "comment": "string" }Response: 202 Accepted with the updated run.
POST /api/runs/{id}/retry
Retries a failed or merge-failed run as a new linked run. The source run is not mutated; child runs are retried through their coordinator parent.
Response 201 Created:
{ "run_id": "new-run-id", "retried_from": "old-run-id", "status": "in_progress" }When a failed coordinator can resume its existing recovery point, the response is instead 200 OK and keeps the same run id:
{ "run_id": "existing-run-id", "retried_from": null, "status": "in_progress", "resumed": true }On an AgentHost deployment, the retry requires the project's explicitly connected, run-bound GitHub Copilot capability before pod creation. A missing connection returns 409 Conflict with the standard model_provider_connection_required action payload.
GET /api/runs/{id}/workspace
Returns a tree of all files in the run's worktree (folders + files). Files include path, is_folder, status (added/modified/deleted, null for unchanged), added_lines, removed_lines. Returns 404 for terminal runs whose worktrees have been removed (failed/merged/declined/merge_failed). Returns empty array for pending. Returns 409 while the worktree does not exist for an active run.
GET /api/runs/{id}/files
Flat list of changed files. Query param filter: all (committed + uncommitted), committed, uncommitted, last-commit. Returns an empty array while asynchronous worktree provisioning is incomplete. Coordinator runs always return an empty array here because they have no per-run worktree; use GET /api/runs/{id}/assembly/files for their collective output.
GET /api/runs/{id}/files/{**path}
Returns diff and content for a specific file. Response includes path, status, diff, content, is_binary.
POST /api/runs/{id}/tool-approvals
Approves a pending tool call.
Request:
{ "request_id": "string", "scope": "once" | "run" | "always" | "tool" }Scope values: once = this call only; run = all calls to the same tool+url this run; always = all calls this server session; tool = all calls to this tool regardless of url. For a decision forwarded to a pod-local gate, always is effectively run-scoped and does not survive a pod restart.
Response 200 OK { "run_id", "request_id", "approved": true }. Terminal/replayed and pod-forwarded responses also include resolved: true, expired, and state: "approved" | "denied" | "expired"; pod-forwarded approvals also include applied, which confirms that the owning AgentHost accepted this exact forwarding request. The returned run_id is the run that actually owned the approval, which may differ from {id}.
Owning-run resolution. The approval context lives on the run that raised the tool call. When {id} is a coordinator run (ParentRunId == null and AgentName == "Coordinator"), the API checks its children and then scans persisted coordinator.child_approval_required events for the matching requestId and childRunId. Approving therefore works whether the client posts the coordinator id or the child id.
Pod-per-run fallback. If the API's DurableToolApprovalGate returns Unknown for the resolved child, the API uses IAgentHostOriginResolver and AgentHostApprovalHttpClient to forward the selected scope to the pod's authenticated /tool-approvals route through the a2a-sandbox-pod client. The bearer is re-fetched with PreviewRunnerCredential.SecretKey(runId). The AgentHost publishes its current-pod scope bridge only when it wins and applies that pending approval; the API publishes the durable cross-pod policy only after it receives resolved: true, state: "approved", and applied: true for that exact forward. A duplicate or late terminal response with applied: false, and every failed, denied, or expired forward, leaves no durable policy. A successful terminal forward emits tool.approval_resolved on the child run.
| Status | Approval result |
|---|---|
200 OK | The request is terminal (approved, denied, or expired) |
404 Not Found | state: "unknown" after owning-run resolution and any pod forward, or the run does not exist |
409 Conflict | state: "pending" and the decision should be retried, or the run is not active |
503 Service Unavailable | state: "agenthost_unreachable" because the AgentHost origin/call/response was unavailable |
Other errors: 400 invalid run id / missing request_id; 403 caller is not the run owner.
POST /api/runs/{id}/tool-denials
Denies a pending tool call.
Request:
{ "request_id": "string" }Response 200 OK { "run_id", "request_id", "denied": true }. Terminal/replayed and pod-forwarded responses also include resolved: true, expired, and state. Denials use the same persisted coordinator-to-child owning-run resolution and authenticated pod fallback as approvals, so the returned run_id may differ from {id}.
Status codes are the same as tool approvals: 200 terminal, 404 state: "unknown", 409 state: "pending", and 503 state: "agenthost_unreachable", plus 400 validation, 403 ownership, and 409 inactive-run errors.
Sources: apps/Agentweaver.Api/Endpoints/RunEndpoints.cs:1559-1705, apps/Agentweaver.Api/Endpoints/RunEndpoints.cs:2590-2718, apps/Agentweaver.Api/Endpoints/EndpointHelpers.cs:43-98, apps/Agentweaver.Api/Sandbox/AgentHostApprovalHttpClient.cs:28-112.
POST /api/runs/{id}/questions/{requestId}/answer
Answers a pending ask_question request, resuming the agent that called the ask_question tool. The requestId is the value carried by the agent.question_asked event. For a coordinator child run, answer against the CHILD run id (carried by coordinator.child_question).
Request:
{ "answer": "string" }Response: 200 OK { "run_id", "request_id", "answered": true }.
Errors: 400 invalid run id / missing answer; 404 run not found; 409 no pending question for this request_id (already answered, timed out, or never asked); 403 caller is not the run owner. The run must be InProgress.
POST /api/runs/{id}/auto-approve
Toggles the per-run auto-approve-tools option. When enabled, only web_fetch and start_preview are auto-granted at the human-in-the-loop gate. Every auto-grant is logged as tool.auto_approved. Preview validation still applies, and destructive, privileged, secret-bearing, and other network approvals remain gated. Set the launch policy with auto_approve_tools on POST /api/projects/{id}/orchestrations; the legacy autoApproveTools spelling is also accepted. It cascades to dispatched children and defaults OFF.
Request:
{ "enabled": true }Response: 200 OK { "run_id", "auto_approve_tools": true }.
Errors: 400 invalid run id; 404 run not found; 403 caller is not the run owner; 409 run is not active (InProgress).
POST /api/runs/{id}/autopilot
Toggles the coordinator Autopilot option. When enabled, CLARIFYING QUESTIONS ONLY (the coordinator's own and those bubbled by child workers as coordinator.child_question) are auto-answered by the coordinator model from the outcome spec + subtask context, then resolved on the child's question gate. Each auto-answer is logged as coordinator.autopilot_answered, and the normal agent.question_answered resolution still surfaces on the child stream. Autopilot does NOT auto-grant tool approvals/permissions (that is the separate auto-approve-tools opt-in). Settable at launch (autopilot on POST /api/projects/{id}/orchestrations) and cascades to children. Defaults to OFF. Set at launch in defineOutcome mode, autopilot additionally auto-confirms the Phase-1 outcome spec unattended (confirmedBy = the submitting user) instead of parking at awaiting_confirmation; this live toggle only governs the clarifying-question answering described above.
Request:
{ "enabled": true }Response: 200 OK { "run_id", "autopilot": true }.
Errors: 400 invalid run id; 404 run not found; 403 caller is not the run owner; 409 run is not active (InProgress).
Assistant endpoints
These endpoints back the Sessions UI (see Sessions & the Assistant — User Guide and Assistant runtime — Deep Dive). Every session is stored as a run record with agent_name: "Operator", so GET /api/runs/{id}, GET /api/runs/{id}/stream//events, and DELETE /api/runs/{id} documented above all work against a session's id too. Auth is enforced globally (no unauthenticated request reaches these handlers); a caller may only ever see their own sessions.
POST /api/assistant/runs
Starts a new session. message is optional — if supplied, the opening turn runs immediately and its reply is returned in the same response; if omitted, the run is created empty and the first message is sent via POST /api/assistant/runs/{id}/messages.
Request:
{
"message": "What's blocked on the board right now?",
"project_id": null,
"run_id": null,
"model_id": null
}All fields are optional. project_id associates the session with a project (sessions are otherwise unscoped); run_id lets a caller pin a specific id instead of a server-generated one; model_id overrides the default model.
Response 201 Created:
{
"run_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "in_progress",
"message": "You have 4 items in Ready and 2 In Progress...",
"tools_invoked": ["backlog_list"]
}message and tools_invoked are null when no opening message was supplied. On an AgentHost deployment, project_id is required and its GitHub Copilot App must be explicitly connected before the API creates a pod; a missing project returns 400 (project_context_required) and a missing connection returns 409 Conflict with the standard redacted model_provider_connection_required action payload. Other errors: 429 Too Many Requests with { "error": "operator_run_limit", "limit": 5 } when the caller already has MaxConcurrentRunsPerUser (5) sessions actively in progress — counted from durable run status, so opening, listing, or replying to an existing conversation never consumes a slot and the API's replicas agree on the count; other 4xx from AssistantRunHttpException; a model/provider failure maps to 401 (auth), 429 (rate limited), or 503 (other provider failure).
GET /api/assistant/runs
Lists the caller's own sessions, newest first. Never returns another user's sessions.
Query: ?limit= — optional, defaults to 50.
Response 200 OK:
{
"runs": [
{ "run_id": "3fa85f64-...", "status": "in_progress", "title": "What's blocked on the board right now?", "created_at": "2026-07-16T21:09:45.75Z" }
]
}POST /api/assistant/runs/{id}/messages
Sends the next user message into an existing session and runs a turn. If the session isn't cached in the pod that receives this request — because it went idle (30-minute timeout), the request landed on a different replica, or the pod restarted — it's transparently rehydrated from the session's persisted history before the turn runs; see Assistant runtime — Deep Dive for the mechanism. A session already idle-closed is flipped back to in_progress.
Request:
{ "message": "And which of those are mine?" }message is required; a blank/whitespace-only value returns 400 Bad Request (error: "message_required").
Response 200 OK:
{
"run_id": "3fa85f64-...",
"message": "Of those 4 in Ready, 2 are assigned to you...",
"status": "in_progress",
"tools_invoked": ["backlog_list"]
}Errors: 404 unknown session id, or one that's been permanently closed/deleted — AssistantRunHttpException (error: "run_not_found"); 403 the caller doesn't own the session (error: "forbidden"); provider failures map the same way as POST /api/assistant/runs.
Sandbox port-forward endpoints
These run-scoped endpoints manage browser previews. Gateway-direct HTTPS is primary when enabled; kubectl forwarding is only the disabled-preview API-host loopback fallback.
POST /api/runs/{runId}/sandbox/port-forward
With Sandbox:Preview:Enabled=true, start provisions Gateway-direct routing through a per-preview HTTPRoute and ClusterIP Service to the sandbox, returning preview_url and keepalive_url. With preview disabled it starts kubectl port-forward on API-host loopback; that address is not automatically reachable from a remote browser. Contributor starts/stops; Viewer lists. The target_port request first validates 1-65535; Gateway also applies its configured allowed range. The pf-*/local_port response below describes only the disabled-preview fallback.
Request:
{ "target_port": 3000 }Response 200 OK:
{
"session_id": "pf-abc123",
"local_port": 54321,
"target_port": 3000,
"pod_name": "agentweaver-agent-host-...",
"started_at": "2026-06-07T21:00:00+00:00"
}targetPort must be between 1 and 65535. Start failures return 409 Conflict with an error message.
GET /api/runs/{runId}/sandbox/port-forward
Lists active port-forward sessions for the run. Response 200 OK is an array of { session_id, local_port, target_port, pod_name, started_at }.
DELETE /api/runs/{runId}/sandbox/port-forward/{sessionId}
Stops an active port-forward session. Response 200 OK:
{ "session_id": "pf-abc123", "stopped": true }Returns 404 Not Found when the run or port-forward session does not exist.
Sandbox policy endpoints
These endpoints read and write the per-project sandbox execution policy stored at .agentweaver/settings.yml in the project repository root. Sandbox policies control whether shell execution is enabled, which commands require human approval, and output handling options. See sandbox-setup.md for setup and deep-dive/sandboxed-execution.md for the full design.
GET /api/sandbox-policy
Returns the sandbox policy for the given repository path by reading {repository_path}/.agentweaver/settings.yml. If the file does not exist, returns the default policy.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
repository_path | Yes | Absolute path to the repository |
Response 200 OK:
{
"repository_path": "C:/repos/myproject",
"shell_enabled": true,
"allowed_repository_roots": [],
"destructive_command_patterns": [
"rm -rf", "del /s", "format ", "mkfs", "dd if=",
"git push --force", "git reset --hard"
],
"require_approval_for_all_shell": false,
"redact_pii": true,
"max_output_bytes": 4194304
}Missing or malformed repository_path returns 400 Bad Request.
PUT /api/sandbox-policy
Creates or replaces the sandbox policy for a repository path by writing {repository_path}/.agentweaver/settings.yml. The entire policy is replaced on each PUT; there is no partial-update merge. After a PUT, the operator should commit the updated file to the project repository to record the change in version history.
Request body (all fields required):
{
"repository_path": "C:/repos/myproject",
"shell_enabled": true,
"allowed_repository_roots": [],
"destructive_command_patterns": ["rm -rf", "del /s"],
"require_approval_for_all_shell": false,
"redact_pii": true,
"max_output_bytes": 4194304
}Response 200 OK returns the stored policy. Validation failures return 400 Bad Request.
| Field | Type | Default | Notes |
|---|---|---|---|
repository_path | string | — | Required. Lookup key. Must be an absolute path. |
shell_enabled | bool | true | When false, run_command is excluded from the model's tool list for this project and denied by the governance gate. |
allowed_repository_roots | string[] | [] | Additional paths mounted read-only inside the sandbox. |
destructive_command_patterns | string[] | see default | Command substrings that trigger a shell.approval_required pause. |
require_approval_for_all_shell | bool | false | When true, every shell command requires approval regardless of pattern matching. |
redact_pii | bool | true | When true, emails and IP addresses are removed from command output in addition to secrets. |
max_output_bytes | int | 4194304 | Output cap in bytes. Exceeded output is truncated and marked output_truncated: true. |
Blueprint endpoints
Blueprint endpoints are global and authenticated. A blueprint response includes both the legacy workflow field and the full workflows array. Generated or inline blueprints may include bespoke_roles; each bespoke role id must also appear in roster.
Blueprint shape:
{
"id": "web-app",
"name": "Web App",
"description": "Frontend + API application",
"roster": ["product-manager", "bespoke-domain-expert"],
"workflow": "default",
"workflows": ["default"],
"review_policy": "default",
"sandbox_profile": "default",
"bespoke_roles": [
{
"id": "bespoke-domain-expert",
"title": "Domain Expert",
"charter": "Inline charter text used when no catalog role fits."
}
]
}GET /api/blueprints
Lists predefined blueprints.
Response 200 OK:
{ "blueprints": [ { "...": "BlueprintDto" } ] }POST /api/blueprints/generate
Accepts a durable Blueprint-generation job from a free-text description. The request must include an Idempotency-Key header.
Request:
{
"description": "Build a travel-planning assistant",
"project_id": null,
"target_repository": null
}When project_id is supplied, the caller must own the project and blueprint generation uses that project's blueprint_generation_model; the generated workflow fallback uses workflow_generation_model. Null/omitted project settings inherit the global Generation fallback.
Response 202 Accepted:
{
"job_id": "f6d47c1d7f44414989be66bb7935b97f",
"status": "queued",
"attempt": 0,
"provider_snapshot": {
"provider_kind": "platform_github_copilot",
"provider_key": "opaque-provider-fingerprint",
"provider_scope": "platform",
"resolution_scope": "platform",
"blueprint_model": "gpt-5",
"workflow_model": "gpt-5",
"credential_binding_version": "3"
},
"status_url": "/api/blueprints/generation-jobs/f6d47c1d7f44414989be66bb7935b97f",
"result_url": "/api/blueprints/generation-jobs/f6d47c1d7f44414989be66bb7935b97f/result",
"cancel_url": "/api/blueprints/generation-jobs/f6d47c1d7f44414989be66bb7935b97f/cancel",
"retry_url": "/api/blueprints/generation-jobs/f6d47c1d7f44414989be66bb7935b97f/retry"
}The same subject, key, and request fingerprint return the original job. Reusing the key for different input returns 409 Conflict with idempotency_key_conflict.
Status values are queued, running, completed, failed, and cancelled. Status, result, cancel, and retry reauthorize the bound subject and project. A completed result contains one immutable artifact_id, Blueprint logical_id, version, Blueprint payload, optional generated_workflow_yaml, and warnings. Provider deadlines and availability failures are redacted to blueprint_provider_timeout and blueprint_provider_unavailable; a failed custom-workflow request never returns a default workflow as a successful result. Retry accepts cancelled jobs and failures marked retryable. If the accepted provider identity or credential-binding version changed, the job fails closed; reauthorize and submit a new generation request to capture a new immutable provider snapshot.
POST /api/blueprints/validate
Validates a blueprint shape, workflow/review policy references, sandbox profile, and roster roles. Roster entries must be catalog role ids or ids declared in bespoke_roles.
Request:
{ "blueprint": { "...": "BlueprintDto" } }Response 200 OK:
{ "valid": true, "errors": [] }Event types on the stream
The full event taxonomy — types, payload fields, and per-event descriptions — is in events.md.
The done frame (no id field) signals the end of the stream.
The run.outcome event is emitted by the agent just before run.completed when the agent supports self-assessment. See events.md for the full payload.
Sandbox event types
The following event types are added by the sandboxed execution feature. They appear on the existing SSE stream alongside the base event types.
sandbox.selected
Emitted at run start after the executor selection probe completes. Present on every run.
{
"backend": "processcontainer",
"is_real_isolation": true,
"reason": "processcontainer supported"
}| Field | Type | Notes |
|---|---|---|
backend | string | One of processcontainer, wsl-lxc, lxc-native-linux, passthrough-deny |
is_real_isolation | bool | true when the backend provides real process isolation. false for passthrough-deny. Shell execution is denied when false. |
reason | string | Human-readable reason from the platform probe or selection logic |
sandbox.warning
Emitted when the selected executor has a known limitation that operators should be aware of.
{
"category": "network-unrestricted",
"message": "Sandbox running with unrestricted network on Windows (allowlist enforcement unavailable). Data exfiltration surface is open.",
"backend": "processcontainer"
}| Field | Type | Notes |
|---|---|---|
category | string | Currently only network-unrestricted — the Windows AppContainer backend cannot enforce a network allowlist |
message | string | Human-readable description |
backend | string | The backend that produced the warning |
shell.approval_required
Emitted when a run_command invocation matches a destructive command pattern or when require_approval_for_all_shell is true. The run pauses pending human approval.
{
"request_id": "apr-f36800fd",
"command_length": 42,
"command_hash": "sha256:a1b2c3...",
"message": "Command matches destructive pattern 'rm -rf'. Approve to proceed."
}| Field | Type | Notes |
|---|---|---|
request_id | string | Unique ID for this approval request. Used by the (pending) approval endpoint. |
command_length | int | Length of the command line in characters |
command_hash | string | SHA-256 of the command line, prefixed with sha256: |
message | string | Human-readable reason the approval was triggered |
The approval API endpoint (POST /api/runs/{id}/shell-approvals) records operator approval for a pending shell command. Use the commandHash from the shell.approval_required event as the request body's command_hash. Once approved, the model may retry the command and it will execute immediately.
POST /api/runs/{id}/shell-approvals
Content-Type: application/json
{ "command_hash": "a1b2c3d4e5f6a1b2" }Response 200 OK:
{ "run_id": "f36800fd-...", "command_hash": "a1b2c3d4e5f6a1b2", "approved": true }Returns 400 Bad Request when command_hash is missing or empty.
tool.output
Emitted for each chunk of stdout or stderr produced by a sandboxed run_command invocation during streaming execution.
{
"stream": "stdout",
"data": "Hello from sandbox\n"
}| Field | Type | Notes |
|---|---|---|
stream | string | "stdout" or "stderr" |
data | string | A line or chunk of output from the command. PII and secrets are redacted per the sandbox policy. |
tool.exec_result
Reports the terminal outcome of a run_command invocation. Planned — not yet emitted separately from tool.result.
{
"exit_code": 0,
"timed_out": false,
"output_truncated": false
}| Field | Type | Notes |
|---|---|---|
exit_code | int | Process exit code. -1 when the command timed out or was denied. |
timed_out | bool | true when the command was terminated because it exceeded the configured time limit |
output_truncated | bool | true when captured output exceeded max_output_bytes and was cut off |
Project endpoints
Projects are role-based: creation establishes ownership and listing returns caller-visible projects. Inspection requires Viewer; supported operational mutations/orchestration require Contributor; administration, including provider settings, role assignments, rename and deletion, requires Owner. Personal Assistant sessions retain their separate caller-ownership rules.
POST /api/projects
Creates a new project. Set origin to "blank" to register a local directory as a project, or "github" to clone a GitHub repository into the working directory first.
Request:
{
"name": "my-project",
"origin": "blank",
"working_directory": "C:/repos/my-project",
"default_provider": "github-copilot",
"default_model_github_copilot": null,
"default_model_microsoft_foundry": null,
"blueprint_id": null,
"blueprint": null,
"generated_workflow_yaml": null
}For a GitHub-origin project, first mint a repository_selection_code through the repository selection endpoints, then provide that code. The server verifies and consumes it before resolving the repository and cloning into working_directory; direct repository URLs and identifiers are rejected.
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | Display name |
origin | string | Yes | "blank" or "github" |
working_directory | string | Yes | Absolute local path for the project |
repository_selection_code | string | When origin is "github" | Short-lived opaque selection code from POST /api/github/repository-selections |
default_provider | string | No | "github-copilot" or "byok". The legacy "microsoft-foundry" value is still accepted on input. Falls back to the runtime default when omitted. |
default_model_github_copilot | string | No | Model name override for the GitHub Copilot provider |
default_model_microsoft_foundry | string | No | Model name override for the BYOK provider. The legacy field name remains supported. |
blueprint_id | string | No | Predefined blueprint id from GET /api/blueprints. Mutually exclusive with blueprint. |
blueprint | object | No | Inline BlueprintDto, including optional bespoke_roles. Mutually exclusive with blueprint_id. |
generated_workflow_yaml | string | No | Custom workflow YAML returned by POST /api/blueprints/generate; materialized before applying the blueprint. |
Response 201 Created returns a project object:
{
"project_id": "a1b2c3d4-...",
"name": "my-project",
"origin": "blank",
"source_repository": null,
"working_directory": "C:/repos/my-project",
"default_branch": "main",
"owner": "local-developer",
"default_provider": "github-copilot",
"default_model_github_copilot": null,
"default_model_microsoft_foundry": null,
"blueprint_generation_model": null,
"workflow_generation_model": null,
"outcome_spec_generation_model": null,
"available": true,
"state": "active",
"source_blueprint_id": null,
"source_blueprint_type": null,
"created_at": "2026-06-07T21:00:00+00:00",
"updated_at": "2026-06-07T21:00:00+00:00"
}available is true when the working directory exists on the server filesystem. state is "active" or "deleting".
Validation failures return 400 Bad Request.
GET /api/projects
Returns all projects owned by the authenticated user. Each entry uses the same shape as the POST /api/projects response.
GET /api/server/info
Returns public server metadata. Response 200 OK:
{ "data_directory": "C:/Users/name/AppData/Local/Agentweaver" }GET /api/projects/{id}
Returns a single project owned by the caller. Returns 404 Not Found when no project exists for the given id or the caller does not own it.
PATCH /api/projects/{id}
Renames a caller-owned project.
Request:
{ "name": "new-name" }Response 204 No Content on success. 400 when name is missing. 404 when the project does not exist or is not owned by the caller.
PUT /api/projects/{id}/provider-settings
Updates the provider and model defaults for a caller-owned project.
Request:
{
"default_provider": "byok",
"default_model_github_copilot": null,
"default_model_microsoft_foundry": "gpt-4o",
"blueprint_generation_model": "gpt-5-mini",
"workflow_generation_model": null,
"outcome_spec_generation_model": "claude-sonnet-4.6"
}Generation model fields are individual nullable project settings. null clears a project override and falls back to the corresponding global Generation:* setting (Generation:Model, then gpt-5.6-sol). They do not affect Console or normal project/run agent execution model selection. Response 204 No Content on success.
DELETE /api/projects/{id}
Deletes a caller-owned project record. Does not touch the working directory or git history. Active runs for the project are cancelled; each cancelled run emits a run.cancelled event on its stream.
Requires the query parameter confirm=true:
DELETE /api/projects/a1b2c3d4-...?confirm=trueWithout confirm=true the request returns 400 Bad Request. Response 204 No Content on success.
GET /api/projects/{id}/runs
Lists all runs for a project. Returns a JSON array. Each entry includes agent_name identifying which team member executed the run (null when the run was not started by a cast team member):
[
{
"workflow_run_id": "workflow-...",
"execution_id": "f36800fd-...",
"status": "merged",
"model_id": null,
"task": "add license headers",
"agent_name": "Aria",
"reviewed_by": "local-developer",
"started_at": "2026-06-07T21:09:45+00:00",
"ended_at": "2026-06-07T21:10:12+00:00",
"result": null,
"coordinator_status": null,
"coordinator_status_reason": null,
"archived_at": null
}
]POST /api/projects/{id}/runs
Deprecated direct project-run submission route. It returns 410 Gone; use POST /api/projects/{id}/orchestrations or backlog pickup instead.
Coordinator endpoints
The Coordinator agent can either start directly from a goal or draft a confirmable outcome spec for a goal and suspend at a confirmation gate. These endpoints are a thin HTTP layer over CoordinatorRunService; all orchestration lives in the service. A coordinator run is an ordinary run (agent_name: "Coordinator", no parent), so its events stream from GET /api/runs/{id}/stream and it is authorized from its persisted project like other project runs.
POST /api/projects/{id}/orchestrations
Starts a coordinator run for the project. The project's working directory, default branch, and authenticated caller are used as the run's repository path, originating branch, and submitting user. A deployment-wide BYOK provider is used when active. Otherwise, the run uses GitHub Copilot.
The project must have at least one dispatchable cast team member before an orchestration can start. The start path calls CoordinatorRosterGuard.EnsureDispatchableTeam before inserting the run (apps/Agentweaver.Api/Coordinator/CoordinatorRunService.cs:111, :125). A dispatchable member is active, has a role, and is not one of the platform-owned Scribe/Ralph/RAI/Build & Test roles (apps/Agentweaver.Api/Coordinator/CoordinatorRosterGuard.cs:54, apps/Agentweaver.Api/Coordinator/CoordinatorOrchestratorExecutor.cs:687, :750).
Request:
{
"goal": "Make the onboarding flow resumable across sessions",
"modelId": null,
"start_mode": "defineOutcome",
"auto_approve_tools": false,
"autopilot": false
}| Field | Type | Required | Notes |
|---|---|---|---|
goal | string | Yes | The user's prompt/outcome for the coordinator. |
modelId | string | No | Model override. Falls back to the project's GitHub Copilot default, then the role default. |
start_mode | "direct" or "defineOutcome" | No | Optional launch mode; define_outcome remains an accepted compatibility alias. Omit or use "defineOutcome" to preserve the current outcome-spec draft/confirm gate. Use "direct" to start coordinator planning/dispatch from goal without model-drafting or pausing to confirm an outcome; a confirmed prompt-backed spec is still persisted. Direct still enforces child tool approvals, assembly review, and merge gates. |
auto_approve_tools | bool | No | Auto-approve only repository-defined safe tools for the coordinator and its children. Currently this covers web_fetch and start_preview. Preview auto-approval bypasses only the human wait; invalid ports, exited/unreachable preview processes, sandbox ownership, and publication failures still fail normally. It does not bypass arbitrary shell, destructive, privileged, secret-bearing, or unrelated network approvals. Defaults to false. The legacy autoApproveTools alias remains accepted. |
autopilot | bool | No | Launch with Autopilot ON: auto-answers clarifying questions and, in defineOutcome mode, auto-confirms the Phase-1 outcome spec unattended (confirmedBy = the submitting user) instead of parking at awaiting_confirmation. Does NOT auto-grant tool approvals. Cascades to children. Defaults to false. |
The selected launch policy is persisted with the run and audited as run.approval_policy_selected, including a stable policySnapshotId. Retries reuse that immutable launch choice with explicit source-run lineage, and children receive an inherited immutable snapshot. Heartbeat-created runs still take their initial policy from pickup_auto_approve_tools and pickup_autopilot; the claim transaction snapshots the current persisted values onto the reserved run. Changing those project defaults affects the next won claim, but does not rewrite an existing run or its retry policy. Run status falls back to this row snapshot if activation has not yet written runtime option events.
Response 201 Created (with Location: /api/runs/{runId}):
{ "runId": "f36800fd-..." }400 Bad Request when id is not a valid project id or goal is missing. 404 Not Found when the project does not exist. 409 Conflict when the project has no dispatchable team:
{
"error": "no_team",
"message": "This project has no team. Cast a team before starting an orchestration."
}409 Conflict with error: "project_deleting" when the project is being deleted. 409 Conflict with error: "workspace_unavailable" when the working directory is not accessible. 422 Unprocessable Entity when the team roster exists but cannot be read:
{
"error": "invalid_team",
"message": "The project team roster could not be read. Fix the team before starting an orchestration."
}GET /api/runs/{id}/outcome-spec
Returns the current persisted outcome spec for a coordinator run. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise).
Response 200 OK:
{
"goal": "Make the onboarding flow resumable across sessions",
"desiredOutcome": "Users can leave and resume onboarding without losing progress",
"scope": "Onboarding wizard, session persistence",
"assumptions": "Existing session store can hold partial onboarding state",
"clarifyingQuestions": "Should resumption work across devices?",
"status": "awaiting_confirmation",
"confirmedBy": null
}| Field | Type | Notes |
|---|---|---|
goal | string | The submitted goal. |
desiredOutcome | string | The drafted desired outcome. |
scope | string | Drafted scope. |
assumptions | string | Drafted assumptions. |
clarifyingQuestions | string | Omitted when none were drafted. |
status | string | drafting, awaiting_confirmation, confirmed, or declined. |
confirmedBy | string | Set once confirmed; omitted otherwise. |
400 Bad Request when id is not a valid run id. 403 Forbidden when the caller does not own the run. 404 Not Found when the run or its outcome spec does not exist.
POST /api/runs/{id}/outcome-spec/confirm
Confirms the drafted outcome spec, resuming the suspended coordinator run. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise). No request body.
Response 200 OK with the current outcome spec (same shape as GET /api/runs/{id}/outcome-spec, or null if not yet readable).
400 Bad Request when id is not a valid run id. 403 Forbidden when the caller does not own the run. 404 Not Found when the run does not exist. 409 Conflict with error: "run_not_active" when no live coordinator run is registered for the id. 409 Conflict with error: "no_pending_gate" when the spec is not currently awaiting confirmation (for example, already confirmed).
POST /api/runs/{id}/outcome-spec/revise
Requests a revision of the drafted outcome spec. The coordinator re-drafts using the feedback and re-suspends at the gate. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise).
Request:
{ "feedback": "Tighten the scope to a single device for now" }| Field | Type | Required | Notes |
|---|---|---|---|
feedback | string | Yes | Revision guidance for the coordinator. |
Response 200 OK with the current outcome spec (same shape as GET /api/runs/{id}/outcome-spec, or null if not yet readable).
400 Bad Request when id is not a valid run id or feedback is missing. 403 Forbidden when the caller does not own the run. 404 Not Found when the run does not exist. 409 Conflict with error: "run_not_active" when no live coordinator run is registered for the id. 409 Conflict with error: "no_pending_gate" when the spec is not currently awaiting confirmation.
The orchestration lifecycle
Confirming the outcome spec advances the coordinator run through Phase 2: confirm -> decompose -> dispatch -> observe -> steer. After confirmation, the coordinator decomposes the spec into a work plan (subtasks plus dependency edges), dispatches the ready subtasks as child runs (independent subtasks in parallel, dependent ones serialized behind their prerequisites), observes each child's read-only timeline, and relays any steering direction to the running subagents. The work plan, child runs, and steering directives are read and driven through the endpoints below; the live graph streams as coordinator.work_plan, coordinator.topology, subtask.*, and coordinator.steering events on the coordinator run's own GET /api/runs/{id}/stream.
GET /api/runs/{coordinatorRunId}/work-plan
Returns the work plan for a coordinator run: the decomposed subtasks and the dependency edges between them. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise). Before asynchronous decomposition persists the plan, returns 404 Not Found with error: "work_plan_not_found"; for an existing coordinator run, clients should treat this as a not-ready state and retry on their normal bounded refresh cadence.
An exact coordinator run id selects its own plan, even when related child-work plans exist. Addressing a parent workflow run instead selects its latest persisted child-work node plan (highest work plan id with a parent workflow node), regardless of the older plan's status or the current plan's resume/review state. The response includes parentRunId, parentWorkflowId, parentWorkflowNodeId, parentJoinNodeId, parentResumeRequestId, parentResumeState, and, when available, joinedOutput. An active coordinator still creating its plan may return 404 with error: "work_plan_not_ready"; a terminal coordinator without a plan returns 200 with null.
Response 200 OK:
{
"workPlanId": "a1b2c3d4-...",
"coordinatorRunId": "f36800fd-...",
"outcomeSpecId": "9e8d7c6b-...",
"status": "dispatching",
"statusReason": null,
"subtasks": [
{
"subtaskId": 5,
"title": "Add session persistence to the onboarding store",
"scope": "Persist partial onboarding state",
"assignedAgent": "morpheus",
"selectedModelId": "gpt-4o",
"phase": "execution",
"isolation": "worktree",
"status": "running",
"childRunId": "7c1f..."
}
],
"dependencies": [
{ "subtaskId": 7, "dependsOnSubtaskId": 5 }
]
}| Field | Type | Notes |
|---|---|---|
workPlanId | string | Persisted work plan id. |
coordinatorRunId | string | The coordinator run that owns the plan. |
outcomeSpecId | string | The confirmed outcome spec the plan was decomposed from. |
status | string | planned, dispatching, awaiting_assembly, assembling, in_review, complete, or a parked/terminal state assembly_blocked / assembly_failed / assembly_declined. |
statusReason | string|null | Human-readable failure reason for a terminal plan, taken from the coordinator run's result (for example assembly_blocked: <reason>, assembly_merge_failed: <reason>, assembly_error: <message>). null while the plan is non-terminal. The UI can render "Failed: <statusReason>" without a second round-trip. |
subtasks | array | Decomposed units of work; each has subtaskId, title, scope, assignedAgent, selectedModelId, phase, isolation, status, and childRunId (null until dispatched). |
dependencies | array | { subtaskId, dependsOnSubtaskId } edges; a subtask dispatches only once every dependency reaches assemble_ready/completed. |
400 Bad Request when id is not a valid run id. 403 Forbidden when the caller does not own the run. 404 Not Found when the run does not exist or has no work plan yet.
GET /api/runs/{coordinatorRunId}/children
Lists the child runs dispatched by a coordinator run, one row per subtask that has a child run, each paired with its subtask status. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise). Empty array when nothing has been dispatched.
An exact coordinator run id selects its own plan; a parent workflow run selects the latest persisted child-work node plan by the same rule as /work-plan. Each child includes its parent workflow/node/join ids and branch node id/ordinal when applicable. The two independent endpoint reads are not an atomic snapshot.
Response 200 OK:
[
{
"subtaskId": 5,
"childRunId": "7c1f...",
"subtaskStatus": "running",
"assignedAgent": "morpheus",
"selectedModelId": "gpt-4o",
"childRunStatus": "in_progress",
"worktreeBranch": "coordinator/5-session-persistence",
"treeHash": null,
"stepCount": 12
}
]| Field | Type | Notes |
|---|---|---|
subtaskId | integer | The subtask this child run executes. |
childRunId | string | The dispatched child run id. |
subtaskStatus | string | The subtask's status in the work plan. |
assignedAgent | string | The roster agent running the subtask. |
selectedModelId | string | The model selected for the subtask. |
childRunStatus | string | The child run's own status. |
worktreeBranch | string | The child run's worktree branch. |
treeHash | string | The committed worktree tree hash once the child reaches assemble-ready; null before then. |
stepCount | integer | Steps observed on the child run so far. |
400 Bad Request when id is not a valid run id. 403 Forbidden when the caller does not own the run. 404 Not Found when the run does not exist.
POST /api/runs/{coordinatorRunId}/steer
Creates a steering directive that the coordinator relays to one or more running subagents. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise).
Request:
{
"kind": "redirect",
"targetChildRunId": "7c1f...",
"instruction": "Use the existing session store instead of adding a new table"
}| Field | Type | Required | Notes |
|---|---|---|---|
kind | string | Yes | stop, send, redirect, or amend. Pause is not supported in Phase 2. |
targetChildRunId | string | No | The child run to steer; omit to broadcast to every active child. At the assembly review gate (see below) this instead narrows the implicated-subtask scope. |
instruction | string | Yes | Direction relayed to the targeted subagent(s). Optional for send. |
Response 201 Created with the created directive:
{
"directiveId": "d4c3b2a1-...",
"kind": "redirect",
"targetChildRunId": "7c1f...",
"status": "queued",
"instruction": "Use the existing session store instead of adding a new table"
}A stop takes effect immediately: it cancels the targeted child run's in-flight turn. A redirect or amend takes effect at the targeted subagent's next turn boundary, without restarting the run — it is queued and applied when the child's current turn completes (or when it next suspends at a gate). The directive's progress is observable as coordinator.steering events (pending -> queued -> relayed -> applied, plus deferred at the review gate — see below) on the coordinator run stream.
Steering at the assembly review gate (#226). When the run is parked at the collective human-review gate (run.status == awaiting_review, coordinator_steerable == true), redirect/amend/send are intercepted and delivered to the parked assembly loop instead of the child-turn queue (previously they returned queued but were silently dropped):
redirect/amend→ delivered as a request-changes review decision through the same mechanism asPOST /assembly/reviewwithrequest_changes: true— the parked loop re-dispatches the implicated subtasks (#223file-scoped implication + transitive dependents) and unconditionally resets the steering budget. With no target files the scope defaults to all contributors; settargetChildRunIdto narrow to that subtask ∪ its co-touching subtasks. Settlesrelayed(ordeferred, below).send→ an advisory note on the coordinator timeline; the gate stays armed with no decision and no budget reset. Settlesapplied.
In all cases the directive reaches a definite terminal status and is never left silently queued. When the review gate is armed on a different API replica, the decision is durably persisted for the owning replica's poller to drain: the directive status is deferred and the endpoint returns 202 Accepted instead of 201 Created (mirroring the /assembly/review deferred response).
400 Bad Request when id is not a valid run id, kind is not one of stop/send/redirect/amend, or instruction is missing (required for redirect/amend). 403 Forbidden when the caller does not own the run. 404 Not Found when the run does not exist. 409 Conflict with error: "run_not_active" when no live coordinator run is registered for the id.
POST /api/runs/{coordinatorRunId}/assembly/review
The ONE collective human-review gate for Phase 3 collective assembly (Feature 008). After every child subtask finishes, the coordinator builds a single integration branch (all eligible child branches merged in dependency order off the originating branch), runs the selected workflow's automated gates over the aggregate diff, then suspends here for one human decision over the combined output of all agents. Mirrors POST /api/runs/{id}/review (run-authorized, at-most-once) but {id} is the coordinator run id, and the decision is delivered to the service-driven gate the collective pipeline is awaiting. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise).
In multi-replica deployments, the reviewer may submit this request to any API replica. If the receiving replica does not own the in-memory assembly pipeline but the durable work plan is still in_review at assembly stage review, the decision is stored as a deferred decision for the owner replica to pick up and apply to the armed gate. A duplicate submit while that deferred decision exists returns the same accepted response rather than replacing the original decision.
Request:
{
"approved": false,
"request_changes": true,
"feedback": "The change in src/auth/login.ts breaks logout",
"target_files": ["src/auth/login.ts"]
}| Field | Type | Required | Notes |
|---|---|---|---|
approved | bool | Yes | true continues to the ONE collective merge → ONE collective scribe → complete. |
request_changes | bool | No | When true (and approved is false), the coordinator re-dispatches the affected children rather than declining. |
feedback | string | No | Free-text reviewer feedback, handed to the revising agent(s). It is not parsed for file paths — use target_files for the implicated-file hint. |
target_files | string[] | No | Explicit list of the repo-relative files your changes should target. Used as the structured implicated-file hint: the coordinator reverse-maps it onto the subtasks that committed those files (AssemblyPlanning.ScopeImplicatedSubtasks), never prose-scraped from feedback. If omitted or unmatched, the re-dispatch falls back to all contributors. |
Decision routing
- Approve (
approved: true) → the pipeline merges the integration branch into the originating branch and runs the collective scribe, emittingcoordinator.assembly_merge_*,coordinator.assembly_scribe_*, thencoordinator.assembly_completed; the work plan reachescomplete. - Request changes (
approved: false,request_changes: true) → the coordinator scopes the re-dispatch to the subtasks that committed one of yourtarget_files(the implicated set) plus their transitive dependents, resets those subtasks topending(leaving the rest intact), returns the plan todispatching, and re-dispatches. Iftarget_filesis omitted or matches no subtask, it falls back to re-dispatching all children and emitscoordinator.assembly_implicated_scope_fallback. Emitscoordinator.assembly_changes_requested. Because a human request-changes is a supervised action, it also unconditionally resets the autonomous steering budget (there is no round-trip cap). - Decline (
approved: false,request_changes: false) → terminalassembly_declined; the coordinator emitscoordinator.assembly_declined(reason,reviewer), the work plan moves toassembly_declined, the run endsdeclined, and the coordinator stream closes.
When the pipeline arms this gate it emits coordinator.assembly_review_requested on the coordinator stream with integrationBranch, treeHash (the assembled integration tree hash), includedSubtaskIds (which subtasks the assembled output covers), raiSafetyFlagged, and hasChanges — the UI subscribes to this to know a collective human review is being requested and to render the assembled output. If the assembly background task hits an unexpected fault it emits coordinator.assembly_failed (reason, phase) and the run ends failed with result: "assembly_error: <message>".
Response 200 OK:
{ "runId": "f36800fd-...", "accepted": true }400 Bad Request when id is not a valid run id. 403 Forbidden when the caller does not own the run, or does not own the pending review request. 404 Not Found when the run does not exist. 409 Conflict with error: "no_assembly_review_pending" when no collective review is currently awaited for the run (the pipeline has not reached the gate yet, or the decision was already consumed and the work plan has left in_review).
GET /api/runs/{id}/assembly/files
Lists files in the coordinator assembly workspace. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise). Returns an empty array before assembly creates the integration branch; this is a normal planning/dispatch state.
GET /api/runs/{id}/assembly/files/{**path}
Returns diff/content metadata for a specific file in the assembly workspace. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise).
GET /api/runs/{id}/assembly/workspace
Returns the assembly workspace tree. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise).
GET /api/runs/{id}/assembly/content/{**path}
Returns raw file content from the assembly workspace. Run-authorized (Viewer for inspection; Contributor for mutation; legacy ownership otherwise).
Team casting endpoints
The team casting API manages the full lifecycle of AI-assisted agent team composition: listing available scenario groupings, creating and amending casting proposals, confirming a proposal into a live team, and committing the resulting .squad/ files back to the repository.
Model-assisted casting uses the accepted effective provider through GenerationModelProviderExecutor; it is not unconditionally Copilot-only. The request does not select a provider directly. Admission, the accepted BYOK configuration when applicable, and pre-call checks determine execution.
GET /api/casting/templates
Lists the available team templates (scenario groupings). Each template groups a curated set of agent roles suitable for a particular project type.
Response 200 OK:
[
{
"id": "quick-software-development",
"title": "Quick Software Development",
"description": "Lean team for rapid software delivery.",
"roles": [
{
"id": "software-engineer",
"title": "Software Engineer",
"summary": "Implements features and fixes bugs.",
"default_model": "gpt-4o"
}
]
}
]GET /api/projects/{id}/casting/universes
Lists allowed universe names for a project.
Response 200 OK:
{ "universes": ["star-wars", "marvel"] }GET /api/catalog/roles
Returns all available role definitions from the catalog. Use the id values when creating proposals in manual mode or adding individual members.
Response 200 OK: a JSON array of role objects, each with id, title, summary, and default_model.
POST /api/projects/{id}/casting/proposals
Creates a casting proposal. Depending on mode, the server selects roles deterministically from a template, runs a model-assisted analysis, or accepts an explicit role list.
Request:
{
"mode": "scenario",
"template_id": "quick-software-development",
"universe": "star-wars",
"team_size": null,
"model_id": null,
"goal": null,
"role_ids": null
}| Field | Type | Required | Notes |
|---|---|---|---|
mode | string | Yes | "scenario", "free_text", "analysis", or "manual" |
template_id | string | When mode is "scenario" | ID from GET /api/casting/templates |
goal | string | When mode is "free_text" | Natural-language description of the team goal |
role_ids | string[] | When mode is "manual" | Explicit list of role IDs from GET /api/catalog/roles |
universe | string | No | Thematic universe name applied to agent personas (e.g. "star-wars") |
team_size | int | No | Desired number of team members; guides model-assisted modes |
model_id | string | No | Model override for free_text and analysis modes |
For "free_text" and "analysis" modes the server runs a GitHub Copilot model to propose roles. All modes return the proposal synchronously once ready.
Response 200 OK — a CastProposalDto:
{
"proposal_id": "prop-a1b2c3",
"mode": "scenario",
"universe": "star-wars",
"run_id": null,
"existing_team_present": false,
"warnings": [],
"rationale": "A balanced team for rapid software delivery.",
"members": [
{
"proposed_name": "Han Solo",
"role": {
"id": "software-engineer",
"title": "Software Engineer",
"summary": "Implements features and fixes bugs.",
"default_model": "gpt-4o"
},
"charter_markdown": "# Han Solo\n...",
"is_named": true,
"default_model": "gpt-4o",
"justification": null
}
]
}run_id is populated for free_text and analysis modes. Use GET /api/runs/{id}/stream to follow the model run while the proposal is being generated; the proposal is ready when the run completes. run_id is null for scenario and manual modes, which resolve synchronously.
Error responses:
| Status | Error | Meaning |
|---|---|---|
400 | — | mode is invalid, or a required mode-specific field is missing |
404 | — | Project not found |
409 | project_unavailable | The project's working directory is not accessible |
409 | layout_conflict | Both canonical and legacy .squad/ layouts are present |
GET /api/projects/{id}/casting/proposals
Lists active proposals for the project. Response 200 OK is an array of CastProposalDto objects.
GET /api/projects/{id}/casting/proposals/{proposalId}
Returns the current state of a proposal.
Response 200 OK — a CastProposalDto (same shape as the POST response above).
Returns 404 Not Found when no proposal exists for the given id.
PATCH /api/projects/{id}/casting/proposals/{proposalId}
Amends a proposal by replacing its member list and/or universe. Use this to add, remove, or modify proposed members before confirming.
Request:
{
"universe": "marvel",
"members": [
{
"proposed_name": "Tony Stark",
"role": {
"id": "software-engineer",
"title": "Software Engineer",
"summary": "Implements features and fixes bugs.",
"default_model": "gpt-4o"
},
"charter_markdown": "# Tony Stark\n...",
"is_named": true,
"default_model": "gpt-4o",
"justification": null
}
]
}Both members and universe are optional; omit either to leave it unchanged.
Response 200 OK returns the updated CastProposalDto. Returns 404 Not Found when the proposal does not exist.
POST /api/projects/{id}/casting/proposals/{proposalId}/confirm
Confirms a proposal and materialises the team by writing .squad/ files. For projects with an existing team, the intent field controls how the proposed team relates to the existing one.
Request:
{
"intent": "new"
}| Field | Type | Required | Notes |
|---|---|---|---|
intent | string | Conditionally | Required when an existing team is detected. "new" replaces the team entirely; "augment" adds the proposed roles to the existing team; "recast" rewrites all existing charters using the proposed configuration. Omit when no existing team is present. |
Response 200 OK — a TeamDto (same shape as GET /api/projects/{id}/team):
{
"project_name": "my-project",
"universe": "star-wars",
"layout": "canonical",
"migration_available": false,
"members": [
{
"name": "Han Solo",
"role_title": "Software Engineer",
"charter_path": ".squad/HanSolo/charter.md",
"status": "active",
"default_model": "gpt-4o",
"is_named": true,
"charter_created_at": "2026-06-07T21:00:00+00:00",
"charter_updated_at": "2026-06-07T21:00:00+00:00"
}
]
}Notable error responses:
| Status | Error | Meaning |
|---|---|---|
404 | — | Proposal or project not found |
409 | requires_choice | An existing team was detected and intent was not provided |
409 | layout_conflict | Both canonical and legacy .squad/ layouts are present; resolve manually before confirming |
409 | project_unavailable | The project's working directory is not accessible |
DELETE /api/projects/{id}/casting/proposals/{proposalId}
Rejects a proposal. No .squad/ files are written or modified. Response 204 No Content.
GET /api/projects/{id}/team
Returns the current team roster and layout metadata.
Response 200 OK:
{
"project_name": "my-project",
"universe": "star-wars",
"layout": "canonical",
"migration_available": false,
"members": [
{
"name": "Han Solo",
"role_title": "Software Engineer",
"charter_path": ".squad/HanSolo/charter.md",
"status": "active",
"default_model": "gpt-4o",
"is_named": true,
"charter_created_at": "2026-06-07T21:00:00+00:00",
"charter_updated_at": "2026-06-07T21:05:00+00:00"
}
]
}| Field | Values | Notes |
|---|---|---|
layout | "canonical", "legacy", "conflict", "absent" | .squad/<Name>/ = canonical; .squad/casting/<Name>/ = legacy; both present = conflict |
migration_available | bool | true when a legacy layout exists and no canonical layout is present |
status | "active", "retired" | Member lifecycle state |
Returns 404 Not Found when no team exists for the project.
GET /api/projects/{id}/team/members/{name}/charter
Returns the charter for a team member as a JSON object.
Response 200 OK:
{
"member_name": "Han Solo",
"content": "# Han Solo\n\nYou are Han Solo, Software Engineer..."
}Returns 404 Not Found when the member does not exist or has no charter file.
PUT /api/projects/{id}/team/members/{name}/charter
Replaces the charter for a team member.
Request:
{
"content": "# Han Solo\n\nUpdated charter content..."
}Response 200 OK returns { "member_name": "...", "content": "..." }. Returns 404 Not Found when the member does not exist.
POST /api/projects/{id}/team/members
Adds a new member to the team. Creates the member's .squad/ directory and an initial charter file generated from the specified role.
Request:
{
"role_id": "software-engineer",
"custom_role_title": null,
"model_id": null
}| Field | Type | Required | Notes |
|---|---|---|---|
role_id | string | Yes | Role ID from GET /api/catalog/roles |
custom_role_title | string | No | Override the role's default title for this member |
model_id | string | No | Override the role's default model for this member |
Response 200 OK — a TeamMemberDto (same shape as members in GET /api/projects/{id}/team).
DELETE /api/projects/{id}/team/members/{name}
Retires a team member. Their .squad/ directory and charter file are preserved; the member's status is set to "retired".
Response 204 No Content. Returns 404 Not Found when the member does not exist.
PATCH /api/projects/{id}/team/members/{name}
Re-roles an existing member, regenerating their charter for the new role.
Request:
{
"new_role_id": "product-manager",
"custom_role_title": null
}| Field | Type | Required | Notes |
|---|---|---|---|
new_role_id | string | Yes | New role ID from GET /api/catalog/roles |
custom_role_title | string | No | Override the role's default title |
Response 200 OK — the updated TeamMemberDto. Returns 404 Not Found when the member does not exist.
GET /api/projects/{projectId}/team/sync
Returns the pending uncommitted changes in the project's .squad/ directory and a hash of the current change set.
Response 200 OK:
{
"changes": [
{ "path": ".squad/HanSolo/charter.md", "kind": "modified" }
],
"change_set_hash": "sha256:a1b2c3...",
"nothing_to_sync": false
}changes is an empty array and nothing_to_sync is true when there is nothing to commit. change_set_hash must be passed to POST /api/projects/{projectId}/team/sync to prevent stale commits.
POST /api/projects/{projectId}/team/sync
Commits the pending .squad/ changes to the project repository.
Request:
{
"expected_change_set_hash": "sha256:a1b2c3...",
"message": "Update Han Solo charter"
}| Field | Type | Required | Notes |
|---|---|---|---|
expected_change_set_hash | string | Yes | Hash from GET /api/projects/{projectId}/team/sync. The server rejects the commit if the change set has shifted since you fetched it. |
message | string | No | Commit message. A default message is used when omitted. |
Returns 409 Conflict with error: "sync_state_changed" when the change set hash does not match. Fetch a fresh hash from GET /api/projects/{projectId}/team/sync and retry.
Response 200 OK returns { "commit_id": "..." }.
Persistence
SQLite tables are created on startup with WAL enabled:
| Table | Purpose |
|---|---|
runs | Run records with status, timing, submitting user, task, model source, model id, project id, and the final result text |
projects | Project records with name, origin, working directory, default branch, owner, provider settings, and state |
github_tokens | Per-user GitHub tokens stored by the OS credential store (not a SQLite table — managed by OsCredentialStoreGitHubTokenStore) |
Run events persist through IRunEventStream; RunStreamStore also maintains local delivery state. /events supplies persisted events, /stream supplies streaming/replay, and /history is separate persisted session history. The final-result agent.message fallback is legacy compatibility for completed runs lacking event rows, not the normal restart contract.
Configuration keys
Core storage and git keys
| Key | Default | Purpose |
|---|---|---|
Database:Path | agentweaver.db in the app data directory | SQLite database file |
Worktrees:BasePath | worktrees in the app data directory | Root folder for run worktrees |
Git:Author:Name | Agentweaver | Author name for commits and merges |
Git:Author:Email | agentweaver@localhost | Author email for commits and merges |
RunBounds:MaxSteps | 50 | Maximum tool-call steps before run.bounded |
RunBounds:MaxMinutes | 10 | Maximum wall-clock duration in minutes |
Security keys
| Key | Default | Purpose |
|---|---|---|
Runs:AllowedRepositoryRoots | [] (permissive) | String array of allowed parent directories for repository_path. Symlinks and junctions in the submitted path are resolved and the final location must fall within one of these roots. When empty (the default), any valid local absolute path is accepted. Shared, exposed, or multi-tenant deployments MUST configure this. |
Authentication keys
| Key | Default | Purpose |
|---|---|---|
Auth:Keys | none | Array of { Token, User } API keys |
Auth:ApiKey | none | Single-key alternative |
Auth:User | none | User paired with Auth:ApiKey |
Provider keys
| Key | Default | Purpose |
|---|---|---|
Providers:GitHubCopilot:ApiKey | none | GitHub Copilot provider credential |
Providers:GitHubCopilot:Endpoint | https://api.githubcopilot.com | GitHub Copilot base URL |
Providers:GitHubCopilot:Model | claude-sonnet-4.6 | GitHub Copilot model name |
Providers:GitHubCopilot:RuntimeCliPath | "" (empty) | Optional explicit path to the native Copilot CLI binary; empty means use the SDK's auto-resolved runtime. Env fallbacks (in order): AGENTWEAVER_COPILOT_CLI_PATH, COPILOT_CLI_PATH. Grounded in packages/Agentweaver.AgentRuntime/Providers/GitHubCopilotClientFactory.cs:50. See Configuration. |
AGENTWEAVER_RUN_COMMAND_DEFAULT_TIMEOUT_SECONDS | 1800 | Environment override for the sandboxed run_command default execution budget. run_command is for finite commands; use start_preview_process for long-lived preview/dev servers. |
Generation:Model | gpt-5.6-sol | Global fallback for blueprint, workflow, and coordinator outcome-spec generation. |
Generation:BlueprintModel | Generation:Model | Optional global fallback when a project has no blueprint_generation_model. |
Generation:WorkflowModel | Generation:Model | Optional global fallback when a project has no workflow_generation_model. |
Generation:OutcomeSpecModel | Generation:Model | Optional global fallback when a project has no outcome_spec_generation_model. |
Work-plan status examples are not exhaustive: delegated, assembly_steering, rai_blocked and needs_resolution also exist. The prepared execution_key authorizes a matching operation/scope and is checked against caller, expiry and provider identity; a provider fingerprint is provenance, not authority. Accepted context is revalidated before model use, separately from run snapshots and capabilities.
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Postgres is the event relay |
| subtitle | Any API replica can serve a cursor over durable RunEvents—no sticky session required. |
| group-title0 | Write path · replica A |
| group-title1 | Read path · replica B |
| Run producer | Run producer |
| Run producer | Append a structured event |
| Run producer | runId + type + payload |
| EF event stream | EF event stream |
| EF event stream | Serialize writes per run |
| EF event stream | pg_advisory_xact_lock |
| RunEvents | RunEvents |
| RunEvents | Shared PostgreSQL table |
| RunEvents | (RunId, Sequence) |
| Web / MCP watcher | Web / MCP watcher |
| Web / MCP watcher | Consume ordered events |
| Web / MCP watcher | last delivered cursor |
| SSE endpoint | SSE endpoint |
| SSE endpoint | Emit id + event + data |
| SSE endpoint | ordered response frames |
| EF subscriber | EF subscriber |
| EF subscriber | Read Sequence > cursor |
| EF subscriber | idle poll: 250 ms |
| e1 | append |
| e2 | commit |
| e3 | ordered batch |
| e4 | yield |
| e5 | SSE frames |
| assurance-title | POSTGRES LANE ONLY |
| assurance-line1 | SQLite register-channel / replay / tail is a separate implementation—not this architecture. |
| assurance-line2 | Late-delta suppression is process-local; do not read it as a database-wide terminal fence. |
| Run producer | Input |
| Run producer | RunStreamEntry |
| Run producer | Identity |
| Run producer | runId + event type |
| Run producer | Body |
| Run producer | Structured payload |
| Run producer | Ack |
| Run producer | After durable commit |
| EF event stream | Lock |
| EF event stream | Per-run advisory lock |
| EF event stream | Next |
| EF event stream | MAX(Sequence) + 1 |
| EF event stream | Write |
| EF event stream | Save transaction |
| EF event stream | Commit |
| EF event stream | Before acknowledgement |
| RunEvents | Table |
| RunEvents | Key |
| RunEvents | RunId + Sequence |
| RunEvents | Order |
| RunEvents | Ascending sequence |
| RunEvents | Reuse |
| RunEvents | Same type / payload |
| Web / MCP watcher | Client |
| Web / MCP watcher | Web or MCP |
| Web / MCP watcher | Resume |
| Web / MCP watcher | Last delivered cursor |
| Web / MCP watcher | Replica |
| Web / MCP watcher | No sticky requirement |
| Web / MCP watcher | History |
| Web / MCP watcher | Durable ordered events |
| SSE endpoint | Frame |
| SSE endpoint | id + event + data |
| SSE endpoint | Cursor |
| SSE endpoint | Last-Event-ID |
| SSE endpoint | Delivery |
| SSE endpoint | Yield ordered events |
| SSE endpoint | Close |
| SSE endpoint | After batch is drained |
| EF subscriber | Query |
| EF subscriber | Sequence > cursor |
| EF subscriber | Idle |
| EF subscriber | Poll after 250 ms |
| EF subscriber | State |
| EF subscriber | Shared durable table |
| EF subscriber | Blocked |
| EF subscriber | Retryable: keep open |
| producer | Coordinator or run execution; Acknowledgement follows commit |
| append | Allocate MAX(Sequence) + 1; Save and commit transaction |
| store | Cross-replica ordered history; Explicit duplicates must match payload |
| client | Reconnect from the cursor; No local channel dependency |
| sse | Cursor advances after delivery; Drain batch before terminal close |
| reader | Query the shared durable table; Retryable assembly_blocked stays open |
| notes | POSTGRES LANE ONLY; SQLite register-channel / replay / tail is a separate implementation—not this architecture.; Late-delta suppression is process-local; do not read it as a database-wide terminal fence. |
| groups | Write path · replica A; Read path · replica B |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | One goal, one collective review |
| subtitle | Confirm intent, dispatch bounded work, then integrate and review the whole result. |
| group-title0 | Plan and execute |
| group-title1 | Integrate, review, finish |
| Confirm intent | Confirm intent |
| Confirm intent | Draft the OutcomeSpec |
| Confirm intent | human confirmation |
| Plan the work | Plan the work |
| Plan the work | Persist a WorkPlan DAG |
| Plan the work | subtasks + dependencies |
| Dispatch children | Dispatch children |
| Dispatch children | Run the eligible frontier |
| Dispatch children | per-child worktrees |
| Merge + Scribe | Merge + Scribe |
| Merge + Scribe | Approved integration path |
| Merge + Scribe | MergeWorktree → Scribe |
| Collective review | Collective review |
| Collective review | One human decision |
| Collective review | approve / revise / decline |
| Integrate + gates | Integrate + gates |
| Integrate + gates | Assemble child branches |
| Integrate + gates | configured checks / review |
| e1 | confirm |
| e2 | dispatch |
| e3 | settled work |
| e4 | request review |
| e5 | approve |
| assurance-title | DO NOT CONFUSE ASSEMBLY WITH PUBLICATION |
| assurance-line1 | The collective workflow reaches MergeWorktree and Scribe; this graphic does not promise PR creation. |
| assurance-line2 | A blocked assembly can be recovered. Review approval does not itself mark the run complete. |
| Confirm intent | Input |
| Confirm intent | Human goal |
| Confirm intent | Artifact |
| Confirm intent | OutcomeSpec |
| Confirm intent | Gate |
| Confirm intent | Confirm or revise |
| Confirm intent | Scope |
| Confirm intent | Explicit assumptions |
| Plan the work | Select |
| Plan the work | Workflow choice |
| Plan the work | WorkPlan DAG |
| Plan the work | Owners |
| Plan the work | Named subtasks |
| Plan the work | Store |
| Plan the work | Persist dependencies |
| Dispatch children | Ready |
| Dispatch children | Satisfied dependencies |
| Dispatch children | Files |
| Dispatch children | Child-owned worktree |
| Dispatch children | Observe |
| Dispatch children | Child status / results |
| Dispatch children | Failure |
| Dispatch children | Blocks dependents |
| Merge + Scribe | Merge |
| Merge + Scribe | Reviewed integration |
| Merge + Scribe | Then |
| Merge + Scribe | Collective Scribe |
| Merge + Scribe | Record |
| Merge + Scribe | Promote decisions |
| Merge + Scribe | Decline |
| Merge + Scribe | Skips Scribe |
| Collective review | Approve |
| Collective review | Proceed to merge |
| Collective review | Revise |
| Collective review | Steer / redispatch |
| Collective review | No Scribe path |
| Collective review | Blocked |
| Collective review | Recoverable state |
| Integrate + gates | Child branches |
| Integrate + gates | Target |
| Integrate + gates | Integration branch |
| Integrate + gates | Gates |
| Integrate + gates | Selected checks |
| Integrate + gates | Output |
| intent | Scope and assumptions are explicit; Revision reopens the intent gate |
| plan | Outcome-complete decomposition; Bounded work with named owners |
| dispatch | Observe child status and results; Failure / RAI blocks dependents |
| finish | Decline skips Scribe; No automatic PR claim here |
| review | Changes can redispatch work; Blocked is recoverable, not terminal |
| integrate | Collective—not per-child delivery; Merge failure may still run Scribe |
| notes | DO NOT CONFUSE ASSEMBLY WITH PUBLICATION; The collective workflow reaches MergeWorktree and Scribe; this graphic does not promise PR creation.; A blocked assembly can be recovered. Review approval does not itself mark the run complete. |
| groups | Plan and execute; Integrate, review, finish |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Preview readiness follows the public path |
| takeaway | Provision the route, then probe its exact HTTPS URL; object creation alone is not ready. |
| group-title0 | CONTROL: PROVISION + PROBE |
| group-title1 | GATEWAY DATA PATH |
| Preview API | Preview API |
| Preview API | Resolve bound SandboxClaim |
| Preview API | Patch run selector on pod |
| Preview API | Create Service + HTTPRoute |
| Preview API | State from cluster, not cache |
| Publication probe | Publication probe |
| Publication probe | Exact generated HTTPS URL |
| Publication probe | Wait for managed DNS |
| Publication probe | Check Gateway + application |
| Publication probe | Only then return ready |
| Browser preview | Browser preview |
| Browser preview | Open the returned URL |
| Browser preview | Run-scoped capability host |
| Browser preview | Keepalive via API |
| Browser preview | Iframe: no-referrer |
| Preview Gateway | Preview Gateway |
| Preview Gateway | Separate shared Gateway |
| Preview Gateway | HTTPS host match |
| Preview Gateway | HTTPRoute selects Service |
| Preview Gateway | Not API port-forward |
| ClusterIP Service | ClusterIP Service |
| ClusterIP Service | Per-preview target selector |
| ClusterIP Service | Service :80 → public port |
| ClusterIP Service | Routes to bound sandbox pod |
| ClusterIP Service | Allowed ports 3000–9000 |
| Sandbox preview app | Sandbox preview app |
| Sandbox preview app | AgentHost pod-local path |
| Sandbox preview app | Live preview: TCP forwarder |
| Sandbox preview app | 0.0.0.0 → loopback app |
| Sandbox preview app | Manual: chosen target port |
| relation-0 | 1 after create |
| relation-1 | 2 ready URL |
| relation-2 | 3 HTTPS probe |
| relation-3 | 4 HTTPS |
| relation-4 | 5 route |
| relation-5 | 6 public port |
| assurance | No API → pod TCP readiness probe. Publication failure rolls back; DNS convergence has a bounded retry window. |
| assurance-0-label | Public readiness |
| assurance-0-fact | Probe the exact generated HTTPS URL. |
| assurance-0-source | SandboxPreviewService.cs |
| assurance-1-label | Rollback on failure |
| assurance-1-fact | Unpublish failed preview resources. |
| assurance-1-source | SandboxPreviewPublicationTests.cs |
| assurance-2-label | Separate ingress |
| assurance-2-fact | DNS managed externally, not by API. |
| assurance-2-source | gateway-preview.yaml |
| n0 | Patch run selector on pod; Create Service + HTTPRoute |
| n1 | Wait for managed DNS; Check Gateway + application |
| n2 | Run-scoped capability host; Keepalive via API |
| n3 | HTTPS host match; HTTPRoute selects Service |
| n4 | Service :80 → public port; Routes to bound sandbox pod |
| n5 | Live preview: TCP forwarder; 0.0.0.0 → loopback app |
| groups | CONTROL: PROVISION + PROBE; GATEWAY DATA PATH |
Diagram details and constraints
| Element | Contract |
|---|---|
| title | Accept a provider before invoking it |
| takeaway | Signed admission context freezes execution choice; live capability checks remain separate. |
| group-title0 | PREPARE AND ACCEPT |
| group-title1 | RUN BOUNDARY AND LIVE FENCES |
| Prepare context | Prepare context |
| Prepare context | Resolve effective provider |
| Prepare context | Bind operation + project |
| Prepare context | Bind subject + provider key |
| Prepare context | Signed • expires in 5 min |
| Accept request | Accept request |
| Accept request | Re-resolve and compare |
| Accept request | Verify signature + expiry |
| Accept request | Reject mismatched context |
| Accept request | Replacement context on error |
| Accepted plan | Accepted plan |
| Accepted plan | One execution provider |
| Accepted plan | Freeze BYOK configuration |
| Accepted plan | Provider choice is immutable |
| Accepted plan | Copilot OR BYOK |
| Run snapshot | Run snapshot |
| Run snapshot | Private durable ownership |
| Run snapshot | Database owner → secret ref |
| Run snapshot | Secret store holds snapshot |
| Run snapshot | Child / retry inheritance |
| Invocation guard | Invocation guard |
| Invocation guard | Check accepted run boundary |
| Invocation guard | Match operation and provider |
| Invocation guard | Reject inconsistent execution |
| Invocation guard | No silent provider fallback |
| Capability fences | Capability fences |
| Capability fences | Separate live permission checks |
| Capability fences | Before / after mint or read |
| Capability fences | Reject revoked or changed grant |
| Capability fences | Snapshot is not a bypass |
| relation-0 | 1 signed context |
| relation-1 | 2 match |
| relation-2 | 3 capture |
| relation-3 | 4 load boundary |
| relation-4 | 5 Copilot capability |
| assurance | Mismatch rejects with replacement context. A frozen provider snapshot does not bypass live GitHub capability fences. |
| assurance-0-label | Prepared key |
| assurance-0-fact | Five minutes; operation / subject bound. |
| assurance-0-source | AiExecutionPlanService.cs |
| assurance-1-label | Private snapshot |
| assurance-1-fact | DB ownership points to secret storage. |
| assurance-1-source | RunModelProviderSnapshotStore.cs |
| assurance-2-label | Live capability |
| assurance-2-fact | Recheck before and after mint / read. |
| assurance-2-source | GitHubCapabilityBroker.cs |
| n0 | Bind operation + project; Bind subject + provider key |
| n1 | Verify signature + expiry; Reject mismatched context |
| n2 | Freeze BYOK configuration; Provider choice is immutable |
| n3 | Database owner → secret ref; Secret store holds snapshot |
| n4 | Match operation and provider; Reject inconsistent execution |
| n5 | Before / after mint or read; Reject revoked or changed grant |
| groups | PREPARE AND ACCEPT; RUN BOUNDARY AND LIVE FENCES |
