billion-context-pi 0.1.66 → 0.1.67-pr.355.167

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/README.md CHANGED
@@ -24,6 +24,12 @@ The model decides <em>when</em> and <em>what</em> to compress — not a hard lim
24
24
 
25
25
  > **Host support:** this plugin is for **Pi**. It does **not** support **OMP (oh-my-pi)** — on an OMP host it refuses to run. OMP users: use [billion-context](https://github.com/ranxianglei/billion-context) instead (`bili omp`, built-in plugin). Full client → package table: see [Which do I need?](#which-do-i-need); OMP details: [docs/omp.md](./docs/omp.md).
26
26
 
27
+ ## Community
28
+
29
+ Discussion, help, and updates on QQ — one group covers all three projects (`billion-context`, `billion-context-pi`, `opencode-acp`):
30
+
31
+ **QQ Group: 1056132097**
32
+
27
33
  ## Why?
28
34
 
29
35
  When conversations get long, the model runs out of context. Most tools hard-truncate — silently dropping earlier messages. **billion-context** gives the model a `compress` tool: the LLM decides **when** and **what** to compress into high-fidelity summaries, preserving critical details (file paths, decisions, error strings) while reclaiming context space.
package/README.zh-CN.md CHANGED
@@ -22,6 +22,12 @@
22
22
 
23
23
  > **宿主支持:** 本插件面向 **Pi**。它**不支持 OMP(oh-my-pi)** —— 在 OMP 宿主上会拒绝运行。OMP 用户请直接改用 [billion-context](https://github.com/ranxianglei/billion-context)(启动命令 bili omp);其他客户端的完整对照见[该选哪个?](#该选哪个)。OMP 详细说明:[docs/omp.zh-CN.md](./docs/omp.zh-CN.md)。
24
24
 
25
+ ## 社区
26
+
27
+ 交流、求助与更新都在 QQ——同一个群覆盖三个项目(`billion-context`、`billion-context-pi`、`opencode-acp`):
28
+
29
+ **QQ 群:1056132097**
30
+
25
31
  ## 为什么选择 billion-context
26
32
 
27
33
  当对话变长,模型的上下文会耗尽。多数工具采用硬截断 —— 静默丢弃早期消息。**billion-context** 把 `compress` 工具交给模型:由 LLM 决定**何时**压缩、压缩**什么**,将内容压缩成高保真摘要,在回收上下文空间的同时保留关键细节(文件路径、决策、错误字符串)。
package/dist/index.js CHANGED
@@ -5282,8 +5282,37 @@ function createRuntime(adapter) {
5282
5282
  let adapterRef = adapter;
5283
5283
  let lastUserConfigKey;
5284
5284
  let promptsRef = defaultPrompts;
5285
- const nudgeShownTurns = /* @__PURE__ */ new Set();
5285
+ const nudgeShownTurns = /* @__PURE__ */ new Map();
5286
5286
  const nudgeShownTokens = /* @__PURE__ */ new Map();
5287
+ function markNudgeShown(sid, turnKey, tokenCount) {
5288
+ let turns = nudgeShownTurns.get(sid);
5289
+ if (!turns) {
5290
+ turns = /* @__PURE__ */ new Set();
5291
+ nudgeShownTurns.set(sid, turns);
5292
+ }
5293
+ turns.add(turnKey);
5294
+ if (tokenCount !== void 0) {
5295
+ let toks = nudgeShownTokens.get(sid);
5296
+ if (!toks) {
5297
+ toks = /* @__PURE__ */ new Map();
5298
+ nudgeShownTokens.set(sid, toks);
5299
+ }
5300
+ toks.set(turnKey, tokenCount);
5301
+ }
5302
+ }
5303
+ function nudgeShownFor(sid, turnKey) {
5304
+ return nudgeShownTurns.get(sid)?.has(turnKey) ?? false;
5305
+ }
5306
+ function nudgeShownTokensFor(sid, turnKey) {
5307
+ return nudgeShownTokens.get(sid)?.get(turnKey);
5308
+ }
5309
+ function clearNudgeTracking(sid) {
5310
+ nudgeShownTurns.delete(sid);
5311
+ nudgeShownTokens.delete(sid);
5312
+ }
5313
+ function clearNudgeTokenStamps(sid) {
5314
+ nudgeShownTokens.delete(sid);
5315
+ }
5287
5316
  const overflowEpisodes = /* @__PURE__ */ new Map();
5288
5317
  function overflowFor(sid) {
5289
5318
  let ep = overflowEpisodes.get(sid);
@@ -5333,34 +5362,40 @@ function createRuntime(adapter) {
5333
5362
  function dropTokenScale(sid) {
5334
5363
  tokenScaleStale.delete(sid);
5335
5364
  }
5336
- const compressOutcomeSeen = /* @__PURE__ */ new Set();
5337
- let compressFailTurnKey = null;
5338
- let compressFailCount = 0;
5339
- function noteCompressOutcomes(turnKey, outcomes) {
5340
- if (compressFailTurnKey !== turnKey) {
5341
- compressFailTurnKey = turnKey;
5342
- compressFailCount = 0;
5365
+ const compressOutcomes = /* @__PURE__ */ new Map();
5366
+ function compressTrackerFor(sid) {
5367
+ let t = compressOutcomes.get(sid);
5368
+ if (!t) {
5369
+ t = { seen: /* @__PURE__ */ new Set(), failTurnKey: null, failCount: 0 };
5370
+ compressOutcomes.set(sid, t);
5371
+ }
5372
+ return t;
5373
+ }
5374
+ function noteCompressOutcomes(sid, turnKey, outcomes) {
5375
+ const t = compressTrackerFor(sid);
5376
+ if (t.failTurnKey !== turnKey) {
5377
+ t.failTurnKey = turnKey;
5378
+ t.failCount = 0;
5343
5379
  }
5344
- const prevCount = compressFailCount;
5380
+ const prevCount = t.failCount;
5345
5381
  for (const o of outcomes) {
5346
- if (compressOutcomeSeen.has(o.toolCallId)) continue;
5347
- compressOutcomeSeen.add(o.toolCallId);
5382
+ if (t.seen.has(o.toolCallId)) continue;
5383
+ t.seen.add(o.toolCallId);
5348
5384
  if (o.isError || o.noop === true) {
5349
- compressFailCount += 1;
5385
+ t.failCount += 1;
5350
5386
  } else if (o.success) {
5351
- compressFailCount = 0;
5387
+ t.failCount = 0;
5352
5388
  }
5353
5389
  }
5354
- const cappedNow = compressFailCount >= MAX_COMPRESS_ATTEMPTS && prevCount < MAX_COMPRESS_ATTEMPTS;
5355
- return { count: compressFailCount, cappedNow };
5390
+ const cappedNow = t.failCount >= MAX_COMPRESS_ATTEMPTS && prevCount < MAX_COMPRESS_ATTEMPTS;
5391
+ return { count: t.failCount, cappedNow };
5356
5392
  }
5357
- function compressRetryCappedFor(turnKey) {
5358
- return compressFailTurnKey === turnKey && compressFailCount >= MAX_COMPRESS_ATTEMPTS;
5393
+ function compressRetryCappedFor(sid, turnKey) {
5394
+ const t = compressOutcomes.get(sid);
5395
+ return t !== void 0 && t.failTurnKey === turnKey && t.failCount >= MAX_COMPRESS_ATTEMPTS;
5359
5396
  }
5360
- function clearCompressRetryTracking() {
5361
- compressOutcomeSeen.clear();
5362
- compressFailTurnKey = null;
5363
- compressFailCount = 0;
5397
+ function clearCompressRetryTracking(sid) {
5398
+ compressOutcomes.delete(sid);
5364
5399
  }
5365
5400
  async function acquireLock(sid) {
5366
5401
  const prev = locks.get(sid) ?? Promise.resolve();
@@ -5461,13 +5496,7 @@ function createRuntime(adapter) {
5461
5496
  return promptsRef;
5462
5497
  }, setPrompts: (p) => {
5463
5498
  promptsRef = p;
5464
- }, markNudgeShown: (k, t) => {
5465
- nudgeShownTurns.add(k);
5466
- if (t !== void 0) nudgeShownTokens.set(k, t);
5467
- }, nudgeShownFor: (k) => nudgeShownTurns.has(k), nudgeShownTokensFor: (k) => nudgeShownTokens.get(k), clearNudgeTracking: () => {
5468
- nudgeShownTurns.clear();
5469
- nudgeShownTokens.clear();
5470
- }, clearNudgeTokenStamps: () => nudgeShownTokens.clear(), noteCompressOutcomes, compressRetryCappedFor, clearCompressRetryTracking, liveContextLimit, configFor, reasoningDropFor, reloadConfig, stateFor, save, acquireLock, overflowFor, overflowDrop, noteDeadCompress, clearDeadCompress, throttleFor, throttleDrop, noteTokenScale, dropTokenScale };
5499
+ }, markNudgeShown, nudgeShownFor, nudgeShownTokensFor, clearNudgeTracking, clearNudgeTokenStamps, noteCompressOutcomes, compressRetryCappedFor, clearCompressRetryTracking, liveContextLimit, configFor, reasoningDropFor, reloadConfig, stateFor, save, acquireLock, overflowFor, overflowDrop, noteDeadCompress, clearDeadCompress, throttleFor, throttleDrop, noteTokenScale, dropTokenScale };
5471
5500
  }
5472
5501
 
5473
5502
  // node_modules/typebox/build/system/memory/memory.mjs
@@ -10190,7 +10219,7 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
10190
10219
  });
10191
10220
  const turnKey = lastUserMessageId(entries) ?? sid;
10192
10221
  const snapshot = compressibleSnapshotText(turn.nudge);
10193
- if (runtime.compressRetryCappedFor(turnKey)) {
10222
+ if (runtime.compressRetryCappedFor(sid, turnKey)) {
10194
10223
  logWarn("compress", { sid, event: "capped-reject", turnKey });
10195
10224
  return cappedRejectionText(snapshot);
10196
10225
  }
@@ -16965,7 +16994,7 @@ async function statusReport(runtime, ctx) {
16965
16994
  const sentTokens = estimateTokens(coreMessages, coveredIds, imageTokens) + systemPromptTokens;
16966
16995
  const viewSentTokens = adjustedTokenCount(runtime.core, coreMessages, state, config, sentTokens, imageTokens, systemPromptTokens);
16967
16996
  const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: anchorStale ? viewSentTokens : Math.max(viewSentTokens, realUsage?.tokens ?? 0) });
16968
- const versionStr = "0.1.66" ? `billion-context-pi@${"0.1.66"}` : void 0;
16997
+ const versionStr = "0.1.67" ? `billion-context-pi@${"0.1.67"}` : void 0;
16969
16998
  let text = buildStatusPanel({
16970
16999
  version: versionStr,
16971
17000
  tokenCount: sessionTokens,
@@ -17663,7 +17692,7 @@ async function autoInstallLatest(latest, extDirOverride) {
17663
17692
  "--no-fund",
17664
17693
  "--no-save"
17665
17694
  ];
17666
- const prevVersion = (await readPackageJson(join11(extDir, "package.json")))?.version ?? "0.1.66";
17695
+ const prevVersion = (await readPackageJson(join11(extDir, "package.json")))?.version ?? "0.1.67";
17667
17696
  const { code, stderr } = await runNpmImpl(installArgs(latest), { cwd: npmDir, timeout: 6e4 });
17668
17697
  if (code !== 0) {
17669
17698
  logWarn("update", {
@@ -17681,7 +17710,7 @@ async function autoInstallLatest(latest, extDirOverride) {
17681
17710
  }
17682
17711
  const verify = await verifyInstall(npmDir, latest);
17683
17712
  if (!verify.ok) {
17684
- const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.66";
17713
+ const rollbackTo = SEMVER_RE.test(prevVersion) ? prevVersion : "0.1.67";
17685
17714
  logWarn("update", { event: "auto-install-verify-failed", latest, reason: verify.reason, rollbackTo });
17686
17715
  const rb = await runNpmImpl(installArgs(rollbackTo), { cwd: npmDir, timeout: 6e4 });
17687
17716
  logInfo("update", { event: "rollback", from: latest, to: rollbackTo, ok: rb.code === 0 });
@@ -17756,7 +17785,7 @@ async function checkForUpdate(autoUpdate, notify) {
17756
17785
  }
17757
17786
  const latest = await fetchLatestVersion(tag);
17758
17787
  if (!latest) return;
17759
- const current = runtimeVersion ?? "0.1.66";
17788
+ const current = runtimeVersion ?? "0.1.67";
17760
17789
  const hasUpdate = isVersionNewer(latest, current);
17761
17790
  debug.event("update-check", {
17762
17791
  current,
@@ -17926,15 +17955,15 @@ function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
17926
17955
  }
17927
17956
  if (standDownIfProxied(ctx)) return;
17928
17957
  runtime.store.invalidate();
17929
- runtime.clearNudgeTracking();
17958
+ runtime.clearNudgeTracking(ctx.sessionManager.getSessionId());
17930
17959
  runtime.throttleFor(ctx.sessionManager.getSessionId()).reset();
17931
- runtime.clearCompressRetryTracking();
17960
+ runtime.clearCompressRetryTracking(ctx.sessionManager.getSessionId());
17932
17961
  resetDelegateUsage();
17933
17962
  setDelegateDisplayUsage("separate");
17934
17963
  setDelegatePolicy(DEFAULT_DELEGATE_POLICY);
17935
17964
  const sid = ctx.sessionManager.getSessionId();
17936
17965
  const modelInfo = ctx.model;
17937
- logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.66" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
17966
+ logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.67" : null, model: modelInfo?.id ?? null, modelApi: modelInfo?.api ?? null, contextWindow: modelInfo?.contextWindow ?? null });
17938
17967
  try {
17939
17968
  await runtime.reloadConfig(ctx.cwd);
17940
17969
  const delegateCfg = resolveDelegate(runtime.adapter);
@@ -17971,8 +18000,11 @@ function wireSessionLifecycle(pi, runtime, standDownIfProxied) {
17971
18000
  delegateStatusWidget.setContext(ctx, runningRunsSnapshot);
17972
18001
  });
17973
18002
  pi.on("session_shutdown", (_event, ctx) => {
17974
- runtime.clearDeadCompress(ctx.sessionManager.getSessionId());
17975
- runtime.dropTokenScale(ctx.sessionManager.getSessionId());
18003
+ const sid = ctx.sessionManager.getSessionId();
18004
+ runtime.clearDeadCompress(sid);
18005
+ runtime.dropTokenScale(sid);
18006
+ runtime.clearNudgeTracking(sid);
18007
+ runtime.clearCompressRetryTracking(sid);
17976
18008
  delegateStatusWidget.dispose();
17977
18009
  closeLogStream();
17978
18010
  });
@@ -18029,7 +18061,7 @@ function wireContextTransform(pi, runtime, standDownIfProxied) {
18029
18061
  state.nudge.lastNudgeShownTokens = 0;
18030
18062
  state.nudge.lastPerMessageNudgeTokens = 0;
18031
18063
  state.nudge.lastShownByTier = {};
18032
- runtime.clearNudgeTokenStamps();
18064
+ runtime.clearNudgeTokenStamps(sid);
18033
18065
  logInfo("growth-scale", { sid, event: "scale-flip-reset", anchorStale: !hostFloorActive });
18034
18066
  }
18035
18067
  debug.event("context-in", {
@@ -18111,24 +18143,24 @@ function wireContextTransform(pi, runtime, standDownIfProxied) {
18111
18143
  const debugOn2 = debug.enabled;
18112
18144
  const turnKey = lastUserMessageId(entries) ?? sid;
18113
18145
  const compressOutcomes = collectCompressOutcomes(entries, turnStartIndex(entries));
18114
- const outcome = compressOutcomes.length > 0 ? runtime.noteCompressOutcomes(turnKey, compressOutcomes) : null;
18146
+ const outcome = compressOutcomes.length > 0 ? runtime.noteCompressOutcomes(sid, turnKey, compressOutcomes) : null;
18115
18147
  const adaptiveGrowth = !config.modelContextLimit || config.modelContextLimit <= 0 ? config.nudge.growthFloor : Math.min(
18116
18148
  config.nudge.growthCap,
18117
18149
  Math.max(config.nudge.growthFloor, Math.round(config.modelContextLimit * config.nudge.growthRatio))
18118
18150
  );
18119
18151
  const reInjectFloor = Math.max(config.nudge.minGrowthFloor, config.nudge.minGrowthRatio * adaptiveGrowth);
18120
- let shownAt = runtime.nudgeShownTokensFor(turnKey);
18152
+ let shownAt = runtime.nudgeShownTokensFor(sid, turnKey);
18121
18153
  if (shownAt !== void 0 && tokenCount < shownAt - adaptiveGrowth) {
18122
18154
  logInfo("nudge", { sid: ctx.sessionManager.getSessionId(), event: "drop-reanchor", turnKey, from: shownAt, to: tokenCount });
18123
18155
  shownAt = tokenCount;
18124
- runtime.markNudgeShown(turnKey, tokenCount);
18156
+ runtime.markNudgeShown(sid, turnKey, tokenCount);
18125
18157
  }
18126
18158
  if (turn.nudge?.shouldInject) {
18127
18159
  const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
18128
18160
  turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
18129
- const retryCapped = runtime.compressRetryCappedFor(turnKey);
18161
+ const retryCapped = runtime.compressRetryCappedFor(sid, turnKey);
18130
18162
  const reInjectReady = shownAt === void 0 || tokenCount - shownAt >= reInjectFloor;
18131
- const alreadyShown = retryCapped || !emergency && runtime.nudgeShownFor(turnKey) && !reInjectReady;
18163
+ const alreadyShown = retryCapped || !emergency && runtime.nudgeShownFor(sid, turnKey) && !reInjectReady;
18132
18164
  if (!alreadyShown) {
18133
18165
  rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b2) => b2.active), runtime.prompts));
18134
18166
  const rendered = renderNudgeText(turn.nudge, runtime.prompts);
@@ -18143,7 +18175,7 @@ Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef
18143
18175
  ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
18144
18176
  ${rendered.text}${example}`);
18145
18177
  }
18146
- if (!emergency) runtime.markNudgeShown(turnKey, tokenCount);
18178
+ if (!emergency) runtime.markNudgeShown(sid, turnKey, tokenCount);
18147
18179
  debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, turnKey, reInject: shownAt !== void 0, text: rendered.text + example });
18148
18180
  } else {
18149
18181
  debug.event("nudge-suppressed", { sid: ctx.sessionManager.getSessionId(), turnKey, reason: turn.nudge.reason, shownAt: shownAt ?? null, tokenCount, adaptiveGrowth, reInjectFloor });