autotel-cloudflare 3.0.0 → 3.1.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.
- package/dist/agents.d.ts +626 -123
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +253 -171
- package/dist/agents.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/agent.ts +327 -0
- package/src/agents/base.ts +26 -0
- package/src/agents/index.ts +5 -1
- package/src/agents/mcp.ts +38 -0
- package/src/agents/observability.ts +145 -0
- package/src/agents/otel-observability.test.ts +165 -232
- package/src/agents/otel-observability.ts +350 -267
- package/src/agents/types.ts +26 -132
- package/src/agents.ts +17 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autotel-cloudflare",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "The #1 OpenTelemetry package for Cloudflare Workers - complete bindings coverage, native CF OTel integration, advanced sampling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import type { BaseEvent } from './base';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent-specific observability events.
|
|
5
|
+
*/
|
|
6
|
+
export type AgentObservabilityEvent =
|
|
7
|
+
| BaseEvent<'state:update'>
|
|
8
|
+
| BaseEvent<'rpc', { method: string; streaming?: boolean }>
|
|
9
|
+
| BaseEvent<'rpc:error', { method: string; error: string }>
|
|
10
|
+
| BaseEvent<'message:request'>
|
|
11
|
+
| BaseEvent<'message:response'>
|
|
12
|
+
| BaseEvent<'message:clear'>
|
|
13
|
+
| BaseEvent<'message:cancel', { requestId: string }>
|
|
14
|
+
| BaseEvent<'message:error', { error: string }>
|
|
15
|
+
| BaseEvent<'tool:result', { toolCallId: string; toolName: string }>
|
|
16
|
+
| BaseEvent<'tool:approval', { toolCallId: string; approved: boolean }>
|
|
17
|
+
| BaseEvent<'schedule:create', { callback: string; id: string }>
|
|
18
|
+
| BaseEvent<'schedule:execute', { callback: string; id: string }>
|
|
19
|
+
| BaseEvent<'schedule:cancel', { callback: string; id: string }>
|
|
20
|
+
| BaseEvent<
|
|
21
|
+
'schedule:retry',
|
|
22
|
+
{ callback: string; id: string; attempt: number; maxAttempts: number }
|
|
23
|
+
>
|
|
24
|
+
| BaseEvent<
|
|
25
|
+
'schedule:error',
|
|
26
|
+
{ callback: string; id: string; error: string; attempts: number }
|
|
27
|
+
>
|
|
28
|
+
| BaseEvent<
|
|
29
|
+
'schedule:duplicate_warning',
|
|
30
|
+
{ callback: string; count: number; type: string }
|
|
31
|
+
>
|
|
32
|
+
| BaseEvent<'queue:create', { callback: string; id: string }>
|
|
33
|
+
| BaseEvent<
|
|
34
|
+
'queue:retry',
|
|
35
|
+
{ callback: string; id: string; attempt: number; maxAttempts: number }
|
|
36
|
+
>
|
|
37
|
+
| BaseEvent<
|
|
38
|
+
'queue:error',
|
|
39
|
+
{ callback: string; id: string; error: string; attempts: number }
|
|
40
|
+
>
|
|
41
|
+
| BaseEvent<
|
|
42
|
+
'submission:create',
|
|
43
|
+
{ submissionId: string; requestId?: string; idempotencyKey?: string }
|
|
44
|
+
>
|
|
45
|
+
| BaseEvent<
|
|
46
|
+
'submission:status',
|
|
47
|
+
{ submissionId: string; requestId?: string; status: string }
|
|
48
|
+
>
|
|
49
|
+
| BaseEvent<
|
|
50
|
+
'submission:error',
|
|
51
|
+
{ submissionId: string; requestId?: string; error: string }
|
|
52
|
+
>
|
|
53
|
+
| BaseEvent<
|
|
54
|
+
'fiber:run:started',
|
|
55
|
+
{ fiberId: string; fiberName: string; managed?: boolean }
|
|
56
|
+
>
|
|
57
|
+
| BaseEvent<
|
|
58
|
+
'fiber:run:completed',
|
|
59
|
+
{
|
|
60
|
+
fiberId: string;
|
|
61
|
+
fiberName: string;
|
|
62
|
+
elapsedMs?: number;
|
|
63
|
+
managed?: boolean;
|
|
64
|
+
}
|
|
65
|
+
>
|
|
66
|
+
| BaseEvent<
|
|
67
|
+
'fiber:run:failed',
|
|
68
|
+
{
|
|
69
|
+
fiberId: string;
|
|
70
|
+
fiberName: string;
|
|
71
|
+
error: string;
|
|
72
|
+
elapsedMs?: number;
|
|
73
|
+
managed?: boolean;
|
|
74
|
+
}
|
|
75
|
+
>
|
|
76
|
+
| BaseEvent<
|
|
77
|
+
'fiber:run:interrupted',
|
|
78
|
+
{
|
|
79
|
+
fiberId: string;
|
|
80
|
+
fiberName: string;
|
|
81
|
+
elapsedMs?: number;
|
|
82
|
+
managed?: boolean;
|
|
83
|
+
recoveryReason: 'interrupted';
|
|
84
|
+
}
|
|
85
|
+
>
|
|
86
|
+
| BaseEvent<
|
|
87
|
+
'fiber:recovery:detected',
|
|
88
|
+
{
|
|
89
|
+
fiberId: string;
|
|
90
|
+
fiberName: string;
|
|
91
|
+
elapsedMs?: number;
|
|
92
|
+
managed?: boolean;
|
|
93
|
+
recoveryReason: 'interrupted';
|
|
94
|
+
}
|
|
95
|
+
>
|
|
96
|
+
| BaseEvent<
|
|
97
|
+
'fiber:recovery:attempt',
|
|
98
|
+
{
|
|
99
|
+
fiberId: string;
|
|
100
|
+
fiberName: string;
|
|
101
|
+
managed?: boolean;
|
|
102
|
+
recoveryReason: 'interrupted';
|
|
103
|
+
}
|
|
104
|
+
>
|
|
105
|
+
| BaseEvent<
|
|
106
|
+
'fiber:recovery:handled',
|
|
107
|
+
{
|
|
108
|
+
fiberId: string;
|
|
109
|
+
fiberName: string;
|
|
110
|
+
status?: string;
|
|
111
|
+
elapsedMs?: number;
|
|
112
|
+
managed?: boolean;
|
|
113
|
+
}
|
|
114
|
+
>
|
|
115
|
+
| BaseEvent<
|
|
116
|
+
'fiber:recovery:skipped',
|
|
117
|
+
{
|
|
118
|
+
fiberId: string;
|
|
119
|
+
fiberName: string;
|
|
120
|
+
reason: string;
|
|
121
|
+
elapsedMs?: number;
|
|
122
|
+
managed?: boolean;
|
|
123
|
+
}
|
|
124
|
+
>
|
|
125
|
+
| BaseEvent<
|
|
126
|
+
'fiber:recovery:failed',
|
|
127
|
+
{
|
|
128
|
+
fiberId: string;
|
|
129
|
+
fiberName: string;
|
|
130
|
+
error: string;
|
|
131
|
+
elapsedMs?: number;
|
|
132
|
+
reason?: string;
|
|
133
|
+
}
|
|
134
|
+
>
|
|
135
|
+
| BaseEvent<
|
|
136
|
+
'chat:request:failed',
|
|
137
|
+
{
|
|
138
|
+
requestId?: string;
|
|
139
|
+
stage:
|
|
140
|
+
| 'parse'
|
|
141
|
+
| 'persist'
|
|
142
|
+
| 'turn'
|
|
143
|
+
| 'stream'
|
|
144
|
+
| 'recovery'
|
|
145
|
+
| 'transcript';
|
|
146
|
+
messagesPersisted?: boolean;
|
|
147
|
+
error: string;
|
|
148
|
+
}
|
|
149
|
+
>
|
|
150
|
+
| BaseEvent<
|
|
151
|
+
'chat:recovery:detected',
|
|
152
|
+
{
|
|
153
|
+
incidentId: string;
|
|
154
|
+
requestId: string;
|
|
155
|
+
attempt: number;
|
|
156
|
+
maxAttempts: number;
|
|
157
|
+
recoveryKind: 'retry' | 'continue';
|
|
158
|
+
}
|
|
159
|
+
>
|
|
160
|
+
| BaseEvent<
|
|
161
|
+
'chat:recovery:scheduled',
|
|
162
|
+
{
|
|
163
|
+
incidentId: string;
|
|
164
|
+
requestId: string;
|
|
165
|
+
attempt: number;
|
|
166
|
+
maxAttempts: number;
|
|
167
|
+
recoveryKind: 'retry' | 'continue';
|
|
168
|
+
}
|
|
169
|
+
>
|
|
170
|
+
| BaseEvent<
|
|
171
|
+
'chat:recovery:attempt',
|
|
172
|
+
{
|
|
173
|
+
incidentId: string;
|
|
174
|
+
requestId: string;
|
|
175
|
+
attempt: number;
|
|
176
|
+
maxAttempts: number;
|
|
177
|
+
recoveryKind: 'retry' | 'continue';
|
|
178
|
+
}
|
|
179
|
+
>
|
|
180
|
+
| BaseEvent<
|
|
181
|
+
'chat:recovery:completed',
|
|
182
|
+
{
|
|
183
|
+
incidentId: string;
|
|
184
|
+
requestId: string;
|
|
185
|
+
attempt: number;
|
|
186
|
+
maxAttempts: number;
|
|
187
|
+
recoveryKind: 'retry' | 'continue';
|
|
188
|
+
}
|
|
189
|
+
>
|
|
190
|
+
| BaseEvent<
|
|
191
|
+
'chat:recovery:skipped',
|
|
192
|
+
{
|
|
193
|
+
incidentId: string;
|
|
194
|
+
requestId: string;
|
|
195
|
+
attempt: number;
|
|
196
|
+
maxAttempts: number;
|
|
197
|
+
recoveryKind: 'retry' | 'continue';
|
|
198
|
+
reason?: string;
|
|
199
|
+
}
|
|
200
|
+
>
|
|
201
|
+
| BaseEvent<
|
|
202
|
+
'chat:recovery:exhausted',
|
|
203
|
+
{
|
|
204
|
+
incidentId: string;
|
|
205
|
+
requestId: string;
|
|
206
|
+
attempt: number;
|
|
207
|
+
maxAttempts: number;
|
|
208
|
+
recoveryKind: 'retry' | 'continue';
|
|
209
|
+
reason: string;
|
|
210
|
+
}
|
|
211
|
+
>
|
|
212
|
+
| BaseEvent<
|
|
213
|
+
'chat:recovery:failed',
|
|
214
|
+
{
|
|
215
|
+
incidentId: string;
|
|
216
|
+
requestId: string;
|
|
217
|
+
attempt: number;
|
|
218
|
+
maxAttempts: number;
|
|
219
|
+
recoveryKind: 'retry' | 'continue';
|
|
220
|
+
reason?: string;
|
|
221
|
+
}
|
|
222
|
+
>
|
|
223
|
+
| BaseEvent<
|
|
224
|
+
'chat:transcript:repaired',
|
|
225
|
+
{
|
|
226
|
+
requestId?: string;
|
|
227
|
+
removedToolCalls: number;
|
|
228
|
+
normalizedInputs: number;
|
|
229
|
+
toolCallIds?: string[];
|
|
230
|
+
}
|
|
231
|
+
>
|
|
232
|
+
| BaseEvent<
|
|
233
|
+
'chat:onstart:degraded',
|
|
234
|
+
{
|
|
235
|
+
step:
|
|
236
|
+
| 'transcript-hydration'
|
|
237
|
+
| 'scheduled-task-reconcile'
|
|
238
|
+
| 'durable-work-recovery';
|
|
239
|
+
error: string;
|
|
240
|
+
}
|
|
241
|
+
>
|
|
242
|
+
| BaseEvent<
|
|
243
|
+
'chat:hydration:windowed',
|
|
244
|
+
{
|
|
245
|
+
totalContentBytes: number;
|
|
246
|
+
budgetBytes: number;
|
|
247
|
+
hydratedMessages: number;
|
|
248
|
+
}
|
|
249
|
+
>
|
|
250
|
+
| BaseEvent<
|
|
251
|
+
'chat:media:evicted',
|
|
252
|
+
{
|
|
253
|
+
messages: number;
|
|
254
|
+
parts: number;
|
|
255
|
+
bytes: number;
|
|
256
|
+
externalizedBytes: number;
|
|
257
|
+
}
|
|
258
|
+
>
|
|
259
|
+
| BaseEvent<
|
|
260
|
+
'chat:stream:stalled',
|
|
261
|
+
{
|
|
262
|
+
requestId: string;
|
|
263
|
+
timeoutMs: number;
|
|
264
|
+
}
|
|
265
|
+
>
|
|
266
|
+
| BaseEvent<
|
|
267
|
+
'chat:context:compacted',
|
|
268
|
+
{
|
|
269
|
+
reason: 'proactive' | 'reactive';
|
|
270
|
+
shortened: boolean;
|
|
271
|
+
requestId?: string;
|
|
272
|
+
attempt?: number;
|
|
273
|
+
}
|
|
274
|
+
>
|
|
275
|
+
| BaseEvent<
|
|
276
|
+
'agent_tool:recovery:begin',
|
|
277
|
+
{ runCount: number; totalTimeoutMs?: number }
|
|
278
|
+
>
|
|
279
|
+
| BaseEvent<
|
|
280
|
+
'agent_tool:recovery:row',
|
|
281
|
+
{
|
|
282
|
+
runId: string;
|
|
283
|
+
agentType: string;
|
|
284
|
+
status: string;
|
|
285
|
+
reason?: string;
|
|
286
|
+
elapsedMs?: number;
|
|
287
|
+
}
|
|
288
|
+
>
|
|
289
|
+
| BaseEvent<
|
|
290
|
+
'agent_tool:recovery:deadline',
|
|
291
|
+
{ runId: string; agentType: string; elapsedMs?: number }
|
|
292
|
+
>
|
|
293
|
+
| BaseEvent<
|
|
294
|
+
'agent_tool:recovery:reattach',
|
|
295
|
+
{ runId: string; agentType: string; budgetMs: number }
|
|
296
|
+
>
|
|
297
|
+
| BaseEvent<
|
|
298
|
+
'agent_tool:recovery:complete',
|
|
299
|
+
{ runCount: number; elapsedMs?: number }
|
|
300
|
+
>
|
|
301
|
+
| BaseEvent<'agent_tool:recovery:failed', { error: string }>
|
|
302
|
+
| BaseEvent<'destroy'>
|
|
303
|
+
| BaseEvent<'connect', { connectionId: string }>
|
|
304
|
+
| BaseEvent<
|
|
305
|
+
'disconnect',
|
|
306
|
+
{ connectionId: string; code: number; reason: string }
|
|
307
|
+
>
|
|
308
|
+
| BaseEvent<'email:receive', { from: string; to: string; subject?: string }>
|
|
309
|
+
| BaseEvent<'email:reply', { from: string; to: string; subject?: string }>
|
|
310
|
+
| BaseEvent<
|
|
311
|
+
'email:send',
|
|
312
|
+
{ from: string; to: string | string[]; subject: string }
|
|
313
|
+
>
|
|
314
|
+
| BaseEvent<'workflow:start', { workflowId: string; workflowName?: string }>
|
|
315
|
+
| BaseEvent<'workflow:event', { workflowId: string; eventType?: string }>
|
|
316
|
+
| BaseEvent<'workflow:approved', { workflowId: string; reason?: string }>
|
|
317
|
+
| BaseEvent<'workflow:rejected', { workflowId: string; reason?: string }>
|
|
318
|
+
| BaseEvent<
|
|
319
|
+
'workflow:terminated',
|
|
320
|
+
{ workflowId: string; workflowName?: string }
|
|
321
|
+
>
|
|
322
|
+
| BaseEvent<'workflow:paused', { workflowId: string; workflowName?: string }>
|
|
323
|
+
| BaseEvent<'workflow:resumed', { workflowId: string; workflowName?: string }>
|
|
324
|
+
| BaseEvent<
|
|
325
|
+
'workflow:restarted',
|
|
326
|
+
{ workflowId: string; workflowName?: string }
|
|
327
|
+
>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base event structure for all Cloudflare Agents observability events.
|
|
3
|
+
*/
|
|
4
|
+
export type BaseEvent<
|
|
5
|
+
T extends string,
|
|
6
|
+
Payload extends Record<string, unknown> = Record<string, never>,
|
|
7
|
+
> = {
|
|
8
|
+
type: T;
|
|
9
|
+
/**
|
|
10
|
+
* The class name of the agent that emitted this event
|
|
11
|
+
* (e.g. "MyChatAgent").
|
|
12
|
+
*/
|
|
13
|
+
agent?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The instance name (Durable Object ID name) of the agent.
|
|
16
|
+
*/
|
|
17
|
+
name?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional legacy fields accepted by the Cloudflare adapter so older
|
|
20
|
+
* examples can still be observed without reshaping the event.
|
|
21
|
+
*/
|
|
22
|
+
id?: string;
|
|
23
|
+
displayMessage?: string;
|
|
24
|
+
payload: Payload;
|
|
25
|
+
timestamp: number;
|
|
26
|
+
};
|
package/src/agents/index.ts
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
*
|
|
18
18
|
* @callable()
|
|
19
19
|
* async doSomething() {
|
|
20
|
-
* //
|
|
20
|
+
* // RPC, chat recovery, queue/schedule, workflow, MCP, and lifecycle
|
|
21
|
+
* // events emitted by the Agents SDK will be converted into spans.
|
|
21
22
|
* return 'done'
|
|
22
23
|
* }
|
|
23
24
|
* }
|
|
@@ -31,12 +32,15 @@ export {
|
|
|
31
32
|
createOtelObservabilityFromEnv,
|
|
32
33
|
OtelObservability,
|
|
33
34
|
} from './otel-observability';
|
|
35
|
+
export { channels, genericObservability, subscribe } from './observability';
|
|
34
36
|
export type {
|
|
37
|
+
BaseEvent,
|
|
35
38
|
OtelObservabilityConfig,
|
|
36
39
|
AgentObservabilityEvent,
|
|
37
40
|
MCPObservabilityEvent,
|
|
38
41
|
ObservabilityEvent,
|
|
39
42
|
Observability,
|
|
43
|
+
ChannelEventMap,
|
|
40
44
|
AgentInstrumentationOptions,
|
|
41
45
|
AgentSpanAttributes,
|
|
42
46
|
} from './types';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { BaseEvent } from './base';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP-specific observability events.
|
|
5
|
+
*/
|
|
6
|
+
export type MCPObservabilityEvent =
|
|
7
|
+
| BaseEvent<'mcp:client:preconnect', { serverId: string }>
|
|
8
|
+
| BaseEvent<
|
|
9
|
+
'mcp:client:connect',
|
|
10
|
+
{ url: string; transport: string; state: string; error?: string }
|
|
11
|
+
>
|
|
12
|
+
| BaseEvent<
|
|
13
|
+
'mcp:client:authorize',
|
|
14
|
+
{
|
|
15
|
+
serverId: string;
|
|
16
|
+
authUrl: string;
|
|
17
|
+
clientId?: string;
|
|
18
|
+
}
|
|
19
|
+
>
|
|
20
|
+
| BaseEvent<
|
|
21
|
+
'mcp:client:discover',
|
|
22
|
+
{
|
|
23
|
+
url?: string;
|
|
24
|
+
state?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
capability?: string;
|
|
27
|
+
}
|
|
28
|
+
>
|
|
29
|
+
| BaseEvent<
|
|
30
|
+
'mcp:client:close',
|
|
31
|
+
{
|
|
32
|
+
url: string;
|
|
33
|
+
transport?: string;
|
|
34
|
+
state: string;
|
|
35
|
+
error?: string;
|
|
36
|
+
phase?: 'terminate-session' | 'client-close';
|
|
37
|
+
}
|
|
38
|
+
>;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { AgentObservabilityEvent } from './agent';
|
|
2
|
+
import type { MCPObservabilityEvent } from './mcp';
|
|
3
|
+
|
|
4
|
+
export type ObservabilityEvent = AgentObservabilityEvent | MCPObservabilityEvent;
|
|
5
|
+
|
|
6
|
+
export interface Observability {
|
|
7
|
+
emit(event: ObservabilityEvent, ctx?: DurableObjectState | ExecutionContext): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ChannelKey =
|
|
11
|
+
| 'state'
|
|
12
|
+
| 'rpc'
|
|
13
|
+
| 'message'
|
|
14
|
+
| 'chat'
|
|
15
|
+
| 'transcript'
|
|
16
|
+
| 'fiber'
|
|
17
|
+
| 'agentTool'
|
|
18
|
+
| 'schedule'
|
|
19
|
+
| 'lifecycle'
|
|
20
|
+
| 'workflow'
|
|
21
|
+
| 'mcp'
|
|
22
|
+
| 'email';
|
|
23
|
+
|
|
24
|
+
export type ChannelEventMap = {
|
|
25
|
+
state: Extract<ObservabilityEvent, { type: `state:${string}` }>;
|
|
26
|
+
rpc: Extract<ObservabilityEvent, { type: 'rpc' | `rpc:${string}` }>;
|
|
27
|
+
message: Extract<
|
|
28
|
+
ObservabilityEvent,
|
|
29
|
+
{ type: `message:${string}` | `tool:${string}` | `submission:${string}` }
|
|
30
|
+
>;
|
|
31
|
+
chat: Exclude<
|
|
32
|
+
Extract<ObservabilityEvent, { type: `chat:${string}` }>,
|
|
33
|
+
{ type: `chat:transcript:${string}` }
|
|
34
|
+
>;
|
|
35
|
+
transcript: Extract<
|
|
36
|
+
ObservabilityEvent,
|
|
37
|
+
{ type: `transcript:${string}` | `chat:transcript:${string}` }
|
|
38
|
+
>;
|
|
39
|
+
fiber: Extract<ObservabilityEvent, { type: `fiber:${string}` }>;
|
|
40
|
+
agentTool: Extract<ObservabilityEvent, { type: `agent_tool:${string}` }>;
|
|
41
|
+
schedule: Extract<
|
|
42
|
+
ObservabilityEvent,
|
|
43
|
+
{ type: `schedule:${string}` | `queue:${string}` }
|
|
44
|
+
>;
|
|
45
|
+
lifecycle: Extract<
|
|
46
|
+
ObservabilityEvent,
|
|
47
|
+
{ type: 'connect' | 'disconnect' | 'destroy' }
|
|
48
|
+
>;
|
|
49
|
+
workflow: Extract<ObservabilityEvent, { type: `workflow:${string}` }>;
|
|
50
|
+
mcp: Extract<ObservabilityEvent, { type: `mcp:${string}` }>;
|
|
51
|
+
email: Extract<ObservabilityEvent, { type: `email:${string}` }>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export interface ObservabilityChannel<TEvent extends ObservabilityEvent = ObservabilityEvent> {
|
|
55
|
+
name: string;
|
|
56
|
+
publish(event: TEvent): void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const listeners: {
|
|
60
|
+
[K in ChannelKey]: Set<(event: ChannelEventMap[K]) => void>;
|
|
61
|
+
} = {
|
|
62
|
+
state: new Set(),
|
|
63
|
+
rpc: new Set(),
|
|
64
|
+
message: new Set(),
|
|
65
|
+
chat: new Set(),
|
|
66
|
+
transcript: new Set(),
|
|
67
|
+
fiber: new Set(),
|
|
68
|
+
agentTool: new Set(),
|
|
69
|
+
schedule: new Set(),
|
|
70
|
+
lifecycle: new Set(),
|
|
71
|
+
workflow: new Set(),
|
|
72
|
+
mcp: new Set(),
|
|
73
|
+
email: new Set(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function createChannel<K extends ChannelKey>(
|
|
77
|
+
key: K,
|
|
78
|
+
name: string,
|
|
79
|
+
): ObservabilityChannel<ChannelEventMap[K]> {
|
|
80
|
+
return {
|
|
81
|
+
name,
|
|
82
|
+
publish(event) {
|
|
83
|
+
for (const callback of listeners[key]) {
|
|
84
|
+
callback(event);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const channels = {
|
|
91
|
+
state: createChannel('state', 'agents:state'),
|
|
92
|
+
rpc: createChannel('rpc', 'agents:rpc'),
|
|
93
|
+
message: createChannel('message', 'agents:message'),
|
|
94
|
+
chat: createChannel('chat', 'agents:chat'),
|
|
95
|
+
transcript: createChannel('transcript', 'agents:transcript'),
|
|
96
|
+
fiber: createChannel('fiber', 'agents:fiber'),
|
|
97
|
+
agentTool: createChannel('agentTool', 'agents:agent_tool'),
|
|
98
|
+
schedule: createChannel('schedule', 'agents:schedule'),
|
|
99
|
+
lifecycle: createChannel('lifecycle', 'agents:lifecycle'),
|
|
100
|
+
workflow: createChannel('workflow', 'agents:workflow'),
|
|
101
|
+
mcp: createChannel('mcp', 'agents:mcp'),
|
|
102
|
+
email: createChannel('email', 'agents:email'),
|
|
103
|
+
} as const;
|
|
104
|
+
|
|
105
|
+
function getChannel(type: string): ObservabilityChannel {
|
|
106
|
+
if (type.startsWith('mcp:')) return channels.mcp;
|
|
107
|
+
if (type.startsWith('workflow:')) return channels.workflow;
|
|
108
|
+
if (type.startsWith('fiber:')) return channels.fiber;
|
|
109
|
+
if (type.startsWith('transcript:') || type.startsWith('chat:transcript:')) {
|
|
110
|
+
return channels.transcript;
|
|
111
|
+
}
|
|
112
|
+
if (type.startsWith('chat:')) return channels.chat;
|
|
113
|
+
if (type.startsWith('agent_tool:')) return channels.agentTool;
|
|
114
|
+
if (type.startsWith('schedule:') || type.startsWith('queue:')) {
|
|
115
|
+
return channels.schedule;
|
|
116
|
+
}
|
|
117
|
+
if (
|
|
118
|
+
type.startsWith('message:') ||
|
|
119
|
+
type.startsWith('tool:') ||
|
|
120
|
+
type.startsWith('submission:')
|
|
121
|
+
) {
|
|
122
|
+
return channels.message;
|
|
123
|
+
}
|
|
124
|
+
if (type === 'rpc' || type.startsWith('rpc:')) return channels.rpc;
|
|
125
|
+
if (type.startsWith('state:')) return channels.state;
|
|
126
|
+
if (type.startsWith('email:')) return channels.email;
|
|
127
|
+
return channels.lifecycle;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const genericObservability: Observability = {
|
|
131
|
+
emit(event) {
|
|
132
|
+
getChannel(event.type).publish(event);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export function subscribe<K extends keyof ChannelEventMap>(
|
|
137
|
+
channelKey: K,
|
|
138
|
+
callback: (event: ChannelEventMap[K]) => void,
|
|
139
|
+
): () => void {
|
|
140
|
+
const bucket = listeners[channelKey];
|
|
141
|
+
bucket.add(callback);
|
|
142
|
+
return () => {
|
|
143
|
+
bucket.delete(callback);
|
|
144
|
+
};
|
|
145
|
+
}
|