@datalayer/agent-runtimes 1.5.43 → 1.5.45
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/dist-embed/chunks/1f30a-DxwAt2Ba.js +1 -0
- package/dist-embed/chunks/1f310-AqUM9a8q.js +1 -0
- package/dist-embed/chunks/1f324-8MHLOhTj.js +1 -0
- package/dist-embed/chunks/1f326-YSBr3aXI.js +1 -0
- package/dist-embed/chunks/1f327-Ie-nQBNO.js +1 -0
- package/dist-embed/chunks/1f33e-Cfl2WM63.js +1 -0
- package/dist-embed/chunks/1f372-3XLDlXkE.js +1 -0
- package/dist-embed/chunks/1f373-BPUJt2bp.js +1 -0
- package/dist-embed/chunks/1f39b-CSNtbAXr.js +1 -0
- package/dist-embed/chunks/1f3ab-BUCNQHz1.js +1 -0
- package/dist-embed/chunks/1f41b-Dre1z0gP.js +1 -0
- package/dist-embed/chunks/1f441-D8yah4C4.js +1 -0
- package/dist-embed/chunks/1f465-CPle6Hjq.js +1 -0
- package/dist-embed/chunks/1f4b9-aacFyckt.js +1 -0
- package/dist-embed/chunks/1f4be-DPAcAU4R.js +1 -0
- package/dist-embed/chunks/1f4c1-Ch6aJ0cj.js +1 -0
- package/dist-embed/chunks/1f4c2-Dx7BKmLO.js +1 -0
- package/dist-embed/chunks/1f4c4-CNvjvC5k.js +1 -0
- package/dist-embed/chunks/1f4d3-GTxRgjv7.js +1 -0
- package/dist-embed/chunks/1f4d6-COOZY-Q_.js +1 -0
- package/dist-embed/chunks/1f4dc-BhoW4cjD.js +1 -0
- package/dist-embed/chunks/1f4e1-DezU8ow-.js +1 -0
- package/dist-embed/chunks/1f4e2-DsStZsI-.js +1 -0
- package/dist-embed/chunks/1f4e7-BBi7c_zx.js +1 -0
- package/dist-embed/chunks/1f500-D9xRo-IT.js +1 -0
- package/dist-embed/chunks/1f501-CzKeDPcw.js +1 -0
- package/dist-embed/chunks/1f504-BYIHMoDP.js +1 -0
- package/dist-embed/chunks/1f52d-2QxCLKhU.js +1 -0
- package/dist-embed/chunks/1f552-4d0Y0kn7.js +1 -0
- package/dist-embed/chunks/1f58b-Cp8F_HBg.js +1 -0
- package/dist-embed/chunks/1f5b2-D6BUULuh.js +1 -0
- package/dist-embed/chunks/1f5c3-DjkgJLaz.js +1 -0
- package/dist-embed/chunks/1f5d1-C4Kpe4HL.js +1 -0
- package/dist-embed/chunks/1f5dc-DyLO7aB9.js +1 -0
- package/dist-embed/chunks/1f604-CEekcaYE.js +1 -0
- package/dist-embed/chunks/1f917-CyyS9fgf.js +1 -0
- package/dist-embed/chunks/1f9d1-200d-2696-D9eSB-0q.js +1 -0
- package/dist-embed/chunks/1fa79-DHhIZbWB.js +1 -0
- package/dist-embed/chunks/1fa9d-DUI9e77m.js +1 -0
- package/dist-embed/chunks/1fa9f-10ALsMaD.js +1 -0
- package/dist-embed/chunks/23f3-ASjqFsbT.js +1 -0
- package/dist-embed/chunks/2611-BnnmLm-K.js +1 -0
- package/dist-embed/chunks/2702-CfLyp35r.js +1 -0
- package/dist-embed/chunks/270f-CLwo30mk.js +1 -0
- package/dist-embed/chunks/2728-Qg6be-6p.js +1 -0
- package/dist-embed/chunks/2b1c-Uc1aRwAf.js +1 -0
- package/dist-embed/chunks/{ChatView-kCmBbnDa.js → ChatView-Dy-IgGFL.js} +1 -1
- package/dist-embed/datalayer-app-main.js +111 -106
- package/lib/apps/plugins/chat/ChatView.js +2 -2
- package/lib/chat/ChatFloating.js +1 -1
- package/lib/chat/ChatInline.js +43 -22
- package/lib/chat/base/ChatBase.js +66 -10
- package/lib/chat/messages/ChatMessageList.d.ts +7 -1
- package/lib/chat/messages/ChatMessageList.js +8 -2
- package/lib/components/agents/AgentSummary.d.ts +2 -24
- package/lib/components/agents/AgentSummary.js +3 -19
- package/lib/hooks/useChat.js +110 -144
- package/lib/portable/activity.d.ts +78 -0
- package/lib/portable/activity.js +129 -0
- package/lib/portable/fixtures.d.ts +7 -0
- package/lib/portable/fixtures.js +50 -0
- package/lib/portable/index.d.ts +4 -1
- package/lib/portable/index.js +7 -1
- package/lib/portable/lifecycle.d.ts +77 -0
- package/lib/portable/lifecycle.js +135 -0
- package/lib/portable/runtimes.d.ts +68 -0
- package/lib/portable/runtimes.js +69 -0
- package/lib/protocols/VercelAIAdapter.js +11 -0
- package/lib/specs/voices.js +62 -0
- package/lib/types/chat.d.ts +10 -0
- package/lib/voice/ComposerVoice.js +12 -3
- package/lib/voice/VoiceMenu.d.ts +2 -1
- package/lib/voice/VoiceMenu.js +2 -2
- package/lib/voice/captions.d.ts +31 -0
- package/lib/voice/captions.js +156 -0
- package/lib/voice/chatVoice.d.ts +5 -2
- package/lib/voice/chatVoice.js +10 -4
- package/lib/voice/choices.d.ts +2 -0
- package/lib/voice/choices.js +6 -0
- package/lib/voice/handsFree.d.ts +12 -0
- package/lib/voice/handsFree.js +18 -0
- package/lib/voice/index.d.ts +1 -0
- package/lib/voice/index.js +1 -0
- package/lib/voice/sayAloud.d.ts +25 -0
- package/lib/voice/sayAloud.js +34 -0
- package/lib/voice/serverHearing.d.ts +63 -2
- package/lib/voice/serverHearing.js +311 -7
- package/lib/voice/settings.d.ts +12 -0
- package/lib/voice/settings.js +9 -0
- package/package.json +1 -1
- package/scripts/voice/measure.py +77 -17
package/lib/hooks/useChat.js
CHANGED
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { useCallback, useMemo } from 'react';
|
|
12
12
|
import { useChatStore } from '../stores';
|
|
13
|
-
import { createUserMessage,
|
|
14
|
-
import {
|
|
13
|
+
import { createUserMessage, generateMessageId } from '../types/messages';
|
|
14
|
+
import { isToolCallMessage } from '../utils';
|
|
15
|
+
import { BROWSER_LIMITS, emptyConversation, reduceConversation, withPrompt, } from '../portable/conversation';
|
|
16
|
+
import { conversationEventsOf, snapshotOfMessages, } from '../portable/adapterEvents';
|
|
17
|
+
import { conversationDrawer, emptyChatLayer, } from '../chat/base/conversationItems';
|
|
15
18
|
/**
|
|
16
19
|
* Main hook for chat functionality
|
|
17
20
|
*
|
|
@@ -56,13 +59,17 @@ export function useChat() {
|
|
|
56
59
|
const clearMessages = useChatStore(state => state.clearMessages);
|
|
57
60
|
const setLoading = useChatStore(state => state.setLoading);
|
|
58
61
|
const setError = useChatStore(state => state.setError);
|
|
62
|
+
const setMessages = useChatStore(state => state.setMessages);
|
|
59
63
|
const startStreaming = useChatStore(state => state.startStreaming);
|
|
60
|
-
const appendToStream = useChatStore(state => state.appendToStream);
|
|
61
64
|
const stopStreaming = useChatStore(state => state.stopStreaming);
|
|
62
65
|
const setSuggestions = useChatStore(state => state.setSuggestions);
|
|
63
66
|
const getTools = useChatStore(state => state.getTools);
|
|
64
67
|
/**
|
|
65
|
-
* Send a user message and get AI response
|
|
68
|
+
* Send a user message and get the AI's response. The conversation is
|
|
69
|
+
* `reduceConversation`'s (MOBILE M-030), as in ChatBase and on the phone:
|
|
70
|
+
* every adapter event read by `conversationEventsOf`, an inference
|
|
71
|
+
* provider's deltas as AG-UI text events, and the store's messages drawn
|
|
72
|
+
* from it.
|
|
66
73
|
*/
|
|
67
74
|
const sendMessage = useCallback(async (content, options) => {
|
|
68
75
|
// Check if we have a way to send messages
|
|
@@ -70,102 +77,95 @@ export function useChat() {
|
|
|
70
77
|
setError(new Error('No inference provider or protocol adapter configured.'));
|
|
71
78
|
return;
|
|
72
79
|
}
|
|
73
|
-
// Create and add user message
|
|
74
80
|
const userMessage = createUserMessage(content);
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
const draw = conversationDrawer(words => words);
|
|
82
|
+
let conversation = withPrompt(reduceConversation(emptyConversation(BROWSER_LIMITS), snapshotOfMessages(messages)), userMessage.id, content);
|
|
83
|
+
const show = (next) => {
|
|
84
|
+
conversation = next;
|
|
85
|
+
setMessages(draw(conversation, emptyChatLayer()).filter((item) => !isToolCallMessage(item)));
|
|
86
|
+
};
|
|
87
|
+
const reduce = (events) => {
|
|
88
|
+
if (events.length) {
|
|
89
|
+
show(events.reduce((kept, event) => reduceConversation(kept, event), conversation));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
/** An adapter's event, as the conversation reads it; its failure is the chat's error. */
|
|
93
|
+
const read = (event) => {
|
|
94
|
+
if (event.type === 'error') {
|
|
95
|
+
if (event.error) {
|
|
96
|
+
setError(event.error);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
reduce(conversationEventsOf(event, conversation));
|
|
102
|
+
}
|
|
103
|
+
catch (failure) {
|
|
104
|
+
setError(failure instanceof Error ? failure : new Error(String(failure)));
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
show(conversation);
|
|
77
108
|
setError(null);
|
|
78
109
|
setLoading(true);
|
|
110
|
+
startStreaming(generateMessageId());
|
|
79
111
|
try {
|
|
80
|
-
// Get registered tools
|
|
81
112
|
const tools = getTools();
|
|
82
|
-
// Merge options
|
|
83
113
|
const requestOptions = {
|
|
84
114
|
...options,
|
|
85
115
|
tools: tools.length > 0 ? tools : undefined,
|
|
86
116
|
};
|
|
87
|
-
//
|
|
117
|
+
// Every message so far, for context.
|
|
88
118
|
const allMessages = [...messages, userMessage];
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
const assistantMessage = createAssistantMessage('');
|
|
92
|
-
assistantMessage.id = assistantMessageId;
|
|
93
|
-
addMessage(assistantMessage);
|
|
94
|
-
// Stream the response
|
|
95
|
-
startStreaming(assistantMessageId);
|
|
96
|
-
// Use protocol adapter if available (for ag-ui, a2a, acp)
|
|
97
|
-
// Otherwise fall back to inference provider (for vercel-ai, OpenAI-compatible)
|
|
119
|
+
// A protocol adapter (ag-ui, a2a, acp) when there is one; else the
|
|
120
|
+
// inference provider (vercel-ai, OpenAI-compatible).
|
|
98
121
|
if (protocolAdapter) {
|
|
99
|
-
//
|
|
122
|
+
// The calls this run asks the page's tools for, run once it ends.
|
|
100
123
|
const pendingToolCalls = new Map();
|
|
101
|
-
let currentAssistantContent = '';
|
|
102
|
-
// Subscribe to adapter events
|
|
103
124
|
const unsubscribe = protocolAdapter.subscribe(event => {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
? event.message.content
|
|
110
|
-
: '';
|
|
111
|
-
currentAssistantContent =
|
|
112
|
-
sanitizeAssistantContent(rawContent);
|
|
113
|
-
// Update the assistant message with the current content
|
|
114
|
-
updateMessage(assistantMessageId, {
|
|
115
|
-
content: currentAssistantContent,
|
|
116
|
-
toolCalls: event.message.toolCalls,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
break;
|
|
120
|
-
case 'tool-call':
|
|
121
|
-
// Track tool call for execution after stream completes
|
|
122
|
-
if (event.toolCall) {
|
|
123
|
-
console.log('[useChat] Tool call received:', event.toolCall.toolCallId, event.toolCall.toolName);
|
|
124
|
-
pendingToolCalls.set(event.toolCall.toolCallId, {
|
|
125
|
-
toolName: event.toolCall.toolName,
|
|
126
|
-
args: event.toolCall.args,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
break;
|
|
130
|
-
case 'error':
|
|
131
|
-
if (event.error) {
|
|
132
|
-
setError(event.error);
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
125
|
+
if (event.type === 'tool-call' && event.toolCall) {
|
|
126
|
+
pendingToolCalls.set(event.toolCall.toolCallId, {
|
|
127
|
+
toolName: event.toolCall.toolName,
|
|
128
|
+
args: event.toolCall.args,
|
|
129
|
+
});
|
|
135
130
|
}
|
|
131
|
+
read(event);
|
|
136
132
|
});
|
|
137
133
|
try {
|
|
138
|
-
// Send the initial message
|
|
139
134
|
await protocolAdapter.sendMessage(userMessage, {
|
|
140
135
|
tools: requestOptions.tools,
|
|
141
136
|
messages: allMessages,
|
|
142
137
|
});
|
|
143
|
-
// After stream completes, execute any pending tool calls
|
|
144
138
|
if (pendingToolCalls.size > 0 && toolExecutor) {
|
|
145
|
-
console.log('[useChat] Executing', pendingToolCalls.size, 'tool calls');
|
|
146
|
-
// Execute tool calls and collect results
|
|
147
139
|
const toolResults = [];
|
|
148
140
|
for (const [toolCallId, { toolName, args }] of pendingToolCalls) {
|
|
149
|
-
console.log('[useChat] Executing tool:', toolName, 'with args:', args);
|
|
150
141
|
const result = await toolExecutor.execute({
|
|
151
142
|
toolCallId,
|
|
152
143
|
toolName,
|
|
153
144
|
args,
|
|
154
145
|
});
|
|
155
|
-
console.log('[useChat] Tool result:', result);
|
|
156
146
|
toolResults.push({
|
|
157
147
|
toolCallId,
|
|
158
148
|
toolName,
|
|
159
149
|
result: result.result,
|
|
160
150
|
isError: !result.success,
|
|
161
151
|
});
|
|
152
|
+
// The call answered, in the conversation.
|
|
153
|
+
reduce(conversationEventsOf({
|
|
154
|
+
type: 'tool-result',
|
|
155
|
+
toolResult: {
|
|
156
|
+
toolCallId,
|
|
157
|
+
success: result.success,
|
|
158
|
+
result: result.result,
|
|
159
|
+
...(result.success ? {} : { error: result.error }),
|
|
160
|
+
},
|
|
161
|
+
}, conversation));
|
|
162
162
|
}
|
|
163
|
-
//
|
|
164
|
-
//
|
|
163
|
+
// The run continued with the results: the assistant's calls,
|
|
164
|
+
// then one `tool` message per result (AG-UI).
|
|
165
165
|
const assistantWithToolCalls = {
|
|
166
|
-
id:
|
|
166
|
+
id: generateMessageId(),
|
|
167
167
|
role: 'assistant',
|
|
168
|
-
content:
|
|
168
|
+
content: '',
|
|
169
169
|
createdAt: new Date(),
|
|
170
170
|
toolCalls: Array.from(pendingToolCalls.entries()).map(([toolCallId, { toolName, args }]) => ({
|
|
171
171
|
type: 'tool-call',
|
|
@@ -175,7 +175,6 @@ export function useChat() {
|
|
|
175
175
|
status: 'completed',
|
|
176
176
|
})),
|
|
177
177
|
};
|
|
178
|
-
// Create tool result messages
|
|
179
178
|
const toolResultMessages = toolResults.map(tr => ({
|
|
180
179
|
id: generateMessageId(),
|
|
181
180
|
role: 'tool',
|
|
@@ -187,48 +186,14 @@ export function useChat() {
|
|
|
187
186
|
isError: tr.isError,
|
|
188
187
|
},
|
|
189
188
|
}));
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// Create new assistant message for the continuation
|
|
194
|
-
const continuationAssistantId = generateMessageId();
|
|
195
|
-
const continuationMessage = createAssistantMessage('');
|
|
196
|
-
continuationMessage.id = continuationAssistantId;
|
|
197
|
-
addMessage(continuationMessage);
|
|
198
|
-
startStreaming(continuationAssistantId);
|
|
199
|
-
// Subscribe to continuation events
|
|
200
|
-
const unsubscribeContinuation = protocolAdapter.subscribe(event => {
|
|
201
|
-
if (event.type === 'message' && event.message) {
|
|
202
|
-
const rawContent = typeof event.message.content === 'string'
|
|
203
|
-
? event.message.content
|
|
204
|
-
: '';
|
|
205
|
-
const content = sanitizeAssistantContent(rawContent);
|
|
206
|
-
updateMessage(continuationAssistantId, {
|
|
207
|
-
content,
|
|
208
|
-
toolCalls: event.message.toolCalls,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
else if (event.type === 'error' && event.error) {
|
|
212
|
-
setError(event.error);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
try {
|
|
216
|
-
// Build the full message history for continuation
|
|
217
|
-
const continuationMessages = [
|
|
189
|
+
await protocolAdapter.sendMessage(toolResultMessages[toolResultMessages.length - 1], {
|
|
190
|
+
tools: requestOptions.tools,
|
|
191
|
+
messages: [
|
|
218
192
|
...allMessages,
|
|
219
193
|
assistantWithToolCalls,
|
|
220
194
|
...toolResultMessages,
|
|
221
|
-
]
|
|
222
|
-
|
|
223
|
-
// AG-UI expects tool results as messages with role: "tool"
|
|
224
|
-
await protocolAdapter.sendMessage(toolResultMessages[toolResultMessages.length - 1], {
|
|
225
|
-
tools: requestOptions.tools,
|
|
226
|
-
messages: continuationMessages,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
finally {
|
|
230
|
-
unsubscribeContinuation();
|
|
231
|
-
}
|
|
195
|
+
],
|
|
196
|
+
});
|
|
232
197
|
}
|
|
233
198
|
}
|
|
234
199
|
finally {
|
|
@@ -236,47 +201,50 @@ export function useChat() {
|
|
|
236
201
|
}
|
|
237
202
|
}
|
|
238
203
|
else if (inferenceProvider) {
|
|
204
|
+
const answerId = generateMessageId();
|
|
205
|
+
reduce([
|
|
206
|
+
{ type: 'RUN_STARTED' },
|
|
207
|
+
{
|
|
208
|
+
type: 'TEXT_MESSAGE_START',
|
|
209
|
+
messageId: answerId,
|
|
210
|
+
role: 'assistant',
|
|
211
|
+
},
|
|
212
|
+
]);
|
|
239
213
|
const response = await inferenceProvider.streamMessage(allMessages, requestOptions, event => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
break;
|
|
253
|
-
case 'error':
|
|
254
|
-
if (event.error) {
|
|
255
|
-
setError(event.error);
|
|
256
|
-
}
|
|
257
|
-
break;
|
|
214
|
+
if (event.type === 'content-delta' &&
|
|
215
|
+
event.delta?.delta.content) {
|
|
216
|
+
reduce([
|
|
217
|
+
{
|
|
218
|
+
type: 'TEXT_MESSAGE_CONTENT',
|
|
219
|
+
messageId: answerId,
|
|
220
|
+
delta: event.delta.delta.content,
|
|
221
|
+
},
|
|
222
|
+
]);
|
|
223
|
+
}
|
|
224
|
+
else if (event.type === 'error' && event.error) {
|
|
225
|
+
setError(event.error);
|
|
258
226
|
}
|
|
259
227
|
});
|
|
260
|
-
//
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
228
|
+
// The answer as the provider gave it whole, then the run's end.
|
|
229
|
+
const said = conversationEventsOf({
|
|
230
|
+
type: 'message',
|
|
231
|
+
message: {
|
|
232
|
+
id: answerId,
|
|
233
|
+
role: 'assistant',
|
|
234
|
+
content: typeof response.message.content === 'string'
|
|
235
|
+
? response.message.content
|
|
236
|
+
: '',
|
|
237
|
+
},
|
|
238
|
+
}, conversation);
|
|
239
|
+
reduce([...said, { type: 'RUN_FINISHED' }]);
|
|
240
|
+
// The page's tools run the calls it asked for.
|
|
241
|
+
if (toolExecutor && response.message.toolCalls?.length) {
|
|
242
|
+
for (const toolCall of response.message.toolCalls) {
|
|
243
|
+
await toolExecutor.execute({
|
|
244
|
+
toolCallId: toolCall.toolCallId,
|
|
245
|
+
toolName: toolCall.toolName,
|
|
246
|
+
args: toolCall.args,
|
|
247
|
+
});
|
|
280
248
|
}
|
|
281
249
|
}
|
|
282
250
|
}
|
|
@@ -293,12 +261,10 @@ export function useChat() {
|
|
|
293
261
|
protocolAdapter,
|
|
294
262
|
toolExecutor,
|
|
295
263
|
messages,
|
|
296
|
-
|
|
297
|
-
updateMessage,
|
|
264
|
+
setMessages,
|
|
298
265
|
setLoading,
|
|
299
266
|
setError,
|
|
300
267
|
startStreaming,
|
|
301
|
-
appendToStream,
|
|
302
268
|
stopStreaming,
|
|
303
269
|
getTools,
|
|
304
270
|
]);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export type RecordKind = 'session'
|
|
2
|
+
/** A run began: in progress until it answers (R-15). Not a step. */
|
|
3
|
+
| 'run' | 'tool_call' | 'decision' | 'check' | 'approval' | 'output' | 'feedback'
|
|
4
|
+
/** A turn of a conversation: what was asked, what it answered (V-16). */
|
|
5
|
+
| 'turn'
|
|
6
|
+
/** What it did, step by step (P-16); a harness's session, turn and plan events (SHIELD §6.4). */
|
|
7
|
+
| 'step'
|
|
8
|
+
/** What one of its channels was sent, or why not: `{channel, event, delivery, detail}` (R-37). */
|
|
9
|
+
| 'notification';
|
|
10
|
+
/** One entry of an application's record. */
|
|
11
|
+
export type RecordEntry = {
|
|
12
|
+
uid: string;
|
|
13
|
+
appUid: string;
|
|
14
|
+
sessionUid: string;
|
|
15
|
+
deploymentUid: string;
|
|
16
|
+
version: number;
|
|
17
|
+
kind: RecordKind;
|
|
18
|
+
summary: string;
|
|
19
|
+
payload: Record<string, unknown>;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
/** `test` for a session its tests ran (the Evals engine's); empty for real use. */
|
|
22
|
+
purpose: string;
|
|
23
|
+
};
|
|
24
|
+
/** An entry as ai-agents answers with it. */
|
|
25
|
+
export declare function entryOf(raw: Record<string, unknown>): RecordEntry;
|
|
26
|
+
/** How the run an entry ends ended: `answered`, `stopped`, `gone`; `''` for any other entry. */
|
|
27
|
+
export declare function runEndOf(entry: RecordEntry): string;
|
|
28
|
+
/** An entry in words: its summary, or how its run ended when its words are not kept. */
|
|
29
|
+
export declare function entrySaid(entry: RecordEntry): string;
|
|
30
|
+
/** A session in a sentence: how many calls, what stopped it, how it ended. */
|
|
31
|
+
export declare function sessionSentence(entries: readonly RecordEntry[]): string;
|
|
32
|
+
/** A run its schedule plans, as the scheduler says (R-15). */
|
|
33
|
+
export type ScheduledRun = {
|
|
34
|
+
deploymentUid: string;
|
|
35
|
+
appUid: string;
|
|
36
|
+
version: number;
|
|
37
|
+
/** Which of its triggers, by position in its Appspec. */
|
|
38
|
+
position: number;
|
|
39
|
+
cron: string;
|
|
40
|
+
description: string;
|
|
41
|
+
/** When it runs next; empty when the scheduler does not say. */
|
|
42
|
+
nextAt: string;
|
|
43
|
+
};
|
|
44
|
+
/** What an application, or all of a person's, is doing (R-15): real use only, its tests read apart. */
|
|
45
|
+
export type AppActivity = {
|
|
46
|
+
/** Sessions whose latest run has not answered yet, newest first: their run's start. */
|
|
47
|
+
inProgress: RecordEntry[];
|
|
48
|
+
/** Its live deployments' next runs, soonest first. */
|
|
49
|
+
scheduled: ScheduledRun[];
|
|
50
|
+
/** Why its schedules could not be read; empty when they were. */
|
|
51
|
+
scheduledProblem: string;
|
|
52
|
+
/** Its other sessions, newest first: their opening entries. */
|
|
53
|
+
completed: RecordEntry[];
|
|
54
|
+
/** The offset of the next page of completed sessions; `null` after the last. */
|
|
55
|
+
nextOffset: number | null;
|
|
56
|
+
};
|
|
57
|
+
/** `/apps/activity`'s answer, read. */
|
|
58
|
+
export declare function activityOf(body: Record<string, unknown>): AppActivity;
|
|
59
|
+
/** Pages as one: in progress and scheduled from the first, completed from all, the last page's next offset. */
|
|
60
|
+
export declare function mergeActivityPages(pages: readonly AppActivity[]): AppActivity;
|
|
61
|
+
/** What Activity says, on every client (checked against the vocabulary). */
|
|
62
|
+
export declare const ACTIVITY_WORDS: {
|
|
63
|
+
readonly inProgress: "In progress";
|
|
64
|
+
readonly scheduled: "Scheduled";
|
|
65
|
+
readonly completed: "Completed";
|
|
66
|
+
readonly working: "Working since";
|
|
67
|
+
readonly next: "next";
|
|
68
|
+
readonly wakes: "It also starts on its own when something happens:";
|
|
69
|
+
readonly nothing: "Nothing yet.";
|
|
70
|
+
};
|
|
71
|
+
/** What happens that starts it on its own, in words (LOOP W-03): its event triggers. */
|
|
72
|
+
export declare const wakesOn: (triggers: ReadonlyArray<{
|
|
73
|
+
type: string;
|
|
74
|
+
event?: string;
|
|
75
|
+
description?: string;
|
|
76
|
+
}> | undefined) => string[];
|
|
77
|
+
/** A scheduled run in words: what it does, and on which version. */
|
|
78
|
+
export declare const scheduledSays: (run: ScheduledRun) => string;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* What an application is doing (LOOP R-15), as ai-agents answers
|
|
7
|
+
* `/api/ai-agents/v1/apps/activity`, and its record's entries
|
|
8
|
+
* (`/apps/sessions`, `/apps/records`): read and said the same by the Studio
|
|
9
|
+
* and Datalayer Mobile (MOBILE M-015b). Pure: each client fetches with its own
|
|
10
|
+
* authentication and hands the body here.
|
|
11
|
+
*
|
|
12
|
+
* @module portable/activity
|
|
13
|
+
*/
|
|
14
|
+
import { RUN_END_WORDS } from './records';
|
|
15
|
+
const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
16
|
+
const records = (value) => Array.isArray(value) ? value.filter(isRecord) : [];
|
|
17
|
+
const said = (value) => typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
18
|
+
/** An entry as ai-agents answers with it. */
|
|
19
|
+
export function entryOf(raw) {
|
|
20
|
+
return {
|
|
21
|
+
uid: said(raw.uid),
|
|
22
|
+
appUid: said(raw.app_uid),
|
|
23
|
+
sessionUid: said(raw.session_uid),
|
|
24
|
+
deploymentUid: said(raw.deployment_uid),
|
|
25
|
+
version: Number(raw.version) || 0,
|
|
26
|
+
kind: (said(raw.kind) || 'output'),
|
|
27
|
+
summary: said(raw.summary),
|
|
28
|
+
payload: isRecord(raw.payload) ? raw.payload : {},
|
|
29
|
+
createdAt: said(raw.created_at),
|
|
30
|
+
purpose: said(raw.purpose),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** How the run an entry ends ended: `answered`, `stopped`, `gone`; `''` for any other entry. */
|
|
34
|
+
export function runEndOf(entry) {
|
|
35
|
+
const status = entry.kind === 'output' ? entry.payload.status : undefined;
|
|
36
|
+
return typeof status === 'string' && status in RUN_END_WORDS ? status : '';
|
|
37
|
+
}
|
|
38
|
+
/** An entry in words: its summary, or how its run ended when its words are not kept. */
|
|
39
|
+
export function entrySaid(entry) {
|
|
40
|
+
return entry.summary || RUN_END_WORDS[runEndOf(entry)] || '';
|
|
41
|
+
}
|
|
42
|
+
const plural = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`;
|
|
43
|
+
/** A session in a sentence: how many calls, what stopped it, how it ended. */
|
|
44
|
+
export function sessionSentence(entries) {
|
|
45
|
+
const calls = entries.filter(entry => entry.kind === 'tool_call').length;
|
|
46
|
+
const checks = entries.filter(entry => entry.kind === 'check');
|
|
47
|
+
const ends = entries.map(runEndOf).filter(Boolean);
|
|
48
|
+
const answers = ends.filter(end => end === 'answered').length;
|
|
49
|
+
const parts = [];
|
|
50
|
+
parts.push(calls === 0 ? 'No tool called' : plural(calls, 'tool call'));
|
|
51
|
+
if (checks.length) {
|
|
52
|
+
parts.push(`${plural(checks.length, 'step')} a check did not let pass`);
|
|
53
|
+
}
|
|
54
|
+
const unsent = entries.filter(entry => entry.kind === 'notification' && entry.payload.delivery !== 'sent');
|
|
55
|
+
if (unsent.length) {
|
|
56
|
+
parts.push(`${plural(unsent.length, 'notification')} not sent`);
|
|
57
|
+
}
|
|
58
|
+
if (ends[ends.length - 1] === 'stopped') {
|
|
59
|
+
parts.push('stopped');
|
|
60
|
+
}
|
|
61
|
+
else if (answers) {
|
|
62
|
+
parts.push(plural(answers, 'answer'));
|
|
63
|
+
}
|
|
64
|
+
return `${parts.join(', ')}.`;
|
|
65
|
+
}
|
|
66
|
+
/** `/apps/activity`'s answer, read. */
|
|
67
|
+
export function activityOf(body) {
|
|
68
|
+
return {
|
|
69
|
+
inProgress: records(body.in_progress).map(entryOf),
|
|
70
|
+
scheduled: records(body.scheduled).map(raw => ({
|
|
71
|
+
deploymentUid: said(raw.deployment_uid),
|
|
72
|
+
appUid: said(raw.app_uid),
|
|
73
|
+
version: Number(raw.version) || 0,
|
|
74
|
+
position: Number(raw.position) || 0,
|
|
75
|
+
cron: said(raw.cron),
|
|
76
|
+
description: said(raw.description),
|
|
77
|
+
nextAt: said(raw.next_at),
|
|
78
|
+
})),
|
|
79
|
+
scheduledProblem: said(body.scheduled_problem),
|
|
80
|
+
completed: records(body.completed).map(entryOf),
|
|
81
|
+
nextOffset: typeof body.next_offset === 'number' ? body.next_offset : null,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** Pages as one: in progress and scheduled from the first, completed from all, the last page's next offset. */
|
|
85
|
+
export function mergeActivityPages(pages) {
|
|
86
|
+
const [first] = pages;
|
|
87
|
+
if (!first) {
|
|
88
|
+
return {
|
|
89
|
+
inProgress: [],
|
|
90
|
+
scheduled: [],
|
|
91
|
+
scheduledProblem: '',
|
|
92
|
+
completed: [],
|
|
93
|
+
nextOffset: null,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
const completed = pages
|
|
98
|
+
.flatMap(page => page.completed)
|
|
99
|
+
.filter(entry => {
|
|
100
|
+
const key = entry.uid || entry.sessionUid;
|
|
101
|
+
if (seen.has(key)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
seen.add(key);
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
...first,
|
|
109
|
+
completed,
|
|
110
|
+
nextOffset: pages[pages.length - 1].nextOffset,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** What Activity says, on every client (checked against the vocabulary). */
|
|
114
|
+
export const ACTIVITY_WORDS = {
|
|
115
|
+
inProgress: 'In progress',
|
|
116
|
+
scheduled: 'Scheduled',
|
|
117
|
+
completed: 'Completed',
|
|
118
|
+
working: 'Working since',
|
|
119
|
+
next: 'next',
|
|
120
|
+
wakes: 'It also starts on its own when something happens:',
|
|
121
|
+
nothing: 'Nothing yet.',
|
|
122
|
+
};
|
|
123
|
+
/** What happens that starts it on its own, in words (LOOP W-03): its event triggers. */
|
|
124
|
+
export const wakesOn = (triggers) => (triggers ?? [])
|
|
125
|
+
.filter(trigger => trigger.type === 'event' && (trigger.description || trigger.event))
|
|
126
|
+
.map(trigger => trigger.description ||
|
|
127
|
+
`When ${String(trigger.event).replace(/_/g, ' ')}`);
|
|
128
|
+
/** A scheduled run in words: what it does, and on which version. */
|
|
129
|
+
export const scheduledSays = (run) => `${run.description || 'Its schedule'} (${run.cron}) · version ${run.version}`;
|
|
@@ -18,3 +18,10 @@ export declare function shieldDisplayFixtures(): {
|
|
|
18
18
|
terminal: Record<string, unknown>;
|
|
19
19
|
};
|
|
20
20
|
export declare function conversationFixture(): ConversationState;
|
|
21
|
+
/**
|
|
22
|
+
* `GET /api/ai-agents/v1/apps/activity`'s answer for one application: a
|
|
23
|
+
* session in progress, a scheduled run, a completed one ended by Stop, and a
|
|
24
|
+
* next page — what the Studio's Activity and the phone's read alike (MOBILE
|
|
25
|
+
* M-015b).
|
|
26
|
+
*/
|
|
27
|
+
export declare function activityFixture(): Record<string, unknown>;
|
package/lib/portable/fixtures.js
CHANGED
|
@@ -118,3 +118,53 @@ export function conversationFixture() {
|
|
|
118
118
|
dropped: 3,
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* `GET /api/ai-agents/v1/apps/activity`'s answer for one application: a
|
|
123
|
+
* session in progress, a scheduled run, a completed one ended by Stop, and a
|
|
124
|
+
* next page — what the Studio's Activity and the phone's read alike (MOBILE
|
|
125
|
+
* M-015b).
|
|
126
|
+
*/
|
|
127
|
+
export function activityFixture() {
|
|
128
|
+
return {
|
|
129
|
+
success: true,
|
|
130
|
+
in_progress: [
|
|
131
|
+
{
|
|
132
|
+
uid: 'e-run',
|
|
133
|
+
app_uid: 'app-1',
|
|
134
|
+
session_uid: 's-2',
|
|
135
|
+
deployment_uid: 'd-1',
|
|
136
|
+
version: 3,
|
|
137
|
+
kind: 'run',
|
|
138
|
+
summary: 'Triage the inbox',
|
|
139
|
+
payload: {},
|
|
140
|
+
created_at: '2026-10-16T08:00:00Z',
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
scheduled: [
|
|
144
|
+
{
|
|
145
|
+
deployment_uid: 'd-1',
|
|
146
|
+
app_uid: 'app-1',
|
|
147
|
+
version: 3,
|
|
148
|
+
position: 0,
|
|
149
|
+
cron: '0 8 * * 1-5',
|
|
150
|
+
description: 'Every weekday morning',
|
|
151
|
+
next_at: '2026-10-17T08:00:00Z',
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
scheduled_problem: '',
|
|
155
|
+
completed: [
|
|
156
|
+
{
|
|
157
|
+
uid: 'e-out',
|
|
158
|
+
app_uid: 'app-1',
|
|
159
|
+
session_uid: 's-1',
|
|
160
|
+
deployment_uid: 'd-1',
|
|
161
|
+
version: 3,
|
|
162
|
+
kind: 'output',
|
|
163
|
+
summary: '',
|
|
164
|
+
payload: { status: 'stopped' },
|
|
165
|
+
created_at: '2026-10-15T08:00:00Z',
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
next_offset: 20,
|
|
169
|
+
};
|
|
170
|
+
}
|
package/lib/portable/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export { BROWSER_LIMITS, CUT_MARK, DEVICE_LIMITS, DIAGNOSTIC_LIMIT, emptyConvers
|
|
|
16
16
|
export { conversationEventsOf, sanitizeAssistantContent, snapshotOfMessages, type AdapterEvent, type SaidMessage, } from './adapterEvents';
|
|
17
17
|
export { PRESENCE_LINES, presenceOfConversation, presenceState, presenceToolOf, type PresenceState, type PresenceTool, } from './presence';
|
|
18
18
|
export { ANSWER_BELOW, ANSWER_COMPLETE, composerOf, droppedWords, ENDED_WORDS, messageLabel, OPENING_WORDS, presenceLine, showsQuestion, stepLine, TOOL_STATUS_WORDS, toolLabel, type ComposerProps, type ComposerState, type ConversationViewProps, type ErrorItem, type ErrorRowProps, type MessageItem, type MessageRowProps, type PresenceLineProps, type ProtocolDisplayProps, type QuestionCardProps, type StepItem, type StepRowProps, type ToolItem, type ToolRowProps, type TurnEndItem, type TurnEndRowProps, } from './views';
|
|
19
|
-
export { conversationFixture } from './fixtures';
|
|
19
|
+
export { activityFixture, conversationFixture } from './fixtures';
|
|
20
20
|
export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor, type ActionClass, type ProtocolDisplay, DIFF_LINES_LIMIT, SHIELD_DIFF_TYPE, SHIELD_TERMINAL_TYPE, languageOf, unifiedDiffLines, unifiedDiffText, type DiffLine, } from './displays';
|
|
21
21
|
export { shieldDisplayFixtures } from './fixtures';
|
|
22
22
|
export { isPapSignInTool, PAP_SIGN_IN_WORDS, readPapSignIn, safeSignInUrl, type PapSignInWaiting, } from './signIn';
|
|
@@ -24,5 +24,8 @@ export { DIRECT_MAX, DIRECT_WORDS, directAnswerOf, directAuthor, directClient, o
|
|
|
24
24
|
export { SIGN_IN_POLL_MS, SIGN_IN_STATUS_PATH, signInStandsOf, signInStatusClient, type SignInStands, } from './signInStatus';
|
|
25
25
|
export { emptyFields, fieldProblems, MAX_VALUE_LENGTH, PROTECTED_INPUT_WORDS, protectedInputClient, protectedRequestOf, ProtectedRequestGone, sentWords, submitBody, submitWords, ticketComplete, ticketOfPage, type ProtectedField, type ProtectedRequest, } from './protectedInput';
|
|
26
26
|
export { failureOf, otlpMetricsOf, RunMeter, type RunMeasure, type StreamFailure, } from './runMeasures';
|
|
27
|
+
export { ACTIVITY_WORDS, activityOf, entryOf, entrySaid, mergeActivityPages, runEndOf, scheduledSays, sessionSentence, wakesOn, type AppActivity, type RecordEntry, type RecordKind, type ScheduledRun, } from './activity';
|
|
28
|
+
export { deploymentEventLine, deploymentEventOf, notDeletedOf, resetBody, resetCountsOf, resetLines, stateChangeBody, stoppedWorkLine, stoppedWorkOf, type DeploymentEvent, type DeploymentEventKind, type DeploymentState, type ResetCounts, type StoppedWork, } from './lifecycle';
|
|
29
|
+
export { agentRuntimesOf, agentSummaryOfRuntime, CREATING_STATUSES, isAgentRuntime, isCreatingAgent, runtimeOf, type AgentRuntime, type AgentSummaryData, } from './runtimes';
|
|
27
30
|
export { agentRunClient, agentRuntimesUrlOf, appSessionsClient, RuntimeRefusal, type AgentRunClient, type AgentTurn, type AppSessionsClient, type RuntimeClientOptions, type SessionStart, type StreamOptions, } from './sessions';
|
|
28
31
|
export type { ChatMessage } from '../types/messages';
|