merge: resolve conflicts from upstream dev

Merge upstream changes while preserving structured output feature:
- Keep tools deprecation notice from upstream
- Keep bypassAgentCheck parameter from upstream
- Keep variant field on user messages from upstream
- Preserve outputFormat and StructuredOutput tool injection

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kyle Mistele 2026-01-13 00:01:34 -08:00
commit 4c7c65a054
598 changed files with 90910 additions and 11698 deletions

View file

@ -1,8 +1,9 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "1.0.193",
"version": "1.1.15",
"type": "module",
"license": "MIT",
"scripts": {
"typecheck": "tsgo --noEmit",
"build": "./script/build.ts"

View file

@ -1174,6 +1174,10 @@ export type Config = {
*/
theme?: string
keybinds?: KeybindsConfig
/**
* Log level
*/
logLevel?: "DEBUG" | "INFO" | "WARN" | "ERROR"
/**
* TUI specific settings
*/

View file

@ -28,7 +28,10 @@ export async function createOpencodeServer(options?: ServerOptions) {
options ?? {},
)
const proc = spawn(`opencode`, [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], {
const args = [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`]
if (options.config?.logLevel) args.push(`--log-level=${options.config.logLevel}`)
const proc = spawn(`opencode`, args, {
signal: options.signal,
env: {
...process.env,

View file

@ -19,9 +19,11 @@ export function createOpencodeClient(config?: Config & { directory?: string }) {
}
if (config?.directory) {
const isNonASCII = /[^\x00-\x7F]/.test(config.directory)
const encodedDirectory = isNonASCII ? encodeURIComponent(config.directory) : config.directory
config.headers = {
...config.headers,
"x-opencode-directory": config.directory,
"x-opencode-directory": encodedDirectory,
}
}

View file

@ -19,9 +19,12 @@ import type {
EventSubscribeResponses,
EventTuiCommandExecute,
EventTuiPromptAppend,
EventTuiSessionSelect,
EventTuiToastShow,
ExperimentalResourceListResponses,
FileListResponses,
FilePartInput,
FilePartSource,
FileReadResponses,
FileStatusResponses,
FindFilesResponses,
@ -55,8 +58,12 @@ import type {
PartUpdateErrors,
PartUpdateResponses,
PathGetResponses,
PermissionListResponses,
PermissionReplyErrors,
PermissionReplyResponses,
PermissionRespondErrors,
PermissionRespondResponses,
PermissionRuleset,
ProjectCurrentResponses,
ProjectListResponses,
ProjectUpdateErrors,
@ -78,6 +85,12 @@ import type {
PtyRemoveResponses,
PtyUpdateErrors,
PtyUpdateResponses,
QuestionAnswer,
QuestionListResponses,
QuestionRejectErrors,
QuestionRejectResponses,
QuestionReplyErrors,
QuestionReplyResponses,
SessionAbortErrors,
SessionAbortResponses,
SessionChildrenErrors,
@ -141,9 +154,15 @@ import type {
TuiOpenThemesResponses,
TuiPublishErrors,
TuiPublishResponses,
TuiSelectSessionErrors,
TuiSelectSessionResponses,
TuiShowToastResponses,
TuiSubmitPromptResponses,
VcsGetResponses,
WorktreeCreateErrors,
WorktreeCreateInput,
WorktreeCreateResponses,
WorktreeListResponses,
} from "./types.gen.js"
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = Options2<
@ -677,6 +696,62 @@ export class Path extends HeyApiClient {
}
}
export class Worktree extends HeyApiClient {
/**
* List worktrees
*
* List all sandbox worktrees for the current project.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
return (options?.client ?? this.client).get<WorktreeListResponses, unknown, ThrowOnError>({
url: "/experimental/worktree",
...options,
...params,
})
}
/**
* Create worktree
*
* Create a new git worktree for the current project.
*/
public create<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
worktreeCreateInput?: WorktreeCreateInput
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ key: "worktreeCreateInput", map: "body" },
],
},
],
)
return (options?.client ?? this.client).post<WorktreeCreateResponses, WorktreeCreateErrors, ThrowOnError>({
url: "/experimental/worktree",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
}
export class Vcs extends HeyApiClient {
/**
* Get VCS info
@ -707,10 +782,25 @@ export class Session extends HeyApiClient {
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
start?: number
search?: string
limit?: number
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "query", key: "start" },
{ in: "query", key: "search" },
{ in: "query", key: "limit" },
],
},
],
)
return (options?.client ?? this.client).get<SessionListResponses, unknown, ThrowOnError>({
url: "/session",
...options,
@ -728,6 +818,7 @@ export class Session extends HeyApiClient {
directory?: string
parentID?: string
title?: string
permission?: PermissionRuleset
},
options?: Options<never, ThrowOnError>,
) {
@ -739,6 +830,7 @@ export class Session extends HeyApiClient {
{ in: "query", key: "directory" },
{ in: "body", key: "parentID" },
{ in: "body", key: "title" },
{ in: "body", key: "permission" },
],
},
],
@ -1229,6 +1321,7 @@ export class Session extends HeyApiClient {
}
outputFormat?: OutputFormat
system?: string
variant?: string
parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
},
options?: Options<never, ThrowOnError>,
@ -1247,6 +1340,7 @@ export class Session extends HeyApiClient {
{ in: "body", key: "tools" },
{ in: "body", key: "outputFormat" },
{ in: "body", key: "system" },
{ in: "body", key: "variant" },
{ in: "body", key: "parts" },
],
},
@ -1317,6 +1411,7 @@ export class Session extends HeyApiClient {
}
outputFormat?: OutputFormat
system?: string
variant?: string
parts?: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
},
options?: Options<never, ThrowOnError>,
@ -1335,6 +1430,7 @@ export class Session extends HeyApiClient {
{ in: "body", key: "tools" },
{ in: "body", key: "outputFormat" },
{ in: "body", key: "system" },
{ in: "body", key: "variant" },
{ in: "body", key: "parts" },
],
},
@ -1366,6 +1462,15 @@ export class Session extends HeyApiClient {
model?: string
arguments?: string
command?: string
variant?: string
parts?: Array<{
id?: string
type: "file"
mime: string
filename?: string
url: string
source?: FilePartSource
}>
},
options?: Options<never, ThrowOnError>,
) {
@ -1381,6 +1486,8 @@ export class Session extends HeyApiClient {
{ in: "body", key: "model" },
{ in: "body", key: "arguments" },
{ in: "body", key: "command" },
{ in: "body", key: "variant" },
{ in: "body", key: "parts" },
],
},
],
@ -1589,6 +1696,8 @@ export class Permission extends HeyApiClient {
* Respond to permission
*
* Approve or deny a permission request from the AI assistant.
*
* @deprecated
*/
public respond<ThrowOnError extends boolean = false>(
parameters: {
@ -1623,6 +1732,152 @@ export class Permission extends HeyApiClient {
},
})
}
/**
* Respond to permission request
*
* Approve or deny a permission request from the AI assistant.
*/
public reply<ThrowOnError extends boolean = false>(
parameters: {
requestID: string
directory?: string
reply?: "once" | "always" | "reject"
message?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "requestID" },
{ in: "query", key: "directory" },
{ in: "body", key: "reply" },
{ in: "body", key: "message" },
],
},
],
)
return (options?.client ?? this.client).post<PermissionReplyResponses, PermissionReplyErrors, ThrowOnError>({
url: "/permission/{requestID}/reply",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
/**
* List pending permissions
*
* Get all pending permission requests across all sessions.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
return (options?.client ?? this.client).get<PermissionListResponses, unknown, ThrowOnError>({
url: "/permission",
...options,
...params,
})
}
}
export class Question extends HeyApiClient {
/**
* List pending questions
*
* Get all pending question requests across all sessions.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
return (options?.client ?? this.client).get<QuestionListResponses, unknown, ThrowOnError>({
url: "/question",
...options,
...params,
})
}
/**
* Reply to question request
*
* Provide answers to a question request from the AI assistant.
*/
public reply<ThrowOnError extends boolean = false>(
parameters: {
requestID: string
directory?: string
answers?: Array<QuestionAnswer>
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "requestID" },
{ in: "query", key: "directory" },
{ in: "body", key: "answers" },
],
},
],
)
return (options?.client ?? this.client).post<QuestionReplyResponses, QuestionReplyErrors, ThrowOnError>({
url: "/question/{requestID}/reply",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
/**
* Reject question request
*
* Reject a question request from the AI assistant.
*/
public reject<ThrowOnError extends boolean = false>(
parameters: {
requestID: string
directory?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "path", key: "requestID" },
{ in: "query", key: "directory" },
],
},
],
)
return (options?.client ?? this.client).post<QuestionRejectResponses, QuestionRejectErrors, ThrowOnError>({
url: "/question/{requestID}/reject",
...options,
...params,
})
}
}
export class Command extends HeyApiClient {
@ -1808,13 +2063,15 @@ export class Find extends HeyApiClient {
/**
* Find files
*
* Search for files by name or pattern in the project directory.
* Search for files or directories by name or pattern in the project directory.
*/
public files<ThrowOnError extends boolean = false>(
parameters: {
directory?: string
query: string
dirs?: "true" | "false"
type?: "file" | "directory"
limit?: number
},
options?: Options<never, ThrowOnError>,
) {
@ -1826,6 +2083,8 @@ export class Find extends HeyApiClient {
{ in: "query", key: "directory" },
{ in: "query", key: "query" },
{ in: "query", key: "dirs" },
{ in: "query", key: "type" },
{ in: "query", key: "limit" },
],
},
],
@ -2297,6 +2556,31 @@ export class Mcp extends HeyApiClient {
auth = new Auth({ client: this.client })
}
export class Resource extends HeyApiClient {
/**
* Get MCP resources
*
* Get all available MCP resources from connected servers. Optionally filter by name.
*/
public list<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams([parameters], [{ args: [{ in: "query", key: "directory" }] }])
return (options?.client ?? this.client).get<ExperimentalResourceListResponses, unknown, ThrowOnError>({
url: "/experimental/resource",
...options,
...params,
})
}
}
export class Experimental extends HeyApiClient {
resource = new Resource({ client: this.client })
}
export class Lsp extends HeyApiClient {
/**
* Get LSP status
@ -2619,7 +2903,7 @@ export class Tui extends HeyApiClient {
public publish<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow
body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow | EventTuiSessionSelect
},
options?: Options<never, ThrowOnError>,
) {
@ -2636,6 +2920,41 @@ export class Tui extends HeyApiClient {
})
}
/**
* Select session
*
* Navigate the TUI to display the specified session.
*/
public selectSession<ThrowOnError extends boolean = false>(
parameters?: {
directory?: string
sessionID?: string
},
options?: Options<never, ThrowOnError>,
) {
const params = buildClientParams(
[parameters],
[
{
args: [
{ in: "query", key: "directory" },
{ in: "body", key: "sessionID" },
],
},
],
)
return (options?.client ?? this.client).post<TuiSelectSessionResponses, TuiSelectSessionErrors, ThrowOnError>({
url: "/tui/select-session",
...options,
...params,
headers: {
"Content-Type": "application/json",
...options?.headers,
...params.headers,
},
})
}
control = new Control({ client: this.client })
}
@ -2682,6 +3001,8 @@ export class OpencodeClient extends HeyApiClient {
path = new Path({ client: this.client })
worktree = new Worktree({ client: this.client })
vcs = new Vcs({ client: this.client })
session = new Session({ client: this.client })
@ -2690,6 +3011,8 @@ export class OpencodeClient extends HeyApiClient {
permission = new Permission({ client: this.client })
question = new Question({ client: this.client })
command = new Command({ client: this.client })
provider = new Provider({ client: this.client })
@ -2702,6 +3025,8 @@ export class OpencodeClient extends HeyApiClient {
mcp = new Mcp({ client: this.client })
experimental = new Experimental({ client: this.client })
lsp = new Lsp({ client: this.client })
formatter = new Formatter({ client: this.client })

View file

@ -32,6 +32,7 @@ export type Project = {
updated: number
initialized?: number
}
sandboxes: Array<string>
}
export type EventProjectUpdated = {
@ -107,6 +108,7 @@ export type UserMessage = {
tools?: {
[key: string]: boolean
}
variant?: string
}
export type ProviderAuthError = {
@ -282,7 +284,14 @@ export type SymbolSource = {
kind: number
}
export type FilePartSource = FileSource | SymbolSource
export type ResourceSource = {
text: FilePartSourceText
type: "resource"
clientName: string
uri: string
}
export type FilePartSource = FileSource | SymbolSource | ResourceSource
export type FilePart = {
id: string
@ -482,33 +491,135 @@ export type EventMessagePartRemoved = {
}
}
export type Permission = {
export type PermissionRequest = {
id: string
type: string
pattern?: string | Array<string>
sessionID: string
messageID: string
callID?: string
title: string
permission: string
patterns: Array<string>
metadata: {
[key: string]: unknown
}
time: {
created: number
always: Array<string>
tool?: {
messageID: string
callID: string
}
}
export type EventPermissionUpdated = {
type: "permission.updated"
properties: Permission
export type EventPermissionAsked = {
type: "permission.asked"
properties: PermissionRequest
}
export type EventPermissionReplied = {
type: "permission.replied"
properties: {
sessionID: string
permissionID: string
response: string
requestID: string
reply: "once" | "always" | "reject"
}
}
export type SessionStatus =
| {
type: "idle"
}
| {
type: "retry"
attempt: number
message: string
next: number
}
| {
type: "busy"
}
export type EventSessionStatus = {
type: "session.status"
properties: {
sessionID: string
status: SessionStatus
}
}
export type EventSessionIdle = {
type: "session.idle"
properties: {
sessionID: string
}
}
export type QuestionOption = {
/**
* Display text (1-5 words, concise)
*/
label: string
/**
* Explanation of choice
*/
description: string
}
export type QuestionInfo = {
/**
* Complete question
*/
question: string
/**
* Very short label (max 12 chars)
*/
header: string
/**
* Available choices
*/
options: Array<QuestionOption>
/**
* Allow selecting multiple choices
*/
multiple?: boolean
}
export type QuestionRequest = {
id: string
sessionID: string
/**
* Questions to ask
*/
questions: Array<QuestionInfo>
tool?: {
messageID: string
callID: string
}
}
export type EventQuestionAsked = {
type: "question.asked"
properties: QuestionRequest
}
export type QuestionAnswer = Array<string>
export type EventQuestionReplied = {
type: "question.replied"
properties: {
sessionID: string
requestID: string
answers: Array<QuestionAnswer>
}
}
export type EventQuestionRejected = {
type: "question.rejected"
properties: {
sessionID: string
requestID: string
}
}
export type EventSessionCompacted = {
type: "session.compacted"
properties: {
sessionID: string
}
}
@ -546,42 +657,6 @@ export type EventTodoUpdated = {
}
}
export type SessionStatus =
| {
type: "idle"
}
| {
type: "retry"
attempt: number
message: string
next: number
}
| {
type: "busy"
}
export type EventSessionStatus = {
type: "session.status"
properties: {
sessionID: string
status: SessionStatus
}
}
export type EventSessionIdle = {
type: "session.idle"
properties: {
sessionID: string
}
}
export type EventSessionCompacted = {
type: "session.compacted"
properties: {
sessionID: string
}
}
export type EventTuiPromptAppend = {
type: "tui.prompt.append"
properties: {
@ -624,6 +699,16 @@ export type EventTuiToastShow = {
}
}
export type EventTuiSessionSelect = {
type: "tui.session.select"
properties: {
/**
* Session ID to navigate to
*/
sessionID: string
}
}
export type EventMcpToolsChanged = {
type: "mcp.tools.changed"
properties: {
@ -641,6 +726,16 @@ export type EventCommandExecuted = {
}
}
export type PermissionAction = "allow" | "deny" | "ask"
export type PermissionRule = {
permission: string
pattern: string
action: PermissionAction
}
export type PermissionRuleset = Array<PermissionRule>
export type Session = {
id: string
projectID: string
@ -663,6 +758,7 @@ export type Session = {
compacting?: number
archived?: number
}
permission?: PermissionRuleset
revert?: {
messageID: string
partID?: string
@ -793,16 +889,20 @@ export type Event =
| EventMessageRemoved
| EventMessagePartUpdated
| EventMessagePartRemoved
| EventPermissionUpdated
| EventPermissionAsked
| EventPermissionReplied
| EventFileEdited
| EventTodoUpdated
| EventSessionStatus
| EventSessionIdle
| EventQuestionAsked
| EventQuestionReplied
| EventQuestionRejected
| EventSessionCompacted
| EventFileEdited
| EventTodoUpdated
| EventTuiPromptAppend
| EventTuiCommandExecute
| EventTuiToastShow
| EventTuiSessionSelect
| EventMcpToolsChanged
| EventCommandExecuted
| EventSessionCreated
@ -1007,6 +1107,10 @@ export type KeybindsConfig = {
* Previous agent
*/
agent_cycle_reverse?: string
/**
* Cycle model variants
*/
variant_cycle?: string
/**
* Clear input field
*/
@ -1189,11 +1293,72 @@ export type KeybindsConfig = {
tips_toggle?: string
}
/**
* Log level
*/
export type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR"
/**
* Server configuration for opencode serve and web commands
*/
export type ServerConfig = {
/**
* Port to listen on
*/
port?: number
/**
* Hostname to listen on
*/
hostname?: string
/**
* Enable mDNS service discovery
*/
mdns?: boolean
/**
* Additional domains to allow for CORS
*/
cors?: Array<string>
}
export type PermissionActionConfig = "ask" | "allow" | "deny"
export type PermissionObjectConfig = {
[key: string]: PermissionActionConfig
}
export type PermissionRuleConfig = PermissionActionConfig | PermissionObjectConfig
export type PermissionConfig =
| {
__originalKeys?: Array<string>
read?: PermissionRuleConfig
edit?: PermissionRuleConfig
glob?: PermissionRuleConfig
grep?: PermissionRuleConfig
list?: PermissionRuleConfig
bash?: PermissionRuleConfig
task?: PermissionRuleConfig
external_directory?: PermissionRuleConfig
todowrite?: PermissionActionConfig
todoread?: PermissionActionConfig
question?: PermissionActionConfig
webfetch?: PermissionActionConfig
websearch?: PermissionActionConfig
codesearch?: PermissionActionConfig
lsp?: PermissionRuleConfig
doom_loop?: PermissionActionConfig
[key: string]: PermissionRuleConfig | Array<string> | PermissionActionConfig | undefined
}
| PermissionActionConfig
export type AgentConfig = {
model?: string
temperature?: number
top_p?: number
prompt?: string
/**
* @deprecated Use 'permission' field instead
*/
tools?: {
[key: string]: boolean
}
@ -1203,6 +1368,13 @@ export type AgentConfig = {
*/
description?: string
mode?: "subagent" | "primary" | "all"
/**
* Hide this subagent from the @ autocomplete menu (default: false, only applies to mode: subagent)
*/
hidden?: boolean
options?: {
[key: string]: unknown
}
/**
* Hex color code for the agent (e.g., #FF5733)
*/
@ -1210,27 +1382,12 @@ export type AgentConfig = {
/**
* Maximum number of agentic iterations before forcing text-only response
*/
steps?: number
/**
* @deprecated Use 'steps' field instead.
*/
maxSteps?: number
permission?: {
edit?: "ask" | "allow" | "deny"
bash?:
| "ask"
| "allow"
| "deny"
| {
[key: string]: "ask" | "allow" | "deny"
}
skill?:
| "ask"
| "allow"
| "deny"
| {
[key: string]: "ask" | "allow" | "deny"
}
webfetch?: "ask" | "allow" | "deny"
doom_loop?: "ask" | "allow" | "deny"
external_directory?: "ask" | "allow" | "deny"
}
permission?: PermissionConfig
[key: string]:
| unknown
| string
@ -1242,28 +1399,12 @@ export type AgentConfig = {
| "subagent"
| "primary"
| "all"
| {
[key: string]: unknown
}
| string
| number
| {
edit?: "ask" | "allow" | "deny"
bash?:
| "ask"
| "allow"
| "deny"
| {
[key: string]: "ask" | "allow" | "deny"
}
skill?:
| "ask"
| "allow"
| "deny"
| {
[key: string]: "ask" | "allow" | "deny"
}
webfetch?: "ask" | "allow" | "deny"
doom_loop?: "ask" | "allow" | "deny"
external_directory?: "ask" | "allow" | "deny"
}
| PermissionConfig
| undefined
}
@ -1319,6 +1460,18 @@ export type ProviderConfig = {
provider?: {
npm: string
}
/**
* Variant-specific configuration
*/
variants?: {
[key: string]: {
/**
* Disable this variant for the model
*/
disabled?: boolean
[key: string]: unknown | boolean | undefined
}
}
}
}
whitelist?: Array<string>
@ -1426,6 +1579,7 @@ export type Config = {
*/
theme?: string
keybinds?: KeybindsConfig
logLevel?: LogLevel
/**
* TUI specific settings
*/
@ -1448,6 +1602,7 @@ export type Config = {
*/
diff_style?: "auto" | "stacked"
}
server?: ServerConfig
/**
* Command configuration, see https://opencode.ai/docs/commands
*/
@ -1532,7 +1687,12 @@ export type Config = {
* MCP (Model Context Protocol) server configurations
*/
mcp?: {
[key: string]: McpLocalConfig | McpRemoteConfig
[key: string]:
| McpLocalConfig
| McpRemoteConfig
| {
enabled: boolean
}
}
formatter?:
| false
@ -1570,26 +1730,7 @@ export type Config = {
*/
instructions?: Array<string>
layout?: LayoutConfig
permission?: {
edit?: "ask" | "allow" | "deny"
bash?:
| "ask"
| "allow"
| "deny"
| {
[key: string]: "ask" | "allow" | "deny"
}
skill?:
| "ask"
| "allow"
| "deny"
| {
[key: string]: "ask" | "allow" | "deny"
}
webfetch?: "ask" | "allow" | "deny"
doom_loop?: "ask" | "allow" | "deny"
external_directory?: "ask" | "allow" | "deny"
}
permission?: PermissionConfig
tools?: {
[key: string]: boolean
}
@ -1599,6 +1740,16 @@ export type Config = {
*/
url?: string
}
compaction?: {
/**
* Enable automatic compaction when context is full (default: true)
*/
auto?: boolean
/**
* Enable pruning of old tool outputs (default: true)
*/
prune?: boolean
}
experimental?: {
hook?: {
file_edited?: {
@ -1637,6 +1788,10 @@ export type Config = {
* Continue the agent loop when a tool call is denied
*/
continue_loop_on_deny?: boolean
/**
* Timeout in milliseconds for model context protocol (MCP) requests
*/
mcp_timeout?: number
}
}
@ -1658,6 +1813,17 @@ export type Path = {
directory: string
}
export type Worktree = {
name: string
branch: string
directory: string
}
export type WorktreeCreateInput = {
name?: string
startCommand?: string
}
export type VcsInfo = {
branch: string
}
@ -1711,8 +1877,10 @@ export type Command = {
description?: string
agent?: string
model?: string
mcp?: boolean
template: string
subtask?: boolean
hints: Array<string>
}
export type Model = {
@ -1778,6 +1946,11 @@ export type Model = {
[key: string]: string
}
release_date: string
variants?: {
[key: string]: {
[key: string]: unknown
}
}
}
export type Provider = {
@ -1857,34 +2030,19 @@ export type Agent = {
mode: "subagent" | "primary" | "all"
native?: boolean
hidden?: boolean
default?: boolean
topP?: number
temperature?: number
color?: string
permission: {
edit: "ask" | "allow" | "deny"
bash: {
[key: string]: "ask" | "allow" | "deny"
}
skill: {
[key: string]: "ask" | "allow" | "deny"
}
webfetch?: "ask" | "allow" | "deny"
doom_loop?: "ask" | "allow" | "deny"
external_directory?: "ask" | "allow" | "deny"
}
permission: PermissionRuleset
model?: {
modelID: string
providerID: string
}
prompt?: string
tools: {
[key: string]: boolean
}
options: {
[key: string]: unknown
}
maxSteps?: number
steps?: number
}
export type McpStatusConnected = {
@ -1916,6 +2074,14 @@ export type McpStatus =
| McpStatusNeedsAuth
| McpStatusNeedsClientRegistration
export type McpResource = {
name: string
uri: string
description?: string
mimeType?: string
client: string
}
export type LspStatus = {
id: string
name: string
@ -1934,6 +2100,7 @@ export type OAuth = {
refresh: string
access: string
expires: number
accountId?: string
enterpriseUrl?: string
}
@ -2388,6 +2555,51 @@ export type PathGetResponses = {
export type PathGetResponse = PathGetResponses[keyof PathGetResponses]
export type WorktreeListData = {
body?: never
path?: never
query?: {
directory?: string
}
url: "/experimental/worktree"
}
export type WorktreeListResponses = {
/**
* List of worktree directories
*/
200: Array<string>
}
export type WorktreeListResponse = WorktreeListResponses[keyof WorktreeListResponses]
export type WorktreeCreateData = {
body?: WorktreeCreateInput
path?: never
query?: {
directory?: string
}
url: "/experimental/worktree"
}
export type WorktreeCreateErrors = {
/**
* Bad request
*/
400: BadRequestError
}
export type WorktreeCreateError = WorktreeCreateErrors[keyof WorktreeCreateErrors]
export type WorktreeCreateResponses = {
/**
* Worktree created
*/
200: Worktree
}
export type WorktreeCreateResponse = WorktreeCreateResponses[keyof WorktreeCreateResponses]
export type VcsGetData = {
body?: never
path?: never
@ -2411,6 +2623,18 @@ export type SessionListData = {
path?: never
query?: {
directory?: string
/**
* Filter sessions updated on or after this timestamp (milliseconds since epoch)
*/
start?: number
/**
* Filter sessions by title (case-insensitive)
*/
search?: string
/**
* Maximum number of sessions to return
*/
limit?: number
}
url: "/session"
}
@ -2428,6 +2652,7 @@ export type SessionCreateData = {
body?: {
parentID?: string
title?: string
permission?: PermissionRuleset
}
path?: never
query?: {
@ -2943,11 +3168,15 @@ export type SessionPromptData = {
}
agent?: string
noReply?: boolean
/**
* @deprecated tools and permissions have been merged, you can set permissions on the session itself now
*/
tools?: {
[key: string]: boolean
}
outputFormat?: OutputFormat
system?: string
variant?: string
parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
}
path: {
@ -3127,11 +3356,15 @@ export type SessionPromptAsyncData = {
}
agent?: string
noReply?: boolean
/**
* @deprecated tools and permissions have been merged, you can set permissions on the session itself now
*/
tools?: {
[key: string]: boolean
}
outputFormat?: OutputFormat
system?: string
variant?: string
parts: Array<TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput>
}
path: {
@ -3175,6 +3408,15 @@ export type SessionCommandData = {
model?: string
arguments: string
command: string
variant?: string
parts?: Array<{
id?: string
type: "file"
mime: string
filename?: string
url: string
source?: FilePartSource
}>
}
path: {
/**
@ -3361,6 +3603,149 @@ export type PermissionRespondResponses = {
export type PermissionRespondResponse = PermissionRespondResponses[keyof PermissionRespondResponses]
export type PermissionReplyData = {
body?: {
reply: "once" | "always" | "reject"
message?: string
}
path: {
requestID: string
}
query?: {
directory?: string
}
url: "/permission/{requestID}/reply"
}
export type PermissionReplyErrors = {
/**
* Bad request
*/
400: BadRequestError
/**
* Not found
*/
404: NotFoundError
}
export type PermissionReplyError = PermissionReplyErrors[keyof PermissionReplyErrors]
export type PermissionReplyResponses = {
/**
* Permission processed successfully
*/
200: boolean
}
export type PermissionReplyResponse = PermissionReplyResponses[keyof PermissionReplyResponses]
export type PermissionListData = {
body?: never
path?: never
query?: {
directory?: string
}
url: "/permission"
}
export type PermissionListResponses = {
/**
* List of pending permissions
*/
200: Array<PermissionRequest>
}
export type PermissionListResponse = PermissionListResponses[keyof PermissionListResponses]
export type QuestionListData = {
body?: never
path?: never
query?: {
directory?: string
}
url: "/question"
}
export type QuestionListResponses = {
/**
* List of pending questions
*/
200: Array<QuestionRequest>
}
export type QuestionListResponse = QuestionListResponses[keyof QuestionListResponses]
export type QuestionReplyData = {
body?: {
/**
* User answers in order of questions (each answer is an array of selected labels)
*/
answers: Array<QuestionAnswer>
}
path: {
requestID: string
}
query?: {
directory?: string
}
url: "/question/{requestID}/reply"
}
export type QuestionReplyErrors = {
/**
* Bad request
*/
400: BadRequestError
/**
* Not found
*/
404: NotFoundError
}
export type QuestionReplyError = QuestionReplyErrors[keyof QuestionReplyErrors]
export type QuestionReplyResponses = {
/**
* Question answered successfully
*/
200: boolean
}
export type QuestionReplyResponse = QuestionReplyResponses[keyof QuestionReplyResponses]
export type QuestionRejectData = {
body?: never
path: {
requestID: string
}
query?: {
directory?: string
}
url: "/question/{requestID}/reject"
}
export type QuestionRejectErrors = {
/**
* Bad request
*/
400: BadRequestError
/**
* Not found
*/
404: NotFoundError
}
export type QuestionRejectError = QuestionRejectErrors[keyof QuestionRejectErrors]
export type QuestionRejectResponses = {
/**
* Question rejected successfully
*/
200: boolean
}
export type QuestionRejectResponse = QuestionRejectResponses[keyof QuestionRejectResponses]
export type CommandListData = {
body?: never
path?: never
@ -3468,6 +3853,11 @@ export type ProviderListResponses = {
provider?: {
npm: string
}
variants?: {
[key: string]: {
[key: string]: unknown
}
}
}
}
}>
@ -3620,6 +4010,8 @@ export type FindFilesData = {
directory?: string
query: string
dirs?: "true" | "false"
type?: "file" | "directory"
limit?: number
}
url: "/find/file"
}
@ -4004,6 +4396,27 @@ export type McpDisconnectResponses = {
export type McpDisconnectResponse = McpDisconnectResponses[keyof McpDisconnectResponses]
export type ExperimentalResourceListData = {
body?: never
path?: never
query?: {
directory?: string
}
url: "/experimental/resource"
}
export type ExperimentalResourceListResponses = {
/**
* MCP resources
*/
200: {
[key: string]: McpResource
}
}
export type ExperimentalResourceListResponse =
ExperimentalResourceListResponses[keyof ExperimentalResourceListResponses]
export type LspStatusData = {
body?: never
path?: never
@ -4233,7 +4646,7 @@ export type TuiShowToastResponses = {
export type TuiShowToastResponse = TuiShowToastResponses[keyof TuiShowToastResponses]
export type TuiPublishData = {
body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow
body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow | EventTuiSessionSelect
path?: never
query?: {
directory?: string
@ -4259,6 +4672,42 @@ export type TuiPublishResponses = {
export type TuiPublishResponse = TuiPublishResponses[keyof TuiPublishResponses]
export type TuiSelectSessionData = {
body?: {
/**
* Session ID to navigate to
*/
sessionID: string
}
path?: never
query?: {
directory?: string
}
url: "/tui/select-session"
}
export type TuiSelectSessionErrors = {
/**
* Bad request
*/
400: BadRequestError
/**
* Not found
*/
404: NotFoundError
}
export type TuiSelectSessionError = TuiSelectSessionErrors[keyof TuiSelectSessionErrors]
export type TuiSelectSessionResponses = {
/**
* Session selected successfully
*/
200: boolean
}
export type TuiSelectSessionResponse = TuiSelectSessionResponses[keyof TuiSelectSessionResponses]
export type TuiControlNextData = {
body?: never
path?: never

View file

@ -28,7 +28,10 @@ export async function createOpencodeServer(options?: ServerOptions) {
options ?? {},
)
const proc = spawn(`opencode`, [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], {
const args = [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`]
if (options.config?.logLevel) args.push(`--log-level=${options.config.logLevel}`)
const proc = spawn(`opencode`, args, {
signal: options.signal,
env: {
...process.env,

File diff suppressed because it is too large Load diff