api.html 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1" />
  6. <title>opencode v2 API</title>
  7. <style>
  8. :root {
  9. --bg: #f6f1e8;
  10. --fg: #1f2723;
  11. --muted: #6f756d;
  12. --dim: #ebe3d6;
  13. --panel: #fffaf1;
  14. --line: #26342f;
  15. --thin: #d6ccbd;
  16. --code: #eee5d8;
  17. --accent: #496b5a;
  18. --accent-soft: #dce7dc;
  19. font-family:
  20. Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  21. }
  22. * {
  23. box-sizing: border-box;
  24. }
  25. html {
  26. background: var(--bg);
  27. color: var(--fg);
  28. }
  29. body {
  30. margin: 0;
  31. background:
  32. radial-gradient(circle at 12% 0%, rgba(73, 107, 90, 0.12), transparent 34rem),
  33. linear-gradient(90deg, rgba(38, 52, 47, 0.055) 1px, transparent 1px),
  34. linear-gradient(rgba(38, 52, 47, 0.045) 1px, transparent 1px),
  35. var(--bg);
  36. background-size: 72px 72px;
  37. color: var(--fg);
  38. line-height: 1.5;
  39. }
  40. main {
  41. width: 100%;
  42. padding: 40px 32px 72px;
  43. }
  44. header {
  45. display: grid;
  46. grid-template-columns: minmax(0, 1.25fr) minmax(360px, 0.75fr);
  47. gap: 32px;
  48. align-items: end;
  49. border-bottom: 2px solid var(--line);
  50. padding-bottom: 32px;
  51. }
  52. h1,
  53. h2,
  54. h3,
  55. p {
  56. margin: 0;
  57. }
  58. h1 {
  59. max-width: 1180px;
  60. font-size: clamp(4rem, 12vw, 13rem);
  61. line-height: 0.82;
  62. letter-spacing: -0.09em;
  63. }
  64. h2 {
  65. font-size: clamp(1.75rem, 4vw, 4rem);
  66. line-height: 0.95;
  67. letter-spacing: -0.07em;
  68. }
  69. h3 {
  70. font-size: 0.78rem;
  71. letter-spacing: 0.12em;
  72. text-transform: uppercase;
  73. }
  74. code,
  75. pre {
  76. font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  77. }
  78. code {
  79. border: 1px solid var(--thin);
  80. padding: 1px 5px;
  81. background: var(--code);
  82. color: var(--fg);
  83. font-size: 0.9em;
  84. }
  85. pre {
  86. overflow: auto;
  87. margin: 0;
  88. border: 1px solid var(--line);
  89. padding: 16px;
  90. background: var(--code);
  91. color: var(--fg);
  92. font-size: 0.92rem;
  93. line-height: 1.5;
  94. }
  95. pre code {
  96. border: 0;
  97. padding: 0;
  98. background: transparent;
  99. font-size: inherit;
  100. }
  101. section {
  102. margin-top: 34px;
  103. }
  104. .eyebrow {
  105. display: inline-block;
  106. border: 1px solid var(--line);
  107. margin-bottom: 18px;
  108. padding: 5px 8px;
  109. font-size: 0.78rem;
  110. font-weight: 800;
  111. letter-spacing: 0.12em;
  112. text-transform: uppercase;
  113. }
  114. .lede {
  115. max-width: 620px;
  116. color: var(--muted);
  117. font-size: 1.15rem;
  118. }
  119. .panel {
  120. border: 2px solid var(--line);
  121. background: rgba(255, 250, 241, 0.92);
  122. box-shadow: 0 20px 50px rgba(31, 39, 35, 0.08);
  123. }
  124. .panel-pad {
  125. padding: 22px;
  126. }
  127. .grid {
  128. display: grid;
  129. grid-template-columns: repeat(12, minmax(0, 1fr));
  130. gap: 18px;
  131. }
  132. .span-12 {
  133. grid-column: span 12;
  134. }
  135. .span-8 {
  136. grid-column: span 8;
  137. }
  138. .span-6 {
  139. grid-column: span 6;
  140. }
  141. .span-4 {
  142. grid-column: span 4;
  143. }
  144. .stack {
  145. display: grid;
  146. gap: 16px;
  147. }
  148. .muted {
  149. color: var(--muted);
  150. }
  151. .rule {
  152. display: grid;
  153. gap: 16px;
  154. border: 2px solid var(--line);
  155. padding: 22px;
  156. background: var(--accent);
  157. color: #fffaf1;
  158. }
  159. .rule strong {
  160. font-size: clamp(1.45rem, 3vw, 2.45rem);
  161. line-height: 1;
  162. letter-spacing: -0.06em;
  163. }
  164. .rule code {
  165. border-color: var(--bg);
  166. background: rgba(255, 250, 241, 0.18);
  167. color: #fffaf1;
  168. }
  169. .key {
  170. display: flex;
  171. flex-wrap: wrap;
  172. gap: 8px;
  173. }
  174. .pill {
  175. display: inline-flex;
  176. align-items: center;
  177. width: fit-content;
  178. border: 1px solid var(--line);
  179. padding: 4px 8px;
  180. background: var(--panel);
  181. color: var(--fg);
  182. font-size: 0.75rem;
  183. font-weight: 900;
  184. letter-spacing: 0.08em;
  185. text-transform: uppercase;
  186. }
  187. .pill.inverse {
  188. background: var(--accent);
  189. color: #fffaf1;
  190. }
  191. .diagram {
  192. display: block;
  193. width: 100%;
  194. height: auto;
  195. border: 2px solid var(--line);
  196. background: var(--panel);
  197. }
  198. .diagram text {
  199. fill: var(--fg);
  200. font-family:
  201. Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  202. }
  203. .diagram .box {
  204. fill: var(--panel);
  205. stroke: var(--fg);
  206. stroke-width: 2;
  207. }
  208. .diagram .fill {
  209. fill: var(--accent);
  210. stroke: var(--accent);
  211. stroke-width: 2;
  212. }
  213. .diagram .fill-text {
  214. fill: #fffaf1;
  215. }
  216. .diagram .line {
  217. stroke: var(--fg);
  218. stroke-width: 2;
  219. fill: none;
  220. marker-end: url(#arrow);
  221. }
  222. table {
  223. width: 100%;
  224. border-collapse: collapse;
  225. border: 2px solid var(--line);
  226. background: var(--panel);
  227. }
  228. th,
  229. td {
  230. border: 1px solid var(--thin);
  231. padding: 11px 12px;
  232. text-align: left;
  233. vertical-align: top;
  234. }
  235. th {
  236. border-bottom: 2px solid var(--line);
  237. background: var(--accent);
  238. color: #fffaf1;
  239. font-size: 0.75rem;
  240. letter-spacing: 0.12em;
  241. text-transform: uppercase;
  242. }
  243. td.route {
  244. width: 34%;
  245. white-space: nowrap;
  246. }
  247. td.body {
  248. width: 28%;
  249. }
  250. td.body code {
  251. display: block;
  252. white-space: pre-wrap;
  253. line-height: 1.45;
  254. }
  255. td.method {
  256. width: 72px;
  257. font-weight: 900;
  258. letter-spacing: 0.06em;
  259. }
  260. td.context {
  261. width: 150px;
  262. }
  263. td.operation {
  264. width: 210px;
  265. white-space: nowrap;
  266. }
  267. .context-tag {
  268. display: inline-block;
  269. border: 1px solid var(--line);
  270. padding: 3px 7px;
  271. font-size: 0.72rem;
  272. font-weight: 900;
  273. letter-spacing: 0.07em;
  274. text-transform: uppercase;
  275. }
  276. tr.question-row td {
  277. background: #f7e8b7;
  278. }
  279. .request {
  280. background: var(--accent);
  281. color: #fffaf1;
  282. }
  283. .session {
  284. background: var(--panel);
  285. color: var(--fg);
  286. }
  287. .server {
  288. background: var(--dim);
  289. color: var(--fg);
  290. }
  291. .note {
  292. border-left: 6px solid var(--line);
  293. padding: 14px 18px;
  294. background: var(--dim);
  295. }
  296. .toc {
  297. display: grid;
  298. gap: 8px;
  299. }
  300. .toc a {
  301. display: flex;
  302. justify-content: space-between;
  303. gap: 16px;
  304. border-bottom: 1px solid var(--thin);
  305. padding: 8px 0;
  306. color: var(--fg);
  307. text-decoration: none;
  308. }
  309. .toc span {
  310. color: var(--muted);
  311. }
  312. @media (max-width: 980px) {
  313. main {
  314. padding: 28px 16px 56px;
  315. }
  316. header,
  317. .grid {
  318. grid-template-columns: 1fr;
  319. }
  320. .span-12,
  321. .span-8,
  322. .span-6,
  323. .span-4 {
  324. grid-column: 1 / -1;
  325. }
  326. table {
  327. display: block;
  328. overflow-x: auto;
  329. white-space: nowrap;
  330. }
  331. }
  332. </style>
  333. </head>
  334. <body>
  335. <main>
  336. <header>
  337. <div>
  338. <div class="eyebrow">opencode v2</div>
  339. <h1>API map</h1>
  340. </div>
  341. <div class="stack">
  342. <p class="lede">
  343. A single <code>/api</code> route surface for simple clients and multi-directory frontends. The important
  344. design question is not route nesting; it is where runtime context comes from.
  345. </p>
  346. <div class="key">
  347. <span class="pill">Server scoped</span>
  348. <span class="pill inverse">Request context</span>
  349. <span class="pill">Session pinned</span>
  350. </div>
  351. </div>
  352. </header>
  353. <section class="grid">
  354. <article class="span-8 rule">
  355. <strong>Everything has one canonical route. Some routes are server-scoped; runtime routes use context; session item routes use the session.</strong>
  356. <p>
  357. Server-scoped routes manage the whole server: projects, workspace lifecycle, and auth accounts. Runtime
  358. context is for anything resolved from an active directory, including config, provider capabilities, tools,
  359. files, and VCS.
  360. </p>
  361. </article>
  362. <nav class="span-4 panel panel-pad toc" aria-label="Page sections">
  363. <a href="#context"><strong>Context Model</strong><span>how calls resolve</span></a>
  364. <a href="#endpoints"><strong>Endpoint Inventory</strong><span>all planned routes</span></a>
  365. <a href="#events"><strong>Events</strong><span>one envelope</span></a>
  366. <a href="#store"><strong>Frontend Store</strong><span>sync model</span></a>
  367. </nav>
  368. </section>
  369. <section id="context" class="grid">
  370. <div class="span-12 stack">
  371. <h2>Context Model</h2>
  372. <svg class="diagram" viewBox="0 0 1280 360" role="img" aria-labelledby="ctx-title ctx-desc">
  373. <title id="ctx-title">API context resolution</title>
  374. <desc id="ctx-desc">Non-session routes resolve from request context, session item routes resolve from session storage.</desc>
  375. <defs>
  376. <marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
  377. <path d="M0,0 L0,6 L9,3 z" fill="#26342f" />
  378. </marker>
  379. </defs>
  380. <rect class="fill" x="34" y="44" width="294" height="96" />
  381. <text class="fill-text" x="58" y="84" font-size="24" font-weight="900">Non-session route</text>
  382. <text class="fill-text" x="58" y="116" font-size="17">/api/file, /api/vcs/status</text>
  383. <path class="line" d="M328 92 H472" />
  384. <rect class="box" x="486" y="44" width="286" height="96" />
  385. <text x="510" y="84" font-size="24" font-weight="900">Request context</text>
  386. <text x="510" y="116" font-size="17">query params or default runtime</text>
  387. <path class="line" d="M772 92 H916" />
  388. <rect class="box" x="930" y="44" width="316" height="96" />
  389. <text x="954" y="84" font-size="24" font-weight="900">Runtime context</text>
  390. <text x="954" y="116" font-size="17">directory + workspaceID?</text>
  391. <rect class="box" x="34" y="220" width="294" height="96" />
  392. <text x="58" y="260" font-size="24" font-weight="900">Session item route</text>
  393. <text x="58" y="292" font-size="17">/api/session/:id/prompt</text>
  394. <path class="line" d="M328 268 H472" />
  395. <rect class="fill" x="486" y="220" width="286" height="96" />
  396. <text class="fill-text" x="510" y="260" font-size="24" font-weight="900">Session row</text>
  397. <text class="fill-text" x="510" y="292" font-size="17">contains pinned context</text>
  398. <path class="line" d="M772 268 H916" />
  399. <rect class="box" x="930" y="220" width="316" height="96" />
  400. <text x="954" y="260" font-size="24" font-weight="900">Runtime context</text>
  401. <text x="954" y="292" font-size="17">directory + workspaceID?</text>
  402. </svg>
  403. </div>
  404. <article class="span-6 panel panel-pad stack">
  405. <h3>Request-context calls</h3>
  406. <p class="muted">
  407. These calls operate against a directory, optionally through a workspace. Simple clients omit context and
  408. use the default runtime.
  409. </p>
  410. <pre><code>GET /api/fs/tree?path=.&directory=/repo/app&workspace=ws_123</code></pre>
  411. </article>
  412. <article class="span-6 panel panel-pad stack">
  413. <h3>Session-pinned calls</h3>
  414. <p class="muted">
  415. These calls never take request context. The session is already pinned to the directory and workspace it was
  416. created in.
  417. </p>
  418. <pre><code>POST /api/session/ses_123/prompt
  419. // server resolves
  420. sessionID -&gt; { directory, workspaceID? }</code></pre>
  421. </article>
  422. </section>
  423. <section id="endpoints" class="grid">
  424. <div class="span-12 stack">
  425. <h2>Operation Inventory</h2>
  426. <p class="muted">
  427. 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.
  428. </p>
  429. </div>
  430. <article class="span-12 panel panel-pad stack">
  431. <table>
  432. <thead><tr><th>Operation</th><th>Input</th><th>Context</th><th>HTTP mount</th><th>Purpose</th></tr></thead>
  433. <tbody>
  434. <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>
  435. <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>
  436. <tr><td class="operation"><code>auth.create</code></td><td class="body"><code>{
  437. serviceID: ServiceID
  438. credential:
  439. | { type: "oauth", refresh: string, access: string, expires: number }
  440. | { type: "api", key: string, metadata?: Record&lt;string, string&gt; }
  441. description?: string
  442. active?: boolean
  443. }</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>
  444. <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>
  445. <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>
  446. <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>
  447. <tr><td class="operation"><code>auth.update</code></td><td class="body"><code>{
  448. accountID: AccountID
  449. description?: string
  450. credential?:
  451. | { type: "oauth", refresh: string, access: string, expires: number }
  452. | { type: "api", key: string, metadata?: Record&lt;string, string&gt; }
  453. }</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>
  454. <tr><td class="operation"><code>catalog.model.get</code></td><td class="body"><code>{
  455. providerID: ProviderID
  456. modelID: ModelID
  457. }</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>
  458. <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>
  459. <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>
  460. <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>
  461. <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>
  462. <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>
  463. <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>
  464. <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>
  465. <tr><td class="operation"><code>fs.grep</code></td><td class="body"><code>{
  466. pattern: string
  467. include?: string
  468. limit?: number
  469. }</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>
  470. <tr><td class="operation"><code>fs.search</code></td><td class="body"><code>{
  471. query: string
  472. type?: "file" | "directory"
  473. limit?: number
  474. }</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>
  475. <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>
  476. <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>
  477. <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>
  478. <tr><td class="operation"><code>mcp.prompt.render</code></td><td class="body"><code>{
  479. server: string
  480. name: string
  481. arguments?: Record&lt;string, string&gt;
  482. }</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>
  483. <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>
  484. <tr><td class="operation"><code>mcp.resource.read</code></td><td class="body"><code>{
  485. server: string
  486. uri: string
  487. }</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>
  488. <tr><td class="operation"><code>mcp.server.create</code></td><td class="body"><code>{
  489. name: string
  490. config:
  491. | { type: "local", command: string, arguments?: string[], environment?: Record&lt;string, string&gt; }
  492. | { type: "remote", url: string, headers?: Record&lt;string, string&gt;, oauth?: boolean | object }
  493. }</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>
  494. <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>
  495. <tr><td class="operation"><code>mcp.server.oauth.callback</code></td><td class="body"><code>{
  496. name: string
  497. code: string
  498. }</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>
  499. <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>
  500. <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>
  501. <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>
  502. <tr><td class="operation"><code>permission.reply</code></td><td class="body"><code>{
  503. permissionID: PermissionID
  504. response: PermissionReply
  505. }</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>
  506. <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>
  507. <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>
  508. <tr><td class="operation"><code>project.update</code></td><td class="body"><code>{
  509. projectID: ProjectID
  510. name?: string
  511. icon?: string
  512. commands?: Array&lt;{
  513. name: string
  514. command: string
  515. }&gt;
  516. }</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>
  517. <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>
  518. <tr><td class="operation"><code>pty.create</code></td><td class="body"><code>{
  519. command?: string
  520. cwd?: string
  521. shell?: string
  522. }</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>
  523. <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>
  524. <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>
  525. <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>
  526. <tr><td class="operation"><code>pty.update</code></td><td class="body"><code>{
  527. ptyID: PtyID
  528. title?: string
  529. size?: { columns: number, rows: number }
  530. }</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>
  531. <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>
  532. <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>
  533. <tr><td class="operation"><code>question.reply</code></td><td class="body"><code>{
  534. questionID: QuestionID
  535. response: QuestionResponse
  536. }</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>
  537. <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>
  538. <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>
  539. <tr><td class="operation"><code>session.create</code></td><td class="body"><code>{
  540. title?: string
  541. agent?: string
  542. model?: { providerID: ProviderID, modelID: ModelID }
  543. permission?: PermissionRule[]
  544. }</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>
  545. <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>
  546. <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>
  547. <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>
  548. <tr><td class="operation"><code>session.list</code></td><td class="body"><code>{
  549. limit?: number
  550. order?: "asc" | "desc"
  551. path?: string
  552. roots?: boolean
  553. start?: number
  554. search?: string
  555. cursor?: string
  556. }</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>
  557. <tr><td class="operation"><code>session.message.list</code></td><td class="body"><code>{
  558. sessionID: SessionID
  559. limit?: number
  560. order?: "asc" | "desc"
  561. cursor?: string
  562. }</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>
  563. <tr><td class="operation"><code>session.prompt</code></td><td class="body"><code>{
  564. sessionID: SessionID
  565. prompt: Prompt
  566. delivery?: "immediate" | "deferred"
  567. }</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>
  568. <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>
  569. <tr><td class="operation"><code>session.update</code></td><td class="body"><code>{
  570. sessionID: SessionID
  571. title?: string
  572. archived?: number
  573. permission?: PermissionRule[]
  574. }</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>
  575. <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>
  576. <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>
  577. <tr><td class="operation"><code>vcs.diff</code></td><td class="body"><code>{
  578. format?: "json" | "patch"
  579. mode?: "worktree" | "default"
  580. }</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>
  581. <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>
  582. <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>
  583. <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>
  584. <tr><td class="operation"><code>workspace.create</code></td><td class="body"><code>{
  585. projectID?: ProjectID
  586. name?: string
  587. directory?: string
  588. type: string
  589. metadata?: Record&lt;string, unknown&gt;
  590. }</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>
  591. <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>
  592. <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>
  593. <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>
  594. <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>
  595. <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>
  596. <tr><td class="operation"><code>workspace.update</code></td><td class="body"><code>{
  597. workspaceID: WorkspaceID
  598. name?: string
  599. metadata?: Record&lt;string, unknown&gt;
  600. archived?: boolean
  601. }</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>
  602. <tr class="question-row"><td class="operation"><code>workspace.warp</code></td><td class="body"><code>{
  603. workspaceID?: WorkspaceID
  604. sessionID: SessionID
  605. copyChanges: boolean
  606. }</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>
  607. </tbody>
  608. </table>
  609. </article>
  610. </section>
  611. <section id="events" class="grid">
  612. <article class="span-12 panel panel-pad stack">
  613. <h2>Event Envelope</h2>
  614. <p class="muted">
  615. Every event uses the same envelope. Resource identity belongs in <code>payload</code>. Runtime identity belongs
  616. in <code>context</code>.
  617. </p>
  618. <div class="grid">
  619. <pre class="span-6"><code>type ApiEvent&lt;Payload&gt; = {
  620. id: string
  621. type: string
  622. time: number
  623. context: {
  624. directory: string
  625. workspaceID?: string
  626. }
  627. payload: Payload
  628. }</code></pre>
  629. <pre class="span-6"><code>{
  630. "id": "evt_01",
  631. "type": "message.part.delta",
  632. "time": 1760000000000,
  633. "context": {
  634. "directory": "/repo/app",
  635. "workspaceID": "ws_123"
  636. },
  637. "payload": {
  638. "sessionID": "ses_123",
  639. "messageID": "msg_456",
  640. "partID": "part_789",
  641. "field": "text",
  642. "delta": "hello"
  643. }
  644. }</code></pre>
  645. </div>
  646. </article>
  647. </section>
  648. <section id="store" class="grid">
  649. <article class="span-12 panel panel-pad stack">
  650. <h2>Frontend Sync Store</h2>
  651. <p class="muted">
  652. A frontend can keep one giant store like the current TUI. Runtime data is partitioned by
  653. <code>contextKey</code>. Durable entities such as sessions and messages are keyed by their own IDs.
  654. </p>
  655. <pre><code>type RuntimeContext = {
  656. directory: string
  657. workspaceID?: string
  658. }
  659. type ContextKey = string
  660. type SessionID = string
  661. type MessageID = string
  662. type SyncStore = {
  663. status: "loading" | "partial" | "complete"
  664. shared: {
  665. provider: Provider[]
  666. provider_default: Record&lt;string, string&gt;
  667. provider_next: ProviderListResponse
  668. provider_auth: Record&lt;string, ProviderAuthMethod[]&gt;
  669. console_state: ConsoleState
  670. }
  671. contexts: Record&lt;
  672. ContextKey,
  673. {
  674. context: RuntimeContext
  675. config: Config
  676. agent: Agent[]
  677. command: Command[]
  678. lsp: LspStatus[]
  679. formatter: FormatterStatus[]
  680. vcs: VcsInfo | undefined
  681. mcp: Record&lt;string, McpStatus&gt;
  682. mcp_resource: Record&lt;string, McpResource&gt;
  683. session: SessionID[]
  684. session_status: Record&lt;SessionID, SessionStatus&gt;
  685. }
  686. &gt;
  687. session: Record&lt;SessionID, Session &amp; { context: RuntimeContext }&gt;
  688. session_diff: Record&lt;SessionID, Snapshot.FileDiff[]&gt;
  689. todo: Record&lt;SessionID, Todo[]&gt;
  690. permission: Record&lt;SessionID, PermissionRequest[]&gt;
  691. question: Record&lt;SessionID, QuestionRequest[]&gt;
  692. message: Record&lt;SessionID, Message[]&gt;
  693. part: Record&lt;MessageID, Part[]&gt;
  694. }
  695. function contextKey(context: RuntimeContext) {
  696. return `${context.workspaceID ?? "local"}:${context.directory}`
  697. }</code></pre>
  698. </article>
  699. </section>
  700. </main>
  701. </body>
  702. </html>