@webless/agent 0.6.2 → 0.6.4
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/README.md +22 -41
- package/dist/{chunk-HXKD46I6.js → chunk-Y4DCEJDC.js} +2542 -665
- package/dist/chunk-Y4DCEJDC.js.map +1 -0
- package/dist/embed.cjs +2281 -409
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +770 -140
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +9 -3
- package/dist/embed.d.ts +9 -3
- package/dist/embed.js +6 -2
- package/dist/embed.js.map +1 -1
- package/dist/index.cjs +431 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +102 -1
- package/dist/index.d.ts +102 -1
- package/dist/index.js +428 -23
- package/dist/index.js.map +1 -1
- package/dist/manifest-Cad-TQJw.d.cts +391 -0
- package/dist/manifest-Cad-TQJw.d.ts +391 -0
- package/dist/react.cjs +2268 -408
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +770 -140
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +13 -7
- package/dist/react.d.ts +13 -7
- package/dist/react.js +5 -1
- package/package.json +1 -1
- package/dist/chunk-HXKD46I6.js.map +0 -1
- package/dist/manifest-H6oHn8Pc.d.cts +0 -185
- package/dist/manifest-H6oHn8Pc.d.ts +0 -185
package/dist/index.js
CHANGED
|
@@ -7,6 +7,26 @@ import {
|
|
|
7
7
|
// src/runtime/capability.ts
|
|
8
8
|
import { ClientError } from "eve/client";
|
|
9
9
|
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
10
|
+
var LOCAL_LOOPBACK_ORIGINS = [
|
|
11
|
+
"http://127.0.0.1:3010",
|
|
12
|
+
"http://127.0.0.1:3001"
|
|
13
|
+
];
|
|
14
|
+
function isLoopbackRuntimeOrigin(origin) {
|
|
15
|
+
try {
|
|
16
|
+
const host = new URL(origin).hostname;
|
|
17
|
+
return host === "127.0.0.1" || host === "localhost";
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function localBootstrapOrigins(origin) {
|
|
23
|
+
const normalized = origin.replace(/\/$/, "");
|
|
24
|
+
if (!isLoopbackRuntimeOrigin(normalized)) return [normalized];
|
|
25
|
+
return [
|
|
26
|
+
normalized,
|
|
27
|
+
...LOCAL_LOOPBACK_ORIGINS.filter((candidate) => candidate !== normalized)
|
|
28
|
+
];
|
|
29
|
+
}
|
|
10
30
|
function isRecord(value) {
|
|
11
31
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
32
|
}
|
|
@@ -53,20 +73,31 @@ function createAgentRuntimeCapability(options) {
|
|
|
53
73
|
);
|
|
54
74
|
}
|
|
55
75
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
const bootstrapBody = JSON.stringify({
|
|
77
|
+
clientSessionId: options.visitorSessionId,
|
|
78
|
+
indexId: options.indexId,
|
|
79
|
+
...previewBuildId ? { previewBuildId } : {},
|
|
80
|
+
...previewGrant ? { previewGrant } : {},
|
|
81
|
+
version: options.version
|
|
82
|
+
});
|
|
83
|
+
const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
|
|
84
|
+
body: bootstrapBody,
|
|
85
|
+
headers: { "content-type": "application/json" },
|
|
86
|
+
method: "POST"
|
|
87
|
+
});
|
|
88
|
+
let response;
|
|
89
|
+
let lastError;
|
|
90
|
+
for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
|
|
91
|
+
try {
|
|
92
|
+
response = await postBootstrap(origin);
|
|
93
|
+
break;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
lastError = error;
|
|
68
96
|
}
|
|
69
|
-
|
|
97
|
+
}
|
|
98
|
+
if (!response) {
|
|
99
|
+
throw lastError instanceof Error ? lastError : new Error("Agent Runtime is unavailable.");
|
|
100
|
+
}
|
|
70
101
|
if (!response.ok) {
|
|
71
102
|
throw new Error(await readBootstrapError(response));
|
|
72
103
|
}
|
|
@@ -237,6 +268,226 @@ function clearPersistedAgentSession(visitorSessionId, options) {
|
|
|
237
268
|
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
238
269
|
}
|
|
239
270
|
|
|
271
|
+
// src/runtime/tool-ui.ts
|
|
272
|
+
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
273
|
+
function isRecord2(value) {
|
|
274
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
275
|
+
}
|
|
276
|
+
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
277
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
281
|
+
if (typeof value !== "object") return false;
|
|
282
|
+
if (seen.has(value)) return false;
|
|
283
|
+
seen.add(value);
|
|
284
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, seen)) : Object.entries(value).every(
|
|
285
|
+
([key, item]) => typeof key === "string" && isJsonValue(item, seen)
|
|
286
|
+
);
|
|
287
|
+
seen.delete(value);
|
|
288
|
+
return valid;
|
|
289
|
+
}
|
|
290
|
+
function boundedString(value, max) {
|
|
291
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > max) {
|
|
292
|
+
return void 0;
|
|
293
|
+
}
|
|
294
|
+
return value.trim();
|
|
295
|
+
}
|
|
296
|
+
function numberValue(value) {
|
|
297
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
298
|
+
}
|
|
299
|
+
function integerValue(value) {
|
|
300
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8e3 ? value : void 0;
|
|
301
|
+
}
|
|
302
|
+
function isFieldKind(value) {
|
|
303
|
+
return typeof value === "string" && [
|
|
304
|
+
"text",
|
|
305
|
+
"textarea",
|
|
306
|
+
"email",
|
|
307
|
+
"number",
|
|
308
|
+
"select",
|
|
309
|
+
"multi-select",
|
|
310
|
+
"checkbox",
|
|
311
|
+
"confirmation",
|
|
312
|
+
"radio",
|
|
313
|
+
"date",
|
|
314
|
+
"time",
|
|
315
|
+
"date-time",
|
|
316
|
+
"calendar",
|
|
317
|
+
"range",
|
|
318
|
+
"json"
|
|
319
|
+
].includes(value);
|
|
320
|
+
}
|
|
321
|
+
function parseField(value) {
|
|
322
|
+
if (!isRecord2(value)) return null;
|
|
323
|
+
if (!hasOnlyKeys(value, [
|
|
324
|
+
"description",
|
|
325
|
+
"kind",
|
|
326
|
+
"label",
|
|
327
|
+
"max",
|
|
328
|
+
"maxItems",
|
|
329
|
+
"maxLength",
|
|
330
|
+
"min",
|
|
331
|
+
"minLength",
|
|
332
|
+
"options",
|
|
333
|
+
"path",
|
|
334
|
+
"placeholder",
|
|
335
|
+
"required",
|
|
336
|
+
"step",
|
|
337
|
+
"defaultValue"
|
|
338
|
+
])) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
if (!isFieldKind(value.kind)) return null;
|
|
342
|
+
const path = boundedString(value.path, 160);
|
|
343
|
+
const label = boundedString(value.label, 160);
|
|
344
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
345
|
+
const placeholder = value.placeholder === void 0 || typeof value.placeholder !== "string" ? void 0 : value.placeholder;
|
|
346
|
+
const required = value.required === void 0 || typeof value.required !== "boolean" ? void 0 : value.required;
|
|
347
|
+
const min = value.min === void 0 ? void 0 : numberValue(value.min);
|
|
348
|
+
const max = value.max === void 0 ? void 0 : numberValue(value.max);
|
|
349
|
+
const maxItems = value.maxItems === void 0 ? void 0 : integerValue(value.maxItems);
|
|
350
|
+
const step = value.step === void 0 ? void 0 : numberValue(value.step);
|
|
351
|
+
const minLength = value.minLength === void 0 ? void 0 : integerValue(value.minLength);
|
|
352
|
+
const maxLength = value.maxLength === void 0 ? void 0 : integerValue(value.maxLength);
|
|
353
|
+
const defaultValue = value.defaultValue === void 0 || !isJsonValue(value.defaultValue) ? void 0 : value.defaultValue;
|
|
354
|
+
if (!path || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || !label) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
if (value.description !== void 0 && !description) return null;
|
|
358
|
+
if (value.placeholder !== void 0 && placeholder === void 0) return null;
|
|
359
|
+
if (value.required !== void 0 && required === void 0) return null;
|
|
360
|
+
if (value.min !== void 0 && min === void 0) return null;
|
|
361
|
+
if (value.max !== void 0 && max === void 0) return null;
|
|
362
|
+
if (value.maxItems !== void 0 && (maxItems === void 0 || maxItems < 1 || maxItems > 100))
|
|
363
|
+
return null;
|
|
364
|
+
if (value.step !== void 0 && step === void 0) return null;
|
|
365
|
+
if (value.minLength !== void 0 && minLength === void 0) return null;
|
|
366
|
+
if (value.maxLength !== void 0 && maxLength === void 0) return null;
|
|
367
|
+
if (value.defaultValue !== void 0 && defaultValue === void 0)
|
|
368
|
+
return null;
|
|
369
|
+
if (value.options !== void 0) {
|
|
370
|
+
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
371
|
+
return null;
|
|
372
|
+
for (const option of value.options) {
|
|
373
|
+
if (!isRecord2(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
kind: value.kind,
|
|
380
|
+
path,
|
|
381
|
+
label,
|
|
382
|
+
...description ? { description } : {},
|
|
383
|
+
...placeholder !== void 0 ? { placeholder } : {},
|
|
384
|
+
...required !== void 0 ? { required } : {},
|
|
385
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
386
|
+
...value.options !== void 0 ? { options: value.options } : {},
|
|
387
|
+
...min !== void 0 ? { min } : {},
|
|
388
|
+
...max !== void 0 ? { max } : {},
|
|
389
|
+
...maxItems !== void 0 ? { maxItems } : {},
|
|
390
|
+
...step !== void 0 ? { step } : {},
|
|
391
|
+
...minLength !== void 0 ? { minLength } : {},
|
|
392
|
+
...maxLength !== void 0 ? { maxLength } : {}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function parseStep(value) {
|
|
396
|
+
if (!isRecord2(value)) return null;
|
|
397
|
+
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
const id = boundedString(value.id, 80);
|
|
401
|
+
const label = boundedString(value.label, 160);
|
|
402
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
403
|
+
if (!id || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(id) || !label || value.description !== void 0 && !description || !Array.isArray(value.fieldPaths) || value.fieldPaths.length < 1 || value.fieldPaths.length > 32 || value.fieldPaths.some(
|
|
404
|
+
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
405
|
+
)) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
id,
|
|
410
|
+
label,
|
|
411
|
+
fieldPaths: value.fieldPaths,
|
|
412
|
+
...description ? { description } : {}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function parseAction(value) {
|
|
416
|
+
if (!isRecord2(value)) return null;
|
|
417
|
+
if (!hasOnlyKeys(value, ["id", "label"])) return null;
|
|
418
|
+
const label = boundedString(value.label, 80);
|
|
419
|
+
if (value.id !== "submit" && value.id !== "back" && value.id !== "next" && value.id !== "reset" || !label) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
id: value.id,
|
|
424
|
+
label
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function parseAgentToolUiSurface(value) {
|
|
428
|
+
if (!isRecord2(value) || value.schemaVersion !== AGENT_TOOL_UI_SCHEMA_VERSION)
|
|
429
|
+
return null;
|
|
430
|
+
if (!hasOnlyKeys(value, [
|
|
431
|
+
"actions",
|
|
432
|
+
"description",
|
|
433
|
+
"fields",
|
|
434
|
+
"id",
|
|
435
|
+
"operationId",
|
|
436
|
+
"requestId",
|
|
437
|
+
"schemaVersion",
|
|
438
|
+
"steps",
|
|
439
|
+
"submitLabel",
|
|
440
|
+
"title",
|
|
441
|
+
"toolSlug",
|
|
442
|
+
"values"
|
|
443
|
+
])) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
const id = boundedString(value.id, 200);
|
|
447
|
+
const title = boundedString(value.title, 200);
|
|
448
|
+
const toolSlug = boundedString(value.toolSlug, 200);
|
|
449
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
450
|
+
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
451
|
+
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
452
|
+
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
453
|
+
if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
const fields = value.fields.map(parseField);
|
|
457
|
+
if (fields.some((field) => field === null)) return null;
|
|
458
|
+
const steps = value.steps === void 0 ? void 0 : Array.isArray(value.steps) && value.steps.length <= 8 ? value.steps.map(parseStep) : null;
|
|
459
|
+
if (steps?.some((step) => step === null)) return null;
|
|
460
|
+
const actions = value.actions === void 0 ? void 0 : Array.isArray(value.actions) && value.actions.length <= 8 ? value.actions.map(parseAction) : null;
|
|
461
|
+
if (actions?.some((action) => action === null)) return null;
|
|
462
|
+
if (value.description !== void 0 && !description) return null;
|
|
463
|
+
if (value.operationId !== void 0 && !operationId) return null;
|
|
464
|
+
if (value.requestId !== void 0 && !requestId) return null;
|
|
465
|
+
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
466
|
+
const values = value.values !== void 0 && isRecord2(value.values) ? value.values : void 0;
|
|
467
|
+
if (value.values !== void 0) {
|
|
468
|
+
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
return {
|
|
472
|
+
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
473
|
+
id,
|
|
474
|
+
title,
|
|
475
|
+
toolSlug,
|
|
476
|
+
fields,
|
|
477
|
+
...actions ? { actions } : {},
|
|
478
|
+
...description ? { description } : {},
|
|
479
|
+
...operationId ? { operationId } : {},
|
|
480
|
+
...requestId ? { requestId } : {},
|
|
481
|
+
...submitLabel ? { submitLabel } : {},
|
|
482
|
+
...steps ? { steps } : {},
|
|
483
|
+
...values ? { values } : {}
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
function hasOnlyKeys(value, allowed) {
|
|
487
|
+
const allowedKeys = new Set(allowed);
|
|
488
|
+
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
489
|
+
}
|
|
490
|
+
|
|
240
491
|
// src/runtime/client.ts
|
|
241
492
|
function isTurnBoundary(event) {
|
|
242
493
|
return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
|
|
@@ -249,12 +500,8 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
249
500
|
if (event.type === "message.completed") {
|
|
250
501
|
handlers.onComplete?.();
|
|
251
502
|
}
|
|
252
|
-
if (event.type === "action.result")
|
|
253
|
-
|
|
254
|
-
if (result && typeof result === "object" && "output" in result) {
|
|
255
|
-
handlers.onActionResult?.(result.output);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
503
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
504
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
258
505
|
if (event.type !== "message.appended") return rendered;
|
|
259
506
|
const { messageDelta, messageSoFar } = event.data;
|
|
260
507
|
let delta = messageDelta;
|
|
@@ -265,9 +512,45 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
265
512
|
} else if (messageDelta) {
|
|
266
513
|
next += messageDelta;
|
|
267
514
|
}
|
|
268
|
-
if (delta) handlers.onDelta(delta);
|
|
515
|
+
if (delta) handlers.onDelta?.(delta);
|
|
269
516
|
return next;
|
|
270
517
|
}
|
|
518
|
+
function emitActionResult(event, handlers) {
|
|
519
|
+
const result = event.data.result;
|
|
520
|
+
if (result.kind === "tool-result") {
|
|
521
|
+
handlers.onToolResult?.({
|
|
522
|
+
callId: result.callId,
|
|
523
|
+
toolName: result.toolName,
|
|
524
|
+
status: event.data.status,
|
|
525
|
+
output: result.output,
|
|
526
|
+
...event.data.error ? { error: event.data.error } : {}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
if ("output" in result) handlers.onActionResult?.(result.output);
|
|
530
|
+
}
|
|
531
|
+
function emitInputRequests(event, handlers) {
|
|
532
|
+
handlers.onInputRequest?.(
|
|
533
|
+
event.data.requests.map((request) => {
|
|
534
|
+
const ui = parseAgentToolUiSurface(
|
|
535
|
+
request.ui
|
|
536
|
+
);
|
|
537
|
+
return {
|
|
538
|
+
requestId: request.requestId,
|
|
539
|
+
kind: request.kind,
|
|
540
|
+
prompt: request.prompt,
|
|
541
|
+
...request.display ? { display: request.display } : {},
|
|
542
|
+
...request.allowFreeform !== void 0 ? { allowFreeform: request.allowFreeform } : {},
|
|
543
|
+
...request.options ? { options: request.options } : {},
|
|
544
|
+
...ui ? { ui } : {},
|
|
545
|
+
action: {
|
|
546
|
+
callId: request.action.callId,
|
|
547
|
+
kind: "tool-call",
|
|
548
|
+
toolName: request.action.toolName
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
})
|
|
552
|
+
);
|
|
553
|
+
}
|
|
271
554
|
function isResumeTurnMessage(received, candidate) {
|
|
272
555
|
if (received === candidate) return true;
|
|
273
556
|
return Boolean(candidate) && received.endsWith(`
|
|
@@ -543,9 +826,11 @@ var AgentSession = class {
|
|
|
543
826
|
let streamIndex = session?.state.streamIndex ?? 0;
|
|
544
827
|
let rendered = "";
|
|
545
828
|
const workItems = /* @__PURE__ */ new Map();
|
|
829
|
+
let requestedInput = false;
|
|
546
830
|
try {
|
|
547
831
|
for await (const event of response) {
|
|
548
832
|
if (signal.aborted) break;
|
|
833
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
549
834
|
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
550
835
|
streamIndex += 1;
|
|
551
836
|
if (session) {
|
|
@@ -563,7 +848,7 @@ var AgentSession = class {
|
|
|
563
848
|
this.persistSessionCursor(session);
|
|
564
849
|
}
|
|
565
850
|
}
|
|
566
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
851
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
567
852
|
throw new Error("Empty response from runtime");
|
|
568
853
|
}
|
|
569
854
|
return rendered.trim();
|
|
@@ -583,6 +868,9 @@ var AgentSession = class {
|
|
|
583
868
|
() => attached.snapshot({ signal })
|
|
584
869
|
);
|
|
585
870
|
const turnEvents = latestTurnEvents(snapshot.events);
|
|
871
|
+
const hasInputRequest = turnEvents.some(
|
|
872
|
+
(event) => event.type === "input.requested"
|
|
873
|
+
);
|
|
586
874
|
const received = turnEvents[0];
|
|
587
875
|
const lastSent = persisted.lastMessage;
|
|
588
876
|
const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
|
|
@@ -593,6 +881,8 @@ var AgentSession = class {
|
|
|
593
881
|
const workItems = /* @__PURE__ */ new Map();
|
|
594
882
|
for (const event of turnEvents) {
|
|
595
883
|
applyWorkEvent(event, handlers, workItems);
|
|
884
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
885
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
596
886
|
}
|
|
597
887
|
if (rendered.startsWith(initialText)) {
|
|
598
888
|
const missedText = rendered.slice(initialText.length);
|
|
@@ -622,7 +912,9 @@ var AgentSession = class {
|
|
|
622
912
|
);
|
|
623
913
|
}
|
|
624
914
|
handlers.onComplete?.();
|
|
625
|
-
if (!rendered.trim()
|
|
915
|
+
if (!rendered.trim() && !hasInputRequest) {
|
|
916
|
+
throw new Error("Empty response from runtime");
|
|
917
|
+
}
|
|
626
918
|
return rendered.trim();
|
|
627
919
|
}
|
|
628
920
|
let streamIndex = snapshot.session.streamIndex;
|
|
@@ -641,7 +933,55 @@ var AgentSession = class {
|
|
|
641
933
|
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
642
934
|
this.session = session;
|
|
643
935
|
this.persistSessionCursor(session);
|
|
644
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
936
|
+
if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
|
|
937
|
+
throw new Error("Empty response from runtime");
|
|
938
|
+
}
|
|
939
|
+
return rendered.trim();
|
|
940
|
+
}
|
|
941
|
+
async respondTurn(responses, signal, handlers) {
|
|
942
|
+
const client = this.ensureClient();
|
|
943
|
+
const session = this.session ?? this.attachPersistedSession(client);
|
|
944
|
+
if (!session) {
|
|
945
|
+
throw new Error("No active session is waiting for input.");
|
|
946
|
+
}
|
|
947
|
+
this.session = session;
|
|
948
|
+
const inputResponses = responses.map(
|
|
949
|
+
({ requestId, optionId, text }) => ({
|
|
950
|
+
requestId,
|
|
951
|
+
...optionId ? { optionId } : {},
|
|
952
|
+
...text ? { text } : {}
|
|
953
|
+
})
|
|
954
|
+
);
|
|
955
|
+
const response = await withCapabilityRefresh(
|
|
956
|
+
this.capability,
|
|
957
|
+
() => session.respond(inputResponses, { signal })
|
|
958
|
+
);
|
|
959
|
+
this.activeResponse = response;
|
|
960
|
+
let streamIndex = session.state.streamIndex;
|
|
961
|
+
let rendered = "";
|
|
962
|
+
let requestedInput = false;
|
|
963
|
+
const workItems = /* @__PURE__ */ new Map();
|
|
964
|
+
try {
|
|
965
|
+
for await (const event of response) {
|
|
966
|
+
if (signal.aborted) break;
|
|
967
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
968
|
+
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
969
|
+
streamIndex += 1;
|
|
970
|
+
savePersistedAgentSession(
|
|
971
|
+
this.visitorSessionId,
|
|
972
|
+
session.state.sessionId,
|
|
973
|
+
streamIndex,
|
|
974
|
+
this.storeOptions
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
} finally {
|
|
978
|
+
this.activeResponse = void 0;
|
|
979
|
+
this.session = client.sessions.attach(session.state.sessionId, {
|
|
980
|
+
streamIndex
|
|
981
|
+
});
|
|
982
|
+
this.persistSessionCursor(this.session);
|
|
983
|
+
}
|
|
984
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
645
985
|
throw new Error("Empty response from runtime");
|
|
646
986
|
}
|
|
647
987
|
return rendered.trim();
|
|
@@ -701,6 +1041,11 @@ function createAgentClient(options) {
|
|
|
701
1041
|
resumeOptions.handlers,
|
|
702
1042
|
resumeOptions.initialText
|
|
703
1043
|
),
|
|
1044
|
+
respondTurn: (respondOptions) => session.respondTurn(
|
|
1045
|
+
respondOptions.responses,
|
|
1046
|
+
respondOptions.signal ?? new AbortController().signal,
|
|
1047
|
+
respondOptions.handlers
|
|
1048
|
+
),
|
|
704
1049
|
reset: () => session.reset(),
|
|
705
1050
|
cancelActive: () => session.cancelActive(),
|
|
706
1051
|
getActiveSessionId: () => session.getActiveSessionId()
|
|
@@ -750,6 +1095,63 @@ function formatAgentError(error) {
|
|
|
750
1095
|
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
751
1096
|
}
|
|
752
1097
|
|
|
1098
|
+
// src/runtime/tool-result-envelope.ts
|
|
1099
|
+
var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
1100
|
+
"schemaVersion",
|
|
1101
|
+
"output",
|
|
1102
|
+
"presentationKinds",
|
|
1103
|
+
"ui"
|
|
1104
|
+
]);
|
|
1105
|
+
function isRecord3(value) {
|
|
1106
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1107
|
+
}
|
|
1108
|
+
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
1109
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
1110
|
+
return true;
|
|
1111
|
+
}
|
|
1112
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1113
|
+
if (typeof value !== "object") return false;
|
|
1114
|
+
if (seen.has(value)) return false;
|
|
1115
|
+
seen.add(value);
|
|
1116
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue2(item, seen)) : Object.entries(value).every(
|
|
1117
|
+
([key, item]) => typeof key === "string" && isJsonValue2(item, seen)
|
|
1118
|
+
);
|
|
1119
|
+
seen.delete(value);
|
|
1120
|
+
return valid;
|
|
1121
|
+
}
|
|
1122
|
+
function decodeEnvelope(value) {
|
|
1123
|
+
if (typeof value !== "string") return value;
|
|
1124
|
+
try {
|
|
1125
|
+
return JSON.parse(value);
|
|
1126
|
+
} catch {
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
function parseAgentToolResultEnvelope(value) {
|
|
1131
|
+
const decoded = decodeEnvelope(value);
|
|
1132
|
+
if (!isRecord3(decoded)) return null;
|
|
1133
|
+
const keys = Object.keys(decoded);
|
|
1134
|
+
if (keys.some((key) => !ENVELOPE_KEYS.has(key)) || decoded.schemaVersion !== "webless.tool-result.v1" || !isJsonValue2(decoded.output) || !Array.isArray(decoded.presentationKinds) || decoded.presentationKinds.length < 1 || decoded.presentationKinds.length > 16) {
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
const presentationKinds = decoded.presentationKinds.flatMap((kind) => {
|
|
1138
|
+
if (typeof kind !== "string") return [];
|
|
1139
|
+
const normalized = kind.trim();
|
|
1140
|
+
return normalized && normalized.length <= 128 ? [normalized] : [];
|
|
1141
|
+
});
|
|
1142
|
+
if (presentationKinds.length !== decoded.presentationKinds.length) {
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
const ui = decoded.ui === void 0 ? void 0 : parseAgentToolUiSurface(decoded.ui);
|
|
1146
|
+
if (decoded.ui !== void 0 && !ui) return null;
|
|
1147
|
+
return {
|
|
1148
|
+
schemaVersion: "webless.tool-result.v1",
|
|
1149
|
+
output: decoded.output,
|
|
1150
|
+
presentationKinds,
|
|
1151
|
+
...ui ? { ui } : {}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
|
|
753
1155
|
// src/runtime/health.ts
|
|
754
1156
|
async function pingAgentHealth(runtimeOrigin, fetchImpl = fetch) {
|
|
755
1157
|
const healthUrl = agentHealthUrl(runtimeOrigin);
|
|
@@ -771,6 +1173,7 @@ async function pingAgentHealth(runtimeOrigin, fetchImpl = fetch) {
|
|
|
771
1173
|
}
|
|
772
1174
|
}
|
|
773
1175
|
export {
|
|
1176
|
+
AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
774
1177
|
DEFAULT_RUNTIME_ORIGIN,
|
|
775
1178
|
agentHealthUrl,
|
|
776
1179
|
buildAgentStorageKeyPrefix,
|
|
@@ -781,6 +1184,8 @@ export {
|
|
|
781
1184
|
formatAgentError,
|
|
782
1185
|
getOrCreateVisitorSessionId,
|
|
783
1186
|
loadPersistedAgentSession,
|
|
1187
|
+
parseAgentToolResultEnvelope,
|
|
1188
|
+
parseAgentToolUiSurface,
|
|
784
1189
|
pingAgentHealth,
|
|
785
1190
|
resolveAgentRuntimeConfig,
|
|
786
1191
|
savePersistedAgentSession
|