diff --git a/packages/server/src/server/agent/providers/opencode-agent.ts b/packages/server/src/server/agent/providers/opencode-agent.ts index 4318c16e3..09485c659 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.ts @@ -1954,6 +1954,15 @@ function getOpenCodeSubAgentMaps(state: OpenCodeEventTranslationState): { }; } +function isOpenCodeSessionTrackedByParent( + sessionId: string, + state: OpenCodeEventTranslationState, +): boolean { + return ( + sessionId === state.sessionId || state.subAgentCallIdByChildSessionId?.has(sessionId) === true + ); +} + function getOpenCodeSubAgentState( callId: string, state: OpenCodeEventTranslationState, @@ -2438,7 +2447,7 @@ function appendOpenCodePermissionAsked( state: OpenCodeEventTranslationState, events: AgentStreamEvent[], ): void { - if (event.properties.sessionID !== state.sessionId) { + if (!isOpenCodeSessionTrackedByParent(event.properties.sessionID, state)) { return; } const metadata = readOpenCodeRecord(event.properties.metadata); diff --git a/packages/server/src/server/agent/providers/opencode/event-translator.test.ts b/packages/server/src/server/agent/providers/opencode/event-translator.test.ts index 1a9fe8b07..ca3e4e49d 100644 --- a/packages/server/src/server/agent/providers/opencode/event-translator.test.ts +++ b/packages/server/src/server/agent/providers/opencode/event-translator.test.ts @@ -387,6 +387,97 @@ describe("translateOpenCodeEvent", () => { ]); }); + it("forwards permission requests from linked OpenCode subagent sessions", () => { + const state = createState(); + + translateOpenCodeEvent( + { + type: "message.part.updated", + properties: { + part: { + id: "part-subagent", + sessionID: "session-1", + messageID: "message-1", + type: "tool", + tool: "task", + callID: "call-subagent", + state: { + status: "running", + input: { + subagent_type: "explore", + description: "Explore external config", + }, + }, + }, + }, + }, + state, + ); + translateOpenCodeEvent( + { + type: "session.created", + properties: { + sessionID: "child-session-1", + info: { + id: "child-session-1", + parentID: "session-1", + }, + }, + }, + state, + ); + + const result = translateOpenCodeEvent( + { + type: "permission.asked", + properties: { + id: "perm-child-1", + sessionID: "child-session-1", + permission: "external_directory", + patterns: ["/Users/example/.config/nvim"], + metadata: { + reason: "Need to inspect the requested config directory", + }, + }, + }, + state, + ); + + expect(result).toEqual([ + { + type: "permission_requested", + provider: "opencode", + request: { + id: "perm-child-1", + provider: "opencode", + name: "external_directory", + kind: "tool", + title: "Access external directory", + description: + "Need to inspect the requested config directory - Scope: /Users/example/.config/nvim", + input: { + patterns: ["/Users/example/.config/nvim"], + metadata: { + reason: "Need to inspect the requested config directory", + }, + }, + detail: { + type: "unknown", + input: { + permission: "external_directory", + patterns: ["/Users/example/.config/nvim"], + metadata: { + reason: "Need to inspect the requested config directory", + }, + }, + output: null, + }, + actions: openCodePermissionActions, + }, + }, + ]); + }); + it("emits usage_updated after step-finish parts", () => { const state = createState(); state.accumulatedUsage.contextWindowMaxTokens = 400_000;