billion-context 0.1.75 → 0.1.76-pr.495.317
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 +322 -195
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -45360,6 +45360,26 @@ function numericBlockId(id) {
|
|
|
45360
45360
|
const parsed = /^b(\d+)$/.exec(id);
|
|
45361
45361
|
return parsed ? Number(parsed[1]) : 0;
|
|
45362
45362
|
}
|
|
45363
|
+
function refGateDiagnostics(state, requestedRanges, unknownCount) {
|
|
45364
|
+
const highest = highestUsedIndex(state.messageRefs);
|
|
45365
|
+
const highestRef = highest > 0 ? indexToRef(highest) : "none";
|
|
45366
|
+
return `[diagnostics: session highest ref=${highestRef}, unknown ranges in request=${unknownCount}/${requestedRanges}, session history=${state.stats.compressionCount} compression(s), ${state.blocks.length} block(s)]`;
|
|
45367
|
+
}
|
|
45368
|
+
function danglingMessageRefs(state, messages, spec) {
|
|
45369
|
+
const visible = new Set(messages.map((m2) => m2.id));
|
|
45370
|
+
const dangling = [];
|
|
45371
|
+
for (const ref of [spec.startRef, spec.endRef]) {
|
|
45372
|
+
const parsed = parseBoundary(ref);
|
|
45373
|
+
if (!parsed || parsed.kind !== "message") continue;
|
|
45374
|
+
const rawId = state.messageRefs.byRef[parsed.raw] ?? state.messageRefs.byRef[indexToRef(parsed.numericId)];
|
|
45375
|
+
if (!rawId || visible.has(rawId)) continue;
|
|
45376
|
+
const covered = state.blocks.some(
|
|
45377
|
+
(block) => block.active && block.effectiveMessageIds.includes(rawId)
|
|
45378
|
+
);
|
|
45379
|
+
if (!covered) dangling.push(parsed.raw);
|
|
45380
|
+
}
|
|
45381
|
+
return dangling;
|
|
45382
|
+
}
|
|
45363
45383
|
function createCore(ports = {}) {
|
|
45364
45384
|
const countTokens = ports.countTokens ?? defaultCountTokens;
|
|
45365
45385
|
function applyCompression(input) {
|
|
@@ -45460,7 +45480,15 @@ function createCore(ports = {}) {
|
|
|
45460
45480
|
if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
45461
45481
|
const live = activeBlocks(state).map((b2) => b2.blockId).sort((x, y) => numericBlockId(x) - numericBlockId(y));
|
|
45462
45482
|
const liveHint = live.length > 0 ? ` Current active blocks span ${live[0]}..${live[live.length - 1]} \u2014 retry with startId/endId set to active block IDs in that span.` : "";
|
|
45463
|
-
const
|
|
45483
|
+
const diagnostics = refGateDiagnostics(
|
|
45484
|
+
state,
|
|
45485
|
+
input.ranges.length,
|
|
45486
|
+
unknownCount
|
|
45487
|
+
);
|
|
45488
|
+
const danglingRefs = consumedRanges.flatMap(
|
|
45489
|
+
(spec) => danglingMessageRefs(state, input.messages, spec)
|
|
45490
|
+
);
|
|
45491
|
+
const gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref is unknown to this session. Refs are per-session snapshots, assigned once when a message is first rendered; no compress reassigns them, so unknown refs cannot come from an earlier compress in this session. They come from a different generation: a previous session instance (switching model or upstream mid-conversation starts a fresh session whose refs restart at m00001), the generation before a native-compaction rebase (which also resets refs to m00001), or a typo. ${diagnostics} Run acp_status, then call the compress tool again using only the refs it reports.` : consumedRanges.length > 0 ? danglingRefs.length > 0 ? `Requested range(s) cannot be anchored (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}) \u2014 the refs exist in this session's ref map, but the messages they point to are no longer in the visible context and no active block covers them: the message content changed (or the message was filtered out of the view) and now carries a new ref, leaving your old refs dangling. ${diagnostics} Run acp_status, then call the compress tool again using only the refs it reports.` : `Requested range(s) already compressed (e.g. ${consumedRanges[0].startRef}..${consumedRanges[0].endRef}) \u2014 those refs no longer point to directly compressible content: the range is covered by active block(s) or the block ref(s) are stale (distilled or consumed). ${diagnostics} Run acp_status, then call the compress tool again using only the CURRENT compressible ranges it reports.${liveHint}` : `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`;
|
|
45464
45492
|
return {
|
|
45465
45493
|
state: input.state,
|
|
45466
45494
|
result: {
|
|
@@ -47856,7 +47884,7 @@ function parseCompressSettings(v2) {
|
|
|
47856
47884
|
}
|
|
47857
47885
|
out[key] = typeof obj[key] === "string" ? obj[key].trim() : obj[key];
|
|
47858
47886
|
}
|
|
47859
|
-
for (const key of ["nudgeGrowthTokens", "preserveRecentMessages", "preserveRecentTokens", "minCompressRange"]) {
|
|
47887
|
+
for (const key of ["nudgeGrowthTokens", "preserveRecentMessages", "preserveRecentTokens", "minCompressRange", "minCompressRangeChars"]) {
|
|
47860
47888
|
takeNumber(key);
|
|
47861
47889
|
}
|
|
47862
47890
|
if ("tiers" in obj) {
|
|
@@ -47922,6 +47950,7 @@ function resolveContextLimitValue(raw, nativeLimit) {
|
|
|
47922
47950
|
function mergeCompress(global2, provider, model) {
|
|
47923
47951
|
const pick = (k2) => model?.[k2] ?? provider?.[k2] ?? global2?.[k2];
|
|
47924
47952
|
const promptLevels = [global2?.prompts, provider?.prompts, model?.prompts].filter(Boolean);
|
|
47953
|
+
const rangeOf = (s3) => s3?.minCompressRangeChars ?? s3?.minCompressRange;
|
|
47925
47954
|
return {
|
|
47926
47955
|
modelContextLimit: pick("modelContextLimit"),
|
|
47927
47956
|
maxContextLimit: pick("maxContextLimit"),
|
|
@@ -47929,7 +47958,7 @@ function mergeCompress(global2, provider, model) {
|
|
|
47929
47958
|
nudgeGrowthTokens: pick("nudgeGrowthTokens"),
|
|
47930
47959
|
preserveRecentMessages: pick("preserveRecentMessages"),
|
|
47931
47960
|
preserveRecentTokens: pick("preserveRecentTokens"),
|
|
47932
|
-
|
|
47961
|
+
minCompressRangeChars: rangeOf(model) ?? rangeOf(provider) ?? rangeOf(global2),
|
|
47933
47962
|
tiers: pick("tiers"),
|
|
47934
47963
|
prompts: promptLevels.length > 0 ? Object.assign({}, ...promptLevels) : void 0,
|
|
47935
47964
|
acknowledgePromptsRisk: pick("acknowledgePromptsRisk")
|
|
@@ -47983,7 +48012,7 @@ function applyCompressSettings(base, limit, s3) {
|
|
|
47983
48012
|
preserveRecentTokens: s3.preserveRecentTokens ?? base.preserveRecentTokens,
|
|
47984
48013
|
compress: {
|
|
47985
48014
|
...base.compress,
|
|
47986
|
-
minCompressRange: s3.minCompressRange ?? base.compress.minCompressRange
|
|
48015
|
+
minCompressRange: s3.minCompressRangeChars ?? s3.minCompressRange ?? base.compress.minCompressRange
|
|
47987
48016
|
}
|
|
47988
48017
|
};
|
|
47989
48018
|
}
|
|
@@ -48008,12 +48037,22 @@ import path3 from "path";
|
|
|
48008
48037
|
// src/fetch-util.ts
|
|
48009
48038
|
var MAX_REQUEST_BYTES = 100 * 1024 * 1024;
|
|
48010
48039
|
var UPSTREAM_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
48040
|
+
var liveUpstreamTimers = /* @__PURE__ */ new Set();
|
|
48011
48041
|
async function fetchWithTimeout(url, opts, timeoutMs = UPSTREAM_TIMEOUT_MS, externalSignal) {
|
|
48012
48042
|
const controller = new AbortController();
|
|
48013
|
-
|
|
48043
|
+
const armTimer = () => {
|
|
48044
|
+
const t = setTimeout(() => {
|
|
48045
|
+
liveUpstreamTimers.delete(t);
|
|
48046
|
+
controller.abort();
|
|
48047
|
+
}, timeoutMs);
|
|
48048
|
+
liveUpstreamTimers.add(t);
|
|
48049
|
+
return t;
|
|
48050
|
+
};
|
|
48051
|
+
let timer2 = armTimer();
|
|
48014
48052
|
const rearm = () => {
|
|
48015
48053
|
clearTimeout(timer2);
|
|
48016
|
-
|
|
48054
|
+
liveUpstreamTimers.delete(timer2);
|
|
48055
|
+
timer2 = armTimer();
|
|
48017
48056
|
};
|
|
48018
48057
|
let onExternalAbort = null;
|
|
48019
48058
|
if (externalSignal) {
|
|
@@ -48025,6 +48064,7 @@ async function fetchWithTimeout(url, opts, timeoutMs = UPSTREAM_TIMEOUT_MS, exte
|
|
|
48025
48064
|
}
|
|
48026
48065
|
const cleanup = () => {
|
|
48027
48066
|
clearTimeout(timer2);
|
|
48067
|
+
liveUpstreamTimers.delete(timer2);
|
|
48028
48068
|
if (onExternalAbort && externalSignal) externalSignal.removeEventListener("abort", onExternalAbort);
|
|
48029
48069
|
};
|
|
48030
48070
|
try {
|
|
@@ -48154,6 +48194,7 @@ var CACHE_FILE = path3.join(cacheDir(), "models-dev.json");
|
|
|
48154
48194
|
var TTL_MS = 24 * 60 * 60 * 1e3;
|
|
48155
48195
|
var cache2 = null;
|
|
48156
48196
|
var loading = null;
|
|
48197
|
+
var warnedConflicts = /* @__PURE__ */ new Set();
|
|
48157
48198
|
var snapshotReg = null;
|
|
48158
48199
|
var snapshotMs = 0;
|
|
48159
48200
|
function bundledSnapshotRegistry() {
|
|
@@ -48326,15 +48367,22 @@ function registryLookup(reg, model, host) {
|
|
|
48326
48367
|
}
|
|
48327
48368
|
if (provider === void 0) {
|
|
48328
48369
|
const suffix = `/${model}`;
|
|
48329
|
-
let
|
|
48370
|
+
let max;
|
|
48371
|
+
const distinct = /* @__PURE__ */ new Set();
|
|
48372
|
+
const parts = [];
|
|
48330
48373
|
for (const key of Object.keys(reg)) {
|
|
48331
48374
|
if (!key.endsWith(suffix)) continue;
|
|
48332
48375
|
const ctx = reg[key].limit?.context;
|
|
48333
48376
|
if (typeof ctx !== "number" || ctx <= 0) continue;
|
|
48334
|
-
if (
|
|
48335
|
-
|
|
48377
|
+
if (max === void 0 || ctx > max) max = ctx;
|
|
48378
|
+
distinct.add(ctx);
|
|
48379
|
+
parts.push(`${key}=${ctx}`);
|
|
48336
48380
|
}
|
|
48337
|
-
|
|
48381
|
+
if (max !== void 0 && distinct.size > 1 && !warnedConflicts.has(model)) {
|
|
48382
|
+
warnedConflicts.add(model);
|
|
48383
|
+
log("warn", `[acp-registry] conflicting context windows for "${model}" (${parts.join(", ")}) \u2014 using max ${max}`);
|
|
48384
|
+
}
|
|
48385
|
+
return max;
|
|
48338
48386
|
}
|
|
48339
48387
|
return void 0;
|
|
48340
48388
|
}
|
|
@@ -48459,11 +48507,14 @@ ${summary}` }]
|
|
|
48459
48507
|
}
|
|
48460
48508
|
return { items, replaced, dropped };
|
|
48461
48509
|
}
|
|
48462
|
-
function
|
|
48463
|
-
if (
|
|
48510
|
+
function codexCompactGatePre(session, effectiveLimit) {
|
|
48511
|
+
if (effectiveLimit <= 0) return false;
|
|
48464
48512
|
if (session.stats.lastInputTokens >= effectiveLimit * 0.9) return false;
|
|
48465
48513
|
return session.state.blocks.some((b2) => b2.active);
|
|
48466
48514
|
}
|
|
48515
|
+
function codexCompactGate(session, effectiveLimit, transformOk) {
|
|
48516
|
+
return transformOk && codexCompactGatePre(session, effectiveLimit);
|
|
48517
|
+
}
|
|
48467
48518
|
function buildTriggerForgeBody(summaryText, usage, stream2) {
|
|
48468
48519
|
const compactionItem = {
|
|
48469
48520
|
type: "compaction",
|
|
@@ -49940,7 +49991,13 @@ function buildSession(parsed) {
|
|
|
49940
49991
|
metadata: parsed.metadata ?? {},
|
|
49941
49992
|
state: mergeState(parsed.state),
|
|
49942
49993
|
createdAt: parsed.createdAt ?? Date.now(),
|
|
49943
|
-
|
|
49994
|
+
// #404: lastSeen reflects the on-disk savedAt (the last real
|
|
49995
|
+
// activity), NOT the restore moment — a restart must not fabricate
|
|
49996
|
+
// activity for every session (broke fallback=latest ties, panel
|
|
49997
|
+
// freshness, and eviction ordering). Consumers that need "has this
|
|
49998
|
+
// session been used since boot" read the restored flag.
|
|
49999
|
+
lastSeen: parsed.savedAt ?? Date.now(),
|
|
50000
|
+
restored: true,
|
|
49944
50001
|
blockContents,
|
|
49945
50002
|
lastMessages: Array.isArray(parsed.messages) ? parsed.messages : void 0,
|
|
49946
50003
|
inFlight: 0,
|
|
@@ -49995,7 +50052,8 @@ async function initSessions() {
|
|
|
49995
50052
|
await store.migrateLegacyIds();
|
|
49996
50053
|
const loaded = await store.loadAll();
|
|
49997
50054
|
if (loaded.size > MAX_SESSIONS) {
|
|
49998
|
-
const
|
|
50055
|
+
const freshness = (s3) => Math.max(s3.createdAt ?? 0, s3.lastSeen ?? 0);
|
|
50056
|
+
const entries = [...loaded.entries()].sort((a, b2) => freshness(b2[1]) - freshness(a[1]));
|
|
49999
50057
|
for (const [id, s3] of entries) {
|
|
50000
50058
|
if (sessions.size >= MAX_SESSIONS) break;
|
|
50001
50059
|
sessions.set(id, s3);
|
|
@@ -50008,6 +50066,7 @@ function getSession(id, meta) {
|
|
|
50008
50066
|
const existing = sessions.get(id);
|
|
50009
50067
|
if (existing) {
|
|
50010
50068
|
existing.lastSeen = Date.now();
|
|
50069
|
+
existing.restored = false;
|
|
50011
50070
|
if (meta?.protocol && !existing.meta.protocol) existing.meta.protocol = meta.protocol;
|
|
50012
50071
|
if (meta?.upstreamOrigin && !existing.meta.upstreamOrigin) existing.meta.upstreamOrigin = meta.upstreamOrigin;
|
|
50013
50072
|
if (meta?.label && !existing.meta.label) existing.meta.label = meta.label;
|
|
@@ -50017,6 +50076,7 @@ function getSession(id, meta) {
|
|
|
50017
50076
|
const reloaded = store.loadSync(id, meta);
|
|
50018
50077
|
if (reloaded) {
|
|
50019
50078
|
reloaded.lastSeen = Date.now();
|
|
50079
|
+
reloaded.restored = false;
|
|
50020
50080
|
reloaded.persisted = true;
|
|
50021
50081
|
sessions.set(id, reloaded);
|
|
50022
50082
|
return reloaded;
|
|
@@ -51027,7 +51087,7 @@ async function saveProviders(){const button=byId("save-providers");busy(button,t
|
|
|
51027
51087
|
async function saveCompress(){const button=byId("save-compress");busy(button,true,"\u4FDD\u5B58\u4E2D\u2026");try{const text=byId("compress-json").value.trim();const compress=text?JSON.parse(text):null;await json("/__bili/config",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({compress})});toast("\u538B\u7F29\u53C2\u6570\u5DF2\u70ED\u66F4\u65B0")}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
51028
51088
|
async function loadOverrides(){try{const data=await loadConfig();const providers=data.providers||{};const box=byId("upstream-overrides");const entries=Object.entries(providers);if(entries.length===0){box.innerHTML='<p class="status">\u6682\u65E0 Provider\u3002\u5728"\u9AD8\u7EA7\u8BBE\u7F6E"\u9875\u6DFB\u52A0 Provider \u540E\u53EF\u5728\u6B64\u6309 URL \u914D\u7F6E\u4EE3\u7406\u3002</p>';return}box.innerHTML='<table><thead><tr><th>Provider URL</th><th>\u4E0A\u6E38\u4EE3\u7406\uFF08\u7A7A=\u7EE7\u627F\u5168\u5C40\uFF09</th></tr></thead><tbody>'+entries.map(([url,route])=>'<tr><td class="mono">'+escapeHtml(url)+'</td><td><input class="mono override-proxy" data-url="'+escapeHtml(url)+'" value="'+escapeHtml((route&&route.proxy)||"")+'" placeholder="\u7EE7\u627F\u5168\u5C40"></td></tr>').join("")+'</tbody></table>'}catch(error){byId("upstream-overrides").textContent=String(error)}}
|
|
51029
51089
|
async function saveOverrides(){const button=byId("save-overrides");busy(button,true,"\u4FDD\u5B58\u4E2D\u2026");try{const data=await loadConfig();const providers=data.providers||{};document.querySelectorAll(".override-proxy").forEach((input)=>{const url=input.dataset.url;if(!url)return;if(!providers[url])providers[url]={};const val=input.value.trim();if(val)providers[url].proxy=val;else delete providers[url].proxy});await json("/__bili/config",{method:"PUT",headers:{"content-type":"application/json"},body:JSON.stringify({providers})});await json("/__bili/config/reload",{method:"POST"});toast("\u6309 URL \u8986\u76D6\u5DF2\u70ED\u66F4\u65B0");await loadOverrides()}catch(error){toast(String(error),true)}finally{busy(button,false)}}
|
|
51030
|
-
async function loadSessions(){try{const data=await json("/__bili/stats");const rows=(data.sessions||[]).map((item)=>"<tr><td>"+escapeHtml(item.title||"\u2014")+"</td><td>"+escapeHtml(item.protocol||"\u2014")+"</td><td class=mono>"+escapeHtml(item.label||"\u2014")+"</td><td>"+escapeHtml(item.requests)+"</td><td>"+escapeHtml(item.contextTokens)+"</td><td>"+escapeHtml(new Date(item.lastSeen).toLocaleString())+"</td></tr>").join("");byId("sessions-body").innerHTML=rows||'<tr><td colspan="6">\u6682\u65E0\u4F1A\u8BDD</td></tr>'}catch(error){toast(String(error),true);throw error}}
|
|
51090
|
+
async function loadSessions(){try{const data=await json("/__bili/stats");const rows=(data.sessions||[]).map((item)=>"<tr><td>"+escapeHtml(item.title||"\u2014")+"</td><td>"+escapeHtml(item.protocol||"\u2014")+"</td><td class=mono>"+escapeHtml(item.label||"\u2014")+"</td><td>"+escapeHtml(item.requests)+"</td><td>"+escapeHtml(item.contextTokens)+"</td><td>"+escapeHtml(new Date(item.lastSeen).toLocaleString())+(item.restored?" <span class=muted>(\u6062\u590D\uFF0C\u5F00\u673A\u540E\u672A\u6D3B\u52A8)</span>":"")+"</td></tr>").join("");byId("sessions-body").innerHTML=rows||'<tr><td colspan="6">\u6682\u65E0\u4F1A\u8BDD</td></tr>'}catch(error){toast(String(error),true);throw error}}
|
|
51031
51091
|
async function refreshSessions(){const button=byId("refresh-sessions");busy(button,true,"\u5237\u65B0\u4E2D\u2026");try{await loadSessions();toast("\u4F1A\u8BDD\u5217\u8868\u5DF2\u5237\u65B0")}catch{}finally{busy(button,false)}}
|
|
51032
51092
|
document.querySelectorAll(".copy-btn").forEach((btn)=>btn.addEventListener("click",async()=>{busy(btn,true,"\u590D\u5236\u4E2D\u2026");try{await navigator.clipboard.writeText(btn.dataset.copy||"");toast("\u5DF2\u590D\u5236")}catch(e){toast(String(e),true)}finally{busy(btn,false)}}));byId("save-upstream").addEventListener("click",saveUpstream);byId("test-upstream").addEventListener("click",testUpstream);byId("save-providers").addEventListener("click",saveProviders);byId("save-compress").addEventListener("click",saveCompress);byId("save-overrides").addEventListener("click",saveOverrides);byId("refresh-sessions").addEventListener("click",refreshSessions);
|
|
51033
51093
|
Promise.all([loadConfig(),loadUpstream()]).catch((error)=>toast(String(error),true));setInterval(()=>{if(byId("page-sessions").classList.contains("active"))loadSessions().catch(()=>{})},5000);
|
|
@@ -51037,7 +51097,7 @@ Promise.all([loadConfig(),loadUpstream()]).catch((error)=>toast(String(error),tr
|
|
|
51037
51097
|
var WEB_STYLES = `
|
|
51038
51098
|
:root{--bg:#0f1115;--panel:#171a21;--panel2:#1e222c;--line:#2b313d;--text:#e7eaf0;--muted:#8f98a8;--blue:#6ea8fe;--green:#55c98b;--yellow:#e9b949;--red:#ef6b73;--radius:10px}
|
|
51039
51099
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;min-height:100vh}.app{display:grid;grid-template-columns:220px 1fr;min-height:100vh}.sidebar{border-right:1px solid var(--line);padding:22px 14px;background:#12151b;position:sticky;top:0;height:100vh}.brand{font-size:18px;font-weight:700;padding:0 10px 22px}.brand span{color:var(--blue)}.version{display:block;color:var(--muted);font-size:11px;font-weight:400;margin-top:2px}.nav{display:grid;gap:4px}.nav button{appearance:none;border:0;background:transparent;color:var(--muted);text-align:left;padding:10px 12px;border-radius:7px;cursor:pointer;font:inherit}.nav button:hover,.nav button.active{background:var(--panel2);color:var(--text)}.main{padding:32px;max-width:1050px;width:100%}.page{display:none}.page.active{display:block}h1{font-size:23px;margin:0 0 4px}h2{font-size:15px;margin:0}.lead{color:var(--muted);margin:0 0 24px}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:18px;margin-bottom:14px}.card-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.badge{border-radius:99px;padding:3px 9px;font-size:12px;background:var(--panel2);color:var(--muted)}.badge.ok{color:var(--green)}.badge.warn{color:var(--yellow)}.badge.err{color:var(--red)}.kv{display:grid;grid-template-columns:135px 1fr;gap:10px 14px;margin:0}.kv dt{color:var(--muted)}.kv dd{margin:0;word-break:break-all}.mono{font-family:"Cascadia Code",Consolas,monospace;font-size:12px}.actions{display:flex;gap:9px;flex-wrap:wrap;margin-top:18px}.btn{border:1px solid var(--line);background:var(--panel2);color:var(--text);border-radius:7px;padding:8px 13px;cursor:pointer;font:inherit}.btn:hover{border-color:#4b5668}.btn.primary{background:var(--blue);border-color:var(--blue);color:#0b1220;font-weight:600}.btn.danger{color:var(--red)}.btn:disabled{opacity:.55;cursor:wait}.field{display:grid;gap:6px;margin:12px 0}.field label{color:var(--muted);font-size:12px}input,textarea,select{width:100%;background:#101319;color:var(--text);border:1px solid var(--line);border-radius:7px;padding:9px 10px;font:inherit}textarea{min-height:250px;font-family:"Cascadia Code",Consolas,monospace;font-size:12px;resize:vertical}.modes{display:flex;gap:18px;margin:12px 0}.modes label{display:flex;gap:7px;align-items:center;color:var(--muted)}.modes input{width:auto}.notice{border-left:3px solid var(--yellow);padding:10px 12px;background:rgba(233,185,73,.08);color:#d8c185;margin-top:14px}.status{color:var(--muted);white-space:pre-wrap}.status.ok{color:var(--green)}.status.err{color:var(--red)}table{width:100%;border-collapse:collapse}th,td{text-align:left;border-bottom:1px solid var(--line);padding:9px 8px;font-size:12px}th{color:var(--muted);font-weight:500}.toast{position:fixed;right:24px;bottom:24px;background:var(--green);color:#07140d;padding:10px 15px;border-radius:8px;opacity:0;transform:translateY(8px);transition:.2s;pointer-events:none}.toast.show{opacity:1;transform:none}.toast.err{background:var(--red);color:#1b0608}
|
|
51040
|
-
.section-title{font-size:17px;margin:28px 0 4px}.hint{color:var(--muted);margin:0 0 14px;font-size:13px}.btn.small{padding:4px 10px;font-size:12px}.config-block{background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:10px 14px;margin:8px 0 12px;overflow-x:auto;font-size:13px}.config-block code{font-family:"Cascadia Code",Consolas,monospace;color:var(--text)}.fork-ribbon{position:fixed;top:0;right:0;background:#1a1a2e;color:#fff;padding:6px 16px;font-size:12px;text-decoration:none;border-radius:0 0 0 8px;z-index:100;opacity:.8;transition:.2s}.fork-ribbon:hover{opacity:1}
|
|
51100
|
+
.section-title{font-size:17px;margin:28px 0 4px}.hint{color:var(--muted);margin:0 0 14px;font-size:13px}.muted{color:var(--muted);font-size:11px}.btn.small{padding:4px 10px;font-size:12px}.config-block{background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:10px 14px;margin:8px 0 12px;overflow-x:auto;font-size:13px}.config-block code{font-family:"Cascadia Code",Consolas,monospace;color:var(--text)}.fork-ribbon{position:fixed;top:0;right:0;background:#1a1a2e;color:#fff;padding:6px 16px;font-size:12px;text-decoration:none;border-radius:0 0 0 8px;z-index:100;opacity:.8;transition:.2s}.fork-ribbon:hover{opacity:1}
|
|
51041
51101
|
@media(max-width:760px){.app{grid-template-columns:1fr}.sidebar{height:auto;position:static;border-right:0;border-bottom:1px solid var(--line)}.nav{display:flex;overflow:auto}.main{padding:20px}.grid{grid-template-columns:1fr}.kv{grid-template-columns:110px 1fr}}
|
|
51042
51102
|
`;
|
|
51043
51103
|
|
|
@@ -51066,7 +51126,7 @@ function renderPage(origin, version2) {
|
|
|
51066
51126
|
<div class="card"><div class="card-head"><h2>ZCode\uFF08\u7F16\u7A0B\u5957\u9910\uFF09</h2><span class="badge">MITM</span><button class="btn small copy-btn" data-copy="${caPathEsc}">\u590D\u5236 CA \u8DEF\u5F84</button></div><dl class="kv"><dt>\u65B9\u5F0F</dt><dd>MITM \u900F\u660E\u4EE3\u7406</dd><dt>\u8BBE\u7F6E</dt><dd>Settings \u2192 Network \u2192 HTTP Proxy = <span class="mono">${origin}</span></dd><dt>CA \u8BC1\u4E66</dt><dd class="mono">${caPathEsc}${caReady ? "" : "\uFF08\u5C1A\u672A\u751F\u6210\uFF0C\u542F\u52A8\u4EE3\u7406\u540E\u81EA\u52A8\u521B\u5EFA\uFF09"}</dd></dl><p class="hint">CA \u8DEF\u5F84\u662F\u672C\u673A bili \u5B9E\u9645\u751F\u6210\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u70B9"\u590D\u5236 CA \u8DEF\u5F84"\u540E\u76F4\u63A5\u7C98\u8D34\u8FDB ZCode\u3002\u26A0\uFE0F Windows \u4E0A ZCode \u4E0D\u4F1A\u5C55\u5F00 <span class="mono">~</span>\uFF0C\u8BF7\u52FF\u624B\u586B <span class="mono">~/\u2026</span> \u5F62\u5F0F\u7684\u8DEF\u5F84\u3002</p></div></section>
|
|
51067
51127
|
<section id="page-upstream" class="page"><h1>\u4E0A\u6E38\u7F51\u7EDC</h1><p class="lead">\u63A7\u5236 bili \u5982\u4F55\u8BBF\u95EE\u771F\u5B9E Provider\uFF0C\u4E0D\u4F1A\u6539\u53D8\u5BA2\u6237\u7AEF\u7684\u672C\u5730\u8DEF\u7531\u5730\u5740\u3002</p><div class="card"><div class="card-head"><h2>\u5168\u5C40\u4EE3\u7406</h2></div><div class="modes"><label><input type="radio" name="proxy-mode" value="direct" checked>\u76F4\u8FDE\uFF08\u9ED8\u8BA4\uFF09</label><label><input type="radio" name="proxy-mode" value="manual">\u624B\u52A8\u4EE3\u7406</label><label><input type="radio" name="proxy-mode" value="auto">\u81EA\u52A8\uFF08\u8DDF\u968F\u7CFB\u7EDF\uFF09</label></div><div class="field"><label>HTTP / HTTPS Proxy</label><input id="proxy-url" class="mono" placeholder="http://127.0.0.1:7897"></div><dl class="kv"><dt>\u5F53\u524D\u6765\u6E90</dt><dd id="upstream-source">\u76F4\u8FDE</dd><dt>\u6709\u6548\u4EE3\u7406</dt><dd id="upstream-effective" class="mono">direct</dd><dt>\u7CFB\u7EDF PAC</dt><dd id="upstream-pac" class="mono">\u2014</dd><dt>\u72B6\u6001</dt><dd id="upstream-state" class="status">\u5C1A\u672A\u6D4B\u8BD5</dd></dl><div class="actions"><button id="save-upstream" class="btn primary">\u4FDD\u5B58\u5E76\u70ED\u66F4\u65B0</button><button id="test-upstream" class="btn">\u6D4B\u8BD5\u8FDE\u63A5</button></div></div><div class="card"><div class="card-head"><h2>\u6309 URL \u8986\u76D6</h2></div><p class="status">\u4E3A\u7279\u5B9A Provider \u5355\u72EC\u8BBE\u7F6E\u4E0A\u6E38\u4EE3\u7406\uFF0C\u8986\u76D6\u5168\u5C40\u8BBE\u7F6E\u3002\u7559\u7A7A = \u7EE7\u627F\u5168\u5C40\u3002</p><div id="upstream-overrides" class="status">\u52A0\u8F7D\u4E2D</div><div class="actions"><button id="save-overrides" class="btn primary">\u4FDD\u5B58\u8986\u76D6\u5E76\u70ED\u66F4\u65B0</button></div></div></section>
|
|
51068
51128
|
<section id="page-sessions" class="page"><div class="card-head"><div><h1>\u4F1A\u8BDD</h1><p class="lead">ACP \u538B\u7F29\u72B6\u6001\u4E0E\u4E0A\u6E38\u7528\u91CF\u3002</p></div><button id="refresh-sessions" class="btn">\u5237\u65B0</button></div><div class="card"><table><thead><tr><th>\u6807\u9898</th><th>\u534F\u8BAE</th><th>\u6807\u8BC6</th><th>\u8BF7\u6C42</th><th>\u4E0A\u4E0B\u6587</th><th>\u6700\u540E\u6D3B\u52A8</th></tr></thead><tbody id="sessions-body"></tbody></table></div></section>
|
|
51069
|
-
<section id="page-settings" class="page"><h1>\u9AD8\u7EA7\u8BBE\u7F6E</h1><p class="lead">\u76F4\u63A5\u7F16\u8F91 providers JSON\uFF08\u6DFB\u52A0\u65B0 Provider\u3001\u914D\u7F6E\u6A21\u578B\u4E0A\u4E0B\u6587\u7A97\u53E3\u3001\u6309 URL \u4EE3\u7406\u7B49\uFF09\uFF1B\u4FDD\u5B58\u540E\u7ACB\u5373\u70ED\u66F4\u65B0\u3002</p><div class="card"><div class="field"><label>providers JSON</label><textarea id="providers-json">{}</textarea></div><div class="actions"><button id="save-providers" class="btn primary">\u4FDD\u5B58\u5E76\u5E94\u7528</button></div></div><div class="card"><div class="card-head"><h2>\u538B\u7F29\u53C2\u6570\uFF08\u5168\u5C40\uFF09</h2></div><p class="status">\u5168\u5C40 compress \u914D\u7F6E\uFF08modelContextLimit / maxContextLimit / emergencyThresholdPercent / nudgeGrowthTokens / preserveRecentMessages / preserveRecentTokens / minCompressRange / tiers / injectTool / injectNudge\uFF09\u3002\u4FDD\u5B58\u540E\u70ED\u66F4\u65B0\uFF0C\u65E0\u9700\u91CD\u542F\u3002\u7F6E\u7A7A = \u6062\u590D\u9ED8\u8BA4\u3002</p><div class="field"><label>compress JSON</label><textarea id="compress-json" placeholder='{"modelContextLimit": 200000}'></textarea></div><div class="actions"><button id="save-compress" class="btn primary">\u4FDD\u5B58\u5E76\u70ED\u66F4\u65B0</button></div></div></section></main></div><div id="toast" class="toast"></div><script>${WEB_CLIENT}</script></body></html>`;
|
|
51129
|
+
<section id="page-settings" class="page"><h1>\u9AD8\u7EA7\u8BBE\u7F6E</h1><p class="lead">\u76F4\u63A5\u7F16\u8F91 providers JSON\uFF08\u6DFB\u52A0\u65B0 Provider\u3001\u914D\u7F6E\u6A21\u578B\u4E0A\u4E0B\u6587\u7A97\u53E3\u3001\u6309 URL \u4EE3\u7406\u7B49\uFF09\uFF1B\u4FDD\u5B58\u540E\u7ACB\u5373\u70ED\u66F4\u65B0\u3002</p><div class="card"><div class="field"><label>providers JSON</label><textarea id="providers-json">{}</textarea></div><div class="actions"><button id="save-providers" class="btn primary">\u4FDD\u5B58\u5E76\u5E94\u7528</button></div></div><div class="card"><div class="card-head"><h2>\u538B\u7F29\u53C2\u6570\uFF08\u5168\u5C40\uFF09</h2></div><p class="status">\u5168\u5C40 compress \u914D\u7F6E\uFF08modelContextLimit / maxContextLimit / emergencyThresholdPercent / nudgeGrowthTokens / preserveRecentMessages / preserveRecentTokens / minCompressRangeChars\uFF08\u5355\u4F4D\uFF1A\u5B57\u7B26\uFF0C\u65E7\u540D minCompressRange \u517C\u5BB9\uFF09 / tiers / injectTool / injectNudge\uFF09\u3002\u4FDD\u5B58\u540E\u70ED\u66F4\u65B0\uFF0C\u65E0\u9700\u91CD\u542F\u3002\u7F6E\u7A7A = \u6062\u590D\u9ED8\u8BA4\u3002</p><div class="field"><label>compress JSON</label><textarea id="compress-json" placeholder='{"modelContextLimit": 200000}'></textarea></div><div class="actions"><button id="save-compress" class="btn primary">\u4FDD\u5B58\u5E76\u70ED\u66F4\u65B0</button></div></div></section></main></div><div id="toast" class="toast"></div><script>${WEB_CLIENT}</script></body></html>`;
|
|
51070
51130
|
}
|
|
51071
51131
|
|
|
51072
51132
|
// src/web/api.ts
|
|
@@ -54353,10 +54413,14 @@ function handlePluginStatus(conversationId2, res, fallbackLatest = false) {
|
|
|
54353
54413
|
let session = entry ? peekSession(entry.sessionId) : void 0;
|
|
54354
54414
|
let viaFallback = false;
|
|
54355
54415
|
if ((!entry || !session) && fallbackLatest) {
|
|
54356
|
-
const latest = listSessions().filter((s3) =>
|
|
54416
|
+
const latest = listSessions().filter((s3) => s3.restored !== true).sort((a, b2) => (b2.lastSeen ?? 0) - (a.lastSeen ?? 0))[0];
|
|
54357
54417
|
if (latest) {
|
|
54358
54418
|
session = latest;
|
|
54359
54419
|
viaFallback = true;
|
|
54420
|
+
} else {
|
|
54421
|
+
res.writeHead(404, { "content-type": "application/json" });
|
|
54422
|
+
res.end(JSON.stringify({ ok: false, error: "no session with activity since boot \u2014 issue a model request or pass the conversation id" }));
|
|
54423
|
+
return;
|
|
54360
54424
|
}
|
|
54361
54425
|
}
|
|
54362
54426
|
if (!session) {
|
|
@@ -54563,6 +54627,12 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
|
|
|
54563
54627
|
return new Promise((r) => res.once("drain", () => r()));
|
|
54564
54628
|
}
|
|
54565
54629
|
};
|
|
54630
|
+
const settleUsage = () => {
|
|
54631
|
+
if (session && (acc.inputTokens !== void 0 || acc.outputTokens !== void 0 || acc.cachedTokens !== void 0)) {
|
|
54632
|
+
applyUsageSample(session, acc, protocol);
|
|
54633
|
+
markDirty(session);
|
|
54634
|
+
}
|
|
54635
|
+
};
|
|
54566
54636
|
const pushField = (field, index, text) => {
|
|
54567
54637
|
const s3 = filterFor(field, index);
|
|
54568
54638
|
const clean = s3.filter.push(text);
|
|
@@ -54668,10 +54738,14 @@ data: ${JSON.stringify({ type: "content_block_delta", index, delta: { type: delt
|
|
|
54668
54738
|
}
|
|
54669
54739
|
const rest = flushTails();
|
|
54670
54740
|
if (rest.length > 0 && !res.destroyed && !res.writableEnded) await write(rest);
|
|
54671
|
-
|
|
54672
|
-
|
|
54673
|
-
|
|
54741
|
+
settleUsage();
|
|
54742
|
+
} catch (e) {
|
|
54743
|
+
settleUsage();
|
|
54744
|
+
if (res.destroyed || res.writableEnded) {
|
|
54745
|
+
log2?.("client aborted mid-stream");
|
|
54746
|
+
return;
|
|
54674
54747
|
}
|
|
54748
|
+
throw e;
|
|
54675
54749
|
} finally {
|
|
54676
54750
|
reader.releaseLock();
|
|
54677
54751
|
res.end();
|
|
@@ -54705,6 +54779,12 @@ async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
|
|
|
54705
54779
|
return new Promise((r) => res.once("drain", () => r()));
|
|
54706
54780
|
}
|
|
54707
54781
|
};
|
|
54782
|
+
const settleUsage = () => {
|
|
54783
|
+
if (session && (acc.inputTokens !== void 0 || acc.outputTokens !== void 0 || acc.cachedTokens !== void 0)) {
|
|
54784
|
+
applyUsageSample(session, acc, "responses");
|
|
54785
|
+
markDirty(session);
|
|
54786
|
+
}
|
|
54787
|
+
};
|
|
54708
54788
|
let lastDeltaMeta = null;
|
|
54709
54789
|
const flushTail = (after) => {
|
|
54710
54790
|
const tail = tagFilter.flush();
|
|
@@ -54777,10 +54857,14 @@ async function pipePluginResponsesWithStrip(stream2, res, session, log2) {
|
|
|
54777
54857
|
const rest = flushTail("");
|
|
54778
54858
|
if (rest.length > 0) await write(rest);
|
|
54779
54859
|
}
|
|
54780
|
-
|
|
54781
|
-
|
|
54782
|
-
|
|
54860
|
+
settleUsage();
|
|
54861
|
+
} catch (e) {
|
|
54862
|
+
settleUsage();
|
|
54863
|
+
if (res.destroyed || res.writableEnded) {
|
|
54864
|
+
log2?.("client aborted mid-stream");
|
|
54865
|
+
return;
|
|
54783
54866
|
}
|
|
54867
|
+
throw e;
|
|
54784
54868
|
} finally {
|
|
54785
54869
|
reader.releaseLock();
|
|
54786
54870
|
res.end();
|
|
@@ -54806,10 +54890,16 @@ function rebuildEvent(rawEvent, ev) {
|
|
|
54806
54890
|
async function pipePluginJson(stream2, res, session, protocol) {
|
|
54807
54891
|
const reader = stream2.getReader();
|
|
54808
54892
|
const chunks = [];
|
|
54809
|
-
|
|
54810
|
-
|
|
54811
|
-
|
|
54812
|
-
|
|
54893
|
+
try {
|
|
54894
|
+
for (; ; ) {
|
|
54895
|
+
const { done, value } = await reader.read();
|
|
54896
|
+
if (done) break;
|
|
54897
|
+
if (value && value.length > 0) chunks.push(Buffer.from(value));
|
|
54898
|
+
}
|
|
54899
|
+
} catch (e) {
|
|
54900
|
+
if (!res.writableEnded) res.end();
|
|
54901
|
+
if (res.destroyed || res.writableEnded) return;
|
|
54902
|
+
throw e;
|
|
54813
54903
|
}
|
|
54814
54904
|
reader.releaseLock();
|
|
54815
54905
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
@@ -56460,7 +56550,10 @@ async function startServer(opts) {
|
|
|
56460
56550
|
await handle(req, res, opts, core, config, log2, instanceId, instanceStartedAt);
|
|
56461
56551
|
} catch (err2) {
|
|
56462
56552
|
const msg = String(err2);
|
|
56463
|
-
|
|
56553
|
+
const e = err2;
|
|
56554
|
+
const clientAbort = (e?.name === "AbortError" || /abort/i.test(String(e?.message ?? ""))) && (res.destroyed || res.writableEnded);
|
|
56555
|
+
if (clientAbort) log2("info", `client aborted mid-stream: ${msg}`);
|
|
56556
|
+
else log2("error", msg);
|
|
56464
56557
|
if (!res.headersSent) {
|
|
56465
56558
|
const status = msg.includes("exceeds") ? 413 : 502;
|
|
56466
56559
|
res.writeHead(status, { "content-type": "application/json" });
|
|
@@ -57012,6 +57105,21 @@ ${bodyBuffer.toString("utf8")}`);
|
|
|
57012
57105
|
try {
|
|
57013
57106
|
await withSessionLock(session, async () => {
|
|
57014
57107
|
const runPrepare = () => countTokens ? prepareCountTokens(parsed, core, reqConfig, log2, session) : protocol === "anthropic" ? prepareAnthropic(parsed, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode) : protocol === "openai" ? prepareOpenai(parsed, req, opts, core, reqConfig, reqPrompts, log2, session, pluginMode, nativeWindow) : responsesCompact ? prepareResponsesCompact(bodyBuffer, parsed, session, req, core, reqConfig, log2) : prepareResponses(parsed, req, opts, core, reqConfig, reqPrompts, log2, session, responsesIdentity, pluginMode, upstreamOrigin, nativeWindow);
|
|
57108
|
+
const isCodexCompactTrigger = protocol === "responses" && !responsesCompact && isCodexClient(req.headers) && hasCompactionTrigger(parsed.input);
|
|
57109
|
+
if (isCodexCompactTrigger) {
|
|
57110
|
+
const mode = codexCompactMode();
|
|
57111
|
+
const gatePre = codexCompactGatePre(session, reqConfig.modelContextLimit);
|
|
57112
|
+
if (mode === "intercept" && gatePre) prepared = runPrepare();
|
|
57113
|
+
if (prepared?.codexForge) {
|
|
57114
|
+
await forward(req, res, opts, prepared.body, prepared, core, reqConfig, log2, route, instanceId, affinity);
|
|
57115
|
+
rememberPluginMessages(sessionId, prepared.processedMessages, prepared.originalMessages, prepared.nudge);
|
|
57116
|
+
return;
|
|
57117
|
+
}
|
|
57118
|
+
const why = mode !== "intercept" ? "BILI_CODEX_COMPACT=pass" : !gatePre ? "gate preconditions not met" : "transform/forge failed";
|
|
57119
|
+
log2("info", `[${session.id}] codex compaction_trigger request not intercepted (${why}) \u2014 forwarding verbatim (no preflight, no rebuild, no window clamp)`);
|
|
57120
|
+
await forward(req, res, opts, bodyBuffer, null, core, reqConfig, log2, route, instanceId, affinity);
|
|
57121
|
+
return;
|
|
57122
|
+
}
|
|
57015
57123
|
prepared = runPrepare();
|
|
57016
57124
|
if (!countTokens && !responsesCompact) {
|
|
57017
57125
|
const outcome = await preflightCompressIfNeeded(
|
|
@@ -57457,7 +57565,7 @@ function prepareResponsesCompact(body, parsed, session, req, core, config, log2)
|
|
|
57457
57565
|
compressInjected: false,
|
|
57458
57566
|
resetAfterSuccess: true
|
|
57459
57567
|
};
|
|
57460
|
-
if (codexCompactMode() !== "intercept" || !isCodexClient(req.headers) || !Array.isArray(parsed.input)) {
|
|
57568
|
+
if (codexCompactMode() !== "intercept" || !isCodexClient(req.headers) || !Array.isArray(parsed.input) || !codexCompactGatePre(session, config.modelContextLimit)) {
|
|
57461
57569
|
return base;
|
|
57462
57570
|
}
|
|
57463
57571
|
const prevState = session.state;
|
|
@@ -57849,187 +57957,205 @@ ${hdrText}
|
|
|
57849
57957
|
return;
|
|
57850
57958
|
}
|
|
57851
57959
|
if (prepared?.pluginMode) {
|
|
57852
|
-
|
|
57853
|
-
|
|
57854
|
-
|
|
57855
|
-
|
|
57856
|
-
|
|
57857
|
-
|
|
57858
|
-
|
|
57859
|
-
|
|
57860
|
-
|
|
57861
|
-
|
|
57862
|
-
|
|
57863
|
-
|
|
57864
|
-
|
|
57865
|
-
|
|
57866
|
-
const resolvedBuf = await resolveFakeCompletion(upstream.body, {
|
|
57867
|
-
protocol: prepared.protocol,
|
|
57868
|
-
body,
|
|
57869
|
-
upstreamUrl,
|
|
57870
|
-
reqHeaders: buildForwardHeaders(headers),
|
|
57871
|
-
proxyUrl,
|
|
57872
|
-
signal: clientAbort.signal,
|
|
57873
|
-
session: prepared.session,
|
|
57874
|
-
log: log2
|
|
57875
|
-
});
|
|
57876
|
-
responseBody = bufferToStream(resolvedBuf);
|
|
57877
|
-
}
|
|
57878
|
-
const useRewriter = prepared !== null && prepared.compressInjected && prepared.processedMessages.length > 0;
|
|
57879
|
-
if (!useRewriter || prepared === null) {
|
|
57880
|
-
if (prepared && prepared.resetAfterSuccess) {
|
|
57881
|
-
const [toClient, toObserve] = responseBody.tee();
|
|
57882
|
-
const observed = observeResponsesTerminalState(toObserve, prepared.stream);
|
|
57883
|
-
const tagLog = (msg) => log2("info", `[${prepared.session.id}] ${msg}`);
|
|
57884
|
-
if ((upstream.headers.get("content-type") ?? "").includes("text/event-stream")) {
|
|
57885
|
-
await pipePluginResponsesWithStrip(toClient, res, void 0, tagLog);
|
|
57886
|
-
} else {
|
|
57887
|
-
await pipeThrough(toClient, res);
|
|
57888
|
-
}
|
|
57889
|
-
clearUpstreamTimer();
|
|
57890
|
-
const terminal = await observed;
|
|
57891
|
-
if (terminal === "completed") {
|
|
57892
|
-
markNativeCompactionBoundary(prepared.session);
|
|
57893
|
-
log2("info", `[${prepared.session.id}] native Responses compact completed; rebase scheduled for next Responses turn`);
|
|
57894
|
-
} else {
|
|
57895
|
-
log2("warn", `[${prepared.session.id}] native compact response terminal=${terminal}; rebase NOT scheduled`);
|
|
57960
|
+
try {
|
|
57961
|
+
let pluginBody = upstream.body;
|
|
57962
|
+
if (prepared.stream && maxFakeCompletionRetries() > 0) {
|
|
57963
|
+
const resolvedBuf = await resolveFakeCompletion(pluginBody, {
|
|
57964
|
+
protocol: prepared.protocol,
|
|
57965
|
+
body,
|
|
57966
|
+
upstreamUrl,
|
|
57967
|
+
reqHeaders: buildForwardHeaders(headers),
|
|
57968
|
+
proxyUrl,
|
|
57969
|
+
signal: clientAbort.signal,
|
|
57970
|
+
session: prepared.session,
|
|
57971
|
+
log: log2
|
|
57972
|
+
});
|
|
57973
|
+
pluginBody = bufferToStream(resolvedBuf);
|
|
57896
57974
|
}
|
|
57897
|
-
|
|
57898
|
-
|
|
57899
|
-
|
|
57900
|
-
|
|
57901
|
-
|
|
57975
|
+
if (prepared.stream) {
|
|
57976
|
+
if (prepared.protocol === "responses") {
|
|
57977
|
+
await pipePluginResponsesWithStrip(pluginBody, res, prepared.session, (msg) => log2("info", `[${prepared.session.id}] ${msg}`));
|
|
57978
|
+
} else {
|
|
57979
|
+
await pipePluginChatWithStrip(pluginBody, res, prepared.protocol, prepared.session, (msg) => log2("info", `[${prepared.session.id}] ${msg}`));
|
|
57980
|
+
}
|
|
57902
57981
|
} else {
|
|
57903
|
-
await
|
|
57982
|
+
await pipePluginJson(pluginBody, res, prepared.session, prepared.protocol);
|
|
57904
57983
|
}
|
|
57905
|
-
|
|
57906
|
-
} else if (prepared && (upstream.headers.get("content-type") ?? "").includes("application/json")) {
|
|
57907
|
-
await pipePluginJson(responseBody, res, void 0, prepared.protocol);
|
|
57908
|
-
clearUpstreamTimer();
|
|
57909
|
-
} else {
|
|
57910
|
-
await pipeThrough(responseBody, res);
|
|
57984
|
+
} finally {
|
|
57911
57985
|
clearUpstreamTimer();
|
|
57912
57986
|
}
|
|
57913
57987
|
return;
|
|
57914
57988
|
}
|
|
57915
|
-
|
|
57916
|
-
|
|
57917
|
-
|
|
57918
|
-
|
|
57919
|
-
|
|
57920
|
-
|
|
57921
|
-
|
|
57922
|
-
|
|
57923
|
-
|
|
57924
|
-
|
|
57925
|
-
|
|
57926
|
-
|
|
57927
|
-
|
|
57928
|
-
|
|
57929
|
-
|
|
57930
|
-
|
|
57931
|
-
|
|
57932
|
-
|
|
57933
|
-
|
|
57934
|
-
|
|
57935
|
-
|
|
57936
|
-
|
|
57937
|
-
|
|
57938
|
-
|
|
57939
|
-
|
|
57940
|
-
state: prepared.session.state,
|
|
57941
|
-
config,
|
|
57942
|
-
tokenCount: prepared.session.stats.lastInputTokens,
|
|
57943
|
-
renderTags: prepared.renderTags ?? "text-only"
|
|
57944
|
-
});
|
|
57945
|
-
prepared.session.state = turn.state;
|
|
57946
|
-
const records = current.filter((m2) => typeof m2.id === "string" && m2.id.startsWith("acp_loop_"));
|
|
57947
|
-
return stripKernelSummaries([...turn.messages, ...records], turn.state);
|
|
57948
|
-
};
|
|
57949
|
-
const loop = runCompressLoop(
|
|
57950
|
-
streamToRead,
|
|
57951
|
-
{ 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, nudge: prepared.nudge, refreshFolded },
|
|
57952
|
-
parsedReq,
|
|
57953
|
-
{ url: upstreamUrl, headers: reqHeaders },
|
|
57954
|
-
adapter,
|
|
57955
|
-
systemPrompt,
|
|
57956
|
-
clientAbort.signal
|
|
57957
|
-
);
|
|
57958
|
-
let protocolFragmentWarned = false;
|
|
57959
|
-
for await (const chunk of loop) {
|
|
57960
|
-
if (res.destroyed || res.writableEnded) break;
|
|
57961
|
-
{
|
|
57962
|
-
const s3 = chunk.toString("utf8");
|
|
57963
|
-
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
57964
|
-
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains <acp tag`);
|
|
57965
|
-
} else if (!protocolFragmentWarned && containsToolCallXmlFragment(s3)) {
|
|
57966
|
-
protocolFragmentWarned = true;
|
|
57967
|
-
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains tool-call XML fragment (possible tag echo; not stripped)`);
|
|
57968
|
-
}
|
|
57969
|
-
}
|
|
57970
|
-
res.write(chunk);
|
|
57971
|
-
if (res.writableNeedDrain) {
|
|
57972
|
-
await Promise.race([
|
|
57973
|
-
new Promise((r) => res.once("drain", () => r())),
|
|
57974
|
-
new Promise((r) => res.once("close", () => r()))
|
|
57975
|
-
]);
|
|
57989
|
+
let responseBody = upstream.body;
|
|
57990
|
+
try {
|
|
57991
|
+
if (prepared !== null && prepared.stream && maxFakeCompletionRetries() > 0) {
|
|
57992
|
+
const resolvedBuf = await resolveFakeCompletion(upstream.body, {
|
|
57993
|
+
protocol: prepared.protocol,
|
|
57994
|
+
body,
|
|
57995
|
+
upstreamUrl,
|
|
57996
|
+
reqHeaders: buildForwardHeaders(headers),
|
|
57997
|
+
proxyUrl,
|
|
57998
|
+
signal: clientAbort.signal,
|
|
57999
|
+
session: prepared.session,
|
|
58000
|
+
log: log2
|
|
58001
|
+
});
|
|
58002
|
+
responseBody = bufferToStream(resolvedBuf);
|
|
58003
|
+
}
|
|
58004
|
+
const useRewriter = prepared !== null && prepared.compressInjected && prepared.processedMessages.length > 0;
|
|
58005
|
+
if (!useRewriter || prepared === null) {
|
|
58006
|
+
if (prepared && prepared.resetAfterSuccess) {
|
|
58007
|
+
const [toClient, toObserve] = responseBody.tee();
|
|
58008
|
+
const observed = observeResponsesTerminalState(toObserve, prepared.stream);
|
|
58009
|
+
const tagLog = (msg) => log2("info", `[${prepared.session.id}] ${msg}`);
|
|
58010
|
+
if ((upstream.headers.get("content-type") ?? "").includes("text/event-stream")) {
|
|
58011
|
+
await pipePluginResponsesWithStrip(toClient, res, void 0, tagLog);
|
|
58012
|
+
} else {
|
|
58013
|
+
await pipeThrough(toClient, res);
|
|
57976
58014
|
}
|
|
57977
|
-
|
|
58015
|
+
const terminal = await observed;
|
|
58016
|
+
if (terminal === "completed") {
|
|
58017
|
+
markNativeCompactionBoundary(prepared.session);
|
|
58018
|
+
log2("info", `[${prepared.session.id}] native Responses compact completed; rebase scheduled for next Responses turn`);
|
|
58019
|
+
} else {
|
|
58020
|
+
log2("warn", `[${prepared.session.id}] native compact response terminal=${terminal}; rebase NOT scheduled`);
|
|
58021
|
+
}
|
|
58022
|
+
} else if (prepared && prepared.stream && (upstream.headers.get("content-type") ?? "").includes("text/event-stream")) {
|
|
58023
|
+
const p2 = prepared;
|
|
58024
|
+
const tagLog = (msg) => log2("info", `[${p2.session.id}] ${msg}`);
|
|
58025
|
+
if (p2.protocol === "responses") {
|
|
58026
|
+
await pipePluginResponsesWithStrip(responseBody, res, void 0, tagLog);
|
|
58027
|
+
} else {
|
|
58028
|
+
await pipePluginChatWithStrip(responseBody, res, p2.protocol, void 0, tagLog);
|
|
58029
|
+
}
|
|
58030
|
+
} else if (prepared && (upstream.headers.get("content-type") ?? "").includes("application/json")) {
|
|
58031
|
+
await pipePluginJson(responseBody, res, void 0, prepared.protocol);
|
|
58032
|
+
} else {
|
|
58033
|
+
await pipeThrough(responseBody, res);
|
|
57978
58034
|
}
|
|
57979
|
-
|
|
57980
|
-
} catch (e) {
|
|
57981
|
-
emitStreamError(res, prepared.protocol, e?.message ?? String(e), (m2) => log2("error", `[${prepared.session.id}] ${m2}`));
|
|
57982
|
-
} finally {
|
|
57983
|
-
clearUpstreamTimer();
|
|
57984
|
-
if (dumpRaw) await dumpRaw;
|
|
58035
|
+
return;
|
|
57985
58036
|
}
|
|
57986
|
-
|
|
57987
|
-
|
|
57988
|
-
|
|
57989
|
-
|
|
58037
|
+
const ctx = {
|
|
58038
|
+
core,
|
|
58039
|
+
config,
|
|
58040
|
+
messages: prepared.originalMessages,
|
|
58041
|
+
session: prepared.session,
|
|
58042
|
+
log: (msg) => log2("info", `[${prepared.session.id}] ${msg}`),
|
|
58043
|
+
debug: opts.debug
|
|
58044
|
+
};
|
|
58045
|
+
if (prepared.stream) {
|
|
58046
|
+
let streamToRead = responseBody;
|
|
58047
|
+
let dumpRaw;
|
|
58048
|
+
if (opts.dumpSse) {
|
|
58049
|
+
const [a, b2] = responseBody.tee();
|
|
58050
|
+
streamToRead = a;
|
|
58051
|
+
dumpRaw = dumpStreamToFile(b2, opts.dumpSse, `${Date.now()}-${safeSessionId(prepared.session.id)}-raw.sse`);
|
|
58052
|
+
}
|
|
57990
58053
|
try {
|
|
57991
|
-
|
|
57992
|
-
|
|
57993
|
-
|
|
57994
|
-
|
|
57995
|
-
|
|
57996
|
-
|
|
57997
|
-
|
|
57998
|
-
|
|
57999
|
-
|
|
58000
|
-
|
|
58001
|
-
|
|
58002
|
-
|
|
58003
|
-
|
|
58004
|
-
|
|
58005
|
-
|
|
58006
|
-
|
|
58007
|
-
|
|
58008
|
-
|
|
58009
|
-
|
|
58010
|
-
|
|
58011
|
-
|
|
58012
|
-
|
|
58054
|
+
const parsedReq = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
58055
|
+
const reqHeaders = buildForwardHeaders(headers);
|
|
58056
|
+
const textProtocol = prepared.protocol === "responses" && !!prepared.responsesTextProtocol;
|
|
58057
|
+
const systemPrompt = textProtocol ? buildCompressHybridSystemPrompt(prepared.prompts ?? defaultPrompts) : buildCompressSystemPrompt(prepared.prompts ?? defaultPrompts);
|
|
58058
|
+
const adapter = pickAdapter(prepared.protocol, parsedReq, textProtocol, prepared.responsesProjection, prepared.anthropicSystem, prepared.openaiSystemText);
|
|
58059
|
+
const refreshFolded = (current) => {
|
|
58060
|
+
const turn = core.processTurn({
|
|
58061
|
+
messages: prepared.originalMessages,
|
|
58062
|
+
state: prepared.session.state,
|
|
58063
|
+
config,
|
|
58064
|
+
tokenCount: prepared.session.stats.lastInputTokens,
|
|
58065
|
+
renderTags: prepared.renderTags ?? "text-only"
|
|
58066
|
+
});
|
|
58067
|
+
prepared.session.state = turn.state;
|
|
58068
|
+
const records = current.filter((m2) => typeof m2.id === "string" && m2.id.startsWith("acp_loop_"));
|
|
58069
|
+
return stripKernelSummaries([...turn.messages, ...records], turn.state);
|
|
58070
|
+
};
|
|
58071
|
+
const loop = runCompressLoop(
|
|
58072
|
+
streamToRead,
|
|
58073
|
+
{ 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, nudge: prepared.nudge, refreshFolded },
|
|
58074
|
+
parsedReq,
|
|
58075
|
+
{ url: upstreamUrl, headers: reqHeaders },
|
|
58076
|
+
adapter,
|
|
58077
|
+
systemPrompt,
|
|
58078
|
+
clientAbort.signal
|
|
58079
|
+
);
|
|
58080
|
+
let protocolFragmentWarned = false;
|
|
58081
|
+
for await (const chunk of loop) {
|
|
58082
|
+
if (res.destroyed || res.writableEnded) break;
|
|
58083
|
+
{
|
|
58084
|
+
const s3 = chunk.toString("utf8");
|
|
58085
|
+
if (s3.includes("<acp ") || s3.includes("</acp")) {
|
|
58086
|
+
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains <acp tag`);
|
|
58087
|
+
} else if (!protocolFragmentWarned && containsToolCallXmlFragment(s3)) {
|
|
58088
|
+
protocolFragmentWarned = true;
|
|
58089
|
+
log2("warn", `[${prepared.session.id}] tag echo: ${prepared.protocol} response stream contains tool-call XML fragment (possible tag echo; not stripped)`);
|
|
58090
|
+
}
|
|
58091
|
+
}
|
|
58092
|
+
res.write(chunk);
|
|
58093
|
+
if (res.writableNeedDrain) {
|
|
58094
|
+
await Promise.race([
|
|
58095
|
+
new Promise((r) => res.once("drain", () => r())),
|
|
58096
|
+
new Promise((r) => res.once("close", () => r()))
|
|
58097
|
+
]);
|
|
58013
58098
|
}
|
|
58014
|
-
|
|
58015
|
-
if (typeof out === "number") prepared.session.stats.outputTokens += out;
|
|
58099
|
+
if (res.destroyed || res.writableEnded) break;
|
|
58016
58100
|
}
|
|
58017
|
-
|
|
58018
|
-
|
|
58019
|
-
|
|
58020
|
-
|
|
58021
|
-
|
|
58022
|
-
|
|
58101
|
+
res.end();
|
|
58102
|
+
} catch (e) {
|
|
58103
|
+
emitStreamError(res, prepared.protocol, e?.message ?? String(e), (m2) => log2("error", `[${prepared.session.id}] ${m2}`));
|
|
58104
|
+
} finally {
|
|
58105
|
+
clearUpstreamTimer();
|
|
58106
|
+
if (dumpRaw) await dumpRaw;
|
|
58107
|
+
markDirty(prepared.session);
|
|
58108
|
+
}
|
|
58109
|
+
} else {
|
|
58110
|
+
try {
|
|
58111
|
+
const buf = await upstream.arrayBuffer();
|
|
58112
|
+
const text = Buffer.from(buf).toString("utf8");
|
|
58113
|
+
try {
|
|
58114
|
+
let json = JSON.parse(text);
|
|
58115
|
+
if (prepared.protocol === "responses" && prepared.responsesTextProtocol) {
|
|
58116
|
+
const requestBody = JSON.parse(typeof body === "string" ? body : body.toString("utf8"));
|
|
58117
|
+
const requestHeaders = buildForwardHeaders(headers);
|
|
58118
|
+
json = await compressLoopResponsesJson(
|
|
58119
|
+
json,
|
|
58120
|
+
{ core, config, messages: prepared.originalMessages, session: prepared.session, log: ctx.log, proxyUrl, textProtocol: true },
|
|
58121
|
+
requestBody,
|
|
58122
|
+
{ url: upstreamUrl, headers: requestHeaders }
|
|
58123
|
+
);
|
|
58124
|
+
}
|
|
58125
|
+
const u2 = json.usage ?? {};
|
|
58126
|
+
const { total, cached } = usageTotals(prepared.protocol, u2);
|
|
58127
|
+
if (typeof total === "number") {
|
|
58128
|
+
prepared.session.stats.inputTokens += total;
|
|
58129
|
+
prepared.session.stats.lastInputTokens = Math.max(
|
|
58130
|
+
0,
|
|
58131
|
+
total - (prepared.session.stats.compressCreditTokens ?? 0)
|
|
58132
|
+
);
|
|
58133
|
+
if (typeof cached === "number") {
|
|
58134
|
+
prepared.session.stats.cachedTokens += cached;
|
|
58135
|
+
prepared.session.stats.cacheSamples += 1;
|
|
58136
|
+
}
|
|
58137
|
+
const out = u2.completion_tokens ?? u2.output_tokens;
|
|
58138
|
+
if (typeof out === "number") prepared.session.stats.outputTokens += out;
|
|
58139
|
+
}
|
|
58140
|
+
if (prepared.protocol === "openai") {
|
|
58141
|
+
rewriteOpenaiJsonResponse(json, ctx);
|
|
58142
|
+
} else if (prepared.protocol === "responses") {
|
|
58143
|
+
rewriteResponsesJsonResponse(json, ctx);
|
|
58144
|
+
} else {
|
|
58145
|
+
rewriteJsonResponse(json, ctx);
|
|
58146
|
+
}
|
|
58147
|
+
res.end(JSON.stringify(json));
|
|
58148
|
+
} catch {
|
|
58149
|
+
res.end(text);
|
|
58023
58150
|
}
|
|
58024
|
-
|
|
58025
|
-
|
|
58026
|
-
|
|
58151
|
+
} finally {
|
|
58152
|
+
clearUpstreamTimer();
|
|
58153
|
+
markDirty(prepared.session);
|
|
58027
58154
|
}
|
|
58028
|
-
} finally {
|
|
58029
|
-
clearUpstreamTimer();
|
|
58030
58155
|
}
|
|
58156
|
+
} finally {
|
|
58157
|
+
clearUpstreamTimer();
|
|
58031
58158
|
}
|
|
58032
|
-
markDirty(prepared.session);
|
|
58033
58159
|
}
|
|
58034
58160
|
async function readStreamToBuffer(stream2, maxBytes = 1 << 20) {
|
|
58035
58161
|
const reader = stream2.getReader();
|
|
@@ -58174,7 +58300,8 @@ function sendStats(res) {
|
|
|
58174
58300
|
outputTokens: s3.stats.outputTokens,
|
|
58175
58301
|
cacheSamples: s3.stats.cacheSamples,
|
|
58176
58302
|
cacheHitPct: s3.stats.cacheSamples > 0 && s3.stats.inputTokens > 0 ? Math.round(s3.stats.cachedTokens / s3.stats.inputTokens * 100) : null,
|
|
58177
|
-
lastSeen: new Date(s3.lastSeen).toISOString()
|
|
58303
|
+
lastSeen: new Date(s3.lastSeen).toISOString(),
|
|
58304
|
+
restored: s3.restored === true
|
|
58178
58305
|
}));
|
|
58179
58306
|
res.writeHead(200, { "content-type": "application/json" });
|
|
58180
58307
|
res.end(JSON.stringify({ sessions: sessions2 }, null, 2));
|