diff --git a/src/agents/types.ts b/src/agents/types.ts index a7cbd06..d2b6e32 100644 --- a/src/agents/types.ts +++ b/src/agents/types.ts @@ -14,26 +14,17 @@ export interface RunAgentOptions { sessionId?: string; model?: string; provider?: 'claude' | 'codex' | 'opencode' | 'mock'; - /** Resolved path to persona prompt file */ personaPath?: string; - /** Allowed tools for this agent run */ allowedTools?: string[]; - /** MCP servers for this agent run */ mcpServers?: Record; - /** Maximum number of agentic turns */ maxTurns?: number; - /** Permission mode for tool execution (from piece step) */ permissionMode?: PermissionMode; - /** Provider-specific movement options */ providerOptions?: MovementProviderOptions; onStream?: StreamCallback; onPermissionRequest?: PermissionHandler; onAskUserQuestion?: AskUserQuestionHandler; - /** Bypass all permission checks (sacrifice-my-pc mode) */ bypassPermissions?: boolean; - /** Language for template resolution */ language?: Language; - /** Piece meta information for system prompt template */ pieceMeta?: { pieceName: string; pieceDescription?: string; @@ -41,6 +32,5 @@ export interface RunAgentOptions { movementsList: ReadonlyArray<{ name: string; description?: string }>; currentPosition: string; }; - /** JSON Schema for structured output */ outputSchema?: Record; } diff --git a/src/infra/opencode/client.ts b/src/infra/opencode/client.ts index 1f33038..07f3970 100644 --- a/src/infra/opencode/client.ts +++ b/src/infra/opencode/client.ts @@ -273,6 +273,8 @@ export class OpenCodeClient { let diagRef: StreamDiagnostics | undefined; let serverClose: (() => void) | undefined; let opencodeApiClient: Awaited>['client'] | undefined; + let sessionId: string | undefined = options.sessionId; + const interactionTimeoutMs = options.interactionTimeoutMs ?? OPENCODE_INTERACTION_TIMEOUT_MS; const resetIdleTimeout = (): void => { if (idleTimeoutId !== undefined) { @@ -330,14 +332,14 @@ export class OpenCodeClient { opencodeApiClient = client; serverClose = server.close; - const sessionResult = options.sessionId - ? { data: { id: options.sessionId } } + const sessionResult = sessionId + ? { data: { id: sessionId } } : await client.session.create({ directory: options.cwd, permission: buildOpenCodePermissionRuleset(options.permissionMode, options.networkAccess), }); - const sessionId = sessionResult.data?.id; + sessionId = sessionResult.data?.id; if (!sessionId) { throw new Error('Failed to create OpenCode session'); } @@ -420,18 +422,24 @@ export class OpenCodeClient { sessionID: string; }; if (permProps.sessionID === sessionId) { - const reply = options.permissionMode - ? mapToOpenCodePermissionReply(options.permissionMode) - : 'once'; - await withTimeout( - (signal) => client.permission.reply({ - requestID: permProps.id, - directory: options.cwd, - reply, - }, { signal }), - OPENCODE_INTERACTION_TIMEOUT_MS, - 'OpenCode permission reply timed out', - ); + try { + const reply = options.permissionMode + ? mapToOpenCodePermissionReply(options.permissionMode) + : 'once'; + await withTimeout( + (signal) => client.permission.reply({ + requestID: permProps.id, + directory: options.cwd, + reply, + }, { signal }), + interactionTimeoutMs, + 'OpenCode permission reply timed out', + ); + } catch (e) { + success = false; + failureMessage = getErrorMessage(e); + break; + } } continue; } @@ -440,14 +448,20 @@ export class OpenCodeClient { const questionProps = sseEvent.properties as OpenCodeQuestionAskedProperties; if (questionProps.sessionID === sessionId) { if (!options.onAskUserQuestion) { - await withTimeout( - (signal) => client.question.reject({ - requestID: questionProps.id, - directory: options.cwd, - }, { signal }), - OPENCODE_INTERACTION_TIMEOUT_MS, - 'OpenCode question reject timed out', - ); + try { + await withTimeout( + (signal) => client.question.reject({ + requestID: questionProps.id, + directory: options.cwd, + }, { signal }), + interactionTimeoutMs, + 'OpenCode question reject timed out', + ); + } catch (e) { + success = false; + failureMessage = getErrorMessage(e); + break; + } continue; } @@ -459,20 +473,12 @@ export class OpenCodeClient { directory: options.cwd, answers: toQuestionAnswers(questionProps, answers), }, { signal }), - OPENCODE_INTERACTION_TIMEOUT_MS, + interactionTimeoutMs, 'OpenCode question reply timed out', ); - } catch { - await withTimeout( - (signal) => client.question.reject({ - requestID: questionProps.id, - directory: options.cwd, - }, { signal }), - OPENCODE_INTERACTION_TIMEOUT_MS, - 'OpenCode question reject timed out', - ); + } catch (e) { success = false; - failureMessage = 'OpenCode question handling failed'; + failureMessage = getErrorMessage(e); break; } } @@ -631,8 +637,8 @@ export class OpenCodeClient { continue; } - if (options.sessionId) { - emitResult(options.onStream, false, errorMessage, options.sessionId); + if (sessionId) { + emitResult(options.onStream, false, errorMessage, sessionId); } return { @@ -640,7 +646,7 @@ export class OpenCodeClient { status: 'error', content: errorMessage, timestamp: new Date(), - sessionId: options.sessionId, + sessionId, }; } finally { if (idleTimeoutId !== undefined) { diff --git a/src/infra/opencode/types.ts b/src/infra/opencode/types.ts index e407d6b..a0c2c42 100644 --- a/src/infra/opencode/types.ts +++ b/src/infra/opencode/types.ts @@ -187,15 +187,11 @@ export interface OpenCodeCallOptions { model: string; systemPrompt?: string; allowedTools?: string[]; - /** Permission mode for automatic permission handling */ permissionMode?: PermissionMode; - /** Override network access (webfetch/websearch) */ networkAccess?: boolean; - /** Enable streaming mode with callback (best-effort) */ onStream?: StreamCallback; onAskUserQuestion?: AskUserQuestionHandler; - /** OpenCode API key */ opencodeApiKey?: string; - /** JSON Schema for structured output */ outputSchema?: Record; + interactionTimeoutMs?: number; } diff --git a/src/infra/providers/types.ts b/src/infra/providers/types.ts index 9560cf2..b5c9263 100644 --- a/src/infra/providers/types.ts +++ b/src/infra/providers/types.ts @@ -24,25 +24,17 @@ export interface ProviderCallOptions { sessionId?: string; model?: string; allowedTools?: string[]; - /** MCP servers configuration */ mcpServers?: Record; - /** Maximum number of agentic turns */ maxTurns?: number; - /** Permission mode for tool execution (from piece step) */ permissionMode?: PermissionMode; - /** Provider-specific movement options */ providerOptions?: MovementProviderOptions; onStream?: StreamCallback; onPermissionRequest?: PermissionHandler; onAskUserQuestion?: AskUserQuestionHandler; bypassPermissions?: boolean; - /** Anthropic API key for Claude provider */ anthropicApiKey?: string; - /** OpenAI API key for Codex provider */ openaiApiKey?: string; - /** OpenCode API key for OpenCode provider */ opencodeApiKey?: string; - /** JSON Schema for structured output */ outputSchema?: Record; }