@sema-agent/core
Version:
Stateless, task-oriented AI agent core
176 lines (175 loc) • 10.5 kB
JavaScript
import { compactionKindOf } from "../task-notification.js";
import { planRejectionClears } from "../context-edit.js";
import { rosterToolName } from "../tool-roster.js";
import { landStaleResultClears } from "./prepare-context-lane.js";
import { DEFAULT_COMPACTION_INSTRUCTIONS, maybeCompact, mintCompactedEvent } from "../auto-compaction.js";
import { emitTrace } from "../trace.js";
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
import { rebaseCadenceWindows } from "./turn-attachments.js";
import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption, legCompactionSettings, legContextOverlayOption } from "./compaction-call-options.js";
import { compactionEnabled } from "../context-settings.js";
import { compactionRestateOptions } from "./compaction-restate.js";
import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
import { asThrownError, thrownText } from "../thrown-value.js";
export function runRecoveryLanes(input) {
const { spec, prepared, queue, rs, ident, compactionBrain, compactionBreaker, windowSafetyOptions, runner } = input;
const runForcedCompactionPass = async (lane, turnSignal) => {
if (!compactionEnabled(spec.compaction?.enabled, legContextOverlayOption(prepared, runner).contextOverlay))
return false;
if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES)
return false;
const passSignal = turnSignal !== undefined ? AbortSignal.any([prepared.abortController.signal, turnSignal]) : prepared.abortController.signal;
try {
const comp = await maybeCompact({
session: prepared.session,
epochDeclaredSections: prepared.epochDeclaredSections,
...centerAdoptionOption(prepared),
model: prepared.harness.getModel(),
...legContextOverlayOption(prepared, runner),
compactionModel: prepared.compModel,
...forkContextOption(prepared, true),
brain: compactionBrain,
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
thinking: prepared.thinking,
settings: legCompactionSettings(spec, legContextOverlayOption(prepared, runner).contextOverlay),
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
signal: passSignal,
minTokens: 0,
force: true,
overheadTokens: prepared.promptOverheadTokens,
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
onInputTruncated: emitInputTruncated(rs.telemetry.tracer, rs.telemetry.taskId),
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
...contextInstructionFilesOption(prepared),
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
...seamCCompactionOptions(runner, prepared),
...compactionRestateOptions(prepared),
...windowSafetyOptions(prepared.harness.getModel()),
...compactionHookOptions(runner, spec, prepared.sessionId, "forced", prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: passSignal }),
});
if (comp.compacted) {
compactionBreaker.failures = 0;
recordCompactionReuse(runner, prepared, comp);
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON && comp.postTriggerTokens !== undefined) {
rs.counters.compactionFloor = Math.ceil(comp.postTriggerTokens * COMPACTION_REGROWTH_FACTOR);
}
queue.push(mintCompactedEvent(comp, "forced", ident()));
rs.counters.compactionPasses += 1;
rs.counters.compactionKind = compactionKindOf("forced");
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
const pd = comp.phaseDurations;
const pdDur = comp.durationMs;
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
}
prepared.cacheBreakDetector?.notifyCompaction();
if (rs.attach.attachState !== undefined) {
rs.attach.attachState.postCompactPending = true;
rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
}
}
else {
if (comp.noop) {
compactionBreaker.failures = 0;
}
else {
compactionBreaker.failures += 1;
}
const declineReason = lane === "rejection" ? "prompt-too-long recovery pass did not land" : "guard-chain forced compaction pass did not land";
if (!comp.noop) {
runner.deps.onError?.(new Error(declineReason), { phase: "compaction", sessionId: prepared.sessionId });
}
queue.push({
type: "compaction_outcome",
outcome: comp.noop ? "noop" : "failed",
trigger: "forced",
reason: declineReason,
...ident(),
});
}
return comp.compacted === true;
}
catch (err) {
if (turnSignal?.aborted === true && !prepared.abortController.signal.aborted) {
const interruptReason = lane === "rejection"
? "prompt-too-long recovery pass cut short by a turn interrupt"
: "guard-chain forced compaction cut short by a turn interrupt";
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: interruptReason, ...ident() });
return false;
}
compactionBreaker.failures += 1;
runner.deps.onError?.(asThrownError(err), { phase: "compaction", sessionId: prepared.sessionId });
const msg = thrownText(err);
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
return false;
}
};
prepared.harness.setLoopRecovery({
truncatedOutput: {},
malformedToolUse: {},
thinkingOnly: {},
degenerateOutput: { detect: (m) => isDegenerateCutMessage(m) },
promptTooLong: {
recover: async (attempt, turnSignal) => {
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
return false;
if (turnSignal?.aborted === true)
return false;
if (prepared.microCompact.clearOnRejection && attempt === 1) {
const proj = prepared.microCompact.projectionRef.current;
const compactionAvailable = compactionEnabled(spec.compaction?.enabled, legContextOverlayOption(prepared, runner).contextOverlay) && compactionBreaker.failures < MAX_CONSECUTIVE_COMPACTION_FAILURES;
const declineArm = compactionAvailable ? "forced_compaction" : "none";
const plan = proj === undefined
? { declined: "no_candidates" }
: planRejectionClears(proj.messages, {
keyOf: proj.keyOf,
toolNameOf: (recorded) => rosterToolName(prepared.toolRosterDeltas.current, recorded),
...(prepared.microCompact.offloadPersist ? { offload: { persist: prepared.microCompact.offloadPersist } } : {}),
});
if ("declined" in plan) {
emitTrace(rs.telemetry.tracer, () => ({
kind: "context.mc_null",
version: 1,
taskId: rs.telemetry.taskId,
reason: plan.declined,
nextArm: declineArm,
ts: Date.now(),
}));
}
else {
landStaleResultClears({
ledger: prepared.microCompact.ledger,
cleared: plan.cleared,
...(prepared.readFileState !== undefined ? { readFileState: prepared.readFileState } : {}),
});
emitTrace(rs.telemetry.tracer, () => ({
kind: "context.mc_clear",
version: 1,
taskId: rs.telemetry.taskId,
clearedCount: plan.cleared.length,
tokensSavedEstimate: plan.tokensSavedEstimate,
trigger: "refusal",
ts: Date.now(),
}));
return true;
}
}
return runForcedCompactionPass("rejection", turnSignal);
},
},
});
prepared.microCompact.inTurnCompactionRef.current = async (turnSignal, anchoredEstimate) => {
if (prepared.abortController.signal.aborted || prepared.pausedRef.current !== undefined)
return false;
if (turnSignal?.aborted === true)
return false;
if (anchoredEstimate !== undefined && rs.counters.compactionFloor > 0 && anchoredEstimate < rs.counters.compactionFloor) {
const floor = rs.counters.compactionFloor;
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.suppressed", version: 1, taskId: rs.telemetry.taskId, estTokens: anchoredEstimate, floor, ts: Date.now() }));
queue.push({ type: "compaction_outcome", outcome: "suppressed", trigger: "forced", ...ident() });
return false;
}
return runForcedCompactionPass("guard", turnSignal);
};
return {};
}