+ Server-scoped routes manage the whole server: projects, workspace lifecycle, and auth accounts. Runtime + context is for anything resolved from an active directory, including config, provider capabilities, tools, + files, and VCS. +
+API map
+
+ A single /api route surface for simple clients and multi-directory frontends. The important
+ design question is not route nesting; it is where runtime context comes from.
+
Context Model
+ +Request-context calls
++ These calls operate against a directory, optionally through a workspace. Simple clients omit context and + use the default runtime. +
+GET /api/fs/tree?path=.&directory=/repo/app&workspace=ws_123
+ Session-pinned calls
++ These calls never take request context. The session is already pinned to the directory and workspace it was + created in. +
+POST /api/session/ses_123/prompt
+
+// server resolves
+sessionID -> { directory, workspaceID? }
+ Operation Inventory
+
+ The SDK is the source of truth. HTTP routes are mounts for RPC-style operations. server operations do not use runtime context. request operations use request/default runtime context from directory and workspace query parameters. session operations use pinned session context and should not accept context input.
+
| Operation | Input | Context | HTTP mount | Purpose |
|---|---|---|---|---|
agent.list | {} | request | GET /api/agent | Available agents. |
auth.activate | { accountID: AccountID } | server | POST /api/auth/:accountID/activate | Set the account as active for its service. |
auth.create | {
+ serviceID: ServiceID
+ credential:
+ | { type: "oauth", refresh: string, access: string, expires: number }
+ | { type: "api", key: string, metadata?: Record<string, string> }
+ description?: string
+ active?: boolean
+} | server | POST /api/auth | Create an auth account. |
auth.delete | { accountID: AccountID } | server | DELETE /api/auth/:accountID | Remove an auth account. |
auth.get | { accountID: AccountID } | server | GET /api/auth/:accountID | Get one auth account. |
auth.list | { serviceID?: ServiceID } | server | GET /api/auth | List saved auth accounts. Response includes active account mapping. |
auth.update | {
+ accountID: AccountID
+ description?: string
+ credential?:
+ | { type: "oauth", refresh: string, access: string, expires: number }
+ | { type: "api", key: string, metadata?: Record<string, string> }
+} | server | PATCH /api/auth/:accountID | Update account description or credential. |
catalog.model.get | {
+ providerID: ProviderID
+ modelID: ModelID
+} | server | GET /api/catalog/model/:providerID/:modelID | Get one catalog model. |
catalog.model.list | {} | server | GET /api/catalog/model | List flattened catalog models. |
command.list | {} | request | GET /api/command | Available commands. |
config.get | {} | request | GET /api/config | Resolved config. |
config.update | { config: Config } | request | PATCH /api/config | Update config. |
event.subscribe | {} | request | GET /api/event | Server-sent events for the resolved runtime context. |
formatter.status | {} | request | GET /api/formatter | Formatter status. |
fs.file | { path: string } | request | GET /api/fs/file | Read one file. |
fs.grep | {
+ pattern: string
+ include?: string
+ limit?: number
+} | request | POST /api/fs/grep | Search file contents. |
fs.search | {
+ query: string
+ type?: "file" | "directory"
+ limit?: number
+} | request | POST /api/fs/search | Search paths by name. |
fs.tree | { path: string } | request | GET /api/fs/tree | Browse a directory. |
lsp.status | {} | request | GET /api/lsp | LSP status. |
mcp.prompt.list | {} | request | GET /api/mcp/prompt | List MCP prompts. |
mcp.prompt.render | {
+ server: string
+ name: string
+ arguments?: Record<string, string>
+} | request | POST /api/mcp/prompt/render | Render one MCP prompt. |
mcp.resource.list | {} | request | GET /api/mcp/resource | List MCP resources. |
mcp.resource.read | {
+ server: string
+ uri: string
+} | request | GET /api/mcp/resource/read | Read one MCP resource. |
mcp.server.create | {
+ name: string
+ config:
+ | { type: "local", command: string, arguments?: string[], environment?: Record<string, string> }
+ | { type: "remote", url: string, headers?: Record<string, string>, oauth?: boolean | object }
+} | request | POST /api/mcp/server | Add an MCP server to runtime config. |
mcp.server.list | {} | request | GET /api/mcp/server | List MCP servers with status and auth state. |
mcp.server.oauth.callback | {
+ name: string
+ code: string
+} | request | POST /api/mcp/server/:name/oauth/callback | Complete MCP OAuth. |
mcp.server.oauth.delete | { name: string } | request | DELETE /api/mcp/server/:name/oauth | Remove MCP OAuth credentials. |
mcp.server.oauth.start | { name: string } | request | POST /api/mcp/server/:name/oauth | Start MCP OAuth. |
permission.list | {} | request | GET /api/permission | Pending permission requests. |
permission.reply | {
+ permissionID: PermissionID
+ response: PermissionReply
+} | request | POST /api/permission/:permissionID/reply | Reply to a permission request. |
project.get | { projectID: ProjectID } | server | GET /api/project/:projectID | Get project metadata. |
project.list | {} | server | GET /api/project | List projects known to this server. |
project.update | {
+ projectID: ProjectID
+ name?: string
+ icon?: string
+ commands?: Array<{
+ name: string
+ command: string
+ }>
+} | server | PATCH /api/project/:projectID | Update project metadata. |
provider.list | {} | request | GET /api/provider | Provider inventory for the runtime context. |
pty.create | {
+ command?: string
+ cwd?: string
+ shell?: string
+} | request | POST /api/pty | Create PTY in the runtime context. |
pty.delete | { ptyID: PtyID } | request | DELETE /api/pty/:ptyID | Delete PTY. |
pty.get | { ptyID: PtyID } | request | GET /api/pty/:ptyID | Get PTY info. |
pty.list | {} | request | GET /api/pty | List PTYs for the runtime. |
pty.update | {
+ ptyID: PtyID
+ title?: string
+ size?: { columns: number, rows: number }
+} | request | PATCH /api/pty/:ptyID | Update PTY. |
question.list | {} | request | GET /api/question | Pending user questions. |
question.reject | { questionID: QuestionID } | request | POST /api/question/:questionID/reject | Reject a question. |
question.reply | {
+ questionID: QuestionID
+ response: QuestionResponse
+} | request | POST /api/question/:questionID/reply | Reply to a question. |
session.compact | { sessionID: SessionID } | session | POST /api/session/:sessionID/compact | Compact the session conversation. |
session.context | { sessionID: SessionID } | session | GET /api/session/:sessionID/context | Return active context messages after the last compaction. |
session.create | {
+ title?: string
+ agent?: string
+ model?: { providerID: ProviderID, modelID: ModelID }
+ permission?: PermissionRule[]
+} | request | POST /api/session | Create a session pinned to resolved runtime context. |
session.delete | { sessionID: SessionID } | session | DELETE /api/session/:sessionID | Delete a session. |
session.diff | { sessionID: SessionID } | session | GET /api/session/:sessionID/diff | Return session diff summary. |
session.get | { sessionID: SessionID } | session | GET /api/session/:sessionID | Get one session. |
session.list | {
+ limit?: number
+ order?: "asc" | "desc"
+ path?: string
+ roots?: boolean
+ start?: number
+ search?: string
+ cursor?: string
+} | request | GET /api/session | List sessions for the current runtime context by default. |
session.message.list | {
+ sessionID: SessionID
+ limit?: number
+ order?: "asc" | "desc"
+ cursor?: string
+} | session | GET /api/session/:sessionID/message | Page through session messages. |
session.prompt | {
+ sessionID: SessionID
+ prompt: Prompt
+ delivery?: "immediate" | "deferred"
+} | session | POST /api/session/:sessionID/prompt | Create a user message and queue the agent loop. |
session.todo | { sessionID: SessionID } | session | GET /api/session/:sessionID/todo | Return todos associated with the session. |
session.update | {
+ sessionID: SessionID
+ title?: string
+ archived?: number
+ permission?: PermissionRule[]
+} | session | PATCH /api/session/:sessionID | Update title, archival state, or session metadata. |
session.wait | { sessionID: SessionID } | session | POST /api/session/:sessionID/wait | Wait until the session is idle. |
skill.list | {} | request | GET /api/skill | Available skills. |
vcs.diff | {
+ format?: "json" | "patch"
+ mode?: "worktree" | "default"
+} | request | GET /api/vcs/diff | Diff for the runtime directory. |
vcs.get | {} | request | GET /api/vcs | VCS metadata. |
vcs.patch | { patch: string } | request | POST /api/vcs/patch | Apply a patch to the runtime directory. |
vcs.status | {} | request | GET /api/vcs/status | Changed files. |
workspace.create | {
+ projectID?: ProjectID
+ name?: string
+ directory?: string
+ type: string
+ metadata?: Record<string, unknown>
+} | server | POST /api/workspace | Create or register a workspace. |
workspace.delete | { workspaceID: WorkspaceID } | server | DELETE /api/workspace/:workspaceID | Remove a workspace registration. |
workspace.get | { workspaceID: WorkspaceID } | server | GET /api/workspace/:workspaceID | Get workspace metadata. |
workspace.list | { projectID?: ProjectID } | server | GET /api/workspace | List workspaces, optionally filtered by project. |
workspace.status | {} | server | GET /api/workspace/status | Connection/lifecycle status for all workspaces. Needs team discussion. |
workspace.sync | {} | server | POST /api/workspace/sync | Sync workspace metadata from adapters. Needs team discussion. |
workspace.update | {
+ workspaceID: WorkspaceID
+ name?: string
+ metadata?: Record<string, unknown>
+ archived?: boolean
+} | server | PATCH /api/workspace/:workspaceID | Update workspace metadata or lifecycle state. |
workspace.warp | {
+ workspaceID?: WorkspaceID
+ sessionID: SessionID
+ copyChanges: boolean
+} | server | POST /api/workspace/warp | Move a session into or out of a workspace. Needs team discussion. |
Event Envelope
+
+ Every event uses the same envelope. Resource identity belongs in payload. Runtime identity belongs
+ in context.
+
type ApiEvent<Payload> = {
+ id: string
+ type: string
+ time: number
+ context: {
+ directory: string
+ workspaceID?: string
+ }
+ payload: Payload
+}
+ {
+ "id": "evt_01",
+ "type": "message.part.delta",
+ "time": 1760000000000,
+ "context": {
+ "directory": "/repo/app",
+ "workspaceID": "ws_123"
+ },
+ "payload": {
+ "sessionID": "ses_123",
+ "messageID": "msg_456",
+ "partID": "part_789",
+ "field": "text",
+ "delta": "hello"
+ }
+}
+ Frontend Sync Store
+
+ A frontend can keep one giant store like the current TUI. Runtime data is partitioned by
+ contextKey. Durable entities such as sessions and messages are keyed by their own IDs.
+
type RuntimeContext = {
+ directory: string
+ workspaceID?: string
+}
+
+type ContextKey = string
+type SessionID = string
+type MessageID = string
+
+type SyncStore = {
+ status: "loading" | "partial" | "complete"
+
+ shared: {
+ provider: Provider[]
+ provider_default: Record<string, string>
+ provider_next: ProviderListResponse
+ provider_auth: Record<string, ProviderAuthMethod[]>
+ console_state: ConsoleState
+ }
+
+ contexts: Record<
+ ContextKey,
+ {
+ context: RuntimeContext
+
+ config: Config
+ agent: Agent[]
+ command: Command[]
+ lsp: LspStatus[]
+ formatter: FormatterStatus[]
+ vcs: VcsInfo | undefined
+ mcp: Record<string, McpStatus>
+ mcp_resource: Record<string, McpResource>
+
+ session: SessionID[]
+ session_status: Record<SessionID, SessionStatus>
+ }
+ >
+
+ session: Record<SessionID, Session & { context: RuntimeContext }>
+ session_diff: Record<SessionID, Snapshot.FileDiff[]>
+ todo: Record<SessionID, Todo[]>
+ permission: Record<SessionID, PermissionRequest[]>
+ question: Record<SessionID, QuestionRequest[]>
+
+ message: Record<SessionID, Message[]>
+ part: Record<MessageID, Part[]>
+}
+
+function contextKey(context: RuntimeContext) {
+ return `${context.workspaceID ?? "local"}:${context.directory}`
+}
+