billion-context-pi 0.1.31 → 0.1.33

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.
@@ -1,5 +1,7 @@
1
+ import { type SpawnOptions } from "node:child_process";
1
2
  import { Type, type Static } from "typebox";
2
3
  import type { ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
4
+ export declare function delegateSpawnOptions(cwd: string, env: NodeJS.ProcessEnv): SpawnOptions;
3
5
  /** Snapshot of currently-running delegate runs, for the TUI status widget. */
4
6
  export declare function runningRunsSnapshot(): {
5
7
  runId: string;
@@ -7,6 +9,34 @@ export declare function runningRunsSnapshot(): {
7
9
  task: string;
8
10
  startedAt: number;
9
11
  }[];
12
+ /** Minimal writable surface accepted by makeEventApplier — real WriteStreams
13
+ * in production, in-memory collectors in tests. */
14
+ export interface EventApplierWriters {
15
+ reply: {
16
+ write(chunk: string): void;
17
+ };
18
+ activity: {
19
+ write(chunk: string): void;
20
+ } | null;
21
+ }
22
+ export interface EventApplier {
23
+ handleEventLine(line: string): void;
24
+ getReplyText(): string;
25
+ /** omp fallback: `-p` prints the plain reply as raw stdout; append it
26
+ * straight through (no event parsing). */
27
+ appendRaw(text: string): void;
28
+ }
29
+ /** Applies parsed delegate JSON-event lines to the live reply/activity files.
30
+ * Extracted from the spawn closure so the write logic is unit-testable.
31
+ *
32
+ * reply-delta (text_delta) is streamed to the reply file as it arrives;
33
+ * reply-complete (text_end) carries the authoritative full content of the
34
+ * text block — any portion not already written is appended (tracked via
35
+ * msgWritten) so a final answer that arrives without preceding deltas is
36
+ * never lost from the file. */
37
+ export declare function makeEventApplier(opts: {
38
+ showThinking: boolean;
39
+ }, writers: EventApplierWriters): EventApplier;
10
40
  declare const DelegateParams: Type.TObject<{
11
41
  agent: Type.TString;
12
42
  task: Type.TString;
package/dist/index.js CHANGED
@@ -463,14 +463,49 @@ function truncateLargeToolOutputs(messages, tokenCount, config, countTokens, opt
463
463
  return { messages: updated, truncatedCount, savedTokens };
464
464
  }
465
465
  var KEEP_LAST_ORPHANED = 0;
466
+ function rangeKey(startRef, endRef) {
467
+ return `${startRef}::${endRef}`;
468
+ }
469
+ function rewriteCompressText(text, liveKeys) {
470
+ let parsed;
471
+ try {
472
+ parsed = JSON.parse(text ?? "");
473
+ } catch {
474
+ return null;
475
+ }
476
+ if (!parsed || typeof parsed !== "object") return null;
477
+ const obj = parsed;
478
+ const content = obj.content;
479
+ if (!Array.isArray(content) || content.length === 0) return null;
480
+ const kept = content.filter((entry) => {
481
+ if (!entry || typeof entry !== "object") return false;
482
+ const s = typeof entry.startId === "string" ? entry.startId : typeof entry.messageId === "string" ? entry.messageId : "";
483
+ const e = typeof entry.endId === "string" ? entry.endId : typeof entry.messageId === "string" ? entry.messageId : "";
484
+ return liveKeys.has(rangeKey(s, e));
485
+ });
486
+ if (kept.length === content.length || kept.length === 0) return null;
487
+ return JSON.stringify({ ...obj, content: kept });
488
+ }
466
489
  function hideConsumedCompressCalls(state, messages) {
467
- const activeCallIds = /* @__PURE__ */ new Set();
468
490
  const allBlockCallIds = /* @__PURE__ */ new Set();
491
+ const activeCallIds = /* @__PURE__ */ new Set();
492
+ const liveRangeKeysByCallId = /* @__PURE__ */ new Map();
493
+ const legacyLiveByCallId = /* @__PURE__ */ new Set();
469
494
  for (const block of state.blocks) {
470
- if (block.compressCallId) {
471
- allBlockCallIds.add(block.compressCallId);
472
- if (block.active) activeCallIds.add(block.compressCallId);
495
+ if (!block.compressCallId) continue;
496
+ allBlockCallIds.add(block.compressCallId);
497
+ if (!block.active) continue;
498
+ activeCallIds.add(block.compressCallId);
499
+ if (block.startRef === void 0 || block.endRef === void 0) {
500
+ legacyLiveByCallId.add(block.compressCallId);
501
+ continue;
502
+ }
503
+ let keys = liveRangeKeysByCallId.get(block.compressCallId);
504
+ if (!keys) {
505
+ keys = /* @__PURE__ */ new Set();
506
+ liveRangeKeysByCallId.set(block.compressCallId, keys);
473
507
  }
508
+ keys.add(rangeKey(block.startRef, block.endRef));
474
509
  }
475
510
  const lastOrphanedCallIds = [];
476
511
  for (let i = messages.length - 1; i >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; i--) {
@@ -499,6 +534,16 @@ function hideConsumedCompressCalls(state, messages) {
499
534
  hidden++;
500
535
  continue;
501
536
  }
537
+ if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
538
+ const liveKeys = liveRangeKeysByCallId.get(message.toolCallId);
539
+ if (liveKeys && liveKeys.size > 0 && !legacyLiveByCallId.has(message.toolCallId)) {
540
+ const rewritten = rewriteCompressText(message.text, liveKeys);
541
+ if (rewritten !== null) {
542
+ result.push({ ...message, text: rewritten });
543
+ continue;
544
+ }
545
+ }
546
+ }
502
547
  result.push(message);
503
548
  }
504
549
  return { messages: result, hidden };
@@ -732,8 +777,8 @@ function refNum(ref) {
732
777
  const n = parseInt(ref.slice(1), 10);
733
778
  return Number.isNaN(n) ? -1 : n;
734
779
  }
735
- function estimateMessageTokens(message) {
736
- return Math.ceil((message.text ?? "").length / 4);
780
+ function estimateTextTokens(text) {
781
+ return Math.ceil(text.length / 4);
737
782
  }
738
783
  function isToolMessage(message) {
739
784
  return message.contentType === "tool-call" || message.contentType === "tool-result";
@@ -745,7 +790,7 @@ function isSyntheticOrPruned(message, state) {
745
790
  }
746
791
  return false;
747
792
  }
748
- function computeProtectedRefs(messages, state, config) {
793
+ function computeProtectedRefs(messages, state, config, countTokens = estimateTextTokens) {
749
794
  const preserveN = config.preserveRecentMessages;
750
795
  const preserveTokens = config.preserveRecentTokens;
751
796
  const result = /* @__PURE__ */ new Set();
@@ -755,7 +800,7 @@ function computeProtectedRefs(messages, state, config) {
755
800
  if (isNeverPreserveRecent(msg)) continue;
756
801
  const ref = state.messageRefs.byRaw[msg.id];
757
802
  if (!ref || ref === "BLOCKED") continue;
758
- visible.push({ ref, tokens: estimateMessageTokens(msg) });
803
+ visible.push({ ref, tokens: countTokens(msg.text ?? "") });
759
804
  }
760
805
  if (preserveN > 0) {
761
806
  for (const m of visible.slice(-preserveN)) {
@@ -780,7 +825,7 @@ function computeProtectedRefs(messages, state, config) {
780
825
  }
781
826
  return result;
782
827
  }
783
- function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
828
+ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, countTokens = estimateTextTokens) {
784
829
  const compressibleMsgs = [];
785
830
  const protectedMsgs = [];
786
831
  const protectedCallIds = collectProtectedToolCallIds(messages, config);
@@ -793,7 +838,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
793
838
  protectedMsgs.push({
794
839
  ref,
795
840
  refNum: rn,
796
- tokens: estimateMessageTokens(msg),
841
+ tokens: countTokens(msg.text ?? ""),
797
842
  tools: msg.toolName ? [msg.toolName] : []
798
843
  });
799
844
  continue;
@@ -804,7 +849,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
804
849
  compressibleMsgs.push({
805
850
  ref,
806
851
  refNum: rn,
807
- tokens: estimateMessageTokens(msg),
852
+ tokens: countTokens(msg.text ?? ""),
808
853
  isTool: isToolMessage(msg),
809
854
  isUser: msg.role === "user"
810
855
  });
@@ -891,7 +936,7 @@ function createCore(ports = {}) {
891
936
  let tokensCompressed = 0;
892
937
  const errors = [];
893
938
  const warnings = [];
894
- const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config);
939
+ const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config, countTokens);
895
940
  const preExistingCoverage = collectCoverage(state);
896
941
  const rangeIndexSets = [];
897
942
  for (const spec of input.ranges) {
@@ -916,29 +961,25 @@ function createCore(ports = {}) {
916
961
  const bMin = b.indices.length > 0 ? Math.min(...b.indices) : Infinity;
917
962
  return aMin - bMin;
918
963
  });
919
- for (let i = 1; i < sortedRanges.length; i++) {
920
- const prev = sortedRanges[i - 1];
921
- const curr = sortedRanges[i];
922
- const prevMax = prev.indices.length > 0 ? Math.max(...prev.indices) : -1;
923
- const currMin = curr.indices.length > 0 ? Math.min(...curr.indices) : -1;
924
- if (prevMax >= currMin && prevMax >= 0) {
925
- return {
926
- state: input.state,
927
- result: {
928
- blocksCreated: 0,
929
- tokensCompressed: 0,
930
- errors: [
931
- `content: range (${prev.spec.startRef}..${prev.spec.endRef}) overlaps (${curr.spec.startRef}..${curr.spec.endRef}). Overlapping ranges cannot be compressed in the same batch.`
932
- ],
933
- warnings: []
934
- }
935
- };
964
+ const skipSpecs = /* @__PURE__ */ new Set();
965
+ let acceptedMaxIndex = -1;
966
+ for (const entry of sortedRanges) {
967
+ const entryMax = entry.indices.length > 0 ? Math.max(...entry.indices) : -1;
968
+ const entryMin = entry.indices.length > 0 ? Math.min(...entry.indices) : -1;
969
+ if (entryMin >= 0 && entryMin <= acceptedMaxIndex) {
970
+ skipSpecs.add(entry.spec);
971
+ warnings.push(
972
+ `Skipped range (${entry.spec.startRef}..${entry.spec.endRef}) \u2014 overlaps an earlier range in the batch; the earlier range takes precedence. Keep ranges disjoint.`
973
+ );
974
+ continue;
936
975
  }
976
+ if (entryMax > acceptedMaxIndex) acceptedMaxIndex = entryMax;
937
977
  }
938
978
  if (input.config.compress.minCompressRange > 0 && input.ranges.length > 0) {
939
979
  let totalRangeChars = 0;
940
980
  let hasBlockBoundaryRange = false;
941
981
  for (const spec of input.ranges) {
982
+ if (skipSpecs.has(spec)) continue;
942
983
  let resolved;
943
984
  try {
944
985
  resolved = resolveBoundaries({
@@ -974,6 +1015,7 @@ function createCore(ports = {}) {
974
1015
  }
975
1016
  }
976
1017
  for (const spec of input.ranges) {
1018
+ if (skipSpecs.has(spec)) continue;
977
1019
  try {
978
1020
  const outcome = applySingleRange({
979
1021
  spec,
@@ -1114,13 +1156,15 @@ var recommendNode = {
1114
1156
  const protectedRefs = computeProtectedRefs(
1115
1157
  io.messages,
1116
1158
  io.state,
1117
- ctx.config
1159
+ ctx.config,
1160
+ ctx.countTokens
1118
1161
  );
1119
1162
  const contextRanges = buildCompressibleRanges(
1120
1163
  io.messages,
1121
1164
  io.state,
1122
1165
  ctx.config,
1123
- protectedRefs
1166
+ protectedRefs,
1167
+ ctx.countTokens
1124
1168
  );
1125
1169
  const nothingToCompress = contextRanges.compressible.length === 0;
1126
1170
  const recommendation = {
@@ -1298,7 +1342,9 @@ function applySingleRange(input) {
1298
1342
  survivedCount: 0,
1299
1343
  generation: "young",
1300
1344
  active: true,
1301
- compressCallId: input.spec.compressCallId
1345
+ compressCallId: input.spec.compressCallId,
1346
+ startRef: input.spec.startRef,
1347
+ endRef: input.spec.endRef
1302
1348
  };
1303
1349
  input.state.blocks.push(block);
1304
1350
  for (const consumedId of consumedBlockIds) {
@@ -1789,7 +1835,7 @@ function renderNudgeText(decision) {
1789
1835
  breakdownStr,
1790
1836
  "",
1791
1837
  `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`,
1792
- isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries.`,
1838
+ isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
1793
1839
  blockList,
1794
1840
  `Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
1795
1841
  "",
@@ -2608,10 +2654,11 @@ import * as path2 from "path";
2608
2654
  import { appendFileSync, mkdirSync, statSync, renameSync, existsSync } from "fs";
2609
2655
  import * as path from "path";
2610
2656
  import { homedir } from "os";
2657
+ import { CONFIG_DIR_NAME } from "@earendil-works/pi-coding-agent";
2611
2658
  var MAX_BYTES = 10 * 1024 * 1024;
2612
2659
  var ENV_DEBUG = process.env.ACP_DEBUG === "1" || process.env.ACP_DEBUG === "true";
2613
2660
  function resolveLogFile() {
2614
- return process.env.ACP_LOG_FILE ?? path.join(homedir(), ".pi", "acp.log");
2661
+ return process.env.ACP_LOG_FILE ?? path.join(homedir(), CONFIG_DIR_NAME, "acp.log");
2615
2662
  }
2616
2663
  var runtimeDebug = null;
2617
2664
  function setDebugEnabled(enabled) {
@@ -8018,6 +8065,14 @@ var ASYNC_TIMEOUT_MS = 30 * 6e4;
8018
8065
  var KILL_GRACE_MS = 1e4;
8019
8066
  var RESULT_SUMMARY_CHARS = 500;
8020
8067
  var OUT_DIR = join4(tmpdir2(), "acp-delegate");
8068
+ function delegateSpawnOptions(cwd, env) {
8069
+ return {
8070
+ cwd,
8071
+ env,
8072
+ stdio: ["pipe", "pipe", "pipe"],
8073
+ shell: false
8074
+ };
8075
+ }
8021
8076
  var ACP_TOOLS = ["compress", "decompress", "search_context", "acp_status"];
8022
8077
  var RESTRICTED_TOOLS = "read,bash,grep,find,ls";
8023
8078
  var AGENTS = {
@@ -8064,6 +8119,69 @@ function runningRunsSnapshot() {
8064
8119
  }
8065
8120
  return out;
8066
8121
  }
8122
+ function makeEventApplier(opts, writers) {
8123
+ let replyText = "";
8124
+ let msgWritten = 0;
8125
+ const lastToolText = /* @__PURE__ */ new Map();
8126
+ const thinking = new ThinkingCollector(opts.showThinking);
8127
+ const flushThinking = () => {
8128
+ const line = thinking.flush();
8129
+ if (line) writers.activity?.write(line);
8130
+ };
8131
+ const handleEventLine = (line) => {
8132
+ const ev = parseEventLine(line);
8133
+ if (!ev) return;
8134
+ if (ev.kind === "thinking-delta") {
8135
+ thinking.push(ev.delta);
8136
+ return;
8137
+ }
8138
+ if (ev.kind === "thinking-end") {
8139
+ flushThinking();
8140
+ return;
8141
+ }
8142
+ if (ev.kind === "reply-delta") {
8143
+ flushThinking();
8144
+ replyText += ev.delta;
8145
+ msgWritten += ev.delta.length;
8146
+ writers.reply.write(ev.delta);
8147
+ return;
8148
+ }
8149
+ if (ev.kind === "reply-complete") {
8150
+ flushThinking();
8151
+ const tail = ev.content.slice(msgWritten);
8152
+ if (tail) {
8153
+ writers.reply.write(tail);
8154
+ debug.event("reply-complete-tail", { tailLen: tail.length, contentLen: ev.content.length });
8155
+ }
8156
+ if (ev.content.length < msgWritten) {
8157
+ logWarn("delegate", { event: "reply-content-shorter-than-delta", contentLen: ev.content.length, written: msgWritten });
8158
+ }
8159
+ msgWritten = 0;
8160
+ replyText = ev.content;
8161
+ return;
8162
+ }
8163
+ if (ev.kind === "tool-update") {
8164
+ flushThinking();
8165
+ const prev = lastToolText.get(ev.toolCallId) ?? "";
8166
+ const add = newPortion(ev.text, prev);
8167
+ lastToolText.set(ev.toolCallId, ev.text);
8168
+ if (add) writers.activity?.write(add.endsWith("\n") ? add : `${add}
8169
+ `);
8170
+ return;
8171
+ }
8172
+ flushThinking();
8173
+ const lines = activityLines(ev, { showThinking: opts.showThinking });
8174
+ if (lines.length) writers.activity?.write(lines.join(""));
8175
+ };
8176
+ return {
8177
+ handleEventLine,
8178
+ getReplyText: () => replyText,
8179
+ appendRaw(text) {
8180
+ replyText += text;
8181
+ writers.reply.write(text);
8182
+ }
8183
+ };
8184
+ }
8067
8185
  var WAIT_TIMEOUT_MS_DEFAULT = 1e4;
8068
8186
  var WAIT_TIMEOUT_MS_MAX = 3e5;
8069
8187
  var DelegateParams = typebox_exports.Object({
@@ -8295,12 +8413,11 @@ async function runDelegate(pi, args, ctx, signal) {
8295
8413
  const runId = `del_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
8296
8414
  debug.event("delegate-spawn", { agent: args.agent, runId, cwd, async: isAsync, useJsonStream, cliArgs });
8297
8415
  logInfo("delegate", { event: "spawn", agent: args.agent, runId, cwd, async: isAsync, useJsonStream, mode: ctx.mode, parentDepth });
8298
- const child = spawn(process.execPath, [process.argv[1], ...cliArgs], {
8299
- cwd,
8300
- env: childEnv,
8301
- stdio: ["pipe", "pipe", "pipe"],
8302
- shell: process.platform === "win32"
8303
- });
8416
+ const child = spawn(
8417
+ process.execPath,
8418
+ [process.argv[1], ...cliArgs],
8419
+ delegateSpawnOptions(cwd, childEnv)
8420
+ );
8304
8421
  child.stdin?.once("error", (e) => {
8305
8422
  debug.event("delegate-stdin-error", { runId: "pre-spawn", error: String(e) });
8306
8423
  logError("delegate", { event: "stdin-error", runId, error: String(e) });
@@ -8334,49 +8451,11 @@ async function runDelegate(pi, args, ctx, signal) {
8334
8451
  if (!s || s.destroyed || s.closed) return resolve2();
8335
8452
  s.end(() => resolve2());
8336
8453
  });
8337
- let replyText = "";
8338
8454
  let stdoutBuf = "";
8339
- const lastToolText = /* @__PURE__ */ new Map();
8340
- const thinking = new ThinkingCollector(args.showThinking === true);
8341
- const flushThinking = () => {
8342
- const line = thinking.flush();
8343
- if (line) activityStream?.write(line);
8344
- };
8345
- const handleEventLine = (line) => {
8346
- const ev = parseEventLine(line);
8347
- if (!ev) return;
8348
- if (ev.kind === "thinking-delta") {
8349
- thinking.push(ev.delta);
8350
- return;
8351
- }
8352
- if (ev.kind === "thinking-end") {
8353
- flushThinking();
8354
- return;
8355
- }
8356
- if (ev.kind === "reply-delta") {
8357
- flushThinking();
8358
- replyText += ev.delta;
8359
- replyStream.write(ev.delta);
8360
- return;
8361
- }
8362
- if (ev.kind === "reply-complete") {
8363
- flushThinking();
8364
- replyText = ev.content;
8365
- return;
8366
- }
8367
- if (ev.kind === "tool-update") {
8368
- flushThinking();
8369
- const prev = lastToolText.get(ev.toolCallId) ?? "";
8370
- const add = newPortion(ev.text, prev);
8371
- lastToolText.set(ev.toolCallId, ev.text);
8372
- if (add) activityStream?.write(add.endsWith("\n") ? add : `${add}
8373
- `);
8374
- return;
8375
- }
8376
- flushThinking();
8377
- const lines = activityLines(ev, { showThinking: args.showThinking === true });
8378
- if (lines.length) activityStream?.write(lines.join(""));
8379
- };
8455
+ const applier = makeEventApplier(
8456
+ { showThinking: args.showThinking === true },
8457
+ { reply: replyStream, activity: activityStream }
8458
+ );
8380
8459
  child.stdout?.on("data", (c) => {
8381
8460
  watchdog.poke();
8382
8461
  if (useJsonStream) {
@@ -8385,12 +8464,11 @@ async function runDelegate(pi, args, ctx, signal) {
8385
8464
  while ((nl = stdoutBuf.indexOf("\n")) >= 0) {
8386
8465
  const line = stdoutBuf.slice(0, nl);
8387
8466
  stdoutBuf = stdoutBuf.slice(nl + 1);
8388
- handleEventLine(line);
8467
+ applier.handleEventLine(line);
8389
8468
  }
8390
8469
  } else {
8391
8470
  const text = c.toString("utf8");
8392
- replyText += text;
8393
- replyStream.write(text);
8471
+ applier.appendRaw(text);
8394
8472
  }
8395
8473
  });
8396
8474
  child.stderr?.on("data", (c) => {
@@ -8415,7 +8493,7 @@ async function runDelegate(pi, args, ctx, signal) {
8415
8493
  void cleanupTmp(tmpDir);
8416
8494
  await Promise.all([endStream(replyStream), endStream(activityStream)]);
8417
8495
  run.exitCode = code;
8418
- const output = replyText.trim();
8496
+ const output = applier.getReplyText().trim();
8419
8497
  const body2 = code === 0 ? output || "(no output)" : stderrText.trim() || output || "(no output)";
8420
8498
  if (run.status === "cancelled") {
8421
8499
  await Promise.all([rm(replyFile, { force: true }), rm(activityFile, { force: true })]);
@@ -8744,7 +8822,7 @@ async function statusReport(runtime, ctx) {
8744
8822
  const activeBlocksList = state.blocks.filter((b) => b.active);
8745
8823
  const totalBlocksList = state.blocks;
8746
8824
  const lines = [];
8747
- const versionStr = "0.1.31" ? `billion-context-pi@${"0.1.31"}` : "";
8825
+ const versionStr = "0.1.33" ? `billion-context-pi@${"0.1.33"}` : "";
8748
8826
  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");
8749
8827
  lines.push("\u2502 ACP Context Analysis \u2502");
8750
8828
  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");
@@ -9006,11 +9084,12 @@ import { join as join5, dirname as dirname3 } from "path";
9006
9084
  import { fileURLToPath } from "url";
9007
9085
  import { execFile } from "child_process";
9008
9086
  import { homedir as homedir3 } from "os";
9087
+ import { CONFIG_DIR_NAME as CONFIG_DIR_NAME2 } from "@earendil-works/pi-coding-agent";
9009
9088
  var PACKAGE_NAME = "billion-context-pi";
9010
9089
  var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
9011
9090
  var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/;
9012
9091
  var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
9013
- var THROTTLE_FILE = join5(homedir3(), ".pi", "agent", ".billion-context-pi-update-check");
9092
+ var THROTTLE_FILE = join5(homedir3(), CONFIG_DIR_NAME2, "agent", ".billion-context-pi-update-check");
9014
9093
  var updateInFlight = false;
9015
9094
  function parseVersion(v) {
9016
9095
  return v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
@@ -9110,7 +9189,7 @@ async function checkForUpdate(autoUpdate, notify) {
9110
9189
  const data = await res.json();
9111
9190
  const latest = data.version;
9112
9191
  if (!latest) return;
9113
- const current = runtimeVersion ?? "0.1.31";
9192
+ const current = runtimeVersion ?? "0.1.33";
9114
9193
  const hasUpdate = isNewer(latest, current);
9115
9194
  debug.event("update-check", {
9116
9195
  current,
@@ -9149,6 +9228,7 @@ import { readFile as readFile2, writeFile as writeFile4, stat, copyFile, rename
9149
9228
  import { existsSync as existsSync2 } from "fs";
9150
9229
  import { homedir as homedir4 } from "os";
9151
9230
  import { join as join6 } from "path";
9231
+ import { CONFIG_DIR_NAME as CONFIG_DIR_NAME3 } from "@earendil-works/pi-coding-agent";
9152
9232
  var ACP_TOOLS2 = ["compress", "decompress", "search_context", "acp_status"];
9153
9233
  var BUILTIN_DEFAULT_TOOLS = {
9154
9234
  advisor: ["read", "grep", "find", "ls", "bash", "intercom"],
@@ -9165,7 +9245,7 @@ function resolveAgentDir() {
9165
9245
  const configured = process.env.PI_CODING_AGENT_DIR;
9166
9246
  if (configured === "~") return homedir4();
9167
9247
  if (configured?.startsWith("~/")) return join6(homedir4(), configured.slice(2));
9168
- return configured || join6(homedir4(), ".pi", "agent");
9248
+ return configured || join6(homedir4(), CONFIG_DIR_NAME3, "agent");
9169
9249
  }
9170
9250
  function desiredTools(existing, name) {
9171
9251
  const base = Array.isArray(existing?.tools) && existing.tools.length > 0 ? [...existing.tools] : [...BUILTIN_DEFAULT_TOOLS[name] ?? []];
@@ -9277,11 +9357,11 @@ async function runSetupAndNotify(notify) {
9277
9357
  import { promises as fs2 } from "fs";
9278
9358
  import * as path3 from "path";
9279
9359
  import { homedir as homedir5 } from "os";
9280
- import { CONFIG_DIR_NAME } from "@earendil-works/pi-coding-agent";
9360
+ import { CONFIG_DIR_NAME as CONFIG_DIR_NAME4 } from "@earendil-works/pi-coding-agent";
9281
9361
  async function loadUserConfig(cwd) {
9282
9362
  const home = homedir5();
9283
9363
  const merged = {};
9284
- for (const base of [join8(home, CONFIG_DIR_NAME), join8(cwd, CONFIG_DIR_NAME)]) {
9364
+ for (const base of [join8(home, CONFIG_DIR_NAME4), join8(cwd, CONFIG_DIR_NAME4)]) {
9285
9365
  const file = join8(base, "acp.json");
9286
9366
  try {
9287
9367
  const raw = await fs2.readFile(file, "utf8");
@@ -9349,7 +9429,7 @@ function wireSessionLifecycle(pi, runtime) {
9349
9429
  runtime.store.invalidate();
9350
9430
  runtime.clearNudgeTracking();
9351
9431
  const sid = ctx.sessionManager.getSessionId();
9352
- logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.31" : null });
9432
+ logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.33" : null });
9353
9433
  try {
9354
9434
  const user = await loadUserConfig(ctx.cwd);
9355
9435
  runtime.setAdapter(applyUserConfig(runtime.adapter, user));