UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

176 lines (175 loc) • 10.5 kB
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 {}; }