@vertesia/client 1.1.0-dev.20260327.125707Z → 1.1.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.
- package/lib/cjs/AnalyticsApi.js +3 -1
- package/lib/cjs/AnalyticsApi.js.map +1 -1
- package/lib/cjs/AppsApi.js +18 -2
- package/lib/cjs/AppsApi.js.map +1 -1
- package/lib/cjs/AuditTrailApi.js +6 -2
- package/lib/cjs/AuditTrailApi.js.map +1 -1
- package/lib/cjs/GroupsApi.js +8 -0
- package/lib/cjs/GroupsApi.js.map +1 -1
- package/lib/cjs/InteractionsApi.js.map +1 -1
- package/lib/cjs/OAuthClientsApi.js +25 -0
- package/lib/cjs/OAuthClientsApi.js.map +1 -0
- package/lib/cjs/OAuthGrantsApi.js +30 -0
- package/lib/cjs/OAuthGrantsApi.js.map +1 -0
- package/lib/cjs/OAuthProvidersApi.js +43 -0
- package/lib/cjs/OAuthProvidersApi.js.map +1 -0
- package/lib/cjs/OAuthServerApi.js +22 -0
- package/lib/cjs/OAuthServerApi.js.map +1 -0
- package/lib/cjs/RemoteMcpConnectionsApi.js +38 -0
- package/lib/cjs/RemoteMcpConnectionsApi.js.map +1 -0
- package/lib/cjs/RunsApi.js +4 -12
- package/lib/cjs/RunsApi.js.map +1 -1
- package/lib/cjs/client.js +48 -45
- package/lib/cjs/client.js.map +1 -1
- package/lib/cjs/index.js +14 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/store/AgentsApi.js +30 -6
- package/lib/cjs/store/AgentsApi.js.map +1 -1
- package/lib/cjs/store/CostApi.js +56 -0
- package/lib/cjs/store/CostApi.js.map +1 -0
- package/lib/cjs/store/IndexingApi.js +94 -30
- package/lib/cjs/store/IndexingApi.js.map +1 -1
- package/lib/cjs/store/ObjectsApi.js +3 -2
- package/lib/cjs/store/ObjectsApi.js.map +1 -1
- package/lib/cjs/store/WorkflowsApi.js +258 -236
- package/lib/cjs/store/WorkflowsApi.js.map +1 -1
- package/lib/cjs/store/client.js +2 -0
- package/lib/cjs/store/client.js.map +1 -1
- package/lib/esm/AnalyticsApi.js +3 -1
- package/lib/esm/AnalyticsApi.js.map +1 -1
- package/lib/esm/AppsApi.js +18 -2
- package/lib/esm/AppsApi.js.map +1 -1
- package/lib/esm/AuditTrailApi.js +6 -2
- package/lib/esm/AuditTrailApi.js.map +1 -1
- package/lib/esm/GroupsApi.js +8 -0
- package/lib/esm/GroupsApi.js.map +1 -1
- package/lib/esm/InteractionsApi.js.map +1 -1
- package/lib/esm/OAuthClientsApi.js +22 -0
- package/lib/esm/OAuthClientsApi.js.map +1 -0
- package/lib/esm/OAuthGrantsApi.js +27 -0
- package/lib/esm/OAuthGrantsApi.js.map +1 -0
- package/lib/esm/OAuthProvidersApi.js +40 -0
- package/lib/esm/OAuthProvidersApi.js.map +1 -0
- package/lib/esm/OAuthServerApi.js +19 -0
- package/lib/esm/OAuthServerApi.js.map +1 -0
- package/lib/esm/RemoteMcpConnectionsApi.js +35 -0
- package/lib/esm/RemoteMcpConnectionsApi.js.map +1 -0
- package/lib/esm/RunsApi.js +4 -12
- package/lib/esm/RunsApi.js.map +1 -1
- package/lib/esm/client.js +48 -45
- package/lib/esm/client.js.map +1 -1
- package/lib/esm/index.js +5 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/store/AgentsApi.js +30 -6
- package/lib/esm/store/AgentsApi.js.map +1 -1
- package/lib/esm/store/CostApi.js +52 -0
- package/lib/esm/store/CostApi.js.map +1 -0
- package/lib/esm/store/IndexingApi.js +94 -30
- package/lib/esm/store/IndexingApi.js.map +1 -1
- package/lib/esm/store/ObjectsApi.js +3 -2
- package/lib/esm/store/ObjectsApi.js.map +1 -1
- package/lib/esm/store/WorkflowsApi.js +258 -236
- package/lib/esm/store/WorkflowsApi.js.map +1 -1
- package/lib/esm/store/client.js +2 -0
- package/lib/esm/store/client.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/AnalyticsApi.d.ts +1 -1
- package/lib/types/AnalyticsApi.d.ts.map +1 -1
- package/lib/types/AppsApi.d.ts +20 -2
- package/lib/types/AppsApi.d.ts.map +1 -1
- package/lib/types/AuditTrailApi.d.ts.map +1 -1
- package/lib/types/GroupsApi.d.ts +6 -0
- package/lib/types/GroupsApi.d.ts.map +1 -1
- package/lib/types/InteractionsApi.d.ts +2 -0
- package/lib/types/InteractionsApi.d.ts.map +1 -1
- package/lib/types/OAuthClientsApi.d.ts +12 -0
- package/lib/types/OAuthClientsApi.d.ts.map +1 -0
- package/lib/types/OAuthGrantsApi.d.ts +11 -0
- package/lib/types/OAuthGrantsApi.d.ts.map +1 -0
- package/lib/types/OAuthProvidersApi.d.ts +22 -0
- package/lib/types/OAuthProvidersApi.d.ts.map +1 -0
- package/lib/types/OAuthServerApi.d.ts +10 -0
- package/lib/types/OAuthServerApi.d.ts.map +1 -0
- package/lib/types/ProjectsApi.d.ts +2 -2
- package/lib/types/ProjectsApi.d.ts.map +1 -1
- package/lib/types/RemoteMcpConnectionsApi.d.ts +13 -0
- package/lib/types/RemoteMcpConnectionsApi.d.ts.map +1 -0
- package/lib/types/RunsApi.d.ts +4 -16
- package/lib/types/RunsApi.d.ts.map +1 -1
- package/lib/types/client.d.ts +15 -9
- package/lib/types/client.d.ts.map +1 -1
- package/lib/types/index.d.ts +5 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/store/AgentsApi.d.ts +27 -47
- package/lib/types/store/AgentsApi.d.ts.map +1 -1
- package/lib/types/store/CostApi.d.ts +35 -0
- package/lib/types/store/CostApi.d.ts.map +1 -0
- package/lib/types/store/IndexingApi.d.ts +48 -23
- package/lib/types/store/IndexingApi.d.ts.map +1 -1
- package/lib/types/store/ObjectsApi.d.ts +7 -2
- package/lib/types/store/ObjectsApi.d.ts.map +1 -1
- package/lib/types/store/WorkflowsApi.d.ts +84 -34
- package/lib/types/store/WorkflowsApi.d.ts.map +1 -1
- package/lib/types/store/client.d.ts +2 -0
- package/lib/types/store/client.d.ts.map +1 -1
- package/lib/vertesia-client.js +1 -1
- package/lib/vertesia-client.js.map +1 -1
- package/package.json +8 -8
- package/src/AnalyticsApi.ts +2 -1
- package/src/AppsApi.ts +26 -3
- package/src/AuditTrailApi.ts +3 -1
- package/src/GroupsApi.ts +9 -0
- package/src/InteractionsApi.ts +6 -1
- package/src/OAuthClientsApi.ts +33 -0
- package/src/OAuthGrantsApi.ts +42 -0
- package/src/OAuthProvidersApi.ts +59 -0
- package/src/OAuthServerApi.ts +30 -0
- package/src/ProjectsApi.ts +3 -3
- package/src/RemoteMcpConnectionsApi.ts +53 -0
- package/src/RunsApi.ts +6 -13
- package/src/client.test.ts +9 -11
- package/src/client.ts +83 -55
- package/src/index.ts +5 -0
- package/src/store/AgentsApi.ts +62 -43
- package/src/store/CostApi.ts +67 -0
- package/src/store/IndexingApi.ts +124 -35
- package/src/store/ObjectsApi.ts +7 -3
- package/src/store/WorkflowsApi.ts +327 -252
- package/src/store/client.ts +2 -0
- package/lib/cjs/MCPOAuthApi.js +0 -69
- package/lib/cjs/MCPOAuthApi.js.map +0 -1
- package/lib/cjs/OAuthAppsApi.js +0 -72
- package/lib/cjs/OAuthAppsApi.js.map +0 -1
- package/lib/esm/MCPOAuthApi.js +0 -66
- package/lib/esm/MCPOAuthApi.js.map +0 -1
- package/lib/esm/OAuthAppsApi.js +0 -69
- package/lib/esm/OAuthAppsApi.js.map +0 -1
- package/lib/types/MCPOAuthApi.d.ts +0 -50
- package/lib/types/MCPOAuthApi.d.ts.map +0 -1
- package/lib/types/OAuthAppsApi.d.ts +0 -51
- package/lib/types/OAuthAppsApi.d.ts.map +0 -1
- package/src/MCPOAuthApi.ts +0 -74
- package/src/OAuthAppsApi.ts +0 -87
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
import { ApiTopic, ClientBase } from "@vertesia/api-fetch-client";
|
|
2
2
|
import {
|
|
3
|
-
ActiveWorkstreamsQueryResult,
|
|
4
3
|
ActivityCatalog,
|
|
4
|
+
AgentEvent,
|
|
5
5
|
AgentMessage,
|
|
6
6
|
AgentMessageType,
|
|
7
7
|
CompactMessage,
|
|
8
8
|
CreateWorkflowRulePayload,
|
|
9
9
|
DSLWorkflowDefinition,
|
|
10
10
|
DSLWorkflowSpec,
|
|
11
|
+
ErrorAnalyticsResponse,
|
|
11
12
|
ExecuteWorkflowPayload,
|
|
13
|
+
FirstResponseBehaviorAnalyticsResponse,
|
|
14
|
+
LatencyAnalyticsResponse,
|
|
12
15
|
ListWorkflowInteractionsResponse,
|
|
13
16
|
ListWorkflowRunsPayload,
|
|
14
17
|
ListWorkflowRunsResponse,
|
|
15
18
|
parseMessage,
|
|
16
19
|
toAgentMessage,
|
|
20
|
+
PromptSizeAnalyticsResponse,
|
|
21
|
+
RunsByAgentAnalyticsResponse,
|
|
22
|
+
TimeToFirstResponseAnalyticsResponse,
|
|
23
|
+
TokenUsageAnalyticsResponse,
|
|
24
|
+
ToolAnalyticsResponse,
|
|
25
|
+
ToolParameterAnalyticsResponse,
|
|
26
|
+
TopPrincipalsAnalyticsResponse,
|
|
17
27
|
WebSocketClientMessage,
|
|
18
28
|
WebSocketServerMessage,
|
|
19
29
|
WorkflowActionPayload,
|
|
30
|
+
WorkflowAnalyticsFilterOptionsResponse,
|
|
31
|
+
WorkflowAnalyticsSummaryQuery,
|
|
32
|
+
WorkflowAnalyticsSummaryResponse,
|
|
33
|
+
WorkflowAnalyticsTimeSeriesQuery,
|
|
20
34
|
WorkflowDefinitionRef,
|
|
21
35
|
WorkflowRule,
|
|
22
36
|
WorkflowRuleItem,
|
|
23
37
|
WorkflowRunWithDetails,
|
|
38
|
+
WorkflowToolParametersQuery,
|
|
24
39
|
} from "@vertesia/common";
|
|
25
40
|
import { VertesiaClient } from "../client.js";
|
|
26
41
|
import { EventSourceProvider } from "../execute.js";
|
|
@@ -102,59 +117,6 @@ export class WorkflowsApi extends ApiTopic {
|
|
|
102
117
|
return this.get(`/runs/${workflowId}/${runId}/query/${queryName}`);
|
|
103
118
|
}
|
|
104
119
|
|
|
105
|
-
// ========================================================================
|
|
106
|
-
// Workstream helpers
|
|
107
|
-
// ========================================================================
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* List active workstreams for a running conversation workflow.
|
|
111
|
-
* Each entry includes `child_workflow_id` / `child_workflow_run_id` which
|
|
112
|
-
* can be passed to `retrieveMessages` or `streamMessages` to fetch the
|
|
113
|
-
* child's own message stream.
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* ```ts
|
|
117
|
-
* const { running } = await client.workflows.getActiveWorkstreams(wfId, runId);
|
|
118
|
-
* for (const ws of running) {
|
|
119
|
-
* const msgs = await client.workflows.retrieveWorkstreamMessages(ws);
|
|
120
|
-
* console.log(ws.workstream_id, msgs.length, 'messages');
|
|
121
|
-
* }
|
|
122
|
-
* ```
|
|
123
|
-
*/
|
|
124
|
-
getActiveWorkstreams(workflowId: string, runId: string): Promise<ActiveWorkstreamsQueryResult> {
|
|
125
|
-
return this.query<ActiveWorkstreamsQueryResult>(workflowId, runId, 'ActiveWorkstreams');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Retrieve historical messages for a specific workstream (child workflow).
|
|
130
|
-
* Convenience wrapper — extracts child IDs from an `ActiveWorkstreamEntry`.
|
|
131
|
-
*/
|
|
132
|
-
retrieveWorkstreamMessages(
|
|
133
|
-
workstream: { child_workflow_id: string; child_workflow_run_id?: string },
|
|
134
|
-
since?: number,
|
|
135
|
-
): Promise<AgentMessage[]> {
|
|
136
|
-
if (!workstream.child_workflow_run_id) {
|
|
137
|
-
return Promise.resolve([]);
|
|
138
|
-
}
|
|
139
|
-
return this.retrieveMessages(workstream.child_workflow_id, workstream.child_workflow_run_id, since);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Stream messages for a specific workstream (child workflow) in real-time.
|
|
144
|
-
* Convenience wrapper — extracts child IDs from an `ActiveWorkstreamEntry`.
|
|
145
|
-
*/
|
|
146
|
-
streamWorkstreamMessages(
|
|
147
|
-
workstream: { child_workflow_id: string; child_workflow_run_id?: string },
|
|
148
|
-
onMessage?: (message: AgentMessage, exitFn?: (payload: unknown) => void) => void,
|
|
149
|
-
since?: number,
|
|
150
|
-
signal?: AbortSignal,
|
|
151
|
-
): Promise<unknown> {
|
|
152
|
-
if (!workstream.child_workflow_run_id) {
|
|
153
|
-
return Promise.resolve(null);
|
|
154
|
-
}
|
|
155
|
-
return this.streamMessages(workstream.child_workflow_id, workstream.child_workflow_run_id, onMessage, since, signal);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
120
|
execute(
|
|
159
121
|
name: string,
|
|
160
122
|
payload: ExecuteWorkflowPayload = {},
|
|
@@ -190,247 +152,199 @@ export class WorkflowsApi extends ApiTopic {
|
|
|
190
152
|
* This approach provides better performance for conversations with large historical messages
|
|
191
153
|
* since HTTP responses are compressed while SSE streams cannot be compressed.
|
|
192
154
|
*/
|
|
193
|
-
async streamMessages(
|
|
194
|
-
workflowId: string,
|
|
195
|
-
runId: string,
|
|
196
|
-
onMessage?: (message: AgentMessage, exitFn?: (payload: unknown) => void) => void,
|
|
197
|
-
since?: number,
|
|
198
|
-
signal?: AbortSignal,
|
|
199
|
-
): Promise<unknown> {
|
|
155
|
+
async streamMessages(workflowId: string, runId: string, onMessage?: (message: AgentMessage, exitFn?: (payload: unknown) => void) => void, since?: number): Promise<unknown> {
|
|
200
156
|
return new Promise<unknown>((resolve, reject) => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
let interval: NodeJS.Timeout | null = null;
|
|
207
|
-
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
208
|
-
let abortHandler: (() => void) | null = null;
|
|
209
|
-
|
|
210
|
-
const maxReconnectAttempts = 10;
|
|
211
|
-
const baseDelay = 1000; // 1 second base delay
|
|
212
|
-
const maxDelay = 30000; // 30 seconds max delay
|
|
213
|
-
|
|
214
|
-
const calculateBackoffDelay = (attempts: number): number => {
|
|
215
|
-
const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempts), maxDelay);
|
|
216
|
-
// Add jitter to prevent thundering herd
|
|
217
|
-
const jitter = Math.random() * 0.1 * exponentialDelay;
|
|
218
|
-
return exponentialDelay + jitter;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const cleanup = () => {
|
|
222
|
-
if (reconnectTimer) {
|
|
223
|
-
clearTimeout(reconnectTimer);
|
|
224
|
-
reconnectTimer = null;
|
|
225
|
-
}
|
|
226
|
-
if (interval) {
|
|
227
|
-
clearInterval(interval);
|
|
228
|
-
interval = null;
|
|
229
|
-
}
|
|
230
|
-
if (currentSse) {
|
|
231
|
-
currentSse.close();
|
|
232
|
-
currentSse = null;
|
|
233
|
-
}
|
|
234
|
-
if (signal && abortHandler) {
|
|
235
|
-
signal.removeEventListener("abort", abortHandler);
|
|
236
|
-
abortHandler = null;
|
|
237
|
-
}
|
|
238
|
-
};
|
|
157
|
+
let reconnectAttempts = 0;
|
|
158
|
+
let lastMessageTimestamp = since || 0;
|
|
159
|
+
let isClosed = false;
|
|
160
|
+
let currentSse: EventSource | null = null;
|
|
161
|
+
let interval: ReturnType<typeof setInterval> | null = null;
|
|
239
162
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
cleanup();
|
|
244
|
-
resolve(payload);
|
|
245
|
-
}
|
|
246
|
-
};
|
|
163
|
+
const maxReconnectAttempts = 10;
|
|
164
|
+
const baseDelay = 1000; // 1 second base delay
|
|
165
|
+
const maxDelay = 30000; // 30 seconds max delay
|
|
247
166
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
167
|
+
const calculateBackoffDelay = (attempts: number): number => {
|
|
168
|
+
const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempts), maxDelay);
|
|
169
|
+
// Add jitter to prevent thundering herd
|
|
170
|
+
const jitter = Math.random() * 0.1 * exponentialDelay;
|
|
171
|
+
return exponentialDelay + jitter;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const cleanup = () => {
|
|
175
|
+
if (interval) {
|
|
176
|
+
clearInterval(interval);
|
|
177
|
+
interval = null;
|
|
178
|
+
}
|
|
179
|
+
if (currentSse) {
|
|
180
|
+
currentSse.close();
|
|
181
|
+
currentSse = null;
|
|
260
182
|
}
|
|
183
|
+
};
|
|
261
184
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (isClosed) return;
|
|
270
|
-
// Update timestamp for SSE connection
|
|
271
|
-
lastMessageTimestamp = Math.max(lastMessageTimestamp, msg.timestamp || 0);
|
|
185
|
+
const exit = (payload: unknown) => {
|
|
186
|
+
if (!isClosed) {
|
|
187
|
+
isClosed = true;
|
|
188
|
+
cleanup();
|
|
189
|
+
resolve(payload);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
272
192
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
if (isClosed) return;
|
|
193
|
+
// 2. Connect to SSE for real-time updates only (skipHistory=true)
|
|
194
|
+
const setupStream = async (isReconnect: boolean = false) => {
|
|
195
|
+
if (isClosed) return;
|
|
278
196
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
197
|
+
try {
|
|
198
|
+
const EventSourceImpl = await EventSourceProvider();
|
|
199
|
+
const client = this.client as VertesiaClient;
|
|
200
|
+
const streamUrl = new URL(client.workflows.baseUrl + `/runs/${workflowId}/${runId}/stream`);
|
|
201
|
+
|
|
202
|
+
// Use the timestamp of the last received message for reconnection
|
|
203
|
+
if (lastMessageTimestamp > 0) {
|
|
204
|
+
streamUrl.searchParams.set("since", lastMessageTimestamp.toString());
|
|
288
205
|
}
|
|
289
|
-
} catch (err) {
|
|
290
|
-
if (isClosed) return;
|
|
291
|
-
console.warn("Failed to fetch historical messages, continuing with SSE:", err);
|
|
292
|
-
// Continue to SSE - it will send historical if skipHistory is not set
|
|
293
|
-
}
|
|
294
206
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (isClosed) return;
|
|
207
|
+
// Skip historical messages - we already fetched them via GET /updates
|
|
208
|
+
streamUrl.searchParams.set("skipHistory", "true");
|
|
298
209
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
210
|
+
const bearerToken = client._auth ? await client._auth() : undefined;
|
|
211
|
+
if (!bearerToken) {
|
|
212
|
+
reject(new Error("No auth token available"));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
304
215
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
streamUrl.searchParams.set("since", lastMessageTimestamp.toString());
|
|
308
|
-
}
|
|
216
|
+
const token = bearerToken.split(" ")[1];
|
|
217
|
+
streamUrl.searchParams.set("access_token", token);
|
|
309
218
|
|
|
310
|
-
|
|
311
|
-
|
|
219
|
+
if (isReconnect) {
|
|
220
|
+
console.log(`Reconnecting to SSE stream for run ${runId} (attempt ${reconnectAttempts + 1}/${maxReconnectAttempts})`);
|
|
221
|
+
}
|
|
312
222
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (!bearerToken) {
|
|
316
|
-
isClosed = true;
|
|
317
|
-
cleanup();
|
|
318
|
-
reject(new Error("No auth token available"));
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
223
|
+
const sse = new EventSourceImpl(streamUrl.href);
|
|
224
|
+
currentSse = sse;
|
|
321
225
|
|
|
322
|
-
|
|
323
|
-
|
|
226
|
+
// Prevent Node from exiting prematurely
|
|
227
|
+
interval = setInterval(() => { }, 1000);
|
|
324
228
|
|
|
229
|
+
sse.onopen = () => {
|
|
325
230
|
if (isReconnect) {
|
|
326
|
-
console.log(`
|
|
231
|
+
console.log(`Successfully reconnected to SSE stream for run ${runId}`);
|
|
327
232
|
}
|
|
233
|
+
// Reset reconnect attempts on successful connection
|
|
234
|
+
reconnectAttempts = 0;
|
|
235
|
+
};
|
|
328
236
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
237
|
+
sse.onmessage = (ev: MessageEvent) => {
|
|
238
|
+
if (!ev.data || ev.data.startsWith(":")) {
|
|
239
|
+
console.log("Received comment or heartbeat; ignoring it.: ", ev.data);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
332
242
|
|
|
333
|
-
|
|
334
|
-
|
|
243
|
+
try {
|
|
244
|
+
// Parse message using parseMessage() which handles both compact and legacy formats
|
|
245
|
+
const compactMessage = parseMessage(ev.data);
|
|
335
246
|
|
|
336
|
-
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
reconnectAttempts = 0;
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
sse.onmessage = (ev: MessageEvent) => {
|
|
345
|
-
if (isClosed) return;
|
|
346
|
-
if (!ev.data || ev.data.startsWith(":")) {
|
|
347
|
-
return;
|
|
247
|
+
// Update last message timestamp for reconnection (use ts field or current time)
|
|
248
|
+
if (compactMessage.ts) {
|
|
249
|
+
lastMessageTimestamp = Math.max(lastMessageTimestamp, compactMessage.ts);
|
|
250
|
+
} else {
|
|
251
|
+
lastMessageTimestamp = Date.now();
|
|
348
252
|
}
|
|
349
253
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if (compactMessage.ts) {
|
|
356
|
-
lastMessageTimestamp = Math.max(lastMessageTimestamp, compactMessage.ts);
|
|
357
|
-
} else {
|
|
358
|
-
lastMessageTimestamp = Date.now();
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Convert to AgentMessage for consumers (they shouldn't need to know about compact format)
|
|
362
|
-
if (onMessage) {
|
|
363
|
-
const agentMessage = toAgentMessage(compactMessage, runId);
|
|
364
|
-
onMessage(agentMessage, exit);
|
|
365
|
-
}
|
|
254
|
+
// Convert to AgentMessage for consumers (they shouldn't need to know about compact format)
|
|
255
|
+
if (onMessage) {
|
|
256
|
+
const agentMessage = toAgentMessage(compactMessage, runId);
|
|
257
|
+
onMessage(agentMessage, exit);
|
|
258
|
+
}
|
|
366
259
|
|
|
367
|
-
|
|
368
|
-
|
|
260
|
+
// Get workstream ID (defaults to 'main' if not set)
|
|
261
|
+
const workstreamId = compactMessage.w || 'main';
|
|
369
262
|
|
|
370
|
-
|
|
371
|
-
|
|
263
|
+
const streamIsOver = compactMessage.t === AgentMessageType.TERMINATED ||
|
|
264
|
+
(compactMessage.t === AgentMessageType.COMPLETE && workstreamId === 'main');
|
|
372
265
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
266
|
+
// Only close the stream when the main workstream completes or terminates
|
|
267
|
+
if (streamIsOver) {
|
|
268
|
+
console.log("Closing stream due to COMPLETE message from main workstream");
|
|
269
|
+
if (!isClosed) {
|
|
270
|
+
isClosed = true;
|
|
271
|
+
cleanup();
|
|
272
|
+
resolve(null);
|
|
379
273
|
}
|
|
380
|
-
}
|
|
381
|
-
console.
|
|
274
|
+
} else if (compactMessage.t === AgentMessageType.COMPLETE) {
|
|
275
|
+
console.log(`Received COMPLETE message from non-main workstream: ${workstreamId}, keeping stream open`);
|
|
382
276
|
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
277
|
+
} catch (err) {
|
|
278
|
+
console.error("Failed to parse SSE message:", err, ev.data);
|
|
279
|
+
}
|
|
280
|
+
};
|
|
387
281
|
|
|
388
|
-
|
|
389
|
-
|
|
282
|
+
sse.onerror = (err: any) => {
|
|
283
|
+
if (isClosed) return;
|
|
390
284
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
const delay = calculateBackoffDelay(reconnectAttempts);
|
|
394
|
-
console.log(`Attempting to reconnect in ${delay}ms (attempt ${reconnectAttempts + 1}/${maxReconnectAttempts})`);
|
|
285
|
+
console.warn(`SSE stream error for run ${runId}:`, err);
|
|
286
|
+
cleanup();
|
|
395
287
|
|
|
396
|
-
|
|
397
|
-
reconnectTimer = setTimeout(() => {
|
|
398
|
-
reconnectTimer = null;
|
|
399
|
-
if (!isClosed) {
|
|
400
|
-
setupStream(true);
|
|
401
|
-
}
|
|
402
|
-
}, delay);
|
|
403
|
-
} else {
|
|
404
|
-
console.error(`Failed to reconnect to SSE stream for run ${runId} after ${maxReconnectAttempts} attempts`);
|
|
405
|
-
isClosed = true;
|
|
406
|
-
cleanup();
|
|
407
|
-
reject(new Error(`SSE connection failed after ${maxReconnectAttempts} reconnection attempts`));
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
|
-
} catch (err) {
|
|
411
|
-
if (isClosed) return;
|
|
412
|
-
console.error("Error setting up SSE stream:", err);
|
|
288
|
+
// Check if we should attempt reconnection
|
|
413
289
|
if (reconnectAttempts < maxReconnectAttempts) {
|
|
414
290
|
const delay = calculateBackoffDelay(reconnectAttempts);
|
|
291
|
+
console.log(`Attempting to reconnect in ${delay}ms (attempt ${reconnectAttempts + 1}/${maxReconnectAttempts})`);
|
|
292
|
+
|
|
415
293
|
reconnectAttempts++;
|
|
416
|
-
|
|
417
|
-
reconnectTimer = null;
|
|
294
|
+
setTimeout(() => {
|
|
418
295
|
if (!isClosed) {
|
|
419
296
|
setupStream(true);
|
|
420
297
|
}
|
|
421
298
|
}, delay);
|
|
422
299
|
} else {
|
|
300
|
+
console.error(`Failed to reconnect to SSE stream for run ${runId} after ${maxReconnectAttempts} attempts`);
|
|
423
301
|
isClosed = true;
|
|
424
|
-
|
|
425
|
-
|
|
302
|
+
reject(new Error(`SSE connection failed after ${maxReconnectAttempts} reconnection attempts`));
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
} catch (err) {
|
|
306
|
+
console.error("Error setting up SSE stream:", err);
|
|
307
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
308
|
+
const delay = calculateBackoffDelay(reconnectAttempts);
|
|
309
|
+
reconnectAttempts++;
|
|
310
|
+
setTimeout(() => {
|
|
311
|
+
if (!isClosed) {
|
|
312
|
+
setupStream(true);
|
|
313
|
+
}
|
|
314
|
+
}, delay);
|
|
315
|
+
} else {
|
|
316
|
+
reject(err);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Start the async setup: fetch historical messages, then connect SSE
|
|
322
|
+
const init = async () => {
|
|
323
|
+
// 1. Fetch historical messages via GET /updates (gzip-compressed if > 3KB)
|
|
324
|
+
try {
|
|
325
|
+
const historical = await this.retrieveMessages(workflowId, runId, since);
|
|
326
|
+
for (const msg of historical) {
|
|
327
|
+
lastMessageTimestamp = Math.max(lastMessageTimestamp, msg.timestamp || 0);
|
|
328
|
+
if (onMessage) {
|
|
329
|
+
onMessage(msg, exit);
|
|
330
|
+
}
|
|
331
|
+
const workstreamId = msg.workstream_id || 'main';
|
|
332
|
+
const streamIsOver = msg.type === AgentMessageType.TERMINATED ||
|
|
333
|
+
(msg.type === AgentMessageType.COMPLETE && workstreamId === 'main');
|
|
334
|
+
if (streamIsOver) {
|
|
335
|
+
resolve(null);
|
|
336
|
+
return;
|
|
426
337
|
}
|
|
427
338
|
}
|
|
428
|
-
}
|
|
339
|
+
} catch (err) {
|
|
340
|
+
console.warn("Failed to fetch historical messages, continuing with SSE:", err);
|
|
341
|
+
}
|
|
429
342
|
|
|
430
|
-
//
|
|
343
|
+
// 2. Connect to SSE
|
|
431
344
|
setupStream(false);
|
|
432
|
-
}
|
|
433
|
-
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
init().catch(reject);
|
|
434
348
|
});
|
|
435
349
|
}
|
|
436
350
|
|
|
@@ -607,6 +521,167 @@ export class WorkflowsApi extends ApiTopic {
|
|
|
607
521
|
});
|
|
608
522
|
}
|
|
609
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Ingest telemetry events for a workflow run.
|
|
526
|
+
* Workers use this to send telemetry to zeno-server for BigQuery storage.
|
|
527
|
+
*/
|
|
528
|
+
ingestEvents(
|
|
529
|
+
workflowId: string,
|
|
530
|
+
runId: string,
|
|
531
|
+
events: AgentEvent[]
|
|
532
|
+
): Promise<{ ingested: number; status?: string; error?: string }> {
|
|
533
|
+
return this.post(`/runs/${workflowId}/${runId}/events`, {
|
|
534
|
+
payload: { events },
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// ========================================================================
|
|
539
|
+
// Analytics API
|
|
540
|
+
// ========================================================================
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Get workflow analytics summary.
|
|
544
|
+
* Returns overall metrics including token usage, success rates, and run counts.
|
|
545
|
+
*/
|
|
546
|
+
getAnalyticsSummary(
|
|
547
|
+
query: WorkflowAnalyticsSummaryQuery = {}
|
|
548
|
+
): Promise<WorkflowAnalyticsSummaryResponse> {
|
|
549
|
+
return this.post('/analytics/summary', { payload: query });
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Get token usage analytics.
|
|
554
|
+
* Returns token consumption metrics by model, agent, tool, or over time.
|
|
555
|
+
*/
|
|
556
|
+
getTokenUsageAnalytics(
|
|
557
|
+
query: WorkflowAnalyticsTimeSeriesQuery = {}
|
|
558
|
+
): Promise<TokenUsageAnalyticsResponse> {
|
|
559
|
+
return this.post('/analytics/tokens', { payload: query });
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Get LLM latency analytics.
|
|
564
|
+
* Returns duration/latency metrics for LLM calls.
|
|
565
|
+
*/
|
|
566
|
+
getLlmLatencyAnalytics(
|
|
567
|
+
query: WorkflowAnalyticsTimeSeriesQuery = {}
|
|
568
|
+
): Promise<LatencyAnalyticsResponse> {
|
|
569
|
+
return this.post('/analytics/latency/llm', { payload: query });
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Get tool latency analytics.
|
|
574
|
+
* Returns duration/latency metrics for tool calls.
|
|
575
|
+
*/
|
|
576
|
+
getToolLatencyAnalytics(
|
|
577
|
+
query: WorkflowAnalyticsTimeSeriesQuery = {}
|
|
578
|
+
): Promise<LatencyAnalyticsResponse> {
|
|
579
|
+
return this.post('/analytics/latency/tools', { payload: query });
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Get agent/workflow latency analytics.
|
|
584
|
+
* Returns duration metrics for complete workflow runs.
|
|
585
|
+
*/
|
|
586
|
+
getAgentLatencyAnalytics(
|
|
587
|
+
query: WorkflowAnalyticsTimeSeriesQuery = {}
|
|
588
|
+
): Promise<LatencyAnalyticsResponse> {
|
|
589
|
+
return this.post('/analytics/latency/agents', { payload: query });
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Get error analytics.
|
|
594
|
+
* Returns error rates, types, and trends.
|
|
595
|
+
*/
|
|
596
|
+
getErrorAnalytics(
|
|
597
|
+
query: WorkflowAnalyticsTimeSeriesQuery = {}
|
|
598
|
+
): Promise<ErrorAnalyticsResponse> {
|
|
599
|
+
return this.post('/analytics/errors', { payload: query });
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Get tool usage analytics.
|
|
604
|
+
* Returns tool invocation counts, success rates, and performance metrics.
|
|
605
|
+
*/
|
|
606
|
+
getToolAnalytics(
|
|
607
|
+
query: WorkflowAnalyticsSummaryQuery = {}
|
|
608
|
+
): Promise<ToolAnalyticsResponse> {
|
|
609
|
+
return this.post('/analytics/tools', { payload: query });
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Get tool parameter analytics.
|
|
614
|
+
* Returns parameter value distributions for a specific tool.
|
|
615
|
+
*/
|
|
616
|
+
getToolParameterAnalytics(
|
|
617
|
+
query: WorkflowToolParametersQuery
|
|
618
|
+
): Promise<ToolParameterAnalyticsResponse> {
|
|
619
|
+
return this.post('/analytics/tools/parameters', { payload: query });
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Get available filter options for analytics.
|
|
624
|
+
* Returns unique agents, environments, and models from telemetry data.
|
|
625
|
+
*/
|
|
626
|
+
getAnalyticsFilterOptions(
|
|
627
|
+
query: WorkflowAnalyticsSummaryQuery = {}
|
|
628
|
+
): Promise<WorkflowAnalyticsFilterOptionsResponse> {
|
|
629
|
+
return this.post('/analytics/filter-options', { payload: query });
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Get average prompt size (input tokens) by agent for startConversation calls.
|
|
634
|
+
* This represents the initial prompt + tools size.
|
|
635
|
+
*/
|
|
636
|
+
getPromptSizeAnalytics(
|
|
637
|
+
query: WorkflowAnalyticsSummaryQuery = {}
|
|
638
|
+
): Promise<PromptSizeAnalyticsResponse> {
|
|
639
|
+
return this.post('/analytics/prompt-size', { payload: query });
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Get top principals (users/API keys) who started the most agent runs.
|
|
644
|
+
* Returns the top N principals sorted by run count descending.
|
|
645
|
+
*/
|
|
646
|
+
getTopPrincipalsAnalytics(
|
|
647
|
+
query: WorkflowAnalyticsSummaryQuery = {}
|
|
648
|
+
): Promise<TopPrincipalsAnalyticsResponse> {
|
|
649
|
+
return this.post('/analytics/top-principals', { payload: query });
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Get agent run distribution - how many runs per agent/interaction type.
|
|
654
|
+
* Returns the top N agents sorted by run count descending.
|
|
655
|
+
*/
|
|
656
|
+
getRunsByAgentAnalytics(
|
|
657
|
+
query: WorkflowAnalyticsSummaryQuery = {}
|
|
658
|
+
): Promise<RunsByAgentAnalyticsResponse> {
|
|
659
|
+
return this.post('/analytics/runs-by-agent', { payload: query });
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Get time to first response analytics.
|
|
664
|
+
* Measures the time from agent start to the completion of the first LLM call.
|
|
665
|
+
* Returns average, min, max, median, p95, and p99 metrics.
|
|
666
|
+
*/
|
|
667
|
+
getTimeToFirstResponseAnalytics(
|
|
668
|
+
query: WorkflowAnalyticsTimeSeriesQuery = {}
|
|
669
|
+
): Promise<TimeToFirstResponseAnalyticsResponse> {
|
|
670
|
+
return this.post('/analytics/time-to-first-response', { payload: query });
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Get first response behavior analytics.
|
|
675
|
+
* Analyzes the agent's first LLM response behavior:
|
|
676
|
+
* - Percentage of agents that start by making a plan
|
|
677
|
+
* - Percentage of agents that return no tool calls at start
|
|
678
|
+
*/
|
|
679
|
+
getFirstResponseBehaviorAnalytics(
|
|
680
|
+
query: WorkflowAnalyticsTimeSeriesQuery = {}
|
|
681
|
+
): Promise<FirstResponseBehaviorAnalyticsResponse> {
|
|
682
|
+
return this.post('/analytics/first-response-behavior', { payload: query });
|
|
683
|
+
}
|
|
684
|
+
|
|
610
685
|
rules = new WorkflowsRulesApi(this);
|
|
611
686
|
definitions = new WorkflowsDefinitionApi(this);
|
|
612
687
|
}
|