Skip to content

brief-files.ts ​

Source: examples/_shared/tools/brief-files.ts · Download source · Example guide

ts
/** Application-owned release-brief files and deterministic acceptance checks. */
import { createHash, randomUUID } from "node:crypto";
import { mkdir, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
import { isAbsolute, join, relative, resolve } from "node:path";
import type { JsonObject, JsonValue } from "@codesoul-co/ditto/contracts";
import type { RegisteredTool } from "@codesoul-co/ditto/worker/interaction";

export interface CatalogEntry { id: string; fields: string[] }
export interface BriefSpec { required: string[]; catalog: CatalogEntry[]; initialDraft: Record<string, string>; initialEvidence: string[] }
export interface Evidence { sourceId: string; sha256: string; value: string }
export interface Attempt { field: string; sourceId: string; found: boolean }
export interface BriefState { draft: Record<string, string>; evidence: Record<string, Evidence>; attempts: Attempt[]; revision: number }
export interface Snapshot extends BriefState { required: string[]; catalog: CatalogEntry[]; missing: string[]; issues: string[]; complete: boolean; blocked: boolean }
export type Action = { kind: "search"; field: string; sourceId: string } | { kind: "repair"; field: string };
export type StopReason = "goal" | "budget" | "round-limit" | "blocked";
export interface RoundRecord { round: number; action: string; modelCalls: number; snapshot: Snapshot; next: Action | null }
export interface BriefReport { reason: StopReason; status: "completed" | "stopped"; rounds: number; modelCalls: number; snapshot: Snapshot }
export function object(value: unknown): Record<string, unknown> {
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected an object");
  return value as Record<string, unknown>;
}
export function id(value: unknown): string {
  if (typeof value !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(value) || ["constructor", "prototype"].includes(value)) throw new Error("Invalid brief identifier");
  return value;
}
function fields(value: unknown): string[] {
  if (!Array.isArray(value) || value.length > 16) throw new Error("Expected at most sixteen fields");
  const result = value.map(id);
  if (new Set(result).size !== result.length) throw new Error("Duplicate fields");
  return result;
}
function strings(value: unknown): Record<string, string> {
  const result: Record<string, string> = {};
  for (const [key, text] of Object.entries(object(value))) {
    id(key);
    if (typeof text !== "string" || !text.trim() || text.length > 2000 || /[\r\n]/.test(text)) throw new Error("Expected a nonempty single-line value");
    result[key] = text;
  }
  return result;
}
export const json = (value: unknown): JsonObject => JSON.parse(JSON.stringify(value)) as JsonObject;
export const readJson = async (path: string): Promise<unknown> => JSON.parse(await readFile(path, "utf8"));
export function parseSpec(value: unknown): BriefSpec {
  const data = object(value), required = fields(data.required);
  if (!Array.isArray(data.catalog) || data.catalog.length > 32) throw new Error("Expected at most thirty-two sources");
  const catalog = data.catalog.map(item => { const entry = object(item); return { id: id(entry.id), fields: fields(entry.fields) }; });
  if (new Set(catalog.map(item => item.id)).size !== catalog.length) throw new Error("Duplicate sources");
  const initialDraft = strings(data.initialDraft ?? {}), initialEvidence = fields(data.initialEvidence ?? []);
  if (Object.keys(initialDraft).some(field => !required.includes(field)) || initialEvidence.some(source => !catalog.some(item => item.id === source))) throw new Error("Unknown initial field or source");
  return { required, catalog, initialDraft, initialEvidence };
}
export function candidates(snapshot: Snapshot, field: string): CatalogEntry[] {
  return snapshot.catalog.filter(source => source.fields.includes(field) && !snapshot.attempts.some(attempt => attempt.field === field && attempt.sourceId === source.id));
}
export function validateAction(value: unknown, snapshot: Snapshot): Action {
  const action = object(value), field = id(action.field);
  if (action.kind === "search" && snapshot.missing.includes(field) && candidates(snapshot, field).some(source => source.id === action.sourceId)) return { kind: "search", field, sourceId: id(action.sourceId) };
  if (action.kind === "repair" && snapshot.issues.includes(field) && snapshot.evidence[field]) return { kind: "repair", field };
  throw new Error("Plan must address an unresolved field using available evidence or an untried source");
}
async function atomic(path: string, value: unknown) {
  const temporary = `${path}.${randomUUID()}.tmp`;
  try { await writeFile(temporary, JSON.stringify(value, null, 2) + "\n", { flag: "wx" }); await rename(temporary, path); }
  finally { await rm(temporary, { force: true }); }
}

/** One adapter per task directory. A fresh run requires a fresh output directory. */
export function createBriefFiles(options: { inputDirectory: string; outputDirectory: string }) {
  const directory = resolve(options.outputDirectory), statePath = join(directory, "state.json");
  const spec = async () => parseSpec(await readJson(join(directory, "spec.json")));
  const state = async () => await readJson(statePath) as BriefState;
  async function source(sourceId: string): Promise<{ facts: Record<string, string>; sha256: string } | null> {
    const root = await realpath(options.inputDirectory);
    let path: string;
    try { path = await realpath(join(root, `${id(sourceId)}.json`)); }
    catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; throw error; }
    const rel = relative(root, path);
    if (rel.startsWith("..") || isAbsolute(rel)) throw new Error("Source outside the input directory");
    const bytes = await readFile(path);
    if (bytes.length > 1024 * 1024) throw new Error("Source exceeds one MiB");
    return { facts: strings(object(JSON.parse(bytes.toString("utf8"))).facts), sha256: createHash("sha256").update(bytes).digest("hex") };
  }
  async function inspect(): Promise<Snapshot> {
    const definition = await spec(), current = await state();
    // A stale or modified source cannot keep an earlier goal check green.
    for (const [field, evidence] of Object.entries(current.evidence)) {
      const loaded = await source(evidence.sourceId);
      if (!loaded || loaded.sha256 !== evidence.sha256 || loaded.facts[field] !== evidence.value) throw new Error("Evidence source changed; start a fresh task");
    }
    const missing = definition.required.filter(field => !current.evidence[field]);
    const issues = definition.required.filter(field => !current.evidence[field] || current.draft[field] !== current.evidence[field]!.value);
    const snapshot: Snapshot = { ...current, required: definition.required, catalog: definition.catalog, missing, issues, complete: issues.length === 0, blocked: false };
    snapshot.blocked = missing.some(field => candidates(snapshot, field).length === 0);
    return snapshot;
  }
  function tool(name: string, effects: ("read" | "write")[], execute: (args: JsonObject) => Promise<unknown>): RegisteredTool {
    return { name, effects, inputSchema: { type: "object" }, validate(args) { object(args); }, async execute(args, context) {
      context.signal?.throwIfAborted();
      return { status: "success", structuredContent: json(await execute(args)) as JsonValue };
    } };
  }
  const tools = [
    tool("brief_open", ["read", "write"], async () => {
      const definition = parseSpec(await readJson(join(options.inputDirectory, "spec.json")));
      const current: BriefState = { draft: definition.initialDraft, evidence: {}, attempts: [], revision: 0 };
      for (const sourceId of definition.initialEvidence) {
        const loaded = await source(sourceId);
        if (!loaded) throw new Error("Initial evidence source was not found");
        for (const field of definition.required) if (definition.catalog.find(item => item.id === sourceId)!.fields.includes(field) && loaded.facts[field]) {
          if (current.evidence[field] && current.evidence[field]!.value !== loaded.facts[field]) throw new Error("Conflicting initial evidence");
          current.evidence[field] = { sourceId, sha256: loaded.sha256, value: loaded.facts[field]! };
        }
      }
      await mkdir(directory); // EEXIST prevents accidental reuse or concurrent runs.
      await mkdir(join(directory, "rounds")); await mkdir(join(directory, "revisions"));
      await atomic(join(directory, "spec.json"), definition); await atomic(statePath, current);
      await atomic(join(directory, "revisions/0.json"), current.draft);
      return inspect();
    }),
    tool("brief_search", ["read", "write"], async args => {
      const before = await inspect(), action = validateAction({ kind: "search", ...args }, before);
      if (action.kind !== "search") throw new Error("Search action required");
      const loaded = await source(action.sourceId), current = await state();
      const found = !!loaded?.facts[action.field];
      current.attempts.push({ field: action.field, sourceId: action.sourceId, found });
      if (found) current.evidence[action.field] = { sourceId: action.sourceId, sha256: loaded!.sha256, value: loaded!.facts[action.field]! };
      await atomic(statePath, current);
      return inspect();
    }),
    tool("brief_patch", ["read", "write"], async args => {
      const before = await inspect(), patches = strings(args.patches), current = await state();
      if (!Object.keys(patches).length) throw new Error("At least one patch required");
      for (const field of Object.keys(patches)) validateAction({ kind: "repair", field }, before);
      Object.assign(current.draft, patches); current.revision++;
      await atomic(join(directory, `revisions/${current.revision}.json`), current.draft); await atomic(statePath, current);
      return inspect();
    }),
    tool("brief_check", ["read"], inspect),
    tool("brief_record", ["read", "write"], async args => {
      if (!Number.isSafeInteger(args.round) || Number(args.round) < 1 || !Number.isSafeInteger(args.modelCalls) || Number(args.modelCalls) < 0 || typeof args.action !== "string") throw new Error("Invalid round record");
      const snapshot = await inspect(), next = args.next === null ? null : validateAction(args.next, snapshot);
      const record = { round: args.round, action: args.action, modelCalls: args.modelCalls, snapshot, next };
      await writeFile(join(directory, `rounds/${args.round}.json`), JSON.stringify(record, null, 2) + "\n", { flag: "wx" });
      return record;
    }),
    tool("brief_finish", ["read", "write"], async args => {
      const snapshot = await inspect();
      if (!["goal", "budget", "round-limit", "blocked"].includes(String(args.reason)) || (args.reason === "goal") !== snapshot.complete || (args.reason === "blocked" && !snapshot.blocked)) throw new Error("Invalid stop reason for the persisted task");
      if (!Number.isSafeInteger(args.rounds) || Number(args.rounds) < 0 || !Number.isSafeInteger(args.modelCalls) || Number(args.modelCalls) < 0) throw new Error("Invalid task counters");
      const report: BriefReport = { reason: args.reason as StopReason, status: snapshot.complete ? "completed" : "stopped", rounds: Number(args.rounds), modelCalls: Number(args.modelCalls), snapshot };
      // JSON is the canonical artifact; Markdown is a readable projection of verified fields.
      const escape = (text: string) => text.replace(/[\\`*_{}\[\]<>#|]/g, "\\$&");
      await writeFile(join(directory, "brief.md"), `# Release brief\n\nStatus: ${report.status} (${report.reason})\n\n` + snapshot.required.map(field => `- ${field}: ${escape(snapshot.draft[field] ?? "[missing]")}`).join("\n") + "\n");
      await atomic(join(directory, "result.json"), report);
      return report;
    }),
  ];
  return { tools, directory, inspect };
}

Ditto · @codesoul-co/ditto · Node.js 24+