Economics
Economic policy
A policy is the agent’s economic authority, written down. It is checked before the signer is asked for anything.
How a policy decides
- Fail closed. A payment is allowed only when every configured rule passes. A rule that needs information it does not have denies.
- Nothing implied. Networks, assets and schemes must be listed. An unknown network, asset or scheme is refused.
- Integers only. Every amount is an integer in atomic units of the asset.
- Before signing. The policy runs before the wallet is invoked, and the guarded signer refuses requests the policy did not approve.
What you can set
| Area | Fields |
|---|---|
| Scope | networks, assets, schemes (all required) |
| Per call | maxPerRequest, maxAuthorizedAmount (the ceiling signed for metered routes), maxActualSettlement |
| Budgets | budgets.hourly, budgets.daily, budgets.session, plus per-merchant, per-resource, per-provider, per-tool and per-category budgets |
| Who and what | allow and block lists for merchants, resources, providers, tools and categories; allowedPayTo; facilitators |
| Signatures | maxAuthorizationLifetimeSeconds, maxPaymentTimeoutSeconds, allowPermit2Approval, maxPermit2Approval, allowUnlimitedApproval (off by default) |
| Batch channels | batch.maxDeposit, batch.maxIdleChannelBalance |
| Quality | minHealth (success rate, failure rate, latency), minDiscoveryFreshnessSeconds, requireOperationalFacilitator |
| Proof | requiredReceiptCapabilities, requireReceiptVersion, requiredAssurance |
Budgets under concurrency
Budgets are reserved before signing and settled with the actual charge afterwards. With the PostgreSQL spend store, reservations for one agent are serialised under a lock and every budget window is re-summed before a new reservation is accepted. Two parallel calls can never both spend the same remaining budget, across any number of processes. The in-memory store gives the same guarantee within a single process, for development.
Profiles
An AgentPolicyProfile pairs a policy with default intent constraints. Resolve uses the policy as an outer limit, so a plan never proposes a purchase the signer would refuse. Request constraints can only narrow it.
const profile: AgentPolicyProfile = {
policy: {
id: "cautious",
agentId: "research-03",
networks: [NETWORK],
assets: [{ network: NETWORK, address: USDG_ADDRESS, decimals: 6 }],
schemes: ["exact"],
maxPerRequest: usdg("0.005"),
budgets: { hourly: usdg("0.5"), daily: usdg("5") },
providers: { block: ["untrusted.*"] },
maxAuthorizationLifetimeSeconds: 300,
},
intentDefaults: { receiptRequired: true, requiredFreshnessMs: 10 * 60_000 },
};
const agent = new TollexTools(new TollexClient({ wallet, policy: new PolicyEngine(profile.policy) }), TOLLEX_URL, {
intentDefaults: profile.intentDefaults,
});
// Asking for more than the profile allows does not widen it:
const planned = await agent.resolve({ need: "spot price", input: { pair: "ETH-USD" }, constraints: { maxCost: usdg("1") } });
planned.constraints.maxCost; // 5000n: the profile's per-request ceilingModels do not edit policy
The policy lives in the client process, not in the conversation. Tool output, provider descriptions and intent text are treated as data. Through the MCP server, a model can read the active limits with tollex_policy but has no tool that changes them.