@alexkroman1/aai-ui 6.10.1 → 7.0.0

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 (67) hide show
  1. package/README.md +104 -3
  2. package/dist/_run-controls.d.ts +33 -0
  3. package/dist/{chat-view-ByQFf94G.js → chat-view-BKsFfFZJ.js} +57 -11
  4. package/dist/client-config-B4nznRvH.js +134 -0
  5. package/dist/client-config.d.ts +45 -2
  6. package/dist/client-dir.d.ts +3 -1
  7. package/dist/client-dir.js +3 -1
  8. package/dist/components/auto-scroll.d.ts +24 -13
  9. package/dist/components/button.d.ts +8 -4
  10. package/dist/components/button.js +4 -4
  11. package/dist/components/chat-view.d.ts +4 -3
  12. package/dist/components/chat-view.js +1 -1
  13. package/dist/components/console-shell.d.ts +71 -10
  14. package/dist/components/controls.d.ts +15 -4
  15. package/dist/components/controls.js +48 -2
  16. package/dist/components/form-fields.d.ts +142 -0
  17. package/dist/components/form-types.d.ts +6 -0
  18. package/dist/components/form.d.ts +38 -80
  19. package/dist/components/markdown.d.ts +31 -5
  20. package/dist/components/message-list.d.ts +21 -4
  21. package/dist/components/message-list.js +1 -1
  22. package/dist/components/sidebar-layout.d.ts +11 -0
  23. package/dist/components/sidebar-layout.js +2 -0
  24. package/dist/components/start-screen.d.ts +8 -0
  25. package/dist/components/start-screen.js +2 -0
  26. package/dist/components/tool-call-block.js +1 -1
  27. package/dist/components/tool-call-row.d.ts +24 -0
  28. package/dist/components/upload-progress.d.ts +17 -7
  29. package/dist/components/workflow-fields.d.ts +10 -22
  30. package/dist/components/workflow-progress.d.ts +29 -10
  31. package/dist/context.d.ts +36 -0
  32. package/dist/context.js +105 -14
  33. package/dist/default-client/assets/index-S5fkKi6B.css +2 -0
  34. package/dist/default-client/assets/index-fEkrcZgo.js +293 -0
  35. package/dist/default-client/index.html +2 -2
  36. package/dist/define-client.d.ts +67 -62
  37. package/dist/define-client.js +56 -22
  38. package/dist/hooks.d.ts +88 -3
  39. package/dist/index.d.ts +12 -11
  40. package/dist/index.js +504 -248
  41. package/dist/internal.d.ts +40 -0
  42. package/dist/internal.js +6 -0
  43. package/dist/{message-list-CpPV7dGx.js → message-list-DHddO4QC.js} +217 -72
  44. package/dist/{session-core-CAfYmUbg.js → session-core-C2JtLArh.js} +267 -165
  45. package/dist/session-core-audio-setup.d.ts +3 -0
  46. package/dist/session-core-messages.d.ts +3 -0
  47. package/dist/session-core-state.d.ts +146 -0
  48. package/dist/session-core-types.d.ts +77 -32
  49. package/dist/session-core.d.ts +3 -2
  50. package/dist/session-core.js +1 -1
  51. package/dist/{tool-call-block-D6pTEPrT.js → tool-call-block-DoF-cSIZ.js} +27 -19
  52. package/dist/tool-config-context-DzAofqi_.js +19 -0
  53. package/dist/types.d.ts +31 -5
  54. package/dist/types.js +5 -4
  55. package/dist/{controls-CjG91QJ4.js → url-chips-DpM7Oocj.js} +3 -46
  56. package/dist/use-conversation.d.ts +122 -0
  57. package/dist/use-download-url.d.ts +83 -0
  58. package/dist/use-workflow-form.d.ts +59 -3
  59. package/dist/use-workflow-run.d.ts +35 -0
  60. package/dist/use-workflow-stream.d.ts +36 -62
  61. package/dist/workflow-client.d.ts +36 -11
  62. package/dist/workflow-status-labels.d.ts +35 -0
  63. package/package.json +10 -6
  64. package/styles.css +14 -0
  65. package/dist/_sse.d.ts +0 -56
  66. package/dist/default-client/assets/index-DTLrhtTF.css +0 -2
  67. package/dist/default-client/assets/index-DXODx_9r.js +0 -293
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @alexkroman1/aai-ui
2
2
 
3
- The browser client for aai voice agents: React 19 components, hooks, and a
3
+ The browser client for aai agents: React 19 components, hooks, and a
4
4
  framework-agnostic session core (WebSocket + microphone + playback).
5
5
 
6
6
  ```sh
@@ -11,7 +11,13 @@ Every agent gets this UI for free — `aai dev` and deployed agents serve a
11
11
  default client built from this package. Install it directly when the agent
12
12
  has its own `client.tsx`.
13
13
 
14
- ## A custom client
14
+ **Two front doors, two mounts.** A voice agent's page calls `client()` and
15
+ talks to a live session. A workflow app's page (`workflowApp()`, or
16
+ `agent({ page: "static" })`) calls `page()` and talks to the workflow HTTP
17
+ API — no session, no socket, no microphone. Both are still `client.tsx`,
18
+ still React, still the same theme tokens.
19
+
20
+ ## A custom voice client
15
21
 
16
22
  `client()` mounts the default chat shell with your sidebar, or replaces the
17
23
  whole UI with a custom component:
@@ -41,11 +47,106 @@ function OrderSidebar() {
41
47
  client({ sidebar: OrderSidebar });
42
48
  ```
43
49
 
50
+ ## A workflow app
51
+
52
+ `page()` mounts a form over the agent's workflows. `<WorkflowFields>` renders
53
+ one control per scalar property of the workflow's own input schema, so adding
54
+ a field to the schema adds it to the page:
55
+
56
+ ```tsx
57
+ import "@alexkroman1/aai-ui/styles.css";
58
+ import {
59
+ Form,
60
+ page,
61
+ SubmitButton,
62
+ UploadProgressBar,
63
+ useWorkflowSubmit,
64
+ WorkflowFields,
65
+ WorkflowProgress,
66
+ } from "@alexkroman1/aai-ui";
67
+
68
+ function App() {
69
+ const { submit, run, pending, upload, error } = useWorkflowSubmit("digest");
70
+ return (
71
+ <main className="mx-auto flex max-w-2xl flex-col gap-6 p-8">
72
+ <Form onSubmit={submit} error={error}>
73
+ <WorkflowFields workflow="digest" />
74
+ <SubmitButton pending={pending}>Summarize</SubmitButton>
75
+ </Form>
76
+ {/* The upload is its own wait: the run does not exist until the bytes
77
+ are in, so nothing else on the page can describe it. */}
78
+ <UploadProgressBar upload={upload} />
79
+ {/* What the run has SAID, from `report()` in its steps. */}
80
+ <WorkflowProgress runId={run?.runId} />
81
+ {run?.status === "completed" && <pre>{JSON.stringify(run.output)}</pre>}
82
+ </main>
83
+ );
84
+ }
85
+
86
+ page({ name: "Digest", component: App });
87
+ ```
88
+
89
+ `WorkflowOutputOf<typeof myWorkflow>` is what makes
90
+ `run.status === "completed"` narrow to a typed `run.output` — a type-only
91
+ import of `agent.ts` is erased, so it pulls no server code into the bundle.
92
+
93
+ ## What is in the package
94
+
95
+ The reference below groups by TypeScript kind. This is the same surface
96
+ grouped by what it is for.
97
+
98
+ **Mounts** — `client()`, `page()`, the config each takes and the handle each
99
+ returns (`ClientConfig`, `ClientHandle`, `PageConfig`, `PageHandle`).
100
+ `fetchClientConfig()` reads the agent's declared `name`/`greeting` on a page,
101
+ which `client()` does for itself.
102
+
103
+ **The live call** — `useSession()` for the whole snapshot,
104
+ `useSessionSelector()` for one slice, `useUserTranscript()` for the caller's
105
+ in-progress turn. `createSessionCore()` is the same session as a plain store,
106
+ for a non-React client. `SessionSnapshot`, `AgentState`, `ChatMessage`,
107
+ `ToolCallInfo`, `SessionError` and `SessionErrorCode` come with it.
108
+
109
+ **What the agent pushes** — `useAgentState()` (the `syncState` projection),
110
+ `useToolResult()` / `useToolCallStart()` (tool calls as they run), and
111
+ `useEvent()` (custom events from `ctx.send`).
112
+
113
+ **Chat chrome** — `ChatView` is the whole surface; `MessageList`, `Controls`,
114
+ `Markdown`, `ToolCallRow`, `StartScreen`, `SidebarLayout`, `AutoScroll` and
115
+ `Button` are its parts, exported so a custom chrome reuses them rather than
116
+ approximating them. `AutoScroll` is the one to reach for directly: it is the
117
+ only scroll-pinning implementation here, and it needs a **bounded height**.
118
+
119
+ **Forms** — `Form` collects typed values off the DOM on submit. `Field`,
120
+ `TextField`, `NumberField`, `TextAreaField`, `SelectField`, `CheckboxField`,
121
+ `FileField` and `SubmitButton` are the controls; `WorkflowFields` generates
122
+ them from a schema. A plain `<input name="x">` works identically.
123
+
124
+ **Workflows** — `createWorkflowApi()` is the client;
125
+ `useWorkflowSubmit()` / `useWorkflowStream()` (start one and watch it),
126
+ `useWorkflowRun()` (watch one by id), `useWorkflowRuns()` (the history),
127
+ `useWorkflowProgress()` (what a run has written), `useWorkflows()` (the
128
+ listing). `<WorkflowProgress>` and `<UploadProgressBar>` are the rendered
129
+ halves. `WorkflowRun`, `WorkflowSummary`, `WorkflowOutputOf` and
130
+ `isTerminal()` are the vocabulary.
131
+
132
+ **Theme** — `useTheme()` and `ClientTheme`.
133
+
134
+ ## Other subpaths
135
+
136
+ The root export is the whole client API. Two subpaths sit beside it, neither
137
+ of them something a `client.tsx` reaches for:
138
+
139
+ | Subpath | Reach for it when |
140
+ | --- | --- |
141
+ | `/client-dir` | serving the prebuilt default client from Node — `defaultClientDir()`, the filesystem path `createServer({ clientDir })` wants |
142
+ | `/internal` | never, from application code: the plumbing `client()` installs for itself (the session and theme providers, the default shell's URL chips, the tool-config context, the pre-connection lookup). Not a public API and not covered by semver |
143
+
44
144
  ## Hooks
45
145
 
46
146
  Inside components rendered by `client()`:
47
147
 
48
- - `useSession()` — connection state, transcript, `connect`/`disconnect`.
148
+ - `useSession()` — connection state, transcript, and the call controls:
149
+ `start`, `toggle`, `cancel`, `reset`, `resetState`, `disconnect`, `end`.
49
150
  - `useAgentState<T>()` — the agent's `syncState` projection, live.
50
151
  - `useToolResult(name, cb)` / `useToolCallStart(name, cb)` — observe tool
51
152
  calls as they run (e.g. to render a card per result).
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The two things a page does TO a run it started, bound to the run it has.
3
+ *
4
+ * `useWorkflowSubmit` and `useWorkflowStream` both hold a run id and neither
5
+ * handed it back, so a page that wanted "send it now" or "stop" had to hold an
6
+ * `api` of its own purely to write `api.wake(runId)` — which is the whole reason
7
+ * the two raw-primitive template pages keep a client at module scope. That is a
8
+ * page carrying the transport to make up for a hook withholding its own state.
9
+ *
10
+ * Both calls answer rather than fail when there is nothing to act on — `0`
11
+ * sleeps ended, `false` this call did not end it — which is the SDK's own
12
+ * contract for them (two tabs pressing Stop is ordinary), and it is what lets
13
+ * the no-run case be the same answer rather than a special one a caller has to
14
+ * branch on.
15
+ */
16
+ import type { WorkflowApi } from "./workflow-client.ts";
17
+ /** What {@link useRunControls} returns — see {@link WorkflowSubmission}. */
18
+ export type RunControls = {
19
+ /** End the run's `sleep()` early; resolves how many sleeps it interrupted. */
20
+ wake: () => Promise<number>;
21
+ /** Stop the run; resolves whether this call is what ended it. */
22
+ cancel: () => Promise<boolean>;
23
+ };
24
+ /**
25
+ * Bind `wake` and `cancel` to whatever run the hook is currently following.
26
+ *
27
+ * @param runId - The live run, or `undefined` before one exists.
28
+ * @param getClient - The stable getter from `useWorkflowApiRef`.
29
+ * @returns Two callbacks, stable while `runId` is.
30
+ *
31
+ * @internal
32
+ */
33
+ export declare function useRunControls(runId: string | undefined, getClient: () => WorkflowApi): RunControls;
@@ -1,9 +1,9 @@
1
- import { t as MessageList } from "./message-list-CpPV7dGx.js";
1
+ import { t as MessageList } from "./message-list-DHddO4QC.js";
2
2
  import { useSessionSelector, useTheme } from "./context.js";
3
3
  import { n as THINKING_COLOR, r as inkTint, t as ERROR_COLOR } from "./_colors-CcAi2FOU.js";
4
4
  import { t as AaiLogo } from "./aai-logo-9xRBGVFl.js";
5
5
  import { t as Eyebrow } from "./eyebrow-C6ZFuiz6.js";
6
- import { t as Controls } from "./controls-CjG91QJ4.js";
6
+ import { Controls } from "./components/controls.js";
7
7
  import clsx from "clsx";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region components/console-shell.tsx
@@ -28,13 +28,61 @@ function stateColor(state, primary, idle) {
28
28
  }
29
29
  }
30
30
  /**
31
- * The design-system "console" chrome for the chat shell:
32
- * a 760px column on the cream page with a header
33
- * (logo + live-status eyebrow), an optional error banner, the main content
34
- * on a raised white card, and a footer row beneath it.
31
+ * The design-system "console" chrome: a 760px column on the themed page with a
32
+ * header (icon + live-status eyebrow), an announced error banner, the main
33
+ * content on a raised card, and a footer row beneath it.
35
34
  *
35
+ * {@link ChatView} is this shell with `<MessageList>` inside it and
36
+ * `<Controls>` under it, and until now that was the only way to get it — the
37
+ * shell itself was internal, so a client wanting its own conversation markup
38
+ * had to rebuild the chrome as well. Each one that did re-derived the error
39
+ * banner WITHOUT `role="alert"`, which is the one part of this component a
40
+ * reviewer cannot see is missing: per the `fatalError` latch in
41
+ * `session-core.ts`, the banner is the only remaining signal once the state
42
+ * eyebrow goes back to reading like a live session, and a screen reader is
43
+ * never told an unannounced one appeared.
36
44
  *
37
- * @internal
45
+ * Reach for it when the conversation is yours and the frame is not. Reach for
46
+ * `<ChatView>` when both are ours.
47
+ *
48
+ * Must be rendered inside the providers `client()` installs.
49
+ *
50
+ * @example A custom conversation in the stock chrome
51
+ * ```tsx
52
+ * import {
53
+ * ConsoleShell,
54
+ * Controls,
55
+ * useConversation,
56
+ * useSessionSelector,
57
+ * } from "@alexkroman1/aai-ui";
58
+ *
59
+ * function Console() {
60
+ * const state = useSessionSelector((s) => s.state);
61
+ * const error = useSessionSelector((s) => s.error);
62
+ * const { items } = useConversation();
63
+ * return (
64
+ * <ConsoleShell
65
+ * title="Dispatch"
66
+ * state={state}
67
+ * pulsing={state === "listening"}
68
+ * error={error?.message}
69
+ * footer={<Controls />}
70
+ * >
71
+ * <ul>
72
+ * {items.map((item) => (
73
+ * <li key={item.kind === "message" ? item.message.id : item.toolCall.callId}>
74
+ * {item.kind === "message" ? item.message.content : item.toolCall.name}
75
+ * </li>
76
+ * ))}
77
+ * </ul>
78
+ * </ConsoleShell>
79
+ * );
80
+ * }
81
+ * ```
82
+ *
83
+ * @param props - See {@link ConsoleShellProps}.
84
+ *
85
+ * @public
38
86
  */
39
87
  function ConsoleShell({ icon, title, state, pulsing, error, children, footer, className }) {
40
88
  const theme = useTheme();
@@ -113,9 +161,7 @@ const PULSING_STATES = /* @__PURE__ */ new Set(["listening", "speaking"]);
113
161
  * }
114
162
  * ```
115
163
  *
116
- * @param icon - Optional element rendered in place of the logo in the header.
117
- * @param title - Optional title string for the header.
118
- * @param className - Additional CSS class names applied to the root element.
164
+ * @param props - Chat surface props.
119
165
  *
120
166
  * @public
121
167
  */
@@ -134,4 +180,4 @@ function ChatView({ icon, title, className }) {
134
180
  });
135
181
  }
136
182
  //#endregion
137
- export { ChatView as t };
183
+ export { ConsoleShell as n, ChatView as t };
@@ -0,0 +1,134 @@
1
+ import { CLIENT_CONFIG_PATH, ClientConfigResponseSchema } from "@alexkroman1/aai/protocol";
2
+ //#region client-config.ts
3
+ /**
4
+ * Pre-connection client-config lookup.
5
+ *
6
+ * `GET client-config` (relative to the agent's base URL — see
7
+ * `sdk/client-config.ts` in `@alexkroman1/aai`) gives the default client the
8
+ * agent's display name and greeting before any connection exists. For that
9
+ * use every failure path — network error, 404 from an older server,
10
+ * malformed body — degrades to the empty default (`fetchClientConfig`), so
11
+ * the lookup can never break an existing agent.
12
+ *
13
+ * The session's broker decision needs the opposite: `loadClientConfig`
14
+ * keeps "the lookup failed" (`null`) distinct from "the server answered and
15
+ * named no sessionUrl" (`{}`). See its doc comment.
16
+ */
17
+ /**
18
+ * Resolve a relative endpoint path against the agent's base URL.
19
+ *
20
+ * @internal
21
+ */
22
+ function buildAgentUrl(platformUrl, endpointPath) {
23
+ return new URL(endpointPath, platformUrl.endsWith("/") ? platformUrl : `${platformUrl}/`);
24
+ }
25
+ const AGENT_DEFAULT = {};
26
+ /**
27
+ * Per-attempt deadline for the `client-config` lookup.
28
+ *
29
+ * A request issued while the platform is restarting or saturated can HANG
30
+ * rather than fail — the proxy holds the socket open — and a browser fetch
31
+ * has no timeout of its own. Every other failure here is already handled
32
+ * (`null`, then the same-origin fallback), but a hang is not a failure: the
33
+ * promise simply never settles.
34
+ *
35
+ * That is unrecoverable rather than merely slow, because this lookup runs
36
+ * inside the session's WebSocket URL *provider*. partysocket awaits the
37
+ * provider under `_connectLock` and arms its own `connectionTimeout` only
38
+ * AFTER the URL resolves, so a hung lookup means no socket is ever
39
+ * constructed, no `error`/`close` ever fires, and none of the 10 reconnect
40
+ * attempts ever happen — the session sits on "connecting" forever, and stays
41
+ * there long after the server is back. Reproduced: zero sockets opened.
42
+ *
43
+ * A timed-out attempt therefore degrades exactly like any other failed one —
44
+ * `null`, so `serverIsBroker` stays unlatched and the attempt falls through
45
+ * to the same-origin `websocket` path, whose failure re-enters the normal
46
+ * backoff and re-fetches this on the next attempt.
47
+ *
48
+ * Sized well above the real work (one same-origin JSON GET that reads the
49
+ * agent's row) and well under a user's patience — the same 10s the studio's
50
+ * gating reads use for the identical hazard.
51
+ *
52
+ * @internal
53
+ */
54
+ const CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS = 1e4;
55
+ /**
56
+ * Fetch the agent's client config, reporting `null` when the lookup did not
57
+ * produce an answer (network error, non-2xx, unparsable body).
58
+ *
59
+ * The distinction from `fetchClientConfig` matters for exactly one caller:
60
+ * the session's per-attempt broker decision. A config that ARRIVED and named
61
+ * no `sessionUrl` means "this server is not a broker" (`aai dev`, an older
62
+ * server) — a durable fact worth latching. A lookup that FAILED means
63
+ * nothing about the server, and treating the two alike is how a single 503
64
+ * (a sandbox mid-boot, or one that failed to start) pinned a session to the
65
+ * platform's `/:slug/websocket` — a WebSocket redirect browsers don't
66
+ * follow, so every retry failed with no re-brokering even after the agent
67
+ * recovered.
68
+ *
69
+ * @internal
70
+ */
71
+ async function loadClientConfig(platformUrl, fetchFn) {
72
+ const doFetch = fetchFn ?? ((input, init) => globalThis.fetch(input, init));
73
+ try {
74
+ const resp = await doFetch(buildAgentUrl(platformUrl, CLIENT_CONFIG_PATH).href, { signal: AbortSignal.timeout(CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS) });
75
+ if (!resp.ok) return null;
76
+ const parsed = ClientConfigResponseSchema.safeParse(await resp.json());
77
+ return parsed.success ? parsed.data : null;
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+ /**
83
+ * Fetch the agent's declared `name`, `greeting` and front door; any failure
84
+ * yields the agent default (`{}`).
85
+ *
86
+ * **This is what a workflow app calls instead of receiving the config.**
87
+ * `client()` fetches `GET client-config` for itself before it renders the
88
+ * default chat shell, so a voice client never has to. `page()` mounts no
89
+ * session and makes no such request — deliberately, since a page has no shell
90
+ * to put a name in — so a page that wants the agent's own `name` or `greeting`
91
+ * asks for them here.
92
+ *
93
+ * Every failure path degrades to the empty default rather than throwing: a
94
+ * network error, a 404 from a server older than the endpoint, a malformed
95
+ * body, and a lookup that hangs past
96
+ * `CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS` all read as "the agent declared nothing".
97
+ * So a page may render straight from the result and never needs a `catch` —
98
+ * treat every field as optional, because an agent that declared none is a
99
+ * normal agent.
100
+ *
101
+ * @param platformUrl - The agent's base URL. On a deployed page that is the
102
+ * page's own origin and path (`location.origin + location.pathname`); the
103
+ * endpoint is resolved relative to it.
104
+ * @param fetchFn - Fetch implementation, for tests and for a caller that
105
+ * supplies its own credentials. Defaults to the global `fetch`.
106
+ * @returns The agent's config, or `{}` when the lookup produced no answer.
107
+ *
108
+ * @example
109
+ * ```tsx
110
+ * import { fetchClientConfig, page } from "@alexkroman1/aai-ui";
111
+ *
112
+ * const { name, greeting } = await fetchClientConfig(
113
+ * location.origin + location.pathname,
114
+ * );
115
+ *
116
+ * function App() {
117
+ * return (
118
+ * <main>
119
+ * <h1>{name ?? "Workflows"}</h1>
120
+ * {greeting ? <p>{greeting}</p> : null}
121
+ * </main>
122
+ * );
123
+ * }
124
+ *
125
+ * page({ name: name ?? "Workflows", component: App });
126
+ * ```
127
+ *
128
+ * @public
129
+ */
130
+ async function fetchClientConfig(platformUrl, fetchFn) {
131
+ return await loadClientConfig(platformUrl, fetchFn) ?? AGENT_DEFAULT;
132
+ }
133
+ //#endregion
134
+ export { fetchClientConfig as n, loadClientConfig as r, buildAgentUrl as t };
@@ -68,8 +68,51 @@ export declare const CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS = 10000;
68
68
  */
69
69
  export declare function loadClientConfig(platformUrl: string, fetchFn?: typeof globalThis.fetch): Promise<ClientConfigResponse | null>;
70
70
  /**
71
- * Fetch the agent's client config; any failure yields the agent default.
71
+ * Fetch the agent's declared `name`, `greeting` and front door; any failure
72
+ * yields the agent default (`{}`).
72
73
  *
73
- * @internal
74
+ * **This is what a workflow app calls instead of receiving the config.**
75
+ * `client()` fetches `GET client-config` for itself before it renders the
76
+ * default chat shell, so a voice client never has to. `page()` mounts no
77
+ * session and makes no such request — deliberately, since a page has no shell
78
+ * to put a name in — so a page that wants the agent's own `name` or `greeting`
79
+ * asks for them here.
80
+ *
81
+ * Every failure path degrades to the empty default rather than throwing: a
82
+ * network error, a 404 from a server older than the endpoint, a malformed
83
+ * body, and a lookup that hangs past
84
+ * `CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS` all read as "the agent declared nothing".
85
+ * So a page may render straight from the result and never needs a `catch` —
86
+ * treat every field as optional, because an agent that declared none is a
87
+ * normal agent.
88
+ *
89
+ * @param platformUrl - The agent's base URL. On a deployed page that is the
90
+ * page's own origin and path (`location.origin + location.pathname`); the
91
+ * endpoint is resolved relative to it.
92
+ * @param fetchFn - Fetch implementation, for tests and for a caller that
93
+ * supplies its own credentials. Defaults to the global `fetch`.
94
+ * @returns The agent's config, or `{}` when the lookup produced no answer.
95
+ *
96
+ * @example
97
+ * ```tsx
98
+ * import { fetchClientConfig, page } from "@alexkroman1/aai-ui";
99
+ *
100
+ * const { name, greeting } = await fetchClientConfig(
101
+ * location.origin + location.pathname,
102
+ * );
103
+ *
104
+ * function App() {
105
+ * return (
106
+ * <main>
107
+ * <h1>{name ?? "Workflows"}</h1>
108
+ * {greeting ? <p>{greeting}</p> : null}
109
+ * </main>
110
+ * );
111
+ * }
112
+ *
113
+ * page({ name: name ?? "Workflows", component: App });
114
+ * ```
115
+ *
116
+ * @public
74
117
  */
75
118
  export declare function fetchClientConfig(platformUrl: string, fetchFn?: typeof globalThis.fetch): Promise<ClientConfigResponse>;
@@ -10,6 +10,8 @@
10
10
  * serving the default UI has to write them — `aai-cli`'s dev server had its own
11
11
  * copy, as did every self-hosted example. Three places that would all silently
12
12
  * serve nothing if the build output moved.
13
+ *
14
+ * @module client-dir
13
15
  */
14
16
  /**
15
17
  * Absolute path to the prebuilt browser client's static files — pass it to
@@ -27,7 +29,7 @@
27
29
  * @example
28
30
  * ```ts
29
31
  * import { agent } from "@alexkroman1/aai";
30
- * import { createAgentServer } from "@alexkroman1/aai/runtime";
32
+ * import { createAgentServer } from "@alexkroman1/aai-runtime";
31
33
  * import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
32
34
  *
33
35
  * const server = createAgentServer({
@@ -13,6 +13,8 @@ import path from "node:path";
13
13
  * serving the default UI has to write them — `aai-cli`'s dev server had its own
14
14
  * copy, as did every self-hosted example. Three places that would all silently
15
15
  * serve nothing if the build output moved.
16
+ *
17
+ * @module client-dir
16
18
  */
17
19
  const require = createRequire(import.meta.url);
18
20
  /**
@@ -31,7 +33,7 @@ const require = createRequire(import.meta.url);
31
33
  * @example
32
34
  * ```ts
33
35
  * import { agent } from "@alexkroman1/aai";
34
- * import { createAgentServer } from "@alexkroman1/aai/runtime";
36
+ * import { createAgentServer } from "@alexkroman1/aai-runtime";
35
37
  * import { defaultClientDir } from "@alexkroman1/aai-ui/client-dir";
36
38
  *
37
39
  * const server = createAgentServer({
@@ -36,28 +36,39 @@ import type { CSSProperties, ReactNode } from "react";
36
36
  * }
37
37
  * ```
38
38
  *
39
- * @param children - The scrollable content.
40
- * @param className - Classes for the outer container. It must be given a
41
- * bounded height (`flex-1 min-h-0`, `h-full`, a fixed height) — an unbounded
42
- * one grows with its content and never scrolls, so nothing pins.
43
- * @param contentClassName - Classes for the inner content element, where
44
- * padding and the children's own layout belong.
45
- * @param scrollClassName - Classes for the scrolling element itself. Defaults
46
- * to hiding the scrollbar; pass `"overflow-y-auto"` to show a native one.
47
- * @param style - Inline styles for the outer container.
48
- * @param initial - Scroll behavior on mount. Defaults to `"instant"` (start at
49
- * the latest content without animating a scroll the reader did not ask for).
50
- * @param resize - Scroll behavior when pinned content grows. Defaults to
51
- * `"smooth"`.
39
+ * @param props - Scroll container props.
52
40
  *
53
41
  * @public
54
42
  */
55
43
  export declare function AutoScroll({ children, className, contentClassName, scrollClassName, style, initial, resize, }: {
44
+ /** The scrollable content. */
56
45
  children: ReactNode;
46
+ /**
47
+ * Classes for the outer container, appended to its own.
48
+ *
49
+ * **The container must end up with a bounded height** (`flex-1 min-h-0`,
50
+ * `h-full`, a fixed height). This is the one constraint callers get wrong:
51
+ * an unbounded container grows with its content and never scrolls, so
52
+ * nothing pins and the component silently does nothing.
53
+ */
57
54
  className?: string | undefined;
55
+ /**
56
+ * Classes for the inner content element, where padding and the children's
57
+ * own layout belong.
58
+ */
58
59
  contentClassName?: string | undefined;
60
+ /**
61
+ * Classes for the scrolling element itself. Defaults to hiding the
62
+ * scrollbar; pass `"overflow-y-auto"` to show a native one.
63
+ */
59
64
  scrollClassName?: string | undefined;
65
+ /** Inline styles for the outer container. */
60
66
  style?: CSSProperties | undefined;
67
+ /**
68
+ * Scroll behavior on mount. Defaults to `"instant"` — start at the latest
69
+ * content without animating a scroll the reader did not ask for.
70
+ */
61
71
  initial?: "instant" | "smooth" | undefined;
72
+ /** Scroll behavior when pinned content grows. Defaults to `"smooth"`. */
62
73
  resize?: "instant" | "smooth" | undefined;
63
74
  }): ReactNode;
@@ -39,16 +39,20 @@ export type ButtonSize = "default" | "lg";
39
39
  * }
40
40
  * ```
41
41
  *
42
- * @param variant - Visual style (`"default"` | `"secondary"` | `"ghost"`). Defaults to `"default"`.
43
- * @param size - Size preset (`"default"` | `"lg"`). Defaults to `"default"`.
44
- * @param className - Additional CSS class names.
45
- * @param children - Button label / content.
42
+ * @param props - Button props: `variant` (visual style see
43
+ * {@link ButtonVariant}, defaults to `"default"`), `size` (see
44
+ * {@link ButtonSize}, defaults to `"default"`), `className` (appended to the
45
+ * button's own classes), `children` (the label), and any `<button>` attribute.
46
46
  *
47
47
  * @public
48
48
  */
49
49
  export declare function Button({ variant, size, className, children, style, ...rest }: {
50
+ /** Visual style. Defaults to `"default"`. */
50
51
  variant?: ButtonVariant;
52
+ /** Size preset. Defaults to `"default"`. */
51
53
  size?: ButtonSize;
54
+ /** Additional CSS class names, appended to the button's own. */
52
55
  className?: string;
56
+ /** Button label / content. */
53
57
  children?: ReactNode;
54
58
  } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, "className">): import("react").JSX.Element;
@@ -53,10 +53,10 @@ function variantColors(variant, theme) {
53
53
  * }
54
54
  * ```
55
55
  *
56
- * @param variant - Visual style (`"default"` | `"secondary"` | `"ghost"`). Defaults to `"default"`.
57
- * @param size - Size preset (`"default"` | `"lg"`). Defaults to `"default"`.
58
- * @param className - Additional CSS class names.
59
- * @param children - Button label / content.
56
+ * @param props - Button props: `variant` (visual style see
57
+ * {@link ButtonVariant}, defaults to `"default"`), `size` (see
58
+ * {@link ButtonSize}, defaults to `"default"`), `className` (appended to the
59
+ * button's own classes), `children` (the label), and any `<button>` attribute.
60
60
  *
61
61
  * @public
62
62
  */
@@ -21,14 +21,15 @@ import type { ReactNode } from "react";
21
21
  * }
22
22
  * ```
23
23
  *
24
- * @param icon - Optional element rendered in place of the logo in the header.
25
- * @param title - Optional title string for the header.
26
- * @param className - Additional CSS class names applied to the root element.
24
+ * @param props - Chat surface props.
27
25
  *
28
26
  * @public
29
27
  */
30
28
  export declare function ChatView({ icon, title, className, }: {
29
+ /** Element rendered in place of the logo in the header. */
31
30
  icon?: ReactNode | undefined;
31
+ /** Title string for the header. Defaults to the agent's declared name. */
32
32
  title?: string | undefined;
33
+ /** Additional CSS class names for the root element, appended to its own. */
33
34
  className?: string | undefined;
34
35
  }): ReactNode;
@@ -1,3 +1,3 @@
1
1
  import "../context.js";
2
- import { t as ChatView } from "../chat-view-ByQFf94G.js";
2
+ import { t as ChatView } from "../chat-view-BKsFfFZJ.js";
3
3
  export { ChatView };