@sentry/junior 0.9.3 → 0.9.4

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.
@@ -2,11 +2,854 @@ import {
2
2
  discoverInstalledPluginPackageContent,
3
3
  pluginRoots
4
4
  } from "./chunk-KCLEEKYX.js";
5
- import {
6
- logInfo,
7
- logWarn,
8
- setSpanAttributes
9
- } from "./chunk-ZW4OVKF5.js";
5
+
6
+ // src/chat/logging.ts
7
+ import { AsyncLocalStorage } from "async_hooks";
8
+ import * as Sentry from "@sentry/nextjs";
9
+ var MAX_STRING_VALUE = 1200;
10
+ var SECRETS_RE = [
11
+ /\b(sk-[A-Za-z0-9_-]{20,})\b/g,
12
+ /\b(xox[baprs]-[A-Za-z0-9-]{10,})\b/g,
13
+ /\bBearer\s+([A-Za-z0-9._\-+=]{20,})\b/gi,
14
+ /\b[A-Z0-9_]+(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD)\s*[=:]\s*([^\s"']{8,})/gi
15
+ ];
16
+ var LEGACY_KEY_MAP = {
17
+ error: "error.message",
18
+ "error.stack": "exception.stacktrace",
19
+ "gen_ai.system": "gen_ai.provider.name",
20
+ "gen_ai.request.messages": "gen_ai.input.messages",
21
+ "gen_ai.response.text": "gen_ai.output.messages",
22
+ "messaging.conversation.id": "messaging.message.conversation_id",
23
+ bytes: "file.size",
24
+ media_type: "file.mime_type",
25
+ skillDir: "file.path",
26
+ root: "file.directory",
27
+ originalLength: "app.output.original_length",
28
+ parsedLength: "app.output.parsed_length",
29
+ directiveMode: "app.output.directive_mode",
30
+ fileCount: "app.output.file_count",
31
+ attempt: "app.retry.attempt",
32
+ steps: "app.ai.steps",
33
+ toolCalls: "app.ai.tool_calls",
34
+ toolResults: "app.ai.tool_results",
35
+ finishReason: "app.ai.finish_reason",
36
+ sources: "app.ai.sources",
37
+ generatedFiles: "app.ai.generated_files",
38
+ resultFiles: "app.ai.result_files",
39
+ responseMessages: "app.ai.response_messages",
40
+ stepDiagnostics: "app.ai.step_diagnostics",
41
+ inferredSkill: "app.skill.name",
42
+ inferredScore: "app.skill.score"
43
+ };
44
+ var contextStorage = new AsyncLocalStorage();
45
+ var logRecordSinks = /* @__PURE__ */ new Set();
46
+ var ANSI = {
47
+ reset: "\x1B[0m",
48
+ faint: "\x1B[2m",
49
+ red: "\x1B[31m",
50
+ yellow: "\x1B[33m",
51
+ green: "\x1B[32m",
52
+ blue: "\x1B[34m",
53
+ cyan: "\x1B[36m",
54
+ gray: "\x1B[90m"
55
+ };
56
+ var CONSOLE_PRIORITY_KEYS = [
57
+ "app.conversation.id",
58
+ "app.turn.id",
59
+ "event.name",
60
+ "error.message",
61
+ "messaging.message.id",
62
+ "app.trace_id",
63
+ "app.span_id",
64
+ "app.agent.id",
65
+ "messaging.message.conversation_id",
66
+ "messaging.destination.name",
67
+ "app.run.id",
68
+ "app.message.kind"
69
+ ];
70
+ var CONSOLE_PRIORITY_INDEX = new Map(
71
+ CONSOLE_PRIORITY_KEYS.map((key, index) => [key, index])
72
+ );
73
+ var CONSOLE_ALWAYS_HIDDEN_KEYS = /* @__PURE__ */ new Set([
74
+ "app.assistant.username",
75
+ "app.platform",
76
+ "enduser.id",
77
+ "enduser.pseudo_id",
78
+ "http.request.method",
79
+ "messaging.system",
80
+ "url.full",
81
+ "url.path",
82
+ "user_agent.original"
83
+ ]);
84
+ var CONSOLE_DROP_WHEN_COUNTED_KEYS = /* @__PURE__ */ new Set([
85
+ "app.capability.names",
86
+ "app.capability.providers",
87
+ "app.config.keys"
88
+ ]);
89
+ var CONSOLE_PREVIEW_KEYS = /* @__PURE__ */ new Set([
90
+ "gen_ai.input.messages",
91
+ "gen_ai.output.messages",
92
+ "gen_ai.tool.call.arguments",
93
+ "gen_ai.tool.call.result"
94
+ ]);
95
+ function getSentryEnvironment() {
96
+ return (process.env.SENTRY_ENVIRONMENT ?? process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT ?? process.env.VERCEL_ENV ?? process.env.NODE_ENV ?? "").trim().toLowerCase();
97
+ }
98
+ function shouldSuppressInfoLog(_level) {
99
+ return false;
100
+ }
101
+ function shouldEmitConsole(level) {
102
+ if (process.env.NODE_ENV === "test") {
103
+ return level === "error";
104
+ }
105
+ return getSentryEnvironment() !== "production";
106
+ }
107
+ function findNextBlankLineBoundary(input, start) {
108
+ const lfBoundary = input.indexOf("\n\n", start);
109
+ const crlfBoundary = input.indexOf("\r\n\r\n", start);
110
+ if (lfBoundary === -1 && crlfBoundary === -1) {
111
+ return null;
112
+ }
113
+ if (lfBoundary === -1) {
114
+ return { start: crlfBoundary, end: crlfBoundary + 4 };
115
+ }
116
+ if (crlfBoundary === -1 || lfBoundary < crlfBoundary) {
117
+ return { start: lfBoundary, end: lfBoundary + 2 };
118
+ }
119
+ return { start: crlfBoundary, end: crlfBoundary + 4 };
120
+ }
121
+ function redactPrivateKeyBlocks(input) {
122
+ const beginPrefix = "-----BEGIN ";
123
+ const footerMarker = "-----";
124
+ let cursor = 0;
125
+ let output = "";
126
+ while (cursor < input.length) {
127
+ const begin = input.indexOf(beginPrefix, cursor);
128
+ if (begin === -1) {
129
+ output += input.slice(cursor);
130
+ break;
131
+ }
132
+ const labelStart = begin + beginPrefix.length;
133
+ const labelEnd = input.indexOf(footerMarker, labelStart);
134
+ if (labelEnd === -1) {
135
+ output += input.slice(cursor);
136
+ break;
137
+ }
138
+ const label = input.slice(labelStart, labelEnd);
139
+ if (!label.endsWith("PRIVATE KEY")) {
140
+ output += input.slice(cursor, labelEnd + footerMarker.length);
141
+ cursor = labelEnd + footerMarker.length;
142
+ continue;
143
+ }
144
+ const header = input.slice(begin, labelEnd + footerMarker.length);
145
+ const footer = `-----END ${label}-----`;
146
+ const footerStart = input.indexOf(footer, labelEnd + footerMarker.length);
147
+ if (footerStart === -1) {
148
+ const resumeBoundary = findNextBlankLineBoundary(
149
+ input,
150
+ labelEnd + footerMarker.length
151
+ );
152
+ output += input.slice(cursor, begin);
153
+ output += `${header}
154
+ ...redacted...`;
155
+ if (!resumeBoundary) {
156
+ break;
157
+ }
158
+ output += input.slice(resumeBoundary.start, resumeBoundary.end);
159
+ cursor = resumeBoundary.end;
160
+ continue;
161
+ }
162
+ output += input.slice(cursor, begin);
163
+ output += `${header}
164
+ ...redacted...
165
+ ${footer}`;
166
+ cursor = footerStart + footer.length;
167
+ }
168
+ return output;
169
+ }
170
+ function redactSecrets(input) {
171
+ let out = redactPrivateKeyBlocks(input);
172
+ for (const pattern of SECRETS_RE) {
173
+ out = out.replace(pattern, (full, token) => {
174
+ if (typeof token !== "string") {
175
+ return "***";
176
+ }
177
+ if (token.length < 12) {
178
+ return full.replace(token, "***");
179
+ }
180
+ return full.replace(token, `${token.slice(0, 4)}...${token.slice(-4)}`);
181
+ });
182
+ }
183
+ return out;
184
+ }
185
+ function toSnakeCase(value) {
186
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
187
+ }
188
+ function isSemanticKey(key) {
189
+ return /^[a-z][a-z0-9_]*(\.[a-z0-9_][a-z0-9_-]*)+$/.test(key);
190
+ }
191
+ function normalizeAttributeKey(key) {
192
+ const mapped = LEGACY_KEY_MAP[key];
193
+ if (mapped) {
194
+ return mapped;
195
+ }
196
+ if (isSemanticKey(key)) {
197
+ return key;
198
+ }
199
+ if (key === "platform") return "app.platform";
200
+ if (key === "request.id") return "app.request.id";
201
+ const snake = toSnakeCase(key);
202
+ if (!snake) {
203
+ return "app.attribute";
204
+ }
205
+ return `app.${snake}`;
206
+ }
207
+ function sanitizePrimitive(value) {
208
+ if (value === null || value === void 0) return void 0;
209
+ if (typeof value === "string") {
210
+ const trimmed = value.trim();
211
+ if (!trimmed) return void 0;
212
+ const redacted = redactSecrets(trimmed);
213
+ return redacted.length > MAX_STRING_VALUE ? `${redacted.slice(0, MAX_STRING_VALUE)}...` : redacted;
214
+ }
215
+ if (typeof value === "number") {
216
+ return Number.isFinite(value) ? value : void 0;
217
+ }
218
+ if (typeof value === "boolean") return value;
219
+ if (value instanceof Error) {
220
+ return redactSecrets(value.message);
221
+ }
222
+ try {
223
+ const json = JSON.stringify(value);
224
+ if (!json) return void 0;
225
+ const redacted = redactSecrets(json);
226
+ return redacted.length > MAX_STRING_VALUE ? `${redacted.slice(0, MAX_STRING_VALUE)}...` : redacted;
227
+ } catch {
228
+ return void 0;
229
+ }
230
+ }
231
+ function sanitizeValue(value) {
232
+ if (Array.isArray(value)) {
233
+ const sanitized = value.filter((entry) => typeof entry === "string").map((entry) => sanitizePrimitive(entry)).filter((entry) => typeof entry === "string");
234
+ return sanitized.length > 0 ? sanitized : void 0;
235
+ }
236
+ return sanitizePrimitive(value);
237
+ }
238
+ function contextToAttributes(context) {
239
+ const attributes = {
240
+ "app.conversation.id": context.conversationId,
241
+ "app.turn.id": context.turnId,
242
+ "app.agent.id": context.agentId,
243
+ "app.platform": context.platform,
244
+ "app.request.id": context.requestId,
245
+ "messaging.system": context.platform === "slack" ? "slack" : context.platform,
246
+ "messaging.message.conversation_id": context.slackThreadId,
247
+ "messaging.destination.name": context.slackChannelId,
248
+ "enduser.id": context.slackUserId,
249
+ "enduser.pseudo_id": context.slackUserName,
250
+ "app.run.id": context.runId,
251
+ "app.assistant.username": context.assistantUserName,
252
+ "gen_ai.request.model": context.modelId,
253
+ "app.skill.name": context.skillName,
254
+ "http.request.method": context.httpMethod,
255
+ "url.path": context.httpPath,
256
+ "url.full": context.urlFull,
257
+ "user_agent.original": context.userAgent
258
+ };
259
+ const normalized = {};
260
+ for (const [key, value] of Object.entries(attributes)) {
261
+ const sanitized = sanitizeValue(value);
262
+ if (sanitized !== void 0) normalized[key] = sanitized;
263
+ }
264
+ return normalized;
265
+ }
266
+ function getTraceCorrelationAttributes() {
267
+ const sentry = Sentry;
268
+ if (typeof sentry.getActiveSpan !== "function" || typeof sentry.spanToJSON !== "function") {
269
+ return {};
270
+ }
271
+ try {
272
+ const span = sentry.getActiveSpan();
273
+ if (!span) return {};
274
+ const json = sentry.spanToJSON(span);
275
+ const attrs = {};
276
+ if (json.trace_id) attrs.trace_id = json.trace_id;
277
+ if (json.span_id) attrs.span_id = json.span_id;
278
+ return attrs;
279
+ } catch {
280
+ return {};
281
+ }
282
+ }
283
+ function mergeAttributes(...maps) {
284
+ const merged = {};
285
+ for (const map of maps) {
286
+ if (!map) continue;
287
+ for (const [rawKey, rawValue] of Object.entries(map)) {
288
+ const key = normalizeAttributeKey(rawKey);
289
+ const value = sanitizeValue(rawValue);
290
+ if (value !== void 0) {
291
+ merged[key] = value;
292
+ }
293
+ }
294
+ }
295
+ return merged;
296
+ }
297
+ function emitSentry(level, body, attributes) {
298
+ if (shouldSuppressInfoLog(level)) {
299
+ return;
300
+ }
301
+ const sentry = Sentry;
302
+ const loggerFn = sentry.logger?.[level];
303
+ if (typeof loggerFn === "function") {
304
+ loggerFn(body, attributes);
305
+ return;
306
+ }
307
+ const sentryWithScope = sentry.withScope;
308
+ const sentryCaptureMessage = sentry.captureMessage;
309
+ const sentryLevel = level === "warn" ? "warning" : level;
310
+ if (typeof sentryWithScope === "function" && typeof sentryCaptureMessage === "function") {
311
+ sentryWithScope((scope) => {
312
+ for (const [key, value] of Object.entries(attributes)) {
313
+ scope.setExtra(key, value);
314
+ }
315
+ sentryCaptureMessage(body, sentryLevel);
316
+ });
317
+ return;
318
+ }
319
+ if (typeof sentryCaptureMessage === "function") {
320
+ sentryCaptureMessage(body, sentryLevel);
321
+ }
322
+ }
323
+ function formatConsoleLevel(level) {
324
+ if (level === "debug") return "DBG";
325
+ if (level === "info") return "INF";
326
+ if (level === "warn") return "WRN";
327
+ return "ERR";
328
+ }
329
+ function consoleLevelColor(level) {
330
+ if (level === "error") return ANSI.red;
331
+ if (level === "warn") return ANSI.yellow;
332
+ if (level === "info") return ANSI.green;
333
+ return ANSI.blue;
334
+ }
335
+ function quoteConsoleValue(value) {
336
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
337
+ }
338
+ function formatConsoleValue(value) {
339
+ if (typeof value === "number" || typeof value === "boolean") {
340
+ return String(value);
341
+ }
342
+ if (Array.isArray(value)) {
343
+ return quoteConsoleValue(JSON.stringify(value));
344
+ }
345
+ if (/^[A-Za-z0-9._:/@+-]+$/.test(value)) {
346
+ return value;
347
+ }
348
+ return quoteConsoleValue(value);
349
+ }
350
+ function shouldShowConsoleDestinationName(eventName) {
351
+ return /^(app_home_|oauth_|queue_|slash_command_|slack_|webhook_)/.test(
352
+ eventName
353
+ );
354
+ }
355
+ function shouldShowConsoleModel(level, eventName) {
356
+ if (level === "warn" || level === "error") {
357
+ return true;
358
+ }
359
+ return eventName.startsWith("ai_") || eventName.startsWith("assistant_") || eventName === "agent_turn_started" || eventName === "agent_turn_completed" || eventName === "agent_turn_provider_error";
360
+ }
361
+ function shouldHideConsoleAttribute(level, eventName, key, attributes) {
362
+ if (CONSOLE_ALWAYS_HIDDEN_KEYS.has(key)) {
363
+ return true;
364
+ }
365
+ if (CONSOLE_DROP_WHEN_COUNTED_KEYS.has(key)) {
366
+ return true;
367
+ }
368
+ if (key === "app.agent.id" && attributes[key] === attributes["app.turn.id"]) {
369
+ return true;
370
+ }
371
+ if (key === "messaging.message.conversation_id" && attributes[key] === attributes["app.conversation.id"]) {
372
+ return true;
373
+ }
374
+ if (key === "app.message.id" && attributes[key] === attributes["messaging.message.id"]) {
375
+ return true;
376
+ }
377
+ if (key === "messaging.destination.name" && !shouldShowConsoleDestinationName(eventName)) {
378
+ return true;
379
+ }
380
+ if (key === "gen_ai.request.model" && !shouldShowConsoleModel(level, eventName)) {
381
+ return true;
382
+ }
383
+ if (key === "gen_ai.provider.name" && eventName.startsWith("agent_tool_call_") && level !== "warn" && level !== "error") {
384
+ return true;
385
+ }
386
+ if (key === "gen_ai.operation.name" && eventName.startsWith("agent_tool_call_")) {
387
+ return true;
388
+ }
389
+ return false;
390
+ }
391
+ function summarizeConsoleString(value, maxChars) {
392
+ const collapsed = value.replace(/\s+/g, " ").trim();
393
+ if (collapsed.length <= maxChars) {
394
+ return collapsed;
395
+ }
396
+ return `${collapsed.slice(0, maxChars)}... [${collapsed.length} chars]`;
397
+ }
398
+ function projectConsoleValue(level, key, value) {
399
+ if ((level === "debug" || level === "info") && CONSOLE_PREVIEW_KEYS.has(key) && typeof value === "string") {
400
+ return summarizeConsoleString(
401
+ value,
402
+ key === "gen_ai.tool.call.result" ? 220 : 140
403
+ );
404
+ }
405
+ return value;
406
+ }
407
+ function projectConsoleAttributes(level, eventName, attributes) {
408
+ const projected = {};
409
+ for (const [key, value] of Object.entries(attributes)) {
410
+ if (shouldHideConsoleAttribute(level, eventName, key, attributes)) {
411
+ continue;
412
+ }
413
+ const nextValue = projectConsoleValue(level, key, value);
414
+ if (nextValue !== void 0) {
415
+ projected[key] = nextValue;
416
+ }
417
+ }
418
+ return projected;
419
+ }
420
+ function formatConsoleLine(level, eventName, body, attributes) {
421
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
422
+ const useColor = process.env.NODE_ENV === "development" && Boolean(process.stdout?.isTTY);
423
+ const levelColor = consoleLevelColor(level);
424
+ const colorize = (text, color) => useColor ? `${color}${text}${ANSI.reset}` : text;
425
+ const parts = [
426
+ `${colorize(timestamp, ANSI.gray)} ${colorize(formatConsoleLevel(level), levelColor)} ${body}`
427
+ ];
428
+ const projectedAttributes = projectConsoleAttributes(
429
+ level,
430
+ eventName,
431
+ attributes
432
+ );
433
+ const sortedAttributes = Object.entries(projectedAttributes).sort(
434
+ ([left], [right]) => {
435
+ const leftRank = CONSOLE_PRIORITY_INDEX.get(left);
436
+ const rightRank = CONSOLE_PRIORITY_INDEX.get(right);
437
+ if (leftRank !== void 0 || rightRank !== void 0) {
438
+ if (leftRank === void 0) return 1;
439
+ if (rightRank === void 0) return -1;
440
+ return leftRank - rightRank;
441
+ }
442
+ return left.localeCompare(right);
443
+ }
444
+ );
445
+ for (const [key, value] of sortedAttributes) {
446
+ const rendered = `${colorize(key, ANSI.cyan)}=${colorize(formatConsoleValue(value), ANSI.faint)}`;
447
+ parts.push(rendered);
448
+ }
449
+ return parts.join(" ");
450
+ }
451
+ function emitConsole(level, eventName, body, attributes) {
452
+ if (!shouldEmitConsole(level)) {
453
+ return;
454
+ }
455
+ const line = formatConsoleLine(level, eventName, body, attributes);
456
+ if (level === "error") {
457
+ console.error(line);
458
+ return;
459
+ }
460
+ if (level === "warn") {
461
+ console.warn(line);
462
+ return;
463
+ }
464
+ if (level === "info") {
465
+ console.info(line);
466
+ return;
467
+ }
468
+ console.debug(line);
469
+ }
470
+ function emit(level, eventName, attrs = {}, body) {
471
+ const contextAttributes = contextStorage.getStore() ?? {};
472
+ const traceAttributes = getTraceCorrelationAttributes();
473
+ const normalizedEventName = toSnakeCase(eventName);
474
+ const message = body ? redactSecrets(body) : normalizedEventName;
475
+ const attributes = mergeAttributes(contextAttributes, traceAttributes, {
476
+ "event.name": normalizedEventName,
477
+ ...attrs
478
+ });
479
+ for (const sink of logRecordSinks) {
480
+ try {
481
+ sink({
482
+ level,
483
+ eventName: normalizedEventName,
484
+ body: message,
485
+ attributes
486
+ });
487
+ } catch {
488
+ }
489
+ }
490
+ emitConsole(level, normalizedEventName, message, attributes);
491
+ emitSentry(level, message, attributes);
492
+ }
493
+ var log = {
494
+ debug(eventName, attrs = {}, body) {
495
+ emit("debug", eventName, attrs, body);
496
+ },
497
+ info(eventName, attrs = {}, body) {
498
+ emit("info", eventName, attrs, body);
499
+ },
500
+ warn(eventName, attrs = {}, body) {
501
+ emit("warn", eventName, attrs, body);
502
+ },
503
+ error(eventName, attrs = {}, body) {
504
+ emit("error", eventName, attrs, body);
505
+ },
506
+ exception(eventName, error, attrs = {}, body) {
507
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
508
+ emit(
509
+ "error",
510
+ eventName,
511
+ {
512
+ ...attrs,
513
+ "error.type": normalizedError.name,
514
+ "error.message": normalizedError.message,
515
+ "exception.type": normalizedError.name,
516
+ "exception.message": normalizedError.message,
517
+ "exception.stacktrace": normalizedError.stack
518
+ },
519
+ body ?? normalizedError.message
520
+ );
521
+ let eventId;
522
+ const sentryWithScope = Sentry.withScope;
523
+ const sentryCaptureException = Sentry.captureException;
524
+ if (typeof sentryWithScope === "function" && typeof sentryCaptureException === "function") {
525
+ sentryWithScope((scope) => {
526
+ for (const [key, value] of Object.entries(
527
+ mergeAttributes(contextStorage.getStore(), attrs)
528
+ )) {
529
+ scope.setExtra(key, value);
530
+ }
531
+ eventId = sentryCaptureException(normalizedError);
532
+ });
533
+ return eventId;
534
+ }
535
+ if (typeof sentryCaptureException === "function") {
536
+ eventId = sentryCaptureException(normalizedError);
537
+ }
538
+ return eventId;
539
+ }
540
+ };
541
+ function withLogContext(context, callback) {
542
+ const next = mergeAttributes(
543
+ contextStorage.getStore(),
544
+ contextToAttributes(context)
545
+ );
546
+ return contextStorage.run(next, callback);
547
+ }
548
+ function setLogContext(context) {
549
+ const merged = mergeAttributes(
550
+ contextStorage.getStore(),
551
+ contextToAttributes(context)
552
+ );
553
+ contextStorage.enterWith(merged);
554
+ }
555
+ function createLogContextFromRequest(request, context = {}) {
556
+ const url = new URL(request.url);
557
+ return {
558
+ ...context,
559
+ requestId: context.requestId ?? request.headers.get("x-request-id") ?? void 0,
560
+ httpMethod: request.method,
561
+ httpPath: url.pathname,
562
+ urlFull: url.toString(),
563
+ userAgent: request.headers.get("user-agent") ?? void 0
564
+ };
565
+ }
566
+ function toSpanAttributes(context) {
567
+ const attrs = contextToAttributes(context);
568
+ return Object.fromEntries(
569
+ Object.entries(attrs).filter(
570
+ ([, value]) => typeof value === "string" && value.length > 0
571
+ )
572
+ );
573
+ }
574
+ function setSentryTagsFromContext(context) {
575
+ const attrs = contextToAttributes(context);
576
+ for (const [key, value] of Object.entries(attrs)) {
577
+ if (typeof value === "string" && value.length > 0) {
578
+ Sentry.setTag(key, value);
579
+ }
580
+ }
581
+ if (context.slackUserId) {
582
+ Sentry.setUser({
583
+ id: context.slackUserId,
584
+ username: context.slackUserName
585
+ });
586
+ }
587
+ }
588
+ function toSpanAttributeValue(value) {
589
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
590
+ return value;
591
+ }
592
+ if (!Array.isArray(value)) {
593
+ return void 0;
594
+ }
595
+ const sanitized = value.filter(
596
+ (entry) => typeof entry === "string"
597
+ );
598
+ return sanitized.length > 0 ? sanitized : void 0;
599
+ }
600
+ function logInfo(eventName, context = {}, attributes = {}, body) {
601
+ log.info(eventName, { ...toSpanAttributes(context), ...attributes }, body);
602
+ }
603
+ function logWarn(eventName, context = {}, attributes = {}, body) {
604
+ log.warn(eventName, { ...toSpanAttributes(context), ...attributes }, body);
605
+ }
606
+ function logError(eventName, context = {}, attributes = {}, body) {
607
+ log.error(eventName, { ...toSpanAttributes(context), ...attributes }, body);
608
+ }
609
+ function logException(error, eventName, context = {}, attributes = {}, body) {
610
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
611
+ return log.exception(
612
+ eventName,
613
+ normalizedError,
614
+ { ...toSpanAttributes(context), ...attributes },
615
+ body
616
+ );
617
+ }
618
+ function setTags(context = {}) {
619
+ setLogContext(context);
620
+ setSentryTagsFromContext(context);
621
+ }
622
+ function createRequestContext(request, context = {}) {
623
+ return createLogContextFromRequest(request, context);
624
+ }
625
+ async function withContext(context, callback) {
626
+ return withLogContext(context, callback);
627
+ }
628
+ async function withSpan(name, op, context, callback, attributes = {}) {
629
+ const normalizedAttributes = {};
630
+ for (const [key, value] of Object.entries(attributes)) {
631
+ const normalizedValue = toSpanAttributeValue(value);
632
+ if (normalizedValue !== void 0) {
633
+ normalizedAttributes[key] = normalizedValue;
634
+ }
635
+ }
636
+ return withLogContext(
637
+ context,
638
+ () => Sentry.startSpan(
639
+ {
640
+ name,
641
+ op,
642
+ attributes: {
643
+ ...toSpanAttributes(context),
644
+ ...normalizedAttributes
645
+ }
646
+ },
647
+ callback
648
+ )
649
+ );
650
+ }
651
+ function setSpanAttributes(attributes) {
652
+ const sentry = Sentry;
653
+ const span = sentry.getActiveSpan?.();
654
+ if (!span) {
655
+ return;
656
+ }
657
+ const setAttribute = span.setAttribute;
658
+ if (typeof setAttribute !== "function") {
659
+ return;
660
+ }
661
+ for (const [key, value] of Object.entries(attributes)) {
662
+ const normalizedValue = toSpanAttributeValue(value);
663
+ if (normalizedValue !== void 0) {
664
+ setAttribute.call(span, key, normalizedValue);
665
+ }
666
+ }
667
+ }
668
+ function setSpanStatus(status) {
669
+ const sentry = Sentry;
670
+ const span = sentry.getActiveSpan?.();
671
+ if (!span) {
672
+ return;
673
+ }
674
+ const setStatus = span.setStatus;
675
+ if (typeof setStatus !== "function") {
676
+ return;
677
+ }
678
+ setStatus.call(span, status === "ok" ? "ok" : "internal_error");
679
+ }
680
+ function toOptionalString(value) {
681
+ return typeof value === "string" && value.trim() ? value : void 0;
682
+ }
683
+ function getActiveTraceId() {
684
+ const sentry = Sentry;
685
+ if (typeof sentry.getActiveSpan !== "function" || typeof sentry.spanToJSON !== "function") {
686
+ return void 0;
687
+ }
688
+ try {
689
+ const span = sentry.getActiveSpan();
690
+ if (!span) {
691
+ return void 0;
692
+ }
693
+ return toOptionalString(sentry.spanToJSON(span).trace_id);
694
+ } catch {
695
+ return void 0;
696
+ }
697
+ }
698
+ function resolveErrorReference(eventId) {
699
+ const traceId = getActiveTraceId();
700
+ if (!eventId && !traceId) {
701
+ return null;
702
+ }
703
+ if (!traceId) {
704
+ return null;
705
+ }
706
+ return {
707
+ traceId,
708
+ ...eventId ? { eventId } : {}
709
+ };
710
+ }
711
+ var GEN_AI_DEFAULT_MAX_ATTRIBUTE_CHARS = 12e3;
712
+ var GEN_AI_MAX_STRING_CHARS = 2e3;
713
+ var GEN_AI_MAX_ARRAY_ITEMS = 50;
714
+ var GEN_AI_MAX_OBJECT_KEYS = 50;
715
+ function truncateGenAiString(value, maxChars) {
716
+ return value.length > maxChars ? `${value.slice(0, maxChars)}...` : value;
717
+ }
718
+ function sanitizeGenAiValue(value, seen, depth, keyName) {
719
+ if (value === null || value === void 0) {
720
+ return void 0;
721
+ }
722
+ if (typeof value === "string") {
723
+ const shouldTreatAsBlob = (keyName === "data" || keyName === "base64" || keyName?.endsWith("_base64") === true) && value.length > 256;
724
+ if (shouldTreatAsBlob) {
725
+ return `[omitted:${value.length}]`;
726
+ }
727
+ return truncateGenAiString(value, GEN_AI_MAX_STRING_CHARS);
728
+ }
729
+ if (typeof value === "number") {
730
+ return Number.isFinite(value) ? value : void 0;
731
+ }
732
+ if (typeof value === "boolean") {
733
+ return value;
734
+ }
735
+ if (depth >= 8) {
736
+ return "[depth_limit]";
737
+ }
738
+ if (Array.isArray(value)) {
739
+ return value.slice(0, GEN_AI_MAX_ARRAY_ITEMS).map((entry) => sanitizeGenAiValue(entry, seen, depth + 1)).filter((entry) => entry !== void 0);
740
+ }
741
+ if (typeof value !== "object") {
742
+ return String(value);
743
+ }
744
+ if (seen.has(value)) {
745
+ return "[circular]";
746
+ }
747
+ seen.add(value);
748
+ const record = value;
749
+ const out = {};
750
+ for (const [key, entryValue] of Object.entries(record).slice(
751
+ 0,
752
+ GEN_AI_MAX_OBJECT_KEYS
753
+ )) {
754
+ const sanitized = sanitizeGenAiValue(entryValue, seen, depth + 1, key);
755
+ if (sanitized !== void 0) {
756
+ out[key] = sanitized;
757
+ }
758
+ }
759
+ return out;
760
+ }
761
+ function serializeGenAiAttribute(value, maxChars = GEN_AI_DEFAULT_MAX_ATTRIBUTE_CHARS) {
762
+ const sanitized = sanitizeGenAiValue(value, /* @__PURE__ */ new WeakSet(), 0);
763
+ if (sanitized === void 0) {
764
+ return void 0;
765
+ }
766
+ const serialized = typeof sanitized === "string" ? sanitized : JSON.stringify(sanitized);
767
+ if (!serialized) {
768
+ return void 0;
769
+ }
770
+ return truncateGenAiString(serialized, maxChars);
771
+ }
772
+ function asRecord(value) {
773
+ return value && typeof value === "object" ? value : void 0;
774
+ }
775
+ function toFiniteTokenCount(value) {
776
+ if (typeof value !== "number" || !Number.isFinite(value)) {
777
+ return void 0;
778
+ }
779
+ const rounded = Math.floor(value);
780
+ return rounded >= 0 ? rounded : void 0;
781
+ }
782
+ function readTokenCount(root, keys) {
783
+ for (const key of keys) {
784
+ const value = toFiniteTokenCount(root[key]);
785
+ if (value !== void 0) {
786
+ return value;
787
+ }
788
+ }
789
+ return void 0;
790
+ }
791
+ function collectUsageRoots(source) {
792
+ const sourceRecord = asRecord(source);
793
+ if (!sourceRecord) {
794
+ return [];
795
+ }
796
+ const roots = [sourceRecord];
797
+ const usage = asRecord(sourceRecord.usage);
798
+ if (usage) {
799
+ roots.push(usage);
800
+ }
801
+ const tokenUsage = asRecord(sourceRecord.tokenUsage);
802
+ if (tokenUsage) {
803
+ roots.push(tokenUsage);
804
+ }
805
+ const providerMetadata = asRecord(sourceRecord.providerMetadata);
806
+ if (providerMetadata) {
807
+ roots.push(providerMetadata);
808
+ const providerUsage = asRecord(providerMetadata.usage);
809
+ if (providerUsage) {
810
+ roots.push(providerUsage);
811
+ }
812
+ }
813
+ const response = asRecord(sourceRecord.response);
814
+ if (response) {
815
+ roots.push(response);
816
+ const responseUsage = asRecord(response.usage);
817
+ if (responseUsage) {
818
+ roots.push(responseUsage);
819
+ }
820
+ }
821
+ return roots;
822
+ }
823
+ function extractGenAiUsageAttributes(...sources) {
824
+ const roots = sources.flatMap((source) => collectUsageRoots(source));
825
+ if (roots.length === 0) {
826
+ return {};
827
+ }
828
+ const inputTokens = roots.map(
829
+ (root) => readTokenCount(root, [
830
+ "input_tokens",
831
+ "inputTokens",
832
+ "prompt_tokens",
833
+ "promptTokens",
834
+ "inputTokenCount",
835
+ "promptTokenCount"
836
+ ])
837
+ ).find((value) => value !== void 0) ?? void 0;
838
+ const outputTokens = roots.map(
839
+ (root) => readTokenCount(root, [
840
+ "output_tokens",
841
+ "outputTokens",
842
+ "completion_tokens",
843
+ "completionTokens",
844
+ "outputTokenCount",
845
+ "completionTokenCount"
846
+ ])
847
+ ).find((value) => value !== void 0) ?? void 0;
848
+ return {
849
+ ...inputTokens !== void 0 ? { "gen_ai.usage.input_tokens": inputTokens } : {},
850
+ ...outputTokens !== void 0 ? { "gen_ai.usage.output_tokens": outputTokens } : {}
851
+ };
852
+ }
10
853
 
11
854
  // src/chat/plugins/manifest.ts
12
855
  import { z } from "zod";
@@ -1417,6 +2260,20 @@ function createPluginBroker(provider, deps) {
1417
2260
  }
1418
2261
 
1419
2262
  export {
2263
+ logInfo,
2264
+ logWarn,
2265
+ logError,
2266
+ logException,
2267
+ setTags,
2268
+ createRequestContext,
2269
+ withContext,
2270
+ withSpan,
2271
+ setSpanAttributes,
2272
+ setSpanStatus,
2273
+ toOptionalString,
2274
+ resolveErrorReference,
2275
+ serializeGenAiAttribute,
2276
+ extractGenAiUsageAttributes,
1420
2277
  resolveAuthTokenPlaceholder,
1421
2278
  parsePluginManifest,
1422
2279
  CredentialUnavailableError,