@webless/agent 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/embed.cjs CHANGED
@@ -22,6 +22,7 @@ var embed_exports = {};
22
22
  __export(embed_exports, {
23
23
  DEFAULT_AGENT_PLACEMENT: () => DEFAULT_AGENT_PLACEMENT,
24
24
  bootstrapAgent: () => bootstrapAgent,
25
+ createWeblessAgentPublicApi: () => createWeblessAgentPublicApi,
25
26
  getMountedAgent: () => getMountedAgent,
26
27
  mountAgent: () => mountAgent,
27
28
  normalizeAgentPlacement: () => normalizeAgentPlacement,
@@ -31,7 +32,7 @@ __export(embed_exports, {
31
32
  module.exports = __toCommonJS(embed_exports);
32
33
 
33
34
  // src/embed/mount.tsx
34
- var import_client4 = require("react-dom/client");
35
+ var import_client5 = require("react-dom/client");
35
36
 
36
37
  // src/react/panel-controller.ts
37
38
  var controllers = /* @__PURE__ */ new Map();
@@ -55,22 +56,110 @@ var import_react5 = require("react");
55
56
  var import_react = require("react");
56
57
 
57
58
  // src/runtime/client.ts
59
+ var import_client2 = require("eve/client");
60
+
61
+ // src/runtime/capability.ts
58
62
  var import_client = require("eve/client");
63
+ var MAX_REFRESH_SKEW_MS = 3e4;
64
+ function isRecord(value) {
65
+ return typeof value === "object" && value !== null && !Array.isArray(value);
66
+ }
67
+ function parseBootstrapResponse(value, indexId, now) {
68
+ if (!isRecord(value) || value.apiVersion !== "webless.ai/agent-runtime-bootstrap/v1" || typeof value.accessToken !== "string" || !value.accessToken || typeof value.expiresAt !== "string" || !isRecord(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) {
69
+ throw new Error("Agent Runtime returned an invalid access response.");
70
+ }
71
+ const expiresAt = Date.parse(value.expiresAt);
72
+ if (!Number.isFinite(expiresAt) || expiresAt <= now) {
73
+ throw new Error("Agent Runtime returned an expired access response.");
74
+ }
75
+ const refreshSkew = Math.min(MAX_REFRESH_SKEW_MS, Math.floor((expiresAt - now) / 10));
76
+ return {
77
+ accessToken: value.accessToken,
78
+ refreshAt: expiresAt - refreshSkew
79
+ };
80
+ }
81
+ async function readBootstrapError(response) {
82
+ const fallback = `Agent Runtime is unavailable (${response.status}).`;
83
+ try {
84
+ const value = await response.json();
85
+ return isRecord(value) && typeof value.error === "string" && value.error ? value.error : fallback;
86
+ } catch {
87
+ return fallback;
88
+ }
89
+ }
90
+ function createAgentRuntimeCapability(options) {
91
+ const fetchImplementation = options.fetchImplementation ?? fetch;
92
+ const now = options.now ?? Date.now;
93
+ let capability;
94
+ let pendingBootstrap;
95
+ const bootstrap = async () => {
96
+ const response = await fetchImplementation(`${options.runtimeOrigin}/webless/v1/bootstrap`, {
97
+ body: JSON.stringify({
98
+ clientSessionId: options.visitorSessionId,
99
+ indexId: options.indexId
100
+ }),
101
+ headers: { "content-type": "application/json" },
102
+ method: "POST"
103
+ });
104
+ if (!response.ok) {
105
+ throw new Error(await readBootstrapError(response));
106
+ }
107
+ let value;
108
+ try {
109
+ value = await response.json();
110
+ } catch {
111
+ throw new Error("Agent Runtime returned an invalid access response.");
112
+ }
113
+ return parseBootstrapResponse(value, options.indexId, now());
114
+ };
115
+ return {
116
+ getAccessToken: async () => {
117
+ if (capability && capability.refreshAt > now()) {
118
+ return capability.accessToken;
119
+ }
120
+ pendingBootstrap ??= bootstrap().finally(() => {
121
+ pendingBootstrap = void 0;
122
+ });
123
+ capability = await pendingBootstrap;
124
+ return capability.accessToken;
125
+ },
126
+ invalidate: () => {
127
+ capability = void 0;
128
+ }
129
+ };
130
+ }
131
+ async function withCapabilityRefresh(capability, request) {
132
+ try {
133
+ return await request();
134
+ } catch (error) {
135
+ if (!(error instanceof import_client.ClientError) || error.status !== 401) {
136
+ throw error;
137
+ }
138
+ capability.invalidate();
139
+ return await request();
140
+ }
141
+ }
59
142
 
60
143
  // src/runtime/config.ts
61
144
  var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
62
145
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
63
- function buildEveHost(origin, indexId) {
146
+ function buildEveHost(origin, indexId, version = "published") {
64
147
  const base = trimTrailingSlash(origin);
65
- return `${base}?indexId=${encodeURIComponent(indexId.trim())}`;
148
+ const searchParams = new URLSearchParams({ indexId: indexId.trim() });
149
+ if (version === "unpublished") {
150
+ searchParams.set("version", version);
151
+ }
152
+ return `${base}?${searchParams.toString()}`;
66
153
  }
67
154
  function resolveAgentRuntimeConfig(input) {
68
155
  const origin = trimTrailingSlash(input.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN);
69
156
  const resolvedIndexId = input.indexId.trim();
157
+ const version = input.version ?? "published";
70
158
  return {
71
159
  origin,
72
160
  indexId: resolvedIndexId,
73
- host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId) : origin
161
+ version,
162
+ host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId, version) : origin
74
163
  };
75
164
  }
76
165
 
@@ -87,7 +176,8 @@ function buildAgentStorageKeyPrefix(input) {
87
176
  if (!indexId) {
88
177
  throw new Error("indexId is required to build agent storage keys.");
89
178
  }
90
- return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
179
+ const versionSuffix = input.version === "unpublished" ? ":unpublished" : "";
180
+ return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}${versionSuffix}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
91
181
  }
92
182
  function createSessionId() {
93
183
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
@@ -156,13 +246,20 @@ function mapStepLabel(event) {
156
246
  };
157
247
  }
158
248
  var AgentSession = class {
159
- constructor(indexId, runtimeOrigin, visitorSessionId, storeOptions) {
249
+ constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions) {
160
250
  this.indexId = indexId;
251
+ this.version = version;
161
252
  this.runtimeOrigin = runtimeOrigin;
162
253
  this.visitorSessionId = visitorSessionId;
163
254
  this.storeOptions = storeOptions;
255
+ this.capability = createAgentRuntimeCapability({
256
+ indexId,
257
+ runtimeOrigin,
258
+ visitorSessionId
259
+ });
164
260
  }
165
261
  indexId;
262
+ version;
166
263
  runtimeOrigin;
167
264
  visitorSessionId;
168
265
  storeOptions;
@@ -171,6 +268,7 @@ var AgentSession = class {
171
268
  session;
172
269
  activeResponse;
173
270
  renderedPrefix = "";
271
+ capability;
174
272
  getActiveSessionId() {
175
273
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
176
274
  }
@@ -193,6 +291,7 @@ var AgentSession = class {
193
291
  ensureClient() {
194
292
  const config = resolveAgentRuntimeConfig({
195
293
  indexId: this.indexId,
294
+ version: this.version,
196
295
  runtimeOrigin: this.runtimeOrigin
197
296
  });
198
297
  if (!config.indexId) {
@@ -202,7 +301,11 @@ var AgentSession = class {
202
301
  return this.client;
203
302
  }
204
303
  this.reset();
205
- this.client = new import_client.Client({ host: config.host });
304
+ this.client = new import_client2.Client({
305
+ auth: { bearer: () => this.capability.getAccessToken() },
306
+ host: config.host,
307
+ redirect: "error"
308
+ });
206
309
  this.clientHost = config.host;
207
310
  return this.client;
208
311
  }
@@ -221,9 +324,13 @@ var AgentSession = class {
221
324
  if (session) {
222
325
  this.session = session;
223
326
  try {
224
- response = await session.send(message, { signal });
327
+ const activeSession = session;
328
+ response = await withCapabilityRefresh(
329
+ this.capability,
330
+ () => activeSession.send(message, { signal })
331
+ );
225
332
  } catch (error) {
226
- if (error instanceof import_client.ClientError && error.status === 409 && error.code === "session_not_active") {
333
+ if (error instanceof import_client2.ClientError && error.status === 409 && error.code === "session_not_active") {
227
334
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
228
335
  this.session = void 0;
229
336
  session = void 0;
@@ -233,7 +340,10 @@ var AgentSession = class {
233
340
  }
234
341
  }
235
342
  if (!response) {
236
- const created = await client.sessions.create({ message, signal });
343
+ const created = await withCapabilityRefresh(
344
+ this.capability,
345
+ () => client.sessions.create({ message, signal })
346
+ );
237
347
  response = created.response;
238
348
  session = created.session;
239
349
  this.session = session;
@@ -291,18 +401,25 @@ function createAgentClient(options) {
291
401
  if (!indexId) {
292
402
  throw new Error("indexId is required.");
293
403
  }
294
- const runtimeOrigin = options.runtimeOrigin?.trim() || resolveAgentRuntimeConfig({ indexId }).origin;
404
+ const version = options.version ?? "published";
405
+ const runtimeOrigin = resolveAgentRuntimeConfig({
406
+ indexId,
407
+ runtimeOrigin: options.runtimeOrigin,
408
+ version
409
+ }).origin;
295
410
  const storeOptions = {
296
411
  storageKeyPrefix: options.storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({
297
412
  customerId: options.customerId,
298
413
  indexId,
414
+ version,
299
415
  runtimeOrigin
300
416
  })
301
417
  };
302
418
  const visitorSessionId = options.visitorSessionId?.trim() || getOrCreateVisitorSessionId(storeOptions);
303
- const session = new AgentSession(indexId, runtimeOrigin, visitorSessionId, storeOptions);
419
+ const session = new AgentSession(indexId, version, runtimeOrigin, visitorSessionId, storeOptions);
304
420
  return {
305
421
  indexId,
422
+ version,
306
423
  runtimeOrigin,
307
424
  visitorSessionId,
308
425
  sendTurn: (message, sendOptions) => session.sendTurn(
@@ -317,9 +434,9 @@ function createAgentClient(options) {
317
434
  }
318
435
 
319
436
  // src/runtime/errors.ts
320
- var import_client2 = require("eve/client");
437
+ var import_client3 = require("eve/client");
321
438
  function formatAgentError(error) {
322
- if (error instanceof import_client2.ClientError) {
439
+ if (error instanceof import_client3.ClientError) {
323
440
  if (error.status === 401 && error.code === "index_required") {
324
441
  return "Missing indexId \u2014 pass a published index id to createAgentClient().";
325
442
  }
@@ -385,6 +502,7 @@ async function runStatusSequence(signal, onStep) {
385
502
  function useAgentChat({
386
503
  customerId,
387
504
  indexId,
505
+ version,
388
506
  runtimeOrigin,
389
507
  visitorSessionId,
390
508
  storageKeyPrefix
@@ -392,8 +510,8 @@ function useAgentChat({
392
510
  const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
393
511
  const runRef = (0, import_react.useRef)(null);
394
512
  const resolvedStorageKeyPrefix = (0, import_react.useMemo)(
395
- () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, runtimeOrigin }),
396
- [customerId, indexId, runtimeOrigin, storageKeyPrefix]
513
+ () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, version, runtimeOrigin }),
514
+ [customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
397
515
  );
398
516
  const visitorId = (0, import_react.useMemo)(
399
517
  () => visitorSessionId?.trim() || getOrCreateVisitorSessionId({ storageKeyPrefix: resolvedStorageKeyPrefix }),
@@ -403,13 +521,14 @@ function useAgentChat({
403
521
  createAgentClient({
404
522
  customerId,
405
523
  indexId,
524
+ version,
406
525
  runtimeOrigin,
407
526
  visitorSessionId: visitorId,
408
527
  storageKeyPrefix: resolvedStorageKeyPrefix
409
528
  })
410
529
  );
411
530
  const identityRef = (0, import_react.useRef)(null);
412
- const identityKey = `${customerId ?? ""}|${indexId}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
531
+ const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
413
532
  (0, import_react.useEffect)(() => {
414
533
  if (identityRef.current === null) {
415
534
  identityRef.current = identityKey;
@@ -425,12 +544,13 @@ function useAgentChat({
425
544
  clientRef.current = createAgentClient({
426
545
  customerId,
427
546
  indexId,
547
+ version,
428
548
  runtimeOrigin,
429
549
  visitorSessionId: visitorId,
430
550
  storageKeyPrefix: resolvedStorageKeyPrefix
431
551
  });
432
552
  setState(INITIAL_STATE);
433
- }, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]);
553
+ }, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]);
434
554
  const reset = (0, import_react.useCallback)(() => {
435
555
  runRef.current?.abort();
436
556
  runRef.current = null;
@@ -533,7 +653,7 @@ function useAgentChat({
533
653
  }));
534
654
  }
535
655
  },
536
- [customerId, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]
656
+ [customerId, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]
537
657
  );
538
658
  (0, import_react.useEffect)(() => {
539
659
  return () => {
@@ -1004,6 +1124,7 @@ var import_jsx_runtime8 = require("react/jsx-runtime");
1004
1124
  function AgentWidget({
1005
1125
  indexId,
1006
1126
  customerId,
1127
+ version,
1007
1128
  runtimeOrigin,
1008
1129
  placement: placementInput,
1009
1130
  defaultCollapsed = true,
@@ -1016,6 +1137,7 @@ function AgentWidget({
1016
1137
  const { state, submit } = useAgentChat({
1017
1138
  customerId,
1018
1139
  indexId,
1140
+ version,
1019
1141
  runtimeOrigin
1020
1142
  });
1021
1143
  const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);
@@ -1096,6 +1218,7 @@ function AgentWidget2({ manifest }) {
1096
1218
  {
1097
1219
  indexId: manifest.indexId,
1098
1220
  customerId: manifest.customerId,
1221
+ version: manifest.version,
1099
1222
  runtimeOrigin: manifest.runtimeOrigin,
1100
1223
  placement: manifest.placement,
1101
1224
  defaultCollapsed: true,
@@ -1114,15 +1237,20 @@ function normalizeAgentTagManifest(manifest) {
1114
1237
  if (!customerId) {
1115
1238
  throw new Error("Agent manifest requires customerId.");
1116
1239
  }
1240
+ const version = manifest.version ?? "published";
1241
+ if (version !== "published" && version !== "unpublished") {
1242
+ throw new Error("Agent manifest version must be published or unpublished.");
1243
+ }
1117
1244
  const runtime = resolveAgentRuntimeConfig({
1118
1245
  indexId,
1246
+ version,
1119
1247
  runtimeOrigin: manifest.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN
1120
1248
  });
1121
1249
  return {
1122
1250
  customerId,
1123
1251
  indexId,
1124
1252
  runtimeOrigin: runtime.origin,
1125
- version: manifest.version?.trim() || "published",
1253
+ version,
1126
1254
  mount: {
1127
1255
  selector: manifest.mount?.selector?.trim(),
1128
1256
  strategy: manifest.mount?.strategy ?? "body"
@@ -1161,7 +1289,7 @@ function mountAgent(input) {
1161
1289
  const mountTarget = resolveMountHost(manifest, input.script ?? null);
1162
1290
  const host = createHost(manifest.customerId);
1163
1291
  mountTarget.append(host);
1164
- const root = (0, import_client4.createRoot)(host);
1292
+ const root = (0, import_client5.createRoot)(host);
1165
1293
  root.render(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentWidget2, { manifest }));
1166
1294
  const handle = {
1167
1295
  customerId: manifest.customerId,
@@ -1203,10 +1331,54 @@ function getMountedAgent(customerId) {
1203
1331
  function bootstrapAgent(manifest, script) {
1204
1332
  return mountAgent({ manifest, script });
1205
1333
  }
1334
+
1335
+ // src/embed/iife-bootstrap.ts
1336
+ function createWeblessAgentPublicApi(manifest) {
1337
+ const script = document.currentScript instanceof HTMLScriptElement ? document.currentScript : null;
1338
+ function mount(options = {}) {
1339
+ return mountAgent({
1340
+ manifest: {
1341
+ ...manifest,
1342
+ ...options,
1343
+ customerId: manifest.customerId
1344
+ },
1345
+ script
1346
+ });
1347
+ }
1348
+ function unmount(customerId) {
1349
+ return unmountAgent(customerId ?? manifest.customerId);
1350
+ }
1351
+ function open(customerId) {
1352
+ getMountedAgent(customerId ?? manifest.customerId)?.open();
1353
+ }
1354
+ function close(customerId) {
1355
+ getMountedAgent(customerId ?? manifest.customerId)?.close();
1356
+ }
1357
+ function get(customerId) {
1358
+ return getMountedAgent(customerId ?? manifest.customerId);
1359
+ }
1360
+ const api = {
1361
+ mount,
1362
+ unmount,
1363
+ open,
1364
+ close,
1365
+ get
1366
+ };
1367
+ window.WeblessAgent = api;
1368
+ void (async () => {
1369
+ try {
1370
+ mount();
1371
+ } catch (error) {
1372
+ console.error("[Webless] Agent mount failed", error);
1373
+ }
1374
+ })();
1375
+ return api;
1376
+ }
1206
1377
  // Annotate the CommonJS export names for ESM import in node:
1207
1378
  0 && (module.exports = {
1208
1379
  DEFAULT_AGENT_PLACEMENT,
1209
1380
  bootstrapAgent,
1381
+ createWeblessAgentPublicApi,
1210
1382
  getMountedAgent,
1211
1383
  mountAgent,
1212
1384
  normalizeAgentPlacement,