import { createHash } from "node:crypto"; import { MongoServerError, ObjectId } from "mongodb"; import { collections } from "$lib/server/database"; import type { AgentConfigurationAction, AgentConfigurationKey, AgentConfigurationReceipt, Workspace, } from "$lib/types/Workspace"; export type AgentConfigurationPayload = | { action: "set-model"; model: string } | { action: "set-thinking"; level: string } | { action: "set-policy"; policy: string }; export type AgentConfigurationReceiptState = | { status: "missing" } | { status: "pending"; receipt: AgentConfigurationReceipt } | { status: "completed"; receipt: AgentConfigurationReceipt } | { status: "conflict" } | { status: "stale" }; /** Canonical hash which binds one stable client id to one exact setting. */ export function fingerprintAgentConfiguration(payload: AgentConfigurationPayload): string { return createHash("sha256") .update( JSON.stringify({ action: payload.action, value: configurationValue(payload), }) ) .digest("hex"); } /** Read-only preflight lets completed retries succeed without opening the sandbox. */ export async function inspectAgentConfiguration(options: { workspaceId: Workspace["_id"]; controlId: string; fingerprint: string; }): Promise { const receipt = await collections.agentConfigurationReceipts.findOne({ workspaceId: options.workspaceId, controlId: options.controlId, }); return classifyReceipt(receipt, options.fingerprint); } /** * Append a command to the durable per-workspace log and make it latest for its * setting kind. Receipts are separate documents, so stable ids are never * evicted and a hostile number of settings cannot grow the Workspace document. * * The placeholder's baseSequence closes both sides of the two-document crash * window: a missing head update can be resumed, while a later request which * advanced the same setting causes the older placeholder to be superseded. */ export async function registerAgentConfiguration(options: { workspace: Workspace; controlId: string; payload: AgentConfigurationPayload; now?: Date; }): Promise { const fingerprint = fingerprintAgentConfiguration(options.payload); const now = options.now ?? new Date(); const key = configurationKey(options.payload.action); const sandbox = options.workspace.sandbox; if (!sandbox.id || !sandbox.generation || !sandbox.nonce) return { status: "stale" }; await ensureLedger(options.workspace, now); for (;;) { const existing = await collections.agentConfigurationReceipts.findOne({ workspaceId: options.workspace._id, controlId: options.controlId, }); if (existing) { return resumeReceipt({ workspace: options.workspace, receipt: existing, fingerprint, key, now, }); } const ledger = await collections.agentConfigurationLedgers.findOne({ _id: options.workspace._id, }); if (!ledger) { await ensureLedger(options.workspace, now); continue; } const receipt: AgentConfigurationReceipt = { _id: new ObjectId(), workspaceId: options.workspace._id, conversationId: options.workspace.conversationId, controlId: options.controlId, fingerprint, action: options.payload.action, value: configurationValue(options.payload), baseSequence: ledger.nextSequence, status: "pending", requestedAt: now, sandboxId: sandbox.id, sandboxGeneration: sandbox.generation, sandboxNonce: sandbox.nonce, createdAt: now, updatedAt: now, }; try { await collections.agentConfigurationReceipts.insertOne(receipt); } catch (cause) { if (cause instanceof MongoServerError && cause.code === 11000) continue; throw cause; } return resumeReceipt({ workspace: options.workspace, receipt, fingerprint, key, now, }); } } export async function completeAgentConfiguration(options: { workspaceId: Workspace["_id"]; controlId: string; fingerprint: string; action: AgentConfigurationAction; now?: Date; }): Promise { const now = options.now ?? new Date(); const receipt = await collections.agentConfigurationReceipts.findOne({ workspaceId: options.workspaceId, controlId: options.controlId, }); const classified = classifyReceipt(receipt, options.fingerprint); if (classified.status === "completed") return true; if (classified.status !== "pending" || classified.receipt.sequence === undefined) return false; const key = configurationKey(options.action); const ledger = await collections.agentConfigurationLedgers.findOne({ _id: options.workspaceId, }); const latest = ledger?.latest[key]; if ( !latest || latest.controlId !== options.controlId || latest.fingerprint !== options.fingerprint || latest.sequence !== classified.receipt.sequence ) { if (latest && latest.sequence > classified.receipt.sequence) { return markSuperseded(classified.receipt, options.fingerprint, now); } return false; } const completed = await collections.agentConfigurationReceipts.updateOne( { _id: classified.receipt._id, fingerprint: options.fingerprint, status: "pending", sequence: classified.receipt.sequence, }, { $set: { status: "completed", completedAt: now, updatedAt: now }, } ); if (completed.matchedCount === 1) return true; const current = await inspectAgentConfiguration({ workspaceId: options.workspaceId, controlId: options.controlId, fingerprint: options.fingerprint, }); return current.status === "completed"; } async function ensureLedger(workspace: Workspace, now: Date): Promise { await collections.agentConfigurationLedgers.updateOne( { _id: workspace._id }, { $setOnInsert: { conversationId: workspace.conversationId, nextSequence: 0, latest: {}, createdAt: now, updatedAt: now, }, }, { upsert: true } ); } async function resumeReceipt(options: { workspace: Workspace; receipt: AgentConfigurationReceipt; fingerprint: string; key: AgentConfigurationKey; now: Date; }): Promise { let receipt = options.receipt; const classified = classifyReceipt(receipt, options.fingerprint); if (classified.status !== "pending") return classified; const sandbox = options.workspace.sandbox; if ( receipt.sandboxId !== sandbox.id || receipt.sandboxGeneration !== sandbox.generation || receipt.sandboxNonce !== sandbox.nonce ) { return { status: "stale" }; } for (;;) { const ledger = await collections.agentConfigurationLedgers.findOne({ _id: options.workspace._id, }); if (!ledger) return { status: "stale" }; const latest = ledger.latest[options.key]; if (receipt.sequence !== undefined) { if (latest && latest.sequence > receipt.sequence) { await markSuperseded(receipt, options.fingerprint, options.now); return inspectAgentConfiguration({ workspaceId: options.workspace._id, controlId: receipt.controlId, fingerprint: options.fingerprint, }); } if ( latest?.controlId === receipt.controlId && latest.fingerprint === options.fingerprint && latest.sequence === receipt.sequence ) { return { status: "pending", receipt }; } return { status: "stale" }; } if (latest?.controlId === receipt.controlId && latest.fingerprint === options.fingerprint) { const restored = await collections.agentConfigurationReceipts.findOneAndUpdate( { _id: receipt._id, fingerprint: options.fingerprint, sequence: { $exists: false } }, { $set: { sequence: latest.sequence, updatedAt: options.now } }, { returnDocument: "after", includeResultMetadata: false } ); if (restored) return { status: "pending", receipt: restored }; const current = await collections.agentConfigurationReceipts.findOne({ _id: receipt._id }); if (!current) return { status: "stale" }; receipt = current; continue; } if (latest && latest.sequence > receipt.baseSequence) { await markSuperseded(receipt, options.fingerprint, options.now); return inspectAgentConfiguration({ workspaceId: options.workspace._id, controlId: receipt.controlId, fingerprint: options.fingerprint, }); } const sequence = ledger.nextSequence + 1; const advanced = await collections.agentConfigurationLedgers.updateOne( { _id: options.workspace._id, nextSequence: ledger.nextSequence }, { $inc: { nextSequence: 1 }, $set: { [`latest.${options.key}`]: { controlId: receipt.controlId, fingerprint: options.fingerprint, sequence, }, updatedAt: options.now, }, } ); if (advanced.matchedCount !== 1) continue; const assigned = await collections.agentConfigurationReceipts.findOneAndUpdate( { _id: receipt._id, fingerprint: options.fingerprint, sequence: { $exists: false } }, { $set: { sequence, updatedAt: options.now } }, { returnDocument: "after", includeResultMetadata: false } ); if (assigned) return { status: "pending", receipt: assigned }; const current = await collections.agentConfigurationReceipts.findOne({ _id: receipt._id }); if (!current) return { status: "stale" }; receipt = current; } } async function markSuperseded( receipt: AgentConfigurationReceipt, fingerprint: string, now: Date ): Promise { const superseded = await collections.agentConfigurationReceipts.updateOne( { _id: receipt._id, fingerprint, status: "pending" }, { $set: { status: "superseded", completedAt: now, updatedAt: now } } ); if (superseded.matchedCount === 1) return true; const current = await collections.agentConfigurationReceipts.findOne({ _id: receipt._id }); return current?.fingerprint === fingerprint && current.status !== "pending"; } function classifyReceipt( receipt: AgentConfigurationReceipt | null | undefined, fingerprint: string ): AgentConfigurationReceiptState { if (!receipt) return { status: "missing" }; if (receipt.fingerprint !== fingerprint) return { status: "conflict" }; return receipt.status === "pending" ? { status: "pending", receipt } : { status: "completed", receipt }; } function configurationKey(action: AgentConfigurationAction): AgentConfigurationKey { switch (action) { case "set-model": return "model"; case "set-thinking": return "thinking"; case "set-policy": return "policy"; } } function configurationValue(payload: AgentConfigurationPayload): string { switch (payload.action) { case "set-model": return payload.model; case "set-thinking": return payload.level; case "set-policy": return payload.policy; } }