@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/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { d as AgentClientOptions, c as AgentClient, f as AgentIndexVersion, l as AgentRuntimeConfig, e as AgentHealthResult, P as PersistedAgentSession } from './types-B2j6hbI5.js';
2
- export { A as AGENT_STRUCTURED_TOOL_INPUT_HEADER, a as AGENT_STRUCTURED_TOOL_INPUT_SCHEMA_VERSION, b as AGENT_TOOL_UI_SCHEMA_VERSION, g as AgentInputOption, h as AgentInputRequest, i as AgentInputResponse, j as AgentRespondTurnOptions, k as AgentResumeTurnOptions, m as AgentSendTurnOptions, n as AgentStreamHandlers, o as AgentStructuredToolInput, p as AgentToolResult, q as AgentToolResultEnvelope, r as AgentToolResultJsonValue, s as AgentToolUiAction, t as AgentToolUiField, u as AgentToolUiFieldKind, v as AgentToolUiOption, w as AgentToolUiStep, x as AgentToolUiSurface, y as AgentWorkItem, z as formatAgentStructuredToolInput, B as parseAgentToolResultEnvelope, C as parseAgentToolUiSurface } from './types-B2j6hbI5.js';
1
+ import { d as AgentClientOptions, c as AgentClient, h as AgentIndexVersion, n as AgentRuntimeConfig, g as AgentHealthResult, P as PersistedAgentSession } from './types-4lTJW5mp.js';
2
+ export { A as AGENT_STRUCTURED_TOOL_INPUT_HEADER, a as AGENT_STRUCTURED_TOOL_INPUT_SCHEMA_VERSION, b as AGENT_TOOL_UI_SCHEMA_VERSION, e as AgentHandoffClient, f as AgentHandoffCommand, o as AgentHandoffEvent, p as AgentHandoffPage, q as AgentHandoffStatus, i as AgentInputOption, j as AgentInputRequest, k as AgentInputResponse, l as AgentRespondTurnOptions, m as AgentResumeTurnOptions, r as AgentSendTurnOptions, s as AgentStreamHandlers, t as AgentStructuredToolInput, u as AgentToolResult, v as AgentToolResultEnvelope, w as AgentToolResultJsonValue, x as AgentToolUiAction, y as AgentToolUiField, z as AgentToolUiFieldKind, B as AgentToolUiOption, C as AgentToolUiStep, D as AgentToolUiSurface, E as AgentWorkItem, F as formatAgentStructuredToolInput, G as parseAgentToolResultEnvelope, H as parseAgentToolUiSurface } from './types-4lTJW5mp.js';
3
+ import 'zod';
3
4
 
4
5
  declare function createAgentClient(options: AgentClientOptions): AgentClient;
5
6
 
package/dist/index.js CHANGED
@@ -12,9 +12,204 @@ import {
12
12
  // src/runtime/client.ts
13
13
  import {
14
14
  Client,
15
- ClientError as ClientError2
15
+ ClientError as ClientError3
16
16
  } from "eve/client";
17
17
 
18
+ // src/runtime/handoff.ts
19
+ import { ClientError as ClientError2 } from "eve/client";
20
+
21
+ // src/runtime/generated/handoff-contract.ts
22
+ import { z } from "zod";
23
+ var id = z.string().min(1).max(200);
24
+ var sequence = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
25
+ var text = z.string().trim().min(1).max(16e3);
26
+ var agentRuntimeHandoffVersion = "webless.ai/agent-runtime-handoff/v1";
27
+ var agentRuntimeHandoffStatusSchema = z.enum([
28
+ "ai",
29
+ "requesting",
30
+ "queued",
31
+ "human",
32
+ "resolved",
33
+ "failed"
34
+ ]);
35
+ var agentRuntimeHandoffActorSchema = z.strictObject({
36
+ id,
37
+ name: z.string().trim().min(1).max(200)
38
+ });
39
+ var eventBase = {
40
+ id,
41
+ sequence: sequence.refine((value) => value > 0),
42
+ handoffId: id,
43
+ epoch: sequence.refine((value) => value > 0),
44
+ createdAt: z.iso.datetime()
45
+ };
46
+ var agentRuntimeHandoffEventSchema = z.discriminatedUnion("type", [
47
+ z.strictObject({
48
+ ...eventBase,
49
+ type: z.literal("status"),
50
+ status: agentRuntimeHandoffStatusSchema,
51
+ actor: agentRuntimeHandoffActorSchema.nullable()
52
+ }),
53
+ z.strictObject({
54
+ ...eventBase,
55
+ type: z.literal("visitor.message"),
56
+ message: text,
57
+ operationId: id
58
+ }),
59
+ z.strictObject({
60
+ ...eventBase,
61
+ type: z.literal("human.message"),
62
+ message: text,
63
+ actor: agentRuntimeHandoffActorSchema
64
+ })
65
+ ]);
66
+ var agentRuntimeHandoffReadRequestSchema = z.strictObject({
67
+ after: sequence.default(0)
68
+ });
69
+ var agentRuntimeHandoffPageSchema = z.strictObject({
70
+ apiVersion: z.literal(agentRuntimeHandoffVersion),
71
+ sessionId: id,
72
+ enabled: z.boolean(),
73
+ status: agentRuntimeHandoffStatusSchema,
74
+ handoffId: id.nullable(),
75
+ epoch: sequence,
76
+ after: sequence,
77
+ cursor: sequence,
78
+ hasMore: z.boolean(),
79
+ events: z.array(agentRuntimeHandoffEventSchema).max(100)
80
+ }).superRefine((page, ctx) => {
81
+ let cursor = page.after;
82
+ let previousEpoch = 0;
83
+ const statuses = /* @__PURE__ */ new Map();
84
+ const ids = /* @__PURE__ */ new Set();
85
+ const handoffs = /* @__PURE__ */ new Map();
86
+ const terminalEpochs = /* @__PURE__ */ new Set();
87
+ let currentStatus;
88
+ for (const event of page.events) {
89
+ if (event.epoch < previousEpoch)
90
+ ctx.addIssue({
91
+ code: "custom",
92
+ message: "Handoff epochs cannot decrease."
93
+ });
94
+ previousEpoch = event.epoch;
95
+ if (ids.has(event.id))
96
+ ctx.addIssue({
97
+ code: "custom",
98
+ message: "Duplicate handoff event ID."
99
+ });
100
+ ids.add(event.id);
101
+ const handoffId = handoffs.get(event.epoch);
102
+ if (handoffId !== void 0 && handoffId !== event.handoffId || event.epoch === page.epoch && event.handoffId !== page.handoffId)
103
+ ctx.addIssue({
104
+ code: "custom",
105
+ message: "Inconsistent handoff epoch identity."
106
+ });
107
+ handoffs.set(event.epoch, event.handoffId);
108
+ if (terminalEpochs.has(event.epoch) && (event.type !== "status" || event.status !== "ai"))
109
+ ctx.addIssue({
110
+ code: "custom",
111
+ message: "Handoff event follows resolution."
112
+ });
113
+ if (event.type === "status") {
114
+ if (event.status === "human" && event.actor === null)
115
+ ctx.addIssue({
116
+ code: "custom",
117
+ message: "Human ownership requires a representative."
118
+ });
119
+ const previous = statuses.get(event.epoch);
120
+ const transitions = {
121
+ ai: [],
122
+ requesting: ["queued", "resolved", "failed"],
123
+ queued: ["human", "resolved", "failed"],
124
+ human: ["resolved", "failed"],
125
+ resolved: ["ai"],
126
+ failed: []
127
+ };
128
+ if (previous !== void 0 && !transitions[previous].includes(event.status))
129
+ ctx.addIssue({
130
+ code: "custom",
131
+ message: "Invalid handoff ownership transition."
132
+ });
133
+ statuses.set(event.epoch, event.status);
134
+ if (["resolved", "failed", "ai"].includes(event.status))
135
+ terminalEpochs.add(event.epoch);
136
+ if (event.epoch === page.epoch) currentStatus = event.status;
137
+ }
138
+ if (event.sequence !== cursor + 1) {
139
+ ctx.addIssue({
140
+ code: "custom",
141
+ message: "Handoff event gap or replay."
142
+ });
143
+ }
144
+ cursor = event.sequence;
145
+ if (event.epoch > page.epoch) {
146
+ ctx.addIssue({
147
+ code: "custom",
148
+ message: "Event exceeds current epoch."
149
+ });
150
+ }
151
+ }
152
+ if (!page.hasMore && currentStatus !== void 0 && currentStatus !== page.status)
153
+ ctx.addIssue({
154
+ code: "custom",
155
+ message: "Handoff ownership contradicts its final status event."
156
+ });
157
+ if (page.cursor !== cursor || page.hasMore && page.events.length === 0) {
158
+ ctx.addIssue({ code: "custom", message: "Invalid handoff page cursor." });
159
+ }
160
+ if (page.epoch === 0 !== (page.handoffId === null)) {
161
+ ctx.addIssue({ code: "custom", message: "Invalid handoff identity." });
162
+ }
163
+ if (page.handoffId === null && page.status !== "ai") {
164
+ ctx.addIssue({
165
+ code: "custom",
166
+ message: "Handoff identity is required."
167
+ });
168
+ }
169
+ });
170
+ var agentRuntimeHandoffStartSchema = z.strictObject({
171
+ operationId: id
172
+ });
173
+ var agentRuntimeHandoffCommandSchema = z.strictObject({
174
+ operationId: id,
175
+ handoffId: id,
176
+ epoch: sequence.refine((value) => value > 0)
177
+ });
178
+ var agentRuntimeHandoffMessageSchema = agentRuntimeHandoffCommandSchema.extend({ message: text });
179
+ var agentRuntimeHandoffBindingSchema = z.strictObject({
180
+ tenantId: id,
181
+ indexId: id,
182
+ visitorSubject: id,
183
+ sessionId: id,
184
+ handoffId: id,
185
+ epoch: sequence.refine((value) => value > 0)
186
+ });
187
+ var providerEventBase = {
188
+ apiVersion: z.literal(agentRuntimeHandoffVersion),
189
+ binding: agentRuntimeHandoffBindingSchema,
190
+ eventId: id,
191
+ sequence: sequence.refine((value) => value > 0)
192
+ };
193
+ var agentRuntimeHandoffProviderEventSchema = z.discriminatedUnion(
194
+ "type",
195
+ [
196
+ z.strictObject({ ...providerEventBase, type: z.literal("queued") }),
197
+ z.strictObject({
198
+ ...providerEventBase,
199
+ type: z.literal("assigned"),
200
+ actor: agentRuntimeHandoffActorSchema
201
+ }),
202
+ z.strictObject({
203
+ ...providerEventBase,
204
+ type: z.literal("human.message"),
205
+ actor: agentRuntimeHandoffActorSchema,
206
+ message: text
207
+ }),
208
+ z.strictObject({ ...providerEventBase, type: z.literal("resolved") }),
209
+ z.strictObject({ ...providerEventBase, type: z.literal("failed") })
210
+ ]
211
+ );
212
+
18
213
  // src/runtime/capability.ts
19
214
  import { ClientError } from "eve/client";
20
215
  var MAX_REFRESH_SKEW_MS = 3e4;
@@ -169,6 +364,75 @@ async function withCapabilityRefresh(capability, request) {
169
364
  }
170
365
  }
171
366
 
367
+ // src/runtime/handoff.ts
368
+ function createHandoffClient(options) {
369
+ const target = () => {
370
+ const sessionId = options.getSessionId();
371
+ if (!sessionId)
372
+ throw new Error(
373
+ "Start a conversation before requesting a representative."
374
+ );
375
+ return {
376
+ sessionId,
377
+ path: `/webless/v1/session/${encodeURIComponent(sessionId)}/handoff`
378
+ };
379
+ };
380
+ const request = (path, init) => withCapabilityRefresh(options.capability, async () => {
381
+ const response = await options.getClient().fetch(path, {
382
+ ...init,
383
+ cache: "no-store",
384
+ redirect: "error"
385
+ });
386
+ if (!response.ok) {
387
+ throw new ClientError2(
388
+ response.status,
389
+ await response.text(),
390
+ response.headers
391
+ );
392
+ }
393
+ return response;
394
+ });
395
+ const command = async (suffix, body, signal) => {
396
+ const { path } = target();
397
+ await request(path + suffix, {
398
+ method: "POST",
399
+ headers: { "content-type": "application/json" },
400
+ body: JSON.stringify(body),
401
+ signal
402
+ });
403
+ };
404
+ return {
405
+ async read(input = {}) {
406
+ const { after } = agentRuntimeHandoffReadRequestSchema.parse({
407
+ after: input.after
408
+ });
409
+ const { sessionId, path } = target();
410
+ const response = await request(`${path}?after=${after}`, {
411
+ signal: input.signal
412
+ });
413
+ const value = await response.json();
414
+ const page = agentRuntimeHandoffPageSchema.parse(value);
415
+ if (page.sessionId !== sessionId || page.after !== after) {
416
+ throw new Error(
417
+ "The handoff response does not belong to this conversation or cursor."
418
+ );
419
+ }
420
+ return page;
421
+ },
422
+ start: (operationId, signal) => command(
423
+ "",
424
+ agentRuntimeHandoffStartSchema.parse({ operationId }),
425
+ signal
426
+ ),
427
+ send: (input, signal) => command(
428
+ "/messages",
429
+ agentRuntimeHandoffMessageSchema.parse(input),
430
+ signal
431
+ ),
432
+ returnToAI: (input, signal) => command("/return", agentRuntimeHandoffCommandSchema.parse(input), signal)
433
+ };
434
+ }
435
+
172
436
  // src/runtime/config.ts
173
437
  var DEFAULT_RUNTIME_ORIGIN = "https://runtime.staging.webless.ai";
174
438
  var trimTrailingSlash = (value) => value.replace(/\/+$/, "");
@@ -768,6 +1032,11 @@ var AgentSession = class {
768
1032
  version,
769
1033
  visitorSessionId
770
1034
  });
1035
+ this.handoff = createHandoffClient({
1036
+ getClient: () => this.ensureClient(),
1037
+ getSessionId: () => this.getActiveSessionId(),
1038
+ capability: this.capability
1039
+ });
771
1040
  }
772
1041
  indexId;
773
1042
  version;
@@ -780,6 +1049,7 @@ var AgentSession = class {
780
1049
  activeResponse;
781
1050
  childStreams;
782
1051
  capability;
1052
+ handoff;
783
1053
  getActiveSessionId() {
784
1054
  return this.session?.state.sessionId ?? loadPersistedAgentSession(this.visitorSessionId, this.storeOptions)?.sessionId;
785
1055
  }
@@ -890,7 +1160,7 @@ var AgentSession = class {
890
1160
  () => activeSession.send(message, { signal })
891
1161
  );
892
1162
  } catch (error) {
893
- if (error instanceof ClientError2 && error.status === 409 && error.code === "session_not_active") {
1163
+ if (error instanceof ClientError3 && error.status === 409 && error.code === "session_not_active") {
894
1164
  clearPersistedAgentSession(this.visitorSessionId, this.storeOptions);
895
1165
  this.session = void 0;
896
1166
  session = void 0;
@@ -1064,10 +1334,10 @@ var AgentSession = class {
1064
1334
  }
1065
1335
  this.session = session;
1066
1336
  const inputResponses = responses.map(
1067
- ({ requestId, optionId, text }) => ({
1337
+ ({ requestId, optionId, text: text2 }) => ({
1068
1338
  requestId,
1069
1339
  ...optionId ? { optionId } : {},
1070
- ...text ? { text } : {}
1340
+ ...text2 ? { text: text2 } : {}
1071
1341
  })
1072
1342
  );
1073
1343
  const response = await withCapabilityRefresh(
@@ -1157,6 +1427,7 @@ function createAgentClient(options) {
1157
1427
  );
1158
1428
  return {
1159
1429
  indexId,
1430
+ handoff: session.handoff,
1160
1431
  version,
1161
1432
  runtimeOrigin,
1162
1433
  visitorSessionId,
@@ -1188,7 +1459,7 @@ function createAgentClient(options) {
1188
1459
  }
1189
1460
 
1190
1461
  // src/runtime/errors.ts
1191
- import { ClientError as ClientError3 } from "eve/client";
1462
+ import { ClientError as ClientError4 } from "eve/client";
1192
1463
  var TRANSIENT_AGENT_ERROR_MESSAGE = "The agent run stopped before the action finished. Please try again.";
1193
1464
  var PREVIEW_AUTHORIZATION_ERROR_MESSAGE = "This preview could not be authorized. Open the latest preview from Webless.";
1194
1465
  function isTransientRuntimeMessage(message) {
@@ -1200,7 +1471,7 @@ function isPreviewAuthorizationMessage(message) {
1200
1471
  return normalized.includes("unpublished preview authorization") || normalized.includes("unpublished preview grant") || normalized.includes("agent studio preview grant") || normalized.includes("preview authorization");
1201
1472
  }
1202
1473
  function formatAgentError(error) {
1203
- if (error instanceof ClientError3) {
1474
+ if (error instanceof ClientError4) {
1204
1475
  if (error.status === 401 && error.code === "index_required") {
1205
1476
  return "Missing indexId \u2014 pass a published index id to createAgentClient().";
1206
1477
  }