From 5fc26c958ad53e444967e075a77a47015e4fd0ad Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 14 Nov 2025 12:32:43 -0500 Subject: add global.event.subscribe() to sdk --- packages/opencode/src/bus/global.ts | 10 + packages/opencode/src/bus/index.ts | 5 + packages/opencode/src/server/server.ts | 46 + packages/sdk/js/src/gen/sdk.gen.ts | 41 +- packages/sdk/js/src/gen/types.gen.ts | 2042 ++++++++++++++++---------------- 5 files changed, 1119 insertions(+), 1025 deletions(-) create mode 100644 packages/opencode/src/bus/global.ts diff --git a/packages/opencode/src/bus/global.ts b/packages/opencode/src/bus/global.ts new file mode 100644 index 000000000..b592cd398 --- /dev/null +++ b/packages/opencode/src/bus/global.ts @@ -0,0 +1,10 @@ +import { EventEmitter } from "events" + +export const GlobalBus = new EventEmitter<{ + event: [ + { + directory: string + payload: any + }, + ] +}>() diff --git a/packages/opencode/src/bus/index.ts b/packages/opencode/src/bus/index.ts index f4dd3ed2c..22e145199 100644 --- a/packages/opencode/src/bus/index.ts +++ b/packages/opencode/src/bus/index.ts @@ -2,6 +2,7 @@ import z from "zod" import type { ZodType } from "zod" import { Log } from "../util/log" import { Instance } from "../project/instance" +import { GlobalBus } from "./global" export namespace Bus { const log = Log.create({ service: "bus" }) @@ -65,6 +66,10 @@ export namespace Bus { pending.push(sub(payload)) } } + GlobalBus.emit("event", { + directory: Instance.directory, + payload, + }) return Promise.all(pending) } diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 2dee6c915..034efa84a 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -40,6 +40,7 @@ import type { ContentfulStatusCode } from "hono/utils/http-status" import { TuiEvent } from "@/cli/cmd/tui/event" import { Snapshot } from "@/snapshot" import { SessionSummary } from "@/session/summary" +import { GlobalBus } from "@/bus/global" const ERRORS = { 400: { @@ -117,6 +118,51 @@ export namespace Server { timer.stop() } }) + .get( + "/global/event", + describeRoute({ + description: "Get events", + operationId: "global.event.subscribe", + responses: { + 200: { + description: "Event stream", + content: { + "text/event-stream": { + schema: resolver( + Bus.payloads().meta({ + ref: "Event", + }), + ), + }, + }, + }, + }, + }), + async (c) => { + log.info("global event connected") + return streamSSE(c, async (stream) => { + stream.writeSSE({ + data: JSON.stringify({ + type: "server.connected", + properties: {}, + }), + }) + async function handler(event: any) { + await stream.writeSSE({ + data: JSON.stringify(event), + }) + } + GlobalBus.on("event", handler) + await new Promise((resolve) => { + stream.onAbort(() => { + GlobalBus.off("event", handler) + resolve() + log.info("global event disconnected") + }) + }) + }) + }, + ) .use(async (c, next) => { const directory = c.req.query("directory") ?? process.cwd() return Instance.provide({ diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts index 6987eb471..c41815624 100644 --- a/packages/sdk/js/src/gen/sdk.gen.ts +++ b/packages/sdk/js/src/gen/sdk.gen.ts @@ -2,6 +2,8 @@ import type { Options as ClientOptions, TDataShape, Client } from "./client/index.js" import type { + GlobalEventButtData, + GlobalEventButtResponses, ProjectListData, ProjectListResponses, ProjectCurrentData, @@ -175,6 +177,32 @@ class _HeyApiClient { } } +class Event extends _HeyApiClient { + /** + * Get events + */ + public butt(options?: Options) { + return (options?.client ?? this._client).get.sse({ + url: "/global/event", + ...options, + }) + } + + /** + * Get events + */ + public subscribe(options?: Options) { + return (options?.client ?? this._client).get.sse({ + url: "/event", + ...options, + }) + } +} + +class Global extends _HeyApiClient { + event = new Event({ client: this._client }) +} + class Project extends _HeyApiClient { /** * List all projects @@ -828,18 +856,6 @@ class Auth extends _HeyApiClient { } } -class Event extends _HeyApiClient { - /** - * Get events - */ - public subscribe(options?: Options) { - return (options?.client ?? this._client).get.sse({ - url: "/event", - ...options, - }) - } -} - export class OpencodeClient extends _HeyApiClient { /** * Respond to a permission request @@ -860,6 +876,7 @@ export class OpencodeClient extends _HeyApiClient { }, }) } + global = new Global({ client: this._client }) project = new Project({ client: this._client }) config = new Config({ client: this._client }) tool = new Tool({ client: this._client }) diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 1d90fe106..8a04af8c6 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -1,547 +1,468 @@ // This file is auto-generated by @hey-api/openapi-ts -export type Project = { +export type EventInstallationUpdated = { + type: "installation.updated" + properties: { + version: string + } +} + +export type EventLspClientDiagnostics = { + type: "lsp.client.diagnostics" + properties: { + serverID: string + path: string + } +} + +export type EventLspUpdated = { + type: "lsp.updated" + properties: { + [key: string]: unknown + } +} + +export type FileDiff = { + file: string + before: string + after: string + additions: number + deletions: number +} + +export type UserMessage = { id: string - worktree: string - vcs?: "git" + sessionID: string + role: "user" time: { created: number - initialized?: number + } + summary?: { + title?: string + body?: string + diffs: Array } } -/** - * Custom keybind configurations - */ -export type KeybindsConfig = { - /** - * Leader key for keybind combinations - */ - leader?: string - /** - * Exit the application - */ - app_exit?: string - /** - * Open external editor - */ - editor_open?: string - /** - * List available themes - */ - theme_list?: string - /** - * Toggle sidebar - */ - sidebar_toggle?: string - /** - * View status - */ - status_view?: string - /** - * Export session to editor - */ - session_export?: string - /** - * Create a new session - */ - session_new?: string - /** - * List all sessions - */ - session_list?: string - /** - * Show session timeline - */ - session_timeline?: string - /** - * Share current session - */ - session_share?: string - /** - * Unshare current session - */ - session_unshare?: string - /** - * Interrupt current session - */ - session_interrupt?: string - /** - * Compact the session - */ - session_compact?: string - /** - * Scroll messages up by one page - */ - messages_page_up?: string - /** - * Scroll messages down by one page - */ - messages_page_down?: string - /** - * Scroll messages up by half page - */ - messages_half_page_up?: string - /** - * Scroll messages down by half page - */ - messages_half_page_down?: string - /** - * Navigate to first message - */ - messages_first?: string - /** - * Navigate to last message - */ - messages_last?: string - /** - * Copy message - */ - messages_copy?: string - /** - * Undo message - */ - messages_undo?: string - /** - * Redo message - */ - messages_redo?: string - /** - * Toggle code block concealment in messages - */ - messages_toggle_conceal?: string - /** - * List available models - */ - model_list?: string - /** - * Next recently used model - */ - model_cycle_recent?: string - /** - * Previous recently used model - */ - model_cycle_recent_reverse?: string - /** - * List available commands - */ - command_list?: string - /** - * List agents - */ - agent_list?: string - /** - * Next agent - */ - agent_cycle?: string - /** - * Previous agent - */ - agent_cycle_reverse?: string - /** - * Clear input field - */ - input_clear?: string - /** - * Forward delete - */ - input_forward_delete?: string - /** - * Paste from clipboard - */ - input_paste?: string - /** - * Submit input - */ - input_submit?: string - /** - * Insert newline in input - */ - input_newline?: string - /** - * Previous history item - */ - history_previous?: string - /** - * Next history item - */ - history_next?: string - /** - * Next child session - */ - session_child_cycle?: string - /** - * Previous child session - */ - session_child_cycle_reverse?: string +export type ProviderAuthError = { + name: "ProviderAuthError" + data: { + providerID: string + message: string + } } -export type AgentConfig = { - model?: string - temperature?: number - top_p?: number - prompt?: string - tools?: { - [key: string]: boolean +export type UnknownError = { + name: "UnknownError" + data: { + message: string } - disable?: boolean - /** - * Description of when to use the agent - */ - description?: string - mode?: "subagent" | "primary" | "all" - /** - * Hex color code for the agent (e.g., #FF5733) - */ - color?: string - permission?: { - edit?: "ask" | "allow" | "deny" - bash?: - | ("ask" | "allow" | "deny") - | { - [key: string]: "ask" | "allow" | "deny" - } - webfetch?: "ask" | "allow" | "deny" - doom_loop?: "ask" | "allow" | "deny" - external_directory?: "ask" | "allow" | "deny" +} + +export type MessageOutputLengthError = { + name: "MessageOutputLengthError" + data: { + [key: string]: unknown } - [key: string]: - | unknown - | string - | number - | { - [key: string]: boolean - } - | boolean - | ("subagent" | "primary" | "all") - | { - edit?: "ask" | "allow" | "deny" - bash?: - | ("ask" | "allow" | "deny") - | { - [key: string]: "ask" | "allow" | "deny" - } - webfetch?: "ask" | "allow" | "deny" - doom_loop?: "ask" | "allow" | "deny" - external_directory?: "ask" | "allow" | "deny" - } - | undefined } -export type McpLocalConfig = { - /** - * Type of MCP server connection - */ - type: "local" - /** - * Command and arguments to run the MCP server - */ - command: Array - /** - * Environment variables to set when running the MCP server - */ - environment?: { - [key: string]: string +export type MessageAbortedError = { + name: "MessageAbortedError" + data: { + message: string } - /** - * Enable or disable the MCP server on startup - */ - enabled?: boolean - /** - * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified. - */ - timeout?: number } -export type McpRemoteConfig = { - /** - * Type of MCP server connection - */ - type: "remote" - /** - * URL of the remote MCP server - */ +export type ApiError = { + name: "APIError" + data: { + message: string + statusCode?: number + isRetryable: boolean + responseHeaders?: { + [key: string]: string + } + responseBody?: string + } +} + +export type AssistantMessage = { + id: string + sessionID: string + role: "assistant" + time: { + created: number + completed?: number + } + error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError + parentID: string + modelID: string + providerID: string + mode: string + path: { + cwd: string + root: string + } + summary?: boolean + cost: number + tokens: { + input: number + output: number + reasoning: number + cache: { + read: number + write: number + } + } +} + +export type Message = UserMessage | AssistantMessage + +export type EventMessageUpdated = { + type: "message.updated" + properties: { + info: Message + } +} + +export type EventMessageRemoved = { + type: "message.removed" + properties: { + sessionID: string + messageID: string + } +} + +export type TextPart = { + id: string + sessionID: string + messageID: string + type: "text" + text: string + synthetic?: boolean + time?: { + start: number + end?: number + } + metadata?: { + [key: string]: unknown + } +} + +export type ReasoningPart = { + id: string + sessionID: string + messageID: string + type: "reasoning" + text: string + metadata?: { + [key: string]: unknown + } + time: { + start: number + end?: number + } +} + +export type FilePartSourceText = { + value: string + start: number + end: number +} + +export type FileSource = { + text: FilePartSourceText + type: "file" + path: string +} + +export type Range = { + start: { + line: number + character: number + } + end: { + line: number + character: number + } +} + +export type SymbolSource = { + text: FilePartSourceText + type: "symbol" + path: string + range: Range + name: string + kind: number +} + +export type FilePartSource = FileSource | SymbolSource + +export type FilePart = { + id: string + sessionID: string + messageID: string + type: "file" + mime: string + filename?: string url: string - /** - * Enable or disable the MCP server on startup - */ - enabled?: boolean - /** - * Headers to send with the request - */ - headers?: { - [key: string]: string + source?: FilePartSource +} + +export type ToolStatePending = { + status: "pending" + input: { + [key: string]: unknown } - /** - * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified. - */ - timeout?: number + raw: string } -/** - * @deprecated Always uses stretch layout. - */ -export type LayoutConfig = "auto" | "stretch" +export type ToolStateRunning = { + status: "running" + input: { + [key: string]: unknown + } + title?: string + metadata?: { + [key: string]: unknown + } + time: { + start: number + } +} -export type Config = { - /** - * JSON schema reference for configuration validation - */ - $schema?: string - /** - * Theme name to use for the interface - */ - theme?: string - keybinds?: KeybindsConfig - /** - * TUI specific settings - */ - tui?: { - /** - * TUI scroll speed - */ - scroll_speed?: number - /** - * Scroll acceleration settings - */ - scroll_acceleration?: { - /** - * Enable scroll acceleration - */ - enabled: boolean - } +export type ToolStateCompleted = { + status: "completed" + input: { + [key: string]: unknown } - /** - * Command configuration, see https://opencode.ai/docs/commands - */ - command?: { - [key: string]: { - template: string - description?: string - agent?: string - model?: string - subtask?: boolean - } + output: string + title: string + metadata: { + [key: string]: unknown } - watcher?: { - ignore?: Array + time: { + start: number + end: number + compacted?: number } - plugin?: Array - snapshot?: boolean - /** - * Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing - */ - share?: "manual" | "auto" | "disabled" - /** - * @deprecated Use 'share' field instead. Share newly created sessions automatically - */ - autoshare?: boolean - /** - * Automatically update to the latest version - */ - autoupdate?: boolean - /** - * Disable providers that are loaded automatically - */ - disabled_providers?: Array - /** - * Model to use in the format of provider/model, eg anthropic/claude-2 - */ - model?: string - /** - * Small model to use for tasks like title generation in the format of provider/model - */ - small_model?: string - /** - * Custom username to display in conversations instead of system username - */ - username?: string - /** - * @deprecated Use `agent` field instead. - */ - mode?: { - build?: AgentConfig - plan?: AgentConfig - [key: string]: AgentConfig | undefined + attachments?: Array +} + +export type ToolStateError = { + status: "error" + input: { + [key: string]: unknown } - /** - * Agent configuration, see https://opencode.ai/docs/agent - */ - agent?: { - plan?: AgentConfig - build?: AgentConfig - general?: AgentConfig - [key: string]: AgentConfig | undefined + error: string + metadata?: { + [key: string]: unknown } - /** - * Custom provider configurations and model overrides - */ - provider?: { - [key: string]: { - api?: string - name?: string - env?: Array - id?: string - npm?: string - models?: { - [key: string]: { - id?: string - name?: string - release_date?: string - attachment?: boolean - reasoning?: boolean - temperature?: boolean - tool_call?: boolean - cost?: { - input: number - output: number - cache_read?: number - cache_write?: number - } - limit?: { - context: number - output: number - } - modalities?: { - input: Array<"text" | "audio" | "image" | "video" | "pdf"> - output: Array<"text" | "audio" | "image" | "video" | "pdf"> - } - experimental?: boolean - status?: "alpha" | "beta" | "deprecated" - options?: { - [key: string]: unknown - } - headers?: { - [key: string]: string - } - provider?: { - npm: string - } - } - } - options?: { - apiKey?: string - baseURL?: string - /** - * GitHub Enterprise URL for copilot authentication - */ - enterpriseUrl?: string - /** - * Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout. - */ - timeout?: number | false - [key: string]: unknown | string | (number | false) | undefined - } - } + time: { + start: number + end: number } - /** - * MCP (Model Context Protocol) server configurations - */ - mcp?: { - [key: string]: McpLocalConfig | McpRemoteConfig +} + +export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError + +export type ToolPart = { + id: string + sessionID: string + messageID: string + type: "tool" + callID: string + tool: string + state: ToolState + metadata?: { + [key: string]: unknown } - formatter?: { - [key: string]: { - disabled?: boolean - command?: Array - environment?: { - [key: string]: string - } - extensions?: Array +} + +export type StepStartPart = { + id: string + sessionID: string + messageID: string + type: "step-start" + snapshot?: string +} + +export type StepFinishPart = { + id: string + sessionID: string + messageID: string + type: "step-finish" + reason: string + snapshot?: string + cost: number + tokens: { + input: number + output: number + reasoning: number + cache: { + read: number + write: number } } - lsp?: { - [key: string]: - | { - disabled: true - } - | { - command: Array - extensions?: Array - disabled?: boolean - env?: { - [key: string]: string - } - initialization?: { - [key: string]: unknown - } - } +} + +export type SnapshotPart = { + id: string + sessionID: string + messageID: string + type: "snapshot" + snapshot: string +} + +export type PatchPart = { + id: string + sessionID: string + messageID: string + type: "patch" + hash: string + files: Array +} + +export type AgentPart = { + id: string + sessionID: string + messageID: string + type: "agent" + name: string + source?: { + value: string + start: number + end: number } - /** - * Additional instruction files or patterns to include - */ - instructions?: Array - layout?: LayoutConfig - permission?: { - edit?: "ask" | "allow" | "deny" - bash?: - | ("ask" | "allow" | "deny") - | { - [key: string]: "ask" | "allow" | "deny" - } - webfetch?: "ask" | "allow" | "deny" - doom_loop?: "ask" | "allow" | "deny" - external_directory?: "ask" | "allow" | "deny" +} + +export type RetryPart = { + id: string + sessionID: string + messageID: string + type: "retry" + attempt: number + error: ApiError + time: { + created: number } - tools?: { - [key: string]: boolean +} + +export type Part = + | TextPart + | ReasoningPart + | FilePart + | ToolPart + | StepStartPart + | StepFinishPart + | SnapshotPart + | PatchPart + | AgentPart + | RetryPart + +export type EventMessagePartUpdated = { + type: "message.part.updated" + properties: { + part: Part + delta?: string } - experimental?: { - hook?: { - file_edited?: { - [key: string]: Array<{ - command: Array - environment?: { - [key: string]: string - } - }> - } - session_completed?: Array<{ - command: Array - environment?: { - [key: string]: string - } - }> - } - /** - * Number of retries for chat completions on failure - */ - chatMaxRetries?: number - disable_paste_summary?: boolean +} + +export type EventMessagePartRemoved = { + type: "message.part.removed" + properties: { + sessionID: string + messageID: string + partID: string } } -export type BadRequestError = { - data: unknown | null - errors: Array<{ +export type EventSessionCompacted = { + type: "session.compacted" + properties: { + sessionID: string + } +} + +export type Permission = { + id: string + type: string + pattern?: string | Array + sessionID: string + messageID: string + callID?: string + title: string + metadata: { [key: string]: unknown - }> - success: false + } + time: { + created: number + } } -export type ToolIds = Array +export type EventPermissionUpdated = { + type: "permission.updated" + properties: Permission +} -export type ToolListItem = { +export type EventPermissionReplied = { + type: "permission.replied" + properties: { + sessionID: string + permissionID: string + response: string + } +} + +export type EventFileEdited = { + type: "file.edited" + properties: { + file: string + } +} + +export type Todo = { + /** + * Brief description of the task + */ + content: string + /** + * Current status of the task: pending, in_progress, completed, cancelled + */ + status: string + /** + * Priority level of the task: high, medium, low + */ + priority: string + /** + * Unique identifier for the todo item + */ id: string - description: string - parameters: unknown } -export type ToolList = Array +export type EventTodoUpdated = { + type: "todo.updated" + properties: { + sessionID: string + todos: Array + } +} -export type Path = { - state: string - config: string - worktree: string - directory: string +export type EventCommandExecuted = { + type: "command.executed" + properties: { + name: string + sessionID: string + arguments: string + messageID: string + } } -export type FileDiff = { - file: string - before: string - after: string - additions: number - deletions: number +export type EventSessionIdle = { + type: "session.idle" + properties: { + sessionID: string + } } export type Session = { @@ -573,346 +494,670 @@ export type Session = { } } -export type NotFoundError = { - name: "NotFoundError" - data: { - message: string +export type EventSessionCreated = { + type: "session.created" + properties: { + info: Session } } -export type Todo = { - /** - * Brief description of the task - */ - content: string - /** - * Current status of the task: pending, in_progress, completed, cancelled - */ - status: string - /** - * Priority level of the task: high, medium, low - */ - priority: string - /** - * Unique identifier for the todo item - */ - id: string +export type EventSessionUpdated = { + type: "session.updated" + properties: { + info: Session + } } -export type UserMessage = { - id: string - sessionID: string - role: "user" - time: { - created: number +export type EventSessionDeleted = { + type: "session.deleted" + properties: { + info: Session } - summary?: { - title?: string - body?: string - diffs: Array +} + +export type EventSessionDiff = { + type: "session.diff" + properties: { + sessionID: string + diff: Array } } -export type ProviderAuthError = { - name: "ProviderAuthError" - data: { - providerID: string - message: string +export type EventSessionError = { + type: "session.error" + properties: { + sessionID?: string + error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError } } -export type UnknownError = { - name: "UnknownError" - data: { +export type EventTuiPromptAppend = { + type: "tui.prompt.append" + properties: { + text: string + } +} + +export type EventTuiCommandExecute = { + type: "tui.command.execute" + properties: { + command: + | ( + | "session.list" + | "session.new" + | "session.share" + | "session.interrupt" + | "session.compact" + | "session.page.up" + | "session.page.down" + | "session.half.page.up" + | "session.half.page.down" + | "session.first" + | "session.last" + | "prompt.clear" + | "prompt.submit" + | "agent.cycle" + ) + | string + } +} + +export type EventTuiToastShow = { + type: "tui.toast.show" + properties: { + title?: string message: string + variant: "info" | "success" | "warning" | "error" + /** + * Duration in milliseconds + */ + duration?: number } } -export type MessageOutputLengthError = { - name: "MessageOutputLengthError" - data: { +export type EventServerConnected = { + type: "server.connected" + properties: { [key: string]: unknown } } -export type MessageAbortedError = { - name: "MessageAbortedError" - data: { - message: string +export type EventFileWatcherUpdated = { + type: "file.watcher.updated" + properties: { + file: string + event: "add" | "change" | "unlink" } } -export type ApiError = { - name: "APIError" - data: { - message: string - statusCode?: number - isRetryable: boolean - responseHeaders?: { - [key: string]: string - } - responseBody?: string - } -} +export type Event = + | EventInstallationUpdated + | EventLspClientDiagnostics + | EventLspUpdated + | EventMessageUpdated + | EventMessageRemoved + | EventMessagePartUpdated + | EventMessagePartRemoved + | EventSessionCompacted + | EventPermissionUpdated + | EventPermissionReplied + | EventFileEdited + | EventTodoUpdated + | EventCommandExecuted + | EventSessionIdle + | EventSessionCreated + | EventSessionUpdated + | EventSessionDeleted + | EventSessionDiff + | EventSessionError + | EventTuiPromptAppend + | EventTuiCommandExecute + | EventTuiToastShow + | EventServerConnected + | EventFileWatcherUpdated -export type AssistantMessage = { +export type Project = { id: string - sessionID: string - role: "assistant" + worktree: string + vcs?: "git" time: { created: number - completed?: number - } - error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError - parentID: string - modelID: string - providerID: string - mode: string - path: { - cwd: string - root: string - } - summary?: boolean - cost: number - tokens: { - input: number - output: number - reasoning: number - cache: { - read: number - write: number - } + initialized?: number } } -export type Message = UserMessage | AssistantMessage - -export type TextPart = { - id: string - sessionID: string - messageID: string - type: "text" - text: string - synthetic?: boolean - time?: { - start: number - end?: number - } - metadata?: { - [key: string]: unknown - } +/** + * Custom keybind configurations + */ +export type KeybindsConfig = { + /** + * Leader key for keybind combinations + */ + leader?: string + /** + * Exit the application + */ + app_exit?: string + /** + * Open external editor + */ + editor_open?: string + /** + * List available themes + */ + theme_list?: string + /** + * Toggle sidebar + */ + sidebar_toggle?: string + /** + * View status + */ + status_view?: string + /** + * Export session to editor + */ + session_export?: string + /** + * Create a new session + */ + session_new?: string + /** + * List all sessions + */ + session_list?: string + /** + * Show session timeline + */ + session_timeline?: string + /** + * Share current session + */ + session_share?: string + /** + * Unshare current session + */ + session_unshare?: string + /** + * Interrupt current session + */ + session_interrupt?: string + /** + * Compact the session + */ + session_compact?: string + /** + * Scroll messages up by one page + */ + messages_page_up?: string + /** + * Scroll messages down by one page + */ + messages_page_down?: string + /** + * Scroll messages up by half page + */ + messages_half_page_up?: string + /** + * Scroll messages down by half page + */ + messages_half_page_down?: string + /** + * Navigate to first message + */ + messages_first?: string + /** + * Navigate to last message + */ + messages_last?: string + /** + * Copy message + */ + messages_copy?: string + /** + * Undo message + */ + messages_undo?: string + /** + * Redo message + */ + messages_redo?: string + /** + * Toggle code block concealment in messages + */ + messages_toggle_conceal?: string + /** + * List available models + */ + model_list?: string + /** + * Next recently used model + */ + model_cycle_recent?: string + /** + * Previous recently used model + */ + model_cycle_recent_reverse?: string + /** + * List available commands + */ + command_list?: string + /** + * List agents + */ + agent_list?: string + /** + * Next agent + */ + agent_cycle?: string + /** + * Previous agent + */ + agent_cycle_reverse?: string + /** + * Clear input field + */ + input_clear?: string + /** + * Forward delete + */ + input_forward_delete?: string + /** + * Paste from clipboard + */ + input_paste?: string + /** + * Submit input + */ + input_submit?: string + /** + * Insert newline in input + */ + input_newline?: string + /** + * Previous history item + */ + history_previous?: string + /** + * Next history item + */ + history_next?: string + /** + * Next child session + */ + session_child_cycle?: string + /** + * Previous child session + */ + session_child_cycle_reverse?: string } -export type ReasoningPart = { - id: string - sessionID: string - messageID: string - type: "reasoning" - text: string - metadata?: { - [key: string]: unknown +export type AgentConfig = { + model?: string + temperature?: number + top_p?: number + prompt?: string + tools?: { + [key: string]: boolean } - time: { - start: number - end?: number + disable?: boolean + /** + * Description of when to use the agent + */ + description?: string + mode?: "subagent" | "primary" | "all" + /** + * Hex color code for the agent (e.g., #FF5733) + */ + color?: string + permission?: { + edit?: "ask" | "allow" | "deny" + bash?: + | ("ask" | "allow" | "deny") + | { + [key: string]: "ask" | "allow" | "deny" + } + webfetch?: "ask" | "allow" | "deny" + doom_loop?: "ask" | "allow" | "deny" + external_directory?: "ask" | "allow" | "deny" } + [key: string]: + | unknown + | string + | number + | { + [key: string]: boolean + } + | boolean + | ("subagent" | "primary" | "all") + | { + edit?: "ask" | "allow" | "deny" + bash?: + | ("ask" | "allow" | "deny") + | { + [key: string]: "ask" | "allow" | "deny" + } + webfetch?: "ask" | "allow" | "deny" + doom_loop?: "ask" | "allow" | "deny" + external_directory?: "ask" | "allow" | "deny" + } + | undefined } -export type FilePartSourceText = { - value: string - start: number - end: number -} - -export type FileSource = { - text: FilePartSourceText - type: "file" - path: string -} - -export type Range = { - start: { - line: number - character: number - } - end: { - line: number - character: number +export type McpLocalConfig = { + /** + * Type of MCP server connection + */ + type: "local" + /** + * Command and arguments to run the MCP server + */ + command: Array + /** + * Environment variables to set when running the MCP server + */ + environment?: { + [key: string]: string } + /** + * Enable or disable the MCP server on startup + */ + enabled?: boolean + /** + * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified. + */ + timeout?: number } -export type SymbolSource = { - text: FilePartSourceText - type: "symbol" - path: string - range: Range - name: string - kind: number -} - -export type FilePartSource = FileSource | SymbolSource - -export type FilePart = { - id: string - sessionID: string - messageID: string - type: "file" - mime: string - filename?: string +export type McpRemoteConfig = { + /** + * Type of MCP server connection + */ + type: "remote" + /** + * URL of the remote MCP server + */ url: string - source?: FilePartSource -} - -export type ToolStatePending = { - status: "pending" - input: { - [key: string]: unknown + /** + * Enable or disable the MCP server on startup + */ + enabled?: boolean + /** + * Headers to send with the request + */ + headers?: { + [key: string]: string } - raw: string + /** + * Timeout in ms for fetching tools from the MCP server. Defaults to 5000 (5 seconds) if not specified. + */ + timeout?: number } -export type ToolStateRunning = { - status: "running" - input: { - [key: string]: unknown +/** + * @deprecated Always uses stretch layout. + */ +export type LayoutConfig = "auto" | "stretch" + +export type Config = { + /** + * JSON schema reference for configuration validation + */ + $schema?: string + /** + * Theme name to use for the interface + */ + theme?: string + keybinds?: KeybindsConfig + /** + * TUI specific settings + */ + tui?: { + /** + * TUI scroll speed + */ + scroll_speed?: number + /** + * Scroll acceleration settings + */ + scroll_acceleration?: { + /** + * Enable scroll acceleration + */ + enabled: boolean + } } - title?: string - metadata?: { - [key: string]: unknown + /** + * Command configuration, see https://opencode.ai/docs/commands + */ + command?: { + [key: string]: { + template: string + description?: string + agent?: string + model?: string + subtask?: boolean + } } - time: { - start: number + watcher?: { + ignore?: Array } -} - -export type ToolStateCompleted = { - status: "completed" - input: { - [key: string]: unknown + plugin?: Array + snapshot?: boolean + /** + * Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing + */ + share?: "manual" | "auto" | "disabled" + /** + * @deprecated Use 'share' field instead. Share newly created sessions automatically + */ + autoshare?: boolean + /** + * Automatically update to the latest version + */ + autoupdate?: boolean + /** + * Disable providers that are loaded automatically + */ + disabled_providers?: Array + /** + * Model to use in the format of provider/model, eg anthropic/claude-2 + */ + model?: string + /** + * Small model to use for tasks like title generation in the format of provider/model + */ + small_model?: string + /** + * Custom username to display in conversations instead of system username + */ + username?: string + /** + * @deprecated Use `agent` field instead. + */ + mode?: { + build?: AgentConfig + plan?: AgentConfig + [key: string]: AgentConfig | undefined + } + /** + * Agent configuration, see https://opencode.ai/docs/agent + */ + agent?: { + plan?: AgentConfig + build?: AgentConfig + general?: AgentConfig + [key: string]: AgentConfig | undefined } - output: string - title: string - metadata: { - [key: string]: unknown + /** + * Custom provider configurations and model overrides + */ + provider?: { + [key: string]: { + api?: string + name?: string + env?: Array + id?: string + npm?: string + models?: { + [key: string]: { + id?: string + name?: string + release_date?: string + attachment?: boolean + reasoning?: boolean + temperature?: boolean + tool_call?: boolean + cost?: { + input: number + output: number + cache_read?: number + cache_write?: number + } + limit?: { + context: number + output: number + } + modalities?: { + input: Array<"text" | "audio" | "image" | "video" | "pdf"> + output: Array<"text" | "audio" | "image" | "video" | "pdf"> + } + experimental?: boolean + status?: "alpha" | "beta" | "deprecated" + options?: { + [key: string]: unknown + } + headers?: { + [key: string]: string + } + provider?: { + npm: string + } + } + } + options?: { + apiKey?: string + baseURL?: string + /** + * GitHub Enterprise URL for copilot authentication + */ + enterpriseUrl?: string + /** + * Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout. + */ + timeout?: number | false + [key: string]: unknown | string | (number | false) | undefined + } + } } - time: { - start: number - end: number - compacted?: number + /** + * MCP (Model Context Protocol) server configurations + */ + mcp?: { + [key: string]: McpLocalConfig | McpRemoteConfig } - attachments?: Array -} - -export type ToolStateError = { - status: "error" - input: { - [key: string]: unknown + formatter?: { + [key: string]: { + disabled?: boolean + command?: Array + environment?: { + [key: string]: string + } + extensions?: Array + } } - error: string - metadata?: { - [key: string]: unknown + lsp?: { + [key: string]: + | { + disabled: true + } + | { + command: Array + extensions?: Array + disabled?: boolean + env?: { + [key: string]: string + } + initialization?: { + [key: string]: unknown + } + } } - time: { - start: number - end: number + /** + * Additional instruction files or patterns to include + */ + instructions?: Array + layout?: LayoutConfig + permission?: { + edit?: "ask" | "allow" | "deny" + bash?: + | ("ask" | "allow" | "deny") + | { + [key: string]: "ask" | "allow" | "deny" + } + webfetch?: "ask" | "allow" | "deny" + doom_loop?: "ask" | "allow" | "deny" + external_directory?: "ask" | "allow" | "deny" } -} - -export type ToolState = ToolStatePending | ToolStateRunning | ToolStateCompleted | ToolStateError - -export type ToolPart = { - id: string - sessionID: string - messageID: string - type: "tool" - callID: string - tool: string - state: ToolState - metadata?: { - [key: string]: unknown + tools?: { + [key: string]: boolean } -} - -export type StepStartPart = { - id: string - sessionID: string - messageID: string - type: "step-start" - snapshot?: string -} - -export type StepFinishPart = { - id: string - sessionID: string - messageID: string - type: "step-finish" - reason: string - snapshot?: string - cost: number - tokens: { - input: number - output: number - reasoning: number - cache: { - read: number - write: number + experimental?: { + hook?: { + file_edited?: { + [key: string]: Array<{ + command: Array + environment?: { + [key: string]: string + } + }> + } + session_completed?: Array<{ + command: Array + environment?: { + [key: string]: string + } + }> } + /** + * Number of retries for chat completions on failure + */ + chatMaxRetries?: number + disable_paste_summary?: boolean } } -export type SnapshotPart = { - id: string - sessionID: string - messageID: string - type: "snapshot" - snapshot: string +export type BadRequestError = { + data: unknown | null + errors: Array<{ + [key: string]: unknown + }> + success: false } -export type PatchPart = { - id: string - sessionID: string - messageID: string - type: "patch" - hash: string - files: Array -} +export type ToolIds = Array -export type AgentPart = { +export type ToolListItem = { id: string - sessionID: string - messageID: string - type: "agent" - name: string - source?: { - value: string - start: number - end: number - } + description: string + parameters: unknown } -export type RetryPart = { - id: string - sessionID: string - messageID: string - type: "retry" - attempt: number - error: ApiError - time: { - created: number - } -} +export type ToolList = Array -export type Part = - | TextPart - | ReasoningPart - | FilePart - | ToolPart - | StepStartPart - | StepFinishPart - | SnapshotPart - | PatchPart - | AgentPart - | RetryPart +export type Path = { + state: string + config: string + worktree: string + directory: string +} + +export type NotFoundError = { + name: "NotFoundError" + data: { + message: string + } +} export type TextPartInput = { id?: string @@ -1107,50 +1352,6 @@ export type FormatterStatus = { enabled: boolean } -export type EventTuiPromptAppend = { - type: "tui.prompt.append" - properties: { - text: string - } -} - -export type EventTuiCommandExecute = { - type: "tui.command.execute" - properties: { - command: - | ( - | "session.list" - | "session.new" - | "session.share" - | "session.interrupt" - | "session.compact" - | "session.page.up" - | "session.page.down" - | "session.half.page.up" - | "session.half.page.down" - | "session.first" - | "session.last" - | "prompt.clear" - | "prompt.submit" - | "agent.cycle" - ) - | string - } -} - -export type EventTuiToastShow = { - type: "tui.toast.show" - properties: { - title?: string - message: string - variant: "info" | "success" | "warning" | "error" - /** - * Duration in milliseconds - */ - duration?: number - } -} - export type OAuth = { type: "oauth" refresh: string @@ -1172,206 +1373,21 @@ export type WellKnownAuth = { export type Auth = OAuth | ApiAuth | WellKnownAuth -export type EventInstallationUpdated = { - type: "installation.updated" - properties: { - version: string - } -} - -export type EventLspClientDiagnostics = { - type: "lsp.client.diagnostics" - properties: { - serverID: string - path: string - } -} - -export type EventLspUpdated = { - type: "lsp.updated" - properties: { - [key: string]: unknown - } -} - -export type EventMessageUpdated = { - type: "message.updated" - properties: { - info: Message - } -} - -export type EventMessageRemoved = { - type: "message.removed" - properties: { - sessionID: string - messageID: string - } -} - -export type EventMessagePartUpdated = { - type: "message.part.updated" - properties: { - part: Part - delta?: string - } -} - -export type EventMessagePartRemoved = { - type: "message.part.removed" - properties: { - sessionID: string - messageID: string - partID: string - } -} - -export type EventSessionCompacted = { - type: "session.compacted" - properties: { - sessionID: string - } -} - -export type Permission = { - id: string - type: string - pattern?: string | Array - sessionID: string - messageID: string - callID?: string - title: string - metadata: { - [key: string]: unknown - } - time: { - created: number - } -} - -export type EventPermissionUpdated = { - type: "permission.updated" - properties: Permission -} - -export type EventPermissionReplied = { - type: "permission.replied" - properties: { - sessionID: string - permissionID: string - response: string - } -} - -export type EventFileEdited = { - type: "file.edited" - properties: { - file: string - } -} - -export type EventTodoUpdated = { - type: "todo.updated" - properties: { - sessionID: string - todos: Array - } -} - -export type EventCommandExecuted = { - type: "command.executed" - properties: { - name: string - sessionID: string - arguments: string - messageID: string - } -} - -export type EventSessionIdle = { - type: "session.idle" - properties: { - sessionID: string - } -} - -export type EventSessionCreated = { - type: "session.created" - properties: { - info: Session - } -} - -export type EventSessionUpdated = { - type: "session.updated" - properties: { - info: Session - } -} - -export type EventSessionDeleted = { - type: "session.deleted" - properties: { - info: Session - } -} - -export type EventSessionDiff = { - type: "session.diff" - properties: { - sessionID: string - diff: Array - } -} - -export type EventSessionError = { - type: "session.error" - properties: { - sessionID?: string - error?: ProviderAuthError | UnknownError | MessageOutputLengthError | MessageAbortedError | ApiError - } -} - -export type EventServerConnected = { - type: "server.connected" - properties: { - [key: string]: unknown - } +export type GlobalEventButtData = { + body?: never + path?: never + query?: never + url: "/global/event" } -export type EventFileWatcherUpdated = { - type: "file.watcher.updated" - properties: { - file: string - event: "add" | "change" | "unlink" - } +export type GlobalEventButtResponses = { + /** + * Event stream + */ + 200: Event } -export type Event = - | EventInstallationUpdated - | EventLspClientDiagnostics - | EventLspUpdated - | EventMessageUpdated - | EventMessageRemoved - | EventMessagePartUpdated - | EventMessagePartRemoved - | EventSessionCompacted - | EventPermissionUpdated - | EventPermissionReplied - | EventFileEdited - | EventTodoUpdated - | EventCommandExecuted - | EventSessionIdle - | EventSessionCreated - | EventSessionUpdated - | EventSessionDeleted - | EventSessionDiff - | EventSessionError - | EventTuiPromptAppend - | EventTuiCommandExecute - | EventTuiToastShow - | EventServerConnected - | EventFileWatcherUpdated +export type GlobalEventButtResponse = GlobalEventButtResponses[keyof GlobalEventButtResponses] export type ProjectListData = { body?: never -- cgit v1.2.3