UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

181 lines (180 loc) • 6.25 kB
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"; }); } }