@kognitivedev/cloud-voice 0.2.29
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/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-test.log +13 -0
- package/CHANGELOG.md +10 -0
- package/README.md +226 -0
- package/dist/browser.d.ts +7 -0
- package/dist/browser.js +301 -0
- package/dist/client.d.ts +219 -0
- package/dist/client.js +535 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +15 -0
- package/dist/server.d.ts +58 -0
- package/dist/server.js +92 -0
- package/dist/server.test.d.ts +1 -0
- package/dist/server.test.js +78 -0
- package/dist/sse.d.ts +7 -0
- package/dist/sse.js +75 -0
- package/dist/types.d.ts +865 -0
- package/dist/types.js +2 -0
- package/package.json +52 -0
- package/src/__tests__/browser.test.ts +196 -0
- package/src/__tests__/client.test.ts +482 -0
- package/src/__tests__/server.test.ts +84 -0
- package/src/browser.ts +342 -0
- package/src/client.ts +610 -0
- package/src/index.ts +100 -0
- package/src/server.ts +140 -0
- package/src/sse.ts +57 -0
- package/src/types.ts +927 -0
- package/tsconfig.json +14 -0
- package/vitest.config.ts +8 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export { KognitiveCloudVoiceClient, KognitiveCloudVoiceEmbedClient } from "./client";
|
|
2
|
+
export { createCloudVoiceTools, syncCloudVoiceCodeDefinedTools } from "./server";
|
|
3
|
+
export { createVoiceSession, createVoiceWidget, mountIframe } from "./browser";
|
|
4
|
+
export { readSSEStream } from "./sse";
|
|
5
|
+
export type { ParsedSSEEvent } from "./sse";
|
|
6
|
+
export type {
|
|
7
|
+
CloudVoiceAgentConfig,
|
|
8
|
+
CloudVoiceAgentConfigInput,
|
|
9
|
+
CloudVoiceAgentMetadata,
|
|
10
|
+
CloudVoiceAgentRecord,
|
|
11
|
+
CloudVoiceAgentStatus,
|
|
12
|
+
CloudVoiceChannel,
|
|
13
|
+
CloudVoiceChannelSettings,
|
|
14
|
+
CloudVoiceClientTool,
|
|
15
|
+
CloudVoiceClientConfig,
|
|
16
|
+
CloudVoiceClientToolDefinition,
|
|
17
|
+
CloudVoiceClientToolExecuteContext,
|
|
18
|
+
CloudVoiceClientToolManifest,
|
|
19
|
+
CloudVoiceClientToolRenderContext,
|
|
20
|
+
CloudVoiceClientToolRenderer,
|
|
21
|
+
CloudVoiceClientTools,
|
|
22
|
+
CloudVoiceClientToolState,
|
|
23
|
+
CloudVoiceCustomVoiceConsent,
|
|
24
|
+
CloudVoiceCustomVoiceRecord,
|
|
25
|
+
CloudVoiceCustomVoiceSource,
|
|
26
|
+
CloudVoiceCustomVoiceVerificationStatus,
|
|
27
|
+
CloudVoiceHumanizationConfig,
|
|
28
|
+
CloudVoiceSipConfig,
|
|
29
|
+
CloudVoiceTelephonyDestination,
|
|
30
|
+
CloudVoiceTelephonyProvider,
|
|
31
|
+
CloudVoiceTransferDestination,
|
|
32
|
+
CloudVoiceTransferMode,
|
|
33
|
+
CloudVoiceTransferPolicy,
|
|
34
|
+
CloudVoiceFlowEdge,
|
|
35
|
+
CloudVoiceFlowGraph,
|
|
36
|
+
CloudVoiceFlowNode,
|
|
37
|
+
CloudVoiceFlowNodeOutput,
|
|
38
|
+
CloudVoiceFlowNodeType,
|
|
39
|
+
CloudVoiceNodeModelSettings,
|
|
40
|
+
CloudVoiceNodeVoiceSettings,
|
|
41
|
+
CloudVoiceParameterDefinition,
|
|
42
|
+
CloudVoiceParameterPreset,
|
|
43
|
+
CloudVoiceParameterSource,
|
|
44
|
+
CloudVoiceParameterType,
|
|
45
|
+
CloudVoiceParameterValueMap,
|
|
46
|
+
CloudVoiceSpeechConfig,
|
|
47
|
+
CloudVoiceEmbedConfig,
|
|
48
|
+
CloudVoiceEmbedOptions,
|
|
49
|
+
CloudVoiceCallLegRecord,
|
|
50
|
+
CloudVoiceCallOutcome,
|
|
51
|
+
CloudVoiceCallOutcomeSource,
|
|
52
|
+
CloudVoiceCallStatusChange,
|
|
53
|
+
CloudVoiceCallWaitOptions,
|
|
54
|
+
CloudVoiceCallWaitResult,
|
|
55
|
+
CloudVoiceHandoffAction,
|
|
56
|
+
CloudVoiceHandoffMode,
|
|
57
|
+
CloudVoiceHandoffRecord,
|
|
58
|
+
CloudVoiceHandoffStatus,
|
|
59
|
+
CloudVoiceCallJudgeResult,
|
|
60
|
+
CloudVoiceListenTokenResult,
|
|
61
|
+
CloudVoiceLiveCallRecord,
|
|
62
|
+
CloudVoiceLiveCallHandoffState,
|
|
63
|
+
CloudVoiceLiveCallTranscriptMessage,
|
|
64
|
+
CloudVoicePhoneBridgeHealth,
|
|
65
|
+
CloudVoicePhoneConnectionRecord,
|
|
66
|
+
CloudVoicePhoneNumberRecord,
|
|
67
|
+
CloudVoiceSipDiagnostics,
|
|
68
|
+
CloudVoiceRecordingAsset,
|
|
69
|
+
CloudVoiceRecordingAssetKind,
|
|
70
|
+
CloudVoiceSessionBootstrap,
|
|
71
|
+
CloudVoiceSessionEventRecord,
|
|
72
|
+
CloudVoiceSessionRecord,
|
|
73
|
+
CloudVoiceSessionStatus,
|
|
74
|
+
CloudVoiceProvider,
|
|
75
|
+
CloudVoiceToolType,
|
|
76
|
+
CloudVoiceTransport,
|
|
77
|
+
CloudVoiceToolBinding,
|
|
78
|
+
CloudVoiceToolCatalog,
|
|
79
|
+
CloudVoiceToolCatalogEntry,
|
|
80
|
+
CloudVoiceToolCatalogSource,
|
|
81
|
+
CloudVoiceWidgetConfig,
|
|
82
|
+
CreateCloudVoiceAgentInput,
|
|
83
|
+
CloneCloudVoiceCustomVoiceInput,
|
|
84
|
+
ImportCloudVoiceCustomVoiceInput,
|
|
85
|
+
AcceptCloudVoiceHandoffInput,
|
|
86
|
+
CancelCloudVoiceHandoffInput,
|
|
87
|
+
CreateCloudVoiceHandoffInput,
|
|
88
|
+
CreateCloudVoiceOutboundCallInput,
|
|
89
|
+
CreateCloudVoicePhoneConnectionInput,
|
|
90
|
+
CreateCloudVoicePhoneNumberInput,
|
|
91
|
+
CreateCloudVoiceSessionInput,
|
|
92
|
+
LogLevel,
|
|
93
|
+
UpdateCloudVoiceAgentInput,
|
|
94
|
+
UpdateCloudVoiceCustomVoiceInput,
|
|
95
|
+
UpdateCloudVoicePhoneNumberInput,
|
|
96
|
+
SyncCloudVoiceToolCatalogInput,
|
|
97
|
+
UseCloudVoiceAgentInput,
|
|
98
|
+
UseCloudVoiceAgentResult,
|
|
99
|
+
UseCloudVoiceAgentAction,
|
|
100
|
+
} from "./types";
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { KognitiveCloudVoiceClient } from "./client";
|
|
3
|
+
import type { CloudVoiceToolCatalog, SyncCloudVoiceToolCatalogInput } from "./types";
|
|
4
|
+
|
|
5
|
+
export interface CloudVoiceToolRequest<TInput = unknown> {
|
|
6
|
+
type: "cloud_voice.tool.execute";
|
|
7
|
+
toolCallId: string;
|
|
8
|
+
toolId: string;
|
|
9
|
+
toolName?: string;
|
|
10
|
+
input: TInput;
|
|
11
|
+
session: {
|
|
12
|
+
id: string;
|
|
13
|
+
sessionId: string;
|
|
14
|
+
agentSlug: string;
|
|
15
|
+
channel: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
resourceId: Record<string, unknown>;
|
|
18
|
+
parameters: Record<string, unknown>;
|
|
19
|
+
metadata: Record<string, unknown>;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CloudVoiceToolContext<TInput = unknown> {
|
|
24
|
+
request: CloudVoiceToolRequest<TInput>;
|
|
25
|
+
abortSignal: AbortSignal;
|
|
26
|
+
toolCallId: string;
|
|
27
|
+
session: CloudVoiceToolRequest<TInput>["session"];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CloudVoiceToolDefinition<TInput = unknown, TOutput = unknown> {
|
|
31
|
+
description?: string;
|
|
32
|
+
input?: { parse: (value: unknown) => TInput };
|
|
33
|
+
execute: (input: TInput, context: CloudVoiceToolContext<TInput>) => Promise<TOutput> | TOutput;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type CloudVoiceToolMap = Record<string, CloudVoiceToolDefinition<any, any> | ((input: any, context: CloudVoiceToolContext<any>) => unknown)>;
|
|
37
|
+
|
|
38
|
+
export interface CreateCloudVoiceToolsOptions<TTools extends CloudVoiceToolMap> {
|
|
39
|
+
tools: TTools;
|
|
40
|
+
secret?: string;
|
|
41
|
+
signatureToleranceMs?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function json(data: unknown, status = 200) {
|
|
45
|
+
return new Response(JSON.stringify(data), {
|
|
46
|
+
status,
|
|
47
|
+
headers: { "Content-Type": "application/json" },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function verifySignature(input: { body: string; timestamp: string | null; signature: string | null; secret?: string; toleranceMs: number }) {
|
|
52
|
+
if (!input.secret) return;
|
|
53
|
+
if (!input.timestamp || !input.signature?.startsWith("sha256=")) {
|
|
54
|
+
throw new Error("Missing Kognitive signature");
|
|
55
|
+
}
|
|
56
|
+
const timestampMs = Date.parse(input.timestamp);
|
|
57
|
+
if (!Number.isFinite(timestampMs) || Math.abs(Date.now() - timestampMs) > input.toleranceMs) {
|
|
58
|
+
throw new Error("Kognitive signature timestamp is outside the allowed window");
|
|
59
|
+
}
|
|
60
|
+
const expected = `sha256=${createHmac("sha256", input.secret).update(`${input.timestamp}.${input.body}`).digest("hex")}`;
|
|
61
|
+
const actualBuffer = Buffer.from(input.signature);
|
|
62
|
+
const expectedBuffer = Buffer.from(expected);
|
|
63
|
+
if (actualBuffer.length !== expectedBuffer.length || !timingSafeEqual(actualBuffer, expectedBuffer)) {
|
|
64
|
+
throw new Error("Invalid Kognitive signature");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveTool<TTools extends CloudVoiceToolMap>(tools: TTools, toolId: string) {
|
|
69
|
+
return tools[toolId] ?? tools[toolId.replace(/-/g, "_")] ?? tools[toolId.replace(/_/g, "-")];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function createCloudVoiceTools<TTools extends CloudVoiceToolMap>(options: CreateCloudVoiceToolsOptions<TTools>) {
|
|
73
|
+
return async function handleCloudVoiceTool(request: Request): Promise<Response> {
|
|
74
|
+
const rawBody = await request.text();
|
|
75
|
+
try {
|
|
76
|
+
verifySignature({
|
|
77
|
+
body: rawBody,
|
|
78
|
+
timestamp: request.headers.get("x-kognitive-timestamp"),
|
|
79
|
+
signature: request.headers.get("x-kognitive-signature"),
|
|
80
|
+
secret: options.secret,
|
|
81
|
+
toleranceMs: options.signatureToleranceMs ?? 5 * 60 * 1000,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const payload = JSON.parse(rawBody || "{}") as CloudVoiceToolRequest;
|
|
85
|
+
const toolId = payload.toolId || payload.toolName || "";
|
|
86
|
+
const tool = resolveTool(options.tools, toolId);
|
|
87
|
+
if (!tool) {
|
|
88
|
+
return json({ error: `Unknown Cloud Voice tool: ${toolId}` }, 404);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const definition = typeof tool === "function" ? { execute: tool } : tool;
|
|
92
|
+
const input = definition.input?.parse ? definition.input.parse(payload.input) : payload.input;
|
|
93
|
+
const result = await definition.execute(input, {
|
|
94
|
+
request: payload,
|
|
95
|
+
abortSignal: request.signal,
|
|
96
|
+
toolCallId: payload.toolCallId,
|
|
97
|
+
session: payload.session,
|
|
98
|
+
});
|
|
99
|
+
return json({ result });
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return json({ error: error instanceof Error ? error.message : "Cloud Voice tool failed" }, 400);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function syncCloudVoiceCodeDefinedTools(input: {
|
|
107
|
+
kognitive: { getToolMetadataList: () => Array<{ id: string; description?: string; inputSchema?: unknown; requireApproval?: boolean }> };
|
|
108
|
+
sourceKey: string;
|
|
109
|
+
bridgeUrl: string;
|
|
110
|
+
name?: string;
|
|
111
|
+
bridgeAuthorization?: string | null;
|
|
112
|
+
bridgeToken?: string | null;
|
|
113
|
+
metadata?: Record<string, unknown>;
|
|
114
|
+
client?: KognitiveCloudVoiceClient;
|
|
115
|
+
baseUrl?: string;
|
|
116
|
+
apiKey?: string;
|
|
117
|
+
}): Promise<CloudVoiceToolCatalog> {
|
|
118
|
+
const client = input.client ?? new KognitiveCloudVoiceClient({
|
|
119
|
+
baseUrl: input.baseUrl ?? process.env.COGNITIVE_API_URL ?? "http://localhost:3001",
|
|
120
|
+
apiKey: input.apiKey ?? process.env.COGNITIVE_API_KEY,
|
|
121
|
+
});
|
|
122
|
+
const payload: SyncCloudVoiceToolCatalogInput = {
|
|
123
|
+
sourceKey: input.sourceKey,
|
|
124
|
+
name: input.name,
|
|
125
|
+
bridgeUrl: input.bridgeUrl,
|
|
126
|
+
bridgeAuthorization: input.bridgeAuthorization,
|
|
127
|
+
bridgeToken: input.bridgeToken,
|
|
128
|
+
metadata: input.metadata,
|
|
129
|
+
tools: input.kognitive.getToolMetadataList().map((tool) => ({
|
|
130
|
+
id: tool.id,
|
|
131
|
+
name: tool.id,
|
|
132
|
+
description: tool.description,
|
|
133
|
+
inputSchema: tool.inputSchema && typeof tool.inputSchema === "object" && !Array.isArray(tool.inputSchema)
|
|
134
|
+
? tool.inputSchema as Record<string, unknown>
|
|
135
|
+
: { type: "object", additionalProperties: true },
|
|
136
|
+
requireApproval: Boolean(tool.requireApproval),
|
|
137
|
+
})),
|
|
138
|
+
};
|
|
139
|
+
return client.tools.catalog.sync(payload);
|
|
140
|
+
}
|
package/src/sse.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export interface ParsedSSEEvent {
|
|
2
|
+
event?: string;
|
|
3
|
+
data: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
retry?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function* readSSEStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<ParsedSSEEvent> {
|
|
9
|
+
const reader = stream.getReader();
|
|
10
|
+
const decoder = new TextDecoder();
|
|
11
|
+
let buffer = "";
|
|
12
|
+
let event = "";
|
|
13
|
+
let data: string[] = [];
|
|
14
|
+
let id: string | undefined;
|
|
15
|
+
let retry: number | undefined;
|
|
16
|
+
|
|
17
|
+
function flush(): ParsedSSEEvent | null {
|
|
18
|
+
if (!event && data.length === 0 && !id && retry === undefined) return null;
|
|
19
|
+
const frame = {
|
|
20
|
+
event: event || undefined,
|
|
21
|
+
data: data.join("\n"),
|
|
22
|
+
id,
|
|
23
|
+
retry,
|
|
24
|
+
};
|
|
25
|
+
event = "";
|
|
26
|
+
data = [];
|
|
27
|
+
id = undefined;
|
|
28
|
+
retry = undefined;
|
|
29
|
+
return frame;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
while (true) {
|
|
33
|
+
const { value, done } = await reader.read();
|
|
34
|
+
if (done) break;
|
|
35
|
+
buffer += decoder.decode(value, { stream: true });
|
|
36
|
+
const lines = buffer.split(/\r\n|\r|\n/);
|
|
37
|
+
buffer = lines.pop() ?? "";
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
if (line === "") {
|
|
40
|
+
const frame = flush();
|
|
41
|
+
if (frame) yield frame;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (line.startsWith(":")) continue;
|
|
45
|
+
const separator = line.indexOf(":");
|
|
46
|
+
const field = separator === -1 ? line : line.slice(0, separator);
|
|
47
|
+
const rawValue = separator === -1 ? "" : line.slice(separator + 1).replace(/^ /, "");
|
|
48
|
+
if (field === "event") event = rawValue;
|
|
49
|
+
if (field === "data") data.push(rawValue);
|
|
50
|
+
if (field === "id") id = rawValue;
|
|
51
|
+
if (field === "retry") retry = Number(rawValue);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const tail = flush();
|
|
55
|
+
if (tail) yield tail;
|
|
56
|
+
}
|
|
57
|
+
|