@aipack-ai/observability 0.0.1 → 0.0.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/dist/index.d.ts CHANGED
@@ -25,6 +25,12 @@ interface RunRecord {
25
25
  outputTokens: number;
26
26
  cacheRead?: number;
27
27
  cacheWrite?: number;
28
+ /** Phase 6 — Cost:本次 run 总成本(分),由 worker 端 CostCalculator 累加后写入 */
29
+ costCents?: number;
30
+ /** Phase 9 — W3C Trace Context:外部系统传入的父 trace-id(跨系统调用链) */
31
+ parentTraceId?: string;
32
+ /** Phase 9 — W3C Trace Context:标准 32 hex 的 w3c trace-id,用于与 OpenTelemetry 链路打通 */
33
+ w3cTraceId?: string;
28
34
  }
29
35
  interface SpanRecord {
30
36
  traceId: string;
@@ -42,6 +48,8 @@ interface SpanRecord {
42
48
  cacheWrite?: number;
43
49
  /** 会话标识(支撑 session 维度 token 统计) */
44
50
  sessionKey?: string;
51
+ /** Phase 6 — Cost:本次模型调用成本(分),由 worker 端 CostCalculator 计算后写入 */
52
+ costCents?: number;
45
53
  }
46
54
  interface ToolCallRecord {
47
55
  traceId: string;
@@ -99,6 +107,142 @@ interface EventBatch {
99
107
  events: EventRecord[];
100
108
  }
101
109
 
110
+ /**
111
+ * Phase 9 — 采样策略模块。
112
+ *
113
+ * 三种采样策略:
114
+ * - none: 不采样(全量,或兼容旧 sampleRate 行为)
115
+ * - traceid-ratio: 一致性 head 采样。同 traceId 全量子采样(runs/spans/toolCalls 一致)
116
+ * - error-priority: 错误 run 必采,成功 run 按 rate 采样
117
+ * - slow-priority: 慢请求(> P95 阈值)必采,其余按 rate 采样
118
+ */
119
+ type SampleStrategy = {
120
+ type: 'none';
121
+ } | {
122
+ type: 'traceid-ratio';
123
+ rate: number;
124
+ } | {
125
+ type: 'error-priority';
126
+ successRate: number;
127
+ } | {
128
+ type: 'slow-priority';
129
+ rate: number;
130
+ slowThresholdMs?: number;
131
+ }
132
+ /** 【旧兼容】sampleRate 的语义:整 run 全量保留,仅过滤 model/tool spans 与 toolCalls(明细级) */
133
+ | {
134
+ type: 'legacy-detail-ratio';
135
+ rate: number;
136
+ };
137
+ interface SampleStrategyOptions {
138
+ /** 采样策略:'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none'(默认 none) */
139
+ strategy?: 'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none';
140
+ /** traceid-ratio / slow-priority 的采样率(0-1),默认 0.1 */
141
+ rate?: number;
142
+ /** error-priority 下成功 run 的采样率(0-1),默认 0.1 */
143
+ successRate?: number;
144
+ /** slow-priority 下慢请求阈值(ms),未配置时走 SDK 内部滑动 P95(若无法统计则默认 30000ms) */
145
+ slowThresholdMs?: number;
146
+ }
147
+ /** 将用户配置解析为结构化 SampleStrategy */
148
+ declare function resolveSampleStrategy(opts: SampleStrategyOptions): SampleStrategy;
149
+ /** 旧 sampleRate(0–1 简单概率)转换为等价策略:
150
+ * 【重要】语义保留:整 run(+ run span)全量保留,仅 model/tool spans 与 toolCalls 按 rate 过滤。
151
+ */
152
+ declare function legacySampleRateToStrategy(rate: number | undefined): SampleStrategy;
153
+ interface SamplingJudge {
154
+ /** run 是否采样:只对整 run 的"是否保留"决策;spans/toolCalls 随 run */
155
+ shouldKeepRun(info: {
156
+ traceId: string;
157
+ status: 'success' | 'error' | 'validation';
158
+ durationMs: number;
159
+ }): boolean;
160
+ /** 明细(span/toolCall)采样:当 run 采样通过时才进一步过滤;未通过直接 false */
161
+ shouldKeepDetail(traceId: string): boolean;
162
+ }
163
+ declare function createSamplingJudge(strategy: SampleStrategy): SamplingJudge;
164
+
165
+ /**
166
+ * Phase 9 — W3C Trace Context 支持。
167
+ *
168
+ * 标准:https://www.w3.org/TR/trace-context/
169
+ *
170
+ * traceparent 格式:
171
+ * version-traceid-parentid-traceflags
172
+ * 例:00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
173
+ *
174
+ * - version: 2 hex (00)
175
+ * - trace-id: 32 hex(16 bytes,必须非全 0)
176
+ * - parent-id: 16 hex(8 bytes,必须非全 0)
177
+ * - trace-flags: 2 hex(bit 0 = sampled,其余保留为 0)
178
+ */
179
+ interface W3cTraceContext {
180
+ /** 标准 W3C trace-id(32 hex 小写) */
181
+ traceId: string;
182
+ /** 标准 W3C parent-id / span-id(16 hex 小写) */
183
+ parentId: string;
184
+ /** 采样标记(trace-flags 最低位) */
185
+ sampled: boolean;
186
+ /** 原始 traceparent 字符串(用于回传) */
187
+ raw: string;
188
+ }
189
+ type ParseResult = {
190
+ ok: true;
191
+ ctx: W3cTraceContext;
192
+ } | {
193
+ ok: false;
194
+ reason: string;
195
+ };
196
+ /** 解析 traceparent 头。版本 ff 或格式不合法时返回 { ok:false },不抛错。 */
197
+ declare function parseTraceparent(raw: string | null | undefined): ParseResult;
198
+ /** 组装 traceparent 头字符串 */
199
+ declare function formatTraceparent(ctx: {
200
+ traceId: string;
201
+ parentId: string;
202
+ sampled: boolean;
203
+ }): string;
204
+ /**
205
+ * 把外部 W3C trace-id 映射到 aipack traceId(双向绑定)。
206
+ *
207
+ * 策略:
208
+ * - 外部 w3cTraceId 与 aipack traceId 不同(aipack traceId 为随机字符串)。
209
+ * 在 RunRecord 上同时记 w3cTraceId(外部传入)与 aipack 自身 traceId,
210
+ * 面板通过 w3cTraceId 建立跨系统跳转。
211
+ */
212
+ /** 生成一个新的 W3C trace-id(32 hex,非全 0) */
213
+ declare function generateW3cTraceId(): string;
214
+ /** 生成一个新的 W3C parent-id / span-id(16 hex,非全 0) */
215
+ declare function generateW3cParentId(): string;
216
+
217
+ /**
218
+ * Phase 9 — PII 内置脱敏规则库。
219
+ *
220
+ * 默认启用:手机号 / 邮箱 / 身份证号 / 银行卡号 / IPv4 地址。
221
+ * 脱敏策略:
222
+ * - mask:保留前后若干位,中间用 * 替换(默认策略)
223
+ * - hash:SHA-256 截断 16 hex(不可逆,只用于等值对比)
224
+ * - drop:完全删除该字段(极端敏感场景)
225
+ *
226
+ * 本文件仅提供「规则」与「脱敏函数」,不做字段级策略配置。
227
+ * 字段级策略(项目级)由服务端 redact_rules 表管理,SDK 侧通过 API 拉取或默认启用内置。
228
+ */
229
+ type RedactAction = 'mask' | 'hash' | 'drop';
230
+ interface RedactRule {
231
+ /** 规则名,用于面板配置匹配 */
232
+ name: string;
233
+ /** 匹配正则(global,允许多次命中) */
234
+ pattern: RegExp;
235
+ /** 默认脱敏动作 */
236
+ defaultAction: RedactAction;
237
+ /** 掩码函数(仅 action=mask 时调用),默认保留前后 N 位 */
238
+ maskFn?: (match: string) => string;
239
+ }
240
+ declare const BUILTIN_RULES: RedactRule[];
241
+ /** 对单个字符串做所有内置规则的脱敏(默认 mask 动作) */
242
+ declare function redactString(raw: string, actionOverrides?: Record<string, RedactAction>): string;
243
+ /** 对任意 JSON 值(对象/数组/字符串)做深度脱敏 */
244
+ declare function redactValue<T = unknown>(value: T, actionOverrides?: Record<string, RedactAction>): T;
245
+
102
246
  /**
103
247
  * Telemetry 接口实现(客户端 SDK):事件 → 原始记录 → 上报队列 → HttpReporter。
104
248
  *
@@ -110,6 +254,11 @@ interface EventBatch {
110
254
  *
111
255
  * 注意:runtime 的 emitTelemetry 会解构方法后调用(fn(info)),故所有回调必须为
112
256
  * 箭头函数属性以绑定 this,否则 this.queued / this.startedAt 为 undefined。
257
+ *
258
+ * Phase 9 新增:
259
+ * - 采样策略:error-priority / slow-priority / traceid-ratio(整 run 维度 + 明细联动)
260
+ * - W3C Trace Context:traceparent 头解析 → parentTraceId/w3cTraceId 写入 RunRecord
261
+ * - PII 脱敏:内置规则库深度遍历 events.data 做脱敏(默认启用,可关闭)
113
262
  */
114
263
 
115
264
  interface FlushQueueOptions {
@@ -117,12 +266,25 @@ interface FlushQueueOptions {
117
266
  intervalMs?: number;
118
267
  /** 积攒条数触发(含 runs/spans/toolCalls/permissions 总数),默认 50 */
119
268
  batchSize?: number;
120
- /** P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events 全量。缺省 1(全量) */
269
+ /** 【旧兼容】P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events 全量。
270
+ * 新版 sampleStrategy 优先,sampleRate 仅在未配置 sampleStrategy 时生效(等价 traceid-ratio)。 */
121
271
  sampleRate?: number;
122
- /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报) */
272
+ /** Phase 9 — 采样策略(error-priority / slow-priority / traceid-ratio)。优先级高于 sampleRate。 */
273
+ sampleStrategy?: SampleStrategyOptions;
274
+ /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报)。
275
+ * 当内置脱敏启用时,自定义钩子在内置之后执行(用户可进一步定制)。 */
123
276
  redact?: (batch: EventBatch) => EventBatch;
277
+ /** Phase 9 — 是否启用内置 PII 脱敏规则(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */
278
+ redactEnabled?: boolean;
279
+ /** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop' }。未配置规则走默认 mask。 */
280
+ redactOverrides?: Record<string, RedactAction>;
124
281
  /** 发布版本:注入每条 run 记录(RunRecord.appVersion),供收集端按版本聚合。缺省不携带 */
125
282
  appVersion?: string;
283
+ /**
284
+ * Phase 9 — 外部 traceparent 上下文(来自 HTTP 请求头 / 消息队列属性)。
285
+ * 传入字符串时自动解析;解析失败则忽略(不影响正常上报)。
286
+ */
287
+ traceparent?: string | W3cTraceContext;
126
288
  }
127
289
  declare class ObservabilityTelemetry implements Telemetry {
128
290
  private reporter;
@@ -132,9 +294,16 @@ declare class ObservabilityTelemetry implements Telemetry {
132
294
  /** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */
133
295
  private inFlightOrder;
134
296
  private batchSize;
135
- private sampleRate;
136
297
  private redact?;
137
298
  private appVersion?;
299
+ private redactEnabled;
300
+ private redactOverrides?;
301
+ private sampleStrategy;
302
+ private judge;
303
+ /** 外部传入的 traceparent 上下文(共享:所有 run 视作其子链路) */
304
+ private w3cCtx;
305
+ /** traceId → 为该 run 生成的 w3c trace 数据(上报时注入 RunRecord) */
306
+ private w3cByRun;
138
307
  constructor(reporter: {
139
308
  send(batch: EventBatch): Promise<boolean>;
140
309
  }, opts?: FlushQueueOptions);
@@ -154,17 +323,18 @@ declare class ObservabilityTelemetry implements Telemetry {
154
323
  /**
155
324
  * P2-1 自定义业务事件(通用埋点)。
156
325
  * run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。
326
+ * Phase 9:内置 PII 脱敏在 flush 阶段对 data 深度遍历。
157
327
  */
158
328
  emit(name: string, data?: unknown, opts?: {
159
329
  traceId?: string;
160
330
  sessionKey?: string;
161
331
  }): void;
332
+ /** Phase 9 — 主动读取/注入当前 W3C traceparent(可传给下游 HTTP 调用)。 */
333
+ currentTraceparent(): string | null;
162
334
  /** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */
163
335
  flush(): void;
164
336
  /** 停止定时器并等残留上报完成(进程退出前调用) */
165
337
  close(): Promise<void>;
166
- /** P2-1 明细采样判定:rate>=1 恒过、<=0 恒弃、否则按概率 */
167
- private sample;
168
338
  private runRecord;
169
339
  private runSpan;
170
340
  private isEmpty;
@@ -314,12 +484,38 @@ interface CreateObservabilityOptions {
314
484
  serviceName?: string;
315
485
  headers?: Record<string, string>;
316
486
  };
317
- /** P2-1 明细采样率(0–1):只对 model/tool spans 与 toolCalls 采样,runs/permissions/events 全量。缺省 1 */
487
+ /**
488
+ * 【旧兼容】P2-1 明细采样率(0–1):只对 model/tool spans 与 toolCalls 采样,
489
+ * runs/permissions/events 全量。缺省 1。
490
+ * 新版 sampleStrategy 优先;sampleRate 未设置 sampleStrategy 时等价 traceid-ratio。
491
+ */
318
492
  sampleRate?: number;
319
- /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报),示例见 docs/observability-roadmap.md §P2-1 */
493
+ /**
494
+ * Phase 9 — 采样策略:错误/慢请求优先或一致性 traceId 采样。
495
+ * 优先级高于 sampleRate。
496
+ *
497
+ * 示例:
498
+ * // 错误 100% 保留,成功按 10% 采样
499
+ * sampleStrategy: { strategy: 'error-priority', successRate: 0.1 }
500
+ * // 同 trace 稳定采/弃(跨明细一致)
501
+ * sampleStrategy: { strategy: 'traceid-ratio', rate: 0.2 }
502
+ */
503
+ sampleStrategy?: SampleStrategyOptions;
504
+ /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报),在内置脱敏之后执行 */
320
505
  redact?: (batch: EventBatch) => EventBatch;
506
+ /** Phase 9 — 是否启用内置 PII 脱敏(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */
507
+ redactEnabled?: boolean;
508
+ /** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop', idCard: 'drop' } */
509
+ redactOverrides?: Record<string, RedactAction>;
321
510
  /** 发布版本(如 '1.3.0'),随每条 run 上报,供收集端按版本聚合对比。缺省不携带 */
322
511
  version?: string;
512
+ /**
513
+ * Phase 9 — W3C Trace Context 父链路。
514
+ * - 字符串:traceparent 请求头原值(如 `00-xxxxxx-yyyyyy-01`)
515
+ * - 对象:已解析的 W3cTraceContext
516
+ * 配置后,所有 run 视作其子链路,面板可跨系统跳转。
517
+ */
518
+ traceparent?: string | W3cTraceContext;
323
519
  }
324
520
  interface Observability {
325
521
  /** 注入 RuntimeOptions.telemetry */
@@ -335,7 +531,12 @@ interface Observability {
335
531
  flush(): void;
336
532
  /** 停止定时器并等残留上报完成(进程退出前调用) */
337
533
  close(): Promise<void>;
534
+ /**
535
+ * Phase 9 — 当前链路的 traceparent 字符串(可传给下游 HTTP 调用实现跨系统追踪)。
536
+ * 无 in-flight run 且未配置外部 traceparent 时返回 null。
537
+ */
538
+ currentTraceparent(): string | null;
338
539
  }
339
540
  declare function createObservability(opts: CreateObservabilityOptions): Observability;
340
541
 
341
- export { type CreateObservabilityOptions, type EventBatch, type EventRecord, type FlushQueueOptions, HttpReporter, type LogFormat, type LogLevel, type Logger, type LoggerOptions, type Observability, ObservabilityTelemetry, type OtlpExporterOptions, type OtlpTraceExporter, type PermissionRecord, type ReporterOptions, type RetryRecord, type RunRecord, type SpanRecord, type ToolCallRecord, createLogger, createObservability, createOtlpTraceExporter, toOtlpJsonTraces };
542
+ export { BUILTIN_RULES, type CreateObservabilityOptions, type EventBatch, type EventRecord, type FlushQueueOptions, HttpReporter, type LogFormat, type LogLevel, type Logger, type LoggerOptions, type Observability, ObservabilityTelemetry, type OtlpExporterOptions, type OtlpTraceExporter, type PermissionRecord, type RedactAction, type RedactRule, type ReporterOptions, type RetryRecord, type RunRecord, type SampleStrategy, type SamplingJudge, type SampleStrategyOptions as SamplingOptions, type SpanRecord, type ToolCallRecord, type W3cTraceContext, createLogger, createObservability, createOtlpTraceExporter, createSamplingJudge, formatTraceparent, generateW3cParentId, generateW3cTraceId, legacySampleRateToStrategy, parseTraceparent, redactString, redactValue, resolveSampleStrategy, toOtlpJsonTraces };
package/dist/index.js CHANGED
@@ -165,6 +165,322 @@ function trimBatch(batch, max) {
165
165
  return out;
166
166
  }
167
167
 
168
+ // src/sampling.ts
169
+ function resolveSampleStrategy(opts) {
170
+ const type = opts.strategy ?? "none";
171
+ switch (type) {
172
+ case "traceid-ratio": {
173
+ const rate = clamp01(opts.rate ?? 0.1);
174
+ return { type: "traceid-ratio", rate };
175
+ }
176
+ case "error-priority": {
177
+ const successRate = clamp01(opts.successRate ?? 0.1);
178
+ return { type: "error-priority", successRate };
179
+ }
180
+ case "slow-priority": {
181
+ const rate = clamp01(opts.rate ?? 0.1);
182
+ return { type: "slow-priority", rate, slowThresholdMs: opts.slowThresholdMs };
183
+ }
184
+ case "none":
185
+ default:
186
+ return { type: "none" };
187
+ }
188
+ }
189
+ function legacySampleRateToStrategy(rate) {
190
+ if (rate === void 0 || rate >= 1) return { type: "none" };
191
+ const r = clamp01(rate);
192
+ return { type: "legacy-detail-ratio", rate: r };
193
+ }
194
+ function createSamplingJudge(strategy) {
195
+ if (strategy.type === "traceid-ratio") {
196
+ const rate = strategy.rate;
197
+ if (rate <= 0) {
198
+ return {
199
+ shouldKeepRun: () => false,
200
+ shouldKeepDetail: () => false
201
+ };
202
+ }
203
+ if (rate >= 1) {
204
+ return {
205
+ shouldKeepRun: () => true,
206
+ shouldKeepDetail: () => true
207
+ };
208
+ }
209
+ return {
210
+ shouldKeepRun: ({ traceId }) => traceIdConsistentHash(traceId) < rate,
211
+ shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate
212
+ };
213
+ }
214
+ if (strategy.type === "error-priority") {
215
+ const { successRate } = strategy;
216
+ if (successRate <= 0) {
217
+ return {
218
+ shouldKeepRun: ({ status }) => status === "error",
219
+ shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
220
+ };
221
+ }
222
+ if (successRate >= 1) {
223
+ return {
224
+ shouldKeepRun: () => true,
225
+ shouldKeepDetail: () => true
226
+ };
227
+ }
228
+ return {
229
+ shouldKeepRun: ({ traceId, status }) => {
230
+ const keep = status === "error" ? true : Math.random() < successRate;
231
+ if (keep) sampledRunSet.add(traceId);
232
+ else sampledRunSet.delete(traceId);
233
+ return keep;
234
+ },
235
+ shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
236
+ };
237
+ }
238
+ if (strategy.type === "slow-priority") {
239
+ const { rate, slowThresholdMs } = strategy;
240
+ const window = new SlidingP95();
241
+ const thresholdFallback = slowThresholdMs ?? 3e4;
242
+ if (rate <= 0) {
243
+ return {
244
+ shouldKeepRun: ({ traceId, durationMs }) => {
245
+ const p95 = window.get() ?? thresholdFallback;
246
+ window.push(durationMs);
247
+ const keep = durationMs >= p95;
248
+ if (keep) sampledRunSet.add(traceId);
249
+ else sampledRunSet.delete(traceId);
250
+ return keep;
251
+ },
252
+ shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
253
+ };
254
+ }
255
+ if (rate >= 1) {
256
+ return {
257
+ shouldKeepRun: () => true,
258
+ shouldKeepDetail: () => true
259
+ };
260
+ }
261
+ return {
262
+ shouldKeepRun: ({ traceId, durationMs }) => {
263
+ const p95 = window.get() ?? thresholdFallback;
264
+ window.push(durationMs);
265
+ const keep = durationMs >= p95 ? true : Math.random() < rate;
266
+ if (keep) sampledRunSet.add(traceId);
267
+ else sampledRunSet.delete(traceId);
268
+ return keep;
269
+ },
270
+ shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
271
+ };
272
+ }
273
+ if (strategy.type === "legacy-detail-ratio") {
274
+ const rate = strategy.rate;
275
+ if (rate <= 0) {
276
+ return {
277
+ shouldKeepRun: ({ traceId }) => {
278
+ sampledRunSet.add(traceId);
279
+ return true;
280
+ },
281
+ shouldKeepDetail: () => false
282
+ };
283
+ }
284
+ if (rate >= 1) {
285
+ return {
286
+ shouldKeepRun: ({ traceId }) => {
287
+ sampledRunSet.add(traceId);
288
+ return true;
289
+ },
290
+ shouldKeepDetail: () => true
291
+ };
292
+ }
293
+ return {
294
+ shouldKeepRun: ({ traceId }) => {
295
+ sampledRunSet.add(traceId);
296
+ return true;
297
+ },
298
+ shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate
299
+ };
300
+ }
301
+ return {
302
+ shouldKeepRun: ({ traceId }) => {
303
+ sampledRunSet.add(traceId);
304
+ return true;
305
+ },
306
+ shouldKeepDetail: () => true
307
+ };
308
+ }
309
+ var sampledRunSet = /* @__PURE__ */ new Set();
310
+ function clamp01(v) {
311
+ if (!Number.isFinite(v)) return 0;
312
+ if (v < 0) return 0;
313
+ if (v > 1) return 1;
314
+ return v;
315
+ }
316
+ function traceIdConsistentHash(traceId) {
317
+ let h = 2166136261;
318
+ for (let i = 0; i < traceId.length; i++) {
319
+ h ^= traceId.charCodeAt(i);
320
+ h = Math.imul(h, 16777619);
321
+ }
322
+ return (h >>> 0) / 4294967296;
323
+ }
324
+ var SlidingP95 = class _SlidingP95 {
325
+ buf = [];
326
+ static CAP = 1e3;
327
+ push(d) {
328
+ this.buf.push(d);
329
+ if (this.buf.length > _SlidingP95.CAP) this.buf.shift();
330
+ }
331
+ /** 返回 p95,样本不足返回 undefined(调用方 fallback 到默认阈值) */
332
+ get() {
333
+ if (this.buf.length < 30) return void 0;
334
+ const sorted = [...this.buf].sort((a, b) => a - b);
335
+ const idx = Math.floor(sorted.length * 0.95);
336
+ return sorted[Math.min(idx, sorted.length - 1)];
337
+ }
338
+ };
339
+
340
+ // src/w3c-trace-context.ts
341
+ function parseTraceparent(raw) {
342
+ if (!raw) return { ok: false, reason: "missing" };
343
+ const s = raw.trim().toLowerCase();
344
+ const parts = s.split("-");
345
+ if (parts.length !== 4) return { ok: false, reason: "format: parts != 4" };
346
+ const [versionHex, traceIdHex, parentIdHex, flagsHex] = parts;
347
+ if (!/^[0-9a-f]{2}$/.test(versionHex)) return { ok: false, reason: "invalid version" };
348
+ if (versionHex === "ff") return { ok: false, reason: "version ff is invalid" };
349
+ if (!/^[0-9a-f]{32}$/.test(traceIdHex)) return { ok: false, reason: "invalid trace-id" };
350
+ if (/^0{32}$/.test(traceIdHex)) return { ok: false, reason: "trace-id all zeros" };
351
+ if (!/^[0-9a-f]{16}$/.test(parentIdHex)) return { ok: false, reason: "invalid parent-id" };
352
+ if (/^0{16}$/.test(parentIdHex)) return { ok: false, reason: "parent-id all zeros" };
353
+ if (!/^[0-9a-f]{2}$/.test(flagsHex)) return { ok: false, reason: "invalid trace-flags" };
354
+ const flags = parseInt(flagsHex, 16);
355
+ return {
356
+ ok: true,
357
+ ctx: {
358
+ traceId: traceIdHex,
359
+ parentId: parentIdHex,
360
+ sampled: (flags & 1) === 1,
361
+ raw: s
362
+ }
363
+ };
364
+ }
365
+ function formatTraceparent(ctx) {
366
+ const flags = ctx.sampled ? "01" : "00";
367
+ return `00-${ctx.traceId}-${ctx.parentId}-${flags}`;
368
+ }
369
+ function generateW3cTraceId() {
370
+ const bytes = cryptoRandomBytes(16);
371
+ if (bytes.every((b) => b === 0)) bytes[0] = 1;
372
+ return toHex(bytes);
373
+ }
374
+ function generateW3cParentId() {
375
+ const bytes = cryptoRandomBytes(8);
376
+ if (bytes.every((b) => b === 0)) bytes[0] = 1;
377
+ return toHex(bytes);
378
+ }
379
+ function cryptoRandomBytes(n) {
380
+ const cryptoObj = globalThis.crypto;
381
+ if (typeof globalThis !== "undefined" && cryptoObj?.getRandomValues) {
382
+ const buf = new Uint8Array(n);
383
+ cryptoObj.getRandomValues(buf);
384
+ return buf;
385
+ }
386
+ const out = new Uint8Array(n);
387
+ for (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256);
388
+ return out;
389
+ }
390
+ function toHex(bytes) {
391
+ let s = "";
392
+ for (const b of bytes) s += b.toString(16).padStart(2, "0");
393
+ return s;
394
+ }
395
+
396
+ // src/redact/rules.ts
397
+ import { createHash } from "crypto";
398
+ var PHONE_RE = /(?<!\d)1[3-9]\d{9}(?!\d)/g;
399
+ var EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
400
+ var ID_CARD_RE = /(?<!\d)\d{17}[\dXx](?!\d)/g;
401
+ var BANK_CARD_RE = /(?<!\d)\d{16,19}(?!\d)/g;
402
+ var IPV4_RE = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
403
+ var BUILTIN_RULES = [
404
+ {
405
+ name: "phone",
406
+ pattern: PHONE_RE,
407
+ defaultAction: "mask",
408
+ maskFn: (m) => m.slice(0, 3) + "****" + m.slice(-4)
409
+ // 139****1234
410
+ },
411
+ {
412
+ name: "email",
413
+ pattern: EMAIL_RE,
414
+ defaultAction: "mask",
415
+ maskFn: (m) => {
416
+ const at = m.indexOf("@");
417
+ if (at <= 1) return "*".repeat(at) + m.slice(at);
418
+ const user = m.slice(0, at);
419
+ return user[0] + "*".repeat(Math.max(at - 2, 1)) + user[at - 1] + m.slice(at);
420
+ }
421
+ },
422
+ {
423
+ name: "idCard",
424
+ pattern: ID_CARD_RE,
425
+ defaultAction: "mask",
426
+ maskFn: (m) => m.slice(0, 6) + "********" + m.slice(-4)
427
+ // 6 + 8* + 4 = 18
428
+ },
429
+ {
430
+ name: "bankCard",
431
+ pattern: BANK_CARD_RE,
432
+ defaultAction: "mask",
433
+ maskFn: (m) => m.slice(0, 4) + " **** **** " + m.slice(-4)
434
+ // 6222 **** **** 1234
435
+ },
436
+ {
437
+ name: "ipv4",
438
+ pattern: IPV4_RE,
439
+ defaultAction: "mask",
440
+ maskFn: (m) => m.replace(/\.\d+\.\d+$/, ".*.*")
441
+ // 192.168.*.*
442
+ }
443
+ ];
444
+ function redactString(raw, actionOverrides) {
445
+ if (!raw) return raw;
446
+ let out = raw;
447
+ for (const rule of BUILTIN_RULES) {
448
+ const action = actionOverrides?.[rule.name] ?? rule.defaultAction;
449
+ if (action === "drop") {
450
+ out = out.replace(rule.pattern, "");
451
+ } else if (action === "hash") {
452
+ out = out.replace(rule.pattern, (m) => sha256Trunc(m));
453
+ } else {
454
+ const fn = rule.maskFn ?? defaultMask;
455
+ out = out.replace(rule.pattern, fn);
456
+ }
457
+ rule.pattern.lastIndex = 0;
458
+ }
459
+ return out;
460
+ }
461
+ function redactValue(value, actionOverrides) {
462
+ if (value === null || value === void 0) return value;
463
+ if (typeof value === "string") return redactString(value, actionOverrides);
464
+ if (typeof value !== "object") return value;
465
+ if (Array.isArray(value)) {
466
+ return value.map((v) => redactValue(v, actionOverrides));
467
+ }
468
+ const out = {};
469
+ for (const [k, v] of Object.entries(value)) {
470
+ out[k] = redactValue(v, actionOverrides);
471
+ }
472
+ return out;
473
+ }
474
+ function defaultMask(m) {
475
+ if (m.length <= 2) return "*".repeat(m.length);
476
+ const keepHead = Math.max(1, Math.floor(m.length * 0.25));
477
+ const keepTail = Math.max(1, Math.floor(m.length * 0.25));
478
+ return m.slice(0, keepHead) + "*".repeat(m.length - keepHead - keepTail) + m.slice(-keepTail);
479
+ }
480
+ function sha256Trunc(s) {
481
+ return createHash("sha256").update(s).digest("hex").slice(0, 16);
482
+ }
483
+
168
484
  // src/telemetry.ts
169
485
  var emptyBatch = () => ({
170
486
  runs: [],
@@ -178,9 +494,17 @@ var ObservabilityTelemetry = class {
178
494
  constructor(reporter, opts = {}) {
179
495
  this.reporter = reporter;
180
496
  this.batchSize = opts.batchSize ?? 50;
181
- this.sampleRate = opts.sampleRate ?? 1;
182
497
  this.redact = opts.redact;
183
498
  this.appVersion = opts.appVersion;
499
+ this.redactEnabled = opts.redactEnabled ?? true;
500
+ this.redactOverrides = opts.redactOverrides;
501
+ if (opts.sampleStrategy && opts.sampleStrategy.strategy) {
502
+ this.sampleStrategy = resolveSampleStrategy(opts.sampleStrategy);
503
+ } else {
504
+ this.sampleStrategy = legacySampleRateToStrategy(opts.sampleRate);
505
+ }
506
+ this.judge = createSamplingJudge(this.sampleStrategy);
507
+ this.w3cCtx = parseTraceparentInput(opts.traceparent);
184
508
  const intervalMs = opts.intervalMs ?? 5e3;
185
509
  this.timer = setInterval(() => void this.flush(), intervalMs);
186
510
  this.timer.unref?.();
@@ -193,24 +517,47 @@ var ObservabilityTelemetry = class {
193
517
  /** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */
194
518
  inFlightOrder = [];
195
519
  batchSize;
196
- sampleRate;
197
520
  redact;
198
521
  appVersion;
522
+ redactEnabled;
523
+ redactOverrides;
524
+ // ─── Phase 9:采样 ───────────────────────────────────────────────
525
+ sampleStrategy;
526
+ judge;
527
+ // ─── Phase 9:W3C Trace Context ──────────────────────────────────
528
+ /** 外部传入的 traceparent 上下文(共享:所有 run 视作其子链路) */
529
+ w3cCtx;
530
+ /** traceId → 为该 run 生成的 w3c trace 数据(上报时注入 RunRecord) */
531
+ w3cByRun = /* @__PURE__ */ new Map();
199
532
  onRunStart = (info) => {
200
533
  this.startedAt.set(info.traceId, info.queuedAt);
201
534
  this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);
202
535
  this.inFlightOrder.push(info.traceId);
536
+ const parent = this.w3cCtx;
537
+ const w3cTraceId = parent ? parent.traceId : generateW3cTraceId();
538
+ const parentTraceId = parent ? parent.traceId : void 0;
539
+ this.w3cByRun.set(info.traceId, { w3cTraceId, parentTraceId });
203
540
  };
204
541
  onRunEnd = (info) => {
205
542
  const queuedAt = this.startedAt.get(info.traceId) ?? Date.now() - info.durationMs;
206
543
  this.startedAt.delete(info.traceId);
207
544
  this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);
208
- this.queued.runs.push(this.runRecord(info, queuedAt));
545
+ const keep = this.judge.shouldKeepRun({
546
+ traceId: info.traceId,
547
+ status: info.success ? "success" : info.errorClass === "validation" ? "validation" : "error",
548
+ durationMs: info.durationMs
549
+ });
550
+ if (!keep) {
551
+ this.w3cByRun.delete(info.traceId);
552
+ return;
553
+ }
554
+ const run = this.runRecord(info, queuedAt);
555
+ this.queued.runs.push(run);
209
556
  this.queued.spans.push(this.runSpan(info, queuedAt));
210
557
  this.maybeFlush();
211
558
  };
212
559
  onToolCall = (info) => {
213
- if (!this.sample()) return;
560
+ if (!this.judge.shouldKeepDetail(info.traceId)) return;
214
561
  const startedAt = Date.now() - info.durationMs;
215
562
  this.queued.spans.push({
216
563
  traceId: info.traceId,
@@ -234,7 +581,7 @@ var ObservabilityTelemetry = class {
234
581
  this.maybeFlush();
235
582
  };
236
583
  onModelCall = (info) => {
237
- if (!this.sample()) return;
584
+ if (!this.judge.shouldKeepDetail(info.traceId)) return;
238
585
  this.queued.spans.push({
239
586
  traceId: info.traceId,
240
587
  spanId: info.spanId,
@@ -254,6 +601,7 @@ var ObservabilityTelemetry = class {
254
601
  this.maybeFlush();
255
602
  };
256
603
  onRetry = (info) => {
604
+ if (!this.judge.shouldKeepDetail(info.traceId)) return;
257
605
  this.queued.retries.push({
258
606
  traceId: info.traceId,
259
607
  spanId: info.spanId,
@@ -288,6 +636,7 @@ var ObservabilityTelemetry = class {
288
636
  /**
289
637
  * P2-1 自定义业务事件(通用埋点)。
290
638
  * run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。
639
+ * Phase 9:内置 PII 脱敏在 flush 阶段对 data 深度遍历。
291
640
  */
292
641
  emit(name, data, opts = {}) {
293
642
  this.queued.events.push({
@@ -299,12 +648,29 @@ var ObservabilityTelemetry = class {
299
648
  });
300
649
  this.maybeFlush();
301
650
  }
651
+ /** Phase 9 — 主动读取/注入当前 W3C traceparent(可传给下游 HTTP 调用)。 */
652
+ currentTraceparent() {
653
+ const traceId = this.currentContext().traceId;
654
+ if (!traceId) {
655
+ if (this.w3cCtx) return this.w3cCtx.raw;
656
+ return null;
657
+ }
658
+ const w3c = this.w3cByRun.get(traceId);
659
+ const w3cTraceId = w3c?.w3cTraceId ?? (this.w3cCtx?.traceId ?? generateW3cTraceId());
660
+ const parentId = aipackSpanIdToW3c(traceId);
661
+ return formatTraceparent({ traceId: w3cTraceId, parentId, sampled: true });
662
+ }
302
663
  /** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */
303
664
  flush() {
304
665
  if (this.isEmpty()) return;
305
666
  const batch = this.queued;
306
667
  this.queued = emptyBatch();
307
- void this.reporter.send(this.redact ? this.redact(batch) : batch);
668
+ let final = batch;
669
+ if (this.redactEnabled) {
670
+ final = applyBuiltinRedact(final, this.redactOverrides);
671
+ }
672
+ if (this.redact) final = this.redact(final);
673
+ void this.reporter.send(final);
308
674
  }
309
675
  /** 停止定时器并等残留上报完成(进程退出前调用) */
310
676
  async close() {
@@ -315,15 +681,14 @@ var ObservabilityTelemetry = class {
315
681
  if (this.isEmpty()) return;
316
682
  const batch = this.queued;
317
683
  this.queued = emptyBatch();
318
- await this.reporter.send(this.redact ? this.redact(batch) : batch);
319
- }
320
- /** P2-1 明细采样判定:rate>=1 恒过、<=0 恒弃、否则按概率 */
321
- sample() {
322
- if (this.sampleRate >= 1) return true;
323
- if (this.sampleRate <= 0) return false;
324
- return Math.random() < this.sampleRate;
684
+ let final = batch;
685
+ if (this.redactEnabled) final = applyBuiltinRedact(final, this.redactOverrides);
686
+ if (this.redact) final = this.redact(final);
687
+ await this.reporter.send(final);
325
688
  }
326
689
  runRecord(info, queuedAt) {
690
+ const w3c = this.w3cByRun.get(info.traceId);
691
+ this.w3cByRun.delete(info.traceId);
327
692
  return {
328
693
  traceId: info.traceId,
329
694
  startedAt: queuedAt,
@@ -342,7 +707,9 @@ var ObservabilityTelemetry = class {
342
707
  outputTokens: info.tokens.output,
343
708
  cacheRead: info.tokens.cacheRead,
344
709
  cacheWrite: info.tokens.cacheWrite,
345
- ...this.appVersion !== void 0 ? { appVersion: this.appVersion } : {}
710
+ ...this.appVersion !== void 0 ? { appVersion: this.appVersion } : {},
711
+ ...w3c?.parentTraceId !== void 0 ? { parentTraceId: w3c.parentTraceId } : {},
712
+ ...w3c?.w3cTraceId !== void 0 ? { w3cTraceId: w3c.w3cTraceId } : {}
346
713
  };
347
714
  }
348
715
  runSpan(info, queuedAt) {
@@ -368,6 +735,52 @@ var ObservabilityTelemetry = class {
368
735
  if (total >= this.batchSize) this.flush();
369
736
  }
370
737
  };
738
+ function applyBuiltinRedact(batch, overrides) {
739
+ const events = batch.events.map(
740
+ (e) => e.data === void 0 ? e : { ...e, data: redactValue(e.data, overrides) }
741
+ );
742
+ const permissions = batch.permissions.map((p) => ({
743
+ ...p,
744
+ reason: redactString2(p.reason, overrides)
745
+ }));
746
+ return {
747
+ runs: batch.runs,
748
+ // runs 字段结构化,不做默认脱敏(避免破坏聚合)
749
+ spans: batch.spans,
750
+ toolCalls: batch.toolCalls,
751
+ retries: batch.retries,
752
+ events,
753
+ permissions
754
+ };
755
+ }
756
+ function parseTraceparentInput(input) {
757
+ if (!input) return null;
758
+ if (typeof input === "string") {
759
+ const r = parseTraceparent(input);
760
+ return r.ok ? r.ctx : null;
761
+ }
762
+ return input && typeof input.traceId === "string" ? input : null;
763
+ }
764
+ function aipackSpanIdToW3c(aipackTraceId) {
765
+ let h = 2166136261;
766
+ for (let i = 0; i < aipackTraceId.length; i++) {
767
+ h ^= aipackTraceId.charCodeAt(i);
768
+ h = Math.imul(h, 16777619);
769
+ }
770
+ const hex = (h >>> 0).toString(16).padStart(8, "0");
771
+ let h2 = 2216829733;
772
+ for (let i = aipackTraceId.length - 1; i >= 0; i--) {
773
+ h2 ^= aipackTraceId.charCodeAt(i);
774
+ h2 = Math.imul(h2, 16777619);
775
+ }
776
+ const hex2 = (h2 >>> 0).toString(16).padStart(8, "0");
777
+ const out = hex + hex2;
778
+ if (/^0{16}$/.test(out)) return "0000000000000001";
779
+ return out;
780
+ }
781
+ function redactString2(raw, overrides) {
782
+ return redactValue(raw, overrides);
783
+ }
371
784
 
372
785
  // src/logger.ts
373
786
  var LEVEL_ORDER = { debug: 10, info: 20, warn: 30, error: 40 };
@@ -432,9 +845,9 @@ function defaultDest(line) {
432
845
  }
433
846
 
434
847
  // src/otlp.ts
435
- import { createHash } from "crypto";
848
+ import { createHash as createHash2 } from "crypto";
436
849
  function bytesOf(input, n) {
437
- return createHash("md5").update(input).digest().subarray(0, n);
850
+ return createHash2("md5").update(input).digest().subarray(0, n);
438
851
  }
439
852
  function str(value) {
440
853
  return { stringValue: value == null ? "" : String(value) };
@@ -593,8 +1006,12 @@ function createObservability(opts) {
593
1006
  intervalMs: opts.flushIntervalMs,
594
1007
  batchSize: opts.flushBatchSize,
595
1008
  sampleRate: opts.sampleRate,
1009
+ sampleStrategy: opts.sampleStrategy,
596
1010
  redact: opts.redact,
597
- appVersion: opts.version
1011
+ redactEnabled: opts.redactEnabled,
1012
+ redactOverrides: opts.redactOverrides,
1013
+ appVersion: opts.version,
1014
+ traceparent: opts.traceparent
598
1015
  });
599
1016
  const logger = createLogger({
600
1017
  tags: { app: opts.appId },
@@ -607,15 +1024,26 @@ function createObservability(opts) {
607
1024
  flush: () => telemetry.flush(),
608
1025
  close: async () => {
609
1026
  await telemetry.close();
610
- }
1027
+ },
1028
+ currentTraceparent: () => telemetry.currentTraceparent()
611
1029
  };
612
1030
  }
613
1031
  export {
1032
+ BUILTIN_RULES,
614
1033
  HttpReporter,
615
1034
  ObservabilityTelemetry,
616
1035
  createLogger,
617
1036
  createObservability,
618
1037
  createOtlpTraceExporter,
1038
+ createSamplingJudge,
1039
+ formatTraceparent,
1040
+ generateW3cParentId,
1041
+ generateW3cTraceId,
1042
+ legacySampleRateToStrategy,
1043
+ parseTraceparent,
1044
+ redactString,
1045
+ redactValue,
1046
+ resolveSampleStrategy,
619
1047
  toOtlpJsonTraces
620
1048
  };
621
1049
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reporter.ts","../src/telemetry.ts","../src/logger.ts","../src/otlp.ts","../src/index.ts"],"sourcesContent":["/**\n * HttpReporter — 把批量事件上报到收集服务(埋点模式)。\n *\n * - POST {endpoint}/api/v1/ingest,携带 x-app-id / x-app-secret 鉴权头\n * - 上报失败(网络错误 / 5xx / 429)→ 写入本地缓存文件({cacheDir}/{appId}.json),\n * 下次 send 前先补报缓存,全部成功才删除\n * - 4xx(鉴权失败、参数错误)判定为不可重试,直接丢弃并 warn,避免缓存无限重试\n * - 缓存条数上限 maxCacheSize,超出后丢弃最旧记录\n * - 并发保护:send 串行执行,重复调用合并为一次\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { EventBatch } from './types';\n\nexport interface ReporterOptions {\n /** 收集服务地址,如 http://localhost:8787(必填) */\n endpoint: string;\n appId: string;\n appSecret: string;\n /** 缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限(含 runs/spans/toolCalls/permissions),默认 2000 */\n maxCacheSize?: number;\n /** 上报超时(ms),默认 5000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nconst EMPTY_BATCH = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class HttpReporter {\n private endpoint: string;\n private appId: string;\n private appSecret: string;\n private cachePath: string;\n private maxCacheSize: number;\n private timeoutMs: number;\n private fetchImpl: typeof fetch;\n /** 串行发送锁:true 表示有发送在进行中 */\n private sending = false;\n /** 发送期间新到的批次,发送完成后继续补发 */\n private pending: EventBatch[] = [];\n\n constructor(opts: ReporterOptions) {\n this.endpoint = opts.endpoint.replace(/\\/+$/, '');\n this.appId = opts.appId;\n this.appSecret = opts.appSecret;\n this.cachePath = path.join(\n opts.cacheDir ?? path.join(process.cwd(), '.aipack', 'observability'),\n `${opts.appId}.json`,\n );\n this.maxCacheSize = opts.maxCacheSize ?? 2000;\n this.timeoutMs = opts.timeoutMs ?? 5000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n }\n\n /** 上报批次;失败自动落缓存,返回是否成功(测试断言用) */\n async send(batch: EventBatch): Promise<boolean> {\n this.pending.push(batch);\n if (this.sending) return false; // 已有一轮发送在跑,数据会被顺带带走\n this.sending = true;\n try {\n return await this.drain();\n } finally {\n this.sending = false;\n }\n }\n\n /** 排空所有批次(含缓存补报)。返回最后一轮是否成功 */\n private async drain(): Promise<boolean> {\n let lastOk = false;\n for (;;) {\n const cache = await this.readCache();\n const batch = mergeBatches(cache, ...this.pending);\n this.pending = [];\n if (isEmpty(batch)) return lastOk;\n\n lastOk = await this.tryUpload(batch);\n if (!lastOk) {\n // 失败 → 写回缓存,等待下次 flush\n await this.writeCache(batch);\n return false;\n }\n // 成功 → 清缓存\n await this.removeCache();\n }\n }\n\n /** 尝试上报;返回是否成功(4xx 视为最终失败,不再重试) */\n private async tryUpload(batch: EventBatch): Promise<boolean> {\n try {\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const res = await this.fetchImpl(`${this.endpoint}/api/v1/ingest`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-app-id': this.appId,\n 'x-app-secret': this.appSecret,\n },\n body: JSON.stringify({ appId: this.appId, ...batch }),\n signal: ac.signal,\n });\n if (res.ok) return true;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n // 鉴权/参数错误:重试无意义,丢弃\n console.warn(\n `[aipack/observability] 上报被拒绝(${res.status}),丢弃 ${count(batch)} 条。检查 appId/appSecret 与 endpoint。`,\n );\n return true; // 视为已处理(丢弃),不再缓存\n }\n // 5xx / 429:可重试\n console.warn(`[aipack/observability] 上报失败(${res.status}),将缓存补报。`);\n return false;\n } finally {\n clearTimeout(timer);\n }\n } catch (err) {\n // 网络错误 / 超时:可重试\n console.warn('[aipack/observability] 上报网络错误,将缓存补报:', (err as Error).message);\n return false;\n }\n }\n\n // ─── 本地缓存 ──────────────────────────────────────────────────\n\n private async readCache(): Promise<EventBatch> {\n try {\n const raw = await fs.readFile(this.cachePath, 'utf8');\n const parsed = JSON.parse(raw) as EventBatch;\n return {\n runs: Array.isArray(parsed.runs) ? parsed.runs : [],\n spans: Array.isArray(parsed.spans) ? parsed.spans : [],\n toolCalls: Array.isArray(parsed.toolCalls) ? parsed.toolCalls : [],\n permissions: Array.isArray(parsed.permissions) ? parsed.permissions : [],\n retries: Array.isArray(parsed.retries) ? parsed.retries : [],\n events: Array.isArray(parsed.events) ? parsed.events : [],\n };\n } catch {\n return EMPTY_BATCH();\n }\n }\n\n private async writeCache(batch: EventBatch): Promise<void> {\n try {\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n await fs.writeFile(this.cachePath, JSON.stringify(trimBatch(batch, this.maxCacheSize)), 'utf8');\n } catch (err) {\n console.warn('[aipack/observability] 缓存写入失败(数据丢弃):', (err as Error).message);\n }\n }\n\n private async removeCache(): Promise<void> {\n try {\n await fs.unlink(this.cachePath);\n } catch {\n // 无缓存文件属正常\n }\n }\n}\n\n// ─── 工具 ─────────────────────────────────────────────────────────\n\nfunction isEmpty(batch: EventBatch): boolean {\n return (\n !batch.runs.length &&\n !batch.spans.length &&\n !batch.toolCalls.length &&\n !batch.permissions.length &&\n !batch.retries.length &&\n !batch.events.length\n );\n}\n\nfunction count(batch: EventBatch): number {\n return (\n batch.runs.length +\n batch.spans.length +\n batch.toolCalls.length +\n batch.permissions.length +\n batch.retries.length +\n batch.events.length\n );\n}\n\nfunction mergeBatches(...batches: EventBatch[]): EventBatch {\n const out = EMPTY_BATCH();\n for (const b of batches) {\n out.runs.push(...b.runs);\n out.spans.push(...b.spans);\n out.toolCalls.push(...b.toolCalls);\n out.permissions.push(...b.permissions);\n out.retries.push(...b.retries);\n out.events.push(...b.events);\n }\n return out;\n}\n\n/** 超过上限时按 runs → spans → toolCalls → permissions → retries → events 顺序从头部裁剪(保留最新) */\nfunction trimBatch(batch: EventBatch, max: number): EventBatch {\n const total = count(batch);\n if (total <= max) return batch;\n let excess = total - max;\n const out: EventBatch = {\n ...batch,\n runs: [...batch.runs],\n spans: [...batch.spans],\n toolCalls: [...batch.toolCalls],\n permissions: [...batch.permissions],\n retries: [...batch.retries],\n events: [...batch.events],\n };\n for (const key of ['runs', 'spans', 'toolCalls', 'permissions', 'retries', 'events'] as const) {\n if (excess <= 0) break;\n const removed = Math.min(out[key].length, excess);\n out[key].splice(0, removed);\n excess -= removed;\n }\n return out;\n}\n","/**\n * Telemetry 接口实现(客户端 SDK):事件 → 原始记录 → 上报队列 → HttpReporter。\n *\n * 埋点模式:不在本地聚合/落盘,事件同步转成 RunRecord / SpanRecord / ToolCallRecord /\n * PermissionRecord 后入队,由定时器批量 POST 到收集服务。上报失败由 reporter 缓存补报,\n * 事件路径零阻塞、失败不阻断 run()。\n *\n * 载荷无 startedAt 字段:用「到达时刻 - durationMs」推导;run 级用 onRunStart.queuedAt。\n *\n * 注意:runtime 的 emitTelemetry 会解构方法后调用(fn(info)),故所有回调必须为\n * 箭头函数属性以绑定 this,否则 this.queued / this.startedAt 为 undefined。\n */\n\nimport type {\n Telemetry,\n RunStartTelemetryInfo,\n RunTelemetryInfo,\n ToolTelemetryInfo,\n ModelTelemetryInfo,\n RetryTelemetryInfo,\n PermissionDeniedTelemetryInfo,\n} from '@aipack-ai/agent';\nimport type { RunRecord, SpanRecord, ToolCallRecord, PermissionRecord, EventBatch } from './types';\n\nexport interface FlushQueueOptions {\n /** 上报周期(ms),默认 5000 */\n intervalMs?: number;\n /** 积攒条数触发(含 runs/spans/toolCalls/permissions 总数),默认 50 */\n batchSize?: number;\n /** P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events 全量。缺省 1(全量) */\n sampleRate?: number;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报) */\n redact?: (batch: EventBatch) => EventBatch;\n /** 发布版本:注入每条 run 记录(RunRecord.appVersion),供收集端按版本聚合。缺省不携带 */\n appVersion?: string;\n}\n\nconst emptyBatch = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class ObservabilityTelemetry implements Telemetry {\n private queued: EventBatch = emptyBatch();\n private timer: NodeJS.Timeout | undefined;\n private startedAt = new Map<string, number>(); // traceId -> queuedAt\n /** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */\n private inFlightOrder: string[] = [];\n private batchSize: number;\n private sampleRate: number;\n private redact?: (batch: EventBatch) => EventBatch;\n private appVersion?: string;\n\n constructor(private reporter: { send(batch: EventBatch): Promise<boolean> }, opts: FlushQueueOptions = {}) {\n this.batchSize = opts.batchSize ?? 50;\n this.sampleRate = opts.sampleRate ?? 1;\n this.redact = opts.redact;\n this.appVersion = opts.appVersion;\n const intervalMs = opts.intervalMs ?? 5000;\n this.timer = setInterval(() => void this.flush(), intervalMs);\n this.timer.unref?.();\n }\n\n onRunStart = (info: RunStartTelemetryInfo): void => {\n this.startedAt.set(info.traceId, info.queuedAt);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n this.inFlightOrder.push(info.traceId);\n };\n\n onRunEnd = (info: RunTelemetryInfo): void => {\n const queuedAt = this.startedAt.get(info.traceId) ?? Date.now() - info.durationMs;\n this.startedAt.delete(info.traceId);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n\n this.queued.runs.push(this.runRecord(info, queuedAt));\n this.queued.spans.push(this.runSpan(info, queuedAt));\n this.maybeFlush();\n };\n\n onToolCall = (info: ToolTelemetryInfo): void => {\n if (!this.sample()) return; // P2-1 明细采样\n const startedAt = Date.now() - info.durationMs;\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'tool',\n name: `tool:${info.toolName}`,\n startedAt,\n durationMs: info.durationMs,\n status: info.status === 'ok' ? 'ok' : 'error',\n errorClass: info.errorClass,\n sessionKey: info.sessionKey,\n });\n this.queued.toolCalls.push({\n traceId: info.traceId,\n spanId: info.spanId,\n toolName: info.toolName,\n status: info.status,\n durationMs: info.durationMs,\n errorClass: info.errorClass,\n });\n this.maybeFlush();\n };\n\n onModelCall = (info: ModelTelemetryInfo): void => {\n if (!this.sample()) return; // P2-1 明细采样\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'model',\n name: `model:${info.modelId}`,\n startedAt: Date.now() - info.durationMs,\n durationMs: info.durationMs,\n status: info.errorClass ? 'error' : 'ok',\n errorClass: info.errorClass,\n attempts: info.attempts,\n inputTokens: info.inputTokens,\n outputTokens: info.outputTokens,\n cacheRead: info.cacheRead,\n cacheWrite: info.cacheWrite,\n sessionKey: info.sessionKey,\n });\n this.maybeFlush();\n };\n\n onRetry = (info: RetryTelemetryInfo): void => {\n // P2-2:per-attempt 重试明细(关联 model span),server 落 retry_attempts 表\n this.queued.retries.push({\n traceId: info.traceId,\n spanId: info.spanId,\n provider: info.provider,\n modelId: info.modelId,\n attempt: info.attempt,\n errorClass: info.errorClass,\n status: info.status,\n delayMs: info.delayMs,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n onPermissionDenied = (info: PermissionDeniedTelemetryInfo): void => {\n this.queued.permissions.push({\n traceId: info.traceId,\n sessionKey: info.sessionKey,\n toolName: info.toolName,\n reason: info.reason,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n /**\n * 当前 run 上下文(供结构化 logger 注入 traceId)。\n * 单并发时返回唯一 in-flight run;并发时返回最近开始的(约定语义,文档说明)。\n */\n currentContext(): { traceId?: string } {\n const traceId = this.inFlightOrder[this.inFlightOrder.length - 1];\n return traceId ? { traceId } : {};\n }\n\n /**\n * P2-1 自定义业务事件(通用埋点)。\n * run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。\n */\n emit(name: string, data?: unknown, opts: { traceId?: string; sessionKey?: string } = {}): void {\n this.queued.events.push({\n traceId: opts.traceId ?? this.currentContext().traceId,\n sessionKey: opts.sessionKey,\n name,\n data,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n }\n\n /** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */\n flush(): void {\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n void this.reporter.send(this.redact ? this.redact(batch) : batch);\n }\n\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n async close(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n await this.reporter.send(this.redact ? this.redact(batch) : batch);\n }\n\n /** P2-1 明细采样判定:rate>=1 恒过、<=0 恒弃、否则按概率 */\n private sample(): boolean {\n if (this.sampleRate >= 1) return true;\n if (this.sampleRate <= 0) return false;\n return Math.random() < this.sampleRate;\n }\n\n private runRecord(info: RunTelemetryInfo, queuedAt: number): RunRecord {\n return {\n traceId: info.traceId,\n startedAt: queuedAt,\n endedAt: queuedAt + info.durationMs,\n sessionKey: info.sessionKey,\n channel: info.request.channel,\n model: info.request.model,\n status: info.success\n ? 'success'\n : info.errorClass === 'validation'\n ? 'validation'\n : 'error',\n errorClass: info.errorClass,\n turns: info.turnCount,\n durationMs: info.durationMs,\n activeMs: info.activeMs,\n queuedMs: info.queuedMs,\n ttftMs: info.ttftMs,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n cacheRead: info.tokens.cacheRead,\n cacheWrite: info.tokens.cacheWrite,\n ...(this.appVersion !== undefined ? { appVersion: this.appVersion } : {}),\n };\n }\n\n private runSpan(info: RunTelemetryInfo, queuedAt: number): SpanRecord {\n return {\n traceId: info.traceId,\n spanId: info.traceId,\n kind: 'run',\n name: 'run',\n startedAt: queuedAt,\n durationMs: info.durationMs,\n status: info.success ? 'ok' : 'error',\n errorClass: info.errorClass,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n sessionKey: info.sessionKey,\n };\n }\n\n private isEmpty(): boolean {\n return (\n !this.queued.runs.length &&\n !this.queued.spans.length &&\n !this.queued.toolCalls.length &&\n !this.queued.permissions.length &&\n !this.queued.retries.length &&\n !this.queued.events.length\n );\n }\n\n private maybeFlush(): void {\n const total =\n this.queued.runs.length +\n this.queued.spans.length +\n this.queued.toolCalls.length +\n this.queued.permissions.length +\n this.queued.retries.length +\n this.queued.events.length;\n if (total >= this.batchSize) this.flush();\n }\n}\n","/**\n * 结构化 logger(P1-1 日志关联)。\n *\n * 约定:logfmt 或 JSON 输出,可通过 context() 动态注入当前上下文(如 traceId),\n * 使应用日志与 Trace 关联(排障时按 traceId 检索日志流)。\n *\n * const logger = createLogger({ format: 'logfmt', context: () => telemetry.currentContext() });\n * logger.info('tool called', { tool: 'search_flights', durationMs: 120 });\n * // => time=2026-08-11T10:00:00.000Z level=info msg=\"tool called\" tool=search_flights durationMs=120\n *\n * 与收集服务 webhook/告警日志不同:本 logger 只负责\"应用日志\"输出约定,\n * 不参与上报(上报走 telemetry)。\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type LogFormat = 'logfmt' | 'json';\n\nexport interface LoggerOptions {\n /** 输出格式,默认 logfmt */\n format?: LogFormat;\n /** 最低输出级别,默认 info */\n level?: LogLevel;\n /** 每行日志的固定字段(如 service/app),会被动态 context / 单行 fields 覆盖 */\n tags?: Record<string, unknown>;\n /** 动态上下文(每行调用),例如 () => telemetry.currentContext() 注入 traceId */\n context?: () => Record<string, unknown> | undefined;\n /** 输出目标,默认 console(line 已含换行) */\n dest?: (line: string) => void;\n /** 是否注入 ISO 时间戳字段 time,默认 true */\n timestamps?: boolean;\n}\n\nexport interface Logger {\n debug(msg: string, fields?: Record<string, unknown>): void;\n info(msg: string, fields?: Record<string, unknown>): void;\n warn(msg: string, fields?: Record<string, unknown>): void;\n error(msg: string, fields?: Record<string, unknown>): void;\n /** 派生子 logger:合并部分配置(如固定业务标签) */\n child(overrides: Partial<LoggerOptions>): Logger;\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };\n\n/** 隐藏敏感字段名(值以 *** 代替),防止 token/key 明文进日志 */\nconst REDACTED_KEYS = /(secret|token|password|key|authorization|apikey)/i;\n\nexport function createLogger(opts: LoggerOptions = {}): Logger {\n const { format = 'logfmt', dest = defaultDest } = opts;\n const minLevel = LEVEL_ORDER[opts.level ?? 'info'];\n const timestamps = opts.timestamps ?? true;\n\n const emit = (level: LogLevel, msg: string, fields?: Record<string, unknown>): void => {\n if (LEVEL_ORDER[level] < minLevel) return;\n const context = opts.context?.() ?? {};\n dest(formatLine({ format, level, msg, fields, context, tags: opts.tags, timestamps }));\n };\n\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (overrides) =>\n createLogger({\n format: overrides.format ?? opts.format,\n level: overrides.level ?? opts.level,\n tags: { ...opts.tags, ...overrides.tags },\n context: overrides.context ?? opts.context,\n dest: overrides.dest ?? opts.dest,\n timestamps: overrides.timestamps ?? opts.timestamps,\n }),\n };\n}\n\ninterface FormatInput {\n format: LogFormat;\n level: LogLevel;\n msg: string;\n fields?: Record<string, unknown>;\n context?: Record<string, unknown>;\n tags?: Record<string, unknown>;\n timestamps: boolean;\n}\n\nfunction formatLine(input: FormatInput): string {\n const data: Record<string, unknown> = {};\n if (input.timestamps) data.time = new Date().toISOString();\n data.level = input.level;\n data.msg = input.msg;\n // 顺序:tags → context → fields(后者覆盖前者)\n for (const src of [input.tags, input.context, input.fields]) {\n if (!src) continue;\n for (const [k, v] of Object.entries(src)) {\n data[k] = REDACTED_KEYS.test(k) ? '***' : v;\n }\n }\n return input.format === 'json' ? JSON.stringify(data) : toLogfmt(data);\n}\n\n/** logfmt 渲染:值含空白/引号/等号/反斜杠时按 RFC 引号包裹并转义 */\nfunction toLogfmt(data: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(data)) {\n if (v === undefined || v === null) continue;\n parts.push(`${k}=${logfmtValue(v)}`);\n }\n return parts.join(' ');\n}\n\nfunction logfmtValue(v: unknown): string {\n if (typeof v === 'string') return quoteLogfmt(v);\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n if (typeof v === 'bigint') return v.toString();\n // 对象/数组 → JSON 序列化后引号包裹\n return quoteLogfmt(JSON.stringify(v));\n}\n\nfunction quoteLogfmt(s: string): string {\n if (!/[ \\t\"=\\\\]/u.test(s)) return s;\n return '\"' + s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') + '\"';\n}\n\nfunction defaultDest(line: string): void {\n // 走 console.log 而非 process.stdout.write,便于宿主拦截/重定向\n console.log(line);\n}\n","/**\n * OTLP/HTTP JSON trace exporter(P1-2 标准协议,档位 B 起步)。\n *\n * 无外部依赖:直接把 EventBatch 映射为 OTLP JSON ExportTraceServiceRequest\n * (protobuf JSON 编码),POST 到 OpenTelemetry Collector 的 /v1/traces。\n *\n * - traceId/spanId:OTLP 要求 16/8 字节二进制(base64),本 SDK 的 traceId 是任意\n * 字符串,故用 md5 做确定性映射(同一字符串恒得同一字节序列);\n * - 导出为 best-effort:失败仅 warn,不影响主上报链路(主链路仍走 HttpReporter);\n * - 未配置 otlp 时零开销(不构造 exporter)。\n */\n\nimport { createHash } from 'node:crypto';\nimport type { EventBatch, RunRecord, SpanRecord } from './types';\n\nexport interface OtlpExporterOptions {\n /** OTLP/HTTP JSON 端点,如 http://otel-collector:4318(POST {endpoint}/v1/traces) */\n endpoint: string;\n /** resource.service.name,默认 = appId */\n serviceName?: string;\n /** 应用标识(写入 span attribute aipack.app,便于按应用过滤) */\n appId?: string;\n /** 额外请求头(如 x-otlp-token) */\n headers?: Record<string, string>;\n /** 超时(ms),默认 3000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nexport interface OtlpTraceExporter {\n /** 导出批次;失败内部消化,恒不抛错,返回是否成功 */\n export(batch: EventBatch): Promise<boolean>;\n}\n\n/** md5 确定性映射为 N 字节(traceId=16B / spanId=8B) */\nfunction bytesOf(input: string, n: number): Buffer {\n return createHash('md5').update(input).digest().subarray(0, n);\n}\n\nfunction str(value: unknown): { stringValue: string } {\n return { stringValue: value == null ? '' : String(value) };\n}\nfunction int64(value: number): { intValue: string } {\n return { intValue: String(Math.round(value || 0)) };\n}\n\nfunction attr(key: string, value: { stringValue?: string; intValue?: string }): object {\n return { key, value };\n}\n\n/** token 总消耗(input + output + cacheRead + cacheWrite);无 token 数据时返回 undefined */\nfunction tokenTotal(r: { inputTokens?: number; outputTokens?: number; cacheRead?: number; cacheWrite?: number }): number | undefined {\n if (r.inputTokens === undefined && r.outputTokens === undefined) return undefined;\n return (r.inputTokens ?? 0) + (r.outputTokens ?? 0) + (r.cacheRead ?? 0) + (r.cacheWrite ?? 0);\n}\n\n/** RunRecord → OTLP span(根 span,kind=INTERNAL) */\nfunction runToSpan(r: RunRecord, appId?: string): object {\n const startNs = BigInt(r.startedAt) * 1_000_000n;\n const endNs = BigInt(r.startedAt + r.durationMs) * 1_000_000n;\n const ok = r.status === 'success';\n const attrs: object[] = [\n attr('session.key', str(r.sessionKey)),\n attr('aipack.status', str(r.status)),\n attr('aipack.turns', int64(r.turns)),\n attr('aipack.duration_ms', int64(r.durationMs)),\n attr('tokens.input', int64(r.inputTokens)),\n attr('tokens.output', int64(r.outputTokens)),\n ];\n if (tokenTotal(r) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(r)!)));\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (r.model) attrs.push(attr('model', str(r.model)));\n if (r.errorClass) attrs.push(attr('aipack.error_class', str(r.errorClass)));\n return {\n traceId: bytesOf(r.traceId, 16).toString('base64'),\n spanId: bytesOf(`${r.traceId}:run`, 8).toString('base64'),\n name: 'agent.run',\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: r.errorClass || r.status },\n };\n}\n\n/** SpanRecord → OTLP span */\nfunction spanToSpan(s: SpanRecord, appId?: string): object {\n const startNs = BigInt(s.startedAt) * 1_000_000n;\n const endNs = BigInt(s.startedAt + s.durationMs) * 1_000_000n;\n const ok = s.status === 'ok';\n const attrs: object[] = [\n attr('span.kind', str(s.kind)),\n attr('session.key', str(s.sessionKey)),\n attr('aipack.status', str(s.status)),\n attr('aipack.duration_ms', int64(s.durationMs)),\n ];\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (s.inputTokens !== undefined) attrs.push(attr('tokens.input', int64(s.inputTokens)));\n if (s.outputTokens !== undefined) attrs.push(attr('tokens.output', int64(s.outputTokens)));\n if (tokenTotal(s) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(s)!)));\n if (s.attempts !== undefined) attrs.push(attr('aipack.attempts', int64(s.attempts)));\n if (s.errorClass) attrs.push(attr('aipack.error_class', str(s.errorClass)));\n return {\n traceId: bytesOf(s.traceId, 16).toString('base64'),\n spanId: bytesOf(s.spanId, 8).toString('base64'),\n name: s.name,\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: s.errorClass || s.status },\n };\n}\n\n/** EventBatch → OTLP ExportTraceServiceRequest(JSON 对象,可直接 JSON.stringify) */\nexport function toOtlpJsonTraces(batch: EventBatch, serviceName: string, appId?: string): object {\n const spans: object[] = [];\n for (const r of batch.runs) spans.push(runToSpan(r, appId));\n for (const s of batch.spans) spans.push(spanToSpan(s, appId));\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: str(serviceName) },\n { key: 'telemetry.sdk.name', value: str('aipack') },\n { key: 'telemetry.sdk.language', value: str('nodejs') },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'aipack', version: '0.1.0' },\n spans,\n },\n ],\n },\n ],\n };\n}\n\nexport function createOtlpTraceExporter(opts: OtlpExporterOptions): OtlpTraceExporter {\n const endpoint = opts.endpoint.replace(/\\/+$/, '');\n const serviceName = opts.serviceName ?? 'aipack-app';\n const timeoutMs = opts.timeoutMs ?? 3000;\n const headers = { 'content-type': 'application/json', ...opts.headers };\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n\n return {\n async export(batch) {\n const runs = batch.runs.length;\n const spans = batch.spans.length;\n if (!runs && !spans) return true; // 无 trace 数据(纯 tool/permission)→ 无操作\n try {\n const body = JSON.stringify(toOtlpJsonTraces(batch, serviceName, opts.appId));\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(`${endpoint}/v1/traces`, {\n method: 'POST',\n headers,\n body,\n signal: ac.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (res.ok) return true;\n console.warn(`[aipack/observability] OTLP 导出被拒绝(${res.status}),已跳过(不影响主上报)`);\n return false;\n } catch (err) {\n console.warn('[aipack/observability] OTLP 导出失败(不影响主上报):', (err as Error).message);\n return false;\n }\n },\n };\n}\n","/**\n * @aipack-ai/observability — aipack 可观测性上报 SDK。\n *\n * 埋点上报模式:客户端只需 appId + appSecret,6 类 Telemetry 事件自动批量\n * POST 到收集服务(默认 http://localhost:8787),失败本地缓存补报。\n *\n * const obs = createObservability({ appId: 'my-app', appSecret: 'xxx' });\n * createRuntime({ ..., telemetry: obs.telemetry });\n *\n * 收集服务见 @aipack-ai/observability-server(独立包,SQLite 落盘 + 内存聚合 + REST API)。\n */\n\nimport { HttpReporter } from './reporter';\nimport { ObservabilityTelemetry } from './telemetry';\nimport { createLogger, type Logger } from './logger';\nimport { createOtlpTraceExporter } from './otlp';\n\nexport interface CreateObservabilityOptions {\n /** 应用标识(必填),收集端用它校验身份并隔离数据 */\n appId: string;\n /** 应用密钥(必填),与收集端 OBS_APPS 白名单匹配 */\n appSecret: string;\n /** 收集服务地址,默认 http://localhost:8787 */\n endpoint?: string;\n /** 上报失败本地缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限,默认 2000 */\n maxCacheSize?: number;\n /** 上报周期(ms),默认 5000 */\n flushIntervalMs?: number;\n /** 积攒条数触发上报,默认 50 */\n flushBatchSize?: number;\n /** 可选:OTLP/HTTP JSON trace 导出(推 OpenTelemetry Collector),失败不影响主上报 */\n otlp?: {\n endpoint: string;\n serviceName?: string;\n headers?: Record<string, string>;\n };\n /** P2-1 明细采样率(0–1):只对 model/tool spans 与 toolCalls 采样,runs/permissions/events 全量。缺省 1 */\n sampleRate?: number;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报),示例见 docs/observability-roadmap.md §P2-1 */\n redact?: (batch: import('./types').EventBatch) => import('./types').EventBatch;\n /** 发布版本(如 '1.3.0'),随每条 run 上报,供收集端按版本聚合对比。缺省不携带 */\n version?: string;\n}\n\nexport interface Observability {\n /** 注入 RuntimeOptions.telemetry */\n telemetry: ObservabilityTelemetry;\n /** 结构化 logger:自动注入当前 run 的 traceId(P1-1 日志关联) */\n logger: Logger;\n /** P2-1 自定义业务事件:run 内自动带 traceId,入 Trace 时间轴 */\n emit(name: string, data?: unknown, opts?: { traceId?: string; sessionKey?: string }): void;\n /** 立即上报队列中的残留数据 */\n flush(): void;\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n close(): Promise<void>;\n}\n\nexport function createObservability(opts: CreateObservabilityOptions): Observability {\n const reporter = new HttpReporter({\n endpoint: opts.endpoint ?? 'http://localhost:8787',\n appId: opts.appId,\n appSecret: opts.appSecret,\n cacheDir: opts.cacheDir,\n maxCacheSize: opts.maxCacheSize,\n });\n\n // OTLP 导出作为旁路:await 但不影响主上报结果;exporter 内部恒不抛错\n const sink: { send(batch: import('./types').EventBatch): Promise<boolean> } = reporter;\n if (opts.otlp) {\n const exporter = createOtlpTraceExporter({\n endpoint: opts.otlp.endpoint,\n serviceName: opts.otlp.serviceName ?? opts.appId,\n appId: opts.appId,\n headers: opts.otlp.headers,\n });\n sink.send = async (batch) => {\n const ok = await reporter.send(batch);\n await exporter.export(batch);\n return ok;\n };\n }\n\n const telemetry = new ObservabilityTelemetry(sink, {\n intervalMs: opts.flushIntervalMs,\n batchSize: opts.flushBatchSize,\n sampleRate: opts.sampleRate,\n redact: opts.redact,\n appVersion: opts.version,\n });\n const logger = createLogger({\n tags: { app: opts.appId },\n context: () => telemetry.currentContext(),\n });\n\n return {\n telemetry,\n logger,\n emit: (name, data, emitOpts) => telemetry.emit(name, data, emitOpts),\n flush: () => telemetry.flush(),\n close: async () => {\n await telemetry.close();\n },\n };\n}\n\n// ─── 类型导出 ─────────────────────────────────────────────────────\n\nexport { ObservabilityTelemetry } from './telemetry';\nexport type { FlushQueueOptions } from './telemetry';\nexport { HttpReporter } from './reporter';\nexport type { ReporterOptions } from './reporter';\nexport { createLogger } from './logger';\nexport type { Logger, LoggerOptions, LogLevel, LogFormat } from './logger';\nexport { createOtlpTraceExporter, toOtlpJsonTraces } from './otlp';\nexport type { OtlpTraceExporter, OtlpExporterOptions } from './otlp';\nexport type {\n RunRecord,\n SpanRecord,\n ToolCallRecord,\n PermissionRecord,\n EventRecord,\n RetryRecord,\n EventBatch,\n} from './types';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/reporter.ts","../src/sampling.ts","../src/w3c-trace-context.ts","../src/redact/rules.ts","../src/telemetry.ts","../src/logger.ts","../src/otlp.ts","../src/index.ts"],"sourcesContent":["/**\n * HttpReporter — 把批量事件上报到收集服务(埋点模式)。\n *\n * - POST {endpoint}/api/v1/ingest,携带 x-app-id / x-app-secret 鉴权头\n * - 上报失败(网络错误 / 5xx / 429)→ 写入本地缓存文件({cacheDir}/{appId}.json),\n * 下次 send 前先补报缓存,全部成功才删除\n * - 4xx(鉴权失败、参数错误)判定为不可重试,直接丢弃并 warn,避免缓存无限重试\n * - 缓存条数上限 maxCacheSize,超出后丢弃最旧记录\n * - 并发保护:send 串行执行,重复调用合并为一次\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { EventBatch } from './types';\n\nexport interface ReporterOptions {\n /** 收集服务地址,如 http://localhost:8787(必填) */\n endpoint: string;\n appId: string;\n appSecret: string;\n /** 缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限(含 runs/spans/toolCalls/permissions),默认 2000 */\n maxCacheSize?: number;\n /** 上报超时(ms),默认 5000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nconst EMPTY_BATCH = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class HttpReporter {\n private endpoint: string;\n private appId: string;\n private appSecret: string;\n private cachePath: string;\n private maxCacheSize: number;\n private timeoutMs: number;\n private fetchImpl: typeof fetch;\n /** 串行发送锁:true 表示有发送在进行中 */\n private sending = false;\n /** 发送期间新到的批次,发送完成后继续补发 */\n private pending: EventBatch[] = [];\n\n constructor(opts: ReporterOptions) {\n this.endpoint = opts.endpoint.replace(/\\/+$/, '');\n this.appId = opts.appId;\n this.appSecret = opts.appSecret;\n this.cachePath = path.join(\n opts.cacheDir ?? path.join(process.cwd(), '.aipack', 'observability'),\n `${opts.appId}.json`,\n );\n this.maxCacheSize = opts.maxCacheSize ?? 2000;\n this.timeoutMs = opts.timeoutMs ?? 5000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n }\n\n /** 上报批次;失败自动落缓存,返回是否成功(测试断言用) */\n async send(batch: EventBatch): Promise<boolean> {\n this.pending.push(batch);\n if (this.sending) return false; // 已有一轮发送在跑,数据会被顺带带走\n this.sending = true;\n try {\n return await this.drain();\n } finally {\n this.sending = false;\n }\n }\n\n /** 排空所有批次(含缓存补报)。返回最后一轮是否成功 */\n private async drain(): Promise<boolean> {\n let lastOk = false;\n for (;;) {\n const cache = await this.readCache();\n const batch = mergeBatches(cache, ...this.pending);\n this.pending = [];\n if (isEmpty(batch)) return lastOk;\n\n lastOk = await this.tryUpload(batch);\n if (!lastOk) {\n // 失败 → 写回缓存,等待下次 flush\n await this.writeCache(batch);\n return false;\n }\n // 成功 → 清缓存\n await this.removeCache();\n }\n }\n\n /** 尝试上报;返回是否成功(4xx 视为最终失败,不再重试) */\n private async tryUpload(batch: EventBatch): Promise<boolean> {\n try {\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const res = await this.fetchImpl(`${this.endpoint}/api/v1/ingest`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-app-id': this.appId,\n 'x-app-secret': this.appSecret,\n },\n body: JSON.stringify({ appId: this.appId, ...batch }),\n signal: ac.signal,\n });\n if (res.ok) return true;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n // 鉴权/参数错误:重试无意义,丢弃\n console.warn(\n `[aipack/observability] 上报被拒绝(${res.status}),丢弃 ${count(batch)} 条。检查 appId/appSecret 与 endpoint。`,\n );\n return true; // 视为已处理(丢弃),不再缓存\n }\n // 5xx / 429:可重试\n console.warn(`[aipack/observability] 上报失败(${res.status}),将缓存补报。`);\n return false;\n } finally {\n clearTimeout(timer);\n }\n } catch (err) {\n // 网络错误 / 超时:可重试\n console.warn('[aipack/observability] 上报网络错误,将缓存补报:', (err as Error).message);\n return false;\n }\n }\n\n // ─── 本地缓存 ──────────────────────────────────────────────────\n\n private async readCache(): Promise<EventBatch> {\n try {\n const raw = await fs.readFile(this.cachePath, 'utf8');\n const parsed = JSON.parse(raw) as EventBatch;\n return {\n runs: Array.isArray(parsed.runs) ? parsed.runs : [],\n spans: Array.isArray(parsed.spans) ? parsed.spans : [],\n toolCalls: Array.isArray(parsed.toolCalls) ? parsed.toolCalls : [],\n permissions: Array.isArray(parsed.permissions) ? parsed.permissions : [],\n retries: Array.isArray(parsed.retries) ? parsed.retries : [],\n events: Array.isArray(parsed.events) ? parsed.events : [],\n };\n } catch {\n return EMPTY_BATCH();\n }\n }\n\n private async writeCache(batch: EventBatch): Promise<void> {\n try {\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n await fs.writeFile(this.cachePath, JSON.stringify(trimBatch(batch, this.maxCacheSize)), 'utf8');\n } catch (err) {\n console.warn('[aipack/observability] 缓存写入失败(数据丢弃):', (err as Error).message);\n }\n }\n\n private async removeCache(): Promise<void> {\n try {\n await fs.unlink(this.cachePath);\n } catch {\n // 无缓存文件属正常\n }\n }\n}\n\n// ─── 工具 ─────────────────────────────────────────────────────────\n\nfunction isEmpty(batch: EventBatch): boolean {\n return (\n !batch.runs.length &&\n !batch.spans.length &&\n !batch.toolCalls.length &&\n !batch.permissions.length &&\n !batch.retries.length &&\n !batch.events.length\n );\n}\n\nfunction count(batch: EventBatch): number {\n return (\n batch.runs.length +\n batch.spans.length +\n batch.toolCalls.length +\n batch.permissions.length +\n batch.retries.length +\n batch.events.length\n );\n}\n\nfunction mergeBatches(...batches: EventBatch[]): EventBatch {\n const out = EMPTY_BATCH();\n for (const b of batches) {\n out.runs.push(...b.runs);\n out.spans.push(...b.spans);\n out.toolCalls.push(...b.toolCalls);\n out.permissions.push(...b.permissions);\n out.retries.push(...b.retries);\n out.events.push(...b.events);\n }\n return out;\n}\n\n/** 超过上限时按 runs → spans → toolCalls → permissions → retries → events 顺序从头部裁剪(保留最新) */\nfunction trimBatch(batch: EventBatch, max: number): EventBatch {\n const total = count(batch);\n if (total <= max) return batch;\n let excess = total - max;\n const out: EventBatch = {\n ...batch,\n runs: [...batch.runs],\n spans: [...batch.spans],\n toolCalls: [...batch.toolCalls],\n permissions: [...batch.permissions],\n retries: [...batch.retries],\n events: [...batch.events],\n };\n for (const key of ['runs', 'spans', 'toolCalls', 'permissions', 'retries', 'events'] as const) {\n if (excess <= 0) break;\n const removed = Math.min(out[key].length, excess);\n out[key].splice(0, removed);\n excess -= removed;\n }\n return out;\n}\n","/**\n * Phase 9 — 采样策略模块。\n *\n * 三种采样策略:\n * - none: 不采样(全量,或兼容旧 sampleRate 行为)\n * - traceid-ratio: 一致性 head 采样。同 traceId 全量子采样(runs/spans/toolCalls 一致)\n * - error-priority: 错误 run 必采,成功 run 按 rate 采样\n * - slow-priority: 慢请求(> P95 阈值)必采,其余按 rate 采样\n */\n\nexport type SampleStrategy =\n | { type: 'none' }\n | { type: 'traceid-ratio'; rate: number }\n | { type: 'error-priority'; successRate: number }\n | { type: 'slow-priority'; rate: number; slowThresholdMs?: number }\n /** 【旧兼容】sampleRate 的语义:整 run 全量保留,仅过滤 model/tool spans 与 toolCalls(明细级) */\n | { type: 'legacy-detail-ratio'; rate: number };\n\nexport interface SampleStrategyOptions {\n /** 采样策略:'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none'(默认 none) */\n strategy?: 'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none';\n /** traceid-ratio / slow-priority 的采样率(0-1),默认 0.1 */\n rate?: number;\n /** error-priority 下成功 run 的采样率(0-1),默认 0.1 */\n successRate?: number;\n /** slow-priority 下慢请求阈值(ms),未配置时走 SDK 内部滑动 P95(若无法统计则默认 30000ms) */\n slowThresholdMs?: number;\n}\n\n/** 将用户配置解析为结构化 SampleStrategy */\nexport function resolveSampleStrategy(opts: SampleStrategyOptions): SampleStrategy {\n const type = opts.strategy ?? 'none';\n switch (type) {\n case 'traceid-ratio': {\n const rate = clamp01(opts.rate ?? 0.1);\n return { type: 'traceid-ratio', rate };\n }\n case 'error-priority': {\n const successRate = clamp01(opts.successRate ?? 0.1);\n return { type: 'error-priority', successRate };\n }\n case 'slow-priority': {\n const rate = clamp01(opts.rate ?? 0.1);\n return { type: 'slow-priority', rate, slowThresholdMs: opts.slowThresholdMs };\n }\n case 'none':\n default:\n return { type: 'none' };\n }\n}\n\n/** 旧 sampleRate(0–1 简单概率)转换为等价策略:\n * 【重要】语义保留:整 run(+ run span)全量保留,仅 model/tool spans 与 toolCalls 按 rate 过滤。\n */\nexport function legacySampleRateToStrategy(rate: number | undefined): SampleStrategy {\n if (rate === undefined || rate >= 1) return { type: 'none' };\n const r = clamp01(rate);\n return { type: 'legacy-detail-ratio', rate: r };\n}\n\n// ─── 采样判定器(SDK 单例,内部维护状态)────────────────────────\n\nexport interface SamplingJudge {\n /** run 是否采样:只对整 run 的\"是否保留\"决策;spans/toolCalls 随 run */\n shouldKeepRun(info: {\n traceId: string;\n status: 'success' | 'error' | 'validation';\n durationMs: number;\n }): boolean;\n /** 明细(span/toolCall)采样:当 run 采样通过时才进一步过滤;未通过直接 false */\n shouldKeepDetail(traceId: string): boolean;\n}\n\nexport function createSamplingJudge(strategy: SampleStrategy): SamplingJudge {\n // ─── traceid-ratio:一致性哈希,同 trace 结果稳定 ────────────────\n if (strategy.type === 'traceid-ratio') {\n const rate = strategy.rate;\n if (rate <= 0) {\n return {\n shouldKeepRun: () => false,\n shouldKeepDetail: () => false,\n };\n }\n if (rate >= 1) {\n return {\n shouldKeepRun: () => true,\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId }) => traceIdConsistentHash(traceId) < rate,\n shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate,\n };\n }\n\n // ─── error-priority:错误必采,成功按 successRate 概率采样 ───────\n if (strategy.type === 'error-priority') {\n const { successRate } = strategy;\n if (successRate <= 0) {\n // 仅保留错误\n return {\n shouldKeepRun: ({ status }) => status === 'error',\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n if (successRate >= 1) {\n return {\n shouldKeepRun: () => true,\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId, status }) => {\n const keep = status === 'error' ? true : Math.random() < successRate;\n if (keep) sampledRunSet.add(traceId);\n else sampledRunSet.delete(traceId);\n return keep;\n },\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n\n // ─── slow-priority:慢请求必采,其余按 rate 采样 ───────────────\n if (strategy.type === 'slow-priority') {\n const { rate, slowThresholdMs } = strategy;\n // 慢阈值:未配置时滑动 P95 近似(前 1000 条 duration 升序数组 + p95)\n const window = new SlidingP95();\n const thresholdFallback = slowThresholdMs ?? 30_000;\n if (rate <= 0) {\n return {\n shouldKeepRun: ({ traceId, durationMs }) => {\n const p95 = window.get() ?? thresholdFallback;\n window.push(durationMs);\n const keep = durationMs >= p95;\n if (keep) sampledRunSet.add(traceId);\n else sampledRunSet.delete(traceId);\n return keep;\n },\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n if (rate >= 1) {\n return {\n shouldKeepRun: () => true,\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId, durationMs }) => {\n const p95 = window.get() ?? thresholdFallback;\n window.push(durationMs);\n const keep = durationMs >= p95 ? true : Math.random() < rate;\n if (keep) sampledRunSet.add(traceId);\n else sampledRunSet.delete(traceId);\n return keep;\n },\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n\n // ─── legacy-detail-ratio:旧 sampleRate 语义(run 全量,仅明细过滤)─────\n if (strategy.type === 'legacy-detail-ratio') {\n const rate = strategy.rate;\n if (rate <= 0) {\n // rate=0:run 保留,所有明细丢弃\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: () => false,\n };\n }\n if (rate >= 1) {\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate,\n };\n }\n\n // ─── none:全量 ───────────────────────────────────────────────\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: () => true,\n };\n}\n\n// ─── 内部工具 ────────────────────────────────────────────────────\n\n/** 已采样通过的 traceId 集合:用于 shouldKeepDetail 联动判定 */\nconst sampledRunSet = new Set<string>();\n\nfunction clamp01(v: number): number {\n if (!Number.isFinite(v)) return 0;\n if (v < 0) return 0;\n if (v > 1) return 1;\n return v;\n}\n\n/** traceId → [0,1) 一致性哈希(简单但足够的 MD5 截断替代:FNV-1a 32bit / 2^32) */\nfunction traceIdConsistentHash(traceId: string): number {\n let h = 0x811c9dc5; // FNV offset basis\n for (let i = 0; i < traceId.length; i++) {\n h ^= traceId.charCodeAt(i);\n h = Math.imul(h, 0x01000193); // FNV prime\n }\n return (h >>> 0) / 0x100000000;\n}\n\n/** 滑动 P95 近似:维护最近 N 条 duration,取排序后第 95% 位置 */\nclass SlidingP95 {\n private buf: number[] = [];\n private static readonly CAP = 1000;\n\n push(d: number): void {\n this.buf.push(d);\n if (this.buf.length > SlidingP95.CAP) this.buf.shift();\n }\n\n /** 返回 p95,样本不足返回 undefined(调用方 fallback 到默认阈值) */\n get(): number | undefined {\n if (this.buf.length < 30) return undefined;\n const sorted = [...this.buf].sort((a, b) => a - b);\n const idx = Math.floor(sorted.length * 0.95);\n return sorted[Math.min(idx, sorted.length - 1)];\n }\n}\n","/**\n * Phase 9 — W3C Trace Context 支持。\n *\n * 标准:https://www.w3.org/TR/trace-context/\n *\n * traceparent 格式:\n * version-traceid-parentid-traceflags\n * 例:00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01\n *\n * - version: 2 hex (00)\n * - trace-id: 32 hex(16 bytes,必须非全 0)\n * - parent-id: 16 hex(8 bytes,必须非全 0)\n * - trace-flags: 2 hex(bit 0 = sampled,其余保留为 0)\n */\n\nexport interface W3cTraceContext {\n /** 标准 W3C trace-id(32 hex 小写) */\n traceId: string;\n /** 标准 W3C parent-id / span-id(16 hex 小写) */\n parentId: string;\n /** 采样标记(trace-flags 最低位) */\n sampled: boolean;\n /** 原始 traceparent 字符串(用于回传) */\n raw: string;\n}\n\nexport type ParseResult =\n | { ok: true; ctx: W3cTraceContext }\n | { ok: false; reason: string };\n\n/** 解析 traceparent 头。版本 ff 或格式不合法时返回 { ok:false },不抛错。 */\nexport function parseTraceparent(raw: string | null | undefined): ParseResult {\n if (!raw) return { ok: false, reason: 'missing' };\n // 允许前后空白 + 大小写不敏感\n const s = raw.trim().toLowerCase();\n const parts = s.split('-');\n if (parts.length !== 4) return { ok: false, reason: 'format: parts != 4' };\n const [versionHex, traceIdHex, parentIdHex, flagsHex] = parts;\n if (!/^[0-9a-f]{2}$/.test(versionHex)) return { ok: false, reason: 'invalid version' };\n if (versionHex === 'ff') return { ok: false, reason: 'version ff is invalid' };\n if (!/^[0-9a-f]{32}$/.test(traceIdHex)) return { ok: false, reason: 'invalid trace-id' };\n if (/^0{32}$/.test(traceIdHex)) return { ok: false, reason: 'trace-id all zeros' };\n if (!/^[0-9a-f]{16}$/.test(parentIdHex)) return { ok: false, reason: 'invalid parent-id' };\n if (/^0{16}$/.test(parentIdHex)) return { ok: false, reason: 'parent-id all zeros' };\n if (!/^[0-9a-f]{2}$/.test(flagsHex)) return { ok: false, reason: 'invalid trace-flags' };\n const flags = parseInt(flagsHex, 16);\n return {\n ok: true,\n ctx: {\n traceId: traceIdHex,\n parentId: parentIdHex,\n sampled: (flags & 0x01) === 0x01,\n raw: s,\n },\n };\n}\n\n/** 组装 traceparent 头字符串 */\nexport function formatTraceparent(ctx: { traceId: string; parentId: string; sampled: boolean }): string {\n const flags = ctx.sampled ? '01' : '00';\n return `00-${ctx.traceId}-${ctx.parentId}-${flags}`;\n}\n\n/**\n * 把外部 W3C trace-id 映射到 aipack traceId(双向绑定)。\n *\n * 策略:\n * - 外部 w3cTraceId 与 aipack traceId 不同(aipack traceId 为随机字符串)。\n * 在 RunRecord 上同时记 w3cTraceId(外部传入)与 aipack 自身 traceId,\n * 面板通过 w3cTraceId 建立跨系统跳转。\n */\n\n/** 生成一个新的 W3C trace-id(32 hex,非全 0) */\nexport function generateW3cTraceId(): string {\n const bytes = cryptoRandomBytes(16);\n if (bytes.every((b) => b === 0)) bytes[0] = 1; // 避免全 0\n return toHex(bytes);\n}\n\n/** 生成一个新的 W3C parent-id / span-id(16 hex,非全 0) */\nexport function generateW3cParentId(): string {\n const bytes = cryptoRandomBytes(8);\n if (bytes.every((b) => b === 0)) bytes[0] = 1;\n return toHex(bytes);\n}\n\n// ─── 工具 ────────────────────────────────────────────────────────\n\n/** 最小化 crypto 接口(避免引用 dom lib 的 Crypto 类型) */\ninterface WebCryptoLike {\n getRandomValues<T extends ArrayBufferView>(array: T): T;\n}\n\nfunction cryptoRandomBytes(n: number): Uint8Array {\n // Node 18+ 全局有 crypto;回退到 Math.random 保证兼容测试/浏览器\n const cryptoObj = (globalThis as unknown as { crypto?: WebCryptoLike }).crypto;\n if (typeof globalThis !== 'undefined' && cryptoObj?.getRandomValues) {\n const buf = new Uint8Array(n);\n cryptoObj.getRandomValues(buf);\n return buf;\n }\n const out = new Uint8Array(n);\n for (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256);\n return out;\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let s = '';\n for (const b of bytes) s += b.toString(16).padStart(2, '0');\n return s;\n}\n","/**\n * Phase 9 — PII 内置脱敏规则库。\n *\n * 默认启用:手机号 / 邮箱 / 身份证号 / 银行卡号 / IPv4 地址。\n * 脱敏策略:\n * - mask:保留前后若干位,中间用 * 替换(默认策略)\n * - hash:SHA-256 截断 16 hex(不可逆,只用于等值对比)\n * - drop:完全删除该字段(极端敏感场景)\n *\n * 本文件仅提供「规则」与「脱敏函数」,不做字段级策略配置。\n * 字段级策略(项目级)由服务端 redact_rules 表管理,SDK 侧通过 API 拉取或默认启用内置。\n */\n\nimport { createHash } from 'node:crypto';\n\nexport type RedactAction = 'mask' | 'hash' | 'drop';\n\nexport interface RedactRule {\n /** 规则名,用于面板配置匹配 */\n name: string;\n /** 匹配正则(global,允许多次命中) */\n pattern: RegExp;\n /** 默认脱敏动作 */\n defaultAction: RedactAction;\n /** 掩码函数(仅 action=mask 时调用),默认保留前后 N 位 */\n maskFn?: (match: string) => string;\n}\n\n/** 中国大陆手机号:11 位,1 开头,第二位 3-9 */\nconst PHONE_RE = /(?<!\\d)1[3-9]\\d{9}(?!\\d)/g;\n/** 邮箱:简单且够用的正则(user@domain.tld) */\nconst EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\n/** 18 位身份证号(17 位数字 + 校验位 X/x/数字),不做校验位数学校验 */\nconst ID_CARD_RE = /(?<!\\d)\\d{17}[\\dXx](?!\\d)/g;\n/** 16-19 位银行卡号(Luhn 校验此处略过,仅按形态) */\nconst BANK_CARD_RE = /(?<!\\d)\\d{16,19}(?!\\d)/g;\n/** IPv4 地址(宽松匹配;不做 0-255 范围校验) */\nconst IPV4_RE = /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g;\n\nexport const BUILTIN_RULES: RedactRule[] = [\n {\n name: 'phone',\n pattern: PHONE_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.slice(0, 3) + '****' + m.slice(-4), // 139****1234\n },\n {\n name: 'email',\n pattern: EMAIL_RE,\n defaultAction: 'mask',\n maskFn: (m) => {\n const at = m.indexOf('@');\n if (at <= 1) return '*'.repeat(at) + m.slice(at);\n const user = m.slice(0, at);\n return user[0] + '*'.repeat(Math.max(at - 2, 1)) + user[at - 1] + m.slice(at);\n },\n },\n {\n name: 'idCard',\n pattern: ID_CARD_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.slice(0, 6) + '********' + m.slice(-4), // 6 + 8* + 4 = 18\n },\n {\n name: 'bankCard',\n pattern: BANK_CARD_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.slice(0, 4) + ' **** **** ' + m.slice(-4), // 6222 **** **** 1234\n },\n {\n name: 'ipv4',\n pattern: IPV4_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.replace(/\\.\\d+\\.\\d+$/, '.*.*'), // 192.168.*.*\n },\n];\n\n/** 对单个字符串做所有内置规则的脱敏(默认 mask 动作) */\nexport function redactString(raw: string, actionOverrides?: Record<string, RedactAction>): string {\n if (!raw) return raw;\n let out = raw;\n for (const rule of BUILTIN_RULES) {\n const action = actionOverrides?.[rule.name] ?? rule.defaultAction;\n if (action === 'drop') {\n out = out.replace(rule.pattern, '');\n } else if (action === 'hash') {\n out = out.replace(rule.pattern, (m) => sha256Trunc(m));\n } else {\n // mask\n const fn = rule.maskFn ?? defaultMask;\n out = out.replace(rule.pattern, fn);\n }\n // reset regex lastIndex(global regex 会维护状态)\n rule.pattern.lastIndex = 0;\n }\n return out;\n}\n\n/** 对任意 JSON 值(对象/数组/字符串)做深度脱敏 */\nexport function redactValue<T = unknown>(value: T, actionOverrides?: Record<string, RedactAction>): T {\n if (value === null || value === undefined) return value;\n if (typeof value === 'string') return redactString(value, actionOverrides) as unknown as T;\n if (typeof value !== 'object') return value;\n if (Array.isArray(value)) {\n return value.map((v) => redactValue(v, actionOverrides)) as unknown as T;\n }\n // plain object\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = redactValue(v, actionOverrides);\n }\n return out as unknown as T;\n}\n\nfunction defaultMask(m: string): string {\n if (m.length <= 2) return '*'.repeat(m.length);\n const keepHead = Math.max(1, Math.floor(m.length * 0.25));\n const keepTail = Math.max(1, Math.floor(m.length * 0.25));\n return m.slice(0, keepHead) + '*'.repeat(m.length - keepHead - keepTail) + m.slice(-keepTail);\n}\n\nfunction sha256Trunc(s: string): string {\n return createHash('sha256').update(s).digest('hex').slice(0, 16);\n}\n","/**\n * Telemetry 接口实现(客户端 SDK):事件 → 原始记录 → 上报队列 → HttpReporter。\n *\n * 埋点模式:不在本地聚合/落盘,事件同步转成 RunRecord / SpanRecord / ToolCallRecord /\n * PermissionRecord 后入队,由定时器批量 POST 到收集服务。上报失败由 reporter 缓存补报,\n * 事件路径零阻塞、失败不阻断 run()。\n *\n * 载荷无 startedAt 字段:用「到达时刻 - durationMs」推导;run 级用 onRunStart.queuedAt。\n *\n * 注意:runtime 的 emitTelemetry 会解构方法后调用(fn(info)),故所有回调必须为\n * 箭头函数属性以绑定 this,否则 this.queued / this.startedAt 为 undefined。\n *\n * Phase 9 新增:\n * - 采样策略:error-priority / slow-priority / traceid-ratio(整 run 维度 + 明细联动)\n * - W3C Trace Context:traceparent 头解析 → parentTraceId/w3cTraceId 写入 RunRecord\n * - PII 脱敏:内置规则库深度遍历 events.data 做脱敏(默认启用,可关闭)\n */\n\nimport type {\n Telemetry,\n RunStartTelemetryInfo,\n RunTelemetryInfo,\n ToolTelemetryInfo,\n ModelTelemetryInfo,\n RetryTelemetryInfo,\n PermissionDeniedTelemetryInfo,\n} from '@aipack-ai/agent';\nimport type { RunRecord, SpanRecord, ToolCallRecord, PermissionRecord, EventBatch } from './types';\nimport {\n createSamplingJudge,\n legacySampleRateToStrategy,\n resolveSampleStrategy,\n type SampleStrategy,\n type SampleStrategyOptions,\n type SamplingJudge,\n} from './sampling';\nimport {\n parseTraceparent,\n generateW3cTraceId,\n generateW3cParentId,\n formatTraceparent,\n type W3cTraceContext,\n} from './w3c-trace-context';\nimport { redactValue, type RedactAction } from './redact/rules';\n\nexport interface FlushQueueOptions {\n /** 上报周期(ms),默认 5000 */\n intervalMs?: number;\n /** 积攒条数触发(含 runs/spans/toolCalls/permissions 总数),默认 50 */\n batchSize?: number;\n /** 【旧兼容】P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events 全量。\n * 新版 sampleStrategy 优先,sampleRate 仅在未配置 sampleStrategy 时生效(等价 traceid-ratio)。 */\n sampleRate?: number;\n /** Phase 9 — 采样策略(error-priority / slow-priority / traceid-ratio)。优先级高于 sampleRate。 */\n sampleStrategy?: SampleStrategyOptions;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报)。\n * 当内置脱敏启用时,自定义钩子在内置之后执行(用户可进一步定制)。 */\n redact?: (batch: EventBatch) => EventBatch;\n /** Phase 9 — 是否启用内置 PII 脱敏规则(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */\n redactEnabled?: boolean;\n /** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop' }。未配置规则走默认 mask。 */\n redactOverrides?: Record<string, RedactAction>;\n /** 发布版本:注入每条 run 记录(RunRecord.appVersion),供收集端按版本聚合。缺省不携带 */\n appVersion?: string;\n /**\n * Phase 9 — 外部 traceparent 上下文(来自 HTTP 请求头 / 消息队列属性)。\n * 传入字符串时自动解析;解析失败则忽略(不影响正常上报)。\n */\n traceparent?: string | W3cTraceContext;\n}\n\nconst emptyBatch = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class ObservabilityTelemetry implements Telemetry {\n private queued: EventBatch = emptyBatch();\n private timer: NodeJS.Timeout | undefined;\n private startedAt = new Map<string, number>(); // traceId -> queuedAt\n /** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */\n private inFlightOrder: string[] = [];\n private batchSize: number;\n private redact?: (batch: EventBatch) => EventBatch;\n private appVersion?: string;\n private redactEnabled: boolean;\n private redactOverrides?: Record<string, RedactAction>;\n\n // ─── Phase 9:采样 ───────────────────────────────────────────────\n private sampleStrategy: SampleStrategy;\n private judge: SamplingJudge;\n\n // ─── Phase 9:W3C Trace Context ──────────────────────────────────\n /** 外部传入的 traceparent 上下文(共享:所有 run 视作其子链路) */\n private w3cCtx: W3cTraceContext | null;\n /** traceId → 为该 run 生成的 w3c trace 数据(上报时注入 RunRecord) */\n private w3cByRun = new Map<string, { w3cTraceId: string; parentTraceId?: string }>();\n\n constructor(private reporter: { send(batch: EventBatch): Promise<boolean> }, opts: FlushQueueOptions = {}) {\n this.batchSize = opts.batchSize ?? 50;\n this.redact = opts.redact;\n this.appVersion = opts.appVersion;\n this.redactEnabled = opts.redactEnabled ?? true;\n this.redactOverrides = opts.redactOverrides;\n\n // ─── 采样策略:sampleStrategy 优先;缺省退回 sampleRate(兼容旧语义);都未配置 = none ───\n if (opts.sampleStrategy && opts.sampleStrategy.strategy) {\n this.sampleStrategy = resolveSampleStrategy(opts.sampleStrategy);\n } else {\n this.sampleStrategy = legacySampleRateToStrategy(opts.sampleRate);\n }\n this.judge = createSamplingJudge(this.sampleStrategy);\n\n // ─── W3C traceparent ─────────────────────────────────────────\n this.w3cCtx = parseTraceparentInput(opts.traceparent);\n\n const intervalMs = opts.intervalMs ?? 5000;\n this.timer = setInterval(() => void this.flush(), intervalMs);\n this.timer.unref?.();\n }\n\n onRunStart = (info: RunStartTelemetryInfo): void => {\n this.startedAt.set(info.traceId, info.queuedAt);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n this.inFlightOrder.push(info.traceId);\n\n // Phase 9:为该 run 生成 W3C 上下文(即使外部未传入也生成自身 w3cTraceId,便于面板统一展示)\n const parent = this.w3cCtx;\n const w3cTraceId = parent ? parent.traceId : generateW3cTraceId();\n const parentTraceId = parent ? parent.traceId : undefined;\n this.w3cByRun.set(info.traceId, { w3cTraceId, parentTraceId });\n };\n\n onRunEnd = (info: RunTelemetryInfo): void => {\n const queuedAt = this.startedAt.get(info.traceId) ?? Date.now() - info.durationMs;\n this.startedAt.delete(info.traceId);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n\n // Phase 9:整 run 采样判定(必须先判定,才能联动明细 shouldKeepDetail)\n const keep = this.judge.shouldKeepRun({\n traceId: info.traceId,\n status: info.success\n ? 'success'\n : info.errorClass === 'validation'\n ? 'validation'\n : 'error',\n durationMs: info.durationMs,\n });\n if (!keep) {\n this.w3cByRun.delete(info.traceId);\n return;\n }\n\n const run = this.runRecord(info, queuedAt);\n this.queued.runs.push(run);\n this.queued.spans.push(this.runSpan(info, queuedAt));\n this.maybeFlush();\n };\n\n onToolCall = (info: ToolTelemetryInfo): void => {\n if (!this.judge.shouldKeepDetail(info.traceId)) return; // Phase 9:采样联动\n const startedAt = Date.now() - info.durationMs;\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'tool',\n name: `tool:${info.toolName}`,\n startedAt,\n durationMs: info.durationMs,\n status: info.status === 'ok' ? 'ok' : 'error',\n errorClass: info.errorClass,\n sessionKey: info.sessionKey,\n });\n this.queued.toolCalls.push({\n traceId: info.traceId,\n spanId: info.spanId,\n toolName: info.toolName,\n status: info.status,\n durationMs: info.durationMs,\n errorClass: info.errorClass,\n });\n this.maybeFlush();\n };\n\n onModelCall = (info: ModelTelemetryInfo): void => {\n if (!this.judge.shouldKeepDetail(info.traceId)) return; // Phase 9:采样联动\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'model',\n name: `model:${info.modelId}`,\n startedAt: Date.now() - info.durationMs,\n durationMs: info.durationMs,\n status: info.errorClass ? 'error' : 'ok',\n errorClass: info.errorClass,\n attempts: info.attempts,\n inputTokens: info.inputTokens,\n outputTokens: info.outputTokens,\n cacheRead: info.cacheRead,\n cacheWrite: info.cacheWrite,\n sessionKey: info.sessionKey,\n });\n this.maybeFlush();\n };\n\n onRetry = (info: RetryTelemetryInfo): void => {\n if (!this.judge.shouldKeepDetail(info.traceId)) return; // Phase 9:采样联动(重试只保留已采 runs)\n // P2-2:per-attempt 重试明细(关联 model span),server 落 retry_attempts 表\n this.queued.retries.push({\n traceId: info.traceId,\n spanId: info.spanId,\n provider: info.provider,\n modelId: info.modelId,\n attempt: info.attempt,\n errorClass: info.errorClass,\n status: info.status,\n delayMs: info.delayMs,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n onPermissionDenied = (info: PermissionDeniedTelemetryInfo): void => {\n // 权限事件一般数量少,全量保留(不参与采样,便于安全审计)\n this.queued.permissions.push({\n traceId: info.traceId,\n sessionKey: info.sessionKey,\n toolName: info.toolName,\n reason: info.reason,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n /**\n * 当前 run 上下文(供结构化 logger 注入 traceId)。\n * 单并发时返回唯一 in-flight run;并发时返回最近开始的(约定语义,文档说明)。\n */\n currentContext(): { traceId?: string } {\n const traceId = this.inFlightOrder[this.inFlightOrder.length - 1];\n return traceId ? { traceId } : {};\n }\n\n /**\n * P2-1 自定义业务事件(通用埋点)。\n * run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。\n * Phase 9:内置 PII 脱敏在 flush 阶段对 data 深度遍历。\n */\n emit(name: string, data?: unknown, opts: { traceId?: string; sessionKey?: string } = {}): void {\n this.queued.events.push({\n traceId: opts.traceId ?? this.currentContext().traceId,\n sessionKey: opts.sessionKey,\n name,\n data,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n }\n\n /** Phase 9 — 主动读取/注入当前 W3C traceparent(可传给下游 HTTP 调用)。 */\n currentTraceparent(): string | null {\n const traceId = this.currentContext().traceId;\n if (!traceId) {\n // 无 in-flight run:若有共享外部 ctx,返回父 spanId;否则生成一次性\n if (this.w3cCtx) return this.w3cCtx.raw;\n return null;\n }\n const w3c = this.w3cByRun.get(traceId);\n const w3cTraceId = w3c?.w3cTraceId ?? (this.w3cCtx?.traceId ?? generateW3cTraceId());\n // 把 aipack traceId 截断/映射为 16 hex 的 spanId(deterministic)\n const parentId = aipackSpanIdToW3c(traceId);\n return formatTraceparent({ traceId: w3cTraceId, parentId, sampled: true });\n }\n\n /** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */\n flush(): void {\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n let final = batch;\n // Phase 9:内置 PII 脱敏(先内置规则,再用户自定义 redact 钩子)\n if (this.redactEnabled) {\n final = applyBuiltinRedact(final, this.redactOverrides);\n }\n if (this.redact) final = this.redact(final);\n void this.reporter.send(final);\n }\n\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n async close(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n let final = batch;\n if (this.redactEnabled) final = applyBuiltinRedact(final, this.redactOverrides);\n if (this.redact) final = this.redact(final);\n await this.reporter.send(final);\n }\n\n private runRecord(info: RunTelemetryInfo, queuedAt: number): RunRecord {\n const w3c = this.w3cByRun.get(info.traceId);\n this.w3cByRun.delete(info.traceId);\n return {\n traceId: info.traceId,\n startedAt: queuedAt,\n endedAt: queuedAt + info.durationMs,\n sessionKey: info.sessionKey,\n channel: info.request.channel,\n model: info.request.model,\n status: info.success\n ? 'success'\n : info.errorClass === 'validation'\n ? 'validation'\n : 'error',\n errorClass: info.errorClass,\n turns: info.turnCount,\n durationMs: info.durationMs,\n activeMs: info.activeMs,\n queuedMs: info.queuedMs,\n ttftMs: info.ttftMs,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n cacheRead: info.tokens.cacheRead,\n cacheWrite: info.tokens.cacheWrite,\n ...(this.appVersion !== undefined ? { appVersion: this.appVersion } : {}),\n ...(w3c?.parentTraceId !== undefined ? { parentTraceId: w3c.parentTraceId } : {}),\n ...(w3c?.w3cTraceId !== undefined ? { w3cTraceId: w3c.w3cTraceId } : {}),\n };\n }\n\n private runSpan(info: RunTelemetryInfo, queuedAt: number): SpanRecord {\n return {\n traceId: info.traceId,\n spanId: info.traceId,\n kind: 'run',\n name: 'run',\n startedAt: queuedAt,\n durationMs: info.durationMs,\n status: info.success ? 'ok' : 'error',\n errorClass: info.errorClass,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n sessionKey: info.sessionKey,\n };\n }\n\n private isEmpty(): boolean {\n return (\n !this.queued.runs.length &&\n !this.queued.spans.length &&\n !this.queued.toolCalls.length &&\n !this.queued.permissions.length &&\n !this.queued.retries.length &&\n !this.queued.events.length\n );\n }\n\n private maybeFlush(): void {\n const total =\n this.queued.runs.length +\n this.queued.spans.length +\n this.queued.toolCalls.length +\n this.queued.permissions.length +\n this.queued.retries.length +\n this.queued.events.length;\n if (total >= this.batchSize) this.flush();\n }\n}\n\n// ─── 工具 ─────────────────────────────────────────────────────────\n\n/** 对整批 EventBatch 应用内置 PII 脱敏 */\nfunction applyBuiltinRedact(batch: EventBatch, overrides?: Record<string, RedactAction>): EventBatch {\n // events.data 深度遍历(最可能含 PII 的自由字段)\n const events = batch.events.map((e) =>\n e.data === undefined ? e : { ...e, data: redactValue(e.data, overrides) },\n );\n // permissions.reason 可能带用户输入\n const permissions = batch.permissions.map((p) => ({\n ...p,\n reason: redactString(p.reason, overrides),\n }));\n return {\n runs: batch.runs, // runs 字段结构化,不做默认脱敏(避免破坏聚合)\n spans: batch.spans,\n toolCalls: batch.toolCalls,\n retries: batch.retries,\n events,\n permissions,\n };\n}\n\n/** 兼容用户输入:字符串或结构化对象 */\nfunction parseTraceparentInput(input: string | W3cTraceContext | undefined): W3cTraceContext | null {\n if (!input) return null;\n if (typeof input === 'string') {\n const r = parseTraceparent(input);\n return r.ok ? r.ctx : null;\n }\n // 结构化对象\n return input && typeof input.traceId === 'string' ? input : null;\n}\n\n/** aipack traceId → 16 hex W3C parentId(deterministic:FNV-1a 截断) */\nfunction aipackSpanIdToW3c(aipackTraceId: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < aipackTraceId.length; i++) {\n h ^= aipackTraceId.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n const hex = (h >>> 0).toString(16).padStart(8, '0');\n // 16 hex 需要再补 8:后半段用简单二次哈希\n let h2 = 0x84222325;\n for (let i = aipackTraceId.length - 1; i >= 0; i--) {\n h2 ^= aipackTraceId.charCodeAt(i);\n h2 = Math.imul(h2, 0x01000193);\n }\n const hex2 = (h2 >>> 0).toString(16).padStart(8, '0');\n const out = hex + hex2;\n if (/^0{16}$/.test(out)) return '0000000000000001';\n return out;\n}\n\n/** 复用 rules.ts 的字符串脱敏(避免 circular import:此处内联简单 wrap) */\nfunction redactString(raw: string, overrides?: Record<string, RedactAction>): string {\n return redactValue<string>(raw, overrides);\n}\n","/**\n * 结构化 logger(P1-1 日志关联)。\n *\n * 约定:logfmt 或 JSON 输出,可通过 context() 动态注入当前上下文(如 traceId),\n * 使应用日志与 Trace 关联(排障时按 traceId 检索日志流)。\n *\n * const logger = createLogger({ format: 'logfmt', context: () => telemetry.currentContext() });\n * logger.info('tool called', { tool: 'search_flights', durationMs: 120 });\n * // => time=2026-08-11T10:00:00.000Z level=info msg=\"tool called\" tool=search_flights durationMs=120\n *\n * 与收集服务 webhook/告警日志不同:本 logger 只负责\"应用日志\"输出约定,\n * 不参与上报(上报走 telemetry)。\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type LogFormat = 'logfmt' | 'json';\n\nexport interface LoggerOptions {\n /** 输出格式,默认 logfmt */\n format?: LogFormat;\n /** 最低输出级别,默认 info */\n level?: LogLevel;\n /** 每行日志的固定字段(如 service/app),会被动态 context / 单行 fields 覆盖 */\n tags?: Record<string, unknown>;\n /** 动态上下文(每行调用),例如 () => telemetry.currentContext() 注入 traceId */\n context?: () => Record<string, unknown> | undefined;\n /** 输出目标,默认 console(line 已含换行) */\n dest?: (line: string) => void;\n /** 是否注入 ISO 时间戳字段 time,默认 true */\n timestamps?: boolean;\n}\n\nexport interface Logger {\n debug(msg: string, fields?: Record<string, unknown>): void;\n info(msg: string, fields?: Record<string, unknown>): void;\n warn(msg: string, fields?: Record<string, unknown>): void;\n error(msg: string, fields?: Record<string, unknown>): void;\n /** 派生子 logger:合并部分配置(如固定业务标签) */\n child(overrides: Partial<LoggerOptions>): Logger;\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };\n\n/** 隐藏敏感字段名(值以 *** 代替),防止 token/key 明文进日志 */\nconst REDACTED_KEYS = /(secret|token|password|key|authorization|apikey)/i;\n\nexport function createLogger(opts: LoggerOptions = {}): Logger {\n const { format = 'logfmt', dest = defaultDest } = opts;\n const minLevel = LEVEL_ORDER[opts.level ?? 'info'];\n const timestamps = opts.timestamps ?? true;\n\n const emit = (level: LogLevel, msg: string, fields?: Record<string, unknown>): void => {\n if (LEVEL_ORDER[level] < minLevel) return;\n const context = opts.context?.() ?? {};\n dest(formatLine({ format, level, msg, fields, context, tags: opts.tags, timestamps }));\n };\n\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (overrides) =>\n createLogger({\n format: overrides.format ?? opts.format,\n level: overrides.level ?? opts.level,\n tags: { ...opts.tags, ...overrides.tags },\n context: overrides.context ?? opts.context,\n dest: overrides.dest ?? opts.dest,\n timestamps: overrides.timestamps ?? opts.timestamps,\n }),\n };\n}\n\ninterface FormatInput {\n format: LogFormat;\n level: LogLevel;\n msg: string;\n fields?: Record<string, unknown>;\n context?: Record<string, unknown>;\n tags?: Record<string, unknown>;\n timestamps: boolean;\n}\n\nfunction formatLine(input: FormatInput): string {\n const data: Record<string, unknown> = {};\n if (input.timestamps) data.time = new Date().toISOString();\n data.level = input.level;\n data.msg = input.msg;\n // 顺序:tags → context → fields(后者覆盖前者)\n for (const src of [input.tags, input.context, input.fields]) {\n if (!src) continue;\n for (const [k, v] of Object.entries(src)) {\n data[k] = REDACTED_KEYS.test(k) ? '***' : v;\n }\n }\n return input.format === 'json' ? JSON.stringify(data) : toLogfmt(data);\n}\n\n/** logfmt 渲染:值含空白/引号/等号/反斜杠时按 RFC 引号包裹并转义 */\nfunction toLogfmt(data: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(data)) {\n if (v === undefined || v === null) continue;\n parts.push(`${k}=${logfmtValue(v)}`);\n }\n return parts.join(' ');\n}\n\nfunction logfmtValue(v: unknown): string {\n if (typeof v === 'string') return quoteLogfmt(v);\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n if (typeof v === 'bigint') return v.toString();\n // 对象/数组 → JSON 序列化后引号包裹\n return quoteLogfmt(JSON.stringify(v));\n}\n\nfunction quoteLogfmt(s: string): string {\n if (!/[ \\t\"=\\\\]/u.test(s)) return s;\n return '\"' + s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') + '\"';\n}\n\nfunction defaultDest(line: string): void {\n // 走 console.log 而非 process.stdout.write,便于宿主拦截/重定向\n console.log(line);\n}\n","/**\n * OTLP/HTTP JSON trace exporter(P1-2 标准协议,档位 B 起步)。\n *\n * 无外部依赖:直接把 EventBatch 映射为 OTLP JSON ExportTraceServiceRequest\n * (protobuf JSON 编码),POST 到 OpenTelemetry Collector 的 /v1/traces。\n *\n * - traceId/spanId:OTLP 要求 16/8 字节二进制(base64),本 SDK 的 traceId 是任意\n * 字符串,故用 md5 做确定性映射(同一字符串恒得同一字节序列);\n * - 导出为 best-effort:失败仅 warn,不影响主上报链路(主链路仍走 HttpReporter);\n * - 未配置 otlp 时零开销(不构造 exporter)。\n */\n\nimport { createHash } from 'node:crypto';\nimport type { EventBatch, RunRecord, SpanRecord } from './types';\n\nexport interface OtlpExporterOptions {\n /** OTLP/HTTP JSON 端点,如 http://otel-collector:4318(POST {endpoint}/v1/traces) */\n endpoint: string;\n /** resource.service.name,默认 = appId */\n serviceName?: string;\n /** 应用标识(写入 span attribute aipack.app,便于按应用过滤) */\n appId?: string;\n /** 额外请求头(如 x-otlp-token) */\n headers?: Record<string, string>;\n /** 超时(ms),默认 3000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nexport interface OtlpTraceExporter {\n /** 导出批次;失败内部消化,恒不抛错,返回是否成功 */\n export(batch: EventBatch): Promise<boolean>;\n}\n\n/** md5 确定性映射为 N 字节(traceId=16B / spanId=8B) */\nfunction bytesOf(input: string, n: number): Buffer {\n return createHash('md5').update(input).digest().subarray(0, n);\n}\n\nfunction str(value: unknown): { stringValue: string } {\n return { stringValue: value == null ? '' : String(value) };\n}\nfunction int64(value: number): { intValue: string } {\n return { intValue: String(Math.round(value || 0)) };\n}\n\nfunction attr(key: string, value: { stringValue?: string; intValue?: string }): object {\n return { key, value };\n}\n\n/** token 总消耗(input + output + cacheRead + cacheWrite);无 token 数据时返回 undefined */\nfunction tokenTotal(r: { inputTokens?: number; outputTokens?: number; cacheRead?: number; cacheWrite?: number }): number | undefined {\n if (r.inputTokens === undefined && r.outputTokens === undefined) return undefined;\n return (r.inputTokens ?? 0) + (r.outputTokens ?? 0) + (r.cacheRead ?? 0) + (r.cacheWrite ?? 0);\n}\n\n/** RunRecord → OTLP span(根 span,kind=INTERNAL) */\nfunction runToSpan(r: RunRecord, appId?: string): object {\n const startNs = BigInt(r.startedAt) * 1_000_000n;\n const endNs = BigInt(r.startedAt + r.durationMs) * 1_000_000n;\n const ok = r.status === 'success';\n const attrs: object[] = [\n attr('session.key', str(r.sessionKey)),\n attr('aipack.status', str(r.status)),\n attr('aipack.turns', int64(r.turns)),\n attr('aipack.duration_ms', int64(r.durationMs)),\n attr('tokens.input', int64(r.inputTokens)),\n attr('tokens.output', int64(r.outputTokens)),\n ];\n if (tokenTotal(r) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(r)!)));\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (r.model) attrs.push(attr('model', str(r.model)));\n if (r.errorClass) attrs.push(attr('aipack.error_class', str(r.errorClass)));\n return {\n traceId: bytesOf(r.traceId, 16).toString('base64'),\n spanId: bytesOf(`${r.traceId}:run`, 8).toString('base64'),\n name: 'agent.run',\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: r.errorClass || r.status },\n };\n}\n\n/** SpanRecord → OTLP span */\nfunction spanToSpan(s: SpanRecord, appId?: string): object {\n const startNs = BigInt(s.startedAt) * 1_000_000n;\n const endNs = BigInt(s.startedAt + s.durationMs) * 1_000_000n;\n const ok = s.status === 'ok';\n const attrs: object[] = [\n attr('span.kind', str(s.kind)),\n attr('session.key', str(s.sessionKey)),\n attr('aipack.status', str(s.status)),\n attr('aipack.duration_ms', int64(s.durationMs)),\n ];\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (s.inputTokens !== undefined) attrs.push(attr('tokens.input', int64(s.inputTokens)));\n if (s.outputTokens !== undefined) attrs.push(attr('tokens.output', int64(s.outputTokens)));\n if (tokenTotal(s) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(s)!)));\n if (s.attempts !== undefined) attrs.push(attr('aipack.attempts', int64(s.attempts)));\n if (s.errorClass) attrs.push(attr('aipack.error_class', str(s.errorClass)));\n return {\n traceId: bytesOf(s.traceId, 16).toString('base64'),\n spanId: bytesOf(s.spanId, 8).toString('base64'),\n name: s.name,\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: s.errorClass || s.status },\n };\n}\n\n/** EventBatch → OTLP ExportTraceServiceRequest(JSON 对象,可直接 JSON.stringify) */\nexport function toOtlpJsonTraces(batch: EventBatch, serviceName: string, appId?: string): object {\n const spans: object[] = [];\n for (const r of batch.runs) spans.push(runToSpan(r, appId));\n for (const s of batch.spans) spans.push(spanToSpan(s, appId));\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: str(serviceName) },\n { key: 'telemetry.sdk.name', value: str('aipack') },\n { key: 'telemetry.sdk.language', value: str('nodejs') },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'aipack', version: '0.1.0' },\n spans,\n },\n ],\n },\n ],\n };\n}\n\nexport function createOtlpTraceExporter(opts: OtlpExporterOptions): OtlpTraceExporter {\n const endpoint = opts.endpoint.replace(/\\/+$/, '');\n const serviceName = opts.serviceName ?? 'aipack-app';\n const timeoutMs = opts.timeoutMs ?? 3000;\n const headers = { 'content-type': 'application/json', ...opts.headers };\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n\n return {\n async export(batch) {\n const runs = batch.runs.length;\n const spans = batch.spans.length;\n if (!runs && !spans) return true; // 无 trace 数据(纯 tool/permission)→ 无操作\n try {\n const body = JSON.stringify(toOtlpJsonTraces(batch, serviceName, opts.appId));\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(`${endpoint}/v1/traces`, {\n method: 'POST',\n headers,\n body,\n signal: ac.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (res.ok) return true;\n console.warn(`[aipack/observability] OTLP 导出被拒绝(${res.status}),已跳过(不影响主上报)`);\n return false;\n } catch (err) {\n console.warn('[aipack/observability] OTLP 导出失败(不影响主上报):', (err as Error).message);\n return false;\n }\n },\n };\n}\n","/**\n * @aipack-ai/observability — aipack 可观测性上报 SDK。\n *\n * 埋点上报模式:客户端只需 appId + appSecret,6 类 Telemetry 事件自动批量\n * POST 到收集服务(默认 http://localhost:8787),失败本地缓存补报。\n *\n * const obs = createObservability({ appId: 'my-app', appSecret: 'xxx' });\n * createRuntime({ ..., telemetry: obs.telemetry });\n *\n * 收集服务见 @aipack-ai/observability-server(独立包,SQLite 落盘 + 内存聚合 + REST API)。\n *\n * Phase 9 新增:\n * - sampleStrategy:error-priority / slow-priority / traceid-ratio(替代简单 sampleRate)\n * - W3C Trace Context:traceparent 输入/输出(跨系统调用链打通)\n * - 内置 PII 脱敏:默认启用,支持规则级动作覆盖(mask/hash/drop)\n */\n\nimport { HttpReporter } from './reporter';\nimport { ObservabilityTelemetry } from './telemetry';\nimport { createLogger, type Logger } from './logger';\nimport { createOtlpTraceExporter } from './otlp';\nimport type { SampleStrategyOptions } from './sampling';\nimport type { W3cTraceContext } from './w3c-trace-context';\nimport type { RedactAction } from './redact/rules';\n\nexport interface CreateObservabilityOptions {\n /** 应用标识(必填),收集端用它校验身份并隔离数据 */\n appId: string;\n /** 应用密钥(必填),与收集端 OBS_APPS 白名单匹配 */\n appSecret: string;\n /** 收集服务地址,默认 http://localhost:8787 */\n endpoint?: string;\n /** 上报失败本地缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限,默认 2000 */\n maxCacheSize?: number;\n /** 上报周期(ms),默认 5000 */\n flushIntervalMs?: number;\n /** 积攒条数触发上报,默认 50 */\n flushBatchSize?: number;\n /** 可选:OTLP/HTTP JSON trace 导出(推 OpenTelemetry Collector),失败不影响主上报 */\n otlp?: {\n endpoint: string;\n serviceName?: string;\n headers?: Record<string, string>;\n };\n /**\n * 【旧兼容】P2-1 明细采样率(0–1):只对 model/tool spans 与 toolCalls 采样,\n * runs/permissions/events 全量。缺省 1。\n * 新版 sampleStrategy 优先;sampleRate 未设置 sampleStrategy 时等价 traceid-ratio。\n */\n sampleRate?: number;\n /**\n * Phase 9 — 采样策略:错误/慢请求优先或一致性 traceId 采样。\n * 优先级高于 sampleRate。\n *\n * 示例:\n * // 错误 100% 保留,成功按 10% 采样\n * sampleStrategy: { strategy: 'error-priority', successRate: 0.1 }\n * // 同 trace 稳定采/弃(跨明细一致)\n * sampleStrategy: { strategy: 'traceid-ratio', rate: 0.2 }\n */\n sampleStrategy?: SampleStrategyOptions;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报),在内置脱敏之后执行 */\n redact?: (batch: import('./types').EventBatch) => import('./types').EventBatch;\n /** Phase 9 — 是否启用内置 PII 脱敏(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */\n redactEnabled?: boolean;\n /** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop', idCard: 'drop' } */\n redactOverrides?: Record<string, RedactAction>;\n /** 发布版本(如 '1.3.0'),随每条 run 上报,供收集端按版本聚合对比。缺省不携带 */\n version?: string;\n /**\n * Phase 9 — W3C Trace Context 父链路。\n * - 字符串:traceparent 请求头原值(如 `00-xxxxxx-yyyyyy-01`)\n * - 对象:已解析的 W3cTraceContext\n * 配置后,所有 run 视作其子链路,面板可跨系统跳转。\n */\n traceparent?: string | W3cTraceContext;\n}\n\nexport interface Observability {\n /** 注入 RuntimeOptions.telemetry */\n telemetry: ObservabilityTelemetry;\n /** 结构化 logger:自动注入当前 run 的 traceId(P1-1 日志关联) */\n logger: Logger;\n /** P2-1 自定义业务事件:run 内自动带 traceId,入 Trace 时间轴 */\n emit(name: string, data?: unknown, opts?: { traceId?: string; sessionKey?: string }): void;\n /** 立即上报队列中的残留数据 */\n flush(): void;\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n close(): Promise<void>;\n /**\n * Phase 9 — 当前链路的 traceparent 字符串(可传给下游 HTTP 调用实现跨系统追踪)。\n * 无 in-flight run 且未配置外部 traceparent 时返回 null。\n */\n currentTraceparent(): string | null;\n}\n\nexport function createObservability(opts: CreateObservabilityOptions): Observability {\n const reporter = new HttpReporter({\n endpoint: opts.endpoint ?? 'http://localhost:8787',\n appId: opts.appId,\n appSecret: opts.appSecret,\n cacheDir: opts.cacheDir,\n maxCacheSize: opts.maxCacheSize,\n });\n\n // OTLP 导出作为旁路:await 但不影响主上报结果;exporter 内部恒不抛错\n const sink: { send(batch: import('./types').EventBatch): Promise<boolean> } = reporter;\n if (opts.otlp) {\n const exporter = createOtlpTraceExporter({\n endpoint: opts.otlp.endpoint,\n serviceName: opts.otlp.serviceName ?? opts.appId,\n appId: opts.appId,\n headers: opts.otlp.headers,\n });\n sink.send = async (batch) => {\n const ok = await reporter.send(batch);\n await exporter.export(batch);\n return ok;\n };\n }\n\n const telemetry = new ObservabilityTelemetry(sink, {\n intervalMs: opts.flushIntervalMs,\n batchSize: opts.flushBatchSize,\n sampleRate: opts.sampleRate,\n sampleStrategy: opts.sampleStrategy,\n redact: opts.redact,\n redactEnabled: opts.redactEnabled,\n redactOverrides: opts.redactOverrides,\n appVersion: opts.version,\n traceparent: opts.traceparent,\n });\n const logger = createLogger({\n tags: { app: opts.appId },\n context: () => telemetry.currentContext(),\n });\n\n return {\n telemetry,\n logger,\n emit: (name, data, emitOpts) => telemetry.emit(name, data, emitOpts),\n flush: () => telemetry.flush(),\n close: async () => {\n await telemetry.close();\n },\n currentTraceparent: () => telemetry.currentTraceparent(),\n };\n}\n\n// ─── 类型导出 ─────────────────────────────────────────────────────\n\nexport { ObservabilityTelemetry } from './telemetry';\nexport type { FlushQueueOptions } from './telemetry';\nexport { HttpReporter } from './reporter';\nexport type { ReporterOptions } from './reporter';\nexport { createLogger } from './logger';\nexport type { Logger, LoggerOptions, LogLevel, LogFormat } from './logger';\nexport { createOtlpTraceExporter, toOtlpJsonTraces } from './otlp';\nexport type { OtlpTraceExporter, OtlpExporterOptions } from './otlp';\n// Phase 9 新增导出:采样 / W3C / 脱敏\nexport {\n createSamplingJudge,\n resolveSampleStrategy,\n legacySampleRateToStrategy,\n} from './sampling';\nexport type {\n SampleStrategy,\n SampleStrategyOptions as SamplingOptions,\n SamplingJudge,\n} from './sampling';\nexport {\n parseTraceparent,\n formatTraceparent,\n generateW3cTraceId,\n generateW3cParentId,\n} from './w3c-trace-context';\nexport type { W3cTraceContext } from './w3c-trace-context';\nexport { BUILTIN_RULES, redactString, redactValue } from './redact/rules';\nexport type { RedactRule, RedactAction } from './redact/rules';\nexport type {\n RunRecord,\n SpanRecord,\n ToolCallRecord,\n PermissionRecord,\n EventRecord,\n RetryRecord,\n EventBatch,\n} from './types';\n"],"mappings":"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","names":["redactString","createHash"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipack-ai/observability",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "aipack 可观测性上报 SDK(S2):埋点上报模式,appId+appSecret 一行接入,失败本地缓存补报",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -28,14 +28,14 @@
28
28
  "node": ">=18.0.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@aipack-ai/agent": "0.0.1"
31
+ "@aipack-ai/agent": "0.0.2"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^20.12.0",
35
35
  "tsup": "^8.5.1",
36
36
  "tsx": "^4.16.0",
37
37
  "typescript": "^5.5.0",
38
- "@aipack-ai/agent": "0.0.1"
38
+ "@aipack-ai/agent": "0.0.2"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsup",