Files
hub/ts/approvals/classes.approvalqueue.ts
T

58 lines
2.0 KiB
TypeScript
Raw Normal View History

2026-05-05 12:03:45 +00:00
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 };
}
}