billion-context 0.1.82 → 0.1.83-pr.397.350
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 +12 -1
- package/README.zh-CN.md +12 -1
- package/dist/index.js +170 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,6 +112,17 @@ turn is a semantic mismatch the model tolerates (it is clearly marked
|
|
|
112
112
|
summaries (their tool call isn't in the agent's history and the agent's view
|
|
113
113
|
skips `acp_summary`) — but that needs the id match above, which doesn't occur.
|
|
114
114
|
|
|
115
|
+
## Which do I need?
|
|
116
|
+
|
|
117
|
+
Pick by your client:
|
|
118
|
+
|
|
119
|
+
| Client | Use |
|
|
120
|
+
|---|---|
|
|
121
|
+
| **pi** | [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi) (in-process extension) |
|
|
122
|
+
| **opencode** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp) (in-process extension) |
|
|
123
|
+
| **omp** | [`billion-context`](https://github.com/ranxianglei/billion-context) via `bili omp` (built-in plugin) |
|
|
124
|
+
| **everything else** (no context hook) | [`billion-context`](https://github.com/ranxianglei/billion-context) — `bili <client>` (launcher, preferred) or `/bili/` prefix |
|
|
125
|
+
|
|
115
126
|
## Install
|
|
116
127
|
|
|
117
128
|
```bash
|
|
@@ -382,7 +393,7 @@ pass an explicit `x-acp-session` header per conversation to avoid collisions.
|
|
|
382
393
|
|
|
383
394
|
Early. Protocol handling and compression work against mock tests (500+ passing). Real-model integration testing is the next milestone. Expect rough edges.
|
|
384
395
|
|
|
385
|
-
|
|
396
|
+
Client-side plugins for pi / omp / opencode ship inside `billion-context` (`dist/agent/*.js`) for the cooperative-proxy path. See the **"Which do I need?"** section above for how `billion-context`, the standalone `billion-context-pi`, and `opencode-acp` relate.
|
|
386
397
|
|
|
387
398
|
## License
|
|
388
399
|
|
package/README.zh-CN.md
CHANGED
|
@@ -50,6 +50,17 @@ AI 编程助手的<strong>通用上下文压缩代理</strong>
|
|
|
50
50
|
|
|
51
51
|
代理向对话注入四个上下文管理工具(`compress`、`decompress`、`search_context`、`acp_status`)。模型在对话增长时调用 `compress`,代理在服务端执行 —— 压缩后的范围在下一轮之前折叠进对话历史。
|
|
52
52
|
|
|
53
|
+
## 该选哪个?
|
|
54
|
+
|
|
55
|
+
按客户端选:
|
|
56
|
+
|
|
57
|
+
| 客户端 | 用这个 |
|
|
58
|
+
|---|---|
|
|
59
|
+
| **pi** | [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi)(进程内扩展) |
|
|
60
|
+
| **opencode** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp)(进程内扩展) |
|
|
61
|
+
| **omp** | [`billion-context`](https://github.com/ranxianglei/billion-context),`bili omp`(内置插件) |
|
|
62
|
+
| **其余所有**(没有上下文 hook) | [`billion-context`](https://github.com/ranxianglei/billion-context) —— `bili <client>`(启动器,优先)或 `/bili/` 前缀 |
|
|
63
|
+
|
|
53
64
|
## 安装
|
|
54
65
|
|
|
55
66
|
```bash
|
|
@@ -258,7 +269,7 @@ Windows 下会自动发现常见 Clash/Mihomo 静态系统代理;Web UI 会显
|
|
|
258
269
|
|
|
259
270
|
早期。协议处理和压缩已通过 mock 测试(500+ 项通过)。真实模型集成测试是下一里程碑。预期会有粗糙的地方。
|
|
260
271
|
|
|
261
|
-
pi
|
|
272
|
+
针对 pi / omp / opencode 的客户端插件随 `billion-context` 一起发布(`dist/agent/*.js`),用于协作代理路径。三者(`billion-context`、独立的 `billion-context-pi`、`opencode-acp`)如何取舍,见上文「该选哪个?」一节。
|
|
262
273
|
|
|
263
274
|
## 许可证
|
|
264
275
|
|
package/dist/index.js
CHANGED
|
@@ -48360,33 +48360,64 @@ async function contextFromRegistry(model, host) {
|
|
|
48360
48360
|
function peekRegistryContext(model, host) {
|
|
48361
48361
|
return registryLookup(cache2, model, host);
|
|
48362
48362
|
}
|
|
48363
|
+
var VARIANT_SUFFIXES = [
|
|
48364
|
+
"-interleaved-thinking",
|
|
48365
|
+
"-thinking",
|
|
48366
|
+
"-reasoning",
|
|
48367
|
+
"-extended",
|
|
48368
|
+
"-fast",
|
|
48369
|
+
"-high",
|
|
48370
|
+
"-medium",
|
|
48371
|
+
"-low"
|
|
48372
|
+
];
|
|
48373
|
+
function modelVariants(name) {
|
|
48374
|
+
const variants = [name];
|
|
48375
|
+
let current = name;
|
|
48376
|
+
for (; ; ) {
|
|
48377
|
+
let stripped = false;
|
|
48378
|
+
for (const suffix of VARIANT_SUFFIXES) {
|
|
48379
|
+
if (current.length > suffix.length && current.endsWith(suffix)) {
|
|
48380
|
+
current = current.slice(0, -suffix.length);
|
|
48381
|
+
variants.push(current);
|
|
48382
|
+
stripped = true;
|
|
48383
|
+
break;
|
|
48384
|
+
}
|
|
48385
|
+
}
|
|
48386
|
+
if (!stripped) break;
|
|
48387
|
+
}
|
|
48388
|
+
return variants;
|
|
48389
|
+
}
|
|
48363
48390
|
function registryLookup(reg, model, host) {
|
|
48364
48391
|
if (!reg || !model) return void 0;
|
|
48365
48392
|
const provider = host ? providerFromHost(host) : void 0;
|
|
48366
|
-
const
|
|
48367
|
-
|
|
48368
|
-
const
|
|
48369
|
-
|
|
48370
|
-
|
|
48371
|
-
|
|
48372
|
-
|
|
48373
|
-
|
|
48374
|
-
|
|
48375
|
-
|
|
48376
|
-
|
|
48377
|
-
|
|
48378
|
-
|
|
48379
|
-
|
|
48380
|
-
|
|
48381
|
-
|
|
48382
|
-
|
|
48383
|
-
|
|
48384
|
-
|
|
48385
|
-
|
|
48386
|
-
|
|
48387
|
-
|
|
48393
|
+
for (const name of modelVariants(model)) {
|
|
48394
|
+
const candidates = provider ? [`${provider}/${name}`, name] : [name];
|
|
48395
|
+
for (const key of candidates) {
|
|
48396
|
+
const entry = reg[key];
|
|
48397
|
+
const ctx = entry?.limit?.context;
|
|
48398
|
+
if (typeof ctx === "number" && ctx > 0) return ctx;
|
|
48399
|
+
}
|
|
48400
|
+
if (provider === void 0) {
|
|
48401
|
+
const suffix = `/${name}`;
|
|
48402
|
+
let max;
|
|
48403
|
+
const distinct = /* @__PURE__ */ new Set();
|
|
48404
|
+
const parts = [];
|
|
48405
|
+
for (const key of Object.keys(reg)) {
|
|
48406
|
+
if (!key.endsWith(suffix)) continue;
|
|
48407
|
+
const ctx = reg[key].limit?.context;
|
|
48408
|
+
if (typeof ctx !== "number" || ctx <= 0) continue;
|
|
48409
|
+
if (max === void 0 || ctx > max) max = ctx;
|
|
48410
|
+
distinct.add(ctx);
|
|
48411
|
+
parts.push(`${key}=${ctx}`);
|
|
48412
|
+
}
|
|
48413
|
+
if (max !== void 0) {
|
|
48414
|
+
if (distinct.size > 1 && !warnedConflicts.has(name)) {
|
|
48415
|
+
warnedConflicts.add(name);
|
|
48416
|
+
log("warn", `[acp-registry] conflicting context windows for "${name}" (${parts.join(", ")}) \u2014 using max ${max}`);
|
|
48417
|
+
}
|
|
48418
|
+
return max;
|
|
48419
|
+
}
|
|
48388
48420
|
}
|
|
48389
|
-
return max;
|
|
48390
48421
|
}
|
|
48391
48422
|
return void 0;
|
|
48392
48423
|
}
|
|
@@ -50260,6 +50291,50 @@ function withStagedCompressGuidance(text) {
|
|
|
50260
50291
|
return text + STAGED_COMPRESS_GUIDANCE;
|
|
50261
50292
|
}
|
|
50262
50293
|
|
|
50294
|
+
// src/acp-status.ts
|
|
50295
|
+
function handleAcpStatus(args, ctx) {
|
|
50296
|
+
const scope = typeof args.scope === "string" ? args.scope : void 0;
|
|
50297
|
+
const view = typeof args.view === "string" ? args.view : void 0;
|
|
50298
|
+
const tool = typeof args.tool === "string" ? args.tool : void 0;
|
|
50299
|
+
const sort = typeof args.sort === "string" ? args.sort : void 0;
|
|
50300
|
+
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
50301
|
+
const base = buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast, { scope, view, tool, sort, limit });
|
|
50302
|
+
if (scope) return base;
|
|
50303
|
+
const extra = [];
|
|
50304
|
+
try {
|
|
50305
|
+
const turn = ctx.core.processTurn({
|
|
50306
|
+
messages: ctx.messages,
|
|
50307
|
+
state: ctx.session.state,
|
|
50308
|
+
config: ctx.config,
|
|
50309
|
+
tokenCount: ctx.session.stats.lastInputTokens,
|
|
50310
|
+
renderTags: "none"
|
|
50311
|
+
});
|
|
50312
|
+
const nudge = turn.nudge;
|
|
50313
|
+
if (nudge) {
|
|
50314
|
+
extra.push("");
|
|
50315
|
+
extra.push(nudge.shouldInject ? `Nudge: ACTIVE \u2014 ${nudge.reason}` : `Nudge: idle \u2014 ${nudge.reason}`);
|
|
50316
|
+
const ranges = viableRanges(nudge.compressibleRanges);
|
|
50317
|
+
const protectedRanges = nudge.protectedRanges ?? [];
|
|
50318
|
+
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
50319
|
+
extra.push("");
|
|
50320
|
+
extra.push(formatRanges(ranges, protectedRanges));
|
|
50321
|
+
}
|
|
50322
|
+
}
|
|
50323
|
+
} catch {
|
|
50324
|
+
}
|
|
50325
|
+
const archive = preCompactionArchiveOf(ctx.session);
|
|
50326
|
+
const archivedIds = Object.keys(archive);
|
|
50327
|
+
if (archivedIds.length > 0) {
|
|
50328
|
+
extra.push("");
|
|
50329
|
+
extra.push(`PRE-COMPACTION ARCHIVE \u2014 ${archivedIds.length} block(s): content was replaced by the client's native compaction summary, so it is no longer in the session history and decompress is unavailable.`);
|
|
50330
|
+
for (const id of archivedIds) {
|
|
50331
|
+
extra.push(` ${id} \u2014 ${archive[id].reason}`);
|
|
50332
|
+
}
|
|
50333
|
+
}
|
|
50334
|
+
return extra.length > 0 ? `${base}
|
|
50335
|
+
${extra.join("\n")}` : base;
|
|
50336
|
+
}
|
|
50337
|
+
|
|
50263
50338
|
// src/decompress-shared.ts
|
|
50264
50339
|
import { mkdirSync as mkdirSync3, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
50265
50340
|
import { dirname as dirname2, join as join2 } from "path";
|
|
@@ -50581,7 +50656,7 @@ function executeAnthropicProxyTool(toolName, args, ctx) {
|
|
|
50581
50656
|
${lines.join("\n\n")}`;
|
|
50582
50657
|
}
|
|
50583
50658
|
if (toolName === "acp_status") {
|
|
50584
|
-
return
|
|
50659
|
+
return handleAcpStatus(args, ctx);
|
|
50585
50660
|
}
|
|
50586
50661
|
return `[Unknown proxy tool: ${toolName}]`;
|
|
50587
50662
|
}
|
|
@@ -51696,38 +51771,6 @@ ${lines.join("\n\n")}`;
|
|
|
51696
51771
|
}
|
|
51697
51772
|
return `[Unknown proxy tool: ${toolName}]`;
|
|
51698
51773
|
}
|
|
51699
|
-
function handleAcpStatus(args, ctx) {
|
|
51700
|
-
const scope = typeof args.scope === "string" ? args.scope : void 0;
|
|
51701
|
-
const view = typeof args.view === "string" ? args.view : void 0;
|
|
51702
|
-
const tool = typeof args.tool === "string" ? args.tool : void 0;
|
|
51703
|
-
const sort = typeof args.sort === "string" ? args.sort : void 0;
|
|
51704
|
-
const limit = typeof args.limit === "number" ? args.limit : void 0;
|
|
51705
|
-
const base = buildStatusReport(ctx.session.state, ctx.messages, estimateTokensFast, { scope, view, tool, sort, limit });
|
|
51706
|
-
if (scope) return base;
|
|
51707
|
-
const nudge = ctx.nudge;
|
|
51708
|
-
const ranges = nudge?.compressibleRanges ?? [];
|
|
51709
|
-
const protectedRanges = nudge?.protectedRanges ?? [];
|
|
51710
|
-
const archive = preCompactionArchiveOf(ctx.session);
|
|
51711
|
-
const archivedIds = Object.keys(archive);
|
|
51712
|
-
const extra = [];
|
|
51713
|
-
if (nudge) {
|
|
51714
|
-
extra.push("");
|
|
51715
|
-
extra.push(nudge.shouldInject ? `Nudge: ACTIVE \u2014 ${nudge.reason}` : `Nudge: idle \u2014 ${nudge.reason}`);
|
|
51716
|
-
}
|
|
51717
|
-
if (ranges.length > 0 || protectedRanges.length > 0) {
|
|
51718
|
-
extra.push("");
|
|
51719
|
-
extra.push(formatRanges(ranges, protectedRanges));
|
|
51720
|
-
}
|
|
51721
|
-
if (archivedIds.length > 0) {
|
|
51722
|
-
extra.push("");
|
|
51723
|
-
extra.push(`PRE-COMPACTION ARCHIVE \u2014 ${archivedIds.length} block(s): content was replaced by the client's native compaction summary, so it is no longer in the session history and decompress is unavailable.`);
|
|
51724
|
-
for (const id of archivedIds) {
|
|
51725
|
-
extra.push(` ${id} \u2014 ${archive[id].reason}`);
|
|
51726
|
-
}
|
|
51727
|
-
}
|
|
51728
|
-
return extra.length > 0 ? `${base}
|
|
51729
|
-
${extra.join("\n")}` : base;
|
|
51730
|
-
}
|
|
51731
51774
|
function recordUsage(ctx, usage, round) {
|
|
51732
51775
|
const prompt = usage.inputTokens;
|
|
51733
51776
|
const cached = usage.cachedTokens;
|
|
@@ -53547,7 +53590,7 @@ function executeProxyTool2(toolName, args, ctx) {
|
|
|
53547
53590
|
${lines.join("\n\n")}`;
|
|
53548
53591
|
}
|
|
53549
53592
|
if (toolName === "acp_status") {
|
|
53550
|
-
return
|
|
53593
|
+
return handleAcpStatus(args, ctx);
|
|
53551
53594
|
}
|
|
53552
53595
|
return `[Unknown proxy tool: ${toolName}]`;
|
|
53553
53596
|
}
|
|
@@ -54705,7 +54748,7 @@ function handlePluginStatus(conversationId2, res, fallbackLatest = false) {
|
|
|
54705
54748
|
if (entry) entry.lastSeen = Date.now();
|
|
54706
54749
|
const limit = session.metadata.effectiveContextLimit;
|
|
54707
54750
|
const mem = remembered.get(session.id);
|
|
54708
|
-
const modelContextLimit = typeof limit === "number" && limit > 0 ? limit :
|
|
54751
|
+
const modelContextLimit = typeof limit === "number" && limit > 0 ? limit : 0;
|
|
54709
54752
|
let panel;
|
|
54710
54753
|
try {
|
|
54711
54754
|
panel = buildStatusPanel({
|
|
@@ -54780,8 +54823,7 @@ async function handlePluginTool(payload, res, deps) {
|
|
|
54780
54823
|
config: deps.config,
|
|
54781
54824
|
messages,
|
|
54782
54825
|
session,
|
|
54783
|
-
log: (m2) => deps.log("info", `[${session.id}] [plugin] ${m2}`)
|
|
54784
|
-
nudge: mem?.nudge
|
|
54826
|
+
log: (m2) => deps.log("info", `[${session.id}] [plugin] ${m2}`)
|
|
54785
54827
|
}, callId);
|
|
54786
54828
|
});
|
|
54787
54829
|
} catch (err2) {
|
|
@@ -56729,6 +56771,9 @@ function logUnrecognizedPath(log2, url) {
|
|
|
56729
56771
|
log2("info", `unrecognized path ${key}: forwarding unchanged; further occurrences suppressed`);
|
|
56730
56772
|
}
|
|
56731
56773
|
}
|
|
56774
|
+
function isModelDiscoveryPath(path18) {
|
|
56775
|
+
return path18.replace(/\/+$/, "").endsWith("/models");
|
|
56776
|
+
}
|
|
56732
56777
|
var BILI_HOP_HEADER = "x-bili-hop";
|
|
56733
56778
|
function parseLauncherModelWindows(raw) {
|
|
56734
56779
|
if (!raw) return {};
|
|
@@ -56930,7 +56975,7 @@ Content-Length: ${Buffer.byteLength(body)}\r
|
|
|
56930
56975
|
);
|
|
56931
56976
|
}
|
|
56932
56977
|
if (opts.debug) {
|
|
56933
|
-
log2("info", `[debug] build features: raw-HTTP-capture(on) | remote_compaction_v2-strip(on) | cert-MITM-launcher(on) | strip-acp-summary(on) \u2014 seeing this line confirms the launcher build (not registry 0.1.34)`);
|
|
56978
|
+
log2("info", `[debug] build features: raw-HTTP-capture(${bodyDumpEnabled() ? "on" : "off"}) | remote_compaction_v2-strip(on) | cert-MITM-launcher(on) | strip-acp-summary(on) \u2014 seeing this line confirms the launcher build (not registry 0.1.34)`);
|
|
56934
56979
|
}
|
|
56935
56980
|
};
|
|
56936
56981
|
const attemptListen = (port) => {
|
|
@@ -56998,6 +57043,13 @@ Content-Length: ${Buffer.byteLength(body)}\r
|
|
|
56998
57043
|
}
|
|
56999
57044
|
return server;
|
|
57000
57045
|
}
|
|
57046
|
+
var SIDE_REQUEST_MAX_TOKENS = 200;
|
|
57047
|
+
function isSideRequest(parsed) {
|
|
57048
|
+
if (!parsed || typeof parsed !== "object") return false;
|
|
57049
|
+
const p2 = parsed;
|
|
57050
|
+
const raw = p2.max_tokens ?? p2.max_completion_tokens ?? p2.max_output_tokens;
|
|
57051
|
+
return typeof raw === "number" && raw > 0 && raw <= SIDE_REQUEST_MAX_TOKENS;
|
|
57052
|
+
}
|
|
57001
57053
|
function isTrustedAdminOrigin(origin, host, trustedHosts) {
|
|
57002
57054
|
if (!host || !trustedHosts.has(host.toLowerCase())) return false;
|
|
57003
57055
|
if (!origin) return true;
|
|
@@ -57274,11 +57326,11 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
57274
57326
|
const pluginWindow = pluginReportedContextWindow(req.headers);
|
|
57275
57327
|
const launcherWindow = launcherContextWindow(model);
|
|
57276
57328
|
const configuredWindow = resolveConfiguredContextLimit(opts.routes, embeddedUrl, model);
|
|
57277
|
-
const peekWindow =
|
|
57329
|
+
const peekWindow = peekRegistryContext(model, host);
|
|
57278
57330
|
let native = betaWindow ?? pluginWindow ?? launcherWindow ?? configuredWindow ?? peekWindow ?? lookupContextLimit(model);
|
|
57279
57331
|
const operatorWindowTuned = resolveCompress(opts.routes, embeddedUrl, model, opts.compress).modelContextLimit !== void 0;
|
|
57280
57332
|
nativeFromFallback = !betaWindow && !pluginWindow && !launcherWindow && !peekWindow && !configuredWindow && !operatorWindowTuned;
|
|
57281
|
-
if (!native
|
|
57333
|
+
if (!native) {
|
|
57282
57334
|
native = await contextFromRegistry(model, host);
|
|
57283
57335
|
if (native) nativeFromFallback = false;
|
|
57284
57336
|
}
|
|
@@ -57379,7 +57431,6 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
57379
57431
|
if (pluginAgent && !pluginConversation) pluginConversation = conversation;
|
|
57380
57432
|
if (pluginAgent) {
|
|
57381
57433
|
if (session.metadata.pluginAgent !== pluginAgent) session.metadata.pluginAgent = pluginAgent;
|
|
57382
|
-
session.metadata.effectiveContextLimit = reqConfig.modelContextLimit;
|
|
57383
57434
|
recordPluginSession(pluginConversation ?? conversation, session.id);
|
|
57384
57435
|
}
|
|
57385
57436
|
if (protocol === "responses" || protocol === "openai" || protocol === "anthropic") {
|
|
@@ -57389,6 +57440,21 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
57389
57440
|
}
|
|
57390
57441
|
}
|
|
57391
57442
|
const pluginMode = pluginAgent !== void 0;
|
|
57443
|
+
if (!countTokens && !responsesCompact && protocol !== null && isSideRequest(parsed)) {
|
|
57444
|
+
log2("info", `[${session.id}] side request (max_tokens<=${SIDE_REQUEST_MAX_TOKENS}) \u2192 passthrough + tag strip only, kernel state untouched`);
|
|
57445
|
+
const sidePrepared = {
|
|
57446
|
+
body: bodyBuffer,
|
|
57447
|
+
session,
|
|
57448
|
+
processedMessages: [],
|
|
57449
|
+
originalMessages: [],
|
|
57450
|
+
protocol,
|
|
57451
|
+
stream: parsed.stream === true,
|
|
57452
|
+
compressInjected: false,
|
|
57453
|
+
sidePassthrough: true
|
|
57454
|
+
};
|
|
57455
|
+
await forward(req, res, opts, bodyBuffer, sidePrepared, core, reqConfig, log2, route, instanceId, affinity);
|
|
57456
|
+
return;
|
|
57457
|
+
}
|
|
57392
57458
|
const reqModel = parsed.model;
|
|
57393
57459
|
const learnedMap = session.metadata.learnedContextLimits;
|
|
57394
57460
|
const learnedLimit = (reqModel && learnedMap ? learnedMap[reqModel] : void 0) ?? session.metadata.learnedContextLimit;
|
|
@@ -57397,6 +57463,22 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
57397
57463
|
reqConfig = { ...reqConfig, modelContextLimit: learnedLimit };
|
|
57398
57464
|
nativeFromFallback = false;
|
|
57399
57465
|
log2("info", `[${session.id}] self-healed context window: ${resolved} \u2192 ${learnedLimit} (learned from an upstream overflow)`);
|
|
57466
|
+
} else if (nativeFromFallback && reqModel) {
|
|
57467
|
+
const prevInput = session.stats.lastInputTokens ?? 0;
|
|
57468
|
+
const prevWindow = session.metadata.lastTurnWindow;
|
|
57469
|
+
const resolved = reqConfig.modelContextLimit;
|
|
57470
|
+
if (prevWindow !== void 0 && prevInput > prevWindow && prevInput > resolved && prevInput >= 1e3) {
|
|
57471
|
+
const map = session.metadata.learnedContextLimits ?? {};
|
|
57472
|
+
const prev = map[reqModel];
|
|
57473
|
+
if (prev === void 0 || prevInput > prev) {
|
|
57474
|
+
map[reqModel] = prevInput;
|
|
57475
|
+
session.metadata.learnedContextLimits = map;
|
|
57476
|
+
markDirty(session);
|
|
57477
|
+
}
|
|
57478
|
+
reqConfig = { ...reqConfig, modelContextLimit: prevInput };
|
|
57479
|
+
nativeFromFallback = false;
|
|
57480
|
+
log2("info", `[${session.id}] self-healed context window upward: ${resolved} \u2192 ${prevInput} (a prior turn used ${prevInput} input tokens, exceeding the fallback window)`);
|
|
57481
|
+
}
|
|
57400
57482
|
}
|
|
57401
57483
|
const nativeWindow = reqConfig.modelContextLimit;
|
|
57402
57484
|
if (shouldReserveOutputHeadroom(protocol)) {
|
|
@@ -57410,6 +57492,8 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
57410
57492
|
}
|
|
57411
57493
|
if (reserved !== reqConfig.modelContextLimit) reqConfig = { ...reqConfig, modelContextLimit: reserved };
|
|
57412
57494
|
}
|
|
57495
|
+
session.metadata.effectiveContextLimit = reqConfig.modelContextLimit;
|
|
57496
|
+
session.metadata.lastTurnWindow = reqConfig.modelContextLimit;
|
|
57413
57497
|
acquireInFlight(session);
|
|
57414
57498
|
try {
|
|
57415
57499
|
await withSessionLock(session, async () => {
|
|
@@ -57474,7 +57558,7 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
57474
57558
|
}
|
|
57475
57559
|
}
|
|
57476
57560
|
if (!prepared) {
|
|
57477
|
-
if (protocol === null && !opts.passthrough) {
|
|
57561
|
+
if (protocol === null && !opts.passthrough && !isModelDiscoveryPath(urlPath)) {
|
|
57478
57562
|
logUnrecognizedPath(log2, req.url ?? "");
|
|
57479
57563
|
}
|
|
57480
57564
|
await forward(req, res, opts, bodyBuffer, null, core, reqConfig, log2, route, instanceId, void 0);
|
|
@@ -58311,7 +58395,7 @@ ${hdrText}
|
|
|
58311
58395
|
}
|
|
58312
58396
|
let responseBody = upstream.body;
|
|
58313
58397
|
try {
|
|
58314
|
-
if (prepared !== null && prepared.stream && maxFakeCompletionRetries() > 0) {
|
|
58398
|
+
if (prepared !== null && prepared.stream && !prepared.sidePassthrough && maxFakeCompletionRetries() > 0) {
|
|
58315
58399
|
const resolvedBuf = await resolveFakeCompletion(upstream.body, {
|
|
58316
58400
|
protocol: prepared.protocol,
|
|
58317
58401
|
body,
|
|
@@ -58393,7 +58477,7 @@ ${hdrText}
|
|
|
58393
58477
|
};
|
|
58394
58478
|
const loop = runCompressLoop(
|
|
58395
58479
|
streamToRead,
|
|
58396
|
-
{ core, config, messages: prepared.processedMessages.length > 0 ? prepared.processedMessages : prepared.originalMessages, compressMessages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, protocol: prepared.protocol, textProtocol, debug: opts.debug,
|
|
58480
|
+
{ core, config, messages: prepared.processedMessages.length > 0 ? prepared.processedMessages : prepared.originalMessages, compressMessages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, protocol: prepared.protocol, textProtocol, debug: opts.debug, refreshFolded },
|
|
58397
58481
|
parsedReq,
|
|
58398
58482
|
{ url: upstreamUrl, headers: reqHeaders },
|
|
58399
58483
|
adapter,
|
|
@@ -62631,7 +62715,6 @@ function selfDistFile(name) {
|
|
|
62631
62715
|
return path15.join(selfPackageRoot(), "dist", name);
|
|
62632
62716
|
}
|
|
62633
62717
|
var LAUNCHER_DEFAULT_HOST = "127.0.0.1";
|
|
62634
|
-
var LAUNCHER_DEFAULT_PORT = 8787;
|
|
62635
62718
|
var LAUNCH_CLIENTS = ["pi", "codex", "claude", "omp", "opencode", "hermes", "dsh", "pi-test"];
|
|
62636
62719
|
var HEALTH_PATH = "/__bili/health";
|
|
62637
62720
|
var HEALTH_POLL_INTERVAL_MS = 200;
|
|
@@ -63805,6 +63888,19 @@ async function probeExistingInstance(readInstance, fetchHealthInfo) {
|
|
|
63805
63888
|
if (health.instanceId !== void 0 && health.instanceId !== inst.instanceId) return void 0;
|
|
63806
63889
|
return inst;
|
|
63807
63890
|
}
|
|
63891
|
+
function pickEphemeralPort(host = LAUNCHER_DEFAULT_HOST) {
|
|
63892
|
+
return new Promise((resolve, reject) => {
|
|
63893
|
+
const srv = net2.createServer();
|
|
63894
|
+
srv.once("error", reject);
|
|
63895
|
+
srv.listen(0, host, () => {
|
|
63896
|
+
const addr = srv.address();
|
|
63897
|
+
srv.close(() => {
|
|
63898
|
+
if (addr && typeof addr === "object") resolve(addr.port);
|
|
63899
|
+
else reject(new Error("could not allocate a free port"));
|
|
63900
|
+
});
|
|
63901
|
+
});
|
|
63902
|
+
});
|
|
63903
|
+
}
|
|
63808
63904
|
var INHERITED_PROXY_VARS = ["http_proxy", "https_proxy", "all_proxy", "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY"];
|
|
63809
63905
|
function stripInheritedProxy(env) {
|
|
63810
63906
|
const cleaned = { ...env };
|
|
@@ -63830,7 +63926,7 @@ async function ensureProxyRunning(opts, deps = {}) {
|
|
|
63830
63926
|
return { origin: existing.origin, port: existing.port, attached: true };
|
|
63831
63927
|
}
|
|
63832
63928
|
const launchToken = randomUUID5();
|
|
63833
|
-
const port = opts.port;
|
|
63929
|
+
const port = opts.port > 0 ? opts.port : await pickEphemeralPort(opts.host);
|
|
63834
63930
|
const script = process.argv[1];
|
|
63835
63931
|
if (!script) throw new Error("bili: cannot resolve launcher script path");
|
|
63836
63932
|
const logPath2 = path15.join(os5.tmpdir(), `bili-proxy-${port}.log`);
|
|
@@ -63839,7 +63935,7 @@ async function ensureProxyRunning(opts, deps = {}) {
|
|
|
63839
63935
|
try {
|
|
63840
63936
|
child = spawnImpl(
|
|
63841
63937
|
process.execPath,
|
|
63842
|
-
[script, ...proxyStartArgs(opts)],
|
|
63938
|
+
[script, ...proxyStartArgs({ ...opts, port })],
|
|
63843
63939
|
{
|
|
63844
63940
|
detached: true,
|
|
63845
63941
|
stdio: ["ignore", logFd, logFd],
|
|
@@ -63946,8 +64042,9 @@ function resolveClientCommand(client, env) {
|
|
|
63946
64042
|
return { command: resolved ?? client, prefixArgs: [] };
|
|
63947
64043
|
}
|
|
63948
64044
|
function parsePort(raw) {
|
|
63949
|
-
|
|
63950
|
-
|
|
64045
|
+
if (!raw || !raw.trim()) return 0;
|
|
64046
|
+
const port = parseInt(raw, 10);
|
|
64047
|
+
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
|
63951
64048
|
console.error(`bili: invalid --port "${raw}"`);
|
|
63952
64049
|
process.exit(2);
|
|
63953
64050
|
}
|
|
@@ -64386,7 +64483,7 @@ Launcher (bili pi / bili codex / bili claude / bili omp / bili opencode / bili h
|
|
|
64386
64483
|
Options (override config file / env):
|
|
64387
64484
|
-F <url> upstream proxy to forward through (gost-style;
|
|
64388
64485
|
http://host:port; must precede the client name)
|
|
64389
|
-
--port <N> listen port (default
|
|
64486
|
+
--port <N> listen port (start: 8787; launcher default: random free port)
|
|
64390
64487
|
--host <ADDR> listen host (default 127.0.0.1)
|
|
64391
64488
|
--mitm-domain <domain> extra MITM domain (repeatable; launcher only)
|
|
64392
64489
|
--config <FILE> path to config JSON (default: XDG location)
|