billion-context-omp 0.2.1 → 0.2.3
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/decompress-tool.d.ts +1 -0
- package/dist/index.js +135 -33
- package/dist/index.js.map +1 -1
- package/dist/instance-guard.d.ts +13 -0
- package/dist/runtime.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ omp's built-in `/compact` is intercepted and replaced by an ACP model-summarized
|
|
|
77
77
|
|------|-------------|
|
|
78
78
|
| `compress` | Replace a contiguous message range with a detailed summary |
|
|
79
79
|
| `decompress` | Restore a previously compressed block's content (to file by default; `inline:true` for single messages) |
|
|
80
|
-
| `search_context` | Search compressed block summaries
|
|
80
|
+
| `search_context` | Search compressed block summaries and the original messages folded into them by keyword (visible messages are not indexed) |
|
|
81
81
|
| `acp_status` | Show context usage, compressed blocks, compressible ranges |
|
|
82
82
|
|
|
83
83
|
> The `acp_delegate` sub-agent subsystem from the Pi build is intentionally **not** registered — omp ships its own multi-agent orchestration, and duplicate delegation tools would conflict.
|
package/README.zh-CN.md
CHANGED
|
@@ -77,7 +77,7 @@ omp 内置的 `/compact` 被拦截,替换为 ACP 模型摘要式 compaction,
|
|
|
77
77
|
|------|------|
|
|
78
78
|
| `compress` | 把一段连续消息区间替换为详细摘要 |
|
|
79
79
|
| `decompress` | 恢复之前压缩的块内容(默认写文件;单条消息可 `inline:true`) |
|
|
80
|
-
| `search_context` |
|
|
80
|
+
| `search_context` | 按关键字搜索压缩块摘要及被折叠其中的原始消息(可见消息不建索引) |
|
|
81
81
|
| `acp_status` | 显示上下文用量、压缩块、可压缩区间 |
|
|
82
82
|
|
|
83
83
|
> Pi 版中的 `acp_delegate` 子代理系统在此**有意不注册**——omp 自带多代理编排,重复的委派工具会冲突。
|
|
@@ -12,4 +12,5 @@ declare const DecompressParams: import("@oh-my-pi/omptype").FluentType<{
|
|
|
12
12
|
toFile?: string | undefined;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function makeDecompressTool(runtime: AcpRuntime): ToolDefinition<typeof DecompressParams>;
|
|
15
|
+
export declare function pruneAutoFiles(): void;
|
|
15
16
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -2769,8 +2769,12 @@ function resolveConfig(adapter, liveContextLimit) {
|
|
|
2769
2769
|
return config;
|
|
2770
2770
|
}
|
|
2771
2771
|
function parsePercent(v) {
|
|
2772
|
-
const
|
|
2773
|
-
if (!Number.isFinite(
|
|
2772
|
+
const raw = typeof v === "number" ? v : v.trim().endsWith("%") ? Number(v.trim().slice(0, -1)) : Number(v);
|
|
2773
|
+
if (!Number.isFinite(raw)) return 0;
|
|
2774
|
+
const n = raw > 1 ? raw / 100 : raw;
|
|
2775
|
+
if (n > 1 || n < 0) {
|
|
2776
|
+
logWarn("config", { event: "percent-clamped", value: String(v), clamped: Math.min(1, Math.max(0, n)) });
|
|
2777
|
+
}
|
|
2774
2778
|
return Math.min(1, Math.max(0, n));
|
|
2775
2779
|
}
|
|
2776
2780
|
|
|
@@ -3159,7 +3163,7 @@ function rangeFingerprints(ranges, coreMessages, byRef, blocks) {
|
|
|
3159
3163
|
|
|
3160
3164
|
// src/runtime.ts
|
|
3161
3165
|
function freshSlot(preserveFrom) {
|
|
3162
|
-
const slot = { identities: [], foldedLen: 0, preview: false, state: createInitialState(), coreMessages: [], appliedCallIds: /* @__PURE__ */ new Set(), rejectStreak: 0 };
|
|
3166
|
+
const slot = { identities: [], foldedLen: 0, preview: false, state: createInitialState(), coreMessages: [], appliedCallIds: /* @__PURE__ */ new Set(), rejectStreak: 0, lastRebuiltOutput: null };
|
|
3163
3167
|
if (preserveFrom) {
|
|
3164
3168
|
slot.state = { ...slot.state, nudge: preserveFrom.state.nudge };
|
|
3165
3169
|
slot.rejectStreak = preserveFrom.rejectStreak;
|
|
@@ -3223,6 +3227,17 @@ function createRuntime(adapter) {
|
|
|
3223
3227
|
slots.set(sid, slot);
|
|
3224
3228
|
}
|
|
3225
3229
|
const ids = stream.map(messageIdentity);
|
|
3230
|
+
const lastOut = slot.lastRebuiltOutput;
|
|
3231
|
+
if (lastOut !== null && ids.length === lastOut.length && ids.every((id, i) => id === lastOut[i])) {
|
|
3232
|
+
const coreMessages2 = streamToCoreMessages(stream);
|
|
3233
|
+
const originalById2 = /* @__PURE__ */ new Map();
|
|
3234
|
+
stream.forEach((message, i) => originalById2.set(`p${i + 1}`, message));
|
|
3235
|
+
slot.identities = ids;
|
|
3236
|
+
slot.foldedLen = ids.length;
|
|
3237
|
+
slot.coreMessages = coreMessages2;
|
|
3238
|
+
debug.event("feedback-reuse", { sid, msgs: ids.length, blocks: slot.state.blocks.length });
|
|
3239
|
+
return { state: slot.state, coreMessages: coreMessages2, originalById: originalById2, streamLen: ids.length };
|
|
3240
|
+
}
|
|
3226
3241
|
let lcp = 0;
|
|
3227
3242
|
while (lcp < Math.min(ids.length, slot.identities.length) && ids[lcp] === slot.identities[lcp]) lcp++;
|
|
3228
3243
|
if (lcp < slot.foldedLen) {
|
|
@@ -3311,6 +3326,10 @@ function createRuntime(adapter) {
|
|
|
3311
3326
|
slot.state = state;
|
|
3312
3327
|
if (toolCallId) slot.appliedCallIds.add(toolCallId);
|
|
3313
3328
|
}
|
|
3329
|
+
function recordRebuiltOutput(ctx, rebuilt) {
|
|
3330
|
+
const slot = slotFor(sidOf(ctx));
|
|
3331
|
+
slot.lastRebuiltOutput = rebuilt.map(messageIdentity);
|
|
3332
|
+
}
|
|
3314
3333
|
function noteCompressOutcome(ctx, ok) {
|
|
3315
3334
|
const slot = slotFor(sidOf(ctx));
|
|
3316
3335
|
slot.rejectStreak = ok ? 0 : slot.rejectStreak + 1;
|
|
@@ -3335,6 +3354,7 @@ function createRuntime(adapter) {
|
|
|
3335
3354
|
foldStream,
|
|
3336
3355
|
stateFor,
|
|
3337
3356
|
commitFoldState,
|
|
3357
|
+
recordRebuiltOutput,
|
|
3338
3358
|
noteCompressOutcome,
|
|
3339
3359
|
forgetSession,
|
|
3340
3360
|
primeFold,
|
|
@@ -3435,11 +3455,13 @@ function makeCompressTool(runtime) {
|
|
|
3435
3455
|
}
|
|
3436
3456
|
async function handleCompress(args, runtime, ctx, toolCallId) {
|
|
3437
3457
|
const ranges = args.content ?? [];
|
|
3438
|
-
if (ranges.length === 0)
|
|
3458
|
+
if (ranges.length === 0) {
|
|
3459
|
+
return rejectionMessage(ctx, runtime, "No ranges provided. No changes applied \u2014 send content: [{ startId, endId, summary }].");
|
|
3460
|
+
}
|
|
3439
3461
|
const invalid = ranges.filter((r) => !r || typeof r.summary !== "string" || !r.summary.trim() || !r.startId || !r.endId);
|
|
3440
3462
|
if (invalid.length > 0) {
|
|
3441
3463
|
logWarn("compress", { sid: ctx.sessionManager.getSessionId(), event: "invalid-ranges", count: invalid.length });
|
|
3442
|
-
return `Every range needs startId, endId and a summary (min 50 chars) \u2014 got ${invalid.length} range(s) missing fields. Re-send the FULL ranges array with summaries included
|
|
3464
|
+
return rejectionMessage(ctx, runtime, `Every range needs startId, endId and a summary (min 50 chars) \u2014 got ${invalid.length} range(s) missing fields. Re-send the FULL ranges array with summaries included. No changes applied \u2014 run acp_status for current refs.`);
|
|
3443
3465
|
}
|
|
3444
3466
|
const releaseLock = await runtime.acquireLock(ctx.sessionManager.getSessionId());
|
|
3445
3467
|
try {
|
|
@@ -3550,10 +3572,12 @@ STOP: ${streak} compress calls rejected in a row. Do NOT retry the same range. R
|
|
|
3550
3572
|
// src/decompress-tool.ts
|
|
3551
3573
|
import { type as type2 } from "@oh-my-pi/omptype";
|
|
3552
3574
|
import { writeFile, mkdir } from "fs/promises";
|
|
3553
|
-
import { realpathSync } from "fs";
|
|
3575
|
+
import { realpathSync, readdirSync, statSync as statSync2, unlinkSync } from "fs";
|
|
3554
3576
|
import { resolve, relative, isAbsolute, join as join2, dirname as dirname2, basename } from "path";
|
|
3555
3577
|
import { tmpdir } from "os";
|
|
3556
|
-
|
|
3578
|
+
function autoDir() {
|
|
3579
|
+
return join2(homeDir() || tmpdir(), ".cache", "omp", "acp-decompress");
|
|
3580
|
+
}
|
|
3557
3581
|
var PREVIEW_CHARS = 600;
|
|
3558
3582
|
var MESSAGE_INLINE_THRESHOLD = 2e3;
|
|
3559
3583
|
var DecompressParams = type2({
|
|
@@ -3618,7 +3642,31 @@ function resolveToFilePath(targetPath) {
|
|
|
3618
3642
|
return resolved;
|
|
3619
3643
|
}
|
|
3620
3644
|
function autoFilePath(blockId) {
|
|
3621
|
-
return join2(
|
|
3645
|
+
return join2(autoDir(), `${blockId}-${Date.now()}.txt`);
|
|
3646
|
+
}
|
|
3647
|
+
var MAX_AUTO_FILES = 100;
|
|
3648
|
+
function pruneAutoFiles() {
|
|
3649
|
+
const dir = autoDir();
|
|
3650
|
+
try {
|
|
3651
|
+
const files = readdirSync(dir).map((f) => {
|
|
3652
|
+
try {
|
|
3653
|
+
return { f, m: statSync2(join2(dir, f)).mtimeMs };
|
|
3654
|
+
} catch {
|
|
3655
|
+
return null;
|
|
3656
|
+
}
|
|
3657
|
+
}).filter((x) => x !== null);
|
|
3658
|
+
if (files.length <= MAX_AUTO_FILES) return;
|
|
3659
|
+
files.sort((a, b) => a.m - b.m);
|
|
3660
|
+
const excess = files.slice(0, files.length - MAX_AUTO_FILES);
|
|
3661
|
+
for (const { f } of excess) {
|
|
3662
|
+
try {
|
|
3663
|
+
unlinkSync(join2(dir, f));
|
|
3664
|
+
} catch {
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
debug.event("decompress-pruned", { dir, removed: excess.length });
|
|
3668
|
+
} catch {
|
|
3669
|
+
}
|
|
3622
3670
|
}
|
|
3623
3671
|
function headPreview(text) {
|
|
3624
3672
|
if (text.length <= PREVIEW_CHARS) return text;
|
|
@@ -3652,8 +3700,9 @@ ${text}`;
|
|
|
3652
3700
|
logError("decompress", { sid: ctx.sessionManager.getSessionId(), event: "message-path-rejected", ref, toFile: args.toFile });
|
|
3653
3701
|
return targetPath.error;
|
|
3654
3702
|
}
|
|
3655
|
-
await mkdir(
|
|
3703
|
+
await mkdir(autoDir(), { recursive: true }).catch((e) => logError("decompress", { event: "mkdir-failed", dir: autoDir(), error: e instanceof Error ? e.message : String(e) }));
|
|
3656
3704
|
await writeFile(targetPath, text, "utf8");
|
|
3705
|
+
if (!args.toFile) pruneAutoFiles();
|
|
3657
3706
|
debug.event("decompress-message", { ref, ownerBlockId, mode: "file", path: targetPath, chars: text.length });
|
|
3658
3707
|
logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "message", mode: "file", ref, ownerBlockId, path: targetPath, chars: text.length });
|
|
3659
3708
|
return [
|
|
@@ -3695,8 +3744,9 @@ ${text}`;
|
|
|
3695
3744
|
logError("decompress", { sid: ctx.sessionManager.getSessionId(), event: "block-path-rejected", blockId, toFile: args.toFile });
|
|
3696
3745
|
return targetPath.error;
|
|
3697
3746
|
}
|
|
3698
|
-
await mkdir(
|
|
3747
|
+
await mkdir(autoDir(), { recursive: true }).catch((e) => logError("decompress", { event: "mkdir-failed", dir: autoDir(), error: e instanceof Error ? e.message : String(e) }));
|
|
3699
3748
|
await writeFile(targetPath, text, "utf8");
|
|
3749
|
+
if (!args.toFile) pruneAutoFiles();
|
|
3700
3750
|
debug.event("decompress", { blockId, full, count, mode: "file", path: targetPath, chars: text.length });
|
|
3701
3751
|
logInfo("decompress", { sid: ctx.sessionManager.getSessionId(), event: "block", mode: "file", blockId, full, count, path: targetPath, chars: text.length });
|
|
3702
3752
|
const itemWord = count === 1 ? "item" : "items";
|
|
@@ -4508,7 +4558,7 @@ async function statusReport(runtime, ctx) {
|
|
|
4508
4558
|
const coveredIds = collectCoveredMessageIds(state);
|
|
4509
4559
|
const sentTokens = estimateTokens(coreMessages, coveredIds) + systemPromptTokens;
|
|
4510
4560
|
const turn = runtime.core.processTurn({ messages: coreMessages, state, config, tokenCount: sentTokens });
|
|
4511
|
-
const versionStr = "0.2.
|
|
4561
|
+
const versionStr = "0.2.3" ? `billion-context-omp@${"0.2.3"}` : void 0;
|
|
4512
4562
|
return buildStatusPanel({
|
|
4513
4563
|
version: versionStr,
|
|
4514
4564
|
tokenCount: sessionTokens,
|
|
@@ -4893,7 +4943,7 @@ You have four context-management tools:
|
|
|
4893
4943
|
|
|
4894
4944
|
- compress \u2014 Replace a contiguous range of older conversation with a single detailed summary you write. Use when content is genuinely consumed (no longer needed for the current task step). Single range: compress({ content: [{ topic: "Session Opener", startId: "m00150", endId: "m00220", summary: "..." }] }) \u2014 topic is recommended but optional. Batch (multiple unrelated ranges, each with its own topic): compress({ content: [{ topic: "Auth", startId: "m00150", endId: "m00220", summary: "..." }, { topic: "Deploy", startId: "m00300", endId: "m00350", summary: "..." }] }). Call it as a normal tool \u2014 summaries are plain string arguments.
|
|
4895
4945
|
- decompress \u2014 Restore a previously compressed block's content. The block stays compressed \u2014 context and cache prefix are not disrupted. By DEFAULT content is written to an auto-generated file (avoids context bloat); use the read tool to view it. Pass inline:true to return content in the tool result instead (appends to context). full:true recurses to original messages. Example: decompress({ blockId: "b5" }) or decompress({ blockId: "b5", full: true }) or decompress({ blockId: "b5", inline: true }).
|
|
4896
|
-
- search_context \u2014 Search compressed block summaries
|
|
4946
|
+
- search_context \u2014 Search compressed block summaries AND the original messages folded into them by keyword (messages still visible in context are not indexed \u2014 you can already see them). Use BEFORE decompressing to find the right block. Example: search_context({ query: "auth token refresh" }).
|
|
4897
4947
|
- acp_status \u2014 Context status with compressible ranges. No args = overview + totals. scope:"uncompressed" for range view; add view:"messages" for per-message listing. scope:"compressed" for block details.
|
|
4898
4948
|
|
|
4899
4949
|
${prompts.compressPhilosophy}
|
|
@@ -5051,9 +5101,44 @@ function wireToolGuardrails(pi, runtime) {
|
|
|
5051
5101
|
});
|
|
5052
5102
|
}
|
|
5053
5103
|
|
|
5104
|
+
// src/instance-guard.ts
|
|
5105
|
+
import { readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
5106
|
+
import { join as join4 } from "path";
|
|
5107
|
+
var MARKER_FILE = ".billion-context-omp-instance.json";
|
|
5108
|
+
var FRESH_MS = 6e4;
|
|
5109
|
+
function markerPath() {
|
|
5110
|
+
return join4(homeDir(), ".omp", MARKER_FILE);
|
|
5111
|
+
}
|
|
5112
|
+
function readMarker() {
|
|
5113
|
+
try {
|
|
5114
|
+
const raw = JSON.parse(readFileSync2(markerPath(), "utf8"));
|
|
5115
|
+
if (typeof raw?.path === "string" && typeof raw?.ts === "number") return raw;
|
|
5116
|
+
} catch {
|
|
5117
|
+
}
|
|
5118
|
+
return void 0;
|
|
5119
|
+
}
|
|
5120
|
+
function detectDualInstance(selfPath, now = Date.now()) {
|
|
5121
|
+
const m = readMarker();
|
|
5122
|
+
if (!m) return void 0;
|
|
5123
|
+
if (m.path === selfPath) return void 0;
|
|
5124
|
+
if (now - m.ts > FRESH_MS) return void 0;
|
|
5125
|
+
return m;
|
|
5126
|
+
}
|
|
5127
|
+
function stampInstance(selfPath, version, pid = process.pid, now = Date.now()) {
|
|
5128
|
+
try {
|
|
5129
|
+
writeFileSync(markerPath(), JSON.stringify({ path: selfPath, version, pid, ts: now }));
|
|
5130
|
+
} catch {
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
function stampAndDetect(selfPath, version, now = Date.now()) {
|
|
5134
|
+
const conflict = detectDualInstance(selfPath, now);
|
|
5135
|
+
stampInstance(selfPath, version, void 0, now);
|
|
5136
|
+
return conflict;
|
|
5137
|
+
}
|
|
5138
|
+
|
|
5054
5139
|
// src/update.ts
|
|
5055
5140
|
import { readFile, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
5056
|
-
import { join as
|
|
5141
|
+
import { join as join5, dirname as dirname3 } from "path";
|
|
5057
5142
|
import { fileURLToPath } from "url";
|
|
5058
5143
|
import { execFile } from "child_process";
|
|
5059
5144
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME3 } from "@oh-my-pi/pi-utils";
|
|
@@ -5061,7 +5146,7 @@ var PACKAGE_NAME = "billion-context-omp";
|
|
|
5061
5146
|
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
5062
5147
|
var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/;
|
|
5063
5148
|
var CHECK_INTERVAL_MS = 3 * 60 * 1e3;
|
|
5064
|
-
var throttleFile = () =>
|
|
5149
|
+
var throttleFile = () => join5(homeDir(), CONFIG_DIR_NAME3, ".billion-context-omp-update-check");
|
|
5065
5150
|
var updateInFlight = false;
|
|
5066
5151
|
function parseVersion(v) {
|
|
5067
5152
|
return v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10) || 0);
|
|
@@ -5111,7 +5196,7 @@ function findNpmRoot(extDir) {
|
|
|
5111
5196
|
async function findExtensionDir() {
|
|
5112
5197
|
let dir = dirname3(fileURLToPath(import.meta.url));
|
|
5113
5198
|
for (; ; ) {
|
|
5114
|
-
const pkg = await readPackageJson(
|
|
5199
|
+
const pkg = await readPackageJson(join5(dir, "package.json"));
|
|
5115
5200
|
if (pkg?.name === PACKAGE_NAME) return dir;
|
|
5116
5201
|
const parent = dirname3(dir);
|
|
5117
5202
|
if (parent === dir) return void 0;
|
|
@@ -5181,7 +5266,7 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
5181
5266
|
const data = await res.json();
|
|
5182
5267
|
const latest = data.version;
|
|
5183
5268
|
if (!latest) return;
|
|
5184
|
-
const current = runtimeVersion ?? "0.2.
|
|
5269
|
+
const current = runtimeVersion ?? "0.2.3";
|
|
5185
5270
|
const hasUpdate = isNewer(latest, current);
|
|
5186
5271
|
debug.event("update-check", {
|
|
5187
5272
|
current,
|
|
@@ -5211,25 +5296,25 @@ async function checkForUpdate(autoUpdate, notify) {
|
|
|
5211
5296
|
async function getRuntimeVersion() {
|
|
5212
5297
|
const extDir = await findExtensionDir();
|
|
5213
5298
|
if (!extDir) return void 0;
|
|
5214
|
-
const pkg = await readPackageJson(
|
|
5299
|
+
const pkg = await readPackageJson(join5(extDir, "package.json"));
|
|
5215
5300
|
return pkg?.version;
|
|
5216
5301
|
}
|
|
5217
5302
|
|
|
5218
5303
|
// src/dump.ts
|
|
5219
|
-
import { mkdirSync as mkdirSync2, writeFileSync, readdirSync, unlinkSync } from "fs";
|
|
5304
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
5220
5305
|
import * as path2 from "path";
|
|
5221
5306
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME4 } from "@oh-my-pi/pi-utils";
|
|
5222
5307
|
var counters = {};
|
|
5223
5308
|
var MAX_FILES_PER_PREFIX = 200;
|
|
5224
5309
|
function pruneDumps(dir, prefixTest, seqOf) {
|
|
5225
5310
|
try {
|
|
5226
|
-
const files =
|
|
5311
|
+
const files = readdirSync2(dir).filter(prefixTest).map((f) => ({ f, n: seqOf(f) })).filter((x) => !Number.isNaN(x.n));
|
|
5227
5312
|
if (files.length <= MAX_FILES_PER_PREFIX) return;
|
|
5228
5313
|
files.sort((a, b) => a.n - b.n);
|
|
5229
5314
|
const excess = files.slice(0, files.length - MAX_FILES_PER_PREFIX);
|
|
5230
5315
|
for (const { f } of excess) {
|
|
5231
5316
|
try {
|
|
5232
|
-
|
|
5317
|
+
unlinkSync2(path2.join(dir, f));
|
|
5233
5318
|
} catch {
|
|
5234
5319
|
}
|
|
5235
5320
|
}
|
|
@@ -5247,7 +5332,7 @@ function dumpContextMessages(messages, meta) {
|
|
|
5247
5332
|
mkdirSync2(dir, { recursive: true });
|
|
5248
5333
|
if (!(dir in counters)) {
|
|
5249
5334
|
try {
|
|
5250
|
-
const existing =
|
|
5335
|
+
const existing = readdirSync2(dir).filter((f) => /^\d{4}\.json$/.test(f));
|
|
5251
5336
|
const max = existing.reduce((mx, f) => {
|
|
5252
5337
|
const n = parseInt(f, 10);
|
|
5253
5338
|
return Number.isNaN(n) ? mx : Math.max(mx, n);
|
|
@@ -5261,7 +5346,7 @@ function dumpContextMessages(messages, meta) {
|
|
|
5261
5346
|
counters[dir] = seq + 1;
|
|
5262
5347
|
const name = `${String(seq).padStart(4, "0")}.json`;
|
|
5263
5348
|
const fullPath = path2.join(dir, name);
|
|
5264
|
-
|
|
5349
|
+
writeFileSync2(
|
|
5265
5350
|
fullPath,
|
|
5266
5351
|
JSON.stringify({
|
|
5267
5352
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5330,7 +5415,7 @@ function dumpProviderRequest(payload, meta) {
|
|
|
5330
5415
|
mkdirSync2(dir, { recursive: true });
|
|
5331
5416
|
if (!("req" in counters)) {
|
|
5332
5417
|
try {
|
|
5333
|
-
const existing =
|
|
5418
|
+
const existing = readdirSync2(dir).filter((f) => /^req_\d+\.json$/.test(f));
|
|
5334
5419
|
const max = existing.reduce((mx, f) => {
|
|
5335
5420
|
const n = parseInt(f.slice(4, -5), 10);
|
|
5336
5421
|
return Number.isNaN(n) ? mx : Math.max(mx, n);
|
|
@@ -5345,7 +5430,7 @@ function dumpProviderRequest(payload, meta) {
|
|
|
5345
5430
|
const name = `req_${String(seq).padStart(4, "0")}.json`;
|
|
5346
5431
|
const fullPath = path2.join(dir, name);
|
|
5347
5432
|
const summary = summarizeProviderPayload(payload);
|
|
5348
|
-
|
|
5433
|
+
writeFileSync2(
|
|
5349
5434
|
fullPath,
|
|
5350
5435
|
JSON.stringify({
|
|
5351
5436
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5369,10 +5454,11 @@ import * as path3 from "path";
|
|
|
5369
5454
|
import { CONFIG_DIR_NAME as CONFIG_DIR_NAME5 } from "@oh-my-pi/pi-utils";
|
|
5370
5455
|
async function loadUserConfig(cwd) {
|
|
5371
5456
|
const home = homeDir();
|
|
5457
|
+
const globalDir = join8(home, CONFIG_DIR_NAME5);
|
|
5372
5458
|
const merged = {};
|
|
5373
|
-
for (const base of [
|
|
5374
|
-
const file =
|
|
5375
|
-
const allowPrompts = base
|
|
5459
|
+
for (const base of [globalDir, join8(cwd, CONFIG_DIR_NAME5)]) {
|
|
5460
|
+
const file = join8(base, "acp-omp.json");
|
|
5461
|
+
const allowPrompts = base === globalDir;
|
|
5376
5462
|
try {
|
|
5377
5463
|
const raw = await fs.readFile(file, "utf8");
|
|
5378
5464
|
const parsed = JSON.parse(raw);
|
|
@@ -5389,7 +5475,7 @@ async function loadUserConfig(cwd) {
|
|
|
5389
5475
|
}
|
|
5390
5476
|
return merged;
|
|
5391
5477
|
}
|
|
5392
|
-
function
|
|
5478
|
+
function join8(...parts) {
|
|
5393
5479
|
return path3.join(...parts);
|
|
5394
5480
|
}
|
|
5395
5481
|
var KNOWN = /* @__PURE__ */ new Set([
|
|
@@ -5497,7 +5583,18 @@ function wireCompactionDisable(pi, runtime) {
|
|
|
5497
5583
|
function wireSessionLifecycle(pi, runtime) {
|
|
5498
5584
|
pi.on("session_start", async (_event, ctx) => {
|
|
5499
5585
|
const sid = ctx.sessionManager.getSessionId();
|
|
5500
|
-
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.
|
|
5586
|
+
logInfo("session", { event: "start", sid, cwd: ctx.cwd, debug: runtime.adapter.debug ?? null, version: true ? "0.2.3" : null });
|
|
5587
|
+
const selfPath = import.meta.url;
|
|
5588
|
+
const conflict = stampAndDetect(selfPath, true ? "0.2.3" : null);
|
|
5589
|
+
if (conflict) {
|
|
5590
|
+
logWarn("instance", { event: "dual-instance", self: selfPath, other: conflict.path, otherPid: conflict.pid, otherVersion: conflict.version });
|
|
5591
|
+
try {
|
|
5592
|
+
if (ctx.hasUI) {
|
|
5593
|
+
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).`);
|
|
5594
|
+
}
|
|
5595
|
+
} catch {
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5501
5598
|
try {
|
|
5502
5599
|
const user = await loadUserConfig(ctx.cwd);
|
|
5503
5600
|
runtime.setAdapter(applyUserConfig(runtime.adapter, user));
|
|
@@ -5527,6 +5624,7 @@ function wireSessionLifecycle(pi, runtime) {
|
|
|
5527
5624
|
async function transformStream(ctx, runtime, input, mode) {
|
|
5528
5625
|
const sid = ctx.sessionManager.getSessionId();
|
|
5529
5626
|
const release = await runtime.acquireLock(sid);
|
|
5627
|
+
let result;
|
|
5530
5628
|
try {
|
|
5531
5629
|
if (input.length === 0) {
|
|
5532
5630
|
debug.event("empty-stream-bypass", { sid });
|
|
@@ -5632,21 +5730,25 @@ ${rendered.text}${example}`);
|
|
|
5632
5730
|
}
|
|
5633
5731
|
}
|
|
5634
5732
|
}
|
|
5733
|
+
if (mode === "context") {
|
|
5734
|
+
runtime.recordRebuiltOutput(ctx, rebuilt);
|
|
5735
|
+
}
|
|
5635
5736
|
dumpContextMessages(rebuilt, {
|
|
5636
5737
|
sid,
|
|
5637
5738
|
injected: nudgeInjected,
|
|
5638
5739
|
emergency: turn.nudge?.breakdown?.emergencyOverride === 1
|
|
5639
5740
|
});
|
|
5640
|
-
|
|
5641
|
-
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
5642
|
-
});
|
|
5643
|
-
return { rebuilt, nudgeInjected };
|
|
5741
|
+
result = { rebuilt, nudgeInjected };
|
|
5644
5742
|
} catch (e) {
|
|
5645
5743
|
logThrow("context", e, { sid, phase: "transform", mode });
|
|
5646
5744
|
throw e;
|
|
5647
5745
|
} finally {
|
|
5648
5746
|
release();
|
|
5649
5747
|
}
|
|
5748
|
+
await checkForUpdate(runtime.adapter.autoUpdate ?? true, (msg) => {
|
|
5749
|
+
if (ctx.hasUI) ctx.ui.notify(msg);
|
|
5750
|
+
});
|
|
5751
|
+
return result;
|
|
5650
5752
|
}
|
|
5651
5753
|
function wireContextTransform(pi, runtime) {
|
|
5652
5754
|
pi.on("context", async (event, ctx) => {
|