@sema-agent/core
Version:
Stateless, task-oriented AI agent core
100 lines (99 loc) • 4.88 kB
JavaScript
import { isCentralAuthorityScope, parseScopeKey } from "./scope-contract.js";
import { CONTROL_PLANE_DIR } from "./layout.js";
import { join } from "node:path";
export function derivePersonalMemoryDir(configRoot) {
return join(configRoot, "personal", "memory");
}
export function derivePersonalControlDir(configRoot) {
return join(configRoot, "personal", CONTROL_PLANE_DIR);
}
export function classifyScopePlanes(scopes, writeScope) {
const project = [];
const personal = [];
for (const s of scopes) {
(isCentralAuthorityScope(parseScopeKey(s)) ? personal : project).push(s);
}
const writePlane = writeScope === null ? null : isCentralAuthorityScope(parseScopeKey(writeScope)) ? "personal" : "project";
return { project, personal, writePlane };
}
export function needsDualRoots(planes) {
const projectActive = planes.project.length > 0 || planes.writePlane === "project";
const personalActive = planes.personal.length > 0 || planes.writePlane === "personal";
return projectActive && personalActive;
}
export function parsedProjectPlane(planes, writeScope) {
const keys = [...planes.project, ...(planes.writePlane === "project" && writeScope !== null ? [writeScope] : [])];
return keys.map(parseScopeKey);
}
export function mergeInjections(project, personal) {
if (!project)
return personal ?? { instruction: "", block: "" };
if (!personal)
return project;
const instruction = project.instruction || personal.instruction;
const readOnlyNotice = instruction === "" && project.readOnlyNotice !== undefined && personal.readOnlyNotice !== undefined ? project.readOnlyNotice : undefined;
const indexParts = [project.index, personal.index].filter((s) => Boolean(s && s.trim()));
const announcements = [...(project.announcements ?? []), ...(personal.announcements ?? [])];
const announceParts = [project.announceBlock, personal.announceBlock].filter((s) => Boolean(s && s.trim()));
const blockParts = [instruction || readOnlyNotice, ...indexParts, ...announceParts];
const indexSeed = project.indexSeed ?? personal.indexSeed;
return {
instruction,
...(readOnlyNotice !== undefined ? { readOnlyNotice } : {}),
...(indexParts.length > 0 ? { index: indexParts.join("\n\n") } : {}),
...(announcements.length > 0 ? { announcements } : {}),
...(announceParts.length > 0 ? { announceBlock: announceParts.join("\n\n") } : {}),
...(indexSeed !== undefined ? { indexSeed } : {}),
block: blockParts.filter((s) => s && s.trim()).join("\n\n"),
};
}
export function mergeHarvestReports(a, b) {
const merged = {
ok: a.ok && b.ok,
patches: {
add: a.patches.add + b.patches.add,
update: a.patches.update + b.patches.update,
delete: a.patches.delete + b.patches.delete,
},
conflicts: [...a.conflicts, ...b.conflicts],
rejections: [...a.rejections, ...b.rejections],
quarantined: [...a.quarantined, ...b.quarantined],
movedToQuarantine: [...a.movedToQuarantine, ...b.movedToQuarantine],
missing: [...a.missing, ...b.missing],
restored: [...a.restored, ...b.restored],
warnings: [...a.warnings, ...b.warnings],
};
const reportId = a.reportId ?? b.reportId;
if (reportId !== undefined)
merged.reportId = reportId;
const parts = [...new Set([a.reportId, b.reportId].filter((x) => x !== undefined))];
if (parts.length > 1)
merged.mergedFrom = parts;
const incident = a.incident ?? b.incident;
if (incident !== undefined)
merged.incident = incident;
const degraded = a.degraded ?? b.degraded;
if (degraded !== undefined)
merged.degraded = degraded;
const inbound = [...(a.inboundFindings ?? []), ...(b.inboundFindings ?? [])];
if (inbound.length > 0)
merged.inboundFindings = inbound;
const qf = [...(a.quarantineFailures ?? []), ...(b.quarantineFailures ?? [])];
if (qf.length > 0)
merged.quarantineFailures = qf;
const rec = a.consolidationRecommended ?? b.consolidationRecommended;
if (rec !== undefined)
merged.consolidationRecommended = rec;
const ac = a.containment;
const bc = b.containment;
if (ac !== undefined || bc !== undefined) {
merged.containment = {
indexRolledBack: ac?.indexRolledBack === true || bc?.indexRolledBack === true,
quarantinedInstruction: [...(ac?.quarantinedInstruction ?? []), ...(bc?.quarantinedInstruction ?? [])],
heldInstruction: [...(ac?.heldInstruction ?? []), ...(bc?.heldInstruction ?? [])],
releasedHolds: [...(ac?.releasedHolds ?? []), ...(bc?.releasedHolds ?? [])],
disposedHolds: [...(ac?.disposedHolds ?? []), ...(bc?.disposedHolds ?? [])],
};
}
return merged;
}