Files
turnstone/sdk/typescript/openapi-console.json
T
Patrick Buckley 2f7f70825b feat: wire prompt templates into session startup with full creation-p… (#47)
* feat: wire prompt templates into session startup with full creation-path support

Prompt templates (prompt_templates table) now have runtime effect:

- is_default=true templates auto-apply as system message content,
  concatenated in name order before user instructions
- Per-workstream template selection via --template CLI flag, template
  field on POST /v1/api/workstreams/new, console creation modal dropdown,
  scheduled task config, and channel adapter config
- {{model}}, {{ws_id}}, {{node_id}} variable substitution via single-pass
  regex (prevents cross-variable injection)
- /template slash command for runtime switching, persisted across resume
- set_template() public API on ChatSession

Security hardening:
- MCP sync resets is_default=False on content update (prevents compromised
  server from injecting defaults)
- 32KB content cap on template create/update + defensive truncation
- Template existence validation returns 400 before workstream creation
- Single-pass regex eliminates cross-variable expansion

Template field plumbed through all creation paths: CLI, server API, MQ
protocol/bridge, console backend, scheduler dispatch, channel router,
MQ client. Migration 010 adds template column to scheduled_tasks.

Frontend: console workstream modal template dropdown, scheduler
create/edit template field, governance template UI variables auto-detected
from content (read-only display replaces editable input). Focus trap and
Enter-key accessibility fixes in workstream modal.

Docs: governance.md template runtime section, api-reference.md template
field, governance + MCP architecture diagrams updated.

Python + TypeScript SDKs, Pydantic schemas all updated. 29 new tests.

* fix: address PR #47 review feedback

- Defer template validation until after resume_ws — a bad template name
  no longer 400s when resume would have ignored it anyway
- Add template field to OpenAPI JSON specs (openapi-server.json,
  openapi-console.json) for SDK/docs consistency
- Validate template existence in schedule create and update endpoints —
  reject unknown template names with 400 instead of allowing schedules
  that would silently fail at dispatch time
2026-03-12 16:57:31 -07:00

827 lines
21 KiB
JSON

{
"openapi": "3.1.0",
"info": {
"title": "turnstone Console API",
"version": "0.3.0",
"description": "Cluster-wide visibility and control across all turnstone nodes."
},
"paths": {
"/v1/api/cluster/overview": {
"get": {
"summary": "Cluster state summary",
"operationId": "v1_api_cluster_overview_get",
"tags": ["Cluster"],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ClusterOverviewResponse"
}
}
}
}
}
}
},
"/v1/api/cluster/nodes": {
"get": {
"summary": "Paginated node list",
"operationId": "v1_api_cluster_nodes_get",
"tags": ["Cluster"],
"parameters": [
{
"name": "sort",
"in": "query",
"required": false,
"schema": {
"type": "string",
"default": "activity",
"enum": ["activity", "tokens", "name"]
},
"description": "Sort field"
},
{
"name": "limit",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 100
},
"description": "Page size"
},
{
"name": "offset",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 0
},
"description": "Pagination offset"
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ClusterNodesResponse"
}
}
}
}
}
}
},
"/v1/api/cluster/workstreams": {
"get": {
"summary": "Filtered workstream list",
"operationId": "v1_api_cluster_workstreams_get",
"tags": ["Cluster"],
"parameters": [
{
"name": "state",
"in": "query",
"required": false,
"schema": {
"type": "string",
"enum": ["running", "thinking", "attention", "idle", "error"]
},
"description": "Filter by state"
},
{
"name": "node",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "Filter by node_id"
},
{
"name": "search",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "Search in name/title/node"
},
{
"name": "sort",
"in": "query",
"required": false,
"schema": {
"type": "string",
"default": "state",
"enum": ["state", "tokens", "name"]
},
"description": "Sort field"
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 1
},
"description": "Page number"
},
{
"name": "per_page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 50
},
"description": "Items per page (max 200)"
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ClusterWorkstreamsResponse"
}
}
}
}
}
}
},
"/v1/api/cluster/node/{node_id}": {
"get": {
"summary": "Single node detail",
"operationId": "v1_api_cluster_node_{node_id}_get",
"tags": ["Cluster"],
"parameters": [
{
"name": "node_id",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NodeDetailResponse"
}
}
}
},
"404": {
"description": "Error 404",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/v1/api/cluster/workstreams/new": {
"post": {
"summary": "Create workstream via MQ dispatch",
"operationId": "v1_api_cluster_workstreams_new_post",
"tags": ["Cluster"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConsoleCreateWsRequest"
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConsoleCreateWsResponse"
}
}
}
},
"400": {
"description": "Error 400",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"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/cluster/events": {
"get": {
"summary": "Cluster SSE event stream",
"operationId": "v1_api_cluster_events_get",
"tags": ["Streaming"],
"description": "Server-Sent Events stream for real-time cluster updates. Returns text/event-stream with node_joined, node_lost, cluster_state, ws_created, ws_closed, ws_rename events.",
"responses": {
"200": {
"description": "Success"
}
}
}
},
"/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/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"
}
}
}
}
}
}
},
"/health": {
"get": {
"summary": "Console health check",
"operationId": "health_get",
"tags": ["Observability"],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ConsoleHealthResponse"
}
}
}
}
}
}
}
},
"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.",
"properties": {
"token": {
"description": "Bearer token to authenticate",
"title": "Token",
"type": "string"
}
},
"required": ["token"],
"title": "AuthLoginRequest",
"type": "object"
},
"AuthLoginResponse": {
"description": "POST /v1/api/auth/login success response.",
"properties": {
"status": {
"default": "ok",
"title": "Status",
"type": "string"
},
"role": {
"description": "Assigned role",
"examples": ["full", "read"],
"title": "Role",
"type": "string"
}
},
"required": ["role"],
"title": "AuthLoginResponse",
"type": "object"
},
"ClusterOverviewResponse": {
"properties": {
"nodes": {
"default": 0,
"title": "Nodes",
"type": "integer"
},
"workstreams": {
"default": 0,
"title": "Workstreams",
"type": "integer"
},
"states": {
"$ref": "#/components/schemas/StateCounts",
"default": {
"running": 0,
"thinking": 0,
"attention": 0,
"idle": 0,
"error": 0
}
},
"aggregate": {
"$ref": "#/components/schemas/ClusterAggregate",
"default": {
"total_tokens": 0,
"total_tool_calls": 0
}
},
"version_drift": {
"default": false,
"title": "Version Drift",
"type": "boolean"
},
"versions": {
"default": [],
"items": {
"type": "string"
},
"title": "Versions",
"type": "array"
}
},
"title": "ClusterOverviewResponse",
"type": "object"
},
"ClusterAggregate": {
"properties": {
"total_tokens": {
"default": 0,
"title": "Total Tokens",
"type": "integer"
},
"total_tool_calls": {
"default": 0,
"title": "Total Tool Calls",
"type": "integer"
}
},
"title": "ClusterAggregate",
"type": "object"
},
"StateCounts": {
"properties": {
"running": {
"default": 0,
"title": "Running",
"type": "integer"
},
"thinking": {
"default": 0,
"title": "Thinking",
"type": "integer"
},
"attention": {
"default": 0,
"title": "Attention",
"type": "integer"
},
"idle": {
"default": 0,
"title": "Idle",
"type": "integer"
},
"error": {
"default": 0,
"title": "Error",
"type": "integer"
}
},
"title": "StateCounts",
"type": "object"
},
"ClusterNodesResponse": {
"properties": {
"nodes": {
"items": {
"$ref": "#/components/schemas/ClusterNodeInfo"
},
"title": "Nodes",
"type": "array"
},
"total": {
"default": 0,
"title": "Total",
"type": "integer"
}
},
"required": ["nodes"],
"title": "ClusterNodesResponse",
"type": "object"
},
"ClusterNodeInfo": {
"properties": {
"node_id": {
"title": "Node Id",
"type": "string"
},
"server_url": {
"default": "",
"title": "Server Url",
"type": "string"
},
"ws_total": {
"default": 0,
"title": "Ws Total",
"type": "integer"
},
"ws_running": {
"default": 0,
"title": "Ws Running",
"type": "integer"
},
"ws_thinking": {
"default": 0,
"title": "Ws Thinking",
"type": "integer"
},
"ws_attention": {
"default": 0,
"title": "Ws Attention",
"type": "integer"
},
"ws_idle": {
"default": 0,
"title": "Ws Idle",
"type": "integer"
},
"ws_error": {
"default": 0,
"title": "Ws Error",
"type": "integer"
},
"total_tokens": {
"default": 0,
"title": "Total Tokens",
"type": "integer"
},
"started": {
"default": 0.0,
"title": "Started",
"type": "number"
},
"reachable": {
"default": true,
"title": "Reachable",
"type": "boolean"
},
"health": {
"additionalProperties": {
"type": "string"
},
"title": "Health",
"type": "object"
},
"version": {
"default": "",
"title": "Version",
"type": "string"
}
},
"required": ["node_id"],
"title": "ClusterNodeInfo",
"type": "object"
},
"ClusterWorkstreamsResponse": {
"properties": {
"workstreams": {
"items": {
"$ref": "#/components/schemas/ClusterWorkstreamInfo"
},
"title": "Workstreams",
"type": "array"
},
"total": {
"default": 0,
"title": "Total",
"type": "integer"
},
"page": {
"default": 1,
"title": "Page",
"type": "integer"
},
"per_page": {
"default": 50,
"title": "Per Page",
"type": "integer"
},
"pages": {
"default": 1,
"title": "Pages",
"type": "integer"
}
},
"required": ["workstreams"],
"title": "ClusterWorkstreamsResponse",
"type": "object"
},
"ClusterWorkstreamInfo": {
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"name": {
"default": "",
"title": "Name",
"type": "string"
},
"state": {
"default": "",
"title": "State",
"type": "string"
},
"node": {
"default": "",
"title": "Node",
"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"
}
},
"required": ["id"],
"title": "ClusterWorkstreamInfo",
"type": "object"
},
"NodeDetailResponse": {
"properties": {
"node_id": {
"title": "Node Id",
"type": "string"
},
"server_url": {
"default": "",
"title": "Server Url",
"type": "string"
},
"health": {
"additionalProperties": {
"type": "string"
},
"title": "Health",
"type": "object"
},
"workstreams": {
"default": [],
"items": {
"$ref": "#/components/schemas/ClusterWorkstreamInfo"
},
"title": "Workstreams",
"type": "array"
},
"aggregate": {
"additionalProperties": {
"type": "integer"
},
"title": "Aggregate",
"type": "object"
},
"reachable": {
"default": true,
"title": "Reachable",
"type": "boolean"
}
},
"required": ["node_id"],
"title": "NodeDetailResponse",
"type": "object"
},
"ConsoleCreateWsRequest": {
"properties": {
"node_id": {
"default": "",
"description": "Target node: specific ID, 'auto', 'pool', or empty for auto",
"title": "Node Id",
"type": "string"
},
"name": {
"default": "",
"description": "Workstream name (auto-generated if empty)",
"title": "Name",
"type": "string"
},
"model": {
"default": "",
"description": "Model alias from node registry",
"title": "Model",
"type": "string"
},
"initial_message": {
"default": "",
"description": "Optional first message sent after creation",
"title": "Initial Message",
"type": "string"
},
"template": {
"default": "",
"description": "Prompt template name (replaces default templates)",
"title": "Template",
"type": "string"
}
},
"title": "ConsoleCreateWsRequest",
"type": "object"
},
"ConsoleCreateWsResponse": {
"properties": {
"status": {
"default": "ok",
"title": "Status",
"type": "string"
},
"correlation_id": {
"default": "",
"title": "Correlation Id",
"type": "string"
},
"target_node": {
"default": "",
"title": "Target Node",
"type": "string"
}
},
"title": "ConsoleCreateWsResponse",
"type": "object"
},
"ConsoleHealthResponse": {
"properties": {
"status": {
"default": "ok",
"examples": ["ok"],
"title": "Status",
"type": "string"
},
"service": {
"default": "turnstone-console",
"title": "Service",
"type": "string"
},
"nodes": {
"default": 0,
"title": "Nodes",
"type": "integer"
},
"workstreams": {
"default": 0,
"title": "Workstreams",
"type": "integer"
},
"version_drift": {
"default": false,
"title": "Version Drift",
"type": "boolean"
},
"versions": {
"default": [],
"items": {
"type": "string"
},
"title": "Versions",
"type": "array"
}
},
"title": "ConsoleHealthResponse",
"type": "object"
}
}
}
}