import { PROP_IDLE_TIMEOUT_MINUTES, PROP_SESSION_REPLAY_ID } from "./core/constants.js";
import { getActiveContext, isTrackerManaged } from "./context.js";
import { calculateCost } from "./utils/costs.js";
import { tryRequire } from "./utils/resolve-module.js";
import { ANTHROPIC_AVAILABLE, _AnthropicModule } from "./providers/anthropic.js";
import { BEDROCK_AVAILABLE, _BedrockModule, extractBedrockInvokeModelResponse } from "./providers/bedrock.js";
import { GEMINI_AVAILABLE, _GeminiModule } from "./providers/gemini.js";
import { MISTRAL_AVAILABLE, _MistralModule } from "./providers/mistral.js";
import { OPENAI_AVAILABLE, _OpenAIModule } from "./providers/openai.js";
import { warnIfProviderMismatch } from "./utils/provider-detect.js";

//#region src/patching.ts
var PatchOwnershipError = class extends Error {};
const _activePatches = [];
const _patchedProviders = /* @__PURE__ */ new Set();
const _providerOwners = /* @__PURE__ */ new Map();
function patchedProviders() {
	return [..._patchedProviders];
}
function _isMethodPatched(target, methodName) {
	return target[methodName]?.__amplitudePatched === true;
}
function _patchMethod(target, methodName, wrapper, providerName) {
	const original = target[methodName];
	if (typeof original !== "function") return;
	if (_isMethodPatched(target, methodName)) return;
	const patched = function(...args) {
		return wrapper(original.bind(this), ...args);
	};
	patched.__amplitudePatched = true;
	target[methodName] = patched;
	_activePatches.push({
		module: target,
		method: methodName,
		original,
		provider: providerName
	});
	_patchedProviders.add(providerName);
}
function _assertPatchOwner(providerName, ai) {
	const existingOwner = _providerOwners.get(providerName);
	if (existingOwner == null) {
		_providerOwners.set(providerName, ai);
		return;
	}
	if (existingOwner !== ai) throw new PatchOwnershipError(`Provider "${providerName}" is already patched by another AmplitudeAI instance. Call unpatch() before patching with a different instance.`);
}
function patchOpenAI(options) {
	const { amplitudeAI } = options;
	const mod = options.module ?? _OpenAIModule;
	if (mod == null) throw new Error("openai package is not installed. Install it with: npm install openai — or pass the module via the modules option.");
	_assertPatchOwner("openai", amplitudeAI);
	if (_patchOpenAIClass(mod.OpenAI, mod, "OpenAI", amplitudeAI, "openai")) _patchedProviders.add("openai");
}
function patchAnthropic(options) {
	const { amplitudeAI } = options;
	const mod = options.module ?? _AnthropicModule;
	if (mod == null) throw new Error("@anthropic-ai/sdk package is not installed. Install it with: npm install @anthropic-ai/sdk — or pass the module via the modules option.");
	_assertPatchOwner("anthropic", amplitudeAI);
	const AnthropicClass = mod.Anthropic;
	if (!AnthropicClass?.prototype) return;
	let didPatch = false;
	const messagesProto = _getNestedPrototype(AnthropicClass, ["messages"]) ?? _probeNestedPrototype(AnthropicClass, ["messages"]);
	if (messagesProto) {
		const target = messagesProto;
		_patchMethod(target, "create", _makeCompletionWrapper(amplitudeAI, "anthropic"), "anthropic");
		_patchMethod(target, "stream", _makeAnthropicStreamWrapper(amplitudeAI), "anthropic");
		didPatch = _isMethodPatched(target, "create") || _isMethodPatched(target, "stream");
	}
	if (didPatch) _patchedProviders.add("anthropic");
}
function patchAzureOpenAI(options) {
	const { amplitudeAI } = options;
	const mod = options.module ?? _OpenAIModule;
	if (mod == null) throw new Error("openai package is not installed. Install it with: npm install openai — or pass the module via the modules option.");
	_assertPatchOwner("azure-openai", amplitudeAI);
	if (_patchOpenAIClass(mod.AzureOpenAI, mod, "AzureOpenAI", amplitudeAI, "azure-openai")) _patchedProviders.add("azure-openai");
}
function patchGemini(options) {
	const { amplitudeAI } = options;
	const mod = options.module ?? _GeminiModule;
	const genAiMod = options.genAiModule ?? tryRequire("@google/genai");
	if (mod == null && genAiMod == null) throw new Error("No Gemini SDK installed. Install @google/generative-ai or @google/genai — or pass the module via the modules option.");
	_assertPatchOwner("gemini", amplitudeAI);
	if (genAiMod != null) _patchGoogleGenAIClient(genAiMod, amplitudeAI);
	if (mod == null) return;
	const GeminiClass = mod.GoogleGenerativeAI;
	if (!GeminiClass?.prototype) return;
	const proto = GeminiClass.prototype;
	_patchMethod(proto, "getGenerativeModel", (original, ...args) => {
		const modelObj = original(...args);
		if (modelObj == null || typeof modelObj !== "object") return modelObj;
		const model = modelObj;
		if (typeof model.generateContent === "function" && !(model.generateContent.__amplitudePatched === true)) _patchMethod(model, "generateContent", (innerOriginal, ...innerArgs) => {
			const startTime = performance.now();
			const result = innerOriginal(...innerArgs);
			if (result instanceof Promise) return result.then((response) => {
				_trackGeminiResponse(amplitudeAI, response, startTime, innerArgs[0]);
				return response;
			}).catch((err) => {
				_trackCompletionError(amplitudeAI, err, startTime, innerArgs[0], "gemini");
				throw err;
			});
			return result;
		}, "gemini");
		if (typeof model.generateContentStream === "function" && !(model.generateContentStream.__amplitudePatched === true)) _patchMethod(model, "generateContentStream", (innerOriginal, ...innerArgs) => {
			const startTime = performance.now();
			const result = innerOriginal(...innerArgs);
			if (result instanceof Promise) return result.then((response) => {
				const streamResp = response;
				const stream = streamResp.stream;
				if (_isAsyncIterable(stream)) return {
					...streamResp,
					stream: _wrapPatchedStream(amplitudeAI, stream, startTime, innerArgs[0], "gemini")
				};
				return response;
			}).catch((err) => {
				_trackCompletionError(amplitudeAI, err, startTime, innerArgs[0], "gemini");
				throw err;
			});
			return result;
		}, "gemini");
		return modelObj;
	}, "gemini");
	if (_isMethodPatched(proto, "getGenerativeModel")) _patchedProviders.add("gemini");
}
/**
* Patch the new `@google/genai` client shape (B7).
*
* Unlike the legacy `@google/generative-ai` package (which exposes
* `getGenerativeModel().generateContent`), `@google/genai` calls run through
* `new GoogleGenAI().models.generateContent` / `.generateContentStream`. The
* `models` namespace is an instance property, so we probe its shared prototype
* and patch the methods there — covering all current and future clients.
*/
function _patchGoogleGenAIClient(mod, amplitudeAI) {
	const GoogleGenAI = mod.GoogleGenAI;
	if (!GoogleGenAI?.prototype) return;
	const modelsProto = _getNestedPrototype(GoogleGenAI, ["models"]) ?? _probeNestedPrototype(GoogleGenAI, ["models"]);
	if (!modelsProto) return;
	_patchMethod(modelsProto, "generateContent", (original, ...args) => {
		const startTime = performance.now();
		return _handleGenAIGenerate(amplitudeAI, original(...args), startTime, args[0]);
	}, "gemini");
	_patchMethod(modelsProto, "generateContentStream", (original, ...args) => {
		const startTime = performance.now();
		return _handleGenAIGenerateStream(amplitudeAI, original(...args), startTime, args[0]);
	}, "gemini");
	if (_isMethodPatched(modelsProto, "generateContent") || _isMethodPatched(modelsProto, "generateContentStream")) _patchedProviders.add("gemini");
}
/**
* Track a `@google/genai` non-streaming generate result. Handles both the
* normal Promise return and a defensive non-Promise return (B7) so a
* synchronously-returned response is not silently left untracked.
*/
function _handleGenAIGenerate(ai, result, startTime, requestOpts) {
	if (result instanceof Promise) return result.then((response) => {
		_trackGeminiResponse(ai, response, startTime, requestOpts);
		return response;
	}).catch((err) => {
		_trackCompletionError(ai, err, startTime, requestOpts, "gemini");
		throw err;
	});
	if (result != null && typeof result === "object") _trackGeminiResponse(ai, result, startTime, requestOpts);
	return result;
}
/**
* Wrap a `@google/genai` streaming generate result. The new package resolves
* directly to an async iterable (not the legacy `{ stream }` envelope), and may
* — defensively — return one synchronously rather than via a Promise (B7).
*/
function _handleGenAIGenerateStream(ai, result, startTime, requestOpts) {
	if (result instanceof Promise) return result.then((resolved) => {
		if (_isAsyncIterable(resolved)) return _wrapPatchedStream(ai, resolved, startTime, requestOpts, "gemini");
		const env = resolved;
		if (env != null && _isAsyncIterable(env.stream)) return {
			...env,
			stream: _wrapPatchedStream(ai, env.stream, startTime, requestOpts, "gemini")
		};
		return resolved;
	}).catch((err) => {
		_trackCompletionError(ai, err, startTime, requestOpts, "gemini");
		throw err;
	});
	if (_isAsyncIterable(result)) return _wrapPatchedStream(ai, result, startTime, requestOpts, "gemini");
	return result;
}
function patchMistral(options) {
	const { amplitudeAI } = options;
	const mod = options.module ?? _MistralModule;
	if (mod == null) throw new Error("@mistralai/mistralai is not installed. Install it with: npm install @mistralai/mistralai — or pass the module via the modules option.");
	_assertPatchOwner("mistral", amplitudeAI);
	const MistralClass = mod.Mistral ?? mod.MistralClient ?? mod.default;
	if (!MistralClass?.prototype) return;
	const chatProto = _getNestedPrototype(MistralClass, ["chat"]) ?? _probeNestedPrototype(MistralClass, ["chat"]);
	let didPatch = false;
	if (chatProto) {
		const target = chatProto;
		_patchMethod(target, "complete", _makeCompletionWrapper(amplitudeAI, "mistral"), "mistral");
		_patchMethod(target, "stream", _makeCompletionWrapper(amplitudeAI, "mistral"), "mistral");
		didPatch = _isMethodPatched(target, "complete") || _isMethodPatched(target, "stream");
	}
	if (didPatch) _patchedProviders.add("mistral");
}
function patchBedrock(options) {
	const { amplitudeAI } = options;
	const mod = options.module ?? _BedrockModule;
	if (mod == null) throw new Error("@aws-sdk/client-bedrock-runtime is not installed. Install it with: npm install @aws-sdk/client-bedrock-runtime — or pass the module via the modules option.");
	_assertPatchOwner("bedrock", amplitudeAI);
	const ClientClass = mod.BedrockRuntimeClient;
	if (!ClientClass?.prototype) return;
	_patchMethod(ClientClass.prototype, "send", (original, ...args) => {
		const command = args[0];
		const commandName = String(command?.constructor?.name ?? "");
		const isInvokeModel = commandName.includes("InvokeModelCommand") && !commandName.includes("WithResponseStream");
		const shouldTrack = commandName.includes("ConverseCommand") || commandName.includes("ConverseStreamCommand") || isInvokeModel;
		const startTime = performance.now();
		const result = original(...args);
		if (result instanceof Promise) return result.then((response) => {
			if (!shouldTrack) return response;
			if (commandName.includes("ConverseStreamCommand")) {
				const streamResp = response;
				const stream = streamResp.stream;
				if (_isAsyncIterable(stream)) return {
					...streamResp,
					stream: _wrapPatchedBedrockStream(amplitudeAI, stream, startTime, args[0])
				};
			} else if (isInvokeModel) _trackBedrockInvokeModelResponse(amplitudeAI, response, startTime, args[0]);
			else _trackBedrockResponse(amplitudeAI, response, startTime, args[0]);
			return response;
		}).catch((err) => {
			if (shouldTrack) _trackCompletionError(amplitudeAI, err, startTime, args[0], "bedrock");
			throw err;
		});
		return result;
	}, "bedrock");
	if (_isMethodPatched(ClientClass.prototype, "send")) _patchedProviders.add("bedrock");
}
/**
* Auto-detects installed provider SDKs and patches each supported surface.
*
* Returns the list of provider names that were successfully patched.
* Ownership conflicts (same provider patched by a different `AmplitudeAI` instance)
* throw immediately to prevent ambiguous instrumentation state.
*
* In bundler environments where `tryRequire` cannot resolve modules, pass them
* explicitly via `modules`:
* ```ts
* import OpenAI from 'openai';
* import Anthropic from '@anthropic-ai/sdk';
* patch({ amplitudeAI: ai, modules: { openai: OpenAI, anthropic: Anthropic } });
* ```
*/
function patch(options) {
	const patched = [];
	const mods = options.modules ?? {};
	const providers = [
		{
			fn: patchOpenAI,
			available: OPENAI_AVAILABLE || mods.openai != null,
			name: "openai",
			moduleKey: "openai"
		},
		{
			fn: patchAzureOpenAI,
			available: OPENAI_AVAILABLE || mods.openai != null,
			name: "azure-openai",
			moduleKey: "openai"
		},
		{
			fn: patchAnthropic,
			available: ANTHROPIC_AVAILABLE || mods.anthropic != null,
			name: "anthropic",
			moduleKey: "anthropic"
		},
		{
			fn: patchGemini,
			available: GEMINI_AVAILABLE || mods.gemini != null,
			name: "gemini",
			moduleKey: "gemini"
		},
		{
			fn: patchMistral,
			available: MISTRAL_AVAILABLE || mods.mistral != null,
			name: "mistral",
			moduleKey: "mistral"
		},
		{
			fn: patchBedrock,
			available: BEDROCK_AVAILABLE || mods.bedrock != null,
			name: "bedrock",
			moduleKey: "bedrock"
		}
	];
	for (const { fn, available, name, moduleKey } of providers) {
		if (!available) continue;
		try {
			fn({
				amplitudeAI: options.amplitudeAI,
				module: mods[moduleKey]
			});
			if (_patchedProviders.has(name)) patched.push(name);
		} catch (error) {
			if (error instanceof PatchOwnershipError) throw error;
		}
	}
	if (options.expectedProviders && options.expectedProviders.length > 0) warnIfProviderMismatch({
		expectedProviders: options.expectedProviders,
		patchedProviders: patched,
		appKey: options.appKey ?? null
	});
	return patched;
}
function unpatch() {
	for (const record of [..._activePatches].reverse()) {
		const target = record.module;
		target[record.method] = record.original;
	}
	_activePatches.length = 0;
	_patchedProviders.clear();
	_providerOwners.clear();
}
function unpatchOpenAI() {
	_unpatchByProvider("openai");
}
function unpatchAnthropic() {
	_unpatchByProvider("anthropic");
}
function unpatchGemini() {
	_unpatchByProvider("gemini");
}
function unpatchMistral() {
	_unpatchByProvider("mistral");
}
function unpatchBedrock() {
	_unpatchByProvider("bedrock");
}
function unpatchAzureOpenAI() {
	_unpatchByProvider("azure-openai");
}
function _unpatchByProvider(providerName) {
	const toRemove = [];
	for (let i = _activePatches.length - 1; i >= 0; i--) {
		const record = _activePatches[i];
		if (record == null) continue;
		if (record.provider === providerName) {
			const target = record.module;
			target[record.method] = record.original;
			toRemove.push(i);
		}
	}
	for (const idx of toRemove) _activePatches.splice(idx, 1);
	_patchedProviders.delete(providerName);
	_providerOwners.delete(providerName);
}
function _makeCompletionWrapper(amplitudeAI, providerName) {
	return (original, ...args) => {
		const startTime = performance.now();
		try {
			_extractAndTrackToolCalls(amplitudeAI, args[0], providerName);
			_trackInputUserMessages(amplitudeAI, args[0], providerName);
		} catch {}
		let result;
		try {
			result = original(...args);
		} catch (syncErr) {
			_trackCompletionError(amplitudeAI, syncErr, startTime, args[0], providerName);
			throw syncErr;
		}
		if (result instanceof Promise) return result.then((response) => {
			if (_isAsyncIterable(response)) {
				if (providerName === "anthropic") return _wrapPatchedAnthropicStream(amplitudeAI, response, startTime, args[0]);
				return _wrapPatchedStream(amplitudeAI, response, startTime, args[0], providerName);
			}
			if (providerName === "anthropic") _trackAnthropicResponse(amplitudeAI, response, startTime, args[0]);
			else _trackCompletionResponse(amplitudeAI, response, startTime, args[0], providerName);
			return response;
		}).catch((err) => {
			_trackCompletionError(amplitudeAI, err, startTime, args[0], providerName);
			throw err;
		});
		return result;
	};
}
function _makeAnthropicStreamWrapper(amplitudeAI) {
	return (original, ...args) => {
		const startTime = performance.now();
		const result = original(...args);
		if (!(result instanceof Promise)) return result;
		return result.then((response) => {
			if (_isAsyncIterable(response)) return _wrapPatchedAnthropicStream(amplitudeAI, response, startTime, args[0]);
			const respObj = response;
			const stream = respObj.stream;
			if (_isAsyncIterable(stream)) return {
				...respObj,
				stream: _wrapPatchedAnthropicStream(amplitudeAI, stream, startTime, args[0])
			};
			return response;
		}).catch((err) => {
			_trackCompletionError(amplitudeAI, err, startTime, args[0], "anthropic");
			throw err;
		});
	};
}
function _isAsyncIterable(value) {
	return value != null && typeof value[Symbol.asyncIterator] === "function";
}
async function* _wrapPatchedStream(ai, stream, startTime, requestOpts, providerName) {
	if (providerName === "gemini") {
		yield* _wrapPatchedGeminiStream(ai, stream, startTime, requestOpts);
		return;
	}
	const ctx = getActiveContext();
	if (ctx == null) {
		yield* stream;
		return;
	}
	const req = requestOpts;
	let content = "";
	let model = String(req?.model ?? "unknown");
	let finishReason = "";
	let inputTokens;
	let outputTokens;
	let totalTokens;
	let reasoningTokens;
	let cachedTokens;
	const streamToolCalls = [];
	let isError = false;
	let errorMessage;
	try {
		for await (const chunk of stream) {
			const c = chunk;
			const choices = c.choices;
			const delta = choices?.[0]?.delta;
			if (delta?.content != null) content += String(delta.content);
			if (c.model != null) model = String(c.model);
			const deltaToolCalls = delta?.tool_calls;
			if (Array.isArray(deltaToolCalls)) for (const call of deltaToolCalls) {
				const idx = call.index;
				if (idx == null) continue;
				const id = call.id;
				const fn = call.function;
				streamToolCalls[idx] ??= {
					type: "function",
					id: id ?? "",
					function: {
						name: "",
						arguments: ""
					}
				};
				const entry = streamToolCalls[idx];
				if (id) entry.id = id;
				const entryFn = entry.function;
				if (fn?.name != null) entryFn.name = fn.name;
				if (fn?.arguments) entryFn.arguments = String(entryFn.arguments ?? "") + String(fn.arguments);
			}
			if (choices?.[0]?.finish_reason != null) finishReason = String(choices[0].finish_reason);
			const usage = c.usage;
			if (usage != null) {
				inputTokens = usage.prompt_tokens;
				outputTokens = usage.completion_tokens;
				totalTokens = usage.total_tokens;
				const completionDetails = usage.completion_tokens_details;
				const promptDetails = usage.prompt_tokens_details;
				if (completionDetails?.reasoning_tokens != null) reasoningTokens = completionDetails.reasoning_tokens;
				if (promptDetails?.cached_tokens != null) cachedTokens = promptDetails.cached_tokens;
			}
			yield chunk;
		}
	} catch (error) {
		isError = true;
		errorMessage = error instanceof Error ? error.message : String(error);
		throw error;
	} finally {
		if (!isTrackerManaged()) {
			const latencyMs = performance.now() - startTime;
			const filteredToolCalls = streamToolCalls.filter(Boolean);
			let costUsd = null;
			if (inputTokens != null && outputTokens != null) try {
				costUsd = calculateCost({
					modelName: model,
					inputTokens,
					outputTokens,
					reasoningTokens: reasoningTokens ?? 0,
					cacheReadInputTokens: cachedTokens ?? 0,
					defaultProvider: providerName === "azure-openai" ? "openai" : providerName
				});
			} catch {}
			ai.trackAiMessage({
				userId: ctx.userId ?? void 0,
				deviceId: ctx.deviceId ?? void 0,
				content,
				sessionId: ctx.sessionId,
				model,
				provider: providerName,
				latencyMs,
				traceId: ctx.traceId,
				inputTokens,
				outputTokens,
				totalTokens,
				reasoningTokens,
				cacheReadTokens: cachedTokens,
				totalCostUsd: costUsd,
				finishReason,
				toolCalls: filteredToolCalls.length > 0 ? filteredToolCalls : void 0,
				systemPrompt: _extractSystemPrompt(req),
				toolDefinitions: _extractToolDefinitions(req),
				temperature: req?.temperature,
				maxOutputTokens: req?.max_tokens ?? req?.max_completion_tokens,
				topP: req?.top_p,
				agentId: ctx.agentId,
				env: ctx.env,
				isStreaming: true,
				isError,
				errorMessage,
				..._contextExtras(ctx)
			});
			_recordToolUses(filteredToolCalls.length > 0 ? filteredToolCalls : void 0, ctx.sessionId, ctx.agentId);
		}
	}
}
async function* _wrapPatchedAnthropicStream(ai, stream, startTime, requestOpts) {
	const ctx = getActiveContext();
	if (ctx == null) {
		yield* stream;
		return;
	}
	const req = requestOpts;
	let content = "";
	let reasoningContent = "";
	let model = String(req?.model ?? "unknown");
	let finishReason = "";
	let inputTokens;
	let outputTokens;
	let cacheRead = 0;
	let cacheCreation = 0;
	const streamToolCalls = [];
	let isError = false;
	let errorMessage;
	try {
		for await (const chunk of stream) {
			const c = chunk;
			if (c.type === "message_start") {
				const message = c.message;
				if (typeof message?.model === "string") model = message.model;
				const usage = message?.usage;
				if (typeof usage?.input_tokens === "number") inputTokens = usage.input_tokens;
				if (typeof usage?.cache_read_input_tokens === "number") cacheRead = usage.cache_read_input_tokens;
				if (typeof usage?.cache_creation_input_tokens === "number") cacheCreation = usage.cache_creation_input_tokens;
			}
			if (c.type === "content_block_start") {
				const block = c.content_block;
				if (block?.type === "tool_use") streamToolCalls.push({
					type: "function",
					id: block.id,
					function: {
						name: String(block.name ?? ""),
						arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {})
					}
				});
			}
			if (c.type === "content_block_delta") {
				const delta = c.delta;
				if (delta?.type === "text_delta" && typeof delta?.text === "string") content += delta.text;
				else if (delta?.type === "thinking_delta" && typeof delta?.thinking === "string") reasoningContent += delta.thinking;
				else if (delta?.type === "input_json_delta" && typeof delta?.partial_json === "string") {
					const lastTc = streamToolCalls[streamToolCalls.length - 1];
					if (lastTc) {
						const fn = lastTc.function;
						fn.arguments = String(fn.arguments ?? "") + delta.partial_json;
					}
				}
			}
			if (c.type === "message_delta") {
				const delta = c.delta;
				if (typeof delta?.stop_reason === "string") finishReason = delta.stop_reason;
				else if (typeof c.stop_reason === "string") finishReason = c.stop_reason;
				const usage = c.usage;
				if (typeof usage?.output_tokens === "number") outputTokens = usage.output_tokens;
			}
			if (c.type === "message_stop") {
				const usage = c.usage;
				if (typeof usage?.output_tokens === "number") outputTokens = usage.output_tokens;
			}
			yield chunk;
		}
	} catch (error) {
		isError = true;
		errorMessage = error instanceof Error ? error.message : String(error);
		throw error;
	} finally {
		if (!isTrackerManaged()) {
			const latencyMs = performance.now() - startTime;
			const rawInput = inputTokens ?? 0;
			const normalizedInput = cacheRead || cacheCreation ? rawInput + cacheRead + cacheCreation : rawInput;
			let costUsd = null;
			if (inputTokens != null && outputTokens != null) try {
				costUsd = calculateCost({
					modelName: model,
					inputTokens: normalizedInput,
					outputTokens,
					cacheReadInputTokens: cacheRead,
					cacheCreationInputTokens: cacheCreation,
					defaultProvider: "anthropic"
				});
			} catch {}
			ai.trackAiMessage({
				userId: ctx.userId ?? void 0,
				deviceId: ctx.deviceId ?? void 0,
				content,
				sessionId: ctx.sessionId,
				model,
				provider: "anthropic",
				latencyMs,
				traceId: ctx.traceId,
				inputTokens: normalizedInput || void 0,
				outputTokens,
				cacheReadTokens: cacheRead || void 0,
				totalCostUsd: costUsd,
				finishReason,
				toolCalls: streamToolCalls.length > 0 ? streamToolCalls : void 0,
				reasoningContent: reasoningContent || void 0,
				systemPrompt: _extractAnthropicSystemPrompt(req?.system),
				toolDefinitions: _extractToolDefinitions(req),
				temperature: req?.temperature,
				maxOutputTokens: req?.max_tokens,
				topP: req?.top_p,
				agentId: ctx.agentId,
				env: ctx.env,
				isStreaming: true,
				isError,
				errorMessage,
				..._contextExtras(ctx)
			});
			_recordToolUses(streamToolCalls.length > 0 ? streamToolCalls : void 0, ctx.sessionId, ctx.agentId);
		}
	}
}
async function* _wrapPatchedGeminiStream(ai, stream, startTime, requestOpts) {
	const ctx = getActiveContext();
	if (ctx == null) {
		yield* stream;
		return;
	}
	let content = "";
	const model = "gemini";
	let finishReason = "";
	let inputTokens;
	let outputTokens;
	let totalTokens;
	let cacheReadTokens;
	let isError = false;
	let errorMessage;
	const streamToolCalls = [];
	try {
		for await (const chunk of stream) {
			const c = chunk;
			const respObj = c.response ?? c;
			const textVal = respObj.text;
			if (typeof textVal === "function") content += String(textVal());
			else if (typeof textVal === "string") content += textVal;
			const usage = respObj.usageMetadata;
			if (typeof usage?.promptTokenCount === "number") inputTokens = usage.promptTokenCount;
			if (typeof usage?.candidatesTokenCount === "number") outputTokens = usage.candidatesTokenCount;
			if (typeof usage?.totalTokenCount === "number") totalTokens = usage.totalTokenCount;
			if (typeof usage?.cachedContentTokenCount === "number") cacheReadTokens = usage.cachedContentTokenCount;
			const candidates = respObj.candidates;
			if (typeof candidates?.[0]?.finishReason === "string") finishReason = candidates[0].finishReason;
			const chunkToolCalls = _extractGeminiToolCalls(respObj);
			if (chunkToolCalls.length > 0) streamToolCalls.push(...chunkToolCalls);
			yield chunk;
		}
	} catch (error) {
		isError = true;
		errorMessage = error instanceof Error ? error.message : String(error);
		throw error;
	} finally {
		if (!isTrackerManaged()) {
			let costUsd = null;
			if (inputTokens != null && outputTokens != null) try {
				costUsd = calculateCost({
					modelName: model,
					inputTokens,
					outputTokens,
					cacheReadInputTokens: cacheReadTokens ?? 0,
					defaultProvider: "gemini"
				});
			} catch {}
			const reqOpts = requestOpts;
			const genConfig = reqOpts?.generationConfig;
			const sysInstr = reqOpts?.systemInstruction;
			const systemPrompt = typeof sysInstr === "string" ? sysInstr : sysInstr != null && typeof sysInstr === "object" ? String(sysInstr.text ?? "") : void 0;
			ai.trackAiMessage({
				userId: ctx.userId ?? void 0,
				deviceId: ctx.deviceId ?? void 0,
				content,
				sessionId: ctx.sessionId,
				model,
				provider: "gemini",
				latencyMs: performance.now() - startTime,
				traceId: ctx.traceId,
				inputTokens,
				outputTokens,
				totalTokens,
				cacheReadTokens,
				totalCostUsd: costUsd,
				toolCalls: streamToolCalls.length > 0 ? streamToolCalls : void 0,
				systemPrompt: systemPrompt || void 0,
				toolDefinitions: _extractToolDefinitions(reqOpts),
				temperature: genConfig?.temperature,
				maxOutputTokens: genConfig?.maxOutputTokens,
				topP: genConfig?.topP,
				finishReason,
				agentId: ctx.agentId,
				env: ctx.env,
				isStreaming: true,
				isError,
				errorMessage,
				..._contextExtras(ctx)
			});
			_recordToolUses(streamToolCalls.length > 0 ? streamToolCalls : void 0, ctx.sessionId, ctx.agentId);
		}
	}
}
async function* _wrapPatchedBedrockStream(ai, stream, startTime, requestOpts) {
	const ctx = getActiveContext();
	if (ctx == null) {
		yield* stream;
		return;
	}
	const opts = requestOpts;
	let model = String(opts?.modelId ?? "unknown");
	let content = "";
	let finishReason = "";
	let inputTokens;
	let outputTokens;
	let totalTokens;
	const streamToolCalls = [];
	let currentToolUse = null;
	let isError = false;
	let errorMessage;
	try {
		for await (const rawEvent of stream) {
			const event = rawEvent;
			const delta = event.contentBlockDelta?.delta;
			if (typeof delta?.text === "string") content += delta.text;
			if (typeof delta?.toolUse === "object" && delta.toolUse != null) {
				const toolUseDelta = delta.toolUse;
				if (typeof toolUseDelta.input === "string" && currentToolUse) {
					const fn = currentToolUse.function;
					fn.arguments = String(fn.arguments ?? "") + toolUseDelta.input;
				}
			}
			const blockStart = event.contentBlockStart;
			if (blockStart?.start != null) {
				const start = blockStart.start;
				if (start.toolUse != null) {
					const tu = start.toolUse;
					currentToolUse = {
						type: "function",
						id: tu.toolUseId,
						function: {
							name: String(tu.name ?? ""),
							arguments: ""
						}
					};
					streamToolCalls.push(currentToolUse);
				}
			}
			const messageStart = event.messageStart;
			if (typeof messageStart?.model === "string") model = messageStart.model;
			const messageStop = event.messageStop;
			if (typeof messageStop?.stopReason === "string") finishReason = messageStop.stopReason;
			const usage = event.metadata?.usage;
			if (typeof usage?.inputTokens === "number") inputTokens = usage.inputTokens;
			if (typeof usage?.outputTokens === "number") outputTokens = usage.outputTokens;
			if (typeof usage?.totalTokens === "number") totalTokens = usage.totalTokens;
			yield rawEvent;
		}
	} catch (error) {
		isError = true;
		errorMessage = error instanceof Error ? error.message : String(error);
		throw error;
	} finally {
		if (!isTrackerManaged()) {
			const latencyMs = performance.now() - startTime;
			let costUsd = null;
			if (inputTokens != null && outputTokens != null) try {
				costUsd = calculateCost({
					modelName: model,
					inputTokens,
					outputTokens,
					defaultProvider: "bedrock"
				});
			} catch {}
			const infConfig = opts?.inferenceConfig;
			ai.trackAiMessage({
				userId: ctx.userId ?? void 0,
				deviceId: ctx.deviceId ?? void 0,
				content,
				sessionId: ctx.sessionId,
				model,
				provider: "bedrock",
				latencyMs,
				traceId: ctx.traceId,
				inputTokens,
				outputTokens,
				totalTokens,
				totalCostUsd: costUsd,
				finishReason,
				toolCalls: streamToolCalls.length > 0 ? streamToolCalls : void 0,
				temperature: infConfig?.temperature,
				maxOutputTokens: infConfig?.maxTokens,
				topP: infConfig?.topP,
				agentId: ctx.agentId,
				env: ctx.env,
				isStreaming: true,
				isError,
				errorMessage,
				..._contextExtras(ctx)
			});
			_recordToolUses(streamToolCalls.length > 0 ? streamToolCalls : void 0, ctx.sessionId, ctx.agentId);
		}
	}
}
function _patchConstructor(cls, moduleExports, exportKey, amplitudeAI, providerName) {
	if (moduleExports[exportKey]?.__amplitudeCtorPatched === true) return;
	const originalCtor = cls;
	const proxy = new Proxy(originalCtor, { construct(target, args, newTarget) {
		const instance = Reflect.construct(target, args, newTarget);
		_patchInstanceCompletions(instance, amplitudeAI, providerName);
		return instance;
	} });
	proxy.__amplitudeCtorPatched = true;
	const mod = moduleExports;
	let patchedExportKey = null;
	if (mod[exportKey] === originalCtor) {
		mod[exportKey] = proxy;
		patchedExportKey = exportKey;
	} else for (const key of Object.keys(mod)) if (mod[key] === originalCtor) {
		mod[key] = proxy;
		patchedExportKey = key;
	}
	if (patchedExportKey != null) _activePatches.push({
		module: mod,
		method: patchedExportKey,
		original: originalCtor,
		provider: providerName
	});
}
function _patchOpenAIClass(maybeClass, moduleExports, exportKey, amplitudeAI, providerName) {
	const OpenAIClass = maybeClass;
	if (!OpenAIClass?.prototype) return false;
	let didPatch = false;
	const completionsProto = _getNestedPrototype(OpenAIClass, ["chat", "completions"]) ?? _probeNestedPrototype(OpenAIClass, ["chat", "completions"]);
	if (completionsProto) {
		const target = completionsProto;
		_patchMethod(target, "create", _makeCompletionWrapper(amplitudeAI, providerName), providerName);
		_patchMethod(target, "parse", _makeCompletionWrapper(amplitudeAI, providerName), providerName);
		didPatch = true;
	} else {
		_patchConstructor(OpenAIClass, moduleExports, exportKey, amplitudeAI, providerName);
		didPatch = true;
	}
	const responsesProto = _getNestedPrototype(OpenAIClass, ["responses"]) ?? _probeNestedPrototype(OpenAIClass, ["responses"]);
	if (responsesProto) {
		const target = responsesProto;
		_patchMethod(target, "create", _makeResponsesWrapper(amplitudeAI, providerName), providerName);
		_patchMethod(target, "stream", _makeResponsesWrapper(amplitudeAI, providerName), providerName);
		didPatch = true;
	}
	return didPatch;
}
const _AMP_INSTANCE_PATCHED = Symbol.for("amplitude.instancePatched");
/**
* Patches completions/responses methods directly on an SDK *instance*.
* Used as the constructor-Proxy fallback when prototype-level patching
* isn't possible (plain-object namespaces with no shared prototype).
*
* NOTE: These per-instance wrappers survive unpatch(). unpatch() restores
* the class export (so new instances are clean), but instances created
* while patched retain their wrapped methods. Test teardown should not
* rely on unpatch() clearing already-constructed instances.
*/
function _patchInstanceCompletions(instance, amplitudeAI, providerName) {
	if (instance[_AMP_INSTANCE_PATCHED]) return;
	instance[_AMP_INSTANCE_PATCHED] = true;
	try {
		const completions = instance.chat?.completions;
		if (completions && typeof completions.create === "function") {
			const original = completions.create.bind(completions);
			completions.create = (...args) => _makeCompletionWrapper(amplitudeAI, providerName)(original, ...args);
		}
		if (completions && typeof completions.parse === "function") {
			const originalParse = completions.parse.bind(completions);
			completions.parse = (...args) => _makeCompletionWrapper(amplitudeAI, providerName)(originalParse, ...args);
		}
		const responses = instance.responses;
		if (responses && typeof responses.create === "function") {
			const originalResponses = responses.create.bind(responses);
			responses.create = (...args) => _makeResponsesWrapper(amplitudeAI, providerName)(originalResponses, ...args);
		}
		if (responses && typeof responses.stream === "function") {
			const originalResponsesStream = responses.stream.bind(responses);
			responses.stream = (...args) => _makeResponsesWrapper(amplitudeAI, providerName)(originalResponsesStream, ...args);
		}
	} catch {}
}
function _makeResponsesWrapper(amplitudeAI, providerName) {
	return (original, ...args) => {
		const startTime = performance.now();
		try {
			_extractResponsesToolCallsFromInput(amplitudeAI, args[0]);
			_trackResponsesUserMessages(amplitudeAI, args[0]);
		} catch {}
		let result;
		try {
			result = original(...args);
		} catch (syncErr) {
			_trackCompletionError(amplitudeAI, syncErr, startTime, args[0], providerName);
			throw syncErr;
		}
		if (result instanceof Promise) return result.then((response) => {
			if (_isAsyncIterable(response)) return _wrapPatchedResponsesStream(amplitudeAI, response, startTime, args[0], providerName);
			_trackResponsesResponse(amplitudeAI, response, startTime, args[0], providerName);
			return response;
		}).catch((err) => {
			_trackCompletionError(amplitudeAI, err, startTime, args[0], providerName);
			throw err;
		});
		return result;
	};
}
/**
* Walk the prototype chain looking for `path` as own properties.
* This is the legacy approach; it only works for SDKs that define nested
* namespaces on the prototype (pre-v4 OpenAI, some Gemini versions).
*/
function _getNestedPrototype(cls, path) {
	let current = cls.prototype;
	for (const key of path) {
		if (current == null || typeof current !== "object") return null;
		const obj = current;
		if (typeof obj[key] === "object" && obj[key] != null) current = obj[key];
		else return null;
	}
	return current;
}
/**
* Modern SDKs (OpenAI v4+, Anthropic, Mistral) define nested namespaces
* (e.g. `client.chat.completions`, `client.messages`) as lazy *instance*
* properties via getters, not on the prototype. `_getNestedPrototype` fails
* silently for these because the properties don't exist until an instance
* is created.
*
* This function creates a disposable instance with dummy args, reads the
* nested property (which triggers the lazy getter), and returns its *shared
* prototype*. Patching the prototype covers all future and existing
* instances.
*/
const _probeCache = /* @__PURE__ */ new WeakMap();
/**
* NOTE: This runs the real SDK constructor via Reflect.construct, which
* has non-trivial side effects (reads process.env, allocates sub-namespace
* objects, initialises fetch shims). The instance is cached per class to
* avoid repeating this for multiple path lookups on the same SDK.
*/
function _probeNestedPrototype(cls, path) {
	try {
		let probe = _probeCache.get(cls);
		if (!probe) {
			const Ctor = cls;
			try {
				probe = Reflect.construct(Ctor, [{
					apiKey: "amp-probe",
					baseURL: "http://localhost"
				}]);
			} catch {
				try {
					probe = Reflect.construct(Ctor, [{ apiKey: "amp-probe" }]);
				} catch {
					probe = Object.create(Ctor.prototype);
				}
			}
			_probeCache.set(cls, probe);
		}
		let current = probe;
		for (const key of path) {
			if (current == null || typeof current !== "object") return null;
			const nested = current[key];
			if (nested == null || typeof nested !== "object") return null;
			current = nested;
		}
		const proto = Object.getPrototypeOf(current);
		if (proto != null && proto !== Object.prototype) return proto;
		return null;
	} catch {
		return null;
	}
}
function _trackCompletionResponse(ai, response, startTime, requestOpts, providerName) {
	if (response == null || typeof response !== "object") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	const resp = response;
	const usage = resp.usage;
	const choice = resp.choices?.[0];
	const message = choice?.message;
	const toolCalls = message?.tool_calls;
	const req = requestOpts;
	const promptDetails = usage?.prompt_tokens_details;
	const reasoningTokens = (usage?.completion_tokens_details)?.reasoning_tokens;
	const cachedTokens = promptDetails?.cached_tokens;
	const inputTokens = usage?.prompt_tokens;
	const outputTokens = usage?.completion_tokens;
	const modelName = String(resp.model ?? req?.model ?? "unknown");
	let costUsd = null;
	if (inputTokens != null && outputTokens != null) try {
		costUsd = calculateCost({
			modelName,
			inputTokens,
			outputTokens,
			reasoningTokens: reasoningTokens ?? 0,
			cacheReadInputTokens: cachedTokens ?? 0,
			defaultProvider: providerName === "azure-openai" ? "openai" : providerName
		});
	} catch {}
	const latencyMs = performance.now() - startTime;
	ai.trackAiMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content: String(message?.content ?? ""),
		sessionId: ctx.sessionId,
		model: modelName,
		provider: providerName,
		latencyMs,
		traceId: ctx.traceId,
		inputTokens,
		outputTokens,
		totalTokens: usage?.total_tokens,
		reasoningTokens,
		cacheReadTokens: cachedTokens,
		totalCostUsd: costUsd,
		finishReason: String(choice?.finish_reason ?? ""),
		toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : void 0,
		systemPrompt: _extractSystemPrompt(req),
		toolDefinitions: _extractToolDefinitions(req),
		temperature: req?.temperature,
		maxOutputTokens: req?.max_tokens ?? req?.max_completion_tokens,
		topP: req?.top_p,
		agentId: ctx.agentId,
		env: ctx.env,
		..._contextExtras(ctx)
	});
	_recordToolUses(toolCalls, ctx.sessionId, ctx.agentId);
}
function _trackAnthropicResponse(ai, response, startTime, requestOpts) {
	if (response == null || typeof response !== "object") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	const resp = response;
	const usage = resp.usage;
	const contentBlocks = resp.content;
	const textBlock = contentBlocks?.find((b) => b.type === "text");
	const req = requestOpts;
	const extracted = _extractAnthropicContent(contentBlocks);
	const cacheRead = usage?.cache_read_input_tokens ?? 0;
	const cacheCreation = usage?.cache_creation_input_tokens ?? 0;
	const rawInput = usage?.input_tokens ?? 0;
	const normalizedInput = cacheRead || cacheCreation ? rawInput + cacheRead + cacheCreation : rawInput;
	const outputTokens = usage?.output_tokens;
	const modelName = String(resp.model ?? req?.model ?? "unknown");
	let costUsd = null;
	if (rawInput != null && outputTokens != null) try {
		costUsd = calculateCost({
			modelName,
			inputTokens: normalizedInput,
			outputTokens,
			cacheReadInputTokens: cacheRead,
			cacheCreationInputTokens: cacheCreation,
			defaultProvider: "anthropic"
		});
	} catch {}
	const latencyMs = performance.now() - startTime;
	ai.trackAiMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content: String(textBlock?.text ?? ""),
		sessionId: ctx.sessionId,
		model: modelName,
		provider: "anthropic",
		latencyMs,
		traceId: ctx.traceId,
		inputTokens: normalizedInput || void 0,
		outputTokens,
		cacheReadTokens: cacheRead || void 0,
		totalCostUsd: costUsd,
		finishReason: String(resp.stop_reason ?? ""),
		toolCalls: extracted.toolCalls.length > 0 ? extracted.toolCalls : void 0,
		reasoningContent: extracted.reasoning,
		systemPrompt: _extractAnthropicSystemPrompt(req?.system),
		toolDefinitions: _extractToolDefinitions(req),
		temperature: req?.temperature,
		maxOutputTokens: req?.max_tokens,
		topP: req?.top_p,
		agentId: ctx.agentId,
		env: ctx.env,
		..._contextExtras(ctx)
	});
	_recordToolUses(extracted.toolCalls.length > 0 ? extracted.toolCalls : void 0, ctx.sessionId, ctx.agentId);
}
function _trackCompletionError(ai, error, startTime, requestOpts, providerName) {
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	const opts = requestOpts;
	const latencyMs = performance.now() - startTime;
	ai.trackAiMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content: "",
		sessionId: ctx.sessionId,
		model: String(opts?.model ?? opts?.modelId ?? "unknown"),
		provider: providerName,
		latencyMs,
		traceId: ctx.traceId,
		isError: true,
		errorMessage: error instanceof Error ? error.message : String(error),
		agentId: ctx.agentId,
		env: ctx.env,
		..._contextExtras(ctx)
	});
}
function _trackGeminiResponse(ai, response, startTime, requestOpts) {
	if (response == null || typeof response !== "object") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	const resp = response;
	const respObj = resp.response ?? resp;
	const text = typeof respObj.text === "function" ? String(respObj.text()) : typeof respObj.text === "string" ? respObj.text : "";
	const usage = respObj.usageMetadata;
	const inputTokens = usage?.promptTokenCount;
	const outputTokens = usage?.candidatesTokenCount;
	const cacheReadTokens = usage?.cachedContentTokenCount;
	let costUsd = null;
	if (inputTokens != null && outputTokens != null) try {
		costUsd = calculateCost({
			modelName: "gemini",
			inputTokens,
			outputTokens,
			cacheReadInputTokens: cacheReadTokens ?? 0,
			defaultProvider: "gemini"
		});
	} catch {}
	const toolCalls = _extractGeminiToolCalls(respObj);
	const latencyMs = performance.now() - startTime;
	const reqOpts = requestOpts;
	const genConfig = reqOpts?.generationConfig;
	const sysInstr = reqOpts?.systemInstruction;
	const systemPrompt = typeof sysInstr === "string" ? sysInstr : sysInstr != null && typeof sysInstr === "object" ? String(sysInstr.text ?? "") : void 0;
	ai.trackAiMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content: text,
		sessionId: ctx.sessionId,
		model: "gemini",
		provider: "gemini",
		latencyMs,
		traceId: ctx.traceId,
		inputTokens,
		outputTokens,
		totalTokens: usage?.totalTokenCount,
		cacheReadTokens,
		totalCostUsd: costUsd,
		toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
		systemPrompt: systemPrompt || void 0,
		toolDefinitions: _extractToolDefinitions(reqOpts),
		temperature: genConfig?.temperature,
		maxOutputTokens: genConfig?.maxOutputTokens,
		topP: genConfig?.topP,
		agentId: ctx.agentId,
		env: ctx.env,
		..._contextExtras(ctx)
	});
	_recordToolUses(toolCalls.length > 0 ? toolCalls : void 0, ctx.sessionId, ctx.agentId);
}
function _trackBedrockResponse(ai, response, startTime, requestOpts) {
	if (response == null || typeof response !== "object") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	const resp = response;
	const content = (resp.output?.message)?.content;
	const textBlock = content?.find((b) => b.text != null);
	const usage = resp.usage;
	const opts = requestOpts;
	const modelName = String(opts?.modelId ?? "unknown");
	const inputTokens = usage?.inputTokens;
	const outputTokens = usage?.outputTokens;
	let costUsd = null;
	if (inputTokens != null && outputTokens != null) try {
		costUsd = calculateCost({
			modelName,
			inputTokens,
			outputTokens,
			defaultProvider: "bedrock"
		});
	} catch {}
	const toolCalls = _extractBedrockToolCalls(content);
	const infConfig = opts?.inferenceConfig;
	const latencyMs = performance.now() - startTime;
	ai.trackAiMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content: String(textBlock?.text ?? ""),
		sessionId: ctx.sessionId,
		model: modelName,
		provider: "bedrock",
		latencyMs,
		traceId: ctx.traceId,
		inputTokens,
		outputTokens,
		totalTokens: usage?.totalTokens,
		totalCostUsd: costUsd,
		finishReason: String(resp.stopReason ?? ""),
		toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
		temperature: infConfig?.temperature,
		maxOutputTokens: infConfig?.maxTokens,
		topP: infConfig?.topP,
		agentId: ctx.agentId,
		env: ctx.env,
		..._contextExtras(ctx)
	});
	_recordToolUses(toolCalls.length > 0 ? toolCalls : void 0, ctx.sessionId, ctx.agentId);
}
function _parseBedrockJson(value) {
	if (value == null) return void 0;
	if (typeof value === "object" && !(value instanceof Uint8Array)) return value;
	const text = value instanceof Uint8Array ? new TextDecoder().decode(value) : typeof value === "string" ? value : "";
	if (!text) return void 0;
	try {
		const parsed = JSON.parse(text);
		return parsed != null && typeof parsed === "object" ? parsed : void 0;
	} catch {
		return;
	}
}
function _trackBedrockInvokeModelResponse(ai, response, startTime, requestOpts) {
	if (response == null || typeof response !== "object") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	const command = requestOpts;
	const input = command?.input ?? command ?? {};
	const modelName = String(input.modelId ?? "unknown");
	const resp = response;
	const extracted = extractBedrockInvokeModelResponse(modelName, _parseBedrockJson(input.body), _parseBedrockJson(resp.body));
	let costUsd = null;
	if (extracted.inputTokens != null && extracted.outputTokens != null) try {
		costUsd = calculateCost({
			modelName,
			inputTokens: extracted.inputTokens,
			outputTokens: extracted.outputTokens,
			defaultProvider: "bedrock"
		});
	} catch {}
	ai.trackAiMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content: extracted.text,
		sessionId: ctx.sessionId,
		model: modelName,
		provider: "bedrock",
		latencyMs: performance.now() - startTime,
		traceId: ctx.traceId,
		inputTokens: extracted.inputTokens,
		outputTokens: extracted.outputTokens,
		totalTokens: extracted.totalTokens,
		totalCostUsd: costUsd,
		finishReason: extracted.stopReason,
		temperature: extracted.temperature,
		maxOutputTokens: extracted.maxOutputTokens,
		topP: extracted.topP,
		agentId: ctx.agentId,
		env: ctx.env,
		..._contextExtras(ctx)
	});
}
function _trackResponsesResponse(ai, response, startTime, requestOpts, providerName) {
	if (response == null || typeof response !== "object") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	const resp = response;
	const usage = resp.usage;
	const outputText = typeof resp.output_text === "string" ? resp.output_text : _extractResponsesText(resp.output);
	const opts = requestOpts;
	const modelName = String(resp.model ?? opts?.model ?? "unknown");
	const inputTokens = typeof usage?.input_tokens === "number" ? usage.input_tokens : void 0;
	const outputTokens = typeof usage?.output_tokens === "number" ? usage.output_tokens : void 0;
	let costUsd = null;
	if (inputTokens != null && outputTokens != null) try {
		costUsd = calculateCost({
			modelName,
			inputTokens,
			outputTokens,
			reasoningTokens: typeof (usage?.output_tokens_details)?.reasoning_tokens === "number" ? (usage?.output_tokens_details).reasoning_tokens : 0,
			defaultProvider: "openai"
		});
	} catch {}
	const toolCalls = _extractResponsesOutputToolCalls(resp.output);
	const systemPrompt = typeof opts?.instructions === "string" ? opts.instructions : void 0;
	const toolDefs = _extractToolDefinitions(opts);
	ai.trackAiMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content: outputText,
		sessionId: ctx.sessionId,
		model: modelName,
		provider: providerName,
		latencyMs: performance.now() - startTime,
		traceId: ctx.traceId,
		inputTokens,
		outputTokens,
		totalTokens: typeof usage?.total_tokens === "number" ? usage.total_tokens : void 0,
		reasoningTokens: typeof (usage?.output_tokens_details)?.reasoning_tokens === "number" ? (usage?.output_tokens_details).reasoning_tokens : void 0,
		totalCostUsd: costUsd,
		finishReason: _extractResponsesFinishReason(resp),
		toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
		systemPrompt,
		toolDefinitions: toolDefs,
		temperature: opts?.temperature,
		maxOutputTokens: opts?.max_output_tokens,
		topP: opts?.top_p,
		agentId: ctx.agentId,
		env: ctx.env,
		..._contextExtras(ctx)
	});
	_recordToolUses(toolCalls.length > 0 ? toolCalls : void 0, ctx.sessionId, ctx.agentId);
}
async function* _wrapPatchedResponsesStream(ai, stream, startTime, requestOpts, providerName) {
	const ctx = getActiveContext();
	if (ctx == null) {
		yield* stream;
		return;
	}
	const opts = requestOpts;
	const model = String(opts?.model ?? "unknown");
	let content = "";
	let finishReason = "";
	let inputTokens;
	let outputTokens;
	let totalTokens;
	let reasoningTokens;
	let isError = false;
	let errorMessage;
	let completedOutput;
	try {
		for await (const event of stream) {
			const e = event;
			const type = e.type;
			if (type === "response.output_text.delta") {
				const delta = e.delta;
				if (typeof delta === "string") content += delta;
			} else if (type === "response.completed") {
				const response = e.response;
				const usage = response?.usage;
				const outputText = response?.output_text;
				if (typeof outputText === "string" && outputText.length > 0) content = outputText;
				if (typeof usage?.input_tokens === "number") inputTokens = usage.input_tokens;
				if (typeof usage?.output_tokens === "number") outputTokens = usage.output_tokens;
				if (typeof usage?.total_tokens === "number") totalTokens = usage.total_tokens;
				const outDetails = usage?.output_tokens_details;
				if (typeof outDetails?.reasoning_tokens === "number") reasoningTokens = outDetails.reasoning_tokens;
				const status = response?.status;
				if (typeof status === "string") finishReason = status;
				completedOutput = response?.output;
			}
			yield event;
		}
	} catch (error) {
		isError = true;
		errorMessage = error instanceof Error ? error.message : String(error);
		throw error;
	} finally {
		if (!isTrackerManaged()) {
			let costUsd = null;
			if (inputTokens != null && outputTokens != null) try {
				costUsd = calculateCost({
					modelName: model,
					inputTokens,
					outputTokens,
					reasoningTokens: reasoningTokens ?? 0,
					defaultProvider: "openai"
				});
			} catch {}
			const toolCalls = _extractResponsesOutputToolCalls(completedOutput);
			const systemPrompt = typeof opts?.instructions === "string" ? opts.instructions : void 0;
			const toolDefs = _extractToolDefinitions(opts);
			ai.trackAiMessage({
				userId: ctx.userId ?? void 0,
				deviceId: ctx.deviceId ?? void 0,
				content,
				sessionId: ctx.sessionId,
				model,
				provider: providerName,
				latencyMs: performance.now() - startTime,
				traceId: ctx.traceId,
				inputTokens,
				outputTokens,
				totalTokens,
				reasoningTokens,
				totalCostUsd: costUsd,
				finishReason,
				toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
				systemPrompt,
				toolDefinitions: toolDefs,
				temperature: opts?.temperature,
				maxOutputTokens: opts?.max_output_tokens,
				topP: opts?.top_p,
				agentId: ctx.agentId,
				env: ctx.env,
				isStreaming: true,
				isError,
				errorMessage,
				..._contextExtras(ctx)
			});
			_recordToolUses(toolCalls.length > 0 ? toolCalls : void 0, ctx.sessionId, ctx.agentId);
		}
	}
}
function _extractResponsesText(output) {
	if (!Array.isArray(output)) return "";
	let text = "";
	for (const item of output) {
		if (item == null || typeof item !== "object") continue;
		const content = item.content;
		if (!Array.isArray(content)) continue;
		for (const part of content) {
			if (part == null || typeof part !== "object") continue;
			const partText = part.text;
			if (typeof partText === "string") text += partText;
		}
	}
	return text;
}
function _extractResponsesFinishReason(response) {
	if (typeof response.status === "string" && response.status.length > 0) return response.status;
	const output = response.output;
	if (!Array.isArray(output) || output.length === 0) return void 0;
	const first = output[0];
	return typeof first?.status === "string" ? first.status : void 0;
}
function _extractResponsesOutputToolCalls(output) {
	if (!Array.isArray(output)) return [];
	const toolCalls = [];
	for (const item of output) {
		if (item == null || typeof item !== "object") continue;
		const obj = item;
		if (obj.type === "function_call") {
			toolCalls.push(obj);
			continue;
		}
		const content = obj.content;
		if (!Array.isArray(content)) continue;
		for (const c of content) {
			if (c == null || typeof c !== "object") continue;
			const cObj = c;
			if (cObj.type === "tool_call" || cObj.type === "function_call") toolCalls.push(cObj);
		}
	}
	return toolCalls;
}
function _trackResponsesUserMessages(ai, requestOpts) {
	if (typeof ai.trackUserMessage !== "function") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	if (!ctx.userId && !ctx.deviceId || !ctx.sessionId) return;
	if (ctx.skipAutoUserTracking) return;
	const opts = requestOpts;
	if (!opts) return;
	const input = opts.input;
	if (input == null) return;
	if (typeof input === "string") {
		_emitUserMessage(ai, ctx, input);
		return;
	}
	if (!Array.isArray(input)) return;
	const entries = input;
	const lastReplyIdx = entries.findLastIndex((e) => {
		if (typeof e === "string") return false;
		return e.role === "assistant" || e.type === "function_call" || e.type === "function_call_output";
	});
	const newEntries = entries.slice(lastReplyIdx + 1);
	for (const entry of newEntries) {
		if (typeof entry === "string") {
			_emitUserMessage(ai, ctx, entry);
			continue;
		}
		if (entry.role !== "user") continue;
		const content = entry.content;
		if (typeof content === "string") _emitUserMessage(ai, ctx, content);
		else if (Array.isArray(content)) for (const part of content) {
			const text = typeof part === "string" ? part : part?.text;
			if (typeof text === "string" && text.length > 0) _emitUserMessage(ai, ctx, text);
		}
	}
}
function _extractResponsesToolCallsFromInput(ai, requestOpts) {
	if (typeof ai.trackToolCall !== "function") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	if (!ctx.userId && !ctx.deviceId || !ctx.sessionId) return;
	if (ctx.skipAutoUserTracking) return;
	const opts = requestOpts;
	if (!opts) return;
	const input = opts.input;
	if (!Array.isArray(input)) return;
	const entries = input;
	const toolCallMap = /* @__PURE__ */ new Map();
	const resultMap = /* @__PURE__ */ new Map();
	for (const entry of entries) if (entry.type === "function_call") {
		const callId = entry.call_id ?? entry.id ?? "";
		const name = entry.name ?? "";
		if (callId) toolCallMap.set(callId, {
			name,
			callId
		});
	} else if (entry.type === "function_call_output") {
		const callId = entry.call_id ?? "";
		const output = typeof entry.output === "string" ? entry.output : JSON.stringify(entry.output ?? "");
		if (callId) resultMap.set(callId, { output });
	}
	for (const [callId, tc] of toolCallMap) {
		const result = resultMap.get(callId);
		if (!result) continue;
		const latencyMs = _consumeToolLatencyMs(ctx.sessionId, callId, ctx.agentId);
		ai.trackToolCall({
			userId: ctx.userId ?? void 0,
			deviceId: ctx.deviceId ?? void 0,
			sessionId: ctx.sessionId ?? "",
			traceId: ctx.traceId,
			agentId: ctx.agentId,
			parentAgentId: ctx.parentAgentId,
			customerOrgId: ctx.customerOrgId,
			env: ctx.env,
			toolName: tc.name,
			output: result.output,
			latencyMs: latencyMs > 0 ? latencyMs : 0,
			success: true
		});
	}
}
function _extractSystemPrompt(req) {
	if (!req) return void 0;
	const messages = req.messages;
	if (!messages?.length) return void 0;
	const systemMsg = messages.find((m) => m.role === "system" || m.role === "developer");
	return systemMsg ? String(systemMsg.content ?? "") : void 0;
}
function _extractAnthropicSystemPrompt(system) {
	if (typeof system === "string") return system;
	if (Array.isArray(system)) return system.map((block) => {
		if (typeof block === "string") return block;
		if (typeof block === "object" && block != null) return String(block.text ?? "");
		return "";
	}).filter(Boolean).join("\n");
}
function _extractToolDefinitions(req) {
	if (!req) return void 0;
	const tools = req.tools;
	return Array.isArray(tools) && tools.length > 0 ? tools : void 0;
}
function _extractAnthropicContent(contentBlocks) {
	let text = "";
	let reasoning;
	const toolCalls = [];
	if (!contentBlocks) return {
		text,
		reasoning,
		toolCalls
	};
	for (const block of contentBlocks) if (block.type === "text") text += String(block.text ?? "");
	else if (block.type === "thinking") reasoning = (reasoning ?? "") + String(block.thinking ?? "");
	else if (block.type === "tool_use") toolCalls.push({
		type: "function",
		id: block.id,
		function: {
			name: String(block.name ?? ""),
			arguments: typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {})
		}
	});
	return {
		text,
		reasoning,
		toolCalls
	};
}
function _extractGeminiToolCalls(respObj) {
	const toolCalls = [];
	const candidates = respObj.candidates;
	if (!candidates) return toolCalls;
	for (const candidate of candidates) {
		const parts = candidate.content?.parts;
		if (!parts) continue;
		for (const part of parts) {
			const functionCall = part.functionCall;
			if (functionCall) toolCalls.push({
				type: "function",
				function: {
					name: String(functionCall.name ?? ""),
					arguments: JSON.stringify(functionCall.args ?? {})
				}
			});
		}
	}
	return toolCalls;
}
function _extractBedrockToolCalls(content) {
	const toolCalls = [];
	if (!content) return toolCalls;
	for (const block of content) if (block.toolUse != null) {
		const tu = block.toolUse;
		toolCalls.push({
			type: "function",
			id: tu.toolUseId,
			function: {
				name: String(tu.name ?? ""),
				arguments: typeof tu.input === "string" ? tu.input : JSON.stringify(tu.input ?? {})
			}
		});
	}
	return toolCalls;
}
/**
* Before an LLM call, extract and track user messages from the request params.
* Mirrors the logic in explicit provider wrappers (WrappedCompletions, etc.)
* but adapted for the monkey-patch path where we only have the raw request args.
*/
function _trackInputUserMessages(ai, requestOpts, providerName) {
	if (typeof ai.trackUserMessage !== "function") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	if (!ctx.userId && !ctx.deviceId || !ctx.sessionId) return;
	if (ctx.skipAutoUserTracking) return;
	const req = requestOpts;
	if (!req) return;
	const messages = req.messages;
	if (!Array.isArray(messages)) return;
	if (providerName === "anthropic") _trackAnthropicUserMessages(ai, messages, ctx);
	else _trackOpenAIUserMessages(ai, messages, ctx);
}
function _emitUserMessage(ai, ctx, content) {
	ai.trackUserMessage({
		userId: ctx.userId ?? void 0,
		deviceId: ctx.deviceId ?? void 0,
		content,
		sessionId: ctx.sessionId ?? "",
		traceId: ctx.traceId,
		agentId: ctx.agentId,
		parentAgentId: ctx.parentAgentId,
		customerOrgId: ctx.customerOrgId,
		env: ctx.env,
		messageSource: ctx.parentAgentId ? "agent" : "user"
	});
}
function _trackOpenAIUserMessages(ai, messages, ctx) {
	const lastReplyIdx = messages.findLastIndex((m) => m?.role === "assistant" || m?.role === "tool");
	const newMessages = messages.slice(lastReplyIdx + 1);
	for (const msg of newMessages) {
		if (msg?.role !== "user") continue;
		const content = msg.content;
		if (typeof content !== "string" || content.length === 0) continue;
		_emitUserMessage(ai, ctx, content);
	}
}
function _trackAnthropicUserMessages(ai, messages, ctx) {
	const lastReplyIdx = messages.findLastIndex((m) => m?.role === "assistant");
	const newMessages = messages.slice(lastReplyIdx + 1);
	for (const msg of newMessages) {
		if (msg?.role !== "user") continue;
		const rawContent = msg.content;
		if (Array.isArray(rawContent)) {
			if (rawContent.some((b) => typeof b === "object" && b != null && b.type === "tool_result")) continue;
		}
		const content = Array.isArray(rawContent) ? rawContent.map((part) => {
			if (typeof part === "string") return part;
			const text = part?.text;
			return typeof text === "string" ? text : "";
		}).join("") : typeof rawContent === "string" ? rawContent : "";
		if (!content) continue;
		_emitUserMessage(ai, ctx, content);
	}
}
const _TOOL_LATENCY_MAX = 1e4;
const _TOOL_LATENCY_TTL_MS = 6e5;
const _toolLatencyRegistry = /* @__PURE__ */ new Map();
function _toolLatencyKey(sessionId, toolUseId, agentId) {
	return `${sessionId ?? ""}|${toolUseId}|${agentId ?? ""}`;
}
let _evictOpCount = 0;
function _evictExpiredToolLatencies(now) {
	for (const [key, entry] of _toolLatencyRegistry) if (now - entry.time > _TOOL_LATENCY_TTL_MS) _toolLatencyRegistry.delete(key);
}
function _recordToolUses(toolCalls, sessionId, agentId) {
	if (!toolCalls || toolCalls.length === 0) return;
	const now = performance.now();
	if (++_evictOpCount % 32 === 0) _evictExpiredToolLatencies(now);
	for (const tc of toolCalls) {
		const id = tc.id ?? "";
		if (!id) continue;
		const key = _toolLatencyKey(sessionId, id, agentId);
		_toolLatencyRegistry.set(key, { time: now });
		while (_toolLatencyRegistry.size > _TOOL_LATENCY_MAX) {
			const oldest = _toolLatencyRegistry.keys().next().value;
			if (oldest != null) _toolLatencyRegistry.delete(oldest);
			else break;
		}
	}
}
function _consumeToolLatencyMs(sessionId, toolUseId, agentId) {
	const key = _toolLatencyKey(sessionId, toolUseId, agentId);
	const entry = _toolLatencyRegistry.get(key);
	if (!entry) return 0;
	_toolLatencyRegistry.delete(key);
	const now = performance.now();
	if (now - entry.time > _TOOL_LATENCY_TTL_MS) return 0;
	return Math.max(0, now - entry.time);
}
function _extractAndTrackToolCalls(ai, requestOpts, providerName) {
	if (typeof ai.trackToolCall !== "function") return;
	const ctx = getActiveContext();
	if (ctx == null) return;
	if (isTrackerManaged()) return;
	if (!ctx.userId && !ctx.deviceId || !ctx.sessionId) return;
	if (ctx.skipAutoUserTracking) return;
	const req = requestOpts;
	if (!req) return;
	const messages = req.messages;
	if (!Array.isArray(messages) || messages.length === 0) return;
	if (providerName === "anthropic") _extractAnthropicToolCalls(ai, messages, ctx);
	else _extractOpenAIToolCalls(ai, messages, ctx);
}
function _extractOpenAIToolCalls(ai, messages, ctx) {
	let lastAssistantIdx = -1;
	for (let i = messages.length - 1; i >= 0; i--) if (messages[i]?.role === "assistant") {
		lastAssistantIdx = i;
		break;
	}
	if (lastAssistantIdx < 0) return;
	const toolCalls = messages[lastAssistantIdx]?.tool_calls;
	if (!Array.isArray(toolCalls) || toolCalls.length === 0) return;
	const toolCallMap = /* @__PURE__ */ new Map();
	for (const tc of toolCalls) {
		const id = tc.id ?? "";
		const fn = tc.function;
		if (id && fn?.name) toolCallMap.set(id, {
			name: String(fn.name),
			args: String(fn.arguments ?? "")
		});
	}
	for (let i = lastAssistantIdx + 1; i < messages.length; i++) {
		const msg = messages[i];
		if (msg?.role !== "tool") break;
		const tcId = msg.tool_call_id ?? "";
		const matched = toolCallMap.get(tcId);
		const toolName = matched?.name ?? "unknown";
		const toolInput = matched?.args;
		const toolOutput = msg.content;
		const latencyMs = _consumeToolLatencyMs(ctx.sessionId, tcId, ctx.agentId);
		ai.trackToolCall({
			userId: ctx.userId ?? void 0,
			deviceId: ctx.deviceId ?? void 0,
			toolName,
			success: true,
			latencyMs,
			input: toolInput,
			output: toolOutput,
			sessionId: ctx.sessionId ?? "",
			traceId: ctx.traceId,
			agentId: ctx.agentId,
			parentAgentId: ctx.parentAgentId,
			customerOrgId: ctx.customerOrgId,
			env: ctx.env
		});
	}
}
function _extractAnthropicToolCalls(ai, messages, ctx) {
	let lastAssistantIdx = -1;
	for (let i = messages.length - 1; i >= 0; i--) if (messages[i]?.role === "assistant") {
		lastAssistantIdx = i;
		break;
	}
	if (lastAssistantIdx < 0) return;
	const content = messages[lastAssistantIdx]?.content;
	if (!Array.isArray(content)) return;
	const toolUseMap = /* @__PURE__ */ new Map();
	for (const block of content) if (block.type === "tool_use") {
		const id = block.id ?? "";
		if (id) toolUseMap.set(id, {
			name: String(block.name ?? "unknown"),
			input: typeof block.input === "string" ? block.input : JSON.stringify(block.input ?? {})
		});
	}
	if (toolUseMap.size === 0) return;
	for (let i = lastAssistantIdx + 1; i < messages.length; i++) {
		const msg = messages[i];
		if (msg?.role !== "user") continue;
		const msgContent = msg.content;
		if (!Array.isArray(msgContent)) continue;
		for (const block of msgContent) {
			if (block.type !== "tool_result") continue;
			const tuId = block.tool_use_id ?? "";
			const matched = toolUseMap.get(tuId);
			const toolName = matched?.name ?? "unknown";
			const toolInput = matched?.input;
			const toolOutput = block.content;
			const isError = block.is_error === true;
			const latencyMs = _consumeToolLatencyMs(ctx.sessionId, tuId, ctx.agentId);
			ai.trackToolCall({
				userId: ctx.userId ?? void 0,
				deviceId: ctx.deviceId ?? void 0,
				toolName,
				success: !isError,
				latencyMs,
				input: toolInput,
				output: toolOutput,
				sessionId: ctx.sessionId ?? "",
				traceId: ctx.traceId,
				agentId: ctx.agentId,
				parentAgentId: ctx.parentAgentId,
				customerOrgId: ctx.customerOrgId,
				env: ctx.env,
				errorMessage: isError ? String(toolOutput ?? "") : void 0
			});
		}
	}
}
function _contextExtras(ctx) {
	const extras = {
		parentAgentId: ctx.parentAgentId ?? void 0,
		customerOrgId: ctx.customerOrgId ?? void 0,
		agentVersion: ctx.agentVersion ?? void 0,
		context: ctx.context ?? void 0,
		groups: ctx.groups ?? void 0
	};
	if (ctx.browserSessionId != null) extras.browserSessionId = ctx.browserSessionId;
	const ep = {};
	if (ctx.idleTimeoutMinutes != null) ep[PROP_IDLE_TIMEOUT_MINUTES] = ctx.idleTimeoutMinutes;
	if (ctx.deviceId && ctx.browserSessionId) ep[PROP_SESSION_REPLAY_ID] = `${ctx.deviceId}/${ctx.browserSessionId}`;
	if (Object.keys(ep).length > 0) extras.eventProperties = ep;
	return extras;
}
/** @internal Test-only: clear the tool latency registry. */
function _resetToolLatencyForTests() {
	_toolLatencyRegistry.clear();
	_evictOpCount = 0;
}

//#endregion
export { _resetToolLatencyForTests, patch, patchAnthropic, patchAzureOpenAI, patchBedrock, patchGemini, patchMistral, patchOpenAI, patchedProviders, unpatch, unpatchAnthropic, unpatchAzureOpenAI, unpatchBedrock, unpatchGemini, unpatchMistral, unpatchOpenAI };
//# sourceMappingURL=patching.js.map