@webless/agent 0.2.7 → 0.2.9

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
@@ -32,7 +32,7 @@ __export(embed_exports, {
32
32
  module.exports = __toCommonJS(embed_exports);
33
33
 
34
34
  // src/embed/mount.tsx
35
- var import_client4 = require("react-dom/client");
35
+ var import_client5 = require("react-dom/client");
36
36
 
37
37
  // src/react/panel-controller.ts
38
38
  var controllers = /* @__PURE__ */ new Map();
@@ -56,7 +56,89 @@ var import_react5 = require("react");
56
56
  var import_react = require("react");
57
57
 
58
58
  // src/runtime/client.ts
59
+ var import_client2 = require("eve/client");
60
+
61
+ // src/runtime/capability.ts
59
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
+ }
60
142
 
61
143
  // src/runtime/config.ts
62
144
  var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
@@ -170,6 +252,11 @@ var AgentSession = class {
170
252
  this.runtimeOrigin = runtimeOrigin;
171
253
  this.visitorSessionId = visitorSessionId;
172
254
  this.storeOptions = storeOptions;
255
+ this.capability = createAgentRuntimeCapability({
256
+ indexId,
257
+ runtimeOrigin,
258
+ visitorSessionId
259
+ });
173
260
  }
174
261
  indexId;
175
262
  version;
@@ -180,7 +267,7 @@ var AgentSession = class {
180
267
  clientHost;
181
268
  session;
182
269
  activeResponse;
183
- renderedPrefix = "";
270
+ capability;
184
271
  getActiveSessionId() {
185
272
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
186
273
  }
@@ -189,7 +276,6 @@ var AgentSession = class {
189
276
  });
190
277
  this.activeResponse = void 0;
191
278
  this.session = void 0;
192
- this.renderedPrefix = "";
193
279
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
194
280
  }
195
281
  persistSessionCursor(session) {
@@ -213,14 +299,17 @@ var AgentSession = class {
213
299
  return this.client;
214
300
  }
215
301
  this.reset();
216
- this.client = new import_client.Client({ host: config.host });
302
+ this.client = new import_client2.Client({
303
+ auth: { bearer: () => this.capability.getAccessToken() },
304
+ host: config.host,
305
+ redirect: "error"
306
+ });
217
307
  this.clientHost = config.host;
218
308
  return this.client;
219
309
  }
220
310
  attachPersistedSession(client) {
221
311
  const persisted = loadPersistedAgentSession(this.visitorSessionId, this.storeOptions);
222
312
  if (!persisted?.sessionId) return void 0;
223
- this.renderedPrefix = "";
224
313
  return client.sessions.attach(persisted.sessionId, {
225
314
  streamIndex: persisted.streamIndex
226
315
  });
@@ -232,9 +321,13 @@ var AgentSession = class {
232
321
  if (session) {
233
322
  this.session = session;
234
323
  try {
235
- response = await session.send(message, { signal });
324
+ const activeSession = session;
325
+ response = await withCapabilityRefresh(
326
+ this.capability,
327
+ () => activeSession.send(message, { signal })
328
+ );
236
329
  } catch (error) {
237
- if (error instanceof import_client.ClientError && error.status === 409 && error.code === "session_not_active") {
330
+ if (error instanceof import_client2.ClientError && error.status === 409 && error.code === "session_not_active") {
238
331
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
239
332
  this.session = void 0;
240
333
  session = void 0;
@@ -244,15 +337,17 @@ var AgentSession = class {
244
337
  }
245
338
  }
246
339
  if (!response) {
247
- const created = await client.sessions.create({ message, signal });
340
+ const created = await withCapabilityRefresh(
341
+ this.capability,
342
+ () => client.sessions.create({ message, signal })
343
+ );
248
344
  response = created.response;
249
345
  session = created.session;
250
346
  this.session = session;
251
- this.renderedPrefix = "";
252
347
  this.persistSessionCursor(session);
253
348
  }
254
349
  this.activeResponse = response;
255
- let rendered = this.renderedPrefix;
350
+ let rendered = "";
256
351
  try {
257
352
  for await (const event of response) {
258
353
  if (signal.aborted) break;
@@ -278,7 +373,6 @@ var AgentSession = class {
278
373
  }
279
374
  } finally {
280
375
  this.activeResponse = void 0;
281
- this.renderedPrefix = rendered;
282
376
  if (session) {
283
377
  this.persistSessionCursor(session);
284
378
  }
@@ -302,8 +396,12 @@ function createAgentClient(options) {
302
396
  if (!indexId) {
303
397
  throw new Error("indexId is required.");
304
398
  }
305
- const runtimeOrigin = options.runtimeOrigin?.trim() || resolveAgentRuntimeConfig({ indexId }).origin;
306
399
  const version = options.version ?? "published";
400
+ const runtimeOrigin = resolveAgentRuntimeConfig({
401
+ indexId,
402
+ runtimeOrigin: options.runtimeOrigin,
403
+ version
404
+ }).origin;
307
405
  const storeOptions = {
308
406
  storageKeyPrefix: options.storageKeyPrefix?.trim() || buildAgentStorageKeyPrefix({
309
407
  customerId: options.customerId,
@@ -331,9 +429,9 @@ function createAgentClient(options) {
331
429
  }
332
430
 
333
431
  // src/runtime/errors.ts
334
- var import_client2 = require("eve/client");
432
+ var import_client3 = require("eve/client");
335
433
  function formatAgentError(error) {
336
- if (error instanceof import_client2.ClientError) {
434
+ if (error instanceof import_client3.ClientError) {
337
435
  if (error.status === 401 && error.code === "index_required") {
338
436
  return "Missing indexId \u2014 pass a published index id to createAgentClient().";
339
437
  }
@@ -372,11 +470,6 @@ var STATUS_SEQUENCE = [
372
470
  { id: "s1", label: "Starting Eve session", ms: 400 },
373
471
  { id: "s2", label: "Connecting to runtime", ms: 500 }
374
472
  ];
375
- var DEFAULT_FOLLOW_UPS = [
376
- { id: "fu-1", label: "What can you help me with?" },
377
- { id: "fu-2", label: "Summarize your capabilities" },
378
- { id: "fu-3", label: "What should I ask next?" }
379
- ];
380
473
  function delay(ms, signal) {
381
474
  return new Promise((resolve, reject) => {
382
475
  const timer = window.setTimeout(resolve, ms);
@@ -533,7 +626,7 @@ function useAgentChat({
533
626
  messages: [...prev.messages, agentMessage],
534
627
  toolSteps: [],
535
628
  streamingText: "",
536
- followUps: DEFAULT_FOLLOW_UPS,
629
+ followUps: [],
537
630
  journey: null
538
631
  }));
539
632
  } catch (error) {
@@ -837,12 +930,17 @@ function AgentRail({
837
930
  const transcriptRef = (0, import_react4.useRef)(null);
838
931
  const railStyle = theme?.railMaxWidth ? { ["--rail-width"]: theme.railMaxWidth, ["--as-rail-max-width"]: theme.railMaxWidth } : void 0;
839
932
  const isBusy = state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming";
840
- const showActivity = state.toolSteps.length > 0 && (state.phase === "thinking" || state.phase === "running-tools" || state.phase === "streaming");
841
- const showStreamingInActivity = state.phase === "streaming" && Boolean(state.streamingText);
933
+ const showActivity = state.toolSteps.length > 0 && (state.phase === "thinking" || state.phase === "running-tools");
842
934
  const hasVisitorMessages2 = state.messages.some((message) => message.role === "visitor");
843
935
  const showIdleFollowUps = !hasVisitorMessages2 && state.followUps.length > 0;
844
- const showCompleteFollowUps = state.phase === "complete" && state.followUps.length > 0 && hasVisitorMessages2;
845
- const showDockFollowUps = expanded && (showIdleFollowUps || showCompleteFollowUps);
936
+ const showDockFollowUps = expanded && showIdleFollowUps;
937
+ const streamingMessage = state.phase === "streaming" && state.streamingText ? {
938
+ createdAt: 0,
939
+ id: "streaming-response",
940
+ role: "agent",
941
+ streaming: true,
942
+ text: state.streamingText
943
+ } : null;
846
944
  (0, import_react4.useEffect)(() => {
847
945
  const node = transcriptRef.current;
848
946
  if (!node) return;
@@ -880,14 +978,8 @@ function AgentRail({
880
978
  ] }) }),
881
979
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: transcriptRef, className: "agent-rail__transcript", children: [
882
980
  state.messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message }, message.id)),
883
- showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
884
- AgentActivityBubble,
885
- {
886
- steps: state.toolSteps,
887
- streamingText: state.streamingText,
888
- showStreaming: showStreamingInActivity
889
- }
890
- ) : null,
981
+ streamingMessage ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageBubble, { message: streamingMessage }) : null,
982
+ showActivity ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AgentActivityBubble, { steps: state.toolSteps }) : null,
891
983
  !expanded && showIdleFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
892
984
  FollowUpChips,
893
985
  {
@@ -897,14 +989,6 @@ function AgentRail({
897
989
  onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
898
990
  }
899
991
  ) }) : null,
900
- !expanded && showCompleteFollowUps ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "agent-rail__followups-slot", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
901
- FollowUpChips,
902
- {
903
- suggestions: state.followUps,
904
- disabled: isBusy,
905
- onSelect: (suggestion) => onFollowUpSelect?.(suggestion.label)
906
- }
907
- ) }) : null,
908
992
  state.error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "alert", style: { fontSize: 13, color: "var(--as-danger)", margin: 0 }, children: state.error }) : null
909
993
  ] }),
910
994
  expanded ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "agent-rail__dock-wrap", children: [
@@ -1186,7 +1270,7 @@ function mountAgent(input) {
1186
1270
  const mountTarget = resolveMountHost(manifest, input.script ?? null);
1187
1271
  const host = createHost(manifest.customerId);
1188
1272
  mountTarget.append(host);
1189
- const root = (0, import_client4.createRoot)(host);
1273
+ const root = (0, import_client5.createRoot)(host);
1190
1274
  root.render(/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentWidget2, { manifest }));
1191
1275
  const handle = {
1192
1276
  customerId: manifest.customerId,