@leviyuan/lodestar 0.13.3 → 0.13.5

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 +2 -2
  2. package/package.json +1 -1
package/dist/lodestar.js CHANGED
@@ -228,7 +228,7 @@ Please migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resour
228
228
 
229
229
  `)+`
230
230
 
231
- `:"";try{this.input.push({type:"user",session_id:this.sessionId??"",message:{role:"user",content:[{type:"text",text:J+Q+x}]},parent_tool_use_id:null,priority:"now"})}catch(X){let z=X instanceof Error?X:Error(String(X));k(`claude-agent-process: sendUserText failed: ${z.message}`),this.failTurnStart(z)}}sendInterrupt(){this.query?.interrupt().catch((x)=>k(`claude-agent-process: interrupt failed: ${x}`))}sendPermissionResponse(x,Z,Q){let J=this.pendingPermissions.get(String(x));if(!J){k(`claude-agent-process: permission response for unknown request ${x}`);return}if(this.pendingPermissions.delete(String(x)),J.cleanup?.(),Z==="allow")J.resolve({behavior:"allow",updatedInput:Q?.updatedInput??{}});else J.resolve({behavior:"deny",message:Q?.denyMessage??""})}sendToolResult(x,Z,Q=!1){k("claude-agent-process: sendToolResult ignored; Claude Agent SDK executes built-in tools internally")}sendHookResponse(x,Z={}){k("claude-agent-process: sendHookResponse ignored; hooks are handled by SDK callbacks")}isAlive(){return this.alive}async kill(x=5000){if(!this.alive)return;this.expectedExit=!0,this.input.close();try{this.query?.close()}catch{}let Z=Date.now();while(this.alive&&Date.now()-Z<x)await new Promise((Q)=>setTimeout(Q,100));if(this.alive)this.alive=!1,this.emit("exit",{code:null,signal:"SIGKILL",expected:this.expectedExit})}async listModels(){if(!this.started)this.sendInitialize();if(!this.query)throw Error("claude-agent-process: SDK query not initialized (sendInitialize failed or not called)");return(await this.query.supportedModels()).map(r$0)}async setModelSettings(x,Z){let Q=aU(x);if(!L3(Z))throw Error(`invalid Claude effort: ${String(Z)}`);if(!this.started)this.sendInitialize();if(!this.query)throw Error("claude-agent-process: SDK query not initialized (sendInitialize failed or not called)");if(Q)await this.query.setModel(Q);if(Z==="max")await this.query.applyFlagSettings({ultracode:!0,effortLevel:null});else await this.query.applyFlagSettings({effortLevel:Z,ultracode:null});this.opts.model=x,this.opts.effort=Z,this.lastModel=Q?c3(x):"claude:default",this.lastEffort=Z}async setModel(x){await this.setModelSettings(x,this.opts.effort)}async compactThread(){if(!this.input)throw Error("claude thread not initialized");return this.input.push({type:"user",session_id:this.sessionId??"",message:{role:"user",content:[{type:"text",text:"/compact"}]},parent_tool_use_id:null,priority:"now"}),new Promise((x,Z)=>{let J=!1,X=()=>{clearTimeout(z),this.off("context_compacted",Y),this.off("result",G)},z=setTimeout(()=>{if(J)return;J=!0,X(),Z(Error("claude /compact timed out (no compact_boundary, no result)"))},600000),Y=()=>{if(J)return;J=!0,X(),x()},G=()=>{if(J)return;J=!0,X(),Z(new J2)};this.on("context_compacted",Y),this.on("result",G)})}async injectThreadItems(x){if(!Array.isArray(x)||x.length===0)return;this.pendingInjectedContext.push(["Host-injected prior tool context for this continuation:",JSON.stringify(x,null,2)].join(`
231
+ `:"";try{this.input.push({type:"user",session_id:this.sessionId??"",message:{role:"user",content:[{type:"text",text:J+Q+x}]},parent_tool_use_id:null,priority:"now"})}catch(X){let z=X instanceof Error?X:Error(String(X));k(`claude-agent-process: sendUserText failed: ${z.message}`),this.failTurnStart(z)}}sendInterrupt(){this.query?.interrupt().catch((x)=>k(`claude-agent-process: interrupt failed: ${x}`))}sendPermissionResponse(x,Z,Q){let J=this.pendingPermissions.get(String(x));if(!J){k(`claude-agent-process: permission response for unknown request ${x}`);return}if(this.pendingPermissions.delete(String(x)),J.cleanup?.(),Z==="allow")J.resolve({behavior:"allow",updatedInput:Q?.updatedInput??{}});else J.resolve({behavior:"deny",message:Q?.denyMessage??""})}sendToolResult(x,Z,Q=!1){k("claude-agent-process: sendToolResult ignored; Claude Agent SDK executes built-in tools internally")}sendHookResponse(x,Z={}){k("claude-agent-process: sendHookResponse ignored; hooks are handled by SDK callbacks")}isAlive(){return this.alive}async kill(x=5000){if(!this.alive)return;this.expectedExit=!0,this.input.close();try{this.query?.close()}catch{}let Z=Date.now();while(this.alive&&Date.now()-Z<x)await new Promise((Q)=>setTimeout(Q,100));if(this.alive)this.alive=!1,this.emit("exit",{code:null,signal:"SIGKILL",expected:this.expectedExit})}async listModels(){if(!this.started)this.sendInitialize();if(!this.query)throw Error("claude-agent-process: SDK query not initialized (sendInitialize failed or not called)");return(await this.query.supportedModels()).map(r$0)}async setModelSettings(x,Z){let Q=aU(x);if(!L3(Z))throw Error(`invalid Claude effort: ${String(Z)}`);if(!this.started)this.sendInitialize();if(!this.query)throw Error("claude-agent-process: SDK query not initialized (sendInitialize failed or not called)");if(Q)await this.query.setModel(Q);if(Z==="max")await this.query.applyFlagSettings({ultracode:!0,effortLevel:null});else await this.query.applyFlagSettings({effortLevel:Z,ultracode:null});this.opts.model=x,this.opts.effort=Z,this.lastModel=Q?c3(x):"claude:default",this.lastEffort=Z}async setModel(x){await this.setModelSettings(x,this.opts.effort)}async compactThread(){if(!this.input)throw Error("claude thread not initialized");return this.input.push({type:"user",session_id:this.sessionId??"",message:{role:"user",content:[{type:"text",text:"/compact"}]},parent_tool_use_id:null,priority:"now"}),new Promise((x,Z)=>{let Q=!1,J=()=>{this.off("context_compacted",X),this.off("assistant_text",z),this.off("exit",Y),this.off("error",G)},X=()=>{if(Q)return;Q=!0,J(),x()},z=(K)=>{if(Q)return;if((K?.text??"").includes("Not enough messages to compact"))Q=!0,J(),Z(new J2)},Y=()=>{if(Q)return;Q=!0,J(),Z(Error("claude thread exited during /compact"))},G=(K)=>{if(Q)return;Q=!0,J(),Z(K instanceof Error?K:Error(String(K)))};this.on("context_compacted",X),this.on("assistant_text",z),this.once("exit",Y),this.once("error",G)})}async injectThreadItems(x){if(!Array.isArray(x)||x.length===0)return;this.pendingInjectedContext.push(["Host-injected prior tool context for this continuation:",JSON.stringify(x,null,2)].join(`
232
232
  `))}canUseTool(x,Z,Q){if(x!=="AskUserQuestion")return Promise.resolve({behavior:"allow",updatedInput:Z});let J=YF0(Z);if(J.length===0)return k("claude-agent-process: AskUserQuestion 无有效 questions — allow 原样"),Promise.resolve({behavior:"allow",updatedInput:Z});let X={...Z,questions:J},z=`claude_perm_${++this.requestCounter}`,Y=Q.toolUseID||z,G={request_id:z,tool_name:"AskUserQuestion",input:X,tool_use_id:Y},K=new Promise((H)=>{let W=(N)=>{Q.signal.removeEventListener("abort",L),H(N)},L=()=>{if(!this.pendingPermissions.delete(z))return;W({behavior:"deny",message:"aborted"})};Q.signal.addEventListener("abort",L,{once:!0}),this.pendingPermissions.set(z,{kind:"dialog",resolve:W,request:G})});return this.emitToolUseOnce(Y,"AskUserQuestion",X,null),this.emit("can_use_tool",G),K}async readLoop(x){try{for await(let Z of x)this.handleMessage(Z);this.finishExit(null,null)}catch(Z){let Q=Z instanceof Error?Z:Error(String(Z));k(`claude-agent-process: read loop failed: ${Q.message}`),this.emit("error",Q),this.finishExit(1,null)}}finishExit(x,Z){if(!this.alive)return;this.alive=!1,this.turnActive=!1;for(let[Q,J]of this.pendingPermissions)J.cleanup?.(),J.resolve({behavior:"cancelled"}),this.pendingPermissions.delete(Q);k(`claude-agent-process: exited code=${x} signal=${Z} expected=${this.expectedExit}`),this.emit("exit",{code:x,signal:Z,expected:this.expectedExit})}handleMessage(x){let Z=x;if(typeof Z.session_id==="string"&&Z.session_id)this.sessionId=Z.session_id;switch(Z.type){case"system":this.handleSystemMessage(Z);return;case"assistant":this.handleAssistantMessage(Z);return;case"user":this.handleUserMessage(Z);return;case"result":this.handleResultMessage(Z);return;case"rate_limit_event":this.emit("rate_limits_updated",Z.rate_limit_info);return;case"stream_event":return;default:k(`claude-agent-process: unhandled message type=${Z.type??"unknown"} subtype=${Z.subtype??""}`)}}handleSystemMessage(x){switch(x.subtype){case"init":if(typeof x.model==="string"&&x.model)this.lastModel=c3(x.model);this.lastEffort=this.opts.effort,this.emit("init",{session_id:this.sessionId,raw:x});return;case"session_state_changed":if(x.state==="running"&&!this.turnActive)this.turnActive=!0,this.emit("turn_started",{turn_id:x.uuid,thread_id:this.sessionId});else if(x.state==="idle")this.turnActive=!1;return;case"compact_boundary":this.emit("context_compacted",{threadId:x.session_id??this.sessionId??void 0,sessionId:x.session_id??this.sessionId??void 0,itemId:x.uuid,phase:"event",sourceMethod:"claude_agent_sdk",sourceType:"compact_boundary",preTokens:x.compact_metadata?.pre_tokens,trigger:x.compact_metadata?.trigger});return;case"api_retry":k(`claude-agent-process: api retry attempt=${x.attempt}/${x.max_retries} status=${x.error_status} error=${x.error}`);return;case"permission_denied":k(`claude-agent-process: permission denied ${x.tool_name} ${x.tool_use_id}: ${x.message}`);return;case"task_started":this.emit("bg_task_started",{task_id:String(x.task_id??""),tool_use_id:typeof x.tool_use_id==="string"?x.tool_use_id:void 0,task_type:typeof x.task_type==="string"?x.task_type:void 0,description:String(x.description??""),subagent_type:typeof x.subagent_type==="string"?x.subagent_type:void 0,workflow_name:typeof x.workflow_name==="string"?x.workflow_name:void 0,prompt:typeof x.prompt==="string"?x.prompt:void 0});return;case"task_progress":this.emit("bg_task_progress",{task_id:String(x.task_id??""),description:typeof x.description==="string"?x.description:void 0,subagent_type:typeof x.subagent_type==="string"?x.subagent_type:void 0,usage:x.usage,last_tool_name:typeof x.last_tool_name==="string"?x.last_tool_name:void 0,summary:typeof x.summary==="string"?x.summary:void 0});return;case"task_updated":this.emit("bg_task_updated",{task_id:String(x.task_id??""),patch:x.patch&&typeof x.patch==="object"?x.patch:{}});return;case"task_notification":this.emit("bg_task_settled",{task_id:String(x.task_id??""),tool_use_id:typeof x.tool_use_id==="string"?x.tool_use_id:void 0,status:x.status==="completed"||x.status==="failed"||x.status==="stopped"?x.status:"completed",summary:typeof x.summary==="string"?x.summary:void 0,usage:x.usage});return;default:return}}handleAssistantMessage(x){let Z=x.message;if(typeof x.uuid==="string")this.lastAssistantUuid=x.uuid;if(typeof Z?.model==="string"&&Z.model)this.lastModel=c3(Z.model);let Q=Array.isArray(Z?.content)?Z.content:[];for(let J of Q){if(!J||typeof J!=="object")continue;if(J.type==="text"&&typeof J.text==="string"&&J.text.length>0){let X=s$0(J.text);if(X){if(this.pendingServerToolInputs.push(X),this.pendingServerToolInputs.length>20)this.pendingServerToolInputs.shift()}if(o$0(J.text))continue;let z=x.uuid??Z?.id;this.emit("assistant_text",{uuid:z,text:J.text}),this.emit("assistant_block_stop",{index:z})}else if(J.type==="tool_use"&&typeof J.id==="string"&&typeof J.name==="string")this.emitToolUseOnce(J.id,J.name,J.input??{},x.parent_tool_use_id??null);else if(J.type==="server_tool_use"&&typeof J.id==="string"&&typeof J.name==="string")this.emitToolUseOnce(J.id,p$0(J.name),{tool:J.name,input:this.serverToolInput(J.name,J.input??{})},x.parent_tool_use_id??null);else if(J.type==="tool_result"&&typeof J.tool_use_id==="string")this.emitToolResultOnce(J.tool_use_id,i$0(J.content),J.is_error===!0,x.parent_tool_use_id??null)}}emitToolUseOnce(x,Z,Q,J){if(this.emittedToolUseIds.has(x))return;this.emittedToolUseIds.add(x),this.emit("tool_use",{id:x,name:Z,input:Q,parentToolUseId:J})}serverToolInput(x,Z){let Q=SY(Z);if(Q&&typeof Q==="object"&&!Array.isArray(Q)&&Object.keys(Q).length>0)return Q;let J=this.pendingServerToolInputs.findIndex((X)=>X.name===x);if(J>=0){let[X]=this.pendingServerToolInputs.splice(J,1);return X.input}return Q}emitToolResultOnce(x,Z,Q,J){if(this.emittedToolResultIds.has(x))return;this.emittedToolResultIds.add(x),this.emit("tool_result",{tool_use_id:x,content:Z,is_error:Q,parentToolUseId:J})}handleUserMessage(x){let Z=Array.isArray(x.message?.content)?x.message.content:[];for(let Q of Z){if(!Q||typeof Q!=="object")continue;if(Q.type!=="tool_result"||typeof Q.tool_use_id!=="string")continue;let J=x.tool_use_result,X=Ju(Q.content),z=J&&typeof J==="object"?[typeof J.stdout==="string"?J.stdout:"",typeof J.stderr==="string"?J.stderr:""].filter(Boolean).join(`
233
233
  `):"",Y=X||z;this.emitToolResultOnce(Q.tool_use_id,Y,Q.is_error===!0||J?.interrupted===!0,x.parent_tool_use_id??null)}}handleResultMessage(x){if(typeof x.session_id==="string"&&x.session_id)this.sessionId=x.session_id;this.turnActive=!1;let Z=m$0(x.usage),Q=x.modelUsage??x.model_usage,J=n$0(Q);if(!Z&&!J.usage)k(`claude-agent-process: result usage missing rootKeys=${xL(x).join(",")||"-"} usageKeys=${xL(x.usage).join(",")||"-"} modelUsageKeys=${xL(Q).join(",")||"-"}`);if(this.lastUsage=Z,J.usage)this.cumulativeUsageFromResults=Yz(J.usage),this.lastTotalUsage=Yz(J.usage);else if(Z)this.cumulativeUsageFromResults=d$0(this.cumulativeUsageFromResults,Z),this.lastTotalUsage=Yz(this.cumulativeUsageFromResults);else this.lastTotalUsage=this.cumulativeUsageFromResults?Yz(this.cumulativeUsageFromResults):null;if(J.contextWindow!=null){let G=ew(this.opts.model),K=RY.get(G)??0;if(J.contextWindow>K)RY.set(G,J.contextWindow),k(`claude-agent-process: SDK contextWindow ${J.contextWindow} (global max for ${G}, prev ${K||"-"})`);else if(J.contextWindow<(RY.get(G)??0))k(`claude-agent-process: SDK contextWindow ${J.contextWindow} ignored (global max ${RY.get(G)} locked for ${G})`)}this.lastContextWindow=RY.get(ew(this.opts.model))??J.contextWindow??null;let X=_$0(this.opts.workDir,this.sessionId??""),z=l$0(X);if(this.lastContextTokens=tw(z)??(Zz(X)?tw(Z):null),this.lastTotalUsage||this.lastUsage)this.emit("token_usage",{usage:this.lastUsage,totalUsage:this.lastTotalUsage,contextWindow:this.lastContextWindow,threadId:this.sessionId??void 0,turnId:x.uuid});let Y=typeof x.subtype==="string"?x.subtype:x.is_error?"error":"success";this.lastResult={cost_usd:null,cost_delta_usd:null,duration_ms:typeof x.duration_ms==="number"?x.duration_ms:null,num_turns:typeof x.num_turns==="number"?x.num_turns:1,usage:this.lastUsage,subtype:Y,is_error:x.is_error===!0||Y!=="success"},this.emit("result",{subtype:Y,is_error:this.lastResult.is_error,duration_ms:this.lastResult.duration_ms,usage:this.lastUsage})}failTurnStart(x){this.turnActive=!1,this.lastResult={cost_usd:null,cost_delta_usd:null,duration_ms:null,num_turns:null,usage:this.lastUsage,subtype:"claude_turn_start_failed",is_error:!0},this.emit("result",{subtype:this.lastResult.subtype,is_error:!0,duration_ms:null,usage:this.lastUsage,error:x.message})}}});import{randomUUID as JF0}from"node:crypto";async function Xu(x){let Z=await H8("feishu chat.get",()=>K1.im.chat.get({path:{chat_id:x},params:{user_id_type:"open_id"}}));if(Z.code&&Z.code!==0)W8("feishu chat.get",Z);let Q=Z.data?.owner_id;if(!Q)throw Error("feishu chat.get returned no owner_id; cannot add project group owner to tasklist");return Q}async function zu(x,Z){let Q=await H8("feishu tasklist.create",()=>K1.task.v2.tasklist.create({params:{user_id_type:"open_id"},data:{name:x,members:[{id:Z,type:"user",role:"editor"}]}}));if(Q.code&&Q.code!==0)W8("feishu tasklist.create",Q);let J=Q.data?.tasklist,X=J?.guid;if(!X)throw Error("feishu tasklist.create returned no guid");return{guid:X,name:J?.name||x,url:J?.url??"",createdAt:J?.created_at}}async function Wu(x){let Z=await H8("feishu tasklist.delete",()=>K1.task.v2.tasklist.delete({path:{tasklist_guid:x}}));if(Z.code&&Z.code!==0)W8("feishu tasklist.delete",Z)}async function Qz(x){let Z=[],Q;do{let J=await H8("feishu section.list",()=>K1.task.v2.section.list({params:{resource_type:"tasklist",resource_id:x,page_size:50,...Q?{page_token:Q}:{}}}));if(J.code&&J.code!==0)W8("feishu section.list",J);for(let X of J.data?.items??[]){if(!X.guid||!X.name)continue;Z.push({guid:X.guid,name:X.name,isDefault:X.is_default})}Q=J.data?.has_more?J.data?.page_token:void 0}while(Q);return Z}async function Ku(x){let Z=await H8("feishu section.create",()=>K1.task.v2.section.create({data:{resource_type:"tasklist",resource_id:x.tasklistGuid,name:x.name,...x.insertAfter?{insert_after:x.insertAfter}:{}}}));if(Z.code&&Z.code!==0)W8("feishu section.create",Z);let Q=Z.data?.section?.guid;if(!Q)throw Error(`feishu section.create returned no guid for "${x.name}"`);return Q}async function zL(x){let Z=await H8("feishu section.get",()=>K1.task.v2.section.get({path:{section_guid:x},params:{user_id_type:"open_id"}}));if(Z.code&&Z.code!==0)W8("feishu section.get",Z);let Q=Z.data?.section;if(!Q?.guid)throw Error(`feishu section.get returned no section: ${x}`);return{guid:Q.guid,name:Q.name??"",isDefault:Q.is_default,tasklistGuid:Q.tasklist?.guid}}async function Vu(x,Z){let Q=await H8("feishu section.patch",()=>K1.task.v2.section.patch({path:{section_guid:x},params:{user_id_type:"open_id"},data:{section:{name:Z},update_fields:["name"]}}));if(Q.code&&Q.code!==0)W8("feishu section.patch",Q);let J=Q.data?.section;if(!J?.guid)throw Error(`feishu section.patch returned no section: ${x}`);return{guid:J.guid,name:J.name??"",isDefault:J.is_default,tasklistGuid:J.tasklist?.guid}}async function Hu(x){let Z=await H8("feishu section.delete",()=>K1.task.v2.section.delete({path:{section_guid:x}}));if(Z.code&&Z.code!==0)W8("feishu section.delete",Z)}async function q3(x,Z){let Q=[],J;do{let X=await H8("feishu section.tasks",()=>K1.task.v2.section.tasks({path:{section_guid:x},params:{page_size:50,...typeof Z==="boolean"?{completed:Z}:{},...J?{page_token:J}:{}}}));if(X.code&&X.code!==0)W8("feishu section.tasks",X);for(let z of X.data?.items??[]){if(!z.guid)continue;Q.push({guid:z.guid,summary:z.summary??"",completedAt:z.completed_at,subtaskCount:z.subtask_count})}J=X.data?.has_more?X.data?.page_token:void 0}while(J);return Q}async function Gu(x,Z){let Q=[],J;do{let X=await H8("feishu tasklist.tasks",()=>K1.task.v2.tasklist.tasks({path:{tasklist_guid:x},params:{page_size:50,user_id_type:"open_id",...typeof Z==="boolean"?{completed:Z}:{},...J?{page_token:J}:{}}}));if(X.code&&X.code!==0)W8("feishu tasklist.tasks",X);for(let z of X.data?.items??[]){if(!z.guid)continue;Q.push({guid:z.guid,summary:z.summary??"",completedAt:z.completed_at,subtaskCount:z.subtask_count})}J=X.data?.has_more?X.data?.page_token:void 0}while(J);return Q}async function $u(x){let Z=`lodestar-default-section-discovery-${Date.now()}-${JF0().slice(0,8)}`,Q="";try{let J=await H8("feishu task.create",()=>K1.task.v2.task.create({params:{user_id_type:"open_id"},data:{summary:Z,tasklists:[{tasklist_guid:x}]}}));if(J.code&&J.code!==0)W8("feishu task.create",J);let X=J.data?.task;if(Q=X?.guid??"",!Q)throw Error("feishu task.create returned no guid during default section discovery");let z=X.tasklists?.find((K)=>K.tasklist_guid===x)?.section_guid;if(z)return z;let G=(await Jz(Q)).tasklists?.find((K)=>K.tasklist_guid===x)?.section_guid;if(!G)throw Error(`failed to discover default section guid for tasklist ${x}`);return G}finally{if(Q)await XF0(Q)}}async function XF0(x){let Z=await H8("feishu task.delete",()=>K1.task.v2.task.delete({path:{task_guid:x}}));if(Z.code&&Z.code!==0)W8("feishu task.delete",Z)}async function Jz(x){let Z=await H8("feishu task.get",()=>K1.task.v2.task.get({path:{task_guid:x},params:{user_id_type:"open_id"}}));if(Z.code&&Z.code!==0)W8("feishu task.get",Z);let Q=Z.data?.task;if(!Q)throw Error(`feishu task.get returned no task: ${x}`);return Q}async function Fu(x){let Z=[],Q;do{let J=await H8("feishu comment.list",()=>K1.task.v2.comment.list({params:{resource_type:"task",resource_id:x,direction:"asc",page_size:50,user_id_type:"open_id",...Q?{page_token:Q}:{}}}));if(J.code&&J.code!==0)W8("feishu comment.list",J);for(let X of J.data?.items??[]){if(!X.id)continue;Z.push({id:X.id,content:X.content??"",createdAt:X.created_at,updatedAt:X.updated_at,creator:X.creator})}Q=J.data?.has_more?J.data?.page_token:void 0}while(Q);return Z}async function Ex(x,Z){let Q=await H8("feishu comment.create",()=>K1.task.v2.comment.create({data:{resource_type:"task",resource_id:x,content:Z},params:{user_id_type:"open_id"}}));if(Q.code&&Q.code!==0)W8("feishu comment.create",Q);let J=Q.data?.comment?.id;if(!J)throw Error(`feishu comment.create returned no id for task ${x}`);return J}async function Xz(x,Z,Q){let J=await H8("feishu task.addTasklist",()=>K1.task.v2.task.addTasklist({path:{task_guid:x},data:{tasklist_guid:Z,section_guid:Q},params:{user_id_type:"open_id"}}));if(J.code&&J.code!==0)W8("feishu task.addTasklist",J)}function zF0(x,Z){let Q=Z&&typeof Z==="object"?Z:{},J=Q.response?.data&&typeof Q.response.data==="object"?Q.response.data:Q.data&&typeof Q.data==="object"?Q.data:Q,X=J.code??Q.code,z=J.msg??J.message??Q.msg??Q.message??"unknown error",Y=J.error?.permission_violations??J.permission_violations??Q.error?.permission_violations,G=Array.isArray(Y)?Y.map((K)=>K?.scope??K?.subject??K?.name??K).filter(Boolean).join(", "):"";return`${x} failed code=${X??"unknown"} msg=${z}${G?` missing_scopes=${G}`:""}`}async function H8(x,Z){try{return await Z()}catch(Q){W8(x,Q)}}function W8(x,Z){throw Error(zF0(x,Z))}var Uu=R0(()=>{h1()});import{execSync as vu}from"node:child_process";import{randomUUID as Mu}from"node:crypto";import{existsSync as WF0,mkdirSync as kx,readFileSync as b5,statSync as Ru,writeFileSync as gx}from"node:fs";import{readFile as Cu}from"node:fs/promises";import{homedir as Lu}from"node:os";import{basename as bx,extname as KF0,join as WL}from"node:path";function VL(x){return Z1.projects[x]}async function E3(){if(zz&&Date.now()<Nu)return zz;let Z=await(await fetch("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({app_id:Su,app_secret:ju})})).json();if(!Z.tenant_access_token)throw Error("feishu: failed to obtain tenant token");return zz=Z.tenant_access_token,Nu=Date.now()+((Z.expire??7200)-60)*1000,zz}function Iu(){try{let x=JSON.parse(b5(jW,"utf8"));for(let[Z,Q]of Object.entries(x))if(typeof Q==="string")s8.set(Z,Q);k(`feishu: loaded ${s8.size} session→chat bindings`)}catch{}}function Tu(){try{let x={};for(let[Z,Q]of s8)x[Z]=Q;kx(C1,{recursive:!0}),gx(jW,JSON.stringify(x,null,2))}catch(x){k(`feishu: save session-chat-map failed: ${x}`)}}function Kz(x,Z){if(s8.get(x)===Z)return;let Q=s8.get(x);s8.set(x,Z),Tu(),k(`feishu: bound session "${x}" → ${Z}${Q?` (was ${Q})`:""}`)}function KL(x){let Z=s8.get(x);if(!Z)return;s8.delete(x),Tu(),k(`feishu: unbound session "${x}" from ${Z}`)}function Wz(x,Z,Q){let J=E5.get(x)??{};J[Z]=Q,E5.set(x,J)}function cu(){try{let x=JSON.parse(b5(IW,"utf8"));for(let[Z,Q]of Object.entries(x)){if(typeof Q==="string"&&Q.trim()){Wz(Z,"codex",Q);continue}if(!Q||typeof Q!=="object")continue;let J=Q,X=J.provider==="claude"||J.provider==="codex"?J.provider:null,z=typeof J.sessionId==="string"?J.sessionId:typeof J.session_id==="string"?J.session_id:null;if(X&&z?.trim()){Wz(Z,X,z);continue}for(let Y of["codex","claude"]){let G=J[Y];if(typeof G==="string"&&G.trim())Wz(Z,Y,G)}}k(`feishu: loaded ${E5.size} session→resume bindings`)}catch{}}function VF0(){try{let x={};for(let[Z,Q]of E5)x[Z]={...Q};kx(C1,{recursive:!0}),gx(IW,JSON.stringify(x,null,2))}catch(x){k(`feishu: save session-resume-map failed: ${x}`)}}function qu(x,Z,Q="codex"){if(E5.get(x)?.[Q]===Z)return;Wz(x,Q,Z),VF0()}function HL(x,Z="codex"){return E5.get(x)?.[Z]??null}function Au(){try{let x=JSON.parse(b5(TW,"utf8")),Z=0;for(let[Q,J]of Object.entries(x)){if(!Array.isArray(J))continue;let X=J.filter((z)=>z&&typeof z.uuid==="string"&&typeof z.ts==="number").map((z)=>({uuid:String(z.uuid),sid:String(z.sid??""),preview:String(z.preview??""),ts:Number(z.ts),writes:Array.isArray(z.writes)?z.writes.filter((Y)=>Y&&typeof Y.path==="string").map((Y)=>({tool:String(Y.tool??"Write"),path:String(Y.path),body:String(Y.body??"")})).filter((Y)=>Y.path!==""||Y.body!==""):[]}));if(X.length)w4.set(Q,X),Z+=X.length}k(`feishu: loaded ${Z} turn anchors across ${w4.size} sessions`)}catch(x){if(x?.code!=="ENOENT")k(`feishu: load session-turns-map failed: ${x?.message??x}`)}}function GL(){try{let x={};for(let[Z,Q]of w4)x[Z]=Q;kx(C1,{recursive:!0}),gx(TW,JSON.stringify(x,null,2))}catch(x){k(`feishu: save session-turns-map failed: ${x}`)}}function Eu(x,Z){let Q=w4.get(x)??[];if(Q.push(Z),Q.length>Du)Q.splice(0,Q.length-Du);w4.set(x,Q),GL()}function IY(x){return w4.get(x)??[]}function bu(x,Z){let Q=w4.get(x);if(!Q||Q.length<=Z)return;w4.set(x,Q.slice(0,Z)),GL()}function wx(x){if(!w4.has(x))return;w4.delete(x),GL()}function ux(x){return Ou.test(x)?x.replace(Ou,""):null}function $L(x){let Z=new Date,Q=(Y)=>String(Y).padStart(2,"0"),J=`${Q(Z.getMonth()+1)}${Q(Z.getDate())}-${Q(Z.getHours())}${Q(Z.getMinutes())}`,X=new Set([...M8.values(),...w4.keys()]),z=`${x}*${J}`;for(let Y=2;X.has(z);Y++)z=`${x}*${J}-${Y}`;return z}function ku(){try{let x=JSON.parse(b5(cW,"utf8"));for(let[Z,Q]of Object.entries(x)){if(typeof Q==="string"&&Q.trim()){A3.set(Z,{provider:d7(Q),model:Q,effort:null});continue}if(!Q||typeof Q!=="object")continue;let{model:J,provider:X}=Q,z=X==="claude"||X==="codex"?X:typeof J==="string"&&J.trim()?d7(J):"claude",Y=typeof J==="string"&&J.trim()?J:null;if(z==="claude"&&!Y)continue;let{effort:G,tokenSourceId:K}=Q,H=typeof K==="string"&&K.trim()?K.trim():null,W=z==="claude"?L3(G)?G:null:Y6(G)?G:null;A3.set(Z,{provider:z,model:Y,effort:W,...H?{tokenSourceId:H}:{}})}k(`feishu: loaded ${A3.size} session→model bindings`)}catch{}}function HF0(){try{let x={};for(let[Z,Q]of A3)x[Z]=Q;kx(C1,{recursive:!0}),gx(cW,JSON.stringify(x,null,2))}catch(x){k(`feishu: save session-model-map failed: ${x}`)}}function gu(x,Z,Q,J,X){let z=A3.get(x);if(z?.provider===Z&&z.model===Q&&z.effort===J&&(z.tokenSourceId??null)===(X??null))return;A3.set(x,{provider:Z,model:Q,effort:J,...X?{tokenSourceId:X}:{}}),HF0()}function Pu(x){let Z=A3.get(x);if(Z)return Z;let Q=ux(x);return Q?A3.get(Q)??null:null}function FL(x){try{gx(UZ,JSON.stringify(x,null,2))}catch(Z){k(`feishu: write alive marker failed: ${Z}`)}}function wu(){if(!WF0(UZ))return[];try{let x=b5(UZ,"utf8"),Z=JSON.parse(x);return Array.isArray(Z)?Z.filter((Q)=>typeof Q==="string"):[]}catch(x){return k(`feishu: read alive marker failed: ${x}`),[]}}function TY(x){let Z=s8.get(x);if(Z){let J=M8.get(Z);if(J&&J!==x)k(`feishu: chatIdForSession("${x}"): persisted binding ${Z} has cached name "${J}", using persisted binding`);return Z}let Q=[];for(let[J,X]of M8)if(X===x)Q.push(J);if(Q.length===1)return Q[0];if(Q.length>1)k(`feishu: chatIdForSession("${x}"): ${Q.length} candidates with no binding — [${Q.join(", ")}]`);return null}async function Vz(){try{let x;do{let Z=await K1.im.chat.list({params:{page_size:100,...x?{page_token:x}:{}}});for(let Q of Z.data?.items??[])if(Q.chat_id&&Q.name)M8.set(Q.chat_id,Q.name);x=Z.data?.page_token}while(x);k(`feishu: refreshed chat list — ${M8.size} groups`)}catch(x){k(`feishu: refresh chat list failed: ${x}`)}}async function UL(){let x=new Map,Z;do{let Q=await K1.im.chat.list({params:{page_size:100,...Z?{page_token:Z}:{}}});if(Q.code&&Q.code!==0)throw Error(`feishu chat.list failed code=${Q.code} msg=${Q.msg}`);for(let J of Q.data?.items??[]){if(!J.chat_id||!J.name)continue;if(J.chat_status&&J.chat_status!=="normal")continue;M8.set(J.chat_id,J.name);let X=x.get(J.name)??[];X.push(J.chat_id),x.set(J.name,X)}Z=Q.data?.page_token}while(Z);return x}async function uu(x){let Z=s8.get(x);if(Z&&M8.get(Z)===x){let z=await $F0(Z);if(z.name===x&&FF0(z.status))return Z;M8.delete(Z),KL(x)}let J=(await UL()).get(x)??[];if(J.length===0)return null;let X=s8.get(x);if(X&&J.includes(X))return X;if(J.length===1)return J[0];throw Error(`multiple Feishu groups named "${x}": ${J.join(", ")}`)}async function Hz(x,Z){if(!Z)throw Error("missing sender open_id; cannot add user to worktree group");let Q=await uu(x);if(Q){let z=await GF0(Q,Z);return Kz(x,Q),{chatId:Q,created:!1,joined:z}}let J=await K1.im.chat.create({params:{user_id_type:"open_id",uuid:Mu()},data:{name:x,user_id_list:[Z],group_message_type:"chat"}});if(J.code&&J.code!==0)throw Error(`feishu chat.create failed code=${J.code} msg=${J.msg}`);let X=J.data?.chat_id;if(!X)throw Error("feishu chat.create returned no chat_id");return M8.set(X,x),Kz(x,X),{chatId:X,created:!0,joined:!0}}async function cY(x){let Z=await uu(x);if(!Z)return KL(x),{chatId:null,disbanded:!1};let Q=await K1.im.chat.delete({path:{chat_id:Z}});if(Q.code&&Q.code!==0)throw Error(`feishu chat.delete failed code=${Q.code} msg=${Q.msg}`);return M8.delete(Z),KL(x),{chatId:Z,disbanded:!0}}async function GF0(x,Z){let Q;do{let X=await K1.im.chatMembers.get({path:{chat_id:x},params:{member_id_type:"open_id",page_size:100,...Q?{page_token:Q}:{}}});if(X.code&&X.code!==0)throw Error(`feishu chatMembers.get failed code=${X.code} msg=${X.msg}`);for(let z of X.data?.items??[])if(z.member_id===Z)return!1;Q=X.data?.page_token}while(Q);let J=await K1.im.chatMembers.create({path:{chat_id:x},params:{member_id_type:"open_id"},data:{id_list:[Z]}});if(J.code&&J.code!==0)throw Error(`feishu chatMembers.create failed code=${J.code} msg=${J.msg}`);return!0}async function fu(x){try{let Z=await E3(),J=await(await fetch(`https://open.feishu.cn/open-apis/im/v1/chats/${encodeURIComponent(x)}`,{headers:{Authorization:`Bearer ${Z}`}})).json();if(J?.code!==0)return k(`feishu: fetchChatName ${x} code=${J?.code} msg=${J?.msg}`),null;let X=J.data?.name;if(typeof X==="string"&&X)return M8.set(x,X),k(`feishu: fetchChatName ${x} → "${X}" (point lookup)`),X;return k(`feishu: fetchChatName ${x} — chat has no name (unnamed group?)`),null}catch(Z){return k(`feishu: fetchChatName ${x} failed: ${Z}`),null}}async function yu(x,Z,Q,J){let X=Mu(),z=null;for(let Y=0;Y<jY.length;Y++){if(jY[Y]>0)await new Promise((G)=>setTimeout(G,jY[Y]));try{let G=await K1.im.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:Z,msg_type:Q,content:J,uuid:X}});if(G?.code&&G.code!==0)return k(`feishu: send${x==="text"?"Text":"Card"} rejected chat=${Z} code=${G.code} msg=${G.msg}`),null;return G?.data?.message_id??null}catch(G){z=G,k(`feishu: send${x==="text"?"Text":"Card"} attempt ${Y+1}/${jY.length} chat=${Z} failed: ${G}`)}}return k(`feishu: send${x==="text"?"Text":"Card"} chat=${Z} EXHAUSTED ${jY.length} retries: ${z}`),null}async function $F0(x){let Z=await K1.im.chat.get({path:{chat_id:x}});if(Z.code&&Z.code!==0)throw Error(`feishu chat.get failed code=${Z.code} msg=${Z.msg}`);return{name:Z.data?.name??null,status:Z.data?.chat_status??null}}function FF0(x){return x===null||x==="normal"}async function W0(x,Z){return yu("text",x,"text",JSON.stringify({text:Z}))}async function M1(x,Z){return yu("card",x,"interactive",JSON.stringify(Z))}async function fx(x,Z){let Q=await K1.im.v1.message.patch({path:{message_id:x},data:{content:JSON.stringify(Z)}});if(Q?.code&&Q.code!==0)throw Error(`feishu message.patch failed code=${Q.code} msg=${Q.msg}`)}async function R1(x,Z){try{let Q=await E3(),X=await(await fetch("https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=chat_id",{method:"POST",headers:{Authorization:`Bearer ${Q}`,"Content-Type":"application/json"},body:JSON.stringify({receive_id:x,msg_type:"text",content:JSON.stringify({text:Z})})})).json();if(X?.code!==0)return k(`feishu: sendTextRaw rejected chat=${x} code=${X?.code} msg=${X?.msg}`),null;return X.data?.message_id??null}catch(Q){return k(`feishu: sendTextRaw chat=${x} failed: ${Q}`),null}}async function k8(x,Z){if(!x)return null;try{return(await K1.im.messageReaction.create({path:{message_id:x},data:{reaction_type:{emoji_type:Z}}}))?.data?.reaction_id??null}catch(Q){return k(`feishu: addReaction ${Z} on ${x} failed: ${Q}`),null}}async function u4(x,Z){if(!x||!Z)return;try{await K1.im.messageReaction.delete({path:{message_id:x,reaction_id:Z}})}catch(Q){k(`feishu: deleteReaction ${Z} on ${x} failed: ${Q}`)}}async function k5(x,Z){if(!x){k("feishu: urgentApp skip — missing messageId");return}if(Z.length===0){k(`feishu: urgentApp skip — empty openIds (msg=${x})`);return}let Q=await E3(),J=`https://open.feishu.cn/open-apis/im/v1/messages/${x}/urgent_app?user_id_type=open_id`;try{let z=await(await fetch(J,{method:"PATCH",headers:{Authorization:`Bearer ${Q}`,"Content-Type":"application/json"},body:JSON.stringify({user_id_list:Z})})).json();if(z?.code!==0){k(`feishu: urgentApp ${x} code=${z?.code} msg=${z?.msg}`);return}let Y=z.data?.invalid_user_id_list??[],G=Z.length-Y.length;k(`feishu: urgentApp ${x} ok — delivered=${G}${Y.length?` invalid=${Y.length}`:""}`)}catch(X){k(`feishu: urgentApp ${x} failed: ${X}`)}}async function qY(x,Z,Q,J){try{let X=await E3(),z=`https://open.feishu.cn/open-apis/im/v1/messages/${x}/resources/${Z}?type=${Q}`,Y=await fetch(z,{headers:{Authorization:`Bearer ${X}`}});if(!Y.ok){k(`feishu: download ${Q} HTTP ${Y.status}: ${(await Y.text()).slice(0,200)}`);return}let G=Buffer.from(await Y.arrayBuffer());kx(qW,{recursive:!0});let K=J?J.replace(/[^a-zA-Z0-9._-]/g,"_"):`${Z.replace(/[^a-zA-Z0-9_-]/g,"_")}.png`,H=WL(qW,`${Date.now()}-${K}`);return gx(H,G),k(`feishu: downloaded ${Q} ${H} (${G.length}B)`),H}catch(X){k(`feishu: download ${Q} failed: ${X instanceof Error?X.message:X}`);return}}function LF0(x){return UF0.has(KF0(x).toLowerCase())}async function mu(x){let Z=await E3(),Q=new Blob([await Cu(x)]),J=new FormData;J.append("image_type","message"),J.append("image",Q,bx(x));let z=await(await fetch("https://open.feishu.cn/open-apis/im/v1/images",{method:"POST",headers:{Authorization:`Bearer ${Z}`},body:J})).json();if(z?.code!==0)return k(`feishu: uploadImage ${x} code=${z.code} msg=${z.msg}`),null;return z.data?.image_key??null}async function _u(x){try{let Z=Ru(x);if(!Z.isFile())return k(`feishu: uploadImageKey not a file — ${x}`),null;if(Z.size>hu)return k(`feishu: uploadImageKey oversize — ${x} (${(Z.size/1024/1024).toFixed(1)} MB)`),null}catch(Z){return k(`feishu: uploadImageKey stat failed — ${x}: ${Z}`),null}return mu(x)}async function NF0(x){let Z=await E3(),Q=new Blob([await Cu(x)]),J=new FormData;J.append("file_type","stream"),J.append("file_name",bx(x)),J.append("file",Q,bx(x));let z=await(await fetch("https://open.feishu.cn/open-apis/im/v1/files",{method:"POST",headers:{Authorization:`Bearer ${Z}`},body:J})).json();if(z?.code!==0)return k(`feishu: uploadFile ${x} code=${z.code} msg=${z.msg}`),null;return z.data?.file_key??null}async function DF0(x,Z){try{let Q=await K1.im.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:x,msg_type:"image",content:JSON.stringify({image_key:Z})}});if(Q?.code&&Q.code!==0)return k(`feishu: sendImage rejected chat=${x} code=${Q.code} msg=${Q.msg}`),null;return Q?.data?.message_id??null}catch(Q){return k(`feishu: sendImage failed chat=${x}: ${Q}`),null}}async function OF0(x,Z){try{let Q=await K1.im.message.create({params:{receive_id_type:"chat_id"},data:{receive_id:x,msg_type:"file",content:JSON.stringify({file_key:Z})}});if(Q?.code&&Q.code!==0)return k(`feishu: sendFile rejected chat=${x} code=${Q.code} msg=${Q.msg}`),null;return Q?.data?.message_id??null}catch(Q){return k(`feishu: sendFile failed chat=${x}: ${Q}`),null}}async function lu(x,Z){try{let J=Ru(Z);if(!J.isFile())return await W0(x,`❌ 出站文件: 路径不是文件 — ${Z}`),!1;if(J.size>hu)return await W0(x,`❌ 出站文件: ${bx(Z)} 超过 30 MB (${(J.size/1024/1024).toFixed(1)} MB)`),!1}catch(J){return await W0(x,`❌ 出站文件: 无法读取 ${Z} (${J})`),!1}let Q=LF0(Z);try{if(Q){let J=await mu(Z);if(!J)return await W0(x,`❌ 出站图片上传失败: ${bx(Z)}`),!1;return await DF0(x,J)!=null}else{let J=await NF0(Z);if(!J)return await W0(x,`❌ 出站文件上传失败: ${bx(Z)}`),!1;return await OF0(x,J)!=null}}catch(J){return k(`feishu: uploadAndSend ${Z} failed: ${J}`),await W0(x,`❌ 出站文件异常: ${bx(Z)} — ${J}`),!1}}function nu(x){kx(x,{recursive:!0}),k(`feishu: provisioned ${x}`);let Z=WL(Lu(),".codex","config.toml");try{kx(WL(Lu(),".codex"),{recursive:!0});let Q="";try{Q=b5(Z,"utf8")}catch{}let J=`[projects.${JSON.stringify(x)}]`;if(!Q.includes(J)){let X=Q.trimEnd();Q=`${X}${X?`
234
234
 
@@ -340,7 +340,7 @@ deepseek-setup <api_key>
340
340
  `)?"":`
341
341
  `;LN0(L8,J+X+`
342
342
  `+ON0(x,Z)+`
343
- `),BN0()}var jh=R0(()=>{n4();m8();RN();c4()});var CN={};GZ(CN,{runTokenSourceSetup:()=>MN0,onTokenSourceEnable:()=>vN0});async function vN0(x,Z){let Q=J4(Z);if(!Q){await W0(x.chatId,`❌ 未知 token source: ${Z}`);return}if(Q.enabled){await W0(x.chatId,`${Q.display} 已启用,发 \`model\` 选择。`);return}let J=p7().find((X)=>X.setup?.commandSuffix===Z||X.configSectionId===Z);if(J?.setup)await W0(x.chatId,J.setup.hint(Q.display));else if(Q.kind==="codex-subscription")await W0(x.chatId,`${Q.display} 需要 ChatGPT 登录:在服务器执行 \`codex login\`,完成后重启 daemon(\`systemctl --user restart feishu-daemon\`)或重发 \`model\` 刷新。`);else if(Q.kind==="claude-native")await W0(x.chatId,`${Q.display} 直接使用本机 Claude Code 配置,无需单独启用。`)}async function MN0(x,Z,Q){let J=p7().find((z)=>z.setup?.commandSuffix===Z);if(!J?.setup||!J.configSectionId){await W0(x.chatId,`❌ 未知或不可配置的 source: ${Z}`);return}let X=J.setup.parseArgs(Q);if("error"in X){await W0(x.chatId,X.error);return}try{Sh(J.configSectionId,X.config),await K2();let z=J4(J.configSectionId);await W0(x.chatId,`✅ ${z?.display??Z} 已启用。发 \`model\` 重新选择。`)}catch(z){await W0(x.chatId,`❌ 启用失败: ${z?.message??z}`)}}var SN=R0(()=>{h1();c4();jh()});async function Ih(x,Z,Q=""){let J=Z.trim().match(/^(?:wt|worktree)(?:\s+(.+))?$/i);if(J){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 wt。"),!0;return await x.runWorktreeCommand((J[1]??"").trim(),Q),!0}if(Z.trim().match(/^btw$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 btw。"),!0;return await x.runBtwCommand(Q),!0}if(Z.trim().match(/^bye$/i))return await x.runByeCommand(),!0;if(Z.trim().match(/^(?:fk|fork)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 fk。"),!0;return await x.showForkList(),!0}if(Z.trim().match(/^(?:bk|back)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 bk。"),!0;if(x.isRunning())await x.stop("回滚前终止",{announce:!1});return await x.showBackList(),!0}let X=Z.trim().match(/^agy(?:\s+([\s\S]+))?$/i);if(X)return await x.runAgyCommand((X[1]??"").trim()),!0;if(Z.trim().toLowerCase()==="task")return await x.showTasklistPanel(),!0;let z=Z.trim().match(/^(\w+)-setup\s+([\s\S]+)$/i);if(z){let{runTokenSourceSetup:G}=await Promise.resolve().then(() => (SN(),CN));return await G(x,z[1].trim(),z[2].trim()),!0}let Y=RN0.get(Z.trim().toLowerCase());if(!Y)return!1;if((x.startingAgy||x.runningAgy)&&!["stop","kill","restart","hi","model"].includes(Y))return await W0(x.chatId,`⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再执行 ${Y}。`),!0;switch(Y){case"model":return await x.showModelPanel(),!0;case"compact":return await x.runCompactCommand(),!0;case"hi":{let K=!x.isRunning(),H=x.backendLabel(),W=K?await x.openStatusCard("hi",x.withModel(`\uD83D\uDE80 启动 ${H}`)):null,L=x.withModel(`\uD83D\uDE80 启动 ${H}`);if(!(K?await x.start({announce:!W,onStatus:(D)=>{L=D,x.setStatusCard(W,D)}}):!0))return await x.closeStatusCard(W,L.startsWith("❌")?L:"❌ 启动失败"),!0;if(W)return await x.replaceStatusCardWithConsole(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`))),!0;if(K)await x.closeStatusCard(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`)))}return await x.showConsole(),!0;case"stop":if(x.runningAgy)return await x.stopAgyTask("\uD83D\uDED1 agy 已打断"),!0;if(x.clearStaleIdleQueueState("stop"),x.clearMultiMsgBuffer("stop command"),!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0){let K=await x.openStatusCard("stop","⚪ 当前没有正在执行的 turn","grey");if(K)await x.closeStatusCard(K,"⚪ 无正在执行的 turn");else await W0(x.chatId,"⚪ 当前没有正在执行的 turn");return!0}k(`session "${x.sessionName}": stop command — interrupt + drop count=${x.pendingUserMessageCount} midBuffer=${x.pendingMidTurnMsgs.length}`);let G=new Set;for(let[K,H]of[...x.pendingReactionIds.entries(),...x.currentBatchReactionIds.entries()]){if(H)u4(K,H);k8(K,"CrossMark"),G.add(K)}for(let K of x.pendingMidTurnMsgs)if(K.msgId&&!G.has(K.msgId))k8(K.msgId,"CrossMark");return x.pendingUserMessageCount=0,x.pendingMidTurnMsgs=[],x.pendingTurnInputs=[],x.lastUserOpenId="",x.pendingReactionIds=new Map,x.currentBatchReactionIds=new Map,x.userInterrupted=!0,x.interrupt(),await x.closeTurnCard("\uD83D\uDED1 打断"),!0;case"kill":{if(x.runningAgy)await x.stopAgyTask("\uD83D\uDED1 agy 已终止");let K=x.isRunning(),H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=K?`\uD83D\uDED1 停止 ${H}`:"⚪ session 当前未运行",L=await x.openStatusCard("kill",W,K?"red":"grey");await x.stop("已终止",{announce:!L,onStatus:(N)=>{x.setStatusCard(L,N)}}),await x.closeStatusCard(L,K?`✅ ${H} 已终止`:`⚪ ${H} 未运行`)}return!0;case"restart":if(!x.isRunning()&&x.selectedProvider!=="codex")return await x.showResumeList(),!0;{let K=x.lastSessionId?x.lastSessionId.slice(0,8):"",H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=x.isRunning()?x.withModel(`\uD83D\uDD01 重启 ${H}`):K?x.withModel(`\uD83D\uDD01 恢复上一会话 thread=${K}…`):x.withModel(`\uD83D\uDD01 启动 ${H}`),L=await x.openStatusCard("restart",W);if(x.runningAgy)x.setStatusCard(L,"\uD83D\uDED1 restart 前终止 agy"),await x.stopAgyTask("\uD83D\uDED1 restart 前已终止 agy");let N=W,D=await x.restart(!0,{announce:!L,onStatus:(v)=>{N=v,x.setStatusCard(L,v)}}),O=D?N.startsWith("✅")?N:x.withWorktreeInstructionNotice(K?"✅ 已恢复上一会话":`✅ ${x.backendLabel()} 已就绪`):N.startsWith("❌")?N:"❌ 重启失败";await x.closeStatusCard(L,D?x.withModel(O):O)}return!0;case"clear":if(!x.isRunning()){x.status="stopped",x.opts.onLifecycleChange?.();let K=await x.openStatusCard("clear","⚪ session 当前未运行","grey");if(K)await x.closeStatusCard(K,`⚪ ${x.backendLabel()} 未运行,clear 无效`);else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,clear 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return!0}{let K=await x.openStatusCard("clear","\uD83E\uDDF9 清空并启动新会话","orange"),H="\uD83E\uDDF9 清空并启动新会话",W=await x.restart(!1,{announce:!K,onStatus:(L)=>{H=L,x.setStatusCard(K,L)}});await x.closeStatusCard(K,W?x.withModel(x.withWorktreeInstructionNotice("✅ 已清空并启动新会话")):H.startsWith("❌")?H:"❌ 清空失败")}return!0}}var RN0;var Th=R0(()=>{h1();X1();RN0=new Map([["hi","hi"],["stop","stop"],["st","stop"],["kill","kill"],["kl","kill"],["restart","restart"],["rs","restart"],["clear","clear"],["cl","clear"],["compact","compact"],["cm","compact"],["model","model"],["md","model"]])});function IN0(x,Z){let Q=!1,J=null,X=()=>{},z=()=>{},Y=x.sessionId,G=()=>{if(J)clearTimeout(J),J=null;x.off("context_compacted",W),x.off("exit",L),x.off("error",N)},K=(O)=>{if(Q)return;Q=!0,G(),X(O)},H=(O)=>{if(Q)return;Q=!0,G(),z(O)},W=(O)=>{if(O.phase==="start")return;if(O.threadId&&Y&&O.threadId!==Y)return;K(O)},L=()=>H(Error("codex app-server exited before context compaction completed")),N=(O)=>H(O instanceof Error?O:Error(String(O))),D=new Promise((O,v)=>{X=O,z=v,J=setTimeout(()=>{H(Error(`context compaction completion timed out after ${Z/1000}s`))},Z)});return x.on("context_compacted",W),x.once("exit",L),x.once("error",N),{promise:D,cancel(){if(Q)return;Q=!0,G()}}}function TN0(x,Z){let Q=[],J=new Set,X=(G,K)=>{let H=K.threadId??Z;if(H&&G.threadId&&G.threadId!==H)return!1;if(K.turnId)return G.turnId===K.turnId;return!0},z=(G)=>{for(let K=Q.length-1;K>=0;K--){let H=Q[K];if(H&&X(H,G))return H}return null},Y=(G)=>{if(Z&&G.threadId&&G.threadId!==Z)return;let K={usage:G.usage,contextWindow:G.contextWindow,threadId:G.threadId,turnId:G.turnId};Q.push(K);for(let H of[...J])if(X(K,H.notice))H.finish(K)};return x.on("token_usage",Y),{waitFor(G,K){let H=z(G);if(H)return Promise.resolve(H);return new Promise((W)=>{let L=null,N={notice:G,finish:(D)=>{if(L)clearTimeout(L);J.delete(N),W(D)}};L=setTimeout(()=>N.finish(null),K),J.add(N)})},cancel(){x.off("token_usage",Y);for(let G of[...J])G.finish(null)}}}function cN0(x){if(!x)return" · \uD83E\uDDE0 MISS";let Z=Fz(x.usage);if(Z==null)return" · \uD83E\uDDE0 MISS";let Q=Uz(x.contextWindow);return` · \uD83E\uDDE0 ${tu(Z,Q)} (${Lz(Z,Q)})`}async function ch(x){x.clearStaleIdleQueueState("compact");let Z=!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0&&!x.openingTurn;if(!x.isRunning()||!x.proc){x.status="stopped",x.opts.onLifecycleChange?.();let H=await x.openStatusCard("compact","⚪ session 当前未运行","grey");if(H)await x.closeStatusCard(H,"⚪ 后端未运行,compact 无效");else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,compact 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return}if(!Z){let H=await x.openStatusCard("compact","⚠️ 当前 turn 正在执行","grey");if(H)await x.closeStatusCard(H,"⚠️ 先 stop 当前 turn,再 compact");else await W0(x.chatId,"⚠️ 当前 turn 正在执行,先 `stop` 后再 `compact`。");return}if(x.manualContextCompactionPending){let H=await x.openStatusCard("compact","⏳ 上下文压缩已在进行中","grey");if(H)await x.closeStatusCard(H,"⏳ 上下文压缩已在进行中");else await W0(x.chatId,"⏳ 上下文压缩已在进行中。");return}let Q=x.proc,J=Q.sessionId?Q.sessionId.slice(0,8):"",X=x.withModel(J?`\uD83E\uDDE0 压缩上下文 thread=${J}…`:"\uD83E\uDDE0 压缩上下文…"),z=await x.openStatusCard("compact",X,"orange"),Y=async(H)=>{if(z)await x.closeStatusCard(z,H);else await W0(x.chatId,H)},G=IN0(Q,SN0),K=TN0(Q,Q.sessionId);x.manualContextCompactionPending=!0;try{x.setStatusCard(z,x.withModel("\uD83E\uDDE0 发起上下文压缩")),await Q.compactThread(),x.setStatusCard(z,x.withModel("⏳ 等待压缩完成事件"));let H=await G.promise,W=await K.waitFor(H,jN0),L=H.threadId?` thread=${H.threadId.slice(0,8)}…`:"";await Y(x.withModel(`✅ 上下文已压缩${L}${cN0(W)}`))}catch(H){if(G.cancel(),H instanceof J2)await Y(x.withModel(`ⓘ ${H.message}`));else await Y(`❌ 上下文压缩失败: ${$1(H)}`)}finally{K.cancel(),x.manualContextCompactionPending=!1}}var SN0=600000,jN0=1500;var qh=R0(()=>{i7();h1()});import{randomUUID as AN0}from"node:crypto";function EN0(x){let Z=x.currentTokenSource(),Q=x.currentModelLabel();return W2().filter((J)=>J.enabled||J.kind!=="claude-native").map((J)=>({provider:J.agent,sourceId:J.id,display:J.display+(Z?.id===J.id&&Q?` · ${Q}`:""),enabled:J.enabled,modelCount:J.models.length,selected:Z?.id===J.id}))}function bN0(x,Z){let Q=x.currentModelLabel(),J=x.currentEffortLabel(),X=x.currentTokenSource()?.id===Z.id;return Z.models.map((z)=>{let Y=X&&Q===z.model;return{provider:Z.agent,sourceId:Z.id,model:z.model,displayName:z.display,description:Z.display,enabled:!0,isDefault:!1,selected:Y,efforts:z.efforts.map((G)=>({effort:G,description:"",isDefault:G===z.defaultEffort,selected:Y&&J===G}))}})}async function Ah(x){let Z=AN0(),Q=EN0(x);if(x.modelPanels.set(Z,{models:[]}),!await M1(x.chatId,yL({sessionName:x.sessionName,panelId:Z,currentDisplay:x.currentTokenSource()?.display??x.currentModelLabel(),providers:Q})))x.modelPanels.delete(Z),await R1(x.chatId,"❌ 模型面板发送失败")}async function Eh(x,Z,Q=""){let J=Z.trim(),X=J4(J);if(!X)return{ok:!1,message:`未知账号: ${J}`};if(!X.enabled)return{ok:!1,message:`${X.display} 未配置,请先点「启用」`};let z=Q.trim(),Y=bN0(x,X);return x.modelPanels.set(z,{models:Y}),{ok:!0,message:"",card:hL({sessionName:x.sessionName,panelId:z,currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),models:Y})}}function kN0(x,Z){return Z?.provider==="claude"||Z?.provider==="codex"?Z.provider:d7(x)}function gN0(x,Z){if(x.currentTurn)return"当前 turn 不变,后续新 turn 使用。";if(x.proc?.isAlive()&&x.proc.provider===Z)return Z==="codex"?"Codex 需重启进程生效(发 restart)。":"下一轮开始使用。";return`下次启动 ${b6(Z)} 时使用。`}async function bh(x,Z,Q="",J="",X=null){let z=Z.trim();if(!z)return await W0(x.chatId,`❌ ${"模型为空"}`),{ok:!1,message:"模型为空"};let Y=kN0(z,X),G=x.modelPanels.get(Q.trim())?.models.find((K)=>K.model===z&&(K.provider??"codex")===Y);if(!G)return{ok:!1,message:"模型不在当前选项中,请重新发送 model"};if(G.enabled===!1)return{ok:!1,message:`${G.displayName} 未配置,请先点「启用」`};if(G.efforts.length===0)return{ok:!1,message:"模型未返回 effort"};return{ok:!0,message:"",card:mL({sessionName:x.sessionName,panelId:Q.trim(),currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),model:G})}}async function kh(x,Z,Q,J="",X="",z=""){let Y=Z.trim(),G=Q.trim();if(!Y)return{ok:!1,message:"模型为空"};let K=J.trim(),H=x.modelPanels.get(K),W=z==="claude"||z==="codex"?z:H?.models.find((c)=>c.model===Y)?.provider??d7(Y);if(W==="claude"){if(!L3(G))return{ok:!1,message:"Claude reasoning effort 无效"}}else if(!Y6(G))return{ok:!1,message:"Codex reasoning effort 无效"};let L=G,N=H?.models.find((c)=>c.model===Y&&(c.provider??"codex")===W);if(!N||!N.efforts.some((c)=>c.effort===L))return{ok:!1,message:`${b6(W)} · ${Y}/${L} 不在选项中`};let D=!!N.sourceId&&x.currentTokenSource()?.id!==N.sourceId;if(x.currentProvider()===W&&!D&&x.currentModelLabel()===Y&&x.currentEffortLabel()===L)return x.modelPanels.delete(K),{ok:!0,message:`当前已是 ${b6(W)} · ${Y} / ${L}`,card:uz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:"当前已是此设置,无需变更。"})};if(x.proc?.isAlive()&&x.proc.provider!==W&&(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0))return{ok:!1,message:`当前 ${x.backendLabel(x.proc.provider)} turn 正在执行或排队;请等结束或 stop 后再切换到 ${b6(W)}`};let B=x.currentModelLabel()!==Y||D,j=!!(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0);if(W==="claude"&&x.proc?.isAlive()&&x.proc.provider==="claude"&&B&&j)return{ok:!1,message:"当前 Claude turn 正在执行或排队;Claude 模型 profile 通过 env 生效,请等结束或 stop 后再切换"};try{if(x.proc?.isAlive()&&x.proc.provider===W){let I=N.sourceId?J4(N.sourceId)?.resolveSpawnModel(Y)??Y:Y;await qy(x.proc.setModelSettings(I,L),20000,"thread/settings/update")}await x.applyModelSelection(W,Y,L,N.sourceId);let c=gN0(x,W);return x.modelPanels.delete(K),{ok:!0,message:`已选择 ${b6(W)} · ${Y} / ${L}`,card:uz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:c})}}catch(c){let I=`模型切换失败: ${$1(c)}`;return k(`session "${x.sessionName}": set model settings failed: ${$1(c)}`),await W0(x.chatId,`❌ ${I}`),{ok:!1,message:I}}}var gh=R0(()=>{c4();_7();i7();m4();h1();X1()});function f3(x,Z,Q=!1){let J=x.worktreeProjectName();return xN({projectName:J,tasklistName:tL(J),binding:N4(J),notice:Z,confirmDelete:Q})}async function Ph(x){if(!await M1(x.chatId,f3(x)))await R1(x.chatId,"❌ task 面板发送失败")}async function wh(x){let Z=x.worktreeProjectName();try{let Q=N4(Z),J=await rf(Z,x.chatId),X=Q?"已启用":`已启用 ${J.name}`;return{ok:!0,message:X,card:f3(x,{type:"success",content:`✅ ${X}`})}}catch(Q){let J=`启用失败: ${$1(Q)}`;return k(`session "${x.sessionName}": tasklist enable failed: ${$1(Q)}`),{ok:!1,message:J,card:f3(x,{type:"error",content:`❌ ${J}`})}}}function uh(x,Z){let Q=x.worktreeProjectName(),J=N4(Q),X=Z.trim();if(!J)return{ok:!1,message:"未启用",card:f3(x,{type:"error",content:"❌ 未启用"})};if(J.guid!==X)return{ok:!1,message:"清单绑定已变化",card:f3(x,{type:"error",content:"❌ 清单绑定已变化,请重新打开 task 面板"})};return{ok:!0,message:"请再次确认删除",card:f3(x,{type:"error",content:"⚠️ 删除会删除所有清单内任务"},!0)}}async function fh(x,Z){let Q=x.worktreeProjectName(),J=Z.trim();try{let z=`已删除 ${(await tf(Q,J)).name}`;return{ok:!0,message:z,card:f3(x,{type:"success",content:`✅ ${z}`})}}catch(X){let z=`删除失败: ${$1(X)}`;return k(`session "${x.sessionName}": tasklist delete failed: ${$1(X)}`),{ok:!1,message:z,card:f3(x,{type:"error",content:`❌ ${z}`})}}}var yh=R0(()=>{m4();h1();X1();iz()});var hh,mh,_h,lh;var nh=R0(()=>{hh=["- 以 `[file: /abs/path]` 开头的文本表示该路径上挂着一个文件,相关时去读它。","- 在回复任意位置,独占一行写 `[[send: /abs/path]]` 即可把该文件作为单独一条消息送出。只在用户主动要文件、或你要交付最终产物时才发。"],mh=["- 判断是否该发文件:导出附件/数据集、用户明确要文件或下载物时,应先写到本机绝对路径,再用独占一行的 `[[send: /abs/path]]` 发给用户;短总结、普通说明、无需留存的正文答复不要为了发文件而发文件。","- 使用图片生成工具生成图片后,不要再补充说明文字或 `[[send: ...]]`;Lodestar 会根据工具返回的本机图片路径自动发给用户。若你用脚本或文件编辑生成图片,按上一条用 `[[send: /abs/path]]` 发出。","- 每次调用 Bash / shell 命令时,第一行都必须写 shell 注释 `# desc: <一句中文说明>`,再写真正命令。这个注释只给 Lodestar 卡片做摘要,不要依赖它改变命令行为。"],_h=[...hh,'- 当你有问题需要澄清时,输出单独一行 `[[askusr: {...}]]`。JSON 必须单行、合法、紧凑。格式是 `{ "questions": [{ "question": "完整问题1", "options": ["问题1的可选方案A","问题1的可选方案B"] }, { "question": "完整问题2", "options": [...] }] }`。每个问题都必须提供 `options`,且每题至少 2 个选项;',"- 一旦输出 `[[askusr: ...]]`,立刻停止继续推理和继续回答,等待用户先回答。需要澄清时,应一次性把当前继续执行所需的所有问题都放进这一个 askusr 标记里,不要拆成多轮连续追问。",...mh].join(`
343
+ `),BN0()}var jh=R0(()=>{n4();m8();RN();c4()});var CN={};GZ(CN,{runTokenSourceSetup:()=>MN0,onTokenSourceEnable:()=>vN0});async function vN0(x,Z){let Q=J4(Z);if(!Q){await W0(x.chatId,`❌ 未知 token source: ${Z}`);return}if(Q.enabled){await W0(x.chatId,`${Q.display} 已启用,发 \`model\` 选择。`);return}let J=p7().find((X)=>X.setup?.commandSuffix===Z||X.configSectionId===Z);if(J?.setup)await W0(x.chatId,J.setup.hint(Q.display));else if(Q.kind==="codex-subscription")await W0(x.chatId,`${Q.display} 需要 ChatGPT 登录:在服务器执行 \`codex login\`,完成后重启 daemon(\`systemctl --user restart feishu-daemon\`)或重发 \`model\` 刷新。`);else if(Q.kind==="claude-native")await W0(x.chatId,`${Q.display} 直接使用本机 Claude Code 配置,无需单独启用。`)}async function MN0(x,Z,Q){let J=p7().find((z)=>z.setup?.commandSuffix===Z);if(!J?.setup||!J.configSectionId){await W0(x.chatId,`❌ 未知或不可配置的 source: ${Z}`);return}let X=J.setup.parseArgs(Q);if("error"in X){await W0(x.chatId,X.error);return}try{Sh(J.configSectionId,X.config),await K2();let z=J4(J.configSectionId);await W0(x.chatId,`✅ ${z?.display??Z} 已启用。发 \`model\` 重新选择。`)}catch(z){await W0(x.chatId,`❌ 启用失败: ${z?.message??z}`)}}var SN=R0(()=>{h1();c4();jh()});async function Ih(x,Z,Q=""){let J=Z.trim().match(/^(?:wt|worktree)(?:\s+(.+))?$/i);if(J){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 wt。"),!0;return await x.runWorktreeCommand((J[1]??"").trim(),Q),!0}if(Z.trim().match(/^btw$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 btw。"),!0;return await x.runBtwCommand(Q),!0}if(Z.trim().match(/^bye$/i))return await x.runByeCommand(),!0;if(Z.trim().match(/^(?:fk|fork)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 fk。"),!0;return await x.showForkList(),!0}if(Z.trim().match(/^(?:bk|back)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 bk。"),!0;if(x.isRunning())await x.stop("回滚前终止",{announce:!1});return await x.showBackList(),!0}let X=Z.trim().match(/^agy(?:\s+([\s\S]+))?$/i);if(X)return await x.runAgyCommand((X[1]??"").trim()),!0;if(Z.trim().toLowerCase()==="task")return await x.showTasklistPanel(),!0;let z=Z.trim().match(/^(\w+)-setup\s+([\s\S]+)$/i);if(z){let{runTokenSourceSetup:G}=await Promise.resolve().then(() => (SN(),CN));return await G(x,z[1].trim(),z[2].trim()),!0}let Y=RN0.get(Z.trim().toLowerCase());if(!Y)return!1;if((x.startingAgy||x.runningAgy)&&!["stop","kill","restart","hi","model"].includes(Y))return await W0(x.chatId,`⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再执行 ${Y}。`),!0;switch(Y){case"model":return await x.showModelPanel(),!0;case"compact":return await x.runCompactCommand(),!0;case"hi":{let K=!x.isRunning(),H=x.backendLabel(),W=K?await x.openStatusCard("hi",x.withModel(`\uD83D\uDE80 启动 ${H}`)):null,L=x.withModel(`\uD83D\uDE80 启动 ${H}`);if(!(K?await x.start({announce:!W,onStatus:(D)=>{L=D,x.setStatusCard(W,D)}}):!0))return await x.closeStatusCard(W,L.startsWith("❌")?L:"❌ 启动失败"),!0;if(W)return await x.replaceStatusCardWithConsole(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`))),!0;if(K)await x.closeStatusCard(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`)))}return await x.showConsole(),!0;case"stop":if(x.runningAgy)return await x.stopAgyTask("\uD83D\uDED1 agy 已打断"),!0;if(x.clearStaleIdleQueueState("stop"),x.clearMultiMsgBuffer("stop command"),!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0){let K=await x.openStatusCard("stop","⚪ 当前没有正在执行的 turn","grey");if(K)await x.closeStatusCard(K,"⚪ 无正在执行的 turn");else await W0(x.chatId,"⚪ 当前没有正在执行的 turn");return!0}k(`session "${x.sessionName}": stop command — interrupt + drop count=${x.pendingUserMessageCount} midBuffer=${x.pendingMidTurnMsgs.length}`);let G=new Set;for(let[K,H]of[...x.pendingReactionIds.entries(),...x.currentBatchReactionIds.entries()]){if(H)u4(K,H);k8(K,"CrossMark"),G.add(K)}for(let K of x.pendingMidTurnMsgs)if(K.msgId&&!G.has(K.msgId))k8(K.msgId,"CrossMark");return x.pendingUserMessageCount=0,x.pendingMidTurnMsgs=[],x.pendingTurnInputs=[],x.lastUserOpenId="",x.pendingReactionIds=new Map,x.currentBatchReactionIds=new Map,x.userInterrupted=!0,x.interrupt(),await x.closeTurnCard("\uD83D\uDED1 打断"),!0;case"kill":{if(x.runningAgy)await x.stopAgyTask("\uD83D\uDED1 agy 已终止");let K=x.isRunning(),H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=K?`\uD83D\uDED1 停止 ${H}`:"⚪ session 当前未运行",L=await x.openStatusCard("kill",W,K?"red":"grey");await x.stop("已终止",{announce:!L,onStatus:(N)=>{x.setStatusCard(L,N)}}),await x.closeStatusCard(L,K?`✅ ${H} 已终止`:`⚪ ${H} 未运行`)}return!0;case"restart":if(!x.isRunning()&&x.selectedProvider!=="codex")return await x.showResumeList(),!0;{let K=x.lastSessionId?x.lastSessionId.slice(0,8):"",H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=x.isRunning()?x.withModel(`\uD83D\uDD01 重启 ${H}`):K?x.withModel(`\uD83D\uDD01 恢复上一会话 thread=${K}…`):x.withModel(`\uD83D\uDD01 启动 ${H}`),L=await x.openStatusCard("restart",W);if(x.runningAgy)x.setStatusCard(L,"\uD83D\uDED1 restart 前终止 agy"),await x.stopAgyTask("\uD83D\uDED1 restart 前已终止 agy");let N=W,D=await x.restart(!0,{announce:!L,onStatus:(v)=>{N=v,x.setStatusCard(L,v)}}),O=D?N.startsWith("✅")?N:x.withWorktreeInstructionNotice(K?"✅ 已恢复上一会话":`✅ ${x.backendLabel()} 已就绪`):N.startsWith("❌")?N:"❌ 重启失败";await x.closeStatusCard(L,D?x.withModel(O):O)}return!0;case"clear":if(!x.isRunning()){x.status="stopped",x.opts.onLifecycleChange?.();let K=await x.openStatusCard("clear","⚪ session 当前未运行","grey");if(K)await x.closeStatusCard(K,`⚪ ${x.backendLabel()} 未运行,clear 无效`);else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,clear 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return!0}{let K=await x.openStatusCard("clear","\uD83E\uDDF9 清空并启动新会话","orange"),H="\uD83E\uDDF9 清空并启动新会话",W=await x.restart(!1,{announce:!K,onStatus:(L)=>{H=L,x.setStatusCard(K,L)}});await x.closeStatusCard(K,W?x.withModel(x.withWorktreeInstructionNotice("✅ 已清空并启动新会话")):H.startsWith("❌")?H:"❌ 清空失败")}return!0}}var RN0;var Th=R0(()=>{h1();X1();RN0=new Map([["hi","hi"],["stop","stop"],["st","stop"],["kill","kill"],["kl","kill"],["restart","restart"],["rs","restart"],["clear","clear"],["cl","clear"],["compact","compact"],["cm","compact"],["model","model"],["md","model"]])});function IN0(x,Z){let Q=!1,J=null,X=()=>{},z=()=>{},Y=x.sessionId,G=()=>{if(J)clearTimeout(J),J=null;x.off("context_compacted",W),x.off("exit",L),x.off("error",N)},K=(O)=>{if(Q)return;Q=!0,G(),X(O)},H=(O)=>{if(Q)return;Q=!0,G(),z(O)},W=(O)=>{if(O.phase==="start")return;if(O.threadId&&Y&&O.threadId!==Y)return;K(O)},L=()=>H(Error("codex app-server exited before context compaction completed")),N=(O)=>H(O instanceof Error?O:Error(String(O))),D=new Promise((O,v)=>{X=O,z=v});return x.on("context_compacted",W),x.once("exit",L),x.once("error",N),{promise:D,cancel(){if(Q)return;Q=!0,G()}}}function TN0(x,Z){let Q=[],J=new Set,X=(G,K)=>{let H=K.threadId??Z;if(H&&G.threadId&&G.threadId!==H)return!1;if(K.turnId)return G.turnId===K.turnId;return!0},z=(G)=>{for(let K=Q.length-1;K>=0;K--){let H=Q[K];if(H&&X(H,G))return H}return null},Y=(G)=>{if(Z&&G.threadId&&G.threadId!==Z)return;let K={usage:G.usage,contextWindow:G.contextWindow,threadId:G.threadId,turnId:G.turnId};Q.push(K);for(let H of[...J])if(X(K,H.notice))H.finish(K)};return x.on("token_usage",Y),{waitFor(G,K){let H=z(G);if(H)return Promise.resolve(H);return new Promise((W)=>{let L=null,N={notice:G,finish:(D)=>{if(L)clearTimeout(L);J.delete(N),W(D)}};L=setTimeout(()=>N.finish(null),K),J.add(N)})},cancel(){x.off("token_usage",Y);for(let G of[...J])G.finish(null)}}}function cN0(x){if(!x)return" · \uD83E\uDDE0 MISS";let Z=Fz(x.usage);if(Z==null)return" · \uD83E\uDDE0 MISS";let Q=Uz(x.contextWindow);return` · \uD83E\uDDE0 ${tu(Z,Q)} (${Lz(Z,Q)})`}async function ch(x){x.clearStaleIdleQueueState("compact");let Z=!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0&&!x.openingTurn;if(!x.isRunning()||!x.proc){x.status="stopped",x.opts.onLifecycleChange?.();let H=await x.openStatusCard("compact","⚪ session 当前未运行","grey");if(H)await x.closeStatusCard(H,"⚪ 后端未运行,compact 无效");else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,compact 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return}if(!Z){let H=await x.openStatusCard("compact","⚠️ 当前 turn 正在执行","grey");if(H)await x.closeStatusCard(H,"⚠️ 先 stop 当前 turn,再 compact");else await W0(x.chatId,"⚠️ 当前 turn 正在执行,先 `stop` 后再 `compact`。");return}if(x.manualContextCompactionPending){let H=await x.openStatusCard("compact","⏳ 上下文压缩已在进行中","grey");if(H)await x.closeStatusCard(H,"⏳ 上下文压缩已在进行中");else await W0(x.chatId,"⏳ 上下文压缩已在进行中。");return}let Q=x.proc,J=Q.sessionId?Q.sessionId.slice(0,8):"",X=x.withModel(J?`\uD83E\uDDE0 压缩上下文 thread=${J}…`:"\uD83E\uDDE0 压缩上下文…"),z=await x.openStatusCard("compact",X,"orange"),Y=async(H)=>{if(z)await x.closeStatusCard(z,H);else await W0(x.chatId,H)},G=IN0(Q,SN0),K=TN0(Q,Q.sessionId);x.manualContextCompactionPending=!0;try{x.setStatusCard(z,x.withModel("\uD83E\uDDE0 发起上下文压缩")),await Q.compactThread(),x.setStatusCard(z,x.withModel("⏳ 等待压缩完成事件"));let H=await G.promise,W=await K.waitFor(H,jN0),L=H.threadId?` thread=${H.threadId.slice(0,8)}…`:"";await Y(x.withModel(`✅ 上下文已压缩${L}${cN0(W)}`))}catch(H){if(G.cancel(),H instanceof J2)await Y(x.withModel(`ⓘ ${H.message}`));else await Y(`❌ 上下文压缩失败: ${$1(H)}`)}finally{K.cancel(),x.manualContextCompactionPending=!1}}var SN0=600000,jN0=1500;var qh=R0(()=>{i7();h1()});import{randomUUID as AN0}from"node:crypto";function EN0(x){let Z=x.currentTokenSource(),Q=x.currentModelLabel();return W2().filter((J)=>J.enabled||J.kind!=="claude-native").map((J)=>({provider:J.agent,sourceId:J.id,display:J.display+(Z?.id===J.id&&Q?` · ${Q}`:""),enabled:J.enabled,modelCount:J.models.length,selected:Z?.id===J.id}))}function bN0(x,Z){let Q=x.currentModelLabel(),J=x.currentEffortLabel(),X=x.currentTokenSource()?.id===Z.id;return Z.models.map((z)=>{let Y=X&&Q===z.model;return{provider:Z.agent,sourceId:Z.id,model:z.model,displayName:z.display,description:Z.display,enabled:!0,isDefault:!1,selected:Y,efforts:z.efforts.map((G)=>({effort:G,description:"",isDefault:G===z.defaultEffort,selected:Y&&J===G}))}})}async function Ah(x){let Z=AN0(),Q=EN0(x);if(x.modelPanels.set(Z,{models:[]}),!await M1(x.chatId,yL({sessionName:x.sessionName,panelId:Z,currentDisplay:x.currentTokenSource()?.display??x.currentModelLabel(),providers:Q})))x.modelPanels.delete(Z),await R1(x.chatId,"❌ 模型面板发送失败")}async function Eh(x,Z,Q=""){let J=Z.trim(),X=J4(J);if(!X)return{ok:!1,message:`未知账号: ${J}`};if(!X.enabled)return{ok:!1,message:`${X.display} 未配置,请先点「启用」`};let z=Q.trim(),Y=bN0(x,X);return x.modelPanels.set(z,{models:Y}),{ok:!0,message:"",card:hL({sessionName:x.sessionName,panelId:z,currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),models:Y})}}function kN0(x,Z){return Z?.provider==="claude"||Z?.provider==="codex"?Z.provider:d7(x)}function gN0(x,Z){if(x.currentTurn)return"当前 turn 不变,后续新 turn 使用。";if(x.proc?.isAlive()&&x.proc.provider===Z)return Z==="codex"?"Codex 需重启进程生效(发 restart)。":"下一轮开始使用。";return`下次启动 ${b6(Z)} 时使用。`}async function bh(x,Z,Q="",J="",X=null){let z=Z.trim();if(!z)return await W0(x.chatId,`❌ ${"模型为空"}`),{ok:!1,message:"模型为空"};let Y=kN0(z,X),G=x.modelPanels.get(Q.trim())?.models.find((K)=>K.model===z&&(K.provider??"codex")===Y);if(!G)return{ok:!1,message:"模型不在当前选项中,请重新发送 model"};if(G.enabled===!1)return{ok:!1,message:`${G.displayName} 未配置,请先点「启用」`};if(G.efforts.length===0)return{ok:!1,message:"模型未返回 effort"};return{ok:!0,message:"",card:mL({sessionName:x.sessionName,panelId:Q.trim(),currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),model:G})}}async function kh(x,Z,Q,J="",X="",z=""){let Y=Z.trim(),G=Q.trim();if(!Y)return{ok:!1,message:"模型为空"};let K=J.trim(),H=x.modelPanels.get(K),W=z==="claude"||z==="codex"?z:H?.models.find((c)=>c.model===Y)?.provider??d7(Y);if(W==="claude"){if(!L3(G))return{ok:!1,message:"Claude reasoning effort 无效"}}else if(!Y6(G))return{ok:!1,message:"Codex reasoning effort 无效"};let L=G,N=H?.models.find((c)=>c.model===Y&&(c.provider??"codex")===W);if(!N||!N.efforts.some((c)=>c.effort===L))return{ok:!1,message:`${b6(W)} · ${Y}/${L} 不在选项中`};let D=!!N.sourceId&&x.currentTokenSource()?.id!==N.sourceId;if(x.currentProvider()===W&&!D&&x.currentModelLabel()===Y&&x.currentEffortLabel()===L)return x.modelPanels.delete(K),{ok:!0,message:`当前已是 ${b6(W)} · ${Y} / ${L}`,card:uz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:"当前已是此设置,无需变更。"})};if(x.proc?.isAlive()&&x.proc.provider!==W&&(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0))return{ok:!1,message:`当前 ${x.backendLabel(x.proc.provider)} turn 正在执行或排队;请等结束或 stop 后再切换到 ${b6(W)}`};let B=x.currentModelLabel()!==Y||D,j=!!(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0);if(W==="claude"&&x.proc?.isAlive()&&x.proc.provider==="claude"&&B&&j)return{ok:!1,message:"当前 Claude turn 正在执行或排队;Claude 模型 profile 通过 env 生效,请等结束或 stop 后再切换"};try{if(x.proc?.isAlive()&&x.proc.provider===W){let I=N.sourceId?J4(N.sourceId)?.resolveSpawnModel(Y)??Y:Y;await qy(x.proc.setModelSettings(I,L),20000,"thread/settings/update")}await x.applyModelSelection(W,Y,L,N.sourceId);let c=gN0(x,W);return x.modelPanels.delete(K),{ok:!0,message:`已选择 ${b6(W)} · ${Y} / ${L}`,card:uz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:c})}}catch(c){let I=`模型切换失败: ${$1(c)}`;return k(`session "${x.sessionName}": set model settings failed: ${$1(c)}`),await W0(x.chatId,`❌ ${I}`),{ok:!1,message:I}}}var gh=R0(()=>{c4();_7();i7();m4();h1();X1()});function f3(x,Z,Q=!1){let J=x.worktreeProjectName();return xN({projectName:J,tasklistName:tL(J),binding:N4(J),notice:Z,confirmDelete:Q})}async function Ph(x){if(!await M1(x.chatId,f3(x)))await R1(x.chatId,"❌ task 面板发送失败")}async function wh(x){let Z=x.worktreeProjectName();try{let Q=N4(Z),J=await rf(Z,x.chatId),X=Q?"已启用":`已启用 ${J.name}`;return{ok:!0,message:X,card:f3(x,{type:"success",content:`✅ ${X}`})}}catch(Q){let J=`启用失败: ${$1(Q)}`;return k(`session "${x.sessionName}": tasklist enable failed: ${$1(Q)}`),{ok:!1,message:J,card:f3(x,{type:"error",content:`❌ ${J}`})}}}function uh(x,Z){let Q=x.worktreeProjectName(),J=N4(Q),X=Z.trim();if(!J)return{ok:!1,message:"未启用",card:f3(x,{type:"error",content:"❌ 未启用"})};if(J.guid!==X)return{ok:!1,message:"清单绑定已变化",card:f3(x,{type:"error",content:"❌ 清单绑定已变化,请重新打开 task 面板"})};return{ok:!0,message:"请再次确认删除",card:f3(x,{type:"error",content:"⚠️ 删除会删除所有清单内任务"},!0)}}async function fh(x,Z){let Q=x.worktreeProjectName(),J=Z.trim();try{let z=`已删除 ${(await tf(Q,J)).name}`;return{ok:!0,message:z,card:f3(x,{type:"success",content:`✅ ${z}`})}}catch(X){let z=`删除失败: ${$1(X)}`;return k(`session "${x.sessionName}": tasklist delete failed: ${$1(X)}`),{ok:!1,message:z,card:f3(x,{type:"error",content:`❌ ${z}`})}}}var yh=R0(()=>{m4();h1();X1();iz()});var hh,mh,_h,lh;var nh=R0(()=>{hh=["- 以 `[file: /abs/path]` 开头的文本表示该路径上挂着一个文件,相关时去读它。","- 在回复任意位置,独占一行写 `[[send: /abs/path]]` 即可把该文件作为单独一条消息送出。只在用户主动要文件、或你要交付最终产物时才发。"],mh=["- 判断是否该发文件:导出附件/数据集、用户明确要文件或下载物时,应先写到本机绝对路径,再用独占一行的 `[[send: /abs/path]]` 发给用户;短总结、普通说明、无需留存的正文答复不要为了发文件而发文件。","- 使用图片生成工具生成图片后,不要再补充说明文字或 `[[send: ...]]`;Lodestar 会根据工具返回的本机图片路径自动发给用户。若你用脚本或文件编辑生成图片,按上一条用 `[[send: /abs/path]]` 发出。","- 每次调用 Bash / shell 命令时,第一行都必须写 shell 注释 `# desc: <一句中文说明>`,再写真正命令。这个注释只给 Lodestar 卡片做摘要,不要依赖它改变命令行为。"],_h=[...hh,'- 当你有问题需要澄清时,输出单独一行 `[[askusr: {...}]]`。JSON 必须单行、合法、紧凑。格式是 `{ "questions": [{ "question": "完整问题1", "options": ["问题1的可选方案A","问题1的可选方案B"] }, { "question": "完整问题2", "options": [...] }] }`。每个问题都必须提供 `options`,且每题至少 2 个选项;',"- 一旦输出 `[[askusr: ...]]`,立刻停止继续推理和继续回答,等待用户先回答。需要澄清时,应一次性把当前继续执行所需的所有问题都放进这一个 askusr 标记里,不要拆成多轮连续追问。",...mh].join(`
344
344
  `),lh=[...hh,"- 当你有问题需要澄清时,使用 Claude Code 自带的 AskUserQuestion 工具向用户提问;不要输出 Codex 专属的 host-marker 文本协议。",...mh].join(`
345
345
  `)});import{execFileSync as uN0}from"node:child_process";import{existsSync as px,readdirSync as fN0,readFileSync as yN0,realpathSync as dh,statSync as hN0}from"node:fs";import{dirname as mN0,join as ph,resolve as WW}from"node:path";function sx(x){let Z=x.trim();if(!_N0.test(Z))return null;return Z}function xZ(x){return x.startsWith(lN0)}function sh(x){return x.match(/^(.+)\[[^\[\]]+\]$/)?.[1]??x}function _5(x,Z){return`${x}[${Z}]`}function oh(x){return`${m5}${x}`}function KW(x,Z,Q){return ph(mN0(x),_5(Z,Q))}function rh(x,Z,Q){let J=nN0(x,Z,Q);if(!J)return null;let X=yN0(J.path,"utf8").trim();if(!X)return null;return{path:J.path,content:X,slug:J.slug}}function th(x,Z){IN(x);let Q=G8(x,["for-each-ref","--format=%(refname:short)",`refs/heads/${m5}`]).split(`
346
346
  `).map((z)=>z.trim()).filter(Boolean).filter((z)=>z.startsWith(m5)),J=TN(x),X=[];for(let z of Q){let Y=z.slice(m5.length);if(!sx(Y))continue;if(xZ(Y))continue;let G=_5(Z,Y),K=KW(x,Z,Y),H=J.get(z)??null,W=H??K,L=!!H&&px(H),N=Ym(x,z,L),D={slug:Y,chatName:G,branch:z,state:N,expectedPath:K,worktreePath:L?H:null,mounted:L,dirtyCount:null,statusLine:null,error:null};if(H&&px(H))try{let v=G8(H,["status","--porcelain=v1","--branch"]).split(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leviyuan/lodestar",
3
- "version": "0.13.3",
3
+ "version": "0.13.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },