@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/CHANGELOG.md +36 -0
- package/README.md +389 -1
- package/dist/index.d.mts +1906 -2
- package/dist/index.d.ts +1906 -2
- package/dist/index.js +1374 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1338 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30,21 +30,56 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AGENT_DELIVERY_MODES: () => AGENT_DELIVERY_MODES,
|
|
34
|
+
AGENT_EXPORT_FORMATS: () => AGENT_EXPORT_FORMATS,
|
|
35
|
+
AGENT_EXPORT_KINDS: () => AGENT_EXPORT_KINDS,
|
|
36
|
+
AGENT_EXPORT_SYNC_MAX_ROWS: () => AGENT_EXPORT_SYNC_MAX_ROWS,
|
|
37
|
+
AGENT_MESSAGE_TYPES: () => AGENT_MESSAGE_TYPES,
|
|
38
|
+
AGENT_POLICY_CODES: () => AGENT_POLICY_CODES,
|
|
39
|
+
AGENT_STATUSES: () => AGENT_STATUSES,
|
|
40
|
+
AGENT_TASK_ERROR_CODES: () => AGENT_TASK_ERROR_CODES,
|
|
41
|
+
AGENT_TASK_STATUSES: () => AGENT_TASK_STATUSES,
|
|
42
|
+
AgentPolicyError: () => AgentPolicyError,
|
|
43
|
+
AgentTaskError: () => AgentTaskError,
|
|
44
|
+
AgentThreadPausedError: () => AgentThreadPausedError,
|
|
33
45
|
AuthenticationError: () => AuthenticationError,
|
|
46
|
+
COST_GROUPINGS: () => COST_GROUPINGS,
|
|
34
47
|
ChannlizeClient: () => ChannlizeClient,
|
|
35
48
|
ChannlizeError: () => ChannlizeError,
|
|
36
49
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
|
|
37
50
|
DEFAULT_TIMEOUT: () => DEFAULT_TIMEOUT,
|
|
38
51
|
EVENT_KEY_PATTERN: () => EVENT_KEY_PATTERN,
|
|
39
52
|
EVENT_SEVERITIES: () => EVENT_SEVERITIES,
|
|
53
|
+
HumanTakeoverError: () => HumanTakeoverError,
|
|
54
|
+
POLICY_SCOPES: () => POLICY_SCOPES,
|
|
55
|
+
QUESTION_KINDS: () => QUESTION_KINDS,
|
|
56
|
+
QUESTION_STATUSES: () => QUESTION_STATUSES,
|
|
57
|
+
QUESTION_URGENCIES: () => QUESTION_URGENCIES,
|
|
58
|
+
QUESTION_WAIT_MAX_SECONDS: () => QUESTION_WAIT_MAX_SECONDS,
|
|
59
|
+
QuestionClosedError: () => QuestionClosedError,
|
|
40
60
|
RateLimitError: () => RateLimitError,
|
|
61
|
+
SENDABLE_AGENT_MESSAGE_TYPES: () => SENDABLE_AGENT_MESSAGE_TYPES,
|
|
62
|
+
TASK_BOARD_STATUSES: () => TASK_BOARD_STATUSES,
|
|
41
63
|
ValidationError: () => ValidationError,
|
|
64
|
+
createAgentsClient: () => createAgentsClient,
|
|
42
65
|
createNotifier: () => createNotifier,
|
|
43
66
|
default: () => ChannlizeClient,
|
|
67
|
+
isAgentMessageWebhook: () => isAgentMessageWebhook,
|
|
68
|
+
isQuestionEscalatedWebhook: () => isQuestionEscalatedWebhook,
|
|
69
|
+
isQuestionOutcomeMessage: () => isQuestionOutcomeMessage,
|
|
70
|
+
isQuestionOutcomeWebhook: () => isQuestionOutcomeWebhook,
|
|
71
|
+
isRetryable: () => isRetryable,
|
|
72
|
+
isTaskHandoffMessage: () => isTaskHandoffMessage,
|
|
44
73
|
isValidEventKey: () => isValidEventKey,
|
|
45
74
|
keyKind: () => keyKind,
|
|
75
|
+
parseAgentWebhook: () => parseAgentWebhook,
|
|
76
|
+
parseAgentWebhookEvent: () => parseAgentWebhookEvent,
|
|
46
77
|
parseApiError: () => parseApiError,
|
|
47
|
-
|
|
78
|
+
parseTelemetryWebhook: () => parseTelemetryWebhook,
|
|
79
|
+
readChannlizeEnv: () => readChannlizeEnv,
|
|
80
|
+
retryAfter: () => retryAfter,
|
|
81
|
+
verifyAgentWebhook: () => verifyAgentWebhook,
|
|
82
|
+
verifyTelemetryWebhook: () => verifyTelemetryWebhook
|
|
48
83
|
});
|
|
49
84
|
module.exports = __toCommonJS(index_exports);
|
|
50
85
|
|
|
@@ -73,7 +108,8 @@ function readChannlizeEnv(env = typeof process !== "undefined" ? process.env : v
|
|
|
73
108
|
apiKey: clean(env?.CHANNLIZE_API_KEY),
|
|
74
109
|
projectId: clean(env?.CHANNLIZE_PROJECT_ID),
|
|
75
110
|
baseUrl: clean(env?.CHANNLIZE_BASE_URL),
|
|
76
|
-
timeout: clean(env?.CHANNLIZE_TIMEOUT)
|
|
111
|
+
timeout: clean(env?.CHANNLIZE_TIMEOUT),
|
|
112
|
+
agentId: clean(env?.CHANNLIZE_AGENT_ID)
|
|
77
113
|
};
|
|
78
114
|
}
|
|
79
115
|
function parseApiError(status, body, statusText = "") {
|
|
@@ -109,6 +145,12 @@ var ChannlizeError = class extends Error {
|
|
|
109
145
|
* `upgradeUrl` on a plan-limit 429. Undefined for network errors.
|
|
110
146
|
*/
|
|
111
147
|
details;
|
|
148
|
+
/**
|
|
149
|
+
* ISO time the call may succeed again: the body's `retryAt` (agent
|
|
150
|
+
* policies, human takeovers), else derived from a `Retry-After` header.
|
|
151
|
+
* See `retryAfter(error)`.
|
|
152
|
+
*/
|
|
153
|
+
retryAt;
|
|
112
154
|
};
|
|
113
155
|
var ValidationError = class extends ChannlizeError {
|
|
114
156
|
constructor(message, field) {
|
|
@@ -131,11 +173,121 @@ var RateLimitError = class extends ChannlizeError {
|
|
|
131
173
|
}
|
|
132
174
|
};
|
|
133
175
|
|
|
176
|
+
// src/agents-errors.ts
|
|
177
|
+
var AGENT_POLICY_CODES = [
|
|
178
|
+
"POLICY_CHANNEL_NOT_ALLOWED",
|
|
179
|
+
"POLICY_DM_NOT_ALLOWED",
|
|
180
|
+
"POLICY_ASK_HUMAN_NOT_ALLOWED",
|
|
181
|
+
"POLICY_URGENCY_NOT_ALLOWED",
|
|
182
|
+
"POLICY_RATE_LIMITED",
|
|
183
|
+
"POLICY_BUDGET_EXHAUSTED",
|
|
184
|
+
"POLICY_OUTSIDE_WORKING_HOURS",
|
|
185
|
+
"POLICY_MAX_DEPTH"
|
|
186
|
+
];
|
|
187
|
+
var AGENT_TASK_ERROR_CODES = [
|
|
188
|
+
"TASK_CLOSED",
|
|
189
|
+
"ALREADY_ASSIGNED",
|
|
190
|
+
"AGENT_NOT_IN_CHANNEL",
|
|
191
|
+
"NOT_TASK_PARTICIPANT"
|
|
192
|
+
];
|
|
193
|
+
var AgentPolicyError = class extends ChannlizeError {
|
|
194
|
+
constructor(message, statusCode, code, retryAt) {
|
|
195
|
+
super(message, statusCode, code);
|
|
196
|
+
this.name = "AgentPolicyError";
|
|
197
|
+
this.retryAt = retryAt;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
var HumanTakeoverError = class extends ChannlizeError {
|
|
201
|
+
constructor(message, retryAt) {
|
|
202
|
+
super(message, 409, "HUMAN_TAKEOVER");
|
|
203
|
+
this.name = "HumanTakeoverError";
|
|
204
|
+
this.retryAt = retryAt;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var AgentThreadPausedError = class extends RateLimitError {
|
|
208
|
+
constructor(message, reason = "thread_cap") {
|
|
209
|
+
super(message, "AGENT_THREAD_PAUSED");
|
|
210
|
+
this.reason = reason;
|
|
211
|
+
this.name = "AgentThreadPausedError";
|
|
212
|
+
}
|
|
213
|
+
reason;
|
|
214
|
+
};
|
|
215
|
+
var AgentTaskError = class extends ChannlizeError {
|
|
216
|
+
constructor(message, statusCode, code) {
|
|
217
|
+
super(message, statusCode, code);
|
|
218
|
+
this.name = "AgentTaskError";
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
var QuestionClosedError = class extends ChannlizeError {
|
|
222
|
+
constructor(message) {
|
|
223
|
+
super(message, 409, "QUESTION_CLOSED");
|
|
224
|
+
this.name = "QuestionClosedError";
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
function agentErrorFor(status, message, code, body, retryAt) {
|
|
228
|
+
if (!code) return void 0;
|
|
229
|
+
if (code.startsWith("POLICY_")) {
|
|
230
|
+
return new AgentPolicyError(message, status, code, retryAt);
|
|
231
|
+
}
|
|
232
|
+
switch (code) {
|
|
233
|
+
case "HUMAN_TAKEOVER":
|
|
234
|
+
return new HumanTakeoverError(message, retryAt);
|
|
235
|
+
case "AGENT_THREAD_PAUSED":
|
|
236
|
+
return new AgentThreadPausedError(
|
|
237
|
+
message,
|
|
238
|
+
typeof body.reason === "string" ? body.reason : void 0
|
|
239
|
+
);
|
|
240
|
+
case "AGENT_RATE_LIMITED":
|
|
241
|
+
return new RateLimitError(message, code);
|
|
242
|
+
case "QUESTION_CLOSED":
|
|
243
|
+
return new QuestionClosedError(message);
|
|
244
|
+
default:
|
|
245
|
+
return AGENT_TASK_ERROR_CODES.includes(code) ? new AgentTaskError(message, status, code) : void 0;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function isRetryable(error) {
|
|
249
|
+
if (!(error instanceof ChannlizeError)) return false;
|
|
250
|
+
const { statusCode: status, code } = error;
|
|
251
|
+
if (code === "NETWORK_ERROR" || code === "TIMEOUT") return true;
|
|
252
|
+
if (status === void 0) return false;
|
|
253
|
+
if (status >= 500) return true;
|
|
254
|
+
if (error instanceof AgentPolicyError || error instanceof HumanTakeoverError) {
|
|
255
|
+
return Boolean(error.retryAt);
|
|
256
|
+
}
|
|
257
|
+
if (status === 429) return code !== "PLAN_LIMIT_REACHED";
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
function retryAfter(error, now = Date.now()) {
|
|
261
|
+
if (!isRetryable(error)) return void 0;
|
|
262
|
+
const at = error.retryAt;
|
|
263
|
+
const ms = at ? Date.parse(at) : NaN;
|
|
264
|
+
return Number.isFinite(ms) ? Math.max(0, ms - now) : void 0;
|
|
265
|
+
}
|
|
266
|
+
|
|
134
267
|
// src/http.ts
|
|
135
268
|
async function requestJson(http, endpoint, options = {}) {
|
|
269
|
+
return send(
|
|
270
|
+
http,
|
|
271
|
+
endpoint,
|
|
272
|
+
options,
|
|
273
|
+
async (res) => await res.json()
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
async function requestText(http, endpoint, options = {}) {
|
|
277
|
+
return send(http, endpoint, options, async (res) => ({
|
|
278
|
+
body: await res.text(),
|
|
279
|
+
status: res.status,
|
|
280
|
+
headers: res.headers
|
|
281
|
+
}));
|
|
282
|
+
}
|
|
283
|
+
async function send(http, endpoint, options, read) {
|
|
136
284
|
const url = `${http.baseUrl}${endpoint}`;
|
|
137
285
|
const controller = new AbortController();
|
|
138
286
|
const timeoutId = setTimeout(() => controller.abort(), http.timeout);
|
|
287
|
+
const external = options.signal ?? void 0;
|
|
288
|
+
const onAbort = () => controller.abort();
|
|
289
|
+
if (external?.aborted) controller.abort();
|
|
290
|
+
else external?.addEventListener("abort", onAbort, { once: true });
|
|
139
291
|
try {
|
|
140
292
|
const mergedHeaders = {
|
|
141
293
|
"Content-Type": "application/json",
|
|
@@ -153,21 +305,33 @@ async function requestJson(http, endpoint, options = {}) {
|
|
|
153
305
|
if (!response.ok) {
|
|
154
306
|
await handleErrorResponse(response);
|
|
155
307
|
}
|
|
156
|
-
|
|
157
|
-
return data;
|
|
308
|
+
return await read(response);
|
|
158
309
|
} catch (error) {
|
|
159
310
|
clearTimeout(timeoutId);
|
|
160
311
|
if (error instanceof ChannlizeError) {
|
|
161
312
|
throw error;
|
|
162
313
|
}
|
|
314
|
+
if (external?.aborted) {
|
|
315
|
+
throw new ChannlizeError("Request aborted", void 0, "ABORTED");
|
|
316
|
+
}
|
|
163
317
|
if (error instanceof Error && error.name === "AbortError") {
|
|
164
318
|
throw new ChannlizeError("Request timeout", 408, "TIMEOUT");
|
|
165
319
|
}
|
|
166
320
|
throw new ChannlizeError(
|
|
167
|
-
`Network error: ${error instanceof Error ? error.message : "Unknown error"}
|
|
321
|
+
`Network error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
322
|
+
void 0,
|
|
323
|
+
"NETWORK_ERROR"
|
|
168
324
|
);
|
|
325
|
+
} finally {
|
|
326
|
+
external?.removeEventListener("abort", onAbort);
|
|
169
327
|
}
|
|
170
328
|
}
|
|
329
|
+
function retryAfterHeader(value) {
|
|
330
|
+
if (!value) return void 0;
|
|
331
|
+
const seconds = Number(value);
|
|
332
|
+
const ms = Number.isFinite(seconds) ? Date.now() + seconds * 1e3 : Date.parse(value);
|
|
333
|
+
return Number.isFinite(ms) ? new Date(ms).toISOString() : void 0;
|
|
334
|
+
}
|
|
171
335
|
async function handleErrorResponse(response) {
|
|
172
336
|
let body = {};
|
|
173
337
|
if (response.headers.get("content-type")?.includes("application/json")) {
|
|
@@ -176,11 +340,27 @@ async function handleErrorResponse(response) {
|
|
|
176
340
|
} catch {
|
|
177
341
|
}
|
|
178
342
|
}
|
|
179
|
-
throw errorFromApi(
|
|
343
|
+
throw errorFromApi(
|
|
344
|
+
parseApiError(response.status, body, response.statusText),
|
|
345
|
+
retryAfterHeader(response.headers.get("retry-after"))
|
|
346
|
+
);
|
|
180
347
|
}
|
|
181
|
-
function errorFromApi(info) {
|
|
348
|
+
function errorFromApi(info, retryAfter2) {
|
|
182
349
|
const { status, message, code } = info;
|
|
183
|
-
|
|
350
|
+
const retryAt = typeof info.body.retryAt === "string" ? info.body.retryAt : retryAfter2;
|
|
351
|
+
let error = agentErrorFor(
|
|
352
|
+
status,
|
|
353
|
+
message,
|
|
354
|
+
code,
|
|
355
|
+
info.body,
|
|
356
|
+
retryAt
|
|
357
|
+
);
|
|
358
|
+
if (error) {
|
|
359
|
+
error.requestId = info.requestId;
|
|
360
|
+
error.details = info.body;
|
|
361
|
+
error.retryAt ??= retryAt;
|
|
362
|
+
return error;
|
|
363
|
+
}
|
|
184
364
|
switch (status) {
|
|
185
365
|
case 401:
|
|
186
366
|
error = new AuthenticationError(message);
|
|
@@ -202,6 +382,7 @@ function errorFromApi(info) {
|
|
|
202
382
|
}
|
|
203
383
|
error.requestId = info.requestId;
|
|
204
384
|
error.details = info.body;
|
|
385
|
+
error.retryAt = retryAt;
|
|
205
386
|
return error;
|
|
206
387
|
}
|
|
207
388
|
|
|
@@ -275,6 +456,1091 @@ function createNotifier(config = {}) {
|
|
|
275
456
|
};
|
|
276
457
|
}
|
|
277
458
|
|
|
459
|
+
// src/agents-governance-types.ts
|
|
460
|
+
var QUESTION_KINDS = ["question", "approval"];
|
|
461
|
+
var QUESTION_URGENCIES = ["low", "normal", "high", "urgent"];
|
|
462
|
+
var QUESTION_STATUSES = [
|
|
463
|
+
"open",
|
|
464
|
+
"answered",
|
|
465
|
+
"expired_default",
|
|
466
|
+
"expired",
|
|
467
|
+
"canceled"
|
|
468
|
+
];
|
|
469
|
+
var QUESTION_WAIT_MAX_SECONDS = 25;
|
|
470
|
+
var TASK_BOARD_STATUSES = [
|
|
471
|
+
"open",
|
|
472
|
+
"in_progress",
|
|
473
|
+
"blocked",
|
|
474
|
+
"done",
|
|
475
|
+
"failed"
|
|
476
|
+
];
|
|
477
|
+
var POLICY_SCOPES = ["org", "team", "agent", "channel"];
|
|
478
|
+
|
|
479
|
+
// src/agents-governance.ts
|
|
480
|
+
var enc = encodeURIComponent;
|
|
481
|
+
var iso = (v) => v instanceof Date ? v.toISOString() : v;
|
|
482
|
+
function sleep(ms, signal) {
|
|
483
|
+
return new Promise((done) => {
|
|
484
|
+
if (signal?.aborted) return done();
|
|
485
|
+
const timer = setTimeout(finish, ms);
|
|
486
|
+
function finish() {
|
|
487
|
+
clearTimeout(timer);
|
|
488
|
+
signal?.removeEventListener("abort", finish);
|
|
489
|
+
done();
|
|
490
|
+
}
|
|
491
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
function aborted() {
|
|
495
|
+
return new ChannlizeError("Request aborted", void 0, "ABORTED");
|
|
496
|
+
}
|
|
497
|
+
function need(value, what, field) {
|
|
498
|
+
if (typeof value !== "string" || !value) {
|
|
499
|
+
throw new ValidationError(`${what} is required`, field);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function createAgentsGovernance(request, defaultAgentId) {
|
|
503
|
+
const agentOf = (agentId) => agentId ?? defaultAgentId;
|
|
504
|
+
const safely = (fn) => {
|
|
505
|
+
try {
|
|
506
|
+
return fn();
|
|
507
|
+
} catch (error) {
|
|
508
|
+
return Promise.reject(error);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
function ask(input) {
|
|
512
|
+
return safely(async () => {
|
|
513
|
+
if (!input || typeof input.question !== "string" || !input.question) {
|
|
514
|
+
throw new ValidationError("ask needs a question", "question");
|
|
515
|
+
}
|
|
516
|
+
const r = await request(
|
|
517
|
+
"POST",
|
|
518
|
+
"/v1/agents/questions",
|
|
519
|
+
{
|
|
520
|
+
body: {
|
|
521
|
+
agent_id: agentOf(input.agentId),
|
|
522
|
+
kind: input.kind,
|
|
523
|
+
question: input.question,
|
|
524
|
+
options: input.options,
|
|
525
|
+
free_text: input.freeText,
|
|
526
|
+
urgency: input.urgency,
|
|
527
|
+
deadline: iso(input.deadline),
|
|
528
|
+
default_option: input.defaultOption,
|
|
529
|
+
channel_id: input.channelId,
|
|
530
|
+
thread_id: input.threadId,
|
|
531
|
+
context: input.context,
|
|
532
|
+
amount: input.amount,
|
|
533
|
+
currency: input.currency,
|
|
534
|
+
resource: input.resource,
|
|
535
|
+
required_roles: input.requiredRoles
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
);
|
|
539
|
+
return r.question;
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
function wait(questionId, options = {}) {
|
|
543
|
+
return safely(async () => {
|
|
544
|
+
need(questionId, "The question id", "questionId");
|
|
545
|
+
const timeoutSec = Math.min(
|
|
546
|
+
QUESTION_WAIT_MAX_SECONDS,
|
|
547
|
+
Math.max(0, options.timeoutSec ?? QUESTION_WAIT_MAX_SECONDS)
|
|
548
|
+
);
|
|
549
|
+
const r = await request(
|
|
550
|
+
"GET",
|
|
551
|
+
`/v1/agents/questions/${enc(questionId)}/wait`,
|
|
552
|
+
{
|
|
553
|
+
query: { agent_id: agentOf(options.agentId), timeout: timeoutSec },
|
|
554
|
+
// The server holds the request for up to timeoutSec.
|
|
555
|
+
timeout: (timeoutSec + 10) * 1e3,
|
|
556
|
+
signal: options.signal
|
|
557
|
+
}
|
|
558
|
+
);
|
|
559
|
+
return {
|
|
560
|
+
question: r.question,
|
|
561
|
+
timedOut: r.timed_out ?? r.question?.status === "open"
|
|
562
|
+
};
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
async function askAndWait(input, options = {}) {
|
|
566
|
+
const { signal } = options;
|
|
567
|
+
const deadline = Date.now() + Math.max(0, options.timeoutMs ?? 6e5);
|
|
568
|
+
if (signal?.aborted) throw aborted();
|
|
569
|
+
let question = await ask(input);
|
|
570
|
+
let failures = 0;
|
|
571
|
+
while (question.status === "open") {
|
|
572
|
+
if (signal?.aborted) throw aborted();
|
|
573
|
+
const left = deadline - Date.now();
|
|
574
|
+
if (left < 1e3) break;
|
|
575
|
+
try {
|
|
576
|
+
const r = await wait(question.id, {
|
|
577
|
+
agentId: input.agentId,
|
|
578
|
+
timeoutSec: Math.min(
|
|
579
|
+
QUESTION_WAIT_MAX_SECONDS,
|
|
580
|
+
Math.floor(left / 1e3)
|
|
581
|
+
),
|
|
582
|
+
signal
|
|
583
|
+
});
|
|
584
|
+
question = r.question;
|
|
585
|
+
failures = 0;
|
|
586
|
+
} catch (error) {
|
|
587
|
+
if (signal?.aborted) throw aborted();
|
|
588
|
+
if (!isRetryable(error)) throw error;
|
|
589
|
+
failures += 1;
|
|
590
|
+
const backoff = retryAfter(error) ?? Math.min(3e4, 1e3 * 2 ** (failures - 1));
|
|
591
|
+
const remaining = deadline - Date.now();
|
|
592
|
+
if (backoff >= remaining) break;
|
|
593
|
+
await sleep(backoff, signal);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (signal?.aborted && question.status === "open") throw aborted();
|
|
597
|
+
return {
|
|
598
|
+
question,
|
|
599
|
+
answer: question.answer ?? null,
|
|
600
|
+
timedOut: question.status === "open"
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
const questionPath = (id) => {
|
|
604
|
+
need(id, "The question id", "questionId");
|
|
605
|
+
return `/v1/agents/questions/${enc(id)}`;
|
|
606
|
+
};
|
|
607
|
+
const taskPath = (id) => {
|
|
608
|
+
need(id, "The task id", "taskId");
|
|
609
|
+
return `/v1/agents/tasks/${enc(id)}`;
|
|
610
|
+
};
|
|
611
|
+
const teamPath = (id) => {
|
|
612
|
+
need(id, "The team id", "teamId");
|
|
613
|
+
return `/v1/agents/teams/${enc(id)}`;
|
|
614
|
+
};
|
|
615
|
+
const agentPath = (id) => {
|
|
616
|
+
need(id, "The agent id", "agentId");
|
|
617
|
+
return enc(id);
|
|
618
|
+
};
|
|
619
|
+
return {
|
|
620
|
+
ask,
|
|
621
|
+
askAndWait,
|
|
622
|
+
questions: {
|
|
623
|
+
get: (id, options = {}) => safely(async () => {
|
|
624
|
+
const r = await request("GET", questionPath(id), {
|
|
625
|
+
query: { agent_id: agentOf(options.agentId) }
|
|
626
|
+
});
|
|
627
|
+
return { question: r.question, escalations: r.escalations ?? [] };
|
|
628
|
+
}),
|
|
629
|
+
list: (options = {}) => safely(async () => {
|
|
630
|
+
const r = await request("GET", "/v1/agents/questions", {
|
|
631
|
+
query: {
|
|
632
|
+
agent_id: agentOf(options.agentId),
|
|
633
|
+
status: options.status,
|
|
634
|
+
kind: options.kind,
|
|
635
|
+
urgency: options.urgency,
|
|
636
|
+
channel_id: options.channelId,
|
|
637
|
+
cursor: options.cursor,
|
|
638
|
+
limit: options.limit
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
return {
|
|
642
|
+
questions: r.questions ?? [],
|
|
643
|
+
nextCursor: r.next_cursor ?? null
|
|
644
|
+
};
|
|
645
|
+
}),
|
|
646
|
+
wait,
|
|
647
|
+
answer: (id, input) => safely(async () => {
|
|
648
|
+
if (!input?.optionId && !input?.text) {
|
|
649
|
+
throw new ValidationError(
|
|
650
|
+
"answer needs optionId or text",
|
|
651
|
+
"optionId"
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
const r = await request(
|
|
655
|
+
"POST",
|
|
656
|
+
`${questionPath(id)}/answer`,
|
|
657
|
+
{
|
|
658
|
+
body: {
|
|
659
|
+
agent_id: agentOf(input.agentId),
|
|
660
|
+
option_id: input.optionId,
|
|
661
|
+
text: input.text,
|
|
662
|
+
comment: input.comment
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
);
|
|
666
|
+
return r.question;
|
|
667
|
+
}),
|
|
668
|
+
cancel: (id, options = {}) => safely(async () => {
|
|
669
|
+
const r = await request(
|
|
670
|
+
"DELETE",
|
|
671
|
+
questionPath(id),
|
|
672
|
+
{ query: { agent_id: agentOf(options.agentId) } }
|
|
673
|
+
);
|
|
674
|
+
return r.question;
|
|
675
|
+
})
|
|
676
|
+
},
|
|
677
|
+
tasks: {
|
|
678
|
+
create: (input) => safely(async () => {
|
|
679
|
+
need(input?.channelId, "channelId", "channelId");
|
|
680
|
+
const r = await request(
|
|
681
|
+
"POST",
|
|
682
|
+
"/v1/agents/tasks",
|
|
683
|
+
{
|
|
684
|
+
body: {
|
|
685
|
+
agent_id: agentOf(input.agentId),
|
|
686
|
+
channel_id: input.channelId,
|
|
687
|
+
text: input.text,
|
|
688
|
+
assignee_agent_id: input.assigneeAgentId,
|
|
689
|
+
due_at: iso(input.dueAt),
|
|
690
|
+
thread_id: input.threadId,
|
|
691
|
+
mentions: input.mentions,
|
|
692
|
+
data: input.data
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
);
|
|
696
|
+
return { task: r.task, deliveries: r.deliveries ?? [] };
|
|
697
|
+
}),
|
|
698
|
+
list: (options = {}) => safely(async () => {
|
|
699
|
+
const r = await request(
|
|
700
|
+
"GET",
|
|
701
|
+
"/v1/agents/tasks",
|
|
702
|
+
{
|
|
703
|
+
query: {
|
|
704
|
+
agent_id: agentOf(options.agentId),
|
|
705
|
+
channel_id: options.channelId,
|
|
706
|
+
assignee_agent_id: options.assigneeAgentId,
|
|
707
|
+
status: options.status,
|
|
708
|
+
limit: options.limit
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
);
|
|
712
|
+
return r.tasks ?? [];
|
|
713
|
+
}),
|
|
714
|
+
board: (channelId, options = {}) => safely(() => {
|
|
715
|
+
need(channelId, "channelId", "channelId");
|
|
716
|
+
return request("GET", "/v1/agents/tasks/board", {
|
|
717
|
+
query: {
|
|
718
|
+
channel_id: channelId,
|
|
719
|
+
assignee_agent_id: options.assigneeAgentId,
|
|
720
|
+
agent_id: agentOf(options.agentId)
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
}),
|
|
724
|
+
get: (taskId, options = {}) => safely(async () => {
|
|
725
|
+
const r = await request("GET", taskPath(taskId), {
|
|
726
|
+
query: { agent_id: agentOf(options.agentId) }
|
|
727
|
+
});
|
|
728
|
+
return { task: r.task, events: r.events ?? [] };
|
|
729
|
+
}),
|
|
730
|
+
update: (taskId, input) => safely(() => {
|
|
731
|
+
if (!input || input.status === void 0 && input.dueAt === void 0) {
|
|
732
|
+
throw new ValidationError("update needs status or dueAt", "status");
|
|
733
|
+
}
|
|
734
|
+
return request("PATCH", taskPath(taskId), {
|
|
735
|
+
body: {
|
|
736
|
+
agent_id: agentOf(input.agentId),
|
|
737
|
+
status: input.status,
|
|
738
|
+
due_at: input.dueAt === null ? null : iso(input.dueAt),
|
|
739
|
+
note: input.note
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
}),
|
|
743
|
+
handoff: (taskId, input) => safely(() => {
|
|
744
|
+
need(input?.toAgentId, "toAgentId", "toAgentId");
|
|
745
|
+
return request(
|
|
746
|
+
"POST",
|
|
747
|
+
`${taskPath(taskId)}/handoff`,
|
|
748
|
+
{
|
|
749
|
+
body: {
|
|
750
|
+
agent_id: agentOf(input.agentId),
|
|
751
|
+
to_agent_id: input.toAgentId,
|
|
752
|
+
reason: input.reason
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
);
|
|
756
|
+
})
|
|
757
|
+
},
|
|
758
|
+
teams: {
|
|
759
|
+
list: () => safely(async () => {
|
|
760
|
+
const r = await request(
|
|
761
|
+
"GET",
|
|
762
|
+
"/v1/agents/teams"
|
|
763
|
+
);
|
|
764
|
+
return r.teams ?? [];
|
|
765
|
+
}),
|
|
766
|
+
get: (teamId) => safely(() => request("GET", teamPath(teamId))),
|
|
767
|
+
create: (input) => safely(async () => {
|
|
768
|
+
const r = await request(
|
|
769
|
+
"POST",
|
|
770
|
+
"/v1/agents/teams",
|
|
771
|
+
{
|
|
772
|
+
body: {
|
|
773
|
+
name: input.name,
|
|
774
|
+
slug: input.slug,
|
|
775
|
+
description: input.description,
|
|
776
|
+
agent_ids: input.agentIds
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
return r.team;
|
|
781
|
+
}),
|
|
782
|
+
update: (teamId, input) => safely(async () => {
|
|
783
|
+
const r = await request(
|
|
784
|
+
"PATCH",
|
|
785
|
+
teamPath(teamId),
|
|
786
|
+
{ body: input }
|
|
787
|
+
);
|
|
788
|
+
return r.team;
|
|
789
|
+
}),
|
|
790
|
+
delete: (teamId) => safely(async () => {
|
|
791
|
+
const r = await request(
|
|
792
|
+
"DELETE",
|
|
793
|
+
teamPath(teamId)
|
|
794
|
+
);
|
|
795
|
+
return { memberships_removed: r.memberships_removed ?? 0 };
|
|
796
|
+
}),
|
|
797
|
+
addMember: (teamId, agentId) => safely(() => {
|
|
798
|
+
need(agentId, "agentId", "agentId");
|
|
799
|
+
return request(
|
|
800
|
+
"POST",
|
|
801
|
+
`${teamPath(teamId)}/members`,
|
|
802
|
+
{ body: { agent_id: agentId } }
|
|
803
|
+
);
|
|
804
|
+
}),
|
|
805
|
+
removeMember: (teamId, agentId) => safely(async () => {
|
|
806
|
+
const r = await request(
|
|
807
|
+
"DELETE",
|
|
808
|
+
`${teamPath(teamId)}/members/${agentPath(agentId)}`
|
|
809
|
+
);
|
|
810
|
+
return { memberships_removed: r.memberships_removed ?? 0 };
|
|
811
|
+
}),
|
|
812
|
+
grantChannel: (teamId, channelId, role) => safely(() => {
|
|
813
|
+
need(channelId, "channelId", "channelId");
|
|
814
|
+
return request("POST", `${teamPath(teamId)}/channels`, {
|
|
815
|
+
body: { channel_id: channelId, role }
|
|
816
|
+
});
|
|
817
|
+
}),
|
|
818
|
+
revokeChannel: (teamId, channelId) => safely(async () => {
|
|
819
|
+
need(channelId, "channelId", "channelId");
|
|
820
|
+
const r = await request(
|
|
821
|
+
"DELETE",
|
|
822
|
+
`${teamPath(teamId)}/channels/${enc(channelId)}`
|
|
823
|
+
);
|
|
824
|
+
return { memberships_removed: r.memberships_removed ?? 0 };
|
|
825
|
+
})
|
|
826
|
+
},
|
|
827
|
+
policies: {
|
|
828
|
+
list: (options = {}) => safely(async () => {
|
|
829
|
+
const r = await request(
|
|
830
|
+
"GET",
|
|
831
|
+
"/v1/agents/policies",
|
|
832
|
+
{ query: { scope: options.scope } }
|
|
833
|
+
);
|
|
834
|
+
return r.policies ?? [];
|
|
835
|
+
}),
|
|
836
|
+
get: (policyId) => safely(async () => {
|
|
837
|
+
need(policyId, "The policy id", "policyId");
|
|
838
|
+
const r = await request(
|
|
839
|
+
"GET",
|
|
840
|
+
`/v1/agents/policies/${enc(policyId)}`
|
|
841
|
+
);
|
|
842
|
+
return r.policy;
|
|
843
|
+
}),
|
|
844
|
+
put: (input) => safely(async () => {
|
|
845
|
+
const r = await request(
|
|
846
|
+
"PUT",
|
|
847
|
+
"/v1/agents/policies",
|
|
848
|
+
{
|
|
849
|
+
body: {
|
|
850
|
+
scope: input.scope,
|
|
851
|
+
scope_id: input.scopeId,
|
|
852
|
+
rules: input.rules,
|
|
853
|
+
enabled: input.enabled
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
);
|
|
857
|
+
return r.policy;
|
|
858
|
+
}),
|
|
859
|
+
delete: (policyId) => safely(async () => {
|
|
860
|
+
need(policyId, "The policy id", "policyId");
|
|
861
|
+
await request("DELETE", `/v1/agents/policies/${enc(policyId)}`);
|
|
862
|
+
}),
|
|
863
|
+
preview: (agentId, options = {}) => safely(() => {
|
|
864
|
+
need(agentId, "agentId", "agentId");
|
|
865
|
+
return request(
|
|
866
|
+
"GET",
|
|
867
|
+
"/v1/agents/policies/preview",
|
|
868
|
+
{ query: { agent_id: agentId, channel_id: options.channelId } }
|
|
869
|
+
);
|
|
870
|
+
})
|
|
871
|
+
},
|
|
872
|
+
orgChart: {
|
|
873
|
+
get: (options = {}) => request("GET", "/v1/agents/org-chart", {
|
|
874
|
+
query: {
|
|
875
|
+
team_id: options.teamId,
|
|
876
|
+
channel_id: options.channelId,
|
|
877
|
+
include_retired: options.includeRetired ? "true" : void 0
|
|
878
|
+
}
|
|
879
|
+
}),
|
|
880
|
+
setManager: (agentId, input) => safely(async () => {
|
|
881
|
+
if (input?.managerAgentId && input?.managerUserId) {
|
|
882
|
+
throw new ValidationError(
|
|
883
|
+
"Set managerAgentId or managerUserId, not both",
|
|
884
|
+
"managerAgentId"
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
const r = await request(
|
|
888
|
+
"PUT",
|
|
889
|
+
`/v1/agents/org-chart/${agentPath(agentId)}/manager`,
|
|
890
|
+
{
|
|
891
|
+
body: {
|
|
892
|
+
manager_agent_id: input?.managerAgentId ?? null,
|
|
893
|
+
manager_user_id: input?.managerUserId ?? null
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
);
|
|
897
|
+
return r.agent;
|
|
898
|
+
}),
|
|
899
|
+
setOwner: (agentId, ownerUserId) => safely(async () => {
|
|
900
|
+
const r = await request(
|
|
901
|
+
"PUT",
|
|
902
|
+
`/v1/agents/org-chart/${agentPath(agentId)}/owner`,
|
|
903
|
+
{ body: { owner_user_id: ownerUserId ?? null } }
|
|
904
|
+
);
|
|
905
|
+
return r.agent;
|
|
906
|
+
})
|
|
907
|
+
},
|
|
908
|
+
onboarding: (agentId) => safely(
|
|
909
|
+
() => request(
|
|
910
|
+
"GET",
|
|
911
|
+
`/v1/agents/${agentPath(agentId)}/onboarding`
|
|
912
|
+
)
|
|
913
|
+
),
|
|
914
|
+
setStatus: (agentId, status, options = {}) => safely(
|
|
915
|
+
() => request(
|
|
916
|
+
"POST",
|
|
917
|
+
`/v1/agents/${agentPath(agentId)}/status`,
|
|
918
|
+
{ body: { status, reason: options.reason } }
|
|
919
|
+
)
|
|
920
|
+
)
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
var QUESTION_OUTCOMES = /* @__PURE__ */ new Set([
|
|
924
|
+
"question.answered",
|
|
925
|
+
"question.expired",
|
|
926
|
+
"question.canceled"
|
|
927
|
+
]);
|
|
928
|
+
function isAgentMessageWebhook(payload) {
|
|
929
|
+
return payload?.event === "agent.message";
|
|
930
|
+
}
|
|
931
|
+
function isQuestionOutcomeWebhook(payload) {
|
|
932
|
+
return Boolean(payload && QUESTION_OUTCOMES.has(payload.event));
|
|
933
|
+
}
|
|
934
|
+
function isQuestionEscalatedWebhook(payload) {
|
|
935
|
+
return payload?.event === "question.escalated";
|
|
936
|
+
}
|
|
937
|
+
function isQuestionOutcomeMessage(message) {
|
|
938
|
+
const data = message?.data;
|
|
939
|
+
return Boolean(
|
|
940
|
+
data && typeof data.type === "string" && QUESTION_OUTCOMES.has(data.type) && data.question && typeof data.question === "object"
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
function isTaskHandoffMessage(message) {
|
|
944
|
+
const handoff = message?.data?.handoff;
|
|
945
|
+
return Boolean(
|
|
946
|
+
message?.type === "status" && handoff && typeof handoff === "object" && typeof handoff.to_agent_id === "string"
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// src/agents-observability.ts
|
|
951
|
+
var enc2 = encodeURIComponent;
|
|
952
|
+
function day(v) {
|
|
953
|
+
return v instanceof Date ? v.toISOString().slice(0, 10) : v;
|
|
954
|
+
}
|
|
955
|
+
function iso2(v) {
|
|
956
|
+
return v instanceof Date ? v.toISOString() : v;
|
|
957
|
+
}
|
|
958
|
+
function list(v) {
|
|
959
|
+
return Array.isArray(v) ? v.join(",") : v;
|
|
960
|
+
}
|
|
961
|
+
function need2(value, what, field) {
|
|
962
|
+
if (typeof value !== "string" || !value) {
|
|
963
|
+
throw new ValidationError(`${what} is required`, field);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
function exportFilterFields(f = {}) {
|
|
967
|
+
return {
|
|
968
|
+
from: iso2(f.from),
|
|
969
|
+
to: iso2(f.to),
|
|
970
|
+
agent_id: f.agentId,
|
|
971
|
+
channel_id: f.channelId,
|
|
972
|
+
thread_id: f.threadId,
|
|
973
|
+
type: f.type,
|
|
974
|
+
status: list(f.status),
|
|
975
|
+
action: f.action,
|
|
976
|
+
actor_type: f.actorType,
|
|
977
|
+
actor_id: f.actorId,
|
|
978
|
+
target_type: f.targetType,
|
|
979
|
+
target_id: f.targetId
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
function defined(o) {
|
|
983
|
+
return Object.fromEntries(
|
|
984
|
+
Object.entries(o).filter(([, v]) => v !== void 0)
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
function filenameOf(disposition) {
|
|
988
|
+
const m = disposition ? /filename="?([^";]+)"?/i.exec(disposition) : null;
|
|
989
|
+
return m ? m[1] : null;
|
|
990
|
+
}
|
|
991
|
+
async function* paginate(page, max) {
|
|
992
|
+
const limit = max ?? Infinity;
|
|
993
|
+
if (limit <= 0) return;
|
|
994
|
+
let cursor;
|
|
995
|
+
let seen = 0;
|
|
996
|
+
for (; ; ) {
|
|
997
|
+
const r = await page(cursor);
|
|
998
|
+
for (const item of r.items) {
|
|
999
|
+
yield item;
|
|
1000
|
+
if (++seen >= limit) return;
|
|
1001
|
+
}
|
|
1002
|
+
if (!r.nextCursor || r.nextCursor === cursor) return;
|
|
1003
|
+
cursor = r.nextCursor;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
function createAgentsObservability(request, requestText2) {
|
|
1007
|
+
const safely = (fn) => {
|
|
1008
|
+
try {
|
|
1009
|
+
return fn();
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
return Promise.reject(error);
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
const rangeQuery = (f = {}) => ({
|
|
1015
|
+
from: day(f.from),
|
|
1016
|
+
to: day(f.to),
|
|
1017
|
+
channel_id: f.channelId,
|
|
1018
|
+
agent_id: f.agentId
|
|
1019
|
+
});
|
|
1020
|
+
function edge(source, target, options = {}) {
|
|
1021
|
+
return safely(async () => {
|
|
1022
|
+
need2(source, "The source node id", "source");
|
|
1023
|
+
need2(target, "The target node id", "target");
|
|
1024
|
+
const r = await request("GET", "/v1/agents/insights/graph/edge", {
|
|
1025
|
+
query: {
|
|
1026
|
+
source,
|
|
1027
|
+
target,
|
|
1028
|
+
from: day(options.from),
|
|
1029
|
+
to: day(options.to),
|
|
1030
|
+
channel_id: options.channelId,
|
|
1031
|
+
cursor: options.cursor,
|
|
1032
|
+
limit: options.limit
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
return {
|
|
1036
|
+
range: r.range,
|
|
1037
|
+
source: r.source,
|
|
1038
|
+
target: r.target,
|
|
1039
|
+
threads: r.threads ?? [],
|
|
1040
|
+
nextCursor: r.next_cursor ?? null
|
|
1041
|
+
};
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
function search(options) {
|
|
1045
|
+
return safely(async () => {
|
|
1046
|
+
if (!options || typeof options.q !== "string" || options.q.trim().length < 2) {
|
|
1047
|
+
throw new ValidationError("search needs q, at least 2 characters", "q");
|
|
1048
|
+
}
|
|
1049
|
+
const r = await request("GET", "/v1/agents/search", {
|
|
1050
|
+
query: {
|
|
1051
|
+
q: options.q,
|
|
1052
|
+
agent_id: options.agentId,
|
|
1053
|
+
channel_id: options.channelId,
|
|
1054
|
+
type: options.type,
|
|
1055
|
+
include: list(options.include),
|
|
1056
|
+
from: iso2(options.from),
|
|
1057
|
+
to: iso2(options.to),
|
|
1058
|
+
cursor: options.cursor,
|
|
1059
|
+
limit: options.limit
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
return {
|
|
1063
|
+
results: r.results ?? [],
|
|
1064
|
+
nextCursor: r.next_cursor ?? null,
|
|
1065
|
+
window: r.window
|
|
1066
|
+
};
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
insights: {
|
|
1071
|
+
graph(filters = {}) {
|
|
1072
|
+
return request("GET", "/v1/agents/insights/graph", {
|
|
1073
|
+
query: {
|
|
1074
|
+
...rangeQuery(filters),
|
|
1075
|
+
team_id: filters.teamId,
|
|
1076
|
+
limit: filters.limit,
|
|
1077
|
+
edge_limit: filters.edgeLimit
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
},
|
|
1081
|
+
edge,
|
|
1082
|
+
edgeAll(source, target, options = {}) {
|
|
1083
|
+
const { max, ...rest } = options;
|
|
1084
|
+
return paginate(async (cursor) => {
|
|
1085
|
+
const page = await edge(source, target, { ...rest, cursor });
|
|
1086
|
+
return { items: page.threads, nextCursor: page.nextCursor };
|
|
1087
|
+
}, max);
|
|
1088
|
+
},
|
|
1089
|
+
cost(options = {}) {
|
|
1090
|
+
return request("GET", "/v1/agents/insights/cost", {
|
|
1091
|
+
query: {
|
|
1092
|
+
...rangeQuery(options),
|
|
1093
|
+
group_by: options.groupBy,
|
|
1094
|
+
team_id: options.teamId,
|
|
1095
|
+
limit: options.limit
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
},
|
|
1099
|
+
metrics(filters = {}) {
|
|
1100
|
+
return request("GET", "/v1/agents/insights/metrics", {
|
|
1101
|
+
query: rangeQuery(filters)
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
},
|
|
1105
|
+
search,
|
|
1106
|
+
searchAll(options) {
|
|
1107
|
+
const { max, ...rest } = options ?? {};
|
|
1108
|
+
return paginate(async (cursor) => {
|
|
1109
|
+
const page = await search({ ...rest, cursor });
|
|
1110
|
+
return { items: page.results, nextCursor: page.nextCursor };
|
|
1111
|
+
}, max);
|
|
1112
|
+
},
|
|
1113
|
+
exports: {
|
|
1114
|
+
download(kind, format = "csv", filters = {}) {
|
|
1115
|
+
return safely(async () => {
|
|
1116
|
+
need2(kind, "The export kind", "kind");
|
|
1117
|
+
const res = await requestText2("/v1/agents/export", {
|
|
1118
|
+
query: { kind, format, ...exportFilterFields(filters) }
|
|
1119
|
+
});
|
|
1120
|
+
return {
|
|
1121
|
+
body: res.body,
|
|
1122
|
+
rows: Number(res.headers.get("x-export-rows") ?? 0) || 0,
|
|
1123
|
+
truncated: res.headers.get("x-export-truncated") === "true",
|
|
1124
|
+
contentType: res.headers.get("content-type") ?? "",
|
|
1125
|
+
filename: filenameOf(res.headers.get("content-disposition"))
|
|
1126
|
+
};
|
|
1127
|
+
});
|
|
1128
|
+
},
|
|
1129
|
+
start(input) {
|
|
1130
|
+
return safely(async () => {
|
|
1131
|
+
need2(input?.kind, "The export kind", "kind");
|
|
1132
|
+
const r = await request("POST", "/v1/agents/export", {
|
|
1133
|
+
body: {
|
|
1134
|
+
kind: input.kind,
|
|
1135
|
+
format: input.format,
|
|
1136
|
+
filters: defined(exportFilterFields(input.filters))
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
return { exportId: r.export_id, status: r.status, export: r.export };
|
|
1140
|
+
});
|
|
1141
|
+
},
|
|
1142
|
+
get(exportId) {
|
|
1143
|
+
return safely(async () => {
|
|
1144
|
+
need2(exportId, "The export id", "exportId");
|
|
1145
|
+
const r = await request(
|
|
1146
|
+
"GET",
|
|
1147
|
+
`/v1/agents/export/${enc2(exportId)}`
|
|
1148
|
+
);
|
|
1149
|
+
return r.export;
|
|
1150
|
+
});
|
|
1151
|
+
},
|
|
1152
|
+
async list(options = {}) {
|
|
1153
|
+
const r = await request(
|
|
1154
|
+
"GET",
|
|
1155
|
+
"/v1/agents/export",
|
|
1156
|
+
{ query: { limit: options.limit } }
|
|
1157
|
+
);
|
|
1158
|
+
return r.exports ?? [];
|
|
1159
|
+
}
|
|
1160
|
+
},
|
|
1161
|
+
telemetry: {
|
|
1162
|
+
async getOtel() {
|
|
1163
|
+
return (await request("GET", "/v1/agents/telemetry/otel")).otel;
|
|
1164
|
+
},
|
|
1165
|
+
async setOtel(input) {
|
|
1166
|
+
const r = await request(
|
|
1167
|
+
"PUT",
|
|
1168
|
+
"/v1/agents/telemetry/otel",
|
|
1169
|
+
{
|
|
1170
|
+
body: {
|
|
1171
|
+
enabled: input?.enabled,
|
|
1172
|
+
endpoint_url: input?.endpointUrl,
|
|
1173
|
+
headers: input?.headers
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
);
|
|
1177
|
+
return r.otel;
|
|
1178
|
+
},
|
|
1179
|
+
testOtel() {
|
|
1180
|
+
return request(
|
|
1181
|
+
"POST",
|
|
1182
|
+
"/v1/agents/telemetry/otel/test"
|
|
1183
|
+
);
|
|
1184
|
+
},
|
|
1185
|
+
async getWebhook() {
|
|
1186
|
+
return (await request(
|
|
1187
|
+
"GET",
|
|
1188
|
+
"/v1/agents/telemetry/webhook"
|
|
1189
|
+
)).webhook;
|
|
1190
|
+
},
|
|
1191
|
+
setWebhook(input) {
|
|
1192
|
+
return request(
|
|
1193
|
+
"PUT",
|
|
1194
|
+
"/v1/agents/telemetry/webhook",
|
|
1195
|
+
{
|
|
1196
|
+
body: {
|
|
1197
|
+
enabled: input?.enabled,
|
|
1198
|
+
url: input?.url,
|
|
1199
|
+
rotate_secret: input?.rotateSecret
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
);
|
|
1203
|
+
},
|
|
1204
|
+
testWebhook() {
|
|
1205
|
+
return request(
|
|
1206
|
+
"POST",
|
|
1207
|
+
"/v1/agents/telemetry/webhook/test"
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// src/agents.ts
|
|
1215
|
+
function resolve(config) {
|
|
1216
|
+
const env = readChannlizeEnv();
|
|
1217
|
+
const apiKey = config.apiKey ?? env.serverApiKey ?? env.apiKey;
|
|
1218
|
+
if (!apiKey) {
|
|
1219
|
+
throw new ValidationError(
|
|
1220
|
+
"An agent API key is required: pass apiKey or set CHANNLIZE_API_KEY (or CHANNLIZE_SERVER_API_KEY)",
|
|
1221
|
+
"apiKey"
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
if (keyKind(apiKey) === "widget") {
|
|
1225
|
+
throw new ValidationError(
|
|
1226
|
+
"Agents need an agent key or a multi-agent key (ch_live_\u2026, agents:send / agents:read scopes), not the public widget key",
|
|
1227
|
+
"apiKey"
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
return {
|
|
1231
|
+
apiKey,
|
|
1232
|
+
agentId: config.agentId ?? env.agentId,
|
|
1233
|
+
baseUrl: (config.baseUrl ?? env.baseUrl ?? DEFAULT_BASE_URL).replace(
|
|
1234
|
+
/\/+$/,
|
|
1235
|
+
""
|
|
1236
|
+
),
|
|
1237
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
var enc3 = encodeURIComponent;
|
|
1241
|
+
function isFatal(error) {
|
|
1242
|
+
return error instanceof ChannlizeError && [400, 401, 403, 404].includes(error.statusCode ?? 0);
|
|
1243
|
+
}
|
|
1244
|
+
function sleep2(ms, signal) {
|
|
1245
|
+
return new Promise((done) => {
|
|
1246
|
+
if (signal?.aborted) return done();
|
|
1247
|
+
const timer = setTimeout(finish, ms);
|
|
1248
|
+
function finish() {
|
|
1249
|
+
clearTimeout(timer);
|
|
1250
|
+
signal?.removeEventListener("abort", finish);
|
|
1251
|
+
done();
|
|
1252
|
+
}
|
|
1253
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
function createAgentsClient(config = {}) {
|
|
1257
|
+
const cfg = resolve(config);
|
|
1258
|
+
const request = async (method, path, options = {}) => {
|
|
1259
|
+
const params = new URLSearchParams();
|
|
1260
|
+
for (const [k, v] of Object.entries(options.query ?? {})) {
|
|
1261
|
+
if (v !== void 0 && v !== "") params.set(k, String(v));
|
|
1262
|
+
}
|
|
1263
|
+
const qs = params.toString();
|
|
1264
|
+
const http = { ...cfg, timeout: options.timeout ?? cfg.timeout };
|
|
1265
|
+
const res = await requestJson(http, `${path}${qs ? `?${qs}` : ""}`, {
|
|
1266
|
+
method,
|
|
1267
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}` },
|
|
1268
|
+
body: options.body === void 0 ? void 0 : JSON.stringify(options.body),
|
|
1269
|
+
signal: options.signal
|
|
1270
|
+
});
|
|
1271
|
+
return res;
|
|
1272
|
+
};
|
|
1273
|
+
const fetchText = (path, options = {}) => {
|
|
1274
|
+
const params = new URLSearchParams();
|
|
1275
|
+
for (const [k, v] of Object.entries(options.query ?? {})) {
|
|
1276
|
+
if (v !== void 0 && v !== "") params.set(k, String(v));
|
|
1277
|
+
}
|
|
1278
|
+
const qs = params.toString();
|
|
1279
|
+
return requestText(cfg, `${path}${qs ? `?${qs}` : ""}`, {
|
|
1280
|
+
method: "GET",
|
|
1281
|
+
headers: { Authorization: `Bearer ${cfg.apiKey}` }
|
|
1282
|
+
});
|
|
1283
|
+
};
|
|
1284
|
+
function agentFor(agentId, what) {
|
|
1285
|
+
const id = agentId ?? cfg.agentId;
|
|
1286
|
+
if (!id) {
|
|
1287
|
+
throw new ValidationError(
|
|
1288
|
+
`${what} needs an agent id: pass it or set agentId (CHANNLIZE_AGENT_ID)`,
|
|
1289
|
+
"agentId"
|
|
1290
|
+
);
|
|
1291
|
+
}
|
|
1292
|
+
return id;
|
|
1293
|
+
}
|
|
1294
|
+
async function inbox(agentId, options = {}) {
|
|
1295
|
+
const id = agentFor(agentId, "inbox");
|
|
1296
|
+
const r = await request("GET", `/v1/agents/${enc3(id)}/inbox`, {
|
|
1297
|
+
query: { cursor: options.cursor, limit: options.limit }
|
|
1298
|
+
});
|
|
1299
|
+
return {
|
|
1300
|
+
messages: r.messages ?? [],
|
|
1301
|
+
nextCursor: r.next_cursor ?? null,
|
|
1302
|
+
hasMore: Boolean(r.has_more)
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
async function ack(agentId, input) {
|
|
1306
|
+
const id = agentFor(agentId, "ack");
|
|
1307
|
+
let body;
|
|
1308
|
+
if ("upTo" in input && input.upTo) body = { up_to: input.upTo };
|
|
1309
|
+
else if ("ids" in input && input.ids?.length) body = { ids: input.ids };
|
|
1310
|
+
else {
|
|
1311
|
+
throw new ValidationError(
|
|
1312
|
+
"ack needs upTo or a non-empty ids list",
|
|
1313
|
+
"ids"
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
return request("POST", `/v1/agents/${enc3(id)}/inbox/ack`, {
|
|
1317
|
+
body
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
async function poll(agentId, handler, options = {}) {
|
|
1321
|
+
const id = agentFor(agentId, "poll");
|
|
1322
|
+
const interval = Math.max(0, options.intervalMs ?? 5e3);
|
|
1323
|
+
const maxBackoff = Math.max(interval, options.maxBackoffMs ?? 6e4);
|
|
1324
|
+
const { signal } = options;
|
|
1325
|
+
let failures = 0;
|
|
1326
|
+
while (!signal?.aborted) {
|
|
1327
|
+
let failed = false;
|
|
1328
|
+
let more = false;
|
|
1329
|
+
try {
|
|
1330
|
+
const page = await inbox(id, { limit: options.limit });
|
|
1331
|
+
more = page.hasMore;
|
|
1332
|
+
const done = [];
|
|
1333
|
+
try {
|
|
1334
|
+
for (const item of page.messages) {
|
|
1335
|
+
if (signal?.aborted) break;
|
|
1336
|
+
try {
|
|
1337
|
+
await handler(item.message, item);
|
|
1338
|
+
} catch (error) {
|
|
1339
|
+
failed = true;
|
|
1340
|
+
options.onError?.(error, item);
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
done.push(item.delivery_id);
|
|
1344
|
+
}
|
|
1345
|
+
} finally {
|
|
1346
|
+
if (done.length) await ack(id, { ids: done });
|
|
1347
|
+
}
|
|
1348
|
+
} catch (error) {
|
|
1349
|
+
if (isFatal(error)) throw error;
|
|
1350
|
+
failed = true;
|
|
1351
|
+
options.onError?.(error);
|
|
1352
|
+
}
|
|
1353
|
+
if (failed) {
|
|
1354
|
+
failures += 1;
|
|
1355
|
+
const backoff = Math.min(
|
|
1356
|
+
maxBackoff,
|
|
1357
|
+
(interval || 1e3) * 2 ** (failures - 1)
|
|
1358
|
+
);
|
|
1359
|
+
await sleep2(backoff, signal);
|
|
1360
|
+
} else {
|
|
1361
|
+
failures = 0;
|
|
1362
|
+
if (!more) await sleep2(interval, signal);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
const channelRead = (options = {}) => ({
|
|
1367
|
+
agent_id: options.agentId ?? cfg.agentId
|
|
1368
|
+
});
|
|
1369
|
+
return {
|
|
1370
|
+
...createAgentsGovernance(request, cfg.agentId),
|
|
1371
|
+
...createAgentsObservability(request, fetchText),
|
|
1372
|
+
agentId: cfg.agentId,
|
|
1373
|
+
send(input) {
|
|
1374
|
+
if (!input || typeof input.channel !== "string" || !input.channel) {
|
|
1375
|
+
return Promise.reject(
|
|
1376
|
+
new ValidationError("send needs a channel id", "channel")
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
return request("POST", "/v1/agents/messages", {
|
|
1380
|
+
body: {
|
|
1381
|
+
agent_id: input.agentId ?? cfg.agentId,
|
|
1382
|
+
channel_id: input.channel,
|
|
1383
|
+
type: input.type,
|
|
1384
|
+
text: input.text,
|
|
1385
|
+
thread_id: input.threadId,
|
|
1386
|
+
mentions: input.mentions,
|
|
1387
|
+
data: input.data,
|
|
1388
|
+
tokens: input.tokens,
|
|
1389
|
+
cost: input.cost,
|
|
1390
|
+
task_id: input.taskId,
|
|
1391
|
+
task_status: input.taskStatus
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
},
|
|
1395
|
+
inbox,
|
|
1396
|
+
ack,
|
|
1397
|
+
poll,
|
|
1398
|
+
async list(options = {}) {
|
|
1399
|
+
const r = await request("GET", "/v1/agents", {
|
|
1400
|
+
query: { include_retired: options.includeRetired || void 0 }
|
|
1401
|
+
});
|
|
1402
|
+
return r.agents ?? [];
|
|
1403
|
+
},
|
|
1404
|
+
get(agentId) {
|
|
1405
|
+
return request("GET", `/v1/agents/${enc3(agentId)}`);
|
|
1406
|
+
},
|
|
1407
|
+
channels: {
|
|
1408
|
+
async list(options = {}) {
|
|
1409
|
+
const r = await request(
|
|
1410
|
+
"GET",
|
|
1411
|
+
"/v1/agents/channels",
|
|
1412
|
+
{ query: { include_archived: options.includeArchived || void 0 } }
|
|
1413
|
+
);
|
|
1414
|
+
return r.channels ?? [];
|
|
1415
|
+
},
|
|
1416
|
+
get(channelId, options) {
|
|
1417
|
+
return request("GET", `/v1/agents/channels/${enc3(channelId)}`, {
|
|
1418
|
+
query: channelRead(options)
|
|
1419
|
+
});
|
|
1420
|
+
},
|
|
1421
|
+
async members(channelId, options) {
|
|
1422
|
+
const r = await request(
|
|
1423
|
+
"GET",
|
|
1424
|
+
`/v1/agents/channels/${enc3(channelId)}/members`,
|
|
1425
|
+
{ query: channelRead(options) }
|
|
1426
|
+
);
|
|
1427
|
+
return r.members ?? [];
|
|
1428
|
+
},
|
|
1429
|
+
async messages(channelId, options = {}) {
|
|
1430
|
+
const r = await request("GET", `/v1/agents/channels/${enc3(channelId)}/messages`, {
|
|
1431
|
+
query: {
|
|
1432
|
+
...channelRead(options),
|
|
1433
|
+
cursor: options.cursor,
|
|
1434
|
+
after: options.after,
|
|
1435
|
+
limit: options.limit,
|
|
1436
|
+
sender_agent_id: options.senderAgentId,
|
|
1437
|
+
type: options.type,
|
|
1438
|
+
thread_id: options.threadId
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
return {
|
|
1442
|
+
channel: r.channel,
|
|
1443
|
+
messages: r.messages ?? [],
|
|
1444
|
+
nextCursor: r.next_cursor ?? null,
|
|
1445
|
+
latestCursor: r.latest_cursor ?? null,
|
|
1446
|
+
hasMore: Boolean(r.has_more)
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
var NODE_CRYPTO = "node:crypto";
|
|
1453
|
+
async function getSubtle() {
|
|
1454
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1455
|
+
if (subtle) return subtle;
|
|
1456
|
+
try {
|
|
1457
|
+
const mod = await import(
|
|
1458
|
+
/* webpackIgnore: true */
|
|
1459
|
+
/* @vite-ignore */
|
|
1460
|
+
NODE_CRYPTO
|
|
1461
|
+
);
|
|
1462
|
+
if (mod.webcrypto?.subtle) return mod.webcrypto.subtle;
|
|
1463
|
+
} catch {
|
|
1464
|
+
}
|
|
1465
|
+
throw new ChannlizeError(
|
|
1466
|
+
"Webhook signature checks need the Web Crypto API (globalThis.crypto.subtle, or node:crypto webcrypto on Node.js 18)"
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
function hexToBytes(hex) {
|
|
1470
|
+
if (hex.length === 0 || hex.length % 2 !== 0 || /[^0-9a-f]/i.test(hex)) {
|
|
1471
|
+
return null;
|
|
1472
|
+
}
|
|
1473
|
+
const out = new Uint8Array(hex.length / 2);
|
|
1474
|
+
for (let i = 0; i < out.length; i++) {
|
|
1475
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1476
|
+
}
|
|
1477
|
+
return out;
|
|
1478
|
+
}
|
|
1479
|
+
function toBytes(body) {
|
|
1480
|
+
if (typeof body === "string") return new TextEncoder().encode(body);
|
|
1481
|
+
const copy = new Uint8Array(body.byteLength);
|
|
1482
|
+
copy.set(body);
|
|
1483
|
+
return copy;
|
|
1484
|
+
}
|
|
1485
|
+
async function verifyAgentWebhook(rawBody, signatureHeader, secret) {
|
|
1486
|
+
if (!secret)
|
|
1487
|
+
throw new ValidationError("The agent secret is required", "secret");
|
|
1488
|
+
return verifyHmacSha256(rawBody, signatureHeader, secret);
|
|
1489
|
+
}
|
|
1490
|
+
async function verifyHmacSha256(rawBody, signatureHeader, secret) {
|
|
1491
|
+
const header = Array.isArray(signatureHeader) ? signatureHeader[0] : signatureHeader;
|
|
1492
|
+
const m = /^sha256=([0-9a-f]+)$/i.exec(header?.trim() ?? "");
|
|
1493
|
+
const expected = m ? hexToBytes(m[1]) : null;
|
|
1494
|
+
if (!expected || expected.length !== 32) return false;
|
|
1495
|
+
const subtle = await getSubtle();
|
|
1496
|
+
const key = await subtle.importKey(
|
|
1497
|
+
"raw",
|
|
1498
|
+
new TextEncoder().encode(secret),
|
|
1499
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1500
|
+
false,
|
|
1501
|
+
["verify"]
|
|
1502
|
+
);
|
|
1503
|
+
return subtle.verify("HMAC", key, expected, toBytes(rawBody));
|
|
1504
|
+
}
|
|
1505
|
+
async function parseAgentWebhook(rawBody, signatureHeader, secret) {
|
|
1506
|
+
return await parseAgentWebhookEvent(
|
|
1507
|
+
rawBody,
|
|
1508
|
+
signatureHeader,
|
|
1509
|
+
secret
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
async function parseAgentWebhookEvent(rawBody, signatureHeader, secret) {
|
|
1513
|
+
if (!await verifyAgentWebhook(rawBody, signatureHeader, secret)) {
|
|
1514
|
+
throw new ChannlizeError(
|
|
1515
|
+
"Invalid agent webhook signature",
|
|
1516
|
+
401,
|
|
1517
|
+
"INVALID_SIGNATURE"
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1520
|
+
const text = typeof rawBody === "string" ? rawBody : new TextDecoder().decode(rawBody);
|
|
1521
|
+
return JSON.parse(text);
|
|
1522
|
+
}
|
|
1523
|
+
async function verifyTelemetryWebhook(rawBody, signatureHeader, secret) {
|
|
1524
|
+
if (!secret) {
|
|
1525
|
+
throw new ValidationError(
|
|
1526
|
+
"The telemetry webhook secret is required",
|
|
1527
|
+
"secret"
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
return verifyHmacSha256(rawBody, signatureHeader, secret);
|
|
1531
|
+
}
|
|
1532
|
+
async function parseTelemetryWebhook(rawBody, signatureHeader, secret) {
|
|
1533
|
+
if (!await verifyTelemetryWebhook(rawBody, signatureHeader, secret)) {
|
|
1534
|
+
throw new ChannlizeError(
|
|
1535
|
+
"Invalid telemetry webhook signature",
|
|
1536
|
+
401,
|
|
1537
|
+
"INVALID_SIGNATURE"
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
const text = typeof rawBody === "string" ? rawBody : new TextDecoder().decode(rawBody);
|
|
1541
|
+
return JSON.parse(text);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
278
1544
|
// src/client.ts
|
|
279
1545
|
var ChannlizeClient = class {
|
|
280
1546
|
config;
|
|
@@ -292,6 +1558,26 @@ var ChannlizeClient = class {
|
|
|
292
1558
|
throw new ValidationError("Widget key is required");
|
|
293
1559
|
}
|
|
294
1560
|
}
|
|
1561
|
+
agentsClient;
|
|
1562
|
+
/**
|
|
1563
|
+
* Channlize for Agents (`agents.send`, `agents.inbox`, `agents.poll`, ...),
|
|
1564
|
+
* authenticated with `serverApiKey`: an agent key or a multi-agent key.
|
|
1565
|
+
* Same as `createAgentsClient({ apiKey: serverApiKey, baseUrl, timeout })`.
|
|
1566
|
+
*/
|
|
1567
|
+
get agents() {
|
|
1568
|
+
if (!this.config.serverApiKey) {
|
|
1569
|
+
throw new ValidationError(
|
|
1570
|
+
"serverApiKey (an agent or multi-agent key) is required for agents",
|
|
1571
|
+
"serverApiKey"
|
|
1572
|
+
);
|
|
1573
|
+
}
|
|
1574
|
+
this.agentsClient ??= createAgentsClient({
|
|
1575
|
+
apiKey: this.config.serverApiKey,
|
|
1576
|
+
baseUrl: this.config.baseUrl,
|
|
1577
|
+
timeout: this.config.timeout
|
|
1578
|
+
});
|
|
1579
|
+
return this.agentsClient;
|
|
1580
|
+
}
|
|
295
1581
|
makeRequest(endpoint, options = {}) {
|
|
296
1582
|
return requestJson(this.config, endpoint, options);
|
|
297
1583
|
}
|
|
@@ -447,9 +1733,9 @@ var ChannlizeClient = class {
|
|
|
447
1733
|
* plan's limit for the billing period) resolves with `success: false`
|
|
448
1734
|
* and `dropped` instead of throwing, with one console warning per client.
|
|
449
1735
|
*/
|
|
450
|
-
async nonFatalTracking(
|
|
1736
|
+
async nonFatalTracking(send2) {
|
|
451
1737
|
try {
|
|
452
|
-
return await
|
|
1738
|
+
return await send2();
|
|
453
1739
|
} catch (error) {
|
|
454
1740
|
if (!(error instanceof RateLimitError)) throw error;
|
|
455
1741
|
const dropped = error.code === "PLAN_LIMIT_REACHED" ? "plan_limit" : "rate_limit";
|
|
@@ -506,21 +1792,98 @@ var ChannlizeClient = class {
|
|
|
506
1792
|
);
|
|
507
1793
|
}
|
|
508
1794
|
};
|
|
1795
|
+
|
|
1796
|
+
// src/agents-types.ts
|
|
1797
|
+
var AGENT_STATUSES = ["active", "paused", "retired"];
|
|
1798
|
+
var AGENT_DELIVERY_MODES = ["webhook", "inbox", "a2a"];
|
|
1799
|
+
var AGENT_MESSAGE_TYPES = [
|
|
1800
|
+
"message",
|
|
1801
|
+
"task",
|
|
1802
|
+
"question",
|
|
1803
|
+
"escalation",
|
|
1804
|
+
"status"
|
|
1805
|
+
];
|
|
1806
|
+
var SENDABLE_AGENT_MESSAGE_TYPES = [
|
|
1807
|
+
"message",
|
|
1808
|
+
"task",
|
|
1809
|
+
"status"
|
|
1810
|
+
];
|
|
1811
|
+
var AGENT_TASK_STATUSES = [
|
|
1812
|
+
"open",
|
|
1813
|
+
"working",
|
|
1814
|
+
"input_required",
|
|
1815
|
+
"completed",
|
|
1816
|
+
"failed",
|
|
1817
|
+
"canceled",
|
|
1818
|
+
"rejected"
|
|
1819
|
+
];
|
|
1820
|
+
|
|
1821
|
+
// src/agents-observability-types.ts
|
|
1822
|
+
var COST_GROUPINGS = [
|
|
1823
|
+
"agent",
|
|
1824
|
+
"channel",
|
|
1825
|
+
"team",
|
|
1826
|
+
"task",
|
|
1827
|
+
"day"
|
|
1828
|
+
];
|
|
1829
|
+
var AGENT_EXPORT_KINDS = [
|
|
1830
|
+
"messages",
|
|
1831
|
+
"tasks",
|
|
1832
|
+
"questions",
|
|
1833
|
+
"audit"
|
|
1834
|
+
];
|
|
1835
|
+
var AGENT_EXPORT_FORMATS = ["csv", "json"];
|
|
1836
|
+
var AGENT_EXPORT_SYNC_MAX_ROWS = 1e4;
|
|
509
1837
|
// Annotate the CommonJS export names for ESM import in node:
|
|
510
1838
|
0 && (module.exports = {
|
|
1839
|
+
AGENT_DELIVERY_MODES,
|
|
1840
|
+
AGENT_EXPORT_FORMATS,
|
|
1841
|
+
AGENT_EXPORT_KINDS,
|
|
1842
|
+
AGENT_EXPORT_SYNC_MAX_ROWS,
|
|
1843
|
+
AGENT_MESSAGE_TYPES,
|
|
1844
|
+
AGENT_POLICY_CODES,
|
|
1845
|
+
AGENT_STATUSES,
|
|
1846
|
+
AGENT_TASK_ERROR_CODES,
|
|
1847
|
+
AGENT_TASK_STATUSES,
|
|
1848
|
+
AgentPolicyError,
|
|
1849
|
+
AgentTaskError,
|
|
1850
|
+
AgentThreadPausedError,
|
|
511
1851
|
AuthenticationError,
|
|
1852
|
+
COST_GROUPINGS,
|
|
512
1853
|
ChannlizeClient,
|
|
513
1854
|
ChannlizeError,
|
|
514
1855
|
DEFAULT_BASE_URL,
|
|
515
1856
|
DEFAULT_TIMEOUT,
|
|
516
1857
|
EVENT_KEY_PATTERN,
|
|
517
1858
|
EVENT_SEVERITIES,
|
|
1859
|
+
HumanTakeoverError,
|
|
1860
|
+
POLICY_SCOPES,
|
|
1861
|
+
QUESTION_KINDS,
|
|
1862
|
+
QUESTION_STATUSES,
|
|
1863
|
+
QUESTION_URGENCIES,
|
|
1864
|
+
QUESTION_WAIT_MAX_SECONDS,
|
|
1865
|
+
QuestionClosedError,
|
|
518
1866
|
RateLimitError,
|
|
1867
|
+
SENDABLE_AGENT_MESSAGE_TYPES,
|
|
1868
|
+
TASK_BOARD_STATUSES,
|
|
519
1869
|
ValidationError,
|
|
1870
|
+
createAgentsClient,
|
|
520
1871
|
createNotifier,
|
|
1872
|
+
isAgentMessageWebhook,
|
|
1873
|
+
isQuestionEscalatedWebhook,
|
|
1874
|
+
isQuestionOutcomeMessage,
|
|
1875
|
+
isQuestionOutcomeWebhook,
|
|
1876
|
+
isRetryable,
|
|
1877
|
+
isTaskHandoffMessage,
|
|
521
1878
|
isValidEventKey,
|
|
522
1879
|
keyKind,
|
|
1880
|
+
parseAgentWebhook,
|
|
1881
|
+
parseAgentWebhookEvent,
|
|
523
1882
|
parseApiError,
|
|
524
|
-
|
|
1883
|
+
parseTelemetryWebhook,
|
|
1884
|
+
readChannlizeEnv,
|
|
1885
|
+
retryAfter,
|
|
1886
|
+
verifyAgentWebhook,
|
|
1887
|
+
verifyTelemetryWebhook
|
|
525
1888
|
});
|
|
526
1889
|
//# sourceMappingURL=index.js.map
|