RFC-0016: Authorized Tool Argument Snapshots
Generated from the repository source of truth. Source file:
/docs/RFC-0016-authorized-tool-argument-snapshots.md. Status:Accepted.
- Status: Accepted
- Date: 2026-07-15
- Accepted: 2026-07-17
- Owners: aioc maintainers
- Depends on: RFC-0002, RFC-0004, RFC-0005, RFC-0015
- Amends: RFC-0005, RFC-0015
Context
Section titled “Context”RFC-0015 moves tool-schema validation and normalization before policy
evaluation. It also requires policy and tool execution to receive the same
parsedArguments object.
Sharing that mutable object closes the pre-schema authorization gap, but leaves an authorization-to-execution alias:
- policy code can retain the object and mutate it after returning;
- asynchronous tracing between policy and execution creates a mutation window;
- a Zod transform can return an object that is also reachable through an application-owned reference;
- suspended proposals currently recompute their fingerprint from a mutable object.
The post-policy canonicalization check detects mutations completed while policy is running. It does not make the authorized value independent from every mutable reference that existed before execution.
The current general-purpose canonicalizer also accepts JavaScript values that
do not have a one-to-one JSON representation. For example, a Date and its ISO
string, or a Set and an array, can produce the same canonical JSON while
remaining operationally different values.
Approval evidence should not depend on an AIOC-specific serialization format. RFC 8785, the JSON Canonicalization Scheme (JCS), provides an interoperable canonical representation for I-JSON data.
Authorization evidence must identify the action that will execute, not merely an object that was equivalent at one earlier observation point.
Decision
Section titled “Decision”AIOC represents effective tool arguments as an immutable RFC 8785 canonical JSON snapshot before invoking policy.
raw provider arguments-> strict JSON decoding-> requested arguments-> one tool-schema parse-> schema output-> strict snapshot-domain validation and detachment-> RFC 8785 canonicalization-> argsCanonicalJson + proposalHash-> policy with a decoded snapshot view-> tool execution with a new decoded snapshot viewargsCanonicalJson is the authoritative authorization payload. Policy input,
tool input, and suspended-proposal parsedArguments are independent decoded
views of that payload.
The following invariants are normative:
- Tool-schema parsing runs exactly once per execution attempt.
- The schema output must be representable in the strict snapshot domain before policy evaluation.
argsCanonicalJsonis the RFC 8785 serialization of the validated detached snapshot and is computed once withproposalHash.- Policy receives a decoded view whose canonical value equals
argsCanonicalJson. - An allowed tool receives a new decoded view whose canonical value equals
argsCanonicalJson. - Policy and tool execution never share a mutable argument reference.
- No schema-output reference is passed directly to policy, execution, or a suspended proposal.
- A suspended tool proposal reuses the fingerprint evaluated by policy; it does not recompute authorization evidence from a mutable argument object.
This RFC supersedes RFC-0015 invariant 5. Exactness means canonical semantic equality, not JavaScript object identity.
Strict Snapshot Domain
Section titled “Strict Snapshot Domain”An authorized tool argument snapshot is a JSON data tree:
type ToolArgumentSnapshotValue = | null | boolean | string | number | ToolArgumentSnapshotValue[] | { [key: string]: ToolArgumentSnapshotValue };Additional constraints are normative:
- strings must not contain lone UTF-16 surrogate code units;
- numbers must be finite and must not be negative zero;
- arrays must be dense and must not contain additional own properties;
- objects must be plain records with
Object.prototypeor a null prototype; - object properties must be own, enumerable, string-keyed data properties;
- accessors, symbol keys, non-enumerable data properties, cycles, and repeated object references are rejected;
undefined,bigint, symbols, functions,Date,RegExp,Map,Set, typed arrays, class instances, and other host objects are rejected.
Object keys are data. The snapshot encoder must preserve keys such as
__proto__, constructor, and prototype without invoking prototype setters.
Rejecting prototype-chain segments remains required where AIOC interprets keys
as paths or merges them into runtime objects, but tool argument canonicalization
must not silently omit or reinterpret an own data property.
Runtime validates property descriptors and constructs a detached JSON tree
before canonicalization. It must not pass the schema output directly to a
serializer or invoke accessors, toJSON, or inherited behavior.
argsCanonicalJson must be exactly the RFC 8785 serialization of that tree.
JCS governs property ordering, strings, numbers, recursion, and whitespace;
strings are not Unicode-normalized. AIOC further rejects negative zero and
repeated references to keep authorization evidence unambiguous.
The snapshot represents the post-schema value, not the lexical spelling of
rawArguments. Library selection is not normative. Decoding
argsCanonicalJson must always produce a fresh value without aliases.
Argument Views
Section titled “Argument Views”No public ToolPolicyInput field is added or renamed.
rawArgumentsremains the exact provider string.requestedArgumentsremains the decoded pre-schema request and diagnostic evidence.parsedArgumentsbecomes a decoded view of the strict authorization snapshot.argsCanonicalJsonis the authoritative snapshot.proposalHashbinds agent, tool, andargsCanonicalJson.
For JSON-safe schema outputs, parsedArguments remains structurally equal to
the schema output. It is intentionally not the same object.
The existing field name is retained for compatibility. Introducing a distinct
public authorizedArguments field is deferred until the ToolSpec and
ToolHandler contracts are considered separately.
Policy Mutation Semantics
Section titled “Policy Mutation Semantics”Runtime canonicalizes the policy view after policy resolves. If the view has
changed or is no longer a valid snapshot value, runtime returns the existing
deterministic deny reason policy_mutated_arguments.
A mutation through a policy-retained reference after policy resolution may no longer be observable by runtime. It cannot affect the authoritative snapshot or the independent execution view and therefore cannot change the action.
This isolation protects the argument boundary. It does not sandbox
application-owned policy code or isolate other shared application state such as
runContext.
Execution Semantics
Section titled “Execution Semantics”After an allow decision, runtime decodes argsCanonicalJson into the value
passed to the tool handler.
The tool may mutate its own execution view. Such mutation is handler behavior after authorization and cannot alter the fingerprint, policy evidence, or suspended proposal.
Runtime must not:
- parse the tool schema again;
- execute the schema output object directly;
- execute the policy view;
- rebuild execution arguments from
rawArgumentsorrequestedArguments.
Proposal Hash And Approval
Section titled “Proposal Hash And Approval”argsCanonicalJson is the canonical action reviewed and approved.
SuspendedToolProposal.parsedArguments is a decoded convenience view. Approval
systems should use argsCanonicalJson for display or deterministic decoding and
proposalHash for evidence matching.
When policy returns require_approval, runtime stores the already evaluated
argsCanonicalJson and proposalHash. The suspended-proposal builder must not
recompute either value from its parsedArguments field.
Approval still does not bypass policy. On resume, a new execution attempt runs
the schema once and creates a new strict snapshot. Existing approval evidence
can match only if that attempt produces the same proposalHash.
This RFC does not add an executor that directly consumes a suspended proposal.
Snapshot Failures
Section titled “Snapshot Failures”Schema output outside the strict snapshot domain produces
ToolArgumentsValidationError with stage snapshot.
Snapshot failure is not a policy decision:
- tool policy is not invoked;
- no allow, deny, or approval-required decision is emitted;
- the tool is not executed;
- no suspended proposal is created.
Error details must identify the tool and failure stage without including the argument payload by default.
Compatibility And Migration
Section titled “Compatibility And Migration”The public ToolPolicyInput and SuspendedToolProposal field shapes remain
unchanged. Runtime identity semantics intentionally change:
- policy and handler no longer receive the same object;
- policies must treat
parsedArgumentsas read-only input; - tests or policies relying on reference identity must use structural or canonical equality instead;
- Zod transforms must return values in the strict snapshot domain;
- conversion from JSON values to
Date,Map, domain classes, or other rich values moves into the tool handler.
Proposal hashes remain stable where the prior representation matches JCS. Otherwise grants must be reissued, including when hashing depended on different Unicode or number serialization, non-JSON values, aliases, or prototype-sensitive handling.
The strict snapshot requirement intentionally replaces RFC-0015’s non-goal of supporting arbitrary Zod transform output without an immutable JSON contract.
Alternatives Considered
Section titled “Alternatives Considered”Keep Shared Arguments And Repeat Mutation Checks
Section titled “Keep Shared Arguments And Repeat Mutation Checks”Rejected because checks observe values at particular times but do not remove the mutable alias between authorization and execution.
Deep-Freeze Parsed Arguments
Section titled “Deep-Freeze Parsed Arguments”Rejected because freezing is not a complete value model for JavaScript host objects and would still leave ambiguity in canonical approval evidence.
Use structuredClone(...)
Section titled “Use structuredClone(...)”Rejected as the authorization contract because structured cloning supports a broader value domain than the existing canonical JSON evidence can identify without collisions. It would also make stored approvals less portable.
Add Type-Tagged Canonicalization For Rich JavaScript Values
Section titled “Add Type-Tagged Canonicalization For Rich JavaScript Values”Deferred. It would require a larger, versioned serialization protocol for a capability that provider tool arguments do not require. Domain conversion can remain handler-owned.
Non-Goals
Section titled “Non-Goals”- No immutable or isolated
runContext. - No sandbox for application-owned policy or tool code.
- No schema digest in proposal fingerprints.
- No direct suspended-proposal executor.
- No change to handoff payload semantics; those remain part of the separate handoff and multiple-call design work.
- No
ToolSpecorToolHandlerAPI split in this RFC.
Minimal Test Matrix
Section titled “Minimal Test Matrix”- Policy and handler receive distinct argument objects with the same canonical value.
- A policy-retained reference cannot mutate the later execution input.
- A schema transform returning an application-owned alias cannot mutate policy or execution input after snapshot creation.
- Immediate policy mutation produces
policy_mutated_argumentsand no execution. - Late policy mutation cannot change handler input.
- Unsupported schema output fails at stage
snapshotbefore policy and tool execution. - Schema parsing still occurs exactly once per execution attempt.
- Suspended proposals reuse the policy fingerprint and contain an independently
decoded
parsedArgumentsview. - Resume approval matches only an identical strict snapshot hash.
- Own
__proto__,constructor, andprototypeproperties are represented as data without prototype mutation or fingerprint omission. - Negative zero, lone surrogates, sparse arrays, aliases, accessors, cycles, and non-JSON host objects fail deterministically.
- Published JCS vectors, including non-ASCII key ordering, produce the required canonical JSON.
Adoption Plan
Section titled “Adoption Plan”- Add strict snapshot validation, detachment, and conforming RFC 8785 canonicalization.
- Prepare canonical JSON and proposal hash before tool policy.
- Isolate policy, execution, and suspended-proposal argument views.
- Reuse prepared fingerprint evidence in suspended proposals.
- Extend validation errors with the
snapshotstage. - Add focused unit, JCS conformance, and approval lifecycle regression coverage.
- Update policy, tool, and approval documentation with migration guidance.
References
Section titled “References”Status
Section titled “Status”Accepted. Implemented in src/tool-argument-snapshot.ts and src/run.ts, with
focused coverage in src/tests/unit/tool-argument-snapshot.unit.ts and
src/tests/unit/tool-arguments.unit.ts.