@webless/agent 0.11.0 → 0.12.0

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.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Root } from 'react-dom/client';
2
- import { u as AgentTagManifest, N as NormalizedAgentTagManifest } from './panel-controller-rxR75tRa.cjs';
3
- export { e as AgentIndexVersion, j as AgentMountStrategy, k as AgentPanelSubmitOptions, l as AgentPlacementConfig, s as AgentTabSide, t as AgentTabVariant, J as DEFAULT_AGENT_PLACEMENT, a1 as normalizeAgentPlacement, a2 as normalizeAgentTagManifest, a4 as resetAgentPanel, a5 as submitAgentPanel } from './panel-controller-rxR75tRa.cjs';
2
+ import { w as AgentTagManifest, O as NormalizedAgentTagManifest } from './panel-controller-CYoivbYB.cjs';
3
+ export { f as AgentIndexVersion, k as AgentMountStrategy, l as AgentPanelSubmitOptions, m as AgentPlacementConfig, u as AgentTabSide, v as AgentTabVariant, L as DEFAULT_AGENT_PLACEMENT, a3 as normalizeAgentPlacement, a4 as normalizeAgentTagManifest, a6 as resetAgentPanel, a7 as submitAgentPanel } from './panel-controller-CYoivbYB.cjs';
4
+ import 'zod';
4
5
  import 'react';
5
6
 
6
7
  type AgentRuntimeHandle = {
package/dist/embed.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Root } from 'react-dom/client';
2
- import { u as AgentTagManifest, N as NormalizedAgentTagManifest } from './panel-controller-rxR75tRa.js';
3
- export { e as AgentIndexVersion, j as AgentMountStrategy, k as AgentPanelSubmitOptions, l as AgentPlacementConfig, s as AgentTabSide, t as AgentTabVariant, J as DEFAULT_AGENT_PLACEMENT, a1 as normalizeAgentPlacement, a2 as normalizeAgentTagManifest, a4 as resetAgentPanel, a5 as submitAgentPanel } from './panel-controller-rxR75tRa.js';
2
+ import { w as AgentTagManifest, O as NormalizedAgentTagManifest } from './panel-controller-CYoivbYB.js';
3
+ export { f as AgentIndexVersion, k as AgentMountStrategy, l as AgentPanelSubmitOptions, m as AgentPlacementConfig, u as AgentTabSide, v as AgentTabVariant, L as DEFAULT_AGENT_PLACEMENT, a3 as normalizeAgentPlacement, a4 as normalizeAgentTagManifest, a6 as resetAgentPanel, a7 as submitAgentPanel } from './panel-controller-CYoivbYB.js';
4
+ import 'zod';
4
5
  import 'react';
5
6
 
6
7
  type AgentRuntimeHandle = {
package/dist/embed.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resetAgentPanel,
10
10
  resolveAgentRuntimeConfig,
11
11
  submitAgentPanel
12
- } from "./chunk-NW37MHU7.js";
12
+ } from "./chunk-EVWZF34F.js";
13
13
 
14
14
  // src/embed/mount.tsx
15
15
  import { createRoot } from "react-dom/client";
package/dist/index.cjs CHANGED
@@ -182,16 +182,16 @@ function parseStep(value) {
182
182
  if (!hasOnlyKeys(value, ["description", "fieldPaths", "id", "label"])) {
183
183
  return null;
184
184
  }
185
- const id = boundedString(value.id, 80);
185
+ const id2 = boundedString(value.id, 80);
186
186
  const label = boundedString(value.label, 160);
187
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(
188
+ if (!id2 || !/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(id2) || !label || value.description !== void 0 && !description || !Array.isArray(value.fieldPaths) || value.fieldPaths.length < 1 || value.fieldPaths.length > 32 || value.fieldPaths.some(
189
189
  (path) => typeof path !== "string" || !/^[A-Za-z0-9_.[\]-]+$/u.test(path) || path.length > 160
190
190
  )) {
191
191
  return null;
192
192
  }
193
193
  return {
194
- id,
194
+ id: id2,
195
195
  label,
196
196
  fieldPaths: value.fieldPaths,
197
197
  ...description ? { description } : {}
@@ -228,14 +228,14 @@ function parseAgentToolUiSurface(value) {
228
228
  ])) {
229
229
  return null;
230
230
  }
231
- const id = boundedString(value.id, 200);
231
+ const id2 = boundedString(value.id, 200);
232
232
  const title = boundedString(value.title, 200);
233
233
  const toolSlug = boundedString(value.toolSlug, 200);
234
234
  const description = value.description === void 0 ? void 0 : boundedString(value.description, 800);
235
235
  const operationId = value.operationId === void 0 ? void 0 : boundedString(value.operationId, 200);
236
236
  const requestId = value.requestId === void 0 ? void 0 : boundedString(value.requestId, 200);
237
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) {
238
+ if (!id2 || !title || !toolSlug || !Array.isArray(value.fields) || value.fields.length < 1 || value.fields.length > 32) {
239
239
  return null;
240
240
  }
241
241
  const fields = value.fields.map(parseField);
@@ -255,7 +255,7 @@ function parseAgentToolUiSurface(value) {
255
255
  }
256
256
  return {
257
257
  schemaVersion: AGENT_TOOL_UI_SCHEMA_VERSION,
258
- id,
258
+ id: id2,
259
259
  title,
260
260
  toolSlug,
261
261
  fields,
@@ -366,8 +366,203 @@ function completeConnectedToolWork(item, result) {
366
366
  }
367
367
 
368
368
  // src/runtime/client.ts
369
+ var import_client3 = require("eve/client");
370
+
371
+ // src/runtime/handoff.ts
369
372
  var import_client2 = require("eve/client");
370
373
 
374
+ // src/runtime/generated/handoff-contract.ts
375
+ var import_zod = require("zod");
376
+ var id = import_zod.z.string().min(1).max(200);
377
+ var sequence = import_zod.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
378
+ var text = import_zod.z.string().trim().min(1).max(16e3);
379
+ var agentRuntimeHandoffVersion = "webless.ai/agent-runtime-handoff/v1";
380
+ var agentRuntimeHandoffStatusSchema = import_zod.z.enum([
381
+ "ai",
382
+ "requesting",
383
+ "queued",
384
+ "human",
385
+ "resolved",
386
+ "failed"
387
+ ]);
388
+ var agentRuntimeHandoffActorSchema = import_zod.z.strictObject({
389
+ id,
390
+ name: import_zod.z.string().trim().min(1).max(200)
391
+ });
392
+ var eventBase = {
393
+ id,
394
+ sequence: sequence.refine((value) => value > 0),
395
+ handoffId: id,
396
+ epoch: sequence.refine((value) => value > 0),
397
+ createdAt: import_zod.z.iso.datetime()
398
+ };
399
+ var agentRuntimeHandoffEventSchema = import_zod.z.discriminatedUnion("type", [
400
+ import_zod.z.strictObject({
401
+ ...eventBase,
402
+ type: import_zod.z.literal("status"),
403
+ status: agentRuntimeHandoffStatusSchema,
404
+ actor: agentRuntimeHandoffActorSchema.nullable()
405
+ }),
406
+ import_zod.z.strictObject({
407
+ ...eventBase,
408
+ type: import_zod.z.literal("visitor.message"),
409
+ message: text,
410
+ operationId: id
411
+ }),
412
+ import_zod.z.strictObject({
413
+ ...eventBase,
414
+ type: import_zod.z.literal("human.message"),
415
+ message: text,
416
+ actor: agentRuntimeHandoffActorSchema
417
+ })
418
+ ]);
419
+ var agentRuntimeHandoffReadRequestSchema = import_zod.z.strictObject({
420
+ after: sequence.default(0)
421
+ });
422
+ var agentRuntimeHandoffPageSchema = import_zod.z.strictObject({
423
+ apiVersion: import_zod.z.literal(agentRuntimeHandoffVersion),
424
+ sessionId: id,
425
+ enabled: import_zod.z.boolean(),
426
+ status: agentRuntimeHandoffStatusSchema,
427
+ handoffId: id.nullable(),
428
+ epoch: sequence,
429
+ after: sequence,
430
+ cursor: sequence,
431
+ hasMore: import_zod.z.boolean(),
432
+ events: import_zod.z.array(agentRuntimeHandoffEventSchema).max(100)
433
+ }).superRefine((page, ctx) => {
434
+ let cursor = page.after;
435
+ let previousEpoch = 0;
436
+ const statuses = /* @__PURE__ */ new Map();
437
+ const ids = /* @__PURE__ */ new Set();
438
+ const handoffs = /* @__PURE__ */ new Map();
439
+ const terminalEpochs = /* @__PURE__ */ new Set();
440
+ let currentStatus;
441
+ for (const event of page.events) {
442
+ if (event.epoch < previousEpoch)
443
+ ctx.addIssue({
444
+ code: "custom",
445
+ message: "Handoff epochs cannot decrease."
446
+ });
447
+ previousEpoch = event.epoch;
448
+ if (ids.has(event.id))
449
+ ctx.addIssue({
450
+ code: "custom",
451
+ message: "Duplicate handoff event ID."
452
+ });
453
+ ids.add(event.id);
454
+ const handoffId = handoffs.get(event.epoch);
455
+ if (handoffId !== void 0 && handoffId !== event.handoffId || event.epoch === page.epoch && event.handoffId !== page.handoffId)
456
+ ctx.addIssue({
457
+ code: "custom",
458
+ message: "Inconsistent handoff epoch identity."
459
+ });
460
+ handoffs.set(event.epoch, event.handoffId);
461
+ if (terminalEpochs.has(event.epoch) && (event.type !== "status" || event.status !== "ai"))
462
+ ctx.addIssue({
463
+ code: "custom",
464
+ message: "Handoff event follows resolution."
465
+ });
466
+ if (event.type === "status") {
467
+ if (event.status === "human" && event.actor === null)
468
+ ctx.addIssue({
469
+ code: "custom",
470
+ message: "Human ownership requires a representative."
471
+ });
472
+ const previous = statuses.get(event.epoch);
473
+ const transitions = {
474
+ ai: [],
475
+ requesting: ["queued", "resolved", "failed"],
476
+ queued: ["human", "resolved", "failed"],
477
+ human: ["resolved", "failed"],
478
+ resolved: ["ai"],
479
+ failed: []
480
+ };
481
+ if (previous !== void 0 && !transitions[previous].includes(event.status))
482
+ ctx.addIssue({
483
+ code: "custom",
484
+ message: "Invalid handoff ownership transition."
485
+ });
486
+ statuses.set(event.epoch, event.status);
487
+ if (["resolved", "failed", "ai"].includes(event.status))
488
+ terminalEpochs.add(event.epoch);
489
+ if (event.epoch === page.epoch) currentStatus = event.status;
490
+ }
491
+ if (event.sequence !== cursor + 1) {
492
+ ctx.addIssue({
493
+ code: "custom",
494
+ message: "Handoff event gap or replay."
495
+ });
496
+ }
497
+ cursor = event.sequence;
498
+ if (event.epoch > page.epoch) {
499
+ ctx.addIssue({
500
+ code: "custom",
501
+ message: "Event exceeds current epoch."
502
+ });
503
+ }
504
+ }
505
+ if (!page.hasMore && currentStatus !== void 0 && currentStatus !== page.status)
506
+ ctx.addIssue({
507
+ code: "custom",
508
+ message: "Handoff ownership contradicts its final status event."
509
+ });
510
+ if (page.cursor !== cursor || page.hasMore && page.events.length === 0) {
511
+ ctx.addIssue({ code: "custom", message: "Invalid handoff page cursor." });
512
+ }
513
+ if (page.epoch === 0 !== (page.handoffId === null)) {
514
+ ctx.addIssue({ code: "custom", message: "Invalid handoff identity." });
515
+ }
516
+ if (page.handoffId === null && page.status !== "ai") {
517
+ ctx.addIssue({
518
+ code: "custom",
519
+ message: "Handoff identity is required."
520
+ });
521
+ }
522
+ });
523
+ var agentRuntimeHandoffStartSchema = import_zod.z.strictObject({
524
+ operationId: id
525
+ });
526
+ var agentRuntimeHandoffCommandSchema = import_zod.z.strictObject({
527
+ operationId: id,
528
+ handoffId: id,
529
+ epoch: sequence.refine((value) => value > 0)
530
+ });
531
+ var agentRuntimeHandoffMessageSchema = agentRuntimeHandoffCommandSchema.extend({ message: text });
532
+ var agentRuntimeHandoffBindingSchema = import_zod.z.strictObject({
533
+ tenantId: id,
534
+ indexId: id,
535
+ visitorSubject: id,
536
+ sessionId: id,
537
+ handoffId: id,
538
+ epoch: sequence.refine((value) => value > 0)
539
+ });
540
+ var providerEventBase = {
541
+ apiVersion: import_zod.z.literal(agentRuntimeHandoffVersion),
542
+ binding: agentRuntimeHandoffBindingSchema,
543
+ eventId: id,
544
+ sequence: sequence.refine((value) => value > 0)
545
+ };
546
+ var agentRuntimeHandoffProviderEventSchema = import_zod.z.discriminatedUnion(
547
+ "type",
548
+ [
549
+ import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("queued") }),
550
+ import_zod.z.strictObject({
551
+ ...providerEventBase,
552
+ type: import_zod.z.literal("assigned"),
553
+ actor: agentRuntimeHandoffActorSchema
554
+ }),
555
+ import_zod.z.strictObject({
556
+ ...providerEventBase,
557
+ type: import_zod.z.literal("human.message"),
558
+ actor: agentRuntimeHandoffActorSchema,
559
+ message: text
560
+ }),
561
+ import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("resolved") }),
562
+ import_zod.z.strictObject({ ...providerEventBase, type: import_zod.z.literal("failed") })
563
+ ]
564
+ );
565
+
371
566
  // src/runtime/capability.ts
372
567
  var import_client = require("eve/client");
373
568
  var MAX_REFRESH_SKEW_MS = 3e4;
@@ -522,6 +717,75 @@ async function withCapabilityRefresh(capability, request) {
522
717
  }
523
718
  }
524
719
 
720
+ // src/runtime/handoff.ts
721
+ function createHandoffClient(options) {
722
+ const target = () => {
723
+ const sessionId = options.getSessionId();
724
+ if (!sessionId)
725
+ throw new Error(
726
+ "Start a conversation before requesting a representative."
727
+ );
728
+ return {
729
+ sessionId,
730
+ path: `/webless/v1/session/${encodeURIComponent(sessionId)}/handoff`
731
+ };
732
+ };
733
+ const request = (path, init) => withCapabilityRefresh(options.capability, async () => {
734
+ const response = await options.getClient().fetch(path, {
735
+ ...init,
736
+ cache: "no-store",
737
+ redirect: "error"
738
+ });
739
+ if (!response.ok) {
740
+ throw new import_client2.ClientError(
741
+ response.status,
742
+ await response.text(),
743
+ response.headers
744
+ );
745
+ }
746
+ return response;
747
+ });
748
+ const command = async (suffix, body, signal) => {
749
+ const { path } = target();
750
+ await request(path + suffix, {
751
+ method: "POST",
752
+ headers: { "content-type": "application/json" },
753
+ body: JSON.stringify(body),
754
+ signal
755
+ });
756
+ };
757
+ return {
758
+ async read(input = {}) {
759
+ const { after } = agentRuntimeHandoffReadRequestSchema.parse({
760
+ after: input.after
761
+ });
762
+ const { sessionId, path } = target();
763
+ const response = await request(`${path}?after=${after}`, {
764
+ signal: input.signal
765
+ });
766
+ const value = await response.json();
767
+ const page = agentRuntimeHandoffPageSchema.parse(value);
768
+ if (page.sessionId !== sessionId || page.after !== after) {
769
+ throw new Error(
770
+ "The handoff response does not belong to this conversation or cursor."
771
+ );
772
+ }
773
+ return page;
774
+ },
775
+ start: (operationId, signal) => command(
776
+ "",
777
+ agentRuntimeHandoffStartSchema.parse({ operationId }),
778
+ signal
779
+ ),
780
+ send: (input, signal) => command(
781
+ "/messages",
782
+ agentRuntimeHandoffMessageSchema.parse(input),
783
+ signal
784
+ ),
785
+ returnToAI: (input, signal) => command("/return", agentRuntimeHandoffCommandSchema.parse(input), signal)
786
+ };
787
+ }
788
+
525
789
  // src/runtime/config.ts
526
790
  var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
527
791
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
@@ -1121,6 +1385,11 @@ var AgentSession = class {
1121
1385
  version,
1122
1386
  visitorSessionId
1123
1387
  });
1388
+ this.handoff = createHandoffClient({
1389
+ getClient: () => this.ensureClient(),
1390
+ getSessionId: () => this.getActiveSessionId(),
1391
+ capability: this.capability
1392
+ });
1124
1393
  }
1125
1394
  indexId;
1126
1395
  version;
@@ -1133,6 +1402,7 @@ var AgentSession = class {
1133
1402
  activeResponse;
1134
1403
  childStreams;
1135
1404
  capability;
1405
+ handoff;
1136
1406
  getActiveSessionId() {
1137
1407
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
1138
1408
  }
@@ -1207,7 +1477,7 @@ var AgentSession = class {
1207
1477
  }
1208
1478
  this.activeResponse = void 0;
1209
1479
  this.session = void 0;
1210
- this.client = new import_client2.Client({
1480
+ this.client = new import_client3.Client({
1211
1481
  auth: { bearer: () => this.capability.getAccessToken() },
1212
1482
  host: config.host,
1213
1483
  redirect: "error"
@@ -1243,7 +1513,7 @@ var AgentSession = class {
1243
1513
  () => activeSession.send(message, { signal })
1244
1514
  );
1245
1515
  } catch (error) {
1246
- if (error instanceof import_client2.ClientError && error.status === 409 && error.code === "session_not_active") {
1516
+ if (error instanceof import_client3.ClientError && error.status === 409 && error.code === "session_not_active") {
1247
1517
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
1248
1518
  this.session = void 0;
1249
1519
  session = void 0;
@@ -1417,10 +1687,10 @@ var AgentSession = class {
1417
1687
  }
1418
1688
  this.session = session;
1419
1689
  const inputResponses = responses.map(
1420
- ({ requestId, optionId, text }) => ({
1690
+ ({ requestId, optionId, text: text2 }) => ({
1421
1691
  requestId,
1422
1692
  ...optionId ? { optionId } : {},
1423
- ...text ? { text } : {}
1693
+ ...text2 ? { text: text2 } : {}
1424
1694
  })
1425
1695
  );
1426
1696
  const response = await withCapabilityRefresh(
@@ -1510,6 +1780,7 @@ function createAgentClient(options) {
1510
1780
  );
1511
1781
  return {
1512
1782
  indexId,
1783
+ handoff: session.handoff,
1513
1784
  version,
1514
1785
  runtimeOrigin,
1515
1786
  visitorSessionId,
@@ -1541,7 +1812,7 @@ function createAgentClient(options) {
1541
1812
  }
1542
1813
 
1543
1814
  // src/runtime/errors.ts
1544
- var import_client3 = require("eve/client");
1815
+ var import_client4 = require("eve/client");
1545
1816
  var TRANSIENT_AGENT_ERROR_MESSAGE = "The agent run stopped before the action finished. Please try again.";
1546
1817
  var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
1547
1818
  function isTransientRuntimeMessage(message) {
@@ -1553,7 +1824,7 @@ function isPreviewAuthorizationMessage(message) {
1553
1824
  return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
1554
1825
  }
1555
1826
  function formatAgentError(error) {
1556
- if (error instanceof import_client3.ClientError) {
1827
+ if (error instanceof import_client4.ClientError) {
1557
1828
  if (error.status === 401 && error.code === "index_required") {
1558
1829
  return "Missing indexId \u2014 pass a published index id to createAgentClient().";
1559
1830
  }