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

AreaFields
Scopenetworks, assets, schemes (all required)
Per callmaxPerRequest, maxAuthorizedAmount (the ceiling signed for metered routes), maxActualSettlement
Budgetsbudgets.hourly, budgets.daily, budgets.session, plus per-merchant, per-resource, per-provider, per-tool and per-category budgets
Who and whatallow and block lists for merchants, resources, providers, tools and categories; allowedPayTo; facilitators
SignaturesmaxAuthorizationLifetimeSeconds, maxPaymentTimeoutSeconds, allowPermit2Approval, maxPermit2Approval, allowUnlimitedApproval (off by default)
Batch channelsbatch.maxDeposit, batch.maxIdleChannelBalance
QualityminHealth (success rate, failure rate, latency), minDiscoveryFreshnessSeconds, requireOperationalFacilitator
ProofrequiredReceiptCapabilities, 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.

TypeScript · runs in the Tollex test suite
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 ceiling

Models 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.