@posthog/ai 8.16.1 → 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.
- package/README.md +12 -0
- package/dist/adk/index.cjs +147 -9
- package/dist/adk/index.cjs.map +1 -1
- package/dist/adk/index.mjs +147 -9
- package/dist/adk/index.mjs.map +1 -1
- package/dist/anthropic/index.cjs +141 -8
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +141 -8
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/claude-agent-sdk/index.cjs +147 -9
- package/dist/claude-agent-sdk/index.cjs.map +1 -1
- package/dist/claude-agent-sdk/index.mjs +147 -9
- package/dist/claude-agent-sdk/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +141 -8
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +141 -8
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +149 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +149 -15
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +141 -7
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.d.ts +1 -0
- package/dist/langchain/index.mjs +141 -7
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/langchain/middleware/index.cjs +150 -11
- package/dist/langchain/middleware/index.cjs.map +1 -1
- package/dist/langchain/middleware/index.mjs +150 -11
- package/dist/langchain/middleware/index.mjs.map +1 -1
- package/dist/openai/index.cjs +141 -8
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +141 -8
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +104 -3
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.mjs +104 -3
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +216 -4
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.d.ts +22 -1
- package/dist/otel/index.mjs +216 -4
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +149 -15
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +149 -15
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -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.16.
|
|
6
|
+
var version = "8.16.2";
|
|
7
7
|
//#endregion
|
|
8
8
|
//#region src/captureAiEvent.ts
|
|
9
9
|
/** @internal */
|
|
@@ -27,6 +27,11 @@ async function captureAiEventImmediate(client, event) {
|
|
|
27
27
|
await client.captureImmediate(event);
|
|
28
28
|
}
|
|
29
29
|
//#endregion
|
|
30
|
+
//#region src/typeGuards.ts
|
|
31
|
+
const isObject = (value) => {
|
|
32
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
30
35
|
//#region src/sanitization/base64_recognizer.ts
|
|
31
36
|
const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
|
|
32
37
|
const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
|
|
@@ -218,6 +223,10 @@ var BinaryContentRedactor = class {
|
|
|
218
223
|
};
|
|
219
224
|
new BinaryContentRedactor();
|
|
220
225
|
//#endregion
|
|
226
|
+
//#region src/privacyMode.ts
|
|
227
|
+
/** Per-call settings can turn redaction on but never off. */
|
|
228
|
+
const isPrivacyModeEnabled = (client, privacyMode = false) => privacyMode === true || client?.privacyMode === true || client?.privacy_mode === true || client?.options?.privacyMode === true;
|
|
229
|
+
//#endregion
|
|
221
230
|
//#region src/utils.ts
|
|
222
231
|
const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
|
|
223
232
|
"$ai_input_tokens",
|
|
@@ -265,7 +274,6 @@ function toContentString(content) {
|
|
|
265
274
|
}
|
|
266
275
|
return String(content);
|
|
267
276
|
}
|
|
268
|
-
const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
|
|
269
277
|
function toSafeString(input) {
|
|
270
278
|
if (input === void 0 || input === null) return "";
|
|
271
279
|
if (typeof input === "string") return input;
|
|
@@ -299,6 +307,7 @@ function sanitizeValues(obj) {
|
|
|
299
307
|
//#region src/serializeError.ts
|
|
300
308
|
const DEFAULT_MAX_DEPTH = 3;
|
|
301
309
|
const MAX_STACK_LINES = 20;
|
|
310
|
+
const OMITTED_FIELD = "requestBodyValues";
|
|
302
311
|
function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
303
312
|
if (depth < 0 || value === null || typeof value !== "object") return value;
|
|
304
313
|
if (value instanceof Error) {
|
|
@@ -307,16 +316,17 @@ function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
|
307
316
|
message: value.message,
|
|
308
317
|
stack: truncateStack(value.stack)
|
|
309
318
|
};
|
|
310
|
-
for (const key of Object.keys(value)) out[key] = serializeError(value
|
|
319
|
+
for (const key of Object.keys(value)) if (key !== OMITTED_FIELD) out[key] = serializeError(Reflect.get(value, key), depth - 1);
|
|
311
320
|
if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
|
|
312
321
|
return out;
|
|
313
322
|
}
|
|
314
323
|
if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
|
|
315
324
|
return value;
|
|
316
325
|
}
|
|
326
|
+
const omitRequestBody = (key, value) => key === OMITTED_FIELD ? void 0 : value;
|
|
317
327
|
function stringifyError(error) {
|
|
318
328
|
try {
|
|
319
|
-
return JSON.stringify(sanitizeValues(serializeError(error)));
|
|
329
|
+
return JSON.stringify(sanitizeValues(serializeError(error)), omitRequestBody);
|
|
320
330
|
} catch {
|
|
321
331
|
if (error instanceof Error) return JSON.stringify({
|
|
322
332
|
name: error.name,
|
|
@@ -325,6 +335,105 @@ function stringifyError(error) {
|
|
|
325
335
|
return JSON.stringify({ message: String(error) });
|
|
326
336
|
}
|
|
327
337
|
}
|
|
338
|
+
const CLASS_NAME = /^[A-Za-z_$][A-Za-z0-9_$.]{0,127}$/;
|
|
339
|
+
const PROVIDER_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
340
|
+
const PARAM = /^[A-Za-z0-9_.[\]-]{1,128}$/;
|
|
341
|
+
const REQUEST_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
342
|
+
const MAX_CAUSES = 3;
|
|
343
|
+
const readField = (value, key) => {
|
|
344
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return;
|
|
345
|
+
try {
|
|
346
|
+
return Reflect.get(value, key);
|
|
347
|
+
} catch {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
const matching = (value, grammar) => typeof value === "string" && grammar.test(value) ? value : void 0;
|
|
352
|
+
const firstMatching = (values, grammar) => {
|
|
353
|
+
for (const value of values) {
|
|
354
|
+
const match = matching(value, grammar);
|
|
355
|
+
if (match !== void 0) return match;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
const className = (value) => {
|
|
359
|
+
if (!(value instanceof Error)) return "NonErrorThrown";
|
|
360
|
+
const name = readField(value, "name");
|
|
361
|
+
return firstMatching([
|
|
362
|
+
name === "Error" ? void 0 : name,
|
|
363
|
+
readField(readField(value, "constructor"), "name"),
|
|
364
|
+
name
|
|
365
|
+
], CLASS_NAME) ?? "Error";
|
|
366
|
+
};
|
|
367
|
+
const httpStatus = (error) => {
|
|
368
|
+
for (const key of ["status", "statusCode"]) {
|
|
369
|
+
const value = readField(error, key);
|
|
370
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599) return value;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
const unwrapEnvelope = (body) => {
|
|
374
|
+
const inner = readField(body, "error");
|
|
375
|
+
return inner !== null && typeof inner === "object" ? inner : body;
|
|
376
|
+
};
|
|
377
|
+
const parsedResponseBody = (error) => {
|
|
378
|
+
const responseBody = readField(error, "responseBody");
|
|
379
|
+
if (typeof responseBody !== "string") return;
|
|
380
|
+
try {
|
|
381
|
+
return unwrapEnvelope(JSON.parse(responseBody));
|
|
382
|
+
} catch {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
const causeNames = (error) => {
|
|
387
|
+
const names = [];
|
|
388
|
+
let cause = readField(error, "cause");
|
|
389
|
+
while (cause !== void 0 && cause !== null && names.length < MAX_CAUSES) {
|
|
390
|
+
names.push(className(cause));
|
|
391
|
+
cause = readField(cause, "cause");
|
|
392
|
+
}
|
|
393
|
+
return names;
|
|
394
|
+
};
|
|
395
|
+
const describeError = (error) => {
|
|
396
|
+
const sources = [
|
|
397
|
+
error,
|
|
398
|
+
unwrapEnvelope(readField(error, "error")),
|
|
399
|
+
parsedResponseBody(error)
|
|
400
|
+
];
|
|
401
|
+
const codes = /* @__PURE__ */ new Set();
|
|
402
|
+
for (const source of sources) for (const key of [
|
|
403
|
+
"code",
|
|
404
|
+
"type",
|
|
405
|
+
"status"
|
|
406
|
+
]) {
|
|
407
|
+
const code = matching(readField(source, key), PROVIDER_CODE);
|
|
408
|
+
if (code !== void 0) codes.add(code);
|
|
409
|
+
}
|
|
410
|
+
const status = httpStatus(error);
|
|
411
|
+
const param = firstMatching(sources.map((source) => readField(source, "param")), PARAM);
|
|
412
|
+
const requestId = firstMatching([readField(error, "requestID"), readField(error, "request_id")], REQUEST_ID);
|
|
413
|
+
const causes = causeNames(error);
|
|
414
|
+
let summary = className(error);
|
|
415
|
+
if (status !== void 0) summary += `: ${status}`;
|
|
416
|
+
for (const code of codes) summary += ` ${code}`;
|
|
417
|
+
if (param !== void 0) summary += ` param=${param}`;
|
|
418
|
+
if (requestId !== void 0) summary += ` request_id=${requestId}`;
|
|
419
|
+
if (causes.length > 0) summary += ` (caused by ${causes.join(", ")})`;
|
|
420
|
+
return summary;
|
|
421
|
+
};
|
|
422
|
+
/**
|
|
423
|
+
* Describes an error for `$ai_error` without its message, stack, or arbitrary fields, which
|
|
424
|
+
* routinely echo the prompt. Only structured fields that pass a strict grammar are kept.
|
|
425
|
+
* The format matches the Python SDK.
|
|
426
|
+
*/
|
|
427
|
+
function summarizeErrorForPrivacy(error) {
|
|
428
|
+
try {
|
|
429
|
+
return describeError(error);
|
|
430
|
+
} catch {
|
|
431
|
+
return error instanceof Error ? "Error" : "NonErrorThrown";
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function formatAiError(error, privacyMode) {
|
|
435
|
+
return privacyMode ? summarizeErrorForPrivacy(error) : stringifyError(error);
|
|
436
|
+
}
|
|
328
437
|
function truncateStack(stack) {
|
|
329
438
|
if (!stack) return stack;
|
|
330
439
|
const lines = stack.split("\n");
|
|
@@ -332,6 +441,33 @@ function truncateStack(stack) {
|
|
|
332
441
|
return [...lines.slice(0, MAX_STACK_LINES), "... (truncated)"].join("\n");
|
|
333
442
|
}
|
|
334
443
|
//#endregion
|
|
444
|
+
//#region src/mcpCredentials.ts
|
|
445
|
+
const MCP_CREDENTIAL_FIELDS = ["headers", "authorization"];
|
|
446
|
+
const MCP_PROVIDER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
447
|
+
"openai.mcp",
|
|
448
|
+
"xai.mcp",
|
|
449
|
+
"google.mcp_server"
|
|
450
|
+
]);
|
|
451
|
+
const isMcpProviderTool = (tool) => (tool.type === "provider" || tool.type === "provider-defined") && typeof tool.id === "string" && MCP_PROVIDER_TOOL_IDS.has(tool.id);
|
|
452
|
+
const withoutMcpCredentials = (config) => {
|
|
453
|
+
const redacted = { ...config };
|
|
454
|
+
for (const field of MCP_CREDENTIAL_FIELDS) if (field in redacted) redacted[field] = "[REDACTED]";
|
|
455
|
+
return redacted;
|
|
456
|
+
};
|
|
457
|
+
/** Hides the credentials of a remote MCP tool definition; everything else is kept as sent. */
|
|
458
|
+
function redactMcpToolDefinition(tool) {
|
|
459
|
+
if (!isObject(tool)) return tool;
|
|
460
|
+
if (tool.type === "mcp") return withoutMcpCredentials(tool);
|
|
461
|
+
if (isMcpProviderTool(tool) && isObject(tool.args)) return {
|
|
462
|
+
...tool,
|
|
463
|
+
args: withoutMcpCredentials(tool.args)
|
|
464
|
+
};
|
|
465
|
+
return tool;
|
|
466
|
+
}
|
|
467
|
+
function redactMcpToolCredentials(tools) {
|
|
468
|
+
return Array.isArray(tools) ? tools.map(redactMcpToolDefinition) : tools;
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
335
471
|
//#region src/gatewayWarning.ts
|
|
336
472
|
const POSTHOG_AI_GATEWAY_HOSTS = [
|
|
337
473
|
"gateway.posthog.com",
|
|
@@ -394,13 +530,15 @@ const captureAiGeneration = async (client, options) => {
|
|
|
394
530
|
}
|
|
395
531
|
let exceptionId;
|
|
396
532
|
if (client.options?.enableExceptionAutocapture) {
|
|
397
|
-
|
|
398
|
-
|
|
533
|
+
if (!shouldRedact) {
|
|
534
|
+
exceptionId = (0, _posthog_core.uuidv7)();
|
|
535
|
+
client.captureException(options.error, void 0, { $ai_trace_id: traceId }, exceptionId);
|
|
536
|
+
}
|
|
399
537
|
if (typeof options.error === "object") options.error.__posthog_previously_captured_error = true;
|
|
400
538
|
}
|
|
401
539
|
errorData = {
|
|
402
540
|
$ai_is_error: true,
|
|
403
|
-
$ai_error:
|
|
541
|
+
$ai_error: formatAiError(options.error, shouldRedact),
|
|
404
542
|
$exception_event_id: exceptionId
|
|
405
543
|
};
|
|
406
544
|
}
|
|
@@ -441,7 +579,7 @@ const captureAiGeneration = async (client, options) => {
|
|
|
441
579
|
$ai_tokens_source: getTokensSource(options.properties),
|
|
442
580
|
...options.distinctId ? {} : { $process_person_profile: false },
|
|
443
581
|
...options.stopReason ? { $ai_stop_reason: options.stopReason } : {},
|
|
444
|
-
...options.tools ? { $ai_tools: options.tools } : {},
|
|
582
|
+
...options.tools ? { $ai_tools: redactMcpToolCredentials(options.tools) } : {},
|
|
445
583
|
...options.completionId ? { $ai_completion_id: options.completionId } : {},
|
|
446
584
|
...options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? { $ai_provider_metadata: options.providerMetadata } : {},
|
|
447
585
|
...errorData,
|
|
@@ -1082,7 +1220,7 @@ var PostHogClaudeAgentProcessor = class {
|
|
|
1082
1220
|
$ai_latency: latency,
|
|
1083
1221
|
...state.turnCost != null ? { $ai_total_cost_usd: state.turnCost } : {},
|
|
1084
1222
|
...isError ? { $ai_is_error: true } : {},
|
|
1085
|
-
...error !== void 0 ? { $ai_error:
|
|
1223
|
+
...error !== void 0 ? { $ai_error: formatAiError(error, isPrivacyModeEnabled(this._client, trace.privacyMode ?? false)) } : {},
|
|
1086
1224
|
...result?.subtype === "success" && result.api_error_status != null ? { $ai_http_status: result.api_error_status } : {}
|
|
1087
1225
|
});
|
|
1088
1226
|
} finally {
|