@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.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AGENT_TOOL_UI_SCHEMA_VERSION: () => AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
23
24
|
DEFAULT_RUNTIME_ORIGIN: () => DEFAULT_RUNTIME_ORIGIN,
|
|
24
25
|
agentHealthUrl: () => agentHealthUrl,
|
|
25
26
|
buildAgentStorageKeyPrefix: () => buildAgentStorageKeyPrefix,
|
|
@@ -30,6 +31,8 @@ __export(index_exports, {
|
|
|
30
31
|
formatAgentError: () => formatAgentError,
|
|
31
32
|
getOrCreateVisitorSessionId: () => getOrCreateVisitorSessionId,
|
|
32
33
|
loadPersistedAgentSession: () => loadPersistedAgentSession,
|
|
34
|
+
parseAgentToolResultEnvelope: () => parseAgentToolResultEnvelope,
|
|
35
|
+
parseAgentToolUiSurface: () => parseAgentToolUiSurface,
|
|
33
36
|
pingAgentHealth: () => pingAgentHealth,
|
|
34
37
|
resolveAgentRuntimeConfig: () => resolveAgentRuntimeConfig,
|
|
35
38
|
savePersistedAgentSession: () => savePersistedAgentSession
|
|
@@ -42,6 +45,26 @@ var import_client2 = require("eve/client");
|
|
|
42
45
|
// src/runtime/capability.ts
|
|
43
46
|
var import_client = require("eve/client");
|
|
44
47
|
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
48
|
+
var LOCAL_LOOPBACK_ORIGINS = [
|
|
49
|
+
"http://127.0.0.1:3010",
|
|
50
|
+
"http://127.0.0.1:3001"
|
|
51
|
+
];
|
|
52
|
+
function isLoopbackRuntimeOrigin(origin) {
|
|
53
|
+
try {
|
|
54
|
+
const host = new URL(origin).hostname;
|
|
55
|
+
return host === "127.0.0.1" || host === "localhost";
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function localBootstrapOrigins(origin) {
|
|
61
|
+
const normalized = origin.replace(/\/$/, "");
|
|
62
|
+
if (!isLoopbackRuntimeOrigin(normalized)) return [normalized];
|
|
63
|
+
return [
|
|
64
|
+
normalized,
|
|
65
|
+
...LOCAL_LOOPBACK_ORIGINS.filter((candidate) => candidate !== normalized)
|
|
66
|
+
];
|
|
67
|
+
}
|
|
45
68
|
function isRecord(value) {
|
|
46
69
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47
70
|
}
|
|
@@ -88,20 +111,31 @@ function createAgentRuntimeCapability(options) {
|
|
|
88
111
|
);
|
|
89
112
|
}
|
|
90
113
|
}
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
114
|
+
const bootstrapBody = JSON.stringify({
|
|
115
|
+
clientSessionId: options.visitorSessionId,
|
|
116
|
+
indexId: options.indexId,
|
|
117
|
+
...previewBuildId ? { previewBuildId } : {},
|
|
118
|
+
...previewGrant ? { previewGrant } : {},
|
|
119
|
+
version: options.version
|
|
120
|
+
});
|
|
121
|
+
const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
|
|
122
|
+
body: bootstrapBody,
|
|
123
|
+
headers: { "content-type": "application/json" },
|
|
124
|
+
method: "POST"
|
|
125
|
+
});
|
|
126
|
+
let response;
|
|
127
|
+
let lastError;
|
|
128
|
+
for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
|
|
129
|
+
try {
|
|
130
|
+
response = await postBootstrap(origin);
|
|
131
|
+
break;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
lastError = error;
|
|
103
134
|
}
|
|
104
|
-
|
|
135
|
+
}
|
|
136
|
+
if (!response) {
|
|
137
|
+
throw lastError instanceof Error ? lastError : new Error("Agent Runtime is unavailable.");
|
|
138
|
+
}
|
|
105
139
|
if (!response.ok) {
|
|
106
140
|
throw new Error(await readBootstrapError(response));
|
|
107
141
|
}
|
|
@@ -272,6 +306,226 @@ function clearPersistedAgentSession(visitorSessionId, options) {
|
|
|
272
306
|
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
273
307
|
}
|
|
274
308
|
|
|
309
|
+
// src/runtime/tool-ui.ts
|
|
310
|
+
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
311
|
+
function isRecord2(value) {
|
|
312
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
313
|
+
}
|
|
314
|
+
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
315
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
319
|
+
if (typeof value !== "object") return false;
|
|
320
|
+
if (seen.has(value)) return false;
|
|
321
|
+
seen.add(value);
|
|
322
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, seen)) : Object.entries(value).every(
|
|
323
|
+
([key, item]) => typeof key === "string" && isJsonValue(item, seen)
|
|
324
|
+
);
|
|
325
|
+
seen.delete(value);
|
|
326
|
+
return valid;
|
|
327
|
+
}
|
|
328
|
+
function boundedString(value, max) {
|
|
329
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > max) {
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
return value.trim();
|
|
333
|
+
}
|
|
334
|
+
function numberValue(value) {
|
|
335
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
336
|
+
}
|
|
337
|
+
function integerValue(value) {
|
|
338
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8e3 ? value : void 0;
|
|
339
|
+
}
|
|
340
|
+
function isFieldKind(value) {
|
|
341
|
+
return typeof value === "string" && [
|
|
342
|
+
"text",
|
|
343
|
+
"textarea",
|
|
344
|
+
"email",
|
|
345
|
+
"number",
|
|
346
|
+
"select",
|
|
347
|
+
"multi-select",
|
|
348
|
+
"checkbox",
|
|
349
|
+
"confirmation",
|
|
350
|
+
"radio",
|
|
351
|
+
"date",
|
|
352
|
+
"time",
|
|
353
|
+
"date-time",
|
|
354
|
+
"calendar",
|
|
355
|
+
"range",
|
|
356
|
+
"json"
|
|
357
|
+
].includes(value);
|
|
358
|
+
}
|
|
359
|
+
function parseField(value) {
|
|
360
|
+
if (!isRecord2(value)) return null;
|
|
361
|
+
if (!hasOnlyKeys(value, [
|
|
362
|
+
"description",
|
|
363
|
+
"kind",
|
|
364
|
+
"label",
|
|
365
|
+
"max",
|
|
366
|
+
"maxItems",
|
|
367
|
+
"maxLength",
|
|
368
|
+
"min",
|
|
369
|
+
"minLength",
|
|
370
|
+
"options",
|
|
371
|
+
"path",
|
|
372
|
+
"placeholder",
|
|
373
|
+
"required",
|
|
374
|
+
"step",
|
|
375
|
+
"defaultValue"
|
|
376
|
+
])) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
if (!isFieldKind(value.kind)) return null;
|
|
380
|
+
const path = boundedString(value.path, 160);
|
|
381
|
+
const label = boundedString(value.label, 160);
|
|
382
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
383
|
+
const placeholder = value.placeholder === void 0 || typeof value.placeholder !== "string" ? void 0 : value.placeholder;
|
|
384
|
+
const required = value.required === void 0 || typeof value.required !== "boolean" ? void 0 : value.required;
|
|
385
|
+
const min = value.min === void 0 ? void 0 : numberValue(value.min);
|
|
386
|
+
const max = value.max === void 0 ? void 0 : numberValue(value.max);
|
|
387
|
+
const maxItems = value.maxItems === void 0 ? void 0 : integerValue(value.maxItems);
|
|
388
|
+
const step = value.step === void 0 ? void 0 : numberValue(value.step);
|
|
389
|
+
const minLength = value.minLength === void 0 ? void 0 : integerValue(value.minLength);
|
|
390
|
+
const maxLength = value.maxLength === void 0 ? void 0 : integerValue(value.maxLength);
|
|
391
|
+
const defaultValue = value.defaultValue === void 0 || !isJsonValue(value.defaultValue) ? void 0 : value.defaultValue;
|
|
392
|
+
if (!path || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || !label) {
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
if (value.description !== void 0 && !description) return null;
|
|
396
|
+
if (value.placeholder !== void 0 && placeholder === void 0) return null;
|
|
397
|
+
if (value.required !== void 0 && required === void 0) return null;
|
|
398
|
+
if (value.min !== void 0 && min === void 0) return null;
|
|
399
|
+
if (value.max !== void 0 && max === void 0) return null;
|
|
400
|
+
if (value.maxItems !== void 0 && (maxItems === void 0 || maxItems < 1 || maxItems > 100))
|
|
401
|
+
return null;
|
|
402
|
+
if (value.step !== void 0 && step === void 0) return null;
|
|
403
|
+
if (value.minLength !== void 0 && minLength === void 0) return null;
|
|
404
|
+
if (value.maxLength !== void 0 && maxLength === void 0) return null;
|
|
405
|
+
if (value.defaultValue !== void 0 && defaultValue === void 0)
|
|
406
|
+
return null;
|
|
407
|
+
if (value.options !== void 0) {
|
|
408
|
+
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
409
|
+
return null;
|
|
410
|
+
for (const option of value.options) {
|
|
411
|
+
if (!isRecord2(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
kind: value.kind,
|
|
418
|
+
path,
|
|
419
|
+
label,
|
|
420
|
+
...description ? { description } : {},
|
|
421
|
+
...placeholder !== void 0 ? { placeholder } : {},
|
|
422
|
+
...required !== void 0 ? { required } : {},
|
|
423
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
424
|
+
...value.options !== void 0 ? { options: value.options } : {},
|
|
425
|
+
...min !== void 0 ? { min } : {},
|
|
426
|
+
...max !== void 0 ? { max } : {},
|
|
427
|
+
...maxItems !== void 0 ? { maxItems } : {},
|
|
428
|
+
...step !== void 0 ? { step } : {},
|
|
429
|
+
...minLength !== void 0 ? { minLength } : {},
|
|
430
|
+
...maxLength !== void 0 ? { maxLength } : {}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
function parseStep(value) {
|
|
434
|
+
if (!isRecord2(value)) return null;
|
|
435
|
+
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
const id = boundedString(value.id, 80);
|
|
439
|
+
const label = boundedString(value.label, 160);
|
|
440
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
441
|
+
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(
|
|
442
|
+
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
443
|
+
)) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
id,
|
|
448
|
+
label,
|
|
449
|
+
fieldPaths: value.fieldPaths,
|
|
450
|
+
...description ? { description } : {}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function parseAction(value) {
|
|
454
|
+
if (!isRecord2(value)) return null;
|
|
455
|
+
if (!hasOnlyKeys(value, ["id", "label"])) return null;
|
|
456
|
+
const label = boundedString(value.label, 80);
|
|
457
|
+
if (value.id !== "submit" && value.id !== "back" && value.id !== "next" && value.id !== "reset" || !label) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
id: value.id,
|
|
462
|
+
label
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
function parseAgentToolUiSurface(value) {
|
|
466
|
+
if (!isRecord2(value) || value.schemaVersion !== AGENT_TOOL_UI_SCHEMA_VERSION)
|
|
467
|
+
return null;
|
|
468
|
+
if (!hasOnlyKeys(value, [
|
|
469
|
+
"actions",
|
|
470
|
+
"description",
|
|
471
|
+
"fields",
|
|
472
|
+
"id",
|
|
473
|
+
"operationId",
|
|
474
|
+
"requestId",
|
|
475
|
+
"schemaVersion",
|
|
476
|
+
"steps",
|
|
477
|
+
"submitLabel",
|
|
478
|
+
"title",
|
|
479
|
+
"toolSlug",
|
|
480
|
+
"values"
|
|
481
|
+
])) {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
const id = boundedString(value.id, 200);
|
|
485
|
+
const title = boundedString(value.title, 200);
|
|
486
|
+
const toolSlug = boundedString(value.toolSlug, 200);
|
|
487
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
488
|
+
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
489
|
+
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
490
|
+
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
491
|
+
if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
const fields = value.fields.map(parseField);
|
|
495
|
+
if (fields.some((field) => field === null)) return null;
|
|
496
|
+
const steps = value.steps === void 0 ? void 0 : Array.isArray(value.steps) && value.steps.length <= 8 ? value.steps.map(parseStep) : null;
|
|
497
|
+
if (steps?.some((step) => step === null)) return null;
|
|
498
|
+
const actions = value.actions === void 0 ? void 0 : Array.isArray(value.actions) && value.actions.length <= 8 ? value.actions.map(parseAction) : null;
|
|
499
|
+
if (actions?.some((action) => action === null)) return null;
|
|
500
|
+
if (value.description !== void 0 && !description) return null;
|
|
501
|
+
if (value.operationId !== void 0 && !operationId) return null;
|
|
502
|
+
if (value.requestId !== void 0 && !requestId) return null;
|
|
503
|
+
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
504
|
+
const values = value.values !== void 0 && isRecord2(value.values) ? value.values : void 0;
|
|
505
|
+
if (value.values !== void 0) {
|
|
506
|
+
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
511
|
+
id,
|
|
512
|
+
title,
|
|
513
|
+
toolSlug,
|
|
514
|
+
fields,
|
|
515
|
+
...actions ? { actions } : {},
|
|
516
|
+
...description ? { description } : {},
|
|
517
|
+
...operationId ? { operationId } : {},
|
|
518
|
+
...requestId ? { requestId } : {},
|
|
519
|
+
...submitLabel ? { submitLabel } : {},
|
|
520
|
+
...steps ? { steps } : {},
|
|
521
|
+
...values ? { values } : {}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
function hasOnlyKeys(value, allowed) {
|
|
525
|
+
const allowedKeys = new Set(allowed);
|
|
526
|
+
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
527
|
+
}
|
|
528
|
+
|
|
275
529
|
// src/runtime/client.ts
|
|
276
530
|
function isTurnBoundary(event) {
|
|
277
531
|
return event.type === "session.waiting" || event.type === "session.completed" || event.type === "session.failed";
|
|
@@ -284,12 +538,8 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
284
538
|
if (event.type === "message.completed") {
|
|
285
539
|
handlers.onComplete?.();
|
|
286
540
|
}
|
|
287
|
-
if (event.type === "action.result")
|
|
288
|
-
|
|
289
|
-
if (result && typeof result === "object" && "output" in result) {
|
|
290
|
-
handlers.onActionResult?.(result.output);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
541
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
542
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
293
543
|
if (event.type !== "message.appended") return rendered;
|
|
294
544
|
const { messageDelta, messageSoFar } = event.data;
|
|
295
545
|
let delta = messageDelta;
|
|
@@ -300,9 +550,45 @@ function applyMessageEvent(event, rendered, handlers, workItems) {
|
|
|
300
550
|
} else if (messageDelta) {
|
|
301
551
|
next += messageDelta;
|
|
302
552
|
}
|
|
303
|
-
if (delta) handlers.onDelta(delta);
|
|
553
|
+
if (delta) handlers.onDelta?.(delta);
|
|
304
554
|
return next;
|
|
305
555
|
}
|
|
556
|
+
function emitActionResult(event, handlers) {
|
|
557
|
+
const result = event.data.result;
|
|
558
|
+
if (result.kind === "tool-result") {
|
|
559
|
+
handlers.onToolResult?.({
|
|
560
|
+
callId: result.callId,
|
|
561
|
+
toolName: result.toolName,
|
|
562
|
+
status: event.data.status,
|
|
563
|
+
output: result.output,
|
|
564
|
+
...event.data.error ? { error: event.data.error } : {}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
if ("output" in result) handlers.onActionResult?.(result.output);
|
|
568
|
+
}
|
|
569
|
+
function emitInputRequests(event, handlers) {
|
|
570
|
+
handlers.onInputRequest?.(
|
|
571
|
+
event.data.requests.map((request) => {
|
|
572
|
+
const ui = parseAgentToolUiSurface(
|
|
573
|
+
request.ui
|
|
574
|
+
);
|
|
575
|
+
return {
|
|
576
|
+
requestId: request.requestId,
|
|
577
|
+
kind: request.kind,
|
|
578
|
+
prompt: request.prompt,
|
|
579
|
+
...request.display ? { display: request.display } : {},
|
|
580
|
+
...request.allowFreeform !== void 0 ? { allowFreeform: request.allowFreeform } : {},
|
|
581
|
+
...request.options ? { options: request.options } : {},
|
|
582
|
+
...ui ? { ui } : {},
|
|
583
|
+
action: {
|
|
584
|
+
callId: request.action.callId,
|
|
585
|
+
kind: "tool-call",
|
|
586
|
+
toolName: request.action.toolName
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
})
|
|
590
|
+
);
|
|
591
|
+
}
|
|
306
592
|
function isResumeTurnMessage(received, candidate) {
|
|
307
593
|
if (received === candidate) return true;
|
|
308
594
|
return Boolean(candidate) && received.endsWith(`
|
|
@@ -578,9 +864,11 @@ var AgentSession = class {
|
|
|
578
864
|
let streamIndex = session?.state.streamIndex ?? 0;
|
|
579
865
|
let rendered = "";
|
|
580
866
|
const workItems = /* @__PURE__ */ new Map();
|
|
867
|
+
let requestedInput = false;
|
|
581
868
|
try {
|
|
582
869
|
for await (const event of response) {
|
|
583
870
|
if (signal.aborted) break;
|
|
871
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
584
872
|
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
585
873
|
streamIndex += 1;
|
|
586
874
|
if (session) {
|
|
@@ -598,7 +886,7 @@ var AgentSession = class {
|
|
|
598
886
|
this.persistSessionCursor(session);
|
|
599
887
|
}
|
|
600
888
|
}
|
|
601
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
889
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
602
890
|
throw new Error("Empty response from runtime");
|
|
603
891
|
}
|
|
604
892
|
return rendered.trim();
|
|
@@ -618,6 +906,9 @@ var AgentSession = class {
|
|
|
618
906
|
() => attached.snapshot({ signal })
|
|
619
907
|
);
|
|
620
908
|
const turnEvents = latestTurnEvents(snapshot.events);
|
|
909
|
+
const hasInputRequest = turnEvents.some(
|
|
910
|
+
(event) => event.type === "input.requested"
|
|
911
|
+
);
|
|
621
912
|
const received = turnEvents[0];
|
|
622
913
|
const lastSent = persisted.lastMessage;
|
|
623
914
|
const inFlight = !turnEvents.some((event) => isTurnBoundary(event));
|
|
@@ -628,6 +919,8 @@ var AgentSession = class {
|
|
|
628
919
|
const workItems = /* @__PURE__ */ new Map();
|
|
629
920
|
for (const event of turnEvents) {
|
|
630
921
|
applyWorkEvent(event, handlers, workItems);
|
|
922
|
+
if (event.type === "input.requested") emitInputRequests(event, handlers);
|
|
923
|
+
if (event.type === "action.result") emitActionResult(event, handlers);
|
|
631
924
|
}
|
|
632
925
|
if (rendered.startsWith(initialText)) {
|
|
633
926
|
const missedText = rendered.slice(initialText.length);
|
|
@@ -657,7 +950,9 @@ var AgentSession = class {
|
|
|
657
950
|
);
|
|
658
951
|
}
|
|
659
952
|
handlers.onComplete?.();
|
|
660
|
-
if (!rendered.trim()
|
|
953
|
+
if (!rendered.trim() && !hasInputRequest) {
|
|
954
|
+
throw new Error("Empty response from runtime");
|
|
955
|
+
}
|
|
661
956
|
return rendered.trim();
|
|
662
957
|
}
|
|
663
958
|
let streamIndex = snapshot.session.streamIndex;
|
|
@@ -676,7 +971,55 @@ var AgentSession = class {
|
|
|
676
971
|
session = client.sessions.attach(session.state.sessionId, { streamIndex });
|
|
677
972
|
this.session = session;
|
|
678
973
|
this.persistSessionCursor(session);
|
|
679
|
-
if (!rendered.trim() && !signal.aborted) {
|
|
974
|
+
if (!rendered.trim() && !signal.aborted && !hasInputRequest) {
|
|
975
|
+
throw new Error("Empty response from runtime");
|
|
976
|
+
}
|
|
977
|
+
return rendered.trim();
|
|
978
|
+
}
|
|
979
|
+
async respondTurn(responses, signal, handlers) {
|
|
980
|
+
const client = this.ensureClient();
|
|
981
|
+
const session = this.session ?? this.attachPersistedSession(client);
|
|
982
|
+
if (!session) {
|
|
983
|
+
throw new Error("No active session is waiting for input.");
|
|
984
|
+
}
|
|
985
|
+
this.session = session;
|
|
986
|
+
const inputResponses = responses.map(
|
|
987
|
+
({ requestId, optionId, text }) => ({
|
|
988
|
+
requestId,
|
|
989
|
+
...optionId ? { optionId } : {},
|
|
990
|
+
...text ? { text } : {}
|
|
991
|
+
})
|
|
992
|
+
);
|
|
993
|
+
const response = await withCapabilityRefresh(
|
|
994
|
+
this.capability,
|
|
995
|
+
() => session.respond(inputResponses, { signal })
|
|
996
|
+
);
|
|
997
|
+
this.activeResponse = response;
|
|
998
|
+
let streamIndex = session.state.streamIndex;
|
|
999
|
+
let rendered = "";
|
|
1000
|
+
let requestedInput = false;
|
|
1001
|
+
const workItems = /* @__PURE__ */ new Map();
|
|
1002
|
+
try {
|
|
1003
|
+
for await (const event of response) {
|
|
1004
|
+
if (signal.aborted) break;
|
|
1005
|
+
if (event.type === "input.requested") requestedInput = true;
|
|
1006
|
+
rendered = applyMessageEvent(event, rendered, handlers, workItems);
|
|
1007
|
+
streamIndex += 1;
|
|
1008
|
+
savePersistedAgentSession(
|
|
1009
|
+
this.visitorSessionId,
|
|
1010
|
+
session.state.sessionId,
|
|
1011
|
+
streamIndex,
|
|
1012
|
+
this.storeOptions
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
} finally {
|
|
1016
|
+
this.activeResponse = void 0;
|
|
1017
|
+
this.session = client.sessions.attach(session.state.sessionId, {
|
|
1018
|
+
streamIndex
|
|
1019
|
+
});
|
|
1020
|
+
this.persistSessionCursor(this.session);
|
|
1021
|
+
}
|
|
1022
|
+
if (!rendered.trim() && !signal.aborted && !requestedInput) {
|
|
680
1023
|
throw new Error("Empty response from runtime");
|
|
681
1024
|
}
|
|
682
1025
|
return rendered.trim();
|
|
@@ -736,6 +1079,11 @@ function createAgentClient(options) {
|
|
|
736
1079
|
resumeOptions.handlers,
|
|
737
1080
|
resumeOptions.initialText
|
|
738
1081
|
),
|
|
1082
|
+
respondTurn: (respondOptions) => session.respondTurn(
|
|
1083
|
+
respondOptions.responses,
|
|
1084
|
+
respondOptions.signal ?? new AbortController().signal,
|
|
1085
|
+
respondOptions.handlers
|
|
1086
|
+
),
|
|
739
1087
|
reset: () => session.reset(),
|
|
740
1088
|
cancelActive: () => session.cancelActive(),
|
|
741
1089
|
getActiveSessionId: () => session.getActiveSessionId()
|
|
@@ -785,6 +1133,63 @@ function formatAgentError(error) {
|
|
|
785
1133
|
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
786
1134
|
}
|
|
787
1135
|
|
|
1136
|
+
// src/runtime/tool-result-envelope.ts
|
|
1137
|
+
var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
1138
|
+
"schemaVersion",
|
|
1139
|
+
"output",
|
|
1140
|
+
"presentationKinds",
|
|
1141
|
+
"ui"
|
|
1142
|
+
]);
|
|
1143
|
+
function isRecord3(value) {
|
|
1144
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1145
|
+
}
|
|
1146
|
+
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
1147
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
1148
|
+
return true;
|
|
1149
|
+
}
|
|
1150
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1151
|
+
if (typeof value !== "object") return false;
|
|
1152
|
+
if (seen.has(value)) return false;
|
|
1153
|
+
seen.add(value);
|
|
1154
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue2(item, seen)) : Object.entries(value).every(
|
|
1155
|
+
([key, item]) => typeof key === "string" && isJsonValue2(item, seen)
|
|
1156
|
+
);
|
|
1157
|
+
seen.delete(value);
|
|
1158
|
+
return valid;
|
|
1159
|
+
}
|
|
1160
|
+
function decodeEnvelope(value) {
|
|
1161
|
+
if (typeof value !== "string") return value;
|
|
1162
|
+
try {
|
|
1163
|
+
return JSON.parse(value);
|
|
1164
|
+
} catch {
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function parseAgentToolResultEnvelope(value) {
|
|
1169
|
+
const decoded = decodeEnvelope(value);
|
|
1170
|
+
if (!isRecord3(decoded)) return null;
|
|
1171
|
+
const keys = Object.keys(decoded);
|
|
1172
|
+
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) {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
const presentationKinds = decoded.presentationKinds.flatMap((kind) => {
|
|
1176
|
+
if (typeof kind !== "string") return [];
|
|
1177
|
+
const normalized = kind.trim();
|
|
1178
|
+
return normalized && normalized.length <= 128 ? [normalized] : [];
|
|
1179
|
+
});
|
|
1180
|
+
if (presentationKinds.length !== decoded.presentationKinds.length) {
|
|
1181
|
+
return null;
|
|
1182
|
+
}
|
|
1183
|
+
const ui = decoded.ui === void 0 ? void 0 : parseAgentToolUiSurface(decoded.ui);
|
|
1184
|
+
if (decoded.ui !== void 0 && !ui) return null;
|
|
1185
|
+
return {
|
|
1186
|
+
schemaVersion: "webless.tool-result.v1",
|
|
1187
|
+
output: decoded.output,
|
|
1188
|
+
presentationKinds,
|
|
1189
|
+
...ui ? { ui } : {}
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
|
|
788
1193
|
// src/runtime/health.ts
|
|
789
1194
|
async function pingAgentHealth(runtimeOrigin, fetchImpl = fetch) {
|
|
790
1195
|
const healthUrl = agentHealthUrl(runtimeOrigin);
|
|
@@ -807,6 +1212,7 @@ async function pingAgentHealth(runtimeOrigin, fetchImpl = fetch) {
|
|
|
807
1212
|
}
|
|
808
1213
|
// Annotate the CommonJS export names for ESM import in node:
|
|
809
1214
|
0 && (module.exports = {
|
|
1215
|
+
AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
810
1216
|
DEFAULT_RUNTIME_ORIGIN,
|
|
811
1217
|
agentHealthUrl,
|
|
812
1218
|
buildAgentStorageKeyPrefix,
|
|
@@ -817,6 +1223,8 @@ async function pingAgentHealth(runtimeOrigin, fetchImpl = fetch) {
|
|
|
817
1223
|
formatAgentError,
|
|
818
1224
|
getOrCreateVisitorSessionId,
|
|
819
1225
|
loadPersistedAgentSession,
|
|
1226
|
+
parseAgentToolResultEnvelope,
|
|
1227
|
+
parseAgentToolUiSurface,
|
|
820
1228
|
pingAgentHealth,
|
|
821
1229
|
resolveAgentRuntimeConfig,
|
|
822
1230
|
savePersistedAgentSession
|