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