billion-context-omp 0.1.8 → 0.1.9
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/auto-compress.d.ts +4 -0
- package/dist/index.js +86 -31
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +4 -0
- package/dist/user-config.d.ts +4 -0
- package/package.json +2 -1
package/dist/auto-compress.d.ts
CHANGED
|
@@ -50,6 +50,10 @@ export declare function summarizeMessages(ctx: ExtensionContext, messages: Agent
|
|
|
50
50
|
customInstructions?: string;
|
|
51
51
|
signal?: AbortSignal;
|
|
52
52
|
completeFn?: typeof complete;
|
|
53
|
+
/** Fold-slot messageRefs — when provided, formatSlice renders stable
|
|
54
|
+
* mNNNNN refs instead of raw pN position ids (issue #14 Minor1: the
|
|
55
|
+
* model was shown p-ids it must never echo back). */
|
|
56
|
+
messageRefs?: CompressionState["messageRefs"];
|
|
53
57
|
}): Promise<{
|
|
54
58
|
summary: string;
|
|
55
59
|
model: string;
|
package/dist/index.js
CHANGED
|
@@ -3158,8 +3158,13 @@ function rangeFingerprints(ranges, coreMessages, byRef, blocks) {
|
|
|
3158
3158
|
}
|
|
3159
3159
|
|
|
3160
3160
|
// src/runtime.ts
|
|
3161
|
-
function freshSlot() {
|
|
3162
|
-
|
|
3161
|
+
function freshSlot(preserveFrom) {
|
|
3162
|
+
const slot = { identities: [], foldedLen: 0, preview: false, state: createInitialState(), coreMessages: [], appliedCallIds: /* @__PURE__ */ new Set(), rejectStreak: 0 };
|
|
3163
|
+
if (preserveFrom) {
|
|
3164
|
+
slot.state = { ...slot.state, nudge: preserveFrom.state.nudge };
|
|
3165
|
+
slot.rejectStreak = preserveFrom.rejectStreak;
|
|
3166
|
+
}
|
|
3167
|
+
return slot;
|
|
3163
3168
|
}
|
|
3164
3169
|
function stateHasCompressCall(state, callId) {
|
|
3165
3170
|
return state.blocks.some((b) => b.compressCallId === callId);
|
|
@@ -3205,7 +3210,7 @@ function createRuntime(adapter) {
|
|
|
3205
3210
|
let slot = slotFor(sid);
|
|
3206
3211
|
if (slot.preview) {
|
|
3207
3212
|
debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp: 0, streamLen: stream.length, reason: "preview" });
|
|
3208
|
-
slot = freshSlot();
|
|
3213
|
+
slot = freshSlot(slot);
|
|
3209
3214
|
slots.set(sid, slot);
|
|
3210
3215
|
}
|
|
3211
3216
|
const ids = stream.map(messageIdentity);
|
|
@@ -3213,7 +3218,7 @@ function createRuntime(adapter) {
|
|
|
3213
3218
|
while (lcp < Math.min(ids.length, slot.identities.length) && ids[lcp] === slot.identities[lcp]) lcp++;
|
|
3214
3219
|
if (lcp < slot.foldedLen) {
|
|
3215
3220
|
debug.event("fold-refold", { sid, foldedLen: slot.foldedLen, lcp, streamLen: ids.length });
|
|
3216
|
-
slot = freshSlot();
|
|
3221
|
+
slot = freshSlot(slot);
|
|
3217
3222
|
slots.set(sid, slot);
|
|
3218
3223
|
lcp = 0;
|
|
3219
3224
|
}
|
|
@@ -3295,6 +3300,11 @@ function createRuntime(adapter) {
|
|
|
3295
3300
|
slot.state = state;
|
|
3296
3301
|
if (toolCallId) slot.appliedCallIds.add(toolCallId);
|
|
3297
3302
|
}
|
|
3303
|
+
function noteCompressOutcome(ctx, ok) {
|
|
3304
|
+
const slot = slotFor(sidOf(ctx));
|
|
3305
|
+
slot.rejectStreak = ok ? 0 : slot.rejectStreak + 1;
|
|
3306
|
+
return slot.rejectStreak;
|
|
3307
|
+
}
|
|
3298
3308
|
return {
|
|
3299
3309
|
core,
|
|
3300
3310
|
get adapter() {
|
|
@@ -3314,6 +3324,7 @@ function createRuntime(adapter) {
|
|
|
3314
3324
|
foldStream,
|
|
3315
3325
|
stateFor,
|
|
3316
3326
|
commitFoldState,
|
|
3327
|
+
noteCompressOutcome,
|
|
3317
3328
|
forgetSession,
|
|
3318
3329
|
primeFold,
|
|
3319
3330
|
acquireLock
|
|
@@ -3444,7 +3455,7 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
3444
3455
|
const invalidRanges = rangeSpecs.filter((r) => !r.startRef || !r.endRef || typeof r.startRef !== "string" || typeof r.endRef !== "string");
|
|
3445
3456
|
if (invalidRanges.length > 0) {
|
|
3446
3457
|
logError("compress", { sid: ctx.sessionManager.getSessionId(), event: "invalid-ranges", count: invalidRanges.length, ranges: invalidRanges.map((r) => `${r.startRef}..${r.endRef}`) });
|
|
3447
|
-
return `Rejected: ${invalidRanges.length} range(s) have invalid startId or endId (missing or non-string). All ranges must have valid message refs (e.g. "m00005") or block IDs (e.g. "b3"). No changes applied \u2014 run acp_status for current refs
|
|
3458
|
+
return rejectionMessage(ctx, runtime, `Rejected: ${invalidRanges.length} range(s) have invalid startId or endId (missing or non-string). All ranges must have valid message refs (e.g. "m00005") or block IDs (e.g. "b3"). No changes applied \u2014 run acp_status for current refs.`);
|
|
3448
3459
|
}
|
|
3449
3460
|
let applied;
|
|
3450
3461
|
try {
|
|
@@ -3456,12 +3467,13 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
3456
3467
|
});
|
|
3457
3468
|
} catch (e) {
|
|
3458
3469
|
logThrow("compress", e, { sid: ctx.sessionManager.getSessionId(), phase: "applyCompression", ranges: rangeSpecs.length });
|
|
3459
|
-
return `Compression failed: ${e instanceof Error ? e.message : String(e)}. No changes applied \u2014 state is unchanged
|
|
3470
|
+
return rejectionMessage(ctx, runtime, `Compression failed: ${e instanceof Error ? e.message : String(e)}. No changes applied \u2014 state is unchanged.`);
|
|
3460
3471
|
}
|
|
3461
3472
|
if (applied.result.errors.length > 0) {
|
|
3462
3473
|
logError("compress", { sid: ctx.sessionManager.getSessionId(), event: "apply-errors", count: applied.result.errors.length, errors: applied.result.errors.slice(0, 5) });
|
|
3463
|
-
return `Compression rejected: ${applied.result.errors.join("; ")}. No changes applied \u2014 run acp_status to verify current state
|
|
3474
|
+
return rejectionMessage(ctx, runtime, `Compression rejected: ${applied.result.errors.join("; ")}. No changes applied \u2014 run acp_status to verify current state.`);
|
|
3464
3475
|
}
|
|
3476
|
+
runtime.noteCompressOutcome(ctx, true);
|
|
3465
3477
|
await runtime.commitFoldState(ctx, applied.state, toolCallId);
|
|
3466
3478
|
const { blocksCreated, tokensCompressed, warnings } = applied.result;
|
|
3467
3479
|
const afterTokens = Math.max(0, beforeTokens - tokensCompressed);
|
|
@@ -3502,6 +3514,22 @@ async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
|
3502
3514
|
releaseLock();
|
|
3503
3515
|
}
|
|
3504
3516
|
}
|
|
3517
|
+
var LOOP_GUARD_STOP = 3;
|
|
3518
|
+
var LOOP_GUARD_SUPPRESS = 4;
|
|
3519
|
+
function rejectionMessage(ctx, runtime, base) {
|
|
3520
|
+
const streak = runtime.noteCompressOutcome(ctx, false);
|
|
3521
|
+
if (streak >= LOOP_GUARD_SUPPRESS) {
|
|
3522
|
+
logWarn("compress", { sid: ctx.sessionManager.getSessionId(), event: "loop-guard", streak, mode: "suppressed" });
|
|
3523
|
+
return `Compression rejected (again \u2014 ${streak} consecutive rejections). No changes applied. STOP calling compress; it is not converging. Continue the task. Compress stays available: a fresh attempt works when acp_status shows a range that can meet the minimum size.`;
|
|
3524
|
+
}
|
|
3525
|
+
if (streak >= LOOP_GUARD_STOP) {
|
|
3526
|
+
logWarn("compress", { sid: ctx.sessionManager.getSessionId(), event: "loop-guard", streak, mode: "stop-directive" });
|
|
3527
|
+
return `${base}
|
|
3528
|
+
|
|
3529
|
+
STOP: ${streak} compress calls rejected in a row. Do NOT retry the same range. Run acp_status to see what is actually compressible now; if no range can meet the minimum size, nothing is left to compress \u2014 stop and continue the actual task.`;
|
|
3530
|
+
}
|
|
3531
|
+
return base;
|
|
3532
|
+
}
|
|
3505
3533
|
|
|
3506
3534
|
// src/decompress-tool.ts
|
|
3507
3535
|
import { type as type2 } from "@oh-my-pi/omptype";
|
|
@@ -4464,7 +4492,7 @@ async function statusReport(runtime, ctx) {
|
|
|
4464
4492
|
const coveredIds = collectCoveredMessageIds(state);
|
|
4465
4493
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
4466
4494
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
4467
|
-
const versionStr = "0.1.
|
|
4495
|
+
const versionStr = "0.1.9" ? `billion-context-omp@${"0.1.9"}` : void 0;
|
|
4468
4496
|
return buildStatusPanel({
|
|
4469
4497
|
version: versionStr,
|
|
4470
4498
|
tokenCount: sessionTokens,
|
|
@@ -4570,7 +4598,7 @@ async function summarizeMessages(ctx, messages, prompts, configuredModel, opts)
|
|
|
4570
4598
|
User instructions for this compaction: ${custom}`;
|
|
4571
4599
|
const userText = `ENTIRE conversation to compress (${slice.length} messages, ~${tokens} tokens). Compress it:
|
|
4572
4600
|
|
|
4573
|
-
` + formatSlice(slice, createInitialState());
|
|
4601
|
+
` + formatSlice(slice, opts?.messageRefs ? { ...createInitialState(), messageRefs: opts.messageRefs } : createInitialState());
|
|
4574
4602
|
const response = await run(
|
|
4575
4603
|
model,
|
|
4576
4604
|
{ systemPrompt: [instructions], messages: [{ role: "user", content: [{ type: "text", text: userText }], timestamp: Date.now() }] },
|
|
@@ -4638,6 +4666,8 @@ WHEN NOT TO COMPRESS
|
|
|
4638
4666
|
- Content the current task step is actively reading or reasoning about.
|
|
4639
4667
|
- Important user messages \u2014 preserve their exact intent, constraints, and acceptance criteria. If a message in the range must stay verbatim, exclude it from the compress range instead of compressing it.
|
|
4640
4668
|
- Protected tool outputs \u2014 hard-excluded from compression ranges, survive intact in visible context.
|
|
4669
|
+
- Nothing left worth compressing \u2014 if no compressible range can meet the minimum size, do NOT call compress at all. Compression is maintenance, never the task goal; when there is nothing to compress, do the task.
|
|
4670
|
+
- A rejected compress call \u2014 never retry a rejected range unchanged. Re-check acp_status first; after 3 rejections, stop compressing and continue the task.
|
|
4641
4671
|
|
|
4642
4672
|
${prompts.howToCompressRules}
|
|
4643
4673
|
|
|
@@ -4784,7 +4814,7 @@ var PACKAGE_NAME = "billion-context-omp";
|
|
|
4784
4814
|
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
4785
4815
|
var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/;
|
|
4786
4816
|
var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
|
|
4787
|
-
var
|
|
4817
|
+
var throttleFile = () => join4(homeDir(), CONFIG_DIR_NAME3, ".billion-context-omp-update-check");
|
|
4788
4818
|
var updateInFlight = false;
|
|
4789
4819
|
function parseVersion(v) {
|
|
4790
4820
|
return v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
@@ -4800,7 +4830,7 @@ function isNewer(latest, current) {
|
|
|
4800
4830
|
}
|
|
4801
4831
|
async function readLastCheck() {
|
|
4802
4832
|
try {
|
|
4803
|
-
const data = await readFile(
|
|
4833
|
+
const data = await readFile(throttleFile(), "utf-8");
|
|
4804
4834
|
return parseInt(data.trim(), 10) || 0;
|
|
4805
4835
|
} catch {
|
|
4806
4836
|
return 0;
|
|
@@ -4808,8 +4838,8 @@ async function readLastCheck() {
|
|
|
4808
4838
|
}
|
|
4809
4839
|
async function writeLastCheck(timestamp) {
|
|
4810
4840
|
try {
|
|
4811
|
-
await mkdir2(dirname3(
|
|
4812
|
-
await writeFile2(
|
|
4841
|
+
await mkdir2(dirname3(throttleFile()), { recursive: true });
|
|
4842
|
+
await writeFile2(throttleFile(), String(timestamp), "utf-8");
|
|
4813
4843
|
} catch {
|
|
4814
4844
|
}
|
|
4815
4845
|
}
|
|
@@ -4891,12 +4921,12 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
4891
4921
|
const now = Date.now();
|
|
4892
4922
|
const lastCheck = await readLastCheck();
|
|
4893
4923
|
if (now - lastCheck < CHECK_INTERVAL_MS) return;
|
|
4894
|
-
await writeLastCheck(now);
|
|
4895
4924
|
const runtimeVersion = await getRuntimeVersion();
|
|
4896
4925
|
const res = await fetch(REGISTRY_URL, {
|
|
4897
4926
|
signal: AbortSignal.timeout(5e3),
|
|
4898
4927
|
headers: { Accept: "application/json" }
|
|
4899
4928
|
});
|
|
4929
|
+
await writeLastCheck(now);
|
|
4900
4930
|
if (!res.ok) {
|
|
4901
4931
|
logWarn("update", { event: "check-http", status: res.status });
|
|
4902
4932
|
return;
|
|
@@ -4904,7 +4934,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
4904
4934
|
const data = await res.json();
|
|
4905
4935
|
const latest = data.version;
|
|
4906
4936
|
if (!latest) return;
|
|
4907
|
-
const current = runtimeVersion ?? "0.1.
|
|
4937
|
+
const current = runtimeVersion ?? "0.1.9";
|
|
4908
4938
|
const hasUpdate = isNewer(latest, current);
|
|
4909
4939
|
debug.event("update-check", {
|
|
4910
4940
|
current,
|
|
@@ -5181,11 +5211,13 @@ function wireCompactionDisable(pi, runtime) {
|
|
|
5181
5211
|
const prep = event.preparation;
|
|
5182
5212
|
const toSummarize = [...prep.messagesToSummarize ?? [], ...prep.turnPrefixMessages ?? []];
|
|
5183
5213
|
if (toSummarize.length === 0) return void 0;
|
|
5214
|
+
const slot = await runtime.stateFor(ctx);
|
|
5184
5215
|
ctx.ui?.notify?.(`ACP: compacting ${toSummarize.length} messages\u2026`, "info");
|
|
5185
5216
|
const result = await summarizeMessages(ctx, toSummarize, runtime.prompts, runtime.adapter.compress?.compressModel, {
|
|
5186
5217
|
previousSummary: prep.previousSummary,
|
|
5187
5218
|
customInstructions: event.customInstructions,
|
|
5188
|
-
signal: event.signal
|
|
5219
|
+
signal: event.signal,
|
|
5220
|
+
messageRefs: slot.state.messageRefs
|
|
5189
5221
|
});
|
|
5190
5222
|
if (!result) {
|
|
5191
5223
|
ctx.ui?.notify?.("ACP: compression fell back to Pi native compaction", "warning");
|
|
@@ -5216,7 +5248,7 @@ function wireCompactionDisable(pi, runtime) {
|
|
|
5216
5248
|
function wireSessionLifecycle(pi, runtime) {
|
|
5217
5249
|
pi.on("session_start", async (_event, ctx) => {
|
|
5218
5250
|
const sid = ctx.sessionManager.getSessionId();
|
|
5219
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.
|
|
5251
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.1.9" : null });
|
|
5220
5252
|
try {
|
|
5221
5253
|
const user = await loadUserConfig(ctx.cwd);
|
|
5222
5254
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
@@ -5255,6 +5287,9 @@ function wireContextTransform(pi, runtime) {
|
|
|
5255
5287
|
}
|
|
5256
5288
|
debug.event("context-in-raw", { sid, msgs: input.length });
|
|
5257
5289
|
const { state, coreMessages, originalById, streamLen } = runtime.foldStream(ctx, input);
|
|
5290
|
+
const preTurnNudgeBaseline = state.nudge.lastPerMessageNudgeTokens;
|
|
5291
|
+
const preTurnNudgeShownTokens = state.nudge.lastNudgeShownTokens;
|
|
5292
|
+
const preTurnNudgeShownByTier = state.nudge.lastShownByTier;
|
|
5258
5293
|
const config = runtime.configFor(ctx);
|
|
5259
5294
|
const coveredIds = collectCoveredMessageIds(state);
|
|
5260
5295
|
const systemPromptTokens = estimateTextTokens2(getSystemPromptText(ctx) ?? "");
|
|
@@ -5310,29 +5345,49 @@ function wireContextTransform(pi, runtime) {
|
|
|
5310
5345
|
rebuiltMsgs: rebuilt.length
|
|
5311
5346
|
});
|
|
5312
5347
|
const debugOn2 = debug.enabled;
|
|
5348
|
+
let nudgeInjected = false;
|
|
5313
5349
|
if (turn.nudge?.shouldInject) {
|
|
5314
|
-
const
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5350
|
+
const lastUser = [...input].reverse().find((m) => m.role === "user");
|
|
5351
|
+
const tailText = lastUser ? JSON.stringify(lastUser.content ?? "") : "";
|
|
5352
|
+
const isFeedbackView = tailText.includes("efficiency nudge to compress early") || tailText.includes("Context limit reached");
|
|
5353
|
+
if (isFeedbackView) {
|
|
5354
|
+
debug.event("nudge-feedback-skip", { sid: ctx.sessionManager.getSessionId(), msgs: input.length });
|
|
5355
|
+
} else {
|
|
5356
|
+
const emergency = turn.nudge.breakdown?.emergencyOverride === 1;
|
|
5357
|
+
const epochReset = turn.state.nudge.lastPerMessageNudgeTokens !== preTurnNudgeBaseline;
|
|
5358
|
+
const prevShown = epochReset ? 0 : preTurnNudgeShownTokens;
|
|
5359
|
+
const cadenceFloor = turn.nudge.breakdown?.growthFloor ?? 0;
|
|
5360
|
+
const suppressed = !emergency && prevShown > 0 && tokenCount - prevShown < cadenceFloor;
|
|
5361
|
+
if (suppressed) {
|
|
5362
|
+
turn.state.nudge.lastNudgeShownTokens = prevShown;
|
|
5363
|
+
turn.state.nudge.lastShownByTier = preTurnNudgeShownByTier;
|
|
5364
|
+
logInfo("nudge", { sid, event: "cadence-suppressed", growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
5365
|
+
debug.event("nudge-suppressed", { sid, growth: tokenCount - prevShown, floor: cadenceFloor, pct: Math.round(turn.nudge.contextUsage * 100), reason: turn.nudge.reason });
|
|
5366
|
+
} else {
|
|
5367
|
+
nudgeInjected = true;
|
|
5368
|
+
{
|
|
5369
|
+
turn.nudge.compressibleRanges = viableRanges(turn.nudge.compressibleRanges);
|
|
5370
|
+
const rendered = renderNudgeText(turn.nudge, runtime.prompts);
|
|
5371
|
+
const top = [...turn.nudge.compressibleRanges].sort((a, b) => b.tokens - a.tokens)[0];
|
|
5372
|
+
const example = top ? `
|
|
5320
5373
|
|
|
5321
5374
|
Example: compress({ content: [{ startId: "${top.startRef}", endId: "${top.endRef}", summary: "..." }] })` : "";
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5375
|
+
rebuilt.push(nudgeMessage(turn.nudge, turn.state.blocks.filter((b) => b.active), runtime.prompts, example));
|
|
5376
|
+
if (emergency) {
|
|
5377
|
+
logWarn("nudge", { sid: ctx.sessionManager.getSessionId(), event: "emergency-inject", pct: Math.round(turn.nudge.contextUsage * 100), voice: rendered.voice, compressible: turn.nudge.compressibleRanges.length });
|
|
5378
|
+
}
|
|
5379
|
+
if (debugOn2 && ctx.hasUI) {
|
|
5380
|
+
ctx.ui.notify(`[ACP nudge \u2192 context]${emergency ? " [EMERGENCY]" : ""}
|
|
5328
5381
|
${rendered.text}${example}`);
|
|
5382
|
+
}
|
|
5383
|
+
debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
5384
|
+
}
|
|
5329
5385
|
}
|
|
5330
|
-
debug.event("nudge-injected", { sid: ctx.sessionManager.getSessionId(), voice: rendered.voice, channels: ["context", debugOn2 ? "terminal" : null].filter(Boolean), emergency, text: rendered.text + example });
|
|
5331
5386
|
}
|
|
5332
5387
|
}
|
|
5333
5388
|
dumpContextMessages(rebuilt, {
|
|
5334
5389
|
sid,
|
|
5335
|
-
injected:
|
|
5390
|
+
injected: nudgeInjected,
|
|
5336
5391
|
emergency: turn.nudge?.breakdown?.emergencyOverride === 1
|
|
5337
5392
|
});
|
|
5338
5393
|
await checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|