Skip to content

Latest commit

 

History

History
434 lines (302 loc) · 10.6 KB

api-reference.md

File metadata and controls

434 lines (302 loc) · 10.6 KB

API reference

The daemon serves a JSON HTTP API on 127.0.0.1:4748 (configurable). Authentication: none. The API is loopback-only. UIs and integrations talk to this.

Health & status

GET /api/health

{"ok": true, "port": 4748}

GET /api/status

{
  "sessionId":      "f3a8c2…",
  "taskId":         "t-f3a8c2-1748906412",
  "taskGoal":       "add refund flow to orders service",
  "role":           "developer",
  "activeProvider": "claude",
  "tokensUsed":     74200,
  "graphNodes":     342,
  "handoffsDone":   2,
  "hfsScore":       0.94,
  "hfsHistory":     [0.40, 0.50, …],
  "fsmState":       "RUNNING"
}

Providers

GET /api/providers

Live runtime status per provider (active/standby/exhausted/error).

GET /api/config/providers

Full metadata + install/auth capability flags + probe result.

POST /api/config/providers

Body:

{
  "name":        "claude",
  "enabled":     true,
  "declaredCap": 40000,
  "model":       "qwen2.5-coder:32b",
  "baseUrl":     "http://localhost:11434"
}

Patches [providers.<name>] in relay.toml and reloads.

POST /api/providers/install {name}

Spawns a terminal running the per-OS install command.

POST /api/providers/oauth {name}

Spawns the provider's OAuth subcommand (e.g. claude login).

POST /api/providers/api-key {name, value}

Writes key to .relay/.env, reloads daemon env.

Accounts

Multiple logins per provider (account-aware handoff). Each account is a label plus an isolated config dir; the adapter is pointed at the active account's dir.

POST /api/providers/account {provider, label}

Switch the active account for a provider. The next session (or handoff) uses it.

POST /api/providers/account/add {provider, label, configDir}

Register a login. configDir optional, defaults to an isolated per-label dir.

POST /api/providers/account/remove {provider, label}

Remove a login.

POST /api/providers/account/login {provider, label}

Spawns a terminal running the provider's login flow against that account's config dir.

Accounts are returned inside GET /api/config/providers as accounts: [{label, active, configDir}] + activeAccount.

Detection & adoption

GET /api/detect?sinceHours=<n>

Scan for AI coding agents already running on this machine (process scan + on-disk transcript stores). Default window 24h.

[
  {
    "id":          "claude_6d569909",
    "provider":    "claude",
    "displayName": "Claude Code",
    "surface":     "cli",
    "pid":         14032,
    "running":     true,
    "workDir":     "C:/dev/my-project",
    "account":     "personal",
    "detectedVia": ["process", "transcript"],
    "lastActive":  1752666000,
    "session": {
      "sessionId":      "6d569909…",
      "model":          "claude-sonnet-5",
      "initialPrompt":  "add a refund flow…",
      "plan":           ["…"],
      "tasksRemaining": ["…"],
      "filesTouched":   ["orders/refund.go"],
      "skills":         ["backend-patterns"],
      "mcps":           ["github"],
      "recentTurns":    [{"role": "user", "text": "…"}, {"role": "assistant", "text": "…"}],
      "tokensIn":       84210,
      "tokensOut":      12045,
      "messageCount":   37
    }
  }
]

recentTurns carries a redacted, verbatim tail of the conversation (last 16 user/assistant turns, oldest first), so a handoff moves real recent context rather than only the first and last prompts.

POST /api/detect/adopt {id, target, start, interactive}

Lift a detected session's intent into a continuation brief (persisted under .relay/adopted/), targeting provider target. Two mutually exclusive continue modes:

  • "interactive": true opens the target provider's own CLI in a new terminal, in the adopted project directory, under the active account, seeded with a prompt that reads the full brief. Relay then steps back and the developer drives the session. The response includes opened (bool) and, on failure, openError.
  • "start": true (headless) launches a Relay-orchestrated session that continues the work and streams events to the dashboard. The response includes started and, on failure, startError.

With neither flag, the brief is rendered and persisted but nothing is launched.

Quota wallet

GET /api/quota/wallet

Per provider+account: remaining quota, reset time, live burn rate, and forecast ETA (feeds predictive handoff).

[
  {
    "provider":     "claude",
    "account":      "personal",
    "remaining":    12400,
    "total":        40000,
    "fractionUsed": 0.69,
    "resetsAt":     "2026-07-16T21:00:00Z",
    "source":       "transcript",
    "burnPerMin":   310.5,
    "etaMinutes":   39.9
  }
]

Pipelines

Multi-agent DAGs: one provider+task per node, explicit dependsOn ordering, fallback providers on failure, verify acceptance commands (verifier gate) per node.

GET /api/pipelines

[
  {
    "name": "feature",
    "nodes": [
      {
        "id":        "build",
        "provider":  "claude",
        "task":      "implement the endpoint",
        "dependsOn": [],
        "fallback":  ["codex"],
        "verify":    ["go build ./...", "go test ./..."]
      }
    ]
  }
]

POST /api/pipelines

Saves the full pipeline list (same shape as GET response). Persisted to .relay/pipelines.json.

POST /api/pipelines/run {name}

Executes the pipeline's nodes in dependency order. Honours the single-session guard: errors with "session already running" if one is active.

Time machine

GET /api/history

The handoff timeline, read from the audit log.

[
  {"seq": 3, "ts": "2026-07-16T09:41:03Z", "event": "handoff", "provider": "claude", "summary": "quota breach → codex"}
]

GET /api/history/commits

Snapshot commits on the session branch: [{sha, short, subject, when}].

GET /api/history/diff?sha=<sha>

The diff for one snapshot commit.

POST /api/history/rewind {sha}

Non-destructive rewind: creates a new branch at the snapshot so current work is never lost.

Profiles

GET /api/profiles

[
  {
    "name":        "backend",
    "chain":       ["claude", "codex"],
    "kinds":       ["api", "service"],
    "skills":      ["go", "ts"],
    "contextHint": "production backend services"
  }
]

POST /api/profiles

{
  "name":        "backend",
  "chain":       ["claude", "codex"],
  "kinds":       ["api", "service"],
  "skills":      ["go", "ts"],
  "contextHint": "production backend services",
  "delete":      false
}

Upserts (or deletes when "delete": true).

Session control

POST /api/run {task, threshold, maxHandoffs}

Starts a session in the running daemon. Single-session: 409 if one is already active.

POST /api/handoff

Trigger immediate handoff.

POST /api/session/pause {pause}

Toggle agent execution (cooperative; honoured at event boundaries).

POST /api/session/reply {reply}

Send a stdin reply to the active adapter (if it implements StdinReplier).

GET /api/session/diff

{
  "summary": " orders/refund.go | 42 +++\n orders/routes.go | 18 +-",
  "diff":    "diff --git a/orders/refund.go …"
}

Returns git diff main...relay/<sid> against the user's main branch.

GET /api/session/cost

{
  "sessionUsd": 0.0042,
  "tokensIn":   1820,
  "tokensOut":  411,
  "provider":   "claude"
}

Approvals

GET /api/approvals

Pending requests waiting for user resolution.

[
  {
    "id":        "abc123",
    "action":    "write 250 lines to orders/refund.go",
    "reason":    "exceeds 200-line threshold",
    "severity":  "warn",
    "createdAt": "2026-06-03T15:41:03Z"
  }
]

POST /api/approvals/<id> {approved, note}

Resolve a pending request.

Events stream

GET /api/events?since=<id>

Pull events newer than the given ID:

[
  {"id": 412, "ts": "09:41:27", "tag": "quota",  "msg": "claude: 74% used"},
  {"id": 413, "ts": "09:41:28", "tag": "tool_use", "msg": "Edit orders/routes.go"}
]

Tag values: tool_use, tool_result, text, system, quota, handoff, waiting, error.

WS /ws

WebSocket push of the same events as they happen. Reduces latency from 1500ms (poll) to ~50ms.

Graph & retrieval

GET /api/graph

{"nodes": 342, "edges": 418}

GET /api/graph/detail

Full node + edge list (~200 most recent). Includes module and symbol nodes from the codebase scan plus session-specific decision, constraint, file, do_not_redo nodes from agent activity.

GET /api/graph/project?path=<repo>

One-shot codegraph scan of an arbitrary repo path (no session needed): returns {nodes, edges} of module / symbol nodes. Used by the desktop Graph page's project picker.

GET /api/retrieval?q=<text>&limit=<n>

FTS5 full-text search over indexed code chunks. Default limit 20.

[
  {
    "ID":        "chunk:orders/refund.go:1-40",
    "Path":      "orders/refund.go",
    "Lang":      "go",
    "StartLine": 1,
    "EndLine":   40,
    "Body":      "package orders\n\nimport ...\n\nfunc (s *RefundService) execute(...) error {\n  ...\n}",
    "SHA":       "a3f8c2d4",
    "Dim":       0
  }
]

Used by the MCP relay_retrieve tool. Chunks are populated by the codegraph scanner at session start; future embedding-based ranking will use the embedding blob column.

Audit & contract

GET /api/contract

The current continuation contract as JSON.

GET /api/instructions

Discovered CLAUDE.md / AGENTS.md / Cursor rules + profile skills.

Vision

GET /api/vision/config

POST /api/vision/config

POST /api/vision/probe

See Vision fallback.

Ollama bridge

GET /api/ollama/models?baseUrl=...

Installed models + curated vision pull list.

POST /api/ollama/pull {baseUrl, tag}

Stream-pulls a model from ollama.com. Progress emitted to event log.

POST /api/ollama/launch {provider, model}

Spawns ollama launch <tool> --model <model> in a new terminal.

Observability

GET /api/circuit

Per-provider circuit breaker snapshots.

[
  {"name": "claude", "state": "closed",    "failures": 0},
  {"name": "codex",  "state": "open",      "failures": 3, "openedAt": "..."},
  {"name": "ollama", "state": "half-open", "failures": 0}
]

GET /api/outcomes

Profile success aggregates.

[
  {
    "profile":    "backend",
    "runs":       12,
    "successes":  10,
    "successRate": 0.833,
    "avgTokens":  18420,
    "avgCostUsd": 0.087,
    "lastRun":    "2026-06-03T15:41:03Z"
  }
]

GET /api/redactions

Counts of secrets scrubbed this session.

{"summary": "openai_key:2, env_secret_pair:1"}