@sema-agent/core
Version:
Stateless, task-oriented AI agent core
50 lines (49 loc) • 3.34 kB
JavaScript
import { createHash } from "node:crypto";
import { createAutoModeDecider } from "./auto-mode.js";
import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "./auto-mode-prompt.js";
import { foldAutoModeArming, } from "./auto-mode-arming.js";
import { CROSS_SESSION_CLASSIFIER_RULE } from "../agents/cross-session-envelope.js";
export function rebuildAutoModeDecider(opts) {
const delegated = opts.delegated;
if (typeof delegated !== "boolean")
throw new TypeError(`rebuildAutoModeDecider: \`delegated\` must be a boolean (got ${typeof delegated}) — state whether the transcript is a delegated leg's`);
const folded = foldAutoModeArming(opts.recorded, opts.current);
if (!folded.ok)
return folded;
const effective = folded.effective;
const promptOptions = {
...(effective.rules !== undefined ? { rules: effective.rules } : {}),
...(effective.settingsDenyRules !== undefined ? { settingsDenyRules: effective.settingsDenyRules } : {}),
...(effective.derivedDenyRules !== undefined ? { derivedDenyRules: effective.derivedDenyRules } : {}),
...(effective.sessionContext !== undefined ? { sessionContext: effective.sessionContext } : {}),
...(effective.crossSessionMessagesRule === true ? { crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : {}),
};
const systemPrompt = buildAutoModePrompt(promptOptions);
const assembledDigest = `apv1:${createHash("sha256").update(systemPrompt).digest("hex")}`;
if (folded.recorded.promptDigest !== assembledDigest) {
return {
ok: false,
reason: "prompt_assets_moved",
message: folded.recorded.promptDigest === undefined
? "the recorded arming carries no classifier-prompt digest, so this build cannot establish that the prompt it assembles is " +
"the one the ancestor enforced (the recipe records overrides; the base shell and permissions document come from the build's " +
"own assets) — refusing to rebuild rather than enforce criteria that may not be the ancestor's"
: "this build assembles a DIFFERENT classifier prompt than the recorded arming did (prompt-asset version skew across the fleet: " +
`recorded ${folded.recorded.promptDigest}, assembled here ${assembledDigest}) — the overrides match but the default rules they ` +
"merge into do not, so the rebuilt classifier would enforce criteria the ancestor never ran; refusing to rebuild",
};
}
const decider = createAutoModeDecider({
...(effective.timeoutMs !== undefined ? { timeoutMs: effective.timeoutMs } : {}),
assemble: async (input) => {
const messages = await opts.transcript();
return renderAutoModeWindow(messages, effective.window, { delegated }) + renderAutoModeAction(input);
},
classify: async (_input, signal, _rung, userPrompt) => {
if (userPrompt === undefined)
throw new Error("the rebuilt auto-mode classifier was asked with no assembled question");
return opts.complete({ systemPrompt, userPrompt }, signal);
},
});
return { ok: true, decider, arming: folded.recorded, effective, tightened: folded.tightened };
}