@octavus/server-sdk 2.18.0 → 2.20.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/dist/index.cjs +1109 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +552 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +32 -12
- package/dist/index.js.map +1 -1
- package/package.json +12 -5
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AgentSession: () => AgentSession,
|
|
24
|
+
AgentSessionsApi: () => AgentSessionsApi,
|
|
25
|
+
AgentsApi: () => AgentsApi,
|
|
26
|
+
ApiError: () => ApiError,
|
|
27
|
+
AppError: () => import_core5.AppError,
|
|
28
|
+
ConflictError: () => import_core5.ConflictError,
|
|
29
|
+
FilesApi: () => FilesApi,
|
|
30
|
+
ForbiddenError: () => import_core5.ForbiddenError,
|
|
31
|
+
MAIN_THREAD: () => import_core5.MAIN_THREAD,
|
|
32
|
+
NotFoundError: () => import_core5.NotFoundError,
|
|
33
|
+
OCTAVUS_SKILL_TOOLS: () => import_core5.OCTAVUS_SKILL_TOOLS,
|
|
34
|
+
OctavusClient: () => OctavusClient,
|
|
35
|
+
OctavusError: () => import_core5.OctavusError,
|
|
36
|
+
Resource: () => Resource,
|
|
37
|
+
ValidationError: () => import_core5.ValidationError,
|
|
38
|
+
WorkerError: () => WorkerError,
|
|
39
|
+
WorkersApi: () => WorkersApi,
|
|
40
|
+
createApiErrorEvent: () => import_core5.createApiErrorEvent,
|
|
41
|
+
createErrorEvent: () => import_core5.createErrorEvent,
|
|
42
|
+
createInternalErrorEvent: () => import_core5.createInternalErrorEvent,
|
|
43
|
+
errorToStreamEvent: () => import_core5.errorToStreamEvent,
|
|
44
|
+
generateId: () => import_core5.generateId,
|
|
45
|
+
getSkillSlugFromToolCall: () => import_core5.getSkillSlugFromToolCall,
|
|
46
|
+
isAbortError: () => import_core5.isAbortError,
|
|
47
|
+
isAuthenticationError: () => import_core5.isAuthenticationError,
|
|
48
|
+
isFileReference: () => import_core5.isFileReference,
|
|
49
|
+
isFileReferenceArray: () => import_core5.isFileReferenceArray,
|
|
50
|
+
isMainThread: () => import_core5.isMainThread,
|
|
51
|
+
isOctavusSkillTool: () => import_core5.isOctavusSkillTool,
|
|
52
|
+
isOtherThread: () => import_core5.isOtherThread,
|
|
53
|
+
isProviderError: () => import_core5.isProviderError,
|
|
54
|
+
isRateLimitError: () => import_core5.isRateLimitError,
|
|
55
|
+
isRetryableError: () => import_core5.isRetryableError,
|
|
56
|
+
isToolError: () => import_core5.isToolError,
|
|
57
|
+
isValidationError: () => import_core5.isValidationError,
|
|
58
|
+
normalizeToolResultImages: () => normalizeToolResultImages,
|
|
59
|
+
resolveThread: () => import_core5.resolveThread,
|
|
60
|
+
safeParseStreamEvent: () => import_core5.safeParseStreamEvent,
|
|
61
|
+
safeParseUIMessage: () => import_core5.safeParseUIMessage,
|
|
62
|
+
safeParseUIMessages: () => import_core5.safeParseUIMessages,
|
|
63
|
+
threadForPart: () => import_core5.threadForPart,
|
|
64
|
+
toSSEStream: () => toSSEStream
|
|
65
|
+
});
|
|
66
|
+
module.exports = __toCommonJS(index_exports);
|
|
67
|
+
|
|
68
|
+
// src/api-error.ts
|
|
69
|
+
var import_zod = require("zod");
|
|
70
|
+
var ApiErrorResponseSchema = import_zod.z.object({
|
|
71
|
+
error: import_zod.z.string().optional(),
|
|
72
|
+
message: import_zod.z.string().optional(),
|
|
73
|
+
code: import_zod.z.string().optional()
|
|
74
|
+
});
|
|
75
|
+
var ApiError = class extends Error {
|
|
76
|
+
constructor(message, status, code) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.status = status;
|
|
79
|
+
this.code = code;
|
|
80
|
+
this.name = "ApiError";
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
async function parseApiError(response, defaultMessage) {
|
|
84
|
+
const fallbackMessage = `${defaultMessage}: ${response.statusText}`;
|
|
85
|
+
try {
|
|
86
|
+
const json = await response.json();
|
|
87
|
+
const parsed = ApiErrorResponseSchema.safeParse(json);
|
|
88
|
+
if (parsed.success) {
|
|
89
|
+
return {
|
|
90
|
+
message: parsed.data.error ?? parsed.data.message ?? fallbackMessage,
|
|
91
|
+
code: parsed.data.code
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
return { message: fallbackMessage };
|
|
97
|
+
}
|
|
98
|
+
async function throwApiError(response, defaultMessage) {
|
|
99
|
+
const { message, code } = await parseApiError(response, defaultMessage);
|
|
100
|
+
throw new ApiError(message, response.status, code);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/base-api-client.ts
|
|
104
|
+
var BaseApiClient = class {
|
|
105
|
+
config;
|
|
106
|
+
constructor(config) {
|
|
107
|
+
this.config = config;
|
|
108
|
+
}
|
|
109
|
+
async httpGet(path, schema) {
|
|
110
|
+
const response = await fetch(`${this.config.baseUrl}${path}`, {
|
|
111
|
+
method: "GET",
|
|
112
|
+
headers: this.config.getHeaders()
|
|
113
|
+
});
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
await throwApiError(response, "Request failed");
|
|
116
|
+
}
|
|
117
|
+
const data = await response.json();
|
|
118
|
+
return schema.parse(data);
|
|
119
|
+
}
|
|
120
|
+
async httpPost(path, body, schema) {
|
|
121
|
+
const response = await fetch(`${this.config.baseUrl}${path}`, {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: this.config.getHeaders(),
|
|
124
|
+
body: JSON.stringify(body)
|
|
125
|
+
});
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
await throwApiError(response, "Request failed");
|
|
128
|
+
}
|
|
129
|
+
const data = await response.json();
|
|
130
|
+
return schema.parse(data);
|
|
131
|
+
}
|
|
132
|
+
async httpPatch(path, body, schema) {
|
|
133
|
+
const response = await fetch(`${this.config.baseUrl}${path}`, {
|
|
134
|
+
method: "PATCH",
|
|
135
|
+
headers: this.config.getHeaders(),
|
|
136
|
+
body: JSON.stringify(body)
|
|
137
|
+
});
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
await throwApiError(response, "Request failed");
|
|
140
|
+
}
|
|
141
|
+
const data = await response.json();
|
|
142
|
+
return schema.parse(data);
|
|
143
|
+
}
|
|
144
|
+
async httpDelete(path, schema) {
|
|
145
|
+
const response = await fetch(`${this.config.baseUrl}${path}`, {
|
|
146
|
+
method: "DELETE",
|
|
147
|
+
headers: this.config.getHeaders()
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
await throwApiError(response, "Request failed");
|
|
151
|
+
}
|
|
152
|
+
const data = await response.json();
|
|
153
|
+
return schema.parse(data);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// src/agent-types.ts
|
|
158
|
+
var import_zod2 = require("zod");
|
|
159
|
+
var agentFormatSchema = import_zod2.z.enum(["interactive", "worker"]);
|
|
160
|
+
var agentSettingsSchema = import_zod2.z.object({
|
|
161
|
+
slug: import_zod2.z.string(),
|
|
162
|
+
name: import_zod2.z.string(),
|
|
163
|
+
description: import_zod2.z.string().optional(),
|
|
164
|
+
format: agentFormatSchema
|
|
165
|
+
});
|
|
166
|
+
var agentPromptSchema = import_zod2.z.object({
|
|
167
|
+
name: import_zod2.z.string(),
|
|
168
|
+
content: import_zod2.z.string()
|
|
169
|
+
});
|
|
170
|
+
var agentSchema = import_zod2.z.object({
|
|
171
|
+
slug: import_zod2.z.string(),
|
|
172
|
+
id: import_zod2.z.string(),
|
|
173
|
+
name: import_zod2.z.string(),
|
|
174
|
+
description: import_zod2.z.string().nullable(),
|
|
175
|
+
format: agentFormatSchema,
|
|
176
|
+
createdAt: import_zod2.z.string(),
|
|
177
|
+
updatedAt: import_zod2.z.string(),
|
|
178
|
+
projectId: import_zod2.z.string()
|
|
179
|
+
});
|
|
180
|
+
var agentsResponseSchema = import_zod2.z.object({
|
|
181
|
+
agents: import_zod2.z.array(agentSchema)
|
|
182
|
+
});
|
|
183
|
+
var agentDefinitionSchema = import_zod2.z.object({
|
|
184
|
+
settings: agentSettingsSchema,
|
|
185
|
+
protocol: import_zod2.z.string(),
|
|
186
|
+
prompts: import_zod2.z.array(agentPromptSchema),
|
|
187
|
+
id: import_zod2.z.string()
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// src/agents.ts
|
|
191
|
+
var AgentsApi = class extends BaseApiClient {
|
|
192
|
+
/** List all agents in the project */
|
|
193
|
+
async list() {
|
|
194
|
+
const response = await this.httpGet("/api/agents", agentsResponseSchema);
|
|
195
|
+
return response.agents;
|
|
196
|
+
}
|
|
197
|
+
/** Get a single agent by ID */
|
|
198
|
+
async get(agentId) {
|
|
199
|
+
return await this.httpGet(`/api/agents/${agentId}`, agentDefinitionSchema);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// src/agent-sessions.ts
|
|
204
|
+
var import_zod3 = require("zod");
|
|
205
|
+
var import_core3 = require("@octavus/core");
|
|
206
|
+
|
|
207
|
+
// src/session.ts
|
|
208
|
+
var import_core2 = require("@octavus/core");
|
|
209
|
+
|
|
210
|
+
// src/streaming.ts
|
|
211
|
+
var import_core = require("@octavus/core");
|
|
212
|
+
async function* executeStream(config, payload, signal) {
|
|
213
|
+
let toolResults = payload.toolResults;
|
|
214
|
+
let executionId = payload.executionId;
|
|
215
|
+
let continueLoop = true;
|
|
216
|
+
while (continueLoop) {
|
|
217
|
+
if (signal?.aborted) {
|
|
218
|
+
yield { type: "finish", finishReason: "stop" };
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const body = config.buildBody({ executionId, toolResults });
|
|
222
|
+
let response;
|
|
223
|
+
try {
|
|
224
|
+
response = await fetch(config.url, {
|
|
225
|
+
method: "POST",
|
|
226
|
+
headers: config.config.getHeaders(),
|
|
227
|
+
body: JSON.stringify(body),
|
|
228
|
+
signal
|
|
229
|
+
});
|
|
230
|
+
} catch (err) {
|
|
231
|
+
if ((0, import_core.isAbortError)(err)) {
|
|
232
|
+
yield { type: "finish", finishReason: "stop" };
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const { message } = await parseApiError(response, config.errorContext ?? "Request failed");
|
|
239
|
+
yield (0, import_core.createApiErrorEvent)(response.status, message);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!response.body) {
|
|
243
|
+
yield (0, import_core.createInternalErrorEvent)("Response body is not readable");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
toolResults = void 0;
|
|
247
|
+
const reader = response.body.getReader();
|
|
248
|
+
const decoder = new TextDecoder();
|
|
249
|
+
let buffer = "";
|
|
250
|
+
let pendingToolCalls = null;
|
|
251
|
+
let streamDone = false;
|
|
252
|
+
while (!streamDone) {
|
|
253
|
+
if (signal?.aborted) {
|
|
254
|
+
reader.releaseLock();
|
|
255
|
+
yield { type: "finish", finishReason: "stop" };
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
let readResult;
|
|
259
|
+
try {
|
|
260
|
+
readResult = await reader.read();
|
|
261
|
+
} catch (err) {
|
|
262
|
+
if ((0, import_core.isAbortError)(err)) {
|
|
263
|
+
reader.releaseLock();
|
|
264
|
+
yield { type: "finish", finishReason: "stop" };
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
throw err;
|
|
268
|
+
}
|
|
269
|
+
const { done, value } = readResult;
|
|
270
|
+
if (done) {
|
|
271
|
+
streamDone = true;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
buffer += decoder.decode(value, { stream: true });
|
|
275
|
+
const lines = buffer.split("\n");
|
|
276
|
+
buffer = lines.pop() ?? "";
|
|
277
|
+
for (const line of lines) {
|
|
278
|
+
if (line.startsWith("data: ") && line !== "data: [DONE]") {
|
|
279
|
+
try {
|
|
280
|
+
const parsed = (0, import_core.safeParseStreamEvent)(JSON.parse(line.slice(6)));
|
|
281
|
+
if (!parsed.success) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
const event = parsed.data;
|
|
285
|
+
if (event.type === "start" && event.executionId) {
|
|
286
|
+
executionId = event.executionId;
|
|
287
|
+
}
|
|
288
|
+
if (event.type === "tool-request") {
|
|
289
|
+
pendingToolCalls = event.toolCalls;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (event.type === "finish") {
|
|
293
|
+
if (event.finishReason === "tool-calls" && pendingToolCalls) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
yield event;
|
|
297
|
+
continueLoop = false;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (event.type === "resource-update" && config.onResourceUpdate) {
|
|
301
|
+
config.onResourceUpdate(event.name, event.value);
|
|
302
|
+
}
|
|
303
|
+
yield event;
|
|
304
|
+
} catch {
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (signal?.aborted) {
|
|
310
|
+
yield { type: "finish", finishReason: "stop" };
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (pendingToolCalls && pendingToolCalls.length > 0) {
|
|
314
|
+
const serverTools = pendingToolCalls.filter((tc) => config.toolHandlers[tc.toolName]);
|
|
315
|
+
const clientTools = pendingToolCalls.filter((tc) => !config.toolHandlers[tc.toolName]);
|
|
316
|
+
const serverResults = await Promise.all(
|
|
317
|
+
serverTools.map(async (tc) => {
|
|
318
|
+
const handler = config.toolHandlers[tc.toolName];
|
|
319
|
+
try {
|
|
320
|
+
const result = await handler(tc.args);
|
|
321
|
+
return {
|
|
322
|
+
toolCallId: tc.toolCallId,
|
|
323
|
+
toolName: tc.toolName,
|
|
324
|
+
result,
|
|
325
|
+
outputVariable: tc.outputVariable,
|
|
326
|
+
blockIndex: tc.blockIndex,
|
|
327
|
+
thread: tc.thread,
|
|
328
|
+
workerId: tc.workerId
|
|
329
|
+
};
|
|
330
|
+
} catch (err) {
|
|
331
|
+
return {
|
|
332
|
+
toolCallId: tc.toolCallId,
|
|
333
|
+
toolName: tc.toolName,
|
|
334
|
+
error: err instanceof Error ? err.message : "Tool execution failed",
|
|
335
|
+
outputVariable: tc.outputVariable,
|
|
336
|
+
blockIndex: tc.blockIndex,
|
|
337
|
+
thread: tc.thread,
|
|
338
|
+
workerId: tc.workerId
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
);
|
|
343
|
+
if (config.onToolResults && serverResults.length > 0) {
|
|
344
|
+
await config.onToolResults(serverResults);
|
|
345
|
+
}
|
|
346
|
+
for (const tr of serverResults) {
|
|
347
|
+
if (tr.error) {
|
|
348
|
+
yield { type: "tool-output-error", toolCallId: tr.toolCallId, error: tr.error };
|
|
349
|
+
} else {
|
|
350
|
+
yield { type: "tool-output-available", toolCallId: tr.toolCallId, output: tr.result };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (clientTools.length > 0) {
|
|
354
|
+
if (config.rejectClientToolCalls) {
|
|
355
|
+
const rejectedResults = clientTools.map((tc) => ({
|
|
356
|
+
toolCallId: tc.toolCallId,
|
|
357
|
+
toolName: tc.toolName,
|
|
358
|
+
error: `Tool "${tc.toolName}" is not available. No handler is registered for this tool.`,
|
|
359
|
+
outputVariable: tc.outputVariable,
|
|
360
|
+
blockIndex: tc.blockIndex,
|
|
361
|
+
thread: tc.thread,
|
|
362
|
+
workerId: tc.workerId
|
|
363
|
+
}));
|
|
364
|
+
for (const tr of rejectedResults) {
|
|
365
|
+
yield { type: "tool-output-error", toolCallId: tr.toolCallId, error: tr.error };
|
|
366
|
+
}
|
|
367
|
+
toolResults = [...serverResults, ...rejectedResults];
|
|
368
|
+
} else {
|
|
369
|
+
if (!executionId) {
|
|
370
|
+
yield (0, import_core.createInternalErrorEvent)("Missing executionId for client-tool-request");
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
yield {
|
|
374
|
+
type: "client-tool-request",
|
|
375
|
+
executionId,
|
|
376
|
+
toolCalls: clientTools,
|
|
377
|
+
serverToolResults: serverResults.length > 0 ? serverResults : void 0
|
|
378
|
+
};
|
|
379
|
+
yield { type: "finish", finishReason: "client-tool-calls", executionId };
|
|
380
|
+
continueLoop = false;
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
toolResults = serverResults;
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
continueLoop = false;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/session.ts
|
|
392
|
+
var SSE_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
393
|
+
function toSSEStream(events) {
|
|
394
|
+
const encoder = new TextEncoder();
|
|
395
|
+
const heartbeatBytes = encoder.encode(": heartbeat\n\n");
|
|
396
|
+
return new ReadableStream({
|
|
397
|
+
async start(controller) {
|
|
398
|
+
const heartbeat = setInterval(() => {
|
|
399
|
+
try {
|
|
400
|
+
controller.enqueue(heartbeatBytes);
|
|
401
|
+
} catch {
|
|
402
|
+
clearInterval(heartbeat);
|
|
403
|
+
}
|
|
404
|
+
}, SSE_HEARTBEAT_INTERVAL_MS);
|
|
405
|
+
try {
|
|
406
|
+
for await (const event of events) {
|
|
407
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}
|
|
408
|
+
|
|
409
|
+
`));
|
|
410
|
+
}
|
|
411
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
412
|
+
controller.close();
|
|
413
|
+
} catch (err) {
|
|
414
|
+
const errorEvent = (0, import_core2.createInternalErrorEvent)(
|
|
415
|
+
err instanceof Error ? err.message : "Unknown error"
|
|
416
|
+
);
|
|
417
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(errorEvent)}
|
|
418
|
+
|
|
419
|
+
`));
|
|
420
|
+
controller.close();
|
|
421
|
+
} finally {
|
|
422
|
+
clearInterval(heartbeat);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
var AgentSession = class {
|
|
428
|
+
sessionId;
|
|
429
|
+
config;
|
|
430
|
+
toolHandlers;
|
|
431
|
+
resourceMap;
|
|
432
|
+
additionalToolSchemas;
|
|
433
|
+
additionalToolSchemasSent = false;
|
|
434
|
+
socketAbortController = null;
|
|
435
|
+
onToolResults;
|
|
436
|
+
rejectClientToolCalls;
|
|
437
|
+
constructor(sessionConfig) {
|
|
438
|
+
this.sessionId = sessionConfig.sessionId;
|
|
439
|
+
this.config = sessionConfig.config;
|
|
440
|
+
this.toolHandlers = sessionConfig.tools ?? {};
|
|
441
|
+
this.additionalToolSchemas = sessionConfig.additionalToolSchemas;
|
|
442
|
+
this.onToolResults = sessionConfig.onToolResults;
|
|
443
|
+
this.rejectClientToolCalls = sessionConfig.rejectClientToolCalls ?? false;
|
|
444
|
+
this.resourceMap = /* @__PURE__ */ new Map();
|
|
445
|
+
for (const resource of sessionConfig.resources ?? []) {
|
|
446
|
+
this.resourceMap.set(resource.name, resource);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Execute a session request and stream the response.
|
|
451
|
+
*
|
|
452
|
+
* This is the unified method that handles both triggers and continuations.
|
|
453
|
+
* Use this when you want to pass through requests from the client directly.
|
|
454
|
+
*
|
|
455
|
+
* @param request - The request (check `request.type` for the kind)
|
|
456
|
+
* @param options - Optional configuration including abort signal
|
|
457
|
+
*
|
|
458
|
+
* @example HTTP route (simple passthrough)
|
|
459
|
+
* ```typescript
|
|
460
|
+
* const events = session.execute(body, { signal: request.signal });
|
|
461
|
+
* return new Response(toSSEStream(events));
|
|
462
|
+
* ```
|
|
463
|
+
*
|
|
464
|
+
* @example WebSocket handler
|
|
465
|
+
* ```typescript
|
|
466
|
+
* socket.on('message', (data) => {
|
|
467
|
+
* const events = session.execute(data);
|
|
468
|
+
* for await (const event of events) {
|
|
469
|
+
* socket.send(JSON.stringify(event));
|
|
470
|
+
* }
|
|
471
|
+
* });
|
|
472
|
+
* ```
|
|
473
|
+
*/
|
|
474
|
+
async *execute(request, options) {
|
|
475
|
+
if (request.type === "continue") {
|
|
476
|
+
yield* this.executeStream(
|
|
477
|
+
{ executionId: request.executionId, toolResults: request.toolResults },
|
|
478
|
+
options?.signal
|
|
479
|
+
);
|
|
480
|
+
} else {
|
|
481
|
+
yield* this.executeStream(
|
|
482
|
+
{
|
|
483
|
+
triggerName: request.triggerName,
|
|
484
|
+
input: request.input,
|
|
485
|
+
rollbackAfterMessageId: request.rollbackAfterMessageId
|
|
486
|
+
},
|
|
487
|
+
options?.signal
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
getSessionId() {
|
|
492
|
+
return this.sessionId;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Handle a WebSocket protocol message (trigger, continue, or stop).
|
|
496
|
+
* Manages abort controller lifecycle internally.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* conn.on('data', (raw) => {
|
|
501
|
+
* session.handleSocketMessage(JSON.parse(raw), {
|
|
502
|
+
* onEvent: (event) => conn.write(JSON.stringify(event)),
|
|
503
|
+
* onFinish: async () => {
|
|
504
|
+
* // Fetch messages and persist to your database for restoration
|
|
505
|
+
* },
|
|
506
|
+
* });
|
|
507
|
+
* });
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
async handleSocketMessage(message, handlers) {
|
|
511
|
+
if (message.type === "stop") {
|
|
512
|
+
this.socketAbortController?.abort();
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
this.socketAbortController?.abort();
|
|
516
|
+
this.socketAbortController = new AbortController();
|
|
517
|
+
const localController = this.socketAbortController;
|
|
518
|
+
try {
|
|
519
|
+
const events = this.execute(message, { signal: localController.signal });
|
|
520
|
+
for await (const event of events) {
|
|
521
|
+
if (localController.signal.aborted) break;
|
|
522
|
+
handlers.onEvent(event);
|
|
523
|
+
}
|
|
524
|
+
if (!localController.signal.aborted && handlers.onFinish) {
|
|
525
|
+
await handlers.onFinish();
|
|
526
|
+
}
|
|
527
|
+
} catch (err) {
|
|
528
|
+
if (!localController.signal.aborted) {
|
|
529
|
+
const errorEvent = (0, import_core2.createInternalErrorEvent)(
|
|
530
|
+
err instanceof Error ? err.message : "Unknown error"
|
|
531
|
+
);
|
|
532
|
+
handlers.onEvent(errorEvent);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
async *executeStream(payload, signal) {
|
|
537
|
+
yield* executeStream(
|
|
538
|
+
{
|
|
539
|
+
config: this.config,
|
|
540
|
+
toolHandlers: this.toolHandlers,
|
|
541
|
+
url: `${this.config.baseUrl}/api/agent-sessions/${this.sessionId}/trigger`,
|
|
542
|
+
buildBody: ({ executionId, toolResults }) => {
|
|
543
|
+
const body = {};
|
|
544
|
+
if (payload.triggerName !== void 0) body.triggerName = payload.triggerName;
|
|
545
|
+
if (payload.input !== void 0) body.input = payload.input;
|
|
546
|
+
if (payload.rollbackAfterMessageId !== void 0)
|
|
547
|
+
body.rollbackAfterMessageId = payload.rollbackAfterMessageId;
|
|
548
|
+
if (executionId !== void 0) body.executionId = executionId;
|
|
549
|
+
if (toolResults !== void 0) body.toolResults = toolResults;
|
|
550
|
+
if (!this.additionalToolSchemasSent && (this.additionalToolSchemas?.length ?? 0) > 0) {
|
|
551
|
+
body.additionalToolSchemas = this.additionalToolSchemas;
|
|
552
|
+
this.additionalToolSchemasSent = true;
|
|
553
|
+
}
|
|
554
|
+
return body;
|
|
555
|
+
},
|
|
556
|
+
onResourceUpdate: (name, value) => this.handleResourceUpdate(name, value),
|
|
557
|
+
onToolResults: this.onToolResults,
|
|
558
|
+
rejectClientToolCalls: this.rejectClientToolCalls,
|
|
559
|
+
errorContext: "Failed to trigger"
|
|
560
|
+
},
|
|
561
|
+
{ executionId: payload.executionId, toolResults: payload.toolResults },
|
|
562
|
+
signal
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
handleResourceUpdate(name, value) {
|
|
566
|
+
const resource = this.resourceMap.get(name);
|
|
567
|
+
if (resource) {
|
|
568
|
+
void resource.onUpdate(value);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
// src/agent-sessions.ts
|
|
574
|
+
var createSessionResponseSchema = import_zod3.z.object({
|
|
575
|
+
sessionId: import_zod3.z.string()
|
|
576
|
+
});
|
|
577
|
+
var sessionStateSchema = import_zod3.z.object({
|
|
578
|
+
id: import_zod3.z.string(),
|
|
579
|
+
agentId: import_zod3.z.string(),
|
|
580
|
+
input: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()),
|
|
581
|
+
variables: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()),
|
|
582
|
+
resources: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()),
|
|
583
|
+
messages: import_zod3.z.array(import_core3.chatMessageSchema),
|
|
584
|
+
status: import_zod3.z.literal("active").optional(),
|
|
585
|
+
createdAt: import_zod3.z.string(),
|
|
586
|
+
updatedAt: import_zod3.z.string()
|
|
587
|
+
});
|
|
588
|
+
var uiSessionResponseSchema = import_zod3.z.object({
|
|
589
|
+
sessionId: import_zod3.z.string(),
|
|
590
|
+
agentId: import_zod3.z.string(),
|
|
591
|
+
messages: import_zod3.z.array(import_core3.uiMessageSchema),
|
|
592
|
+
status: import_zod3.z.literal("active").optional()
|
|
593
|
+
});
|
|
594
|
+
var expiredSessionResponseSchema = import_zod3.z.object({
|
|
595
|
+
sessionId: import_zod3.z.string(),
|
|
596
|
+
agentId: import_zod3.z.string(),
|
|
597
|
+
status: import_zod3.z.literal("expired"),
|
|
598
|
+
createdAt: import_zod3.z.string()
|
|
599
|
+
});
|
|
600
|
+
var restoreSessionResponseSchema = import_zod3.z.object({
|
|
601
|
+
sessionId: import_zod3.z.string(),
|
|
602
|
+
restored: import_zod3.z.boolean()
|
|
603
|
+
});
|
|
604
|
+
var clearSessionResponseSchema = import_zod3.z.object({
|
|
605
|
+
sessionId: import_zod3.z.string(),
|
|
606
|
+
cleared: import_zod3.z.boolean()
|
|
607
|
+
});
|
|
608
|
+
var AgentSessionsApi = class extends BaseApiClient {
|
|
609
|
+
/** Create a new session for an agent */
|
|
610
|
+
async create(agentId, input) {
|
|
611
|
+
const response = await this.httpPost(
|
|
612
|
+
"/api/agent-sessions",
|
|
613
|
+
{ agentId, input },
|
|
614
|
+
createSessionResponseSchema
|
|
615
|
+
);
|
|
616
|
+
return response.sessionId;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Get full session state (for internal/debug use)
|
|
620
|
+
* Note: Contains all messages including hidden content
|
|
621
|
+
*
|
|
622
|
+
* Returns SessionState for active sessions, ExpiredSessionState for expired sessions.
|
|
623
|
+
* Check `status` field to determine which type was returned.
|
|
624
|
+
*/
|
|
625
|
+
async get(sessionId) {
|
|
626
|
+
const response = await fetch(`${this.config.baseUrl}/api/agent-sessions/${sessionId}`, {
|
|
627
|
+
method: "GET",
|
|
628
|
+
headers: this.config.getHeaders()
|
|
629
|
+
});
|
|
630
|
+
if (!response.ok) {
|
|
631
|
+
await throwApiError(response, "Request failed");
|
|
632
|
+
}
|
|
633
|
+
const data = await response.json();
|
|
634
|
+
const expiredResult = expiredSessionResponseSchema.safeParse(data);
|
|
635
|
+
if (expiredResult.success) {
|
|
636
|
+
return expiredResult.data;
|
|
637
|
+
}
|
|
638
|
+
return sessionStateSchema.parse(data);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Get UI-ready session messages (for client display)
|
|
642
|
+
* Returns only visible messages with hidden content filtered out.
|
|
643
|
+
*
|
|
644
|
+
* For expired sessions, returns status: 'expired' without messages.
|
|
645
|
+
* Use restore() to restore from stored messages before continuing.
|
|
646
|
+
*/
|
|
647
|
+
async getMessages(sessionId) {
|
|
648
|
+
const response = await fetch(
|
|
649
|
+
`${this.config.baseUrl}/api/agent-sessions/${sessionId}?format=ui`,
|
|
650
|
+
{
|
|
651
|
+
method: "GET",
|
|
652
|
+
headers: this.config.getHeaders()
|
|
653
|
+
}
|
|
654
|
+
);
|
|
655
|
+
if (!response.ok) {
|
|
656
|
+
await throwApiError(response, "Request failed");
|
|
657
|
+
}
|
|
658
|
+
const data = await response.json();
|
|
659
|
+
const expiredResult = expiredSessionResponseSchema.safeParse(data);
|
|
660
|
+
if (expiredResult.success) {
|
|
661
|
+
return expiredResult.data;
|
|
662
|
+
}
|
|
663
|
+
return uiSessionResponseSchema.parse(data);
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Restore an expired session from stored messages.
|
|
667
|
+
*
|
|
668
|
+
* Use this to restore a session after its state has expired.
|
|
669
|
+
* The consumer should have stored the UIMessage[] array from previous interactions.
|
|
670
|
+
*
|
|
671
|
+
* @param sessionId - The session ID to restore
|
|
672
|
+
* @param messages - Previously stored UIMessage[] array
|
|
673
|
+
* @param input - Optional session input for system prompt interpolation (same as create)
|
|
674
|
+
* @returns { sessionId, restored: true } if restored, { sessionId, restored: false } if already active
|
|
675
|
+
*/
|
|
676
|
+
async restore(sessionId, messages, input) {
|
|
677
|
+
return await this.httpPost(
|
|
678
|
+
`/api/agent-sessions/${sessionId}/restore`,
|
|
679
|
+
{ messages, input },
|
|
680
|
+
restoreSessionResponseSchema
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Clear session state from the server.
|
|
685
|
+
* The session will transition to 'expired' status and can be restored with restore().
|
|
686
|
+
* Idempotent: succeeds even if state was already cleared/expired.
|
|
687
|
+
*/
|
|
688
|
+
async clear(sessionId) {
|
|
689
|
+
return await this.httpDelete(`/api/agent-sessions/${sessionId}`, clearSessionResponseSchema);
|
|
690
|
+
}
|
|
691
|
+
/** Attach to an existing session for triggering events */
|
|
692
|
+
attach(sessionId, options = {}) {
|
|
693
|
+
const computerHandlers = options.computer?.toolHandlers() ?? {};
|
|
694
|
+
const mergedTools = { ...computerHandlers, ...options.tools };
|
|
695
|
+
return new AgentSession({
|
|
696
|
+
sessionId,
|
|
697
|
+
config: this.config,
|
|
698
|
+
tools: mergedTools,
|
|
699
|
+
resources: options.resources,
|
|
700
|
+
additionalToolSchemas: options.computer?.toolSchemas(),
|
|
701
|
+
onToolResults: options.onToolResults,
|
|
702
|
+
rejectClientToolCalls: options.rejectClientToolCalls
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
// src/files.ts
|
|
708
|
+
var import_zod4 = require("zod");
|
|
709
|
+
var fileUploadRequestSchema = import_zod4.z.object({
|
|
710
|
+
filename: import_zod4.z.string().min(1).max(255),
|
|
711
|
+
mediaType: import_zod4.z.string().min(1),
|
|
712
|
+
size: import_zod4.z.number().int().positive()
|
|
713
|
+
});
|
|
714
|
+
var fileUploadInfoSchema = import_zod4.z.object({
|
|
715
|
+
/** File ID to reference in messages */
|
|
716
|
+
id: import_zod4.z.string(),
|
|
717
|
+
/** Presigned PUT URL for uploading to S3 */
|
|
718
|
+
uploadUrl: import_zod4.z.url(),
|
|
719
|
+
/** Presigned GET URL for downloading after upload */
|
|
720
|
+
downloadUrl: import_zod4.z.url()
|
|
721
|
+
});
|
|
722
|
+
var uploadUrlsResponseSchema = import_zod4.z.object({
|
|
723
|
+
files: import_zod4.z.array(fileUploadInfoSchema)
|
|
724
|
+
});
|
|
725
|
+
var FilesApi = class extends BaseApiClient {
|
|
726
|
+
/**
|
|
727
|
+
* Get presigned URLs for uploading files to a session.
|
|
728
|
+
*
|
|
729
|
+
* Returns upload URLs (PUT) and download URLs (GET) for each file.
|
|
730
|
+
* Upload URLs expire in 15 minutes, download URLs match session TTL (24 hours).
|
|
731
|
+
*
|
|
732
|
+
* @param sessionId - The session ID to associate files with
|
|
733
|
+
* @param files - Array of file metadata (filename, mediaType, size)
|
|
734
|
+
* @returns Upload info with presigned URLs for each file
|
|
735
|
+
*
|
|
736
|
+
* @throws ApiError if session doesn't exist or validation fails
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ```typescript
|
|
740
|
+
* const { files } = await client.files.getUploadUrls(sessionId, [
|
|
741
|
+
* { filename: 'photo.jpg', mediaType: 'image/jpeg', size: 102400 },
|
|
742
|
+
* { filename: 'doc.pdf', mediaType: 'application/pdf', size: 204800 },
|
|
743
|
+
* ]);
|
|
744
|
+
*
|
|
745
|
+
* // files[0].id - Use in FileReference
|
|
746
|
+
* // files[0].uploadUrl - PUT to this URL
|
|
747
|
+
* // files[0].downloadUrl - Use as FileReference.url
|
|
748
|
+
* ```
|
|
749
|
+
*/
|
|
750
|
+
async getUploadUrls(sessionId, files) {
|
|
751
|
+
return await this.httpPost(
|
|
752
|
+
"/api/files/upload-urls",
|
|
753
|
+
{ sessionId, files },
|
|
754
|
+
uploadUrlsResponseSchema
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
|
|
759
|
+
// src/worker-error.ts
|
|
760
|
+
var WorkerError = class extends Error {
|
|
761
|
+
constructor(message, sessionId) {
|
|
762
|
+
super(message);
|
|
763
|
+
this.sessionId = sessionId;
|
|
764
|
+
this.name = "WorkerError";
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// src/workers.ts
|
|
769
|
+
var WorkersApi = class extends BaseApiClient {
|
|
770
|
+
/**
|
|
771
|
+
* Execute a worker agent and stream the response.
|
|
772
|
+
*
|
|
773
|
+
* Worker agents execute steps sequentially and return an output value.
|
|
774
|
+
* Unlike interactive sessions, workers don't maintain persistent state.
|
|
775
|
+
*
|
|
776
|
+
* The execution handles the tool continuation pattern automatically:
|
|
777
|
+
* - Server tools (with handlers provided) are executed automatically
|
|
778
|
+
* - Client tools (without handlers) emit a client-tool-request event
|
|
779
|
+
*
|
|
780
|
+
* @param agentId - The worker agent ID
|
|
781
|
+
* @param input - Input values for the worker
|
|
782
|
+
* @param options - Optional configuration including tools and abort signal
|
|
783
|
+
* @returns An async generator of stream events
|
|
784
|
+
*
|
|
785
|
+
* @example Basic execution
|
|
786
|
+
* ```typescript
|
|
787
|
+
* const events = client.workers.execute(agentId, { TOPIC: 'AI safety' });
|
|
788
|
+
* for await (const event of events) {
|
|
789
|
+
* if (event.type === 'worker-start') {
|
|
790
|
+
* console.log(`Worker ${event.workerSlug} started (${event.workerId})`);
|
|
791
|
+
* }
|
|
792
|
+
* if (event.type === 'worker-result') {
|
|
793
|
+
* if (event.error) {
|
|
794
|
+
* console.error('Worker failed:', event.error);
|
|
795
|
+
* } else {
|
|
796
|
+
* console.log('Output:', event.output);
|
|
797
|
+
* }
|
|
798
|
+
* }
|
|
799
|
+
* }
|
|
800
|
+
* ```
|
|
801
|
+
*
|
|
802
|
+
* @example With tool handlers
|
|
803
|
+
* ```typescript
|
|
804
|
+
* const events = client.workers.execute(agentId, { TOPIC: 'AI safety' }, {
|
|
805
|
+
* tools: {
|
|
806
|
+
* 'web-search': async (args) => {
|
|
807
|
+
* return await searchWeb(args.query);
|
|
808
|
+
* },
|
|
809
|
+
* },
|
|
810
|
+
* });
|
|
811
|
+
* ```
|
|
812
|
+
*/
|
|
813
|
+
async *execute(agentId, input, options = {}) {
|
|
814
|
+
yield* executeStream(
|
|
815
|
+
{
|
|
816
|
+
config: this.config,
|
|
817
|
+
toolHandlers: options.tools ?? {},
|
|
818
|
+
url: `${this.config.baseUrl}/api/agents/${agentId}/execute`,
|
|
819
|
+
buildBody: ({ executionId, toolResults }) => !executionId ? { type: "start", input } : { type: "continue", executionId, toolResults },
|
|
820
|
+
errorContext: "Failed to execute worker"
|
|
821
|
+
},
|
|
822
|
+
{},
|
|
823
|
+
options.signal
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Execute a worker agent and return the final output.
|
|
828
|
+
*
|
|
829
|
+
* Non-streaming equivalent of `execute()` — runs the worker to completion
|
|
830
|
+
* and returns the output value directly. Use this when you don't need to
|
|
831
|
+
* observe intermediate streaming events.
|
|
832
|
+
*
|
|
833
|
+
* @param agentId - The worker agent ID
|
|
834
|
+
* @param input - Input values for the worker
|
|
835
|
+
* @param options - Optional configuration including tools and abort signal
|
|
836
|
+
* @returns The worker output and session ID
|
|
837
|
+
* @throws {WorkerError} If the worker fails or completes without output
|
|
838
|
+
*
|
|
839
|
+
* @example Basic usage
|
|
840
|
+
* ```typescript
|
|
841
|
+
* const { output, sessionId } = await client.workers.generate(agentId, {
|
|
842
|
+
* TOPIC: 'AI safety',
|
|
843
|
+
* });
|
|
844
|
+
* console.log(output);
|
|
845
|
+
* console.log(`Debug: ${client.baseUrl}/platform/sessions/${sessionId}`);
|
|
846
|
+
* ```
|
|
847
|
+
*
|
|
848
|
+
* @example With timeout
|
|
849
|
+
* ```typescript
|
|
850
|
+
* const { output } = await client.workers.generate(agentId, input, {
|
|
851
|
+
* signal: AbortSignal.timeout(120_000),
|
|
852
|
+
* });
|
|
853
|
+
* ```
|
|
854
|
+
*/
|
|
855
|
+
async generate(agentId, input, options = {}) {
|
|
856
|
+
let sessionId;
|
|
857
|
+
for await (const event of this.execute(agentId, input, options)) {
|
|
858
|
+
if (event.type === "start" && event.executionId) {
|
|
859
|
+
sessionId = event.executionId;
|
|
860
|
+
} else if (event.type === "error") {
|
|
861
|
+
throw new WorkerError(event.message, sessionId);
|
|
862
|
+
} else if (event.type === "worker-result") {
|
|
863
|
+
if (event.error) {
|
|
864
|
+
throw new WorkerError(event.error, sessionId ?? event.workerId);
|
|
865
|
+
}
|
|
866
|
+
return {
|
|
867
|
+
output: event.output,
|
|
868
|
+
sessionId: sessionId ?? event.workerId
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
throw new WorkerError("Worker completed without producing a result", sessionId);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Continue a worker execution after client-side tool handling.
|
|
876
|
+
*
|
|
877
|
+
* Use this when your worker has tools without server-side handlers.
|
|
878
|
+
* The execution returns a client-tool-request event with an executionId.
|
|
879
|
+
* Execute the tools client-side, then call this method to continue.
|
|
880
|
+
*
|
|
881
|
+
* @param agentId - The worker agent ID
|
|
882
|
+
* @param executionId - The execution ID from the client-tool-request event
|
|
883
|
+
* @param toolResults - Results from client-side tool execution
|
|
884
|
+
* @param options - Optional configuration including tools and abort signal
|
|
885
|
+
* @returns An async generator of stream events
|
|
886
|
+
*
|
|
887
|
+
* @example
|
|
888
|
+
* ```typescript
|
|
889
|
+
* // Start execution
|
|
890
|
+
* for await (const event of client.workers.execute(agentId, input)) {
|
|
891
|
+
* if (event.type === 'client-tool-request') {
|
|
892
|
+
* // Execute tools client-side
|
|
893
|
+
* const results = await executeToolsClientSide(event.toolCalls);
|
|
894
|
+
* // Continue execution
|
|
895
|
+
* for await (const ev of client.workers.continue(agentId, event.executionId, results)) {
|
|
896
|
+
* // Handle remaining events
|
|
897
|
+
* }
|
|
898
|
+
* }
|
|
899
|
+
* }
|
|
900
|
+
* ```
|
|
901
|
+
*/
|
|
902
|
+
async *continue(agentId, executionId, toolResults, options = {}) {
|
|
903
|
+
yield* executeStream(
|
|
904
|
+
{
|
|
905
|
+
config: this.config,
|
|
906
|
+
toolHandlers: options.tools ?? {},
|
|
907
|
+
url: `${this.config.baseUrl}/api/agents/${agentId}/execute`,
|
|
908
|
+
buildBody: ({ executionId: execId, toolResults: results }) => ({
|
|
909
|
+
type: "continue",
|
|
910
|
+
executionId: execId ?? executionId,
|
|
911
|
+
toolResults: results ?? toolResults
|
|
912
|
+
}),
|
|
913
|
+
errorContext: "Failed to continue worker"
|
|
914
|
+
},
|
|
915
|
+
{ executionId, toolResults },
|
|
916
|
+
options.signal
|
|
917
|
+
);
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
|
|
921
|
+
// src/client.ts
|
|
922
|
+
var OctavusClient = class {
|
|
923
|
+
agents;
|
|
924
|
+
agentSessions;
|
|
925
|
+
files;
|
|
926
|
+
workers;
|
|
927
|
+
baseUrl;
|
|
928
|
+
apiKey;
|
|
929
|
+
traceModelRequests;
|
|
930
|
+
constructor(config) {
|
|
931
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
932
|
+
this.apiKey = config.apiKey;
|
|
933
|
+
this.traceModelRequests = config.traceModelRequests ?? false;
|
|
934
|
+
const apiConfig = {
|
|
935
|
+
baseUrl: this.baseUrl,
|
|
936
|
+
getHeaders: () => this.getHeaders()
|
|
937
|
+
};
|
|
938
|
+
this.agents = new AgentsApi(apiConfig);
|
|
939
|
+
this.agentSessions = new AgentSessionsApi(apiConfig);
|
|
940
|
+
this.files = new FilesApi(apiConfig);
|
|
941
|
+
this.workers = new WorkersApi(apiConfig);
|
|
942
|
+
}
|
|
943
|
+
getHeaders() {
|
|
944
|
+
const headers = {
|
|
945
|
+
"Content-Type": "application/json"
|
|
946
|
+
};
|
|
947
|
+
if (this.apiKey) {
|
|
948
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
949
|
+
}
|
|
950
|
+
if (this.traceModelRequests) {
|
|
951
|
+
headers["X-Octavus-Trace"] = "true";
|
|
952
|
+
}
|
|
953
|
+
return headers;
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
// src/resource.ts
|
|
958
|
+
var Resource = class {
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
// src/normalize-images.ts
|
|
962
|
+
var import_core4 = require("@octavus/core");
|
|
963
|
+
var IMAGE_EXTENSIONS = {
|
|
964
|
+
"image/png": "png",
|
|
965
|
+
"image/jpeg": "jpg",
|
|
966
|
+
"image/webp": "webp",
|
|
967
|
+
"image/gif": "gif",
|
|
968
|
+
"image/svg+xml": "svg"
|
|
969
|
+
};
|
|
970
|
+
function getExtensionFromMediaType(mediaType) {
|
|
971
|
+
return IMAGE_EXTENSIONS[mediaType] ?? "png";
|
|
972
|
+
}
|
|
973
|
+
function isImagePart(part) {
|
|
974
|
+
return typeof part === "object" && part !== null && part.type === "image" && typeof part.data === "string";
|
|
975
|
+
}
|
|
976
|
+
function hasImageParts(value) {
|
|
977
|
+
return Array.isArray(value) && value.some(isImagePart);
|
|
978
|
+
}
|
|
979
|
+
function base64ToArrayBuffer(base64) {
|
|
980
|
+
const binary = atob(base64);
|
|
981
|
+
const buffer = new ArrayBuffer(binary.length);
|
|
982
|
+
const view = new Uint8Array(buffer);
|
|
983
|
+
for (let i = 0; i < binary.length; i++) {
|
|
984
|
+
view[i] = binary.charCodeAt(i);
|
|
985
|
+
}
|
|
986
|
+
return buffer;
|
|
987
|
+
}
|
|
988
|
+
async function normalizeToolResultImages(toolResults, filesApi, sessionId) {
|
|
989
|
+
for (const toolResult of toolResults) {
|
|
990
|
+
if (toolResult.outputVariable) continue;
|
|
991
|
+
if (!hasImageParts(toolResult.result)) continue;
|
|
992
|
+
const parts = toolResult.result;
|
|
993
|
+
const files = toolResult.files ? [...toolResult.files] : [];
|
|
994
|
+
const imageIndices = [];
|
|
995
|
+
const imageBuffers = [];
|
|
996
|
+
const uploadRequests = [];
|
|
997
|
+
for (let i = 0; i < parts.length; i++) {
|
|
998
|
+
const part = parts[i];
|
|
999
|
+
if (!isImagePart(part)) continue;
|
|
1000
|
+
const buffer = base64ToArrayBuffer(part.data);
|
|
1001
|
+
const mimeType = part.mimeType || "image/png";
|
|
1002
|
+
imageIndices.push(i);
|
|
1003
|
+
imageBuffers.push(buffer);
|
|
1004
|
+
uploadRequests.push({
|
|
1005
|
+
filename: `image-${(0, import_core4.generateId)()}.${getExtensionFromMediaType(mimeType)}`,
|
|
1006
|
+
mediaType: mimeType,
|
|
1007
|
+
size: buffer.byteLength
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
if (uploadRequests.length === 0) continue;
|
|
1011
|
+
let uploadInfos;
|
|
1012
|
+
try {
|
|
1013
|
+
const response = await filesApi.getUploadUrls(sessionId, uploadRequests);
|
|
1014
|
+
uploadInfos = response.files;
|
|
1015
|
+
} catch {
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
const uploadResults = await Promise.allSettled(
|
|
1019
|
+
uploadInfos.map(
|
|
1020
|
+
(info, i) => fetch(info.uploadUrl, {
|
|
1021
|
+
method: "PUT",
|
|
1022
|
+
body: imageBuffers[i],
|
|
1023
|
+
headers: { "Content-Type": uploadRequests[i].mediaType }
|
|
1024
|
+
})
|
|
1025
|
+
)
|
|
1026
|
+
);
|
|
1027
|
+
const summaryParts = [];
|
|
1028
|
+
let imageIdx = 0;
|
|
1029
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1030
|
+
if (imageIdx < imageIndices.length && imageIndices[imageIdx] === i) {
|
|
1031
|
+
const uploadResult = uploadResults[imageIdx];
|
|
1032
|
+
const info = uploadInfos[imageIdx];
|
|
1033
|
+
const request = uploadRequests[imageIdx];
|
|
1034
|
+
const buf = imageBuffers[imageIdx];
|
|
1035
|
+
if (uploadResult.status === "fulfilled" && uploadResult.value.ok) {
|
|
1036
|
+
files.push({
|
|
1037
|
+
id: info.id,
|
|
1038
|
+
mediaType: request.mediaType,
|
|
1039
|
+
url: info.downloadUrl,
|
|
1040
|
+
filename: request.filename,
|
|
1041
|
+
size: buf.byteLength
|
|
1042
|
+
});
|
|
1043
|
+
summaryParts.push({
|
|
1044
|
+
type: "image",
|
|
1045
|
+
mediaType: request.mediaType,
|
|
1046
|
+
size: buf.byteLength,
|
|
1047
|
+
url: info.downloadUrl
|
|
1048
|
+
});
|
|
1049
|
+
} else {
|
|
1050
|
+
summaryParts.push(parts[i]);
|
|
1051
|
+
}
|
|
1052
|
+
imageIdx += 1;
|
|
1053
|
+
} else {
|
|
1054
|
+
summaryParts.push(parts[i]);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
toolResult.files = files;
|
|
1058
|
+
toolResult.result = summaryParts;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// src/index.ts
|
|
1063
|
+
var import_core5 = require("@octavus/core");
|
|
1064
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1065
|
+
0 && (module.exports = {
|
|
1066
|
+
AgentSession,
|
|
1067
|
+
AgentSessionsApi,
|
|
1068
|
+
AgentsApi,
|
|
1069
|
+
ApiError,
|
|
1070
|
+
AppError,
|
|
1071
|
+
ConflictError,
|
|
1072
|
+
FilesApi,
|
|
1073
|
+
ForbiddenError,
|
|
1074
|
+
MAIN_THREAD,
|
|
1075
|
+
NotFoundError,
|
|
1076
|
+
OCTAVUS_SKILL_TOOLS,
|
|
1077
|
+
OctavusClient,
|
|
1078
|
+
OctavusError,
|
|
1079
|
+
Resource,
|
|
1080
|
+
ValidationError,
|
|
1081
|
+
WorkerError,
|
|
1082
|
+
WorkersApi,
|
|
1083
|
+
createApiErrorEvent,
|
|
1084
|
+
createErrorEvent,
|
|
1085
|
+
createInternalErrorEvent,
|
|
1086
|
+
errorToStreamEvent,
|
|
1087
|
+
generateId,
|
|
1088
|
+
getSkillSlugFromToolCall,
|
|
1089
|
+
isAbortError,
|
|
1090
|
+
isAuthenticationError,
|
|
1091
|
+
isFileReference,
|
|
1092
|
+
isFileReferenceArray,
|
|
1093
|
+
isMainThread,
|
|
1094
|
+
isOctavusSkillTool,
|
|
1095
|
+
isOtherThread,
|
|
1096
|
+
isProviderError,
|
|
1097
|
+
isRateLimitError,
|
|
1098
|
+
isRetryableError,
|
|
1099
|
+
isToolError,
|
|
1100
|
+
isValidationError,
|
|
1101
|
+
normalizeToolResultImages,
|
|
1102
|
+
resolveThread,
|
|
1103
|
+
safeParseStreamEvent,
|
|
1104
|
+
safeParseUIMessage,
|
|
1105
|
+
safeParseUIMessages,
|
|
1106
|
+
threadForPart,
|
|
1107
|
+
toSSEStream
|
|
1108
|
+
});
|
|
1109
|
+
//# sourceMappingURL=index.cjs.map
|