sintbot 1.4.14 → 1.4.16

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.
@@ -1639,8 +1639,7 @@ function providerWindowText(p,withSource=false){
1639
1639
  return fmtContextTokens(w.tokens)+(withSource?' · '+source+(w.activeTokens&&w.activeTokens!==w.tokens?' · '+active+' '+fmtContextTokens(w.activeTokens):'')+(w.usableTokens&&w.usableTokens!==w.tokens?' · '+(en?'Currently usable':'当前可用')+' '+fmtContextTokens(w.usableTokens):''):'');
1640
1640
  }
1641
1641
  function providerCompactText(p){
1642
- const inherited=p.autocompactTokens==null;
1643
- const n=inherited?(S.agent?.autocompactTokens??400000):p.autocompactTokens;
1642
+ const n=p.autocompactTokens||0;
1644
1643
  if(!n)return '执行器默认';
1645
1644
  return fmtContextTokens(n);
1646
1645
  }
@@ -1651,6 +1650,7 @@ function claudeChip(){
1651
1650
  if(!s.loggedIn) return '<span class="chip none" title="这台机器还没登录 Claude —— 跑 claude auth login">未登录</span>';
1652
1651
  return `<span class="chip set" title="claude auth status: ${esc(s.method||'')} · ${esc(s.email||'')}${s.org?' · '+esc(s.org):''}">订阅登录 ${IC.check}${s.plan?' · '+esc(s.plan):''}</span>`;
1653
1652
  }
1653
+ function providerGroupCount(name){return (S.groups||[]).filter(g=>!g.paused&&(g.effectiveProvider||g.provider||S.defaultProvider)===name).length;}
1654
1654
  function renderProviders(){
1655
1655
  const cards=S.providers.filter(p=>showAll||(!p.hidden&&p.configured)).map(p=>{
1656
1656
  const cs=p.runner==='codex'?codexStatus[p.name]:null;
@@ -1658,13 +1658,12 @@ function renderProviders(){
1658
1658
  return `<div class="card pcard${p.hidden?' is-hidden':(!p.configured?' is-unconfig':'')}" data-prov="${esc(p.name)}">
1659
1659
  <div class="ph"><div class="pn"><span class="drag" draggable="true" title="拖动排序">${IC.grip}</span>${esc(p.name)}${p.runner==='codex'?'<span class="cli-tag codex" title="GPT 经 Codex CLI 后端 · 订阅登录">Codex CLI</span>':'<span class="cli-tag claude" title="经 Claude Code CLI · 模型大脑按 env 切换">Claude Code CLI</span>'}</div>
1660
1660
  <div class="inline" style="gap:8px">${chip}<button class="iconbtn cfg" data-act="edit" title="配置模型">${IC.settings}</button></div></div>
1661
- <div class="kv"><span class="kk">model</span><span class="vv" title="${esc(p.model||(p.loggedIn&&p.runner!=='codex'?'未指定 ⇒ spawn 时不设 ANTHROPIC_MODEL,用 Claude Code 自己的默认模型':''))}">${esc(p.model||(p.loggedIn&&p.runner!=='codex'?'跟随 Claude Code 默认':'—'))}</span></div>
1662
- <div class="kv"><span class="kk">${p.contextWindow?.source==='official'?'模型上限':'当前窗口'}</span><span class="vv" title="${esc(providerWindowText(p,true))} · 只读,不会改变模型容量">${esc(providerWindowText(p))}</span></div>
1663
- <div class="kv"><span class="kk">${p.runner==='codex'?'压缩阈值':'压缩窗口'}</span><span class="vv" title="${esc(providerCompactText(p))} · ${p.autocompactTokens==null?'沿用旧全局设置':'逐模型设置'} · ${p.runner==='codex'?'Codex 可能按当前窗口提前压缩':'Claude Code 在压缩窗口满前触发,设置值受模型窗口限制'}">${esc(providerCompactText(p))}</span></div>
1664
- <div class="kv"><span class="kk">endpoint</span><span class="vv" title="${esc(p.runner==='codex'?'Codex CLI(codex exec · 订阅登录)':(p.baseUrl||'本机 Claude Code 的登录态 —— 无需 baseUrl / API key'))}">${esc(p.runner==='codex'?'Codex CLI (订阅登录)':(p.baseUrl?p.baseUrl.replace('https://',''):'Claude 订阅登录'))}</span></div>
1665
- ${p.effort?`<div class="kv"><span class="kk">思考深度</span><span class="vv">${esc(p.effort)}</span></div>`:''}
1661
+ ${p.baseUrl?`<div class="kv"><span class="kk">型号</span><span class="vv" title="${esc(p.model||'')}">${esc(p.model?modelName(p.model):'—')}</span></div>
1662
+ <div class="kv"><span class="kk">${p.contextWindow?.source==='official'?'模型上限':'当前窗口'}</span><span class="vv" title="${esc(providerWindowText(p,true))} · 只读,不会改变模型容量">${esc(providerWindowText(p))}</span></div>`:`<div class="kv"><span class="kk">型号与深度</span><span class="vv" title="型号与思考深度按群在「群·项目」里选">按群选 · ${providerGroupCount(p.name)} 个群在用</span></div>`}
1663
+ ${p.runner==='codex'?`<div class="kv"><span class="kk">窗口设置</span><span class="vv">${p.contextWindowTokens?esc(fmtContextTokens(p.contextWindowTokens)):'执行器默认'}</span></div>`:''}
1664
+ <div class="kv"><span class="kk">${p.runner==='codex'?'压缩阈值':'压缩窗口'}</span><span class="vv" title="${esc(providerCompactText(p))} · 逐模型设置 · ${p.runner==='codex'?'Codex 可能按当前窗口提前压缩':'Claude Code 在压缩窗口满前触发,设置值受模型窗口限制'}">${esc(providerCompactText(p))}</span></div>
1665
+ <div class="kv"><span class="kk">连接方式</span><span class="vv" title="${esc(p.runner==='codex'?'Codex CLI(codex exec · 订阅登录)':(p.baseUrl||'本机 Claude Code 的登录态 —— 无需 baseUrl / API key'))}">${esc(p.runner==='codex'?'Codex CLI (订阅登录)':(p.baseUrl?p.baseUrl.replace('https://',''):'Claude 订阅登录'))}</span></div>
1666
1666
  ${p.vision===false?'<div class="kv"><span class="kk">识图</span><span class="vv" title="纯文本模型:送给它的图片(群里发的图/网页截图/读取的图片文件)自动降级成占位文本,不会 400 崩掉整轮">不支持 · 图片自动降级</span></div>':''}
1667
- ${p.runner==='codex'?`<div class="kv"><span class="kk">推理强度</span><span class="vv">${esc(p.reasoningEffort||cxDefEffortLabel(p.model))}</span></div>`:''}
1668
1667
  ${usage[p.name]?`<div class="kv"><span class="kk" style="flex:none">${esc(usage[p.name].label)}${usageRefreshBtn(false)}</span><span class="vv" title="${esc(usage[p.name].value)}${usage[p.name].label==='订阅额度'?' —— 百分比 = 已用,括号 = 距重置,与这台机器上交互式 Claude Code 共享的订阅窗口':''}" style="color:var(--ok);font-weight:600;white-space:normal;overflow:visible;max-width:none;flex:1;min-width:0;line-height:1.5">${esc(usage[p.name].value)}</span></div>`:''}
1669
1668
  ${(cs&&cs.usageText)?`<div class="kv"><span class="kk" style="flex:none">订阅额度${usageRefreshBtn(true)}</span><span class="vv" title="百分比 = 已用,括号 = 距重置 —— 与交互式 Codex 共享的订阅窗口(时长随官方口径)" style="color:var(--ok);font-weight:600;white-space:normal;overflow:visible;max-width:none;flex:1;min-width:0;line-height:1.5">${esc(cs.usageText)}</span></div>`:''}
1670
1669
  <div class="acts"><button class="btn" data-act="test">测试</button>${p.hidden?'<span class="hint" style="align-self:center">已隐藏</span>':''}</div>
@@ -1674,7 +1673,7 @@ function renderProviders(){
1674
1673
  const extra=S.providers.filter(p=>p.hidden||!p.configured).length;
1675
1674
  const toggle=extra?`<button class="btn ghost" id="showAll">${showAll?'只看已配置':`显示全部 (${extra})`}</button>`:'';
1676
1675
  $('#providers').innerHTML=`<div class="inline" style="justify-content:space-between;align-items:flex-start;margin:0 2px 14px;gap:10px">
1677
- <div class="hint" style="flex:1">${S.runtimeCapabilities?esc(runtimeAllows('POST','/api/providers/add')?RUNTIME_UI_TEXT.providers:RUNTIME_UI_TEXT.providersView):`管理模型 · 拖 ${IC.grip} 排序 · ${IC.settings} 配端点/key/思考深度 · 模型按群在「群·项目」选`}</div>
1676
+ <div class="hint" style="flex:1">${S.runtimeCapabilities?esc(runtimeAllows('POST','/api/providers/add')?RUNTIME_UI_TEXT.providers:RUNTIME_UI_TEXT.providersView):`管理模型 · 拖 ${IC.grip} 排序 · ${IC.settings} 配端点/key/压缩 · 型号与思考深度按群在「群·项目」选`}</div>
1678
1677
  <div class="inline" style="gap:8px">${toggle}<button class="btn primary" id="addModel">${IC.plus} 添加模型</button></div></div>
1679
1678
  <div class="grid cols2 stagger" id="pgrid">${cards}</div>
1680
1679
  <h2 class="sec" style="margin-top:32px">插件库 · MCP</h2>
@@ -1804,36 +1803,48 @@ function openProviderModal(name,opts){
1804
1803
  // 查表 —— 界面语言一切成英文,选项文本变了,预设联动就整个失效(逻辑不许依赖显示文本)。
1805
1804
  const preset=edit?'':`<div class="field"><label class="f">预设(自动填端点/模型/调优)</label><select id="amPreset">${Object.keys(PRESETS).map(k=>`<option value="${esc(k)}" ${opts.preset===k?'selected':''}>${esc(k)}</option>`).join('')}</select></div>`;
1806
1805
  const keyPh=edit?(p.keyStatus==='set'?'已设置 · 留空保持':p.keyStatus==='env'?'env:'+esc(p.keyRef)+' · 留空保持':'粘贴 key'):'粘贴 key(存进本地 gitignored 配置)';
1807
- const codexNote=isCodex?`<div class="dim" style="font-size:12px;line-height:1.55;margin:-2px 0 12px;padding:9px 11px;border:1px solid var(--line-hi);border-radius:8px"><b style="color:var(--ember)">Codex CLI 后端(GPT)</b> · 经 ChatGPT 订阅登录(<span class="mono">codex login</span>),<b>无 baseUrl、无 API key</b>。写文件权限由该群「执行模式」决定(bypassPermissions→可写)。额度与你交互式 Codex 共享,省着用。</div>`:'';
1806
+ const codexNote=isCodex?'<div class="hint" style="margin-bottom:12px">使用本机 ChatGPT 订阅登录,额度与本机 Codex 共享。</div>':'';
1807
+ // Durable hosts: model and thinking depth are chosen per group (group card / /model);
1808
+ // only a third-party endpoint keeps its model here as connection configuration.
1809
+ const perGroup=!!S.runtimeCapabilities;
1810
+ const showModelSec=!edit||!perGroup||(!isCodex&&!isClaudeLogin);
1808
1811
  $('#modal').innerHTML=`<h3>${edit?'配置 '+esc(name):'添加模型'}</h3>
1809
1812
  ${preset}
1810
1813
  ${codexNote}
1811
- <div class="field"><label class="f">名称(唯一标识,英文)</label><input id="amName" value="${edit?esc(name):''}" placeholder="如 glm / my-model" ${edit?'disabled':''}/></div>
1812
- ${(isCodex||isClaudeLogin)?'':`<div class="field"><label class="f">端点 baseUrl</label><input id="amBase" value="${edit?esc(p.baseUrl):''}" placeholder="https://.../anthropic"/></div>`}
1813
- ${isCodex?`<div class="field"><label class="f">模型 model</label>
1814
+ ${showModelSec?`<h4 class="sec">${perGroup?'名称与型号':'型号与推理'}</h4>`:''}
1815
+ ${edit?'':'<div class="field"><label class="f" for="amName">名称</label><input id="amName" placeholder="如 glm / my-model"/></div>'}
1816
+ ${perGroup?`<div class="hint" style="margin-bottom:12px">型号与思考深度按群在「群·项目」里选${(isCodex||isClaudeLogin)?'':';这个端点的型号在下面填,各群共用'}。</div>`:''}
1817
+ ${isCodex&&perGroup?'':isCodex?`<div class="field"><label class="f">模型 model</label>
1814
1818
  <select id="amModelSel"><option value="${edit?esc(p.model):''}" selected>${edit&&p.model?esc(p.model):'(未设 — 用 codex 默认)'}</option></select>
1815
1819
  <input id="amModel" value="${edit?esc(p.model):''}" placeholder="如 gpt-5.6-sol" style="display:none;margin-top:8px"/>
1816
1820
  <div class="hint" id="amModelSrc" style="margin-top:4px">加载模型列表…</div></div>`
1817
- :isClaudeLogin?`<div class="field" id="clModelWrap"><label class="f">模型 model</label>
1821
+ :isClaudeLogin&&perGroup?'':isClaudeLogin?`<div class="field" id="clModelWrap"><label class="f">模型 model</label>
1818
1822
  <div class="hint">检测登录中…</div></div>`
1819
1823
  :`<div class="field"><label class="f">模型 model</label><input id="amModel" value="${edit?esc(p.model):''}" placeholder="如 glm-5.2[1m]"/></div>`}
1820
- <div class="field"><label class="f">模型窗口</label><div class="dim" id="amWindow" style="padding:9px 11px;border:1px solid var(--line-hi);border-radius:8px">${edit?esc(providerWindowText(p,true)):'添加后按模型目录或会话上报显示'}</div>
1821
- <div class="hint" style="margin-top:5px">只读 · 有官方上限时显示上限;否则显示当前执行器窗口。可用容量可能更低。</div></div>
1822
- <div class="field"><label class="f">${isCodex?'自动压缩阈值(token)':'自动压缩窗口(token)'}</label><input id="amComp" type="number" min="0" max="1000000" step="10000" value="${edit?(p.autocompactTokens??S.agent?.autocompactTokens??400000):(S.agent?.autocompactTokens??400000)}"/>
1823
- <div class="hint" style="margin-top:5px">${isCodex?'0 = 使用 Codex 默认;否则填 10万–100万。Codex 可能按当前窗口提前压缩。':'0 = 使用 Claude Code 默认;否则填 10万–100万压缩窗口。实际压缩会在窗口满前触发。'}${edit&&p.autocompactTokens==null?'<span>当前沿用旧全局设置,保存后改为本模型独立设置。</span>':''}</div></div>
1824
+ ${isCodex&&!perGroup?`<div class="field"><label class="f">推理强度</label><select id="amRE">${RE_CODEX.concat(edit&&p.reasoningEffort&&!RE_CODEX.includes(p.reasoningEffort)?[p.reasoningEffort]:[]).map(v=>`<option value="${v}" ${edit&&v===(p.reasoningEffort||'')?'selected':''}>${v||'默认(模型内置)'}</option>`).join('')}</select>
1825
+ <div class="hint" style="margin-top:4px">默认沿用模型推荐强度;更高强度通常需要更多时间和额度。</div></div>`:''}
1826
+ ${isCodex||perGroup?'':`<div class="field"><label class="f">思考深度</label><select id="amEff">${EFFORTS.map(v=>`<option value="${v}" ${edit&&v===p.effort?'selected':''}>${v||'默认'}</option>`).join('')}</select>
1827
+ <div class="hint" style="margin-top:5px">默认沿用模型推荐强度;更高强度通常需要更多时间和额度。</div></div>`}
1828
+ <h4 class="sec">上下文与压缩</h4>
1829
+ <details class="field"><summary class="hint" style="cursor:pointer">容量与运行信息</summary><div class="dim" id="amWindow" style="padding:9px 0">${edit?esc(providerWindowText(p,true)):'添加后按模型目录或会话上报显示'}</div></details>
1830
+ ${isCodex?`<div class="field"><label class="f" for="amContextMode">上下文窗口</label><select id="amContextMode"><option value="default" ${!edit||!p.contextWindowTokens?'selected':''}>执行器默认</option><option value="custom" ${edit&&p.contextWindowTokens>0?'selected':''}>自定义</option></select></div>
1831
+ <div class="field" id="amContextField"><label class="f" for="amContext">窗口大小(token)</label><input id="amContext" type="number" min="100000" max="1050000" step="1" value="${edit?(p.contextWindowTokens||500000):500000}"/>
1832
+ <div class="hint" id="amContextMax" style="margin-top:5px">${edit&&p.contextWindowMax?esc((langOf()==='en'?'Catalog limit: ':'目录上限:')+fmtContextTokens(p.contextWindowMax)):'自定义值不能超过执行器支持的上限,超出已知上限无法保存。'}</div></div>`:''}
1833
+ <div class="field"><label class="f" for="amCompMode">自动压缩</label><select id="amCompMode"><option value="default" ${!edit||!p.autocompactTokens?'selected':''}>执行器默认</option><option value="custom" ${edit&&p.autocompactTokens>0?'selected':''}>自定义</option></select></div>
1834
+ <div class="field" id="amCompField"><label class="f" for="amComp">${isCodex?'压缩阈值(token)':'压缩窗口(token)'}</label><input id="amComp" type="number" min="100000" max="1000000" step="1" value="${edit?(p.autocompactTokens||400000):400000}"/>
1835
+ <div class="hint" style="margin-top:5px">${isCodex?'自定义阈值为 10万–100万。Codex 可能按当前窗口提前压缩。':'自定义压缩窗口为 10万–100万。Claude Code 会预留空间,可能提前压缩。'}</div></div>
1836
+ <div class="hint" style="margin-bottom:14px">默认沿用执行器配置;自定义从下轮生效,保留当前会话。</div>
1837
+ <h4 class="sec">连接与登录</h4>
1838
+ ${(isCodex||isClaudeLogin)?'':`<div class="field"><label class="f">端点 baseUrl</label><input id="amBase" value="${edit?esc(p.baseUrl):''}" placeholder="https://.../anthropic"/></div>`}
1839
+ ${(isCodex||isClaudeLogin)?'':`<div class="field"><label class="f">API key</label><input id="amKey" placeholder="${keyPh}"/></div>`}
1824
1840
  ${isClaudeLogin?`<div class="field"><label class="f">Claude 订阅登录</label>
1825
1841
  <div id="clStatus" style="border:1px solid var(--line-hi);border-radius:8px;padding:9px 11px;font-size:13px">${clStatusHtml()}</div>
1826
1842
  <button class="btn" id="clVerify" style="margin-top:8px">检测登录</button>
1827
- <div class="hint" style="margin-top:6px">读的是这台机器 <span class="mono">claude auth status</span> 的结果(不联网计费、不占订阅额度)。没登录就在这台机器上跑一次 <span class="mono">claude auth login</span>。</div></div>`:''}
1843
+ <div class="hint" style="margin-top:6px">检查本机 Claude Code 登录状态,不发起模型对话。</div></div>`:''}
1828
1844
  ${isCodex?`<div class="field"><label class="f">Codex 订阅登录 / 额度</label>
1829
1845
  <div id="cxStatus" style="border:1px solid var(--line-hi);border-radius:8px;padding:9px 11px;font-size:13px">${cxStatusHtml(cxKey)}</div>
1830
1846
  <button class="btn" id="cxVerify" style="margin-top:8px">验证登录 / 刷新额度</button>
1831
- <div class="hint" style="margin-top:6px">额度 = 与你交互式 Codex 共享的订阅窗口剩余(窗口时长随官方口径,当前多为「周」)。查询不耗消息额度。</div></div>`:''}
1832
- ${isCodex?`<div class="field"><label class="f">推理强度(model_reasoning_effort)</label><select id="amRE">${RE_CODEX.concat(edit&&p.reasoningEffort&&!RE_CODEX.includes(p.reasoningEffort)?[p.reasoningEffort]:[]).map(v=>`<option value="${v}" ${edit&&v===(p.reasoningEffort||'')?'selected':''}>${v||'默认(模型内置)'}</option>`).join('')}</select>
1833
- <div class="hint" style="margin-top:4px">低档更快省额度,高档更强更慢;编码/agentic 类任务 xhigh 通常是甜点档。选项随所选模型变化(默认走模型内置),逐轮生效、切即用。万一某档超出你的订阅套餐,运行时会被拒 → 自动去档重跑一轮,群里不会空等。</div></div>`:''}
1834
- ${(isCodex||isClaudeLogin)?'':`<div class="field"><label class="f">API key</label><input id="amKey" placeholder="${keyPh}"/></div>`}
1835
- ${isCodex?'':`<div class="field"><label class="f">思考深度(CLAUDE_CODE_EFFORT_LEVEL)</label><select id="amEff">${EFFORTS.map(v=>`<option value="${v}" ${edit&&v===p.effort?'selected':''}>${v||'默认'}</option>`).join('')}</select>
1836
- <div class="hint" style="margin-top:5px">低档更快更省,高档更强更慢。编码 / agentic 类任务 <b>xhigh</b> 通常是甜点档(也是 Claude Code 自己的默认)。</div></div>`}
1847
+ <div class="hint" style="margin-top:6px">显示本机账号返回的额度与重置时间,不发起模型对话。</div></div>`:''}
1837
1848
  ${(isCodex||isClaudeLogin)?'':`<div class="inline" style="margin:2px 0 2px"><input type="checkbox" id="amVision" ${edit&&p.vision===false?'checked':''} style="width:auto"/> <span class="dim">此模型不支持识图(纯文本模型)</span></div>
1838
1849
  <div class="hint" style="margin:0 0 10px">勾选后,送给它的图片(群里发的图、网页截图、读取的图片文件)会自动降级成一句占位文本 —— 不勾则整轮会被模型以「只支持文本输入」打断。拿不准就先跑一轮试:报这个错就回来勾上。</div>`}
1839
1850
  ${edit?`<div class="inline" style="margin:2px 0 10px"><input type="checkbox" id="amHide" ${p.hidden?'checked':''} style="width:auto"/> <span class="dim">在列表中隐藏此模型</span></div>`:''}
@@ -1842,9 +1853,13 @@ function openProviderModal(name,opts){
1842
1853
  <div class="inline" style="gap:8px"><button class="btn" id="amCancel">取消</button><button class="btn primary" id="amSave">${edit?'保存':'添加'}</button></div></div>`;
1843
1854
  $('#modalBg').classList.add('show');
1844
1855
  $('#modal').oninput=null;$('#modal').onchange=null;
1856
+ const syncMode=(id,field)=>{$('#'+field).hidden=$('#'+id).value!=='custom';};
1857
+ $('#amCompMode').onchange=()=>syncMode('amCompMode','amCompField');$('#amCompMode').onchange();
1858
+ if(isCodex){$('#amContextMode').onchange=()=>syncMode('amContextMode','amContextField');$('#amContextMode').onchange();}
1845
1859
  if(edit){
1846
1860
  const refreshWindow=()=>{const sel=$('#amModelSel'),input=$('#amModel');const selected=sel&&sel.value!=='__custom__'?sel.value:input?.value?.trim();
1847
- const box=$('#amWindow');if(box)box.textContent=selected===(p.model||'')?providerWindowText(p,true):'新型号保存后刷新';};
1861
+ const box=$('#amWindow');if(box)box.textContent=selected===(p.model||'')?providerWindowText(p,true):'新型号保存后刷新';
1862
+ const maximum=$('#amContextMax');if(maximum)maximum.textContent=selected===(p.model||'')&&p.contextWindowMax?(langOf()==='en'?'Catalog limit: ':'目录上限:')+fmtContextTokens(p.contextWindowMax):(langOf()==='en'?'The selected model limit will be checked on save.':'保存时核对所选型号的上限。');};
1848
1863
  $('#modal').oninput=e=>{if(e.target.id==='amModel'||e.target.id==='amModelSel')refreshWindow();};
1849
1864
  $('#modal').onchange=e=>{if(e.target.id==='amModel'||e.target.id==='amModelSel')refreshWindow();};
1850
1865
  }
@@ -1931,19 +1946,21 @@ function openProviderModal(name,opts){
1931
1946
  const selModel=(isCodex||isClaudeLogin)
1932
1947
  ?(hasModelCtl?(()=>{const sv=($('#amModelSel')||{}).value;return (sv==null||sv==='__custom__')?(($('#amModel')||{}).value||'').trim():sv;})():null)
1933
1948
  :$('#amModel').value.trim();
1934
- if(!$('#amComp').value.trim()){toast('请填写压缩阈值,使用执行器默认请填 0','err');return;}
1935
- const b={name:nm,autocompactTokens:Number($('#amComp').value)};
1936
- if(!Number.isInteger(b.autocompactTokens)||b.autocompactTokens<0||(b.autocompactTokens>0&&(b.autocompactTokens<100000||b.autocompactTokens>1000000))){toast('压缩阈值须为 0 或 10万–100万 token','err');return;}
1949
+ if($('#amCompMode').value==='custom'&&!$('#amComp').value.trim()){toast('请填写压缩值','err');return;}
1950
+ const b={name:nm,autocompactTokens:$('#amCompMode').value==='default'?0:Number($('#amComp').value)};
1951
+ if(!Number.isInteger(b.autocompactTokens)||b.autocompactTokens<0||($('#amCompMode').value==='custom'&&(b.autocompactTokens<100000||b.autocompactTokens>1000000))){toast('压缩阈值须为 0 或 10万–100万 token','err');return;}
1937
1952
  if(selModel)b.model=selModel;
1938
- if(isCodex){b.model=selModel||'';b.reasoningEffort=$('#amRE').value;
1953
+ if(isCodex){if(!perGroup){b.model=selModel||'';b.reasoningEffort=$('#amRE').value;}
1954
+ const value=$('#amContextMode').value==='default'?'0':$('#amContext').value.trim();b.contextWindowTokens=Number(value);
1955
+ if(!value||!Number.isSafeInteger(b.contextWindowTokens)||b.contextWindowTokens<0||($('#amContextMode').value==='custom'&&(b.contextWindowTokens<100000||b.contextWindowTokens>1050000))){toast('自定义窗口须为 10万–105万 token','err');return;}
1939
1956
  // ⚠ 新建时必须显式带 runner —— 少了它建出来的是个"没有端点没有 key 的 claude provider",
1940
1957
  // 跑起来才发现不对。服务端那边是白名单校验(只认 'codex'),不是原样存。
1941
1958
  if(!edit){b.runner='codex';const pr=PRESETS[($('#amPreset')||{}).value]||{};if(pr.toolQuality)b.toolQuality=pr.toolQuality;}}
1942
1959
  else if(isClaudeLogin){
1943
1960
  // 订阅登录:端点/key/识图三项面板里就没有 ⇒ 不回传(回传空串会把已有值清掉)。
1944
1961
  // 型号可以被清空成「跟随 Claude Code 默认」,那要显式 modelClear —— 但只在控件真在时才发。
1945
- b.effort=$('#amEff').value; if(hasModelCtl&&!selModel)b.modelClear=true;
1946
- } else {b.model=selModel;b.baseUrl=$('#amBase').value.trim();b.apiKey=$('#amKey').value.trim();b.effort=$('#amEff').value;b.vision=!$('#amVision').checked;}
1962
+ if(!perGroup){b.effort=$('#amEff').value; if(hasModelCtl&&!selModel)b.modelClear=true;}
1963
+ } else {b.model=selModel;b.baseUrl=$('#amBase').value.trim();b.apiKey=$('#amKey').value.trim();if($('#amEff'))b.effort=$('#amEff').value;b.vision=!$('#amVision').checked;}
1947
1964
  if(!edit)b.env=JSON.parse($('#amPreset').dataset.env||'{}');
1948
1965
  if(edit)b.hidden=$('#amHide').checked;
1949
1966
  try{const r=await api('/api/providers/add','POST',b);S=r;
@@ -2642,7 +2659,7 @@ function cronFullHtml(text){
2642
2659
  }).join('');
2643
2660
  }
2644
2661
  const RUNTIME_UI_TEXT={
2645
- "groups": "可绑定新群,修改模型、项目目录、权限、数据输出和忙时跟进;该群须没有进行中的任务和未结记录。切模型或项目会开始新的一段对话。停止任务和开新会话在「概览」,开新会话须等该群空闲。群里发 /model 也能切换模型。",
2662
+ "groups": "可绑定新群,修改后端、型号、思考深度、项目目录、权限、数据输出和忙时跟进;该群须没有进行中的任务和未结记录。换后端或项目会开始新的一段对话,换型号或调深度接着当前对话。停止任务和开新会话在「概览」,开新会话须等该群空闲。群里发 /model 也能改。",
2646
2663
  "skills": "当前支持飞书文字消息、忙时跟进、任务回复和停止。定时任务、生图、群消息管理、画布与预览暂未接入。",
2647
2664
  "cron": "当前不执行定时任务;已有定义仅供查看。",
2648
2665
  "board": "画布创建、同步与反馈暂未接入;已有链接仅供查看。",
@@ -2652,10 +2669,80 @@ const RUNTIME_UI_TEXT={
2652
2669
  "resumeLabel": "恢复",
2653
2670
  "steer": "Codex 与 Claude Code 默认开启忙时跟进;长命令执行期间的补充会在工具步骤之间读取。只有确认模型已读取才显示已加入;不支持此能力时排队。控制台插话同样支持忙时跟进;关闭或暂不支持时排队。",
2654
2671
  "preview": "当前版本未接入预览服务。",
2655
- "providers": "可添加模型,修改型号、端点、密钥、思考深度与推理强度,测试、隐藏或拖动排序;各群的模型在「群·项目」里切换。改型号、后端或端点时,用它的群须空闲,并会开始新的一段对话;默认模型和群正在用的模型不能隐藏。移除模型暂不可用。",
2672
+ "providers": "可添加模型,修改端点、密钥、上下文与压缩,测试、隐藏或拖动排序;型号与思考深度按群在「群·项目」里选(第三方端点的型号在这里填)。改后端或端点时,用它的群须空闲,并会开始新的一段对话;默认模型和群正在用的模型不能隐藏。移除模型暂不可用。",
2656
2673
  "providersView": "当前可查看模型配置与登录状态;各群的模型在「群·项目」里切换。添加或修改模型暂不可用。",
2657
2674
  "unavailable": "当前版本暂不支持此操作。"
2658
2675
  };
2676
+ // Model display names (claude-opus-5-5 → Opus 5.5): the server injects the same
2677
+ // function the group replies use; stored and sent values stay the official ids.
2678
+ const modelNameRule=/*__SBOT_MODELNAME__*/(s=>String(s==null?'':s));
2679
+ const MODEL_OPTS={}; // provider name → /api/groups/model-options result
2680
+ const modelName=(id)=>{const k=String(id==null?'':id).trim();if(!k)return '';
2681
+ const listed=Object.values(MODEL_OPTS).map(c=>c&&Array.isArray(c.models)&&c.models.find(m=>m&&m.id===k)).find(Boolean);
2682
+ return (S&&S.modelNames&&S.modelNames[k])||(listed&&listed.name)||modelNameRule(k);};
2683
+ // Backend · model · thinking depth on the group card. Choices are stored per
2684
+ // backend; only a backend change starts a new conversation.
2685
+ const MODEL_OPTS_LOADING=new Set();
2686
+ function loadModelOpts(name){
2687
+ if(!name||MODEL_OPTS[name]||MODEL_OPTS_LOADING.has(name))return;
2688
+ MODEL_OPTS_LOADING.add(name);
2689
+ api('/api/groups/model-options?provider='+encodeURIComponent(name)).then(c=>{MODEL_OPTS[name]=c||{ok:false,models:[],efforts:[]};})
2690
+ .catch(()=>{MODEL_OPTS[name]={ok:false,models:[],efforts:[]};})
2691
+ .finally(()=>{MODEL_OPTS_LOADING.delete(name);if($('#groups'))renderGroups();});
2692
+ }
2693
+ function catEfforts(cat,model){
2694
+ if(!cat)return null;
2695
+ const m=Array.isArray(cat.models)?cat.models.find(x=>x.id===model):null;
2696
+ if(m&&Array.isArray(m.efforts))return m.efforts; // [] = this model has no depth setting
2697
+ return cat.efforts||[];
2698
+ }
2699
+ function groupBrainRows(g){
2700
+ const b=g.brain||{provider:g.effectiveProvider||g.provider||S.defaultProvider,model:'',effort:''};
2701
+ const name=b.provider, cat=MODEL_OPTS[name];
2702
+ if(!cat)loadModelOpts(name);
2703
+ const sel='style="width:auto;min-width:110px;max-width:100%;padding:5px 30px 5px 10px;font-size:11.5px"';
2704
+ const unset='<span style="color:var(--fail)">未选</span>';
2705
+ const provs=S.providers.filter(pp=>!pp.hidden&&pp.configured);
2706
+ if(!provs.some(pp=>pp.name===name))provs.unshift({name});
2707
+ const backend=`<select class="gprov" data-g="${esc(g.chatId)}" ${sel}>${provs.map(pp=>`<option value="${esc(pp.name)}" ${pp.name===name?'selected':''}>${esc(pp.name)}</option>`).join('')}</select>`;
2708
+ let model;
2709
+ if(!cat)model=`<span class="vv">${b.model?esc(modelName(b.model)):unset} <span class="faint">· 加载清单…</span></span>`;
2710
+ else if(cat.models===null)model=`<span class="vv" title="这个端点的型号在「模型」页设置,各群共用">${b.model?esc(modelName(b.model)):'—'} <span class="faint">· 在「模型」页改</span></span>`;
2711
+ else{
2712
+ const list=cat.models||[];
2713
+ const opts=list.map(m=>`<option value="${esc(m.id)}" ${m.id===b.model?'selected':''}>${esc(m.name||modelName(m.id))}${cat.recommended&&cat.recommended.model===m.id?' ★':''}</option>`);
2714
+ if(b.model&&!list.some(m=>m.id===b.model))opts.unshift(`<option value="${esc(b.model)}" selected>${esc(modelName(b.model))}</option>`);
2715
+ if(!b.model)opts.unshift('<option value="" selected disabled>未选</option>');
2716
+ opts.push('<option value="__custom__">自定义…</option>');
2717
+ model=`<span class="vv inline" style="gap:6px;flex-wrap:wrap;min-width:0"><select class="gmodel" data-g="${esc(g.chatId)}" ${sel}>${opts.join('')}</select>
2718
+ <span class="gcustom" data-g="${esc(g.chatId)}" hidden><input class="gcustomv" placeholder="型号 id" style="width:150px;padding:5px 8px;font-size:11.5px"/> <button class="btn" data-gcustom="${esc(g.chatId)}" style="padding:3px 9px;font-size:11px">保存</button></span>
2719
+ ${cat.ok===false?'<span class="faint" style="font-size:11px">清单暂时取不到</span>':''}</span>`;
2720
+ }
2721
+ const effs=catEfforts(cat,b.model);
2722
+ let effort;
2723
+ if(effs&&!effs.length&&cat&&Array.isArray(cat.models)&&cat.models.some(m=>m.id===b.model))effort='<span class="vv faint">这个型号不支持</span>';
2724
+ else{
2725
+ const list=(effs&&effs.length?effs:['low','medium','high','xhigh','max']).slice();
2726
+ if(b.effort&&!list.includes(b.effort))list.push(b.effort);
2727
+ effort=`<select class="geffort" data-g="${esc(g.chatId)}" ${sel}>${b.effort?'':'<option value="" selected disabled>未选</option>'}${list.map(e=>`<option value="${esc(e)}" ${e===b.effort?'selected':''}>${esc(e)}</option>`).join('')}</select>`;
2728
+ }
2729
+ const pr=S.providers.find(pp=>pp.name===name)||{};
2730
+ const win=g.contextWindow&&g.contextWindow.tokens?providerWindowText({contextWindow:g.contextWindow,runner:pr.runner},true):'';
2731
+ // One "模型" row: backend and model side by side (they wrap on a narrow screen).
2732
+ return `<div class="kv"><span class="kk">模型</span><span class="vv inline" style="gap:6px;flex-wrap:wrap;min-width:0">${backend}${model}</span></div>
2733
+ <div class="kv"><span class="kk">思考深度</span>${effort}</div>
2734
+ ${win?`<div class="kv"><span class="kk">上下文</span><span class="vv" title="${esc(win)}">${esc(win)}</span></div>`:''}
2735
+ <div class="kv"><span class="kk"></span><span class="faint" style="font-size:11px">换后端会开新对话;换型号或调深度接着当前对话 · 群空闲时才能改</span></div>`;
2736
+ }
2737
+ async function saveGroupBrain(chatId,change){
2738
+ try{
2739
+ const r=await api('/api/groups/brain','POST',{chatId,...change});S=r;renderOverview();renderGroups();
2740
+ const c=r.brainChange||{};
2741
+ const filled=c.filled&&c.filled.length?'(未选的'+c.filled.join('和')+'先用推荐)':'';
2742
+ const what=[c.model?modelName(c.model):'',c.effort||''].filter(Boolean).join(' · ');
2743
+ toast(!c.changed?'没有变化':c.switching?`已切到 ${c.provider}${what?' · '+what:''} · 会话已重置,下条消息生效${filled}`:c.modelChanged?`已改为 ${what} · 接着当前对话,下条消息生效${filled}`:`思考深度已调为 ${c.effort} · 下条消息生效`,'ok');
2744
+ }catch(e){toast(e.message,'err');renderGroups();}
2745
+ }
2659
2746
  function renderGroups(){
2660
2747
  const provOf=g=>S.providers.find(pp=>pp.name===(g.provider||S.defaultProvider));
2661
2748
  const cards=S.groups.map(g=>{
@@ -2872,10 +2959,7 @@ function renderGroups(){
2872
2959
  </label>
2873
2960
  <span class="faint" style="font-size:11px">${g.redactPii===false?'当前:明文':'当前:遮盖'}</span>
2874
2961
  </span></div>
2875
- <div class="kv"><span class="kk">模型</span>
2876
- <select class="gprov" data-g="${esc(g.chatId)}" style="width:auto;min-width:110px;padding:5px 30px 5px 10px;font-size:11.5px">
2877
- ${S.providers.filter(pp=>!pp.hidden&&pp.configured).map(pp=>`<option value="${esc(pp.name)}" ${pp.name===(g.provider||S.defaultProvider)?'selected':''}>${esc(pp.name)}${pp.model?' · '+esc(pp.model):''}</option>`).join('')}
2878
- </select></div>
2962
+ ${groupBrainRows(g)}
2879
2963
  <div class="kv" style="align-items:flex-start"><span class="kk">忙时跟进</span><span class="vv" style="min-width:0">
2880
2964
  <label class="inline" style="gap:6px;cursor:pointer" title="${esc(S.runtimeCapabilities?RUNTIME_UI_TEXT.steer:'开启后,处理任务期间:群成员的后续消息、控制台勾了「回复」的插话,都会加入当前任务 —— 相关要求及时吸收,独立请求按先后回应。定时任务和不回复的插话仍排在后面。')}">
2881
2965
  <input type="checkbox" class="gsteer" data-g="${esc(g.chatId)}"${g.followupEnabled?' checked':''} style="width:auto;margin:0"/>
@@ -2919,7 +3003,14 @@ function renderGroups(){
2919
3003
  const on=b.dataset.p!=='1';
2920
3004
  if(on&&!confirm(S.runtimeCapabilities?'暂停本群?只有任务结束且记录全部结账后才能暂停;暂停期间不处理新消息,配置和会话保留。':'暂停本群?\n期间消息不处理(静默丢弃)、排队清空、定时任务与画布播报停发;进行中的任务会跑完。\n完全可逆:配置/会话/画布全保留,点「恢复」即原样回来。'))return;
2921
3005
  try{S=await api('/api/groups/pause','POST',{chatId:b.dataset.gpause,paused:on});renderGroups();renderOverview();toast(S.runtimeCapabilities?(on?'已暂停':'已恢复'):(on?'已暂停 · 群内已通知':'已恢复 · 群内已通知'),'ok');}catch(e){toast(e.message,'err');}});
2922
- $$('#groups .gprov').forEach(sel=>sel.onchange=async()=>{try{const r=await api('/api/groups/provider','POST',{chatId:sel.dataset.g,provider:sel.value});S=r;renderOverview();renderGroups();toast(r.switched&&r.switched.live?('已切到 '+sel.value+' · 会话已重置,下条消息生效'):('已保存 '+sel.value+' · daemon 未运行下次生效'),'ok');}catch(e){toast(e.message,'err');}});
3006
+ $$('#groups .gprov').forEach(sel=>sel.onchange=()=>saveGroupBrain(sel.dataset.g,{provider:sel.value}));
3007
+ $$('#groups .gmodel').forEach(sel=>sel.onchange=()=>{const box=$('.gcustom[data-g="'+sel.dataset.g+'"]');
3008
+ if(sel.value==='__custom__'){if(box){box.hidden=false;box.querySelector('input').focus();}return;}
3009
+ if(box)box.hidden=true;saveGroupBrain(sel.dataset.g,{model:sel.value});});
3010
+ $$('#groups [data-gcustom]').forEach(b=>b.onclick=()=>{const box=$('.gcustom[data-g="'+b.dataset.gcustom+'"]'),v=box&&box.querySelector('input').value.trim();
3011
+ if(!v){toast('填写型号 id','err');return;}saveGroupBrain(b.dataset.gcustom,{model:v});});
3012
+ $$('#groups .geffort').forEach(sel=>sel.onchange=()=>saveGroupBrain(sel.dataset.g,{effort:sel.value}));
3013
+
2923
3014
  $$('#groups .gsteer').forEach(c=>c.onchange=async()=>{const on=c.checked;
2924
3015
  try{S=await api('/api/groups/steer','POST',{chatId:c.dataset.g,enabled:on});renderGroups();toast(on?'已开启 · 下轮生效':'已关闭 · 下轮恢复排队','ok');}
2925
3016
  catch(e){c.checked=!on;toast(e.message,'err');}});
@@ -4337,7 +4428,7 @@ const I18N_EN=[
4337
4428
  ["等待模型读取,尚未确认加入", "Waiting for the model to read it; not yet confirmed joined"],
4338
4429
  ["处理状态请查看实时活动", "Check live activity for processing status"],
4339
4430
  ["需要处理", "Needs attention"],
4340
- ["可绑定新群,修改模型、项目目录、权限、数据输出和忙时跟进;该群须没有进行中的任务和未结记录。切模型或项目会开始新的一段对话。停止任务和开新会话在「概览」,开新会话须等该群空闲。群里发 /model 也能切换模型。", "Bind new groups and change the model, project folder, permissions, data display and busy-turn followups of a group with no running task or unsettled record. Changing the model or project starts a new conversation. Stop tasks and start new sessions in Overview; a new session needs the group to be idle. Members can also switch models with /model in the group."],
4431
+ ["可绑定新群,修改后端、型号、思考深度、项目目录、权限、数据输出和忙时跟进;该群须没有进行中的任务和未结记录。换后端或项目会开始新的一段对话,换型号或调深度接着当前对话。停止任务和开新会话在「概览」,开新会话须等该群空闲。群里发 /model 也能改。", "Bind new groups and change the backend, model, thinking depth, project folder, permissions, data display and busy-turn followups of a group with no running task or unsettled record. Changing the backend or project starts a new conversation; changing the model or depth continues it. Stop tasks and start new sessions in Overview; a new session needs the group to be idle. Members can also change these with /model in the group."],
4341
4432
  ["当前支持飞书文字消息、忙时跟进、任务回复和停止。定时任务、生图、群消息管理、画布与预览暂未接入。", "Feishu text messages, busy-turn followups, task replies and stopping are supported. Schedules, image generation, group message management, canvas and previews are unavailable."],
4342
4433
  ["当前不执行定时任务;已有定义仅供查看。", "Schedules are not executed. Existing definitions are shown for reference only."],
4343
4434
  ["画布创建、同步与反馈暂未接入;已有链接仅供查看。", "Canvas creation, sync and feedback are unavailable. Existing links are view-only."],
@@ -4348,11 +4439,42 @@ const I18N_EN=[
4348
4439
  ["Codex 与 Claude Code 默认开启忙时跟进;长命令执行期间的补充会在工具步骤之间读取。只有确认模型已读取才显示已加入;不支持此能力时排队。控制台插话同样支持忙时跟进;关闭或暂不支持时排队。", "Busy-turn followups are on by default for Codex and Claude Code. Followups are read between tool steps; a long command must finish first. Joined means the model has read the message. Unsupported clients queue followups. Console messages also support busy-turn followups, or queue when followups are disabled or unavailable."],
4349
4440
  ["当前版本未接入预览服务。", "Preview services are unavailable in this version."],
4350
4441
  ["当前可查看模型配置与登录状态;各群的模型在「群·项目」里切换。添加或修改模型暂不可用。", "Model configuration and login status are shown here. Switch each group's model under Groups. Adding or changing models is unavailable."],
4351
- ["可添加模型,修改型号、端点、密钥、思考深度与推理强度,测试、隐藏或拖动排序;各群的模型在「群·项目」里切换。改型号、后端或端点时,用它的群须空闲,并会开始新的一段对话;默认模型和群正在用的模型不能隐藏。移除模型暂不可用。", "Add models; change their model id, endpoint, key, thinking depth and reasoning effort; test, hide or drag to reorder them. Switch each group's model under Groups. Changing a model id, backend or endpoint needs the groups using it to be idle and starts a new conversation for them. The default model and models in use by a group cannot be hidden. Removing models is unavailable."],
4442
+ ["可添加模型,修改端点、密钥、上下文与压缩,测试、隐藏或拖动排序;型号与思考深度按群在「群·项目」里选(第三方端点的型号在这里填)。改后端或端点时,用它的群须空闲,并会开始新的一段对话;默认模型和群正在用的模型不能隐藏。移除模型暂不可用。", "Add models; change their endpoint, key, context and compaction; test, hide or drag to reorder them. Model and thinking depth are chosen per group under Groups (a third-party endpoint's model is set here). Changing a backend or endpoint needs the groups using it to be idle and starts a new conversation for them. The default model and models in use by a group cannot be hidden. Removing models is unavailable."],
4352
4443
  ["默认开启 · 下轮生效", "On by default · Applies next turn"],
4353
4444
  ["当前只能添加或修改 Codex 或 Claude Code 模型", "Only Codex or Claude Code models can be added or changed right now."],
4354
4445
  ["模型上限", "Model limit"],
4355
4446
  ["当前窗口", "Active context"],
4447
+ ["检查本机 Claude Code 登录状态,不发起模型对话。", "Checks local Claude Code sign-in without starting a model conversation."],
4448
+ ["显示本机账号返回的额度与重置时间,不发起模型对话。", "Shows quota and reset times reported by the local account without starting a model conversation."],
4449
+ ["自定义值不能超过执行器支持的上限,超出已知上限无法保存。", "A custom window must stay within the executor limit. Values above a known limit cannot be saved."],
4450
+ ["上下文窗口设置仅适用于 Codex", "Context window settings are only available for Codex"],
4451
+ ["上下文窗口须为 0 或 100000–1050000 之间的整数", "Context window must be 0 or an integer from 100000 to 1050000"],
4452
+ ["自定义阈值为 10万–100万。Codex 可能按当前窗口提前压缩。", "Custom limit: 100k–1M tokens. Codex may compact earlier based on its active context."],
4453
+ ["自定义压缩窗口为 10万–100万。Claude Code 会预留空间,可能提前压缩。", "Custom window: 100k–1M tokens. Claude Code reserves space and may compact earlier."],
4454
+ ["容量与运行信息", "Capacity and runtime details"],
4455
+ ["上下文窗口", "Context window"],
4456
+ ["窗口大小(token)", "Window size (tokens)"],
4457
+ ["自动压缩", "Auto-compaction"],
4458
+ ["压缩阈值(token)", "Compaction limit (tokens)"],
4459
+ ["压缩窗口(token)", "Compaction window (tokens)"],
4460
+ ["默认沿用执行器配置;自定义从下轮生效,保留当前会话。", "Default follows executor settings. Custom values apply next turn without resetting the session."],
4461
+ ["请在模型卡片中设置压缩策略", "Set compaction on the model card"],
4462
+ ["请填写压缩值", "Enter a compaction value"],
4463
+ ["自定义窗口须为 10万–105万 token", "Custom context must be 100k–1.05M tokens"],
4464
+ ["型号与推理", "Model and reasoning"],
4465
+ ["上下文与压缩", "Context and compaction"],
4466
+ ["连接与登录", "Connection and sign-in"],
4467
+ ["模型容量参考", "Model capacity reference"],
4468
+ ["目录默认窗口", "Catalog default context"],
4469
+ ["最近上报可用窗口", "Last reported usable context"],
4470
+ ["实际可用窗口", "Actual usable context"],
4471
+ ["连接方式", "Connection"],
4472
+ ["型号", "Model"],
4473
+ ["窗口设置", "Configured context"],
4474
+ ["上下文窗口(token)", "Context window (tokens)"],
4475
+ ["使用本机 ChatGPT 订阅登录,额度与本机 Codex 共享。", "Uses the local ChatGPT subscription and shares usage with Codex on this machine."],
4476
+ ["默认沿用模型推荐强度;更高强度通常需要更多时间和额度。", "Default follows the model recommendation. Higher effort generally uses more time and quota."],
4477
+ ["0 = 沿用 Codex 已有配置或默认窗口。自定义窗口从下轮生效,不重置会话;实际容量以执行器回报为准。", "0 = existing Codex configuration or default. A custom window applies next turn without resetting the session; actual capacity depends on the executor report."],
4356
4478
  ["压缩阈值", "Compaction limit"],
4357
4479
  ["压缩窗口", "Compaction window"],
4358
4480
  ["模型窗口", "Model context"],
@@ -4365,15 +4487,12 @@ const I18N_EN=[
4365
4487
  ["最近上报", "Last reported"],
4366
4488
  ["官方型号", "Official model"],
4367
4489
  ["执行器默认", "Executor default"],
4368
- ["沿用旧全局设置", "Inherited legacy setting"],
4369
4490
  ["逐模型设置", "Per-model setting"],
4370
4491
  ["只读,不会改变模型容量", "Read-only; does not change model capacity"],
4371
4492
  ["只读 · 有官方上限时显示上限;否则显示当前执行器窗口。可用容量可能更低。", "Read-only · Shows the official model limit when known; otherwise the active executor context. Usable capacity may be lower."],
4372
4493
  ["添加后按模型目录或会话上报显示", "Shown from the model catalog or a session report after adding"],
4373
4494
  ["新型号保存后刷新", "Updates after saving the new model ID"],
4374
4495
  ["0 = 交给执行器默认;否则填 10万–100万。逐模型生效,实际触发点不会超过执行器的窗口限制。", "0 = executor default; otherwise enter 100k–1M. Applies per model; the executor still limits the actual trigger to its context window."],
4375
- ["当前沿用旧全局设置,保存后改为本模型独立设置。", "Currently inherits the old global value; saving makes it a setting for this model."],
4376
- ["请填写压缩阈值,使用执行器默认请填 0", "Enter a compaction limit, or 0 for the executor default"],
4377
4496
  ["本模型的设置值;执行器仍会按自身模型窗口限制实际触发点", "This model's configured value; the executor still caps the actual trigger by its context window"],
4378
4497
  ["Codex 可能按当前窗口提前压缩", "Codex may compact earlier based on its active context window"],
4379
4498
  ["Claude Code 在压缩窗口满前触发,设置值受模型窗口限制", "Claude Code compacts before this window fills; the value is capped by the model context"],
@@ -4538,6 +4657,24 @@ const I18N_EN=[
4538
4657
  ["管理模型 · 拖","Manage models · drag"],
4539
4658
  ["排序 ·","to reorder ·"],
4540
4659
  ["配端点/key/思考深度 · 模型按群在「群·项目」选","sets endpoint / key / thinking depth · models are chosen per group on the Groups · Projects page"],
4660
+ ["配端点/key/压缩 · 型号与思考深度按群在「群·项目」选", "sets endpoint / key / compaction · model and thinking depth are chosen per group on the Groups · Projects page"],
4661
+ ["按群选", "Per group"],
4662
+ ["个群在用", "groups using it"],
4663
+ ["型号与深度", "Model and depth"],
4664
+ ["型号与思考深度按群在「群·项目」里选", "Model and thinking depth are chosen per group under Groups"],
4665
+ [";这个端点的型号在下面填,各群共用", "; this endpoint's model is set below and shared by its groups"],
4666
+ ["名称与型号", "Name and model"],
4667
+ ["后端", "Backend"],
4668
+ ["未选", "Not chosen"],
4669
+ ["加载清单…", "loading list…"],
4670
+ ["在「模型」页改", "change on the Models page"],
4671
+ ["清单暂时取不到", "List unavailable right now"],
4672
+ ["这个型号不支持", "Not supported by this model"],
4673
+ ["上下文", "Context"],
4674
+ ["型号 id", "Model id"],
4675
+ ["换后端会开新对话;换型号或调深度接着当前对话 · 群空闲时才能改", "A backend change starts a new conversation; a model or depth change continues it · only while the group is idle"],
4676
+ ["没有变化", "No change"],
4677
+ ["填写型号 id", "Enter a model id"],
4541
4678
  ["添加模型","Add model"],
4542
4679
  ["插件库 · MCP","Plugin library · MCP"],
4543
4680
  ["本机装好的 MCP · 按运行时分两套 · 命令行安装、即时刷新","MCP servers installed on this machine · two sets, one per runtime · installed from the command line, refreshes instantly"],
@@ -4601,11 +4738,7 @@ const I18N_EN=[
4601
4738
  ["。",". "],
4602
4739
  ["Codex 订阅登录 / 额度","Codex subscription sign-in / quota"],
4603
4740
  ["验证登录 / 刷新额度","Verify sign-in / refresh quota"],
4604
- ["额度 = 与你交互式 Codex 共享的订阅窗口剩余(窗口时长随官方口径,当前多为「周」)。查询不耗消息额度。","Quota = what remains of the subscription window shared with your interactive Codex (window length follows the official definition, currently mostly weekly). Checking does not use message quota."],
4605
- ["推理强度(model_reasoning_effort)","Reasoning effort (model_reasoning_effort)"],
4606
4741
  ["默认(模型内置)","Default (model built-in)"],
4607
- ["低档更快省额度,高档更强更慢;编码/agentic 类任务 xhigh 通常是甜点档。选项随所选模型变化(默认走模型内置),逐轮生效、切即用。万一某档超出你的订阅套餐,运行时会被拒 → 自动去档重跑一轮,群里不会空等。","Lower levels are faster and cheaper on quota, higher ones stronger and slower; xhigh is usually the sweet spot for coding / agentic work. Options follow the selected model (default = model built-in); applies per turn, takes effect immediately. If a level exceeds your subscription plan the runtime rejects it → the level is dropped and the turn rerun automatically, so the group is never left waiting."],
4608
- ["思考深度(CLAUDE_CODE_EFFORT_LEVEL)","Thinking depth (CLAUDE_CODE_EFFORT_LEVEL)"],
4609
4742
  ["默认","Default"],
4610
4743
  ["低档更快更省,高档更强更慢。编码 / agentic 类任务","Lower is faster and cheaper, higher is stronger and slower. For coding / agentic work,"],
4611
4744
  ["通常是甜点档(也是 Claude Code 自己的默认)。","is usually the sweet spot (it is also Claude Code's own default)."],
@@ -5551,6 +5684,7 @@ const I18N_EN=[
5551
5684
  ["bot 不在任何群 / 未配飞书", "The bot is in no group, or Feishu isn't configured"],
5552
5685
  ];
5553
5686
  const I18N_EN_RX=[
5687
+ [/^上下文窗口超过当前模型目录上限 (\d+) token$/,(_,n)=>`Context window exceeds the current model catalog limit of ${n} tokens`],
5554
5688
  [/^Codex 当前窗口 (\d+(?:\.\d+)?)万 token$/,(_all,n)=>'Current Codex context '+(+n>=100?(+n/100).toFixed(2).replace(/\.00$/,'')+'M':(+n*10).toFixed(1).replace(/\.0$/,'')+'k')+' tokens'],
5555
5689
  [/^当前窗口 (\d+(?:\.\d+)?)万 token$/,(_all,n)=>'Active context '+(+n>=100?(+n/100).toFixed(2).replace(/\.00$/,'')+'M':(+n*10).toFixed(1).replace(/\.0$/,'')+'k')+' tokens'],
5556
5690
  [/^当前可用 (\d+(?:\.\d+)?)万 token$/,(_all,n)=>'Currently usable '+(+n>=100?(+n/100).toFixed(2).replace(/\.00$/,'')+'M':(+n*10).toFixed(1).replace(/\.0$/,'')+'k')+' tokens'],