Skip to main content

Module v2

Module v2 

Source
Expand description

v2 /api/* surface (opencode 1.18.31 contract).

The 1.18.31 TUI and web UI are hybrid clients: their SyncProvider boots on v1 paths (served elsewhere in this crate) while DataProvider and the session view call the v2 SDK (client.v2.*), which resolves to /api/*. Before this module every /api/* call fell through the web-UI catch-all to SPA HTML, and JSON clients that parse it degrade or crash (e.g. the session.diff → SidebarFiles.flatMap TypeError on an HTML string, 2026-10-08).

Shapes are taken from the vendored OpenAPI contract (assets/openapi/1.18.31.json, served at /doc) and probed live from the freeze. Envelope rules captured live:

  • {location, data} for location-scoped collections (agent/model/…),
  • {data: …} for single resources,
  • {data, cursor:{previous,next}} for paginated message lists.

The v2 session prompt route does NOT duplicate the prompt loop — it normalizes the v2 body and calls the same ocserve_core::prompt::run_prompt executor the v1 routes use (single owner of the state machine).

Functions§

active_sessions
GET /api/session/active — {data:{}} (no per-request foreground drains are tracked on the v2 wire; ocserve’s own prompt locks are v1 status).
compact
POST /api/session/{sessionID}/compact — NoContent (triggers summarize).
console_orgs
GET /experimental/console/orgs — {orgs:[]}.
console_switch
POST /experimental/console/switch — 400 payload (accountID required).
control_move_session
POST /experimental/control-plane/move-session — 400 (sessionID required).
create_session
POST /api/session — create a session (v2). Shares the v1 creation path.
credential_remove
DELETE /api/credential/{credentialID} — NoContent.
credential_update
PATCH /api/credential/{credentialID} — NoContent (no credential store; ocserve has no OAuth credential backend — named divergence D-CREDENTIAL).
fs_find
GET /api/fs/find?query=&type=&limit= — {location, data:[FileSystemEntry]}.
fs_list
GET /api/fs/list?path= — direct children of path (default “.”).
fs_read
GET /api/fs/read/*path — raw bytes of one file (Uint8Array on the wire).
get_integration
GET /api/integration/{integrationID} — single integration.
get_message
GET /api/session/{sessionID}/message/{messageID} — single message.
get_provider
GET /api/provider/{providerID} — {location, data:ProviderV2Info}.
get_session
GET /api/session/{sessionID} — {data:SessionV2Info}.
git_init
POST /project/git/init — Project record (freeze shape). ocserve does not initialize a git repo; it echoes the current project with vcs:"git" when the worktree is a git checkout (probed 2026-10-09).
global_upgrade
POST /global/upgrade — 400 BadRequest (missing target). ocserve does not self-upgrade; cited divergence.
health
GET /api/health — {healthy:true} (schema: enum true).
integration_attempt_complete
POST /api/integration/attempt/{attemptID}/complete — 404 (no attempts).
integration_attempt_delete
DELETE /api/integration/attempt/{attemptID} — 404 (no attempts).
integration_attempt_get
GET /api/integration/attempt/{attemptID} — 404 (no attempts).
integration_connect_key
POST /api/integration/{integrationID}/connect/key — NoContent (no credential backend; named divergence D-CREDENTIAL).
integration_connect_oauth
POST /api/integration/{integrationID}/connect/oauth — 401 (no OAuth backend configured; freezes probe returns UnauthorizedError).
integrations_body
Pure integration-list projection (the /api/integration body).
interrupt
POST /api/session/{sessionID}/interrupt — NoContent (abort the task).
list_integrations
GET /api/integration — {location, data:[IntegrationInfo]}.
list_messages
GET /api/session/{sessionID}/message — paginated projected messages.
list_models
GET /api/model — {location, data:[ModelV2Info]}, available providers only, non-deprecated models, sorted by release descending (upstream order).
list_providers
GET /api/provider — {location, data:[ProviderV2Info]} for available providers.
list_references
GET /api/reference — {location, data:[]} (no references configured).
list_sessions
GET /api/session — {data:[SessionV2Info], cursor:{previous,next}}.
list_skills
GET /api/skill — {location, data:[SkillV2Info]}. ocserve exposes the one built-in skill it ships (the customize-opencode command, source “skill”).
list_skills_raw
GET /skill — bare array (v1 shape; distinct from /api/skill’s envelope).
location
GET /api/location — bare LocationInfo (not enveloped).
models_body
Pure model-list projection (the /api/model body).
patch_project
PATCH /project/{projectID} — echo the project (freeze returns the project record; updates to name/icon are not persisted by ocserve).
permission_create
POST /api/session/{sessionID}/permission — create a permission request. ocserve’s gate is a rendezvous for the RUNNING prompt; a standalone create is acknowledged with the evaluated effect (freeze shape {data:{id,effect}}). Full blocking semantics belong to the prompt path.
permission_reply
POST /api/session/{sessionID}/permission/{requestID}/reply — reply to ask. Body decode precedes the 404 (same Payload envelope as v1); the old unwrap_or("reject") silently rejected a real ask on a malformed body.
permission_request
GET /api/permission/request — {location, data:[...]} pending asks.
permission_saved
GET /api/permission/saved — {data:[PermissionSavedInfo]}.
permission_saved_delete
DELETE /api/permission/saved/{id} — NoContent (best-effort grant removal).
project_copy_create
POST /experimental/project/{projectID}/copy — 400 (strategy required).
project_copy_generate_name
POST /experimental/project/{projectID}/copy/generate-name — random name.
project_copy_refresh
POST /experimental/project/{projectID}/copy/refresh — 204.
project_copy_remove
DELETE /experimental/project/{projectID}/copy — 400 (body required).
prompt
POST /api/session/{sessionID}/prompt — spec SessionInput.Admitted. Normalizes the v2 body {id?, prompt:{text,files,agents}, delivery, resume} into the v1 payload form and runs the SAME ocserve_core::prompt::run_prompt the v1 /session/{id}/message and /prompt_async routes use. This route is synchronous (the v1 session.prompt semantics), so the loop stays in one place; only the body shape differs.
providers_body
Pure projections of the v2 metadata group over the payloads — the ONE source of truth for both the request path and rebuild_wire (the wire bytes are these values serialized with serde_json). Byte-parity pinned by tests/v2_body_parity.rs (including a planted-drift negative control).
pty_connect_token
POST /api/pty/{ptyID}/connect-token.
pty_create
POST /api/pty — create (location-wrapped info).
pty_get
GET /api/pty/{ptyID} — get.
pty_list
GET /api/pty — {location, data:[Pty]}.
pty_remove
DELETE /api/pty/{ptyID} — remove (true).
pty_update
PUT /api/pty/{ptyID} — update title/size.
question_reject
POST /api/session/{sessionID}/question/{requestID}/reject — reject.
question_reply
POST /api/session/{sessionID}/question/{requestID}/reply — reply.
question_request
GET /api/question/request — {location, data:[...]} pending questions.
revert_clear
POST /api/session/{sessionID}/revert/clear — restore the snapshot, clear the marker, emit session.next.revert.cleared; NoContent.
revert_commit
POST /api/session/{sessionID}/revert/commit — finalize the revert: delete the reverted range and clear the marker; emit session.next.revert.committed.
revert_stage
POST /api/session/{sessionID}/revert/stage — {data:RevertState}. Runs the real revert engine (restore files, compute the range diff, set the marker) and emits session.next.revert.staged so the web UI resyncs.
session_background
POST /experimental/session/{sessionID}/background — false.
session_context
GET /api/session/{sessionID}/context — {data:[SessionMessage.Message]}. Projection over the stored message stream (thinner than upstream’s full event-sourcing; documented divergence).
session_diff
GET /session/{id}/diff — with ?messageID=, the message’s stored summary.diffs (upstream SessionSummary.diff); without it, the session convenience path returns []. Crucially this is JSON, not SPA HTML: the TUI’s SidebarFiles flatMaps the result and an HTML string crashes it.
session_event
GET /api/session/{sessionID}/event — durable SSE for one session. ocserve has no per-session event stream distinct from the global bus; alias the global SSE (frames carry sessionID, clients filter). Semantics are a subset; wire shape (text/event-stream) matches.
session_history
GET /api/session/{sessionID}/history — {data:[DurableEvent], hasMore}. ocserve serves its own event log; the wire shape matches, semantics are a subset (documented).
session_permission
GET /api/session/{sessionID}/permission — {data:[]} (per-session asks).
session_permission_one
GET /api/session/{sessionID}/permission/{requestID} — one ask.
session_question
GET /api/session/{sessionID}/question — {data:[]} (per-session questions).
session_share
POST /session/{id}/share — freeze returns the Session record (share is a local-only marker; ocserve has no remote share backend, so the record is returned with share unset — a named divergence D-SHARE-NOBACKEND).
session_unshare
DELETE /session/{id}/share — freeze returns the (unshared) Session record.
switch_agent
POST /api/session/{sessionID}/agent — switch agent (NoContent).
switch_model
POST /api/session/{sessionID}/model — switch model (NoContent).
sync_history
POST /sync/history — {data:[]}.
sync_replay
POST /sync/replay — true.
sync_start
POST /sync/start — true (freeze: workspace sync started).
sync_steal
POST /sync/steal — true.
vcs_apply
POST /vcs/apply — apply a patch to the session directory’s git worktree (upstream project/vcs.ts:399): git apply → {applied:true}; not a git worktree → reason:"non-git"; patch rejected → reason:"not-clean". missing patch → 400 payload. The route DOES mutate the worktree.
wait
POST /api/session/{sessionID}/wait — freeze returns ServiceUnavailable (“not available yet”); mirror honestly (no long-poll engine).
workspace_adapter
GET /experimental/workspace/adapter — [{type,name,description}].
workspace_create
POST /experimental/workspace — create a workspace. Freeze requires type and rejects unknown adapters. ocserve registers the built-in adapters and mirrors the error envelope for others.
workspace_delete
DELETE /experimental/workspace/{id} — 204.
workspace_list
GET /experimental/workspace — [].
workspace_status
GET /experimental/workspace/status — [].
workspace_sync_list
POST /experimental/workspace/sync-list — 204.
workspace_warp
POST /experimental/workspace/warp — 400 payload (id required).
worktree_create
POST /experimental/worktree — WorktreeNotGitError on non-git.
worktree_list
GET /experimental/worktree — [].
worktree_remove
DELETE /experimental/worktree — 400 WorktreeRemoveInput required.
worktree_reset
POST /experimental/worktree/reset — 400 payload (directory required).