fix(prediction): align PredictionMarket tool with Predexon v2 schema#62
Merged
Conversation
The PredictionMarket tool was written against the public Polymarket
Gamma / Kalshi API field conventions, not Predexon's normalized v2
schema that actually sits behind the BlockRun gateway. Hand-written
types cast from `unknown` gave no compile-time signal, and the local
test suite makes no API calls, so every mismatch was invisible until a
live call. All field names below verified against openapi-v2.json and
live gateway responses (2026-05-20).
Fixes:
- searchPolymarket: status defaulted to `active`, but Predexon's
StatusOption enum is {open, closed} → 422 on every call. Add
normalizeMarketStatus() (active/live→open, archived/resolved→closed);
default `open`. Same normalization applied to searchKalshi/searchAll.
- searchPolymarket/searchKalshi metrics rendered `n/a`: real fields are
total_volume_usd / liquidity_usd / outcomes[].price (nested) for
Polymarket and last_price for Kalshi — not volume/liquidity/
outcome_prices[]/yes_bid/yes_ask. Parse the real shapes (legacy names
kept as fallbacks).
- crossPlatform rendered blank titles: venues are UPPERCASE nested
sub-objects (POLYMARKET/KALSHI{title,...}), KALSHI nullable. Resolve
via pickString over the sub-object.
- leaderboard returned "no data": rows live under `entries` (added to
unwrapList) with stats nested under `metrics.*` and the address in
`user`; the flat reads returned undefined for every row. Also map
the `sort` input to Predexon's real `sort_by` enum.
- smartActivity/smartMoney 400'd every call: both require at least one
smart-wallet criterion — inject a default min_total_pnl. smartMoney
also returns a single `positioning` aggregate, not buyers/sellers
arrays — rewrite the formatter. smartActivity field is
smart_wallet_count (singular).
- condition_id was truncated to 14 chars in searchPolymarket/
smartActivity/searchAll output, so the agent could only chain a
partial id into smartMoney → 404. Emit the full condition_id.
All 10 actions now verified end-to-end against the live gateway.
The external-wall failure guard breaks a turn after 5 consecutive "retrying won't help" failures, but only matched 401/403/429/5xx. A 422 (failed param validation) was charged-free AND unmatched here, so a PredictionMarket status=active 422 spun the agent to the 50-call HARD_TOOL_CAP — neither the cost guard (idle, 422s aren't billed) nor the wall guard fired. Add 400/422 to the pattern. 404 is intentionally excluded — "not found" is a legitimate cue to retry with a different query, not a dead wall.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The
PredictionMarkettool was modeled on the public Polymarket Gamma / Kalshi API field conventions, but the data actually comes from Predexon's normalized v2 schema behind the BlockRun gateway (the gateway is a pure pass-through proxy). Hand-written types cast fromunknowngave no compile-time signal, and the local test suite makes no API calls — so every field/param mismatch was invisible until a live call. This PR aligns all 10 actions to the real schema (verified againstopenapi-v2.jsonand live gateway responses on 2026-05-20).Bugs fixed (9)
statusdefaulted toactive; Predexon enum is{open, closed}n/atotal_volume_usd/liquidity_usd/outcomes[].price(PM) andlast_price(Kalshi), notvolume/outcome_prices[]/yes_bidPOLYMARKET/KALSHI{title}(KALSHI nullable)entries, stats undermetrics.*, address isuser;sortshould besort_byenumsmart_wallet_countpositioningaggregate, not buyers/sellers arrayscondition_idtruncated to 14 chars in search/activity output, so the agent could only pass a partial idactivenormalization missingWhy it mattered
Before: a simple "compare Fed-cut odds on Polymarket vs Kalshi" 422'd, returned
n/a, or pushed the agent to bash-curl the public Gamma API (forbidden by the system prompt) — burning ~$0.30 and sometimes freezing the terminal under the render churn of a runaway loop.After (live-verified through the real agent):
Testing
npm test— 405 pass, 0 failtsc --noEmitcleanfranklin start -p.Notes