UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

43 lines (42 loc) • 2.35 kB
import { createHash } from "node:crypto"; export const ARTIFACT_REJECT_BREAKER_THRESHOLD = 3; export const ARTIFACT_REJECT_KEY_COMPONENT_MAX = 1024; const HEAD = `IMPORTANT: Artifact calls for this target have now been rejected ${ARTIFACT_REJECT_BREAKER_THRESHOLD} or more times in this session for the same reason.`; export const ARTIFACT_REJECT_BANNER_RETRYABLE = `${HEAD} Do not send the same call again: either make the specific change the error describes, or stop calling Artifact for this target and tell the user what is failing and why.`; export const ARTIFACT_REJECT_BANNER_REFUSAL = `${HEAD} Stop calling Artifact for this target and tell the user the artifact could not be published and why — do not try to work around this refusal.`; export const ARTIFACT_REJECT_REFUSAL_CODES = new Set(["artifact.force_refused", "artifact.forbidden"]); const clip = (v) => (typeof v === "string" ? v.slice(0, ARTIFACT_REJECT_KEY_COMPONENT_MAX) : ""); export function artifactRejectKeyOf(args) { if (args === null || typeof args !== "object") return "publish\0\0"; const a = args; return `${clip(a["action"]) || "publish"}\0${clip(a["file_path"])}\0${clip(a["url"])}`; } export function stripArtifactRejectBanner(text) { for (const banner of [ARTIFACT_REJECT_BANNER_RETRYABLE, ARTIFACT_REJECT_BANNER_REFUSAL]) if (text.startsWith(`${banner}\n\n`)) return text.slice(banner.length + 2); return text; } export function artifactRejectBannerFor(reason) { return ARTIFACT_REJECT_REFUSAL_CODES.has(reason) ? ARTIFACT_REJECT_BANNER_REFUSAL : ARTIFACT_REJECT_BANNER_RETRYABLE; } export function createArtifactRejectBreaker() { const table = new Map(); return { hit(key, reason, text) { const fingerprint = createHash("sha256").update(stripArtifactRejectBanner(text)).digest("hex"); const prior = table.get(key); const count = prior !== undefined && prior.reason === reason && prior.fingerprint === fingerprint ? prior.count + 1 : 1; table.set(key, { reason, fingerprint, count }); return count; }, clear() { table.clear(); }, streakOf(key) { const s = table.get(key); return s === undefined ? undefined : { reason: s.reason, count: s.count }; }, }; }