@pikku/assistant-ui 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ export { usePikkuAgentRuntime } from './use-pikku-agent-runtime.js';
2
+ export type { PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
3
+ export { PikkuAgentChat } from './pikku-agent-chat.js';
4
+ export type { PikkuAgentChatProps } from './pikku-agent-chat.js';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PikkuAgentChat = exports.usePikkuAgentRuntime = void 0;
4
+ var use_pikku_agent_runtime_js_1 = require("./use-pikku-agent-runtime.js");
5
+ Object.defineProperty(exports, "usePikkuAgentRuntime", { enumerable: true, get: function () { return use_pikku_agent_runtime_js_1.usePikkuAgentRuntime; } });
6
+ var pikku_agent_chat_js_1 = require("./pikku-agent-chat.js");
7
+ Object.defineProperty(exports, "PikkuAgentChat", { enumerable: true, get: function () { return pikku_agent_chat_js_1.PikkuAgentChat; } });
@@ -0,0 +1 @@
1
+ {"type": "commonjs"}
@@ -0,0 +1,5 @@
1
+ import { type PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
2
+ export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
3
+ emptyMessage?: string;
4
+ }
5
+ export declare function PikkuAgentChat(props: PikkuAgentChatProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.PikkuAgentChat = PikkuAgentChat;
15
+ const jsx_runtime_1 = require("react/jsx-runtime");
16
+ const react_1 = require("react");
17
+ const react_2 = require("@assistant-ui/react");
18
+ const use_pikku_agent_runtime_js_1 = require("./use-pikku-agent-runtime.js");
19
+ const ToolCallDisplay = ({ toolName, args, result, status, addResult }) => {
20
+ const [expanded, setExpanded] = (0, react_1.useState)(false);
21
+ const isApproval = status.type === 'requires-action';
22
+ const approvalReason = args === null || args === void 0 ? void 0 : args.__approvalReason;
23
+ const displayArgs = Object.assign({}, args);
24
+ delete displayArgs.__approvalReason;
25
+ const [responded, setResponded] = (0, react_1.useState)(null);
26
+ if (isApproval && !responded) {
27
+ return ((0, jsx_runtime_1.jsxs)("div", { style: {
28
+ border: '1px solid #e9a211',
29
+ borderRadius: 6,
30
+ padding: 12,
31
+ margin: '4px 0',
32
+ backgroundColor: '#fef9e7',
33
+ }, children: [(0, jsx_runtime_1.jsx)("div", { style: {
34
+ display: 'flex',
35
+ alignItems: 'center',
36
+ gap: 6,
37
+ marginBottom: 8,
38
+ fontWeight: 600,
39
+ fontSize: 13,
40
+ }, children: "Approval required" }), approvalReason && ((0, jsx_runtime_1.jsx)("div", { style: { fontSize: 13, marginBottom: 4 }, children: approvalReason })), (0, jsx_runtime_1.jsxs)("div", { style: { fontSize: 12, color: '#666', marginBottom: 4 }, children: ["The agent wants to call ", (0, jsx_runtime_1.jsx)("code", { children: toolName })] }), (0, jsx_runtime_1.jsx)("pre", { style: {
41
+ fontSize: 11,
42
+ background: '#f5f5f5',
43
+ padding: 8,
44
+ borderRadius: 4,
45
+ overflow: 'auto',
46
+ marginBottom: 8,
47
+ }, children: JSON.stringify(displayArgs, null, 2) }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: 8 }, children: [(0, jsx_runtime_1.jsx)("button", { onClick: () => {
48
+ setResponded('approved');
49
+ addResult === null || addResult === void 0 ? void 0 : addResult({ approved: true });
50
+ }, style: {
51
+ padding: '4px 12px',
52
+ fontSize: 12,
53
+ border: '1px solid #2e7d32',
54
+ borderRadius: 4,
55
+ background: '#e8f5e9',
56
+ color: '#2e7d32',
57
+ cursor: 'pointer',
58
+ }, children: "Approve" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => {
59
+ setResponded('denied');
60
+ addResult === null || addResult === void 0 ? void 0 : addResult({ approved: false });
61
+ }, style: {
62
+ padding: '4px 12px',
63
+ fontSize: 12,
64
+ border: '1px solid #c62828',
65
+ borderRadius: 4,
66
+ background: '#ffebee',
67
+ color: '#c62828',
68
+ cursor: 'pointer',
69
+ }, children: "Deny" })] })] }));
70
+ }
71
+ if (isApproval && responded) {
72
+ return ((0, jsx_runtime_1.jsxs)("div", { style: {
73
+ border: '1px solid #ddd',
74
+ borderRadius: 6,
75
+ padding: 8,
76
+ margin: '4px 0',
77
+ display: 'flex',
78
+ alignItems: 'center',
79
+ gap: 8,
80
+ fontSize: 13,
81
+ }, children: [(0, jsx_runtime_1.jsx)("span", { style: { fontWeight: 500 }, children: toolName }), (0, jsx_runtime_1.jsx)("span", { style: {
82
+ fontSize: 11,
83
+ padding: '2px 6px',
84
+ borderRadius: 3,
85
+ background: responded === 'approved' ? '#e8f5e9' : '#ffebee',
86
+ color: responded === 'approved' ? '#2e7d32' : '#c62828',
87
+ }, children: responded })] }));
88
+ }
89
+ return ((0, jsx_runtime_1.jsxs)("div", { style: {
90
+ border: '1px solid #ddd',
91
+ borderRadius: 6,
92
+ padding: 8,
93
+ margin: '4px 0',
94
+ }, children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setExpanded((e) => !e), style: {
95
+ background: 'none',
96
+ border: 'none',
97
+ cursor: 'pointer',
98
+ display: 'flex',
99
+ alignItems: 'center',
100
+ gap: 6,
101
+ width: '100%',
102
+ padding: 0,
103
+ fontSize: 13,
104
+ }, children: [(0, jsx_runtime_1.jsx)("span", { children: expanded ? '\u25BC' : '\u25B6' }), (0, jsx_runtime_1.jsx)("span", { style: { fontFamily: 'monospace', fontWeight: 500 }, children: toolName }), status.type === 'running' && ((0, jsx_runtime_1.jsx)("span", { style: { fontSize: 11, color: '#888' }, children: "running..." })), status.type === 'complete' && ((0, jsx_runtime_1.jsx)("span", { style: {
105
+ fontSize: 11,
106
+ padding: '1px 5px',
107
+ borderRadius: 3,
108
+ background: '#e8f5e9',
109
+ color: '#2e7d32',
110
+ }, children: "done" }))] }), expanded && ((0, jsx_runtime_1.jsxs)("div", { style: { marginTop: 8 }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: 12, color: '#888', marginBottom: 2 }, children: "Arguments:" }), (0, jsx_runtime_1.jsx)("pre", { style: {
111
+ fontSize: 11,
112
+ background: '#f5f5f5',
113
+ padding: 8,
114
+ borderRadius: 4,
115
+ overflow: 'auto',
116
+ }, 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: { fontSize: 12, color: '#888', marginTop: 8, marginBottom: 2 }, children: "Result:" }), (0, jsx_runtime_1.jsx)("pre", { style: {
117
+ fontSize: 11,
118
+ background: '#f5f5f5',
119
+ padding: 8,
120
+ borderRadius: 4,
121
+ overflow: 'auto',
122
+ }, children: typeof result === 'string'
123
+ ? result
124
+ : JSON.stringify(result, null, 2) })] }))] }))] }));
125
+ };
126
+ const UserMessage = () => ((0, jsx_runtime_1.jsx)("div", { style: {
127
+ display: 'flex',
128
+ justifyContent: 'flex-end',
129
+ width: '100%',
130
+ }, children: (0, jsx_runtime_1.jsxs)("div", { style: { maxWidth: '80%' }, children: [(0, jsx_runtime_1.jsx)("div", { style: {
131
+ fontSize: 12,
132
+ color: '#888',
133
+ marginBottom: 4,
134
+ textAlign: 'right',
135
+ }, children: "You" }), (0, jsx_runtime_1.jsx)("div", { style: {
136
+ padding: 12,
137
+ borderRadius: 12,
138
+ backgroundColor: '#e3f2fd',
139
+ }, children: (0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.Content, { components: {
140
+ Text: ({ text }) => ((0, jsx_runtime_1.jsx)("span", { style: { fontSize: 14, whiteSpace: 'pre-wrap' }, children: text })),
141
+ } }) })] }) }));
142
+ const AssistantMessage = () => ((0, jsx_runtime_1.jsx)("div", { style: {
143
+ display: 'flex',
144
+ justifyContent: 'flex-start',
145
+ width: '100%',
146
+ }, children: (0, jsx_runtime_1.jsxs)("div", { style: { maxWidth: '80%' }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: 12, color: '#888', marginBottom: 4 }, children: "Assistant" }), (0, jsx_runtime_1.jsxs)("div", { style: {
147
+ padding: 12,
148
+ borderRadius: 12,
149
+ backgroundColor: '#f5f5f5',
150
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.MessagePrimitive.Content, { components: {
151
+ Text: ({ text }) => ((0, jsx_runtime_1.jsx)("span", { style: { fontSize: 14, whiteSpace: 'pre-wrap' }, children: text })),
152
+ tools: {
153
+ Fallback: (props) => ((0, jsx_runtime_1.jsx)(ToolCallDisplay, { toolName: props.toolName, args: props.args, result: props.result, status: props.status, addResult: props.addResult })),
154
+ },
155
+ } }), (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: {
156
+ display: 'flex',
157
+ alignItems: 'center',
158
+ gap: 6,
159
+ marginTop: 8,
160
+ fontSize: 13,
161
+ color: '#888',
162
+ }, children: "Thinking..." }) }) })] })] }) }));
163
+ const PikkuComposer = () => ((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: {
164
+ border: '1px solid #ddd',
165
+ borderRadius: 12,
166
+ overflow: 'hidden',
167
+ display: 'flex',
168
+ alignItems: 'flex-end',
169
+ padding: '6px 12px',
170
+ gap: 8,
171
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Input, { placeholder: "Message...", rows: 2, style: {
172
+ flex: 1,
173
+ border: 'none',
174
+ outline: 'none',
175
+ resize: 'none',
176
+ fontSize: 14,
177
+ fontFamily: 'inherit',
178
+ padding: '4px 0',
179
+ background: 'transparent',
180
+ } }), (0, jsx_runtime_1.jsx)(react_2.ComposerPrimitive.Send, { style: {
181
+ width: 28,
182
+ height: 28,
183
+ borderRadius: '50%',
184
+ border: 'none',
185
+ background: '#1976d2',
186
+ color: '#fff',
187
+ cursor: 'pointer',
188
+ display: 'flex',
189
+ alignItems: 'center',
190
+ justifyContent: 'center',
191
+ flexShrink: 0,
192
+ marginBottom: 2,
193
+ fontSize: 14,
194
+ }, children: "\u25B6" })] }) }) }));
195
+ function PikkuAgentChat(props) {
196
+ const { emptyMessage } = props, runtimeOptions = __rest(props, ["emptyMessage"]);
197
+ const runtime = (0, use_pikku_agent_runtime_js_1.usePikkuAgentRuntime)(runtimeOptions);
198
+ return ((0, jsx_runtime_1.jsx)(react_2.AssistantRuntimeProvider, { runtime: runtime, children: (0, jsx_runtime_1.jsx)("div", { style: {
199
+ height: '100%',
200
+ display: 'flex',
201
+ flexDirection: 'column',
202
+ }, children: (0, jsx_runtime_1.jsxs)(react_2.ThreadPrimitive.Root, { style: {
203
+ display: 'flex',
204
+ flexDirection: 'column',
205
+ flex: 1,
206
+ minHeight: 0,
207
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Viewport, { style: {
208
+ flex: 1,
209
+ minHeight: 0,
210
+ overflowY: 'auto',
211
+ }, children: (0, jsx_runtime_1.jsxs)("div", { style: {
212
+ maxWidth: 768,
213
+ margin: '0 auto',
214
+ padding: 16,
215
+ display: 'flex',
216
+ flexDirection: 'column',
217
+ gap: 16,
218
+ }, children: [(0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Empty, { children: (0, jsx_runtime_1.jsx)("div", { style: {
219
+ display: 'flex',
220
+ alignItems: 'center',
221
+ justifyContent: 'center',
222
+ minHeight: 300,
223
+ color: '#888',
224
+ textAlign: 'center',
225
+ fontSize: 14,
226
+ }, children: emptyMessage !== null && emptyMessage !== void 0 ? emptyMessage : (props.threadId
227
+ ? 'Send a message to start the conversation.'
228
+ : 'Start a new conversation.') }) }), (0, jsx_runtime_1.jsx)(react_2.ThreadPrimitive.Messages, { components: {
229
+ UserMessage,
230
+ AssistantMessage,
231
+ } })] }) }), (0, jsx_runtime_1.jsx)("div", { style: { maxWidth: 768, margin: '0 auto', width: '100%', padding: '0 16px' }, children: (0, jsx_runtime_1.jsx)(PikkuComposer, {}) })] }) }) }));
232
+ }
@@ -0,0 +1,13 @@
1
+ export interface PikkuAgentRuntimeOptions {
2
+ api: string;
3
+ threadId?: string | null;
4
+ initialMessages?: any[];
5
+ onThreadCreated?: (id: string) => void;
6
+ onFinish?: () => void;
7
+ onApprovalRequest?: (data: {
8
+ toolCallId: string;
9
+ }) => void;
10
+ credentials?: RequestCredentials;
11
+ headers?: Record<string, string>;
12
+ }
13
+ export declare function usePikkuAgentRuntime(options: PikkuAgentRuntimeOptions): import("@assistant-ui/react").AssistantRuntime;
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePikkuAgentRuntime = usePikkuAgentRuntime;
4
+ const react_1 = require("react");
5
+ const react_data_stream_1 = require("@assistant-ui/react-data-stream");
6
+ const convertDbMessages = (dbMessages) => {
7
+ const result = [];
8
+ let currentAssistant = null;
9
+ for (const msg of dbMessages) {
10
+ if (msg.role === 'user') {
11
+ if (currentAssistant) {
12
+ result.push(currentAssistant);
13
+ currentAssistant = null;
14
+ }
15
+ result.push({
16
+ role: 'user',
17
+ content: msg.content || '',
18
+ id: msg.id,
19
+ createdAt: new Date(msg.createdAt),
20
+ });
21
+ continue;
22
+ }
23
+ if (msg.role === 'tool' &&
24
+ currentAssistant &&
25
+ Array.isArray(msg.toolResults)) {
26
+ const parts = Array.isArray(currentAssistant.content)
27
+ ? [...currentAssistant.content]
28
+ : currentAssistant.content
29
+ ? [
30
+ {
31
+ type: 'text',
32
+ text: currentAssistant.content,
33
+ },
34
+ ]
35
+ : [];
36
+ for (const tr of msg.toolResults) {
37
+ const tcIdx = parts.findIndex((p) => p.type === 'tool-call' && p.toolCallId === tr.id);
38
+ if (tcIdx !== -1) {
39
+ parts[tcIdx] = Object.assign(Object.assign({}, parts[tcIdx]), { result: typeof tr.result === 'string'
40
+ ? tr.result
41
+ : JSON.stringify(tr.result) });
42
+ }
43
+ }
44
+ currentAssistant = {
45
+ role: currentAssistant.role,
46
+ id: currentAssistant.id,
47
+ createdAt: currentAssistant.createdAt,
48
+ status: currentAssistant.status,
49
+ content: parts,
50
+ };
51
+ continue;
52
+ }
53
+ if (msg.role === 'tool')
54
+ continue;
55
+ const parts = [];
56
+ if (msg.content) {
57
+ parts.push({ type: 'text', text: msg.content });
58
+ }
59
+ if (Array.isArray(msg.toolCalls)) {
60
+ for (const tc of msg.toolCalls) {
61
+ parts.push({
62
+ type: 'tool-call',
63
+ toolCallId: tc.id,
64
+ toolName: tc.name,
65
+ args: tc.args || {},
66
+ });
67
+ }
68
+ }
69
+ if (currentAssistant) {
70
+ const prev = currentAssistant.content;
71
+ const existingParts = Array.isArray(prev)
72
+ ? [...prev]
73
+ : prev
74
+ ? [{ type: 'text', text: prev }]
75
+ : [];
76
+ currentAssistant = {
77
+ role: currentAssistant.role,
78
+ id: currentAssistant.id,
79
+ createdAt: currentAssistant.createdAt,
80
+ status: currentAssistant.status,
81
+ content: [...existingParts, ...parts],
82
+ };
83
+ }
84
+ else {
85
+ currentAssistant = {
86
+ role: 'assistant',
87
+ content: parts.length > 0 ? parts : '',
88
+ id: msg.id,
89
+ createdAt: new Date(msg.createdAt),
90
+ status: { type: 'complete', reason: 'stop' },
91
+ };
92
+ }
93
+ }
94
+ if (currentAssistant) {
95
+ result.push(currentAssistant);
96
+ }
97
+ return result;
98
+ };
99
+ function usePikkuAgentRuntime(options) {
100
+ const { api, threadId = null, initialMessages: rawInitialMessages, onThreadCreated, onFinish, onApprovalRequest, credentials, headers, } = options;
101
+ const threadIdRef = (0, react_1.useRef)(threadId);
102
+ threadIdRef.current = threadId;
103
+ const justCreatedThreadRef = (0, react_1.useRef)(false);
104
+ const bodyFn = (0, react_1.useCallback)(() => {
105
+ let currentThreadId = threadIdRef.current;
106
+ if (!currentThreadId) {
107
+ currentThreadId = crypto.randomUUID();
108
+ justCreatedThreadRef.current = true;
109
+ onThreadCreated === null || onThreadCreated === void 0 ? void 0 : onThreadCreated(currentThreadId);
110
+ }
111
+ return { threadId: currentThreadId };
112
+ }, [onThreadCreated]);
113
+ const onData = (0, react_1.useCallback)((event) => {
114
+ if (event.name === 'approval-request') {
115
+ const approval = event.data;
116
+ onApprovalRequest === null || onApprovalRequest === void 0 ? void 0 : onApprovalRequest({
117
+ toolCallId: approval.toolCallId,
118
+ });
119
+ }
120
+ }, [onApprovalRequest]);
121
+ const onFinishCb = (0, react_1.useCallback)(() => {
122
+ onFinish === null || onFinish === void 0 ? void 0 : onFinish();
123
+ }, [onFinish]);
124
+ const initialMessages = (0, react_1.useMemo)(() => (rawInitialMessages ? convertDbMessages(rawInitialMessages) : []), [rawInitialMessages]);
125
+ const runtime = (0, react_data_stream_1.useDataStreamRuntime)({
126
+ api,
127
+ protocol: 'ui-message-stream',
128
+ body: bodyFn,
129
+ onData,
130
+ onFinish: onFinishCb,
131
+ initialMessages,
132
+ credentials,
133
+ headers,
134
+ });
135
+ const prevThreadIdRef = (0, react_1.useRef)(threadId);
136
+ const hasResetRef = (0, react_1.useRef)(false);
137
+ (0, react_1.useEffect)(() => {
138
+ if (prevThreadIdRef.current !== threadId) {
139
+ prevThreadIdRef.current = threadId;
140
+ if (justCreatedThreadRef.current) {
141
+ justCreatedThreadRef.current = false;
142
+ hasResetRef.current = true;
143
+ return;
144
+ }
145
+ hasResetRef.current = false;
146
+ if (rawInitialMessages) {
147
+ ;
148
+ runtime.thread.reset(convertDbMessages(rawInitialMessages));
149
+ hasResetRef.current = true;
150
+ }
151
+ else {
152
+ ;
153
+ runtime.thread.reset([]);
154
+ }
155
+ }
156
+ else if (!hasResetRef.current && rawInitialMessages) {
157
+ ;
158
+ runtime.thread.reset(convertDbMessages(rawInitialMessages));
159
+ hasResetRef.current = true;
160
+ }
161
+ }, [threadId, rawInitialMessages, runtime]);
162
+ return runtime;
163
+ }
@@ -0,0 +1,4 @@
1
+ export { usePikkuAgentRuntime } from './use-pikku-agent-runtime.js';
2
+ export type { PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
3
+ export { PikkuAgentChat } from './pikku-agent-chat.js';
4
+ export type { PikkuAgentChatProps } from './pikku-agent-chat.js';
@@ -0,0 +1,2 @@
1
+ export { usePikkuAgentRuntime } from './use-pikku-agent-runtime.js';
2
+ export { PikkuAgentChat } from './pikku-agent-chat.js';
@@ -0,0 +1 @@
1
+ {"type": "module"}
@@ -0,0 +1,5 @@
1
+ import { type PikkuAgentRuntimeOptions } from './use-pikku-agent-runtime.js';
2
+ export interface PikkuAgentChatProps extends PikkuAgentRuntimeOptions {
3
+ emptyMessage?: string;
4
+ }
5
+ export declare function PikkuAgentChat(props: PikkuAgentChatProps): import("react/jsx-runtime").JSX.Element;