kernl 0.11.4 → 0.12.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > kernl@0.11.4 build /home/runner/work/kernl/kernl/packages/kernl
2
+ > kernl@0.12.0 build /home/runner/work/kernl/kernl/packages/kernl
3
3
  > tsc && tsc-alias --resolve-full-paths
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # @kernl/core
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8815744: **BREAKING:** Refactor event kind naming from kebab-case to dot notation
8
+
9
+ This aligns the language model stream/item kinds with the existing realtime events naming convention.
10
+
11
+ ### Kind value changes
12
+
13
+ | Old | New |
14
+ | ------------------ | ------------------ |
15
+ | `tool-call` | `tool.call` |
16
+ | `tool-result` | `tool.result` |
17
+ | `text-start` | `text.start` |
18
+ | `text-delta` | `text.delta` |
19
+ | `text-end` | `text.end` |
20
+ | `reasoning-start` | `reasoning.start` |
21
+ | `reasoning-delta` | `reasoning.delta` |
22
+ | `reasoning-end` | `reasoning.end` |
23
+ | `tool-input-start` | `tool.input.start` |
24
+ | `tool-input-delta` | `tool.input.delta` |
25
+ | `tool-input-end` | `tool.input.end` |
26
+ | `stream-start` | `stream.start` |
27
+
28
+ ### ToolInputStartEvent: `toolName` → `toolId`
29
+
30
+ The `ToolInputStartEvent` now uses `toolId` to match `ToolCall` and `ToolResult`.
31
+
32
+ ### Migration
33
+
34
+ If you have persisted thread events, run:
35
+
36
+ ```sql
37
+ UPDATE thread_events SET kind = 'tool.call' WHERE kind = 'tool-call';
38
+ UPDATE thread_events SET kind = 'tool.result' WHERE kind = 'tool-result';
39
+ ```
40
+
41
+ ### Patch Changes
42
+
43
+ - Updated dependencies [8815744]
44
+ - @kernl-sdk/protocol@0.5.0
45
+ - @kernl-sdk/retrieval@0.1.9
46
+
3
47
  ## 0.11.4
4
48
 
5
49
  ### Patch Changes
@@ -218,7 +218,7 @@ describe("Agent.stream() lifecycle", () => {
218
218
  for await (const event of agent.stream("Hello")) {
219
219
  events.push(event);
220
220
  }
221
- expect(events[0]).toEqual({ kind: "stream-start" });
221
+ expect(events[0]).toEqual({ kind: "stream.start" });
222
222
  });
223
223
  it("should have same persistence behavior as run()", async () => {
224
224
  const storage = new InMemoryStorage();
@@ -247,7 +247,7 @@ describe("Agent.stream() lifecycle", () => {
247
247
  expect(history.length).toBeGreaterThan(0);
248
248
  // Should have streamed events
249
249
  expect(events).toEqual(expect.arrayContaining([
250
- { kind: "stream-start" },
250
+ { kind: "stream.start" },
251
251
  expect.objectContaining({ kind: "message" }),
252
252
  ]));
253
253
  });
@@ -238,7 +238,7 @@ describe("Lifecycle Hooks", () => {
238
238
  content: [
239
239
  message({ role: "assistant", text: "" }),
240
240
  {
241
- kind: "tool-call",
241
+ kind: "tool.call",
242
242
  toolId: "echo",
243
243
  state: IN_PROGRESS,
244
244
  callId: "call_1",
@@ -302,7 +302,7 @@ describe("Lifecycle Hooks", () => {
302
302
  content: [
303
303
  message({ role: "assistant", text: "" }),
304
304
  {
305
- kind: "tool-call",
305
+ kind: "tool.call",
306
306
  toolId: "add",
307
307
  state: IN_PROGRESS,
308
308
  callId: "call_1",
@@ -359,7 +359,7 @@ describe("Lifecycle Hooks", () => {
359
359
  content: [
360
360
  message({ role: "assistant", text: "" }),
361
361
  {
362
- kind: "tool-call",
362
+ kind: "tool.call",
363
363
  toolId: "add",
364
364
  state: IN_PROGRESS,
365
365
  callId: "call_1",
@@ -415,7 +415,7 @@ describe("Lifecycle Hooks", () => {
415
415
  content: [
416
416
  message({ role: "assistant", text: "" }),
417
417
  {
418
- kind: "tool-call",
418
+ kind: "tool.call",
419
419
  toolId: "failing",
420
420
  state: IN_PROGRESS,
421
421
  callId: "call_1",
@@ -473,14 +473,14 @@ describe("Lifecycle Hooks", () => {
473
473
  content: [
474
474
  message({ role: "assistant", text: "" }),
475
475
  {
476
- kind: "tool-call",
476
+ kind: "tool.call",
477
477
  toolId: "tool1",
478
478
  state: IN_PROGRESS,
479
479
  callId: "call_1",
480
480
  arguments: JSON.stringify({ value: "a" }),
481
481
  },
482
482
  {
483
- kind: "tool-call",
483
+ kind: "tool.call",
484
484
  toolId: "tool2",
485
485
  state: IN_PROGRESS,
486
486
  callId: "call_2",
@@ -312,7 +312,7 @@ describe("InMemoryThreadStore", () => {
312
312
  it("should filter by kinds", async () => {
313
313
  await store.append([
314
314
  {
315
- kind: "tool-call",
315
+ kind: "tool.call",
316
316
  id: "tc-1",
317
317
  tid: "thread-1",
318
318
  seq: 3,
@@ -10,18 +10,18 @@ async function* streamFromResponse(response) {
10
10
  if (contentItem.kind === "text") {
11
11
  // Yield text-start
12
12
  yield {
13
- kind: "text-start",
13
+ kind: "text.start",
14
14
  id: item.id,
15
15
  };
16
16
  // Yield text-delta
17
17
  yield {
18
- kind: "text-delta",
18
+ kind: "text.delta",
19
19
  id: item.id,
20
20
  text: contentItem.text,
21
21
  };
22
22
  // Yield text-end
23
23
  yield {
24
- kind: "text-end",
24
+ kind: "text.end",
25
25
  id: item.id,
26
26
  };
27
27
  }
@@ -48,13 +48,13 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
48
48
  }
49
49
  expect(events.length).toBeGreaterThan(0);
50
50
  // Should have text-delta events (for streaming UX)
51
- const textDeltas = events.filter((e) => e.kind === "text-delta");
51
+ const textDeltas = events.filter((e) => e.kind === "text.delta");
52
52
  expect(textDeltas.length).toBeGreaterThan(0);
53
53
  // Should have text-start event
54
- const textStarts = events.filter((e) => e.kind === "text-start");
54
+ const textStarts = events.filter((e) => e.kind === "text.start");
55
55
  expect(textStarts.length).toBeGreaterThan(0);
56
56
  // Should have text-end event
57
- const textEnds = events.filter((e) => e.kind === "text-end");
57
+ const textEnds = events.filter((e) => e.kind === "text.end");
58
58
  expect(textEnds.length).toBeGreaterThan(0);
59
59
  // Should have complete Message item (for history)
60
60
  const messages = events.filter((e) => e.kind === "message");
@@ -103,9 +103,9 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
103
103
  expect(["message", "reasoning", "tool-call", "tool-result"]).toContain(event.kind);
104
104
  }
105
105
  // Stream events should include deltas (but history should not)
106
- const streamDeltas = streamEvents.filter((e) => e.kind === "text-delta" ||
107
- e.kind === "text-start" ||
108
- e.kind === "text-end");
106
+ const streamDeltas = streamEvents.filter((e) => e.kind === "text.delta" ||
107
+ e.kind === "text.start" ||
108
+ e.kind === "text.end");
109
109
  expect(streamDeltas.length).toBeGreaterThan(0);
110
110
  // History should contain the input message (with ThreadEvent headers added)
111
111
  expect(history[0]).toMatchObject({
@@ -162,14 +162,14 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
162
162
  }
163
163
  expect(events.length).toBeGreaterThan(0);
164
164
  // Should have tool calls
165
- const toolCalls = events.filter((e) => e.kind === "tool-call");
165
+ const toolCalls = events.filter((e) => e.kind === "tool.call");
166
166
  expect(toolCalls.length).toBeGreaterThan(0);
167
167
  // Verify tool was called with correct parameters
168
168
  const addToolCall = toolCalls.find((tc) => tc.toolId === "add");
169
169
  expect(addToolCall).toBeDefined();
170
170
  expect(JSON.parse(addToolCall.arguments)).toEqual({ a: 25, b: 17 });
171
171
  // Should have tool results
172
- const toolResults = events.filter((e) => e.kind === "tool-result");
172
+ const toolResults = events.filter((e) => e.kind === "tool.result");
173
173
  expect(toolResults.length).toBeGreaterThan(0);
174
174
  // Verify tool result is correct
175
175
  const addToolResult = toolResults.find((tr) => tr.callId === addToolCall.callId);
@@ -177,8 +177,8 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
177
177
  expect(addToolResult.result).toBe(42);
178
178
  // History should contain tool calls and results
179
179
  const history = thread.history;
180
- const historyToolCalls = history.filter((e) => e.kind === "tool-call");
181
- const historyToolResults = history.filter((e) => e.kind === "tool-result");
180
+ const historyToolCalls = history.filter((e) => e.kind === "tool.call");
181
+ const historyToolResults = history.filter((e) => e.kind === "tool.result");
182
182
  expect(historyToolCalls.length).toBe(toolCalls.length);
183
183
  expect(historyToolResults.length).toBe(toolResults.length);
184
184
  // Verify the assistant's final response references the correct answer
@@ -228,8 +228,8 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
228
228
  events.push(event);
229
229
  }
230
230
  // Find the tool call and result
231
- const toolCalls = events.filter((e) => e.kind === "tool-call");
232
- const toolResults = events.filter((e) => e.kind === "tool-result");
231
+ const toolCalls = events.filter((e) => e.kind === "tool.call");
232
+ const toolResults = events.filter((e) => e.kind === "tool.result");
233
233
  expect(toolCalls.length).toBeGreaterThan(0);
234
234
  expect(toolResults.length).toBeGreaterThan(0);
235
235
  const multiplyCall = toolCalls[0];
@@ -244,8 +244,8 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
244
244
  expect(multiplyResult.callId.length).toBeGreaterThan(0);
245
245
  // Verify history contains both with matching callIds
246
246
  const history = thread.history;
247
- const historyToolCall = history.find((e) => e.kind === "tool-call" && e.toolId === "multiply");
248
- const historyToolResult = history.find((e) => e.kind === "tool-result" && e.toolId === "multiply");
247
+ const historyToolCall = history.find((e) => e.kind === "tool.call" && e.toolId === "multiply");
248
+ const historyToolResult = history.find((e) => e.kind === "tool.result" && e.toolId === "multiply");
249
249
  expect(historyToolCall).toBeDefined();
250
250
  expect(historyToolResult).toBeDefined();
251
251
  expect(historyToolCall.callId).toBe(historyToolResult.callId);
@@ -67,7 +67,7 @@ describe("Thread Persistence", () => {
67
67
  // return {
68
68
  // content: [
69
69
  // message({ role: "assistant", text: "" }),
70
- // { kind: "tool-call", toolId: "test", callId: "call_1", state: IN_PROGRESS, arguments: "{}" },
70
+ // { kind: "tool.call", toolId: "test", callId: "call_1", state: IN_PROGRESS, arguments: "{}" },
71
71
  // ],
72
72
  // finishReason: "stop",
73
73
  // usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
@@ -127,7 +127,7 @@ describe("Thread Persistence", () => {
127
127
  // return {
128
128
  // content: [
129
129
  // message({ role: "assistant", text: "" }),
130
- // { kind: "tool-call", toolId: "echo", callId: "call_1", state: IN_PROGRESS, arguments: '{"text":"test"}' },
130
+ // { kind: "tool.call", toolId: "echo", callId: "call_1", state: IN_PROGRESS, arguments: '{"text":"test"}' },
131
131
  // ],
132
132
  // finishReason: "stop",
133
133
  // usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
@@ -167,7 +167,7 @@ describe("Thread Persistence", () => {
167
167
  //
168
168
  // // Find the append call for tick 1 (should include model message, tool-call, and tool-result)
169
169
  // const tick1Events = storage.calls.append.find(batch =>
170
- // batch.some(e => e.kind === "tool-result")
170
+ // batch.some(e => e.kind === "tool.result")
171
171
  // );
172
172
  //
173
173
  // expect(tick1Events).toBeDefined();
@@ -177,8 +177,8 @@ describe("Thread Persistence", () => {
177
177
  // expect(tick1Events).toEqual(
178
178
  // expect.arrayContaining([
179
179
  // expect.objectContaining({ kind: "message", role: "assistant" }),
180
- // expect.objectContaining({ kind: "tool-call", toolId: "echo" }),
181
- // expect.objectContaining({ kind: "tool-result", toolId: "echo", result: "Echo: test" }),
180
+ // expect.objectContaining({ kind: "tool.call", toolId: "echo" }),
181
+ // expect.objectContaining({ kind: "tool.result", toolId: "echo", result: "Echo: test" }),
182
182
  // ])
183
183
  // );
184
184
  });
@@ -220,7 +220,7 @@ describe("Thread Persistence", () => {
220
220
  // expect(lastUpdate.patch.state).toBe(STOPPED);
221
221
  //
222
222
  // // Verify stream-start event
223
- // expect(events[0]).toEqual({ kind: "stream-start" });
223
+ // expect(events[0]).toEqual({ kind: "stream.start" });
224
224
  });
225
225
  it.skip("should persist STOPPED state even on model error", async () => {
226
226
  // TODO: Enable once InMemoryStorage is implemented
@@ -164,7 +164,7 @@ describe("Thread", () => {
164
164
  content: [],
165
165
  },
166
166
  {
167
- kind: "tool-call",
167
+ kind: "tool.call",
168
168
  toolId: "echo",
169
169
  state: IN_PROGRESS,
170
170
  callId: "call_1",
@@ -233,7 +233,7 @@ describe("Thread", () => {
233
233
  }),
234
234
  // Tool call (tick 1)
235
235
  expect.objectContaining({
236
- kind: "tool-call",
236
+ kind: "tool.call",
237
237
  toolId: "echo",
238
238
  callId: "call_1",
239
239
  state: IN_PROGRESS,
@@ -241,7 +241,7 @@ describe("Thread", () => {
241
241
  }),
242
242
  // Tool result (executed after tick 1)
243
243
  expect.objectContaining({
244
- kind: "tool-result",
244
+ kind: "tool.result",
245
245
  callId: "call_1",
246
246
  toolId: "echo",
247
247
  state: COMPLETED,
@@ -271,7 +271,7 @@ describe("Thread", () => {
271
271
  content: [],
272
272
  },
273
273
  {
274
- kind: "tool-call",
274
+ kind: "tool.call",
275
275
  toolId: "simple",
276
276
  state: IN_PROGRESS,
277
277
  callId: "call_1",
@@ -297,7 +297,7 @@ describe("Thread", () => {
297
297
  content: [],
298
298
  },
299
299
  {
300
- kind: "tool-call",
300
+ kind: "tool.call",
301
301
  toolId: "simple",
302
302
  state: IN_PROGRESS,
303
303
  callId: "call_2",
@@ -371,7 +371,7 @@ describe("Thread", () => {
371
371
  content: [],
372
372
  },
373
373
  {
374
- kind: "tool-call",
374
+ kind: "tool.call",
375
375
  toolId: "nonexistent",
376
376
  state: IN_PROGRESS,
377
377
  callId: "call_1",
@@ -418,9 +418,9 @@ describe("Thread", () => {
418
418
  await thread.execute();
419
419
  const history = thread.history;
420
420
  // Check that the tool result is an error
421
- const toolResult = history.find((e) => e.kind === "tool-result");
421
+ const toolResult = history.find((e) => e.kind === "tool.result");
422
422
  expect(toolResult).toEqual(expect.objectContaining({
423
- kind: "tool-result",
423
+ kind: "tool.result",
424
424
  callId: "call_1",
425
425
  toolId: "nonexistent",
426
426
  state: FAILED,
@@ -443,7 +443,7 @@ describe("Thread", () => {
443
443
  content: [],
444
444
  },
445
445
  {
446
- kind: "tool-call",
446
+ kind: "tool.call",
447
447
  toolId: "failing",
448
448
  state: IN_PROGRESS,
449
449
  callId: "call_1",
@@ -499,9 +499,9 @@ describe("Thread", () => {
499
499
  const thread = new Thread({ agent, input: userMessage("test") });
500
500
  await thread.execute();
501
501
  const history = thread.history;
502
- const toolResult = history.find((e) => e.kind === "tool-result");
502
+ const toolResult = history.find((e) => e.kind === "tool.result");
503
503
  expect(toolResult).toMatchObject({
504
- kind: "tool-result",
504
+ kind: "tool.result",
505
505
  callId: "call_1",
506
506
  toolId: "failing",
507
507
  state: FAILED,
@@ -524,7 +524,7 @@ describe("Thread", () => {
524
524
  content: [],
525
525
  },
526
526
  {
527
- kind: "tool-call",
527
+ kind: "tool.call",
528
528
  toolId: "add",
529
529
  state: IN_PROGRESS,
530
530
  callId: "call_1",
@@ -577,9 +577,9 @@ describe("Thread", () => {
577
577
  await thread.execute();
578
578
  // @ts-expect-error
579
579
  const history = thread.history;
580
- const toolResult = history.find((e) => e.kind === "tool-result");
580
+ const toolResult = history.find((e) => e.kind === "tool.result");
581
581
  expect(toolResult).toEqual(expect.objectContaining({
582
- kind: "tool-result",
582
+ kind: "tool.result",
583
583
  callId: "call_1",
584
584
  toolId: "add",
585
585
  state: COMPLETED,
@@ -604,14 +604,14 @@ describe("Thread", () => {
604
604
  content: [],
605
605
  },
606
606
  {
607
- kind: "tool-call",
607
+ kind: "tool.call",
608
608
  toolId: "tool1",
609
609
  state: IN_PROGRESS,
610
610
  callId: "call_1",
611
611
  arguments: JSON.stringify({ value: "a" }),
612
612
  },
613
613
  {
614
- kind: "tool-call",
614
+ kind: "tool.call",
615
615
  toolId: "tool2",
616
616
  state: IN_PROGRESS,
617
617
  callId: "call_2",
@@ -672,11 +672,11 @@ describe("Thread", () => {
672
672
  await thread.execute();
673
673
  const history = thread.history;
674
674
  // Should have both tool results in history
675
- const toolResults = history.filter((e) => e.kind === "tool-result");
675
+ const toolResults = history.filter((e) => e.kind === "tool.result");
676
676
  expect(toolResults).toHaveLength(2);
677
677
  expect(toolResults).toEqual(expect.arrayContaining([
678
678
  expect.objectContaining({
679
- kind: "tool-result",
679
+ kind: "tool.result",
680
680
  callId: "call_1",
681
681
  toolId: "tool1",
682
682
  state: COMPLETED,
@@ -684,7 +684,7 @@ describe("Thread", () => {
684
684
  error: null,
685
685
  }),
686
686
  expect.objectContaining({
687
- kind: "tool-result",
687
+ kind: "tool.result",
688
688
  callId: "call_2",
689
689
  toolId: "tool2",
690
690
  state: COMPLETED,
@@ -709,7 +709,7 @@ describe("Thread", () => {
709
709
  content: [],
710
710
  },
711
711
  {
712
- kind: "tool-call",
712
+ kind: "tool.call",
713
713
  toolId: "simple",
714
714
  state: IN_PROGRESS,
715
715
  callId: `call_${callCount}`,
@@ -777,7 +777,7 @@ describe("Thread", () => {
777
777
  content: [],
778
778
  },
779
779
  {
780
- kind: "tool-call",
780
+ kind: "tool.call",
781
781
  toolId: "simple",
782
782
  state: IN_PROGRESS,
783
783
  callId: "call_1",
@@ -880,7 +880,7 @@ describe("Thread", () => {
880
880
  content: [{ kind: "text", text: "Let me use a tool" }],
881
881
  },
882
882
  {
883
- kind: "tool-call",
883
+ kind: "tool.call",
884
884
  toolId: "simple",
885
885
  state: IN_PROGRESS,
886
886
  callId: "call_1",
@@ -127,7 +127,7 @@ export class Thread {
127
127
  namespace: this.namespace,
128
128
  context: this.context,
129
129
  });
130
- yield { kind: "stream-start" }; // always yield start immediately
130
+ yield { kind: "stream.start" }; // always yield start immediately
131
131
  try {
132
132
  yield* this._execute();
133
133
  this.agent.emit("thread.stop", {
@@ -380,7 +380,7 @@ export class Thread {
380
380
  const pendingApprovals = [];
381
381
  // (TODO): clean this - approval tracking should be handled differently
382
382
  for (const e of toolEvents) {
383
- if (e.kind === "tool-result" &&
383
+ if (e.kind === "tool.result" &&
384
384
  e.state === "requires_approval" // (TODO): fix this
385
385
  ) {
386
386
  // find the original tool call for this pending approval
@@ -440,7 +440,7 @@ export class Thread {
440
440
  error: res.error,
441
441
  });
442
442
  return {
443
- kind: "tool-result",
443
+ kind: "tool.result",
444
444
  callId: call.callId,
445
445
  toolId: call.toolId,
446
446
  state: res.state,
@@ -460,7 +460,7 @@ export class Thread {
460
460
  error: error instanceof Error ? error.message : String(error),
461
461
  });
462
462
  return {
463
- kind: "tool-result",
463
+ kind: "tool.result",
464
464
  callId: call.callId,
465
465
  toolId: call.toolId,
466
466
  state: FAILED,
@@ -32,7 +32,7 @@ export function tevent(event) {
32
32
  * Check if an event is a tool call
33
33
  */
34
34
  export function isActionIntention(event) {
35
- return event.kind === "tool-call";
35
+ return event.kind === "tool.call";
36
36
  }
37
37
  /**
38
38
  * Extract action intentions from a list of events.
@@ -50,8 +50,8 @@ export function notDelta(event) {
50
50
  switch (event.kind) {
51
51
  case "message":
52
52
  case "reasoning":
53
- case "tool-call":
54
- case "tool-result":
53
+ case "tool.call":
54
+ case "tool.result":
55
55
  return true;
56
56
  // all other events are streaming deltas/control events
57
57
  default:
@@ -66,8 +66,8 @@ export function isPublicEvent(event) {
66
66
  switch (event.kind) {
67
67
  case "message":
68
68
  case "reasoning":
69
- case "tool-call":
70
- case "tool-result":
69
+ case "tool.call":
70
+ case "tool.result":
71
71
  return true;
72
72
  case "system":
73
73
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kernl",
3
- "version": "0.11.4",
3
+ "version": "0.12.0",
4
4
  "description": "A modern AI agent framework",
5
5
  "keywords": [
6
6
  "kernl",
@@ -34,9 +34,9 @@
34
34
  "dependencies": {
35
35
  "@modelcontextprotocol/sdk": "^1.20.2",
36
36
  "yaml": "^2.8.2",
37
- "@kernl-sdk/protocol": "0.4.2",
38
- "@kernl-sdk/shared": "^0.4.0",
39
- "@kernl-sdk/retrieval": "0.1.8"
37
+ "@kernl-sdk/protocol": "0.5.0",
38
+ "@kernl-sdk/retrieval": "0.1.9",
39
+ "@kernl-sdk/shared": "^0.4.0"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "zod": "^4.1.8"
@@ -48,7 +48,7 @@
48
48
  "typescript": "5.9.2",
49
49
  "vitest": "^4.0.8",
50
50
  "zod": "^4.1.8",
51
- "@kernl-sdk/ai": "0.3.5"
51
+ "@kernl-sdk/ai": "0.4.0"
52
52
  },
53
53
  "scripts": {
54
54
  "clean": "rm -rf dist",
@@ -268,7 +268,7 @@ describe("Agent.stream() lifecycle", () => {
268
268
  events.push(event);
269
269
  }
270
270
 
271
- expect(events[0]).toEqual({ kind: "stream-start" });
271
+ expect(events[0]).toEqual({ kind: "stream.start" });
272
272
  });
273
273
 
274
274
  it("should have same persistence behavior as run()", async () => {
@@ -305,7 +305,7 @@ describe("Agent.stream() lifecycle", () => {
305
305
  // Should have streamed events
306
306
  expect(events).toEqual(
307
307
  expect.arrayContaining([
308
- { kind: "stream-start" },
308
+ { kind: "stream.start" },
309
309
  expect.objectContaining({ kind: "message" }),
310
310
  ]),
311
311
  );
@@ -308,7 +308,7 @@ describe("Lifecycle Hooks", () => {
308
308
  content: [
309
309
  message({ role: "assistant", text: "" }),
310
310
  {
311
- kind: "tool-call" as const,
311
+ kind: "tool.call" as const,
312
312
  toolId: "echo",
313
313
  state: IN_PROGRESS,
314
314
  callId: "call_1",
@@ -381,7 +381,7 @@ describe("Lifecycle Hooks", () => {
381
381
  content: [
382
382
  message({ role: "assistant", text: "" }),
383
383
  {
384
- kind: "tool-call" as const,
384
+ kind: "tool.call" as const,
385
385
  toolId: "add",
386
386
  state: IN_PROGRESS,
387
387
  callId: "call_1",
@@ -446,7 +446,7 @@ describe("Lifecycle Hooks", () => {
446
446
  content: [
447
447
  message({ role: "assistant", text: "" }),
448
448
  {
449
- kind: "tool-call" as const,
449
+ kind: "tool.call" as const,
450
450
  toolId: "add",
451
451
  state: IN_PROGRESS,
452
452
  callId: "call_1",
@@ -510,7 +510,7 @@ describe("Lifecycle Hooks", () => {
510
510
  content: [
511
511
  message({ role: "assistant", text: "" }),
512
512
  {
513
- kind: "tool-call" as const,
513
+ kind: "tool.call" as const,
514
514
  toolId: "failing",
515
515
  state: IN_PROGRESS,
516
516
  callId: "call_1",
@@ -576,14 +576,14 @@ describe("Lifecycle Hooks", () => {
576
576
  content: [
577
577
  message({ role: "assistant", text: "" }),
578
578
  {
579
- kind: "tool-call" as const,
579
+ kind: "tool.call" as const,
580
580
  toolId: "tool1",
581
581
  state: IN_PROGRESS,
582
582
  callId: "call_1",
583
583
  arguments: JSON.stringify({ value: "a" }),
584
584
  },
585
585
  {
586
- kind: "tool-call" as const,
586
+ kind: "tool.call" as const,
587
587
  toolId: "tool2",
588
588
  state: IN_PROGRESS,
589
589
  callId: "call_2",
@@ -367,7 +367,7 @@ describe("InMemoryThreadStore", () => {
367
367
  it("should filter by kinds", async () => {
368
368
  await store.append([
369
369
  {
370
- kind: "tool-call",
370
+ kind: "tool.call",
371
371
  id: "tc-1",
372
372
  tid: "thread-1",
373
373
  seq: 3,
@@ -20,18 +20,18 @@ async function* streamFromResponse(
20
20
  if (contentItem.kind === "text") {
21
21
  // Yield text-start
22
22
  yield {
23
- kind: "text-start" as const,
23
+ kind: "text.start" as const,
24
24
  id: item.id,
25
25
  };
26
26
  // Yield text-delta
27
27
  yield {
28
- kind: "text-delta" as const,
28
+ kind: "text.delta" as const,
29
29
  id: item.id,
30
30
  text: contentItem.text,
31
31
  };
32
32
  // Yield text-end
33
33
  yield {
34
- kind: "text-end" as const,
34
+ kind: "text.end" as const,
35
35
  id: item.id,
36
36
  };
37
37
  }
@@ -63,15 +63,15 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
63
63
  expect(events.length).toBeGreaterThan(0);
64
64
 
65
65
  // Should have text-delta events (for streaming UX)
66
- const textDeltas = events.filter((e) => e.kind === "text-delta");
66
+ const textDeltas = events.filter((e) => e.kind === "text.delta");
67
67
  expect(textDeltas.length).toBeGreaterThan(0);
68
68
 
69
69
  // Should have text-start event
70
- const textStarts = events.filter((e) => e.kind === "text-start");
70
+ const textStarts = events.filter((e) => e.kind === "text.start");
71
71
  expect(textStarts.length).toBeGreaterThan(0);
72
72
 
73
73
  // Should have text-end event
74
- const textEnds = events.filter((e) => e.kind === "text-end");
74
+ const textEnds = events.filter((e) => e.kind === "text.end");
75
75
  expect(textEnds.length).toBeGreaterThan(0);
76
76
 
77
77
  // Should have complete Message item (for history)
@@ -140,9 +140,9 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
140
140
  // Stream events should include deltas (but history should not)
141
141
  const streamDeltas = streamEvents.filter(
142
142
  (e: any) =>
143
- e.kind === "text-delta" ||
144
- e.kind === "text-start" ||
145
- e.kind === "text-end",
143
+ e.kind === "text.delta" ||
144
+ e.kind === "text.start" ||
145
+ e.kind === "text.end",
146
146
  );
147
147
  expect(streamDeltas.length).toBeGreaterThan(0);
148
148
 
@@ -216,8 +216,8 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
216
216
 
217
217
  // Should have tool calls
218
218
  const toolCalls = events.filter(
219
- (e): e is Extract<ThreadStreamEvent, { kind: "tool-call" }> =>
220
- e.kind === "tool-call",
219
+ (e): e is Extract<ThreadStreamEvent, { kind: "tool.call" }> =>
220
+ e.kind === "tool.call",
221
221
  );
222
222
  expect(toolCalls.length).toBeGreaterThan(0);
223
223
 
@@ -228,8 +228,8 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
228
228
 
229
229
  // Should have tool results
230
230
  const toolResults = events.filter(
231
- (e): e is Extract<ThreadStreamEvent, { kind: "tool-result" }> =>
232
- e.kind === "tool-result",
231
+ (e): e is Extract<ThreadStreamEvent, { kind: "tool.result" }> =>
232
+ e.kind === "tool.result",
233
233
  );
234
234
  expect(toolResults.length).toBeGreaterThan(0);
235
235
 
@@ -242,9 +242,9 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
242
242
 
243
243
  // History should contain tool calls and results
244
244
  const history = (thread as any).history as ThreadEvent[];
245
- const historyToolCalls = history.filter((e) => e.kind === "tool-call");
245
+ const historyToolCalls = history.filter((e) => e.kind === "tool.call");
246
246
  const historyToolResults = history.filter(
247
- (e) => e.kind === "tool-result",
247
+ (e) => e.kind === "tool.result",
248
248
  );
249
249
 
250
250
  expect(historyToolCalls.length).toBe(toolCalls.length);
@@ -309,12 +309,12 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
309
309
 
310
310
  // Find the tool call and result
311
311
  const toolCalls = events.filter(
312
- (e): e is Extract<ThreadStreamEvent, { kind: "tool-call" }> =>
313
- e.kind === "tool-call",
312
+ (e): e is Extract<ThreadStreamEvent, { kind: "tool.call" }> =>
313
+ e.kind === "tool.call",
314
314
  );
315
315
  const toolResults = events.filter(
316
- (e): e is Extract<ThreadStreamEvent, { kind: "tool-result" }> =>
317
- e.kind === "tool-result",
316
+ (e): e is Extract<ThreadStreamEvent, { kind: "tool.result" }> =>
317
+ e.kind === "tool.result",
318
318
  );
319
319
 
320
320
  expect(toolCalls.length).toBeGreaterThan(0);
@@ -336,10 +336,10 @@ describe.skipIf(SKIP_INTEGRATION_TESTS)("Thread streaming integration", () => {
336
336
  // Verify history contains both with matching callIds
337
337
  const history = (thread as any).history as ThreadEvent[];
338
338
  const historyToolCall = history.find(
339
- (e) => e.kind === "tool-call" && e.toolId === "multiply",
339
+ (e) => e.kind === "tool.call" && e.toolId === "multiply",
340
340
  );
341
341
  const historyToolResult = history.find(
342
- (e) => e.kind === "tool-result" && e.toolId === "multiply",
342
+ (e) => e.kind === "tool.result" && e.toolId === "multiply",
343
343
  );
344
344
 
345
345
  expect(historyToolCall).toBeDefined();
@@ -77,7 +77,7 @@ describe("Thread Persistence", () => {
77
77
  // return {
78
78
  // content: [
79
79
  // message({ role: "assistant", text: "" }),
80
- // { kind: "tool-call", toolId: "test", callId: "call_1", state: IN_PROGRESS, arguments: "{}" },
80
+ // { kind: "tool.call", toolId: "test", callId: "call_1", state: IN_PROGRESS, arguments: "{}" },
81
81
  // ],
82
82
  // finishReason: "stop",
83
83
  // usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
@@ -138,7 +138,7 @@ describe("Thread Persistence", () => {
138
138
  // return {
139
139
  // content: [
140
140
  // message({ role: "assistant", text: "" }),
141
- // { kind: "tool-call", toolId: "echo", callId: "call_1", state: IN_PROGRESS, arguments: '{"text":"test"}' },
141
+ // { kind: "tool.call", toolId: "echo", callId: "call_1", state: IN_PROGRESS, arguments: '{"text":"test"}' },
142
142
  // ],
143
143
  // finishReason: "stop",
144
144
  // usage: { inputTokens: 2, outputTokens: 2, totalTokens: 4 },
@@ -178,7 +178,7 @@ describe("Thread Persistence", () => {
178
178
  //
179
179
  // // Find the append call for tick 1 (should include model message, tool-call, and tool-result)
180
180
  // const tick1Events = storage.calls.append.find(batch =>
181
- // batch.some(e => e.kind === "tool-result")
181
+ // batch.some(e => e.kind === "tool.result")
182
182
  // );
183
183
  //
184
184
  // expect(tick1Events).toBeDefined();
@@ -188,8 +188,8 @@ describe("Thread Persistence", () => {
188
188
  // expect(tick1Events).toEqual(
189
189
  // expect.arrayContaining([
190
190
  // expect.objectContaining({ kind: "message", role: "assistant" }),
191
- // expect.objectContaining({ kind: "tool-call", toolId: "echo" }),
192
- // expect.objectContaining({ kind: "tool-result", toolId: "echo", result: "Echo: test" }),
191
+ // expect.objectContaining({ kind: "tool.call", toolId: "echo" }),
192
+ // expect.objectContaining({ kind: "tool.result", toolId: "echo", result: "Echo: test" }),
193
193
  // ])
194
194
  // );
195
195
  });
@@ -232,7 +232,7 @@ describe("Thread Persistence", () => {
232
232
  // expect(lastUpdate.patch.state).toBe(STOPPED);
233
233
  //
234
234
  // // Verify stream-start event
235
- // expect(events[0]).toEqual({ kind: "stream-start" });
235
+ // expect(events[0]).toEqual({ kind: "stream.start" });
236
236
  });
237
237
 
238
238
  it.skip("should persist STOPPED state even on model error", async () => {
@@ -194,7 +194,7 @@ describe("Thread", () => {
194
194
  content: [],
195
195
  },
196
196
  {
197
- kind: "tool-call" as const,
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-call",
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-result",
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-call" as const,
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-call" as const,
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-call" as const,
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-result");
479
+ const toolResult = history.find((e) => e.kind === "tool.result");
480
480
  expect(toolResult).toEqual(expect.objectContaining({
481
- kind: "tool-result",
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-call" as const,
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-result");
570
+ const toolResult = history.find((e) => e.kind === "tool.result");
571
571
  expect(toolResult).toMatchObject({
572
- kind: "tool-result",
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-call" as const,
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-result");
658
+ const toolResult = history.find((e) => e.kind === "tool.result");
659
659
  expect(toolResult).toEqual(expect.objectContaining({
660
- kind: "tool-result",
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-call" as const,
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-call" as const,
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-result");
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-result",
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-result",
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-call" as const,
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-call" as const,
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-call" as const,
1000
+ kind: "tool.call" as const,
1001
1001
  toolId: "simple",
1002
1002
  state: IN_PROGRESS,
1003
1003
  callId: "call_1",
@@ -184,7 +184,7 @@ export class Thread<
184
184
  context: this.context,
185
185
  });
186
186
 
187
- yield { kind: "stream-start" }; // always yield start immediately
187
+ yield { kind: "stream.start" }; // always yield start immediately
188
188
 
189
189
  try {
190
190
  yield* this._execute();
@@ -470,7 +470,7 @@ export class Thread<
470
470
  // (TODO): clean this - approval tracking should be handled differently
471
471
  for (const e of toolEvents) {
472
472
  if (
473
- e.kind === "tool-result" &&
473
+ e.kind === "tool.result" &&
474
474
  (e.state as any) === "requires_approval" // (TODO): fix this
475
475
  ) {
476
476
  // find the original tool call for this pending approval
@@ -542,7 +542,7 @@ export class Thread<
542
542
  });
543
543
 
544
544
  return {
545
- kind: "tool-result" as const,
545
+ kind: "tool.result" as const,
546
546
  callId: call.callId,
547
547
  toolId: call.toolId,
548
548
  state: res.state,
@@ -562,7 +562,7 @@ export class Thread<
562
562
  });
563
563
 
564
564
  return {
565
- kind: "tool-result" as const,
565
+ kind: "tool.result" as const,
566
566
  callId: call.callId,
567
567
  toolId: call.toolId,
568
568
  state: FAILED,
@@ -58,7 +58,7 @@ export function tevent(event: {
58
58
  * Check if an event is a tool call
59
59
  */
60
60
  export function isActionIntention(event: LanguageModelItem): event is ToolCall {
61
- return event.kind === "tool-call";
61
+ return event.kind === "tool.call";
62
62
  }
63
63
 
64
64
  /**
@@ -78,8 +78,8 @@ export function notDelta(event: ThreadStreamEvent): event is LanguageModelItem {
78
78
  switch (event.kind) {
79
79
  case "message":
80
80
  case "reasoning":
81
- case "tool-call":
82
- case "tool-result":
81
+ case "tool.call":
82
+ case "tool.result":
83
83
  return true;
84
84
 
85
85
  // all other events are streaming deltas/control events
@@ -96,8 +96,8 @@ export function isPublicEvent(event: ThreadEvent): event is PublicThreadEvent {
96
96
  switch (event.kind) {
97
97
  case "message":
98
98
  case "reasoning":
99
- case "tool-call":
100
- case "tool-result":
99
+ case "tool.call":
100
+ case "tool.result":
101
101
  return true;
102
102
 
103
103
  case "system":