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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +18 -0
- package/README.md +29 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +2 -0
- package/dist/kernl/kernl.d.ts +6 -0
- package/dist/kernl/kernl.d.ts.map +1 -1
- package/dist/kernl/kernl.js +19 -0
- package/dist/kernl/types.d.ts +6 -0
- package/dist/kernl/types.d.ts.map +1 -1
- package/dist/lib/env.d.ts +2 -2
- package/dist/mcp/http.d.ts.map +1 -1
- package/dist/mcp/http.js +1 -5
- package/dist/mcp/sse.d.ts.map +1 -1
- package/dist/mcp/sse.js +1 -5
- package/dist/mcp/stdio.d.ts.map +1 -1
- package/dist/mcp/stdio.js +1 -5
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +0 -1
- package/dist/thread/__tests__/thread.test.js +241 -0
- package/dist/thread/thread.d.ts +5 -4
- package/dist/thread/thread.d.ts.map +1 -1
- package/dist/thread/thread.js +91 -22
- package/dist/thread/types.d.ts +5 -0
- package/dist/thread/types.d.ts.map +1 -1
- package/dist/tool/tool.d.ts +2 -2
- package/dist/tool/tool.d.ts.map +1 -1
- package/dist/tracing/__tests__/composite.test.d.ts +2 -0
- package/dist/tracing/__tests__/composite.test.d.ts.map +1 -0
- package/dist/tracing/__tests__/composite.test.js +146 -0
- package/dist/tracing/__tests__/dispatch.test.d.ts +2 -0
- package/dist/tracing/__tests__/dispatch.test.d.ts.map +1 -0
- package/dist/tracing/__tests__/dispatch.test.js +160 -0
- package/dist/tracing/__tests__/helpers.d.ts +69 -0
- package/dist/tracing/__tests__/helpers.d.ts.map +1 -0
- package/dist/tracing/__tests__/helpers.js +109 -0
- package/dist/tracing/__tests__/integration.test.d.ts +2 -0
- package/dist/tracing/__tests__/integration.test.d.ts.map +1 -0
- package/dist/tracing/__tests__/integration.test.js +675 -0
- package/dist/tracing/__tests__/span.test.d.ts +2 -0
- package/dist/tracing/__tests__/span.test.d.ts.map +1 -0
- package/dist/tracing/__tests__/span.test.js +188 -0
- package/dist/tracing/dispatch.d.ts +43 -0
- package/dist/tracing/dispatch.d.ts.map +1 -0
- package/dist/tracing/dispatch.js +70 -0
- package/dist/tracing/index.d.ts +8 -0
- package/dist/tracing/index.d.ts.map +1 -0
- package/dist/tracing/index.js +6 -0
- package/dist/tracing/span.d.ts +69 -0
- package/dist/tracing/span.d.ts.map +1 -0
- package/dist/tracing/span.js +64 -0
- package/dist/tracing/subscriber.d.ts +53 -0
- package/dist/tracing/subscriber.d.ts.map +1 -0
- package/dist/tracing/subscriber.js +1 -0
- package/dist/tracing/subscribers/composite.d.ts +26 -0
- package/dist/tracing/subscribers/composite.d.ts.map +1 -0
- package/dist/tracing/subscribers/composite.js +96 -0
- package/dist/tracing/subscribers/console.d.ts +22 -0
- package/dist/tracing/subscribers/console.d.ts.map +1 -0
- package/dist/tracing/subscribers/console.js +82 -0
- package/dist/tracing/types.d.ts +77 -0
- package/dist/tracing/types.d.ts.map +1 -0
- package/dist/tracing/types.js +1 -0
- package/package.json +6 -2
- package/src/agent.ts +2 -0
- package/src/index.ts +1 -0
- package/src/kernl/kernl.ts +21 -0
- package/src/kernl/types.ts +7 -0
- package/src/mcp/http.ts +1 -9
- package/src/mcp/sse.ts +1 -10
- package/src/mcp/stdio.ts +1 -10
- package/src/task.ts +0 -1
- package/src/thread/__tests__/thread.test.ts +280 -0
- package/src/thread/thread.ts +111 -24
- package/src/thread/types.ts +5 -0
- package/src/tool/tool.ts +1 -1
- package/src/tracing/__tests__/composite.test.ts +218 -0
- package/src/tracing/__tests__/dispatch.test.ts +222 -0
- package/src/tracing/__tests__/helpers.ts +138 -0
- package/src/tracing/__tests__/integration.test.ts +808 -0
- package/src/tracing/__tests__/span.test.ts +250 -0
- package/src/tracing/dispatch.ts +114 -0
- package/src/tracing/index.ts +39 -0
- package/src/tracing/span.ts +115 -0
- package/src/tracing/subscriber.ts +62 -0
- package/src/tracing/subscribers/composite.ts +102 -0
- package/src/tracing/subscribers/console.ts +101 -0
- package/src/tracing/types.ts +115 -0
- package/dist/trace/processor.d.ts +0 -1
- package/dist/trace/processor.d.ts.map +0 -1
- package/dist/trace/processor.js +0 -1
- package/dist/trace/traces.d.ts +0 -1
- package/dist/trace/traces.d.ts.map +0 -1
- package/dist/trace/traces.js +0 -73
- package/dist/trace/utils.d.ts +0 -22
- package/dist/trace/utils.d.ts.map +0 -1
- package/dist/trace/utils.js +0 -30
- package/src/trace/processor.ts +0 -0
- package/src/trace/traces.ts +0 -86
- package/src/trace/utils.ts +0 -38
package/dist/thread/thread.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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 = {
|
|
253
|
+
let finishReason = {
|
|
254
|
+
unified: "other",
|
|
255
|
+
raw: undefined,
|
|
256
|
+
};
|
|
220
257
|
try {
|
|
221
258
|
if (this.model.stream) {
|
|
222
|
-
for await (const
|
|
223
|
-
if (
|
|
224
|
-
usage =
|
|
225
|
-
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
|
-
|
|
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
|
|
236
|
-
|
|
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
|
-
*
|
|
363
|
+
* Abort the running thread.
|
|
313
364
|
*
|
|
314
|
-
*
|
|
365
|
+
* @throws {Error} Not implemented - use AbortSignal via options instead
|
|
315
366
|
*/
|
|
316
|
-
|
|
317
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
}
|
package/dist/thread/types.d.ts
CHANGED
|
@@ -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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"}
|
package/dist/tool/tool.d.ts
CHANGED
|
@@ -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
|
-
|
|
45
|
+
execute: ToolExecuteFunction<TContext, TParameters, TResult>;
|
|
46
46
|
errorfn: ToolErrorFunction | null;
|
|
47
47
|
requiresApproval: ToolApprovalFunction<TParameters>;
|
|
48
48
|
isEnabled: ToolEnabledFunction<TContext>;
|
package/dist/tool/tool.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool/tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMpD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,mBAAmB,EAEnB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tool/tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 @@
|
|
|
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
|
+
});
|