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