Skip to content

feat(mobile): agent message rendering with passkey-gated transaction proposals - #593

Open
Adeolu01 wants to merge 1 commit into
Miracle656:mainfrom
Adeolu01:feat/mobile-agent-messages-tx
Open

feat(mobile): agent message rendering with passkey-gated transaction proposals#593
Adeolu01 wants to merge 1 commit into
Miracle656:mainfrom
Adeolu01:feat/mobile-agent-messages-tx

Conversation

@Adeolu01

Copy link
Copy Markdown

Summary

The agent can read, explain, and propose — it cannot move funds. Each of its message types renders distinctly, and a transaction it builds becomes a proposal that stays unsigned until the user confirms it with the device passkey.

closes #494

Issue #493 (the chat screen and lib/agentSocket.ts) is still open, so app/agent.tsx did not exist to modify. This PR creates it with the socket wiring kept deliberately thin — connect, send, retry on close — and everything of substance in the rendering and approval layer this issue is about. The durable client, history, and reconnect policy remain #493's to own, in the one file it already has to touch.

Message types

Five, each with its own treatment rather than one bubble style for everything:

  • User — right-aligned, gold-tinted.
  • Agent prose**bold** and `code` rendered as styled Text segments. The web wallet substitutes HTML into dangerouslySetInnerHTML for the same two forms; this is model-generated text, and turning it into markup a renderer will interpret is a habit worth not carrying to a new surface, so parseInlineMarkup returns segments instead.
  • Service errors — labelled as a failure, not attributed to the agent as if it had said it.
  • App notices — muted and centred, for things the app is telling the user (history cleared), never dressed up as the assistant speaking.
  • Transaction proposals — the approval card below.

Frames from the service are decoded by parseAgentServerEvent, which only accepts the shapes packages/agent/src/server.ts sends. Anything malformed, unknown, or missing its prose is dropped rather than rendered as something the user might act on — and a pendingTxXdr that is not a non-empty string never reaches the approval path at all.

The approval gate

An agent-proposed payment cannot execute without an explicit passkey confirmation, and the card is built so the user is approving the transaction rather than the agent's description of it:

  • What it does comes from the XDR. reviewProposedTransaction decodes the transaction and states each operation in the user's terms — amounts, assets, destinations, the minimum received on a swap, the fee, the memo. Fee bumps are unwrapped so the inner transaction, which is what executes, is the one described. An operation the app cannot describe is called out as such, never passed over silently.
  • The agent's summary is a claim. pendingTxSummary is shown beneath the decoded facts and labelled "The agent says", so the two can be compared instead of conflated.
  • Undecodable means unapprovable. If the XDR will not parse, the card says so and Confirm is disabled.
  • Wrong source means refused. A transaction sourced from any account other than this wallet's fee payer is rejected before the passkey is ever prompted — the agent built that for someone else's account, and signing it is never what the user meant.
  • The passkey comes before the key. Only after the prompt clears is the fee-payer secret read from the keychain and the transaction signed and submitted. The prompt is over the transaction's own hash, so the assertion is bound to the transaction on screen; the network never sees it (these are classic operations authorised by the fee-payer signature), but it is what stands between a proposal and the signing key on this device, and binding it to the hash means a confirmation cannot be reused for a different transaction.
  • Dismissing is declining. A dismissed prompt sets the proposal to declined — nothing signed, nothing sent — rather than surfacing an error. Decline does the same without a prompt.

Each proposal carries its own status, so approving one does not disturb another and a resolved card cannot be tapped twice.

Testing

npm test — 20 new unit tests in lib/__tests__/agentMessages.test.ts: every frame the service sends plus the malformed, unknown, prose-less, empty-XDR and non-string-XDR rejections; inline markup including unmatched markers, markup-looking text and the empty string; transaction review over real signed XDR for payments, strict-send swaps, multi-operation transactions, the undescribable-operation flag and unparseable input; the fee-payer source check; and Horizon result_codes extraction. 132 tests pass on this branch.

npm run typecheck passes.

The confirm flow itself is not covered end to end — it needs a device passkey and the running agent service, and the app has no component-test setup yet. The decisions that make it safe are in the tested library for that reason: the screen decides nothing about a proposal that reviewProposedTransaction and isProposalForFeePayer have not already established.

Notes for review

  • No new dependencies.
  • lib/passkey.ts is reused as-is for the confirmation, so the agent path and the WalletConnect path prompt through the same signer.
  • The role-based onboarding and greeting personalisation in the web agent page are not ported here: they belong with the profile settings screen (backlog Add Playwright e2e test for wallet onboarding flow #61), and this PR stays on message rendering and the transaction gate.

Renders each of the agent's message types and turns a transaction it builds
into a proposal the user has to approve. The proposal card is built from the
decoded XDR rather than from the summary the agent wrote for it, so the amounts
and destinations on screen are the ones being signed, and the agent's own
description sits beneath them labelled as a claim.

Approving prompts the device passkey over the transaction's own hash before the
fee-payer key is read from the keychain, so the assistant can never move funds
on its own: a dismissed prompt is a decline, an undecodable transaction cannot
be approved at all, and a transaction sourced from any account other than this
wallet's fee payer is refused before the prompt is raised.
@Adeolu01
Adeolu01 requested a review from Miracle656 as a code owner July 29, 2026 21:34
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

@Adeolu01 is attempting to deploy a commit to the miracle656's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 29, 2026

Copy link
Copy Markdown

@Adeolu01 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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.

66. Agent message rendering + tx actions

1 participant