@alexkroman1/aai-ui 6.11.0 → 8.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 (71) hide show
  1. package/README.md +104 -3
  2. package/dist/{_colors-CcAi2FOU.js → _colors-j8XMToi9.js} +13 -2
  3. package/dist/_run-controls.d.ts +33 -0
  4. package/dist/_upload-pause.d.ts +31 -0
  5. package/dist/{chat-view-ByQFf94G.js → chat-view-BJ6sOARu.js} +58 -12
  6. package/dist/client-config-BT_kWID5.js +134 -0
  7. package/dist/client-config.d.ts +45 -2
  8. package/dist/client-dir.d.ts +3 -1
  9. package/dist/client-dir.js +3 -1
  10. package/dist/components/auto-scroll.d.ts +24 -13
  11. package/dist/components/button.d.ts +8 -4
  12. package/dist/components/button.js +5 -5
  13. package/dist/components/chat-view.d.ts +4 -3
  14. package/dist/components/chat-view.js +1 -1
  15. package/dist/components/console-shell.d.ts +71 -10
  16. package/dist/components/controls.d.ts +15 -4
  17. package/dist/components/controls.js +48 -2
  18. package/dist/components/form-fields.d.ts +142 -0
  19. package/dist/components/form-types.d.ts +6 -0
  20. package/dist/components/form.d.ts +38 -80
  21. package/dist/components/markdown.d.ts +31 -5
  22. package/dist/components/message-list.d.ts +21 -4
  23. package/dist/components/message-list.js +1 -1
  24. package/dist/components/sidebar-layout.d.ts +11 -0
  25. package/dist/components/sidebar-layout.js +2 -0
  26. package/dist/components/start-screen.d.ts +8 -0
  27. package/dist/components/start-screen.js +3 -1
  28. package/dist/components/tool-call-block.js +1 -1
  29. package/dist/components/tool-call-row.d.ts +24 -0
  30. package/dist/components/upload-progress.d.ts +17 -7
  31. package/dist/components/workflow-fields.d.ts +10 -22
  32. package/dist/components/workflow-progress.d.ts +29 -10
  33. package/dist/context.d.ts +36 -0
  34. package/dist/context.js +105 -14
  35. package/dist/default-client/assets/index-Du8SVFin.js +299 -0
  36. package/dist/default-client/assets/index-S5fkKi6B.css +2 -0
  37. package/dist/default-client/index.html +2 -2
  38. package/dist/define-client.d.ts +67 -62
  39. package/dist/define-client.js +56 -22
  40. package/dist/hooks.d.ts +88 -3
  41. package/dist/index.d.ts +15 -14
  42. package/dist/index.js +548 -819
  43. package/dist/internal.d.ts +43 -0
  44. package/dist/internal.js +8 -0
  45. package/dist/{message-list-CpPV7dGx.js → message-list-Dtl03sGr.js} +154 -73
  46. package/dist/{session-core-CAfYmUbg.js → session-core-CMEpwRP1.js} +269 -167
  47. package/dist/session-core-audio-setup.d.ts +5 -2
  48. package/dist/session-core-messages.d.ts +4 -1
  49. package/dist/session-core-state.d.ts +146 -0
  50. package/dist/session-core-types.d.ts +77 -32
  51. package/dist/session-core.d.ts +3 -2
  52. package/dist/session-core.js +1 -1
  53. package/dist/{tool-call-block-D6pTEPrT.js → tool-call-block-B7KlhaWl.js} +28 -20
  54. package/dist/tool-config-context-DzAofqi_.js +19 -0
  55. package/dist/types.d.ts +31 -5
  56. package/dist/types.js +5 -4
  57. package/dist/{controls-CjG91QJ4.js → url-chips-DxXKgyL9.js} +4 -47
  58. package/dist/use-conversation.d.ts +122 -0
  59. package/dist/use-download-url.d.ts +83 -0
  60. package/dist/use-user-transcript-C14qWFu2.js +72 -0
  61. package/dist/use-user-transcript.d.ts +7 -2
  62. package/dist/use-workflow-form.d.ts +59 -3
  63. package/dist/use-workflow-progress-C4KgVldr.js +610 -0
  64. package/dist/use-workflow-run.d.ts +35 -0
  65. package/dist/use-workflow-stream.d.ts +36 -62
  66. package/dist/workflow-client.d.ts +36 -11
  67. package/dist/workflow-status-labels.d.ts +35 -0
  68. package/package.json +10 -5
  69. package/styles.css +14 -0
  70. package/dist/default-client/assets/index-DTLrhtTF.css +0 -2
  71. package/dist/default-client/assets/index-DXODx_9r.js +0 -293
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `@alexkroman1/aai-ui/internal` — the plumbing `client()` and the default
3
+ * client install for themselves, NOT part of the public client API and not
4
+ * covered by semver. A `client.tsx` should never import from here; everything
5
+ * an author writes a page or a custom chrome against lives on the root export
6
+ * and the documented subpaths.
7
+ *
8
+ * These names used to ride on the root barrel, tagged `@internal` and nothing
9
+ * else, which meant they were in a client author's autocomplete beside
10
+ * `client()`, `<Form>`, `useAgentState` and `useWorkflowRun` — eight symbols an
11
+ * author is invited to reach for and no capability contract covers. Keeping
12
+ * them on their own subpath keeps the root importable surface the same shape as
13
+ * the promise: what is on it is contracted.
14
+ *
15
+ * A release tag cannot do this from the barrel. API Extractor reads `@internal`
16
+ * at the DECLARATION site, so a tag written on a re-export clause member is
17
+ * silently ignored and the name stays `@public` in the report — and the
18
+ * contract exemption in `contracts/internal-surface.json` is per SUBPATH, so a
19
+ * tag on the root barrel buys an entry on that ratchet rather than removing
20
+ * one. A subpath is the mechanism, the same one `@alexkroman1/aai` and
21
+ * `@alexkroman1/aai-runtime` already use, and `NON_AUTHORING_SUBPATHS` in
22
+ * `scripts/_api-contracts-tree.mjs` carries the matching entry so a name
23
+ * arriving here joins no capability contract.
24
+ *
25
+ * The corollary is the rule for adding to this file, and it runs the other way
26
+ * too: a name that WANTS to be public gets its `@internal` tag removed and
27
+ * joins a capability contract — it does not stay on the barrel wearing a tag.
28
+ * That is what happened to `fetchClientConfig`, which the SDK's own `@public`
29
+ * prose told a workflow-app author to call while the export excluded it.
30
+ *
31
+ * Named re-exports rather than `export *`: the wildcard form needs a
32
+ * `noReExportAll` suppression, and the escape-hatch ratchet only moves down.
33
+ *
34
+ * @module internal
35
+ */
36
+ export { buildAgentUrl, loadClientConfig } from "./client-config.ts";
37
+ export { ToolConfigContext } from "./components/tool-config-context.ts";
38
+ export { ApiUrlChip, SessionUrlChips, UiUrlChip } from "./components/url-chips.tsx";
39
+ export { SessionProvider, ThemeProvider } from "./context.ts";
40
+ export { VOICE_CAPTURE_CONSTRAINTS } from "./types.ts";
41
+ export { TRANSCRIBING_PLACEHOLDER } from "./use-user-transcript.ts";
42
+ export { DEFAULT_PROGRESS_POLL_MS } from "./use-workflow-progress.ts";
43
+ export { DEFAULT_WORKFLOW_POLL_MS, MAX_MISSING_READS } from "./use-workflow-run.ts";
@@ -0,0 +1,8 @@
1
+ import { r as loadClientConfig, t as buildAgentUrl } from "./client-config-BT_kWID5.js";
2
+ import { SessionProvider, ThemeProvider } from "./context.js";
3
+ import { n as SessionUrlChips, r as UiUrlChip, t as ApiUrlChip } from "./url-chips-DxXKgyL9.js";
4
+ import { t as TRANSCRIBING_PLACEHOLDER } from "./use-user-transcript-C14qWFu2.js";
5
+ import { t as ToolConfigContext } from "./tool-config-context-DzAofqi_.js";
6
+ import { i as MAX_MISSING_READS, r as DEFAULT_WORKFLOW_POLL_MS, t as DEFAULT_PROGRESS_POLL_MS } from "./use-workflow-progress-C4KgVldr.js";
7
+ import { VOICE_CAPTURE_CONSTRAINTS } from "./types.js";
8
+ export { ApiUrlChip, DEFAULT_PROGRESS_POLL_MS, DEFAULT_WORKFLOW_POLL_MS, MAX_MISSING_READS, SessionProvider, SessionUrlChips, TRANSCRIBING_PLACEHOLDER, ThemeProvider, ToolConfigContext, UiUrlChip, VOICE_CAPTURE_CONSTRAINTS, buildAgentUrl, loadClientConfig };
@@ -1,6 +1,7 @@
1
1
  import { useSessionSelector, useTheme } from "./context.js";
2
- import { i as primaryTint, r as inkTint } from "./_colors-CcAi2FOU.js";
3
- import { t as ToolCallBlock } from "./tool-call-block-D6pTEPrT.js";
2
+ import { i as primaryTint, r as inkTint } from "./_colors-j8XMToi9.js";
3
+ import { n as useUserTranscript } from "./use-user-transcript-C14qWFu2.js";
4
+ import { t as ToolCallBlock } from "./tool-call-block-B7KlhaWl.js";
4
5
  import clsx from "clsx";
5
6
  import { StickToBottom } from "use-stick-to-bottom";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -46,19 +47,7 @@ import remarkGfm from "remark-gfm";
46
47
  * }
47
48
  * ```
48
49
  *
49
- * @param children - The scrollable content.
50
- * @param className - Classes for the outer container. It must be given a
51
- * bounded height (`flex-1 min-h-0`, `h-full`, a fixed height) — an unbounded
52
- * one grows with its content and never scrolls, so nothing pins.
53
- * @param contentClassName - Classes for the inner content element, where
54
- * padding and the children's own layout belong.
55
- * @param scrollClassName - Classes for the scrolling element itself. Defaults
56
- * to hiding the scrollbar; pass `"overflow-y-auto"` to show a native one.
57
- * @param style - Inline styles for the outer container.
58
- * @param initial - Scroll behavior on mount. Defaults to `"instant"` (start at
59
- * the latest content without animating a scroll the reader did not ask for).
60
- * @param resize - Scroll behavior when pinned content grows. Defaults to
61
- * `"smooth"`.
50
+ * @param props - Scroll container props.
62
51
  *
63
52
  * @public
64
53
  */
@@ -77,6 +66,131 @@ function AutoScroll({ children, className, contentClassName, scrollClassName = "
77
66
  });
78
67
  }
79
68
  //#endregion
69
+ //#region use-conversation.ts
70
+ /**
71
+ * `useConversation` — the exchange, already assembled, with nothing rendered.
72
+ *
73
+ * `<MessageList>` owns four decisions that are not obvious and are not
74
+ * derivable from the snapshot by eye: the message/tool-call INTERLEAVE, the
75
+ * streaming agent utterance, the live user transcript with its `null`-vs-`""`
76
+ * protocol distinction, and the thinking indicator's suppression rule. Its only
77
+ * prop is `className`, so the moment a client wants its own bubble markup it
78
+ * drops all four at once — and the three hand-rolled chromes in the template
79
+ * tree each shipped a strictly worse conversation for exactly that reason. One
80
+ * of them runs fifteen tools and its operator sees none of them, because the
81
+ * tool calls live in a second array nothing in that page ever reads.
82
+ *
83
+ * This is the headless half. `<MessageList>` is now a thin consumer of it —
84
+ * which is the only way to know the hook is complete, since a hook that the
85
+ * package's own list cannot be built from is a hook a custom chrome will find a
86
+ * hole in.
87
+ *
88
+ * ## It subscribes NARROWLY, and that is half the value
89
+ *
90
+ * The three custom chromes also call whole-page `useSession()`, which re-renders
91
+ * on *every* snapshot change — so a dispatch board re-renders at STT-partial
92
+ * rate. `<ChatView>` deliberately avoids that with per-field
93
+ * {@link useSessionSelector} calls, and so does this: a component that reads the
94
+ * conversation gets the conversation's own update rate, not the session's.
95
+ */
96
+ /**
97
+ * Interleave messages and tool calls, ordered by insertion time.
98
+ *
99
+ * Each tool call belongs immediately after its anchor message
100
+ * (`afterMessageId`); tool calls whose anchor slid out of the retained window —
101
+ * or that were inserted before any message existed — come first, since there is
102
+ * nothing left for them to follow.
103
+ */
104
+ function interleave(messages, toolCalls) {
105
+ const items = [];
106
+ let tci = 0;
107
+ const pushToolCallsThrough = (maxAfterId) => {
108
+ let tc = toolCalls[tci];
109
+ while (tc && tc.afterMessageId <= maxAfterId) {
110
+ items.push({
111
+ kind: "tool",
112
+ toolCall: tc
113
+ });
114
+ tci++;
115
+ tc = toolCalls[tci];
116
+ }
117
+ };
118
+ const firstMessage = messages[0];
119
+ if (firstMessage) pushToolCallsThrough(firstMessage.id - 1);
120
+ for (const message of messages) {
121
+ items.push({
122
+ kind: "message",
123
+ message
124
+ });
125
+ pushToolCallsThrough(message.id);
126
+ }
127
+ pushToolCallsThrough(Number.POSITIVE_INFINITY);
128
+ return items;
129
+ }
130
+ /**
131
+ * Whether the thinking indicator should show — see
132
+ * {@link UseConversationResult.thinking} for the rule and the argument.
133
+ */
134
+ function isThinking(state, messages, toolCalls) {
135
+ if (state !== "thinking") return false;
136
+ const last = toolCalls.at(-1);
137
+ if (last?.status === "pending") return false;
138
+ const lastMessage = messages.at(-1);
139
+ return !lastMessage || lastMessage.role === "user" || Boolean(last);
140
+ }
141
+ /**
142
+ * Subscribe to the conversation: the interleaved exchange, the streaming
143
+ * utterance, the live transcript and the thinking rule — with no markup.
144
+ *
145
+ * Must be used inside the provider `client()` installs.
146
+ *
147
+ * @example A custom bubble, keeping every rule `<MessageList>` knows
148
+ * ```tsx
149
+ * import { useConversation } from "@alexkroman1/aai-ui";
150
+ *
151
+ * function Transcript() {
152
+ * const { items, streaming, transcript, thinking } = useConversation();
153
+ * return (
154
+ * <div>
155
+ * {items.map((item) =>
156
+ * item.kind === "message" ? (
157
+ * <p key={item.message.id} data-role={item.message.role}>
158
+ * {item.message.content}
159
+ * </p>
160
+ * ) : (
161
+ * <code key={item.toolCall.callId}>{item.toolCall.name}</code>
162
+ * ),
163
+ * )}
164
+ * {streaming !== null && <p data-role="assistant">{streaming}</p>}
165
+ * {transcript.speaking && <p data-role="user">{transcript.text}</p>}
166
+ * {thinking && <p>…</p>}
167
+ * </div>
168
+ * );
169
+ * }
170
+ * ```
171
+ *
172
+ * @returns See {@link UseConversationResult}.
173
+ *
174
+ * @public
175
+ */
176
+ function useConversation() {
177
+ const state = useSessionSelector((s) => s.state);
178
+ const messages = useSessionSelector((s) => s.messages);
179
+ const toolCalls = useSessionSelector((s) => s.toolCalls);
180
+ const streaming = useSessionSelector((s) => s.agentTranscript);
181
+ const transcript = useUserTranscript();
182
+ return {
183
+ items: useMemo(() => interleave(messages, toolCalls), [messages, toolCalls]),
184
+ streaming,
185
+ transcript,
186
+ thinking: useMemo(() => isThinking(state, messages, toolCalls), [
187
+ state,
188
+ messages,
189
+ toolCalls
190
+ ])
191
+ };
192
+ }
193
+ //#endregion
80
194
  //#region components/markdown.tsx
81
195
  /** @jsxImportSource react */
82
196
  const BARE_ORDERED_MARKER = /^(\s*)(\d{1,9})([.)])\s*$/;
@@ -137,6 +251,18 @@ const SCALE_CLASSES = {
137
251
  * Memoized alongside `MessageBubble`: message content is referentially
138
252
  * stable across snapshots, so only the streaming row re-parses.
139
253
  *
254
+ * @example
255
+ * ```tsx
256
+ * import { Markdown, useSessionSelector } from "@alexkroman1/aai-ui";
257
+ *
258
+ * // The agent's reply as it streams, rendered rather than shown as literal
259
+ * // asterisks and backticks.
260
+ * function LiveReply() {
261
+ * const text = useSessionSelector((snapshot) => snapshot.agentTranscript);
262
+ * return text === null ? null : <Markdown text={text} variant="compact" />;
263
+ * }
264
+ * ```
265
+ *
140
266
  * @public
141
267
  */
142
268
  const Markdown = memo(function Markdown({ text, variant = "default" }) {
@@ -339,32 +465,6 @@ const MessageBubble = memo(function MessageBubble({ message, theme }) {
339
465
  });
340
466
  });
341
467
  /**
342
- * Interleave messages and tool calls into render items, ordered by insertion
343
- * time. Each tool call renders immediately after its anchor message
344
- * (`afterMessageId`); tool calls whose anchor slid out of the retained window
345
- * (or that were inserted before any message existed) render first.
346
- */
347
- function interleave(messages, toolCalls, renderMessage, renderToolCall) {
348
- const items = [];
349
- let tci = 0;
350
- const pushToolCallsThrough = (maxAfterId) => {
351
- let tc = toolCalls[tci];
352
- while (tc && tc.afterMessageId <= maxAfterId) {
353
- items.push(renderToolCall(tc));
354
- tci++;
355
- tc = toolCalls[tci];
356
- }
357
- };
358
- const firstMessage = messages[0];
359
- if (firstMessage) pushToolCallsThrough(firstMessage.id - 1);
360
- for (const msg of messages) {
361
- items.push(renderMessage(msg));
362
- pushToolCallsThrough(msg.id);
363
- }
364
- pushToolCallsThrough(Number.POSITIVE_INFINITY);
365
- return items;
366
- }
367
- /**
368
468
  * Scrollable list of all chat messages, tool-call blocks, live transcript,
369
469
  * streaming agent utterance, and a thinking indicator.
370
470
  *
@@ -382,58 +482,39 @@ function interleave(messages, toolCalls, renderMessage, renderToolCall) {
382
482
  * }
383
483
  * ```
384
484
  *
385
- * @param className - Additional CSS class names applied to the outer list container.
485
+ * @param props - Container props.
386
486
  *
387
487
  * @public
388
488
  */
389
489
  const MessageList = memo(function MessageList({ className }) {
390
- const state = useSessionSelector((s) => s.state);
391
- const messages = useSessionSelector((s) => s.messages);
392
- const toolCalls = useSessionSelector((s) => s.toolCalls);
393
- const userTranscript = useSessionSelector((s) => s.userTranscript);
394
- const agentTranscript = useSessionSelector((s) => s.agentTranscript);
490
+ const { items, streaming, transcript, thinking } = useConversation();
395
491
  const theme = useTheme();
396
- const showThinking = useMemo(() => {
397
- if (state !== "thinking") return false;
398
- const last = toolCalls.at(-1);
399
- if (last?.status === "pending") return false;
400
- const lastMsg = messages.at(-1);
401
- return !lastMsg || lastMsg.role === "user" || Boolean(last);
402
- }, [
403
- state,
404
- toolCalls,
405
- messages
406
- ]);
407
- const streamingMessage = useMemo(() => agentTranscript ? {
492
+ const streamingMessage = useMemo(() => streaming ? {
408
493
  role: "assistant",
409
- content: agentTranscript
410
- } : null, [agentTranscript]);
411
- const items = useMemo(() => interleave(messages, toolCalls, (msg) => /* @__PURE__ */ jsx(MessageBubble, {
412
- message: msg,
413
- theme
414
- }, msg.id), (tc) => /* @__PURE__ */ jsx(ToolCallBlock, { toolCall: tc }, tc.callId)), [
415
- messages,
416
- toolCalls,
494
+ content: streaming
495
+ } : null, [streaming]);
496
+ const rows = useMemo(() => items.map((item) => item.kind === "message" ? /* @__PURE__ */ jsx(MessageBubble, {
497
+ message: item.message,
417
498
  theme
418
- ]);
499
+ }, item.message.id) : /* @__PURE__ */ jsx(ToolCallBlock, { toolCall: item.toolCall }, item.toolCall.callId)), [items, theme]);
419
500
  return /* @__PURE__ */ jsxs(AutoScroll, {
420
501
  className,
421
502
  style: { background: theme.surface },
422
503
  contentClassName: "flex flex-col gap-4 p-7",
423
504
  children: [
424
- items,
505
+ rows,
425
506
  streamingMessage && /* @__PURE__ */ jsx(MessageBubble, {
426
507
  message: streamingMessage,
427
508
  theme
428
509
  }),
429
- userTranscript !== null && /* @__PURE__ */ jsx(UserBubble, {
510
+ transcript.speaking && /* @__PURE__ */ jsx(UserBubble, {
430
511
  theme,
431
512
  color: inkTint(theme.text, theme.surface, 65),
432
- children: userTranscript ? userTranscript : /* @__PURE__ */ jsx(ThinkingDots, {})
513
+ children: transcript.partial ? transcript.partial : /* @__PURE__ */ jsx(ThinkingDots, {})
433
514
  }),
434
- showThinking && /* @__PURE__ */ jsx(ThinkingDots, {})
515
+ thinking && /* @__PURE__ */ jsx(ThinkingDots, {})
435
516
  ]
436
517
  });
437
518
  });
438
519
  //#endregion
439
- export { Markdown as n, AutoScroll as r, MessageList as t };
520
+ export { AutoScroll as i, Markdown as n, useConversation as r, MessageList as t };