UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

194 lines (193 loc) • 8.85 kB
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs"; import { _ as normalizeUniqueTrimmedStringList } from "./string-normalization-CECOqf64.mjs"; import { a as resolveBootstrapTotalMaxChars, i as resolveBootstrapMaxChars, t as USER_BOOTSTRAP_MAX_CHARS } from "./bootstrap-DROECufe.mjs"; import path from "node:path"; //#region src/agents/bootstrap-budget-warning.ts const DEFAULT_BOOTSTRAP_PROMPT_WARNING_SIGNATURE_HISTORY_MAX = 32; const BOOTSTRAP_TRUNCATION_NOTICE = [ "[Bootstrap truncation warning]", "Some workspace bootstrap files were truncated before Project Context injection.", "Treat Project Context as partial and read the relevant files directly if details seem missing." ].join("\n"); /** Builds the compact truncation notice mirrored into run metadata. */ function buildBootstrapPromptWarningNotice(warningLines) { return warningLines?.some((line) => line.trim().length > 0) ? BOOTSTRAP_TRUNCATION_NOTICE : void 0; } function appendSeenSignature(signatures, signature) { if (!signature.trim() || signatures.includes(signature)) return signatures; const next = [...signatures, signature]; return next.length <= DEFAULT_BOOTSTRAP_PROMPT_WARNING_SIGNATURE_HISTORY_MAX ? next : next.slice(-32); } function buildBootstrapTruncationSignature(analysis) { if (!analysis.hasTruncation) return; const files = analysis.truncatedFiles.map((file) => ({ path: file.path || file.name, rawChars: file.rawChars, injectedChars: file.injectedChars, causes: file.causes.toSorted() })).toSorted((a, b) => a.path.localeCompare(b.path) || a.rawChars - b.rawChars || a.injectedChars - b.injectedChars || a.causes.join("+").localeCompare(b.causes.join("+"))); return JSON.stringify({ bootstrapMaxChars: analysis.totals.bootstrapMaxChars, bootstrapTotalMaxChars: analysis.totals.bootstrapTotalMaxChars, files }); } /** Decides whether to show a prompt warning and returns the updated dedupe state. */ function buildBootstrapPromptWarning(params) { const signature = buildBootstrapTruncationSignature(params.analysis); let seenSignatures = normalizeUniqueTrimmedStringList(params.seenSignatures); if (params.previousSignature && !seenSignatures.includes(params.previousSignature)) seenSignatures = appendSeenSignature(seenSignatures, params.previousSignature); const hasSeenSignature = Boolean(signature && seenSignatures.includes(signature)); const warningShown = params.mode !== "off" && Boolean(signature) && (params.mode === "always" || !hasSeenSignature); const warningSignaturesSeen = signature && params.mode !== "off" ? appendSeenSignature(seenSignatures, signature) : seenSignatures; return { signature, warningShown, lines: warningShown ? [BOOTSTRAP_TRUNCATION_NOTICE] : [], warningSignaturesSeen }; } //#endregion //#region src/agents/bootstrap-budget.ts const DEFAULT_BOOTSTRAP_NEAR_LIMIT_RATIO = .85; function normalizePositiveLimit(value) { if (!Number.isFinite(value) || value <= 0) return 1; return Math.floor(value); } function effectiveBootstrapFileLimit(name, bootstrapMaxChars) { return name.toLowerCase() === "user.md" ? Math.min(bootstrapMaxChars, USER_BOOTSTRAP_MAX_CHARS) : bootstrapMaxChars; } /** * USER.md carries a deliberate fixed cap: tuning bootstrapMaxChars can only * lower it, so per-file remediation must never suggest raising that setting * for it. The effective limit equals the fixed cap exactly when the cap (not * the configured limit) is the binding constraint. */ function isFixedUserCapFile(file) { return file.name.toLowerCase() === "user.md" && file.effectiveFileLimit === 4e3; } /** Restores prompt-warning dedupe state from a previous bootstrap report. */ function resolveBootstrapWarningSignaturesSeen(report) { const truncation = report?.bootstrapTruncation; const seenFromReport = normalizeUniqueTrimmedStringList(truncation?.warningSignaturesSeen); if (seenFromReport.length > 0) return seenFromReport; if (truncation?.warningMode === "off") return []; const single = normalizeOptionalString(truncation?.promptWarningSignature); return single ? [single] : []; } /** * Matches injected content by source path, because basenames can repeat even * when the total budget drops one of those files. Account before remapping * source paths into the prompt workspace. */ function buildBootstrapInjectionStats(params) { const injectedByPath = /* @__PURE__ */ new Map(); for (const file of params.injectedFiles) { const pathValue = normalizeOptionalString(file.path) ?? ""; if (pathValue && !injectedByPath.has(pathValue)) injectedByPath.set(pathValue, file.content); } return params.bootstrapFiles.map((file) => { const pathValue = normalizeOptionalString(file.path) ?? ""; const normalizedPath = pathValue.replace(/\\/g, "/"); const name = normalizeOptionalString(file.name) ?? (normalizedPath ? path.posix.basename(normalizedPath) : "bootstrap"); const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length; const injectedChars = (pathValue ? injectedByPath.get(pathValue) : void 0)?.length ?? 0; const truncated = !file.missing && injectedChars < rawChars; return { name, path: pathValue || name, missing: file.missing, rawChars, injectedChars, truncated }; }); } /** Classifies bootstrap truncation and near-limit pressure for prompt/report output. */ function analyzeBootstrapBudget(params) { const bootstrapMaxChars = normalizePositiveLimit(params.bootstrapMaxChars); const bootstrapTotalMaxChars = normalizePositiveLimit(params.bootstrapTotalMaxChars); const nearLimitRatio = typeof params.nearLimitRatio === "number" && Number.isFinite(params.nearLimitRatio) && params.nearLimitRatio > 0 && params.nearLimitRatio < 1 ? params.nearLimitRatio : DEFAULT_BOOTSTRAP_NEAR_LIMIT_RATIO; let rawChars = 0; let injectedChars = 0; let remainingTotalChars = bootstrapTotalMaxChars; const files = params.files.map((file) => { const effectiveFileLimit = effectiveBootstrapFileLimit(file.name, bootstrapMaxChars); const availableTotalChars = remainingTotalChars; remainingTotalChars = Math.max(0, remainingTotalChars - file.injectedChars); if (file.missing) return { ...file, effectiveFileLimit, nearLimit: false, causes: [] }; rawChars += file.rawChars; injectedChars += file.injectedChars; const perFileOverLimit = file.rawChars > effectiveFileLimit; const nearLimit = file.rawChars >= Math.ceil(effectiveFileLimit * nearLimitRatio); const causes = []; if (file.truncated) { if (perFileOverLimit) causes.push("per-file-limit"); if (availableTotalChars < effectiveFileLimit && file.rawChars > availableTotalChars) causes.push("total-limit"); } return { ...file, effectiveFileLimit, nearLimit, causes }; }); const truncatedFiles = files.filter((file) => file.truncated); return { files, truncatedFiles, nearLimitFiles: files.filter((file) => file.nearLimit), totalNearLimit: injectedChars >= Math.ceil(bootstrapTotalMaxChars * nearLimitRatio), hasTruncation: truncatedFiles.length > 0, totals: { rawChars, injectedChars, truncatedChars: Math.max(0, rawChars - injectedChars), bootstrapMaxChars, bootstrapTotalMaxChars, nearLimitRatio } }; } /** Builds the canonical bootstrap budget diagnosis after caller-owned routing. */ function buildBootstrapBudgetState(params) { const bootstrapMaxChars = resolveBootstrapMaxChars(params.config, params.agentId); const bootstrapTotalMaxChars = resolveBootstrapTotalMaxChars(params.config, params.agentId); const bootstrapAnalysis = analyzeBootstrapBudget({ files: params.files, bootstrapMaxChars, bootstrapTotalMaxChars }); const bootstrapPromptWarningMode = "always"; return { bootstrapAnalysis, bootstrapMaxChars, bootstrapPromptWarning: buildBootstrapPromptWarning({ analysis: bootstrapAnalysis, mode: bootstrapPromptWarningMode, seenSignatures: params.seenSignatures, previousSignature: params.previousSignature }), bootstrapPromptWarningMode, bootstrapTotalMaxChars }; } /** Serializes truncation warning state for run reports and future dedupe. */ function buildBootstrapTruncationReportMeta(params) { return { warningMode: params.warningMode, warningShown: params.warning.warningShown, promptWarningSignature: params.warning.signature, ...params.warning.warningSignaturesSeen.length > 0 ? { warningSignaturesSeen: params.warning.warningSignaturesSeen } : {}, truncatedFiles: params.analysis.truncatedFiles.length, nearLimitFiles: params.analysis.nearLimitFiles.length, totalNearLimit: params.analysis.totalNearLimit }; } //#endregion export { isFixedUserCapFile as a, buildBootstrapTruncationReportMeta as i, buildBootstrapBudgetState as n, resolveBootstrapWarningSignaturesSeen as o, buildBootstrapInjectionStats as r, buildBootstrapPromptWarningNotice as s, analyzeBootstrapBudget as t };