( result: OrchestratorResult, publisher: StreamWriter, runId: string, requestId: string )
| 105 | } |
| 106 | |
| 107 | async function handleError( |
| 108 | result: OrchestratorResult, |
| 109 | publisher: StreamWriter, |
| 110 | runId: string, |
| 111 | requestId: string |
| 112 | ): Promise<void> { |
| 113 | const errorMessage = |
| 114 | result.error || |
| 115 | result.errors?.[0] || |
| 116 | 'An unexpected error occurred while processing the response.' |
| 117 | |
| 118 | // Persist whatever was generated before the failure, exactly like an abort — |
| 119 | // a transient provider error (e.g. overloaded) shouldn't discard the partial |
| 120 | // assistant output the user already saw streaming. |
| 121 | const partialContent = result.content || undefined |
| 122 | const partialContentLen = result.content?.length ?? 0 |
| 123 | const toolCallCount = result.toolCalls?.length ?? 0 |
| 124 | |
| 125 | if (publisher.clientDisconnected) { |
| 126 | logger.info(`[${requestId}] Stream failed after client disconnect`, { error: errorMessage }) |
| 127 | } |
| 128 | logger.error(`[${requestId}] Orchestration returned failure`, { |
| 129 | error: errorMessage, |
| 130 | partialContentLen, |
| 131 | toolCallCount, |
| 132 | }) |
| 133 | |
| 134 | // Surface the real error (Go already classifies provider errors like |
| 135 | // "overloaded" into a friendly displayMessage). Don't clobber it with a |
| 136 | // generic string. |
| 137 | await publisher.publish({ |
| 138 | type: MothershipStreamV1EventType.error, |
| 139 | payload: { |
| 140 | message: errorMessage, |
| 141 | error: errorMessage, |
| 142 | displayMessage: errorMessage, |
| 143 | data: { displayMessage: errorMessage }, |
| 144 | }, |
| 145 | }) |
| 146 | if (!publisher.sawComplete) { |
| 147 | await publisher.publish({ |
| 148 | type: MothershipStreamV1EventType.complete, |
| 149 | payload: { |
| 150 | status: MothershipStreamV1CompletionStatus.error, |
| 151 | ...(partialContent ? { partialContent } : {}), |
| 152 | ...(partialContentLen ? { partialContentLen } : {}), |
| 153 | ...(toolCallCount ? { toolCallCount } : {}), |
| 154 | }, |
| 155 | }) |
| 156 | } |
| 157 | await publisher.flush() |
| 158 | await loggedRunStatusUpdate(runId, MothershipStreamV1CompletionStatus.error, requestId, { |
| 159 | completedAt: new Date(), |
| 160 | error: errorMessage, |
| 161 | }) |
| 162 | } |
| 163 | |
| 164 | async function handleSuccess( |
no test coverage detected