@webless/agent 0.9.0 → 0.9.1
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 +6 -0
- package/dist/{chunk-QMTI5646.js → chunk-3E632FNE.js} +332 -203
- package/dist/chunk-3E632FNE.js.map +1 -0
- package/dist/{chunk-5BCSXCLT.js → chunk-TCO62TRN.js} +40 -2
- package/dist/chunk-TCO62TRN.js.map +1 -0
- package/dist/embed.cjs +754 -625
- package/dist/embed.cjs.map +1 -1
- package/dist/embed.css +60 -10
- package/dist/embed.css.map +1 -1
- package/dist/embed.d.cts +3 -9
- package/dist/embed.d.ts +3 -9
- package/dist/embed.js +1 -1
- package/dist/index.cjs +752 -680
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +43 -4
- package/dist/index.js.map +1 -1
- package/dist/{manifest-DgjT8-tW.d.cts → panel-controller-D_XIQa1U.d.cts} +9 -2
- package/dist/{manifest-DgjT8-tW.d.ts → panel-controller-D_XIQa1U.d.ts} +9 -2
- package/dist/presentation.cjs +79 -67
- package/dist/presentation.cjs.map +1 -1
- package/dist/presentation.d.cts +1 -1
- package/dist/presentation.d.ts +1 -1
- package/dist/presentation.js +7 -3
- package/dist/presentation.js.map +1 -1
- package/dist/react.cjs +756 -625
- package/dist/react.cjs.map +1 -1
- package/dist/react.css +60 -10
- package/dist/react.css.map +1 -1
- package/dist/react.d.cts +3 -3
- package/dist/react.d.ts +3 -3
- package/dist/react.js +3 -1
- package/dist/react.js.map +1 -1
- package/dist/{types-ohWeTG9i.d.cts → types-sLEIYDqm.d.cts} +1 -1
- package/dist/{types-ohWeTG9i.d.ts → types-sLEIYDqm.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-5BCSXCLT.js.map +0 -1
- package/dist/chunk-QMTI5646.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -42,700 +42,816 @@ __export(index_exports, {
|
|
|
42
42
|
});
|
|
43
43
|
module.exports = __toCommonJS(index_exports);
|
|
44
44
|
|
|
45
|
-
// src/runtime/
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const host = new URL(origin).hostname;
|
|
58
|
-
return host === "127.0.0.1" || host === "localhost";
|
|
59
|
-
} catch {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function localBootstrapOrigins(origin) {
|
|
64
|
-
const normalized = origin.replace(/\/$/, "");
|
|
65
|
-
if (!isLoopbackRuntimeOrigin(normalized)) return [normalized];
|
|
66
|
-
return [
|
|
67
|
-
normalized,
|
|
68
|
-
...LOCAL_LOOPBACK_ORIGINS.filter((candidate) => candidate !== normalized)
|
|
69
|
-
];
|
|
45
|
+
// src/runtime/tool-ui.ts
|
|
46
|
+
var AGENT_TOOL_UI_SCHEMA_VERSION = "webless.tool-ui.v1";
|
|
47
|
+
var AGENT_STRUCTURED_TOOL_INPUT_SCHEMA_VERSION = "webless.structured-tool-input.v1";
|
|
48
|
+
var AGENT_STRUCTURED_TOOL_INPUT_HEADER = "Webless-Structured-Tool-Input-JSON:";
|
|
49
|
+
function formatAgentStructuredToolInput(surface, values) {
|
|
50
|
+
const payload = {
|
|
51
|
+
schemaVersion: AGENT_STRUCTURED_TOOL_INPUT_SCHEMA_VERSION,
|
|
52
|
+
toolSlug: surface.toolSlug,
|
|
53
|
+
...surface.operationId ? { operationId: surface.operationId } : {},
|
|
54
|
+
values
|
|
55
|
+
};
|
|
56
|
+
return `${AGENT_STRUCTURED_TOOL_INPUT_HEADER} ${JSON.stringify(payload)}`;
|
|
70
57
|
}
|
|
71
58
|
function isRecord(value) {
|
|
72
|
-
return typeof value === "object" &&
|
|
59
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
73
60
|
}
|
|
74
|
-
function
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
const expiresAt = Date.parse(value.expiresAt);
|
|
79
|
-
if (!Number.isFinite(expiresAt) || expiresAt <= now) {
|
|
80
|
-
throw new Error("Agent Runtime returned an expired access response.");
|
|
61
|
+
function isJsonValue(value, seen = /* @__PURE__ */ new Set()) {
|
|
62
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
63
|
+
return true;
|
|
81
64
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
65
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
66
|
+
if (typeof value !== "object") return false;
|
|
67
|
+
if (seen.has(value)) return false;
|
|
68
|
+
seen.add(value);
|
|
69
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, seen)) : Object.entries(value).every(
|
|
70
|
+
([key, item]) => typeof key === "string" && isJsonValue(item, seen)
|
|
85
71
|
);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
refreshAt: expiresAt - refreshSkew
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
async function readBootstrapError(response) {
|
|
92
|
-
const fallback = `Agent Runtime is unavailable (${response.status}).`;
|
|
93
|
-
try {
|
|
94
|
-
const value = await response.json();
|
|
95
|
-
return isRecord(value) && typeof value.error === "string" && value.error ? value.error : fallback;
|
|
96
|
-
} catch {
|
|
97
|
-
return fallback;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
function createAgentRuntimeCapability(options) {
|
|
101
|
-
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
102
|
-
const now = options.now ?? Date.now;
|
|
103
|
-
let capability;
|
|
104
|
-
let generation = 0;
|
|
105
|
-
let pendingBootstrap;
|
|
106
|
-
const bootstrap = async () => {
|
|
107
|
-
let previewGrant;
|
|
108
|
-
const previewBuildId = options.previewBuildId?.trim();
|
|
109
|
-
if (options.version === "unpublished") {
|
|
110
|
-
previewGrant = previewBuildId ? void 0 : (await options.getUnpublishedPreviewGrant?.())?.trim();
|
|
111
|
-
if (!previewGrant && !previewBuildId) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
"Unpublished preview authorization is required to use the Agent Runtime preview."
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const bootstrapBody = JSON.stringify({
|
|
118
|
-
clientSessionId: options.visitorSessionId,
|
|
119
|
-
indexId: options.indexId,
|
|
120
|
-
...previewBuildId ? { previewBuildId } : {},
|
|
121
|
-
...previewGrant ? { previewGrant } : {},
|
|
122
|
-
version: options.version
|
|
123
|
-
});
|
|
124
|
-
const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
|
|
125
|
-
body: bootstrapBody,
|
|
126
|
-
headers: { "content-type": "application/json" },
|
|
127
|
-
method: "POST"
|
|
128
|
-
});
|
|
129
|
-
let response;
|
|
130
|
-
let lastError;
|
|
131
|
-
for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
|
|
132
|
-
try {
|
|
133
|
-
response = await postBootstrap(origin);
|
|
134
|
-
break;
|
|
135
|
-
} catch (error) {
|
|
136
|
-
lastError = error;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (!response) {
|
|
140
|
-
throw lastError instanceof Error ? lastError : new Error("Agent Runtime is unavailable.");
|
|
141
|
-
}
|
|
142
|
-
if (!response.ok) {
|
|
143
|
-
throw new Error(await readBootstrapError(response));
|
|
144
|
-
}
|
|
145
|
-
let value;
|
|
146
|
-
try {
|
|
147
|
-
value = await response.json();
|
|
148
|
-
} catch {
|
|
149
|
-
throw new Error("Agent Runtime returned an invalid access response.");
|
|
150
|
-
}
|
|
151
|
-
return parseBootstrapResponse(value, options.indexId, now());
|
|
152
|
-
};
|
|
153
|
-
const getAccessToken = async () => {
|
|
154
|
-
if (capability && capability.refreshAt > now()) {
|
|
155
|
-
return capability.accessToken;
|
|
156
|
-
}
|
|
157
|
-
const requestedGeneration = generation;
|
|
158
|
-
if (!pendingBootstrap) {
|
|
159
|
-
const trackedBootstrap = bootstrap().finally(() => {
|
|
160
|
-
if (pendingBootstrap === trackedBootstrap) {
|
|
161
|
-
pendingBootstrap = void 0;
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
pendingBootstrap = trackedBootstrap;
|
|
165
|
-
}
|
|
166
|
-
const nextCapability = await pendingBootstrap;
|
|
167
|
-
if (requestedGeneration !== generation) {
|
|
168
|
-
return getAccessToken();
|
|
169
|
-
}
|
|
170
|
-
capability = nextCapability;
|
|
171
|
-
return capability.accessToken;
|
|
172
|
-
};
|
|
173
|
-
return {
|
|
174
|
-
getAccessToken,
|
|
175
|
-
invalidate: () => {
|
|
176
|
-
capability = void 0;
|
|
177
|
-
generation += 1;
|
|
178
|
-
pendingBootstrap = void 0;
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
async function withCapabilityRefresh(capability, request) {
|
|
183
|
-
try {
|
|
184
|
-
return await request();
|
|
185
|
-
} catch (error) {
|
|
186
|
-
if (!(error instanceof import_client.ClientError) || error.status !== 401) {
|
|
187
|
-
throw error;
|
|
188
|
-
}
|
|
189
|
-
capability.invalidate();
|
|
190
|
-
return await request();
|
|
191
|
-
}
|
|
72
|
+
seen.delete(value);
|
|
73
|
+
return valid;
|
|
192
74
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
197
|
-
function buildEveHost(origin, indexId, version = "published") {
|
|
198
|
-
const base = trimTrailingSlash(origin);
|
|
199
|
-
const searchParams = new URLSearchParams({ indexId: indexId.trim() });
|
|
200
|
-
if (version === "unpublished") {
|
|
201
|
-
searchParams.set("version", version);
|
|
75
|
+
function boundedString(value, max) {
|
|
76
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > max) {
|
|
77
|
+
return void 0;
|
|
202
78
|
}
|
|
203
|
-
return
|
|
79
|
+
return value.trim();
|
|
204
80
|
}
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
const resolvedIndexId = input.indexId.trim();
|
|
208
|
-
const version = input.version ?? "published";
|
|
209
|
-
return {
|
|
210
|
-
origin,
|
|
211
|
-
indexId: resolvedIndexId,
|
|
212
|
-
version,
|
|
213
|
-
host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId, version) : origin
|
|
214
|
-
};
|
|
81
|
+
function numberValue(value) {
|
|
82
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
215
83
|
}
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
return `${origin}/eve/v1/health`;
|
|
84
|
+
function integerValue(value) {
|
|
85
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 8e3 ? value : void 0;
|
|
219
86
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
87
|
+
function isFieldKind(value) {
|
|
88
|
+
return typeof value === "string" && [
|
|
89
|
+
"text",
|
|
90
|
+
"textarea",
|
|
91
|
+
"email",
|
|
92
|
+
"number",
|
|
93
|
+
"select",
|
|
94
|
+
"multi-select",
|
|
95
|
+
"checkbox",
|
|
96
|
+
"confirmation",
|
|
97
|
+
"radio",
|
|
98
|
+
"date",
|
|
99
|
+
"time",
|
|
100
|
+
"date-time",
|
|
101
|
+
"calendar",
|
|
102
|
+
"range",
|
|
103
|
+
"json"
|
|
104
|
+
].includes(value);
|
|
227
105
|
}
|
|
228
|
-
function
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
106
|
+
function parseField(value) {
|
|
107
|
+
if (!isRecord(value)) return null;
|
|
108
|
+
if (!hasOnlyKeys(value, [
|
|
109
|
+
"description",
|
|
110
|
+
"kind",
|
|
111
|
+
"label",
|
|
112
|
+
"max",
|
|
113
|
+
"maxItems",
|
|
114
|
+
"maxLength",
|
|
115
|
+
"min",
|
|
116
|
+
"minLength",
|
|
117
|
+
"options",
|
|
118
|
+
"path",
|
|
119
|
+
"placeholder",
|
|
120
|
+
"required",
|
|
121
|
+
"step",
|
|
122
|
+
"defaultValue"
|
|
123
|
+
])) {
|
|
124
|
+
return null;
|
|
233
125
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
126
|
+
if (!isFieldKind(value.kind)) return null;
|
|
127
|
+
const path = boundedString(value.path, 160);
|
|
128
|
+
const label = boundedString(value.label, 160);
|
|
129
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
130
|
+
const placeholder = value.placeholder === void 0 || typeof value.placeholder !== "string" ? void 0 : value.placeholder;
|
|
131
|
+
const required = value.required === void 0 || typeof value.required !== "boolean" ? void 0 : value.required;
|
|
132
|
+
const min = value.min === void 0 ? void 0 : numberValue(value.min);
|
|
133
|
+
const max = value.max === void 0 ? void 0 : numberValue(value.max);
|
|
134
|
+
const maxItems = value.maxItems === void 0 ? void 0 : integerValue(value.maxItems);
|
|
135
|
+
const step = value.step === void 0 ? void 0 : numberValue(value.step);
|
|
136
|
+
const minLength = value.minLength === void 0 ? void 0 : integerValue(value.minLength);
|
|
137
|
+
const maxLength = value.maxLength === void 0 ? void 0 : integerValue(value.maxLength);
|
|
138
|
+
const defaultValue = value.defaultValue === void 0 || !isJsonValue(value.defaultValue) ? void 0 : value.defaultValue;
|
|
139
|
+
if (!path || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || !label) {
|
|
140
|
+
return null;
|
|
240
141
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
|
|
142
|
+
if (value.description !== void 0 && !description) return null;
|
|
143
|
+
if (value.placeholder !== void 0 && placeholder === void 0) return null;
|
|
144
|
+
if (value.required !== void 0 && required === void 0) return null;
|
|
145
|
+
if (value.min !== void 0 && min === void 0) return null;
|
|
146
|
+
if (value.max !== void 0 && max === void 0) return null;
|
|
147
|
+
if (value.maxItems !== void 0 && (maxItems === void 0 || maxItems < 1 || maxItems > 100))
|
|
148
|
+
return null;
|
|
149
|
+
if (value.step !== void 0 && step === void 0) return null;
|
|
150
|
+
if (value.minLength !== void 0 && minLength === void 0) return null;
|
|
151
|
+
if (value.maxLength !== void 0 && maxLength === void 0) return null;
|
|
152
|
+
if (value.defaultValue !== void 0 && defaultValue === void 0)
|
|
153
|
+
return null;
|
|
154
|
+
if (value.options !== void 0) {
|
|
155
|
+
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
156
|
+
return null;
|
|
157
|
+
for (const option of value.options) {
|
|
158
|
+
if (!isRecord(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
251
162
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
function runtimeLastMessageKey(visitorSessionId, prefix) {
|
|
270
|
-
return `${prefix}:eve:${visitorSessionId}:lastMessage`;
|
|
163
|
+
return {
|
|
164
|
+
kind: value.kind,
|
|
165
|
+
path,
|
|
166
|
+
label,
|
|
167
|
+
...description ? { description } : {},
|
|
168
|
+
...placeholder !== void 0 ? { placeholder } : {},
|
|
169
|
+
...required !== void 0 ? { required } : {},
|
|
170
|
+
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
171
|
+
...value.options !== void 0 ? { options: value.options } : {},
|
|
172
|
+
...min !== void 0 ? { min } : {},
|
|
173
|
+
...max !== void 0 ? { max } : {},
|
|
174
|
+
...maxItems !== void 0 ? { maxItems } : {},
|
|
175
|
+
...step !== void 0 ? { step } : {},
|
|
176
|
+
...minLength !== void 0 ? { minLength } : {},
|
|
177
|
+
...maxLength !== void 0 ? { maxLength } : {}
|
|
178
|
+
};
|
|
271
179
|
}
|
|
272
|
-
function
|
|
273
|
-
if (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
const
|
|
180
|
+
function parseStep(value) {
|
|
181
|
+
if (!isRecord(value)) return null;
|
|
182
|
+
if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const id = boundedString(value.id, 80);
|
|
186
|
+
const label = boundedString(value.label, 160);
|
|
187
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
188
|
+
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(
|
|
189
|
+
(path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
|
|
190
|
+
)) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
280
193
|
return {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
194
|
+
id,
|
|
195
|
+
label,
|
|
196
|
+
fieldPaths: value.fieldPaths,
|
|
197
|
+
...description ? { description } : {}
|
|
284
198
|
};
|
|
285
199
|
}
|
|
286
|
-
function
|
|
287
|
-
if (
|
|
288
|
-
|
|
200
|
+
function parseAction(value) {
|
|
201
|
+
if (!isRecord(value)) return null;
|
|
202
|
+
if (!hasOnlyKeys(value, ["id", "label"])) return null;
|
|
203
|
+
const label = boundedString(value.label, 80);
|
|
204
|
+
if (value.id !== "submit" && value.id !== "back" && value.id !== "next" && value.id !== "reset" || !label) {
|
|
205
|
+
return null;
|
|
289
206
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
String(Math.max(0, streamIndex))
|
|
295
|
-
);
|
|
207
|
+
return {
|
|
208
|
+
id: value.id,
|
|
209
|
+
label
|
|
210
|
+
};
|
|
296
211
|
}
|
|
297
|
-
function
|
|
298
|
-
if (
|
|
299
|
-
return;
|
|
212
|
+
function parseAgentToolUiSurface(value) {
|
|
213
|
+
if (!isRecord(value) || value.schemaVersion !== AGENT_TOOL_UI_SCHEMA_VERSION)
|
|
214
|
+
return null;
|
|
215
|
+
if (!hasOnlyKeys(value, [
|
|
216
|
+
"actions",
|
|
217
|
+
"description",
|
|
218
|
+
"fields",
|
|
219
|
+
"id",
|
|
220
|
+
"operationId",
|
|
221
|
+
"requestId",
|
|
222
|
+
"schemaVersion",
|
|
223
|
+
"steps",
|
|
224
|
+
"submitLabel",
|
|
225
|
+
"title",
|
|
226
|
+
"toolSlug",
|
|
227
|
+
"values"
|
|
228
|
+
])) {
|
|
229
|
+
return null;
|
|
300
230
|
}
|
|
301
|
-
const
|
|
302
|
-
|
|
231
|
+
const id = boundedString(value.id, 200);
|
|
232
|
+
const title = boundedString(value.title, 200);
|
|
233
|
+
const toolSlug = boundedString(value.toolSlug, 200);
|
|
234
|
+
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
235
|
+
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
236
|
+
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
237
|
+
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
238
|
+
if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const fields = value.fields.map(parseField);
|
|
242
|
+
if (fields.some((field) => field === null)) return null;
|
|
243
|
+
const steps = value.steps === void 0 ? void 0 : Array.isArray(value.steps) && value.steps.length <= 8 ? value.steps.map(parseStep) : null;
|
|
244
|
+
if (steps?.some((step) => step === null)) return null;
|
|
245
|
+
const actions = value.actions === void 0 ? void 0 : Array.isArray(value.actions) && value.actions.length <= 8 ? value.actions.map(parseAction) : null;
|
|
246
|
+
if (actions?.some((action) => action === null)) return null;
|
|
247
|
+
if (value.description !== void 0 && !description) return null;
|
|
248
|
+
if (value.operationId !== void 0 && !operationId) return null;
|
|
249
|
+
if (value.requestId !== void 0 && !requestId) return null;
|
|
250
|
+
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
251
|
+
const values = value.values !== void 0 && isRecord(value.values) ? value.values : void 0;
|
|
252
|
+
if (value.values !== void 0) {
|
|
253
|
+
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
|
|
258
|
+
id,
|
|
259
|
+
title,
|
|
260
|
+
toolSlug,
|
|
261
|
+
fields,
|
|
262
|
+
...actions ? { actions } : {},
|
|
263
|
+
...description ? { description } : {},
|
|
264
|
+
...operationId ? { operationId } : {},
|
|
265
|
+
...requestId ? { requestId } : {},
|
|
266
|
+
...submitLabel ? { submitLabel } : {},
|
|
267
|
+
...steps ? { steps } : {},
|
|
268
|
+
...values ? { values } : {}
|
|
269
|
+
};
|
|
303
270
|
}
|
|
304
|
-
function
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
|
|
308
|
-
sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
|
|
309
|
-
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
271
|
+
function hasOnlyKeys(value, allowed) {
|
|
272
|
+
const allowedKeys = new Set(allowed);
|
|
273
|
+
return Object.keys(value).every((key) => allowedKeys.has(key));
|
|
310
274
|
}
|
|
311
275
|
|
|
312
|
-
// src/runtime/
|
|
313
|
-
var
|
|
314
|
-
|
|
315
|
-
|
|
276
|
+
// src/runtime/tool-result-envelope.ts
|
|
277
|
+
var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
278
|
+
"schemaVersion",
|
|
279
|
+
"output",
|
|
280
|
+
"presentationKinds",
|
|
281
|
+
"ui"
|
|
282
|
+
]);
|
|
316
283
|
function isRecord2(value) {
|
|
317
|
-
return typeof value === "object" &&
|
|
284
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
318
285
|
}
|
|
319
|
-
function
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
if (typeof code !== "string" || typeof message !== "string") {
|
|
323
|
-
return void 0;
|
|
286
|
+
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
287
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
288
|
+
return true;
|
|
324
289
|
}
|
|
325
|
-
|
|
290
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
291
|
+
if (typeof value !== "object") return false;
|
|
292
|
+
if (seen.has(value)) return false;
|
|
293
|
+
seen.add(value);
|
|
294
|
+
const valid = Array.isArray(value) ? value.every((item) => isJsonValue2(item, seen)) : Object.entries(value).every(
|
|
295
|
+
([key, item]) => typeof key === "string" && isJsonValue2(item, seen)
|
|
296
|
+
);
|
|
297
|
+
seen.delete(value);
|
|
298
|
+
return valid;
|
|
326
299
|
}
|
|
327
|
-
function
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
return
|
|
333
|
-
}
|
|
334
|
-
if (value.type === "subagent.event" && isRecord2(value.data) && Object.hasOwn(value.data, "event")) {
|
|
335
|
-
return parseChildStreamEvent(value.data.event);
|
|
336
|
-
}
|
|
337
|
-
if (value.type === "subagent.called" && isRecord2(value.data)) {
|
|
338
|
-
const { childStreamPath } = value.data;
|
|
339
|
-
if (typeof childStreamPath === "string") {
|
|
340
|
-
return { childStreamPath, type: "subagent.called" };
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
if (value.type !== "action.result" || !isRecord2(value.data)) {
|
|
344
|
-
return { type: "other" };
|
|
345
|
-
}
|
|
346
|
-
const { data } = value;
|
|
347
|
-
if (data.status !== "completed" && data.status !== "failed" && data.status !== "rejected") {
|
|
348
|
-
return { type: "other" };
|
|
300
|
+
function decodeEnvelope(value) {
|
|
301
|
+
if (typeof value !== "string") return value;
|
|
302
|
+
try {
|
|
303
|
+
return JSON.parse(value);
|
|
304
|
+
} catch {
|
|
305
|
+
return null;
|
|
349
306
|
}
|
|
350
|
-
|
|
351
|
-
|
|
307
|
+
}
|
|
308
|
+
function parseAgentToolResultEnvelope(value) {
|
|
309
|
+
const decoded = decodeEnvelope(value);
|
|
310
|
+
if (!isRecord2(decoded)) return null;
|
|
311
|
+
const keys = Object.keys(decoded);
|
|
312
|
+
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) {
|
|
313
|
+
return null;
|
|
352
314
|
}
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
315
|
+
const presentationKinds = decoded.presentationKinds.flatMap((kind) => {
|
|
316
|
+
if (typeof kind !== "string") return [];
|
|
317
|
+
const normalized = kind.trim();
|
|
318
|
+
return normalized && normalized.length <= 128 ? [normalized] : [];
|
|
319
|
+
});
|
|
320
|
+
if (presentationKinds.length !== decoded.presentationKinds.length) {
|
|
321
|
+
return null;
|
|
356
322
|
}
|
|
357
|
-
const
|
|
323
|
+
const ui = decoded.ui === void 0 ? void 0 : parseAgentToolUiSurface(decoded.ui);
|
|
324
|
+
if (decoded.ui !== void 0 && !ui) return null;
|
|
358
325
|
return {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
toolName: result.toolName,
|
|
364
|
-
status: data.status,
|
|
365
|
-
...Object.hasOwn(result, "output") ? { output: result.output } : {},
|
|
366
|
-
...error ? { error } : {}
|
|
367
|
-
}
|
|
326
|
+
schemaVersion: "webless.tool-result.v1",
|
|
327
|
+
output: decoded.output,
|
|
328
|
+
presentationKinds,
|
|
329
|
+
...ui ? { ui } : {}
|
|
368
330
|
};
|
|
369
331
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
332
|
+
|
|
333
|
+
// src/runtime/connected-tool-work.ts
|
|
334
|
+
var HUBSPOT_ACTION_LABELS = {
|
|
335
|
+
HUBSPOT_LIST_CONTACTS: "Checking your details",
|
|
336
|
+
HUBSPOT_CREATE_CONTACT: "Saving your details",
|
|
337
|
+
HUBSPOT_UPDATE_CONTACT: "Updating your details",
|
|
338
|
+
HUBSPOT_CREATE_COMPANY: "Saving your company details"
|
|
339
|
+
};
|
|
340
|
+
function connectedToolWork(action) {
|
|
341
|
+
const slug = action.toolName === "COMPOSIO_MULTI_EXECUTE_TOOL" ? action.input.toolSlug : action.toolName;
|
|
342
|
+
if (typeof slug !== "string") return null;
|
|
343
|
+
const detail = HUBSPOT_ACTION_LABELS[slug];
|
|
344
|
+
return detail ? {
|
|
345
|
+
id: action.callId,
|
|
346
|
+
kind: "tool",
|
|
347
|
+
label: "Contact details",
|
|
348
|
+
detail,
|
|
349
|
+
state: "active"
|
|
350
|
+
} : null;
|
|
351
|
+
}
|
|
352
|
+
function toolResultFailed(result) {
|
|
353
|
+
if (result.status !== "completed") return true;
|
|
354
|
+
const output = parseAgentToolResultEnvelope(result.output)?.output ?? result.output;
|
|
355
|
+
if (typeof output !== "object" || output === null || Array.isArray(output))
|
|
356
|
+
return false;
|
|
357
|
+
return "error" in output && Boolean(output.error) || "providerError" in output && Boolean(output.providerError);
|
|
358
|
+
}
|
|
359
|
+
function completeConnectedToolWork(item, result) {
|
|
360
|
+
const failed = toolResultFailed(result);
|
|
361
|
+
return {
|
|
362
|
+
...item,
|
|
363
|
+
state: failed ? "error" : "completed",
|
|
364
|
+
detail: failed ? "Action could not be confirmed" : "Action completed"
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/runtime/client.ts
|
|
369
|
+
var import_client2 = require("eve/client");
|
|
370
|
+
|
|
371
|
+
// src/runtime/capability.ts
|
|
372
|
+
var import_client = require("eve/client");
|
|
373
|
+
var MAX_REFRESH_SKEW_MS = 3e4;
|
|
374
|
+
var LOCAL_LOOPBACK_ORIGINS = [
|
|
375
|
+
"http://127.0.0.1:3010",
|
|
376
|
+
"http://127.0.0.1:3001"
|
|
377
|
+
];
|
|
378
|
+
function isLoopbackRuntimeOrigin(origin) {
|
|
374
379
|
try {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
buffer = lines.pop() ?? "";
|
|
380
|
-
for (const line of lines) {
|
|
381
|
-
const trimmed2 = line.trim();
|
|
382
|
-
if (!trimmed2) continue;
|
|
383
|
-
try {
|
|
384
|
-
const parsed = JSON.parse(trimmed2);
|
|
385
|
-
yield parsed;
|
|
386
|
-
} catch {
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (done) break;
|
|
390
|
-
}
|
|
391
|
-
const trimmed = buffer.trim();
|
|
392
|
-
if (trimmed) {
|
|
393
|
-
try {
|
|
394
|
-
const parsed = JSON.parse(trimmed);
|
|
395
|
-
yield parsed;
|
|
396
|
-
} catch {
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
} finally {
|
|
400
|
-
reader.releaseLock();
|
|
380
|
+
const host = new URL(origin).hostname;
|
|
381
|
+
return host === "127.0.0.1" || host === "localhost";
|
|
382
|
+
} catch {
|
|
383
|
+
return false;
|
|
401
384
|
}
|
|
402
385
|
}
|
|
403
|
-
function
|
|
404
|
-
|
|
405
|
-
|
|
386
|
+
function localBootstrapOrigins(origin) {
|
|
387
|
+
const normalized = origin.replace(/\/$/, "");
|
|
388
|
+
if (!isLoopbackRuntimeOrigin(normalized)) return [normalized];
|
|
389
|
+
return [
|
|
390
|
+
normalized,
|
|
391
|
+
...LOCAL_LOOPBACK_ORIGINS.filter((candidate) => candidate !== normalized)
|
|
392
|
+
];
|
|
406
393
|
}
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
return new Promise((resolve) => {
|
|
410
|
-
const finish = () => {
|
|
411
|
-
clearTimeout(timeout);
|
|
412
|
-
signal.removeEventListener("abort", finish);
|
|
413
|
-
resolve();
|
|
414
|
-
};
|
|
415
|
-
const timeout = setTimeout(finish, delayMs);
|
|
416
|
-
signal.addEventListener("abort", finish, { once: true });
|
|
417
|
-
});
|
|
394
|
+
function isRecord3(value) {
|
|
395
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
418
396
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
this.handlers = handlers;
|
|
423
|
-
this.parentSignal = parentSignal;
|
|
424
|
-
}
|
|
425
|
-
client;
|
|
426
|
-
handlers;
|
|
427
|
-
parentSignal;
|
|
428
|
-
controllers = /* @__PURE__ */ new Map();
|
|
429
|
-
tasks = /* @__PURE__ */ new Map();
|
|
430
|
-
begin(event) {
|
|
431
|
-
this.beginPath(event.data.childStreamPath);
|
|
397
|
+
function parseBootstrapResponse(value, indexId, now) {
|
|
398
|
+
if (!isRecord3(value) || value.apiVersion !== "webless.ai/agent-runtime-bootstrap/v1" || typeof value.accessToken !== "string" || !value.accessToken || typeof value.expiresAt !== "string" || !isRecord3(value.identity) || value.identity.indexId !== indexId || typeof value.identity.revision !== "string" || !value.identity.revision || typeof value.identity.tenantId !== "string" || !value.identity.tenantId || typeof value.origin !== "string" || !value.origin || value.tokenType !== "Bearer" || typeof value.visitorSubject !== "string" || !value.visitorSubject) {
|
|
399
|
+
throw new Error("Agent Runtime returned an invalid access response.");
|
|
432
400
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
observedTaskCount = this.tasks.size;
|
|
437
|
-
await Promise.all(this.tasks.values());
|
|
438
|
-
}
|
|
401
|
+
const expiresAt = Date.parse(value.expiresAt);
|
|
402
|
+
if (!Number.isFinite(expiresAt) || expiresAt <= now) {
|
|
403
|
+
throw new Error("Agent Runtime returned an expired access response.");
|
|
439
404
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
405
|
+
const refreshSkew = Math.min(
|
|
406
|
+
MAX_REFRESH_SKEW_MS,
|
|
407
|
+
Math.floor((expiresAt - now) / 10)
|
|
408
|
+
);
|
|
409
|
+
return {
|
|
410
|
+
accessToken: value.accessToken,
|
|
411
|
+
refreshAt: expiresAt - refreshSkew
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async function readBootstrapError(response) {
|
|
415
|
+
const fallback = `Agent Runtime is unavailable (${response.status}).`;
|
|
416
|
+
try {
|
|
417
|
+
const value = await response.json();
|
|
418
|
+
return isRecord3(value) && typeof value.error === "string" && value.error ? value.error : fallback;
|
|
419
|
+
} catch {
|
|
420
|
+
return fallback;
|
|
443
421
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
()
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
422
|
+
}
|
|
423
|
+
function createAgentRuntimeCapability(options) {
|
|
424
|
+
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
425
|
+
const now = options.now ?? Date.now;
|
|
426
|
+
let capability;
|
|
427
|
+
let generation = 0;
|
|
428
|
+
let pendingBootstrap;
|
|
429
|
+
const bootstrap = async () => {
|
|
430
|
+
let previewGrant;
|
|
431
|
+
const previewBuildId = options.previewBuildId?.trim();
|
|
432
|
+
if (options.version === "unpublished") {
|
|
433
|
+
previewGrant = previewBuildId ? void 0 : (await options.getUnpublishedPreviewGrant?.())?.trim();
|
|
434
|
+
if (!previewGrant && !previewBuildId) {
|
|
435
|
+
throw new Error(
|
|
436
|
+
"Unpublished preview authorization is required to use the Agent Runtime preview."
|
|
437
|
+
);
|
|
460
438
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
439
|
+
}
|
|
440
|
+
const bootstrapBody = JSON.stringify({
|
|
441
|
+
clientSessionId: options.visitorSessionId,
|
|
442
|
+
indexId: options.indexId,
|
|
443
|
+
...previewBuildId ? { previewBuildId } : {},
|
|
444
|
+
...previewGrant ? { previewGrant } : {},
|
|
445
|
+
version: options.version
|
|
446
|
+
});
|
|
447
|
+
const postBootstrap = (origin) => fetchImplementation(`${origin}/webless/v1/bootstrap`, {
|
|
448
|
+
body: bootstrapBody,
|
|
449
|
+
headers: { "content-type": "application/json" },
|
|
450
|
+
method: "POST"
|
|
451
|
+
});
|
|
452
|
+
let response;
|
|
453
|
+
let lastError;
|
|
454
|
+
for (const origin of localBootstrapOrigins(options.runtimeOrigin)) {
|
|
470
455
|
try {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
signal
|
|
476
|
-
}
|
|
477
|
-
);
|
|
478
|
-
if (!response.ok || response.body === null) {
|
|
479
|
-
await response.body?.cancel().catch(() => {
|
|
480
|
-
});
|
|
481
|
-
throw new Error(`Child stream returned ${response.status}.`);
|
|
482
|
-
}
|
|
483
|
-
for await (const rawEvent of readNdjsonStream(response.body)) {
|
|
484
|
-
if (signal.aborted) return;
|
|
485
|
-
receivedEvent = true;
|
|
486
|
-
streamIndex += 1;
|
|
487
|
-
const event = parseChildStreamEvent(rawEvent);
|
|
488
|
-
if (event.type === "session.boundary") return;
|
|
489
|
-
if (event.type === "subagent.called") {
|
|
490
|
-
this.beginPath(event.childStreamPath);
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
if (event.type !== "action.result") continue;
|
|
494
|
-
this.handlers.onToolResult?.(event.result);
|
|
495
|
-
if (event.hasOutput) {
|
|
496
|
-
this.handlers.onActionResult?.(event.result.output);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
} catch {
|
|
500
|
-
if (signal.aborted) return;
|
|
456
|
+
response = await postBootstrap(origin);
|
|
457
|
+
break;
|
|
458
|
+
} catch (error) {
|
|
459
|
+
lastError = error;
|
|
501
460
|
}
|
|
502
|
-
consecutiveRetries = receivedEvent ? 0 : consecutiveRetries + 1;
|
|
503
|
-
retryDelayMs = receivedEvent ? INITIAL_RETRY_DELAY_MS : Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
504
|
-
await abortableDelay(retryDelayMs, signal);
|
|
505
461
|
}
|
|
462
|
+
if (!response) {
|
|
463
|
+
throw lastError instanceof Error ? lastError : new Error("Agent Runtime is unavailable.");
|
|
464
|
+
}
|
|
465
|
+
if (!response.ok) {
|
|
466
|
+
throw new Error(await readBootstrapError(response));
|
|
467
|
+
}
|
|
468
|
+
let value;
|
|
469
|
+
try {
|
|
470
|
+
value = await response.json();
|
|
471
|
+
} catch {
|
|
472
|
+
throw new Error("Agent Runtime returned an invalid access response.");
|
|
473
|
+
}
|
|
474
|
+
return parseBootstrapResponse(value, options.indexId, now());
|
|
475
|
+
};
|
|
476
|
+
const getAccessToken = async () => {
|
|
477
|
+
if (capability && capability.refreshAt > now()) {
|
|
478
|
+
return capability.accessToken;
|
|
479
|
+
}
|
|
480
|
+
const requestedGeneration = generation;
|
|
481
|
+
if (!pendingBootstrap) {
|
|
482
|
+
const trackedBootstrap = bootstrap().finally(() => {
|
|
483
|
+
if (pendingBootstrap === trackedBootstrap) {
|
|
484
|
+
pendingBootstrap = void 0;
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
pendingBootstrap = trackedBootstrap;
|
|
488
|
+
}
|
|
489
|
+
const nextCapability = await pendingBootstrap;
|
|
490
|
+
if (requestedGeneration !== generation) {
|
|
491
|
+
return getAccessToken();
|
|
492
|
+
}
|
|
493
|
+
capability = nextCapability;
|
|
494
|
+
return capability.accessToken;
|
|
495
|
+
};
|
|
496
|
+
return {
|
|
497
|
+
getAccessToken,
|
|
498
|
+
invalidate: () => {
|
|
499
|
+
capability = void 0;
|
|
500
|
+
generation += 1;
|
|
501
|
+
pendingBootstrap = void 0;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
async function withCapabilityRefresh(capability, request) {
|
|
506
|
+
try {
|
|
507
|
+
return await request();
|
|
508
|
+
} catch (error) {
|
|
509
|
+
if (!(error instanceof import_client.ClientError) || error.status !== 401) {
|
|
510
|
+
throw error;
|
|
511
|
+
}
|
|
512
|
+
capability.invalidate();
|
|
513
|
+
return await request();
|
|
506
514
|
}
|
|
507
|
-
}
|
|
515
|
+
}
|
|
508
516
|
|
|
509
|
-
// src/runtime/
|
|
510
|
-
var
|
|
511
|
-
var
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
517
|
+
// src/runtime/config.ts
|
|
518
|
+
var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
|
|
519
|
+
var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
|
|
520
|
+
function buildEveHost(origin, indexId, version = "published") {
|
|
521
|
+
const base = trimTrailingSlash(origin);
|
|
522
|
+
const searchParams = new URLSearchParams({ indexId: indexId.trim() });
|
|
523
|
+
if (version === "unpublished") {
|
|
524
|
+
searchParams.set("version", version);
|
|
525
|
+
}
|
|
526
|
+
return `${base}?${searchParams.toString()}`;
|
|
527
|
+
}
|
|
528
|
+
function resolveAgentRuntimeConfig(input) {
|
|
529
|
+
const origin = trimTrailingSlash(input.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN);
|
|
530
|
+
const resolvedIndexId = input.indexId.trim();
|
|
531
|
+
const version = input.version ?? "published";
|
|
532
|
+
return {
|
|
533
|
+
origin,
|
|
534
|
+
indexId: resolvedIndexId,
|
|
535
|
+
version,
|
|
536
|
+
host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId, version) : origin
|
|
519
537
|
};
|
|
520
|
-
return `${AGENT_STRUCTURED_TOOL_INPUT_HEADER} ${JSON.stringify(payload)}`;
|
|
521
538
|
}
|
|
522
|
-
function
|
|
523
|
-
|
|
539
|
+
function agentHealthUrl(runtimeOrigin) {
|
|
540
|
+
const origin = trimTrailingSlash(runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN);
|
|
541
|
+
return `${origin}/eve/v1/health`;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/runtime/session-store.ts
|
|
545
|
+
var DEFAULT_STORAGE_KEY_PREFIX = "webless:agent";
|
|
546
|
+
function normalizeRuntimeOrigin(runtimeOrigin) {
|
|
547
|
+
const trimmed = runtimeOrigin?.trim();
|
|
548
|
+
if (!trimmed) return "_";
|
|
549
|
+
return trimmed.replace(/\/+$/, "");
|
|
550
|
+
}
|
|
551
|
+
function buildAgentStorageKeyPrefix(input) {
|
|
552
|
+
const customerId = input.customerId?.trim() || "_";
|
|
553
|
+
const indexId = input.indexId.trim();
|
|
554
|
+
if (!indexId) {
|
|
555
|
+
throw new Error("indexId is required to build agent storage keys.");
|
|
556
|
+
}
|
|
557
|
+
const versionSuffix = input.version === "unpublished" ? ":unpublished" : "";
|
|
558
|
+
return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}${versionSuffix}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
|
|
524
559
|
}
|
|
525
|
-
function
|
|
526
|
-
if (
|
|
527
|
-
return
|
|
560
|
+
function createSessionId() {
|
|
561
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
562
|
+
return crypto.randomUUID();
|
|
528
563
|
}
|
|
529
|
-
|
|
530
|
-
if (typeof value !== "object") return false;
|
|
531
|
-
if (seen.has(value)) return false;
|
|
532
|
-
seen.add(value);
|
|
533
|
-
const valid = Array.isArray(value) ? value.every((item) => isJsonValue(item, seen)) : Object.entries(value).every(
|
|
534
|
-
([key, item]) => typeof key === "string" && isJsonValue(item, seen)
|
|
535
|
-
);
|
|
536
|
-
seen.delete(value);
|
|
537
|
-
return valid;
|
|
564
|
+
return `sess_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
538
565
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
|
|
566
|
+
function resolvePrefix(options) {
|
|
567
|
+
return options?.storageKeyPrefix?.trim() || DEFAULT_STORAGE_KEY_PREFIX;
|
|
568
|
+
}
|
|
569
|
+
function getOrCreateVisitorSessionId(options) {
|
|
570
|
+
const prefix = resolvePrefix(options);
|
|
571
|
+
const visitorKey = `${prefix}:visitorSessionId`;
|
|
572
|
+
if (typeof sessionStorage === "undefined") {
|
|
573
|
+
return createSessionId();
|
|
542
574
|
}
|
|
543
|
-
|
|
575
|
+
const existing = sessionStorage.getItem(visitorKey)?.trim();
|
|
576
|
+
if (existing) return existing;
|
|
577
|
+
const next = createSessionId();
|
|
578
|
+
sessionStorage.setItem(visitorKey, next);
|
|
579
|
+
return next;
|
|
544
580
|
}
|
|
545
|
-
function
|
|
546
|
-
|
|
581
|
+
function clearVisitorSessionId(options) {
|
|
582
|
+
if (typeof sessionStorage === "undefined") return;
|
|
583
|
+
const prefix = resolvePrefix(options);
|
|
584
|
+
sessionStorage.removeItem(`${prefix}:visitorSessionId`);
|
|
547
585
|
}
|
|
548
|
-
function
|
|
549
|
-
return
|
|
586
|
+
function runtimeSessionIdKey(visitorSessionId, prefix) {
|
|
587
|
+
return `${prefix}:eve:${visitorSessionId}:sessionId`;
|
|
550
588
|
}
|
|
551
|
-
function
|
|
552
|
-
return
|
|
553
|
-
"text",
|
|
554
|
-
"textarea",
|
|
555
|
-
"email",
|
|
556
|
-
"number",
|
|
557
|
-
"select",
|
|
558
|
-
"multi-select",
|
|
559
|
-
"checkbox",
|
|
560
|
-
"confirmation",
|
|
561
|
-
"radio",
|
|
562
|
-
"date",
|
|
563
|
-
"time",
|
|
564
|
-
"date-time",
|
|
565
|
-
"calendar",
|
|
566
|
-
"range",
|
|
567
|
-
"json"
|
|
568
|
-
].includes(value);
|
|
589
|
+
function runtimeStreamIndexKey(visitorSessionId, prefix) {
|
|
590
|
+
return `${prefix}:eve:${visitorSessionId}:streamIndex`;
|
|
569
591
|
}
|
|
570
|
-
function
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
"options",
|
|
582
|
-
"path",
|
|
583
|
-
"placeholder",
|
|
584
|
-
"required",
|
|
585
|
-
"step",
|
|
586
|
-
"defaultValue"
|
|
587
|
-
])) {
|
|
588
|
-
return null;
|
|
589
|
-
}
|
|
590
|
-
if (!isFieldKind(value.kind)) return null;
|
|
591
|
-
const path = boundedString(value.path, 160);
|
|
592
|
-
const label = boundedString(value.label, 160);
|
|
593
|
-
const description = value.description === void 0 ? void 0 : boundedString(value.description, 500);
|
|
594
|
-
const placeholder = value.placeholder === void 0 || typeof value.placeholder !== "string" ? void 0 : value.placeholder;
|
|
595
|
-
const required = value.required === void 0 || typeof value.required !== "boolean" ? void 0 : value.required;
|
|
596
|
-
const min = value.min === void 0 ? void 0 : numberValue(value.min);
|
|
597
|
-
const max = value.max === void 0 ? void 0 : numberValue(value.max);
|
|
598
|
-
const maxItems = value.maxItems === void 0 ? void 0 : integerValue(value.maxItems);
|
|
599
|
-
const step = value.step === void 0 ? void 0 : numberValue(value.step);
|
|
600
|
-
const minLength = value.minLength === void 0 ? void 0 : integerValue(value.minLength);
|
|
601
|
-
const maxLength = value.maxLength === void 0 ? void 0 : integerValue(value.maxLength);
|
|
602
|
-
const defaultValue = value.defaultValue === void 0 || !isJsonValue(value.defaultValue) ? void 0 : value.defaultValue;
|
|
603
|
-
if (!path || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || !label) {
|
|
604
|
-
return null;
|
|
605
|
-
}
|
|
606
|
-
if (value.description !== void 0 && !description) return null;
|
|
607
|
-
if (value.placeholder !== void 0 && placeholder === void 0) return null;
|
|
608
|
-
if (value.required !== void 0 && required === void 0) return null;
|
|
609
|
-
if (value.min !== void 0 && min === void 0) return null;
|
|
610
|
-
if (value.max !== void 0 && max === void 0) return null;
|
|
611
|
-
if (value.maxItems !== void 0 && (maxItems === void 0 || maxItems < 1 || maxItems > 100))
|
|
612
|
-
return null;
|
|
613
|
-
if (value.step !== void 0 && step === void 0) return null;
|
|
614
|
-
if (value.minLength !== void 0 && minLength === void 0) return null;
|
|
615
|
-
if (value.maxLength !== void 0 && maxLength === void 0) return null;
|
|
616
|
-
if (value.defaultValue !== void 0 && defaultValue === void 0)
|
|
617
|
-
return null;
|
|
618
|
-
if (value.options !== void 0) {
|
|
619
|
-
if (!Array.isArray(value.options) || value.options.length > 100)
|
|
620
|
-
return null;
|
|
621
|
-
for (const option of value.options) {
|
|
622
|
-
if (!isRecord3(option) || !boundedString(option.label, 160) || !isJsonValue(option.value)) {
|
|
623
|
-
return null;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
}
|
|
592
|
+
function runtimeLastMessageKey(visitorSessionId, prefix) {
|
|
593
|
+
return `${prefix}:eve:${visitorSessionId}:lastMessage`;
|
|
594
|
+
}
|
|
595
|
+
function loadPersistedAgentSession(visitorSessionId, options) {
|
|
596
|
+
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return null;
|
|
597
|
+
const prefix = resolvePrefix(options);
|
|
598
|
+
const sessionId = sessionStorage.getItem(runtimeSessionIdKey(visitorSessionId, prefix))?.trim();
|
|
599
|
+
if (!sessionId) return null;
|
|
600
|
+
const rawIndex = sessionStorage.getItem(runtimeStreamIndexKey(visitorSessionId, prefix));
|
|
601
|
+
const streamIndex = rawIndex ? Number.parseInt(rawIndex, 10) : 0;
|
|
602
|
+
const lastMessage = sessionStorage.getItem(runtimeLastMessageKey(visitorSessionId, prefix))?.trim();
|
|
627
603
|
return {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
...description ? { description } : {},
|
|
632
|
-
...placeholder !== void 0 ? { placeholder } : {},
|
|
633
|
-
...required !== void 0 ? { required } : {},
|
|
634
|
-
...defaultValue !== void 0 ? { defaultValue } : {},
|
|
635
|
-
...value.options !== void 0 ? { options: value.options } : {},
|
|
636
|
-
...min !== void 0 ? { min } : {},
|
|
637
|
-
...max !== void 0 ? { max } : {},
|
|
638
|
-
...maxItems !== void 0 ? { maxItems } : {},
|
|
639
|
-
...step !== void 0 ? { step } : {},
|
|
640
|
-
...minLength !== void 0 ? { minLength } : {},
|
|
641
|
-
...maxLength !== void 0 ? { maxLength } : {}
|
|
604
|
+
sessionId,
|
|
605
|
+
streamIndex: Number.isFinite(streamIndex) && streamIndex >= 0 ? streamIndex : 0,
|
|
606
|
+
...lastMessage ? { lastMessage } : {}
|
|
642
607
|
};
|
|
643
608
|
}
|
|
644
|
-
function
|
|
645
|
-
if (!
|
|
646
|
-
|
|
647
|
-
return null;
|
|
609
|
+
function savePersistedAgentSession(visitorSessionId, sessionId, streamIndex, options) {
|
|
610
|
+
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !sessionId.trim()) {
|
|
611
|
+
return;
|
|
648
612
|
}
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
(
|
|
654
|
-
)
|
|
655
|
-
|
|
613
|
+
const prefix = resolvePrefix(options);
|
|
614
|
+
sessionStorage.setItem(runtimeSessionIdKey(visitorSessionId, prefix), sessionId);
|
|
615
|
+
sessionStorage.setItem(
|
|
616
|
+
runtimeStreamIndexKey(visitorSessionId, prefix),
|
|
617
|
+
String(Math.max(0, streamIndex))
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
function savePersistedAgentTurnMessage(visitorSessionId, message, options) {
|
|
621
|
+
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim() || !message) {
|
|
622
|
+
return;
|
|
656
623
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
label,
|
|
660
|
-
fieldPaths: value.fieldPaths,
|
|
661
|
-
...description ? { description } : {}
|
|
662
|
-
};
|
|
624
|
+
const prefix = resolvePrefix(options);
|
|
625
|
+
sessionStorage.setItem(runtimeLastMessageKey(visitorSessionId, prefix), message);
|
|
663
626
|
}
|
|
664
|
-
function
|
|
665
|
-
if (!
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
627
|
+
function clearPersistedAgentSession(visitorSessionId, options) {
|
|
628
|
+
if (typeof sessionStorage === "undefined" || !visitorSessionId.trim()) return;
|
|
629
|
+
const prefix = resolvePrefix(options);
|
|
630
|
+
sessionStorage.removeItem(runtimeSessionIdKey(visitorSessionId, prefix));
|
|
631
|
+
sessionStorage.removeItem(runtimeStreamIndexKey(visitorSessionId, prefix));
|
|
632
|
+
sessionStorage.removeItem(runtimeLastMessageKey(visitorSessionId, prefix));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/runtime/subagent-child-stream.ts
|
|
636
|
+
var INITIAL_RETRY_DELAY_MS = 100;
|
|
637
|
+
var MAX_RETRY_DELAY_MS = 2e3;
|
|
638
|
+
var MAX_CONSECUTIVE_RETRIES = 6;
|
|
639
|
+
function isRecord4(value) {
|
|
640
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
641
|
+
}
|
|
642
|
+
function parseError(value) {
|
|
643
|
+
if (!isRecord4(value)) return void 0;
|
|
644
|
+
const { code, message } = value;
|
|
645
|
+
if (typeof code !== "string" || typeof message !== "string") {
|
|
646
|
+
return void 0;
|
|
670
647
|
}
|
|
671
|
-
return {
|
|
672
|
-
id: value.id,
|
|
673
|
-
label
|
|
674
|
-
};
|
|
648
|
+
return { code, message };
|
|
675
649
|
}
|
|
676
|
-
function
|
|
677
|
-
if (!
|
|
678
|
-
return
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
"
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
"
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
650
|
+
function parseChildStreamEvent(value) {
|
|
651
|
+
if (!isRecord4(value) || typeof value.type !== "string") {
|
|
652
|
+
return { type: "other" };
|
|
653
|
+
}
|
|
654
|
+
if (value.type === "session.waiting" || value.type === "session.completed" || value.type === "session.failed") {
|
|
655
|
+
return { type: "session.boundary" };
|
|
656
|
+
}
|
|
657
|
+
if (value.type === "subagent.event" && isRecord4(value.data) && Object.hasOwn(value.data, "event")) {
|
|
658
|
+
return parseChildStreamEvent(value.data.event);
|
|
659
|
+
}
|
|
660
|
+
if (value.type === "subagent.called" && isRecord4(value.data)) {
|
|
661
|
+
const { childStreamPath } = value.data;
|
|
662
|
+
if (typeof childStreamPath === "string") {
|
|
663
|
+
return { childStreamPath, type: "subagent.called" };
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (value.type === "actions.requested" && isRecord4(value.data) && Array.isArray(value.data.actions)) {
|
|
667
|
+
const items = value.data.actions.flatMap((action) => {
|
|
668
|
+
if (!isRecord4(action) || action.kind !== "tool-call" || typeof action.callId !== "string" || typeof action.toolName !== "string" || !isRecord4(action.input)) return [];
|
|
669
|
+
const item = connectedToolWork({
|
|
670
|
+
callId: action.callId,
|
|
671
|
+
toolName: action.toolName,
|
|
672
|
+
input: action.input
|
|
673
|
+
});
|
|
674
|
+
return item ? [item] : [];
|
|
675
|
+
});
|
|
676
|
+
return { type: "actions.requested", items };
|
|
677
|
+
}
|
|
678
|
+
if (value.type !== "action.result" || !isRecord4(value.data)) {
|
|
679
|
+
return { type: "other" };
|
|
680
|
+
}
|
|
681
|
+
const { data } = value;
|
|
682
|
+
if (data.status !== "completed" && data.status !== "failed" && data.status !== "rejected") {
|
|
683
|
+
return { type: "other" };
|
|
694
684
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const toolSlug = boundedString(value.toolSlug, 200);
|
|
698
|
-
const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
|
|
699
|
-
const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
|
|
700
|
-
const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
|
|
701
|
-
const submitLabel = value.submitLabel === void 0 ? void 0 : boundedString(value.submitLabel, 80);
|
|
702
|
-
if (!id || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
|
|
703
|
-
return null;
|
|
685
|
+
if (!isRecord4(data.result) || data.result.kind !== "tool-result") {
|
|
686
|
+
return { type: "other" };
|
|
704
687
|
}
|
|
705
|
-
const
|
|
706
|
-
if (
|
|
707
|
-
|
|
708
|
-
if (steps?.some((step) => step === null)) return null;
|
|
709
|
-
const actions = value.actions === void 0 ? void 0 : Array.isArray(value.actions) && value.actions.length <= 8 ? value.actions.map(parseAction) : null;
|
|
710
|
-
if (actions?.some((action) => action === null)) return null;
|
|
711
|
-
if (value.description !== void 0 && !description) return null;
|
|
712
|
-
if (value.operationId !== void 0 && !operationId) return null;
|
|
713
|
-
if (value.requestId !== void 0 && !requestId) return null;
|
|
714
|
-
if (value.submitLabel !== void 0 && !submitLabel) return null;
|
|
715
|
-
const values = value.values !== void 0 && isRecord3(value.values) ? value.values : void 0;
|
|
716
|
-
if (value.values !== void 0) {
|
|
717
|
-
if (!values || !Object.values(values).every((item) => isJsonValue(item)))
|
|
718
|
-
return null;
|
|
688
|
+
const result = data.result;
|
|
689
|
+
if (typeof result.callId !== "string" || typeof result.toolName !== "string") {
|
|
690
|
+
return { type: "other" };
|
|
719
691
|
}
|
|
692
|
+
const error = parseError(data.error);
|
|
720
693
|
return {
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
...submitLabel ? { submitLabel } : {},
|
|
731
|
-
...steps ? { steps } : {},
|
|
732
|
-
...values ? { values } : {}
|
|
694
|
+
type: "action.result",
|
|
695
|
+
hasOutput: Object.hasOwn(result, "output"),
|
|
696
|
+
result: {
|
|
697
|
+
callId: result.callId,
|
|
698
|
+
toolName: result.toolName,
|
|
699
|
+
status: result.isError === true ? "failed" : data.status,
|
|
700
|
+
...Object.hasOwn(result, "output") ? { output: result.output } : {},
|
|
701
|
+
...error ? { error } : {}
|
|
702
|
+
}
|
|
733
703
|
};
|
|
734
704
|
}
|
|
735
|
-
function
|
|
736
|
-
const
|
|
737
|
-
|
|
705
|
+
async function* readNdjsonStream(body) {
|
|
706
|
+
const reader = body.getReader();
|
|
707
|
+
const decoder = new TextDecoder();
|
|
708
|
+
let buffer = "";
|
|
709
|
+
try {
|
|
710
|
+
while (true) {
|
|
711
|
+
const { done, value } = await reader.read();
|
|
712
|
+
buffer += decoder.decode(value, { stream: !done });
|
|
713
|
+
const lines = buffer.split("\n");
|
|
714
|
+
buffer = lines.pop() ?? "";
|
|
715
|
+
for (const line of lines) {
|
|
716
|
+
const trimmed2 = line.trim();
|
|
717
|
+
if (!trimmed2) continue;
|
|
718
|
+
try {
|
|
719
|
+
const parsed = JSON.parse(trimmed2);
|
|
720
|
+
yield parsed;
|
|
721
|
+
} catch {
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
if (done) break;
|
|
725
|
+
}
|
|
726
|
+
const trimmed = buffer.trim();
|
|
727
|
+
if (trimmed) {
|
|
728
|
+
try {
|
|
729
|
+
const parsed = JSON.parse(trimmed);
|
|
730
|
+
yield parsed;
|
|
731
|
+
} catch {
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
} finally {
|
|
735
|
+
reader.releaseLock();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function streamPathAt(path, streamIndex) {
|
|
739
|
+
if (streamIndex === 0) return path;
|
|
740
|
+
return `${path}${path.includes("?") ? "&" : "?"}startIndex=${streamIndex}`;
|
|
741
|
+
}
|
|
742
|
+
function abortableDelay(delayMs, signal) {
|
|
743
|
+
if (signal.aborted) return Promise.resolve();
|
|
744
|
+
return new Promise((resolve) => {
|
|
745
|
+
const finish = () => {
|
|
746
|
+
clearTimeout(timeout);
|
|
747
|
+
signal.removeEventListener("abort", finish);
|
|
748
|
+
resolve();
|
|
749
|
+
};
|
|
750
|
+
const timeout = setTimeout(finish, delayMs);
|
|
751
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
752
|
+
});
|
|
738
753
|
}
|
|
754
|
+
var SubagentChildStreamCoordinator = class {
|
|
755
|
+
constructor(client, handlers, parentSignal) {
|
|
756
|
+
this.client = client;
|
|
757
|
+
this.handlers = handlers;
|
|
758
|
+
this.parentSignal = parentSignal;
|
|
759
|
+
}
|
|
760
|
+
client;
|
|
761
|
+
handlers;
|
|
762
|
+
parentSignal;
|
|
763
|
+
controllers = /* @__PURE__ */ new Map();
|
|
764
|
+
tasks = /* @__PURE__ */ new Map();
|
|
765
|
+
begin(event) {
|
|
766
|
+
this.beginPath(event.data.childStreamPath);
|
|
767
|
+
}
|
|
768
|
+
async waitForAll() {
|
|
769
|
+
let observedTaskCount = -1;
|
|
770
|
+
while (observedTaskCount !== this.tasks.size) {
|
|
771
|
+
observedTaskCount = this.tasks.size;
|
|
772
|
+
await Promise.all(this.tasks.values());
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
abortAll() {
|
|
776
|
+
for (const controller of this.controllers.values()) controller.abort();
|
|
777
|
+
this.controllers.clear();
|
|
778
|
+
}
|
|
779
|
+
beginPath(childStreamPath) {
|
|
780
|
+
if (this.tasks.has(childStreamPath)) return;
|
|
781
|
+
const controller = new AbortController();
|
|
782
|
+
const abort = () => controller.abort();
|
|
783
|
+
if (this.parentSignal.aborted) {
|
|
784
|
+
controller.abort();
|
|
785
|
+
} else {
|
|
786
|
+
this.parentSignal.addEventListener("abort", abort, { once: true });
|
|
787
|
+
}
|
|
788
|
+
this.controllers.set(childStreamPath, controller);
|
|
789
|
+
const task = this.consume(childStreamPath, controller.signal).finally(
|
|
790
|
+
() => {
|
|
791
|
+
this.parentSignal.removeEventListener("abort", abort);
|
|
792
|
+
if (this.controllers.get(childStreamPath) === controller) {
|
|
793
|
+
this.controllers.delete(childStreamPath);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
this.tasks.set(childStreamPath, task);
|
|
798
|
+
}
|
|
799
|
+
async consume(path, signal) {
|
|
800
|
+
const workItems = /* @__PURE__ */ new Map();
|
|
801
|
+
let streamIndex = 0;
|
|
802
|
+
let consecutiveRetries = 0;
|
|
803
|
+
let retryDelayMs = INITIAL_RETRY_DELAY_MS;
|
|
804
|
+
while (!signal.aborted && consecutiveRetries < MAX_CONSECUTIVE_RETRIES) {
|
|
805
|
+
let receivedEvent = false;
|
|
806
|
+
try {
|
|
807
|
+
const response = await this.client.fetch(
|
|
808
|
+
streamPathAt(path, streamIndex),
|
|
809
|
+
{
|
|
810
|
+
cache: "no-store",
|
|
811
|
+
signal
|
|
812
|
+
}
|
|
813
|
+
);
|
|
814
|
+
if (!response.ok || response.body === null) {
|
|
815
|
+
await response.body?.cancel().catch(() => {
|
|
816
|
+
});
|
|
817
|
+
throw new Error(`Child stream returned ${response.status}.`);
|
|
818
|
+
}
|
|
819
|
+
for await (const rawEvent of readNdjsonStream(response.body)) {
|
|
820
|
+
if (signal.aborted) return;
|
|
821
|
+
receivedEvent = true;
|
|
822
|
+
streamIndex += 1;
|
|
823
|
+
const event = parseChildStreamEvent(rawEvent);
|
|
824
|
+
if (event.type === "session.boundary") return;
|
|
825
|
+
if (event.type === "subagent.called") {
|
|
826
|
+
this.beginPath(event.childStreamPath);
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
if (event.type === "actions.requested") {
|
|
830
|
+
for (const item2 of event.items) {
|
|
831
|
+
workItems.set(item2.id, item2);
|
|
832
|
+
this.handlers.onWork?.(item2);
|
|
833
|
+
}
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (event.type !== "action.result") continue;
|
|
837
|
+
const item = workItems.get(event.result.callId);
|
|
838
|
+
if (item) {
|
|
839
|
+
this.handlers.onWork?.(completeConnectedToolWork(item, event.result));
|
|
840
|
+
}
|
|
841
|
+
this.handlers.onToolResult?.(event.result);
|
|
842
|
+
if (event.hasOutput) {
|
|
843
|
+
this.handlers.onActionResult?.(event.result.output);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
} catch {
|
|
847
|
+
if (signal.aborted) return;
|
|
848
|
+
}
|
|
849
|
+
consecutiveRetries = receivedEvent ? 0 : consecutiveRetries + 1;
|
|
850
|
+
retryDelayMs = receivedEvent ? INITIAL_RETRY_DELAY_MS : Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);
|
|
851
|
+
await abortableDelay(retryDelayMs, signal);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
};
|
|
739
855
|
|
|
740
856
|
// src/runtime/client.ts
|
|
741
857
|
function isTurnBoundary(event) {
|
|
@@ -769,7 +885,7 @@ function emitActionResult(event, handlers) {
|
|
|
769
885
|
handlers.onToolResult?.({
|
|
770
886
|
callId: result.callId,
|
|
771
887
|
toolName: result.toolName,
|
|
772
|
-
status: event.data.status,
|
|
888
|
+
status: result.isError ? "failed" : event.data.status,
|
|
773
889
|
output: result.output,
|
|
774
890
|
...event.data.error ? { error: event.data.error } : {}
|
|
775
891
|
});
|
|
@@ -895,9 +1011,13 @@ function requestedWorkItem(action) {
|
|
|
895
1011
|
state: "active"
|
|
896
1012
|
};
|
|
897
1013
|
}
|
|
898
|
-
return null;
|
|
1014
|
+
return action.kind === "tool-call" ? connectedToolWork(action) : null;
|
|
899
1015
|
}
|
|
900
1016
|
function applyWorkEvent(event, handlers, workItems) {
|
|
1017
|
+
if (event.type === "subagent.event") {
|
|
1018
|
+
applyWorkEvent(event.data.event, handlers, workItems);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
901
1021
|
if (event.type === "step.started" && workItems.size === 0) {
|
|
902
1022
|
emitWorkItem(
|
|
903
1023
|
{
|
|
@@ -956,6 +1076,15 @@ function applyWorkEvent(event, handlers, workItems) {
|
|
|
956
1076
|
const { result, status } = event.data;
|
|
957
1077
|
const current = workItems.get(result.callId);
|
|
958
1078
|
if (!current) return;
|
|
1079
|
+
if (current.kind === "tool" && result.kind === "tool-result") {
|
|
1080
|
+
emitWorkItem(completeConnectedToolWork(current, {
|
|
1081
|
+
callId: result.callId,
|
|
1082
|
+
toolName: result.toolName,
|
|
1083
|
+
status: result.isError ? "failed" : status,
|
|
1084
|
+
output: result.output
|
|
1085
|
+
}), handlers, workItems);
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
959
1088
|
const failed = status !== "completed" || result.isError === true;
|
|
960
1089
|
emitWorkItem(
|
|
961
1090
|
{
|
|
@@ -1443,63 +1572,6 @@ function formatAgentError(error) {
|
|
|
1443
1572
|
return TRANSIENT_AGENT_ERROR_MESSAGE;
|
|
1444
1573
|
}
|
|
1445
1574
|
|
|
1446
|
-
// src/runtime/tool-result-envelope.ts
|
|
1447
|
-
var ENVELOPE_KEYS = /* @__PURE__ */ new Set([
|
|
1448
|
-
"schemaVersion",
|
|
1449
|
-
"output",
|
|
1450
|
-
"presentationKinds",
|
|
1451
|
-
"ui"
|
|
1452
|
-
]);
|
|
1453
|
-
function isRecord4(value) {
|
|
1454
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1455
|
-
}
|
|
1456
|
-
function isJsonValue2(value, seen = /* @__PURE__ */ new Set()) {
|
|
1457
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
1458
|
-
return true;
|
|
1459
|
-
}
|
|
1460
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
1461
|
-
if (typeof value !== "object") return false;
|
|
1462
|
-
if (seen.has(value)) return false;
|
|
1463
|
-
seen.add(value);
|
|
1464
|
-
const valid = Array.isArray(value) ? value.every((item) => isJsonValue2(item, seen)) : Object.entries(value).every(
|
|
1465
|
-
([key, item]) => typeof key === "string" && isJsonValue2(item, seen)
|
|
1466
|
-
);
|
|
1467
|
-
seen.delete(value);
|
|
1468
|
-
return valid;
|
|
1469
|
-
}
|
|
1470
|
-
function decodeEnvelope(value) {
|
|
1471
|
-
if (typeof value !== "string") return value;
|
|
1472
|
-
try {
|
|
1473
|
-
return JSON.parse(value);
|
|
1474
|
-
} catch {
|
|
1475
|
-
return null;
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
function parseAgentToolResultEnvelope(value) {
|
|
1479
|
-
const decoded = decodeEnvelope(value);
|
|
1480
|
-
if (!isRecord4(decoded)) return null;
|
|
1481
|
-
const keys = Object.keys(decoded);
|
|
1482
|
-
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) {
|
|
1483
|
-
return null;
|
|
1484
|
-
}
|
|
1485
|
-
const presentationKinds = decoded.presentationKinds.flatMap((kind) => {
|
|
1486
|
-
if (typeof kind !== "string") return [];
|
|
1487
|
-
const normalized = kind.trim();
|
|
1488
|
-
return normalized && normalized.length <= 128 ? [normalized] : [];
|
|
1489
|
-
});
|
|
1490
|
-
if (presentationKinds.length !== decoded.presentationKinds.length) {
|
|
1491
|
-
return null;
|
|
1492
|
-
}
|
|
1493
|
-
const ui = decoded.ui === void 0 ? void 0 : parseAgentToolUiSurface(decoded.ui);
|
|
1494
|
-
if (decoded.ui !== void 0 && !ui) return null;
|
|
1495
|
-
return {
|
|
1496
|
-
schemaVersion: "webless.tool-result.v1",
|
|
1497
|
-
output: decoded.output,
|
|
1498
|
-
presentationKinds,
|
|
1499
|
-
...ui ? { ui } : {}
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
1575
|
// src/runtime/health.ts
|
|
1504
1576
|
async function pingAgentHealth(runtimeOrigin, fetchImpl = fetch) {
|
|
1505
1577
|
const healthUrl = agentHealthUrl(runtimeOrigin);
|