kernl 0.12.3 → 0.12.6

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.
Files changed (100) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/README.md +29 -0
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +2 -0
  6. package/dist/kernl/kernl.d.ts +6 -0
  7. package/dist/kernl/kernl.d.ts.map +1 -1
  8. package/dist/kernl/kernl.js +19 -0
  9. package/dist/kernl/types.d.ts +6 -0
  10. package/dist/kernl/types.d.ts.map +1 -1
  11. package/dist/lib/env.d.ts +2 -2
  12. package/dist/mcp/http.d.ts.map +1 -1
  13. package/dist/mcp/http.js +1 -5
  14. package/dist/mcp/sse.d.ts.map +1 -1
  15. package/dist/mcp/sse.js +1 -5
  16. package/dist/mcp/stdio.d.ts.map +1 -1
  17. package/dist/mcp/stdio.js +1 -5
  18. package/dist/task.d.ts.map +1 -1
  19. package/dist/task.js +0 -1
  20. package/dist/thread/__tests__/thread.test.js +241 -0
  21. package/dist/thread/thread.d.ts +5 -4
  22. package/dist/thread/thread.d.ts.map +1 -1
  23. package/dist/thread/thread.js +91 -22
  24. package/dist/thread/types.d.ts +5 -0
  25. package/dist/thread/types.d.ts.map +1 -1
  26. package/dist/tool/tool.d.ts +2 -2
  27. package/dist/tool/tool.d.ts.map +1 -1
  28. package/dist/tracing/__tests__/composite.test.d.ts +2 -0
  29. package/dist/tracing/__tests__/composite.test.d.ts.map +1 -0
  30. package/dist/tracing/__tests__/composite.test.js +146 -0
  31. package/dist/tracing/__tests__/dispatch.test.d.ts +2 -0
  32. package/dist/tracing/__tests__/dispatch.test.d.ts.map +1 -0
  33. package/dist/tracing/__tests__/dispatch.test.js +160 -0
  34. package/dist/tracing/__tests__/helpers.d.ts +69 -0
  35. package/dist/tracing/__tests__/helpers.d.ts.map +1 -0
  36. package/dist/tracing/__tests__/helpers.js +109 -0
  37. package/dist/tracing/__tests__/integration.test.d.ts +2 -0
  38. package/dist/tracing/__tests__/integration.test.d.ts.map +1 -0
  39. package/dist/tracing/__tests__/integration.test.js +675 -0
  40. package/dist/tracing/__tests__/span.test.d.ts +2 -0
  41. package/dist/tracing/__tests__/span.test.d.ts.map +1 -0
  42. package/dist/tracing/__tests__/span.test.js +188 -0
  43. package/dist/tracing/dispatch.d.ts +43 -0
  44. package/dist/tracing/dispatch.d.ts.map +1 -0
  45. package/dist/tracing/dispatch.js +70 -0
  46. package/dist/tracing/index.d.ts +8 -0
  47. package/dist/tracing/index.d.ts.map +1 -0
  48. package/dist/tracing/index.js +6 -0
  49. package/dist/tracing/span.d.ts +69 -0
  50. package/dist/tracing/span.d.ts.map +1 -0
  51. package/dist/tracing/span.js +64 -0
  52. package/dist/tracing/subscriber.d.ts +53 -0
  53. package/dist/tracing/subscriber.d.ts.map +1 -0
  54. package/dist/tracing/subscriber.js +1 -0
  55. package/dist/tracing/subscribers/composite.d.ts +26 -0
  56. package/dist/tracing/subscribers/composite.d.ts.map +1 -0
  57. package/dist/tracing/subscribers/composite.js +96 -0
  58. package/dist/tracing/subscribers/console.d.ts +22 -0
  59. package/dist/tracing/subscribers/console.d.ts.map +1 -0
  60. package/dist/tracing/subscribers/console.js +82 -0
  61. package/dist/tracing/types.d.ts +77 -0
  62. package/dist/tracing/types.d.ts.map +1 -0
  63. package/dist/tracing/types.js +1 -0
  64. package/package.json +6 -2
  65. package/src/agent.ts +2 -0
  66. package/src/index.ts +1 -0
  67. package/src/kernl/kernl.ts +21 -0
  68. package/src/kernl/types.ts +7 -0
  69. package/src/mcp/http.ts +1 -9
  70. package/src/mcp/sse.ts +1 -10
  71. package/src/mcp/stdio.ts +1 -10
  72. package/src/task.ts +0 -1
  73. package/src/thread/__tests__/thread.test.ts +280 -0
  74. package/src/thread/thread.ts +111 -24
  75. package/src/thread/types.ts +5 -0
  76. package/src/tool/tool.ts +1 -1
  77. package/src/tracing/__tests__/composite.test.ts +218 -0
  78. package/src/tracing/__tests__/dispatch.test.ts +222 -0
  79. package/src/tracing/__tests__/helpers.ts +138 -0
  80. package/src/tracing/__tests__/integration.test.ts +808 -0
  81. package/src/tracing/__tests__/span.test.ts +250 -0
  82. package/src/tracing/dispatch.ts +114 -0
  83. package/src/tracing/index.ts +39 -0
  84. package/src/tracing/span.ts +115 -0
  85. package/src/tracing/subscriber.ts +62 -0
  86. package/src/tracing/subscribers/composite.ts +102 -0
  87. package/src/tracing/subscribers/console.ts +101 -0
  88. package/src/tracing/types.ts +115 -0
  89. package/dist/trace/processor.d.ts +0 -1
  90. package/dist/trace/processor.d.ts.map +0 -1
  91. package/dist/trace/processor.js +0 -1
  92. package/dist/trace/traces.d.ts +0 -1
  93. package/dist/trace/traces.d.ts.map +0 -1
  94. package/dist/trace/traces.js +0 -73
  95. package/dist/trace/utils.d.ts +0 -22
  96. package/dist/trace/utils.d.ts.map +0 -1
  97. package/dist/trace/utils.js +0 -30
  98. package/src/trace/processor.ts +0 -0
  99. package/src/trace/traces.ts +0 -86
  100. package/src/trace/utils.ts +0 -38
@@ -1,6 +1,7 @@
1
1
  import assert from "assert";
2
2
  import * as z from "zod";
3
3
  import { Context } from "../context.js";
4
+ import { span, event, } from "../tracing/index.js";
4
5
  import { logger } from "../lib/logger.js";
5
6
  import { FAILED, RUNNING, STOPPED, message, } from "@kernl-sdk/protocol";
6
7
  import { randomID, filter } from "@kernl-sdk/shared/lib";
@@ -64,8 +65,9 @@ export class Thread {
64
65
  persisted; /* indicates thread was hydrated from storage */
65
66
  history;
66
67
  tickres; /* final result from terminal tick */
67
- abort;
68
+ _abort;
68
69
  storage;
70
+ _span; /* tracing span for current execution */
69
71
  constructor(options) {
70
72
  this.tid = options.tid ?? `tid_${randomID()}`;
71
73
  this.namespace = options.namespace ?? "kernl";
@@ -85,6 +87,7 @@ export class Thread {
85
87
  this.cpbuf = [];
86
88
  this.persisted = options.persisted ?? false;
87
89
  this.history = options.history ?? [];
90
+ this._abort = options.abort;
88
91
  // seek to latest seq (not persisted)
89
92
  if (this.history.length > 0) {
90
93
  this._seq = Math.max(...this.history.map((e) => e.seq));
@@ -113,20 +116,35 @@ export class Thread {
113
116
  * - Exactly one thread.stop (with result on success, error on failure)
114
117
  */
115
118
  async *stream() {
116
- if (this.state === RUNNING && this.abort) {
119
+ if (this.state === RUNNING) {
117
120
  throw new Error("thread already running");
118
121
  }
119
122
  this.state = RUNNING;
120
- this.abort = new AbortController();
121
123
  this.tickres = undefined; // reset for this run
122
124
  await this.checkpoint(); /* c1: persist RUNNING state + initial input */
125
+ // create thread span (root span for this execution)
126
+ this._span = span({
127
+ kind: "thread",
128
+ threadId: this.tid,
129
+ agentId: this.agent.id,
130
+ namespace: this.namespace,
131
+ context: this.context.context,
132
+ }, null);
133
+ this._span.enter();
123
134
  this.emit("thread.start");
124
135
  yield { kind: "stream.start" }; // always yield start immediately
125
136
  try {
126
137
  yield* this._execute();
138
+ this._span.record({ state: "stopped", result: this.tickres });
127
139
  this.emit("thread.stop", { state: STOPPED, result: this.tickres });
128
140
  }
129
141
  catch (err) {
142
+ this._span.error(err instanceof Error ? err : new Error(String(err)));
143
+ event({
144
+ kind: "thread.error",
145
+ message: err instanceof Error ? err.message : String(err),
146
+ stack: err instanceof Error ? err.stack : undefined,
147
+ });
130
148
  this.emit("thread.stop", {
131
149
  state: STOPPED,
132
150
  error: err instanceof Error ? err.message : String(err),
@@ -135,7 +153,9 @@ export class Thread {
135
153
  }
136
154
  finally {
137
155
  this.state = STOPPED;
138
- this.abort = undefined;
156
+ this._span.close();
157
+ // (TODO): questionable whether this should be undefined. perhaps a single thread should exit + resume..
158
+ this._span = undefined;
139
159
  await this.checkpoint(); /* c4: final checkpoint - persist STOPPED state */
140
160
  }
141
161
  }
@@ -148,7 +168,7 @@ export class Thread {
148
168
  async *_execute() {
149
169
  for (;;) {
150
170
  let err = undefined;
151
- if (this.abort?.signal.aborted) {
171
+ if (this._abort?.aborted) {
152
172
  return;
153
173
  }
154
174
  const events = [];
@@ -214,17 +234,36 @@ export class Thread {
214
234
  // (TODO): run input guardrails on first tick (if this._tick === 1)
215
235
  // (TODO): compaction if necessary
216
236
  const req = await this.prepareModelRequest(this.history);
237
+ const s = span({
238
+ kind: "model.call",
239
+ provider: this.model.provider,
240
+ modelId: this.model.modelId,
241
+ request: {
242
+ input: req.input,
243
+ settings: req.settings,
244
+ responseType: req.responseType,
245
+ tools: req.tools,
246
+ },
247
+ }, this._span.id);
248
+ s.enter();
217
249
  this.emit("model.call.start", { settings: req.settings ?? {} });
250
+ // tracing / observability
251
+ const content = [];
218
252
  let usage;
219
- let finishReason = { unified: "other", raw: undefined };
253
+ let finishReason = {
254
+ unified: "other",
255
+ raw: undefined,
256
+ };
220
257
  try {
221
258
  if (this.model.stream) {
222
- for await (const event of this.model.stream(req)) {
223
- if (event.kind === "finish") {
224
- usage = event.usage;
225
- finishReason = event.finishReason;
259
+ for await (const e of this.model.stream(req)) {
260
+ if (e.kind === "finish") {
261
+ usage = e.usage;
262
+ finishReason = e.finishReason;
226
263
  }
227
- yield event;
264
+ if (notDelta(e))
265
+ content.push(e);
266
+ yield e;
228
267
  }
229
268
  }
230
269
  else {
@@ -232,19 +271,31 @@ export class Thread {
232
271
  const res = await this.model.generate(req);
233
272
  usage = res.usage;
234
273
  finishReason = res.finishReason;
235
- for (const event of res.content) {
236
- yield event;
274
+ for (const e of res.content) {
275
+ content.push(e);
276
+ yield e;
237
277
  }
238
278
  }
279
+ s.record({
280
+ response: {
281
+ content,
282
+ finishReason,
283
+ usage,
284
+ },
285
+ });
239
286
  this.emit("model.call.end", { finishReason, usage });
240
287
  }
241
288
  catch (error) {
289
+ s.error(error instanceof Error ? error : new Error(String(error)));
242
290
  this.emit("model.call.end", { finishReason: "error" });
243
291
  yield {
244
292
  kind: "error",
245
293
  error: error instanceof Error ? error : new Error(String(error)),
246
294
  };
247
295
  }
296
+ finally {
297
+ s.close();
298
+ }
248
299
  }
249
300
  /**
250
301
  * Persist current thread state to storage.
@@ -309,12 +360,12 @@ export class Thread {
309
360
  return events;
310
361
  }
311
362
  /**
312
- * Cancel the running thread
363
+ * Abort the running thread.
313
364
  *
314
- * TODO: Emit thread.stop when cancelled (neither result nor error set)
365
+ * @throws {Error} Not implemented - use AbortSignal via options instead
315
366
  */
316
- cancel() {
317
- this.abort?.abort();
367
+ abort() {
368
+ throw new Error("Not implemented: use AbortSignal via ThreadExecuteOptions");
318
369
  }
319
370
  /**
320
371
  * Emit an agent event with common fields auto-filled.
@@ -384,6 +435,14 @@ export class Thread {
384
435
  async executeTools(calls) {
385
436
  return await Promise.all(calls.map(async (call) => {
386
437
  const parsedArgs = JSON.parse(call.arguments || "{}");
438
+ // create tool.call span
439
+ const s = span({
440
+ kind: "tool.call",
441
+ toolId: call.toolId,
442
+ callId: call.callId,
443
+ args: parsedArgs,
444
+ }, this._span.id);
445
+ s.enter();
387
446
  this.emit("tool.call.start", {
388
447
  toolId: call.toolId,
389
448
  callId: call.callId,
@@ -402,13 +461,16 @@ export class Thread {
402
461
  ctx.agent = this.agent;
403
462
  ctx.approve(call.callId); // mark this call as approved
404
463
  const res = await tool.invoke(ctx, call.arguments, call.callId);
464
+ s.record({
465
+ state: res.state,
466
+ result: res.result,
467
+ error: res.error,
468
+ });
405
469
  this.emit("tool.call.end", {
406
470
  toolId: call.toolId,
407
471
  callId: call.callId,
408
472
  state: res.state,
409
- result: typeof res.result === "string"
410
- ? res.result
411
- : JSON.stringify(res.result),
473
+ result: res.result,
412
474
  error: res.error,
413
475
  });
414
476
  return {
@@ -421,11 +483,14 @@ export class Thread {
421
483
  };
422
484
  }
423
485
  catch (error) {
486
+ const errMsg = error instanceof Error ? error.message : String(error);
487
+ s.error(error instanceof Error ? error : new Error(errMsg));
488
+ s.record({ state: "failed", error: errMsg });
424
489
  this.emit("tool.call.end", {
425
490
  toolId: call.toolId,
426
491
  callId: call.callId,
427
492
  state: FAILED,
428
- error: error instanceof Error ? error.message : String(error),
493
+ error: errMsg,
429
494
  });
430
495
  return {
431
496
  kind: "tool.result",
@@ -433,9 +498,12 @@ export class Thread {
433
498
  toolId: call.toolId,
434
499
  state: FAILED,
435
500
  result: undefined,
436
- error: error instanceof Error ? error.message : String(error),
501
+ error: errMsg,
437
502
  };
438
503
  }
504
+ finally {
505
+ s.close();
506
+ }
439
507
  }));
440
508
  }
441
509
  /**
@@ -479,6 +547,7 @@ export class Thread {
479
547
  settings,
480
548
  tools,
481
549
  responseType,
550
+ abort: this._abort,
482
551
  };
483
552
  }
484
553
  }
@@ -134,6 +134,11 @@ export interface ThreadOptions<TContext = unknown, TOutput extends AgentOutputTy
134
134
  * hydrating from a store. Callers creating new threads should omit it.
135
135
  */
136
136
  persisted?: boolean;
137
+ /**
138
+ * Abort signal for cancelling thread execution.
139
+ * When aborted, the thread will stop at the next safe point.
140
+ */
141
+ abort?: AbortSignal;
137
142
  }
138
143
  /**
139
144
  * Options passed to agent.execute() and agent.stream().
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/thread/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/thread/types.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Context, UnknownContext } from "../context.js";
2
2
  import { type LanguageModelTool } from "@kernl-sdk/protocol";
3
- import type { ToolConfig, ToolApprovalFunction, ToolEnabledFunction, ToolErrorFunction, ToolInputParameters, ToolResult } from "./types.js";
3
+ import type { ToolConfig, ToolApprovalFunction, ToolEnabledFunction, ToolErrorFunction, ToolExecuteFunction, ToolInputParameters, ToolResult } from "./types.js";
4
4
  /**
5
5
  * Exposes a function to the agent as a tool to be called
6
6
  *
@@ -42,7 +42,7 @@ export declare class FunctionTool<TContext = UnknownContext, TParameters extends
42
42
  readonly description: string;
43
43
  readonly parameters?: TParameters;
44
44
  readonly mode: "blocking" | "async";
45
- private execute;
45
+ execute: ToolExecuteFunction<TContext, TParameters, TResult>;
46
46
  errorfn: ToolErrorFunction | null;
47
47
  requiresApproval: ToolApprovalFunction<TParameters>;
48
48
  isEnabled: ToolEnabledFunction<TContext>;
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool/tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool/tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=composite.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite.test.d.ts","sourceRoot":"","sources":["../../../src/tracing/__tests__/composite.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,146 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { CompositeSubscriber } from "../subscribers/composite.js";
3
+ import { TestSubscriber } from "./helpers.js";
4
+ describe("CompositeSubscriber", () => {
5
+ let sub1;
6
+ let sub2;
7
+ let composite;
8
+ beforeEach(() => {
9
+ sub1 = new TestSubscriber();
10
+ sub2 = new TestSubscriber();
11
+ composite = new CompositeSubscriber([sub1, sub2]);
12
+ });
13
+ describe("enabled", () => {
14
+ it("should return true if any subscriber is enabled", () => {
15
+ sub1.enabledKinds = new Set(["thread"]);
16
+ sub2.enabledKinds = new Set(["model.call"]);
17
+ expect(composite.enabled({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" })).toBe(true);
18
+ expect(composite.enabled({ kind: "model.call", provider: "test", modelId: "m1" })).toBe(true);
19
+ });
20
+ it("should return false if no subscriber is enabled", () => {
21
+ sub1.enabledKinds = new Set(["thread"]);
22
+ sub2.enabledKinds = new Set(["thread"]);
23
+ expect(composite.enabled({ kind: "model.call", provider: "test", modelId: "m1" })).toBe(false);
24
+ });
25
+ it("should return true if all subscribers are enabled (default)", () => {
26
+ expect(composite.enabled({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" })).toBe(true);
27
+ });
28
+ });
29
+ describe("span", () => {
30
+ it("should create span in all enabled subscribers", () => {
31
+ const data = { kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" };
32
+ composite.span(data, null);
33
+ expect(sub1.spans.size).toBe(1);
34
+ expect(sub2.spans.size).toBe(1);
35
+ const [, s1] = [...sub1.spans.entries()][0];
36
+ const [, s2] = [...sub2.spans.entries()][0];
37
+ expect(s1.data).toEqual(data);
38
+ expect(s2.data).toEqual(data);
39
+ });
40
+ it("should return composite span ID", () => {
41
+ const id = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
42
+ expect(id).toMatch(/^composite_\d+$/);
43
+ });
44
+ it("should skip disabled subscribers", () => {
45
+ sub1.enabledKinds = new Set(["thread"]);
46
+ sub2.enabledKinds = new Set(["model.call"]); // not enabled for thread
47
+ composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
48
+ expect(sub1.spans.size).toBe(1);
49
+ expect(sub2.spans.size).toBe(0);
50
+ });
51
+ it("should map parent span IDs correctly", () => {
52
+ // Create parent
53
+ const parentId = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
54
+ // Create child with parent
55
+ composite.span({ kind: "model.call", provider: "test", modelId: "m1" }, parentId);
56
+ // Each subscriber should have correct parent mapping
57
+ const threadSpan1 = sub1.spansOfKind("thread")[0];
58
+ const modelSpan1 = sub1.spansOfKind("model.call")[0];
59
+ expect(modelSpan1.parent).toBe(threadSpan1.id);
60
+ const threadSpan2 = sub2.spansOfKind("thread")[0];
61
+ const modelSpan2 = sub2.spansOfKind("model.call")[0];
62
+ expect(modelSpan2.parent).toBe(threadSpan2.id);
63
+ });
64
+ });
65
+ describe("enter / exit", () => {
66
+ it("should dispatch to all subscribers", () => {
67
+ const spanId = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
68
+ composite.enter(spanId);
69
+ expect(sub1.entered.size).toBe(1);
70
+ expect(sub2.entered.size).toBe(1);
71
+ composite.exit(spanId);
72
+ expect(sub1.exited.size).toBe(1);
73
+ expect(sub2.exited.size).toBe(1);
74
+ });
75
+ it("should skip disabled subscribers", () => {
76
+ sub2.enabledKinds = new Set(["model.call"]); // not enabled for thread
77
+ const spanId = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
78
+ composite.enter(spanId);
79
+ expect(sub1.entered.size).toBe(1);
80
+ expect(sub2.entered.size).toBe(0);
81
+ });
82
+ });
83
+ describe("record", () => {
84
+ it("should dispatch to all subscribers", () => {
85
+ const spanId = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
86
+ composite.record(spanId, { state: "running" });
87
+ const sub1SpanId = [...sub1.spans.keys()][0];
88
+ const sub2SpanId = [...sub2.spans.keys()][0];
89
+ expect(sub1.getRecorded(sub1SpanId)).toHaveLength(1);
90
+ expect(sub2.getRecorded(sub2SpanId)).toHaveLength(1);
91
+ });
92
+ });
93
+ describe("error", () => {
94
+ it("should dispatch to all subscribers", () => {
95
+ const spanId = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
96
+ const err = new Error("test");
97
+ composite.error(spanId, err);
98
+ const sub1SpanId = [...sub1.spans.keys()][0];
99
+ const sub2SpanId = [...sub2.spans.keys()][0];
100
+ expect(sub1.errors.get(sub1SpanId)).toHaveLength(1);
101
+ expect(sub2.errors.get(sub2SpanId)).toHaveLength(1);
102
+ });
103
+ });
104
+ describe("close", () => {
105
+ it("should dispatch to all subscribers and clean up", () => {
106
+ const spanId = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
107
+ composite.close(spanId);
108
+ expect(sub1.closed.size).toBe(1);
109
+ expect(sub2.closed.size).toBe(1);
110
+ });
111
+ });
112
+ describe("event", () => {
113
+ it("should dispatch to all subscribers", () => {
114
+ composite.event({ kind: "thread.error", message: "test" }, null);
115
+ expect(sub1.events).toHaveLength(1);
116
+ expect(sub2.events).toHaveLength(1);
117
+ });
118
+ it("should map parent span IDs correctly", () => {
119
+ const spanId = composite.span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
120
+ composite.event({ kind: "thread.error", message: "test" }, spanId);
121
+ const sub1SpanId = [...sub1.spans.keys()][0];
122
+ const sub2SpanId = [...sub2.spans.keys()][0];
123
+ expect(sub1.events[0].parent).toBe(sub1SpanId);
124
+ expect(sub2.events[0].parent).toBe(sub2SpanId);
125
+ });
126
+ it("should handle null parent", () => {
127
+ composite.event({ kind: "thread.error", message: "test" }, null);
128
+ expect(sub1.events[0].parent).toBeNull();
129
+ expect(sub2.events[0].parent).toBeNull();
130
+ });
131
+ });
132
+ describe("flush", () => {
133
+ it("should flush all subscribers", async () => {
134
+ await composite.flush();
135
+ expect(sub1.calls.some((c) => c.method === "flush")).toBe(true);
136
+ expect(sub2.calls.some((c) => c.method === "flush")).toBe(true);
137
+ });
138
+ });
139
+ describe("shutdown", () => {
140
+ it("should shutdown all subscribers", async () => {
141
+ await composite.shutdown(5000);
142
+ expect(sub1.calls.some((c) => c.method === "shutdown")).toBe(true);
143
+ expect(sub2.calls.some((c) => c.method === "shutdown")).toBe(true);
144
+ });
145
+ });
146
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dispatch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.test.d.ts","sourceRoot":"","sources":["../../../src/tracing/__tests__/dispatch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,160 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { span, event, run, current, setSubscriber, clearSubscriber, getSubscriber, } from "../dispatch.js";
3
+ import { NoopSpan, SpanImpl } from "../span.js";
4
+ import { TestSubscriber } from "./helpers.js";
5
+ describe("dispatch", () => {
6
+ let subscriber;
7
+ beforeEach(() => {
8
+ subscriber = new TestSubscriber();
9
+ });
10
+ afterEach(() => {
11
+ clearSubscriber();
12
+ });
13
+ describe("setSubscriber / clearSubscriber / getSubscriber", () => {
14
+ it("should set and get subscriber", () => {
15
+ expect(getSubscriber()).toBeNull();
16
+ setSubscriber(subscriber);
17
+ expect(getSubscriber()).toBe(subscriber);
18
+ });
19
+ it("should throw if subscriber already set", () => {
20
+ setSubscriber(subscriber);
21
+ expect(() => setSubscriber(new TestSubscriber())).toThrow("Global subscriber already set");
22
+ });
23
+ it("should allow re-setting after clear", () => {
24
+ setSubscriber(subscriber);
25
+ clearSubscriber();
26
+ expect(getSubscriber()).toBeNull();
27
+ const newSubscriber = new TestSubscriber();
28
+ setSubscriber(newSubscriber);
29
+ expect(getSubscriber()).toBe(newSubscriber);
30
+ });
31
+ });
32
+ describe("span", () => {
33
+ it("should return NoopSpan when no subscriber is set", () => {
34
+ const s = span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" });
35
+ expect(s).toBeInstanceOf(NoopSpan);
36
+ expect(s.noop()).toBe(true);
37
+ });
38
+ it("should return NoopSpan when subscriber.enabled returns false", () => {
39
+ subscriber.enabledKinds = new Set(["model.call"]); // only model.call enabled
40
+ setSubscriber(subscriber);
41
+ const s = span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" });
42
+ expect(s).toBeInstanceOf(NoopSpan);
43
+ expect(subscriber.spans.size).toBe(0);
44
+ });
45
+ it("should return SpanImpl when subscriber is set and enabled", () => {
46
+ setSubscriber(subscriber);
47
+ const s = span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" });
48
+ expect(s).toBeInstanceOf(SpanImpl);
49
+ expect(s.noop()).toBe(false);
50
+ expect(s.id).toBeDefined();
51
+ });
52
+ it("should pass span data to subscriber", () => {
53
+ setSubscriber(subscriber);
54
+ const data = { kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" };
55
+ span(data);
56
+ expect(subscriber.spans.size).toBe(1);
57
+ const [, captured] = [...subscriber.spans.entries()][0];
58
+ expect(captured.data).toEqual(data);
59
+ });
60
+ it("should use null parent when parent=null", () => {
61
+ setSubscriber(subscriber);
62
+ span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
63
+ const [, captured] = [...subscriber.spans.entries()][0];
64
+ expect(captured.parent).toBeNull();
65
+ });
66
+ it("should use explicit parent when provided", () => {
67
+ setSubscriber(subscriber);
68
+ span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, "parent_123");
69
+ const [, captured] = [...subscriber.spans.entries()][0];
70
+ expect(captured.parent).toBe("parent_123");
71
+ });
72
+ it("should resolve parent from context when parent='current' (default)", () => {
73
+ setSubscriber(subscriber);
74
+ // Create parent span
75
+ const parentSpan = span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" }, null);
76
+ // Create child span within run() context
77
+ run(parentSpan.id, () => {
78
+ span({ kind: "model.call", provider: "test", modelId: "m1" });
79
+ });
80
+ const modelSpans = subscriber.spansOfKind("model.call");
81
+ expect(modelSpans).toHaveLength(1);
82
+ expect(modelSpans[0].parent).toBe(parentSpan.id);
83
+ });
84
+ it("should use null parent when parent='current' but no context", () => {
85
+ setSubscriber(subscriber);
86
+ // No run() context
87
+ span({ kind: "thread", threadId: "t1", agentId: "a1", namespace: "ns" });
88
+ const [, captured] = [...subscriber.spans.entries()][0];
89
+ expect(captured.parent).toBeNull();
90
+ });
91
+ });
92
+ describe("run / current", () => {
93
+ it("should return null when no context", () => {
94
+ expect(current()).toBeNull();
95
+ });
96
+ it("should return spanId within run context", () => {
97
+ const result = run("span_123", () => {
98
+ return current();
99
+ });
100
+ expect(result).toBe("span_123");
101
+ });
102
+ it("should return to null after run completes", () => {
103
+ run("span_123", () => {
104
+ expect(current()).toBe("span_123");
105
+ });
106
+ expect(current()).toBeNull();
107
+ });
108
+ it("should support nested run contexts", () => {
109
+ const results = [];
110
+ run("outer", () => {
111
+ results.push(current());
112
+ run("inner", () => {
113
+ results.push(current());
114
+ });
115
+ results.push(current());
116
+ });
117
+ expect(results).toEqual(["outer", "inner", "outer"]);
118
+ });
119
+ it("should handle null spanId in run", () => {
120
+ run("span_123", () => {
121
+ run(null, () => {
122
+ expect(current()).toBeNull();
123
+ });
124
+ });
125
+ });
126
+ });
127
+ describe("event", () => {
128
+ it("should do nothing when no subscriber is set", () => {
129
+ // Should not throw
130
+ event({ kind: "thread.error", message: "test" });
131
+ });
132
+ it("should emit event to subscriber", () => {
133
+ setSubscriber(subscriber);
134
+ event({ kind: "thread.error", message: "test error", stack: "stack" });
135
+ expect(subscriber.events).toHaveLength(1);
136
+ expect(subscriber.events[0].data).toEqual({
137
+ kind: "thread.error",
138
+ message: "test error",
139
+ stack: "stack",
140
+ });
141
+ });
142
+ it("should use null parent when parent=null", () => {
143
+ setSubscriber(subscriber);
144
+ event({ kind: "thread.error", message: "test" }, null);
145
+ expect(subscriber.events[0].parent).toBeNull();
146
+ });
147
+ it("should use explicit parent when provided", () => {
148
+ setSubscriber(subscriber);
149
+ event({ kind: "thread.error", message: "test" }, "parent_123");
150
+ expect(subscriber.events[0].parent).toBe("parent_123");
151
+ });
152
+ it("should resolve parent from context when parent='current' (default)", () => {
153
+ setSubscriber(subscriber);
154
+ run("span_123", () => {
155
+ event({ kind: "thread.error", message: "test" });
156
+ });
157
+ expect(subscriber.events[0].parent).toBe("span_123");
158
+ });
159
+ });
160
+ });