import { randomUUID } from 'crypto'; import { OPEN_CODE_DELIVERY_ACCEPTANCE_CONTRACT_VERSION, stableHash, } from './OpenCodeBridgeCommandContract'; import { buildOpenCodeBridgeSupportDiagnostic } from './OpenCodeBridgeSupportDiagnostics'; import type { OpenCodeTeamRuntimeBridgePort } from '../../runtime/OpenCodeTeamRuntimeAdapter'; import type { OpenCodeTeamLaunchReadiness, OpenCodeTeamLaunchReadinessState, } from '../readiness/OpenCodeTeamLaunchReadiness'; import type { OpenCodeAnswerPermissionCommandBody, OpenCodeBackfillTaskLedgerCommandBody, OpenCodeBackfillTaskLedgerCommandData, OpenCodeBridgeCommandName, OpenCodeBridgeDiagnosticEvent, OpenCodeBridgeFailureKind, OpenCodeBridgeResult, OpenCodeBridgeRuntimeSnapshot, OpenCodeCleanupHostsCommandBody, OpenCodeCleanupHostsCommandData, OpenCodeCommandStatusCommandBody, OpenCodeCommandStatusCommandData, OpenCodeLaunchTeamCommandBody, OpenCodeLaunchTeamCommandData, OpenCodeListRuntimePermissionsCommandBody, OpenCodeListRuntimePermissionsCommandData, OpenCodeObserveMessageDeliveryCommandBody, OpenCodeObserveMessageDeliveryCommandData, OpenCodeReconcileTeamCommandBody, OpenCodeSendMessageCommandBody, OpenCodeSendMessageCommandData, OpenCodeStopTeamCommandBody, OpenCodeStopTeamCommandData, } from './OpenCodeBridgeCommandContract'; import type { OpenCodeStateChangingBridgeCommandService } from './OpenCodeStateChangingBridgeCommandService'; export interface OpenCodeLedgerBackfillPort { backfillOpenCodeTaskLedger( input: OpenCodeBackfillTaskLedgerCommandBody ): Promise; } export interface OpenCodeReadinessBridgeCommandExecutor { execute( command: OpenCodeBridgeCommandName, body: TBody, options: { cwd: string; timeoutMs: number; requestId?: string; stdoutLimitBytes?: number; stderrLimitBytes?: number; } ): Promise>; } export interface OpenCodeReadinessBridgeOptions { timeoutMs?: number; launchTimeoutMs?: number; reconcileTimeoutMs?: number; sendTimeoutMs?: number; observeTimeoutMs?: number; stopTimeoutMs?: number; cleanupTimeoutMs?: number; appVersion?: string; stateChangingCommands?: Pick; } export interface OpenCodeReadinessBridgeCommandBody { projectPath: string; selectedModel: string | null; requireExecutionProbe: boolean; } const DEFAULT_READINESS_TIMEOUT_MS = 120_000; const DEFAULT_LAUNCH_TIMEOUT_MS = 120_000; const DEFAULT_RECONCILE_TIMEOUT_MS = 30_000; // Longer than the renderer-facing UI timeout: late OpenCode turns should still // finish bridge-side observation and emit member-work-sync signals. const DEFAULT_SEND_TIMEOUT_MS = 45_000; const DEFAULT_OBSERVE_TIMEOUT_MS = 20_000; const DEFAULT_STOP_TIMEOUT_MS = 30_000; const DEFAULT_CLEANUP_TIMEOUT_MS = 10_000; const DEFAULT_PERMISSION_TIMEOUT_MS = 30_000; const DEFAULT_BACKFILL_TIMEOUT_MS = 45_000; const DEFAULT_COMMAND_STATUS_TIMEOUT_MS = 5_000; const OPEN_CODE_COMPLETED_COMMAND_RECOVERY_MESSAGE = 'OpenCode bridge command already completed; recover through commandStatus'; function buildSendPayloadHash(input: OpenCodeSendMessageCommandBody): string { const { payloadHash: _payloadHash, settlementMode: _settlementMode, ...hashable } = input; return stableHash(hashable); } export class OpenCodeReadinessBridge implements OpenCodeTeamRuntimeBridgePort { private readonly lastRuntimeSnapshotsByProjectPath = new Map< string, OpenCodeBridgeRuntimeSnapshot >(); constructor( private readonly bridge: OpenCodeReadinessBridgeCommandExecutor, private readonly options: OpenCodeReadinessBridgeOptions = {} ) {} async checkOpenCodeTeamLaunchReadiness( input: OpenCodeReadinessBridgeCommandBody ): Promise { const result = await this.bridge.execute< OpenCodeReadinessBridgeCommandBody, OpenCodeTeamLaunchReadiness >('opencode.readiness', input, { cwd: input.projectPath, timeoutMs: this.options.timeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS, }); if (result.ok) { this.lastRuntimeSnapshotsByProjectPath.set(input.projectPath, result.runtime); return result.data; } this.lastRuntimeSnapshotsByProjectPath.delete(input.projectPath); const supportDiagnostic = buildOpenCodeBridgeSupportDiagnostic({ result, projectPath: input.projectPath, selectedModel: input.selectedModel, appVersion: this.options.appVersion ?? null, }); return blockedReadiness({ state: mapBridgeFailureToReadinessState(result.error.kind), modelId: input.selectedModel, diagnostics: [ `OpenCode readiness bridge failed: ${result.error.kind}: ${result.error.message}`, ...result.diagnostics.map(formatDiagnosticEvent), ], missing: [result.error.message], supportDiagnostics: supportDiagnostic ? [supportDiagnostic] : undefined, }); } getLastOpenCodeRuntimeSnapshot(projectPath: string): OpenCodeBridgeRuntimeSnapshot | null { return this.lastRuntimeSnapshotsByProjectPath.get(projectPath) ?? null; } async launchOpenCodeTeam( input: OpenCodeLaunchTeamCommandBody ): Promise { const result = await this.executeStateChangingCommand< OpenCodeLaunchTeamCommandBody, OpenCodeLaunchTeamCommandData >('opencode.launchTeam', input, { teamName: input.teamName, laneId: input.laneId, runId: input.runId, capabilitySnapshotId: input.expectedCapabilitySnapshotId, cwd: input.projectPath, timeoutMs: this.options.launchTimeoutMs ?? DEFAULT_LAUNCH_TIMEOUT_MS, }); return result.ok ? result.data : blockedLaunchData(input.runId, result); } async reconcileOpenCodeTeam( input: OpenCodeReconcileTeamCommandBody ): Promise { const cwd = input.projectPath ?? process.cwd(); const result = await this.executeStateChangingCommand< OpenCodeReconcileTeamCommandBody, OpenCodeLaunchTeamCommandData >('opencode.reconcileTeam', input, { teamName: input.teamName, laneId: input.laneId, runId: input.runId, capabilitySnapshotId: input.expectedCapabilitySnapshotId ?? null, cwd, timeoutMs: this.options.reconcileTimeoutMs ?? DEFAULT_RECONCILE_TIMEOUT_MS, }); return result.ok ? result.data : blockedLaunchData(input.runId, result); } async stopOpenCodeTeam(input: OpenCodeStopTeamCommandBody): Promise { const cwd = input.projectPath ?? process.cwd(); const result = await this.executeStateChangingCommand< OpenCodeStopTeamCommandBody, OpenCodeStopTeamCommandData >('opencode.stopTeam', input, { teamName: input.teamName, laneId: input.laneId, runId: input.runId, capabilitySnapshotId: input.expectedCapabilitySnapshotId ?? null, cwd, timeoutMs: this.options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, }); if (result.ok) { return result.data; } return { runId: input.runId, stopped: false, members: {}, warnings: [], diagnostics: [ { code: result.error.kind, severity: 'error', message: `OpenCode stop bridge failed: ${result.error.message}`, }, ...result.diagnostics.map((event) => ({ code: event.type, severity: event.severity, message: event.message, })), ], }; } async answerOpenCodeRuntimePermission( input: OpenCodeAnswerPermissionCommandBody ): Promise { const result = await this.executeStateChangingCommand< OpenCodeAnswerPermissionCommandBody, OpenCodeLaunchTeamCommandData >('opencode.answerPermission', input, { teamName: input.teamName, laneId: input.laneId, runId: input.runId, capabilitySnapshotId: input.expectedCapabilitySnapshotId ?? null, cwd: input.projectPath, timeoutMs: DEFAULT_PERMISSION_TIMEOUT_MS, }); return result.ok ? result.data : blockedLaunchData(input.runId, result); } async listOpenCodeRuntimePermissions( input: OpenCodeListRuntimePermissionsCommandBody ): Promise { const cwd = input.projectPath ?? process.cwd(); const result = await this.bridge.execute< OpenCodeListRuntimePermissionsCommandBody, OpenCodeListRuntimePermissionsCommandData >('opencode.listRuntimePermissions', input, { cwd, timeoutMs: DEFAULT_PERMISSION_TIMEOUT_MS, }); if (result.ok) { return result.data; } return { permissions: [] }; } async cleanupOpenCodeHosts( input: OpenCodeCleanupHostsCommandBody ): Promise { const cwd = input.projectPath ?? process.cwd(); const result = await this.bridge.execute< OpenCodeCleanupHostsCommandBody, OpenCodeCleanupHostsCommandData >('opencode.cleanupHosts', input, { cwd, timeoutMs: this.options.cleanupTimeoutMs ?? DEFAULT_CLEANUP_TIMEOUT_MS, }); if (result.ok) { return result.data; } return { cleaned: 0, remaining: 0, hosts: [], diagnostics: [ `OpenCode host cleanup bridge failed: ${result.error.kind}: ${result.error.message}`, ...result.diagnostics.map(formatDiagnosticEvent), ], }; } async sendOpenCodeTeamMessage( input: OpenCodeSendMessageCommandBody ): Promise { const commandRequestId = `opencode-send-${randomUUID()}`; const body: OpenCodeSendMessageCommandBody = { ...input, payloadHash: input.payloadHash ?? buildSendPayloadHash(input), }; let activeRequestId = commandRequestId; let activeBody = body; let usedObservedFallback = false; const executeSend = async ( nextBody: OpenCodeSendMessageCommandBody, requestId: string ): Promise<{ result: OpenCodeBridgeResult; requestId: string; }> => { if (this.options.stateChangingCommands && nextBody.settlementMode === 'acceptance') { const result = await this.options.stateChangingCommands.execute< OpenCodeSendMessageCommandBody, OpenCodeSendMessageCommandData >({ command: 'opencode.sendMessage', teamName: nextBody.teamName, laneId: nextBody.laneId, runId: nextBody.runId ?? null, capabilitySnapshotId: null, behaviorFingerprint: null, body: nextBody, cwd: nextBody.projectPath, timeoutMs: this.options.sendTimeoutMs ?? DEFAULT_SEND_TIMEOUT_MS, }); return { result, requestId: result.requestId || requestId }; } const result = await this.bridge.execute< OpenCodeSendMessageCommandBody, OpenCodeSendMessageCommandData >('opencode.sendMessage', nextBody, { cwd: nextBody.projectPath, timeoutMs: this.options.sendTimeoutMs ?? DEFAULT_SEND_TIMEOUT_MS, requestId, }); return { result, requestId: result.requestId || requestId }; }; let result: OpenCodeBridgeResult; try { const executed = await executeSend(activeBody, activeRequestId); result = executed.result; activeRequestId = executed.requestId; } catch (error) { if (body.settlementMode === 'acceptance' && isOpenCodeCompletedCommandRecoveryError(error)) { const recovered = await this.recoverSendMessageOutcome({ originalRequestId: null, body: activeBody, diagnosticCode: 'opencode_send_recovered_after_duplicate_completed_command', diagnosticMessage: 'OpenCode bridge outcome recovered after duplicate completed command.', }); if (recovered) { return recovered; } } if ( body.settlementMode !== 'acceptance' || !isOpenCodeAcceptanceContractMissingError(error) ) { throw error; } if (body.forceSessionRefreshReason?.trim()) { return buildOpenCodeForceSessionRefreshUnsupportedData(body, error); } activeRequestId = `${commandRequestId}-observed`; activeBody = { ...body, settlementMode: 'observed', }; usedObservedFallback = true; const executed = await executeSend(activeBody, activeRequestId); result = executed.result; activeRequestId = executed.requestId; } if ( !result.ok && activeBody.settlementMode === 'acceptance' && isOpenCodeAcceptanceContractMissingError(result.error.message) ) { if (body.forceSessionRefreshReason?.trim()) { return buildOpenCodeForceSessionRefreshUnsupportedData(body, result.error.message); } activeRequestId = `${commandRequestId}-observed`; activeBody = { ...body, settlementMode: 'observed', }; usedObservedFallback = true; const executed = await executeSend(activeBody, activeRequestId); result = executed.result; activeRequestId = executed.requestId; } if (result.ok) { return usedObservedFallback ? withOpenCodeObservedFallbackDiagnostic(result.data) : result.data; } if (result.error.kind === 'timeout') { const recovered = await this.recoverSendMessageOutcome({ originalRequestId: activeRequestId, body: activeBody, diagnosticCode: 'opencode_send_recovered_after_bridge_timeout', diagnosticMessage: 'OpenCode bridge outcome recovered after timeout.', }); if (recovered) { return usedObservedFallback ? withOpenCodeObservedFallbackDiagnostic(recovered) : recovered; } } return { accepted: false, memberName: activeBody.memberName, diagnostics: [ { code: result.error.kind, severity: 'error', message: `OpenCode message bridge failed: ${result.error.message}`, }, ...result.diagnostics.map((event) => ({ code: event.type, severity: event.severity, message: event.message, })), ], }; } private async recoverSendMessageOutcome(input: { originalRequestId?: string | null; body: OpenCodeSendMessageCommandBody; diagnosticCode: string; diagnosticMessage: string; }): Promise { if (!input.originalRequestId && !input.body.deliveryAttemptId) { return null; } const statusBody: OpenCodeCommandStatusCommandBody = { originalCommand: 'opencode.sendMessage', deliveryAttemptId: input.body.deliveryAttemptId, teamId: input.body.teamId, teamName: input.body.teamName, laneId: input.body.laneId, memberName: input.body.memberName, messageId: input.body.messageId, payloadHash: input.body.payloadHash, projectPath: input.body.projectPath, runId: input.body.runId, ...(input.originalRequestId ? { originalRequestId: input.originalRequestId } : {}), }; const statusResult = await this.bridge.execute< OpenCodeCommandStatusCommandBody, OpenCodeCommandStatusCommandData >('opencode.commandStatus', statusBody, { cwd: input.body.projectPath, timeoutMs: DEFAULT_COMMAND_STATUS_TIMEOUT_MS, }); if (!statusResult.ok) { return null; } const status = statusResult.data; if ( input.originalRequestId && status.originalRequestId && status.originalRequestId !== input.originalRequestId ) { return null; } if ( input.body.deliveryAttemptId && status.deliveryAttemptId && status.deliveryAttemptId !== input.body.deliveryAttemptId ) { return null; } if (status.status === 'precondition_mismatch' || status.accepted !== true) { return null; } const diagnostics = [ { code: input.diagnosticCode, severity: 'warning' as const, message: input.diagnosticMessage, }, ...status.diagnostics.map((message) => ({ code: 'opencode_command_status', severity: 'info' as const, message, })), ]; if (status.sendMessageData?.accepted === true) { return { ...status.sendMessageData, diagnostics: [...diagnostics, ...status.sendMessageData.diagnostics], }; } return { accepted: true, memberName: input.body.memberName, sessionId: status.sessionId, runtimePid: status.runtimePid, runtimePromptMessageId: status.runtimePromptMessageId, prePromptCursor: status.prePromptCursor, diagnostics, }; } async observeOpenCodeTeamMessageDelivery( input: OpenCodeObserveMessageDeliveryCommandBody ): Promise { const result = await this.bridge.execute< OpenCodeObserveMessageDeliveryCommandBody, OpenCodeObserveMessageDeliveryCommandData >('opencode.observeMessageDelivery', input, { cwd: input.projectPath, timeoutMs: this.options.observeTimeoutMs ?? DEFAULT_OBSERVE_TIMEOUT_MS, }); if (result.ok) { return result.data; } return { observed: false, memberName: input.memberName, responseObservation: { state: 'reconcile_failed', deliveredUserMessageId: null, assistantMessageId: null, toolCallNames: [], visibleMessageToolCallId: null, visibleReplyMessageId: null, visibleReplyCorrelation: null, latestAssistantPreview: null, reason: result.error.message, }, diagnostics: [ { code: result.error.kind, severity: 'error', message: `OpenCode message delivery observe bridge failed: ${result.error.message}`, }, ...result.diagnostics.map((event) => ({ code: event.type, severity: event.severity, message: event.message, })), ], }; } async backfillOpenCodeTaskLedger( input: OpenCodeBackfillTaskLedgerCommandBody ): Promise { const cwd = input.workspaceRoot ?? input.projectDir ?? process.cwd(); const result = await this.bridge.execute< OpenCodeBackfillTaskLedgerCommandBody, OpenCodeBackfillTaskLedgerCommandData >('opencode.backfillTaskLedger', input, { cwd, timeoutMs: DEFAULT_BACKFILL_TIMEOUT_MS, stdoutLimitBytes: 2_000_000, stderrLimitBytes: 512_000, }); if (result.ok) { return result.data; } return { schemaVersion: 1, providerId: 'opencode', teamName: input.teamName, ...(input.taskId ? { taskId: input.taskId } : {}), ...(input.projectDir ? { projectDir: input.projectDir } : {}), ...(input.workspaceRoot ? { workspaceRoot: input.workspaceRoot } : {}), dryRun: input.dryRun === true, ...(input.attributionMode ? { attributionMode: input.attributionMode } : {}), scannedSessions: 0, scannedToolparts: 0, candidateEvents: 0, importedEvents: 0, skippedEvents: 0, outcome: result.error.retryable ? 'transient-error' : 'unsafe-input', notices: [], diagnostics: [ `OpenCode task ledger backfill bridge failed: ${result.error.kind}: ${result.error.message}`, ...result.diagnostics.map(formatDiagnosticEvent), ], }; } private async executeStateChangingCommand( command: OpenCodeStateChangingTeamCommandName, body: TBody, input: { teamName: string; laneId: string; runId: string; capabilitySnapshotId: string | null; cwd: string; timeoutMs: number; } ): Promise> { if (this.options.stateChangingCommands) { try { return await this.options.stateChangingCommands.execute({ command, teamName: input.teamName, laneId: input.laneId, runId: input.runId, capabilitySnapshotId: input.capabilitySnapshotId, behaviorFingerprint: null, body, cwd: input.cwd, timeoutMs: input.timeoutMs, }); } catch (error) { return thrownBridgeFailure(command, input.runId, error); } } return this.bridge.execute(command, body, { cwd: input.cwd, timeoutMs: input.timeoutMs, }); } } type OpenCodeStateChangingTeamCommandName = Extract< OpenCodeBridgeCommandName, | 'opencode.launchTeam' | 'opencode.reconcileTeam' | 'opencode.stopTeam' | 'opencode.sendMessage' | 'opencode.answerPermission' >; function blockedLaunchData( runId: string, result: OpenCodeBridgeResult ): OpenCodeLaunchTeamCommandData { if (result.ok) { throw new Error('blockedLaunchData expects a failed bridge result'); } return { runId, teamLaunchState: 'failed', members: {}, warnings: [], diagnostics: [ { code: result.error.kind, severity: 'error', message: `OpenCode bridge failed: ${result.error.message}`, }, ...result.diagnostics.map((event) => ({ code: event.type, severity: event.severity, message: event.message, })), ], }; } function blockedReadiness(input: { state: OpenCodeTeamLaunchReadinessState; modelId: string | null; diagnostics: string[]; missing: string[]; supportDiagnostics?: OpenCodeTeamLaunchReadiness['supportDiagnostics']; }): OpenCodeTeamLaunchReadiness { return { state: input.state, launchAllowed: false, modelId: input.modelId, availableModels: [], opencodeVersion: null, installMethod: null, binaryPath: null, hostHealthy: false, appMcpConnected: false, requiredToolsPresent: false, permissionBridgeReady: false, runtimeStoresReady: false, supportLevel: null, missing: dedupe(input.missing), diagnostics: dedupe(input.diagnostics), ...(input.supportDiagnostics?.length ? { supportDiagnostics: [...input.supportDiagnostics] } : {}), evidence: { capabilitiesReady: false, mcpToolProofRoute: null, observedMcpTools: [], runtimeStoreReadinessReason: null, }, }; } function mapBridgeFailureToReadinessState( kind: OpenCodeBridgeFailureKind ): OpenCodeTeamLaunchReadinessState { switch (kind) { case 'runtime_not_ready': return 'adapter_disabled'; case 'timeout': case 'contract_violation': case 'provider_error': case 'unsupported_schema': case 'unsupported_command': case 'invalid_input': case 'internal_error': default: return 'unknown_error'; } } function formatDiagnosticEvent(event: OpenCodeBridgeDiagnosticEvent): string { return `${event.type}: ${event.message}`; } function isOpenCodeAcceptanceContractMissingError(error: unknown): boolean { const message = error instanceof Error ? error.message : String(error); return message.includes('OpenCode delivery acceptance mode is required'); } function buildOpenCodeForceSessionRefreshUnsupportedData( body: OpenCodeSendMessageCommandBody, error: unknown ): OpenCodeSendMessageCommandData { const detail = error instanceof Error ? error.message : String(error); const reason = `OpenCode forced session refresh requires delivery acceptance contract version ${OPEN_CODE_DELIVERY_ACCEPTANCE_CONTRACT_VERSION}. Update agent_teams_orchestrator and restart the app.`; return { accepted: false, memberName: body.memberName, responseObservation: { state: 'session_stale', deliveredUserMessageId: null, assistantMessageId: null, toolCallNames: [], visibleMessageToolCallId: null, visibleReplyMessageId: null, visibleReplyCorrelation: null, latestAssistantPreview: null, reason, }, diagnostics: [ { code: 'opencode_force_session_refresh_contract_missing', severity: 'error', message: `${reason} ${detail}`, }, ], }; } function isOpenCodeCompletedCommandRecoveryError(error: unknown): boolean { const message = error instanceof Error ? error.message : String(error); return message.includes(OPEN_CODE_COMPLETED_COMMAND_RECOVERY_MESSAGE); } function withOpenCodeObservedFallbackDiagnostic( data: OpenCodeSendMessageCommandData ): OpenCodeSendMessageCommandData { return { ...data, diagnostics: [ { code: 'opencode_accept_fast_capability_missing', severity: 'warning', message: 'OpenCode delivery acceptance capability was not advertised by the orchestrator; used observed delivery mode.', }, ...data.diagnostics, ], }; } function thrownBridgeFailure( command: OpenCodeBridgeCommandName, runId: string, error: unknown ): OpenCodeBridgeResult { const message = error instanceof Error ? error.message : String(error); const completedAt = new Date().toISOString(); return { ok: false, schemaVersion: 1, requestId: 'opencode-state-changing-bridge-exception', command, completedAt, durationMs: 0, error: { kind: 'internal_error', message, retryable: false, }, diagnostics: [ { type: 'opencode_state_changing_bridge_exception', providerId: 'opencode', runId, severity: 'error', message, createdAt: completedAt, }, ], }; } function dedupe(values: string[]): string[] { return [...new Set(values.filter((value) => value.trim().length > 0))]; }