kernl 0.12.4 → 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 (95) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -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/thread.d.ts +5 -4
  21. package/dist/thread/thread.d.ts.map +1 -1
  22. package/dist/thread/thread.js +91 -22
  23. package/dist/thread/types.d.ts +5 -0
  24. package/dist/thread/types.d.ts.map +1 -1
  25. package/dist/tracing/__tests__/composite.test.d.ts +2 -0
  26. package/dist/tracing/__tests__/composite.test.d.ts.map +1 -0
  27. package/dist/tracing/__tests__/composite.test.js +146 -0
  28. package/dist/tracing/__tests__/dispatch.test.d.ts +2 -0
  29. package/dist/tracing/__tests__/dispatch.test.d.ts.map +1 -0
  30. package/dist/tracing/__tests__/dispatch.test.js +160 -0
  31. package/dist/tracing/__tests__/helpers.d.ts +69 -0
  32. package/dist/tracing/__tests__/helpers.d.ts.map +1 -0
  33. package/dist/tracing/__tests__/helpers.js +109 -0
  34. package/dist/tracing/__tests__/integration.test.d.ts +2 -0
  35. package/dist/tracing/__tests__/integration.test.d.ts.map +1 -0
  36. package/dist/tracing/__tests__/integration.test.js +675 -0
  37. package/dist/tracing/__tests__/span.test.d.ts +2 -0
  38. package/dist/tracing/__tests__/span.test.d.ts.map +1 -0
  39. package/dist/tracing/__tests__/span.test.js +188 -0
  40. package/dist/tracing/dispatch.d.ts +43 -0
  41. package/dist/tracing/dispatch.d.ts.map +1 -0
  42. package/dist/tracing/dispatch.js +70 -0
  43. package/dist/tracing/index.d.ts +8 -0
  44. package/dist/tracing/index.d.ts.map +1 -0
  45. package/dist/tracing/index.js +6 -0
  46. package/dist/tracing/span.d.ts +69 -0
  47. package/dist/tracing/span.d.ts.map +1 -0
  48. package/dist/tracing/span.js +64 -0
  49. package/dist/tracing/subscriber.d.ts +53 -0
  50. package/dist/tracing/subscriber.d.ts.map +1 -0
  51. package/dist/tracing/subscriber.js +1 -0
  52. package/dist/tracing/subscribers/composite.d.ts +26 -0
  53. package/dist/tracing/subscribers/composite.d.ts.map +1 -0
  54. package/dist/tracing/subscribers/composite.js +96 -0
  55. package/dist/tracing/subscribers/console.d.ts +22 -0
  56. package/dist/tracing/subscribers/console.d.ts.map +1 -0
  57. package/dist/tracing/subscribers/console.js +82 -0
  58. package/dist/tracing/types.d.ts +77 -0
  59. package/dist/tracing/types.d.ts.map +1 -0
  60. package/dist/tracing/types.js +1 -0
  61. package/package.json +5 -1
  62. package/src/agent.ts +2 -0
  63. package/src/index.ts +1 -0
  64. package/src/kernl/kernl.ts +21 -0
  65. package/src/kernl/types.ts +7 -0
  66. package/src/mcp/http.ts +1 -9
  67. package/src/mcp/sse.ts +1 -10
  68. package/src/mcp/stdio.ts +1 -10
  69. package/src/task.ts +0 -1
  70. package/src/thread/thread.ts +111 -24
  71. package/src/thread/types.ts +5 -0
  72. package/src/tracing/__tests__/composite.test.ts +218 -0
  73. package/src/tracing/__tests__/dispatch.test.ts +222 -0
  74. package/src/tracing/__tests__/helpers.ts +138 -0
  75. package/src/tracing/__tests__/integration.test.ts +808 -0
  76. package/src/tracing/__tests__/span.test.ts +250 -0
  77. package/src/tracing/dispatch.ts +114 -0
  78. package/src/tracing/index.ts +39 -0
  79. package/src/tracing/span.ts +115 -0
  80. package/src/tracing/subscriber.ts +62 -0
  81. package/src/tracing/subscribers/composite.ts +102 -0
  82. package/src/tracing/subscribers/console.ts +101 -0
  83. package/src/tracing/types.ts +115 -0
  84. package/dist/trace/processor.d.ts +0 -1
  85. package/dist/trace/processor.d.ts.map +0 -1
  86. package/dist/trace/processor.js +0 -1
  87. package/dist/trace/traces.d.ts +0 -1
  88. package/dist/trace/traces.d.ts.map +0 -1
  89. package/dist/trace/traces.js +0 -73
  90. package/dist/trace/utils.d.ts +0 -22
  91. package/dist/trace/utils.d.ts.map +0 -1
  92. package/dist/trace/utils.js +0 -30
  93. package/src/trace/processor.ts +0 -0
  94. package/src/trace/traces.ts +0 -86
  95. package/src/trace/utils.ts +0 -38
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../../src/tracing/subscribers/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAShD;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,UAAU;IAClD,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,MAAM,CAAK;IAEnB,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAIjC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAYnD,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAO1B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI;IAQtD,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAOzC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAS3B,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAI7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,OAAO,CAAC,GAAG;CAeZ"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * A simple console subscriber for development and debugging.
3
+ * Logs span lifecycle events and events to the console.
4
+ */
5
+ export class ConsoleSubscriber {
6
+ spans = new Map();
7
+ nextId = 0;
8
+ enabled(_data) {
9
+ return true;
10
+ }
11
+ span(data, parent) {
12
+ const id = `span_${this.nextId++}`;
13
+ this.spans.set(id, {
14
+ data,
15
+ parent,
16
+ startTime: Date.now(),
17
+ entered: false,
18
+ });
19
+ console.log(`[span:new] ${id} ${data.kind}`, this.fmt(data));
20
+ return id;
21
+ }
22
+ enter(spanId) {
23
+ const record = this.spans.get(spanId);
24
+ if (record) {
25
+ record.entered = true;
26
+ console.log(`[span:enter] ${spanId} ${record.data.kind}`);
27
+ }
28
+ }
29
+ exit(spanId) {
30
+ const record = this.spans.get(spanId);
31
+ if (record) {
32
+ console.log(`[span:exit] ${spanId} ${record.data.kind}`);
33
+ }
34
+ }
35
+ record(spanId, delta) {
36
+ const record = this.spans.get(spanId);
37
+ if (record) {
38
+ Object.assign(record.data, delta);
39
+ console.log(`[span:record] ${spanId}`, this.fmt(delta));
40
+ }
41
+ }
42
+ error(spanId, error) {
43
+ const record = this.spans.get(spanId);
44
+ if (record) {
45
+ console.log(`[span:error] ${spanId} ${record.data.kind}`, error.message);
46
+ }
47
+ }
48
+ close(spanId) {
49
+ const record = this.spans.get(spanId);
50
+ if (record) {
51
+ const duration = Date.now() - record.startTime;
52
+ console.log(`[span:close] ${spanId} ${record.data.kind} (${duration}ms)`);
53
+ this.spans.delete(spanId);
54
+ }
55
+ }
56
+ event(data, parent) {
57
+ console.log(`[event] ${data.kind}`, { ...data, parent });
58
+ }
59
+ async flush() {
60
+ // Console subscriber doesn't buffer
61
+ }
62
+ async shutdown(_timeout) {
63
+ this.spans.clear();
64
+ }
65
+ fmt(data) {
66
+ const { kind, ...rest } = data;
67
+ // Truncate large fields for readability
68
+ const formatted = {};
69
+ for (const [key, value] of Object.entries(rest)) {
70
+ if (typeof value === "string" && value.length > 100) {
71
+ formatted[key] = value.slice(0, 100) + "...";
72
+ }
73
+ else if (Array.isArray(value) && value.length > 3) {
74
+ formatted[key] = `[${value.length} items]`;
75
+ }
76
+ else {
77
+ formatted[key] = value;
78
+ }
79
+ }
80
+ return formatted;
81
+ }
82
+ }
@@ -0,0 +1,77 @@
1
+ import type { LanguageModelItem, LanguageModelTool, LanguageModelUsage, LanguageModelFinishReason, LanguageModelResponseItem, LanguageModelRequestSettings, LanguageModelResponseType, SharedWarning, ToolCallState } from "@kernl-sdk/protocol";
2
+ import { ThreadState } from "../internal.js";
3
+ export type SpanId = string;
4
+ export interface ThreadSpan {
5
+ kind: "thread";
6
+ threadId: string;
7
+ agentId: string;
8
+ namespace: string;
9
+ context?: unknown;
10
+ state?: ThreadState;
11
+ result?: unknown;
12
+ error?: string;
13
+ }
14
+ export interface ModelCallSpan {
15
+ kind: "model.call";
16
+ provider: string;
17
+ modelId: string;
18
+ request?: {
19
+ input: LanguageModelItem[];
20
+ settings?: LanguageModelRequestSettings;
21
+ responseType?: LanguageModelResponseType;
22
+ tools?: LanguageModelTool[];
23
+ };
24
+ response?: {
25
+ content: LanguageModelResponseItem[];
26
+ finishReason: LanguageModelFinishReason;
27
+ usage?: LanguageModelUsage;
28
+ warnings?: SharedWarning[];
29
+ };
30
+ }
31
+ export interface ToolCallSpan {
32
+ kind: "tool.call";
33
+ toolId: string;
34
+ callId: string;
35
+ args?: Record<string, unknown>;
36
+ state?: ToolCallState;
37
+ result?: string;
38
+ error?: string | null;
39
+ }
40
+ export type SpanData = ThreadSpan | ModelCallSpan | ToolCallSpan;
41
+ export type SpanKind = SpanData["kind"];
42
+ export interface ThreadErrorEvent {
43
+ kind: "thread.error";
44
+ message: string;
45
+ stack?: string;
46
+ }
47
+ export interface ThreadAbortedEvent {
48
+ kind: "thread.aborted";
49
+ reason?: string;
50
+ }
51
+ export interface ThreadGuardrailTriggeredEvent {
52
+ kind: "thread.guardrail_triggered";
53
+ name: string;
54
+ type: "input" | "output";
55
+ passed: boolean;
56
+ reason?: string;
57
+ }
58
+ export interface ToolApprovalRequestedEvent {
59
+ kind: "tool.approval_requested";
60
+ toolId: string;
61
+ callId: string;
62
+ }
63
+ export interface ToolApprovalGrantedEvent {
64
+ kind: "tool.approval_granted";
65
+ toolId: string;
66
+ callId: string;
67
+ approver?: string;
68
+ }
69
+ export interface ToolApprovalDeniedEvent {
70
+ kind: "tool.approval_denied";
71
+ toolId: string;
72
+ callId: string;
73
+ reason?: string;
74
+ }
75
+ export type EventData = ThreadErrorEvent | ThreadAbortedEvent | ThreadGuardrailTriggeredEvent | ToolApprovalRequestedEvent | ToolApprovalGrantedEvent | ToolApprovalDeniedEvent;
76
+ export type EventKind = EventData["kind"];
77
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tracing/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kernl",
3
- "version": "0.12.4",
3
+ "version": "0.12.6",
4
4
  "description": "A modern AI agent framework",
5
5
  "keywords": [
6
6
  "kernl",
@@ -29,6 +29,10 @@
29
29
  "./internal": {
30
30
  "types": "./dist/internal.d.ts",
31
31
  "import": "./dist/internal.js"
32
+ },
33
+ "./tracing": {
34
+ "types": "./dist/tracing/index.d.ts",
35
+ "import": "./dist/tracing/index.js"
32
36
  }
33
37
  },
34
38
  "dependencies": {
package/src/agent.ts CHANGED
@@ -115,6 +115,7 @@ export class Agent<
115
115
  tid: options?.threadId,
116
116
  namespace: options?.namespace,
117
117
  storage: this.kernl.storage?.threads,
118
+ abort: options?.abort,
118
119
  });
119
120
  return this.kernl.spawn(thread);
120
121
  }
@@ -186,6 +187,7 @@ export class Agent<
186
187
  tid: options?.threadId,
187
188
  namespace: options?.namespace,
188
189
  storage: this.kernl.storage?.threads,
190
+ abort: options?.abort,
189
191
  });
190
192
  yield* this.kernl.spawnStream(thread);
191
193
  return;
package/src/index.ts CHANGED
@@ -102,3 +102,4 @@ export type {
102
102
  MemoryByte,
103
103
  MemoryByteCodec,
104
104
  } from "./memory";
105
+
@@ -12,6 +12,7 @@ import {
12
12
  MemoryIndexHandle,
13
13
  buildMemoryIndexSchema,
14
14
  } from "@/memory";
15
+ import { setSubscriber, clearSubscriber, type Subscriber } from "@/tracing";
15
16
 
16
17
  import { logger } from "@/lib/logger";
17
18
  import { RThreads } from "@/api/resources/threads";
@@ -33,6 +34,7 @@ export class Kernl extends KernlHooks {
33
34
  private readonly _models: ModelRegistry;
34
35
  private readonly _memopts: MemoryOptions | undefined;
35
36
  private readonly _storopts: StorageOptions | undefined;
37
+ private readonly _subscriber: Subscriber | null = null;
36
38
 
37
39
  readonly storage: KernlStorage;
38
40
  athreads: Map<string, Thread<any, any>> = new Map(); /* active threads */
@@ -60,6 +62,12 @@ export class Kernl extends KernlHooks {
60
62
  this.threads = new RThreads(this.storage.threads);
61
63
  this.agents = new RAgents(this._agents);
62
64
  this.memories = this.initmem();
65
+
66
+ // wire tracing
67
+ if (options.tracer) {
68
+ this._subscriber = options.tracer;
69
+ setSubscriber(this._subscriber);
70
+ }
63
71
  }
64
72
 
65
73
  /**
@@ -196,4 +204,17 @@ export class Kernl extends KernlHooks {
196
204
  encoder,
197
205
  });
198
206
  }
207
+
208
+ /**
209
+ * Gracefully shutdown the Kernl instance.
210
+ * Flushes and shuts down tracing subscribers, closes storage connections.
211
+ */
212
+ async shutdown(timeout?: number): Promise<void> {
213
+ if (this._subscriber) {
214
+ await this._subscriber.flush();
215
+ await this._subscriber.shutdown(timeout);
216
+ clearSubscriber();
217
+ }
218
+ // TODO: close storage connections when storage supports it
219
+ }
199
220
  }
@@ -3,6 +3,7 @@ import { SearchIndex } from "@kernl-sdk/retrieval";
3
3
 
4
4
  import { BaseAgent } from "@/agent/base";
5
5
  import { KernlStorage } from "@/storage";
6
+ import type { Subscriber } from "@/tracing";
6
7
 
7
8
  /**
8
9
  * Storage configuration for Kernl.
@@ -82,6 +83,12 @@ export interface KernlOptions {
82
83
  * Memory system configuration.
83
84
  */
84
85
  memory?: MemoryOptions;
86
+
87
+ /**
88
+ * Tracer for observability. Receives span and event data.
89
+ * Use CompositeTracer to fan out to multiple backends.
90
+ */
91
+ tracer?: Subscriber;
85
92
  }
86
93
 
87
94
  /**
package/src/mcp/http.ts CHANGED
@@ -77,7 +77,6 @@ export class MCPServerStreamableHttp extends BaseMCPServer {
77
77
  throw e;
78
78
  }
79
79
 
80
- this.logger.debug(`Connected to MCP server: ${this.id}`);
81
80
  }
82
81
 
83
82
  /**
@@ -107,7 +106,6 @@ export class MCPServerStreamableHttp extends BaseMCPServer {
107
106
  }
108
107
 
109
108
  const response = await this.session.listTools();
110
- this.logger.debug(`Listed tools: ${JSON.stringify(response)}`);
111
109
  return ListToolsResultSchema.parse(response).tools;
112
110
  }
113
111
 
@@ -136,12 +134,6 @@ export class MCPServerStreamableHttp extends BaseMCPServer {
136
134
  );
137
135
 
138
136
  const parsed = CallToolResultSchema.parse(response);
139
- const result = parsed.content;
140
-
141
- this.logger.debug(
142
- `Called tool ${toolName} (args: ${JSON.stringify(args)}, result: ${JSON.stringify(result)})`,
143
- );
144
-
145
- return result as CallToolResultContent;
137
+ return parsed.content as CallToolResultContent;
146
138
  }
147
139
  }
package/src/mcp/sse.ts CHANGED
@@ -66,8 +66,6 @@ export class MCPServerSSE extends BaseMCPServer {
66
66
  await this.close();
67
67
  throw e;
68
68
  }
69
-
70
- this.logger.debug(`Connected to MCP server: ${this.id}`);
71
69
  }
72
70
 
73
71
  /**
@@ -83,7 +81,6 @@ export class MCPServerSSE extends BaseMCPServer {
83
81
  }
84
82
 
85
83
  const response = await this.session.listTools();
86
- this.logger.debug(`Listed tools: ${JSON.stringify(response)}`);
87
84
  return ListToolsResultSchema.parse(response).tools;
88
85
  }
89
86
 
@@ -112,13 +109,7 @@ export class MCPServerSSE extends BaseMCPServer {
112
109
  );
113
110
 
114
111
  const parsed = CallToolResultSchema.parse(response);
115
- const result = parsed.content;
116
-
117
- this.logger.debug(
118
- `Called tool ${toolName} (args: ${JSON.stringify(args)}, result: ${JSON.stringify(result)})`,
119
- );
120
-
121
- return result as CallToolResultContent;
112
+ return parsed.content as CallToolResultContent;
122
113
  }
123
114
 
124
115
  /**
package/src/mcp/stdio.ts CHANGED
@@ -62,8 +62,6 @@ export class MCPServerStdio extends BaseMCPServer {
62
62
  this.serverInitializeResult = {
63
63
  serverInfo: { name: this.id, version: "1.0.0" },
64
64
  } as InitializeResult;
65
-
66
- this.logger.debug(`Connected to MCP server: ${this.id}`);
67
65
  } catch (e) {
68
66
  this.logger.error("Error initializing MCP server:", e);
69
67
  await this.close();
@@ -82,7 +80,6 @@ export class MCPServerStdio extends BaseMCPServer {
82
80
  }
83
81
 
84
82
  const response = await this.session.listTools();
85
- this.logger.debug(`Listed tools: ${JSON.stringify(response)}`);
86
83
  return ListToolsResultSchema.parse(response).tools;
87
84
  }
88
85
 
@@ -111,13 +108,7 @@ export class MCPServerStdio extends BaseMCPServer {
111
108
  );
112
109
 
113
110
  const parsed = CallToolResultSchema.parse(response);
114
- const result = parsed.content;
115
-
116
- this.logger.debug(
117
- `Called tool ${toolName} (args: ${JSON.stringify(args)}, result: ${JSON.stringify(result)})`,
118
- );
119
-
120
- return result as CallToolResultContent;
111
+ return parsed.content as CallToolResultContent;
121
112
  }
122
113
 
123
114
  /**
package/src/task.ts CHANGED
@@ -8,7 +8,6 @@ import type { Thread } from "@/thread";
8
8
  */
9
9
  export class Task<TContext = UnknownContext, TResult = unknown> {
10
10
  id: string /* unique identifier for this task (pid) */;
11
- // tgid: string | null; /* task groupid */
12
11
  // prio: TaskPriority;
13
12
  instructions:
14
13
  | string
@@ -7,6 +7,14 @@ import { Context } from "@/context";
7
7
  import type { Task } from "@/task";
8
8
  import type { ResolvedAgentResponse } from "@/guardrail";
9
9
  import type { ThreadStore } from "@/storage";
10
+ import {
11
+ span,
12
+ event,
13
+ run,
14
+ type Span,
15
+ type ModelCallSpan,
16
+ type ToolCallSpan,
17
+ } from "@/tracing";
10
18
 
11
19
  import { logger } from "@/lib/logger";
12
20
 
@@ -22,6 +30,7 @@ import {
22
30
  LanguageModelStreamEvent,
23
31
  type LanguageModelUsage,
24
32
  type LanguageModelFinishReason,
33
+ type LanguageModelResponseItem,
25
34
  } from "@kernl-sdk/protocol";
26
35
  import { randomID, filter } from "@kernl-sdk/shared/lib";
27
36
 
@@ -111,8 +120,9 @@ export class Thread<
111
120
  private history: ThreadEvent[] /* history representing the event log for the thread */;
112
121
  private tickres?: ResolvedAgentResponse<TOutput>; /* final result from terminal tick */
113
122
 
114
- private abort?: AbortController;
123
+ private _abort?: AbortSignal;
115
124
  private storage?: ThreadStore;
125
+ private _span?: Span; /* tracing span for current execution */
116
126
 
117
127
  constructor(options: ThreadOptions<TContext, TOutput>) {
118
128
  this.tid = options.tid ?? `tid_${randomID()}`;
@@ -134,6 +144,7 @@ export class Thread<
134
144
  this.cpbuf = [];
135
145
  this.persisted = options.persisted ?? false;
136
146
  this.history = options.history ?? [];
147
+ this._abort = options.abort;
137
148
 
138
149
  // seek to latest seq (not persisted)
139
150
  if (this.history.length > 0) {
@@ -168,24 +179,42 @@ export class Thread<
168
179
  * - Exactly one thread.stop (with result on success, error on failure)
169
180
  */
170
181
  async *stream(): AsyncIterable<ThreadStreamEvent> {
171
- if (this.state === RUNNING && this.abort) {
182
+ if (this.state === RUNNING) {
172
183
  throw new Error("thread already running");
173
184
  }
174
185
 
175
186
  this.state = RUNNING;
176
- this.abort = new AbortController();
177
187
  this.tickres = undefined; // reset for this run
178
188
 
179
189
  await this.checkpoint(); /* c1: persist RUNNING state + initial input */
180
190
 
181
- this.emit("thread.start");
191
+ // create thread span (root span for this execution)
192
+ this._span = span(
193
+ {
194
+ kind: "thread",
195
+ threadId: this.tid,
196
+ agentId: this.agent.id,
197
+ namespace: this.namespace,
198
+ context: this.context.context,
199
+ },
200
+ null,
201
+ );
202
+ this._span.enter();
182
203
 
204
+ this.emit("thread.start");
183
205
  yield { kind: "stream.start" }; // always yield start immediately
184
206
 
185
207
  try {
186
208
  yield* this._execute();
209
+ this._span.record({ state: "stopped", result: this.tickres });
187
210
  this.emit("thread.stop", { state: STOPPED, result: this.tickres });
188
211
  } catch (err) {
212
+ this._span.error(err instanceof Error ? err : new Error(String(err)));
213
+ event({
214
+ kind: "thread.error",
215
+ message: err instanceof Error ? err.message : String(err),
216
+ stack: err instanceof Error ? err.stack : undefined,
217
+ });
189
218
  this.emit("thread.stop", {
190
219
  state: STOPPED,
191
220
  error: err instanceof Error ? err.message : String(err),
@@ -193,7 +222,9 @@ export class Thread<
193
222
  throw err;
194
223
  } finally {
195
224
  this.state = STOPPED;
196
- this.abort = undefined;
225
+ this._span.close();
226
+ // (TODO): questionable whether this should be undefined. perhaps a single thread should exit + resume..
227
+ this._span = undefined;
197
228
  await this.checkpoint(); /* c4: final checkpoint - persist STOPPED state */
198
229
  }
199
230
  }
@@ -208,7 +239,7 @@ export class Thread<
208
239
  for (;;) {
209
240
  let err: Error | undefined = undefined;
210
241
 
211
- if (this.abort?.signal.aborted) {
242
+ if (this._abort?.aborted) {
212
243
  return;
213
244
  }
214
245
 
@@ -286,37 +317,70 @@ export class Thread<
286
317
 
287
318
  const req = await this.prepareModelRequest(this.history);
288
319
 
320
+ const s = span<ModelCallSpan>(
321
+ {
322
+ kind: "model.call",
323
+ provider: this.model.provider,
324
+ modelId: this.model.modelId,
325
+ request: {
326
+ input: req.input,
327
+ settings: req.settings,
328
+ responseType: req.responseType,
329
+ tools: req.tools,
330
+ },
331
+ },
332
+ this._span!.id,
333
+ );
334
+ s.enter();
335
+
289
336
  this.emit("model.call.start", { settings: req.settings ?? {} });
290
337
 
338
+ // tracing / observability
339
+ const content: LanguageModelResponseItem[] = [];
291
340
  let usage: LanguageModelUsage | undefined;
292
- let finishReason: LanguageModelFinishReason = { unified: "other", raw: undefined };
341
+ let finishReason: LanguageModelFinishReason = {
342
+ unified: "other",
343
+ raw: undefined,
344
+ };
293
345
 
294
346
  try {
295
347
  if (this.model.stream) {
296
- for await (const event of this.model.stream(req)) {
297
- if (event.kind === "finish") {
298
- usage = event.usage;
299
- finishReason = event.finishReason;
348
+ for await (const e of this.model.stream(req)) {
349
+ if (e.kind === "finish") {
350
+ usage = e.usage;
351
+ finishReason = e.finishReason;
300
352
  }
301
- yield event;
353
+ if (notDelta(e)) content.push(e as LanguageModelResponseItem);
354
+ yield e;
302
355
  }
303
356
  } else {
304
357
  // fallback: blocking generate, yield events as batch
305
358
  const res = await this.model.generate(req);
306
359
  usage = res.usage;
307
360
  finishReason = res.finishReason;
308
- for (const event of res.content) {
309
- yield event;
361
+ for (const e of res.content) {
362
+ content.push(e);
363
+ yield e;
310
364
  }
311
365
  }
312
366
 
367
+ s.record({
368
+ response: {
369
+ content,
370
+ finishReason,
371
+ usage,
372
+ },
373
+ });
313
374
  this.emit("model.call.end", { finishReason, usage });
314
375
  } catch (error) {
376
+ s.error(error instanceof Error ? error : new Error(String(error)));
315
377
  this.emit("model.call.end", { finishReason: "error" });
316
378
  yield {
317
379
  kind: "error",
318
380
  error: error instanceof Error ? error : new Error(String(error)),
319
381
  };
382
+ } finally {
383
+ s.close();
320
384
  }
321
385
  }
322
386
 
@@ -390,12 +454,12 @@ export class Thread<
390
454
  }
391
455
 
392
456
  /**
393
- * Cancel the running thread
457
+ * Abort the running thread.
394
458
  *
395
- * TODO: Emit thread.stop when cancelled (neither result nor error set)
459
+ * @throws {Error} Not implemented - use AbortSignal via options instead
396
460
  */
397
- cancel() {
398
- this.abort?.abort();
461
+ abort() {
462
+ throw new Error("Not implemented: use AbortSignal via ThreadExecuteOptions");
399
463
  }
400
464
 
401
465
  /**
@@ -478,6 +542,18 @@ export class Thread<
478
542
  calls.map(async (call: ToolCall) => {
479
543
  const parsedArgs = JSON.parse(call.arguments || "{}");
480
544
 
545
+ // create tool.call span
546
+ const s = span<ToolCallSpan>(
547
+ {
548
+ kind: "tool.call",
549
+ toolId: call.toolId,
550
+ callId: call.callId,
551
+ args: parsedArgs,
552
+ },
553
+ this._span!.id,
554
+ );
555
+ s.enter();
556
+
481
557
  this.emit("tool.call.start", {
482
558
  toolId: call.toolId,
483
559
  callId: call.callId,
@@ -501,16 +577,20 @@ export class Thread<
501
577
  const ctx = new Context(this.namespace, this.context.context);
502
578
  ctx.agent = this.agent;
503
579
  ctx.approve(call.callId); // mark this call as approved
580
+
504
581
  const res = await tool.invoke(ctx, call.arguments, call.callId);
505
582
 
583
+ s.record({
584
+ state: res.state,
585
+ result: res.result,
586
+ error: res.error,
587
+ });
588
+
506
589
  this.emit("tool.call.end", {
507
590
  toolId: call.toolId,
508
591
  callId: call.callId,
509
592
  state: res.state,
510
- result:
511
- typeof res.result === "string"
512
- ? res.result
513
- : JSON.stringify(res.result),
593
+ result: res.result,
514
594
  error: res.error,
515
595
  });
516
596
 
@@ -523,11 +603,15 @@ export class Thread<
523
603
  error: res.error,
524
604
  };
525
605
  } catch (error) {
606
+ const errMsg = error instanceof Error ? error.message : String(error);
607
+ s.error(error instanceof Error ? error : new Error(errMsg));
608
+ s.record({ state: "failed", error: errMsg });
609
+
526
610
  this.emit("tool.call.end", {
527
611
  toolId: call.toolId,
528
612
  callId: call.callId,
529
613
  state: FAILED,
530
- error: error instanceof Error ? error.message : String(error),
614
+ error: errMsg,
531
615
  });
532
616
 
533
617
  return {
@@ -536,8 +620,10 @@ export class Thread<
536
620
  toolId: call.toolId,
537
621
  state: FAILED,
538
622
  result: undefined as any,
539
- error: error instanceof Error ? error.message : String(error),
623
+ error: errMsg,
540
624
  };
625
+ } finally {
626
+ s.close();
541
627
  }
542
628
  }),
543
629
  );
@@ -598,6 +684,7 @@ export class Thread<
598
684
  settings,
599
685
  tools,
600
686
  responseType,
687
+ abort: this._abort,
601
688
  };
602
689
  }
603
690
  }
@@ -219,6 +219,11 @@ export interface ThreadOptions<
219
219
  * hydrating from a store. Callers creating new threads should omit it.
220
220
  */
221
221
  persisted?: boolean;
222
+ /**
223
+ * Abort signal for cancelling thread execution.
224
+ * When aborted, the thread will stop at the next safe point.
225
+ */
226
+ abort?: AbortSignal;
222
227
  }
223
228
 
224
229
  /**