Skip to content

Validate machine input against its schema; add chat-with-pdf example - #88

Merged
davidkpiano merged 1 commit into
nextfrom
feat/validate-machine-input
Aug 5, 2026
Merged

Validate machine input against its schema; add chat-with-pdf example#88
davidkpiano merged 1 commit into
nextfrom
feat/validate-machine-input

Conversation

@davidkpiano

Copy link
Copy Markdown
Member

XState's schemas are types only — it never validates, and it resolves schemas.input to one type shared by createActor's input option and the context: ({ input }) factory. A field declared with a default was therefore both absent at runtime and required at the call site.

runAgent/createAgentActor now validate options.input against the machine's registered input schema before the actor starts. The resolved value (defaults filled, transforms applied) is what reaches the actor, the replayable init entry, and the run.start trace, so a replay reproduces the run even if a default is computed. Invalid input throws an AgentError with code invalid-machine-input, matching how a mismatched resume snapshot behaves; omitted input still skips validation.

Types: setupAgent brands the machine's input type with its own schema, and runAgent's input is now AgentInputFrom<TMachine>, which reads the schema's pre-validation side while the context factory keeps the validated one. The brand is a ~-prefixed phantom property rather than a unique symbol — a symbol has to be exported as a runtime value for declaration emit to name it. Only object-shaped input is branded, since intersecting a brand into xstate's NonReducibleUnknown collapses members to never. Standard Schema throughout; no validation library referenced.

Also ports agentcn's chat-with-PDF quiz recipe, where the quiz loop lives in instructions.md as prose. The sequencing moves into the machine: one question per entry into asking, a refreshEvery guard instead of "after 3-4 questions", covered pages fed back to retrieval as excludePages, documentId threaded from context, page hints read off the chunk, and an idle choosingDocument state when the library is ambiguous. Voice and question formatting stay in the prompt.

XState's `schemas` are types only — it never validates, and it resolves
`schemas.input` to one type shared by `createActor`'s `input` option and
the `context: ({ input })` factory. A field declared with a default was
therefore both absent at runtime and required at the call site.

runAgent/createAgentActor now validate `options.input` against the
machine's registered input schema before the actor starts. The resolved
value (defaults filled, transforms applied) is what reaches the actor,
the replayable init entry, and the `run.start` trace, so a replay
reproduces the run even if a default is computed. Invalid input throws
an AgentError with code `invalid-machine-input`, matching how a
mismatched resume snapshot behaves; omitted input still skips
validation.

Types: `setupAgent` brands the machine's input type with its own schema,
and runAgent's `input` is now `AgentInputFrom<TMachine>`, which reads the
schema's pre-validation side while the context factory keeps the
validated one. The brand is a `~`-prefixed phantom property rather than a
unique symbol — a symbol has to be exported as a runtime value for
declaration emit to name it. Only object-shaped input is branded, since
intersecting a brand into xstate's NonReducibleUnknown collapses members
to never. Standard Schema throughout; no validation library referenced.

Also ports agentcn's chat-with-PDF quiz recipe, where the quiz loop lives
in instructions.md as prose. The sequencing moves into the machine: one
question per entry into `asking`, a refreshEvery guard instead of "after
3-4 questions", covered pages fed back to retrieval as excludePages,
documentId threaded from context, page hints read off the chunk, and an
idle choosingDocument state when the library is ambiguous. Voice and
question formatting stay in the prompt.
@changeset-bot

changeset-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d857a8a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@statelyai/agent Minor
@statelyai/agent-demo Patch
@statelyai/example-next-host Patch
@statelyai/example-tanstack-start-host Patch
@statelyai/example-tanstack-ai-stream Patch
@statelyai/example-cloudflare-agent-host Patch
@statelyai/example-cloudflare-workers-ai-host Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@davidkpiano
davidkpiano merged commit c9fae4c into next Aug 5, 2026
1 of 2 checks passed
@github-actions github-actions Bot mentioned this pull request Aug 5, 2026
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