import type { StreamFn } from "@mariozechner/pi-agent-core"; import type { ProviderWrapStreamFnContext } from "openclaw/plugin-sdk/plugin-entry"; import { applyAnthropicEphemeralCacheControlMarkers, buildCopilotDynamicHeaders, hasCopilotVisionInput, streamWithPayloadPatch, } from "openclaw/plugin-sdk/provider-stream-shared"; import { rewriteCopilotResponsePayloadConnectionBoundIds } from "./connection-bound-ids.js"; type _StreamContext = Parameters[1]; type StreamOptions = Parameters[2]; function patchOnPayloadResult(result: unknown): unknown { if (result && typeof result === "object" && "then" in result) { return Promise.resolve(result).then((next) => { rewriteCopilotResponsePayloadConnectionBoundIds(next); return next; }); } rewriteCopilotResponsePayloadConnectionBoundIds(result); return result; } function buildCopilotRequestHeaders( context: Parameters[1], headers: Record | undefined, ): Record { return { ...buildCopilotDynamicHeaders({ messages: context.messages, hasImages: hasCopilotVisionInput(context.messages), }), ...headers, }; } export function wrapCopilotAnthropicStream( baseStreamFn: StreamFn | undefined, ): StreamFn | undefined { if (!baseStreamFn) { return undefined; } const underlying = baseStreamFn; return (model, context, options) => { if (model.provider !== "github-copilot" || model.api !== "anthropic-messages") { return underlying(model, context, options); } return streamWithPayloadPatch( underlying, model, context, { ...options, headers: buildCopilotRequestHeaders(context, options?.headers), }, applyAnthropicEphemeralCacheControlMarkers, ); }; } export function wrapCopilotOpenAIResponsesStream( baseStreamFn: StreamFn | undefined, ): StreamFn | undefined { if (!baseStreamFn) { return undefined; } const underlying = baseStreamFn; return (model, context, options) => { if (model.provider !== "github-copilot" || model.api !== "openai-responses") { return underlying(model, context, options); } const originalOnPayload = options?.onPayload; const wrappedOptions: StreamOptions = { ...options, headers: buildCopilotRequestHeaders(context, options?.headers), onPayload: (payload, payloadModel) => { rewriteCopilotResponsePayloadConnectionBoundIds(payload); return patchOnPayloadResult(originalOnPayload?.(payload, payloadModel)); }, }; return underlying(model, context, wrappedOptions); }; } export function wrapCopilotProviderStream(ctx: ProviderWrapStreamFnContext): StreamFn | undefined { return wrapCopilotOpenAIResponsesStream(wrapCopilotAnthropicStream(ctx.streamFn)); }