plugin-keeperhub npm source ElizaOS plugin MIT

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.

123 tests across 7 files, 117 of them with no credentials and no network
1 receipt on Ethereum Sepolia that resolves on Etherscan
0 code paths from the model to a broadcast
loading the recorded run...
Loading the recorded session.

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 addressRejected before any call. Nothing queued.
Dry run says it would revertNothing queued, with the decoded reason.
Simulator cannot reach the chainReported as unavailable, explicitly not as a revert — nothing was learned either way.
Broadcast returns unconfirmedReported as in flight, never as success or failure. Calling a live transaction failed is what provokes a double spend.
Broadcast times outPlan kept. Confirming again derives the same key and replays.
User replies ambiguouslyApproval must be unmistakable. Nothing broadcasts.
User approves twiceThe plan is consumed on the first take.
Solana chainRefused 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.

loading the receipt...

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.