Skip to content

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, not timestamp

Authentication ​

Send the API key on API requests unless the endpoint is explicitly public (/, /health, /auth/entra/*, or /api/server/info):

http
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.

json
{
  "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.

MethodPathPurpose
GET/Health banner (Agentweaver API)
GET/api/runs/{id}Get current run state
POST/api/runs/{id}/archiveArchive a run
DELETE/api/runs/{id}Cancel (if active) and delete a run record
POST/api/runs/{id}/cancelCancel a run's live work but keep the record
GET/api/runs/{id}/streamStream ordered run events over SSE
GET/api/runs/{id}/eventsReturn persisted run events
POST/api/runs/{id}/reviewRecord an approve or decline decision
POST/api/runs/{id}/shell-approvalsApprove a pending destructive shell command
POST/api/runs/{id}/shell-denialsDeny a pending destructive shell command
GET/api/runs/{id}/historyReplay persisted session events for terminal runs
GET/api/runs/{id}/graphGet the workflow graph descriptor for rendering the run topology
POST/api/runs/{id}/commitCommit worktree changes and merge into originating branch
POST/api/runs/{id}/request-changesRequest a revision cycle: agent rewrites in place
POST/api/runs/{id}/retryRetry a failed run as a new linked run
GET/api/runs/{id}/workspaceList workspace files with change status and line counts
GET/api/runs/{id}/filesList changed files (flat, with filter)
GET/api/runs/{id}/files/{**path}Get diff or content for a specific file
POST/api/runs/{id}/tool-approvalsApprove a pending tool call
POST/api/runs/{id}/tool-denialsDeny a pending tool call
POST/api/runs/{id}/questions/{requestId}/answerAnswer a pending ask_question request
POST/api/runs/{id}/auto-approveToggle the per-run auto-approve-tools option
POST/api/runs/{id}/autopilotToggle the coordinator Autopilot option
POST/api/runs/{runId}/sandbox/port-forwardStart a sandbox pod port-forward
GET/api/runs/{runId}/sandbox/port-forwardList 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.

MethodPathPurpose
POST/api/assistant/runsStart a new session, optionally running an opening turn
GET/api/assistant/runsList the caller's own sessions, newest first
POST/api/assistant/runs/{id}/messagesSend the next message into an existing session

Projects ​

MethodPathPurpose
POST/api/projectsCreate a project (blank or from GitHub)
GET/api/server/infoGet server metadata, including the optional Repo App install URL when configured
GET/api/projectsList all projects
GET/api/projects/{id}Get a project by id
PATCH/api/projects/{id}Rename a project
PUT/api/projects/{id}/provider-settingsUpdate provider and model defaults
DELETE/api/projects/{id}Delete a project (record only; cancels active runs)
GET/api/projects/{id}/runsList runs for a project
POST/api/projects/{id}/runsDeprecated direct run submission; returns 410 Gone
POST/api/projects/{id}/orchestrationsStart 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.

MethodPathPurpose
GET/api/github/repository-selectionsList up to 200 bounded, metadata-only repositories available to the caller
POST/api/github/repository-selectionsVerify one selected browse result and mint a short-lived opaque selection code

GET returns:

json
{
  "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:

json
{ "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 ​

MethodPathPurpose
POST/api/projects/{id}/decisions/inboxSubmit a decision or learning to the inbox
GET/api/projects/{id}/decisions/inboxList inbox entries (?agent=, ?type=, ?status=)
POST/api/projects/{id}/decisions/inbox/{entryId}/mergeMerge a pending entry into decisions
POST/api/projects/{id}/decisions/inbox/{entryId}/promoteAlias for merge/promote
POST/api/projects/{id}/decisions/inbox/{entryId}/rejectReject a pending entry

Decisions ​

MethodPathPurpose
POST/api/projects/{id}/decisionsCreate a decision directly
GET/api/projects/{id}/decisionsList decisions (?type=, ?agent=)
GET/api/projects/{id}/decisions/{decisionId}Get a single decision
POST/api/projects/{id}/decisions/{decisionId}/approveApprove a legacy or pending-trust decision for compilation
PUT/api/projects/{id}/decisions/{decisionId}Update decision status/content

Agent Memory ​

MethodPathPurpose
POST/api/projects/{id}/agents/{name}/memoryAdd a memory entry for an agent
GET/api/projects/{id}/agents/{name}/memoryList 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}/promoteApprove memory for cross-agent compilation
GET/api/projects/{id}/memoryCross-agent memory search (?type=, ?tags=)

Sessions ​

MethodPathPurpose
POST/api/projects/{id}/sessionsStart a new session (auto-ends existing)
GET/api/projects/{id}/sessions/currentGet current open session
PUT/api/projects/{id}/sessions/currentUpdate focus, summary, or end session
GET/api/projects/{id}/sessionsList sessions
PATCH/api/projects/{id}/sessions/{sessionId}Update a specific session

Export / Import ​

MethodPathPurpose
POST/api/projects/{id}/memory/exportExport DB memory → .squad/ + .agentweaver/context/
POST/api/projects/{id}/memory/importImport .squad/decisions/inbox/*.md → DB

GitHub capability authorization ​

MethodPathPurpose
POST/api/auth/github/repo-app/authorizationsBegin an Entra-user-bound Repo App authorization; returns an authorization URL and opaque transaction ID
POST/api/auth/github/repo-app/authorizations/handoffBegin 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/callbackComplete 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/refreshRefresh the caller's Repo App authorization without changing its grant identity
DELETE/api/auth/github/repo-app/authorizationRevoke 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.

MethodPathPurpose
GET/api/casting/templatesList available scenario groupings (team templates)
GET/api/projects/{id}/casting/universesList allowed universe names
GET/api/catalog/rolesList all available role definitions
POST/api/projects/{id}/casting/proposalsCreate a casting proposal
GET/api/projects/{id}/casting/proposalsList 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}/confirmConfirm a proposal and create the team
DELETE/api/projects/{id}/casting/proposals/{proposalId}Reject a proposal
GET/api/projects/{id}/teamGet team roster and layout metadata
GET/api/projects/{id}/team/members/{name}/charterGet a member's charter
PUT/api/projects/{id}/team/members/{name}/charterReplace a member's charter
GET/api/projects/{id}/team/members/{name}/historyGet agent interaction history
POST/api/projects/{id}/team/membersAdd 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/syncGet pending .squad/ changes and change set hash
POST/api/projects/{projectId}/team/syncCommit 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 ​

MethodPathPurpose
GET/api/blueprintsList predefined blueprints
POST/api/blueprints/generateAccept a durable Blueprint-generation job
GET/api/blueprints/generation-jobs/{jobId}Read authorized generation status
GET/api/blueprints/generation-jobs/{jobId}/resultRead the immutable generated Blueprint artifact
POST/api/blueprints/generation-jobs/{jobId}/cancelCancel queued or running generation
POST/api/blueprints/generation-jobs/{jobId}/retryRetry failed or cancelled generation
POST/api/blueprints/suggestAnalyze a GitHub repository and recommend a catalog blueprint
POST/api/blueprints/validateValidate 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.

MethodPathPurpose
GET/api/projects/{id}/workspace/filesList project workspace files for decomposition
POST/api/projects/{id}/backlog/decomposeDecompose workspace files into backlog tasks
POST/api/projects/{projectId}/backlog/tasksCreate 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}/readyMove a task to ready
POST/api/projects/{projectId}/backlog/ready-allMove all eligible tasks to ready
POST/api/projects/{projectId}/backlog/tasks/{taskId}/backlogMove a task back to backlog
POST/api/projects/{projectId}/backlog/tasks/{taskId}/reorderReorder a backlog task
POST/api/projects/{projectId}/backlog/tasks/{taskId}/archiveArchive a backlog task
GET/api/projects/{projectId}/boardGet the board state
GET/api/projects/{projectId}/workflow-stagesGet workflow stages
GET/api/projects/{projectId}/backlog/settingsGet backlog pickup settings
PUT/api/projects/{projectId}/backlog/settingsUpdate backlog pickup settings
GET/api/projects/{projectId}/review-policiesList review policies
POST/api/projects/{projectId}/review-policies/syncReload review policies from disk
GET/api/projects/{projectId}/workflowsList workflows, including structured trigger metadata
GET/api/projects/{projectId}/workflows/{workflowId}Get one workflow, including all triggers
GET/api/projects/{projectId}/workflows/{workflowId}/triggerGet the workflow's trigger configs as structured JSON
PUT/api/projects/{projectId}/workflows/{workflowId}/triggerCreate/replace one trigger by type
PATCH/api/projects/{projectId}/workflows/{workflowId}/triggerPartially update one trigger by type
DELETE/api/projects/{projectId}/workflows/{workflowId}/triggerClear all triggers, or one type with ?type=
POST/api/projects/{projectId}/workflow-eventsFire 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.

MethodPathPurpose
POST/api/github/webhooks/repo-appReceive 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 } where state is approved, changes_requested, or commented
  • ref: { branch, matchMode } where matchMode is equals or prefix
  • category: { name }
  • commentMatches: { pattern }

Example trigger payload:

json
{
  "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.

http
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": [...] }.
  • PUT accepts a WorkflowTriggerDto and upserts that trigger type without removing other types.
  • PATCH partially updates the trigger named by type. When the workflow has multiple triggers, type is required; a single-trigger workflow retains the legacy type-optional behavior.
  • DELETE ?type=schedule or ?type=event removes only that type.
  • DELETE without a type preserves 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, and time_of_day;
  • nested predicate objects use camelCase keys such as hasLabel, baseBranch, commentMatches, and matchMode.

Schedule trigger body ​

json
{
  "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 ​

json
{
  "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:

PredicateJSON shapeSupported 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 ​

MethodPathPurpose
GET/healthPublic liveness probe
GET/api/healthAPI liveness probe
GET/api/diagnosticsGet API diagnostics (SQLite/disk/workflow/heartbeat)
GET/api/diagnostics/clusterGet cluster diagnostics (pods, quota, component health, pending runs)
GET/api/diagnostics/heartbeatGet diagnostics heartbeat
GET/api/projects/{id}/diagnosticsGet project diagnostics
GET/api/projects/{id}/workspace/refsList workspace refs
GET/api/projects/{id}/workspaceList project workspace files
GET/api/projects/{id}/workspace/files/{**path}Read a project workspace file
GET/api/projects/{id}/dashboardGet project dashboard summary plus compatibility throughput / leaderboard fields
GET/api/projects/{id}/metricsGet App Insights-backed throughput and leaderboard widgets
GET/api/overviewGet global overview metrics
GET/api/runs/{id}/token-breakdownGet per-agent token and AI-credit data for a run
GET/api/metrics/runs/{runId}/tracesGet 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 nameWhat it tests
postgresqlPostgres connectivity
key_vaultAzure 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_quotaEffective admission headroom in the sandbox namespace, computed from the tighter of the pods and SandboxClaim object quotas.
warm_poolWarm-pool agent-sandbox availability
kubernetes_apiKubernetes API server reachability

Response 200 OK — a ClusterDiagnosticsDto:

json
{
  "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 }
  ]
}
FieldTypeNotes
checksDetailedHealthCheckDto[]One entry per check. Its status is healthy, warning, critical, or unknown.
active_agent_podsAgentPodInfoDto[]Pods currently running with a matching active run.
orphaned_agent_podsAgentPodInfoDto[]Pods running with no matching active run (candidates for next reaper sweep).
pending_capacity_runsPendingCapacityRunDto[]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:

json
{
  "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:

json
{
  "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:

json
{ "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.started marker with the run's matching runId, agentName: "Operator", and kind: "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:

json
{ "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:

json
{ "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:

text
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-stream
  • Cache-Control: no-cache
  • Connection: 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:

json
{ "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 for review.approved followed by either merge.completed or merge.failed to learn the outcome.
  • Decline — 200 OK, status: "declined". The workflow terminates; review.declined is emitted on the stream.
json
{ "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-merged run returns 200 OK with status: "merged" and the stored merge_result.
  • Re-declining an already-declined run returns 200 OK with status: "declined".

Error responses

StatusCondition
403 ForbiddenThe caller does not own the run
404 Not FoundNo run found for the given id
409 ConflictThe 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:

json
{ "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, status merged. The run's worktree branch is merged into the originating branch. merge_result is merged:{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, status awaiting_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, status merge_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_result is conflict:{reason}.

  • Decline — 200 OK, status declined, merge_result: null.

json
{ "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:

json
{ "command_hash": "sha256:..." }

Response 200 OK { "run_id", "command_hash", "approved": true }.

POST /api/runs/{id}/shell-denials ​

Denies a pending shell command.

Request:

json
{ "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:

json
{
  "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 in workflow.step events). 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: true when 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_ref is run:{childRunId} so the client can expand the child's own graph via GET /api/runs/{childRunId}/graph; it is null until 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.
json
{
  "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 success
  • 200 OK { run_id, status: "merge_failed", merge_result: "conflict:{reason}", conflicting_files: [...] } on conflict
  • 409 Conflict { error, status: "awaiting_review" } on retriable block (dirty working tree, concurrent request, etc.)
  • 409 Conflict { error } if the run is not in awaiting_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:

json
{ "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:

json
{ "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:

json
{ "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:

json
{ "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.

StatusApproval result
200 OKThe request is terminal (approved, denied, or expired)
404 Not Foundstate: "unknown" after owning-run resolution and any pod forward, or the run does not exist
409 Conflictstate: "pending" and the decision should be retried, or the run is not active
503 Service Unavailablestate: "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:

json
{ "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:

json
{ "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:

json
{ "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:

json
{ "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:

json
{
  "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:

json
{
  "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:

json
{
  "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:

json
{ "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:

json
{
  "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:

json
{ "target_port": 3000 }

Response 200 OK:

json
{
  "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:

json
{ "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:

ParameterRequiredDescription
repository_pathYesAbsolute path to the repository

Response 200 OK:

json
{
  "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):

json
{
  "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.

FieldTypeDefaultNotes
repository_pathstring—Required. Lookup key. Must be an absolute path.
shell_enabledbooltrueWhen false, run_command is excluded from the model's tool list for this project and denied by the governance gate.
allowed_repository_rootsstring[][]Additional paths mounted read-only inside the sandbox.
destructive_command_patternsstring[]see defaultCommand substrings that trigger a shell.approval_required pause.
require_approval_for_all_shellboolfalseWhen true, every shell command requires approval regardless of pattern matching.
redact_piibooltrueWhen true, emails and IP addresses are removed from command output in addition to secrets.
max_output_bytesint4194304Output 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:

json
{
  "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:

json
{ "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:

json
{
  "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:

json
{
  "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:

json
{ "blueprint": { "...": "BlueprintDto" } }

Response 200 OK:

json
{ "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.

json
{
  "backend": "processcontainer",
  "is_real_isolation": true,
  "reason": "processcontainer supported"
}
FieldTypeNotes
backendstringOne of processcontainer, wsl-lxc, lxc-native-linux, passthrough-deny
is_real_isolationbooltrue when the backend provides real process isolation. false for passthrough-deny. Shell execution is denied when false.
reasonstringHuman-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.

json
{
  "category": "network-unrestricted",
  "message": "Sandbox running with unrestricted network on Windows (allowlist enforcement unavailable). Data exfiltration surface is open.",
  "backend": "processcontainer"
}
FieldTypeNotes
categorystringCurrently only network-unrestricted — the Windows AppContainer backend cannot enforce a network allowlist
messagestringHuman-readable description
backendstringThe 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.

json
{
  "request_id": "apr-f36800fd",
  "command_length": 42,
  "command_hash": "sha256:a1b2c3...",
  "message": "Command matches destructive pattern 'rm -rf'. Approve to proceed."
}
FieldTypeNotes
request_idstringUnique ID for this approval request. Used by the (pending) approval endpoint.
command_lengthintLength of the command line in characters
command_hashstringSHA-256 of the command line, prefixed with sha256:
messagestringHuman-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.

http
POST /api/runs/{id}/shell-approvals
Content-Type: application/json

{ "command_hash": "a1b2c3d4e5f6a1b2" }

Response 200 OK:

json
{ "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.

json
{
  "stream": "stdout",
  "data": "Hello from sandbox\n"
}
FieldTypeNotes
streamstring"stdout" or "stderr"
datastringA 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.

json
{
  "exit_code": 0,
  "timed_out": false,
  "output_truncated": false
}
FieldTypeNotes
exit_codeintProcess exit code. -1 when the command timed out or was denied.
timed_outbooltrue when the command was terminated because it exceeded the configured time limit
output_truncatedbooltrue 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:

json
{
  "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.

FieldTypeRequiredNotes
namestringYesDisplay name
originstringYes"blank" or "github"
working_directorystringYesAbsolute local path for the project
repository_selection_codestringWhen origin is "github"Short-lived opaque selection code from POST /api/github/repository-selections
default_providerstringNo"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_copilotstringNoModel name override for the GitHub Copilot provider
default_model_microsoft_foundrystringNoModel name override for the BYOK provider. The legacy field name remains supported.
blueprint_idstringNoPredefined blueprint id from GET /api/blueprints. Mutually exclusive with blueprint.
blueprintobjectNoInline BlueprintDto, including optional bespoke_roles. Mutually exclusive with blueprint_id.
generated_workflow_yamlstringNoCustom workflow YAML returned by POST /api/blueprints/generate; materialized before applying the blueprint.

Response 201 Created returns a project object:

json
{
  "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:

json
{ "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:

json
{ "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:

json
{
  "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=true

Without 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):

json
[
  {
    "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:

json
{
  "goal": "Make the onboarding flow resumable across sessions",
  "modelId": null,
  "start_mode": "defineOutcome",
  "auto_approve_tools": false,
  "autopilot": false
}
FieldTypeRequiredNotes
goalstringYesThe user's prompt/outcome for the coordinator.
modelIdstringNoModel override. Falls back to the project's GitHub Copilot default, then the role default.
start_mode"direct" or "defineOutcome"NoOptional 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_toolsboolNoAuto-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.
autopilotboolNoLaunch 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}):

json
{ "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:

json
{
  "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:

json
{
  "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:

json
{
  "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
}
FieldTypeNotes
goalstringThe submitted goal.
desiredOutcomestringThe drafted desired outcome.
scopestringDrafted scope.
assumptionsstringDrafted assumptions.
clarifyingQuestionsstringOmitted when none were drafted.
statusstringdrafting, awaiting_confirmation, confirmed, or declined.
confirmedBystringSet 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:

json
{ "feedback": "Tighten the scope to a single device for now" }
FieldTypeRequiredNotes
feedbackstringYesRevision 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:

json
{
  "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 }
  ]
}
FieldTypeNotes
workPlanIdstringPersisted work plan id.
coordinatorRunIdstringThe coordinator run that owns the plan.
outcomeSpecIdstringThe confirmed outcome spec the plan was decomposed from.
statusstringplanned, dispatching, awaiting_assembly, assembling, in_review, complete, or a parked/terminal state assembly_blocked / assembly_failed / assembly_declined.
statusReasonstring|nullHuman-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.
subtasksarrayDecomposed units of work; each has subtaskId, title, scope, assignedAgent, selectedModelId, phase, isolation, status, and childRunId (null until dispatched).
dependenciesarray{ 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:

json
[
  {
    "subtaskId": 5,
    "childRunId": "7c1f...",
    "subtaskStatus": "running",
    "assignedAgent": "morpheus",
    "selectedModelId": "gpt-4o",
    "childRunStatus": "in_progress",
    "worktreeBranch": "coordinator/5-session-persistence",
    "treeHash": null,
    "stepCount": 12
  }
]
FieldTypeNotes
subtaskIdintegerThe subtask this child run executes.
childRunIdstringThe dispatched child run id.
subtaskStatusstringThe subtask's status in the work plan.
assignedAgentstringThe roster agent running the subtask.
selectedModelIdstringThe model selected for the subtask.
childRunStatusstringThe child run's own status.
worktreeBranchstringThe child run's worktree branch.
treeHashstringThe committed worktree tree hash once the child reaches assemble-ready; null before then.
stepCountintegerSteps 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:

json
{
  "kind": "redirect",
  "targetChildRunId": "7c1f...",
  "instruction": "Use the existing session store instead of adding a new table"
}
FieldTypeRequiredNotes
kindstringYesstop, send, redirect, or amend. Pause is not supported in Phase 2.
targetChildRunIdstringNoThe 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.
instructionstringYesDirection relayed to the targeted subagent(s). Optional for send.

Response 201 Created with the created directive:

json
{
  "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 as POST /assembly/review with request_changes: true — the parked loop re-dispatches the implicated subtasks (#223 file-scoped implication + transitive dependents) and unconditionally resets the steering budget. With no target files the scope defaults to all contributors; set targetChildRunId to narrow to that subtask ∪ its co-touching subtasks. Settles relayed (or deferred, below).
  • send → an advisory note on the coordinator timeline; the gate stays armed with no decision and no budget reset. Settles applied.

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:

json
{
  "approved": false,
  "request_changes": true,
  "feedback": "The change in src/auth/login.ts breaks logout",
  "target_files": ["src/auth/login.ts"]
}
FieldTypeRequiredNotes
approvedboolYestrue continues to the ONE collective merge → ONE collective scribe → complete.
request_changesboolNoWhen true (and approved is false), the coordinator re-dispatches the affected children rather than declining.
feedbackstringNoFree-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_filesstring[]NoExplicit 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, emitting coordinator.assembly_merge_*, coordinator.assembly_scribe_*, then coordinator.assembly_completed; the work plan reaches complete.
  • Request changes (approved: false, request_changes: true) → the coordinator scopes the re-dispatch to the subtasks that committed one of your target_files (the implicated set) plus their transitive dependents, resets those subtasks to pending (leaving the rest intact), returns the plan to dispatching, and re-dispatches. If target_files is omitted or matches no subtask, it falls back to re-dispatching all children and emits coordinator.assembly_implicated_scope_fallback. Emits coordinator.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) → terminal assembly_declined; the coordinator emits coordinator.assembly_declined (reason, reviewer), the work plan moves to assembly_declined, the run ends declined, 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: &lt;message&gt;".

Response 200 OK:

json
{ "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:

json
[
  {
    "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:

json
{ "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:

json
{
  "mode": "scenario",
  "template_id": "quick-software-development",
  "universe": "star-wars",
  "team_size": null,
  "model_id": null,
  "goal": null,
  "role_ids": null
}
FieldTypeRequiredNotes
modestringYes"scenario", "free_text", "analysis", or "manual"
template_idstringWhen mode is "scenario"ID from GET /api/casting/templates
goalstringWhen mode is "free_text"Natural-language description of the team goal
role_idsstring[]When mode is "manual"Explicit list of role IDs from GET /api/catalog/roles
universestringNoThematic universe name applied to agent personas (e.g. "star-wars")
team_sizeintNoDesired number of team members; guides model-assisted modes
model_idstringNoModel 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:

json
{
  "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:

StatusErrorMeaning
400—mode is invalid, or a required mode-specific field is missing
404—Project not found
409project_unavailableThe project's working directory is not accessible
409layout_conflictBoth 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:

json
{
  "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:

json
{
  "intent": "new"
}
FieldTypeRequiredNotes
intentstringConditionallyRequired 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):

json
{
  "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:

StatusErrorMeaning
404—Proposal or project not found
409requires_choiceAn existing team was detected and intent was not provided
409layout_conflictBoth canonical and legacy .squad/ layouts are present; resolve manually before confirming
409project_unavailableThe 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:

json
{
  "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"
    }
  ]
}
FieldValuesNotes
layout"canonical", "legacy", "conflict", "absent".squad/<Name>/ = canonical; .squad/casting/<Name>/ = legacy; both present = conflict
migration_availablebooltrue 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:

json
{
  "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:

json
{
  "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:

json
{
  "role_id": "software-engineer",
  "custom_role_title": null,
  "model_id": null
}
FieldTypeRequiredNotes
role_idstringYesRole ID from GET /api/catalog/roles
custom_role_titlestringNoOverride the role's default title for this member
model_idstringNoOverride 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:

json
{
  "new_role_id": "product-manager",
  "custom_role_title": null
}
FieldTypeRequiredNotes
new_role_idstringYesNew role ID from GET /api/catalog/roles
custom_role_titlestringNoOverride 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:

json
{
  "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:

json
{
  "expected_change_set_hash": "sha256:a1b2c3...",
  "message": "Update Han Solo charter"
}
FieldTypeRequiredNotes
expected_change_set_hashstringYesHash from GET /api/projects/{projectId}/team/sync. The server rejects the commit if the change set has shifted since you fetched it.
messagestringNoCommit 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:

TablePurpose
runsRun records with status, timing, submitting user, task, model source, model id, project id, and the final result text
projectsProject records with name, origin, working directory, default branch, owner, provider settings, and state
github_tokensPer-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 ​

KeyDefaultPurpose
Database:Pathagentweaver.db in the app data directorySQLite database file
Worktrees:BasePathworktrees in the app data directoryRoot folder for run worktrees
Git:Author:NameAgentweaverAuthor name for commits and merges
Git:Author:Emailagentweaver@localhostAuthor email for commits and merges
RunBounds:MaxSteps50Maximum tool-call steps before run.bounded
RunBounds:MaxMinutes10Maximum wall-clock duration in minutes

Security keys ​

KeyDefaultPurpose
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 ​

KeyDefaultPurpose
Auth:KeysnoneArray of { Token, User } API keys
Auth:ApiKeynoneSingle-key alternative
Auth:UsernoneUser paired with Auth:ApiKey

Provider keys ​

KeyDefaultPurpose
Providers:GitHubCopilot:ApiKeynoneGitHub Copilot provider credential
Providers:GitHubCopilot:Endpointhttps://api.githubcopilot.comGitHub Copilot base URL
Providers:GitHubCopilot:Modelclaude-sonnet-4.6GitHub 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_SECONDS1800Environment 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:Modelgpt-5.6-solGlobal fallback for blueprint, workflow, and coordinator outcome-spec generation.
Generation:BlueprintModelGeneration:ModelOptional global fallback when a project has no blueprint_generation_model.
Generation:WorkflowModelGeneration:ModelOptional global fallback when a project has no workflow_generation_model.
Generation:OutcomeSpecModelGeneration:ModelOptional 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
ElementContract
titlePostgres is the event relay
subtitleAny API replica can serve a cursor over durable RunEvents—no sticky session required.
group-title0Write path · replica A
group-title1Read path · replica B
Run producerRun producer
Run producerAppend a structured event
Run producerrunId + type + payload
EF event streamEF event stream
EF event streamSerialize writes per run
EF event streampg_advisory_xact_lock
RunEventsRunEvents
RunEventsShared PostgreSQL table
RunEvents(RunId, Sequence)
Web / MCP watcherWeb / MCP watcher
Web / MCP watcherConsume ordered events
Web / MCP watcherlast delivered cursor
SSE endpointSSE endpoint
SSE endpointEmit id + event + data
SSE endpointordered response frames
EF subscriberEF subscriber
EF subscriberRead Sequence > cursor
EF subscriberidle poll: 250 ms
e1append
e2commit
e3ordered batch
e4yield
e5SSE frames
assurance-titlePOSTGRES LANE ONLY
assurance-line1SQLite register-channel / replay / tail is a separate implementation—not this architecture.
assurance-line2Late-delta suppression is process-local; do not read it as a database-wide terminal fence.
Run producerInput
Run producerRunStreamEntry
Run producerIdentity
Run producerrunId + event type
Run producerBody
Run producerStructured payload
Run producerAck
Run producerAfter durable commit
EF event streamLock
EF event streamPer-run advisory lock
EF event streamNext
EF event streamMAX(Sequence) + 1
EF event streamWrite
EF event streamSave transaction
EF event streamCommit
EF event streamBefore acknowledgement
RunEventsTable
RunEventsKey
RunEventsRunId + Sequence
RunEventsOrder
RunEventsAscending sequence
RunEventsReuse
RunEventsSame type / payload
Web / MCP watcherClient
Web / MCP watcherWeb or MCP
Web / MCP watcherResume
Web / MCP watcherLast delivered cursor
Web / MCP watcherReplica
Web / MCP watcherNo sticky requirement
Web / MCP watcherHistory
Web / MCP watcherDurable ordered events
SSE endpointFrame
SSE endpointid + event + data
SSE endpointCursor
SSE endpointLast-Event-ID
SSE endpointDelivery
SSE endpointYield ordered events
SSE endpointClose
SSE endpointAfter batch is drained
EF subscriberQuery
EF subscriberSequence > cursor
EF subscriberIdle
EF subscriberPoll after 250 ms
EF subscriberState
EF subscriberShared durable table
EF subscriberBlocked
EF subscriberRetryable: keep open
producerCoordinator or run execution; Acknowledgement follows commit
appendAllocate MAX(Sequence) + 1; Save and commit transaction
storeCross-replica ordered history; Explicit duplicates must match payload
clientReconnect from the cursor; No local channel dependency
sseCursor advances after delivery; Drain batch before terminal close
readerQuery the shared durable table; Retryable assembly_blocked stays open
notesPOSTGRES 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.
groupsWrite path · replica A; Read path · replica B
Diagram details and constraints
ElementContract
titleOne goal, one collective review
subtitleConfirm intent, dispatch bounded work, then integrate and review the whole result.
group-title0Plan and execute
group-title1Integrate, review, finish
Confirm intentConfirm intent
Confirm intentDraft the OutcomeSpec
Confirm intenthuman confirmation
Plan the workPlan the work
Plan the workPersist a WorkPlan DAG
Plan the worksubtasks + dependencies
Dispatch childrenDispatch children
Dispatch childrenRun the eligible frontier
Dispatch childrenper-child worktrees
Merge + ScribeMerge + Scribe
Merge + ScribeApproved integration path
Merge + ScribeMergeWorktree → Scribe
Collective reviewCollective review
Collective reviewOne human decision
Collective reviewapprove / revise / decline
Integrate + gatesIntegrate + gates
Integrate + gatesAssemble child branches
Integrate + gatesconfigured checks / review
e1confirm
e2dispatch
e3settled work
e4request review
e5approve
assurance-titleDO NOT CONFUSE ASSEMBLY WITH PUBLICATION
assurance-line1The collective workflow reaches MergeWorktree and Scribe; this graphic does not promise PR creation.
assurance-line2A blocked assembly can be recovered. Review approval does not itself mark the run complete.
Confirm intentInput
Confirm intentHuman goal
Confirm intentArtifact
Confirm intentOutcomeSpec
Confirm intentGate
Confirm intentConfirm or revise
Confirm intentScope
Confirm intentExplicit assumptions
Plan the workSelect
Plan the workWorkflow choice
Plan the workWorkPlan DAG
Plan the workOwners
Plan the workNamed subtasks
Plan the workStore
Plan the workPersist dependencies
Dispatch childrenReady
Dispatch childrenSatisfied dependencies
Dispatch childrenFiles
Dispatch childrenChild-owned worktree
Dispatch childrenObserve
Dispatch childrenChild status / results
Dispatch childrenFailure
Dispatch childrenBlocks dependents
Merge + ScribeMerge
Merge + ScribeReviewed integration
Merge + ScribeThen
Merge + ScribeCollective Scribe
Merge + ScribeRecord
Merge + ScribePromote decisions
Merge + ScribeDecline
Merge + ScribeSkips Scribe
Collective reviewApprove
Collective reviewProceed to merge
Collective reviewRevise
Collective reviewSteer / redispatch
Collective reviewNo Scribe path
Collective reviewBlocked
Collective reviewRecoverable state
Integrate + gatesChild branches
Integrate + gatesTarget
Integrate + gatesIntegration branch
Integrate + gatesGates
Integrate + gatesSelected checks
Integrate + gatesOutput
intentScope and assumptions are explicit; Revision reopens the intent gate
planOutcome-complete decomposition; Bounded work with named owners
dispatchObserve child status and results; Failure / RAI blocks dependents
finishDecline skips Scribe; No automatic PR claim here
reviewChanges can redispatch work; Blocked is recoverable, not terminal
integrateCollective—not per-child delivery; Merge failure may still run Scribe
notesDO 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.
groupsPlan and execute; Integrate, review, finish
Diagram details and constraints
ElementContract
titlePreview readiness follows the public path
takeawayProvision the route, then probe its exact HTTPS URL; object creation alone is not ready.
group-title0CONTROL: PROVISION + PROBE
group-title1GATEWAY DATA PATH
Preview APIPreview API
Preview APIResolve bound SandboxClaim
Preview APIPatch run selector on pod
Preview APICreate Service + HTTPRoute
Preview APIState from cluster, not cache
Publication probePublication probe
Publication probeExact generated HTTPS URL
Publication probeWait for managed DNS
Publication probeCheck Gateway + application
Publication probeOnly then return ready
Browser previewBrowser preview
Browser previewOpen the returned URL
Browser previewRun-scoped capability host
Browser previewKeepalive via API
Browser previewIframe: no-referrer
Preview GatewayPreview Gateway
Preview GatewaySeparate shared Gateway
Preview GatewayHTTPS host match
Preview GatewayHTTPRoute selects Service
Preview GatewayNot API port-forward
ClusterIP ServiceClusterIP Service
ClusterIP ServicePer-preview target selector
ClusterIP ServiceService :80 → public port
ClusterIP ServiceRoutes to bound sandbox pod
ClusterIP ServiceAllowed ports 3000–9000
Sandbox preview appSandbox preview app
Sandbox preview appAgentHost pod-local path
Sandbox preview appLive preview: TCP forwarder
Sandbox preview app0.0.0.0 → loopback app
Sandbox preview appManual: chosen target port
relation-01 after create
relation-12 ready URL
relation-23 HTTPS probe
relation-34 HTTPS
relation-45 route
relation-56 public port
assuranceNo API → pod TCP readiness probe. Publication failure rolls back; DNS convergence has a bounded retry window.
assurance-0-labelPublic readiness
assurance-0-factProbe the exact generated HTTPS URL.
assurance-0-sourceSandboxPreviewService.cs
assurance-1-labelRollback on failure
assurance-1-factUnpublish failed preview resources.
assurance-1-sourceSandboxPreviewPublicationTests.cs
assurance-2-labelSeparate ingress
assurance-2-factDNS managed externally, not by API.
assurance-2-sourcegateway-preview.yaml
n0Patch run selector on pod; Create Service + HTTPRoute
n1Wait for managed DNS; Check Gateway + application
n2Run-scoped capability host; Keepalive via API
n3HTTPS host match; HTTPRoute selects Service
n4Service :80 → public port; Routes to bound sandbox pod
n5Live preview: TCP forwarder; 0.0.0.0 → loopback app
groupsCONTROL: PROVISION + PROBE; GATEWAY DATA PATH
Diagram details and constraints
ElementContract
titleAccept a provider before invoking it
takeawaySigned admission context freezes execution choice; live capability checks remain separate.
group-title0PREPARE AND ACCEPT
group-title1RUN BOUNDARY AND LIVE FENCES
Prepare contextPrepare context
Prepare contextResolve effective provider
Prepare contextBind operation + project
Prepare contextBind subject + provider key
Prepare contextSigned • expires in 5 min
Accept requestAccept request
Accept requestRe-resolve and compare
Accept requestVerify signature + expiry
Accept requestReject mismatched context
Accept requestReplacement context on error
Accepted planAccepted plan
Accepted planOne execution provider
Accepted planFreeze BYOK configuration
Accepted planProvider choice is immutable
Accepted planCopilot OR BYOK
Run snapshotRun snapshot
Run snapshotPrivate durable ownership
Run snapshotDatabase owner → secret ref
Run snapshotSecret store holds snapshot
Run snapshotChild / retry inheritance
Invocation guardInvocation guard
Invocation guardCheck accepted run boundary
Invocation guardMatch operation and provider
Invocation guardReject inconsistent execution
Invocation guardNo silent provider fallback
Capability fencesCapability fences
Capability fencesSeparate live permission checks
Capability fencesBefore / after mint or read
Capability fencesReject revoked or changed grant
Capability fencesSnapshot is not a bypass
relation-01 signed context
relation-12 match
relation-23 capture
relation-34 load boundary
relation-45 Copilot capability
assuranceMismatch rejects with replacement context. A frozen provider snapshot does not bypass live GitHub capability fences.
assurance-0-labelPrepared key
assurance-0-factFive minutes; operation / subject bound.
assurance-0-sourceAiExecutionPlanService.cs
assurance-1-labelPrivate snapshot
assurance-1-factDB ownership points to secret storage.
assurance-1-sourceRunModelProviderSnapshotStore.cs
assurance-2-labelLive capability
assurance-2-factRecheck before and after mint / read.
assurance-2-sourceGitHubCapabilityBroker.cs
n0Bind operation + project; Bind subject + provider key
n1Verify signature + expiry; Reject mismatched context
n2Freeze BYOK configuration; Provider choice is immutable
n3Database owner → secret ref; Secret store holds snapshot
n4Match operation and provider; Reject inconsistent execution
n5Before / after mint or read; Reject revoked or changed grant
groupsPREPARE AND ACCEPT; RUN BOUNDARY AND LIVE FENCES