import * as plugins from '../plugins.js'; import type { AuditLog } from '../audit/classes.auditlog.js'; export class ApprovalQueue { private approvals: plugins.shxInterfaces.data.IApprovalRequest[] = []; constructor(private auditLog: AuditLog) {} public createApproval(optionsArg: { plan: plugins.shxInterfaces.data.IToolPlan; call: plugins.shxInterfaces.data.IToolCall; agentId: string; title: string; reason: string; requestedScopes: plugins.shxInterfaces.data.TToolScope[]; }) { const approval: plugins.shxInterfaces.data.IApprovalRequest = { id: `approval:${Date.now()}:${Math.random().toString(36).slice(2)}`, planId: optionsArg.plan.id, callId: optionsArg.call.id, agentId: optionsArg.agentId, title: optionsArg.title, reason: optionsArg.reason, confidence: optionsArg.plan.confidence, requestedScopes: optionsArg.requestedScopes, status: 'pending', createdAt: new Date().toISOString(), }; this.approvals.unshift(approval); return approval; } public listApprovals(filterArg: { status?: plugins.shxInterfaces.data.IApprovalRequest['status'] } = {}) { return this.approvals.filter((approvalArg) => !filterArg.status || approvalArg.status === filterArg.status); } public submitApproval(approvalIdArg: string, decisionArg: 'approved' | 'rejected') { const approval = this.approvals.find((approvalArg) => approvalArg.id === approvalIdArg); if (!approval) { throw new Error(`Approval not found: ${approvalIdArg}`); } if (approval.status !== 'pending') { throw new Error(`Approval already decided: ${approvalIdArg}`); } approval.status = decisionArg; approval.decidedAt = new Date().toISOString(); const receipt = this.auditLog.appendReceipt({ kind: 'approval', callerId: approval.agentId, approvalId: approval.id, inputSummary: approval.title, outputSummary: decisionArg, reversible: false, }); return { approval, receipt }; } }