Your agent is about to move money.
It decides exactly what that means at the moment it happens, and by the time you can see the decision it is already on chain. This plugin puts one step in between.
loading the recorded run...Every line below is replayed from run.json, captured from a real ElizaOS session.
An agent that is right 99% of the time drains a treasury on its hundredth transfer
The model is not the problem. Being asked to be exact, once, with no second look, on the one operation that cannot be undone is.
A confirmation prompt does not fix this. It confirms an intent, not a transaction. A spending cap bounds the damage rather than preventing the mistake. Asking the model to restate its plan just asks a probabilistic system to describe itself.
So the model contributes exactly once, before anything is reviewable, and what it produced is shown to a person before it can move value. On approval the stored payload is replayed byte for byte. Nothing is inferred at execution time, because by then there is nothing left to infer.
Three moves, and the model only reaches the first
Compose and dry run
The model extracts the address, amount and chain. KeeperHub simulates it. A mangled address or a revert ends here, and nothing is queued.
Hold for a person
The exact simulated payload is stored and shown. Ambiguous replies do not pass. This is the step that does not exist anywhere else.
Replay byte for byte
Confirming drops simulate, attaches the derived key, and sends the stored bytes. No argument is rebuilt at execution time.
What gets installed
KEEPERHUB_WALLET
A provider that puts the real wallet, the enforced daily cap, and the remaining headroom into the agent's context, so it reasons against limits that exist.
KEEPERHUB_SIMULATE
Dry runs a token transfer. Has no code path that broadcasts.
KEEPERHUB_SIMULATE_CALL
Dry runs a contract call, including payable functions.
KEEPERHUB_SIMULATE_CONDITIONAL
Dry runs a conditional action: read one value, act only if a comparison holds.
KEEPERHUB_CONFIRM
Replays the reviewed plan. Has no code path that constructs arguments.
KEEPERHUB_STATUS
Reads back whether a broadcast settled, because unconfirmed is a real outcome and the answer to it is to poll, never to re-send.
KEEPERHUB_RECORD_EXECUTION
An evaluator that writes each execution into agent memory.
reads state dry runs only can broadcast records after the fact
The idempotency key is derived, never generated
A key minted per attempt does not survive a retry: the second attempt sends a different key, KeeperHub treats the request as new work, and the transfer executes twice. So a task id is minted once when the dry run queues the plan, and the key is a SHA-256 of that id joined with the fields that decide the onchain effect — chain aliases resolved, addresses lowercased, amounts normalised.
Every attempt at one plan sends one key. A fresh dry run is different work and gets a different one. That property is what makes the retry rule safe: keep the plan when nothing definite is known, discard it once an outcome is settled.
It survives the paths that are not the happy one
| Model mangles an address | Rejected before any call. Nothing queued. |
| Dry run says it would revert | Nothing queued, with the decoded reason. |
| Simulator cannot reach the chain | Reported as unavailable, explicitly not as a revert — nothing was learned either way. |
| Broadcast returns unconfirmed | Reported as in flight, never as success or failure. Calling a live transaction failed is what provokes a double spend. |
| Broadcast times out | Plan kept. Confirming again derives the same key and replays. |
| User replies ambiguously | Approval must be unmistakable. Nothing broadcasts. |
| User approves twice | The plan is consumed on the first take. |
| Solana chain | Refused before any call. The dry run is EVM-only, and this will not broadcast what it could not preview. |
A real receipt
The agent broadcast this through KeeperHub. The receipt below was not read back from KeeperHub: it was fetched from a public Sepolia node, so the confirmation comes from a party with no stake in the claim.
Install
npm install plugin-keeperhub
Add it to your character and give it an organisation API key:
{
"plugins": ["plugin-keeperhub"],
"settings": {
"secrets": { "KEEPERHUB_API_KEY": "kh_..." }
}
}
The default chain is Ethereum Sepolia, on purpose. Point it at mainnet deliberately, never
by forgetting to. A key scoped mcp:read can dry run but is refused on
broadcast; broadcasting needs mcp:write or mcp:admin.