billion-context-pi 0.1.28 → 0.1.29

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.js CHANGED
@@ -2411,9 +2411,9 @@ function projectMessage(message, id) {
2411
2411
  if (calls.length === 1) {
2412
2412
  const call = calls[0];
2413
2413
  const argStr = stringifyArgs(call.arguments);
2414
- const text = argStr && textParts ? `${textParts}
2414
+ const text2 = argStr && textParts ? `${textParts}
2415
2415
  ${argStr}` : argStr || textParts;
2416
- return [{ id, role: "assistant", contentType: "tool-call", toolName: call.name, toolCallId: call.id, text }];
2416
+ return [{ id, role: "assistant", contentType: "tool-call", toolName: call.name, toolCallId: call.id, text: text2 }];
2417
2417
  }
2418
2418
  return calls.map((call) => {
2419
2419
  const argStr = stringifyArgs(call.arguments);
@@ -2427,7 +2427,9 @@ ${argStr}` : argStr || textParts;
2427
2427
  };
2428
2428
  });
2429
2429
  }
2430
- return [{ id, role: "assistant", contentType: "text", text: extractText(msg.content) }];
2430
+ const text = extractText(msg.content);
2431
+ if (!text.trim()) return [];
2432
+ return [{ id, role: "assistant", contentType: "text", text }];
2431
2433
  }
2432
2434
  const customText = extractText(msg.content) || fallbackText(msg);
2433
2435
  return customText.length > 0 ? [{ id, role: "user", contentType: "text", text: customText }] : [];
@@ -2574,7 +2576,87 @@ function peelRefTagBlocks(blocks) {
2574
2576
 
2575
2577
  // src/state.ts
2576
2578
  import { promises as fs } from "fs";
2579
+ import * as path2 from "path";
2580
+
2581
+ // src/log.ts
2582
+ import { appendFileSync, mkdirSync, statSync, renameSync, existsSync } from "fs";
2577
2583
  import * as path from "path";
2584
+ import { homedir } from "os";
2585
+ var MAX_BYTES = 10 * 1024 * 1024;
2586
+ var ENV_DEBUG = process.env.ACP_DEBUG === "1" || process.env.ACP_DEBUG === "true";
2587
+ function resolveLogFile() {
2588
+ return process.env.ACP_LOG_FILE ?? path.join(homedir(), ".pi", "acp.log");
2589
+ }
2590
+ var runtimeDebug = null;
2591
+ function setDebugEnabled(enabled) {
2592
+ runtimeDebug = enabled;
2593
+ }
2594
+ function debugOn() {
2595
+ return runtimeDebug ?? ENV_DEBUG;
2596
+ }
2597
+ function fmt(v) {
2598
+ if (typeof v === "string") return v;
2599
+ if (v instanceof Error) return v.stack || String(v);
2600
+ try {
2601
+ return JSON.stringify(v);
2602
+ } catch {
2603
+ return String(v);
2604
+ }
2605
+ }
2606
+ function ts() {
2607
+ return (/* @__PURE__ */ new Date()).toISOString();
2608
+ }
2609
+ function writeLine(level, scope, fields) {
2610
+ const file = resolveLogFile();
2611
+ try {
2612
+ if (existsSync(file) && statSync(file).size >= MAX_BYTES) {
2613
+ renameSync(file, file + ".old");
2614
+ }
2615
+ } catch {
2616
+ }
2617
+ const body = Object.keys(fields).map((k) => `${k}=${fmt(fields[k])}`).join(" ");
2618
+ const line = `${ts()} [${level}] [${scope}] ${body}
2619
+ `;
2620
+ try {
2621
+ mkdirSync(path.dirname(file), { recursive: true });
2622
+ appendFileSync(file, line);
2623
+ } catch {
2624
+ }
2625
+ }
2626
+ function closeLogStream() {
2627
+ }
2628
+ function logError(scope, fields) {
2629
+ writeLine("error", scope, fields);
2630
+ }
2631
+ function logWarn(scope, fields) {
2632
+ writeLine("warn", scope, fields);
2633
+ }
2634
+ function logInfo(scope, fields) {
2635
+ writeLine("info", scope, fields);
2636
+ }
2637
+ function logThrow(scope, err, extra = {}) {
2638
+ const fields = { ...extra };
2639
+ if (err instanceof Error) {
2640
+ fields.error = err.message;
2641
+ fields.stack = err.stack ?? "";
2642
+ } else {
2643
+ fields.error = String(err);
2644
+ }
2645
+ writeLine("error", scope, fields);
2646
+ }
2647
+ var debug = {
2648
+ get enabled() {
2649
+ return debugOn();
2650
+ },
2651
+ get logFile() {
2652
+ return resolveLogFile();
2653
+ },
2654
+ event(scope, fields) {
2655
+ if (debugOn()) writeLine("debug", scope, fields);
2656
+ }
2657
+ };
2658
+
2659
+ // src/state.ts
2578
2660
  var STATE_SUFFIX = ".acp.json";
2579
2661
  function stateFileFor(sessionFile) {
2580
2662
  if (sessionFile) return sessionFile + STATE_SUFFIX;
@@ -2592,7 +2674,11 @@ var SessionStateStore = class {
2592
2674
  const raw = await fs.readFile(file, "utf8");
2593
2675
  const parsed = JSON.parse(raw);
2594
2676
  if (parsed && Array.isArray(parsed.blocks)) state = mergeInitialState(parsed);
2595
- } catch {
2677
+ } catch (e) {
2678
+ const code = e.code;
2679
+ if (code !== "ENOENT") {
2680
+ logWarn("state", { event: "load-failed", file, error: e instanceof Error ? e.message : String(e) });
2681
+ }
2596
2682
  }
2597
2683
  }
2598
2684
  this.cache = state;
@@ -2604,12 +2690,17 @@ var SessionStateStore = class {
2604
2690
  if (!file) return;
2605
2691
  this.cache = state;
2606
2692
  this.loadedKey = file;
2607
- const dir = path.dirname(file);
2608
- await fs.mkdir(dir, { recursive: true }).catch(() => {
2693
+ const dir = path2.dirname(file);
2694
+ await fs.mkdir(dir, { recursive: true }).catch((e) => {
2695
+ logError("state", { event: "save-mkdir-failed", dir, error: e instanceof Error ? e.message : String(e) });
2609
2696
  });
2610
- const tmp = path.join(dir, `.acp-tmp-${path.basename(file)}`);
2611
- await fs.writeFile(tmp, JSON.stringify(state), "utf8");
2612
- await fs.rename(tmp, file);
2697
+ const tmp = path2.join(dir, `.acp-tmp-${path2.basename(file)}`);
2698
+ try {
2699
+ await fs.writeFile(tmp, JSON.stringify(state), "utf8");
2700
+ await fs.rename(tmp, file);
2701
+ } catch (e) {
2702
+ logError("state", { event: "save-failed", file, error: e instanceof Error ? e.message : String(e) });
2703
+ }
2613
2704
  }
2614
2705
  invalidate() {
2615
2706
  this.cache = null;
@@ -7082,64 +7173,6 @@ __export(typebox_exports, {
7082
7173
  With: () => With2
7083
7174
  });
7084
7175
 
7085
- // src/log.ts
7086
- import { promises as fs2 } from "fs";
7087
- import * as path2 from "path";
7088
- import { homedir } from "os";
7089
- var ENV_DEBUG = process.env.ACP_DEBUG === "1" || process.env.ACP_DEBUG === "true";
7090
- var LOG_FILE = process.env.ACP_LOG_FILE ?? path2.join(homedir(), ".pi", "acp-debug.log");
7091
- var runtimeDebug = null;
7092
- var initialized = false;
7093
- function setDebugEnabled(enabled) {
7094
- runtimeDebug = enabled;
7095
- }
7096
- function debugOn() {
7097
- return runtimeDebug ?? ENV_DEBUG;
7098
- }
7099
- async function write(line) {
7100
- if (!debugOn()) return;
7101
- if (!initialized) {
7102
- initialized = true;
7103
- await fs2.mkdir(path2.dirname(LOG_FILE), { recursive: true }).catch(() => {
7104
- });
7105
- }
7106
- await fs2.appendFile(LOG_FILE, line, "utf8").catch(() => {
7107
- });
7108
- }
7109
- var debug = {
7110
- get enabled() {
7111
- return debugOn();
7112
- },
7113
- get logFile() {
7114
- return LOG_FILE;
7115
- },
7116
- event(scope, fields) {
7117
- if (!debugOn()) return;
7118
- const ts = (/* @__PURE__ */ new Date()).toISOString();
7119
- const body = Object.entries(fields).map(([k, v]) => `${k}=${fmt(v)}`).join(" ");
7120
- void write(`${ts} [${scope}] ${body}
7121
- `);
7122
- }
7123
- };
7124
- function fmt(v) {
7125
- if (typeof v === "string") return v;
7126
- if (Array.isArray(v)) {
7127
- try {
7128
- return JSON.stringify(v);
7129
- } catch {
7130
- return `[${v.length}]`;
7131
- }
7132
- }
7133
- if (v && typeof v === "object") {
7134
- try {
7135
- return JSON.stringify(v);
7136
- } catch {
7137
- return String(v);
7138
- }
7139
- }
7140
- return String(v);
7141
- }
7142
-
7143
7176
  // src/tokens.ts
7144
7177
  function collectCoveredMessageIds(state) {
7145
7178
  const ids = /* @__PURE__ */ new Set();
@@ -7195,7 +7228,13 @@ function makeCompressTool(runtime) {
7195
7228
  ],
7196
7229
  parameters: CompressParams,
7197
7230
  async execute(toolCallId, params, _signal, _onUpdate, ctx) {
7198
- const result = await handleCompress(params, runtime, ctx, toolCallId);
7231
+ let result;
7232
+ try {
7233
+ result = await handleCompress(params, runtime, ctx, toolCallId);
7234
+ } catch (e) {
7235
+ logThrow("compress", e, { sid: ctx.sessionManager.getSessionId(), ranges: params.content?.length ?? 0 });
7236
+ throw e;
7237
+ }
7199
7238
  return { details: void 0, content: [{ type: "text", text: result }] };
7200
7239
  }
7201
7240
  };
@@ -7240,6 +7279,24 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
7240
7279
  activeAfter: applied.state.blocks.filter((b) => b.active).length,
7241
7280
  newBlocks: newBlocks.map((b) => ({ blockId: b.blockId, tier: b.tier, summaryLen: b.summary.length, directMsgCount: b.directMessageIds.length, effectiveMsgCount: b.effectiveMessageIds.length, summary: b.summary }))
7242
7281
  });
7282
+ logInfo("compress", {
7283
+ sid: ctx.sessionManager.getSessionId(),
7284
+ event: "applied",
7285
+ ranges: ranges.length,
7286
+ blocksCreated,
7287
+ tokensCompressed,
7288
+ beforeTokens,
7289
+ afterTokens,
7290
+ warnings: warnings.length,
7291
+ errors: errors.length,
7292
+ newBlockIds: newBlocks.map((b) => b.blockId)
7293
+ });
7294
+ if (errors.length > 0) {
7295
+ logError("compress", { sid: ctx.sessionManager.getSessionId(), event: "errors", count: errors.length, errors: errors.slice(0, 5) });
7296
+ }
7297
+ if (warnings.length > 0) {
7298
+ logError("compress", { sid: ctx.sessionManager.getSessionId(), event: "warnings", count: warnings.length, warnings: warnings.slice(0, 5) });
7299
+ }
7243
7300
  const lines = [`\u25A3 ACP | ${formatK2(beforeTokens)} \u2192 ${formatK2(afterTokens)} tokens (~${formatK2(tokensCompressed)} reclaimed, ${blocksCreated} block${blocksCreated > 1 ? "s" : ""})`];
7244
7301
  if (warnings.length > 0) lines.push("\u26A0\uFE0F " + warnings.join("; "));
7245
7302
  if (errors.length > 0) lines.push("Errors: " + errors.join("; "));
@@ -7273,7 +7330,13 @@ function makeDecompressTool(runtime) {
7273
7330
  ],
7274
7331
  parameters: DecompressParams,
7275
7332
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
7276
- const result = await handleDecompress(params, runtime, ctx);
7333
+ let result;
7334
+ try {
7335
+ result = await handleDecompress(params, runtime, ctx);
7336
+ } catch (e) {
7337
+ logThrow("decompress", e, { sid: ctx.sessionManager.getSessionId(), blockId: params.blockId });
7338
+ throw e;
7339
+ }
7277
7340
  return { details: void 0, content: [{ type: "text", text: result }] };
7278
7341
  }
7279
7342
  };
@@ -7322,16 +7385,21 @@ async function handleMessageRef(ref, ownerBlockId, args, ctx) {
7322
7385
  const wantFile = args.toFile !== void 0 || args.inline === false || text.length >= MESSAGE_INLINE_THRESHOLD;
7323
7386
  if (!wantFile) {
7324
7387
  debug.event("decompress-message", { ref, ownerBlockId, mode: "inline", chars: text.length });
7388
+ logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "message", mode: "inline", ref, ownerBlockId, chars: text.length });
7325
7389
  return `Message ${ref} (${role}, block ${ownerBlockId}, ${text.length} chars) restored inline:
7326
7390
 
7327
7391
  ${text}`;
7328
7392
  }
7329
7393
  const targetPath = args.toFile ? resolveToFilePath(args.toFile) : autoFilePath(`msg-${ref}`);
7330
- if (typeof targetPath === "object" && "error" in targetPath) return targetPath.error;
7394
+ if (typeof targetPath === "object" && "error" in targetPath) {
7395
+ logError("decompress", { sid: ctx.sessionManager.getSessionId(), event: "message-path-rejected", ref, toFile: args.toFile });
7396
+ return targetPath.error;
7397
+ }
7331
7398
  await mkdir(AUTO_DIR, { recursive: true }).catch(() => {
7332
7399
  });
7333
7400
  await writeFile(targetPath, text, "utf8");
7334
7401
  debug.event("decompress-message", { ref, ownerBlockId, mode: "file", path: targetPath, chars: text.length });
7402
+ logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "message", mode: "file", ref, ownerBlockId, path: targetPath, chars: text.length });
7335
7403
  return [
7336
7404
  `Message ${ref} (${role}, block ${ownerBlockId}, ${text.length} chars) written to ${targetPath}.`,
7337
7405
  "Block stays compressed \u2014 context unchanged. Use the read tool to access the content.",
@@ -7359,16 +7427,21 @@ async function handleDecompress(args, runtime, ctx) {
7359
7427
  if (count === 0) return `Block ${blockId} has no restorable message content.`;
7360
7428
  if (args.inline === true && !args.toFile) {
7361
7429
  debug.event("decompress", { blockId, full, count, mode: "inline" });
7430
+ logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "block", mode: "inline", blockId, full, count });
7362
7431
  return `Restored block ${blockId} (${count} item${count === 1 ? "" : "s"}) inline:
7363
7432
 
7364
7433
  ${text}`;
7365
7434
  }
7366
7435
  const targetPath = args.toFile ? resolveToFilePath(args.toFile) : autoFilePath(blockId);
7367
- if (typeof targetPath === "object" && "error" in targetPath) return targetPath.error;
7436
+ if (typeof targetPath === "object" && "error" in targetPath) {
7437
+ logError("decompress", { sid: ctx.sessionManager.getSessionId(), event: "block-path-rejected", blockId, toFile: args.toFile });
7438
+ return targetPath.error;
7439
+ }
7368
7440
  await mkdir(AUTO_DIR, { recursive: true }).catch(() => {
7369
7441
  });
7370
7442
  await writeFile(targetPath, text, "utf8");
7371
7443
  debug.event("decompress", { blockId, full, count, mode: "file", path: targetPath, chars: text.length });
7444
+ logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "block", mode: "file", blockId, full, count, path: targetPath, chars: text.length });
7372
7445
  const itemWord = count === 1 ? "item" : "items";
7373
7446
  const lines = [
7374
7447
  `Block ${blockId} (${count} ${itemWord}, ${text.length} chars) written to ${targetPath}.`,
@@ -7458,7 +7531,13 @@ function makeSearchTool(runtime) {
7458
7531
  ],
7459
7532
  parameters: SearchParams,
7460
7533
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
7461
- const result = await handleSearch(params, runtime, ctx);
7534
+ let result;
7535
+ try {
7536
+ result = await handleSearch(params, runtime, ctx);
7537
+ } catch (e) {
7538
+ logThrow("search", e, { sid: ctx.sessionManager.getSessionId(), query: params.query });
7539
+ throw e;
7540
+ }
7462
7541
  return { details: void 0, content: [{ type: "text", text: result }] };
7463
7542
  }
7464
7543
  };
@@ -7523,7 +7602,13 @@ function makeStatusTool(runtime) {
7523
7602
  ],
7524
7603
  parameters: StatusParams,
7525
7604
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
7526
- const result = await handleStatus(params, runtime, ctx);
7605
+ let result;
7606
+ try {
7607
+ result = await handleStatus(params, runtime, ctx);
7608
+ } catch (e) {
7609
+ logThrow("status", e, { sid: ctx.sessionManager.getSessionId(), scope: params.scope ?? null });
7610
+ throw e;
7611
+ }
7527
7612
  return { details: void 0, content: [{ type: "text", text: result }] };
7528
7613
  }
7529
7614
  };
@@ -8103,6 +8188,7 @@ function makeDelegateCancelTool(_pi) {
8103
8188
  run.child?.kill("SIGTERM");
8104
8189
  } catch (err) {
8105
8190
  debug.event("delegate-cancel-kill-error", { runId, error: String(err) });
8191
+ logError("delegate", { event: "cancel-kill-error", runId, error: String(err) });
8106
8192
  }
8107
8193
  delegateStatusWidget.poke();
8108
8194
  return {
@@ -8133,8 +8219,11 @@ async function runDelegate(pi, args, ctx, signal) {
8133
8219
  const requestedAsync = args.async !== false;
8134
8220
  if (requestedAsync && !isAsync) {
8135
8221
  debug.event("delegate-async-downgraded", { reason: `mode=${ctx.mode}` });
8222
+ logInfo("delegate", { event: "async-downgraded", reason: `mode=${ctx.mode}` });
8136
8223
  }
8137
- debug.event("delegate-spawn", { agent: args.agent, cwd, async: isAsync, useJsonStream, cliArgs });
8224
+ const runId = `del_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
8225
+ debug.event("delegate-spawn", { agent: args.agent, runId, cwd, async: isAsync, useJsonStream, cliArgs });
8226
+ logInfo("delegate", { event: "spawn", agent: args.agent, runId, cwd, async: isAsync, useJsonStream, mode: ctx.mode, parentDepth });
8138
8227
  const child = spawn(process.execPath, [process.argv[1], ...cliArgs], {
8139
8228
  cwd,
8140
8229
  env: childEnv,
@@ -8143,10 +8232,10 @@ async function runDelegate(pi, args, ctx, signal) {
8143
8232
  });
8144
8233
  child.stdin?.once("error", (e) => {
8145
8234
  debug.event("delegate-stdin-error", { runId: "pre-spawn", error: String(e) });
8235
+ logError("delegate", { event: "stdin-error", runId, error: String(e) });
8146
8236
  });
8147
8237
  child.stdin?.end(args.task);
8148
8238
  let stderrText = "";
8149
- const runId = `del_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
8150
8239
  const startedAt = Date.now();
8151
8240
  if (isAsync) {
8152
8241
  let settled = false;
@@ -8278,23 +8367,27 @@ async function runDelegate(pi, args, ctx, signal) {
8278
8367
  run.finishedAt = Date.now();
8279
8368
  if (run.waiter) {
8280
8369
  debug.event("delegate-done", { runId, code, status: run.status, injected: false, via: "wait", outLen: output.length, file: file2 });
8370
+ logInfo("delegate", { event: "done", runId, agent: args.agent, code, status: run.status, injected: false, via: "wait", outLen: output.length, file: file2 });
8281
8371
  run.waiter();
8282
8372
  delegateStatusWidget.poke();
8283
8373
  return;
8284
8374
  }
8285
8375
  if (run.consumed) {
8286
8376
  debug.event("delegate-done", { runId, code, status: run.status, injected: false, via: "consumed", outLen: output.length, file: file2 });
8377
+ logInfo("delegate", { event: "done", runId, agent: args.agent, code, status: run.status, injected: false, via: "consumed", outLen: output.length, file: file2 });
8287
8378
  delegateStatusWidget.poke();
8288
8379
  return;
8289
8380
  }
8290
8381
  const injected = injectResult(pi, args.agent, runId, args.task, code, file2, run.timedOut);
8291
8382
  run.injected = injected;
8292
8383
  debug.event("delegate-done", { runId, code, status: run.status, injected, outLen: output.length, file: file2 });
8384
+ logInfo("delegate", { event: "done", runId, agent: args.agent, code, status: run.status, injected, outLen: output.length, file: file2 });
8293
8385
  delegateStatusWidget.poke();
8294
8386
  } catch (err) {
8295
8387
  run.status = "failed";
8296
8388
  run.finishedAt = Date.now();
8297
8389
  debug.event("delegate-done-error", { runId, error: String(err) });
8390
+ logError("delegate", { event: "done-error", runId, agent: args.agent, error: String(err) });
8298
8391
  run.waiter?.();
8299
8392
  delegateStatusWidget.poke();
8300
8393
  }
@@ -8315,6 +8408,7 @@ async function runDelegate(pi, args, ctx, signal) {
8315
8408
  run.finishedAt = Date.now();
8316
8409
  run.result = { code: null, file: "", body: `spawn error: ${String(err)}` };
8317
8410
  debug.event("delegate-spawn-error", { runId, error: String(err) });
8411
+ logError("delegate", { event: "spawn-error", runId, agent: args.agent, error: String(err) });
8318
8412
  run.waiter?.();
8319
8413
  delegateStatusWidget.poke();
8320
8414
  }
@@ -8409,6 +8503,7 @@ function injectResult(pi, agent, runId, task, code, file, timedOut) {
8409
8503
  const send = pi.sendUserMessage;
8410
8504
  if (typeof send !== "function") {
8411
8505
  debug.event("delegate-inject-skipped", { runId, reason: "sendUserMessage unavailable" });
8506
+ logWarn("delegate", { event: "inject-skipped", runId, reason: "sendUserMessage unavailable" });
8412
8507
  return false;
8413
8508
  }
8414
8509
  const status = code === 0 ? "completed" : "failed";
@@ -8422,6 +8517,7 @@ function injectResult(pi, agent, runId, task, code, file, timedOut) {
8422
8517
  return true;
8423
8518
  } catch (err) {
8424
8519
  debug.event("delegate-inject-error", { runId, error: String(err) });
8520
+ logError("delegate", { event: "inject-error", runId, agent, error: String(err) });
8425
8521
  return false;
8426
8522
  }
8427
8523
  }
@@ -8449,6 +8545,7 @@ async function persistResult(runId, body) {
8449
8545
  return file;
8450
8546
  } catch (err) {
8451
8547
  debug.event("delegate-persist-error", { runId, file, error: String(err) });
8548
+ logError("delegate", { event: "persist-error", runId, file, error: String(err) });
8452
8549
  return "";
8453
8550
  }
8454
8551
  }
@@ -8559,7 +8656,7 @@ async function statusReport(runtime, ctx) {
8559
8656
  const activeBlocksList = state.blocks.filter((b) => b.active);
8560
8657
  const totalBlocksList = state.blocks;
8561
8658
  const lines = [];
8562
- const versionStr = "0.1.28" ? `billion-context-pi@${"0.1.28"}` : "";
8659
+ const versionStr = "0.1.29" ? `billion-context-pi@${"0.1.29"}` : "";
8563
8660
  lines.push("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E");
8564
8661
  lines.push("\u2502 ACP Context Analysis \u2502");
8565
8662
  lines.push("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F");
@@ -8803,11 +8900,13 @@ function wireToolGuardrails(pi, runtime) {
8803
8900
  if (next) {
8804
8901
  modified = next;
8805
8902
  debug.event("guardrail-output-cap", { max, hadPath: !!fullPath });
8903
+ logWarn("guardrail", { event: "output-cap", max, hadPath: !!fullPath });
8806
8904
  }
8807
8905
  }
8808
8906
  if (timeoutSecs !== void 0) {
8809
8907
  modified = appendTimeoutNotice(modified ?? event.content, timeoutSecs);
8810
8908
  debug.event("guardrail-bash-timeout-notice", { secs: timeoutSecs });
8909
+ logInfo("guardrail", { event: "bash-timeout-notice", secs: timeoutSecs });
8811
8910
  }
8812
8911
  if (modified) return { content: modified };
8813
8912
  });
@@ -8915,29 +9014,36 @@ async function checkForUpdate(autoUpdate, notify) {
8915
9014
  signal: AbortSignal.timeout(5e3),
8916
9015
  headers: { Accept: "application/json" }
8917
9016
  });
8918
- if (!res.ok) return;
9017
+ if (!res.ok) {
9018
+ logWarn("update", { event: "check-http", status: res.status });
9019
+ return;
9020
+ }
8919
9021
  const data = await res.json();
8920
9022
  const latest = data.version;
8921
9023
  if (!latest) return;
8922
- const current = runtimeVersion ?? "0.1.28";
9024
+ const current = runtimeVersion ?? "0.1.29";
9025
+ const hasUpdate = isNewer(latest, current);
8923
9026
  debug.event("update-check", {
8924
9027
  current,
8925
9028
  latest,
8926
- hasUpdate: isNewer(latest, current)
9029
+ hasUpdate
8927
9030
  });
8928
- if (isNewer(latest, current)) {
9031
+ logInfo("update", { event: "check", current, latest, hasUpdate });
9032
+ if (hasUpdate) {
8929
9033
  const installed = await autoInstallLatest(latest);
8930
9034
  if (installed && notify) {
8931
9035
  notify(
8932
9036
  `\x1B[32m\u2714 ACP auto-updated ${current} \u2192 ${latest}. Restart Pi to finish.\x1B[0m`
8933
9037
  );
9038
+ logInfo("update", { event: "auto-installed", from: current, to: latest });
8934
9039
  } else if (!installed && notify) {
8935
9040
  notify(
8936
9041
  `${PACKAGE_NAME} ${latest} available (you have ${current}). Run: pi update --extension npm:${PACKAGE_NAME}`
8937
9042
  );
8938
9043
  }
8939
9044
  }
8940
- } catch {
9045
+ } catch (e) {
9046
+ logWarn("update", { event: "check-error", error: e instanceof Error ? e.message : String(e) });
8941
9047
  } finally {
8942
9048
  updateInFlight = false;
8943
9049
  }
@@ -8951,7 +9057,7 @@ async function getRuntimeVersion() {
8951
9057
 
8952
9058
  // src/setup-subagent-tools.ts
8953
9059
  import { readFile as readFile2, writeFile as writeFile4, stat, copyFile, rename } from "fs/promises";
8954
- import { existsSync } from "fs";
9060
+ import { existsSync as existsSync2 } from "fs";
8955
9061
  import { homedir as homedir4 } from "os";
8956
9062
  import { join as join6 } from "path";
8957
9063
  var ACP_TOOLS2 = ["compress", "decompress", "search_context", "acp_status"];
@@ -9015,7 +9121,7 @@ async function ensureSubagentAcpTools(settingsPath) {
9015
9121
  return { path: path4, action: "skipped", reason: "all builtin agents already have ACP tools" };
9016
9122
  }
9017
9123
  const backupPath = `${path4}.acp-bak`;
9018
- if (!existsSync(backupPath)) {
9124
+ if (!existsSync2(backupPath)) {
9019
9125
  try {
9020
9126
  await copyFile(path4, backupPath);
9021
9127
  } catch {
@@ -9067,15 +9173,19 @@ async function runSetupAndNotify(notify) {
9067
9173
  if (result.action === "updated" && notify) {
9068
9174
  notify(`ACP: enabled context tools (compress/decompress/search_context/acp_status) for subagents`);
9069
9175
  }
9176
+ if (result.action === "failed") {
9177
+ logWarn("setup", { event: "subagent-tools", action: result.action, reason: result.reason });
9178
+ }
9070
9179
  return result;
9071
9180
  } catch (e) {
9072
9181
  debug.event("setup-subagent-tools-error", { msg: String(e) });
9182
+ logError("setup", { event: "subagent-tools-error", error: e instanceof Error ? e.message : String(e), stack: e instanceof Error ? e.stack ?? "" : "" });
9073
9183
  return { path: "", action: "failed", reason: String(e) };
9074
9184
  }
9075
9185
  }
9076
9186
 
9077
9187
  // src/user-config.ts
9078
- import { promises as fs3 } from "fs";
9188
+ import { promises as fs2 } from "fs";
9079
9189
  import * as path3 from "path";
9080
9190
  import { homedir as homedir5 } from "os";
9081
9191
  import { CONFIG_DIR_NAME } from "@earendil-works/pi-coding-agent";
@@ -9085,13 +9195,17 @@ async function loadUserConfig(cwd) {
9085
9195
  for (const base of [join8(home, CONFIG_DIR_NAME), join8(cwd, CONFIG_DIR_NAME)]) {
9086
9196
  const file = join8(base, "acp.json");
9087
9197
  try {
9088
- const raw = await fs3.readFile(file, "utf8");
9198
+ const raw = await fs2.readFile(file, "utf8");
9089
9199
  const parsed = JSON.parse(raw);
9090
9200
  if (parsed && typeof parsed === "object") {
9091
9201
  Object.assign(merged, pickKnown(parsed));
9092
9202
  debug.event("config-loaded", { file });
9093
9203
  }
9094
- } catch {
9204
+ } catch (e) {
9205
+ const code = e.code;
9206
+ if (code !== "ENOENT") {
9207
+ logWarn("config", { event: "load-failed", file, error: e instanceof Error ? e.message : String(e) });
9208
+ }
9095
9209
  }
9096
9210
  }
9097
9211
  return merged;
@@ -9145,11 +9259,14 @@ function wireSessionLifecycle(pi, runtime) {
9145
9259
  pi.on("session_start", async (_event, ctx) => {
9146
9260
  runtime.store.invalidate();
9147
9261
  runtime.clearNudgeTracking();
9262
+ const sid = ctx.sessionManager.getSessionId();
9263
+ logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.29" : null });
9148
9264
  try {
9149
9265
  const user = await loadUserConfig(ctx.cwd);
9150
9266
  runtime.setAdapter(applyUserConfig(runtime.adapter, user));
9151
9267
  if (runtime.adapter.debug !== void 0) setDebugEnabled(runtime.adapter.debug);
9152
- } catch {
9268
+ } catch (e) {
9269
+ logThrow("config", e, { sid, phase: "session_start" });
9153
9270
  }
9154
9271
  if (runtime.adapter.delegate !== false) {
9155
9272
  pi.registerTool(makeDelegateTool(pi));
@@ -9164,6 +9281,7 @@ function wireSessionLifecycle(pi, runtime) {
9164
9281
  });
9165
9282
  pi.on("session_shutdown", () => {
9166
9283
  delegateStatusWidget.dispose();
9284
+ closeLogStream();
9167
9285
  });
9168
9286
  }
9169
9287
  function wireContextTransform(pi, runtime) {
@@ -9192,6 +9310,18 @@ function wireContextTransform(pi, runtime) {
9192
9310
  });
9193
9311
  const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount });
9194
9312
  await runtime.save(turn.state, ctx);
9313
+ logInfo("turn", {
9314
+ sid,
9315
+ inMsgs: coreMessages.length,
9316
+ outMsgs: turn.messages.length,
9317
+ tokens: tokenCount,
9318
+ pct: realUsage?.percent ?? (config.modelContextLimit > 0 ? Math.round(tokenCount / config.modelContextLimit * 100) : null),
9319
+ limit: config.modelContextLimit,
9320
+ nudge: turn.nudge?.shouldInject ? turn.nudge.breakdown?.emergencyOverride === 1 ? "emergency" : "active" : "idle",
9321
+ nudgeReason: turn.nudge?.reason ?? null,
9322
+ blocks: turn.state.blocks.length,
9323
+ activeBlocks: turn.state.blocks.filter((b) => b.active).length
9324
+ });
9195
9325
  debug.event("processTurn", {
9196
9326
  outMsgs: turn.messages.length,
9197
9327
  summaryMsgs: turn.messages.filter((m) => m.id.startsWith("acp_summary")).length,
@@ -9221,6 +9351,9 @@ function wireContextTransform(pi, runtime) {
9221
9351
  const example = top ? `
9222
9352
 
9223
9353
  Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
9354
+ if (emergency) {
9355
+ logWarn("nudge", { sid: ctx.sessionManager.getSessionId(), event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
9356
+ }
9224
9357
  if (debugOn2 && ctx.hasUI) {
9225
9358
  ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
9226
9359
  ${rendered.text}${example}`);
@@ -9236,6 +9369,9 @@ ${rendered.text}${example}`);
9236
9369
  if (ctx.hasUI) ctx.ui.notify(msg);
9237
9370
  });
9238
9371
  return { messages: rebuilt };
9372
+ } catch (e) {
9373
+ logThrow("context", e, { sid, phase: "transform" });
9374
+ throw e;
9239
9375
  } finally {
9240
9376
  release();
9241
9377
  }