@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/react.cjs CHANGED
@@ -40,22 +40,110 @@ var import_react5 = require("react");
40
40
  var import_react = require("react");
41
41
 
42
42
  // src/runtime/client.ts
43
+ var import_client2 = require("eve/client");
44
+
45
+ // src/runtime/capability.ts
43
46
  var import_client = require("eve/client");
47
+ var MAX_REFRESH_SKEW_MS = 3e4;
48
+ function isRecord(value) {
49
+ return typeof value === "object" && value !== null && !Array.isArray(value);
50
+ }
51
+ function parseBootstrapResponse(value, indexId, now) {
52
+ 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) {
53
+ throw new Error("Agent Runtime returned an invalid access response.");
54
+ }
55
+ const expiresAt = Date.parse(value.expiresAt);
56
+ if (!Number.isFinite(expiresAt) || expiresAt <= now) {
57
+ throw new Error("Agent Runtime returned an expired access response.");
58
+ }
59
+ const refreshSkew = Math.min(MAX_REFRESH_SKEW_MS, Math.floor((expiresAt - now) / 10));
60
+ return {
61
+ accessToken: value.accessToken,
62
+ refreshAt: expiresAt - refreshSkew
63
+ };
64
+ }
65
+ async function readBootstrapError(response) {
66
+ const fallback = `Agent Runtime is unavailable (${response.status}).`;
67
+ try {
68
+ const value = await response.json();
69
+ return isRecord(value) && typeof value.error === "string" && value.error ? value.error : fallback;
70
+ } catch {
71
+ return fallback;
72
+ }
73
+ }
74
+ function createAgentRuntimeCapability(options) {
75
+ const fetchImplementation = options.fetchImplementation ?? fetch;
76
+ const now = options.now ?? Date.now;
77
+ let capability;
78
+ let pendingBootstrap;
79
+ const bootstrap = async () => {
80
+ const response = await fetchImplementation(`${options.runtimeOrigin}/webless/v1/bootstrap`, {
81
+ body: JSON.stringify({
82
+ clientSessionId: options.visitorSessionId,
83
+ indexId: options.indexId
84
+ }),
85
+ headers: { "content-type": "application/json" },
86
+ method: "POST"
87
+ });
88
+ if (!response.ok) {
89
+ throw new Error(await readBootstrapError(response));
90
+ }
91
+ let value;
92
+ try {
93
+ value = await response.json();
94
+ } catch {
95
+ throw new Error("Agent Runtime returned an invalid access response.");
96
+ }
97
+ return parseBootstrapResponse(value, options.indexId, now());
98
+ };
99
+ return {
100
+ getAccessToken: async () => {
101
+ if (capability && capability.refreshAt > now()) {
102
+ return capability.accessToken;
103
+ }
104
+ pendingBootstrap ??= bootstrap().finally(() => {
105
+ pendingBootstrap = void 0;
106
+ });
107
+ capability = await pendingBootstrap;
108
+ return capability.accessToken;
109
+ },
110
+ invalidate: () => {
111
+ capability = void 0;
112
+ }
113
+ };
114
+ }
115
+ async function withCapabilityRefresh(capability, request) {
116
+ try {
117
+ return await request();
118
+ } catch (error) {
119
+ if (!(error instanceof import_client.ClientError) || error.status !== 401) {
120
+ throw error;
121
+ }
122
+ capability.invalidate();
123
+ return await request();
124
+ }
125
+ }
44
126
 
45
127
  // src/runtime/config.ts
46
128
  var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
47
129
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
48
- function buildEveHost(origin, indexId) {
130
+ function buildEveHost(origin, indexId, version = "published") {
49
131
  const base = trimTrailingSlash(origin);
50
- return `${base}?indexId=${encodeURIComponent(indexId.trim())}`;
132
+ const searchParams = new URLSearchParams({ indexId: indexId.trim() });
133
+ if (version === "unpublished") {
134
+ searchParams.set("version", version);
135
+ }
136
+ return `${base}?${searchParams.toString()}`;
51
137
  }
52
138
  function resolveAgentRuntimeConfig(input) {
53
139
  const origin = trimTrailingSlash(input.runtimeOrigin?.trim() || DEFAULT_RUNTIME_ORIGIN);
54
140
  const resolvedIndexId = input.indexId.trim();
141
+ const version = input.version ?? "published";
55
142
  return {
56
143
  origin,
57
144
  indexId: resolvedIndexId,
58
- host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId) : origin
145
+ version,
146
+ host: resolvedIndexId ? buildEveHost(origin, resolvedIndexId, version) : origin
59
147
  };
60
148
  }
61
149
 
@@ -72,7 +160,8 @@ function buildAgentStorageKeyPrefix(input) {
72
160
  if (!indexId) {
73
161
  throw new Error("indexId is required to build agent storage keys.");
74
162
  }
75
- return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
163
+ const versionSuffix = input.version === "unpublished" ? ":unpublished" : "";
164
+ return `${DEFAULT_STORAGE_KEY_PREFIX}:${customerId}:${indexId}${versionSuffix}:${normalizeRuntimeOrigin(input.runtimeOrigin)}`;
76
165
  }
77
166
  function createSessionId() {
78
167
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
@@ -141,13 +230,20 @@ function mapStepLabel(event) {
141
230
  };
142
231
  }
143
232
  var AgentSession = class {
144
- constructor(indexId, runtimeOrigin, visitorSessionId, storeOptions) {
233
+ constructor(indexId, version, runtimeOrigin, visitorSessionId, storeOptions) {
145
234
  this.indexId = indexId;
235
+ this.version = version;
146
236
  this.runtimeOrigin = runtimeOrigin;
147
237
  this.visitorSessionId = visitorSessionId;
148
238
  this.storeOptions = storeOptions;
239
+ this.capability = createAgentRuntimeCapability({
240
+ indexId,
241
+ runtimeOrigin,
242
+ visitorSessionId
243
+ });
149
244
  }
150
245
  indexId;
246
+ version;
151
247
  runtimeOrigin;
152
248
  visitorSessionId;
153
249
  storeOptions;
@@ -156,6 +252,7 @@ var AgentSession = class {
156
252
  session;
157
253
  activeResponse;
158
254
  renderedPrefix = "";
255
+ capability;
159
256
  getActiveSessionId() {
160
257
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
161
258
  }
@@ -178,6 +275,7 @@ var AgentSession = class {
178
275
  ensureClient() {
179
276
  const config = resolveAgentRuntimeConfig({
180
277
  indexId: this.indexId,
278
+ version: this.version,
181
279
  runtimeOrigin: this.runtimeOrigin
182
280
  });
183
281
  if (!config.indexId) {
@@ -187,7 +285,11 @@ var AgentSession = class {
187
285
  return this.client;
188
286
  }
189
287
  this.reset();
190
- this.client = new import_client.Client({ host: config.host });
288
+ this.client = new import_client2.Client({
289
+ auth: { bearer: () => this.capability.getAccessToken() },
290
+ host: config.host,
291
+ redirect: "error"
292
+ });
191
293
  this.clientHost = config.host;
192
294
  return this.client;
193
295
  }
@@ -206,9 +308,13 @@ var AgentSession = class {
206
308
  if (session) {
207
309
  this.session = session;
208
310
  try {
209
- response = await session.send(message, { signal });
311
+ const activeSession = session;
312
+ response = await withCapabilityRefresh(
313
+ this.capability,
314
+ () => activeSession.send(message, { signal })
315
+ );
210
316
  } catch (error) {
211
- if (error instanceof import_client.ClientError && error.status === 409 && error.code === "session_not_active") {
317
+ if (error instanceof import_client2.ClientError && error.status === 409 && error.code === "session_not_active") {
212
318
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
213
319
  this.session = void 0;
214
320
  session = void 0;
@@ -218,7 +324,10 @@ var AgentSession = class {
218
324
  }
219
325
  }
220
326
  if (!response) {
221
- const created = await client.sessions.create({ message, signal });
327
+ const created = await withCapabilityRefresh(
328
+ this.capability,
329
+ () => client.sessions.create({ message, signal })
330
+ );
222
331
  response = created.response;
223
332
  session = created.session;
224
333
  this.session = session;
@@ -276,18 +385,25 @@ function createAgentClient(options) {
276
385
  if (!indexId) {
277
386
  throw new Error("indexId is required.");
278
387
  }
279
- const runtimeOrigin = options.runtimeOrigin?.trim() || resolveAgentRuntimeConfig({ indexId }).origin;
388
+ const version = options.version ?? "published";
389
+ const runtimeOrigin = resolveAgentRuntimeConfig({
390
+ indexId,
391
+ runtimeOrigin: options.runtimeOrigin,
392
+ version
393
+ }).origin;
280
394
  const storeOptions = {
281
395
  storageKeyPrefix: options.storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({
282
396
  customerId: options.customerId,
283
397
  indexId,
398
+ version,
284
399
  runtimeOrigin
285
400
  })
286
401
  };
287
402
  const visitorSessionId = options.visitorSessionId?.trim() || getOrCreateVisitorSessionId(storeOptions);
288
- const session = new AgentSession(indexId, runtimeOrigin, visitorSessionId, storeOptions);
403
+ const session = new AgentSession(indexId, version, runtimeOrigin, visitorSessionId, storeOptions);
289
404
  return {
290
405
  indexId,
406
+ version,
291
407
  runtimeOrigin,
292
408
  visitorSessionId,
293
409
  sendTurn: (message, sendOptions) => session.sendTurn(
@@ -302,9 +418,9 @@ function createAgentClient(options) {
302
418
  }
303
419
 
304
420
  // src/runtime/errors.ts
305
- var import_client2 = require("eve/client");
421
+ var import_client3 = require("eve/client");
306
422
  function formatAgentError(error) {
307
- if (error instanceof import_client2.ClientError) {
423
+ if (error instanceof import_client3.ClientError) {
308
424
  if (error.status === 401 && error.code === "index_required") {
309
425
  return "Missing indexId \u2014 pass a published index id to createAgentClient().";
310
426
  }
@@ -370,6 +486,7 @@ async function runStatusSequence(signal, onStep) {
370
486
  function useAgentChat({
371
487
  customerId,
372
488
  indexId,
489
+ version,
373
490
  runtimeOrigin,
374
491
  visitorSessionId,
375
492
  storageKeyPrefix
@@ -377,8 +494,8 @@ function useAgentChat({
377
494
  const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
378
495
  const runRef = (0, import_react.useRef)(null);
379
496
  const resolvedStorageKeyPrefix = (0, import_react.useMemo)(
380
- () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, runtimeOrigin }),
381
- [customerId, indexId, runtimeOrigin, storageKeyPrefix]
497
+ () => storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({ customerId, indexId, version, runtimeOrigin }),
498
+ [customerId, indexId, runtimeOrigin, storageKeyPrefix, version]
382
499
  );
383
500
  const visitorId = (0, import_react.useMemo)(
384
501
  () => visitorSessionId?.trim() || getOrCreateVisitorSessionId({ storageKeyPrefix: resolvedStorageKeyPrefix }),
@@ -388,13 +505,14 @@ function useAgentChat({
388
505
  createAgentClient({
389
506
  customerId,
390
507
  indexId,
508
+ version,
391
509
  runtimeOrigin,
392
510
  visitorSessionId: visitorId,
393
511
  storageKeyPrefix: resolvedStorageKeyPrefix
394
512
  })
395
513
  );
396
514
  const identityRef = (0, import_react.useRef)(null);
397
- const identityKey = `${customerId ?? ""}|${indexId}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
515
+ const identityKey = `${customerId ?? ""}|${indexId}|${version ?? "published"}|${runtimeOrigin ?? ""}|${resolvedStorageKeyPrefix}|${visitorId}`;
398
516
  (0, import_react.useEffect)(() => {
399
517
  if (identityRef.current === null) {
400
518
  identityRef.current = identityKey;
@@ -410,12 +528,13 @@ function useAgentChat({
410
528
  clientRef.current = createAgentClient({
411
529
  customerId,
412
530
  indexId,
531
+ version,
413
532
  runtimeOrigin,
414
533
  visitorSessionId: visitorId,
415
534
  storageKeyPrefix: resolvedStorageKeyPrefix
416
535
  });
417
536
  setState(INITIAL_STATE);
418
- }, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]);
537
+ }, [customerId, identityKey, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]);
419
538
  const reset = (0, import_react.useCallback)(() => {
420
539
  runRef.current?.abort();
421
540
  runRef.current = null;
@@ -518,7 +637,7 @@ function useAgentChat({
518
637
  }));
519
638
  }
520
639
  },
521
- [customerId, indexId, runtimeOrigin, resolvedStorageKeyPrefix, visitorId]
640
+ [customerId, indexId, runtimeOrigin, resolvedStorageKeyPrefix, version, visitorId]
522
641
  );
523
642
  (0, import_react.useEffect)(() => {
524
643
  return () => {
@@ -1001,6 +1120,7 @@ var import_jsx_runtime8 = require("react/jsx-runtime");
1001
1120
  function AgentWidget({
1002
1121
  indexId,
1003
1122
  customerId,
1123
+ version,
1004
1124
  runtimeOrigin,
1005
1125
  placement: placementInput,
1006
1126
  defaultCollapsed = true,
@@ -1013,6 +1133,7 @@ function AgentWidget({
1013
1133
  const { state, submit } = useAgentChat({
1014
1134
  customerId,
1015
1135
  indexId,
1136
+ version,
1016
1137
  runtimeOrigin
1017
1138
  });
1018
1139
  const idle = state.phase === "idle" && !hasVisitorMessages(state.messages);