@sema-agent/core
Version:
Stateless, task-oriented AI agent core
127 lines (126 loc) • 5.72 kB
JavaScript
import { emitTrace } from "../trace.js";
import { buildToolResultRef, OFFLOAD_TOOL_NAME, PERSISTED_OUTPUT_PREFIX, toolResultContentSegment, toolResultProvenanceOf, } from "../tool-result-store.js";
import { compactionEnabled, contextOverlayFor } from "../context-settings.js";
import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
export function buildWorkingFileAttachments(spec, prepared) {
if (spec.compaction?.attachWorkingFiles === false || !prepared.readTaskFile)
return undefined;
return {
readFile: prepared.readTaskFile,
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
...(prepared.normalizeAttachmentPath ? { normalizePath: prepared.normalizeAttachmentPath } : {}),
...(prepared.isDedupStubResult ? { isDedupStubResult: prepared.isDedupStubResult } : {}),
...(prepared.instructionSources
? { instructionSourcePaths: prepared.instructionSources.filter((s) => s.contentHash !== null).map((s) => s.path) }
: {}),
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
};
}
export function contextInstructionFilesOption(prepared) {
return prepared.projectInstructionContent !== undefined ? { contextInstructionFiles: prepared.projectInstructionContent } : {};
}
export function forkContextOption(prepared, disable) {
return disable ? {} : { forkContext: prepared.lastBrainContext };
}
export function centerAdoptionOption(prepared) {
const ca = prepared.centerCompactionCandidate?.();
return ca !== undefined ? { centerAdoption: ca } : {};
}
export const STALE_OFFLOAD_DEFAULT_KEEP_RECENT_PER_TOOL = 3;
export const STALE_OFFLOAD_DEFAULT_MIN_SAVINGS_CHARS = 2000;
export function resolveStaleToolResultOffload(knob) {
if (knob === undefined)
return undefined;
const check = (name, v, fallback) => {
if (v === undefined)
return fallback;
if (!Number.isInteger(v) || v < 0) {
const e = new Error(`compaction.staleToolResultOffload.${name} must be a non-negative integer, got ${String(v)}`);
e.code = "config.stale_tool_result_offload_invalid";
throw e;
}
return v;
};
return {
keepRecentPerTool: check("keepRecentPerTool", knob.keepRecentPerTool, STALE_OFFLOAD_DEFAULT_KEEP_RECENT_PER_TOOL),
minSavingsChars: check("minSavingsChars", knob.minSavingsChars, STALE_OFFLOAD_DEFAULT_MIN_SAVINGS_CHARS),
};
}
export function buildStaleOffloadPointer(toolName, ref, chars) {
return (`[Stale tool result offloaded to save context: ${chars} chars from an earlier "${toolName}" call ` +
`saved to persisted output ref "${ref}". Newer results of this tool are still shown in full below; ` +
`use the ${OFFLOAD_TOOL_NAME} tool with this ref if you need the offloaded content again.]`);
}
function toolResultText(m) {
return m.content
.filter((b) => b.type === "text")
.map((b) => b.text)
.join("\n");
}
export async function projectStaleToolResults(context, cfg, store, sessionId, writtenRefs) {
const byTool = new Map();
context.messages.forEach((m, i) => {
if (m.role !== "toolResult" || m.isError)
return;
const list = byTool.get(m.toolName);
const row = { idx: i, msg: m };
if (list === undefined)
byTool.set(m.toolName, [row]);
else
list.push(row);
});
const replacements = new Map();
for (const [toolName, rows] of byTool) {
const staleCount = rows.length - cfg.keepRecentPerTool;
for (let k = 0; k < staleCount; k++) {
const { idx, msg } = rows[k];
const text = toolResultText(msg);
if (text.startsWith(PERSISTED_OUTPUT_PREFIX))
continue;
const ref = buildToolResultRef(sessionId, msg.toolCallId, toolResultContentSegment(text));
const pointer = buildStaleOffloadPointer(toolName, ref, text.length);
if (text.length - pointer.length < cfg.minSavingsChars)
continue;
if (!writtenRefs.has(ref)) {
try {
await store.put(ref, text, toolResultProvenanceOf(sessionId));
writtenRefs.add(ref);
}
catch {
continue;
}
}
replacements.set(idx, { msg, pointer });
}
}
if (replacements.size === 0)
return context;
return {
...context,
messages: context.messages.map((m, i) => {
const hit = replacements.get(i);
if (hit === undefined)
return m;
const rest = hit.msg.content.filter((b) => b.type !== "text");
return { ...hit.msg, content: [{ type: "text", text: hit.pointer }, ...rest] };
}),
};
}
export function emitInputTruncated(tracer, taskId) {
return (info) => emitTrace(tracer, () => ({
kind: "compaction.input_truncated",
version: 1,
taskId,
label: info.label,
droppedChars: info.droppedChars,
keptChars: info.keptChars,
ts: Date.now(),
}));
}
export function legContextOverlayOption(prepared, runner) {
const overlay = contextOverlayFor(runner.deps.contextSettings, prepared.harness.getModelRef(), runner.deps);
return overlay !== undefined ? { contextOverlay: overlay } : {};
}
export function legCompactionSettings(spec, overlay) {
return { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction, enabled: compactionEnabled(spec.compaction?.enabled, overlay) };
}