Approval Evidence Helpers
Approval evidence helpers reduce boilerplate around approval-required flows.
They do not add an approval workflow engine, queue, store, or automatic resume behavior.
The application still owns:
- approval request persistence
- reviewer workflow
- grant lifecycle
- resume UX
- policy-specific authorization logic
aioc only standardizes small data transformations around
SuspendedProposal, ApprovalGrant, and proposalHash.
type ApprovalGrant = { proposalHash: string; approvedAt: string; expiresAt?: string; revokedAt?: string; metadata?: Record<string, unknown>;};ApprovalGrant is application-side evidence that a specific suspended proposal
was approved.
Runtime does not consume grants directly. Policy code decides how to interpret them.
type ApprovalRequestSeed = { proposalHash: string; kind: "tool" | "handoff"; reason: string; publicReason?: string; policyVersion?: string; expiresAt?: string; resourceName: string; canonicalPayloadJson: string;};ApprovalRequestSeed is a compact starting point for the application approval
request record.
It is not a storage schema.
createApprovalRequestSeed(...)
Section titled “createApprovalRequestSeed(...)”createApprovalRequestSeed(proposal: SuspendedProposal): ApprovalRequestSeedBuilds an approval-request seed from the SuspendedProposal carried by an
approval-required error or stored in a RunRecord.
For tool proposals:
resourceNameis the tool namecanonicalPayloadJsonisargsCanonicalJson
For handoff proposals:
resourceNameis the target agent namecanonicalPayloadJsonispayloadCanonicalJson
try { await run(agent, input, { context, policies });} catch (error) { if (!(error instanceof ToolCallApprovalRequiredError)) { throw error; }
const approvalRequest = createApprovalRequestSeed( error.result.suspendedProposal, );
await approvalQueue.save(approvalRequest);}isApprovalGrantActive(...)
Section titled “isApprovalGrantActive(...)”isApprovalGrantActive(grant: ApprovalGrant, now?: string): booleanReturns false when:
revokedAtis presentexpiresAtis present and earlier thannow
Otherwise returns true.
If now is omitted, the helper uses the current time.
findActiveApprovalGrant(...)
Section titled “findActiveApprovalGrant(...)”findActiveApprovalGrant( proposalHash: string, grants: readonly ApprovalGrant[], now?: string,): ApprovalGrant | nullFinds an active grant for a proposal hash.
If multiple active grants exist for the same proposal, the one with the most
recent approvedAt wins.
const grant = findActiveApprovalGrant(proposalHash, grants);
if (grant) { return allow("approval_granted");}toApprovedProposalHashes(...)
Section titled “toApprovedProposalHashes(...)”toApprovedProposalHashes( grants: readonly ApprovalGrant[], now?: string,): string[]Projects active grants into the simplest policy-friendly context shape.
const context = { approvedProposalHashes: toApprovedProposalHashes(grants),};Policy code can then remain explicit:
const toolPolicy: ToolPolicy<typeof context> = ({ proposalHash, runContext,}) => { if (runContext.context.approvedProposalHashes.includes(proposalHash)) { return allow("approval_granted"); }
return requireApproval("approval_export_report", { resultMode: "tool_result", publicReason: "Export requires explicit approval.", });};toActiveApprovalGrantMap(...)
Section titled “toActiveApprovalGrantMap(...)”toActiveApprovalGrantMap( grants: readonly ApprovalGrant[], now?: string,): Record<string, ApprovalGrant>Projects active grants into a map keyed by proposalHash.
Use this when policy code needs grant metadata, for example reviewer or workflow information.
const context = { activeApprovalGrants: toActiveApprovalGrantMap(grants),};
const grant = context.activeApprovalGrants[proposalHash];Enforcement Boundary
Section titled “Enforcement Boundary”Approval grants do not execute tools or handoffs.
The boundary remains:
ApprovalGrant -> policy reevaluation -> allow | deny | require_approvalThis keeps approval evidence inside the deterministic policy path instead of turning it into a runtime bypass.