agentic-ui-kit 0.0.0-stage → 0.2.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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -2
  3. package/dist/core.js +1 -0
  4. package/dist/index.js +2875 -0
  5. package/dist/sse-BA_ebMcH.js +408 -0
  6. package/dist/styles.css +3 -0
  7. package/dist/tailwind.css +350 -0
  8. package/dist/types/core/src/blocks.d.ts +22 -0
  9. package/dist/types/core/src/events.d.ts +75 -0
  10. package/dist/types/core/src/index.d.ts +7 -0
  11. package/dist/types/core/src/partial-json.d.ts +7 -0
  12. package/dist/types/core/src/reduce.d.ts +33 -0
  13. package/dist/types/core/src/sse.d.ts +8 -0
  14. package/dist/types/core/src/types.d.ts +171 -0
  15. package/dist/types/react/src/components/activity-group.d.ts +13 -0
  16. package/dist/types/react/src/components/approval-card.d.ts +11 -0
  17. package/dist/types/react/src/components/attachments.d.ts +78 -0
  18. package/dist/types/react/src/components/chat-header.d.ts +30 -0
  19. package/dist/types/react/src/components/chat-list.d.ts +47 -0
  20. package/dist/types/react/src/components/composer.d.ts +45 -0
  21. package/dist/types/react/src/components/context-panel.d.ts +13 -0
  22. package/dist/types/react/src/components/markdown.d.ts +8 -0
  23. package/dist/types/react/src/components/message-list.d.ts +18 -0
  24. package/dist/types/react/src/components/messages.d.ts +33 -0
  25. package/dist/types/react/src/components/model-picker.d.ts +16 -0
  26. package/dist/types/react/src/components/token-counter.d.ts +16 -0
  27. package/dist/types/react/src/components/tools.d.ts +68 -0
  28. package/dist/types/react/src/components/word-fade.d.ts +19 -0
  29. package/dist/types/react/src/core.d.ts +1 -0
  30. package/dist/types/react/src/hooks/agent.d.ts +86 -0
  31. package/dist/types/react/src/hooks/sidebar-state.d.ts +18 -0
  32. package/dist/types/react/src/hooks/smooth-text.d.ts +9 -0
  33. package/dist/types/react/src/hooks/sse-transport.d.ts +15 -0
  34. package/dist/types/react/src/hooks/use-chat.d.ts +68 -0
  35. package/dist/types/react/src/index.d.ts +57 -0
  36. package/dist/types/react/src/primitives/appearance.d.ts +38 -0
  37. package/dist/types/react/src/primitives/badge.d.ts +15 -0
  38. package/dist/types/react/src/primitives/button.d.ts +32 -0
  39. package/dist/types/react/src/primitives/caret.d.ts +4 -0
  40. package/dist/types/react/src/primitives/chat-theme.d.ts +22 -0
  41. package/dist/types/react/src/primitives/code-block.d.ts +13 -0
  42. package/dist/types/react/src/primitives/context-menu.d.ts +13 -0
  43. package/dist/types/react/src/primitives/disclosure.d.ts +16 -0
  44. package/dist/types/react/src/primitives/highlight.d.ts +13 -0
  45. package/dist/types/react/src/primitives/menu.d.ts +52 -0
  46. package/dist/types/react/src/primitives/overlay.d.ts +21 -0
  47. package/dist/types/react/src/primitives/panel.d.ts +19 -0
  48. package/dist/types/react/src/primitives/tooltip.d.ts +10 -0
  49. package/dist/types/react/src/views/app-shell.d.ts +20 -0
  50. package/dist/types/react/src/views/chat-view.d.ts +51 -0
  51. package/dist/types/react/src/views/sidebar.d.ts +38 -0
  52. package/package.json +80 -3
@@ -0,0 +1,350 @@
1
+ /*
2
+ * Theme and Tailwind setup for agentic-ui-kit. Import after Tailwind in the host app's CSS:
3
+ *
4
+ * @import "tailwindcss";
5
+ * @import "agentic-ui-kit/tailwind.css";
6
+ *
7
+ * Every visual value is a --chat-* variable. A host retheme sets them on :root or on any wrapper element;
8
+ * components only use the Tailwind names below (bg-chat-surface, text-chat-muted, ...), never raw colors.
9
+ */
10
+
11
+ @import "katex/dist/katex.min.css";
12
+
13
+ @source "./";
14
+
15
+ @theme inline {
16
+ --color-chat-bg: var(--chat-bg);
17
+ --color-chat-sidebar: var(--chat-sidebar);
18
+ --color-chat-surface: var(--chat-surface);
19
+ --color-chat-raised: var(--chat-raised);
20
+ --color-chat-hover: var(--chat-hover);
21
+ --color-chat-selected: var(--chat-selected);
22
+ --color-chat-bubble: var(--chat-bubble);
23
+ --color-chat-popover: var(--chat-popover);
24
+ --color-chat-popover-border: var(--chat-popover-border);
25
+ --color-chat-popover-hover: var(--chat-popover-hover);
26
+ --color-chat-border: var(--chat-border);
27
+ --color-chat-border-strong: var(--chat-border-strong);
28
+ --color-chat-divider: var(--chat-divider);
29
+ --color-chat-code-bg: var(--chat-code-bg);
30
+ --color-chat-code-border: var(--chat-code-border);
31
+ --color-chat-inline-code: var(--chat-inline-code);
32
+ --color-chat-fg: var(--chat-fg);
33
+ --color-chat-muted: var(--chat-muted);
34
+ --color-chat-subtle: var(--chat-subtle);
35
+ --color-chat-accent: var(--chat-accent);
36
+ --color-chat-check: var(--chat-check);
37
+ --color-chat-warning: var(--chat-warning);
38
+ --color-chat-warning-bg: var(--chat-warning-bg);
39
+ --color-chat-danger: var(--chat-danger);
40
+ --color-chat-focus: var(--chat-focus);
41
+
42
+ --font-chat-sans: var(--chat-font-sans);
43
+ --font-chat-serif: var(--chat-font-serif);
44
+ --font-chat-mono: var(--chat-font-mono);
45
+
46
+ --radius-chat-sm: var(--chat-radius-sm);
47
+ --radius-chat-md: var(--chat-radius-md);
48
+ --radius-chat-lg: var(--chat-radius-lg);
49
+ --radius-chat-popover: var(--chat-radius-popover);
50
+
51
+ --shadow-chat-popover: var(--chat-shadow-popover);
52
+
53
+ --animate-chat-in: chat-in 120ms ease-out;
54
+ --animate-chat-out: chat-out 90ms ease-in forwards;
55
+ --animate-chat-fade-in: chat-fade-in 200ms ease-out;
56
+ --animate-chat-fade-out: chat-fade-in 150ms ease-in reverse forwards;
57
+ --animate-chat-drawer-in: chat-drawer-in 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
58
+ --animate-chat-drawer-out: chat-drawer-in 160ms ease-in reverse forwards;
59
+ --animate-chat-drawer-in-right: chat-drawer-in-right 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
60
+ --animate-chat-drawer-out-right: chat-drawer-in-right 160ms ease-in reverse forwards;
61
+
62
+ @keyframes chat-drawer-in-right {
63
+ from {
64
+ transform: translateX(100%);
65
+ }
66
+ }
67
+
68
+ @keyframes chat-fade-in {
69
+ from {
70
+ opacity: 0;
71
+ }
72
+ }
73
+ @keyframes chat-drawer-in {
74
+ from {
75
+ transform: translateX(-100%);
76
+ }
77
+ }
78
+
79
+ @keyframes chat-in {
80
+ from {
81
+ opacity: 0;
82
+ transform: scale(0.98);
83
+ }
84
+ }
85
+ @keyframes chat-out {
86
+ to {
87
+ opacity: 0;
88
+ transform: scale(0.98);
89
+ }
90
+ }
91
+ }
92
+
93
+ @layer base {
94
+ /* Default theme: claude.ai dark. */
95
+ :root,
96
+ [data-chat-theme="claude-dark"] {
97
+ color-scheme: dark;
98
+
99
+ --chat-bg: #191919;
100
+ --chat-sidebar: #1f1f1f;
101
+ --chat-surface: #303030;
102
+ --chat-raised: #3a3a3a;
103
+ --chat-hover: #3d3d3d;
104
+ --chat-selected: #4a4a4a;
105
+ --chat-bubble: #2a2a2a;
106
+ --chat-popover: #303030;
107
+ --chat-popover-border: #383838;
108
+ --chat-popover-hover: #4f4f4f;
109
+ --chat-border: #303030;
110
+ --chat-border-strong: #454545;
111
+ /* Faint lines inside the message column, e.g. the activity box. */
112
+ --chat-divider: #222222;
113
+ --chat-code-bg: #1c1c1c;
114
+ --chat-code-border: #252525;
115
+ --chat-inline-code: #2e2e2e;
116
+ --chat-fg: #ecebe6;
117
+ --chat-muted: #a3a29e;
118
+ --chat-subtle: #75746f;
119
+ --chat-accent: #d97757;
120
+ --chat-check: #6c9cf0;
121
+ --chat-warning: #e8b04b;
122
+ --chat-warning-bg: #4a3b1c;
123
+ --chat-danger: #e5675c;
124
+ --chat-focus: #6c9cf0;
125
+
126
+ --chat-font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
127
+ --chat-font-serif: "Source Serif 4", ui-serif, Georgia, serif;
128
+ --chat-font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
129
+ /*
130
+ * --chat-font-message (unset): assistant message text. Falls back to the serif, like claude.ai; hosts
131
+ * with sans UIs set it to var(--chat-font-sans). Left unset here so it follows a nested theme's fonts.
132
+ */
133
+
134
+ --chat-radius-sm: 6px;
135
+ --chat-radius-md: 10px;
136
+ --chat-radius-lg: 16px;
137
+ --chat-radius-popover: 12px;
138
+ --chat-shadow-popover: 0 8px 24px rgb(0 0 0 / 0.5);
139
+
140
+ /* Message column. Hosts and users adjust these; ChatView reads them. */
141
+ --chat-content-width: 48rem;
142
+ --chat-content-padding: 1.5rem;
143
+ --chat-message-font-size: 1.0625rem;
144
+ --chat-message-line-height: 1.65;
145
+ }
146
+ }
147
+
148
+ /*
149
+ * Markdown in messages. Streamdown runs with a class prefix, so none of its own utility classes apply and
150
+ * the host's classes never leak in; everything is styled here by element and data-streamdown attribute.
151
+ */
152
+ @layer components {
153
+ .chat-markdown {
154
+ font-family: var(--chat-font-message, var(--chat-font-serif));
155
+ font-size: var(--chat-message-font-size);
156
+ line-height: var(--chat-message-line-height);
157
+ color: var(--chat-fg);
158
+ overflow-wrap: anywhere;
159
+ }
160
+ .chat-markdown > div > * + * {
161
+ margin-top: 0.75em;
162
+ }
163
+ .chat-markdown :is(h1, h2, h3, h4) {
164
+ font-weight: 600;
165
+ line-height: 1.3;
166
+ margin-top: 1.25em;
167
+ }
168
+ .chat-markdown h1 { font-size: 1.5em; }
169
+ .chat-markdown h2 { font-size: 1.3em; }
170
+ .chat-markdown h3 { font-size: 1.12em; }
171
+ .chat-markdown strong { font-weight: 650; }
172
+ .chat-markdown :is(ul, ol) {
173
+ padding-left: 1.6em;
174
+ }
175
+ .chat-markdown ul { list-style: disc; }
176
+ .chat-markdown ol { list-style: decimal; }
177
+ .chat-markdown li + li { margin-top: 0.3em; }
178
+ .chat-markdown li > :is(ul, ol) { margin-top: 0.3em; }
179
+ .chat-markdown a {
180
+ color: var(--chat-fg);
181
+ text-decoration: underline;
182
+ text-decoration-color: var(--chat-subtle);
183
+ text-underline-offset: 3px;
184
+ }
185
+ .chat-markdown a:hover { text-decoration-color: var(--chat-fg); }
186
+ .chat-markdown blockquote {
187
+ border-left: 3px solid var(--chat-border-strong);
188
+ padding-left: 1em;
189
+ color: var(--chat-muted);
190
+ }
191
+ .chat-markdown hr { border-color: var(--chat-border); margin-block: 1.5em; }
192
+ .chat-markdown [data-chat-inline-code] {
193
+ font-family: var(--chat-font-mono);
194
+ font-size: 0.82em;
195
+ background: var(--chat-inline-code);
196
+ border-radius: 5px;
197
+ padding: 0.12em 0.35em;
198
+ }
199
+ .chat-markdown [data-streamdown="table-wrapper"] {
200
+ overflow-x: auto;
201
+ margin-block: 1em;
202
+ }
203
+ .chat-markdown table {
204
+ border-collapse: collapse;
205
+ font-family: var(--chat-font-sans);
206
+ font-size: 0.875rem;
207
+ }
208
+ .chat-markdown :is(th, td) {
209
+ border-bottom: 1px solid var(--chat-border);
210
+ padding: 0.45em 0.9em 0.45em 0;
211
+ text-align: left;
212
+ }
213
+ .chat-markdown th { font-weight: 600; }
214
+ .chat-markdown .katex-display {
215
+ margin-block: 1em;
216
+ overflow-x: auto;
217
+ overflow-y: hidden;
218
+ }
219
+ .chat-markdown .katex { font-size: 1.08em; }
220
+ /* KaTeX CSS must match the katex version @streamdown/math renders with (0.16.x). */
221
+ }
222
+
223
+ /*
224
+ * Streamed words fade in as they mount (see word-fade.ts). The fade is long enough that several groups
225
+ * are mid-fade at once, so the end of the text trails off instead of typing left to right.
226
+ */
227
+ @layer components {
228
+ .chat-markdown.chat-fade .chat-word {
229
+ animation: chat-word-in var(--chat-fade-duration, 700ms) ease-out both;
230
+ }
231
+ }
232
+
233
+ @keyframes chat-word-in {
234
+ from { opacity: 0; }
235
+ }
236
+
237
+ /*
238
+ * A ChatView sizes itself to its pane, not the window, so several can sit side by side or stacked (an
239
+ * agent dashboard). The view is an inline-size container ("chat"); its body is a size container
240
+ * ("chat-body") so the composer and empty state can respond to a short pane too.
241
+ */
242
+ @layer components {
243
+ .chat-view {
244
+ container: chat / inline-size;
245
+ }
246
+ .chat-view-body {
247
+ container: chat-body / size;
248
+ --chat-gutter: var(--chat-content-padding);
249
+ }
250
+ @container chat (max-width: 34rem) {
251
+ .chat-view-body {
252
+ --chat-gutter: min(var(--chat-content-padding), 0.75rem);
253
+ }
254
+ }
255
+ @container chat (max-width: 24rem) {
256
+ .chat-picker-detail {
257
+ display: none;
258
+ }
259
+ }
260
+ @container chat-body (max-height: 24rem) {
261
+ .chat-empty-greeting {
262
+ display: none;
263
+ }
264
+ }
265
+ }
266
+
267
+ /* Highlighted code: each token has both theme colors; the chat theme's color-scheme picks one. */
268
+ @layer components {
269
+ .chat-code-token {
270
+ color: light-dark(var(--shiki-light), var(--shiki-dark));
271
+ }
272
+ }
273
+
274
+ /* codeThemes.background = "theme": the block takes the Shiki theme's own background and text color. Unlayered so it wins over utilities. */
275
+ [data-chat-code-bg="theme"] {
276
+ background-color: light-dark(var(--shiki-light-bg), var(--shiki-dark-bg));
277
+ }
278
+ [data-chat-code-bg="theme"] pre {
279
+ color: light-dark(var(--shiki-light), var(--shiki-dark));
280
+ }
281
+
282
+ /* A highlight sweeping across live labels ("Thinking…", "Searching the web"). */
283
+ @layer components {
284
+ /*
285
+ * The gradient repeats in fixed-size tiles and moves one tile per cycle, so the highlight travels at
286
+ * the same speed whatever the label's length. Live (activity-group.tsx) offsets the start to a shared
287
+ * clock, so re-renders and remounts never restart or jump the sweep.
288
+ */
289
+ .chat-shimmer {
290
+ background-image: linear-gradient(
291
+ 90deg,
292
+ var(--chat-muted) 0%,
293
+ var(--chat-muted) 35%,
294
+ var(--chat-fg) 50%,
295
+ var(--chat-muted) 65%,
296
+ var(--chat-muted) 100%
297
+ );
298
+ background-size: 20em 100%;
299
+ background-repeat: repeat-x;
300
+ -webkit-background-clip: text;
301
+ background-clip: text;
302
+ color: transparent;
303
+ animation: chat-shimmer var(--chat-shimmer-duration, 2s) linear infinite;
304
+ }
305
+ .chat-markdown.chat-thinking {
306
+ font-family: var(--chat-font-sans);
307
+ font-size: 0.9375rem;
308
+ line-height: 1.6;
309
+ color: var(--chat-muted);
310
+ }
311
+ }
312
+
313
+ /* A light sweeping across a card while it uploads. */
314
+ @layer components {
315
+ .chat-shimmer-surface::after {
316
+ content: "";
317
+ position: absolute;
318
+ inset: 0;
319
+ background: linear-gradient(100deg, transparent 30%, color-mix(in srgb, var(--chat-fg) 12%, transparent) 50%, transparent 70%) 0 0 / 300% 100%;
320
+ animation: chat-shimmer-surface 1.6s linear infinite;
321
+ pointer-events: none;
322
+ }
323
+ }
324
+
325
+ @keyframes chat-shimmer-surface {
326
+ from { background-position: 150% 0; }
327
+ to { background-position: -50% 0; }
328
+ }
329
+
330
+ @keyframes chat-shimmer {
331
+ from { background-position: -20em 0; }
332
+ to { background-position: 0 0; }
333
+ }
334
+
335
+ /*
336
+ * Reduced motion. Applies with the OS setting, unless data-chat-motion="full" is on an ancestor (ChatTheme
337
+ * `motion`, or <html>); and always under data-chat-motion="reduce". Keep the two lists in sync.
338
+ */
339
+ @media (prefers-reduced-motion: reduce) {
340
+ .chat-markdown.chat-fade .chat-word:not([data-chat-motion="full"] *) { animation: none; }
341
+ .chat-shimmer-surface:not([data-chat-motion="full"] *)::after { animation: none; background: color-mix(in srgb, var(--chat-fg) 6%, transparent); }
342
+ .chat-shimmer:not([data-chat-motion="full"] *) { animation: none; color: var(--chat-muted); background: none; }
343
+ /* Popovers, drawers, dialogs: no slide or scale. A tiny duration keeps exit handling working. */
344
+ [data-entering]:not([data-chat-motion="full"] *), [data-exiting]:not([data-chat-motion="full"] *) { animation-duration: 1ms !important; }
345
+ }
346
+
347
+ [data-chat-motion="reduce"] .chat-markdown.chat-fade .chat-word { animation: none; }
348
+ [data-chat-motion="reduce"] .chat-shimmer-surface::after { animation: none; background: color-mix(in srgb, var(--chat-fg) 6%, transparent); }
349
+ [data-chat-motion="reduce"] .chat-shimmer { animation: none; color: var(--chat-muted); background: none; }
350
+ [data-chat-motion="reduce"] [data-entering], [data-chat-motion="reduce"] [data-exiting] { animation-duration: 1ms !important; }
@@ -0,0 +1,22 @@
1
+ import type { Part, ThinkingPart, ToolCallPart } from "./types";
2
+ export type ActivityPart = ThinkingPart | ToolCallPart;
3
+ /**
4
+ * How an assistant message is laid out: runs of consecutive thinking and tool-call parts fold into one
5
+ * activity group (the "Recalled memory ▾" dropdown); every other part stands alone. Tool calls for which
6
+ * `standalone` is true stand alone too (`kind: "tool"`), e.g. a question's answers or a chart.
7
+ */
8
+ export type Block = {
9
+ kind: "activity";
10
+ id: string;
11
+ parts: ActivityPart[];
12
+ } | {
13
+ kind: "tool";
14
+ id: string;
15
+ part: ToolCallPart;
16
+ } | {
17
+ kind: "part";
18
+ id: string;
19
+ part: Exclude<Part, ActivityPart>;
20
+ };
21
+ export declare function isActivityPart(part: Part): part is ActivityPart;
22
+ export declare function toBlocks(parts: readonly Part[], standalone?: (part: ToolCallPart) => boolean): Block[];
@@ -0,0 +1,75 @@
1
+ import type { MessageStatus, Part, PartStatus, Usage } from "./types";
2
+ /**
3
+ * The stream a backend sends for one run (one assistant reply). Sent as server-sent events: the SSE `id`
4
+ * is `seq`, the `data` is the JSON event. Any backend in any language that emits these works with the UI.
5
+ */
6
+ export type StreamEvent = RunStart | PartStart | PartDelta | PartUpdate | PartEnd | RunUsage | RunEnd | TitleEvent;
7
+ interface Base {
8
+ runId: string;
9
+ /** Increases by one per event within a run. Clients resume with the last seq they applied. */
10
+ seq: number;
11
+ }
12
+ export interface RunStart extends Base {
13
+ type: "run.start";
14
+ messageId: string;
15
+ parentId: string | null;
16
+ createdAt: string;
17
+ model?: string;
18
+ effort?: string;
19
+ }
20
+ /** A new part. `part` is its initial state; text fields usually start empty and grow through deltas. */
21
+ export interface PartStart extends Base {
22
+ type: "part.start";
23
+ part: Part;
24
+ }
25
+ /**
26
+ * Appends to a part. `text` goes to the text of text, thinking and custom parts and to the html of widget
27
+ * parts; `inputJson` goes to the raw input of tool calls.
28
+ */
29
+ export interface PartDelta extends Base {
30
+ type: "part.delta";
31
+ partId: string;
32
+ text?: string;
33
+ inputJson?: string;
34
+ }
35
+ /** Fields a backend can set on a part after it starts. Only the fields valid for the part's type apply. */
36
+ export interface PartFields {
37
+ status?: PartStatus;
38
+ title?: string;
39
+ subtitle?: string;
40
+ durationMs?: number;
41
+ input?: unknown;
42
+ result?: unknown;
43
+ error?: string;
44
+ data?: unknown;
45
+ url?: string;
46
+ resourceUri?: string;
47
+ }
48
+ /** Sets fields while the part is still open, e.g. a tool call moving from input to running. */
49
+ export interface PartUpdate extends Base, PartFields {
50
+ type: "part.update";
51
+ partId: string;
52
+ }
53
+ /** Closes a part. Status becomes "done" unless the event sets "error". */
54
+ export interface PartEnd extends Base, PartFields {
55
+ type: "part.end";
56
+ partId: string;
57
+ }
58
+ /** Token counts so far, for backends whose provider reports usage while streaming. Replaces the last one. */
59
+ export interface RunUsage extends Base {
60
+ type: "run.usage";
61
+ usage: Usage;
62
+ }
63
+ export interface RunEnd extends Base {
64
+ type: "run.end";
65
+ status: Exclude<MessageStatus, "streaming">;
66
+ usage?: Usage;
67
+ error?: string;
68
+ }
69
+ /** The conversation got a title, usually after the first reply. Not applied to the message. */
70
+ export interface TitleEvent extends Base {
71
+ type: "title";
72
+ conversationId: string;
73
+ title: string;
74
+ }
75
+ export {};
@@ -0,0 +1,7 @@
1
+ export type * from "./types";
2
+ export type * from "./events";
3
+ export { applyEvent, reduceEvents, estimateTokens, outputTokens, activityTokens, resolveEffort, awaitingToolCalls, settleToolCall } from "./reduce";
4
+ export { toBlocks, isActivityPart } from "./blocks";
5
+ export type { Block, ActivityPart } from "./blocks";
6
+ export { readEventStream, formatEvent } from "./sse";
7
+ export { parsePartialJson } from "./partial-json";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Best-effort value of JSON that is still streaming, so a tool call's fields can show while the model
3
+ * writes them: `{"title":"Stand-up","day":"Tu` gives `{ title: "Stand-up", day: "Tu" }`. Open strings are
4
+ * cut where the text ends, open objects and arrays are closed, and a key without its value yet is left
5
+ * out. Returns undefined when nothing usable has arrived. One pass over the text.
6
+ */
7
+ export declare function parsePartialJson(text: string): unknown;
@@ -0,0 +1,33 @@
1
+ import type { StreamEvent } from "./events";
2
+ import type { Message, ModelInfo, Part, ToolCallPart } from "./types";
3
+ /**
4
+ * Applies one stream event to the message it builds. Pure and immutable: returns the same object when the
5
+ * event changes nothing, so React can skip renders.
6
+ *
7
+ * Events with a seq at or below `message.lastSeq` are dropped, so replaying from an earlier seq after a
8
+ * reconnect is safe. Events other than run.start need the message to exist; without it they are dropped.
9
+ */
10
+ export declare function applyEvent(message: Message | undefined, event: StreamEvent): Message | undefined;
11
+ /** Builds a message from a full event list, e.g. a stored run or a test fixture. */
12
+ export declare function reduceEvents(events: Iterable<StreamEvent>, initial?: Message): Message | undefined;
13
+ /** Rough token count of text, about four characters per token. For live counters, not billing. */
14
+ export declare function estimateTokens(text: string): number;
15
+ /**
16
+ * Tokens the model spent inside an activity group: its reasoning and the tool inputs it wrote. Tool
17
+ * results are not model output, so they do not count. An estimate; per-step counts are not reported.
18
+ */
19
+ export declare function activityTokens(parts: readonly Part[]): number;
20
+ /**
21
+ * Output tokens of a message so far: the backend's count when it has sent one, else an estimate from
22
+ * everything the model has written (text, thinking, tool input, widget html).
23
+ */
24
+ export declare function outputTokens(message: Message): number;
25
+ /**
26
+ * The effort to send with a model: the chosen one if the model offers it, else the model's default, else
27
+ * none. Models without effort levels never get one.
28
+ */
29
+ export declare function resolveEffort(model: ModelInfo | undefined, effort?: string): string | undefined;
30
+ /** Tool calls the client has to act on: run them (client tools) or ask the user (approvals). */
31
+ export declare function awaitingToolCalls(message: Message): ToolCallPart[];
32
+ /** Sets fields on one tool call locally, e.g. a client tool's result or the user's approval. */
33
+ export declare function settleToolCall(message: Message, partId: string, fields: Partial<Pick<ToolCallPart, "status" | "result" | "error" | "approval" | "durationMs">>): Message;
@@ -0,0 +1,8 @@
1
+ import type { StreamEvent } from "./events";
2
+ /**
3
+ * Reads a server-sent event stream of `StreamEvent`s. Each SSE message carries one JSON event in `data`;
4
+ * comments and messages without data (keep-alives) are skipped.
5
+ */
6
+ export declare function readEventStream(body: ReadableStream<Uint8Array>): AsyncGenerator<StreamEvent>;
7
+ /** Formats one event as an SSE message, for servers written in TS. */
8
+ export declare function formatEvent(event: StreamEvent): string;
@@ -0,0 +1,171 @@
1
+ export type Role = "user" | "assistant";
2
+ export type MessageStatus = "streaming" | "done" | "stopped" | "error";
3
+ /**
4
+ * Status of a single part. Tool calls go input -> running -> done | error; other parts go streaming -> done.
5
+ * A tool call is "awaiting" when the run ended waiting on the client: to run a client tool, or for the
6
+ * user to approve a tool (see ToolCallPart).
7
+ */
8
+ export type PartStatus = "streaming" | "input" | "awaiting" | "running" | "done" | "error";
9
+ export interface TextPart {
10
+ type: "text";
11
+ id: string;
12
+ text: string;
13
+ status: PartStatus;
14
+ }
15
+ /**
16
+ * Model reasoning. `title` is the short line shown collapsed ("Deriving the rejection region...").
17
+ * `visibility` says what `text` holds: the model's full reasoning, a summary of it, or nothing because the
18
+ * provider hides it (only the duration is known).
19
+ */
20
+ export interface ThinkingPart {
21
+ type: "thinking";
22
+ id: string;
23
+ text: string;
24
+ status: PartStatus;
25
+ visibility?: "full" | "summary" | "hidden";
26
+ title?: string;
27
+ durationMs?: number;
28
+ }
29
+ /**
30
+ * A tool call. `inputJson` is the raw input as streamed; `input` is the parsed value once complete.
31
+ * `title` is the display line ("Recalled memory"), `subtitle` the highlighted detail ("preferences.md").
32
+ */
33
+ export interface ToolCallPart {
34
+ type: "tool_call";
35
+ id: string;
36
+ name: string;
37
+ status: PartStatus;
38
+ inputJson: string;
39
+ input?: unknown;
40
+ /**
41
+ * While the input streams: a best-effort parse of `inputJson`, so pages and views can show fields as the
42
+ * model writes them. Strings may be cut short and keys missing. Gone once `input` is set.
43
+ */
44
+ partialInput?: unknown;
45
+ result?: unknown;
46
+ error?: string;
47
+ title?: string;
48
+ subtitle?: string;
49
+ durationMs?: number;
50
+ /**
51
+ * Where the tool runs. "server" (the default): the backend runs it and streams the result; the page can
52
+ * still react to it through the tool's `onResult`. "client": the backend ends the run with this call
53
+ * "awaiting", the browser runs it and sends the result back in a continuation request.
54
+ */
55
+ runsOn?: "server" | "client";
56
+ /** The backend asks the user before the tool runs. The client may also require it per tool. */
57
+ needsApproval?: boolean;
58
+ /** The user's answer, sent back in the continuation request. */
59
+ approval?: "granted" | "denied";
60
+ }
61
+ export interface AttachmentPart {
62
+ type: "attachment";
63
+ id: string;
64
+ status: PartStatus;
65
+ fileId: string;
66
+ name: string;
67
+ mime: string;
68
+ size: number;
69
+ url?: string;
70
+ /** Pasted text kept inline instead of uploaded. */
71
+ text?: string;
72
+ }
73
+ /**
74
+ * An interactive visual rendered in a sandboxed frame. Either inline `html` (streamed as text deltas)
75
+ * or an MCP Apps UI resource.
76
+ */
77
+ export interface WidgetPart {
78
+ type: "widget";
79
+ id: string;
80
+ status: PartStatus;
81
+ title?: string;
82
+ html?: string;
83
+ resourceUri?: string;
84
+ }
85
+ /** A host-defined part, rendered by a renderer the host registers for `kind`. */
86
+ export interface CustomPart {
87
+ type: "custom";
88
+ id: string;
89
+ status: PartStatus;
90
+ kind: string;
91
+ data: unknown;
92
+ /** Plain text fallback for hosts without a renderer for `kind`, and for copy. */
93
+ text?: string;
94
+ }
95
+ export type Part = TextPart | ThinkingPart | ToolCallPart | AttachmentPart | WidgetPart | CustomPart;
96
+ export type PartType = Part["type"];
97
+ export interface Usage {
98
+ inputTokens?: number;
99
+ outputTokens?: number;
100
+ reasoningTokens?: number;
101
+ cachedInputTokens?: number;
102
+ }
103
+ export interface Message {
104
+ id: string;
105
+ /** Previous message in the conversation tree. Edits and retries create siblings with the same parent. */
106
+ parentId: string | null;
107
+ role: Role;
108
+ status: MessageStatus;
109
+ parts: Part[];
110
+ createdAt: string;
111
+ model?: string;
112
+ effort?: string;
113
+ usage?: Usage;
114
+ error?: string;
115
+ /** Run that produced this message, and the last event applied from it. Used to resume and to drop repeats. */
116
+ runId?: string;
117
+ lastSeq?: number;
118
+ }
119
+ export interface EffortLevel {
120
+ id: string;
121
+ label: string;
122
+ recommended?: boolean;
123
+ /** Short warning shown next to the level, e.g. "5.5x or more usage". */
124
+ warning?: string;
125
+ }
126
+ export interface ModelInfo {
127
+ /** The slug sent to the backend, e.g. "gpt-6-astra". */
128
+ id: string;
129
+ /** Display name, e.g. "GPT 6 Astra". */
130
+ label: string;
131
+ description?: string;
132
+ /** Context window in tokens; the context panel's limit. */
133
+ contextWindow?: number;
134
+ /** Reasoning effort levels. Leave unset for models without them: no Effort menu, no effort sent. */
135
+ efforts?: EffortLevel[];
136
+ defaultEffort?: string;
137
+ /** Shown under "More models" instead of the main list. */
138
+ secondary?: boolean;
139
+ disabled?: boolean;
140
+ disabledReason?: string;
141
+ }
142
+ /** One slice of the context window, e.g. "System prompt" or "MCP tools". */
143
+ export interface ContextCategory {
144
+ id: string;
145
+ label: string;
146
+ tokens: number;
147
+ /** CSS color for the bar segment and swatch. Defaults to a grey. */
148
+ color?: string;
149
+ /** Loaded on demand, not counted in `used`; shown with a dash instead of a percentage. */
150
+ deferred?: boolean;
151
+ /** Held back (e.g. an autocompact buffer): shown in the bar and taken from free space, but not "used". */
152
+ reserved?: boolean;
153
+ /** Tooltip explaining the category. Categories with a hint are underlined. */
154
+ hint?: string;
155
+ }
156
+ /** An expandable list under the categories, e.g. each MCP tool or memory file and its size. */
157
+ export interface ContextGroup {
158
+ id: string;
159
+ label: string;
160
+ items: {
161
+ id: string;
162
+ label: string;
163
+ tokens: number;
164
+ }[];
165
+ }
166
+ /** What fills a conversation's context window, as reported by the backend. */
167
+ export interface ContextUsage {
168
+ limit: number;
169
+ categories: ContextCategory[];
170
+ groups?: ContextGroup[];
171
+ }
@@ -0,0 +1,13 @@
1
+ import { type ActivityPart } from "../../../core/src/index.js";
2
+ export interface ActivityGroupProps {
3
+ /** Consecutive thinking and tool-call parts, as grouped by `toBlocks`. */
4
+ parts: readonly ActivityPart[];
5
+ defaultExpanded?: boolean;
6
+ className?: string;
7
+ }
8
+ /**
9
+ * The "Ran a command, searched the web ▾" dropdown: a one-line heading that opens a bordered box with one
10
+ * row per step. Each row expands on its own; tool rows render through the registered `ToolView`.
11
+ * A group of one step skips the rows: the heading is the step's own label and the box holds its detail.
12
+ */
13
+ export declare function ActivityGroup({ parts, defaultExpanded, className }: ActivityGroupProps): import("react").JSX.Element;