UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

255 lines (254 loc) • 12.9 kB
import { AUTO_MODE_DEFAULT_TIMEOUT_MS, AUTO_MODE_RECORDED_ABSENT_TIMEOUT_MS, AUTO_MODE_DEFAULT_WINDOW_MAX_CHARS, AUTO_MODE_DEFAULT_WINDOW_MAX_ENTRIES, AUTO_MODE_DEFAULTS_SENTINEL, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_DENIAL_LIMIT_DEFAULTS, } from "./auto-mode-defaults.js"; export const AUTO_MODE_ARMING_RECIPE_VERSION = 2; const isPlainRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v); function canonicalStrings(v) { if (v === undefined) return undefined; if (!Array.isArray(v)) return null; for (let i = 0; i < v.length; i++) { if (!(i in v) || typeof v[i] !== "string") return null; } return v.length === 0 ? undefined : v.slice(); } function canonicalDerivedDenyRules(v) { if (v === undefined) return undefined; if (!Array.isArray(v)) return null; const out = []; for (let i = 0; i < v.length; i++) { const e = v[i]; if (!(i in v) || !isPlainRecord(e) || typeof e.rule !== "string") return null; if (e.anyCase !== undefined && typeof e.anyCase !== "boolean") return null; out.push({ rule: e.rule, ...(e.anyCase === true ? { anyCase: true } : {}) }); } return out.length === 0 ? undefined : out; } function canonicalRuleSection(v) { const list = canonicalStrings(v); if (list === null || list === undefined) return list; let seen = false; const deduped = list.filter((r) => (r === AUTO_MODE_DEFAULTS_SENTINEL ? (seen ? false : ((seen = true), true)) : true)); if (deduped.length === 0) return undefined; return deduped.length === 1 && deduped[0] === AUTO_MODE_DEFAULTS_SENTINEL ? undefined : deduped; } const MAX_TIMER_DELAY_MS = 2_147_483_647; function canonicalDenialBound(v, min, max) { if (v === undefined) return undefined; if (typeof v !== "number" || !Number.isInteger(v) || v < min || v > max) return null; return v; } const NO_BOUND_CEILING = Number.POSITIVE_INFINITY; function canonicalDenialLimit(v) { if (v === undefined) return undefined; if (!isPlainRecord(v)) return null; const maxConsecutive = canonicalDenialBound(v.maxConsecutive, 1, NO_BOUND_CEILING); const maxTotal = canonicalDenialBound(v.maxTotal, 1, NO_BOUND_CEILING); const autoDenyAfterMs = canonicalDenialBound(v.autoDenyAfterMs, 0, MAX_TIMER_DELAY_MS); if (maxConsecutive === null || maxTotal === null || autoDenyAfterMs === null) return null; const merged = { ...(maxConsecutive !== undefined ? { maxConsecutive } : {}), ...(maxTotal !== undefined ? { maxTotal } : {}), ...(autoDenyAfterMs !== undefined ? { autoDenyAfterMs } : {}), }; return Object.keys(merged).length > 0 ? merged : undefined; } export function tightenDenialLimit(a, b) { const window = (v) => { const eff = v ?? AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS; return eff === 0 ? Number.POSITIVE_INFINITY : eff; }; const shortest = Math.min(window(a?.autoDenyAfterMs), window(b?.autoDenyAfterMs)); return { maxConsecutive: Math.min(a?.maxConsecutive ?? AUTO_MODE_DENIAL_LIMIT_DEFAULTS.maxConsecutive, b?.maxConsecutive ?? AUTO_MODE_DENIAL_LIMIT_DEFAULTS.maxConsecutive), maxTotal: Math.min(a?.maxTotal ?? AUTO_MODE_DENIAL_LIMIT_DEFAULTS.maxTotal, b?.maxTotal ?? AUTO_MODE_DENIAL_LIMIT_DEFAULTS.maxTotal), autoDenyAfterMs: Number.isFinite(shortest) ? shortest : 0, }; } function canonicalNumber(v, min, floor) { if (v === undefined) return undefined; if (typeof v !== "number" || !Number.isFinite(v)) return null; const n = floor ? Math.floor(v) : v; return n < min ? null : n; } export function sanitizeAutoModeArmingRecipe(value) { if (!isPlainRecord(value)) return undefined; if (value.v !== AUTO_MODE_ARMING_RECIPE_VERSION) return undefined; const settingsDenyRules = canonicalStrings(value.settingsDenyRules); const derivedDenyRules = canonicalDerivedDenyRules(value.derivedDenyRules); const sessionContext = canonicalStrings(value.sessionContext); const timeoutMs = canonicalNumber(value.timeoutMs, 1, false); const denialLimit = canonicalDenialLimit(value.denialLimit); if (settingsDenyRules === null || derivedDenyRules === null || sessionContext === null || timeoutMs === null || denialLimit === null) return undefined; let rules; if (value.rules !== undefined) { if (!isPlainRecord(value.rules)) return undefined; const allow = canonicalRuleSection(value.rules.allow); const softDeny = canonicalRuleSection(value.rules.softDeny); const hardDeny = canonicalRuleSection(value.rules.hardDeny); const environment = canonicalRuleSection(value.rules.environment); if (allow === null || softDeny === null || hardDeny === null || environment === null) return undefined; const merged = { ...(allow !== undefined ? { allow } : {}), ...(softDeny !== undefined ? { softDeny } : {}), ...(hardDeny !== undefined ? { hardDeny } : {}), ...(environment !== undefined ? { environment } : {}), }; if (Object.keys(merged).length > 0) rules = merged; } let window; if (value.window !== undefined) { if (!isPlainRecord(value.window)) return undefined; const maxEntries = canonicalNumber(value.window.maxEntries, 0, true); const maxCharsPerEntry = canonicalNumber(value.window.maxCharsPerEntry, 0, true); if (maxEntries === null || maxCharsPerEntry === null) return undefined; const merged = { ...(maxEntries !== undefined && maxEntries !== AUTO_MODE_DEFAULT_WINDOW_MAX_ENTRIES ? { maxEntries } : {}), ...(maxCharsPerEntry !== undefined && maxCharsPerEntry !== AUTO_MODE_DEFAULT_WINDOW_MAX_CHARS ? { maxCharsPerEntry } : {}), }; if (Object.keys(merged).length > 0) window = merged; } const settingsEpoch = value.settingsEpoch; if (settingsEpoch !== undefined && typeof settingsEpoch !== "string") return undefined; const promptDigest = value.promptDigest; if (promptDigest !== undefined && (typeof promptDigest !== "string" || promptDigest === "")) return undefined; const crossSessionMessagesRule = value.crossSessionMessagesRule; if (crossSessionMessagesRule !== undefined && typeof crossSessionMessagesRule !== "boolean") return undefined; return { v: AUTO_MODE_ARMING_RECIPE_VERSION, ...(promptDigest !== undefined ? { promptDigest } : {}), ...(crossSessionMessagesRule === true ? { crossSessionMessagesRule: true } : {}), ...(rules !== undefined ? { rules } : {}), ...(settingsDenyRules !== undefined ? { settingsDenyRules } : {}), ...(derivedDenyRules !== undefined ? { derivedDenyRules } : {}), ...(sessionContext !== undefined ? { sessionContext } : {}), ...(window !== undefined ? { window } : {}), ...(timeoutMs !== undefined ? { timeoutMs } : {}), ...(denialLimit !== undefined ? { denialLimit } : {}), ...(settingsEpoch !== undefined && settingsEpoch !== "" ? { settingsEpoch } : {}), }; } export function autoModeArmingRecipeOf(face, bind) { return sanitizeAutoModeArmingRecipe({ v: AUTO_MODE_ARMING_RECIPE_VERSION, ...(bind?.promptDigest !== undefined ? { promptDigest: bind.promptDigest } : {}), ...(bind?.derivedDenyRules !== undefined ? { derivedDenyRules: bind.derivedDenyRules } : {}), ...(face.rules !== undefined ? { rules: face.rules } : {}), ...(face.settingsDenyRules !== undefined ? { settingsDenyRules: face.settingsDenyRules } : {}), ...(face.sessionContext !== undefined ? { sessionContext: face.sessionContext } : {}), ...(face.window !== undefined ? { window: face.window } : {}), timeoutMs: face.timeoutMs ?? AUTO_MODE_DEFAULT_TIMEOUT_MS, ...(face.denialLimit !== undefined ? { denialLimit: face.denialLimit } : {}), ...(face.settingsEpoch !== undefined ? { settingsEpoch: face.settingsEpoch } : {}), ...(face.crossSessionMessagesRule !== undefined ? { crossSessionMessagesRule: face.crossSessionMessagesRule } : {}), }); } function sameArmingBody(a, b) { const body = (r) => JSON.stringify([ r.rules?.allow ?? null, r.rules?.softDeny ?? null, r.rules?.hardDeny ?? null, r.rules?.environment ?? null, r.settingsDenyRules ?? null, r.derivedDenyRules ?? null, r.sessionContext ?? null, r.window?.maxEntries ?? null, r.window?.maxCharsPerEntry ?? null, r.crossSessionMessagesRule === true, ]); return body(a) === body(b); } function armableRecipe(r) { return r !== undefined && (r.timeoutMs === undefined || r.timeoutMs <= MAX_TIMER_DELAY_MS) ? r : undefined; } export function foldAutoModeArming(recorded, current) { const rec = armableRecipe(sanitizeAutoModeArmingRecipe(recorded)); if (rec === undefined) { return { ok: false, reason: "malformed_recipe", message: "the recorded auto-mode arming recipe is not readable by this build (unknown recipe version, not plain recipe data, or a " + "classifier budget outside the range the live knob takes) — " + "no classifier is rebuilt; the redemption keeps its honest refusal and the inherited ask flows the original chain", }; } if (current === undefined) { return { ok: false, reason: "current_settings_missing", message: "no current auto-mode settings were declared for this rebuild, so nothing bounds it — the recorded arming is refused " + "rather than rebuilt unbounded (pass the redeeming deployment's own face through autoModeArmingRecipeOf; a deployment " + "with no overrides of its own still declares that, and the declaration is what the bound is made of)", }; } const cur = armableRecipe(sanitizeAutoModeArmingRecipe(current)); if (cur === undefined) { return { ok: false, reason: "malformed_current_settings", message: "the redeeming deployment's own auto-mode settings did not canonicalize into a recipe its own arming accepts (a malformed " + "member, or a classifier budget outside the range the live knob takes), so the recorded arming cannot be " + "bounded by the settings in force — no classifier is rebuilt (fail-closed: an unbounded rebuild is the one outcome this fold exists to prevent)", }; } if (!sameArmingBody(rec, cur)) { const sameEpoch = rec.settingsEpoch !== undefined && rec.settingsEpoch === cur.settingsEpoch; return sameEpoch ? { ok: false, reason: "epoch_inconsistent", message: `the recorded arming and the current settings both declare settings epoch ${JSON.stringify(rec.settingsEpoch)} while their ` + "prompt bodies differ — one of the two is mislabelled; refusing to rebuild rather than choosing which set of rules is the policy in force", } : { ok: false, reason: "settings_moved", message: "the auto-mode settings moved between the recorded arming and this deployment's current ones (rule sections / settings-deny rules / the engine's read-deny lines / " + "session context / window bounds / the cross-session lane rule differ), and free-text rule sets have no sound stricter-than ordering — refusing to rebuild " + "(the strictest decidable answer: the inherited ask flows the original chain to a human)", }; } const recordedTimeoutMs = rec.timeoutMs ?? AUTO_MODE_RECORDED_ABSENT_TIMEOUT_MS; const timeoutMs = Math.min(recordedTimeoutMs, cur.timeoutMs ?? AUTO_MODE_DEFAULT_TIMEOUT_MS); const denialLimit = tightenDenialLimit(rec.denialLimit, cur.denialLimit); const recordedDenial = tightenDenialLimit(rec.denialLimit, rec.denialLimit); return { ok: true, recorded: rec, effective: { ...rec, timeoutMs, denialLimit, ...(cur.settingsEpoch !== undefined ? { settingsEpoch: cur.settingsEpoch } : {}), }, tightened: timeoutMs < recordedTimeoutMs || denialLimit.maxConsecutive < recordedDenial.maxConsecutive || denialLimit.maxTotal < recordedDenial.maxTotal || denialLimit.autoDenyAfterMs !== recordedDenial.autoDenyAfterMs, }; }