YouBothAgent▾
You — Business rules and flows you own. Read these yourself.
Both — Know the idea; your agent follows the details.
Agent — Conventions and references your agent follows. Look up as needed.














CLI Reference▾
AkanJS Reference▾Agent namespace is that UI. One <Agent.Chat /> in a layout is the whole integration; the other members narrow it or show what it sees.import { Agent } from "akanjs/ui";

st.tool, usually the handler its button calls.Agent.Chat and Agent.Zone each build one.navigate, goBack, readScreen, readState, highlight.st.expose or st.useState for the agent to read.Agent.Context, Agent.Section, Agent.StateKey, Agent.Tool, Agent.Transcript: the dock's pieces, for an inspector of your own.Agent.Zone or AgentProvider the chat joins that session, so these belong to whoever built it.Agent.Guides layers on top of it.runAgentTurn; httpRunner({ url }) posts elsewhere.at estimated tokens or buffer short of the known window. { at: 0 } turns it off.true gives all five built-ins, false none, an array only those named. askUser always stays.{ storage: "local" } or SessionHistory moves it.reveal) and a pointer presses it (cursor). false turns both off.open alone draws no close button.false draws no launcher, for an app that opens the panel from a control of its own.false gives the chord back, for a shell that already uses it.className reaches both.chrome={false} drops the whole bar, leaving /new to clear.?prompt= value to prefill without sending it.null falls back to the built-in reader for images and text.@ menu sources, each with a search. mentions draws pointers as names, on when sources exist.persist it ends with the page.attach, voice, reference, runner, onCompact and onOpenChange carry functions, which cannot cross the RSC boundary from a server layout. Wrap the chat in ui/ the way apps/akan/ui/DocsAgentChat.tsx does with useSpeech() from @libs/util/webkit.open alone stays server-safe. Without onOpenChange no function is passed, so a server component can still render it.lazy(…, { ssr: false }) boundary, so its chunk loads once the page has hydrated. That is normal.lib/option.ts. option.setLlm({ apiKey, model, host }) and option.setAgentAccess(SignedIn) configure it, never environment variables.data when the provider cannot reach a url. The provider fetches an attach result's url itself. Answering both sends the bytes to the model and keeps the address for the thumbnail.Agent.Chat inside binds to it automatically, so two zones on one screen run two conversations side by side, each seeing only its own subtree.data-agent-zone; characters outside A-Za-z0-9_- become -.div that carries data-agent-zone.Agent.Guide: the root agent reads it too, a sibling zone never.st.use subscriptions and guides mounted inside belong to this zone's session and to the root agent both.<id>.<name>. Instructions that name a tool must carry the prefix; a bare name is a tool that does not exist, and the model spends a turn on Unknown tool. Build the name from the id, and check the published list with Agent.Context's Assemble.builtins={["readScreen", "readState"]} takes navigate, goBack and highlight away rather than discouraging them, so no prompt can talk the model past it.persist is keyed by the zone's scope path, so two zones never share a transcript._layout.tsx or a page, and its text joins every turn's instructions while that subtree is mounted. It draws nothing.l() rule does not apply.page() nor pageConfig has an instructions field, and *.abstract.md is never served to agents.persist does, as a mounted leaf instead of a prop, and draws nothing.persist. A function cannot cross the server/client boundary as a prop, so passing persist to whoever builds the session makes every ancestor up to it a client component. With this leaf as the only client module, the zone and its chat stay in a server component.Agent.Zone or AgentProvider, as in <Agent.Zone id="thread"><ThreadHistory … /></Agent.Zone>. The root Agent.Chat hands no session down, so use its persist there.session.setHistory yourself; that takes the slot, so a later unmount here leaves it alone.section takes to read it anyway. Required.div.[skipped: <label>] stands in its place, so asked about the footer it says it did not read one instead of saying there is none. section: "<label>" reads it on request.st.tool inside is published as before, and highlight still reaches a control in here.<footer data-agent-skip="site footer">.<id>.<name>, nested scopes joined with dots.Agent.Zone opens its own with kind="zone".Agent.Scope when a repeated subtree needs distinct tool names but shares the screen's one agent. Agent.Zone wraps a scope and adds a conversation of its own.Agent.Dock shows it. Mount it next to the chat during development:Agent.Dock and Agent.Context render nothing when AKAN_PUBLIC_ENV=main, so leaving them mounted costs a visitor nothing.Agent.Section, Agent.StateKey, Agent.Tool or Agent.Transcript has to hide itself in production.open expands Tools. Transcript always starts expanded; the other sections start folded.bridge supplies the state keys, surface the tools, open expands Tools.<details> group with a count beside its title. The dock draws five.AgentChat replaces the whole panel.AgentLauncherAgentBubbleAgentStepsAgentComposerAgentApprovalAgentQuestionAgentQueuedAgentMenuAgentMarkdownAgentToolCardAgentCode