{ "openapi": "3.1.0", "info": { "title": "turnstone Server API", "version": "1.7.0rc1", "description": "Single-node workstream management, chat interaction, and real-time streaming." }, "paths": { "/v1/api/workstreams": { "get": { "summary": "List active workstreams", "operationId": "v1_api_workstreams_get", "tags": [ "Workstreams" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListWorkstreamsResponse" } } } } } } }, "/v1/api/dashboard": { "get": { "summary": "Dashboard with workstream details and aggregates", "operationId": "v1_api_dashboard_get", "tags": [ "Workstreams" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DashboardResponse" } } } } } } }, "/v1/api/workstreams/new": { "post": { "summary": "Create a new workstream", "operationId": "v1_api_workstreams_new_post", "tags": [ "Workstreams" ], "description": "Accepts two content types. Default is `application/json` with a `CreateWorkstreamRequest` body. Alternatively, `multipart/form-data` with one `meta` field (JSON-encoded `CreateWorkstreamRequest` shape) plus zero-or-more `file` parts saves each file as an attachment under the new workstream. When `initial_message` is also set, attachments are resolved onto that turn before the worker thread dispatches; otherwise they remain pending for a follow-up `POST /v1/api/workstreams/{ws_id}/send`.", "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWorkstreamRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWorkstreamResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Error 409", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "413": { "description": "Error 413", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/close": { "post": { "summary": "Close a workstream", "operationId": "v1_api_workstreams_{ws_id}_close_post", "tags": [ "Workstreams" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CloseWorkstreamRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/send": { "post": { "summary": "Send a user message", "operationId": "v1_api_workstreams_{ws_id}_send_post", "tags": [ "Chat" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SendRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SendResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "delete": { "summary": "Cancel a queued message", "operationId": "v1_api_workstreams_{ws_id}_send_delete", "tags": [ "Chat" ], "description": "Removes a previously-queued message from the workstream's pending queue. Returns ``status: removed`` when the queue had the entry, ``status: not_found`` otherwise.", "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DequeueRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/approve": { "post": { "summary": "Approve or deny a tool call", "operationId": "v1_api_workstreams_{ws_id}_approve_post", "tags": [ "Chat" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApproveRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/command": { "post": { "summary": "Execute a slash command", "operationId": "v1_api_command_post", "tags": [ "Chat" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CommandRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/cancel": { "post": { "summary": "Cancel the active generation in a workstream", "operationId": "v1_api_workstreams_{ws_id}_cancel_post", "tags": [ "Chat" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CancelRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/rewind": { "post": { "summary": "Drop the last N conversation turns (emits clear_ui)", "operationId": "v1_api_workstreams_{ws_id}_rewind_post", "tags": [ "Chat" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RewindRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/retry": { "post": { "summary": "Drop the last response and re-send the last user message", "operationId": "v1_api_workstreams_{ws_id}_retry_post", "tags": [ "Chat" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/events": { "get": { "summary": "Per-workstream SSE event stream", "operationId": "v1_api_workstreams_{ws_id}_events_get", "tags": [ "Streaming" ], "description": "Opens a Server-Sent Events stream scoped to a single workstream. Returns text/event-stream. See API reference for event types.", "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/events/global": { "get": { "summary": "Global SSE event stream", "operationId": "v1_api_events_global_get", "tags": [ "Streaming" ], "description": "Server-Sent Events stream for node-level state broadcasts. Emits a node_snapshot event on connect (workstreams, health, aggregate), followed by real-time delta events (ws_state, ws_activity, ws_created, ws_closed, ws_rename, health_changed, aggregate). Pass ?expected_node_id=X for identity verification (returns 409 on mismatch).", "responses": { "200": { "description": "Success" } } } }, "/v1/api/workstreams/{ws_id}/delete": { "post": { "summary": "Permanently delete a saved workstream", "operationId": "v1_api_workstreams_{ws_id}_delete_post", "tags": [ "Workstreams" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Error 500", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/open": { "post": { "summary": "Load a saved workstream into memory", "operationId": "v1_api_workstreams_{ws_id}_open_post", "tags": [ "Workstreams" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Error 500", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/title": { "post": { "summary": "Set workstream title manually", "operationId": "v1_api_workstreams_{ws_id}_title_post", "tags": [ "Workstreams" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Error 409", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/refresh-title": { "post": { "summary": "Regenerate workstream title via LLM", "operationId": "v1_api_workstreams_{ws_id}_refresh-title_post", "tags": [ "Workstreams" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}": { "get": { "summary": "Get workstream detail (rehydrates lazily on miss)", "operationId": "v1_api_workstreams_{ws_id}_get", "tags": [ "Workstreams" ], "description": "Returns the persisted workstream's display fields. If the session isn't currently in memory the manager rehydrates it before responding; ``500`` on rehydrate failure carries a correlation id matching the server log line. Lifted from the coord-only surface in the Stage 2 history/detail verb lift \u2014 interactive previously had no detail endpoint.", "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkstreamDetailResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Error 500", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/history": { "get": { "summary": "Read the workstream's reconstructed message history", "operationId": "v1_api_workstreams_{ws_id}_history_get", "tags": [ "Workstreams" ], "description": "Returns the tail of the conversation in OpenAI-like message format. Persisted-but-not-loaded workstreams (closed / evicted) serve history without rehydrating. Lifted from the coord-only surface in the Stage 2 history/detail verb lift \u2014 interactive previously only exposed history through the SSE replay on ``/events``.", "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "default": 100 }, "description": "Max conversation rows to fetch from storage (default 100, max 500)." } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WorkstreamHistoryResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Error 500", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/export": { "get": { "summary": "Export the workstream's conversation as OpenAI messages JSON", "operationId": "v1_api_workstreams_{ws_id}_export_get", "tags": [ "Workstreams" ], "description": "Returns the full conversation as an ``{\"messages\": [...]}`` OpenAI Chat Completions envelope, served as a ``.json`` file download (``Content-Disposition: attachment``). Persisted reasoning is surfaced on assistant messages as a ``reasoning_content`` field. Conversation-only \u2014 the parent + per-child zip bundle is exposed only through the ``turnstone-admin export --children`` CLI.", "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "500": { "description": "Error 500", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/attachments": { "post": { "summary": "Upload a file (multipart/form-data, field 'file') and attach it to the caller's next user turn on this workstream. Validates size, MIME, and UTF-8 for text; magic-byte sniff for images. Ownership failures are masked as 404 so non-owners cannot enumerate workstream existence; a 403 indicates a scope/auth failure from the middleware layer.", "operationId": "v1_api_workstreams_{ws_id}_attachments_post", "tags": [ "Attachments" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UploadAttachmentResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Error 403", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Error 409", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "413": { "description": "Error 413", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "get": { "summary": "List the caller's pending (unconsumed) attachments for this workstream. Ownership failures are masked as 404.", "operationId": "v1_api_workstreams_{ws_id}_attachments_get", "tags": [ "Attachments" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListAttachmentsResponse" } } } }, "403": { "description": "Error 403", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/attachments/{attachment_id}/content": { "get": { "summary": "Return raw bytes of an attachment with its stored Content-Type. Ownership failures are masked as 404.", "operationId": "v1_api_workstreams_{ws_id}_attachments_{attachment_id}_content_get", "tags": [ "Attachments" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "attachment_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "403": { "description": "Error 403", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/attachments/{attachment_id}": { "delete": { "summary": "Remove a pending attachment (consumed attachments return 404). Ownership failures are also masked as 404.", "operationId": "v1_api_workstreams_{ws_id}_attachments_{attachment_id}_delete", "tags": [ "Attachments" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "attachment_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "403": { "description": "Error 403", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/{ws_id}/speech-to-text": { "post": { "summary": "Transcribe a short audio clip (multipart/form-data, field 'audio') using the configured STT model role. Returns the transcript for the client to place into the composer; this endpoint never sends on the user's behalf. Returns 503 when no STT role is configured.", "operationId": "v1_api_workstreams_{ws_id}_speech-to-text_post", "tags": [ "Attachments" ], "parameters": [ { "name": "ws_id", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SpeechToTextResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "403": { "description": "Error 403", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "413": { "description": "Error 413", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "502": { "description": "Error 502", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/tts": { "post": { "summary": "Synthesize text to speech audio for browser playback using the configured TTS model role. Returns audio bytes; 503 when no TTS role is configured.", "operationId": "v1_api_tts_post", "tags": [ "Chat" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TextToSpeechRequest" } } } }, "responses": { "200": { "description": "Success" }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "502": { "description": "Error 502", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/workstreams/saved": { "get": { "summary": "List saved workstreams", "operationId": "v1_api_workstreams_saved_get", "tags": [ "Workstreams" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListSavedWorkstreamsResponse" } } } } } } }, "/v1/api/skills": { "get": { "summary": "List available skills (summary)", "operationId": "v1_api_skills_get", "tags": [ "Skills" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListSkillSummaryResponse" } } } } } } }, "/v1/api/personas": { "get": { "summary": "List enabled personas for the workstream-creation picker", "operationId": "v1_api_personas_get", "tags": [ "Personas" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListPersonaChoicesResponse" } } } } } } }, "/v1/api/models": { "get": { "summary": "List available model aliases", "operationId": "v1_api_models_get", "tags": [ "Models" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListAvailableModelsResponse" } } } } } } }, "/v1/api/auth/login": { "post": { "summary": "Authenticate with a token", "operationId": "v1_api_auth_login_post", "tags": [ "Auth" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthLoginRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthLoginResponse" } } } }, "401": { "description": "Error 401", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/auth/setup": { "post": { "summary": "Create first admin user", "operationId": "v1_api_auth_setup_post", "tags": [ "Auth" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthSetupRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthSetupResponse" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "409": { "description": "Error 409", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/auth/status": { "get": { "summary": "Return auth state", "operationId": "v1_api_auth_status_get", "tags": [ "Auth" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthStatusResponse" } } } } } } }, "/v1/api/auth/logout": { "post": { "summary": "Clear auth cookie", "operationId": "v1_api_auth_logout_post", "tags": [ "Auth" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } } } } }, "/v1/api/auth/oidc/authorize": { "get": { "summary": "Redirect to OIDC provider for SSO login", "operationId": "v1_api_auth_oidc_authorize_get", "tags": [ "Auth" ], "responses": { "302": { "description": "Success" }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/auth/oidc/callback": { "get": { "summary": "OIDC callback \u2014 validates code, provisions user, sets JWT cookie, redirects to app", "operationId": "v1_api_auth_oidc_callback_get", "tags": [ "Auth" ], "responses": { "302": { "description": "Success" } } } }, "/v1/api/auth/whoami": { "get": { "summary": "Return authenticated user info and permissions", "operationId": "v1_api_auth_whoami_get", "tags": [ "Auth" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthWhoamiResponse" } } } }, "401": { "description": "Error 401", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/memories": { "get": { "summary": "List structured memories", "operationId": "v1_api_memories_get", "tags": [ "Memories" ], "parameters": [ { "name": "type", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Filter by memory type" }, { "name": "scope", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Filter by scope" }, { "name": "scope_id", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Filter by scope identifier" }, { "name": "limit", "in": "query", "required": false, "schema": { "type": "integer", "default": 100 }, "description": "Max results (default 100, max 200)" } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListMemoriesResponse" } } } } } }, "post": { "summary": "Save (upsert) a structured memory", "operationId": "v1_api_memories_post", "tags": [ "Memories" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SaveMemoryRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MemoryInfo" } } } }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/memories/search": { "post": { "summary": "Search structured memories by query", "operationId": "v1_api_memories_search_post", "tags": [ "Memories" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchMemoriesRequest" } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListMemoriesResponse" } } } } } } }, "/v1/api/memories/{name}": { "delete": { "summary": "Delete a structured memory by name and scope", "operationId": "v1_api_memories_{name}_delete", "tags": [ "Memories" ], "parameters": [ { "name": "name", "in": "path", "required": true, "schema": { "type": "string" } }, { "name": "scope", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Scope (default: global)" }, { "name": "scope_id", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Scope identifier" } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/StatusResponse" } } } }, "404": { "description": "Error 404", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/v1/api/admin/settings": { "get": { "summary": "List interface.* settings with values and sources", "operationId": "v1_api_admin_settings_get", "tags": [ "Admin" ], "responses": { "200": { "description": "Success" } } } }, "/v1/api/admin/settings/{key}": { "put": { "summary": "Update an interface.* setting", "operationId": "v1_api_admin_settings_{key}_put", "tags": [ "Admin" ], "parameters": [ { "name": "key", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } }, "post": { "summary": "Update an interface.* setting (alias for PUT)", "operationId": "v1_api_admin_settings_{key}_post", "tags": [ "Admin" ], "parameters": [ { "name": "key", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": { "200": { "description": "Success" }, "400": { "description": "Error 400", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } }, "503": { "description": "Error 503", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } } } } } }, "/health": { "get": { "summary": "Server health check", "operationId": "health_get", "tags": [ "Observability" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HealthResponse" } } } } } } } }, "components": { "schemas": { "ErrorResponse": { "description": "Standard error response body.", "properties": { "error": { "description": "Error message", "title": "Error", "type": "string" } }, "required": [ "error" ], "title": "ErrorResponse", "type": "object" }, "StatusResponse": { "description": "Generic success response.", "properties": { "status": { "default": "ok", "examples": [ "ok" ], "title": "Status", "type": "string" } }, "title": "StatusResponse", "type": "object" }, "AuthLoginRequest": { "description": "POST /v1/api/auth/login request body.\n\nEither username+password or token must be provided.", "properties": { "username": { "default": "", "description": "Login username", "title": "Username", "type": "string" }, "password": { "default": "", "description": "Login password", "title": "Password", "type": "string" }, "token": { "default": "", "description": "Legacy: bearer token to authenticate", "title": "Token", "type": "string" } }, "title": "AuthLoginRequest", "type": "object" }, "AuthLoginResponse": { "description": "POST /v1/api/auth/login success response.", "properties": { "status": { "default": "ok", "title": "Status", "type": "string" }, "user_id": { "default": "", "description": "Authenticated user ID", "title": "User Id", "type": "string" }, "role": { "description": "Legacy role", "examples": [ "full", "read" ], "title": "Role", "type": "string" }, "scopes": { "default": "", "description": "Comma-separated scopes", "examples": [ "read,write,approve" ], "title": "Scopes", "type": "string" }, "jwt": { "default": "", "description": "JWT session token (if JWT auth is configured)", "title": "Jwt", "type": "string" } }, "required": [ "role" ], "title": "AuthLoginResponse", "type": "object" }, "AuthSetupRequest": { "description": "POST /v1/api/auth/setup request body.", "properties": { "username": { "description": "Login username (1-64 ASCII characters)", "title": "Username", "type": "string" }, "display_name": { "description": "Display name", "title": "Display Name", "type": "string" }, "password": { "description": "Password (minimum 8 characters)", "title": "Password", "type": "string" } }, "required": [ "username", "display_name", "password" ], "title": "AuthSetupRequest", "type": "object" }, "AuthSetupResponse": { "description": "POST /v1/api/auth/setup success response.", "properties": { "status": { "default": "ok", "title": "Status", "type": "string" }, "user_id": { "title": "User Id", "type": "string" }, "username": { "title": "Username", "type": "string" }, "role": { "default": "full", "title": "Role", "type": "string" }, "scopes": { "default": "approve,read,write", "title": "Scopes", "type": "string" }, "jwt": { "default": "", "description": "JWT session token", "title": "Jwt", "type": "string" } }, "required": [ "user_id", "username" ], "title": "AuthSetupResponse", "type": "object" }, "AuthStatusResponse": { "description": "GET /v1/api/auth/status response.", "properties": { "auth_enabled": { "title": "Auth Enabled", "type": "boolean" }, "has_users": { "title": "Has Users", "type": "boolean" }, "setup_required": { "title": "Setup Required", "type": "boolean" }, "oidc_enabled": { "default": false, "title": "Oidc Enabled", "type": "boolean" }, "oidc_provider_name": { "default": "", "title": "Oidc Provider Name", "type": "string" }, "password_enabled": { "default": true, "title": "Password Enabled", "type": "boolean" } }, "required": [ "auth_enabled", "has_users", "setup_required" ], "title": "AuthStatusResponse", "type": "object" }, "SendRequest": { "properties": { "message": { "description": "User message text", "title": "Message", "type": "string" }, "attachment_ids": { "anyOf": [ { "items": { "type": "string" }, "type": "array" }, { "type": "null" } ], "default": null, "description": "Explicit list of attachment ids to inject into this turn. When omitted, any pending attachments for the caller on this workstream are auto-consumed. An empty list disables auto-consumption for this send.", "title": "Attachment Ids" } }, "required": [ "message" ], "title": "SendRequest", "type": "object" }, "SendResponse": { "properties": { "status": { "description": "'ok', 'busy', 'queued', or 'queue_full'", "examples": [ "ok", "busy", "queued", "queue_full" ], "title": "Status", "type": "string" }, "attached_ids": { "description": "Attachment ids actually attached to this turn. Subset of the request's `attachment_ids` (or the auto-consumed pending set). Empty when the send carries no attachments.", "items": { "type": "string" }, "title": "Attached Ids", "type": "array" }, "dropped_attachment_ids": { "description": "Attachment ids the caller requested that the server could not reserve (lost a race, already consumed, or cross-scope). The request still proceeds with whatever was reserved; the client can retry uploads or surface a partial-attach warning.", "items": { "type": "string" }, "title": "Dropped Attachment Ids", "type": "array" }, "priority": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "Set on `queued` responses: relative priority of the queued message.", "title": "Priority" }, "msg_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "Set on `queued` responses: id used to dequeue the message.", "title": "Msg Id" } }, "required": [ "status" ], "title": "SendResponse", "type": "object" }, "DequeueRequest": { "description": "Body for ``DELETE /v1/api/workstreams/{ws_id}/send``.\n\nRemoves a previously-queued message from the workstream's pending\nqueue. ``msg_id`` is the id returned in a prior ``send`` response\nwhen the workstream was busy and the message was queued.", "properties": { "msg_id": { "description": "Id of the queued message to remove", "title": "Msg Id", "type": "string" } }, "required": [ "msg_id" ], "title": "DequeueRequest", "type": "object" }, "ApproveRequest": { "properties": { "approved": { "description": "True to approve, false to deny", "title": "Approved", "type": "boolean" }, "feedback": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "Optional denial reason", "title": "Feedback" }, "always": { "default": false, "description": "Auto-approve the tools in this batch going forward", "title": "Always", "type": "boolean" } }, "required": [ "approved" ], "title": "ApproveRequest", "type": "object" }, "CommandRequest": { "properties": { "command": { "description": "Slash command (e.g. /clear, /new, /resume)", "title": "Command", "type": "string" }, "ws_id": { "description": "Target workstream ID", "title": "Ws Id", "type": "string" } }, "required": [ "command", "ws_id" ], "title": "CommandRequest", "type": "object" }, "CancelRequest": { "properties": { "force": { "default": false, "description": "Force cancel: abandon the stuck worker thread immediately. Use when cooperative cancel has not resolved within a few seconds.", "title": "Force", "type": "boolean" } }, "title": "CancelRequest", "type": "object" }, "RewindRequest": { "properties": { "turns": { "description": "Number of conversation turns (user message + its responses) to drop from the end. Clamped to the available turn count.", "minimum": 1, "title": "Turns", "type": "integer" } }, "required": [ "turns" ], "title": "RewindRequest", "type": "object" }, "CreateWorkstreamRequest": { "properties": { "name": { "default": "", "description": "Workstream display name (auto-generated if empty)", "title": "Name", "type": "string" }, "model": { "default": "", "description": "Model alias from registry", "title": "Model", "type": "string" }, "auto_approve": { "default": false, "description": "Auto-approve all tool calls", "title": "Auto Approve", "type": "boolean" }, "resume_ws": { "default": "", "description": "Workstream ID to resume atomically during creation (empty = fresh start)", "title": "Resume Ws", "type": "string" }, "skill": { "default": "", "description": "Skill name (replaces default skills)", "title": "Skill", "type": "string" }, "persona": { "default": "", "description": "Persona name (slug) to create the workstream with. Resolved and snapshotted at creation \u2014 later persona edits never affect this workstream. Empty selects the kind's default persona; on a database with no personas seeded the workstream is created with legacy (unrestricted) behavior.", "title": "Persona", "type": "string" }, "notify_targets": { "anyOf": [ { "type": "string" }, { "items": { "additionalProperties": { "type": "string" }, "type": "object" }, "type": "array" } ], "default": "[]", "description": "Notification targets, accepted as either a JSON string or a structured array of objects containing channel_type + channel_id/user_id", "title": "Notify Targets" }, "client_type": { "default": "", "description": "Client surface type (web, cli, chat). Defaults to web for server-created sessions.", "title": "Client Type", "type": "string" }, "initial_message": { "default": "", "description": "Optional first user message dispatched as a background turn after the workstream is created. When attachments are also provided (via the multipart variant), they are attached to this turn.", "title": "Initial Message", "type": "string" }, "ws_id": { "default": "", "description": "Optional caller-supplied workstream id (32-hex). Required when creating with attachments via the cluster routing layer so the console can hash to the owning node before the multipart body lands. Auto-generated when omitted.", "title": "Ws Id", "type": "string" }, "kind": { "$ref": "#/components/schemas/WorkstreamKind", "default": "interactive", "description": "Workstream kind \u2014 'interactive' (default) or 'coordinator'. Coordinator workstreams are created by the console's own /v1/api/workstreams/new endpoint; clients hitting /v1/api/workstreams/new should leave this at the default." }, "parent_ws_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "Optional parent workstream id. Populated on children spawned by a coordinator so the parent/child relationship survives restart and appears in audit / list views.", "title": "Parent Ws Id" }, "project_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "Optional project to attach this workstream to. Drives the shared 'project' memory scope; coordinator children inherit the parent's project.", "title": "Project Id" } }, "title": "CreateWorkstreamRequest", "type": "object" }, "WorkstreamKind": { "description": "Classifier for which manager hosts a workstream.\n\nStrEnum so members are drop-in ``str`` replacements for the DB column,\nJSON payloads, and existing ``==`` comparisons against raw strings.\nNarrow internal annotations to this type; wide boundaries (HTTP body,\nDB row) stay ``str`` and parse via ``WorkstreamKind(raw)`` / ``from_raw``\nat the edge.", "enum": [ "interactive", "coordinator" ], "title": "WorkstreamKind", "type": "string" }, "CreateWorkstreamResponse": { "properties": { "ws_id": { "description": "Unique ID of the new workstream", "title": "Ws Id", "type": "string" }, "name": { "description": "Assigned workstream name", "title": "Name", "type": "string" }, "resumed": { "default": false, "description": "Whether a previous workstream was resumed", "title": "Resumed", "type": "boolean" }, "message_count": { "default": 0, "description": "Number of messages in the resumed workstream", "title": "Message Count", "type": "integer" }, "attachment_ids": { "description": "Ids of attachments saved by this request (multipart variant only). Already attached to the initial_message turn when one was provided; otherwise left pending for a follow-up POST /v1/api/workstreams/{ws_id}/send.", "items": { "type": "string" }, "title": "Attachment Ids", "type": "array" }, "initial_message_status": { "anyOf": [ { "enum": [ "queue_full", "refused_closed" ], "type": "string" }, { "type": "null" } ], "default": null, "description": "Present ONLY when the workstream was created but its initial_message could not be delivered: 'queue_full' (a raced live worker's interjection queue was at capacity \u2014 resend via /send; any uploads stay staged) or 'refused_closed' (the workstream was closed mid-create). Absent whenever the message was dispatched.", "title": "Initial Message Status" } }, "required": [ "ws_id", "name" ], "title": "CreateWorkstreamResponse", "type": "object" }, "CloseWorkstreamRequest": { "description": "Body for ``POST /v1/api/workstreams/{ws_id}/close``.\n\nThe body must be valid JSON; send ``{}`` when omitting all\nfields. Pre-1.5 the model also carried a body-keyed ``ws_id``;\n1.5 moved that to the path so the body shrinks to the optional\n``reason``. Coord ignores the body entirely (its close handler\nis wired ``supports_close_reason=False``).", "properties": { "reason": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "description": "Optional close reason persisted to ``workstream_config`` for postmortem. Capped at 512 UTF-8 bytes server-side; credential-redaction is applied via the output guard.", "title": "Reason" } }, "title": "CloseWorkstreamRequest", "type": "object" }, "ListWorkstreamsResponse": { "description": "Response body for ``GET /v1/api/workstreams`` on either kind.\n\nTop-level key is ``workstreams`` regardless of the kind serving\nthe request \u2014 pre-lift coord returned ``{\"coordinators\": [...]}``;\nconvergence lifted both kinds onto the same shape. Coord SDK /\nfrontend consumers branching on ``data.coordinators`` swap to\n``data.workstreams``.", "properties": { "workstreams": { "items": { "$ref": "#/components/schemas/WorkstreamInfo" }, "title": "Workstreams", "type": "array" } }, "required": [ "workstreams" ], "title": "ListWorkstreamsResponse", "type": "object" }, "WorkstreamInfo": { "description": "Active-list row shape, shared across both kinds.\n\nRenamed ``id`` \u2192 ``ws_id`` and added ``user_id`` in the Stage 2\n``list``/``saved`` verb lift so the active-list response shape\nmatches the rest of the v1 surface (every other shared verb's\npayload uses ``ws_id``). ``user_id`` was previously coord-only;\ninteractive now populates it too. SDK consumers reading\n``row.id`` should swap to ``row.ws_id``.", "properties": { "ws_id": { "title": "Ws Id", "type": "string" }, "name": { "title": "Name", "type": "string" }, "state": { "title": "State", "type": "string" }, "kind": { "$ref": "#/components/schemas/WorkstreamKind", "default": "interactive" }, "parent_ws_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Parent Ws Id" }, "user_id": { "default": "", "title": "User Id", "type": "string" }, "project_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Project Id" } }, "required": [ "ws_id", "name", "state" ], "title": "WorkstreamInfo", "type": "object" }, "WorkstreamDetailResponse": { "description": "Response body for ``GET /v1/api/workstreams/{ws_id}``.\n\nRenamed and relocated from ``CoordinatorDetailResponse`` in the\nStage 2 history/detail verb lift. Both kinds populate every field;\nSDK consumers don't branch on kind to read them. The lift adds the\nendpoint to interactive as a feature gain (pre-lift only coord\nexposed it).", "properties": { "ws_id": { "title": "Ws Id", "type": "string" }, "name": { "title": "Name", "type": "string" }, "state": { "title": "State", "type": "string" }, "user_id": { "title": "User Id", "type": "string" }, "kind": { "$ref": "#/components/schemas/WorkstreamKind", "default": "interactive" }, "pending_approval": { "default": false, "description": "True when at least one approval cycle is live (a gate thread parked awaiting an operator approve/deny). Mirrors the same field on ``DashboardWorkstream`` / cluster live projections so a freshly-loaded chat tab can render the inline approval gate from the detail snapshot before SSE replay arrives.", "title": "Pending Approval", "type": "boolean" }, "pending_approval_details": { "description": "Inline approval payloads, one per live cycle, oldest first \u2014 same shape as ``DashboardWorkstream.pending_approval_details``. Empty when no approval is pending. Lets a reload paint every action row + judge verdicts immediately instead of relying on the SSE approve_request replay timing window. Replaces 1.6's ``pending_approval_detail`` single-object field (breaking, 1.7).", "items": { "$ref": "#/components/schemas/PendingApprovalDetail" }, "title": "Pending Approval Details", "type": "array" } }, "required": [ "ws_id", "name", "state", "user_id" ], "title": "WorkstreamDetailResponse", "type": "object" }, "PendingApprovalDetail": { "description": "Inline approval payload merged into ``DashboardWorkstream``.\n\nOne entry per live approval CYCLE \u2014 a gate thread parked in\n``approve_tools`` awaiting the operator. Parallel task agents run\nconcurrent gates, so a workstream can have several of these at\nonce (``pending_approval_details``, oldest first). Cross-tenant\nexposure here follows the same trusted-team posture as\n``activity`` / ``tokens`` \u2014 see ``server.py``'s ``dashboard``\nhandler comment.", "properties": { "cycle_id": { "default": "", "description": "Identity of this approval cycle. Echo it back on ``POST /v1/api/workstreams/{ws_id}/approve`` to resolve exactly this round \u2014 required for correctness when several cycles are live (parallel task agents).", "title": "Cycle Id", "type": "string" }, "call_id": { "default": "", "description": "Primary call_id \u2014 first non-empty call_id in items list order. Matches the 409 ``current_call_id`` response from ``POST /v1/api/workstreams/{ws_id}/approve`` so the UI can render the same identifier the server reports as current.", "title": "Call Id", "type": "string" }, "judge_pending": { "default": false, "description": "LLM judge tier still running; heuristic verdicts may already be present on items.", "title": "Judge Pending", "type": "boolean" }, "items": { "items": { "$ref": "#/components/schemas/PendingApprovalItem" }, "title": "Items", "type": "array" } }, "title": "PendingApprovalDetail", "type": "object" }, "PendingApprovalItem": { "description": "One pending tool-call inside a ``PendingApprovalDetail`` envelope.\n\nMirrors the dict ``SessionUIBase.serialize_pending_approval_details``\nemits per item inside each cycle entry. ``heuristic_verdict`` / ``judge_verdict`` are kept\nloosely-typed because the underlying verdict shape varies by tier;\nconsumers that want the full structure can decode against\n:class:`turnstone.sdk.events.IntentVerdictEvent`.", "properties": { "call_id": { "default": "", "title": "Call Id", "type": "string" }, "header": { "default": "", "title": "Header", "type": "string" }, "preview": { "default": "", "title": "Preview", "type": "string" }, "func_name": { "default": "", "title": "Func Name", "type": "string" }, "approval_label": { "default": "", "title": "Approval Label", "type": "string" }, "needs_approval": { "default": false, "title": "Needs Approval", "type": "boolean" }, "error": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Error" }, "heuristic_verdict": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Heuristic Verdict" }, "judge_verdict": { "anyOf": [ { "additionalProperties": true, "type": "object" }, { "type": "null" } ], "default": null, "title": "Judge Verdict" } }, "title": "PendingApprovalItem", "type": "object" }, "WorkstreamHistoryResponse": { "description": "Response body for ``GET /v1/api/workstreams/{ws_id}/history``.\n\nRenamed and relocated from ``CoordinatorHistoryResponse`` in the\nStage 2 history/detail verb lift. Same projected render shape on\nboth kinds; the lift adds the endpoint to interactive as a feature\ngain (pre-lift interactive only exposed history through the SSE\nreplay on ``/events``).", "properties": { "ws_id": { "title": "Ws Id", "type": "string" }, "messages": { "description": "Tail of the workstream's message history, projected to the canonical render shape (``role`` may be ``system`` for operator-context turns; flat tool_calls with verdict / output_assessment; top-level source / attachments / reasoning; derived denied / is_error / pending). Bounded by the ``limit`` query parameter (default 100, max 500).", "items": { "additionalProperties": true, "type": "object" }, "title": "Messages", "type": "array" }, "cursor": { "anyOf": [ { "type": "integer" }, { "type": "null" } ], "default": null, "description": "SSE resume cursor (a ``Last-Event-ID`` value). Non-null only when the trailing turn is an executing in-flight tool batch that the live ring buffer can replay: ``messages`` then omits that turn and the client opens its initial SSE with this cursor so the existing delta replay fast-forwards the in-flight turn (tool calls, results, prompts) instead of the lossy synthetic snapshot. Null on every other read \u2014 the client connects fresh.", "title": "Cursor" } }, "required": [ "ws_id" ], "title": "WorkstreamHistoryResponse", "type": "object" }, "DashboardResponse": { "properties": { "workstreams": { "items": { "$ref": "#/components/schemas/DashboardWorkstream" }, "title": "Workstreams", "type": "array" }, "aggregate": { "$ref": "#/components/schemas/DashboardAggregate" } }, "required": [ "workstreams", "aggregate" ], "title": "DashboardResponse", "type": "object" }, "DashboardAggregate": { "properties": { "total_tokens": { "default": 0, "title": "Total Tokens", "type": "integer" }, "total_tool_calls": { "default": 0, "title": "Total Tool Calls", "type": "integer" }, "active_count": { "default": 0, "title": "Active Count", "type": "integer" }, "total_count": { "default": 0, "title": "Total Count", "type": "integer" }, "uptime_seconds": { "default": 0, "title": "Uptime Seconds", "type": "integer" }, "node": { "default": "local", "title": "Node", "type": "string" } }, "title": "DashboardAggregate", "type": "object" }, "DashboardWorkstream": { "description": "Dashboard row shape for ``GET /v1/api/dashboard``.\n\nRenamed ``id`` \u2192 ``ws_id`` for v1 row-shape consistency with\nthe rest of the workstream surface (active list, saved list,\nhistory, detail, etc.). Frontend consumers reading\n``dashboard.workstreams[].id`` swap to ``.ws_id``.", "properties": { "ws_id": { "title": "Ws Id", "type": "string" }, "name": { "title": "Name", "type": "string" }, "state": { "title": "State", "type": "string" }, "title": { "default": "", "title": "Title", "type": "string" }, "tokens": { "default": 0, "title": "Tokens", "type": "integer" }, "context_ratio": { "default": 0.0, "title": "Context Ratio", "type": "number" }, "activity": { "default": "", "title": "Activity", "type": "string" }, "activity_state": { "default": "", "title": "Activity State", "type": "string" }, "tool_calls": { "default": 0, "title": "Tool Calls", "type": "integer" }, "node": { "default": "", "title": "Node", "type": "string" }, "model": { "default": "", "title": "Model", "type": "string" }, "model_alias": { "default": "", "title": "Model Alias", "type": "string" }, "kind": { "$ref": "#/components/schemas/WorkstreamKind", "default": "interactive" }, "parent_ws_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Parent Ws Id" }, "user_id": { "default": "", "title": "User Id", "type": "string" }, "project_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Project Id" }, "pending_approval_details": { "description": "Inline approval payload for the coordinator children-tree UI: EVERY live approval cycle, oldest first \u2014 parallel task agents gate concurrently, so a workstream can hold several prompts at once. Each entry carries the cycle's items + per-call_id LLM verdict cache so a coord can render approve/deny buttons + judge pill without a separate per-child round-trip; resolve each with its ``cycle_id``. Empty when no approval is pending. Also surfaced (verbatim) on ``GET /v1/api/cluster/ws/live`` via the ``_CLUSTER_WS_LIVE_KEYS`` projection. Replaces 1.6's ``pending_approval_detail`` single-object field (breaking, 1.7).", "items": { "$ref": "#/components/schemas/PendingApprovalDetail" }, "title": "Pending Approval Details", "type": "array" }, "recent_auto_approvals": { "description": "Per-ws ring buffer (cap 10) of recent tool calls that bypassed the operator approval gate. Surfaces ``WebUI._recent_auto_approvals`` so the coord-tree row can render an 'auto-approved by ...' pill when the child's skill / blanket / admin-policy rules silently let a tool through. Also projected onto ``GET /v1/api/cluster/ws/live`` via ``_CLUSTER_WS_LIVE_KEYS``.", "items": { "$ref": "#/components/schemas/RecentAutoApproval" }, "title": "Recent Auto Approvals", "type": "array" } }, "required": [ "ws_id", "name", "state" ], "title": "DashboardWorkstream", "type": "object" }, "RecentAutoApproval": { "description": "One ring-buffer entry for ``DashboardWorkstream.recent_auto_approvals``.\n\nRecords a tool call that bypassed the operator approval gate\n(admin tool policy / skill ``allowed_tools`` allowlist / blanket\n``auto_approve`` / \"Approve + Always\" memory). The coord-tree\npill reads this list to surface \"auto-approved by skill X\" so\nthe operator can see WHICH calls bypassed and WHY.", "properties": { "call_id": { "default": "", "title": "Call Id", "type": "string" }, "func_name": { "default": "", "title": "Func Name", "type": "string" }, "approval_label": { "default": "", "title": "Approval Label", "type": "string" }, "auto_approve_reason": { "default": "", "description": "Source that fired the bypass. ``skill`` (skill template's ``allowed_tools``), ``always`` (user 'Approve + Always' click), ``policy`` (admin tool-policy ``allow`` rule), ``blanket`` (workstream-level ``auto_approve=True``), ``smart_approval`` (Smart Approvals: high-confidence LLM judge ``approve`` verdict), or ``auto_approve_tools`` (legacy / unknown writer).", "title": "Auto Approve Reason", "type": "string" }, "ts": { "default": 0.0, "description": "Unix epoch seconds when the auto-approve fired.", "title": "Ts", "type": "number" } }, "title": "RecentAutoApproval", "type": "object" }, "ListSavedWorkstreamsResponse": { "properties": { "workstreams": { "items": { "$ref": "#/components/schemas/SavedWorkstreamInfo" }, "title": "Workstreams", "type": "array" } }, "required": [ "workstreams" ], "title": "ListSavedWorkstreamsResponse", "type": "object" }, "SavedWorkstreamInfo": { "properties": { "ws_id": { "title": "Ws Id", "type": "string" }, "alias": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Alias" }, "title": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Title" }, "created": { "title": "Created", "type": "string" }, "updated": { "title": "Updated", "type": "string" }, "message_count": { "title": "Message Count", "type": "integer" }, "state": { "default": "idle", "title": "State", "type": "string" }, "kind": { "$ref": "#/components/schemas/WorkstreamKind", "default": "interactive" }, "node_id": { "default": "", "title": "Node Id", "type": "string" }, "model_alias": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Model Alias" }, "launch_skill": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Launch Skill" }, "child_count": { "default": 0, "title": "Child Count", "type": "integer" }, "context_tokens": { "default": 0, "title": "Context Tokens", "type": "integer" }, "context_ratio": { "default": 0.0, "title": "Context Ratio", "type": "number" }, "project_id": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Project Id" }, "persona": { "anyOf": [ { "type": "string" }, { "type": "null" } ], "default": null, "title": "Persona" } }, "required": [ "ws_id", "created", "updated", "message_count" ], "title": "SavedWorkstreamInfo", "type": "object" }, "UploadAttachmentResponse": { "description": "Returned after a successful upload.", "properties": { "attachment_id": { "description": "Opaque id for this attachment", "title": "Attachment Id", "type": "string" }, "filename": { "description": "Original upload filename", "title": "Filename", "type": "string" }, "mime_type": { "description": "Canonicalized MIME type", "title": "Mime Type", "type": "string" }, "size_bytes": { "description": "Payload size in bytes", "title": "Size Bytes", "type": "integer" }, "kind": { "description": "'image', 'text', 'pdf', or 'audio'", "examples": [ "image", "text", "pdf", "audio" ], "title": "Kind", "type": "string" } }, "required": [ "attachment_id", "filename", "mime_type", "size_bytes", "kind" ], "title": "UploadAttachmentResponse", "type": "object" }, "ListAttachmentsResponse": { "properties": { "attachments": { "description": "Pending (unconsumed) attachments for caller+workstream", "items": { "$ref": "#/components/schemas/AttachmentInfo" }, "title": "Attachments", "type": "array" } }, "required": [ "attachments" ], "title": "ListAttachmentsResponse", "type": "object" }, "AttachmentInfo": { "properties": { "attachment_id": { "description": "Opaque id for this attachment", "title": "Attachment Id", "type": "string" }, "filename": { "description": "Original upload filename", "title": "Filename", "type": "string" }, "mime_type": { "description": "Canonicalized MIME type", "title": "Mime Type", "type": "string" }, "size_bytes": { "description": "Payload size in bytes", "title": "Size Bytes", "type": "integer" }, "kind": { "description": "'image', 'text', 'pdf', or 'audio'", "examples": [ "image", "text", "pdf", "audio" ], "title": "Kind", "type": "string" } }, "required": [ "attachment_id", "filename", "mime_type", "size_bytes", "kind" ], "title": "AttachmentInfo", "type": "object" }, "SpeechToTextResponse": { "description": "Transcript returned for the browser to place into the composer.", "properties": { "status": { "default": "ok", "description": "Request outcome", "title": "Status", "type": "string" }, "transcript": { "description": "Transcribed text", "title": "Transcript", "type": "string" }, "model_alias": { "default": "", "description": "STT role alias used", "title": "Model Alias", "type": "string" } }, "required": [ "transcript" ], "title": "SpeechToTextResponse", "type": "object" }, "TextToSpeechRequest": { "properties": { "text": { "description": "Text to synthesize", "title": "Text", "type": "string" }, "voice": { "default": "", "description": "Optional voice override (else audio.tts_voice setting)", "title": "Voice", "type": "string" } }, "required": [ "text" ], "title": "TextToSpeechRequest", "type": "object" }, "HealthResponse": { "properties": { "status": { "examples": [ "ok", "degraded" ], "title": "Status", "type": "string" }, "version": { "default": "", "title": "Version", "type": "string" }, "node_id": { "default": "", "title": "Node Id", "type": "string" }, "uptime_seconds": { "default": 0.0, "title": "Uptime Seconds", "type": "number" }, "model": { "default": "", "title": "Model", "type": "string" }, "max_ws": { "default": 10, "description": "Maximum concurrent workstreams", "title": "Max Ws", "type": "integer" }, "workstreams": { "$ref": "#/components/schemas/WorkstreamCounts", "default": { "total": 0, "idle": 0, "thinking": 0, "running": 0, "attention": 0, "error": 0 } }, "backend": { "anyOf": [ { "$ref": "#/components/schemas/BackendStatus" }, { "type": "null" } ], "default": null }, "mcp": { "anyOf": [ { "$ref": "#/components/schemas/McpStatus" }, { "type": "null" } ], "default": null } }, "required": [ "status" ], "title": "HealthResponse", "type": "object" }, "BackendStatus": { "properties": { "status": { "examples": [ "up", "down" ], "title": "Status", "type": "string" } }, "required": [ "status" ], "title": "BackendStatus", "type": "object" }, "McpStatus": { "properties": { "servers": { "default": 0, "title": "Servers", "type": "integer" }, "resources": { "default": 0, "title": "Resources", "type": "integer" }, "prompts": { "default": 0, "title": "Prompts", "type": "integer" } }, "title": "McpStatus", "type": "object" }, "WorkstreamCounts": { "properties": { "total": { "default": 0, "title": "Total", "type": "integer" }, "idle": { "default": 0, "title": "Idle", "type": "integer" }, "thinking": { "default": 0, "title": "Thinking", "type": "integer" }, "running": { "default": 0, "title": "Running", "type": "integer" }, "attention": { "default": 0, "title": "Attention", "type": "integer" }, "error": { "default": 0, "title": "Error", "type": "integer" } }, "title": "WorkstreamCounts", "type": "object" }, "SaveMemoryRequest": { "properties": { "name": { "description": "Memory identifier (normalized to snake_case)", "title": "Name", "type": "string" }, "content": { "description": "Memory content", "maxLength": 65536, "title": "Content", "type": "string" }, "description": { "default": "", "description": "Short description for relevance matching", "title": "Description", "type": "string" }, "type": { "default": "general", "description": "Memory type", "enum": [ "user", "general", "feedback", "reference" ], "title": "Type", "type": "string" }, "scope": { "default": "global", "description": "Memory scope", "enum": [ "global", "workstream", "user" ], "title": "Scope", "type": "string" }, "scope_id": { "default": "", "description": "Scope identifier (ws_id for workstream, user_id for user scope)", "title": "Scope Id", "type": "string" } }, "required": [ "name", "content" ], "title": "SaveMemoryRequest", "type": "object" }, "MemoryInfo": { "properties": { "memory_id": { "title": "Memory Id", "type": "string" }, "name": { "title": "Name", "type": "string" }, "description": { "default": "", "title": "Description", "type": "string" }, "type": { "enum": [ "user", "general", "feedback", "reference" ], "title": "Type", "type": "string" }, "scope": { "enum": [ "global", "workstream", "user" ], "title": "Scope", "type": "string" }, "scope_id": { "default": "", "title": "Scope Id", "type": "string" }, "content": { "title": "Content", "type": "string" }, "created": { "title": "Created", "type": "string" }, "updated": { "title": "Updated", "type": "string" } }, "required": [ "memory_id", "name", "type", "scope", "content", "created", "updated" ], "title": "MemoryInfo", "type": "object" }, "ListMemoriesResponse": { "properties": { "memories": { "items": { "$ref": "#/components/schemas/MemoryInfo" }, "title": "Memories", "type": "array" }, "total": { "default": 0, "title": "Total", "type": "integer" } }, "required": [ "memories" ], "title": "ListMemoriesResponse", "type": "object" }, "SearchMemoriesRequest": { "properties": { "query": { "description": "Search query text", "title": "Query", "type": "string" }, "type": { "default": "", "description": "Filter by memory type", "enum": [ "", "user", "general", "feedback", "reference" ], "title": "Type", "type": "string" }, "scope": { "default": "", "description": "Filter by scope", "enum": [ "", "global", "workstream", "user" ], "title": "Scope", "type": "string" }, "scope_id": { "default": "", "description": "Filter by scope_id", "title": "Scope Id", "type": "string" }, "limit": { "default": 20, "description": "Max results (1-50)", "maximum": 50, "minimum": 1, "title": "Limit", "type": "integer" } }, "required": [ "query" ], "title": "SearchMemoriesRequest", "type": "object" }, "SkillSummary": { "properties": { "name": { "description": "Skill name", "title": "Name", "type": "string" }, "category": { "default": "", "description": "Skill category", "title": "Category", "type": "string" }, "description": { "default": "", "description": "Skill description for discovery", "title": "Description", "type": "string" }, "tags": { "description": "Semantic tags", "items": { "type": "string" }, "title": "Tags", "type": "array" }, "is_default": { "default": false, "description": "Whether auto-applied to all sessions", "title": "Is Default", "type": "boolean" }, "activation": { "default": "named", "description": "Activation mode: default, named, search", "title": "Activation", "type": "string" }, "origin": { "default": "manual", "description": "Source: manual, mcp, skills.sh, github", "title": "Origin", "type": "string" }, "author": { "default": "", "description": "Skill author", "title": "Author", "type": "string" }, "version": { "default": "1.0.0", "description": "Skill version", "title": "Version", "type": "string" } }, "required": [ "name" ], "title": "SkillSummary", "type": "object" }, "ListSkillSummaryResponse": { "properties": { "skills": { "items": { "$ref": "#/components/schemas/SkillSummary" }, "title": "Skills", "type": "array" } }, "required": [ "skills" ], "title": "ListSkillSummaryResponse", "type": "object" }, "PersonaChoice": { "description": "Display fields for the creation picker \u2014 the persona's levers\n(prompt / tool set / toggles) deliberately stay server-side.", "properties": { "name": { "description": "Persona slug, the value to pass as CreateWorkstreamRequest.persona", "title": "Name", "type": "string" }, "display_name": { "default": "", "description": "Human-readable name", "title": "Display Name", "type": "string" }, "description": { "default": "", "description": "What this persona is for", "title": "Description", "type": "string" }, "applies_to_kinds": { "description": "Workstream kinds this persona can be attached to", "items": { "type": "string" }, "title": "Applies To Kinds", "type": "array" }, "is_default": { "default": false, "description": "Whether an empty persona field resolves to this one", "title": "Is Default", "type": "boolean" } }, "required": [ "name" ], "title": "PersonaChoice", "type": "object" }, "ListPersonaChoicesResponse": { "properties": { "personas": { "items": { "$ref": "#/components/schemas/PersonaChoice" }, "title": "Personas", "type": "array" }, "total": { "default": 0, "title": "Total", "type": "integer" } }, "title": "ListPersonaChoicesResponse", "type": "object" }, "AvailableModelInfo": { "properties": { "alias": { "title": "Alias", "type": "string" }, "model": { "title": "Model", "type": "string" }, "provider": { "title": "Provider", "type": "string" }, "capabilities": { "additionalProperties": true, "description": "Operator-set capability flags for this alias (e.g. supports_transcription)", "title": "Capabilities", "type": "object" } }, "required": [ "alias", "model", "provider" ], "title": "AvailableModelInfo", "type": "object" }, "ListAvailableModelsResponse": { "properties": { "models": { "items": { "$ref": "#/components/schemas/AvailableModelInfo" }, "title": "Models", "type": "array" }, "default_alias": { "default": "", "title": "Default Alias", "type": "string" }, "channel_default_alias": { "default": "", "title": "Channel Default Alias", "type": "string" }, "judge_default_alias": { "default": "", "title": "Judge Default Alias", "type": "string" }, "stt_default_alias": { "default": "", "description": "Effective speech-to-text role alias (blank = voice input disabled)", "title": "Stt Default Alias", "type": "string" }, "tts_default_alias": { "default": "", "description": "Effective text-to-speech role alias (blank = voice output disabled)", "title": "Tts Default Alias", "type": "string" } }, "title": "ListAvailableModelsResponse", "type": "object" } } } }