@posthog/ai 8.14.1 → 8.14.2

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 (48) hide show
  1. package/README.md +35 -1
  2. package/dist/adk/index.cjs +16 -6
  3. package/dist/adk/index.cjs.map +1 -1
  4. package/dist/adk/index.mjs +16 -6
  5. package/dist/adk/index.mjs.map +1 -1
  6. package/dist/anthropic/index.cjs +156 -15
  7. package/dist/anthropic/index.cjs.map +1 -1
  8. package/dist/anthropic/index.d.ts +1 -0
  9. package/dist/anthropic/index.mjs +156 -15
  10. package/dist/anthropic/index.mjs.map +1 -1
  11. package/dist/claude-agent-sdk/index.cjs +111 -37
  12. package/dist/claude-agent-sdk/index.cjs.map +1 -1
  13. package/dist/claude-agent-sdk/index.mjs +111 -37
  14. package/dist/claude-agent-sdk/index.mjs.map +1 -1
  15. package/dist/gemini/index.cjs +175 -39
  16. package/dist/gemini/index.cjs.map +1 -1
  17. package/dist/gemini/index.d.ts +1 -1
  18. package/dist/gemini/index.mjs +175 -39
  19. package/dist/gemini/index.mjs.map +1 -1
  20. package/dist/index.cjs +15 -8
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.mjs +15 -8
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/langchain/index.cjs +15 -5
  25. package/dist/langchain/index.cjs.map +1 -1
  26. package/dist/langchain/index.mjs +15 -5
  27. package/dist/langchain/index.mjs.map +1 -1
  28. package/dist/langchain/middleware/index.cjs +15 -5
  29. package/dist/langchain/middleware/index.cjs.map +1 -1
  30. package/dist/langchain/middleware/index.mjs +15 -5
  31. package/dist/langchain/middleware/index.mjs.map +1 -1
  32. package/dist/openai/index.cjs +169 -13
  33. package/dist/openai/index.cjs.map +1 -1
  34. package/dist/openai/index.mjs +169 -13
  35. package/dist/openai/index.mjs.map +1 -1
  36. package/dist/openai-agents/index.cjs +15 -5
  37. package/dist/openai-agents/index.cjs.map +1 -1
  38. package/dist/openai-agents/index.mjs +15 -5
  39. package/dist/openai-agents/index.mjs.map +1 -1
  40. package/dist/otel/index.cjs +12 -3
  41. package/dist/otel/index.cjs.map +1 -1
  42. package/dist/otel/index.mjs +12 -3
  43. package/dist/otel/index.mjs.map +1 -1
  44. package/dist/vercel/index.cjs +15 -8
  45. package/dist/vercel/index.cjs.map +1 -1
  46. package/dist/vercel/index.mjs +15 -8
  47. package/dist/vercel/index.mjs.map +1 -1
  48. package/package.json +2 -2
@@ -2,7 +2,7 @@ import { SYSTEM_PROMPT_DYNAMIC_BOUNDARY, query as query$1 } from "@anthropic-ai/
2
2
  import { v4 } from "uuid";
3
3
  import { toJsonSafeValue, uuidv7 } from "@posthog/core";
4
4
  //#region package.json
5
- var version = "8.14.1";
5
+ var version = "8.14.2";
6
6
  //#endregion
7
7
  //#region src/captureAiEvent.ts
8
8
  /** @internal */
@@ -169,17 +169,26 @@ var BinaryContentRedactor = class {
169
169
  this.visited = /* @__PURE__ */ new WeakSet();
170
170
  return this.walk(value, mediaType ? new MediaTypeContext(void 0, void 0, mediaType) : MediaTypeContext.EMPTY);
171
171
  }
172
+ /** Redact one leaf while a caller owns bounded, type-preserving traversal. @internal */
173
+ redactLeaf(value, context) {
174
+ return typeof value === "string" ? this.redactString(value, context) : this.placeholderFor(context.inferMediaType());
175
+ }
172
176
  walk(value, ctx) {
173
177
  if (value === null || value === void 0) return value;
174
- if (typeof value === "string") return this.redactString(value, ctx);
178
+ if (typeof value === "string") return this.redactLeaf(value, ctx);
175
179
  if (typeof value !== "object") return value;
176
- if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.placeholderFor(ctx.inferMediaType());
180
+ if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.redactLeaf(value, ctx);
177
181
  if (this.visited.has(value)) return null;
178
182
  this.visited.add(value);
179
183
  if (Array.isArray(value)) return value.map((item) => this.walk(item, ctx));
180
184
  const obj = value;
181
185
  const out = {};
182
- for (const k of Object.keys(obj)) out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
186
+ for (const k of Object.keys(obj)) Object.defineProperty(out, k, {
187
+ value: this.walk(obj[k], new MediaTypeContext(obj, k)),
188
+ enumerable: true,
189
+ writable: true,
190
+ configurable: true
191
+ });
183
192
  return out;
184
193
  }
185
194
  redactString(value, ctx) {
@@ -199,11 +208,7 @@ var BinaryContentRedactor = class {
199
208
  return `[base64 ${mediaType} redacted]`;
200
209
  }
201
210
  };
202
- //#endregion
203
- //#region src/sanitization.ts
204
- const redactor = new BinaryContentRedactor();
205
- const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
206
- const sanitizeAnthropic = (data, client) => sanitize(data, client);
211
+ new BinaryContentRedactor();
207
212
  //#endregion
208
213
  //#region src/utils.ts
209
214
  const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
@@ -252,9 +257,7 @@ function toContentString(content) {
252
257
  }
253
258
  return String(content);
254
259
  }
255
- const withPrivacyMode = (client, privacyMode, input) => {
256
- return client.privacy_mode || privacyMode ? null : input;
257
- };
260
+ const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
258
261
  function toSafeString(input) {
259
262
  if (input === void 0 || input === null) return "";
260
263
  if (typeof input === "string") return input;
@@ -370,7 +373,7 @@ const captureAiGeneration = async (client, options) => {
370
373
  const eventType = options.eventType ?? "$ai_generation";
371
374
  const privacyMode = options.privacyMode ?? false;
372
375
  const usage = options.usage ?? {};
373
- const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
376
+ const shouldRedact = isPrivacyModeEnabled(client, privacyMode);
374
377
  const safeInput = shouldRedact ? null : toJsonSafeValue(options.input);
375
378
  const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output);
376
379
  let httpStatus = options.httpStatus;
@@ -452,22 +455,92 @@ const captureAiGeneration = async (client, options) => {
452
455
  }
453
456
  };
454
457
  //#endregion
455
- //#region src/claude-agent-sdk/formatting.ts
456
- /**
457
- * Tool results in an agent run carry whole files and command output, and the
458
- * same result is replayed as input on every later turn, so each string is
459
- * capped before it reaches the event.
460
- */
461
- const TOOL_RESULT_MAX_STRING_LENGTH = 5e3;
462
- function capStrings(value, max) {
463
- if (typeof value === "string") return truncate(value, void 0, max);
464
- if (Array.isArray(value)) return value.map((item) => capStrings(item, max));
465
- if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, capStrings(item, max)]));
466
- return value;
458
+ //#region src/toolResult.ts
459
+ const MAX_DEPTH = 20;
460
+ const MAX_ITEMS = 1e3;
461
+ const MAX_NODES = 1e4;
462
+ const TRUNCATED = "[Truncated]";
463
+ const UNSERIALIZABLE = "[Unserializable]";
464
+ const redactor = new BinaryContentRedactor();
465
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
466
+ /** @internal Preserve tool-result structure while bounding each string value. */
467
+ function formatToolResult(content, client, maxBytes = 5e3) {
468
+ try {
469
+ if (isFullAiCaptureEnabled(client)) return toJsonSafeValue(content);
470
+ const ancestors = /* @__PURE__ */ new WeakSet();
471
+ let remainingNodes = MAX_NODES;
472
+ const convert = (value, depth, context) => {
473
+ if (remainingNodes <= 0) return TRUNCATED;
474
+ remainingNodes--;
475
+ try {
476
+ if (value === null || typeof value !== "object") {
477
+ const safe = toJsonSafeValue(value);
478
+ return typeof safe === "string" ? truncate(redactor.redactLeaf(safe, context), void 0, maxBytes) : safe;
479
+ }
480
+ if (depth >= MAX_DEPTH) return TRUNCATED;
481
+ if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return redactor.redactLeaf(value, context);
482
+ if (ancestors.has(value)) return "[Circular]";
483
+ ancestors.add(value);
484
+ try {
485
+ if (value instanceof Date) {
486
+ const safe = toJsonSafeValue(value);
487
+ return typeof safe === "string" ? truncate(redactor.redactLeaf(safe, context), void 0, maxBytes) : safe;
488
+ }
489
+ let hasToJSONResult = false;
490
+ let toJSONResult;
491
+ try {
492
+ const toJSON = value.toJSON;
493
+ if (typeof toJSON === "function") {
494
+ toJSONResult = toJSON.call(value);
495
+ hasToJSONResult = true;
496
+ }
497
+ } catch {}
498
+ if (hasToJSONResult) return convert(toJSONResult, depth + 1, context);
499
+ if (Array.isArray(value)) {
500
+ const output = [];
501
+ const count = Math.min(value.length, MAX_ITEMS);
502
+ let index = 0;
503
+ for (; index < count && remainingNodes > 0; index++) output.push(convert(value[index], depth + 1, context));
504
+ if (value.length > index) output.push(TRUNCATED);
505
+ return output;
506
+ }
507
+ const input = value;
508
+ const output = {};
509
+ let count = 0;
510
+ let truncated = false;
511
+ for (const key in input) {
512
+ if (!propertyIsEnumerable.call(input, key)) break;
513
+ if (count >= MAX_ITEMS || remainingNodes <= 0) {
514
+ truncated = true;
515
+ break;
516
+ }
517
+ const converted = convert(input[key], depth + 1, new MediaTypeContext(input, key));
518
+ Object.defineProperty(output, key, {
519
+ value: converted,
520
+ enumerable: true,
521
+ writable: true,
522
+ configurable: true
523
+ });
524
+ count++;
525
+ }
526
+ if (truncated) output[TRUNCATED] = "Additional properties omitted";
527
+ return output;
528
+ } finally {
529
+ ancestors.delete(value);
530
+ }
531
+ } catch {
532
+ return UNSERIALIZABLE;
533
+ }
534
+ };
535
+ return convert(content, 0, MediaTypeContext.EMPTY);
536
+ } catch {
537
+ return UNSERIALIZABLE;
538
+ }
467
539
  }
540
+ //#endregion
541
+ //#region src/claude-agent-sdk/formatting.ts
468
542
  function formatContent(content, client, maxBytes = MAX_OUTPUT_SIZE) {
469
- const redacted = sanitizeAnthropic(toJsonSafeValue(content), client);
470
- return isFullAiCaptureEnabled(client) ? redacted : capStrings(redacted, maxBytes);
543
+ return formatToolResult(content, client, maxBytes);
471
544
  }
472
545
  /** Read the system prompt out of the SDK options, whatever shape it takes. */
473
546
  function extractSystemPrompt(options) {
@@ -506,7 +579,8 @@ function formatAssistantBlocks(blocks, client) {
506
579
  return content;
507
580
  }
508
581
  /** Convert an Anthropic user message body into PostHog content items. */
509
- function formatUserContent(content, client) {
582
+ function formatUserContent(content, client, privacyMode = false) {
583
+ if (isPrivacyModeEnabled(client, privacyMode)) return [];
510
584
  if (typeof content === "string") return content;
511
585
  if (!Array.isArray(content)) return [];
512
586
  const formatted = [];
@@ -515,7 +589,7 @@ function formatUserContent(content, client) {
515
589
  if (block.type === "tool_result") formatted.push({
516
590
  type: "tool_result",
517
591
  tool_use_id: block.tool_use_id,
518
- content: formatContent(block.content, client, TOOL_RESULT_MAX_STRING_LENGTH),
592
+ content: formatToolResult(block.content, client),
519
593
  ...typeof block.is_error === "boolean" ? { is_error: block.is_error } : {}
520
594
  });
521
595
  else if (typeof block.text === "string") formatted.push({
@@ -819,7 +893,7 @@ var PostHogClaudeAgentProcessor = class {
819
893
  yield message;
820
894
  }
821
895
  }
822
- _beginTurn(state, userMessageUuid) {
896
+ _beginTurn(state, trace, userMessageUuid) {
823
897
  if (state.turnActive) return;
824
898
  state.turnActive = true;
825
899
  let end = userMessageUuid ? state.pendingPrompts.findIndex((message) => message.uuid === userMessageUuid) : -1;
@@ -833,7 +907,7 @@ var PostHogClaudeAgentProcessor = class {
833
907
  while (start > 0 && state.pendingPrompts[start - 1].shouldQuery === false) start -= 1;
834
908
  for (const message of state.pendingPrompts.splice(start, end - start + 1)) state.tracker.appendPendingInput([{
835
909
  role: "user",
836
- content: formatUserContent(message.message.content, this._client)
910
+ content: formatUserContent(message.message.content, this._client, trace.privacyMode)
837
911
  }]);
838
912
  }
839
913
  async _handleMessage(message, state, trace) {
@@ -845,11 +919,11 @@ var PostHogClaudeAgentProcessor = class {
845
919
  } else if (message.type === "stream_event") {
846
920
  if (message.parent_tool_use_id) return;
847
921
  if (message.user_message_uuid) state.userMessageUuid = message.user_message_uuid;
848
- if (message.event.type === "message_start") this._beginTurn(state, message.user_message_uuid);
922
+ if (message.event.type === "message_start") this._beginTurn(state, trace, message.user_message_uuid);
849
923
  state.tracker.processStreamEvent(message.event, this._client, message.ttft_ms);
850
924
  await this._captureCompletedGenerations(state, trace);
851
925
  } else if (message.type === "assistant") {
852
- if (!message.parent_tool_use_id) this._beginTurn(state, message.user_message_uuid);
926
+ if (!message.parent_tool_use_id) this._beginTurn(state, trace, message.user_message_uuid);
853
927
  this._handleAssistantMessage(message, state, trace);
854
928
  } else if (message.type === "user") {
855
929
  if ("isReplay" in message && message.isReplay) return;
@@ -857,13 +931,13 @@ var PostHogClaudeAgentProcessor = class {
857
931
  for (const block of message.message.content) if (block.type === "tool_result") await this._finishToolSpan(block.tool_use_id, state, trace, block);
858
932
  }
859
933
  if (message.parent_tool_use_id) return;
860
- const content = formatUserContent(message.message?.content, this._client);
934
+ const content = formatUserContent(message.message?.content, this._client, trace.privacyMode);
861
935
  if (typeof content === "string" || Array.isArray(content) && content.length > 0) state.tracker.appendPendingInput([{
862
936
  role: "user",
863
937
  content
864
938
  }]);
865
939
  } else if (message.type === "result") try {
866
- this._beginTurn(state, message.user_message_uuid);
940
+ this._beginTurn(state, trace, message.user_message_uuid);
867
941
  state.tracker.finishCurrent();
868
942
  await this._captureCompletedGenerations(state, trace, resultError(message), message);
869
943
  if (message.total_cost_usd != null) {
@@ -953,7 +1027,7 @@ var PostHogClaudeAgentProcessor = class {
953
1027
  ...parentSpanId ? { $ai_parent_id: parentSpanId } : {},
954
1028
  $ai_span_name: block.name,
955
1029
  $ai_span_type: "tool",
956
- $ai_input_state: withPrivacyMode(this._client, trace.privacyMode ?? false, formatContent(block.input ?? {}, this._client))
1030
+ $ai_input_state: isPrivacyModeEnabled(this._client, trace.privacyMode ?? false) ? null : formatContent(block.input ?? {}, this._client)
957
1031
  };
958
1032
  state.pendingTools.set(properties.$ai_span_id, {
959
1033
  startTime: performance.now(),
@@ -968,7 +1042,7 @@ var PostHogClaudeAgentProcessor = class {
968
1042
  ...tool.properties,
969
1043
  $ai_latency: (performance.now() - tool.startTime) / 1e3,
970
1044
  ...result ? {
971
- $ai_output_state: withPrivacyMode(this._client, trace.privacyMode ?? false, formatContent(result.content, this._client)),
1045
+ $ai_output_state: isPrivacyModeEnabled(this._client, trace.privacyMode ?? false) ? null : formatContent(result.content, this._client),
972
1046
  $ai_is_error: result.is_error === true
973
1047
  } : {}
974
1048
  };