@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/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kognitivedev/cloud-voice",
|
|
3
|
+
"version": "0.2.29",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./browser": {
|
|
12
|
+
"types": "./dist/browser.d.ts",
|
|
13
|
+
"default": "./dist/browser.js"
|
|
14
|
+
},
|
|
15
|
+
"./server": {
|
|
16
|
+
"types": "./dist/server.d.ts",
|
|
17
|
+
"default": "./dist/server.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"dev": "tsc -w --noCheck",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@kognitivedev/client-core": "^0.2.29"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"typescript": "^5.0.0",
|
|
35
|
+
"vitest": "^3.0.0"
|
|
36
|
+
},
|
|
37
|
+
"description": "Cloud voice agents SDK for Kognitive hosted voice APIs",
|
|
38
|
+
"keywords": [
|
|
39
|
+
"kognitive",
|
|
40
|
+
"voice",
|
|
41
|
+
"cloud",
|
|
42
|
+
"sdk",
|
|
43
|
+
"agents"
|
|
44
|
+
],
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/kognitivedev/kognitive",
|
|
49
|
+
"directory": "packages/cloud-voice"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://kognitive.dev"
|
|
52
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { createVoiceWidget } from "../browser";
|
|
3
|
+
|
|
4
|
+
type Listener = (event: any) => void;
|
|
5
|
+
|
|
6
|
+
class FakeElement {
|
|
7
|
+
children: FakeElement[] = [];
|
|
8
|
+
dataset: Record<string, string> = {};
|
|
9
|
+
style: Record<string, string> = {};
|
|
10
|
+
className = "";
|
|
11
|
+
textContent = "";
|
|
12
|
+
attributes: Record<string, string> = {};
|
|
13
|
+
parent: FakeElement | null = null;
|
|
14
|
+
contentWindow?: { postMessage: ReturnType<typeof vi.fn> };
|
|
15
|
+
|
|
16
|
+
constructor(public readonly tagName = "div") {
|
|
17
|
+
if (tagName === "iframe") {
|
|
18
|
+
this.contentWindow = { postMessage: vi.fn() };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
appendChild(child: FakeElement) {
|
|
23
|
+
child.parent = this;
|
|
24
|
+
this.children.push(child);
|
|
25
|
+
return child;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
append(...children: FakeElement[]) {
|
|
29
|
+
for (const child of children) this.appendChild(child);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
remove() {
|
|
33
|
+
if (!this.parent) return;
|
|
34
|
+
this.parent.children = this.parent.children.filter((child) => child !== this);
|
|
35
|
+
this.parent = null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
replaceChildren(...children: FakeElement[]) {
|
|
39
|
+
this.children = [];
|
|
40
|
+
for (const child of children) this.appendChild(child);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setAttribute(name: string, value: string) {
|
|
44
|
+
this.attributes[name] = value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
addEventListener() {}
|
|
48
|
+
removeEventListener() {}
|
|
49
|
+
dispatchEvent() {}
|
|
50
|
+
|
|
51
|
+
querySelector(selector: string) {
|
|
52
|
+
const match = selector.match(/data-kognitive-tool-call-id="([^"]+)"/);
|
|
53
|
+
if (!match) return null;
|
|
54
|
+
return this.children.find((child) => child.dataset.kognitiveToolCallId === match[1]) ?? null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe("createVoiceWidget", () => {
|
|
59
|
+
let body: FakeElement;
|
|
60
|
+
let listeners: Record<string, Set<Listener>>;
|
|
61
|
+
let originalWindow: unknown;
|
|
62
|
+
let originalDocument: unknown;
|
|
63
|
+
let originalHTMLElement: unknown;
|
|
64
|
+
let originalNode: unknown;
|
|
65
|
+
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
body = new FakeElement("body");
|
|
68
|
+
listeners = {};
|
|
69
|
+
originalWindow = (globalThis as any).window;
|
|
70
|
+
originalDocument = (globalThis as any).document;
|
|
71
|
+
originalHTMLElement = (globalThis as any).HTMLElement;
|
|
72
|
+
originalNode = (globalThis as any).Node;
|
|
73
|
+
(globalThis as any).HTMLElement = FakeElement;
|
|
74
|
+
(globalThis as any).Node = FakeElement;
|
|
75
|
+
(globalThis as any).document = {
|
|
76
|
+
body,
|
|
77
|
+
createElement: (tagName: string) => new FakeElement(tagName),
|
|
78
|
+
querySelector: () => body,
|
|
79
|
+
};
|
|
80
|
+
(globalThis as any).window = {
|
|
81
|
+
addEventListener: (type: string, listener: Listener) => {
|
|
82
|
+
listeners[type] ??= new Set();
|
|
83
|
+
listeners[type].add(listener);
|
|
84
|
+
},
|
|
85
|
+
removeEventListener: (type: string, listener: Listener) => {
|
|
86
|
+
listeners[type]?.delete(listener);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
(globalThis as any).window = originalWindow;
|
|
93
|
+
(globalThis as any).document = originalDocument;
|
|
94
|
+
(globalThis as any).HTMLElement = originalHTMLElement;
|
|
95
|
+
(globalThis as any).Node = originalNode;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("registers executable client tools with the iframe", () => {
|
|
99
|
+
const widget = createVoiceWidget({
|
|
100
|
+
baseUrl: "https://dashboard.example.com",
|
|
101
|
+
publicKey: "kvp_test",
|
|
102
|
+
agent: "support",
|
|
103
|
+
tools: {
|
|
104
|
+
lookup_order: {
|
|
105
|
+
description: "Look up an order",
|
|
106
|
+
inputSchema: { type: "object", properties: { orderNumber: { type: "string" } } },
|
|
107
|
+
execute: async () => ({ ok: true }),
|
|
108
|
+
},
|
|
109
|
+
render_only: () => "rendered",
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const iframe = widget.iframe as unknown as FakeElement;
|
|
114
|
+
listeners.message.forEach((listener) => listener({
|
|
115
|
+
source: iframe.contentWindow,
|
|
116
|
+
data: { type: "kognitive.cloudVoice.iframe.ready" },
|
|
117
|
+
}));
|
|
118
|
+
|
|
119
|
+
expect(iframe.contentWindow?.postMessage).toHaveBeenCalledWith({
|
|
120
|
+
type: "kognitive.cloudVoice.tools.register",
|
|
121
|
+
tools: [{
|
|
122
|
+
id: "lookup_order",
|
|
123
|
+
name: "lookup_order",
|
|
124
|
+
description: "Look up an order",
|
|
125
|
+
inputSchema: { type: "object", properties: { orderNumber: { type: "string" } } },
|
|
126
|
+
}],
|
|
127
|
+
}, "https://dashboard.example.com");
|
|
128
|
+
|
|
129
|
+
widget.destroy();
|
|
130
|
+
expect(listeners.message.size).toBe(0);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("includes startup parameters in the embedded iframe URL", () => {
|
|
134
|
+
const widget = createVoiceWidget({
|
|
135
|
+
baseUrl: "https://dashboard.example.com",
|
|
136
|
+
publicKey: "kvp_test",
|
|
137
|
+
agent: "support",
|
|
138
|
+
parameters: { customer_name: "Ada", account_tier: "gold" },
|
|
139
|
+
});
|
|
140
|
+
const iframe = widget.iframe as unknown as FakeElement & { src: string };
|
|
141
|
+
const url = new URL(iframe.src);
|
|
142
|
+
|
|
143
|
+
expect(url.searchParams.get("parameters")).toBe(JSON.stringify({ customer_name: "Ada", account_tier: "gold" }));
|
|
144
|
+
widget.destroy();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("executes client tools and posts results back to the iframe", async () => {
|
|
148
|
+
const execute = vi.fn(async (input: unknown) => ({ status: "ok", input }));
|
|
149
|
+
const onToolState = vi.fn();
|
|
150
|
+
const widget = createVoiceWidget({
|
|
151
|
+
baseUrl: "https://dashboard.example.com",
|
|
152
|
+
publicKey: "kvp_test",
|
|
153
|
+
agent: "support",
|
|
154
|
+
tools: {
|
|
155
|
+
lookup_order: {
|
|
156
|
+
execute,
|
|
157
|
+
render: ({ state }) => `state:${state}`,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
onToolState,
|
|
161
|
+
});
|
|
162
|
+
const iframe = widget.iframe as unknown as FakeElement;
|
|
163
|
+
|
|
164
|
+
listeners.message.forEach((listener) => listener({
|
|
165
|
+
source: iframe.contentWindow,
|
|
166
|
+
data: {
|
|
167
|
+
type: "kognitive.cloudVoice.tool.call",
|
|
168
|
+
requestId: "request_1",
|
|
169
|
+
toolName: "lookup_order",
|
|
170
|
+
args: { orderNumber: "KV-1042" },
|
|
171
|
+
},
|
|
172
|
+
}));
|
|
173
|
+
|
|
174
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
175
|
+
|
|
176
|
+
expect(execute).toHaveBeenCalledWith({ orderNumber: "KV-1042" }, {
|
|
177
|
+
toolName: "lookup_order",
|
|
178
|
+
toolCallId: "request_1",
|
|
179
|
+
callId: undefined,
|
|
180
|
+
sessionId: undefined,
|
|
181
|
+
});
|
|
182
|
+
expect(iframe.contentWindow?.postMessage).toHaveBeenCalledWith({
|
|
183
|
+
type: "kognitive.cloudVoice.tool.result",
|
|
184
|
+
requestId: "request_1",
|
|
185
|
+
result: { status: "ok", input: { orderNumber: "KV-1042" } },
|
|
186
|
+
}, "https://dashboard.example.com");
|
|
187
|
+
expect(onToolState).toHaveBeenCalledWith(expect.objectContaining({
|
|
188
|
+
toolName: "lookup_order",
|
|
189
|
+
state: "output-available",
|
|
190
|
+
}));
|
|
191
|
+
const toolRoot = body.children[0];
|
|
192
|
+
expect(toolRoot.children).toHaveLength(1);
|
|
193
|
+
expect(toolRoot.children[0].dataset.kognitiveToolCallId).toBe("request_1");
|
|
194
|
+
expect(toolRoot.children[0].textContent).toBe("state:output-available");
|
|
195
|
+
});
|
|
196
|
+
});
|