From 592ea61fc63c75544b4a837e277b05fef3154b61 Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 12 Jul 2026 16:35:03 +0200 Subject: [PATCH 1/2] Update kit typings to host API v3; migrate kitchensink off agent.run cate.panel.list gains url and becomes the single enumeration surface; browser.list, editor.active, browser back/forward/current, and the agent.run sugar are removed. Kitchensink's /api/agent-run route now composes agent.open -> send -> dispose, with dispose in a finally. --- .../cate.aisession/src/_kit/cate-host.d.ts | 60 ++++++++++++++++++- .../src/public/cate-host.d.ts | 3 +- extensions/cate.kitchensink/src/server.ts | 33 +++++++--- extensions/cate.mcp/src/_kit/cate-host.d.ts | 60 ++++++++++++++++++- .../cate.mermaid/src/_kit/cate-host.d.ts | 60 ++++++++++++++++++- .../cate.sqlite/src/_kit/cate-host.d.ts | 60 ++++++++++++++++++- extensions/cate.usage/src/_kit/cate-host.d.ts | 60 ++++++++++++++++++- kit/cate-host.d.ts | 60 ++++++++++++++++++- 8 files changed, 380 insertions(+), 16 deletions(-) diff --git a/extensions/cate.aisession/src/_kit/cate-host.d.ts b/extensions/cate.aisession/src/_kit/cate-host.d.ts index 6bffc19..ee7a687 100644 --- a/extensions/cate.aisession/src/_kit/cate-host.d.ts +++ b/extensions/cate.aisession/src/_kit/cate-host.d.ts @@ -42,6 +42,43 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise + /** List this window's panels (requires the `panel` scope). Panels detached + * into other windows are not included. THE single enumeration surface: the + * focused entry answers "what is the user looking at", and browser panels + * carry their `url` (there is no separate browser list). */ + list(): Promise + /** Reveal/focus a panel by id (requires the `panel` scope). */ + focus(panelId: string): Promise +} + +/** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare + * runtime path (same form as `workspace.get().rootPath`), present for panels + * backed by a file (editors, documents). `url` is present for browser panels + * (empty while on the start page). */ +export interface CatePanelInfo { + panelId: string + type: string + title: string + focused: boolean + filePath?: string + url?: string +} + +/** One interactable element in an accessibility `snapshot()`. `ref` is an opaque + * handle to pass back to `click`/`type`/`press`; it is only valid for the + * snapshot it came from (re-snapshot after a navigation or mutation). */ +export interface CateBrowserRef { + ref: string + role: string + name: string + value?: string +} + +/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */ +export interface CateBrowserSnapshot { + url: string + title: string + refs: CateBrowserRef[] } /** A file the user dragged onto this panel, delivered to `cate.files.onDrop`. */ @@ -86,8 +123,29 @@ export interface CateHost { open(opts?: { resume?: string }): Promise<{ sessionId: string } | { error: string }> send(sessionId: string, prompt: string): Promise dispose(sessionId: string): Promise - run(prompt: string): Promise cancel(): Promise } + /** Drive Cate's browser panels (requires the `browser` scope + first-use user + * consent). These panels hold the user's real, logged-in session — treat it + * accordingly. `panelId` picks a target; omitted, the focused (or only) + * browser panel is used. */ + browser: { + /** To enumerate open browser panels, use `cate.panel.list()`. */ + open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> + reload(opts?: { panelId?: string }): Promise<{ ok: true }> + /** Capture a screenshot; returns a host filesystem path (a webview guest + * can't read it directly; a server-backed extension can). */ + screenshot(opts?: { panelId?: string }): Promise<{ path: string }> + snapshot(opts?: { panelId?: string }): Promise + click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> + type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }> + /** Resolve once the panel stops loading (`timeoutMs` defaults to 5000, + * capped at 8000). Rejects in-band with `still-loading`. */ + wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }> + /** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows, + * PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms. + * With `ref` the element is focused first. */ + press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }> + } storage: CateHostStorage } diff --git a/extensions/cate.kitchensink/src/public/cate-host.d.ts b/extensions/cate.kitchensink/src/public/cate-host.d.ts index f371b44..f5b4693 100644 --- a/extensions/cate.kitchensink/src/public/cate-host.d.ts +++ b/extensions/cate.kitchensink/src/public/cate-host.d.ts @@ -15,7 +15,7 @@ interface CateHostTheme { terminal: Record } -/** Result of one agent turn (`cate.agent.send` / `cate.agent.run`): the flattened +/** Result of one agent turn (`cate.agent.send`): the flattened * `text` for convenience plus the raw final assistant `message` from pi (its role * and content blocks — text, tool calls, etc.), or null if the turn produced none. */ interface AgentTurnResult { @@ -92,7 +92,6 @@ interface CateHost { /** Tear down the live session (pi's jsonl stays; reopen via `resume`). */ dispose(sessionId: string): Promise /** One-shot sugar over open -> send -> dispose. */ - run(prompt: string): Promise /** Abort the in-flight turn of this extension's session. */ cancel(): Promise } diff --git a/extensions/cate.kitchensink/src/server.ts b/extensions/cate.kitchensink/src/server.ts index b5e0d62..da67c39 100644 --- a/extensions/cate.kitchensink/src/server.ts +++ b/extensions/cate.kitchensink/src/server.ts @@ -13,7 +13,7 @@ // POST /api/echo echoes the JSON body // GET /ws WebSocket echo // POST /api/cate-roundtrip server calls back into Cate over CATE_API -// POST /api/agent-run server runs one agent turn via cate.agent.run +// POST /api/agent-run server runs one agent turn (agent.open -> send -> dispose) import http from 'http' import crypto from 'crypto' @@ -259,8 +259,9 @@ const server = http.createServer(async (req, res) => { return } - // The server runs one agent turn via cate.agent.run (needs the `agent` scope - // and first-use consent). It resolves only when the agent finishes. + // The server runs one agent turn by composing open -> send -> dispose (needs + // the `agent` scope and first-use consent; there is no one-shot host method). + // It resolves only when the agent finishes. if (pathname === '/api/agent-run' && req.method === 'POST') { const raw = await readBody(req) let prompt = '' @@ -274,14 +275,30 @@ const server = http.createServer(async (req, res) => { return } try { - const result = unwrap(await callCateApi('cate.agent.run', { prompt })) as - | { text?: string; error?: string } + const opened = unwrap(await callCateApi('cate.agent.open', {})) as + | { sessionId?: string; error?: string } | undefined - if (result && typeof result.error === 'string') { - sendJson(res, 200, { ok: false, error: result.error }) + if (!opened || typeof opened.sessionId !== 'string') { + sendJson(res, 200, { ok: false, error: opened?.error ?? 'open-failed' }) return } - sendJson(res, 200, { ok: true, text: result?.text ?? '' }) + try { + const result = unwrap( + await callCateApi('cate.agent.send', { sessionId: opened.sessionId, prompt }), + ) as { text?: string; error?: string } | undefined + if (result && typeof result.error === 'string') { + sendJson(res, 200, { ok: false, error: result.error }) + return + } + sendJson(res, 200, { ok: true, text: result?.text ?? '' }) + } finally { + // Best effort: the session must not outlive the one-shot turn. + try { + await callCateApi('cate.agent.dispose', { sessionId: opened.sessionId }) + } catch { + /* noop */ + } + } } catch (err) { const message = err instanceof Error ? err.message : String(err) sendJson(res, 500, { ok: false, error: message }) diff --git a/extensions/cate.mcp/src/_kit/cate-host.d.ts b/extensions/cate.mcp/src/_kit/cate-host.d.ts index 6bffc19..ee7a687 100644 --- a/extensions/cate.mcp/src/_kit/cate-host.d.ts +++ b/extensions/cate.mcp/src/_kit/cate-host.d.ts @@ -42,6 +42,43 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise + /** List this window's panels (requires the `panel` scope). Panels detached + * into other windows are not included. THE single enumeration surface: the + * focused entry answers "what is the user looking at", and browser panels + * carry their `url` (there is no separate browser list). */ + list(): Promise + /** Reveal/focus a panel by id (requires the `panel` scope). */ + focus(panelId: string): Promise +} + +/** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare + * runtime path (same form as `workspace.get().rootPath`), present for panels + * backed by a file (editors, documents). `url` is present for browser panels + * (empty while on the start page). */ +export interface CatePanelInfo { + panelId: string + type: string + title: string + focused: boolean + filePath?: string + url?: string +} + +/** One interactable element in an accessibility `snapshot()`. `ref` is an opaque + * handle to pass back to `click`/`type`/`press`; it is only valid for the + * snapshot it came from (re-snapshot after a navigation or mutation). */ +export interface CateBrowserRef { + ref: string + role: string + name: string + value?: string +} + +/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */ +export interface CateBrowserSnapshot { + url: string + title: string + refs: CateBrowserRef[] } /** A file the user dragged onto this panel, delivered to `cate.files.onDrop`. */ @@ -86,8 +123,29 @@ export interface CateHost { open(opts?: { resume?: string }): Promise<{ sessionId: string } | { error: string }> send(sessionId: string, prompt: string): Promise dispose(sessionId: string): Promise - run(prompt: string): Promise cancel(): Promise } + /** Drive Cate's browser panels (requires the `browser` scope + first-use user + * consent). These panels hold the user's real, logged-in session — treat it + * accordingly. `panelId` picks a target; omitted, the focused (or only) + * browser panel is used. */ + browser: { + /** To enumerate open browser panels, use `cate.panel.list()`. */ + open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> + reload(opts?: { panelId?: string }): Promise<{ ok: true }> + /** Capture a screenshot; returns a host filesystem path (a webview guest + * can't read it directly; a server-backed extension can). */ + screenshot(opts?: { panelId?: string }): Promise<{ path: string }> + snapshot(opts?: { panelId?: string }): Promise + click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> + type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }> + /** Resolve once the panel stops loading (`timeoutMs` defaults to 5000, + * capped at 8000). Rejects in-band with `still-loading`. */ + wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }> + /** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows, + * PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms. + * With `ref` the element is focused first. */ + press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }> + } storage: CateHostStorage } diff --git a/extensions/cate.mermaid/src/_kit/cate-host.d.ts b/extensions/cate.mermaid/src/_kit/cate-host.d.ts index 6bffc19..ee7a687 100644 --- a/extensions/cate.mermaid/src/_kit/cate-host.d.ts +++ b/extensions/cate.mermaid/src/_kit/cate-host.d.ts @@ -42,6 +42,43 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise + /** List this window's panels (requires the `panel` scope). Panels detached + * into other windows are not included. THE single enumeration surface: the + * focused entry answers "what is the user looking at", and browser panels + * carry their `url` (there is no separate browser list). */ + list(): Promise + /** Reveal/focus a panel by id (requires the `panel` scope). */ + focus(panelId: string): Promise +} + +/** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare + * runtime path (same form as `workspace.get().rootPath`), present for panels + * backed by a file (editors, documents). `url` is present for browser panels + * (empty while on the start page). */ +export interface CatePanelInfo { + panelId: string + type: string + title: string + focused: boolean + filePath?: string + url?: string +} + +/** One interactable element in an accessibility `snapshot()`. `ref` is an opaque + * handle to pass back to `click`/`type`/`press`; it is only valid for the + * snapshot it came from (re-snapshot after a navigation or mutation). */ +export interface CateBrowserRef { + ref: string + role: string + name: string + value?: string +} + +/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */ +export interface CateBrowserSnapshot { + url: string + title: string + refs: CateBrowserRef[] } /** A file the user dragged onto this panel, delivered to `cate.files.onDrop`. */ @@ -86,8 +123,29 @@ export interface CateHost { open(opts?: { resume?: string }): Promise<{ sessionId: string } | { error: string }> send(sessionId: string, prompt: string): Promise dispose(sessionId: string): Promise - run(prompt: string): Promise cancel(): Promise } + /** Drive Cate's browser panels (requires the `browser` scope + first-use user + * consent). These panels hold the user's real, logged-in session — treat it + * accordingly. `panelId` picks a target; omitted, the focused (or only) + * browser panel is used. */ + browser: { + /** To enumerate open browser panels, use `cate.panel.list()`. */ + open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> + reload(opts?: { panelId?: string }): Promise<{ ok: true }> + /** Capture a screenshot; returns a host filesystem path (a webview guest + * can't read it directly; a server-backed extension can). */ + screenshot(opts?: { panelId?: string }): Promise<{ path: string }> + snapshot(opts?: { panelId?: string }): Promise + click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> + type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }> + /** Resolve once the panel stops loading (`timeoutMs` defaults to 5000, + * capped at 8000). Rejects in-band with `still-loading`. */ + wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }> + /** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows, + * PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms. + * With `ref` the element is focused first. */ + press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }> + } storage: CateHostStorage } diff --git a/extensions/cate.sqlite/src/_kit/cate-host.d.ts b/extensions/cate.sqlite/src/_kit/cate-host.d.ts index 6bffc19..ee7a687 100644 --- a/extensions/cate.sqlite/src/_kit/cate-host.d.ts +++ b/extensions/cate.sqlite/src/_kit/cate-host.d.ts @@ -42,6 +42,43 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise + /** List this window's panels (requires the `panel` scope). Panels detached + * into other windows are not included. THE single enumeration surface: the + * focused entry answers "what is the user looking at", and browser panels + * carry their `url` (there is no separate browser list). */ + list(): Promise + /** Reveal/focus a panel by id (requires the `panel` scope). */ + focus(panelId: string): Promise +} + +/** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare + * runtime path (same form as `workspace.get().rootPath`), present for panels + * backed by a file (editors, documents). `url` is present for browser panels + * (empty while on the start page). */ +export interface CatePanelInfo { + panelId: string + type: string + title: string + focused: boolean + filePath?: string + url?: string +} + +/** One interactable element in an accessibility `snapshot()`. `ref` is an opaque + * handle to pass back to `click`/`type`/`press`; it is only valid for the + * snapshot it came from (re-snapshot after a navigation or mutation). */ +export interface CateBrowserRef { + ref: string + role: string + name: string + value?: string +} + +/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */ +export interface CateBrowserSnapshot { + url: string + title: string + refs: CateBrowserRef[] } /** A file the user dragged onto this panel, delivered to `cate.files.onDrop`. */ @@ -86,8 +123,29 @@ export interface CateHost { open(opts?: { resume?: string }): Promise<{ sessionId: string } | { error: string }> send(sessionId: string, prompt: string): Promise dispose(sessionId: string): Promise - run(prompt: string): Promise cancel(): Promise } + /** Drive Cate's browser panels (requires the `browser` scope + first-use user + * consent). These panels hold the user's real, logged-in session — treat it + * accordingly. `panelId` picks a target; omitted, the focused (or only) + * browser panel is used. */ + browser: { + /** To enumerate open browser panels, use `cate.panel.list()`. */ + open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> + reload(opts?: { panelId?: string }): Promise<{ ok: true }> + /** Capture a screenshot; returns a host filesystem path (a webview guest + * can't read it directly; a server-backed extension can). */ + screenshot(opts?: { panelId?: string }): Promise<{ path: string }> + snapshot(opts?: { panelId?: string }): Promise + click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> + type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }> + /** Resolve once the panel stops loading (`timeoutMs` defaults to 5000, + * capped at 8000). Rejects in-band with `still-loading`. */ + wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }> + /** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows, + * PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms. + * With `ref` the element is focused first. */ + press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }> + } storage: CateHostStorage } diff --git a/extensions/cate.usage/src/_kit/cate-host.d.ts b/extensions/cate.usage/src/_kit/cate-host.d.ts index 6bffc19..ee7a687 100644 --- a/extensions/cate.usage/src/_kit/cate-host.d.ts +++ b/extensions/cate.usage/src/_kit/cate-host.d.ts @@ -42,6 +42,43 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise + /** List this window's panels (requires the `panel` scope). Panels detached + * into other windows are not included. THE single enumeration surface: the + * focused entry answers "what is the user looking at", and browser panels + * carry their `url` (there is no separate browser list). */ + list(): Promise + /** Reveal/focus a panel by id (requires the `panel` scope). */ + focus(panelId: string): Promise +} + +/** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare + * runtime path (same form as `workspace.get().rootPath`), present for panels + * backed by a file (editors, documents). `url` is present for browser panels + * (empty while on the start page). */ +export interface CatePanelInfo { + panelId: string + type: string + title: string + focused: boolean + filePath?: string + url?: string +} + +/** One interactable element in an accessibility `snapshot()`. `ref` is an opaque + * handle to pass back to `click`/`type`/`press`; it is only valid for the + * snapshot it came from (re-snapshot after a navigation or mutation). */ +export interface CateBrowserRef { + ref: string + role: string + name: string + value?: string +} + +/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */ +export interface CateBrowserSnapshot { + url: string + title: string + refs: CateBrowserRef[] } /** A file the user dragged onto this panel, delivered to `cate.files.onDrop`. */ @@ -86,8 +123,29 @@ export interface CateHost { open(opts?: { resume?: string }): Promise<{ sessionId: string } | { error: string }> send(sessionId: string, prompt: string): Promise dispose(sessionId: string): Promise - run(prompt: string): Promise cancel(): Promise } + /** Drive Cate's browser panels (requires the `browser` scope + first-use user + * consent). These panels hold the user's real, logged-in session — treat it + * accordingly. `panelId` picks a target; omitted, the focused (or only) + * browser panel is used. */ + browser: { + /** To enumerate open browser panels, use `cate.panel.list()`. */ + open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> + reload(opts?: { panelId?: string }): Promise<{ ok: true }> + /** Capture a screenshot; returns a host filesystem path (a webview guest + * can't read it directly; a server-backed extension can). */ + screenshot(opts?: { panelId?: string }): Promise<{ path: string }> + snapshot(opts?: { panelId?: string }): Promise + click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> + type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }> + /** Resolve once the panel stops loading (`timeoutMs` defaults to 5000, + * capped at 8000). Rejects in-band with `still-loading`. */ + wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }> + /** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows, + * PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms. + * With `ref` the element is focused first. */ + press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }> + } storage: CateHostStorage } diff --git a/kit/cate-host.d.ts b/kit/cate-host.d.ts index e2c231c..16dbe70 100644 --- a/kit/cate-host.d.ts +++ b/kit/cate-host.d.ts @@ -41,6 +41,43 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise + /** List this window's panels (requires the `panel` scope). Panels detached + * into other windows are not included. THE single enumeration surface: the + * focused entry answers "what is the user looking at", and browser panels + * carry their `url` (there is no separate browser list). */ + list(): Promise + /** Reveal/focus a panel by id (requires the `panel` scope). */ + focus(panelId: string): Promise +} + +/** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare + * runtime path (same form as `workspace.get().rootPath`), present for panels + * backed by a file (editors, documents). `url` is present for browser panels + * (empty while on the start page). */ +export interface CatePanelInfo { + panelId: string + type: string + title: string + focused: boolean + filePath?: string + url?: string +} + +/** One interactable element in an accessibility `snapshot()`. `ref` is an opaque + * handle to pass back to `click`/`type`/`press`; it is only valid for the + * snapshot it came from (re-snapshot after a navigation or mutation). */ +export interface CateBrowserRef { + ref: string + role: string + name: string + value?: string +} + +/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */ +export interface CateBrowserSnapshot { + url: string + title: string + refs: CateBrowserRef[] } /** A file the user dragged onto this panel, delivered to `cate.files.onDrop`. */ @@ -85,8 +122,29 @@ export interface CateHost { open(opts?: { resume?: string }): Promise<{ sessionId: string } | { error: string }> send(sessionId: string, prompt: string): Promise dispose(sessionId: string): Promise - run(prompt: string): Promise cancel(): Promise } + /** Drive Cate's browser panels (requires the `browser` scope + first-use user + * consent). These panels hold the user's real, logged-in session — treat it + * accordingly. `panelId` picks a target; omitted, the focused (or only) + * browser panel is used. */ + browser: { + /** To enumerate open browser panels, use `cate.panel.list()`. */ + open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> + reload(opts?: { panelId?: string }): Promise<{ ok: true }> + /** Capture a screenshot; returns a host filesystem path (a webview guest + * can't read it directly; a server-backed extension can). */ + screenshot(opts?: { panelId?: string }): Promise<{ path: string }> + snapshot(opts?: { panelId?: string }): Promise + click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> + type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }> + /** Resolve once the panel stops loading (`timeoutMs` defaults to 5000, + * capped at 8000). Rejects in-band with `still-loading`. */ + wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }> + /** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows, + * PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms. + * With `ref` the element is focused first. */ + press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }> + } storage: CateHostStorage } From 9c346d5e626ead72a2786b095c5c6a3082ea36c2 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 18 Jul 2026 14:56:46 +0200 Subject: [PATCH 2/2] Add panel.close to kit typings, fix list and screenshot notes close() is exposed by the preload and documented in the Cate repo but was missing here. panel.list spans the workspace's windows, and screenshots now land in the OS temp dir instead of the Desktop. --- extensions/cate.aisession/src/_kit/cate-host.d.ts | 11 +++++++---- extensions/cate.mcp/src/_kit/cate-host.d.ts | 11 +++++++---- extensions/cate.mermaid/src/_kit/cate-host.d.ts | 11 +++++++---- extensions/cate.sqlite/src/_kit/cate-host.d.ts | 11 +++++++---- extensions/cate.usage/src/_kit/cate-host.d.ts | 11 +++++++---- kit/cate-host.d.ts | 11 +++++++---- 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/extensions/cate.aisession/src/_kit/cate-host.d.ts b/extensions/cate.aisession/src/_kit/cate-host.d.ts index ee7a687..1cf2d47 100644 --- a/extensions/cate.aisession/src/_kit/cate-host.d.ts +++ b/extensions/cate.aisession/src/_kit/cate-host.d.ts @@ -42,13 +42,16 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise - /** List this window's panels (requires the `panel` scope). Panels detached - * into other windows are not included. THE single enumeration surface: the + /** List panels across this workspace's windows (requires the `panel` scope). + * THE single enumeration surface: the * focused entry answers "what is the user looking at", and browser panels * carry their `url` (there is no separate browser list). */ list(): Promise /** Reveal/focus a panel by id (requires the `panel` scope). */ focus(panelId: string): Promise + /** Close a panel through its normal dirty/running confirmation path. Does not + * reveal or focus the panel first (requires the `panel` scope). */ + close(panelId: string): Promise } /** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare @@ -133,8 +136,8 @@ export interface CateHost { /** To enumerate open browser panels, use `cate.panel.list()`. */ open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> reload(opts?: { panelId?: string }): Promise<{ ok: true }> - /** Capture a screenshot; returns a host filesystem path (a webview guest - * can't read it directly; a server-backed extension can). */ + /** Capture a screenshot; returns a host filesystem path in the OS temp dir + * (a webview guest can't read it directly; a server-backed extension can). */ screenshot(opts?: { panelId?: string }): Promise<{ path: string }> snapshot(opts?: { panelId?: string }): Promise click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> diff --git a/extensions/cate.mcp/src/_kit/cate-host.d.ts b/extensions/cate.mcp/src/_kit/cate-host.d.ts index ee7a687..1cf2d47 100644 --- a/extensions/cate.mcp/src/_kit/cate-host.d.ts +++ b/extensions/cate.mcp/src/_kit/cate-host.d.ts @@ -42,13 +42,16 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise - /** List this window's panels (requires the `panel` scope). Panels detached - * into other windows are not included. THE single enumeration surface: the + /** List panels across this workspace's windows (requires the `panel` scope). + * THE single enumeration surface: the * focused entry answers "what is the user looking at", and browser panels * carry their `url` (there is no separate browser list). */ list(): Promise /** Reveal/focus a panel by id (requires the `panel` scope). */ focus(panelId: string): Promise + /** Close a panel through its normal dirty/running confirmation path. Does not + * reveal or focus the panel first (requires the `panel` scope). */ + close(panelId: string): Promise } /** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare @@ -133,8 +136,8 @@ export interface CateHost { /** To enumerate open browser panels, use `cate.panel.list()`. */ open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> reload(opts?: { panelId?: string }): Promise<{ ok: true }> - /** Capture a screenshot; returns a host filesystem path (a webview guest - * can't read it directly; a server-backed extension can). */ + /** Capture a screenshot; returns a host filesystem path in the OS temp dir + * (a webview guest can't read it directly; a server-backed extension can). */ screenshot(opts?: { panelId?: string }): Promise<{ path: string }> snapshot(opts?: { panelId?: string }): Promise click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> diff --git a/extensions/cate.mermaid/src/_kit/cate-host.d.ts b/extensions/cate.mermaid/src/_kit/cate-host.d.ts index ee7a687..1cf2d47 100644 --- a/extensions/cate.mermaid/src/_kit/cate-host.d.ts +++ b/extensions/cate.mermaid/src/_kit/cate-host.d.ts @@ -42,13 +42,16 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise - /** List this window's panels (requires the `panel` scope). Panels detached - * into other windows are not included. THE single enumeration surface: the + /** List panels across this workspace's windows (requires the `panel` scope). + * THE single enumeration surface: the * focused entry answers "what is the user looking at", and browser panels * carry their `url` (there is no separate browser list). */ list(): Promise /** Reveal/focus a panel by id (requires the `panel` scope). */ focus(panelId: string): Promise + /** Close a panel through its normal dirty/running confirmation path. Does not + * reveal or focus the panel first (requires the `panel` scope). */ + close(panelId: string): Promise } /** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare @@ -133,8 +136,8 @@ export interface CateHost { /** To enumerate open browser panels, use `cate.panel.list()`. */ open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> reload(opts?: { panelId?: string }): Promise<{ ok: true }> - /** Capture a screenshot; returns a host filesystem path (a webview guest - * can't read it directly; a server-backed extension can). */ + /** Capture a screenshot; returns a host filesystem path in the OS temp dir + * (a webview guest can't read it directly; a server-backed extension can). */ screenshot(opts?: { panelId?: string }): Promise<{ path: string }> snapshot(opts?: { panelId?: string }): Promise click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> diff --git a/extensions/cate.sqlite/src/_kit/cate-host.d.ts b/extensions/cate.sqlite/src/_kit/cate-host.d.ts index ee7a687..1cf2d47 100644 --- a/extensions/cate.sqlite/src/_kit/cate-host.d.ts +++ b/extensions/cate.sqlite/src/_kit/cate-host.d.ts @@ -42,13 +42,16 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise - /** List this window's panels (requires the `panel` scope). Panels detached - * into other windows are not included. THE single enumeration surface: the + /** List panels across this workspace's windows (requires the `panel` scope). + * THE single enumeration surface: the * focused entry answers "what is the user looking at", and browser panels * carry their `url` (there is no separate browser list). */ list(): Promise /** Reveal/focus a panel by id (requires the `panel` scope). */ focus(panelId: string): Promise + /** Close a panel through its normal dirty/running confirmation path. Does not + * reveal or focus the panel first (requires the `panel` scope). */ + close(panelId: string): Promise } /** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare @@ -133,8 +136,8 @@ export interface CateHost { /** To enumerate open browser panels, use `cate.panel.list()`. */ open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> reload(opts?: { panelId?: string }): Promise<{ ok: true }> - /** Capture a screenshot; returns a host filesystem path (a webview guest - * can't read it directly; a server-backed extension can). */ + /** Capture a screenshot; returns a host filesystem path in the OS temp dir + * (a webview guest can't read it directly; a server-backed extension can). */ screenshot(opts?: { panelId?: string }): Promise<{ path: string }> snapshot(opts?: { panelId?: string }): Promise click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> diff --git a/extensions/cate.usage/src/_kit/cate-host.d.ts b/extensions/cate.usage/src/_kit/cate-host.d.ts index ee7a687..1cf2d47 100644 --- a/extensions/cate.usage/src/_kit/cate-host.d.ts +++ b/extensions/cate.usage/src/_kit/cate-host.d.ts @@ -42,13 +42,16 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise - /** List this window's panels (requires the `panel` scope). Panels detached - * into other windows are not included. THE single enumeration surface: the + /** List panels across this workspace's windows (requires the `panel` scope). + * THE single enumeration surface: the * focused entry answers "what is the user looking at", and browser panels * carry their `url` (there is no separate browser list). */ list(): Promise /** Reveal/focus a panel by id (requires the `panel` scope). */ focus(panelId: string): Promise + /** Close a panel through its normal dirty/running confirmation path. Does not + * reveal or focus the panel first (requires the `panel` scope). */ + close(panelId: string): Promise } /** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare @@ -133,8 +136,8 @@ export interface CateHost { /** To enumerate open browser panels, use `cate.panel.list()`. */ open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> reload(opts?: { panelId?: string }): Promise<{ ok: true }> - /** Capture a screenshot; returns a host filesystem path (a webview guest - * can't read it directly; a server-backed extension can). */ + /** Capture a screenshot; returns a host filesystem path in the OS temp dir + * (a webview guest can't read it directly; a server-backed extension can). */ screenshot(opts?: { panelId?: string }): Promise<{ path: string }> snapshot(opts?: { panelId?: string }): Promise click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }> diff --git a/kit/cate-host.d.ts b/kit/cate-host.d.ts index 16dbe70..36f7039 100644 --- a/kit/cate-host.d.ts +++ b/kit/cate-host.d.ts @@ -41,13 +41,16 @@ export interface CateHostStorage { export interface CatePanel { readonly id: string setTitle(title: string): Promise - /** List this window's panels (requires the `panel` scope). Panels detached - * into other windows are not included. THE single enumeration surface: the + /** List panels across this workspace's windows (requires the `panel` scope). + * THE single enumeration surface: the * focused entry answers "what is the user looking at", and browser panels * carry their `url` (there is no separate browser list). */ list(): Promise /** Reveal/focus a panel by id (requires the `panel` scope). */ focus(panelId: string): Promise + /** Close a panel through its normal dirty/running confirmation path. Does not + * reveal or focus the panel first (requires the `panel` scope). */ + close(panelId: string): Promise } /** One open panel, as reported by `cate.panel.list()`. `filePath` is the bare @@ -132,8 +135,8 @@ export interface CateHost { /** To enumerate open browser panels, use `cate.panel.list()`. */ open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }> reload(opts?: { panelId?: string }): Promise<{ ok: true }> - /** Capture a screenshot; returns a host filesystem path (a webview guest - * can't read it directly; a server-backed extension can). */ + /** Capture a screenshot; returns a host filesystem path in the OS temp dir + * (a webview guest can't read it directly; a server-backed extension can). */ screenshot(opts?: { panelId?: string }): Promise<{ path: string }> snapshot(opts?: { panelId?: string }): Promise click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }>