Concepts
Standalone React composer for a typed Shiitake Session
Parakeet renders an Agent conversation from a typed Shiitake Session. Browser hosts use its React view; terminal hosts use its headless hooks to render the same history, composer and status through their own UI.
Website hosts mount the same Parakeet component inside an isolated iframe. A
visitor-session adapter supplies the visitor’s scoped Session read, watch and
prompt commands; credentials and admission remain with the host.
markdownPolicy="visitor" renders Markdown links without loading images or
unsafe destinations. A prompt recorded for a human returns status: "recorded",
not a fabricated Agent-work acceptance. Closing the view stops observation, not
the Agent’s work. Pass visitor={{ agentName: "Front Desk" }} to hide model,
usage and command controls. Slash-prefixed text remains a normal visitor
message; the visitor mode does not register or execute slash commands.
Core concepts
Section titled “Core concepts”| Surface | What it owns |
|---|---|
Parakeet |
The browser conversation, including provider and view. |
ParakeetProvider / ParakeetView |
Separate composition when the host needs custom controls in the same conversation scope. |
useTimeline() |
Durable history and the replaceable live tail, bounded history paging and model/usage presentation. |
useComposer() |
Draft, submission, pending work, cancellation and retry controls. |
useStatus() |
Observed work and connection state, including goal controls. |
useMarkdown(content) |
Parsed chat Markdown for custom and terminal renderers. |
Both composition forms require the host’s QueryClientProvider. Parakeet
takes the session directly. With separate composition, pass session to
ParakeetProvider and put ParakeetView and custom hook consumers beneath it.
Pass commands and presentation props to the view, not the provider.
Parakeet and ParakeetView share these optional presentation props:
| Prop | Contract |
|---|---|
composerActionVariant |
"primary" or "outline". |
renderToolActions |
Renders host actions for a completed ToolItem. |
renderAssistantAttachment |
Receives the original settled reply text. |
formatAssistantText |
Changes displayed reply text without changing the attachment input. |
onLink |
Receives a transcript URL; return true to prevent browser navigation. |
empty |
Replaces the empty conversation content. |
Import renderer-neutral hooks from /headless in the terminal. That entry
avoids loading the DOM presentation, Caps and Sprite closure. The root exports
those same hooks for browser callers. They share the Session’s TanStack Query
cache. Derived atoms select observed values rather than duplicating Session
truth. History pages share the host’s cache by Session address and survive
provider remounts until that cache expires. Paging retains four 100-message
pages, with bounded return cursors for the Session’s before-only read API.
Jumping to latest cancels the outstanding history fetch and clears that shared
history window; the live observation remains separate.
Tool calls are grouped in the shared projection. Browser and terminal renderers receive the same typed summaries, error-first sections and edit diffs. Disclosure state belongs to the view. A goal row displays the committed goal; pausing it leaves current accepted work running. Idle conversation state does not mean the goal is complete.
Commands and errors
Section titled “Commands and errors”A host may supply its Whistle runtime through commands. Parakeet builds the
slash registry from that host, its built-ins and advertised extension catalogs.
It owns parsing, completion, stable request identities and interaction state.
The browser and terminal render the same questions, choices and notices.
/model and /thinking use that interaction path.
ParakeetProvider accepts onToast for transient command information. The host
renders it through its existing toast owner: the terminal’s shared toast
viewport or Grove’s ui.showToast. ParakeetInteractions.toast and
subscribeToasts expose the same renderer-neutral seam. Ordinary first-success
echoes are silent, including after a dialog completes. Progress, questions and
handoffs remain in their interaction while active; failures retain their retry
or attention UI rather than expiring as information.
After Try again, a final information or success notice stays in that interaction’s existing result row until Done/close, without an extra toast. A producer that supplies no completion notice does not gain a fabricated result.
Account commands are provider-neutral. The host’s account owner and policy decide which login methods are available. The presentation layer does not gain a credential store or make an advertised command authorized.
Queued timeline rows offer Cancel through
useTimeline().cancelQueued(commandId). The row disappears immediately while
the existing Session owner decides the pending-only cancellation. This never
stops the running turn or restores text to the composer. TooLate or an
unconfirmed response produces a warning and restores any row still reported as
queued; an accepted response stays hidden through stale queued snapshots until
the owner projection catches up. Only one queued gesture waits for its owner at
a time, and switching conversations discards its presentation state. Recall
remains a separate restore-to-composer gesture.
The composer reconciles optimistic prompts with owner facts by request identity. Submitting clears the draft immediately and adds a faded pending message. Acceptance restores normal emphasis; owner-queued messages remain faded. If admission fails, the exact submitted text returns to the composer and the host receives an error toast. Any newer draft stays first, followed by a blank line and the restored text; acceptance never clears newer typing. An uncertain admission retains its request identity while the owner resolves it. An accepted resolution confirms the admitted command or recorded entry; only confirmed withdrawal restores the draft and permits retry with a new request identity, because the old identity is durably fenced. Retrying failed or interrupted durable work also submits a new request identity, preserving the failed prompt’s text and image cargo, including image-only prompts after live-page eviction. It does not consume a newer unsent draft. Failed observation shows connection failure; reconnecting does not prove pending work failed or resubmit it automatically. Typed command errors remain visible and retryable. The host remains responsible for the Session transport, current permissions and operator diagnostics.
An owner’s accepted withdrawal receipt can name a run-free recorded entry:
receipt.commandId === message.id even though message.commandId === null.
That receipt confirms durable recording, not cancellation or model admission. A
translating host retains the original composer identity in its durable metadata
and projects it as messages[].requestId or pending.prompts[].requestId; its
internal admission key is not that identity.
TypeScript consumer dependencies
Section titled “TypeScript consumer dependencies”Install @modelcontextprotocol/sdk@1.25.2 alongside this package when compiling
its complete public declarations. The shared provider SDK imports those types
through an optional peer; the packed consumer supplies it explicitly. This does
not require a provider account or make a provider call.
Packed consumer readiness
Section titled “Packed consumer readiness”Run pnpm run build, pnpm run pack and pnpm run stranger in this package.
The maintained stranger proof installs only package tarballs into a fresh npm
consumer, checks the exact shipped files, runs the public runtime fixture and
compiles every JavaScript entrypoint with strict NodeNext and no library-check
skip. This qualification does not publish npm releases or push a repository
split.
See LICENSE for the license and the package manifest for its public repository destination.