billion-context 0.1.75-pr.489.307 → 0.1.75-pr.490.308

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -47856,7 +47856,7 @@ function parseCompressSettings(v2) {
47856
47856
  }
47857
47857
  out[key] = typeof obj[key] === "string" ? obj[key].trim() : obj[key];
47858
47858
  }
47859
- for (const key of ["nudgeGrowthTokens", "preserveRecentMessages", "preserveRecentTokens", "minCompressRange"]) {
47859
+ for (const key of ["nudgeGrowthTokens", "preserveRecentMessages", "preserveRecentTokens", "minCompressRange", "minCompressRangeChars"]) {
47860
47860
  takeNumber(key);
47861
47861
  }
47862
47862
  if ("tiers" in obj) {
@@ -47922,6 +47922,7 @@ function resolveContextLimitValue(raw, nativeLimit) {
47922
47922
  function mergeCompress(global2, provider, model) {
47923
47923
  const pick = (k2) => model?.[k2] ?? provider?.[k2] ?? global2?.[k2];
47924
47924
  const promptLevels = [global2?.prompts, provider?.prompts, model?.prompts].filter(Boolean);
47925
+ const rangeOf = (s3) => s3?.minCompressRangeChars ?? s3?.minCompressRange;
47925
47926
  return {
47926
47927
  modelContextLimit: pick("modelContextLimit"),
47927
47928
  maxContextLimit: pick("maxContextLimit"),
@@ -47929,7 +47930,7 @@ function mergeCompress(global2, provider, model) {
47929
47930
  nudgeGrowthTokens: pick("nudgeGrowthTokens"),
47930
47931
  preserveRecentMessages: pick("preserveRecentMessages"),
47931
47932
  preserveRecentTokens: pick("preserveRecentTokens"),
47932
- minCompressRange: pick("minCompressRange"),
47933
+ minCompressRangeChars: rangeOf(model) ?? rangeOf(provider) ?? rangeOf(global2),
47933
47934
  tiers: pick("tiers"),
47934
47935
  prompts: promptLevels.length > 0 ? Object.assign({}, ...promptLevels) : void 0,
47935
47936
  acknowledgePromptsRisk: pick("acknowledgePromptsRisk")
@@ -47983,7 +47984,7 @@ function applyCompressSettings(base, limit, s3) {
47983
47984
  preserveRecentTokens: s3.preserveRecentTokens ?? base.preserveRecentTokens,
47984
47985
  compress: {
47985
47986
  ...base.compress,
47986
- minCompressRange: s3.minCompressRange ?? base.compress.minCompressRange
47987
+ minCompressRange: s3.minCompressRangeChars ?? s3.minCompressRange ?? base.compress.minCompressRange
47987
47988
  }
47988
47989
  };
47989
47990
  }
@@ -48165,6 +48166,7 @@ var CACHE_FILE = path3.join(cacheDir(), "models-dev.json");
48165
48166
  var TTL_MS = 24 * 60 * 60 * 1e3;
48166
48167
  var cache2 = null;
48167
48168
  var loading = null;
48169
+ var warnedConflicts = /* @__PURE__ */ new Set();
48168
48170
  var snapshotReg = null;
48169
48171
  var snapshotMs = 0;
48170
48172
  function bundledSnapshotRegistry() {
@@ -48337,15 +48339,22 @@ function registryLookup(reg, model, host) {
48337
48339
  }
48338
48340
  if (provider === void 0) {
48339
48341
  const suffix = `/${model}`;
48340
- let agreed;
48342
+ let max;
48343
+ const distinct = /* @__PURE__ */ new Set();
48344
+ const parts = [];
48341
48345
  for (const key of Object.keys(reg)) {
48342
48346
  if (!key.endsWith(suffix)) continue;
48343
48347
  const ctx = reg[key].limit?.context;
48344
48348
  if (typeof ctx !== "number" || ctx <= 0) continue;
48345
- if (agreed === void 0) agreed = ctx;
48346
- else if (agreed !== ctx) return void 0;
48349
+ if (max === void 0 || ctx > max) max = ctx;
48350
+ distinct.add(ctx);
48351
+ parts.push(`${key}=${ctx}`);
48352
+ }
48353
+ if (max !== void 0 && distinct.size > 1 && !warnedConflicts.has(model)) {
48354
+ warnedConflicts.add(model);
48355
+ log("warn", `[acp-registry] conflicting context windows for "${model}" (${parts.join(", ")}) \u2014 using max ${max}`);
48347
48356
  }
48348
- return agreed;
48357
+ return max;
48349
48358
  }
48350
48359
  return void 0;
48351
48360
  }
@@ -49951,7 +49960,13 @@ function buildSession(parsed) {
49951
49960
  metadata: parsed.metadata ?? {},
49952
49961
  state: mergeState(parsed.state),
49953
49962
  createdAt: parsed.createdAt ?? Date.now(),
49954
- lastSeen: Date.now(),
49963
+ // #404: lastSeen reflects the on-disk savedAt (the last real
49964
+ // activity), NOT the restore moment — a restart must not fabricate
49965
+ // activity for every session (broke fallback=latest ties, panel
49966
+ // freshness, and eviction ordering). Consumers that need "has this
49967
+ // session been used since boot" read the restored flag.
49968
+ lastSeen: parsed.savedAt ?? Date.now(),
49969
+ restored: true,
49955
49970
  blockContents,
49956
49971
  lastMessages: Array.isArray(parsed.messages) ? parsed.messages : void 0,
49957
49972
  inFlight: 0,
@@ -50006,7 +50021,8 @@ async function initSessions() {
50006
50021
  await store.migrateLegacyIds();
50007
50022
  const loaded = await store.loadAll();
50008
50023
  if (loaded.size > MAX_SESSIONS) {
50009
- const entries = [...loaded.entries()].sort((a, b2) => (b2[1].createdAt ?? 0) - (a[1].createdAt ?? 0));
50024
+ const freshness = (s3) => Math.max(s3.createdAt ?? 0, s3.lastSeen ?? 0);
50025
+ const entries = [...loaded.entries()].sort((a, b2) => freshness(b2[1]) - freshness(a[1]));
50010
50026
  for (const [id, s3] of entries) {
50011
50027
  if (sessions.size >= MAX_SESSIONS) break;
50012
50028
  sessions.set(id, s3);
@@ -50019,6 +50035,7 @@ function getSession(id, meta) {
50019
50035
  const existing = sessions.get(id);
50020
50036
  if (existing) {
50021
50037
  existing.lastSeen = Date.now();
50038
+ existing.restored = false;
50022
50039
  if (meta?.protocol && !existing.meta.protocol) existing.meta.protocol = meta.protocol;
50023
50040
  if (meta?.upstreamOrigin && !existing.meta.upstreamOrigin) existing.meta.upstreamOrigin = meta.upstreamOrigin;
50024
50041
  if (meta?.label && !existing.meta.label) existing.meta.label = meta.label;
@@ -50028,6 +50045,7 @@ function getSession(id, meta) {
50028
50045
  const reloaded = store.loadSync(id, meta);
50029
50046
  if (reloaded) {
50030
50047
  reloaded.lastSeen = Date.now();
50048
+ reloaded.restored = false;
50031
50049
  reloaded.persisted = true;
50032
50050
  sessions.set(id, reloaded);
50033
50051
  return reloaded;
@@ -50696,7 +50714,7 @@ function summaryPayload(protocol, model, system, content) {
50696
50714
  if (protocol === "openai") {
50697
50715
  return { model, max_tokens: MAX_SUMMARY_OUTPUT_TOKENS, messages: [{ role: "system", content: system }, { role: "user", content }], stream: false };
50698
50716
  }
50699
- return { model, max_output_tokens: MAX_SUMMARY_OUTPUT_TOKENS, instructions: system, input: [{ role: "user", content }], stream: false, store: false };
50717
+ return { model, max_output_tokens: MAX_SUMMARY_OUTPUT_TOKENS, instructions: system, input: [{ role: "user", content }], stream: false };
50700
50718
  }
50701
50719
  function extractSummaryText(protocol, json) {
50702
50720
  if (protocol === "anthropic") {
@@ -50762,7 +50780,7 @@ TASK: The conversation segment below (messages ${startRef}\u2013${endRef}) must
50762
50780
  var ABORTED_FAILURE = { kind: "aborted", detail: "the client disconnected during preflight compression" };
50763
50781
  async function preflightCompress(deps, messages) {
50764
50782
  const limit = deps.config.modelContextLimit;
50765
- const result = { compressedRanges: 0, savedTokens: 0, payloadEstimate: estimateCoreMessages(messages) + (deps.imageFloor ?? 0) };
50783
+ const result = { compressedRanges: 0, savedTokens: 0, payloadEstimate: estimateCoreMessages(messages) };
50766
50784
  if (limit <= 0) return result;
50767
50785
  const budget = Math.max(MIN_CHUNK_TOKENS, Math.floor(limit * CHUNK_FRACTION));
50768
50786
  const minChars = deps.config.compress.minCompressRange;
@@ -50782,8 +50800,8 @@ async function preflightCompress(deps, messages) {
50782
50800
  renderTags: "text-only"
50783
50801
  });
50784
50802
  deps.session.state = turn.state;
50785
- currentTokens = Math.max(deps.session.stats.lastInputTokens, estimateCoreMessages(turn.messages) + (deps.imageFloor ?? 0));
50786
- result.payloadEstimate = estimateCoreMessages(turn.messages) + (deps.imageFloor ?? 0);
50803
+ currentTokens = Math.max(deps.session.stats.lastInputTokens, estimateCoreMessages(turn.messages));
50804
+ result.payloadEstimate = estimateCoreMessages(turn.messages);
50787
50805
  if (startTokens < 0) startTokens = currentTokens;
50788
50806
  if (currentTokens < limit) break;
50789
50807
  const ranges = viableRanges(turn.nudge?.compressibleRanges ?? []);
@@ -50869,78 +50887,6 @@ async function preflightCompress(deps, messages) {
50869
50887
  return result;
50870
50888
  }
50871
50889
 
50872
- // src/image-tokens.ts
50873
- var REMOTE_IMAGE_TOKENS = 4096;
50874
- function imageTokenCap() {
50875
- const v2 = Number(process.env.BILI_IMAGE_TOKEN_CAP ?? "");
50876
- return Number.isInteger(v2) && v2 > 0 ? v2 : 0;
50877
- }
50878
- function applyCap(cost) {
50879
- const cap = imageTokenCap();
50880
- return cap > 0 ? Math.min(cost, cap) : cost;
50881
- }
50882
- function costForUrl(url) {
50883
- if (url.startsWith("data:")) {
50884
- const i = url.indexOf("base64,");
50885
- if (i >= 0) return applyCap(Math.ceil((url.length - i - "base64,".length) / 4));
50886
- }
50887
- return applyCap(REMOTE_IMAGE_TOKENS);
50888
- }
50889
- function isObj(v2) {
50890
- return typeof v2 === "object" && v2 !== null;
50891
- }
50892
- function urlOf(v2) {
50893
- if (typeof v2 === "string") return v2;
50894
- if (isObj(v2) && typeof v2.url === "string") return v2.url;
50895
- return void 0;
50896
- }
50897
- function imageTokensInParsedBody(protocol, body) {
50898
- if (!isObj(body)) return 0;
50899
- let total = 0;
50900
- if (protocol === "responses") {
50901
- const input = body.input;
50902
- if (!Array.isArray(input)) return 0;
50903
- for (const item of input) {
50904
- if (!isObj(item) || !Array.isArray(item.content)) continue;
50905
- for (const part of item.content) {
50906
- if (!isObj(part) || part.type !== "input_image") continue;
50907
- const url = urlOf(part.image_url);
50908
- if (url) total += costForUrl(url);
50909
- }
50910
- }
50911
- return total;
50912
- }
50913
- const messages = body.messages;
50914
- if (!Array.isArray(messages)) return 0;
50915
- for (const m2 of messages) {
50916
- if (!isObj(m2) || !Array.isArray(m2.content)) continue;
50917
- for (const part of m2.content) {
50918
- if (!isObj(part)) continue;
50919
- if (protocol === "openai") {
50920
- if (part.type !== "image_url") continue;
50921
- const url = urlOf(part.image_url);
50922
- if (url) total += costForUrl(url);
50923
- } else {
50924
- if (part.type !== "image") continue;
50925
- const src = part.source;
50926
- if (isObj(src) && src.type === "base64" && typeof src.data === "string") total += applyCap(Math.ceil(src.data.length / 4));
50927
- else if (isObj(src) && src.type === "url" && typeof src.url === "string") total += costForUrl(src.url);
50928
- }
50929
- }
50930
- }
50931
- return total;
50932
- }
50933
- function imageTokensInRawBody(protocol, raw) {
50934
- const s3 = typeof raw === "string" ? raw : raw.toString("utf8");
50935
- const probe = protocol === "responses" ? s3.includes("input_image") : protocol === "openai" ? s3.includes("image_url") : s3.includes('"type":"image"') || s3.includes('"type": "image"');
50936
- if (!probe) return 0;
50937
- try {
50938
- return imageTokensInParsedBody(protocol, JSON.parse(s3));
50939
- } catch {
50940
- return 0;
50941
- }
50942
- }
50943
-
50944
50890
  // src/web/index.ts
50945
50891
  import { readFileSync as readFileSync3 } from "fs";
50946
50892
  import { dirname as dirname4, join as join3 } from "path";
@@ -51110,7 +51056,7 @@ async function saveProviders(){const button=byId("save-providers");busy(button,t
51110
51056
  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)}}
51111
51057
  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)}}
51112
51058
  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)}}
51113
- 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}}
51059
+ 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}}
51114
51060
  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)}}
51115
51061
  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);
51116
51062
  Promise.all([loadConfig(),loadUpstream()]).catch((error)=>toast(String(error),true));setInterval(()=>{if(byId("page-sessions").classList.contains("active"))loadSessions().catch(()=>{})},5000);
@@ -51120,7 +51066,7 @@ Promise.all([loadConfig(),loadUpstream()]).catch((error)=>toast(String(error),tr
51120
51066
  var WEB_STYLES = `
51121
51067
  :root{--bg:#0f1115;--panel:#171a21;--panel2:#1e222c;--line:#2b313d;--text:#e7eaf0;--muted:#8f98a8;--blue:#6ea8fe;--green:#55c98b;--yellow:#e9b949;--red:#ef6b73;--radius:10px}
51122
51068
  *{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}
51123
- .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}
51069
+ .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}
51124
51070
  @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}}
51125
51071
  `;
51126
51072
 
@@ -51149,7 +51095,7 @@ function renderPage(origin, version2) {
51149
51095
  <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>
51150
51096
  <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>
51151
51097
  <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>
51152
- <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>`;
51098
+ <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>`;
51153
51099
  }
51154
51100
 
51155
51101
  // src/web/api.ts
@@ -54436,10 +54382,14 @@ function handlePluginStatus(conversationId2, res, fallbackLatest = false) {
54436
54382
  let session = entry ? peekSession(entry.sessionId) : void 0;
54437
54383
  let viaFallback = false;
54438
54384
  if ((!entry || !session) && fallbackLatest) {
54439
- const latest = listSessions().filter((s3) => typeof s3.lastSeen === "number").sort((a, b2) => (b2.lastSeen ?? 0) - (a.lastSeen ?? 0))[0];
54385
+ const latest = listSessions().filter((s3) => s3.restored !== true).sort((a, b2) => (b2.lastSeen ?? 0) - (a.lastSeen ?? 0))[0];
54440
54386
  if (latest) {
54441
54387
  session = latest;
54442
54388
  viaFallback = true;
54389
+ } else {
54390
+ res.writeHead(404, { "content-type": "application/json" });
54391
+ res.end(JSON.stringify({ ok: false, error: "no session with activity since boot \u2014 issue a model request or pass the conversation id" }));
54392
+ return;
54443
54393
  }
54444
54394
  }
54445
54395
  if (!session) {
@@ -57702,13 +57652,11 @@ function isPreflightFailFast(outcome) {
57702
57652
  async function preflightCompressIfNeeded(prepared, runPrepare, req, res, opts, core, config, model, route, affinity, log2, instanceId) {
57703
57653
  const session = prepared.session;
57704
57654
  const limit = config.modelContextLimit;
57705
- const imageTokens = imageTokensInRawBody(prepared.protocol, prepared.body);
57706
- const payloadEstimate = estimateCoreMessages(prepared.processedMessages) + imageTokens;
57655
+ const payloadEstimate = estimateCoreMessages(prepared.processedMessages);
57707
57656
  const tokenCount = Math.max(session.stats.lastInputTokens, payloadEstimate);
57708
57657
  if (limit <= 0 || !model || tokenCount < limit) return prepared;
57709
57658
  const failFast = (status2, detail, retryable) => {
57710
- const imageNote = imageTokens >= limit ? ` Images alone account for ~${imageTokens} tokens (\u2265 window ${limit}); compression cannot remove them \u2014 shrink or remove the images, or raise the window.` : "";
57711
- const message = `context ~${tokenCount} tokens exceeds the model window ${limit} (model=${model}) and preflight compression could not bring it under: ${detail}.` + imageNote + ` The over-window payload was NOT forwarded.`;
57659
+ const message = `context ~${tokenCount} tokens exceeds the model window ${limit} (model=${model}) and preflight compression could not bring it under: ${detail}. The over-window payload was NOT forwarded.`;
57712
57660
  log2("error", `[${session.id}] preflight fail-fast ${status2} (retryable=${retryable}): ${message}`);
57713
57661
  return { failFast: true, status: status2, message, retryable, respond: !res.writableEnded };
57714
57662
  };
@@ -57738,8 +57686,7 @@ async function preflightCompressIfNeeded(prepared, runPrepare, req, res, opts, c
57738
57686
  model,
57739
57687
  proxyUrl,
57740
57688
  signal: clientAbort.signal,
57741
- log: log2,
57742
- imageFloor: imageTokens
57689
+ log: log2
57743
57690
  },
57744
57691
  prepared.originalMessages
57745
57692
  );
@@ -57908,12 +57855,9 @@ ${hdrText}
57908
57855
  const info = inspectContextOverflow(upstream.status, errBody.toString("utf8"));
57909
57856
  if (info.isOverflow) {
57910
57857
  let reqModel;
57911
- let rejectedImageTokens = 0;
57912
57858
  try {
57913
57859
  const rawBody = typeof prepared.body === "string" ? prepared.body : prepared.body.toString("utf8");
57914
- const parsedBody = JSON.parse(rawBody);
57915
- reqModel = typeof parsedBody.model === "string" ? parsedBody.model : void 0;
57916
- rejectedImageTokens = imageTokensInParsedBody(prepared.protocol, parsedBody);
57860
+ reqModel = JSON.parse(rawBody).model;
57917
57861
  } catch {
57918
57862
  reqModel = void 0;
57919
57863
  }
@@ -57925,7 +57869,7 @@ ${hdrText}
57925
57869
  s3.metadata.learnedContextLimits = learnedMap;
57926
57870
  log2("warn", `[${s3.id}] upstream context overflow \u2014 learned real window ${info.window} for ${reqModel ?? "(unknown model)"} (was ${prev ?? "unset"}); arming emergency shrink`);
57927
57871
  } else {
57928
- const payloadEstimate = estimateCoreMessages(prepared.processedMessages) + rejectedImageTokens;
57872
+ const payloadEstimate = estimateCoreMessages(prepared.processedMessages);
57929
57873
  const prev = (reqModel ? learnedMap[reqModel] : void 0) ?? s3.metadata.learnedContextLimit;
57930
57874
  if (payloadEstimate >= 1e3 && (prev === void 0 || payloadEstimate < prev)) {
57931
57875
  if (reqModel) learnedMap[reqModel] = payloadEstimate;
@@ -58310,7 +58254,8 @@ function sendStats(res) {
58310
58254
  outputTokens: s3.stats.outputTokens,
58311
58255
  cacheSamples: s3.stats.cacheSamples,
58312
58256
  cacheHitPct: s3.stats.cacheSamples > 0 && s3.stats.inputTokens > 0 ? Math.round(s3.stats.cachedTokens / s3.stats.inputTokens * 100) : null,
58313
- lastSeen: new Date(s3.lastSeen).toISOString()
58257
+ lastSeen: new Date(s3.lastSeen).toISOString(),
58258
+ restored: s3.restored === true
58314
58259
  }));
58315
58260
  res.writeHead(200, { "content-type": "application/json" });
58316
58261
  res.end(JSON.stringify({ sessions: sessions2 }, null, 2));