m4Mindset docs

Demos / Your own UI · Halcyon

Halcyon

Your own UI: the UI-less client hands you the conversation as data, so every pixel is yours. In a sleep app, the agent’s breathing widget arrives as data and drives the app’s own orb.

Halcyon has no Mindset chat window. The page talks to its agent through the UI-less client and draws everything itself, including a breathing orb that becomes the agent's timer widget.

Start a conversation with no UI

Create a conversation with the agent's handle and a session callback, then listen to its events.

src/demos/shared/useDemoAgent.ts
const conv = mod.createAgentConversation({
  agent: handle,
  getSession: async () => {
  …
unsub.current = conv.on((event) => {
  dispatch({ type: 'event', event })
  …

A widget arrives as data

With no renderer, a widget call is just an event carrying the agent's arguments.

src/demos/shared/transcript.ts
case 'widget-payload': {
  const kind = str(e.kind) || 'widget'
  …

Read the arguments

The page checks what the agent sent before using it.

src/demos/halcyon/breath.ts
const data = (item.doc as { data?: Record<string, unknown> } | null)?.data ?? {}
const minutes = toMinutes(data.minutes)
const pattern = toPattern(data.pattern)
if (minutes === null || pattern === null) return null
return { minutes, pattern }

The orb becomes the timer

When a new timer arrives, the orb starts breathing to it. Nothing else changes the orb.

src/demos/halcyon/useBreathing.ts
const req = timerRequest(newest)
if (req) {
  const startedAt = now()
  setSession({ itemId: newest.id, pattern: PATTERNS[req.pattern], minutes: req.minutes, startedAt })
  …
}