pi-langfuse 1.4.2 → 1.4.4

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/src/config.ts CHANGED
@@ -2,18 +2,25 @@ import { mkdirSync, readFileSync, existsSync, writeFileSync } from "node:fs";
2
2
  import type { Config } from "./types.js";
3
3
  import { CONFIG_DIR, CONFIG_PATH, DEFAULT_LANGFUSE_HOST } from "./constants.js";
4
4
  import { state } from "./state.js";
5
- import { shutdownRuntime } from "./langfuse.js";
5
+ import { forceShutdownRuntime } from "./langfuse.js";
6
+ import { createCapturePolicy, type EnvLike } from "./capture-policy.js";
6
7
 
7
- export function loadConfigFromFile(): Config | null {
8
- if (existsSync(CONFIG_PATH)) {
8
+ export function loadConfigFromFile(path = CONFIG_PATH, env: EnvLike = process.env as EnvLike): Config | null {
9
+ if (existsSync(path)) {
9
10
  try {
10
- const content = readFileSync(CONFIG_PATH, "utf-8");
11
- const config = JSON.parse(content) as Config;
11
+ const content = readFileSync(path, "utf-8");
12
+ const config = JSON.parse(content) as Config & { capture?: EnvLike; privacyPreset?: string };
12
13
  if (config.publicKey && config.secretKey) {
14
+ const captureSource: EnvLike = {
15
+ ...(config.capture ?? {}),
16
+ ...(config.privacyPreset ? { LANGFUSE_PRIVACY_PRESET: config.privacyPreset } : {}),
17
+ ...env,
18
+ };
13
19
  return {
14
20
  publicKey: config.publicKey,
15
21
  secretKey: config.secretKey,
16
22
  host: config.host || DEFAULT_LANGFUSE_HOST,
23
+ capturePolicy: createCapturePolicy(captureSource),
17
24
  };
18
25
  }
19
26
  } catch (e) {
@@ -24,9 +31,9 @@ export function loadConfigFromFile(): Config | null {
24
31
  return null;
25
32
  }
26
33
 
27
- export function loadConfigFromEnv(): Config | null {
28
- const publicKey = process.env.LANGFUSE_PUBLIC_KEY || "";
29
- const secretKey = process.env.LANGFUSE_SECRET_KEY || "";
34
+ export function loadConfigFromEnv(env: EnvLike = process.env as EnvLike): Config | null {
35
+ const publicKey = env.LANGFUSE_PUBLIC_KEY || "";
36
+ const secretKey = env.LANGFUSE_SECRET_KEY || "";
30
37
  if (!publicKey || !secretKey) {
31
38
  return null;
32
39
  }
@@ -34,12 +41,13 @@ export function loadConfigFromEnv(): Config | null {
34
41
  return {
35
42
  publicKey,
36
43
  secretKey,
37
- host: process.env.LANGFUSE_BASE_URL || process.env.LANGFUSE_HOST || DEFAULT_LANGFUSE_HOST,
44
+ host: env.LANGFUSE_BASE_URL || env.LANGFUSE_HOST || DEFAULT_LANGFUSE_HOST,
45
+ capturePolicy: createCapturePolicy(env),
38
46
  };
39
47
  }
40
48
 
41
- export function loadConfig(): Config | null {
42
- return loadConfigFromFile() || loadConfigFromEnv();
49
+ export function loadConfig(env: EnvLike = process.env as EnvLike, path = CONFIG_PATH): Config | null {
50
+ return loadConfigFromFile(path, env) || loadConfigFromEnv(env);
43
51
  }
44
52
 
45
53
  export function saveConfig(config: Config) {
@@ -72,6 +80,7 @@ async function collectConfigFromUI(ctx: any, reason: string): Promise<Config | n
72
80
  publicKey,
73
81
  secretKey,
74
82
  host: hostInput || DEFAULT_LANGFUSE_HOST,
83
+ capturePolicy: createCapturePolicy(),
75
84
  };
76
85
  }
77
86
 
@@ -115,7 +124,7 @@ export async function ensureConfig(ctx: any): Promise<boolean> {
115
124
  export async function promptForConfig(ctx: any): Promise<boolean> {
116
125
  state.setupAttemptedThisSession = false;
117
126
  state.config = null;
118
- await shutdownRuntime();
127
+ await forceShutdownRuntime();
119
128
 
120
129
  const config = await collectConfigFromUI(ctx, "Manual setup requested");
121
130
  if (!config) {
package/src/constants.ts CHANGED
@@ -10,3 +10,4 @@ export const MAX_TOOL_PAYLOAD_LENGTH = 24_000;
10
10
  export const MAX_DEPTH = 6;
11
11
  export const MAX_ARRAY_ITEMS = 50;
12
12
  export const MAX_OBJECT_KEYS = 80;
13
+ export const MAX_PAYLOAD_NODES = 2_000;
@@ -1,8 +1,25 @@
1
1
  import { state, resetRunState, computeEvaluationScores } from "../state.js";
2
2
  import { getRuntime, sendScore } from "../langfuse.js";
3
3
  import { ensureConfig } from "../config.js";
4
- import { shapePayload, truncate, extractFinalAssistant, extractAssistantOutput } from "../utils.js";
4
+ import { shapePayload, truncate, extractFinalAssistant, extractAssistantOutput, getCapturePolicy } from "../utils.js";
5
5
  import { closeDanglingObservations } from "./tool.js";
6
+ import { applyCapturePolicy } from "../capture-policy.js";
7
+
8
+ function stringMetadata(metadata: Record<string, unknown> | undefined): Record<string, string> | undefined {
9
+ if (!metadata) {
10
+ return undefined;
11
+ }
12
+
13
+ const output: Record<string, string> = {};
14
+ for (const [key, value] of Object.entries(metadata)) {
15
+ if (typeof value === "string") {
16
+ output[key] = value;
17
+ } else if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
18
+ output[key] = String(value);
19
+ }
20
+ }
21
+ return Object.keys(output).length > 0 ? output : undefined;
22
+ }
6
23
 
7
24
  export function updateTraceIO(input?: unknown, output?: unknown) {
8
25
  const root = state.agentState?.root;
@@ -19,6 +36,7 @@ export function updateTraceIO(input?: unknown, output?: unknown) {
19
36
 
20
37
  export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
21
38
  if (!(await ensureConfig(ctx))) {
39
+ state.isTracingDisabled = true;
22
40
  return;
23
41
  }
24
42
 
@@ -44,15 +62,28 @@ export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
44
62
  // Ignore if getSystemPrompt is not available or fails
45
63
  }
46
64
 
47
- const promptInput = shapePayload({
65
+ const rawPromptInput = shapePayload({
48
66
  prompt: event.prompt,
49
67
  images: event.images,
50
68
  context: event.context ?? event.attachments,
51
69
  });
70
+ const captured = applyCapturePolicy(
71
+ {
72
+ input: rawPromptInput,
73
+ metadata: {
74
+ cwd,
75
+ ...(state.currentModel ? { model: state.currentModel } : {}),
76
+ ...(state.currentProvider ? { provider: state.currentProvider } : {}),
77
+ sessionId: state.currentSessionId || undefined,
78
+ },
79
+ systemPrompt: systemPrompt ? truncate(String(systemPrompt), 20000) : undefined,
80
+ },
81
+ getCapturePolicy(),
82
+ );
52
83
 
53
84
  state.agentState = {
54
85
  cwd,
55
- promptInput,
86
+ promptInput: captured.input,
56
87
  generationSeq: 0,
57
88
  activeGenerations: new Map(),
58
89
  generationOrder: [],
@@ -64,34 +95,28 @@ export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
64
95
  {
65
96
  sessionId: state.currentSessionId ? truncate(state.currentSessionId, 200) : undefined,
66
97
  traceName: "pi-agent",
67
- metadata: {
68
- cwd: truncate(cwd, 200),
69
- ...(state.currentModel ? { model: truncate(state.currentModel, 200) } : {}),
70
- ...(state.currentProvider ? { provider: truncate(state.currentProvider, 200) } : {}),
71
- },
98
+ metadata: stringMetadata(captured.metadata),
72
99
  },
73
100
  () =>
74
101
  rt.startObservation(
75
102
  "pi-agent",
76
- {
77
- input: promptInput,
78
- metadata: {
79
- cwd,
80
- model: state.currentModel || undefined,
81
- provider: state.currentProvider || undefined,
82
- sessionId: state.currentSessionId || undefined,
83
- ...(systemPrompt ? { systemPrompt: truncate(String(systemPrompt), 20000) } : {}),
103
+ {
104
+ input: captured.input,
105
+ metadata: {
106
+ ...(captured.metadata ?? {}),
107
+ ...(captured.systemPrompt ? { systemPrompt: captured.systemPrompt } : {}),
108
+ },
84
109
  },
85
- },
86
110
  { asType: "agent" },
87
111
  ),
88
112
  );
89
113
 
90
114
  state.agentState.root = root;
91
115
  state.agentState.traceId = root.traceId;
92
- updateTraceIO(promptInput, undefined);
116
+ updateTraceIO(captured.input, undefined);
93
117
  } catch (e) {
94
118
  console.warn("📊 Langfuse: Failed to create agent observation", e);
119
+ state.isTracingDisabled = true;
95
120
  }
96
121
  }
97
122
 
@@ -102,7 +127,21 @@ export async function finishAgentRun(event: Record<string, unknown> = {}) {
102
127
  }
103
128
 
104
129
  const lastAssistant = extractFinalAssistant(event.messages);
105
- const output = lastAssistant ? extractAssistantOutput(lastAssistant) : state.agentState.latestAssistantOutput;
130
+ const rawOutput = lastAssistant ? extractAssistantOutput(lastAssistant) : state.agentState.latestAssistantOutput;
131
+ const captured = applyCapturePolicy(
132
+ {
133
+ output: rawOutput,
134
+ metadata: {
135
+ cwd: state.agentState.cwd,
136
+ completed: true,
137
+ model: state.currentModel || undefined,
138
+ provider: state.currentProvider || undefined,
139
+ totalTools: state.toolCallCount,
140
+ ...computeEvaluationScores(),
141
+ },
142
+ },
143
+ getCapturePolicy(),
144
+ );
106
145
  const scores = computeEvaluationScores();
107
146
 
108
147
  closeDanglingObservations("Agent run ended before observation finalized");
@@ -110,18 +149,11 @@ export async function finishAgentRun(event: Record<string, unknown> = {}) {
110
149
  try {
111
150
  state.agentState.root
112
151
  .update({
113
- output,
114
- metadata: {
115
- cwd: state.agentState.cwd,
116
- completed: true,
117
- model: state.currentModel || undefined,
118
- provider: state.currentProvider || undefined,
119
- totalTools: state.toolCallCount,
120
- ...scores,
121
- },
152
+ output: captured.output,
153
+ metadata: captured.metadata,
122
154
  })
123
155
  .end();
124
- updateTraceIO(state.agentState.promptInput, output);
156
+ updateTraceIO(state.agentState.promptInput, captured.output);
125
157
 
126
158
  await sendScore("tool_call_count", scores.tool_call_count, { traceId: state.agentState.traceId });
127
159
  await sendScore("turn_count", scores.turn_count, { traceId: state.agentState.traceId });
@@ -1,5 +1,6 @@
1
1
  import { state } from "../state.js";
2
2
  import { getRuntime } from "../langfuse.js";
3
+ import { startChildObservation } from "../observation.js";
3
4
  import {
4
5
  getRequestKey,
5
6
  getProviderPayload,
@@ -9,11 +10,13 @@ import {
9
10
  extractAssistantOutput,
10
11
  extractUsage,
11
12
  extractCostDetails,
13
+ getCapturePolicy,
12
14
  } from "../utils.js";
13
15
  import type { GenerationState, ObservationUpdate } from "../types.js";
16
+ import { applyCapturePolicy } from "../capture-policy.js";
14
17
 
15
18
  export function getOpenGeneration(): GenerationState | undefined {
16
- if (!state.agentState) {
19
+ if (state.isTracingDisabled || !state.agentState) {
17
20
  return undefined;
18
21
  }
19
22
 
@@ -29,7 +32,7 @@ export function getOpenGeneration(): GenerationState | undefined {
29
32
  }
30
33
 
31
34
  export async function startGeneration(event: Record<string, unknown>) {
32
- if (!state.agentState?.root) {
35
+ if (state.isTracingDisabled || !state.agentState?.root) {
33
36
  return;
34
37
  }
35
38
 
@@ -44,33 +47,32 @@ export async function startGeneration(event: Record<string, unknown>) {
44
47
  url: event.url,
45
48
  method: event.method,
46
49
  }) as Record<string, unknown>;
50
+ const captured = applyCapturePolicy(
51
+ {
52
+ input: shapePayload(payload),
53
+ metadata,
54
+ },
55
+ getCapturePolicy(),
56
+ );
47
57
 
48
58
  const parent = state.agentState.activeTurn ?? state.agentState.root;
49
- const generation = parent.startObservation
50
- ? parent.startObservation(
51
- "llm-generation",
52
- {
53
- input: shapePayload(payload),
54
- model: model || undefined,
55
- metadata,
56
- },
57
- { asType: "generation" },
58
- )
59
- : (await getRuntime()).startObservation(
60
- "llm-generation",
61
- {
62
- input: shapePayload(payload),
63
- model: model || undefined,
64
- metadata,
65
- },
66
- { asType: "generation" },
67
- );
59
+ const generation = await startChildObservation({
60
+ parent,
61
+ runtime: getRuntime,
62
+ name: "llm-generation",
63
+ body: {
64
+ input: captured.input,
65
+ model: model || undefined,
66
+ metadata: captured.metadata,
67
+ },
68
+ asType: "generation",
69
+ });
68
70
 
69
71
  state.agentState.activeGenerations.set(key, {
70
72
  observation: generation,
71
73
  requestKey: key,
72
74
  ended: false,
73
- metadata,
75
+ metadata: captured.metadata ?? {},
74
76
  });
75
77
  state.agentState.generationOrder.push(key);
76
78
  } catch (e) {
@@ -79,12 +81,12 @@ export async function startGeneration(event: Record<string, unknown>) {
79
81
  }
80
82
 
81
83
  export function updateGenerationMetadata(event: Record<string, unknown>) {
82
- if (!state.agentState) {
84
+ if (state.isTracingDisabled || !state.agentState) {
83
85
  return;
84
86
  }
85
87
 
86
88
  const key = getRequestKey(event, "");
87
- const metadata = extractResponseMetadata(event);
89
+ const metadata = applyCapturePolicy({ metadata: extractResponseMetadata(event) }, getCapturePolicy()).metadata ?? {};
88
90
  if (!key) {
89
91
  const generation = getOpenGeneration();
90
92
  if (generation) {
@@ -132,7 +134,7 @@ export function updateGenerationMetadata(event: Record<string, unknown>) {
132
134
  }
133
135
 
134
136
  export function recordTTFT(event: Record<string, unknown>) {
135
- if (!state.agentState) {
137
+ if (state.isTracingDisabled || !state.agentState) {
136
138
  return;
137
139
  }
138
140
 
@@ -150,7 +152,7 @@ export function recordTTFT(event: Record<string, unknown>) {
150
152
  }
151
153
 
152
154
  export async function finishGenerationFromMessage(event: Record<string, unknown>) {
153
- if (!state.agentState) {
155
+ if (state.isTracingDisabled || !state.agentState) {
154
156
  return;
155
157
  }
156
158
 
@@ -160,7 +162,9 @@ export async function finishGenerationFromMessage(event: Record<string, unknown>
160
162
  }
161
163
 
162
164
  const generation = getOpenGeneration();
163
- const output = extractAssistantOutput(message);
165
+ const rawOutput = extractAssistantOutput(message);
166
+ const captured = applyCapturePolicy({ output: rawOutput }, getCapturePolicy());
167
+ const output = captured.output;
164
168
  state.agentState.latestAssistantOutput = output;
165
169
 
166
170
  if (!generation) {
@@ -180,6 +184,7 @@ export async function finishGenerationFromMessage(event: Record<string, unknown>
180
184
  finishReason: message.finishReason ?? message.stopReason ?? event.finishReason,
181
185
  },
182
186
  };
187
+ update.metadata = applyCapturePolicy({ metadata: update.metadata }, getCapturePolicy()).metadata;
183
188
 
184
189
  try {
185
190
  generation.observation.update(update).end();
@@ -190,7 +195,7 @@ export async function finishGenerationFromMessage(event: Record<string, unknown>
190
195
  }
191
196
 
192
197
  export async function createFallbackGenerationFromTurn(event: Record<string, unknown>, message: Record<string, unknown>) {
193
- if (!state.agentState?.root || state.agentState.generationOrder.length > 0) {
198
+ if (state.isTracingDisabled || !state.agentState?.root || state.agentState.generationOrder.length > 0) {
194
199
  return;
195
200
  }
196
201
 
@@ -198,38 +203,32 @@ export async function createFallbackGenerationFromTurn(event: Record<string, unk
198
203
  const usageDetails = extractUsage({ ...event, message });
199
204
  const costDetails = extractCostDetails({ ...event, message });
200
205
  const model = String(message.model ?? event.model ?? state.currentModel ?? "");
206
+ const captured = applyCapturePolicy(
207
+ {
208
+ input: state.agentState.promptInput,
209
+ output: extractAssistantOutput(message),
210
+ metadata: {
211
+ provider: state.currentProvider || undefined,
212
+ sourceEvent: "turn_end",
213
+ },
214
+ },
215
+ getCapturePolicy(),
216
+ );
201
217
  const parent = state.agentState.activeTurn ?? state.agentState.root;
202
- const generation = parent.startObservation
203
- ? parent.startObservation(
204
- "llm-generation",
205
- {
206
- input: state.agentState.promptInput,
207
- output: extractAssistantOutput(message),
208
- model: model || undefined,
209
- usageDetails,
210
- costDetails,
211
- metadata: {
212
- provider: state.currentProvider || undefined,
213
- sourceEvent: "turn_end",
214
- },
215
- },
216
- { asType: "generation" },
217
- )
218
- : (await getRuntime()).startObservation(
219
- "llm-generation",
220
- {
221
- input: state.agentState.promptInput,
222
- output: extractAssistantOutput(message),
223
- model: model || undefined,
224
- usageDetails,
225
- costDetails,
226
- metadata: {
227
- provider: state.currentProvider || undefined,
228
- sourceEvent: "turn_end",
229
- },
230
- },
231
- { asType: "generation" },
232
- );
218
+ const generation = await startChildObservation({
219
+ parent,
220
+ runtime: getRuntime,
221
+ name: "llm-generation",
222
+ body: {
223
+ input: captured.input,
224
+ output: captured.output,
225
+ model: model || undefined,
226
+ usageDetails,
227
+ costDetails,
228
+ metadata: captured.metadata,
229
+ },
230
+ asType: "generation",
231
+ });
233
232
 
234
233
  generation.end();
235
234
  state.agentState.generationOrder.push("turn-end-fallback");
@@ -1,5 +1,6 @@
1
1
  import { state } from "../state.js";
2
2
  import { getRuntime, sendScore } from "../langfuse.js";
3
+ import { startChildObservation } from "../observation.js";
3
4
  import {
4
5
  getToolCallId,
5
6
  getToolName,
@@ -8,11 +9,14 @@ import {
8
9
  extractTextContent,
9
10
  truncate,
10
11
  estimatePayloadBytes,
12
+ getCapturePolicy,
11
13
  } from "../utils.js";
12
14
  import { MAX_TOOL_PAYLOAD_LENGTH } from "../constants.js";
15
+ import { applyCapturePolicy } from "../capture-policy.js";
16
+ import { redactString } from "../redaction.js";
13
17
 
14
18
  export async function startToolObservation(event: Record<string, unknown>) {
15
- if (!state.agentState?.root) {
19
+ if (state.isTracingDisabled || !state.agentState?.root) {
16
20
  return;
17
21
  }
18
22
 
@@ -25,25 +29,25 @@ export async function startToolObservation(event: Record<string, unknown>) {
25
29
  const toolName = getToolName(event);
26
30
  const toolInput = getToolInput(event);
27
31
  const shapedInput = shapePayload(toolInput, { maxString: MAX_TOOL_PAYLOAD_LENGTH });
28
- const inputBytes = estimatePayloadBytes(shapedInput, MAX_TOOL_PAYLOAD_LENGTH);
32
+ const captured = applyCapturePolicy(
33
+ {
34
+ toolInput: shapedInput,
35
+ metadata: { toolName, toolCallId },
36
+ },
37
+ getCapturePolicy(),
38
+ );
39
+ const inputBytes = estimatePayloadBytes(captured.toolInput, MAX_TOOL_PAYLOAD_LENGTH);
29
40
  const parent = state.agentState.activeTurn ?? state.agentState.root;
30
- const tool = parent.startObservation
31
- ? parent.startObservation(
32
- toolName,
33
- {
34
- input: shapedInput,
35
- metadata: { toolName, toolCallId, inputBytes },
36
- },
37
- { asType: "tool" },
38
- )
39
- : (await getRuntime()).startObservation(
40
- toolName,
41
- {
42
- input: shapedInput,
43
- metadata: { toolName, toolCallId, inputBytes },
44
- },
45
- { asType: "tool" },
46
- );
41
+ const tool = await startChildObservation({
42
+ parent,
43
+ runtime: getRuntime,
44
+ name: toolName,
45
+ body: {
46
+ input: captured.toolInput,
47
+ metadata: { ...(captured.metadata ?? {}), inputBytes },
48
+ },
49
+ asType: "tool",
50
+ });
47
51
 
48
52
  state.toolCallCount++;
49
53
  state.agentState.activeTools.set(toolCallId, {
@@ -59,7 +63,7 @@ export async function startToolObservation(event: Record<string, unknown>) {
59
63
  }
60
64
 
61
65
  export async function finishToolObservation(event: Record<string, unknown>) {
62
- if (!state.agentState) {
66
+ if (state.isTracingDisabled || !state.agentState) {
63
67
  return;
64
68
  }
65
69
 
@@ -84,18 +88,27 @@ export async function finishToolObservation(event: Record<string, unknown>) {
84
88
 
85
89
  try {
86
90
  const shapedOutput = shapePayload(output, { maxString: MAX_TOOL_PAYLOAD_LENGTH });
87
- const outputBytes = estimatePayloadBytes(shapedOutput, MAX_TOOL_PAYLOAD_LENGTH);
91
+ const captured = applyCapturePolicy(
92
+ {
93
+ toolOutput: shapedOutput,
94
+ metadata: {
95
+ toolName: activeTool.toolName,
96
+ toolCallId,
97
+ isError,
98
+ },
99
+ },
100
+ getCapturePolicy(),
101
+ );
102
+ const outputBytes = estimatePayloadBytes(captured.toolOutput, MAX_TOOL_PAYLOAD_LENGTH);
88
103
  const durationMs = Math.max(0, Date.now() - activeTool.startedAt);
89
104
 
90
105
  activeTool.observation
91
106
  .update({
92
- output: shapedOutput,
107
+ output: captured.toolOutput,
93
108
  level: isError ? "ERROR" : "DEFAULT",
94
- statusMessage: isError ? truncate(String(event.error ?? output), 1_000) : undefined,
109
+ statusMessage: isError ? redactString(truncate(String(event.error ?? output), 1_000)) : undefined,
95
110
  metadata: {
96
- toolName: activeTool.toolName,
97
- toolCallId,
98
- isError,
111
+ ...(captured.metadata ?? {}),
99
112
  durationMs,
100
113
  inputBytes: activeTool.inputBytes,
101
114
  outputBytes,
@@ -119,7 +132,7 @@ export async function finishToolObservation(event: Record<string, unknown>) {
119
132
  }
120
133
 
121
134
  export function closeDanglingObservations(statusMessage: string) {
122
- if (!state.agentState) {
135
+ if (state.isTracingDisabled || !state.agentState) {
123
136
  return;
124
137
  }
125
138
 
@@ -1,9 +1,11 @@
1
1
  import { state } from "../state.js";
2
2
  import { getRuntime } from "../langfuse.js";
3
- import { shapePayload } from "../utils.js";
3
+ import { startChildObservation } from "../observation.js";
4
+ import { shapePayload, getCapturePolicy } from "../utils.js";
5
+ import { applyCapturePolicy } from "../capture-policy.js";
4
6
 
5
7
  export async function startTurnObservation(event: Record<string, unknown>) {
6
- if (!state.agentState?.root) {
8
+ if (state.isTracingDisabled || !state.agentState?.root) {
7
9
  return;
8
10
  }
9
11
 
@@ -15,23 +17,23 @@ export async function startTurnObservation(event: Record<string, unknown>) {
15
17
 
16
18
  try {
17
19
  const turnIndex = event.turnIndex ?? state.turnCount;
18
- const observation = state.agentState.root.startObservation
19
- ? state.agentState.root.startObservation(
20
- "turn",
21
- {
22
- input: shapePayload(event.context ?? event),
23
- metadata: { turnIndex },
24
- },
25
- { asType: "span" },
26
- )
27
- : (await getRuntime()).startObservation(
28
- "turn",
29
- {
30
- input: shapePayload(event.context ?? event),
31
- metadata: { turnIndex },
32
- },
33
- { asType: "span" },
34
- );
20
+ const captured = applyCapturePolicy(
21
+ {
22
+ input: shapePayload(event.context ?? event),
23
+ metadata: { turnIndex },
24
+ },
25
+ getCapturePolicy(),
26
+ );
27
+ const observation = await startChildObservation({
28
+ parent: state.agentState.root,
29
+ runtime: getRuntime,
30
+ name: "turn",
31
+ body: {
32
+ input: captured.input,
33
+ metadata: captured.metadata,
34
+ },
35
+ asType: "span",
36
+ });
35
37
 
36
38
  state.agentState.activeTurn = observation;
37
39
  } catch (e) {
@@ -39,8 +41,8 @@ export async function startTurnObservation(event: Record<string, unknown>) {
39
41
  }
40
42
  }
41
43
 
42
- export function finishTurnObservation(event?: Record<string, unknown>) {
43
- if (!state.agentState?.activeTurn) {
44
+ export function finishTurnObservation(_event?: Record<string, unknown>) {
45
+ if (state.isTracingDisabled || !state.agentState?.activeTurn) {
44
46
  return;
45
47
  }
46
48