UNPKG

agents

Version:

A home for your AI agents

1,628 lines 66.1 kB
import { t as __DO_NOT_USE_WILL_BREAK__agentContext } from "../../current-agent-DhoDkSnH.js";
import "../../internal_context.js";
import { r as writeSpanAttributes, t as tracer } from "../../cloudflare-Dzvc7V2N.js";
import { AsyncLocalStorage } from "node:async_hooks";
//#region src/observability/ai/read.ts
/** Narrows an unknown value to a string. */
function readString(value) {
	return typeof value === "string" ? value : void 0;
}
/** Narrows an unknown value to a number. */
function readNumber(value) {
	return typeof value === "number" ? value : void 0;
}
/** AI SDK token counts are either a plain number or `{ total?: number, ... }`. */
function readTokenCount(value) {
	if (typeof value === "number") return value;
	if (typeof value === "object" && value !== null) {
		const total = value.total;
		return typeof total === "number" ? total : void 0;
	}
}
/** Reads a numeric sub-field from a nested AI SDK token count object. */
function readNestedTokenField(value, key) {
	if (typeof value !== "object" || value === null) return;
	const nested = value[key];
	return typeof nested === "number" ? nested : void 0;
}
//#endregion
//#region src/observability/genai/attributes.ts
/**
* Local trace attribute keys used by the Cloudflare GenAI projection.
*
* `gen_ai.*` keys follow OpenTelemetry GenAI semantic conventions where they
* exist (Development status — tracked so spec churn stays an internal edit;
* none of these constants are exported from the package). Keys with no
* semconv home live under the `cloudflare.agents.*` vendor namespace — never
* bare top-level keys, never `ai.*` (the Vercel AI SDK's de-facto namespace).
*/
const TraceAttribute = {
	Cloudflare: {
		AIGatewayLogID: "cloudflare.ai_gateway.log.id",
		IntegrationName: "cloudflare.agents.integration.name",
		MetadataPrefix: "cloudflare.agents.metadata.",
		OperationName: "cloudflare.agents.operation.name",
		ResponseFinishReason: "cloudflare.agents.response.finish_reason",
		RuntimeContextPrefix: "cloudflare.agents.runtime_context.",
		ToolApprovalState: "cloudflare.agents.tool.approval.state",
		ToolCount: "cloudflare.agents.tool.count",
		TurnAdmission: "cloudflare.agents.turn.admission",
		TurnChannel: "cloudflare.agents.turn.channel",
		TurnContinuation: "cloudflare.agents.turn.continuation",
		TurnGeneration: "cloudflare.agents.turn.generation",
		TurnRequestID: "cloudflare.agents.turn.request_id",
		TurnTrigger: "cloudflare.agents.turn.trigger",
		UsageTotalTokens: "cloudflare.agents.usage.total_tokens"
	},
	General: { UserID: "user.id" },
	GenAI: {
		AgentID: "gen_ai.agent.id",
		AgentName: "gen_ai.agent.name",
		AgentVersion: "gen_ai.agent.version",
		ConversationID: "gen_ai.conversation.id",
		InputMessages: "gen_ai.input.messages",
		OperationName: "gen_ai.operation.name",
		OperationNameValueChat: "chat",
		OperationNameValueExecuteTool: "execute_tool",
		OperationNameValueInvokeAgent: "invoke_agent",
		OutputMessages: "gen_ai.output.messages",
		OutputType: "gen_ai.output.type",
		ProviderName: "gen_ai.provider.name",
		RequestFrequencyPenalty: "gen_ai.request.frequency_penalty",
		RequestMaxTokens: "gen_ai.request.max_tokens",
		RequestModel: "gen_ai.request.model",
		RequestPresencePenalty: "gen_ai.request.presence_penalty",
		RequestSeed: "gen_ai.request.seed",
		RequestStream: "gen_ai.request.stream",
		RequestTemperature: "gen_ai.request.temperature",
		RequestTopK: "gen_ai.request.top_k",
		RequestTopP: "gen_ai.request.top_p",
		ResponseID: "gen_ai.response.id",
		ResponseModel: "gen_ai.response.model",
		ResponseTimeToFirstChunk: "gen_ai.response.time_to_first_chunk",
		ToolCallArguments: "gen_ai.tool.call.arguments",
		ToolCallID: "gen_ai.tool.call.id",
		ToolCallResult: "gen_ai.tool.call.result",
		ToolName: "gen_ai.tool.name",
		ToolType: "gen_ai.tool.type",
		UsageCacheCreationInputTokens: "gen_ai.usage.cache_creation.input_tokens",
		UsageCacheReadInputTokens: "gen_ai.usage.cache_read.input_tokens",
		UsageInputTokens: "gen_ai.usage.input_tokens",
		UsageOutputTokens: "gen_ai.usage.output_tokens",
		UsageReasoningOutputTokens: "gen_ai.usage.reasoning.output_tokens"
	}
};
//#endregion
//#region src/observability/genai/telemetry.ts
/**
* Builds a semconv-formula span name (`"{operation} {target}"`), falling back
* to the bare operation when the target is unavailable or the combined name
* exceeds the Workers Observability 64 UTF-8-byte budget. The full target
* remains available as an attribute; the stable query key is always
* `gen_ai.operation.name`, never the span name.
*/
function spanName(operation, target) {
	if (!target) return operation;
	const name = `${operation} ${target}`;
	return new TextEncoder().encode(name).length <= 64 ? name : operation;
}
/**
* Normalizes an AI SDK provider identifier to the semconv
* `gen_ai.provider.name` enum where a member exists: sub-provider suffixes
* are stripped (`anthropic.messages` → `anthropic`) and known aliases mapped.
* Unknown providers pass through verbatim (semconv sanctions custom values).
*/
function normalizeProviderName(provider) {
	if (provider === void 0) return;
	const lower = provider.toLowerCase();
	for (const [prefix, value] of [
		["google.vertex", "gcp.vertex_ai"],
		["google.generative-ai", "gcp.gemini"],
		["google-vertex", "gcp.vertex_ai"],
		["amazon-bedrock", "aws.bedrock"],
		["azure-openai", "azure.ai.openai"],
		["anthropic", "anthropic"],
		["openai", "openai"],
		["azure", "azure.ai.inference"],
		["google", "gcp.gemini"],
		["mistral", "mistral_ai"],
		["cohere", "cohere"],
		["bedrock", "aws.bedrock"],
		["groq", "groq"],
		["deepseek", "deepseek"],
		["perplexity", "perplexity"],
		["xai", "x_ai"]
	]) if (lower === prefix || lower.startsWith(`${prefix}.`) || lower.startsWith(`${prefix}-`)) return value;
	return provider;
}
/**
* Reserved telemetry-metadata keys that map to dedicated attributes on the
* operation root span. Everything else scalar passes through under
* `cloudflare.agents.metadata.{key}`; identity keys are consumed by
* SemanticContext extraction and never passed through.
*/
const RESERVED_METADATA_ATTRIBUTES = {
	[TraceAttribute.Cloudflare.TurnAdmission]: TraceAttribute.Cloudflare.TurnAdmission,
	[TraceAttribute.Cloudflare.TurnChannel]: TraceAttribute.Cloudflare.TurnChannel,
	[TraceAttribute.Cloudflare.TurnContinuation]: TraceAttribute.Cloudflare.TurnContinuation,
	[TraceAttribute.Cloudflare.TurnGeneration]: TraceAttribute.Cloudflare.TurnGeneration,
	[TraceAttribute.Cloudflare.TurnRequestID]: TraceAttribute.Cloudflare.TurnRequestID,
	[TraceAttribute.Cloudflare.TurnTrigger]: TraceAttribute.Cloudflare.TurnTrigger,
	[TraceAttribute.General.UserID]: TraceAttribute.General.UserID
};
const CONSUMED_METADATA_KEYS = /* @__PURE__ */ new Set([
	"agentId",
	"agentName",
	"agentVersion",
	"conversationId",
	"gen_ai.agent.id",
	"gen_ai.agent.name",
	"gen_ai.agent.version",
	"gen_ai.conversation.id"
]);
/**
* Projects a per-call telemetry record onto root span attributes: reserved
* keys map to their dedicated attributes, any other SCALAR entry passes
* through under `passthroughPrefix`, and object/array values are dropped
* (scalar-only attribute rule).
*
* v6 passes `experimental_telemetry.metadata` and keeps the default prefix;
* v7 has no metadata option and passes the included subset of `runtimeContext`
* under its own prefix. Reserved keys land on the same attribute either way,
* so turn identity does not move namespace between SDK majors.
*/
function metadataAttributes(metadata, passthroughPrefix = TraceAttribute.Cloudflare.MetadataPrefix) {
	if (metadata === void 0) return {};
	const attributes = {};
	for (const [key, value] of Object.entries(metadata)) {
		if (CONSUMED_METADATA_KEYS.has(key)) continue;
		if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") continue;
		const reserved = Object.hasOwn(RESERVED_METADATA_ATTRIBUTES, key) ? RESERVED_METADATA_ATTRIBUTES[key] : void 0;
		attributes[reserved ?? `${passthroughPrefix}${key}`] = value;
	}
	return attributes;
}
/**
* Cheap root-span name for an agent operation: needs only the agent name (the
* one value instrumentation may read before the sampling check), never the
* full attribute spec.
*/
function operationSpanName(agentName) {
	return spanName(TraceAttribute.GenAI.OperationNameValueInvokeAgent, agentName);
}
function semanticContextAttributes(context) {
	return {
		[TraceAttribute.GenAI.AgentID]: context?.agentId,
		[TraceAttribute.GenAI.AgentName]: context?.agentName,
		[TraceAttribute.GenAI.AgentVersion]: context?.agentVersion,
		[TraceAttribute.GenAI.ConversationID]: context?.conversationId
	};
}
/** Builds the root span for an SDK operation such as generateText or streamText. */
function operationSpan(input) {
	return {
		attributes: {
			...input.attributes,
			[TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
			[TraceAttribute.Cloudflare.OperationName]: input.operation,
			...semanticContextAttributes(input.context),
			[TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueInvokeAgent,
			[TraceAttribute.GenAI.ProviderName]: normalizeProviderName(input.provider),
			...requestAttributes(input.request, input.model)
		},
		name: spanName(TraceAttribute.GenAI.OperationNameValueInvokeAgent, input.context?.agentName)
	};
}
/** Builds the child span for an underlying model call. */
function modelCallSpan(input) {
	return {
		attributes: {
			...input.attributes,
			[TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
			[TraceAttribute.Cloudflare.OperationName]: input.operation,
			...semanticContextAttributes(input.context),
			[TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueChat,
			[TraceAttribute.GenAI.ProviderName]: normalizeProviderName(input.provider),
			...requestAttributes(input.request, input.model)
		},
		name: spanName(TraceAttribute.GenAI.OperationNameValueChat, input.model)
	};
}
function requestAttributes(request, model) {
	return {
		[TraceAttribute.GenAI.OutputType]: request?.outputType,
		[TraceAttribute.GenAI.RequestFrequencyPenalty]: request?.frequencyPenalty,
		[TraceAttribute.GenAI.RequestMaxTokens]: request?.maxTokens,
		[TraceAttribute.GenAI.RequestModel]: model,
		[TraceAttribute.GenAI.RequestPresencePenalty]: request?.presencePenalty,
		[TraceAttribute.GenAI.RequestSeed]: request?.seed,
		[TraceAttribute.GenAI.RequestStream]: request?.stream === true ? true : void 0,
		[TraceAttribute.GenAI.RequestTemperature]: request?.temperature,
		[TraceAttribute.GenAI.RequestTopK]: request?.topK,
		[TraceAttribute.GenAI.RequestTopP]: request?.topP
	};
}
/** Builds the child span for a tool execution. */
function toolCallSpan(input) {
	return {
		attributes: {
			[TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
			[TraceAttribute.Cloudflare.OperationName]: input.operation,
			...semanticContextAttributes(input.context),
			[TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueExecuteTool,
			[TraceAttribute.GenAI.ToolCallID]: input.toolCallId,
			[TraceAttribute.GenAI.ToolName]: input.toolName,
			[TraceAttribute.GenAI.ToolType]: "function"
		},
		name: spanName(TraceAttribute.GenAI.OperationNameValueExecuteTool, input.toolName)
	};
}
/** Builds a bounded child span for one tool-approval lifecycle segment. */
function toolApprovalSpan(input) {
	return {
		attributes: {
			[TraceAttribute.Cloudflare.IntegrationName]: "ai-sdk",
			[TraceAttribute.Cloudflare.OperationName]: "tool.approval",
			[TraceAttribute.Cloudflare.ToolApprovalState]: input.state,
			...semanticContextAttributes(input.context),
			[TraceAttribute.GenAI.OperationName]: TraceAttribute.GenAI.OperationNameValueExecuteTool,
			[TraceAttribute.GenAI.ToolCallID]: input.toolCallId,
			[TraceAttribute.GenAI.ToolName]: input.toolName,
			[TraceAttribute.GenAI.ToolType]: "function"
		},
		name: spanName("tool_approval", input.toolName)
	};
}
/** Projects a completed model operation into canonical finish attributes. */
function finishAttributes(input) {
	return {
		[TraceAttribute.Cloudflare.AIGatewayLogID]: input.aiGatewayLogId,
		[TraceAttribute.Cloudflare.ResponseFinishReason]: input.finishReason,
		[TraceAttribute.Cloudflare.ToolCount]: input.toolCallCount,
		[TraceAttribute.Cloudflare.UsageTotalTokens]: totalTokens(input.usage),
		[TraceAttribute.GenAI.ResponseID]: input.response?.id,
		[TraceAttribute.GenAI.ResponseModel]: input.response?.model,
		[TraceAttribute.GenAI.ResponseTimeToFirstChunk]: input.timeToFirstChunkSeconds,
		[TraceAttribute.GenAI.UsageCacheCreationInputTokens]: input.usage?.cacheCreationInputTokens,
		[TraceAttribute.GenAI.UsageCacheReadInputTokens]: input.usage?.cacheReadInputTokens,
		[TraceAttribute.GenAI.UsageInputTokens]: input.usage?.inputTokens,
		[TraceAttribute.GenAI.UsageOutputTokens]: input.usage?.outputTokens,
		[TraceAttribute.GenAI.UsageReasoningOutputTokens]: input.usage?.reasoningTokens
	};
}
/** Attribute projection for an AI Gateway log reference discovered on error. */
function aiGatewayLogAttributes(aiGatewayLogId) {
	return { [TraceAttribute.Cloudflare.AIGatewayLogID]: aiGatewayLogId };
}
function totalTokens(usage) {
	if (usage?.totalTokens !== void 0) return usage.totalTokens;
	return usage?.inputTokens !== void 0 && usage.outputTokens !== void 0 ? usage.inputTokens + usage.outputTokens : void 0;
}
//#endregion
//#region src/observability/ai/wrapper/extract.ts
function finishAttributesFromResult(result, options = {}) {
	return finishAttributes({
		aiGatewayLogId: options.aiGatewayLogId,
		finishReason: extractFinishReason(result),
		response: options.includeResponse ? extractResponseInfo(result) : void 0,
		toolCallCount: extractToolCallCount(result),
		usage: extractAISDKTokenUsage(result)
	});
}
function extractRequestSummary(params, operation) {
	return {
		frequencyPenalty: readNumber(params.frequencyPenalty),
		maxTokens: readNumber(params.maxOutputTokens ?? params.maxTokens),
		outputType: operation === "generateObject" || operation === "streamObject" ? "json" : "text",
		presencePenalty: readNumber(params.presencePenalty),
		seed: readNumber(params.seed),
		stream: operation === "streamText" || operation === "streamObject",
		temperature: readNumber(params.temperature),
		topK: readNumber(params.topK),
		topP: readNumber(params.topP)
	};
}
/** Extracts model identity from an AI SDK model object. */
function extractModelInfo(value) {
	if (typeof value === "string") return value.length > 0 ? {
		modelId: value,
		provider: void 0
	} : void 0;
	if (typeof value !== "object" || value === null) return;
	const record = value;
	const modelId = typeof record.modelId === "string" ? record.modelId : void 0;
	const provider = typeof record.provider === "string" ? record.provider : void 0;
	if (modelId === void 0 && provider === void 0) return;
	return {
		modelId,
		provider
	};
}
/**
* Extracts token usage from an AI SDK result or stream chunk across supported
* majors. Token counts may be plain numbers or nested objects such as
* `{ total, cacheRead, cacheWrite }` / `{ total, reasoning }`.
*/
function extractAISDKTokenUsage(value) {
	if (typeof value !== "object" || value === null) return;
	const record = value;
	const raw = record.totalUsage ?? record.usage;
	if (typeof raw !== "object" || raw === null) return;
	const usage = raw;
	const inputTokens = readTokenCount(usage.inputTokens);
	const outputTokens = readTokenCount(usage.outputTokens);
	const totalTokens = readNumber(usage.totalTokens);
	const cacheReadInputTokens = readNestedTokenField(usage.inputTokenDetails, "cacheReadTokens") ?? readNestedTokenField(usage.inputTokens, "cacheRead") ?? readNumber(usage.cachedInputTokens);
	const cacheCreationInputTokens = readNestedTokenField(usage.inputTokenDetails, "cacheWriteTokens") ?? readNestedTokenField(usage.inputTokens, "cacheWrite");
	const reasoningTokens = readNestedTokenField(usage.outputTokenDetails, "reasoningTokens") ?? readNestedTokenField(usage.outputTokens, "reasoning") ?? readNumber(usage.reasoningTokens);
	if (inputTokens === void 0 && outputTokens === void 0 && totalTokens === void 0 && cacheReadInputTokens === void 0 && cacheCreationInputTokens === void 0 && reasoningTokens === void 0) return;
	return {
		...cacheCreationInputTokens !== void 0 ? { cacheCreationInputTokens } : {},
		...cacheReadInputTokens !== void 0 ? { cacheReadInputTokens } : {},
		...inputTokens !== void 0 ? { inputTokens } : {},
		...outputTokens !== void 0 ? { outputTokens } : {},
		...reasoningTokens !== void 0 ? { reasoningTokens } : {},
		...totalTokens !== void 0 ? { totalTokens } : {}
	};
}
function extractToolCallCount(value) {
	if (typeof value !== "object" || value === null) return;
	const toolCalls = value.toolCalls;
	return Array.isArray(toolCalls) && toolCalls.length > 0 ? toolCalls.length : void 0;
}
/**
* Reads a finish reason from an AI SDK result or `finish`-type stream chunk.
*/
function extractFinishReason(value) {
	if (typeof value !== "object" || value === null) return;
	const finishReason = value.finishReason;
	if (typeof finishReason === "string") return finishReason;
	if (typeof finishReason === "object" && finishReason !== null) {
		const unified = finishReason.unified;
		return typeof unified === "string" ? unified : void 0;
	}
}
function extractResponseInfo(value) {
	if (typeof value !== "object" || value === null) return;
	const record = value;
	if (record.type === "response-metadata") {
		const id = readString(record.id);
		const model = readString(record.modelId);
		if (id === void 0 && model === void 0) return;
		return {
			...id !== void 0 ? { id } : {},
			...model !== void 0 ? { model } : {}
		};
	}
	const response = typeof record.response === "object" && record.response !== null ? record.response : void 0;
	const id = readString(record.responseId ?? response?.id);
	const model = readString(record.responseModel ?? response?.modelId ?? response?.model);
	if (id === void 0 && model === void 0) return;
	return {
		...id !== void 0 ? { id } : {},
		...model !== void 0 ? { model } : {}
	};
}
//#endregion
//#region src/observability/ai/ai-gateway.ts
const MAX_AI_GATEWAY_LOG_ID_BYTES = 256;
const AI_GATEWAY_CONTAINER_KEYS = /* @__PURE__ */ new Set([
	"aigateway",
	"binding",
	"cause",
	"cloudflare",
	"config",
	"context",
	"error",
	"gateway",
	"providermetadata",
	"rawresponse",
	"response",
	"workersai"
]);
/**
* Reads an AI Gateway log id from explicit provider surfaces only: response
* headers, provider metadata, gateway errors, or a Workers AI binding. The
* walk is bounded and uses data-property descriptors, so telemetry cannot get
* stuck on cycles or invoke arbitrary application getters. Unknown shapes fail
* open and simply omit the attribute.
*/
function extractAIGatewayLogId(value) {
	const seen = /* @__PURE__ */ new Set();
	let visited = 0;
	const visit = (candidate, depth, gatewayScoped, providerMetadata, responseScoped) => {
		if (candidate === null || candidate === void 0 || depth > 6 || typeof candidate !== "object" && typeof candidate !== "function" || seen.has(candidate) || visited >= 200) return;
		seen.add(candidate);
		visited += 1;
		if (typeof Response !== "undefined" && candidate instanceof Response) return readHeaderLogId(candidate.headers);
		let descriptors;
		try {
			descriptors = Object.getOwnPropertyDescriptors(candidate);
		} catch {
			return;
		}
		const objectName = dataString(descriptors.name);
		const scopedHere = gatewayScoped || objectName !== void 0 && isGatewayKey(normalizeKey(objectName));
		for (const [key, descriptor] of Object.entries(descriptors)) {
			if (!("value" in descriptor)) continue;
			const normalizedKey = normalizeKey(key);
			if (normalizedKey === "cfaiglogid" || normalizedKey === "aigatewaylogid") {
				const logId = boundedAIGatewayLogId(descriptor.value);
				if (logId !== void 0) return logId;
			}
			if (normalizedKey === "responseheaders" || normalizedKey === "headers" && responseScoped) {
				const logId = readHeaderLogId(descriptor.value);
				if (logId !== void 0) return logId;
			}
			if (normalizedKey === "logid" && scopedHere) {
				const logId = boundedAIGatewayLogId(descriptor.value);
				if (logId !== void 0) return logId;
			}
		}
		for (const [key, descriptor] of Object.entries(descriptors)) {
			if (!("value" in descriptor)) continue;
			const normalizedKey = normalizeKey(key);
			if (!providerMetadata && !AI_GATEWAY_CONTAINER_KEYS.has(normalizedKey)) continue;
			const nested = visit(descriptor.value, depth + 1, scopedHere || isGatewayKey(normalizedKey), providerMetadata || normalizedKey === "providermetadata", normalizedKey === "response" || normalizedKey === "rawresponse");
			if (nested !== void 0) return nested;
		}
	};
	return visit(value, 0, false, false, false);
}
/**
* workers-ai-provider currently exposes the gateway ID on its `Ai` binding,
* not in the LanguageModel result. Clone only that known runtime shape and
* proxy `binding.run` so the ID is captured as that call settles instead of
* reading the binding's mutable latest value later when the span ends.
*/
function captureAIGatewayLogFromModel(model, provider) {
	let logId;
	const capture = {
		model,
		get: () => logId,
		reset: () => {
			logId = void 0;
		}
	};
	if (!provider?.toLowerCase().startsWith("workersai")) return capture;
	try {
		const modelDescriptors = Object.getOwnPropertyDescriptors(model);
		const configDescriptor = modelDescriptors.config;
		if (!configDescriptor || !("value" in configDescriptor)) return capture;
		const config = configDescriptor.value;
		if (typeof config !== "object" || config === null) return capture;
		const configDescriptors = Object.getOwnPropertyDescriptors(config);
		const bindingDescriptor = configDescriptors.binding;
		if (!bindingDescriptor || !("value" in bindingDescriptor)) return capture;
		const binding = bindingDescriptor.value;
		if (typeof binding !== "object" || binding === null || !("aiGatewayLogId" in binding) || typeof binding.run !== "function") return capture;
		const bindingProxy = new Proxy(binding, { get(target, property, receiver) {
			if (property !== "run") return Reflect.get(target, property, receiver);
			return (...args) => {
				const run = Reflect.get(target, property, target);
				const previousLogId = extractAIGatewayLogId(target);
				logId = void 0;
				const captureResult = (result) => {
					const resultLogId = extractAIGatewayLogId(result);
					const currentLogId = extractAIGatewayLogId(target);
					logId = resultLogId ?? (currentLogId !== previousLogId ? currentLogId : void 0);
				};
				try {
					return Promise.resolve(Reflect.apply(run, target, args)).then((result) => {
						captureResult(result);
						return result;
					}, (cause) => {
						captureResult(cause);
						throw cause;
					});
				} catch (cause) {
					captureResult(cause);
					throw cause;
				}
			};
		} });
		const configClone = Object.create(Object.getPrototypeOf(config), {
			...configDescriptors,
			binding: {
				...bindingDescriptor,
				value: bindingProxy
			}
		});
		capture.model = Object.create(Object.getPrototypeOf(model), {
			...modelDescriptors,
			config: {
				...configDescriptor,
				value: configClone
			}
		});
	} catch {}
	return capture;
}
function readHeaderLogId(value) {
	if (isHeadersLike(value)) try {
		return boundedAIGatewayLogId(value.get("cf-aig-log-id"));
	} catch {
		return;
	}
	if (typeof value !== "object" || value === null) return;
	let descriptors;
	try {
		descriptors = Object.getOwnPropertyDescriptors(value);
	} catch {
		return;
	}
	for (const [key, descriptor] of Object.entries(descriptors)) if ("value" in descriptor && key.toLowerCase() === "cf-aig-log-id") return boundedAIGatewayLogId(descriptor.value);
}
function isHeadersLike(value) {
	if (typeof value !== "object" || value === null) return false;
	try {
		return "get" in value && typeof value.get === "function";
	} catch {
		return false;
	}
}
function isGatewayKey(value) {
	return value.includes("gateway") || value.includes("aig") || value.includes("workersai") || value === "cloudflare";
}
function normalizeKey(value) {
	return value.toLowerCase().replaceAll(/[-_.]/g, "");
}
function dataString(descriptor) {
	return descriptor && "value" in descriptor ? nonEmptyString(descriptor.value) : void 0;
}
function nonEmptyString(value) {
	return typeof value === "string" && value.length > 0 ? value : void 0;
}
function boundedAIGatewayLogId(value) {
	const id = nonEmptyString(value);
	return id !== void 0 && new TextEncoder().encode(id).length <= MAX_AI_GATEWAY_LOG_ID_BYTES ? id : void 0;
}
//#endregion
//#region src/observability/ai/content.ts
const MAX_ATTRIBUTE_BYTES = 28 * 1024;
const PROTECTED_HEAD_MESSAGES = 2;
function inputMessageAttributes(value, enabled) {
	if (!enabled || typeof value !== "object" || value === null) return {};
	const record = value;
	const messages = Array.isArray(record.prompt) ? record.prompt : Array.isArray(record.messages) ? record.messages : typeof record.prompt === "string" ? [{
		role: "user",
		content: record.prompt
	}] : void 0;
	return { [TraceAttribute.GenAI.InputMessages]: serializeMessages(messages === void 0 ? void 0 : formatInputMessages(messages)) };
}
function outputMessageAttributes(value, enabled) {
	if (!enabled || typeof value !== "object" || value === null) return {};
	const record = value;
	const parts = outputParts(record);
	const finishReason = readFinishReason(record);
	return outputMessageAttributesFrom(parts.length > 0 || finishReason !== void 0 ? [outputMessage(parts, finishReason)] : void 0);
}
function outputMessageAttributesFrom(messages) {
	return { [TraceAttribute.GenAI.OutputMessages]: serializeMessages(messages) };
}
function toolInputAttributes(value, enabled) {
	return enabled ? { [TraceAttribute.GenAI.ToolCallArguments]: serialize(value) } : {};
}
function toolOutputAttributes(value, enabled) {
	return enabled ? { [TraceAttribute.GenAI.ToolCallResult]: serialize(value) } : {};
}
function createStreamMessages() {
	let text = "";
	let reasoning = "";
	const toolParts = [];
	return {
		messages(finishReason) {
			const parts = [
				...reasoning ? [{
					type: "reasoning",
					content: reasoning
				}] : [],
				...text ? [{
					type: "text",
					content: text
				}] : [],
				...toolParts
			];
			return parts.length > 0 || finishReason !== void 0 ? [outputMessage(parts, finishReason)] : void 0;
		},
		observe(chunk) {
			if (typeof chunk !== "object" || chunk === null) return;
			const record = chunk;
			const delta = record.text ?? record.delta;
			if (record.type === "text-delta" && typeof delta === "string") text += delta;
			else if (record.type === "reasoning-delta" && typeof delta === "string") reasoning += delta;
			else if (record.type === "tool-call" || record.type === "tool-result") {
				const part = formatMessagePart(record);
				if (part !== void 0) toolParts.push(part);
			}
		}
	};
}
function formatInputMessages(messages) {
	const formatted = [];
	for (const message of messages) {
		const next = formatInputMessage(message);
		if (next !== void 0) formatted.push(next);
	}
	return formatted;
}
function formatInputMessage(value) {
	if (typeof value !== "object" || value === null) return void 0;
	const record = value;
	if (typeof record.role !== "string") return void 0;
	const parts = (Array.isArray(record.parts) ? record.parts : Array.isArray(record.content) ? record.content : typeof record.content === "string" ? [record.content] : []).map(formatMessagePart).filter((part) => part !== void 0);
	const name = typeof record.name === "string" ? record.name : void 0;
	return {
		role: record.role,
		parts,
		...name !== void 0 ? { name } : {}
	};
}
function outputParts(record) {
	if (Array.isArray(record.content)) return record.content.map(formatMessagePart).filter((part) => part !== void 0);
	const parts = [];
	appendReasoningParts(parts, record.reasoning);
	if (typeof record.text === "string" && record.text.length > 0) parts.push({
		type: "text",
		content: record.text
	});
	if (Array.isArray(record.toolCalls)) for (const toolCall of record.toolCalls) {
		const part = formatMessagePart(toolCall);
		if (part !== void 0) parts.push(part);
	}
	return parts;
}
function appendReasoningParts(parts, reasoning) {
	if (typeof reasoning === "string" && reasoning.length > 0) {
		parts.push({
			type: "reasoning",
			content: reasoning
		});
		return;
	}
	if (!Array.isArray(reasoning)) return;
	for (const entry of reasoning) {
		if (typeof entry !== "object" || entry === null) continue;
		const text = entry.text;
		if (typeof text === "string" && text.length > 0) parts.push({
			type: "reasoning",
			content: text
		});
	}
}
function formatMessagePart(value) {
	if (typeof value === "string") return {
		type: "text",
		content: value
	};
	if (typeof value !== "object" || value === null) return void 0;
	const record = value;
	if (typeof record.type !== "string") return void 0;
	switch (record.type) {
		case "text":
		case "reasoning": {
			const content = record.content ?? record.text;
			return typeof content === "string" ? {
				type: record.type,
				content
			} : void 0;
		}
		case "tool-call":
		case "tool_call": return formatToolCall(record);
		case "tool-result":
		case "tool_result":
		case "tool_call_response": return formatToolCallResponse(record);
		default: return { type: record.type.replaceAll("-", "_") };
	}
}
function formatToolCall(record) {
	const name = record.name ?? record.toolName;
	if (typeof name !== "string") return void 0;
	const id = record.id ?? record.toolCallId;
	const args = record.arguments ?? record.input;
	return {
		type: "tool_call",
		...typeof id === "string" ? { id } : {},
		name,
		...args !== void 0 ? { arguments: parseJson(args) } : {}
	};
}
function formatToolCallResponse(record) {
	const id = record.id ?? record.toolCallId;
	const rawResponse = record.response ?? record.output ?? record.result ?? null;
	return {
		type: "tool_call_response",
		...typeof id === "string" ? { id } : {},
		response: toolResponse(rawResponse)
	};
}
function toolResponse(value) {
	if (typeof value !== "object" || value === null) return value;
	const record = value;
	switch (record.type) {
		case "text":
		case "error-text":
		case "json":
		case "error-json":
		case "content": return record.value ?? null;
		case "execution-denied": return {
			denied: true,
			...typeof record.reason === "string" ? { reason: record.reason } : {}
		};
		default: return value;
	}
}
function outputMessage(parts, finishReason) {
	return {
		role: "assistant",
		parts,
		finish_reason: normalizeFinishReason(finishReason ?? "unknown")
	};
}
function readFinishReason(record) {
	const value = record.finishReason ?? record.finish_reason;
	if (typeof value === "string") return value;
	if (typeof value !== "object" || value === null) return void 0;
	const unified = value.unified;
	return typeof unified === "string" ? unified : void 0;
}
function normalizeFinishReason(value) {
	switch (value) {
		case "content-filter": return "content_filter";
		case "tool-calls":
		case "tool_calls": return "tool_call";
		case "other":
		case "unknown": return "stop";
		default: return value;
	}
}
function parseJson(value) {
	if (typeof value !== "string") return value;
	try {
		return JSON.parse(value);
	} catch {
		return value;
	}
}
function serializeMessages(messages) {
	if (messages === void 0) return void 0;
	const kept = [...messages];
	while (true) {
		const json = stringify(kept);
		if (json === void 0) return void 0;
		if (byteLength(json) <= MAX_ATTRIBUTE_BYTES) return json;
		if (kept.length <= PROTECTED_HEAD_MESSAGES) return void 0;
		kept.splice(PROTECTED_HEAD_MESSAGES, 1);
	}
}
function serialize(value) {
	const json = stringify(value);
	return json !== void 0 && byteLength(json) <= MAX_ATTRIBUTE_BYTES ? json : void 0;
}
function stringify(value) {
	if (value === void 0) return void 0;
	try {
		return JSON.stringify(value);
	} catch {
		return;
	}
}
function byteLength(value) {
	return new TextEncoder().encode(value).length;
}
//#endregion
//#region src/observability/ai/wrapper/streams.ts
function finishWhenStreamCompletes(result, span, options = {}) {
	return patchStreamFields(result, {
		onComplete: (summary) => {
			span.finish(finishAttributesFromStreamSummary(summary, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
		},
		onError: (cause, observedAIGatewayLogId, observed) => {
			if (options.includeAIGatewayLog) writeSpanAttributes(span, aiGatewayLogAttributes(observedAIGatewayLogId ?? extractAIGatewayLogId(cause) ?? options.aiGatewayLogId));
			if (observed !== void 0) writeSpanAttributes(span, finishAttributesFromStreamSummary(observed, options.includeResponse === true, options.includeAIGatewayLog === true, options.aiGatewayLogId, options.storeMessages === true));
			span.fail(cause);
		}
	}, options.startedAtMs, options.includeAIGatewayLog ? options.aiGatewayLogId : void 0, options.storeMessages === true);
}
function finishAttributesFromStreamSummary(summary, includeResponse, includeAIGatewayLog, initialAIGatewayLogId, storeMessages) {
	return {
		...finishAttributes({
			aiGatewayLogId: includeAIGatewayLog ? summary?.aiGatewayLogId ?? initialAIGatewayLogId : void 0,
			finishReason: summary?.finishReason,
			response: includeResponse ? summary?.response : void 0,
			timeToFirstChunkSeconds: summary?.timeToFirstChunkSeconds,
			toolCallCount: summary?.toolCallCount,
			usage: summary?.usage
		}),
		...outputMessageAttributesFrom(storeMessages ? summary?.outputMessages : void 0)
	};
}
function patchStreamFields(result, hooks, startedAtMs, aiGatewayLogId, storeMessages) {
	if (typeof result !== "object" || result === null) {
		hooks.onComplete(void 0);
		return result;
	}
	const record = result;
	let patchedAny = false;
	let closed = false;
	const completeOnce = (summary) => {
		if (closed) return;
		closed = true;
		hooks.onComplete(summary);
	};
	const errorOnce = (cause, observedAIGatewayLogId, observed) => {
		if (closed) return;
		closed = true;
		hooks.onError(cause, observedAIGatewayLogId, observed);
	};
	try {
		if (isReadableStream(record.baseStream)) {
			Object.defineProperty(record, "baseStream", {
				configurable: true,
				enumerable: true,
				value: wrapReadableStream(record.baseStream, {
					onComplete: completeOnce,
					onError: errorOnce
				}, startedAtMs, aiGatewayLogId, storeMessages),
				writable: true
			});
			return result;
		}
		const streamField = findStreamField(record, [
			"partialObjectStream",
			"textStream",
			"fullStream",
			"stream"
		]);
		if (streamField) {
			Object.defineProperty(record, streamField.field, {
				configurable: true,
				enumerable: true,
				value: streamField.kind === "readable" ? wrapReadableStream(streamField.stream, {
					onComplete: completeOnce,
					onError: errorOnce
				}, startedAtMs, aiGatewayLogId, storeMessages) : wrapAsyncIterable(streamField.stream, {
					onComplete: completeOnce,
					onError: errorOnce
				}, startedAtMs, aiGatewayLogId, storeMessages),
				writable: true
			});
			patchedAny = true;
		}
	} catch {
		patchedAny = false;
	}
	if (!patchedAny) {
		hooks.onComplete(void 0);
		return result;
	}
	return result;
}
function findStreamField(result, candidateFields) {
	for (const field of candidateFields) try {
		const stream = result[field];
		if (isReadableStream(stream)) return {
			field,
			kind: "readable",
			stream
		};
		if (isAsyncIterable$1(stream)) return {
			field,
			kind: "asyncIterable",
			stream
		};
	} catch {}
}
function wrapReadableStream(stream, hooks, startedAtMs, aiGatewayLogId, storeMessages) {
	let reader;
	const state = createStreamState(hooks, startedAtMs, aiGatewayLogId, storeMessages);
	return new ReadableStream({
		async pull(controller) {
			reader ??= stream.getReader();
			try {
				const result = await reader.read();
				if (state.closed) return;
				if (result.done) {
					state.complete();
					controller.close();
					releaseReader();
					return;
				}
				state.observeChunk(result.value);
				controller.enqueue(result.value);
			} catch (cause) {
				if (!state.closed) {
					state.fail(cause);
					controller.error(cause);
				}
				releaseReader();
			}
		},
		async cancel(reason) {
			state.cancel();
			try {
				if (reader) {
					await reader.cancel(reason);
					return;
				}
				await stream.cancel(reason);
			} catch (cause) {
				state.fail(cause);
				throw cause;
			} finally {
				releaseReader();
			}
		}
	});
	function releaseReader() {
		if (!reader) return;
		try {
			reader.releaseLock();
		} catch {} finally {
			reader = void 0;
		}
	}
}
function wrapAsyncIterable(stream, hooks, startedAtMs, aiGatewayLogId, storeMessages) {
	return { async *[Symbol.asyncIterator]() {
		const state = createStreamState(hooks, startedAtMs, aiGatewayLogId, storeMessages);
		try {
			for await (const chunk of stream) {
				state.observeChunk(chunk);
				yield chunk;
			}
			state.complete();
		} catch (cause) {
			state.fail(cause);
			throw cause;
		} finally {
			state.cancel();
		}
	} };
}
function createStreamState(hooks, startedAtMs, initialAIGatewayLogId, storeMessages) {
	let closed = false;
	let aiGatewayLogId = initialAIGatewayLogId;
	let finishReason;
	let toolCallCount = 0;
	let usage;
	let response;
	let observedError;
	let observedAbort = false;
	let firstChunkAtMs;
	const output = createStreamMessages();
	/** What the stream reported before it stopped, complete or not. */
	const observedSummary = () => streamSummaryFromParts({
		aiGatewayLogId,
		finishReason,
		outputMessages: storeMessages ? output.messages(finishReason) : void 0,
		response,
		timeToFirstChunkSeconds: firstChunkAtMs === void 0 || startedAtMs === void 0 ? void 0 : (firstChunkAtMs - startedAtMs) / 1e3,
		toolCallCount,
		usage
	});
	const settleObserved = () => {
		if (observedError) {
			hooks.onError(observedError.cause, aiGatewayLogId, observedSummary());
			return true;
		}
		if (observedAbort) {
			hooks.onError({ name: "AbortError" }, aiGatewayLogId, observedSummary());
			return true;
		}
		return false;
	};
	return {
		get closed() {
			return closed;
		},
		cancel() {
			if (closed) return;
			closed = true;
			if (settleObserved()) return;
			hooks.onComplete(void 0);
		},
		complete() {
			if (closed) return;
			closed = true;
			if (settleObserved()) return;
			hooks.onComplete(streamSummaryFromParts({
				aiGatewayLogId,
				finishReason,
				outputMessages: storeMessages ? output.messages(finishReason) : void 0,
				response,
				timeToFirstChunkSeconds: firstChunkAtMs === void 0 || startedAtMs === void 0 ? void 0 : (firstChunkAtMs - startedAtMs) / 1e3,
				toolCallCount,
				usage
			}));
		},
		fail(cause) {
			if (closed) return;
			closed = true;
			hooks.onError(cause, aiGatewayLogId);
		},
		observeChunk(rawChunk) {
			firstChunkAtMs ??= Date.now();
			const chunk = unwrapChunkEnvelope(rawChunk);
			aiGatewayLogId = extractAIGatewayLogId(chunk) ?? aiGatewayLogId;
			if (storeMessages) output.observe(chunk);
			if (isErrorChunk(chunk)) observedError = { cause: chunk.error };
			if (isAbortChunk(chunk)) observedAbort = true;
			if (isToolCallChunk(chunk)) toolCallCount += 1;
			finishReason = extractFinishReason(chunk) ?? finishReason;
			usage = extractAISDKTokenUsage(chunk) ?? usage;
			response = extractResponseInfo(chunk) ?? response;
		}
	};
}
/**
* The streamText result's private `baseStream` carries `{ part, partialOutput }`
* envelopes rather than bare stream parts; `fullStream` and provider-level
* streams carry bare parts. Unwrap the envelope when present so chunk
* inspection sees the actual part in both cases.
*/
function unwrapChunkEnvelope(chunk) {
	if (typeof chunk !== "object" || chunk === null) return chunk;
	const part = chunk.part;
	return typeof part === "object" && part !== null && "type" in part ? part : chunk;
}
function isErrorChunk(chunk) {
	return typeof chunk === "object" && chunk !== null && chunk.type === "error";
}
function isAbortChunk(chunk) {
	return typeof chunk === "object" && chunk !== null && chunk.type === "abort";
}
function streamSummaryFromParts(input) {
	return {
		...input.aiGatewayLogId !== void 0 ? { aiGatewayLogId: input.aiGatewayLogId } : {},
		...input.finishReason !== void 0 ? { finishReason: input.finishReason } : {},
		...input.outputMessages !== void 0 ? { outputMessages: input.outputMessages } : {},
		...input.response ? { response: input.response } : {},
		...input.timeToFirstChunkSeconds !== void 0 ? { timeToFirstChunkSeconds: input.timeToFirstChunkSeconds } : {},
		...input.toolCallCount > 0 ? { toolCallCount: input.toolCallCount } : {},
		...input.usage ? { usage: input.usage } : {}
	};
}
function isReadableStream(value) {
	return typeof value === "object" && value !== null && "pipeThrough" in value && typeof value.pipeThrough === "function" && "getReader" in value && typeof value.getReader === "function";
}
function isToolCallChunk(chunk) {
	return typeof chunk === "object" && chunk !== null && chunk.type === "tool-call";
}
function isAsyncIterable$1(value) {
	return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
}
//#endregion
//#region src/observability/ai/wrapper/model.ts
function wrapModel(tracer, wrapLanguageModel, model, parentOperation, storeMessages, context, boundToInvocation = false) {
	if (!wrapLanguageModel) return model;
	if (typeof model !== "object" || model === null) return model;
	const modelInfo = extractModelInfo(model);
	const aiGatewayLog = captureAIGatewayLogFromModel(model, modelInfo?.provider);
	return wrapLanguageModel({
		model: aiGatewayLog.model,
		middleware: {
			wrapGenerate: async ({ doGenerate, params }) => {
				const span = modelCallSpanForModel("doGenerate", modelInfo, params, parentOperation, storeMessages, context);
				return tracer.withSpan(span.name, span.attributes, async (modelCall) => {
					aiGatewayLog.reset();
					try {
						const result = await doGenerate();
						modelCall.finish({
							...finishAttributesFromResult(result, {
								aiGatewayLogId: extractAIGatewayLogId(result) ?? aiGatewayLog.get(),
								includeResponse: true
							}),
							...outputMessageAttributes(result, storeMessages)
						});
						return result;
					} catch (cause) {
						recordAIGatewayLogOnError(modelCall, cause, aiGatewayLog.get());
						throw cause;
					}
				}, boundToInvocation ? { boundToInvocation: true } : void 0);
			},
			wrapStream: async ({ doStream, params }) => {
				const span = modelCallSpanForModel("doStream", modelInfo, params, parentOperation, storeMessages, context);
				return tracer.openSpan(span.name, span.attributes, async (modelCall) => {
					aiGatewayLog.reset();
					try {
						const startedAtMs = Date.now();
						const result = await doStream();
						return finishWhenStreamCompletes(result, modelCall, {
							aiGatewayLogId: extractAIGatewayLogId(result) ?? aiGatewayLog.get(),
							includeAIGatewayLog: true,
							includeResponse: true,
							storeMessages,
							startedAtMs
						});
					} catch (cause) {
						recordAIGatewayLogOnError(modelCall, cause, aiGatewayLog.get());
						modelCall.fail(cause);
						throw cause;
					}
				}, boundToInvocation ? { boundToInvocation: true } : void 0);
			}
		}
	});
}
function recordAIGatewayLogOnError(span, cause, capturedLogId) {
	writeSpanAttributes(span, aiGatewayLogAttributes(extractAIGatewayLogId(cause) ?? capturedLogId));
}
function modelCallSpanForModel(operation, model, params, parentOperation, storeMessages, context) {
	const record = typeof params === "object" && params !== null ? params : {};
	const span = modelCallSpan({
		context,
		model: model?.modelId,
		operation,
		provider: model?.provider,
		request: extractRequestSummary(record, parentOperation)
	});
	return {
		...span,
		attributes: {
			...span.attributes,
			...inputMessageAttributes(record, storeMessages)
		}
	};
}
//#endregion
//#region src/observability/ai/wrapper/tools.ts
function wrapTools(tracer, tools, storeTools, context, boundToInvocation = false, approvedToolCalls) {
	if (typeof tools !== "object" || tools === null) return tools;
	const toolRecord = tools;
	const wrappedTools = {};
	for (const [toolName, tool] of Object.entries(toolRecord)) wrappedTools[toolName] = wrapTool(tracer, toolName, tool, storeTools, context, boundToInvocation, approvedToolCalls);
	return wrappedTools;
}
function wrapTool(tracer, toolName, tool, storeTools, context, boundToInvocation, approvedToolCalls) {
	if (typeof tool !== "object" || tool === null) return tool;
	const toolRecord = tool;
	const hasExecute = typeof toolRecord.execute === "function";
	const hasApproval = typeof toolRecord.needsApproval === "boolean" || typeof toolRecord.needsApproval === "function";
	if (!hasExecute && !hasApproval) return tool;
	const wrappedTool = Object.assign(Object.create(Object.getPrototypeOf(tool)), tool);
	if (hasApproval) wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, context, boundToInvocation);
	if (!hasExecute) return wrappedTool;
	const execute = toolRecord.execute;
	if (typeof execute !== "function") return wrappedTool;
	const originalExecute = execute.bind(tool);
	wrappedTool.execute = (...args) => {
		const span = toolCallSpan({
			context,
			operation: "tool.execute",
			toolCallId: extractToolCallId(args[1]),
			toolName
		});
		const attributes = {
			...span.attributes,
			...toolInputAttributes(args[0], storeTools)
		};
		return tracer.openSpan(span.name, attributes, (toolSpan) => {
			const inSpanContext = AsyncLocalStorage.snapshot();
			const toolCallId = extractToolCallId(args[1]);
			if (approvalResponseForOptions(args[1], toolCallId)?.approved === true || toolCallId !== void 0 && approvedToolCalls?.get(toolCallId) === toolName) {
				recordApprovalChild(tracer, toolName, toolCallId, "approved", context, boundToInvocation);
				if (toolCallId !== void 0) approvedToolCalls?.delete(toolCallId);
			}
			const result = originalExecute(...args);
			if (isPromiseLike(result)) return Promise.resolve(result).then((resolved) => settleToolResult(resolved, toolSpan, inSpanContext, storeTools), (cause) => {
				toolSpan.fail(cause);
				throw cause;
			});
			return settleToolResult(result, toolSpan, inSpanContext, storeTools);
		}, boundToInvocation ? { boundToInvocation: true } : void 0);
	};
	return wrappedTool;
}
function wrapApprovalCheck(tracer, wrappedTool, toolRecord, tool, toolName, context, boundToInvocation) {
	const approval = toolRecord.needsApproval;
	const original = typeof approval === "function" ? approval.bind(tool) : void 0;
	wrappedTool.needsApproval = (...args) => {
		const result = original ? original(...args) : approval;
		const recordRequested = (needed) => {
			const toolCallId = extractToolCallId(args[1]);
			if (needed === true && !hasApprovalResponse(args[1], toolCallId)) recordApprovalSegment(tracer, toolName, toolCallId, "requested", context, boundToInvocation);
			return needed;
		};
		return isPromiseLike(result) ? Promise.resolve(result).then(recordRequested) : recordRequested(result);
	};
}
/** Instruments AI SDK v7's top-level tool approval policy. */
function wrapToolApprovalPolicy(tracer, policy, approvedToolCalls, context, boundToInvocation = false) {
	if (typeof policy === "function") return (...args) => {
		const toolCall = recordValue(recordValue(args[0])?.toolCall);
		return observePolicyResult(policy(...args), readString(toolCall?.toolName) ?? "tool", readString(toolCall?.toolCallId), tracer, approvedToolCalls, context, boundToInvocation);
	};
	if (typeof policy !== "object" || policy === null) return policy;
	return Object.fromEntries(Object.entries(policy).map(([toolName, setting]) => [toolName, (...args) => observePolicyResult(typeof setting === "function" ? setting(...args) : setting, toolName, extractToolCallId(args[1]), tracer, approvedToolCalls, context, boundToInvocation)]));
}
function observePolicyResult(result, toolName, toolCallId, tracer, approvedToolCalls, context, boundToInvocation = false) {
	const observe = (status) => {
		if (toolCallId === void 0) return status;
		const type = typeof status === "string" ? status : readString(recordValue(status)?.type);
		if (type === "approved") approvedToolCalls.set(toolCallId, toolName);
		else if (type === "user-approval" || type === "denied") recordApprovalSegment(tracer, toolName, toolCallId, type === "denied" ? "denied" : "requested", context, boundToInvocation);
		return status;
	};
	return isPromiseLike(result) ? Promise.resolve(result).then(observe) : observe(result);
}
/** Records denied responses, whose tool never reaches execute(). */
function recordDeniedApprovalResponses(tracer, messages, context) {
	for (const response of approvalResponses(messages)) if (!response.approved) recordApprovalSegment(tracer, response.toolName, response.toolCallId, "denied", context);
}
function recordApprovalSegment(tracer, toolName, toolCallId, state, context, boundToInvocation = false) {
	const tool = toolCallSpan({
		context,
		operation: "tool.approval",
		toolCallId,
		toolName
	});
	tracer.withSpan(tool.name, tool.attributes, () => {
		recordApprovalChild(tracer, toolName, toolCallId, state, context, boundToInvocation);
	}, boundToInvocation ? { boundToInvocation: true } : void 0);
}
function recordApprovalChild(tracer, toolName, toolCallId, state, context, boundToInvocation = false) {
	const approval = toolApprovalSpan({
		context,
		state,
		toolCallId,
		toolName
	});
	tracer.withSpan(approval.name, approval.attributes, () => void 0, boundToInvocation ? { boundToInvocation: true } : void 0);
}
function hasApprovalResponse(options, toolCallId) {
	return approvalResponseForOptions(options, toolCallId) !== void 0;
}
function approvalResponseForOptions(options, toolCallId) {
	if (toolCallId === void 0 || typeof options !== "object" || options === null) return;
	return approvalResponses(options.messages).find((response) => response.toolCallId === toolCallId);
}
function approvalResponses(messagesValue) {
	if (!Array.isArray(messagesValue)) return [];
	const approvalToTool = /* @__PURE__ */ new Map();
	const toolNames = /* @__PURE__ */ new Map();
	for (const message of messagesValue) {
		if (typeof message !== "object" || message === null) continue;
		const content = message.content;
		if (!Array.isArray(content)) continue;
		for (const part of content) {
			if (typeof part !== "object" || part === null) continue;
			const record = part;
			const type = readString(record.type);
			if (type === "tool-call") {
				const toolCallId = readString(record.toolCallId);
				const toolName = readString(record.toolName);
				if (toolCallId && toolName) toolNames.set(toolCallId, toolName);
			} else if (type === "tool-approval-request") {
				const approvalId = readString(record.approvalId);
				const toolCallId = readString(record.toolCallId);
				if (approvalId && toolCallId) approvalToTool.set(approvalId, toolCallId);
			}
		}
	}
	const lastMessage = messagesValue.at(-1);
	const lastContent = typeof lastMessage === "object" && lastMessage !== null ? lastMessage.content : void 0;
	const decisions = [];
	if (Array.isArray(lastContent)) for (const part of lastContent) {
		if (typeof part !== "object" || part === null) continue;
		const record = part;
		if (record.type !== "tool-approval-response") continue;
		const approvalId = readString(record.approvalId);
		if (approvalId && typeof record.approved === "boolean") decisions.push({
			approvalId,
			approved: record.approved
		});
	}
	return decisions.flatMap(({ approvalId, approved }) => {
		const toolCallId = approvalToTool.get(approvalId);
		if (!toolCallId) return [];
		return [{
			approved,
			toolCallId,
			toolName: toolNames.get(toolCallId) ?? "tool"
		}];
	});
}
/**
* Finishes the tool span for a settled result. Streaming tools (async
* generators) return an iterable whose consumption is the tool's real
* duration, so the span closes when iteration ends instead of at creation.
*/
function settleToolResult(result, span, inSpanContext, storeTools) {
	if (isAsyncIterable(result)) return finishWhenIterableCompletes(result, span, inSpanContext, storeTools);
	span.finish(toolOutputAttributes(result, storeTools));
	return result;
}
function finishWhenIterableCompletes(iterable, span, inSpanContext, storeTools) {
	return { async *[Symbol.asyncIterator]() {
		const iterator = inSpanContext(() => iterable[Symbol.asyncIterator]());
		let exhausted = false;
		let returnValue;
		try {
			while (true) {
				const step = await inSpanContext(() => iterator.next());
				if (step.done) {
					exhausted = true;
					returnValue = step.value;
					return step.value;
				}
				yield step.value;
			}
		} catch (cause) {
			span.fail(cause);
			throw cause;
		} finally {
			if (!exhausted) try {
				await inSpanContext(() => iterator.return?.(void 0));
			} catch {}
			span.finish(toolOutputAttributes(returnValue, storeTools));
		}
	} };
}
/** Reads the AI SDK tool-call id from the execute options argument. */
function extractToolCallId(options) {
	if (typeof options !== "object" || options === null) return;
	return readString(options.toolCallId);
}
function isAsyncIterable(value) {
	return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
}
function recordValue(value) {
	return typeof value === "object" && value !== null ? value : void 0;
}
function isPromiseLike(value) {
	return value !== null && value !== void 0 && (typeof value === "object" || typeof value === "function") && "then" in value && typeof value.then === "function";
}
//#endregion
//#region src/observability/ai/wrapper/wrap.ts
/**
* Wraps an AI SDK namespace object with tracing while preserving its public
* shape and overloaded call signatures across supported majors.
*/
function createAISDKWrapper(ai, instrumentation) {
	const target = isModuleNamespace(ai) ? Object.setPrototypeOf({}, ai) : ai;
	const wrapperCache = /* @__PURE__ */ new Map();
	return new Proxy(target, { get(proxyTarget, property, receiver) {
		const original = Reflect.get(proxyTarget, property, receiver);
		if (isWrappedOperationName(property) && typeof original === "function") {
			let wrapper = wrapperCache.get(property);
			if (!wrapper) {
				wrapper = createOperationWrapper(property, toAISDKOperation(original), readWrapLanguageModel(ai), instrumentation);
				wrapperCache.set(property, wrapper);
			}
			return wrapper;
		}
		return original;
	} });
}
function readWrapLanguageModel(ai) {
	const value = ai.wrapLanguageModel;
	if (typeof value !== "function") return;
	return value;
}
function toAISDKOperation(value) {
	return value;
}
function isModuleNamespace(value) {
	if (typeof value !== "object" || value === null) return false;
	if (value.constructor?.name === "Module") return true;
	try {
		const firstKey = Object.keys(value)[0];
		if (firstKey === void 0) return false;
		const descriptor = Object.getOwnPropertyDescriptor(value, firstKey);
		return descriptor ? !descriptor.configurable && !descriptor.writable : false;
	} catch {
		return false;
	}
}
function createOperationWrapper(operationName, operation, wrapLanguageModel, instrumentation) {
	const storage = {
		storeMessages: instrumentation.options?.storeMessages === true,
		storeTools: instrumentation.options?.storeTools === true
	};
	if (isStreamOperation(operationName)) return (params, ...args) => {
		const boundToInvocation = isAISDKInvocationBounded(params);
		return instrumentation.tracer.openSpan(operationSpanName(agentNameForCall(params)), {}, (operationSpan) => {
			if (!operationSpan.isTraced) {
				operationSpan.finish();
				return operation(params, ...args);
			}
			const context = semanticContext(params);
			writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
			recordDeniedApprovalResponses(instrumentation.tracer, params.messages, context);
			const startedAtMs = Date.now();
			const result = operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, context, boundToInvocation), ...args);
			const hasModelSpan = canWrapModel(wrapLanguageModel, params.model);
			return finishWhenStreamCompletes(result, operationSpan, {
				includeResponse: !hasModelSpan,
				startedAtMs: hasModelSpan ? void 0 : startedAtMs
			});
		}, boundToInvocation ? { boundToInvocation: true } : void 0);
	};
	return async (params, ...args) => {
		const boundToInvocation = isAISDKInvocationBounded(params);
		return instrumentation.tracer.withSpan(operationSpanName(agentNameForCall(params)), {}, async (operationSpan) => {
			if (!operationSpan.isTraced) return operation(params, ...args);
			const context = semanticContext(params);
			writeSpanAttributes(operationSpan, operationSpanForCall(operationName, extractModelInfo(params.model), params, instrumentation.options).attributes);
			recordDeniedApprovalResponses(instrumentation.tracer, params.messages, context);
			const result = await operation(operationParamsForCall(params, operationName, wrapLanguageModel, instrumentation.tracer, storage, context, boundToInvocation), ...args);
			operationSpan.finish(finishAttributesFromResult(result, { includeResponse: !canWrapModel(wrapLanguageModel, params.model) }));
			return result;
		}, boundToInvocation ? { boundToInvocation: true } : void 0);
	};
}
/**
* Reads only the agent name for the span name, from the same sources and in
* the same order as {@link semanticContext}, so the span name and
* `gen_ai.agent.name` never disagree. `functionId` is the AI SDK's canonical
* projection; an explicit name from v6 metadata or v7 runtime context wins.
*/
function agentNameForCall(params) {
	const telemetry = telemetryOptions(params);
	const metadata = telemetryMetadata(params);
	const runtimeContext = runtimeContextRecord(params);
	return metadataValue(metadata, "agentName", "gen_ai.agent.name") ?? metadataValue(runtimeContext, "agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId);
}
function operationParamsForCall(params, operationName, wrapLanguageModel, tracer, storage, context, boundToInvocation = false) {
	const approvedToolCalls = /* @__PURE__ */ new Map();
	return {
		...params,
		...shouldWrapTools(operationName) && params.tools !== void 0 ? { tools: wrapTools(tracer, params.tools, storage.storeTools, context, boundToInvocation, approvedToolCalls) } : {},
		...params.toolApproval !== void 0 ? { toolApproval: wrapToolApprovalPolicy(tracer, params.toolApproval, approvedToolCalls, context, boundToInvocation) } : {},
		...params.model !== void 0 ? { model: wrapModel(tracer, wrapLanguageModel, params.model, operationName, storage.storeMessages, context, boundToInvocation) } : {}
	};
}
function canWrapModel(wrapLanguageModel, model) {
	return wrapLanguageModel !== void 0 && typeof model === "object" && model !== null;
}
function isStreamOperation(operationName) {
	return operationName === "streamObject" || operationName === "streamText";
}
function shouldWrapTools(operationName) {
	return operationName === "generateText" || operationName === "streamText";
}
function isWrappedOperationName(value) {
	return value === "generateObject" || value === "generateText" || value === "streamObject" || value === "streamText";
}
function operationSpanForCall(operation, model, params, options) {
	return operationSpan({
		attributes: {
			...metadataAttributes(includedRuntimeContext(params), TraceAttribute.Cloudflare.RuntimeContextPrefix),
			...metadataAttributes(telemetryMetadata(params)),
			...contextAttributes(params, options)
		},
		context: semanticContext(params),
		model: model?.modelId,
		operation,
		provider: model?.provider,
		request: extractRequestSummary(params, operation)
	});
}
/** Reads the per-call `experimental_telemetry.metadata` record, if present. */
function telemetryMetadata(params) {
	const telemetry = telemetryOptions(params);
	return typeof telemetry?.metadata === "object" && telemetry.metadata !== null ? telemetry.metadata : void 0;
}
/**
* The caller's application-data channel: `runtimeContext` on v7, the
* `experimental_context` it replaced on v6. Read through one accessor so the
* two majors cannot drift on which one identity and metadata come from.
*/
function runtimeContextRecord(params) {
	const value = params.runtimeContext ?? params.experimental_context;
	return typeof value === "object" && value !== null ? value : void 0;
}
function telemetryOptions(params) {
	const telemetryValue = params.telemetry ?? params.experimental_telemetry;
	return typeof telemetryValue === "object" && telemetryValue !== null ? telemetryValue : void 0;
}
/**
* The v7 stand-in for `experimental_telemetry.metadata`.
*
* v7 dropped `metadata` from its telemetry options; callers put the same
* values in `runtimeContext` and mark the telemetry-visible ones through the
* SDK's own `telemetry.includeRuntimeContext`. Running the included subset
* through {@link metadataAttributes} is what keeps reserved keys — Think's
* `cloudflare.agents.turn.*` above all — on the attribute names v6 emits, so
* a query written against v6 traces still matches v7 ones. Everything else
* passes through as documented, under `cloudflare.agents.runtime_context.*`.
*
* Runtime context the caller did not mark as included stays off the span as a
* passthrough attribute: on v7 this is a general application-data channel, not
* a telemetry bag. Identity (`agentId`, `agentName`, `agentVersion`,
* `conversationId`) is separate — {@link semanticContext} reads it regardless,
* because it names the operation rather than describing it, and v7 left
* callers nowhere else to put it.
*/
function includedRuntimeContext(params) {
	const runtimeContext = runtimeContextRecord(params);
	if (runtimeContext === void 0) return;
	const included = includedContextKeys(telemetryOptions(params));
	if (included === void 0) return;
	const projected = {};
	for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
	return projected;
}
/**
* The keys a caller marked as telemetry-visible.
*
* The SDK's own shape is `{ [key]: boolean }`, included only when explicitly
* true; a plain array of key names is accepted too, since that is the shape of
* the wrapper's option of the same name and silently ignoring it would be
* worse than honouring it. There is deliberately no "include everything"
* shorthand — runtime context routinely carries credentials and user data that
* no one asked to put on a span.
*/
function includedContextKeys(telemetry) {
	const included = telemetry?.includeRuntimeContext;
	if (Array.isArray(included)) return included.filter((key) => typeof key === "string");
	if (typeof included !== "object" || included === null) return;
	return Object.entries(included).filter(([, enabled]) => enabled === true).map(([key]) => key);
}
/**
* Whether the caller explicitly opted a key OUT. Identity is read from runtime
* context without an opt-in — on v7 there is nowhere else to put it, and
* requiring one would silently cost every caller `gen_ai.agent.id` — but an
* explicit `false` is a stated intention and is honoured.
*/
function isExcludedFromContext(params, key) {
	const included = telemetryOptions(params)?.includeRuntimeContext;
	return typeof included === "object" && included !== null && !Array.isArray(included) && included[key] === false;
}
/**
* Reads agent/conversation semantic context from the AI SDK's own telemetry
* fields. The AI SDK maps `functionId` to `gen_ai.agent.name`; an explicit
* name takes priority. Each field comes from v6 `telemetry.metadata` or, on
* v7 where that option no longer exists, from `runtimeContext`.
*/
function semanticContext(params) {
	const telemetry = telemetryOptions(params);
	const metadata = telemetryMetadata(params);
	const runtimeContext = runtimeContextRecord(params);
	const fromContext = (key, semanticKey) => isExcludedFromContext(params, key) ? void 0 : metadataValue(runtimeContext, key, semanticKey);
	return {
		agentId: metadataValue(metadata, "agentId", "gen_ai.agent.id") ?? fromContext("agentId", "gen_ai.agent.id"),
		agentName: metadataValue(metadata, "agentName", "gen_ai.agent.name") ?? fromContext("agentName", "gen_ai.agent.name") ?? readString(telemetry?.functionId),
		agentVersion: metadataValue(metadata, "agentVersion", "gen_ai.agent.version") ?? fromContext("agentVersion", "gen_ai.agent.version"),
		conversationId: metadataValue(metadata, "conversationId", "gen_ai.conversation.id") ?? fromContext("conversationId", "gen_ai.conversation.id")
	};
}
function metadataValue(metadata, key, semanticKey) {
	return readString(metadata?.[key] ?? metadata?.[semanticKey]);
}
/**
* The wrapper-level allowlist, set once for the instrumentation rather than
* per call. It selects from the same context and lands on the same attributes
* as the SDK's per-call allowlist: selecting a key through both must produce
* one attribute, not a canonical one and a `runtime_context.*` near-duplicate.
*/
function contextAttributes(params, options) {
	const included = options?.includeRuntimeContext;
	if (included === void 0 || included.length === 0) return;
	const runtimeContext = runtimeContextRecord(params);
	if (runtimeContext === void 0) return;
	const projected = {};
	for (const key of included) if (Object.hasOwn(runtimeContext, key)) projected[key] = runtimeContext[key];
	return metadataAttributes(projected, TraceAttribute.Cloudflare.RuntimeContextPrefix);
}
const invocationBounded = Symbol.for("cloudflare.agents.ai-sdk.invocation-bounded");
function isAISDKInvocationBounded(params) {
	return params[invocationBounded] === true || __DO_NOT_USE_WILL_BREAK__agentContext.getStore()?.connection !== void 0;
}
//#endregion
//#region src/observability/ai/index.ts
/**
* Wraps an AI SDK namespace with tracing.
*/
function wrapAISDK(ai, options = {}) {
	return createAISDKWrapper(ai, {
		options,
		tracer
	});
}
//#endregion
export { wrapAISDK };

//# sourceMappingURL=index.js.map