@smithers-orchestrator/observability 0.20.0 → 0.20.1

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.
@@ -0,0 +1,397 @@
1
+ import { extractTextFromJsonValue } from "@smithers-orchestrator/agents/BaseCliAgent";
2
+ import { normalizeTokenUsage } from "@smithers-orchestrator/agents/BaseCliAgent";
3
+
4
+ /**
5
+ * @typedef {import('./agentTrace.ts').AgentFamily} AgentFamily
6
+ * @typedef {import('./agentTrace.ts').CanonicalAgentTraceEventKind} CanonicalAgentTraceEventKind
7
+ *
8
+ * @typedef {"message" | "tool" | "pi" | "none"} PayloadKind
9
+ *
10
+ * @typedef {{
11
+ * kind: CanonicalAgentTraceEventKind;
12
+ * payloadKind: PayloadKind;
13
+ * expect?: CanonicalAgentTraceEventKind;
14
+ * }} MappedStructuredEvent
15
+ *
16
+ * @typedef {{
17
+ * kind: CanonicalAgentTraceEventKind;
18
+ * payload: Record<string, unknown> | null;
19
+ * raw: unknown;
20
+ * rawType?: string;
21
+ * observed?: boolean;
22
+ * }} NormalizedTraceEvent
23
+ *
24
+ * @typedef {{
25
+ * events: NormalizedTraceEvent[];
26
+ * expectedKinds?: CanonicalAgentTraceEventKind[];
27
+ * }} NormalizedTraceBatch
28
+ */
29
+
30
+ /** @type {Record<string, MappedStructuredEvent>} */
31
+ const piSimpleEventMap = {
32
+ session: { kind: "session.start", payloadKind: "pi" },
33
+ agent_start: { kind: "session.start", payloadKind: "pi" },
34
+ agent_end: { kind: "session.end", payloadKind: "pi" },
35
+ turn_start: { kind: "turn.start", payloadKind: "pi", expect: "turn.end" },
36
+ message_start: { kind: "message.start", payloadKind: "pi" },
37
+ tool_execution_start: { kind: "tool.execution.start", payloadKind: "tool", expect: "tool.execution.end" },
38
+ tool_execution_update: { kind: "tool.execution.update", payloadKind: "tool", expect: "tool.execution.end" },
39
+ tool_execution_end: { kind: "tool.execution.end", payloadKind: "tool" },
40
+ auto_compaction_start: { kind: "compaction.start", payloadKind: "pi" },
41
+ auto_compaction_end: { kind: "compaction.end", payloadKind: "pi" },
42
+ auto_retry_start: { kind: "retry.start", payloadKind: "pi" },
43
+ auto_retry_end: { kind: "retry.end", payloadKind: "pi" },
44
+ };
45
+
46
+ /** @type {Record<string, MappedStructuredEvent>} */
47
+ const genericStructuredEventMap = {
48
+ message_start: { kind: "message.start", payloadKind: "message" },
49
+ assistant_message_start: { kind: "message.start", payloadKind: "message" },
50
+ "response.started": { kind: "message.start", payloadKind: "message" },
51
+ tool_call_start: { kind: "tool.execution.start", payloadKind: "tool", expect: "tool.execution.end" },
52
+ tool_execution_start: { kind: "tool.execution.start", payloadKind: "tool", expect: "tool.execution.end" },
53
+ "tool_call.started": { kind: "tool.execution.start", payloadKind: "tool", expect: "tool.execution.end" },
54
+ tool_call_delta: { kind: "tool.execution.update", payloadKind: "tool", expect: "tool.execution.end" },
55
+ tool_call_update: { kind: "tool.execution.update", payloadKind: "tool", expect: "tool.execution.end" },
56
+ tool_execution_update: { kind: "tool.execution.update", payloadKind: "tool", expect: "tool.execution.end" },
57
+ "tool_call.delta": { kind: "tool.execution.update", payloadKind: "tool", expect: "tool.execution.end" },
58
+ tool_call_end: { kind: "tool.execution.end", payloadKind: "tool" },
59
+ tool_execution_end: { kind: "tool.execution.end", payloadKind: "tool" },
60
+ "tool_call.completed": { kind: "tool.execution.end", payloadKind: "tool" },
61
+ tool_result: { kind: "tool.result", payloadKind: "tool" },
62
+ "tool_result.completed": { kind: "tool.result", payloadKind: "tool" },
63
+ };
64
+
65
+ /**
66
+ * @param {any} parsed
67
+ * @returns {string | undefined}
68
+ */
69
+ function extractGenericDeltaText(parsed) {
70
+ const candidates = [
71
+ parsed?.delta?.text,
72
+ parsed?.delta,
73
+ parsed?.text,
74
+ parsed?.content_block?.text,
75
+ parsed?.contentBlock?.text,
76
+ parsed?.message?.text,
77
+ parsed?.message?.content,
78
+ parsed?.output_text,
79
+ ];
80
+ for (const candidate of candidates) {
81
+ if (typeof candidate === "string" && candidate) return candidate;
82
+ }
83
+ return undefined;
84
+ }
85
+
86
+ /**
87
+ * @param {any} parsed
88
+ * @returns {string | undefined}
89
+ */
90
+ function extractGenericMessageText(parsed) {
91
+ return extractTextFromJsonValue(parsed?.message ?? parsed?.response ?? parsed?.item ?? parsed);
92
+ }
93
+
94
+ /**
95
+ * @param {any} parsed
96
+ * @returns {Record<string, unknown>}
97
+ */
98
+ function extractGenericMessagePayload(parsed) {
99
+ /** @type {Record<string, unknown>} */
100
+ const payload = {};
101
+ const role = parsed?.message?.role ?? parsed?.role ?? parsed?.response?.role;
102
+ if (typeof role === "string") payload.role = role;
103
+ const text = extractGenericMessageText(parsed);
104
+ if (text) payload.text = text;
105
+ if (parsed?.id) payload.id = parsed.id;
106
+ return payload;
107
+ }
108
+
109
+ /**
110
+ * @param {any} parsed
111
+ * @returns {Record<string, unknown>}
112
+ */
113
+ function extractGenericToolPayload(parsed) {
114
+ const tool = parsed?.tool ?? parsed?.toolCall ?? parsed?.tool_call ?? parsed?.toolExecution ?? parsed;
115
+ return {
116
+ toolCallId: tool?.id ?? tool?.toolCallId ?? parsed?.id,
117
+ toolName: tool?.name ?? tool?.toolName ?? parsed?.toolName,
118
+ argsPreview: tool?.args ?? tool?.arguments ?? parsed?.args,
119
+ resultPreview: tool?.result ?? parsed?.result,
120
+ isError: Boolean(tool?.isError ?? parsed?.isError ?? parsed?.error),
121
+ };
122
+ }
123
+
124
+ /**
125
+ * @param {any} parsed
126
+ * @returns {Record<string, unknown>}
127
+ */
128
+ function extractPiPayload(parsed) {
129
+ /** @type {Record<string, unknown>} */
130
+ const payload = {};
131
+ if (parsed?.message?.role) payload.role = parsed.message.role;
132
+ const text = extractGenericMessageText(parsed?.message);
133
+ if (text) payload.text = text;
134
+ if (parsed?.id) payload.id = parsed.id;
135
+ return payload;
136
+ }
137
+
138
+ /**
139
+ * @param {any} parsed
140
+ * @param {PayloadKind} payloadKind
141
+ * @returns {Record<string, unknown> | null}
142
+ */
143
+ function extractMappedPayload(parsed, payloadKind) {
144
+ if (payloadKind === "message") return extractGenericMessagePayload(parsed);
145
+ if (payloadKind === "tool") return extractGenericToolPayload(parsed);
146
+ if (payloadKind === "pi") return extractPiPayload(parsed);
147
+ return {};
148
+ }
149
+
150
+ /**
151
+ * @param {CanonicalAgentTraceEventKind} kind
152
+ * @param {Record<string, unknown> | null} payload
153
+ * @param {unknown} raw
154
+ * @param {string | undefined} rawType
155
+ * @param {boolean} [observed]
156
+ * @returns {NormalizedTraceEvent}
157
+ */
158
+ function buildNormalizedEvent(kind, payload, raw, rawType, observed = false) {
159
+ return { kind, payload, raw, rawType, observed };
160
+ }
161
+
162
+ /**
163
+ * @param {any} parsed
164
+ * @param {string} rawType
165
+ * @param {MappedStructuredEvent} mapped
166
+ * @returns {NormalizedTraceBatch}
167
+ */
168
+ function normalizeMappedEvent(parsed, rawType, mapped) {
169
+ return {
170
+ events: [buildNormalizedEvent(mapped.kind, extractMappedPayload(parsed, mapped.payloadKind), parsed, rawType)],
171
+ expectedKinds: mapped.expect ? [mapped.expect] : undefined,
172
+ };
173
+ }
174
+
175
+ /**
176
+ * @param {any} parsed
177
+ * @param {string} rawType
178
+ * @returns {NormalizedTraceBatch | null}
179
+ */
180
+ function normalizeClaudeStructuredEvent(parsed, rawType) {
181
+ if (rawType === "assistant") {
182
+ const text = extractGenericMessageText(parsed?.message ?? parsed);
183
+ const events = text
184
+ ? [buildNormalizedEvent("message.update", extractGenericMessagePayload(parsed?.message ?? parsed), parsed, rawType)]
185
+ : [buildNormalizedEvent("stdout", { eventType: rawType }, parsed, rawType, true)];
186
+ const usage = normalizeTokenUsage(parsed?.message?.usage);
187
+ if (usage) events.push(buildNormalizedEvent("usage", usage, parsed, rawType));
188
+ return { events };
189
+ }
190
+ if (rawType === "result") {
191
+ /** @type {NormalizedTraceEvent[]} */
192
+ const events = [];
193
+ const usage = normalizeTokenUsage(parsed?.usage);
194
+ if (usage) events.push(buildNormalizedEvent("usage", usage, parsed, rawType));
195
+ const text = extractGenericMessageText(parsed);
196
+ if (text) {
197
+ events.push(buildNormalizedEvent("assistant.message.final", { text }, parsed, rawType));
198
+ }
199
+ return events.length > 0 ? { events } : null;
200
+ }
201
+ return null;
202
+ }
203
+
204
+ /**
205
+ * @param {any} parsed
206
+ * @param {string} rawType
207
+ * @returns {NormalizedTraceBatch | null}
208
+ */
209
+ function normalizeGeminiStructuredEvent(parsed, rawType) {
210
+ if (rawType === "message") {
211
+ const text = extractGenericMessageText(parsed);
212
+ if (parsed?.role === "assistant" && typeof text === "string" && text) {
213
+ return {
214
+ events: [
215
+ buildNormalizedEvent(parsed?.delta ? "assistant.text.delta" : "assistant.message.final", { text }, parsed, rawType),
216
+ ],
217
+ };
218
+ }
219
+ }
220
+ if (rawType === "result" && parsed?.stats) {
221
+ const usage = normalizeTokenUsage(parsed.stats);
222
+ return usage ? { events: [buildNormalizedEvent("usage", usage, parsed, rawType)] } : null;
223
+ }
224
+ return null;
225
+ }
226
+
227
+ /**
228
+ * @param {any} parsed
229
+ * @param {string} rawType
230
+ * @returns {NormalizedTraceBatch | null}
231
+ */
232
+ function normalizeCodexStructuredEvent(parsed, rawType) {
233
+ if (rawType === "thread.started") {
234
+ return { events: [buildNormalizedEvent("stdout", { eventType: rawType }, parsed, rawType, true)] };
235
+ }
236
+ if (rawType === "turn.started") {
237
+ return { events: [buildNormalizedEvent("turn.start", {}, parsed, rawType)], expectedKinds: ["turn.end"] };
238
+ }
239
+ if (rawType === "item.completed" && parsed?.item?.type === "agent_message") {
240
+ const text = extractGenericMessageText(parsed.item);
241
+ if (typeof text === "string" && text) {
242
+ return { events: [buildNormalizedEvent("assistant.message.final", { text }, parsed, rawType)] };
243
+ }
244
+ }
245
+ if (rawType === "turn.completed") {
246
+ /** @type {NormalizedTraceEvent[]} */
247
+ const events = [];
248
+ const usage = normalizeTokenUsage(parsed?.usage);
249
+ if (usage) events.push(buildNormalizedEvent("usage", usage, parsed, rawType));
250
+ events.push(buildNormalizedEvent("turn.end", {}, parsed, rawType));
251
+ const text = extractGenericMessageText(parsed);
252
+ if (text) {
253
+ events.push(buildNormalizedEvent("assistant.message.final", { text }, parsed, rawType));
254
+ }
255
+ return { events };
256
+ }
257
+ return null;
258
+ }
259
+
260
+ /**
261
+ * @param {any} parsed
262
+ * @param {string} rawType
263
+ * @returns {NormalizedTraceBatch | null}
264
+ */
265
+ function normalizePiStructuredEvent(parsed, rawType) {
266
+ const simple = piSimpleEventMap[rawType];
267
+ if (simple) return normalizeMappedEvent(parsed, rawType, simple);
268
+ if (rawType === "turn_end") {
269
+ /** @type {NormalizedTraceEvent[]} */
270
+ const events = [buildNormalizedEvent("turn.end", extractPiPayload(parsed), parsed, rawType)];
271
+ const text = extractGenericMessageText(parsed?.message);
272
+ if (text) {
273
+ events.push(buildNormalizedEvent("assistant.message.final", { text }, parsed?.message, rawType));
274
+ }
275
+ const usage = normalizeTokenUsage(parsed?.message?.usage);
276
+ if (usage) events.push(buildNormalizedEvent("usage", usage, parsed.message.usage, "usage"));
277
+ return { events };
278
+ }
279
+ if (rawType === "message_end") {
280
+ /** @type {NormalizedTraceEvent[]} */
281
+ const events = [buildNormalizedEvent("message.end", extractPiPayload(parsed), parsed, rawType)];
282
+ const text = extractGenericMessageText(parsed?.message);
283
+ if (parsed?.message?.role === "assistant" && text) {
284
+ events.push(buildNormalizedEvent("assistant.message.final", { text }, parsed?.message, rawType));
285
+ }
286
+ return { events };
287
+ }
288
+ if (rawType === "message_update") {
289
+ const evt = parsed?.assistantMessageEvent;
290
+ if (evt?.type === "text_delta" && typeof evt.delta === "string") {
291
+ return { events: [buildNormalizedEvent("assistant.text.delta", { text: evt.delta }, parsed, evt.type)] };
292
+ }
293
+ if ((evt?.type === "thinking_delta" || evt?.type === "reasoning_delta") && typeof evt.delta === "string") {
294
+ return { events: [buildNormalizedEvent("assistant.thinking.delta", { text: evt.delta }, parsed, evt.type)] };
295
+ }
296
+ return { events: [buildNormalizedEvent("message.update", extractPiPayload(parsed), parsed, rawType)] };
297
+ }
298
+ return { events: [buildNormalizedEvent("stdout", { eventType: rawType }, parsed, rawType, true)] };
299
+ }
300
+
301
+ /**
302
+ * @param {any} parsed
303
+ * @param {string} rawType
304
+ * @returns {NormalizedTraceBatch | null}
305
+ */
306
+ function normalizeSharedStructuredEvent(parsed, rawType) {
307
+ const mapped = genericStructuredEventMap[rawType];
308
+ if (mapped) return normalizeMappedEvent(parsed, rawType, mapped);
309
+ if ([
310
+ "message_delta",
311
+ "assistant_message.delta",
312
+ "assistant_message_delta",
313
+ "response.output_text.delta",
314
+ "content_block_delta",
315
+ ].includes(rawType)) {
316
+ const text = extractGenericDeltaText(parsed);
317
+ if (typeof text === "string" && text) {
318
+ return { events: [buildNormalizedEvent("assistant.text.delta", { text }, parsed, rawType)] };
319
+ }
320
+ }
321
+ if ([
322
+ "thinking_delta",
323
+ "reasoning_delta",
324
+ "response.reasoning.delta",
325
+ ].includes(rawType)) {
326
+ const text = extractGenericDeltaText(parsed);
327
+ if (typeof text === "string" && text) {
328
+ return { events: [buildNormalizedEvent("assistant.thinking.delta", { text }, parsed, rawType)] };
329
+ }
330
+ }
331
+ if ([
332
+ "message_end",
333
+ "assistant_message_end",
334
+ "response.completed",
335
+ "message_stop",
336
+ ].includes(rawType)) {
337
+ /** @type {NormalizedTraceEvent[]} */
338
+ const events = [buildNormalizedEvent("message.end", extractGenericMessagePayload(parsed), parsed, rawType)];
339
+ const text = extractGenericMessageText(parsed);
340
+ if (text) {
341
+ events.push(buildNormalizedEvent("assistant.message.final", { text }, parsed, rawType));
342
+ }
343
+ const usage = normalizeTokenUsage(parsed?.usage);
344
+ if (usage) events.push(buildNormalizedEvent("usage", usage, parsed, rawType));
345
+ return { events };
346
+ }
347
+ return null;
348
+ }
349
+
350
+ /**
351
+ * @param {AgentFamily} agentFamily
352
+ * @param {any} parsed
353
+ * @param {string} rawType
354
+ * @returns {NormalizedTraceBatch}
355
+ */
356
+ export function normalizeStructuredEventForFamily(agentFamily, parsed, rawType) {
357
+ if (agentFamily === "pi") {
358
+ return normalizePiStructuredEvent(parsed, rawType) ?? {
359
+ events: [buildNormalizedEvent("stdout", { eventType: rawType }, parsed, rawType, true)],
360
+ };
361
+ }
362
+ if (agentFamily === "claude-code") {
363
+ const normalized = normalizeClaudeStructuredEvent(parsed, rawType);
364
+ if (normalized) return normalized;
365
+ }
366
+ if (agentFamily === "gemini") {
367
+ const normalized = normalizeGeminiStructuredEvent(parsed, rawType);
368
+ if (normalized) return normalized;
369
+ }
370
+ if (agentFamily === "codex") {
371
+ const normalized = normalizeCodexStructuredEvent(parsed, rawType);
372
+ if (normalized) return normalized;
373
+ }
374
+ const shared = normalizeSharedStructuredEvent(parsed, rawType);
375
+ if (shared) return shared;
376
+ return { events: [buildNormalizedEvent("stdout", { eventType: rawType }, parsed, rawType, true)] };
377
+ }
378
+
379
+ /**
380
+ * @param {AgentFamily} agentFamily
381
+ * @param {any} parsed
382
+ * @returns {{ sessionId?: string; threadId?: string }}
383
+ */
384
+ export function extractProviderSessionCorrelation(agentFamily, parsed) {
385
+ if (agentFamily === "codex") {
386
+ const threadId = typeof parsed?.thread_id === "string" ? parsed.thread_id : undefined;
387
+ return { threadId };
388
+ }
389
+ const sessionId = typeof parsed?.session_id === "string"
390
+ ? parsed.session_id
391
+ : typeof parsed?.sessionId === "string"
392
+ ? parsed.sessionId
393
+ : agentFamily === "pi" && typeof parsed?.id === "string"
394
+ ? parsed.id
395
+ : undefined;
396
+ return { sessionId };
397
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @typedef {{ value: unknown; applied: boolean; ruleIds: string[] }} RedactionResult
3
+ */
4
+
5
+ /** @type {Array<{ id: string; pattern: RegExp; replace: string }>} */
6
+ const rules = [
7
+ {
8
+ id: "api-key",
9
+ pattern: /\b(?:sk|pk)_[A-Za-z0-9_-]{8,}\b/g,
10
+ replace: "[REDACTED_API_KEY]",
11
+ },
12
+ {
13
+ id: "bearer-token",
14
+ pattern: /Bearer\s+[A-Za-z0-9._-]{8,}/gi,
15
+ replace: "Bearer [REDACTED_TOKEN]",
16
+ },
17
+ {
18
+ id: "auth-header",
19
+ pattern: /"authorization"\s*:\s*"[^"]+"/gi,
20
+ replace: '"authorization":"[REDACTED]"',
21
+ },
22
+ {
23
+ id: "cookie-header",
24
+ pattern: /"cookie"\s*:\s*"[^"]+"/gi,
25
+ replace: '"cookie":"[REDACTED]"',
26
+ },
27
+ {
28
+ id: "secret-ish",
29
+ pattern: /\b(?:api[_-]?key|token|secret|password)=([^\s"']+)/gi,
30
+ replace: "",
31
+ },
32
+ ];
33
+
34
+ /**
35
+ * @param {unknown} value
36
+ * @returns {RedactionResult}
37
+ */
38
+ export function redactValue(value) {
39
+ const input = typeof value === "string" ? value : JSON.stringify(value ?? null);
40
+ let next = input;
41
+ /** @type {Set<string>} */
42
+ const applied = new Set();
43
+ for (const rule of rules) {
44
+ next = next.replace(rule.pattern, (match) => {
45
+ applied.add(rule.id);
46
+ if (rule.id === "secret-ish") {
47
+ const idx = match.indexOf("=");
48
+ return `${match.slice(0, idx + 1)}[REDACTED_SECRET]`;
49
+ }
50
+ return rule.replace;
51
+ });
52
+ }
53
+ if (applied.size === 0) return { value, applied: false, ruleIds: [] };
54
+ if (typeof value === "string") {
55
+ return { value: next, applied: true, ruleIds: [...applied] };
56
+ }
57
+ try {
58
+ return { value: JSON.parse(next), applied: true, ruleIds: [...applied] };
59
+ } catch {
60
+ return { value: next, applied: true, ruleIds: [...applied] };
61
+ }
62
+ }
@@ -0,0 +1,52 @@
1
+ import {
2
+ buildOtelAttributes,
3
+ buildOtelLogRecord,
4
+ inferSessionSeverity,
5
+ } from "./_otelLogBuilders.js";
6
+ /**
7
+ * @typedef {import('./agentTrace.ts').AgentSessionTranscriptEvent} AgentSessionTranscriptEvent
8
+ * @typedef {import('./_otelLogBuilders.js').OtelLogRecord} OtelLogRecord
9
+ */
10
+
11
+ /**
12
+ * @param {AgentSessionTranscriptEvent} event
13
+ * @param {{ agentId?: string; model?: string }} [context]
14
+ * @returns {OtelLogRecord}
15
+ */
16
+ export function agentSessionEventToOtelLogRecord(event, context) {
17
+ const attributes = buildOtelAttributes({
18
+ "smithers.event.category": "agent-session",
19
+ "smithers.trace.version": undefined,
20
+ "smithers.transcript.version": event.transcriptVersion,
21
+ "run.id": event.runId,
22
+ "workflow.path": event.workflowPath,
23
+ "workflow.hash": event.workflowHash,
24
+ "node.id": event.nodeId,
25
+ "node.iteration": event.iteration,
26
+ "node.attempt": event.attempt,
27
+ "agent.family": event.source.agentFamily,
28
+ "agent.id": context?.agentId,
29
+ "agent.model": context?.model,
30
+ "agent.capture_mode": event.source.captureMode,
31
+ "trace.completeness": undefined,
32
+ "event.kind": undefined,
33
+ "event.phase": undefined,
34
+ "event.sequence": undefined,
35
+ "source.raw_type": undefined,
36
+ "source.raw_event_id": undefined,
37
+ "source.observed": undefined,
38
+ "provider.session_id": event.source.providerSessionId,
39
+ "provider.thread_id": event.source.providerThreadId,
40
+ "session.ingest_source": event.source.ingestSource,
41
+ "session.observed_live": event.source.observedLive,
42
+ "session.row_type": event.event.rowType,
43
+ "session.row_sequence": event.event.sequence,
44
+ }, event.annotations);
45
+ return buildOtelLogRecord({
46
+ category: "agent-session",
47
+ payload: undefined,
48
+ raw: event.raw,
49
+ redaction: event.redaction,
50
+ annotations: event.annotations,
51
+ }, attributes, inferSessionSeverity(event.raw));
52
+ }
@@ -0,0 +1,154 @@
1
+ export type AgentFamily =
2
+ | "pi"
3
+ | "codex"
4
+ | "claude-code"
5
+ | "gemini"
6
+ | "kimi"
7
+ | "openai"
8
+ | "anthropic"
9
+ | "amp"
10
+ | "forge"
11
+ | "unknown";
12
+
13
+ export type AgentCaptureMode =
14
+ | "sdk-events"
15
+ | "rpc-events"
16
+ | "cli-json-stream"
17
+ | "cli-json"
18
+ | "cli-text"
19
+ | "artifact-import";
20
+
21
+ export type TraceCompleteness =
22
+ | "full-observed"
23
+ | "partial-observed"
24
+ | "final-only"
25
+ | "capture-failed";
26
+
27
+ export type CanonicalAgentTraceEventKind =
28
+ | "session.start"
29
+ | "session.end"
30
+ | "turn.start"
31
+ | "turn.end"
32
+ | "message.start"
33
+ | "message.update"
34
+ | "message.end"
35
+ | "assistant.text.delta"
36
+ | "assistant.thinking.delta"
37
+ | "assistant.message.final"
38
+ | "tool.execution.start"
39
+ | "tool.execution.update"
40
+ | "tool.execution.end"
41
+ | "tool.result"
42
+ | "retry.start"
43
+ | "retry.end"
44
+ | "compaction.start"
45
+ | "compaction.end"
46
+ | "stderr"
47
+ | "stdout"
48
+ | "usage"
49
+ | "capture.warning"
50
+ | "capture.error"
51
+ | "artifact.created";
52
+
53
+ export type CanonicalAgentTraceEventPhase =
54
+ | "agent"
55
+ | "turn"
56
+ | "message"
57
+ | "tool"
58
+ | "session"
59
+ | "capture"
60
+ | "artifact";
61
+
62
+ export type CanonicalAgentTraceEvent = {
63
+ traceVersion: "1";
64
+ runId: string;
65
+ workflowPath?: string;
66
+ workflowHash?: string;
67
+ nodeId: string;
68
+ iteration: number;
69
+ attempt: number;
70
+ timestampMs: number;
71
+ event: {
72
+ sequence: number;
73
+ kind: CanonicalAgentTraceEventKind;
74
+ phase: CanonicalAgentTraceEventPhase;
75
+ };
76
+ source: {
77
+ agentFamily: AgentFamily;
78
+ captureMode: AgentCaptureMode;
79
+ rawType?: string;
80
+ rawEventId?: string;
81
+ observed: boolean;
82
+ };
83
+ traceCompleteness: TraceCompleteness;
84
+ payload: Record<string, unknown> | null;
85
+ raw: unknown;
86
+ redaction: {
87
+ applied: boolean;
88
+ ruleIds: string[];
89
+ };
90
+ annotations: Record<string, string | number | boolean>;
91
+ };
92
+
93
+ export type AgentTraceSummary = {
94
+ traceVersion: "1";
95
+ runId: string;
96
+ workflowPath?: string;
97
+ workflowHash?: string;
98
+ nodeId: string;
99
+ iteration: number;
100
+ attempt: number;
101
+ traceStartedAtMs: number;
102
+ traceFinishedAtMs: number;
103
+ agentFamily: AgentFamily;
104
+ agentId?: string;
105
+ model?: string;
106
+ captureMode: AgentCaptureMode;
107
+ traceCompleteness: TraceCompleteness;
108
+ unsupportedEventKinds: CanonicalAgentTraceEventKind[];
109
+ missingExpectedEventKinds: CanonicalAgentTraceEventKind[];
110
+ rawArtifactRefs: string[];
111
+ };
112
+
113
+ export type AgentSessionTranscriptEvent = {
114
+ transcriptVersion: "1";
115
+ runId: string;
116
+ workflowPath?: string;
117
+ workflowHash?: string;
118
+ nodeId: string;
119
+ iteration: number;
120
+ attempt: number;
121
+ timestampMs: number;
122
+ event: {
123
+ sequence: number;
124
+ rowType: string;
125
+ };
126
+ source: {
127
+ agentFamily: AgentFamily;
128
+ captureMode: AgentCaptureMode;
129
+ ingestSource: "live" | "artifact";
130
+ observedLive: boolean;
131
+ providerSessionId?: string;
132
+ providerThreadId?: string;
133
+ };
134
+ raw: unknown;
135
+ redaction: {
136
+ applied: boolean;
137
+ ruleIds: string[];
138
+ };
139
+ annotations: Record<string, string | number | boolean>;
140
+ };
141
+
142
+ export type AgentTraceCapabilityProfile = {
143
+ sessionMetadata: boolean;
144
+ assistantTextDeltas: boolean;
145
+ visibleThinkingDeltas: boolean;
146
+ finalAssistantMessage: boolean;
147
+ toolExecutionStart: boolean;
148
+ toolExecutionUpdate: boolean;
149
+ toolExecutionEnd: boolean;
150
+ retryEvents: boolean;
151
+ compactionEvents: boolean;
152
+ rawStderrDiagnostics: boolean;
153
+ persistedSessionArtifact: boolean;
154
+ };