summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-11-05 00:50:48 -0600
committerGitHub <[email protected]>2025-11-05 00:50:48 -0600
commit1e0596bc46fcd130257174e6691c74484391f905 (patch)
tree470766dba92c831c06e6f92d4db61ec874294de1
parent3ebec2435a4c9dd9b5080fbb1d2591a8836305b8 (diff)
downloadopencode-1e0596bc46fcd130257174e6691c74484391f905.tar.gz
opencode-1e0596bc46fcd130257174e6691c74484391f905.zip
ACP: update package, fix slash command bug (#3906)
-rw-r--r--bun.lock4
-rw-r--r--packages/opencode/package.json4
-rw-r--r--packages/opencode/src/acp/agent.ts88
-rw-r--r--packages/opencode/src/acp/session.ts7
-rw-r--r--packages/opencode/src/acp/types.ts1
-rw-r--r--packages/opencode/src/mcp/index.ts20
6 files changed, 88 insertions, 36 deletions
diff --git a/bun.lock b/bun.lock
index 53ed347dc..866ce4472 100644
--- a/bun.lock
+++ b/bun.lock
@@ -173,7 +173,7 @@
"dependencies": {
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
- "@agentclientprotocol/sdk": "0.4.9",
+ "@agentclientprotocol/sdk": "0.5.1",
"@clack/prompts": "1.0.0-alpha.1",
"@hono/standard-validator": "0.1.5",
"@hono/zod-validator": "catalog:",
@@ -401,7 +401,7 @@
"@adobe/css-tools": ["@adobe/[email protected]", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="],
- "@agentclientprotocol/sdk": ["@agentclientprotocol/[email protected]", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-ExwH828LaTGoTTjxuw49l+fwOLA+Yx0+qkWn1TcHMOsY5mVI9CUfkj7ZDhv2klgZ7mJeT+lxX/Dn/KINv1AkNQ=="],
+ "@agentclientprotocol/sdk": ["@agentclientprotocol/[email protected]", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg=="],
"@ai-sdk/amazon-bedrock": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-icLGO7Q0NinnHIPgT+y1QjHVwH4HwV+brWbvM+FfCG2Afpa89PyKa3Ret91kGjZpBgM/xnj1B7K5eM+rRlsXQA=="],
diff --git a/packages/opencode/package.json b/packages/opencode/package.json
index 841f972b9..d9fe076f9 100644
--- a/packages/opencode/package.json
+++ b/packages/opencode/package.json
@@ -43,7 +43,7 @@
"dependencies": {
"@actions/core": "1.11.1",
"@actions/github": "6.0.1",
- "@agentclientprotocol/sdk": "0.4.9",
+ "@agentclientprotocol/sdk": "0.5.1",
"@clack/prompts": "1.0.0-alpha.1",
"@hono/standard-validator": "0.1.5",
"@hono/zod-validator": "catalog:",
@@ -57,8 +57,8 @@
"@opentui/core": "0.1.33",
"@opentui/solid": "0.1.33",
"@parcel/watcher": "2.5.1",
- "@solid-primitives/event-bus": "1.1.2",
"@pierre/precision-diffs": "catalog:",
+ "@solid-primitives/event-bus": "1.1.2",
"@standard-schema/spec": "1.0.0",
"@zip.js/zip.js": "2.7.62",
"ai": "catalog:",
diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts
index 1eae36e66..47bac4e57 100644
--- a/packages/opencode/src/acp/agent.ts
+++ b/packages/opencode/src/acp/agent.ts
@@ -1,9 +1,12 @@
import {
+ RequestError,
type Agent as ACPAgent,
type AgentSideConnection,
type AuthenticateRequest,
+ type AuthMethod,
type CancelNotification,
type InitializeRequest,
+ type InitializeResponse,
type LoadSessionRequest,
type NewSessionRequest,
type PermissionOption,
@@ -33,6 +36,7 @@ import type { Config } from "@/config/config"
import { MCP } from "@/mcp"
import { Todo } from "@/session/todo"
import { z } from "zod"
+import { LoadAPIKeyError } from "ai"
export namespace ACP {
const log = Log.create({ service: "acp-agent" })
@@ -302,9 +306,26 @@ export namespace ACP {
})
}
- async initialize(params: InitializeRequest) {
+ async initialize(params: InitializeRequest): Promise<InitializeResponse> {
log.info("initialize", { protocolVersion: params.protocolVersion })
+ const authMethod: AuthMethod = {
+ description: "Run `opencode auth login` in the terminal",
+ name: "Login with opencode",
+ id: "opencode-login",
+ }
+
+ // If client supports terminal-auth capability, use that instead.
+ if (params.clientCapabilities?._meta?.["terminal-auth"] === true) {
+ authMethod._meta = {
+ "terminal-auth": {
+ command: "opencode",
+ args: ["auth", "login"],
+ label: "OpenCode Login",
+ },
+ }
+ }
+
return {
protocolVersion: 1,
agentCapabilities: {
@@ -325,10 +346,9 @@ export namespace ACP {
id: "opencode-login",
},
],
- _meta: {
- opencode: {
- version: Installation.VERSION,
- },
+ agentInfo: {
+ name: "OpenCode",
+ version: Installation.VERSION,
},
}
}
@@ -338,21 +358,31 @@ export namespace ACP {
}
async newSession(params: NewSessionRequest) {
- const model = await defaultModel(this.config)
- const session = await this.sessionManager.create(params.cwd, params.mcpServers, model)
-
- log.info("creating_session", { mcpServers: params.mcpServers.length })
- const load = await this.loadSession({
- cwd: params.cwd,
- mcpServers: params.mcpServers,
- sessionId: session.id,
- })
+ try {
+ const model = await defaultModel(this.config)
+ const session = await this.sessionManager.create(params.cwd, params.mcpServers, model)
+
+ log.info("creating_session", { mcpServers: params.mcpServers.length })
+ const load = await this.loadSession({
+ cwd: params.cwd,
+ mcpServers: params.mcpServers,
+ sessionId: session.id,
+ })
- return {
- sessionId: session.id,
- models: load.models,
- modes: load.modes,
- _meta: {},
+ return {
+ sessionId: session.id,
+ models: load.models,
+ modes: load.modes,
+ _meta: {},
+ }
+ } catch (e) {
+ const error = MessageV2.fromError(e, {
+ providerID: this.config.defaultModel?.providerID ?? "unknown",
+ })
+ if (LoadAPIKeyError.isInstance(error)) {
+ throw RequestError.authRequired()
+ }
+ throw e
}
}
@@ -387,16 +417,6 @@ export namespace ACP {
description: "compact the session",
})
- setTimeout(() => {
- this.connection.sessionUpdate({
- sessionId,
- update: {
- sessionUpdate: "available_commands_update",
- availableCommands,
- },
- })
- }, 0)
-
const availableModes = (await Agents.list())
.filter((agent) => agent.mode !== "subagent")
.map((agent) => ({
@@ -437,6 +457,16 @@ export namespace ACP {
}),
)
+ setTimeout(() => {
+ this.connection.sessionUpdate({
+ sessionId,
+ update: {
+ sessionUpdate: "available_commands_update",
+ availableCommands,
+ },
+ })
+ }, 0)
+
return {
sessionId,
models: {
diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts
index 5d45ee283..d3ab73d21 100644
--- a/packages/opencode/src/acp/session.ts
+++ b/packages/opencode/src/acp/session.ts
@@ -6,13 +6,18 @@ import type { ACPSessionState } from "./types"
export class ACPSessionManager {
private sessions = new Map<string, ACPSessionState>()
- async create(cwd: string, mcpServers: McpServer[], model?: ACPSessionState["model"]): Promise<ACPSessionState> {
+ async create(
+ cwd: string,
+ mcpServers: McpServer[],
+ model?: ACPSessionState["model"],
+ ): Promise<ACPSessionState> {
const session = await Session.create({ title: `ACP Session ${crypto.randomUUID()}` })
const sessionId = session.id
const resolvedModel = model ?? (await Provider.defaultModel())
const state: ACPSessionState = {
id: sessionId,
+ parentId: session.parentID,
cwd,
mcpServers,
createdAt: new Date(),
diff --git a/packages/opencode/src/acp/types.ts b/packages/opencode/src/acp/types.ts
index 56308cb76..119b335ce 100644
--- a/packages/opencode/src/acp/types.ts
+++ b/packages/opencode/src/acp/types.ts
@@ -2,6 +2,7 @@ import type { McpServer } from "@agentclientprotocol/sdk"
export interface ACPSessionState {
id: string
+ parentId?: string
cwd: string
mcpServers: McpServer[]
createdAt: Date
diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts
index d6de4a59b..fbc887030 100644
--- a/packages/opencode/src/mcp/index.ts
+++ b/packages/opencode/src/mcp/index.ts
@@ -77,7 +77,15 @@ export namespace MCP {
}
},
async (state) => {
- await Promise.all(Object.values(state.clients).map((client) => client.close()))
+ await Promise.all(
+ Object.values(state.clients).map((client) =>
+ client.close().catch((error) => {
+ log.error("Failed to close MCP client", {
+ error,
+ })
+ }),
+ ),
+ )
},
)
@@ -201,7 +209,15 @@ export namespace MCP {
const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch(() => {})
if (!result) {
- await mcpClient.close()
+ await mcpClient.close().catch((error) => {
+ log.error("Failed to close MCP client", {
+ error,
+ })
+ })
+ status = {
+ status: "failed",
+ error: "Failed to get tools",
+ }
return {
mcpClient: undefined,
status: {