@cognidesk/http 0.0.3-dev.3 → 0.0.3-dev.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +153 -3
- package/dist/index.js +733 -126
- package/package.json +5 -4
- package/dist/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/handler/index.ts
|
|
2
|
+
import { defineChannelContext as defineChannelContext3 } from "@cognidesk/core";
|
|
3
|
+
|
|
1
4
|
// src/responses.ts
|
|
2
5
|
var HttpInputError = class extends Error {
|
|
3
6
|
status = 400;
|
|
@@ -25,14 +28,75 @@ function emptyResponse(status, options) {
|
|
|
25
28
|
});
|
|
26
29
|
}
|
|
27
30
|
function withCors(headers, options) {
|
|
28
|
-
|
|
31
|
+
const corsHeaders = getCorsHeaders(options);
|
|
32
|
+
if (Object.keys(corsHeaders).length === 0) return headers;
|
|
33
|
+
return mergeHeaderRecords(headers, corsHeaders);
|
|
34
|
+
}
|
|
35
|
+
function responseWithCors(response, options) {
|
|
36
|
+
const corsHeaders = getCorsHeaders(options);
|
|
37
|
+
if (Object.keys(corsHeaders).length === 0) return response;
|
|
38
|
+
const headers = new Headers(response.headers);
|
|
39
|
+
for (const [key, value] of Object.entries(corsHeaders)) {
|
|
40
|
+
if (key === "vary") {
|
|
41
|
+
headers.set("vary", mergeVary(headers.get("vary"), value));
|
|
42
|
+
} else if (!headers.has(key)) {
|
|
43
|
+
headers.set(key, value);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return new Response(response.body, {
|
|
47
|
+
status: response.status,
|
|
48
|
+
statusText: response.statusText,
|
|
49
|
+
headers
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function getCorsHeaders(options) {
|
|
53
|
+
const cors = options.cors;
|
|
54
|
+
if (!cors) return {};
|
|
55
|
+
if (cors === true) {
|
|
56
|
+
return {
|
|
57
|
+
"access-control-allow-origin": "*",
|
|
58
|
+
"access-control-allow-methods": "GET,POST,OPTIONS",
|
|
59
|
+
"access-control-allow-headers": "content-type,authorization"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const origin = options.request?.headers.get("origin");
|
|
63
|
+
if (!origin || !isAllowedOrigin(cors, origin)) return {};
|
|
29
64
|
return {
|
|
30
|
-
|
|
31
|
-
"access-control-allow-
|
|
32
|
-
"access-control-allow-
|
|
33
|
-
"access-control-
|
|
65
|
+
"access-control-allow-origin": origin,
|
|
66
|
+
"access-control-allow-methods": formatHeaderList(cors.methods, "GET,POST,OPTIONS"),
|
|
67
|
+
"access-control-allow-headers": formatHeaderList(cors.allowedHeaders, "content-type,authorization"),
|
|
68
|
+
...cors.exposedHeaders ? { "access-control-expose-headers": formatHeaderList(cors.exposedHeaders, "") } : {},
|
|
69
|
+
...cors.credentials ? { "access-control-allow-credentials": "true" } : {},
|
|
70
|
+
...cors.maxAgeSeconds !== void 0 ? { "access-control-max-age": String(cors.maxAgeSeconds) } : {},
|
|
71
|
+
vary: "Origin"
|
|
34
72
|
};
|
|
35
73
|
}
|
|
74
|
+
function isAllowedOrigin(cors, origin) {
|
|
75
|
+
const origins = Array.isArray(cors.origins) ? cors.origins : [cors.origins];
|
|
76
|
+
return origins.includes(origin);
|
|
77
|
+
}
|
|
78
|
+
function formatHeaderList(value, fallback) {
|
|
79
|
+
if (value === void 0) return fallback;
|
|
80
|
+
return typeof value === "string" ? value : value.join(",");
|
|
81
|
+
}
|
|
82
|
+
function mergeHeaderRecords(headers, corsHeaders) {
|
|
83
|
+
const merged = { ...headers };
|
|
84
|
+
for (const [key, value] of Object.entries(corsHeaders)) {
|
|
85
|
+
if (key === "vary") {
|
|
86
|
+
merged.vary = mergeVary(merged.vary, value);
|
|
87
|
+
} else if (merged[key] === void 0) {
|
|
88
|
+
merged[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return merged;
|
|
92
|
+
}
|
|
93
|
+
function mergeVary(existing, value) {
|
|
94
|
+
if (!existing) return value;
|
|
95
|
+
const existingValues = existing.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
96
|
+
const normalizedValue = value.toLowerCase();
|
|
97
|
+
if (existingValues.some((entry) => entry.toLowerCase() === normalizedValue)) return existing;
|
|
98
|
+
return `${existing}, ${value}`;
|
|
99
|
+
}
|
|
36
100
|
|
|
37
101
|
// src/path.ts
|
|
38
102
|
function normalizeBasePath(basePath) {
|
|
@@ -119,36 +183,617 @@ function formatSseError(error) {
|
|
|
119
183
|
].join("\n");
|
|
120
184
|
}
|
|
121
185
|
|
|
122
|
-
// src/handler.ts
|
|
186
|
+
// src/handler/auth.ts
|
|
187
|
+
async function authorizeRequest(options, input, responseOptions) {
|
|
188
|
+
if (!options.authorize) return void 0;
|
|
189
|
+
const result = await options.authorize(input);
|
|
190
|
+
if (result === true) return void 0;
|
|
191
|
+
if (result instanceof Response) return responseWithCors(result, responseOptions);
|
|
192
|
+
return json({ error: "Unauthorized" }, 401, responseOptions);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/handler/channel-events.ts
|
|
196
|
+
import {
|
|
197
|
+
defineChannelContext as defineChannelContext2,
|
|
198
|
+
defineChannelEvent
|
|
199
|
+
} from "@cognidesk/core";
|
|
200
|
+
|
|
201
|
+
// src/channel-events.ts
|
|
202
|
+
function createChannelEventInput(input) {
|
|
203
|
+
if (!isRecord(input)) throw new Error("Channel Event input must be an object.");
|
|
204
|
+
const eventRecord = isRecord(input.event) ? input.event : void 0;
|
|
205
|
+
const sourceRecord = eventRecord ?? input;
|
|
206
|
+
const kind = stringValue(sourceRecord.kind ?? sourceRecord.nature) ?? inferKind(sourceRecord, input);
|
|
207
|
+
if (!kind) throw new Error("Channel Event kind or nature is required.");
|
|
208
|
+
const direction = stringValue(sourceRecord.direction ?? input.direction) ?? inferDirection(kind);
|
|
209
|
+
const channel = sourceRecord.channel ?? input.channel;
|
|
210
|
+
if (channel === void 0) throw new Error("Channel Event channel is required.");
|
|
211
|
+
const text = firstText(sourceRecord, input);
|
|
212
|
+
const turn = firstDefined(sourceRecord.turn, input.turn);
|
|
213
|
+
const payload = buildPayload(sourceRecord, input, text, turn);
|
|
214
|
+
const source = buildSource(sourceRecord, input, inferSourceType(kind));
|
|
215
|
+
const identity = buildIdentity(sourceRecord, input);
|
|
216
|
+
const actor = normalizeActor(firstDefined(sourceRecord.actor, input.actor)) ?? inferActor(kind, direction);
|
|
217
|
+
const handling = buildHandling(input, text, turn);
|
|
218
|
+
const event = compact({
|
|
219
|
+
...passthroughEventFields(eventRecord),
|
|
220
|
+
id: stringValue(sourceRecord.id ?? input.id),
|
|
221
|
+
channel,
|
|
222
|
+
kind,
|
|
223
|
+
nature: stringValue(sourceRecord.nature ?? input.nature) ?? kind,
|
|
224
|
+
direction,
|
|
225
|
+
intent: stringValue(sourceRecord.intent ?? input.intent) ?? inferIntent(kind, direction),
|
|
226
|
+
actor,
|
|
227
|
+
occurredAt: stringValue(sourceRecord.occurredAt ?? input.occurredAt),
|
|
228
|
+
payload,
|
|
229
|
+
identity,
|
|
230
|
+
source,
|
|
231
|
+
metadata: recordValue(sourceRecord.metadata ?? input.metadata)
|
|
232
|
+
});
|
|
233
|
+
return compact({
|
|
234
|
+
event,
|
|
235
|
+
conversationId: stringValue(input.conversationId ?? sourceRecord.conversationId),
|
|
236
|
+
agentId: stringValue(input.agentId ?? sourceRecord.agentId),
|
|
237
|
+
conversationContext: input.conversationContext,
|
|
238
|
+
createConversation: input.createConversation,
|
|
239
|
+
binding: input.binding,
|
|
240
|
+
handling,
|
|
241
|
+
app: input.app,
|
|
242
|
+
signal: input.signal,
|
|
243
|
+
onAssistantTextDelta: input.onAssistantTextDelta
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function createChannelEventRequestBody(input) {
|
|
247
|
+
const { signal: _signal, onAssistantTextDelta: _onAssistantTextDelta, ...body } = createChannelEventInput(input);
|
|
248
|
+
return body;
|
|
249
|
+
}
|
|
250
|
+
function createMessageChannelEventInput(input) {
|
|
251
|
+
const direction = input.direction ?? "inbound";
|
|
252
|
+
return createChannelEventInput({
|
|
253
|
+
...input,
|
|
254
|
+
kind: input.kind ?? "message",
|
|
255
|
+
nature: input.nature ?? input.kind ?? "message",
|
|
256
|
+
direction,
|
|
257
|
+
intent: input.intent ?? (direction === "outbound" ? "agent-message" : "customer-message"),
|
|
258
|
+
actor: input.actor ?? (direction === "outbound" ? "agent" : "customer")
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
function createProviderObjectChannelEventInput(input) {
|
|
262
|
+
return createChannelEventInput({
|
|
263
|
+
...input,
|
|
264
|
+
kind: input.kind ?? "provider.object.updated",
|
|
265
|
+
nature: input.nature ?? input.kind ?? "provider.object.updated",
|
|
266
|
+
direction: input.direction ?? "internal",
|
|
267
|
+
intent: input.intent ?? "provider-update",
|
|
268
|
+
actor: input.actor ?? "provider",
|
|
269
|
+
sourceType: input.sourceType ?? input.source?.sourceType ?? "provider-adapter"
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
function createVoiceTurnChannelEventInput(input) {
|
|
273
|
+
const text = input.text ?? input.transcript;
|
|
274
|
+
return createChannelEventInput({
|
|
275
|
+
...input,
|
|
276
|
+
channel: input.channel ?? "voice",
|
|
277
|
+
...text !== void 0 ? { text } : {},
|
|
278
|
+
kind: input.kind ?? "voice.turn.finalized",
|
|
279
|
+
nature: input.nature ?? input.kind ?? "voice.turn.finalized",
|
|
280
|
+
direction: input.direction ?? "inbound",
|
|
281
|
+
intent: input.intent ?? "customer-voice-turn",
|
|
282
|
+
actor: input.actor ?? "customer",
|
|
283
|
+
sourceType: input.sourceType ?? input.source?.sourceType ?? "provider-adapter"
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
function createOutboundContactChannelEventInput(input) {
|
|
287
|
+
return createChannelEventInput({
|
|
288
|
+
...input,
|
|
289
|
+
kind: input.kind ?? "outbound.contact.requested",
|
|
290
|
+
nature: input.nature ?? input.kind ?? "outbound.contact.requested",
|
|
291
|
+
direction: input.direction ?? "outbound",
|
|
292
|
+
intent: input.intent ?? "outbound-contact",
|
|
293
|
+
actor: input.actor ?? "application"
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function createScheduledChannelEventInput(input) {
|
|
297
|
+
return createChannelEventInput({
|
|
298
|
+
...input,
|
|
299
|
+
kind: input.kind ?? "schedule.due",
|
|
300
|
+
nature: input.nature ?? input.kind ?? "schedule.due",
|
|
301
|
+
direction: input.direction ?? "internal",
|
|
302
|
+
intent: input.intent ?? "scheduled-support-action",
|
|
303
|
+
actor: input.actor ?? "scheduler",
|
|
304
|
+
sourceType: input.sourceType ?? input.source?.sourceType ?? "schedule-adapter"
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
function createChannelOutputResolutionEventInput(input) {
|
|
308
|
+
return createChannelEventInput({
|
|
309
|
+
...input,
|
|
310
|
+
kind: input.kind ?? "output.resolution",
|
|
311
|
+
nature: input.nature ?? input.kind ?? "output.resolution",
|
|
312
|
+
direction: input.direction ?? "outbound",
|
|
313
|
+
intent: input.intent ?? "output-resolution",
|
|
314
|
+
actor: input.actor ?? "application",
|
|
315
|
+
handling: {
|
|
316
|
+
...input.handling,
|
|
317
|
+
disposition: input.handling?.disposition ?? "output-resolution"
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
function createChannelHandoffEventInput(input) {
|
|
322
|
+
const payload = buildPayload(input, input, void 0, input.turn);
|
|
323
|
+
return createChannelEventInput({
|
|
324
|
+
...input,
|
|
325
|
+
payload: {
|
|
326
|
+
...isRecord(payload) ? payload : {},
|
|
327
|
+
...input.fromChannel ? { fromChannel: input.fromChannel } : {},
|
|
328
|
+
toChannel: input.channel,
|
|
329
|
+
...input.reason ? { reason: input.reason } : {},
|
|
330
|
+
...input.reasonCode ? { reasonCode: input.reasonCode } : {},
|
|
331
|
+
...input.reasonLabel ? { reasonLabel: input.reasonLabel } : {}
|
|
332
|
+
},
|
|
333
|
+
kind: input.kind ?? "channel.handoff.requested",
|
|
334
|
+
nature: input.nature ?? input.kind ?? "channel.handoff.requested",
|
|
335
|
+
direction: input.direction ?? "internal",
|
|
336
|
+
intent: input.intent ?? "channel-handoff",
|
|
337
|
+
actor: input.actor ?? "application",
|
|
338
|
+
handling: {
|
|
339
|
+
...input.handling,
|
|
340
|
+
disposition: input.handling?.disposition ?? "handoff-review"
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function createChannelHandoffReviewEventInput(input) {
|
|
345
|
+
return createChannelEventInput({
|
|
346
|
+
...input,
|
|
347
|
+
kind: input.kind ?? "custom",
|
|
348
|
+
nature: input.nature ?? input.kind ?? "custom",
|
|
349
|
+
direction: input.direction ?? "internal",
|
|
350
|
+
intent: input.intent ?? "handoff-review",
|
|
351
|
+
actor: input.actor ?? "operator",
|
|
352
|
+
handling: {
|
|
353
|
+
...input.handling,
|
|
354
|
+
disposition: input.handling?.disposition ?? "handoff-review"
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
function buildHandling(input, _text, turn) {
|
|
359
|
+
if (!input.handling && turn === void 0) return void 0;
|
|
360
|
+
return compact({
|
|
361
|
+
...input.handling ?? {},
|
|
362
|
+
text: input.handling?.text ?? void 0,
|
|
363
|
+
turn: input.handling?.turn ?? turn
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
function buildPayload(sourceRecord, input, text, turn) {
|
|
367
|
+
const seed = firstDefined(sourceRecord.payload, input.payload);
|
|
368
|
+
const fields = compact({
|
|
369
|
+
text,
|
|
370
|
+
subject: stringValue(sourceRecord.subject ?? input.subject),
|
|
371
|
+
body: stringValue(sourceRecord.body ?? input.body),
|
|
372
|
+
summary: stringValue(sourceRecord.summary ?? input.summary),
|
|
373
|
+
attachments: firstDefined(sourceRecord.attachments, input.attachments),
|
|
374
|
+
providerObject: firstDefined(sourceRecord.providerObject, input.providerObject),
|
|
375
|
+
status: stringValue(sourceRecord.status ?? input.status),
|
|
376
|
+
turn
|
|
377
|
+
});
|
|
378
|
+
if (seed === void 0) return Object.keys(fields).length > 0 ? fields : void 0;
|
|
379
|
+
if (typeof seed === "string") return Object.keys(fields).length > 0 ? { ...fields, text: fields.text ?? seed } : { text: seed };
|
|
380
|
+
if (!isRecord(seed)) return Object.keys(fields).length > 0 ? { value: seed, ...fields } : seed;
|
|
381
|
+
return { ...fields, ...seed };
|
|
382
|
+
}
|
|
383
|
+
function buildSource(sourceRecord, input, defaultSourceType) {
|
|
384
|
+
const seed = recordValue(sourceRecord.source ?? input.source) ?? {};
|
|
385
|
+
const hasEvidence = hasSourceEvidence(sourceRecord, input, seed);
|
|
386
|
+
const source = compact({
|
|
387
|
+
...seed,
|
|
388
|
+
sourceType: stringValue(sourceRecord.sourceType ?? input.sourceType) ?? seed.sourceType ?? (hasEvidence ? defaultSourceType : void 0),
|
|
389
|
+
sourceId: stringValue(sourceRecord.sourceId ?? input.sourceId) ?? seed.sourceId,
|
|
390
|
+
provider: stringValue(sourceRecord.provider ?? input.provider) ?? seed.provider,
|
|
391
|
+
providerPackageId: stringValue(sourceRecord.providerPackageId ?? input.providerPackageId) ?? seed.providerPackageId,
|
|
392
|
+
eventId: stringValue(sourceRecord.eventId ?? input.eventId) ?? seed.eventId,
|
|
393
|
+
streamId: stringValue(sourceRecord.streamId ?? input.streamId) ?? seed.streamId,
|
|
394
|
+
deliveryId: stringValue(sourceRecord.deliveryId ?? input.deliveryId) ?? seed.deliveryId,
|
|
395
|
+
receivedAt: stringValue(sourceRecord.receivedAt ?? input.receivedAt) ?? seed.receivedAt,
|
|
396
|
+
verified: booleanValue(sourceRecord.verified ?? input.verified) ?? seed.verified,
|
|
397
|
+
externalObjectIds: recordValue(sourceRecord.externalObjectIds ?? input.externalObjectIds) ?? seed.externalObjectIds,
|
|
398
|
+
raw: firstDefined(sourceRecord.raw, input.raw, seed.raw)
|
|
399
|
+
});
|
|
400
|
+
return Object.keys(source).length > 0 ? source : void 0;
|
|
401
|
+
}
|
|
402
|
+
function buildIdentity(sourceRecord, input) {
|
|
403
|
+
const seed = recordValue(sourceRecord.identity ?? input.identity) ?? {};
|
|
404
|
+
const identity = compact({
|
|
405
|
+
...seed,
|
|
406
|
+
key: stringValue(sourceRecord.identityKey ?? input.identityKey) ?? seed.key,
|
|
407
|
+
dedupeKey: stringValue(sourceRecord.dedupeKey ?? input.dedupeKey) ?? seed.dedupeKey,
|
|
408
|
+
streamId: stringValue(sourceRecord.identityStreamId ?? input.identityStreamId) ?? seed.streamId,
|
|
409
|
+
sequence: sequenceValue(sourceRecord.sequence ?? input.sequence) ?? seed.sequence,
|
|
410
|
+
idempotencyKey: stringValue(sourceRecord.idempotencyKey ?? input.idempotencyKey) ?? seed.idempotencyKey,
|
|
411
|
+
metadata: recordValue(sourceRecord.identityMetadata ?? input.identityMetadata) ?? seed.metadata
|
|
412
|
+
});
|
|
413
|
+
return Object.keys(identity).length > 0 ? identity : void 0;
|
|
414
|
+
}
|
|
415
|
+
function passthroughEventFields(eventRecord) {
|
|
416
|
+
if (!eventRecord) return {};
|
|
417
|
+
const output = { ...eventRecord };
|
|
418
|
+
for (const key of [
|
|
419
|
+
"conversationId",
|
|
420
|
+
"agentId",
|
|
421
|
+
"text",
|
|
422
|
+
"message",
|
|
423
|
+
"subject",
|
|
424
|
+
"body",
|
|
425
|
+
"summary",
|
|
426
|
+
"attachments",
|
|
427
|
+
"providerObject",
|
|
428
|
+
"status",
|
|
429
|
+
"turn",
|
|
430
|
+
"raw",
|
|
431
|
+
"sourceType",
|
|
432
|
+
"sourceId",
|
|
433
|
+
"provider",
|
|
434
|
+
"providerPackageId",
|
|
435
|
+
"eventId",
|
|
436
|
+
"streamId",
|
|
437
|
+
"deliveryId",
|
|
438
|
+
"receivedAt",
|
|
439
|
+
"verified",
|
|
440
|
+
"externalObjectIds",
|
|
441
|
+
"identity",
|
|
442
|
+
"identityKey",
|
|
443
|
+
"dedupeKey",
|
|
444
|
+
"idempotencyKey",
|
|
445
|
+
"identityStreamId",
|
|
446
|
+
"sequence",
|
|
447
|
+
"identityMetadata"
|
|
448
|
+
]) {
|
|
449
|
+
delete output[key];
|
|
450
|
+
}
|
|
451
|
+
return output;
|
|
452
|
+
}
|
|
453
|
+
function firstText(sourceRecord, input) {
|
|
454
|
+
const explicit = stringValue(sourceRecord.text ?? sourceRecord.message ?? input.text ?? input.message);
|
|
455
|
+
if (explicit) return explicit;
|
|
456
|
+
const payload = firstDefined(sourceRecord.payload, input.payload);
|
|
457
|
+
if (typeof payload === "string") return payload.trim() || void 0;
|
|
458
|
+
if (isRecord(payload)) return stringValue(payload.text ?? payload.message);
|
|
459
|
+
return void 0;
|
|
460
|
+
}
|
|
461
|
+
function inferKind(sourceRecord, input) {
|
|
462
|
+
if (firstText(sourceRecord, input)) return "message";
|
|
463
|
+
if (sourceRecord.providerObject !== void 0 || input.providerObject !== void 0 || sourceRecord.status !== void 0 || input.status !== void 0) return "provider.object.updated";
|
|
464
|
+
return void 0;
|
|
465
|
+
}
|
|
466
|
+
function inferDirection(kind) {
|
|
467
|
+
if (kind === "provider.object.updated" || kind === "schedule.due" || kind === "custom") return "internal";
|
|
468
|
+
if (kind === "outbound.contact.requested" || kind === "output.resolution") return "outbound";
|
|
469
|
+
return "inbound";
|
|
470
|
+
}
|
|
471
|
+
function inferIntent(kind, direction) {
|
|
472
|
+
if (kind === "message") return direction === "outbound" ? "agent-message" : "customer-message";
|
|
473
|
+
if (kind === "voice.turn.finalized") return "customer-voice-turn";
|
|
474
|
+
if (kind === "provider.object.updated") return "provider-update";
|
|
475
|
+
if (kind === "operator.resume") return "operator-resume";
|
|
476
|
+
if (kind === "outbound.contact.requested") return "outbound-contact";
|
|
477
|
+
if (kind === "channel.handoff.requested") return "channel-handoff";
|
|
478
|
+
if (kind === "schedule.due") return "scheduled-support-action";
|
|
479
|
+
if (kind === "output.resolution") return "output-resolution";
|
|
480
|
+
if (kind === "delivery.updated") return "delivery-update";
|
|
481
|
+
return void 0;
|
|
482
|
+
}
|
|
483
|
+
function inferActor(kind, direction) {
|
|
484
|
+
if (kind === "message") return { type: direction === "outbound" ? "agent" : "customer" };
|
|
485
|
+
if (kind === "voice.turn.finalized") return { type: "customer" };
|
|
486
|
+
if (kind === "provider.object.updated" || kind === "delivery.updated") return { type: "provider" };
|
|
487
|
+
if (kind === "schedule.due") return { type: "scheduler" };
|
|
488
|
+
if (kind === "channel.handoff.requested") return { type: "application" };
|
|
489
|
+
if (kind === "outbound.contact.requested" || kind === "output.resolution") return { type: "application" };
|
|
490
|
+
if (kind === "operator.resume") return { type: "operator" };
|
|
491
|
+
return void 0;
|
|
492
|
+
}
|
|
493
|
+
function inferSourceType(kind) {
|
|
494
|
+
if (kind === "schedule.due") return "schedule-adapter";
|
|
495
|
+
if (kind === "message" || kind === "voice.session.started" || kind === "voice.turn.finalized" || kind === "provider.object.updated" || kind === "delivery.updated") return "provider-adapter";
|
|
496
|
+
return void 0;
|
|
497
|
+
}
|
|
498
|
+
function hasSourceEvidence(sourceRecord, input, seed) {
|
|
499
|
+
return Object.keys(seed).length > 0 || sourceRecord.provider !== void 0 || input.provider !== void 0 || sourceRecord.providerPackageId !== void 0 || input.providerPackageId !== void 0 || sourceRecord.eventId !== void 0 || input.eventId !== void 0 || sourceRecord.streamId !== void 0 || input.streamId !== void 0 || sourceRecord.deliveryId !== void 0 || input.deliveryId !== void 0 || sourceRecord.raw !== void 0 || input.raw !== void 0;
|
|
500
|
+
}
|
|
501
|
+
function normalizeActor(value) {
|
|
502
|
+
if (value === void 0 || value === null) return void 0;
|
|
503
|
+
if (typeof value === "string") return { type: value };
|
|
504
|
+
if (isRecord(value)) return value;
|
|
505
|
+
return void 0;
|
|
506
|
+
}
|
|
507
|
+
function compact(record) {
|
|
508
|
+
const output = {};
|
|
509
|
+
for (const [key, value] of Object.entries(record)) {
|
|
510
|
+
if (value !== void 0) output[key] = value;
|
|
511
|
+
}
|
|
512
|
+
return output;
|
|
513
|
+
}
|
|
514
|
+
function firstDefined(...values) {
|
|
515
|
+
for (const value of values) {
|
|
516
|
+
if (value !== void 0) return value;
|
|
517
|
+
}
|
|
518
|
+
return void 0;
|
|
519
|
+
}
|
|
520
|
+
function stringValue(value) {
|
|
521
|
+
if (typeof value !== "string") return void 0;
|
|
522
|
+
const trimmed = value.trim();
|
|
523
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
524
|
+
}
|
|
525
|
+
function booleanValue(value) {
|
|
526
|
+
return typeof value === "boolean" ? value : void 0;
|
|
527
|
+
}
|
|
528
|
+
function sequenceValue(value) {
|
|
529
|
+
return typeof value === "string" || typeof value === "number" ? value : void 0;
|
|
530
|
+
}
|
|
531
|
+
function recordValue(value) {
|
|
532
|
+
return isRecord(value) ? value : void 0;
|
|
533
|
+
}
|
|
534
|
+
function isRecord(value) {
|
|
535
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// src/handler/body.ts
|
|
539
|
+
async function readObject(request) {
|
|
540
|
+
const body = await readJson(request);
|
|
541
|
+
if (!isRecord2(body)) throw new HttpInputError("Request body must be a JSON object.");
|
|
542
|
+
return body;
|
|
543
|
+
}
|
|
544
|
+
function isRecord2(value) {
|
|
545
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/handler/optional.ts
|
|
549
|
+
import { defineChannelContext } from "@cognidesk/core";
|
|
550
|
+
function optionalString(body, key) {
|
|
551
|
+
const value = body[key];
|
|
552
|
+
if (value === void 0 || value === null) return void 0;
|
|
553
|
+
if (typeof value !== "string") throw new HttpInputError(`${key} must be a string.`);
|
|
554
|
+
const trimmed = value.trim();
|
|
555
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
556
|
+
}
|
|
557
|
+
function optionalStringProperty(body, key) {
|
|
558
|
+
const value = optionalString(body, key);
|
|
559
|
+
return value ? { [key]: value } : {};
|
|
560
|
+
}
|
|
561
|
+
function optionalChannel(body, key) {
|
|
562
|
+
const value = body[key];
|
|
563
|
+
if (value === void 0 || value === null) return void 0;
|
|
564
|
+
try {
|
|
565
|
+
return defineChannelContext(value);
|
|
566
|
+
} catch {
|
|
567
|
+
throw new HttpInputError(`${key} must be a valid conversation channel.`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function optionalChannelProperty(body) {
|
|
571
|
+
const channel = optionalChannel(body, "channel");
|
|
572
|
+
return channel ? { channel } : {};
|
|
573
|
+
}
|
|
574
|
+
function optionalNonNegativeNumber(body, key) {
|
|
575
|
+
const value = body[key];
|
|
576
|
+
if (value === void 0 || value === null) return void 0;
|
|
577
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
578
|
+
throw new HttpInputError(`${key} must be a non-negative integer.`);
|
|
579
|
+
}
|
|
580
|
+
return value;
|
|
581
|
+
}
|
|
582
|
+
function optionalRouting(body, key) {
|
|
583
|
+
const value = optionalString(body, key);
|
|
584
|
+
if (!value) return void 0;
|
|
585
|
+
if (value !== "none" && value !== "activeJourneyOnly" && value !== "full" && value !== "targeted") {
|
|
586
|
+
throw new HttpInputError(`${key} must be a valid routing mode.`);
|
|
587
|
+
}
|
|
588
|
+
return value;
|
|
589
|
+
}
|
|
590
|
+
function optionalTarget(body, key) {
|
|
591
|
+
const value = body[key];
|
|
592
|
+
if (value === void 0 || value === null) return void 0;
|
|
593
|
+
if (!isRecord2(value)) throw new HttpInputError(`${key} must be an object.`);
|
|
594
|
+
const journeyId = optionalString(value, "journeyId");
|
|
595
|
+
const stateId = optionalString(value, "stateId");
|
|
596
|
+
return {
|
|
597
|
+
...journeyId ? { journeyId } : {},
|
|
598
|
+
...stateId ? { stateId } : {}
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function optionalVoiceClient(body) {
|
|
602
|
+
const value = body.client;
|
|
603
|
+
if (value === void 0 || value === null) return {};
|
|
604
|
+
if (!isRecord2(value)) throw new HttpInputError("client must be an object.");
|
|
605
|
+
const userAgent = optionalString(value, "userAgent");
|
|
606
|
+
const locale = optionalString(value, "locale");
|
|
607
|
+
const metadata = value.metadata;
|
|
608
|
+
if (metadata !== void 0 && metadata !== null && !isRecord2(metadata)) {
|
|
609
|
+
throw new HttpInputError("client.metadata must be an object.");
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
client: {
|
|
613
|
+
...userAgent ? { userAgent } : {},
|
|
614
|
+
...locale ? { locale } : {},
|
|
615
|
+
...isRecord2(metadata) ? { metadata } : {}
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// src/handler/channel-events.ts
|
|
621
|
+
async function handleChannelEvent(options, input, responseOptions) {
|
|
622
|
+
if (hasHandleChannelEvent(options.runtime)) {
|
|
623
|
+
return options.runtime.handleChannelEvent(input);
|
|
624
|
+
}
|
|
625
|
+
const userMessageInput = channelEventToUserMessageInput(input);
|
|
626
|
+
if (!userMessageInput) {
|
|
627
|
+
return json({ error: "Channel events are not supported by this runtime" }, 501, responseOptions);
|
|
628
|
+
}
|
|
629
|
+
const result = await options.runtime.handleUserMessage(userMessageInput);
|
|
630
|
+
return channelEventResultFromUserMessageResult(input, result);
|
|
631
|
+
}
|
|
632
|
+
function hasHandleChannelEvent(runtime) {
|
|
633
|
+
return typeof runtime.handleChannelEvent === "function";
|
|
634
|
+
}
|
|
635
|
+
function readChannelEventInput(body, signal) {
|
|
636
|
+
try {
|
|
637
|
+
return createChannelEventInput({ ...body, signal });
|
|
638
|
+
} catch (error) {
|
|
639
|
+
throw new HttpInputError(error instanceof Error ? error.message : "Invalid Channel Event input.");
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
function channelEventToUserMessageInput(input) {
|
|
643
|
+
const event = input.event;
|
|
644
|
+
if (!isInboundMessageEvent(event)) return null;
|
|
645
|
+
const conversationId = input.conversationId ?? input.binding?.conversationId;
|
|
646
|
+
if (!conversationId) return null;
|
|
647
|
+
const text = input.handling?.text ?? textFromChannelEventPayload(event.payload);
|
|
648
|
+
if (!text) return null;
|
|
649
|
+
const turn = turnFromChannelEventPayload(event.payload);
|
|
650
|
+
return {
|
|
651
|
+
conversationId,
|
|
652
|
+
text,
|
|
653
|
+
channel: defineChannelContext2(event.channel),
|
|
654
|
+
...input.handling?.turn !== void 0 ? { turn: input.handling.turn } : turn !== void 0 ? { turn } : {},
|
|
655
|
+
...input.app !== void 0 ? { app: input.app } : {},
|
|
656
|
+
...input.signal ? { signal: input.signal } : {}
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
function isInboundMessageEvent(event) {
|
|
660
|
+
const inbound = event.direction === void 0 || event.direction === "inbound";
|
|
661
|
+
return inbound && (event.nature === "message" || event.kind === "message" || event.intent === "customer-message");
|
|
662
|
+
}
|
|
663
|
+
function textFromChannelEventPayload(payload) {
|
|
664
|
+
if (typeof payload === "string") return payload.trim() || void 0;
|
|
665
|
+
if (!isRecord2(payload)) return void 0;
|
|
666
|
+
return optionalString(payload, "text") ?? optionalString(payload, "message");
|
|
667
|
+
}
|
|
668
|
+
function turnFromChannelEventPayload(payload) {
|
|
669
|
+
if (!isRecord2(payload)) return void 0;
|
|
670
|
+
return payload.turn;
|
|
671
|
+
}
|
|
672
|
+
function channelEventResultFromUserMessageResult(input, result) {
|
|
673
|
+
const channelEvent = defineChannelEvent(input.event);
|
|
674
|
+
return {
|
|
675
|
+
channelEvent,
|
|
676
|
+
intake: {
|
|
677
|
+
outcome: "accepted",
|
|
678
|
+
bindingOutcome: input.binding?.outcome ?? "resume-existing",
|
|
679
|
+
conversationId: result.conversation.id,
|
|
680
|
+
handling: "started"
|
|
681
|
+
},
|
|
682
|
+
conversation: result.conversation,
|
|
683
|
+
turn: result,
|
|
684
|
+
snapshot: result.snapshot,
|
|
685
|
+
events: result.events,
|
|
686
|
+
text: result.text,
|
|
687
|
+
...result.activeJourneyId ? { activeJourneyId: result.activeJourneyId } : {},
|
|
688
|
+
disposition: "model-turn"
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
function sendMessageResultFromChannelEvent(result) {
|
|
692
|
+
return {
|
|
693
|
+
text: result.turn?.text ?? result.text ?? textFromEvents(result.events) ?? "",
|
|
694
|
+
events: result.events,
|
|
695
|
+
...result.turn?.activeJourneyId ?? result.activeJourneyId ? { activeJourneyId: result.turn?.activeJourneyId ?? result.activeJourneyId } : {}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
function textFromEvents(events) {
|
|
699
|
+
for (const event of [...events].reverse()) {
|
|
700
|
+
if (event.type === "message.completed" && typeof event.data.text === "string") {
|
|
701
|
+
return event.data.text;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return void 0;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/handler/channel-output.ts
|
|
708
|
+
function hasResolveChannelOutput(runtime) {
|
|
709
|
+
return typeof runtime.resolveChannelOutput === "function";
|
|
710
|
+
}
|
|
711
|
+
function readChannelOutputResolutionInput(body, signal) {
|
|
712
|
+
const record = body;
|
|
713
|
+
const conversationId = optionalString(record, "conversationId");
|
|
714
|
+
if (!conversationId) throw new HttpInputError("conversationId is required");
|
|
715
|
+
if (!isRecord2(record.intent)) throw new HttpInputError("intent is required");
|
|
716
|
+
const input = {
|
|
717
|
+
conversationId,
|
|
718
|
+
intent: record.intent,
|
|
719
|
+
signal
|
|
720
|
+
};
|
|
721
|
+
if (record.resolution !== void 0) {
|
|
722
|
+
input.resolution = record.resolution;
|
|
723
|
+
}
|
|
724
|
+
if (record.app !== void 0) {
|
|
725
|
+
input.app = record.app;
|
|
726
|
+
}
|
|
727
|
+
return input;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/handler/errors.ts
|
|
731
|
+
function internalErrorMessage(error, options) {
|
|
732
|
+
if (!options.exposeInternalErrors) return "Internal server error";
|
|
733
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// src/handler/voice.ts
|
|
737
|
+
function withVoiceEventsUrl(result, basePath) {
|
|
738
|
+
return {
|
|
739
|
+
...result,
|
|
740
|
+
eventsUrl: `${basePath}/conversations/${encodeURIComponent(result.conversation.id)}/events/stream`
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/handler/index.ts
|
|
123
745
|
function createCognideskHttpHandler(options) {
|
|
124
746
|
const basePath = normalizeBasePath(options.basePath ?? "");
|
|
125
747
|
const pollIntervalMs = options.ssePollIntervalMs ?? 500;
|
|
126
748
|
return {
|
|
127
749
|
async handle(request) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
750
|
+
const responseOptions = {
|
|
751
|
+
request,
|
|
752
|
+
...options.cors !== void 0 ? { cors: options.cors } : {}
|
|
753
|
+
};
|
|
131
754
|
try {
|
|
132
755
|
const url = new URL(request.url);
|
|
133
756
|
const path = stripBasePath(url.pathname, basePath);
|
|
134
|
-
if (path === null) return json({ error: "Not found" }, 404,
|
|
757
|
+
if (path === null) return json({ error: "Not found" }, 404, responseOptions);
|
|
758
|
+
if (request.method === "OPTIONS" && options.cors) {
|
|
759
|
+
return emptyResponse(204, responseOptions);
|
|
760
|
+
}
|
|
761
|
+
const authorizationResponse = await authorizeRequest(options, { request, url, path }, responseOptions);
|
|
762
|
+
if (authorizationResponse) return authorizationResponse;
|
|
135
763
|
if (request.method === "POST" && path === "/conversations") {
|
|
136
764
|
const body = await readObject(request);
|
|
137
765
|
const agentId = optionalString(body, "agentId") ?? options.agentId;
|
|
138
|
-
if (!agentId) return json({ error: "agentId is required" }, 400,
|
|
766
|
+
if (!agentId) return json({ error: "agentId is required" }, 400, responseOptions);
|
|
139
767
|
const conversation = await options.runtime.createConversation({
|
|
140
768
|
...optionalStringProperty(body, "id"),
|
|
141
769
|
agentId,
|
|
770
|
+
...optionalChannelProperty(body),
|
|
142
771
|
context: body.context ?? {}
|
|
143
772
|
});
|
|
144
|
-
return json({ conversation }, 201,
|
|
773
|
+
return json({ conversation }, 201, responseOptions);
|
|
774
|
+
}
|
|
775
|
+
if (request.method === "POST" && path === "/channel-events") {
|
|
776
|
+
const body = await readObject(request);
|
|
777
|
+
const input = readChannelEventInput(body, request.signal);
|
|
778
|
+
const result = await handleChannelEvent(options, input, responseOptions);
|
|
779
|
+
if (result instanceof Response) return result;
|
|
780
|
+
return json(result, 200, responseOptions);
|
|
781
|
+
}
|
|
782
|
+
if (request.method === "POST" && path === "/channel-outputs/resolve") {
|
|
783
|
+
if (!hasResolveChannelOutput(options.runtime)) {
|
|
784
|
+
return json({ error: "Channel Output resolution is not supported by this runtime" }, 501, responseOptions);
|
|
785
|
+
}
|
|
786
|
+
const body = await readObject(request);
|
|
787
|
+
const input = readChannelOutputResolutionInput(body, request.signal);
|
|
788
|
+
const result = await options.runtime.resolveChannelOutput(input);
|
|
789
|
+
return json(result, 200, responseOptions);
|
|
145
790
|
}
|
|
146
791
|
if (request.method === "POST" && path === "/voice/conversations") {
|
|
147
|
-
if (!options.runtime.startVoiceConversation) return json({ error: "Voice conversations are not supported by this runtime" }, 501,
|
|
148
|
-
if (!options.voice?.createSocket) return json({ error: "Voice socket handshakes are not configured" }, 501,
|
|
792
|
+
if (!options.runtime.startVoiceConversation) return json({ error: "Voice conversations are not supported by this runtime" }, 501, responseOptions);
|
|
793
|
+
if (!options.voice?.createSocket) return json({ error: "Voice socket handshakes are not configured" }, 501, responseOptions);
|
|
149
794
|
const body = await readObject(request);
|
|
150
795
|
const agentId = optionalString(body, "agentId") ?? options.agentId;
|
|
151
|
-
if (!agentId) return json({ error: "agentId is required" }, 400,
|
|
796
|
+
if (!agentId) return json({ error: "agentId is required" }, 400, responseOptions);
|
|
152
797
|
const result = await options.runtime.startVoiceConversation({
|
|
153
798
|
...optionalStringProperty(body, "id"),
|
|
154
799
|
agentId,
|
|
@@ -157,12 +802,12 @@ function createCognideskHttpHandler(options) {
|
|
|
157
802
|
...body.app !== void 0 ? { app: body.app } : {}
|
|
158
803
|
});
|
|
159
804
|
const socket = await options.voice.createSocket({ result, request, basePath });
|
|
160
|
-
return json(withVoiceEventsUrl({ ...result, socket }, basePath), 201,
|
|
805
|
+
return json(withVoiceEventsUrl({ ...result, socket }, basePath), 201, responseOptions);
|
|
161
806
|
}
|
|
162
807
|
const voiceSegmentMatch = path.match(/^\/conversations\/([^/]+)\/voice-segments$/);
|
|
163
808
|
if (request.method === "POST" && voiceSegmentMatch) {
|
|
164
|
-
if (!options.runtime.startVoiceSegment) return json({ error: "Voice segments are not supported by this runtime" }, 501,
|
|
165
|
-
if (!options.voice?.createSocket) return json({ error: "Voice socket handshakes are not configured" }, 501,
|
|
809
|
+
if (!options.runtime.startVoiceSegment) return json({ error: "Voice segments are not supported by this runtime" }, 501, responseOptions);
|
|
810
|
+
if (!options.voice?.createSocket) return json({ error: "Voice socket handshakes are not configured" }, 501, responseOptions);
|
|
166
811
|
const conversationId = decodeURIComponent(voiceSegmentMatch[1] ?? "");
|
|
167
812
|
const body = await readObject(request);
|
|
168
813
|
const result = await options.runtime.startVoiceSegment({
|
|
@@ -171,45 +816,68 @@ function createCognideskHttpHandler(options) {
|
|
|
171
816
|
...body.app !== void 0 ? { app: body.app } : {}
|
|
172
817
|
});
|
|
173
818
|
const socket = await options.voice.createSocket({ result, request, basePath });
|
|
174
|
-
return json(withVoiceEventsUrl({ ...result, socket }, basePath), 200,
|
|
819
|
+
return json(withVoiceEventsUrl({ ...result, socket }, basePath), 200, responseOptions);
|
|
175
820
|
}
|
|
176
821
|
const messageMatch = path.match(/^\/conversations\/([^/]+)\/messages$/);
|
|
177
822
|
if (request.method === "POST" && messageMatch) {
|
|
178
823
|
const conversationId = decodeURIComponent(messageMatch[1] ?? "");
|
|
179
824
|
const body = await readObject(request);
|
|
180
825
|
const text = optionalString(body, "text") ?? optionalString(body, "message");
|
|
181
|
-
if (!text) return json({ error: "message is required" }, 400,
|
|
826
|
+
if (!text) return json({ error: "message is required" }, 400, responseOptions);
|
|
827
|
+
const channel = optionalChannel(body, "channel") ?? defineChannelContext3("chat");
|
|
828
|
+
if (hasHandleChannelEvent(options.runtime)) {
|
|
829
|
+
const result2 = await options.runtime.handleChannelEvent({
|
|
830
|
+
conversationId,
|
|
831
|
+
event: {
|
|
832
|
+
channel,
|
|
833
|
+
kind: "message",
|
|
834
|
+
nature: "message",
|
|
835
|
+
direction: "inbound",
|
|
836
|
+
intent: "customer-message",
|
|
837
|
+
actor: { type: "customer" },
|
|
838
|
+
payload: {
|
|
839
|
+
text,
|
|
840
|
+
...body.turn !== void 0 ? { turn: body.turn } : {}
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
...body.turn !== void 0 ? { handling: { turn: body.turn } } : {},
|
|
844
|
+
...body.app !== void 0 ? { app: body.app } : {},
|
|
845
|
+
signal: request.signal
|
|
846
|
+
});
|
|
847
|
+
return json(sendMessageResultFromChannelEvent(result2), 200, responseOptions);
|
|
848
|
+
}
|
|
182
849
|
const result = await options.runtime.handleUserMessage({
|
|
183
850
|
conversationId,
|
|
184
851
|
text,
|
|
852
|
+
channel,
|
|
185
853
|
...body.turn !== void 0 ? { turn: body.turn } : {},
|
|
186
854
|
...body.app !== void 0 ? { app: body.app } : {},
|
|
187
855
|
signal: request.signal
|
|
188
856
|
});
|
|
189
|
-
return json(result, 200,
|
|
857
|
+
return json(result, 200, responseOptions);
|
|
190
858
|
}
|
|
191
859
|
const customEventMatch = path.match(/^\/conversations\/([^/]+)\/custom-events\/([^/]+)$/);
|
|
192
860
|
if (request.method === "POST" && customEventMatch) {
|
|
193
|
-
if (!options.runtime.emitCustomEvent) return json({ error: "Custom events are not supported by this runtime" }, 501,
|
|
861
|
+
if (!options.runtime.emitCustomEvent) return json({ error: "Custom events are not supported by this runtime" }, 501, responseOptions);
|
|
194
862
|
const conversationId = decodeURIComponent(customEventMatch[1] ?? "");
|
|
195
863
|
const eventName = decodeURIComponent(customEventMatch[2] ?? "");
|
|
196
864
|
const event = options.customEvents?.find((candidate) => candidate.name === eventName);
|
|
197
|
-
if (!event) return json({ error: `Custom event '${eventName}' is not registered with the HTTP handler` }, 404,
|
|
865
|
+
if (!event) return json({ error: `Custom event '${eventName}' is not registered with the HTTP handler` }, 404, responseOptions);
|
|
198
866
|
const body = await readObject(request);
|
|
199
867
|
const emitted = await options.runtime.emitCustomEvent({
|
|
200
868
|
conversationId,
|
|
201
869
|
event,
|
|
202
870
|
payload: body.payload
|
|
203
871
|
});
|
|
204
|
-
return json({ event: emitted }, 200,
|
|
872
|
+
return json({ event: emitted }, 200, responseOptions);
|
|
205
873
|
}
|
|
206
874
|
const journeyEventMatch = path.match(/^\/conversations\/([^/]+)\/journey-events\/([^/]+)$/);
|
|
207
875
|
if (request.method === "POST" && journeyEventMatch) {
|
|
208
|
-
if (!options.runtime.emitJourneyEvent) return json({ error: "Journey events are not supported by this runtime" }, 501,
|
|
876
|
+
if (!options.runtime.emitJourneyEvent) return json({ error: "Journey events are not supported by this runtime" }, 501, responseOptions);
|
|
209
877
|
const conversationId = decodeURIComponent(journeyEventMatch[1] ?? "");
|
|
210
878
|
const eventName = decodeURIComponent(journeyEventMatch[2] ?? "");
|
|
211
879
|
const event = options.journeyEvents?.find((candidate) => candidate.name === eventName);
|
|
212
|
-
if (!event) return json({ error: `Journey event '${eventName}' is not registered with the HTTP handler` }, 404,
|
|
880
|
+
if (!event) return json({ error: `Journey event '${eventName}' is not registered with the HTTP handler` }, 404, responseOptions);
|
|
213
881
|
const body = await readObject(request);
|
|
214
882
|
const routing = optionalRouting(body, "routing");
|
|
215
883
|
const target = optionalTarget(body, "target");
|
|
@@ -222,37 +890,37 @@ function createCognideskHttpHandler(options) {
|
|
|
222
890
|
...body.app !== void 0 ? { app: body.app } : {},
|
|
223
891
|
signal: request.signal
|
|
224
892
|
});
|
|
225
|
-
return json(result, 200,
|
|
893
|
+
return json(result, 200, responseOptions);
|
|
226
894
|
}
|
|
227
895
|
const handoffMatch = path.match(/^\/conversations\/([^/]+)\/handoff$/);
|
|
228
896
|
if (request.method === "POST" && handoffMatch) {
|
|
229
|
-
if (!options.runtime.requestHandoff) return json({ error: "Handoff is not supported by this runtime" }, 501,
|
|
897
|
+
if (!options.runtime.requestHandoff) return json({ error: "Handoff is not supported by this runtime" }, 501, responseOptions);
|
|
230
898
|
const conversationId = decodeURIComponent(handoffMatch[1] ?? "");
|
|
231
899
|
const body = await readObject(request);
|
|
232
900
|
const reason = optionalString(body, "reason");
|
|
233
|
-
if (!reason) return json({ error: "reason is required" }, 400,
|
|
901
|
+
if (!reason) return json({ error: "reason is required" }, 400, responseOptions);
|
|
234
902
|
const result = await options.runtime.requestHandoff({
|
|
235
903
|
conversationId,
|
|
236
904
|
reason,
|
|
237
905
|
...optionalStringProperty(body, "summary"),
|
|
238
906
|
...body.payload !== void 0 ? { payload: body.payload } : {}
|
|
239
907
|
});
|
|
240
|
-
return json(result, 200,
|
|
908
|
+
return json(result, 200, responseOptions);
|
|
241
909
|
}
|
|
242
910
|
const closeMatch = path.match(/^\/conversations\/([^/]+)\/close$/);
|
|
243
911
|
if (request.method === "POST" && closeMatch) {
|
|
244
|
-
if (!options.runtime.closeConversation) return json({ error: "Conversation closure is not supported by this runtime" }, 501,
|
|
912
|
+
if (!options.runtime.closeConversation) return json({ error: "Conversation closure is not supported by this runtime" }, 501, responseOptions);
|
|
245
913
|
const conversationId = decodeURIComponent(closeMatch[1] ?? "");
|
|
246
914
|
const body = await readObject(request);
|
|
247
915
|
const conversation = await options.runtime.closeConversation(
|
|
248
916
|
conversationId,
|
|
249
917
|
optionalString(body, "reason")
|
|
250
918
|
);
|
|
251
|
-
return json({ conversation }, 200,
|
|
919
|
+
return json({ conversation }, 200, responseOptions);
|
|
252
920
|
}
|
|
253
921
|
const resumeMatch = path.match(/^\/conversations\/([^/]+)\/resume$/);
|
|
254
922
|
if (request.method === "POST" && resumeMatch) {
|
|
255
|
-
if (!options.runtime.resumeConversation) return json({ error: "Conversation resume is not supported by this runtime" }, 501,
|
|
923
|
+
if (!options.runtime.resumeConversation) return json({ error: "Conversation resume is not supported by this runtime" }, 501, responseOptions);
|
|
256
924
|
const conversationId = decodeURIComponent(resumeMatch[1] ?? "");
|
|
257
925
|
const body = await readObject(request);
|
|
258
926
|
const result = await options.runtime.resumeConversation({
|
|
@@ -260,25 +928,25 @@ function createCognideskHttpHandler(options) {
|
|
|
260
928
|
...optionalStringProperty(body, "reason"),
|
|
261
929
|
...body.payload !== void 0 ? { payload: body.payload } : {}
|
|
262
930
|
});
|
|
263
|
-
return json(result, 200,
|
|
931
|
+
return json(result, 200, responseOptions);
|
|
264
932
|
}
|
|
265
933
|
const intermediateMessageMatch = path.match(/^\/conversations\/([^/]+)\/intermediate-messages$/);
|
|
266
934
|
if (request.method === "POST" && intermediateMessageMatch) {
|
|
267
|
-
if (!options.runtime.emitIntermediateMessage) return json({ error: "Intermediate messages are not supported by this runtime" }, 501,
|
|
935
|
+
if (!options.runtime.emitIntermediateMessage) return json({ error: "Intermediate messages are not supported by this runtime" }, 501, responseOptions);
|
|
268
936
|
const conversationId = decodeURIComponent(intermediateMessageMatch[1] ?? "");
|
|
269
937
|
const body = await readObject(request);
|
|
270
938
|
const text = optionalString(body, "text");
|
|
271
|
-
if (!text) return json({ error: "text is required" }, 400,
|
|
939
|
+
if (!text) return json({ error: "text is required" }, 400, responseOptions);
|
|
272
940
|
const result = await options.runtime.emitIntermediateMessage({
|
|
273
941
|
conversationId,
|
|
274
942
|
text,
|
|
275
943
|
...body.visibleToModel === true ? { visibleToModel: true } : {}
|
|
276
944
|
});
|
|
277
|
-
return json(result, 200,
|
|
945
|
+
return json(result, 200, responseOptions);
|
|
278
946
|
}
|
|
279
947
|
const preambleMatch = path.match(/^\/conversations\/([^/]+)\/preambles$/);
|
|
280
948
|
if (request.method === "POST" && preambleMatch) {
|
|
281
|
-
if (!options.runtime.emitGeneratedPreamble) return json({ error: "Generated preambles are not supported by this runtime" }, 501,
|
|
949
|
+
if (!options.runtime.emitGeneratedPreamble) return json({ error: "Generated preambles are not supported by this runtime" }, 501, responseOptions);
|
|
282
950
|
const conversationId = decodeURIComponent(preambleMatch[1] ?? "");
|
|
283
951
|
const body = await readObject(request);
|
|
284
952
|
const maxWords = optionalNonNegativeNumber(body, "maxWords");
|
|
@@ -288,11 +956,11 @@ function createCognideskHttpHandler(options) {
|
|
|
288
956
|
...maxWords !== void 0 ? { maxWords } : {},
|
|
289
957
|
signal: request.signal
|
|
290
958
|
});
|
|
291
|
-
return json(result, 200,
|
|
959
|
+
return json(result, 200, responseOptions);
|
|
292
960
|
}
|
|
293
961
|
const compactionMatch = path.match(/^\/conversations\/([^/]+)\/compact$/);
|
|
294
962
|
if (request.method === "POST" && compactionMatch) {
|
|
295
|
-
if (!options.runtime.compactConversation) return json({ error: "Conversation compaction is not supported by this runtime" }, 501,
|
|
963
|
+
if (!options.runtime.compactConversation) return json({ error: "Conversation compaction is not supported by this runtime" }, 501, responseOptions);
|
|
296
964
|
const conversationId = decodeURIComponent(compactionMatch[1] ?? "");
|
|
297
965
|
const body = await readObject(request);
|
|
298
966
|
const fromOffset = optionalNonNegativeNumber(body, "fromOffset");
|
|
@@ -304,48 +972,48 @@ function createCognideskHttpHandler(options) {
|
|
|
304
972
|
...optionalStringProperty(body, "schemaVersion"),
|
|
305
973
|
signal: request.signal
|
|
306
974
|
});
|
|
307
|
-
return json(result, 200,
|
|
975
|
+
return json(result, 200, responseOptions);
|
|
308
976
|
}
|
|
309
977
|
const widgetSubmissionMatch = path.match(/^\/conversations\/([^/]+)\/widgets\/([^/]+)\/submissions$/);
|
|
310
978
|
if (request.method === "POST" && widgetSubmissionMatch) {
|
|
311
|
-
if (!options.runtime.submitWidget) return json({ error: "Widget submissions are not supported by this runtime" }, 501,
|
|
979
|
+
if (!options.runtime.submitWidget) return json({ error: "Widget submissions are not supported by this runtime" }, 501, responseOptions);
|
|
312
980
|
const conversationId = decodeURIComponent(widgetSubmissionMatch[1] ?? "");
|
|
313
981
|
const promptId = decodeURIComponent(widgetSubmissionMatch[2] ?? "");
|
|
314
982
|
const body = await readObject(request);
|
|
315
983
|
const widgetKind = optionalString(body, "widgetKind");
|
|
316
|
-
if (!widgetKind) return json({ error: "widgetKind is required" }, 400,
|
|
984
|
+
if (!widgetKind) return json({ error: "widgetKind is required" }, 400, responseOptions);
|
|
317
985
|
const event = await options.runtime.submitWidget({
|
|
318
986
|
conversationId,
|
|
319
987
|
promptId,
|
|
320
988
|
widgetKind,
|
|
321
989
|
output: body.output
|
|
322
990
|
});
|
|
323
|
-
return json({ event }, 200,
|
|
991
|
+
return json({ event }, 200, responseOptions);
|
|
324
992
|
}
|
|
325
993
|
const snapshotMatch = path.match(/^\/conversations\/([^/]+)\/snapshot$/);
|
|
326
994
|
if (request.method === "GET" && snapshotMatch) {
|
|
327
|
-
if (!options.runtime.getSnapshot) return json({ error: "Snapshots are not supported by this runtime" }, 501,
|
|
995
|
+
if (!options.runtime.getSnapshot) return json({ error: "Snapshots are not supported by this runtime" }, 501, responseOptions);
|
|
328
996
|
const conversationId = decodeURIComponent(snapshotMatch[1] ?? "");
|
|
329
997
|
const snapshot = await options.runtime.getSnapshot(conversationId);
|
|
330
|
-
return json({ snapshot }, 200,
|
|
998
|
+
return json({ snapshot }, 200, responseOptions);
|
|
331
999
|
}
|
|
332
1000
|
const replayMatch = path.match(/^\/conversations\/([^/]+)\/replay$/);
|
|
333
1001
|
if (request.method === "GET" && replayMatch) {
|
|
334
|
-
if (!options.runtime.replayConversation) return json({ error: "Event replay is not supported by this runtime" }, 501,
|
|
1002
|
+
if (!options.runtime.replayConversation) return json({ error: "Event replay is not supported by this runtime" }, 501, responseOptions);
|
|
335
1003
|
const conversationId = decodeURIComponent(replayMatch[1] ?? "");
|
|
336
1004
|
const afterOffset = parseOptionalInteger(url.searchParams.get("after"));
|
|
337
1005
|
const replay = await options.runtime.replayConversation({
|
|
338
1006
|
conversationId,
|
|
339
1007
|
...afterOffset !== void 0 ? { afterOffset } : {}
|
|
340
1008
|
});
|
|
341
|
-
return json(replay, 200,
|
|
1009
|
+
return json(replay, 200, responseOptions);
|
|
342
1010
|
}
|
|
343
1011
|
const eventsMatch = path.match(/^\/conversations\/([^/]+)\/events$/);
|
|
344
1012
|
if (request.method === "GET" && eventsMatch) {
|
|
345
1013
|
const conversationId = decodeURIComponent(eventsMatch[1] ?? "");
|
|
346
1014
|
const afterOffset = parseOptionalInteger(url.searchParams.get("after"));
|
|
347
1015
|
const events = await options.runtime.listEvents(conversationId, afterOffset);
|
|
348
|
-
return json({ events }, 200,
|
|
1016
|
+
return json({ events }, 200, responseOptions);
|
|
349
1017
|
}
|
|
350
1018
|
const streamMatch = path.match(/^\/conversations\/([^/]+)\/events\/stream$/);
|
|
351
1019
|
if (request.method === "GET" && streamMatch) {
|
|
@@ -357,92 +1025,31 @@ function createCognideskHttpHandler(options) {
|
|
|
357
1025
|
afterOffset,
|
|
358
1026
|
pollIntervalMs,
|
|
359
1027
|
signal: request.signal,
|
|
360
|
-
responseOptions
|
|
1028
|
+
responseOptions
|
|
361
1029
|
});
|
|
362
1030
|
}
|
|
363
|
-
return json({ error: "Not found" }, 404,
|
|
1031
|
+
return json({ error: "Not found" }, 404, responseOptions);
|
|
364
1032
|
} catch (error) {
|
|
365
1033
|
if (error instanceof HttpInputError) {
|
|
366
|
-
return json({ error: error.message }, error.status,
|
|
1034
|
+
return json({ error: error.message }, error.status, responseOptions);
|
|
367
1035
|
}
|
|
368
1036
|
return json({
|
|
369
|
-
error: error
|
|
370
|
-
}, 500,
|
|
1037
|
+
error: internalErrorMessage(error, options)
|
|
1038
|
+
}, 500, responseOptions);
|
|
371
1039
|
}
|
|
372
1040
|
}
|
|
373
1041
|
};
|
|
374
1042
|
}
|
|
375
|
-
async function readObject(request) {
|
|
376
|
-
const body = await readJson(request);
|
|
377
|
-
if (!isRecord(body)) throw new HttpInputError("Request body must be a JSON object.");
|
|
378
|
-
return body;
|
|
379
|
-
}
|
|
380
|
-
function optionalString(body, key) {
|
|
381
|
-
const value = body[key];
|
|
382
|
-
if (value === void 0 || value === null) return void 0;
|
|
383
|
-
if (typeof value !== "string") throw new HttpInputError(`${key} must be a string.`);
|
|
384
|
-
const trimmed = value.trim();
|
|
385
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
386
|
-
}
|
|
387
|
-
function optionalStringProperty(body, key) {
|
|
388
|
-
const value = optionalString(body, key);
|
|
389
|
-
return value ? { [key]: value } : {};
|
|
390
|
-
}
|
|
391
|
-
function optionalNonNegativeNumber(body, key) {
|
|
392
|
-
const value = body[key];
|
|
393
|
-
if (value === void 0 || value === null) return void 0;
|
|
394
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
395
|
-
throw new HttpInputError(`${key} must be a non-negative integer.`);
|
|
396
|
-
}
|
|
397
|
-
return value;
|
|
398
|
-
}
|
|
399
|
-
function optionalRouting(body, key) {
|
|
400
|
-
const value = optionalString(body, key);
|
|
401
|
-
if (!value) return void 0;
|
|
402
|
-
if (value !== "none" && value !== "activeJourneyOnly" && value !== "full" && value !== "targeted") {
|
|
403
|
-
throw new HttpInputError(`${key} must be a valid routing mode.`);
|
|
404
|
-
}
|
|
405
|
-
return value;
|
|
406
|
-
}
|
|
407
|
-
function optionalTarget(body, key) {
|
|
408
|
-
const value = body[key];
|
|
409
|
-
if (value === void 0 || value === null) return void 0;
|
|
410
|
-
if (!isRecord(value)) throw new HttpInputError(`${key} must be an object.`);
|
|
411
|
-
const journeyId = optionalString(value, "journeyId");
|
|
412
|
-
const stateId = optionalString(value, "stateId");
|
|
413
|
-
return {
|
|
414
|
-
...journeyId ? { journeyId } : {},
|
|
415
|
-
...stateId ? { stateId } : {}
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
function optionalVoiceClient(body) {
|
|
419
|
-
const value = body.client;
|
|
420
|
-
if (value === void 0 || value === null) return {};
|
|
421
|
-
if (!isRecord(value)) throw new HttpInputError("client must be an object.");
|
|
422
|
-
const userAgent = optionalString(value, "userAgent");
|
|
423
|
-
const locale = optionalString(value, "locale");
|
|
424
|
-
const metadata = value.metadata;
|
|
425
|
-
if (metadata !== void 0 && metadata !== null && !isRecord(metadata)) {
|
|
426
|
-
throw new HttpInputError("client.metadata must be an object.");
|
|
427
|
-
}
|
|
428
|
-
return {
|
|
429
|
-
client: {
|
|
430
|
-
...userAgent ? { userAgent } : {},
|
|
431
|
-
...locale ? { locale } : {},
|
|
432
|
-
...isRecord(metadata) ? { metadata } : {}
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function withVoiceEventsUrl(result, basePath) {
|
|
437
|
-
return {
|
|
438
|
-
...result,
|
|
439
|
-
eventsUrl: `${basePath}/conversations/${encodeURIComponent(result.conversation.id)}/events/stream`
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
function isRecord(value) {
|
|
443
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
444
|
-
}
|
|
445
1043
|
export {
|
|
446
|
-
|
|
1044
|
+
createChannelEventInput,
|
|
1045
|
+
createChannelEventRequestBody,
|
|
1046
|
+
createChannelHandoffEventInput,
|
|
1047
|
+
createChannelHandoffReviewEventInput,
|
|
1048
|
+
createChannelOutputResolutionEventInput,
|
|
1049
|
+
createCognideskHttpHandler,
|
|
1050
|
+
createMessageChannelEventInput,
|
|
1051
|
+
createOutboundContactChannelEventInput,
|
|
1052
|
+
createProviderObjectChannelEventInput,
|
|
1053
|
+
createScheduledChannelEventInput,
|
|
1054
|
+
createVoiceTurnChannelEventInput
|
|
447
1055
|
};
|
|
448
|
-
//# sourceMappingURL=index.js.map
|