@posthog/ai 8.16.0 → 8.16.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 +12 -0
  2. package/dist/adk/index.cjs +147 -9
  3. package/dist/adk/index.cjs.map +1 -1
  4. package/dist/adk/index.mjs +147 -9
  5. package/dist/adk/index.mjs.map +1 -1
  6. package/dist/anthropic/index.cjs +141 -8
  7. package/dist/anthropic/index.cjs.map +1 -1
  8. package/dist/anthropic/index.mjs +141 -8
  9. package/dist/anthropic/index.mjs.map +1 -1
  10. package/dist/claude-agent-sdk/index.cjs +147 -9
  11. package/dist/claude-agent-sdk/index.cjs.map +1 -1
  12. package/dist/claude-agent-sdk/index.mjs +147 -9
  13. package/dist/claude-agent-sdk/index.mjs.map +1 -1
  14. package/dist/gemini/index.cjs +141 -8
  15. package/dist/gemini/index.cjs.map +1 -1
  16. package/dist/gemini/index.mjs +141 -8
  17. package/dist/gemini/index.mjs.map +1 -1
  18. package/dist/index.cjs +149 -15
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.mjs +149 -15
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/langchain/index.cjs +141 -7
  23. package/dist/langchain/index.cjs.map +1 -1
  24. package/dist/langchain/index.d.ts +1 -0
  25. package/dist/langchain/index.mjs +141 -7
  26. package/dist/langchain/index.mjs.map +1 -1
  27. package/dist/langchain/middleware/index.cjs +150 -11
  28. package/dist/langchain/middleware/index.cjs.map +1 -1
  29. package/dist/langchain/middleware/index.mjs +150 -11
  30. package/dist/langchain/middleware/index.mjs.map +1 -1
  31. package/dist/openai/index.cjs +154 -8
  32. package/dist/openai/index.cjs.map +1 -1
  33. package/dist/openai/index.mjs +154 -8
  34. package/dist/openai/index.mjs.map +1 -1
  35. package/dist/openai-agents/index.cjs +104 -3
  36. package/dist/openai-agents/index.cjs.map +1 -1
  37. package/dist/openai-agents/index.mjs +104 -3
  38. package/dist/openai-agents/index.mjs.map +1 -1
  39. package/dist/otel/index.cjs +216 -4
  40. package/dist/otel/index.cjs.map +1 -1
  41. package/dist/otel/index.d.ts +22 -1
  42. package/dist/otel/index.mjs +216 -4
  43. package/dist/otel/index.mjs.map +1 -1
  44. package/dist/vercel/index.cjs +149 -15
  45. package/dist/vercel/index.cjs.map +1 -1
  46. package/dist/vercel/index.mjs +149 -15
  47. package/dist/vercel/index.mjs.map +1 -1
  48. package/package.json +3 -3
@@ -220,6 +220,10 @@ const redactor = new BinaryContentRedactor();
220
220
  const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
221
221
  const sanitizeLangChain = (data, client) => sanitize(data, client);
222
222
  //#endregion
223
+ //#region src/privacyMode.ts
224
+ /** Per-call settings can turn redaction on but never off. */
225
+ const isPrivacyModeEnabled = (client, privacyMode = false) => privacyMode === true || client?.privacyMode === true || client?.privacy_mode === true || client?.options?.privacyMode === true;
226
+ //#endregion
223
227
  //#region src/utils.ts
224
228
  const STRING_FORMAT = "utf8";
225
229
  new TextEncoder();
@@ -269,7 +273,6 @@ const getModelParams = (params, responseServiceTier) => {
269
273
  if (responseServiceTier != null) modelParams.service_tier = responseServiceTier;
270
274
  return modelParams;
271
275
  };
272
- const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
273
276
  const withPrivacyMode = (client, privacyMode, input) => {
274
277
  return isPrivacyModeEnabled(client, privacyMode) ? null : input;
275
278
  };
@@ -282,12 +285,40 @@ function sanitizeValues(obj) {
282
285
  return jsonSafe;
283
286
  }
284
287
  //#endregion
288
+ //#region src/mcpCredentials.ts
289
+ const MCP_CREDENTIAL_FIELDS = ["headers", "authorization"];
290
+ const MCP_PROVIDER_TOOL_IDS = /* @__PURE__ */ new Set([
291
+ "openai.mcp",
292
+ "xai.mcp",
293
+ "google.mcp_server"
294
+ ]);
295
+ const isMcpProviderTool = (tool) => (tool.type === "provider" || tool.type === "provider-defined") && typeof tool.id === "string" && MCP_PROVIDER_TOOL_IDS.has(tool.id);
296
+ const withoutMcpCredentials = (config) => {
297
+ const redacted = { ...config };
298
+ for (const field of MCP_CREDENTIAL_FIELDS) if (field in redacted) redacted[field] = "[REDACTED]";
299
+ return redacted;
300
+ };
301
+ /** Hides the credentials of a remote MCP tool definition; everything else is kept as sent. */
302
+ function redactMcpToolDefinition(tool) {
303
+ if (!isObject(tool)) return tool;
304
+ if (tool.type === "mcp") return withoutMcpCredentials(tool);
305
+ if (isMcpProviderTool(tool) && isObject(tool.args)) return {
306
+ ...tool,
307
+ args: withoutMcpCredentials(tool.args)
308
+ };
309
+ return tool;
310
+ }
311
+ function redactMcpToolCredentials(tools) {
312
+ return Array.isArray(tools) ? tools.map(redactMcpToolDefinition) : tools;
313
+ }
314
+ //#endregion
285
315
  //#region package.json
286
- var version = "8.16.0";
316
+ var version = "8.16.2";
287
317
  //#endregion
288
318
  //#region src/serializeError.ts
289
319
  const DEFAULT_MAX_DEPTH = 3;
290
320
  const MAX_STACK_LINES = 20;
321
+ const OMITTED_FIELD = "requestBodyValues";
291
322
  function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
292
323
  if (depth < 0 || value === null || typeof value !== "object") return value;
293
324
  if (value instanceof Error) {
@@ -296,16 +327,17 @@ function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
296
327
  message: value.message,
297
328
  stack: truncateStack(value.stack)
298
329
  };
299
- for (const key of Object.keys(value)) out[key] = serializeError(value[key], depth - 1);
330
+ for (const key of Object.keys(value)) if (key !== OMITTED_FIELD) out[key] = serializeError(Reflect.get(value, key), depth - 1);
300
331
  if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
301
332
  return out;
302
333
  }
303
334
  if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
304
335
  return value;
305
336
  }
337
+ const omitRequestBody = (key, value) => key === OMITTED_FIELD ? void 0 : value;
306
338
  function stringifyError(error) {
307
339
  try {
308
- return JSON.stringify(sanitizeValues(serializeError(error)));
340
+ return JSON.stringify(sanitizeValues(serializeError(error)), omitRequestBody);
309
341
  } catch {
310
342
  if (error instanceof Error) return JSON.stringify({
311
343
  name: error.name,
@@ -314,6 +346,105 @@ function stringifyError(error) {
314
346
  return JSON.stringify({ message: String(error) });
315
347
  }
316
348
  }
349
+ const CLASS_NAME = /^[A-Za-z_$][A-Za-z0-9_$.]{0,127}$/;
350
+ const PROVIDER_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
351
+ const PARAM = /^[A-Za-z0-9_.[\]-]{1,128}$/;
352
+ const REQUEST_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
353
+ const MAX_CAUSES = 3;
354
+ const readField = (value, key) => {
355
+ if (value === null || typeof value !== "object" && typeof value !== "function") return;
356
+ try {
357
+ return Reflect.get(value, key);
358
+ } catch {
359
+ return;
360
+ }
361
+ };
362
+ const matching = (value, grammar) => typeof value === "string" && grammar.test(value) ? value : void 0;
363
+ const firstMatching = (values, grammar) => {
364
+ for (const value of values) {
365
+ const match = matching(value, grammar);
366
+ if (match !== void 0) return match;
367
+ }
368
+ };
369
+ const className = (value) => {
370
+ if (!(value instanceof Error)) return "NonErrorThrown";
371
+ const name = readField(value, "name");
372
+ return firstMatching([
373
+ name === "Error" ? void 0 : name,
374
+ readField(readField(value, "constructor"), "name"),
375
+ name
376
+ ], CLASS_NAME) ?? "Error";
377
+ };
378
+ const httpStatus = (error) => {
379
+ for (const key of ["status", "statusCode"]) {
380
+ const value = readField(error, key);
381
+ if (typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599) return value;
382
+ }
383
+ };
384
+ const unwrapEnvelope = (body) => {
385
+ const inner = readField(body, "error");
386
+ return inner !== null && typeof inner === "object" ? inner : body;
387
+ };
388
+ const parsedResponseBody = (error) => {
389
+ const responseBody = readField(error, "responseBody");
390
+ if (typeof responseBody !== "string") return;
391
+ try {
392
+ return unwrapEnvelope(JSON.parse(responseBody));
393
+ } catch {
394
+ return;
395
+ }
396
+ };
397
+ const causeNames = (error) => {
398
+ const names = [];
399
+ let cause = readField(error, "cause");
400
+ while (cause !== void 0 && cause !== null && names.length < MAX_CAUSES) {
401
+ names.push(className(cause));
402
+ cause = readField(cause, "cause");
403
+ }
404
+ return names;
405
+ };
406
+ const describeError = (error) => {
407
+ const sources = [
408
+ error,
409
+ unwrapEnvelope(readField(error, "error")),
410
+ parsedResponseBody(error)
411
+ ];
412
+ const codes = /* @__PURE__ */ new Set();
413
+ for (const source of sources) for (const key of [
414
+ "code",
415
+ "type",
416
+ "status"
417
+ ]) {
418
+ const code = matching(readField(source, key), PROVIDER_CODE);
419
+ if (code !== void 0) codes.add(code);
420
+ }
421
+ const status = httpStatus(error);
422
+ const param = firstMatching(sources.map((source) => readField(source, "param")), PARAM);
423
+ const requestId = firstMatching([readField(error, "requestID"), readField(error, "request_id")], REQUEST_ID);
424
+ const causes = causeNames(error);
425
+ let summary = className(error);
426
+ if (status !== void 0) summary += `: ${status}`;
427
+ for (const code of codes) summary += ` ${code}`;
428
+ if (param !== void 0) summary += ` param=${param}`;
429
+ if (requestId !== void 0) summary += ` request_id=${requestId}`;
430
+ if (causes.length > 0) summary += ` (caused by ${causes.join(", ")})`;
431
+ return summary;
432
+ };
433
+ /**
434
+ * Describes an error for `$ai_error` without its message, stack, or arbitrary fields, which
435
+ * routinely echo the prompt. Only structured fields that pass a strict grammar are kept.
436
+ * The format matches the Python SDK.
437
+ */
438
+ function summarizeErrorForPrivacy(error) {
439
+ try {
440
+ return describeError(error);
441
+ } catch {
442
+ return error instanceof Error ? "Error" : "NonErrorThrown";
443
+ }
444
+ }
445
+ function formatAiError(error, privacyMode) {
446
+ return privacyMode ? summarizeErrorForPrivacy(error) : stringifyError(error);
447
+ }
317
448
  function truncateStack(stack) {
318
449
  if (!stack) return stack;
319
450
  const lines = stack.split("\n");
@@ -564,6 +695,9 @@ var LangChainCallbackHandler = class extends _langchain_core_callbacks_base.Base
564
695
  if (parentRunId && !this.parentTree[parentRunId]) return traceId;
565
696
  return parentRunId;
566
697
  }
698
+ _isPrivacyModeEnabled() {
699
+ return isPrivacyModeEnabled(this.client, this.privacyMode);
700
+ }
567
701
  _safeCapture(message) {
568
702
  try {
569
703
  captureAiEvent(this.client, message);
@@ -603,7 +737,7 @@ var LangChainCallbackHandler = class extends _langchain_core_callbacks_base.Base
603
737
  const interrupts = outputs.interrupts;
604
738
  if (interrupts !== void 0) eventProperties["$ai_output_state"] = withPrivacyMode(this.client, this.privacyMode, sanitizeLangChain({ __interrupt__: interrupts }, this.client));
605
739
  } else {
606
- eventProperties["$ai_error"] = stringifyError(outputs);
740
+ eventProperties["$ai_error"] = formatAiError(outputs, this._isPrivacyModeEnabled());
607
741
  eventProperties["$ai_is_error"] = true;
608
742
  }
609
743
  } else if (outputs !== void 0) eventProperties["$ai_output_state"] = withPrivacyMode(this.client, this.privacyMode, sanitizeLangChain(outputs, this.client));
@@ -652,10 +786,10 @@ var LangChainCallbackHandler = class extends _langchain_core_callbacks_base.Base
652
786
  ...eventPropertiesServedTier
653
787
  };
654
788
  if (parentRunId) eventProperties["$ai_parent_id"] = parentRunId;
655
- if (run.tools) eventProperties["$ai_tools"] = run.tools;
789
+ if (run.tools) eventProperties["$ai_tools"] = redactMcpToolCredentials(run.tools);
656
790
  if (output instanceof Error) {
657
791
  eventProperties["$ai_http_status"] = output.status || 500;
658
- eventProperties["$ai_error"] = stringifyError(output);
792
+ eventProperties["$ai_error"] = formatAiError(output, this._isPrivacyModeEnabled());
659
793
  eventProperties["$ai_is_error"] = true;
660
794
  } else {
661
795
  const [inputTokens, outputTokens, additionalTokenData] = this.parseUsage(output, run.provider, run.model);
@@ -950,7 +1084,11 @@ const stringify = (value) => {
950
1084
  }
951
1085
  }
952
1086
  };
953
- const toError = (error) => error instanceof Error ? error : new Error(stringify(error));
1087
+ const toError = (error, privacyMode) => {
1088
+ if (error instanceof Error) return error;
1089
+ return privacyMode ? Object.assign(/* @__PURE__ */ new Error(), { name: "NonErrorThrown" }) : new Error(stringify(error));
1090
+ };
1091
+ const toolMessageError = (message, privacyMode) => new Error(privacyMode ? "Tool returned an error" : toContentString(message.content));
954
1092
  const safely = (callback) => {
955
1093
  try {
956
1094
  callback();
@@ -1025,6 +1163,7 @@ const toLLMResult = (response) => {
1025
1163
  const createPostHogMiddleware = (options) => {
1026
1164
  const { stateSchema, ...callbackOptions } = options;
1027
1165
  const callback = new LangChainMiddlewareCallbackHandler(callbackOptions);
1166
+ const privacyMode = () => isPrivacyModeEnabled(callbackOptions.client, callbackOptions.privacyMode);
1028
1167
  const middlewareStateSchema = stateSchema ? (0, _langchain_core_utils_types.extendInteropZodObject)(stateSchema, postHogStateSchema.shape) : postHogStateSchema;
1029
1168
  return (0, langchain.createMiddleware)({
1030
1169
  name: "PostHogMiddleware",
@@ -1066,7 +1205,7 @@ const createPostHogMiddleware = (options) => {
1066
1205
  safely(() => callback.handleLLMEnd(toLLMResult(response), runId, parentRunId));
1067
1206
  return response;
1068
1207
  } catch (error) {
1069
- safely(() => callback.handleLLMError(toError(error), runId));
1208
+ safely(() => callback.handleLLMError(toError(error, privacyMode()), runId));
1070
1209
  throw error;
1071
1210
  }
1072
1211
  },
@@ -1087,11 +1226,11 @@ const createPostHogMiddleware = (options) => {
1087
1226
  safely(() => callback.handleToolStart(serializedTool, stringify(request.toolCall.args), runId, parentRunId, void 0, void 0, toolName));
1088
1227
  try {
1089
1228
  const result = await handler(request);
1090
- if (_langchain_core_messages.ToolMessage.isInstance(result) && result.status === "error") safely(() => callback.handleToolError(new Error(toContentString(result.content)), runId, parentRunId));
1229
+ if (_langchain_core_messages.ToolMessage.isInstance(result) && result.status === "error") safely(() => callback.handleToolError(toolMessageError(result, privacyMode()), runId, parentRunId));
1091
1230
  else safely(() => callback.handleToolEnd(result, runId, parentRunId));
1092
1231
  return result;
1093
1232
  } catch (error) {
1094
- safely(() => callback.handleToolError(toError(error), runId));
1233
+ safely(() => callback.handleToolError(toError(error, privacyMode()), runId));
1095
1234
  throw error;
1096
1235
  }
1097
1236
  }