@cubos/agent-sdk-react 0.0.1142184 → 0.0.1142284

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -157,7 +157,7 @@ function useConversation(conversationId, options = {}) {
157
157
  const [isSending, setIsSending] = useState(false);
158
158
  const [hasOlder, setHasOlder] = useState(false);
159
159
  const [isLoadingOlder, setIsLoadingOlder] = useState(false);
160
- const [workspaceRevision, setWorkspaceRevision] = useState(0);
160
+ const [filesystemRevision, setFilesystemRevision] = useState(0);
161
161
  const onCreated = useRef2(options.onCreated);
162
162
  onCreated.current = options.onCreated;
163
163
  const adopting = useRef2(null);
@@ -282,8 +282,8 @@ function useConversation(conversationId, options = {}) {
282
282
  setStreamError(err instanceof Error ? err : new Error(String(err)));
283
283
  },
284
284
  onEvent: (event) => {
285
- if (event.type === "workspace_root")
286
- setWorkspaceRevision((n) => n + 1);
285
+ if (event.type === "filesystem_root")
286
+ setFilesystemRevision((n) => n + 1);
287
287
  },
288
288
  onToolActivity: (activity2) => {
289
289
  setToolActivity((prev) => mergeToolActivities([...prev, activity2]));
@@ -542,19 +542,19 @@ function useConversation(conversationId, options = {}) {
542
542
  return;
543
543
  const id = await target();
544
544
  await client.writeFiles(id, files);
545
- setWorkspaceRevision((n) => n + 1);
545
+ setFilesystemRevision((n) => n + 1);
546
546
  }, [client, target]);
547
547
  const deleteFile = useCallback(async (path) => {
548
548
  if (conversationId === null)
549
549
  return;
550
550
  await client.deleteFile(conversationId, path);
551
- setWorkspaceRevision((n) => n + 1);
551
+ setFilesystemRevision((n) => n + 1);
552
552
  }, [client, conversationId]);
553
553
  const moveFile = useCallback(async (from, to) => {
554
554
  if (conversationId === null)
555
555
  return;
556
556
  await client.moveFile(conversationId, from, to);
557
- setWorkspaceRevision((n) => n + 1);
557
+ setFilesystemRevision((n) => n + 1);
558
558
  }, [client, conversationId]);
559
559
  const steer = useCallback(async (content) => {
560
560
  const text = content.trim();
@@ -598,7 +598,7 @@ function useConversation(conversationId, options = {}) {
598
598
  isSending,
599
599
  renderMessage,
600
600
  syncClientTools,
601
- workspaceRevision,
601
+ filesystemRevision,
602
602
  listFiles,
603
603
  readFile,
604
604
  writeFiles,
@@ -629,7 +629,7 @@ function useConversation(conversationId, options = {}) {
629
629
  isSending,
630
630
  renderMessage,
631
631
  syncClientTools,
632
- workspaceRevision,
632
+ filesystemRevision,
633
633
  listFiles,
634
634
  readFile,
635
635
  writeFiles,
@@ -892,5 +892,5 @@ export {
892
892
  AgentProvider
893
893
  };
894
894
 
895
- //# debugId=AE29A8EC1333D43864756E2164756E21
895
+ //# debugId=C38847D70E438CE564756E2164756E21
896
896
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -4,12 +4,12 @@
4
4
  "sourcesContent": [
5
5
  "// Turning an agent message's blocks into React nodes. `createElement`, never\n// JSX with a host element: this package renders no DOM of its own, so the same\n// code path works in React Native.\n\nimport type { Block, Message } from \"@cubos/agent-sdk\";\nimport { type ComponentType, createElement, Fragment, type ReactNode } from \"react\";\n\n/**\n * What draws one component, keyed by tag name — PascalCase, as the agent writes\n * it.\n *\n * Renderers only. What the *agent* is told about a component (its summary, the\n * prop schema, the example) lives in a component library the operator authors,\n * because those words go into the model's context verbatim and a browser must\n * not be the one writing them. Your app supplies the drawing half and the list\n * of library slugs it can draw; `useConversation` warns when the two disagree.\n *\n * Keep renderers here for components no enabled library offers any more — that\n * is what draws the components of older messages in the transcript.\n */\nexport type ComponentMap = Record<\n string,\n // biome-ignore lint/suspicious/noExplicitAny: props come off the wire, validated by the server against the library's `props_schema`; the component's own signature is what narrows them.\n ComponentType<any>\n>;\n\nexport interface RenderBlocksOptions {\n components?: ComponentMap;\n /** Draws the prose between components. Without it the markdown is rendered as\n * plain text — correct, just not formatted; the SDK ships no markdown parser\n * and a chat app already has one. */\n renderMarkdown?: (text: string) => ReactNode;\n /**\n * Wraps each rendered block, given the block it came from.\n *\n * The reason it takes the block and not just the node: laying blocks out\n * needs to know which are prose and which are components — two cards side by\n * side and the paragraph above them full width — and by the time a block is a\n * `ReactNode` that is gone. The SDK will not decide the layout (it renders no\n * DOM at all), so it hands back what the app needs to decide it.\n */\n wrapBlock?: (node: ReactNode, block: Block) => ReactNode;\n}\n\n/**\n * The message split into nodes: prose through `renderMarkdown`, each component\n * block through the component registered under its tag.\n *\n * Null when the message carries no blocks — a user message, or an older server.\n * That distinction is real, so it is left to the caller rather than papered over\n * with `content`: a user's own text usually shouldn't go through a markdown\n * renderer at all.\n *\n * A tag with no component renders nothing. It means the set changed after the\n * message was written, and showing raw `<Tag …/>` text would be worse than a\n * gap.\n */\nexport function renderBlocks(\n message: Message,\n options: RenderBlocksOptions = {},\n): ReactNode[] | null {\n if (message.blocks === undefined) return null;\n const out: ReactNode[] = [];\n for (const [index, block] of message.blocks.entries()) {\n const node = renderBlock(block, `${message.id}:${index}`, options);\n if (node !== null) out.push(node);\n }\n return out;\n}\n\nfunction renderBlock(block: Block, key: string, options: RenderBlocksOptions): ReactNode {\n if (block.type === \"markdown\") {\n if (block.text.trim() === \"\") return null;\n // Wrapped, because `renderMarkdown` may return a keyless element (or a bare\n // string) and this sits in an array.\n const node = options.renderMarkdown?.(block.text) ?? block.text;\n return createElement(Fragment, { key }, options.wrapBlock?.(node, block) ?? node);\n }\n\n const render = options.components?.[block.tag];\n if (!render) return null;\n // Unwrapped when nobody is wrapping: the node stays the component itself,\n // which is what a caller inspecting the output expects and what keeps this\n // free of a Fragment per block for the common case.\n //\n // `key` last, so a library declaring a prop called `key` can't take it over:\n // React would read the key off the props and two instances of that component\n // in one message would collide. The props schema is the operator's now, not\n // the app's, so the app can't avoid it at the source.\n if (options.wrapBlock === undefined) return createElement(render, { ...block.props, key });\n return createElement(\n Fragment,\n { key },\n options.wrapBlock(createElement(render, block.props), block),\n );\n}\n",
6
6
  "import type { AgentClient, ClientOptions } from \"@cubos/agent-sdk\";\nimport { createUserClient } from \"@cubos/agent-sdk\";\nimport { createContext, createElement, type ReactNode, useContext, useMemo, useRef } from \"react\";\n\nconst AgentContext = createContext<AgentClient | null>(null);\n\n/** An intersection, not `extends`: `ClientOptions` is a union (`getToken` or\n * `token`), and an interface cannot extend one. */\nexport type AgentProviderProps = ClientOptions & { children: ReactNode };\n\n/**\n * Holds one `AgentClient` for the tree below it.\n *\n * `baseUrl`, `tenant`, `fetch` and `timeoutMs` are identity keys: change one and\n * a new client is built, so every subscription below reconnects. That is right\n * when you switch backend or user, and a waste when it happens because the props\n * object was rebuilt for nothing — keep `fetch` stable if you pass it.\n *\n * The credential is exempt: both `getToken` and `token` reach the client through\n * a ref, so a fresh closure every render (the normal case for an inline arrow)\n * neither rebuilds the client nor pins a stale token.\n */\nexport function AgentProvider({ children, ...options }: AgentProviderProps) {\n const credentialRef = useRef(options);\n credentialRef.current = options;\n\n const client = useMemo(\n () =>\n createUserClient({\n baseUrl: options.baseUrl,\n tenant: options.tenant,\n fetch: options.fetch,\n timeoutMs: options.timeoutMs,\n getToken: (opts) => {\n const current = credentialRef.current;\n return \"getToken\" in current ? current.getToken(opts) : current.token;\n },\n }),\n [options.baseUrl, options.tenant, options.fetch, options.timeoutMs],\n );\n\n return createElement(AgentContext.Provider, { value: client }, children);\n}\n\n/** The client from the nearest `AgentProvider`. Throws outside one, because the\n * alternative is a component that silently never loads. */\nexport function useAgentClient(): AgentClient {\n const client = useContext(AgentContext);\n if (!client) {\n throw new Error(\n \"No AgentProvider found. Wrap your app in <AgentProvider baseUrl=… getToken=…>.\",\n );\n }\n return client;\n}\n",
7
- "import {\n type Activity,\n type Block,\n type ClientTool,\n type ClientToolsSession,\n type Conversation,\n type Message,\n mergeToolActivities,\n mergeVoiceMessages,\n type PlanSnapshot,\n type Todo,\n type ToolActivity,\n type WorkspaceEntry,\n} from \"@cubos/agent-sdk\";\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { type ComponentMap, renderBlocks } from \"./blocks.js\";\nimport { useAgentClient } from \"./context.js\";\n\nexport interface UseConversationOptions {\n /**\n * How many events a history page asks for. Counts events, not messages, so a\n * page of a tool-heavy turn can yield only a couple of bubbles — which is why\n * a UI should keep calling `loadOlder` until `hasOlder` is false rather than\n * assuming one page fills the viewport.\n */\n pageSize?: number;\n /** Which agent a conversation created by the first `send` talks to. Optional\n * when the token names exactly one. */\n agentSlug?: string;\n /**\n * Called once, with the conversation the first `send` created, when this hook\n * was given `null`. Point it at whatever owns the selection — `setState`, a\n * router navigation — and pass the id back on the next render.\n *\n * The messages already on screen survive that hand-off, so the user's first\n * message doesn't blink out while the conversation is adopted.\n *\n * **Awaited.** It runs after the conversation exists and before the message\n * is posted, which is the only window in which an app can set something up\n * that the very first turn depends on — declaring its own client tools, for\n * one. Returning a promise holds the send until it resolves; a throw fails\n * the send, which is the honest outcome for setup that did not happen.\n */\n onCreated?: (conversation: Conversation) => void | Promise<void>;\n /**\n * Functions the agent may call, keyed by tool name. Declared on the\n * conversation, then executed here as the agent calls them: the turn suspends\n * on the call and resumes with whatever the handler returns.\n *\n * They ride the subscription this hook already holds — no second stream.\n */\n clientTools?: Record<string, ClientTool<never, unknown>>;\n /**\n * Whether this hook declares `clientTools` on the conversation. Defaults to\n * true, which is what an app with a fixed tool set wants.\n *\n * Turn it off to declare the set yourself, with `setClientTools`, and pass\n * every handler the agent might ever call here. That splits two things this\n * hook otherwise ties together: *what can run* (the handlers, which must\n * cover any call that arrives) and *what the agent is told about* (the\n * declaration). An app whose tools depend on the open screen needs that\n * split — the declaration should change on navigation, while the handlers\n * must stay complete, or a call issued a moment earlier would find no\n * implementation.\n */\n declareClientTools?: boolean;\n /**\n * What your app currently has on screen — the open page, the selected record,\n * the filters in force. Free-form: prose or JSON, whatever the agent reads\n * best.\n *\n * Set it and forget it — update it as often as your screen moves. Nothing is\n * sent while nobody is listening: the value is held and flushed at the two\n * moments the agent can actually read it, just before a message and on any\n * change while a turn is running. A user browsing eleven screens before typing\n * anything costs one request, and the agent is told once.\n *\n * Undefined leaves it alone; `null` clears it.\n */\n context?: string | null;\n /**\n * What draws each component, keyed by tag name (PascalCase, as the agent\n * writes them). Renderers only — what the agent is *told* about a component\n * comes from the libraries below.\n *\n * Extra entries are fine and useful: they draw components of older messages\n * that no enabled library offers any more.\n */\n components?: ComponentMap;\n /**\n * Slugs of the component libraries this screen can render. Replaces the\n * conversation's list, so pass the complete one; `[]` takes the agent back to\n * plain markdown.\n *\n * The libraries are authored by the operator over the admin API, because their\n * entries reach the model verbatim. This is the half that depends on which of\n * your screens is open, which is why it is yours. Narrow it as the app\n * navigates — the hook only re-declares at moments the change can't disturb a\n * turn in flight.\n *\n * A tag the libraries offer with no renderer in `components` is a block the\n * agent will write and nothing will draw, so the hook logs a warning naming\n * it. The reverse is never warned about.\n */\n componentLibraries?: string[];\n /** Draws the prose around those components. Without it markdown renders as\n * plain text — see `renderBlocks`. */\n renderMarkdown?: (text: string) => ReactNode;\n /** Wraps each block of an agent message, so the app can lay prose and\n * components out differently — see `renderBlocks`. */\n wrapBlock?: (node: ReactNode, block: Block) => ReactNode;\n /** A client tool's handler threw, or its result couldn't be delivered. The\n * agent is told either way; this is for your logs. */\n onClientToolError?: (err: unknown) => void;\n}\n\nexport interface UseConversationResult {\n conversation: Conversation | null;\n /** Oldest first, deduped by id, including any message still in flight. */\n messages: Message[];\n /** Every tool the agent used, folded and ordered by `seq`. Use `trailFor` to\n * get the ones behind a given reply; this is the whole conversation's. */\n toolActivity: ToolActivity[];\n /**\n * The steps behind one reply — what the agent did between the message before\n * it and this one — for a \"how I got here\" disclosure.\n *\n * Empty for a user message: a trail belongs to the answer, not the question.\n * Every entry carries its `arguments` once the call that issued them has been\n * seen — which for a step still running can be a moment later, so phrase from\n * the tool name and let the arguments sharpen it.\n */\n trailFor: (message: Message) => ToolActivity[];\n /**\n * The steps no reply has claimed yet — what the agent has done since the last\n * message. Non-empty while a turn is running, which is what lets a UI draw the\n * trail as it happens instead of only once the answer lands, and non-empty\n * after a turn that produced no message at all.\n */\n pendingTrail: ToolActivity[];\n /** The plan the given reply was written under, or null when the agent kept\n * none — the last revision inside the same window as `trailFor`, so an old\n * answer never shows a newer plan. */\n planFor: (message: Message) => Todo[] | null;\n /** The plan of the turn still running, for the same reason `pendingTrail`\n * exists. */\n pendingPlan: Todo[] | null;\n /** The newest plan in the conversation, regardless of which turn wrote it. */\n todos: Todo[];\n activity: Activity;\n /**\n * Whether a turn is running right now — the signal to key a \"working\"\n * indicator on.\n *\n * The server decides this, not you and not the SDK: `hasPendingTurn` is a\n * trigger over the event log (\"a user message newer than the last\n * `turn_done`\"), pushed on connect and on every change, so it cannot read\n * false while a turn is in flight — not between two replies of one turn, not\n * while a tool runs, not while a client tool suspends it.\n *\n * Not the same as `activity.isProcessing`, and the difference is the whole\n * reason this exists: a client tool **suspends** the turn while your app\n * answers it, so the conversation stops being \"processing\" for as long as\n * that takes. A UI keyed on activity alone stops its spinner in the middle of\n * the work and starts it again a second later.\n *\n * True from the moment a message is sent until the `turn_done` that follows\n * it, and true whenever the server says it is processing — either is enough.\n */\n isTurnRunning: boolean;\n /** True until the first history page and the first stream frame land. False\n * immediately when the conversation came from the client's cache. */\n isLoading: boolean;\n /** There is older history behind what is loaded. */\n hasOlder: boolean;\n /** A `loadOlder` is in flight. */\n isLoadingOlder: boolean;\n /**\n * Prepends the page before the oldest message held. Safe to call while the\n * agent is mid-turn — live messages keep arriving and land in order.\n *\n * Resolves to the number of messages added, so a list can tell \"nothing came\n * back\" from \"we reached the beginning\" without watching `hasOlder`.\n */\n loadOlder: () => Promise<number>;\n /** Set on a failed send or an unrecoverable load. Stream failures land in\n * `streamError` instead, so a dead subscription and a rejected send can be\n * told apart. */\n error: Error | null;\n /**\n * The live subscription died and will not come back: the token was rejected\n * twice (the SDK forces a refresh and retries once before giving up), the\n * conversation is gone, or the server refused the stream.\n *\n * This is the case a UI cannot infer — everything still renders, the agent\n * simply never answers again. Nothing here recovers on its own: remount the\n * hook, or change `conversationId` and back, to resubscribe.\n */\n streamError: Error | null;\n /**\n * True while the event stream is connected. False before the first connect,\n * between a drop and the reconnect that follows it, and whenever\n * `conversationId` is null.\n *\n * A drop is ordinary — the SDK reconnects with backoff, and a socket that\n * went silent is dropped by the watchdog within ~30s — so treat this as the\n * signal for a discreet \"reconnecting…\" hint, never as an error.\n */\n isStreamLive: boolean;\n /**\n * Appends the message optimistically, then reconciles with the server's echo.\n * Rejects on failure, having already rolled the optimistic copy back.\n *\n * On the blank slate (`null` id) it creates the conversation first and reports\n * it through `onCreated`, so a UI never branches on \"no conversation yet\".\n */\n send: (content: string) => Promise<void>;\n /** Uploads a recorded clip. The words appear once the agent's STT model has\n * transcribed it, so nothing is echoed optimistically — there is no text to\n * echo yet. Creates the conversation on the blank slate, like `send`. */\n sendAudio: (audio: Blob) => Promise<void>;\n /**\n * Up to 10 images as ONE message, so the agent reasons over the set instead of\n * one turn per picture. `caption` becomes the message's text, and a per-image\n * `label` names it for the model.\n *\n * Nothing is echoed optimistically: the bubble needs the server's attachment\n * ids to fetch the bytes back, so it appears with the echo. Creates the\n * conversation on the blank slate, like `send`.\n */\n sendImages: (\n images: Array<{ image: Blob; filename?: string; label?: string }>,\n caption?: string,\n ) => Promise<void>;\n steer: (content: string) => Promise<void>;\n /** True while a send is in flight. */\n isSending: boolean;\n /**\n * The agent's reply as React nodes: your components where it used them, your\n * markdown renderer around them.\n *\n * Null for a message with no blocks — every user message, and any agent\n * message from a server that predates them. Render `content` for those.\n */\n renderMessage: (message: Message) => ReactNode[] | null;\n /**\n * Declare the current `clientTools` now.\n *\n * Rarely needed: the hook already declares when the session starts and before\n * every message, which covers an app whose tool set follows the open screen.\n * This is for the one moment it cannot see — inside a tool that just moved the\n * app, where the verbs of the new screen have to be available for the step the\n * agent takes next, and where declaring is safe because the call running it\n * holds the lease.\n */\n syncClientTools: () => Promise<void>;\n /**\n * Bumped whenever the conversation's files change, from any source — the\n * agent writing one, this app uploading one, another tab deleting one.\n *\n * The hook does not hold the listing itself, for the same reason it does not\n * hold which conversation is open: the directory the user is looking at is\n * the app's state. What only the hook can supply is *when* to look again,\n * since it already has the stream. Use it as a dependency:\n *\n * ```ts\n * useEffect(() => { void listFiles(path).then(setEntries) }, [listFiles, path, workspaceRevision])\n * ```\n */\n workspaceRevision: number;\n /** One directory, never recursive. Empty on the blank slate — there is no\n * conversation yet, and listing is not a reason to create one. */\n listFiles: (path?: string) => Promise<WorkspaceEntry[]>;\n /** One file's bytes. */\n readFile: (path: string) => Promise<Blob>;\n /**\n * Uploads files. Creates the conversation on the blank slate, like `send`.\n *\n * Starts no turn: the user dropping a file is not asking a question. The\n * agent is told what changed at the start of its next request, so upload\n * first and then `send` if you want it acted on — those are one turn, not two.\n */\n writeFiles: (files: Array<{ path: string; file: Blob; filename?: string }>) => Promise<void>;\n deleteFile: (path: string) => Promise<void>;\n moveFile: (from: string, to: string) => Promise<void>;\n}\n\nconst IDLE: Activity = { isProcessing: false, hasPendingTurn: false };\n\n/** A message can arrive twice — once in a history page, once as a stream frame —\n * and a `loadOlder` page lands in front of what is already held. Key by id and\n * sort by seq so both are non-events. */\nexport function mergeMessages(existing: Message[], incoming: Message[]): Message[] {\n const byId = new Map(existing.map((m) => [m.id, m]));\n for (const m of incoming) byId.set(m.id, m);\n const held = [...byId.values()];\n\n // Drop the optimistic copy as soon as the real message is held, whichever\n // path delivered it. Doing this only on the stream frame leaves a permanent\n // duplicate on the blank slate: the first `send` creates the conversation, so\n // its echo arrives in the *history page*, and the stream — opened with\n // `since` that page's cursor — never replays it.\n const arrived = new Set(held.filter((m) => !isOptimistic(m)).map((m) => optimisticIdFor(m)));\n const ordered = held\n .filter((m) => !(isOptimistic(m) && arrived.has(m.id)))\n .sort((a, b) => a.seq - b.seq);\n\n // Re-run over the whole list rather than per arrival: a clip and its\n // transcription can arrive in either order relative to a re-render, and the\n // pairing is only correct with every message in hand.\n return mergeVoiceMessages(ordered);\n}\n\nfunction isOptimistic(message: Message): boolean {\n return message.id.startsWith(\"optimistic:\");\n}\n\n/**\n * The steps behind one reply: what the agent did between the message before it\n * and this one.\n *\n * The previous message is what bounds the turn. Without it the first reply\n * would claim every step ever taken, and each later one would claim the steps\n * of the reply before it. Exported for its test — the hook is the only caller.\n *\n * Expects `messages` ordered by `seq`.\n */\nexport function trailOf(\n message: Message,\n messages: Message[],\n activity: ToolActivity[],\n): ToolActivity[] {\n if (message.role !== \"agent\" || activity.length === 0) return [];\n let previousSeq = -1;\n for (const m of messages) {\n if (m.seq >= message.seq) break;\n previousSeq = m.seq;\n }\n return activity.filter((a) => a.seq > previousSeq && a.seq < message.seq);\n}\n\n/**\n * The steps that no reply has claimed yet: everything after the last message\n * held.\n *\n * This is what makes a trail watchable while it happens. `trailOf` needs a\n * message to hang steps on, and mid-turn there is none — the agent is still\n * working. These are those steps.\n *\n * It also covers the turn that ends with **no** message at all: an agent can\n * call tools and stop without writing a reply, and without this those steps\n * would never be shown by anything, because nothing would ever arrive to anchor\n * them.\n *\n * The optimistic copy of a just-sent message is skipped: it carries\n * `MAX_SAFE_INTEGER` as its seq, so counting it would put the boundary past\n * every real step and this would always come back empty.\n *\n * Expects `messages` ordered by `seq`.\n */\nexport function trailAfterLast(messages: Message[], activity: ToolActivity[]): ToolActivity[] {\n if (activity.length === 0) return [];\n let lastSeq = -1;\n for (const m of messages) {\n if (isOptimistic(m)) continue;\n if (m.seq > lastSeq) lastSeq = m.seq;\n }\n return activity.filter((a) => a.seq > lastSeq);\n}\n\n/**\n * Is a turn running?\n *\n * Three signals, and none of them is a comparison this file makes up.\n *\n * `hasPendingTurn` is the server's, and the trigger behind it computes exactly\n * \"there is a user message newer than the last `turn_done`\" — which is what\n * being owed a reply means, and it stays true while a client tool suspends the\n * turn, when nothing is processing and the agent has answered nothing yet.\n * `isProcessing` says the work is happening right now. The optimistic copy\n * covers the moment between pressing enter and the first frame.\n *\n * Both arrive as `conversation_status` frames on the *event* stream, which is\n * what makes reading them here safe. Published on the metadata stream instead —\n * a second connection — they raced the reply they were about, and the UI showed\n * a finished turn with nothing in it for as long as that race lasted.\n *\n * What is deliberately *not* here is a rule deriving \"finished\" from the log.\n * Twice now that has been wrong in opposite directions: reading a missing turn\n * cursor as \"no turn has ended\" left the dots on a conversation idle for hours,\n * and reading a *previous* turn's `turn_done` as this turn's end collapsed the\n * trail mid-work, while the agent was paused waiting on a client tool. The\n * server already publishes the answer; recomputing it from a cursor the client\n * keeps separately only creates two halves that can disagree.\n *\n * Exported for its test; the hook is the only caller.\n */\nexport function turnIsRunning(messages: Message[], activity: Activity): boolean {\n if (messages.some(isOptimistic)) return true;\n return activity.isProcessing || activity.hasPendingTurn;\n}\n\n/**\n * Plan revisions, deduped by `seq` and ordered.\n *\n * The same revision arrives twice routinely — once from the history page and\n * again from the stream frame that follows it — and two identical plans against\n * one turn would make `planOf` pick arbitrarily between them.\n */\nexport function mergePlans(existing: PlanSnapshot[], incoming: PlanSnapshot[]): PlanSnapshot[] {\n const bySeq = new Map<number, PlanSnapshot>();\n for (const plan of existing) bySeq.set(plan.seq, plan);\n for (const plan of incoming) bySeq.set(plan.seq, plan);\n return [...bySeq.values()].sort((a, b) => a.seq - b.seq);\n}\n\n/**\n * The plan as it stood when a reply was written.\n *\n * The agent revises the list several times per turn — one revision per step it\n * ticks off. Showing them all would be a diff log; showing the newest against\n * an old answer would be a lie. So: the last revision in the same window\n * `trailOf` uses, which is the plan the reply was written under.\n *\n * Returns null rather than an empty list when there is none, so a caller can\n * tell \"no plan\" from \"a plan with nothing in it\".\n *\n * Expects `messages` and `plans` ordered by `seq`.\n */\nexport function planOf(\n message: Message,\n messages: Message[],\n plans: PlanSnapshot[],\n): Todo[] | null {\n if (message.role !== \"agent\" || plans.length === 0) return null;\n let previousSeq = -1;\n for (const m of messages) {\n if (m.seq >= message.seq) break;\n previousSeq = m.seq;\n }\n let found: Todo[] | null = null;\n for (const plan of plans) {\n if (plan.seq > previousSeq && plan.seq < message.seq) found = plan.todos;\n }\n return found;\n}\n\n/** The plan of the turn still running — the counterpart of `trailAfterLast`. */\nexport function planAfterLast(messages: Message[], plans: PlanSnapshot[]): Todo[] | null {\n if (plans.length === 0) return null;\n let lastSeq = -1;\n for (const m of messages) {\n if (isOptimistic(m)) continue;\n if (m.seq > lastSeq) lastSeq = m.seq;\n }\n let found: Todo[] | null = null;\n for (const plan of plans) {\n if (plan.seq > lastSeq) found = plan.todos;\n }\n return found;\n}\n\n/**\n * One conversation, live: history, streamed messages, the agent's plan and\n * whether it is working right now.\n *\n * The id is yours to own — component state, a route param, whatever. Pass\n * `null` for the blank slate: nothing connects, and the first `send` creates the\n * conversation and hands it to `onCreated`.\n *\n * ```tsx\n * const [id, setId] = useState<string | null>(null);\n * const { messages, send } = useConversation(id, {\n * agentSlug: \"support\",\n * onCreated: (c) => setId(c.id),\n * });\n * ```\n */\nexport function useConversation(\n conversationId: string | null,\n options: UseConversationOptions = {},\n): UseConversationResult {\n const pageSize = options.pageSize ?? 50;\n const { agentSlug } = options;\n const declareTools = options.declareClientTools ?? true;\n const client = useAgentClient();\n const [conversation, setConversation] = useState<Conversation | null>(null);\n const [messages, setMessages] = useState<Message[]>([]);\n const [toolActivity, setToolActivity] = useState<ToolActivity[]>([]);\n const [plans, setPlans] = useState<PlanSnapshot[]>([]);\n const [todos, setTodos] = useState<Todo[]>([]);\n const [activity, setActivity] = useState<Activity>(IDLE);\n const [isLoading, setIsLoading] = useState(conversationId !== null);\n const [error, setError] = useState<Error | null>(null);\n const [streamError, setStreamError] = useState<Error | null>(null);\n const [isStreamLive, setIsStreamLive] = useState(false);\n const [isSending, setIsSending] = useState(false);\n const [hasOlder, setHasOlder] = useState(false);\n const [isLoadingOlder, setIsLoadingOlder] = useState(false);\n const [workspaceRevision, setWorkspaceRevision] = useState(0);\n\n // Read through a ref so an inline arrow from the parent doesn't rebuild `send`\n // on every render.\n const onCreated = useRef(options.onCreated);\n onCreated.current = options.onCreated;\n\n // The id `send` just created. The effect below reads it to tell \"the parent\n // switched conversations\" (wipe the pane) from \"the parent adopted the one we\n // just made\" (keep what is already on screen).\n const adopting = useRef<string | null>(null);\n\n // The `seq` of the oldest event fetched so far — the cursor for the next page\n // back. A ref, not state: paging must not be restarted by a re-render, and\n // nothing renders from it.\n const oldestSeq = useRef<number | null>(null);\n // Guards against two concurrent `loadOlder` calls (a scroll handler firing\n // twice) fetching the same page and prepending it twice.\n const loadingOlder = useRef(false);\n\n // Highest `change_seq` folded into `messages`. The cache is only correct if\n // this and the messages are written together, so it rides along in a ref\n // rather than being recomputed at save time.\n const cursor = useRef<number | null>(null);\n const hasOlderRef = useRef(false);\n\n // Lets `send` roll back its optimistic message without depending on the\n // messages state, which would rebuild the callback on every frame.\n const pendingIds = useRef(new Set<string>());\n\n // Both bags are usually written inline in the parent's JSX, so their identity\n // changes every render while their content doesn't. Effects key on a string\n // built from the declarations and read the live objects through refs, which\n // is what keeps a re-render from re-declaring tools or restarting a session.\n const clientTools = useRef(options.clientTools);\n clientTools.current = options.clientTools;\n const components = useRef(options.components);\n components.current = options.components;\n const onClientToolError = useRef(options.onClientToolError);\n onClientToolError.current = options.onClientToolError;\n\n /**\n * The context is held, not sent.\n *\n * The server only ever reads it when a turn does, so a user browsing eleven\n * screens before typing anything needs one request, not eleven. `held` is the\n * latest value the app gave us; `sent` is what the server has. They differ\n * only between a navigation and the next moment the agent could possibly\n * look.\n *\n * Those moments are exactly two, and both flush below: just before a message\n * (it is about to start the turn that reads it) and any change while a turn is\n * running (the loop re-reads the context on every iteration, so a screen the\n * user opens mid-work still reaches the tool the agent is about to call).\n */\n const heldContext = useRef<string | null | undefined>(undefined);\n /**\n * Keyed by conversation, not a bare value: the first message *creates* the\n * conversation, so the flush that rides with it belongs to an id that did not\n * exist a moment earlier. Forgetting on every id change would re-send what was\n * just sent; forgetting nothing would starve a second conversation.\n */\n const sentContext = useRef<{ id: string; value: string | null } | null>(null);\n /** Read inside the effect below, which must not re-run when it changes. */\n const isTurnRunningRef = useRef(false);\n\n const flushContext = useCallback(\n async (id: string) => {\n const held = heldContext.current;\n if (held === undefined) return;\n const sent = sentContext.current;\n if (sent?.id === id && sent.value === held) return;\n sentContext.current = { id, value: held };\n try {\n await client.setContext(id, held);\n } catch {\n // Context is a nicety: an agent that misses one answers about the wrong\n // screen, which is better than a send that fails because a PUT did. The\n // value is left un-sent so the next flush tries again.\n sentContext.current = null;\n }\n },\n [client],\n );\n\n useEffect(() => {\n heldContext.current = options.context;\n if (conversationId !== null && isTurnRunningRef.current) void flushContext(conversationId);\n }, [options.context, conversationId, flushContext]);\n\n const toolsKey = useMemo(() => stableKey(options.clientTools), [options.clientTools]);\n\n /**\n * Every handler this conversation has ever been given, newest per name.\n *\n * Never pruned, and that is the point: `PUT /client-tools` replaces the whole\n * set, so an app whose tools follow the open screen narrows what the agent is\n * *told about* — but a call it issued a moment before that has to keep finding\n * its implementation. The runner reads this object at dispatch time, so the\n * identity stays put and the session survives a set that changes under it.\n */\n const handlers = useRef<Record<string, ClientTool<never, unknown>>>({});\n /** What the last successful declaration said, to skip a PUT that says it\n * again. */\n const declaredKey = useRef<{ id: string; key: string } | null>(null);\n\n useEffect(() => {\n Object.assign(handlers.current, options.clientTools ?? {});\n }, [options.clientTools]);\n\n /**\n * Declares the current set, when it differs from what the server was last\n * told.\n *\n * Held rather than sent on change, because the send is destructive: a tool\n * that leaves the set while a call of its own is in flight and unclaimed is\n * failed by the server's reaper. So this runs only where that cannot bite —\n * when the session starts, just before a message, and wherever the app calls\n * `syncClientTools` from inside a tool it is already running (that call holds\n * the lease, so its own result is never at risk).\n */\n const flushClientTools = useCallback(\n async (id: string) => {\n if (!declareTools || toolsKey === null) return;\n const last = declaredKey.current;\n if (last?.id === id && last.key === toolsKey) return;\n declaredKey.current = { id, key: toolsKey };\n try {\n await client.setClientTools(id, clientTools.current ?? {});\n } catch (err) {\n declaredKey.current = null;\n onClientToolError.current?.(err);\n }\n },\n [client, declareTools, toolsKey],\n );\n\n /** Declare now. For the one moment an app knows is safe and the hook cannot:\n * inside a tool that just moved the app, whose own call holds the lease. */\n const syncClientTools = useCallback(async () => {\n if (conversationId !== null) await flushClientTools(conversationId);\n }, [conversationId, flushClientTools]);\n // Held in a ref and keyed by its serialization, like `clientTools` above and\n // for the same reason: this is an array prop, so `componentLibraries={[\"x\"]}`\n // written inline in JSX -- which is what narrowing per screen looks like --\n // is a new identity on every render. In a dependency array that re-runs the\n // effect each time, and its cleanup aborts the in-flight PUT, so a conversation\n // that re-renders on every SSE frame would abort and re-issue forever, never\n // registering and eventually taking a 429. The key is what changes when the\n // list actually changes.\n const libraries = useRef(options.componentLibraries);\n libraries.current = options.componentLibraries;\n const librariesKey =\n options.componentLibraries === undefined ? null : JSON.stringify(options.componentLibraries);\n\n // The running client-tool runner, so the stream handlers can nudge it without\n // re-subscribing when it starts.\n const toolSession = useRef<ClientToolsSession | null>(null);\n // `${conversationId}\\u0000${componentsKey}` of the set already registered —\n // set by `target` too, so creating with components inline doesn't PUT twice.\n const registered = useRef<string | null>(null);\n\n useEffect(() => {\n const adopted = conversationId !== null && conversationId === adopting.current;\n adopting.current = null;\n\n if (!adopted) {\n setConversation(null);\n setMessages([]);\n setToolActivity([]);\n setTodos([]);\n setPlans([]);\n setActivity(IDLE);\n pendingIds.current.clear();\n }\n setError(null);\n // The stream state belongs to the conversation being left, not the one\n // arriving: a fatal from the old id must not be shown against the new one,\n // and nothing is connected until this effect's `onOpen`.\n setStreamError(null);\n setIsStreamLive(false);\n setHasOlder(false);\n setIsLoadingOlder(false);\n oldestSeq.current = null;\n loadingOlder.current = false;\n cursor.current = null;\n hasOlderRef.current = false;\n\n if (conversationId === null) {\n setIsLoading(false);\n return;\n }\n setIsLoading(true);\n\n const controller = new AbortController();\n let subscription: { close(): void } | null = null;\n let closed = false;\n\n /**\n * History first, then the stream from where that page ends.\n *\n * The order matters and is not a race: the server catches up everything\n * past `since` before going live, so an event committed between the two\n * calls still arrives. Subscribing first and paging after would be the racy\n * version — the page could then contain rows the stream had already sent\n * with no cursor to reconcile them by.\n */\n void (async () => {\n try {\n // Cached when this conversation has been opened before, which makes the\n // whole branch below free — the stream then delivers only the delta.\n const page = await client.loadHistory(conversationId, {\n pageSize,\n signal: controller.signal,\n });\n if (controller.signal.aborted) return;\n\n setMessages((prev) => mergeMessages(prev, page.messages));\n setToolActivity((prev) => mergeToolActivities([...prev, ...page.toolActivity]));\n setPlans((prev) => mergePlans(prev, page.plans));\n oldestSeq.current = page.oldestSeq;\n cursor.current = page.latestChangeSeq;\n hasOlderRef.current = page.hasOlder;\n setHasOlder(page.hasOlder);\n setIsLoading(false);\n\n if (closed) return;\n subscription = client.subscribe(\n conversationId,\n {\n // Both nudge the client-tool runner instead of it holding a second\n // stream: `onOpen` covers what happened while we were away (a call\n // can predate the connection, or a reconnect can swallow its\n // frame), `onClientToolCall` covers everything after.\n onOpen: () => {\n setIsStreamLive(true);\n toolSession.current?.poke();\n },\n onClientToolCall: () => toolSession.current?.poke(),\n // A failed attempt only means \"not connected right now\": the SDK is\n // already backing off toward the next one.\n onError: () => setIsStreamLive(false),\n // The loop gave up. Nothing else will arrive, so say so rather than\n // leaving a view that looks live and never moves again.\n onFatal: (err) => {\n setIsStreamLive(false);\n setStreamError(err instanceof Error ? err : new Error(String(err)));\n },\n // The log is the only place a file change is announced: it has no\n // curated projection, because a listing is a request away and\n // mirroring the tree here would be a cache to invalidate.\n onEvent: (event) => {\n if (event.type === \"workspace_root\") setWorkspaceRevision((n) => n + 1);\n },\n onToolActivity: (activity) => {\n setToolActivity((prev) => mergeToolActivities([...prev, activity]));\n },\n onMessage: (message) => {\n pendingIds.current.delete(optimisticIdFor(message));\n setMessages((prev) => mergeMessages(dropOptimisticEcho(prev, message), [message]));\n setIsLoading(false);\n },\n onTodos: (todos, seq) => {\n setTodos(todos);\n setPlans((prev) => mergePlans(prev, [{ todos, seq }]));\n },\n onActivity: setActivity,\n onConversation: setConversation,\n onCursor: (changeSeq) => {\n if (cursor.current === null || changeSeq > cursor.current) {\n cursor.current = changeSeq;\n }\n },\n },\n // `?? undefined` on an empty conversation: with no events yet there is\n // nothing to skip, and the stream should deliver from the start.\n { since: page.latestChangeSeq ?? undefined },\n );\n } catch (err: unknown) {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n })();\n\n return () => {\n closed = true;\n controller.abort();\n subscription?.close();\n };\n }, [client, conversationId, pageSize]);\n\n // What `saveHistory` should record, kept fresh so the unmount path below can\n // read it without depending on state.\n const snapshot = useRef({\n messages,\n toolActivity,\n plans,\n oldestSeq,\n cursor,\n hasOlderRef,\n });\n snapshot.current.messages = messages;\n snapshot.current.toolActivity = toolActivity;\n snapshot.current.plans = plans;\n\n const persist = useCallback(\n (id: string) => {\n const { messages: held, toolActivity: trail, plans: planned } = snapshot.current;\n void client.saveHistory(id, {\n toolActivity: trail,\n plans: planned,\n // The optimistic copy carries a placeholder id and MAX_SAFE_INTEGER as\n // its seq. Cached, it would outlive the send and sit at the bottom of\n // the transcript forever.\n messages: held.filter((m) => !m.id.startsWith(\"optimistic:\")),\n oldestSeq: oldestSeq.current,\n latestChangeSeq: cursor.current,\n hasOlder: hasOlderRef.current,\n });\n },\n [client],\n );\n\n // Debounced: a single turn can append a dozen messages in a second, and a\n // persistent adapter should not pay a write for each.\n useEffect(() => {\n if (conversationId === null || messages.length === 0) return;\n const timer = setTimeout(() => persist(conversationId), 400);\n return () => clearTimeout(timer);\n }, [conversationId, messages, persist]);\n\n // Switching conversations cancels that timer, so without this a chat the user\n // opened and left quickly would never be cached.\n useEffect(() => {\n if (conversationId === null) return;\n return () => persist(conversationId);\n }, [conversationId, persist]);\n\n // Enabling the libraries is what makes the agent aware of them; the server\n // refuses any component they don't resolve to. Skipped when the option is\n // absent, so an app that doesn't use components never pays for a request —\n // pass `[]` to deliberately clear a list enabled earlier.\n useEffect(() => {\n const current = libraries.current;\n if (conversationId === null || librariesKey === null || current === undefined) return;\n const stamp = `${conversationId}\\u0000${librariesKey}`;\n if (registered.current === stamp) return;\n\n const controller = new AbortController();\n void client\n .setComponentLibraries(conversationId, current, controller.signal)\n .then((enabled) => {\n registered.current = stamp;\n warnMissingRenderers(enabled.tags, components.current);\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err : new Error(String(err)));\n });\n return () => controller.abort();\n }, [client, conversationId, librariesKey]);\n\n /** Whether this app serves client tools at all — a boolean, so the session\n * below is not restarted every time the set itself changes. */\n const servesTools = options.clientTools !== undefined;\n /** Same reason as `onClientToolError` above: the session effect must not list\n * a callback whose identity moves with the tool set, or every navigation\n * restarts the runner and drops the claim on whatever it is running. */\n const flushToolsRef = useRef(flushClientTools);\n flushToolsRef.current = flushClientTools;\n\n // One runner per open conversation, driven by the subscription above. It\n // declares on start, which is what converges a set that changed while this\n // conversation was closed.\n useEffect(() => {\n if (conversationId === null || !servesTools) return;\n\n let session: ClientToolsSession | null = null;\n let cancelled = false;\n void (async () => {\n try {\n const started = await client.serveClientTools(conversationId, {\n // The live object, not a copy: a set that changes while a call is in\n // flight must not cost the runner its handler.\n tools: handlers.current,\n watch: false,\n // The hook declares, so there is one place that decides *when* — see\n // `flushClientTools`.\n declare: false,\n onError: (err) => onClientToolError.current?.(err),\n });\n void flushToolsRef.current(conversationId);\n if (cancelled) {\n started.stop();\n return;\n }\n session = started;\n toolSession.current = started;\n // The stream is already open by now, so its `onOpen` has come and gone:\n // catch up here instead of waiting for the next call.\n started.poke();\n } catch (err) {\n if (!cancelled) onClientToolError.current?.(err);\n }\n })();\n\n return () => {\n cancelled = true;\n toolSession.current = null;\n session?.stop();\n };\n // `toolsKey` is deliberately absent: the runner reads `handlers` live, so a\n // set that changes needs a declaration, not a new session — restarting one\n // mid-turn drops the claim on whatever it is running.\n }, [client, conversationId, servesTools]);\n\n const loadOlder = useCallback(async () => {\n if (conversationId === null || loadingOlder.current) return 0;\n const before = oldestSeq.current;\n if (before === null) return 0;\n\n loadingOlder.current = true;\n setIsLoadingOlder(true);\n try {\n const page = await client.listMessagesPage(conversationId, {\n before,\n limit: pageSize,\n });\n // `merge` sorts by seq, so a prepend needs no special handling — and a\n // live message that arrived mid-fetch keeps its place.\n setMessages((prev) => mergeMessages(prev, page.messages));\n setToolActivity((prev) => mergeToolActivities([...prev, ...page.toolActivity]));\n setPlans((prev) => mergePlans(prev, page.plans));\n if (page.oldestSeq !== null) oldestSeq.current = page.oldestSeq;\n hasOlderRef.current = page.hasOlder;\n setHasOlder(page.hasOlder);\n return page.messages.length;\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n return 0;\n } finally {\n loadingOlder.current = false;\n setIsLoadingOlder(false);\n }\n }, [client, conversationId, pageSize]);\n\n /** The conversation to send into, created on the blank slate. */\n const target = useCallback(async () => {\n if (conversationId !== null) return conversationId;\n // The libraries ride the create call, and the tools are declared before this\n // returns, because the caller posts a message the moment it does — and the\n // effects that would otherwise do both only run a render later. A tool the\n // agent wasn't told about can't be called in the turn that follows.\n const created = await client.createConversation({\n agentSlug,\n ...(libraries.current === undefined ? {} : { componentLibraries: libraries.current }),\n });\n if (libraries.current !== undefined && librariesKey !== null) {\n registered.current = `${created.id}\\u0000${librariesKey}`;\n // The create response doesn't carry the resolved tags, so the check that\n // the enabling PUT does runs here off its own read.\n void client\n .listComponentLibraries(created.id)\n .then((enabled) => warnMissingRenderers(enabled.tags, components.current))\n .catch(() => {});\n }\n // Skipped when the app declares its own set: it will do so before the\n // message that follows this call, and declaring everything here first would\n // hand the agent tools the open screen does not offer.\n if (clientTools.current !== undefined && declareTools) {\n await client.setClientTools(created.id, clientTools.current);\n }\n // Recorded before the parent re-renders with the new id, so the effect knows\n // the messages on screen already belong to this conversation.\n adopting.current = created.id;\n setConversation(created);\n await onCreated.current?.(created);\n return created.id;\n }, [client, conversationId, agentSlug, librariesKey, declareTools]);\n\n const send = useCallback(\n async (content: string) => {\n const text = content.trim();\n if (!text) return;\n\n const optimistic: Message = {\n id: `optimistic:${text}`,\n role: \"user\",\n content: text,\n attachments: [],\n // Sorts after everything received so far; the server's echo replaces it\n // with the real seq.\n seq: Number.MAX_SAFE_INTEGER,\n at: new Date().toISOString(),\n };\n pendingIds.current.add(optimistic.id);\n setMessages((prev) => mergeMessages(prev, [optimistic]));\n setIsSending(true);\n setError(null);\n // The turn is queued the moment the POST lands, and the meta stream can\n // take a beat to say so. Without this the composer looks ignored.\n setActivity((prev) => ({ ...prev, hasPendingTurn: true }));\n\n try {\n const id = await target();\n await flushContext(id);\n await flushClientTools(id);\n await client.sendMessage(id, text);\n } catch (err) {\n pendingIds.current.delete(optimistic.id);\n setMessages((prev) => prev.filter((m) => m.id !== optimistic.id));\n setActivity((prev) => ({ ...prev, hasPendingTurn: false }));\n const wrapped = err instanceof Error ? err : new Error(String(err));\n setError(wrapped);\n throw wrapped;\n } finally {\n setIsSending(false);\n }\n },\n [client, target, flushContext, flushClientTools],\n );\n\n const sendAudio = useCallback(\n async (audio: Blob) => {\n if (audio.size === 0) return;\n setIsSending(true);\n setError(null);\n setActivity((prev) => ({ ...prev, hasPendingTurn: true }));\n try {\n const id = await target();\n await flushContext(id);\n await flushClientTools(id);\n await client.sendAudio(id, audio);\n } catch (err) {\n setActivity((prev) => ({ ...prev, hasPendingTurn: false }));\n const wrapped = err instanceof Error ? err : new Error(String(err));\n setError(wrapped);\n throw wrapped;\n } finally {\n setIsSending(false);\n }\n },\n [client, target, flushContext, flushClientTools],\n );\n\n const sendImages = useCallback(\n async (images: Array<{ image: Blob; filename?: string; label?: string }>, caption?: string) => {\n if (images.length === 0) return;\n setIsSending(true);\n setError(null);\n setActivity((prev) => ({ ...prev, hasPendingTurn: true }));\n try {\n const id = await target();\n await flushContext(id);\n await flushClientTools(id);\n await client.sendImages(id, images, { caption });\n } catch (err) {\n setActivity((prev) => ({ ...prev, hasPendingTurn: false }));\n const wrapped = err instanceof Error ? err : new Error(String(err));\n setError(wrapped);\n throw wrapped;\n } finally {\n setIsSending(false);\n }\n },\n [client, target, flushContext, flushClientTools],\n );\n\n const listFiles = useCallback(\n async (path?: string) => {\n if (conversationId === null) return [];\n const dir = await client.listFiles(conversationId, { path });\n return dir.entries;\n },\n [client, conversationId],\n );\n\n const readFile = useCallback(\n async (path: string) => {\n if (conversationId === null) {\n throw new Error(\"There is no conversation yet, so there are no files to read.\");\n }\n return await client.readFile(conversationId, path);\n },\n [client, conversationId],\n );\n\n const writeFiles = useCallback(\n async (files: Array<{ path: string; file: Blob; filename?: string }>) => {\n if (files.length === 0) return;\n const id = await target();\n await client.writeFiles(id, files);\n // The stream reports the change too, but only for a conversation this\n // hook is already subscribed to — on the blank slate the subscription is\n // still being set up, and the app would be left waiting for a bump that\n // arrived before it was listening.\n setWorkspaceRevision((n) => n + 1);\n },\n [client, target],\n );\n\n const deleteFile = useCallback(\n async (path: string) => {\n if (conversationId === null) return;\n await client.deleteFile(conversationId, path);\n setWorkspaceRevision((n) => n + 1);\n },\n [client, conversationId],\n );\n\n const moveFile = useCallback(\n async (from: string, to: string) => {\n if (conversationId === null) return;\n await client.moveFile(conversationId, from, to);\n setWorkspaceRevision((n) => n + 1);\n },\n [client, conversationId],\n );\n\n const steer = useCallback(\n async (content: string) => {\n const text = content.trim();\n if (!text || conversationId === null) return;\n await client.steer(conversationId, text);\n },\n [client, conversationId],\n );\n\n const trailFor = useCallback(\n (message: Message) => trailOf(message, messages, toolActivity),\n [messages, toolActivity],\n );\n\n const pendingTrail = useMemo(\n () => trailAfterLast(messages, toolActivity),\n [messages, toolActivity],\n );\n\n const planFor = useCallback(\n (message: Message) => planOf(message, messages, plans),\n [messages, plans],\n );\n\n const pendingPlan = useMemo(() => planAfterLast(messages, plans), [messages, plans]);\n\n const isTurnRunning = useMemo(() => turnIsRunning(messages, activity), [messages, activity]);\n isTurnRunningRef.current = isTurnRunning;\n\n const renderMessage = useCallback(\n (message: Message) =>\n renderBlocks(message, {\n components: components.current,\n renderMarkdown: options.renderMarkdown,\n wrapBlock: options.wrapBlock,\n }),\n // Rebuilt when the set changes so a message already on screen picks up a\n // newly registered component; `renderMarkdown` is read fresh either way.\n [options.renderMarkdown, options.wrapBlock],\n );\n\n return useMemo(\n () => ({\n conversation,\n messages,\n toolActivity,\n trailFor,\n pendingTrail,\n planFor,\n pendingPlan,\n todos,\n activity,\n isTurnRunning,\n isLoading,\n hasOlder,\n isLoadingOlder,\n loadOlder,\n error,\n streamError,\n isStreamLive,\n send,\n sendAudio,\n sendImages,\n steer,\n isSending,\n renderMessage,\n syncClientTools,\n workspaceRevision,\n listFiles,\n readFile,\n writeFiles,\n deleteFile,\n moveFile,\n }),\n [\n conversation,\n messages,\n toolActivity,\n trailFor,\n pendingTrail,\n planFor,\n pendingPlan,\n todos,\n activity,\n isTurnRunning,\n isLoading,\n hasOlder,\n isLoadingOlder,\n loadOlder,\n error,\n streamError,\n isStreamLive,\n send,\n sendAudio,\n sendImages,\n steer,\n isSending,\n renderMessage,\n syncClientTools,\n workspaceRevision,\n listFiles,\n readFile,\n writeFiles,\n deleteFile,\n moveFile,\n ],\n );\n}\n\nfunction optimisticIdFor(message: Message): string {\n return `optimistic:${message.content.trim()}`;\n}\n\n/** Drops the placeholder once the real one arrives, matched on the text the user\n * typed — the server assigns the id, so there is nothing else to match on. */\nfunction dropOptimisticEcho(messages: Message[], real: Message): Message[] {\n if (real.role !== \"user\") return messages;\n const echoId = optimisticIdFor(real);\n return messages.filter((m) => m.id !== echoId);\n}\n\n/** Warn about tags the agent may now write that this app has no renderer for.\n *\n * One-directional on purpose: a renderer with no tag is how older messages keep\n * drawing after a library stopped offering the component, so it is normal. The\n * other way round is a block the agent will write and nothing will draw — a\n * silent gap in a reply, and this is the only moment the enabled catalog and the\n * component map are both in hand. */\nfunction warnMissingRenderers(tags: string[], components: ComponentMap | undefined): void {\n const missing = tags.filter((tag) => components?.[tag] === undefined);\n if (missing.length === 0) return;\n console.warn(\n `[cubos-agent] The enabled component libraries offer ${missing\n .map((t) => `<${t}>`)\n .join(\", \")}, which this app has no renderer for. The agent may use them ` +\n \"and nothing will be drawn. Add them to `components`, or drop the library \" +\n \"that declares them from `componentLibraries`.\",\n );\n}\n\n/** What an effect can compare across renders: everything about the tools except\n * the handlers, which are functions and change identity for free. Null when the\n * option is absent, which is how \"don't touch the set\" is expressed. */\nfunction stableKey(tools: Record<string, ClientTool<never, unknown>> | undefined): string | null {\n if (tools === undefined) return null;\n return JSON.stringify(\n Object.entries(tools).map(([name, tool]) => [\n name,\n tool.description,\n tool.inputSchema,\n tool.outputSchema,\n tool.readOnlyHint,\n tool.destructiveHint,\n tool.idempotentHint,\n tool.timeoutSeconds,\n ]),\n );\n}\n",
7
+ "import {\n type Activity,\n type Block,\n type ClientTool,\n type ClientToolsSession,\n type Conversation,\n type FilesystemEntry,\n type Message,\n mergeToolActivities,\n mergeVoiceMessages,\n type PlanSnapshot,\n type Todo,\n type ToolActivity,\n} from \"@cubos/agent-sdk\";\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { type ComponentMap, renderBlocks } from \"./blocks.js\";\nimport { useAgentClient } from \"./context.js\";\n\nexport interface UseConversationOptions {\n /**\n * How many events a history page asks for. Counts events, not messages, so a\n * page of a tool-heavy turn can yield only a couple of bubbles — which is why\n * a UI should keep calling `loadOlder` until `hasOlder` is false rather than\n * assuming one page fills the viewport.\n */\n pageSize?: number;\n /** Which agent a conversation created by the first `send` talks to. Optional\n * when the token names exactly one. */\n agentSlug?: string;\n /**\n * Called once, with the conversation the first `send` created, when this hook\n * was given `null`. Point it at whatever owns the selection — `setState`, a\n * router navigation — and pass the id back on the next render.\n *\n * The messages already on screen survive that hand-off, so the user's first\n * message doesn't blink out while the conversation is adopted.\n *\n * **Awaited.** It runs after the conversation exists and before the message\n * is posted, which is the only window in which an app can set something up\n * that the very first turn depends on — declaring its own client tools, for\n * one. Returning a promise holds the send until it resolves; a throw fails\n * the send, which is the honest outcome for setup that did not happen.\n */\n onCreated?: (conversation: Conversation) => void | Promise<void>;\n /**\n * Functions the agent may call, keyed by tool name. Declared on the\n * conversation, then executed here as the agent calls them: the turn suspends\n * on the call and resumes with whatever the handler returns.\n *\n * They ride the subscription this hook already holds — no second stream.\n */\n clientTools?: Record<string, ClientTool<never, unknown>>;\n /**\n * Whether this hook declares `clientTools` on the conversation. Defaults to\n * true, which is what an app with a fixed tool set wants.\n *\n * Turn it off to declare the set yourself, with `setClientTools`, and pass\n * every handler the agent might ever call here. That splits two things this\n * hook otherwise ties together: *what can run* (the handlers, which must\n * cover any call that arrives) and *what the agent is told about* (the\n * declaration). An app whose tools depend on the open screen needs that\n * split — the declaration should change on navigation, while the handlers\n * must stay complete, or a call issued a moment earlier would find no\n * implementation.\n */\n declareClientTools?: boolean;\n /**\n * What your app currently has on screen — the open page, the selected record,\n * the filters in force. Free-form: prose or JSON, whatever the agent reads\n * best.\n *\n * Set it and forget it — update it as often as your screen moves. Nothing is\n * sent while nobody is listening: the value is held and flushed at the two\n * moments the agent can actually read it, just before a message and on any\n * change while a turn is running. A user browsing eleven screens before typing\n * anything costs one request, and the agent is told once.\n *\n * Undefined leaves it alone; `null` clears it.\n */\n context?: string | null;\n /**\n * What draws each component, keyed by tag name (PascalCase, as the agent\n * writes them). Renderers only — what the agent is *told* about a component\n * comes from the libraries below.\n *\n * Extra entries are fine and useful: they draw components of older messages\n * that no enabled library offers any more.\n */\n components?: ComponentMap;\n /**\n * Slugs of the component libraries this screen can render. Replaces the\n * conversation's list, so pass the complete one; `[]` takes the agent back to\n * plain markdown.\n *\n * The libraries are authored by the operator over the admin API, because their\n * entries reach the model verbatim. This is the half that depends on which of\n * your screens is open, which is why it is yours. Narrow it as the app\n * navigates — the hook only re-declares at moments the change can't disturb a\n * turn in flight.\n *\n * A tag the libraries offer with no renderer in `components` is a block the\n * agent will write and nothing will draw, so the hook logs a warning naming\n * it. The reverse is never warned about.\n */\n componentLibraries?: string[];\n /** Draws the prose around those components. Without it markdown renders as\n * plain text — see `renderBlocks`. */\n renderMarkdown?: (text: string) => ReactNode;\n /** Wraps each block of an agent message, so the app can lay prose and\n * components out differently — see `renderBlocks`. */\n wrapBlock?: (node: ReactNode, block: Block) => ReactNode;\n /** A client tool's handler threw, or its result couldn't be delivered. The\n * agent is told either way; this is for your logs. */\n onClientToolError?: (err: unknown) => void;\n}\n\nexport interface UseConversationResult {\n conversation: Conversation | null;\n /** Oldest first, deduped by id, including any message still in flight. */\n messages: Message[];\n /** Every tool the agent used, folded and ordered by `seq`. Use `trailFor` to\n * get the ones behind a given reply; this is the whole conversation's. */\n toolActivity: ToolActivity[];\n /**\n * The steps behind one reply — what the agent did between the message before\n * it and this one — for a \"how I got here\" disclosure.\n *\n * Empty for a user message: a trail belongs to the answer, not the question.\n * Every entry carries its `arguments` once the call that issued them has been\n * seen — which for a step still running can be a moment later, so phrase from\n * the tool name and let the arguments sharpen it.\n */\n trailFor: (message: Message) => ToolActivity[];\n /**\n * The steps no reply has claimed yet — what the agent has done since the last\n * message. Non-empty while a turn is running, which is what lets a UI draw the\n * trail as it happens instead of only once the answer lands, and non-empty\n * after a turn that produced no message at all.\n */\n pendingTrail: ToolActivity[];\n /** The plan the given reply was written under, or null when the agent kept\n * none — the last revision inside the same window as `trailFor`, so an old\n * answer never shows a newer plan. */\n planFor: (message: Message) => Todo[] | null;\n /** The plan of the turn still running, for the same reason `pendingTrail`\n * exists. */\n pendingPlan: Todo[] | null;\n /** The newest plan in the conversation, regardless of which turn wrote it. */\n todos: Todo[];\n activity: Activity;\n /**\n * Whether a turn is running right now — the signal to key a \"working\"\n * indicator on.\n *\n * The server decides this, not you and not the SDK: `hasPendingTurn` is a\n * trigger over the event log (\"a user message newer than the last\n * `turn_done`\"), pushed on connect and on every change, so it cannot read\n * false while a turn is in flight — not between two replies of one turn, not\n * while a tool runs, not while a client tool suspends it.\n *\n * Not the same as `activity.isProcessing`, and the difference is the whole\n * reason this exists: a client tool **suspends** the turn while your app\n * answers it, so the conversation stops being \"processing\" for as long as\n * that takes. A UI keyed on activity alone stops its spinner in the middle of\n * the work and starts it again a second later.\n *\n * True from the moment a message is sent until the `turn_done` that follows\n * it, and true whenever the server says it is processing — either is enough.\n */\n isTurnRunning: boolean;\n /** True until the first history page and the first stream frame land. False\n * immediately when the conversation came from the client's cache. */\n isLoading: boolean;\n /** There is older history behind what is loaded. */\n hasOlder: boolean;\n /** A `loadOlder` is in flight. */\n isLoadingOlder: boolean;\n /**\n * Prepends the page before the oldest message held. Safe to call while the\n * agent is mid-turn — live messages keep arriving and land in order.\n *\n * Resolves to the number of messages added, so a list can tell \"nothing came\n * back\" from \"we reached the beginning\" without watching `hasOlder`.\n */\n loadOlder: () => Promise<number>;\n /** Set on a failed send or an unrecoverable load. Stream failures land in\n * `streamError` instead, so a dead subscription and a rejected send can be\n * told apart. */\n error: Error | null;\n /**\n * The live subscription died and will not come back: the token was rejected\n * twice (the SDK forces a refresh and retries once before giving up), the\n * conversation is gone, or the server refused the stream.\n *\n * This is the case a UI cannot infer — everything still renders, the agent\n * simply never answers again. Nothing here recovers on its own: remount the\n * hook, or change `conversationId` and back, to resubscribe.\n */\n streamError: Error | null;\n /**\n * True while the event stream is connected. False before the first connect,\n * between a drop and the reconnect that follows it, and whenever\n * `conversationId` is null.\n *\n * A drop is ordinary — the SDK reconnects with backoff, and a socket that\n * went silent is dropped by the watchdog within ~30s — so treat this as the\n * signal for a discreet \"reconnecting…\" hint, never as an error.\n */\n isStreamLive: boolean;\n /**\n * Appends the message optimistically, then reconciles with the server's echo.\n * Rejects on failure, having already rolled the optimistic copy back.\n *\n * On the blank slate (`null` id) it creates the conversation first and reports\n * it through `onCreated`, so a UI never branches on \"no conversation yet\".\n */\n send: (content: string) => Promise<void>;\n /** Uploads a recorded clip. The words appear once the agent's STT model has\n * transcribed it, so nothing is echoed optimistically — there is no text to\n * echo yet. Creates the conversation on the blank slate, like `send`. */\n sendAudio: (audio: Blob) => Promise<void>;\n /**\n * Up to 10 images as ONE message, so the agent reasons over the set instead of\n * one turn per picture. `caption` becomes the message's text, and a per-image\n * `label` names it for the model.\n *\n * Nothing is echoed optimistically: the bubble needs the server's attachment\n * ids to fetch the bytes back, so it appears with the echo. Creates the\n * conversation on the blank slate, like `send`.\n */\n sendImages: (\n images: Array<{ image: Blob; filename?: string; label?: string }>,\n caption?: string,\n ) => Promise<void>;\n steer: (content: string) => Promise<void>;\n /** True while a send is in flight. */\n isSending: boolean;\n /**\n * The agent's reply as React nodes: your components where it used them, your\n * markdown renderer around them.\n *\n * Null for a message with no blocks — every user message, and any agent\n * message from a server that predates them. Render `content` for those.\n */\n renderMessage: (message: Message) => ReactNode[] | null;\n /**\n * Declare the current `clientTools` now.\n *\n * Rarely needed: the hook already declares when the session starts and before\n * every message, which covers an app whose tool set follows the open screen.\n * This is for the one moment it cannot see — inside a tool that just moved the\n * app, where the verbs of the new screen have to be available for the step the\n * agent takes next, and where declaring is safe because the call running it\n * holds the lease.\n */\n syncClientTools: () => Promise<void>;\n /**\n * Bumped whenever the conversation's files change, from any source — the\n * agent writing one, this app uploading one, another tab deleting one.\n *\n * The hook does not hold the listing itself, for the same reason it does not\n * hold which conversation is open: the directory the user is looking at is\n * the app's state. What only the hook can supply is *when* to look again,\n * since it already has the stream. Use it as a dependency:\n *\n * ```ts\n * useEffect(() => { void listFiles(path).then(setEntries) }, [listFiles, path, filesystemRevision])\n * ```\n */\n filesystemRevision: number;\n /** One directory, never recursive. Empty on the blank slate — there is no\n * conversation yet, and listing is not a reason to create one. */\n listFiles: (path?: string) => Promise<FilesystemEntry[]>;\n /** One file's bytes. */\n readFile: (path: string) => Promise<Blob>;\n /**\n * Uploads files. Creates the conversation on the blank slate, like `send`.\n *\n * Starts no turn: the user dropping a file is not asking a question. The\n * agent is told what changed at the start of its next request, so upload\n * first and then `send` if you want it acted on — those are one turn, not two.\n */\n writeFiles: (files: Array<{ path: string; file: Blob; filename?: string }>) => Promise<void>;\n deleteFile: (path: string) => Promise<void>;\n moveFile: (from: string, to: string) => Promise<void>;\n}\n\nconst IDLE: Activity = { isProcessing: false, hasPendingTurn: false };\n\n/** A message can arrive twice — once in a history page, once as a stream frame —\n * and a `loadOlder` page lands in front of what is already held. Key by id and\n * sort by seq so both are non-events. */\nexport function mergeMessages(existing: Message[], incoming: Message[]): Message[] {\n const byId = new Map(existing.map((m) => [m.id, m]));\n for (const m of incoming) byId.set(m.id, m);\n const held = [...byId.values()];\n\n // Drop the optimistic copy as soon as the real message is held, whichever\n // path delivered it. Doing this only on the stream frame leaves a permanent\n // duplicate on the blank slate: the first `send` creates the conversation, so\n // its echo arrives in the *history page*, and the stream — opened with\n // `since` that page's cursor — never replays it.\n const arrived = new Set(held.filter((m) => !isOptimistic(m)).map((m) => optimisticIdFor(m)));\n const ordered = held\n .filter((m) => !(isOptimistic(m) && arrived.has(m.id)))\n .sort((a, b) => a.seq - b.seq);\n\n // Re-run over the whole list rather than per arrival: a clip and its\n // transcription can arrive in either order relative to a re-render, and the\n // pairing is only correct with every message in hand.\n return mergeVoiceMessages(ordered);\n}\n\nfunction isOptimistic(message: Message): boolean {\n return message.id.startsWith(\"optimistic:\");\n}\n\n/**\n * The steps behind one reply: what the agent did between the message before it\n * and this one.\n *\n * The previous message is what bounds the turn. Without it the first reply\n * would claim every step ever taken, and each later one would claim the steps\n * of the reply before it. Exported for its test — the hook is the only caller.\n *\n * Expects `messages` ordered by `seq`.\n */\nexport function trailOf(\n message: Message,\n messages: Message[],\n activity: ToolActivity[],\n): ToolActivity[] {\n if (message.role !== \"agent\" || activity.length === 0) return [];\n let previousSeq = -1;\n for (const m of messages) {\n if (m.seq >= message.seq) break;\n previousSeq = m.seq;\n }\n return activity.filter((a) => a.seq > previousSeq && a.seq < message.seq);\n}\n\n/**\n * The steps that no reply has claimed yet: everything after the last message\n * held.\n *\n * This is what makes a trail watchable while it happens. `trailOf` needs a\n * message to hang steps on, and mid-turn there is none — the agent is still\n * working. These are those steps.\n *\n * It also covers the turn that ends with **no** message at all: an agent can\n * call tools and stop without writing a reply, and without this those steps\n * would never be shown by anything, because nothing would ever arrive to anchor\n * them.\n *\n * The optimistic copy of a just-sent message is skipped: it carries\n * `MAX_SAFE_INTEGER` as its seq, so counting it would put the boundary past\n * every real step and this would always come back empty.\n *\n * Expects `messages` ordered by `seq`.\n */\nexport function trailAfterLast(messages: Message[], activity: ToolActivity[]): ToolActivity[] {\n if (activity.length === 0) return [];\n let lastSeq = -1;\n for (const m of messages) {\n if (isOptimistic(m)) continue;\n if (m.seq > lastSeq) lastSeq = m.seq;\n }\n return activity.filter((a) => a.seq > lastSeq);\n}\n\n/**\n * Is a turn running?\n *\n * Three signals, and none of them is a comparison this file makes up.\n *\n * `hasPendingTurn` is the server's, and the trigger behind it computes exactly\n * \"there is a user message newer than the last `turn_done`\" — which is what\n * being owed a reply means, and it stays true while a client tool suspends the\n * turn, when nothing is processing and the agent has answered nothing yet.\n * `isProcessing` says the work is happening right now. The optimistic copy\n * covers the moment between pressing enter and the first frame.\n *\n * Both arrive as `conversation_status` frames on the *event* stream, which is\n * what makes reading them here safe. Published on the metadata stream instead —\n * a second connection — they raced the reply they were about, and the UI showed\n * a finished turn with nothing in it for as long as that race lasted.\n *\n * What is deliberately *not* here is a rule deriving \"finished\" from the log.\n * Twice now that has been wrong in opposite directions: reading a missing turn\n * cursor as \"no turn has ended\" left the dots on a conversation idle for hours,\n * and reading a *previous* turn's `turn_done` as this turn's end collapsed the\n * trail mid-work, while the agent was paused waiting on a client tool. The\n * server already publishes the answer; recomputing it from a cursor the client\n * keeps separately only creates two halves that can disagree.\n *\n * Exported for its test; the hook is the only caller.\n */\nexport function turnIsRunning(messages: Message[], activity: Activity): boolean {\n if (messages.some(isOptimistic)) return true;\n return activity.isProcessing || activity.hasPendingTurn;\n}\n\n/**\n * Plan revisions, deduped by `seq` and ordered.\n *\n * The same revision arrives twice routinely — once from the history page and\n * again from the stream frame that follows it — and two identical plans against\n * one turn would make `planOf` pick arbitrarily between them.\n */\nexport function mergePlans(existing: PlanSnapshot[], incoming: PlanSnapshot[]): PlanSnapshot[] {\n const bySeq = new Map<number, PlanSnapshot>();\n for (const plan of existing) bySeq.set(plan.seq, plan);\n for (const plan of incoming) bySeq.set(plan.seq, plan);\n return [...bySeq.values()].sort((a, b) => a.seq - b.seq);\n}\n\n/**\n * The plan as it stood when a reply was written.\n *\n * The agent revises the list several times per turn — one revision per step it\n * ticks off. Showing them all would be a diff log; showing the newest against\n * an old answer would be a lie. So: the last revision in the same window\n * `trailOf` uses, which is the plan the reply was written under.\n *\n * Returns null rather than an empty list when there is none, so a caller can\n * tell \"no plan\" from \"a plan with nothing in it\".\n *\n * Expects `messages` and `plans` ordered by `seq`.\n */\nexport function planOf(\n message: Message,\n messages: Message[],\n plans: PlanSnapshot[],\n): Todo[] | null {\n if (message.role !== \"agent\" || plans.length === 0) return null;\n let previousSeq = -1;\n for (const m of messages) {\n if (m.seq >= message.seq) break;\n previousSeq = m.seq;\n }\n let found: Todo[] | null = null;\n for (const plan of plans) {\n if (plan.seq > previousSeq && plan.seq < message.seq) found = plan.todos;\n }\n return found;\n}\n\n/** The plan of the turn still running — the counterpart of `trailAfterLast`. */\nexport function planAfterLast(messages: Message[], plans: PlanSnapshot[]): Todo[] | null {\n if (plans.length === 0) return null;\n let lastSeq = -1;\n for (const m of messages) {\n if (isOptimistic(m)) continue;\n if (m.seq > lastSeq) lastSeq = m.seq;\n }\n let found: Todo[] | null = null;\n for (const plan of plans) {\n if (plan.seq > lastSeq) found = plan.todos;\n }\n return found;\n}\n\n/**\n * One conversation, live: history, streamed messages, the agent's plan and\n * whether it is working right now.\n *\n * The id is yours to own — component state, a route param, whatever. Pass\n * `null` for the blank slate: nothing connects, and the first `send` creates the\n * conversation and hands it to `onCreated`.\n *\n * ```tsx\n * const [id, setId] = useState<string | null>(null);\n * const { messages, send } = useConversation(id, {\n * agentSlug: \"support\",\n * onCreated: (c) => setId(c.id),\n * });\n * ```\n */\nexport function useConversation(\n conversationId: string | null,\n options: UseConversationOptions = {},\n): UseConversationResult {\n const pageSize = options.pageSize ?? 50;\n const { agentSlug } = options;\n const declareTools = options.declareClientTools ?? true;\n const client = useAgentClient();\n const [conversation, setConversation] = useState<Conversation | null>(null);\n const [messages, setMessages] = useState<Message[]>([]);\n const [toolActivity, setToolActivity] = useState<ToolActivity[]>([]);\n const [plans, setPlans] = useState<PlanSnapshot[]>([]);\n const [todos, setTodos] = useState<Todo[]>([]);\n const [activity, setActivity] = useState<Activity>(IDLE);\n const [isLoading, setIsLoading] = useState(conversationId !== null);\n const [error, setError] = useState<Error | null>(null);\n const [streamError, setStreamError] = useState<Error | null>(null);\n const [isStreamLive, setIsStreamLive] = useState(false);\n const [isSending, setIsSending] = useState(false);\n const [hasOlder, setHasOlder] = useState(false);\n const [isLoadingOlder, setIsLoadingOlder] = useState(false);\n const [filesystemRevision, setFilesystemRevision] = useState(0);\n\n // Read through a ref so an inline arrow from the parent doesn't rebuild `send`\n // on every render.\n const onCreated = useRef(options.onCreated);\n onCreated.current = options.onCreated;\n\n // The id `send` just created. The effect below reads it to tell \"the parent\n // switched conversations\" (wipe the pane) from \"the parent adopted the one we\n // just made\" (keep what is already on screen).\n const adopting = useRef<string | null>(null);\n\n // The `seq` of the oldest event fetched so far — the cursor for the next page\n // back. A ref, not state: paging must not be restarted by a re-render, and\n // nothing renders from it.\n const oldestSeq = useRef<number | null>(null);\n // Guards against two concurrent `loadOlder` calls (a scroll handler firing\n // twice) fetching the same page and prepending it twice.\n const loadingOlder = useRef(false);\n\n // Highest `change_seq` folded into `messages`. The cache is only correct if\n // this and the messages are written together, so it rides along in a ref\n // rather than being recomputed at save time.\n const cursor = useRef<number | null>(null);\n const hasOlderRef = useRef(false);\n\n // Lets `send` roll back its optimistic message without depending on the\n // messages state, which would rebuild the callback on every frame.\n const pendingIds = useRef(new Set<string>());\n\n // Both bags are usually written inline in the parent's JSX, so their identity\n // changes every render while their content doesn't. Effects key on a string\n // built from the declarations and read the live objects through refs, which\n // is what keeps a re-render from re-declaring tools or restarting a session.\n const clientTools = useRef(options.clientTools);\n clientTools.current = options.clientTools;\n const components = useRef(options.components);\n components.current = options.components;\n const onClientToolError = useRef(options.onClientToolError);\n onClientToolError.current = options.onClientToolError;\n\n /**\n * The context is held, not sent.\n *\n * The server only ever reads it when a turn does, so a user browsing eleven\n * screens before typing anything needs one request, not eleven. `held` is the\n * latest value the app gave us; `sent` is what the server has. They differ\n * only between a navigation and the next moment the agent could possibly\n * look.\n *\n * Those moments are exactly two, and both flush below: just before a message\n * (it is about to start the turn that reads it) and any change while a turn is\n * running (the loop re-reads the context on every iteration, so a screen the\n * user opens mid-work still reaches the tool the agent is about to call).\n */\n const heldContext = useRef<string | null | undefined>(undefined);\n /**\n * Keyed by conversation, not a bare value: the first message *creates* the\n * conversation, so the flush that rides with it belongs to an id that did not\n * exist a moment earlier. Forgetting on every id change would re-send what was\n * just sent; forgetting nothing would starve a second conversation.\n */\n const sentContext = useRef<{ id: string; value: string | null } | null>(null);\n /** Read inside the effect below, which must not re-run when it changes. */\n const isTurnRunningRef = useRef(false);\n\n const flushContext = useCallback(\n async (id: string) => {\n const held = heldContext.current;\n if (held === undefined) return;\n const sent = sentContext.current;\n if (sent?.id === id && sent.value === held) return;\n sentContext.current = { id, value: held };\n try {\n await client.setContext(id, held);\n } catch {\n // Context is a nicety: an agent that misses one answers about the wrong\n // screen, which is better than a send that fails because a PUT did. The\n // value is left un-sent so the next flush tries again.\n sentContext.current = null;\n }\n },\n [client],\n );\n\n useEffect(() => {\n heldContext.current = options.context;\n if (conversationId !== null && isTurnRunningRef.current) void flushContext(conversationId);\n }, [options.context, conversationId, flushContext]);\n\n const toolsKey = useMemo(() => stableKey(options.clientTools), [options.clientTools]);\n\n /**\n * Every handler this conversation has ever been given, newest per name.\n *\n * Never pruned, and that is the point: `PUT /client-tools` replaces the whole\n * set, so an app whose tools follow the open screen narrows what the agent is\n * *told about* — but a call it issued a moment before that has to keep finding\n * its implementation. The runner reads this object at dispatch time, so the\n * identity stays put and the session survives a set that changes under it.\n */\n const handlers = useRef<Record<string, ClientTool<never, unknown>>>({});\n /** What the last successful declaration said, to skip a PUT that says it\n * again. */\n const declaredKey = useRef<{ id: string; key: string } | null>(null);\n\n useEffect(() => {\n Object.assign(handlers.current, options.clientTools ?? {});\n }, [options.clientTools]);\n\n /**\n * Declares the current set, when it differs from what the server was last\n * told.\n *\n * Held rather than sent on change, because the send is destructive: a tool\n * that leaves the set while a call of its own is in flight and unclaimed is\n * failed by the server's reaper. So this runs only where that cannot bite —\n * when the session starts, just before a message, and wherever the app calls\n * `syncClientTools` from inside a tool it is already running (that call holds\n * the lease, so its own result is never at risk).\n */\n const flushClientTools = useCallback(\n async (id: string) => {\n if (!declareTools || toolsKey === null) return;\n const last = declaredKey.current;\n if (last?.id === id && last.key === toolsKey) return;\n declaredKey.current = { id, key: toolsKey };\n try {\n await client.setClientTools(id, clientTools.current ?? {});\n } catch (err) {\n declaredKey.current = null;\n onClientToolError.current?.(err);\n }\n },\n [client, declareTools, toolsKey],\n );\n\n /** Declare now. For the one moment an app knows is safe and the hook cannot:\n * inside a tool that just moved the app, whose own call holds the lease. */\n const syncClientTools = useCallback(async () => {\n if (conversationId !== null) await flushClientTools(conversationId);\n }, [conversationId, flushClientTools]);\n // Held in a ref and keyed by its serialization, like `clientTools` above and\n // for the same reason: this is an array prop, so `componentLibraries={[\"x\"]}`\n // written inline in JSX -- which is what narrowing per screen looks like --\n // is a new identity on every render. In a dependency array that re-runs the\n // effect each time, and its cleanup aborts the in-flight PUT, so a conversation\n // that re-renders on every SSE frame would abort and re-issue forever, never\n // registering and eventually taking a 429. The key is what changes when the\n // list actually changes.\n const libraries = useRef(options.componentLibraries);\n libraries.current = options.componentLibraries;\n const librariesKey =\n options.componentLibraries === undefined ? null : JSON.stringify(options.componentLibraries);\n\n // The running client-tool runner, so the stream handlers can nudge it without\n // re-subscribing when it starts.\n const toolSession = useRef<ClientToolsSession | null>(null);\n // `${conversationId}\\u0000${componentsKey}` of the set already registered —\n // set by `target` too, so creating with components inline doesn't PUT twice.\n const registered = useRef<string | null>(null);\n\n useEffect(() => {\n const adopted = conversationId !== null && conversationId === adopting.current;\n adopting.current = null;\n\n if (!adopted) {\n setConversation(null);\n setMessages([]);\n setToolActivity([]);\n setTodos([]);\n setPlans([]);\n setActivity(IDLE);\n pendingIds.current.clear();\n }\n setError(null);\n // The stream state belongs to the conversation being left, not the one\n // arriving: a fatal from the old id must not be shown against the new one,\n // and nothing is connected until this effect's `onOpen`.\n setStreamError(null);\n setIsStreamLive(false);\n setHasOlder(false);\n setIsLoadingOlder(false);\n oldestSeq.current = null;\n loadingOlder.current = false;\n cursor.current = null;\n hasOlderRef.current = false;\n\n if (conversationId === null) {\n setIsLoading(false);\n return;\n }\n setIsLoading(true);\n\n const controller = new AbortController();\n let subscription: { close(): void } | null = null;\n let closed = false;\n\n /**\n * History first, then the stream from where that page ends.\n *\n * The order matters and is not a race: the server catches up everything\n * past `since` before going live, so an event committed between the two\n * calls still arrives. Subscribing first and paging after would be the racy\n * version — the page could then contain rows the stream had already sent\n * with no cursor to reconcile them by.\n */\n void (async () => {\n try {\n // Cached when this conversation has been opened before, which makes the\n // whole branch below free — the stream then delivers only the delta.\n const page = await client.loadHistory(conversationId, {\n pageSize,\n signal: controller.signal,\n });\n if (controller.signal.aborted) return;\n\n setMessages((prev) => mergeMessages(prev, page.messages));\n setToolActivity((prev) => mergeToolActivities([...prev, ...page.toolActivity]));\n setPlans((prev) => mergePlans(prev, page.plans));\n oldestSeq.current = page.oldestSeq;\n cursor.current = page.latestChangeSeq;\n hasOlderRef.current = page.hasOlder;\n setHasOlder(page.hasOlder);\n setIsLoading(false);\n\n if (closed) return;\n subscription = client.subscribe(\n conversationId,\n {\n // Both nudge the client-tool runner instead of it holding a second\n // stream: `onOpen` covers what happened while we were away (a call\n // can predate the connection, or a reconnect can swallow its\n // frame), `onClientToolCall` covers everything after.\n onOpen: () => {\n setIsStreamLive(true);\n toolSession.current?.poke();\n },\n onClientToolCall: () => toolSession.current?.poke(),\n // A failed attempt only means \"not connected right now\": the SDK is\n // already backing off toward the next one.\n onError: () => setIsStreamLive(false),\n // The loop gave up. Nothing else will arrive, so say so rather than\n // leaving a view that looks live and never moves again.\n onFatal: (err) => {\n setIsStreamLive(false);\n setStreamError(err instanceof Error ? err : new Error(String(err)));\n },\n // The log is the only place a file change is announced: it has no\n // curated projection, because a listing is a request away and\n // mirroring the tree here would be a cache to invalidate.\n onEvent: (event) => {\n if (event.type === \"filesystem_root\") setFilesystemRevision((n) => n + 1);\n },\n onToolActivity: (activity) => {\n setToolActivity((prev) => mergeToolActivities([...prev, activity]));\n },\n onMessage: (message) => {\n pendingIds.current.delete(optimisticIdFor(message));\n setMessages((prev) => mergeMessages(dropOptimisticEcho(prev, message), [message]));\n setIsLoading(false);\n },\n onTodos: (todos, seq) => {\n setTodos(todos);\n setPlans((prev) => mergePlans(prev, [{ todos, seq }]));\n },\n onActivity: setActivity,\n onConversation: setConversation,\n onCursor: (changeSeq) => {\n if (cursor.current === null || changeSeq > cursor.current) {\n cursor.current = changeSeq;\n }\n },\n },\n // `?? undefined` on an empty conversation: with no events yet there is\n // nothing to skip, and the stream should deliver from the start.\n { since: page.latestChangeSeq ?? undefined },\n );\n } catch (err: unknown) {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n }\n })();\n\n return () => {\n closed = true;\n controller.abort();\n subscription?.close();\n };\n }, [client, conversationId, pageSize]);\n\n // What `saveHistory` should record, kept fresh so the unmount path below can\n // read it without depending on state.\n const snapshot = useRef({\n messages,\n toolActivity,\n plans,\n oldestSeq,\n cursor,\n hasOlderRef,\n });\n snapshot.current.messages = messages;\n snapshot.current.toolActivity = toolActivity;\n snapshot.current.plans = plans;\n\n const persist = useCallback(\n (id: string) => {\n const { messages: held, toolActivity: trail, plans: planned } = snapshot.current;\n void client.saveHistory(id, {\n toolActivity: trail,\n plans: planned,\n // The optimistic copy carries a placeholder id and MAX_SAFE_INTEGER as\n // its seq. Cached, it would outlive the send and sit at the bottom of\n // the transcript forever.\n messages: held.filter((m) => !m.id.startsWith(\"optimistic:\")),\n oldestSeq: oldestSeq.current,\n latestChangeSeq: cursor.current,\n hasOlder: hasOlderRef.current,\n });\n },\n [client],\n );\n\n // Debounced: a single turn can append a dozen messages in a second, and a\n // persistent adapter should not pay a write for each.\n useEffect(() => {\n if (conversationId === null || messages.length === 0) return;\n const timer = setTimeout(() => persist(conversationId), 400);\n return () => clearTimeout(timer);\n }, [conversationId, messages, persist]);\n\n // Switching conversations cancels that timer, so without this a chat the user\n // opened and left quickly would never be cached.\n useEffect(() => {\n if (conversationId === null) return;\n return () => persist(conversationId);\n }, [conversationId, persist]);\n\n // Enabling the libraries is what makes the agent aware of them; the server\n // refuses any component they don't resolve to. Skipped when the option is\n // absent, so an app that doesn't use components never pays for a request —\n // pass `[]` to deliberately clear a list enabled earlier.\n useEffect(() => {\n const current = libraries.current;\n if (conversationId === null || librariesKey === null || current === undefined) return;\n const stamp = `${conversationId}\\u0000${librariesKey}`;\n if (registered.current === stamp) return;\n\n const controller = new AbortController();\n void client\n .setComponentLibraries(conversationId, current, controller.signal)\n .then((enabled) => {\n registered.current = stamp;\n warnMissingRenderers(enabled.tags, components.current);\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err : new Error(String(err)));\n });\n return () => controller.abort();\n }, [client, conversationId, librariesKey]);\n\n /** Whether this app serves client tools at all — a boolean, so the session\n * below is not restarted every time the set itself changes. */\n const servesTools = options.clientTools !== undefined;\n /** Same reason as `onClientToolError` above: the session effect must not list\n * a callback whose identity moves with the tool set, or every navigation\n * restarts the runner and drops the claim on whatever it is running. */\n const flushToolsRef = useRef(flushClientTools);\n flushToolsRef.current = flushClientTools;\n\n // One runner per open conversation, driven by the subscription above. It\n // declares on start, which is what converges a set that changed while this\n // conversation was closed.\n useEffect(() => {\n if (conversationId === null || !servesTools) return;\n\n let session: ClientToolsSession | null = null;\n let cancelled = false;\n void (async () => {\n try {\n const started = await client.serveClientTools(conversationId, {\n // The live object, not a copy: a set that changes while a call is in\n // flight must not cost the runner its handler.\n tools: handlers.current,\n watch: false,\n // The hook declares, so there is one place that decides *when* — see\n // `flushClientTools`.\n declare: false,\n onError: (err) => onClientToolError.current?.(err),\n });\n void flushToolsRef.current(conversationId);\n if (cancelled) {\n started.stop();\n return;\n }\n session = started;\n toolSession.current = started;\n // The stream is already open by now, so its `onOpen` has come and gone:\n // catch up here instead of waiting for the next call.\n started.poke();\n } catch (err) {\n if (!cancelled) onClientToolError.current?.(err);\n }\n })();\n\n return () => {\n cancelled = true;\n toolSession.current = null;\n session?.stop();\n };\n // `toolsKey` is deliberately absent: the runner reads `handlers` live, so a\n // set that changes needs a declaration, not a new session — restarting one\n // mid-turn drops the claim on whatever it is running.\n }, [client, conversationId, servesTools]);\n\n const loadOlder = useCallback(async () => {\n if (conversationId === null || loadingOlder.current) return 0;\n const before = oldestSeq.current;\n if (before === null) return 0;\n\n loadingOlder.current = true;\n setIsLoadingOlder(true);\n try {\n const page = await client.listMessagesPage(conversationId, {\n before,\n limit: pageSize,\n });\n // `merge` sorts by seq, so a prepend needs no special handling — and a\n // live message that arrived mid-fetch keeps its place.\n setMessages((prev) => mergeMessages(prev, page.messages));\n setToolActivity((prev) => mergeToolActivities([...prev, ...page.toolActivity]));\n setPlans((prev) => mergePlans(prev, page.plans));\n if (page.oldestSeq !== null) oldestSeq.current = page.oldestSeq;\n hasOlderRef.current = page.hasOlder;\n setHasOlder(page.hasOlder);\n return page.messages.length;\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n return 0;\n } finally {\n loadingOlder.current = false;\n setIsLoadingOlder(false);\n }\n }, [client, conversationId, pageSize]);\n\n /** The conversation to send into, created on the blank slate. */\n const target = useCallback(async () => {\n if (conversationId !== null) return conversationId;\n // The libraries ride the create call, and the tools are declared before this\n // returns, because the caller posts a message the moment it does — and the\n // effects that would otherwise do both only run a render later. A tool the\n // agent wasn't told about can't be called in the turn that follows.\n const created = await client.createConversation({\n agentSlug,\n ...(libraries.current === undefined ? {} : { componentLibraries: libraries.current }),\n });\n if (libraries.current !== undefined && librariesKey !== null) {\n registered.current = `${created.id}\\u0000${librariesKey}`;\n // The create response doesn't carry the resolved tags, so the check that\n // the enabling PUT does runs here off its own read.\n void client\n .listComponentLibraries(created.id)\n .then((enabled) => warnMissingRenderers(enabled.tags, components.current))\n .catch(() => {});\n }\n // Skipped when the app declares its own set: it will do so before the\n // message that follows this call, and declaring everything here first would\n // hand the agent tools the open screen does not offer.\n if (clientTools.current !== undefined && declareTools) {\n await client.setClientTools(created.id, clientTools.current);\n }\n // Recorded before the parent re-renders with the new id, so the effect knows\n // the messages on screen already belong to this conversation.\n adopting.current = created.id;\n setConversation(created);\n await onCreated.current?.(created);\n return created.id;\n }, [client, conversationId, agentSlug, librariesKey, declareTools]);\n\n const send = useCallback(\n async (content: string) => {\n const text = content.trim();\n if (!text) return;\n\n const optimistic: Message = {\n id: `optimistic:${text}`,\n role: \"user\",\n content: text,\n attachments: [],\n // Sorts after everything received so far; the server's echo replaces it\n // with the real seq.\n seq: Number.MAX_SAFE_INTEGER,\n at: new Date().toISOString(),\n };\n pendingIds.current.add(optimistic.id);\n setMessages((prev) => mergeMessages(prev, [optimistic]));\n setIsSending(true);\n setError(null);\n // The turn is queued the moment the POST lands, and the meta stream can\n // take a beat to say so. Without this the composer looks ignored.\n setActivity((prev) => ({ ...prev, hasPendingTurn: true }));\n\n try {\n const id = await target();\n await flushContext(id);\n await flushClientTools(id);\n await client.sendMessage(id, text);\n } catch (err) {\n pendingIds.current.delete(optimistic.id);\n setMessages((prev) => prev.filter((m) => m.id !== optimistic.id));\n setActivity((prev) => ({ ...prev, hasPendingTurn: false }));\n const wrapped = err instanceof Error ? err : new Error(String(err));\n setError(wrapped);\n throw wrapped;\n } finally {\n setIsSending(false);\n }\n },\n [client, target, flushContext, flushClientTools],\n );\n\n const sendAudio = useCallback(\n async (audio: Blob) => {\n if (audio.size === 0) return;\n setIsSending(true);\n setError(null);\n setActivity((prev) => ({ ...prev, hasPendingTurn: true }));\n try {\n const id = await target();\n await flushContext(id);\n await flushClientTools(id);\n await client.sendAudio(id, audio);\n } catch (err) {\n setActivity((prev) => ({ ...prev, hasPendingTurn: false }));\n const wrapped = err instanceof Error ? err : new Error(String(err));\n setError(wrapped);\n throw wrapped;\n } finally {\n setIsSending(false);\n }\n },\n [client, target, flushContext, flushClientTools],\n );\n\n const sendImages = useCallback(\n async (images: Array<{ image: Blob; filename?: string; label?: string }>, caption?: string) => {\n if (images.length === 0) return;\n setIsSending(true);\n setError(null);\n setActivity((prev) => ({ ...prev, hasPendingTurn: true }));\n try {\n const id = await target();\n await flushContext(id);\n await flushClientTools(id);\n await client.sendImages(id, images, { caption });\n } catch (err) {\n setActivity((prev) => ({ ...prev, hasPendingTurn: false }));\n const wrapped = err instanceof Error ? err : new Error(String(err));\n setError(wrapped);\n throw wrapped;\n } finally {\n setIsSending(false);\n }\n },\n [client, target, flushContext, flushClientTools],\n );\n\n const listFiles = useCallback(\n async (path?: string) => {\n if (conversationId === null) return [];\n const dir = await client.listFiles(conversationId, { path });\n return dir.entries;\n },\n [client, conversationId],\n );\n\n const readFile = useCallback(\n async (path: string) => {\n if (conversationId === null) {\n throw new Error(\"There is no conversation yet, so there are no files to read.\");\n }\n return await client.readFile(conversationId, path);\n },\n [client, conversationId],\n );\n\n const writeFiles = useCallback(\n async (files: Array<{ path: string; file: Blob; filename?: string }>) => {\n if (files.length === 0) return;\n const id = await target();\n await client.writeFiles(id, files);\n // The stream reports the change too, but only for a conversation this\n // hook is already subscribed to — on the blank slate the subscription is\n // still being set up, and the app would be left waiting for a bump that\n // arrived before it was listening.\n setFilesystemRevision((n) => n + 1);\n },\n [client, target],\n );\n\n const deleteFile = useCallback(\n async (path: string) => {\n if (conversationId === null) return;\n await client.deleteFile(conversationId, path);\n setFilesystemRevision((n) => n + 1);\n },\n [client, conversationId],\n );\n\n const moveFile = useCallback(\n async (from: string, to: string) => {\n if (conversationId === null) return;\n await client.moveFile(conversationId, from, to);\n setFilesystemRevision((n) => n + 1);\n },\n [client, conversationId],\n );\n\n const steer = useCallback(\n async (content: string) => {\n const text = content.trim();\n if (!text || conversationId === null) return;\n await client.steer(conversationId, text);\n },\n [client, conversationId],\n );\n\n const trailFor = useCallback(\n (message: Message) => trailOf(message, messages, toolActivity),\n [messages, toolActivity],\n );\n\n const pendingTrail = useMemo(\n () => trailAfterLast(messages, toolActivity),\n [messages, toolActivity],\n );\n\n const planFor = useCallback(\n (message: Message) => planOf(message, messages, plans),\n [messages, plans],\n );\n\n const pendingPlan = useMemo(() => planAfterLast(messages, plans), [messages, plans]);\n\n const isTurnRunning = useMemo(() => turnIsRunning(messages, activity), [messages, activity]);\n isTurnRunningRef.current = isTurnRunning;\n\n const renderMessage = useCallback(\n (message: Message) =>\n renderBlocks(message, {\n components: components.current,\n renderMarkdown: options.renderMarkdown,\n wrapBlock: options.wrapBlock,\n }),\n // Rebuilt when the set changes so a message already on screen picks up a\n // newly registered component; `renderMarkdown` is read fresh either way.\n [options.renderMarkdown, options.wrapBlock],\n );\n\n return useMemo(\n () => ({\n conversation,\n messages,\n toolActivity,\n trailFor,\n pendingTrail,\n planFor,\n pendingPlan,\n todos,\n activity,\n isTurnRunning,\n isLoading,\n hasOlder,\n isLoadingOlder,\n loadOlder,\n error,\n streamError,\n isStreamLive,\n send,\n sendAudio,\n sendImages,\n steer,\n isSending,\n renderMessage,\n syncClientTools,\n filesystemRevision,\n listFiles,\n readFile,\n writeFiles,\n deleteFile,\n moveFile,\n }),\n [\n conversation,\n messages,\n toolActivity,\n trailFor,\n pendingTrail,\n planFor,\n pendingPlan,\n todos,\n activity,\n isTurnRunning,\n isLoading,\n hasOlder,\n isLoadingOlder,\n loadOlder,\n error,\n streamError,\n isStreamLive,\n send,\n sendAudio,\n sendImages,\n steer,\n isSending,\n renderMessage,\n syncClientTools,\n filesystemRevision,\n listFiles,\n readFile,\n writeFiles,\n deleteFile,\n moveFile,\n ],\n );\n}\n\nfunction optimisticIdFor(message: Message): string {\n return `optimistic:${message.content.trim()}`;\n}\n\n/** Drops the placeholder once the real one arrives, matched on the text the user\n * typed — the server assigns the id, so there is nothing else to match on. */\nfunction dropOptimisticEcho(messages: Message[], real: Message): Message[] {\n if (real.role !== \"user\") return messages;\n const echoId = optimisticIdFor(real);\n return messages.filter((m) => m.id !== echoId);\n}\n\n/** Warn about tags the agent may now write that this app has no renderer for.\n *\n * One-directional on purpose: a renderer with no tag is how older messages keep\n * drawing after a library stopped offering the component, so it is normal. The\n * other way round is a block the agent will write and nothing will draw — a\n * silent gap in a reply, and this is the only moment the enabled catalog and the\n * component map are both in hand. */\nfunction warnMissingRenderers(tags: string[], components: ComponentMap | undefined): void {\n const missing = tags.filter((tag) => components?.[tag] === undefined);\n if (missing.length === 0) return;\n console.warn(\n `[cubos-agent] The enabled component libraries offer ${missing\n .map((t) => `<${t}>`)\n .join(\", \")}, which this app has no renderer for. The agent may use them ` +\n \"and nothing will be drawn. Add them to `components`, or drop the library \" +\n \"that declares them from `componentLibraries`.\",\n );\n}\n\n/** What an effect can compare across renders: everything about the tools except\n * the handlers, which are functions and change identity for free. Null when the\n * option is absent, which is how \"don't touch the set\" is expressed. */\nfunction stableKey(tools: Record<string, ClientTool<never, unknown>> | undefined): string | null {\n if (tools === undefined) return null;\n return JSON.stringify(\n Object.entries(tools).map(([name, tool]) => [\n name,\n tool.description,\n tool.inputSchema,\n tool.outputSchema,\n tool.readOnlyHint,\n tool.destructiveHint,\n tool.idempotentHint,\n tool.timeoutSeconds,\n ]),\n );\n}\n",
8
8
  "import type { Activity, ConversationEvent } from \"@cubos/agent-sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useAgentClient } from \"./context.js\";\n\nexport interface UseConversationEventsOptions {\n /**\n * Events per page. Counts events, not messages — the log holds the agent's\n * tool calls and bookkeeping too.\n */\n pageSize?: number;\n /** Fires for each event as it arrives, in stream order, after it has been\n * folded into `events`. For cross-cache effects, not for rendering. */\n onEvent?: (event: ConversationEvent) => void;\n}\n\nexport interface UseConversationEventsResult {\n /** Oldest first, deduped by id. A row that changes — a tentative event\n * consolidated, a delivery receipt — replaces the one held. */\n events: ConversationEvent[];\n /** The first page has landed. Distinguishes \"loading\" from \"empty\". */\n initialLoaded: boolean;\n /** There is older history behind what is held. */\n hasOlder: boolean;\n isLoadingOlder: boolean;\n /** Prepends the page before the oldest event held; resolves with how many\n * were added, so a caller can tell \"nothing came back\" from \"we reached the\n * beginning\". */\n loadOlder: () => Promise<number>;\n /**\n * Whether a turn is running, from the frames that ride this same stream —\n * so \"the turn ended\" can never overtake the events it ended with. Reading it\n * from anywhere else is what puts a finished turn on screen with no answer\n * in it.\n */\n activity: Activity;\n}\n\n/**\n * The conversation's event log, live.\n *\n * The companion to `useConversation` for a UI that draws the log itself rather\n * than a conversation — an operator console, an audit view, an activity tree.\n * Same connection and same guarantees; what differs is only how much is hidden:\n * `useConversation` hands you `Message`s, this hands you the rows.\n *\n * What it owns is the part that is easy to get subtly wrong, and that every app\n * writing this by hand has had to rediscover: the first page, then a stream that\n * resumes from that page's cursor so nothing lands twice and nothing is missed,\n * upsert-by-id because the server re-emits a row it mutated, and paging\n * backwards while events keep arriving at the other end.\n */\nexport function useConversationEvents(\n conversationId: string | null,\n options: UseConversationEventsOptions = {},\n): UseConversationEventsResult {\n const client = useAgentClient();\n const pageSize = options.pageSize ?? 50;\n\n const [events, setEvents] = useState<ConversationEvent[]>([]);\n const [initialLoaded, setInitialLoaded] = useState(false);\n const [hasOlder, setHasOlder] = useState(false);\n const [isLoadingOlder, setIsLoadingOlder] = useState(false);\n const [activity, setActivity] = useState<Activity>(IDLE);\n\n const oldestSeq = useRef<number | null>(null);\n const loadingOlder = useRef(false);\n // Identity rotates on every parent render, and a subscription keyed on it\n // would tear down the stream and lose whatever arrived in the gap.\n const onEvent = useRef(options.onEvent);\n onEvent.current = options.onEvent;\n\n useEffect(() => {\n setEvents([]);\n setInitialLoaded(false);\n setHasOlder(false);\n setIsLoadingOlder(false);\n setActivity(IDLE);\n oldestSeq.current = null;\n loadingOlder.current = false;\n\n if (conversationId === null) return;\n\n const controller = new AbortController();\n let subscription: { close(): void } | null = null;\n let closed = false;\n\n // Page first, then stream from where it ended. The other order is the racy\n // one: the page could then contain rows the stream had already delivered,\n // with no cursor to reconcile them by.\n void (async () => {\n try {\n const page = await client.listEventsPage(conversationId, {\n limit: pageSize,\n signal: controller.signal,\n });\n if (controller.signal.aborted) return;\n\n setEvents(page.events);\n oldestSeq.current = page.oldestSeq;\n setHasOlder(page.hasOlder);\n setInitialLoaded(true);\n\n if (closed) return;\n subscription = client.subscribe(\n conversationId,\n {\n onEvent: (event) => {\n setEvents((prev) => upsert(prev, event));\n onEvent.current?.(event);\n },\n onActivity: setActivity,\n },\n { since: page.latestChangeSeq ?? undefined },\n );\n } catch {\n if (!controller.signal.aborted) setInitialLoaded(true);\n }\n })();\n\n return () => {\n closed = true;\n controller.abort();\n subscription?.close();\n };\n }, [client, conversationId, pageSize]);\n\n const loadOlder = useCallback(async (): Promise<number> => {\n const before = oldestSeq.current;\n if (conversationId === null || before === null) return 0;\n if (loadingOlder.current || !hasOlder) return 0;\n loadingOlder.current = true;\n setIsLoadingOlder(true);\n try {\n const page = await client.listEventsPage(conversationId, { before, limit: pageSize });\n if (page.events.length === 0) {\n setHasOlder(false);\n return 0;\n }\n let added = 0;\n setEvents((prev) => {\n const held = new Set(prev.map((e) => e.id));\n const fresh = page.events.filter((e) => !held.has(e.id));\n added = fresh.length;\n return [...fresh, ...prev];\n });\n oldestSeq.current = page.oldestSeq ?? before;\n setHasOlder(page.hasOlder);\n return added;\n } catch {\n return 0;\n } finally {\n loadingOlder.current = false;\n setIsLoadingOlder(false);\n }\n }, [client, conversationId, hasOlder, pageSize]);\n\n return { events, initialLoaded, hasOlder, isLoadingOlder, loadOlder, activity };\n}\n\nconst IDLE: Activity = { isProcessing: false, hasPendingTurn: false };\n\n/** By id, not by append: the server re-emits a row it mutated — a tentative\n * event consolidated, a discard, a delivery receipt — and the replacement has\n * to land where the original was. */\nfunction upsert(held: ConversationEvent[], incoming: ConversationEvent): ConversationEvent[] {\n const at = held.findIndex((e) => e.id === incoming.id);\n if (at >= 0) {\n const next = held.slice();\n next[at] = incoming;\n return next;\n }\n const next = [...held, incoming];\n next.sort((a, b) => a.seq - b.seq);\n return next;\n}\n",
9
9
  "import type { Conversation } from \"@cubos/agent-sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useAgentClient } from \"./context.js\";\n\nexport interface UseConversationListResult {\n /** Newest activity first. */\n conversations: Conversation[];\n isLoading: boolean;\n error: Error | null;\n /** The live stream died and will not come back — the list still renders what\n * it loaded, but nothing will move again until the hook remounts. Transient\n * drops don't land here; the SDK reconnects through those. */\n streamError: Error | null;\n /** Null when there is nothing older to load. */\n hasMore: boolean;\n loadMore: () => Promise<void>;\n isLoadingMore: boolean;\n /** Starts an empty conversation and prepends it. Usually unnecessary —\n * `useConversation(null).send` creates one on the first message, which is what\n * keeps abandoned \"new chat\" rows out of the list. */\n create: (opts?: { agentSlug?: string; title?: string }) => Promise<Conversation>;\n archive: (id: string) => Promise<void>;\n}\n\nfunction upsert(list: Conversation[], incoming: Conversation): Conversation[] {\n const next = list.filter((c) => c.id !== incoming.id);\n next.push(incoming);\n return next.sort((a, b) => b.lastActivityAt.localeCompare(a.lastActivityAt));\n}\n\nexport interface UseConversationListOptions {\n /** Conversations per page. Defaults to the server's 30. */\n pageSize?: number;\n}\n\n/**\n * The user's conversations, kept live: any conversation whose activity advances\n * is re-sorted to the top without a refetch, and an archived one leaves the list\n * on its own.\n *\n * Selection is not its business — hold the open id yourself and pass it to\n * `useConversation`, so a route param works as well as component state.\n */\nexport function useConversationList(\n opts: UseConversationListOptions = {},\n): UseConversationListResult {\n const client = useAgentClient();\n const pageSize = opts.pageSize ?? 30;\n const [conversations, setConversations] = useState<Conversation[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [isLoadingMore, setIsLoadingMore] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [streamError, setStreamError] = useState<Error | null>(null);\n const cursor = useRef<string | null>(null);\n const [hasMore, setHasMore] = useState(false);\n\n useEffect(() => {\n const controller = new AbortController();\n setIsLoading(true);\n setStreamError(null);\n\n void client\n .listConversations({ limit: pageSize, signal: controller.signal })\n .then((page) => {\n if (controller.signal.aborted) return;\n setConversations(page.items);\n cursor.current = page.nextCursor;\n setHasMore(page.nextCursor !== null);\n setIsLoading(false);\n })\n .catch((err: unknown) => {\n if (controller.signal.aborted) return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n });\n\n // Archived conversations stay out of the list; the stream re-emits a\n // conversation on archive, so this also removes it live.\n const subscription = client.subscribeToConversations({\n onConversation: (conversation) => {\n setConversations((prev) =>\n conversation.archived\n ? prev.filter((c) => c.id !== conversation.id)\n : upsert(prev, conversation),\n );\n },\n onFatal: (err) => setStreamError(err instanceof Error ? err : new Error(String(err))),\n });\n\n return () => {\n controller.abort();\n subscription.close();\n };\n }, [client, pageSize]);\n\n const loadMore = useCallback(async () => {\n if (cursor.current === null) return;\n setIsLoadingMore(true);\n try {\n const page = await client.listConversations({ limit: pageSize, before: cursor.current });\n setConversations((prev) => {\n const seen = new Set(prev.map((c) => c.id));\n return [...prev, ...page.items.filter((c) => !seen.has(c.id))];\n });\n cursor.current = page.nextCursor;\n setHasMore(page.nextCursor !== null);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n setIsLoadingMore(false);\n }\n }, [client, pageSize]);\n\n const create = useCallback(\n async (createOpts: { agentSlug?: string; title?: string } = {}) => {\n const conversation = await client.createConversation(createOpts);\n setConversations((prev) => upsert(prev, conversation));\n return conversation;\n },\n [client],\n );\n\n const archive = useCallback(\n async (id: string) => {\n await client.archiveConversation(id);\n setConversations((prev) => prev.filter((c) => c.id !== id));\n },\n [client],\n );\n\n return {\n conversations,\n isLoading,\n isLoadingMore,\n error,\n streamError,\n hasMore,\n loadMore,\n create,\n archive,\n };\n}\n",
10
10
  "import type { Identity } from \"@cubos/agent-sdk\";\nimport { useEffect, useState } from \"react\";\nimport { useAgentClient } from \"./context.js\";\n\nexport interface UseIdentityResult {\n identity: Identity | null;\n isLoading: boolean;\n error: Error | null;\n}\n\n/** Who the current token acts as, and which agents it may talk to. Resolved once\n * per client and cached inside it. */\nexport function useIdentity(): UseIdentityResult {\n const client = useAgentClient();\n const [identity, setIdentity] = useState<Identity | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n let active = true;\n setIsLoading(true);\n setError(null);\n\n void client\n .me()\n .then((next) => {\n if (!active) return;\n setIdentity(next);\n setIsLoading(false);\n })\n .catch((err: unknown) => {\n if (!active) return;\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsLoading(false);\n });\n\n return () => {\n active = false;\n };\n }, [client]);\n\n return { identity, isLoading, error };\n}\n"
11
11
  ],
12
- "mappings": "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",
13
- "debugId": "AE29A8EC1333D43864756E2164756E21",
12
+ "mappings": "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",
13
+ "debugId": "C38847D70E438CE564756E2164756E21",
14
14
  "names": []
15
15
  }
@@ -1,4 +1,4 @@
1
- import { type Activity, type Block, type ClientTool, type Conversation, type Message, type PlanSnapshot, type Todo, type ToolActivity, type WorkspaceEntry } from "@cubos/agent-sdk";
1
+ import { type Activity, type Block, type ClientTool, type Conversation, type FilesystemEntry, type Message, type PlanSnapshot, type Todo, type ToolActivity } from "@cubos/agent-sdk";
2
2
  import { type ReactNode } from "react";
3
3
  import { type ComponentMap } from "./blocks.js";
4
4
  export interface UseConversationOptions {
@@ -249,13 +249,13 @@ export interface UseConversationResult {
249
249
  * since it already has the stream. Use it as a dependency:
250
250
  *
251
251
  * ```ts
252
- * useEffect(() => { void listFiles(path).then(setEntries) }, [listFiles, path, workspaceRevision])
252
+ * useEffect(() => { void listFiles(path).then(setEntries) }, [listFiles, path, filesystemRevision])
253
253
  * ```
254
254
  */
255
- workspaceRevision: number;
255
+ filesystemRevision: number;
256
256
  /** One directory, never recursive. Empty on the blank slate — there is no
257
257
  * conversation yet, and listing is not a reason to create one. */
258
- listFiles: (path?: string) => Promise<WorkspaceEntry[]>;
258
+ listFiles: (path?: string) => Promise<FilesystemEntry[]>;
259
259
  /** One file's bytes. */
260
260
  readFile: (path: string) => Promise<Blob>;
261
261
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cubos/agent-sdk-react",
3
- "version": "0.0.1142184",
3
+ "version": "0.0.1142284",
4
4
  "type": "module",
5
5
  "description": "Headless React hooks for the Cubos Agent conversation API. No DOM — works in React Native too.",
6
6
  "license": "SEE LICENSE IN LICENSE",