@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 +6 -1
- package/dist/cjs/pikku-agent-chat.d.ts +4 -0
- package/dist/cjs/pikku-agent-chat.js +91 -63
- package/dist/cjs/use-pikku-agent-runtime.js +70 -19
- package/dist/esm/pikku-agent-chat.d.ts +4 -0
- package/dist/esm/pikku-agent-chat.js +102 -67
- package/dist/esm/use-pikku-agent-runtime.js +70 -19
- package/package.json +1 -1
- package/src/pikku-agent-chat.tsx +362 -156
- package/src/use-pikku-agent-runtime.ts +103 -15
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
|
-
|
|
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({
|
|
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:
|
|
341
|
-
|
|
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
|
-
|
|
366
|
+
color: colors.text,
|
|
345
367
|
fontSize: 14,
|
|
346
368
|
fontFamily: 'inherit',
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
} }), (0, jsx_runtime_1.jsx)(
|
|
351
|
-
width:
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
391
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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;
|