@zhushanwen/pi-extension-logger 0.4.1 → 0.6.1

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-extension-logger",
3
- "version": "0.4.1",
4
- "description": "Shared logging helper for Pi extensions — three-channel routing (AI realtime / audit / debug) (shared library, not a Pi extension - no install.mjs needed)",
3
+ "version": "0.6.1",
4
+ "description": "Shared logging helper for Pi extensions — two-channel routing (audit appendEntry / debug file log; the third AI-realtime channel stays with pi native) (shared library, not a Pi extension - no install.mjs needed)",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "keywords": [
@@ -27,7 +27,7 @@ describe("extension-logger", () => {
27
27
  setPiHandle(undefined);
28
28
  clearRateLimiterState();
29
29
  vi.restoreAllMocks();
30
- // 还原环境变量,避免文件日志测试的 XYZ_AGENT_DEBUG/PI_CODING_AGENT_DIR 泄漏到其它用例
30
+ // 还原环境变量,避免文件日志测试的 TAIJI_AGENT_DEBUG/PI_CODING_AGENT_DIR 泄漏到其它用例
31
31
  process.env = { ...prevEnv };
32
32
  });
33
33
 
@@ -140,7 +140,7 @@ describe("extension-logger", () => {
140
140
  });
141
141
  });
142
142
 
143
- describe("XYZ_AGENT_DEBUG 文件日志", () => {
143
+ describe("TAIJI_AGENT_DEBUG 文件日志", () => {
144
144
  // 文件日志的 agentDir 通过 getAgentDir() 推导(读 PI_CODING_AGENT_DIR,
145
145
  // 默认 ~/.pi/agent)。设 PI_CODING_AGENT_DIR 到 tmpdir 子目录,与组 A 的
146
146
  // arch-boundary 改动(fileLog 用 getAgentDir())保持一致。
@@ -155,19 +155,19 @@ describe("extension-logger", () => {
155
155
  afterEach(() => {
156
156
  vi.useRealTimers();
157
157
  if (tmpAgentDir) {
158
- rmSync(tmpAgentDir, { recursive: true, force: true });
158
+ rmSync(tmpAgentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
159
159
  }
160
160
  });
161
161
 
162
- it("XYZ_AGENT_DEBUG 未设时 debug 是 no-op(不抛错即可)", () => {
163
- delete process.env.XYZ_AGENT_DEBUG;
162
+ it("TAIJI_AGENT_DEBUG 未设时 debug 是 no-op(不抛错即可)", () => {
163
+ delete process.env.TAIJI_AGENT_DEBUG;
164
164
  const logger = createLogger("test", pi);
165
165
  expect(() => logger.debug("no-op")).not.toThrow();
166
166
  });
167
167
 
168
168
  // ---- Suggestion #8:fileLog 实际写入路径 ----
169
- it("XYZ_AGENT_DEBUG=1 时 debug 写入日志文件,内容含 [debug] 与序列化 data", () => {
170
- process.env.XYZ_AGENT_DEBUG = "1";
169
+ it("TAIJI_AGENT_DEBUG=1 时 debug 写入日志文件,内容含 [debug] 与序列化 data", () => {
170
+ process.env.TAIJI_AGENT_DEBUG = "1";
171
171
  // 固定日期 → 文件名 <extName>-2026-08-01.log
172
172
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
173
173
 
@@ -190,8 +190,8 @@ describe("extension-logger", () => {
190
190
  expect(appendSpy).not.toHaveBeenCalled();
191
191
  });
192
192
 
193
- it("XYZ_AGENT_DEBUG=1 时 warn 既写文件又走 appendEntry(文件内容含 [warn])", () => {
194
- process.env.XYZ_AGENT_DEBUG = "1";
193
+ it("TAIJI_AGENT_DEBUG=1 时 warn 既写文件又走 appendEntry(文件内容含 [warn])", () => {
194
+ process.env.TAIJI_AGENT_DEBUG = "1";
195
195
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
196
196
 
197
197
  const logger = createLogger("warn-ext", pi);
@@ -207,8 +207,8 @@ describe("extension-logger", () => {
207
207
  expect(appendSpy).toHaveBeenCalledOnce();
208
208
  });
209
209
 
210
- it("XYZ_AGENT_DEBUG=1 写失败(只读目录)不 throw", () => {
211
- process.env.XYZ_AGENT_DEBUG = "1";
210
+ it("TAIJI_AGENT_DEBUG=1 写失败(只读目录)不 throw", () => {
211
+ process.env.TAIJI_AGENT_DEBUG = "1";
212
212
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
213
213
 
214
214
  // 已存在的 logs 目录改为只读,让 mkdirSync/appendFileSync 失败。
@@ -226,7 +226,7 @@ describe("extension-logger", () => {
226
226
 
227
227
  // 清理:先恢复写权限才能删除
228
228
  chmodSync(logDir, RESTORE_MODE);
229
- rmSync(readonlyAgentDir, { recursive: true, force: true });
229
+ rmSync(readonlyAgentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
230
230
  });
231
231
  });
232
232
 
@@ -243,13 +243,13 @@ describe("extension-logger", () => {
243
243
  const [, entry] = appendSpy.mock.calls[0]!;
244
244
  // appendEntry payload 的 data 字段保留原对象(safeStringify 仅在文件日志路径用)
245
245
  expect(entry).toMatchObject({ level: "warn" });
246
- // 验证 safeStringify 的 fallback 分支:用 XYZ_AGENT_DEBUG=1 触发 fileLog,
246
+ // 验证 safeStringify 的 fallback 分支:用 TAIJI_AGENT_DEBUG=1 触发 fileLog,
247
247
  // 文件内 data 应是 String() 形式(含 [object Object] 或循环结构字符串)
248
248
  expect(() => JSON.stringify(cyclic)).toThrow(); // 对照:原对象确实不可序列化
249
249
  });
250
250
 
251
251
  it("BigInt:warn 不崩,文件日志路径走 String fallback", () => {
252
- process.env.XYZ_AGENT_DEBUG = "1";
252
+ process.env.TAIJI_AGENT_DEBUG = "1";
253
253
  const tmpAgentDir = mkdtempSync(join(tmpdir(), "pi-ext-bigint-"));
254
254
  process.env.PI_CODING_AGENT_DIR = tmpAgentDir;
255
255
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
@@ -269,21 +269,21 @@ describe("extension-logger", () => {
269
269
  expect(content).not.toContain('"count"');
270
270
 
271
271
  vi.useRealTimers();
272
- rmSync(tmpAgentDir, { recursive: true, force: true });
272
+ rmSync(tmpAgentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
273
273
  });
274
274
  });
275
275
 
276
276
  // ============================================================
277
- // XYZ_AGENT_EXT_LOG 托管观测档(设计 file-lock-unification-and-reaper-sink §3.2-D4)
277
+ // TAIJI_AGENT_EXT_LOG 托管观测档(设计 file-lock-unification-and-reaper-sink §3.2-D4)
278
278
  // ============================================================
279
- describe("XYZ_AGENT_EXT_LOG 托管观测档", () => {
279
+ describe("TAIJI_AGENT_EXT_LOG 托管观测档", () => {
280
280
  let tmpAgentDir: string;
281
281
 
282
282
  beforeEach(() => {
283
283
  tmpAgentDir = mkdtempSync(join(tmpdir(), "pi-ext-extlog-"));
284
284
  process.env.PI_CODING_AGENT_DIR = tmpAgentDir;
285
- delete process.env.XYZ_AGENT_DEBUG;
286
- delete process.env.XYZ_AGENT_EXT_LOG;
285
+ delete process.env.TAIJI_AGENT_DEBUG;
286
+ delete process.env.TAIJI_AGENT_EXT_LOG;
287
287
  resetExtLogCleanupForTest();
288
288
  vi.useFakeTimers();
289
289
  });
@@ -291,7 +291,7 @@ describe("extension-logger", () => {
291
291
  afterEach(() => {
292
292
  vi.useRealTimers();
293
293
  resetExtLogCleanupForTest();
294
- rmSync(tmpAgentDir, { recursive: true, force: true });
294
+ rmSync(tmpAgentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
295
295
  });
296
296
 
297
297
  it("均未注入时 no-op:debug/warn/error 调用后 logs 目录都不创建(裸 pi 用户零磁盘影响)", () => {
@@ -302,8 +302,8 @@ describe("extension-logger", () => {
302
302
  expect(existsSync(join(tmpAgentDir, "logs"))).toBe(false);
303
303
  });
304
304
 
305
- it("仅 XYZ_AGENT_EXT_LOG=1:debug 调用以 info 级落盘(含序列化 data),不标 debug", () => {
306
- process.env.XYZ_AGENT_EXT_LOG = "1";
305
+ it("仅 TAIJI_AGENT_EXT_LOG=1:debug 调用以 info 级落盘(含序列化 data),不标 debug", () => {
306
+ process.env.TAIJI_AGENT_EXT_LOG = "1";
307
307
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
308
308
 
309
309
  const logger = createLogger("extlog-ext", pi);
@@ -320,8 +320,8 @@ describe("extension-logger", () => {
320
320
  expect(appendSpy).not.toHaveBeenCalled();
321
321
  });
322
322
 
323
- it("仅 XYZ_AGENT_EXT_LOG=1:warn/error 照常落盘且保持原级标注", () => {
324
- process.env.XYZ_AGENT_EXT_LOG = "1";
323
+ it("仅 TAIJI_AGENT_EXT_LOG=1:warn/error 照常落盘且保持原级标注", () => {
324
+ process.env.TAIJI_AGENT_EXT_LOG = "1";
325
325
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
326
326
 
327
327
  const logger = createLogger("extlog-err", pi);
@@ -335,9 +335,9 @@ describe("extension-logger", () => {
335
335
  expect(content).not.toContain("[info]");
336
336
  });
337
337
 
338
- it("XYZ_AGENT_DEBUG=1 与 EXT_LOG 同注入:按更详细的生效(debug 全量,原级标注)", () => {
339
- process.env.XYZ_AGENT_EXT_LOG = "1";
340
- process.env.XYZ_AGENT_DEBUG = "1";
338
+ it("TAIJI_AGENT_DEBUG=1 与 EXT_LOG 同注入:按更详细的生效(debug 全量,原级标注)", () => {
339
+ process.env.TAIJI_AGENT_EXT_LOG = "1";
340
+ process.env.TAIJI_AGENT_DEBUG = "1";
341
341
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
342
342
 
343
343
  const logger = createLogger("both-ext", pi);
@@ -355,7 +355,7 @@ describe("extension-logger", () => {
355
355
  });
356
356
 
357
357
  it("保留期清理:7 天前的 <ext>-<date>.log 被删,近期与本包 pattern 外文件保留", () => {
358
- process.env.XYZ_AGENT_EXT_LOG = "1";
358
+ process.env.TAIJI_AGENT_EXT_LOG = "1";
359
359
  // fake timers 冻结 Date 在真实当前时刻:文件 mtime 走真实时钟,utimesSync 把
360
360
  // old 的 mtime 设为 8 天前(> 7 天保留期),recent 与 unrelated 保留。
361
361
  const logDir = join(tmpAgentDir, "logs");
@@ -381,7 +381,7 @@ describe("extension-logger", () => {
381
381
  });
382
382
 
383
383
  it("清理只认 <ext>-YYYY-MM-DD.log pattern,不动其他 .log 文件", () => {
384
- process.env.XYZ_AGENT_EXT_LOG = "1";
384
+ process.env.TAIJI_AGENT_EXT_LOG = "1";
385
385
  const logDir = join(tmpAgentDir, "logs");
386
386
  mkdirSync(logDir, { recursive: true });
387
387
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
@@ -510,8 +510,8 @@ describe("extension-logger", () => {
510
510
  expect(appendSpy).toHaveBeenCalledTimes(10);
511
511
  });
512
512
 
513
- it("fileLog 通道不受限(XYZ_AGENT_DEBUG=1 时 100 条全写文件)", () => {
514
- process.env.XYZ_AGENT_DEBUG = "1";
513
+ it("fileLog 通道不受限(TAIJI_AGENT_DEBUG=1 时 100 条全写文件)", () => {
514
+ process.env.TAIJI_AGENT_DEBUG = "1";
515
515
  const tmpAgentDir = mkdtempSync(join(tmpdir(), "pi-ext-ratelimit-"));
516
516
  process.env.PI_CODING_AGENT_DIR = tmpAgentDir;
517
517
  vi.setSystemTime(new Date("2026-08-01T12:34:56.789Z"));
@@ -531,7 +531,7 @@ describe("extension-logger", () => {
531
531
  const lines = content.split("\n").filter(Boolean);
532
532
  expect(lines).toHaveLength(100);
533
533
 
534
- rmSync(tmpAgentDir, { recursive: true, force: true });
534
+ rmSync(tmpAgentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
535
535
  });
536
536
 
537
537
  it("appendEntry 抛错时降级不 throw(限流计数正常)", () => {
package/src/index.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  // src/index.ts
2
2
  //
3
- // 共享 extension logger —— 三层通道分类,按受众路由日志。
3
+ // 共享 extension logger —— 双通道日志封装,按受众路由。
4
4
  //
5
5
  // 设计依据:pi 宿主层(ExtensionAPI/ExtensionContext)不提供 logger 接口。
6
6
  // extension 只能裸 console(污染 TUI raw stderr)或自组 ctx.ui.notify(刷屏)+
7
- // pi.appendEntry(持久化但用户不可见)。本模块封装这套三层通道,让 @zhushanwen/pi-*
7
+ // pi.appendEntry(持久化但用户不可见)。本模块封装这套通道,让 @zhushanwen/pi-*
8
8
  // 各包统一调用,消除裸 console。
9
9
  //
10
- // 三层通道:
10
+ // 三通道分类法(本模块实装其中两通道;通道 1 为 pi 原生行为,仅登记不封装):
11
11
  // 1. AI 实时 → tool result / block reason(pi 原生,本模块不涉及)
12
- // 2. 事后排查 → pi.appendEntry(custom entry 不进 LLM 上下文,不显 TUI)
13
- // 3. 开发者调试 → 文件日志,双开关分级(写 <agentDir>/logs/,均未注入默认 no-op):
14
- // - XYZ_AGENT_DEBUG=1 → DEBUG 全量(现状语义,level 原样标注)
15
- // - XYZ_AGENT_EXT_LOG=1 → INFO 级落盘(xyz 托管环境由 runtime spawn 时经
12
+ // 2. 事后排查 → pi.appendEntry(custom entry 不进 LLM 上下文,不显 TUI)【实装】
13
+ // 3. 开发者调试 → 文件日志,双开关分级(写 <agentDir>/logs/,均未注入默认 no-op)【实装】:
14
+ // - TAIJI_AGENT_DEBUG=1 → DEBUG 全量(现状语义,level 原样标注)
15
+ // - TAIJI_AGENT_EXT_LOG=1 → INFO 级落盘(taiji 托管环境由 runtime spawn 时经
16
16
  // buildOutboundChildEnv extras 注入,设计 file-lock-unification-and-reaper-sink
17
17
  // §3.2-D4 / U3-3)+ 7 天保留期清理;debug() 调用此模式下重标 info 写入
18
18
  // - 两变量同时注入按更详细的生效(DEBUG 全量优先)
@@ -20,6 +20,9 @@
20
20
  //
21
21
  // notify(用户操作反馈)刻意不封装——它是 UI 决策,留给各 extension 在命令/视图层
22
22
  // 直接调 ctx.ui.notify。
23
+ //
24
+ // fileLog 单日文件无 size cap——显式豁免,依据:写入方为每 session 短命 pi 进程
25
+ // + 7 天保留期清理 + DEBUG 档系开发者主动排障。
23
26
 
24
27
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
25
28
  import {
@@ -44,11 +47,11 @@ import { join } from "node:path";
44
47
  // - key = msg 原文(不包含 data 参数)。若调用方把动态 id 拼进 msg
45
48
  // (如 `session=${id}`),每条 msg 不同则限流不命中。根治靠调用方把
46
49
  // 动态值放 data 参数(D4 已声明)。
47
- // - fileLog 通道全量不限流(XYZ_AGENT_DEBUG=1 / XYZ_AGENT_EXT_LOG=1 时日志写文件)。
50
+ // - fileLog 通道全量不限流(TAIJI_AGENT_DEBUG=1 / TAIJI_AGENT_EXT_LOG=1 时日志写文件)。
48
51
  // - Map cap 512 超限时全量清空(简化策略,对齐 cap-1024 先例),
49
52
  // 等价于所有 key 窗口重置,防无界增长。
50
53
  //
51
- // 设计依据:docs/todo/extension-log-cleanup-design.md §3.4 D4
54
+ // 设计依据:extension-log-cleanup-design §3.4 D4
52
55
  // ============================================================
53
56
 
54
57
  /** 同 key 每窗口允许直写 appendEntry 的最大条数。 */
@@ -73,7 +76,7 @@ interface RateLimiterEntry {
73
76
  * key = `${extName}:${level}:${msg}`(msg 为原 msg,非 prefixed)。
74
77
  * 同进程所有 logger 实例共享(模块级 singleton)。
75
78
  *
76
- * ⚠️ 生命周期与进程一致——xyz-agent 每 session 一个独立 pi 进程
79
+ * ⚠️ 生命周期与进程一致——taiji 每 session 一个独立 pi 进程
77
80
  * (见 runtime process-manager.ts 的类注释),故不存在跨 session 残留问题。
78
81
  */
79
82
  const rateLimiterState = new Map<string, RateLimiterEntry>();
@@ -153,8 +156,8 @@ export interface PiLike {
153
156
  appendEntry?(customType: string, data?: unknown): void;
154
157
  }
155
158
 
156
- /** 日志级别。debug = 开发调试;info = XYZ_AGENT_EXT_LOG 模式下 debug() 的落盘标注;warn/error = 内部降级与失败(事后排查价值)。 */
157
- export type LogLevel = "debug" | "info" | "warn" | "error";
159
+ /** 日志级别。debug = 开发调试;info = TAIJI_AGENT_EXT_LOG 模式下 debug() 的落盘标注;warn/error = 内部降级与失败(事后排查价值)。 */
160
+ type LogLevel = "debug" | "info" | "warn" | "error";
158
161
 
159
162
  /**
160
163
  * Extension logger 接口。方法名即语义——不做运行时 level filtering(pi 不支持,
@@ -162,8 +165,8 @@ export type LogLevel = "debug" | "info" | "warn" | "error";
162
165
  */
163
166
  export interface ExtensionLogger {
164
167
  /**
165
- * 开发调试日志。默认 no-op;XYZ_AGENT_DEBUG=1 时写文件日志(原级标注);
166
- * XYZ_AGENT_EXT_LOG=1(无 DEBUG)时以 info 级落盘(托管环境 INFO 观测档)。
168
+ * 开发调试日志。默认 no-op;TAIJI_AGENT_DEBUG=1 时写文件日志(原级标注);
169
+ * TAIJI_AGENT_EXT_LOG=1(无 DEBUG)时以 info 级落盘(托管环境 INFO 观测档)。
167
170
  * 不进 appendEntry。
168
171
  */
169
172
  debug(msg: string, data?: unknown): void;
@@ -219,11 +222,12 @@ export function setPiHandle(pi: PiLike | undefined): void {
219
222
  /**
220
223
  * 创建具名 logger。
221
224
  *
222
- * @param extName extension 名(如 "subagents"、"unified-hooks"),用作:
225
+ * @param extName extension 名(如 "subagents"、"smart-context"),用作:
223
226
  * - appendEntry 的 customType 前缀(`<extName>:log`)
224
227
  * - 文件日志名(`<extName>-YYYY-MM-DD.log`)
225
228
  * - msg 前缀(`[<extName>]`,自动补)
226
- * @param pi 可选 pi handle;不传则用 setPiHandle 注入的全局 handle
229
+ * @param pi 可选 pi handle;测试隔离注入用,生产一律 setPiHandle/getLogger。
230
+ * 不传则用 setPiHandle 注入的全局 handle
227
231
  * @returns ExtensionLogger 实例(每次返回同一引用,便于模块级 const 缓存)
228
232
  */
229
233
  export function createLogger(extName: string, pi?: PiLike): ExtensionLogger {
@@ -268,7 +272,7 @@ export function createLogger(extName: string, pi?: PiLike): ExtensionLogger {
268
272
  // appendEntry 失败(session 已 disposed 等)→ 降级文件日志(下方 fileLog 兜底),不 throw
269
273
  void appendErr;
270
274
  }
271
- // fileLog 全量不限流(XYZ_AGENT_DEBUG=1 排障时可见全部)
275
+ // fileLog 全量不限流(TAIJI_AGENT_DEBUG=1 排障时可见全部)
272
276
  fileLog(extName, "warn", prefixed, data);
273
277
  },
274
278
  error(msg: string, data?: unknown): void {
@@ -343,10 +347,10 @@ function prefixMsg(extName: string, msg: string): string {
343
347
  }
344
348
 
345
349
  // ============================================================
346
- // XYZ_AGENT_EXT_LOG 托管观测档 + 保留期清理
350
+ // TAIJI_AGENT_EXT_LOG 托管观测档 + 保留期清理
347
351
  // ============================================================
348
352
 
349
- /** 托管观测档(XYZ_AGENT_EXT_LOG=1)的日志保留天数,超期文件在首次落盘时清理。 */
353
+ /** 托管观测档(TAIJI_AGENT_EXT_LOG=1)的日志保留天数,超期文件在首次落盘时清理。 */
350
354
  const EXT_LOG_KEEP_DAYS = 7;
351
355
  // 一天的毫秒数(数字分隔符形式对齐 session-reader hash-provider 的时间常量惯例)
352
356
  const MS_PER_DAY = 86_400_000;
@@ -407,8 +411,8 @@ export function resetExtLogCleanupForTest(): void {
407
411
  * 写文件日志到 `<agentDir>/logs/<extName>-YYYY-MM-DD.log`。
408
412
  *
409
413
  * 双开关分级(设计 file-lock-unification-and-reaper-sink §3.2-D4):
410
- * - XYZ_AGENT_DEBUG=1 → DEBUG 全量,level 原样标注(现状语义不变);
411
- * - 仅 XYZ_AGENT_EXT_LOG=1 → INFO 级落盘:debug() 调用重标 info 写入,warn/error 照写;
414
+ * - TAIJI_AGENT_DEBUG=1 → DEBUG 全量,level 原样标注(现状语义不变);
415
+ * - 仅 TAIJI_AGENT_EXT_LOG=1 → INFO 级落盘:debug() 调用重标 info 写入,warn/error 照写;
412
416
  * - 均未注入 → no-op(零 fs 调用,裸 pi 独立用户零磁盘/行为影响)。
413
417
  * agentDir 通过 pi 的 SSOT `getAgentDir()` 推导(读
414
418
  * `PI_CODING_AGENT_DIR`/`${APP_NAME}_CODING_AGENT_DIR`,默认 `~/.pi/agent`),
@@ -420,8 +424,8 @@ export function resetExtLogCleanupForTest(): void {
420
424
  * 行可能交错(可接受——debug 日志不要求严格顺序)。
421
425
  */
422
426
  function fileLog(extName: string, level: LogLevel, msg: string, data?: unknown): void {
423
- const debugMode = process.env.XYZ_AGENT_DEBUG === "1";
424
- const extLogMode = !debugMode && process.env.XYZ_AGENT_EXT_LOG === "1";
427
+ const debugMode = process.env.TAIJI_AGENT_DEBUG === "1";
428
+ const extLogMode = !debugMode && process.env.TAIJI_AGENT_EXT_LOG === "1";
425
429
  if (!debugMode && !extLogMode) return;
426
430
  // 托管观测档:debug() 调用降档标注为 info(INFO 级观测,非 DEBUG 全量)
427
431
  const effectiveLevel: LogLevel = extLogMode && level === "debug" ? "info" : level;