Skip to content

feat(agent-wallet): a separate, budgeted wallet KaleidoMind can pay services from - #119

Merged
bitwalt merged 9 commits into
mainfrom
feat/agent-wallet
Oct 9, 2026
Merged

bitwalt merged 9 commits into
mainfrom
feat/agent-wallet

Conversation

@bitwalt

@bitwalt bitwalt commented Oct 9, 2026

Copy link
Copy Markdown
Member

Summary

The Agent wallet is a separate wallet the user funds and the assistant can spend from, within rules the user sets. The user tops it up from the main wallet. KaleidoMind can then pay for services, such as L402-protected APIs, without asking for small amounts, and asks for confirmation above a threshold. Every spend is logged, and the user can pause it, withdraw or turn it off at any time.

How it works

  • Truly separate funds. It is a second Spark account from the same recovery phrase: account index 1, derived at m/44'/998'/1'/0/…; the main wallet is index 0.

    • Nothing new is stored; it comes back from the recovery phrase.
    • On every connect, the live identity key must match the derived index-1 key and differ from the main wallet's, or the account refuses to open.
    • It is opened directly through the WDK Spark manager and is never registered with ProtocolManager, so it stays out of main balances, Send and history. The shared Spark client used by Flashnet only ever holds the main wallet.
  • Spending rules (services/agentWallet/policy.ts, per wallet):

    • per-payment, daily and monthly limits, fees included;
    • a pay-without-asking threshold;
    • an allowed-services list (services not on it always ask);
    • a pause switch.

    Over limit, paused or insufficient balance means refused. Anything malformed fails closed. Only the settings screen can change the rules; no tool or service response can.

  • One payment gate, payFromAgentWallet, handles every agent payment and runs them one at a time.

  • L402: fetch_paid_resource is reimplemented in-app (services/agentWallet/tools.ts):

    1. https only, and the 402 must come from the requested host;
    2. the announced price must equal the invoice amount;
    3. amountless, expired, wrong-network or near-expiry invoices are refused;
    4. the policy decides; above the threshold, the existing confirm sheet appears;
    5. the spend is logged, the invoice is paid and the preimage is checked;
    6. the request is retried with the credential.

    Only L402/LSAT is wired; MPP is not wired yet.

Details

  • Pending reconciliation: each spend stores its invoice, expiry and Spark send id. Pending entries are settled before every payment decision, on open and on budget status:
    • paid becomes paid, failed becomes failed;
    • unsettled past invoice expiry becomes failed and stops counting;
    • a lookup error keeps the entry counted.
  • Lifecycle: the agent account is closed and forgotten on wallet switch, Remove Wallet, lock and active-wallet reset, and when the Agent wallet is turned off (after sweeping the balance back).
  • UI:
    • Settings → KaleidoMind → Agent wallet, a Mind tab card, and an Agent tab in Activity;
    • top up and withdraw through PaymentConfirmationModal;
    • rules editable in Advanced, pause in both modes.
  • Assistant: a read-only agent_budget_status tool and an agent-wallet skill, so it can say what it can spend and why it can't pay.

Testing

  • tsc is clean and the full jest suite is green.
  • New tests:
    • policy, ledger and day/month windows, and fail-closed handling;
    • the payment gate and transfers;
    • derivation and account checks;
    • the Flashnet isolation;
    • pending reconciliation and lifecycle;
    • the L402 tool, budget readback, activity and the screen.

Needs a device:

  • the agent address differs from the main one;
  • top up, withdraw and turning it off move real sats;
  • the agent balance comes back after a restore from the recovery phrase;
  • an L402 call pays without asking under the threshold and confirms above it;
  • Flashnet swaps still use the main wallet.

…nt gate

The Agent wallet is a second Spark account derived from the recovery phrase
(next account index). Add its key derivation with vectors from the WDK Spark
signer, the per-wallet spending rules (per-payment, daily, monthly, pay-without-
asking threshold, allowed services, pause), the spend ledger, and the one
function every assistant payment goes through. Malformed rules, logs or
invoices refuse the payment.
…and disable

The agent account is the Spark adapter at account index 1, kept out of the
protocol manager. On connect it checks the identity key against the derived
agent key and hands the shared Spark wallet glue back to the main account.
Top-ups, withdrawals and the sweep on disable are fee-free Spark transfers,
logged before they are sent and run one at a time with agent payments.
fetch_paid_resource now runs the L402 flow itself: it checks the challenge
came from the requested https host, compares any announced price with the
invoice, pays through the Agent wallet payment gate (on its own below the
threshold for allowed services, otherwise through the assistant's confirm
sheet) and retries with the proof. A read-only agent_budget_status tool and an
agent-wallet skill let the assistant say what is left and why it could not pay.
The old main-wallet L402 binding is removed.
Agent payments join the main feed; the new Agent tab lists them with top-ups
and withdrawals. Refused and declined attempts stay in the Agent wallet log.
…ntry

Turn the Agent wallet on, see its balance and what is left today, top up from
and withdraw to the main wallet through the confirm sheet, set its spending
rules (editable in Advanced, pause in both modes), read its spend log, and
turn it off, which sends the balance back first.
…lient

The engine's Spark connect adopts the new wallet into the client Flashnet
shares, so opening the agent account briefly swapped it in before handing
it back. Open account index 1 directly through the WDK manager instead; the
shared client only ever holds the main wallet. A test runs a Flashnet-style
read loop through a full agent connect and disconnect.
…g forever

Pending spends now keep their invoice, its expiry and Spark's send id. Before
each payment decision, on opening the Agent wallet and on budget questions,
they are looked up by id, or by invoice in the wallet's send history: settled
ones become paid, failed ones failed, and unsettled ones past their invoice
expiry failed. A lookup that errors leaves the entry pending and counted.
The agent account is now closed and forgotten on wallet switch, Remove
Wallet, wallet lock, app lock and active-wallet reset (a store watcher plus
explicit calls before the switch and delete), and after turning the Agent
wallet off. An open still in flight when it is closed is disconnected and
never handed out, so a new wallet can't reuse the previous wallet's account.
# Conflicts:
#	screens/AIAssistantScreen.tsx
#	services/mindAgent.ts
@bitwalt
bitwalt merged commit 1a05a22 into main Oct 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant