@channlize/core 0.4.0 → 0.6.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.mjs CHANGED
@@ -23,7 +23,8 @@ function readChannlizeEnv(env = typeof process !== "undefined" ? process.env : v
23
23
  apiKey: clean(env?.CHANNLIZE_API_KEY),
24
24
  projectId: clean(env?.CHANNLIZE_PROJECT_ID),
25
25
  baseUrl: clean(env?.CHANNLIZE_BASE_URL),
26
- timeout: clean(env?.CHANNLIZE_TIMEOUT)
26
+ timeout: clean(env?.CHANNLIZE_TIMEOUT),
27
+ agentId: clean(env?.CHANNLIZE_AGENT_ID)
27
28
  };
28
29
  }
29
30
  function parseApiError(status, body, statusText = "") {
@@ -59,6 +60,12 @@ var ChannlizeError = class extends Error {
59
60
  * `upgradeUrl` on a plan-limit 429. Undefined for network errors.
60
61
  */
61
62
  details;
63
+ /**
64
+ * ISO time the call may succeed again: the body's `retryAt` (agent
65
+ * policies, human takeovers), else derived from a `Retry-After` header.
66
+ * See `retryAfter(error)`.
67
+ */
68
+ retryAt;
62
69
  };
63
70
  var ValidationError = class extends ChannlizeError {
64
71
  constructor(message, field) {
@@ -81,11 +88,121 @@ var RateLimitError = class extends ChannlizeError {
81
88
  }
82
89
  };
83
90
 
91
+ // src/agents-errors.ts
92
+ var AGENT_POLICY_CODES = [
93
+ "POLICY_CHANNEL_NOT_ALLOWED",
94
+ "POLICY_DM_NOT_ALLOWED",
95
+ "POLICY_ASK_HUMAN_NOT_ALLOWED",
96
+ "POLICY_URGENCY_NOT_ALLOWED",
97
+ "POLICY_RATE_LIMITED",
98
+ "POLICY_BUDGET_EXHAUSTED",
99
+ "POLICY_OUTSIDE_WORKING_HOURS",
100
+ "POLICY_MAX_DEPTH"
101
+ ];
102
+ var AGENT_TASK_ERROR_CODES = [
103
+ "TASK_CLOSED",
104
+ "ALREADY_ASSIGNED",
105
+ "AGENT_NOT_IN_CHANNEL",
106
+ "NOT_TASK_PARTICIPANT"
107
+ ];
108
+ var AgentPolicyError = class extends ChannlizeError {
109
+ constructor(message, statusCode, code, retryAt) {
110
+ super(message, statusCode, code);
111
+ this.name = "AgentPolicyError";
112
+ this.retryAt = retryAt;
113
+ }
114
+ };
115
+ var HumanTakeoverError = class extends ChannlizeError {
116
+ constructor(message, retryAt) {
117
+ super(message, 409, "HUMAN_TAKEOVER");
118
+ this.name = "HumanTakeoverError";
119
+ this.retryAt = retryAt;
120
+ }
121
+ };
122
+ var AgentThreadPausedError = class extends RateLimitError {
123
+ constructor(message, reason = "thread_cap") {
124
+ super(message, "AGENT_THREAD_PAUSED");
125
+ this.reason = reason;
126
+ this.name = "AgentThreadPausedError";
127
+ }
128
+ reason;
129
+ };
130
+ var AgentTaskError = class extends ChannlizeError {
131
+ constructor(message, statusCode, code) {
132
+ super(message, statusCode, code);
133
+ this.name = "AgentTaskError";
134
+ }
135
+ };
136
+ var QuestionClosedError = class extends ChannlizeError {
137
+ constructor(message) {
138
+ super(message, 409, "QUESTION_CLOSED");
139
+ this.name = "QuestionClosedError";
140
+ }
141
+ };
142
+ function agentErrorFor(status, message, code, body, retryAt) {
143
+ if (!code) return void 0;
144
+ if (code.startsWith("POLICY_")) {
145
+ return new AgentPolicyError(message, status, code, retryAt);
146
+ }
147
+ switch (code) {
148
+ case "HUMAN_TAKEOVER":
149
+ return new HumanTakeoverError(message, retryAt);
150
+ case "AGENT_THREAD_PAUSED":
151
+ return new AgentThreadPausedError(
152
+ message,
153
+ typeof body.reason === "string" ? body.reason : void 0
154
+ );
155
+ case "AGENT_RATE_LIMITED":
156
+ return new RateLimitError(message, code);
157
+ case "QUESTION_CLOSED":
158
+ return new QuestionClosedError(message);
159
+ default:
160
+ return AGENT_TASK_ERROR_CODES.includes(code) ? new AgentTaskError(message, status, code) : void 0;
161
+ }
162
+ }
163
+ function isRetryable(error) {
164
+ if (!(error instanceof ChannlizeError)) return false;
165
+ const { statusCode: status, code } = error;
166
+ if (code === "NETWORK_ERROR" || code === "TIMEOUT") return true;
167
+ if (status === void 0) return false;
168
+ if (status >= 500) return true;
169
+ if (error instanceof AgentPolicyError || error instanceof HumanTakeoverError) {
170
+ return Boolean(error.retryAt);
171
+ }
172
+ if (status === 429) return code !== "PLAN_LIMIT_REACHED";
173
+ return false;
174
+ }
175
+ function retryAfter(error, now = Date.now()) {
176
+ if (!isRetryable(error)) return void 0;
177
+ const at = error.retryAt;
178
+ const ms = at ? Date.parse(at) : NaN;
179
+ return Number.isFinite(ms) ? Math.max(0, ms - now) : void 0;
180
+ }
181
+
84
182
  // src/http.ts
85
183
  async function requestJson(http, endpoint, options = {}) {
184
+ return send(
185
+ http,
186
+ endpoint,
187
+ options,
188
+ async (res) => await res.json()
189
+ );
190
+ }
191
+ async function requestText(http, endpoint, options = {}) {
192
+ return send(http, endpoint, options, async (res) => ({
193
+ body: await res.text(),
194
+ status: res.status,
195
+ headers: res.headers
196
+ }));
197
+ }
198
+ async function send(http, endpoint, options, read) {
86
199
  const url = `${http.baseUrl}${endpoint}`;
87
200
  const controller = new AbortController();
88
201
  const timeoutId = setTimeout(() => controller.abort(), http.timeout);
202
+ const external = options.signal ?? void 0;
203
+ const onAbort = () => controller.abort();
204
+ if (external?.aborted) controller.abort();
205
+ else external?.addEventListener("abort", onAbort, { once: true });
89
206
  try {
90
207
  const mergedHeaders = {
91
208
  "Content-Type": "application/json",
@@ -103,21 +220,33 @@ async function requestJson(http, endpoint, options = {}) {
103
220
  if (!response.ok) {
104
221
  await handleErrorResponse(response);
105
222
  }
106
- const data = await response.json();
107
- return data;
223
+ return await read(response);
108
224
  } catch (error) {
109
225
  clearTimeout(timeoutId);
110
226
  if (error instanceof ChannlizeError) {
111
227
  throw error;
112
228
  }
229
+ if (external?.aborted) {
230
+ throw new ChannlizeError("Request aborted", void 0, "ABORTED");
231
+ }
113
232
  if (error instanceof Error && error.name === "AbortError") {
114
233
  throw new ChannlizeError("Request timeout", 408, "TIMEOUT");
115
234
  }
116
235
  throw new ChannlizeError(
117
- `Network error: ${error instanceof Error ? error.message : "Unknown error"}`
236
+ `Network error: ${error instanceof Error ? error.message : "Unknown error"}`,
237
+ void 0,
238
+ "NETWORK_ERROR"
118
239
  );
240
+ } finally {
241
+ external?.removeEventListener("abort", onAbort);
119
242
  }
120
243
  }
244
+ function retryAfterHeader(value) {
245
+ if (!value) return void 0;
246
+ const seconds = Number(value);
247
+ const ms = Number.isFinite(seconds) ? Date.now() + seconds * 1e3 : Date.parse(value);
248
+ return Number.isFinite(ms) ? new Date(ms).toISOString() : void 0;
249
+ }
121
250
  async function handleErrorResponse(response) {
122
251
  let body = {};
123
252
  if (response.headers.get("content-type")?.includes("application/json")) {
@@ -126,11 +255,27 @@ async function handleErrorResponse(response) {
126
255
  } catch {
127
256
  }
128
257
  }
129
- throw errorFromApi(parseApiError(response.status, body, response.statusText));
258
+ throw errorFromApi(
259
+ parseApiError(response.status, body, response.statusText),
260
+ retryAfterHeader(response.headers.get("retry-after"))
261
+ );
130
262
  }
131
- function errorFromApi(info) {
263
+ function errorFromApi(info, retryAfter2) {
132
264
  const { status, message, code } = info;
133
- let error;
265
+ const retryAt = typeof info.body.retryAt === "string" ? info.body.retryAt : retryAfter2;
266
+ let error = agentErrorFor(
267
+ status,
268
+ message,
269
+ code,
270
+ info.body,
271
+ retryAt
272
+ );
273
+ if (error) {
274
+ error.requestId = info.requestId;
275
+ error.details = info.body;
276
+ error.retryAt ??= retryAt;
277
+ return error;
278
+ }
134
279
  switch (status) {
135
280
  case 401:
136
281
  error = new AuthenticationError(message);
@@ -152,6 +297,7 @@ function errorFromApi(info) {
152
297
  }
153
298
  error.requestId = info.requestId;
154
299
  error.details = info.body;
300
+ error.retryAt = retryAt;
155
301
  return error;
156
302
  }
157
303
 
@@ -225,6 +371,1091 @@ function createNotifier(config = {}) {
225
371
  };
226
372
  }
227
373
 
374
+ // src/agents-governance-types.ts
375
+ var QUESTION_KINDS = ["question", "approval"];
376
+ var QUESTION_URGENCIES = ["low", "normal", "high", "urgent"];
377
+ var QUESTION_STATUSES = [
378
+ "open",
379
+ "answered",
380
+ "expired_default",
381
+ "expired",
382
+ "canceled"
383
+ ];
384
+ var QUESTION_WAIT_MAX_SECONDS = 25;
385
+ var TASK_BOARD_STATUSES = [
386
+ "open",
387
+ "in_progress",
388
+ "blocked",
389
+ "done",
390
+ "failed"
391
+ ];
392
+ var POLICY_SCOPES = ["org", "team", "agent", "channel"];
393
+
394
+ // src/agents-governance.ts
395
+ var enc = encodeURIComponent;
396
+ var iso = (v) => v instanceof Date ? v.toISOString() : v;
397
+ function sleep(ms, signal) {
398
+ return new Promise((done) => {
399
+ if (signal?.aborted) return done();
400
+ const timer = setTimeout(finish, ms);
401
+ function finish() {
402
+ clearTimeout(timer);
403
+ signal?.removeEventListener("abort", finish);
404
+ done();
405
+ }
406
+ signal?.addEventListener("abort", finish, { once: true });
407
+ });
408
+ }
409
+ function aborted() {
410
+ return new ChannlizeError("Request aborted", void 0, "ABORTED");
411
+ }
412
+ function need(value, what, field) {
413
+ if (typeof value !== "string" || !value) {
414
+ throw new ValidationError(`${what} is required`, field);
415
+ }
416
+ }
417
+ function createAgentsGovernance(request, defaultAgentId) {
418
+ const agentOf = (agentId) => agentId ?? defaultAgentId;
419
+ const safely = (fn) => {
420
+ try {
421
+ return fn();
422
+ } catch (error) {
423
+ return Promise.reject(error);
424
+ }
425
+ };
426
+ function ask(input) {
427
+ return safely(async () => {
428
+ if (!input || typeof input.question !== "string" || !input.question) {
429
+ throw new ValidationError("ask needs a question", "question");
430
+ }
431
+ const r = await request(
432
+ "POST",
433
+ "/v1/agents/questions",
434
+ {
435
+ body: {
436
+ agent_id: agentOf(input.agentId),
437
+ kind: input.kind,
438
+ question: input.question,
439
+ options: input.options,
440
+ free_text: input.freeText,
441
+ urgency: input.urgency,
442
+ deadline: iso(input.deadline),
443
+ default_option: input.defaultOption,
444
+ channel_id: input.channelId,
445
+ thread_id: input.threadId,
446
+ context: input.context,
447
+ amount: input.amount,
448
+ currency: input.currency,
449
+ resource: input.resource,
450
+ required_roles: input.requiredRoles
451
+ }
452
+ }
453
+ );
454
+ return r.question;
455
+ });
456
+ }
457
+ function wait(questionId, options = {}) {
458
+ return safely(async () => {
459
+ need(questionId, "The question id", "questionId");
460
+ const timeoutSec = Math.min(
461
+ QUESTION_WAIT_MAX_SECONDS,
462
+ Math.max(0, options.timeoutSec ?? QUESTION_WAIT_MAX_SECONDS)
463
+ );
464
+ const r = await request(
465
+ "GET",
466
+ `/v1/agents/questions/${enc(questionId)}/wait`,
467
+ {
468
+ query: { agent_id: agentOf(options.agentId), timeout: timeoutSec },
469
+ // The server holds the request for up to timeoutSec.
470
+ timeout: (timeoutSec + 10) * 1e3,
471
+ signal: options.signal
472
+ }
473
+ );
474
+ return {
475
+ question: r.question,
476
+ timedOut: r.timed_out ?? r.question?.status === "open"
477
+ };
478
+ });
479
+ }
480
+ async function askAndWait(input, options = {}) {
481
+ const { signal } = options;
482
+ const deadline = Date.now() + Math.max(0, options.timeoutMs ?? 6e5);
483
+ if (signal?.aborted) throw aborted();
484
+ let question = await ask(input);
485
+ let failures = 0;
486
+ while (question.status === "open") {
487
+ if (signal?.aborted) throw aborted();
488
+ const left = deadline - Date.now();
489
+ if (left < 1e3) break;
490
+ try {
491
+ const r = await wait(question.id, {
492
+ agentId: input.agentId,
493
+ timeoutSec: Math.min(
494
+ QUESTION_WAIT_MAX_SECONDS,
495
+ Math.floor(left / 1e3)
496
+ ),
497
+ signal
498
+ });
499
+ question = r.question;
500
+ failures = 0;
501
+ } catch (error) {
502
+ if (signal?.aborted) throw aborted();
503
+ if (!isRetryable(error)) throw error;
504
+ failures += 1;
505
+ const backoff = retryAfter(error) ?? Math.min(3e4, 1e3 * 2 ** (failures - 1));
506
+ const remaining = deadline - Date.now();
507
+ if (backoff >= remaining) break;
508
+ await sleep(backoff, signal);
509
+ }
510
+ }
511
+ if (signal?.aborted && question.status === "open") throw aborted();
512
+ return {
513
+ question,
514
+ answer: question.answer ?? null,
515
+ timedOut: question.status === "open"
516
+ };
517
+ }
518
+ const questionPath = (id) => {
519
+ need(id, "The question id", "questionId");
520
+ return `/v1/agents/questions/${enc(id)}`;
521
+ };
522
+ const taskPath = (id) => {
523
+ need(id, "The task id", "taskId");
524
+ return `/v1/agents/tasks/${enc(id)}`;
525
+ };
526
+ const teamPath = (id) => {
527
+ need(id, "The team id", "teamId");
528
+ return `/v1/agents/teams/${enc(id)}`;
529
+ };
530
+ const agentPath = (id) => {
531
+ need(id, "The agent id", "agentId");
532
+ return enc(id);
533
+ };
534
+ return {
535
+ ask,
536
+ askAndWait,
537
+ questions: {
538
+ get: (id, options = {}) => safely(async () => {
539
+ const r = await request("GET", questionPath(id), {
540
+ query: { agent_id: agentOf(options.agentId) }
541
+ });
542
+ return { question: r.question, escalations: r.escalations ?? [] };
543
+ }),
544
+ list: (options = {}) => safely(async () => {
545
+ const r = await request("GET", "/v1/agents/questions", {
546
+ query: {
547
+ agent_id: agentOf(options.agentId),
548
+ status: options.status,
549
+ kind: options.kind,
550
+ urgency: options.urgency,
551
+ channel_id: options.channelId,
552
+ cursor: options.cursor,
553
+ limit: options.limit
554
+ }
555
+ });
556
+ return {
557
+ questions: r.questions ?? [],
558
+ nextCursor: r.next_cursor ?? null
559
+ };
560
+ }),
561
+ wait,
562
+ answer: (id, input) => safely(async () => {
563
+ if (!input?.optionId && !input?.text) {
564
+ throw new ValidationError(
565
+ "answer needs optionId or text",
566
+ "optionId"
567
+ );
568
+ }
569
+ const r = await request(
570
+ "POST",
571
+ `${questionPath(id)}/answer`,
572
+ {
573
+ body: {
574
+ agent_id: agentOf(input.agentId),
575
+ option_id: input.optionId,
576
+ text: input.text,
577
+ comment: input.comment
578
+ }
579
+ }
580
+ );
581
+ return r.question;
582
+ }),
583
+ cancel: (id, options = {}) => safely(async () => {
584
+ const r = await request(
585
+ "DELETE",
586
+ questionPath(id),
587
+ { query: { agent_id: agentOf(options.agentId) } }
588
+ );
589
+ return r.question;
590
+ })
591
+ },
592
+ tasks: {
593
+ create: (input) => safely(async () => {
594
+ need(input?.channelId, "channelId", "channelId");
595
+ const r = await request(
596
+ "POST",
597
+ "/v1/agents/tasks",
598
+ {
599
+ body: {
600
+ agent_id: agentOf(input.agentId),
601
+ channel_id: input.channelId,
602
+ text: input.text,
603
+ assignee_agent_id: input.assigneeAgentId,
604
+ due_at: iso(input.dueAt),
605
+ thread_id: input.threadId,
606
+ mentions: input.mentions,
607
+ data: input.data
608
+ }
609
+ }
610
+ );
611
+ return { task: r.task, deliveries: r.deliveries ?? [] };
612
+ }),
613
+ list: (options = {}) => safely(async () => {
614
+ const r = await request(
615
+ "GET",
616
+ "/v1/agents/tasks",
617
+ {
618
+ query: {
619
+ agent_id: agentOf(options.agentId),
620
+ channel_id: options.channelId,
621
+ assignee_agent_id: options.assigneeAgentId,
622
+ status: options.status,
623
+ limit: options.limit
624
+ }
625
+ }
626
+ );
627
+ return r.tasks ?? [];
628
+ }),
629
+ board: (channelId, options = {}) => safely(() => {
630
+ need(channelId, "channelId", "channelId");
631
+ return request("GET", "/v1/agents/tasks/board", {
632
+ query: {
633
+ channel_id: channelId,
634
+ assignee_agent_id: options.assigneeAgentId,
635
+ agent_id: agentOf(options.agentId)
636
+ }
637
+ });
638
+ }),
639
+ get: (taskId, options = {}) => safely(async () => {
640
+ const r = await request("GET", taskPath(taskId), {
641
+ query: { agent_id: agentOf(options.agentId) }
642
+ });
643
+ return { task: r.task, events: r.events ?? [] };
644
+ }),
645
+ update: (taskId, input) => safely(() => {
646
+ if (!input || input.status === void 0 && input.dueAt === void 0) {
647
+ throw new ValidationError("update needs status or dueAt", "status");
648
+ }
649
+ return request("PATCH", taskPath(taskId), {
650
+ body: {
651
+ agent_id: agentOf(input.agentId),
652
+ status: input.status,
653
+ due_at: input.dueAt === null ? null : iso(input.dueAt),
654
+ note: input.note
655
+ }
656
+ });
657
+ }),
658
+ handoff: (taskId, input) => safely(() => {
659
+ need(input?.toAgentId, "toAgentId", "toAgentId");
660
+ return request(
661
+ "POST",
662
+ `${taskPath(taskId)}/handoff`,
663
+ {
664
+ body: {
665
+ agent_id: agentOf(input.agentId),
666
+ to_agent_id: input.toAgentId,
667
+ reason: input.reason
668
+ }
669
+ }
670
+ );
671
+ })
672
+ },
673
+ teams: {
674
+ list: () => safely(async () => {
675
+ const r = await request(
676
+ "GET",
677
+ "/v1/agents/teams"
678
+ );
679
+ return r.teams ?? [];
680
+ }),
681
+ get: (teamId) => safely(() => request("GET", teamPath(teamId))),
682
+ create: (input) => safely(async () => {
683
+ const r = await request(
684
+ "POST",
685
+ "/v1/agents/teams",
686
+ {
687
+ body: {
688
+ name: input.name,
689
+ slug: input.slug,
690
+ description: input.description,
691
+ agent_ids: input.agentIds
692
+ }
693
+ }
694
+ );
695
+ return r.team;
696
+ }),
697
+ update: (teamId, input) => safely(async () => {
698
+ const r = await request(
699
+ "PATCH",
700
+ teamPath(teamId),
701
+ { body: input }
702
+ );
703
+ return r.team;
704
+ }),
705
+ delete: (teamId) => safely(async () => {
706
+ const r = await request(
707
+ "DELETE",
708
+ teamPath(teamId)
709
+ );
710
+ return { memberships_removed: r.memberships_removed ?? 0 };
711
+ }),
712
+ addMember: (teamId, agentId) => safely(() => {
713
+ need(agentId, "agentId", "agentId");
714
+ return request(
715
+ "POST",
716
+ `${teamPath(teamId)}/members`,
717
+ { body: { agent_id: agentId } }
718
+ );
719
+ }),
720
+ removeMember: (teamId, agentId) => safely(async () => {
721
+ const r = await request(
722
+ "DELETE",
723
+ `${teamPath(teamId)}/members/${agentPath(agentId)}`
724
+ );
725
+ return { memberships_removed: r.memberships_removed ?? 0 };
726
+ }),
727
+ grantChannel: (teamId, channelId, role) => safely(() => {
728
+ need(channelId, "channelId", "channelId");
729
+ return request("POST", `${teamPath(teamId)}/channels`, {
730
+ body: { channel_id: channelId, role }
731
+ });
732
+ }),
733
+ revokeChannel: (teamId, channelId) => safely(async () => {
734
+ need(channelId, "channelId", "channelId");
735
+ const r = await request(
736
+ "DELETE",
737
+ `${teamPath(teamId)}/channels/${enc(channelId)}`
738
+ );
739
+ return { memberships_removed: r.memberships_removed ?? 0 };
740
+ })
741
+ },
742
+ policies: {
743
+ list: (options = {}) => safely(async () => {
744
+ const r = await request(
745
+ "GET",
746
+ "/v1/agents/policies",
747
+ { query: { scope: options.scope } }
748
+ );
749
+ return r.policies ?? [];
750
+ }),
751
+ get: (policyId) => safely(async () => {
752
+ need(policyId, "The policy id", "policyId");
753
+ const r = await request(
754
+ "GET",
755
+ `/v1/agents/policies/${enc(policyId)}`
756
+ );
757
+ return r.policy;
758
+ }),
759
+ put: (input) => safely(async () => {
760
+ const r = await request(
761
+ "PUT",
762
+ "/v1/agents/policies",
763
+ {
764
+ body: {
765
+ scope: input.scope,
766
+ scope_id: input.scopeId,
767
+ rules: input.rules,
768
+ enabled: input.enabled
769
+ }
770
+ }
771
+ );
772
+ return r.policy;
773
+ }),
774
+ delete: (policyId) => safely(async () => {
775
+ need(policyId, "The policy id", "policyId");
776
+ await request("DELETE", `/v1/agents/policies/${enc(policyId)}`);
777
+ }),
778
+ preview: (agentId, options = {}) => safely(() => {
779
+ need(agentId, "agentId", "agentId");
780
+ return request(
781
+ "GET",
782
+ "/v1/agents/policies/preview",
783
+ { query: { agent_id: agentId, channel_id: options.channelId } }
784
+ );
785
+ })
786
+ },
787
+ orgChart: {
788
+ get: (options = {}) => request("GET", "/v1/agents/org-chart", {
789
+ query: {
790
+ team_id: options.teamId,
791
+ channel_id: options.channelId,
792
+ include_retired: options.includeRetired ? "true" : void 0
793
+ }
794
+ }),
795
+ setManager: (agentId, input) => safely(async () => {
796
+ if (input?.managerAgentId && input?.managerUserId) {
797
+ throw new ValidationError(
798
+ "Set managerAgentId or managerUserId, not both",
799
+ "managerAgentId"
800
+ );
801
+ }
802
+ const r = await request(
803
+ "PUT",
804
+ `/v1/agents/org-chart/${agentPath(agentId)}/manager`,
805
+ {
806
+ body: {
807
+ manager_agent_id: input?.managerAgentId ?? null,
808
+ manager_user_id: input?.managerUserId ?? null
809
+ }
810
+ }
811
+ );
812
+ return r.agent;
813
+ }),
814
+ setOwner: (agentId, ownerUserId) => safely(async () => {
815
+ const r = await request(
816
+ "PUT",
817
+ `/v1/agents/org-chart/${agentPath(agentId)}/owner`,
818
+ { body: { owner_user_id: ownerUserId ?? null } }
819
+ );
820
+ return r.agent;
821
+ })
822
+ },
823
+ onboarding: (agentId) => safely(
824
+ () => request(
825
+ "GET",
826
+ `/v1/agents/${agentPath(agentId)}/onboarding`
827
+ )
828
+ ),
829
+ setStatus: (agentId, status, options = {}) => safely(
830
+ () => request(
831
+ "POST",
832
+ `/v1/agents/${agentPath(agentId)}/status`,
833
+ { body: { status, reason: options.reason } }
834
+ )
835
+ )
836
+ };
837
+ }
838
+ var QUESTION_OUTCOMES = /* @__PURE__ */ new Set([
839
+ "question.answered",
840
+ "question.expired",
841
+ "question.canceled"
842
+ ]);
843
+ function isAgentMessageWebhook(payload) {
844
+ return payload?.event === "agent.message";
845
+ }
846
+ function isQuestionOutcomeWebhook(payload) {
847
+ return Boolean(payload && QUESTION_OUTCOMES.has(payload.event));
848
+ }
849
+ function isQuestionEscalatedWebhook(payload) {
850
+ return payload?.event === "question.escalated";
851
+ }
852
+ function isQuestionOutcomeMessage(message) {
853
+ const data = message?.data;
854
+ return Boolean(
855
+ data && typeof data.type === "string" && QUESTION_OUTCOMES.has(data.type) && data.question && typeof data.question === "object"
856
+ );
857
+ }
858
+ function isTaskHandoffMessage(message) {
859
+ const handoff = message?.data?.handoff;
860
+ return Boolean(
861
+ message?.type === "status" && handoff && typeof handoff === "object" && typeof handoff.to_agent_id === "string"
862
+ );
863
+ }
864
+
865
+ // src/agents-observability.ts
866
+ var enc2 = encodeURIComponent;
867
+ function day(v) {
868
+ return v instanceof Date ? v.toISOString().slice(0, 10) : v;
869
+ }
870
+ function iso2(v) {
871
+ return v instanceof Date ? v.toISOString() : v;
872
+ }
873
+ function list(v) {
874
+ return Array.isArray(v) ? v.join(",") : v;
875
+ }
876
+ function need2(value, what, field) {
877
+ if (typeof value !== "string" || !value) {
878
+ throw new ValidationError(`${what} is required`, field);
879
+ }
880
+ }
881
+ function exportFilterFields(f = {}) {
882
+ return {
883
+ from: iso2(f.from),
884
+ to: iso2(f.to),
885
+ agent_id: f.agentId,
886
+ channel_id: f.channelId,
887
+ thread_id: f.threadId,
888
+ type: f.type,
889
+ status: list(f.status),
890
+ action: f.action,
891
+ actor_type: f.actorType,
892
+ actor_id: f.actorId,
893
+ target_type: f.targetType,
894
+ target_id: f.targetId
895
+ };
896
+ }
897
+ function defined(o) {
898
+ return Object.fromEntries(
899
+ Object.entries(o).filter(([, v]) => v !== void 0)
900
+ );
901
+ }
902
+ function filenameOf(disposition) {
903
+ const m = disposition ? /filename="?([^";]+)"?/i.exec(disposition) : null;
904
+ return m ? m[1] : null;
905
+ }
906
+ async function* paginate(page, max) {
907
+ const limit = max ?? Infinity;
908
+ if (limit <= 0) return;
909
+ let cursor;
910
+ let seen = 0;
911
+ for (; ; ) {
912
+ const r = await page(cursor);
913
+ for (const item of r.items) {
914
+ yield item;
915
+ if (++seen >= limit) return;
916
+ }
917
+ if (!r.nextCursor || r.nextCursor === cursor) return;
918
+ cursor = r.nextCursor;
919
+ }
920
+ }
921
+ function createAgentsObservability(request, requestText2) {
922
+ const safely = (fn) => {
923
+ try {
924
+ return fn();
925
+ } catch (error) {
926
+ return Promise.reject(error);
927
+ }
928
+ };
929
+ const rangeQuery = (f = {}) => ({
930
+ from: day(f.from),
931
+ to: day(f.to),
932
+ channel_id: f.channelId,
933
+ agent_id: f.agentId
934
+ });
935
+ function edge(source, target, options = {}) {
936
+ return safely(async () => {
937
+ need2(source, "The source node id", "source");
938
+ need2(target, "The target node id", "target");
939
+ const r = await request("GET", "/v1/agents/insights/graph/edge", {
940
+ query: {
941
+ source,
942
+ target,
943
+ from: day(options.from),
944
+ to: day(options.to),
945
+ channel_id: options.channelId,
946
+ cursor: options.cursor,
947
+ limit: options.limit
948
+ }
949
+ });
950
+ return {
951
+ range: r.range,
952
+ source: r.source,
953
+ target: r.target,
954
+ threads: r.threads ?? [],
955
+ nextCursor: r.next_cursor ?? null
956
+ };
957
+ });
958
+ }
959
+ function search(options) {
960
+ return safely(async () => {
961
+ if (!options || typeof options.q !== "string" || options.q.trim().length < 2) {
962
+ throw new ValidationError("search needs q, at least 2 characters", "q");
963
+ }
964
+ const r = await request("GET", "/v1/agents/search", {
965
+ query: {
966
+ q: options.q,
967
+ agent_id: options.agentId,
968
+ channel_id: options.channelId,
969
+ type: options.type,
970
+ include: list(options.include),
971
+ from: iso2(options.from),
972
+ to: iso2(options.to),
973
+ cursor: options.cursor,
974
+ limit: options.limit
975
+ }
976
+ });
977
+ return {
978
+ results: r.results ?? [],
979
+ nextCursor: r.next_cursor ?? null,
980
+ window: r.window
981
+ };
982
+ });
983
+ }
984
+ return {
985
+ insights: {
986
+ graph(filters = {}) {
987
+ return request("GET", "/v1/agents/insights/graph", {
988
+ query: {
989
+ ...rangeQuery(filters),
990
+ team_id: filters.teamId,
991
+ limit: filters.limit,
992
+ edge_limit: filters.edgeLimit
993
+ }
994
+ });
995
+ },
996
+ edge,
997
+ edgeAll(source, target, options = {}) {
998
+ const { max, ...rest } = options;
999
+ return paginate(async (cursor) => {
1000
+ const page = await edge(source, target, { ...rest, cursor });
1001
+ return { items: page.threads, nextCursor: page.nextCursor };
1002
+ }, max);
1003
+ },
1004
+ cost(options = {}) {
1005
+ return request("GET", "/v1/agents/insights/cost", {
1006
+ query: {
1007
+ ...rangeQuery(options),
1008
+ group_by: options.groupBy,
1009
+ team_id: options.teamId,
1010
+ limit: options.limit
1011
+ }
1012
+ });
1013
+ },
1014
+ metrics(filters = {}) {
1015
+ return request("GET", "/v1/agents/insights/metrics", {
1016
+ query: rangeQuery(filters)
1017
+ });
1018
+ }
1019
+ },
1020
+ search,
1021
+ searchAll(options) {
1022
+ const { max, ...rest } = options ?? {};
1023
+ return paginate(async (cursor) => {
1024
+ const page = await search({ ...rest, cursor });
1025
+ return { items: page.results, nextCursor: page.nextCursor };
1026
+ }, max);
1027
+ },
1028
+ exports: {
1029
+ download(kind, format = "csv", filters = {}) {
1030
+ return safely(async () => {
1031
+ need2(kind, "The export kind", "kind");
1032
+ const res = await requestText2("/v1/agents/export", {
1033
+ query: { kind, format, ...exportFilterFields(filters) }
1034
+ });
1035
+ return {
1036
+ body: res.body,
1037
+ rows: Number(res.headers.get("x-export-rows") ?? 0) || 0,
1038
+ truncated: res.headers.get("x-export-truncated") === "true",
1039
+ contentType: res.headers.get("content-type") ?? "",
1040
+ filename: filenameOf(res.headers.get("content-disposition"))
1041
+ };
1042
+ });
1043
+ },
1044
+ start(input) {
1045
+ return safely(async () => {
1046
+ need2(input?.kind, "The export kind", "kind");
1047
+ const r = await request("POST", "/v1/agents/export", {
1048
+ body: {
1049
+ kind: input.kind,
1050
+ format: input.format,
1051
+ filters: defined(exportFilterFields(input.filters))
1052
+ }
1053
+ });
1054
+ return { exportId: r.export_id, status: r.status, export: r.export };
1055
+ });
1056
+ },
1057
+ get(exportId) {
1058
+ return safely(async () => {
1059
+ need2(exportId, "The export id", "exportId");
1060
+ const r = await request(
1061
+ "GET",
1062
+ `/v1/agents/export/${enc2(exportId)}`
1063
+ );
1064
+ return r.export;
1065
+ });
1066
+ },
1067
+ async list(options = {}) {
1068
+ const r = await request(
1069
+ "GET",
1070
+ "/v1/agents/export",
1071
+ { query: { limit: options.limit } }
1072
+ );
1073
+ return r.exports ?? [];
1074
+ }
1075
+ },
1076
+ telemetry: {
1077
+ async getOtel() {
1078
+ return (await request("GET", "/v1/agents/telemetry/otel")).otel;
1079
+ },
1080
+ async setOtel(input) {
1081
+ const r = await request(
1082
+ "PUT",
1083
+ "/v1/agents/telemetry/otel",
1084
+ {
1085
+ body: {
1086
+ enabled: input?.enabled,
1087
+ endpoint_url: input?.endpointUrl,
1088
+ headers: input?.headers
1089
+ }
1090
+ }
1091
+ );
1092
+ return r.otel;
1093
+ },
1094
+ testOtel() {
1095
+ return request(
1096
+ "POST",
1097
+ "/v1/agents/telemetry/otel/test"
1098
+ );
1099
+ },
1100
+ async getWebhook() {
1101
+ return (await request(
1102
+ "GET",
1103
+ "/v1/agents/telemetry/webhook"
1104
+ )).webhook;
1105
+ },
1106
+ setWebhook(input) {
1107
+ return request(
1108
+ "PUT",
1109
+ "/v1/agents/telemetry/webhook",
1110
+ {
1111
+ body: {
1112
+ enabled: input?.enabled,
1113
+ url: input?.url,
1114
+ rotate_secret: input?.rotateSecret
1115
+ }
1116
+ }
1117
+ );
1118
+ },
1119
+ testWebhook() {
1120
+ return request(
1121
+ "POST",
1122
+ "/v1/agents/telemetry/webhook/test"
1123
+ );
1124
+ }
1125
+ }
1126
+ };
1127
+ }
1128
+
1129
+ // src/agents.ts
1130
+ function resolve(config) {
1131
+ const env = readChannlizeEnv();
1132
+ const apiKey = config.apiKey ?? env.serverApiKey ?? env.apiKey;
1133
+ if (!apiKey) {
1134
+ throw new ValidationError(
1135
+ "An agent API key is required: pass apiKey or set CHANNLIZE_API_KEY (or CHANNLIZE_SERVER_API_KEY)",
1136
+ "apiKey"
1137
+ );
1138
+ }
1139
+ if (keyKind(apiKey) === "widget") {
1140
+ throw new ValidationError(
1141
+ "Agents need an agent key or a multi-agent key (ch_live_\u2026, agents:send / agents:read scopes), not the public widget key",
1142
+ "apiKey"
1143
+ );
1144
+ }
1145
+ return {
1146
+ apiKey,
1147
+ agentId: config.agentId ?? env.agentId,
1148
+ baseUrl: (config.baseUrl ?? env.baseUrl ?? DEFAULT_BASE_URL).replace(
1149
+ /\/+$/,
1150
+ ""
1151
+ ),
1152
+ timeout: config.timeout ?? DEFAULT_TIMEOUT
1153
+ };
1154
+ }
1155
+ var enc3 = encodeURIComponent;
1156
+ function isFatal(error) {
1157
+ return error instanceof ChannlizeError && [400, 401, 403, 404].includes(error.statusCode ?? 0);
1158
+ }
1159
+ function sleep2(ms, signal) {
1160
+ return new Promise((done) => {
1161
+ if (signal?.aborted) return done();
1162
+ const timer = setTimeout(finish, ms);
1163
+ function finish() {
1164
+ clearTimeout(timer);
1165
+ signal?.removeEventListener("abort", finish);
1166
+ done();
1167
+ }
1168
+ signal?.addEventListener("abort", finish, { once: true });
1169
+ });
1170
+ }
1171
+ function createAgentsClient(config = {}) {
1172
+ const cfg = resolve(config);
1173
+ const request = async (method, path, options = {}) => {
1174
+ const params = new URLSearchParams();
1175
+ for (const [k, v] of Object.entries(options.query ?? {})) {
1176
+ if (v !== void 0 && v !== "") params.set(k, String(v));
1177
+ }
1178
+ const qs = params.toString();
1179
+ const http = { ...cfg, timeout: options.timeout ?? cfg.timeout };
1180
+ const res = await requestJson(http, `${path}${qs ? `?${qs}` : ""}`, {
1181
+ method,
1182
+ headers: { Authorization: `Bearer ${cfg.apiKey}` },
1183
+ body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
1184
+ signal: options.signal
1185
+ });
1186
+ return res;
1187
+ };
1188
+ const fetchText = (path, options = {}) => {
1189
+ const params = new URLSearchParams();
1190
+ for (const [k, v] of Object.entries(options.query ?? {})) {
1191
+ if (v !== void 0 && v !== "") params.set(k, String(v));
1192
+ }
1193
+ const qs = params.toString();
1194
+ return requestText(cfg, `${path}${qs ? `?${qs}` : ""}`, {
1195
+ method: "GET",
1196
+ headers: { Authorization: `Bearer ${cfg.apiKey}` }
1197
+ });
1198
+ };
1199
+ function agentFor(agentId, what) {
1200
+ const id = agentId ?? cfg.agentId;
1201
+ if (!id) {
1202
+ throw new ValidationError(
1203
+ `${what} needs an agent id: pass it or set agentId (CHANNLIZE_AGENT_ID)`,
1204
+ "agentId"
1205
+ );
1206
+ }
1207
+ return id;
1208
+ }
1209
+ async function inbox(agentId, options = {}) {
1210
+ const id = agentFor(agentId, "inbox");
1211
+ const r = await request("GET", `/v1/agents/${enc3(id)}/inbox`, {
1212
+ query: { cursor: options.cursor, limit: options.limit }
1213
+ });
1214
+ return {
1215
+ messages: r.messages ?? [],
1216
+ nextCursor: r.next_cursor ?? null,
1217
+ hasMore: Boolean(r.has_more)
1218
+ };
1219
+ }
1220
+ async function ack(agentId, input) {
1221
+ const id = agentFor(agentId, "ack");
1222
+ let body;
1223
+ if ("upTo" in input && input.upTo) body = { up_to: input.upTo };
1224
+ else if ("ids" in input && input.ids?.length) body = { ids: input.ids };
1225
+ else {
1226
+ throw new ValidationError(
1227
+ "ack needs upTo or a non-empty ids list",
1228
+ "ids"
1229
+ );
1230
+ }
1231
+ return request("POST", `/v1/agents/${enc3(id)}/inbox/ack`, {
1232
+ body
1233
+ });
1234
+ }
1235
+ async function poll(agentId, handler, options = {}) {
1236
+ const id = agentFor(agentId, "poll");
1237
+ const interval = Math.max(0, options.intervalMs ?? 5e3);
1238
+ const maxBackoff = Math.max(interval, options.maxBackoffMs ?? 6e4);
1239
+ const { signal } = options;
1240
+ let failures = 0;
1241
+ while (!signal?.aborted) {
1242
+ let failed = false;
1243
+ let more = false;
1244
+ try {
1245
+ const page = await inbox(id, { limit: options.limit });
1246
+ more = page.hasMore;
1247
+ const done = [];
1248
+ try {
1249
+ for (const item of page.messages) {
1250
+ if (signal?.aborted) break;
1251
+ try {
1252
+ await handler(item.message, item);
1253
+ } catch (error) {
1254
+ failed = true;
1255
+ options.onError?.(error, item);
1256
+ break;
1257
+ }
1258
+ done.push(item.delivery_id);
1259
+ }
1260
+ } finally {
1261
+ if (done.length) await ack(id, { ids: done });
1262
+ }
1263
+ } catch (error) {
1264
+ if (isFatal(error)) throw error;
1265
+ failed = true;
1266
+ options.onError?.(error);
1267
+ }
1268
+ if (failed) {
1269
+ failures += 1;
1270
+ const backoff = Math.min(
1271
+ maxBackoff,
1272
+ (interval || 1e3) * 2 ** (failures - 1)
1273
+ );
1274
+ await sleep2(backoff, signal);
1275
+ } else {
1276
+ failures = 0;
1277
+ if (!more) await sleep2(interval, signal);
1278
+ }
1279
+ }
1280
+ }
1281
+ const channelRead = (options = {}) => ({
1282
+ agent_id: options.agentId ?? cfg.agentId
1283
+ });
1284
+ return {
1285
+ ...createAgentsGovernance(request, cfg.agentId),
1286
+ ...createAgentsObservability(request, fetchText),
1287
+ agentId: cfg.agentId,
1288
+ send(input) {
1289
+ if (!input || typeof input.channel !== "string" || !input.channel) {
1290
+ return Promise.reject(
1291
+ new ValidationError("send needs a channel id", "channel")
1292
+ );
1293
+ }
1294
+ return request("POST", "/v1/agents/messages", {
1295
+ body: {
1296
+ agent_id: input.agentId ?? cfg.agentId,
1297
+ channel_id: input.channel,
1298
+ type: input.type,
1299
+ text: input.text,
1300
+ thread_id: input.threadId,
1301
+ mentions: input.mentions,
1302
+ data: input.data,
1303
+ tokens: input.tokens,
1304
+ cost: input.cost,
1305
+ task_id: input.taskId,
1306
+ task_status: input.taskStatus
1307
+ }
1308
+ });
1309
+ },
1310
+ inbox,
1311
+ ack,
1312
+ poll,
1313
+ async list(options = {}) {
1314
+ const r = await request("GET", "/v1/agents", {
1315
+ query: { include_retired: options.includeRetired || void 0 }
1316
+ });
1317
+ return r.agents ?? [];
1318
+ },
1319
+ get(agentId) {
1320
+ return request("GET", `/v1/agents/${enc3(agentId)}`);
1321
+ },
1322
+ channels: {
1323
+ async list(options = {}) {
1324
+ const r = await request(
1325
+ "GET",
1326
+ "/v1/agents/channels",
1327
+ { query: { include_archived: options.includeArchived || void 0 } }
1328
+ );
1329
+ return r.channels ?? [];
1330
+ },
1331
+ get(channelId, options) {
1332
+ return request("GET", `/v1/agents/channels/${enc3(channelId)}`, {
1333
+ query: channelRead(options)
1334
+ });
1335
+ },
1336
+ async members(channelId, options) {
1337
+ const r = await request(
1338
+ "GET",
1339
+ `/v1/agents/channels/${enc3(channelId)}/members`,
1340
+ { query: channelRead(options) }
1341
+ );
1342
+ return r.members ?? [];
1343
+ },
1344
+ async messages(channelId, options = {}) {
1345
+ const r = await request("GET", `/v1/agents/channels/${enc3(channelId)}/messages`, {
1346
+ query: {
1347
+ ...channelRead(options),
1348
+ cursor: options.cursor,
1349
+ after: options.after,
1350
+ limit: options.limit,
1351
+ sender_agent_id: options.senderAgentId,
1352
+ type: options.type,
1353
+ thread_id: options.threadId
1354
+ }
1355
+ });
1356
+ return {
1357
+ channel: r.channel,
1358
+ messages: r.messages ?? [],
1359
+ nextCursor: r.next_cursor ?? null,
1360
+ latestCursor: r.latest_cursor ?? null,
1361
+ hasMore: Boolean(r.has_more)
1362
+ };
1363
+ }
1364
+ }
1365
+ };
1366
+ }
1367
+ var NODE_CRYPTO = "node:crypto";
1368
+ async function getSubtle() {
1369
+ const subtle = globalThis.crypto?.subtle;
1370
+ if (subtle) return subtle;
1371
+ try {
1372
+ const mod = await import(
1373
+ /* webpackIgnore: true */
1374
+ /* @vite-ignore */
1375
+ NODE_CRYPTO
1376
+ );
1377
+ if (mod.webcrypto?.subtle) return mod.webcrypto.subtle;
1378
+ } catch {
1379
+ }
1380
+ throw new ChannlizeError(
1381
+ "Webhook signature checks need the Web Crypto API (globalThis.crypto.subtle, or node:crypto webcrypto on Node.js 18)"
1382
+ );
1383
+ }
1384
+ function hexToBytes(hex) {
1385
+ if (hex.length === 0 || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
1386
+ return null;
1387
+ }
1388
+ const out = new Uint8Array(hex.length / 2);
1389
+ for (let i = 0; i < out.length; i++) {
1390
+ out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
1391
+ }
1392
+ return out;
1393
+ }
1394
+ function toBytes(body) {
1395
+ if (typeof body === "string") return new TextEncoder().encode(body);
1396
+ const copy = new Uint8Array(body.byteLength);
1397
+ copy.set(body);
1398
+ return copy;
1399
+ }
1400
+ async function verifyAgentWebhook(rawBody, signatureHeader, secret) {
1401
+ if (!secret)
1402
+ throw new ValidationError("The agent secret is required", "secret");
1403
+ return verifyHmacSha256(rawBody, signatureHeader, secret);
1404
+ }
1405
+ async function verifyHmacSha256(rawBody, signatureHeader, secret) {
1406
+ const header = Array.isArray(signatureHeader) ? signatureHeader[0] : signatureHeader;
1407
+ const m = /^sha256=([0-9a-f]+)$/i.exec(header?.trim() ?? "");
1408
+ const expected = m ? hexToBytes(m[1]) : null;
1409
+ if (!expected || expected.length !== 32) return false;
1410
+ const subtle = await getSubtle();
1411
+ const key = await subtle.importKey(
1412
+ "raw",
1413
+ new TextEncoder().encode(secret),
1414
+ { name: "HMAC", hash: "SHA-256" },
1415
+ false,
1416
+ ["verify"]
1417
+ );
1418
+ return subtle.verify("HMAC", key, expected, toBytes(rawBody));
1419
+ }
1420
+ async function parseAgentWebhook(rawBody, signatureHeader, secret) {
1421
+ return await parseAgentWebhookEvent(
1422
+ rawBody,
1423
+ signatureHeader,
1424
+ secret
1425
+ );
1426
+ }
1427
+ async function parseAgentWebhookEvent(rawBody, signatureHeader, secret) {
1428
+ if (!await verifyAgentWebhook(rawBody, signatureHeader, secret)) {
1429
+ throw new ChannlizeError(
1430
+ "Invalid agent webhook signature",
1431
+ 401,
1432
+ "INVALID_SIGNATURE"
1433
+ );
1434
+ }
1435
+ const text = typeof rawBody === "string" ? rawBody : new TextDecoder().decode(rawBody);
1436
+ return JSON.parse(text);
1437
+ }
1438
+ async function verifyTelemetryWebhook(rawBody, signatureHeader, secret) {
1439
+ if (!secret) {
1440
+ throw new ValidationError(
1441
+ "The telemetry webhook secret is required",
1442
+ "secret"
1443
+ );
1444
+ }
1445
+ return verifyHmacSha256(rawBody, signatureHeader, secret);
1446
+ }
1447
+ async function parseTelemetryWebhook(rawBody, signatureHeader, secret) {
1448
+ if (!await verifyTelemetryWebhook(rawBody, signatureHeader, secret)) {
1449
+ throw new ChannlizeError(
1450
+ "Invalid telemetry webhook signature",
1451
+ 401,
1452
+ "INVALID_SIGNATURE"
1453
+ );
1454
+ }
1455
+ const text = typeof rawBody === "string" ? rawBody : new TextDecoder().decode(rawBody);
1456
+ return JSON.parse(text);
1457
+ }
1458
+
228
1459
  // src/client.ts
229
1460
  var ChannlizeClient = class {
230
1461
  config;
@@ -242,6 +1473,26 @@ var ChannlizeClient = class {
242
1473
  throw new ValidationError("Widget key is required");
243
1474
  }
244
1475
  }
1476
+ agentsClient;
1477
+ /**
1478
+ * Channlize for Agents (`agents.send`, `agents.inbox`, `agents.poll`, ...),
1479
+ * authenticated with `serverApiKey`: an agent key or a multi-agent key.
1480
+ * Same as `createAgentsClient({ apiKey: serverApiKey, baseUrl, timeout })`.
1481
+ */
1482
+ get agents() {
1483
+ if (!this.config.serverApiKey) {
1484
+ throw new ValidationError(
1485
+ "serverApiKey (an agent or multi-agent key) is required for agents",
1486
+ "serverApiKey"
1487
+ );
1488
+ }
1489
+ this.agentsClient ??= createAgentsClient({
1490
+ apiKey: this.config.serverApiKey,
1491
+ baseUrl: this.config.baseUrl,
1492
+ timeout: this.config.timeout
1493
+ });
1494
+ return this.agentsClient;
1495
+ }
245
1496
  makeRequest(endpoint, options = {}) {
246
1497
  return requestJson(this.config, endpoint, options);
247
1498
  }
@@ -397,9 +1648,9 @@ var ChannlizeClient = class {
397
1648
  * plan's limit for the billing period) resolves with `success: false`
398
1649
  * and `dropped` instead of throwing, with one console warning per client.
399
1650
  */
400
- async nonFatalTracking(send) {
1651
+ async nonFatalTracking(send2) {
401
1652
  try {
402
- return await send();
1653
+ return await send2();
403
1654
  } catch (error) {
404
1655
  if (!(error instanceof RateLimitError)) throw error;
405
1656
  const dropped = error.code === "PLAN_LIMIT_REACHED" ? "plan_limit" : "rate_limit";
@@ -456,21 +1707,98 @@ var ChannlizeClient = class {
456
1707
  );
457
1708
  }
458
1709
  };
1710
+
1711
+ // src/agents-types.ts
1712
+ var AGENT_STATUSES = ["active", "paused", "retired"];
1713
+ var AGENT_DELIVERY_MODES = ["webhook", "inbox", "a2a"];
1714
+ var AGENT_MESSAGE_TYPES = [
1715
+ "message",
1716
+ "task",
1717
+ "question",
1718
+ "escalation",
1719
+ "status"
1720
+ ];
1721
+ var SENDABLE_AGENT_MESSAGE_TYPES = [
1722
+ "message",
1723
+ "task",
1724
+ "status"
1725
+ ];
1726
+ var AGENT_TASK_STATUSES = [
1727
+ "open",
1728
+ "working",
1729
+ "input_required",
1730
+ "completed",
1731
+ "failed",
1732
+ "canceled",
1733
+ "rejected"
1734
+ ];
1735
+
1736
+ // src/agents-observability-types.ts
1737
+ var COST_GROUPINGS = [
1738
+ "agent",
1739
+ "channel",
1740
+ "team",
1741
+ "task",
1742
+ "day"
1743
+ ];
1744
+ var AGENT_EXPORT_KINDS = [
1745
+ "messages",
1746
+ "tasks",
1747
+ "questions",
1748
+ "audit"
1749
+ ];
1750
+ var AGENT_EXPORT_FORMATS = ["csv", "json"];
1751
+ var AGENT_EXPORT_SYNC_MAX_ROWS = 1e4;
459
1752
  export {
1753
+ AGENT_DELIVERY_MODES,
1754
+ AGENT_EXPORT_FORMATS,
1755
+ AGENT_EXPORT_KINDS,
1756
+ AGENT_EXPORT_SYNC_MAX_ROWS,
1757
+ AGENT_MESSAGE_TYPES,
1758
+ AGENT_POLICY_CODES,
1759
+ AGENT_STATUSES,
1760
+ AGENT_TASK_ERROR_CODES,
1761
+ AGENT_TASK_STATUSES,
1762
+ AgentPolicyError,
1763
+ AgentTaskError,
1764
+ AgentThreadPausedError,
460
1765
  AuthenticationError,
1766
+ COST_GROUPINGS,
461
1767
  ChannlizeClient,
462
1768
  ChannlizeError,
463
1769
  DEFAULT_BASE_URL,
464
1770
  DEFAULT_TIMEOUT,
465
1771
  EVENT_KEY_PATTERN,
466
1772
  EVENT_SEVERITIES,
1773
+ HumanTakeoverError,
1774
+ POLICY_SCOPES,
1775
+ QUESTION_KINDS,
1776
+ QUESTION_STATUSES,
1777
+ QUESTION_URGENCIES,
1778
+ QUESTION_WAIT_MAX_SECONDS,
1779
+ QuestionClosedError,
467
1780
  RateLimitError,
1781
+ SENDABLE_AGENT_MESSAGE_TYPES,
1782
+ TASK_BOARD_STATUSES,
468
1783
  ValidationError,
1784
+ createAgentsClient,
469
1785
  createNotifier,
470
1786
  ChannlizeClient as default,
1787
+ isAgentMessageWebhook,
1788
+ isQuestionEscalatedWebhook,
1789
+ isQuestionOutcomeMessage,
1790
+ isQuestionOutcomeWebhook,
1791
+ isRetryable,
1792
+ isTaskHandoffMessage,
471
1793
  isValidEventKey,
472
1794
  keyKind,
1795
+ parseAgentWebhook,
1796
+ parseAgentWebhookEvent,
473
1797
  parseApiError,
474
- readChannlizeEnv
1798
+ parseTelemetryWebhook,
1799
+ readChannlizeEnv,
1800
+ retryAfter,
1801
+ verifyAgentWebhook,
1802
+ verifyTelemetryWebhook
475
1803
  };
476
1804
  //# sourceMappingURL=index.mjs.map