@sema-agent/core
Version:
Stateless, task-oriented AI agent core
452 lines (451 loc) • 25.1 kB
JavaScript
import { resolveBrainCallGuardrailMs } from "../../brain/timeout.js";
import { assertContainmentFaceValue } from "../../tools/fs/index.js";
import { MICRO_COMPACT_MACHINES } from "../context-edit.js";
import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
import { resolveCheckpointStore } from "../checkpoint-store.js";
import { assertAutoConsolidationWirable } from "./memory-consolidation-driver.js";
import { isModelPriced, malformedPricingField, modelCostToPricing } from "../pricing.js";
import { preflightLockedConfig } from "../locked-config.js";
import { assertRetentionCapability } from "../retention.js";
import { resolveModel, resolveTaskModel, roleModelIfSet } from "../roles.js";
import { resolveSkillListingBudget } from "../skill-listing-budget.js";
import { describeRejectedValue } from "../tool-search-setting.js";
import { applyToolModelGate, assertRestoreGatedToolsValue, modelIdTail } from "../tool-model-gate.js";
import { resolveUsageWindows } from "../usage-window-store.js";
import { deriveAskEffective, resolveAskSeamForm, resolveQuestionSeam } from "../wiring-manifest.js";
import { assertPeerPresenceValue } from "../../agents/peer-directory.js";
import { TOTAL_TOKENS_REMINDER_MODES } from "./turn-attachments.js";
const TASK_LIMIT_KEY_DICT = {
maxTokens: true,
maxCostUsd: true,
maxTurns: true,
maxWalltimeMs: true,
maxOutputTokens: true,
approachNotice: true,
budgetStreamCancel: true,
degrade: true,
brainCallGuardrailMs: true,
};
const TASK_LIMIT_KEYS = Object.keys(TASK_LIMIT_KEY_DICT);
const NUMERIC_TASK_LIMIT_KEYS = ["maxTokens", "maxCostUsd", "maxTurns", "maxWalltimeMs", "maxOutputTokens"];
const RETIRED_TASK_LIMIT_KEYS = {
timeoutSec: "maxWalltimeMs (milliseconds, not seconds)",
deadlineNudge: "approachNotice",
callCapByDeadline: "(retired — no replacement)",
gracefulFinalize: "(retired — the approach notice is the only end-of-budget prompt)",
};
export function limitConfigError(code, message) {
const e = new Error(message);
e.code = code;
return e;
}
export function resolveTaskLimits(limits) {
if (limits === undefined)
return undefined;
if (typeof limits !== "object" || Array.isArray(limits)) {
throw limitConfigError("config.limit_invalid", `TaskSpec.limits must be an object (got ${Array.isArray(limits) ? "an array" : typeof limits})`);
}
const raw = limits;
const legal = TASK_LIMIT_KEYS;
for (const key of Object.keys(raw)) {
if (legal.includes(key))
continue;
const replacement = RETIRED_TASK_LIMIT_KEYS[key];
throw limitConfigError("config.limit_unknown_key", `TaskSpec.limits.${key} is not a limit this engine reads` +
(replacement !== undefined ? ` — it was replaced by \`${replacement}\`` : "") +
`. Legal keys: ${TASK_LIMIT_KEYS.join(", ")}. Refused rather than ignored: a limit that is silently dropped reads to the caller as an armed ceiling.`);
}
for (const key of NUMERIC_TASK_LIMIT_KEYS) {
const value = raw[key];
if (value === undefined)
continue;
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
throw limitConfigError("config.limit_invalid", `TaskSpec.limits.${key} must be a finite, non-negative number (got ${String(value)}) — an unevaluable ceiling is not a ceiling, and folding it to a default would run the task under a limit nobody chose.`);
}
}
const notice = raw.approachNotice;
if (notice !== undefined && notice !== false) {
if (typeof notice !== "object" || notice === null || Array.isArray(notice)) {
throw limitConfigError("config.limit_invalid", `TaskSpec.limits.approachNotice must be \`false\` or { at?: [first, second] } (got ${String(notice)})`);
}
const at = notice.at;
if (at !== undefined) {
const ok = Array.isArray(at) &&
at.length === 2 &&
at.every((n) => typeof n === "number" && Number.isFinite(n) && n > 0 && n <= 1) &&
at[0] <= at[1];
if (!ok) {
throw limitConfigError("config.limit_invalid", `TaskSpec.limits.approachNotice.at must be two fractions in (0, 1] with first <= second (got ${JSON.stringify(at)})`);
}
}
}
return limits;
}
export function resolveAttachmentsConfig(attachments) {
if (attachments === null || (typeof attachments !== "object" && typeof attachments !== "function"))
return attachments;
const mode = attachments.totalTokensReminderMode;
if (mode !== undefined) {
const legal = TOTAL_TOKENS_REMINDER_MODES;
if (typeof mode !== "string" || !legal.includes(mode)) {
throw limitConfigError("config.attachment_invalid", `TaskSpec.attachments.totalTokensReminderMode must be one of ${TOTAL_TOKENS_REMINDER_MODES.map((m) => `"${m}"`).join(", ")} ` +
`(got ${describeRejectedValue(mode)}) — refused rather than folded to the default, which would publish a different readout than the one the deployment asked for.`);
}
}
return attachments;
}
export function isFableFamilyModelId(id) {
const tail = modelIdTail(id);
return /^claude-fable-\d/.test(tail) || /^claude-mythos-5(?!\d)/.test(tail);
}
export function resolveModelPromptTraits(model, spec, internals) {
return {
promptProfile: spec.promptProfile ?? internals?.promptProfile ?? "simple",
fableMitigations: isFableFamilyModelId(model.id),
};
}
function microCompactConfigError(field, value, legal) {
let shown;
try {
shown = JSON.stringify(value) ?? String(value);
}
catch {
try {
shown = String(value);
}
catch {
shown = `[unrepresentable ${typeof value}]`;
}
}
const seat = field === undefined ? "microCompact" : `microCompact.${field}`;
const e = new Error(`${seat} ${shown} is not ${legal} — an unevaluable declaration is refused loudly, ` +
`never folded to the default: a silently-ignored declaration would run the DEFAULT machine ` +
`while the deployment believes its opt-in or opt-out took effect.`);
e.code = "config.microcompact_invalid";
return e;
}
export function resolveMicroCompactKnob(bag) {
if (bag !== undefined && (typeof bag !== "object" || bag === null || Array.isArray(bag))) {
throw microCompactConfigError(undefined, bag, "an object carrying optional machine/clearOnRejection keys");
}
const declaredMachine = bag?.machine;
if (declaredMachine !== undefined && !MICRO_COMPACT_MACHINES.includes(declaredMachine)) {
throw microCompactConfigError("machine", declaredMachine, MICRO_COMPACT_MACHINES.map((m) => `"${m}"`).join(" | "));
}
return { machine: declaredMachine ?? "cc", clearOnRejection: resolveClearOnRejection(bag) };
}
function resolveClearOnRejection(bag) {
const declared = bag?.clearOnRejection;
if (declared !== undefined && typeof declared !== "boolean") {
throw microCompactConfigError("clearOnRejection", declared, "a boolean");
}
return declared ?? true;
}
export function prepareConfigDoors(input) {
const { deps, sessions, resume, internals } = input;
let spec = input.spec;
assertNoRetiredRunnerDeps(deps);
assertPeerPresenceValue(spec.peerPresence);
assertRestoreGatedToolsValue(spec.restoreGatedTools);
const microCompactKnob = resolveMicroCompactKnob(deps.microCompact);
const assertToolNameListValue = (value, seat) => {
if (value === undefined)
return;
if (!Array.isArray(value)) {
const e = new Error(`TaskSpec.${seat} must be an array of tool names or absent (got ${value === null ? "null" : typeof value}) — a garbage list must refuse loudly, never be read as "no ${seat}".`);
e.code = "config.tool_face_control";
throw e;
}
const entries = value;
for (const entry of entries) {
if (typeof entry !== "string") {
const e = new Error(`TaskSpec.${seat} entries must be strings (got ${entry === null ? "null" : typeof entry}).`);
e.code = "config.tool_face_control";
throw e;
}
}
};
assertToolNameListValue(spec.excludeTools, "excludeTools");
assertToolNameListValue(spec.deferTools, "deferTools");
assertToolNameListValue(spec.alwaysLoadTools, "alwaysLoadTools");
const toolFaceSnapshot = {
exclude: spec.excludeTools ? Object.freeze([...spec.excludeTools]) : undefined,
defer: spec.deferTools ? Object.freeze([...spec.deferTools]) : undefined,
alwaysLoad: spec.alwaysLoadTools ? Object.freeze([...spec.alwaysLoadTools]) : undefined,
restoreGated: spec.restoreGatedTools === true
? true
: spec.restoreGatedTools !== undefined
? Object.freeze([...spec.restoreGatedTools])
: undefined,
};
const promptProfile = resolveModelPromptTraits({ id: "" }, spec, internals).promptProfile;
if (spec.resumeAt !== undefined) {
if (resume) {
const e = new Error("resumeAt cannot be combined with a durable resume");
e.code = "resume_at.conflicts_resume";
throw e;
}
if (!spec.sessionId) {
const e = new Error("resumeAt requires a sessionId (the session to branch)");
e.code = "resume_at.no_session";
throw e;
}
}
assertContainmentFaceValue(spec.readFace, "read", "spec");
assertContainmentFaceValue(deps.readFace, "read", "deps");
assertContainmentFaceValue(spec.writeFace, "write", "spec");
assertContainmentFaceValue(deps.writeFace, "write", "deps");
const memoryDelegationEvidenceRaw = deps.memoryDelegationEvidence;
if (memoryDelegationEvidenceRaw !== undefined && memoryDelegationEvidenceRaw !== "static-face" && memoryDelegationEvidenceRaw !== "attested-only") {
const evidence = memoryDelegationEvidenceRaw;
const got = typeof evidence === "string"
? JSON.stringify(evidence.length > 64 ? `${evidence.slice(0, 64)}…` : evidence)
: evidence === null
? "null"
: Array.isArray(evidence)
? "an array"
: typeof evidence;
const e = new Error(`RunnerDeps.memoryDelegationEvidence must be "static-face" or "attested-only" when present (got ${got}) — ` +
`an unevaluable evidence standard is refused loudly, never folded to either standard.`);
e.code = "config.memory_delegation_evidence";
throw e;
}
const memoryDelegationEvidence = memoryDelegationEvidenceRaw === "attested-only" ? "attested-only" : "static-face";
const memoryProvenanceRaw = deps.memoryProvenance;
if (memoryProvenanceRaw !== undefined && memoryProvenanceRaw !== "off" && memoryProvenanceRaw !== "carry") {
const got = typeof memoryProvenanceRaw === "string"
? JSON.stringify(memoryProvenanceRaw.length > 64 ? `${memoryProvenanceRaw.slice(0, 64)}…` : memoryProvenanceRaw)
: memoryProvenanceRaw === null
? "null"
: Array.isArray(memoryProvenanceRaw)
? "an array"
: typeof memoryProvenanceRaw;
const e = new Error(`RunnerDeps.memoryProvenance must be "off" or "carry" when present (got ${got}) — ` + `an unevaluable provenance mode is refused loudly, never folded to either mode.`);
e.code = "config.memory_provenance";
throw e;
}
const memoryProvenance = memoryProvenanceRaw === "off" ? "off" : "carry";
const memoryCapturePolicyRaw = deps.memoryCapturePolicy;
if (memoryCapturePolicyRaw !== undefined && memoryCapturePolicyRaw !== "open" && memoryCapturePolicyRaw !== "governed" && memoryCapturePolicyRaw !== "capture-required") {
const got = typeof memoryCapturePolicyRaw === "string"
? JSON.stringify(memoryCapturePolicyRaw.length > 64 ? `${memoryCapturePolicyRaw.slice(0, 64)}…` : memoryCapturePolicyRaw)
: memoryCapturePolicyRaw === null
? "null"
: Array.isArray(memoryCapturePolicyRaw)
? "an array"
: typeof memoryCapturePolicyRaw;
const e = new Error(`RunnerDeps.memoryCapturePolicy must be "open", "governed" or "capture-required" when present (got ${got}) — ` +
`an unevaluable capture posture is refused loudly, never folded to any posture (the fault arms differ per posture, so a guess would pick a polarity nobody configured).`);
e.code = "config.memory_capture_policy";
throw e;
}
const memoryCapturePolicy = memoryCapturePolicyRaw === "governed" ? "governed" : memoryCapturePolicyRaw === "capture-required" ? "capture-required" : "open";
assertAutoConsolidationWirable(deps);
if (spec.resumeAtMode !== undefined) {
if (spec.resumeAt === undefined) {
const e = new Error(`resumeAtMode "${spec.resumeAtMode}" requires resumeAt (there is no branch target to position against)`);
e.code = "resume_at.mode_without_target";
throw e;
}
if (spec.resumeAtMode !== "at" && spec.resumeAtMode !== "before") {
const e = new Error(`resumeAtMode "${String(spec.resumeAtMode)}" is invalid — expected "at" (inclusive branch, the default) or "before" (exclusive branch)`);
e.code = "resume_at.invalid_mode";
throw e;
}
}
if (spec.toolMaterializeStrategy !== undefined && spec.toolMaterializeStrategy !== "swap" && spec.toolMaterializeStrategy !== "static") {
const e = new Error(`toolMaterializeStrategy must be "swap" or "static" (got ${JSON.stringify(spec.toolMaterializeStrategy)}).`);
e.code = "config.tool_materialize_invalid";
throw e;
}
if (spec.memoryPersistenceCapable !== undefined && typeof spec.memoryPersistenceCapable !== "boolean") {
const e = new Error(`memoryPersistenceCapable must be a boolean when present (got ${JSON.stringify(spec.memoryPersistenceCapable)}) — a non-boolean would silently read as capable.`);
e.code = "config.memory_persistence_invalid";
throw e;
}
if (spec.toolMaterializeStrategy === "static" && spec.deferSelfResolve === false) {
const e = new Error(`toolMaterializeStrategy "static" cannot be combined with deferSelfResolve: false — with the direct-call ` +
`lane disabled a placeholder is never swapped and never self-resolves, so no deferred tool could ever be ` +
`called. Use "swap", or leave deferSelfResolve on.`);
e.code = "config.tool_materialize_unreachable";
throw e;
}
if (resume === undefined && spec.objective.trim().length === 0) {
const e = new Error("TaskSpec.objective is empty — a task needs an instruction (an empty user message is rejected by strict model endpoints and would fail every later request of the session).");
e.code = "config.empty_objective";
throw e;
}
const lockedPreflight = preflightLockedConfig(spec, deps);
assertRetentionCapability({
policy: deps.retentionPolicy,
locked: lockedPreflight.lockedKeys.has("retentionPolicy"),
stores: [
{ name: "sessionStore", store: sessions },
{ name: "checkpointStore", store: resolveCheckpointStore(spec, deps) },
{ name: "toolResultStore", store: deps.toolResultStore },
],
});
const resolvedInteractionPosture = spec.interactionPosture ?? internals?.parentInteractionPosture ?? deps.interactionPosture;
{
const interactionPosture = resolvedInteractionPosture;
const discloseInteractionPostureRefusal = (err) => {
try {
deps.onError?.(err, { phase: "config", sessionId: spec.sessionId ?? "(pre-session)", classification: "interaction-posture-refused" });
}
catch {
}
};
if (interactionPosture !== undefined && interactionPosture !== "interactive" && interactionPosture !== "headless") {
const e = new Error(`interactionPosture ${JSON.stringify(interactionPosture)} is not a recognized posture ("interactive" | "headless") — ` +
`an unevaluable declaration is refused loudly, never folded to either posture.`);
e.code = "config.interaction_posture";
discloseInteractionPostureRefusal(e);
throw e;
}
if (interactionPosture === "interactive") {
const askDoor = resolveAskSeamForm(spec, deps);
const humanReachable = deriveAskEffective(askDoor.form, "unresolved") === "human_reachable";
const questionDoor = resolveQuestionSeam(spec, deps);
const strippedByEngine = internals?.questionFaceStripped === true;
if (spec.interactiveTools === false) {
const e = new Error(`interaction posture "interactive" declared together with interactiveTools: false — the hard-headless ` +
`clamp removes the AskUserQuestion mount, so no content question can ever reach the human this posture ` +
`promises. Drop one of the two declarations.`);
e.code = "config.interaction_posture";
discloseInteractionPostureRefusal(e);
throw e;
}
if (!humanReachable || !(questionDoor.wired || strippedByEngine)) {
const missing = [];
if (!humanReachable) {
missing.push(askDoor.form === "absent"
? "no onAsk approver is wired (spec.onAsk ?? deps.onAsk is absent — asks would auto-deny or park)"
: `the resolved onAsk seat is the blanket policy "${askDoor.form}" (${askDoor.provenance ?? "?"}) — a policy setting is not a reachable human`);
}
if (!questionDoor.wired && !strippedByEngine) {
missing.push("no content-question channel (spec.onQuestion ?? deps.onQuestion is absent, and this leg is not an engine-stripped background lane)");
}
const e = new Error(`interaction posture "interactive" declared, but this assembly cannot reach a human: ${missing.join("; ")}. ` +
`Wire the missing seam(s), or drop the posture declaration (absent = no check).`);
e.code = "config.interaction_posture";
discloseInteractionPostureRefusal(e);
throw e;
}
}
}
resolveTaskLimits(spec.limits);
resolveAttachmentsConfig(spec.attachments);
if (spec.resourceSuspend !== undefined) {
const rsus = spec.resourceSuspend;
if (typeof rsus.scope !== "string" || rsus.scope === "") {
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.scope must be a non-empty string (got ${String(rsus.scope)}) — it is the multi-tenant isolation key every resource checkpoint is filed under.`);
}
for (const key of ["totalBudgetUsd", "totalTokens", "maxSlices", "ttlMs"]) {
const value = rsus[key];
if (value === undefined)
continue;
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
throw limitConfigError("config.limit_invalid", `TaskSpec.resourceSuspend.${key} must be a finite, non-negative number (got ${String(value)}) — an unevaluable allocation is not an allocation, and a NaN here blinds even the validated per-slice window.`);
}
}
}
const usageWindows = resolveUsageWindows(deps.usageWindows);
const brainCallGuardrailRef = {};
const brainCallGuardrailMs = resolveBrainCallGuardrailMs(spec.limits?.brainCallGuardrailMs ?? deps.brainCallGuardrailMs);
if (spec.agents !== undefined && spec.agents.length > 0) {
const pool = spec.tools ?? [];
if (!pool.some((t) => typeof t.withAgents === "function")) {
const e = new Error(`TaskSpec.agents was provided but no delegation tool (createSubagentTool) is mounted in spec.tools — the per-task agents could never be offered. Mount the delegation tool, or drop spec.agents.`);
e.code = "config.agents.no_delegation_tool";
throw e;
}
const perTask = spec.agents;
spec = {
...spec,
tools: pool.map((t) => {
if (typeof t.withAgents !== "function")
return t;
const rebuilt = t.withAgents(perTask);
return t.modelGate !== undefined && rebuilt.modelGate === undefined ? { ...rebuilt, modelGate: t.modelGate } : rebuilt;
}),
};
}
const resolvedRoleRaw = resolveTaskModel(spec, deps);
const resolvedRole = internals?.inheritedModelRef !== undefined ? { ...resolvedRoleRaw, modelRef: internals.inheritedModelRef } : resolvedRoleRaw;
const model = resolvedRole.model;
if (usageWindows !== undefined && deps.usageWindowStore !== undefined) {
const costed = usageWindows.find((w) => w.maxCostUsd !== undefined);
const why = costed === undefined
? undefined
: !isModelPriced(model, deps.pricing)
? `model ${JSON.stringify(model.id)} is unpriced — it has neither a RunnerDeps.pricing entry nor a Model.cost declaration, so this run produces NO cost figure to charge the window with (an unpriced run has no cost, which is not the same as a cost of 0). Price the model, or drop maxCostUsd from the window.`
: (() => {
const bad = malformedPricingField(deps.pricing?.[model.id] ?? modelCostToPricing(model.cost));
return bad === undefined
? undefined
: `the price table for model ${JSON.stringify(model.id)} declares an unevaluable ${bad} — every rate must be a finite, non-negative number of USD per 1M tokens. A table like this prices every turn at zero (or below), so the ceiling would never fill: refused rather than enforced in name only.`;
})();
if (costed !== undefined && why !== undefined) {
const e = new Error(`RunnerDeps.usageWindows declares a cost ceiling (maxCostUsd ${String(costed.maxCostUsd)} over ${String(costed.windowMs)}ms, ${costed.anchor}) but ${why}`);
e.code = "config.usage_window_unpriced";
throw e;
}
}
const dollarCeiling = spec.limits?.maxCostUsd !== undefined
? "limits.maxCostUsd"
: spec.resourceSuspend?.totalBudgetUsd !== undefined
? "resourceSuspend.totalBudgetUsd"
: undefined;
if (dollarCeiling !== undefined && isModelPriced(model, deps.pricing)) {
const bad = malformedPricingField(deps.pricing?.[model.id] ?? modelCostToPricing(model.cost));
if (bad !== undefined) {
throw limitConfigError("config.limit_invalid", `TaskSpec.${dollarCeiling} is a money ceiling, but the price table for model ${JSON.stringify(model.id)} declares an unevaluable ${bad} — every rate must be a finite, non-negative number of USD per 1M tokens. A table like this prices every turn at zero (or below), so the ceiling would never trip: refused rather than enforced in name only.`);
}
}
const fableMitigations = resolveModelPromptTraits(model, spec, internals).fableMitigations;
const gateDecision = applyToolModelGate({
tools: spec.tools,
modelId: model.id,
depsSeat: deps.toolModelGate,
restoreGated: toolFaceSnapshot.restoreGated,
envRaw: process.env.SEMA_TOOL_MODEL_GATE,
});
if (gateDecision.survivors !== undefined) {
spec = { ...spec, tools: gateDecision.survivors };
}
const skillListingBudget = resolveSkillListingBudget({
fraction: spec.skillListingBudgetFraction,
maxDescChars: spec.skillListingMaxDescChars,
model,
});
const modelGate = Object.freeze({
removedByClass: gateDecision.removedByClass,
unknownClasses: gateDecision.unknownClasses,
discardedEnvRaw: gateDecision.discardedEnvRaw,
});
const thinking = spec.thinking ?? resolvedRole.thinking ?? model.defaultThinking;
const compModel = spec.compactionModel
? resolveModel(spec.compactionModel, deps.models)
: roleModelIfSet("summarize", spec, deps);
return {
spec,
toolFaceSnapshot,
promptProfile,
lockedPreflight,
resolvedInteractionPosture,
microCompactKnob,
resolvedRole,
model,
thinking,
compModel,
fableMitigations,
modelGate,
memoryDelegationEvidence,
skillListingBudget,
memoryProvenance,
memoryCapturePolicy,
usageWindows,
brainCallGuardrailRef,
brainCallGuardrailMs,
};
}