Agent-boundary compaction: trading tool outputs stop misleading the model#331
Merged
Conversation
…g to the model
Live audit of alice-uta found the agent surface poisoning analysis:
a staged order echoed the full IBKR Order serialization — 4.3KB, 38
UNSET sentinels per order. Sentinels READ AS DATA to an LLM
("minQty: 2147483647" looks like a real constraint), and a single
stage→status→push round burned thousands of tokens of noise.
trading-compact.ts: at the tool boundary, unset = absent. Contracts
keep instrument identity (IBKR-superset derivative fields ride along
exactly when set), Orders keep only set fields, results drop the raw
echo and the 120-field orderState while keeping the one signal field
(rejectReason). Money displays at 2dp, prices at 8dp; full precision
stays in the ledger. Measured live: git status 4278B/38 sentinels →
404B/0.
Discoverability fix: every order verb's --help showed "(no flags)" —
positiveNumeric's .transform() made z.toJSONSchema throw and the catch
silently emptied the manifest schema. io:'input' + unrepresentable:
'any' renders the input side, which is what a CLI manifest wants;
flags are now fully listed.
Surface completion: tradingReject (the undo for a wrong stage —
auto-prepares the commit so the mental model stays stage→reject),
orderHistory + tradeHistory tools (the same exchange-frontend
projections the UI reads), wired as git reject / order history /
order trades on alice-uta.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…hannel, self-correcting errors
Used alice-uta end-to-end as the trading agent it serves. Three more
catches:
1. Per-account contract search ALWAYS returned [] — the SDK assumed the
aggregated /contracts/search returns grouped {id, results[]} rows,
but the route returns flat {source, contract} rows; the find() never
matched. Worst possible failure for an agent: "SOL isn't tradeable"
(it was). Shape aligned + search results now compact their contracts
like every other output.
2. The tradingPush approval-wall message told agents users could
approve via "Telegram /trading" — the Telegram connector was deleted
in 0.30.0. Agents were relaying dead instructions. Now points at the
Web UI surfaces that exist.
3. resolve(source) misses said "No accounts available." — false when
accounts exist and the SOURCE didn't match. Now: 'Unknown source
"okx-banana". Available accounts: …' — the agent self-corrects in
one step instead of concluding the broker layer is down.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sweep
The headline catch from a full pre-trade→TPSL dogfood run: an attached
TP/SL order on okx spot STAGED fine, PUSHED fine, FILLED fine — and the
stop never existed. ccxt accepted the unified takeProfit/stopLoss
params and the venue mapping silently dropped them: the ledger said
"long with a stop", the exchange said "naked long". CcxtBroker now
REFUSES tpsl placement unless the exchange has a venue-verified
placeOrderWithTpSl override (none yet — okx needs attachAlgoOrds,
bybit its native v5 fields), with an actionable message. A missing
stop that looks attached is the worst failure mode a trading system
has; loud refusal beats silent downgrade.
CLI shim (all four byte-identical copies):
- JSON-looking flag values parse into objects — `--takeProfit
'{"price":"1725"}'` previously shipped as a raw string and failed
validation opaquely.
- Gateway validation errors now surface field-level zod issues, and the
shim prints them — "Validation failed" alone stranded the agent
guessing flag names.
Trading-as-git tool ergonomics (from the same run):
- placeOrder/closePosition/modifyOrder/cancelOrder accept commitMessage
— stage AND commit in one call (one decision = one operation is the
dominant agent flow; the push approval wall is untouched).
- placeOrder/closePosition derive source from aliceId when omitted —
the account id was always inside the aliceId.
- compactStatus renames the overloaded "pending*" fields to
awaitingApproval (pending ORDERS are a different thing).
- Sync commit messages carry symbols: "[sync] ETH filled" instead of
"[sync] 1 order(s) updated".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
trading-compact.ts): unset = absent. Staged-order echoes / git status / push results / quotes / contract details / commits no longer dump 120-field IBKR serializations full of UNSET sentinels that read as data to an LLM. Measured live on okx demo:git statuswith one staged order went 4278B / 38 sentinels → 404B / 0. Money 2dp, prices 8dp at display; ledger precision untouched.--helphonesty: every order verb showed "(no flags)" because.transform()in the qty/price validators madez.toJSONSchemathrow (silently caught). Input-side conversion fixes it — full flag lists now render.git reject(undo for a wrong stage; auto-prepares the commit),order history/order trades(the same domain projections the UI reads — AI and human see one translation).Test plan
pnpm test1895 passing (new: sentinel normalization across all three value forms incl. Decimal toFixed rendering; no-sentinel-in-JSON invariant; reject-reason preservation)Boundary touch
Agent-facing trading tool outputs only; order execution paths unchanged.
🤖 Generated with Claude Code