billion-context-omp 0.2.0 → 0.2.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.js CHANGED
@@ -3166,6 +3166,15 @@ function freshSlot(preserveFrom) {
3166
3166
  }
3167
3167
  return slot;
3168
3168
  }
3169
+ function isViewFlip(foldedLen, lcp) {
3170
+ return foldedLen > 0 && lcp >= Math.floor(foldedLen / 2);
3171
+ }
3172
+ function preserveCompressedSlot(prev) {
3173
+ const slot = freshSlot(prev);
3174
+ slot.state = { ...slot.state, blocks: prev.state.blocks, messageRefs: prev.state.messageRefs, stats: prev.state.stats };
3175
+ slot.appliedCallIds = new Set(prev.appliedCallIds);
3176
+ return slot;
3177
+ }
3169
3178
  function stateHasCompressCall(state, callId) {
3170
3179
  return state.blocks.some((b) => b.compressCallId === callId);
3171
3180
  }
@@ -3217,8 +3226,9 @@ function createRuntime(adapter) {
3217
3226
  let lcp = 0;
3218
3227
  while (lcp < Math.min(ids.length, slot.identities.length) && ids[lcp] === slot.identities[lcp]) lcp++;
3219
3228
  if (lcp < slot.foldedLen) {
3220
- debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp, streamLen: ids.length });
3221
- slot = freshSlot(slot);
3229
+ const flip = isViewFlip(slot.foldedLen, lcp);
3230
+ debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp, streamLen: ids.length, flip });
3231
+ slot = flip ? preserveCompressedSlot(slot) : freshSlot(slot);
3222
3232
  slots.set(sid, slot);
3223
3233
  lcp = 0;
3224
3234
  }
@@ -3244,6 +3254,7 @@ function createRuntime(adapter) {
3244
3254
  debug.event("fold-replay-skipped", { sid, callId: call.id });
3245
3255
  continue;
3246
3256
  }
3257
+ if (slot.appliedCallIds.has(call.id) || stateHasCompressCall(slot.state, call.id)) continue;
3247
3258
  const stale = call.ranges.map((r, ri) => staleRange(r, ri, resultText, coreMessages, i, slot.state.messageRefs.byRef, slot.state.blocks)).find((s) => s !== false);
3248
3259
  if (stale) {
3249
3260
  debug.event("fold-replay-stale", { sid, callId: call.id, reason: stale });
@@ -4497,7 +4508,7 @@ async function statusReport(runtime, ctx) {
4497
4508
  const coveredIds = collectCoveredMessageIds(state);
4498
4509
  const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
4499
4510
  const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
4500
- const versionStr = "0.2.0" ? `billion-context-omp@${"0.2.0"}` : void 0;
4511
+ const versionStr = "0.2.2" ? `billion-context-omp@${"0.2.2"}` : void 0;
4501
4512
  return buildStatusPanel({
4502
4513
  version: versionStr,
4503
4514
  tokenCount: sessionTokens,
@@ -5040,9 +5051,44 @@ function wireToolGuardrails(pi, runtime) {
5040
5051
  });
5041
5052
  }
5042
5053
 
5054
+ // src/instance-guard.ts
5055
+ import { readFileSync as readFileSync2, writeFileSync } from "fs";
5056
+ import { join as join4 } from "path";
5057
+ var MARKER_FILE = ".billion-context-omp-instance.json";
5058
+ var FRESH_MS = 6e4;
5059
+ function markerPath() {
5060
+ return join4(homeDir(), ".omp", MARKER_FILE);
5061
+ }
5062
+ function readMarker() {
5063
+ try {
5064
+ const raw = JSON.parse(readFileSync2(markerPath(), "utf8"));
5065
+ if (typeof raw?.path === "string" && typeof raw?.ts === "number") return raw;
5066
+ } catch {
5067
+ }
5068
+ return void 0;
5069
+ }
5070
+ function detectDualInstance(selfPath, now = Date.now()) {
5071
+ const m = readMarker();
5072
+ if (!m) return void 0;
5073
+ if (m.path === selfPath) return void 0;
5074
+ if (now - m.ts > FRESH_MS) return void 0;
5075
+ return m;
5076
+ }
5077
+ function stampInstance(selfPath, version, pid = process.pid, now = Date.now()) {
5078
+ try {
5079
+ writeFileSync(markerPath(), JSON.stringify({ path: selfPath, version, pid, ts: now }));
5080
+ } catch {
5081
+ }
5082
+ }
5083
+ function stampAndDetect(selfPath, version, now = Date.now()) {
5084
+ const conflict = detectDualInstance(selfPath, now);
5085
+ stampInstance(selfPath, version, void 0, now);
5086
+ return conflict;
5087
+ }
5088
+
5043
5089
  // src/update.ts
5044
5090
  import { readFile, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
5045
- import { join as join4, dirname as dirname3 } from "path";
5091
+ import { join as join5, dirname as dirname3 } from "path";
5046
5092
  import { fileURLToPath } from "url";
5047
5093
  import { execFile } from "child_process";
5048
5094
  import { CONFIG_DIR_NAME as CONFIG_DIR_NAME3 } from "@oh-my-pi/pi-utils";
@@ -5050,7 +5096,7 @@ var PACKAGE_NAME = "billion-context-omp";
5050
5096
  var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
5051
5097
  var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/;
5052
5098
  var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
5053
- var throttleFile = () => join4(homeDir(), CONFIG_DIR_NAME3, ".billion-context-omp-update-check");
5099
+ var throttleFile = () => join5(homeDir(), CONFIG_DIR_NAME3, ".billion-context-omp-update-check");
5054
5100
  var updateInFlight = false;
5055
5101
  function parseVersion(v) {
5056
5102
  return v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
@@ -5100,7 +5146,7 @@ function findNpmRoot(extDir) {
5100
5146
  async function findExtensionDir() {
5101
5147
  let dir = dirname3(fileURLToPath(import.meta.url));
5102
5148
  for (; ; ) {
5103
- const pkg = await readPackageJson(join4(dir, "package.json"));
5149
+ const pkg = await readPackageJson(join5(dir, "package.json"));
5104
5150
  if (pkg?.name === PACKAGE_NAME) return dir;
5105
5151
  const parent = dirname3(dir);
5106
5152
  if (parent === dir) return void 0;
@@ -5170,7 +5216,7 @@ async function checkForUpdate(autoUpdate, notify) {
5170
5216
  const data = await res.json();
5171
5217
  const latest = data.version;
5172
5218
  if (!latest) return;
5173
- const current = runtimeVersion ?? "0.2.0";
5219
+ const current = runtimeVersion ?? "0.2.2";
5174
5220
  const hasUpdate = isNewer(latest, current);
5175
5221
  debug.event("update-check", {
5176
5222
  current,
@@ -5200,12 +5246,12 @@ async function checkForUpdate(autoUpdate, notify) {
5200
5246
  async function getRuntimeVersion() {
5201
5247
  const extDir = await findExtensionDir();
5202
5248
  if (!extDir) return void 0;
5203
- const pkg = await readPackageJson(join4(extDir, "package.json"));
5249
+ const pkg = await readPackageJson(join5(extDir, "package.json"));
5204
5250
  return pkg?.version;
5205
5251
  }
5206
5252
 
5207
5253
  // src/dump.ts
5208
- import { mkdirSync as mkdirSync2, writeFileSync, readdirSync, unlinkSync } from "fs";
5254
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readdirSync, unlinkSync } from "fs";
5209
5255
  import * as path2 from "path";
5210
5256
  import { CONFIG_DIR_NAME as CONFIG_DIR_NAME4 } from "@oh-my-pi/pi-utils";
5211
5257
  var counters = {};
@@ -5250,7 +5296,7 @@ function dumpContextMessages(messages, meta) {
5250
5296
  counters[dir] = seq + 1;
5251
5297
  const name = `${String(seq).padStart(4, "0")}.json`;
5252
5298
  const fullPath = path2.join(dir, name);
5253
- writeFileSync(
5299
+ writeFileSync2(
5254
5300
  fullPath,
5255
5301
  JSON.stringify({
5256
5302
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -5334,7 +5380,7 @@ function dumpProviderRequest(payload, meta) {
5334
5380
  const name = `req_${String(seq).padStart(4, "0")}.json`;
5335
5381
  const fullPath = path2.join(dir, name);
5336
5382
  const summary = summarizeProviderPayload(payload);
5337
- writeFileSync(
5383
+ writeFileSync2(
5338
5384
  fullPath,
5339
5385
  JSON.stringify({
5340
5386
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -5359,8 +5405,8 @@ import { CONFIG_DIR_NAME as CONFIG_DIR_NAME5 } from "@oh-my-pi/pi-utils";
5359
5405
  async function loadUserConfig(cwd) {
5360
5406
  const home = homeDir();
5361
5407
  const merged = {};
5362
- for (const base of [join7(home, CONFIG_DIR_NAME5), join7(cwd, CONFIG_DIR_NAME5)]) {
5363
- const file = join7(base, "acp-omp.json");
5408
+ for (const base of [join8(home, CONFIG_DIR_NAME5), join8(cwd, CONFIG_DIR_NAME5)]) {
5409
+ const file = join8(base, "acp-omp.json");
5364
5410
  const allowPrompts = base.startsWith(home);
5365
5411
  try {
5366
5412
  const raw = await fs.readFile(file, "utf8");
@@ -5378,7 +5424,7 @@ async function loadUserConfig(cwd) {
5378
5424
  }
5379
5425
  return merged;
5380
5426
  }
5381
- function join7(...parts) {
5427
+ function join8(...parts) {
5382
5428
  return path3.join(...parts);
5383
5429
  }
5384
5430
  var KNOWN = /* @__PURE__ */ new Set([
@@ -5486,7 +5532,18 @@ function wireCompactionDisable(pi, runtime) {
5486
5532
  function wireSessionLifecycle(pi, runtime) {
5487
5533
  pi.on("session_start", async (_event, ctx) => {
5488
5534
  const sid = ctx.sessionManager.getSessionId();
5489
- logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.0" : null });
5535
+ logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.2" : null });
5536
+ const selfPath = import.meta.url;
5537
+ const conflict = stampAndDetect(selfPath, true ? "0.2.2" : null);
5538
+ if (conflict) {
5539
+ logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
5540
+ try {
5541
+ if (ctx.hasUI) {
5542
+ ctx.ui.notify(`\u26A0 billion-context-omp loaded TWICE (also from ${conflict.path}). Two instances corrupt compression state \u2014 remove one (check 'omp plugin list' vs config.yml extensions).`);
5543
+ }
5544
+ } catch {
5545
+ }
5546
+ }
5490
5547
  try {
5491
5548
  const user = await loadUserConfig(ctx.cwd);
5492
5549
  runtime.setAdapter(applyUserConfig(runtime.adapter, user));