@sema-agent/core
Version:
Stateless, task-oriented AI agent core
181 lines (180 loc) • 6.25 kB
JavaScript
import { mkdirSync, promises as fs } from "node:fs";
import path from "node:path";
import { verifyPromptArtifact, } from "./artifact.js";
function normalizedEnvelope(parsed, artifact) {
return { ...parsed, artifact };
}
export class MemoryPromptArtifactStore {
byDigest = new Map();
async get(artifactDigest, opts) {
const bytes = this.byDigest.get(artifactDigest);
if (bytes === undefined)
return undefined;
let parsed;
try {
parsed = JSON.parse(bytes);
}
catch {
return undefined;
}
const v = verifyPromptArtifact(parsed, opts);
if (!v.ok || v.artifact.artifactDigest !== artifactDigest)
return undefined;
return normalizedEnvelope(parsed, v.artifact);
}
async put(envelope) {
this.byDigest.set(envelope.artifact.artifactDigest, JSON.stringify(envelope));
}
}
export class FilePromptArtifactStore {
dir;
constructor(dir) {
this.dir = dir;
mkdirSync(path.join(dir, "artifacts"), { recursive: true });
}
fileFor(artifactDigest) {
const m = /^sha256:([0-9a-f]{64})$/.exec(artifactDigest);
return m ? path.join(this.dir, "artifacts", `${m[1]}.json`) : undefined;
}
async get(artifactDigest, opts) {
const file = this.fileFor(artifactDigest);
if (file === undefined)
return undefined;
let bytes;
try {
bytes = await fs.readFile(file, "utf8");
}
catch {
return undefined;
}
let parsed;
try {
parsed = JSON.parse(bytes);
}
catch {
return undefined;
}
const v = verifyPromptArtifact(parsed, opts);
if (!v.ok || v.artifact.artifactDigest !== artifactDigest)
return undefined;
return normalizedEnvelope(parsed, v.artifact);
}
async put(envelope) {
const file = this.fileFor(envelope.artifact.artifactDigest);
if (file === undefined)
throw new Error(`prompt-artifact store: malformed digest ${JSON.stringify(envelope.artifact.artifactDigest).slice(0, 120)}`);
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
await fs.writeFile(tmp, JSON.stringify(envelope), "utf8");
await fs.rename(tmp, file);
}
}
export class MemoryPromptSourceStateStore {
state;
async load() {
return this.state;
}
async save(state) {
this.state = state;
}
}
export class FilePromptSourceStateStore {
file;
constructor(dir) {
mkdirSync(dir, { recursive: true });
this.file = path.join(dir, "source-state.json");
}
async load() {
let bytes;
try {
bytes = await fs.readFile(this.file, "utf8");
}
catch {
return undefined;
}
try {
const raw = JSON.parse(bytes);
if (raw?.state === "active" && typeof raw.artifactDigest === "string" && typeof raw.sourceRevision === "string") {
return { state: "active", artifactDigest: raw.artifactDigest, sourceRevision: raw.sourceRevision };
}
if (raw?.state === "disabled" && typeof raw.sourceRevision === "string") {
return { state: "disabled", sourceRevision: raw.sourceRevision };
}
}
catch {
}
return undefined;
}
async save(state) {
const tmp = `${this.file}.tmp-${process.pid}-${Date.now()}`;
await fs.writeFile(tmp, JSON.stringify(state), "utf8");
await fs.rename(tmp, this.file);
}
}
export class CenterPromptSource {
store;
stateStore;
engineVersion;
candidate;
state = "unavailable";
tail = Promise.resolve();
exclusive(fn) {
const run = this.tail.then(fn, fn);
this.tail = run.then(() => undefined, () => undefined);
return run;
}
constructor(opts) {
this.store = opts.store;
this.stateStore = opts.stateStore ?? new MemoryPromptSourceStateStore();
this.engineVersion = opts.engineVersion;
}
current() {
return this.candidate;
}
async get(artifactDigest) {
if (this.candidate?.artifact.artifactDigest === artifactDigest)
return this.candidate.artifact;
const env = await this.store.get(artifactDigest, { engineVersion: this.engineVersion });
return env?.artifact;
}
async restore() {
return this.exclusive(async () => {
const state = await this.stateStore.load();
if (state?.state === "disabled") {
this.candidate = undefined;
this.state = "disabled";
return;
}
if (state?.state !== "active") {
this.candidate = undefined;
this.state = "unavailable";
return;
}
const env = await this.store.get(state.artifactDigest, { engineVersion: this.engineVersion });
this.candidate = env !== undefined ? { artifact: env.artifact, sourceRevision: state.sourceRevision } : undefined;
this.state = env !== undefined ? "active" : "unavailable";
});
}
sourceState() {
return this.state;
}
async applySnapshot(raw) {
const v = verifyPromptArtifact(raw, { engineVersion: this.engineVersion });
if (!v.ok)
return v;
const envelope = raw;
return this.exclusive(async () => {
await this.store.put(envelope);
await this.stateStore.save({ state: "active", artifactDigest: v.artifact.artifactDigest, sourceRevision: envelope.sourceRevision });
this.candidate = { artifact: v.artifact, sourceRevision: envelope.sourceRevision };
this.state = "active";
return { ok: true, artifactDigest: v.artifact.artifactDigest };
});
}
async applyDisabled(sourceRevision) {
return this.exclusive(async () => {
await this.stateStore.save({ state: "disabled", sourceRevision });
this.candidate = undefined;
this.state = "disabled";
});
}
}