fix(agent): count pending queued orders against the per-token budget - #24
Merged
ralyodio merged 1 commit intoJul 4, 2026
Merged
Conversation
placeAgentOrder checks the token's remaining budget against
tokenSpentThisWindow(), which only reflects orders the daemon has
already executed into crypto_spend_ledger. Orders this token already
has enqueued in source_state ('agent-orders') but not yet executed are
invisible to that check. So N rapid POST /api/agent/orders requests
submitted before the daemon's next tick each see the same stale
`spent` figure and can all pass the per-token budget_usd check — the
only guard applied at execution time is the engine's global account
spend budget (see apps/daemon crypto-trade.ts executeAgentMarketBuy →
applySpendBudget), not the token's own cap. That defeats the entire
point of "Coinbase for Agents" scoped tokens: a user handing out a
$50-budget token to a semi-trusted agent should not be exposed to
$50 × N in real exchange buys just because the ledger hasn't caught
up yet.
Fix: read the pending queue before the budget check and add this
token's already-enqueued-but-unexecuted usd to `spent` before calling
checkAgentBudget, in both placeAgentOrder (enforcement) and
getAgentBudget (so the reported remainingUsd matches). Added a test
that reproduces the bypass: two 80-usd orders against a 100-usd budget
now correctly reject the second with 402 instead of enqueuing both.
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.
Fixes #23.
placeAgentOrderonly checked a token's remaining budget againsttokenSpentThisWindow(), which sums confirmed spend fromcrypto_spend_ledger— a table the daemon only writes to once it actually executes a queued buy. Orders this same token already has sitting unexecuted in theagent-ordersqueue (source_state) were invisible to the check, so rapid back-to-back orders could each see the same stalespentfigure and all pass, letting real committed spend exceedbudget_usdby however many requests land before the next daemon tick. The only re-check at execution time is the engine's global account spend budget, not this token's own cap.Fix
pendingQueue()/pendingUsdFor()helpers inagent-trade.ts.placeAgentOrder: read the queue before the budget check, add this token's own pending (enqueued-but-unexecuted)usdtospentbefore callingcheckAgentBudget. Reused the same queue read for the existing idempotency-key check (was a separate read before).getAgentBudget: same adjustment, so the reportedremainingUsdreflects orders already in flight, not just ledgered ones.remainingUsd: 20) instead of both enqueuing.No changes to the daemon side or the global spend-budget path — this only closes the per-token accounting gap on the authorize/enqueue side.