@pikku/assistant-ui 0.12.4 → 0.12.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @pikku/assistant-ui
2
2
 
3
+ ## 0.12.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 9060165: The console now shows function version history, live queue depths with a Failed column, and scheduler last-run status with run history. Workflow canvas and run selector have been polished. The console build is ~6.5× faster thanks to a switch to rolldown-vite (Vite 7 + Oxc React transform).
8
+
3
9
  ## 0.12.4
4
10
 
5
11
  ### Patch Changes
@@ -33,7 +39,6 @@
33
39
  ### Patch Changes
34
40
 
35
41
  - f85c234: Add unified credential system with per-user OAuth and AI agent pre-flight checks
36
-
37
42
  - Unified CredentialService with lazy loading per user via pikkuUserId
38
43
  - wire.getCredential() for typed single credential lookup
39
44
  - MissingCredentialError with structured payload for client-side connect flows
@@ -1,6 +1,8 @@
1
+ import { type ComponentType, type ReactNode } from 'react';
1
2
  import { type ToolCallMessagePartComponent } from '@assistant-ui/react';
2
3
  import { type PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
3
4
  export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
5
+ initialPrompt?: string;
4
6
  emptyMessage?: string;
5
7
  /** Hide tool calls from the chat display.
6
8
  * - `true`: hide all non-approval tool calls
@@ -21,5 +23,7 @@ export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
21
23
  * (which still respects `hideToolCalls` and the approval-request UI).
22
24
  */
23
25
  toolComponents?: Record<string, ToolCallMessagePartComponent>;
26
+ renderAssistantText?: (text: string) => ReactNode;
27
+ generativeUIComponents?: Record<string, ComponentType<any>>;
24
28
  }
25
29
  export declare function PikkuAgentChat(props: PikkuAgentChatProps): import("react/jsx-runtime").JSX.Element;
@@ -60,6 +60,8 @@ const darkColors = {
60
60
  const ColorsContext = (0, react_1.createContext)(lightColors);
61
61
  const HideToolCallsContext = (0, react_1.createContext)(undefined);
62
62
  const ToolComponentsContext = (0, react_1.createContext)(undefined);
63
+ const GenerativeUIComponentsContext = (0, react_1.createContext)(undefined);
64
+ const RenderAssistantTextContext = (0, react_1.createContext)(undefined);
63
65
  function shouldHideToolCall(hideToolCalls, toolName) {
64
66
  if (!hideToolCalls)
65
67
  return false;
@@ -310,6 +312,8 @@ const UserMessage = () => {
310
312
  const AssistantMessage = () => {
311
313
  const colors = (0, react_1.useContext)(ColorsContext);
312
314
  const toolComponents = (0, react_1.useContext)(ToolComponentsContext);
315
+ const generativeUIComponents = (0, react_1.useContext)(GenerativeUIComponentsContext);
316
+ const renderAssistantText = (0, react_1.useContext)(RenderAssistantTextContext);
313
317
  return ((0, jsx_runtime_1.jsx)("div", { style: {
314
318
  display: 'flex',
315
319
  justifyContent: 'flex-start',
@@ -318,13 +322,16 @@ const AssistantMessage = () => {
318
322
  padding: 12,
319
323
  borderRadius: 12,
320
324
  backgroundColor: colors.assistantBubble,
321
- }, children: [(0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.Content, { components: {
322
- Text: ({ text }) => (0, jsx_runtime_1.jsx)(MarkdownText, { text: text, colors: colors }),
323
- tools: {
325
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.Content, { components: Object.assign({ Text: ({ text }) => renderAssistantText ? ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: renderAssistantText(text) })) : ((0, jsx_runtime_1.jsx)(MarkdownText, { text: text, colors: colors })), tools: {
324
326
  by_name: toolComponents,
325
327
  Fallback: (props) => ((0, jsx_runtime_1.jsx)(ToolCallDisplay, { toolCallId: props.toolCallId, toolName: props.toolName, args: props.args, result: props.result, status: (0, use_pikku_agent_runtime_js_1.resolvePikkuToolStatus)(props.status, props.result), addResult: props.addResult })),
326
- },
327
- } }), (0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.If, { last: true, children: (0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.If, { running: true, children: (0, jsx_runtime_1.jsx)("div", { style: {
328
+ } }, (generativeUIComponents
329
+ ? {
330
+ generativeUI: {
331
+ components: generativeUIComponents,
332
+ },
333
+ }
334
+ : {})) }), (0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.If, { last: true, children: (0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.If, { running: true, children: (0, jsx_runtime_1.jsx)("div", { style: {
328
335
  display: 'flex',
329
336
  alignItems: 'center',
330
337
  gap: 6,
@@ -333,78 +340,99 @@ const AssistantMessage = () => {
333
340
  color: colors.textMuted,
334
341
  }, children: "Thinking..." }) }) })] })] }) }));
335
342
  };
343
+ const ComposerPrefill = ({ text }) => {
344
+ const composer = (0, react_2.useComposerRuntime)();
345
+ const filled = (0, react_1.useRef)(false);
346
+ (0, react_1.useEffect)(() => {
347
+ if (filled.current || !text)
348
+ return;
349
+ filled.current = true;
350
+ if (composer.getState().text === '')
351
+ composer.setText(text);
352
+ }, [text, composer]);
353
+ return null;
354
+ };
336
355
  const PikkuComposer = ({ disabled, }) => {
337
356
  const colors = (0, react_1.useContext)(ColorsContext);
338
- return ((0, jsx_runtime_1.jsx)("div", { style: { padding: '8px 0 16px' }, children: (0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Root, { children: (0, jsx_runtime_1.jsxs)("div", { style: Object.assign({ border: `1px solid ${colors.border}`, borderRadius: 12, overflow: 'hidden', display: 'flex', alignItems: 'flex-end', padding: '6px 12px', gap: 8 }, (disabled
357
+ return ((0, jsx_runtime_1.jsx)("div", { style: { padding: '8px 0 16px' }, children: (0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Root, { children: (0, jsx_runtime_1.jsxs)("div", { style: Object.assign({ position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column', gap: 10, backgroundColor: colors.assistantBubble, border: `1px solid ${colors.border}`, borderRadius: 24, padding: '14px 12px 10px', boxShadow: darkColors.text === colors.text
358
+ ? '0 14px 30px rgba(0,0,0,0.24)'
359
+ : '0 14px 30px rgba(0,0,0,0.08)' }, (disabled
339
360
  ? { opacity: 0.5, pointerEvents: 'none' }
340
- : {})), children: [(0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Input, { placeholder: disabled ? 'Respond to approval request above...' : 'Message...', rows: 2, disabled: disabled, style: {
341
- flex: 1,
361
+ : {})), children: [(0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Input, { placeholder: disabled ? 'Respond to approval request above...' : 'Message...', rows: 1, disabled: disabled !== null && disabled !== void 0 ? disabled : false, style: {
362
+ width: '100%',
363
+ background: 'transparent',
342
364
  border: 'none',
343
365
  outline: 'none',
344
- resize: 'none',
366
+ color: colors.text,
345
367
  fontSize: 14,
346
368
  fontFamily: 'inherit',
347
- padding: '4px 0',
348
- background: colors.inputBg,
349
- color: colors.text,
350
- } }), (0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Send, { disabled: disabled, style: {
351
- width: 28,
352
- height: 28,
353
- borderRadius: '50%',
354
- border: 'none',
355
- background: disabled ? colors.textMuted : colors.sendBg,
356
- color: colors.sendColor,
357
- cursor: disabled ? 'not-allowed' : 'pointer',
369
+ resize: 'none',
370
+ lineHeight: 1.5,
371
+ overflowY: 'auto',
372
+ } }), (0, jsx_runtime_1.jsx)("div", { style: {
373
+ width: '100%',
358
374
  display: 'flex',
359
375
  alignItems: 'center',
360
- justifyContent: 'center',
361
- flexShrink: 0,
362
- marginBottom: 2,
363
- fontSize: 14,
364
- }, children: "\u25B6" })] }) }) }));
376
+ gap: 6,
377
+ minWidth: 0,
378
+ justifyContent: 'flex-end',
379
+ }, children: (0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Send, { disabled: disabled !== null && disabled !== void 0 ? disabled : false, style: {
380
+ width: 30,
381
+ height: 30,
382
+ borderRadius: 999,
383
+ border: `1px solid ${colors.border}`,
384
+ background: '#e8e8e8',
385
+ color: '#111111',
386
+ cursor: disabled ? 'not-allowed' : 'pointer',
387
+ display: 'flex',
388
+ alignItems: 'center',
389
+ justifyContent: 'center',
390
+ flexShrink: 0,
391
+ transition: 'background-color 150ms ease, color 150ms ease, border-color 150ms ease',
392
+ }, children: "\u2191" }) })] }) }) }));
365
393
  };
366
394
  function PikkuAgentChat(props) {
367
- const { emptyMessage, hideToolCalls, dark, maxWidth = 768, toolComponents } = props, runtimeOptions = __rest(props, ["emptyMessage", "hideToolCalls", "dark", "maxWidth", "toolComponents"]);
395
+ const { emptyMessage, hideToolCalls, dark, maxWidth = 768, toolComponents, renderAssistantText, generativeUIComponents, initialPrompt } = props, runtimeOptions = __rest(props, ["emptyMessage", "hideToolCalls", "dark", "maxWidth", "toolComponents", "renderAssistantText", "generativeUIComponents", "initialPrompt"]);
368
396
  const { runtime, isAwaitingApproval, pendingApprovals, handleApproval } = (0, use_pikku_agent_runtime_js_1.usePikkuAgentRuntime)(runtimeOptions);
369
397
  const colors = dark ? darkColors : lightColors;
370
- return ((0, jsx_runtime_1.jsx)(ColorsContext.Provider, { value: colors, children: (0, jsx_runtime_1.jsx)(use_pikku_agent_runtime_js_1.PikkuApprovalContext.Provider, { value: { pendingApprovals, handleApproval }, children: (0, jsx_runtime_1.jsx)(HideToolCallsContext.Provider, { value: hideToolCalls, children: (0, jsx_runtime_1.jsx)(ToolComponentsContext.Provider, { value: toolComponents, children: (0, jsx_runtime_1.jsx)(react_2.AssistantRuntimeProvider, { runtime: runtime, children: (0, jsx_runtime_1.jsx)("div", { style: {
371
- height: '100%',
372
- display: 'flex',
373
- flexDirection: 'column',
374
- background: colors.bg,
375
- }, children: (0, jsx_runtime_1.jsxs)(react_2.ThreadPrimitive.Root, { style: {
376
- display: 'flex',
377
- flexDirection: 'column',
378
- flex: 1,
379
- minHeight: 0,
380
- }, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Viewport, { style: {
381
- flex: 1,
382
- minHeight: 0,
383
- overflowY: 'auto',
384
- }, children: (0, jsx_runtime_1.jsxs)("div", { style: {
385
- maxWidth: maxWidth === 'none' ? undefined : maxWidth,
386
- margin: '0 auto',
387
- padding: 16,
398
+ return ((0, jsx_runtime_1.jsx)(ColorsContext.Provider, { value: colors, children: (0, jsx_runtime_1.jsx)(use_pikku_agent_runtime_js_1.PikkuApprovalContext.Provider, { value: { pendingApprovals, handleApproval }, children: (0, jsx_runtime_1.jsx)(HideToolCallsContext.Provider, { value: hideToolCalls, children: (0, jsx_runtime_1.jsx)(ToolComponentsContext.Provider, { value: toolComponents, children: (0, jsx_runtime_1.jsx)(GenerativeUIComponentsContext.Provider, { value: generativeUIComponents, children: (0, jsx_runtime_1.jsx)(RenderAssistantTextContext.Provider, { value: renderAssistantText, children: (0, jsx_runtime_1.jsxs)(react_2.AssistantRuntimeProvider, { runtime: runtime, children: [(0, jsx_runtime_1.jsx)(ComposerPrefill, { text: initialPrompt }), (0, jsx_runtime_1.jsx)("div", { style: {
399
+ height: '100%',
400
+ display: 'flex',
401
+ flexDirection: 'column',
402
+ background: colors.bg,
403
+ }, children: (0, jsx_runtime_1.jsxs)(react_2.ThreadPrimitive.Root, { style: {
388
404
  display: 'flex',
389
405
  flexDirection: 'column',
390
- gap: 16,
391
- }, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Empty, { children: (0, jsx_runtime_1.jsx)("div", { style: {
406
+ flex: 1,
407
+ minHeight: 0,
408
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Viewport, { style: {
409
+ flex: 1,
410
+ minHeight: 0,
411
+ overflowY: 'auto',
412
+ }, children: (0, jsx_runtime_1.jsxs)("div", { style: {
413
+ maxWidth: maxWidth === 'none' ? undefined : maxWidth,
414
+ margin: '0 auto',
415
+ padding: 16,
392
416
  display: 'flex',
393
- alignItems: 'center',
394
- justifyContent: 'center',
395
- minHeight: 300,
396
- color: colors.textMuted,
397
- textAlign: 'center',
398
- fontSize: 14,
399
- }, children: emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : (props.threadId
400
- ? 'Send a message to start the conversation.'
401
- : 'Start a new conversation.') }) }), (0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Messages, { components: {
402
- UserMessage,
403
- AssistantMessage,
404
- } })] }) }), (0, jsx_runtime_1.jsx)("div", { style: {
405
- maxWidth: maxWidth === 'none' ? undefined : maxWidth,
406
- margin: '0 auto',
407
- width: '100%',
408
- padding: '0 16px',
409
- }, children: (0, jsx_runtime_1.jsx)(PikkuComposer, { disabled: isAwaitingApproval }) })] }) }) }) }) }) }) }));
417
+ flexDirection: 'column',
418
+ gap: 16,
419
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Empty, { children: (0, jsx_runtime_1.jsx)("div", { style: {
420
+ display: 'flex',
421
+ alignItems: 'center',
422
+ justifyContent: 'center',
423
+ minHeight: 300,
424
+ color: colors.textMuted,
425
+ textAlign: 'center',
426
+ fontSize: 14,
427
+ }, children: emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : (props.threadId
428
+ ? 'Send a message to start the conversation.'
429
+ : 'Start a new conversation.') }) }), (0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Messages, { components: {
430
+ UserMessage,
431
+ AssistantMessage,
432
+ } })] }) }), (0, jsx_runtime_1.jsx)("div", { style: {
433
+ maxWidth: maxWidth === 'none' ? undefined : maxWidth,
434
+ margin: '0 auto',
435
+ width: '100%',
436
+ padding: '0 16px',
437
+ }, children: (0, jsx_runtime_1.jsx)(PikkuComposer, { disabled: isAwaitingApproval }) })] }) })] }) }) }) }) }) }) }));
410
438
  }
@@ -73,7 +73,7 @@ function parseSSEStream(reader) {
73
73
  * Shared helper: consume an SSE stream and populate text/toolCalls.
74
74
  * Returns an array of PendingApprovals when the stream requests them, or empty when done.
75
75
  */
76
- function processStream(reader, text, toolCalls, yieldContent, onFinish) {
76
+ function processStream(reader, text, toolCalls, structuredParts, yieldContent, onFinish) {
77
77
  return __awaiter(this, void 0, void 0, function* () {
78
78
  var _a, e_1, _b, _c;
79
79
  const pendingApprovals = [];
@@ -122,6 +122,31 @@ function processStream(reader, text, toolCalls, yieldContent, onFinish) {
122
122
  }
123
123
  break;
124
124
  }
125
+ case 'generative-ui': {
126
+ const nextPart = {
127
+ type: 'generative-ui',
128
+ spec: event.spec,
129
+ };
130
+ const existingIndex = structuredParts.findIndex((part) => part.type === 'generative-ui');
131
+ if (existingIndex === -1)
132
+ structuredParts.push(nextPart);
133
+ else
134
+ structuredParts[existingIndex] = nextPart;
135
+ break;
136
+ }
137
+ case 'data': {
138
+ const nextPart = {
139
+ type: 'data',
140
+ name: event.name,
141
+ data: event.data,
142
+ };
143
+ const existingIndex = structuredParts.findIndex((part) => part.type === 'data' && part.name === event.name);
144
+ if (existingIndex === -1)
145
+ structuredParts.push(nextPart);
146
+ else
147
+ structuredParts[existingIndex] = nextPart;
148
+ break;
149
+ }
125
150
  case 'approval-request':
126
151
  pendingApprovals.push({
127
152
  toolCallId: event.toolCallId,
@@ -205,13 +230,32 @@ function resolvePikkuToolStatus(status, result) {
205
230
  return { type: 'error' };
206
231
  return { type: 'completed' };
207
232
  }
208
- function buildContent(text, toolCalls) {
233
+ function buildRichContent(text, structuredParts, toolCalls) {
209
234
  const content = [];
210
235
  if (text.value)
211
236
  content.push({ type: 'text', text: text.value });
237
+ content.push(...structuredParts);
212
238
  content.push(...toolCalls);
213
239
  return content;
214
240
  }
241
+ function buildContentFromAgentResult(result) {
242
+ const content = [];
243
+ if (typeof result === 'string') {
244
+ if (result)
245
+ content.push({ type: 'text', text: result });
246
+ return content;
247
+ }
248
+ if (!result || typeof result !== 'object')
249
+ return content;
250
+ const record = result;
251
+ if (typeof record.text === 'string' && record.text) {
252
+ content.push({ type: 'text', text: record.text });
253
+ }
254
+ if (record.ui != null) {
255
+ content.push({ type: 'generative-ui', spec: record.ui });
256
+ }
257
+ return content;
258
+ }
215
259
  function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDecisionsRef, setPendingApprovalsRef, onFinishRef) {
216
260
  return {
217
261
  run(_a) {
@@ -237,6 +281,7 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
237
281
  // The last resume triggers continuation (next LLM step).
238
282
  let lastText = { value: '' };
239
283
  let lastToolCalls = [];
284
+ let lastStructuredParts = [];
240
285
  let nextApprovals = [];
241
286
  for (let i = 0; i < decisions.length; i++) {
242
287
  const decision = decisions[i];
@@ -262,8 +307,9 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
262
307
  }
263
308
  const text = { value: '' };
264
309
  const toolCalls = [];
310
+ const structuredParts = [];
265
311
  const reader = resumeResponse.body.getReader();
266
- const streamApprovals = yield __await(processStream(reader, text, toolCalls, () => { }, i === decisions.length - 1
312
+ const streamApprovals = yield __await(processStream(reader, text, toolCalls, structuredParts, () => { }, i === decisions.length - 1
267
313
  ? ((_d = onFinishRef.current) !== null && _d !== void 0 ? _d : undefined)
268
314
  : undefined)
269
315
  // Keep the last resume's output (it has continuation content)
@@ -271,12 +317,13 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
271
317
  // Keep the last resume's output (it has continuation content)
272
318
  lastText = text;
273
319
  lastToolCalls = toolCalls;
320
+ lastStructuredParts = structuredParts;
274
321
  if (streamApprovals.length > 0) {
275
322
  nextApprovals = streamApprovals;
276
323
  }
277
324
  }
278
325
  // Build content from the last resume's output
279
- const content = buildContent(lastText, lastToolCalls);
326
+ const content = buildRichContent(lastText, lastStructuredParts, lastToolCalls);
280
327
  if (nextApprovals.length > 0) {
281
328
  // More approvals from continuation — show them
282
329
  pendingApprovalsRef.current = nextApprovals;
@@ -301,7 +348,7 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
301
348
  lastToolCalls.push(approvalToolCall);
302
349
  }
303
350
  }
304
- const updatedContent = buildContent(lastText, lastToolCalls);
351
+ const updatedContent = buildRichContent(lastText, lastStructuredParts, lastToolCalls);
305
352
  yield yield __await({
306
353
  content: updatedContent,
307
354
  status: {
@@ -380,14 +427,15 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
380
427
  }
381
428
  const text = { value: '' };
382
429
  const toolCalls = [];
430
+ const structuredParts = [];
383
431
  let pendingContent = null;
384
432
  const yieldContent = () => {
385
- const content = buildContent(text, toolCalls);
433
+ const content = buildRichContent(text, structuredParts, toolCalls);
386
434
  if (content.length > 0)
387
435
  pendingContent = content;
388
436
  };
389
437
  const reader = response.body.getReader();
390
- const approvals = yield __await(processStream(reader, text, toolCalls, yieldContent, (_e = onFinishRef.current) !== null && _e !== void 0 ? _e : undefined));
438
+ const approvals = yield __await(processStream(reader, text, toolCalls, structuredParts, yieldContent, (_e = onFinishRef.current) !== null && _e !== void 0 ? _e : undefined));
391
439
  if (approvals.length === 0) {
392
440
  // No approval needed — yield final content and done
393
441
  if (pendingContent) {
@@ -427,7 +475,7 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
427
475
  toolCalls.splice(i, 1);
428
476
  }
429
477
  }
430
- const content = buildContent(text, toolCalls);
478
+ const content = buildRichContent(text, structuredParts, toolCalls);
431
479
  yield yield __await({
432
480
  content,
433
481
  status: {
@@ -497,7 +545,12 @@ const convertDbMessages = (dbMessages) => {
497
545
  continue;
498
546
  const parts = [];
499
547
  if (msg.content) {
500
- parts.push({ type: 'text', text: msg.content });
548
+ if (Array.isArray(msg.content)) {
549
+ parts.push(...msg.content);
550
+ }
551
+ else {
552
+ parts.push({ type: 'text', text: msg.content });
553
+ }
501
554
  }
502
555
  if (Array.isArray(msg.toolCalls)) {
503
556
  for (const tc of msg.toolCalls) {
@@ -587,6 +640,7 @@ function createPikkuNonStreamingAdapter(optionsRef, pendingApprovalsRef, approva
587
640
  // Resume uses SSE (same as streaming mode)
588
641
  let lastText = { value: '' };
589
642
  let lastToolCalls = [];
643
+ let lastStructuredParts = [];
590
644
  let nextApprovals = [];
591
645
  for (let i = 0; i < decisions.length; i++) {
592
646
  const decision = decisions[i];
@@ -611,17 +665,19 @@ function createPikkuNonStreamingAdapter(optionsRef, pendingApprovalsRef, approva
611
665
  }
612
666
  const text = { value: '' };
613
667
  const toolCalls = [];
668
+ const structuredParts = [];
614
669
  const reader = resumeResponse.body.getReader();
615
- const streamApprovals = yield __await(processStream(reader, text, toolCalls, () => { }, i === decisions.length - 1
670
+ const streamApprovals = yield __await(processStream(reader, text, toolCalls, structuredParts, () => { }, i === decisions.length - 1
616
671
  ? ((_d = onFinishRef.current) !== null && _d !== void 0 ? _d : undefined)
617
672
  : undefined));
618
673
  lastText = text;
619
674
  lastToolCalls = toolCalls;
675
+ lastStructuredParts = structuredParts;
620
676
  if (streamApprovals.length > 0) {
621
677
  nextApprovals = streamApprovals;
622
678
  }
623
679
  }
624
- const content = buildContent(lastText, lastToolCalls);
680
+ const content = buildRichContent(lastText, lastStructuredParts, lastToolCalls);
625
681
  if (nextApprovals.length > 0) {
626
682
  pendingApprovalsRef.current = nextApprovals;
627
683
  setPendingApprovalsRef.current(nextApprovals);
@@ -644,7 +700,7 @@ function createPikkuNonStreamingAdapter(optionsRef, pendingApprovalsRef, approva
644
700
  lastToolCalls.push(approvalToolCall);
645
701
  }
646
702
  }
647
- const updatedContent = buildContent(lastText, lastToolCalls);
703
+ const updatedContent = buildRichContent(lastText, lastStructuredParts, lastToolCalls);
648
704
  yield yield __await({
649
705
  content: updatedContent,
650
706
  status: {
@@ -701,9 +757,7 @@ function createPikkuNonStreamingAdapter(optionsRef, pendingApprovalsRef, approva
701
757
  : {})), (approval.reason ? { __approvalReason: approval.reason } : {})),
702
758
  }));
703
759
  const content = [];
704
- if (json.result) {
705
- content.push({ type: 'text', text: json.result });
706
- }
760
+ content.push(...buildContentFromAgentResult(json.result));
707
761
  content.push(...toolCalls);
708
762
  yield yield __await({
709
763
  content,
@@ -716,10 +770,7 @@ function createPikkuNonStreamingAdapter(optionsRef, pendingApprovalsRef, approva
716
770
  }
717
771
  // No approvals — yield complete content
718
772
  (_f = onFinishRef.current) === null || _f === void 0 ? void 0 : _f.call(onFinishRef);
719
- const content = [];
720
- if (json.result) {
721
- content.push({ type: 'text', text: String(json.result) });
722
- }
773
+ const content = buildContentFromAgentResult(json.result);
723
774
  if (content.length > 0) {
724
775
  yield yield __await({ content });
725
776
  }
@@ -1,6 +1,8 @@
1
+ import { type ComponentType, type ReactNode } from 'react';
1
2
  import { type ToolCallMessagePartComponent } from '@assistant-ui/react';
2
3
  import { type PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
3
4
  export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
5
+ initialPrompt?: string;
4
6
  emptyMessage?: string;
5
7
  /** Hide tool calls from the chat display.
6
8
  * - `true`: hide all non-approval tool calls
@@ -21,5 +23,7 @@ export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
21
23
  * (which still respects `hideToolCalls` and the approval-request UI).
22
24
  */
23
25
  toolComponents?: Record<string, ToolCallMessagePartComponent>;
26
+ renderAssistantText?: (text: string) => ReactNode;
27
+ generativeUIComponents?: Record<string, ComponentType<any>>;
24
28
  }
25
29
  export declare function PikkuAgentChat(props: PikkuAgentChatProps): import("react/jsx-runtime").JSX.Element;