@pikku/assistant-ui 0.12.2 → 0.12.4
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 +42 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/pikku-agent-chat.d.ts +14 -0
- package/dist/cjs/pikku-agent-chat.js +122 -48
- package/dist/cjs/use-pikku-agent-runtime.d.ts +15 -2
- package/dist/cjs/use-pikku-agent-runtime.js +41 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/pikku-agent-chat.d.ts +14 -0
- package/dist/esm/pikku-agent-chat.js +124 -50
- package/dist/esm/use-pikku-agent-runtime.d.ts +15 -2
- package/dist/esm/use-pikku-agent-runtime.js +41 -4
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/pikku-agent-chat.tsx +43 -3
- package/src/use-pikku-agent-runtime.ts +60 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @pikku/assistant-ui
|
|
2
2
|
|
|
3
|
+
## 0.12.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 424c777: `PikkuAgentChat` now accepts a `toolComponents` prop — a map of
|
|
8
|
+
`toolName` → React component — for per-tool custom rendering inside
|
|
9
|
+
the assistant bubble. Unmatched tool calls continue to fall through to
|
|
10
|
+
the default expandable tool-call display.
|
|
11
|
+
|
|
12
|
+
This unlocks generative-UI patterns: register a `renderWidget` tool on
|
|
13
|
+
the agent, return structured props from it, and mount real UI (charts,
|
|
14
|
+
diffs, cards) inline in the chat from the persisted tool-call args.
|
|
15
|
+
Because the rendered widget is just a tool call under the hood, it
|
|
16
|
+
survives refresh, streams correctly, and stays part of the thread's
|
|
17
|
+
history.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
<PikkuAgentChat
|
|
21
|
+
agentName="myAgent"
|
|
22
|
+
threadId={threadId}
|
|
23
|
+
resourceId={userId}
|
|
24
|
+
api="/rpc/agent"
|
|
25
|
+
toolComponents={{
|
|
26
|
+
renderWidget: ({ args }) => <WidgetRegistry spec={args} />,
|
|
27
|
+
}}
|
|
28
|
+
/>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 0.12.3
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- f85c234: Add unified credential system with per-user OAuth and AI agent pre-flight checks
|
|
36
|
+
|
|
37
|
+
- Unified CredentialService with lazy loading per user via pikkuUserId
|
|
38
|
+
- wire.getCredential() for typed single credential lookup
|
|
39
|
+
- MissingCredentialError with structured payload for client-side connect flows
|
|
40
|
+
- Console UI: Global/Users credential tabs, per-user OAuth connect/revoke
|
|
41
|
+
- AI agent pre-flight check: detects missing OAuth credentials from addon metadata, shows "Connect your accounts" prompt before chat
|
|
42
|
+
- CLI codegen: generates credentialsMeta per addon package for runtime lookup
|
|
43
|
+
- Vercel AI runner: catches MissingCredentialError as runtime fallback
|
|
44
|
+
|
|
3
45
|
## 0.12.2
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { usePikkuAgentRuntime, usePikkuAgentNonStreamingRuntime, PikkuApprovalContext, usePikkuApproval, convertDbMessages, isDeniedResult, resolvePikkuToolStatus, } from './use-pikku-agent-runtime.js';
|
|
2
|
-
export type { PikkuAgentRuntimeOptions, PendingApproval, PikkuApprovalContextValue, PikkuToolStatusType, PikkuToolStatus, } from './use-pikku-agent-runtime.js';
|
|
2
|
+
export type { PikkuAgentRuntimeOptions, PendingApproval, PikkuApprovalContextValue, PikkuToolStatusType, PikkuToolStatus, MissingCredentialPayload, } from './use-pikku-agent-runtime.js';
|
|
3
3
|
export { PikkuAgentChat } from './pikku-agent-chat.js';
|
|
4
4
|
export type { PikkuAgentChatProps } from './pikku-agent-chat.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ToolCallMessagePartComponent } from '@assistant-ui/react';
|
|
1
2
|
import { type PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
|
|
2
3
|
export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
|
|
3
4
|
emptyMessage?: string;
|
|
@@ -7,5 +8,18 @@ export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
|
|
|
7
8
|
*/
|
|
8
9
|
hideToolCalls?: boolean | string[];
|
|
9
10
|
dark?: boolean;
|
|
11
|
+
/** Max width of the chat content area. Defaults to 768. Set to 'none' for full width. */
|
|
12
|
+
maxWidth?: number | 'none';
|
|
13
|
+
/**
|
|
14
|
+
* Per-tool renderers. Map `toolName` → React component to replace the
|
|
15
|
+
* default expandable tool-call box for that tool. Enables generative-UI
|
|
16
|
+
* patterns: e.g. register a `renderWidget` tool on the agent and mount
|
|
17
|
+
* real UI (charts, diffs, cards) inline in the assistant bubble from the
|
|
18
|
+
* persisted tool-call args.
|
|
19
|
+
*
|
|
20
|
+
* Any tool without an entry here falls through to the default renderer
|
|
21
|
+
* (which still respects `hideToolCalls` and the approval-request UI).
|
|
22
|
+
*/
|
|
23
|
+
toolComponents?: Record<string, ToolCallMessagePartComponent>;
|
|
10
24
|
}
|
|
11
25
|
export declare function PikkuAgentChat(props: PikkuAgentChatProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -34,6 +34,8 @@ const lightColors = {
|
|
|
34
34
|
successColor: '#2e7d32',
|
|
35
35
|
errorBg: '#ffebee',
|
|
36
36
|
errorColor: '#c62828',
|
|
37
|
+
warningBg: '#fff3e0',
|
|
38
|
+
warningColor: '#e65100',
|
|
37
39
|
};
|
|
38
40
|
const darkColors = {
|
|
39
41
|
bg: 'transparent',
|
|
@@ -52,9 +54,12 @@ const darkColors = {
|
|
|
52
54
|
successColor: '#00e68a',
|
|
53
55
|
errorBg: 'rgba(220, 38, 38, 0.1)',
|
|
54
56
|
errorColor: '#f87171',
|
|
57
|
+
warningBg: 'rgba(245, 158, 11, 0.1)',
|
|
58
|
+
warningColor: '#fbbf24',
|
|
55
59
|
};
|
|
56
60
|
const ColorsContext = (0, react_1.createContext)(lightColors);
|
|
57
61
|
const HideToolCallsContext = (0, react_1.createContext)(undefined);
|
|
62
|
+
const ToolComponentsContext = (0, react_1.createContext)(undefined);
|
|
58
63
|
function shouldHideToolCall(hideToolCalls, toolName) {
|
|
59
64
|
if (!hideToolCalls)
|
|
60
65
|
return false;
|
|
@@ -145,7 +150,9 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
145
150
|
padding: '2px 6px',
|
|
146
151
|
borderRadius: 3,
|
|
147
152
|
background: responded === 'approved' ? colors.successBg : colors.errorBg,
|
|
148
|
-
color: responded === 'approved'
|
|
153
|
+
color: responded === 'approved'
|
|
154
|
+
? colors.successColor
|
|
155
|
+
: colors.errorColor,
|
|
149
156
|
}, children: responded })] }));
|
|
150
157
|
}
|
|
151
158
|
return ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
@@ -170,7 +177,13 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
170
177
|
borderRadius: 3,
|
|
171
178
|
background: colors.errorBg,
|
|
172
179
|
color: colors.errorColor,
|
|
173
|
-
}, children: "error" })), status.type === '
|
|
180
|
+
}, children: "error" })), status.type === 'missing-credential' && ((0, jsx_runtime_1.jsx)("span", { style: {
|
|
181
|
+
fontSize: 11,
|
|
182
|
+
padding: '1px 5px',
|
|
183
|
+
borderRadius: 3,
|
|
184
|
+
background: colors.warningBg,
|
|
185
|
+
color: colors.warningColor,
|
|
186
|
+
}, children: "credential required" })), status.type === 'denied' && ((0, jsx_runtime_1.jsx)("span", { style: {
|
|
174
187
|
fontSize: 11,
|
|
175
188
|
padding: '1px 5px',
|
|
176
189
|
borderRadius: 3,
|
|
@@ -189,7 +202,12 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
189
202
|
borderRadius: 4,
|
|
190
203
|
overflow: 'auto',
|
|
191
204
|
color: colors.text,
|
|
192
|
-
}, children: JSON.stringify(displayArgs, null, 2) }), result !== undefined && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: {
|
|
205
|
+
}, children: JSON.stringify(displayArgs, null, 2) }), result !== undefined && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: {
|
|
206
|
+
fontSize: 12,
|
|
207
|
+
color: colors.textMuted,
|
|
208
|
+
marginTop: 8,
|
|
209
|
+
marginBottom: 2,
|
|
210
|
+
}, children: "Result:" }), (0, jsx_runtime_1.jsx)("pre", { style: {
|
|
193
211
|
fontSize: 11,
|
|
194
212
|
background: colors.codeBg,
|
|
195
213
|
padding: 8,
|
|
@@ -200,25 +218,68 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
200
218
|
? result
|
|
201
219
|
: JSON.stringify(result, null, 2) })] }))] }))] }));
|
|
202
220
|
};
|
|
203
|
-
const MarkdownText = ({ text, colors }) => {
|
|
221
|
+
const MarkdownText = ({ text, colors, }) => {
|
|
204
222
|
const components = (0, react_1.useMemo)(() => ({
|
|
205
|
-
p: ({ children }) => ((0, jsx_runtime_1.jsx)("p", { style: {
|
|
223
|
+
p: ({ children }) => ((0, jsx_runtime_1.jsx)("p", { style: {
|
|
224
|
+
margin: '0 0 8px',
|
|
225
|
+
fontSize: 14,
|
|
226
|
+
lineHeight: 1.6,
|
|
227
|
+
color: colors.text,
|
|
228
|
+
}, children: children })),
|
|
206
229
|
strong: ({ children }) => ((0, jsx_runtime_1.jsx)("strong", { style: { fontWeight: 600, color: colors.text }, children: children })),
|
|
207
230
|
em: ({ children }) => ((0, jsx_runtime_1.jsx)("em", { style: { color: colors.text }, children: children })),
|
|
208
|
-
ul: ({ children }) => ((0, jsx_runtime_1.jsx)("ul", { style: {
|
|
209
|
-
|
|
231
|
+
ul: ({ children }) => ((0, jsx_runtime_1.jsx)("ul", { style: {
|
|
232
|
+
margin: '4px 0 8px',
|
|
233
|
+
paddingLeft: 20,
|
|
234
|
+
fontSize: 14,
|
|
235
|
+
color: colors.text,
|
|
236
|
+
}, children: children })),
|
|
237
|
+
ol: ({ children }) => ((0, jsx_runtime_1.jsx)("ol", { style: {
|
|
238
|
+
margin: '4px 0 8px',
|
|
239
|
+
paddingLeft: 20,
|
|
240
|
+
fontSize: 14,
|
|
241
|
+
color: colors.text,
|
|
242
|
+
}, children: children })),
|
|
210
243
|
li: ({ children }) => ((0, jsx_runtime_1.jsx)("li", { style: { marginBottom: 2, lineHeight: 1.6 }, children: children })),
|
|
211
244
|
code: ({ children, className }) => {
|
|
212
245
|
const isBlock = className === null || className === void 0 ? void 0 : className.startsWith('language-');
|
|
213
246
|
if (isBlock) {
|
|
214
|
-
return ((0, jsx_runtime_1.jsx)("pre", { style: {
|
|
247
|
+
return ((0, jsx_runtime_1.jsx)("pre", { style: {
|
|
248
|
+
background: colors.codeBg,
|
|
249
|
+
padding: 10,
|
|
250
|
+
borderRadius: 4,
|
|
251
|
+
overflow: 'auto',
|
|
252
|
+
margin: '4px 0 8px',
|
|
253
|
+
fontSize: 12,
|
|
254
|
+
}, children: (0, jsx_runtime_1.jsx)("code", { style: { color: colors.text }, children: children }) }));
|
|
215
255
|
}
|
|
216
|
-
return ((0, jsx_runtime_1.jsx)("code", { style: {
|
|
256
|
+
return ((0, jsx_runtime_1.jsx)("code", { style: {
|
|
257
|
+
background: colors.codeBg,
|
|
258
|
+
padding: '1px 4px',
|
|
259
|
+
borderRadius: 3,
|
|
260
|
+
fontSize: 13,
|
|
261
|
+
color: colors.text,
|
|
262
|
+
}, children: children }));
|
|
217
263
|
},
|
|
218
264
|
pre: ({ children }) => (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children }),
|
|
219
|
-
h1: ({ children }) => (0, jsx_runtime_1.jsx)("h3", { style: {
|
|
220
|
-
|
|
221
|
-
|
|
265
|
+
h1: ({ children }) => ((0, jsx_runtime_1.jsx)("h3", { style: {
|
|
266
|
+
margin: '8px 0 4px',
|
|
267
|
+
fontSize: 16,
|
|
268
|
+
fontWeight: 600,
|
|
269
|
+
color: colors.text,
|
|
270
|
+
}, children: children })),
|
|
271
|
+
h2: ({ children }) => ((0, jsx_runtime_1.jsx)("h4", { style: {
|
|
272
|
+
margin: '8px 0 4px',
|
|
273
|
+
fontSize: 15,
|
|
274
|
+
fontWeight: 600,
|
|
275
|
+
color: colors.text,
|
|
276
|
+
}, children: children })),
|
|
277
|
+
h3: ({ children }) => ((0, jsx_runtime_1.jsx)("h5", { style: {
|
|
278
|
+
margin: '8px 0 4px',
|
|
279
|
+
fontSize: 14,
|
|
280
|
+
fontWeight: 600,
|
|
281
|
+
color: colors.text,
|
|
282
|
+
}, children: children })),
|
|
222
283
|
a: ({ href, children }) => ((0, jsx_runtime_1.jsx)("a", { href: href, target: "_blank", rel: "noopener noreferrer", style: { color: colors.textMuted, textDecoration: 'underline' }, children: children })),
|
|
223
284
|
}), [colors]);
|
|
224
285
|
return (0, jsx_runtime_1.jsx)(react_markdown_1.default, { components: components, children: text });
|
|
@@ -239,11 +300,16 @@ const UserMessage = () => {
|
|
|
239
300
|
borderRadius: 12,
|
|
240
301
|
backgroundColor: colors.userBubble,
|
|
241
302
|
}, children: (0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.Content, { components: {
|
|
242
|
-
Text: ({ text }) => ((0, jsx_runtime_1.jsx)("span", { style: {
|
|
303
|
+
Text: ({ text }) => ((0, jsx_runtime_1.jsx)("span", { style: {
|
|
304
|
+
fontSize: 14,
|
|
305
|
+
whiteSpace: 'pre-wrap',
|
|
306
|
+
color: colors.text,
|
|
307
|
+
}, children: text })),
|
|
243
308
|
} }) })] }) }));
|
|
244
309
|
};
|
|
245
310
|
const AssistantMessage = () => {
|
|
246
311
|
const colors = (0, react_1.useContext)(ColorsContext);
|
|
312
|
+
const toolComponents = (0, react_1.useContext)(ToolComponentsContext);
|
|
247
313
|
return ((0, jsx_runtime_1.jsx)("div", { style: {
|
|
248
314
|
display: 'flex',
|
|
249
315
|
justifyContent: 'flex-start',
|
|
@@ -253,8 +319,9 @@ const AssistantMessage = () => {
|
|
|
253
319
|
borderRadius: 12,
|
|
254
320
|
backgroundColor: colors.assistantBubble,
|
|
255
321
|
}, children: [(0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.Content, { components: {
|
|
256
|
-
Text: ({ text }) => (
|
|
322
|
+
Text: ({ text }) => (0, jsx_runtime_1.jsx)(MarkdownText, { text: text, colors: colors }),
|
|
257
323
|
tools: {
|
|
324
|
+
by_name: toolComponents,
|
|
258
325
|
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 })),
|
|
259
326
|
},
|
|
260
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: {
|
|
@@ -268,7 +335,9 @@ const AssistantMessage = () => {
|
|
|
268
335
|
};
|
|
269
336
|
const PikkuComposer = ({ disabled, }) => {
|
|
270
337
|
const colors = (0, react_1.useContext)(ColorsContext);
|
|
271
|
-
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
|
|
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
|
|
339
|
+
? { 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: {
|
|
272
341
|
flex: 1,
|
|
273
342
|
border: 'none',
|
|
274
343
|
outline: 'none',
|
|
@@ -295,42 +364,47 @@ const PikkuComposer = ({ disabled, }) => {
|
|
|
295
364
|
}, children: "\u25B6" })] }) }) }));
|
|
296
365
|
};
|
|
297
366
|
function PikkuAgentChat(props) {
|
|
298
|
-
const { emptyMessage, hideToolCalls, dark } = props, runtimeOptions = __rest(props, ["emptyMessage", "hideToolCalls", "dark"]);
|
|
367
|
+
const { emptyMessage, hideToolCalls, dark, maxWidth = 768, toolComponents } = props, runtimeOptions = __rest(props, ["emptyMessage", "hideToolCalls", "dark", "maxWidth", "toolComponents"]);
|
|
299
368
|
const { runtime, isAwaitingApproval, pendingApprovals, handleApproval } = (0, use_pikku_agent_runtime_js_1.usePikkuAgentRuntime)(runtimeOptions);
|
|
300
369
|
const colors = dark ? darkColors : lightColors;
|
|
301
|
-
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)(react_2.AssistantRuntimeProvider, { runtime: runtime, children: (0, jsx_runtime_1.jsx)("div", { style: {
|
|
302
|
-
|
|
303
|
-
display: 'flex',
|
|
304
|
-
flexDirection: 'column',
|
|
305
|
-
background: colors.bg,
|
|
306
|
-
}, children: (0, jsx_runtime_1.jsxs)(react_2.ThreadPrimitive.Root, { style: {
|
|
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%',
|
|
307
372
|
display: 'flex',
|
|
308
373
|
flexDirection: 'column',
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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,
|
|
388
|
+
display: 'flex',
|
|
389
|
+
flexDirection: 'column',
|
|
390
|
+
gap: 16,
|
|
391
|
+
}, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Empty, { children: (0, jsx_runtime_1.jsx)("div", { style: {
|
|
392
|
+
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,
|
|
317
406
|
margin: '0 auto',
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
gap: 16,
|
|
322
|
-
}, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Empty, { children: (0, jsx_runtime_1.jsx)("div", { style: {
|
|
323
|
-
display: 'flex',
|
|
324
|
-
alignItems: 'center',
|
|
325
|
-
justifyContent: 'center',
|
|
326
|
-
minHeight: 300,
|
|
327
|
-
color: colors.textMuted,
|
|
328
|
-
textAlign: 'center',
|
|
329
|
-
fontSize: 14,
|
|
330
|
-
}, children: emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : (props.threadId
|
|
331
|
-
? 'Send a message to start the conversation.'
|
|
332
|
-
: 'Start a new conversation.') }) }), (0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Messages, { components: {
|
|
333
|
-
UserMessage,
|
|
334
|
-
AssistantMessage,
|
|
335
|
-
} })] }) }), (0, jsx_runtime_1.jsx)("div", { style: { maxWidth: 768, margin: '0 auto', width: '100%', padding: '0 16px' }, children: (0, jsx_runtime_1.jsx)(PikkuComposer, { disabled: isAwaitingApproval }) })] }) }) }) }) }) }));
|
|
407
|
+
width: '100%',
|
|
408
|
+
padding: '0 16px',
|
|
409
|
+
}, children: (0, jsx_runtime_1.jsx)(PikkuComposer, { disabled: isAwaitingApproval }) })] }) }) }) }) }) }) }));
|
|
336
410
|
}
|
|
@@ -17,6 +17,10 @@ export interface PendingApproval {
|
|
|
17
17
|
args: unknown;
|
|
18
18
|
reason?: string;
|
|
19
19
|
runId: string;
|
|
20
|
+
type?: 'approval-request' | 'credential-request';
|
|
21
|
+
credentialName?: string;
|
|
22
|
+
credentialType?: 'oauth2' | 'apikey';
|
|
23
|
+
connectUrl?: string;
|
|
20
24
|
}
|
|
21
25
|
export interface PikkuApprovalContextValue {
|
|
22
26
|
pendingApprovals: PendingApproval[];
|
|
@@ -25,9 +29,18 @@ export interface PikkuApprovalContextValue {
|
|
|
25
29
|
export declare const PikkuApprovalContext: import("react").Context<PikkuApprovalContextValue>;
|
|
26
30
|
export declare const usePikkuApproval: () => PikkuApprovalContextValue;
|
|
27
31
|
export declare function isDeniedResult(result: unknown): boolean;
|
|
28
|
-
export type PikkuToolStatusType = 'running' | 'requires-action' | 'completed' | 'denied' | 'error';
|
|
32
|
+
export type PikkuToolStatusType = 'running' | 'requires-action' | 'completed' | 'denied' | 'error' | 'missing-credential';
|
|
33
|
+
export type MissingCredentialPayload = {
|
|
34
|
+
error: 'missing_credential';
|
|
35
|
+
credentialName: string;
|
|
36
|
+
credentialType: 'oauth2' | 'apikey';
|
|
37
|
+
connectUrl?: string;
|
|
38
|
+
};
|
|
29
39
|
export type PikkuToolStatus = {
|
|
30
|
-
type: PikkuToolStatusType
|
|
40
|
+
type: Exclude<PikkuToolStatusType, 'missing-credential'>;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'missing-credential';
|
|
43
|
+
payload: MissingCredentialPayload;
|
|
31
44
|
};
|
|
32
45
|
export declare function resolvePikkuToolStatus(status: {
|
|
33
46
|
type: string;
|
|
@@ -129,6 +129,19 @@ function processStream(reader, text, toolCalls, yieldContent, onFinish) {
|
|
|
129
129
|
args: event.args,
|
|
130
130
|
reason: event.reason,
|
|
131
131
|
runId: event.runId,
|
|
132
|
+
type: 'approval-request',
|
|
133
|
+
});
|
|
134
|
+
break;
|
|
135
|
+
case 'credential-request':
|
|
136
|
+
pendingApprovals.push({
|
|
137
|
+
toolCallId: event.toolCallId,
|
|
138
|
+
toolName: event.toolName,
|
|
139
|
+
args: event.args,
|
|
140
|
+
runId: event.runId,
|
|
141
|
+
type: 'credential-request',
|
|
142
|
+
credentialName: event.credentialName,
|
|
143
|
+
credentialType: event.credentialType,
|
|
144
|
+
connectUrl: event.connectUrl,
|
|
132
145
|
});
|
|
133
146
|
break;
|
|
134
147
|
case 'error':
|
|
@@ -162,6 +175,22 @@ function isDeniedResult(result) {
|
|
|
162
175
|
return false;
|
|
163
176
|
}
|
|
164
177
|
}
|
|
178
|
+
function isMissingCredentialResult(result) {
|
|
179
|
+
if (typeof result === 'object' &&
|
|
180
|
+
result &&
|
|
181
|
+
result.error === 'missing_credential') {
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
if (typeof result === 'string') {
|
|
185
|
+
try {
|
|
186
|
+
const parsed = JSON.parse(result);
|
|
187
|
+
if ((parsed === null || parsed === void 0 ? void 0 : parsed.error) === 'missing_credential')
|
|
188
|
+
return parsed;
|
|
189
|
+
}
|
|
190
|
+
catch (_a) { }
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
165
194
|
function resolvePikkuToolStatus(status, result) {
|
|
166
195
|
if (status.type === 'running')
|
|
167
196
|
return { type: 'running' };
|
|
@@ -169,6 +198,9 @@ function resolvePikkuToolStatus(status, result) {
|
|
|
169
198
|
return { type: 'requires-action' };
|
|
170
199
|
if (isDeniedResult(result))
|
|
171
200
|
return { type: 'denied' };
|
|
201
|
+
const missingCred = isMissingCredentialResult(result);
|
|
202
|
+
if (missingCred)
|
|
203
|
+
return { type: 'missing-credential', payload: missingCred };
|
|
172
204
|
if (typeof result === 'string' && result.startsWith('Error:'))
|
|
173
205
|
return { type: 'error' };
|
|
174
206
|
return { type: 'completed' };
|
|
@@ -315,8 +347,11 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
|
|
|
315
347
|
catch (e) {
|
|
316
348
|
const msg = (e === null || e === void 0 ? void 0 : e.message) || 'Unknown error';
|
|
317
349
|
let errorText = 'Failed to connect to the agent.';
|
|
318
|
-
if (msg.includes('Failed to fetch') ||
|
|
319
|
-
|
|
350
|
+
if (msg.includes('Failed to fetch') ||
|
|
351
|
+
msg.includes('NetworkError') ||
|
|
352
|
+
msg.includes('CORS')) {
|
|
353
|
+
errorText =
|
|
354
|
+
'Unable to reach the agent server. The deployment may be down or the URL may be incorrect.';
|
|
320
355
|
}
|
|
321
356
|
else if (msg.includes('abort')) {
|
|
322
357
|
return yield __await(void 0);
|
|
@@ -330,10 +365,12 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
|
|
|
330
365
|
errorText = 'Authentication error.';
|
|
331
366
|
}
|
|
332
367
|
else if (response.status === 404) {
|
|
333
|
-
errorText =
|
|
368
|
+
errorText =
|
|
369
|
+
'Agent not found — the agent may not be configured for this project.';
|
|
334
370
|
}
|
|
335
371
|
else if (response.status === 502 || response.status === 503) {
|
|
336
|
-
errorText =
|
|
372
|
+
errorText =
|
|
373
|
+
'The agent server is currently unavailable. Try again in a moment.';
|
|
337
374
|
}
|
|
338
375
|
else if (response.status === 429) {
|
|
339
376
|
errorText = 'Rate limited — too many requests. Please wait a moment.';
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { usePikkuAgentRuntime, usePikkuAgentNonStreamingRuntime, PikkuApprovalContext, usePikkuApproval, convertDbMessages, isDeniedResult, resolvePikkuToolStatus, } from './use-pikku-agent-runtime.js';
|
|
2
|
-
export type { PikkuAgentRuntimeOptions, PendingApproval, PikkuApprovalContextValue, PikkuToolStatusType, PikkuToolStatus, } from './use-pikku-agent-runtime.js';
|
|
2
|
+
export type { PikkuAgentRuntimeOptions, PendingApproval, PikkuApprovalContextValue, PikkuToolStatusType, PikkuToolStatus, MissingCredentialPayload, } from './use-pikku-agent-runtime.js';
|
|
3
3
|
export { PikkuAgentChat } from './pikku-agent-chat.js';
|
|
4
4
|
export type { PikkuAgentChatProps } from './pikku-agent-chat.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ToolCallMessagePartComponent } from '@assistant-ui/react';
|
|
1
2
|
import { type PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
|
|
2
3
|
export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
|
|
3
4
|
emptyMessage?: string;
|
|
@@ -7,5 +8,18 @@ export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
|
|
|
7
8
|
*/
|
|
8
9
|
hideToolCalls?: boolean | string[];
|
|
9
10
|
dark?: boolean;
|
|
11
|
+
/** Max width of the chat content area. Defaults to 768. Set to 'none' for full width. */
|
|
12
|
+
maxWidth?: number | 'none';
|
|
13
|
+
/**
|
|
14
|
+
* Per-tool renderers. Map `toolName` → React component to replace the
|
|
15
|
+
* default expandable tool-call box for that tool. Enables generative-UI
|
|
16
|
+
* patterns: e.g. register a `renderWidget` tool on the agent and mount
|
|
17
|
+
* real UI (charts, diffs, cards) inline in the assistant bubble from the
|
|
18
|
+
* persisted tool-call args.
|
|
19
|
+
*
|
|
20
|
+
* Any tool without an entry here falls through to the default renderer
|
|
21
|
+
* (which still respects `hideToolCalls` and the approval-request UI).
|
|
22
|
+
*/
|
|
23
|
+
toolComponents?: Record<string, ToolCallMessagePartComponent>;
|
|
10
24
|
}
|
|
11
25
|
export declare function PikkuAgentChat(props: PikkuAgentChatProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext, useState, useMemo } from 'react';
|
|
2
|
+
import { createContext, useContext, useState, useMemo, } from 'react';
|
|
3
3
|
import Markdown from 'react-markdown';
|
|
4
4
|
import { AssistantRuntimeProvider, ThreadPrimitive, MessagePrimitive, ComposerPrimitive, } from '@assistant-ui/react';
|
|
5
5
|
import { usePikkuAgentRuntime, PikkuApprovalContext, usePikkuApproval, resolvePikkuToolStatus, } from './use-pikku-agent-runtime.js';
|
|
@@ -20,6 +20,8 @@ const lightColors = {
|
|
|
20
20
|
successColor: '#2e7d32',
|
|
21
21
|
errorBg: '#ffebee',
|
|
22
22
|
errorColor: '#c62828',
|
|
23
|
+
warningBg: '#fff3e0',
|
|
24
|
+
warningColor: '#e65100',
|
|
23
25
|
};
|
|
24
26
|
const darkColors = {
|
|
25
27
|
bg: 'transparent',
|
|
@@ -38,9 +40,12 @@ const darkColors = {
|
|
|
38
40
|
successColor: '#00e68a',
|
|
39
41
|
errorBg: 'rgba(220, 38, 38, 0.1)',
|
|
40
42
|
errorColor: '#f87171',
|
|
43
|
+
warningBg: 'rgba(245, 158, 11, 0.1)',
|
|
44
|
+
warningColor: '#fbbf24',
|
|
41
45
|
};
|
|
42
46
|
const ColorsContext = createContext(lightColors);
|
|
43
47
|
const HideToolCallsContext = createContext(undefined);
|
|
48
|
+
const ToolComponentsContext = createContext(undefined);
|
|
44
49
|
function shouldHideToolCall(hideToolCalls, toolName) {
|
|
45
50
|
if (!hideToolCalls)
|
|
46
51
|
return false;
|
|
@@ -131,7 +136,9 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
131
136
|
padding: '2px 6px',
|
|
132
137
|
borderRadius: 3,
|
|
133
138
|
background: responded === 'approved' ? colors.successBg : colors.errorBg,
|
|
134
|
-
color: responded === 'approved'
|
|
139
|
+
color: responded === 'approved'
|
|
140
|
+
? colors.successColor
|
|
141
|
+
: colors.errorColor,
|
|
135
142
|
}, children: responded })] }));
|
|
136
143
|
}
|
|
137
144
|
return (_jsxs("div", { style: {
|
|
@@ -156,7 +163,13 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
156
163
|
borderRadius: 3,
|
|
157
164
|
background: colors.errorBg,
|
|
158
165
|
color: colors.errorColor,
|
|
159
|
-
}, children: "error" })), status.type === '
|
|
166
|
+
}, children: "error" })), status.type === 'missing-credential' && (_jsx("span", { style: {
|
|
167
|
+
fontSize: 11,
|
|
168
|
+
padding: '1px 5px',
|
|
169
|
+
borderRadius: 3,
|
|
170
|
+
background: colors.warningBg,
|
|
171
|
+
color: colors.warningColor,
|
|
172
|
+
}, children: "credential required" })), status.type === 'denied' && (_jsx("span", { style: {
|
|
160
173
|
fontSize: 11,
|
|
161
174
|
padding: '1px 5px',
|
|
162
175
|
borderRadius: 3,
|
|
@@ -175,7 +188,12 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
175
188
|
borderRadius: 4,
|
|
176
189
|
overflow: 'auto',
|
|
177
190
|
color: colors.text,
|
|
178
|
-
}, children: JSON.stringify(displayArgs, null, 2) }), result !== undefined && (_jsxs(_Fragment, { children: [_jsx("div", { style: {
|
|
191
|
+
}, children: JSON.stringify(displayArgs, null, 2) }), result !== undefined && (_jsxs(_Fragment, { children: [_jsx("div", { style: {
|
|
192
|
+
fontSize: 12,
|
|
193
|
+
color: colors.textMuted,
|
|
194
|
+
marginTop: 8,
|
|
195
|
+
marginBottom: 2,
|
|
196
|
+
}, children: "Result:" }), _jsx("pre", { style: {
|
|
179
197
|
fontSize: 11,
|
|
180
198
|
background: colors.codeBg,
|
|
181
199
|
padding: 8,
|
|
@@ -186,25 +204,68 @@ const ToolCallDisplay = ({ toolCallId, toolName, args, result, status, addResult
|
|
|
186
204
|
? result
|
|
187
205
|
: JSON.stringify(result, null, 2) })] }))] }))] }));
|
|
188
206
|
};
|
|
189
|
-
const MarkdownText = ({ text, colors }) => {
|
|
207
|
+
const MarkdownText = ({ text, colors, }) => {
|
|
190
208
|
const components = useMemo(() => ({
|
|
191
|
-
p: ({ children }) => (_jsx("p", { style: {
|
|
209
|
+
p: ({ children }) => (_jsx("p", { style: {
|
|
210
|
+
margin: '0 0 8px',
|
|
211
|
+
fontSize: 14,
|
|
212
|
+
lineHeight: 1.6,
|
|
213
|
+
color: colors.text,
|
|
214
|
+
}, children: children })),
|
|
192
215
|
strong: ({ children }) => (_jsx("strong", { style: { fontWeight: 600, color: colors.text }, children: children })),
|
|
193
216
|
em: ({ children }) => (_jsx("em", { style: { color: colors.text }, children: children })),
|
|
194
|
-
ul: ({ children }) => (_jsx("ul", { style: {
|
|
195
|
-
|
|
217
|
+
ul: ({ children }) => (_jsx("ul", { style: {
|
|
218
|
+
margin: '4px 0 8px',
|
|
219
|
+
paddingLeft: 20,
|
|
220
|
+
fontSize: 14,
|
|
221
|
+
color: colors.text,
|
|
222
|
+
}, children: children })),
|
|
223
|
+
ol: ({ children }) => (_jsx("ol", { style: {
|
|
224
|
+
margin: '4px 0 8px',
|
|
225
|
+
paddingLeft: 20,
|
|
226
|
+
fontSize: 14,
|
|
227
|
+
color: colors.text,
|
|
228
|
+
}, children: children })),
|
|
196
229
|
li: ({ children }) => (_jsx("li", { style: { marginBottom: 2, lineHeight: 1.6 }, children: children })),
|
|
197
230
|
code: ({ children, className }) => {
|
|
198
231
|
const isBlock = className?.startsWith('language-');
|
|
199
232
|
if (isBlock) {
|
|
200
|
-
return (_jsx("pre", { style: {
|
|
233
|
+
return (_jsx("pre", { style: {
|
|
234
|
+
background: colors.codeBg,
|
|
235
|
+
padding: 10,
|
|
236
|
+
borderRadius: 4,
|
|
237
|
+
overflow: 'auto',
|
|
238
|
+
margin: '4px 0 8px',
|
|
239
|
+
fontSize: 12,
|
|
240
|
+
}, children: _jsx("code", { style: { color: colors.text }, children: children }) }));
|
|
201
241
|
}
|
|
202
|
-
return (_jsx("code", { style: {
|
|
242
|
+
return (_jsx("code", { style: {
|
|
243
|
+
background: colors.codeBg,
|
|
244
|
+
padding: '1px 4px',
|
|
245
|
+
borderRadius: 3,
|
|
246
|
+
fontSize: 13,
|
|
247
|
+
color: colors.text,
|
|
248
|
+
}, children: children }));
|
|
203
249
|
},
|
|
204
250
|
pre: ({ children }) => _jsx(_Fragment, { children: children }),
|
|
205
|
-
h1: ({ children }) => _jsx("h3", { style: {
|
|
206
|
-
|
|
207
|
-
|
|
251
|
+
h1: ({ children }) => (_jsx("h3", { style: {
|
|
252
|
+
margin: '8px 0 4px',
|
|
253
|
+
fontSize: 16,
|
|
254
|
+
fontWeight: 600,
|
|
255
|
+
color: colors.text,
|
|
256
|
+
}, children: children })),
|
|
257
|
+
h2: ({ children }) => (_jsx("h4", { style: {
|
|
258
|
+
margin: '8px 0 4px',
|
|
259
|
+
fontSize: 15,
|
|
260
|
+
fontWeight: 600,
|
|
261
|
+
color: colors.text,
|
|
262
|
+
}, children: children })),
|
|
263
|
+
h3: ({ children }) => (_jsx("h5", { style: {
|
|
264
|
+
margin: '8px 0 4px',
|
|
265
|
+
fontSize: 14,
|
|
266
|
+
fontWeight: 600,
|
|
267
|
+
color: colors.text,
|
|
268
|
+
}, children: children })),
|
|
208
269
|
a: ({ href, children }) => (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", style: { color: colors.textMuted, textDecoration: 'underline' }, children: children })),
|
|
209
270
|
}), [colors]);
|
|
210
271
|
return _jsx(Markdown, { components: components, children: text });
|
|
@@ -225,11 +286,16 @@ const UserMessage = () => {
|
|
|
225
286
|
borderRadius: 12,
|
|
226
287
|
backgroundColor: colors.userBubble,
|
|
227
288
|
}, children: _jsx(MessagePrimitive.Content, { components: {
|
|
228
|
-
Text: ({ text }) => (_jsx("span", { style: {
|
|
289
|
+
Text: ({ text }) => (_jsx("span", { style: {
|
|
290
|
+
fontSize: 14,
|
|
291
|
+
whiteSpace: 'pre-wrap',
|
|
292
|
+
color: colors.text,
|
|
293
|
+
}, children: text })),
|
|
229
294
|
} }) })] }) }));
|
|
230
295
|
};
|
|
231
296
|
const AssistantMessage = () => {
|
|
232
297
|
const colors = useContext(ColorsContext);
|
|
298
|
+
const toolComponents = useContext(ToolComponentsContext);
|
|
233
299
|
return (_jsx("div", { style: {
|
|
234
300
|
display: 'flex',
|
|
235
301
|
justifyContent: 'flex-start',
|
|
@@ -239,8 +305,9 @@ const AssistantMessage = () => {
|
|
|
239
305
|
borderRadius: 12,
|
|
240
306
|
backgroundColor: colors.assistantBubble,
|
|
241
307
|
}, children: [_jsx(MessagePrimitive.Content, { components: {
|
|
242
|
-
Text: ({ text }) =>
|
|
308
|
+
Text: ({ text }) => _jsx(MarkdownText, { text: text, colors: colors }),
|
|
243
309
|
tools: {
|
|
310
|
+
by_name: toolComponents,
|
|
244
311
|
Fallback: (props) => (_jsx(ToolCallDisplay, { toolCallId: props.toolCallId, toolName: props.toolName, args: props.args, result: props.result, status: resolvePikkuToolStatus(props.status, props.result), addResult: props.addResult })),
|
|
245
312
|
},
|
|
246
313
|
} }), _jsx(MessagePrimitive.If, { last: true, children: _jsx(ThreadPrimitive.If, { running: true, children: _jsx("div", { style: {
|
|
@@ -262,7 +329,9 @@ const PikkuComposer = ({ disabled, }) => {
|
|
|
262
329
|
alignItems: 'flex-end',
|
|
263
330
|
padding: '6px 12px',
|
|
264
331
|
gap: 8,
|
|
265
|
-
...(disabled
|
|
332
|
+
...(disabled
|
|
333
|
+
? { opacity: 0.5, pointerEvents: 'none' }
|
|
334
|
+
: {}),
|
|
266
335
|
}, children: [_jsx(ComposerPrimitive.Input, { placeholder: disabled ? 'Respond to approval request above...' : 'Message...', rows: 2, disabled: disabled, style: {
|
|
267
336
|
flex: 1,
|
|
268
337
|
border: 'none',
|
|
@@ -290,43 +359,48 @@ const PikkuComposer = ({ disabled, }) => {
|
|
|
290
359
|
}, children: "\u25B6" })] }) }) }));
|
|
291
360
|
};
|
|
292
361
|
export function PikkuAgentChat(props) {
|
|
293
|
-
const { emptyMessage, hideToolCalls, dark, ...runtimeOptions } = props;
|
|
362
|
+
const { emptyMessage, hideToolCalls, dark, maxWidth = 768, toolComponents, ...runtimeOptions } = props;
|
|
294
363
|
const { runtime, isAwaitingApproval, pendingApprovals, handleApproval } = usePikkuAgentRuntime(runtimeOptions);
|
|
295
364
|
const colors = dark ? darkColors : lightColors;
|
|
296
|
-
return (_jsx(ColorsContext.Provider, { value: colors, children: _jsx(PikkuApprovalContext.Provider, { value: { pendingApprovals, handleApproval }, children: _jsx(HideToolCallsContext.Provider, { value: hideToolCalls, children: _jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx("div", { style: {
|
|
297
|
-
|
|
298
|
-
display: 'flex',
|
|
299
|
-
flexDirection: 'column',
|
|
300
|
-
background: colors.bg,
|
|
301
|
-
}, children: _jsxs(ThreadPrimitive.Root, { style: {
|
|
365
|
+
return (_jsx(ColorsContext.Provider, { value: colors, children: _jsx(PikkuApprovalContext.Provider, { value: { pendingApprovals, handleApproval }, children: _jsx(HideToolCallsContext.Provider, { value: hideToolCalls, children: _jsx(ToolComponentsContext.Provider, { value: toolComponents, children: _jsx(AssistantRuntimeProvider, { runtime: runtime, children: _jsx("div", { style: {
|
|
366
|
+
height: '100%',
|
|
302
367
|
display: 'flex',
|
|
303
368
|
flexDirection: 'column',
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
369
|
+
background: colors.bg,
|
|
370
|
+
}, children: _jsxs(ThreadPrimitive.Root, { style: {
|
|
371
|
+
display: 'flex',
|
|
372
|
+
flexDirection: 'column',
|
|
373
|
+
flex: 1,
|
|
374
|
+
minHeight: 0,
|
|
375
|
+
}, children: [_jsx(ThreadPrimitive.Viewport, { style: {
|
|
376
|
+
flex: 1,
|
|
377
|
+
minHeight: 0,
|
|
378
|
+
overflowY: 'auto',
|
|
379
|
+
}, children: _jsxs("div", { style: {
|
|
380
|
+
maxWidth: maxWidth === 'none' ? undefined : maxWidth,
|
|
381
|
+
margin: '0 auto',
|
|
382
|
+
padding: 16,
|
|
383
|
+
display: 'flex',
|
|
384
|
+
flexDirection: 'column',
|
|
385
|
+
gap: 16,
|
|
386
|
+
}, children: [_jsx(ThreadPrimitive.Empty, { children: _jsx("div", { style: {
|
|
387
|
+
display: 'flex',
|
|
388
|
+
alignItems: 'center',
|
|
389
|
+
justifyContent: 'center',
|
|
390
|
+
minHeight: 300,
|
|
391
|
+
color: colors.textMuted,
|
|
392
|
+
textAlign: 'center',
|
|
393
|
+
fontSize: 14,
|
|
394
|
+
}, children: emptyMessage ??
|
|
395
|
+
(props.threadId
|
|
396
|
+
? 'Send a message to start the conversation.'
|
|
397
|
+
: 'Start a new conversation.') }) }), _jsx(ThreadPrimitive.Messages, { components: {
|
|
398
|
+
UserMessage,
|
|
399
|
+
AssistantMessage,
|
|
400
|
+
} })] }) }), _jsx("div", { style: {
|
|
401
|
+
maxWidth: maxWidth === 'none' ? undefined : maxWidth,
|
|
312
402
|
margin: '0 auto',
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
gap: 16,
|
|
317
|
-
}, children: [_jsx(ThreadPrimitive.Empty, { children: _jsx("div", { style: {
|
|
318
|
-
display: 'flex',
|
|
319
|
-
alignItems: 'center',
|
|
320
|
-
justifyContent: 'center',
|
|
321
|
-
minHeight: 300,
|
|
322
|
-
color: colors.textMuted,
|
|
323
|
-
textAlign: 'center',
|
|
324
|
-
fontSize: 14,
|
|
325
|
-
}, children: emptyMessage ??
|
|
326
|
-
(props.threadId
|
|
327
|
-
? 'Send a message to start the conversation.'
|
|
328
|
-
: 'Start a new conversation.') }) }), _jsx(ThreadPrimitive.Messages, { components: {
|
|
329
|
-
UserMessage,
|
|
330
|
-
AssistantMessage,
|
|
331
|
-
} })] }) }), _jsx("div", { style: { maxWidth: 768, margin: '0 auto', width: '100%', padding: '0 16px' }, children: _jsx(PikkuComposer, { disabled: isAwaitingApproval }) })] }) }) }) }) }) }));
|
|
403
|
+
width: '100%',
|
|
404
|
+
padding: '0 16px',
|
|
405
|
+
}, children: _jsx(PikkuComposer, { disabled: isAwaitingApproval }) })] }) }) }) }) }) }) }));
|
|
332
406
|
}
|
|
@@ -17,6 +17,10 @@ export interface PendingApproval {
|
|
|
17
17
|
args: unknown;
|
|
18
18
|
reason?: string;
|
|
19
19
|
runId: string;
|
|
20
|
+
type?: 'approval-request' | 'credential-request';
|
|
21
|
+
credentialName?: string;
|
|
22
|
+
credentialType?: 'oauth2' | 'apikey';
|
|
23
|
+
connectUrl?: string;
|
|
20
24
|
}
|
|
21
25
|
export interface PikkuApprovalContextValue {
|
|
22
26
|
pendingApprovals: PendingApproval[];
|
|
@@ -25,9 +29,18 @@ export interface PikkuApprovalContextValue {
|
|
|
25
29
|
export declare const PikkuApprovalContext: import("react").Context<PikkuApprovalContextValue>;
|
|
26
30
|
export declare const usePikkuApproval: () => PikkuApprovalContextValue;
|
|
27
31
|
export declare function isDeniedResult(result: unknown): boolean;
|
|
28
|
-
export type PikkuToolStatusType = 'running' | 'requires-action' | 'completed' | 'denied' | 'error';
|
|
32
|
+
export type PikkuToolStatusType = 'running' | 'requires-action' | 'completed' | 'denied' | 'error' | 'missing-credential';
|
|
33
|
+
export type MissingCredentialPayload = {
|
|
34
|
+
error: 'missing_credential';
|
|
35
|
+
credentialName: string;
|
|
36
|
+
credentialType: 'oauth2' | 'apikey';
|
|
37
|
+
connectUrl?: string;
|
|
38
|
+
};
|
|
29
39
|
export type PikkuToolStatus = {
|
|
30
|
-
type: PikkuToolStatusType
|
|
40
|
+
type: Exclude<PikkuToolStatusType, 'missing-credential'>;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'missing-credential';
|
|
43
|
+
payload: MissingCredentialPayload;
|
|
31
44
|
};
|
|
32
45
|
export declare function resolvePikkuToolStatus(status: {
|
|
33
46
|
type: string;
|
|
@@ -84,6 +84,19 @@ async function processStream(reader, text, toolCalls, yieldContent, onFinish) {
|
|
|
84
84
|
args: event.args,
|
|
85
85
|
reason: event.reason,
|
|
86
86
|
runId: event.runId,
|
|
87
|
+
type: 'approval-request',
|
|
88
|
+
});
|
|
89
|
+
break;
|
|
90
|
+
case 'credential-request':
|
|
91
|
+
pendingApprovals.push({
|
|
92
|
+
toolCallId: event.toolCallId,
|
|
93
|
+
toolName: event.toolName,
|
|
94
|
+
args: event.args,
|
|
95
|
+
runId: event.runId,
|
|
96
|
+
type: 'credential-request',
|
|
97
|
+
credentialName: event.credentialName,
|
|
98
|
+
credentialType: event.credentialType,
|
|
99
|
+
connectUrl: event.connectUrl,
|
|
87
100
|
});
|
|
88
101
|
break;
|
|
89
102
|
case 'error':
|
|
@@ -108,6 +121,22 @@ export function isDeniedResult(result) {
|
|
|
108
121
|
return false;
|
|
109
122
|
}
|
|
110
123
|
}
|
|
124
|
+
function isMissingCredentialResult(result) {
|
|
125
|
+
if (typeof result === 'object' &&
|
|
126
|
+
result &&
|
|
127
|
+
result.error === 'missing_credential') {
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
if (typeof result === 'string') {
|
|
131
|
+
try {
|
|
132
|
+
const parsed = JSON.parse(result);
|
|
133
|
+
if (parsed?.error === 'missing_credential')
|
|
134
|
+
return parsed;
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
111
140
|
export function resolvePikkuToolStatus(status, result) {
|
|
112
141
|
if (status.type === 'running')
|
|
113
142
|
return { type: 'running' };
|
|
@@ -115,6 +144,9 @@ export function resolvePikkuToolStatus(status, result) {
|
|
|
115
144
|
return { type: 'requires-action' };
|
|
116
145
|
if (isDeniedResult(result))
|
|
117
146
|
return { type: 'denied' };
|
|
147
|
+
const missingCred = isMissingCredentialResult(result);
|
|
148
|
+
if (missingCred)
|
|
149
|
+
return { type: 'missing-credential', payload: missingCred };
|
|
118
150
|
if (typeof result === 'string' && result.startsWith('Error:'))
|
|
119
151
|
return { type: 'error' };
|
|
120
152
|
return { type: 'completed' };
|
|
@@ -263,8 +295,11 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
|
|
|
263
295
|
catch (e) {
|
|
264
296
|
const msg = e?.message || 'Unknown error';
|
|
265
297
|
let errorText = 'Failed to connect to the agent.';
|
|
266
|
-
if (msg.includes('Failed to fetch') ||
|
|
267
|
-
|
|
298
|
+
if (msg.includes('Failed to fetch') ||
|
|
299
|
+
msg.includes('NetworkError') ||
|
|
300
|
+
msg.includes('CORS')) {
|
|
301
|
+
errorText =
|
|
302
|
+
'Unable to reach the agent server. The deployment may be down or the URL may be incorrect.';
|
|
268
303
|
}
|
|
269
304
|
else if (msg.includes('abort')) {
|
|
270
305
|
return;
|
|
@@ -278,10 +313,12 @@ function createPikkuStreamingAdapter(optionsRef, pendingApprovalsRef, approvalDe
|
|
|
278
313
|
errorText = 'Authentication error.';
|
|
279
314
|
}
|
|
280
315
|
else if (response.status === 404) {
|
|
281
|
-
errorText =
|
|
316
|
+
errorText =
|
|
317
|
+
'Agent not found — the agent may not be configured for this project.';
|
|
282
318
|
}
|
|
283
319
|
else if (response.status === 502 || response.status === 503) {
|
|
284
|
-
errorText =
|
|
320
|
+
errorText =
|
|
321
|
+
'The agent server is currently unavailable. Try again in a moment.';
|
|
285
322
|
}
|
|
286
323
|
else if (response.status === 429) {
|
|
287
324
|
errorText = 'Rate limited — too many requests. Please wait a moment.';
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type {
|
|
|
13
13
|
PikkuApprovalContextValue,
|
|
14
14
|
PikkuToolStatusType,
|
|
15
15
|
PikkuToolStatus,
|
|
16
|
+
MissingCredentialPayload,
|
|
16
17
|
} from './use-pikku-agent-runtime.js'
|
|
17
18
|
export { PikkuAgentChat } from './pikku-agent-chat.js'
|
|
18
19
|
export type { PikkuAgentChatProps } from './pikku-agent-chat.js'
|
package/src/pikku-agent-chat.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
ThreadPrimitive,
|
|
6
6
|
MessagePrimitive,
|
|
7
7
|
ComposerPrimitive,
|
|
8
|
+
type ToolCallMessagePartComponent,
|
|
8
9
|
} from '@assistant-ui/react'
|
|
9
10
|
import {
|
|
10
11
|
usePikkuAgentRuntime,
|
|
@@ -23,6 +24,19 @@ export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
|
|
|
23
24
|
*/
|
|
24
25
|
hideToolCalls?: boolean | string[]
|
|
25
26
|
dark?: boolean
|
|
27
|
+
/** Max width of the chat content area. Defaults to 768. Set to 'none' for full width. */
|
|
28
|
+
maxWidth?: number | 'none'
|
|
29
|
+
/**
|
|
30
|
+
* Per-tool renderers. Map `toolName` → React component to replace the
|
|
31
|
+
* default expandable tool-call box for that tool. Enables generative-UI
|
|
32
|
+
* patterns: e.g. register a `renderWidget` tool on the agent and mount
|
|
33
|
+
* real UI (charts, diffs, cards) inline in the assistant bubble from the
|
|
34
|
+
* persisted tool-call args.
|
|
35
|
+
*
|
|
36
|
+
* Any tool without an entry here falls through to the default renderer
|
|
37
|
+
* (which still respects `hideToolCalls` and the approval-request UI).
|
|
38
|
+
*/
|
|
39
|
+
toolComponents?: Record<string, ToolCallMessagePartComponent>
|
|
26
40
|
}
|
|
27
41
|
|
|
28
42
|
interface ChatColors {
|
|
@@ -42,6 +56,8 @@ interface ChatColors {
|
|
|
42
56
|
successColor: string
|
|
43
57
|
errorBg: string
|
|
44
58
|
errorColor: string
|
|
59
|
+
warningBg: string
|
|
60
|
+
warningColor: string
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
const lightColors: ChatColors = {
|
|
@@ -61,6 +77,8 @@ const lightColors: ChatColors = {
|
|
|
61
77
|
successColor: '#2e7d32',
|
|
62
78
|
errorBg: '#ffebee',
|
|
63
79
|
errorColor: '#c62828',
|
|
80
|
+
warningBg: '#fff3e0',
|
|
81
|
+
warningColor: '#e65100',
|
|
64
82
|
}
|
|
65
83
|
|
|
66
84
|
const darkColors: ChatColors = {
|
|
@@ -80,10 +98,15 @@ const darkColors: ChatColors = {
|
|
|
80
98
|
successColor: '#00e68a',
|
|
81
99
|
errorBg: 'rgba(220, 38, 38, 0.1)',
|
|
82
100
|
errorColor: '#f87171',
|
|
101
|
+
warningBg: 'rgba(245, 158, 11, 0.1)',
|
|
102
|
+
warningColor: '#fbbf24',
|
|
83
103
|
}
|
|
84
104
|
|
|
85
105
|
const ColorsContext = createContext<ChatColors>(lightColors)
|
|
86
106
|
const HideToolCallsContext = createContext<boolean | string[] | undefined>(undefined)
|
|
107
|
+
const ToolComponentsContext = createContext<
|
|
108
|
+
Record<string, ToolCallMessagePartComponent> | undefined
|
|
109
|
+
>(undefined)
|
|
87
110
|
|
|
88
111
|
function shouldHideToolCall(
|
|
89
112
|
hideToolCalls: boolean | string[] | undefined,
|
|
@@ -284,6 +307,19 @@ const ToolCallDisplay: FunctionComponent<{
|
|
|
284
307
|
error
|
|
285
308
|
</span>
|
|
286
309
|
)}
|
|
310
|
+
{status.type === 'missing-credential' && (
|
|
311
|
+
<span
|
|
312
|
+
style={{
|
|
313
|
+
fontSize: 11,
|
|
314
|
+
padding: '1px 5px',
|
|
315
|
+
borderRadius: 3,
|
|
316
|
+
background: colors.warningBg,
|
|
317
|
+
color: colors.warningColor,
|
|
318
|
+
}}
|
|
319
|
+
>
|
|
320
|
+
credential required
|
|
321
|
+
</span>
|
|
322
|
+
)}
|
|
287
323
|
{status.type === 'denied' && (
|
|
288
324
|
<span
|
|
289
325
|
style={{
|
|
@@ -449,6 +485,7 @@ const UserMessage: FunctionComponent = () => {
|
|
|
449
485
|
|
|
450
486
|
const AssistantMessage: FunctionComponent = () => {
|
|
451
487
|
const colors = useContext(ColorsContext)
|
|
488
|
+
const toolComponents = useContext(ToolComponentsContext)
|
|
452
489
|
return (
|
|
453
490
|
<div
|
|
454
491
|
style={{
|
|
@@ -474,6 +511,7 @@ const AssistantMessage: FunctionComponent = () => {
|
|
|
474
511
|
<MarkdownText text={text} colors={colors} />
|
|
475
512
|
),
|
|
476
513
|
tools: {
|
|
514
|
+
by_name: toolComponents,
|
|
477
515
|
Fallback: (props) => (
|
|
478
516
|
<ToolCallDisplay
|
|
479
517
|
toolCallId={props.toolCallId}
|
|
@@ -571,7 +609,7 @@ const PikkuComposer: FunctionComponent<{ disabled?: boolean }> = ({
|
|
|
571
609
|
}
|
|
572
610
|
|
|
573
611
|
export function PikkuAgentChat(props: PikkuAgentChatProps) {
|
|
574
|
-
const { emptyMessage, hideToolCalls, dark, ...runtimeOptions } = props
|
|
612
|
+
const { emptyMessage, hideToolCalls, dark, maxWidth = 768, toolComponents, ...runtimeOptions } = props
|
|
575
613
|
const { runtime, isAwaitingApproval, pendingApprovals, handleApproval } =
|
|
576
614
|
usePikkuAgentRuntime(runtimeOptions)
|
|
577
615
|
|
|
@@ -581,6 +619,7 @@ export function PikkuAgentChat(props: PikkuAgentChatProps) {
|
|
|
581
619
|
<ColorsContext.Provider value={colors}>
|
|
582
620
|
<PikkuApprovalContext.Provider value={{ pendingApprovals, handleApproval }}>
|
|
583
621
|
<HideToolCallsContext.Provider value={hideToolCalls}>
|
|
622
|
+
<ToolComponentsContext.Provider value={toolComponents}>
|
|
584
623
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
585
624
|
<div
|
|
586
625
|
style={{
|
|
@@ -607,7 +646,7 @@ export function PikkuAgentChat(props: PikkuAgentChatProps) {
|
|
|
607
646
|
>
|
|
608
647
|
<div
|
|
609
648
|
style={{
|
|
610
|
-
maxWidth:
|
|
649
|
+
maxWidth: maxWidth === 'none' ? undefined : maxWidth,
|
|
611
650
|
margin: '0 auto',
|
|
612
651
|
padding: 16,
|
|
613
652
|
display: 'flex',
|
|
@@ -641,12 +680,13 @@ export function PikkuAgentChat(props: PikkuAgentChatProps) {
|
|
|
641
680
|
/>
|
|
642
681
|
</div>
|
|
643
682
|
</ThreadPrimitive.Viewport>
|
|
644
|
-
<div style={{ maxWidth:
|
|
683
|
+
<div style={{ maxWidth: maxWidth === 'none' ? undefined : maxWidth, margin: '0 auto', width: '100%', padding: '0 16px' }}>
|
|
645
684
|
<PikkuComposer disabled={isAwaitingApproval} />
|
|
646
685
|
</div>
|
|
647
686
|
</ThreadPrimitive.Root>
|
|
648
687
|
</div>
|
|
649
688
|
</AssistantRuntimeProvider>
|
|
689
|
+
</ToolComponentsContext.Provider>
|
|
650
690
|
</HideToolCallsContext.Provider>
|
|
651
691
|
</PikkuApprovalContext.Provider>
|
|
652
692
|
</ColorsContext.Provider>
|
|
@@ -31,6 +31,10 @@ export interface PendingApproval {
|
|
|
31
31
|
args: unknown
|
|
32
32
|
reason?: string
|
|
33
33
|
runId: string
|
|
34
|
+
type?: 'approval-request' | 'credential-request'
|
|
35
|
+
credentialName?: string
|
|
36
|
+
credentialType?: 'oauth2' | 'apikey'
|
|
37
|
+
connectUrl?: string
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export interface PikkuApprovalContextValue {
|
|
@@ -143,6 +147,19 @@ async function processStream(
|
|
|
143
147
|
args: event.args,
|
|
144
148
|
reason: event.reason,
|
|
145
149
|
runId: event.runId,
|
|
150
|
+
type: 'approval-request',
|
|
151
|
+
})
|
|
152
|
+
break
|
|
153
|
+
case 'credential-request':
|
|
154
|
+
pendingApprovals.push({
|
|
155
|
+
toolCallId: event.toolCallId,
|
|
156
|
+
toolName: event.toolName,
|
|
157
|
+
args: event.args,
|
|
158
|
+
runId: event.runId,
|
|
159
|
+
type: 'credential-request',
|
|
160
|
+
credentialName: event.credentialName,
|
|
161
|
+
credentialType: event.credentialType,
|
|
162
|
+
connectUrl: event.connectUrl,
|
|
146
163
|
})
|
|
147
164
|
break
|
|
148
165
|
case 'error':
|
|
@@ -174,8 +191,37 @@ export type PikkuToolStatusType =
|
|
|
174
191
|
| 'completed'
|
|
175
192
|
| 'denied'
|
|
176
193
|
| 'error'
|
|
194
|
+
| 'missing-credential'
|
|
177
195
|
|
|
178
|
-
export type
|
|
196
|
+
export type MissingCredentialPayload = {
|
|
197
|
+
error: 'missing_credential'
|
|
198
|
+
credentialName: string
|
|
199
|
+
credentialType: 'oauth2' | 'apikey'
|
|
200
|
+
connectUrl?: string
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export type PikkuToolStatus =
|
|
204
|
+
| { type: Exclude<PikkuToolStatusType, 'missing-credential'> }
|
|
205
|
+
| { type: 'missing-credential'; payload: MissingCredentialPayload }
|
|
206
|
+
|
|
207
|
+
function isMissingCredentialResult(
|
|
208
|
+
result: unknown
|
|
209
|
+
): MissingCredentialPayload | null {
|
|
210
|
+
if (
|
|
211
|
+
typeof result === 'object' &&
|
|
212
|
+
result &&
|
|
213
|
+
(result as any).error === 'missing_credential'
|
|
214
|
+
) {
|
|
215
|
+
return result as MissingCredentialPayload
|
|
216
|
+
}
|
|
217
|
+
if (typeof result === 'string') {
|
|
218
|
+
try {
|
|
219
|
+
const parsed = JSON.parse(result)
|
|
220
|
+
if (parsed?.error === 'missing_credential') return parsed
|
|
221
|
+
} catch {}
|
|
222
|
+
}
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
179
225
|
|
|
180
226
|
export function resolvePikkuToolStatus(
|
|
181
227
|
status: { type: string },
|
|
@@ -184,6 +230,8 @@ export function resolvePikkuToolStatus(
|
|
|
184
230
|
if (status.type === 'running') return { type: 'running' }
|
|
185
231
|
if (status.type === 'requires-action') return { type: 'requires-action' }
|
|
186
232
|
if (isDeniedResult(result)) return { type: 'denied' }
|
|
233
|
+
const missingCred = isMissingCredentialResult(result)
|
|
234
|
+
if (missingCred) return { type: 'missing-credential', payload: missingCred }
|
|
187
235
|
if (typeof result === 'string' && result.startsWith('Error:'))
|
|
188
236
|
return { type: 'error' }
|
|
189
237
|
return { type: 'completed' }
|
|
@@ -370,8 +418,13 @@ function createPikkuStreamingAdapter(
|
|
|
370
418
|
} catch (e: any) {
|
|
371
419
|
const msg = e?.message || 'Unknown error'
|
|
372
420
|
let errorText = 'Failed to connect to the agent.'
|
|
373
|
-
if (
|
|
374
|
-
|
|
421
|
+
if (
|
|
422
|
+
msg.includes('Failed to fetch') ||
|
|
423
|
+
msg.includes('NetworkError') ||
|
|
424
|
+
msg.includes('CORS')
|
|
425
|
+
) {
|
|
426
|
+
errorText =
|
|
427
|
+
'Unable to reach the agent server. The deployment may be down or the URL may be incorrect.'
|
|
375
428
|
} else if (msg.includes('abort')) {
|
|
376
429
|
return
|
|
377
430
|
}
|
|
@@ -384,9 +437,11 @@ function createPikkuStreamingAdapter(
|
|
|
384
437
|
if (response.status === 401 || response.status === 403) {
|
|
385
438
|
errorText = 'Authentication error.'
|
|
386
439
|
} else if (response.status === 404) {
|
|
387
|
-
errorText =
|
|
440
|
+
errorText =
|
|
441
|
+
'Agent not found — the agent may not be configured for this project.'
|
|
388
442
|
} else if (response.status === 502 || response.status === 503) {
|
|
389
|
-
errorText =
|
|
443
|
+
errorText =
|
|
444
|
+
'The agent server is currently unavailable. Try again in a moment.'
|
|
390
445
|
} else if (response.status === 429) {
|
|
391
446
|
errorText = 'Rate limited — too many requests. Please wait a moment.'
|
|
392
447
|
}
|