kernl 0.12.0 → 0.12.2
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 +21 -0
- package/dist/api/resources/agents/agents.d.ts +2 -2
- package/dist/api/resources/agents/agents.d.ts.map +1 -1
- package/dist/api/resources/agents/agents.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/kernl/index.d.ts +2 -1
- package/dist/kernl/index.d.ts.map +1 -1
- package/dist/kernl/index.js +1 -0
- package/dist/kernl/kernl.d.ts +4 -4
- package/dist/kernl/kernl.d.ts.map +1 -1
- package/dist/kernl/kernl.js +17 -13
- package/dist/kernl/registry.d.ts +46 -0
- package/dist/kernl/registry.d.ts.map +1 -0
- package/dist/kernl/registry.js +57 -0
- package/dist/kernl/types.d.ts +2 -2
- package/dist/kernl/types.d.ts.map +1 -1
- package/dist/storage/base.d.ts +3 -3
- package/dist/storage/base.d.ts.map +1 -1
- package/dist/storage/in-memory.d.ts +5 -5
- package/dist/storage/in-memory.d.ts.map +1 -1
- package/dist/thread/__tests__/mock.d.ts +2 -3
- package/dist/thread/__tests__/mock.d.ts.map +1 -1
- package/dist/thread/thread.d.ts +4 -0
- package/dist/thread/thread.d.ts.map +1 -1
- package/dist/thread/thread.js +43 -75
- package/dist/thread/types.d.ts +18 -3
- package/dist/thread/types.d.ts.map +1 -1
- package/dist/thread/utils.d.ts +7 -7
- package/dist/thread/utils.d.ts.map +1 -1
- package/dist/thread/utils.js +1 -1
- package/package.json +4 -6
- package/src/api/__tests__/threads.test.ts +3 -3
- package/src/api/resources/agents/agents.ts +3 -3
- package/src/index.ts +4 -2
- package/src/kernl/index.ts +3 -2
- package/src/kernl/kernl.ts +18 -15
- package/src/kernl/registry.ts +69 -0
- package/src/kernl/types.ts +2 -2
- package/src/storage/base.ts +2 -2
- package/src/storage/in-memory.ts +4 -4
- package/src/thread/__tests__/mock.ts +2 -2
- package/src/thread/thread.ts +47 -78
- package/src/thread/types.ts +49 -3
- package/src/thread/utils.ts +14 -7
- package/dist/thread/__tests__/integration.test.d.ts +0 -2
- package/dist/thread/__tests__/integration.test.d.ts.map +0 -1
- package/dist/thread/__tests__/integration.test.js +0 -320
- package/src/thread/__tests__/integration.test.ts +0 -434
package/src/storage/in-memory.ts
CHANGED
|
@@ -25,7 +25,7 @@ import type {
|
|
|
25
25
|
SortOrder,
|
|
26
26
|
} from "@/storage";
|
|
27
27
|
import type { ThreadEvent, ThreadState } from "@/thread/types";
|
|
28
|
-
import type {
|
|
28
|
+
import type { IAgentRegistry, IModelRegistry } from "@/kernl/types";
|
|
29
29
|
import type {
|
|
30
30
|
MemoryStore,
|
|
31
31
|
MemoryRecord,
|
|
@@ -47,7 +47,7 @@ export class InMemoryStorage implements KernlStorage {
|
|
|
47
47
|
this.memories = new InMemoryMemoryStore();
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
bind(registries: { agents:
|
|
50
|
+
bind(registries: { agents: IAgentRegistry; models: IModelRegistry }): void {
|
|
51
51
|
this.threads.bind(registries);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -93,10 +93,10 @@ interface ThreadData {
|
|
|
93
93
|
export class InMemoryThreadStore implements ThreadStore {
|
|
94
94
|
private threads = new Map<string, ThreadData>();
|
|
95
95
|
private events = new Map<string, ThreadEvent[]>(); // tid -> events
|
|
96
|
-
private registries: { agents:
|
|
96
|
+
private registries: { agents: IAgentRegistry; models: IModelRegistry } | null =
|
|
97
97
|
null;
|
|
98
98
|
|
|
99
|
-
bind(registries: { agents:
|
|
99
|
+
bind(registries: { agents: IAgentRegistry; models: IModelRegistry }): void {
|
|
100
100
|
this.registries = registries;
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
LanguageModelResponse,
|
|
5
5
|
LanguageModelResponseItem,
|
|
6
6
|
LanguageModelItem,
|
|
7
|
+
LanguageModelStreamEvent,
|
|
7
8
|
} from "@kernl-sdk/protocol";
|
|
8
|
-
import type { ThreadStreamEvent } from "@/thread/types";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A mock language model that echoes the user input back as an assistant message.
|
|
@@ -54,7 +54,7 @@ export class MockLanguageModel implements LanguageModel {
|
|
|
54
54
|
|
|
55
55
|
async *stream(
|
|
56
56
|
request: LanguageModelRequest,
|
|
57
|
-
): AsyncIterable<
|
|
57
|
+
): AsyncIterable<LanguageModelStreamEvent> {
|
|
58
58
|
// TODO: Implement streaming (not needed for hello world)
|
|
59
59
|
throw new Error("MockLanguageModel.stream() not implemented yet");
|
|
60
60
|
}
|
package/src/thread/thread.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
LanguageModel,
|
|
20
20
|
LanguageModelItem,
|
|
21
21
|
LanguageModelRequest,
|
|
22
|
+
LanguageModelStreamEvent,
|
|
22
23
|
type LanguageModelUsage,
|
|
23
24
|
type LanguageModelFinishReason,
|
|
24
25
|
} from "@kernl-sdk/protocol";
|
|
@@ -33,6 +34,7 @@ import type {
|
|
|
33
34
|
ThreadStreamEvent,
|
|
34
35
|
ThreadExecuteResult,
|
|
35
36
|
PerformActionsResult,
|
|
37
|
+
PublicThreadEvent,
|
|
36
38
|
} from "./types";
|
|
37
39
|
import type { AgentOutputType } from "@/agent/types";
|
|
38
40
|
import type { LanguageModelResponseType } from "@kernl-sdk/protocol";
|
|
@@ -176,35 +178,15 @@ export class Thread<
|
|
|
176
178
|
|
|
177
179
|
await this.checkpoint(); /* c1: persist RUNNING state + initial input */
|
|
178
180
|
|
|
179
|
-
this.
|
|
180
|
-
kind: "thread.start",
|
|
181
|
-
threadId: this.tid,
|
|
182
|
-
agentId: this.agent.id,
|
|
183
|
-
namespace: this.namespace,
|
|
184
|
-
context: this.context,
|
|
185
|
-
});
|
|
181
|
+
this.emit("thread.start");
|
|
186
182
|
|
|
187
183
|
yield { kind: "stream.start" }; // always yield start immediately
|
|
188
184
|
|
|
189
185
|
try {
|
|
190
186
|
yield* this._execute();
|
|
191
|
-
|
|
192
|
-
this.agent.emit("thread.stop", {
|
|
193
|
-
kind: "thread.stop",
|
|
194
|
-
threadId: this.tid,
|
|
195
|
-
agentId: this.agent.id,
|
|
196
|
-
namespace: this.namespace,
|
|
197
|
-
context: this.context,
|
|
198
|
-
state: STOPPED,
|
|
199
|
-
result: this.tickres,
|
|
200
|
-
});
|
|
187
|
+
this.emit("thread.stop", { state: STOPPED, result: this.tickres });
|
|
201
188
|
} catch (err) {
|
|
202
|
-
this.
|
|
203
|
-
kind: "thread.stop",
|
|
204
|
-
threadId: this.tid,
|
|
205
|
-
agentId: this.agent.id,
|
|
206
|
-
namespace: this.namespace,
|
|
207
|
-
context: this.context,
|
|
189
|
+
this.emit("thread.stop", {
|
|
208
190
|
state: STOPPED,
|
|
209
191
|
error: err instanceof Error ? err.message : String(err),
|
|
210
192
|
});
|
|
@@ -236,12 +218,14 @@ export class Thread<
|
|
|
236
218
|
err = e.error;
|
|
237
219
|
logger.error(e.error); // (TODO): onError callback in options
|
|
238
220
|
}
|
|
239
|
-
//
|
|
221
|
+
// complete items get persisted with seq, deltas are ephemeral
|
|
240
222
|
if (notDelta(e)) {
|
|
241
|
-
|
|
242
|
-
|
|
223
|
+
const [seqd] = this.append(e);
|
|
224
|
+
events.push(seqd);
|
|
225
|
+
yield seqd;
|
|
226
|
+
} else {
|
|
227
|
+
yield e;
|
|
243
228
|
}
|
|
244
|
-
yield e;
|
|
245
229
|
}
|
|
246
230
|
|
|
247
231
|
// if an error event occurred → throw it
|
|
@@ -267,10 +251,10 @@ export class Thread<
|
|
|
267
251
|
const { actions, pendingApprovals } =
|
|
268
252
|
await this.performActions(intentions);
|
|
269
253
|
|
|
270
|
-
// append + yield action events
|
|
254
|
+
// append + yield action events (sequenced)
|
|
271
255
|
for (const a of actions) {
|
|
272
|
-
this.append(a);
|
|
273
|
-
yield
|
|
256
|
+
const [seqd] = this.append(a);
|
|
257
|
+
yield seqd;
|
|
274
258
|
}
|
|
275
259
|
|
|
276
260
|
await this.checkpoint(); /* c3: tick complete */
|
|
@@ -293,23 +277,16 @@ export class Thread<
|
|
|
293
277
|
* NOTE: Streaming structured outputs deferred until concrete use cases emerge.
|
|
294
278
|
* For now, we stream text-delta and tool events, final validation happens in _execute().
|
|
295
279
|
*/
|
|
296
|
-
private async *tick(): AsyncGenerator<
|
|
280
|
+
private async *tick(): AsyncGenerator<LanguageModelStreamEvent> {
|
|
297
281
|
this._tick++;
|
|
298
282
|
|
|
299
283
|
// (TODO): check limits (if this._tick > this.limits.maxTicks)
|
|
300
284
|
// (TODO): run input guardrails on first tick (if this._tick === 1)
|
|
285
|
+
// (TODO): compaction if necessary
|
|
301
286
|
|
|
302
287
|
const req = await this.prepareModelRequest(this.history);
|
|
303
288
|
|
|
304
|
-
this.
|
|
305
|
-
kind: "model.call.start",
|
|
306
|
-
provider: this.model.provider,
|
|
307
|
-
modelId: this.model.modelId,
|
|
308
|
-
settings: req.settings ?? {},
|
|
309
|
-
threadId: this.tid,
|
|
310
|
-
agentId: this.agent.id,
|
|
311
|
-
context: this.context,
|
|
312
|
-
});
|
|
289
|
+
this.emit("model.call.start", { settings: req.settings ?? {} });
|
|
313
290
|
|
|
314
291
|
let usage: LanguageModelUsage | undefined;
|
|
315
292
|
let finishReason: LanguageModelFinishReason = "unknown";
|
|
@@ -333,27 +310,9 @@ export class Thread<
|
|
|
333
310
|
}
|
|
334
311
|
}
|
|
335
312
|
|
|
336
|
-
this.
|
|
337
|
-
kind: "model.call.end",
|
|
338
|
-
provider: this.model.provider,
|
|
339
|
-
modelId: this.model.modelId,
|
|
340
|
-
finishReason,
|
|
341
|
-
usage,
|
|
342
|
-
threadId: this.tid,
|
|
343
|
-
agentId: this.agent.id,
|
|
344
|
-
context: this.context,
|
|
345
|
-
});
|
|
313
|
+
this.emit("model.call.end", { finishReason, usage });
|
|
346
314
|
} catch (error) {
|
|
347
|
-
this.
|
|
348
|
-
kind: "model.call.end",
|
|
349
|
-
provider: this.model.provider,
|
|
350
|
-
modelId: this.model.modelId,
|
|
351
|
-
finishReason: "error",
|
|
352
|
-
threadId: this.tid,
|
|
353
|
-
agentId: this.agent.id,
|
|
354
|
-
context: this.context,
|
|
355
|
-
});
|
|
356
|
-
|
|
315
|
+
this.emit("model.call.end", { finishReason: "error" });
|
|
357
316
|
yield {
|
|
358
317
|
kind: "error",
|
|
359
318
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
@@ -439,9 +398,31 @@ export class Thread<
|
|
|
439
398
|
this.abort?.abort();
|
|
440
399
|
}
|
|
441
400
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Emit an agent event with common fields auto-filled.
|
|
403
|
+
*/
|
|
404
|
+
private emit(kind: string, payload?: Record<string, unknown>): void {
|
|
405
|
+
const base = {
|
|
406
|
+
kind,
|
|
407
|
+
threadId: this.tid,
|
|
408
|
+
agentId: this.agent.id,
|
|
409
|
+
context: this.context,
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
let auto = {};
|
|
413
|
+
switch (kind) {
|
|
414
|
+
case "thread.start":
|
|
415
|
+
case "thread.stop":
|
|
416
|
+
auto = { namespace: this.namespace };
|
|
417
|
+
break;
|
|
418
|
+
case "model.call.start":
|
|
419
|
+
case "model.call.end":
|
|
420
|
+
auto = { provider: this.model.provider, modelId: this.model.modelId };
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
this.agent.emit(kind as any, { ...base, ...auto, ...payload } as any);
|
|
425
|
+
}
|
|
445
426
|
|
|
446
427
|
/**
|
|
447
428
|
* Perform the actions returned by the model
|
|
@@ -497,11 +478,7 @@ export class Thread<
|
|
|
497
478
|
calls.map(async (call: ToolCall) => {
|
|
498
479
|
const parsedArgs = JSON.parse(call.arguments || "{}");
|
|
499
480
|
|
|
500
|
-
this.
|
|
501
|
-
kind: "tool.call.start",
|
|
502
|
-
threadId: this.tid,
|
|
503
|
-
agentId: this.agent.id,
|
|
504
|
-
context: this.context,
|
|
481
|
+
this.emit("tool.call.start", {
|
|
505
482
|
toolId: call.toolId,
|
|
506
483
|
callId: call.callId,
|
|
507
484
|
args: parsedArgs,
|
|
@@ -526,11 +503,7 @@ export class Thread<
|
|
|
526
503
|
ctx.approve(call.callId); // mark this call as approved
|
|
527
504
|
const res = await tool.invoke(ctx, call.arguments, call.callId);
|
|
528
505
|
|
|
529
|
-
this.
|
|
530
|
-
kind: "tool.call.end",
|
|
531
|
-
threadId: this.tid,
|
|
532
|
-
agentId: this.agent.id,
|
|
533
|
-
context: this.context,
|
|
506
|
+
this.emit("tool.call.end", {
|
|
534
507
|
toolId: call.toolId,
|
|
535
508
|
callId: call.callId,
|
|
536
509
|
state: res.state,
|
|
@@ -550,11 +523,7 @@ export class Thread<
|
|
|
550
523
|
error: res.error,
|
|
551
524
|
};
|
|
552
525
|
} catch (error) {
|
|
553
|
-
this.
|
|
554
|
-
kind: "tool.call.end",
|
|
555
|
-
threadId: this.tid,
|
|
556
|
-
agentId: this.agent.id,
|
|
557
|
-
context: this.context,
|
|
526
|
+
this.emit("tool.call.end", {
|
|
558
527
|
toolId: call.toolId,
|
|
559
528
|
callId: call.callId,
|
|
560
529
|
state: FAILED,
|
package/src/thread/types.ts
CHANGED
|
@@ -2,13 +2,27 @@ import {
|
|
|
2
2
|
ToolCall,
|
|
3
3
|
LanguageModel,
|
|
4
4
|
LanguageModelItem,
|
|
5
|
-
LanguageModelStreamEvent,
|
|
6
5
|
RUNNING,
|
|
7
6
|
STOPPED,
|
|
8
7
|
INTERRUPTIBLE,
|
|
9
8
|
UNINTERRUPTIBLE,
|
|
10
9
|
ZOMBIE,
|
|
11
10
|
DEAD,
|
|
11
|
+
// Stream event types
|
|
12
|
+
TextStartEvent,
|
|
13
|
+
TextEndEvent,
|
|
14
|
+
TextDeltaEvent,
|
|
15
|
+
ReasoningStartEvent,
|
|
16
|
+
ReasoningEndEvent,
|
|
17
|
+
ReasoningDeltaEvent,
|
|
18
|
+
ToolInputStartEvent,
|
|
19
|
+
ToolInputEndEvent,
|
|
20
|
+
ToolInputDeltaEvent,
|
|
21
|
+
StartEvent,
|
|
22
|
+
FinishEvent,
|
|
23
|
+
AbortEvent,
|
|
24
|
+
ErrorEvent,
|
|
25
|
+
RawEvent,
|
|
12
26
|
} from "@kernl-sdk/protocol";
|
|
13
27
|
|
|
14
28
|
import { Task } from "@/task";
|
|
@@ -128,9 +142,41 @@ export type ThreadEvent =
|
|
|
128
142
|
| ThreadSystemEvent;
|
|
129
143
|
|
|
130
144
|
/**
|
|
131
|
-
*
|
|
145
|
+
* Incremental content chunks (ephemeral, not persisted).
|
|
132
146
|
*/
|
|
133
|
-
export type
|
|
147
|
+
export type StreamDeltaEvent =
|
|
148
|
+
| TextDeltaEvent
|
|
149
|
+
| ReasoningDeltaEvent
|
|
150
|
+
| ToolInputDeltaEvent;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Boundary markers + control flow (ephemeral, not persisted).
|
|
154
|
+
*/
|
|
155
|
+
export type StreamControlEvent =
|
|
156
|
+
| TextStartEvent
|
|
157
|
+
| TextEndEvent
|
|
158
|
+
| ReasoningStartEvent
|
|
159
|
+
| ReasoningEndEvent
|
|
160
|
+
| ToolInputStartEvent
|
|
161
|
+
| ToolInputEndEvent
|
|
162
|
+
| StartEvent
|
|
163
|
+
| FinishEvent
|
|
164
|
+
| AbortEvent
|
|
165
|
+
| ErrorEvent
|
|
166
|
+
| RawEvent;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* All ephemeral stream types (not persisted to history).
|
|
170
|
+
*/
|
|
171
|
+
export type StreamEvent = StreamDeltaEvent | StreamControlEvent;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Thread stream events = sequenced ThreadEvents + ephemeral StreamEvents.
|
|
175
|
+
*
|
|
176
|
+
* Complete items (Message, ToolCall, etc.) are yielded as ThreadEvents with seq.
|
|
177
|
+
* Deltas and control events are yielded as StreamEvents without seq.
|
|
178
|
+
*/
|
|
179
|
+
export type ThreadStreamEvent = ThreadEvent | StreamEvent;
|
|
134
180
|
|
|
135
181
|
/**
|
|
136
182
|
* Result of thread execution
|
package/src/thread/utils.ts
CHANGED
|
@@ -4,7 +4,11 @@ import type { ResolvedAgentResponse } from "@/guardrail";
|
|
|
4
4
|
|
|
5
5
|
/* lib */
|
|
6
6
|
import { json, randomID } from "@kernl-sdk/shared/lib";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ToolCall,
|
|
9
|
+
LanguageModelItem,
|
|
10
|
+
LanguageModelStreamEvent,
|
|
11
|
+
} from "@kernl-sdk/protocol";
|
|
8
12
|
import { ModelBehaviorError } from "@/lib/error";
|
|
9
13
|
|
|
10
14
|
/* types */
|
|
@@ -12,7 +16,6 @@ import type { AgentOutputType } from "@/agent/types";
|
|
|
12
16
|
import type {
|
|
13
17
|
ThreadEvent,
|
|
14
18
|
ThreadEventBase,
|
|
15
|
-
ThreadStreamEvent,
|
|
16
19
|
ActionSet,
|
|
17
20
|
PublicThreadEvent,
|
|
18
21
|
} from "./types";
|
|
@@ -21,7 +24,7 @@ import type {
|
|
|
21
24
|
* Create a ThreadEvent from a LanguageModelItem with thread metadata.
|
|
22
25
|
*
|
|
23
26
|
* @example
|
|
24
|
-
* ```
|
|
27
|
+
* ```ts
|
|
25
28
|
* tevent({
|
|
26
29
|
* kind: "message",
|
|
27
30
|
* seq: 0,
|
|
@@ -57,7 +60,9 @@ export function tevent(event: {
|
|
|
57
60
|
/**
|
|
58
61
|
* Check if an event is a tool call
|
|
59
62
|
*/
|
|
60
|
-
export function isActionIntention(
|
|
63
|
+
export function isActionIntention(
|
|
64
|
+
event: ThreadEvent,
|
|
65
|
+
): event is ToolCall & ThreadEventBase {
|
|
61
66
|
return event.kind === "tool.call";
|
|
62
67
|
}
|
|
63
68
|
|
|
@@ -65,7 +70,7 @@ export function isActionIntention(event: LanguageModelItem): event is ToolCall {
|
|
|
65
70
|
* Extract action intentions from a list of events.
|
|
66
71
|
* Returns ActionSet if there are any tool calls, null otherwise.
|
|
67
72
|
*/
|
|
68
|
-
export function getIntentions(events:
|
|
73
|
+
export function getIntentions(events: ThreadEvent[]): ActionSet | null {
|
|
69
74
|
const toolCalls = events.filter(isActionIntention);
|
|
70
75
|
return toolCalls.length > 0 ? { toolCalls } : null;
|
|
71
76
|
}
|
|
@@ -74,7 +79,9 @@ export function getIntentions(events: LanguageModelItem[]): ActionSet | null {
|
|
|
74
79
|
* Check if an event is NOT a delta/start/end event (i.e., a complete item).
|
|
75
80
|
* Returns true for complete items: Message, Reasoning, ToolCall, ToolResult
|
|
76
81
|
*/
|
|
77
|
-
export function notDelta(
|
|
82
|
+
export function notDelta(
|
|
83
|
+
event: LanguageModelStreamEvent,
|
|
84
|
+
): event is LanguageModelItem {
|
|
78
85
|
switch (event.kind) {
|
|
79
86
|
case "message":
|
|
80
87
|
case "reasoning":
|
|
@@ -112,7 +119,7 @@ export function isPublicEvent(event: ThreadEvent): event is PublicThreadEvent {
|
|
|
112
119
|
* Extract the final text response from a list of items.
|
|
113
120
|
* Returns null if no assistant message with text content is found.
|
|
114
121
|
*/
|
|
115
|
-
export function getFinalResponse(items:
|
|
122
|
+
export function getFinalResponse(items: ThreadEvent[]): string | null {
|
|
116
123
|
// scan backwards for the last assistant message
|
|
117
124
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
118
125
|
const item = items[i];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../../../src/thread/__tests__/integration.test.ts"],"names":[],"mappings":"AAIA,OAAO,sBAAsB,CAAC"}
|