Skip to content

Provisioning as one action: create agent -> derive key -> fund gas + USDC -> open channel - #107

Merged
ALLiDoizCode merged 1 commit into
mainfrom
sandcastle/issue-74
Aug 4, 2026
Merged

Provisioning as one action: create agent -> derive key -> fund gas + USDC -> open channel#107
ALLiDoizCode merged 1 commit into
mainfrom
sandcastle/issue-74

Conversation

@toon-backlog-bot

Copy link
Copy Markdown

Combines agent creation, key derivation, gas+USDC funding, and channel opening into a single provisioning action.

Part of #74

This PR was produced by the sandcastle agent:implement runner and is awaiting human review.

🤖 Generated with Claude Code

…hannel (buzz#74)

Task: #74, part of the agent-fleet-money epic
(toon-meta#261 decision "provisioning as one action"). Unblocked by
toon-client#491 (plain sendTransfer, merged into @toon-protocol/client
0.26.0) and buzz#79 (account-index registry, merged). Before this, making a
managed agent able to pay on TOON needed manual CLI work outside the app;
this wires the desktop app itself to derive the agent's payment address,
fund it from the owner's wallet, and open its channel, with progress shown
inline in the create-agent flow.

Key decisions:
- Bumped `@toon-protocol/client` to ^0.26.0 for `sendTransfer` (issue block#491)
  and `getRoutePrice`-based quoting — 0.25.1 had no transfer primitive at
  all, only channel-collateral locking.
- Step derivation (`agentProvisioningState.ts`) mirrors
  `toonOnboardingState.ts`'s ADR exactly: key/fund/channel status comes from
  live reads (Rust-assigned account index, the agent's own derived-address
  balances), never a stored counter, so reopening the flow for a partly
  provisioned agent resumes from reality. The channel-open step is the one
  persisted flag (`agentProvisioningStore.ts`, keyed per agent pubkey) —
  same tradeoff the onboarding wizard already makes for its own channel
  step, since there's no free on-chain probe for "does this address already
  have a channel."
- Initial allowance (`agentProvisioningAllowance.ts`) prefers a measured
  burn rate (reusing `agentNetworkFlow.ts`'s `NetworkFlowRead` shape) and
  falls back to `quotedPrice × FALLBACK_WRITES_PER_DAY × FALLBACK_RUNWAY_DAYS`
  for a brand-new agent's inherent lack of history — every creation-time
  caller takes this fallback today, since the per-agent burn-rate feed
  (`agentNetworkFlow.ts`'s own documented blocker, buzz#86) isn't wired yet.
- Funding (`provisionAgent.ts`) relies on `sendTransfer`'s own built-in
  balance-delta confirmation (throws `TransferNotDeliveredError` rather than
  resolving on a send that landed but delivered nothing — the exact devnet
  faucet failure mode connector#691 documents) instead of re-verifying
  itself. The two legs (gas, USDC) run independently via `Promise.all` and
  report as a tagged result rather than throwing, so a failed gas leg never
  loses a successful USDC leg — this is what makes "token landed, gas
  didn't" a legitimate resumable state instead of a hard failure.
- Channel-open uses the actual funded balance on hand as the collateral
  amount (not a re-quoted estimate), so it's correct even resumed in a
  later session with no memory of what the fund step originally computed.
- Extracted `buildToonClientOptions` out of `toonPaidWriter.ts`'s
  `createToonClient` so the writer's client and both provisioning clients
  (owner-scoped for `sendTransfer`, agent-scoped for `openChannel`) share
  one bootstrap rather than duplicating it.
- Rust: `account_index.rs` gains a pure read-only `find_account_index`
  (buzz#79's `assign_account_index` mutates/creates; provisioning only
  needs to read what `create_managed_agent` already assigned), exposed via
  a new `commands/agent_provisioning.rs` module rather than added to
  `agents.rs`/`tauri.ts`, both already at their file-size ratchet ceiling.
- UI: `AgentProvisioningDialog` (StepProgress, mirrors `ToonOnboardingGate`)
  is handed off from `RequestedAgentCreateDialogs` once `SecretRevealDialog`
  closes for a successfully created agent — sequential, not simultaneous,
  so only one dialog is ever open at a time.

Files changed: desktop/package.json, pnpm-lock.yaml, pnpm-workspace.yaml,
desktop/src-tauri/src/{commands/agent_provisioning.rs (new),commands/mod.rs,
lib.rs,managed_agents/{account_index.rs,mod.rs}},
desktop/src/features/agents/{lib/agentProvisioningState.ts (new, +test),
lib/agentProvisioningAllowance.ts (new, +test),
lib/agentProvisioningStore.ts (new, +test),lib/provisionAgent.ts (new, +test),
useAgentProvisioning.ts (new),
ui/{AgentProvisioningDialog.tsx (new),RequestedAgentCreateDialogs.tsx}},
desktop/src/shared/api/{tauriAgentProvisioning.ts (new),toonPaidWriter.ts}.

Verified: fmt-check, desktop-tauri-fmt-check, clippy (workspace, -D
warnings), test-unit (864 tests), desktop-check, desktop-test (4291
passed), desktop-build, web-check, web-build — all green.

Blockers/notes for next iteration: desktop/src-tauri cannot be compiled or
unit-tested in this sandbox (no GTK/WebKit/sidecar stubs), so
`account_index.rs`'s new `find_account_index` and its tests are verified by
`cargo fmt --check` plus manual review, not `cargo check`/`clippy`/`test` —
upstream ci.yml's desktop-tauri-check/test job is the first real compile.
`solana`/`mina` are typed as valid `TransferChain`/faucet chains in
provisionAgent.ts but the fund step always calls with `chain: "evm"`
(TOON_DEVNET_DEFAULTS.chain is evm:84532 only) — fine today, would need a
config-driven chain pick if the devnet's default settlement chain changes.
The "one action" is UX-sequential (fund button, then open-channel button,
both inline in one dialog with derived progress) rather than one click that
silently chains both network calls — matches the onboarding wizard's own
established "every network step is a button press" convention rather than
adding a new auto-chaining pattern.

Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>
@ALLiDoizCode
ALLiDoizCode merged commit fd94178 into main Aug 4, 2026
46 of 48 checks passed
@ALLiDoizCode
ALLiDoizCode deleted the sandcastle/issue-74 branch August 4, 2026 04:20
ALLiDoizCode pushed a commit that referenced this pull request Aug 4, 2026
…113)

Task: buzz#108. The version bump half was already done by buzz#74's PR
#107 (main carries @toon-protocol/client@^0.26.0 and the pnpm
minimumReleaseAgeExclude entry) — per the issue's own re-scope comments,
this lands only the remaining read-path work.

Key decisions:
- Deleted ToonPaidWriter's hand-rolled `ClaimStateReadResult` stand-in and
  imported the real `ClaimStateResult` type from `@toon-protocol/client`
  (type-only import — no runtime cost, matches this module's "lazy heavy
  import" discipline). `tryClaimState`'s narrowing logic (`result.ok` /
  `result.depositTotal`) was already structurally correct against the
  hand-rolled type and needed no behavior change against the real one.
- `RawNetworkFlowStatus` gains `creditedBaseUnits`, fed by
  `tryClaimState`. Per `@toon-protocol/client`'s own "Earning" docs,
  earnings net off-chain onto the SAME channel's claim watermark — there
  is no separate earned ledger — so `cumulativeClaimed` can read below
  zero once an identity is credited more than it has spent.
  `tryClaimState` splits that signed watermark into the two non-negative
  buckets `agentNetworkFlow.ts`'s `spendable = deposit − owed + credited`
  (toon-meta#262 decision 9) already expects, rather than making every
  caller re-interpret a signed "owed". The local-fallback path always
  reports `creditedBaseUnits: 0n` — this client's own tracked watermark
  only knows what it spent, never a connector-applied credit.
- `networkSpendState.ts` now passes `input.raw.creditedBaseUnits` through
  instead of hardcoding `0n`; updated its stale comment (previously said
  "no income source is wired") and `agentNetworkFlow.ts`'s module doc
  (previously said the claim-state read "is not yet vendored" against
  the 0.25.1 pin) — both now correctly describe income RATE
  (`incomeRateBaseUnitsPerSec`/`incomeSampleCount`) as the remaining
  unwired gap, not the credited balance itself.

Files changed: desktop/src/shared/api/toonPaidWriter.ts (+test),
desktop/src/features/profile/lib/{networkSpendState.ts (+test),
agentNetworkFlow.ts (doc only)}.

Verified: fmt-check, desktop-tauri-fmt-check, clippy (workspace,
-D warnings), test-unit (864 tests), desktop-check, desktop-test (4331
passed), desktop-build, web-check, web-build — all green.

Blockers/notes for next iteration: income RATE tracking
(incomeRateBaseUnitsPerSec/incomeSampleCount in agentNetworkFlow.ts's
NetworkFlowRead) is still unwired — no live event feed exists for inbound
payments, only networkSpendLiveStore.ts's outbound onPaidWrite — so
runway still degrades to burn-only until a caller measures income over
time. This is a separate, real gap from this ticket's balance-side
creditedBaseUnits wiring, not something this ticket's AC asked for.

Signed-off-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

2 participants