molecule-core/docs/api-reference.md
Hongming Wang 24fec62d7f initial commit — Molecule AI platform
Forked clean from public hackathon repo (Starfire-AgentTeam, BSL 1.1)
with full rebrand to Molecule AI under github.com/Molecule-AI/molecule-monorepo.

Brand: Starfire → Molecule AI.
Slug: starfire / agent-molecule → molecule.
Env vars: STARFIRE_* → MOLECULE_*.
Go module: github.com/agent-molecule/platform → github.com/Molecule-AI/molecule-monorepo/platform.
Python packages: starfire_plugin → molecule_plugin, starfire_agent → molecule_agent.
DB: agentmolecule → molecule.

History truncated; see public repo for prior commits and contributor
attribution. Verified green: go test -race ./... (platform), pytest
(workspace-template 1129 + sdk 132), vitest (canvas 352), build (mcp).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:55:37 -07:00

455 lines
15 KiB
Markdown

# API Reference
Platform API server runs on `:8080` by default. All endpoints return JSON.
**Rate limit:** 600 req/min (configurable via `RATE_LIMIT` env var).
**CORS:** `http://localhost:3000`, `http://localhost:3001` by default (configurable via `CORS_ORIGINS`).
---
## REST Endpoints
### Workspaces
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/workspaces` | Create workspace and provision container |
| `GET` | `/workspaces` | List all workspaces |
| `GET` | `/workspaces/:id` | Get single workspace |
| `PATCH` | `/workspaces/:id` | Update workspace fields |
| `DELETE` | `/workspaces/:id` | Delete workspace and remove container |
| `POST` | `/workspaces/:id/restart` | Restart workspace container |
| `POST` | `/workspaces/:id/pause` | Pause workspace (cascades to children) |
| `POST` | `/workspaces/:id/resume` | Resume paused workspace |
#### POST /workspaces
Create a new workspace. Provisions a Docker container automatically.
```json
{
"name": "Marketing Lead",
"role": "Manages marketing campaigns",
"template": "general-assistant",
"tier": 2,
"model": "anthropic:claude-sonnet-4-6",
"runtime": "langgraph",
"parent_id": "uuid-of-parent",
"canvas": { "x": 100, "y": 200 }
}
```
Response: workspace object with `id`, `status: "provisioning"`.
---
### A2A Proxy
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/workspaces/:id/a2a` | Proxy A2A JSON-RPC to workspace agent |
Forwards JSON-RPC 2.0 requests to the workspace's agent container. Automatically wraps in JSON-RPC envelope if missing.
**Headers:**
- `X-Workspace-ID` -- set to caller workspace ID for agent-to-agent calls; empty for canvas-initiated
**Timeouts:**
- Canvas-initiated (no X-Workspace-ID): 5 minutes
- Agent-to-agent (X-Workspace-ID set): 30 minutes
**Example -- send message:**
```json
{
"jsonrpc": "2.0",
"id": "uuid",
"method": "message/send",
"params": {
"message": {
"role": "user",
"parts": [{ "kind": "text", "text": "Hello agent" }]
}
}
}
```
On success for canvas-initiated requests, also broadcasts an `A2A_RESPONSE` WebSocket event.
---
### Secrets
Secrets are encrypted with AES-256-GCM at rest. Values are never returned to the client.
#### Global Secrets
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/settings/secrets` | List global secrets (keys only) |
| `PUT` | `/settings/secrets` | Set a global secret |
| `POST` | `/settings/secrets` | Set a global secret (alias) |
| `DELETE` | `/settings/secrets/:key` | Delete a global secret |
Legacy aliases: `GET/POST/DELETE /admin/secrets` (backward compatible).
**PUT /settings/secrets:**
```json
{ "key": "ANTHROPIC_API_KEY", "value": "sk-ant-..." }
```
Response: `{ "status": "saved", "key": "ANTHROPIC_API_KEY", "scope": "global" }`
#### Workspace Secrets
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/workspaces/:id/secrets` | List merged secrets (workspace + inherited global) |
| `PUT` | `/workspaces/:id/secrets` | Set workspace-level secret override |
| `POST` | `/workspaces/:id/secrets` | Set workspace-level secret override (alias) |
| `DELETE` | `/workspaces/:id/secrets/:key` | Delete workspace-level secret |
**GET /workspaces/:id/secrets** returns a merged view:
```json
[
{ "key": "ANTHROPIC_API_KEY", "has_value": true, "scope": "workspace", "created_at": "...", "updated_at": "..." },
{ "key": "OPENAI_API_KEY", "has_value": true, "scope": "global", "created_at": "...", "updated_at": "..." }
]
```
- `scope: "workspace"` -- set directly on this workspace (overrides global)
- `scope: "global"` -- inherited from global secrets (not overridden)
Setting or deleting a workspace secret triggers an automatic container restart.
#### Precedence
When provisioning a container, secrets are loaded: global first, then workspace-specific. Workspace secrets with the same key override globals. The merged set is injected as environment variables.
#### Model Config
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/workspaces/:id/model` | Get current MODEL_PROVIDER config |
---
### Activity Logs
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/workspaces/:id/activity` | List activity logs (`?type=&limit=`) |
| `GET` | `/workspaces/:id/session-search` | Full-text search across activity + memories (`?q=&limit=`) |
| `POST` | `/workspaces/:id/activity` | Agent self-reports activity |
| `POST` | `/workspaces/:id/notify` | Agent pushes a chat message to canvas |
**POST /workspaces/:id/notify:**
```json
{ "message": "I've completed the analysis." }
```
Broadcasts an `AGENT_MESSAGE` WebSocket event. Does not persist to activity_logs.
**POST /workspaces/:id/activity:**
```json
{
"activity_type": "a2a_send",
"method": "message/send",
"summary": "Delegated task to Dev Lead",
"target_id": "uuid-of-target",
"status": "ok",
"duration_ms": 1500,
"request_body": {},
"response_body": {}
}
```
Valid activity types: `a2a_send`, `a2a_receive`, `task_update`, `agent_log`, `skill_promotion`, `error`.
---
### Registry (agent-facing)
Used by workspace agents to self-register and maintain liveness.
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/registry/register` | Agent registers on startup |
| `POST` | `/registry/heartbeat` | Agent heartbeat (includes task state) |
| `POST` | `/registry/update-card` | Agent updates its AgentCard |
**POST /registry/register:**
```json
{
"id": "workspace-uuid",
"url": "http://hostname:9000",
"agent_card": { "name": "...", "skills": [...], "capabilities": {...} }
}
```
Transitions workspace status to `online`, broadcasts `WORKSPACE_ONLINE`.
**POST /registry/heartbeat:**
```json
{
"workspace_id": "uuid",
"current_task": "Analyzing report...",
"active_tasks": 2,
"error_rate": 0.0,
"uptime_seconds": 3600
}
```
If error_rate > 0.5, broadcasts `WORKSPACE_DEGRADED`. Recovery broadcasts `WORKSPACE_ONLINE`.
---
### Discovery
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/registry/discover/:id` | Discover workspace by ID |
| `GET` | `/registry/:id/peers` | List accessible peer workspaces |
| `POST` | `/registry/check-access` | Check if two workspaces can communicate |
---
### Team Expansion
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/workspaces/:id/expand` | Expand workspace into a sub-team |
| `POST` | `/workspaces/:id/collapse` | Remove all children, collapse back to single workspace |
---
### Agents
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/workspaces/:id/agent` | Assign agent to workspace |
| `PATCH` | `/workspaces/:id/agent` | Replace agent |
| `DELETE` | `/workspaces/:id/agent` | Remove agent |
| `POST` | `/workspaces/:id/agent/move` | Move agent between workspaces |
---
### Config & Memory
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/workspaces/:id/config` | Get workspace config (JSONB) |
| `PATCH` | `/workspaces/:id/config` | Merge-patch config |
| `GET` | `/workspaces/:id/memory` | List KV memory entries |
| `GET` | `/workspaces/:id/memory/:key` | Get single KV entry |
| `POST` | `/workspaces/:id/memory` | Set KV entry (with optional TTL) |
| `DELETE` | `/workspaces/:id/memory/:key` | Delete KV entry |
---
### Agent Memories (HMA)
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/workspaces/:id/memories` | Commit a memory (LOCAL, TEAM, or GLOBAL scope) |
| `GET` | `/workspaces/:id/memories` | Search memories |
| `DELETE` | `/workspaces/:id/memories/:memoryId` | Delete a memory |
---
### Approvals
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/approvals/pending` | List all pending approvals (cross-workspace) |
| `POST` | `/workspaces/:id/approvals` | Create approval request |
| `GET` | `/workspaces/:id/approvals` | List workspace approvals |
| `POST` | `/workspaces/:id/approvals/:approvalId/decide` | Approve or reject |
---
### Async Delegation
| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/workspaces/:id/delegate` | Fire-and-forget delegation (`{target_id, task}`) |
| `GET` | `/workspaces/:id/delegations` | List delegations with status and results |
---
### Templates & Files
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/templates` | List available workspace templates |
| `POST` | `/templates/import` | Import template from URL |
| `GET` | `/workspaces/:id/shared-context` | Get shared context files |
| `PUT` | `/workspaces/:id/files` | Replace all config files |
| `GET` | `/workspaces/:id/files` | List files (lazy: `?depth=1&path=subdir`) |
| `GET` | `/workspaces/:id/files/*path` | Read a config file |
| `PUT` | `/workspaces/:id/files/*path` | Write a config file |
| `DELETE` | `/workspaces/:id/files/*path` | Delete a config file |
---
### Plugins
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/plugins` | List available plugins (`?runtime=<name>` filters to compatible) |
| `GET` | `/plugins/sources` | List registered install-source schemes (e.g. `github`, `local`) |
| `GET` | `/workspaces/:id/plugins` | List plugins installed in workspace |
| `GET` | `/workspaces/:id/plugins/available` | Plugins filtered to the workspace's runtime |
| `GET` | `/workspaces/:id/plugins/compatibility?runtime=X` | Preflight runtime change |
| `POST` | `/workspaces/:id/plugins` | Install plugin (`{"source":"<scheme>://<spec>"}`, e.g. `local://ecc`, `github://owner/repo#v1.0`) — auto-restarts |
| `DELETE` | `/workspaces/:id/plugins/:name` | Uninstall plugin — auto-restarts |
---
### Bundles
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/bundles/export/:id` | Export workspace as portable bundle |
| `POST` | `/bundles/import` | Import workspace from bundle |
---
### Other
| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/health` | Health check (`{"status": "ok"}`) |
| `GET` | `/metrics` | Prometheus metrics |
| `GET` | `/events` | List structure events |
| `GET` | `/events/:workspaceId` | List events for a workspace |
| `GET` | `/workspaces/:id/traces` | Proxy to Langfuse traces |
| `GET` | `/workspaces/:id/terminal` | WebSocket terminal into container |
| `GET` | `/canvas/viewport` | Get saved canvas viewport |
| `PUT` | `/canvas/viewport` | Save canvas viewport |
| `POST` | `/webhooks/github` | GitHub webhook receiver |
---
## WebSocket Events
Connect to `ws://localhost:8080/ws`. All messages use this envelope:
```json
{
"event": "EVENT_TYPE",
"workspace_id": "uuid",
"timestamp": "2024-01-01T00:00:00Z",
"payload": { ... }
}
```
**Routing:** Canvas clients (no workspace ID) receive all events. Workspace clients receive only events for workspaces they can communicate with (per hierarchy rules).
### Workspace Lifecycle Events
These are persisted to the `structure_events` table.
| Event | Payload | Trigger |
|-------|---------|---------|
| `WORKSPACE_PROVISIONING` | `{name, tier, parent_id?}` | Container creation or restart begins |
| `WORKSPACE_ONLINE` | `{url, agent_card}` | Agent self-registers or recovers from degraded |
| `WORKSPACE_OFFLINE` | `{}` | A2A proxy detects dead container |
| `WORKSPACE_PAUSED` | `{}` | Pause operation completes |
| `WORKSPACE_DEGRADED` | `{error_rate, sample_error}` | Heartbeat reports error_rate > 0.5 |
| `WORKSPACE_REMOVED` | `{name?}` | Workspace deleted |
| `WORKSPACE_PROVISION_FAILED` | `{error}` | Container start failed |
| `WORKSPACE_EXPANDED` | `{children: [ids]}` | Team expansion complete |
| `WORKSPACE_COLLAPSED` | `{children: [ids]}` | Team collapse complete |
### Agent Events
Persisted to `structure_events`.
| Event | Payload | Trigger |
|-------|---------|---------|
| `AGENT_CARD_UPDATED` | `{agent_card}` | Agent updates its discovery card |
| `AGENT_ASSIGNED` | `{agent_id, name}` | Agent assigned to workspace |
| `AGENT_REPLACED` | `{agent_id, name}` | Agent replaced in workspace |
| `AGENT_REMOVED` | `{agent_id}` | Agent removed from workspace |
| `AGENT_MOVED` | `{from, to, agent_id}` | Agent moved (fired on both source and target) |
### Approval Events
Persisted to `structure_events`.
| Event | Payload | Trigger |
|-------|---------|---------|
| `APPROVAL_REQUESTED` | `{approval_id, workspace_id, ...}` | Agent requests human approval |
| `APPROVAL_ESCALATED` | `{approval_id, child_id, ...}` | Approval escalated to parent workspace |
### High-Frequency Events (broadcast only, not persisted)
| Event | Payload | Trigger |
|-------|---------|---------|
| `TASK_UPDATED` | `{current_task, active_tasks}` | Heartbeat includes task state changes |
| `AGENT_MESSAGE` | `{message, workspace_id, name}` | Agent pushes chat message via `POST /notify` |
| `ACTIVITY_LOGGED` | `{activity_type, method, summary, status, source_id, target_id, duration_ms}` | Any activity log insert |
| `A2A_RESPONSE` | `{response_body, method, duration_ms}` | Canvas-initiated A2A proxy returns success |
### Frontend Handling
The canvas (`canvas-events.ts`) handles these events in its Zustand store:
| Event | Frontend Action |
|-------|----------------|
| `WORKSPACE_ONLINE` | Set node status to `"online"` |
| `WORKSPACE_OFFLINE` | Set node status to `"offline"` |
| `WORKSPACE_PAUSED` | Set node status to `"paused"`, clear currentTask |
| `WORKSPACE_DEGRADED` | Set node status to `"degraded"`, store error rate |
| `WORKSPACE_PROVISIONING` | Update existing node or create new node |
| `WORKSPACE_REMOVED` | Remove node, reparent children, clean edges |
| `AGENT_CARD_UPDATED` | Update node's agentCard |
| `TASK_UPDATED` | Update node's currentTask and activeTasks |
| `AGENT_MESSAGE` | Append to chat messages for the workspace |
| `A2A_RESPONSE` | Extract response text, append to chat messages |
---
## A2A JSON-RPC Methods
Workspace agents implement the A2A protocol via the `a2a-sdk`. The Platform A2A proxy forwards these methods transparently.
### message/send
Synchronous message exchange. Blocks until the agent completes processing.
```json
{
"jsonrpc": "2.0",
"id": "unique-id",
"method": "message/send",
"params": {
"message": {
"messageId": "unique-msg-id",
"role": "user",
"parts": [
{ "kind": "text", "text": "Analyze the Q4 report" }
]
}
}
}
```
Response contains the agent's reply message with `parts` (text, data, etc.).
### message/stream
SSE streaming variant of `message/send`. Returns token-level Server-Sent Events as the agent generates its response.
### tasks/get
Poll the status of a previously submitted async task.
```json
{
"jsonrpc": "2.0",
"id": "unique-id",
"method": "tasks/get",
"params": {
"id": "task-uuid"
}
}
```
Returns task state: `submitted`, `working`, `input-required`, `completed`, `failed`, `canceled`.