@expanse-ade/mcp 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # canvas-ade-mcp
2
+
3
+ The **MCP (Model Context Protocol) layer** for Canvas ADE — the tool/resource/prompt contract that
4
+ lets AI coding agents running _inside_ Canvas ADE boards orchestrate the canvas itself. This is what
5
+ turns the canvas from a human-driven cockpit into an **AI-orchestratable swarm environment** with a
6
+ **command board** (an orchestrator agent) driving a fleet of worker agents.
7
+
8
+ > **Status:** planning / docs only. No code yet. Build order lives in [`docs/roadmap.md`](docs/roadmap.md).
9
+
10
+ ---
11
+
12
+ ## What this is (and is NOT)
13
+
14
+ - **IS:** a **standalone package with its own git repo**, living at `Z:\canvas-ade-mcp` — a
15
+ **sibling of the Canvas ADE repo, NOT nested inside it**. It owns the MCP _contract_ — tool +
16
+ resource + prompt schemas, the streamable-HTTP transport, auth (per-board bearer tokens), and the
17
+ capability tier-factory (orchestrator vs worker).
18
+ - **IS NOT:** a standalone _app_. The MCP server has nothing to orchestrate on its own — every tool
19
+ needs live access to PTYs, git worktrees, `WebContentsView`s, and the canvas store, all of which
20
+ live in **Canvas ADE's Electron MAIN process**. Canvas ADE **consumes this package as a dependency**
21
+ (a local linked / path dependency during dev; published + pinned later) and MAIN binds the contract
22
+ to the real implementations.
23
+
24
+ ```
25
+ Z:\
26
+ ├─ Canvas ADE\ ← the Electron app (its own git repo) — CONSUMES canvas-ade-mcp as a dep
27
+ └─ canvas-ade-mcp\ ← THIS repo (separate git repo, sibling — never nested in Canvas ADE)
28
+ ```
29
+
30
+ **Why separate:** keeps the MCP contract versioned + testable on its own, and avoids nesting a second
31
+ git repo inside the Canvas ADE working tree. The two repos are wired only by a dependency edge + the
32
+ live test harness.
33
+
34
+ ```
35
+ ┌──────────────────────────── Canvas ADE (Electron) ────────────────────────────┐
36
+ │ │
37
+ │ MAIN process │
38
+ │ ┌─────────────────────────┐ ┌──────────────────────────────────────┐ │
39
+ │ │ CanvasOrchestrator │◄──────►│ canvas-ade-mcp (THIS PACKAGE) │ │
40
+ │ │ (board state · git · │ binds │ · tool/resource/prompt schemas │ │
41
+ │ │ PTY control · status) │ impls │ · streamable-HTTP transport (/mcp) │ │
42
+ │ └─────────────────────────┘ │ · per-board token auth + tier factory│ │
43
+ │ ▲ └──────────────────────────────────────┘ │
44
+ │ │ IPC ▲ │
45
+ │ ┌────────┴───────────┐ │ loopback HTTP (127.0.0.1) │
46
+ │ │ renderer (human UI) │ │ Bearer <per-board token> │
47
+ │ │ glyph · queue · diff │ ┌────────────┴───────────────┐ │
48
+ │ └─────────────────────┘ │ Terminal boards' agents │ │
49
+ │ │ (Claude Code / Codex / …) │ │
50
+ │ │ = MCP CLIENTS │ │
51
+ │ │ command board = orchestrator│ │
52
+ │ │ others = workers │ │
53
+ │ └──────────────────────────────┘ │
54
+ └─────────────────────────────────────────────────────────────────────────────────┘
55
+ ```
56
+
57
+ **Two views of one core:** the same `CanvasOrchestrator` is exposed to **humans** (renderer UI over
58
+ IPC) and to **agents** (this MCP server over loopback HTTP). The MCP is the second interface.
59
+
60
+ ---
61
+
62
+ ## The non-negotiable rule: every tool is tested against Canvas ADE
63
+
64
+ No MCP tool/resource ships until **both** layers are green. This is the project's whole point —
65
+ it keeps the agent↔canvas connection real, not theoretical.
66
+
67
+ 1. **Contract test** — the tool against a mock `CanvasOrchestrator`. Fast, isolated, validates the
68
+ schema + auth + tier gating + error shapes.
69
+ 2. **Live test** — drive the **real running Canvas ADE** (reuse the `CANVAS_SMOKE=e2e` in-process
70
+ harness; later the planned Playwright `_electron` harness) and assert the tool actually moved the
71
+ canvas: a board appeared, a prompt landed in a PTY, a diff came back, the camera flew.
72
+
73
+ Each phase ends **runnable + committed**, with its live-against-Canvas-ADE test passing — mirroring
74
+ the Canvas ADE roadmap convention.
75
+
76
+ ---
77
+
78
+ ## Core architectural decisions (locked by research, 2026-05-30)
79
+
80
+ These are validated against real prior art (Claude Code Agent Teams, Cursor 3, Warp, Anthropic's
81
+ multi-agent research system, the MCP spec). Full reasoning in the sibling Canvas ADE repo's
82
+ `docs/feature-proposals.md` research + the two research workflows that produced this.
83
+
84
+ | Decision | Why |
85
+ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
86
+ | **Transport = streamable-HTTP on a loopback `localServer` in MAIN** | stdio is process-per-client; we need ONE shared bus for many board-agents → one MAIN. Spec defines streamable-HTTP exactly for this. |
87
+ | **Control plane only — PTY data stays on MessagePort** | MessagePort isn't reachable on `127.0.0.1`; avoids head-of-line blocking of high-volume terminal bytes on shared SSE. |
88
+ | **Capability tiers enforced SERVER-SIDE by token, never by annotation/prompt** | Tool annotations "don't enforce anything." Build a fresh `McpServer` per session from a **factory that registers only the allowed tier's tools** (orchestrator vs worker), decided from the bearer token at `initialize`. |
89
+ | **Command board = orchestrator (elevated tools); other boards = workers (read-only/scoped)** | Canonical lead/subagent pattern. Workers cannot dispatch, spawn, or do git writes. |
90
+ | **Read-only context = resources; mutating actions = tools** | Spec-correct. `boards/status/attention/diff/output/console` are resources; `spawn/send_prompt/commit/merge` are tools with accurate destructive/openWorld annotations. |
91
+ | **Risky tools gated: human-confirm + nonce + audit_log** | `send_prompt`/`answer_permission`/git-writes can write into another agent's shell or touch git. Human-in-the-loop is a MUST, not a SHOULD. |
92
+ | **No OAuth discovery endpoints** | We're a trusted local server with static per-board tokens; advertising `/.well-known/oauth-*` makes Claude Code falsely flag "needs authentication." |
93
+
94
+ ### Security model (inherited + extended)
95
+
96
+ The orchestrator board **is the lethal trifecta** at the system level: it reads untrusted worker
97
+ output + holds dispatch power + has external comms (commit/PR/navigate). Therefore:
98
+
99
+ - **Treat all worker-originated resources as TAINTED.** Never let worker output _auto-trigger_
100
+ `send_prompt`/`broadcast`/`commit`/`open_pr` — always human-confirm.
101
+ - **`answer_permission` = unconditional human-confirm, no auto-answer.** (Approving a prompt inside
102
+ another agent's shell is irreversible.)
103
+ - **Provenance tagging** on every cross-agent message (unspoofable "this came from the orchestrator,
104
+ not your operator") + worker instruction hardening. No single defense suffices.
105
+ - **Replay protection:** single-use nonce + monotonic sequence per dispatch; bind to **opaque
106
+ server-issued board ids, never agent-chosen labels.**
107
+ - **Hard runtime boundary:** every git/file op scoped to the board's own `canvas-ade/<board-id>`
108
+ worktree, server-enforced. Validate `Origin` (403 on bad → blocks DNS-rebinding); bind
109
+ `127.0.0.1`, never `0.0.0.0`.
110
+ - Preserves Canvas ADE's locked invariant: **Browser-board content must never reach the PTY write
111
+ channel**; orchestrator prompt text is trusted-user-only and fully audit-logged.
112
+
113
+ ---
114
+
115
+ ## Dependency on Canvas ADE phases
116
+
117
+ The MCP build can **start now** for the transport/auth/observation layers, but the dispatch + git
118
+ layers bind to features still on the Canvas ADE roadmap:
119
+
120
+ | MCP phase | Needs from Canvas ADE |
121
+ | --------------------------------------------- | ------------------------------------------------------------------ |
122
+ | 0–3 (transport, auth, observation, lifecycle) | nothing hard — board state + spawn exist today |
123
+ | 4 (dispatch) | the `pty.write` channel (exists); persistence for audit (Phase 3) |
124
+ | 6 (git tools) | **git-worktrees-per-board + per-board ports (Canvas ADE Phase 3)** |
125
+ | 8 (best-of-N + merge queue) | worktrees + Duplicate/fan-out (Canvas ADE Phase 3) |
126
+
127
+ See [`docs/roadmap.md`](docs/roadmap.md) for the full phase-by-phase plan.
128
+
129
+ ---
130
+
131
+ ## Layout (planned)
132
+
133
+ ```
134
+ Z:\canvas-ade-mcp\ ← its own git repo (sibling of Z:\Canvas ADE)
135
+ README.md ← this file
136
+ docs/
137
+ roadmap.md ← phased build plan (0 → full), per-phase test gate
138
+ decisions/ ← ADRs specific to the MCP layer (transport, auth, safety)
139
+ src/ ← (added Phase 0) schemas · transport · auth · tier-factory
140
+ test/
141
+ contract/ ← tool-vs-mock-orchestrator tests
142
+ live/ ← tool-vs-real-Canvas-ADE tests (drives Z:\Canvas ADE: CANVAS_SMOKE=e2e / Playwright)
143
+ package.json ← (added Phase 0) standalone package, MCP SDK dep
144
+ ```
@@ -0,0 +1,336 @@
1
+ import { Server } from 'node:http';
2
+ import { Express } from 'express';
3
+
4
+ /** Capability tier of a board's MCP session. */
5
+ type Tier = 'orchestrator' | 'worker';
6
+ /** A coarse permission scope string (refined in later phases). */
7
+ type Scope = string;
8
+ /** Opaque server-issued board id. */
9
+ type BoardId = string;
10
+ /** A row in the per-board token store: what a minted bearer token grants. */
11
+ interface AuthRow {
12
+ boardId: BoardId;
13
+ tier: Tier;
14
+ scopes: Scope[];
15
+ /** Seconds since epoch. Set to board lifetime so long agent runs don't expire. */
16
+ expiresAt?: number;
17
+ }
18
+
19
+ /** Minimal board projection exposed to agents. */
20
+ interface BoardSummary {
21
+ id: BoardId;
22
+ /** Board type — 'terminal' | 'browser' | 'planning' (open string for forward types). */
23
+ type: string;
24
+ /** User-facing board title (trusted-user content; no page/whiteboard content). */
25
+ title: string;
26
+ status: string;
27
+ }
28
+ /**
29
+ * One capped, tail-anchored page of a board's plain-text scrollback (T1.4). The
30
+ * host strips ANSI escape codes server-side (control-sequence injection surface)
31
+ * and slices the last `MAX_OUTPUT_PAGE` chars; older content is reached by passing
32
+ * the returned `nextCursor` back as the next read's cursor. NEVER the raw ring.
33
+ */
34
+ interface BoardOutput {
35
+ /** ANSI-stripped scrollback for this page, in chronological order. */
36
+ text: string;
37
+ /** Total chars currently available (the full clean ring length). */
38
+ total: number;
39
+ /** Chars in this page (`text.length`; always ≤ `MAX_OUTPUT_PAGE`). */
40
+ returned: number;
41
+ /**
42
+ * Tail-anchored cursor (chars-from-end already consumed) for the NEXT, OLDER
43
+ * page; absent once the oldest available char has been returned.
44
+ */
45
+ nextCursor?: number;
46
+ /**
47
+ * True when older output has been discarded by the host's capped ring (honest
48
+ * truncation — the buffer was saturated, so the head is gone for good).
49
+ */
50
+ droppedOlder: boolean;
51
+ }
52
+ /**
53
+ * A board's structured last result (T1.5) — references and a verdict, NOT raw logs
54
+ * (raw scrollback is `BoardOutput`). v1 is an observational shell: until M4's
55
+ * `write_result` tool lets a worker record one, every board reads `{ present: false }`.
56
+ * Designed so M4 fills the optional fields without changing the contract.
57
+ */
58
+ interface BoardResult {
59
+ /** Whether a result has been recorded for this board (false until M4 writes one). */
60
+ present: boolean;
61
+ /** Verdict of the last completed task, e.g. 'success' | 'failure' (open string). */
62
+ status?: string;
63
+ /** One-line human summary (references, not raw logs). */
64
+ summary?: string;
65
+ /** Structured references the worker produced — file paths, PR/issue URLs, etc. */
66
+ refs?: string[];
67
+ /** ISO-8601 timestamp when the result was recorded. */
68
+ at?: string;
69
+ }
70
+ /**
71
+ * A coarse per-board status change (M5 event-driven attention). `status` is a status
72
+ * bucket value (`idle`/`running`/`awaiting-review`/`blocked`/`failed`/`static`) or
73
+ * `'gone'` when the board left the canvas. `result` is attached by the host when the
74
+ * board settles to `idle` and a `write_result` exists (so a barrier can return it).
75
+ */
76
+ interface BoardStatusChange {
77
+ id: BoardId;
78
+ status: string;
79
+ result?: BoardResult;
80
+ }
81
+ /**
82
+ * The fields a worker may record for its OWN board via `write_result` (T4.4) — a verdict,
83
+ * a one-line summary, and structured references (NOT raw logs). All optional. The host
84
+ * stamps `present: true` + `at` and binds the board id to the caller's token, producing
85
+ * the {@link BoardResult} the `canvas://board/{id}/result` resource then serves.
86
+ */
87
+ interface BoardResultInput {
88
+ /** Verdict of the last completed task, e.g. 'success' | 'failure' (open string). */
89
+ status?: string;
90
+ /** One-line human summary (references, not raw logs). */
91
+ summary?: string;
92
+ /** Structured references the worker produced — file paths, PR/issue URLs, etc. */
93
+ refs?: string[];
94
+ }
95
+ /**
96
+ * A read-only slice of the project's persistent memory (T1.7) — the project index
97
+ * (`canvas://memory`) or a per-board summary (`canvas://board/{id}/summary`). It is
98
+ * produced by the sibling Brain/Memory engine's `.canvas/memory/`; 🔒 PASSIVE context
99
+ * only — it grants no action. When that subsystem hasn't written anything (it ships on
100
+ * a separate track), the doc is the empty shell `{ present: false, text: '' }` — the
101
+ * resource gracefully empties, never errors.
102
+ */
103
+ interface MemoryDoc {
104
+ /** Whether the memory engine has produced this document. */
105
+ present: boolean;
106
+ /** The markdown content (empty when absent). */
107
+ text: string;
108
+ }
109
+ /**
110
+ * Durable per-type config an orchestrator may change on a board (T3.3). All optional —
111
+ * only the supplied fields are applied. Host-side, off-type/identity keys are dropped
112
+ * (the canvas patch is filtered by the board's patchable keys), so this is the safe set.
113
+ */
114
+ interface BoardConfig {
115
+ /** Terminal shell (Win: pwsh|powershell|cmd; *nix: $SHELL/zsh/bash). */
116
+ shell?: string;
117
+ /** First PTY line written after the shell starts (the agentic CLI / command). */
118
+ launchCommand?: string;
119
+ /** Working directory for the board. */
120
+ cwd?: string;
121
+ }
122
+ /**
123
+ * The canvas control surface injected by Canvas ADE MAIN. Phase 0 defines the
124
+ * shape; tools wire to it in later phases. Keeping it an interface (with a mock)
125
+ * lets canvas-ade-mcp build + test standalone, with no Electron dependency.
126
+ */
127
+ interface Orchestrator {
128
+ listBoards(): Promise<BoardSummary[]>;
129
+ spawnBoard(input: {
130
+ type: string;
131
+ prompt?: string;
132
+ cwd?: string;
133
+ }): Promise<{
134
+ id: BoardId;
135
+ }>;
136
+ /**
137
+ * Close a board (T3.2). The host drains the board's PTY gracefully (not an abrupt
138
+ * SIGKILL) before removing it from the canvas. Idempotent: closing an absent board
139
+ * resolves. The dirty-worktree prompt arrives with Feature Workspaces (M6).
140
+ */
141
+ closeBoard(boardId: BoardId): Promise<void>;
142
+ /**
143
+ * Change a board's durable config (T3.3) — shell / launchCommand / cwd. Only the
144
+ * supplied fields change; the host filters to the board type's patchable keys.
145
+ */
146
+ configureBoard(boardId: BoardId, config: BoardConfig): Promise<void>;
147
+ dispatchPrompt(boardId: BoardId, text: string): Promise<void>;
148
+ /**
149
+ * 🔒 Record the calling worker's OWN board result (M4 T4.4, worker-tier WRITE). The
150
+ * `boardId` is the caller's token-bound board (never client-supplied), so a worker can
151
+ * only write its own result. Feeds the `canvas://board/{id}/result` resource (T1.5).
152
+ */
153
+ writeResult(boardId: BoardId, result: BoardResultInput): Promise<void>;
154
+ /**
155
+ * 🔒 Interrupt the target terminal board (M4 T4.5) — send Ctrl-C (`\x03`) to its PTY to
156
+ * stop a runaway/long-running command. Orchestrator-tier only. The host gates it behind
157
+ * a single-use nonce + a mandatory human confirm + an audit entry, and rejects any
158
+ * non-terminal target (Browser/Planning never reach a PTY). Content-less.
159
+ */
160
+ interrupt(boardId: BoardId): Promise<void>;
161
+ /**
162
+ * 🔒 Agent-to-agent relay (M4 T4.6) — dispatch `text` from board `sourceId` to board
163
+ * `targetId`, expressed by an ORCHESTRATION connector `sourceId → targetId` (the cable
164
+ * is the route + intent). Orchestrator-tier only. The host validates the directed edge
165
+ * exists and is **terminal → terminal** (never Browser → PTY), then gates the write
166
+ * behind a single-use nonce + a mandatory human confirm + an audit entry. Fire-and-forget.
167
+ */
168
+ relayPrompt(sourceId: BoardId, targetId: BoardId, text: string): Promise<void>;
169
+ /**
170
+ * 🔒 Blocking hand-off (M4 T4.3): write `text` into the target terminal board's PTY,
171
+ * wait until it goes idle, and return its structured last result. Orchestrator-tier
172
+ * only. The host gates it behind a single-use nonce + a mandatory human confirm +
173
+ * an audit entry, and rejects any non-terminal target (Browser/Planning content
174
+ * never reaches a PTY). Resolves to the {@link BoardResult} the target produced.
175
+ */
176
+ handoffPrompt(boardId: BoardId, text: string): Promise<BoardResult>;
177
+ gitDiff(boardId: BoardId): Promise<string>;
178
+ boardStatus(boardId: BoardId): Promise<string>;
179
+ /**
180
+ * Read one capped page of a board's scrollback (T1.4, read-only). `cursor` is the
181
+ * tail-anchored offset from a prior page's `nextCursor`; omit for the newest tail.
182
+ */
183
+ boardOutput(boardId: BoardId, opts?: {
184
+ cursor?: number;
185
+ }): Promise<BoardOutput>;
186
+ /**
187
+ * Read a board's structured last result (T1.5, read-only). Returns the empty shell
188
+ * `{ present: false }` until a result has been recorded (M4 `write_result`).
189
+ */
190
+ boardResult(boardId: BoardId): Promise<BoardResult>;
191
+ /**
192
+ * Read the project memory index (T1.7, 🔒 read-only passive context). Empty shell
193
+ * when the memory engine is absent — graceful, never an error.
194
+ */
195
+ projectMemory(): Promise<MemoryDoc>;
196
+ /**
197
+ * Read a board's memory summary (T1.7, 🔒 read-only passive context). Empty shell
198
+ * when absent.
199
+ */
200
+ boardSummary(boardId: BoardId): Promise<MemoryDoc>;
201
+ /**
202
+ * Subscribe to per-board coarse status changes (M5). MAIN forwards
203
+ * `boardRegistry.ts`'s `subscribeBoardStatus`, attaching the last result when a board
204
+ * settles to `idle`. Returns an unsubscribe fn. Barriers + the attention notifier wake
205
+ * on this instead of polling. SYNCHRONOUS (returns the unsubscribe directly, not a Promise).
206
+ */
207
+ subscribeStatus(listener: (change: BoardStatusChange) => void): () => void;
208
+ }
209
+
210
+ /**
211
+ * In-memory per-board token store. Tokens are minted out-of-band when a board
212
+ * spawns and revoked when it closes — no OAuth flow. In-memory by design:
213
+ * sessions die on MAIN restart (correct for a single-user desktop app).
214
+ */
215
+ declare class TokenStore {
216
+ private readonly rows;
217
+ mint(token: string, row: AuthRow): void;
218
+ revoke(token: string): void;
219
+ get(token: string): AuthRow | undefined;
220
+ }
221
+
222
+ interface McpServerDeps {
223
+ orchestrator: Orchestrator;
224
+ tokens: TokenStore;
225
+ /**
226
+ * Optional single command-orchestrator board id (BUG-021). When set, `relay_prompt` is
227
+ * restricted to the token bound to this board, so a second orchestrator-tier token can't
228
+ * drive orchestration cables it doesn't own. Omit to keep the prior open-to-any-orchestrator
229
+ * behaviour (correct for a single-orchestrator-token deployment).
230
+ */
231
+ commandBoardId?: BoardId;
232
+ }
233
+ interface RunningMcpServer {
234
+ app: Express;
235
+ httpServer: Server;
236
+ port: number;
237
+ setAllowedOrigins(origins: readonly string[]): void;
238
+ close(): Promise<void>;
239
+ }
240
+ /**
241
+ * Creates the loopback streamable-HTTP MCP server and starts listening on an
242
+ * ephemeral 127.0.0.1 port. Pipeline: originGuard -> requireBearerAuth -> /mcp.
243
+ * NO OAuth discovery routes are mounted (resourceMetadataUrl is left unset), so
244
+ * MCP clients use the static per-board bearer token without a "needs auth" flag.
245
+ */
246
+ declare function createMcpHttpServer(deps: McpServerDeps): Promise<RunningMcpServer>;
247
+
248
+ interface MintedToken {
249
+ token: string;
250
+ row: AuthRow;
251
+ }
252
+ /**
253
+ * Mint a crypto-random per-board bearer token, store it, and return token + row.
254
+ * Scopes come from the tier (ADR 0002, D2). Always carries a board-lifetime
255
+ * `expiresAt` (the SDK requires a numeric expiry); override with `ttlSeconds`.
256
+ */
257
+ declare function mintBoardToken(store: TokenStore, input: {
258
+ boardId: BoardId;
259
+ tier: Tier;
260
+ ttlSeconds?: number;
261
+ }): MintedToken;
262
+
263
+ declare const SCOPE_READ: Scope;
264
+ declare const SCOPE_DISPATCH: Scope;
265
+ declare const SCOPE_SPAWN: Scope;
266
+ declare const SCOPE_GIT_WRITE: Scope;
267
+ declare const SCOPE_ANSWER_PERMISSION: Scope;
268
+ /** Default scopes granted to a freshly-minted token of the given tier. */
269
+ declare function defaultScopesFor(tier: Tier): Scope[];
270
+
271
+ interface McpJson {
272
+ mcpServers: {
273
+ 'canvas-ade': {
274
+ type: 'http';
275
+ url: string;
276
+ headers: {
277
+ Authorization: string;
278
+ };
279
+ };
280
+ };
281
+ }
282
+ /**
283
+ * Pure builder for a board worktree's project-scoped .mcp.json: a single loopback
284
+ * streamable-HTTP server entry carrying the board's bearer token. No OAuth
285
+ * discovery is referenced (ADR 0001) — the static bearer token is the authority.
286
+ */
287
+ declare function buildMcpJson(port: number, token: string): McpJson;
288
+ /**
289
+ * Write .mcp.json into a board's worktree dir. Returns the written file path.
290
+ * Written owner-only (0600): the file embeds a plaintext bearer token, so it must
291
+ * not be world-readable on a multi-user box. (POSIX mode is a no-op on Windows.)
292
+ */
293
+ declare function writeMcpJson(dir: string, port: number, token: string): string;
294
+
295
+ /** A no-op Orchestrator for contract tests and standalone runs. */
296
+ declare class MockOrchestrator implements Orchestrator {
297
+ listBoards(): Promise<BoardSummary[]>;
298
+ spawnBoard(_input: {
299
+ type: string;
300
+ prompt?: string;
301
+ cwd?: string;
302
+ }): Promise<{
303
+ id: BoardId;
304
+ }>;
305
+ closeBoard(_boardId: BoardId): Promise<void>;
306
+ configureBoard(_boardId: BoardId, _config: BoardConfig): Promise<void>;
307
+ dispatchPrompt(_boardId: BoardId, _text: string): Promise<void>;
308
+ writeResult(_boardId: BoardId, _result: BoardResultInput): Promise<void>;
309
+ interrupt(_boardId: BoardId): Promise<void>;
310
+ relayPrompt(_sourceId: BoardId, _targetId: BoardId, _text: string): Promise<void>;
311
+ handoffPrompt(_boardId: BoardId, _text: string): Promise<BoardResult>;
312
+ gitDiff(_boardId: BoardId): Promise<string>;
313
+ boardStatus(_boardId: BoardId): Promise<string>;
314
+ boardOutput(_boardId: BoardId, _opts?: {
315
+ cursor?: number;
316
+ }): Promise<BoardOutput>;
317
+ boardResult(_boardId: BoardId): Promise<BoardResult>;
318
+ projectMemory(): Promise<MemoryDoc>;
319
+ boardSummary(_boardId: BoardId): Promise<MemoryDoc>;
320
+ /** @internal subscribers for the M5 status stream. */
321
+ private readonly statusListeners;
322
+ subscribeStatus(listener: (change: BoardStatusChange) => void): () => void;
323
+ /** Test seam: drive a status change through the subscription fan-out. */
324
+ __emitStatus(change: BoardStatusChange): void;
325
+ }
326
+
327
+ /**
328
+ * Hard cap on the chars returned by ONE `canvas://board/{id}/output` page (T1.4 🔒).
329
+ * The MCP output budget is ~25k; we never emit a larger page even if the host
330
+ * over-returns. MUST match the app accessor's page size (`MAX_OUTPUT_PAGE` in
331
+ * `src/main/ptyOutput.ts`) so the tail-anchored cursor math lines up across the
332
+ * two repos. Unit = UTF-16 code units (JS `String.length`), matching the host ring.
333
+ */
334
+ declare const MAX_OUTPUT_PAGE = 25000;
335
+
336
+ export { type AuthRow, type BoardId, type BoardOutput, type BoardResult, type BoardResultInput, type BoardStatusChange, type BoardSummary, MAX_OUTPUT_PAGE, type McpJson, type McpServerDeps, type MemoryDoc, type MintedToken, MockOrchestrator, type Orchestrator, type RunningMcpServer, SCOPE_ANSWER_PERMISSION, SCOPE_DISPATCH, SCOPE_GIT_WRITE, SCOPE_READ, SCOPE_SPAWN, type Scope, type Tier, TokenStore, buildMcpJson, createMcpHttpServer, defaultScopesFor, mintBoardToken, writeMcpJson };