@qoniai/qoni 0.4.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.js ADDED
@@ -0,0 +1,2866 @@
1
+ // src/signature.ts
2
+ import { createHmac } from "crypto";
3
+ function filter(value) {
4
+ return String(value).replace(/[\t\n\r\f]/g, " ");
5
+ }
6
+ function canonicalizedHeaders(headers) {
7
+ return Object.keys(headers).filter((key) => key.startsWith("x-authing-") || key === "date").sort().map((key) => `${key}:${filter(headers[key]).trim()}
8
+ `).join("");
9
+ }
10
+ function canonicalizedResource(pathname, params) {
11
+ const keys = Object.keys(params).sort();
12
+ if (!keys.length) return pathname;
13
+ const query = keys.map((key) => {
14
+ const value = params[key];
15
+ return `${key}=${typeof value === "object" && value !== null ? JSON.stringify(value) : value}`;
16
+ });
17
+ return `${pathname}?${query.join("&")}`;
18
+ }
19
+ function buildStringToSign(method, pathname, headers, params) {
20
+ return `${method.toUpperCase()}
21
+ ${canonicalizedHeaders(headers)}${canonicalizedResource(
22
+ pathname,
23
+ params
24
+ )}`;
25
+ }
26
+ function buildSignature(secretKey, stringToSign) {
27
+ return createHmac("sha1", secretKey).update(stringToSign, "utf8").digest("base64");
28
+ }
29
+ function buildAuthorization(accessKey, secretKey, stringToSign) {
30
+ return `authing ${accessKey}:${buildSignature(secretKey, stringToSign)}`;
31
+ }
32
+
33
+ // src/auth.ts
34
+ var lastSigningTimestamp = 0;
35
+ function nextSigningTimestamp() {
36
+ const now = Date.now();
37
+ lastSigningTimestamp = Math.max(now, lastSigningTimestamp + 1);
38
+ return lastSigningTimestamp;
39
+ }
40
+ function buildSignedHeaders(credentials, method, pathname, payload) {
41
+ const headers = {
42
+ "content-type": "application/json",
43
+ // GenAuth rejects an already-consumed signature. A strictly monotonic
44
+ // timestamp keeps otherwise identical requests unique within a process,
45
+ // including retries or concurrent calls that start in the same millisecond.
46
+ "x-authing-date": String(nextSigningTimestamp()),
47
+ "x-authing-signature-method": "HMAC-SHA1",
48
+ "x-authing-signature-version": "1.0"
49
+ };
50
+ const stringToSign = buildStringToSign(method, pathname, headers, payload);
51
+ headers.authorization = buildAuthorization(
52
+ credentials.accessKey,
53
+ credentials.secretKey,
54
+ stringToSign
55
+ );
56
+ return headers;
57
+ }
58
+
59
+ // src/scopes.ts
60
+ var QoniScopes = {
61
+ GUMEM_SESSION_CREATE: "gumem.session:create",
62
+ GUMEM_MESSAGE_WRITE: "gumem.message:write",
63
+ GUMEM_MEMORY_READ: "gumem.memory:read",
64
+ GUMEM_MEMORY_WRITE: "gumem.memory:write",
65
+ GUMEM_MEMORY_DELETE: "gumem.memory:delete",
66
+ GUMEM_SEARCH_RUN: "gumem.search:run",
67
+ GUMEM_ADMIN_MANAGE: "gumem.admin:manage",
68
+ GUMEM_RESOURCE_WRITE: "gumem.resource:write",
69
+ GUMEM_ACTION_WRITE: "gumem.action:write",
70
+ GUMEM_ACTION_READ: "gumem.action:read",
71
+ GUMEM_PROFILE_READ: "gumem.profile:read",
72
+ // WebAgent products: exactly two verbs per product — `read` (observe) and
73
+ // `manage` (anything that changes execution state). See
74
+ // wa/docs/qoni-sdk-public-surface.md §3.
75
+ DO_ANYTHING_READ: "webagent.do_anything:read",
76
+ DO_ANYTHING_MANAGE: "webagent.do_anything:manage",
77
+ WEB_SEARCH_READ: "webagent.web_search:read",
78
+ WEB_SEARCH_MANAGE: "webagent.web_search:manage",
79
+ DEEP_RESEARCH_READ: "webagent.deep_research:read",
80
+ DEEP_RESEARCH_MANAGE: "webagent.deep_research:manage",
81
+ TRACK_READ: "webagent.track:read",
82
+ TRACK_MANAGE: "webagent.track:manage"
83
+ };
84
+ var QoniProductScopes = {
85
+ doAnything: [
86
+ QoniScopes.DO_ANYTHING_READ,
87
+ QoniScopes.DO_ANYTHING_MANAGE
88
+ ],
89
+ webSearch: [
90
+ QoniScopes.WEB_SEARCH_READ,
91
+ QoniScopes.WEB_SEARCH_MANAGE
92
+ ],
93
+ deepResearch: [
94
+ QoniScopes.DEEP_RESEARCH_READ,
95
+ QoniScopes.DEEP_RESEARCH_MANAGE
96
+ ],
97
+ track: [
98
+ QoniScopes.TRACK_READ,
99
+ QoniScopes.TRACK_MANAGE
100
+ ]
101
+ };
102
+ var KNOWN_SCOPES = Object.values(QoniScopes);
103
+ var QoniScopeBundles = {
104
+ GUMEM_READONLY: [QoniScopes.GUMEM_MEMORY_READ, QoniScopes.GUMEM_PROFILE_READ],
105
+ GUMEM_SESSION_RECALL: [
106
+ QoniScopes.GUMEM_MEMORY_READ,
107
+ QoniScopes.GUMEM_MEMORY_WRITE,
108
+ QoniScopes.GUMEM_MESSAGE_WRITE
109
+ ],
110
+ GUMEM_WRITE: [
111
+ QoniScopes.GUMEM_SESSION_CREATE,
112
+ QoniScopes.GUMEM_MESSAGE_WRITE,
113
+ QoniScopes.GUMEM_ACTION_WRITE
114
+ ],
115
+ // One bundle per WebAgent product = [read, manage].
116
+ DO_ANYTHING: [
117
+ QoniScopes.DO_ANYTHING_READ,
118
+ QoniScopes.DO_ANYTHING_MANAGE
119
+ ],
120
+ WEB_SEARCH: [
121
+ QoniScopes.WEB_SEARCH_READ,
122
+ QoniScopes.WEB_SEARCH_MANAGE
123
+ ],
124
+ DEEP_RESEARCH: [
125
+ QoniScopes.DEEP_RESEARCH_READ,
126
+ QoniScopes.DEEP_RESEARCH_MANAGE
127
+ ],
128
+ TRACK: [
129
+ QoniScopes.TRACK_READ,
130
+ QoniScopes.TRACK_MANAGE
131
+ ]
132
+ };
133
+
134
+ // src/types.ts
135
+ function isJsonObject(value) {
136
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
137
+ }
138
+
139
+ // src/errors.ts
140
+ var QoniError = class extends Error {
141
+ code;
142
+ status;
143
+ requestId;
144
+ traceId;
145
+ auditId;
146
+ retryable;
147
+ body;
148
+ constructor(message, options = {}) {
149
+ super(message);
150
+ this.name = "QoniError";
151
+ this.code = options.code || "qoni_error";
152
+ this.status = options.status;
153
+ this.requestId = options.requestId;
154
+ this.traceId = options.traceId;
155
+ this.auditId = options.auditId;
156
+ this.retryable = options.retryable ?? false;
157
+ this.body = options.body;
158
+ if (options.cause !== void 0) {
159
+ this.cause = options.cause;
160
+ }
161
+ }
162
+ };
163
+ var QoniAuthError = class extends QoniError {
164
+ constructor(message, options = {}) {
165
+ super(message, { code: "auth.failed", ...options });
166
+ this.name = "QoniAuthError";
167
+ }
168
+ };
169
+ var QoniDelegationRequiredError = class extends QoniError {
170
+ constructor(message, options = {}) {
171
+ super(message, { code: "delegation.required", ...options });
172
+ this.name = "QoniDelegationRequiredError";
173
+ }
174
+ };
175
+ var QoniPermissionDeniedError = class extends QoniError {
176
+ constructor(message, options = {}) {
177
+ super(message, { code: "permission_denied", ...options });
178
+ this.name = "QoniPermissionDeniedError";
179
+ }
180
+ };
181
+ var QoniTokenExpiredError = class extends QoniError {
182
+ constructor(message, options = {}) {
183
+ super(message, { code: "token.expired", ...options });
184
+ this.name = "QoniTokenExpiredError";
185
+ }
186
+ };
187
+ var QoniValidationError = class extends QoniError {
188
+ constructor(message, options = {}) {
189
+ super(message, { code: "validation.failed", ...options });
190
+ this.name = "QoniValidationError";
191
+ }
192
+ };
193
+ var QoniRateLimitError = class extends QoniError {
194
+ constructor(message, options = {}) {
195
+ super(message, { code: "rate_limit.exceeded", retryable: true, ...options });
196
+ this.name = "QoniRateLimitError";
197
+ }
198
+ };
199
+ var QoniUpstreamError = class extends QoniError {
200
+ constructor(message, options = {}) {
201
+ super(message, { code: "upstream.failed", retryable: true, ...options });
202
+ this.name = "QoniUpstreamError";
203
+ }
204
+ };
205
+ var QoniTimeoutError = class extends QoniError {
206
+ constructor(message, options = {}) {
207
+ super(message, { code: "timeout", retryable: true, ...options });
208
+ this.name = "QoniTimeoutError";
209
+ }
210
+ };
211
+ function errorFromPayload(status, payload, fallbackMessage) {
212
+ const detail = isJsonObject(payload) ? payload.detail : void 0;
213
+ const detailObject = isJsonObject(detail) ? detail : void 0;
214
+ const source = detailObject || (isJsonObject(payload) ? payload : {});
215
+ const code = stringField(source, "code") || stringField(source, "apiCode") || statusCodeToCode(status);
216
+ const rawMessage = stringField(source, "message") || stringField(source, "detail") || fallbackMessage;
217
+ const message = appendOnboardingHint(
218
+ appendDeniedScopes(appendUpstreamDescription(rawMessage, source), source),
219
+ code
220
+ );
221
+ const options = {
222
+ code,
223
+ status,
224
+ requestId: stringField(payload, "requestId") || stringField(source, "requestId"),
225
+ traceId: stringField(payload, "traceId") || stringField(source, "traceId"),
226
+ auditId: stringField(payload, "auditId") || stringField(source, "auditId"),
227
+ retryable: status === 429 || status >= 500,
228
+ body: payload
229
+ };
230
+ if (isTokenExpired(code, message)) return new QoniTokenExpiredError(message, options);
231
+ if (isDelegationRequired(code)) return new QoniDelegationRequiredError(message, options);
232
+ if (status === 401) return new QoniAuthError(message, options);
233
+ if (status === 403 || isPermissionDenied(code)) {
234
+ return new QoniPermissionDeniedError(appendKnownScopes(message), options);
235
+ }
236
+ if (status === 400 || status === 422) return new QoniValidationError(message, options);
237
+ if (status === 429) return new QoniRateLimitError(message, options);
238
+ if (status >= 500) return new QoniUpstreamError(message, options);
239
+ return new QoniError(message, options);
240
+ }
241
+ function timeoutError(cause) {
242
+ return new QoniTimeoutError("Qoni request timed out", { cause });
243
+ }
244
+ function networkError(cause) {
245
+ return new QoniUpstreamError("Qoni network request failed", { cause });
246
+ }
247
+ function stringField(value, key) {
248
+ if (!isJsonObject(value)) return void 0;
249
+ const field = value[key];
250
+ return typeof field === "string" && field ? field : void 0;
251
+ }
252
+ function appendDeniedScopes(message, source) {
253
+ const details = isJsonObject(source) ? source.details : void 0;
254
+ const raw = (isJsonObject(details) ? details.deniedScopes : void 0) ?? (isJsonObject(source) ? source.deniedScopes : void 0);
255
+ if (!Array.isArray(raw) || raw.length === 0) return message;
256
+ const scopes = raw.filter((s) => typeof s === "string");
257
+ if (scopes.length === 0 || message.includes(scopes[0])) return message;
258
+ return `${message} (scopes not granted: ${scopes.join(", ")})`;
259
+ }
260
+ function appendUpstreamDescription(message, source) {
261
+ const details = isJsonObject(source) ? source.details : void 0;
262
+ const upstream = isJsonObject(details) ? details.upstream : void 0;
263
+ const description = stringField(upstream, "error_description") || stringField(upstream, "message") || stringField(upstream, "error");
264
+ if (!description || message.includes(description)) return message;
265
+ return `${message}: ${description}`;
266
+ }
267
+ var ONBOARDING_HINTS = {
268
+ "eak.delegation.user_not_bound": "Hint: the userId is not in the GenAuth userpool bound to this credential. Resolve a real user with qoni.resolveAnyBoundUser() (demo/smoke) or qoni.currentUser({ accessToken }).",
269
+ "eak.genauth.userpool_binding_missing": "Hint: bind the Qoni credential to a GenAuth userpool before calling genauth.users.*."
270
+ };
271
+ function appendOnboardingHint(message, code) {
272
+ const hint = ONBOARDING_HINTS[code];
273
+ if (!hint || message.includes(hint)) return message;
274
+ return `${message} ${hint}`;
275
+ }
276
+ function appendKnownScopes(message) {
277
+ if (message.includes("Known scopes:")) return message;
278
+ return `${message} (Known scopes: ${KNOWN_SCOPES.join(", ")})`;
279
+ }
280
+ function statusCodeToCode(status) {
281
+ if (status === 401) return "auth.failed";
282
+ if (status === 403) return "permission_denied";
283
+ if (status === 404) return "not_found";
284
+ if (status === 409) return "conflict";
285
+ if (status === 422) return "validation.failed";
286
+ if (status === 429) return "rate_limit.exceeded";
287
+ if (status >= 500) return "upstream.failed";
288
+ return "qoni_error";
289
+ }
290
+ function isPermissionDenied(code) {
291
+ return code.includes("permission") || code.includes("insufficient_scope");
292
+ }
293
+ function isDelegationRequired(code) {
294
+ return code.includes("interactive_required") || code.includes("delegation.required");
295
+ }
296
+ function isTokenExpired(code, message) {
297
+ return code.includes("expired") || /token expired/i.test(message);
298
+ }
299
+
300
+ // src/run-events.ts
301
+ var QoniEventTypes = {
302
+ // --- Core (any product run can emit these) ---
303
+ /** The agent is working — folds ALL internal steps (actions, plan/subtask, sections, telemetry, take-control, …); `event.data` is a best-effort human-readable line (string). */
304
+ Progress: "progress",
305
+ /** The agent produced a user-facing chat message. */
306
+ Message: "message",
307
+ /** HITL — a typed interaction needs the user (login / clarification / confirmation / take-control / wait); `event.data` is the full {@link Interaction} object. Act on it via the action methods (`run.interactionHandle(event.data).answer(…)` etc). */
308
+ Interaction: "interaction",
309
+ /** A live screenshot frame (opt in with `capture: { screenshots: true }`); image on `event.image`. */
310
+ Screenshot: "screenshot",
311
+ /** Terminal — the run finished; outcome + output in `event.data` (cost is on the `RunResult`). */
312
+ Done: "done",
313
+ // --- Web Search ---
314
+ /** Web Search results are ready; `event.data` is the count (the list is in `done.output`). */
315
+ ResultsReady: "resultsReady",
316
+ // --- Deep Research ---
317
+ /** Deep Research coarse phase changed (brief → gather → synthesize …). */
318
+ Phase: "phase",
319
+ /** A Deep Research outline section was completed (stream sections as they finish). */
320
+ SectionReady: "sectionReady",
321
+ // --- Track (monitor) ---
322
+ /** A Track monitor was created (e.g. Do Anything handed a recurring request to Track). */
323
+ MonitorCreated: "monitorCreated",
324
+ /** A monitor detected a change worth acting on (the Track signal you react to). */
325
+ Triggered: "triggered",
326
+ /** A monitor's scheduled check finished (`event.data` is whether it found a change). */
327
+ CheckCompleted: "checkCompleted"
328
+ };
329
+ function isTerminalRunStatus(status) {
330
+ return status === "succeeded" || status === "failed" || status === "canceled";
331
+ }
332
+ function camelKey(key) {
333
+ return key.replace(/_([a-z0-9])/g, (_, ch) => ch.toUpperCase());
334
+ }
335
+ function camelizeKeys(value) {
336
+ if (Array.isArray(value)) return value.map(camelizeKeys);
337
+ if (!isJsonObject(value)) return value;
338
+ const out = {};
339
+ for (const [key, item] of Object.entries(value)) {
340
+ out[camelKey(key)] = camelizeKeys(item);
341
+ }
342
+ return out;
343
+ }
344
+ function camelizeRecord(value) {
345
+ const out = camelizeKeys(value);
346
+ return isJsonObject(out) ? out : {};
347
+ }
348
+ function decodeDataUri(uri) {
349
+ const match = /^data:([^;,]*)?(;base64)?,(.*)$/s.exec(uri);
350
+ if (!match) return void 0;
351
+ const [, mime, isBase64, body] = match;
352
+ const bytes = isBase64 ? new Uint8Array(Buffer.from(body, "base64")) : new TextEncoder().encode(decodeURIComponent(body));
353
+ return { bytes, mime: mime || "application/octet-stream" };
354
+ }
355
+ function asString(value) {
356
+ return typeof value === "string" && value ? value : void 0;
357
+ }
358
+ function asNumber(value) {
359
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
360
+ }
361
+ function decodeScreenshotImage(payload) {
362
+ const url = asString(payload.screenshot_url);
363
+ if (!url) return void 0;
364
+ const decoded = decodeDataUri(url);
365
+ if (!decoded) return void 0;
366
+ return {
367
+ ...decoded,
368
+ pageUrl: asString(payload.page_url) ?? asString(payload.url),
369
+ step: asNumber(payload.step) ?? asNumber(payload.step_index)
370
+ };
371
+ }
372
+ var HEARTBEAT = "stream.heartbeat";
373
+ var SCREENSHOT = "run.screenshot";
374
+ var TERMINAL = "run.completed";
375
+ var INTERACTION = "run.interaction";
376
+ var WIRE_TO_SEMANTIC = {
377
+ // --- Headline events a developer reacts to ---
378
+ "run.message": "message",
379
+ "search.results_ready": "resultsReady",
380
+ "run.phase_changed": "phase",
381
+ // Deep Research coarse phase
382
+ "run.section_completed": "sectionReady",
383
+ // Deep Research section finished
384
+ "run.monitor_created": "monitorCreated",
385
+ // Do Anything → Track handoff
386
+ // NB: `run.interaction` is NOT here — it's an out-of-band case in the
387
+ // normalizer (→ "interaction", carrying the typed Interaction object), like
388
+ // screenshot/done. The old `run.input_request` / `run.take_control_*` family
389
+ // it replaces is being removed backend-side; while the backend still lists
390
+ // them in the catalog (dual-emit window) they fold to progress below — the
391
+ // SDK no longer has an `inputRequired` surface (clean break).
392
+ // --- Internal churn → progress (raw type stays on event.raw.event) ---
393
+ // supervisor lifecycle / planning
394
+ "run.status_changed": "progress",
395
+ "run.input_request": "progress",
396
+ // superseded by run.interaction
397
+ "run.input_request_resolved": "progress",
398
+ "run.cost_update": "progress",
399
+ // cost is surfaced on RunResult, not as an event
400
+ "run.plan_ready": "progress",
401
+ "run.plan_finalized": "progress",
402
+ "run.subtask_started": "progress",
403
+ "run.subtask_graded": "progress",
404
+ "run.subtask_status": "progress",
405
+ "run.todos_updated": "progress",
406
+ // sub-agent execution (actions + browser-agent telemetry)
407
+ "run.action.started": "progress",
408
+ "run.action.completed": "progress",
409
+ "run.action.failed": "progress",
410
+ "run.action.progress": "progress",
411
+ "run.action_result": "progress",
412
+ "run.browser_agent.config_applied": "progress",
413
+ "run.browser_agent.summary": "progress",
414
+ "run.browser_agent.vision_decision": "progress",
415
+ "run.browser_agent.observe": "progress",
416
+ "run.browser_agent.page_gate": "progress",
417
+ // web search / deep research intermediate progress
418
+ "search.progress": "progress",
419
+ "search.summarize_progress": "progress",
420
+ "search.done": "progress",
421
+ // run ends on run.completed → "done"
422
+ "run.section_started": "progress",
423
+ "run.section_failed": "progress",
424
+ "run.replan_started": "progress",
425
+ "run.crosscheck_done": "progress",
426
+ "run.synthesize_progress": "progress",
427
+ // take-control / recording / live-url protocol
428
+ "run.take_control_pending": "progress",
429
+ "run.take_control_expired": "progress",
430
+ "run.user_paused": "progress",
431
+ "run.user_released": "progress",
432
+ "run.recording_started": "progress",
433
+ "run.recording_paused": "progress",
434
+ "run.recording_finalized": "progress",
435
+ "run.browser_live_url_changed": "progress",
436
+ // side-channel
437
+ "run.feedback_submitted": "progress"
438
+ };
439
+ var MONITOR_TRIGGERED = "monitor.triggered";
440
+ var MONITOR_LIFECYCLE = "monitor.lifecycle_changed";
441
+ function pickNote(c) {
442
+ return asString(c.note) ?? asString(c.summary) ?? asString(c.lastStepSummary) ?? asString(c.label) ?? asString(c.text) ?? asString(c.kind) ?? // action events: "click" / "input" / "navigate" / …
443
+ asString(c.toPhase) ?? asString(c.status) ?? asString(c.to) ?? asString(c.reason) ?? "";
444
+ }
445
+ function shapeData(type, c) {
446
+ switch (type) {
447
+ // Simple events: event.data IS the value.
448
+ case "progress":
449
+ return pickNote(c);
450
+ // a human-readable line (string)
451
+ case "resultsReady":
452
+ return asNumber(c.totalUniqueResults) ?? asNumber(c.count) ?? 0;
453
+ // count
454
+ case "phase":
455
+ return asString(c.toPhase) ?? asString(c.phase) ?? "";
456
+ // phase name
457
+ case "sectionReady":
458
+ return asString(c.title) ?? asString(c.sectionTitle) ?? "";
459
+ // section title
460
+ case "monitorCreated":
461
+ return asString(c.monitorId) ?? "";
462
+ // monitor id
463
+ case "triggered":
464
+ return asString(c.summary) ?? asString(c.note) ?? "";
465
+ // change summary
466
+ case "checkCompleted":
467
+ return c.changed === true;
468
+ // whether a change was found
469
+ // Rich events: event.data is a small object.
470
+ case "message":
471
+ return { text: asString(c.text) ?? "", role: asString(c.role) ?? "" };
472
+ }
473
+ }
474
+ function normalizeRunEvent(wire, options = {}) {
475
+ const envelope = isJsonObject(wire.data) ? wire.data : void 0;
476
+ const wireType = wire.event ?? asString(envelope?.type);
477
+ if (!wireType) return null;
478
+ if (wireType === HEARTBEAT) return null;
479
+ const payload = isJsonObject(envelope?.data) ? envelope.data : {};
480
+ const taskId = asString(envelope?.task_id);
481
+ const runId = taskId ?? options.topRunId ?? "";
482
+ const at = asString(envelope?.occurred_at) ?? asString(payload.occurred_at) ?? asString(payload.timestamp) ?? (/* @__PURE__ */ new Date()).toISOString();
483
+ const c = camelizeRecord(payload);
484
+ const base = { runId, at, raw: wire };
485
+ const isSubRun = Boolean(options.topRunId && taskId && taskId !== options.topRunId);
486
+ if (wireType === SCREENSHOT) {
487
+ const image = decodeScreenshotImage(payload);
488
+ if (image) {
489
+ const sData = {};
490
+ const pageUrl = asString(c.pageUrl) ?? asString(c.url);
491
+ const step = asNumber(c.step) ?? asNumber(c.stepIndex);
492
+ if (pageUrl !== void 0) sData.pageUrl = pageUrl;
493
+ if (step !== void 0) sData.step = step;
494
+ return { ...base, type: "screenshot", data: sData, image, isTerminal: false };
495
+ }
496
+ }
497
+ if (wireType === INTERACTION) {
498
+ const interaction = normalizeInteraction(payload);
499
+ if (interaction) {
500
+ return { ...base, type: "interaction", data: interaction, isTerminal: false };
501
+ }
502
+ }
503
+ if (wireType === TERMINAL && !isSubRun) {
504
+ const dData = {
505
+ output: c.output,
506
+ succeeded: typeof c.isTaskSuccessful === "boolean" ? c.isTaskSuccessful : null,
507
+ terminalReason: asString(c.terminalReason) ?? ""
508
+ };
509
+ return { ...base, type: "done", data: dData, isTerminal: true };
510
+ }
511
+ let type;
512
+ if (wireType === MONITOR_TRIGGERED) type = "triggered";
513
+ else if (wireType === MONITOR_LIFECYCLE) type = "checkCompleted";
514
+ else type = WIRE_TO_SEMANTIC[wireType] ?? "progress";
515
+ const data = shapeData(type, c);
516
+ if (type === "progress" && data === "") return null;
517
+ return { ...base, type, data, isTerminal: false };
518
+ }
519
+
520
+ // src/generated/interaction-types.ts
521
+ var InteractionTypes = {
522
+ SiteLogin: "site_login",
523
+ Clarification: "clarification",
524
+ Confirmation: "confirmation",
525
+ TakeControl: "take_control",
526
+ Wait: "wait"
527
+ };
528
+ var INTERACTION_TYPE_VALUES = Object.values(InteractionTypes);
529
+ var InteractionStatuses = {
530
+ Pending: "pending",
531
+ Active: "active",
532
+ Resolved: "resolved",
533
+ Expired: "expired",
534
+ Canceled: "canceled"
535
+ };
536
+ var INTERACTION_STATUS_VALUES = Object.values(InteractionStatuses);
537
+ var ActionKinds = {
538
+ answer: "answer",
539
+ skip: "skip",
540
+ confirm: "confirm",
541
+ reject: "reject",
542
+ openLogin: "open_login",
543
+ confirmSignedIn: "confirm_signed_in",
544
+ connectControl: "connect_control",
545
+ refreshControl: "refresh_control",
546
+ releaseControl: "release_control",
547
+ retry: "retry",
548
+ switchProfile: "switch_profile"
549
+ };
550
+ var ACTION_KIND_VALUES = Object.values(ActionKinds);
551
+
552
+ // src/interactions.ts
553
+ function asString2(value) {
554
+ return typeof value === "string" && value ? value : void 0;
555
+ }
556
+ function asBool(value) {
557
+ return typeof value === "boolean" ? value : void 0;
558
+ }
559
+ function normalizeActions(value) {
560
+ if (!Array.isArray(value)) return [];
561
+ const out = [];
562
+ for (const item of value) {
563
+ if (!isJsonObject(item)) continue;
564
+ const kind = asString2(item.kind);
565
+ if (!kind) continue;
566
+ const action = {
567
+ kind,
568
+ label: asString2(item.label) ?? "",
569
+ method: asString2(item.method) ?? "POST",
570
+ endpoint: asString2(item.endpoint) ?? ""
571
+ };
572
+ if (isJsonObject(item.inputSchema)) action.inputSchema = item.inputSchema;
573
+ out.push(action);
574
+ }
575
+ return out;
576
+ }
577
+ function normalizeEvidence(c) {
578
+ const artifactId = asString2(c.evidenceArtifactId) ?? (isJsonObject(c.evidence) ? asString2(c.evidence.artifactId) : void 0);
579
+ return artifactId ? { artifactId } : void 0;
580
+ }
581
+ function shapePayload(type, c) {
582
+ switch (type) {
583
+ case "site_login": {
584
+ const sites = Array.isArray(c.sites) ? c.sites.flatMap((raw) => {
585
+ if (!isJsonObject(raw)) return [];
586
+ return [
587
+ {
588
+ siteId: asString2(raw.siteId) ?? "",
589
+ displayName: asString2(raw.displayName) ?? "",
590
+ loginUrl: asString2(raw.loginUrl) ?? ""
591
+ }
592
+ ];
593
+ }) : [];
594
+ const payload = { sites };
595
+ const monitorId = asString2(c.monitorId);
596
+ if (monitorId) payload.monitorId = monitorId;
597
+ return payload;
598
+ }
599
+ case "clarification": {
600
+ const payload = { question: asString2(c.question) ?? "" };
601
+ const inputType = asString2(c.inputType);
602
+ if (inputType) payload.inputType = inputType;
603
+ return payload;
604
+ }
605
+ case "confirmation":
606
+ return { summary: asString2(c.summary) ?? "" };
607
+ case "take_control": {
608
+ const payload = { liveUrl: asString2(c.liveUrl) ?? "" };
609
+ const surface = asString2(c.surface);
610
+ const reason = asString2(c.reason);
611
+ if (surface) payload.surface = surface;
612
+ if (reason) payload.reason = reason;
613
+ return payload;
614
+ }
615
+ case "wait": {
616
+ const payload = {
617
+ waitKind: asString2(c.waitKind) ?? "external"
618
+ };
619
+ const until = asString2(c.until);
620
+ const retryable = asBool(c.retryable);
621
+ if (until) payload.until = until;
622
+ if (retryable !== void 0) payload.retryable = retryable;
623
+ return payload;
624
+ }
625
+ }
626
+ }
627
+ function normalizeInteraction(payload) {
628
+ const c = camelizeRecord(payload);
629
+ const type = asString2(c.type);
630
+ if (!type) return null;
631
+ const interaction = {
632
+ id: asString2(c.id) ?? "",
633
+ type,
634
+ status: asString2(c.status) ?? "pending",
635
+ createdAt: asString2(c.createdAt) ?? "",
636
+ title: asString2(c.title) ?? "",
637
+ actions: normalizeActions(c.actions),
638
+ payload: shapePayload(type, c)
639
+ };
640
+ const resolvedAt = asString2(c.resolvedAt);
641
+ const expiresAt = asString2(c.expiresAt);
642
+ const prompt = asString2(c.prompt);
643
+ const evidence = normalizeEvidence(c);
644
+ if (resolvedAt) interaction.resolvedAt = resolvedAt;
645
+ if (expiresAt) interaction.expiresAt = expiresAt;
646
+ if (prompt) interaction.prompt = prompt;
647
+ if (evidence) interaction.evidence = evidence;
648
+ return interaction;
649
+ }
650
+ var InteractionHandle = class {
651
+ constructor(interaction, post) {
652
+ this.post = post;
653
+ this.interaction = interaction;
654
+ }
655
+ post;
656
+ interaction;
657
+ get id() {
658
+ return this.interaction.id;
659
+ }
660
+ get type() {
661
+ return this.interaction.type;
662
+ }
663
+ get status() {
664
+ return this.interaction.status;
665
+ }
666
+ get actions() {
667
+ return this.interaction.actions;
668
+ }
669
+ /** Whether the backend declared an action of this kind on the interaction. */
670
+ can(kind) {
671
+ return this.interaction.actions.some((a) => a.kind === kind);
672
+ }
673
+ /** Answer a clarification ask. */
674
+ answer(text) {
675
+ return this.invoke(ActionKinds.answer, { response: text });
676
+ }
677
+ /** Skip / dismiss the interaction (proceed without acting on it). */
678
+ skip() {
679
+ return this.invoke(ActionKinds.skip);
680
+ }
681
+ /** Confirm a pending confirmation (approve the plan / destructive op). */
682
+ confirm() {
683
+ return this.invoke(ActionKinds.confirm);
684
+ }
685
+ /** Reject a pending confirmation. */
686
+ reject() {
687
+ return this.invoke(ActionKinds.reject);
688
+ }
689
+ /** Open the login surface for a site_login interaction. */
690
+ openLogin() {
691
+ return this.invoke(ActionKinds.openLogin);
692
+ }
693
+ /** Tell the agent the user has finished signing in. */
694
+ confirmSignedIn() {
695
+ return this.invoke(ActionKinds.confirmSignedIn);
696
+ }
697
+ /** Connect to the live browser for a take_control interaction. */
698
+ connectControl() {
699
+ return this.invoke(ActionKinds.connectControl);
700
+ }
701
+ /** Refresh the live control surface / live url. */
702
+ refreshControl() {
703
+ return this.invoke(ActionKinds.refreshControl);
704
+ }
705
+ /** Hand control back to the agent. */
706
+ releaseControl() {
707
+ return this.invoke(ActionKinds.releaseControl);
708
+ }
709
+ /** Retry a wait interaction now. */
710
+ retry() {
711
+ return this.invoke(ActionKinds.retry);
712
+ }
713
+ /** Switch the profile a wait/login interaction is bound to. */
714
+ switchProfile() {
715
+ return this.invoke(ActionKinds.switchProfile);
716
+ }
717
+ /**
718
+ * Invoke an action by kind with an optional body. `async` so a missing-action
719
+ * error surfaces as a rejected promise (not a synchronous throw), keeping the
720
+ * method contract uniformly promise-based.
721
+ */
722
+ async invoke(kind, body) {
723
+ const action = this.interaction.actions.find((a) => a.kind === kind);
724
+ if (!action) {
725
+ const offered = this.interaction.actions.map((a) => a.kind).join(", ") || "none";
726
+ throw new QoniValidationError(
727
+ `interaction "${this.interaction.id}" (${this.interaction.type}) does not offer the "${kind}" action \u2014 the backend declared: ${offered}. Only call methods whose action the interaction currently offers.`
728
+ );
729
+ }
730
+ await this.post(action, body);
731
+ }
732
+ };
733
+
734
+ // src/run-handle.ts
735
+ var CANCEL_IDEMPOTENT_STATUSES = /* @__PURE__ */ new Set([400, 404, 409, 410, 422]);
736
+ var RunHandle = class {
737
+ constructor(ops, init) {
738
+ this.ops = ops;
739
+ this.id = init.id;
740
+ this.sessionRef = init.sessionRef;
741
+ this.capture = init.capture ?? {};
742
+ }
743
+ ops;
744
+ id;
745
+ /** Pass back to `run({ session })` to reuse the same backend session. */
746
+ sessionRef;
747
+ capture;
748
+ /** Refresh and return the run's current state. */
749
+ async status() {
750
+ const detail = await this.ops.getDetail();
751
+ this.#adoptSessionRef(detail);
752
+ return normalizeRunStatus(this.id, detail);
753
+ }
754
+ /**
755
+ * Stream semantic events. Ends automatically at the run's terminal event.
756
+ * Reconnects with `Last-Event-ID` catch-up are handled internally.
757
+ */
758
+ async *events(opts = {}) {
759
+ for await (const wire of this.ops.streamEvents(opts)) {
760
+ await opts.onWireEvent?.(wire);
761
+ const event = normalizeRunEvent(wire, { topRunId: this.id });
762
+ if (!event) continue;
763
+ if (event.type === "screenshot" && !this.capture.screenshots) continue;
764
+ if (isVideoFrame(wire) && !this.capture.videoFrames) continue;
765
+ yield event;
766
+ if (event.isTerminal) return;
767
+ }
768
+ }
769
+ /** Drive the run to a terminal state and return the settled result. */
770
+ async wait(opts = {}) {
771
+ const { signal, timedOut } = withTimeout(opts.signal, opts.timeoutMs);
772
+ let lastEventId;
773
+ let eventIdAtLastClose;
774
+ let sawCleanClose = false;
775
+ let terminalPayload;
776
+ let terminalDoneReason;
777
+ let screenshotIndex = 0;
778
+ try {
779
+ settled: for (; ; ) {
780
+ for await (const event of this.events({
781
+ signal,
782
+ lastEventId,
783
+ onWireEvent: opts.onWireEvent,
784
+ onReconnect: opts.onReconnect
785
+ })) {
786
+ if (event.raw.id) lastEventId = event.raw.id;
787
+ await opts.onEvent?.(event);
788
+ if (event.type === "screenshot") {
789
+ await opts.onScreenshot?.(event.image, screenshotIndex++);
790
+ } else if (event.type === "interaction") {
791
+ await opts.onInteraction?.(this.interactionHandle(event.data), event);
792
+ }
793
+ if (event.isTerminal) {
794
+ terminalPayload = isJsonObject(event.raw.data) ? event.raw.data : void 0;
795
+ if (event.type === "done") terminalDoneReason = event.data.terminalReason || void 0;
796
+ break settled;
797
+ }
798
+ }
799
+ const detail2 = await this.ops.getDetail();
800
+ if (isTerminalRunStatus(detail2.status)) break;
801
+ if (sawCleanClose && lastEventId === eventIdAtLastClose) {
802
+ throw new QoniError(
803
+ `run event stream ended before a terminal event while the run is still "${String(detail2.status)}" \u2014 re-attach or poll status()`,
804
+ { code: "upstream.failed", retryable: true }
805
+ );
806
+ }
807
+ sawCleanClose = true;
808
+ eventIdAtLastClose = lastEventId;
809
+ }
810
+ } catch (err) {
811
+ if (timedOut() && !opts.signal?.aborted) {
812
+ throw new QoniTimeoutError(
813
+ `run.wait timed out after ${opts.timeoutMs} ms \u2014 the run keeps executing server-side; attach() to it again or cancel() it`,
814
+ { cause: err }
815
+ );
816
+ }
817
+ throw err;
818
+ }
819
+ let detail;
820
+ try {
821
+ detail = await this.ops.getDetail();
822
+ } catch (err) {
823
+ if (!terminalPayload) throw err;
824
+ detail = terminalPayload;
825
+ }
826
+ this.#adoptSessionRef(detail);
827
+ const artifacts = this.ops.listArtifacts ? await this.ops.listArtifacts() : [];
828
+ return settleRunResult(this.id, detail, artifacts, terminalDoneReason);
829
+ }
830
+ /**
831
+ * Wrap an {@link Interaction} (from `event.data` of an `interaction` event)
832
+ * in a typed {@link InteractionHandle} bound to this run. The handle's action
833
+ * methods (`answer` / `confirmSignedIn` / `releaseControl` / …) post to the
834
+ * endpoint the backend declared for that action.
835
+ */
836
+ interactionHandle(interaction) {
837
+ return new InteractionHandle(
838
+ interaction,
839
+ (action, body) => this.ops.postAction(action, body)
840
+ );
841
+ }
842
+ /**
843
+ * Cancel the run. Idempotent: cancelling an already-terminal run returns
844
+ * its terminal state instead of throwing.
845
+ */
846
+ async cancel(reason) {
847
+ try {
848
+ const data = await this.ops.cancel(reason);
849
+ this.#adoptSessionRef(data);
850
+ return normalizeRunStatus(this.id, data);
851
+ } catch (err) {
852
+ if (err instanceof QoniError && CANCEL_IDEMPOTENT_STATUSES.has(err.status ?? 0)) {
853
+ let snapshot;
854
+ try {
855
+ snapshot = await this.status();
856
+ } catch {
857
+ throw err;
858
+ }
859
+ if (isTerminalRunStatus(snapshot.status)) return snapshot;
860
+ }
861
+ throw err;
862
+ }
863
+ }
864
+ #adoptSessionRef(detail) {
865
+ const sessionId = typeof detail.session_id === "string" ? detail.session_id : void 0;
866
+ if (sessionId && !this.sessionRef) this.sessionRef = { sessionId };
867
+ }
868
+ };
869
+ function normalizeRunStatus(id, detail) {
870
+ return {
871
+ id,
872
+ status: typeof detail.status === "string" ? detail.status : inferStatus(detail),
873
+ sessionId: typeof detail.session_id === "string" ? detail.session_id : void 0,
874
+ output: runOutput(detail),
875
+ raw: detail
876
+ };
877
+ }
878
+ function settleRunResult(runId, detail, artifacts, fallbackTerminalReason) {
879
+ const terminalReason = (typeof detail.terminal_reason === "string" ? detail.terminal_reason : void 0) ?? fallbackTerminalReason;
880
+ const isTaskSuccessful = typeof detail.is_task_successful === "boolean" ? detail.is_task_successful : void 0;
881
+ let status;
882
+ if (isTerminalRunStatus(detail.status)) {
883
+ status = detail.status;
884
+ } else if (terminalReason === "canceled") {
885
+ status = "canceled";
886
+ } else if (terminalReason === "failed" || terminalReason === "expired" || isTaskSuccessful === false) {
887
+ status = "failed";
888
+ } else {
889
+ status = "succeeded";
890
+ }
891
+ return {
892
+ runId,
893
+ status,
894
+ output: runOutput(detail),
895
+ artifacts,
896
+ terminalReason,
897
+ isTaskSuccessful,
898
+ raw: detail
899
+ };
900
+ }
901
+ function runOutput(detail) {
902
+ return detail.output ?? detail.result ?? detail.results;
903
+ }
904
+ function inferStatus(detail) {
905
+ const reason = typeof detail.terminal_reason === "string" ? detail.terminal_reason : void 0;
906
+ if (reason === "canceled") return "canceled";
907
+ if (reason === "failed" || reason === "expired") return "failed";
908
+ if (reason === "done") return "succeeded";
909
+ return "";
910
+ }
911
+ function isVideoFrame(wire) {
912
+ const type = wire.event ?? (isJsonObject(wire.data) ? wire.data.type : void 0);
913
+ return type === "browser_video_frame" || type === "run.browser_video_frame";
914
+ }
915
+ function withTimeout(signal, timeoutMs) {
916
+ if (!timeoutMs) return { signal, timedOut: () => false };
917
+ const controller = new AbortController();
918
+ let fired = false;
919
+ const timer = setTimeout(() => {
920
+ fired = true;
921
+ controller.abort();
922
+ }, timeoutMs);
923
+ controller.signal.addEventListener("abort", () => clearTimeout(timer), { once: true });
924
+ if (signal) {
925
+ if (signal.aborted) controller.abort();
926
+ else signal.addEventListener("abort", () => controller.abort(), { once: true });
927
+ }
928
+ return { signal: controller.signal, timedOut: () => fired };
929
+ }
930
+
931
+ // src/deep-research.ts
932
+ function createDeepResearchNamespace(transport) {
933
+ const api = {
934
+ run: (input) => transport.webAgentJson("POST", "/deep_research/runs", input.token, {
935
+ body: normalizeDeepResearchRunInput(omit(input, "token")),
936
+ requiredScopes: ["webagent.deep_research:manage"]
937
+ }),
938
+ get: (input) => transport.webAgentJson(
939
+ "GET",
940
+ `/deep_research/runs/${encodeURIComponent(input.runId)}`,
941
+ input.token,
942
+ {
943
+ requiredScopes: ["webagent.deep_research:read"]
944
+ }
945
+ ),
946
+ events: (input) => transport.webAgentSSE(
947
+ `/deep_research/runs/${encodeURIComponent(input.runId)}/events`,
948
+ input.token,
949
+ {
950
+ lastEventId: input.lastEventId,
951
+ requiredScopes: ["webagent.deep_research:read"],
952
+ signal: input.signal,
953
+ onReconnect: input.onReconnect
954
+ }
955
+ ),
956
+ followUp: (input) => transport.webAgentJson(
957
+ "POST",
958
+ `/deep_research/runs/${encodeURIComponent(input.runId)}/messages`,
959
+ input.token,
960
+ {
961
+ body: omit(input, "token", "runId"),
962
+ requiredScopes: ["webagent.deep_research:manage"]
963
+ }
964
+ ),
965
+ cancel: (input) => transport.webAgentJson(
966
+ "POST",
967
+ `/deep_research/runs/${encodeURIComponent(input.runId)}/cancel`,
968
+ input.token,
969
+ {
970
+ body: {},
971
+ requiredScopes: ["webagent.deep_research:manage"]
972
+ }
973
+ ),
974
+ feedback: (input) => transport.webAgentJson(
975
+ "POST",
976
+ `/deep_research/runs/${encodeURIComponent(input.runId)}/feedback`,
977
+ input.token,
978
+ {
979
+ body: renameKeys(omit(input, "token", "runId"), { feedbackText: "feedback_text" }),
980
+ requiredScopes: ["webagent.deep_research:manage"]
981
+ }
982
+ ),
983
+ listArtifacts: (input) => transport.webAgentJson(
984
+ "GET",
985
+ `/deep_research/runs/${encodeURIComponent(input.runId)}/artifacts`,
986
+ input.token,
987
+ {
988
+ requiredScopes: ["webagent.deep_research:read"]
989
+ }
990
+ ),
991
+ getArtifact: (input) => transport.webAgentJson(
992
+ "GET",
993
+ `/deep_research/runs/${encodeURIComponent(input.runId)}/artifacts/${encodeURIComponent(input.artifactId)}`,
994
+ input.token,
995
+ {
996
+ requiredScopes: ["webagent.deep_research:read"]
997
+ }
998
+ )
999
+ };
1000
+ function buildOps(token, runId) {
1001
+ return {
1002
+ getDetail: async () => asRecord((await api.get({ token, runId })).data),
1003
+ streamEvents: (opts) => api.events({
1004
+ token,
1005
+ runId,
1006
+ lastEventId: opts.lastEventId,
1007
+ signal: opts.signal,
1008
+ onReconnect: opts.onReconnect
1009
+ }),
1010
+ postAction: async (action, body) => {
1011
+ await transport.webAgentJson(
1012
+ action.method || "POST",
1013
+ action.endpoint,
1014
+ token,
1015
+ {
1016
+ ...body !== void 0 ? { body } : {},
1017
+ requiredScopes: ["webagent.deep_research:manage"]
1018
+ }
1019
+ );
1020
+ },
1021
+ cancel: async () => asRecord((await api.cancel({ token, runId })).data),
1022
+ listArtifacts: async () => {
1023
+ const res = await api.listArtifacts({ token, runId });
1024
+ const items = Array.isArray(res.data?.items) ? res.data.items : [];
1025
+ return items.filter(isJsonObject).map((item) => toArtifact(item, token, runId));
1026
+ }
1027
+ };
1028
+ }
1029
+ function toArtifact(item, token, runId) {
1030
+ const id = typeof item.id === "string" ? item.id : "";
1031
+ return {
1032
+ id,
1033
+ name: typeof item.name === "string" && item.name ? item.name : void 0,
1034
+ mime: typeof item.mime_type === "string" ? item.mime_type : void 0,
1035
+ sizeBytes: typeof item.size_bytes === "number" ? item.size_bytes : void 0,
1036
+ createdAt: typeof item.created_at === "string" ? item.created_at : void 0,
1037
+ content: async () => (await transport.webAgentBytes(
1038
+ `/deep_research/runs/${encodeURIComponent(runId)}/artifacts/${encodeURIComponent(id)}`,
1039
+ token,
1040
+ { requiredScopes: ["webagent.deep_research:read"] }
1041
+ )).bytes
1042
+ };
1043
+ }
1044
+ return {
1045
+ /**
1046
+ * Start a research run. Pass `session: prior.sessionRef` to start a
1047
+ * follow-up run that inherits the prior run's context.
1048
+ */
1049
+ run: async (input) => {
1050
+ const { token, prompt, capture, limits, session, ...rest } = input;
1051
+ if (!prompt) {
1052
+ throw new QoniValidationError("deepResearch.run requires a prompt");
1053
+ }
1054
+ const run = await api.run({
1055
+ token,
1056
+ topic: prompt,
1057
+ ...rest,
1058
+ ...limits?.maxDurationMinutes !== void 0 ? { maxDurationMinutes: limits.maxDurationMinutes } : {},
1059
+ ...session ? { session_id: session.sessionId } : {}
1060
+ });
1061
+ const runId = run.data.run_id || run.data.id;
1062
+ if (!runId) {
1063
+ throw new QoniValidationError("deepResearch run create did not return a run id");
1064
+ }
1065
+ return new RunHandle(buildOps(token, runId), {
1066
+ id: runId,
1067
+ sessionRef: session,
1068
+ capture
1069
+ });
1070
+ },
1071
+ /** Reconnect to an existing run. Verifies the run exists before returning. */
1072
+ attach: async (runId, opts) => {
1073
+ const handle = new RunHandle(buildOps(opts.token, runId), {
1074
+ id: runId,
1075
+ capture: opts.capture
1076
+ });
1077
+ await handle.status();
1078
+ return handle;
1079
+ },
1080
+ api
1081
+ };
1082
+ }
1083
+ function normalizeDeepResearchRunInput(input) {
1084
+ return renameKeys(input, {
1085
+ outputFormat: "output_format",
1086
+ targetAudience: "target_audience",
1087
+ maxDurationMinutes: "max_duration_minutes",
1088
+ callbackUrl: "callback_url",
1089
+ domainWhitelist: "domain_whitelist",
1090
+ domainBlacklist: "domain_blacklist"
1091
+ });
1092
+ }
1093
+ function asRecord(value) {
1094
+ return isJsonObject(value) ? value : {};
1095
+ }
1096
+ function omit(value, ...keys) {
1097
+ const skipped = new Set(keys);
1098
+ const out = {};
1099
+ for (const [key, item] of Object.entries(value)) {
1100
+ if (!skipped.has(key) && item !== void 0) out[key] = item;
1101
+ }
1102
+ return out;
1103
+ }
1104
+ function renameKeys(value, mapping) {
1105
+ const out = {};
1106
+ for (const [key, item] of Object.entries(value)) {
1107
+ const target = mapping[key] || key;
1108
+ if (out[target] === void 0) out[target] = item;
1109
+ }
1110
+ return out;
1111
+ }
1112
+
1113
+ // src/do-anything.ts
1114
+ var PLATFORM_DECIDED_RUN_OPTIONS = [
1115
+ "model",
1116
+ "proxyCountryCode",
1117
+ "callbackUrl",
1118
+ "workspaceId",
1119
+ "outputSchema",
1120
+ "cacheScript"
1121
+ ];
1122
+ function createDoAnythingNamespace(transport) {
1123
+ const api = {
1124
+ createSession: (input) => transport.webAgentJson("POST", "/do_anything/sessions", input.token, {
1125
+ body: omit2(input, "token"),
1126
+ requiredScopes: ["webagent.do_anything:manage"]
1127
+ }),
1128
+ /**
1129
+ * `POST /do_anything/runs` — one call creates the run. Without
1130
+ * `session_id` a fresh session is created around the run; with it the
1131
+ * run is a follow-up in that session (409 while the prior run is
1132
+ * non-terminal).
1133
+ */
1134
+ createRun: (input) => transport.webAgentJson("POST", "/do_anything/runs", input.token, {
1135
+ body: normalizeDoAnythingRunInput(omit2(input, "token")),
1136
+ requiredScopes: ["webagent.do_anything:manage"]
1137
+ }),
1138
+ getRun: (input) => transport.webAgentJson(
1139
+ "GET",
1140
+ `/do_anything/runs/${encodeURIComponent(input.runId)}`,
1141
+ input.token,
1142
+ {
1143
+ requiredScopes: ["webagent.do_anything:read"]
1144
+ }
1145
+ ),
1146
+ events: async function* (input) {
1147
+ const path = `/do_anything/runs/${encodeURIComponent(input.runId)}/events`;
1148
+ const stream = transport.webAgentSSE(path, input.token, {
1149
+ lastEventId: input.lastEventId,
1150
+ requiredScopes: ["webagent.do_anything:read"],
1151
+ signal: input.signal,
1152
+ onReconnect: input.onReconnect,
1153
+ ...input.includeScreenshots === void 0 ? {} : { query: { include_screenshots: input.includeScreenshots } }
1154
+ });
1155
+ for await (const event of stream) {
1156
+ if (input.onlyTopLevel) {
1157
+ const taskId = envelopeTaskId(event);
1158
+ if (taskId && taskId !== input.runId) continue;
1159
+ }
1160
+ yield event;
1161
+ }
1162
+ },
1163
+ intervene: (input) => transport.webAgentJson(
1164
+ "POST",
1165
+ `/do_anything/runs/${encodeURIComponent(input.runId)}/intervene`,
1166
+ input.token,
1167
+ {
1168
+ body: normalizeDoAnythingInterveneInput(omit2(input, "token", "runId")),
1169
+ requiredScopes: ["webagent.do_anything:manage"]
1170
+ }
1171
+ ),
1172
+ cancel: (input) => transport.webAgentJson(
1173
+ "POST",
1174
+ `/do_anything/runs/${encodeURIComponent(input.runId)}/cancel`,
1175
+ input.token,
1176
+ {
1177
+ body: omit2(input, "token", "runId"),
1178
+ requiredScopes: ["webagent.do_anything:manage"]
1179
+ }
1180
+ ),
1181
+ readArtifacts: (input) => transport.webAgentJson(
1182
+ "GET",
1183
+ `/do_anything/sessions/${encodeURIComponent(input.sessionId)}/artifacts/${encodeURIComponent(input.artifactId)}`,
1184
+ input.token,
1185
+ {
1186
+ requiredScopes: ["webagent.do_anything:read"]
1187
+ }
1188
+ ),
1189
+ readRecording: (input) => transport.webAgentJson(
1190
+ "GET",
1191
+ `/do_anything/sessions/${encodeURIComponent(input.sessionId)}/recording`,
1192
+ input.token,
1193
+ {
1194
+ requiredScopes: ["webagent.do_anything:read"]
1195
+ }
1196
+ )
1197
+ };
1198
+ function buildOps(token, runId, capture) {
1199
+ return {
1200
+ getDetail: async () => asRecord2((await api.getRun({ token, runId })).data),
1201
+ streamEvents: (opts) => api.events({
1202
+ token,
1203
+ runId,
1204
+ lastEventId: opts.lastEventId,
1205
+ signal: opts.signal,
1206
+ onReconnect: opts.onReconnect,
1207
+ // Skip screenshot rows in backfill when the caller did not opt in.
1208
+ includeScreenshots: capture?.screenshots ? void 0 : false
1209
+ }),
1210
+ postAction: async (action, body) => {
1211
+ await transport.webAgentJson(
1212
+ action.method || "POST",
1213
+ action.endpoint,
1214
+ token,
1215
+ {
1216
+ ...body !== void 0 ? { body } : {},
1217
+ requiredScopes: ["webagent.do_anything:manage"]
1218
+ }
1219
+ );
1220
+ },
1221
+ cancel: async (reason) => asRecord2(
1222
+ (await api.cancel({
1223
+ token,
1224
+ runId,
1225
+ ...reason === void 0 ? {} : { reason }
1226
+ })).data
1227
+ )
1228
+ };
1229
+ }
1230
+ return {
1231
+ /** Start a run. Returns a handle — see `RunHandle` for the run lifecycle. */
1232
+ run: async (input) => {
1233
+ const { token, prompt, capture, limits, session, ...rest } = input;
1234
+ if (!prompt || typeof prompt !== "string") {
1235
+ throw new QoniValidationError("doAnything.run requires a prompt string");
1236
+ }
1237
+ const unsupported = PLATFORM_DECIDED_RUN_OPTIONS.filter(
1238
+ (key) => rest[key] !== void 0
1239
+ );
1240
+ if (unsupported.length > 0) {
1241
+ throw new QoniValidationError(
1242
+ `doAnything.run does not support ${unsupported.join(", ")} \u2014 the wire create body has no such field (the platform decides these server-side), so the value would be silently dropped. Remove it from the call.`
1243
+ );
1244
+ }
1245
+ const created = await api.createRun({
1246
+ token,
1247
+ instructions: prompt,
1248
+ ...rest,
1249
+ ...session?.sessionId ? { sessionId: session.sessionId } : {},
1250
+ ...limits?.maxDurationMinutes !== void 0 ? { maxDurationMinutes: limits.maxDurationMinutes } : {}
1251
+ });
1252
+ const runId = created.data.run_id || created.data.id;
1253
+ if (!runId) {
1254
+ throw new QoniValidationError("doAnything run create did not return a run id");
1255
+ }
1256
+ const sessionId = typeof created.data.session_id === "string" && created.data.session_id ? created.data.session_id : session?.sessionId;
1257
+ return new RunHandle(buildOps(token, runId, capture), {
1258
+ id: runId,
1259
+ sessionRef: sessionId ? { sessionId } : void 0,
1260
+ capture
1261
+ });
1262
+ },
1263
+ /**
1264
+ * Reconnect to an existing run by id. Verifies the run exists before
1265
+ * returning; `sessionRef` is adopted from the run detail envelope when
1266
+ * not supplied.
1267
+ */
1268
+ attach: async (runId, opts) => {
1269
+ const sessionId = opts.session?.sessionId;
1270
+ const handle = new RunHandle(buildOps(opts.token, runId, opts.capture), {
1271
+ id: runId,
1272
+ sessionRef: sessionId ? { sessionId } : void 0,
1273
+ capture: opts.capture
1274
+ });
1275
+ await handle.status();
1276
+ return handle;
1277
+ },
1278
+ api
1279
+ };
1280
+ }
1281
+ function normalizeDoAnythingRunInput(input) {
1282
+ const body = renameKeys2(input, {
1283
+ sessionId: "session_id",
1284
+ profileId: "profile_id",
1285
+ keepAlive: "keep_alive",
1286
+ allowedActions: "allowed_actions",
1287
+ maxDurationMinutes: "max_duration_minutes"
1288
+ });
1289
+ return body;
1290
+ }
1291
+ function normalizeDoAnythingInterveneInput(input) {
1292
+ const body = renameKeys2(input, { requestId: "input_request_id" });
1293
+ if (body.input_request_id !== void 0 && body.kind === void 0) {
1294
+ body.kind = body.response === void 0 ? "skip_input_request" : "answer_input_request";
1295
+ }
1296
+ return body;
1297
+ }
1298
+ function envelopeTaskId(event) {
1299
+ const env = event.data;
1300
+ return env && typeof env === "object" && typeof env.task_id === "string" ? env.task_id : void 0;
1301
+ }
1302
+ function asRecord2(value) {
1303
+ return isJsonObject(value) ? value : {};
1304
+ }
1305
+ function omit2(value, ...keys) {
1306
+ const skipped = new Set(keys);
1307
+ const out = {};
1308
+ for (const [key, item] of Object.entries(value)) {
1309
+ if (!skipped.has(key) && item !== void 0) out[key] = item;
1310
+ }
1311
+ return out;
1312
+ }
1313
+ function renameKeys2(value, mapping) {
1314
+ const out = {};
1315
+ for (const [key, item] of Object.entries(value)) {
1316
+ const target = mapping[key] || key;
1317
+ if (out[target] === void 0) out[target] = item;
1318
+ }
1319
+ return out;
1320
+ }
1321
+
1322
+ // src/qoni.ts
1323
+ function createQoniNamespace(_transport, delegateToken, completeDelegateToken) {
1324
+ return {
1325
+ delegateToken,
1326
+ /** @deprecated Use delegateToken. */
1327
+ delegateAgent: (input) => delegateToken(input),
1328
+ completeDelegateToken,
1329
+ /** @deprecated Use completeDelegateToken. */
1330
+ completeDelegateAgent: (input) => completeDelegateToken(input)
1331
+ };
1332
+ }
1333
+
1334
+ // src/genauth.ts
1335
+ function createGenAuthNamespace(transport, options = {}) {
1336
+ return {
1337
+ userInfo: (input) => transport.genauthJson("GET", "/oidc/me", input.accessToken),
1338
+ jwks: () => transport.genauthJson("GET", "/oidc/.well-known/jwks.json"),
1339
+ discovery: () => transport.genauthJson("GET", "/oidc/.well-known/openid-configuration"),
1340
+ introspectDelegationToken: (input) => transport.genauthJson("POST", "/api/v3/eak/delegations/introspect", void 0, {
1341
+ body: { token: requiredDelegationToken(input) }
1342
+ }),
1343
+ users: {
1344
+ list: async (input = {}) => {
1345
+ const auth = await adminAuthFor(options.adminTokenFor, input);
1346
+ return transport.genauthJson("POST", "/api/v3/list-users", auth.accessToken, {
1347
+ body: listUsersBody(input),
1348
+ headers: userPoolHeaders(auth.userPoolId)
1349
+ });
1350
+ },
1351
+ get: async (input) => {
1352
+ const auth = await adminAuthFor(options.adminTokenFor, input);
1353
+ return transport.genauthJson("GET", "/api/v3/get-user", auth.accessToken, {
1354
+ query: omit3(input, "adminToken", "userPoolId"),
1355
+ headers: userPoolHeaders(auth.userPoolId)
1356
+ });
1357
+ },
1358
+ getBatch: async (input) => {
1359
+ const auth = await adminAuthFor(options.adminTokenFor, input);
1360
+ return transport.genauthJson("GET", "/api/v3/get-user-batch", auth.accessToken, {
1361
+ query: omit3(input, "adminToken", "userPoolId"),
1362
+ headers: userPoolHeaders(auth.userPoolId)
1363
+ });
1364
+ },
1365
+ create: async (input) => {
1366
+ const auth = await adminAuthFor(options.adminTokenFor, input);
1367
+ return transport.genauthJson("POST", "/api/v3/create-user", auth.accessToken, {
1368
+ body: omit3(input, "adminToken", "userPoolId"),
1369
+ headers: userPoolHeaders(auth.userPoolId)
1370
+ });
1371
+ },
1372
+ createBatch: async (input) => {
1373
+ const auth = await adminAuthFor(options.adminTokenFor, input);
1374
+ return transport.genauthJson("POST", "/api/v3/create-users-batch", auth.accessToken, {
1375
+ body: createUsersBatchBody(input),
1376
+ headers: userPoolHeaders(auth.userPoolId)
1377
+ });
1378
+ },
1379
+ update: async (input) => {
1380
+ const auth = await adminAuthFor(options.adminTokenFor, input);
1381
+ return transport.genauthJson("POST", "/api/v3/update-user", auth.accessToken, {
1382
+ body: omit3(input, "adminToken", "userPoolId"),
1383
+ headers: userPoolHeaders(auth.userPoolId)
1384
+ });
1385
+ },
1386
+ deleteBatch: async (input) => {
1387
+ const auth = await adminAuthFor(options.adminTokenFor, input);
1388
+ return transport.genauthJson("POST", "/api/v3/delete-users-batch", auth.accessToken, {
1389
+ body: omit3(input, "adminToken", "userPoolId"),
1390
+ headers: userPoolHeaders(auth.userPoolId)
1391
+ });
1392
+ }
1393
+ }
1394
+ };
1395
+ }
1396
+ function requiredDelegationToken(input) {
1397
+ const token = input.token ?? input.delegateAgentToken;
1398
+ if (token?.trim()) return token;
1399
+ throw new Error("GenAuth delegation introspection requires token");
1400
+ }
1401
+ async function adminAuthFor(provider, input) {
1402
+ if (input.adminToken?.trim()) {
1403
+ if (!input.userPoolId?.trim()) {
1404
+ throw new Error("GenAuth adminToken override requires userPoolId");
1405
+ }
1406
+ return { accessToken: input.adminToken, userPoolId: input.userPoolId };
1407
+ }
1408
+ if (!provider) throw new Error("GenAuth user management requires Qoni AK/SK admin token exchange");
1409
+ return provider();
1410
+ }
1411
+ function userPoolHeaders(userPoolId) {
1412
+ return { "x-authing-userpool-id": userPoolId };
1413
+ }
1414
+ function omit3(value, ...keys) {
1415
+ const skipped = new Set(keys);
1416
+ const out = {};
1417
+ for (const [key, item] of Object.entries(value)) {
1418
+ if (!skipped.has(key) && item !== void 0) out[key] = item;
1419
+ }
1420
+ return out;
1421
+ }
1422
+ function listUsersBody(input) {
1423
+ const body = omit3(input, "adminToken", "userPoolId", "page", "limit");
1424
+ if (input.page === void 0 && input.limit === void 0) return body;
1425
+ const options = isJsonObject2(body.options) ? { ...body.options } : {};
1426
+ const pagination = isJsonObject2(options.pagination) ? { ...options.pagination } : {};
1427
+ if (input.page !== void 0) pagination.page = input.page;
1428
+ if (input.limit !== void 0) pagination.limit = input.limit;
1429
+ options.pagination = pagination;
1430
+ body.options = options;
1431
+ return body;
1432
+ }
1433
+ function createUsersBatchBody(input) {
1434
+ const body = omit3(input, "adminToken", "userPoolId", "users");
1435
+ if (input.users && !body.list) body.list = input.users;
1436
+ return body;
1437
+ }
1438
+ function isJsonObject2(value) {
1439
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1440
+ }
1441
+
1442
+ // src/gumem.ts
1443
+ function createGumemNamespace(transport) {
1444
+ return {
1445
+ createSession: (input) => transport.gumemJson("POST", "/api/sessions", input.token, {
1446
+ body: gumemBody(omit4(input, "token")),
1447
+ requiredScopes: ["gumem.memory:write"]
1448
+ }),
1449
+ addMessages: (input) => transport.gumemJson(
1450
+ "POST",
1451
+ `/api/sessions/${encodeURIComponent(input.sessionId)}/messages`,
1452
+ input.token,
1453
+ {
1454
+ body: gumemBody(omit4(input, "token", "sessionId")),
1455
+ requiredScopes: ["gumem.memory:write"]
1456
+ }
1457
+ ),
1458
+ recall: (input) => transport.gumemJson(
1459
+ "POST",
1460
+ `/api/sessions/${encodeURIComponent(input.sessionId || "default")}/context`,
1461
+ input.token,
1462
+ {
1463
+ query: { query: input.query || "", details: input.details ?? false },
1464
+ body: gumemBody(omit4(input, "token", "sessionId", "query", "details")),
1465
+ requiredScopes: ["gumem.memory:read"]
1466
+ }
1467
+ ),
1468
+ uploadResource: (input) => {
1469
+ const form = new FormData();
1470
+ form.set("file", input.file, input.filename);
1471
+ const userId = input.userId ?? input.user_id;
1472
+ const sessionId = input.sessionId ?? input.session_id;
1473
+ if (userId) form.set("user_id", userId);
1474
+ if (sessionId) form.set("session_id", sessionId);
1475
+ if (input.contentType) form.set("content_type", input.contentType);
1476
+ return transport.gumemJson("POST", "/api/resources", input.token, {
1477
+ body: form,
1478
+ requiredScopes: ["gumem.memory:write"]
1479
+ });
1480
+ },
1481
+ actions: {
1482
+ record: (input) => transport.gumemJson("POST", "/api/user/actions", input.token, {
1483
+ body: omit4(input, "token"),
1484
+ requiredScopes: ["gumem.action:write"]
1485
+ }),
1486
+ recall: (input) => transport.gumemJson("GET", "/api/user/actions/query", input.token, {
1487
+ query: omit4(input, "token"),
1488
+ requiredScopes: ["gumem.action:read"]
1489
+ }),
1490
+ stream: (input) => transport.gumemJson("GET", "/api/user/actions/stream", input.token, {
1491
+ query: omit4(input, "token"),
1492
+ requiredScopes: ["gumem.action:read"]
1493
+ })
1494
+ }
1495
+ };
1496
+ }
1497
+ function omit4(value, ...keys) {
1498
+ const skipped = new Set(keys);
1499
+ const out = {};
1500
+ for (const [key, item] of Object.entries(value)) {
1501
+ if (!skipped.has(key) && item !== void 0) out[key] = item;
1502
+ }
1503
+ return out;
1504
+ }
1505
+ function gumemBody(value) {
1506
+ return renameKeys3(value, {
1507
+ userId: "user_id",
1508
+ sessionId: "session_id",
1509
+ recallConfig: "recall_config",
1510
+ metadataFilters: "metadata_filters"
1511
+ });
1512
+ }
1513
+ function renameKeys3(value, mapping) {
1514
+ const out = {};
1515
+ for (const [key, item] of Object.entries(value)) {
1516
+ const target = mapping[key] || key;
1517
+ if (out[target] === void 0) out[target] = item;
1518
+ }
1519
+ return out;
1520
+ }
1521
+
1522
+ // src/track.ts
1523
+ var MonitorHandle = class {
1524
+ constructor(ops, id) {
1525
+ this.ops = ops;
1526
+ this.id = id;
1527
+ }
1528
+ ops;
1529
+ id;
1530
+ /** Refresh and return the monitor (normalized camelCase fields). */
1531
+ async get() {
1532
+ return camelizeRecord(await this.ops.get());
1533
+ }
1534
+ /**
1535
+ * Pause the monitor — stops scheduled ticks until {@link resume}. The
1536
+ * monitor stays addressable; this is the way to "stop monitoring" without
1537
+ * deleting.
1538
+ */
1539
+ async pause() {
1540
+ return camelizeRecord(await this.ops.update({ action: "pause" }));
1541
+ }
1542
+ /** Resume a paused monitor. */
1543
+ async resume() {
1544
+ return camelizeRecord(await this.ops.update({ action: "resume" }));
1545
+ }
1546
+ /**
1547
+ * Change the monitor definition. Pass any subset of fields (at least one);
1548
+ * `schedule` is `{ kind: "cron", expr }` or `{ kind: "interval", intervalSeconds }`.
1549
+ * The backend re-validates the trigger DSL against the schema, so a bad
1550
+ * change is rejected, not applied. The natural-language intent is not
1551
+ * refinable — create a new monitor to change it.
1552
+ */
1553
+ async refine(patch) {
1554
+ const { schedule, ...rest } = patch;
1555
+ const body = snakeifyKeys(rest);
1556
+ if (schedule) body.schedule = normalizeSchedule(schedule);
1557
+ if (Object.keys(body).length === 0) {
1558
+ throw new QoniValidationError(
1559
+ "track refine requires at least one field to change (schedule / targetUrls / extractionSchema / triggerDsl / stopConditionDsl / notifyChannel)"
1560
+ );
1561
+ }
1562
+ return camelizeRecord(await this.ops.update({ action: "refine", patch: body }));
1563
+ }
1564
+ /** Trigger an immediate tick outside the schedule. */
1565
+ async runNow() {
1566
+ return camelizeRecord(await this.ops.runNow());
1567
+ }
1568
+ /**
1569
+ * Stream semantic monitor events. Monitors are resident, so the stream has
1570
+ * no terminal event — it ends only when closed by the caller or the server.
1571
+ */
1572
+ async *events(opts = {}) {
1573
+ for await (const wire of this.ops.streamEvents(opts)) {
1574
+ const event = normalizeRunEvent(wire);
1575
+ if (event) yield event;
1576
+ }
1577
+ }
1578
+ /**
1579
+ * Wrap an {@link Interaction} (from `event.data` of an `interaction` event on
1580
+ * the monitor stream — e.g. a `site_login` to unlock a `needs_reauth` park)
1581
+ * in a typed {@link InteractionHandle}. The handle's action methods post to
1582
+ * the endpoint the backend declared for that action.
1583
+ */
1584
+ interactionHandle(interaction) {
1585
+ return new InteractionHandle(
1586
+ interaction,
1587
+ (action, body) => this.ops.postAction(action, body)
1588
+ );
1589
+ }
1590
+ /**
1591
+ * Read-only list of the runs the monitor's ticks produced. Tick runs are
1592
+ * owned by the scheduler — to stop monitoring use `pause()` / `delete()`.
1593
+ */
1594
+ async runs(opts = {}) {
1595
+ const data = await this.ops.listRuns(opts);
1596
+ const items = Array.isArray(data.items) ? data.items : Array.isArray(data.runs) ? data.runs : [];
1597
+ return items.filter(isJsonObject).map(normalizeMonitorRun);
1598
+ }
1599
+ /** Read-only detail of a single tick run. */
1600
+ async run(runId) {
1601
+ return normalizeMonitorRun(await this.ops.getRun(runId));
1602
+ }
1603
+ /** Delete the monitor. */
1604
+ async delete() {
1605
+ await this.ops.delete();
1606
+ }
1607
+ };
1608
+ function createTrackNamespace(transport) {
1609
+ const api = {
1610
+ createMonitor: (input) => transport.webAgentJson("POST", "/track/monitors", input.token, {
1611
+ body: omit5(input, "token"),
1612
+ requiredScopes: ["webagent.track:manage"]
1613
+ }),
1614
+ getMonitor: (input) => transport.webAgentJson(
1615
+ "GET",
1616
+ `/track/monitors/${encodeURIComponent(input.monitorId)}`,
1617
+ input.token,
1618
+ {
1619
+ requiredScopes: ["webagent.track:read"]
1620
+ }
1621
+ ),
1622
+ runNow: (input) => transport.webAgentJson(
1623
+ "POST",
1624
+ `/track/monitors/${encodeURIComponent(input.monitorId)}/run_now`,
1625
+ input.token,
1626
+ {
1627
+ body: {},
1628
+ requiredScopes: ["webagent.track:manage"]
1629
+ }
1630
+ ),
1631
+ events: (input) => transport.webAgentSSE(
1632
+ `/track/monitors/${encodeURIComponent(input.monitorId)}/events`,
1633
+ input.token,
1634
+ {
1635
+ lastEventId: input.lastEventId,
1636
+ requiredScopes: ["webagent.track:read"],
1637
+ signal: input.signal
1638
+ }
1639
+ ),
1640
+ intervene: (input) => transport.webAgentJson(
1641
+ "POST",
1642
+ `/track/monitors/${encodeURIComponent(input.monitorId)}/intervene`,
1643
+ input.token,
1644
+ {
1645
+ body: normalizeTrackInterveneInput(omit5(input, "token", "monitorId")),
1646
+ requiredScopes: ["webagent.track:manage"]
1647
+ }
1648
+ ),
1649
+ listRuns: (input) => transport.webAgentJson(
1650
+ "GET",
1651
+ `/track/monitors/${encodeURIComponent(input.monitorId)}/runs`,
1652
+ input.token,
1653
+ {
1654
+ query: omit5(input, "token", "monitorId"),
1655
+ requiredScopes: ["webagent.track:read"]
1656
+ }
1657
+ ),
1658
+ getRun: (input) => transport.webAgentJson(
1659
+ "GET",
1660
+ `/track/monitors/${encodeURIComponent(input.monitorId)}/runs/${encodeURIComponent(input.runId)}`,
1661
+ input.token,
1662
+ {
1663
+ requiredScopes: ["webagent.track:read"]
1664
+ }
1665
+ ),
1666
+ updateMonitor: (input) => transport.webAgentJson(
1667
+ "PATCH",
1668
+ `/track/monitors/${encodeURIComponent(input.monitorId)}`,
1669
+ input.token,
1670
+ {
1671
+ body: omit5(input, "token", "monitorId"),
1672
+ requiredScopes: ["webagent.track:manage"]
1673
+ }
1674
+ ),
1675
+ deleteMonitor: (input) => transport.webAgentJson(
1676
+ "DELETE",
1677
+ `/track/monitors/${encodeURIComponent(input.monitorId)}`,
1678
+ input.token,
1679
+ {
1680
+ // Backend DELETE /track/monitors/{id} requires track:manage
1681
+ // (see api/v1/track/monitors.py:delete_monitor).
1682
+ requiredScopes: ["webagent.track:manage"]
1683
+ }
1684
+ )
1685
+ };
1686
+ function buildOps(token, monitorId) {
1687
+ return {
1688
+ get: async () => asRecord3((await api.getMonitor({ token, monitorId })).data),
1689
+ update: async (input) => asRecord3((await api.updateMonitor({ token, monitorId, ...input })).data),
1690
+ runNow: async () => asRecord3((await api.runNow({ token, monitorId })).data),
1691
+ streamEvents: (opts) => api.events({ token, monitorId, lastEventId: opts.lastEventId, signal: opts.signal }),
1692
+ postAction: async (action, body) => {
1693
+ await transport.webAgentJson(
1694
+ action.method || "POST",
1695
+ action.endpoint,
1696
+ token,
1697
+ {
1698
+ ...body !== void 0 ? { body } : {},
1699
+ requiredScopes: ["webagent.track:manage"]
1700
+ }
1701
+ );
1702
+ },
1703
+ listRuns: async (opts) => asRecord3((await api.listRuns({ token, monitorId, ...opts })).data),
1704
+ getRun: async (runId) => asRecord3((await api.getRun({ token, monitorId, runId })).data),
1705
+ delete: async () => {
1706
+ await api.deleteMonitor({ token, monitorId });
1707
+ }
1708
+ };
1709
+ }
1710
+ return {
1711
+ /** Create a monitor. Returns a handle — see `MonitorHandle`. */
1712
+ create: async (input) => {
1713
+ const { token, prompt, ...rest } = input;
1714
+ if (!prompt || typeof prompt !== "string") {
1715
+ throw new QoniValidationError("track.create requires a prompt string");
1716
+ }
1717
+ const created = await api.createMonitor({
1718
+ token,
1719
+ intent: prompt,
1720
+ ...snakeifyKeys(rest)
1721
+ });
1722
+ const monitorId = created.data.id || created.data.monitor_id;
1723
+ if (!monitorId) {
1724
+ throw new QoniValidationError("track monitor create did not return a monitor id");
1725
+ }
1726
+ return new MonitorHandle(buildOps(token, monitorId), monitorId);
1727
+ },
1728
+ /** Reconnect to an existing monitor. Verifies it exists before returning. */
1729
+ attach: async (monitorId, opts) => {
1730
+ const handle = new MonitorHandle(buildOps(opts.token, monitorId), monitorId);
1731
+ await handle.get();
1732
+ return handle;
1733
+ },
1734
+ api
1735
+ };
1736
+ }
1737
+ function normalizeMonitorRun(item) {
1738
+ const normalized = camelizeRecord(item);
1739
+ if (typeof normalized.id !== "string" || !normalized.id) {
1740
+ const id = normalized.runId ?? normalized.snapshotId;
1741
+ if (typeof id === "string" && id) normalized.id = id;
1742
+ }
1743
+ return normalized;
1744
+ }
1745
+ function normalizeTrackInterveneInput(input) {
1746
+ return renameKeys4(input, { requestId: "request_id" });
1747
+ }
1748
+ function normalizeSchedule(schedule) {
1749
+ if (schedule.kind === "interval") {
1750
+ return { kind: "interval", interval_seconds: schedule.intervalSeconds };
1751
+ }
1752
+ return { kind: "cron", expr: schedule.expr };
1753
+ }
1754
+ function snakeifyKeys(value) {
1755
+ const out = {};
1756
+ for (const [key, item] of Object.entries(value)) {
1757
+ if (item === void 0) continue;
1758
+ out[key.replace(/([A-Z])/g, (_, ch) => `_${ch.toLowerCase()}`)] = item;
1759
+ }
1760
+ return out;
1761
+ }
1762
+ function asRecord3(value) {
1763
+ return isJsonObject(value) ? value : {};
1764
+ }
1765
+ function omit5(value, ...keys) {
1766
+ const skipped = new Set(keys);
1767
+ const out = {};
1768
+ for (const [key, item] of Object.entries(value)) {
1769
+ if (!skipped.has(key) && item !== void 0) out[key] = item;
1770
+ }
1771
+ return out;
1772
+ }
1773
+ function renameKeys4(value, mapping) {
1774
+ const out = {};
1775
+ for (const [key, item] of Object.entries(value)) {
1776
+ const target = mapping[key] || key;
1777
+ if (out[target] === void 0) out[target] = item;
1778
+ }
1779
+ return out;
1780
+ }
1781
+
1782
+ // src/web-search.ts
1783
+ function createWebSearchNamespace(transport) {
1784
+ const api = {
1785
+ run: (input) => transport.webAgentJson("POST", "/web_search/runs", input.token, {
1786
+ body: normalizeWebSearchRunInput(omit6(input, "token")),
1787
+ requiredScopes: ["webagent.web_search:manage"]
1788
+ }),
1789
+ get: (input) => transport.webAgentJson(
1790
+ "GET",
1791
+ `/web_search/runs/${encodeURIComponent(input.runId)}`,
1792
+ input.token,
1793
+ {
1794
+ requiredScopes: ["webagent.web_search:read"]
1795
+ }
1796
+ ),
1797
+ events: (input) => transport.webAgentSSE(
1798
+ `/web_search/runs/${encodeURIComponent(input.runId)}/events`,
1799
+ input.token,
1800
+ {
1801
+ lastEventId: input.lastEventId,
1802
+ requiredScopes: ["webagent.web_search:read"],
1803
+ signal: input.signal,
1804
+ onReconnect: input.onReconnect
1805
+ }
1806
+ ),
1807
+ cancel: (input) => transport.webAgentJson(
1808
+ "POST",
1809
+ `/web_search/runs/${encodeURIComponent(input.runId)}/cancel`,
1810
+ input.token,
1811
+ {
1812
+ body: omit6(input, "token", "runId"),
1813
+ requiredScopes: ["webagent.web_search:manage"]
1814
+ }
1815
+ )
1816
+ };
1817
+ function buildOps(token, runId) {
1818
+ return {
1819
+ getDetail: async () => asRecord4((await api.get({ token, runId })).data),
1820
+ streamEvents: (opts) => api.events({
1821
+ token,
1822
+ runId,
1823
+ lastEventId: opts.lastEventId,
1824
+ signal: opts.signal,
1825
+ onReconnect: opts.onReconnect
1826
+ }),
1827
+ postAction: async () => {
1828
+ throw new QoniError(
1829
+ "webSearch runs never request interaction \u2014 there is no action to invoke",
1830
+ { code: "validation.failed" }
1831
+ );
1832
+ },
1833
+ cancel: async (reason) => asRecord4(
1834
+ (await api.cancel({
1835
+ token,
1836
+ runId,
1837
+ ...reason === void 0 ? {} : { reason }
1838
+ })).data
1839
+ )
1840
+ };
1841
+ }
1842
+ return {
1843
+ /** Start a search run. Returns a handle — see `RunHandle` for the run lifecycle. */
1844
+ run: async (input) => {
1845
+ const { token, prompt, capture, limits, session, ...rest } = input;
1846
+ if (session) {
1847
+ throw new QoniValidationError(
1848
+ "webSearch runs do not execute in reusable sessions \u2014 drop the session option"
1849
+ );
1850
+ }
1851
+ if (limits) {
1852
+ throw new QoniValidationError(
1853
+ "webSearch does not support limits yet \u2014 drop the limits option"
1854
+ );
1855
+ }
1856
+ const resolvedQueries = Array.isArray(prompt) ? prompt : prompt ? [prompt] : void 0;
1857
+ if (!resolvedQueries || resolvedQueries.length === 0) {
1858
+ throw new QoniValidationError("webSearch.run requires a prompt");
1859
+ }
1860
+ const run = await api.run({
1861
+ token,
1862
+ queries: resolvedQueries,
1863
+ ...rest
1864
+ });
1865
+ const runId = run.data.run_id || run.data.id;
1866
+ if (!runId) {
1867
+ throw new QoniValidationError("webSearch run create did not return a run id");
1868
+ }
1869
+ return new RunHandle(buildOps(token, runId), { id: runId, capture });
1870
+ },
1871
+ /** Reconnect to an existing run. Verifies the run exists before returning. */
1872
+ attach: async (runId, opts) => {
1873
+ const handle = new RunHandle(buildOps(opts.token, runId), {
1874
+ id: runId,
1875
+ capture: opts.capture
1876
+ });
1877
+ await handle.status();
1878
+ return handle;
1879
+ },
1880
+ api
1881
+ };
1882
+ }
1883
+ function normalizeWebSearchRunInput(input) {
1884
+ const body = renameKeys5(input, {
1885
+ maxResultsPerQuery: "max_results_per_query",
1886
+ siteWhitelist: "site_whitelist",
1887
+ siteBlacklist: "site_blacklist"
1888
+ });
1889
+ return body;
1890
+ }
1891
+ function asRecord4(value) {
1892
+ return isJsonObject(value) ? value : {};
1893
+ }
1894
+ function omit6(value, ...keys) {
1895
+ const skipped = new Set(keys);
1896
+ const out = {};
1897
+ for (const [key, item] of Object.entries(value)) {
1898
+ if (!skipped.has(key) && item !== void 0) out[key] = item;
1899
+ }
1900
+ return out;
1901
+ }
1902
+ function renameKeys5(value, mapping) {
1903
+ const out = {};
1904
+ for (const [key, item] of Object.entries(value)) {
1905
+ const target = mapping[key] || key;
1906
+ if (out[target] === void 0) out[target] = item;
1907
+ }
1908
+ return out;
1909
+ }
1910
+
1911
+ // src/client.ts
1912
+ var DEFAULT_QONI_HOST = "https://anima.eazo.ai/dashboard";
1913
+ var QoniAgentKit = class {
1914
+ constructor(options) {
1915
+ this.options = options;
1916
+ const accessKey = options.accessKey ?? options.accessKeyId;
1917
+ const secretKey = options.secretKey ?? options.accessKeySecret;
1918
+ const host = options.host ?? options.qoniBaseUrl ?? options.genauthBaseUrl ?? DEFAULT_QONI_HOST;
1919
+ if (!accessKey || !secretKey) {
1920
+ throw new Error("Qoni requires accessKey and secretKey");
1921
+ }
1922
+ this.accessKey = accessKey ?? "";
1923
+ this.secretKey = secretKey ?? "";
1924
+ this.host = normalizeBaseUrl(host);
1925
+ this.fetchImpl = options.fetch ?? fetch;
1926
+ this.timeoutMs = options.timeoutMs ?? 3e4;
1927
+ this.sseMaxRetries = options.sseMaxRetries ?? 5;
1928
+ const transport = {
1929
+ qoniJson: (...args) => this.qoniJson(...args),
1930
+ genauthJson: (...args) => this.genauthJson(...args),
1931
+ gumemJson: (...args) => this.gumemJson(...args),
1932
+ gumemSSE: (...args) => this.gumemSSE(...args),
1933
+ webAgentJson: (...args) => this.webAgentJson(...args),
1934
+ webAgentBytes: (...args) => this.webAgentBytes(...args),
1935
+ webAgentSSE: (...args) => this.webAgentSSE(...args)
1936
+ };
1937
+ this.genauth = createGenAuthNamespace(transport, {
1938
+ adminTokenFor: () => this.genauthAdminTokenFor()
1939
+ });
1940
+ this.qoni = createQoniNamespace(
1941
+ transport,
1942
+ this.delegateToken.bind(this),
1943
+ (input) => this.completeDelegateToken(input)
1944
+ );
1945
+ this.gumem = createGumemNamespace(transport);
1946
+ this.webSearch = createWebSearchNamespace(transport);
1947
+ this.doAnything = createDoAnythingNamespace(transport);
1948
+ this.track = createTrackNamespace(transport);
1949
+ this.deepResearch = createDeepResearchNamespace(transport);
1950
+ }
1951
+ options;
1952
+ fetchImpl;
1953
+ timeoutMs;
1954
+ sseMaxRetries;
1955
+ accessKey;
1956
+ secretKey;
1957
+ host;
1958
+ runtimeConfigPromise;
1959
+ productTokenCache = /* @__PURE__ */ new Map();
1960
+ genAuthAdminTokenCache;
1961
+ gumem;
1962
+ genauth;
1963
+ qoni;
1964
+ webSearch;
1965
+ doAnything;
1966
+ track;
1967
+ deepResearch;
1968
+ delegateToken(input) {
1969
+ let body;
1970
+ try {
1971
+ body = normalizeDelegateTokenInput(input);
1972
+ } catch (err) {
1973
+ return Promise.reject(err);
1974
+ }
1975
+ return this.signedPost(
1976
+ "/api/v3/eak/delegations",
1977
+ body
1978
+ ).then(normalizeDelegateAgentTokenResponse);
1979
+ }
1980
+ /** @deprecated Use delegateToken. */
1981
+ delegateAgent(input) {
1982
+ return this.delegateToken(input);
1983
+ }
1984
+ completeDelegateToken(input) {
1985
+ return this.signedPost(
1986
+ "/api/v3/eak/delegations/complete",
1987
+ input
1988
+ ).then(
1989
+ normalizeDelegateAgentTokenResponse
1990
+ );
1991
+ }
1992
+ /** @deprecated Use completeDelegateToken. */
1993
+ completeDelegateAgent(input) {
1994
+ return this.completeDelegateToken(input);
1995
+ }
1996
+ currentUser(input) {
1997
+ return this.genauth.userInfo(input);
1998
+ }
1999
+ /**
2000
+ * Resolve a real GenAuth user id from the userpool bound to this AK/SK, by
2001
+ * listing users and returning the first one. Convenience for demos / smoke
2002
+ * tests so `delegateToken` has a valid `userId` without hardcoding one. In
2003
+ * application code, resolve the actual logged-in user via `currentUser`
2004
+ * instead of picking an arbitrary userpool member.
2005
+ */
2006
+ async resolveAnyBoundUser() {
2007
+ const res = await this.genauth.users.list({ page: 1, limit: 1 });
2008
+ const userId = firstBoundUserId(res.data);
2009
+ if (!userId) {
2010
+ throw new QoniError(
2011
+ "resolveAnyBoundUser: the credential-bound GenAuth userpool returned no users. Create a user in the userpool, or pass a real userId to delegateToken.",
2012
+ { code: "eak.genauth.no_bound_users" }
2013
+ );
2014
+ }
2015
+ return userId;
2016
+ }
2017
+ request(input) {
2018
+ return this.unstableRequest(input);
2019
+ }
2020
+ unstableRequest(input) {
2021
+ if (!input.token) {
2022
+ const pathname = normalizePath(input.path);
2023
+ const signaturePayload = input.method === "GET" ? input.query || {} : input.body || {};
2024
+ const signedHeaders = buildSignedHeaders(
2025
+ {
2026
+ accessKey: this.accessKey,
2027
+ secretKey: this.secretKey
2028
+ },
2029
+ input.method,
2030
+ pathname,
2031
+ signaturePayload
2032
+ );
2033
+ return this.baseUrlFor("qoni").then((baseUrl) => this.jsonRequest(baseUrl, pathname, {
2034
+ method: input.method,
2035
+ query: input.query,
2036
+ body: input.body,
2037
+ headers: {
2038
+ ...nonSignatureHeaders(input.headers),
2039
+ ...signedHeaders
2040
+ },
2041
+ service: "qoni",
2042
+ signed: true
2043
+ }));
2044
+ }
2045
+ return this.baseUrlFor("qoni").then((baseUrl) => this.jsonRequest(baseUrl, input.path, {
2046
+ method: input.method,
2047
+ token: input.token,
2048
+ query: input.query,
2049
+ body: input.body,
2050
+ headers: input.headers,
2051
+ service: "qoni"
2052
+ }));
2053
+ }
2054
+ qoniJson(method, path, token, payload = {}) {
2055
+ return this.baseUrlFor("qoni").then((baseUrl) => this.jsonRequest(baseUrl, path, {
2056
+ ...payload,
2057
+ method,
2058
+ token,
2059
+ service: "qoni"
2060
+ }));
2061
+ }
2062
+ genauthJson(method, path, token, payload = {}) {
2063
+ return this.baseUrlFor("genauth").then((baseUrl) => this.jsonRequest(baseUrl, path, {
2064
+ ...payload,
2065
+ method,
2066
+ token,
2067
+ service: "genauth"
2068
+ }));
2069
+ }
2070
+ async gumemJson(method, path, token, payload = {}) {
2071
+ const runtimeToken = await this.productTokenFor("gumem", token, payload.requiredScopes);
2072
+ const baseUrl = await this.baseUrlFor("gumem");
2073
+ return this.jsonRequest(baseUrl, path, {
2074
+ ...payload,
2075
+ method,
2076
+ token: runtimeToken,
2077
+ service: "gumem"
2078
+ });
2079
+ }
2080
+ async *gumemSSE(path, token, payload = {}) {
2081
+ const runtimeToken = await this.productTokenFor("gumem", token, payload.requiredScopes);
2082
+ const baseUrl = await this.baseUrlFor("gumem");
2083
+ for await (const event of this.sseRequest(baseUrl, path, {
2084
+ ...payload,
2085
+ token: runtimeToken,
2086
+ service: "gumem"
2087
+ })) {
2088
+ yield event;
2089
+ }
2090
+ }
2091
+ async webAgentJson(method, path, token, payload = {}) {
2092
+ const runtimeToken = await this.productTokenFor("webagent", token, payload.requiredScopes);
2093
+ const baseUrl = await this.baseUrlFor("webagent");
2094
+ return this.jsonRequest(
2095
+ baseUrl,
2096
+ this.webAgentPath(runtimeToken, path),
2097
+ { ...payload, method, token: runtimeToken, service: "webagent" }
2098
+ );
2099
+ }
2100
+ // Binary GET against the WebAgent API — artifact content downloads. JSON
2101
+ // error payloads still map to typed QoniErrors.
2102
+ async webAgentBytes(path, token, payload = {}) {
2103
+ const runtimeToken = await this.productTokenFor("webagent", token, payload.requiredScopes);
2104
+ const baseUrl = await this.baseUrlFor("webagent");
2105
+ const response = await this.fetchWithTimeout(
2106
+ urlWithBasePath(baseUrl, this.webAgentPath(runtimeToken, path), payload.query),
2107
+ {
2108
+ method: "GET",
2109
+ headers: {
2110
+ accept: "*/*",
2111
+ authorization: `Bearer ${runtimeToken}`,
2112
+ ...payload.headers || {}
2113
+ }
2114
+ }
2115
+ );
2116
+ if (!response.ok) {
2117
+ throw errorFromPayload(
2118
+ response.status,
2119
+ await readJson(response),
2120
+ `Qoni request failed with HTTP ${response.status}`
2121
+ );
2122
+ }
2123
+ const buffer = await response.arrayBuffer();
2124
+ return {
2125
+ bytes: new Uint8Array(buffer),
2126
+ mime: response.headers.get("content-type") || "application/octet-stream"
2127
+ };
2128
+ }
2129
+ async *webAgentSSE(path, token, payload = {}) {
2130
+ const runtimeToken = await this.productTokenFor("webagent", token, payload.requiredScopes);
2131
+ const baseUrl = await this.baseUrlFor("webagent");
2132
+ for await (const event of this.sseRequest(
2133
+ baseUrl,
2134
+ this.webAgentPath(runtimeToken, path),
2135
+ {
2136
+ ...payload,
2137
+ token: runtimeToken,
2138
+ service: "webagent",
2139
+ // Re-resolve the product token before each reconnect (refreshes if the
2140
+ // cached one expired); the tenant — and therefore the path — is stable.
2141
+ refreshToken: () => this.productTokenFor("webagent", token, payload.requiredScopes)
2142
+ }
2143
+ )) {
2144
+ yield normalizeWebAgentEvent(event);
2145
+ }
2146
+ }
2147
+ async signedPost(pathname, body) {
2148
+ const payload = toRecord(body);
2149
+ const headers = buildSignedHeaders(
2150
+ {
2151
+ accessKey: this.accessKey,
2152
+ secretKey: this.secretKey
2153
+ },
2154
+ "POST",
2155
+ pathname,
2156
+ payload
2157
+ );
2158
+ return this.jsonRequest(await this.baseUrlFor("qoni"), pathname, {
2159
+ method: "POST",
2160
+ body: payload,
2161
+ headers,
2162
+ service: "qoni",
2163
+ signed: true
2164
+ });
2165
+ }
2166
+ async jsonRequest(baseUrl, pathname, options) {
2167
+ const response = await this.fetchWithTimeout(
2168
+ urlWithBasePath(baseUrl, pathname, options.query),
2169
+ {
2170
+ method: options.method,
2171
+ headers: requestHeaders(options),
2172
+ body: requestBody(options.body)
2173
+ }
2174
+ );
2175
+ const payload = await readJson(response);
2176
+ if (!response.ok) {
2177
+ throw errorFromPayload(
2178
+ response.status,
2179
+ payload,
2180
+ `Qoni request failed with HTTP ${response.status}`
2181
+ );
2182
+ }
2183
+ const envelopeStatus = errorEnvelopeStatus(payload);
2184
+ if (envelopeStatus !== void 0) {
2185
+ throw errorFromPayload(
2186
+ envelopeStatus,
2187
+ payload,
2188
+ `Qoni request failed with statusCode ${envelopeStatus}`
2189
+ );
2190
+ }
2191
+ return normalizeResponse(payload, options.service);
2192
+ }
2193
+ // Reconnecting wrapper: yields events from a fresh connection, and on a
2194
+ // dropped stream (network/5xx, not a clean close or a user abort) reconnects
2195
+ // with `last-event-id` so the backend replays from where we left off. Each
2196
+ // attempt re-issues the GET with the same Bearer product token (no per-call
2197
+ // AK/SK signature, so no gateway nonce replay). Caps at `sseMaxRetries`.
2198
+ async *sseRequest(baseUrl, pathname, options) {
2199
+ let lastEventId = options.lastEventId;
2200
+ let attempt = 0;
2201
+ for (; ; ) {
2202
+ const token = options.refreshToken ? await options.refreshToken() : options.token;
2203
+ try {
2204
+ for await (const event of this.sseConnectOnce(baseUrl, pathname, {
2205
+ ...options,
2206
+ token,
2207
+ lastEventId
2208
+ })) {
2209
+ if (event.id) lastEventId = event.id;
2210
+ attempt = 0;
2211
+ yield event;
2212
+ }
2213
+ return;
2214
+ } catch (err) {
2215
+ if (options.signal?.aborted) throw err;
2216
+ const canRetry = isRetryableStreamError(err) || !!options.refreshToken && isAuthError(err);
2217
+ if (attempt >= this.sseMaxRetries || !canRetry) throw err;
2218
+ attempt++;
2219
+ options.onReconnect?.({ attempt, lastEventId, error: err });
2220
+ await sleepWithAbort(sseBackoffMs(attempt), options.signal);
2221
+ }
2222
+ }
2223
+ }
2224
+ async *sseConnectOnce(baseUrl, pathname, options) {
2225
+ const response = await this.fetchWithTimeout(
2226
+ urlWithBasePath(baseUrl, pathname, options.query),
2227
+ {
2228
+ method: "GET",
2229
+ headers: {
2230
+ accept: "text/event-stream",
2231
+ authorization: `Bearer ${options.token}`,
2232
+ ...options.headers || {},
2233
+ ...options.lastEventId ? { "last-event-id": options.lastEventId } : {}
2234
+ }
2235
+ },
2236
+ options.signal,
2237
+ { streamBody: true }
2238
+ );
2239
+ if (!response.ok) {
2240
+ throw errorFromPayload(
2241
+ response.status,
2242
+ await readJson(response),
2243
+ `Qoni stream failed with HTTP ${response.status}`
2244
+ );
2245
+ }
2246
+ if (!response.body) {
2247
+ throw errorFromPayload(502, void 0, "Qoni stream response has no body");
2248
+ }
2249
+ const reader = response.body.getReader();
2250
+ const decoder = new TextDecoder();
2251
+ let buffer = "";
2252
+ try {
2253
+ while (true) {
2254
+ if (options.signal?.aborted) {
2255
+ await reader.cancel().catch(() => void 0);
2256
+ throw options.signal.reason || new DOMException("Aborted", "AbortError");
2257
+ }
2258
+ const { done, value } = await reader.read();
2259
+ if (done) break;
2260
+ buffer += decoder.decode(value, { stream: true });
2261
+ const chunks = buffer.split(/\r?\n\r?\n/);
2262
+ buffer = chunks.pop() || "";
2263
+ for (const chunk of chunks) {
2264
+ const event = parseSSEEvent(chunk);
2265
+ if (event) yield event;
2266
+ }
2267
+ }
2268
+ const finalEvent = parseSSEEvent(buffer);
2269
+ if (finalEvent) yield finalEvent;
2270
+ } finally {
2271
+ reader.releaseLock();
2272
+ }
2273
+ }
2274
+ async fetchWithTimeout(input, init, signal, opts) {
2275
+ if (signal?.aborted) throw signal.reason || new DOMException("Aborted", "AbortError");
2276
+ const controller = new AbortController();
2277
+ const resolved = resolveLocalGenauthRequest(input);
2278
+ const requestInit = resolved.hostHeader ? {
2279
+ ...init,
2280
+ headers: withHostHeader(init.headers, resolved.hostHeader)
2281
+ } : init;
2282
+ let didTimeout = false;
2283
+ const abortFromSignal = () => controller.abort(signal?.reason);
2284
+ signal?.addEventListener("abort", abortFromSignal, { once: true });
2285
+ const timeout = setTimeout(() => {
2286
+ didTimeout = true;
2287
+ controller.abort();
2288
+ }, this.timeoutMs);
2289
+ try {
2290
+ return await this.fetchImpl(resolved.url, { ...requestInit, signal: controller.signal });
2291
+ } catch (error) {
2292
+ if (isAbortError(error) && didTimeout || isConnectTimeoutError(error)) {
2293
+ throw timeoutError(error);
2294
+ }
2295
+ if (isFetchNetworkError(error)) throw networkError(error);
2296
+ throw error;
2297
+ } finally {
2298
+ clearTimeout(timeout);
2299
+ if (!opts?.streamBody) signal?.removeEventListener("abort", abortFromSignal);
2300
+ }
2301
+ }
2302
+ async baseUrlFor(service) {
2303
+ const explicit = this.explicitBaseUrlFor(service);
2304
+ if (explicit) return explicit;
2305
+ const runtime = await this.runtimeConfig();
2306
+ if (service === "qoni") return this.host;
2307
+ const hostOrigin = originBaseUrl(this.host);
2308
+ if (service === "genauth") return runtime.genauthBaseUrl ?? hostOrigin;
2309
+ if (service === "gumem") return runtime.gumemBaseUrl ?? hostOrigin;
2310
+ return runtime.webAgentBaseUrl ?? hostOrigin;
2311
+ }
2312
+ explicitBaseUrlFor(service) {
2313
+ if (service === "qoni") {
2314
+ const value = this.options.qoniBaseUrl ?? this.options.genauthBaseUrl;
2315
+ return value ? normalizeBaseUrl(value) : void 0;
2316
+ }
2317
+ if (service === "genauth" && this.options.genauthBaseUrl) {
2318
+ return normalizeBaseUrl(this.options.genauthBaseUrl);
2319
+ }
2320
+ if (service === "genauth") {
2321
+ const value = serviceHostOption(this.options, "genauth");
2322
+ return value ? normalizeBaseUrl(value) : void 0;
2323
+ }
2324
+ if (service === "gumem" && this.options.gumemBaseUrl) {
2325
+ return normalizeBaseUrl(this.options.gumemBaseUrl);
2326
+ }
2327
+ if (service === "gumem") {
2328
+ const value = serviceHostOption(this.options, "gumem");
2329
+ return value ? normalizeBaseUrl(value) : void 0;
2330
+ }
2331
+ if (service === "webagent" && this.options.webAgentBaseUrl) {
2332
+ return normalizeBaseUrl(this.options.webAgentBaseUrl);
2333
+ }
2334
+ if (service === "webagent") {
2335
+ const value = serviceHostOption(this.options, "webagent");
2336
+ return value ? normalizeBaseUrl(value) : void 0;
2337
+ }
2338
+ return void 0;
2339
+ }
2340
+ runtimeConfig() {
2341
+ this.runtimeConfigPromise ??= this.fetchRuntimeConfig();
2342
+ return this.runtimeConfigPromise;
2343
+ }
2344
+ async fetchRuntimeConfig() {
2345
+ const pathname = "/api/v3/eak/runtime-config";
2346
+ const headers = buildSignedHeaders(
2347
+ {
2348
+ accessKey: this.accessKey,
2349
+ secretKey: this.secretKey
2350
+ },
2351
+ "GET",
2352
+ pathname,
2353
+ {}
2354
+ );
2355
+ const response = await this.fetchWithTimeout(
2356
+ urlWithBasePath(this.host, pathname),
2357
+ {
2358
+ method: "GET",
2359
+ headers: {
2360
+ accept: "application/json",
2361
+ ...headers
2362
+ }
2363
+ }
2364
+ );
2365
+ const payload = await readJson(response);
2366
+ if (!response.ok) {
2367
+ throw errorFromPayload(
2368
+ response.status,
2369
+ payload,
2370
+ `Qoni runtime config request failed with HTTP ${response.status}`
2371
+ );
2372
+ }
2373
+ return normalizeRuntimeConfig(payload);
2374
+ }
2375
+ webAgentPath(token, path) {
2376
+ const normalized = normalizePath(path);
2377
+ if (normalized.startsWith("/api/v1/projects/")) return normalized;
2378
+ const tenantId = requiredWebAgentTenantId(token);
2379
+ return `/api/v1/projects/${encodeURIComponent(tenantId)}${normalized}`;
2380
+ }
2381
+ async productTokenFor(service, token, requiredScopes) {
2382
+ const runtimeToken = requiredRuntimeToken(token);
2383
+ if (!isQoniDelegationToken(runtimeToken)) return runtimeToken;
2384
+ const scopes = [...requiredScopes || []].sort();
2385
+ const cacheKey = [service, runtimeToken, scopes.join(" ")].join(":");
2386
+ const cached = this.productTokenCache.get(cacheKey);
2387
+ if (cached && cached.expiresAt > Date.now() + 3e4) return cached.token;
2388
+ const response = await this.signedPost("/api/v3/eak/token-exchange", {
2389
+ subjectToken: runtimeToken,
2390
+ resource: service === "gumem" ? "gumem" : "webagent",
2391
+ scopes
2392
+ });
2393
+ const payload = response.data;
2394
+ const accessToken = readAccessToken(payload);
2395
+ if (!accessToken) {
2396
+ throw errorFromPayload(
2397
+ 502,
2398
+ payload,
2399
+ "Qoni product token exchange response did not include an access token"
2400
+ );
2401
+ }
2402
+ this.productTokenCache.set(cacheKey, {
2403
+ token: accessToken,
2404
+ expiresAt: Date.now() + Math.max(0, readExpiresIn(payload) ?? 3600) * 1e3
2405
+ });
2406
+ return accessToken;
2407
+ }
2408
+ async genauthAdminTokenFor() {
2409
+ const cached = this.genAuthAdminTokenCache;
2410
+ if (cached && cached.expiresAt > Date.now() + 3e4) {
2411
+ return { accessToken: cached.accessToken, userPoolId: cached.userPoolId };
2412
+ }
2413
+ const response = await this.signedPost("/api/v3/eak/genauth/admin-token", {});
2414
+ const payload = response.data;
2415
+ const accessToken = readAccessToken(payload);
2416
+ const userPoolId = readString(payload, "userPoolId") ?? readString(payload, "userpoolId") ?? readString(payload, "user_pool_id");
2417
+ if (!accessToken || !userPoolId) {
2418
+ throw errorFromPayload(
2419
+ 502,
2420
+ payload,
2421
+ "Qoni GenAuth admin-token response did not include accessToken and userPoolId"
2422
+ );
2423
+ }
2424
+ this.genAuthAdminTokenCache = {
2425
+ accessToken,
2426
+ userPoolId,
2427
+ expiresAt: Date.now() + Math.max(0, readExpiresIn(payload) ?? 3600) * 1e3
2428
+ };
2429
+ return { accessToken, userPoolId };
2430
+ }
2431
+ };
2432
+ function requestHeaders(options) {
2433
+ const headers = {
2434
+ accept: "application/json",
2435
+ ...options.headers || {}
2436
+ };
2437
+ if (options.token) headers.authorization = `Bearer ${options.token}`;
2438
+ if (options.body !== void 0 && !isBodyInit(options.body) && !headers["content-type"]) {
2439
+ headers["content-type"] = "application/json";
2440
+ }
2441
+ return headers;
2442
+ }
2443
+ function nonSignatureHeaders(headers) {
2444
+ return Object.fromEntries(
2445
+ Object.entries(headers || {}).filter(([key]) => {
2446
+ const normalized = key.toLowerCase();
2447
+ return normalized !== "authorization" && normalized !== "content-type" && normalized !== "date" && !normalized.startsWith("x-authing-");
2448
+ })
2449
+ );
2450
+ }
2451
+ function requestBody(body) {
2452
+ if (body === void 0) return void 0;
2453
+ if (isBodyInit(body)) return body;
2454
+ return JSON.stringify(body);
2455
+ }
2456
+ function isBodyInit(value) {
2457
+ return typeof value === "string" || value instanceof ArrayBuffer || value instanceof Blob || value instanceof FormData || value instanceof URLSearchParams;
2458
+ }
2459
+ async function readJson(response) {
2460
+ const contentType = response.headers.get("content-type")?.toLowerCase() || "";
2461
+ if (contentType.includes("ndjson") || contentType.includes("jsonl")) {
2462
+ const body = await response.text();
2463
+ if (!body.trim()) return [];
2464
+ try {
2465
+ return body.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line) => JSON.parse(line));
2466
+ } catch {
2467
+ return void 0;
2468
+ }
2469
+ }
2470
+ return response.json().catch(() => void 0);
2471
+ }
2472
+ function normalizeResponse(payload, _service) {
2473
+ if (isJsonObject(payload) && ("data" in payload || "meta" in payload)) {
2474
+ return {
2475
+ data: "data" in payload ? payload.data : payload,
2476
+ meta: {
2477
+ ...isJsonObject(payload.meta) ? payload.meta : {},
2478
+ ...topLevelMeta(payload)
2479
+ }
2480
+ };
2481
+ }
2482
+ return { data: payload, meta: {} };
2483
+ }
2484
+ function topLevelMeta(payload) {
2485
+ const meta = {};
2486
+ for (const key of ["requestId", "traceId", "auditId"]) {
2487
+ if (typeof payload[key] === "string") meta[key] = payload[key];
2488
+ }
2489
+ return meta;
2490
+ }
2491
+ function urlWithQuery(baseUrl, pathname, query) {
2492
+ const url = new URL(normalizePath(pathname), baseUrl);
2493
+ for (const [key, value] of Object.entries(query || {})) {
2494
+ if (value !== void 0 && value !== null) url.searchParams.set(key, String(value));
2495
+ }
2496
+ return url;
2497
+ }
2498
+ function urlWithBasePath(baseUrl, pathname, query) {
2499
+ const base = new URL(baseUrl);
2500
+ const basePath = base.pathname === "/" ? "" : base.pathname.replace(/\/+$/, "");
2501
+ const normalizedPath = normalizePath(pathname);
2502
+ const fullPath = basePath && (normalizedPath === basePath || normalizedPath.startsWith(`${basePath}/`)) ? normalizedPath : `${basePath}${normalizedPath}`;
2503
+ return urlWithQuery(base.origin, fullPath, query);
2504
+ }
2505
+ function resolveLocalGenauthRequest(input) {
2506
+ const url = new URL(input.toString());
2507
+ if (process.env.QONI_LOCAL_GENAUTH_REWRITE === "false" || url.protocol !== "http:" || !url.hostname.endsWith(".genauth.localhost")) {
2508
+ return { url };
2509
+ }
2510
+ const hostHeader = url.host;
2511
+ url.hostname = process.env.QONI_LOCAL_GENAUTH_TARGET_HOST || "127.0.0.1";
2512
+ return { url, hostHeader };
2513
+ }
2514
+ function withHostHeader(headers, hostHeader) {
2515
+ const out = {};
2516
+ new Headers(headers).forEach((value, key) => {
2517
+ out[key] = value;
2518
+ });
2519
+ out.Host = hostHeader;
2520
+ return out;
2521
+ }
2522
+ function normalizePath(path) {
2523
+ return path.startsWith("/") ? path : `/${path}`;
2524
+ }
2525
+ function normalizeBaseUrl(host) {
2526
+ const withProtocol = /^https?:\/\//i.test(host) ? host : `https://${host}`;
2527
+ return withProtocol.replace(/\/+$/, "");
2528
+ }
2529
+ function originBaseUrl(host) {
2530
+ return new URL(host).origin;
2531
+ }
2532
+ function serviceHostOption(options, service) {
2533
+ if (service === "genauth") {
2534
+ return options.genauthHost;
2535
+ }
2536
+ if (service === "gumem") {
2537
+ return options.gumemHost;
2538
+ }
2539
+ return options.webAgentHost;
2540
+ }
2541
+ function normalizeRuntimeConfig(payload) {
2542
+ const data = envelopeData(payload);
2543
+ if (!isJsonObject(data)) return {};
2544
+ const config = data;
2545
+ return {
2546
+ eakBaseUrl: runtimeServiceUrl(config, "eak", "eakBaseUrl"),
2547
+ genauthBaseUrl: runtimeServiceUrl(config, "genauth", "genauthBaseUrl"),
2548
+ gumemBaseUrl: runtimeServiceUrl(config, "gumem", "gumemBaseUrl"),
2549
+ webAgentBaseUrl: runtimeServiceUrl(config, "webAgent", "webAgentBaseUrl") ?? runtimeServiceUrl(config, "webagent", "webAgentBaseUrl")
2550
+ };
2551
+ }
2552
+ function envelopeData(payload) {
2553
+ if (isJsonObject(payload) && "data" in payload) return payload.data;
2554
+ return payload;
2555
+ }
2556
+ function runtimeServiceUrl(config, service, topLevelKey) {
2557
+ const topLevel = config[topLevelKey];
2558
+ if (typeof topLevel === "string" && topLevel.trim()) return normalizeBaseUrl(topLevel);
2559
+ const serviceValue = config.services?.[service];
2560
+ if (typeof serviceValue === "string" && serviceValue.trim()) {
2561
+ return normalizeBaseUrl(serviceValue);
2562
+ }
2563
+ if (isJsonObject(serviceValue)) {
2564
+ for (const key of ["baseUrl", "url", "host"]) {
2565
+ const value = serviceValue[key];
2566
+ if (typeof value === "string" && value.trim()) return normalizeBaseUrl(value);
2567
+ }
2568
+ }
2569
+ return void 0;
2570
+ }
2571
+ function toRecord(value) {
2572
+ const out = {};
2573
+ for (const [key, item] of Object.entries(value)) {
2574
+ if (item !== void 0) out[key] = item;
2575
+ }
2576
+ return out;
2577
+ }
2578
+ function normalizeDelegateTokenInput(input) {
2579
+ const body = toRecord(input);
2580
+ if (!body.userId && isJsonObject(input.user) && typeof input.user.id === "string") {
2581
+ body.userId = input.user.id;
2582
+ }
2583
+ if (input.mode !== "interactive" && !body.userId) {
2584
+ throw new QoniValidationError(
2585
+ 'delegateToken: silent mode requires a user \u2014 pass `user: { id: <genauthUserId> }` (or the deprecated top-level `userId`). Interactive mode (`mode: "interactive"`) does not need one.'
2586
+ );
2587
+ }
2588
+ body.agent = input.agent ?? "sdk";
2589
+ body.scopes = resolveDelegationScopes(input.scopes, input.products);
2590
+ delete body.products;
2591
+ return body;
2592
+ }
2593
+ function resolveDelegationScopes(scopes, products) {
2594
+ const resolved = /* @__PURE__ */ new Set();
2595
+ for (const product of products ?? []) {
2596
+ const expansion = QoniProductScopes[product];
2597
+ if (!expansion) {
2598
+ throw new QoniValidationError(
2599
+ `delegateToken: unknown product "${product}" \u2014 expected one of ${Object.keys(QoniProductScopes).join(", ")}`
2600
+ );
2601
+ }
2602
+ for (const scope of expansion) resolved.add(scope);
2603
+ }
2604
+ for (const scope of scopes ?? []) {
2605
+ validateScopeFormat(scope);
2606
+ resolved.add(scope);
2607
+ }
2608
+ if (resolved.size === 0) {
2609
+ throw new QoniValidationError(
2610
+ 'delegateToken requires at least one scope \u2014 pass scopes (e.g. ["webagent.do_anything:manage"]) and/or products (e.g. ["doAnything"])'
2611
+ );
2612
+ }
2613
+ return [...resolved];
2614
+ }
2615
+ function validateScopeFormat(scope) {
2616
+ if (KNOWN_SCOPES.includes(scope)) return;
2617
+ const prefixed = KNOWN_SCOPES.find((known) => known.endsWith(`.${scope}`) || known === `webagent.${scope}`);
2618
+ if (prefixed) {
2619
+ throw new QoniValidationError(
2620
+ `delegateToken: scope "${scope}" is missing its service prefix \u2014 write "${prefixed}"`
2621
+ );
2622
+ }
2623
+ if (!/^[a-z0-9_]+(\.[a-z0-9_]+)+:[a-z0-9_]+$/.test(scope)) {
2624
+ throw new QoniValidationError(
2625
+ `delegateToken: malformed scope "${scope}" \u2014 scopes look like "<service>.<resource>:<action>", e.g. "webagent.do_anything:manage"`
2626
+ );
2627
+ }
2628
+ }
2629
+ function errorEnvelopeStatus(payload) {
2630
+ if (!isJsonObject(payload)) return void 0;
2631
+ const statusCode = payload.statusCode;
2632
+ if (typeof statusCode !== "number" || statusCode < 400) return void 0;
2633
+ if ("data" in payload && payload.data !== void 0) return void 0;
2634
+ return statusCode;
2635
+ }
2636
+ function decodeJwtPayload(token) {
2637
+ const [, payload] = token.split(".");
2638
+ if (!payload) throw new Error("token is not a JWT");
2639
+ return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
2640
+ }
2641
+ function safeDecodeJwtPayload(token) {
2642
+ try {
2643
+ return decodeJwtPayload(token);
2644
+ } catch {
2645
+ return void 0;
2646
+ }
2647
+ }
2648
+ function isQoniDelegationToken(token) {
2649
+ return safeDecodeJwtPayload(token)?.token_type === "eak_delegation_token";
2650
+ }
2651
+ function readAccessToken(payload) {
2652
+ if (!isJsonObject(payload)) return void 0;
2653
+ for (const key of ["accessToken", "access_token", "token"]) {
2654
+ const token = payload[key];
2655
+ if (typeof token === "string" && token.trim()) return token;
2656
+ }
2657
+ return void 0;
2658
+ }
2659
+ function readExpiresIn(payload) {
2660
+ if (!isJsonObject(payload)) return void 0;
2661
+ const expiresIn = payload.expiresIn ?? payload.expires_in;
2662
+ if (typeof expiresIn === "number" && Number.isFinite(expiresIn)) return expiresIn;
2663
+ if (typeof expiresIn === "string" && expiresIn.trim()) {
2664
+ const parsed = Number(expiresIn);
2665
+ return Number.isFinite(parsed) ? parsed : void 0;
2666
+ }
2667
+ return void 0;
2668
+ }
2669
+ function readString(payload, key) {
2670
+ if (!isJsonObject(payload)) return void 0;
2671
+ const value = payload[key];
2672
+ return typeof value === "string" && value.trim() ? value : void 0;
2673
+ }
2674
+ function requiredWebAgentTenantId(token) {
2675
+ const payload = decodeJwtPayload(token);
2676
+ const productResource = payload.product_resource;
2677
+ if (isJsonObject(productResource) && productResource.type === "webagent_tenant") {
2678
+ const id = productResource.id;
2679
+ if (typeof id === "string" && id.trim()) return id;
2680
+ }
2681
+ const resourceBindingTenantId = nestedString(payload, [
2682
+ "resource_bindings",
2683
+ "webagent",
2684
+ "tenant_id"
2685
+ ]);
2686
+ if (resourceBindingTenantId) return resourceBindingTenantId;
2687
+ const tenantId = payload.webagent_tenant_id;
2688
+ if (typeof tenantId === "string" && tenantId.trim()) return tenantId;
2689
+ const projectId = payload.webagent_project_id;
2690
+ if (typeof projectId === "string" && projectId.trim()) return projectId;
2691
+ throw new Error("token is missing WebAgent tenant binding");
2692
+ }
2693
+ function nestedString(value, path) {
2694
+ let cursor = value;
2695
+ for (const segment of path) {
2696
+ if (!isJsonObject(cursor)) return void 0;
2697
+ cursor = cursor[segment];
2698
+ }
2699
+ return typeof cursor === "string" && cursor.trim() ? cursor : void 0;
2700
+ }
2701
+ function normalizeWebAgentEvent(event) {
2702
+ if (event.event) return event;
2703
+ const data = event.data;
2704
+ if (data && typeof data === "object" && typeof data.type === "string") {
2705
+ return { ...event, event: data.type };
2706
+ }
2707
+ return event;
2708
+ }
2709
+ function isRetryableStreamError(err) {
2710
+ if (err && typeof err === "object") {
2711
+ const e = err;
2712
+ if (e.name === "AbortError") return false;
2713
+ if (typeof e.retryable === "boolean") return e.retryable;
2714
+ if (typeof e.status === "number") {
2715
+ return e.status >= 500 || e.status === 408 || e.status === 429;
2716
+ }
2717
+ }
2718
+ return true;
2719
+ }
2720
+ function isAuthError(err) {
2721
+ const status = err?.status;
2722
+ return status === 401 || status === 403;
2723
+ }
2724
+ function sseBackoffMs(attempt) {
2725
+ const base = Math.min(500 * 2 ** (attempt - 1), 1e4);
2726
+ return base + Math.floor(Math.random() * 250);
2727
+ }
2728
+ function sleepWithAbort(ms, signal) {
2729
+ return new Promise((resolve, reject) => {
2730
+ if (signal?.aborted) {
2731
+ reject(new DOMException("The operation was aborted", "AbortError"));
2732
+ return;
2733
+ }
2734
+ const timer = setTimeout(() => {
2735
+ signal?.removeEventListener("abort", onAbort);
2736
+ resolve();
2737
+ }, ms);
2738
+ const onAbort = () => {
2739
+ clearTimeout(timer);
2740
+ reject(new DOMException("The operation was aborted", "AbortError"));
2741
+ };
2742
+ signal?.addEventListener("abort", onAbort, { once: true });
2743
+ });
2744
+ }
2745
+ function firstBoundUserId(data) {
2746
+ const arrays = [
2747
+ data,
2748
+ data?.list,
2749
+ data?.users,
2750
+ data?.data,
2751
+ data?.items
2752
+ ];
2753
+ for (const candidate of arrays) {
2754
+ if (Array.isArray(candidate) && candidate.length) {
2755
+ const u = candidate[0];
2756
+ for (const key of ["userId", "id", "user_id", "sub", "username"]) {
2757
+ const v = u?.[key];
2758
+ if (typeof v === "string" && v) return v;
2759
+ }
2760
+ }
2761
+ }
2762
+ return void 0;
2763
+ }
2764
+ function parseSSEEvent(chunk) {
2765
+ const lines = chunk.split(/\r?\n/);
2766
+ let id;
2767
+ let event;
2768
+ const data = [];
2769
+ for (const line of lines) {
2770
+ if (!line || line.startsWith(":")) continue;
2771
+ const index = line.indexOf(":");
2772
+ const field = index >= 0 ? line.slice(0, index) : line;
2773
+ const value = index >= 0 ? line.slice(index + 1).replace(/^ /, "") : "";
2774
+ if (field === "id") id = value;
2775
+ if (field === "event") event = value;
2776
+ if (field === "data") data.push(value);
2777
+ }
2778
+ if (!id && !event && !data.length) return null;
2779
+ return { id, event, data: parseEventData(data.join("\n")) };
2780
+ }
2781
+ function parseEventData(raw) {
2782
+ if (!raw) return void 0;
2783
+ try {
2784
+ return JSON.parse(raw);
2785
+ } catch {
2786
+ return raw;
2787
+ }
2788
+ }
2789
+ function requiredRuntimeToken(token) {
2790
+ if (token && token.trim()) return token;
2791
+ throw new QoniDelegationRequiredError(
2792
+ "Product capability calls require an explicit token in the token field"
2793
+ );
2794
+ }
2795
+ function normalizeDelegateAgentTokenResponse(response) {
2796
+ if (!isJsonObject(response.data)) return response;
2797
+ const grantState = typeof response.data.grantState === "string" ? response.data.grantState : typeof response.data.state === "string" ? response.data.state : typeof response.data.grantId === "string" ? response.data.grantId : void 0;
2798
+ if (typeof response.data.authorizationUrl === "string" && grantState) {
2799
+ return {
2800
+ ...response,
2801
+ data: {
2802
+ ...response.data,
2803
+ grantState,
2804
+ state: grantState
2805
+ }
2806
+ };
2807
+ }
2808
+ const token = typeof response.data.delegateAgentToken === "string" ? response.data.delegateAgentToken : typeof response.data.delegationToken === "string" ? response.data.delegationToken : typeof response.data.token === "string" ? response.data.token : void 0;
2809
+ if (!token) return response;
2810
+ return {
2811
+ ...response,
2812
+ data: {
2813
+ ...response.data,
2814
+ token,
2815
+ delegateAgentToken: token
2816
+ }
2817
+ };
2818
+ }
2819
+ function isAbortError(error) {
2820
+ return error instanceof DOMException && error.name === "AbortError";
2821
+ }
2822
+ function isConnectTimeoutError(error) {
2823
+ const cause = errorCause(error);
2824
+ return errorCode(cause) === "UND_ERR_CONNECT_TIMEOUT" || errorName(cause) === "ConnectTimeoutError";
2825
+ }
2826
+ function isFetchNetworkError(error) {
2827
+ if (!(error instanceof TypeError)) return false;
2828
+ const message = error.message.toLowerCase();
2829
+ return message.includes("fetch failed") || message.includes("network");
2830
+ }
2831
+ function errorCause(error) {
2832
+ return error && typeof error === "object" ? error.cause : void 0;
2833
+ }
2834
+ function errorCode(error) {
2835
+ return error && typeof error === "object" && typeof error.code === "string" ? error.code : void 0;
2836
+ }
2837
+ function errorName(error) {
2838
+ return error && typeof error === "object" && typeof error.name === "string" ? error.name : void 0;
2839
+ }
2840
+ export {
2841
+ ActionKinds,
2842
+ InteractionHandle,
2843
+ InteractionStatuses,
2844
+ InteractionTypes,
2845
+ MonitorHandle,
2846
+ QoniAgentKit as Qoni,
2847
+ QoniAgentKit,
2848
+ QoniAuthError,
2849
+ QoniDelegationRequiredError,
2850
+ QoniError,
2851
+ QoniEventTypes,
2852
+ QoniPermissionDeniedError,
2853
+ QoniProductScopes,
2854
+ QoniRateLimitError,
2855
+ QoniScopeBundles,
2856
+ QoniScopes,
2857
+ QoniTimeoutError,
2858
+ QoniTokenExpiredError,
2859
+ QoniUpstreamError,
2860
+ QoniValidationError,
2861
+ RunHandle,
2862
+ buildAuthorization,
2863
+ buildSignature,
2864
+ buildStringToSign
2865
+ };
2866
+ //# sourceMappingURL=index.js.map