#551 — sidebar filters prior-session agents (from_prior_session) #552 — status messages prioritise ↑ affordance over /queue #553 — oversized paste consolidation to @mention file (+uuid suffix) #523 — release.yml: add if: guard so release job doesn't skip on dispatch #526 — verify cost_status side-channel is fully wired (already in place) #554 — mouse/trackpad scroll now sets user_scrolled_during_stream #522 — set RELEASE_TAG_PAT secret for auto-tag → release trigger #504 — session-context panel (SidebarFocus::Context, config toggle, default off) #501 — multi-arch Dockerfile (+BUILDPLATFORM pin) + devcontainer + release CI #484 — docs/RUNTIME_API.md rewritten against actual runtime_api.rs endpoints #482 — close v0.8.8 planning tracker Fixes from review: - RUNTIME_API.md: corrected endpoints (/v1/...), port (7878), doctor JSON schema (flat) - Dockerfile: added --platform=$BUILDPLATFORM for native multi-arch builds - docs/DOCKER.md: removed Docker Hub references (GHCR only) - sidebar.rs: dropped unused _theme variable - settings.rs: context_panel default changed to false - app.rs: paste filename now includes 8-char uuid suffix to avoid collision
8.8 KiB
Runtime API & Integration Contract
DeepSeek TUI exposes a local runtime API through deepseek serve --http and
machine-readable health via deepseek doctor --json. This document is the
stable integration contract for native macOS workbench applications (and other
local supervisors) that embed the DeepSeek engine without screen-scraping
terminal output.
Architecture
macOS workbench (or any local supervisor)
│
├─ deepseek doctor --json → machine-readable health & capability
├─ deepseek serve --http → HTTP/SSE runtime API
├─ deepseek serve --mcp → MCP stdio server
└─ deepseek [args] → interactive TUI session
The engine runs as a local-only process. All APIs bind to localhost by
default. No hosted relay, no provider-token custody, no secret leakage.
Capability endpoint: deepseek doctor --json
Returns a JSON object describing the current installation's readiness state. Suitable for health-check polling from a macOS workbench.
deepseek doctor --json
Response schema (key fields)
| Field | Type | Description |
|---|---|---|
version |
string | Installed version (e.g. "0.8.9") |
config_path |
string | Resolved config file path |
config_present |
bool | Whether the config file exists |
workspace |
string | Default workspace directory |
api_key.source |
string | env, config, or missing |
base_url |
string | API base URL |
default_text_model |
string | Default model |
memory.enabled |
bool | Whether the memory feature is on |
memory.path |
string | Path to memory file |
memory.file_present |
bool | Whether memory file exists |
mcp.config_path |
string | MCP config file path |
mcp.present |
bool | Whether MCP config exists |
mcp.servers |
array | Per-server health: {name, enabled, status, detail} |
skills.selected |
string | Resolved skills directory |
skills.global.path / .present / .count |
— | Global skills dir |
skills.agents.path / .present / .count |
— | .agents/skills/ dir |
skills.local.path / .present / .count |
— | skills/ dir |
skills.opencode.path / .present / .count |
— | .opencode/skills/ dir |
skills.claude.path / .present / .count |
— | .claude/skills/ dir |
tools.path / .present / .count |
— | Global tools directory |
plugins.path / .present / .count |
— | Global plugins directory |
sandbox.available |
bool | Whether sandbox is supported on this OS |
sandbox.kind |
string or null | Sandbox kind (e.g. "macos_seatbelt") |
storage.spillover.path / .present / .count |
— | Tool output spillover dir |
storage.stash.path / .present / .count |
— | Composer stash |
Example
{
"version": "0.8.9",
"config_path": "/Users/you/.deepseek/config.toml",
"config_present": true,
"workspace": "/Users/you/projects/deepseek-tui",
"api_key": {
"source": "env"
},
"base_url": "https://api.deepseek.com",
"default_text_model": "deepseek-v4-pro",
"memory": {
"enabled": false,
"path": "/Users/you/.deepseek/memory.md",
"file_present": true
},
"mcp": {
"config_path": "/Users/you/.deepseek/mcp.json",
"present": true,
"servers": [
{"name": "filesystem", "enabled": true, "status": "ok", "detail": "ready"}
]
},
"sandbox": {
"available": true,
"kind": "macos_seatbelt"
}
}
HTTP/SSE runtime API: deepseek serve --http
deepseek serve --http [--host 127.0.0.1] [--port 7878] [--workers 2]
Defaults: host 127.0.0.1, port 7878, 2 workers (clamped 1–8).
The server binds to localhost by default. Configuration is via CLI flags —
there is no [app_server] config section.
Endpoints
Health
GET /health
Sessions (legacy session manager)
GET /v1/sessions?limit=50&search=<substring>GET /v1/sessions/{id}DELETE /v1/sessions/{id}POST /v1/sessions/{id}/resume-thread
Threads (durable runtime data model)
GET /v1/threads?limit=50&include_archived=falseGET /v1/threads/summary?limit=50&search=<optional>&include_archived=falsePOST /v1/threadsGET /v1/threads/{id}PATCH /v1/threads/{id}(currently supports{ "archived": true|false })POST /v1/threads/{id}/resumePOST /v1/threads/{id}/fork
Turns (within a thread)
POST /v1/threads/{id}/turnsPOST /v1/threads/{id}/turns/{turn_id}/steerPOST /v1/threads/{id}/turns/{turn_id}/interruptPOST /v1/threads/{id}/compact(manual compaction)
Events (SSE replay + live stream)
GET /v1/threads/{id}/events?since_seq=<u64>
Compatibility stream (one-shot, backwards-compatible)
POST /v1/stream
Tasks (durable background work)
GET /v1/tasksPOST /v1/tasksGET /v1/tasks/{id}POST /v1/tasks/{id}/cancel
Automations (scheduled recurring work)
GET /v1/automationsPOST /v1/automationsGET /v1/automations/{id}PATCH /v1/automations/{id}DELETE /v1/automations/{id}POST /v1/automations/{id}/runPOST /v1/automations/{id}/pausePOST /v1/automations/{id}/resumeGET /v1/automations/{id}/runs?limit=20
Introspection
GET /v1/workspace/statusGET /v1/skillsGET /v1/apps/mcp/serversGET /v1/apps/mcp/tools?server=<optional>
Runtime data model
The runtime uses a durable Thread/Turn/Item lifecycle.
- ThreadRecord —
id,created_at,updated_at,model,workspace,mode,task_id,coherence_state,system_prompt,latest_turn_id,latest_response_bookmark,archived - TurnRecord —
id,thread_id,status(queued|in_progress|completed| failed|interrupted|canceled), timestamps, duration, usage, error summary - TurnItemRecord —
id,turn_id,kind(user_message|agent_message| tool_call|file_change|command_execution|context_compaction|status|error), lifecyclestatus,metadata
Events are append-only with a global monotonic seq for replay/resume.
Restart semantics
- If the process restarts while a turn or item is
queuedorin_progress, the recovered record is markedinterruptedwith an"Interrupted by process restart"error. - Task execution performs its own recovery on top of the same persisted thread/turn store.
Approval model
- The
auto_approveflag applies to the runtime approval bridge and engine tool context. When enabled for a thread/turn/task, approval-required tools are auto-approved in the non-interactive runtime path, shell safety checks run in auto-approved mode, and spawned sub-agents inherit that setting. - When omitted,
auto_approvedefaults tofalse.
SSE event stream
The SSE event payload shape:
{
"seq": 42,
"timestamp": "2026-02-11T20:18:49.123Z",
"thread_id": "thr_1234abcd",
"turn_id": "turn_5678efgh",
"item_id": "item_90ab12cd",
"event": "item.delta",
"payload": {
"delta": "partial output",
"kind": "agent_message"
}
}
Common event names: thread.started, thread.forked, turn.started,
turn.lifecycle, turn.steered, turn.interrupt_requested,
turn.completed, item.started, item.delta, item.completed,
item.failed, item.interrupted, approval.required, sandbox.denied,
coherence.state.
Security boundary
- Localhost only. The server binds to
127.0.0.1by default. Set--host 0.0.0.0only when you have a reverse-proxy / VPN that authenticates — there is no built-in auth, user isolation, or TLS. - No provider-token custody. The server never returns the API key. The
api_key.sourcecapability field reportsenv,config, ormissing— never the key itself. - No hosted relay. The app-server is a local process under the user's control. There is no cloud component.
- Capability responses never leak secrets, file contents, or session message bodies. They report metadata: presence, counts, status flags.
Session lifecycle (native UI supervision)
| Operation | Endpoint |
|---|---|
| List sessions | GET /v1/sessions |
| Get session | GET /v1/sessions/{id} |
| Delete session | DELETE /v1/sessions/{id} |
| Resume into thread | POST /v1/sessions/{id}/resume-thread |
| Create thread | POST /v1/threads |
| List threads | GET /v1/threads |
| Attach to events | GET /v1/threads/{id}/events?since_seq=0 |
| Send message | POST /v1/threads/{id}/turns |
| Steer | POST /v1/threads/{id}/turns/{turn_id}/steer |
| Interrupt | POST /v1/threads/{id}/turns/{turn_id}/interrupt |
| Compact | POST /v1/threads/{id}/compact |
Compatibility tests
Contract snapshots live in crates/protocol/tests/. Run:
cargo test -p deepseek-protocol --test parity_protocol --locked
This validates that the app-server's event schema hasn't drifted from the
documented contract. CI runs this on every push to main and on release tags.