import { randomUUID } from "node:crypto"; import { unlink } from "node:fs/promises"; import { BOARD_TYPES, WAR_ROOM_MEMBERS, type BoardEvent, type CoordinationAction, type WarRoomSignal, type WarRoomWorkerBinding } from "../workflows/war-room.js"; import type { WarRoomBindings } from "../backends/pi-agents-backend.js"; import { sameJsonValue } from "./provenance.js"; import { isTerminal } from "./state-machine.js"; import type { AgentFact } from "../types.js"; import type { AcceptedWarRoomSignal, ActiveRun, WarRoomParticipant, WarRoomWriterFact } from "./run-state.js"; /** * War Room bookkeeping: who is on the board right now, which one-shot credential lets them * post, and which board events a writer is allowed to stand on. These operate only on the run * record, which is why they live outside the controller — none of them can start work, spend * credits, or record telemetry, and keeping that true is the point of the separation. */ export function retireWarRoomCredential(active: ActiveRun, author: string): void { const credential = active.warRoomParticipantCredentials?.get(author); if (!credential) return; active.warRoomSignalBindings?.get(credential)?.settle(); active.warRoomSignalBindings?.delete(credential); active.warRoomSignalReceipts?.delete(credential); active.warRoomParticipantCredentials?.delete(author); } export function issueWarRoomCredential(active: ActiveRun, author: string, instance: string): WarRoomWorkerBinding { if (!active.warRoomSignalPath) throw new Error("War Room signal bridge is unavailable"); retireWarRoomCredential(active, author); const credential = randomUUID(); let settle!: () => void; const ready = new Promise((resolvePromise) => { settle = resolvePromise; }); (active.warRoomSignalBindings ??= new Map()).set(credential, { parentRunId: active.identity.runId, author, instance, generation: active.warRoomGeneration ?? 1, ready, settle }); (active.warRoomParticipantCredentials ??= new Map()).set(author, credential); return { signalPath: active.warRoomSignalPath, credential }; } export function issueWarRoomRoundCredentials(active: ActiveRun): WarRoomBindings { active.warRoomGeneration = (active.warRoomGeneration ?? 0) + 1; return Object.fromEntries([...WAR_ROOM_MEMBERS].map((author) => [author, issueWarRoomCredential(active, author, `$.branches.${author}`)])) as WarRoomBindings; } export function activateWarRoomParticipant(active: ActiveRun, author: string, piAgentsRunId: string, instance: string): void { const credential = active.warRoomParticipantCredentials?.get(author); const binding = credential ? active.warRoomSignalBindings?.get(credential) : undefined; if (!credential || !binding || binding.parentRunId !== active.identity.runId || binding.author !== author || binding.instance !== instance || binding.generation !== active.warRoomGeneration) throw new Error("War Room participant credential is unavailable"); binding.delegatedRunId = piAgentsRunId; binding.settle(); (active.warRoomParticipants ??= new Map()).set(author, { runId: piAgentsRunId, instance, credential, generation: binding.generation, state: "pending" }); } export function registerWarRoomRoundParticipants(active: ActiveRun, piAgentsRunId: string): void { for (const author of WAR_ROOM_MEMBERS) activateWarRoomParticipant(active, author, piAgentsRunId, `$.branches.${author}`); } export function registerWarRoomSpecialist(active: ActiveRun, author: string, piAgentsRunId: string, instance: string): void { activateWarRoomParticipant(active, author, piAgentsRunId, instance); } export function setWarRoomParticipantState(active: ActiveRun, piAgentsRunId: string, instance: string, state: WarRoomParticipant["state"]): void { for (const [author, participant] of active.warRoomParticipants?.entries() ?? []) { if (participant.runId === piAgentsRunId && participant.instance === instance) { participant.state = state; if (state === "completed") retireWarRoomCredential(active, author); } } } export function completeWarRoomParticipantRun(active: ActiveRun, piAgentsRunId: string): void { for (const [author, participant] of active.warRoomParticipants?.entries() ?? []) if (participant.runId === piAgentsRunId) { participant.state = "completed"; retireWarRoomCredential(active, author); } } export function unsettledWarRoomSpecialistRuns(active: ActiveRun): Set { return new Set([...active.warRoomParticipants?.entries() ?? []].filter(([author, participant]) => active.warRoomSpecialists?.has(author) && active.delegatedRunStates?.get(participant.runId) !== "settled").map(([, participant]) => participant.runId)); } export function liveWarRoomParticipantCount(active: ActiveRun): number { return [...active.warRoomParticipants?.values() ?? []].filter((participant) => participant.state !== "completed").length; } export async function closeWarRoomSignalBridge(active: ActiveRun): Promise { const server = active.warRoomSignalServer; active.warRoomSignalServer = undefined; const path = active.warRoomSignalPath; active.warRoomSignalPath = undefined; for (const binding of active.warRoomSignalBindings?.values() ?? []) binding.settle(); if (server) await new Promise((resolvePromise) => server.close(() => resolvePromise())); if (path) await unlink(path).catch(() => undefined); await Promise.allSettled([...(active.warRoomSignalDeliveries ?? [])]); active.warRoomSignalBindings?.clear(); active.warRoomParticipantCredentials?.clear(); active.warRoomSignalReceipts?.clear(); } export function parseWarRoomSignal(raw: unknown): WarRoomSignal { if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new Error("War Room signal is malformed"); const signal = raw as Record; const fields = new Set(["credential", "signalId", "type", "target", "targetCursor", "claim", "evidence"]); if (Object.keys(signal).some((field) => !fields.has(field)) || typeof signal.credential !== "string" || signal.credential.length < 16 || signal.credential.length > 128 || typeof signal.signalId !== "string" || !signal.signalId.trim() || signal.signalId.length > 128 || typeof signal.type !== "string" || !["evidence", "challenge", "request", "blocker"].includes(signal.type) || typeof signal.claim !== "string" || !signal.claim.trim() || signal.claim.length > 1000 || signal.target !== undefined && (typeof signal.target !== "string" || !signal.target.trim() || signal.target.length > 256) || signal.targetCursor !== undefined && (!Number.isSafeInteger(signal.targetCursor) || (signal.targetCursor as number) < 1) || signal.evidence !== undefined && (!Array.isArray(signal.evidence) || signal.evidence.length > 8 || signal.evidence.some((item) => typeof item !== "string" || !item.trim() || item.length > 256))) throw new Error("War Room signal is malformed"); return signal as unknown as WarRoomSignal; } export function assertWarRoomTarget(active: ActiveRun, event: BoardEvent): void { if (!event.target) return; if (event.target === event.author) throw new Error("War Room signal target cannot be its author"); const participant = active.warRoomParticipants?.get(event.target); if (!participant) throw new Error("War Room signal target is unknown"); if (participant.state !== "live") throw new Error(`War Room signal target is ${participant.state}`); } export function warRoomWriterFacts(active: ActiveRun): WarRoomWriterFact[] { const board = active.warRoom?.events ?? []; const hypotheses = new Map(board.filter((event) => event.type === "hypothesis").map((event) => [event.cursor, event])); return board.flatMap((event): WarRoomWriterFact[] => { if (event.type === "evidence" && event.evidence?.length) return [{ id: `F:${active.identity.runId}:warroom:e${event.cursor}`, claim: event.claim, evidence: [...event.evidence], boardProvenance: { qualification: "direct-evidence", source: { cursor: event.cursor, author: event.author, eventType: "evidence" } }, }]; if (event.type !== "confirmation" || !event.evidence?.length || event.targetCursor === undefined) return []; const hypothesis = hypotheses.get(event.targetCursor); if (!hypothesis || hypothesis.author === event.author) return []; return [{ id: `F:${active.identity.runId}:warroom:h${hypothesis.cursor}:c${event.cursor}`, claim: hypothesis.claim, evidence: [...event.evidence], boardProvenance: { qualification: "confirmed-hypothesis", source: { cursor: hypothesis.cursor, author: hypothesis.author, eventType: "hypothesis" }, confirmation: { cursor: event.cursor, author: event.author, eventType: "confirmation", evidence: [...event.evidence] }, }, }]; }); } export function warRoomWriterAttributionFacts(active: ActiveRun): AgentFact[] { const canonical = warRoomWriterFacts(active); const retained = active.envelope?.knownFacts ?? []; const selected: WarRoomWriterFact[] = []; const seen = new Set(); for (const retainedFact of retained) { if (seen.has(retainedFact.id)) throw new Error(`War Room writer attribution retained fact ${retainedFact.id} is duplicated`); seen.add(retainedFact.id); const matches = canonical.filter((fact) => fact.id === retainedFact.id); if (matches.length !== 1) throw new Error(`War Room writer attribution retained fact ${retainedFact.id} is missing from the canonical board`); const match = matches[0]!; if (!sameJsonValue(retainedFact, match)) throw new Error(`War Room writer attribution retained fact ${retainedFact.id} does not match the canonical board`); selected.push(match); } return selected.map((fact) => ({ id: fact.id, claim: fact.claim, evidence: fact.evidence.map((observation) => ({ observation })), confidence: fact.boardProvenance.qualification === "confirmed-hypothesis" ? 0.9 : 0.7 })); } export function acceptWarRoomSignal(active: ActiveRun, raw: unknown): AcceptedWarRoomSignal { const signal = parseWarRoomSignal(raw); const binding = active.warRoomSignalBindings?.get(signal.credential); const participant = binding ? active.warRoomParticipants?.get(binding.author) : undefined; if (!binding || binding.parentRunId !== active.identity.runId || binding.generation !== active.warRoomGeneration || !binding.delegatedRunId || !participant || participant.credential !== signal.credential || participant.generation !== binding.generation || participant.runId !== binding.delegatedRunId || participant.instance !== binding.instance || participant.state !== "live") throw new Error("War Room signal credential is invalid or retired"); if (active.warRoomFinalizing || !active.warRoom || active.warRoomPhase !== "rounds" || active.terminalIntent || isTerminal(active.state)) throw new Error("War Room signal has no active round"); const receipts = active.warRoomSignalReceipts?.get(signal.credential); const duplicate = receipts?.get(signal.signalId); if (duplicate !== undefined) return { event: active.warRoom.events[duplicate - 1]!, record: active.warRoom.events[duplicate - 1]!, actions: [], duplicate: true }; const event = { type: signal.type, author: binding.author, claim: signal.claim, ...(signal.target ? { target: signal.target } : {}), ...(signal.targetCursor !== undefined ? { targetCursor: signal.targetCursor } : {}), ...(signal.evidence ? { evidence: signal.evidence } : {}) } as BoardEvent; assertWarRoomTarget(active, event); const actions = active.warRoom.accept(event); const record = active.warRoom.events.at(-1)!; (active.warRoomSignalReceipts ??= new Map()).set(signal.credential, receipts ?? new Map([[signal.signalId, record.cursor]])); if (receipts) receipts.set(signal.signalId, record.cursor); return { event, record, actions }; }