Scout
Observes. Reads the world and labels the regime. Scout has two modes:- Rebalance mode: listens for
flow_start, reads one position from chain, classifies its regime. - Create mode: listens for
flow_create_start, surveys multiple pools viadiscoverPools(chainId)(real on-chain probe of the v4 PoolManager), measures vol/gas/yield per pool, classifies each.
- Reads chain state through viem.
- Loads realized volatility (CoinGecko, fallback deterministic seed).
- Reads gas via
eth_gasPricefor the chain. - Estimates 24h fee yield from
liquidity × feeTier × in_range_share. - Classifies regime:
ranging,trending,volatile, orstressed. - Asks GPT to summarize why the regime is what it is, in plain English.
- Forwards
context_observedto Strategist (MarketContextfor rebalance,CreateContextwithsurveyedPools[]for create).
Strategist
Proposes. 2-5 candidate ranges, regime-aware. Two variants: Rebalance mode asks GPT for(widthMultiplier, centerOffsetTicks)
tuples. Snaps each to the pool’s tickSpacing and builds a real
PlanCandidate with allocateInventory.
Create mode asks GPT for (poolIndex, widthMultiplier, centerOffsetTicks, exposureBias) tuples. The poolIndex points into
Scout’s surveyedPools. exposureBias = "long-token" shifts the range
so the user’s capital token sits more in the position; "neutral"
centers on current price for ~50/50 deposit. allocateForCreate
computes deposit amounts and surfaces a prepAction (e.g. “swap 0.4
WETH → USDC first”) if the capital is single-sided.
Both modes:
- Stress-test every candidate (1×, 2×, 3× vol).
- Estimate yield per candidate so the rationale quotes real numbers.
- Forward
proposal(round 0) orrevision(round > 0) to Critic.
revise, Strategist gets the full critique back
and revises with priorProposal and priorCritique in context.
Risk-Critic
Challenges. Default skeptical; vetoes are the strong move. Listens forproposal and revision from Strategist. For each:
- Recomputes deterministic stress (1×, 2×, 3×) and gas/yield ratios.
- Asks GPT to judge each candidate as
accept|revise|veto, honoring the user’s risk profile (conservative|balanced|aggressive) - different floors on buffer hours and gas/yield. - Sets an overall decision:
accept(one passes),revise(try again), orveto_all(every candidate failed at this vol/gas regime). - On
accept: emitsplan_readydirect to CLI, flow ends. - On
revise: forwardscritiqueback to Strategist; round counter increments. - After
ZUNO_MAX_DEBATE_ROUNDS(default 2) without convergence: emitsdeadlockto Arbiter with the entire history.
Arbiter
Decides. Only fires on deadlock. Listens fordeadlock. For each one:
- Reads the full debate (every proposal, every critique, every judgment).
- Asks GPT to pick exactly one candidate from the latest proposal, set a verdict, and write paragraph-length reasoning that quotes the debate by round.
- Tiebreak axis honors the risk profile:
conservative→ largest 2× vol buffer winsbalanced→ best buffer × yield balanceaggressive→ highest yield among non-vetoed candidates
- Emits
plan_readyto CLI withdecidedBy: "arbiter".
Message lifecycle
Every agent also emitsagent_thought envelopes back to the CLI as it
works. The CLI buffers these and renders them as the live debate
transcript under the recommendation card.
When the LLM is unavailable
IfOPENAI_API_KEY is unset or ZUNO_DETERMINISTIC=true:
- Scout falls back to a deterministic regime label from the same numbers.
- Strategist falls back to fixed multipliers (1.4×/0.65×/1.0×).
- Critic falls back to threshold rules (buffer floors, gas/yield ceilings).
- Arbiter falls back to a scored tiebreak (accept=3, revise=1, veto=-10).
Hallucination resistance
The LLMs in Zuno never produce numbers that hit chain. Four mechanisms keep fabrication out of the plan:- Bounded shape outputs. Strategist returns
(widthMultiplier, centerOffsetTicks)only - zod-validated, range-clamped. Real ticks come fromnearestUsableTicksnapping; real amounts come fromallocateInventory/allocateForCreate. The model cannot invent a tick, an amount, or an address. - Critic re-verifies, never trusts. The Critic recomputes
stressProfile,gasYieldRatio, andrebalanceCostUsdfrom the same on-chain inputs Scout used. Hard floors perRiskProfile(BUFFER_FLOOR_HOURS,GAS_YIELD_CEILING) reject candidates mechanically, regardless of the rationale. - Disagreement detects drift. A wrong proposal gets vetoed in the next
round. A persistently wrong proposal triggers
deadlock→ Arbiter, who picks from concrete candidates by deterministic tiebreak. No single confidently-wrong agent can ship a plan unchecked. - Schema-enforced structured outputs. Every agent call uses OpenAI structured outputs with a zod schema. Malformed or out-of-range responses are rejected before they reach business logic.
apply.
When AXL is unavailable
If the four agent peers aren’t visible on the local AXL topology, the CLI falls back to an in-process orchestrator (runDebate) that runs
the same four handlers in a single Node process. Same prompts, same
deterministic tools, same transcript - only the transport changes.