Approval Flows
require_approval blocks execution, but it does not define an approval product.
aioc gives you the runtime contract:
- a typed approval-required outcome
- a canonical
SuspendedProposal - a stable
proposalHash
Your application still owns:
- approval requests
- reviewer workflow
- notifications
- thread UX
- resume actions
What Gets Blocked
Section titled “What Gets Blocked”When the model proposes a tool call or a handoff:
- runtime evaluates policy
- policy returns
require_approval - runtime does not execute the tool or transition the handoff
- runtime constructs a
SuspendedProposal
That proposal contains:
- the operational proposal itself
- canonical arguments or payload
proposalHash- policy-facing metadata such as
reason,publicReason,policyVersion, andexpiresAt
proposalHash identifies the proposal, not the approval workflow.
What The App Stores
Section titled “What The App Stores”The host application should create its own approval request from the SuspendedProposal.
The minimum useful binding key is:
proposalHash
The rest is application-specific:
- request status (
pending,approved,rejected,expired,dismissed) - reviewer identity
- timestamps
- business justification
- notification state
aioc does not prescribe this storage model.
How Approval Evidence Re-Enters Policy
Section titled “How Approval Evidence Re-Enters Policy”The usual pattern is:
- the application stores approval externally
- later it makes that approval evidence available to the next
run(...) - policy checks whether the current
proposalHashis approved - policy returns
alloworrequire_approval
Minimal example:
type ApprovalEvidenceContext = { approvedProposalHashes: string[];};
const toolPolicy: ToolPolicy<ApprovalEvidenceContext> = ({ proposalHash, runContext,}) => { if (runContext.context.approvedProposalHashes.includes(proposalHash)) { return allow("approval_granted"); }
return requireApproval("approval_export_report", { publicReason: "Export requires explicit approval.", });};This is why ToolPolicyInput and HandoffPolicyInput expose:
proposalHash- canonical payload (
argsCanonicalJsonorpayloadCanonicalJson)
The policy should not need to reimplement fingerprint logic.
Why Policy Remains The Enforcement Point
Section titled “Why Policy Remains The Enforcement Point”Approval does not execute anything by itself.
The sequence is:
- proposal is blocked
- app records approval externally
- app starts a new run with approval evidence in
context - policy evaluates again
- only
allowpermits execution
This matters because a stored approval may still be insufficient:
- wrong proposal hash
- expired grant
- revoked grant
- additional contextual deny conditions
If the runtime converted approved hash => allow by itself, it would be taking governance decisions away from the application.
Tool Policy vs Handoff Policy
Section titled “Tool Policy vs Handoff Policy”ToolPolicygoverns capability executionHandoffPolicygoverns transfer of control between agents
The approval pattern is the same in both cases:
- runtime blocks a proposal
- app stores approval externally
- policy reevaluates using
proposalHash
Privacy Note
Section titled “Privacy Note”If approval evidence is passed through context, remember that record.contextSnapshot may persist it in RunRecord.
If that data is sensitive, use:
record.contextRedactor
to avoid storing approval details in clear text.
Example
Section titled “Example”See:
npm run example:approval-evidencesrc/examples/basic/approval-evidence.ts