mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-13 15:44:56 +00:00
ignore: notes
This commit is contained in:
781
specs/v2/api.html
Normal file
781
specs/v2/api.html
Normal file
@@ -0,0 +1,781 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>opencode v2 API</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f6f1e8;
|
||||
--fg: #1f2723;
|
||||
--muted: #6f756d;
|
||||
--dim: #ebe3d6;
|
||||
--panel: #fffaf1;
|
||||
--line: #26342f;
|
||||
--thin: #d6ccbd;
|
||||
--code: #eee5d8;
|
||||
--accent: #496b5a;
|
||||
--accent-soft: #dce7dc;
|
||||
font-family:
|
||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background:
|
||||
radial-gradient(circle at 12% 0%, rgba(73, 107, 90, 0.12), transparent 34rem),
|
||||
linear-gradient(90deg, rgba(38, 52, 47, 0.055) 1px, transparent 1px),
|
||||
linear-gradient(rgba(38, 52, 47, 0.045) 1px, transparent 1px),
|
||||
var(--bg);
|
||||
background-size: 72px 72px;
|
||||
color: var(--fg);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
padding: 40px 32px 72px;
|
||||
}
|
||||
|
||||
header {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.25fr) minmax(360px, 0.75fr);
|
||||
gap: 32px;
|
||||
align-items: end;
|
||||
border-bottom: 2px solid var(--line);
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
max-width: 1180px;
|
||||
font-size: clamp(4rem, 12vw, 13rem);
|
||||
line-height: 0.82;
|
||||
letter-spacing: -0.09em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.75rem, 4vw, 4rem);
|
||||
line-height: 0.95;
|
||||
letter-spacing: -0.07em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
|
||||
}
|
||||
|
||||
code {
|
||||
border: 1px solid var(--thin);
|
||||
padding: 1px 5px;
|
||||
background: var(--code);
|
||||
color: var(--fg);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
border: 1px solid var(--line);
|
||||
padding: 16px;
|
||||
background: var(--code);
|
||||
color: var(--fg);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
pre code {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-top: 34px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
display: inline-block;
|
||||
border: 1px solid var(--line);
|
||||
margin-bottom: 18px;
|
||||
padding: 5px 8px;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.lede {
|
||||
max-width: 620px;
|
||||
color: var(--muted);
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: 2px solid var(--line);
|
||||
background: rgba(255, 250, 241, 0.92);
|
||||
box-shadow: 0 20px 50px rgba(31, 39, 35, 0.08);
|
||||
}
|
||||
|
||||
.panel-pad {
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.span-12 {
|
||||
grid-column: span 12;
|
||||
}
|
||||
|
||||
.span-8 {
|
||||
grid-column: span 8;
|
||||
}
|
||||
|
||||
.span-6 {
|
||||
grid-column: span 6;
|
||||
}
|
||||
|
||||
.span-4 {
|
||||
grid-column: span 4;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.rule {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
border: 2px solid var(--line);
|
||||
padding: 22px;
|
||||
background: var(--accent);
|
||||
color: #fffaf1;
|
||||
}
|
||||
|
||||
.rule strong {
|
||||
font-size: clamp(1.45rem, 3vw, 2.45rem);
|
||||
line-height: 1;
|
||||
letter-spacing: -0.06em;
|
||||
}
|
||||
|
||||
.rule code {
|
||||
border-color: var(--bg);
|
||||
background: rgba(255, 250, 241, 0.18);
|
||||
color: #fffaf1;
|
||||
}
|
||||
|
||||
.key {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
border: 1px solid var(--line);
|
||||
padding: 4px 8px;
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.pill.inverse {
|
||||
background: var(--accent);
|
||||
color: #fffaf1;
|
||||
}
|
||||
|
||||
.diagram {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: 2px solid var(--line);
|
||||
background: var(--panel);
|
||||
}
|
||||
|
||||
.diagram text {
|
||||
fill: var(--fg);
|
||||
font-family:
|
||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
.diagram .box {
|
||||
fill: var(--panel);
|
||||
stroke: var(--fg);
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.diagram .fill {
|
||||
fill: var(--accent);
|
||||
stroke: var(--accent);
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.diagram .fill-text {
|
||||
fill: #fffaf1;
|
||||
}
|
||||
|
||||
.diagram .line {
|
||||
stroke: var(--fg);
|
||||
stroke-width: 2;
|
||||
fill: none;
|
||||
marker-end: url(#arrow);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 2px solid var(--line);
|
||||
background: var(--panel);
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid var(--thin);
|
||||
padding: 11px 12px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
border-bottom: 2px solid var(--line);
|
||||
background: var(--accent);
|
||||
color: #fffaf1;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
td.route {
|
||||
width: 34%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
td.body {
|
||||
width: 28%;
|
||||
}
|
||||
|
||||
td.body code {
|
||||
display: block;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
td.method {
|
||||
width: 72px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
td.context {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
td.operation {
|
||||
width: 210px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.context-tag {
|
||||
display: inline-block;
|
||||
border: 1px solid var(--line);
|
||||
padding: 3px 7px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.07em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
tr.question-row td {
|
||||
background: #f7e8b7;
|
||||
}
|
||||
|
||||
.request {
|
||||
background: var(--accent);
|
||||
color: #fffaf1;
|
||||
}
|
||||
|
||||
.session {
|
||||
background: var(--panel);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.server {
|
||||
background: var(--dim);
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
.note {
|
||||
border-left: 6px solid var(--line);
|
||||
padding: 14px 18px;
|
||||
background: var(--dim);
|
||||
}
|
||||
|
||||
.toc {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toc a {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
border-bottom: 1px solid var(--thin);
|
||||
padding: 8px 0;
|
||||
color: var(--fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.toc span {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
main {
|
||||
padding: 28px 16px 56px;
|
||||
}
|
||||
|
||||
header,
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.span-12,
|
||||
.span-8,
|
||||
.span-6,
|
||||
.span-4 {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<div>
|
||||
<div class="eyebrow">opencode v2</div>
|
||||
<h1>API map</h1>
|
||||
</div>
|
||||
<div class="stack">
|
||||
<p class="lede">
|
||||
A single <code>/api</code> route surface for simple clients and multi-directory frontends. The important
|
||||
design question is not route nesting; it is where runtime context comes from.
|
||||
</p>
|
||||
<div class="key">
|
||||
<span class="pill">Server scoped</span>
|
||||
<span class="pill inverse">Request context</span>
|
||||
<span class="pill">Session pinned</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="grid">
|
||||
<article class="span-8 rule">
|
||||
<strong>Everything has one canonical route. Some routes are server-scoped; runtime routes use context; session item routes use the session.</strong>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</article>
|
||||
<nav class="span-4 panel panel-pad toc" aria-label="Page sections">
|
||||
<a href="#context"><strong>Context Model</strong><span>how calls resolve</span></a>
|
||||
<a href="#endpoints"><strong>Endpoint Inventory</strong><span>all planned routes</span></a>
|
||||
<a href="#events"><strong>Events</strong><span>one envelope</span></a>
|
||||
<a href="#store"><strong>Frontend Store</strong><span>sync model</span></a>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<section id="context" class="grid">
|
||||
<div class="span-12 stack">
|
||||
<h2>Context Model</h2>
|
||||
<svg class="diagram" viewBox="0 0 1280 360" role="img" aria-labelledby="ctx-title ctx-desc">
|
||||
<title id="ctx-title">API context resolution</title>
|
||||
<desc id="ctx-desc">Non-session routes resolve from request context, session item routes resolve from session storage.</desc>
|
||||
<defs>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#26342f" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect class="fill" x="34" y="44" width="294" height="96" />
|
||||
<text class="fill-text" x="58" y="84" font-size="24" font-weight="900">Non-session route</text>
|
||||
<text class="fill-text" x="58" y="116" font-size="17">/api/file, /api/vcs/status</text>
|
||||
<path class="line" d="M328 92 H472" />
|
||||
<rect class="box" x="486" y="44" width="286" height="96" />
|
||||
<text x="510" y="84" font-size="24" font-weight="900">Request context</text>
|
||||
<text x="510" y="116" font-size="17">query params or default runtime</text>
|
||||
<path class="line" d="M772 92 H916" />
|
||||
<rect class="box" x="930" y="44" width="316" height="96" />
|
||||
<text x="954" y="84" font-size="24" font-weight="900">Runtime context</text>
|
||||
<text x="954" y="116" font-size="17">directory + workspaceID?</text>
|
||||
|
||||
<rect class="box" x="34" y="220" width="294" height="96" />
|
||||
<text x="58" y="260" font-size="24" font-weight="900">Session item route</text>
|
||||
<text x="58" y="292" font-size="17">/api/session/:id/prompt</text>
|
||||
<path class="line" d="M328 268 H472" />
|
||||
<rect class="fill" x="486" y="220" width="286" height="96" />
|
||||
<text class="fill-text" x="510" y="260" font-size="24" font-weight="900">Session row</text>
|
||||
<text class="fill-text" x="510" y="292" font-size="17">contains pinned context</text>
|
||||
<path class="line" d="M772 268 H916" />
|
||||
<rect class="box" x="930" y="220" width="316" height="96" />
|
||||
<text x="954" y="260" font-size="24" font-weight="900">Runtime context</text>
|
||||
<text x="954" y="292" font-size="17">directory + workspaceID?</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<article class="span-6 panel panel-pad stack">
|
||||
<h3>Request-context calls</h3>
|
||||
<p class="muted">
|
||||
These calls operate against a directory, optionally through a workspace. Simple clients omit context and
|
||||
use the default runtime.
|
||||
</p>
|
||||
<pre><code>GET /api/fs/tree?path=.&directory=/repo/app&workspace=ws_123</code></pre>
|
||||
</article>
|
||||
|
||||
<article class="span-6 panel panel-pad stack">
|
||||
<h3>Session-pinned calls</h3>
|
||||
<p class="muted">
|
||||
These calls never take request context. The session is already pinned to the directory and workspace it was
|
||||
created in.
|
||||
</p>
|
||||
<pre><code>POST /api/session/ses_123/prompt
|
||||
|
||||
// server resolves
|
||||
sessionID -> { directory, workspaceID? }</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section id="endpoints" class="grid">
|
||||
<div class="span-12 stack">
|
||||
<h2>Operation Inventory</h2>
|
||||
<p class="muted">
|
||||
The SDK is the source of truth. HTTP routes are mounts for RPC-style operations. <span class="context-tag server">server</span> operations do not use runtime context. <span class="context-tag request">request</span> operations use request/default runtime context from <code>directory</code> and <code>workspace</code> query parameters. <span class="context-tag session">session</span> operations use pinned session context and should not accept context input.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<article class="span-12 panel panel-pad stack">
|
||||
<table>
|
||||
<thead><tr><th>Operation</th><th>Input</th><th>Context</th><th>HTTP mount</th><th>Purpose</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="operation"><code>agent.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/agent</code></td><td>Available agents.</td></tr>
|
||||
<tr><td class="operation"><code>auth.activate</code></td><td class="body"><code>{ accountID: AccountID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>POST /api/auth/:accountID/activate</code></td><td>Set the account as active for its service.</td></tr>
|
||||
<tr><td class="operation"><code>auth.create</code></td><td class="body"><code>{
|
||||
serviceID: ServiceID
|
||||
credential:
|
||||
| { type: "oauth", refresh: string, access: string, expires: number }
|
||||
| { type: "api", key: string, metadata?: Record<string, string> }
|
||||
description?: string
|
||||
active?: boolean
|
||||
}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>POST /api/auth</code></td><td>Create an auth account.</td></tr>
|
||||
<tr><td class="operation"><code>auth.delete</code></td><td class="body"><code>{ accountID: AccountID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>DELETE /api/auth/:accountID</code></td><td>Remove an auth account.</td></tr>
|
||||
<tr><td class="operation"><code>auth.get</code></td><td class="body"><code>{ accountID: AccountID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/auth/:accountID</code></td><td>Get one auth account.</td></tr>
|
||||
<tr><td class="operation"><code>auth.list</code></td><td class="body"><code>{ serviceID?: ServiceID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/auth</code></td><td>List saved auth accounts. Response includes active account mapping.</td></tr>
|
||||
<tr><td class="operation"><code>auth.update</code></td><td class="body"><code>{
|
||||
accountID: AccountID
|
||||
description?: string
|
||||
credential?:
|
||||
| { type: "oauth", refresh: string, access: string, expires: number }
|
||||
| { type: "api", key: string, metadata?: Record<string, string> }
|
||||
}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>PATCH /api/auth/:accountID</code></td><td>Update account description or credential.</td></tr>
|
||||
<tr><td class="operation"><code>catalog.model.get</code></td><td class="body"><code>{
|
||||
providerID: ProviderID
|
||||
modelID: ModelID
|
||||
}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/catalog/model/:providerID/:modelID</code></td><td>Get one catalog model.</td></tr>
|
||||
<tr><td class="operation"><code>catalog.model.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/catalog/model</code></td><td>List flattened catalog models.</td></tr>
|
||||
<tr><td class="operation"><code>command.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/command</code></td><td>Available commands.</td></tr>
|
||||
<tr><td class="operation"><code>config.get</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/config</code></td><td>Resolved config.</td></tr>
|
||||
<tr><td class="operation"><code>config.update</code></td><td class="body"><code>{ config: Config }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>PATCH /api/config</code></td><td>Update config.</td></tr>
|
||||
<tr><td class="operation"><code>event.subscribe</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/event</code></td><td>Server-sent events for the resolved runtime context.</td></tr>
|
||||
<tr><td class="operation"><code>formatter.status</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/formatter</code></td><td>Formatter status.</td></tr>
|
||||
<tr><td class="operation"><code>fs.file</code></td><td class="body"><code>{ path: string }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/fs/file</code></td><td>Read one file.</td></tr>
|
||||
<tr><td class="operation"><code>fs.grep</code></td><td class="body"><code>{
|
||||
pattern: string
|
||||
include?: string
|
||||
limit?: number
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/fs/grep</code></td><td>Search file contents.</td></tr>
|
||||
<tr><td class="operation"><code>fs.search</code></td><td class="body"><code>{
|
||||
query: string
|
||||
type?: "file" | "directory"
|
||||
limit?: number
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/fs/search</code></td><td>Search paths by name.</td></tr>
|
||||
<tr><td class="operation"><code>fs.tree</code></td><td class="body"><code>{ path: string }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/fs/tree</code></td><td>Browse a directory.</td></tr>
|
||||
<tr><td class="operation"><code>lsp.status</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/lsp</code></td><td>LSP status.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.prompt.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/mcp/prompt</code></td><td>List MCP prompts.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.prompt.render</code></td><td class="body"><code>{
|
||||
server: string
|
||||
name: string
|
||||
arguments?: Record<string, string>
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/mcp/prompt/render</code></td><td>Render one MCP prompt.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.resource.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/mcp/resource</code></td><td>List MCP resources.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.resource.read</code></td><td class="body"><code>{
|
||||
server: string
|
||||
uri: string
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/mcp/resource/read</code></td><td>Read one MCP resource.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.server.create</code></td><td class="body"><code>{
|
||||
name: string
|
||||
config:
|
||||
| { type: "local", command: string, arguments?: string[], environment?: Record<string, string> }
|
||||
| { type: "remote", url: string, headers?: Record<string, string>, oauth?: boolean | object }
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/mcp/server</code></td><td>Add an MCP server to runtime config.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.server.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/mcp/server</code></td><td>List MCP servers with status and auth state.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.server.oauth.callback</code></td><td class="body"><code>{
|
||||
name: string
|
||||
code: string
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/mcp/server/:name/oauth/callback</code></td><td>Complete MCP OAuth.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.server.oauth.delete</code></td><td class="body"><code>{ name: string }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>DELETE /api/mcp/server/:name/oauth</code></td><td>Remove MCP OAuth credentials.</td></tr>
|
||||
<tr><td class="operation"><code>mcp.server.oauth.start</code></td><td class="body"><code>{ name: string }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/mcp/server/:name/oauth</code></td><td>Start MCP OAuth.</td></tr>
|
||||
<tr><td class="operation"><code>permission.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/permission</code></td><td>Pending permission requests.</td></tr>
|
||||
<tr><td class="operation"><code>permission.reply</code></td><td class="body"><code>{
|
||||
permissionID: PermissionID
|
||||
response: PermissionReply
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/permission/:permissionID/reply</code></td><td>Reply to a permission request.</td></tr>
|
||||
<tr><td class="operation"><code>project.get</code></td><td class="body"><code>{ projectID: ProjectID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/project/:projectID</code></td><td>Get project metadata.</td></tr>
|
||||
<tr><td class="operation"><code>project.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/project</code></td><td>List projects known to this server.</td></tr>
|
||||
<tr><td class="operation"><code>project.update</code></td><td class="body"><code>{
|
||||
projectID: ProjectID
|
||||
name?: string
|
||||
icon?: string
|
||||
commands?: Array<{
|
||||
name: string
|
||||
command: string
|
||||
}>
|
||||
}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>PATCH /api/project/:projectID</code></td><td>Update project metadata.</td></tr>
|
||||
<tr><td class="operation"><code>provider.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/provider</code></td><td>Provider inventory for the runtime context.</td></tr>
|
||||
<tr><td class="operation"><code>pty.create</code></td><td class="body"><code>{
|
||||
command?: string
|
||||
cwd?: string
|
||||
shell?: string
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/pty</code></td><td>Create PTY in the runtime context.</td></tr>
|
||||
<tr><td class="operation"><code>pty.delete</code></td><td class="body"><code>{ ptyID: PtyID }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>DELETE /api/pty/:ptyID</code></td><td>Delete PTY.</td></tr>
|
||||
<tr><td class="operation"><code>pty.get</code></td><td class="body"><code>{ ptyID: PtyID }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/pty/:ptyID</code></td><td>Get PTY info.</td></tr>
|
||||
<tr><td class="operation"><code>pty.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/pty</code></td><td>List PTYs for the runtime.</td></tr>
|
||||
<tr><td class="operation"><code>pty.update</code></td><td class="body"><code>{
|
||||
ptyID: PtyID
|
||||
title?: string
|
||||
size?: { columns: number, rows: number }
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>PATCH /api/pty/:ptyID</code></td><td>Update PTY.</td></tr>
|
||||
<tr><td class="operation"><code>question.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/question</code></td><td>Pending user questions.</td></tr>
|
||||
<tr><td class="operation"><code>question.reject</code></td><td class="body"><code>{ questionID: QuestionID }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/question/:questionID/reject</code></td><td>Reject a question.</td></tr>
|
||||
<tr><td class="operation"><code>question.reply</code></td><td class="body"><code>{
|
||||
questionID: QuestionID
|
||||
response: QuestionResponse
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/question/:questionID/reply</code></td><td>Reply to a question.</td></tr>
|
||||
<tr><td class="operation"><code>session.compact</code></td><td class="body"><code>{ sessionID: SessionID }</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>POST /api/session/:sessionID/compact</code></td><td>Compact the session conversation.</td></tr>
|
||||
<tr><td class="operation"><code>session.context</code></td><td class="body"><code>{ sessionID: SessionID }</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>GET /api/session/:sessionID/context</code></td><td>Return active context messages after the last compaction.</td></tr>
|
||||
<tr><td class="operation"><code>session.create</code></td><td class="body"><code>{
|
||||
title?: string
|
||||
agent?: string
|
||||
model?: { providerID: ProviderID, modelID: ModelID }
|
||||
permission?: PermissionRule[]
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/session</code></td><td>Create a session pinned to resolved runtime context.</td></tr>
|
||||
<tr><td class="operation"><code>session.delete</code></td><td class="body"><code>{ sessionID: SessionID }</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>DELETE /api/session/:sessionID</code></td><td>Delete a session.</td></tr>
|
||||
<tr><td class="operation"><code>session.diff</code></td><td class="body"><code>{ sessionID: SessionID }</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>GET /api/session/:sessionID/diff</code></td><td>Return session diff summary.</td></tr>
|
||||
<tr><td class="operation"><code>session.get</code></td><td class="body"><code>{ sessionID: SessionID }</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>GET /api/session/:sessionID</code></td><td>Get one session.</td></tr>
|
||||
<tr><td class="operation"><code>session.list</code></td><td class="body"><code>{
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
path?: string
|
||||
roots?: boolean
|
||||
start?: number
|
||||
search?: string
|
||||
cursor?: string
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/session</code></td><td>List sessions for the current runtime context by default.</td></tr>
|
||||
<tr><td class="operation"><code>session.message.list</code></td><td class="body"><code>{
|
||||
sessionID: SessionID
|
||||
limit?: number
|
||||
order?: "asc" | "desc"
|
||||
cursor?: string
|
||||
}</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>GET /api/session/:sessionID/message</code></td><td>Page through session messages.</td></tr>
|
||||
<tr><td class="operation"><code>session.prompt</code></td><td class="body"><code>{
|
||||
sessionID: SessionID
|
||||
prompt: Prompt
|
||||
delivery?: "immediate" | "deferred"
|
||||
}</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>POST /api/session/:sessionID/prompt</code></td><td>Create a user message and queue the agent loop.</td></tr>
|
||||
<tr><td class="operation"><code>session.todo</code></td><td class="body"><code>{ sessionID: SessionID }</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>GET /api/session/:sessionID/todo</code></td><td>Return todos associated with the session.</td></tr>
|
||||
<tr><td class="operation"><code>session.update</code></td><td class="body"><code>{
|
||||
sessionID: SessionID
|
||||
title?: string
|
||||
archived?: number
|
||||
permission?: PermissionRule[]
|
||||
}</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>PATCH /api/session/:sessionID</code></td><td>Update title, archival state, or session metadata.</td></tr>
|
||||
<tr><td class="operation"><code>session.wait</code></td><td class="body"><code>{ sessionID: SessionID }</code></td><td><span class="context-tag session">session</span></td><td class="route"><code>POST /api/session/:sessionID/wait</code></td><td>Wait until the session is idle.</td></tr>
|
||||
<tr><td class="operation"><code>skill.list</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/skill</code></td><td>Available skills.</td></tr>
|
||||
<tr><td class="operation"><code>vcs.diff</code></td><td class="body"><code>{
|
||||
format?: "json" | "patch"
|
||||
mode?: "worktree" | "default"
|
||||
}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/vcs/diff</code></td><td>Diff for the runtime directory.</td></tr>
|
||||
<tr><td class="operation"><code>vcs.get</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/vcs</code></td><td>VCS metadata.</td></tr>
|
||||
<tr><td class="operation"><code>vcs.patch</code></td><td class="body"><code>{ patch: string }</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>POST /api/vcs/patch</code></td><td>Apply a patch to the runtime directory.</td></tr>
|
||||
<tr><td class="operation"><code>vcs.status</code></td><td class="body"><code>{}</code></td><td><span class="context-tag request">request</span></td><td class="route"><code>GET /api/vcs/status</code></td><td>Changed files.</td></tr>
|
||||
<tr><td class="operation"><code>workspace.create</code></td><td class="body"><code>{
|
||||
projectID?: ProjectID
|
||||
name?: string
|
||||
directory?: string
|
||||
type: string
|
||||
metadata?: Record<string, unknown>
|
||||
}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>POST /api/workspace</code></td><td>Create or register a workspace.</td></tr>
|
||||
<tr><td class="operation"><code>workspace.delete</code></td><td class="body"><code>{ workspaceID: WorkspaceID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>DELETE /api/workspace/:workspaceID</code></td><td>Remove a workspace registration.</td></tr>
|
||||
<tr><td class="operation"><code>workspace.get</code></td><td class="body"><code>{ workspaceID: WorkspaceID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/workspace/:workspaceID</code></td><td>Get workspace metadata.</td></tr>
|
||||
<tr><td class="operation"><code>workspace.list</code></td><td class="body"><code>{ projectID?: ProjectID }</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/workspace</code></td><td>List workspaces, optionally filtered by project.</td></tr>
|
||||
<tr class="question-row"><td class="operation"><code>workspace.status</code></td><td class="body"><code>{}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>GET /api/workspace/status</code></td><td>Connection/lifecycle status for all workspaces. Needs team discussion.</td></tr>
|
||||
<tr class="question-row"><td class="operation"><code>workspace.sync</code></td><td class="body"><code>{}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>POST /api/workspace/sync</code></td><td>Sync workspace metadata from adapters. Needs team discussion.</td></tr>
|
||||
<tr><td class="operation"><code>workspace.update</code></td><td class="body"><code>{
|
||||
workspaceID: WorkspaceID
|
||||
name?: string
|
||||
metadata?: Record<string, unknown>
|
||||
archived?: boolean
|
||||
}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>PATCH /api/workspace/:workspaceID</code></td><td>Update workspace metadata or lifecycle state.</td></tr>
|
||||
<tr class="question-row"><td class="operation"><code>workspace.warp</code></td><td class="body"><code>{
|
||||
workspaceID?: WorkspaceID
|
||||
sessionID: SessionID
|
||||
copyChanges: boolean
|
||||
}</code></td><td><span class="context-tag server">server</span></td><td class="route"><code>POST /api/workspace/warp</code></td><td>Move a session into or out of a workspace. Needs team discussion.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section id="events" class="grid">
|
||||
<article class="span-12 panel panel-pad stack">
|
||||
<h2>Event Envelope</h2>
|
||||
<p class="muted">
|
||||
Every event uses the same envelope. Resource identity belongs in <code>payload</code>. Runtime identity belongs
|
||||
in <code>context</code>.
|
||||
</p>
|
||||
<div class="grid">
|
||||
<pre class="span-6"><code>type ApiEvent<Payload> = {
|
||||
id: string
|
||||
type: string
|
||||
time: number
|
||||
context: {
|
||||
directory: string
|
||||
workspaceID?: string
|
||||
}
|
||||
payload: Payload
|
||||
}</code></pre>
|
||||
<pre class="span-6"><code>{
|
||||
"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"
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section id="store" class="grid">
|
||||
<article class="span-12 panel panel-pad stack">
|
||||
<h2>Frontend Sync Store</h2>
|
||||
<p class="muted">
|
||||
A frontend can keep one giant store like the current TUI. Runtime data is partitioned by
|
||||
<code>contextKey</code>. Durable entities such as sessions and messages are keyed by their own IDs.
|
||||
</p>
|
||||
<pre><code>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}`
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user