@applica-software-guru/persona-chat-sdk 0.1.113 → 0.1.115
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/.eslintrc.cjs +7 -0
- package/dist/bundle.cjs.js +14 -14
- package/dist/bundle.cjs.js.map +1 -1
- package/dist/bundle.es.js +2867 -2633
- package/dist/bundle.es.js.map +1 -1
- package/dist/bundle.iife.js +17 -17
- package/dist/bundle.iife.js.map +1 -1
- package/dist/bundle.umd.js +14 -14
- package/dist/bundle.umd.js.map +1 -1
- package/dist/protocol/rest.d.ts +1 -0
- package/dist/protocol/rest.d.ts.map +1 -1
- package/dist/runtime/context.d.ts +11 -1
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/handlers.d.ts +7 -6
- package/dist/runtime/handlers.d.ts.map +1 -1
- package/dist/runtime/listeners.d.ts +6 -3
- package/dist/runtime/listeners.d.ts.map +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/types.d.ts +68 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/playground/src/hooks/theme.ts +0 -1
- package/playground/src/tools.ts +4 -3
- package/src/protocol/rest.ts +134 -21
- package/src/runtime/context.ts +25 -1
- package/src/runtime/handlers.ts +70 -32
- package/src/runtime/listeners.ts +190 -18
- package/src/runtime.tsx +56 -26
- package/src/types.ts +88 -3
package/src/runtime/handlers.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AppendMessage } from '@assistant-ui/react';
|
|
2
2
|
import { StartRunConfig } from '@assistant-ui/react/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore';
|
|
3
|
-
import { PersonaProtocol, PersonaMessage, TransformMessages } from '../types';
|
|
3
|
+
import { PersonaProtocol, PersonaMessage, TransformMessages, AgentState } from '../types';
|
|
4
4
|
import type { PersonaLogger } from '../logging';
|
|
5
5
|
import { parseMessages } from '../messages';
|
|
6
6
|
import { fileToBase64 } from './utils';
|
|
@@ -11,7 +11,7 @@ import { fileToBase64 } from './utils';
|
|
|
11
11
|
export function createOnNewHandler(
|
|
12
12
|
protocols: PersonaProtocol[],
|
|
13
13
|
setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
14
|
-
|
|
14
|
+
setAgentState: React.Dispatch<React.SetStateAction<AgentState>>,
|
|
15
15
|
transformMessages?: TransformMessages,
|
|
16
16
|
logger?: PersonaLogger,
|
|
17
17
|
) {
|
|
@@ -23,7 +23,19 @@ export function createOnNewHandler(
|
|
|
23
23
|
throw new Error('Message must contain text or attachments');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const
|
|
26
|
+
const input = textPart?.type === 'text' ? textPart.text ?? '' : '';
|
|
27
|
+
|
|
28
|
+
// Check for steer command: /steer <text>
|
|
29
|
+
if (input.startsWith('/steer ') && !hasAttachments) {
|
|
30
|
+
const steerText = input.slice(7).trim();
|
|
31
|
+
if (steerText) {
|
|
32
|
+
return steerAgent(protocols, steerText, logger);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ready = protocols.some(
|
|
37
|
+
(protocol) => protocol.getName() !== 'transaction' && protocol.status === 'connected',
|
|
38
|
+
);
|
|
27
39
|
|
|
28
40
|
if (!ready) {
|
|
29
41
|
setMessages((currentConversation) => {
|
|
@@ -40,7 +52,6 @@ export function createOnNewHandler(
|
|
|
40
52
|
return;
|
|
41
53
|
}
|
|
42
54
|
|
|
43
|
-
const input = textPart?.type === 'text' ? textPart.text ?? '' : '';
|
|
44
55
|
const userMessage: PersonaMessage = {
|
|
45
56
|
role: 'user',
|
|
46
57
|
type: 'text',
|
|
@@ -53,9 +64,11 @@ export function createOnNewHandler(
|
|
|
53
64
|
return transformMessages ? transformMessages(newMessages) : newMessages;
|
|
54
65
|
});
|
|
55
66
|
|
|
56
|
-
|
|
67
|
+
setAgentState('running');
|
|
57
68
|
|
|
58
|
-
const protocol = protocols
|
|
69
|
+
const protocol = protocols
|
|
70
|
+
.sort((a, b) => b.getPriority() - a.getPriority())
|
|
71
|
+
.find((protocol) => protocol.status === 'connected');
|
|
59
72
|
|
|
60
73
|
const content: Array<PersonaMessage> = [];
|
|
61
74
|
|
|
@@ -133,6 +146,36 @@ export function createOnNewHandler(
|
|
|
133
146
|
};
|
|
134
147
|
}
|
|
135
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Sends a steer command to all connected protocols.
|
|
151
|
+
*/
|
|
152
|
+
async function steerAgent(
|
|
153
|
+
protocols: PersonaProtocol[],
|
|
154
|
+
steerText: string,
|
|
155
|
+
logger?: PersonaLogger,
|
|
156
|
+
) {
|
|
157
|
+
const connectedProtocols = protocols.filter(
|
|
158
|
+
(p) => p.getName() !== 'transaction' && p.status === 'connected',
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (connectedProtocols.length === 0) {
|
|
162
|
+
logger?.warn('No connected protocol available for steer');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
logger?.debug('Sending steer command:', steerText);
|
|
167
|
+
|
|
168
|
+
// Send steer to all connected protocols
|
|
169
|
+
await Promise.allSettled(
|
|
170
|
+
connectedProtocols.map((protocol) =>
|
|
171
|
+
protocol.sendPacket({
|
|
172
|
+
type: 'command',
|
|
173
|
+
payload: { command: 'steer', arguments: { text: steerText } },
|
|
174
|
+
}),
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
136
179
|
/**
|
|
137
180
|
* Creates the onEdit handler for editing messages
|
|
138
181
|
*/
|
|
@@ -140,7 +183,7 @@ export function createOnEditHandler(
|
|
|
140
183
|
protocols: PersonaProtocol[],
|
|
141
184
|
messages: PersonaMessage[],
|
|
142
185
|
setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
143
|
-
|
|
186
|
+
setAgentState: React.Dispatch<React.SetStateAction<AgentState>>,
|
|
144
187
|
transformMessages?: TransformMessages,
|
|
145
188
|
logger?: PersonaLogger,
|
|
146
189
|
) {
|
|
@@ -149,7 +192,9 @@ export function createOnEditHandler(
|
|
|
149
192
|
throw new Error('Only text messages are supported');
|
|
150
193
|
}
|
|
151
194
|
|
|
152
|
-
const protocol = protocols
|
|
195
|
+
const protocol = protocols
|
|
196
|
+
.sort((a, b) => b.getPriority() - a.getPriority())
|
|
197
|
+
.find((protocol) => protocol.status === 'connected');
|
|
153
198
|
|
|
154
199
|
if (!protocol) {
|
|
155
200
|
logger?.debug('No protocol available for edit');
|
|
@@ -179,7 +224,7 @@ export function createOnEditHandler(
|
|
|
179
224
|
const newMessages = parseMessages([...messagesToKeep, editedMessage]);
|
|
180
225
|
setMessages(transformMessages ? transformMessages(newMessages) : newMessages);
|
|
181
226
|
|
|
182
|
-
|
|
227
|
+
setAgentState('running');
|
|
183
228
|
|
|
184
229
|
const content: Array<PersonaMessage> = [
|
|
185
230
|
{
|
|
@@ -202,12 +247,14 @@ export function createOnReloadHandler(
|
|
|
202
247
|
protocols: PersonaProtocol[],
|
|
203
248
|
messages: PersonaMessage[],
|
|
204
249
|
setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
205
|
-
|
|
250
|
+
setAgentState: React.Dispatch<React.SetStateAction<AgentState>>,
|
|
206
251
|
transformMessages?: TransformMessages,
|
|
207
252
|
logger?: PersonaLogger,
|
|
208
253
|
) {
|
|
209
254
|
return async (parentId: string | null, config: StartRunConfig) => {
|
|
210
|
-
const protocol = protocols
|
|
255
|
+
const protocol = protocols
|
|
256
|
+
.sort((a, b) => b.getPriority() - a.getPriority())
|
|
257
|
+
.find((protocol) => protocol.status === 'connected');
|
|
211
258
|
|
|
212
259
|
if (!protocol) {
|
|
213
260
|
logger?.debug('No protocol available for reload');
|
|
@@ -250,7 +297,7 @@ export function createOnReloadHandler(
|
|
|
250
297
|
return;
|
|
251
298
|
}
|
|
252
299
|
|
|
253
|
-
|
|
300
|
+
setAgentState('running');
|
|
254
301
|
|
|
255
302
|
const content: Array<PersonaMessage> = [];
|
|
256
303
|
|
|
@@ -281,39 +328,30 @@ export function createOnReloadHandler(
|
|
|
281
328
|
}
|
|
282
329
|
|
|
283
330
|
/**
|
|
284
|
-
* Creates the onCancel handler for cancelling
|
|
331
|
+
* Creates the onCancel handler for cancelling agent execution.
|
|
332
|
+
* Sends the dedicated 'cancel' command to all connected protocols.
|
|
285
333
|
*/
|
|
286
334
|
export function createOnCancelHandler(
|
|
287
335
|
protocols: PersonaProtocol[],
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
transformMessages?: TransformMessages,
|
|
336
|
+
setAgentState: React.Dispatch<React.SetStateAction<AgentState>>,
|
|
337
|
+
logger?: PersonaLogger,
|
|
291
338
|
) {
|
|
292
339
|
return () => {
|
|
293
|
-
|
|
340
|
+
setAgentState('cancelling');
|
|
294
341
|
|
|
295
|
-
|
|
296
|
-
const lastMessage = currentMessages[currentMessages.length - 1];
|
|
297
|
-
if (lastMessage?.role === 'assistant' && !lastMessage.finishReason) {
|
|
298
|
-
const updatedMessages = [...currentMessages];
|
|
299
|
-
updatedMessages[updatedMessages.length - 1] = {
|
|
300
|
-
...lastMessage,
|
|
301
|
-
finishReason: 'stop',
|
|
302
|
-
status: { type: 'incomplete' },
|
|
303
|
-
};
|
|
304
|
-
return transformMessages ? transformMessages(updatedMessages) : updatedMessages;
|
|
305
|
-
}
|
|
306
|
-
return currentMessages;
|
|
307
|
-
});
|
|
342
|
+
logger?.debug('Sending cancel command to all connected protocols');
|
|
308
343
|
|
|
344
|
+
// Send the dedicated cancel command to ALL connected protocols
|
|
309
345
|
protocols.forEach((protocol) => {
|
|
310
346
|
if (protocol.status === 'connected') {
|
|
311
347
|
protocol
|
|
312
348
|
.sendPacket({
|
|
313
349
|
type: 'command',
|
|
314
|
-
payload: { command: '
|
|
350
|
+
payload: { command: 'cancel', arguments: {} },
|
|
315
351
|
})
|
|
316
|
-
.catch(() => {
|
|
352
|
+
.catch((err) => {
|
|
353
|
+
logger?.error('Failed to send cancel command:', err);
|
|
354
|
+
});
|
|
317
355
|
}
|
|
318
356
|
});
|
|
319
357
|
|
package/src/runtime/listeners.ts
CHANGED
|
@@ -1,23 +1,147 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
PersonaPacket,
|
|
3
|
+
PersonaMessage,
|
|
4
|
+
PersonaReasoning,
|
|
5
|
+
PersonaTransaction,
|
|
6
|
+
PersonaProtocol,
|
|
7
|
+
TransformMessages,
|
|
8
|
+
AgentState,
|
|
9
|
+
AgentEndPayload,
|
|
10
|
+
ToolExecutionStartPayload,
|
|
11
|
+
ToolExecutionUpdatePayload,
|
|
12
|
+
ToolExecutionEndPayload,
|
|
13
|
+
SteerAcceptedPayload,
|
|
14
|
+
SteerProcessedPayload,
|
|
15
|
+
} from '../types';
|
|
2
16
|
import { parseMessages } from '../messages';
|
|
3
17
|
|
|
4
18
|
/**
|
|
5
|
-
* Creates packet listener for a protocol
|
|
19
|
+
* Creates packet listener for a protocol.
|
|
20
|
+
*
|
|
21
|
+
* Uses setAgentState instead of setIsRunning for proper lifecycle management.
|
|
22
|
+
* The send button is only re-enabled after agent_end.
|
|
6
23
|
*/
|
|
7
24
|
export function createPacketListener(
|
|
8
25
|
protocol: PersonaProtocol,
|
|
9
26
|
setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
10
|
-
|
|
27
|
+
setAgentState: React.Dispatch<React.SetStateAction<AgentState>>,
|
|
11
28
|
protocols: PersonaProtocol[],
|
|
12
29
|
transformMessages?: TransformMessages,
|
|
30
|
+
onSteerAccepted?: (payload: SteerAcceptedPayload) => void,
|
|
31
|
+
onSteerProcessed?: (payload: SteerProcessedPayload) => void,
|
|
13
32
|
) {
|
|
14
|
-
return (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
33
|
+
return (packet: PersonaPacket) => {
|
|
34
|
+
switch (packet.type) {
|
|
35
|
+
// ─── Lifecycle: Agent ───────────────────────────────────────
|
|
36
|
+
case 'agent_start':
|
|
37
|
+
setAgentState('running');
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
case 'agent_end': {
|
|
41
|
+
const endPayload = packet.payload as unknown as AgentEndPayload;
|
|
42
|
+
setAgentState('idle');
|
|
43
|
+
|
|
44
|
+
// Aggiungi messaggio di sistema per stop reason speciali
|
|
45
|
+
if (endPayload.stopReason === 'cancelled') {
|
|
46
|
+
setMessages((current) => [
|
|
47
|
+
...current,
|
|
48
|
+
{
|
|
49
|
+
role: 'assistant' as const,
|
|
50
|
+
type: 'text' as const,
|
|
51
|
+
text: '⏹ Task cancelled',
|
|
52
|
+
createdAt: new Date(),
|
|
53
|
+
finishReason: 'stop',
|
|
54
|
+
status: { type: 'incomplete' as const },
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
} else if (endPayload.stopReason === 'error') {
|
|
58
|
+
setMessages((current) => [
|
|
59
|
+
...current,
|
|
60
|
+
{
|
|
61
|
+
role: 'assistant' as const,
|
|
62
|
+
type: 'text' as const,
|
|
63
|
+
text: `❌ Error: ${endPayload.errorMessage || 'Unknown error'}`,
|
|
64
|
+
createdAt: new Date(),
|
|
65
|
+
finishReason: 'stop',
|
|
66
|
+
status: { type: 'incomplete' as const },
|
|
67
|
+
},
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── Lifecycle: Turn ────────────────────────────────────────
|
|
74
|
+
case 'turn_start':
|
|
75
|
+
// Opzionale: indicatore di nuovo turno nell'UI
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'turn_end':
|
|
79
|
+
// Opzionale: riepilogo turno
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
// ─── Lifecycle: Tool ────────────────────────────────────────
|
|
83
|
+
case 'tool_execution_start':
|
|
84
|
+
handleToolStart(
|
|
85
|
+
packet.payload as unknown as ToolExecutionStartPayload,
|
|
86
|
+
protocol,
|
|
87
|
+
setMessages,
|
|
88
|
+
);
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
case 'tool_execution_update':
|
|
92
|
+
handleToolUpdate(
|
|
93
|
+
packet.payload as unknown as ToolExecutionUpdatePayload,
|
|
94
|
+
setMessages,
|
|
95
|
+
);
|
|
96
|
+
break;
|
|
97
|
+
|
|
98
|
+
case 'tool_execution_end':
|
|
99
|
+
handleToolEnd(
|
|
100
|
+
packet.payload as unknown as ToolExecutionEndPayload,
|
|
101
|
+
protocol,
|
|
102
|
+
setMessages,
|
|
103
|
+
);
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
// ─── Steer ──────────────────────────────────────────────────
|
|
107
|
+
case 'steer_accepted':
|
|
108
|
+
onSteerAccepted?.(packet.payload as unknown as SteerAcceptedPayload);
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
case 'steer_processed':
|
|
112
|
+
onSteerProcessed?.(packet.payload as unknown as SteerProcessedPayload);
|
|
113
|
+
break;
|
|
114
|
+
|
|
115
|
+
// ─── Messages ───────────────────────────────────────────────
|
|
116
|
+
case 'message':
|
|
117
|
+
handleMessagePacket(
|
|
118
|
+
packet.payload as unknown as PersonaMessage,
|
|
119
|
+
protocol,
|
|
120
|
+
setMessages,
|
|
121
|
+
transformMessages,
|
|
122
|
+
);
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
case 'reasoning':
|
|
126
|
+
handleReasoningPacket(
|
|
127
|
+
packet.payload as unknown as PersonaReasoning,
|
|
128
|
+
protocol,
|
|
129
|
+
setMessages,
|
|
130
|
+
transformMessages,
|
|
131
|
+
);
|
|
132
|
+
break;
|
|
133
|
+
|
|
134
|
+
case 'transaction':
|
|
135
|
+
handleTransactionPacket(
|
|
136
|
+
packet.payload as unknown as PersonaTransaction,
|
|
137
|
+
protocol,
|
|
138
|
+
protocols,
|
|
139
|
+
);
|
|
140
|
+
break;
|
|
141
|
+
|
|
142
|
+
default:
|
|
143
|
+
// Ignora packet types sconosciuti (forward compatibility)
|
|
144
|
+
break;
|
|
21
145
|
}
|
|
22
146
|
};
|
|
23
147
|
}
|
|
@@ -26,7 +150,6 @@ function handleMessagePacket(
|
|
|
26
150
|
personaMessage: PersonaMessage,
|
|
27
151
|
protocol: PersonaProtocol,
|
|
28
152
|
setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
29
|
-
setIsRunning: React.Dispatch<React.SetStateAction<boolean>>,
|
|
30
153
|
transformMessages?: TransformMessages,
|
|
31
154
|
) {
|
|
32
155
|
// Ensure message has proper timestamp
|
|
@@ -34,11 +157,6 @@ function handleMessagePacket(
|
|
|
34
157
|
personaMessage.createdAt = new Date();
|
|
35
158
|
}
|
|
36
159
|
|
|
37
|
-
// Handle finish reason and update isRunning state
|
|
38
|
-
if (personaMessage?.finishReason === 'stop' && !personaMessage?.functionResponse && !personaMessage?.thought) {
|
|
39
|
-
setIsRunning(false);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
160
|
// Convert thought to reasoning type
|
|
43
161
|
if (personaMessage.thought) {
|
|
44
162
|
personaMessage.type = 'reasoning';
|
|
@@ -74,6 +192,60 @@ function handleReasoningPacket(
|
|
|
74
192
|
});
|
|
75
193
|
}
|
|
76
194
|
|
|
77
|
-
function handleTransactionPacket(
|
|
78
|
-
|
|
195
|
+
function handleTransactionPacket(
|
|
196
|
+
transaction: PersonaTransaction,
|
|
197
|
+
currentProtocol: PersonaProtocol,
|
|
198
|
+
protocols: PersonaProtocol[],
|
|
199
|
+
) {
|
|
200
|
+
protocols
|
|
201
|
+
.filter((p) => p !== currentProtocol)
|
|
202
|
+
.forEach((p) => p.onTransaction(transaction));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ─── Tool Execution Handlers ──────────────────────────────────────────
|
|
206
|
+
|
|
207
|
+
function handleToolStart(
|
|
208
|
+
payload: ToolExecutionStartPayload,
|
|
209
|
+
protocol: PersonaProtocol,
|
|
210
|
+
_setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
211
|
+
) {
|
|
212
|
+
// Opzionale: crea un messaggio "tool in esecuzione" visibile nell'UI
|
|
213
|
+
// Per ora, il tool call viene gestito dal flusso functionCalls esistente.
|
|
214
|
+
// Qui possiamo emettere un log o un indicatore.
|
|
215
|
+
if (typeof window !== 'undefined' && (window as any).__PERSONA_DEBUG__) {
|
|
216
|
+
console.debug(
|
|
217
|
+
`[${protocol.getName()}] Tool start: ${payload.toolName}`,
|
|
218
|
+
payload.args,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function handleToolUpdate(
|
|
224
|
+
payload: ToolExecutionUpdatePayload,
|
|
225
|
+
_setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
226
|
+
) {
|
|
227
|
+
// Tool streaming update: aggiorna il risultato parziale
|
|
228
|
+
// Per ora logghiamo; in futuro si può aggiornare un messaggio tool inline
|
|
229
|
+
if (typeof window !== 'undefined' && (window as any).__PERSONA_DEBUG__) {
|
|
230
|
+
console.debug(
|
|
231
|
+
`Tool update: ${payload.toolCallId}`,
|
|
232
|
+
payload.partialResult,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function handleToolEnd(
|
|
238
|
+
payload: ToolExecutionEndPayload,
|
|
239
|
+
protocol: PersonaProtocol,
|
|
240
|
+
_setMessages: React.Dispatch<React.SetStateAction<PersonaMessage[]>>,
|
|
241
|
+
) {
|
|
242
|
+
// Tool execution completata. Il result arriva anche come functionResponse
|
|
243
|
+
// nel flusso messaggi. Qui logghiamo per debug.
|
|
244
|
+
if (typeof window !== 'undefined' && (window as any).__PERSONA_DEBUG__) {
|
|
245
|
+
console.debug(
|
|
246
|
+
`[${protocol.getName()}] Tool end: ${payload.toolCallId}`,
|
|
247
|
+
payload.durationMs ? `(${payload.durationMs}ms)` : '',
|
|
248
|
+
payload.error ? `ERROR: ${payload.error}` : '',
|
|
249
|
+
);
|
|
250
|
+
}
|
|
79
251
|
}
|
package/src/runtime.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
SimpleTextAttachmentAdapter,
|
|
8
8
|
} from '@assistant-ui/react';
|
|
9
9
|
import { SimpleFileAttachmentAdapter } from './runtime/file-attachment-adapter';
|
|
10
|
-
import { PersonaConfig, PersonaMessage, PersonaResponse, ProtocolStatus, Session, ThreadData } from './types';
|
|
10
|
+
import { PersonaConfig, PersonaMessage, PersonaResponse, ProtocolStatus, Session, ThreadData, AgentState } from './types';
|
|
11
11
|
import { parseMessages, convertMessage } from './messages';
|
|
12
12
|
import { initializeProtocols } from './runtime/protocols';
|
|
13
13
|
import { createPacketListener } from './runtime/listeners';
|
|
@@ -23,6 +23,8 @@ export {
|
|
|
23
23
|
usePersonaRuntimeWebRTCProtocol,
|
|
24
24
|
usePersonaRuntimeMessages,
|
|
25
25
|
usePersonaRenameThread,
|
|
26
|
+
useAgentState,
|
|
27
|
+
useSteerAgent,
|
|
26
28
|
} from './runtime/context';
|
|
27
29
|
|
|
28
30
|
export type { PersonaMessage, PersonaResponse, ThreadData };
|
|
@@ -44,9 +46,12 @@ function PersonaRuntimeProviderInner({
|
|
|
44
46
|
onThreadUnarchive,
|
|
45
47
|
onThreadDelete,
|
|
46
48
|
enableAttachments = false,
|
|
49
|
+
enableSteer = false,
|
|
50
|
+
onSteerAccepted,
|
|
51
|
+
onSteerProcessed,
|
|
47
52
|
...config
|
|
48
53
|
}: Readonly<PersonaConfig>) {
|
|
49
|
-
const [
|
|
54
|
+
const [agentState, setAgentState] = useState<AgentState>('idle');
|
|
50
55
|
const [messages, setMessages] = useState<PersonaMessage[]>([]);
|
|
51
56
|
const [protocolsStatus, setProtocolsStatus] = useState<Map<string, ProtocolStatus>>(new Map());
|
|
52
57
|
const didMount = useRef(false);
|
|
@@ -150,13 +155,23 @@ function PersonaRuntimeProviderInner({
|
|
|
150
155
|
});
|
|
151
156
|
|
|
152
157
|
// Packet listener
|
|
153
|
-
protocol.addPacketListener(
|
|
158
|
+
protocol.addPacketListener(
|
|
159
|
+
createPacketListener(
|
|
160
|
+
protocol,
|
|
161
|
+
setMessages,
|
|
162
|
+
setAgentState,
|
|
163
|
+
protocols,
|
|
164
|
+
transformMessages,
|
|
165
|
+
onSteerAccepted,
|
|
166
|
+
onSteerProcessed,
|
|
167
|
+
),
|
|
168
|
+
);
|
|
154
169
|
|
|
155
170
|
// Auto-connect if configured
|
|
156
171
|
if (protocol.autostart && protocol.status === 'disconnected') {
|
|
157
172
|
logger?.debug(`Auto-connecting protocol: ${protocol.getName()}`);
|
|
158
173
|
protocol.connect(currentThreadRef.current).catch(() => {});
|
|
159
|
-
|
|
174
|
+
setAgentState('idle');
|
|
160
175
|
}
|
|
161
176
|
});
|
|
162
177
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -235,30 +250,22 @@ function PersonaRuntimeProviderInner({
|
|
|
235
250
|
}, [currentThreadId, threads, protocols, sessionStorage, transformMessages, logger]);
|
|
236
251
|
|
|
237
252
|
// Message handlers
|
|
238
|
-
const onNew = useCallback(
|
|
239
|
-
protocols,
|
|
240
|
-
transformMessages,
|
|
241
|
-
|
|
242
|
-
]);
|
|
253
|
+
const onNew = useCallback(
|
|
254
|
+
createOnNewHandler(protocols, setMessages, setAgentState, transformMessages, logger),
|
|
255
|
+
[protocols, transformMessages, logger],
|
|
256
|
+
);
|
|
243
257
|
|
|
244
|
-
const onEdit = useCallback(
|
|
245
|
-
protocols,
|
|
246
|
-
messages,
|
|
247
|
-
|
|
248
|
-
logger,
|
|
249
|
-
]);
|
|
258
|
+
const onEdit = useCallback(
|
|
259
|
+
createOnEditHandler(protocols, messages, setMessages, setAgentState, transformMessages, logger),
|
|
260
|
+
[protocols, messages, transformMessages, logger],
|
|
261
|
+
);
|
|
250
262
|
|
|
251
|
-
const onReload = useCallback(
|
|
252
|
-
protocols,
|
|
253
|
-
messages,
|
|
254
|
-
|
|
255
|
-
logger,
|
|
256
|
-
]);
|
|
263
|
+
const onReload = useCallback(
|
|
264
|
+
createOnReloadHandler(protocols, messages, setMessages, setAgentState, transformMessages, logger),
|
|
265
|
+
[protocols, messages, transformMessages, logger],
|
|
266
|
+
);
|
|
257
267
|
|
|
258
|
-
const onCancel = useCallback(createOnCancelHandler(protocols,
|
|
259
|
-
protocols,
|
|
260
|
-
transformMessages,
|
|
261
|
-
]);
|
|
268
|
+
const onCancel = useCallback(createOnCancelHandler(protocols, setAgentState, logger), [protocols, logger]);
|
|
262
269
|
|
|
263
270
|
const handleSetMessages = useCallback(
|
|
264
271
|
(newMessages: readonly PersonaMessage[]) => {
|
|
@@ -302,6 +309,7 @@ function PersonaRuntimeProviderInner({
|
|
|
302
309
|
]);
|
|
303
310
|
|
|
304
311
|
// Create runtime
|
|
312
|
+
const isRunning = agentState === 'running' || agentState === 'cancelling';
|
|
305
313
|
const runtime = useExternalStoreRuntime({
|
|
306
314
|
isRunning,
|
|
307
315
|
messages,
|
|
@@ -334,8 +342,30 @@ function PersonaRuntimeProviderInner({
|
|
|
334
342
|
[sessionStorage, onThreadRename],
|
|
335
343
|
);
|
|
336
344
|
|
|
345
|
+
const steerAgent = useCallback(
|
|
346
|
+
async (text: string) => {
|
|
347
|
+
const connectedProtocols = protocols.filter(
|
|
348
|
+
(p) => p.getName() !== 'transaction' && p.status === 'connected',
|
|
349
|
+
);
|
|
350
|
+
if (connectedProtocols.length === 0) {
|
|
351
|
+
logger?.warn('No connected protocol available for steer');
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
logger?.debug('Sending steer command:', text);
|
|
355
|
+
await Promise.allSettled(
|
|
356
|
+
connectedProtocols.map((protocol) =>
|
|
357
|
+
protocol.sendPacket({
|
|
358
|
+
type: 'command',
|
|
359
|
+
payload: { command: 'steer', arguments: { text } },
|
|
360
|
+
}),
|
|
361
|
+
),
|
|
362
|
+
);
|
|
363
|
+
},
|
|
364
|
+
[protocols, logger],
|
|
365
|
+
);
|
|
366
|
+
|
|
337
367
|
return (
|
|
338
|
-
<PersonaRuntimeContext.Provider value={{ protocols, protocolsStatus, getMessages, renameThread }}>
|
|
368
|
+
<PersonaRuntimeContext.Provider value={{ protocols, protocolsStatus, agentState, getMessages, renameThread, steerAgent }}>
|
|
339
369
|
<AssistantRuntimeProvider runtime={runtime}>{children}</AssistantRuntimeProvider>
|
|
340
370
|
</PersonaRuntimeContext.Provider>
|
|
341
371
|
);
|
package/src/types.ts
CHANGED
|
@@ -103,16 +103,25 @@ export type PersonaSource = {
|
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
export type PersonaCommand = {
|
|
106
|
-
command: 'get_mcp_assets' | 'set_initial_context' | 'set_local_tools';
|
|
106
|
+
command: 'get_mcp_assets' | 'set_initial_context' | 'set_local_tools' | 'cancel' | 'steer' | 'get_agent_state';
|
|
107
107
|
arguments: Record<string, unknown>;
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
export type PersonaRequest = PersonaMessage | Array<PersonaMessage> | string;
|
|
111
111
|
|
|
112
|
-
export type PersonaPacketPayload = PersonaRequest | PersonaCommand | PersonaReasoning | PersonaTransaction
|
|
112
|
+
export type PersonaPacketPayload = PersonaRequest | PersonaCommand | PersonaReasoning | PersonaTransaction
|
|
113
|
+
| AgentStartPayload | AgentEndPayload
|
|
114
|
+
| TurnStartPayload | TurnEndPayload
|
|
115
|
+
| ToolExecutionStartPayload | ToolExecutionUpdatePayload | ToolExecutionEndPayload
|
|
116
|
+
| SteerAcceptedPayload | SteerProcessedPayload
|
|
117
|
+
| Record<string, unknown>;
|
|
113
118
|
|
|
114
119
|
export type PersonaPacket = {
|
|
115
|
-
type: 'request' | 'message' | 'command' | 'reasoning' | 'mcp_assets' | 'transaction'
|
|
120
|
+
type: 'request' | 'message' | 'command' | 'reasoning' | 'mcp_assets' | 'transaction'
|
|
121
|
+
| 'agent_start' | 'agent_end'
|
|
122
|
+
| 'turn_start' | 'turn_end'
|
|
123
|
+
| 'tool_execution_start' | 'tool_execution_update' | 'tool_execution_end'
|
|
124
|
+
| 'steer_accepted' | 'steer_processed';
|
|
116
125
|
payload?: PersonaPacketPayload;
|
|
117
126
|
};
|
|
118
127
|
|
|
@@ -139,6 +148,66 @@ export type PersonaMessage = {
|
|
|
139
148
|
};
|
|
140
149
|
};
|
|
141
150
|
|
|
151
|
+
// ─── Nuovi tipi per il lifecycle dell'agente ─────────────────────────
|
|
152
|
+
|
|
153
|
+
export type AgentState = 'idle' | 'running' | 'cancelling';
|
|
154
|
+
|
|
155
|
+
export type AgentStartPayload = {
|
|
156
|
+
sessionId: string;
|
|
157
|
+
agentId: string;
|
|
158
|
+
timestamp: string;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export type AgentEndPayload = {
|
|
162
|
+
sessionId: string;
|
|
163
|
+
stopReason: 'completed' | 'cancelled' | 'error' | 'max_turns' | 'steered_completion';
|
|
164
|
+
totalTurns: number;
|
|
165
|
+
totalCredits?: number;
|
|
166
|
+
errorMessage?: string;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export type TurnStartPayload = {
|
|
170
|
+
turnNumber: number;
|
|
171
|
+
timestamp: string;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export type TurnEndPayload = {
|
|
175
|
+
turnNumber: number;
|
|
176
|
+
messageCount: number;
|
|
177
|
+
toolResults: Array<{ toolCallId: string; toolName: string; result: string }>;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export type ToolExecutionStartPayload = {
|
|
181
|
+
toolCallId: string;
|
|
182
|
+
toolName: string;
|
|
183
|
+
args: Record<string, unknown>;
|
|
184
|
+
timestamp: string;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export type ToolExecutionUpdatePayload = {
|
|
188
|
+
toolCallId: string;
|
|
189
|
+
partialResult: unknown;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export type ToolExecutionEndPayload = {
|
|
193
|
+
toolCallId: string;
|
|
194
|
+
result: unknown;
|
|
195
|
+
durationMs?: number;
|
|
196
|
+
error?: string;
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export type SteerAcceptedPayload = {
|
|
200
|
+
steerId: string;
|
|
201
|
+
text: string;
|
|
202
|
+
acceptedAt: string;
|
|
203
|
+
willProcessAfterTurn: number | null;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export type SteerProcessedPayload = {
|
|
207
|
+
steerId: string;
|
|
208
|
+
processedAtTurn: number;
|
|
209
|
+
};
|
|
210
|
+
|
|
142
211
|
export type ModelResponse = {
|
|
143
212
|
messages: PersonaMessage[];
|
|
144
213
|
};
|
|
@@ -283,4 +352,20 @@ export type PersonaConfig = PersonaBaseConfig &
|
|
|
283
352
|
* Enable file attachments (images, PDFs, text files) in the composer
|
|
284
353
|
*/
|
|
285
354
|
enableAttachments?: boolean;
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Enable steer commands during agent execution.
|
|
358
|
+
* When true, the user can send steer instructions while the agent is running.
|
|
359
|
+
*/
|
|
360
|
+
enableSteer?: boolean;
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Callback when a steer command is accepted by the server.
|
|
364
|
+
*/
|
|
365
|
+
onSteerAccepted?: (payload: SteerAcceptedPayload) => void;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Callback when a steer command is processed (injected into the agent context).
|
|
369
|
+
*/
|
|
370
|
+
onSteerProcessed?: (payload: SteerProcessedPayload) => void;
|
|
286
371
|
};
|