UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

796 lines (795 loc) • 33.7 kB
import { t as hasNonEmptyString } from "./string-coerce-1VuiLaaR.mjs"; import { p as clampPositiveTimerTimeoutMs } from "./number-coercion-CuVhdCBs.mjs"; import { n as captureAsyncWorkTracker } from "./async-work-scope-BYeX3gh6.mjs"; import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.mjs"; import { O as freezeDiagnosticTraceContext, S as formatPropagatedDiagnosticTraceparent, t as areDiagnosticsEnabledForProcess, w as createChildDiagnosticTraceContext } from "./diagnostic-events-BADdmgZf.mjs"; import { t as createSubsystemLogger } from "./subsystem-I-uqdx4f.mjs"; import { t as isPromiseLike } from "./promise-like-D7-l5Fsp.mjs"; import { n as emitDiagnosticsTimelineEvent } from "./diagnostics-timeline-DxQ8TaVy.mjs"; import { t as settlesWithin } from "./settle-within-CTpJLoBG.mjs"; import { t as getGlobalHookRunner } from "./hook-runner-global-BfCvU9TT.mjs"; import { f as fireAndForgetBoundedHook } from "./hooks-CM9Qxrxp.mjs"; import { s as getStreamLlmRuntime } from "./model-runtime-binding-DgnrsJj9.mjs"; import { r as derivePromptTokens, u as normalizeUsage } from "./usage-XmAH1cfG.mjs"; import { C as emitCoreModelRequestStartedDiagnosticEvent, S as emitCoreModelRequestEndedDiagnosticEvent, x as emitCoreSemanticRunProgressDiagnosticEvent } from "./diagnostic-run-activity-BlCpLag4.mjs"; import { a as diagnosticProviderRequestIdHash, i as diagnosticHttpStatusCode, n as diagnosticErrorFailureKind, t as diagnosticErrorCategory } from "./diagnostic-error-metadata-BSgZdc_o.mjs"; import { t as cloneDiagnosticContentValue } from "./diagnostic-llm-content-P2SGagoP.mjs"; import { a as createAnthropicVertexStreamFnForModel } from "./ai-transport-runtime-host-BiT734Cc.mjs"; import { r as supportsOpenAIResponsesFastMode } from "./openai-fast-mode-B8-SEg6R.mjs"; import { t as createModelCallStreamProgressReporter } from "./diagnostic-model-stream-progress-CcHaqwpX.mjs"; import { stripSystemPromptCacheBoundary } from "@openclaw/ai/internal/shared"; import { createBoundaryAwareStreamFnForModel, withProviderAcceptanceObserver } from "@openclaw/ai/transports"; import { modelRequestBodyState, responsesServiceTierObserver } from "@openclaw/ai/internal/openai"; import { notifyLlmRequestActivity, onLlmRequestActivity } from "@openclaw/ai/internal/runtime"; //#region src/agents/embedded-agent-runner/stream-resolution.ts const embeddedAgentBaseStreamFnCache = /* @__PURE__ */ new WeakMap(); function resolveEmbeddedAgentBaseStreamFn(params) { if (!embeddedAgentBaseStreamFnCache.has(params.session)) embeddedAgentBaseStreamFnCache.set(params.session, params.session.agent.streamFn); const baseStreamFn = embeddedAgentBaseStreamFnCache.get(params.session); if (!baseStreamFn) throw new Error("Agent session has no lifecycle-owned base stream."); return baseStreamFn; } function resolveEmbeddedStreamRuntime(owner) { const runtime = owner.llmRuntime ?? getStreamLlmRuntime(owner.currentStreamFn); if (!runtime) throw new Error("Embedded stream has no lifecycle runtime owner."); return runtime; } function isDefaultOpenClawStreamFnForModel(model, streamFn, llmRuntime) { if (!streamFn || streamFn === llmRuntime.streamSimple) return true; const api = typeof model.api === "string" ? model.api.trim() : ""; if (!api) return false; const provider = llmRuntime.registry.getApiProvider(api); return streamFn === provider?.streamSimple || streamFn === provider?.stream; } async function resolveEmbeddedAgentApiKey(params) { const resolvedApiKey = params.resolvedApiKey?.trim(); if (resolvedApiKey) return resolvedApiKey; return params.authStorage ? await params.authStorage.getApiKey(params.provider) : void 0; } function resolveEmbeddedAgentStream(params) { const { streamFn, strategy, wrapApiKey } = selectEmbeddedAgentStream(params); return { streamFn: wrapApiKey(streamFn), strategy }; } /** * Selects the embedded stream and returns its run-credential wrapper separately. * Callers that compose provider wrappers apply wrapApiKey outside them, because * those wrappers classify auth from options.apiKey. */ function selectEmbeddedAgentStream(params) { const llmRuntime = resolveEmbeddedStreamRuntime(params); const wrapOptions = { runSignal: params.signal, authProfileId: params.authProfileId, promptCacheKey: params.promptCacheKey, assertCurrent: params.assertCurrent }; const wrapRunApiKey = (streamFn) => wrapEmbeddedAgentStreamApiKey(streamFn, { providerId: params.model.provider, resolvedApiKey: params.resolvedApiKey, authStorage: params.authStorage, assertCurrent: params.assertCurrent }); const keepStreamAuth = (streamFn) => streamFn; const stripCacheBoundary = (context) => context.systemPrompt ? { ...context, systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt) } : context; if (params.providerStreamFn) return { streamFn: wrapEmbeddedAgentStreamFn(params.providerStreamFn, wrapOptions), strategy: "provider", wrapApiKey: wrapRunApiKey }; const currentStreamFn = params.currentStreamFn ?? llmRuntime.streamSimple; if (params.model.provider === "anthropic-vertex") { const vertexStreamFn = createAnthropicVertexStreamFnForModel(params.model); return { streamFn: params.signal || params.assertCurrent ? wrapEmbeddedAgentStreamFn(vertexStreamFn, { runSignal: params.signal, assertCurrent: params.assertCurrent }) : vertexStreamFn, strategy: "anthropic-vertex", wrapApiKey: keepStreamAuth }; } if (params.model.provider === "openai" && params.model.api === "openai-chatgpt-responses" && (isDefaultOpenClawStreamFnForModel(params.model, params.currentStreamFn, llmRuntime) || getStreamLlmRuntime(params.currentStreamFn) === llmRuntime)) return { streamFn: wrapEmbeddedAgentStreamFn(currentStreamFn, { ...wrapOptions, sessionId: params.sessionId, transformContext: stripCacheBoundary }), strategy: "openclaw-native-codex-responses", wrapApiKey: wrapRunApiKey }; const isDefault = isDefaultOpenClawStreamFnForModel(params.model, params.currentStreamFn, llmRuntime); if (isDefault || hasNonEmptyString(params.resolvedApiKey) || params.transportAuthAvailable || params.model.api === "anthropic-messages" && params.model.provider !== "anthropic") { const boundaryAwareStreamFn = createBoundaryAwareStreamFnForModel(params.model); if (boundaryAwareStreamFn) return { streamFn: wrapEmbeddedAgentStreamFn(boundaryAwareStreamFn, { ...wrapOptions, sessionId: params.sessionId }), strategy: `boundary-aware:${params.model.api}`, wrapApiKey: wrapRunApiKey }; } const promptCacheKey = params.promptCacheKey?.trim(); return { streamFn: !promptCacheKey && !params.signal && !params.assertCurrent ? currentStreamFn : wrapEmbeddedAgentStreamFn(currentStreamFn, { runSignal: params.signal, promptCacheKey, assertCurrent: params.assertCurrent }), strategy: isDefault ? "stream-simple" : "session-custom", wrapApiKey: keepStreamAuth }; } /** Preserve request activity across cancellation composition without retaining completed turns. */ function composeRunSignal(callerSignal, runSignal) { const composedSignal = AbortSignal.any([callerSignal, runSignal]); onLlmRequestActivity(composedSignal, () => { if (!composedSignal.aborted) notifyLlmRequestActivity(callerSignal); }); return composedSignal; } function wrapEmbeddedAgentStreamFn(inner, params) { const transformContext = params.transformContext ?? ((context) => context); const mergeRunSignal = (options) => { const embeddedOptions = options; const callerSignal = embeddedOptions?.signal; const signal = callerSignal && params.runSignal && callerSignal !== params.runSignal ? composeRunSignal(callerSignal, params.runSignal) : callerSignal ?? params.runSignal; let merged = params.sessionId && !embeddedOptions?.sessionId ? { ...embeddedOptions, sessionId: params.sessionId } : embeddedOptions; const promptCacheKey = params.promptCacheKey?.trim(); if (promptCacheKey && !merged?.promptCacheKey) merged = { ...merged, promptCacheKey }; if (params.authProfileId && !merged?.authProfileId) merged = { ...merged, authProfileId: params.authProfileId }; return signal ? { ...merged, signal } : merged; }; return (m, context, options) => { params.assertCurrent?.(); return inner(m, transformContext(context), mergeRunSignal(options)); }; } /** Resolve the run credential for each request and pass it to every inner wrapper. */ function wrapEmbeddedAgentStreamApiKey(inner, params) { if (!params.authStorage && !params.resolvedApiKey) return inner; const { authStorage, providerId, resolvedApiKey } = params; return async (m, context, options) => { params.assertCurrent?.(); const apiKey = await resolveEmbeddedAgentApiKey({ provider: providerId, resolvedApiKey, authStorage }); params.assertCurrent?.(); return inner(m, context, { ...options, apiKey: apiKey ?? options?.apiKey }); }; } //#endregion //#region src/llm/providers/stream-wrappers/openai-service-tier-observation.ts const log = createSubsystemLogger("llm/providers/stream-wrappers"); /** Carry a provider echo back to the selected account's catalog observation owner. */ function createOpenAIServiceTierObservationWrapper(underlying, recordDowngrade) { return (model, context, options) => { if (model.api !== "openai-responses" || !supportsOpenAIResponsesFastMode(model)) return underlying(model, context, options); const observedOptions = { ...options }; const previous = options && responsesServiceTierObserver.get(options); responsesServiceTierObserver.set(observedOptions, (observation) => { previous?.(observation); if (observation.requestedTier === "ultrafast" && observation.responseTier !== "ultrafast" && recordDowngrade(model)) log.info("OpenAI downgraded an Ultrafast request; the account's model route now offers Fast."); }); return underlying(model, context, observedOptions); }; } //#endregion //#region src/agents/embedded-agent-runner/run/attempt.model-diagnostic-lifecycle.ts const TRACEPARENT_HEADER_NAME = "traceparent"; const TIMELINE_ATTRIBUTE_MAX_LENGTH = 256; function modelContentPrivateData(modelContent) { return modelContent ? { modelContent } : void 0; } function boundedTimelineAttribute(value) { return truncateUtf16Safe(value?.trim() ?? "", TIMELINE_ATTRIBUTE_MAX_LENGTH) || void 0; } function emitProviderRequestTimelineEvent(eventBase, startedAt, durationMs, ok, responseStatus, state, terminalAtMs, terminalReason, config) { const { providerAcceptanceKind } = state; const provider = boundedTimelineAttribute(eventBase.provider); const model = boundedTimelineAttribute(eventBase.model); const api = boundedTimelineAttribute(eventBase.api); const transport = boundedTimelineAttribute(eventBase.transport); emitDiagnosticsTimelineEvent({ type: "provider.request", name: "provider.request", timestamp: new Date(startedAt).toISOString(), runId: eventBase.runId, spanId: eventBase.callId, durationMs, provider, operation: api ?? transport ?? "model.call", ok, ...responseStatus !== void 0 ? { status: responseStatus } : {}, attributes: { ...model ? { model } : {}, ...api ? { api } : {}, ...transport ? { transport } : {}, terminalAtMs, ...state.lastProviderActivityAtMs !== void 0 ? { lastProviderActivityAtMs: state.lastProviderActivityAtMs } : {}, terminalReason, providerAccepted: providerAcceptanceKind !== void 0, ...providerAcceptanceKind ? { providerAcceptanceKind } : {} } }, { config }); } function modelCallErrorFields(err) { const upstreamRequestIdHash = diagnosticProviderRequestIdHash(err); const failureKind = diagnosticErrorFailureKind(err); return { errorCategory: diagnosticErrorCategory(err), ...failureKind ? { failureKind, memory: processMemoryUsageSnapshot() } : {}, ...upstreamRequestIdHash ? { upstreamRequestIdHash } : {} }; } function processMemoryUsageSnapshot() { try { const memory = process.memoryUsage(); return { rssBytes: memory.rss, heapTotalBytes: memory.heapTotal, heapUsedBytes: memory.heapUsed, externalBytes: memory.external, arrayBuffersBytes: memory.arrayBuffers }; } catch { return; } } function modelCallHookEventBase(eventBase) { return { runId: eventBase.runId, callId: eventBase.callId, ...eventBase.sessionKey ? { sessionKey: eventBase.sessionKey } : {}, ...eventBase.sessionId ? { sessionId: eventBase.sessionId } : {}, provider: eventBase.provider, model: eventBase.model, ...eventBase.api ? { api: eventBase.api } : {}, ...eventBase.transport ? { transport: eventBase.transport } : {}, ...eventBase.contextTokenBudget ? { contextTokenBudget: eventBase.contextTokenBudget } : {}, ...eventBase.contextWindowSource ? { contextWindowSource: eventBase.contextWindowSource } : {}, ...eventBase.contextWindowReferenceTokens ? { contextWindowReferenceTokens: eventBase.contextWindowReferenceTokens } : {} }; } function modelCallHookContext(eventBase) { return Object.freeze({ runId: eventBase.runId, ...eventBase.agentId ? { agentId: eventBase.agentId } : {}, trace: eventBase.trace, ...eventBase.sessionKey ? { sessionKey: eventBase.sessionKey } : {}, ...eventBase.sessionId ? { sessionId: eventBase.sessionId } : {}, modelProviderId: eventBase.provider, modelId: eventBase.model, ...eventBase.contextTokenBudget ? { contextTokenBudget: eventBase.contextTokenBudget } : {}, ...eventBase.contextWindowSource ? { contextWindowSource: eventBase.contextWindowSource } : {}, ...eventBase.contextWindowReferenceTokens ? { contextWindowReferenceTokens: eventBase.contextWindowReferenceTokens } : {} }); } function dispatchModelCallHook(eventBase, fields) { const hookRunner = getGlobalHookRunner(); const hookName = fields ? "model_call_ended" : "model_call_started"; if (!hookRunner?.hasHooks(hookName)) return; const event = Object.freeze(modelCallHookEventBase(eventBase)); const hookCtx = modelCallHookContext(eventBase); fireAndForgetBoundedHook(() => fields ? hookRunner.runModelCallEnded(Object.freeze({ ...event, ...fields }), hookCtx) : hookRunner.runModelCallStarted(event, hookCtx), `${hookName} plugin hook failed`); } function emitModelCallEnded(eventBase, startedAt, observer, failure, ownerGeneration, config) { if (observer.state.terminalEventEmitted) return; observer.state.terminalEventEmitted = true; const terminalAtMs = Date.now(); const durationMs = terminalAtMs - startedAt; const sizeTimingFields = observer.sizeTimingFields(); const fields = failure ? modelCallErrorFields(failure.error) : void 0; const terminal = fields ? { type: "model.call.error", ...fields } : { type: "model.call.completed" }; const errorStatus = failure ? diagnosticHttpStatusCode(failure.error) : void 0; const responseStatus = observer.state.responseStatus ?? (errorStatus === void 0 ? void 0 : Number(errorStatus)); emitProviderRequestTimelineEvent(eventBase, startedAt, durationMs, failure === void 0, responseStatus, observer.state, terminalAtMs, failure ? observer.state.terminalReason === "aborted" ? "aborted" : fields?.failureKind ?? "error" : observer.state.terminalReason ?? "unknown", config); emitCoreModelRequestEndedDiagnosticEvent({ ...terminal, ...eventBase, durationMs, ...sizeTimingFields, ...observer.usageField() }, ownerGeneration, modelContentPrivateData(observer.completedContent())); if (!observer.state.suppressPluginHooks) dispatchModelCallHook(eventBase, { durationMs, outcome: failure ? "error" : "completed", ...sizeTimingFields, ...fields }); } function withDiagnosticRequestContext(options, trace, observer, callId) { const traceparent = formatPropagatedDiagnosticTraceparent(trace); const originalOnPayload = options?.onPayload; const originalOnResponse = options?.onResponse; const onPayload = (payload, model) => { if (modelRequestBodyState(requestOptions).enabled) return originalOnPayload?.(payload, model); if (!originalOnPayload) { observer.assignRequestPayloadBytes(payload); return; } const result = originalOnPayload(payload, model); if (isPromiseLike(result)) return result.then((replacement) => { observer.assignRequestPayloadBytes(replacement ?? payload); return replacement; }); observer.assignRequestPayloadBytes(result ?? payload); return result; }; const onResponse = (response, model) => { if (!observer.state.terminalEventEmitted) { observer.state.responseStatus = response.status; observer.state.lastProviderActivityAtMs = Date.now(); } return originalOnResponse?.(response, model); }; const headers = {}; for (const [key, value] of Object.entries(options?.headers ?? {})) { if (key.toLowerCase() === TRACEPARENT_HEADER_NAME) continue; headers[key] = value; } if (traceparent) headers[TRACEPARENT_HEADER_NAME] = traceparent; const requestOptions = { ...options, requestId: callId, ...(options?.headers || traceparent) && { headers }, onPayload, onResponse }; modelRequestBodyState(requestOptions).onBytes = (bytes) => { observer.state.requestPayloadBytes = bytes; }; return withProviderAcceptanceObserver(requestOptions, (acceptance) => { if (observer.state.terminalEventEmitted) return; observer.state.lastProviderActivityAtMs = Date.now(); observer.state.providerAcceptanceKind = acceptance.kind; if (acceptance.kind === "http_response") observer.state.responseStatus = acceptance.status; }); } function createModelLifecycle(params) { const callId = params.ctx.nextCallId(); const trace = freezeDiagnosticTraceContext(createChildDiagnosticTraceContext(params.ctx.trace)); const observer = params.createObserver(areDiagnosticsEnabledForProcess()); const eventBase = { ...modelCallHookEventBase({ ...params.ctx, callId }), ...params.ctx.agentId ? { agentId: params.ctx.agentId } : {}, observationUnit: "request", ...observer.promptStats ? { promptStats: observer.promptStats } : {}, trace }; emitCoreModelRequestStartedDiagnosticEvent(eventBase, params.ctx.ownerGeneration, params.requestTimeoutMs, modelContentPrivateData(observer.modelContent)); if (params.ctx.suppressPluginHooks !== true) dispatchModelCallHook(eventBase); params.ctx.onStarted?.(); const startedAt = Date.now(); emitDiagnosticsTimelineEvent({ type: "mark", name: "provider.request.started", timestamp: new Date(startedAt).toISOString(), runId: eventBase.runId, spanId: callId }, { config: params.ctx.config }); const propagatedOptions = withDiagnosticRequestContext(params.options, trace, observer, callId); let terminalNotified = false; return { eventBase, observer, propagatedOptions, startedAt, emitCompleted() { if (!terminalNotified && (observer.state.terminalSucceeded || observer.state.terminalError)) { terminalNotified = true; params.ctx.onTerminal?.(); if (observer.state.terminalSucceeded && !observer.state.terminalError) params.ctx.onSucceeded?.(startedAt); } emitModelCallEnded(eventBase, startedAt, observer, observer.state.terminalError ? { error: observer.state.terminalError } : void 0, params.ctx.ownerGeneration, params.ctx.config); }, emitError(err) { emitModelCallEnded(eventBase, startedAt, observer, { error: err }, params.ctx.ownerGeneration, params.ctx.config); } }; } //#endregion //#region src/agents/embedded-agent-runner/run/attempt.model-diagnostic-observation.ts const MODEL_CALL_SEMANTIC_PROGRESS_REASON = "model_call:semantic_result"; function jsonLength(value, utf8, promptStringLengths) { try { let stringLengths = 0; const serialized = JSON.stringify(value, function(key, part) { if (typeof part !== "string" || part.length < 4096) return part; const cached = !utf8 ? promptStringLengths?.previous.get(this)?.get(key) : void 0; let chars = cached?.text === part ? cached.chars : void 0; if (chars === void 0) { const encoded = JSON.stringify(part); chars = (utf8 ? Buffer.byteLength(encoded, "utf8") : encoded.length) - 2; } if (!utf8 && promptStringLengths) { const fields = promptStringLengths.current.get(this) ?? /* @__PURE__ */ new Map(); fields.set(key, { text: part, chars }); promptStringLengths.current.set(this, fields); } stringLengths += chars; return ""; }); return serialized === void 0 ? void 0 : stringLengths + (utf8 ? Buffer.byteLength(serialized, "utf8") : serialized.length); } catch { return; } } function responseStreamChunkByteLength(chunk) { try { if (!isRecord(chunk)) return jsonLength(chunk, true); const type = chunk.type; if ((type === "text_delta" || type === "thinking_delta" || type === "toolcall_delta") && typeof chunk.delta === "string") return Buffer.byteLength(chunk.delta, "utf8"); if (!("partial" in chunk)) return jsonLength(chunk, true); const { partial: _partial, ...snapshotlessChunk } = chunk; return jsonLength(snapshotlessChunk, true); } catch { return; } } function streamContextModelContentFields(policy, streamContext) { if (!policy?.anyModelContent || !isRecord(streamContext)) return; const content = { ...policy.inputMessages && Array.isArray(streamContext.messages) ? { inputMessages: cloneDiagnosticContentValue(streamContext.messages) } : {}, ...policy.systemPrompt && typeof streamContext.systemPrompt === "string" ? { systemPrompt: streamContext.systemPrompt } : {}, ...policy.toolDefinitions && Array.isArray(streamContext.tools) ? { toolDefinitions: cloneDiagnosticContentValue(streamContext.tools) } : {} }; return Object.keys(content).length > 0 ? content : void 0; } function createModelPromptStats() { let previous = /* @__PURE__ */ new WeakMap(); return (streamContext) => { const current = /* @__PURE__ */ new WeakMap(); const stats = streamContextModelPromptStats(streamContext, { previous, current }); previous = current; return stats; }; } function streamContextModelPromptStats(streamContext, promptStringLengths) { if (!isRecord(streamContext)) return; const messages = Array.isArray(streamContext.messages) ? streamContext.messages : void 0; const tools = Array.isArray(streamContext.tools) ? streamContext.tools : void 0; const systemPrompt = typeof streamContext.systemPrompt === "string" ? streamContext.systemPrompt : void 0; const inputMessagesChars = messages ? jsonLength(messages, false, promptStringLengths) : void 0; const toolDefinitionsChars = tools ? jsonLength(tools, false, promptStringLengths) : void 0; const systemPromptChars = systemPrompt?.length; if (messages === void 0 && tools === void 0 && systemPrompt === void 0) return; const totalChars = (inputMessagesChars ?? 0) + (systemPromptChars ?? 0) + (toolDefinitionsChars ?? 0); return { ...messages ? { inputMessagesCount: messages.length } : {}, ...inputMessagesChars !== void 0 ? { inputMessagesChars } : {}, ...systemPromptChars !== void 0 ? { systemPromptChars } : {}, ...tools ? { toolDefinitionsCount: tools.length } : {}, ...toolDefinitionsChars !== void 0 ? { toolDefinitionsChars } : {}, totalChars }; } function normalizedModelCallUsage(rawUsage) { if (!isRecord(rawUsage)) return; const usage = normalizeUsage(rawUsage); if (!usage) return; const promptTokens = derivePromptTokens(usage); return { ...usage, ...promptTokens !== void 0 ? { promptTokens } : {} }; } function observeModelCallTerminalMessage(state, value) { if (!isRecord(value)) return; let rawUsage; try { rawUsage = value.usage; const stopReason = value.stopReason; if (value.role === "assistant" && (stopReason === "stop" || stopReason === "length" || stopReason === "toolUse")) { state.terminalSucceeded = true; state.terminalReason = stopReason; } if (value.role === "assistant" && (stopReason === "error" || stopReason === "aborted")) { state.terminalReason = stopReason; state.terminalError ??= Object.assign(new Error(typeof value.errorMessage === "string" ? value.errorMessage : stopReason), { code: stopReason === "aborted" ? "ABORT_ERR" : value.errorCode }); } } catch { return; } const usage = normalizedModelCallUsage(rawUsage); if (usage) state.usage = usage; } function observeOutputMessageContent(state, chunk) { if (!isRecord(chunk)) return; let type; let message; try { type = chunk.type; message = type === "done" ? chunk.message : type === "error" ? chunk.error : void 0; } catch { return; } if (message !== void 0) { observeModelCallTerminalMessage(state, message); if (state.contentCapture?.outputMessages) state.outputMessages = [cloneDiagnosticContentValue(message)]; } } function observeResultMessageContent(state, startedAt, result) { if (!state.terminalEventEmitted && state.terminalReason === void 0) state.lastProviderActivityAtMs = Date.now(); state.timeToFirstByteMs ??= Math.max(0, Date.now() - startedAt); observeModelCallTerminalMessage(state, result); if (state.contentCapture?.outputMessages && state.outputMessages === void 0) state.outputMessages = [cloneDiagnosticContentValue(result)]; if (state.responseStreamBytes === 0) { const bytes = jsonLength(result, true); if (bytes !== void 0) state.responseStreamBytes = bytes; } } function isNormalizedToolCall(value) { if (!isRecord(value) || value.type !== "toolCall") return false; return typeof value.id === "string" && value.id.trim().length > 0 && typeof value.name === "string" && value.name.trim().length > 0 && isRecord(value.arguments); } function isSemanticModelCallResult(result) { try { if (!isRecord(result) || result.role !== "assistant" || result.stopReason === "error" || result.stopReason === "aborted" || !Array.isArray(result.content)) return false; return result.stopReason === "toolUse" && result.content.some(isNormalizedToolCall) || result.content.some((item) => isRecord(item) && item.type === "text" && typeof item.text === "string" && item.text.trim().length > 0); } catch { return false; } } function maybeEmitModelCallSemanticProgress(eventBase, state, result) { if (state.semanticProgressEmitted || !isSemanticModelCallResult(result)) return; state.semanticProgressEmitted = true; emitCoreSemanticRunProgressDiagnosticEvent({ runId: eventBase.runId, ...eventBase.sessionKey ? { sessionKey: eventBase.sessionKey } : {}, ...eventBase.sessionId ? { sessionId: eventBase.sessionId } : {}, reason: MODEL_CALL_SEMANTIC_PROGRESS_REASON }); } function observeResponseChunk(state, startedAt, chunk) { if (!state.terminalEventEmitted) state.lastProviderActivityAtMs = Date.now(); state.timeToFirstByteMs ??= Math.max(0, Date.now() - startedAt); observeOutputMessageContent(state, chunk); const bytes = responseStreamChunkByteLength(chunk); if (bytes !== void 0) state.responseStreamBytes += bytes; } function createModelObserver(params) { const modelContent = streamContextModelContentFields(params.contentCapture, params.streamContext); const promptStats = params.capturePromptStats ? (params.measurePromptStats ?? streamContextModelPromptStats)(params.streamContext) : void 0; const state = { responseStreamBytes: 0, modelContent, contentCapture: params.contentCapture, suppressPluginHooks: params.suppressPluginHooks }; const reportStreamProgress = createModelCallStreamProgressReporter({ config: params.config }); return { state, promptStats, modelContent, assignRequestPayloadBytes(payload) { const bytes = jsonLength(payload, true); if (bytes !== void 0) state.requestPayloadBytes = bytes; }, observeResponseChunk(startedAt, chunk) { observeResponseChunk(state, startedAt, chunk); }, observeFinalResult(eventBase, startedAt, result) { observeResultMessageContent(state, startedAt, result); maybeEmitModelCallSemanticProgress(eventBase, state, result); }, maybeEmitStreamProgress(eventBase) { reportStreamProgress({ ...eventBase, callId: state.terminalEventEmitted ? void 0 : eventBase.callId }); }, sizeTimingFields() { return { ...state.requestPayloadBytes !== void 0 ? { requestPayloadBytes: state.requestPayloadBytes } : {}, ...state.responseStreamBytes > 0 ? { responseStreamBytes: state.responseStreamBytes } : {}, ...state.timeToFirstByteMs !== void 0 ? { timeToFirstByteMs: state.timeToFirstByteMs } : {} }; }, completedContent() { return state.modelContent || state.outputMessages ? { ...state.modelContent, ...state.outputMessages ? { outputMessages: state.outputMessages } : {} } : void 0; }, usageField() { return state.usage ? { usage: state.usage } : {}; } }; } //#endregion //#region src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts const MODEL_CALL_STREAM_RETURN_TIMEOUT_MS = 1e3; function asyncIteratorFactory(value) { if (value === null || typeof value !== "object") return; try { const asyncIterator = value[Symbol.asyncIterator]; if (typeof asyncIterator !== "function") return; return () => asyncIterator.call(value); } catch { return; } } async function safeReturnIterator(iterator, trackCleanup) { const returnResult = trackCleanup(() => iterator.return?.()); await settlesWithin(Promise.resolve(returnResult).catch(() => void 0), MODEL_CALL_STREAM_RETURN_TIMEOUT_MS); } function observeModelCallIterator(iterator, lifecycle, observeSharedResult) { const trackCleanup = captureAsyncWorkTracker(); let started = false; let returning; const observed = observe(); return { [Symbol.asyncIterator]() { return this; }, next() { if (returning) return returning.then(() => ({ done: true, value: void 0 })); started = true; return observed.next(); }, return(value) { returning ??= started ? observed.return(value) : Promise.resolve().then(async () => { await safeReturnIterator(iterator, trackCleanup); lifecycle.emitCompleted(); return { done: true, value }; }); return returning; }, throw(error) { started = true; return observed.throw(error); } }; async function* observe() { let iteratorSettled = false; try { for (;;) { const next = await iterator.next(); if (next.done) { iteratorSettled = true; break; } const chunk = next.value; lifecycle.observer.observeResponseChunk(lifecycle.startedAt, chunk); lifecycle.observer.maybeEmitStreamProgress(lifecycle.eventBase); yield chunk; } if (observeSharedResult && !lifecycle.observer.state.terminalError) trackCleanup(observeSharedResult).catch(() => void 0); else lifecycle.emitCompleted(); } catch (err) { iteratorSettled = true; lifecycle.emitError(err); throw err; } finally { if (!iteratorSettled) { await safeReturnIterator(iterator, trackCleanup); lifecycle.emitCompleted(); } } } } function createSharedResultObserver(stream, lifecycle) { if (!isRecord(stream) || typeof stream.result !== "function") return; const resultFn = stream.result; let cached; return () => { if (!cached) { cached = Promise.resolve().then(() => resultFn.call(stream)).then((resolved) => { lifecycle.observer.observeFinalResult(lifecycle.eventBase, lifecycle.startedAt, resolved); lifecycle.emitCompleted(); return resolved; }, (err) => { lifecycle.emitError(err); throw err; }); cached.catch(() => void 0); } return cached; }; } function observeModelCallStream(stream, createIterator, lifecycle) { const observedResult = createSharedResultObserver(stream, lifecycle); const observedIterator = () => observeModelCallIterator(createIterator(), lifecycle, observedResult); let hasNonConfigurableIterator; try { hasNonConfigurableIterator = Object.getOwnPropertyDescriptor(stream, Symbol.asyncIterator)?.configurable === false; } catch { hasNonConfigurableIterator = true; } if (hasNonConfigurableIterator) return { [Symbol.asyncIterator]: observedIterator, ...observedResult ? { result: observedResult } : {} }; return new Proxy(stream, { get(target, property, receiver) { if (property === Symbol.asyncIterator) return observedIterator; if (property === "result" && observedResult) return observedResult; const value = Reflect.get(target, property, receiver); return typeof value === "function" ? value.bind(target) : value; } }); } function observeModelCallResult(result, lifecycle) { const createIterator = asyncIteratorFactory(result); if (createIterator) return observeModelCallStream(result, createIterator, lifecycle); lifecycle.emitCompleted(); return result; } function wrapStreamFnWithDiagnosticModelCallEvents(streamFn, ctx) { const measurePromptStats = createModelPromptStats(); return ((model, streamContext, options) => { const lifecycle = createModelLifecycle({ ctx, options, requestTimeoutMs: clampPositiveTimerTimeoutMs((isRecord(model) ? model.requestTimeoutMs : void 0) ?? ctx.requestTimeoutMs), createObserver: (capturePromptStats) => createModelObserver({ config: ctx.config, streamContext, contentCapture: ctx.contentCapture, suppressPluginHooks: ctx.suppressPluginHooks, capturePromptStats, measurePromptStats }) }); try { const result = streamFn(model, streamContext, lifecycle.propagatedOptions); if (isPromiseLike(result)) return result.then((resolved) => observeModelCallResult(resolved, lifecycle), (err) => { lifecycle.emitError(err); throw err; }); return observeModelCallResult(result, lifecycle); } catch (err) { lifecycle.emitError(err); throw err; } }); } //#endregion export { resolveEmbeddedAgentStream as a, resolveEmbeddedAgentBaseStreamFn as i, createOpenAIServiceTierObservationWrapper as n, selectEmbeddedAgentStream as o, resolveEmbeddedAgentApiKey as r, wrapStreamFnWithDiagnosticModelCallEvents as t };