@leviyuan/lodestar 0.11.13 → 0.11.14

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.
Files changed (2) hide show
  1. package/dist/lodestar.js +10 -6
  2. package/package.json +1 -1
package/dist/lodestar.js CHANGED
@@ -271,7 +271,7 @@ ${z}`:"",N=fX(Z)?Ru(Q,J,z):yX(Z)?Cu(Q,J,z):oX(Z)?gG0(Z,Q,J,z):hX(Z)?qu(Q,J,z):XY
271
271
  `)}]}}function rX(x,Z,Q,J){let X=C3(Z,Q),z=zY(Z),Y=X?`\uD83D\uDD10 等审批 · ${z}: ${X}`:`\uD83D\uDD10 等审批 · ${z}`,G=fX(Z)?Ru(Q,null):yX(Z)?Cu(Q,null):hX(Z)?qu(Q,null):XY(Z)?fu(Q,null):mX(Z)?yu(Q,null):DU(Z)?hu(Q,null):_X(Z)?mu(Q,null):nX(Z)?Au(Q,null):lX(Z)?Eu(Q,null):dX(Z)?ku(Q,null):iX(Z)?gu(Q,null):BU(Z)?Pu(Z,Q,null):pX(Z)?uu(Q,null):sX(Z)?wX({prompt:KY(Q)||"提交计划"},null):OU(Z)?wX(Q,null):"```\n"+JSON.stringify(Q??{},null,2).slice(0,2000)+"\n```";return{tag:"collapsible_panel",element_id:$0.tool(x),header:{title:{tag:"plain_text",content:Y}},expanded:!0,elements:[{tag:"markdown",content:G},{tag:"column_set",columns:[FU("✅ 允许","primary",J,"allow"),FU("♾️ 始终允许","default",J,"allow_always"),FU("❌ 拒绝","danger",J,"deny")]}]}}function FU(x,Z,Q,J){return{tag:"column",width:"weighted",weight:1,elements:[{tag:"button",text:{tag:"plain_text",content:x},type:Z,behaviors:[{type:"callback",value:{kind:"permission",request_id:Q,decision:J}}]}]}}var NG0=300,Bu="server_tool:";var CU=A0(()=>{v6()});function eX(x){return JSON.stringify({objective:x.objective,status:x.status,tokenBudget:x.tokenBudget})}function wG0(x){switch(x){case"pending":return"☐";case"inProgress":return"\uD83D\uDD04";case"completed":return"✅";default:return"·"}}function fG0(x,Z){return x.length>Z?x.slice(0,Z)+"…":x}function tX(x){if(!Number.isFinite(x)||x<0)return"--";if(x<1000)return String(Math.round(x));if(x<1e6)return(x/1000).toFixed(x<1e4?1:0).replace(/\.0$/,"")+"k";return(x/1e6).toFixed(2).replace(/\.?0+$/,"")+"m"}function _u(x){switch(x){case"active":return"进行中";case"paused":return"已暂停";case"blocked":return"受阻";case"usageLimited":return"额度受限";case"budgetLimited":return"预算受限";case"complete":return"已完成";default:return x}}function yG0(x,Z=""){let Q=Z.trim();if(x.length===0)return Q?"\uD83D\uDCCB 计划草稿":"\uD83D\uDCCB 计划更新";let J=x.filter((G)=>G.status==="completed").length,X=x.filter((G)=>G.status==="inProgress").length,z=x.filter((G)=>G.status==="pending").length,Y=[`${x.length} 项`];if(X)Y.push(`${X} 进行中`);if(J)Y.push(`${J} 完成`);if(z)Y.push(`${z} 待办`);return`\uD83D\uDCCB 计划更新 · ${Y.join(" · ")}`}function lu(x){if(!Number.isFinite(x)||x<0)return"MISS";if(x<60)return`${Math.round(x)}s`;let Z=Math.floor(x/60),Q=Math.round(x%60);if(Z<60)return Q?`${Z}m ${Q}s`:`${Z}m`;let J=Math.floor(Z/60),X=Z%60;return X?`${J}h ${X}m`:`${J}h`}function jU(x){return typeof x==="number"&&Number.isFinite(x)?x:null}function hG0(x){let Z=jU(x.startedAtMs),Q=jU(x.completedAtMs);if(Z==null||Q==null||Q<Z)return"";return` · 耗时 ${lu((Q-Z)/1000)}`}function mG0(x){let Z=jU(x);return Z==null?null:tX(Z)}function _G0(x,Z){if(!Z)return["压缩中..."];let Q=[],J=typeof x.trigger==="string"&&x.trigger.trim()?x.trigger.trim():"",X=mG0(x.preTokens),z=typeof x.sourceType==="string"&&x.sourceType.trim()?x.sourceType.trim():"";if(J)Q.push(`**触发**: ${J}`);if(X)Q.push(`**压缩前**: ${X} tokens`);if(z)Q.push(`**来源**: ${z}`);return Q.length>0?Q:["压缩完成,无摘要内容"]}function IU(x,Z,Q){if(x==null||!Number.isFinite(x))return null;let J=Lu(x,Z,Q);return J?`${J.used}% · ${uX(x,Z)}`:"--"}function TU(x){let Z=tX(x?.input_tokens??Number.NaN),Q=tX(x?.cache_read_input_tokens??Number.NaN),J=tX(x?.output_tokens??Number.NaN);return`└ 入 ${Z} | 缓 ${Q} | 出 ${J}`}function WY(x,Z,Q){let J=Z&&typeof Z==="object"?Z:{},X=J.phase==="end"||J.phase==="event",z=X?"✅":"⏳",Y=X?hG0(J):"",G=`${z} \uD83D\uDEA8 上下文压缩 #${x+1}${Y}`,W=_G0(J,X);return{tag:"collapsible_panel",element_id:Q,header:{title:{tag:"plain_text",content:G}},expanded:!1,elements:[{tag:"markdown",content:W.join(`
272
272
  `)}]}}function nG0(x,Z,Q=""){let J=["**\uD83D\uDCCB 当前计划**"],X=Z?.trim();if(X)J.push(""),J.push(X);if(x.length>0){J.push("");for(let z of x)J.push(`- ${wG0(z.status)} ${z.step}`)}else{let z=Q.trim();if(J.push(""),z)J.push("正在生成计划草稿..."),J.push(""),J.push(z);else J.push("--")}return J.join(`
273
273
  `)}function cU(x,Z,Q="",J){return{tag:"collapsible_panel",element_id:J,header:{title:{tag:"plain_text",content:yG0(x,Q)}},expanded:!1,elements:[{tag:"markdown",content:nG0(x,Z,Q)}]}}function qU(x,Z){let Q=Number.isFinite(x.tokensUsed)?String(x.tokensUsed):"MISS",J=x.tokenBudget==null?"":Number.isFinite(x.tokenBudget)?` / ${x.tokenBudget}`:" / MISS",X=[`**\uD83C\uDFAF 当前目标** · ${_u(x.status)}`,"",x.objective,"",`- 用量: ${Q}${J} tokens`,`- 用时: ${lu(x.timeUsedSeconds)}`];return{tag:"collapsible_panel",element_id:Z,header:{title:{tag:"plain_text",content:`\uD83C\uDFAF 目标更新 · ${_u(x.status)}: ${fG0(x.objective,48)}`}},expanded:!1,elements:[{tag:"markdown",content:X.join(`
274
- `)}]}}function aX(x){let Z=x.provider==="claude"?"Claude":"Codex",Q=x.kind==="card_full"?[{tag:"markdown",content:`\uD83D\uDCE8 接续上一张(同一轮 ${Z},前卡写满或写入受限)`}]:[],J=x.userInputs??[],X=`\uD83D\uDCE5 收到 (${J.length}) ${x.directStart?"\uD83D\uDE80":`#${x.turn}`}`,z=J.length>0?[{tag:"collapsible_panel",element_id:$0.userInput,header:{title:{tag:"plain_text",content:X}},expanded:!1,elements:J.map((Y)=>({tag:"markdown",content:Y.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}))}]:[];return{schema:"2.0",config:{streaming_mode:!0,summary:{content:"[Lodestar 正在生成…]"},streaming_config:{print_frequency_ms:{default:60,android:60,ios:60,pc:30},print_step:{default:2,android:2,ios:2,pc:4},print_strategy:"fast"}},body:{elements:[...Q,...z,{tag:"markdown",element_id:$0.footer,content:x.initialFooter??"Waiting...(0s)"}]}}}function du(x,Z,Q){let J=x.question.trim()||`问题 ${Z+1}`;return`${Z+1}/${Q} · ${J}`}function lG0(x,Z){if(!Z)return"?";return Z.customText??(Z.optionIdx!==void 0?x.options[Z.optionIdx]?.label:void 0)??"?"}function dG0(x,Z,Q,J="ask"){let X=[];for(let z=0;z<x.options.length;z++){let Y=x.options[z],G=Y.description?` · ${Y.description}`:"";X.push({tag:"interactive_container",background_style:"default",has_border:!0,corner_radius:"6px",padding:"8px 12px",margin:"4px 0px 4px 0px",behaviors:[{type:"callback",value:{kind:J,tool_use_id:Z,question_idx:Q,option_idx:z}}],elements:[{tag:"markdown",content:`**${Y.label}**${G}`}]})}return X}function nu(x,Z,Q,J,X){if(x.length===0)return null;let z=x.length,Y=[];for(let G=0;G<x.length;G++){let W=x[G];if(!W)continue;let H=du(W,G,z),K=J.get(G);if(K){let N=lG0(W,K),L=[`**✅ ${H}**`,`**回答**:${N}`];Y.push({tag:"markdown",content:L.join(`
274
+ `)}]}}function aX(x){let Z=x.provider==="claude"?"Claude":"Codex",Q=x.kind==="card_full"?[{tag:"markdown",content:`\uD83D\uDCE8 接续上一张(同一轮 ${Z},前卡写满或写入受限)`}]:x.kind==="bg_task_resume"?[{tag:"markdown",content:`\uD83D\uDD01 后台任务完成,${Z} 继续处理结果 #${x.turn}`}]:[],J=x.userInputs??[],X=`\uD83D\uDCE5 收到 (${J.length}) ${x.directStart?"\uD83D\uDE80":`#${x.turn}`}`,z=J.length>0?[{tag:"collapsible_panel",element_id:$0.userInput,header:{title:{tag:"plain_text",content:X}},expanded:!1,elements:J.map((Y)=>({tag:"markdown",content:Y.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}))}]:[];return{schema:"2.0",config:{streaming_mode:!0,summary:{content:"[Lodestar 正在生成…]"},streaming_config:{print_frequency_ms:{default:60,android:60,ios:60,pc:30},print_step:{default:2,android:2,ios:2,pc:4},print_strategy:"fast"}},body:{elements:[...Q,...z,{tag:"markdown",element_id:$0.footer,content:x.initialFooter??"Waiting...(0s)"}]}}}function du(x,Z,Q){let J=x.question.trim()||`问题 ${Z+1}`;return`${Z+1}/${Q} · ${J}`}function lG0(x,Z){if(!Z)return"?";return Z.customText??(Z.optionIdx!==void 0?x.options[Z.optionIdx]?.label:void 0)??"?"}function dG0(x,Z,Q,J="ask"){let X=[];for(let z=0;z<x.options.length;z++){let Y=x.options[z],G=Y.description?` · ${Y.description}`:"";X.push({tag:"interactive_container",background_style:"default",has_border:!0,corner_radius:"6px",padding:"8px 12px",margin:"4px 0px 4px 0px",behaviors:[{type:"callback",value:{kind:J,tool_use_id:Z,question_idx:Q,option_idx:z}}],elements:[{tag:"markdown",content:`**${Y.label}**${G}`}]})}return X}function nu(x,Z,Q,J,X){if(x.length===0)return null;let z=x.length,Y=[];for(let G=0;G<x.length;G++){let W=x[G];if(!W)continue;let H=du(W,G,z),K=J.get(G);if(K){let N=lG0(W,K),L=[`**✅ ${H}**`,`**回答**:${N}`];Y.push({tag:"markdown",content:L.join(`
275
275
  `)});continue}if(G===Q){if(Y.push({tag:"markdown",content:`**\uD83E\uDD14 ${H}**`}),W.options.length>0)Y.push(...dG0(W,Z,G,X));Y.push({tag:"markdown",content:"_\uD83D\uDCAC 也可以直接在群里回复你的答案(裸词命令 `hi`/`stop`/`kill`/`restart`/`clear`/`model` 仍然优先)_"});continue}Y.push({tag:"markdown",content:`**⏳ ${H}**`})}return Y}function P4(x,Z,Q,J="\uD83E\uDD14",X,z="ask"){let Y=Q.length,G=X?.answered??new Map,W=X?.currentIdx,H=W===void 0&&G.size>0,K=[],N;if(H){N=`${J} 已回答 · ${Y}/${Y}`,K.push(...nu(Q,Z,W,G,z)??[]);let L=[...G.values()].reverse().find((D)=>D.user)?.user;if(L)K.push({tag:"markdown",content:`
276
276
  *— 由 ${L} 回答*`})}else if(W!==void 0&&Q[W])N=`${J} 等你确认 · ${W+1}/${Y}`,K.push(...nu(Q,Z,W,G,z)??[]);else if(N=`${J} 等你确认`,Q[0])K.push({tag:"markdown",content:`**${du(Q[0],0,Y||1)}**`});return{tag:"collapsible_panel",element_id:$0.tool(x),header:{title:{tag:"plain_text",content:N}},expanded:!H,elements:K}}function xz(x,Z,Q){let J=Z.length||1,X=Q.currentIdx??Math.max(0,Math.min(J-1,Q.answered.size)),z=Z[X]?.question?.trim()||Z[0]?.question?.trim()||"Codex 请求澄清";return{schema:"2.0",config:{update_multi:!0,summary:{content:`❓ ${Math.min(X+1,J)}/${J} ${z.slice(0,54)}`}},body:{elements:[P4(0,x,Z,Q.currentIdx===void 0?"✅":"\uD83E\uDD14",Q,"host_ask")]}}}var iu=A0(()=>{v6();CU()});import{execFile as iG0}from"node:child_process";import{readFileSync as bU,statfsSync as pG0,statSync as pu}from"node:fs";import{cpus as sG0,homedir as oG0}from"node:os";import{promisify as rG0}from"node:util";function eG0(){try{let x=bU("/proc/loadavg","utf8").trim().split(/\s+/);return{cores:sG0().length,load1:parseFloat(x[0]??"0"),load5:parseFloat(x[1]??"0"),load15:parseFloat(x[2]??"0")}}catch(x){return g(`sysinfo: read /proc/loadavg failed: ${x}`),null}}function aG0(){try{let x=bU("/proc/meminfo","utf8"),Z=(z)=>{let Y=x.match(new RegExp(`^${z}:\\s+(\\d+)\\s*kB`,"m"));return Y?parseInt(Y[1],10)*1024:0},Q=Z("MemTotal"),J=Z("MemAvailable");if(!Q)return null;let X=Math.max(0,Q-J);return{totalBytes:Q,availBytes:J,usedBytes:X,percent:Math.round(X/Q*100)}}catch(x){return g(`sysinfo: read /proc/meminfo failed: ${x}`),null}}function su(x,Z){try{let Q=pG0(Z,{bigint:!1}),J=Q.blocks*Q.bsize,X=Q.bavail*Q.bsize,z=Math.max(0,J-X);if(!J)return null;return{label:x,path:Z,totalBytes:J,availBytes:X,usedBytes:z,percent:Math.round(z/J*100)}}catch(Q){return g(`sysinfo: statfs ${Z} failed: ${Q}`),null}}function xF0(){let x=[],Z=su("/","/");if(Z)x.push(Z);let Q=oG0();if(Q&&Q!=="/"){let J=!1;try{let X=pu("/"),z=pu(Q);J=X.dev===z.dev}catch{}if(!J){let X=su("$HOME",Q);if(X)x.push(X)}}return x}function YF0(){try{let x=bU("/proc/uptime","utf8").trim().split(/\s+/);return parseFloat(x[0]??"0")}catch{return null}}async function ou(x,Z=2000){try{let{stdout:Q}=await tG0("systemctl",["--user",...x],{timeout:Z,maxBuffer:4194304});return Q}catch(Q){return g(`sysinfo: systemctl ${x.join(" ")} failed: ${Q}`),null}}function ru(x){return tu.some((Z)=>x.startsWith(Z))&&x.endsWith(".service")}async function ZF0(){let x=await ou(["list-units","--type=service","--all","--no-legend","--plain",...tu.map((W)=>`${W}*`)]);if(x===null)return{services:[],error:"systemctl 不可用"};let Z=x.split(`
277
277
  `).map((W)=>W.trim()).filter((W)=>W.length>0);if(Z.length===0)return{services:[],error:null};let Q=[];for(let W of Z){let K=W.replace(/^●\s*/,"").split(/\s+/)[0];if(!K)continue;if(!ru(K))continue;Q.push(K)}if(Q.length===0)return{services:[],error:null};let J=await ou(["show",...Q,"-p","Id","-p","ActiveState","-p","SubState","-p","ActiveEnterTimestampMonotonic","-p","StateChangeTimestampMonotonic"]);if(J===null)return{services:[],error:"systemctl show 失败"};let X=YF0(),z=J.split(/\n\s*\n/),Y=[],G=(W)=>{let H=parseInt(W??"0",10);if(H<=0||X===null)return null;return Math.max(0,X-H/1e6)};for(let W of z){let H={};for(let N of W.split(`
@@ -325,15 +325,19 @@ ${Q}`:Z}function RN(x){return Cz(x)?"已载入wt特殊约定":null}function Ly(x
325
325
  ${Q}`:Z}function Cz(x){let Z=k3(x),Q=Wy(x.workDir,R5(x),Z);if(!Q)return null;return[`你要把下面这份额外的工作树约定视为和AGENTS.md一样重要。来源文件:${Q.path}`,"",`# Additional AGENTS.md instructions for ${x.workDir}`,"","<INSTRUCTIONS>",Q.content,"</INSTRUCTIONS>"].join(`
326
326
  `)}async function Dy(x,Z,Q){if(!Z){await CN(x);return}let J=E3(Z);if(!J){await C0(x.chatId,"❌ 名称无效。用英文/数字/._-,最长 63。");return}if(jY(J)){await C0(x.chatId,`❌ ${J} 是 AI 自动化系统保留 worktree,不能用 wt 命令操作。`);return}if(!Q){await C0(x.chatId,"❌ 找不到发起人,不能拉群。");return}let X=k3(x),z=R5(x),Y;try{Y=Hy(z,X,J)}catch(G){await C0(x.chatId,`❌ wt 失败: ${Hx(G)}`);return}try{let G=await Zu(Y.chatName,Q),W=G.created?"已创建":G.joined?"已加入":"已在群内";if(!await mx(x.chatId,$Y({slug:J,branch:Y.branch,status:W})))await wx(x.chatId,`❌ wt 卡片失败: ${J}`);if(!await mx(G.chatId,$Y({slug:J,branch:Y.branch,status:"就绪",body:"开始吧。"})))await wx(G.chatId,`❌ wt 卡片失败: ${J}`)}catch(G){await C0(x.chatId,`❌ wt 已建,拉群失败: ${Hx(G)}`)}}async function Oy(x,Z){let Q=k3(x),J=R5(x),X=Vy(J,Q),z=X.filter((W)=>W.state==="merged"&&!W.mounted).length,Y=X.filter((W)=>W.state!=="merged"||W.mounted),G=await VU();return fU({projectName:Q,projectDir:J,hiddenMergedUnmountedCount:z,notice:Z,entries:Y.map((W)=>{let H=G.get(W.chatName)??[],K=l1.get(W.chatName),N=K&&H.includes(K)?K:H.length===1?H[0]:null;return{slug:W.slug,chatName:W.chatName,branch:W.branch,state:W.state,path:W.worktreePath??W.expectedPath,mounted:W.mounted,dirtyCount:W.dirtyCount,statusLine:W.statusLine,error:W.error,chatId:N,duplicateChatCount:H.length,protected:jY(W.slug)}})})}async function CN(x){try{let Z=await Oy(x);if(!await mx(x.chatId,Z))await wx(x.chatId,"❌ wt 列表失败")}catch(Z){await C0(x.chatId,`❌ wt 列表失败: ${Hx(Z)}`)}}async function IY(x,Z,Q,J){try{return{ok:Z,message:Q,card:await Oy(x,{type:J,content:Q})}}catch(X){let z=`列表刷新失败: ${Hx(X)}`;return g(`session "${x.sessionName}": wt action panel refresh failed: ${Hx(X)}`),{ok:!1,message:`${Q}
327
327
  ${z}`,card:$Y({slug:"wt",branch:"work/*",status:Q,body:z,template:"red"})}}}async function By(x,Z){let Q=E3(Z);if(!Q)return IY(x,!1,"❌ 名称无效","error");if(jY(Q))return IY(x,!1,`❌ ${Q} 是 AI 自动化系统保留 worktree,不能解散。`,"error");let J=k3(x),X=R5(x);try{let z=S5(J,Q);if(x.hasRunningPeerSession(z)){let H=`❌ 解散 ${Q} 失败: Codex 正在运行,请先在 ${z} 群里 stop 或 kill。`;return IY(x,!1,H,"error")}BN(X,J,Q);let Y=await Qu(z),G=Gy(X,J,Q),W=[`✅ ${Q} 已解散`,G.removedWorktree?"dir removed":"dir missing",Y.disbanded?"group removed":"group missing",G.branch].join(`
328
- `);return IY(x,!0,W,"success")}catch(z){let Y=`❌ 解散 ${Q} 失败: ${Hx(z)}`;return IY(x,!1,Y,"error")}}var vy=A0(()=>{Zy();Q4();ox();Bx();Uy()});import{existsSync as SU0}from"node:fs";import{isAbsolute as RU0,join as CU0}from"node:path";function jU0(x){return x.itemId||x.turnId||"__latest__"}function IU0(x){let Z=null;for(let Q of x.contextCompactionPending.keys())Z=Q;return Z}function TU0(x,Z){if(!x)return Z;return{...x,...Z,phase:"end"}}function jy(x,Z){let Q=Math.max(0,Math.floor((Date.now()-Z)/1000));return`${x} (${Q}s)`}var My=1000,cU0="Thinking...",qU0="Writing...",bU0="Working...",Sy=1e4,Ry=120000,AU0=50,jN=5,Cy=250,C5;var Iy=A0(()=>{R7();P9();TP();R3();Q4();ox();Bx();AU();kw();fw();nw();ow();Uf();Of();LN();kf();uf();ff();hf();df();tf();vy();C5=class C5{sessionName;chatId;opts;static all=new Set;proc=null;currentTurn=null;backgroundTasks=[];pendingBgTasks=[];backgroundCard=null;backgroundRefreshTimer=null;openingBackground=!1;backgroundDetailAdded=new Set;lastMainTaskToolUseId=null;pendingRebuildBackgroundCard=!1;pendingPermissions=new Map;pendingAsks=new Map;pendingHostAsks=new Map;currentGoal=null;status="stopped";pendingUserMessageCount=0;pendingMidTurnMsgs=[];pendingTurnInputs=[];multiMsgBuffer=null;multiMsgReactions=new Map;lastUserOpenId="";pendingReactionIds=new Map;currentBatchReactionIds=new Map;initCount=0;openingTurn=!1;turnCounter=0;userInterrupted=!1;lastSessionId=null;selectedProvider="claude";selectedModel=null;selectedEffort=null;modelPanels=new Map;startedAt=0;cumStats={tokens:0,costUsd:0,turns:0};lastTurnDelta=null;currentTurnUsageBaseline=null;currentTurnUsageBaselineKnown=!1;lastTurnUsage=null;usageTotalsSeedUnknown=!1;manualContextCompactionPending=!1;runningAgy=null;startingAgy=!1;agyForwardPrompts=new Map;taskBoard=[];constructor(x,Z,Q={}){this.sessionName=x;this.chatId=Z;this.opts=Q;C5.all.add(this);let J=aP(x);if(this.selectedProvider=J?.provider??"claude",this.selectedModel=J?.model??null,this.selectedEffort=J?.effort??null,J){if(this.selectedProvider==="claude"&&this.selectedModel!=="claude:glm")this.selectedModel="claude:glm",this.selectedEffort="max";else if(this.selectedProvider==="codex"&&this.selectedModel!=="gpt-5.5")this.selectedModel="gpt-5.5",this.selectedEffort="xhigh"}if(this.selectedModel)g(`session "${x}": restored selected provider=${this.selectedProvider} model=${this.selectedModel} effort=${this.selectedEffort??"unset"}`);if(this.lastSessionId=KU(x,this.selectedProvider),this.lastSessionId)g(`session "${x}": restored ${this.selectedProvider} lastSessionId=${this.lastSessionId.slice(0,8)}…`)}peerSnapshot(){return{name:this.sessionName,status:this.status,uptimeMs:this.startedAt?Date.now()-this.startedAt:void 0}}get workDir(){let x=zU(this.sessionName)?.cwd;return x&&x.trim()?x:CU0(M3,this.sessionName)}isRunning(){return!!this.proc&&this.proc.isAlive()}currentProvider(){return this.selectedProvider}hasRunningPeerSession(x){return[...C5.all].some((Z)=>Z.sessionName===x&&Z.isRunning())}modelForSpawn(){return this.selectedModel??void 0}effortForSpawn(){return this.selectedProvider==="codex"&&S8(this.selectedEffort)?this.selectedEffort:iV}claudeEffortForSpawn(){return this.selectedProvider==="claude"&&Z6(this.selectedEffort)?this.selectedEffort:Y3}currentModelLabel(){return this.selectedModel??this.proc?.lastModel??null}currentEffortLabel(){return this.selectedEffort??this.proc?.lastEffort??(this.selectedProvider==="claude"?Y3:iV)}modelEffortLabel(){let x=this.currentModelLabel(),Z=this.currentEffortLabel();return x?`${x}/${Z}`:Z}withModel(x){let Z=this.modelEffortLabel();return x.includes(Z)?x:`${x} · ${Z}`}replaceFooterContent(x,Z){return Zx(x,$0.footer,{tag:"markdown",element_id:$0.footer,content:Z.trim()||" "})}modelLine(){let x=this.currentModelLabel(),Z=this.currentEffortLabel(),Q=this.selectedProvider==="claude"?x?.replace(/^claude:/i,""):x,J=Q?`${Q}/${Z}`:Z;return this.selectedProvider==="claude"?`${Q6(this.selectedProvider)} · ${J}`:J}backendLabel(x=this.selectedProvider){return Q6(x)}spawnAgent(x){if(this.selectedProvider==="claude")return CP(),new ZU({workDir:this.workDir,model:this.modelForSpawn(),effort:this.claudeEffortForSpawn(),resumeSessionId:x,appendSystemPrompt:this.spawnDeveloperInstructions(),profile:zU(this.sessionName)});return new pV({workDir:this.workDir,model:this.modelForSpawn(),effort:this.effortForSpawn(),resumeSessionId:x,appendSystemPrompt:this.spawnDeveloperInstructions()})}async applyModelSelection(x,Z,Q){this.selectedProvider=x,this.selectedModel=Z,this.selectedEffort=Q,this.lastSessionId=KU(this.sessionName,x),eP(this.sessionName,x,Z,Q),await this.stopIdleMismatchedProcess()}async stopIdleMismatchedProcess(){if(!this.proc?.isAlive())return;if(this.proc.provider===this.selectedProvider)return;if(this.currentTurn||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0)return;let x=this.proc;g(`session "${this.sessionName}": stop idle ${x.provider} process after switching to ${this.selectedProvider}`),this.proc=null,this.initCount=0,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await x.kill(1000)}async stopIdleCurrentProcess(x){if(!this.proc?.isAlive())return!1;if(this.currentTurn||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0)return!1;let Z=this.proc;return g(`session "${this.sessionName}": stop idle ${Z.provider} process: ${x}`),this.proc=null,this.initCount=0,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await Z.kill(1000),!0}startFooterTimer(x,Z,Q=(J)=>J){let J=Date.now(),X=Z,z=!1,Y=()=>{if(z)return;this.replaceFooterContent(x,Q(jy(X,J)))},G=setInterval(Y,My);return Y(),{setStatus(W){X=W,Y()},stop(){if(z)return;z=!0,clearInterval(G)},elapsedSec(){return((Date.now()-J)/1000).toFixed(1)}}}async openStatusCard(x,Z,Q="blue"){let J=Date.now(),X=kU({sessionName:this.sessionName,title:x,status:jy(Z,J),template:Q}),z=await mx(this.chatId,X);if(!z)return g(`session "${this.sessionName}": status card send failed title=${x}`),await wx(this.chatId,`❌ 创建状态卡片失败: ${x}`),null;let Y;try{Y=await a8(z)}catch(G){return g(`session "${this.sessionName}": status card id_convert failed title=${x}: ${G}`),await wx(this.chatId,`❌ 状态卡片初始化失败: ${x}`),null}return E4(Y,1),{cardId:Y,title:x,timer:this.startFooterTimer(Y,Z,(G)=>GY(x,G))}}setStatusCard(x,Z){x?.timer.setStatus(Z)}async closeStatusCard(x,Z){if(!x)return;x.timer.stop();let Q=x.timer.elapsedSec(),J=GY(x.title,`${Z} (${Q}s)`);await E8(x.cardId),await this.replaceFooterContent(x.cardId,J),v1(x.cardId),await E1(x.cardId,x4({durationSec:Q,suffix:Z})),await d1(x.cardId)}beginAgyForwardToCodex(x,Z=""){return Hf(this,x,Z)}runAgyCommand(x){return Gf(this,x)}stopAgyTask(x="\uD83D\uDED1 agy 已打断"){return $f(this,x)}resetFreshConversationState(){this.turnCounter=0,this.currentGoal=null,this.cumStats={tokens:0,costUsd:0,turns:0},this.lastTurnDelta=null,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.lastTurnUsage=null,this.usageTotalsSeedUnknown=!1}async start(x={}){let Z=x.announce??!0,Q=x.onStatus;if(this.isRunning()){if(this.proc?.provider===this.selectedProvider)return Q?.(this.withModel(`✅ ${this.backendLabel()} 已运行`)),!0;if(await this.stopIdleMismatchedProcess(),this.proc?.isAlive())return Q?.(`⚠️ 当前 ${this.backendLabel(this.proc.provider)} turn 尚未结束,模型切换将在后续新 turn 生效`),!0}if(this.selectedProvider==="codex")Q?.("\uD83D\uDD0E 检查 Codex 登录");else Q?.("\uD83D\uDD0E 检查 Claude Code");if(this.selectedProvider==="codex"&&!Gu()){if(this.status="stopped",this.opts.onLifecycleChange?.(),Q?.("❌ Codex 未登录 ChatGPT 账号"),Z)await C0(this.chatId,"❌ Codex 未登录 ChatGPT 账号。\n请在服务器上运行 `codex login` 后再试。");return!1}if(!SU0(this.workDir)){if(Q?.(`\uD83C\uDD95 创建项目目录 ~/${this.sessionName}`),Z)await C0(this.chatId,`\uD83C\uDD95 目录 ~/${this.sessionName} 不存在,正在创建…`);try{Hu(this.workDir)}catch(G){if(this.status="stopped",this.opts.onLifecycleChange?.(),Q?.(`❌ 创建项目失败: ${G}`),Z)await C0(this.chatId,`❌ 创建项目失败: ${G}`);return!1}}if(!x.freshConversationStateAlreadyReset)this.resetFreshConversationState();this.status="starting",Q?.(this.withModel(`\uD83D\uDE80 启动 ${this.backendLabel()}`));let J;try{J=this.spawnAgent()}catch(G){let W=`${this.backendLabel()} 启动失败: ${Hx(G)}`;if(g(`session "${this.sessionName}": ${W}`),Q?.(`❌ ${W}`),Z)await C0(this.chatId,`❌ ${W}`);return this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),!1}this.proc=J,this.wireProc(this.proc);let X=this.backendLabel(),z=this.selectedProvider==="claude"?this.waitForProcEarlyFailure(this.proc,Cy):this.waitForProcInit(this.proc,5000);Q?.(this.selectedProvider==="claude"?`⏳ 检查 ${X} 启动`:`⏳ 等待 ${X} init`),this.proc.sendInitialize();let Y=await z;if(Y.state==="error"||Y.state==="exit"){let G=Y.error?Hx(Y.error):Y.state;if(g(`session "${this.sessionName}": ${this.selectedProvider} init failed: ${G}`),Q?.(`❌ ${X} 启动失败: ${G}`),Z)await C0(this.chatId,`❌ ${X} 启动失败: ${G}`);return await this.proc?.kill(1000).catch(()=>{}),this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),!1}if(Y.state==="timeout")g(`session "${this.sessionName}": ${this.selectedProvider} init wait timeout (5s)`),Q?.(this.withModel(this.withWorktreeInstructionNotice(`⏳ ${X} 已启动,init 确认超时`)));if(Z){let G=this.modelLine();await C0(this.chatId,[this.withWorktreeInstructionNotice(`✅ Lodestar session "${this.sessionName}" 已就绪,发消息开始对话。`),G].filter(Boolean).join(`
329
- `))}return this.status="idle",this.startedAt=Date.now(),this.opts.onLifecycleChange?.(),Q?.(this.withModel(this.withWorktreeInstructionNotice(`✅ ${this.backendLabel()} 已就绪`))),!0}async waitForProcInit(x,Z){return await new Promise((Q)=>{let J=!1,X=(H,K)=>{if(J)return;J=!0,clearTimeout(z),x.off("init",Y),x.off("error",G),x.off("exit",W),Q({state:H,error:K})},z=setTimeout(()=>X("timeout"),Z),Y=()=>X("init"),G=(H)=>X("error",H),W=(H)=>X("exit",H);x.once("init",Y),x.once("error",G),x.once("exit",W)})}async waitForProcEarlyFailure(x,Z){return await new Promise((Q)=>{let J=!1,X=(H,K)=>{if(J)return;J=!0,clearTimeout(z),x.off("init",Y),x.off("error",G),x.off("exit",W),Q({state:H,error:K})},z=setTimeout(()=>X("ready"),Z),Y=()=>X("init"),G=(H)=>X("error",H),W=(H)=>X("exit",H);x.once("init",Y),x.once("error",G),x.once("exit",W)})}async waitForProcResumeInit(x,Z){return await new Promise((Q)=>{let J=!1,X=(K,N)=>{if(J)return;J=!0,clearTimeout(z),clearTimeout(Y),x.off("init",G),x.off("error",W),x.off("exit",H),Q({state:K,error:N})},z=setTimeout(()=>{if(!J)Z()},Sy),Y=setTimeout(()=>{X("timeout",Error(`resume init timed out after ${Ry/1000}s`))},Ry),G=()=>X("init"),W=(K)=>X("error",K),H=(K)=>X("exit",K);x.once("init",G),x.once("error",W),x.once("exit",H)})}releaseAllReactions(){for(let[x,Z]of[...this.pendingReactionIds.entries(),...this.currentBatchReactionIds.entries()])if(Z)A8(x,Z);this.pendingReactionIds=new Map,this.currentBatchReactionIds=new Map}clearStaleIdleQueueState(x){if(this.initCount<1||this.currentTurn||this.openingTurn||this.pendingUserMessageCount===0)return;g(`session "${this.sessionName}": clear stale pending queue before ${x} pendingCount=${this.pendingUserMessageCount} reactions=${this.pendingReactionIds.size}`),this.pendingUserMessageCount=0;for(let[Z,Q]of this.pendingReactionIds)if(Q)A8(Z,Q);this.pendingReactionIds=new Map}async stop(x="已终止",Z={}){let Q=Z.announce??!0,J=Z.onStatus,X=await this.stopAgyTask(`\uD83D\uDED1 ${x}`);if(!this.proc){if(this.status="stopped",this.opts.onLifecycleChange?.(),J?.(X?`✅ ${x}`:"⚪ session 当前未运行"),Q&&!X)await C0(this.chatId,`⚪ session "${this.sessionName}" 当前未运行`);return}J?.(`\uD83D\uDED1 停止 ${this.backendLabel(this.proc.provider)}`),g(`session "${this.sessionName}": stop (${x})`);let z=this.proc;if(this.proc=null,this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("stop"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await z.kill(),J?.(`✅ ${x}`),Q)await C0(this.chatId,`\uD83D\uDD34 ${x} (session: ${this.sessionName})`)}async restart(x=!1,Z={}){let Q=Z.announce??!0,J=Z.onStatus,X=this.lastSessionId,z=X?X.slice(0,8):"",Y=null;if(!J&&Q&&x&&X){let H=this.proc?this.withModel(`\uD83D\uDD01 重启 ${this.backendLabel(this.proc.provider)}`):this.withModel(`\uD83D\uDD01 恢复上一会话 thread=${z}…`);if(Y=await this.openStatusCard("restart",H),Y)J=(K)=>this.setStatusCard(Y,K)}let G=Q&&!Y,W=async(H)=>{if(Y)await this.closeStatusCard(Y,H)};if(this.proc)J?.(`\uD83D\uDED1 停止当前 ${this.backendLabel(this.proc.provider)}`),await this.proc.kill(),this.proc=null;if(this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("restart"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,x&&X){this.status="starting",this.usageTotalsSeedUnknown=!0,J?.(this.withModel(`\uD83D\uDD01 恢复上一会话 thread=${z}…`));let H;try{H=this.spawnAgent(X)}catch(O){let v=`❌ ${this.backendLabel()} 恢复失败: ${Hx(O)}`;if(g(`session "${this.sessionName}": ${this.selectedProvider} resume failed before spawn: ${Hx(O)}`),J?.(v),G)await C0(this.chatId,v);return this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),await W(v),!1}this.proc=H,this.wireProc(this.proc);let K=this.backendLabel(),N=this.selectedProvider==="claude"?this.waitForProcEarlyFailure(this.proc,Cy):this.waitForProcResumeInit(this.proc,()=>{g(`session "${this.sessionName}": ${this.selectedProvider} resume init still pending after ${Sy/1000}s`),J?.(this.withModel(`⏳ 仍在等待 ${K} init 确认 thread=${z}…`))});J?.(this.selectedProvider==="claude"?`⏳ 检查 ${K} 恢复启动`:`⏳ 等待 ${K} init 确认`),this.proc.sendInitialize();let L=await N;if(L.state==="error"||L.state==="exit"||L.state==="timeout"){let O=L.error?Hx(L.error):L.state;g(`session "${this.sessionName}": ${this.selectedProvider} resume failed: ${O}`);let v=L.state==="timeout"?`❌ ${K} 恢复超时`:`❌ ${K} 恢复失败: ${O}`;if(J?.(v),G)await C0(this.chatId,v);return await this.proc?.kill(1000).catch(()=>{}),this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),await W(v),!1}let D=this.withModel(this.withWorktreeInstructionNotice(this.selectedProvider==="claude"&&L.state==="ready"?`✅ 已准备恢复上一会话 thread=${z}…`:`✅ 已恢复上一会话 thread=${z}…`));if(J?.(D),G)await C0(this.chatId,D);return this.status="idle",this.startedAt=Date.now(),this.opts.onLifecycleChange?.(),await W(D),!0}else{if(x){if(J?.("⚠️ 没有可恢复的上一会话,将以新会话启动"),G)await C0(this.chatId,"⚠️ 没有可恢复的上一会话,将以新会话启动")}return this.resetFreshConversationState(),await this.start({...Z,freshConversationStateAlreadyReset:!0})}}worktreeProjectName(){return k3(this)}worktreeProjectDir(){return R5(this)}spawnDeveloperInstructions(){return Ny(this)}worktreeInstructionLoadedNotice(){return RN(this)}withWorktreeInstructionNotice(x){return Ly(this,x)}worktreeExtraInstruction(){return Cz(this)}runWorktreeCommand(x,Z){return Dy(this,x,Z)}showWorktrees(){return CN(this)}showTasklistPanel(){return pf(this)}onTasklistEnable(){return sf(this)}onTasklistDeletePrompt(x){return of(this,x)}onTasklistDeleteConfirm(x){return rf(this,x)}runCompactCommand(){return yf(this)}showModelPanel(){return nf(this)}onModelSelect(x,Z="",Q="",J=null){return lf(this,x,Z,Q,J)}onModelEffortSelect(x,Z,Q="",J="",X=""){return ON(this,x,Z,Q,J,X)}onWorktreeDisband(x){return By(this,x)}runCommand(x,Z=""){return wf(this,x,Z)}async buildConsoleOpts(x,Z){let Q=await eu();return{sessionName:this.sessionName,status:this.status,provider:this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),worktreeInstructionNotice:this.worktreeInstructionLoadedNotice(),peers:[...C5.all].filter((J)=>J.isRunning()).map((J)=>({...J.peerSnapshot(),isCurrent:J===this})),usage:x,glmUsage:Z,sysinfo:Q}}async buildConsoleCard(x){return gU(await this.buildConsoleOpts(x))}async patchConsoleUsage(x){let Z=await this.buildConsoleOpts(void 0);if(this.currentProvider()==="claude")Z.glmUsage=await KN();else Z.usage=await Ew();await Zx(x,$0.consoleUsage,FY(Z))}patchConsoleUsageLater(x){(async()=>{try{await this.patchConsoleUsage(x)}catch(Z){g(`session "${this.sessionName}": consoleUsage patch failed: ${Z}`)}finally{try{await d1(x)}catch(Z){g(`session "${this.sessionName}": console card dispose failed: ${Z}`)}}})()}async replaceStatusCardWithConsole(x,Z){x.timer.stop();let Q=x.timer.elapsedSec(),J=await this.buildConsoleOpts(void 0);await E8(x.cardId),await Zx(x.cardId,$0.footer,Jz(J,$0.footer)),await Ex(x.cardId,Qz(J),{type:"insert_after",targetElementId:$0.footer}),await Ex(x.cardId,Xz(J.sysinfo),{type:"insert_after",targetElementId:$0.consoleProjects}),await Ex(x.cardId,FY(J),{type:"insert_after",targetElementId:$0.consoleHost}),v1(x.cardId),await E1(x.cardId,x4({durationSec:Q,suffix:Z})),this.patchConsoleUsageLater(x.cardId)}async showConsole(){let x=await this.buildConsoleCard(void 0),Z=await mx(this.chatId,x);if(!Z)return;(async()=>{let Q="";try{Q=await a8(Z),await this.patchConsoleUsage(Q)}catch(J){g(`session "${this.sessionName}": consoleUsage patch failed: ${J}`)}finally{if(Q)try{await d1(Q)}catch(J){g(`session "${this.sessionName}": console card dispose failed: ${J}`)}}})()}interrupt(){if(!this.proc)return;g(`session "${this.sessionName}": interrupt`),this.proc.sendInterrupt()}async startColdUserTurn(x,Z,Q){this.openingTurn=!0,this.resetFreshConversationState(),this.pendingTurnInputs.push(x);try{await this.openTurnCard(Q,"user_message",{initialFooter:"Waiting...(0s)",startThinking:!1,directStart:!0});let J=this.currentTurn;if(!J)return;let X=this.startFooterTimer(J.cardId,`\uD83D\uDE80 启动 ${this.backendLabel()}`,(G)=>this.withModel(G)),z=`\uD83D\uDE80 启动 ${this.backendLabel()}`,Y=await this.start({announce:!1,freshConversationStateAlreadyReset:!0,onStatus:(G)=>{z=G,X.setStatus(G)}});if(X.stop(),await E8(J.cardId),!Y){this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),await this.closeTurnCard(z.startsWith("❌")?z:"❌ 启动失败",{forcePush:!0});return}this.startThinkingFooter(J),this.proc.sendUserText(Z,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}}async onUserMessage(x,Z=[],Q="",J=""){if(this.clearStaleIdleQueueState("user_message"),this.startingAgy||this.runningAgy){await C0(this.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再继续。");return}if(this.proc?.isAlive()&&this.proc.provider!==this.selectedProvider&&!this.currentTurn&&!this.openingTurn&&this.pendingUserMessageCount===0&&this.pendingMidTurnMsgs.length===0)await this.stopIdleMismatchedProcess();let X=this.currentTurn!==null||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0;this.lastUserOpenId=Q;let Y=(Z.length?Z.map((W)=>`[file: ${W}]`).join(" ")+`
330
- `:"")+x,G=(W)=>{this.pendingReactionIds.set(W,""),(async()=>{let H=await A1(W,"OneSecond");if(!H)return;if(this.pendingReactionIds.has(W))this.pendingReactionIds.set(W,H);else if(this.currentBatchReactionIds.has(W))this.currentBatchReactionIds.set(W,H);else A8(W,H)})()};if(!this.isRunning()){if(this.openingTurn||this.currentTurn){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}await this.startColdUserTurn(x,Y,Q);return}if(this.currentTurn!==null){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(!this.openingTurn&&this.initCount>=1){this.openingTurn=!0;try{if(this.pendingTurnInputs.push(x),await this.openTurnCard(Q,"user_message"),!this.currentTurn)return;this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}return}if(this.openingTurn||this.pendingUserMessageCount>0){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(this.pendingTurnInputs.push(x),this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,X&&J)G(J)}hasPendingAsk(){return Bf(this)}hasPendingHostAsk(){return qf(this)}onMultiMessageInbound(x,Z,Q,J){return sw(this,x,Z,Q,J)}clearMultiMsgBuffer(x){VN(this,x)}onAskMessageAnswer(x,Z,Q){return vf(this,x,Z,Q)}onHostAskMessageAnswer(x,Z,Q){return Af(this,x,Z,Q)}onAskAnswer(x,Z,Q,J){return Mf(this,x,Z,Q,J)}onHostAskAnswer(x,Z,Q,J){return Ef(this,x,Z,Q,J)}onAskCustomAnswer(x,Z,Q,J){return UN(this,x,Z,Q,J)}onHostAskCustomAnswer(x,Z,Q,J){return DN(this,x,Z,Q,J)}onPermissionDecision(x,Z,Q){return gf(this,x,Z,Q)}async startHostAskContinuation(x){if(!this.isRunning())throw Error(`${this.backendLabel()} is not running`);if(this.proc?.provider!=="codex")throw Error("askusr host continuation is only supported by Codex");if(this.currentTurn||this.openingTurn)throw Error(`${this.backendLabel()} turn still active`);this.openingTurn=!0;try{if(await this.openTurnCard("","user_message"),!this.currentTurn)throw Error("failed to open continuation turn card");this.proc.sendUserText(x,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}}persistResumableSessionId(){let x=this.proc,Z=x?.sessionId;if(!x||!Z)return;if(rP(this.sessionName,Z,x.provider),x.provider!==this.selectedProvider)return;if(Z===this.lastSessionId)return;this.lastSessionId=Z}bgStore(){return{active:this.backgroundTasks,pending:this.pendingBgTasks}}applyBgStore(x){this.backgroundTasks=x.active,this.pendingBgTasks=x.pending}bgTaskOwns(x){return this.backgroundTasks.some((Z)=>Z.toolUseId===x)||this.pendingBgTasks.some((Z)=>Z.toolUseId===x)}onBackgroundTaskChanged(){if(!vY(this.backgroundTasks)){if(this.backgroundCard)this.settleBackgroundCard();return}if(!this.backgroundCard&&!this.openingBackground){this.openingBackground=!0,this.openBackgroundCard().finally(()=>{this.openingBackground=!1});return}this.scheduleBackgroundRefresh()}async openBackgroundCard(){if(this.backgroundCard)return;let x=ZN(this.backgroundTasks),Z=await mx(this.chatId,x);if(!Z){g(`session "${this.sessionName}": background card send failed`);return}let Q;try{Q=await a8(Z)}catch(J){g(`session "${this.sessionName}": background card id_convert failed: ${J}`);return}E4(Q,this.backgroundTasks.length),this.backgroundCard={messageId:Z,cardId:Q},this.backgroundDetailAdded=new Set(this.backgroundTasks.map((J)=>J.id)),g(`session "${this.sessionName}": background card opened cardId=${Q.slice(0,12)} tasks=${this.backgroundTasks.length}`)}scheduleBackgroundRefresh(){if(!this.backgroundCard)return;if(this.backgroundRefreshTimer)return;this.backgroundRefreshTimer=setTimeout(()=>{this.backgroundRefreshTimer=null,this.refreshBackgroundCardFull()},1500)}refreshBackgroundCardFull(){let x=this.backgroundCard;if(!x)return;let Z=Date.now();for(let Q of this.backgroundTasks)if(!this.backgroundDetailAdded.has(Q.id))this.backgroundDetailAdded.add(Q.id),Ex(x.cardId,D5(Q,Z));else Zx(x.cardId,BY.panel(Q.id),D5(Q,Z))}async settleBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundCard=null,this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;await E8(x.cardId),await bX(x.messageId,Uz(this.backgroundTasks)),v1(x.cardId),await E1(x.cardId,x4({suffix:"\uD83E\uDDED 后台任务已结束"})),await d1(x.cardId),this.backgroundTasks=[],this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card settled cardId=${x.cardId.slice(0,12)}`)}async migrateBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;let Z=this.backgroundTasks.filter(b3).length;if(await E8(x.cardId),Z>0)await bX(x.messageId,Uz(this.backgroundTasks));else await bX(x.messageId,QN());v1(x.cardId),await E1(x.cardId,x4({suffix:"\uD83E\uDDED 已迁移至新卡"})),await d1(x.cardId),this.backgroundCard=null,this.backgroundTasks=this.backgroundTasks.filter((Q)=>!b3(Q)),this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card migrated cardId=${x.cardId.slice(0,12)} terminal=${Z} active=${this.backgroundTasks.length}`)}wireProc(x){x.on("error",(Z)=>{g(`session "${this.sessionName}": ${x.provider} process error: ${Z}`)}),x.on("init",()=>{if(this.persistResumableSessionId(),this.initCount++,g(`session "${this.sessionName}": SDK init#${this.initCount} pendingCount=${this.pendingUserMessageCount} midBuffer=${this.pendingMidTurnMsgs.length} currentTurn=${this.currentTurn?"yes":"no"} openingTurn=${this.openingTurn}`),this.currentTurn||this.openingTurn)return;let Z=this.pendingUserMessageCount>0;if(!Z)return;let Q=this.lastUserOpenId;if(Z)this.pendingUserMessageCount=0,this.currentBatchReactionIds=this.pendingReactionIds,this.pendingReactionIds=new Map;this.openingTurn=!0,(async()=>{try{if(await this.openTurnCard(Q,"user_message"),!this.currentTurn)g(`session "${this.sessionName}": init-path openTurnCard failed — sendInterrupt + release reactions`),this.proc?.sendInterrupt(),this.releaseAllReactions();else this.status="working"}finally{this.openingTurn=!1}})()}),x.on("turn_started",()=>{this.persistResumableSessionId();let Z=this.proc?.lastTotalUsage;if(this.usageTotalsSeedUnknown&&!Z){this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;return}this.currentTurnUsageBaseline=Z?{...Z}:null,this.currentTurnUsageBaselineKnown=!0}),x.on("token_usage",({totalUsage:Z})=>{if(this.persistResumableSessionId(),Z)this.usageTotalsSeedUnknown=!1}),x.on("turn_plan_updated",(Z)=>{this.handleTurnPlanUpdated(Z)}),x.on("plan_delta",(Z)=>{this.handlePlanDelta(Z)}),x.on("context_compacted",(Z)=>{this.handleContextCompacted(Z)}),x.on("rate_limits_updated",(Z)=>{bw(Z)}),x.on("thread_goal_updated",(Z)=>{this.handleThreadGoalUpdated(Z)}),x.on("thread_goal_cleared",()=>{this.handleThreadGoalCleared()}),x.on("assistant_text",({text:Z})=>{this.appendAssistant(Z)}),x.on("assistant_block_stop",()=>{this.finalizeCurrentAssistantSegment()}),x.on("tool_use",({id:Z,name:Q,input:J,parentToolUseId:X})=>{if(Nf(this,Z,Q,J),!X&&(Q==="Task"||Q==="Agent"))this.lastMainTaskToolUseId=Z;if(X&&this.bgTaskOwns(X))this.applyBgStore(xN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("tool_result",({tool_use_id:Z,content:Q,is_error:J,parentToolUseId:X})=>{if(Lf(this,Z,Q,J),X&&this.bgTaskOwns(X))this.applyBgStore(YN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("can_use_tool",(Z)=>{Pf(this,Z)}),x.on("hook_callback",(Z)=>{this.proc?.sendHookResponse(Z.request_id,{})}),x.on("result",()=>{if(this.persistResumableSessionId(),this.accumulateResultStats(),this.userInterrupted){this.userInterrupted=!1;let W=this.proc?.lastResult.subtype??"unknown",H=this.proc?.lastResult.is_error===!0;g(`session "${this.sessionName}": SDK result after user stop subtype=${W} isError=${H} — ignored`),this.status="idle";return}let Z=this.pendingMidTurnMsgs.length>0,Q=this.proc?.lastResult.is_error===!0,J=this.proc?.lastResult.subtype??"success",X=this.pendingHostAsks.size>0,z,Y=!1,G=this.proc?this.backendLabel(this.proc.provider):this.backendLabel();if(Z&&!X)z=Q?`⚠️ ${G} ${J},用户已介入`:"\uD83D\uDCE8 转交新卡";else if(Q)z=`⚠️ ${G} ${J}`,Y=!0;if(g(`session "${this.sessionName}": SDK result subtype=${J} isError=${Q} midBuffer=${this.pendingMidTurnMsgs.length} forcePush=${Y}`),this.closeTurnCard(z,{forcePush:Y,hasFreshResult:!0}),this.status="idle",Tf(this),Z&&!X)this.drainMidTurnAndOpen()}),x.on("bg_task_started",(Z)=>{let Q=Z.tool_use_id??this.lastMainTaskToolUseId??void 0;g(`session "${this.sessionName}": bg_task_started task=${Z.task_id} type=${Z.task_type??"-"} subagent=${Z.subagent_type??"-"} toolUseId=${Q?.slice(0,8)??"-"} desc=${(Z.description??"").slice(0,40)}`),this.applyBgStore(rU(this.bgStore(),{...Z,tool_use_id:Q})),this.onBackgroundTaskChanged()}),x.on("bg_task_progress",(Z)=>{this.applyBgStore(tU(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_updated",(Z)=>{this.applyBgStore(eU(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_settled",(Z)=>{g(`session "${this.sessionName}": bg_task_settled task=${Z.task_id} status=${Z.status}`),this.applyBgStore(aU(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("exit",({code:Z,signal:Q,expected:J})=>{if(g(`session "${this.sessionName}": ${x.provider} exited code=${Z} signal=${Q} expected=${J}`),this.proc!==x){g(`session "${this.sessionName}": ignore stale ${x.provider} exit; current=${this.proc?.provider??"none"}`);return}if(this.proc=null,this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("process exit"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.userInterrupted=!1,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),!J&&Z!==0&&Q!=="SIGTERM")C0(this.chatId,`⚠️ ${this.backendLabel(x.provider)} 异常退出 (code=${Z}, signal=${Q})。回复任意消息将重新启动。`)})}accumulateResultStats(){let x=this.proc?.lastResult;if(!x)return;let Z=this.currentTurnUsageBaselineKnown?nV(this.proc?.lastTotalUsage,this.currentTurnUsageBaseline):this.proc?.provider==="claude"&&x.usage?{...x.usage}:null;this.lastTurnUsage=Z,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;let Q=lV(Z),J=this.proc?.provider==="claude"?0:x.cost_delta_usd??0,X=x.duration_ms??0;if(Q!=null)this.cumStats.tokens+=Q;this.cumStats.costUsd+=J,this.cumStats.turns+=x.num_turns??1,this.lastTurnDelta={tokens:Q,costUsd:J,durationMs:X}}currentContextTokens(){if(this.proc?.provider==="claude")return this.proc?.lastContextTokens??null;let x=this.proc?.lastUsage;return gX(x)}contextLimitForDisplay(){return PX(this.proc?.lastContextWindow)}async drainMidTurnAndOpen(){if(this.pendingMidTurnMsgs.length===0)return;let x=this.pendingMidTurnMsgs;this.pendingMidTurnMsgs=[],this.openingTurn=!0;try{for(let X of x)if(this.pendingTurnInputs.push(X.text),X.msgId){let z=this.pendingReactionIds.get(X.msgId)??"";this.currentBatchReactionIds.set(X.msgId,z),this.pendingReactionIds.delete(X.msgId)}let Z=x.map((X)=>X.wireText).join(`
328
+ `);return IY(x,!0,W,"success")}catch(z){let Y=`❌ 解散 ${Q} 失败: ${Hx(z)}`;return IY(x,!1,Y,"error")}}var vy=A0(()=>{Zy();Q4();ox();Bx();Uy()});import{existsSync as SU0}from"node:fs";import{isAbsolute as RU0,join as CU0}from"node:path";function jU0(x){return x.itemId||x.turnId||"__latest__"}function IU0(x){let Z=null;for(let Q of x.contextCompactionPending.keys())Z=Q;return Z}function TU0(x,Z){if(!x)return Z;return{...x,...Z,phase:"end"}}function jy(x,Z){let Q=Math.max(0,Math.floor((Date.now()-Z)/1000));return`${x} (${Q}s)`}var My=1000,cU0="Thinking...",qU0="Writing...",bU0="Working...",Sy=1e4,Ry=120000,AU0=50,jN=5,Cy=250,C5;var Iy=A0(()=>{R7();P9();TP();R3();Q4();ox();Bx();AU();kw();fw();nw();ow();Uf();Of();LN();kf();uf();ff();hf();df();tf();vy();C5=class C5{sessionName;chatId;opts;static all=new Set;proc=null;currentTurn=null;backgroundTasks=[];pendingBgTasks=[];backgroundCard=null;backgroundRefreshTimer=null;openingBackground=!1;backgroundDetailAdded=new Set;lastMainTaskToolUseId=null;pendingRebuildBackgroundCard=!1;bgResumePending=!1;orphanAssistantSegments=[];orphanAssistantCurrent="";bgResumeCardless=!1;sawResultWhileOpening=!1;pendingPermissions=new Map;pendingAsks=new Map;pendingHostAsks=new Map;currentGoal=null;status="stopped";pendingUserMessageCount=0;pendingMidTurnMsgs=[];pendingTurnInputs=[];multiMsgBuffer=null;multiMsgReactions=new Map;lastUserOpenId="";pendingReactionIds=new Map;currentBatchReactionIds=new Map;initCount=0;openingTurn=!1;turnCounter=0;userInterrupted=!1;lastSessionId=null;selectedProvider="claude";selectedModel=null;selectedEffort=null;modelPanels=new Map;startedAt=0;cumStats={tokens:0,costUsd:0,turns:0};lastTurnDelta=null;currentTurnUsageBaseline=null;currentTurnUsageBaselineKnown=!1;lastTurnUsage=null;usageTotalsSeedUnknown=!1;manualContextCompactionPending=!1;runningAgy=null;startingAgy=!1;agyForwardPrompts=new Map;taskBoard=[];constructor(x,Z,Q={}){this.sessionName=x;this.chatId=Z;this.opts=Q;C5.all.add(this);let J=aP(x);if(this.selectedProvider=J?.provider??"claude",this.selectedModel=J?.model??null,this.selectedEffort=J?.effort??null,J){if(this.selectedProvider==="claude"&&this.selectedModel!=="claude:glm")this.selectedModel="claude:glm",this.selectedEffort="max";else if(this.selectedProvider==="codex"&&this.selectedModel!=="gpt-5.5")this.selectedModel="gpt-5.5",this.selectedEffort="xhigh"}if(this.selectedModel)g(`session "${x}": restored selected provider=${this.selectedProvider} model=${this.selectedModel} effort=${this.selectedEffort??"unset"}`);if(this.lastSessionId=KU(x,this.selectedProvider),this.lastSessionId)g(`session "${x}": restored ${this.selectedProvider} lastSessionId=${this.lastSessionId.slice(0,8)}…`)}peerSnapshot(){return{name:this.sessionName,status:this.status,uptimeMs:this.startedAt?Date.now()-this.startedAt:void 0}}get workDir(){let x=zU(this.sessionName)?.cwd;return x&&x.trim()?x:CU0(M3,this.sessionName)}isRunning(){return!!this.proc&&this.proc.isAlive()}currentProvider(){return this.selectedProvider}hasRunningPeerSession(x){return[...C5.all].some((Z)=>Z.sessionName===x&&Z.isRunning())}modelForSpawn(){return this.selectedModel??void 0}effortForSpawn(){return this.selectedProvider==="codex"&&S8(this.selectedEffort)?this.selectedEffort:iV}claudeEffortForSpawn(){return this.selectedProvider==="claude"&&Z6(this.selectedEffort)?this.selectedEffort:Y3}currentModelLabel(){return this.selectedModel??this.proc?.lastModel??null}currentEffortLabel(){return this.selectedEffort??this.proc?.lastEffort??(this.selectedProvider==="claude"?Y3:iV)}modelEffortLabel(){let x=this.currentModelLabel(),Z=this.currentEffortLabel();return x?`${x}/${Z}`:Z}withModel(x){let Z=this.modelEffortLabel();return x.includes(Z)?x:`${x} · ${Z}`}replaceFooterContent(x,Z){return Zx(x,$0.footer,{tag:"markdown",element_id:$0.footer,content:Z.trim()||" "})}modelLine(){let x=this.currentModelLabel(),Z=this.currentEffortLabel(),Q=this.selectedProvider==="claude"?x?.replace(/^claude:/i,""):x,J=Q?`${Q}/${Z}`:Z;return this.selectedProvider==="claude"?`${Q6(this.selectedProvider)} · ${J}`:J}backendLabel(x=this.selectedProvider){return Q6(x)}spawnAgent(x){if(this.selectedProvider==="claude")return CP(),new ZU({workDir:this.workDir,model:this.modelForSpawn(),effort:this.claudeEffortForSpawn(),resumeSessionId:x,appendSystemPrompt:this.spawnDeveloperInstructions(),profile:zU(this.sessionName)});return new pV({workDir:this.workDir,model:this.modelForSpawn(),effort:this.effortForSpawn(),resumeSessionId:x,appendSystemPrompt:this.spawnDeveloperInstructions()})}async applyModelSelection(x,Z,Q){this.selectedProvider=x,this.selectedModel=Z,this.selectedEffort=Q,this.lastSessionId=KU(this.sessionName,x),eP(this.sessionName,x,Z,Q),await this.stopIdleMismatchedProcess()}async stopIdleMismatchedProcess(){if(!this.proc?.isAlive())return;if(this.proc.provider===this.selectedProvider)return;if(this.currentTurn||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0)return;let x=this.proc;g(`session "${this.sessionName}": stop idle ${x.provider} process after switching to ${this.selectedProvider}`),this.proc=null,this.initCount=0,this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await x.kill(1000)}async stopIdleCurrentProcess(x){if(!this.proc?.isAlive())return!1;if(this.currentTurn||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0)return!1;let Z=this.proc;return g(`session "${this.sessionName}": stop idle ${Z.provider} process: ${x}`),this.proc=null,this.initCount=0,this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await Z.kill(1000),!0}startFooterTimer(x,Z,Q=(J)=>J){let J=Date.now(),X=Z,z=!1,Y=()=>{if(z)return;this.replaceFooterContent(x,Q(jy(X,J)))},G=setInterval(Y,My);return Y(),{setStatus(W){X=W,Y()},stop(){if(z)return;z=!0,clearInterval(G)},elapsedSec(){return((Date.now()-J)/1000).toFixed(1)}}}async openStatusCard(x,Z,Q="blue"){let J=Date.now(),X=kU({sessionName:this.sessionName,title:x,status:jy(Z,J),template:Q}),z=await mx(this.chatId,X);if(!z)return g(`session "${this.sessionName}": status card send failed title=${x}`),await wx(this.chatId,`❌ 创建状态卡片失败: ${x}`),null;let Y;try{Y=await a8(z)}catch(G){return g(`session "${this.sessionName}": status card id_convert failed title=${x}: ${G}`),await wx(this.chatId,`❌ 状态卡片初始化失败: ${x}`),null}return E4(Y,1),{cardId:Y,title:x,timer:this.startFooterTimer(Y,Z,(G)=>GY(x,G))}}setStatusCard(x,Z){x?.timer.setStatus(Z)}async closeStatusCard(x,Z){if(!x)return;x.timer.stop();let Q=x.timer.elapsedSec(),J=GY(x.title,`${Z} (${Q}s)`);await E8(x.cardId),await this.replaceFooterContent(x.cardId,J),v1(x.cardId),await E1(x.cardId,x4({durationSec:Q,suffix:Z})),await d1(x.cardId)}beginAgyForwardToCodex(x,Z=""){return Hf(this,x,Z)}runAgyCommand(x){return Gf(this,x)}stopAgyTask(x="\uD83D\uDED1 agy 已打断"){return $f(this,x)}resetFreshConversationState(){this.turnCounter=0,this.currentGoal=null,this.cumStats={tokens:0,costUsd:0,turns:0},this.lastTurnDelta=null,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.lastTurnUsage=null,this.usageTotalsSeedUnknown=!1}async start(x={}){let Z=x.announce??!0,Q=x.onStatus;if(this.isRunning()){if(this.proc?.provider===this.selectedProvider)return Q?.(this.withModel(`✅ ${this.backendLabel()} 已运行`)),!0;if(await this.stopIdleMismatchedProcess(),this.proc?.isAlive())return Q?.(`⚠️ 当前 ${this.backendLabel(this.proc.provider)} turn 尚未结束,模型切换将在后续新 turn 生效`),!0}if(this.selectedProvider==="codex")Q?.("\uD83D\uDD0E 检查 Codex 登录");else Q?.("\uD83D\uDD0E 检查 Claude Code");if(this.selectedProvider==="codex"&&!Gu()){if(this.status="stopped",this.opts.onLifecycleChange?.(),Q?.("❌ Codex 未登录 ChatGPT 账号"),Z)await C0(this.chatId,"❌ Codex 未登录 ChatGPT 账号。\n请在服务器上运行 `codex login` 后再试。");return!1}if(!SU0(this.workDir)){if(Q?.(`\uD83C\uDD95 创建项目目录 ~/${this.sessionName}`),Z)await C0(this.chatId,`\uD83C\uDD95 目录 ~/${this.sessionName} 不存在,正在创建…`);try{Hu(this.workDir)}catch(G){if(this.status="stopped",this.opts.onLifecycleChange?.(),Q?.(`❌ 创建项目失败: ${G}`),Z)await C0(this.chatId,`❌ 创建项目失败: ${G}`);return!1}}if(!x.freshConversationStateAlreadyReset)this.resetFreshConversationState();this.status="starting",Q?.(this.withModel(`\uD83D\uDE80 启动 ${this.backendLabel()}`));let J;try{J=this.spawnAgent()}catch(G){let W=`${this.backendLabel()} 启动失败: ${Hx(G)}`;if(g(`session "${this.sessionName}": ${W}`),Q?.(`❌ ${W}`),Z)await C0(this.chatId,`❌ ${W}`);return this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),!1}this.proc=J,this.wireProc(this.proc);let X=this.backendLabel(),z=this.selectedProvider==="claude"?this.waitForProcEarlyFailure(this.proc,Cy):this.waitForProcInit(this.proc,5000);Q?.(this.selectedProvider==="claude"?`⏳ 检查 ${X} 启动`:`⏳ 等待 ${X} init`),this.proc.sendInitialize();let Y=await z;if(Y.state==="error"||Y.state==="exit"){let G=Y.error?Hx(Y.error):Y.state;if(g(`session "${this.sessionName}": ${this.selectedProvider} init failed: ${G}`),Q?.(`❌ ${X} 启动失败: ${G}`),Z)await C0(this.chatId,`❌ ${X} 启动失败: ${G}`);return await this.proc?.kill(1000).catch(()=>{}),this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),!1}if(Y.state==="timeout")g(`session "${this.sessionName}": ${this.selectedProvider} init wait timeout (5s)`),Q?.(this.withModel(this.withWorktreeInstructionNotice(`⏳ ${X} 已启动,init 确认超时`)));if(Z){let G=this.modelLine();await C0(this.chatId,[this.withWorktreeInstructionNotice(`✅ Lodestar session "${this.sessionName}" 已就绪,发消息开始对话。`),G].filter(Boolean).join(`
329
+ `))}return this.status="idle",this.startedAt=Date.now(),this.opts.onLifecycleChange?.(),Q?.(this.withModel(this.withWorktreeInstructionNotice(`✅ ${this.backendLabel()} 已就绪`))),!0}async waitForProcInit(x,Z){return await new Promise((Q)=>{let J=!1,X=(H,K)=>{if(J)return;J=!0,clearTimeout(z),x.off("init",Y),x.off("error",G),x.off("exit",W),Q({state:H,error:K})},z=setTimeout(()=>X("timeout"),Z),Y=()=>X("init"),G=(H)=>X("error",H),W=(H)=>X("exit",H);x.once("init",Y),x.once("error",G),x.once("exit",W)})}async waitForProcEarlyFailure(x,Z){return await new Promise((Q)=>{let J=!1,X=(H,K)=>{if(J)return;J=!0,clearTimeout(z),x.off("init",Y),x.off("error",G),x.off("exit",W),Q({state:H,error:K})},z=setTimeout(()=>X("ready"),Z),Y=()=>X("init"),G=(H)=>X("error",H),W=(H)=>X("exit",H);x.once("init",Y),x.once("error",G),x.once("exit",W)})}async waitForProcResumeInit(x,Z){return await new Promise((Q)=>{let J=!1,X=(K,N)=>{if(J)return;J=!0,clearTimeout(z),clearTimeout(Y),x.off("init",G),x.off("error",W),x.off("exit",H),Q({state:K,error:N})},z=setTimeout(()=>{if(!J)Z()},Sy),Y=setTimeout(()=>{X("timeout",Error(`resume init timed out after ${Ry/1000}s`))},Ry),G=()=>X("init"),W=(K)=>X("error",K),H=(K)=>X("exit",K);x.once("init",G),x.once("error",W),x.once("exit",H)})}releaseAllReactions(){for(let[x,Z]of[...this.pendingReactionIds.entries(),...this.currentBatchReactionIds.entries()])if(Z)A8(x,Z);this.pendingReactionIds=new Map,this.currentBatchReactionIds=new Map}clearStaleIdleQueueState(x){if(this.initCount<1||this.currentTurn||this.openingTurn||this.pendingUserMessageCount===0)return;g(`session "${this.sessionName}": clear stale pending queue before ${x} pendingCount=${this.pendingUserMessageCount} reactions=${this.pendingReactionIds.size}`),this.pendingUserMessageCount=0;for(let[Z,Q]of this.pendingReactionIds)if(Q)A8(Z,Q);this.pendingReactionIds=new Map}async stop(x="已终止",Z={}){let Q=Z.announce??!0,J=Z.onStatus,X=await this.stopAgyTask(`\uD83D\uDED1 ${x}`);if(!this.proc){if(this.status="stopped",this.opts.onLifecycleChange?.(),J?.(X?`✅ ${x}`:"⚪ session 当前未运行"),Q&&!X)await C0(this.chatId,`⚪ session "${this.sessionName}" 当前未运行`);return}J?.(`\uD83D\uDED1 停止 ${this.backendLabel(this.proc.provider)}`),g(`session "${this.sessionName}": stop (${x})`);let z=this.proc;if(this.proc=null,this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("stop"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await z.kill(),J?.(`✅ ${x}`),Q)await C0(this.chatId,`\uD83D\uDD34 ${x} (session: ${this.sessionName})`)}async restart(x=!1,Z={}){let Q=Z.announce??!0,J=Z.onStatus,X=this.lastSessionId,z=X?X.slice(0,8):"",Y=null;if(!J&&Q&&x&&X){let H=this.proc?this.withModel(`\uD83D\uDD01 重启 ${this.backendLabel(this.proc.provider)}`):this.withModel(`\uD83D\uDD01 恢复上一会话 thread=${z}…`);if(Y=await this.openStatusCard("restart",H),Y)J=(K)=>this.setStatusCard(Y,K)}let G=Q&&!Y,W=async(H)=>{if(Y)await this.closeStatusCard(Y,H)};if(this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.proc){J?.(`\uD83D\uDED1 停止当前 ${this.backendLabel(this.proc.provider)}`);let H=this.proc;this.proc=null,await H.kill()}if(this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("restart"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,x&&X){this.status="starting",this.usageTotalsSeedUnknown=!0,J?.(this.withModel(`\uD83D\uDD01 恢复上一会话 thread=${z}…`));let H;try{H=this.spawnAgent(X)}catch(O){let v=`❌ ${this.backendLabel()} 恢复失败: ${Hx(O)}`;if(g(`session "${this.sessionName}": ${this.selectedProvider} resume failed before spawn: ${Hx(O)}`),J?.(v),G)await C0(this.chatId,v);return this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),await W(v),!1}this.proc=H,this.wireProc(this.proc);let K=this.backendLabel(),N=this.selectedProvider==="claude"?this.waitForProcEarlyFailure(this.proc,Cy):this.waitForProcResumeInit(this.proc,()=>{g(`session "${this.sessionName}": ${this.selectedProvider} resume init still pending after ${Sy/1000}s`),J?.(this.withModel(`⏳ 仍在等待 ${K} init 确认 thread=${z}…`))});J?.(this.selectedProvider==="claude"?`⏳ 检查 ${K} 恢复启动`:`⏳ 等待 ${K} init 确认`),this.proc.sendInitialize();let L=await N;if(L.state==="error"||L.state==="exit"||L.state==="timeout"){let O=L.error?Hx(L.error):L.state;g(`session "${this.sessionName}": ${this.selectedProvider} resume failed: ${O}`);let v=L.state==="timeout"?`❌ ${K} 恢复超时`:`❌ ${K} 恢复失败: ${O}`;if(J?.(v),G)await C0(this.chatId,v);return await this.proc?.kill(1000).catch(()=>{}),this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),await W(v),!1}let D=this.withModel(this.withWorktreeInstructionNotice(this.selectedProvider==="claude"&&L.state==="ready"?`✅ 已准备恢复上一会话 thread=${z}…`:`✅ 已恢复上一会话 thread=${z}…`));if(J?.(D),G)await C0(this.chatId,D);return this.status="idle",this.startedAt=Date.now(),this.opts.onLifecycleChange?.(),await W(D),!0}else{if(x){if(J?.("⚠️ 没有可恢复的上一会话,将以新会话启动"),G)await C0(this.chatId,"⚠️ 没有可恢复的上一会话,将以新会话启动")}return this.resetFreshConversationState(),await this.start({...Z,freshConversationStateAlreadyReset:!0})}}worktreeProjectName(){return k3(this)}worktreeProjectDir(){return R5(this)}spawnDeveloperInstructions(){return Ny(this)}worktreeInstructionLoadedNotice(){return RN(this)}withWorktreeInstructionNotice(x){return Ly(this,x)}worktreeExtraInstruction(){return Cz(this)}runWorktreeCommand(x,Z){return Dy(this,x,Z)}showWorktrees(){return CN(this)}showTasklistPanel(){return pf(this)}onTasklistEnable(){return sf(this)}onTasklistDeletePrompt(x){return of(this,x)}onTasklistDeleteConfirm(x){return rf(this,x)}runCompactCommand(){return yf(this)}showModelPanel(){return nf(this)}onModelSelect(x,Z="",Q="",J=null){return lf(this,x,Z,Q,J)}onModelEffortSelect(x,Z,Q="",J="",X=""){return ON(this,x,Z,Q,J,X)}onWorktreeDisband(x){return By(this,x)}runCommand(x,Z=""){return wf(this,x,Z)}async buildConsoleOpts(x,Z){let Q=await eu();return{sessionName:this.sessionName,status:this.status,provider:this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),worktreeInstructionNotice:this.worktreeInstructionLoadedNotice(),peers:[...C5.all].filter((J)=>J.isRunning()).map((J)=>({...J.peerSnapshot(),isCurrent:J===this})),usage:x,glmUsage:Z,sysinfo:Q}}async buildConsoleCard(x){return gU(await this.buildConsoleOpts(x))}async patchConsoleUsage(x){let Z=await this.buildConsoleOpts(void 0);if(this.currentProvider()==="claude")Z.glmUsage=await KN();else Z.usage=await Ew();await Zx(x,$0.consoleUsage,FY(Z))}patchConsoleUsageLater(x){(async()=>{try{await this.patchConsoleUsage(x)}catch(Z){g(`session "${this.sessionName}": consoleUsage patch failed: ${Z}`)}finally{try{await d1(x)}catch(Z){g(`session "${this.sessionName}": console card dispose failed: ${Z}`)}}})()}async replaceStatusCardWithConsole(x,Z){x.timer.stop();let Q=x.timer.elapsedSec(),J=await this.buildConsoleOpts(void 0);await E8(x.cardId),await Zx(x.cardId,$0.footer,Jz(J,$0.footer)),await Ex(x.cardId,Qz(J),{type:"insert_after",targetElementId:$0.footer}),await Ex(x.cardId,Xz(J.sysinfo),{type:"insert_after",targetElementId:$0.consoleProjects}),await Ex(x.cardId,FY(J),{type:"insert_after",targetElementId:$0.consoleHost}),v1(x.cardId),await E1(x.cardId,x4({durationSec:Q,suffix:Z})),this.patchConsoleUsageLater(x.cardId)}async showConsole(){let x=await this.buildConsoleCard(void 0),Z=await mx(this.chatId,x);if(!Z)return;(async()=>{let Q="";try{Q=await a8(Z),await this.patchConsoleUsage(Q)}catch(J){g(`session "${this.sessionName}": consoleUsage patch failed: ${J}`)}finally{if(Q)try{await d1(Q)}catch(J){g(`session "${this.sessionName}": console card dispose failed: ${J}`)}}})()}interrupt(){if(!this.proc)return;g(`session "${this.sessionName}": interrupt`),this.proc.sendInterrupt()}async startColdUserTurn(x,Z,Q){this.openingTurn=!0,this.resetFreshConversationState(),this.pendingTurnInputs.push(x);try{await this.openTurnCard(Q,"user_message",{initialFooter:"Waiting...(0s)",startThinking:!1,directStart:!0});let J=this.currentTurn;if(!J)return;let X=this.startFooterTimer(J.cardId,`\uD83D\uDE80 启动 ${this.backendLabel()}`,(G)=>this.withModel(G)),z=`\uD83D\uDE80 启动 ${this.backendLabel()}`,Y=await this.start({announce:!1,freshConversationStateAlreadyReset:!0,onStatus:(G)=>{z=G,X.setStatus(G)}});if(X.stop(),await E8(J.cardId),!Y){this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),await this.closeTurnCard(z.startsWith("❌")?z:"❌ 启动失败",{forcePush:!0});return}this.startThinkingFooter(J),this.proc.sendUserText(Z,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}}async onUserMessage(x,Z=[],Q="",J=""){if(this.clearStaleIdleQueueState("user_message"),this.startingAgy||this.runningAgy){await C0(this.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再继续。");return}if(this.proc?.isAlive()&&this.proc.provider!==this.selectedProvider&&!this.currentTurn&&!this.openingTurn&&this.pendingUserMessageCount===0&&this.pendingMidTurnMsgs.length===0)await this.stopIdleMismatchedProcess();let X=this.currentTurn!==null||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0;this.lastUserOpenId=Q;let Y=(Z.length?Z.map((W)=>`[file: ${W}]`).join(" ")+`
330
+ `:"")+x,G=(W)=>{this.pendingReactionIds.set(W,""),(async()=>{let H=await A1(W,"OneSecond");if(!H)return;if(this.pendingReactionIds.has(W))this.pendingReactionIds.set(W,H);else if(this.currentBatchReactionIds.has(W))this.currentBatchReactionIds.set(W,H);else A8(W,H)})()};if(!this.isRunning()){if(this.openingTurn||this.currentTurn){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}await this.startColdUserTurn(x,Y,Q);return}if(this.currentTurn!==null){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(!this.openingTurn&&this.initCount>=1){this.openingTurn=!0;try{if(this.pendingTurnInputs.push(x),await this.openTurnCard(Q,"user_message"),!this.currentTurn)return;this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}return}if(this.openingTurn||this.pendingUserMessageCount>0){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(this.pendingTurnInputs.push(x),this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,X&&J)G(J)}hasPendingAsk(){return Bf(this)}hasPendingHostAsk(){return qf(this)}onMultiMessageInbound(x,Z,Q,J){return sw(this,x,Z,Q,J)}clearMultiMsgBuffer(x){VN(this,x)}onAskMessageAnswer(x,Z,Q){return vf(this,x,Z,Q)}onHostAskMessageAnswer(x,Z,Q){return Af(this,x,Z,Q)}onAskAnswer(x,Z,Q,J){return Mf(this,x,Z,Q,J)}onHostAskAnswer(x,Z,Q,J){return Ef(this,x,Z,Q,J)}onAskCustomAnswer(x,Z,Q,J){return UN(this,x,Z,Q,J)}onHostAskCustomAnswer(x,Z,Q,J){return DN(this,x,Z,Q,J)}onPermissionDecision(x,Z,Q){return gf(this,x,Z,Q)}async startHostAskContinuation(x){if(!this.isRunning())throw Error(`${this.backendLabel()} is not running`);if(this.proc?.provider!=="codex")throw Error("askusr host continuation is only supported by Codex");if(this.currentTurn||this.openingTurn)throw Error(`${this.backendLabel()} turn still active`);this.openingTurn=!0;try{if(await this.openTurnCard("","user_message"),!this.currentTurn)throw Error("failed to open continuation turn card");this.proc.sendUserText(x,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}}persistResumableSessionId(){let x=this.proc,Z=x?.sessionId;if(!x||!Z)return;if(rP(this.sessionName,Z,x.provider),x.provider!==this.selectedProvider)return;if(Z===this.lastSessionId)return;this.lastSessionId=Z}bgStore(){return{active:this.backgroundTasks,pending:this.pendingBgTasks}}applyBgStore(x){this.backgroundTasks=x.active,this.pendingBgTasks=x.pending}bgTaskOwns(x){return this.backgroundTasks.some((Z)=>Z.toolUseId===x)||this.pendingBgTasks.some((Z)=>Z.toolUseId===x)}onBackgroundTaskChanged(){if(!vY(this.backgroundTasks)){if(this.backgroundCard)this.settleBackgroundCard();return}if(!this.backgroundCard&&!this.openingBackground){this.openingBackground=!0,this.openBackgroundCard().finally(()=>{this.openingBackground=!1});return}this.scheduleBackgroundRefresh()}async openBackgroundCard(){if(this.backgroundCard)return;let x=ZN(this.backgroundTasks),Z=await mx(this.chatId,x);if(!Z){g(`session "${this.sessionName}": background card send failed`);return}let Q;try{Q=await a8(Z)}catch(J){g(`session "${this.sessionName}": background card id_convert failed: ${J}`);return}E4(Q,this.backgroundTasks.length),this.backgroundCard={messageId:Z,cardId:Q},this.backgroundDetailAdded=new Set(this.backgroundTasks.map((J)=>J.id)),g(`session "${this.sessionName}": background card opened cardId=${Q.slice(0,12)} tasks=${this.backgroundTasks.length}`)}scheduleBackgroundRefresh(){if(!this.backgroundCard)return;if(this.backgroundRefreshTimer)return;this.backgroundRefreshTimer=setTimeout(()=>{this.backgroundRefreshTimer=null,this.refreshBackgroundCardFull()},1500)}refreshBackgroundCardFull(){let x=this.backgroundCard;if(!x)return;let Z=Date.now();for(let Q of this.backgroundTasks)if(!this.backgroundDetailAdded.has(Q.id))this.backgroundDetailAdded.add(Q.id),Ex(x.cardId,D5(Q,Z));else Zx(x.cardId,BY.panel(Q.id),D5(Q,Z))}async settleBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundCard=null,this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;await E8(x.cardId),await bX(x.messageId,Uz(this.backgroundTasks)),v1(x.cardId),await E1(x.cardId,x4({suffix:"\uD83E\uDDED 后台任务已结束"})),await d1(x.cardId),this.backgroundTasks=[],this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card settled cardId=${x.cardId.slice(0,12)}`)}async migrateBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;let Z=this.backgroundTasks.filter(b3).length;if(await E8(x.cardId),Z>0)await bX(x.messageId,Uz(this.backgroundTasks));else await bX(x.messageId,QN());v1(x.cardId),await E1(x.cardId,x4({suffix:"\uD83E\uDDED 已迁移至新卡"})),await d1(x.cardId),this.backgroundCard=null,this.backgroundTasks=this.backgroundTasks.filter((Q)=>!b3(Q)),this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card migrated cardId=${x.cardId.slice(0,12)} terminal=${Z} active=${this.backgroundTasks.length}`)}wireProc(x){x.on("error",(Z)=>{g(`session "${this.sessionName}": ${x.provider} process error: ${Z}`)}),x.on("init",()=>{if(this.persistResumableSessionId(),this.initCount++,g(`session "${this.sessionName}": SDK init#${this.initCount} pendingCount=${this.pendingUserMessageCount} midBuffer=${this.pendingMidTurnMsgs.length} currentTurn=${this.currentTurn?"yes":"no"} openingTurn=${this.openingTurn}`),this.currentTurn||this.openingTurn)return;let Z=this.pendingUserMessageCount>0,Q=!Z&&this.bgResumePending;if(!Z&&!Q)return;this.bgResumePending=!1;let J=this.lastUserOpenId;if(Z)this.pendingUserMessageCount=0,this.currentBatchReactionIds=this.pendingReactionIds,this.pendingReactionIds=new Map;this.openingTurn=!0,this.sawResultWhileOpening=!1,(async()=>{try{if(await this.openTurnCard(J,Z?"user_message":"bg_task_resume"),!this.currentTurn)if(Z)g(`session "${this.sessionName}": init-path openTurnCard failed — sendInterrupt + release reactions`),this.proc?.sendInterrupt(),this.releaseAllReactions();else if(this.bgResumeCardless=!0,this.sawResultWhileOpening)this.sawResultWhileOpening=!1,g(`session "${this.sessionName}": bg-resume openTurnCard failed, result already arrived — flushing orphan now`),this.flushOrphanAssistantToChat("bg-resume open failed, result already arrived");else g(`session "${this.sessionName}": bg-resume openTurnCard failed — orphan text flush will cover the output`);else if(this.sawResultWhileOpening)this.sawResultWhileOpening=!1,g(`session "${this.sessionName}": result raced card-open — closing freshly-opened turn card now`),await this.closeTurnCard(void 0,{hasFreshResult:!0}),this.status="idle";else this.status="working"}finally{this.openingTurn=!1}})()}),x.on("turn_started",()=>{this.persistResumableSessionId();let Z=this.proc?.lastTotalUsage;if(this.usageTotalsSeedUnknown&&!Z){this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;return}this.currentTurnUsageBaseline=Z?{...Z}:null,this.currentTurnUsageBaselineKnown=!0}),x.on("token_usage",({totalUsage:Z})=>{if(this.persistResumableSessionId(),Z)this.usageTotalsSeedUnknown=!1}),x.on("turn_plan_updated",(Z)=>{this.handleTurnPlanUpdated(Z)}),x.on("plan_delta",(Z)=>{this.handlePlanDelta(Z)}),x.on("context_compacted",(Z)=>{this.handleContextCompacted(Z)}),x.on("rate_limits_updated",(Z)=>{bw(Z)}),x.on("thread_goal_updated",(Z)=>{this.handleThreadGoalUpdated(Z)}),x.on("thread_goal_cleared",()=>{this.handleThreadGoalCleared()}),x.on("assistant_text",({text:Z})=>{this.appendAssistant(Z)}),x.on("assistant_block_stop",()=>{this.finalizeCurrentAssistantSegment()}),x.on("tool_use",({id:Z,name:Q,input:J,parentToolUseId:X})=>{if(Nf(this,Z,Q,J),!X&&(Q==="Task"||Q==="Agent"))this.lastMainTaskToolUseId=Z;if(X&&this.bgTaskOwns(X))this.applyBgStore(xN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("tool_result",({tool_use_id:Z,content:Q,is_error:J,parentToolUseId:X})=>{if(Lf(this,Z,Q,J),X&&this.bgTaskOwns(X))this.applyBgStore(YN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("can_use_tool",(Z)=>{Pf(this,Z)}),x.on("hook_callback",(Z)=>{this.proc?.sendHookResponse(Z.request_id,{})}),x.on("result",()=>{if(this.persistResumableSessionId(),this.accumulateResultStats(),this.openingTurn)this.sawResultWhileOpening=!0;if(this.userInterrupted){this.userInterrupted=!1,this.discardOrphanAssistant(),this.bgResumePending=!1;let W=this.proc?.lastResult.subtype??"unknown",H=this.proc?.lastResult.is_error===!0;g(`session "${this.sessionName}": SDK result after user stop subtype=${W} isError=${H} — ignored`),this.status="idle";return}if(!this.currentTurn&&!this.openingTurn)this.flushOrphanAssistantToChat("result with no turn card");let Z=this.pendingMidTurnMsgs.length>0,Q=this.proc?.lastResult.is_error===!0,J=this.proc?.lastResult.subtype??"success",X=this.pendingHostAsks.size>0,z,Y=!1,G=this.proc?this.backendLabel(this.proc.provider):this.backendLabel();if(Z&&!X)z=Q?`⚠️ ${G} ${J},用户已介入`:"\uD83D\uDCE8 转交新卡";else if(Q)z=`⚠️ ${G} ${J}`,Y=!0;if(g(`session "${this.sessionName}": SDK result subtype=${J} isError=${Q} midBuffer=${this.pendingMidTurnMsgs.length} forcePush=${Y}`),this.closeTurnCard(z,{forcePush:Y,hasFreshResult:!0}),this.status="idle",Tf(this),Z&&!X)this.drainMidTurnAndOpen()}),x.on("bg_task_started",(Z)=>{let Q=Z.tool_use_id??this.lastMainTaskToolUseId??void 0;g(`session "${this.sessionName}": bg_task_started task=${Z.task_id} type=${Z.task_type??"-"} subagent=${Z.subagent_type??"-"} toolUseId=${Q?.slice(0,8)??"-"} desc=${(Z.description??"").slice(0,40)}`),this.applyBgStore(rU(this.bgStore(),{...Z,tool_use_id:Q})),this.onBackgroundTaskChanged()}),x.on("bg_task_progress",(Z)=>{this.applyBgStore(tU(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_updated",(Z)=>{this.applyBgStore(eU(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_settled",(Z)=>{if(g(`session "${this.sessionName}": bg_task_settled task=${Z.task_id} status=${Z.status}`),this.applyBgStore(aU(this.bgStore(),Z)),this.onBackgroundTaskChanged(),!this.currentTurn&&!this.openingTurn&&this.initCount>=1)this.bgResumePending=!0}),x.on("exit",({code:Z,signal:Q,expected:J})=>{if(g(`session "${this.sessionName}": ${x.provider} exited code=${Z} signal=${Q} expected=${J}`),this.proc!==x){g(`session "${this.sessionName}": ignore stale ${x.provider} exit; current=${this.proc?.provider??"none"}`);return}if(this.proc=null,this.flushOrphanAssistantToChat("process exit"),this.bgResumePending=!1,this.bgResumeCardless=!1,this.sawResultWhileOpening=!1,this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("process exit"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.userInterrupted=!1,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),!J&&Z!==0&&Q!=="SIGTERM")C0(this.chatId,`⚠️ ${this.backendLabel(x.provider)} 异常退出 (code=${Z}, signal=${Q})。回复任意消息将重新启动。`)})}accumulateResultStats(){let x=this.proc?.lastResult;if(!x)return;let Z=this.currentTurnUsageBaselineKnown?nV(this.proc?.lastTotalUsage,this.currentTurnUsageBaseline):this.proc?.provider==="claude"&&x.usage?{...x.usage}:null;this.lastTurnUsage=Z,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;let Q=lV(Z),J=this.proc?.provider==="claude"?0:x.cost_delta_usd??0,X=x.duration_ms??0;if(Q!=null)this.cumStats.tokens+=Q;this.cumStats.costUsd+=J,this.cumStats.turns+=x.num_turns??1,this.lastTurnDelta={tokens:Q,costUsd:J,durationMs:X}}currentContextTokens(){if(this.proc?.provider==="claude")return this.proc?.lastContextTokens??null;let x=this.proc?.lastUsage;return gX(x)}contextLimitForDisplay(){return PX(this.proc?.lastContextWindow)}async drainMidTurnAndOpen(){if(this.pendingMidTurnMsgs.length===0)return;let x=this.pendingMidTurnMsgs;this.pendingMidTurnMsgs=[],this.openingTurn=!0;try{for(let X of x)if(this.pendingTurnInputs.push(X.text),X.msgId){let z=this.pendingReactionIds.get(X.msgId)??"";this.currentBatchReactionIds.set(X.msgId,z),this.pendingReactionIds.delete(X.msgId)}let Z=x.map((X)=>X.wireText).join(`
331
331
 
332
- `);this.proc.sendUserText(Z,[]),this.pendingUserMessageCount++;let J=x[x.length-1]?.userOpenId??this.lastUserOpenId;await this.openTurnCard(J,"user_message"),this.status="working"}finally{this.openingTurn=!1}}async openTurnCard(x,Z,Q={}){if(this.backgroundCard&&vY(this.backgroundTasks))try{await this.migrateBackgroundCard(),this.pendingRebuildBackgroundCard=!0}catch(N){g(`session "${this.sessionName}": background migrate failed (non-blocking): ${N}`)}let J=++this.turnCounter,X=this.pendingTurnInputs;this.pendingTurnInputs=[],g(`session "${this.sessionName}": openTurnCard turn=${J} trigger=${Z} inputs=${X.length}`);let z=this.withModel(Q.initialFooter??"Waiting...(0s)"),Y=aX({sessionName:this.sessionName,turn:J,provider:this.proc?.provider??this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),kind:Z,userInputs:X,initialFooter:z,directStart:Q.directStart}),G=await mx(this.chatId,Y);if(!G){g(`session "${this.sessionName}": openTurnCard sendCard EXHAUSTED retries — surfacing via raw text`),await wx(this.chatId,"❌ 创建对话卡片失败 (Feishu SDK 重试 3 次后仍连不上)。你这条消息没能送到 Codex,请稍后重发。");return}let W;try{W=await a8(G)}catch(N){g(`session "${this.sessionName}": id_convert failed: ${N}`);return}let H=(X.length>0?1:0)+1;E4(W,H,(N)=>this.onCardWriteFailure(N));let K={cardId:W,messageId:G,userOpenId:x,trigger:Z,toolCount:0,toolByUseId:new Map,planSteps:[],planExplanation:null,planUpdateCount:0,goalUpdateCount:0,contextCompactCount:0,contextCompactionPending:new Map,readBatches:new Map,openReadBatchI:null,taskCreateI:null,taskUpdateI:null,taskBoardResetThisTurn:!1,taskLiveInserted:!1,assistantSegmentCount:0,currentAssistantSegmentId:null,currentAssistantText:"",segmentTexts:new Map,startedAt:Date.now(),footerStatusHandle:null,footerStatusStartedAt:0,footerStatusLabel:null,rotating:null,rotateCount:0,failureRotateCount:0,rotateGivenUp:!1,outboundSeenPaths:new Set,outboundSentPaths:new Set,hostAskMarkersSeen:new Set};if(this.currentTurn=K,Q.startThinking!==!1)this.startThinkingFooter(K);if(this.pendingRebuildBackgroundCard){if(this.pendingRebuildBackgroundCard=!1,vY(this.backgroundTasks))this.openBackgroundCard()}}maybeMidTurnRotate(){let x=this.currentTurn;if(!x)return;if(x.rotating)return;if(HU(x.cardId)<AU0)return;this.startMidTurnRotate(x)}onCardWriteFailure(x){let Z=this.currentTurn;if(!Z)return;if(Z.rotating)return;if(Z.rotateGivenUp)return;if(Z.failureRotateCount>=jN){Z.rotateGivenUp=!0,this.stopFooterStatus(Z),$u(Z.cardId),g(`session "${this.sessionName}": failure-rotate cap (${jN}) hit — giving up, rest of turn is log-only`),wx(this.chatId,`⚠️ 卡片写入失败已触发 ${jN} 次换卡仍未恢复(疑似飞书故障或元素超限),本轮后续输出仅日志可见。`);return}let Q=Fu(x)?`element limit (${x})`:`write failure (code=${x??"n/a"})`;g(`session "${this.sessionName}": ${Q} on card=${Z.cardId.slice(0,8)}… — rotating to fresh card`),Z.failureRotateCount++,this.startMidTurnRotate(Z)}startMidTurnRotate(x){if(x.rotating)return;x.rotateCount++;let{cardId:Z,toolByUseId:Q,readBatches:J}=x;x.rotating=(async()=>{try{g(`session "${this.sessionName}": mid-turn rotate triggered card=${Z.slice(0,8)}… elementCount=${HU(Z)}`);let X=aX({sessionName:this.sessionName,turn:this.turnCounter,provider:this.proc?.provider??this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),kind:"card_full",userInputs:[]}),z=await mx(this.chatId,X);if(!z){g(`session "${this.sessionName}": mid-turn rotate sendCard EXHAUSTED — staying on old card,subsequent adds will drop`),await wx(this.chatId,"⚠️ 卡片元素超出飞书上限,本轮后续输出仅日志可见(开新卡失败)。");return}let Y;try{Y=await a8(z)}catch(K){g(`session "${this.sessionName}": mid-turn rotate id_convert failed: ${K}`);return}E4(Y,2,(K)=>this.onCardWriteFailure(K)),this.stopFooterStatus(x),x.cardId=Y,x.messageId=z,x.toolCount=0,x.toolByUseId=new Map,x.readBatches=new Map,x.openReadBatchI=null,x.planUpdateCount=0,x.goalUpdateCount=0;let G=x.currentAssistantSegmentId,W=(x.currentAssistantText??"").trim(),H=x.segmentTexts;if(x.assistantSegmentCount=0,x.currentAssistantSegmentId=null,x.currentAssistantText="",x.segmentTexts=new Map,W)this.startWritingFooter(x);else this.startThinkingFooter(x);if(x.taskLiveInserted)Ex(x.cardId,q3(this.taskBoard),{type:"insert_before",targetElementId:$0.footer});for(let[K,N]of H){if(G&&W&&K===G)continue;if(!ZY(Z,K))continue;let L=x.assistantSegmentCount++,D=$0.assistant(L);x.segmentTexts.set(D,N),Ex(Y,this.completedAssistantElement(D,N),{type:"insert_before",targetElementId:M1(x)})}if(Df(this,Z,Y,Q,J),G&&W){let K=x.assistantSegmentCount++,N=$0.assistant(K);x.currentAssistantSegmentId=N,x.currentAssistantText=W,x.segmentTexts.set(N,W)}try{await E8(Z);for(let[N,L]of H){if(G&&W&&N===G)continue;if(ZY(Z,N))continue;await Zx(Z,N,{tag:"markdown",element_id:N,content:this.cleanAssistantTextForDisplay(L).trim()||" "})}let K=x.contextCompactCount>0?` · \uD83D\uDEA8 压缩×${x.contextCompactCount}`:"";await this.replaceFooterContent(Z,this.withModel(`\uD83D\uDCE8 已续至下一张卡 ↓${K}`)),v1(Z),await E1(Z,x4({suffix:"\uD83D\uDCE8 转下一张"})),await d1(Z)}catch(K){g(`session "${this.sessionName}": mid-turn rotate close-old failed: ${K}`)}g(`session "${this.sessionName}": mid-turn rotate done old=${Z.slice(0,8)}… new=${Y.slice(0,8)}…`)}finally{x.rotating=null}})()}addPlanSnapshotOnCurrentTurn(){let x=this.currentTurn;if(!x||x.planSteps.length===0)return;this.maybeMidTurnRotate();let Z=x.cardId,Q=$0.planUpdate(x.planUpdateCount++);Ex(Z,cU(x.planSteps,x.planExplanation,"",Q),{type:"insert_before",targetElementId:M1(x)})}addGoalUpdateOnCurrentTurn(x){let Z=this.currentTurn;if(!Z)return;this.maybeMidTurnRotate();let Q=$0.goalUpdate(Z.goalUpdateCount++);Ex(Z.cardId,qU(x,Q),{type:"insert_before",targetElementId:M1(Z)})}addGoalClearedOnCurrentTurn(){let x=this.currentTurn;if(!x)return;this.maybeMidTurnRotate();let Z=$0.goalUpdate(x.goalUpdateCount++);Ex(x.cardId,{tag:"markdown",element_id:Z,content:`**\uD83C\uDFAF 当前目标**
332
+ `);this.proc.sendUserText(Z,[]),this.pendingUserMessageCount++;let J=x[x.length-1]?.userOpenId??this.lastUserOpenId;await this.openTurnCard(J,"user_message"),this.status="working"}finally{this.openingTurn=!1}}async openTurnCard(x,Z,Q={}){if(this.bgResumePending=!1,this.backgroundCard&&vY(this.backgroundTasks))try{await this.migrateBackgroundCard(),this.pendingRebuildBackgroundCard=!0}catch(L){g(`session "${this.sessionName}": background migrate failed (non-blocking): ${L}`)}let J=++this.turnCounter,X=this.pendingTurnInputs;this.pendingTurnInputs=[],g(`session "${this.sessionName}": openTurnCard turn=${J} trigger=${Z} inputs=${X.length}`);let z=this.withModel(Q.initialFooter??"Waiting...(0s)"),Y=aX({sessionName:this.sessionName,turn:J,provider:this.proc?.provider??this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),kind:Z,userInputs:X,initialFooter:z,directStart:Q.directStart}),G=await mx(this.chatId,Y);if(!G){if(g(`session "${this.sessionName}": openTurnCard sendCard EXHAUSTED retries — surfacing via raw text`),Z==="user_message")await wx(this.chatId,"❌ 创建对话卡片失败 (Feishu SDK 重试 3 次后仍连不上)。你这条消息没能送到 Codex,请稍后重发。");return}let W;try{W=await a8(G)}catch(L){g(`session "${this.sessionName}": id_convert failed: ${L}`);return}let H=(Z==="bg_task_resume"?1:0)+(X.length>0?1:0)+1;E4(W,H,(L)=>this.onCardWriteFailure(L));let K={cardId:W,messageId:G,userOpenId:x,trigger:Z,toolCount:0,toolByUseId:new Map,planSteps:[],planExplanation:null,planUpdateCount:0,goalUpdateCount:0,contextCompactCount:0,contextCompactionPending:new Map,readBatches:new Map,openReadBatchI:null,taskCreateI:null,taskUpdateI:null,taskBoardResetThisTurn:!1,taskLiveInserted:!1,assistantSegmentCount:0,currentAssistantSegmentId:null,currentAssistantText:"",segmentTexts:new Map,startedAt:Date.now(),footerStatusHandle:null,footerStatusStartedAt:0,footerStatusLabel:null,rotating:null,rotateCount:0,failureRotateCount:0,rotateGivenUp:!1,outboundSeenPaths:new Set,outboundSentPaths:new Set,hostAskMarkersSeen:new Set};if(this.currentTurn=K,Q.startThinking!==!1)this.startThinkingFooter(K);let N=this.takeOrphanAssistantText();if(N)this.appendAssistant(N),this.finalizeCurrentAssistantSegment();if(this.pendingRebuildBackgroundCard){if(this.pendingRebuildBackgroundCard=!1,vY(this.backgroundTasks))this.openBackgroundCard()}}maybeMidTurnRotate(){let x=this.currentTurn;if(!x)return;if(x.rotating)return;if(HU(x.cardId)<AU0)return;this.startMidTurnRotate(x)}onCardWriteFailure(x){let Z=this.currentTurn;if(!Z)return;if(Z.rotating)return;if(Z.rotateGivenUp)return;if(Z.failureRotateCount>=jN){Z.rotateGivenUp=!0,this.stopFooterStatus(Z),$u(Z.cardId),g(`session "${this.sessionName}": failure-rotate cap (${jN}) hit — giving up, rest of turn is log-only`),wx(this.chatId,`⚠️ 卡片写入失败已触发 ${jN} 次换卡仍未恢复(疑似飞书故障或元素超限),本轮后续输出仅日志可见。`);return}let Q=Fu(x)?`element limit (${x})`:`write failure (code=${x??"n/a"})`;g(`session "${this.sessionName}": ${Q} on card=${Z.cardId.slice(0,8)}… — rotating to fresh card`),Z.failureRotateCount++,this.startMidTurnRotate(Z)}startMidTurnRotate(x){if(x.rotating)return;x.rotateCount++;let{cardId:Z,toolByUseId:Q,readBatches:J}=x;x.rotating=(async()=>{try{g(`session "${this.sessionName}": mid-turn rotate triggered card=${Z.slice(0,8)}… elementCount=${HU(Z)}`);let X=aX({sessionName:this.sessionName,turn:this.turnCounter,provider:this.proc?.provider??this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),kind:"card_full",userInputs:[]}),z=await mx(this.chatId,X);if(!z){g(`session "${this.sessionName}": mid-turn rotate sendCard EXHAUSTED — staying on old card,subsequent adds will drop`),await wx(this.chatId,"⚠️ 卡片元素超出飞书上限,本轮后续输出仅日志可见(开新卡失败)。");return}let Y;try{Y=await a8(z)}catch(K){g(`session "${this.sessionName}": mid-turn rotate id_convert failed: ${K}`);return}E4(Y,2,(K)=>this.onCardWriteFailure(K)),this.stopFooterStatus(x),x.cardId=Y,x.messageId=z,x.toolCount=0,x.toolByUseId=new Map,x.readBatches=new Map,x.openReadBatchI=null,x.planUpdateCount=0,x.goalUpdateCount=0;let G=x.currentAssistantSegmentId,W=(x.currentAssistantText??"").trim(),H=x.segmentTexts;if(x.assistantSegmentCount=0,x.currentAssistantSegmentId=null,x.currentAssistantText="",x.segmentTexts=new Map,W)this.startWritingFooter(x);else this.startThinkingFooter(x);if(x.taskLiveInserted)Ex(x.cardId,q3(this.taskBoard),{type:"insert_before",targetElementId:$0.footer});for(let[K,N]of H){if(G&&W&&K===G)continue;if(!ZY(Z,K))continue;let L=x.assistantSegmentCount++,D=$0.assistant(L);x.segmentTexts.set(D,N),Ex(Y,this.completedAssistantElement(D,N),{type:"insert_before",targetElementId:M1(x)})}if(Df(this,Z,Y,Q,J),G&&W){let K=x.assistantSegmentCount++,N=$0.assistant(K);x.currentAssistantSegmentId=N,x.currentAssistantText=W,x.segmentTexts.set(N,W)}try{await E8(Z);for(let[N,L]of H){if(G&&W&&N===G)continue;if(ZY(Z,N))continue;await Zx(Z,N,{tag:"markdown",element_id:N,content:this.cleanAssistantTextForDisplay(L).trim()||" "})}let K=x.contextCompactCount>0?` · \uD83D\uDEA8 压缩×${x.contextCompactCount}`:"";await this.replaceFooterContent(Z,this.withModel(`\uD83D\uDCE8 已续至下一张卡 ↓${K}`)),v1(Z),await E1(Z,x4({suffix:"\uD83D\uDCE8 转下一张"})),await d1(Z)}catch(K){g(`session "${this.sessionName}": mid-turn rotate close-old failed: ${K}`)}g(`session "${this.sessionName}": mid-turn rotate done old=${Z.slice(0,8)}… new=${Y.slice(0,8)}…`)}finally{x.rotating=null}})()}addPlanSnapshotOnCurrentTurn(){let x=this.currentTurn;if(!x||x.planSteps.length===0)return;this.maybeMidTurnRotate();let Z=x.cardId,Q=$0.planUpdate(x.planUpdateCount++);Ex(Z,cU(x.planSteps,x.planExplanation,"",Q),{type:"insert_before",targetElementId:M1(x)})}addGoalUpdateOnCurrentTurn(x){let Z=this.currentTurn;if(!Z)return;this.maybeMidTurnRotate();let Q=$0.goalUpdate(Z.goalUpdateCount++);Ex(Z.cardId,qU(x,Q),{type:"insert_before",targetElementId:M1(Z)})}addGoalClearedOnCurrentTurn(){let x=this.currentTurn;if(!x)return;this.maybeMidTurnRotate();let Z=$0.goalUpdate(x.goalUpdateCount++);Ex(x.cardId,{tag:"markdown",element_id:Z,content:`**\uD83C\uDFAF 当前目标**
333
333
 
334
334
  已清除`},{type:"insert_before",targetElementId:M1(x)})}handleContextCompacted(x){let Z=this.currentTurn;if(!Z){if(this.manualContextCompactionPending){g(`session "${this.sessionName}": manual context compaction ${x.phase??"event"} with no current turn`);return}if(x.phase==="start"){g(`session "${this.sessionName}": context compaction start with no current turn`);return}g(`session "${this.sessionName}": context compacted with no current turn`);let W=this.proc?this.backendLabel(this.proc.provider):this.backendLabel();wx(this.chatId,`\uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8 CONTEXT COMPACTED / 上下文已压缩 \uD83D\uDEA8\uD83D\uDEA8\uD83D\uDEA8
335
335
 
336
- ${W} 报告发生了上下文压缩,但当前没有可写的对话卡片。`);return}if(this.startWorkingFooter(Z),Z.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();if(Z.openReadBatchI=null,this.maybeMidTurnRotate(),x.phase==="start"){let W=Z.contextCompactCount++,H=jU0(x);Z.contextCompactionPending.set(H,{i:W,cardId:Z.cardId,notice:x});let K=$0.contextCompact(W);g(`session "${this.sessionName}": context compaction start #${W+1} key=${H}`),Ex(Z.cardId,WY(W,x,K),{type:"insert_before",targetElementId:M1(Z)}),kX(Z.cardId,`\uD83D\uDEA8 压缩×${Z.contextCompactCount}`);return}let Q=x.itemId&&Z.contextCompactionPending.has(x.itemId)?x.itemId:IU0(Z),J=Q?Z.contextCompactionPending.get(Q):void 0;if(Q)Z.contextCompactionPending.delete(Q);let X=TU0(J?.notice,x),z=J?.i??Z.contextCompactCount++,Y=J?.cardId??Z.cardId,G=$0.contextCompact(z);if(g(`session "${this.sessionName}": context compaction completed #${z+1}${Q?` key=${Q}`:""}`),J)Zx(Y,G,WY(z,X,G));else Ex(Y,WY(z,X,G),{type:"insert_before",targetElementId:M1(Z)});kX(Z.cardId,`\uD83D\uDEA8 压缩×${Z.contextCompactCount}`)}handleTurnPlanUpdated(x){let Z=this.currentTurn;if(!Z){g(`session "${this.sessionName}": turn/plan/updated with no current turn`);return}if(this.startWorkingFooter(Z),Z.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();if(Z.openReadBatchI=null,!Array.isArray(x.plan))g(`session "${this.sessionName}": turn/plan/updated missing plan array`),Z.planSteps=[];else Z.planSteps=x.plan.map((Q)=>({step:typeof Q.step==="string"&&Q.step?Q.step:"MISS",status:typeof Q.status==="string"&&Q.status?Q.status:"MISS"}));Z.planExplanation=typeof x.explanation==="string"?x.explanation:null,this.addPlanSnapshotOnCurrentTurn()}handlePlanDelta(x){if(typeof x.delta!=="string"||!x.delta){g(`session "${this.sessionName}": item/plan/delta missing delta text`);return}if(typeof x.itemId!=="string"||!x.itemId)g(`session "${this.sessionName}": item/plan/delta missing itemId`)}handleThreadGoalUpdated(x){if(!x||typeof x.objective!=="string"){g(`session "${this.sessionName}": thread/goal/updated missing objective`);return}if(x.tokenBudget!=null&&typeof x.tokenBudget!=="number")g(`session "${this.sessionName}": thread/goal/updated invalid tokenBudget`);let Z=this.currentGoal,Q={objective:x.objective,status:typeof x.status==="string"&&x.status?x.status:"MISS",tokenBudget:typeof x.tokenBudget==="number"?x.tokenBudget:x.tokenBudget===null?null:Number.NaN,tokensUsed:typeof x.tokensUsed==="number"?x.tokensUsed:Number.NaN,timeUsedSeconds:typeof x.timeUsedSeconds==="number"?x.timeUsedSeconds:Number.NaN};if(this.currentGoal=Q,Z&&eX(Z)===eX(Q))return;let J=this.currentTurn;if(J){if(this.startWorkingFooter(J),J.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();J.openReadBatchI=null}this.addGoalUpdateOnCurrentTurn(Q)}handleThreadGoalCleared(){if(!this.currentGoal)return;this.currentGoal=null;let x=this.currentTurn;if(!x)return;if(this.startWorkingFooter(x),x.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();x.openReadBatchI=null,this.addGoalClearedOnCurrentTurn()}appendAssistant(x){if(!this.currentTurn)return;let Z=this.currentTurn;if(this.startWritingFooter(Z),!Z.currentAssistantSegmentId){Z.openReadBatchI=null,this.maybeMidTurnRotate();let z=Z.assistantSegmentCount++,Y=$0.assistant(z);Z.currentAssistantSegmentId=Y,Z.currentAssistantText=""}Z.currentAssistantText+=x;let Q=Z.currentAssistantSegmentId;if(!Q)return;Z.segmentTexts.set(Q,Z.currentAssistantText),this.processOutboundMarkers(Z.currentAssistantText),this.processHostAskMarkers(Z.currentAssistantText,Z);let X=this.cleanAssistantTextForDisplay(Z.currentAssistantText).slice(-60);kX(Z.cardId,X)}completedAssistantElement(x,Z){return{tag:"markdown",element_id:x,content:this.cleanAssistantTextForDisplay(Z).trim()||" "}}addCompletedAssistantSegment(x,Z,Q){return Ex(x.cardId,this.completedAssistantElement(Z,Q),{type:"insert_before",targetElementId:M1(x)})}finalizeCurrentAssistantSegment(){let x=this.currentTurn;if(!x)return;if(x.rotating)return;let Z=x.currentAssistantSegmentId,Q=x.currentAssistantText??"";if(Z&&Q.trim())this.addCompletedAssistantSegment(x,Z,Q),this.startWorkingFooter(x);x.currentAssistantSegmentId=null,x.currentAssistantText=""}processOutboundMarkers(x){for(let Z of hw(x))this.sendOutboundPath(Z,"send marker")}processHostAskMarkers(x,Z){if(this.proc?.provider!=="codex")return;for(let Q of mw(x)){if(Z.hostAskMarkersSeen.has(Q.raw))continue;Z.hostAskMarkersSeen.add(Q.raw),bf(this,Q.payload,Q.raw)}}cleanAssistantTextForDisplay(x){let Z=this.proc?.provider==="codex"?`
336
+ ${W} 报告发生了上下文压缩,但当前没有可写的对话卡片。`);return}if(this.startWorkingFooter(Z),Z.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();if(Z.openReadBatchI=null,this.maybeMidTurnRotate(),x.phase==="start"){let W=Z.contextCompactCount++,H=jU0(x);Z.contextCompactionPending.set(H,{i:W,cardId:Z.cardId,notice:x});let K=$0.contextCompact(W);g(`session "${this.sessionName}": context compaction start #${W+1} key=${H}`),Ex(Z.cardId,WY(W,x,K),{type:"insert_before",targetElementId:M1(Z)}),kX(Z.cardId,`\uD83D\uDEA8 压缩×${Z.contextCompactCount}`);return}let Q=x.itemId&&Z.contextCompactionPending.has(x.itemId)?x.itemId:IU0(Z),J=Q?Z.contextCompactionPending.get(Q):void 0;if(Q)Z.contextCompactionPending.delete(Q);let X=TU0(J?.notice,x),z=J?.i??Z.contextCompactCount++,Y=J?.cardId??Z.cardId,G=$0.contextCompact(z);if(g(`session "${this.sessionName}": context compaction completed #${z+1}${Q?` key=${Q}`:""}`),J)Zx(Y,G,WY(z,X,G));else Ex(Y,WY(z,X,G),{type:"insert_before",targetElementId:M1(Z)});kX(Z.cardId,`\uD83D\uDEA8 压缩×${Z.contextCompactCount}`)}handleTurnPlanUpdated(x){let Z=this.currentTurn;if(!Z){g(`session "${this.sessionName}": turn/plan/updated with no current turn`);return}if(this.startWorkingFooter(Z),Z.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();if(Z.openReadBatchI=null,!Array.isArray(x.plan))g(`session "${this.sessionName}": turn/plan/updated missing plan array`),Z.planSteps=[];else Z.planSteps=x.plan.map((Q)=>({step:typeof Q.step==="string"&&Q.step?Q.step:"MISS",status:typeof Q.status==="string"&&Q.status?Q.status:"MISS"}));Z.planExplanation=typeof x.explanation==="string"?x.explanation:null,this.addPlanSnapshotOnCurrentTurn()}handlePlanDelta(x){if(typeof x.delta!=="string"||!x.delta){g(`session "${this.sessionName}": item/plan/delta missing delta text`);return}if(typeof x.itemId!=="string"||!x.itemId)g(`session "${this.sessionName}": item/plan/delta missing itemId`)}handleThreadGoalUpdated(x){if(!x||typeof x.objective!=="string"){g(`session "${this.sessionName}": thread/goal/updated missing objective`);return}if(x.tokenBudget!=null&&typeof x.tokenBudget!=="number")g(`session "${this.sessionName}": thread/goal/updated invalid tokenBudget`);let Z=this.currentGoal,Q={objective:x.objective,status:typeof x.status==="string"&&x.status?x.status:"MISS",tokenBudget:typeof x.tokenBudget==="number"?x.tokenBudget:x.tokenBudget===null?null:Number.NaN,tokensUsed:typeof x.tokensUsed==="number"?x.tokensUsed:Number.NaN,timeUsedSeconds:typeof x.timeUsedSeconds==="number"?x.timeUsedSeconds:Number.NaN};if(this.currentGoal=Q,Z&&eX(Z)===eX(Q))return;let J=this.currentTurn;if(J){if(this.startWorkingFooter(J),J.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();J.openReadBatchI=null}this.addGoalUpdateOnCurrentTurn(Q)}handleThreadGoalCleared(){if(!this.currentGoal)return;this.currentGoal=null;let x=this.currentTurn;if(!x)return;if(this.startWorkingFooter(x),x.currentAssistantSegmentId)this.finalizeCurrentAssistantSegment();x.openReadBatchI=null,this.addGoalClearedOnCurrentTurn()}appendAssistant(x){if(!this.currentTurn){if(this.openingTurn||this.bgResumeCardless)this.orphanAssistantCurrent+=x;return}let Z=this.currentTurn;if(this.startWritingFooter(Z),!Z.currentAssistantSegmentId){Z.openReadBatchI=null,this.maybeMidTurnRotate();let z=Z.assistantSegmentCount++,Y=$0.assistant(z);Z.currentAssistantSegmentId=Y,Z.currentAssistantText=""}Z.currentAssistantText+=x;let Q=Z.currentAssistantSegmentId;if(!Q)return;Z.segmentTexts.set(Q,Z.currentAssistantText),this.processOutboundMarkers(Z.currentAssistantText),this.processHostAskMarkers(Z.currentAssistantText,Z);let X=this.cleanAssistantTextForDisplay(Z.currentAssistantText).slice(-60);kX(Z.cardId,X)}takeOrphanAssistantText(){let x=[...this.orphanAssistantSegments];if(this.orphanAssistantCurrent.trim())x.push(this.orphanAssistantCurrent);return this.orphanAssistantSegments=[],this.orphanAssistantCurrent="",x.join(`
337
+
338
+ `).trim()}discardOrphanAssistant(){this.orphanAssistantSegments=[],this.orphanAssistantCurrent="",this.bgResumeCardless=!1}flushOrphanAssistantToChat(x){let Z=this.takeOrphanAssistantText();if(!Z)return;let Q=this.cleanAssistantTextForDisplay(Z).trim();if(!Q)return;g(`session "${this.sessionName}": flushing ${Q.length} chars of orphan assistant text (${x})`),C0(this.chatId,`\uD83D\uDCC4 后台轮输出(未能建卡,纯文本兜底):
339
+
340
+ ${Q}`)}completedAssistantElement(x,Z){return{tag:"markdown",element_id:x,content:this.cleanAssistantTextForDisplay(Z).trim()||" "}}addCompletedAssistantSegment(x,Z,Q){return Ex(x.cardId,this.completedAssistantElement(Z,Q),{type:"insert_before",targetElementId:M1(x)})}finalizeCurrentAssistantSegment(){let x=this.currentTurn;if(!x){if(this.orphanAssistantCurrent.trim())this.orphanAssistantSegments.push(this.orphanAssistantCurrent);this.orphanAssistantCurrent="";return}if(x.rotating)return;let Z=x.currentAssistantSegmentId,Q=x.currentAssistantText??"";if(Z&&Q.trim())this.addCompletedAssistantSegment(x,Z,Q),this.startWorkingFooter(x);x.currentAssistantSegmentId=null,x.currentAssistantText=""}processOutboundMarkers(x){for(let Z of hw(x))this.sendOutboundPath(Z,"send marker")}processHostAskMarkers(x,Z){if(this.proc?.provider!=="codex")return;for(let Q of mw(x)){if(Z.hostAskMarkersSeen.has(Q.raw))continue;Z.hostAskMarkersSeen.add(Q.raw),bf(this,Q.payload,Q.raw)}}cleanAssistantTextForDisplay(x){let Z=this.proc?.provider==="codex"?`
337
341
 
338
342
  _已发起澄清问题,请回答对应卡片。_`:"";return _w(x,Z)}sendOutboundPath(x,Z){let Q=x.trim();if(!Q)return;let J=this.currentTurn;if(J?.outboundSeenPaths.has(Q))return;if(J?.outboundSeenPaths.add(Q),!RU0(Q)){g(`session "${this.sessionName}": ignore non-absolute outbound path from ${Z}: ${Q}`);return}J?.outboundSentPaths.add(Q),g(`session "${this.sessionName}": outbound send from ${Z}: ${Q}`),Vu(this.chatId,Q)}startFooterStatus(x,Z){if(x.rotateGivenUp)return;if(x.footerStatusHandle&&x.footerStatusLabel===Z)return;this.stopFooterStatus(x),x.footerStatusLabel=Z,x.footerStatusStartedAt=Date.now();let Q=()=>{if(x.footerStatusHandle==null||!x.footerStatusLabel)return;let J=Math.max(0,Math.floor((Date.now()-x.footerStatusStartedAt)/1000));this.replaceFooterContent(x.cardId,this.withModel(`${x.footerStatusLabel}(${J}s)`))};x.footerStatusHandle=setInterval(Q,My),Q()}startThinkingFooter(x){this.startFooterStatus(x,cU0)}startWritingFooter(x){this.startFooterStatus(x,qU0)}startWorkingFooter(x){this.startFooterStatus(x,bU0)}stopFooterStatus(x){if(!x)return;if(x.footerStatusHandle)clearInterval(x.footerStatusHandle);x.footerStatusHandle=null,x.footerStatusStartedAt=0,x.footerStatusLabel=null}async footerFiveHourSuffix(){let x=null,Z=null;if(this.proc?.provider==="claude"){let J=await KN();if(J.state==="ok")x=J.fiveHour?.percent??null,Z=J.fiveHour?.resetsAt??null}else{let J=Aw();if(J?.state==="ok")x=J.fiveHour?.percent??null,Z=J.fiveHour?.resetsAt??null}if(x==null)return"";let Q=Z&&Z.getTime()>Date.now()?I3(Z):"";return Q?` | 5h·${Math.round(x)}%·[${Q}]`:` | 5h·${Math.round(x)}%`}async closeTurnCard(x,Z={}){let Q=this.currentTurn;if(!Q)return;if(this.currentTurn=null,this.stopFooterStatus(Q),Q.rotating)await Q.rotating;this.stopFooterStatus(Q);let J=((Date.now()-Q.startedAt)/1000).toFixed(1),X=Q.cardId,z=Q.segmentTexts;if(await E8(X),Q.currentAssistantSegmentId&&Q.currentAssistantText.trim())await this.addCompletedAssistantSegment(Q,Q.currentAssistantSegmentId,Q.currentAssistantText),Q.currentAssistantSegmentId=null,Q.currentAssistantText="";for(let[D,O]of z)await Zx(X,D,this.completedAssistantElement(D,O));let G=[`${x?x:"✅"} ⏱ ${J}s`];if(Z.hasFreshResult){let D=this.currentContextTokens(),O=this.contextLimitForDisplay(),v=this.proc?.provider==="claude",B=IU(D,O,v?0:void 0);if(B)G.push(`\uD83E\uDDE0 ${B}`);let j=this.lastTurnDelta?.costUsd??0;if(j>0)G.push(`\uD83D\uDCB0 $${j.toFixed(3)}`)}if(Q.contextCompactCount>0)G.push(`\uD83D\uDEA8 压缩×${Q.contextCompactCount}`);if(Q.outboundSentPaths.size>0)G.push(`\uD83D\uDCCE ${Q.outboundSentPaths.size}`);let W=this.modelLine();if(W)G.push(W);let H=G.join(" | "),K=Z.hasFreshResult?TU(this.lastTurnUsage)+(Q.rotateGivenUp?"":await this.footerFiveHourSuffix()):"",N=K?`${H}
339
343
  ${K}`:H;if(await this.replaceFooterContent(X,N),v1(X),await E1(X,x4({durationSec:J,outputTokens:Z.hasFreshResult?this.lastTurnUsage?.output_tokens:void 0,suffix:x})),await d1(X),(Z.forcePush||!x)&&Q.userOpenId&&Q.messageId&&!Q.rotateGivenUp)F5(Q.messageId,[Q.userOpenId]);let L=[...this.currentBatchReactionIds.entries(),...this.pendingReactionIds.entries()];if(L.length>0){for(let[D,O]of L)if(O)A8(D,O);this.currentBatchReactionIds=new Map,this.pendingReactionIds=new Map}}}});function w4(x){return{card:{type:"raw",data:x}}}import{createServer as EU0}from"node:http";function gU0(x){let Z=x.level==="error"?"red":x.level==="warn"?"yellow":"blue",Q=x.level==="error"?"❌":x.level==="warn"?"⚠️":"\uD83D\uDD14",J=new Date,X=`${String(J.getHours()).padStart(2,"0")}:${String(J.getMinutes()).padStart(2,"0")}`,z=[];for(let Y of x.images??[])if(Y.key)z.push({tag:"img",img_key:Y.key,alt:{tag:"plain_text",content:"screenshot"}});else z.push({tag:"markdown",content:`<font color='red'>\uD83D\uDCF7 图片上传失败: ${Y.src}</font>`});return z.push({tag:"markdown",content:x.text||"_(空消息)_"}),z.push({tag:"hr"}),z.push({tag:"markdown",content:`<font color='grey'>via notify · ${X}</font>`}),{schema:"2.0",config:{},header:{title:{tag:"plain_text",content:`${Q} ${x.title}`},template:Z},body:{elements:z}}}function Ty(x){try{let Z=EU0((Q,J)=>{PU0(Q,J).catch((X)=>{if(g(`notify: handler crash: ${X?.message??X}`),!J.headersSent)J.statusCode=500,J.setHeader("content-type","text/plain; charset=utf-8"),J.end("internal error");else try{J.end()}catch{}})});Z.on("error",(Q)=>{g(`notify: server bind failed (${x.bind}:${x.port}): ${Q.message}`)}),Z.listen(x.port,x.bind,()=>{g(`notify: HTTP listening at http://${x.bind}:${x.port}/notify`)})}catch(Z){g(`notify: server bind failed (${x.bind}:${x.port}): ${Z}`)}}async function PU0(x,Z){let Q=new URL(x.url??"/",`http://${x.headers.host??"localhost"}`),J=(I,k)=>{Z.statusCode=I,Z.setHeader("content-type","text/plain; charset=utf-8"),Z.end(k)},X=(I,k)=>{Z.statusCode=I,Z.setHeader("content-type","application/json"),Z.end(JSON.stringify(k))};if(x.method==="GET"&&Q.pathname==="/")return J(200,`lodestar notify
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leviyuan/lodestar",
3
- "version": "0.11.13",
3
+ "version": "0.11.14",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },