UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

111 lines (110 loc) • 4.59 kB
import { isAbsoluteForFamily, pathFamilyOf } from "../../tools/fs/safety.js"; import { applyRecordedEdit } from "../../tools/fs/fs-shared.js"; import { normalizeFileText } from "../../tools/fs/encoding.js"; import { isOffloadedDetailReplacement } from "../tool-result-store.js"; const READ_TOOL = "Read"; const WRITE_TOOL = "Write"; const EDIT_TOOL = "Edit"; const RETRACTING_RESULTS = [ { toolName: "NotebookEdit", type: "notebook-edit", pathField: "notebookPath" }, ]; function replayedEditContent(rest) { const originalFile = rest.originalFile; if (typeof originalFile !== "string" || isOffloadedDetailReplacement(originalFile)) return undefined; const member = (v) => (typeof v === "string" && !isOffloadedDetailReplacement(v) ? v : undefined); let edits; const rawEdits = rest.edits; if (Array.isArray(rawEdits) && rawEdits.length > 0) { const out = []; for (const e of rawEdits) { const r = e; const oldS = member(r?.oldString); const newS = member(r?.newString); if (oldS === undefined || newS === undefined) return undefined; out.push({ oldString: oldS, newString: newS, replaceAll: r.replaceAll === true }); } edits = out; } else { const oldS = member(rest.oldString); const newS = member(rest.newString); if (oldS === undefined || newS === undefined) return undefined; edits = [{ oldString: oldS, newString: newS, replaceAll: rest.replaceAll === true }]; } let working = originalFile; for (const e of edits) { if (e.oldString !== "" && !working.includes(e.oldString)) return undefined; working = applyRecordedEdit(working, e); } if (normalizeFileText(working) !== working) return undefined; return working; } function cardOf(details) { if (typeof details !== "object" || details === null) return undefined; const rest = details; return typeof rest.type === "string" ? { type: rest.type, rest } : undefined; } function wholeFileFromReadCard(rest) { const file = rest.file; if (typeof file !== "object" || file === null) return undefined; const f = file; if (typeof f.content !== "string" || f.truncatedByTokenCap === true) return undefined; if (f.startLine !== 1 || typeof f.numLines !== "number" || f.numLines !== f.totalLines) return undefined; return f.content; } export function wholeFileRecordsFromTranscript(messages) { const byPath = new Map(); for (const m of messages) { if (m.role !== "toolResult" || m.isError) continue; const card = cardOf(m.details); if (card === undefined) continue; const { type, rest } = card; if (m.toolName === EDIT_TOOL && type === "edit") { const changed = rest.filePath; if (typeof changed !== "string") continue; const replayed = replayedEditContent(rest); if (replayed !== undefined && isAbsoluteForFamily(pathFamilyOf({ root: changed }), changed)) { byPath.set(changed, { path: changed, content: replayed, at: m.timestamp }); } else { byPath.delete(changed); } continue; } const retracts = RETRACTING_RESULTS.find((r) => r.toolName === m.toolName && r.type === type); if (retracts !== undefined) { const changed = rest[retracts.pathField]; if (typeof changed === "string") byPath.delete(changed); continue; } const isRead = m.toolName === READ_TOOL && type === "text"; const isWrite = m.toolName === WRITE_TOOL && (type === "create" || type === "update"); if (!isRead && !isWrite) continue; const nested = rest.file; const holder = isRead ? (typeof nested === "object" && nested !== null ? nested : undefined) : rest; const filePath = holder?.filePath; if (typeof filePath !== "string" || !isAbsoluteForFamily(pathFamilyOf({ root: filePath }), filePath)) continue; const content = isRead ? wholeFileFromReadCard(rest) : typeof rest.content === "string" ? rest.content : undefined; if (content === undefined) continue; if (isOffloadedDetailReplacement(content)) continue; byPath.set(filePath, { path: filePath, content, at: m.timestamp }); } return [...byPath.values()]; }