( result: OrchestratorResult, publisher: StreamWriter, runId: string, outcome: RequestTraceV1Outcome, requestId: string )
| 22 | // Single finalization path. `outcome` is the caller's resolved verdict |
| 23 | // so we don't have to re-derive cancel vs error from raw signals. |
| 24 | export async function finalizeStream( |
| 25 | result: OrchestratorResult, |
| 26 | publisher: StreamWriter, |
| 27 | runId: string, |
| 28 | outcome: RequestTraceV1Outcome, |
| 29 | requestId: string |
| 30 | ): Promise<void> { |
| 31 | const spanOutcome = |
| 32 | outcome === RequestTraceV1OutcomeConst.cancelled |
| 33 | ? CopilotFinalizeOutcome.Aborted |
| 34 | : outcome === RequestTraceV1OutcomeConst.success |
| 35 | ? CopilotFinalizeOutcome.Success |
| 36 | : CopilotFinalizeOutcome.Error |
| 37 | const span = getTracer().startSpan(TraceSpan.CopilotFinalizeStream, { |
| 38 | attributes: { |
| 39 | [TraceAttr.CopilotFinalizeOutcome]: spanOutcome, |
| 40 | [TraceAttr.RunId]: runId, |
| 41 | [TraceAttr.RequestId]: requestId, |
| 42 | [TraceAttr.CopilotResultToolCalls]: result.toolCalls?.length ?? 0, |
| 43 | [TraceAttr.CopilotResultContentBlocks]: result.contentBlocks?.length ?? 0, |
| 44 | [TraceAttr.CopilotResultContentLength]: result.content?.length ?? 0, |
| 45 | [TraceAttr.CopilotPublisherSawComplete]: publisher.sawComplete, |
| 46 | [TraceAttr.CopilotPublisherClientDisconnected]: publisher.clientDisconnected, |
| 47 | }, |
| 48 | }) |
| 49 | try { |
| 50 | if (outcome === RequestTraceV1OutcomeConst.cancelled) { |
| 51 | await handleAborted(result, publisher, runId, requestId) |
| 52 | } else if (outcome === RequestTraceV1OutcomeConst.error) { |
| 53 | span.setStatus({ |
| 54 | code: SpanStatusCode.ERROR, |
| 55 | message: result.error || 'orchestration failed', |
| 56 | }) |
| 57 | await handleError(result, publisher, runId, requestId) |
| 58 | } else { |
| 59 | await handleSuccess(publisher, runId, requestId) |
| 60 | } |
| 61 | // Successful + cancelled paths fall through as status-unset → set |
| 62 | // OK so dashboards don't show "incomplete" for normal terminals. |
| 63 | if (outcome !== RequestTraceV1OutcomeConst.error) { |
| 64 | span.setStatus({ code: SpanStatusCode.OK }) |
| 65 | } |
| 66 | } catch (error) { |
| 67 | span.recordException(toError(error)) |
| 68 | span.setStatus({ code: SpanStatusCode.ERROR, message: 'finalize threw' }) |
| 69 | throw error |
| 70 | } finally { |
| 71 | span.end() |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | async function handleAborted( |
| 76 | result: OrchestratorResult, |
no test coverage detected