Skip to content

Answer checkout forms with typed fields and protected inputs - #2

Merged
mPaella merged 2 commits into
mainfrom
protected-input-fields
Oct 2, 2026
Merged

mPaella merged 2 commits into
mainfrom
protected-input-fields

Conversation

@mPaella

@mPaella mPaella commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Agent Checkouts replaced JSON Schema forms and the separate password request with typed fields (UC #1156, Crossmint #31268). This PR moves the sample app to the new contract.

  • Forms: each field is standard or protected. A form is answered once, with every field keyed by its key. A protected field takes only the { protectedInputId } that Crossmint's protected field returns.
  • Server: protectedRequest replaces passwordRequest in CheckoutView. It lists the protected fields and gives the link to the checkout's page. A protected field answered with a plain value, or a required one left out, is refused with 409 protected_input_required. The password_in_form guard stays for a standard field that looks like a password.
  • UI: PendingActionForm renders text, number, whole number, boolean, choice of one and choice of many. Each protected field is CrossmintProtectedInput (@crossmint/client-sdk-react-ui 4.9.0), collected on submit. An optional protected field is left out until the user adds it. AnswerProtectedRequest replaces AnswerPasswordRequest: it reads the form from the checkout, and a resend with the same answers keeps the same message id.
  • Web chat: awaiting_protected_input replaces awaiting_password. The model answers standard fields by key and can never send a protected answer. The card and dialog wording fit any secret, not only a password.
  • MCP and CLI: a protected form is never shown to the agent as a form. The agent gets the link. The CLI prints "Secure input needed." and the JSON exit reason is protected_input_needed.
  • Docs: docs/API.md, the skill (synced copies), the tool docs and the build prompt follow.
  • Side fix: the payment step's merchant is read as name and url, as the API sends it. It was read as domain, which never came. The approval text now reads "Purchase at ".

Rollout

Deploy after UC #1156 is deployed. The old and new form formats do not mix.

Chats saved before this change that hold an awaiting_password or responseSchema result fail history validation when continued (400). A new chat works. No compatibility code was added.

Test plan

  • pnpm turbo run build --filter='./packages/*', typecheck, test, lint and the web build, as in CI: 158 tests pass (core 35, server 67, ui 6, mcp 15, cli 32, auth 3).
  • New tests: typed field rendering, the protected-field guard, protected forms never rendered to MCP/CLI agents, and form render tests with a stand-in for Crossmint's iframe.
  • A real sign-in checkout on staging: a protected field collected, the form answered, the run carries on. Not run: no keys in this checkout.

In --json mode the CLI still prints the server's full view, which for a protected form includes the field descriptors (labels and types, never values).

🤖 Generated with Claude Code


Devin Review

Agent Checkouts replaced JSON Schema forms and the separate password
request with typed fields. Each field is standard or protected, and a form
is answered once, with every field. A protected field takes only the
{ protectedInputId } that Crossmint's protected field returns.

- core: typed fields on PendingUserAction, submitResponse with answers,
  and a flat RenderedField with select, multiselect and protected kinds.
  protectedResponse and the protected interaction are gone.
- server: protectedRequest replaces passwordRequest. A protected field
  answered with a plain value, or a required one left out, is refused
  with 409 protected_input_required.
- ui: PendingActionForm renders every field kind and collects each
  protected field with CrossmintProtectedInput (client SDK 4.9.0) on
  submit. AnswerProtectedRequest replaces AnswerPasswordRequest. It reads
  the form from the checkout, and a resend with the same answers keeps
  the same message id.
- web chat: awaiting_protected_input replaces awaiting_password. The
  model answers standard fields by key and never a protected one.
- mcp and cli: a protected form is never shown to the agent as a form;
  the agent gets the link to the checkout's page.
- docs, the skill and the build prompt follow.

The payment step's merchant is read as name and url, as the API sends it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
agent-commerce-sample-app Ready Ready Preview Oct 2, 2026 5:47am UTC

Request Review

devin-ai-integration[bot]

This comment was marked as resolved.

…ed ids

- A checkout stretch that stops for secure input no longer shows its last
  step as failed.
- A disabled option the store preselected is locked in, not unavailable:
  a choice of one can pick it, and a choice of many keeps it checked.
- A protected input id on a standard field is refused with
  409 not_a_protected_field. The stale not_a_protected_request code is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mPaella
mPaella merged commit bd8067c into main Oct 2, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — f6b7a687 Deployed Oct 2, 2026 by vercel[bot]
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