import { o as logger } from "./logger-CtGm8dC_.js";
import { o as safeResolve } from "./esm-CxsIm9IU.js";
import { i as getCache, o as isCacheEnabled } from "./cache-wQj0h878.js";
import fs from "fs/promises";
import path from "path";
import dedent from "dedent";
import crypto from "crypto";
//#region src/providers/agentic-utils.ts
/**
* Shared utilities for agentic providers (Claude Agent SDK, OpenCode SDK, etc.)
*
* These utilities handle common functionality needed by coding agent providers:
* - Working directory fingerprinting for cache key generation
* - Response caching with fingerprint support
*/
const AGENTIC_PROVIDER_IDS = [
	"anthropic:claude-agent-sdk",
	"anthropic:claude-code",
	"openai:codex",
	"openai:codex-app-server",
	"openai:codex-desktop",
	"openai:codex-sdk",
	"opencode",
	"opencode:sdk"
];
/**
* Whether a provider runs a coding-agent runtime rather than a plain model API.
*
* Keep this list aligned with docs/agents/coding-agent-provider-taxonomy.md.
*/
function isAgenticProvider(provider) {
	if (!provider || typeof provider.id !== "function") return false;
	let providerId;
	try {
		providerId = provider.id();
	} catch {
		return false;
	}
	return AGENTIC_PROVIDER_IDS.some((agenticId) => providerId === agenticId || providerId.startsWith(`${agenticId}:`));
}
/**
* Timeout for working directory fingerprint generation (ms)
* Prevents hanging on extremely large directories
*/
const FINGERPRINT_TIMEOUT_MS = 2e3;
const STAT_BATCH_SIZE = 32;
/**
* Resolve a coding-agent working directory relative to the config file directory.
*
* `cliState.basePath` tracks the loaded config file's directory. Coding-agent providers
* use `process.cwd()` only when no config base path is available, which keeps relative
* paths stable when the same config is run from different shells.
*/
function resolveAgenticWorkingDir(workingDir, configBasePath) {
	if (!workingDir) return;
	return safeResolve(configBasePath ? path.resolve(configBasePath) : process.cwd(), workingDir);
}
/**
* Get a fingerprint for a working directory to use as a cache key.
* Checks directory mtime and descendant file mtimes recursively.
*
* This allows for caching prompts that use the same working directory
* when the files haven't changed.
*
* @param workingDir - Absolute path to the working directory
* @returns SHA-256 hash fingerprint of the directory state
* @throws Error if fingerprinting times out or directory is inaccessible
*/
async function getWorkingDirFingerprint(workingDir) {
	const dirMtime = (await fs.stat(workingDir)).mtimeMs;
	const startTime = Date.now();
	const getAllFiles = async (dir, files = []) => {
		if (Date.now() - startTime > FINGERPRINT_TIMEOUT_MS) throw new Error("Working directory fingerprint timed out");
		const entries = await fs.readdir(dir, { withFileTypes: true });
		for (const entry of entries) {
			const fullPath = path.join(dir, entry.name);
			if (entry.isDirectory()) await getAllFiles(fullPath, files);
			else if (entry.isFile()) files.push(fullPath);
		}
		return files;
	};
	const allFiles = await getAllFiles(workingDir);
	const fileMtimes = [];
	for (let i = 0; i < allFiles.length; i += STAT_BATCH_SIZE) {
		if (Date.now() - startTime > FINGERPRINT_TIMEOUT_MS) throw new Error("Working directory fingerprint timed out");
		const batch = allFiles.slice(i, i + STAT_BATCH_SIZE);
		const batchMtimes = await Promise.all(batch.map(async (file) => {
			const stat = await fs.stat(file);
			return `${path.relative(workingDir, file)}:${stat.mtimeMs}`;
		}));
		fileMtimes.push(...batchMtimes);
	}
	fileMtimes.sort();
	const fingerprintData = `dir:${dirMtime};files:${fileMtimes.join(",")}`;
	return crypto.createHash("sha256").update(fingerprintData).digest("hex");
}
/**
* Generate a cache key from arbitrary data using SHA-256 hash
*
* @param prefix - Cache key prefix (provider identifier)
* @param data - Data to hash for the cache key
* @returns Prefixed SHA-256 hash cache key
*/
function generateCacheKey(prefix, data) {
	const stringified = JSON.stringify(data);
	return `${prefix}:${crypto.createHash("sha256").update(stringified).digest("hex")}`;
}
/**
* Initialize cache and check for cached response
*
* This handles the common caching pattern used by agentic providers:
* 1. Check if caching is enabled
* 2. Generate working directory fingerprint if needed
* 3. Generate cache key
* 4. Return cache configuration for use by the provider
*
* @param options - Cache options including prefix and working directory
* @param cacheKeyData - Data to include in the cache key
* @returns Cache configuration and optional cached response
*/
async function initializeAgenticCache(options, cacheKeyData) {
	if (!isCacheEnabled()) return {
		shouldCache: false,
		shouldReadCache: false,
		shouldWriteCache: false
	};
	if (options.mcp && !options.cacheMcp) return {
		shouldCache: false,
		shouldReadCache: false,
		shouldWriteCache: false
	};
	let workingDirFingerprint = null;
	if (options.workingDir) try {
		workingDirFingerprint = await getWorkingDirFingerprint(options.workingDir);
	} catch (error) {
		logger.error(dedent`Error getting working directory fingerprint for cache key - ${options.workingDir}: ${String(error)}

        Caching is disabled.`);
		return {
			shouldCache: false,
			shouldReadCache: false,
			shouldWriteCache: false
		};
	}
	const cache = await getCache();
	const cacheKey = generateCacheKey(options.cacheKeyPrefix, {
		...cacheKeyData,
		workingDirFingerprint,
		...options.mcp ? { mcp: options.mcp } : {}
	});
	return {
		shouldCache: true,
		shouldReadCache: !options.bustCache,
		shouldWriteCache: true,
		cache,
		cacheKey,
		workingDirFingerprint
	};
}
/**
* Try to get a cached response
*
* @param cacheResult - Result from initializeAgenticCache
* @param debugContext - Context for debug logging (e.g., prompt preview)
* @returns Cached ProviderResponse if found, undefined otherwise
*/
async function getCachedResponse(cacheResult, debugContext) {
	if (!cacheResult.shouldReadCache || !cacheResult.cache || !cacheResult.cacheKey) return;
	try {
		const cachedResponse = await cacheResult.cache.get(cacheResult.cacheKey);
		if (cachedResponse) {
			logger.debug(`Returning cached response${debugContext ? ` for ${debugContext}` : ""} (cache key: ${cacheResult.cacheKey})`);
			return {
				...JSON.parse(cachedResponse),
				cached: true
			};
		}
	} catch (error) {
		logger.error(`Error getting cached response: ${String(error)}`);
	}
}
/**
* Cache a provider response
*
* @param cacheResult - Result from initializeAgenticCache
* @param response - The ProviderResponse to cache
* @param debugContext - Context for debug logging
*/
async function cacheResponse(cacheResult, response, debugContext) {
	if (!cacheResult.shouldWriteCache || !cacheResult.cache || !cacheResult.cacheKey) return;
	try {
		await cacheResult.cache.set(cacheResult.cacheKey, JSON.stringify(response));
	} catch (error) {
		logger.error(`Error caching response${debugContext ? ` for ${debugContext}` : ""}: ${String(error)}`);
	}
}
//#endregion
export { isAgenticProvider as a, initializeAgenticCache as i, generateCacheKey as n, resolveAgenticWorkingDir as o, getCachedResponse as r, cacheResponse as t };

//# sourceMappingURL=agentic-utils-BNNuEjFF.js.map