Agents

Resolve

Resolve turns an intent into an explained plan. It never spends money and never signs anything.

TypeScript · runs in the Tollex test suite
const plan = await tollex.resolve({
  need: "token balance of a wallet",
  input: { address: wallet.address },
  constraints: { maxCost: usdg("0.001"), receiptRequired: true },
});

plan.selected?.toolId; // the capability that would be bought
plan.plan.reasons; // why it won, in plain language
plan.plan.rejectedBy; // e.g. { PRICE_LIMIT: 9, NOT_RELEVANT: 21 }

Eligibility comes first

Every capability is checked against the hard constraints before anything is ranked. A candidate that fails any check is rejected and keeps a reason code. Price and relevance cannot rescue it. Requirements that depend on measurement fail when the measurement is missing: a provider nobody has timed cannot satisfy a latency requirement.

CodeWhy the candidate was rejected
NETWORK_INCOMPATIBLENo supported network is in the allowed list.
ASSET_INCOMPATIBLEThe required asset is not accepted on an allowed network.
SCHEME_INCOMPATIBLEThe capability's payment scheme is not one the agent can use.
PRICE_LIMITThe route's maximum exceeds maxCost (or the live price does, checked by the client).
PROVIDER_BLOCKED / PROVIDER_NOT_ALLOWEDProvider rules.
TOOL_NOT_ALLOWED / CATEGORY_NOT_ALLOWEDTool and category rules.
SIDE_EFFECTSThe capability writes more than the agent accepts.
RECEIPT_REQUIREDA receipt is required and the capability returns none.
EVIDENCE_REQUIREDRequired execution evidence is missing.
HEALTH_REQUIREMENTFailing health check, or success rate below minimum or unmeasured.
LATENCY_REQUIREMENTObserved p95 above the limit, or no observations.
STALE_DISCOVERYTerms and health not observed recently enough.
NOT_RELEVANTNothing in the capability matches the need.
INPUT_INVALIDThe supplied input fails the capability's schema.
POLICY_REJECTEDClient side: the signing policy refuses the live payment terms.
NOT_PAYABLEClient side: the capability did not return valid x402 terms.

plan.rejectedBy counts rejected candidates by their first failing check. Economic checks run before relevance, so a rejection names its economic reason when there is one.

Ranking eligible candidates

Eligible candidates are scored on six components, each between 0 and 1, and every component is returned with the plan:

Ranking formula (returned in plan.ranking)
score = 0.40 relevance + 0.20 price + 0.15 reliability
      + 0.10 latency  + 0.10 evidence + 0.05 freshness

relevance    overlap of the need with name, tags, category, description
price        cheapest eligible maximum scores 1
reliability  observed success rate after 5+ calls, otherwise 0.5
latency      1 - p95 / (maxLatencyMs or 5000 ms), unobserved 0.5
evidence     share of evidence kinds the capability attaches
freshness    1 - age / (requiredFreshnessMs or 1 h), unknown 0.5

ties: lower maximum price, then lower observed p95, then id

The result is deterministic: the same catalogue, observations and clock give the same plan. Resolve does not need a language model. If one is used to reorder candidates, it only ever sees the eligible list, and its answer is filtered back to that list.

This is capability routing: choosing the best eligible capability under the constraints you declared. It is not best execution in the securities sense and carries no such guarantee.

What the client adds

Catalogue prices are not trusted for the final decision. The TypeScript client fetches the live x402 terms for the leading candidates and runs them through your signing policy. The selected route is the first candidate that passes both. Its verdicts are in checks, with POLICY_REJECTED, PRICE_LIMIT or NOT_PAYABLE where a candidate failed.

What comes back

  • resolutionId: send it with the purchase and the operator’s console links the plan to the payment.
  • plan.selected: the capability, its route (scheme, maximum amount, network, asset) and any preconditions such as a Permit2 allowance.
  • plan.reasons: plain-language reasons built from the reported fields.
  • plan.evaluations: eligible candidates in rank order, then rejected ones with their codes.
  • quote: with quote: true and an input, a hash-bound quote for the selected route.