The AI SDK owns the model/tool conversation.
streamText, tool schemas, tool execution, step boundaries, and stopWhen form the native loop. Snappy consumes the SDK’s typed stream instead of parsing an invented protocol. [AI1]
Snappy’s core invention is not a clever prompt, a tool catalog, or an OpenUI renderer. It is the causal chain that turns arbitrary language into grounded evidence, executable capability, governed interface state, and a result whose status is never stronger than its proof.
One front door. One provider-neutral loop. One connector road. One render exit. Many explicit truth states.
A greeting, a database question, a cross-source analysis, and a request for a live interface do not get separate regex lanes. Disclosure depth changes; the harness does not.
streamText, tool schemas, tool execution, step boundaries, and stopWhen form the native loop. Snappy consumes the SDK’s typed stream instead of parsing an invented protocol. [AI1]
Text deltas, reasoning, tool calls, tool results, errors, denials, approvals, step starts, and step finishes are translated into the application’s event and transcript model by one adapter. [H1]
The harness lint rejects regex intent lanes because they make demos look smart while arbitrary language fails. The model chooses; schemas and runtime boundaries constrain. [H2]
Do not write a second agent loop around an SDK that already owns the loop. Put product semantics at the tool and lifecycle boundaries, where they can be typed, tested, and observed.
The resident surface is deliberately tiny. The model sees five stable product concepts; the long tail arrives only after it asks for an exact capability.
Typed choice and clarification surface.
Pulls exact deferred schemas into a later step.
Fetches the OpenUI vocabulary when shape detail matters.
The sole connected-source read road.
The sole OpenUI render exit.
Coverage was high, but every turn paid the catalog cost and attention diluted.
They reduced payload, but the router had to guess before the model had reasoned.
The model gets a small semantic map and fetches schemas by need. The old 182 definitions remain searchable, not resident. [L1]
resident = [AskUserQuestion, ToolSearch, ListComponents,
RunConnectorCommand, ComposeUI]
ToolSearch("select:invoke_mutation")
→ attaches the exact callable schema for the next SDK step
The key implementation detail is timing: a deferred schema is inserted into the callable tool map only after ToolSearch returns, before the next SDK step selects a tool. [H3] [H1]
The model is allowed to judge what the user means. It is not allowed to invent the connector operation, row shape, source identity, or executable handle.
{connector, request, limit?} is compact enough to stay resident. Ready connector slugs can be enum-constrained once the boot snapshot is warm.
An exact resolved read executes directly. Thin ID-only lists can fan out to parallel detail reads. No match can still enter a bounded focused loop.
Printing Press owns auto selection. Mirror-first sources can answer instantly while a scheduled live sync runs in the background.
Each operation keeps arguments, served fields, source state, query handle, query contract, and eligible mutation contracts.
RunConnectorCommand({ connector: "calendar", request: "What conflicts next week?" })
→ {
rows: [...bounded evidence...],
operation_ledger: [{ command, args, served_fields, source }],
registered_query_handles: ["calendar_events"],
query_contracts: [...], mutation_contracts: [...],
mirror_snapshot: { freshness, credential_state, serving_decision }
}
If which resolves a complete operation, it runs immediately—no inner model tax.
Counts, filters, aggregations, and compound conditions can run against the connector’s generated SQLite store when the binary declares the verb.
When list rows are only identifiers, the focused loop can fetch bounded details in parallel rather than asking ComposeUI to hallucinate them.
Rows ground model judgment at composition time. The exact Query() handle executes again at render time. The source-binding contract proves that preview and live binding refer to the same operation, arguments, source, and served fields.
Primary implementation: RunConnectorCommand boundary, focused Printing Press loop, result contracts, and the Printing Press generator.
OpenUI supplies the language and renderer. Snappy turns that renderer into a product boundary that understands evidence, identity, placement, durability, and effects.
A component library defines the allowed vocabulary. Its schema produces model instructions. Streaming Lang is parsed progressively, then mapped to React. toolProvider executes Query() and Mutation() at runtime. [OU1] [OU2]
One authored Html(html, data) canvas can own the ambitious composition. HtmlMount portals native OpenUI children into named slots. No second JavaScript runtime is given to the model. [H4]
events = Query("calendar_events", { range: "next_week" }, { rows: [] })
create = Mutation("calendar_create", { title: $title, start: $start })
save = Button("Stage event", Action([@Run(create)]))
root = Html("<main>...{{#each events}}...{{/each}}...<div data-mount='save'/></main>",
{ events: events.rows },
[HtmlMount("save", save)])
Syntax is insufficient. Source-backed claims must bind served fields; multi-source asks must visibly synthesize; rootless patches need an existing program; unsupported runtime JavaScript is rejected.
An answer can stay inline, open in a detailed panel, or—when deliberately named with app_slug and live-app kind—become a durable right-panel application.
Prose and the ComposeUI tool part remain one assistant turn. A previous sentinel plus separate surface message created duplicate rendering and prose loss on reload; it was deleted.
The central boundary is harness-tools-compose-inline.ts; product-semantic checks live in tool-registry-validation.ts; the runtime provider is openui-provider.ts.
The runtime compiles instruction ownership, cache placement, current reality, relevant skills, active object state, and tool evidence into a decision-local frame.
Identity, tool usage law, grammar, and enduring product rules are separated so provider prompt caching can reuse them safely.
Local time, ready connector names, internal query map, grants, active artifact identity, and response controls are rebuilt unconditionally.
Semantic loaders select a skill, then digest markers prove which exact file body entered context. ToolSearch can pull runtime Skill capability into the same loop.
Tool results are remembered with caps and wrappers. They are evidence for the task, never instructions allowed to rewrite system law.
Live context was once skipped only when a profile-bearing request arrived—the exact shape sent by the real app. Curl probes saw connector names; user turns did not. Every isolated test passed while production failed. The repair was architectural: live reality now builds unconditionally.
A tool result can reach the wire while the browser has not painted it. A write can return while its remote effect is unknown. Snappy names these states instead of flattening them into “done.”
stopWhen decides whether generation continues.It is about orchestration: step count, terminal tool shape, or another stop predicate. The Vercel AI SDK can feed tool results into later model steps. [AI2]
Terminal-surface detection and onStepFinish read the same results. A previous two-detector split called composed surfaces empty; one ledger now owns the fact.
A timeout after dispatch becomes execution_outcome_unknown. Durable task identity supports poll, input, resume, or reconcile—never duplicate execution.
A static report with no Query() or entity binding owes no paint acknowledgement. Waiting for one sentenced correctly rendered static work to a false timeout. Paint proof is required only where live binding actually needs it.
See surface-result-detectors.ts, ai-sdk-terminal-status.ts, and execute-governed-mutation.ts.
The product can know how to perform an operation without having permission to do it. Snappy makes that separation visible to humans and other AIs.
Ask naturally, read governed connector data, request grants, stage actions, inspect approvals, render OpenUI, open artifacts, and read Activity—without guessing local routes.
The loopback MCP developer surface may mint and forward an operator session from the single-user trust root. A remote hub never holds that root.
The generated one-to-one wrapper is intentionally broad. It forces each local HTTP route to state object, action, access class, mode, grant, receipt, params, result, and proof.
| Mode | What it means | Required evidence |
|---|---|---|
| Read | Source or internal state may be observed inside granted scope. | Grant decision, source identity, rows/result, Activity event where applicable. |
| Staged write | The capability can be declared and prepared, but no remote effect occurs yet. | Exact mutation, schema-valid args, risk/approval policy, durable approval request. |
| Approved execution | A human-plane decision authorizes one bounded effect. | Approval identity, idempotency, execution receipt, outcome state. |
| Verified | A fresh read or provider proof confirms the promised effect. | Read-after-write link or explicit verification tier. |
The forcing function lives in mcp-capability-registry.ts. It is not the user-facing story; it is the x-ray that prevents hidden unclassified side doors.
The deepest engineering pattern is how the system was built: observe a real failure, name the violated invariant, place one owner at the earliest enforceable boundary, add a red proof, and delete the superseded road.
Marking invented content as fake was hiding the failure. Factual data must bind a served field or be omitted.
A generic feed cannot masquerade as the requested operation. Wrong-resource or low-confidence resolution pauses honestly.
A late instruction could not beat 19.6k of familiar component signatures. Disclosure moved to the decision moment.
Compact OpenLang did not need 1,500 hidden reasoning tokens per step. Auto now resolves low; explicit operator choice wins.
Sentinel text plus a separate surface message duplicated renders and lost prose. One SDK-native assistant message owns both.
Stop and final truth now inspect the same step results instead of grading two projections of one event.
A green probe against packaged :3147 does not prove source HEAD. /deploy-truth must identify owner and commit before and after.
When curl and the app disagree, compare prompt block sizes and request shape before blaming the model.
Four 60k connector results exhausted provider input after the surface already existed. Each result is now capped at 24k characters.
When an issue resists the first probe, instrument the whole suspect chain with roughly ten numbered logs, drive the real flow once, and find the missing or wrong-numbered event. Remove the logs after the invariant is restored.
Nothing here diminishes the SDKs. The differentiation is precise: Snappy keeps their primitives, then adds product laws for truth, authority, source time, durable objects, and AI-operable governance.
| Concern | Vercel AI SDK | OpenUI | Snappy harness |
|---|---|---|---|
| Model execution | Provider-neutral generation, typed tools, automatic multi-step loop, streaming parts, stop predicates. | Consumes model-produced Lang; does not own the provider loop. | Maps one native SDK loop into product events, tool ledger, progress, recovery, and terminal truth. |
| UI generation | Transports text, data, files, reasoning, and tool states; UI shape is application-owned. | Library contract, prompt generator, streaming parser, Renderer, state, actions, Query/Mutation provider. | HTML-first ambitious canvases, semantic validation, source-binding receipts, placement, identity, and durable Live Apps. |
| Connected data | Applications define tools and execution. | toolProvider invokes named Query/Mutation functions. | Printing Press mints semantic connectors, SQLite mirrors, operation ledgers, bounded rows, exact handles, freshness, and connection truth. |
| Authority | Tool approval stream parts exist; policy remains application-owned. | Mutation declares executable data capability; application owns authorization. | Grants, staged writes, operator plane, human approvals, idempotency, receipts, reconcile, and verification tiers. |
| Durability | Message and persistence strategy is application-owned. | Artifacts expose preview and detailed views; storage remains application integration. | Artifact identity, versions, exact Lang body, bindings, app slug, surface kind, patch lineage, and fail-closed explicit saves. |
| Success | Generation and step finish states. | Parse/render hooks and error callbacks. | Separate emitted, parsed, painted, hydrated, persisted, approved, executed, verified, and outcome-unknown states. |
API or traffic → Printing Press capability → bounded evidence + exact handle → model judgment → OpenUI program → product-semantic validation → runtime binding → governed action → durable receipt → fresh verification. Each arrow is owned and testable.
Its visual confidence should never exceed the confidence of the evidence, authority, identity, and lifecycle state underneath it.
The interface is not the answer. It is the most legible proof the system can currently support.
Let the model reconcile ambiguity and synthesize meaning.
Let the boundary own source names, object IDs, permissions, and exact executable handles.
Say whether data was live, mirrored, stale, empty, syncing, or disconnected.
Separate capability declaration, gesture-time staging, human approval, execution, and verification.
For every fact—message, surface, artifact, receipt, verdict—choose one canonical writer and reader.
When an architecture can only be made truthful by removing a road, remove it.
Current implementation, measured production failure, historical lineage, inference, and target state are not interchangeable.