grix-connector 2.0.6 → 2.0.8

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.
@@ -6,11 +6,11 @@ Error: ${n}`,r,!1,a)}},t=this.config.adapterOptions??{};return new Y({command:th
6
6
 
7
7
  Error: ${n}`,0,!1)},sendUpdateBindingCard:(o,i,n)=>this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:i,cwd:n}),agentInvoke:async(o,i)=>this.platformInvoke(o,i),sendLocalActionResult:(o,i,n,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:o,status:i,...n!==void 0?{result:n}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})}},t=this.config.adapterOptions??{};return new Z({command:this.config.agent.command,args:this.config.agent.args,env:this.config.agent.env,options:t},s,{port:t.port,host:t.host,workspaceDir:t.workspace_dir,sessionToken:t.session_token,enableSessionBinding:!0,aibotSessionId:e})}createOpenCodeAdapter(e){const s={sendStreamChunk:(o,i,n,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(o,i,n,r,a,d)},sendFinalStreamChunkReliable:async(o,i,n,r)=>{if(this.finalizeThinking(o,i),this.resolveEventRuntimeConfig(o).responseDelivery==="single_message"&&o){this.bufferStreamChunk(o,i,"",!0);return}try{await this.aibotHandle.sendStreamChunkRequest({event_id:o,session_id:i,delta_content:"",chunk_seq:n,is_finish:!0,...r?{client_msg_id:r}:{}})}catch(d){u.warn("bridge",`[opencode] sendFinalStreamChunkReliable ACK failed event=${o}: ${d}`)}},sendEventResult:(o,i,n)=>{this.sendEventResultWithCleanup(o,i,n),u.info("bridge",`[opencode] sendEventResult event=${o} status=${i}`)},sendEventAck:(o,i)=>this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()}),sendToolUse:(o,i,n,r)=>{this.sendToolExecutionCard(o,i,E(n,r))},sendToolResult:(o,i,n,r)=>{this.sendToolExecutionCard(o,i,$(n,r))},sendThinking:(o,i,n)=>{this.sendThinkingByRuntimeConfig(o,i,n)},sendRunError:(o,i,n)=>{this.sendStreamChunkByRuntimeConfig(o,i,`
8
8
 
9
- Error: ${n}`,0,!1)},sendUpdateBindingCard:(o,i,n)=>this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:i,cwd:n}),agentInvoke:async(o,i)=>this.platformInvoke(o,i),sendLocalActionResult:(o,i,n,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:o,status:i,...n!==void 0?{result:n}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})}},t=this.config.adapterOptions??{};return new ee({command:this.config.agent.command,args:this.config.agent.args,env:this.config.agent.env,options:t},s,{port:t.port,hostname:t.hostname,model:t.model,agent:t.agent,permissionPolicy:t.permission_policy,enableSessionBinding:!0,aibotSessionId:e})}createAgyAdapter(e){const s={sendStreamChunk:(o,i,n,r,a)=>{this.sendStreamChunkByRuntimeConfig(o,i,n,r,a)},sendEventResult:(o,i,n)=>{this.sendEventResultWithCleanup(o,i,n)},sendEventAck:(o,i)=>{this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()})},agentInvoke:async(o,i,n)=>this.platformInvoke(o,i,n),forceCompleteInternalEvent:(o,i)=>{this.pool.eventComplete(o,i),this.pushQueueSnapshotForSession(i)},persistConversationId:(o,i)=>{this.bindingStore.setAgyConversationId(o,i)}},t=o=>{const i=this.bindingStore.get(o);return{cwd:i?.cwd,modelId:i?.modelId??this.globalConfigStore?.get(this.name)?.modelId,conversationId:i?.agyConversationId}};return new U({command:this.config.agent.command,args:this.config.agent.args,env:this.config.agent.env,options:this.config.adapterOptions??{}},s,t)}createAcpAdapter(e){const s=this.isAcpRawTransportEnabled(),t={sendStreamChunk:(a,d,c,l,h,m)=>{this.finalizeThinking(a,d),this.sendStreamChunkByRuntimeConfig(a,d,c,l,h,m)},sendFinalStreamChunkReliable:async(a,d,c,l)=>{if(this.finalizeThinking(a,d),this.resolveEventRuntimeConfig(a).responseDelivery==="single_message"&&a){this.bufferStreamChunk(a,d,"",!0);return}try{await this.aibotHandle.sendStreamChunkRequest({event_id:a,session_id:d,delta_content:"",chunk_seq:c,is_finish:!0,...l?{client_msg_id:l}:{}})}catch(m){u.warn("bridge",`[acp] sendFinalStreamChunkReliable ACK failed event=${a}: ${m}`)}u.info("bridge",`[acp] sendFinalStreamChunkReliable done event=${a} seq=${c}`)},sendEventResult:(a,d,c)=>{this.sendEventResultWithCleanup(a,d,c)},sendEventAck:(a,d)=>{this.aibotHandle.sendEventAck({event_id:a,session_id:d,received_at:Date.now()})},agentInvoke:async(a,d)=>this.platformInvoke(a,d),sendLocalActionResult:(a,d,c,l,h)=>{this.aibotHandle.sendLocalActionResult({action_id:a,status:d,...c!==void 0?{result:c}:{},...l?{error_code:l}:{},...h?{error_msg:h}:{}})},sendRawEventEnvelope:(a,d,c)=>{this.sendAcpRawEventEnvelope(a,d,c)},sendToolUse:(a,d,c,l)=>{if(s){this.sendAcpRawEventEnvelope(a,d,{type:"tool_use",payload:{tool_name:c,tool_input:l??""}});return}this.sendToolExecutionCard(a,d,E(c,l))},sendToolResult:(a,d,c,l)=>{this.sendToolExecutionCard(a,d,$(c,l))},sendThinking:(a,d,c)=>{this.sendThinkingByRuntimeConfig(a,d,c)},sendRunError:(a,d,c)=>{this.sendStreamChunkByRuntimeConfig(a,d,`
9
+ Error: ${n}`,0,!1)},sendUpdateBindingCard:(o,i,n)=>this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:i,cwd:n}),agentInvoke:async(o,i)=>this.platformInvoke(o,i),sendLocalActionResult:(o,i,n,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:o,status:i,...n!==void 0?{result:n}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})}},t=this.config.adapterOptions??{};return new ee({command:this.config.agent.command,args:this.config.agent.args,env:this.config.agent.env,options:t},s,{port:t.port,hostname:t.hostname,model:t.model,agent:t.agent,permissionPolicy:t.permission_policy,enableSessionBinding:!0,aibotSessionId:e})}createAgyAdapter(e){const s={sendStreamChunk:(o,i,n,r,a)=>{this.sendStreamChunkByRuntimeConfig(o,i,n,r,a)},sendEventResult:(o,i,n)=>{this.sendEventResultWithCleanup(o,i,n)},sendEventAck:(o,i)=>{this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()})},agentInvoke:async(o,i,n)=>this.platformInvoke(o,i,n),forceCompleteInternalEvent:(o,i)=>{this.pool.eventComplete(o,i),this.pushQueueSnapshotForSession(i)},persistConversationId:(o,i)=>{this.bindingStore.setAgyConversationId(o,i)}},t=o=>{const i=this.bindingStore.get(o);return{cwd:i?.cwd,modelId:i?.modelId??this.globalConfigStore?.get(this.name)?.modelId,conversationId:i?.agyConversationId}};return new U({command:this.config.agent.command,args:this.config.agent.args,env:this.config.agent.env,options:this.config.adapterOptions??{}},s,t)}createAcpAdapter(e){const s=this.isAcpRawTransportEnabled(),t={sendStreamChunk:(a,d,c,l,h,m,v)=>{this.finalizeThinking(a,d),this.sendStreamChunkByRuntimeConfig(a,d,c,l,h,m,v)},sendFinalStreamChunkReliable:async(a,d,c,l)=>{if(this.finalizeThinking(a,d),this.resolveEventRuntimeConfig(a).responseDelivery==="single_message"&&a){this.bufferStreamChunk(a,d,"",!0);return}try{await this.aibotHandle.sendStreamChunkRequest({event_id:a,session_id:d,delta_content:"",chunk_seq:c,is_finish:!0,...l?{client_msg_id:l}:{}})}catch(m){u.warn("bridge",`[acp] sendFinalStreamChunkReliable ACK failed event=${a}: ${m}`)}u.info("bridge",`[acp] sendFinalStreamChunkReliable done event=${a} seq=${c}`)},sendEventResult:(a,d,c)=>{this.sendEventResultWithCleanup(a,d,c)},sendEventAck:(a,d)=>{this.aibotHandle.sendEventAck({event_id:a,session_id:d,received_at:Date.now()})},agentInvoke:async(a,d)=>this.platformInvoke(a,d),sendLocalActionResult:(a,d,c,l,h)=>{this.aibotHandle.sendLocalActionResult({action_id:a,status:d,...c!==void 0?{result:c}:{},...l?{error_code:l}:{},...h?{error_msg:h}:{}})},sendRawEventEnvelope:(a,d,c)=>{this.sendAcpRawEventEnvelope(a,d,c)},sendToolUse:(a,d,c,l)=>{if(s){this.sendAcpRawEventEnvelope(a,d,{type:"tool_use",payload:{tool_name:c,tool_input:l??""}});return}this.sendToolExecutionCard(a,d,E(c,l))},sendToolResult:(a,d,c,l)=>{this.sendToolExecutionCard(a,d,$(c,l))},sendThinking:(a,d,c)=>{this.sendThinkingByRuntimeConfig(a,d,c)},sendRunError:(a,d,c)=>{this.sendStreamChunkByRuntimeConfig(a,d,`
10
10
 
11
11
  Error: ${c}`,1,!1)},sendPermissionCard:a=>{if(s){this.sendAcpRawEventEnvelope(a.eventId,a.sessionId,{type:"permission_request",payload:{tool_call_id:a.toolCallId,tool_name:a.toolName,tool_title:a.toolTitle,options:a.options}});return}this.aibotHandle.sendMsg({event_id:a.eventId,session_id:a.sessionId,client_msg_id:`perm_${F()}`,msg_type:1,content:a.toolTitle?`Permission required: ${a.toolTitle}`:"Permission request",extra:{channel_data:{execApproval:{approvalId:a.toolCallId,approvalSlug:a.toolName},grix:{execApproval:{approval_command_id:a.toolCallId,command:a.toolTitle||a.toolName,host:"acp"}}},agent_api_origin:!0}})},sendAuthNotification:(a,d)=>{a&&this.aibotHandle.sendMsg({session_id:a,msg_type:1,content:d,extra:{biz_card:{version:1,type:"agent_error",payload:{error:{name:"AuthRequired",message:d}}}}})},sendUpdateBindingCard:(a,d,c,l)=>{const h={...l??{}};if(!h.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const m=this.providerQuotaToRateLimits(this.cachedProviderQuota);m&&(h.rate_limits=m)}!h.provider_quota&&this.cachedProviderQuota?.success&&(h.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:a,worker_status:d,cwd:c,...Object.keys(h).length>0?{meta:h}:{}})},onSkillsUpdate:a=>{try{this.aibotHandle.sendSkillsUpdate({skills:a})}catch(d){}},onContextWindowUpdated:a=>{this.cachedAcpContextWindow=a,this.cachedAcpContextWindowSampledAtMs=Date.now();const d="usedPercentage"in a?a.usedPercentage.toFixed(1):(a.used/a.size*100).toFixed(1);u.info(this.name,`[acp] context_window updated: ${d}%`);const c=this.bindingStore.get(e);if(c?.cwd){const l={context_window:"usedPercentage"in a?{usedPercentage:a.usedPercentage,remainingPercentage:100-a.usedPercentage}:a};if(this.config.aibot.clientType==="kiro"&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{}),l.provider_quota=this.cachedProviderQuota;const h=this.providerQuotaToRateLimits(this.cachedProviderQuota);h&&(l.rate_limits=h)}this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:c.cwd,meta:l})}},sendMcpFrame:a=>{this.aibotHandle.sendMcpFrame(e,a)}},o=e?this.bindingStore.get(e):void 0,i=this.globalConfigStore?.get(this.name),{initialModel:n,initialMode:r}=Ae({sessionBinding:o,globalDefaults:i,configInitialMode:this.config.acpInitialMode});return(n||r)&&u.info(this.name,`[toolbar] hydrate from binding: session=${e} model=${n??"<none>"} mode=${r??"<none>"}`),new A({command:this.config.agent.command,args:this.config.agent.args,env:this.config.agent.env},t,{acpAuthMethod:this.config.acpAuthMethod,acpInitialMode:r,acpInitialModel:n,acpMcpTools:this.config.acpMcpTools,rawTransport:s,eventResultsPath:this.config.eventResultsPath,approvalMode:this.config.approvalMode,bindingStore:this.config.enableSessionBinding?this.bindingStore:void 0,aibotSessionId:e,autoInjectArgs:this.config.autoInjectArgs,bridgeLog:this.config.logDir?new _e(this.config.logDir,`${this.name}-${e}`):null})}async connectAibot(){const e=new J;if(this.aibotHandle=await e.connect(this.aibotConfig,{aborted:()=>this.stopped,label:this.name,packetLog:this.packetLog}),this.aibotHandle.onEvent(s=>{this.handleAibotEvent(s).catch(t=>{u.error(this.name,`handleAibotEvent failed: ${t}`),this.aibotHandle.sendEventAck({event_id:s.event_id,session_id:s.session_id,received_at:Date.now()});const o=t instanceof Error?t.message:String(t);if(/CWD must be|Bound directory does not exist|Bound path is not a directory/i.test(o)&&s.session_id){this.bindingStore.delete(s.session_id),this.sessionBindings.delete(s.session_id);const n=this.pool.getSlot(s.session_id);n?.adapter instanceof A&&n.adapter.getSessionBindings().delete(s.session_id);const r=this.config.adapterType??"acp",a=this.resolveBindingChannelKey(r);this.aibotHandle.sendMsg({event_id:s.event_id,session_id:s.session_id,msg_type:1,content:o,extra:{channel_data:{[a]:{sessionBinding:{status:"missing",reason:"binding_stale",error_code:g.invalidCwd}}}},quoted_message_id:s.msg_id});return}this.aibotHandle.sendEventResult({event_id:s.event_id,status:"failed",msg:o,updated_at:Date.now()})})}),this.aibotHandle.onStop(s=>{try{this.handleAibotStop(s)}catch(t){u.error(this.name,`handleAibotStop failed: ${t}`)}}),this.aibotHandle.onRevoke(s=>{try{this.handleAibotRevoke(s)}catch(t){u.error(this.name,`handleAibotRevoke failed: ${t}`)}}),this.aibotHandle.onLocalAction(s=>{this.handleAibotLocalAction(s).catch(t=>{u.error(this.name,`handleAibotLocalAction failed: ${t}`)})}),this.aibotHandle.onEventCancel(s=>{u.info(this.name,`recv event_cancel event_id=${s.event_id} session_id=${s.session_id}`),this.handleEventCancel(s).catch(t=>{u.error(this.name,`handleEventCancel failed: ${t}`)})}),this.aibotHandle.onMcpFrame((s,t)=>{const o=this.pool.getSlot(s)?.adapter;o?.deliverMcpFrameToAgent?o.deliverMcpFrameToAgent(t):u.warn(this.name,`mcp_frame: no adapter for session=${s}`)}),this.aibotHandle.onQueueClear(s=>{const t=this.pool.clearQueue(s.session_id);this.aibotHandle.sendQueueClearResult({session_id:s.session_id,canceled_event_ids:t}),this.pushQueueSnapshotForSession(s.session_id)}),this.aibotHandle.onQueueSnapshotQuery(s=>{this.replyQueueSnapshotForSession(s.session_id)}),u.info(this.name,"Connected to aibot"),this.activeEventStore){const s=await this.activeEventStore.drain();if(s.length>0){u.warn(this.name,`Recovering ${s.length} stale event(s) from previous run`);for(const t of s)u.info(this.name,`Failing stale event on startup: ${t}`),this.aibotHandle.sendEventResult({event_id:t,status:"failed",msg:"process restarted, event lost",updated_at:Date.now()})}}this.pushQueueSnapshots(),this.aibotHandle.onReconnected(()=>{this.pushQueueSnapshots()}),this.aibotHandle.onStreamRejected((s,t)=>{this.sendCtrl.markEventRejected(s)})}pushQueueSnapshots(){if(!(!this.config.eventQueue||!this.pool))for(const e of this.pool.getAllSlots())this.pushQueueSnapshotForSession(e.sessionId)}buildQueueSnapshotPayload(e){const s=this.pool?.getQueueSnapshot(e)??null,t=s?[...s.running]:[],o=s?s.running_items.map(n=>({event_id:n.event_id,...n.content_preview?{content_preview:n.content_preview}:{},...n.title?{title:n.title}:{},...n.summary?{summary:n.summary}:{},actions:[{type:"stop"}]})):[],i=s?s.queued.map(n=>({event_id:n.event_id,position:n.position,...n.content_preview?{content_preview:n.content_preview}:{},...n.title?{title:n.title}:{},...n.summary?{summary:n.summary}:{},actions:[{type:"cancel"}]})):[];if(t.length===0&&this.selfDrivenSessions.has(e)&&this.pool?.getSlot(e)){const n=`selfdrive_${e}`,r="Background task in progress";t.push(n),o.push({event_id:n,content_preview:r,title:r,summary:r,actions:[]})}return{session_id:e,running:t,running_items:o,queued:i}}pushQueueSnapshotForSession(e){if(!this.config.eventQueue||!this.pool)return;const s=this.buildQueueSnapshotPayload(e);u.info(this.name,`[queue-debug] push snapshot session=${e} running=${s.running.length} queued=${s.queued.length} running_ids=[${s.running.join(",")}]`),this.aibotHandle.sendQueueSnapshot(s)}replyQueueSnapshotForSession(e){!this.config.eventQueue||!this.pool||this.aibotHandle.sendQueueSnapshot(this.buildQueueSnapshotPayload(e))}async platformInvoke(e,s,t){return e==="file_link"?$e(s):this.aibotHandle.agentInvoke(e,s,t)}sendReplyByRuntimeConfig(e,s,t,o,i){this.indexEventSession(e,s),this.sendCtrl.sendReply(e,s,t,o,i),t&&this.conversationLog?.logOutbound?.(s,e,"reply",t)}sendStreamChunkByRuntimeConfig(e,s,t,o,i,n,r){this.indexEventSession(e,s),this.sendCtrl.sendStreamChunk(e,s,t,o,i,n,r),(t||i)&&this.conversationLog?.logOutbound?.(s,e,i?"stream_chunk_finish":"stream_chunk",t)}sendRunErrorAsChunk(e,s,t){this.sendStreamChunkByRuntimeConfig(e,s,`
12
12
 
13
13
  Error: ${t}`,1,!1)}sendEventResultWithCleanup(e,s,t,o){this.sendCtrl.sendEventResult(e,s,t,o);const i=this.eventSessionIndex.get(e);i&&(this.pool.eventComplete(e,i),this.pushQueueSnapshotForSession(i),this.conversationLog?.logResult?.(i,e,s,t),this.eventSessionIndex.delete(e)),this.inflightEvents.delete(e),this.restartCount.delete(e),s==="responded"&&(this.cachedProviderQuotaSampledAtMs=null,this.maybeQueryProviderQuota().catch(()=>{}))}async handleSessionInternalError(e){const{eventId:s,sessionId:t,errorMsg:o}=e;if(this.stopped)return;const i=this.inflightEvents.get(s);if(!i){u.warn(this.name,`[recovery] no inflight event for internalError event=${s} session=${t}; surface failure directly`),this.sendRunErrorAsChunk(s,t,o),this.sendEventResultWithCleanup(s,"failed",o,"agent_stop_failure");return}const n=(this.restartCount.get(s)??0)+1;this.restartCount.set(s,n);const r=this.config.adapterType??"acp";if(n>B){u.error(this.name,`[recovery] adapter=${r} session=${t} event=${s} restart=${n}/${B} outcome=give-up err=${o}`),this.sendRunErrorAsChunk(s,t,o),this.sendEventResultWithCleanup(s,"failed",o,"agent_stop_failure");return}u.info(this.name,`[recovery] adapter=${r} session=${t} event=${s} restart=${n}/${B} outcome=restarting err=${o}`);const a=this.pool.drainQueuedForSession(t);a.length>0&&u.info(this.name,`[recovery] session=${t} preserved ${a.length} queued sibling event(s) across restart`);try{await this.pool.removeSlot(t)}catch(l){u.warn(this.name,`[recovery] removeSlot failed session=${t}: ${l instanceof Error?l.message:String(l)}`)}if(this.stopped)return;const d=this.resolveRecoveryPrompt(r,i),c={...i,content:d};try{await this.pool.deliverInboundEvent(c)}catch(l){u.error(this.name,`[recovery] redeliver failed event=${s} session=${t}: ${l instanceof Error?l.message:String(l)}`),this.sendEventResultWithCleanup(s,"failed",l instanceof Error?l.message:String(l));return}for(const l of a){if(this.stopped)break;try{await this.pool.deliverInboundEvent(l)}catch(h){u.error(this.name,`[recovery] sibling redeliver failed event=${l.event_id} session=${t}: ${h instanceof Error?h.message:String(h)}`),this.sendEventResultWithCleanup(l.event_id,"failed",h instanceof Error?h.message:String(h))}}}resolveRecoveryPrompt(e,s){return e==="acp"?"continue":s.content}sendThinkingByRuntimeConfig(e,s,t){this.sendCtrl.sendThinking(e,s,t)}bufferStreamChunk(e,s,t,o,i){this.sendCtrl.bufferOnly(e,s,t,o,i)}flushBufferedStreamText(e){}resolveEventRuntimeConfig(e){return this.sendCtrl.resolveEventRuntimeConfig(e)}captureEventRuntimeConfig(e){this.sendCtrl.captureEventRuntimeConfig(e),this.indexEventSession(e.event_id,e.session_id),e.event_id&&this.inflightEvents.set(e.event_id,e)}indexEventSession(e,s){!e||!s||this.eventSessionIndex.set(e,s)}shouldDropToolDisplayEvent(e){return this.sendCtrl.shouldDropToolDisplayEvent(e)}shouldDropThinkingDisplayEvent(e){return this.sendCtrl.shouldDropThinkingDisplayEvent(e)}shouldDropCodexDisplayEvent(e,s){return this.sendCtrl.shouldDropCodexDisplayEvent(e,s)}logCodexEventToConversation(e){if(!this.conversationLog||e.codex_method!=="item/agentMessage/delta")return;const t=e.codex_payload?.params?.delta;if(!t)return;const o=this.eventSessionIndex.get(e.event_id)??e.session_id;this.conversationLog.append(o,{ts:Date.now(),dir:"outbound",event_id:e.event_id,kind:"codex_delta",text_len:t.length,content:t})}isAcpRawTransportEnabled(){return(this.config.adapterOptions??{}).raw_transport===!0}shouldDropAcpRawDisplayEvent(e,s){return this.sendCtrl.shouldDropAcpRawDisplayEvent(e,s)}sendAcpRawEventEnvelope(e,s,t){this.shouldDropAcpRawDisplayEvent(e,t.type)||this.aibotHandle.sendMsg({event_id:e,session_id:s,msg_type:1,content:this.buildAcpRawEventFallbackText(t),extra:{channel_data:{acp:{raw_event:t}},agent_api_origin:!0}})}buildAcpRawEventFallbackText(e){const s=String(e.type??"").trim();if(!s)return"[acp] event";switch(s){case"permission_request":return`Permission required: ${String(e.payload?.tool_title??e.payload?.tool_name??"permission request")}`;case"tool_use":return`[tool] ${String(e.payload?.tool_name??"tool")}`;case"tool_result":return"[tool result]";case"thinking":return"[thinking]";case"error":return`[error] ${String(e.payload?.message??"agent error")}`;case"result":return"[result]";default:return`[acp] ${s}`}}sendToolExecutionCard(e,s,t,o){this.sendCtrl.sendToolExecutionCard(e,s,t,o)}async handleAibotEvent(e){if(this.stopped){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:"agent shutting down",updated_at:Date.now()});return}this.logInboundConversation(e);const s=this.config.adapterType??"acp";if(this.allowlistGate&&e.sender_id&&!await this.allowlistGate.checkAccess(e.sender_id)){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",code:"access_denied",msg:`sender ${e.sender_id} is not authorized`,updated_at:Date.now()});return}const t=Me(e.extra);if(t.error){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"connector_config_invalid",msg:t.error,updated_at:Date.now()});return}const o=t.patch,i=we(e);if(i){if(i.verb===p.exec){await this.handleSessionControlCommand(i,e);return}if(i.verb===p.listSessions){await this.handleListSessionsTextCommand(e);return}if(s==="claude"){await this.handleSessionControlCommand(i,e);return}if(s==="codex"&&i.verb===p.open){await this.handleCodexSessionControlOpen(i,e);return}if(s==="pi"&&i.verb===p.open){await this.handlePiSessionControlOpen(i,e);return}if(s==="pi"&&i.verb===p.restart){await this.handlePiSessionControlRestart(e);return}if((s==="openhuman"||s==="opencode")&&i.verb===p.open){await this.handleOpenHumanSessionControlOpen(i,e);return}if(s==="codewhale"&&i.verb===p.open){await this.handleCodeWhaleSessionControlOpen(i,e);return}if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),i.verb===p.open){const a=i.args.trim();if(!a){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:g.cwdRequired,msg:"cwd is required",updated_at:Date.now()});return}try{const d=x.resolve(a);if(!(await H(d)).isDirectory()){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:g.invalidCwd,msg:`Path is not a directory: ${d}`,updated_at:Date.now()});return}}catch(d){const c=String(d?.code??""),l=c==="ENOENT"?`Directory does not exist: ${x.resolve(a)}`:c==="EACCES"||c==="EPERM"?"Directory is not accessible":`Invalid path: ${a}`;this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:g.invalidCwd,msg:l,updated_at:Date.now()});return}}if(i.verb===p.open){const a=this.bindingStore.get(e.session_id);if(a?.cwd)try{await H(a.cwd)}catch{u.info("bridge",`Stale binding detected for session ${e.session_id}: ${a.cwd} no longer exists, clearing`),this.bindingStore.delete(e.session_id),this.sessionBindings.delete(e.session_id);const d=this.pool.getSlot(e.session_id);d?.adapter instanceof A&&d.adapter.getSessionBindings().delete(e.session_id)}}if(await this.handleSessionControlForPool(i,e),s==="acp"&&i.verb===p.stop){const d=this.bindingStore.get(e.session_id)?.cwd??"";await this.pool.removeSlot(e.session_id).catch(()=>{}),this.sessionBindings.delete(e.session_id),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:`Session worker stopped for ${d}`,updated_at:Date.now()});return}if(O(i,e,this.sessionControlCtx(e.session_id),{...this.sessionControlSenders(),sendEventAck:()=>{}}),i.verb===p.open&&(await this.deferredMgr.release(e.session_id,this.deferredCallbacks()),s==="agy")){const a=this.bindingStore.get(e.session_id)?.cwd??"";a&&this.aibotHandle.sendUpdateBindingCard({session_id:e.session_id,worker_status:"ready",cwd:a,meta:this.buildAgyToolbarMeta(e.session_id)})}return}if(e.mirror_mode==="record_only"){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",updated_at:Date.now()});return}if(this.isStaleEvent(e)){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"event_stale",msg:"event is stale and will not be processed",updated_at:Date.now()});return}if(Oe.has(s)&&!this.bindingStore.get(e.session_id)?.cwd){const d=s;u.info(this.name,`[${d}] binding missing session_id=${e.session_id} event_id=${e.event_id}`),this.deferredMgr.defer(d,String(e.session_id??"").trim(),this.buildInboundEvent(e,o));const c=this.resolveBindingChannelKey(s);this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendMsg({event_id:e.event_id,session_id:e.session_id,msg_type:1,content:"Session binding missing.",extra:{channel_data:{[c]:{sessionBinding:{status:"missing",reason:"binding_missing",error_code:g.bindingMissing}}}},quoted_message_id:e.msg_id});return}if((this.config.adapterType??"acp")==="acp"){const d=String(e.content??"").trim().match(/^\/(\S+)\s*(.*)/);if(d){const[,c,l]=d,m=this.pool.getSlot(e.session_id)?.adapter;if(m?.execCommand&&(m.getSupportedCommands?.()??[]).some(_=>_.name===c||_.name===`/${c}`)){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});try{const _=await m.execCommand(c,l.trim(),e.session_id);_.status==="options"&&_.data&&this.handleExecCommandOptions(e.session_id,c,_.data),this.aibotHandle.sendEventResult({event_id:e.event_id,status:_.status==="failed"?"failed":"responded",msg:_.message,updated_at:Date.now()})}catch(_){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:_ instanceof Error?_.message:String(_),updated_at:Date.now()})}return}}}const r=this.buildInboundEvent(e,o);try{this.captureEventRuntimeConfig(r),await this.pool.deliverInboundEvent(r)}catch(a){if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.inflightEvents.delete(e.event_id),this.restartCount.delete(e.event_id),this.eventSessionIndex.delete(e.event_id),a instanceof Se)this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:a.message,updated_at:Date.now()});else throw a}}async handleCodexSessionControlOpen(e,s){const t=s.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:s.event_id,session_id:t,received_at:Date.now()}),n=(r,a)=>this.aibotHandle.sendEventResult({event_id:s.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),n("failed",{msg:"Usage: /grix open <working-directory>",code:g.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{if(await this.resolveCwdForBinding(a.cwd)===r){i(),n("responded",{msg:`Session already bound to ${a.cwd}`});return}}catch{d=!0,u.info("bridge",`Stale codex binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){i(),n("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:g.rebindForbidden});return}}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),this.deferredMgr.sendCodexDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),n("responded",{msg:`Session bound to ${r}`})}catch(r){i(),n("failed",{code:g.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handleSessionControlForPool(e,s){e.verb===p.open&&await this.bindSessionForPool(s.session_id,e.args.trim())}async replayDeferredEventsForSession(e){await this.deferredMgr.release(e,this.deferredCallbacks())}async handleSessionControlLocalActionForPool(e){if(String(e.action_type??"")!==S.sessionControl)return;const s=e.params??{};if(String(s.verb??"").trim().toLowerCase()!==p.open)return;const o=String(s.session_id??"").trim(),i=String(s.cwd??"").trim();!o||!i||await this.bindSessionForPool(o,i)}async handleCodexSessionControlLocalActionOpen(e){const s=e.params??{},t=String(s.session_id??"").trim(),o=String(s.cwd??"").trim(),i=String(s.agent_session_id??"").trim(),n=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){n("failed",void 0,g.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{const c=await this.resolveCwdForBinding(a.cwd);if(c===r){this.setResolvedAgentSessionId(t,i),n("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,c)});return}}catch{d=!0,u.info("bridge",`Stale codex binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){n("failed",void 0,g.rebindForbidden,`Session already bound to ${a.cwd}`);return}}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),this.deferredMgr.sendCodexDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),n("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){n("failed",void 0,r?.sessionControlErrorCode??g.invalidCwd,r instanceof Error?r.message:String(r))}}async handleCursorSessionControlLocalActionOpen(e){const s=e.params??{},t=String(s.session_id??"").trim(),o=String(s.cwd??"").trim(),i=String(s.agent_session_id??"").trim(),n=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){n("failed",void 0,g.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{const c=await this.resolveCwdForBinding(a.cwd);if(c===r){this.setResolvedAgentSessionId(t,i),n("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,c)});return}}catch{d=!0,u.info("bridge",`Stale cursor binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){n("failed",void 0,g.rebindForbidden,`Session already bound to ${a.cwd}`);return}}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),this.deferredMgr.sendCursorDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),n("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){n("failed",void 0,r?.sessionControlErrorCode??g.invalidCwd,r instanceof Error?r.message:String(r))}}async handlePiSessionControlOpen(e,s){const t=s.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:s.event_id,session_id:t,received_at:Date.now()}),n=(r,a)=>this.aibotHandle.sendEventResult({event_id:s.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),n("failed",{msg:"Usage: /grix open <working-directory>",code:g.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{if(await this.resolveCwdForBinding(a.cwd)===r){i(),n("responded",{msg:`Session already bound to ${a.cwd}`}),this.aibotHandle.sendMsg({event_id:s.event_id,session_id:t,msg_type:1,content:`\u2705 Session already bound to \`${a.cwd}\``,quoted_message_id:s.msg_id});return}}catch{d=!0,u.info("bridge",`Stale pi binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){i(),n("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:g.rebindForbidden});return}}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),n("responded",{msg:`Session bound to ${r}`}),this.aibotHandle.sendMsg({event_id:s.event_id,session_id:t,msg_type:1,content:`\u2705 Working directory bound: \`${r}\``,quoted_message_id:s.msg_id})}catch(r){i(),n("failed",{code:g.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handlePiSessionControlRestart(e){const s=e.session_id,t=()=>this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:s,received_at:Date.now()}),o=(r,a)=>this.aibotHandle.sendEventResult({event_id:e.event_id,status:r,...a,updated_at:Date.now()}),n=this.bindingStore.get(s)?.cwd??"";if(!n){t(),o("failed",{msg:"session binding was not found",code:g.bindingMissing});return}t(),await this.pool.removeSlot(s).catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"ready",cwd:n}),o("responded",{msg:`Session worker restarted for ${n}`})}async handlePiSessionControlRestartLocalAction(e){const s=e.params??{},t=String(s.session_id??"").trim(),i=(t?this.bindingStore.get(t):void 0)?.cwd??"",n=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t){n("failed",void 0,"session_id_required","session_id is required for restart");return}if(!i){n("failed",void 0,g.bindingMissing,"Session binding missing. Open a workspace first.");return}await this.pool.removeSlot(t).catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:i}),n("ok",{outcome:"restarted",binding:{aibotSessionId:t,cwd:i,workerStatus:"ready"}})}async bindSessionForPool(e,s){const t=String(s??"").trim();if(!e||!t)return;const o=this.pool.getOrCreateSlot(e);if(!o||(o.startPromise&&await o.startPromise,!o.adapter))return;const i=o.adapter instanceof A?o.adapter:null;if(!i?.hasSessionBinding)return;const n=await this.resolveCwdForBinding(t);i.announceDeferredComposing(e),await i.bindSession(e,n),this.sessionBindings.set(e,n),this.sessionScanCache.invalidate(),i.replayDeferredEvents(e)}deferredCallbacks(){return{captureEventRuntimeConfig:e=>this.captureEventRuntimeConfig(e),deliverInboundEvent:e=>this.pool.deliverInboundEvent(e),sendEventResult:(e,s,t)=>this.sendEventResultWithCleanup(e,s,t),sendSessionComposing:(e,s,t)=>{const o={};s&&(o.ttl_ms=t?.ttlMs??3e4,t?.activity&&(o.activity=t.activity)),this.aibotHandle.sendSessionActivitySet({session_id:e,kind:"composing",active:s,...o})}}}async handleOpenHumanSessionControlOpen(e,s){const t=()=>this.aibotHandle.sendEventAck({event_id:s.event_id,session_id:s.session_id,received_at:Date.now()});try{await this.resolveCwdForBinding(e.args.trim()),await this.handleSessionControlForPool(e,s),O(e,s,this.sessionControlCtx(s.session_id),this.sessionControlSenders()),await this.deferredMgr.release(s.session_id,this.deferredCallbacks())}catch(o){t(),this.aibotHandle.sendEventResult({event_id:s.event_id,status:"failed",code:o?.cwdErrorCode,msg:o instanceof Error?o.message:String(o),updated_at:Date.now()})}}async handleCodeWhaleSessionControlOpen(e,s){const t=s.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:s.event_id,session_id:t,received_at:Date.now()}),n=(r,a)=>this.aibotHandle.sendEventResult({event_id:s.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),n("failed",{msg:"Usage: /grix open <working-directory>",code:g.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){if(await this.resolveCwdForBinding(a.cwd)!==r){i(),n("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:g.rebindForbidden});return}i(),n("responded",{msg:`Session already bound to ${a.cwd}`});return}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),n("responded",{msg:`Session bound to ${r}`})}catch(r){i(),n("failed",{code:g.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handleCodeWhaleSessionControlLocalActionOpen(e){const s=e.params??{},t=String(s.session_id??"").trim(),o=String(s.cwd??"").trim(),i=String(s.agent_session_id??"").trim(),n=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){n("failed",void 0,g.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){const d=await this.resolveCwdForBinding(a.cwd);if(d!==r){n("failed",void 0,g.rebindForbidden,`Session already bound to ${a.cwd}`);return}this.setResolvedAgentSessionId(t,i),n("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,d)});return}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),n("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){n("failed",void 0,r?.sessionControlErrorCode??g.invalidCwd,r instanceof Error?r.message:String(r))}}normalizeClaudeModeId(e){return String(e??"").trim().toLowerCase()===C.approval?C.approval:C.fullAuto}handleExecCommandOptions(e,s,t){const o=Array.isArray(t?.options)?t.options:[];if(o.length===0)return;const n=this.bindingStore.get(e)?.cwd??"",r={};if(s==="model"){r.available_models=o.map(d=>({id:d.id,displayName:d.label}));const a=o.find(d=>d.current);a&&(r.model_id=a.id)}else if(s==="mode"){r.available_modes=o.map(d=>({id:d.id,displayName:d.label}));const a=o.find(d=>d.current);a&&(r.mode_id=a.id)}else r[`${s}_options`]=o;this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:n,meta:r})}buildAgyToolbarMeta(e){const s=this.bindingStore.get(e);if(!s)return;const t=te(this.config.agent.command),o=t.length>0?t[0].id:"";let i=s.modelId||this.globalConfigStore?.get(this.name)?.modelId||o;t.length>0&&!t.some(r=>r.id===i)&&(i=o);const n=ie();return{model_id:i,currentModelId:i,available_models:t.map(r=>({id:r.id,displayName:r.displayName})),...n.plan!==void 0&&{plan:n.plan},...n.quota_exhausted!==void 0&&{quota_exhausted:n.quota_exhausted},...n.quota_reset_at!==void 0&&{quota_reset_at:n.quota_reset_at},...n.available_credits!==void 0&&{available_credits:n.available_credits}}}async handleAgySetModel(e,s){const t=e.params??{},o=String(t.model_id??t.modelId??"").trim();if(!s){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"session_id_required",error_msg:"session_id is required for set_model"});return}if(!o){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"set_model_invalid",error_msg:"model_id is required"});return}const i=this.bindingStore.get(s);if(!i?.cwd){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.bindingMissing,error_msg:"session binding was not found"});return}i.modelId!==o&&(this.bindingStore.setModelId(s,o),this.globalConfigStore?.set(this.name,{modelId:o})),this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"ready",cwd:i.cwd,meta:this.buildAgyToolbarMeta(s)}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"model_set",model_id:o,binding:{cwd:i.cwd,model_id:o}}})}buildClaudeToolbarMeta(e){const s=this.bindingStore.get(e);if(!s)return;const t=D(),o=t.length>0?t[0].id:"";let i=s.modelId||this.globalConfigStore?.get(this.name)?.modelId||o;t.length>0&&!t.some(l=>l.id===i)&&(i=o);const n=this.normalizeClaudeModeId(s.modeId),r={model_id:i,mode_id:n,currentModelId:i,currentModeId:n,available_models:t.map(l=>({id:l.id,displayName:l.displayName}))},a=this.pool.getSlot(e),d=a?.adapter instanceof T?a.adapter.getSessionState():null;(d?.rateLimits?.fiveHour||d?.rateLimits?.sevenDay)&&(this.cachedClaudeRateLimitState=d);const c=d??this.getFreshClaudeRateLimitState();if(c){const l=c.rateLimits;l&&(l.fiveHour||l.sevenDay)&&(r.rate_limits={...l.fiveHour?{fiveHour:l.fiveHour}:{},...l.sevenDay?{sevenDay:l.sevenDay}:{},sampledAt:c.sampledAt||Date.now()})}if(d){const l=d.contextWindow;l.usedPercentage!=null&&(r.context_window={usedPercentage:l.usedPercentage,remainingPercentage:l.remainingPercentage})}if(!r.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const l=this.providerQuotaToRateLimits(this.cachedProviderQuota);l&&(r.rate_limits=l),u.info(this.name,`[toolbar-meta] provider quota fallback: hasCached=${!!this.cachedProviderQuota} fromProvider=${JSON.stringify(l)}`)}return r.rate_limits&&u.info(this.name,`[toolbar-meta] rate_limits included: ${JSON.stringify(r.rate_limits)}`),r}providerQuotaToCodexRateLimits(e){const s=e.tiers.find(i=>i.name==="five_hour"),t=e.tiers.find(i=>i.name==="weekly_limit"),o=i=>i||null;if(s||t){const i={credits:{hasCredits:!0,unlimited:!1,balance:null},sampledAt:this.cachedProviderQuotaSampledAtMs??Date.now()};let n=0,r=0,a=0,d=0;return s&&(i.primary={usedPercent:s.usedPercent,windowMinutes:300,resetsAt:o(s.resetsAt)},n=s.usedPercent,r=300),t&&(i.secondary={usedPercent:t.usedPercent,windowMinutes:10080,resetsAt:o(t.resetsAt)},a=t.usedPercent,d=10080),{rateLimits:i,primaryPercent:n,secondaryPercent:a,primaryWindowMin:r,secondaryWindowMin:d}}if(e.balance){const i=e.balance,n=i.total&&i.total>0?Math.min(100,(i.used??i.total-i.remaining)/i.total*100):0;return{rateLimits:{primary:{usedPercent:n,windowMinutes:0,resetsAt:null},secondary:{usedPercent:0,windowMinutes:0,resetsAt:null},credits:{hasCredits:!0,unlimited:!1,balance:i.remaining},sampledAt:this.cachedProviderQuotaSampledAtMs??Date.now()},primaryPercent:n,secondaryPercent:0,primaryWindowMin:0,secondaryWindowMin:0}}return null}providerQuotaToRateLimits(e){const s=i=>{if(!i)return 0;const n=Date.parse(i);return Number.isFinite(n)?Math.floor(n/1e3):0},t=e.tiers.find(i=>i.name==="five_hour"),o=e.tiers.find(i=>i.name==="weekly_limit");if(t||o)return{...t?{fiveHour:{usedPercentage:t.usedPercent,resetsAt:s(t.resetsAt)}}:{},...o?{sevenDay:{usedPercentage:o.usedPercent,resetsAt:s(o.resetsAt)}}:{},sampledAt:this.cachedProviderQuotaSampledAtMs??Date.now()};if(e.balance){const i=e.balance;return{credit:{remaining:i.remaining,total:i.total,used:i.used,unit:i.unit,resetsAt:i.resetsAt?s(i.resetsAt):0},planName:e.planName,sampledAt:this.cachedProviderQuotaSampledAtMs??Date.now()}}return null}async resolveCwdForBinding(e){const s=String(e??"").trim();if(process.platform!=="win32"&&(/^[a-zA-Z]:[\\/]/.test(s)||/^\\\\/.test(s))){const i=new Error(`Specified path is not valid on this host: ${s}`);throw i.cwdErrorCode=g.invalidCwd,i}const t=x.resolve(s);let o;try{o=await H(t)}catch(i){const n=String(i?.code??"");if(n==="ENOENT"){const r=new Error(`Specified path does not exist: ${t}`);throw r.cwdErrorCode=g.invalidCwd,r}if(n==="EACCES"||n==="EPERM"){const r=new Error("Specified path is not accessible.");throw r.cwdErrorCode=g.invalidCwd,r}throw i}if(!o.isDirectory()){const i=new Error("Specified path is not a directory.");throw i.cwdErrorCode=g.invalidCwd,i}try{return await G(t)}catch{return t}}getClaudeWorkerStatus(e){const s=this.pool.getSlot(e);return s?s.state==="starting"?"starting":s.state==="stopped"?"stopped":s.adapter.getStatus().busy?"busy":"ready":"stopped"}refreshClaudeWorkerStatusCard(e,s){const t=this.getClaudeWorkerStatus(e);return this.claudeWorkerStatus.set(e,t),this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:t,cwd:s,meta:this.buildClaudeToolbarMeta(e)}),t}async ensureSlotStarted(e,s=6e4){const t=this.pool.getOrCreateSlot(e);if(!t)throw new Error("Failed to allocate session slot");t.startPromise&&(u.info(this.name,`ensureSlotStarted: awaiting startPromise for session=${e}`),await Promise.race([t.startPromise,new Promise((o,i)=>setTimeout(()=>i(new Error(`ensureSlotStarted timeout (${s}ms) session=${e}`)),s))]),u.info(this.name,`ensureSlotStarted: startPromise resolved for session=${e}`))}resolveAgentSessionId(e){switch(this.config.adapterType??"acp"){case"claude":return e.claudeSessionId;case"codex":return e.codexThreadId;case"pi":return e.piSessionPath;case"codewhale":return e.codewhaleThreadId;case"agy":return e.agyConversationId;default:return e.acpSessionId}}providerKeyForAdapter(){const e=this.config.adapterType??"acp";switch(e){case"claude":case"codex":case"pi":case"codewhale":return e;default:return"acp"}}setResolvedAgentSessionId(e,s){const t=String(e??"").trim(),o=String(s??"").trim();if(!t||!o)return;switch(this.config.adapterType??"acp"){case"claude":this.bindingStore.setClaudeSessionId(t,o);break;case"codex":this.bindingStore.setCodexThreadId(t,o);break;case"pi":this.bindingStore.setPiSessionPath(t,o);break;case"codewhale":this.bindingStore.setCodeWhaleThreadId(t,o);break;case"agy":this.bindingStore.setAgyConversationId(t,o);break;default:this.bindingStore.setAcpSessionId(t,o);break}this.sessionScanCache.invalidate()}normalizePathForCompare(e){const s=String(e??"").trim();if(!s)return"";const t=x.resolve(s);return process.platform==="win32"?t.toLowerCase():t}ensureImportedAgentSession(e,s){const t=String(e??"").trim();if(!t)return;const o=this.normalizePathForCompare(s);let i="";const n=this.config.adapterType??"acp";if(n==="codex"?i=this.sessionScanCache.get().find(d=>d.threadId===t)?.cwd??"":n==="claude"?i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??"":n==="acp"&&(i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??""),!i){for(const[,a]of this.bindingStore.entries())if(this.resolveAgentSessionId(a)===t){i=a.cwd??"";break}}if(!i){const a=new Error(`agent session not found: ${t}`);throw a.sessionControlErrorCode=g.invalidAgentSession,a}const r=this.normalizePathForCompare(i);if(r&&o&&r!==o){const a=new Error(`agent session cwd mismatch: expected ${s}, got ${i}`);throw a.sessionControlErrorCode=g.invalidAgentSession,a}}buildOpenedBindingResult(e,s,t="ready"){const o=this.bindingStore.get(e),i=o?String(this.resolveAgentSessionId(o)??"").trim():"",n={aibotSessionId:e,providerKey:this.providerKeyForAdapter(),cwd:s,workerStatus:t};return i&&(n.bindingId=i,n.agentSessionId=i),n}hasDiskScanner(){const e=this.config.adapterType??"acp";return e==="codex"||e==="claude"||e==="acp"}async handleListSessionsTextCommand(e){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});const s=this.config.adapterType??"acp",t=new Map;for(const a of this.pool.getAllSlots())t.set(a.sessionId,a);const o=Array.from(this.bindingStore.entries()),i=new Map;for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c){const l=t.get(a),h=l?l.adapter.getStatus().busy?"busy":"ready":"inactive";i.set(c,{aibotSessionId:a,workerStatus:h})}}const n=[],r=new Set;if(s==="codex"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.threadId);const c=i.get(d.threadId);d.title&&n.push(` Title: ${d.title}`),n.push(` Agent: ${d.threadId}`),c&&n.push(` AIBot: ${c.aibotSessionId}`),n.push(` CWD: ${d.cwd||"-"}`),n.push(` State: ${c?.workerStatus??(d.archived?"archived":"inactive")}`),n.push(` Created: ${new Date(d.createdAt).toISOString()}`),n.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),n.push("---")}}else if(s==="claude"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&n.push(` Title: ${d.title}`),n.push(` Agent: ${d.sessionId}`),c&&n.push(` AIBot: ${c.aibotSessionId}`),n.push(` CWD: ${d.cwd||"-"}`),n.push(` State: ${c?.workerStatus??"inactive"}`),n.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),n.push("---")}}else if(s==="acp"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&n.push(` Title: ${d.title}`),n.push(` Agent: ${d.sessionId}`),c&&n.push(` AIBot: ${c.aibotSessionId}`),n.push(` CWD: ${d.cwd||"-"}`),n.push(` State: ${c?.workerStatus??"inactive"}`),n.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),n.push("---")}}for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c&&r.has(c))continue;const l=t.get(a),h=l?l.adapter.getStatus().busy?"busy":"ready":"closed";n.push(` Title: ${c?c.slice(0,8)+"\u2026":a.slice(0,8)+"\u2026"}`),n.push(` AIBot: ${a}`),c&&n.push(` Agent: ${c}`),n.push(` CWD: ${d.cwd??"-"}`),n.push(` State: ${h}`),n.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),n.push("---")}if(n.length===0){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:"No sessions found.",updated_at:Date.now()});return}this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:`Sessions (${n.filter(a=>a==="---").length}):
14
14
  ${n.join(`
15
15
  `)}`,updated_at:Date.now()})}async handleListSessionsLocalAction(e){const s=this.config.adapterType??"acp",t=new Map;for(const a of this.pool.getAllSlots())t.set(a.sessionId,a);const o=Array.from(this.bindingStore.entries()),i=new Map;for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c){const l=t.get(a),h=l?l.adapter.getStatus().busy?"busy":"ready":"inactive";i.set(c,{aibotSessionId:a,workerStatus:h,bindingUpdatedAt:d.updatedAt??0})}}const n=[],r=new Set;if(s==="codex"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.threadId);const c=i.get(d.threadId),l={agentSessionId:d.threadId,cwd:d.cwd||null,workerStatus:c?.workerStatus??(d.archived?"archived":"inactive"),createdAt:d.createdAt,updatedAt:d.updatedAt,archived:d.archived};c&&(l.aibotSessionId=c.aibotSessionId),d.title&&(l.title=d.title),n.push(l)}}else if(s==="claude"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId),l={agentSessionId:d.sessionId,cwd:d.cwd||null,workerStatus:c?.workerStatus??"inactive",updatedAt:d.updatedAt};c&&(l.aibotSessionId=c.aibotSessionId),d.title&&(l.title=d.title),n.push(l)}}else if(s==="acp"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId),l=c&&c.bindingUpdatedAt>0?c.bindingUpdatedAt:d.updatedAt,h={agentSessionId:d.sessionId,cwd:d.cwd||null,agentType:d.agentType,workerStatus:c?.workerStatus??"inactive",updatedAt:l};c&&(h.aibotSessionId=c.aibotSessionId),d.title&&(h.title=d.title),d.createdAt&&(h.createdAt=d.createdAt),n.push(h)}}for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c&&r.has(c))continue;const l=t.get(a),h=l?l.adapter.getStatus().busy?"busy":"ready":"closed",m={aibotSessionId:a,cwd:d.cwd??null,workerStatus:h,updatedAt:d.updatedAt,title:c?`${c.slice(0,8)}\u2026`:`${a.slice(0,8)}\u2026`};c&&(m.agentSessionId=c),n.push(m)}n.sort((a,d)=>d.updatedAt-a.updatedAt),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"sessions_listed",sessions:n,total:n.length}})}async handleSessionControlCommand(e,s){const t=s.session_id,o=(i,n,r)=>{this.aibotHandle.sendEventResult({event_id:s.event_id,status:i,...n?{msg:n}:{},...r?{code:r}:{},updated_at:Date.now()})};this.aibotHandle.sendEventAck({event_id:s.event_id,session_id:t,received_at:Date.now()});try{switch(e.verb){case p.open:{await I().catch(()=>{});const i=e.args.trim();if(!i){o("failed","Usage: /grix open <working-directory>",g.cwdRequired);return}let n="";try{n=await this.resolveCwdForBinding(i)}catch(a){o("failed",a instanceof Error?a.message:String(a),g.invalidCwd);return}const r=this.bindingStore.get(t);if(r?.cwd){const a=await this.resolveCwdForBinding(r.cwd);if(a!==n){o("failed","session binding cannot be changed to another working directory",g.rebindForbidden);return}this.bindingStore.ensureModeId(t,C.fullAuto),this.sessionBindings.set(t,a),await this.ensureSlotStarted(t),await this.deferredMgr.release(t,this.deferredCallbacks()),this.refreshClaudeWorkerStatusCard(t,a),o("responded",`Working directory already bound: ${a}`);return}this.bindingStore.set(t,n,{modeId:C.fullAuto}),this.sessionBindings.set(t,n),await this.ensureSlotStarted(t),await this.deferredMgr.release(t,this.deferredCallbacks()),this.refreshClaudeWorkerStatusCard(t,n),o("responded",`Session bound to ${n}`);return}case p.where:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",g.bindingMissing);return}o("responded",`Working directory: ${i.cwd}`);return}case p.status:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",g.bindingMissing);return}const n=this.normalizeClaudeModeId(i.modeId),r=this.getClaudeWorkerStatus(t);o("responded",`Status: worker=${r} mode=${n} cwd=${i.cwd}`);return}case p.stop:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",g.bindingMissing);return}await this.pool.removeSlot(t),this.claudeWorkerStatus.set(t,"stopped"),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"stopped",cwd:i.cwd}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"stopped",cwd:i.cwd,meta:this.buildClaudeToolbarMeta(t)}),o("responded",`Session worker stopped for ${i.cwd}`);return}case p.restart:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",g.bindingMissing);return}await this.pool.removeSlot(t),await this.ensureSlotStarted(t),this.refreshClaudeWorkerStatusCard(t,i.cwd),o("responded",`Session worker restarted for ${i.cwd}`);return}case p.setMode:{const i=e.args.trim();if(!i){o("failed","Usage: /grix set_mode <mode-id>",y.modeInvalid);return}const n=this.normalizeClaudeModeId(i);if(n!==i.toLowerCase()){o("failed","set mode_id is invalid",y.modeInvalid);return}const r=this.bindingStore.get(t);if(!r?.cwd){o("failed","session binding was not found",g.bindingMissing);return}if(this.normalizeClaudeModeId(r.modeId)===n){o("responded",`Mode unchanged: ${n}`);return}if(this.getClaudeWorkerStatus(t)==="busy"){o("failed","\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148 /grix stop \u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u5F0F",g.workerBusy);return}this.bindingStore.setModeId(t,n),await this.pool.removeSlot(t),await this.ensureSlotStarted(t),this.refreshClaudeWorkerStatusCard(t,r.cwd),o("responded",`Mode set to ${n}`);return}case p.setModel:{const i=e.args.trim();if(!i){o("failed","Usage: /grix set_model <model-id>");return}const n=this.bindingStore.get(t);if(!n?.cwd){o("failed","session binding was not found",g.bindingMissing);return}if((n.modelId??"")===i){o("responded",`Model unchanged: ${i}`);return}if(this.getClaudeWorkerStatus(t)==="busy"){o("failed","\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148 /grix stop \u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u578B",g.workerBusy);return}this.bindingStore.setModelId(t,i),this.globalConfigStore?.set(this.name,{modelId:i}),await this.pool.removeSlot(t),await this.ensureSlotStarted(t),this.refreshClaudeWorkerStatusCard(t,n.cwd),o("responded",`Model set to ${i}`);return}case p.listOptions:{const i=D(),n=this.bindingStore.get(t),r=i.map(d=>d.id).join(", "),a=`${C.fullAuto}, ${C.approval}`;o("responded",`Modes (current: ${this.normalizeClaudeModeId(n?.modeId)}): ${a}
16
- Models (current: ${n?.modelId??"default"}): ${r}`);return}case p.exec:{const[i,...n]=e.args.trim().split(/\s+/);if(!i){o("failed","Usage: /grix exec <command> [args]",g.verbInvalid);return}const a=this.pool.getSlot(t)?.adapter;if(!a?.execCommand){o("failed","Agent does not support command execution",g.verbInvalid);return}const d=a.getSupportedCommands?.()??[];if(!d.some(c=>c.name===i)){o("failed",`Unknown command: ${i}. Supported: ${d.map(c=>c.name).join(", ")}`,g.verbInvalid);return}try{const c=await a.execCommand(i,n.join(" "),t);o(c.status==="ok"?"responded":"failed",c.message??`${i} ${c.status}`,c.status==="ok"?void 0:g.runtimeError)}catch(c){o("failed",`exec error: ${c instanceof Error?c.message:c}`,g.runtimeError)}return}default:o("failed",`Unsupported command for Claude: /grix ${e.verb}`,g.verbInvalid)}}catch(i){o("failed",i instanceof Error?i.message:String(i),g.runtimeError)}}async handleSessionControlLocalAction(e){const s=String(e.action_type??"").trim();if(s!==S.sessionControl&&s!==S.setMode&&s!=="set_mode"&&s!==S.setModel&&s!=="set_model")return!1;const t=e.params??{},o=String(t.session_id??"").trim(),i=s===S.setMode?p.setMode:s===S.setModel?p.setModel:String(t.verb??"").trim().toLowerCase();if(u.info(this.name,`handleSessionControlLocalAction verb=${i} action_id=${e.action_id} session_id=${o}`),!o)return this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:y.localActionRouteMissing,error_msg:"local action session_id is required"}),!0;const n=a=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:a})},r=(a,d)=>{u.warn(this.name,`session_control local_action failed action_id=${e.action_id} session_id=${o} verb=${i} code=${a} msg=${d}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:a,error_msg:d})};try{switch(i){case p.open:{await I().catch(()=>{});const a=String(t.cwd??"").trim(),d=String(t.agent_session_id??"").trim();if(!a)return r(g.cwdRequired,"session control cwd is required"),!0;u.info(this.name,`handleSessionControlLocalAction open cwd=${a} session_id=${o}`);const c=await this.resolveCwdForBinding(a);this.ensureImportedAgentSession(d,c);const l=this.bindingStore.get(o);if(l?.cwd){const h=await this.resolveCwdForBinding(l.cwd);return h!==c?(r(g.rebindForbidden,"session binding cannot be changed to another working directory"),!0):(this.bindingStore.ensureModeId(o,C.fullAuto),this.setResolvedAgentSessionId(o,d),this.sessionBindings.set(o,h),await this.ensureSlotStarted(o),await this.replayDeferredEventsForSession(o),this.refreshClaudeWorkerStatusCard(o,h),n({outcome:"opened",binding:{...this.buildOpenedBindingResult(o,h),mode_id:this.normalizeClaudeModeId(this.bindingStore.get(o)?.modeId)}}),!0)}return this.bindingStore.set(o,c,{modeId:C.fullAuto}),this.setResolvedAgentSessionId(o,d),this.sessionBindings.set(o,c),await this.ensureSlotStarted(o),await this.replayDeferredEventsForSession(o),this.refreshClaudeWorkerStatusCard(o,c),n({outcome:"opened",binding:{...this.buildOpenedBindingResult(o,c),mode_id:this.normalizeClaudeModeId(this.bindingStore.get(o)?.modeId)}}),!0}case p.status:case p.where:{const a=this.bindingStore.get(o);return a?.cwd?(n({outcome:i,binding:{cwd:a.cwd,mode_id:this.normalizeClaudeModeId(a.modeId),worker_status:this.getClaudeWorkerStatus(o)}}),!0):(r(g.bindingMissing,"session binding was not found"),!0)}case p.stop:{const a=this.bindingStore.get(o);return a?.cwd?(await this.pool.removeSlot(o),n({outcome:"stopped",binding:{cwd:a.cwd,mode_id:this.normalizeClaudeModeId(a.modeId),worker_status:"stopped"}}),!0):(r(g.bindingMissing,"session binding was not found"),!0)}case p.restart:{const a=this.bindingStore.get(o);return a?.cwd?(await this.pool.removeSlot(o),await this.ensureSlotStarted(o),this.refreshClaudeWorkerStatusCard(o,a.cwd),n({outcome:"restarted",binding:{cwd:a.cwd,mode_id:this.normalizeClaudeModeId(a.modeId)}}),!0):(r(g.bindingMissing,"session binding was not found"),!0)}case p.setMode:{const a=String(t.mode_id??t.modeId??"").trim();if(!a)return r(y.modeInvalid,"set mode_id is invalid"),!0;const d=this.normalizeClaudeModeId(a);if(d!==a.toLowerCase())return r(y.modeInvalid,"set mode_id is invalid"),!0;const c=this.bindingStore.get(o);if(!c?.cwd)return r(g.bindingMissing,"session binding was not found"),!0;if(this.normalizeClaudeModeId(c.modeId)!==d){if(this.getClaudeWorkerStatus(o)==="busy")return r(g.workerBusy,"\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u5F0F"),!0;this.bindingStore.setModeId(o,d),await this.pool.removeSlot(o),await this.ensureSlotStarted(o),this.refreshClaudeWorkerStatusCard(o,c.cwd)}return n({outcome:"mode_set",mode_id:d,binding:{cwd:c.cwd,mode_id:d}}),!0}case p.setModel:{const a=String(t.model_id??t.modelId??"").trim();if(!a)return r("set_model_invalid","model_id is required"),!0;const d=this.bindingStore.get(o);if(!d?.cwd)return r(g.bindingMissing,"session binding was not found"),!0;if((d.modelId??"")!==a){if(this.getClaudeWorkerStatus(o)==="busy")return r(g.workerBusy,"\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u578B"),!0;this.bindingStore.setModelId(o,a),this.globalConfigStore?.set(this.name,{modelId:a}),await this.pool.removeSlot(o),await this.ensureSlotStarted(o),this.refreshClaudeWorkerStatusCard(o,d.cwd)}return n({outcome:"model_set",model_id:a,binding:{cwd:d.cwd,model_id:a}}),!0}case p.listOptions:{const a=D(),d=this.pool.getSlot(o);return n({modes:[C.fullAuto,C.approval],currentModeId:this.normalizeClaudeModeId(this.bindingStore.get(o)?.modeId),models:a.map(c=>({modelId:c.id,name:c.displayName})),currentModelId:this.bindingStore.get(o)?.modelId??"",available_models:a.map(c=>({id:c.id,displayName:c.displayName})),agent_commands:d?.adapter?.getSupportedCommands?.()??[]}),!0}case p.exec:{const[a,...d]=String(t.args??"").trim().split(/\s+/);if(!a)return r(g.verbInvalid,"Usage: exec <command> [args]"),!0;await this.ensureSlotStarted(o);const l=this.pool.getSlot(o)?.adapter;if(!l?.execCommand)return r(g.verbInvalid,"Agent does not support command execution"),!0;const h=l.getSupportedCommands?.()??[];if(!h.some(m=>m.name===a))return r(g.verbInvalid,`Unknown command: ${a}. Supported: ${h.map(m=>m.name).join(", ")}`),!0;try{const m=await l.execCommand(a,d.join(" "),o);m.status==="ok"?n({outcome:"exec",command:a,message:m.message,data:m.data}):r(g.runtimeError,m.message??`${a} failed`)}catch(m){r(g.runtimeError,`exec error: ${m instanceof Error?m.message:m}`)}return!0}default:return r(g.verbInvalid,`session control verb ${i} is not supported`),!0}}catch(a){const d=a instanceof Error&&a.cwdErrorCode?a.cwdErrorCode:a instanceof Error&&a.sessionControlErrorCode?a.sessionControlErrorCode:g.runtimeError;return u.error(this.name,`handleSessionControlLocalAction error verb=${i} session_id=${o}: ${a instanceof Error?a.message:a}`),r(d,a instanceof Error?a.message:String(a)),!0}}async handleEventCancel(e){const{event_id:s,session_id:t}=e;if(u.info(this.name,`handleEventCancel start event_id=${s} session_id=${t}`),this.pool.cancelEvent(s,t)){if(!(this.pool.getSlot(t)?.adapter?.getActiveEventIds().includes(s)??!1)){this.sendEventResultWithCleanup(s,"canceled","canceled"),this.aibotHandle.sendEventCancelResult({event_id:s,accepted:!0,final_state:"canceled"});return}await this.waitForEventDone(s,t,15e3),this.aibotHandle.sendEventCancelResult({event_id:s,accepted:!0,final_state:"canceled"}),this.pushQueueSnapshotForSession(t);return}this.aibotHandle.sendEventCancelResult({event_id:s,accepted:!1,reason:"event not found or not cancelable"})}waitForEventDone(e,s,t){return new Promise(o=>{const i=this.pool.getSlot(s);if(!i?.adapter){o();return}const n=setTimeout(()=>{i.adapter.removeListener("eventDone",r),o()},t),r=a=>{a===e&&(clearTimeout(n),i.adapter.removeListener("eventDone",r),o())};i.adapter.on("eventDone",r)})}handleAibotStop(e){if(u.info(this.name,`[stop-trace] handleAibotStop begin session=${e.session_id} event=${e.event_id} stop_id=${e.stop_id||"-"} adapterType=${this.config.adapterType??"acp"}`),this.aibotHandle.sendEventStopAck({stop_id:e.stop_id,event_id:e.event_id,accepted:!0,updated_at:Date.now()}),this.pool.removeQueuedEvent(e.session_id,e.event_id)){u.info(this.name,`[stop-trace] handleAibotStop removed queued(not-running) event -> stopResult(stopped) session=${e.session_id} event=${e.event_id} stop_id=${e.stop_id||"-"}`),this.pushQueueSnapshotForSession(e.session_id),this.aibotHandle.sendEventStopResult({stop_id:e.stop_id,event_id:e.event_id,status:"stopped",updated_at:Date.now()});return}const s=this.pool.getSlot(e.session_id),t=s?.adapter?.getStatus().busy??!1,o=(this.config.adapterType??"acp")==="acp",i=s?.adapter?.getActiveEventIds,n=typeof i=="function"?i.call(s.adapter):[],r=n.length>0?n.includes(e.event_id):t,a=(o||(this.config.adapterType??"acp")==="codex"||(this.config.adapterType??"acp")==="claude")&&r;if(u.info(this.name,`[stop-trace] handleAibotStop decision session=${e.session_id} event=${e.event_id} slotExists=${!!s?.adapter} busy=${t} activeIds=[${n.join(",")}] stoppingActiveEvent=${r} killOnStop=${a}`),r&&this.sendCtrl.markEventStopped(e.event_id),s?.adapter&&t){const d=a?this.pool.drainQueuedForSession(e.session_id):[];let c=!1,l=null;const h=b=>{c||(c=!0,l&&(clearTimeout(l),l=null),s.adapter.removeListener("eventDone",m),u.info(this.name,`[stop-trace] handleAibotStop ${b} -> stopResult(stopped) session=${e.session_id} event=${e.event_id} stop_id=${e.stop_id||"-"} killOnStop=${a}`),this.aibotHandle.sendEventStopResult({stop_id:e.stop_id,event_id:e.event_id,status:"stopped",updated_at:Date.now()}),a&&this.killAndResumeStopSlot(e.session_id,d))},m=b=>{b===e.event_id&&h("eventDone")};s.adapter.on("eventDone",m),l=setTimeout(()=>h("timeout"),Ue),this.pool.deliverStopEvent(e.event_id,e.session_id)}else u.info(this.name,`[stop-trace] handleAibotStop slot-not-busy -> immediate stopResult(stopped) session=${e.session_id} event=${e.event_id} slotExists=${!!s?.adapter}`),this.pool.deliverStopEvent(e.event_id,e.session_id),this.aibotHandle.sendEventStopResult({stop_id:e.stop_id,event_id:e.event_id,status:"stopped",updated_at:Date.now()});(this.config.adapterType??"acp")==="pi"&&this.aibotHandle.sendText({event_id:e.event_id,session_id:e.session_id,content:"/stop",msg_type:0})}async killAndResumeStopSlot(e,s){if(!this.stopped){u.info(this.name,`[stop-trace] killAndResumeStopSlot begin session=${e} siblings=${s.length} -> removeSlot (kill process group)`);try{await this.pool.removeSlot(e),u.info(this.name,`[stop-trace] killAndResumeStopSlot removeSlot done session=${e} (process killed) -> redeliver ${s.length} sibling(s)`)}catch(t){u.warn(this.name,`[acp-stop] removeSlot failed session=${e}: ${t instanceof Error?t.message:String(t)}`)}if(!this.stopped)for(const t of s){if(this.stopped)break;try{await this.pool.deliverInboundEvent(t)}catch(o){u.error(this.name,`[acp-stop] sibling redeliver failed event=${t.event_id} session=${e}: ${o instanceof Error?o.message:String(o)}`),this.sendEventResultWithCleanup(t.event_id,"failed",o instanceof Error?o.message:String(o))}}}}handleAibotRevoke(e){if(!e.event_id||!this.revokeHandler.checkAndTrack(e.event_id))return;const s=e.event_id,t=e.session_id;if(t&&this.pool.cancelEvent(s,t)){(this.pool.getSlot(t)?.adapter?.getActiveEventIds().includes(s)??!1)||this.sendEventResultWithCleanup(s,"canceled","revoked");return}if(this.deferredMgr.removeEvent(s)){this.aibotHandle.sendEventResult({event_id:s,status:"canceled",msg:"revoked",updated_at:Date.now()});return}this.pool.deliverStopEvent(s,t||void 0)}async handleAibotLocalAction(e){const s=e.action_type??"",t=String((e.params??{}).session_id??""),o=String((e.params??{}).verb??"").trim().toLowerCase();u.debug(this.name,`local_action received action_type=${s} verb=${o||"-"} action_id=${e.action_id} session_id=${t}`);const i=(this.config.adapterType??"acp")==="claude";if(s===S.sessionControl&&o===p.exec&&await this.handleSessionControlLocalAction(e))return;if(s===S.sessionControl&&o===p.listSessions){await this.handleListSessionsLocalAction(e);return}if(i&&(s===S.interactionReply||s==="exec_approve"||s==="exec_reject")&&(await this.pool.deliverLocalAction(e)).handled||i&&await this.handleSessionControlLocalAction(e))return;if(s===S.sessionControl){const c=this.config.adapterType??"acp",l=c==="codex",h=c==="pi",m=String((e.params??{}).verb??"").trim().toLowerCase();if(l&&m===p.open){await this.handleCodexSessionControlLocalActionOpen(e);return}if(c==="cursor"&&m===p.open){await this.handleCursorSessionControlLocalActionOpen(e);return}if(l&&m==="restart"){const v=this.bindingStore.get(t)?.cwd??"";await this.pool.removeSlot(t).catch(()=>{}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"restarted",binding:{aibotSessionId:t,cwd:v,workerStatus:"ready"}}});return}if(h&&m===p.open){try{const f=e.params??{},v=String(f.cwd??"").trim();if(!v){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.cwdRequired,error_msg:"session cwd is required"});return}const w=await this.resolveCwdForBinding(v),k=String(f.agent_session_id??"").trim();this.ensureImportedAgentSession(k,w),this.bindingStore.set(t,w),this.setResolvedAgentSessionId(t,k),this.sessionBindings.set(t,w),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:w}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"opened",binding:this.buildOpenedBindingResult(t,w)}})}catch(f){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:f?.sessionControlErrorCode??f?.cwdErrorCode??g.invalidCwd,error_msg:f instanceof Error?f.message:String(f)})}return}if(h&&m===p.restart){await this.handlePiSessionControlRestartLocalAction(e);return}if((c==="openhuman"||c==="opencode")&&m===p.open){try{const f=e.params??{},v=String(f.cwd??"").trim();if(!v){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.cwdRequired,error_msg:"session cwd is required"});return}const w=await this.resolveCwdForBinding(v),k=String(f.agent_session_id??"").trim();this.ensureImportedAgentSession(k,w),this.bindingStore.set(t,w),this.setResolvedAgentSessionId(t,k),this.sessionBindings.set(t,w),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"opened",binding:this.buildOpenedBindingResult(t,w)}})}catch(f){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:f?.sessionControlErrorCode??f?.cwdErrorCode??g.invalidCwd,error_msg:f instanceof Error?f.message:String(f)})}return}if(c==="codewhale"&&m===p.open){await this.handleCodeWhaleSessionControlLocalActionOpen(e);return}if(c==="acp"&&m===p.stop){const v=this.bindingStore.get(t)?.cwd??"";await this.pool.removeSlot(t).catch(()=>{}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"stopped",binding:{aibotSessionId:t,cwd:v,workerStatus:"stopped"}}});return}try{if(m===p.open){const f=e.params??{},v=String(f.cwd??"").trim();if(!v){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.cwdRequired,error_msg:"session cwd is required"});return}const w=String(f.agent_session_id??"").trim();if(w){const k=await this.resolveCwdForBinding(v);this.ensureImportedAgentSession(w,k)}}await this.handleSessionControlLocalActionForPool(e)}catch(f){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:f?.sessionControlErrorCode??f?.cwdErrorCode??g.runtimeError,error_msg:f instanceof Error?f.message:String(f)});return}if(m===p.open){const f=e.params??{},v=await this.resolveCwdForBinding(String(f.cwd??"").trim());this.setResolvedAgentSessionId(t,String(f.agent_session_id??"").trim()),c==="agy"&&(this.bindingStore.set(t,v),this.sessionBindings.set(t,v)),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"opened",binding:this.buildOpenedBindingResult(t,v)}}),(this.config.adapterType??"acp")==="agy"&&this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:v,meta:this.buildAgyToolbarMeta(t)})}else Ce(e,this.sessionControlCtx(t),this.sessionControlSenders());return}if(s==="file_list"){const c=Date.now(),l=t?this.bindingStore.get(t)?.cwd:void 0,h=e.params??{},m=String(h.parent_id??"").trim(),b=Array.isArray(h.allowed_extensions)?h.allowed_extensions.filter(v=>typeof v=="string").map(v=>v.trim()).filter(v=>v.length>0):[];u.info("file-list-diag",`plugin << recv action_id=${e.action_id} session_id=${t} parent_id=${m||"<root>"} show_hidden=${!!h.show_hidden} ext_count=${b.length} bound_cwd=${l??"<none>"}`);const _=await Re({parent_id:m||null,session_id:t,show_hidden:!!h.show_hidden,allowed_extensions:b},{resolveCwd:()=>l??this.config.agent.cwd??process.cwd(),fallbackDir:z()}),R=Date.now()-c,f=_.result?.files?.length??0;u.info("file-list-diag",`plugin -> reply action_id=${e.action_id} status=${_.status} elapsed=${R}ms count=${f} current_path=${_.result?.current_path??""} error_code=${_.error_code??""} error_msg=${_.error_msg??""}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:_.status,..._.result?{result:_.result}:{},..._.error_code?{error_code:_.error_code}:{},..._.error_msg?{error_msg:_.error_msg}:{}});return}if(s==="create_folder"){const c=t?this.bindingStore.get(t)?.cwd:void 0,l=String((e.params??{}).parent_id??"").trim(),h=String((e.params??{}).name??"").trim(),m=await Ee({parent_id:l||null,name:h,session_id:t},{resolveCwd:()=>c??this.config.agent.cwd??process.cwd(),fallbackDir:z()});this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:m.status,...m.result?{result:m.result}:{},...m.error_code?{error_code:m.error_code}:{},...m.error_msg?{error_msg:m.error_msg}:{}});return}if(s===S.setModel&&(this.config.adapterType??"acp")==="agy"){await this.handleAgySetModel(e,t);return}if(s===S.getSessionUsage){await this.handleGetSessionUsage(e,t);return}if(s===S.getRateLimits){await this.handleGetRateLimits(e,t);return}const n=(this.config.adapterType??"acp")==="acp";if((i||n)&&s===S.threadCompact){await this.handleThreadCompact(e,t);return}if(s==="connector_rollback"){await this.handleConnectorRollback(e);return}if(s==="connector_upgrade_push"){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{accepted:!0}}),this.upgradeTrigger?.();return}if(s===S.getAgentGlobalConfig){const c=this.globalConfigStore?.get(this.name);this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{agentName:this.name,...c?{config:c}:{config:null}}});return}const r=this.config.adapterType??"acp",a=(r==="codex"||r==="cursor"||r==="pi"||r==="openhuman"||r==="opencode"||r==="acp")&&!!t&&!!this.bindingStore.get(t)?.cwd,d=await this.pool.deliverLocalAction(e,{autoCreateSlot:a});if(d.handled){if(d.kind==="set_mode"){const c=String((e.params??{}).mode_id??"");c&&(r==="cursor"||r==="claude"?this.bindingStore.setModeId(t,c):M.has(r)||this.globalConfigStore?.set(this.name,{acpInitialMode:c}))}else if(d.kind==="set_model"){const c=String((e.params??{}).model_id??"");c&&(r==="cursor"?(this.bindingStore.setModelId(t,c),this.globalConfigStore?.set(this.name,{modelId:c})):M.has(r)||this.globalConfigStore?.set(this.name,{modelId:c}))}else if(d.kind==="set_reasoning_effort"){const c=String((e.params??{}).reasoning_effort??(e.params??{}).reasoning_eff??(e.params??{}).effort??"");c&&this.globalConfigStore?.set(this.name,{codexReasoningEffort:c})}else if(d.kind==="set_sandbox_mode"){const c=String((e.params??{}).sandbox_mode??(e.params??{}).sandboxMode??"");if(c){const l=c==="default"?void 0:c;this.globalConfigStore?.set(this.name,{codexSandboxMode:l})}}return}if((r==="codex"||r==="cursor"||r==="pi"||r==="openhuman"||r==="opencode")&&t&&!this.bindingStore.get(t)?.cwd){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.bindingMissing,error_msg:"Session binding missing. Open a workspace first."});return}if(r==="acp"&&(s==="set_mode"||s==="set_model")){const c=this.sessionControlSenders(),l=this.pool.getSlot(t)?.adapter,h={bindingStore:this.bindingStore,acpAdapter:l instanceof A?l:null,globalConfigStore:this.globalConfigStore,agentName:this.name,log:u};if(s==="set_mode"){const m=String((e.params??{}).mode_id??""),b=await N(h,t,m);if(b.status==="failed")c.sendLocalActionResult(e.action_id,"failed",void 0,b.errorCode,b.errorMsg);else{const _=l instanceof A?l.buildToolbarContext(b.result?.outcome==="mode_set"?"mode_set":"mode_set_failed"):null,R=_?{..._,...b.result}:b.result;c.sendLocalActionResult(e.action_id,"ok",R)}}else{const m=String((e.params??{}).model_id??""),b=await W(h,t,m);if(b.status==="failed")c.sendLocalActionResult(e.action_id,"failed",void 0,b.errorCode,b.errorMsg);else{const _=l instanceof A?l.buildToolbarContext(b.result?.outcome==="model_set"?"model_set":"model_set_failed"):null,R=_?{..._,...b.result}:b.result;c.sendLocalActionResult(e.action_id,"ok",R)}}return}this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"unsupported_local_action",error_msg:`action type ${s} is not supported`})}async handleGetSessionUsage(e,s){if(!s){u.warn(this.name,`[usage] get_session_usage rejected: no session_id in params, action_id=${e.action_id}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"session_id_required",error_msg:"session_id is required for get_session_usage"});return}const t=this.config.adapterType??"acp",o=this.bindingStore.get(s),i=o?.cwd??this.config.agent.cwd??process.cwd();u.info(this.name,`[usage] get_session_usage action_id=${e.action_id} session_id=${s} adapterType=${t} hasBinding=${!!o} cwd=${i}`);let n=null,r,a;switch(t){case"claude":{if(r=o?.claudeSessionId,!r){u.warn(this.name,`[usage] no claude binding for session_id=${s}, action_id=${e.action_id}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Claude session binding found"});return}u.info(this.name,`[usage] parsing claude usage: claudeSessionId=${r} cwd=${i}`),n=await ne(r,i),a="claude";break}case"agy":{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:s,adapterType:"agy",available:!1,reason:"agy print-mode adapter does not track token usage"}});return}case"acp":default:{if(r=o?.acpSessionId,!r){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No ACP session binding found"});return}n=await oe(r,i,this.config.aibot.clientType),a=t,(!a||a==="acp")&&(a="acp");break}case"codex":{if(r=o?.codexThreadId,!r){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Codex thread binding found"});return}n=await re(r),a="codex";break}case"pi":{if(r=o?.piSessionPath,!r){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Pi session path binding found"});return}n=await ue(r),a="pi";break}case"cursor":{const c=this.pool.getSlot(s)?.adapter;if(!(c instanceof P)){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Cursor session found"});return}const l=c.getUsageSnapshot(s);if(!l){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"usage_not_found",error_msg:"No usage data found for this session"});return}this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:s,adapterType:"cursor",models:[{modelId:this.bindingStore.get(s)?.modelId??"auto",turns:l.turns,input:l.total.input,output:l.total.output,cacheRead:l.total.cacheRead,cacheWrite:l.total.cacheWrite}],total:{input:l.total.input,output:l.total.output,cacheRead:l.total.cacheRead,cacheWrite:l.total.cacheWrite},turns:l.turns,sampledAt:l.sampledAt}});return}case"codewhale":{const c=this.pool.getSlot(s)?.adapter;if(!(c instanceof q)){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No CodeWhale session found"});return}const l=c.getUsageSnapshot();if(!l){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"usage_not_found",error_msg:"No usage data found for this session"});return}this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:s,adapterType:"codewhale",models:[{modelId:this.bindingStore.get(s)?.modelId??"codewhale",turns:l.turns,input:l.total.input,output:l.total.output}],total:{input:l.total.input,output:l.total.output},turns:l.turns,sampledAt:l.sampledAt}});return}}if(!n){u.info(this.name,`[usage] no usage data found: session_id=${s} adapterSessionId=${r} adapterType=${a}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"usage_not_found",error_msg:"No usage data found for this session"});return}u.info(this.name,`[usage] result ok: session_id=${s} adapterSessionId=${r} turns=${n.turns} models=${n.models.length}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:r,adapterType:a,models:n.models,total:n.total,turns:n.turns,sampledAt:new Date().toISOString()}})}async handleThreadCompact(e,s){if(!s){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"session_id_required",error_msg:"session_id is required for thread_compact"});return}if(!this.bindingStore.get(s)?.cwd){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.bindingMissing,error_msg:"session binding was not found"});return}try{await this.ensureSlotStarted(s);const i=this.pool.getSlot(s)?.adapter;if(!i?.execCommand){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.runtimeError,error_msg:"Agent does not support command execution"});return}u.info(this.name,`thread_compact session_id=${s} action_id=${e.action_id}`);const n=await i.execCommand("compact","",s);n.status==="ok"?this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"compacted",message:n.message,data:n.data}}):this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.runtimeError,error_msg:n.message??"compact failed"})}catch(o){u.warn(this.name,`thread_compact error session_id=${s}: ${o instanceof Error?o.message:o}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.runtimeError,error_msg:o instanceof Error?o.message:String(o)})}}async handleGetRateLimits(e,s){const t=this.config.adapterType??"acp";if(this.config.aibot.clientType==="kiro"){const o=this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs),i=this.cachedAcpContextWindow;if(o&&this.cachedProviderQuota){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"acp",available:!0,cached:!0,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:i,tokenUsage:null,providerQuota:this.cachedProviderQuota}});return}try{const n=await Q();this.cachedProviderQuota=n,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[rate-limits] kiro quota queried: success=${n.success}`+(n.balance?` balance=${n.balance.remaining} ${n.balance.unit}`:"")+(n.error?` error=${n.error}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"acp",available:!0,cached:!1,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:i,tokenUsage:null,providerQuota:n}})}catch(n){u.warn(this.name,`[rate-limits] kiro quota query failed: ${n instanceof Error?n.message:String(n)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"acp",available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:i,tokenUsage:null}})}return}switch(t){case"codex":{const o=this.maybeQueryProviderQuota(),i=this.getFreshCodexGlobalRateLimitCache();if(i.hasData&&!this.isRateLimitsCacheFresh(this.cachedRateLimitsSampledAtMs)&&!this.isRateLimitsCacheFresh(this.cachedCodexUsageSampledAtMs)){const l=this.pool.getAllSlots().find(h=>h.state==="ready"&&h.adapter);if(l&&(u.info(this.name,`[rate-limits] codex cache stale, refreshing from slot: session=${l.sessionId}`),(await l.adapter.handleLocalAction?.(e))?.handled))return}if(i.hasData){i.rateLimits?u.info(this.name,`[rate-limits] codex cached: primary=${i.rateLimits.primary.usedPercent.toFixed(1)}% resetsAt=${i.rateLimits.primary.resetsAt} secondary=${i.rateLimits.secondary.usedPercent.toFixed(1)}% resetsAt=${i.rateLimits.secondary.resetsAt}`):u.info(this.name,`[rate-limits] codex cached context/token only: hasContext=${!!i.contextWindow} hasToken=${!!i.tokenUsage}`);const l=await o;this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"codex",available:i.hasData||!!l,cached:!0,sampledAt:i.sampledAt,rateLimits:i.rateLimits,contextWindow:i.contextWindow,tokenUsage:i.tokenUsage,providerQuota:l}});return}const r=this.pool.getAllSlots().find(l=>l.state==="ready"&&l.adapter);if(r&&(u.info(this.name,`[rate-limits] codex reuse existing slot: session=${r.sessionId}`),(await r.adapter.handleLocalAction?.(e))?.handled))return;const a=this.resolveRateLimitWakeSessionId(s,t);if(a){const l=await this.wakeRateLimitSlot(a,t);if(l?.adapter&&(await l.adapter.handleLocalAction?.(e))?.handled)return}const d=await o,c=!!d;u.info(this.name,`[rate-limits] codex no native data, providerQuota=${d?d.provider:"none"} available=${c}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"codex",available:c,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:d}});return}case"claude":{const o=this.maybeQueryProviderQuota(),i=this.getFreshClaudeRateLimitState();if(i){const c=i.rateLimits,l=await o;u.info(this.name,`[rate-limits] claude global cached: sampledAt=${i.sampledAt} hasRateLimits=${!!c}`+(l?` providerQuota=${l.provider}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"claude",available:!0,cached:!0,sampledAt:i.sampledAt,rateLimits:i.rateLimits??null,contextWindow:i.contextWindow,tokenUsage:null,providerQuota:l}});return}let n=this.pool.getAllSlots().find(c=>c.state==="ready"&&c.adapter instanceof T)??null;if(!n){const c=this.resolveRateLimitWakeSessionId(s,t);c&&(n=await this.wakeRateLimitSlot(c,t))}u.info(this.name,`[rate-limits] handleGetRateLimits: session_id=${s} adapterType=claude hasSlot=${!!n} hasAdapter=${!!n?.adapter}`);const r=n?.adapter,a=r instanceof T?r.getSessionState():null,d=await o;if(a){(a.rateLimits?.fiveHour||a.rateLimits?.sevenDay||a.contextWindow?.usedPercentage!=null)&&(this.cachedClaudeRateLimitState=a);const c=this.getFreshClaudeRateLimitState(),l=c?.rateLimits&&!a.rateLimits?{...a,rateLimits:c.rateLimits}:a,h=l.rateLimits;u.info(this.name,`[rate-limits] claude global state: sampledAt=${l.sampledAt} hasRateLimits=${!!h}`+(h?` fiveHour=${h.fiveHour?.usedPercentage??"n/a"}% resetsAt=${h.fiveHour?.resetsAt??"n/a"} sevenDay=${h.sevenDay?.usedPercentage??"n/a"}% resetsAt=${h.sevenDay?.resetsAt??"n/a"}`:"")+(c?.rateLimits&&!a.rateLimits?" source=live+cached-fallback":" source=live")+(d?` providerQuota=${d.provider}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"claude",available:!0,cached:!1,sampledAt:l.sampledAt,rateLimits:l.rateLimits??null,contextWindow:l.contextWindow,tokenUsage:null,providerQuota:d}})}else u.info(this.name,`[rate-limits] claude no global state: hasAdapter=${!!r} adapterType=${r?.constructor?.name??"n/a"}`+(d?` providerQuota=${d.provider}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"claude",available:!!d,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:d}});return}case"cursor":{const i=(s?this.pool.getSlot(s):null)?.adapter,n=i instanceof P?i.getRateLimitsSnapshot():{adapterType:"cursor",available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null};this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:n});return}default:{const o=this.config.providerBaseUrl,i=this.config.providerApiKey;if(!o||!i){u.info(this.name,`[rate-limits] no provider config for adapterType=${t}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null}});return}if(this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota){u.info(this.name,`[rate-limits] provider quota cached: provider=${this.cachedProviderQuota.provider} success=${this.cachedProviderQuota.success}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!0,cached:!0,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:this.cachedProviderQuota}});return}try{const r=await K(o,i);this.cachedProviderQuota=r,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[rate-limits] provider quota queried: provider=${r.provider} success=${r.success}`+(r.tiers.length>0?` tiers=${r.tiers.map(a=>`${a.name}=${a.usedPercent}%`).join(",")}`:"")+(r.balance?` balance=${r.balance.remaining} ${r.balance.unit}`:"")+(r.error?` error=${r.error}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!0,cached:!1,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:r}})}catch(r){u.warn(this.name,`[rate-limits] provider quota query failed: ${r instanceof Error?r.message:String(r)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null}})}return}}}resolveRateLimitWakeSessionId(e,s){const t=String(e??"").trim(),o=this.bindingStore.getMostRecentlyUpdatedSessionId({requireCwd:!0});return o?t&&t===o?t:s==="codex"||s==="claude"?(t&&t!==o&&u.info(this.name,`[rate-limits] ${s} remap wake session: requested=${t} use_recent=${o}`),o):t&&t!==o?(u.info(this.name,`[rate-limits] skip wake slot: session=${t} adapterType=${s} reason=not_recent_session recent=${o}`),null):t||null:(u.info(this.name,`[rate-limits] skip wake slot: adapterType=${s} reason=no_recent_binding`),null)}async wakeRateLimitSlot(e,s){const t=String(e??"").trim();if(!t)return null;if(!this.bindingStore.get(t)?.cwd)return u.info(this.name,`[rate-limits] skip wake slot: session=${t} adapterType=${s} reason=binding_cwd_missing`),null;try{const i=this.pool.getOrCreateSlot(t);if(!i)return u.info(this.name,`[rate-limits] skip wake slot: session=${t} adapterType=${s} reason=slot_unavailable`),null;if(i.startPromise){const n=s==="claude"?6e4:2e4;await Promise.race([i.startPromise,new Promise((r,a)=>setTimeout(()=>a(new Error(`wake rate-limits slot timeout (${n}ms)`)),n))])}return u.info(this.name,`[rate-limits] wake slot success: session=${t} adapterType=${s}`),this.pool.getSlot(t)??i}catch(i){return u.warn(this.name,`[rate-limits] wake slot failed: session=${t} adapterType=${s} err=${i instanceof Error?i.message:String(i)}`),null}}sessionControlCtx(e){const s=this.pool.getSlot(e),t=s?.adapter instanceof A?s.adapter:null,o=(this.config.adapterType??"acp")==="acp",i={bindingStore:this.bindingStore,acpAdapter:t,globalConfigStore:this.globalConfigStore,agentName:this.name,log:u};return{getCwd:()=>this.bindingStore.get(e)?.cwd??this.config.agent.cwd??process.cwd(),getSessionBindings:()=>{if(t)return t.getSessionBindings();const n=this.sessionBindings;if(e&&!n.has(e)){const r=this.bindingStore.get(e);r?.cwd&&n.set(e,r.cwd)}return n},getStatus:()=>this.getStatus(),isAcpAlive:!!t?.isAlive(),getAcpSessionOptions:()=>t?.acpSessionOptions??null,setMode:n=>t?t.setMode(n):Promise.resolve(!1),setModel:n=>t?t.setModel(n):Promise.resolve(!1),acpSetMode:o?(n,r)=>N(i,n,r):void 0,acpSetModel:o?(n,r)=>W(i,n,r):void 0,getPendingApproval:n=>{const r=t?.pendingApprovalEntries.get(n);return r?{requestId:r}:void 0},deletePendingApproval:n=>t?.pendingApprovalEntries.delete(n)??!1,respondPermission:(n,r)=>(t&&t.respondToPermission(n,r),Promise.resolve()),onSessionBound:(n,r)=>{this.bindingStore.set(n,r)},onSessionUnbound:n=>{const a=this.bindingStore.get(n)?.cwd??"";this.bindingStore.delete(n),this.sessionBindings.delete(n),this.claudeWorkerStatus.delete(n),this.deferredMgr.clearSession(n),this.pool.drainQueuedForSession(n),a&&this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:"stopped",cwd:a})},cancelActiveRun:()=>(this.config.adapterType??"acp")==="agy"&&s?.adapter instanceof U?(s.adapter.cancelCurrentRun(),Promise.resolve()):s?.adapter?.cancel("")??Promise.resolve(),onModeSet:n=>{const r=this.config.adapterType??"acp";M.has(r)||this.globalConfigStore?.set(this.name,{acpInitialMode:n})},onModelSet:n=>{const r=this.config.adapterType??"acp";M.has(r)||this.globalConfigStore?.set(this.name,{modelId:n})}}}sessionControlSenders(){return{sendEventAck:(e,s)=>this.aibotHandle.sendEventAck({event_id:e,session_id:s,received_at:Date.now()}),sendEventResult:(e,s,t)=>this.aibotHandle.sendEventResult({event_id:e,status:s,...t?.msg?{msg:t.msg}:{},...t?.code?{code:t.code}:{},updated_at:Date.now()}),sendLocalActionResult:(e,s,t,o,i)=>this.aibotHandle.sendLocalActionResult({action_id:e,status:s,...t?{result:t}:{},...o?{error_code:o}:{},...i?{error_msg:i}:{}})}}resolveBindingChannelKey(e){switch(e){case"claude":return"grix-claude";case"codex":return"codex";case"cursor":return"cursor";case"pi":return"pi";case"openhuman":return"openhuman";case"codewhale":return"codewhale";case"opencode":return"opencode";case"agy":return"acp";case"acp":return this.config.aibot.clientType==="qwen"?"qwen":"acp";default:return e}}finalizeThinking(e,s){this.sendCtrl.finalizeThinking(e,s)}logInboundConversation(e){const s=String(e.session_id??"").trim();s&&this.conversationLog?.logInbound(s,{event_id:e.event_id,msg_id:e.msg_id,sender_id:e.sender_id,msg_type:e.msg_type,content:e.content??""})}buildInboundEvent(e,s){const t=Le(this.sendCtrl.getGlobalRuntimeConfig(),s);return{event_id:e.event_id,session_id:e.session_id,thread_id:e.thread_id,sender_id:e.sender_id,msg_id:e.msg_id,msg_type:e.msg_type,content:e.content??"",quoted_message_id:e.quoted_message_id,context_messages_json:e.context_messages?JSON.stringify(e.context_messages):void 0,extra_json:e.extra?JSON.stringify(e.extra):void 0,connector_runtime_config:{response_delivery:t.responseDelivery,tool_events:t.toolEvents,thinking_events:t.thinkingEvents},session_type:e.session_type,created_at:e.created_at}}isStaleEvent(e){const s=Number(e.created_at);return!Number.isFinite(s)||s<=0?!1:Date.now()-s>Fe}async handleConnectorRollback(e){const s=String((e.params??{}).target_version??"").trim(),t=String((e.params??{}).reason??"server_initiated");if(!s){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"MISSING_TARGET_VERSION",error_msg:"target_version is required for connector_rollback"});return}u.info(this.name,`connector_rollback: target=${s} reason=${t}`);try{const{npmInstall:o,writePending:i,removePending:n,upgradeLog:r}=await import("../core/upgrade/npm-upgrader.js"),{resolveClientVersion:a}=await import("../core/util/client-version.js"),d=a();r(`server rollback: ${d} -> ${s} reason=${t}`),i(d,s),await o("grix-connector",s),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{rolled_back_to:s}}),process.kill(process.pid,"SIGTERM")}catch(o){try{const{removePending:n}=await import("../core/upgrade/npm-upgrader.js");n()}catch{}const i=o instanceof Error?o.message:String(o);u.error(this.name,`connector_rollback failed: ${i}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"ROLLBACK_FAILED",error_msg:i})}}setUpgradeTrigger(e){this.upgradeTrigger=e}}export{Ht as AgentInstance};
16
+ Models (current: ${n?.modelId??"default"}): ${r}`);return}case p.exec:{const[i,...n]=e.args.trim().split(/\s+/);if(!i){o("failed","Usage: /grix exec <command> [args]",g.verbInvalid);return}const a=this.pool.getSlot(t)?.adapter;if(!a?.execCommand){o("failed","Agent does not support command execution",g.verbInvalid);return}const d=a.getSupportedCommands?.()??[];if(!d.some(c=>c.name===i)){o("failed",`Unknown command: ${i}. Supported: ${d.map(c=>c.name).join(", ")}`,g.verbInvalid);return}try{const c=await a.execCommand(i,n.join(" "),t);o(c.status==="ok"?"responded":"failed",c.message??`${i} ${c.status}`,c.status==="ok"?void 0:g.runtimeError)}catch(c){o("failed",`exec error: ${c instanceof Error?c.message:c}`,g.runtimeError)}return}default:o("failed",`Unsupported command for Claude: /grix ${e.verb}`,g.verbInvalid)}}catch(i){o("failed",i instanceof Error?i.message:String(i),g.runtimeError)}}async handleSessionControlLocalAction(e){const s=String(e.action_type??"").trim();if(s!==S.sessionControl&&s!==S.setMode&&s!=="set_mode"&&s!==S.setModel&&s!=="set_model")return!1;const t=e.params??{},o=String(t.session_id??"").trim(),i=s===S.setMode?p.setMode:s===S.setModel?p.setModel:String(t.verb??"").trim().toLowerCase();if(u.info(this.name,`handleSessionControlLocalAction verb=${i} action_id=${e.action_id} session_id=${o}`),!o)return this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:y.localActionRouteMissing,error_msg:"local action session_id is required"}),!0;const n=a=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:a})},r=(a,d)=>{u.warn(this.name,`session_control local_action failed action_id=${e.action_id} session_id=${o} verb=${i} code=${a} msg=${d}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:a,error_msg:d})};try{switch(i){case p.open:{await I().catch(()=>{});const a=String(t.cwd??"").trim(),d=String(t.agent_session_id??"").trim();if(!a)return r(g.cwdRequired,"session control cwd is required"),!0;u.info(this.name,`handleSessionControlLocalAction open cwd=${a} session_id=${o}`);const c=await this.resolveCwdForBinding(a);this.ensureImportedAgentSession(d,c);const l=this.bindingStore.get(o);if(l?.cwd){const h=await this.resolveCwdForBinding(l.cwd);return h!==c?(r(g.rebindForbidden,"session binding cannot be changed to another working directory"),!0):(this.bindingStore.ensureModeId(o,C.fullAuto),this.setResolvedAgentSessionId(o,d),this.sessionBindings.set(o,h),await this.ensureSlotStarted(o),await this.replayDeferredEventsForSession(o),this.refreshClaudeWorkerStatusCard(o,h),n({outcome:"opened",binding:{...this.buildOpenedBindingResult(o,h),mode_id:this.normalizeClaudeModeId(this.bindingStore.get(o)?.modeId)}}),!0)}return this.bindingStore.set(o,c,{modeId:C.fullAuto}),this.setResolvedAgentSessionId(o,d),this.sessionBindings.set(o,c),await this.ensureSlotStarted(o),await this.replayDeferredEventsForSession(o),this.refreshClaudeWorkerStatusCard(o,c),n({outcome:"opened",binding:{...this.buildOpenedBindingResult(o,c),mode_id:this.normalizeClaudeModeId(this.bindingStore.get(o)?.modeId)}}),!0}case p.status:case p.where:{const a=this.bindingStore.get(o);return a?.cwd?(n({outcome:i,binding:{cwd:a.cwd,mode_id:this.normalizeClaudeModeId(a.modeId),worker_status:this.getClaudeWorkerStatus(o)}}),!0):(r(g.bindingMissing,"session binding was not found"),!0)}case p.stop:{const a=this.bindingStore.get(o);return a?.cwd?(await this.pool.removeSlot(o),n({outcome:"stopped",binding:{cwd:a.cwd,mode_id:this.normalizeClaudeModeId(a.modeId),worker_status:"stopped"}}),!0):(r(g.bindingMissing,"session binding was not found"),!0)}case p.restart:{const a=this.bindingStore.get(o);return a?.cwd?(await this.pool.removeSlot(o),await this.ensureSlotStarted(o),this.refreshClaudeWorkerStatusCard(o,a.cwd),n({outcome:"restarted",binding:{cwd:a.cwd,mode_id:this.normalizeClaudeModeId(a.modeId)}}),!0):(r(g.bindingMissing,"session binding was not found"),!0)}case p.setMode:{const a=String(t.mode_id??t.modeId??"").trim();if(!a)return r(y.modeInvalid,"set mode_id is invalid"),!0;const d=this.normalizeClaudeModeId(a);if(d!==a.toLowerCase())return r(y.modeInvalid,"set mode_id is invalid"),!0;const c=this.bindingStore.get(o);if(!c?.cwd)return r(g.bindingMissing,"session binding was not found"),!0;if(this.normalizeClaudeModeId(c.modeId)!==d){if(this.getClaudeWorkerStatus(o)==="busy")return r(g.workerBusy,"\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u5F0F"),!0;this.bindingStore.setModeId(o,d),await this.pool.removeSlot(o),await this.ensureSlotStarted(o),this.refreshClaudeWorkerStatusCard(o,c.cwd)}return n({outcome:"mode_set",mode_id:d,binding:{cwd:c.cwd,mode_id:d}}),!0}case p.setModel:{const a=String(t.model_id??t.modelId??"").trim();if(!a)return r("set_model_invalid","model_id is required"),!0;const d=this.bindingStore.get(o);if(!d?.cwd)return r(g.bindingMissing,"session binding was not found"),!0;if((d.modelId??"")!==a){if(this.getClaudeWorkerStatus(o)==="busy")return r(g.workerBusy,"\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u578B"),!0;this.bindingStore.setModelId(o,a),this.globalConfigStore?.set(this.name,{modelId:a}),await this.pool.removeSlot(o),await this.ensureSlotStarted(o),this.refreshClaudeWorkerStatusCard(o,d.cwd)}return n({outcome:"model_set",model_id:a,binding:{cwd:d.cwd,model_id:a}}),!0}case p.listOptions:{const a=D(),d=this.pool.getSlot(o);return n({modes:[C.fullAuto,C.approval],currentModeId:this.normalizeClaudeModeId(this.bindingStore.get(o)?.modeId),models:a.map(c=>({modelId:c.id,name:c.displayName})),currentModelId:this.bindingStore.get(o)?.modelId??"",available_models:a.map(c=>({id:c.id,displayName:c.displayName})),agent_commands:d?.adapter?.getSupportedCommands?.()??[]}),!0}case p.exec:{const[a,...d]=String(t.args??"").trim().split(/\s+/);if(!a)return r(g.verbInvalid,"Usage: exec <command> [args]"),!0;await this.ensureSlotStarted(o);const l=this.pool.getSlot(o)?.adapter;if(!l?.execCommand)return r(g.verbInvalid,"Agent does not support command execution"),!0;const h=l.getSupportedCommands?.()??[];if(!h.some(m=>m.name===a))return r(g.verbInvalid,`Unknown command: ${a}. Supported: ${h.map(m=>m.name).join(", ")}`),!0;try{const m=await l.execCommand(a,d.join(" "),o);m.status==="ok"?n({outcome:"exec",command:a,message:m.message,data:m.data}):r(g.runtimeError,m.message??`${a} failed`)}catch(m){r(g.runtimeError,`exec error: ${m instanceof Error?m.message:m}`)}return!0}default:return r(g.verbInvalid,`session control verb ${i} is not supported`),!0}}catch(a){const d=a instanceof Error&&a.cwdErrorCode?a.cwdErrorCode:a instanceof Error&&a.sessionControlErrorCode?a.sessionControlErrorCode:g.runtimeError;return u.error(this.name,`handleSessionControlLocalAction error verb=${i} session_id=${o}: ${a instanceof Error?a.message:a}`),r(d,a instanceof Error?a.message:String(a)),!0}}async handleEventCancel(e){const{event_id:s,session_id:t}=e;if(u.info(this.name,`handleEventCancel start event_id=${s} session_id=${t}`),this.pool.cancelEvent(s,t)){if(!(this.pool.getSlot(t)?.adapter?.getActiveEventIds().includes(s)??!1)){this.sendEventResultWithCleanup(s,"canceled","canceled"),this.aibotHandle.sendEventCancelResult({event_id:s,accepted:!0,final_state:"canceled"});return}await this.waitForEventDone(s,t,15e3),this.aibotHandle.sendEventCancelResult({event_id:s,accepted:!0,final_state:"canceled"}),this.pushQueueSnapshotForSession(t);return}this.aibotHandle.sendEventCancelResult({event_id:s,accepted:!1,reason:"event not found or not cancelable"})}waitForEventDone(e,s,t){return new Promise(o=>{const i=this.pool.getSlot(s);if(!i?.adapter){o();return}const n=setTimeout(()=>{i.adapter.removeListener("eventDone",r),o()},t),r=a=>{a===e&&(clearTimeout(n),i.adapter.removeListener("eventDone",r),o())};i.adapter.on("eventDone",r)})}handleAibotStop(e){if(u.info(this.name,`[stop-trace] handleAibotStop begin session=${e.session_id} event=${e.event_id} stop_id=${e.stop_id||"-"} adapterType=${this.config.adapterType??"acp"}`),this.aibotHandle.sendEventStopAck({stop_id:e.stop_id,event_id:e.event_id,accepted:!0,updated_at:Date.now()}),this.pool.removeQueuedEvent(e.session_id,e.event_id)){u.info(this.name,`[stop-trace] handleAibotStop removed queued(not-running) event -> stopResult(stopped) session=${e.session_id} event=${e.event_id} stop_id=${e.stop_id||"-"}`),this.pushQueueSnapshotForSession(e.session_id),this.aibotHandle.sendEventStopResult({stop_id:e.stop_id,event_id:e.event_id,status:"stopped",updated_at:Date.now()});return}const s=this.pool.getSlot(e.session_id),t=s?.adapter?.getStatus().busy??!1,o=(this.config.adapterType??"acp")==="acp",i=s?.adapter?.getActiveEventIds,n=typeof i=="function"?i.call(s.adapter):[],r=n.length>0?n.includes(e.event_id):t,a=(o||(this.config.adapterType??"acp")==="codex"||(this.config.adapterType??"acp")==="claude")&&r;if(u.info(this.name,`[stop-trace] handleAibotStop decision session=${e.session_id} event=${e.event_id} slotExists=${!!s?.adapter} busy=${t} activeIds=[${n.join(",")}] stoppingActiveEvent=${r} killOnStop=${a}`),r&&this.sendCtrl.markEventStopped(e.event_id),s?.adapter&&t){const d=a?this.pool.drainQueuedForSession(e.session_id):[];let c=!1,l=null;const h=v=>{c||(c=!0,l&&(clearTimeout(l),l=null),s.adapter.removeListener("eventDone",m),u.info(this.name,`[stop-trace] handleAibotStop ${v} -> stopResult(stopped) session=${e.session_id} event=${e.event_id} stop_id=${e.stop_id||"-"} killOnStop=${a}`),this.aibotHandle.sendEventStopResult({stop_id:e.stop_id,event_id:e.event_id,status:"stopped",updated_at:Date.now()}),a&&this.killAndResumeStopSlot(e.session_id,d))},m=v=>{v===e.event_id&&h("eventDone")};s.adapter.on("eventDone",m),l=setTimeout(()=>h("timeout"),Ue),this.pool.deliverStopEvent(e.event_id,e.session_id)}else u.info(this.name,`[stop-trace] handleAibotStop slot-not-busy -> immediate stopResult(stopped) session=${e.session_id} event=${e.event_id} slotExists=${!!s?.adapter}`),this.pool.deliverStopEvent(e.event_id,e.session_id),this.aibotHandle.sendEventStopResult({stop_id:e.stop_id,event_id:e.event_id,status:"stopped",updated_at:Date.now()});(this.config.adapterType??"acp")==="pi"&&this.aibotHandle.sendText({event_id:e.event_id,session_id:e.session_id,content:"/stop",msg_type:0})}async killAndResumeStopSlot(e,s){if(!this.stopped){u.info(this.name,`[stop-trace] killAndResumeStopSlot begin session=${e} siblings=${s.length} -> removeSlot (kill process group)`);try{await this.pool.removeSlot(e),u.info(this.name,`[stop-trace] killAndResumeStopSlot removeSlot done session=${e} (process killed) -> redeliver ${s.length} sibling(s)`)}catch(t){u.warn(this.name,`[acp-stop] removeSlot failed session=${e}: ${t instanceof Error?t.message:String(t)}`)}if(!this.stopped)for(const t of s){if(this.stopped)break;try{await this.pool.deliverInboundEvent(t)}catch(o){u.error(this.name,`[acp-stop] sibling redeliver failed event=${t.event_id} session=${e}: ${o instanceof Error?o.message:String(o)}`),this.sendEventResultWithCleanup(t.event_id,"failed",o instanceof Error?o.message:String(o))}}}}handleAibotRevoke(e){if(!e.event_id||!this.revokeHandler.checkAndTrack(e.event_id))return;const s=e.event_id,t=e.session_id;if(t&&this.pool.cancelEvent(s,t)){(this.pool.getSlot(t)?.adapter?.getActiveEventIds().includes(s)??!1)||this.sendEventResultWithCleanup(s,"canceled","revoked");return}if(this.deferredMgr.removeEvent(s)){this.aibotHandle.sendEventResult({event_id:s,status:"canceled",msg:"revoked",updated_at:Date.now()});return}this.pool.deliverStopEvent(s,t||void 0)}async handleAibotLocalAction(e){const s=e.action_type??"",t=String((e.params??{}).session_id??""),o=String((e.params??{}).verb??"").trim().toLowerCase();u.debug(this.name,`local_action received action_type=${s} verb=${o||"-"} action_id=${e.action_id} session_id=${t}`);const i=(this.config.adapterType??"acp")==="claude";if(s===S.sessionControl&&o===p.exec&&await this.handleSessionControlLocalAction(e))return;if(s===S.sessionControl&&o===p.listSessions){await this.handleListSessionsLocalAction(e);return}if(i&&(s===S.interactionReply||s==="exec_approve"||s==="exec_reject")&&(await this.pool.deliverLocalAction(e)).handled||i&&await this.handleSessionControlLocalAction(e))return;if(s===S.sessionControl){const c=this.config.adapterType??"acp",l=c==="codex",h=c==="pi",m=String((e.params??{}).verb??"").trim().toLowerCase();if(l&&m===p.open){await this.handleCodexSessionControlLocalActionOpen(e);return}if(c==="cursor"&&m===p.open){await this.handleCursorSessionControlLocalActionOpen(e);return}if(l&&m==="restart"){const b=this.bindingStore.get(t)?.cwd??"";await this.pool.removeSlot(t).catch(()=>{}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"restarted",binding:{aibotSessionId:t,cwd:b,workerStatus:"ready"}}});return}if(h&&m===p.open){try{const f=e.params??{},b=String(f.cwd??"").trim();if(!b){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.cwdRequired,error_msg:"session cwd is required"});return}const w=await this.resolveCwdForBinding(b),k=String(f.agent_session_id??"").trim();this.ensureImportedAgentSession(k,w),this.bindingStore.set(t,w),this.setResolvedAgentSessionId(t,k),this.sessionBindings.set(t,w),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:w}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"opened",binding:this.buildOpenedBindingResult(t,w)}})}catch(f){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:f?.sessionControlErrorCode??f?.cwdErrorCode??g.invalidCwd,error_msg:f instanceof Error?f.message:String(f)})}return}if(h&&m===p.restart){await this.handlePiSessionControlRestartLocalAction(e);return}if((c==="openhuman"||c==="opencode")&&m===p.open){try{const f=e.params??{},b=String(f.cwd??"").trim();if(!b){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.cwdRequired,error_msg:"session cwd is required"});return}const w=await this.resolveCwdForBinding(b),k=String(f.agent_session_id??"").trim();this.ensureImportedAgentSession(k,w),this.bindingStore.set(t,w),this.setResolvedAgentSessionId(t,k),this.sessionBindings.set(t,w),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"opened",binding:this.buildOpenedBindingResult(t,w)}})}catch(f){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:f?.sessionControlErrorCode??f?.cwdErrorCode??g.invalidCwd,error_msg:f instanceof Error?f.message:String(f)})}return}if(c==="codewhale"&&m===p.open){await this.handleCodeWhaleSessionControlLocalActionOpen(e);return}if(c==="acp"&&m===p.stop){const b=this.bindingStore.get(t)?.cwd??"";await this.pool.removeSlot(t).catch(()=>{}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"stopped",binding:{aibotSessionId:t,cwd:b,workerStatus:"stopped"}}});return}try{if(m===p.open){const f=e.params??{},b=String(f.cwd??"").trim();if(!b){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.cwdRequired,error_msg:"session cwd is required"});return}const w=String(f.agent_session_id??"").trim();if(w){const k=await this.resolveCwdForBinding(b);this.ensureImportedAgentSession(w,k)}}await this.handleSessionControlLocalActionForPool(e)}catch(f){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:f?.sessionControlErrorCode??f?.cwdErrorCode??g.runtimeError,error_msg:f instanceof Error?f.message:String(f)});return}if(m===p.open){const f=e.params??{},b=await this.resolveCwdForBinding(String(f.cwd??"").trim());this.setResolvedAgentSessionId(t,String(f.agent_session_id??"").trim()),c==="agy"&&(this.bindingStore.set(t,b),this.sessionBindings.set(t,b)),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"opened",binding:this.buildOpenedBindingResult(t,b)}}),(this.config.adapterType??"acp")==="agy"&&this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:b,meta:this.buildAgyToolbarMeta(t)})}else Ce(e,this.sessionControlCtx(t),this.sessionControlSenders());return}if(s==="file_list"){const c=Date.now(),l=t?this.bindingStore.get(t)?.cwd:void 0,h=e.params??{},m=String(h.parent_id??"").trim(),v=Array.isArray(h.allowed_extensions)?h.allowed_extensions.filter(b=>typeof b=="string").map(b=>b.trim()).filter(b=>b.length>0):[];u.info("file-list-diag",`plugin << recv action_id=${e.action_id} session_id=${t} parent_id=${m||"<root>"} show_hidden=${!!h.show_hidden} ext_count=${v.length} bound_cwd=${l??"<none>"}`);const _=await Re({parent_id:m||null,session_id:t,show_hidden:!!h.show_hidden,allowed_extensions:v},{resolveCwd:()=>l??this.config.agent.cwd??process.cwd(),fallbackDir:z()}),R=Date.now()-c,f=_.result?.files?.length??0;u.info("file-list-diag",`plugin -> reply action_id=${e.action_id} status=${_.status} elapsed=${R}ms count=${f} current_path=${_.result?.current_path??""} error_code=${_.error_code??""} error_msg=${_.error_msg??""}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:_.status,..._.result?{result:_.result}:{},..._.error_code?{error_code:_.error_code}:{},..._.error_msg?{error_msg:_.error_msg}:{}});return}if(s==="create_folder"){const c=t?this.bindingStore.get(t)?.cwd:void 0,l=String((e.params??{}).parent_id??"").trim(),h=String((e.params??{}).name??"").trim(),m=await Ee({parent_id:l||null,name:h,session_id:t},{resolveCwd:()=>c??this.config.agent.cwd??process.cwd(),fallbackDir:z()});this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:m.status,...m.result?{result:m.result}:{},...m.error_code?{error_code:m.error_code}:{},...m.error_msg?{error_msg:m.error_msg}:{}});return}if(s===S.setModel&&(this.config.adapterType??"acp")==="agy"){await this.handleAgySetModel(e,t);return}if(s===S.getSessionUsage){await this.handleGetSessionUsage(e,t);return}if(s===S.getRateLimits){await this.handleGetRateLimits(e,t);return}const n=(this.config.adapterType??"acp")==="acp";if((i||n)&&s===S.threadCompact){await this.handleThreadCompact(e,t);return}if(s==="connector_rollback"){await this.handleConnectorRollback(e);return}if(s==="connector_upgrade_push"){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{accepted:!0}}),this.upgradeTrigger?.();return}if(s===S.getAgentGlobalConfig){const c=this.globalConfigStore?.get(this.name);this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{agentName:this.name,...c?{config:c}:{config:null}}});return}const r=this.config.adapterType??"acp",a=(r==="codex"||r==="cursor"||r==="pi"||r==="openhuman"||r==="opencode"||r==="acp")&&!!t&&!!this.bindingStore.get(t)?.cwd,d=await this.pool.deliverLocalAction(e,{autoCreateSlot:a});if(d.handled){if(d.kind==="set_mode"){const c=String((e.params??{}).mode_id??"");c&&(r==="cursor"||r==="claude"?this.bindingStore.setModeId(t,c):M.has(r)||this.globalConfigStore?.set(this.name,{acpInitialMode:c}))}else if(d.kind==="set_model"){const c=String((e.params??{}).model_id??"");c&&(r==="cursor"?(this.bindingStore.setModelId(t,c),this.globalConfigStore?.set(this.name,{modelId:c})):M.has(r)||this.globalConfigStore?.set(this.name,{modelId:c}))}else if(d.kind==="set_reasoning_effort"){const c=String((e.params??{}).reasoning_effort??(e.params??{}).reasoning_eff??(e.params??{}).effort??"");c&&this.globalConfigStore?.set(this.name,{codexReasoningEffort:c})}else if(d.kind==="set_sandbox_mode"){const c=String((e.params??{}).sandbox_mode??(e.params??{}).sandboxMode??"");if(c){const l=c==="default"?void 0:c;this.globalConfigStore?.set(this.name,{codexSandboxMode:l})}}return}if((r==="codex"||r==="cursor"||r==="pi"||r==="openhuman"||r==="opencode")&&t&&!this.bindingStore.get(t)?.cwd){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.bindingMissing,error_msg:"Session binding missing. Open a workspace first."});return}if(r==="acp"&&(s==="set_mode"||s==="set_model")){const c=this.sessionControlSenders(),l=this.pool.getSlot(t)?.adapter,h={bindingStore:this.bindingStore,acpAdapter:l instanceof A?l:null,globalConfigStore:this.globalConfigStore,agentName:this.name,log:u};if(s==="set_mode"){const m=String((e.params??{}).mode_id??""),v=await N(h,t,m);if(v.status==="failed")c.sendLocalActionResult(e.action_id,"failed",void 0,v.errorCode,v.errorMsg);else{const _=l instanceof A?l.buildToolbarContext(v.result?.outcome==="mode_set"?"mode_set":"mode_set_failed"):null,R=_?{..._,...v.result}:v.result;c.sendLocalActionResult(e.action_id,"ok",R)}}else{const m=String((e.params??{}).model_id??""),v=await W(h,t,m);if(v.status==="failed")c.sendLocalActionResult(e.action_id,"failed",void 0,v.errorCode,v.errorMsg);else{const _=l instanceof A?l.buildToolbarContext(v.result?.outcome==="model_set"?"model_set":"model_set_failed"):null,R=_?{..._,...v.result}:v.result;c.sendLocalActionResult(e.action_id,"ok",R)}}return}this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"unsupported_local_action",error_msg:`action type ${s} is not supported`})}async handleGetSessionUsage(e,s){if(!s){u.warn(this.name,`[usage] get_session_usage rejected: no session_id in params, action_id=${e.action_id}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"session_id_required",error_msg:"session_id is required for get_session_usage"});return}const t=this.config.adapterType??"acp",o=this.bindingStore.get(s),i=o?.cwd??this.config.agent.cwd??process.cwd();u.info(this.name,`[usage] get_session_usage action_id=${e.action_id} session_id=${s} adapterType=${t} hasBinding=${!!o} cwd=${i}`);let n=null,r,a;switch(t){case"claude":{if(r=o?.claudeSessionId,!r){u.warn(this.name,`[usage] no claude binding for session_id=${s}, action_id=${e.action_id}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Claude session binding found"});return}u.info(this.name,`[usage] parsing claude usage: claudeSessionId=${r} cwd=${i}`),n=await ne(r,i),a="claude";break}case"agy":{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:s,adapterType:"agy",available:!1,reason:"agy print-mode adapter does not track token usage"}});return}case"acp":default:{if(r=o?.acpSessionId,!r){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No ACP session binding found"});return}n=await oe(r,i,this.config.aibot.clientType),a=t,(!a||a==="acp")&&(a="acp");break}case"codex":{if(r=o?.codexThreadId,!r){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Codex thread binding found"});return}n=await re(r),a="codex";break}case"pi":{if(r=o?.piSessionPath,!r){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Pi session path binding found"});return}n=await ue(r),a="pi";break}case"cursor":{const c=this.pool.getSlot(s)?.adapter;if(!(c instanceof P)){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No Cursor session found"});return}const l=c.getUsageSnapshot(s);if(!l){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"usage_not_found",error_msg:"No usage data found for this session"});return}this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:s,adapterType:"cursor",models:[{modelId:this.bindingStore.get(s)?.modelId??"auto",turns:l.turns,input:l.total.input,output:l.total.output,cacheRead:l.total.cacheRead,cacheWrite:l.total.cacheWrite}],total:{input:l.total.input,output:l.total.output,cacheRead:l.total.cacheRead,cacheWrite:l.total.cacheWrite},turns:l.turns,sampledAt:l.sampledAt}});return}case"codewhale":{const c=this.pool.getSlot(s)?.adapter;if(!(c instanceof q)){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"no_binding",error_msg:"No CodeWhale session found"});return}const l=c.getUsageSnapshot();if(!l){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"usage_not_found",error_msg:"No usage data found for this session"});return}this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:s,adapterType:"codewhale",models:[{modelId:this.bindingStore.get(s)?.modelId??"codewhale",turns:l.turns,input:l.total.input,output:l.total.output}],total:{input:l.total.input,output:l.total.output},turns:l.turns,sampledAt:l.sampledAt}});return}}if(!n){u.info(this.name,`[usage] no usage data found: session_id=${s} adapterSessionId=${r} adapterType=${a}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"usage_not_found",error_msg:"No usage data found for this session"});return}u.info(this.name,`[usage] result ok: session_id=${s} adapterSessionId=${r} turns=${n.turns} models=${n.models.length}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{sessionId:r,adapterType:a,models:n.models,total:n.total,turns:n.turns,sampledAt:new Date().toISOString()}})}async handleThreadCompact(e,s){if(!s){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"session_id_required",error_msg:"session_id is required for thread_compact"});return}if(!this.bindingStore.get(s)?.cwd){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.bindingMissing,error_msg:"session binding was not found"});return}try{await this.ensureSlotStarted(s);const i=this.pool.getSlot(s)?.adapter;if(!i?.execCommand){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.runtimeError,error_msg:"Agent does not support command execution"});return}u.info(this.name,`thread_compact session_id=${s} action_id=${e.action_id}`);const n=await i.execCommand("compact","",s);n.status==="ok"?this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"compacted",message:n.message,data:n.data}}):this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.runtimeError,error_msg:n.message??"compact failed"})}catch(o){u.warn(this.name,`thread_compact error session_id=${s}: ${o instanceof Error?o.message:o}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:g.runtimeError,error_msg:o instanceof Error?o.message:String(o)})}}async handleGetRateLimits(e,s){const t=this.config.adapterType??"acp";if(this.config.aibot.clientType==="kiro"){const o=this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs),i=this.cachedAcpContextWindow;if(o&&this.cachedProviderQuota){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"acp",available:!0,cached:!0,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:i,tokenUsage:null,providerQuota:this.cachedProviderQuota}});return}try{const n=await Q();this.cachedProviderQuota=n,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[rate-limits] kiro quota queried: success=${n.success}`+(n.balance?` balance=${n.balance.remaining} ${n.balance.unit}`:"")+(n.error?` error=${n.error}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"acp",available:!0,cached:!1,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:i,tokenUsage:null,providerQuota:n}})}catch(n){u.warn(this.name,`[rate-limits] kiro quota query failed: ${n instanceof Error?n.message:String(n)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"acp",available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:i,tokenUsage:null}})}return}switch(t){case"codex":{const o=this.maybeQueryProviderQuota(),i=this.getFreshCodexGlobalRateLimitCache();if(i.hasData&&!this.isRateLimitsCacheFresh(this.cachedRateLimitsSampledAtMs)&&!this.isRateLimitsCacheFresh(this.cachedCodexUsageSampledAtMs)){const l=this.pool.getAllSlots().find(h=>h.state==="ready"&&h.adapter);if(l&&(u.info(this.name,`[rate-limits] codex cache stale, refreshing from slot: session=${l.sessionId}`),(await l.adapter.handleLocalAction?.(e))?.handled))return}if(i.hasData){i.rateLimits?u.info(this.name,`[rate-limits] codex cached: primary=${i.rateLimits.primary.usedPercent.toFixed(1)}% resetsAt=${i.rateLimits.primary.resetsAt} secondary=${i.rateLimits.secondary.usedPercent.toFixed(1)}% resetsAt=${i.rateLimits.secondary.resetsAt}`):u.info(this.name,`[rate-limits] codex cached context/token only: hasContext=${!!i.contextWindow} hasToken=${!!i.tokenUsage}`);const l=await o;this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"codex",available:i.hasData||!!l,cached:!0,sampledAt:i.sampledAt,rateLimits:i.rateLimits,contextWindow:i.contextWindow,tokenUsage:i.tokenUsage,providerQuota:l}});return}const r=this.pool.getAllSlots().find(l=>l.state==="ready"&&l.adapter);if(r&&(u.info(this.name,`[rate-limits] codex reuse existing slot: session=${r.sessionId}`),(await r.adapter.handleLocalAction?.(e))?.handled))return;const a=this.resolveRateLimitWakeSessionId(s,t);if(a){const l=await this.wakeRateLimitSlot(a,t);if(l?.adapter&&(await l.adapter.handleLocalAction?.(e))?.handled)return}const d=await o,c=!!d;u.info(this.name,`[rate-limits] codex no native data, providerQuota=${d?d.provider:"none"} available=${c}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"codex",available:c,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:d}});return}case"claude":{const o=this.maybeQueryProviderQuota(),i=this.getFreshClaudeRateLimitState();if(i){const c=i.rateLimits,l=await o;u.info(this.name,`[rate-limits] claude global cached: sampledAt=${i.sampledAt} hasRateLimits=${!!c}`+(l?` providerQuota=${l.provider}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"claude",available:!0,cached:!0,sampledAt:i.sampledAt,rateLimits:i.rateLimits??null,contextWindow:i.contextWindow,tokenUsage:null,providerQuota:l}});return}let n=this.pool.getAllSlots().find(c=>c.state==="ready"&&c.adapter instanceof T)??null;if(!n){const c=this.resolveRateLimitWakeSessionId(s,t);c&&(n=await this.wakeRateLimitSlot(c,t))}u.info(this.name,`[rate-limits] handleGetRateLimits: session_id=${s} adapterType=claude hasSlot=${!!n} hasAdapter=${!!n?.adapter}`);const r=n?.adapter,a=r instanceof T?r.getSessionState():null,d=await o;if(a){(a.rateLimits?.fiveHour||a.rateLimits?.sevenDay||a.contextWindow?.usedPercentage!=null)&&(this.cachedClaudeRateLimitState=a);const c=this.getFreshClaudeRateLimitState(),l=c?.rateLimits&&!a.rateLimits?{...a,rateLimits:c.rateLimits}:a,h=l.rateLimits;u.info(this.name,`[rate-limits] claude global state: sampledAt=${l.sampledAt} hasRateLimits=${!!h}`+(h?` fiveHour=${h.fiveHour?.usedPercentage??"n/a"}% resetsAt=${h.fiveHour?.resetsAt??"n/a"} sevenDay=${h.sevenDay?.usedPercentage??"n/a"}% resetsAt=${h.sevenDay?.resetsAt??"n/a"}`:"")+(c?.rateLimits&&!a.rateLimits?" source=live+cached-fallback":" source=live")+(d?` providerQuota=${d.provider}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"claude",available:!0,cached:!1,sampledAt:l.sampledAt,rateLimits:l.rateLimits??null,contextWindow:l.contextWindow,tokenUsage:null,providerQuota:d}})}else u.info(this.name,`[rate-limits] claude no global state: hasAdapter=${!!r} adapterType=${r?.constructor?.name??"n/a"}`+(d?` providerQuota=${d.provider}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:"claude",available:!!d,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:d}});return}case"cursor":{const i=(s?this.pool.getSlot(s):null)?.adapter,n=i instanceof P?i.getRateLimitsSnapshot():{adapterType:"cursor",available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null};this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:n});return}default:{const o=this.config.providerBaseUrl,i=this.config.providerApiKey;if(!o||!i){u.info(this.name,`[rate-limits] no provider config for adapterType=${t}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null}});return}if(this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota){u.info(this.name,`[rate-limits] provider quota cached: provider=${this.cachedProviderQuota.provider} success=${this.cachedProviderQuota.success}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!0,cached:!0,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:this.cachedProviderQuota}});return}try{const r=await K(o,i);this.cachedProviderQuota=r,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[rate-limits] provider quota queried: provider=${r.provider} success=${r.success}`+(r.tiers.length>0?` tiers=${r.tiers.map(a=>`${a.name}=${a.usedPercent}%`).join(",")}`:"")+(r.balance?` balance=${r.balance.remaining} ${r.balance.unit}`:"")+(r.error?` error=${r.error}`:"")),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!0,cached:!1,sampledAt:this.cachedProviderQuotaSampledAtMs,rateLimits:null,contextWindow:null,tokenUsage:null,providerQuota:r}})}catch(r){u.warn(this.name,`[rate-limits] provider quota query failed: ${r instanceof Error?r.message:String(r)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{adapterType:t,available:!1,cached:!1,sampledAt:null,rateLimits:null,contextWindow:null,tokenUsage:null}})}return}}}resolveRateLimitWakeSessionId(e,s){const t=String(e??"").trim(),o=this.bindingStore.getMostRecentlyUpdatedSessionId({requireCwd:!0});return o?t&&t===o?t:s==="codex"||s==="claude"?(t&&t!==o&&u.info(this.name,`[rate-limits] ${s} remap wake session: requested=${t} use_recent=${o}`),o):t&&t!==o?(u.info(this.name,`[rate-limits] skip wake slot: session=${t} adapterType=${s} reason=not_recent_session recent=${o}`),null):t||null:(u.info(this.name,`[rate-limits] skip wake slot: adapterType=${s} reason=no_recent_binding`),null)}async wakeRateLimitSlot(e,s){const t=String(e??"").trim();if(!t)return null;if(!this.bindingStore.get(t)?.cwd)return u.info(this.name,`[rate-limits] skip wake slot: session=${t} adapterType=${s} reason=binding_cwd_missing`),null;try{const i=this.pool.getOrCreateSlot(t);if(!i)return u.info(this.name,`[rate-limits] skip wake slot: session=${t} adapterType=${s} reason=slot_unavailable`),null;if(i.startPromise){const n=s==="claude"?6e4:2e4;await Promise.race([i.startPromise,new Promise((r,a)=>setTimeout(()=>a(new Error(`wake rate-limits slot timeout (${n}ms)`)),n))])}return u.info(this.name,`[rate-limits] wake slot success: session=${t} adapterType=${s}`),this.pool.getSlot(t)??i}catch(i){return u.warn(this.name,`[rate-limits] wake slot failed: session=${t} adapterType=${s} err=${i instanceof Error?i.message:String(i)}`),null}}sessionControlCtx(e){const s=this.pool.getSlot(e),t=s?.adapter instanceof A?s.adapter:null,o=(this.config.adapterType??"acp")==="acp",i={bindingStore:this.bindingStore,acpAdapter:t,globalConfigStore:this.globalConfigStore,agentName:this.name,log:u};return{getCwd:()=>this.bindingStore.get(e)?.cwd??this.config.agent.cwd??process.cwd(),getSessionBindings:()=>{if(t)return t.getSessionBindings();const n=this.sessionBindings;if(e&&!n.has(e)){const r=this.bindingStore.get(e);r?.cwd&&n.set(e,r.cwd)}return n},getStatus:()=>this.getStatus(),isAcpAlive:!!t?.isAlive(),getAcpSessionOptions:()=>t?.acpSessionOptions??null,setMode:n=>t?t.setMode(n):Promise.resolve(!1),setModel:n=>t?t.setModel(n):Promise.resolve(!1),acpSetMode:o?(n,r)=>N(i,n,r):void 0,acpSetModel:o?(n,r)=>W(i,n,r):void 0,getPendingApproval:n=>{const r=t?.pendingApprovalEntries.get(n);return r?{requestId:r}:void 0},deletePendingApproval:n=>t?.pendingApprovalEntries.delete(n)??!1,respondPermission:(n,r)=>(t&&t.respondToPermission(n,r),Promise.resolve()),onSessionBound:(n,r)=>{this.bindingStore.set(n,r)},onSessionUnbound:n=>{const a=this.bindingStore.get(n)?.cwd??"";this.bindingStore.delete(n),this.sessionBindings.delete(n),this.claudeWorkerStatus.delete(n),this.deferredMgr.clearSession(n),this.pool.drainQueuedForSession(n),a&&this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:"stopped",cwd:a})},cancelActiveRun:()=>(this.config.adapterType??"acp")==="agy"&&s?.adapter instanceof U?(s.adapter.cancelCurrentRun(),Promise.resolve()):s?.adapter?.cancel("")??Promise.resolve(),onModeSet:n=>{const r=this.config.adapterType??"acp";M.has(r)||this.globalConfigStore?.set(this.name,{acpInitialMode:n})},onModelSet:n=>{const r=this.config.adapterType??"acp";M.has(r)||this.globalConfigStore?.set(this.name,{modelId:n})}}}sessionControlSenders(){return{sendEventAck:(e,s)=>this.aibotHandle.sendEventAck({event_id:e,session_id:s,received_at:Date.now()}),sendEventResult:(e,s,t)=>this.aibotHandle.sendEventResult({event_id:e,status:s,...t?.msg?{msg:t.msg}:{},...t?.code?{code:t.code}:{},updated_at:Date.now()}),sendLocalActionResult:(e,s,t,o,i)=>this.aibotHandle.sendLocalActionResult({action_id:e,status:s,...t?{result:t}:{},...o?{error_code:o}:{},...i?{error_msg:i}:{}})}}resolveBindingChannelKey(e){switch(e){case"claude":return"grix-claude";case"codex":return"codex";case"cursor":return"cursor";case"pi":return"pi";case"openhuman":return"openhuman";case"codewhale":return"codewhale";case"opencode":return"opencode";case"agy":return"acp";case"acp":return this.config.aibot.clientType==="qwen"?"qwen":"acp";default:return e}}finalizeThinking(e,s){this.sendCtrl.finalizeThinking(e,s)}logInboundConversation(e){const s=String(e.session_id??"").trim();s&&this.conversationLog?.logInbound(s,{event_id:e.event_id,msg_id:e.msg_id,sender_id:e.sender_id,msg_type:e.msg_type,content:e.content??""})}buildInboundEvent(e,s){const t=Le(this.sendCtrl.getGlobalRuntimeConfig(),s);return{event_id:e.event_id,session_id:e.session_id,thread_id:e.thread_id,sender_id:e.sender_id,msg_id:e.msg_id,msg_type:e.msg_type,content:e.content??"",quoted_message_id:e.quoted_message_id,context_messages_json:e.context_messages?JSON.stringify(e.context_messages):void 0,extra_json:e.extra?JSON.stringify(e.extra):void 0,connector_runtime_config:{response_delivery:t.responseDelivery,tool_events:t.toolEvents,thinking_events:t.thinkingEvents},session_type:e.session_type,created_at:e.created_at}}isStaleEvent(e){const s=Number(e.created_at);return!Number.isFinite(s)||s<=0?!1:Date.now()-s>Fe}async handleConnectorRollback(e){const s=String((e.params??{}).target_version??"").trim(),t=String((e.params??{}).reason??"server_initiated");if(!s){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"MISSING_TARGET_VERSION",error_msg:"target_version is required for connector_rollback"});return}u.info(this.name,`connector_rollback: target=${s} reason=${t}`);try{const{npmInstall:o,writePending:i,removePending:n,upgradeLog:r}=await import("../core/upgrade/npm-upgrader.js"),{resolveClientVersion:a}=await import("../core/util/client-version.js"),d=a();r(`server rollback: ${d} -> ${s} reason=${t}`),i(d,s),await o("grix-connector",s),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{rolled_back_to:s}}),process.kill(process.pid,"SIGTERM")}catch(o){try{const{removePending:n}=await import("../core/upgrade/npm-upgrader.js");n()}catch{}const i=o instanceof Error?o.message:String(o);u.error(this.name,`connector_rollback failed: ${i}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"ROLLBACK_FAILED",error_msg:i})}}setUpgradeTrigger(e){this.upgradeTrigger=e}}export{Ht as AgentInstance};
@@ -1 +1 @@
1
- import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const i of c){if(!p&&i.name.startsWith("."))continue;const t=l(a,i.name),e={id:t,name:i.name,is_directory:i.isDirectory()};try{if(i.isDirectory()){const o=await m(t);e.modified_at=o.mtime.toISOString()}else{const o=await m(t);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(i.name)}}catch{}s.push(e)}return s.sort((i,t)=>i.is_directory!==t.is_directory?i.is_directory?-1:1:i.name.localeCompare(t.name)),s}export{f as listFiles,n as resolveMimeType};
1
+ import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const t of c){if(!p&&t.name.startsWith("."))continue;const i=l(a,t.name),e={id:i,name:t.name,is_directory:t.isDirectory()};try{if(t.isDirectory()){const o=await m(i);e.modified_at=o.mtime.toISOString()}else{const o=await m(i);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(t.name)}}catch{}s.push(e)}return s.sort((t,i)=>t.is_directory!==i.is_directory?t.is_directory?-1:1:t.name.localeCompare(i.name)),s}export{f as listFiles,n as resolveMimeType};
@@ -1 +1 @@
1
- const a=[{name:"grix_query",description:"Search contacts, sessions, message history, or messages by keyword in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"],description:"Query action type."},id:{type:"string",description:"Contact ID (contact_search) or Session ID (session_search)."},keyword:{type:"string",description:"Search keyword."},limit:{type:"integer",description:"Max results."},offset:{type:"integer",description:"Result offset."},sessionId:{type:"string",description:"Session ID (message_history, message_search)."},beforeId:{type:"string",description:"Pagination cursor (message_history, message_search)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}}},{name:"grix_group",description:"Manage groups in the Grix/AIBot platform: create, get details, leave, dissolve, manage members and permissions.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"],description:"Group action type."},sessionId:{type:"string",description:"Group session ID."},name:{type:"string",description:"Group name (create)."},memberIds:{type:"array",items:{type:"string"},description:"Member IDs to add/remove."},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]},description:"Member types (1=user, 2=agent)."},memberId:{type:"string",description:"Target member ID."},role:{type:"integer",enum:[1,2],description:"New role (1=admin, 2=member)."},memberType:{type:"integer",description:"Member type."},allMembersMuted:{type:"boolean",description:"Whether to mute all members."},isSpeakMuted:{type:"boolean",description:"Whether member is muted."},canSpeakWhenAllMuted:{type:"boolean",description:"Allow speaking when all muted."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}}},{name:"grix_message_send",description:"Send a message to a session in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Target session ID"},content:{type:"string",description:"Message content"},msgType:{type:"integer",description:"Message type (1=text, default 1)"},quotedMessageId:{type:"string",description:"Message ID to reply to"},threadId:{type:"string",description:"Thread ID for threaded reply"}},required:["sessionId","content"]},validation:{required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}}},{name:"grix_message_unsend",description:"Recall/unsend a message in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Session ID"},msgId:{type:"string",description:"Message ID to unsend"}},required:["sessionId","msgId"]},validation:{required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}}},{name:"grix_file_link",description:"Create a direct, tailnet-only download link for a local file on this host. Use this whenever the user asks you to send, share, give, or deliver a file that exists on the machine where you run (a report, log, build artifact, export, or any local path). It returns a ready-to-use Markdown link in the `markdown` field \u2014 include that exact Markdown link in your reply so the user can click and download the file directly over the shared Tailscale network. Each link is one-time and expires, so call this again to produce a fresh link every time you deliver a file. Requires this host to be on a tailnet (Tailscale running).",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to a local file on this host to share with the user."},ttl_ms:{type:"integer",description:"Optional link lifetime in milliseconds (default 10 minutes)."}},required:["file_path"]},validation:{required:["file_path"],properties:{file_path:{type:"string",maxLength:4096},ttl_ms:{type:"integer",minimum:1e4,maximum:864e5}}}},{name:"grix_admin",description:"Agent and category management in the Grix/AIBot platform: create agents, manage categories, rotate API keys.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"],description:"Admin action type."},agentName:{type:"string",description:"Agent name (create_agent)."},introduction:{type:"string",description:"Agent introduction (create_agent)."},isMain:{type:"boolean",description:"Set as main agent (create_agent)."},agentId:{type:"string",description:"Agent ID (assign_category, rotate_api_key)."},categoryId:{type:"string",description:"Category ID (create_agent, update_category, assign_category)."},name:{type:"string",description:"Category name (create_category, update_category)."},parentId:{type:"string",description:"Parent category ID (create_category, update_category)."},sortOrder:{type:"integer",description:"Sort order (create_category, update_category)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}}},{name:"grix_call_owner",description:"Call your owner into this session to talk by voice. Use this when, during your work, you need to reach your owner \u2014 to discuss something or to get an approval/review. It sends the owner an offline notification; when they tap it they land directly in this conversation and a voice-brain call is started automatically. Requires the owner to have configured a voice brain. Rate-limited per session.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"The session ID to call the owner into."}},required:["session_id"]},validation:{required:["session_id"],properties:{session_id:{type:"string"}}}},{name:"grix_agent_update",description:"Update the text introduction of one of your owner's agents, identified by its numeric agent ID.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},introduction:{type:"string",description:"New text introduction (max 300 characters)."}},required:["agent_id","introduction"]},validation:{required:["agent_id","introduction"],properties:{agent_id:{type:"string"},introduction:{type:"string",maxLength:300}}}},{name:"grix_dispatch_agent",description:"Dispatch one of your owner's agents to do work in a given working directory. Provide the target agent numeric ID, the working directory, and a text description of the task. The backend opens (or reuses) a private session between the owner and that agent, binds the working directory when the agent type requires it (claude/codex/etc.), and sends the task into the session as the owner so the agent starts working.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},cwd:{type:"string",description:"Absolute working directory where the agent should do the work."},task:{type:"string",description:"Text description of the task to perform."}},required:["agent_id","cwd","task"]},validation:{required:["agent_id","cwd","task"],properties:{agent_id:{type:"string"},cwd:{type:"string",maxLength:4096},task:{type:"string",maxLength:1e4}}}},{name:"grix_session_send",description:"Send a message into a session AS THE OWNER \u2014 it appears as if the owner sent it, NOT as you (the agent). Use ONLY to relay on the owner's behalf into one of the owner's OTHER sessions that you are not part of (e.g. you were dispatched to work and need to drop a note to the owner elsewhere). NEVER use this to send your own reply in a session you are conversing in \u2014 that would make your words show up as the owner's message. To answer in your current conversation, reply normally (or use grix_message_send to send as yourself). The owner must be a member of the target session, and you (the agent) must NOT be a member of it \u2014 sending into a session you belong to is rejected.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Target session ID."},content:{type:"string",description:"Message content to send as the owner."}},required:["session_id","content"]},validation:{required:["session_id","content"],properties:{session_id:{type:"string"},content:{type:"string",maxLength:1e4}}}},{name:"grix_task_query",description:"Query the session-level task states of all your owner's sessions. Takes no parameters \u2014 owner and agent are resolved from your authenticated connection. Returns one entry per session with a single mutually-exclusive state: running (working), waiting_approval (blocked on your owner to approve/deny), waiting_question (asked the owner a question, awaiting their reply), completed, failed, or idle (no task / stopped). Use this to see at a glance which tasks are done, still running, or waiting on the owner.",inputSchema:{type:"object",properties:{}},validation:{required:[],properties:{}}}],d=[{name:"grix_reply",description:"Send a reply message to the specified session. Supports streaming in chunks; the frontend will automatically aggregate them into one complete message. Any content meant for the user \u2014 including your final conclusion at the end of a turn \u2014 MUST be sent through this tool; text written outside this tool is not delivered to the user.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"Associated event ID from the inbound event."},session_id:{type:"string",description:"Target session ID."},text:{type:"string",description:"Reply text content."},quoted_message_id:{type:"string",description:"Quoted message ID (optional)."},is_final:{type:"boolean",description:"Whether this is a stage-final reply. Advisory only \u2014 does not trigger event completion; completion is handled by the complete tool or Stop hook."}},required:["session_id","text"]},validation:{required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}}},{name:"grix_complete",description:"Mark event processing as complete, notifying the backend that no more replies are expected.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to complete."},status:{type:"string",enum:["responded","canceled","failed"],description:"Completion status."},msg:{type:"string",description:"Additional note (optional)."}},required:["event_id","status"]},validation:{required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}}},{name:"grix_event_ack",description:"Acknowledge event receipt (usually done automatically by the Dispatcher; agents typically do not need to call this manually).",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to acknowledge."},session_id:{type:"string",description:"Session ID."}},required:["event_id"]},validation:{required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}}},{name:"grix_composing",description:'Set the "typing" indicator status for a session.',inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Session ID."},active:{type:"boolean",description:"true = typing, false = stopped."},event_id:{type:"string",description:"Associated event ID (optional)."}},required:["session_id","active"]},validation:{required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}}},{name:"grix_access_control",description:"Manage sender access control: pair approval, allow/remove senders, set policy.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"],description:"Access control action type."},code:{type:"string",description:"Pairing code (required for pair_approve/pair_deny)."},sender_id:{type:"string",description:"Sender ID (required for allow_sender/remove_sender)."},policy:{type:"string",enum:["allowlist","open","disabled"],description:"Access policy (required for set_policy)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}}},{name:"grix_status",description:"Query the Grix connection status of the current MCP session.",inputSchema:{type:"object",properties:{}},validation:{required:[],properties:{}}}],p=[{name:"reply",description:"Send a visible message back to the chat for this grix-claude event.",inputSchema:{type:"object",properties:{text:{type:"string",description:"The visible reply text to send."},chat_id:{type:"string",description:"The target chat/session id from the <channel> tag."},event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},reply_to:{type:"string",description:"Optional message_id to quote instead of the inbound trigger message."},final:{type:"boolean",description:"Advisory flag only. It does not complete the event; completion is handled by complete tool or Stop hook."}},required:["chat_id","event_id","text"]}},{name:"complete",description:"Finish an event without sending a visible reply so the backend does not time out.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string"},code:{type:"string"}},required:["event_id","status"]}}],c=new Set(p.map(e=>e.name)),b=new Set(a.map(e=>e.name)),v=new Set(d.map(e=>e.name)),I=/([A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+)/,w=/[A-Za-z0-9._-]+/;function C(e){return!!(b.has(e)||v.has(e)||c.has(e)||e.startsWith("mcp__grix"))}const k=[...a,...d],x=[...k,...p],P=new Map(x.map(e=>[e.name,e]));function R(e,t){return e==="reply"?{name:"grix_reply",args:_("grix_reply",{event_id:t.event_id,session_id:t.chat_id,text:t.text,quoted_message_id:t.reply_to,is_final:t.final})}:e==="complete"?{name:"grix_complete",args:_("grix_complete",{event_id:t.event_id,status:t.status,msg:t.msg,code:t.code})}:{name:e,args:t}}function l(e){const t=String(e??"").trim();return t?t.match(I)?.[1]:void 0}function u(e){const t=String(e??"").trim();if(!t)return;const n=l(t);if(n)return m(n);const i=t.match(/(?:chat_id|session_id)\s*=\s*"([A-Za-z0-9._-]+)"/)?.[1];if(i)return i;const r=t.match(/[A-Za-z0-9._-]+/g)??[];for(const s of r)if(!(s==="event_id"||s==="chat_id"||s==="session_id")&&s.length>0)return s;return t.match(w)?.[0]}function m(e){if(!e)return;const t=e.split(":",1)[0]?.trim();if(t)return u(t)}function _(e,t){if(e!=="grix_reply"&&e!=="grix_complete")return t;const n={...t},i=l(n.event_id);if(i&&(n.event_id=i),e==="grix_reply"){const r=m(i),o=String(n.session_id??""),s=u(n.session_id),f=/\bevent_id\b|["'<>\s]/.test(o);s&&!(f&&r)?n.session_id=s:r&&(n.session_id=r)}return n}function U(e){return c.has(e)}function z(e,t){switch(e){case"grix_query":return S(t);case"grix_group":return A(t);case"grix_message_send":return q(t);case"grix_message_unsend":return T(t);case"grix_file_link":return M(t);case"grix_admin":return j(t);case"grix_call_owner":return O(t);case"grix_agent_update":return D(t);case"grix_dispatch_agent":return E(t);case"grix_session_send":return L(t);case"grix_task_query":return N();default:throw new Error(`Unknown tool: ${e}`)}}const g={contact_search:"contact_search",session_search:"session_search",message_history:"message_history",message_search:"message_search"};function S(e){const t=String(e.action??""),n=g[t];if(!n)throw new Error(`Unknown grix_query action: ${t}`);const i={};return e.id!=null&&(i.id=e.id),e.keyword!=null&&(i.keyword=e.keyword),e.limit!=null&&(i.limit=e.limit),e.offset!=null&&(i.offset=e.offset),e.sessionId!=null&&(i.session_id=e.sessionId),e.beforeId!=null&&(i.before_id=e.beforeId),{action:n,params:i}}const y={create:"group_create",detail:"group_detail_read",leave:"group_leave_self",add_members:"group_member_add",remove_members:"group_member_remove",update_member_role:"group_member_role_update",update_all_members_muted:"group_all_members_muted_update",update_member_speaking:"group_member_speaking_update",dissolve:"group_dissolve"};function A(e){const t=String(e.action??""),n=y[t];if(!n)throw new Error(`Unknown grix_group action: ${t}`);const i={};return e.sessionId!=null&&(i.session_id=e.sessionId),e.name!=null&&(i.name=e.name),e.memberIds!=null&&(i.member_ids=e.memberIds),e.memberTypes!=null&&(i.member_types=e.memberTypes),e.memberId!=null&&(i.member_id=e.memberId),e.role!=null&&(i.role=e.role),e.memberType!=null&&(i.member_type=e.memberType),e.allMembersMuted!=null&&(i.all_members_muted=e.allMembersMuted),e.isSpeakMuted!=null&&(i.is_speak_muted=e.isSpeakMuted),e.canSpeakWhenAllMuted!=null&&(i.can_speak_when_all_muted=e.canSpeakWhenAllMuted),{action:n,params:i}}function q(e){const t={session_id:e.sessionId,msg_type:e.msgType??1,content:e.content};return e.quotedMessageId!=null&&(t.quoted_message_id=e.quotedMessageId),e.threadId!=null&&(t.thread_id=e.threadId),{action:"send_msg",params:t}}function T(e){return{action:"delete_msg",params:{session_id:e.sessionId,msg_id:e.msgId}}}function M(e){const t={file_path:e.file_path};return e.ttl_ms!=null&&(t.ttl_ms=e.ttl_ms),{action:"file_link",params:t}}const h={create_agent:"agent_api_create",list_categories:"agent_category_list",create_category:"agent_category_create",update_category:"agent_category_update",assign_category:"agent_category_assign",rotate_api_key:"agent_api_key_rotate"};function O(e){return{action:"call_owner",params:{session_id:e.session_id}}}function D(e){return{action:"agent_introduction_update",params:{agent_id:e.agent_id,introduction:e.introduction}}}function E(e){return{action:"dispatch_agent",params:{agent_id:e.agent_id,cwd:e.cwd,task:e.task}}}function L(e){return{action:"session_send",params:{session_id:e.session_id,content:e.content}}}function N(){return{action:"agent_task_query",params:{}}}function j(e){const t=String(e.action??""),n=h[t];if(!n)throw new Error(`Unknown grix_admin action: ${t}`);const i={};return e.agentName!=null&&(i.agent_name=e.agentName),e.introduction!=null&&(i.introduction=e.introduction),e.isMain!=null&&(i.is_main=e.isMain),e.agentId!=null&&(i.agent_id=e.agentId),e.categoryId!=null&&(i.category_id=e.categoryId),e.name!=null&&(i.name=e.name),e.parentId!=null&&(i.parent_id=e.parentId),e.sortOrder!=null&&(i.sort_order=e.sortOrder),{action:n,params:i}}const G=new Set([...Object.values(g),...Object.values(y),...Object.values(h),"send_msg","delete_msg","file_link","call_owner","agent_introduction_update","dispatch_agent","session_send","agent_task_query"]),W={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"};export{W as ACCESS_CONTROL_ACTION_MAP,k as ALL_TOOLS,d as EVENT_TOOLS,x as EXPOSED_TOOLS,G as PHASE1_INVOKE_ACTIONS,b as PHASE1_TOOL_NAMES,v as PHASE2_TOOL_NAMES,a as TOOLS,p as TOOL_ALIASES,P as TOOL_MAP,U as isAlias,C as isGrixInternalToolName,R as mapToolAlias,_ as normalizeEventToolArgs,z as toolCallToInvoke};
1
+ const a=[{name:"grix_query",description:"Search contacts, sessions, message history, or messages by keyword in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"],description:"Query action type."},id:{type:"string",description:"Contact ID (contact_search) or Session ID (session_search)."},keyword:{type:"string",description:"Search keyword."},limit:{type:"integer",description:"Max results."},offset:{type:"integer",description:"Result offset."},sessionId:{type:"string",description:"Session ID (message_history, message_search)."},beforeId:{type:"string",description:"Pagination cursor (message_history, message_search)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}}},{name:"grix_group",description:"Manage groups in the Grix/AIBot platform: create, get details, leave, dissolve, manage members and permissions.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"],description:"Group action type."},sessionId:{type:"string",description:"Group session ID."},name:{type:"string",description:"Group name (create)."},memberIds:{type:"array",items:{type:"string"},description:"Member IDs to add/remove."},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]},description:"Member types (1=user, 2=agent)."},memberId:{type:"string",description:"Target member ID."},role:{type:"integer",enum:[1,2],description:"New role (1=admin, 2=member)."},memberType:{type:"integer",description:"Member type."},allMembersMuted:{type:"boolean",description:"Whether to mute all members."},isSpeakMuted:{type:"boolean",description:"Whether member is muted."},canSpeakWhenAllMuted:{type:"boolean",description:"Allow speaking when all muted."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}}},{name:"grix_message_send",description:"Send a message to a session in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Target session ID"},content:{type:"string",description:"Message content"},msgType:{type:"integer",description:"Message type (1=text, default 1)"},quotedMessageId:{type:"string",description:"Message ID to reply to"},threadId:{type:"string",description:"Thread ID for threaded reply"}},required:["sessionId","content"]},validation:{required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}}},{name:"grix_message_unsend",description:"Recall/unsend a message in the Grix/AIBot platform.",inputSchema:{type:"object",properties:{sessionId:{type:"string",description:"Session ID"},msgId:{type:"string",description:"Message ID to unsend"}},required:["sessionId","msgId"]},validation:{required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}}},{name:"grix_file_link",description:"Create a direct, tailnet-only download link for a local file on this host. Use this whenever the user asks you to send, share, give, or deliver a file that exists on the machine where you run (a report, log, build artifact, export, or any local path). It returns a ready-to-use Markdown link in the `markdown` field \u2014 include that exact Markdown link in your reply so the user can click and download the file directly over the shared Tailscale network. Each link is one-time and expires, so call this again to produce a fresh link every time you deliver a file. Requires this host to be on a tailnet (Tailscale running).",inputSchema:{type:"object",properties:{file_path:{type:"string",description:"Absolute path to a local file on this host to share with the user."},ttl_ms:{type:"integer",description:"Optional link lifetime in milliseconds (default 10 minutes)."}},required:["file_path"]},validation:{required:["file_path"],properties:{file_path:{type:"string",maxLength:4096},ttl_ms:{type:"integer",minimum:1e4,maximum:864e5}}}},{name:"grix_admin",description:"Agent and category management in the Grix/AIBot platform: create agents, manage categories, rotate API keys.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"],description:"Admin action type."},agentName:{type:"string",description:"Agent name (create_agent)."},introduction:{type:"string",description:"Agent introduction (create_agent)."},isMain:{type:"boolean",description:"Set as main agent (create_agent)."},agentId:{type:"string",description:"Agent ID (assign_category, rotate_api_key)."},categoryId:{type:"string",description:"Category ID (create_agent, update_category, assign_category)."},name:{type:"string",description:"Category name (create_category, update_category)."},parentId:{type:"string",description:"Parent category ID (create_category, update_category)."},sortOrder:{type:"integer",description:"Sort order (create_category, update_category)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}}},{name:"grix_call_owner",description:"Call your owner into this session to talk by voice. Use this when, during your work, you need to reach your owner \u2014 to discuss something or to get an approval/review. It sends the owner an offline notification; when they tap it they land directly in this conversation and a voice-brain call is started automatically. Requires the owner to have configured a voice brain. Rate-limited per session.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"The session ID to call the owner into."}},required:["session_id"]},validation:{required:["session_id"],properties:{session_id:{type:"string"}}}},{name:"grix_agent_update",description:"Update the text introduction of one of your owner's agents, identified by its numeric agent ID.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},introduction:{type:"string",description:"New text introduction (max 300 characters)."}},required:["agent_id","introduction"]},validation:{required:["agent_id","introduction"],properties:{agent_id:{type:"string"},introduction:{type:"string",maxLength:300}}}},{name:"grix_dispatch_agent",description:"Dispatch one of your owner's agents to do work in a given working directory. Provide the target agent numeric ID, the working directory, and a text description of the task. The backend opens (or reuses) a private session between the owner and that agent, binds the working directory when the agent type requires it (claude/codex/etc.), and sends the task into the session as the owner so the agent starts working.",inputSchema:{type:"object",properties:{agent_id:{type:"string",description:"Target agent's numeric ID, passed as a string."},cwd:{type:"string",description:"Absolute working directory where the agent should do the work."},task:{type:"string",description:"Text description of the task to perform."}},required:["agent_id","cwd","task"]},validation:{required:["agent_id","cwd","task"],properties:{agent_id:{type:"string"},cwd:{type:"string",maxLength:4096},task:{type:"string",maxLength:1e4}}}},{name:"grix_session_send",description:"Send a message into a session AS THE OWNER \u2014 it appears as if the owner sent it, NOT as you (the agent). Use ONLY to relay on the owner's behalf into one of the owner's OTHER sessions that you are not part of (e.g. you were dispatched to work and need to drop a note to the owner elsewhere). NEVER use this to send your own reply in a session you are conversing in \u2014 that would make your words show up as the owner's message. To answer in your current conversation, reply normally (or use grix_message_send to send as yourself). The owner must be a member of the target session, and you (the agent) must NOT be a member of it \u2014 sending into a session you belong to is rejected.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Target session ID."},content:{type:"string",description:"Message content to send as the owner."}},required:["session_id","content"]},validation:{required:["session_id","content"],properties:{session_id:{type:"string"},content:{type:"string",maxLength:1e4}}}},{name:"grix_chat_state_query",description:"Query the chat-level task states across all of your owner's sessions (including direct and group chats). Returns one entry per session with a single mutually-exclusive state: running (working), waiting_approval (blocked on your owner to approve/deny), waiting_question (asked the owner a question, awaiting their reply), completed, failed, or idle (no task / stopped). Also returns the session title (task_title) for easy identification. Supports pagination (page/page_size) and optional state filtering. Use this to see at a glance which chats are done, still running, or waiting on the owner.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"(Optional) Query a single session by its ID. Omit to return all sessions."},page:{type:"number",description:"(Optional) Page number, starting from 1. Defaults to 1 if omitted."},page_size:{type:"number",description:"(Optional) Number of items per page, max 100. Defaults to 10 if omitted."},state:{type:"string",description:"(Optional) Filter by a specific state: running, waiting_approval, waiting_question, completed, failed, or idle. Omit to return all states."}}},validation:{required:[],properties:{}}},{name:"grix_chat_state_update",description:"Manually update the task state of a specific chat session. Use this to mark a chat as completed, failed, idle, or any other state when you need to override it manually. The reason is written to stop_reason and is optional.",inputSchema:{type:"object",properties:{session_id:{type:"string",description:"The session ID whose state to update."},state:{type:"string",enum:["running","waiting_approval","waiting_question","completed","failed","idle"],description:"The new state to set. Must be one of: running, waiting_approval, waiting_question, completed, failed, idle."},reason:{type:"string",description:"(Optional) Reason for the state change, written to stop_reason."}}},validation:{required:["session_id","state"],properties:{}}}],d=[{name:"grix_reply",description:"Send a reply message to the specified session. Supports streaming in chunks; the frontend will automatically aggregate them into one complete message. Any content meant for the user \u2014 including your final conclusion at the end of a turn \u2014 MUST be sent through this tool; text written outside this tool is not delivered to the user.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"Associated event ID from the inbound event."},session_id:{type:"string",description:"Target session ID."},text:{type:"string",description:"Reply text content."},quoted_message_id:{type:"string",description:"Quoted message ID (optional)."},is_final:{type:"boolean",description:"Whether this is a stage-final reply. Advisory only \u2014 does not trigger event completion; completion is handled by the complete tool or Stop hook."}},required:["session_id","text"]},validation:{required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}}},{name:"grix_complete",description:"Mark event processing as complete, notifying the backend that no more replies are expected.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to complete."},status:{type:"string",enum:["responded","canceled","failed"],description:"Completion status."},msg:{type:"string",description:"Additional note (optional)."}},required:["event_id","status"]},validation:{required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}}},{name:"grix_event_ack",description:"Acknowledge event receipt (usually done automatically by the Dispatcher; agents typically do not need to call this manually).",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The event ID to acknowledge."},session_id:{type:"string",description:"Session ID."}},required:["event_id"]},validation:{required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}}},{name:"grix_composing",description:'Set the "typing" indicator status for a session.',inputSchema:{type:"object",properties:{session_id:{type:"string",description:"Session ID."},active:{type:"boolean",description:"true = typing, false = stopped."},event_id:{type:"string",description:"Associated event ID (optional)."}},required:["session_id","active"]},validation:{required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}}},{name:"grix_access_control",description:"Manage sender access control: pair approval, allow/remove senders, set policy.",inputSchema:{type:"object",properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"],description:"Access control action type."},code:{type:"string",description:"Pairing code (required for pair_approve/pair_deny)."},sender_id:{type:"string",description:"Sender ID (required for allow_sender/remove_sender)."},policy:{type:"string",enum:["allowlist","open","disabled"],description:"Access policy (required for set_policy)."}},required:["action"]},validation:{required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}}},{name:"grix_status",description:"Query the Grix connection status of the current MCP session.",inputSchema:{type:"object",properties:{}},validation:{required:[],properties:{}}}],p=[{name:"reply",description:"Send a visible message back to the chat for this grix-claude event.",inputSchema:{type:"object",properties:{text:{type:"string",description:"The visible reply text to send."},chat_id:{type:"string",description:"The target chat/session id from the <channel> tag."},event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},reply_to:{type:"string",description:"Optional message_id to quote instead of the inbound trigger message."},final:{type:"boolean",description:"Advisory flag only. It does not complete the event; completion is handled by complete tool or Stop hook."}},required:["chat_id","event_id","text"]}},{name:"complete",description:"Finish an event without sending a visible reply so the backend does not time out.",inputSchema:{type:"object",properties:{event_id:{type:"string",description:"The Aibot event_id from the <channel> tag."},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string"},code:{type:"string"}},required:["event_id","status"]}}],c=new Set(p.map(e=>e.name)),b=new Set(a.map(e=>e.name)),v=new Set(d.map(e=>e.name)),I=/([A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+:[A-Za-z0-9._-]+)/,w=/[A-Za-z0-9._-]+/;function z(e){return!!(b.has(e)||v.has(e)||c.has(e)||e.startsWith("mcp__grix"))}const k=[...a,...d],x=[...k,...p],P=new Map(x.map(e=>[e.name,e]));function R(e,t){return e==="reply"?{name:"grix_reply",args:_("grix_reply",{event_id:t.event_id,session_id:t.chat_id,text:t.text,quoted_message_id:t.reply_to,is_final:t.final})}:e==="complete"?{name:"grix_complete",args:_("grix_complete",{event_id:t.event_id,status:t.status,msg:t.msg,code:t.code})}:{name:e,args:t}}function l(e){const t=String(e??"").trim();return t?t.match(I)?.[1]:void 0}function u(e){const t=String(e??"").trim();if(!t)return;const n=l(t);if(n)return m(n);const i=t.match(/(?:chat_id|session_id)\s*=\s*"([A-Za-z0-9._-]+)"/)?.[1];if(i)return i;const o=t.match(/[A-Za-z0-9._-]+/g)??[];for(const s of o)if(!(s==="event_id"||s==="chat_id"||s==="session_id")&&s.length>0)return s;return t.match(w)?.[0]}function m(e){if(!e)return;const t=e.split(":",1)[0]?.trim();if(t)return u(t)}function _(e,t){if(e!=="grix_reply"&&e!=="grix_complete")return t;const n={...t},i=l(n.event_id);if(i&&(n.event_id=i),e==="grix_reply"){const o=m(i),r=String(n.session_id??""),s=u(n.session_id),f=/\bevent_id\b|["'<>\s]/.test(r);s&&!(f&&o)?n.session_id=s:o&&(n.session_id=o)}return n}function U(e){return c.has(e)}function G(e,t){switch(e){case"grix_query":return S(t);case"grix_group":return A(t);case"grix_message_send":return q(t);case"grix_message_unsend":return T(t);case"grix_file_link":return M(t);case"grix_admin":return C(t);case"grix_call_owner":return O(t);case"grix_agent_update":return D(t);case"grix_dispatch_agent":return E(t);case"grix_session_send":return L(t);case"grix_chat_state_query":return N(t);case"grix_chat_state_update":return j(t);default:throw new Error(`Unknown tool: ${e}`)}}const g={contact_search:"contact_search",session_search:"session_search",message_history:"message_history",message_search:"message_search"};function S(e){const t=String(e.action??""),n=g[t];if(!n)throw new Error(`Unknown grix_query action: ${t}`);const i={};return e.id!=null&&(i.id=e.id),e.keyword!=null&&(i.keyword=e.keyword),e.limit!=null&&(i.limit=e.limit),e.offset!=null&&(i.offset=e.offset),e.sessionId!=null&&(i.session_id=e.sessionId),e.beforeId!=null&&(i.before_id=e.beforeId),{action:n,params:i}}const y={create:"group_create",detail:"group_detail_read",leave:"group_leave_self",add_members:"group_member_add",remove_members:"group_member_remove",update_member_role:"group_member_role_update",update_all_members_muted:"group_all_members_muted_update",update_member_speaking:"group_member_speaking_update",dissolve:"group_dissolve"};function A(e){const t=String(e.action??""),n=y[t];if(!n)throw new Error(`Unknown grix_group action: ${t}`);const i={};return e.sessionId!=null&&(i.session_id=e.sessionId),e.name!=null&&(i.name=e.name),e.memberIds!=null&&(i.member_ids=e.memberIds),e.memberTypes!=null&&(i.member_types=e.memberTypes),e.memberId!=null&&(i.member_id=e.memberId),e.role!=null&&(i.role=e.role),e.memberType!=null&&(i.member_type=e.memberType),e.allMembersMuted!=null&&(i.all_members_muted=e.allMembersMuted),e.isSpeakMuted!=null&&(i.is_speak_muted=e.isSpeakMuted),e.canSpeakWhenAllMuted!=null&&(i.can_speak_when_all_muted=e.canSpeakWhenAllMuted),{action:n,params:i}}function q(e){const t={session_id:e.sessionId,msg_type:e.msgType??1,content:e.content};return e.quotedMessageId!=null&&(t.quoted_message_id=e.quotedMessageId),e.threadId!=null&&(t.thread_id=e.threadId),{action:"send_msg",params:t}}function T(e){return{action:"delete_msg",params:{session_id:e.sessionId,msg_id:e.msgId}}}function M(e){const t={file_path:e.file_path};return e.ttl_ms!=null&&(t.ttl_ms=e.ttl_ms),{action:"file_link",params:t}}const h={create_agent:"agent_api_create",list_categories:"agent_category_list",create_category:"agent_category_create",update_category:"agent_category_update",assign_category:"agent_category_assign",rotate_api_key:"agent_api_key_rotate"};function O(e){return{action:"call_owner",params:{session_id:e.session_id}}}function D(e){return{action:"agent_introduction_update",params:{agent_id:e.agent_id,introduction:e.introduction}}}function E(e){return{action:"dispatch_agent",params:{agent_id:e.agent_id,cwd:e.cwd,task:e.task}}}function L(e){return{action:"session_send",params:{session_id:e.session_id,content:e.content}}}function N(e){const t={};return e.session_id!=null&&(t.session_id=e.session_id),e.page!=null&&(t.page=e.page),e.page_size!=null&&(t.page_size=e.page_size),e.state!=null&&(t.state=e.state),{action:"chat_state_query",params:t}}function j(e){const t={session_id:e.session_id,state:e.state};return e.reason!=null&&(t.reason=e.reason),{action:"chat_state_update",params:t}}function C(e){const t=String(e.action??""),n=h[t];if(!n)throw new Error(`Unknown grix_admin action: ${t}`);const i={};return e.agentName!=null&&(i.agent_name=e.agentName),e.introduction!=null&&(i.introduction=e.introduction),e.isMain!=null&&(i.is_main=e.isMain),e.agentId!=null&&(i.agent_id=e.agentId),e.categoryId!=null&&(i.category_id=e.categoryId),e.name!=null&&(i.name=e.name),e.parentId!=null&&(i.parent_id=e.parentId),e.sortOrder!=null&&(i.sort_order=e.sortOrder),{action:n,params:i}}const W=new Set([...Object.values(g),...Object.values(y),...Object.values(h),"send_msg","delete_msg","file_link","call_owner","agent_introduction_update","dispatch_agent","session_send","chat_state_query","chat_state_update"]),Q={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"};export{Q as ACCESS_CONTROL_ACTION_MAP,k as ALL_TOOLS,d as EVENT_TOOLS,x as EXPOSED_TOOLS,W as PHASE1_INVOKE_ACTIONS,b as PHASE1_TOOL_NAMES,v as PHASE2_TOOL_NAMES,a as TOOLS,p as TOOL_ALIASES,P as TOOL_MAP,U as isAlias,z as isGrixInternalToolName,R as mapToolAlias,_ as normalizeEventToolArgs,G as toolCallToInvoke};
@@ -1,11 +1,11 @@
1
- import{isAbsolute as y}from"node:path";const m="[\u5F15\u7528\u6D88\u606F]",g=/^(?:(?:[ \t]*>[^\r\n]*(?:\r?\n|$))+)(?:[ \t]*\r?\n)*[ \t]*-{3,}[ \t]*(?:\r?\n|$)(?:[ \t]*\r?\n)*/;function M(t,n){const r=_(t.extra_json),o=h(t.context_messages_json),e=A(t,r),i=b(e,n),s=x(t,e,o),c=D(e),u=O(e,"additional_directories","additionalDirectories"),f=a(e,"mode_id","modeId"),p=a(e,"model_id","modelId"),d=k(e,"timeout_ms","timeoutMs");return{cwd:i,prompt:s,mcpServers:c,additionalDirectories:u??[],modeId:f,modelId:p,timeoutMs:d}}function A(t,n){const r=[t,l(t.acp),l(t.gemini_acp)];return n&&(r.push(n),r.push(l(n.acp)),r.push(l(n.gemini_acp))),r.filter(o=>o!==null)}function _(t){if(t==null)return null;if(typeof t=="object"&&!Array.isArray(t))return t;if(typeof t!="string")return null;const n=t.trim();if(!n)return null;try{const r=JSON.parse(n);if(r&&typeof r=="object"&&!Array.isArray(r))return r}catch{}return null}function h(t){if(t==null)return[];if(Array.isArray(t))return t;if(typeof t!="string")return[];const n=t.trim();if(!n)return[];try{const r=JSON.parse(n);if(Array.isArray(r))return r}catch{}return[]}function b(t,n){const o=a(t,"cwd","workdir","working_directory")?.trim()||n?.trim()||"";if(!o)return process.cwd();if(!y(o))throw new Error(`CWD must be an absolute path: ${o}`);return o}function x(t,n,r){const o=E(n,"prompt");if(o&&o.length>0){const u=o.map(f=>f?.type==="text"?f.text:"").filter(Boolean);if(u.length>0)return u.join(`
2
- `)}const e=t.quoted_message_id,i=t.msg_id;let s=a(n,"text")??t.content?.trim()??"";s=I(s,r,e);const c=j(r,i);return!c&&!s?"":c?s?`Conversation context:
1
+ import{isAbsolute as y}from"node:path";const p="[\u5F15\u7528\u6D88\u606F]",g=/^(?:(?:[ \t]*>[^\r\n]*(?:\r?\n|$))+)(?:[ \t]*\r?\n)*[ \t]*-{3,}[ \t]*(?:\r?\n|$)(?:[ \t]*\r?\n)*/;function M(t,n){const e=_(t.extra_json),o=h(t.context_messages_json),r=A(t,e),i=b(r,n),s=x(t,r,o),u=S(r),c=E(r,"additional_directories","additionalDirectories"),f=l(r,"mode_id","modeId"),a=l(r,"model_id","modelId"),d=$(r,"timeout_ms","timeoutMs");return{cwd:i,prompt:s,mcpServers:u,additionalDirectories:c??[],modeId:f,modelId:a,timeoutMs:d}}function A(t,n){const e=[t,m(t.acp),m(t.gemini_acp)];return n&&(e.push(n),e.push(m(n.acp)),e.push(m(n.gemini_acp))),e.filter(o=>o!==null)}function _(t){if(t==null)return null;if(typeof t=="object"&&!Array.isArray(t))return t;if(typeof t!="string")return null;const n=t.trim();if(!n)return null;try{const e=JSON.parse(n);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return null}function h(t){if(t==null)return[];if(Array.isArray(t))return t;if(typeof t!="string")return[];const n=t.trim();if(!n)return[];try{const e=JSON.parse(n);if(Array.isArray(e))return e}catch{}return[]}function b(t,n){const o=l(t,"cwd","workdir","working_directory")?.trim()||n?.trim()||"";if(!o)return process.cwd();if(!y(o))throw new Error(`CWD must be an absolute path: ${o}`);return o}function x(t,n,e){const o=D(n,"prompt");if(o&&o.length>0){const f=o.map(a=>a?.type==="text"?a.text:"").filter(Boolean);if(f.length>0)return f.join(`
2
+ `)}const r=t.quoted_message_id,i=t.msg_id;let s=l(n,"text")??t.content?.trim()??"";s=I(s,e,r);const u=Number(t.session_type)===2,c=j(e,i,u);return!c&&!s?"":c?s?`Conversation context:
3
3
  `+c+`
4
4
 
5
5
  Latest user message:
6
6
  `+s:`Conversation context:
7
- `+c:s}function j(t,n){if(!t||t.length===0)return"";const r=[];for(const o of t){if(n&&o.msg_id===n)continue;const e=o.content?.trim();if(!e)continue;const i=C(e);if(i)r.push(`Quoted message:
8
- `+i);else{const s=o.sender_type===2?"Assistant message":"User message";r.push(`${s}:
9
- ${e}`)}}return r.join(`
7
+ `+c:s}function j(t,n,e){if(!t||t.length===0)return"";const o=[];for(const r of t){if(n&&r.msg_id===n)continue;const i=r.content?.trim();if(!i)continue;const s=e&&r.sender_id?` (from ${r.sender_id})`:"",u=C(i);if(u)o.push(`Quoted message${s}:
8
+ `+u);else{const c=r.sender_type===2?"Assistant message":"User message";o.push(`${c}${s}:
9
+ ${i}`)}}return o.join(`
10
10
 
11
- `)}function C(t){return t.startsWith(m)?t.slice(m.length).replace(/^\s*\n?/,"").trim()||m:null}function I(t,n,r){return!r||!n?.some(o=>o.msg_id===r&&o.content?.trim())?t:t.replace(g,"")}function D(t){for(const n of t){const r=n.mcp_servers??n.mcpServers,o=S(r);if(o&&o.every(e=>!!e&&typeof e=="object"&&!Array.isArray(e)))return o.map(e=>({name:e.name??"unknown",command:e.command??"",args:e.args,env:e.env}))}return[]}function a(t,...n){for(const r of t)for(const o of n){const e=r[o];if(typeof e=="string"&&e.trim())return e.trim()}return null}function E(t,n){for(const r of t){const o=r[n];if(Array.isArray(o)&&o.length>0)return o}return null}function O(t,...n){for(const r of t)for(const o of n){const e=r[o];if(!Array.isArray(e))continue;const i=e.filter(s=>typeof s=="string");if(i.length>0)return i}return null}function l(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function S(t){return Array.isArray(t)?t:null}function k(t,...n){for(const r of t)for(const o of n){const e=r[o];if(typeof e=="number"&&Number.isFinite(e)&&e>0)return Math.floor(e)}return null}export{M as extractAcpTurnInput};
11
+ `)}function C(t){return t.startsWith(p)?t.slice(p.length).replace(/^\s*\n?/,"").trim()||p:null}function I(t,n,e){return!e||!n?.some(o=>o.msg_id===e&&o.content?.trim())?t:t.replace(g,"")}function S(t){for(const n of t){const e=n.mcp_servers??n.mcpServers,o=O(e);if(o&&o.every(r=>!!r&&typeof r=="object"&&!Array.isArray(r)))return o.map(r=>({name:r.name??"unknown",command:r.command??"",args:r.args,env:r.env}))}return[]}function l(t,...n){for(const e of t)for(const o of n){const r=e[o];if(typeof r=="string"&&r.trim())return r.trim()}return null}function D(t,n){for(const e of t){const o=e[n];if(Array.isArray(o)&&o.length>0)return o}return null}function E(t,...n){for(const e of t)for(const o of n){const r=e[o];if(!Array.isArray(r))continue;const i=r.filter(s=>typeof s=="string");if(i.length>0)return i}return null}function m(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function O(t){return Array.isArray(t)?t:null}function $(t,...n){for(const e of t)for(const o of n){const r=e[o];if(typeof r=="number"&&Number.isFinite(r)&&r>0)return Math.floor(r)}return null}export{M as extractAcpTurnInput};
@@ -1,3 +1,3 @@
1
- const r="[[quoted_message_id:";class n{mode="pending";buffer="";quotedMessageId;consume(e){if(!e)return{deltaContent:"",quotedMessageId:this.quotedMessageId};if(this.mode==="streaming")return{deltaContent:e,quotedMessageId:this.quotedMessageId};this.buffer+=e;const t=this.tryParseBuffer();return t.status==="need_more"?{deltaContent:"",quotedMessageId:this.quotedMessageId}:(this.mode="streaming",this.buffer="",t.status==="matched"?(this.quotedMessageId=t.quotedMessageId,{deltaContent:t.remainingContent,quotedMessageId:this.quotedMessageId}):{deltaContent:t.remainingContent,quotedMessageId:this.quotedMessageId})}flush(){if(this.mode==="streaming"||!this.buffer)return{deltaContent:"",quotedMessageId:this.quotedMessageId};const e=this.buffer;return this.buffer="",this.mode="streaming",{deltaContent:e,quotedMessageId:this.quotedMessageId}}getQuotedMessageId(){return this.quotedMessageId}tryParseBuffer(){if(!this.buffer.startsWith("[["))return{status:"plain_text",remainingContent:this.buffer};if(!r.startsWith(this.buffer)&&!this.buffer.startsWith(r))return{status:"plain_text",remainingContent:this.buffer};if(r.startsWith(this.buffer))return{status:"need_more"};const e=this.buffer.indexOf("]]",r.length);if(e<0)return{status:"need_more"};const t=this.buffer.slice(r.length,e).trim();if(!t)return{status:"plain_text",remainingContent:this.buffer};let s=this.buffer.slice(e+2);return s.startsWith(`\r
2
- `)?s=s.slice(2):s.startsWith(`
3
- `)&&(s=s.slice(1)),{status:"matched",quotedMessageId:t,remainingContent:s}}}export{n as QuotedMessageStream};
1
+ const i="[[quoted_message_id:",n="[[message_id:",d=/\[\[message_id:[^\]]*\]\]\r?\n?/g;class f{mode="pending";buffer="";quotedMessageId;idTail="";consume(t){if(!t)return{deltaContent:"",quotedMessageId:this.quotedMessageId};if(this.mode==="streaming")return{deltaContent:this.stripMessageIdMarkers(t,!0),quotedMessageId:this.quotedMessageId};this.buffer+=t;const s=this.tryParseBuffer();return s.status==="need_more"?{deltaContent:"",quotedMessageId:this.quotedMessageId}:(this.mode="streaming",this.buffer="",s.status==="matched"&&(this.quotedMessageId=s.quotedMessageId),{deltaContent:this.stripMessageIdMarkers(s.remainingContent,!0),quotedMessageId:this.quotedMessageId})}flush(){let t="";return this.mode!=="streaming"&&this.buffer&&(t=this.buffer,this.buffer="",this.mode="streaming"),{deltaContent:this.stripMessageIdMarkers(t,!1),quotedMessageId:this.quotedMessageId}}getQuotedMessageId(){return this.quotedMessageId}stripMessageIdMarkers(t,s){let e=this.idTail+t;if(this.idTail="",e=e.replace(d,""),s){const a=e.lastIndexOf("[[");if(a>=0){const r=e.slice(a);!r.includes("]]")&&(n.startsWith(r)||r.startsWith(n))&&(this.idTail=r,e=e.slice(0,a))}}return e}tryParseBuffer(){if(!this.buffer.startsWith("[["))return{status:"plain_text",remainingContent:this.buffer};if(!i.startsWith(this.buffer)&&!this.buffer.startsWith(i))return{status:"plain_text",remainingContent:this.buffer};if(i.startsWith(this.buffer))return{status:"need_more"};const t=this.buffer.indexOf("]]",i.length);if(t<0)return{status:"need_more"};const s=this.buffer.slice(i.length,t).trim();if(!s)return{status:"plain_text",remainingContent:this.buffer};let e=this.buffer.slice(t+2);return e.startsWith(`\r
2
+ `)?e=e.slice(2):e.startsWith(`
3
+ `)&&(e=e.slice(1)),{status:"matched",quotedMessageId:s,remainingContent:e}}}export{f as QuotedMessageStream};