@sema-agent/core
Version:
Stateless, task-oriented AI agent core
255 lines (254 loc) • 12.9 kB
JavaScript
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,
};
}