Sidebar

Displays the mobile sidebar.

Command palette

Search for a command to run…

Docs
Installation
Theming
Typography
Testing
I18n
Stimulus
Form Builder
Pagination
Deferred Regions
Optimistic Forms
Recipes
Page Agent
Agent Skills
Editors
API Reference
Data Table
Caching
Stable IDs
Rails Engines
Accessibility
MCP Server
WebMCP
AG-UI Relay
A2UI Surfaces
Libraries
Core
UI
Charts
Agent
Simple Form
Extract
Icons
Lucide
Components
Accordion
Alert
Alert Dialog
Aspect Ratio
Attachment
Autocomplete
Avatar
Badge
Breadcrumb
Bubble
Button
Button Group
Calendar
Card
Carousel
Checkbox
Clipboard Text
Code Block
Collapsible
Combobox
Command
Command Dialog
Context Menu
Data Table
Date Field
Date Picker
Date Time Field
Deferred
Dialog
Drawer
Dropdown Menu
Empty
Field
Field Group
Field Separator
Fieldset
File Input
Hover Card
Icon
Input
Input Group
Input Otp
Item
Kbd
Label
Link
Marker
Menubar
Message
Message Scroller
Metadata List
Meter
Native Select
Navigation Menu
Number Field
Pagination
Popover
Progress
Questionnaire
Radio Group
Resizable
Scroll Area
Search Field
Select
Sensitive Input
Separator
Sheet
Sidebar
Skeleton
Slider
Spinner
Stat
Switch
Table
Tabs
Tag Group
Textarea
Time Field
Timeline
Toast
Toast Trigger
Toaster
Toggle
Toggle Group
Toolbar
Tooltip
Tree
Typeset
Charts
Adapter Chart
Area Chart
Bar Chart
Composed Chart
Line Chart
Pie Chart
Radar Chart
Radial Bar Chart
Scatter Chart
Demos
Chat Replay
AG-UI Relay
A2UI Surface
Interactive Filter
Live Streaming
Synced Tooltips
Brush & Zoom
Reference
Installation — Install
Installation — Upgrade an existing app
Installation — The three ownership tiers
Installation — Copy-ins: poetry:diff
Installation — Scaffold templates
Theming — The nine themes
Theming — Installing a theme
Theming — Owning the design outright
Theming — How this site renders all nine
Theming — The font pairing
Theming — Overriding the theme
Theming — DESIGN.md interop
Theming — What stays out of the theme
Theming — Consumer utilities
Theming — Color scheme (dark mode)
Theming — Portals and scoped themes
Typography — Default
Typography — Headings
Typography — Inline code
Typography — Large
Typography — Lead
Typography — Muted
Typography — Small
Testing — The doctrine
Testing — Lint first: Poetry check
Testing — Tier 1: wiring tests
Testing — Tier 2: behavior
Testing — Tier 3: the browser pass
Testing — The system-test helpers
Testing — What to assert, in one line
I18n — The shipped catalogue
I18n — Overriding keys and adding locales
I18n — Visible copy is yours
I18n — Forms localize through the model
I18n — Dates
Stimulus — The model
Stimulus — 1. Attach your controller to a component
Stimulus — 2. React to what a component does
Stimulus — 3. Drive a component from your markup
Stimulus — 4. Declare it in Ruby
Stimulus — 5. Change what a Poetry controller does
Stimulus — Checked at every rung
Form Builder — Coming from Simple Form?
Form Builder — Default
Form Builder — Errors
Form Builder — Inference
Form Builder — Layout
Pagination — Installation
Pagination — Wire your controller
Pagination — kaminari
Pagination — pagy
Pagination — will_paginate
Pagination — The window, labels, and one page
Pagination — Kaminari
Pagination — Pagy
Pagination — Will paginate
Deferred Regions — Error retry
Deferred Regions — In tabs
Deferred Regions — Lazy
Optimistic Forms — How it works
Optimistic Forms — Setup
Optimistic Forms — Usage
Optimistic Forms — The server contract
Optimistic Forms — The submitted value
Optimistic Forms — When the prediction can be wrong
Optimistic Forms — Authoring streams directly
Optimistic Forms — When to reach for it
Optimistic Forms — Default
Optimistic Forms — Reconciliation
Recipes — How it works
Recipes — In-page agent embed
Recipes — poetry scaffold templates
Recipes — Data index screen
Recipes — Settings screen
Recipes — poetry skill bundle
Recipes — poetry-component skill bundle
Recipes — poetry-design skill bundle
Page Agent — What this is
Page Agent — Or hand the agent tools
Page Agent — Activate
Page Agent — Task list
Page Agent — Sample implementation
Page Agent — Provenance & limits
Agent Skills — How it works
Agent Skills — poetry
Agent Skills — In a Rails app with Poetry
Editors — One generator
Editors — The MCP server
Editors — The check hook
Editors — Snippets
Editors — HTML+ERB tooling
Editors — Design tools
Data Table — The controller
Data Table — The view
Data Table — Sticky headers
Data Table — Row selection
Data Table — Scoped updates (optional)
Data Table — Filter as you type (optional)
Data Table — Accessibility
Data Table — Row mutations belong to another tier
Caching — The digest blind spot
Caching — The recipe: version-keyed cache keys
Caching — Rules for markup that can be cached at all
Caching — Components you copied in
Caching — Ids inside cached fragments
Caching — HTTP caching, ETags, and CSRF
Stable IDs — The identity ladder
Stable IDs — When you must pass identity
Stable IDs — Keeping primary keys out of the DOM
Stable IDs — What morph identity does and does not preserve
Stable IDs — The sequence mode (opt-in, experimental)
Stable IDs — ETags and CSRF: what ids do and don't buy
Rails Engines — Why this works
Rails Engines — The engine recipe
Rails Engines — Page-level utilities: the one wiring step
Rails Engines — Retheming the whole application
Rails Engines — Customizing a component from an engine
Rails Engines — Keep one theme contract
Accessibility — What Poetry guarantees
Accessibility — What automation cannot see
Accessibility — Set up a pass
Accessibility — The universal checklist
Accessibility — Pattern checklists
Accessibility — Form controls
Accessibility — Overlays
Accessibility — Menus and command palettes
Accessibility — Disclosure and navigation
Accessibility — Data, feedback, and streaming
Accessibility — Automated checks in your app
Accessibility — Recording findings
MCP Server — The server
MCP Server — The loop it closes
MCP Server — The ten tools
MCP Server — Over HTTP
MCP Server — Routing a brief
MCP Server — The check verdict
MCP Server — Skills and guidance at runtime
MCP Server — One source, every surface
WebMCP — What it is
WebMCP — Live: the tools on this page
WebMCP — Search the docs: a declarative tool
WebMCP — Two paths
WebMCP — Designing tools for your app
WebMCP — Declaring tools in your own components
WebMCP — Setup
WebMCP — Safety by construction
AG-UI Relay — What it is
AG-UI Relay — Run an agent
AG-UI Relay — Render the transcript
AG-UI Relay — Frontend tools
AG-UI Relay — Interrupts
AG-UI Relay — A2UI surfaces in the stream
AG-UI Relay — Deterministic development
AG-UI Relay — Setup
A2UI Surfaces — Two catalogs
A2UI Surfaces — Fold the envelope
A2UI Surfaces — Render a surface
A2UI Surfaces — Stream updates
A2UI Surfaces — Actions
A2UI Surfaces — Checks
A2UI Surfaces — Functions
A2UI Surfaces — State across updates
A2UI Surfaces — Transports
A2UI Surfaces — Setup
Accordion — Installation
Accordion — Default
Accordion — Borders
Accordion — Card
Accordion — Disabled
Accordion — Multiple
Accordion — Non collapsible
Accordion — Wiring
Alert — Installation
Alert — Default
Alert — Action
Alert — Custom colors
Alert — Destructive
Alert — Dismissible
Alert — With action
Alert — With icon
Alert Dialog — Installation
Alert Dialog — Default
Alert Dialog — Small
Alert Dialog — Small with media
Alert Dialog — With media
Alert Dialog — Wiring
Aspect Ratio — Installation
Aspect Ratio — Default
Aspect Ratio — Portrait
Aspect Ratio — Square
Aspect Ratio — With image
Attachment — Installation
Attachment — Default
Attachment — Group
Attachment — Idle dropzone
Attachment — Sizes
Attachment — States
Attachment — Vertical with actions
Attachment — With trigger
Autocomplete — Installation
Autocomplete — Default
Autocomplete — Value override
Autocomplete — Wiring
Avatar — Installation
Avatar — Default
Avatar — Badge
Avatar — Badge with icon
Avatar — Dropdown
Avatar — Group
Avatar — Group with icon
Avatar — Sizes
Avatar — With image
Badge — Installation
Badge — Default
Badge — Custom colors
Badge — Link
Badge — Status
Badge — Variants
Badge — With icon
Badge — With spinner
Breadcrumb — Installation
Breadcrumb — Default
Breadcrumb — Collapsed
Breadcrumb — Custom separator
Breadcrumb — Dropdown
Breadcrumb — Link component
Bubble — Installation
Bubble — Default
Bubble — Collapsible
Bubble — Group
Bubble — Links and buttons
Bubble — Popover
Bubble — Quick replies
Bubble — Reactions
Bubble — Tooltip
Bubble — Variants
Button — Installation
Button — Default
Button — As link
Button — Button group
Button — Icon buttons
Button — Link
Button — Loading
Button — Rounded
Button — Sizes
Button — With icon
Button Group — Installation
Button Group — Default
Button Group — Input action
Button Group — Input group
Button Group — Nested
Button Group — Popover
Button Group — Separator
Button Group — Sizes
Button Group — Split
Button Group — Text prefix
Button Group — Vertical
Button Group — With dropdown
Button Group — With select
Calendar — Installation
Calendar — Default
Calendar — Bounded
Calendar — Custom cell size
Calendar — Date and time
Calendar — Month and year selector
Calendar — Range
Calendar — Week numbers
Calendar — Wiring
Card — Installation
Card — Default
Card — Login form
Card — Terms of service
Card — With action and footer
Card — With media
Carousel — Installation
Carousel — Default
Carousel — Sizes
Carousel — Spacing
Carousel — Vertical
Carousel — Wiring
Checkbox — Installation
Checkbox — Default
Checkbox — Card
Checkbox — Disabled
Checkbox — Group
Checkbox — In a field
Checkbox — In a table
Checkbox — States
Checkbox — With description
Checkbox — Wiring
Clipboard Text — Installation
Clipboard Text — Default
Clipboard Text — Truncated copy
Clipboard Text — Wiring
Code Block — Installation
Code Block — Default
Code Block — Numbered and highlighted
Code Block — Wiring
Collapsible — Installation
Collapsible — Default
Collapsible — File tree
Collapsible — Initially open
Collapsible — Settings panel
Collapsible — With icon
Collapsible — Wiring
Combobox — Installation
Combobox — Default
Combobox — Auto highlight
Combobox — Clear button
Combobox — Custom items
Combobox — Disabled
Combobox — Disabled options
Combobox — Grouped
Combobox — Invalid
Combobox — Multiple
Combobox — Valued
Combobox — Wide
Combobox — With icon
Combobox — Agent tools
Combobox — Wiring
Command — Installation
Command — Default
Command — Close button
Command — Dialog
Command — Initial value
Command — Scrollable
Command — With icons
Command — Zero match
Command — Wiring
Command Dialog — Installation
Command Dialog — Default
Command Dialog — Empty state
Command Dialog — Hotkey
Command Dialog — Scrollable
Command Dialog — With icons
Command Dialog — Wiring
Context Menu — Installation
Context Menu — Default
Context Menu — Checkboxes
Context Menu — Destructive
Context Menu — Disabled surface
Context Menu — Focusable surface
Context Menu — Groups
Context Menu — Radio
Context Menu — Sides
Context Menu — Submenu
Context Menu — With icon
Context Menu — Wiring
Data Table — Installation
Data Table — Default
Data Table — Cell formatting
Data Table — Empty
Data Table — Filtered and paginated
Data Table — Row actions
Data Table — Selectable
Data Table — Wiring
Date Field — Installation
Date Field — Default
Date Field — With range
Date Field — Year first
Date Field — Wiring
Date Picker — Installation
Date Picker — Default
Date Picker — Date of birth
Date Picker — Input
Date Picker — Preselected
Date Picker — Range
Date Picker — With time
Date Picker — Wiring
Date Time Field — Installation
Date Time Field — Default
Date Time Field — With seconds
Date Time Field — Wiring
Deferred — Installation
Deferred — Default
Deferred — Wiring
Dialog — Installation
Dialog — Default
Dialog — Confirmation
Dialog — No close button
Dialog — Scrollable content
Dialog — Sticky footer
Dialog — Agent tools
Dialog — Wiring
Drawer — Installation
Drawer — Default
Drawer — Edit form
Drawer — Nested
Drawer — Non modal
Drawer — Position
Drawer — Responsive
Drawer — Snap points
Drawer — Swipe handle
Drawer — Agent tools
Drawer — Wiring
Dropdown Menu — Installation
Dropdown Menu — Default
Dropdown Menu — Avatar
Dropdown Menu — Checkbox with icons
Dropdown Menu — Checkboxes
Dropdown Menu — Complex
Dropdown Menu — Inset and disabled
Dropdown Menu — Radio group
Dropdown Menu — Radio with icons
Dropdown Menu — With icons
Dropdown Menu — Wiring
Empty — Installation
Empty — Default
Empty — Background
Empty — Outline
Empty — With avatar
Empty — With avatar group
Empty — With icon and actions
Empty — With input
Field — Installation
Field — Default
Field — Checkbox
Field — Choice card
Field — Fieldset
Field — Group
Field — Horizontal switch
Field — Responsive
Field — With error
Field — With hint
Field — With radio group
Field — With select
Field — With slider
Field — With textarea
Field Group — Installation
Field Group — Default
Field Group — Choices
Field Separator — Installation
Field Separator — Default
Field Separator — With caption
Fieldset — Installation
Fieldset — Default
Fieldset — Label legend
File Input — Installation
File Input — Default
File Input — Dropzone
File Input — Wiring
Hover Card — Installation
Hover Card — Default
Hover Card — Custom delays
Hover Card — Sides
Hover Card — Wiring
Icon — Installation
Icon — Default
Icon — Animated
Icon — Colors
Icon — Labeled
Icon — Sizes
Icon — Stroke width
Icon — With tooltip
Input — Installation
Input — Default
Input — Button group
Input — Disabled
Input — Field
Input — File
Input — Form
Input — Grid
Input — Inline
Input — Invalid
Input — Required
Input — With badge
Input — With value
Input Group — Installation
Input Group — Default
Input Group — Align
Input Group — Button
Input Group — Custom
Input Group — Dropdown
Input Group — Icon
Input Group — Kbd
Input Group — Spinner
Input Group — Text
Input Group — Textarea block
Input Group — Textarea header
Input Otp — Installation
Input Otp — Default
Input Otp — Alphanumeric
Input Otp — Form
Input Otp — Four digits
Input Otp — Groupings
Input Otp — In a field
Input Otp — States
Input Otp — Wiring
Item — Installation
Item — Default
Item — Avatar
Item — Dropdown
Item — Group
Item — Image
Item — Link
Item — Outline with icon and actions
Item — Sizes
Item — Variants
Item — With header and footer
Kbd — Installation
Kbd — Default
Kbd — Chord
Kbd — Command
Kbd — Group
Kbd — In button
Kbd — In tooltip
Kbd — Search hint
Label — Installation
Label — Default
Label — Disabled
Label — Form
Label — With checkbox
Label — With description
Link — Installation
Link — Default
Link — Current
Link — External
Link — Underline
Marker — Installation
Marker — Default
Marker — Border
Marker — Links and buttons
Marker — Separator
Marker — Shimmer
Marker — Status
Marker — Variants
Marker — With icon
Menubar — Installation
Menubar — Default
Menubar — Checkbox
Menubar — Disabled menu
Menubar — Icons
Menubar — Radio
Menubar — Submenu
Menubar — Wiring
Message — Installation
Message — Default
Message — Actions
Message — Align end
Message — Attachment
Message — Conversation
Message — Grouped
Message — Header and footer
Message — Markdown
Message Scroller — Installation
Message Scroller — Default
Message Scroller — Anchored turns
Message Scroller — Commands
Message Scroller — Group chat
Message Scroller — Open at start
Message Scroller — Scroll state
Message Scroller — Streaming
Message Scroller — Without jump button
Message Scroller — Wiring
Metadata List — Installation
Metadata List — Default
Metadata List — Two columns
Meter — Installation
Meter — Default
Meter — With value text
Native Select — Installation
Native Select — Default
Native Select — Grouped
Native Select — Invalid
Native Select — Small disabled
Navigation Menu — Installation
Navigation Menu — Default
Navigation Menu — Viewport
Navigation Menu — With descriptions
Navigation Menu — With icon
Navigation Menu — Wiring
Number Field — Installation
Number Field — Default
Number Field — Currency
Number Field — Decimal steps
Number Field — Wiring
Pagination — Installation
Pagination — Adapters
Pagination — Default
Pagination — First page
Pagination — Icons only
Pagination — Long range
Pagination — Short range
Pagination — Simple
Popover — Installation
Popover — Default
Popover — Align
Popover — Basic
Popover — Modal
Popover — Open
Popover — Side top
Popover — With anchor
Popover — With form
Popover — Wiring
Progress — Installation
Progress — Default
Progress — Complete
Progress — Controlled
Progress — Custom max without value
Progress — Label
Questionnaire — Installation
Questionnaire — Default
Questionnaire — Animated
Questionnaire — Card
Questionnaire — Custom progress
Questionnaire — Dialog
Questionnaire — Shortcuts
Questionnaire — Skip
Questionnaire — Validation
Questionnaire — Wiring
Radio Group — Installation
Radio Group — Default
Radio Group — Cards
Radio Group — Disabled
Radio Group — Fieldset
Radio Group — Horizontal layout
Radio Group — In a field
Radio Group — Invalid
Radio Group — With description
Radio Group — Wiring
Resizable — Installation
Resizable — Default
Resizable — Handle
Resizable — Nested
Resizable — Three panels
Resizable — Vertical
Resizable — Wiring
Scroll Area — Installation
Scroll Area — Default
Scroll Area — Horizontal
Search Field — Installation
Search Field — Default
Search Field — With value
Search Field — Wiring
Select — Installation
Select — Default
Select — Align item with trigger
Select — Disabled
Select — Groups
Select — Invalid
Select — Scrollable
Select — Small
Select — Valued
Select — Wiring
Sensitive Input — Installation
Sensitive Input — Default
Sensitive Input — With copy
Sensitive Input — With copy and tooltip
Sensitive Input — Wiring
Separator — Installation
Separator — Default
Separator — List
Separator — Menu
Separator — Vertical semantic
Sheet — Installation
Sheet — Default
Sheet — Form
Sheet — No close button
Sheet — Sides
Sheet — Agent tools
Sheet — Wiring
Sidebar — Installation
Sidebar — Default
Sidebar — Loading
Sidebar — Offcanvas collapsed
Sidebar — Submenu
Sidebar — User menu
Sidebar — Workspace switcher
Sidebar — Wiring
Skeleton — Installation
Skeleton — Default
Skeleton — Avatar
Skeleton — Block
Skeleton — Card
Skeleton — Composed
Skeleton — Form
Skeleton — Table
Skeleton — Text
Slider — Installation
Slider — Default
Slider — Controlled
Slider — Disabled
Slider — Multiple thumbs
Slider — Range
Slider — Range with gap
Slider — Vertical
Slider — Wiring
Spinner — Installation
Spinner — Default
Spinner — Button
Spinner — Custom
Spinner — Empty state
Spinner — In badge
Spinner — In input group
Spinner — Size
Spinner — With context label
Stat — Installation
Stat — Default
Stat — With description
Switch — Installation
Switch — Default
Switch — Card
Switch — Description
Switch — In a field
Switch — Invalid
Switch — Sizes
Switch — States
Switch — Wiring
Table — Installation
Table — Default
Table — Actions
Table — Footer
Table — Minimal
Table — Sticky
Tabs — Installation
Tabs — Default
Tabs — Disabled
Tabs — Line
Tabs — Vertical
Tabs — With icon
Tabs — Agent tools
Tabs — Wiring
Tag Group — Installation
Tag Group — Default
Tag Group — As form value
Tag Group — Wiring
Textarea — Installation
Textarea — Default
Textarea — Auto grown
Textarea — Button
Textarea — Disabled
Textarea — Field
Textarea — Invalid
Textarea — With hint
Textarea — With label
Time Field — Installation
Time Field — Default
Time Field — Twenty four hour
Time Field — Wiring
Timeline — Installation
Timeline — Default
Timeline — Horizontal
Timeline — With icons
Toast — Installation
Toast — Default
Toast — Promise
Toast — Variants
Toast — With action
Toast — Wiring
Toast Trigger — Installation
Toast Trigger — Default
Toast Trigger — Wiring
Toaster — Installation
Toaster — Default
Toaster — Positions
Toaster — Wiring
Toggle — Installation
Toggle — Default
Toggle — Outline
Toggle — Sizes
Toggle — States
Toggle — With text
Toggle — Wiring
Toggle Group — Installation
Toggle Group — Default
Toggle Group — Custom
Toggle Group — Disabled
Toggle Group — Outline
Toggle Group — Single
Toggle Group — Sizes
Toggle Group — Spaced
Toggle Group — Vertical
Toggle Group — Wiring
Toolbar — Installation
Toolbar — Default
Tooltip — Installation
Tooltip — Default
Tooltip — Disabled trigger
Tooltip — Keyboard
Tooltip — Open
Tooltip — Provider row
Tooltip — Side bottom
Tooltip — Sides
Tooltip — With label
Tooltip — Wiring
Tree — Installation
Tree — Default
Tree — With links
Tree — Wiring
Typeset — Installation
Typeset — Default
Typeset — Chat preset
Typeset — Docs preset
Typeset — Not typeset
Typeset — Overrides
Typeset — Responsive table
Adapter Chart — Installation
Adapter Chart — Bring your own engine
Adapter Chart — Default
Adapter Chart — Wiring
Area Chart — Installation
Area Chart — Default
Area Chart — Legend toggle
Area Chart — Wiring
Bar Chart — Installation
Bar Chart — Default
Bar Chart — Active
Bar Chart — Horizontal
Bar Chart — Label
Bar Chart — Mixed
Bar Chart — Multiple
Bar Chart — Negative
Bar Chart — Stacked
Bar Chart — Wiring
Composed Chart — Installation
Composed Chart — Default
Composed Chart — Full mix
Composed Chart — Wiring
Line Chart — Installation
Line Chart — Default
Line Chart — Dots
Line Chart — Dots colors
Line Chart — Label
Line Chart — Linear
Line Chart — Multiple
Line Chart — References and errors
Line Chart — Step
Line Chart — Wiring
Pie Chart — Installation
Pie Chart — Default
Pie Chart — Donut
Pie Chart — Donut active
Pie Chart — Donut text
Pie Chart — Label list
Pie Chart — Legend
Pie Chart — Stacked
Pie Chart — Wiring
Radar Chart — Installation
Radar Chart — Default
Radar Chart — Dots
Radar Chart — Grid circle
Radar Chart — Grid fill
Radar Chart — Grid none
Radar Chart — Legend
Radar Chart — Lines only
Radar Chart — Multiple
Radar Chart — Wiring
Radial Bar Chart — Installation
Radial Bar Chart — Default
Radial Bar Chart — Grid
Radial Bar Chart — Label
Radial Bar Chart — Shape
Radial Bar Chart — Stacked
Radial Bar Chart — Text
Radial Bar Chart — Wiring
Scatter Chart — Installation
Scatter Chart — Default
Scatter Chart — Bubbles
Scatter Chart — Multiple
Scatter Chart — Reference zones
Scatter Chart — Wiring
Action bar — Installation
Action bar — Default
Action bar — Styling
App shell — Installation
App shell — Default
App shell — Styling
Data index — Installation
Data index — Default
Data index — Styling
Destructive panel — Installation
Destructive panel — Default
Destructive panel — Revoke deploy token
Destructive panel — Styling
Page header — Installation
Page header — Default
Page header — Styling
Section card — Installation
Section card — Default
Section card — Styling
Stepper — Installation
Stepper — Default
Stepper — Styling
Top nav — Installation
Top nav — Default
Top nav — Styling
Chat Replay — Default
AG-UI Relay — Default
A2UI Surface — Default
Interactive Filter — Default
Live Streaming — Default
Synced Tooltips — Default
Brush & Zoom — Default
poetry-core — ActiveModel
poetry-core — ActiveModel::Type
poetry-core — ActiveModel::Type::List
poetry-core — ActiveModel::Type::Symbol
poetry-core — Poetry
@poetry/controllers — IncompleteDate
poetry-ui — Poetry
poetry-charts — Poetry
poetry-agent — Poetry
poetry-simple_form — Poetry
poetry-extract — Poetry
Core — What it is
Core — Install
Core — Build your own component library
Core — The component class
Core — The style dictionary
Core — Behavior from the primitives
Core — The registry
Core — Two styling paths: Tailwind or BEM
Core — Design tokens
Core — The check
Core — Box and Wrapper
Core — Box, the polymorphic element
Core — Wrapper, conditional outer HTML
UI — What it is
UI — Install and first components
UI — The form builder handoff
UI — Nine themes ship in the gem
UI — Own the code when you want to
UI — Blocks and recipes
UI — The agent surface
UI — Testing helpers
Charts — What it is
Charts — Install
Charts — Basic usage
Charts — Works wherever HTML renders
Charts — Live updates
Charts — Bring your own engine
Charts — The theme palette
Agent — What it is
Agent — Install
Agent — The MCP server
Agent — The WebMCP runtime
Agent — The AG-UI relay
Agent — The A2UI catalog and renderer
Agent — What's in the gem
Simple Form — How it works
Simple Form — Install
Simple Form — What restyles
Simple Form — The end state
Extract — Install and run
Extract — The pipeline
Extract — What it emits
Extract — From extract to theme
Lucide — Install
Lucide — Names, misses, and the fallback
Lucide — The pin

poetry-agent API

The agent-interop gem: the MCP server (stdio exe and the Rack HTTP transport), its bundled assembly, the WebMCP runtime's Ruby side, and the origin-trial middleware.

Generated from the gem's source documentation. Component classes are documented on their own gallery pages — this page carries the framework surface.

Poetry::Agent

module · lib/poetry/agent.rb

poetry-agent: the agent-interop gem of the poetry family - the surfaces through which agents reach the component contract, built once (the registry) and projected many ways:

  • {MCP::Server} - the boot-free poetry-agent MCP server (discover:

list_components / describe_component / check / compose / build_page / get_skill) for coding agents in editors.

  • {WebMCP} - the in-page runtime: rendered components' declared tools

(tool declarations in poetry-core) registered with the browser's document.modelContext for the user's own agent (operate), plus the declarative-form path and the origin-trial delivery.

  • {A2UI} - the registry projected as an A2UI catalog (the vocabulary

an agent generates declarative UI against).

  • {AGUI} - the Rails-side AG-UI client: an agent backend's event stream

folded into chat frames and relayed as versioned Turbo Streams, with the component tools the browser executes advertised as the agent's frontend tools.

Both read the same committed registries; neither is a second source.

Constant Description
VERSION The gem version (lockstep with the poetry family).

.config

The gem's configuration (origin-trial tokens, registration budget).

.configure

Yields the configuration for block-style setup.

Poetry::Agent.configure do |config|
  config.origin_trial_tokens = [ENV["WEBMCP_ORIGIN_TRIAL_TOKEN"]].compact
end

.root

Gem root (the directory containing lib/, exe/, app/).

Poetry::Agent::MCP::Server

class · lib/poetry/agent/mcp/server.rb

The server: constructed with the registry root (and the host's helper names, so check knows the group/provider helpers - though the registry's own "helpers" section now carries those boot-free). icon_names: the active icon set's names, so the check tool validates icon values by membership, not just shape. Everything read is the live committed registry.

Poetry::Agent::MCP::Server.from_registry("registry").serve
Constant Description
ARCHETYPE_MATCH The brief-word -> page-architecture routing table.
STEPS build_page: the guided workflow's five steps, the themes its probe/direct steps sniff for, and the plan step's match floor (one curated archetype keyword = 2, so 2 is the weakest real hit).
STOPWORDS Tokens compose ignores when scoring a brief.
STRONG_MATCH compose routing: the strong-match threshold, and the words too generic to signal a block - connectives, task verbs, and component-anatomy words (title/description/action) every brief uses regardless of scale.
THEMES The theme roster build_page's direct step reads.

.from_registry(root, helpers: nil, icon_names: nil, skills: {}, app_root: nil, recipes: [])

skills: skill name => a zero-arg callable returning the skill's {relative path => content} file map (the get_skill tool). Lazy because the usage skill is generated from the registry on first fetch - server boot stays instant.

#handle(request)

A JSON-RPC 2.0 request hash -> a response hash (or nil for a notification, which gets no reply).

  • request (Hash) — one parsed JSON-RPC 2.0 request

Returns (Hash, nil) — the response hash, or nil for a notification

#initialize(entries:, catalog:, blocks: {}, root: nil, skills: {}, app_root: nil, recipes: [])

app_root: the HOST app directory (where bundle exec poetry-agent runs, i.e. Dir.pwd), so build_page's probe/direct steps can read the app's config/theme. nil => the host is not inspected and those steps degrade gracefully; the registry read (root) is unaffected. recipes: registry-item SUMMARIES (content-free) from the owning gem's RecipeItems projection - the exe passes them so this class stays poetry-ui-free.

Returns (Server) — a new instance of Server

#serve(input: $stdin, output: $stdout)

The thin stdio loop: newline-delimited JSON-RPC in, replies out. A malformed line yields a parse error, never a crashed server.

Poetry::Agent::MCP::HTTP

class · lib/poetry/agent/mcp/http.rb

The MCP server over HTTP: a Rack app answering JSON-RPC 2.0 POSTs (the Streamable HTTP transport's request/response half) with the same pure {Server#handle} the stdio exe uses - one surface, two transports. Mount it at the conventional same-origin /mcp, which is where in-page bridges (WebMCP site MCP server packs among them) look for a site's own MCP server.

Read-only by construction (every tool describes or verifies), so exposing it is a discovery surface, not a mutation surface. The Origin header is validated against the request's own host, so a cross-site page cannot drive it through a visitor's browser.

mount Poetry::Agent::MCP::HTTP.new => "/mcp"
Constant Description
JSON_TYPE The response media type.

#call(env)

The Rack entry point.

  • env (Hash) — the Rack environment

Returns (Array(Integer, Hash, Array<String>)) — status, headers, body

#initialize(server = nil)

  • server (Server, Proc, nil) — the server, a lambda building it on first request, or nil for the bundled assembly

Returns (HTTP) — a new instance of HTTP

Poetry::Agent::MCP::Bundled

module · lib/poetry/agent/mcp/bundled.rb

The server as the exe assembles it: the registry root defaults to the bundled poetry-ui gem, poetry-lucide's icon names power check's icon-membership tier, and poetry-ui's skills, helper names, and recipes ride along when the gem is bundled - each a soft require, so the same assembly serves a core-only host. ONE assembly for the exe and the HTTP mount, so no surface can lag another.

.icon_names

The lucide names, or nil for a host without poetry-lucide (check then validates icon-name shape only).

.server(root: nil, app_root: Dir.pwd)

  • root (String, nil) — a registry root; defaults to poetry-ui
  • app_root (String) — the host app (build_page's probe/direct steps read its config/theme)

Poetry::Agent::WebMCP

module · lib/poetry/agent/webmcp.rb

The WebMCP runtime's Ruby side. The contract itself lives in poetry-core (the tool declarations, their registry projection, and the per-instance webmcp: payload a component root carries); this module owns what only the runtime gem knows: the Stimulus controller identifiers the JS registers, the controllers manifest that lets core's attribute builder validate them, and origin-trial delivery.

Nothing here exposes a tool by itself - a rendered instance opts in (webmcp: "country" on the helper call), and the registrar controller registers that instance's declared tools with document.modelContext on connect, aborting them on disconnect.

Constant Description
CONTROLLER The registrar controller: reads a root's payload and registers its tools, dispatching each to the component's own controller.
FORM_CONTROLLER The declarative-form controller: answers an agent-invoked submit with the form's outcome through SubmitEvent.respondWith.

.manifest_path

The committed controllers manifest (the JS surface of the two controllers), merged into poetry-core's catalog by the engine so use_stimulus / the Builder validate poetry--agent--* names exactly like core's own.

Poetry::Agent::WebMCP::OriginTrial

class · lib/poetry/agent/webmcp/origin_trial.rb

Rack middleware serving the Origin-Trial response header on HTML responses, one token per browser trial (Chrome and Edge run separate trials and issue separate tokens). Tokens come from {Poetry::Agent::Config#origin_trial_tokens}; with none configured the middleware is a pass-through, so it is always safe to mount.

Local development needs no token: enable the API through the browser flag instead.

config.middleware.use Poetry::Agent::WebMCP::OriginTrial
Constant Description
HEADER The response header the browsers read (Rack 3 lowercases names).

#call(env)

The Rack entry point: appends the tokens to HTML responses.

  • env (Hash) — the Rack environment

Returns (Array(Integer, Hash, Object)) — the downstream response

#initialize(app, tokens: nil)

Returns (OriginTrial) — a new instance of OriginTrial

Poetry::Agent::Config

class · lib/poetry/agent/config.rb

The gem's configuration. Every WebMCP surface is OFF until a rendered instance opts in; the settings here shape delivery and budgets, never exposure.

.current

The process-wide configuration instance.

#initialize

Returns (Config) — a new instance of Config

#origin_trial_tokens

Origin-trial tokens the {OriginTrial} middleware serves in the Origin-Trial response header (one per browser trial - Chrome and Edge run separate trials). Empty by default: local development enables the API through the browser flag instead.

#origin_trial_tokens=(value)

Origin-trial tokens the {OriginTrial} middleware serves in the Origin-Trial response header (one per browser trial - Chrome and Edge run separate trials). Empty by default: local development enables the API through the browser flag instead.

#registration_budget

The per-page registration budget the registrar enforces: past this many registered tools on one document, further registrations are dropped with a console warning (each tool costs the agent context; overlap confuses tool choice). Stored on poetry-core's configuration, where the component contract reads it to put the budget on every opted-in root - this accessor writes through.

#registration_budget=(count)

Sets the per-page registration budget.

  • count (Integer)

Poetry

module · lib/poetry/agent.rb

The poetry namespace.

Poetry::Agent::A2UI

module · lib/poetry/agent/a2ui.rb

The A2UI surface: Google's declarative generative-UI format, where an agent emits a flat component list against a client-owned catalog and the client renders it with its own components. Two halves ship:

  • {Catalog} projects Poetry's registry into an A2UI v1.0 catalog

document, so any A2UI agent generates against Poetry's vocabulary and a renderer validates what arrives against the same document.

  • The renderer: {Session} folds the envelope (createSurface,

updateComponents, updateDataModel, deleteSurface) into {Surface}s, {Renderer} renders a surface through the host's view context with a catalog binding ({Catalogs::Basic} for the spec's basic catalog, {Catalogs::Native} for Poetry's own), {Streams} delivers changes as versioned Turbo Streams, and a submitted surface form becomes the spec's action message ({Session#action}). {Functions} holds the basic catalog's function set - the formatString grammar ({Expression}), the formatters, the validators behind checks ({Checks}) - which {Evaluator} runs for both catalogs.

Constant Description
COMMON_TYPES The shared type definitions a catalog may reference.
PROTOCOL_VERSION The protocol version the projection targets.

.catalogs

The catalog bindings a {Session} starts with: the spec's basic catalog and Poetry's own, keyed by catalog id.

Poetry::Agent::A2UI::Catalog

class · lib/poetry/agent/a2ui/catalog.rb

Projects the component registry into an A2UI v1.0 catalog: one JSON Schema component per registry entry (the discriminator component: { const: Name }, style axes as enums, options as typed properties, slots as child references, the content block as text or children, Button's action), the catalog's composition instructions, and the $defs the envelope schema references. The document obeys the v1.0 catalog rules: the allowed top-level keys only, $defs holding exactly anyComponent and anyFunction, external refs into common_types.json only.

catalog = Poetry::Agent::A2UI::Catalog.from_registry(Poetry::Ui.root)
catalog.to_h["components"].keys.first(3) # => ["Accordion", "ActionBar", "Alert"]
catalog.inline                            # => { "catalogId" => ..., "components" => {...} }
Constant Description
DEFAULT_ID The default catalog id (a versioned, conventionally URI-shaped string identifier - it does not need to resolve).
DEFAULT_INSTRUCTIONS The catalog-level guidance every generation reads.
DYNAMIC_BOOLEANS Option names whose boolean values bind to the data model.
DYNAMIC_STRINGS Option names whose values bind to the data model ({path}).
NAME_PATTERN Component and property names must be UAX #31 identifiers.
SKIPPED_OPTIONS Options an agent never sets: styling escape hatches, the envelope's own id, and the universal wiring keywords.

.component_name(path)

The catalog component name of a registry path (PascalCase of its last segment).

  • path (String)

.from_registry(root = nil, **)

Builds the catalog from a registry root (the directory holding config/component_registry.yml), or from the bundled poetry-ui registry when no root is given.

Keyword arguments pass through to {#initialize} (catalog_id:, title:, description:, instructions:, exclude:).

  • root (String, Pathname, nil)

.load_entries(root = nil)

Loads the registry entries under a root (the bundled poetry-ui registry when no root is given).

  • root (String, Pathname, nil)

Returns (Hash{String => Hash}) — entries by registry path

.skipped_option?(name)

  • name (String) — an option name

Returns (Boolean) — whether an agent never sets it

#catalog_id

#component_name(path)

The A2UI component name of a registry path (poetry/ui/alert_dialog becomes AlertDialog).

  • path (String)

#components

The component schemas by name.

#functions

The functions the catalog declares (the basic catalog's set, which the renderer implements).

#initialize(entries:, catalog_id: DEFAULT_ID, title: "Poetry UI Catalog", description: "Poetry's component library as an A2UI catalog, projected from its registry.", instructions: DEFAULT_INSTRUCTIONS, exclude: [])

  • entries (Hash{String => Hash}) — registry entries by path
  • catalog_id (String)
  • title (String)
  • description (String)
  • instructions (String) — catalog-level guidance for the model
  • exclude (Array<String>) — registry paths to leave out

Returns (Catalog) — a new instance of Catalog

#inline

The inline form a transport ships to an agent or a middleware fetches at boot.

Returns (Hash){ "catalogId", "components" }

#to_h

The catalog document (JSON Schema, string keys, components sorted by name).

#to_json(*)

Returns (String) — the document as JSON

Poetry::Agent::A2UI::Catalogs

module · lib/poetry/agent/a2ui/catalogs/basic.rb

The catalog bindings a {Surface} renders through: how a catalog's components reference their children, which of them are bound inputs, and how each one renders with Poetry.

Poetry::Agent::A2UI::Catalogs::Basic

class · lib/poetry/agent/a2ui/catalogs/basic.rb

The A2UI basic catalog (v1.0) rendered with Poetry: every one of its components maps onto a library component or a plain element, its enums onto the library's axes, its bound inputs onto named form controls, and its checks onto native constraint attributes where the browser can enforce them.

Constant Description
ALIGN Cross-axis alignment classes by the catalog's align values.
BUTTON_VARIANTS Poetry's Button variant by the catalog's Button variant values.
FIT Object-fit classes by the Image fit values.
ICON_ALIASES Lucide names for the Material-style icon names agents tend to emit; anything else converts camelCase to kebab-case as is.
ID The catalog id agents name in createSurface.
IMAGE_VARIANTS Sizing classes by the Image variant values.
INPUT_KINDS The bound kinds of the input components.
JUSTIFY Main-axis alignment classes by the catalog's justify values.

#functions

Returns (Functions) — the basic catalog's function set

#id

#inputs(component, scope)

The bound input of a component, if it is one.

  • component (Hash)
  • scope (String, nil)

Returns (Array<Hash>){ path:, kind: }

#references(component)

The child references of a component (ids, id lists, templates).

  • component (Hash)

#render(component, scope, renderer)

  • component (Hash)
  • scope (String, nil)
  • renderer (Renderer)

Returns (String) — HTML

Poetry::Agent::A2UI::Catalogs::Basic::Choice

class < Struct · lib/poetry/agent/a2ui/catalogs/basic.rb

A ChoicePicker's resolved options, binding, selection, and label.

#label

Returns the value of attribute label

Returns (Object) — the current value of label

#label=(value)

Sets the attribute label

  • value (Object) — the value to set the attribute label to.

Returns (Object) — the newly set value

#options

Returns the value of attribute options

Returns (Object) — the current value of options

#options=(value)

Sets the attribute options

  • value (Object) — the value to set the attribute options to.

Returns (Object) — the newly set value

#path

Returns the value of attribute path

Returns (Object) — the current value of path

#path=(value)

Sets the attribute path

  • value (Object) — the value to set the attribute path to.

Returns (Object) — the newly set value

#selected

Returns the value of attribute selected

Returns (Object) — the current value of selected

#selected=(value)

Sets the attribute selected

  • value (Object) — the value to set the attribute selected to.

Returns (Object) — the newly set value

Poetry::Agent::A2UI::Catalogs::Native

class · lib/poetry/agent/a2ui/catalogs/native.rb

Poetry's own catalog (the {Catalog} projection) rendered back through the registry: a component name resolves to its registry entry, style axes and options become constructor keywords, slot properties drive the generated slot setters, text is the content block, children and child nest, and a bound value names the control for the surface form. The registry is the one source: whatever it declares renders, nothing is hand-mapped.

Constant Description
BOUND_OPTIONS The bound options an input component may carry.
KINDS Registry option types by bound kind.

#by_name

Returns (Hash{String => Array(String, Hash)})[path, entry] by component name

#entries

Returns (Hash{String => Hash}) — registry entries by path

#functions

Returns (Functions) — the function set Poetry's catalog declares (the basic one)

#id

#initialize(entries: nil, id: Catalog::DEFAULT_ID)

  • entries (Hash{String => Hash}, nil) — registry entries by path (the poetry-ui registry when nil)
  • id (String) — the catalog id this binding answers to

Returns (Native) — a new instance of Native

#inputs(component, scope)

  • component (Hash)
  • scope (String, nil)

Returns (Array<Hash>){ path:, kind: } for each bound option

#references(component)

The child references: child, children, and every slot property.

  • component (Hash)

#render(component, scope, renderer)

  • component (Hash)
  • scope (String, nil)
  • renderer (Renderer)

Returns (String) — HTML

Poetry::Agent::A2UI::Checks

module · lib/poetry/agent/a2ui/checks.rb

Evaluates a component's checks: each rule's condition (a binding or a function call) yields a ValidationResult - { "valid", "code", "message", "severity" } - or a boolean; a failing rule of error severity is a failure, carrying the result's message or the rule's fallback.

Constant Description
DEFAULT_MESSAGE The message when neither the result nor the rule carries one.

.failures(component, evaluator)

  • component (Hash)
  • evaluator (Evaluator)

Returns (Array<Hash>) — failures as { code:, message:, severity: }

Poetry::Agent::A2UI::Evaluator

class · lib/poetry/agent/a2ui/evaluator.rb

Resolves dynamic values in a scope: a { "path" } binding reads the data model, a { "call", "args" } invokes a registered function with its arguments resolved first (a string argument with ${} blocks interpolates, an array resolves item by item), and @index reads the collection index the scope carries. Problems (an unknown function, a bad argument, a malformed expression) resolve to nil and reach the on_error callback.

Evaluator.new(surface, "/items/2").resolve({ "call" => "@index", "args" => { "offset" => 1 } }) # => 3

#argument(value)

Resolves a function argument: like {#resolve}, plus strings interpolate and arrays and plain objects resolve inside.

  • value (Object)

#call(name, args, resolved: false)

Calls a function by name.

  • name (String)
  • args (Hash, nil) — raw arguments (resolved here unless resolved:)
  • resolved (Boolean) — whether the arguments are already resolved

#evaluate(node)

Evaluates a parsed expression node.

  • node (Array)

#index

Returns (Integer, nil) — the collection index the scope carries

#initialize(surface, scope = nil, on_error: nil)

  • surface (Surface)
  • scope (String, nil)
  • on_error (#call, nil) — receives each problem's message

Returns (Evaluator) — a new instance of Evaluator

#interpolate(text)

Interpolates a formatString template.

  • text (String)

#resolve(value)

Resolves a component property: bindings and calls resolve, everything else is a literal.

  • value (Object)

#scope

Returns (String, nil) — the collection-item pointer in effect

#stringify(value)

The string a value displays as (the spec's conversion rules: nil is empty, containers are JSON, whole floats drop their fraction).

  • value (Object)

#surface

Poetry::Agent::A2UI::Expression

module · lib/poetry/agent/a2ui/expression.rb

The formatString grammar: literal text with ${...} blocks, each block a data path, a literal, or a function call with named arguments whose values are expressions again (a bare argument is value); \${ is a literal ${. Parsing yields a plain tree the {Evaluator} walks:

[:template, nodes] the whole string [:text, "literal text"] [:path, "/absolute"] or a relative path [:literal, 12] / [:literal, "quoted"] / [:literal, true] [:call, "formatDate", { "value" => node, "format" => node }]

Expression.parse("Hi ${/user/name}, ${formatNumber(value: ${/n}, decimals: 1)}")
Constant Description
IDENT Identifier: function names, argument names, relative path heads.
KEYWORDS Keyword literals.
MAX_DEPTH Nesting depth beyond which an expression is refused.
NUMBER A number literal not followed by a path or identifier character.

.dynamic?(text)

  • text (String)

Returns (Boolean) — whether the text carries an interpolation block

.parse(text)

  • text (String)

Returns (Array) — the [:template, nodes] tree

Poetry::Agent::A2UI::Expression::Parser

class · lib/poetry/agent/a2ui/expression.rb

The recursive-descent parser.

#initialize(source)

  • source (String)

Returns (Parser) — a new instance of Parser

#template

Returns (Array) — the [:template, nodes] tree

Poetry::Agent::A2UI::Expression::SyntaxError

class < StandardError · lib/poetry/agent/a2ui/expression.rb

A malformed expression.

Poetry::Agent::A2UI::Functions

class · lib/poetry/agent/a2ui/functions.rb

The renderer's function registry: named functions an agent may reference in a component's dynamic values and checks, each with the declaration a catalog document publishes (functions and $defs.anyFunction). {Functions.basic} holds the spec's basic catalog set - the validators, the formatters, the boolean combinators, openUrl - implemented from their descriptions; @index is the evaluator's own system function.

Functions.basic.call("pluralize", { "value" => 2, "one" => "item", "other" => "items" }, evaluator)
# => "items"
Constant Description
CALLERS The spec's execution boundaries: who may invoke a function.
CURRENCIES Currency symbols (and fraction digits) by ISO 4217 code; other codes render as a code prefix.
DATE_FIELDS Unicode TR35 date-pattern fields to strftime.
EMAIL The email shape the basic catalog names.

.basic

The basic catalog's functions.

.number(value)

  • value (Object)

Returns (Numeric, nil) — the value as a number, when it is one

.truthy?(value)

The boolean reading of a value: a ValidationResult by its valid, strings by content, nil and false as false.

  • value (Object)

#agent_callable?(name)

  • name (String)

Returns (Boolean) — whether an agent may invoke the function through callRendererFunction

#any_function

The catalog document's $defs.anyFunction.

#call(name, args, evaluator)

Calls a function with resolved arguments.

  • name (String)
  • args (Hash{String => Object})
  • evaluator (Evaluator)

#declared?(name)

  • name (String)

#define(name, description:, returns:, params: {}, required: [], activation: false, # rubocop:disable Metrics/ParameterLists callers: "rendererOnly", &impl)

Declares a function.

  • name (String)
  • description (String)
  • returns (String) — the spec's returnType
  • params (Hash{String => Hash}) — argument schemas by name
  • required (Array<String>) — required argument names
  • activation (Boolean) — whether the call needs a user activation
  • callers (String) — the execution boundary (rendererOnly - the default - keeps the function out of an agent's callRendererFunction; agentOnly / rendererOrAgent admit it)

Returns (Functions) — self

#initialize

Returns (Functions) — a new instance of Functions

#names

Returns (Array<String>) — the declared names

#schema

The catalog document's functions section.

Poetry::Agent::A2UI::Functions::Basic

module · lib/poetry/agent/a2ui/functions.rb

The basic catalog's set, from the spec's descriptions.

Constant Description
CHECKED The argument every validator checks.
DYNAMIC A parameter schema referencing one of the common dynamic types.
PLURAL_CATEGORIES The CLDR plural categories pluralize selects among.

.install(registry)

  • registry (Functions)

.list

A boolean list argument (and, or).

Poetry::Agent::A2UI::Functions::Definition

class < Struct · lib/poetry/agent/a2ui/functions.rb

One declared function.

#activation

Returns the value of attribute activation

Returns (Object) — the current value of activation

#activation=(value)

Sets the attribute activation

  • value (Object) — the value to set the attribute activation to.

Returns (Object) — the newly set value

#callers

Returns the value of attribute callers

Returns (Object) — the current value of callers

#callers=(value)

Sets the attribute callers

  • value (Object) — the value to set the attribute callers to.

Returns (Object) — the newly set value

#description

Returns the value of attribute description

Returns (Object) — the current value of description

#description=(value)

Sets the attribute description

  • value (Object) — the value to set the attribute description to.

Returns (Object) — the newly set value

#impl

Returns the value of attribute impl

Returns (Object) — the current value of impl

#impl=(value)

Sets the attribute impl

  • value (Object) — the value to set the attribute impl to.

Returns (Object) — the newly set value

#name

Returns the value of attribute name

Returns (Object) — the current value of name

#name=(value)

Sets the attribute name

  • value (Object) — the value to set the attribute name to.

Returns (Object) — the newly set value

#params

Returns the value of attribute params

Returns (Object) — the current value of params

#params=(value)

Sets the attribute params

  • value (Object) — the value to set the attribute params to.

Returns (Object) — the newly set value

#required

Returns the value of attribute required

Returns (Object) — the current value of required

#required=(value)

Sets the attribute required

  • value (Object) — the value to set the attribute required to.

Returns (Object) — the newly set value

#returns

Returns the value of attribute returns

Returns (Object) — the current value of returns

#returns=(value)

Sets the attribute returns

  • value (Object) — the value to set the attribute returns to.

Returns (Object) — the newly set value

Poetry::Agent::A2UI::Functions::Error

class < StandardError · lib/poetry/agent/a2ui/functions.rb

A missing function or a bad argument list.

Poetry::Agent::A2UI::Markdown

module · lib/poetry/agent/a2ui/markdown.rb

The Markdown subset an A2UI Text component needs, rendered without a Markdown dependency: ATX headings, paragraphs, bullet lists, emphasis, strong, inline code, and links. Input is escaped first, so agent text never reaches the page as markup.

Markdown.render("## Login\nWelcome **back**") # => "<h2>Login</h2><p>Welcome <strong>back</strong></p>"

.render(text)

  • text (String)

Returns (String) — HTML (unmarked; wrap in html_safe at the render site)

.strip(text)

Strips the same markers instead of rendering them - the fallback the basic catalog guide asks for when markup is unwanted.

  • text (String)

Returns (String) — plain text

Poetry::Agent::A2UI::Pointer

module · lib/poetry/agent/a2ui/pointer.rb

JSON Pointer (RFC 6901) over plain Ruby documents, with A2UI's two extensions: relative paths (no leading slash) resolve against a collection scope, and an upsert writes through missing objects.

Pointer.get({ "user" => { "name" => "Ada" } }, "/user/name") # => "Ada"
Pointer.absolute("name", "/users/1")                       # => "/users/1/name"

.absolute(path, scope = nil)

Resolves a path against a scope: absolute paths pass through, relative ones append to the scope (the root when no scope).

  • path (String)
  • scope (String, nil) — the collection-item pointer in effect

Returns (String) — an absolute pointer

.build(parts)

Joins tokens back into a pointer.

  • parts (Array<String>)

.get(document, path)

Reads the value at a pointer; nil for any missing step.

  • document (Object)
  • path (String)

.tokens(path)

Splits a pointer into unescaped reference tokens; "" and "/" both name the whole document.

  • path (String)

.upsert(document, path, value)

Writes a value at a pointer (A2UI upsert semantics): missing objects are created along the way, a nil value removes the key, and the whole-document pointer replaces the document.

  • document (Hash, Array, nil)
  • path (String)
  • value (Object, nil)

Returns (Object) — the updated document

Poetry::Agent::A2UI::Renderer

class · lib/poetry/agent/a2ui/renderer.rb

Renders one {Surface} to HTML through the host's view context, dispatching each component to the surface's catalog binding. The surface becomes a form when an action_url is given: bound inputs are named by their absolute data-model pointer and every agent action is a submit button, so a user action posts the surface's current inputs plus the source component - the spec's "inputs sync only on an action" contract, in Hotwire's native shape. The wrapper carries the surface's version for the versioned Turbo Stream replace.

Rendering never raises for an agent's mistake: an unknown component, a dangling reference, a component the library refuses to build, or an unsupported function renders nothing and lands in {#warnings}.

renderer = Renderer.new(surface, view: view_context, action_url: "/a2ui/action")
html = renderer.call
renderer.warnings # => []
Constant Description
ACTION_PARAM The parameter carrying the source component of the action.
COMPONENT_ERRORS The errors an agent-authored component may provoke in the library.
ELEMENT_PREFIX The DOM id prefix of a rendered surface.
EVALUATE_ACTIONS The form events that re-run the checks.
SURFACE_CONTROLLER The Stimulus controller that runs a surface's checks as the user types.
SURFACE_PARAM The parameter carrying the surface id.
VALUES_PARAM The parameter carrying the bound values, keyed by absolute pointer.

.element_id(surface_or_id)

  • surface_or_id (Surface, String)

Returns (String) — the DOM id of the surface's wrapper

#action_url

#aria_label(component, scope = nil)

  • component (Hash)
  • scope (String, nil)

Returns (String, nil) — the component's accessibility label

#blank

Returns (String) — an empty html_safe string

#call

Returns (String) — the surface's HTML (html_safe)

#call_function(name, args, scope = nil)

Calls a catalog function; a problem warns and returns nil.

  • name (String)
  • args (Hash, nil)
  • scope (String, nil)

#component(klass, attributes = {}, suffix: nil, **keywords, &)

Builds and renders a library component. Every instance gets a render-stable key: (the surface, the component, its scope, and a suffix for repeated instances), so Turbo morph pairs the same logical element across updates and local state survives.

  • klass (Class) — the component class
  • attributes (Hash) — constructor keywords
  • suffix (String, nil) — distinguishes several instances of one class for one component
  • keywords (Hash) — constructor keywords given keyword-style (merged into attributes)

#control_id(component, scope = nil)

  • component (Hash)
  • scope (String, nil)

Returns (String) — a DOM id for the component's control

#current_key

Returns (String, nil) — the key of the component being rendered (id, or id@scope)

#error_for(component, scope = nil)

  • component (Hash)
  • scope (String, nil)

Returns (String, nil) — the first check failure message for the component

#errors

Returns (Hash{String => Array<Hash>}) — check failures by component key (see {Surface#failures})

#initialize(surface, view:, action_url: nil, html: {}, errors: {})

  • surface (Surface)
  • view (Object) — an ActionView context (view_context)
  • action_url (String, nil) — where actions post; nil renders a plain container
  • html (Hash) — extra attributes for the wrapper (class: etc.)
  • errors (Hash{String => Array<Hash>}) — check failures to show, by component key (a rejected action's errors)

Returns (Renderer) — a new instance of Renderer

#input_name(path, scope = nil)

  • path (String) — a bound pointer
  • scope (String, nil)

Returns (String) — the input's form name

#markdown(text)

  • text (String)

Returns (String) — the Markdown subset rendered (html_safe)

#render_children(reference, scope = nil)

Renders a child reference (an id, an id list, or a template).

  • reference (String, Array<String>, Hash, nil)
  • scope (String, nil)

#render_component(component_id, scope = nil)

  • component_id (String)
  • scope (String, nil)

Returns (String) — the component's HTML (empty when it cannot render)

#resolve(value, scope = nil)

  • value (Object)
  • scope (String, nil)

Returns (Object, nil) — the resolved dynamic value

#stable_key(suffix = nil)

  • suffix (String, nil)

Returns (String) — the render-stable key of the component being rendered

#submit_attributes(component, scope = nil)

The attributes that make a button an agent action.

  • component (Hash)
  • scope (String, nil)

#surface

#text(value, scope = nil)

The display string of a dynamic value; a function problem warns.

  • value (Object)
  • scope (String, nil)

#view

Returns (Object) — the view context

#warn(message)

Records a problem and renders nothing for it.

  • message (String)

Returns (String) — an empty html_safe string

#warnings

Returns (Array<String>) — what could not be rendered, in render order

Poetry::Agent::A2UI::Session

class · lib/poetry/agent/a2ui/session.rb

The renderer-side consumer of the A2UI envelope: applies createSurface, updateComponents, updateDataModel, and deleteSurface to a set of {Surface}s, answers what it cannot honor with renderer-to-agent error messages, and turns a submitted form into the spec's action message.

session = Session.new
session.apply_all(messages)  # => ["login"]
session.surfaces["login"].data
session.errors               # => [] or renderer-to-agent error messages
Constant Description
MESSAGE_KEYS Every message carries exactly one of these keys.

#action(surface_id:, source:, values: {}, timestamp: Time.now.utc)

Turns a submitted surface form into the agent's action message: bound input values are written to the data model first (two-way binding syncs on an action), then the source component's event context resolves against the updated model. Returns nil when the source has no agent event (a local action, or an unknown component), and an invalid action - no message, errors by component key - when a checks rule fails.

  • surface_id (String)
  • source (String) — the submit button's value (id or id@scope)
  • values (Hash{String => Object}) — submitted values by absolute pointer
  • timestamp (Time)

#apply(message)

Applies one envelope message. Returns the ids of the surfaces it changed (a deleted surface counts); problems are recorded in {#errors} and return no ids.

  • message (Hash)

#apply_activity(content)

Applies the A2UI messages an AG-UI a2ui-surface activity carries (an a2ui_operations, messages, or operations list, or one bare message).

  • content (Hash, Array)

Returns (Array<String>) — the changed surface ids

#apply_all(messages)

  • messages (Array<Hash>)

Returns (Array<String>) — the changed surface ids, deduplicated

#catalog_for(catalog_id)

  • catalog_id (String, nil)

Returns (Object) — the catalog binding for an id (the default when unknown)

#catalogs

Returns (Hash{String => Object}) — catalog bindings by catalog id

#deleted

Returns (Array<String>) — ids of deleted surfaces, in order

#errors

Returns (Array<Hash>) — renderer-to-agent error messages, in order

#initialize(catalogs: A2UI.catalogs, default_catalog: nil)

  • catalogs (Hash{String => Object}) — catalog bindings by id
  • default_catalog (Object, nil) — the binding for unknown catalog ids (Poetry's own when nil)

Returns (Session) — a new instance of Session

#responses

Returns (Array<Hash>) — renderer-to-agent rendererFunctionResponse messages, in order

#surface(surface_id)

  • surface_id (String)

#surfaces

Returns (Hash{String => Surface}) — live surfaces by id

Poetry::Agent::A2UI::Session::Action

class < Struct · lib/poetry/agent/a2ui/session.rb

A user action, ready for the agent: the spec message plus the AG-UI placement (forwardedProps.a2uiAction.userAction).

#errors

Returns the value of attribute errors

Returns (Object) — the current value of errors

#errors=(value)

Sets the attribute errors

  • value (Object) — the value to set the attribute errors to.

Returns (Object) — the newly set value

#forwarded_props

Returns (Hash) — the AG-UI forwardedProps carrying the action (and the data model when the surface asked for it); empty when invalid

#message

Returns the value of attribute message

Returns (Object) — the current value of message

#message=(value)

Sets the attribute message

  • value (Object) — the value to set the attribute message to.

Returns (Object) — the newly set value

#surface

Returns the value of attribute surface

Returns (Object) — the current value of surface

#surface=(value)

Sets the attribute surface

  • value (Object) — the value to set the attribute surface to.

Returns (Object) — the newly set value

#to_h

Returns (Hash, nil) — the { "version", "action" } renderer-to-agent message; nil when a check failed

#valid?

Returns (Boolean) — whether every check passed and the message exists

Poetry::Agent::A2UI::Streams

class · lib/poetry/agent/a2ui/streams.rb

Delivers a {Session}'s surfaces as Turbo Streams: a surface's first appearance appends into the container (when one is given), every later change is a versioned replace of its wrapper (vreplace, from the AG-UI relay: a stale version never overwrites a newer one), and a deletion removes it. The host renders each surface through the render callable (typically a {Renderer}).

streams = Streams.new(session: session, container: "surfaces",
                      render: ->(surface) { Renderer.new(surface, view: view_context).call })
response.stream.write(AGUI::TurboStream.sse(streams.apply(message)))

#apply(message)

Applies one message and returns the streams for what changed.

  • message (Hash)

Returns (String) — Turbo Stream HTML (empty when nothing changed)

#apply_all(messages)

  • messages (Array<Hash>)

Returns (String) — Turbo Stream HTML

#initialize(session:, render:, container: nil, morph: true)

  • session (Session)
  • render (#call)(surface) -> html
  • container (String, nil) — the DOM id new surfaces append into
  • morph (Boolean) — morph replaced surfaces (the default) so local state - typed text, a selected tab, an open dialog - survives an update; false swaps

Returns (Streams) — a new instance of Streams

#mark_seen(*ids)

Marks surfaces as already on the page (rendered server-side), so their next change replaces instead of appending.

  • ids (Array<String>)

#session

#stream_for(id)

  • id (String)

Returns (String) — the stream for one surface (remove, append, or vreplace)

#streams(ids)

The streams for a set of surface ids.

  • ids (Array<String>)

Poetry::Agent::A2UI::Surface

class · lib/poetry/agent/a2ui/surface.rb

One A2UI surface on the renderer side: its flat component list (an adjacency list keyed by id, root at the top), its data model, and a monotonic version the Turbo Stream delivery compares. A surface belongs to a catalog binding, which knows how each component references its children; the surface itself is catalog-agnostic beyond that.

surface = Surface.new(id: "card", catalog: Catalogs::Basic.new)
surface.update_components([{ "id" => "root", "component" => "Text", "text" => { "path" => "/name" } }])
surface.update_data("/name", "Ada")
surface.resolve({ "path" => "/name" }) # => "Ada"
Constant Description
NAME_PATTERN Component names are UAX #31 identifiers.
RESERVED_COMPONENT The reserved container the renderer instantiates on createSurface.

#binding?(value)

  • value (Object)

Returns (Boolean) — whether the value is a { "path" => ... } data binding

#catalog

Returns (Object) — the catalog binding (see {Catalogs::Basic})

#catalog_id

Returns (String, nil) — the catalog id the agent named

#component(component_id)

  • component_id (String)

#components

Returns (Hash{String => Hash}) — components by id

#data

Returns (Hash) — the data model

#expand(reference, scope = nil)

Expands a child reference into [id, scope] pairs: an id array keeps the scope, a template instantiates its component once per item of the bound array with the item's pointer as the scope.

  • reference (Array<String>, Hash, String, nil)
  • scope (String, nil)

#failures(on_error: nil)

Evaluates every rendered component's checks against the data model, keyed the way an action names its source (id, or id@scope inside a template).

  • on_error (#call, nil) — receives each function problem's message

Returns (Hash{String => Array<Hash>}) — failures by component key

#function_call?(value)

  • value (Object)

Returns (Boolean) — whether the value is a { "call" => ... } function call

#id

#initialize(id:, catalog:, catalog_id: nil, send_data_model: false, data: nil, components: [])

  • id (String)
  • catalog (Object) — the catalog binding
  • catalog_id (String, nil)
  • send_data_model (Boolean)
  • data (Hash, nil) — the initial data model
  • components (Array<Hash>) — the initial component list

Returns (Surface) — a new instance of Surface

#inputs

Bound input descriptors of the rendered tree, absolute paths only.

Returns (Array<Hash>){ path:, kind: } (kind: :string, :boolean, :number, :string_list)

#program

What a client-side evaluator needs to run the checks as the user types: every checked component's rules with its bindings made absolute for its scope, the bound inputs by absolute path with their kinds, and the data model for paths no input carries.

Returns (Hash){ "checks" => { key => { "kind", "rules" } }, "inputs" => { path => kind }, "model" => data }

#read(path, scope = nil)

Reads a bound path in a scope.

  • path (String)
  • scope (String, nil)

#resolve(value, scope = nil, on_error: nil)

Resolves a dynamic value in a scope: a { "path" => ... } binding reads the data model (relative paths against the scope), a { "call" => ... } function call runs through the catalog's functions (see {Evaluator}), anything else is a literal.

  • value (Object)
  • scope (String, nil) — the collection-item pointer in effect
  • on_error (#call, nil) — receives each function problem's message

#root

Returns (Hash, nil) — the top-level component

#send_data_model

Returns (Boolean) — whether actions carry the whole data model

#source_key(component, scope = nil)

  • component (Hash)
  • scope (String, nil)

Returns (String)id, or id@scope inside a template

#template?(value)

  • value (Object)

Returns (Boolean) — whether the value is a { "componentId", "path" } template

#text(value, scope = nil, on_error: nil)

The string a resolved value displays as (the spec's conversion rules: nil is empty, containers are JSON).

  • value (Object)
  • scope (String, nil)
  • on_error (#call, nil) — receives each function problem's message

#to_h

Returns (Hash) — a JSON-ready snapshot

#update_components(list)

Upserts components by id and validates the result. Returns the validation errors (each { code:, path:, message: }); a dangling child reference is not one - streaming delivers children later.

  • list (Array<Hash>)

#update_data(path, value)

Applies an updateDataModel (upsert; nil removes; the root pointer replaces the whole model).

  • path (String, nil)
  • value (Object, nil)

#version

Returns (Integer) — bumps on every applied change

#walk(&)

Walks the rendered tree depth-first from the root, yielding each [component, scope] in render order (templates instantiate once per item; a cycle guard keeps the walk finite).

Poetry::Agent::AGUI

module · lib/poetry/agent/agui.rb

The AG-UI surface: a Rails-side CLIENT of the Agent-User Interaction protocol. An agent backend (any AG-UI integration, or a Ruby server) streams events - text deltas, tool calls, state, activities, run lifecycle, interrupts - and this module turns that stream into server-rendered chat frames a Hotwire page updates through Turbo Streams, the same pipeline the chat replay rig proves.

The pieces, each usable alone:

  • {SSE} parses text/event-stream chunks into event hashes.
  • {Client} POSTs a run to an AG-UI endpoint and yields its events.
  • {RunInput} builds the RunAgentInput wire hash, and

{.tool_descriptor} advertises a rendered component's declared tools as frontend-defined tools the browser executes.

  • {Transcript} folds events into messages (Chat-shaped parts),

shared state (JSON Patch), activities, the run status, pending client tools, and interrupts.

  • {Relay} renders each change as a versioned Turbo Stream through a

host-supplied row renderer, plus the client-tool bridge element the poetry--agent--agui-client-tool controller executes.

Nothing here calls a model: the agent is whatever the host points the client at.

Constant Description
EVENT_TYPES The AG-UI event types this transcript understands (the wire strings; deprecated THINKING_* aliases included).

.field(hash, name)

Reads a wire field from an event or message that may arrive camelCased (the protocol) or snake_cased (a Ruby producer).

  • hash (Hash)
  • name (String) — the camelCase name

.tool_descriptor(instance, definition)

The frontend-defined tool descriptor for one of a rendered component's declared tools: the MCP Tool shape the registry projects, renamed to AG-UI's parameters and prefixed with the instance name exactly as the WebMCP registrar registers it, so a call the agent makes is executable in the browser by name.

  • instance (String) — the webmcp: instance name
  • definition (Hash) — one entry of Component#webmcp_tools

Returns (Hash){ "name", "description", "parameters" }

Poetry::Agent::AGUI.tool_descriptor("sections", tabs.webmcp_tools.first)
# => { "name" => "poetry.sections.set_value", "description" => "...", "parameters" => {...} }

Poetry::Agent::AGUI::Client

class · lib/poetry/agent/agui/client.rb

The HTTP client: POSTs a RunAgentInput to an AG-UI endpoint and yields the streamed events as they arrive (stdlib Net::HTTP, text/event-stream). One call is one run; the multi-run model (client tools, interrupts) is the caller's loop over {Transcript}.

#initialize(url:, headers: {}, open_timeout: 10, read_timeout: 120)

  • url (String) — the agent's run endpoint
  • headers (Hash{String => String}) — extra request headers (auth)
  • open_timeout (Numeric) — seconds
  • read_timeout (Numeric) — seconds between chunks

Returns (Client) — a new instance of Client

#run(input, &)

Runs the agent and yields every event.

  • input (Hash) — the wire hash ({RunInput.build})

Returns (SSE::Parser) — the parser (its errors list any unreadable lines)

client.run(input) { |event| transcript.apply(event) }

Poetry::Agent::AGUI::Client::Error

class < Poetry::Core::Error · lib/poetry/agent/agui/client.rb

Raised for a non-success HTTP status.

#initialize(message, status:)

Returns (Error) — a new instance of Error

#status

Poetry::Agent::AGUI::JsonPatch

module · lib/poetry/agent/agui/json_patch.rb

RFC 6902 JSON Patch over plain Ruby data (Hash / Array), with RFC 6901 JSON Pointer paths - what AG-UI's STATE_DELTA and ACTIVITY_DELTA carry. Applies atomically: the document is deep- copied first and the copy is returned, so a failing operation leaves the caller's document untouched.

.apply(document, operations)

Applies a patch and returns the patched copy.

  • document (Hash, Array)
  • operations (Array<Hash>) — RFC 6902 operations (string or symbol keys)

Returns (Hash, Array) — the new document

JsonPatch.apply({ "a" => 1 }, [{ "op" => "replace", "path" => "/a", "value" => 2 }])
# => { "a" => 2 }

.get(document, path)

Reads the value at a JSON Pointer.

  • document (Hash, Array)
  • path (String) — RFC 6901 pointer ("" is the whole document)

Poetry::Agent::AGUI::JsonPatch::Error

class < Poetry::Core::Error · lib/poetry/agent/agui/json_patch.rb

Raised for an operation the document cannot take (an unknown op, a missing path, a failed test).

Poetry::Agent::AGUI::Relay

class · lib/poetry/agent/agui/relay.rb

Turns transcript changes into Turbo Streams. The host supplies the row renderer (its own partial or component: a message and its version in, the row's HTML out - the row must carry data-version), the target id scheme, and the container new rows append to. The relay stays view-free.

Client tools ride the same channel: when a run ends with tool calls the browser must execute, {#client_tool_streams} appends one bridge element per call; the poetry--agent--agui-client-tool controller executes it through the registrar and POSTs the result to the continue URL, whose response streams the next run.

relay = Poetry::Agent::AGUI::Relay.new(transcript: transcript, container: "chat-messages",
                                       render: ->(message, version) { render_row(message, version) })
client.run(input) { |event| relay.apply(event).each { |stream| write(TurboStream.sse(stream)) } }
Constant Description
CLIENT_TOOL_CONTROLLER The bridge controller's identifier.

#apply(event)

Applies an event and answers the Turbo Streams it produced: an append for a message's first appearance (when a container is set), then the update action for every change.

  • event (Hash)

#client_tool_streams(continue_url:, container: @container)

Bridge elements for every pending client tool call.

  • continue_url (String) — where the browser POSTs { toolCallId, name, content, error }
  • container (String) — the element the bridge elements append to

#initialize(transcript:, render:, container: nil, target: ->(message) { "row-#{message.id}" }, # rubocop:disable Metrics/ParameterLists action: "vreplace", append_render: nil, morph: false)

  • transcript (Transcript)
  • render (#call)(message, version) -> String the row HTML
  • container (String, nil) — the id new rows append to (nil: replace only)
  • target (#call)(message) -> String the row's element id
  • action (String) — the stream action for updates (vreplace by default)
  • append_render (#call, nil)(message, version) -> String the HTML a first appearance appends - the row inside its list wrapper (a scroller item); defaults to render

Returns (Relay) — a new instance of Relay

#mark_seen(*ids)

Marks message ids the page already renders, so their next change is an update rather than an append (server-rendered history).

  • ids (Array<String>)

#stream_for(id)

The stream for one message id (nil when the message is unknown).

  • id (String)

#transcript

Poetry::Agent::AGUI::RunInput

module · lib/poetry/agent/agui/run_input.rb

Builds the RunAgentInput wire hash an AG-UI agent accepts: camelCased keys, messages in the protocol's shapes, the frontend-defined tools, context entries, state, forwarded props, and the resume entries that answer interrupts.

.build( # rubocop:disable Metrics/ParameterLists -- one keyword per RunAgentInput field thread_id:, messages:, run_id: SecureRandom.uuid, tools: [], context: [], state: {}, forwarded_props: {}, parent_run_id: nil, resume: nil)

  • thread_id (String) — the conversation thread
  • messages (Array<Hash>) — protocol messages (id, role, content, ...)
  • run_id (String) — defaults to a fresh UUID
  • tools (Array<Hash>) — frontend-defined tools ({AGUI.tool_descriptor})
  • context (Array<Hash>){ "description", "value" } entries
  • state (Hash) — the shared state to send
  • forwarded_props (Hash) — integration-specific props
  • parent_run_id (String, nil) — the run this one branches from
  • resume (Array<Hash>, nil){ "interruptId", "status", "payload" } answers

Returns (Hash) — the wire hash (string keys)

RunInput.build(thread_id: "t1", messages: [RunInput.user_message("hi")],
               tools: [Poetry::Agent::AGUI.tool_descriptor("sections", definition)])

.resume_entry(interrupt_id, status: nil, payload: nil)

A resume entry answering an interrupt.

  • interrupt_id (String)
  • status (String, nil) — e.g. "approved", "rejected"
  • payload (Object, nil)

.tool_message(tool_call_id, content, error: nil, id: SecureRandom.uuid)

A tool-result message answering a tool call the browser ran.

  • tool_call_id (String)
  • content (String) — the result as text (JSON for structured results)
  • error (String, nil) — set when the tool failed
  • id (String)

.user_message(content, id: SecureRandom.uuid)

A user message.

  • content (String)
  • id (String)

Poetry::Agent::AGUI::SSE

module · lib/poetry/agent/agui/sse.rb

A text/event-stream parser for AG-UI: every event is a JSON object on one or more data: lines, terminated by a blank line. Incremental (feed chunks as they arrive) and tolerant of comments, event: / id: / retry: fields, and CRLF.

.parse(source, &block)

Parses a complete stream (a String or anything responding to each with chunks) and yields every event.

  • source (String, #each)

Returns (Array<Hash>) — every event, when no block is given

Poetry::Agent::AGUI::SSE.parse("data: {\"type\":\"RUN_STARTED\"}\n\n") # => [{ "type" => "RUN_STARTED" }]

Poetry::Agent::AGUI::SSE::Parser

class · lib/poetry/agent/agui/sse.rb

The incremental parser.

#errors

Lines that carried data the parser could not read as JSON.

#feed(chunk, &)

Feeds a chunk and yields each completed event.

  • chunk (String)

#finish(&)

Flushes a trailing event that lacked its blank line.

#initialize

Returns (Parser) — a new instance of Parser

Poetry::Agent::AGUI::Transcript

class · lib/poetry/agent/agui/transcript.rb

Folds an AG-UI event stream into what a chat page renders: the messages in order, each assistant message as Chat-shaped parts ({kind: :text, text:}, {kind: :reasoning, text:}, {kind: :tool, name:, input:, output:, state:, tool_call_id:}), the shared state (snapshots and JSON Patch deltas), activities, the run's status, its interrupts, and the tool calls the browser must execute before the next run.

Every change bumps {#version}, and {#apply} answers the ids of the messages it touched, so a relay re-renders exactly those rows with a monotonic version the page's versioned replace honors.

#activities

Activities by message id: { "type" => ..., "content" => ... }.

#apply(event)

Applies one event.

  • event (Hash) — an AG-UI event (camelCase or snake_case keys)

Returns (Array<String>) — the ids of the messages this event changed

#apply_all(events)

Applies every event of a stream.

  • events (#each) — event hashes

#client_tools

The frontend-defined tool names the browser executes.

#custom_events

RAW and CUSTOM events, in order.

#ended?

Returns (Boolean) — the run ended (finished, interrupted, or errored)

#error

The run error, if any: { message:, code: }.

#frame(id)

The render-ready frame of one message.

  • id (String)

Returns (Hash){ parts:, version: }

#initialize(client_tools: [])

  • client_tools (Array<String>) — names of tools the browser executes

Returns (Transcript) — a new instance of Transcript

#interrupted?

#interrupts

The open interrupts (string-keyed hashes as on the wire).

#message(id)

  • id (String)

#messages

The messages in arrival order.

#messages_for_input

The messages as the next run's RunAgentInput.messages: user and assistant messages (assistant tool calls in the protocol's toolCalls shape) and a tool message for every finished tool call; reasoning and activities stay client-side, as the protocol says.

#pending_client_tools

Tool calls to client tools awaiting execution: { tool_call_id:, name:, input:, message_id: }.

#resolve_client_tool(tool_call_id, content, error: nil)

Marks a client tool call as executed and records its result, so the next run's input carries the tool message.

  • tool_call_id (String)
  • content (Object) — the result (a string, or data serialized as JSON)
  • error (String, nil)

Returns (String, nil) — the id of the message that changed

#run

The run: { thread_id:, run_id:, status:, interrupts:, error:, result: }; status is :idle, :running, :finished, :interrupted, or :error.

#state

The shared state after the last snapshot / delta.

#unknown_events

Event types this transcript did not understand.

#version

A monotonic clock over every applied change.

Poetry::Agent::AGUI::Transcript::Message

class < Struct · lib/poetry/agent/agui/transcript.rb

One message. role is the protocol's ("user", "assistant", "tool", "activity", ...); parts is the render-ready list.

#id

Returns the value of attribute id

Returns (Object) — the current value of id

#id=(value)

Sets the attribute id

  • value (Object) — the value to set the attribute id to.

Returns (Object) — the newly set value

#parts

Returns the value of attribute parts

Returns (Object) — the current value of parts

#parts=(value)

Sets the attribute parts

  • value (Object) — the value to set the attribute parts to.

Returns (Object) — the newly set value

#role

Returns the value of attribute role

Returns (Object) — the current value of role

#role=(value)

Sets the attribute role

  • value (Object) — the value to set the attribute role to.

Returns (Object) — the newly set value

#version

Returns the value of attribute version

Returns (Object) — the current value of version

#version=(value)

Sets the attribute version

  • value (Object) — the value to set the attribute version to.

Returns (Object) — the newly set value

Poetry::Agent::AGUI::TurboStream

module · lib/poetry/agent/agui/turbo_stream.rb

Turbo Stream builders for the relay: plain strings, no view context needed. vreplace is the versioned replace the runtime installs on Turbo (registerPoetryAgent) - it applies a frame only when its data-version is newer than the row's, so an out-of-order delivery can never paint an older state over a newer one.

.append(target, html)

  • target (String)
  • html (String)

.build(action, target, html = nil, method: nil)

  • action (String) — a Turbo Stream action (append, replace, vreplace, remove, ...)
  • target (String) — the target element id
  • html (String, nil) — the template content (already rendered, trusted)
  • method (String, nil) — Turbo's method attribute ("morph" morphs instead of swapping)

.remove(target)

  • target (String)

.replace(target, html)

  • target (String)
  • html (String)

.sse(html)

One SSE frame carrying the streams (newlines folded, as Turbo's stream source expects one data: line).

  • html (String)

.vreplace(target, html, morph: false)

  • target (String)
  • html (String)
  • morph (Boolean) — morph the target (Turbo's idiomorph) instead of swapping it, so local state - typed text, a selected tab, an open dialog - survives the update

Poetry::Agent::Engine

class < Rails::Engine · lib/poetry/agent/engine.rb

The Rails engine: merges the WebMCP controllers manifest into poetry-core's catalog (so webmcp: roots validate at render), serves the runtime JavaScript through the importmap-first channel, and mounts the origin-trial middleware. Loading the gem is the only integration step; the host imports @poetry/agent beside @poetry/controllers.

Poetry::Agent::MCP

module · lib/poetry/agent/mcp/server.rb

The MCP server projecting the component contract over Model Context Protocol so an agent in Claude Code / Cursor queries the LIVE registry and runs the linter as a tool. Thin - it projects the surfaces already built (Registry + LlmsText + Check), never a second source. Read-only, progressive-disclosure (brief|detailed|full), and verdict-returning (check returns per-finding pass/fail).

Two transports, one server: newline-delimited JSON-RPC 2.0 over stdio (the poetry-agent exe; own the supply chain - no MCP SDK dependency) and POST JSON-RPC over HTTP ({HTTP}, for the same-origin /mcp mount in-page bridges read). {Server#handle} is a pure request->response function (testable without either transport); {Server#serve} is the stdio loop; {Bundled} is the one assembly both transports share.

v1 is the read/verify surface. The heavier roadmap - verify_screen running the eval gate array, component:// artifact resources, tag browsing, SSE streaming - is maturity-gated and NOT in this cut.

Constant Description
PROTOCOL_VERSION The MCP protocol revision this server negotiates.
SERVER_INFO The serverInfo payload returned by the initialize handshake.
TOOLS The tool roster the server advertises (tools/list): MCP Tool-shaped definitions, read-only by construction.