kernl 0.8.4 → 0.9.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +15 -0
- package/dist/agent/base.d.ts +73 -0
- package/dist/agent/base.d.ts.map +1 -0
- package/dist/agent/base.js +137 -0
- package/dist/agent/index.d.ts +2 -0
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +2 -1
- package/dist/agent/types.d.ts +4 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent.d.ts +10 -90
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +5 -171
- package/dist/api/resources/agents/agents.d.ts +11 -7
- package/dist/api/resources/agents/agents.d.ts.map +1 -1
- package/dist/api/resources/agents/agents.js +14 -8
- package/dist/kernl/kernl.d.ts +2 -2
- package/dist/kernl/kernl.d.ts.map +1 -1
- package/dist/kernl/kernl.js +6 -6
- package/dist/kernl/types.d.ts +3 -3
- package/dist/kernl/types.d.ts.map +1 -1
- package/dist/lib/env.d.ts +2 -2
- package/dist/mcp/__tests__/utils.test.js +4 -2
- package/dist/mcp/utils.d.ts +1 -1
- package/dist/mcp/utils.js +1 -1
- package/dist/realtime/agent.d.ts +17 -0
- package/dist/realtime/agent.d.ts.map +1 -0
- package/dist/realtime/agent.js +17 -0
- package/dist/realtime/channel.d.ts +30 -0
- package/dist/realtime/channel.d.ts.map +1 -0
- package/dist/realtime/channel.js +1 -0
- package/dist/realtime/index.d.ts +5 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +4 -0
- package/dist/realtime/session.d.ts +98 -0
- package/dist/realtime/session.d.ts.map +1 -0
- package/dist/realtime/session.js +203 -0
- package/dist/realtime/types.d.ts +58 -0
- package/dist/realtime/types.d.ts.map +1 -0
- package/dist/realtime/types.js +1 -0
- package/dist/storage/in-memory.d.ts.map +1 -1
- package/dist/storage/in-memory.js +5 -1
- package/dist/tool/__tests__/toolkit.test.js +2 -2
- package/dist/tool/tool.d.ts +2 -1
- package/dist/tool/tool.d.ts.map +1 -1
- package/dist/tool/toolkit.d.ts +4 -4
- package/dist/tool/toolkit.d.ts.map +1 -1
- package/dist/tool/toolkit.js +2 -1
- package/dist/tool/types.d.ts +4 -4
- package/dist/tool/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/agent/base.ts +220 -0
- package/src/agent/index.ts +2 -0
- package/src/agent/types.ts +5 -0
- package/src/agent.ts +12 -231
- package/src/api/resources/agents/agents.ts +19 -13
- package/src/kernl/kernl.ts +9 -9
- package/src/kernl/types.ts +3 -3
- package/src/mcp/__tests__/utils.test.ts +4 -2
- package/src/mcp/utils.ts +1 -1
- package/src/realtime/agent.ts +24 -0
- package/src/realtime/channel.ts +32 -0
- package/src/realtime/index.ts +4 -0
- package/src/realtime/session.ts +259 -0
- package/src/realtime/types.ts +73 -0
- package/src/storage/in-memory.ts +9 -1
- package/src/tool/__tests__/toolkit.test.ts +2 -2
- package/src/tool/tool.ts +2 -1
- package/src/tool/toolkit.ts +6 -5
- package/src/tool/types.ts +4 -4
package/src/agent.ts
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
} from "@kernl-sdk/protocol";
|
|
7
7
|
|
|
8
8
|
import { Thread } from "./thread";
|
|
9
|
-
import type { Kernl } from "./kernl";
|
|
10
9
|
import type {
|
|
11
10
|
RThreadsListParams,
|
|
12
11
|
RThreadCreateParams,
|
|
@@ -15,30 +14,16 @@ import type {
|
|
|
15
14
|
RThreadUpdateParams,
|
|
16
15
|
} from "@/api/resources/threads/types";
|
|
17
16
|
import type { Context, UnknownContext } from "./context";
|
|
18
|
-
import { Tool, memory } from "./tool";
|
|
19
|
-
import { BaseToolkit } from "./tool/toolkit";
|
|
20
17
|
import {
|
|
21
18
|
InputGuardrail,
|
|
22
19
|
OutputGuardrail,
|
|
23
20
|
type ResolvedAgentResponse,
|
|
24
21
|
} from "./guardrail";
|
|
25
|
-
import {
|
|
26
|
-
import type {
|
|
27
|
-
AgentMemoryCreate,
|
|
28
|
-
AgentMemoryUpdate,
|
|
29
|
-
MemoryListOptions,
|
|
30
|
-
MemorySearchQuery,
|
|
31
|
-
} from "./memory";
|
|
22
|
+
import { BaseAgent } from "./agent/base";
|
|
32
23
|
|
|
33
|
-
import { randomID } from "@kernl-sdk/shared/lib";
|
|
34
24
|
import { MisconfiguredError, RuntimeError } from "./lib/error";
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
import type {
|
|
38
|
-
AgentConfig,
|
|
39
|
-
AgentMemoryConfig,
|
|
40
|
-
AgentOutputType,
|
|
41
|
-
} from "./agent/types";
|
|
26
|
+
import type { AgentConfig, AgentOutputType } from "./agent/types";
|
|
42
27
|
import type {
|
|
43
28
|
TextOutput,
|
|
44
29
|
ThreadExecuteOptions,
|
|
@@ -50,74 +35,30 @@ export class Agent<
|
|
|
50
35
|
TContext = UnknownContext,
|
|
51
36
|
TOutput extends AgentOutputType = TextOutput,
|
|
52
37
|
>
|
|
53
|
-
extends
|
|
38
|
+
extends BaseAgent<TContext, TOutput>
|
|
54
39
|
implements AgentConfig<TContext, TOutput>
|
|
55
40
|
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
name: string;
|
|
60
|
-
description?: string;
|
|
61
|
-
instructions: (context: Context<TContext>) => Promise<string> | string;
|
|
41
|
+
readonly kind = "llm";
|
|
42
|
+
readonly model: LanguageModel;
|
|
43
|
+
readonly modelSettings: LanguageModelRequestSettings;
|
|
62
44
|
|
|
63
|
-
|
|
64
|
-
modelSettings: LanguageModelRequestSettings;
|
|
65
|
-
// actions: ActionSet; /* TODO */
|
|
66
|
-
toolkits: BaseToolkit<TContext>[];
|
|
67
|
-
systools: BaseToolkit<TContext>[];
|
|
68
|
-
memory: AgentMemoryConfig;
|
|
69
|
-
|
|
70
|
-
guardrails: {
|
|
45
|
+
readonly guardrails: {
|
|
71
46
|
input: InputGuardrail[];
|
|
72
47
|
output: OutputGuardrail<AgentOutputType>[];
|
|
73
48
|
};
|
|
74
|
-
output: TOutput = "text" as TOutput;
|
|
75
|
-
resetToolChoice: boolean;
|
|
49
|
+
readonly output: TOutput = "text" as TOutput;
|
|
50
|
+
readonly resetToolChoice: boolean;
|
|
76
51
|
|
|
77
52
|
constructor(config: AgentConfig<TContext, TOutput>) {
|
|
78
|
-
super();
|
|
79
|
-
if (config.id.trim() === "") {
|
|
80
|
-
throw new MisconfiguredError("Agent must have an id.");
|
|
81
|
-
}
|
|
82
|
-
this.id = config.id;
|
|
83
|
-
this.name = config.name;
|
|
84
|
-
this.description = config.description;
|
|
85
|
-
this.instructions =
|
|
86
|
-
typeof config.instructions === "function"
|
|
87
|
-
? config.instructions
|
|
88
|
-
: () => config.instructions as string;
|
|
89
|
-
this.model = config.model; // (TODO): include optional default setting for convenience like env.DEFAULT_LLM = "gpt-5"
|
|
90
|
-
this.modelSettings = config.modelSettings ?? {};
|
|
91
|
-
|
|
92
|
-
this.toolkits = config.toolkits ?? [];
|
|
93
|
-
this.systools = [];
|
|
94
|
-
this.memory = config.memory ?? { enabled: false };
|
|
95
|
-
|
|
96
|
-
for (const toolkit of this.toolkits) {
|
|
97
|
-
toolkit.bind(this);
|
|
98
|
-
}
|
|
53
|
+
super(config);
|
|
99
54
|
|
|
55
|
+
this.model = config.model;
|
|
56
|
+
this.modelSettings = config.modelSettings ?? {};
|
|
100
57
|
this.guardrails = config.guardrails ?? { input: [], output: [] };
|
|
101
58
|
if (config.output) {
|
|
102
59
|
this.output = config.output;
|
|
103
60
|
}
|
|
104
61
|
this.resetToolChoice = config.resetToolChoice ?? true;
|
|
105
|
-
// this.toolUseBehavior = config.toolUseBehavior ?? "run_llm_again";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Bind this agent to a kernl instance. Called by kernl.register().
|
|
110
|
-
*/
|
|
111
|
-
bind(kernl: Kernl): void {
|
|
112
|
-
this.kernl = kernl;
|
|
113
|
-
|
|
114
|
-
// initialize system toolkits
|
|
115
|
-
if (this.memory.enabled) {
|
|
116
|
-
// safety: system tools only rely on ctx.agent, not ctx.context
|
|
117
|
-
const toolkit = memory as unknown as BaseToolkit<TContext>;
|
|
118
|
-
this.systools.push(toolkit);
|
|
119
|
-
toolkit.bind(this);
|
|
120
|
-
}
|
|
121
62
|
}
|
|
122
63
|
|
|
123
64
|
/**
|
|
@@ -241,60 +182,6 @@ export class Agent<
|
|
|
241
182
|
yield* this.kernl.scheduleStream(thread);
|
|
242
183
|
}
|
|
243
184
|
|
|
244
|
-
/**
|
|
245
|
-
* @internal
|
|
246
|
-
*
|
|
247
|
-
* Get a specific tool by ID from systools and toolkits.
|
|
248
|
-
*
|
|
249
|
-
* @param id The tool ID to look up
|
|
250
|
-
* @returns The tool if found, undefined otherwise
|
|
251
|
-
*/
|
|
252
|
-
tool(id: string): Tool<TContext> | undefined {
|
|
253
|
-
// Check systools first
|
|
254
|
-
for (const toolkit of this.systools) {
|
|
255
|
-
const tool = toolkit.get(id);
|
|
256
|
-
if (tool) return tool;
|
|
257
|
-
}
|
|
258
|
-
// Then user toolkits
|
|
259
|
-
for (const toolkit of this.toolkits) {
|
|
260
|
-
const tool = toolkit.get(id);
|
|
261
|
-
if (tool) return tool;
|
|
262
|
-
}
|
|
263
|
-
return undefined;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* @internal
|
|
268
|
-
*
|
|
269
|
-
* Get all tools available from systools and toolkits for the given context.
|
|
270
|
-
* Checks for duplicate tool IDs across toolkits and throws an error if found.
|
|
271
|
-
*
|
|
272
|
-
* (TODO): Consider returning toolkits alongside tools so we can serialize them
|
|
273
|
-
* together and give agents more options for dealing with tool groups.
|
|
274
|
-
*
|
|
275
|
-
* @param context The context to use for filtering tools
|
|
276
|
-
* @returns Array of all available tools
|
|
277
|
-
* @throws {MisconfiguredError} If duplicate tool IDs are found across toolkits
|
|
278
|
-
*/
|
|
279
|
-
async tools(context: Context<TContext>): Promise<Tool<TContext>[]> {
|
|
280
|
-
const all: Tool<TContext>[] = [];
|
|
281
|
-
|
|
282
|
-
for (const toolkit of [...this.systools, ...this.toolkits]) {
|
|
283
|
-
all.push(...(await toolkit.list(context)));
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const ids = all.map((t) => t.id);
|
|
287
|
-
const duplicates = ids.filter((id, i) => ids.indexOf(id) !== i);
|
|
288
|
-
|
|
289
|
-
if (duplicates.length > 0) {
|
|
290
|
-
throw new MisconfiguredError(
|
|
291
|
-
`Duplicate tool IDs found: ${[...new Set(duplicates)].join(", ")}`,
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return all;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
185
|
/**
|
|
299
186
|
* Thread management scoped to this agent.
|
|
300
187
|
*
|
|
@@ -331,110 +218,4 @@ export class Agent<
|
|
|
331
218
|
kthreads.update(tid, patch),
|
|
332
219
|
};
|
|
333
220
|
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Memory management scoped to this agent.
|
|
337
|
-
*
|
|
338
|
-
* Provides a simplified API for creating memories with:
|
|
339
|
-
* - Auto-generated IDs
|
|
340
|
-
* - Flattened scope fields (namespace, entityId) - agentId is implicit
|
|
341
|
-
* - Default kind of "semantic"
|
|
342
|
-
*
|
|
343
|
-
* @example
|
|
344
|
-
* ```ts
|
|
345
|
-
* await agent.memories.create({
|
|
346
|
-
* namespace: "user-123",
|
|
347
|
-
* collection: "preferences",
|
|
348
|
-
* content: { text: "User prefers TypeScript" },
|
|
349
|
-
* });
|
|
350
|
-
* ```
|
|
351
|
-
*/
|
|
352
|
-
get memories() {
|
|
353
|
-
if (!this.kernl) {
|
|
354
|
-
throw new MisconfiguredError(
|
|
355
|
-
`Agent ${this.id} not bound to kernl. Call kernl.register(agent) first.`,
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const agentId = this.id;
|
|
360
|
-
const kmem = this.kernl.memories;
|
|
361
|
-
|
|
362
|
-
return {
|
|
363
|
-
/**
|
|
364
|
-
* List memories scoped to this agent.
|
|
365
|
-
*/
|
|
366
|
-
list: (
|
|
367
|
-
params?: Omit<MemoryListOptions, "filter"> & {
|
|
368
|
-
collection?: string;
|
|
369
|
-
limit?: number;
|
|
370
|
-
// (TODO): we might want to add the filter back here
|
|
371
|
-
},
|
|
372
|
-
) =>
|
|
373
|
-
kmem.list({
|
|
374
|
-
filter: {
|
|
375
|
-
scope: { agentId },
|
|
376
|
-
collections: params?.collection ? [params.collection] : undefined,
|
|
377
|
-
},
|
|
378
|
-
limit: params?.limit,
|
|
379
|
-
}),
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Create a new memory scoped to this agent.
|
|
383
|
-
*/
|
|
384
|
-
create: (params: AgentMemoryCreate) =>
|
|
385
|
-
kmem.create({
|
|
386
|
-
id: params.id ?? `mem_${randomID()}`,
|
|
387
|
-
scope: {
|
|
388
|
-
namespace: params.namespace,
|
|
389
|
-
entityId: params.entityId,
|
|
390
|
-
agentId,
|
|
391
|
-
},
|
|
392
|
-
kind: "semantic",
|
|
393
|
-
collection: params.collection,
|
|
394
|
-
content: params.content,
|
|
395
|
-
wmem: params.wmem,
|
|
396
|
-
smem: params.smem,
|
|
397
|
-
timestamp: params.timestamp,
|
|
398
|
-
metadata: params.metadata,
|
|
399
|
-
}),
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Update an existing memory scoped to this agent.
|
|
403
|
-
*/
|
|
404
|
-
update: (params: AgentMemoryUpdate) =>
|
|
405
|
-
kmem.update({
|
|
406
|
-
id: params.id,
|
|
407
|
-
content: params.content,
|
|
408
|
-
collection: params.collection,
|
|
409
|
-
wmem: params.wmem,
|
|
410
|
-
smem: params.smem,
|
|
411
|
-
metadata: params.metadata,
|
|
412
|
-
}),
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Search memories scoped to this agent.
|
|
416
|
-
*/
|
|
417
|
-
search: (
|
|
418
|
-
params: Omit<MemorySearchQuery, "filter"> & {
|
|
419
|
-
// (TODO): is this correct?
|
|
420
|
-
filter?: Omit<NonNullable<MemorySearchQuery["filter"]>, "scope"> & {
|
|
421
|
-
scope?: Omit<
|
|
422
|
-
NonNullable<NonNullable<MemorySearchQuery["filter"]>["scope"]>,
|
|
423
|
-
"agentId"
|
|
424
|
-
>;
|
|
425
|
-
};
|
|
426
|
-
},
|
|
427
|
-
) =>
|
|
428
|
-
kmem.search({
|
|
429
|
-
...params,
|
|
430
|
-
filter: {
|
|
431
|
-
...params.filter,
|
|
432
|
-
scope: {
|
|
433
|
-
...params.filter?.scope,
|
|
434
|
-
agentId,
|
|
435
|
-
},
|
|
436
|
-
},
|
|
437
|
-
}),
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
221
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Agent } from "@/agent";
|
|
2
|
+
import type { BaseAgent } from "@/agent/base";
|
|
2
3
|
import type { AgentOutputType } from "@/agent/types";
|
|
3
4
|
import type { UnknownContext } from "@/context";
|
|
4
5
|
import type { TextOutput } from "@/thread/types";
|
|
@@ -9,13 +10,19 @@ import type { TextOutput } from "@/thread/types";
|
|
|
9
10
|
* Thin facade over the in-process agent registry, returning live Agent instances.
|
|
10
11
|
*
|
|
11
12
|
* Note: agents are code, not persisted data; this is process-local.
|
|
13
|
+
*
|
|
14
|
+
* Currently only exposes LLM agents (kind: "llm"). RealtimeAgents are stored
|
|
15
|
+
* in the internal registry but not returned by these methods. If public access
|
|
16
|
+
* to realtime agents is needed, add a separate `kernl.realtimeAgents` resource.
|
|
12
17
|
*/
|
|
13
18
|
export class RAgents {
|
|
14
|
-
constructor(private readonly registry: Map<string,
|
|
19
|
+
constructor(private readonly registry: Map<string, BaseAgent>) {}
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* Get a live Agent instance by id.
|
|
18
23
|
*
|
|
24
|
+
* Only returns LLM agents. Returns undefined for realtime agents.
|
|
25
|
+
*
|
|
19
26
|
* Callers are expected to know the concrete TContext/TOutput types
|
|
20
27
|
* for their own agents and can specify them via generics.
|
|
21
28
|
*/
|
|
@@ -24,27 +31,26 @@ export class RAgents {
|
|
|
24
31
|
TOutput extends AgentOutputType = TextOutput,
|
|
25
32
|
>(id: string): Agent<TContext, TOutput> | undefined {
|
|
26
33
|
const agent = this.registry.get(id);
|
|
27
|
-
|
|
34
|
+
if (agent?.kind === "llm") {
|
|
35
|
+
return agent as Agent<TContext, TOutput>;
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
28
38
|
}
|
|
29
39
|
|
|
30
40
|
/**
|
|
31
|
-
* Check if an agent with the given id is registered.
|
|
41
|
+
* Check if an LLM agent with the given id is registered.
|
|
32
42
|
*/
|
|
33
43
|
has(id: string): boolean {
|
|
34
|
-
return this.registry.
|
|
44
|
+
return this.registry.get(id)?.kind === "llm";
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
/**
|
|
38
|
-
* List all registered agents as live instances.
|
|
39
|
-
*
|
|
40
|
-
* Since this is a heterogeneous collection, we expose the widest safe
|
|
41
|
-
* type parameters here.
|
|
48
|
+
* List all registered LLM agents as live instances.
|
|
42
49
|
*/
|
|
43
50
|
list(): Agent<UnknownContext, AgentOutputType>[] {
|
|
44
|
-
return Array.from(this.registry.values())
|
|
45
|
-
UnknownContext,
|
|
46
|
-
|
|
47
|
-
>[];
|
|
51
|
+
return Array.from(this.registry.values()).filter(
|
|
52
|
+
(a): a is Agent<UnknownContext, AgentOutputType> => a.kind === "llm",
|
|
53
|
+
);
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
/**
|
package/src/kernl/kernl.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LanguageModel } from "@kernl-sdk/protocol";
|
|
2
2
|
import { resolveEmbeddingModel } from "@kernl-sdk/retrieval";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { BaseAgent } from "@/agent/base";
|
|
5
5
|
import { UnknownContext } from "@/context";
|
|
6
6
|
import { KernlHooks } from "@/lifecycle";
|
|
7
7
|
import type { Thread } from "@/thread";
|
|
@@ -29,7 +29,7 @@ import type { KernlOptions, MemoryOptions, StorageOptions } from "./types";
|
|
|
29
29
|
* tracing.
|
|
30
30
|
*/
|
|
31
31
|
export class Kernl extends KernlHooks<UnknownContext, AgentOutputType> {
|
|
32
|
-
private readonly _agents: Map<string,
|
|
32
|
+
private readonly _agents: Map<string, BaseAgent> = new Map();
|
|
33
33
|
private readonly _models: Map<string, LanguageModel> = new Map();
|
|
34
34
|
|
|
35
35
|
readonly storage: KernlStorage;
|
|
@@ -64,7 +64,7 @@ export class Kernl extends KernlHooks<UnknownContext, AgentOutputType> {
|
|
|
64
64
|
/**
|
|
65
65
|
* Registers a new agent with the kernl instance.
|
|
66
66
|
*/
|
|
67
|
-
register(agent:
|
|
67
|
+
register(agent: BaseAgent): void {
|
|
68
68
|
this._agents.set(agent.id, agent);
|
|
69
69
|
agent.bind(this);
|
|
70
70
|
|
|
@@ -87,12 +87,12 @@ export class Kernl extends KernlHooks<UnknownContext, AgentOutputType> {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
// auto-populate model registry for storage hydration (llm agents only - for now)
|
|
91
|
+
if (agent.kind === "llm") {
|
|
92
|
+
const key = `${agent.model.provider}/${agent.model.modelId}`;
|
|
93
|
+
if (!this._models.has(key)) {
|
|
94
|
+
this._models.set(key, agent.model as LanguageModel);
|
|
95
|
+
}
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
package/src/kernl/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LanguageModel, EmbeddingModel } from "@kernl-sdk/protocol";
|
|
2
2
|
import { SearchIndex } from "@kernl-sdk/retrieval";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { BaseAgent } from "@/agent/base";
|
|
5
5
|
import { KernlStorage } from "@/storage";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -87,10 +87,10 @@ export interface KernlOptions {
|
|
|
87
87
|
/**
|
|
88
88
|
* Agent registry interface.
|
|
89
89
|
*
|
|
90
|
-
* Satisfied by Map<string,
|
|
90
|
+
* Satisfied by Map<string, BaseAgent>.
|
|
91
91
|
*/
|
|
92
92
|
export interface AgentRegistry {
|
|
93
|
-
get(id: string):
|
|
93
|
+
get(id: string): BaseAgent<any> | undefined;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -63,7 +63,7 @@ describe("mcpToFunctionTool", () => {
|
|
|
63
63
|
expect(functionTool.parameters).toBeDefined();
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
it("should handle tools without inputSchema (
|
|
66
|
+
it("should handle tools without inputSchema (empty object parameters)", () => {
|
|
67
67
|
const server = createMockServer();
|
|
68
68
|
// In practice, MCP SDK tools require inputSchema, but our function handles
|
|
69
69
|
// the case where it might not be present. We use 'as any' to test this edge case.
|
|
@@ -75,7 +75,9 @@ describe("mcpToFunctionTool", () => {
|
|
|
75
75
|
const functionTool = mcpToFunctionTool(server, mcpTool);
|
|
76
76
|
|
|
77
77
|
expect(functionTool.id).toBe("no_params");
|
|
78
|
-
|
|
78
|
+
// When no inputSchema, we use an empty z.object({}) to match AI SDK behavior
|
|
79
|
+
expect(functionTool.parameters).toBeDefined();
|
|
80
|
+
expect(functionTool.parameters?.def.type).toBe("object");
|
|
79
81
|
});
|
|
80
82
|
|
|
81
83
|
it("should invoke server.callTool with correct params", async () => {
|
package/src/mcp/utils.ts
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { RealtimeModel } from "@kernl-sdk/protocol";
|
|
2
|
+
|
|
3
|
+
import type { UnknownContext } from "@/context";
|
|
4
|
+
import { BaseAgent } from "@/agent/base";
|
|
5
|
+
|
|
6
|
+
import type { RealtimeAgentConfig, RealtimeAgentVoiceConfig } from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A realtime agent definition.
|
|
10
|
+
*
|
|
11
|
+
* Stateless configuration that describes what a realtime voice agent does.
|
|
12
|
+
* Create sessions with `new RealtimeSession(agent, options)`.
|
|
13
|
+
*/
|
|
14
|
+
export class RealtimeAgent<TContext = UnknownContext> extends BaseAgent<TContext> {
|
|
15
|
+
readonly kind = "realtime";
|
|
16
|
+
readonly model: RealtimeModel;
|
|
17
|
+
readonly voice?: RealtimeAgentVoiceConfig;
|
|
18
|
+
|
|
19
|
+
constructor(config: RealtimeAgentConfig<TContext>) {
|
|
20
|
+
super(config);
|
|
21
|
+
this.model = config.model;
|
|
22
|
+
this.voice = config.voice;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base interface for audio I/O channels.
|
|
5
|
+
*
|
|
6
|
+
* Channels bridge between audio sources (browser mic, Twilio, Discord)
|
|
7
|
+
* and the realtime session. They handle audio capture/playback and emit
|
|
8
|
+
* events that the session listens to.
|
|
9
|
+
*
|
|
10
|
+
* Events emitted:
|
|
11
|
+
* - 'audio' (audio: string) - Raw audio chunk (base64)
|
|
12
|
+
* - 'commit' () - User finished speaking (VAD or manual)
|
|
13
|
+
* - 'interrupt' () - User started speaking mid-response
|
|
14
|
+
*/
|
|
15
|
+
export interface RealtimeChannel extends EventEmitter {
|
|
16
|
+
/**
|
|
17
|
+
* Send audio to be played/transmitted by the channel.
|
|
18
|
+
* Called by session when audio is received from the model.
|
|
19
|
+
*/
|
|
20
|
+
sendAudio(audio: string): void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Interrupt current audio playback.
|
|
24
|
+
* Called by session when response is cancelled.
|
|
25
|
+
*/
|
|
26
|
+
interrupt(): void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Clean up resources and close the channel.
|
|
30
|
+
*/
|
|
31
|
+
close(): void;
|
|
32
|
+
}
|