import { DELEGATE_TOOL_PREFIX, TRANSFER_TOOL_PREFIX } from "@inkeep/agents-core";

//#region src/domains/run/agents/agent-types.ts
/**
* Creates a stopWhen condition that stops when any tool call name starts with the given prefix
* @param prefix - The prefix to check for in tool call names
* @returns A function that can be used as a stopWhen condition
*/
function hasToolCallWithPrefix(prefix) {
	return ({ steps }) => {
		const last = steps.at(-1);
		if (last && "toolCalls" in last && last.toolCalls) return last.toolCalls.some((tc) => tc.toolName.startsWith(prefix));
		return false;
	};
}
/**
* Detect the AI SDK's NoObjectGeneratedError by `error.name` rather than
* `instanceof`, because multiple resolved SDK versions in the dependency tree
* can break instanceof checks. Names sourced from ai@6 — the prefixed
* `AI_NoObjectGeneratedError` is current; the bare form is kept as a safety
* net for downgrades or upstream renames. Re-verify on AI SDK upgrades.
*/
function isNoObjectGeneratedError(error) {
	if (!(error instanceof Error)) return false;
	return error.name === "AI_NoObjectGeneratedError" || error.name === "NoObjectGeneratedError";
}
/**
* Build a debuggable, action-oriented description of why field resolution failed.
* Routes by the cause's error name so message phrasing matches the actual failure
* (no structured output emitted vs. emitted-but-unparseable vs. unrelated SDK fault).
*/
function getResolutionHint(field, cause, toolCallNames) {
	if (field !== "output") return `Resolving the "${field}" property of the AI SDK response failed. This is usually a streaming, network, or SDK-internal fault rather than a model problem.`;
	const causeName = cause instanceof Error ? cause.name : "";
	if (causeName === "AI_NoObjectGeneratedError" || causeName === "NoObjectGeneratedError") {
		const nonRoutingTools = toolCallNames.filter((n) => !n.startsWith(TRANSFER_TOOL_PREFIX) && !n.startsWith(DELEGATE_TOOL_PREFIX));
		if (nonRoutingTools.length > 0) return `The model called tool(s) [${nonRoutingTools.join(", ")}] but did not emit the structured object Output.object() expected. If this agent should answer with a tool call instead of data components, remove dataComponents from the agent or have the model transfer/delegate. To require data components, set outputContract.requireComponent.`;
		return `The model produced no structured output, no transfer, and no delegate call. The agent declares dataComponents but the model emitted text only — it ignored the structured-output instruction. To enforce structured output, set outputContract.requireComponent (forces a named component) or outputContract.allowText=false (forbids plain text).`;
	}
	if (causeName.includes("ParseError") || causeName.includes("TypeValidationError") || causeName.includes("ValidationError") || causeName === "ZodError") return `The model produced output but it did not match the expected Output.object() schema. This is usually a model failure — try simplifying the schema, splitting data components into smaller ones, or using a more capable model.`;
	return `Resolving the "output" property of the AI SDK response failed with ${causeName || "a non-Error rejection"}. See the cause for details.`;
}
/**
* Resolves a generation response from either `generateText` or `streamText` into
* a plain object with all needed values as own properties.
*
* **Why this exists:** The AI SDK returns class instances whose key properties
* (`text`, `steps`, `finishReason`, `output`) are prototype getters.
* `StreamTextResult` getters return `PromiseLike` values; `GenerateTextResult`
* getters return direct values. In both cases, the spread operator `{ ...result }`
* silently drops them. This function uses `Promise.resolve()` to safely resolve
* both styles, then spreads them as explicit own properties so downstream code
* (and further spreads) never loses them.
*
* **Tolerance:** when the model legitimately transferred or delegated (a
* `transfer_to_*` / `delegate_to_*` tool call in `steps`), `Output.object()`
* rejects with `NoObjectGeneratedError` because no structured object was emitted.
* That rejection is expected and we resolve `output` to undefined so the
* contract layer can run. Every other failure throws a `GenerationResponseError`
* carrying a debug hint plus `cause`, `field`, `finishReason`, and `toolCalls`.
*/
async function resolveGenerationResponse(response) {
	const stepsValue = response.steps;
	if (!stepsValue) return response;
	const fields = [
		"steps",
		"text",
		"finishReason",
		"output",
		"usage",
		"totalUsage",
		"response"
	];
	const settled = await Promise.allSettled([
		Promise.resolve(stepsValue),
		Promise.resolve(response.text),
		Promise.resolve(response.finishReason),
		Promise.resolve(response.output),
		Promise.resolve(response.usage),
		Promise.resolve(response.totalUsage),
		Promise.resolve(response.response)
	]);
	const stepsResult = settled[0];
	const toolCallNames = stepsResult.status === "fulfilled" && Array.isArray(stepsResult.value) ? stepsResult.value.flatMap((s) => (s.toolCalls ?? []).map((tc) => tc.toolName).filter((name) => Boolean(name))) : [];
	const outputResult = settled[3];
	if (outputResult.status === "rejected" && isNoObjectGeneratedError(outputResult.reason) && toolCallNames.some((n) => n.startsWith(TRANSFER_TOOL_PREFIX) || n.startsWith(DELEGATE_TOOL_PREFIX))) settled[3] = {
		status: "fulfilled",
		value: void 0
	};
	const failedIndex = settled.findIndex((r) => r.status === "rejected");
	if (failedIndex !== -1) {
		const cause = settled[failedIndex].reason;
		const failedField = fields[failedIndex];
		const finishReasonResult = settled[2];
		const finishReason$1 = finishReasonResult.status === "fulfilled" ? String(finishReasonResult.value ?? "unknown") : "unresolved";
		const hint = getResolutionHint(failedField, cause, toolCallNames);
		const causeName = cause instanceof Error ? cause.name : typeof cause;
		const causeMessage = cause instanceof Error ? cause.message : String(cause);
		const toolCallsRendered = toolCallNames.length > 0 ? `[${toolCallNames.join(",")}]` : "[]";
		const wrapped = new Error(`${hint} field=${failedField} finishReason=${finishReason$1} toolCalls=${toolCallsRendered} cause=${causeName}: ${causeMessage}`, { cause: cause instanceof Error ? cause : void 0 });
		wrapped.name = "GenerationResponseError";
		throw wrapped;
	}
	const [steps, text, finishReason, output, usage, totalUsage, responseObj] = settled.map((r) => r.value);
	return {
		...response,
		steps,
		text,
		finishReason,
		output,
		usage,
		totalUsage,
		response: responseObj
	};
}
function validateModel(modelString, modelType) {
	if (!modelString?.trim()) throw new Error(`${modelType} model is required. Please configure models at the project level.`);
	return modelString.trim();
}
function isValidTool(tool) {
	if (!tool || typeof tool !== "object") return false;
	const t = tool;
	return typeof t.description === "string" && t.inputSchema != null && typeof t.execute === "function";
}

//#endregion
export { hasToolCallWithPrefix, isValidTool, resolveGenerationResponse, validateModel };