@alexkroman1/aai-ui 0.12.3 → 0.14.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.
- package/README.md +253 -0
- package/dist/_colors-CpZO-88A.js +78 -0
- package/dist/_module-url-C13kAJ87.js +15 -0
- package/dist/_page-shell.d.ts +11 -0
- package/dist/_recover-run.d.ts +79 -0
- package/dist/_repeat-until.d.ts +30 -0
- package/dist/_run-controls.d.ts +33 -0
- package/dist/_submission-state.d.ts +92 -0
- package/dist/_tap-to-talk-state.d.ts +88 -0
- package/dist/_utils-Mi-SCFA1.js +47 -0
- package/dist/_utils.d.ts +26 -0
- package/dist/_web-storage.d.ts +43 -0
- package/dist/_workflow-api-ref.d.ts +37 -0
- package/dist/_workflow-files.d.ts +34 -0
- package/dist/_workflow-test-defs.d.ts +34 -0
- package/dist/aai-logo-D8pMVn9_.js +85 -0
- package/dist/agent-state-labels.d.ts +65 -0
- package/dist/agent-state.d.ts +108 -0
- package/dist/audio/capture.d.ts +44 -0
- package/dist/audio/index.d.ts +18 -0
- package/dist/audio/index.js +431 -0
- package/dist/audio/preconnect.d.ts +87 -0
- package/dist/audio/voice-io.d.ts +112 -0
- package/dist/build-default-client.d.ts +1 -0
- package/dist/chat-view-BCbI7gD4.js +396 -0
- package/dist/client-config-C7fQrioM.js +139 -0
- package/dist/client-config.d.ts +119 -0
- package/dist/client-dir.d.ts +44 -0
- package/dist/client-dir.js +58 -0
- package/dist/client-identity.d.ts +131 -0
- package/dist/client-tool.d.ts +51 -0
- package/dist/components/_colors.d.ts +73 -0
- package/dist/components/_form-readiness.d.ts +56 -0
- package/dist/components/_form-values.d.ts +19 -0
- package/dist/components/aai-logo.d.ts +12 -0
- package/dist/components/audio-result.d.ts +94 -0
- package/dist/components/auto-scroll.d.ts +74 -0
- package/dist/components/bullet-list.d.ts +74 -0
- package/dist/components/button.d.ts +29 -21
- package/dist/components/button.js +62 -23
- package/dist/components/chat-view.d.ts +24 -18
- package/dist/components/chat-view.js +2 -60
- package/dist/components/console-shell.d.ts +85 -0
- package/dist/components/controls.d.ts +21 -6
- package/dist/components/controls.js +31 -19
- package/dist/components/conversation-view.d.ts +130 -0
- package/dist/components/eyebrow.d.ts +12 -0
- package/dist/components/facts.d.ts +81 -0
- package/dist/components/form-fields.d.ts +142 -0
- package/dist/components/form-types.d.ts +73 -0
- package/dist/components/form.d.ts +96 -0
- package/dist/components/markdown.d.ts +57 -0
- package/dist/components/message-list.d.ts +34 -7
- package/dist/components/message-list.js +2 -75
- package/dist/components/session-controls.d.ts +125 -0
- package/dist/components/session-error-banner.d.ts +69 -0
- package/dist/components/session-state-dot.d.ts +111 -0
- package/dist/components/sidebar-layout.d.ts +31 -10
- package/dist/components/sidebar-layout.js +32 -12
- package/dist/components/start-screen.d.ts +17 -6
- package/dist/components/start-screen.js +34 -14
- package/dist/components/tool-call-block.d.ts +15 -14
- package/dist/components/tool-call-block.js +2 -117
- package/dist/components/tool-call-row.d.ts +75 -0
- package/dist/components/tool-config-context.d.ts +22 -0
- package/dist/components/upload-progress.d.ts +76 -0
- package/dist/components/url-chips.d.ts +29 -0
- package/dist/components/workflow-fields.d.ts +46 -0
- package/dist/components/workflow-pending-note.d.ts +81 -0
- package/dist/components/workflow-progress.d.ts +81 -0
- package/dist/components/workflow-run-error.d.ts +48 -0
- package/dist/components/workflow-run-panel.d.ts +79 -0
- package/dist/context.d.ts +288 -0
- package/dist/context.js +382 -0
- package/dist/conversation-log.d.ts +88 -0
- package/dist/default-client/assets/_module-url-BX0RuRU2.js +1 -0
- package/dist/default-client/assets/audio-mpgipo2Q.js +1 -0
- package/dist/default-client/assets/capture-processor-D0q7Qrdd.js +90 -0
- package/dist/default-client/assets/index-8ihb1Urm.css +2 -0
- package/dist/default-client/assets/index-DPfmSFmc.js +126 -0
- package/dist/default-client/assets/playback-processor-CjDbc9iH.js +314 -0
- package/dist/default-client/favicon.ico +0 -0
- package/dist/default-client/index.html +15 -0
- package/dist/default-client-entry.d.ts +6 -0
- package/dist/default-client.d.ts +19 -0
- package/dist/define-client.d.ts +169 -26
- package/dist/define-client.js +209 -55
- package/dist/eyebrow-UfmSz9yy.js +27 -0
- package/dist/hooks-DN4gHYsj.js +182 -0
- package/dist/hooks.d.ts +172 -0
- package/dist/hooks.js +3 -0
- package/dist/hooks.test-d.d.ts +14 -0
- package/dist/inbox-protocol.d.ts +128 -0
- package/dist/inbox.d.ts +132 -0
- package/dist/index.d.ts +126 -29
- package/dist/index.js +5500 -15
- package/dist/internal.d.ts +43 -0
- package/dist/internal.js +8 -0
- package/dist/linked-client.d.ts +70 -0
- package/dist/message-list-DHZcLhRA.js +682 -0
- package/dist/notice-player.d.ts +70 -0
- package/dist/page.d.ts +114 -0
- package/dist/phone.d.ts +50 -0
- package/dist/route-fetch.d.ts +79 -0
- package/dist/session/audio-effects.d.ts +37 -0
- package/dist/session/audio-setup.d.ts +57 -0
- package/dist/session/audio-state.d.ts +157 -0
- package/dist/session/browser-session.d.ts +39 -0
- package/dist/session/close.d.ts +26 -0
- package/dist/session/dial.d.ts +65 -0
- package/dist/session/handshake.d.ts +60 -0
- package/dist/session/index.d.ts +17 -0
- package/dist/session/index.js +2 -0
- package/dist/session/messages.d.ts +63 -0
- package/dist/session/mic.d.ts +64 -0
- package/dist/session/preconnect.d.ts +48 -0
- package/dist/session/reconnect.d.ts +25 -0
- package/dist/session/resume-store.d.ts +57 -0
- package/dist/session/state.d.ts +146 -0
- package/dist/session/ticket.d.ts +44 -0
- package/dist/session/types.d.ts +476 -0
- package/dist/session/url.d.ts +17 -0
- package/dist/session/user-turn.d.ts +55 -0
- package/dist/session-D9jM5-TF.js +2140 -0
- package/dist/stored-value.d.ts +103 -0
- package/dist/tool-call-block-uXtMOUWN.js +204 -0
- package/dist/tool-config-context-Es4YUzV2.js +19 -0
- package/dist/types.d.ts +272 -34
- package/dist/types.js +35 -4
- package/dist/upload/files.d.ts +83 -0
- package/dist/upload/index.d.ts +15 -0
- package/dist/upload/pause.d.ts +31 -0
- package/dist/upload/recall.d.ts +67 -0
- package/dist/upload/report.d.ts +26 -0
- package/dist/upload/session.d.ts +99 -0
- package/dist/url-chips-oyy_Eiqh.js +239 -0
- package/dist/use-client-runs.d.ts +87 -0
- package/dist/use-conversation-log.d.ts +86 -0
- package/dist/use-conversation.d.ts +131 -0
- package/dist/use-copy.d.ts +74 -0
- package/dist/use-download-url.d.ts +84 -0
- package/dist/use-flash.d.ts +61 -0
- package/dist/use-inbox.d.ts +101 -0
- package/dist/use-push-to-talk.d.ts +105 -0
- package/dist/use-route-mutation.d.ts +113 -0
- package/dist/use-route.d.ts +82 -0
- package/dist/use-run-key.d.ts +133 -0
- package/dist/use-session-controls.d.ts +69 -0
- package/dist/use-session-id.d.ts +57 -0
- package/dist/use-tap-to-talk.d.ts +138 -0
- package/dist/use-user-transcript-uyHhzy4d.js +73 -0
- package/dist/use-user-transcript.d.ts +75 -0
- package/dist/use-workflow-form.d.ts +285 -0
- package/dist/use-workflow-progress.d.ts +137 -0
- package/dist/use-workflow-run-DdhWmbx-.js +697 -0
- package/dist/use-workflow-run.d.ts +100 -0
- package/dist/use-workflow-runs.d.ts +71 -0
- package/dist/use-workflow-stream.d.ts +154 -0
- package/dist/use-workflows.d.ts +77 -0
- package/dist/workflow-client.d.ts +136 -0
- package/dist/workflow-def-types.d.ts +31 -0
- package/dist/workflow-events.d.ts +39 -0
- package/dist/workflow-field-kind.d.ts +95 -0
- package/dist/workflow-status-labels.d.ts +35 -0
- package/dist/worklets/_module-url.d.ts +10 -0
- package/dist/worklets/_playback-bench-harness.d.ts +181 -0
- package/dist/worklets/_playback-bench-host.d.ts +63 -0
- package/dist/worklets/_playback-bench-page.d.ts +65 -0
- package/dist/worklets/_tts-trace-harness.d.ts +142 -0
- package/dist/worklets/_worklet-test-utils.d.ts +27 -0
- package/dist/worklets/capture-processor.d.ts +4 -2
- package/dist/worklets/capture-processor.js +73 -33
- package/dist/worklets/playback-processor.d.ts +4 -2
- package/dist/worklets/playback-processor.js +258 -42
- package/package.json +59 -32
- package/styles.css +152 -30
- package/dist/_jsdom-setup.d.ts +0 -0
- package/dist/audio.d.ts +0 -45
- package/dist/audio.js +0 -121
- package/dist/client-context.d.ts +0 -33
- package/dist/client-context.js +0 -15
- package/dist/client-handler.d.ts +0 -43
- package/dist/components/app.d.ts +0 -23
- package/dist/components/app.js +0 -41
- package/dist/components/error-banner.d.ts +0 -21
- package/dist/components/error-banner.js +0 -27
- package/dist/components/message-bubble.d.ts +0 -23
- package/dist/components/message-bubble.js +0 -35
- package/dist/components/state-indicator.d.ts +0 -29
- package/dist/components/state-indicator.js +0 -37
- package/dist/components/thinking-indicator.d.ts +0 -16
- package/dist/components/thinking-indicator.js +0 -34
- package/dist/components/tool-icons.d.ts +0 -17
- package/dist/components/tool-icons.js +0 -128
- package/dist/components/transcript.d.ts +0 -25
- package/dist/components/transcript.js +0 -35
- package/dist/session-CWB7vmqz.js +0 -429
- package/dist/session.d.ts +0 -69
- package/dist/session.js +0 -2
- package/dist/signals.d.ts +0 -120
- package/dist/signals.js +0 -161
- package/dist/tsdown.config.d.ts +0 -2
- package/dist/types.test-d.d.ts +0 -7
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { useSessionError, useSessionStatus, useTheme } from "./context.js";
|
|
2
|
+
import { t as MessageList } from "./message-list-DHZcLhRA.js";
|
|
3
|
+
import { a as inkTint, r as THINKING_COLOR, t as ERROR_COLOR } from "./_colors-CpZO-88A.js";
|
|
4
|
+
import { t as AaiLogo } from "./aai-logo-D8pMVn9_.js";
|
|
5
|
+
import { t as Eyebrow } from "./eyebrow-UfmSz9yy.js";
|
|
6
|
+
import { Controls } from "./components/controls.js";
|
|
7
|
+
import clsx from "clsx";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/agent-state-labels.ts
|
|
10
|
+
/**
|
|
11
|
+
* The default label per {@link AgentState}.
|
|
12
|
+
*
|
|
13
|
+
* Override the ones your page has a better word for and keep the rest:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import type { AgentState } from "@alexkroman1/aai-ui";
|
|
17
|
+
* import { AGENT_STATE_LABELS } from "@alexkroman1/aai-ui";
|
|
18
|
+
*
|
|
19
|
+
* // A dispatch board that shouts, and renames one state.
|
|
20
|
+
* const STATE_LABEL = { ...AGENT_STATE_LABELS, thinking: "Processing" };
|
|
21
|
+
* const shout = (s: AgentState) => STATE_LABEL[s].toUpperCase();
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* **Sentence case, deliberately.** A template that wants caps applies its own
|
|
25
|
+
* `.toUpperCase()`, and a template that wants Title Case is already there;
|
|
26
|
+
* shipping the shouted form instead would leave the two chromes that do not
|
|
27
|
+
* shout with a string they have to un-shout, which no case transform does
|
|
28
|
+
* correctly.
|
|
29
|
+
*
|
|
30
|
+
* Two wordings are decisions rather than transliterations of the member name:
|
|
31
|
+
*
|
|
32
|
+
* - `disconnected` is **"Idle"**. It is the state a session is in BEFORE it has
|
|
33
|
+
* ever started as well as after it ends, so it is the first word most callers
|
|
34
|
+
* see; "Disconnected" reads as a fault on a page where nothing has gone
|
|
35
|
+
* wrong yet. Both chromes that mapped this state by hand chose "Idle" too.
|
|
36
|
+
* - `connecting` and `thinking` carry an ellipsis, `listening` and `speaking`
|
|
37
|
+
* do not. The first two are waits with nothing for the caller to do; the
|
|
38
|
+
* other two describe someone actually talking. Same distinction
|
|
39
|
+
* `WORKFLOW_STATUS_LABELS` draws with its one "Working…".
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
const AGENT_STATE_LABELS = {
|
|
44
|
+
disconnected: "Idle",
|
|
45
|
+
connecting: "Connecting…",
|
|
46
|
+
ready: "Ready",
|
|
47
|
+
listening: "Listening",
|
|
48
|
+
thinking: "Thinking…",
|
|
49
|
+
speaking: "Speaking",
|
|
50
|
+
error: "Error"
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/components/session-error-banner.tsx
|
|
54
|
+
/** @jsxImportSource react */
|
|
55
|
+
/**
|
|
56
|
+
* The announced banner for a failed session: the error's message and code, or
|
|
57
|
+
* nothing at all when the session is fine.
|
|
58
|
+
*
|
|
59
|
+
* **This used to be four lines inside `ConsoleShell`, and that is why it is its
|
|
60
|
+
* own component.** The banner was the reason `ConsoleShell` was published —
|
|
61
|
+
* `role="alert"` is the one part of that component a reviewer cannot see is
|
|
62
|
+
* missing, since per the `fatalError` latch in `session/browser-session.ts` the banner is
|
|
63
|
+
* the ONLY remaining signal a session died (the state eyebrow beside it goes
|
|
64
|
+
* back to reading like a live session), and a screen reader is never told an
|
|
65
|
+
* unannounced one appeared. But `ConsoleShell` is a whole FRAME: a centred
|
|
66
|
+
* `max-w-190` column with its own header and footer. Every full-bleed chrome —
|
|
67
|
+
* a two-pane board, a CRT — therefore could not adopt it, rebuilt the banner
|
|
68
|
+
* instead, and the three that did had ALREADY drifted: one rendered
|
|
69
|
+
* `ERROR: {message}` and dropped the code entirely, one `ERROR: {message}
|
|
70
|
+
* ({code})`, one `{message} ({code})`. Splitting the banner out is what lets a
|
|
71
|
+
* chrome take the announced-error decision without taking the layout, and
|
|
72
|
+
* `ConsoleShell` composes this rather than keeping a second copy, so the two
|
|
73
|
+
* cannot drift again.
|
|
74
|
+
*
|
|
75
|
+
* **It reads the session itself.** There is no `error` prop: a banner that
|
|
76
|
+
* takes its text from the caller is a banner a caller can forget to wire, which
|
|
77
|
+
* is exactly the failure above with an extra step. It subscribes narrowly via
|
|
78
|
+
* {@link useSessionError}, so a page that renders it does not re-render with
|
|
79
|
+
* the transcript.
|
|
80
|
+
*
|
|
81
|
+
* **The code is shown, always.** `SessionError.code` is the eight-member wire
|
|
82
|
+
* union — it is what a user pastes into a bug report and the only part of the
|
|
83
|
+
* error that is stable across wordings — and the chrome that dropped it left
|
|
84
|
+
* its readers with a sentence and no way to say which failure it was.
|
|
85
|
+
*
|
|
86
|
+
* Must be rendered inside the providers `client()` installs.
|
|
87
|
+
*
|
|
88
|
+
* @example A full-bleed chrome that wants the banner and not the frame
|
|
89
|
+
* ```tsx
|
|
90
|
+
* import { SessionErrorBanner } from "@alexkroman1/aai-ui";
|
|
91
|
+
*
|
|
92
|
+
* function Board() {
|
|
93
|
+
* return (
|
|
94
|
+
* <div className="grid grid-cols-[1fr_320px] h-screen">
|
|
95
|
+
* <main>…</main>
|
|
96
|
+
* <aside>…</aside>
|
|
97
|
+
* <SessionErrorBanner className="col-span-2" />
|
|
98
|
+
* </div>
|
|
99
|
+
* );
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @param props - See {@link SessionErrorBannerProps}.
|
|
104
|
+
*
|
|
105
|
+
* @public
|
|
106
|
+
*/
|
|
107
|
+
function SessionErrorBanner({ className }) {
|
|
108
|
+
const error = useSessionError();
|
|
109
|
+
if (!error) return null;
|
|
110
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
111
|
+
role: "alert",
|
|
112
|
+
className: clsx("px-3.5 py-2.5 rounded-aai border text-[13px] leading-[130%] shrink-0", className),
|
|
113
|
+
style: {
|
|
114
|
+
borderColor: "rgba(179,38,30,0.35)",
|
|
115
|
+
background: "rgba(179,38,30,0.06)",
|
|
116
|
+
color: ERROR_COLOR
|
|
117
|
+
},
|
|
118
|
+
children: [
|
|
119
|
+
error.message,
|
|
120
|
+
" (",
|
|
121
|
+
error.code,
|
|
122
|
+
")"
|
|
123
|
+
]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/components/session-state-dot.tsx
|
|
128
|
+
/** @jsxImportSource react */
|
|
129
|
+
/**
|
|
130
|
+
* One `aai-pulse` cycle per pulsing state, in milliseconds. The two states
|
|
131
|
+
* that pulse are the two in which the agent is DOING something the reader
|
|
132
|
+
* cannot otherwise see — the microphone is open, or a reply is being worked
|
|
133
|
+
* out — and `thinking` beats faster because it is the shorter wait. `speaking`
|
|
134
|
+
* has the audio itself for a signal and stays still.
|
|
135
|
+
*/
|
|
136
|
+
const PULSE_MS = {
|
|
137
|
+
listening: 1500,
|
|
138
|
+
thinking: 800
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* The dot itself: a coloured circle, pulsing or still.
|
|
142
|
+
*
|
|
143
|
+
* Shared by {@link SessionStateDot} and `ConsoleShell`, which computes its
|
|
144
|
+
* colour from the theme rather than from a palette prop and decides for itself
|
|
145
|
+
* when to pulse. Not on the package barrel — the public component is the one
|
|
146
|
+
* that reads the session.
|
|
147
|
+
*
|
|
148
|
+
* The colour is written to `background` AND `color`, so a caller can extend
|
|
149
|
+
* the dot with a `currentColor` utility (`shadow-[0_0_6px_currentColor]` for a
|
|
150
|
+
* glow) without restating the palette.
|
|
151
|
+
*
|
|
152
|
+
* @internal
|
|
153
|
+
*/
|
|
154
|
+
function StateDot({ color, pulseMs, className, title }) {
|
|
155
|
+
return /* @__PURE__ */ jsx("span", {
|
|
156
|
+
className: clsx("rounded-full inline-block shrink-0", className),
|
|
157
|
+
style: {
|
|
158
|
+
background: color,
|
|
159
|
+
color,
|
|
160
|
+
animation: pulseMs === null ? "none" : `aai-pulse ${pulseMs}ms ease-in-out infinite`
|
|
161
|
+
},
|
|
162
|
+
title
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The live session state as a coloured dot and a word, on its own narrow
|
|
167
|
+
* subscription.
|
|
168
|
+
*
|
|
169
|
+
* Three custom chromes had each written this: a `satisfies Record<AgentState,
|
|
170
|
+
* string>` palette (kept — it is the prop), a `STATE_LABELS` spread over
|
|
171
|
+
* {@link AGENT_STATE_LABELS} (kept — it is `labels`), and then the same
|
|
172
|
+
* fourteen lines of markup around them, including the same two-arm ternary
|
|
173
|
+
* deciding which states pulse and how fast. `agent-state-labels.ts` argued
|
|
174
|
+
* against a dot component on the grounds that it would take the shared part
|
|
175
|
+
* (the words) hostage to the part that is not (the palette); this takes the
|
|
176
|
+
* palette as a prop precisely so it does not. What is shared is the structure
|
|
177
|
+
* — the exhaustive colour lookup, the label fallback, the pulse rule, the
|
|
178
|
+
* `useSessionStatus()` subscription that keeps the rest of the header from
|
|
179
|
+
* re-rendering at STT-partial rate.
|
|
180
|
+
*
|
|
181
|
+
* Must be rendered inside the providers `mountClient()` installs.
|
|
182
|
+
*
|
|
183
|
+
* @example A dispatch board's readout: its own colours, three of its own words
|
|
184
|
+
* ```tsx
|
|
185
|
+
* import type { AgentState } from "@alexkroman1/aai-ui";
|
|
186
|
+
* import { SessionStateDot } from "@alexkroman1/aai-ui";
|
|
187
|
+
*
|
|
188
|
+
* const STATE_COLORS = {
|
|
189
|
+
* disconnected: "#6b7280",
|
|
190
|
+
* connecting: "#6b7280",
|
|
191
|
+
* ready: "#22c55e",
|
|
192
|
+
* listening: "#22c55e",
|
|
193
|
+
* thinking: "#eab308",
|
|
194
|
+
* speaking: "#3b82f6",
|
|
195
|
+
* error: "#6b7280",
|
|
196
|
+
* } satisfies Record<AgentState, string>;
|
|
197
|
+
*
|
|
198
|
+
* function StatusReadout() {
|
|
199
|
+
* return (
|
|
200
|
+
* <SessionStateDot
|
|
201
|
+
* colors={STATE_COLORS}
|
|
202
|
+
* labels={{ listening: "LISTENING", thinking: "PROCESSING", speaking: "TRANSMITTING" }}
|
|
203
|
+
* labelClassName="text-[11px] uppercase"
|
|
204
|
+
* />
|
|
205
|
+
* );
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* @param props - See {@link SessionStateDotProps}.
|
|
210
|
+
*
|
|
211
|
+
* @public
|
|
212
|
+
*/
|
|
213
|
+
function SessionStateDot({ colors, labels, pulse = true, className, dotClassName, labelClassName }) {
|
|
214
|
+
const state = useSessionStatus();
|
|
215
|
+
const pulseMs = pulse ? PULSE_MS[state] ?? null : null;
|
|
216
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
217
|
+
className: clsx("inline-flex items-center gap-2", className),
|
|
218
|
+
"data-state": state,
|
|
219
|
+
children: [/* @__PURE__ */ jsx(StateDot, {
|
|
220
|
+
color: colors[state],
|
|
221
|
+
pulseMs,
|
|
222
|
+
className: dotClassName ?? "w-2 h-2",
|
|
223
|
+
title: state
|
|
224
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
225
|
+
className: labelClassName,
|
|
226
|
+
children: labels?.[state] ?? AGENT_STATE_LABELS[state]
|
|
227
|
+
})]
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/components/console-shell.tsx
|
|
232
|
+
/** @jsxImportSource react */
|
|
233
|
+
/**
|
|
234
|
+
* Indicator dot color per state.
|
|
235
|
+
*
|
|
236
|
+
* `idle` is the theme's own faint ink step rather than a fixed warm grey, so
|
|
237
|
+
* the dot stays visible on a dark ground; the thinking amber and error red
|
|
238
|
+
* are semantic and stay fixed.
|
|
239
|
+
*
|
|
240
|
+
* @internal
|
|
241
|
+
*/
|
|
242
|
+
function stateColor(state, primary, idle) {
|
|
243
|
+
switch (state) {
|
|
244
|
+
case "listening":
|
|
245
|
+
case "speaking":
|
|
246
|
+
case "ready": return primary;
|
|
247
|
+
case "thinking": return THINKING_COLOR;
|
|
248
|
+
case "error": return ERROR_COLOR;
|
|
249
|
+
default: return idle;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The design-system "console" chrome: a 760px column on the themed page with a
|
|
254
|
+
* header (icon + live-status eyebrow), an announced error banner, the main
|
|
255
|
+
* content on a raised card, and a footer row beneath it.
|
|
256
|
+
*
|
|
257
|
+
* {@link ChatView} is this shell with `<MessageList>` inside it and
|
|
258
|
+
* `<Controls>` under it, and until now that was the only way to get it — the
|
|
259
|
+
* shell itself was internal, so a client wanting its own conversation markup
|
|
260
|
+
* had to rebuild the chrome as well. Each one that did re-derived the error
|
|
261
|
+
* banner WITHOUT `role="alert"`.
|
|
262
|
+
*
|
|
263
|
+
* **The banner is {@link SessionErrorBanner} now, composed here rather than
|
|
264
|
+
* spelled out.** It was four lines of this file, and this file is a whole
|
|
265
|
+
* FRAME — a centred `max-w-190` column — so the full-bleed chromes that needed
|
|
266
|
+
* the announced banner could not take it without taking a layout that would
|
|
267
|
+
* replace the design they exist to demonstrate. Composing means there is one
|
|
268
|
+
* banner, and it means this component no longer takes an `error` prop: the
|
|
269
|
+
* banner reads the session itself, which is one fewer thing a caller can wire
|
|
270
|
+
* up wrong.
|
|
271
|
+
*
|
|
272
|
+
* Reach for it when the conversation is yours and the frame is not. Reach for
|
|
273
|
+
* `<ChatView>` when both are ours. Reach for `<SessionErrorBanner>` alone when
|
|
274
|
+
* neither is.
|
|
275
|
+
*
|
|
276
|
+
* Must be rendered inside the providers `mountClient()` installs.
|
|
277
|
+
*
|
|
278
|
+
* @example A custom conversation in the stock chrome
|
|
279
|
+
* ```tsx
|
|
280
|
+
* import {
|
|
281
|
+
* ConsoleShell,
|
|
282
|
+
* Controls,
|
|
283
|
+
* useConversation,
|
|
284
|
+
* useSessionStatus,
|
|
285
|
+
* } from "@alexkroman1/aai-ui";
|
|
286
|
+
*
|
|
287
|
+
* function Console() {
|
|
288
|
+
* const state = useSessionStatus();
|
|
289
|
+
* const { items } = useConversation();
|
|
290
|
+
* return (
|
|
291
|
+
* <ConsoleShell
|
|
292
|
+
* title="Dispatch"
|
|
293
|
+
* state={state}
|
|
294
|
+
* pulsing={state === "listening"}
|
|
295
|
+
* footer={<Controls />}
|
|
296
|
+
* >
|
|
297
|
+
* <ul>
|
|
298
|
+
* {items.map((item) => (
|
|
299
|
+
* <li key={item.kind === "message" ? item.message.id : item.toolCall.callId}>
|
|
300
|
+
* {item.kind === "message" ? item.message.content : item.toolCall.name}
|
|
301
|
+
* </li>
|
|
302
|
+
* ))}
|
|
303
|
+
* </ul>
|
|
304
|
+
* </ConsoleShell>
|
|
305
|
+
* );
|
|
306
|
+
* }
|
|
307
|
+
* ```
|
|
308
|
+
*
|
|
309
|
+
* @param props - See {@link ConsoleShellProps}.
|
|
310
|
+
*
|
|
311
|
+
* @public
|
|
312
|
+
*/
|
|
313
|
+
function ConsoleShell({ icon, title, state, pulsing, children, footer, className }) {
|
|
314
|
+
const theme = useTheme();
|
|
315
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
316
|
+
className: clsx("flex flex-col h-screen w-full max-w-190 mx-auto box-border px-6 py-8 gap-5 font-aai text-sm", className),
|
|
317
|
+
style: {
|
|
318
|
+
background: theme.bg,
|
|
319
|
+
color: theme.text
|
|
320
|
+
},
|
|
321
|
+
children: [
|
|
322
|
+
/* @__PURE__ */ jsxs("div", {
|
|
323
|
+
className: "flex items-center justify-between shrink-0",
|
|
324
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
325
|
+
className: "flex items-center gap-3 min-w-0",
|
|
326
|
+
children: [icon ?? /* @__PURE__ */ jsx(AaiLogo, { size: 22 }), title && /* @__PURE__ */ jsx("span", {
|
|
327
|
+
className: "font-aai-serif text-[22px] leading-[1.2] font-normal truncate",
|
|
328
|
+
style: { color: theme.text },
|
|
329
|
+
children: title
|
|
330
|
+
})]
|
|
331
|
+
}), /* @__PURE__ */ jsxs(Eyebrow, {
|
|
332
|
+
className: "shrink-0",
|
|
333
|
+
"data-state": state,
|
|
334
|
+
children: [/* @__PURE__ */ jsx(StateDot, {
|
|
335
|
+
color: stateColor(state, theme.primary, inkTint(theme.text, theme.bg, 65)),
|
|
336
|
+
pulseMs: pulsing ? 1600 : null,
|
|
337
|
+
className: "w-[7px] h-[7px]"
|
|
338
|
+
}), state]
|
|
339
|
+
})]
|
|
340
|
+
}),
|
|
341
|
+
/* @__PURE__ */ jsx(SessionErrorBanner, {}),
|
|
342
|
+
/* @__PURE__ */ jsx("div", {
|
|
343
|
+
className: "flex flex-col flex-1 min-h-0 border rounded-lg overflow-hidden",
|
|
344
|
+
style: {
|
|
345
|
+
background: theme.surface,
|
|
346
|
+
borderColor: theme.border,
|
|
347
|
+
boxShadow: "0 1px 3px 0 rgb(20 18 12 / 0.06)"
|
|
348
|
+
},
|
|
349
|
+
children
|
|
350
|
+
}),
|
|
351
|
+
footer
|
|
352
|
+
]
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/components/chat-view.tsx
|
|
357
|
+
const PULSING_STATES = /* @__PURE__ */ new Set(["listening", "speaking"]);
|
|
358
|
+
/**
|
|
359
|
+
* The main chat interface for a voice agent session — the design-system
|
|
360
|
+
* "voice agent console": a 760px column on the cream page with a header
|
|
361
|
+
* (logo + live-status eyebrow), the conversation on a raised white card,
|
|
362
|
+
* and the session controls beneath it.
|
|
363
|
+
*
|
|
364
|
+
* Must be rendered inside a `SessionProvider`.
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```tsx
|
|
368
|
+
* import { ChatView, StartScreen } from "@alexkroman1/aai-ui";
|
|
369
|
+
*
|
|
370
|
+
* function App() {
|
|
371
|
+
* return (
|
|
372
|
+
* <StartScreen icon="🍕" title="Pizza Palace">
|
|
373
|
+
* <ChatView />
|
|
374
|
+
* </StartScreen>
|
|
375
|
+
* );
|
|
376
|
+
* }
|
|
377
|
+
* ```
|
|
378
|
+
*
|
|
379
|
+
* @param props - Chat surface props.
|
|
380
|
+
*
|
|
381
|
+
* @public
|
|
382
|
+
*/
|
|
383
|
+
function ChatView({ icon, title, className }) {
|
|
384
|
+
const state = useSessionStatus();
|
|
385
|
+
return /* @__PURE__ */ jsx(ConsoleShell, {
|
|
386
|
+
icon,
|
|
387
|
+
title,
|
|
388
|
+
state,
|
|
389
|
+
pulsing: PULSING_STATES.has(state),
|
|
390
|
+
className,
|
|
391
|
+
footer: /* @__PURE__ */ jsx(Controls, {}),
|
|
392
|
+
children: /* @__PURE__ */ jsx(MessageList, {})
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
//#endregion
|
|
396
|
+
export { AGENT_STATE_LABELS as a, SessionErrorBanner as i, ConsoleShell as n, SessionStateDot as r, ChatView as t };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { n as pageBaseUrl } from "./_utils-Mi-SCFA1.js";
|
|
2
|
+
import { CLIENT_CONFIG_PATH, ClientConfigResponseSchema, SESSION_TICKET_HEADER } from "@alexkroman1/aai/protocol";
|
|
3
|
+
//#region src/client-config.ts
|
|
4
|
+
/**
|
|
5
|
+
* Pre-connection client-config lookup.
|
|
6
|
+
*
|
|
7
|
+
* `GET client-config` (relative to the agent's base URL — see
|
|
8
|
+
* `sdk/client-config.ts` in `@alexkroman1/aai`) gives the default client the
|
|
9
|
+
* agent's display name and greeting before any connection exists. For that
|
|
10
|
+
* use every failure path — network error, 404 from an older server,
|
|
11
|
+
* malformed body — degrades to the empty default (`fetchClientConfig`), so
|
|
12
|
+
* the lookup can never break an existing agent.
|
|
13
|
+
*
|
|
14
|
+
* The session's broker decision needs the opposite: `loadClientConfig`
|
|
15
|
+
* keeps "the lookup failed" (`null`) distinct from "the server answered and
|
|
16
|
+
* named no sessionUrl" (`{}`). See its doc comment.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a relative endpoint path against the agent's base URL.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
function buildAgentUrl(platformUrl, endpointPath) {
|
|
24
|
+
return new URL(endpointPath, platformUrl.endsWith("/") ? platformUrl : `${platformUrl}/`);
|
|
25
|
+
}
|
|
26
|
+
const AGENT_DEFAULT = { page: "voice" };
|
|
27
|
+
/**
|
|
28
|
+
* Per-attempt deadline for the `client-config` lookup.
|
|
29
|
+
*
|
|
30
|
+
* A request issued while the platform is restarting or saturated can HANG
|
|
31
|
+
* rather than fail — the proxy holds the socket open — and a browser fetch
|
|
32
|
+
* has no timeout of its own. Every other failure here is already handled
|
|
33
|
+
* (`null`, then the same-origin fallback), but a hang is not a failure: the
|
|
34
|
+
* promise simply never settles.
|
|
35
|
+
*
|
|
36
|
+
* That is unrecoverable rather than merely slow, because this lookup runs
|
|
37
|
+
* inside the session's WebSocket URL *provider*. partysocket awaits the
|
|
38
|
+
* provider under `_connectLock` and arms its own `connectionTimeout` only
|
|
39
|
+
* AFTER the URL resolves, so a hung lookup means no socket is ever
|
|
40
|
+
* constructed, no `error`/`close` ever fires, and none of the 10 reconnect
|
|
41
|
+
* attempts ever happen — the session sits on "connecting" forever, and stays
|
|
42
|
+
* there long after the server is back. Reproduced: zero sockets opened.
|
|
43
|
+
*
|
|
44
|
+
* A timed-out attempt therefore degrades exactly like any other failed one —
|
|
45
|
+
* `null`, so `configPerAttempt` stays unlatched and the attempt falls through
|
|
46
|
+
* to the same-origin `websocket` path, whose failure re-enters the normal
|
|
47
|
+
* backoff and re-fetches this on the next attempt.
|
|
48
|
+
*
|
|
49
|
+
* Sized well above the real work (one same-origin JSON GET that reads the
|
|
50
|
+
* agent's row) and well under a user's patience — the same 10s the studio's
|
|
51
|
+
* gating reads use for the identical hazard.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
const CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS = 1e4;
|
|
56
|
+
/**
|
|
57
|
+
* Fetch the agent's client config, reporting `null` when the lookup did not
|
|
58
|
+
* produce an answer (network error, non-2xx, unparsable body).
|
|
59
|
+
*
|
|
60
|
+
* The distinction from `fetchClientConfig` matters for exactly one caller:
|
|
61
|
+
* the session's per-attempt broker decision. A config that ARRIVED and named
|
|
62
|
+
* no `sessionUrl` means "this server is not a broker" (`aai dev`, an older
|
|
63
|
+
* server) — a durable fact worth latching. A lookup that FAILED means
|
|
64
|
+
* nothing about the server, and treating the two alike is how a single 503
|
|
65
|
+
* (a sandbox mid-boot, or one that failed to start) pinned a session to the
|
|
66
|
+
* platform's `/:slug/websocket` — a WebSocket redirect browsers don't
|
|
67
|
+
* follow, so every retry failed with no re-brokering even after the agent
|
|
68
|
+
* recovered.
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
async function loadClientConfig(platformUrl, fetchFn, presentedTicket) {
|
|
73
|
+
const doFetch = fetchFn ?? ((input, init) => globalThis.fetch(input, init));
|
|
74
|
+
try {
|
|
75
|
+
const resp = await doFetch(buildAgentUrl(platformUrl, CLIENT_CONFIG_PATH).href, {
|
|
76
|
+
signal: AbortSignal.timeout(CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS),
|
|
77
|
+
...presentedTicket === void 0 ? {} : { headers: { [SESSION_TICKET_HEADER]: presentedTicket } }
|
|
78
|
+
});
|
|
79
|
+
if (!resp.ok) return null;
|
|
80
|
+
const parsed = ClientConfigResponseSchema.safeParse(await resp.json());
|
|
81
|
+
return parsed.success ? parsed.data : null;
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Fetch the agent's declared `name`, `greeting` and front door; any failure
|
|
88
|
+
* yields the agent default (`{}`).
|
|
89
|
+
*
|
|
90
|
+
* **This is what a workflow app's own component calls instead of receiving the
|
|
91
|
+
* config.** Both mounts fetch `GET client-config` for the shell they render
|
|
92
|
+
* themselves — `mountClient()` for the chat shell, `mountPage()` for the
|
|
93
|
+
* generated workflow shell — so neither DEFAULT has to be told the agent's
|
|
94
|
+
* name. A `component:` replaces that shell, and with it the lookup, so a page
|
|
95
|
+
* that wants the agent's own `name` or `greeting` asks for them here.
|
|
96
|
+
*
|
|
97
|
+
* Every failure path degrades to the empty default rather than throwing: a
|
|
98
|
+
* network error, a 404 from a server older than the endpoint, a malformed
|
|
99
|
+
* body, and a lookup that hangs past
|
|
100
|
+
* `CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS` all read as "the agent declared nothing".
|
|
101
|
+
* So a page may render straight from the result and never needs a `catch` —
|
|
102
|
+
* treat every field as optional, because an agent that declared none is a
|
|
103
|
+
* normal agent.
|
|
104
|
+
*
|
|
105
|
+
* @param platformUrl - The agent's base URL. **Defaults to the page's own
|
|
106
|
+
* origin and path** (`pageBaseUrl()`), which is the agent that served the page
|
|
107
|
+
* and the only case a browser has — the argument was required while the default
|
|
108
|
+
* sat one module away in this same package, so every caller wrote
|
|
109
|
+
* `location.origin + location.pathname` for itself. Pass one for a page reading
|
|
110
|
+
* a DIFFERENT agent. The endpoint is resolved relative to it.
|
|
111
|
+
* @param fetchFn - Fetch implementation, for tests and for a caller that
|
|
112
|
+
* supplies its own credentials. Defaults to the global `fetch`.
|
|
113
|
+
* @returns The agent's config, or `{}` when the lookup produced no answer.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```tsx
|
|
117
|
+
* import { fetchClientConfig, mountPage } from "@alexkroman1/aai-ui";
|
|
118
|
+
*
|
|
119
|
+
* const { name, greeting } = await fetchClientConfig();
|
|
120
|
+
*
|
|
121
|
+
* function App() {
|
|
122
|
+
* return (
|
|
123
|
+
* <main>
|
|
124
|
+
* <h1>{name ?? "Workflows"}</h1>
|
|
125
|
+
* {greeting ? <p>{greeting}</p> : null}
|
|
126
|
+
* </main>
|
|
127
|
+
* );
|
|
128
|
+
* }
|
|
129
|
+
*
|
|
130
|
+
* mountPage({ name: name ?? "Workflows", component: App });
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* @public
|
|
134
|
+
*/
|
|
135
|
+
async function fetchClientConfig(platformUrl = pageBaseUrl(), fetchFn) {
|
|
136
|
+
return await loadClientConfig(platformUrl, fetchFn) ?? AGENT_DEFAULT;
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { fetchClientConfig as n, loadClientConfig as r, buildAgentUrl as t };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-connection client-config lookup.
|
|
3
|
+
*
|
|
4
|
+
* `GET client-config` (relative to the agent's base URL — see
|
|
5
|
+
* `sdk/client-config.ts` in `@alexkroman1/aai`) gives the default client the
|
|
6
|
+
* agent's display name and greeting before any connection exists. For that
|
|
7
|
+
* use every failure path — network error, 404 from an older server,
|
|
8
|
+
* malformed body — degrades to the empty default (`fetchClientConfig`), so
|
|
9
|
+
* the lookup can never break an existing agent.
|
|
10
|
+
*
|
|
11
|
+
* The session's broker decision needs the opposite: `loadClientConfig`
|
|
12
|
+
* keeps "the lookup failed" (`null`) distinct from "the server answered and
|
|
13
|
+
* named no sessionUrl" (`{}`). See its doc comment.
|
|
14
|
+
*/
|
|
15
|
+
import { type ClientConfigResponse } from "@alexkroman1/aai/protocol";
|
|
16
|
+
/** @internal Re-exported for the sibling modules; the SDK's `/protocol` subpath is the canonical home. */
|
|
17
|
+
export type { ClientConfigResponse } from "@alexkroman1/aai/protocol";
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a relative endpoint path against the agent's base URL.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildAgentUrl(platformUrl: string, endpointPath: string): URL;
|
|
24
|
+
/**
|
|
25
|
+
* Per-attempt deadline for the `client-config` lookup.
|
|
26
|
+
*
|
|
27
|
+
* A request issued while the platform is restarting or saturated can HANG
|
|
28
|
+
* rather than fail — the proxy holds the socket open — and a browser fetch
|
|
29
|
+
* has no timeout of its own. Every other failure here is already handled
|
|
30
|
+
* (`null`, then the same-origin fallback), but a hang is not a failure: the
|
|
31
|
+
* promise simply never settles.
|
|
32
|
+
*
|
|
33
|
+
* That is unrecoverable rather than merely slow, because this lookup runs
|
|
34
|
+
* inside the session's WebSocket URL *provider*. partysocket awaits the
|
|
35
|
+
* provider under `_connectLock` and arms its own `connectionTimeout` only
|
|
36
|
+
* AFTER the URL resolves, so a hung lookup means no socket is ever
|
|
37
|
+
* constructed, no `error`/`close` ever fires, and none of the 10 reconnect
|
|
38
|
+
* attempts ever happen — the session sits on "connecting" forever, and stays
|
|
39
|
+
* there long after the server is back. Reproduced: zero sockets opened.
|
|
40
|
+
*
|
|
41
|
+
* A timed-out attempt therefore degrades exactly like any other failed one —
|
|
42
|
+
* `null`, so `configPerAttempt` stays unlatched and the attempt falls through
|
|
43
|
+
* to the same-origin `websocket` path, whose failure re-enters the normal
|
|
44
|
+
* backoff and re-fetches this on the next attempt.
|
|
45
|
+
*
|
|
46
|
+
* Sized well above the real work (one same-origin JSON GET that reads the
|
|
47
|
+
* agent's row) and well under a user's patience — the same 10s the studio's
|
|
48
|
+
* gating reads use for the identical hazard.
|
|
49
|
+
*
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export declare const CLIENT_CONFIG_ATTEMPT_TIMEOUT_MS = 10000;
|
|
53
|
+
/**
|
|
54
|
+
* Fetch the agent's client config, reporting `null` when the lookup did not
|
|
55
|
+
* produce an answer (network error, non-2xx, unparsable body).
|
|
56
|
+
*
|
|
57
|
+
* The distinction from `fetchClientConfig` matters for exactly one caller:
|
|
58
|
+
* the session's per-attempt broker decision. A config that ARRIVED and named
|
|
59
|
+
* no `sessionUrl` means "this server is not a broker" (`aai dev`, an older
|
|
60
|
+
* server) — a durable fact worth latching. A lookup that FAILED means
|
|
61
|
+
* nothing about the server, and treating the two alike is how a single 503
|
|
62
|
+
* (a sandbox mid-boot, or one that failed to start) pinned a session to the
|
|
63
|
+
* platform's `/:slug/websocket` — a WebSocket redirect browsers don't
|
|
64
|
+
* follow, so every retry failed with no re-brokering even after the agent
|
|
65
|
+
* recovered.
|
|
66
|
+
*
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export declare function loadClientConfig(platformUrl: string, fetchFn?: typeof globalThis.fetch, presentedTicket?: string): Promise<ClientConfigResponse | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch the agent's declared `name`, `greeting` and front door; any failure
|
|
72
|
+
* yields the agent default (`{}`).
|
|
73
|
+
*
|
|
74
|
+
* **This is what a workflow app's own component calls instead of receiving the
|
|
75
|
+
* config.** Both mounts fetch `GET client-config` for the shell they render
|
|
76
|
+
* themselves — `mountClient()` for the chat shell, `mountPage()` for the
|
|
77
|
+
* generated workflow shell — so neither DEFAULT has to be told the agent's
|
|
78
|
+
* name. A `component:` replaces that shell, and with it the lookup, so a page
|
|
79
|
+
* that wants the agent's own `name` or `greeting` 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. **Defaults to the page's own
|
|
90
|
+
* origin and path** (`pageBaseUrl()`), which is the agent that served the page
|
|
91
|
+
* and the only case a browser has — the argument was required while the default
|
|
92
|
+
* sat one module away in this same package, so every caller wrote
|
|
93
|
+
* `location.origin + location.pathname` for itself. Pass one for a page reading
|
|
94
|
+
* a DIFFERENT agent. The endpoint is resolved relative to it.
|
|
95
|
+
* @param fetchFn - Fetch implementation, for tests and for a caller that
|
|
96
|
+
* supplies its own credentials. Defaults to the global `fetch`.
|
|
97
|
+
* @returns The agent's config, or `{}` when the lookup produced no answer.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```tsx
|
|
101
|
+
* import { fetchClientConfig, mountPage } from "@alexkroman1/aai-ui";
|
|
102
|
+
*
|
|
103
|
+
* const { name, greeting } = await fetchClientConfig();
|
|
104
|
+
*
|
|
105
|
+
* function App() {
|
|
106
|
+
* return (
|
|
107
|
+
* <main>
|
|
108
|
+
* <h1>{name ?? "Workflows"}</h1>
|
|
109
|
+
* {greeting ? <p>{greeting}</p> : null}
|
|
110
|
+
* </main>
|
|
111
|
+
* );
|
|
112
|
+
* }
|
|
113
|
+
*
|
|
114
|
+
* mountPage({ name: name ?? "Workflows", component: App });
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @public
|
|
118
|
+
*/
|
|
119
|
+
export declare function fetchClientConfig(platformUrl?: string, fetchFn?: typeof globalThis.fetch): Promise<ClientConfigResponse>;
|