@agent-native/core 0.153.8 → 0.153.10

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.
Files changed (57) hide show
  1. package/corpus/templates/analytics/app/pages/DataSources.tsx +2 -1
  2. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +4 -2
  3. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -2
  4. package/corpus/templates/clips/app/root.tsx +4 -1
  5. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -2
  6. package/corpus/templates/clips/desktop/src/app.tsx +1 -1
  7. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +2 -1
  8. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
  9. package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -2
  10. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -3
  11. package/dist/cli/skills-content/help.d.ts +1 -1
  12. package/dist/cli/skills-content/help.js +2 -1
  13. package/dist/cli/skills.js +2 -1
  14. package/dist/client/NewWorkspaceAppFlow.js +4 -1
  15. package/dist/client/agent-page/AgentTabsPage.d.ts +2 -2
  16. package/dist/client/agent-page/AgentTabsPage.js +12 -9
  17. package/dist/client/extensions/ExtensionsSidebarSection.js +2 -1
  18. package/dist/client/hooks/index.d.ts +1 -1
  19. package/dist/client/hooks/index.js +1 -1
  20. package/dist/client/onboarding/FirstRunOnboarding.js +2 -1
  21. package/dist/client/org/TeamPage.js +14 -3
  22. package/dist/client/settings/SettingsPanel.js +26 -1
  23. package/dist/collab/routes.d.ts +1 -1
  24. package/dist/integrations/catalog.js +9 -8
  25. package/dist/notifications/routes.d.ts +1 -1
  26. package/dist/observability/routes.d.ts +3 -3
  27. package/dist/progress/routes.d.ts +1 -1
  28. package/dist/server/agent-chat-plugin.js +2 -2
  29. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  30. package/dist/server/onboarding-html.js +2 -1
  31. package/dist/server/realtime-token.d.ts +1 -1
  32. package/dist/shared/docs-url.d.ts +24 -0
  33. package/dist/shared/docs-url.js +39 -0
  34. package/dist/shared/index.d.ts +1 -0
  35. package/dist/shared/index.js +1 -0
  36. package/dist/templates/default/app/routes/_index.tsx +2 -1
  37. package/docs/content/client-advanced.mdx +200 -0
  38. package/docs/content/client-agent-chat.mdx +313 -0
  39. package/docs/content/client-data.mdx +432 -0
  40. package/docs/content/client-entry-points.mdx +27 -0
  41. package/docs/content/client-overview.mdx +206 -0
  42. package/docs/content/client-routing.mdx +121 -0
  43. package/docs/content/client-sync-internals.mdx +124 -0
  44. package/docs/content/context-awareness.mdx +1 -1
  45. package/docs/content/docs-components.mdx +201 -0
  46. package/docs/content/drop-in-agent.mdx +1 -1
  47. package/docs/content/internationalization.mdx +1 -1
  48. package/docs/content/key-concepts.mdx +4 -0
  49. package/docs/content/notifications.mdx +1 -1
  50. package/docs/content/progress.mdx +2 -2
  51. package/docs/content/real-time-collaboration.mdx +2 -2
  52. package/docs/content/template-chat-developers.mdx +1 -1
  53. package/docs/content/toolkit-command-navigation.mdx +1 -1
  54. package/package.json +3 -3
  55. package/src/templates/default/app/routes/_index.tsx +2 -1
  56. package/docs/content/client.mdx +0 -486
  57. package/docs/content/routing.mdx +0 -119
@@ -1,486 +0,0 @@
1
- ---
2
- title: "Client"
3
- description: "React hooks and utilities for agent-native apps: sendToAgentChat, optional agent chat context state, useDbSync, useAgentChatGenerating, and cn."
4
- ---
5
-
6
- # Client
7
-
8
- `@agent-native/core` provides React hooks and utilities for the browser-side of agent-native apps.
9
-
10
- These client/React APIs are exported from both `@agent-native/core` and `@agent-native/core/client`. Import them from `@agent-native/core/client` (the browser entry) for clarity and correct bundling, since the bare `@agent-native/core` root resolves to the Node build by default.
11
-
12
- For file-based routing — adding pages, dynamic params, and navigation — see [Routing](/docs/routing).
13
-
14
- On this page: [data hooks](#fetching-mutating), [sendToAgentChat](#sendtoagentchat),
15
- [agent chat context state](#agent-chat-context-state),
16
- [openAgentSettings](#openagentsettings), [askUserQuestion](#ask-user-question),
17
- [MCP App host bridge](#mcp-app-host-bridge),
18
- [dynamic suggestions](#dynamic-suggestions),
19
- [useAgentChatGenerating](#useagentchatgenerating), [useDbSync](#usedbsync),
20
- [change versions](#use-change-version), and [cn](#cn).
21
-
22
- ## Fetching and Mutating Data {#fetching-mutating}
23
-
24
- The primary way to read and write app data from the browser is through the action hooks. Never hand-write `fetch` calls to `/_agent-native/*` routes — use the named helpers instead (see [Actions](/docs/actions)).
25
-
26
- <Diagram id="doc-block-1agk0cd" title="The browser data loop" summary={"Hooks read and write through actions; useDbSync watches the database so agent and background writes refetch the same caches automatically."}>
27
-
28
- ```html
29
- <div class="diagram-client">
30
- <div class="diagram-col">
31
- <div class="diagram-node">
32
- useActionQuery<br /><small class="diagram-muted">cached read</small>
33
- </div>
34
- <div class="diagram-node">
35
- useActionMutation<br /><small class="diagram-muted"
36
- >write + invalidate</small
37
- >
38
- </div>
39
- </div>
40
- <div class="diagram-arrow diagram-muted" aria-hidden="true">&harr;</div>
41
- <div class="diagram-box" data-rough>
42
- Actions<br /><small class="diagram-muted">/_agent-native/actions/*</small>
43
- </div>
44
- <div class="diagram-arrow diagram-muted" aria-hidden="true">&harr;</div>
45
- <div class="diagram-panel" data-rough><strong>SQL database</strong></div>
46
- <div class="diagram-arrow diagram-muted" aria-hidden="true">&#8635;</div>
47
- <div class="diagram-pill ok">useDbSync &rarr; refetch on change</div>
48
- </div>
49
- ```
50
-
51
- ```css
52
- .diagram-client {
53
- display: flex;
54
- align-items: center;
55
- gap: 12px;
56
- flex-wrap: wrap;
57
- }
58
- .diagram-client .diagram-col {
59
- display: flex;
60
- flex-direction: column;
61
- gap: 10px;
62
- }
63
- .diagram-client .diagram-arrow {
64
- font-size: 22px;
65
- line-height: 1;
66
- }
67
- ```
68
-
69
- </Diagram>
70
-
71
- ```tsx
72
- import {
73
- useActionQuery,
74
- useActionMutation,
75
- callAction,
76
- } from "@agent-native/core/client/hooks";
77
- // Read: auto-cached, auto-invalidated on mutations
78
- const { data, isLoading } = useActionQuery("get-lead", { leadId });
79
-
80
- // Mutate: emits a change event so query caches refetch
81
- const { mutate, isPending } = useActionMutation("create-lead");
82
- mutate({ name: "Alice", company: "Acme" });
83
-
84
- // Imperative: for one-off calls outside a component
85
- await callAction("archive-lead", { leadId });
86
- ```
87
-
88
- ## sendToAgentChat(opts) {#sendtoagentchat}
89
-
90
- Send a message to the agent chat via postMessage — the common way to delegate an AI task from a UI interaction. Pass `context` for hidden model context and `submit: true` to send immediately, or `submit: false` to prefill a draft the user reviews first.
91
-
92
- ```ts
93
- import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
94
- // Auto-submit a prompt with hidden context
95
- sendToAgentChat({
96
- message: "Generate alt text for this image",
97
- context: "Image path: /api/projects/hero.jpg",
98
- submit: true,
99
- });
100
-
101
- // Prefill without submitting (user reviews first)
102
- sendToAgentChat({
103
- message: "Rewrite this in a conversational tone",
104
- context: selectedText,
105
- submit: false,
106
- });
107
- ```
108
-
109
- Inside an MCP App embed created with `embedApp()`, auto-submitted messages
110
- (`submit` omitted or `true`) are forwarded to the MCP App host bridge, which
111
- asks the containing host to add hidden context and send the visible user turn.
112
- `context` stays model-visible without being posted as user-facing chat.
113
- `submit: false` keeps the local prefill/review behavior because MCP Apps do not
114
- define a standard draft-prefill API. Internally this is the submitted-chat path
115
- sometimes surfaced as `agentNative.submitChat`; app code should call
116
- `sendToAgentChat()` rather than posting that event directly.
117
-
118
- ### Silent background sends {#background-send}
119
-
120
- Use `background: true` when a UI action should kick off real agent work without
121
- opening or focusing the sidebar. This still creates a normal chat thread/run,
122
- uses the agent's tools/actions/context, and keeps the work observable through
123
- the runs tray; it is not a raw one-shot model call.
124
-
125
- ```ts
126
- const tabId = sendToAgentChat({
127
- message: "Analyze this import and create any missing records",
128
- context: `Import batch id: ${batchId}`,
129
- submit: true,
130
- newTab: true,
131
- background: true,
132
- openSidebar: false,
133
- });
134
- ```
135
-
136
- `background` is intended to be paired with `newTab` so the hidden work does not
137
- overwrite the user's active conversation. Use the returned `tabId` if the UI
138
- needs to correlate follow-up status or deep-link into the run later.
139
-
140
- ### AgentChatMessage {#agentchatmessage}
141
-
142
- | Option | Type | Description |
143
- | --------------------- | ----------- | -------------------------------------------------------------------------- |
144
- | `message` | `string` | The visible prompt sent to the chat |
145
- | `context` | `string?` | Hidden context appended (not shown in chat UI) |
146
- | `submit` | `boolean?` | true = auto-submit, false = prefill only |
147
- | `newTab` | `boolean?` | Create a separate chat thread for this prompt |
148
- | `background` | `boolean?` | With `newTab`, run without focusing the tab and show the run in `RunsTray` |
149
- | `openSidebar` | `boolean?` | Set false to submit/prefill without opening the sidebar |
150
- | `projectSlug` | `string?` | Optional project slug for structured context |
151
- | `preset` | `string?` | Optional preset name for downstream consumers |
152
- | `referenceImagePaths` | `string[]?` | Optional reference image paths |
153
-
154
- ## Agent Chat Context State (Advanced) {#agent-chat-context-state}
155
-
156
- The context-state APIs are optional plumbing for UI that needs two-way sync with
157
- staged context chips: rendering the current staged items outside the composer,
158
- reflecting whether an item is already attached, or providing explicit
159
- remove/clear controls.
160
-
161
- Do not reach for these helpers for simple "send this to the agent" or
162
- "prefill this draft for review" flows. Use `sendToAgentChat()` with `context`
163
- and `submit` for those.
164
-
165
- | API | Use when |
166
- | --------------------------------- | ---------------------------------------------------------------------- |
167
- | `useAgentChatContext()` | A React component needs the live staged context list |
168
- | `setAgentChatContextItem(item)` | Imperative code should stage or replace one keyed context item |
169
- | `listAgentChatContext()` | Non-React code needs a one-time snapshot of staged context |
170
- | `removeAgentChatContextItem(key)` | UI should remove one staged context item by its stable `key` |
171
- | `clearAgentChatContext()` | UI should clear all staged context, such as after a view or mode reset |
172
- | `refreshAgentChatContext()` | Imperative code should re-read the latest persisted context snapshot |
173
-
174
- `useAgentChatContext()` returns `{ items, set, remove, clear, refresh }`.
175
-
176
- ## openAgentSettings(section?) {#openagentsettings}
177
-
178
- Use `openAgentSettings()` when an app settings page or setup card should open
179
- the agent sidebar's Settings tab. Pass a section id such as `"llm"`, `"secrets"`,
180
- `"automations"`, `"voice"`, or `"limits"` to open a specific section.
181
-
182
- ```ts
183
- import { openAgentSettings } from "@agent-native/core/client/navigation";
184
- openAgentSettings();
185
- openAgentSettings("secrets");
186
- ```
187
-
188
- Prefer this helper over dispatching `agent-panel:open-settings` directly.
189
-
190
- ```tsx
191
- import { useAgentChatContext } from "@agent-native/core/client/agent-chat";
192
- function SelectionContextButton({ record }: { record: { id: string } }) {
193
- const chatContext = useAgentChatContext();
194
- const contextKey = `selected-record:${record.id}`;
195
- const isAttached = chatContext.items.some((item) => item.key === contextKey);
196
-
197
- return (
198
- <button
199
- type="button"
200
- onClick={() => {
201
- if (isAttached) {
202
- chatContext.remove(contextKey);
203
- return;
204
- }
205
-
206
- chatContext.set({
207
- key: contextKey,
208
- title: "Selected Record",
209
- context: JSON.stringify(record, null, 2),
210
- openSidebar: false,
211
- });
212
- }}
213
- >
214
- {isAttached ? "Remove from prompt context" : "Add to prompt context"}
215
- </button>
216
- );
217
- }
218
- ```
219
-
220
- `listAgentChatContext()` is for imperative code that only needs to inspect the
221
- current staged items once. `clearAgentChatContext()` is intentionally broad; use
222
- `removeAgentChatContextItem(key)` when only one selection changed.
223
-
224
- ### AgentChatContextSetOptions {#agentchatcontextsetoptions}
225
-
226
- | Option | Type | Description |
227
- | ------------- | ---------- | ------------------------------------------------------ |
228
- | `key` | `string` | Stable identifier used to replace an existing nugget |
229
- | `title` | `string` | Short label shown in the composer chip |
230
- | `context` | `string` | Hidden context included with the next submitted prompt |
231
- | `openSidebar` | `boolean?` | Defaults to true; pass false to stage context silently |
232
-
233
- ## askUserQuestion(opts) {#ask-user-question}
234
-
235
- Ask the user a multiple-choice question from app code, render it inline in the
236
- agent panel, and **await their answer**. It's the client-side twin of the
237
- agent's built-in `ask-question` tool: it writes a `GuidedQuestionPayload` to the
238
- `"guided-questions"` application-state key (where the mounted
239
- `GuidedQuestionFlow` renders it) and reveals the agent panel so the question is
240
- visible. Unlike the agent tool — whose answer flows back to the agent —
241
- `askUserQuestion()` **resolves with the answer to the caller**, so the UI can
242
- branch on it.
243
-
244
- Use it when the UI needs exactly one small decision (2–4 options) before it
245
- kicks off agent work — instead of building a custom modal. Reach for the
246
- composer for freeform detail, and a form/popover for multi-field input.
247
-
248
- ```tsx
249
- import {
250
- askUserQuestion,
251
- sendToAgentChat,
252
- } from "@agent-native/core/client/agent-chat";
253
- const length = await askUserQuestion({
254
- question: "How long should this deck be?",
255
- header: "Deck length", // optional short chip/heading (≈12 chars)
256
- options: [
257
- { label: "Short (3–5 slides)", value: "short" },
258
- { label: "Medium (6–10 slides)", value: "medium", recommended: true },
259
- { label: "Long (11+ slides)", value: "long" },
260
- ],
261
- allowFreeText: false, // omit the "Other" free-text option (default adds it)
262
- allowMultiple: false, // single-select (default)
263
- });
264
-
265
- if (length) {
266
- sendToAgentChat({ message: `Generate a ${length} deck.`, submit: true });
267
- }
268
- ```
269
-
270
- Each option is `{ label, value?, description?, preview?, recommended? }`; `value`
271
- defaults to `label`, and `preview` renders a small mockup/code snippet under the
272
- option. The promise resolves with the selected `value` (or `value[]` when
273
- `allowMultiple`), the free-text string when the user picks "Other", or `null`
274
- if they skip — it stays pending until the user answers. Requires the agent panel
275
- to be mounted (it is in every template).
276
-
277
- The agent reaches the same UI through its `ask-question` tool: prefer letting the
278
- agent ask when _it_ hits a genuine fork it can't resolve from context; use
279
- `askUserQuestion()` when the _UI_ needs to gate an action on a choice.
280
-
281
- ## MCP App Host Bridge {#mcp-app-host-bridge}
282
-
283
- Routes embedded as MCP Apps should be URL-first: load the current artifact from
284
- path/query params, render the real React route or a focused shared component,
285
- and use the host bridge only for host-owned behavior. `@agent-native/core/client`
286
- exports the helpers embedded routes call:
287
-
288
- ```ts
289
- import {
290
- getMcpAppHostContext,
291
- openMcpAppHostLink,
292
- requestMcpAppDisplayMode,
293
- updateMcpAppModelContext,
294
- useMcpAppHostContext,
295
- } from "@agent-native/core/client/agent-chat";
296
- ```
297
-
298
- `getMcpAppHostContext()` reads the latest pushed host context snapshot;
299
- `useMcpAppHostContext()` subscribes React components to changes. The request
300
- helpers (`openMcpAppHostLink`, `requestMcpAppDisplayMode`,
301
- `updateMcpAppModelContext`) return `false` outside an embedded MCP App frame, or
302
- `Promise<boolean>` inside a frame. `sendToAgentChat()` uses the same bridge for
303
- auto-submitted prompts from embedded routes.
304
-
305
- The bridge itself — the `ui/*` JSON-RPC messages, the `agentNative.mcpHost.*`
306
- wrapper relay, transplant vs. controlled-frame rendering, host context, and
307
- display-mode requests — is owned by
308
- [External Agents](/docs/external-agents#mcp-app-bridge).
309
-
310
- ## Dynamic Suggestions {#dynamic-suggestions}
311
-
312
- `<AgentSidebar>`, `<AgentPanel>`, and `<AssistantChat>` merge static `suggestions` with context-aware suggestions by default. The framework reads `navigation`, `selection`, `pending-selection-context`, and the current URL from application state while an empty chat is visible, then offers prompt chips that match the current screen.
313
-
314
- ```tsx
315
- <AgentSidebar
316
- suggestions={["Summarize my inbox"]}
317
- dynamicSuggestions={{ max: 4 }}
318
- >
319
- <App />
320
- </AgentSidebar>
321
- ```
322
-
323
- Set `dynamicSuggestions={false}` to keep only static chips. Pass `getSuggestions` when an app wants deterministic domain-specific chips from the same application-state context.
324
-
325
- ## useAgentChatGenerating() {#useagentchatgenerating}
326
-
327
- React hook that wraps sendToAgentChat with loading state tracking:
328
-
329
- ```ts
330
- import {
331
- useAgentChatGenerating,
332
- } from "@agent-native/core/client/agent-chat"
333
- function GenerateButton() {
334
- const [isGenerating, send] = useAgentChatGenerating();
335
-
336
- return (
337
- <button
338
- disabled={isGenerating}
339
- onClick={() => send({
340
- message: "Generate a summary",
341
- context: documentContent,
342
- submit: true,
343
- })}
344
- >
345
- {isGenerating ? "Generating..." : "Generate"}
346
- </button>
347
- );
348
- }
349
- ```
350
-
351
- `isGenerating` turns true when you call `send()` and automatically resets to false when the agent finishes generating.
352
-
353
- ## useDbSync(options?) {#usedbsync}
354
-
355
- React hook (formerly `useFileWatcher`) that listens for database changes over SSE, falls back to polling, and invalidates the framework query caches that keep the UI aligned with agent writes:
356
-
357
- ```ts
358
- import {
359
- useDbSync,
360
- } from "@agent-native/core/client/hooks"
361
- import { useQueryClient } from "@tanstack/react-query";
362
-
363
- function App() {
364
- const queryClient = useQueryClient();
365
-
366
- useDbSync({
367
- queryClient,
368
- pollUrl: "/_agent-native/poll",
369
- onEvent: (data) => console.log("Data changed:", data),
370
- });
371
-
372
- return <div>...</div>;
373
- }
374
- ```
375
-
376
- ### Options {#usedbsync-options}
377
-
378
- | Option | Type | Description |
379
- | ------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
380
- | `queryClient` | `QueryClient?` | React-query client for cache invalidation |
381
- | `queryKeys` | `string[]?` | Deprecated and ignored; kept for old call sites |
382
- | `pollUrl` | `string?` | Poll endpoint URL. Default: `"/_agent-native/poll"` |
383
- | `sseUrl` | `string \| false?` | SSE endpoint URL. Default: `"/_agent-native/events"`; pass `false` to use polling only |
384
- | `interval` | `number?` | Polling interval in ms. Default: `2000` |
385
- | `fallbackInterval` | `number?` | Fallback polling interval when SSE is unavailable. Default: `60000` |
386
- | `pauseWhenHidden` | `boolean?` | Pause polling when the browser tab is hidden. Default: `false` — hidden tabs keep SSE connected and poll no faster than every 10s while active, 60s once idle |
387
- | `ignoreSource` | `string?` | Per-tab request source to ignore so a tab does not refetch from its own writes |
388
- | `onEvent` | `(data) => void` | Optional callback when SSE/polling receives a change event |
389
-
390
- For normal CRUD, prefer `useActionQuery` and `useActionMutation`; mutating actions emit `source: "action"` and those hooks refetch automatically.
391
-
392
- ## useChangeVersion / useChangeVersions {#use-change-version}
393
-
394
- The framework uses change versions to sync React Query caches with changes made by background agents, cron jobs, or other users.
395
-
396
- When any server-side database mutation occurs, the server records a change event with a specific `source` key. The client's `useDbSync` listener receives these events and bumps the local change version counter for that source. By folding the version counter into your React Query keys, queries automatically refetch whenever the backend notifies the client of new activity.
397
-
398
- - **`useChangeVersion(source: string): number`** — returns a counter that increments whenever the specified `source` is mutated.
399
- - **`useChangeVersions(sources: readonly string[]): number`** — returns the sum of version counters for multiple sources.
400
-
401
- ### Example: Syncing a raw query with the database
402
-
403
- ```tsx
404
- import { useQuery } from "@tanstack/react-query";
405
- import { useChangeVersion } from "@agent-native/core/client/hooks";
406
- function DashboardView({ id }) {
407
- // Get version for dashboards domain source
408
- const v = useChangeVersion("dashboards");
409
-
410
- const { data } = useQuery({
411
- queryKey: ["dashboard", id, v], // Invalidate automatically when version bumps
412
- queryFn: () => fetchDashboard(id),
413
- placeholderData: (prev) => prev, // Prevent layout flicker during refetch
414
- });
415
-
416
- return <div>{data?.title}</div>;
417
- }
418
- ```
419
-
420
- ### Latency Models & Invalidation Behavior
421
-
422
- - **UI-Initiated mutations:** When you execute an action from the UI using `useActionMutation`, the mutation immediately fires a local event with `source: "action"` on success. This triggers an **instant, optimistic refetch** of all query keys depending on that action, avoiding visual delay.
423
- - **Background or Agent Mutations:** When the AI agent, a webhook, or a background worker mutates data, the update is broadcast to the client. The client's `useDbSync` captures this either instantly over SSE (Server-Sent Events) or falls back to the **2-second polling tick**. The query key version then bumps, triggering a background refetch.
424
-
425
- <Diagram id="doc-block-pcrqt5" title="Two paths to a refetch" summary={"A local mutation invalidates its own caches instantly; a remote write reaches this tab over SSE, or the polling tick as a fallback."}>
426
-
427
- ```html
428
- <div class="diagram-latency">
429
- <div class="diagram-col">
430
- <div class="diagram-card" data-rough>
431
- <span class="diagram-pill ok">This tab</span
432
- ><strong>useActionMutation</strong
433
- ><small class="diagram-muted"
434
- >fires source: "action" on success &rarr; instant local refetch</small
435
- >
436
- </div>
437
- <div class="diagram-card" data-rough>
438
- <span class="diagram-pill accent">Agent · webhook · other tab</span
439
- ><strong>Remote write</strong
440
- ><small class="diagram-muted"
441
- >SSE push, or the ~2s polling tick as fallback &rarr; version bumps
442
- &rarr; background refetch</small
443
- >
444
- </div>
445
- </div>
446
- </div>
447
- ```
448
-
449
- ```css
450
- .diagram-latency .diagram-col {
451
- display: flex;
452
- flex-direction: column;
453
- gap: 12px;
454
- }
455
- .diagram-latency .diagram-card {
456
- display: flex;
457
- flex-direction: column;
458
- gap: 4px;
459
- padding: 14px 16px;
460
- }
461
- ```
462
-
463
- </Diagram>
464
-
465
- ## cn(...inputs) {#cn}
466
-
467
- Utility for merging class names (clsx + tailwind-merge):
468
-
469
- ```ts
470
- import {
471
- cn,
472
- } from "@agent-native/toolkit/utils"
473
- <div className={cn(
474
- "px-4 py-2 rounded",
475
- isActive && "bg-primary text-primary-foreground",
476
- className
477
- )} />
478
- ```
479
-
480
- ## What's next
481
-
482
- - [**Actions**](/docs/actions) — the `defineAction` surface behind every hook on this page
483
- - [**Routing**](/docs/routing) — file-based pages, dynamic params, and navigation
484
- - [**Real-Time Sync**](/docs/key-concepts#polling-sync) — how `useDbSync` and change versions keep the UI current
485
- - [**Context Awareness**](/docs/context-awareness) — the `navigation`/`selection` state dynamic suggestions read
486
- - [**Generative UI**](/docs/generative-ui) — building richer inline chat controls beyond `askUserQuestion`
@@ -1,119 +0,0 @@
1
- ---
2
- title: "Routing"
3
- description: "File-based routing for agent-native apps with React Router v8 — pages, dynamic params, and navigation."
4
- ---
5
-
6
- # Routing
7
-
8
- Agent-native apps use **React Router v8** with file-based routing via `flatRoutes()` from `@react-router/fs-routes`. Every file in `app/routes/` becomes a URL. Templates use the dot-notation convention — dots separate URL segments inside a single filename.
9
-
10
- ## File-Based Routing {#file-based-routing}
11
-
12
- ### File → URL mapping
13
-
14
- | File | URL | Notes |
15
- | --------------------- | ------------------ | -------------------------------------- |
16
- | `_index.tsx` | `/` | Index route |
17
- | `settings.tsx` | `/settings` | Simple page |
18
- | `inbox.$threadId.tsx` | `/inbox/:threadId` | Dot = `/`, `$` = dynamic param |
19
- | `_app.tsx` | (no URL segment) | Pathless layout — prefix with `_` |
20
- | `inbox/route.tsx` | `/inbox` | Folder form — `route.tsx` is the index |
21
-
22
- Prefix a segment with `$` for a dynamic param. Prefix with `_` to make it a pathless layout route (no URL segment). Templates use `flatRoutes()` — the dot-notation file above is primary; the nested-folder form `inbox/route.tsx` also works.
23
-
24
- <Diagram id="doc-block-19aa3pu" title="Pathless layout wraps the pages" summary={"A _app.tsx layout (no URL segment) renders the shared shell once; matched pages render inside its <Outlet/>, so the agent sidebar never remounts on navigation."}>
25
-
26
- ```html
27
- <div class="diagram-layout" data-rough>
28
- <div class="diagram-shell">
29
- <span class="diagram-pill accent">_app.tsx</span
30
- ><small class="diagram-muted"
31
- >pathless layout · persistent shell + agent sidebar</small
32
- >
33
- <div class="diagram-outlet" data-rough>
34
- <small class="diagram-muted">&lt;Outlet/&gt; — the matched page</small>
35
- <div class="diagram-row">
36
- <span class="diagram-pill">_index.tsx &rarr; /</span
37
- ><span class="diagram-pill">settings.tsx &rarr; /settings</span
38
- ><span class="diagram-pill"
39
- >inbox.$threadId.tsx &rarr; /inbox/:threadId</span
40
- >
41
- </div>
42
- </div>
43
- </div>
44
- </div>
45
- ```
46
-
47
- ```css
48
- .diagram-layout .diagram-shell {
49
- display: flex;
50
- flex-direction: column;
51
- gap: 8px;
52
- padding: 16px;
53
- }
54
- .diagram-layout .diagram-outlet {
55
- display: flex;
56
- flex-direction: column;
57
- gap: 8px;
58
- padding: 14px;
59
- margin-top: 6px;
60
- }
61
- .diagram-layout .diagram-row {
62
- display: flex;
63
- flex-wrap: wrap;
64
- gap: 8px;
65
- margin-top: 4px;
66
- }
67
- ```
68
-
69
- </Diagram>
70
-
71
- ## Adding a new page {#adding-a-page}
72
-
73
- Create the file and export a default component:
74
-
75
- ```tsx filename="app/routes/settings.tsx"
76
- export function meta() {
77
- return [{ title: "Settings" }];
78
- }
79
-
80
- export default function SettingsPage() {
81
- return <div>Settings</div>;
82
- }
83
- ```
84
-
85
- That's it — React Router picks it up automatically after the route table is
86
- rebuilt. If `pnpm dev` is already running, restart it after adding the file. No
87
- registration is needed.
88
-
89
- ## Dynamic params {#dynamic-params}
90
-
91
- ```tsx filename="app/routes/inbox.$threadId.tsx"
92
- import { useParams } from "react-router";
93
-
94
- export default function ThreadPage() {
95
- const { threadId } = useParams();
96
- return <div>Thread: {threadId}</div>;
97
- }
98
- ```
99
-
100
- ## Navigation {#navigation}
101
-
102
- Use `<Link>` for client-side navigation and `useNavigate()` for programmatic navigation:
103
-
104
- ```tsx
105
- import { Link, useNavigate } from "react-router";
106
-
107
- // In JSX
108
- <Link to="/settings">Settings</Link>;
109
-
110
- // Programmatic
111
- const navigate = useNavigate();
112
- navigate(`/inbox/${threadId}`);
113
- ```
114
-
115
- ## What's next
116
-
117
- - [**Client**](/docs/client) — the agent-native browser hooks and utilities
118
- - [**Server**](/docs/server-overview) — file-based server routes and the `/_agent-native/` namespace
119
- - [**Context Awareness**](/docs/context-awareness) — how the agent learns the current route and selection