kernl 0.11.4 → 0.12.1
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 +59 -0
- package/dist/agent/__tests__/run.test.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lifecycle/__tests__/hooks.test.js +6 -6
- package/dist/storage/__tests__/in-memory.test.js +1 -1
- package/dist/thread/__tests__/fixtures/mock-model.js +3 -3
- package/dist/thread/__tests__/mock.d.ts +2 -3
- package/dist/thread/__tests__/mock.d.ts.map +1 -1
- package/dist/thread/__tests__/thread-persistence.test.js +6 -6
- package/dist/thread/__tests__/thread.test.js +22 -22
- package/dist/thread/thread.d.ts +4 -0
- package/dist/thread/thread.d.ts.map +1 -1
- package/dist/thread/thread.js +47 -79
- 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 +6 -6
- package/package.json +5 -7
- package/src/agent/__tests__/run.test.ts +2 -2
- package/src/index.ts +1 -0
- package/src/lifecycle/__tests__/hooks.test.ts +6 -6
- package/src/storage/__tests__/in-memory.test.ts +1 -1
- package/src/thread/__tests__/fixtures/mock-model.ts +3 -3
- package/src/thread/__tests__/mock.ts +2 -2
- package/src/thread/__tests__/thread-persistence.test.ts +6 -6
- package/src/thread/__tests__/thread.test.ts +22 -22
- package/src/thread/thread.ts +51 -82
- package/src/thread/types.ts +49 -3
- package/src/thread/utils.ts +19 -12
- 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
|
@@ -194,7 +194,7 @@ describe("Thread", () => {
|
|
|
194
194
|
content: [],
|
|
195
195
|
},
|
|
196
196
|
{
|
|
197
|
-
kind: "tool
|
|
197
|
+
kind: "tool.call" as const,
|
|
198
198
|
toolId: "echo",
|
|
199
199
|
state: IN_PROGRESS,
|
|
200
200
|
callId: "call_1",
|
|
@@ -270,7 +270,7 @@ describe("Thread", () => {
|
|
|
270
270
|
}),
|
|
271
271
|
// Tool call (tick 1)
|
|
272
272
|
expect.objectContaining({
|
|
273
|
-
kind: "tool
|
|
273
|
+
kind: "tool.call",
|
|
274
274
|
toolId: "echo",
|
|
275
275
|
callId: "call_1",
|
|
276
276
|
state: IN_PROGRESS,
|
|
@@ -278,7 +278,7 @@ describe("Thread", () => {
|
|
|
278
278
|
}),
|
|
279
279
|
// Tool result (executed after tick 1)
|
|
280
280
|
expect.objectContaining({
|
|
281
|
-
kind: "tool
|
|
281
|
+
kind: "tool.result",
|
|
282
282
|
callId: "call_1",
|
|
283
283
|
toolId: "echo",
|
|
284
284
|
state: COMPLETED,
|
|
@@ -312,7 +312,7 @@ describe("Thread", () => {
|
|
|
312
312
|
content: [],
|
|
313
313
|
},
|
|
314
314
|
{
|
|
315
|
-
kind: "tool
|
|
315
|
+
kind: "tool.call" as const,
|
|
316
316
|
toolId: "simple",
|
|
317
317
|
state: IN_PROGRESS,
|
|
318
318
|
callId: "call_1",
|
|
@@ -339,7 +339,7 @@ describe("Thread", () => {
|
|
|
339
339
|
content: [],
|
|
340
340
|
},
|
|
341
341
|
{
|
|
342
|
-
kind: "tool
|
|
342
|
+
kind: "tool.call" as const,
|
|
343
343
|
toolId: "simple",
|
|
344
344
|
state: IN_PROGRESS,
|
|
345
345
|
callId: "call_2",
|
|
@@ -423,7 +423,7 @@ describe("Thread", () => {
|
|
|
423
423
|
content: [],
|
|
424
424
|
},
|
|
425
425
|
{
|
|
426
|
-
kind: "tool
|
|
426
|
+
kind: "tool.call" as const,
|
|
427
427
|
toolId: "nonexistent",
|
|
428
428
|
state: IN_PROGRESS,
|
|
429
429
|
callId: "call_1",
|
|
@@ -476,9 +476,9 @@ describe("Thread", () => {
|
|
|
476
476
|
const history = (thread as any).history as ThreadEvent[];
|
|
477
477
|
|
|
478
478
|
// Check that the tool result is an error
|
|
479
|
-
const toolResult = history.find((e) => e.kind === "tool
|
|
479
|
+
const toolResult = history.find((e) => e.kind === "tool.result");
|
|
480
480
|
expect(toolResult).toEqual(expect.objectContaining({
|
|
481
|
-
kind: "tool
|
|
481
|
+
kind: "tool.result",
|
|
482
482
|
callId: "call_1",
|
|
483
483
|
toolId: "nonexistent",
|
|
484
484
|
state: FAILED,
|
|
@@ -504,7 +504,7 @@ describe("Thread", () => {
|
|
|
504
504
|
content: [],
|
|
505
505
|
},
|
|
506
506
|
{
|
|
507
|
-
kind: "tool
|
|
507
|
+
kind: "tool.call" as const,
|
|
508
508
|
toolId: "failing",
|
|
509
509
|
state: IN_PROGRESS,
|
|
510
510
|
callId: "call_1",
|
|
@@ -567,9 +567,9 @@ describe("Thread", () => {
|
|
|
567
567
|
|
|
568
568
|
const history = (thread as any).history as ThreadEvent[];
|
|
569
569
|
|
|
570
|
-
const toolResult = history.find((e) => e.kind === "tool
|
|
570
|
+
const toolResult = history.find((e) => e.kind === "tool.result");
|
|
571
571
|
expect(toolResult).toMatchObject({
|
|
572
|
-
kind: "tool
|
|
572
|
+
kind: "tool.result",
|
|
573
573
|
callId: "call_1",
|
|
574
574
|
toolId: "failing",
|
|
575
575
|
state: FAILED,
|
|
@@ -595,7 +595,7 @@ describe("Thread", () => {
|
|
|
595
595
|
content: [],
|
|
596
596
|
},
|
|
597
597
|
{
|
|
598
|
-
kind: "tool
|
|
598
|
+
kind: "tool.call" as const,
|
|
599
599
|
toolId: "add",
|
|
600
600
|
state: IN_PROGRESS,
|
|
601
601
|
callId: "call_1",
|
|
@@ -655,9 +655,9 @@ describe("Thread", () => {
|
|
|
655
655
|
// @ts-expect-error
|
|
656
656
|
const history = thread.history as ThreadEvent[];
|
|
657
657
|
|
|
658
|
-
const toolResult = history.find((e) => e.kind === "tool
|
|
658
|
+
const toolResult = history.find((e) => e.kind === "tool.result");
|
|
659
659
|
expect(toolResult).toEqual(expect.objectContaining({
|
|
660
|
-
kind: "tool
|
|
660
|
+
kind: "tool.result",
|
|
661
661
|
callId: "call_1",
|
|
662
662
|
toolId: "add",
|
|
663
663
|
state: COMPLETED,
|
|
@@ -685,14 +685,14 @@ describe("Thread", () => {
|
|
|
685
685
|
content: [],
|
|
686
686
|
},
|
|
687
687
|
{
|
|
688
|
-
kind: "tool
|
|
688
|
+
kind: "tool.call" as const,
|
|
689
689
|
toolId: "tool1",
|
|
690
690
|
state: IN_PROGRESS,
|
|
691
691
|
callId: "call_1",
|
|
692
692
|
arguments: JSON.stringify({ value: "a" }),
|
|
693
693
|
},
|
|
694
694
|
{
|
|
695
|
-
kind: "tool
|
|
695
|
+
kind: "tool.call" as const,
|
|
696
696
|
toolId: "tool2",
|
|
697
697
|
state: IN_PROGRESS,
|
|
698
698
|
callId: "call_2",
|
|
@@ -761,12 +761,12 @@ describe("Thread", () => {
|
|
|
761
761
|
const history = (thread as any).history as ThreadEvent[];
|
|
762
762
|
|
|
763
763
|
// Should have both tool results in history
|
|
764
|
-
const toolResults = history.filter((e) => e.kind === "tool
|
|
764
|
+
const toolResults = history.filter((e) => e.kind === "tool.result");
|
|
765
765
|
expect(toolResults).toHaveLength(2);
|
|
766
766
|
expect(toolResults).toEqual(
|
|
767
767
|
expect.arrayContaining([
|
|
768
768
|
expect.objectContaining({
|
|
769
|
-
kind: "tool
|
|
769
|
+
kind: "tool.result",
|
|
770
770
|
callId: "call_1",
|
|
771
771
|
toolId: "tool1",
|
|
772
772
|
state: COMPLETED,
|
|
@@ -774,7 +774,7 @@ describe("Thread", () => {
|
|
|
774
774
|
error: null,
|
|
775
775
|
}),
|
|
776
776
|
expect.objectContaining({
|
|
777
|
-
kind: "tool
|
|
777
|
+
kind: "tool.result",
|
|
778
778
|
callId: "call_2",
|
|
779
779
|
toolId: "tool2",
|
|
780
780
|
state: COMPLETED,
|
|
@@ -803,7 +803,7 @@ describe("Thread", () => {
|
|
|
803
803
|
content: [],
|
|
804
804
|
},
|
|
805
805
|
{
|
|
806
|
-
kind: "tool
|
|
806
|
+
kind: "tool.call" as const,
|
|
807
807
|
toolId: "simple",
|
|
808
808
|
state: IN_PROGRESS,
|
|
809
809
|
callId: `call_${callCount}`,
|
|
@@ -880,7 +880,7 @@ describe("Thread", () => {
|
|
|
880
880
|
content: [],
|
|
881
881
|
},
|
|
882
882
|
{
|
|
883
|
-
kind: "tool
|
|
883
|
+
kind: "tool.call" as const,
|
|
884
884
|
toolId: "simple",
|
|
885
885
|
state: IN_PROGRESS,
|
|
886
886
|
callId: "call_1",
|
|
@@ -997,7 +997,7 @@ describe("Thread", () => {
|
|
|
997
997
|
content: [{ kind: "text" as const, text: "Let me use a tool" }],
|
|
998
998
|
},
|
|
999
999
|
{
|
|
1000
|
-
kind: "tool
|
|
1000
|
+
kind: "tool.call" as const,
|
|
1001
1001
|
toolId: "simple",
|
|
1002
1002
|
state: IN_PROGRESS,
|
|
1003
1003
|
callId: "call_1",
|
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
|
-
yield { kind: "stream
|
|
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
|
|
@@ -470,7 +451,7 @@ export class Thread<
|
|
|
470
451
|
// (TODO): clean this - approval tracking should be handled differently
|
|
471
452
|
for (const e of toolEvents) {
|
|
472
453
|
if (
|
|
473
|
-
e.kind === "tool
|
|
454
|
+
e.kind === "tool.result" &&
|
|
474
455
|
(e.state as any) === "requires_approval" // (TODO): fix this
|
|
475
456
|
) {
|
|
476
457
|
// find the original tool call for this pending approval
|
|
@@ -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,
|
|
@@ -542,7 +515,7 @@ export class Thread<
|
|
|
542
515
|
});
|
|
543
516
|
|
|
544
517
|
return {
|
|
545
|
-
kind: "tool
|
|
518
|
+
kind: "tool.result" as const,
|
|
546
519
|
callId: call.callId,
|
|
547
520
|
toolId: call.toolId,
|
|
548
521
|
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,
|
|
@@ -562,7 +531,7 @@ export class Thread<
|
|
|
562
531
|
});
|
|
563
532
|
|
|
564
533
|
return {
|
|
565
|
-
kind: "tool
|
|
534
|
+
kind: "tool.result" as const,
|
|
566
535
|
callId: call.callId,
|
|
567
536
|
toolId: call.toolId,
|
|
568
537
|
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,15 +60,17 @@ export function tevent(event: {
|
|
|
57
60
|
/**
|
|
58
61
|
* Check if an event is a tool call
|
|
59
62
|
*/
|
|
60
|
-
export function isActionIntention(
|
|
61
|
-
|
|
63
|
+
export function isActionIntention(
|
|
64
|
+
event: ThreadEvent,
|
|
65
|
+
): event is ToolCall & ThreadEventBase {
|
|
66
|
+
return event.kind === "tool.call";
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
/**
|
|
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,12 +79,14 @@ 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":
|
|
81
|
-
case "tool
|
|
82
|
-
case "tool
|
|
88
|
+
case "tool.call":
|
|
89
|
+
case "tool.result":
|
|
83
90
|
return true;
|
|
84
91
|
|
|
85
92
|
// all other events are streaming deltas/control events
|
|
@@ -96,8 +103,8 @@ export function isPublicEvent(event: ThreadEvent): event is PublicThreadEvent {
|
|
|
96
103
|
switch (event.kind) {
|
|
97
104
|
case "message":
|
|
98
105
|
case "reasoning":
|
|
99
|
-
case "tool
|
|
100
|
-
case "tool
|
|
106
|
+
case "tool.call":
|
|
107
|
+
case "tool.result":
|
|
101
108
|
return true;
|
|
102
109
|
|
|
103
110
|
case "system":
|
|
@@ -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"}
|