grix-connector 3.0.3 → 3.1.1

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.
@@ -1,17 +1,20 @@
1
- import{fileURLToPath as A}from"node:url";import m from"node:path";import{homedir as v}from"node:os";import{promises as g}from"node:fs";import{EventEmitter as C}from"node:events";import{spawn as R}from"node:child_process";import{AgentProcess as y}from"../../agent/process.js";import{hasChildProcesses as T}from"../../core/runtime/spawn.js";import{syncDefaultSkillsToDir as w}from"../../default-skills/index.js";import{AcpClient as k,AcpAuthRequiredError as _,isAuthRequiredError as $}from"../../protocol/acp-client.js";import{AgentEventType as p}from"../../types/events.js";import{InternalApiServer as E}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as P}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as M}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as x}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as q}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as D}from"../../core/protocol/message-metadata.js";import{log as a}from"../../core/log/index.js";import{scanSkills as I}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as N}from"./session-activity.js";import{resolveCliPath as B,getCliVersion as U}from"../../core/util/cli-probe.js";const S=m.dirname(A(import.meta.url)),j=200,F=60*1e3,O=600*1e3,b=80,L=["[grix protocol] Markers like [[message_id:<id>]] and [[quoted_message_id:<id>]] are metadata, not user prose \u2014 never repeat them verbatim except as instructed here.","The message you are replying to carries its id in a [[message_id:<id>]] marker.","If (and only if) you want your reply to quote/thread onto that specific message, include [[quoted_message_id:<id>]] anywhere in your reply using that id; otherwise omit it."].join(" ");function K(h){return!(!(h instanceof Error)||h.code!==-32603||$(h))}function f(h){if(!(h instanceof Error))return String(h);let e=h.message;const i=h.data;return i&&(e+=` data=${JSON.stringify(i)}`),e}function H(h){return h.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class pe extends C{type="acp";config;callbacks;agentProcess=null;acpClient=null;internalApi=null;activeRun=null;compacting=!1;compactingTimer=null;compactionDoneResolver=null;pendingAutoCompact=!1;eventResults=null;pendingApprovals=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;acpMcpTools;rawTransport;approvalMode;autoInjectArgs;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;bridgeLog;sessionConnected=!1;rawEventSeq=0;recoveryContextBySessionId=new Map;quoteProtocolTaughtSessions=new Set;constructor(e,i,t){if(super(),this.config=e,this.callbacks=i,this.bridgeLog=t?.bridgeLog??null,this.acpAuthMethod=t?.acpAuthMethod,this.acpInitialMode=t?.acpInitialMode,this.acpInitialModel=t?.acpInitialModel,this.acpMcpTools=t?.acpMcpTools??!0,this.rawTransport=t?.rawTransport??!1,this.approvalMode=t?.approvalMode??"default",this.autoInjectArgs=t?.autoInjectArgs,this.bindingStore=t?.bindingStore??null,this.currentAibotSessionId=t?.aibotSessionId?String(t.aibotSessionId).trim():void 0,t?.eventResultsPath&&(this.eventResults=new P(t.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const s=this.bindingStore.get(this.currentAibotSessionId);s?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,s.cwd)}}async start(){if(await this.spawnProcess(),!this.bindingStore){await this.connectSession(this.resolveCwd());return}const e=this.currentAibotSessionId?this.sessionBindings.get(this.currentAibotSessionId):void 0;e&&await this.connectSession(e)}async stop(){this.stopped=!0,this.rejectDeferredEvents("adapter stopped"),this.cancelAllDeferredTimers(),this.pendingAutoCompact=!1,this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver(),this.compactionDoneResolver=null),this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null),this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,i){}async destroySession(e){}sendPrompt(e){const i=new W(e.adapterSessionId);return this.acpClient?.isAlive&&this.acpClient.send(e.text).catch(t=>{i.emitError(t instanceof Error?t:new Error(String(t)))}),i}async cancel(e){this.activeRun&&this.acpClient&&(await this.acpClient.cancel(),this.flushStream(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(e){}async ping(e){return this.acpClient?.isAlive?this.acpClient.ping(e):this.agentProcess?.alive??!1}getStatus(){return{alive:this.agentProcess?.alive??!1,busy:this.activeRun!==null||this.compacting,sessions:this.sessionConnected?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null)}getMcpConfig(){if(!this.internalApi)return null;this.internalApi.setMcpBridgeUpHandler(i=>this.onMcpBridgeUp(i));const e=m.resolve(S,"../../mcp/mcp-bridge-server.js");return{name:"grix-app-bridge",command:process.execPath,args:[e,"--ws-url",this.internalApi.mcpBridgeWsUrl]}}async hasBackgroundWork(){const e=this.agentProcess?.pid;return e?T(e,[e]):!1}async probe(e){const i=this.config.command||"gemini",t=await B(i),s=t!==null;let n=null,o;if(s){const u=await U(i);n=u.version,u.error&&(o=u.error)}else o={code:"cli_not_found",message:`command not found: ${i}`};const r=this.acpClient?.model??null,c=this.getStatus();let d;return e?.conversation?this.acpClient?.isAlive?d={attempted:!0,ok:!0,latency_ms:null}:d={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:d={attempted:!1,ok:!1,latency_ms:null},{cli:{command:i,installed:s,path:t,version:n,...o?{error:o}:{}},conversation:d,config:{model:r,base_url:null,source:{model:r?"runtime":"unknown",base_url:"unknown"}},process:{started:!!this.agentProcess,alive:c.alive,busy:c.busy}}}getSupportedCommands(){return this.acpClient?.availableCommands?.length?this.acpClient.availableCommands.map(e=>({name:e.name,description:e.description??"",args:e.args})):[{name:"model",description:"List or set model",args:"[model_id]"},{name:"mode",description:"List or set collaboration mode",args:"[mode_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"compact",description:"\u538B\u7F29\u4E0A\u4E0B\u6587"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(e,i,t){return this.acpClient?.supportsCommandsExecute?this.execCommandViaAcp(e,i):this.execCommandFallback(e,i)}async execCommandViaAcp(e,i){if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(e==="interrupt")return this.activeRun?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};if(e==="compact"){if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;try{const t=await this.acpClient.executeCommand(e,i||void 0);return this.finishCompaction("acp-commands-execute"),{status:t.status==="failed"?"failed":"ok",message:t.message??"Compacted",data:t.data}}catch(t){return this.finishCompaction("error"),t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`compact failed: ${t instanceof Error?t.message:t}`}}}try{const t=await this.acpClient.executeCommand(e,i||void 0);return{status:t.status??"ok",message:t.message,data:t.options?{command:e,options:t.options}:t.data}}catch(t){return t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`Command failed: ${t instanceof Error?t.message:t}`}}}async execCommandFallback(e,i){try{switch(e){case"model":{const t=i.trim();if(t)return await this.setModel(t)?{status:"ok",message:`Model set to ${t}`}:{status:"failed",message:`Failed to set model: ${t}`};const s=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${s.currentModelId||"unknown"}`,data:s}}case"mode":{const t=i.trim();if(t)return await this.setMode(t)?{status:"ok",message:`Mode set to ${t}`}:{status:"failed",message:`Failed to set mode: ${t}`};const s=this.buildToolbarContext("mode_list");return{status:"ok",message:`Current: ${s.currentModeId||"unknown"}`,data:s}}case"interrupt":return this.activeRun?this.acpClient?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"Not connected"}:{status:"failed",message:"No active run to interrupt"};case"compact":{if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;const t=new Promise(s=>{this.compactionDoneResolver=s});try{await this.acpClient.send("/compact")}catch(s){throw this.finishCompaction("send-failed"),s}return this.compactingTimer=setTimeout(()=>this.finishCompaction("timeout"),15e3),this.compactingTimer.unref?.(),await t,{status:"ok",message:"Compacted"}}case"status":{const t=this.getStatus(),s=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${t.alive}, Busy: ${t.busy}, Model: ${s?.currentModelId??"unknown"}, Mode: ${s?.currentModeId??"unknown"}`,data:{alive:t.alive,busy:t.busy,sessions:t.sessions,model:s?.currentModelId??"",mode:s?.currentModeId??""}}}case"skills":{const t=I({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),s=t.map(n=>`- ${n.name}${n.trigger?` (${n.trigger})`:""} [${n.source}]: ${n.description}`);return{status:"ok",message:s.length>0?s.join(`
2
- `):"No skills found",data:t}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(t){return{status:"failed",message:t instanceof Error?t.message:String(t)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}buildToolbarContext(e,i){const t=this.acpClient?.sessionOptions,s=t?.currentModeId??"";let n=t?.currentModelId??"";const o=t?.modes.map(l=>({id:l.id,name:l.name}))??[],r=t?.models.map(l=>({modelId:l.modelId,name:l.name}))??[],c=t?.models.map(l=>({id:l.modelId,displayName:l.name}))??[],d=t?.modes.map(l=>({id:l.id,displayName:l.name}))??[];c.length>0&&!c.some(l=>l.id===n)&&(n=c[0].id);const u={outcome:e,...i?{cwd:i}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:c,available_modes:d,availableModels:c,availableModes:d,models:r,modes:o};return a.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(c.map(l=>l.id))} currentModelId=${n}`),u}get pendingApprovalEntries(){return this.pendingApprovals}get hasSessionBinding(){return this.bindingStore!==null}setMode(e){return this.acpClient?this.acpClient.setLiveMode(e):Promise.resolve(!1)}setModel(e){return this.acpClient?this.acpClient.setModel(e):Promise.resolve(!1)}handleAcpApprovalAction(e,i){const t=this.pendingApprovals.get(e);return t?(this.pendingApprovals.delete(e),this.acpClient&&this.acpClient.respondPermission(t,{behavior:i}).catch(s=>{a.error("acp-adapter",`Failed to respond to permission: ${s}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun),!0):!1}respondToPermission(e,i){this.acpClient&&this.acpClient.respondPermission(e,i).catch(t=>{a.error("acp-adapter",`Failed to respond to permission: ${t}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun)}async handleLocalAction(e){const i=e.action_type??"",t=e.params??{};if(i==="exec_approve"||i==="exec_reject"||i==="permission_approve"||i==="permission_reject"){const s=String(t.tool_call_id??t.approval_command_id??t.approval_id??t.exec_context_id??""),n=i==="exec_approve"||i==="permission_approve",o=String(t.decision??"");let r;return n&&(o==="allow-once"||o==="allow-always")?r=o:n?r="allow":r="deny",s?this.handleAcpApprovalAction(s,r)?(this.callbacks.sendLocalActionResult(e.action_id,"ok"),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"approval_not_found",`no pending approval for tool_call_id: ${s}`),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"tool_call_id_required","tool_call_id is required"),{handled:!0,kind:"approval"})}return{handled:!1,kind:""}}resolveCwd(){if(this.currentAibotSessionId){const e=this.sessionBindings.get(this.currentAibotSessionId);if(e)return e}if(this.bindingStore&&this.currentAibotSessionId){const e=this.bindingStore.get(this.currentAibotSessionId);if(e?.cwd)return e.cwd}return process.cwd()}async bindSession(e,i){const t=this.sessionBindings.get(e);if(t)try{return await g.stat(t),!1}catch{a.info("acp-adapter",`Stale binding for session ${e}: ${t} no longer exists, allowing rebind to ${i}`),this.sessionBindings.delete(e)}return this.sessionBindings.set(e,i),this.bindingStore&&this.bindingStore.set(e,i),this.maybeRefreshKiroSkills(i),!this.sessionConnected&&this.agentProcess?.alive?this.connectSession(i).then(()=>!0).catch(s=>(a.error("acp-adapter",`Failed to create session on bind: ${f(s)}`),this.callbacks.sendUpdateBindingCard(e,"failed",i),!0)):(this.acpClient?.isAlive&&(this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(e,this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(e,"connected",i,this.buildToolbarContext("binding_ready",i))),Promise.resolve(!0))}getSessionCwd(e){return this.sessionBindings.get(e)}getSessionBindings(){return this.sessionBindings}maybeRefreshKiroSkills(e){if(this.config.command!=="kiro-cli"||!this.callbacks.onSkillsUpdate)return;const i=String(e??"").trim()||void 0;let t=[];try{t=I({mode:"kiro",projectDir:i})}catch(s){a.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(t)}catch(s){a.warn("acp-adapter",`onSkillsUpdate failed: ${s instanceof Error?s.message:String(s)}`)}}replayDeferredEvents(e){const i=this.deferredEvents.get(e);if(!i||i.length===0)return;if(this.activeRun){a.info("acp-adapter",`Cannot replay deferred events for session ${e}: agent busy`);return}const t=i.shift();t&&(i.length===0?(this.deferredEvents.delete(e),this.cancelDeferredTimer(e)):this.deferredEvents.set(e,i),a.info("acp-adapter",`Replaying deferred event ${t.event.event_id} for session ${e} (remaining: ${i.length})`),this.startRun(t.event,!1))}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),a.info("acp-adapter",`Compaction finished (${e}); replaying deferred events`),this.replayNextDeferredEvent();const i=this.compactionDoneResolver;this.compactionDoneResolver=null,i?.()}replayNextDeferredEvent(){if(this.activeRun||this.compacting)return;const e=this.deferredEvents.keys().next().value;e&&this.replayDeferredEvents(e)}announceDeferredComposing(e){}deliverInboundEvent(e){if(this.eventResults?.has(e.session_id,e.event_id)){const i=this.eventResults.get(e.session_id,e.event_id);a.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${i.status})`),this.callbacks.sendEventResult(e.event_id,i.status,i.msg);return}if(this.compacting){a.info("acp-adapter",`Event ${e.event_id} deferred: compaction in progress`),this.deferEvent(e);return}if(this.activeRun){a.info("acp-adapter",`Event ${e.event_id} rejected: busy`),this.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){this.callbacks.sendEventResult(e.event_id,"failed","agent not alive");return}e.session_id&&e.session_id!==this.currentAibotSessionId&&(this.currentAibotSessionId=e.session_id),this.startRun(e,!1)}deliverStopEvent(e,i){this.acpClient?(this.acpClient.cancel().catch(()=>{}),this.flushStream()):this.flushStream(),this.activeRun?.eventId===e&&this.finishRun("canceled","stopped by user")}deferEvent(e){const i=this.deferredEvents.get(e.session_id)??[];i.some(t=>t.event.event_id===e.event_id)||(i.push({event:e,queuedAt:Date.now()}),this.deferredEvents.set(e.session_id,i),a.info("acp-adapter",`Deferred event ${e.event_id} for session ${e.session_id} (queue: ${i.length})`),this.scheduleDeferredTimeout(e.session_id))}deferredTimers=new Map;rejectDeferredEvents(e){for(const[i,t]of this.deferredEvents)for(const{event:s}of t)a.info("acp-adapter",`Rejecting deferred event ${s.event_id}: ${e}`),this.callbacks.sendEventResult(s.event_id,"failed",e);this.deferredEvents.clear(),this.cancelAllDeferredTimers()}cancelDeferredTimer(e){const i=this.deferredTimers.get(e);i&&(clearTimeout(i),this.deferredTimers.delete(e))}cancelAllDeferredTimers(){for(const e of this.deferredTimers.values())clearTimeout(e);this.deferredTimers.clear()}scheduleDeferredTimeout(e){if(this.deferredTimers.has(e))return;const i=3e4,t=setTimeout(()=>{this.deferredTimers.delete(e);const s=this.deferredEvents.get(e);if(!(!s||s.length===0)&&!this.acpClient?.isAlive){a.error("acp-adapter",`Deferred events for session ${e} timed out after ${i/1e3}s (no ACP client)`),this.deferredEvents.delete(e);for(const{event:n}of s)this.callbacks.sendEventResult(n.event_id,"failed","agent initialization timed out")}},i);this.deferredTimers.set(e,t)}startRun(e,i){if(!this.acpClient?.isAlive){this.deferEvent(e);return}const t=`acp_${++this.clientMsgSeq}_${Date.now()}`;this.activeRun={eventId:e.event_id,sessionId:e.session_id,threadId:e.thread_id,clientMsgIdBase:t,currentClientMsgId:t,currentSegmentIndex:0,chunkSeq:0,buffer:"",quotedStream:new M,markdownSegmenter:new x,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:i,lastProgressAt:Date.now(),lastIdleCheckAt:Date.now(),idleNoProgressCount:0};const s=this.activeRun,n=q(e,this.resolveCwd());this.resetIdleTimer(s),n.modeId&&this.acpClient&&this.acpClient.setLiveMode(n.modeId).catch(()=>{}),n.modelId&&this.acpClient&&this.acpClient.setModel(n.modelId).catch(()=>{});const o=D(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),r=this.injectQuoteProtocolOnce(s.sessionId,o),c=this.injectRecoveryContext(s.sessionId,r);this.sendPromptWithRetry(s,c)}injectQuoteProtocolOnce(e,i){return!e||this.quoteProtocolTaughtSessions.has(e)?i:(this.quoteProtocolTaughtSessions.add(e),`${L}
1
+ import{fileURLToPath as y}from"node:url";import m from"node:path";import{homedir as v}from"node:os";import{promises as g}from"node:fs";import{EventEmitter as C}from"node:events";import{spawn as A}from"node:child_process";import{AgentProcess as R}from"../../agent/process.js";import{hasChildProcesses as T}from"../../core/runtime/spawn.js";import{syncDefaultSkillsToDir as w}from"../../default-skills/index.js";import{AcpClient as k,AcpAuthRequiredError as _,isAuthRequiredError as $}from"../../protocol/acp-client.js";import{AgentEventType as p}from"../../types/events.js";import{InternalApiServer as P}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as E}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as M}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as x}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as q}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as N}from"../../core/protocol/message-metadata.js";import{log as r}from"../../core/log/index.js";import{scanSkills as I}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as D}from"./session-activity.js";import{resolveCliPath as j,getCliVersion as B}from"../../core/util/cli-probe.js";const S=m.dirname(y(import.meta.url)),U=200,O=60*1e3,F=600*1e3,b=80,L=["[grix protocol] Markers like [[message_id:<id>]] and [[quoted_message_id:<id>]] are metadata, not user prose \u2014 never repeat them verbatim except as instructed here.","The message you are replying to carries its id in a [[message_id:<id>]] marker.","If (and only if) you want your reply to quote/thread onto that specific message, include [[quoted_message_id:<id>]] anywhere in your reply using that id; otherwise omit it."].join(" ");function K(h){return!(!(h instanceof Error)||h.code!==-32603||$(h))}function f(h){if(!(h instanceof Error))return String(h);let e=h.message;const i=h.data;return i&&(e+=` data=${JSON.stringify(i)}`),e}function H(h){return h.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class pe extends C{type="acp";config;callbacks;agentProcess=null;acpClient=null;internalApi=null;activeRun=null;compacting=!1;compactingTimer=null;compactionDoneResolver=null;pendingAutoCompact=!1;eventResults=null;pendingApprovals=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;acpMcpTools;rawTransport;approvalMode;autoInjectArgs;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;bridgeLog;sessionConnected=!1;rawEventSeq=0;recoveryContextBySessionId=new Map;quoteProtocolTaughtSessions=new Set;identityInjectedSessions=new Set;constructor(e,i,t){if(super(),this.config=e,this.callbacks=i,this.bridgeLog=t?.bridgeLog??null,this.acpAuthMethod=t?.acpAuthMethod,this.acpInitialMode=t?.acpInitialMode,this.acpInitialModel=t?.acpInitialModel,this.acpMcpTools=t?.acpMcpTools??!0,this.rawTransport=t?.rawTransport??!1,this.approvalMode=t?.approvalMode??"default",this.autoInjectArgs=t?.autoInjectArgs,this.bindingStore=t?.bindingStore??null,this.currentAibotSessionId=t?.aibotSessionId?String(t.aibotSessionId).trim():void 0,t?.eventResultsPath&&(this.eventResults=new E(t.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const s=this.bindingStore.get(this.currentAibotSessionId);s?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,s.cwd)}}async start(){if(await this.spawnProcess(),!this.bindingStore){await this.connectSession(this.resolveCwd());return}const e=this.currentAibotSessionId?this.sessionBindings.get(this.currentAibotSessionId):void 0;e&&await this.connectSession(e)}async stop(){this.stopped=!0,this.rejectDeferredEvents("adapter stopped"),this.cancelAllDeferredTimers(),this.pendingAutoCompact=!1,this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver(),this.compactionDoneResolver=null),this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null),this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,i){}async destroySession(e){this.quoteProtocolTaughtSessions.delete(e),this.identityInjectedSessions.delete(e)}onAgentProfileChanged(){this.identityInjectedSessions.size>0&&(r.info("acp-adapter",`agent profile changed; clearing ${this.identityInjectedSessions.size} identity-injected session(s)`),this.identityInjectedSessions.clear())}sendPrompt(e){const i=new W(e.adapterSessionId);return this.acpClient?.isAlive&&this.acpClient.send(e.text).catch(t=>{i.emitError(t instanceof Error?t:new Error(String(t)))}),i}async cancel(e){this.activeRun&&this.acpClient&&(await this.acpClient.cancel(),this.flushStream(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(e){}async ping(e){return this.acpClient?.isAlive?this.acpClient.ping(e):this.agentProcess?.alive??!1}getStatus(){return{alive:this.agentProcess?.alive??!1,busy:this.activeRun!==null||this.compacting,sessions:this.sessionConnected?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null)}getMcpConfig(){if(!this.internalApi)return null;this.internalApi.setMcpBridgeUpHandler(i=>this.onMcpBridgeUp(i));const e=m.resolve(S,"../../mcp/mcp-bridge-server.js");return{name:"grix-app-bridge",command:process.execPath,args:[e,"--ws-url",this.internalApi.mcpBridgeWsUrl]}}async hasBackgroundWork(){const e=this.agentProcess?.pid;return e?T(e,[e]):!1}async probe(e){const i=this.config.command||"gemini",t=await j(i),s=t!==null;let n=null,a;if(s){const u=await B(i);n=u.version,u.error&&(a=u.error)}else a={code:"cli_not_found",message:`command not found: ${i}`};const o=this.acpClient?.model??null,c=this.getStatus();let d;return e?.conversation?this.acpClient?.isAlive?d={attempted:!0,ok:!0,latency_ms:null}:d={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:d={attempted:!1,ok:!1,latency_ms:null},{cli:{command:i,installed:s,path:t,version:n,...a?{error:a}:{}},conversation:d,config:{model:o,base_url:null,source:{model:o?"runtime":"unknown",base_url:"unknown"}},process:{started:!!this.agentProcess,alive:c.alive,busy:c.busy}}}getSupportedCommands(){return this.acpClient?.availableCommands?.length?this.acpClient.availableCommands.map(e=>({name:e.name,description:e.description??"",args:e.args})):[{name:"model",description:"List or set model",args:"[model_id]"},{name:"mode",description:"List or set collaboration mode",args:"[mode_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"compact",description:"\u538B\u7F29\u4E0A\u4E0B\u6587"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(e,i,t){return this.acpClient?.supportsCommandsExecute?this.execCommandViaAcp(e,i):this.execCommandFallback(e,i)}async execCommandViaAcp(e,i){if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(e==="interrupt")return this.activeRun?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};if(e==="compact"){if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;try{const t=await this.acpClient.executeCommand(e,i||void 0);return this.finishCompaction("acp-commands-execute"),{status:t.status==="failed"?"failed":"ok",message:t.message??"Compacted",data:t.data}}catch(t){return this.finishCompaction("error"),t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`compact failed: ${t instanceof Error?t.message:t}`}}}try{const t=await this.acpClient.executeCommand(e,i||void 0);return{status:t.status??"ok",message:t.message,data:t.options?{command:e,options:t.options}:t.data}}catch(t){return t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`Command failed: ${t instanceof Error?t.message:t}`}}}async execCommandFallback(e,i){try{switch(e){case"model":{const t=i.trim();if(t)return await this.setModel(t)?{status:"ok",message:`Model set to ${t}`}:{status:"failed",message:`Failed to set model: ${t}`};const s=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${s.currentModelId||"unknown"}`,data:s}}case"mode":{const t=i.trim();if(t)return await this.setMode(t)?{status:"ok",message:`Mode set to ${t}`}:{status:"failed",message:`Failed to set mode: ${t}`};const s=this.buildToolbarContext("mode_list");return{status:"ok",message:`Current: ${s.currentModeId||"unknown"}`,data:s}}case"interrupt":return this.activeRun?this.acpClient?(await this.cancel(this.activeRun.sessionId),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"Not connected"}:{status:"failed",message:"No active run to interrupt"};case"compact":{if(!this.acpClient)return{status:"failed",message:"No active ACP session"};if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.activeRun)return{status:"failed",message:"agent busy"};this.compacting=!0;const t=new Promise(s=>{this.compactionDoneResolver=s});try{await this.acpClient.send("/compact")}catch(s){throw this.finishCompaction("send-failed"),s}return this.compactingTimer=setTimeout(()=>this.finishCompaction("timeout"),15e3),this.compactingTimer.unref?.(),await t,{status:"ok",message:"Compacted"}}case"status":{const t=this.getStatus(),s=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${t.alive}, Busy: ${t.busy}, Model: ${s?.currentModelId??"unknown"}, Mode: ${s?.currentModeId??"unknown"}`,data:{alive:t.alive,busy:t.busy,sessions:t.sessions,model:s?.currentModelId??"",mode:s?.currentModeId??""}}}case"skills":{const t=I({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),s=t.map(n=>`- ${n.name}${n.trigger?` (${n.trigger})`:""} [${n.source}]: ${n.description}`);return{status:"ok",message:s.length>0?s.join(`
2
+ `):"No skills found",data:t}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(t){return{status:"failed",message:t instanceof Error?t.message:String(t)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}buildToolbarContext(e,i){const t=this.acpClient?.sessionOptions,s=t?.currentModeId??"";let n=t?.currentModelId??"";const a=t?.modes.map(l=>({id:l.id,name:l.name}))??[],o=t?.models.map(l=>({modelId:l.modelId,name:l.name}))??[],c=t?.models.map(l=>({id:l.modelId,displayName:l.name}))??[],d=t?.modes.map(l=>({id:l.id,displayName:l.name}))??[];c.length>0&&!c.some(l=>l.id===n)&&(n=c[0].id);const u={outcome:e,...i?{cwd:i}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:c,available_modes:d,availableModels:c,availableModes:d,models:o,modes:a};return r.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(c.map(l=>l.id))} currentModelId=${n}`),u}get pendingApprovalEntries(){return this.pendingApprovals}get hasSessionBinding(){return this.bindingStore!==null}setMode(e){return this.acpClient?this.acpClient.setLiveMode(e):Promise.resolve(!1)}setModel(e){return this.acpClient?this.acpClient.setModel(e):Promise.resolve(!1)}handleAcpApprovalAction(e,i){const t=this.pendingApprovals.get(e);return t?(this.pendingApprovals.delete(e),this.acpClient&&this.acpClient.respondPermission(t,{behavior:i}).catch(s=>{r.error("acp-adapter",`Failed to respond to permission: ${s}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun),!0):!1}respondToPermission(e,i){this.acpClient&&this.acpClient.respondPermission(e,i).catch(t=>{r.error("acp-adapter",`Failed to respond to permission: ${t}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun)}async handleLocalAction(e){const i=e.action_type??"",t=e.params??{};if(i==="exec_approve"||i==="exec_reject"||i==="permission_approve"||i==="permission_reject"){const s=String(t.tool_call_id??t.approval_command_id??t.approval_id??t.exec_context_id??""),n=i==="exec_approve"||i==="permission_approve",a=String(t.decision??"");let o;return n&&(a==="allow-once"||a==="allow-always")?o=a:n?o="allow":o="deny",s?this.handleAcpApprovalAction(s,o)?(this.callbacks.sendLocalActionResult(e.action_id,"ok"),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"approval_not_found",`no pending approval for tool_call_id: ${s}`),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"tool_call_id_required","tool_call_id is required"),{handled:!0,kind:"approval"})}return{handled:!1,kind:""}}resolveCwd(){if(this.currentAibotSessionId){const e=this.sessionBindings.get(this.currentAibotSessionId);if(e)return e}if(this.bindingStore&&this.currentAibotSessionId){const e=this.bindingStore.get(this.currentAibotSessionId);if(e?.cwd)return e.cwd}return process.cwd()}async bindSession(e,i){const t=this.sessionBindings.get(e);if(t)try{return await g.stat(t),!1}catch{r.info("acp-adapter",`Stale binding for session ${e}: ${t} no longer exists, allowing rebind to ${i}`),this.sessionBindings.delete(e)}return this.sessionBindings.set(e,i),this.bindingStore&&this.bindingStore.set(e,i),this.maybeRefreshKiroSkills(i),!this.sessionConnected&&this.agentProcess?.alive?this.connectSession(i).then(()=>!0).catch(s=>(r.error("acp-adapter",`Failed to create session on bind: ${f(s)}`),this.callbacks.sendUpdateBindingCard(e,"failed",i),!0)):(this.acpClient?.isAlive&&(this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(e,this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(e,"connected",i,this.buildToolbarContext("binding_ready",i))),Promise.resolve(!0))}getSessionCwd(e){return this.sessionBindings.get(e)}getSessionBindings(){return this.sessionBindings}maybeRefreshKiroSkills(e){if(this.config.command!=="kiro-cli"||!this.callbacks.onSkillsUpdate)return;const i=String(e??"").trim()||void 0;let t=[];try{t=I({mode:"kiro",projectDir:i})}catch(s){r.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(t)}catch(s){r.warn("acp-adapter",`onSkillsUpdate failed: ${s instanceof Error?s.message:String(s)}`)}}replayDeferredEvents(e){const i=this.deferredEvents.get(e);if(!i||i.length===0)return;if(this.activeRun){r.info("acp-adapter",`Cannot replay deferred events for session ${e}: agent busy`);return}const t=i.shift();t&&(i.length===0?(this.deferredEvents.delete(e),this.cancelDeferredTimer(e)):this.deferredEvents.set(e,i),r.info("acp-adapter",`Replaying deferred event ${t.event.event_id} for session ${e} (remaining: ${i.length})`),this.startRun(t.event,!1))}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),r.info("acp-adapter",`Compaction finished (${e}); replaying deferred events`),this.replayNextDeferredEvent();const i=this.compactionDoneResolver;this.compactionDoneResolver=null,i?.()}replayNextDeferredEvent(){if(this.activeRun||this.compacting)return;const e=this.deferredEvents.keys().next().value;e&&this.replayDeferredEvents(e)}announceDeferredComposing(e){}deliverInboundEvent(e){if(this.eventResults?.has(e.session_id,e.event_id)){const i=this.eventResults.get(e.session_id,e.event_id);r.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${i.status})`),this.callbacks.sendEventResult(e.event_id,i.status,i.msg);return}if(this.compacting){r.info("acp-adapter",`Event ${e.event_id} deferred: compaction in progress`),this.deferEvent(e);return}if(this.activeRun){r.info("acp-adapter",`Event ${e.event_id} rejected: busy`),this.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){this.callbacks.sendEventResult(e.event_id,"failed","agent not alive");return}e.session_id&&e.session_id!==this.currentAibotSessionId&&(this.currentAibotSessionId=e.session_id),this.startRun(e,!1)}deliverStopEvent(e,i){this.acpClient?(this.acpClient.cancel().catch(()=>{}),this.flushStream()):this.flushStream(),this.activeRun?.eventId===e&&this.finishRun("canceled","stopped by user")}deferEvent(e){const i=this.deferredEvents.get(e.session_id)??[];i.some(t=>t.event.event_id===e.event_id)||(i.push({event:e,queuedAt:Date.now()}),this.deferredEvents.set(e.session_id,i),r.info("acp-adapter",`Deferred event ${e.event_id} for session ${e.session_id} (queue: ${i.length})`),this.scheduleDeferredTimeout(e.session_id))}deferredTimers=new Map;rejectDeferredEvents(e){for(const[i,t]of this.deferredEvents)for(const{event:s}of t)r.info("acp-adapter",`Rejecting deferred event ${s.event_id}: ${e}`),this.callbacks.sendEventResult(s.event_id,"failed",e);this.deferredEvents.clear(),this.cancelAllDeferredTimers()}cancelDeferredTimer(e){const i=this.deferredTimers.get(e);i&&(clearTimeout(i),this.deferredTimers.delete(e))}cancelAllDeferredTimers(){for(const e of this.deferredTimers.values())clearTimeout(e);this.deferredTimers.clear()}scheduleDeferredTimeout(e){if(this.deferredTimers.has(e))return;const i=3e4,t=setTimeout(()=>{this.deferredTimers.delete(e);const s=this.deferredEvents.get(e);if(!(!s||s.length===0)&&!this.acpClient?.isAlive){r.error("acp-adapter",`Deferred events for session ${e} timed out after ${i/1e3}s (no ACP client)`),this.deferredEvents.delete(e);for(const{event:n}of s)this.callbacks.sendEventResult(n.event_id,"failed","agent initialization timed out")}},i);this.deferredTimers.set(e,t)}startRun(e,i){if(!this.acpClient?.isAlive){this.deferEvent(e);return}const t=`acp_${++this.clientMsgSeq}_${Date.now()}`;this.activeRun={eventId:e.event_id,sessionId:e.session_id,threadId:e.thread_id,clientMsgIdBase:t,currentClientMsgId:t,currentSegmentIndex:0,chunkSeq:0,buffer:"",quotedStream:new M,markdownSegmenter:new x,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:i,lastProgressAt:Date.now(),lastIdleCheckAt:Date.now(),idleNoProgressCount:0};const s=this.activeRun,n=q(e,this.resolveCwd());this.resetIdleTimer(s),n.modeId&&this.acpClient&&this.acpClient.setLiveMode(n.modeId).catch(()=>{}),n.modelId&&this.acpClient&&this.acpClient.setModel(n.modelId).catch(()=>{});const a=N(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),o=this.injectQuoteProtocolOnce(s.sessionId,a),c=this.injectIdentityOnce(s.sessionId,o),d=this.injectRecoveryContext(s.sessionId,c);this.sendPromptWithRetry(s,d)}injectQuoteProtocolOnce(e,i){return!e||this.quoteProtocolTaughtSessions.has(e)?i:(this.quoteProtocolTaughtSessions.add(e),`${L}
3
3
 
4
- ${i}`)}injectRecoveryContext(e,i){const t=this.recoveryContextBySessionId.get(e);return t?(this.recoveryContextBySessionId.delete(e),a.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${t.length})`),`${t}
4
+ ${i}`)}injectIdentityOnce(e,i){if(!e||this.identityInjectedSessions.has(e))return i;const t=this.callbacks.getAgentProfile?.(),s=t?.agentName?.trim()??"",n=t?.introduction?.trim()??"";if(!s&&!n)return i;this.identityInjectedSessions.add(e);const a=["[system-identity]"];return s&&a.push(`Your name is "${s}".`),n&&a.push(`Your self-introduction: ${n}`),a.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),a.push("[/system-identity]"),r.info("acp-adapter",`Injected agent identity for session=${e} name="${s}"`),`${a.join(`
5
+ `)}
6
+
7
+ ${i}`}injectRecoveryContext(e,i){const t=this.recoveryContextBySessionId.get(e);return t?(this.recoveryContextBySessionId.delete(e),r.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${t.length})`),`${t}
5
8
 
6
9
  [\u5F53\u524D\u7528\u6237\u6D88\u606F]
7
- ${i}`):i}sendPromptWithRetry(e,i){this.acpClient.send(i).catch(t=>{if(a.error("acp-adapter",`Prompt failed: ${f(t)}`),K(t)){const n=f(t),o=e.eventId,r=e.sessionId;a.info("acp-adapter",`Internal error escalated to bridge: event=${o} session=${r} err=${n}`),this.silentlyDiscardActiveRun(e),this.emit("internalError",{eventId:o,sessionId:r,errorMsg:n});return}const s=t instanceof Error?t.message:String(t);this.finishRun("failed",s)})}silentlyDiscardActiveRun(e){this.activeRun===e&&(this.activeRun=null,this.acpClient?.clearSettleTimer(),e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),this.emit("eventDone",e.eventId))}async spawnProcess(){const e=[...this.config.args??[]];this.autoInjectArgs?.acp&&e.push("--acp"),this.autoInjectArgs?.model&&e.push("--model",this.autoInjectArgs.model),this.config.command==="gemini"&&!e.includes("--skip-trust")&&e.push("--skip-trust"),this.config.command.includes("qwen")&&!e.includes("--experimental-skills")&&e.push("--experimental-skills"),this.config.command==="kiro-cli"&&!e.includes("--trust-all-tools")&&e.push("--trust-all-tools"),(this.config.command==="copilot"||this.config.command.endsWith("/copilot")||this.config.command==="gh"&&e.includes("copilot"))&&(e.includes("--allow-all-tools")||e.push("--allow-all-tools"),e.includes("--allow-all-paths")||e.push("--allow-all-paths")),this.agentProcess=new y(this.bridgeLog);const t=this.resolveCwd();try{if(!(await g.stat(t)).isDirectory())throw new Error(`Bound path is not a directory: ${t}`)}catch(s){throw String(s?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${t}. Please rebind with /grix open <valid-directory>.`):s}try{await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:t,env:this.config.env})}catch(s){throw a.error("acp-adapter",`Failed to spawn agent process: ${s}`),this.rejectDeferredEvents(`agent spawn failed: ${s instanceof Error?s.message:String(s)}`),this.emit("exit",null),s}a.info("acp-adapter","ACP agent process started"),this.agentProcess.on("error",s=>{this.stopped||(a.error("acp-adapter",`Agent process error: ${s.message}`),this.activeRun&&this.finishRun("failed",`agent process error: ${s.message}`),this.rejectDeferredEvents(`agent process error: ${s.message}`),this.emit("exit",null))}),this.agentProcess.on("exit",s=>{this.stopped||(a.error("acp-adapter",`Agent process exited unexpectedly (code=${s})`),this.activeRun&&this.finishRun("failed",`agent process exited (code=${s})`),this.rejectDeferredEvents(`agent process exited (code=${s})`),this.emit("exit",s))}),this.agentProcess.on("stderr",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)})}async connectSession(e){if(this.sessionConnected||!this.agentProcess?.transport)return;let i;this.acpMcpTools&&(i=await this.startInternalApiAndMcp()),this.acpClient=new k,this.acpClient.on("event",r=>this.handleAcpEvent(r)),this.acpClient.on("activity",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)}),this.acpClient.on("session-lost",()=>{this.stopped||this.agentProcess?.alive&&(a.error("acp-adapter","ACP transport closed while agent process still alive, triggering respawn"),this.activeRun&&(this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,"ACP transport closed"),this.finishRun("failed","ACP transport closed")),this.emit("exit",null))});const t=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=async r=>{await this.acpClient.connect({transport:this.agentProcess.transport,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:e||this.resolveCwd(),mcpServers:i,sessionId:r})};let n=!1,o;try{await s(t)}catch(r){if(r instanceof _){await this.handleAuthRequired(r);return}if(!t)throw this.handleConnectFailure(r),r;if(a.warn("acp-adapter",`Failed to load persisted session ${t}, fallback to session/new: ${f(r)}`),n=!0,o=t,this.config.command==="kiro-cli"&&this.currentAibotSessionId){a.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${t}`);const c=await this.buildKiroRecoveryContext(this.currentAibotSessionId,t,e||this.resolveCwd());c?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,c),a.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${c.length})`)):a.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${t}`)}await s(void 0)}this.sessionConnected=!0,a.info("acp-adapter",`ACP session ready: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId),this.currentAibotSessionId&&this.bindingStore&&this.bindingStore.setAcpSessionId(this.currentAibotSessionId,this.acpClient.sessionId);for(const[r,c]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(r,this.acpClient.sessionId);const d=n?"binding_recreated":"binding_ready",u=this.buildToolbarContext(d,c);n&&o&&(u.previous_session_id=o,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(r,"ready",c,u)}}async buildKiroRecoveryContext(e,i,t){try{const s=m.join(v(),".kiro","sessions","cli",`${i}.json`);await g.access(s),a.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,t);if(!n)return;const o=m.join(v(),".grix","data","session-summaries");await g.mkdir(o,{recursive:!0});const r=m.join(o,`${e}.md`),c=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
8
- `);return await g.writeFile(r,c,"utf8"),a.info("acp-adapter",`Recovery summary saved: ${r} (chars=${n.length})`),["[\u5386\u53F2\u4E0A\u4E0B\u6587\u56DE\u704C\u8BF4\u660E]","\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u65E7\u4F1A\u8BDD\u81EA\u52A8\u6458\u8981\uFF0C\u8BF7\u5728\u540E\u7EED\u56DE\u7B54\u4E2D\u5EF6\u7EED\u5176\u4E2D\u7684\u76EE\u6807\u3001\u7EA6\u675F\u548C\u7ED3\u8BBA\u3002",`SOURCE_KIRO_SESSION_FILE: ${s}`,`SUMMARY_FILE: ${r}`,"",n.trim()].join(`
9
- `)}catch(s){a.warn("acp-adapter",`Failed to build kiro recovery context: ${f(s)}`);return}}async generateSummaryByKiroCli(e,i){const s=["chat","--no-interactive","--trust-tools=read,grep",["\u8BF7\u57FA\u4E8E\u4E0B\u9762\u5F15\u7528\u7684 Kiro \u539F\u59CB\u4F1A\u8BDD\u6587\u4EF6\u751F\u6210\u6458\u8981\uFF0C\u8F93\u51FA\u5FC5\u987B\u4E3A Markdown\u3002","\u8981\u6C42\uFF1A1) \u7B80\u660E\u51C6\u786E\uFF1B2) \u5305\u542B goals/constraints/decisions/open_items \u56DB\u90E8\u5206\uFF1B3) \u4E0D\u8981\u8F93\u51FA\u4EE3\u7801\u5757\u56F4\u680F\u3002",`\u4F1A\u8BDD\u6587\u4EF6\uFF1A@"${e}"`].join(`
10
- `)],n=await this.runCommandCapture("kiro-cli",s,i,45e3);if(!n)return;const o=H(n).trim(),r=o.indexOf("> "),c=r>=0?o.slice(r+2).trim():o;if(c)return a.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${o.length}, body_chars=${c.length})`),c.split(`
10
+ ${i}`):i}sendPromptWithRetry(e,i){this.acpClient.send(i).catch(t=>{if(r.error("acp-adapter",`Prompt failed: ${f(t)}`),K(t)){const n=f(t),a=e.eventId,o=e.sessionId;r.info("acp-adapter",`Internal error escalated to bridge: event=${a} session=${o} err=${n}`),this.silentlyDiscardActiveRun(e),this.emit("internalError",{eventId:a,sessionId:o,errorMsg:n});return}const s=t instanceof Error?t.message:String(t);this.finishRun("failed",s)})}silentlyDiscardActiveRun(e){this.activeRun===e&&(this.activeRun=null,this.acpClient?.clearSettleTimer(),e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),this.emit("eventDone",e.eventId))}async spawnProcess(){const e=[...this.config.args??[]];this.autoInjectArgs?.acp&&e.push("--acp"),this.autoInjectArgs?.model&&e.push("--model",this.autoInjectArgs.model),this.config.command==="gemini"&&!e.includes("--skip-trust")&&e.push("--skip-trust"),this.config.command.includes("qwen")&&!e.includes("--experimental-skills")&&e.push("--experimental-skills"),this.config.command==="kiro-cli"&&!e.includes("--trust-all-tools")&&e.push("--trust-all-tools"),(this.config.command==="copilot"||this.config.command.endsWith("/copilot")||this.config.command==="gh"&&e.includes("copilot"))&&(e.includes("--allow-all-tools")||e.push("--allow-all-tools"),e.includes("--allow-all-paths")||e.push("--allow-all-paths")),this.agentProcess=new R(this.bridgeLog);const t=this.resolveCwd();try{if(!(await g.stat(t)).isDirectory())throw new Error(`Bound path is not a directory: ${t}`)}catch(s){throw String(s?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${t}. Please rebind with /grix open <valid-directory>.`):s}try{await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:t,env:this.config.env})}catch(s){throw r.error("acp-adapter",`Failed to spawn agent process: ${s}`),this.rejectDeferredEvents(`agent spawn failed: ${s instanceof Error?s.message:String(s)}`),this.emit("exit",null),s}r.info("acp-adapter","ACP agent process started"),this.agentProcess.on("error",s=>{this.stopped||(r.error("acp-adapter",`Agent process error: ${s.message}`),this.activeRun&&this.finishRun("failed",`agent process error: ${s.message}`),this.rejectDeferredEvents(`agent process error: ${s.message}`),this.emit("exit",null))}),this.agentProcess.on("exit",s=>{this.stopped||(r.error("acp-adapter",`Agent process exited unexpectedly (code=${s})`),this.activeRun&&this.finishRun("failed",`agent process exited (code=${s})`),this.rejectDeferredEvents(`agent process exited (code=${s})`),this.emit("exit",s))}),this.agentProcess.on("stderr",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)})}async connectSession(e){if(this.sessionConnected||!this.agentProcess?.transport)return;let i;this.acpMcpTools&&(i=await this.startInternalApiAndMcp()),this.acpClient=new k,this.acpClient.on("event",o=>this.handleAcpEvent(o)),this.acpClient.on("activity",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)}),this.acpClient.on("session-lost",()=>{this.stopped||this.agentProcess?.alive&&(r.error("acp-adapter","ACP transport closed while agent process still alive, triggering respawn"),this.activeRun&&(this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,"ACP transport closed"),this.finishRun("failed","ACP transport closed")),this.emit("exit",null))});const t=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=async o=>{await this.acpClient.connect({transport:this.agentProcess.transport,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:e||this.resolveCwd(),mcpServers:i,sessionId:o})};let n=!1,a;try{await s(t)}catch(o){if(o instanceof _){await this.handleAuthRequired(o);return}if(!t)throw this.handleConnectFailure(o),o;if(r.warn("acp-adapter",`Failed to load persisted session ${t}, fallback to session/new: ${f(o)}`),n=!0,a=t,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${t}`);const c=await this.buildKiroRecoveryContext(this.currentAibotSessionId,t,e||this.resolveCwd());c?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,c),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${c.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${t}`)}await s(void 0)}this.sessionConnected=!0,r.info("acp-adapter",`ACP session ready: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId),this.currentAibotSessionId&&this.bindingStore&&this.bindingStore.setAcpSessionId(this.currentAibotSessionId,this.acpClient.sessionId);for(const[o,c]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(o,this.acpClient.sessionId);const d=n?"binding_recreated":"binding_ready",u=this.buildToolbarContext(d,c);n&&a&&(u.previous_session_id=a,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(o,"ready",c,u)}}async buildKiroRecoveryContext(e,i,t){try{const s=m.join(v(),".kiro","sessions","cli",`${i}.json`);await g.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,t);if(!n)return;const a=m.join(v(),".grix","data","session-summaries");await g.mkdir(a,{recursive:!0});const o=m.join(a,`${e}.md`),c=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
11
+ `);return await g.writeFile(o,c,"utf8"),r.info("acp-adapter",`Recovery summary saved: ${o} (chars=${n.length})`),["[\u5386\u53F2\u4E0A\u4E0B\u6587\u56DE\u704C\u8BF4\u660E]","\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u65E7\u4F1A\u8BDD\u81EA\u52A8\u6458\u8981\uFF0C\u8BF7\u5728\u540E\u7EED\u56DE\u7B54\u4E2D\u5EF6\u7EED\u5176\u4E2D\u7684\u76EE\u6807\u3001\u7EA6\u675F\u548C\u7ED3\u8BBA\u3002",`SOURCE_KIRO_SESSION_FILE: ${s}`,`SUMMARY_FILE: ${o}`,"",n.trim()].join(`
12
+ `)}catch(s){r.warn("acp-adapter",`Failed to build kiro recovery context: ${f(s)}`);return}}async generateSummaryByKiroCli(e,i){const s=["chat","--no-interactive","--trust-tools=read,grep",["\u8BF7\u57FA\u4E8E\u4E0B\u9762\u5F15\u7528\u7684 Kiro \u539F\u59CB\u4F1A\u8BDD\u6587\u4EF6\u751F\u6210\u6458\u8981\uFF0C\u8F93\u51FA\u5FC5\u987B\u4E3A Markdown\u3002","\u8981\u6C42\uFF1A1) \u7B80\u660E\u51C6\u786E\uFF1B2) \u5305\u542B goals/constraints/decisions/open_items \u56DB\u90E8\u5206\uFF1B3) \u4E0D\u8981\u8F93\u51FA\u4EE3\u7801\u5757\u56F4\u680F\u3002",`\u4F1A\u8BDD\u6587\u4EF6\uFF1A@"${e}"`].join(`
13
+ `)],n=await this.runCommandCapture("kiro-cli",s,i,45e3);if(!n)return;const a=H(n).trim(),o=a.indexOf("> "),c=o>=0?a.slice(o+2).trim():a;if(c)return r.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${a.length}, body_chars=${c.length})`),c.split(`
11
14
  `).filter(d=>!d.includes("Credits:")&&!d.includes("Time:")).join(`
12
- `).trim()}runCommandCapture(e,i,t,s){return new Promise((n,o)=>{const r=R(e,i,{cwd:t,stdio:["ignore","pipe","pipe"]});let c="",d="";const u=setTimeout(()=>{r.kill("SIGTERM"),o(new Error(`${e} timed out after ${s}ms`))},s);r.stdout.on("data",l=>{c+=String(l)}),r.stderr.on("data",l=>{d+=String(l)}),r.on("error",l=>{clearTimeout(u),o(l)}),r.on("close",l=>{if(clearTimeout(u),l===0){n(c||d);return}o(new Error(`${e} exited with code ${l}: ${d||c}`))})})}handleConnectFailure(e){a.error("acp-adapter",`ACP session creation failed: ${f(e)}`),this.acpClient?.removeAllListeners(),this.acpClient=null,this.emit("exit",null)}async startInternalApiAndMcp(){try{this.internalApi||(this.internalApi=new E,this.internalApi.setInvokeHandler(async(o,r)=>this.callbacks.agentInvoke(o,r)),await this.internalApi.start(0),a.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(o=>this.onMcpBridgeUp(o));const e=this.config.command??"",i=e.includes("qwen")?".qwen":e.includes("gemini")?".gemini":e.includes("kiro")?".kiro":e.includes("reasonix")?".reasonix":void 0;if(i){const o=m.join(v(),i,"skills"),r=w(o);r.length>0&&a.info("acp-adapter",`Synced connector skills to ${o}: [${r.join(", ")}]`)}const t=m.resolve(S,"../../mcp/mcp-bridge-server.js"),s=this.internalApi.mcpBridgeWsUrl,n=(this.config.command??"").includes("reasonix");return[{name:"grix-app-bridge",command:process.execPath,args:[t,"--ws-url",s],...!n&&{env:{GRIX_CONNECTOR_MCP_BRIDGE_WS:s}}}]}catch(e){a.error("acp-adapter",`Failed to start MCP tools: ${e}`);return}}onMcpBridgeUp(e){this.callbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}cleanup(){this.activeRun,this.pendingApprovals.clear(),this.pendingAutoCompact=!1,this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null)}async handleAuthRequired(e){a.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(n=>n.id).join(", ")}`);const i=e.authMethods.find(n=>/oauth|browser/i.test(n.id))??e.authMethods[0];if(!i)throw e;const t=this.currentAibotSessionId??"";this.callbacks.sendAuthNotification(t,`Authentication required (${i.id}). Initiating auth flow...`);for(const[n,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(n,"failed",o);const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(t,`Please open this URL to authenticate:
15
+ `).trim()}runCommandCapture(e,i,t,s){return new Promise((n,a)=>{const o=A(e,i,{cwd:t,stdio:["ignore","pipe","pipe"]});let c="",d="";const u=setTimeout(()=>{o.kill("SIGTERM"),a(new Error(`${e} timed out after ${s}ms`))},s);o.stdout.on("data",l=>{c+=String(l)}),o.stderr.on("data",l=>{d+=String(l)}),o.on("error",l=>{clearTimeout(u),a(l)}),o.on("close",l=>{if(clearTimeout(u),l===0){n(c||d);return}a(new Error(`${e} exited with code ${l}: ${d||c}`))})})}handleConnectFailure(e){r.error("acp-adapter",`ACP session creation failed: ${f(e)}`),this.acpClient?.removeAllListeners(),this.acpClient=null,this.emit("exit",null)}async startInternalApiAndMcp(){try{this.internalApi||(this.internalApi=new P,this.internalApi.setInvokeHandler(async(a,o)=>this.callbacks.agentInvoke(a,o)),await this.internalApi.start(0),r.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(a=>this.onMcpBridgeUp(a));const e=this.config.command??"",i=e.includes("qwen")?".qwen":e.includes("gemini")?".gemini":e.includes("kiro")?".kiro":e.includes("reasonix")?".reasonix":void 0;if(i){const a=m.join(v(),i,"skills"),o=w(a);o.length>0&&r.info("acp-adapter",`Synced connector skills to ${a}: [${o.join(", ")}]`)}const t=m.resolve(S,"../../mcp/mcp-bridge-server.js"),s=this.internalApi.mcpBridgeWsUrl,n=(this.config.command??"").includes("reasonix");return[{name:"grix-app-bridge",command:process.execPath,args:[t,"--ws-url",s],...!n&&{env:{GRIX_CONNECTOR_MCP_BRIDGE_WS:s}}}]}catch(e){r.error("acp-adapter",`Failed to start MCP tools: ${e}`);return}}onMcpBridgeUp(e){this.callbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}cleanup(){this.activeRun,this.pendingApprovals.clear(),this.pendingAutoCompact=!1,this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null)}async handleAuthRequired(e){r.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(n=>n.id).join(", ")}`);const i=e.authMethods.find(n=>/oauth|browser/i.test(n.id))??e.authMethods[0];if(!i)throw e;const t=this.currentAibotSessionId??"";this.callbacks.sendAuthNotification(t,`Authentication required (${i.id}). Initiating auth flow...`);for(const[n,a]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(n,"failed",a);const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(t,`Please open this URL to authenticate:
13
16
  ${s}
14
17
 
15
- Waiting for authentication to complete...`);try{await this.acpClient.authenticate(i.id),a.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(t,"Authentication successful. Resuming...");const n=this.acpMcpTools?await this.startInternalApiAndMcp():void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:this.resolveCwd(),mcpServers:n}),a.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[o,r]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(o,"ready",r,this.buildToolbarContext("binding_ready",r))}catch(n){throw a.error("acp-adapter",`Auth retry failed: ${n}`),n}}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const i=/https?:\/\/[^\s"')\]]+/;let t=!1;const s=n=>{if(t)return;const r=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(i);r&&(t=!0,this.agentProcess.removeListener("stderr",s),e(r[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{t||(t=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){if(e.type===p.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===p.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting?this.finishCompaction("context-window-update"):e.contextWindow&&this.maybeScheduleAutoCompact(e.contextWindow);return}const i=this.activeRun;if(!i)return;i.responded||(i.responded=!0);let t=!1;switch(e.type){case p.Text:{if(e.content){t=!0;const s=i.quotedStream.consume(e.content);s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(i,s.deltaContent)}break}case p.ToolUse:{t=!0,i.awaitingToolResult=!0,e.toolName&&(this.emitRawEventEnvelope(i,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:e.toolInput??""}})||this.callbacks.sendToolUse(i.eventId,i.sessionId,e.toolName,e.toolInput??""));break}case p.ToolResult:{i.awaitingToolResult=!1,e.content&&(t=!0,this.emitRawEventEnvelope(i,{type:"tool_result",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(i.eventId,i.sessionId,e.toolName??"",e.content));break}case p.ToolProgress:{t=!0,e.content&&(this.emitRawEventEnvelope(i,{type:"tool_progress",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(i.eventId,i.sessionId,e.toolName??"",e.content));break}case p.Thinking:{e.content&&(t=!0,this.emitRawEventEnvelope(i,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(i.eventId,i.sessionId,e.content));break}case p.Error:{const s=String(e.error??"unknown error");this.emitRawEventEnvelope(i,{type:"error",payload:{message:s}}),a.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(i.eventId,i.sessionId,s);break}case p.Result:{this.emitRawEventEnvelope(i,{type:"result",payload:{done:e.done??!0}});const s=i.quotedStream.flush();s.deltaContent&&this.appendToStream(i,s.deltaContent),s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),this.flushStream(),this.finishRun("responded");break}}t&&(i.lastProgressAt=Date.now(),i.idleNoProgressCount=0),this.resetIdleTimer(i)}handlePermissionRequest(e){const i=e.permissionRequest;if(!i||!e.requestId||!this.acpClient)return;if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){a.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${i.toolName}`),this.acpClient.respondPermission(i.requestId,{behavior:"allow"}).catch(()=>{});return}const t=i.toolCallId;this.pendingApprovals.set(t,e.requestId);const s=this.activeRun;s?(s.idleTimer&&(clearTimeout(s.idleTimer),s.idleTimer=null),this.emitRawEventEnvelope(s,{type:"permission_request",payload:{request_id:i.requestId,tool_call_id:t,tool_name:i.toolName,tool_title:i.toolTitle,options:i.options}})||this.callbacks.sendPermissionCard({eventId:s.eventId,sessionId:s.sessionId,toolCallId:t,toolName:i.toolName,toolTitle:i.toolTitle,options:i.options})):(a.info("acp-adapter",`Permission request without active run, auto-approving: ${i.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(t))}emitRawEventEnvelope(e,i){return!e||!this.rawTransport||!this.callbacks.sendRawEventEnvelope?!1:(this.callbacks.sendRawEventEnvelope(e.eventId,e.sessionId,{type:i.type,payload:i.payload,seq:++this.rawEventSeq,at:new Date().toISOString()}),!0)}resetIdleTimer(e){e.idleTimer&&clearTimeout(e.idleTimer);const i=e.awaitingToolResult?O:F;e.idleTimer=setTimeout(()=>{if(this.activeRun?.eventId!==e.eventId)return;const t=this.acpClient;t?.isAlive?t.ping(5e3).then(async s=>{if(this.activeRun?.eventId!==e.eventId)return;if(!s){a.error("acp-adapter",`Ping failed, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck();return}if(e.awaitingToolResult){a.info("acp-adapter",`Idle timer: tool running, ping ok, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e);return}const n=e.lastIdleCheckAt,o=e.lastProgressAt>n,r=o?!1:await N(t.sessionId,n),c=o||r;e.lastIdleCheckAt=Date.now(),c?(r&&a.info("acp-adapter",`Idle timer: no stream progress but kiro session active, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,e.idleNoProgressCount>=2?(a.error("acp-adapter",`Agent alive but no progress for ${e.idleNoProgressCount} idle cycles, declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent alive but no progress for ${e.idleNoProgressCount} idle cycles`),this.declareStuck()):(a.warn("acp-adapter",`Idle timer fired, ping ok, no progress (${e.idleNoProgressCount}/2): ${e.eventId}`),this.resetIdleTimer(e)))}).catch(()=>{this.activeRun?.eventId===e.eventId&&(a.error("acp-adapter",`Ping error, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())}):(a.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())},i)}declareStuck(){this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.close().catch(()=>{}),this.agentProcess=null),this.emit("stuck")}appendToStream(e,i){e.buffer+=i,e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushStream(),j))}emitSegmentedStream(e,i){if(!i)return;const t=e.markdownSegmenter.push(i);for(const s of t)s.text&&(this.callbacks.sendStreamChunk(e.eventId,e.sessionId,s.text,++e.chunkSeq,s.closeAfter===!0,e.currentClientMsgId,e.quotedMessageId),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,a.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const i=e.buffer;e.buffer="",this.emitSegmentedStream(e,i)}finishRun(e,i){const t=this.activeRun;if(!t)return;this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",t.eventId),t.flushTimer&&(clearTimeout(t.flushTimer),t.flushTimer=null),t.idleTimer&&(clearTimeout(t.idleTimer),t.idleTimer=null);const s=t.quotedStream.flush();s.deltaContent&&(t.buffer+=s.deltaContent),s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),i&&(e==="failed"?(a.error("acp-adapter",`finishRun failed: ${i} event=${t.eventId}`),t.buffer+=`
18
+ Waiting for authentication to complete...`);try{await this.acpClient.authenticate(i.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(t,"Authentication successful. Resuming...");const n=this.acpMcpTools?await this.startInternalApiAndMcp():void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:this.resolveCwd(),mcpServers:n}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[a,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(a,"ready",o,this.buildToolbarContext("binding_ready",o))}catch(n){throw r.error("acp-adapter",`Auth retry failed: ${n}`),n}}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const i=/https?:\/\/[^\s"')\]]+/;let t=!1;const s=n=>{if(t)return;const o=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(i);o&&(t=!0,this.agentProcess.removeListener("stderr",s),e(o[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{t||(t=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){if(e.type===p.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===p.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting?this.finishCompaction("context-window-update"):e.contextWindow&&this.maybeScheduleAutoCompact(e.contextWindow);return}const i=this.activeRun;if(!i)return;i.responded||(i.responded=!0);let t=!1;switch(e.type){case p.Text:{if(e.content){t=!0;const s=i.quotedStream.consume(e.content);s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(i,s.deltaContent)}break}case p.ToolUse:{t=!0,i.awaitingToolResult=!0,e.toolName&&(this.emitRawEventEnvelope(i,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:e.toolInput??""}})||this.callbacks.sendToolUse(i.eventId,i.sessionId,e.toolName,e.toolInput??""));break}case p.ToolResult:{i.awaitingToolResult=!1,e.content&&(t=!0,this.emitRawEventEnvelope(i,{type:"tool_result",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(i.eventId,i.sessionId,e.toolName??"",e.content));break}case p.ToolProgress:{t=!0,e.content&&(this.emitRawEventEnvelope(i,{type:"tool_progress",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(i.eventId,i.sessionId,e.toolName??"",e.content));break}case p.Thinking:{e.content&&(t=!0,this.emitRawEventEnvelope(i,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(i.eventId,i.sessionId,e.content));break}case p.Error:{const s=String(e.error??"unknown error");this.emitRawEventEnvelope(i,{type:"error",payload:{message:s}}),r.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(i.eventId,i.sessionId,s);break}case p.Result:{this.emitRawEventEnvelope(i,{type:"result",payload:{done:e.done??!0}});const s=i.quotedStream.flush();s.deltaContent&&this.appendToStream(i,s.deltaContent),s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),this.flushStream(),this.finishRun("responded");break}}t&&(i.lastProgressAt=Date.now(),i.idleNoProgressCount=0),this.resetIdleTimer(i)}handlePermissionRequest(e){const i=e.permissionRequest;if(!i||!e.requestId||!this.acpClient)return;if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){r.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${i.toolName}`),this.acpClient.respondPermission(i.requestId,{behavior:"allow"}).catch(()=>{});return}const t=i.toolCallId;this.pendingApprovals.set(t,e.requestId);const s=this.activeRun;s?(s.idleTimer&&(clearTimeout(s.idleTimer),s.idleTimer=null),this.emitRawEventEnvelope(s,{type:"permission_request",payload:{request_id:i.requestId,tool_call_id:t,tool_name:i.toolName,tool_title:i.toolTitle,options:i.options}})||this.callbacks.sendPermissionCard({eventId:s.eventId,sessionId:s.sessionId,toolCallId:t,toolName:i.toolName,toolTitle:i.toolTitle,options:i.options})):(r.info("acp-adapter",`Permission request without active run, auto-approving: ${i.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(t))}emitRawEventEnvelope(e,i){return!e||!this.rawTransport||!this.callbacks.sendRawEventEnvelope?!1:(this.callbacks.sendRawEventEnvelope(e.eventId,e.sessionId,{type:i.type,payload:i.payload,seq:++this.rawEventSeq,at:new Date().toISOString()}),!0)}resetIdleTimer(e){e.idleTimer&&clearTimeout(e.idleTimer);const i=e.awaitingToolResult?F:O;e.idleTimer=setTimeout(()=>{if(this.activeRun?.eventId!==e.eventId)return;const t=this.acpClient;t?.isAlive?t.ping(5e3).then(async s=>{if(this.activeRun?.eventId!==e.eventId)return;if(!s){r.error("acp-adapter",`Ping failed, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck();return}if(e.awaitingToolResult){r.info("acp-adapter",`Idle timer: tool running, ping ok, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e);return}const n=e.lastIdleCheckAt,a=e.lastProgressAt>n,o=a?!1:await D(t.sessionId,n),c=a||o;e.lastIdleCheckAt=Date.now(),c?(o&&r.info("acp-adapter",`Idle timer: no stream progress but kiro session active, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,e.idleNoProgressCount>=2?(r.error("acp-adapter",`Agent alive but no progress for ${e.idleNoProgressCount} idle cycles, declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent alive but no progress for ${e.idleNoProgressCount} idle cycles`),this.declareStuck()):(r.warn("acp-adapter",`Idle timer fired, ping ok, no progress (${e.idleNoProgressCount}/2): ${e.eventId}`),this.resetIdleTimer(e)))}).catch(()=>{this.activeRun?.eventId===e.eventId&&(r.error("acp-adapter",`Ping error, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())}):(r.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())},i)}declareStuck(){this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.close().catch(()=>{}),this.agentProcess=null),this.emit("stuck")}appendToStream(e,i){e.buffer+=i,e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushStream(),U))}emitSegmentedStream(e,i){if(!i)return;const t=e.markdownSegmenter.push(i);for(const s of t)s.text&&(this.callbacks.sendStreamChunk(e.eventId,e.sessionId,s.text,++e.chunkSeq,s.closeAfter===!0,e.currentClientMsgId,e.quotedMessageId),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,r.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const i=e.buffer;e.buffer="",this.emitSegmentedStream(e,i)}finishRun(e,i){const t=this.activeRun;if(!t)return;this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",t.eventId),t.flushTimer&&(clearTimeout(t.flushTimer),t.flushTimer=null),t.idleTimer&&(clearTimeout(t.idleTimer),t.idleTimer=null);const s=t.quotedStream.flush();s.deltaContent&&(t.buffer+=s.deltaContent),s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),i&&(e==="failed"?(r.error("acp-adapter",`finishRun failed: ${i} event=${t.eventId}`),t.buffer+=`
16
19
 
17
- The task was interrupted. Please resend your instruction.`):a.info("acp-adapter",`finishRun ${e}: ${i} event=${t.eventId}`)),t.buffer&&(this.emitSegmentedStream(t,t.buffer),t.buffer="");const n=++t.chunkSeq;if(this.callbacks.sendFinalStreamChunkReliable){let r;const c=this.callbacks.sendFinalStreamChunkReliable(t.eventId,t.sessionId,n,t.currentClientMsgId);c.catch(()=>{});const d=new Promise((u,l)=>{r=setTimeout(()=>l(new Error("final chunk ACK timeout")),5e3),r.unref()});Promise.race([c,d]).then(()=>{clearTimeout(r),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(u=>{clearTimeout(r),a.warn("acp-adapter",`finalStreamChunk ACK timeout/failed event=${t.eventId}: ${u instanceof Error?u.message:u}`),this.callbacks.sendStreamChunk(t.eventId,t.sessionId,"",n,!0,t.currentClientMsgId),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()})}else this.callbacks.sendStreamChunk(t.eventId,t.sessionId,"",n,!0,t.currentClientMsgId),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}getContextWindowUsedPercentage(e){return"usedPercentage"in e?Number.isFinite(e.usedPercentage)?e.usedPercentage:null:!Number.isFinite(e.used)||!Number.isFinite(e.size)||e.size<=0?null:Math.min(100,e.used/e.size*100)}maybeScheduleAutoCompact(e){const i=this.getContextWindowUsedPercentage(e);i===null||i<b||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,a.info("acp-adapter",`[auto-compact] context_window usedPercentage=${i.toFixed(1)}% >= ${b}%, scheduling compact`),this.tryRunPendingAutoCompact())}tryRunPendingAutoCompact(){this.pendingAutoCompact&&(this.stopped||!this.acpClient?.isAlive||this.activeRun||this.compacting||(this.pendingAutoCompact=!1,this.execCommand("compact","",this.currentAibotSessionId??"").then(e=>{a.info("acp-adapter",`[auto-compact] compact done status=${e.status} msg=${e.message??""}`)}).catch(e=>{a.warn("acp-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:String(e)}`)})))}persistEventResult(e,i,t){this.eventResults&&!e.silent&&this.eventResults.set({sessionId:e.sessionId,eventId:e.eventId,status:i,msg:t,updatedAt:Date.now()})}}class W extends C{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){a.warn("acp-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{pe as AcpAdapter};
20
+ The task was interrupted. Please resend your instruction.`):r.info("acp-adapter",`finishRun ${e}: ${i} event=${t.eventId}`)),t.buffer&&(this.emitSegmentedStream(t,t.buffer),t.buffer="");const n=++t.chunkSeq;if(this.callbacks.sendFinalStreamChunkReliable){let o;const c=this.callbacks.sendFinalStreamChunkReliable(t.eventId,t.sessionId,n,t.currentClientMsgId);c.catch(()=>{});const d=new Promise((u,l)=>{o=setTimeout(()=>l(new Error("final chunk ACK timeout")),5e3),o.unref()});Promise.race([c,d]).then(()=>{clearTimeout(o),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(u=>{clearTimeout(o),r.warn("acp-adapter",`finalStreamChunk ACK timeout/failed event=${t.eventId}: ${u instanceof Error?u.message:u}`),this.callbacks.sendStreamChunk(t.eventId,t.sessionId,"",n,!0,t.currentClientMsgId),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()})}else this.callbacks.sendStreamChunk(t.eventId,t.sessionId,"",n,!0,t.currentClientMsgId),t.silent||this.callbacks.sendEventResult(t.eventId,e,i),this.persistEventResult(t,e,i),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}getContextWindowUsedPercentage(e){return"usedPercentage"in e?Number.isFinite(e.usedPercentage)?e.usedPercentage:null:!Number.isFinite(e.used)||!Number.isFinite(e.size)||e.size<=0?null:Math.min(100,e.used/e.size*100)}maybeScheduleAutoCompact(e){const i=this.getContextWindowUsedPercentage(e);i===null||i<b||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("acp-adapter",`[auto-compact] context_window usedPercentage=${i.toFixed(1)}% >= ${b}%, scheduling compact`),this.tryRunPendingAutoCompact())}tryRunPendingAutoCompact(){this.pendingAutoCompact&&(this.stopped||!this.acpClient?.isAlive||this.activeRun||this.compacting||(this.pendingAutoCompact=!1,this.execCommand("compact","",this.currentAibotSessionId??"").then(e=>{r.info("acp-adapter",`[auto-compact] compact done status=${e.status} msg=${e.message??""}`)}).catch(e=>{r.warn("acp-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:String(e)}`)})))}persistEventResult(e,i,t){this.eventResults&&!e.silent&&this.eventResults.set({sessionId:e.sessionId,eventId:e.eventId,status:i,msg:t,updatedAt:Date.now()})}}class W extends C{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){r.warn("acp-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{pe as AcpAdapter};
@@ -1,10 +1,13 @@
1
- import{spawn as P}from"node:child_process";import{randomUUID as $}from"node:crypto";import{EventEmitter as _}from"node:events";import{existsSync as S,readFileSync as y,readdirSync as w,statSync as k,mkdirSync as b,writeFileSync as x}from"node:fs";import{homedir as g}from"node:os";import{join as p,dirname as C,resolve as L}from"node:path";import{fileURLToPath as R}from"node:url";import{log as o}from"../../core/log/index.js";import{killProcessGroup as f}from"../../core/runtime/spawn.js";import{InternalApiServer as T}from"../../core/mcp/internal-api-server.js";import{syncDefaultSkillsToDir as j}from"../../default-skills/index.js";import{buildSimpleProbeReport as G}from"../shared/probe-util.js";const a="agy-adapter",m=p(g(),".gemini","antigravity-cli","log"),I=p(g(),".gemini","antigravity-cli","cache","last_conversations.json"),M=["\u4F60\u63A5\u5165\u7684\u662F Grix \u804A\u5929\u3002\u7528\u6237\u6D88\u606F\u88AB\u5305\u5728 <channel ...> \u6807\u7B7E\u91CC\uFF0C\u6807\u7B7E\u5C5E\u6027\u643A\u5E26\u672C\u8F6E\u4E0A\u4E0B\u6587\uFF1A","chat_id\uFF08\u5F53\u524D\u4F1A\u8BDD\uFF09\u3001event_id\uFF08\u672C\u8F6E\u4E8B\u4EF6\uFF09\u3001message_id\uFF08\u7528\u6237\u8FD9\u6761\u6D88\u606F\u7684 ID\uFF09\u3001user_id\uFF08\u53D1\u9001\u8005\uFF09\u3002","\u91CD\u8981\uFF1A\u4F60\u7684\u56DE\u590D\u5FC5\u987B\u8C03\u7528 grix \u7684 `grix_message_send` \u5DE5\u5177\u53D1\u9001\uFF08sessionId \u53D6\u81EA channel \u7684 chat_id\uFF0Ccontent \u4E3A\u4F60\u7684\u56DE\u590D\u5185\u5BB9\uFF09\uFF0C","\u4E0D\u8981\u53EA\u628A\u7B54\u6848\u6253\u5370\u5230\u6807\u51C6\u8F93\u51FA\u2014\u2014\u53EA\u6709\u8C03\u7528\u5DE5\u5177\u624D\u4F1A\u628A\u6D88\u606F\u771F\u6B63\u53D1\u7ED9\u7528\u6237\u3002","grix_message_send \u4F1A\u8FD4\u56DE\u4F60\u521A\u53D1\u51FA\u6D88\u606F\u7684 msg_id\u3002\u9700\u8981\u65F6\u4F60\u53EF\u4EE5\u7528\u8FD9\u4E2A ID \u505A\u540E\u7EED\u64CD\u4F5C\uFF0C\u4F8B\u5982\u8C03\u7528 grix_message_unsend\uFF08\u4F20 msgId\uFF09\u64A4\u56DE\u67D0\u6761\u6D88\u606F\u3002"].join(""),O=new Set(["send_msg","grix_reply"]),D=new Set(["send_msg","grix_reply","delete_msg","session_send","grix_complete","grix_composing","grix_event_ack"]);class X extends _{type="agy";config;callbacks;runtimeResolver;alive=!1;stopped=!1;internalApi=null;activeEventId=null;activeEventSessionId=null;activeProcess=null;completedEventIds=new Set;activeEventSentViaTool=!1;sessionConversationMap=new Map;conversationOutputCache=new Map;eventQueue=[];constructor(t,e,s){super(),this.config=t,this.callbacks=e,this.runtimeResolver=s}async start(){this.alive||(this.alive=!0,this.stopped=!1,await this.startInternalApiAndInjectMcp(),o.info(a,"AgyAdapter started"))}async startInternalApiAndInjectMcp(){try{this.internalApi=new T,this.internalApi.setInvokeHandler(async(r,d)=>this.handleToolInvoke(r,d)),await this.internalApi.start(0),o.info(a,`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=p(g(),".gemini","config","mcp_config.json");b(C(e),{recursive:!0});let s={};try{S(e)&&(s=JSON.parse(y(e,"utf8")))}catch{}const i=s.mcpServers&&typeof s.mcpServers=="object"?s.mcpServers:{};i[t.name]={command:t.command,args:t.args},s.mcpServers=i,x(e,`${JSON.stringify(s,null,2)}
2
- `,"utf8"),o.info(a,`MCP config injected into ${e}`);const n=p(g(),".gemini","skills"),c=j(n);c.length>0&&o.info(a,`Synced connector skills to ${n}: [${c.join(", ")}]`)}catch(t){o.warn(a,`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async handleToolInvoke(t,e){let s=e;D.has(t)&&this.activeEventSessionId&&(s.session_id==null||s.session_id==="")&&(s={...s,session_id:this.activeEventSessionId});const i=await this.callbacks.agentInvoke(t,s);return O.has(t)&&this.activeEventId&&(this.activeEventSentViaTool=!0),i}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.activeProcess){try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null}if(this.activeEventId&&this.activeEventSessionId)try{this.callbacks.sendEventResult(this.activeEventId,"canceled","adapter stopped")}catch{}if(this.activeEventId=null,this.activeEventSessionId=null,this.internalApi){try{await this.internalApi.stop()}catch{}this.internalApi=null}this.emit("exit",0),o.info(a,"AgyAdapter stopped")}}isAlive(){return this.alive}async createSession(t){return t.cwd??$()}async resumeSession(t,e){}async destroySession(t){}sendPrompt(t){const e=new _;return e.adapterSessionId=t.adapterSessionId,e.cancel=async()=>{e.emit("done",{status:"canceled"})},e}async cancel(t){}cancelCurrentRun(){if(!this.activeEventId||!this.activeProcess)return;const t=this.activeEventId;o.info(a,`cancelCurrentRun: killing process group for event ${t}`);try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","restarted by user"),this.clearActiveEvent(t),this.drainQueue()}deliverInboundEvent(t){if(!this.stopped){if(this.completedEventIds.has(t.event_id)){o.debug(a,`Skipping duplicate event: ${t.event_id}`);return}if(this.callbacks.sendEventAck(t.event_id,t.session_id),this.activeEventId){o.info(a,`Queuing event ${t.event_id} (active: ${this.activeEventId})`),this.eventQueue.push(t);return}this.processEvent(t)}}deliverStopEvent(t,e){if(this.activeEventId===t&&this.activeProcess){o.info(a,`Stopping event ${t}`);try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActiveEvent(t),this.drainQueue()}}setPermissionHandler(){}async ping(t){return this.alive}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.sessionConversationMap.size}}async probe(t){const e=this.getStatus();return G(this.config.command||"agy",{alive:e.alive,busy:e.busy,started:e.alive},t)}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){if(this.activeProcess)try{f(this.activeProcess,"SIGKILL")}catch{}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1}getMcpConfig(){if(!this.internalApi)return null;const t=L(R(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}processEvent(t){const{event_id:e,session_id:s}=t,i=this.runtimeResolver(s);if(!i.cwd){o.warn(a,`No working directory for session ${s}, event should have been intercepted by bridge`),this.callbacks.forceCompleteInternalEvent(e,s);return}this.activeEventId=e,this.activeEventSessionId=s,this.activeEventSentViaTool=!1,this.emit("eventStarted",e,s),o.info(a,`Processing event ${e} for session ${s}`);const n=this.buildAgyPrompt(t);this.spawnAgyPrint(e,s,n,i)}buildAgyPrompt(t){const s=[["chat_id",t.session_id],["event_id",t.event_id],["message_id",t.msg_id],["user_id",t.sender_id],["quoted_message_id",t.quoted_message_id]].filter(([,r])=>r!=null&&r!=="").map(([r,d])=>`${r}="${d}"`).join(" "),i=this.renderContextBlock(t),n=i?`${i}
1
+ import{spawn as P}from"node:child_process";import{randomUUID as $}from"node:crypto";import{EventEmitter as _}from"node:events";import{existsSync as E,readFileSync as S,readdirSync as k,statSync as w,mkdirSync as b,writeFileSync as x}from"node:fs";import{homedir as g}from"node:os";import{join as p,dirname as C,resolve as L}from"node:path";import{fileURLToPath as R}from"node:url";import{log as o}from"../../core/log/index.js";import{killProcessGroup as f}from"../../core/runtime/spawn.js";import{InternalApiServer as T}from"../../core/mcp/internal-api-server.js";import{syncDefaultSkillsToDir as j}from"../../default-skills/index.js";import{buildSimpleProbeReport as G}from"../shared/probe-util.js";const a="agy-adapter",m=p(g(),".gemini","antigravity-cli","log"),I=p(g(),".gemini","antigravity-cli","cache","last_conversations.json"),M=["\u4F60\u63A5\u5165\u7684\u662F Grix \u804A\u5929\u3002\u7528\u6237\u6D88\u606F\u88AB\u5305\u5728 <channel ...> \u6807\u7B7E\u91CC\uFF0C\u6807\u7B7E\u5C5E\u6027\u643A\u5E26\u672C\u8F6E\u4E0A\u4E0B\u6587\uFF1A","chat_id\uFF08\u5F53\u524D\u4F1A\u8BDD\uFF09\u3001event_id\uFF08\u672C\u8F6E\u4E8B\u4EF6\uFF09\u3001message_id\uFF08\u7528\u6237\u8FD9\u6761\u6D88\u606F\u7684 ID\uFF09\u3001user_id\uFF08\u53D1\u9001\u8005\uFF09\u3002","\u91CD\u8981\uFF1A\u4F60\u7684\u56DE\u590D\u5FC5\u987B\u8C03\u7528 grix \u7684 `grix_message_send` \u5DE5\u5177\u53D1\u9001\uFF08sessionId \u53D6\u81EA channel \u7684 chat_id\uFF0Ccontent \u4E3A\u4F60\u7684\u56DE\u590D\u5185\u5BB9\uFF09\uFF0C","\u4E0D\u8981\u53EA\u628A\u7B54\u6848\u6253\u5370\u5230\u6807\u51C6\u8F93\u51FA\u2014\u2014\u53EA\u6709\u8C03\u7528\u5DE5\u5177\u624D\u4F1A\u628A\u6D88\u606F\u771F\u6B63\u53D1\u7ED9\u7528\u6237\u3002","grix_message_send \u4F1A\u8FD4\u56DE\u4F60\u521A\u53D1\u51FA\u6D88\u606F\u7684 msg_id\u3002\u9700\u8981\u65F6\u4F60\u53EF\u4EE5\u7528\u8FD9\u4E2A ID \u505A\u540E\u7EED\u64CD\u4F5C\uFF0C\u4F8B\u5982\u8C03\u7528 grix_message_unsend\uFF08\u4F20 msgId\uFF09\u64A4\u56DE\u67D0\u6761\u6D88\u606F\u3002"].join(""),O=new Set(["send_msg","grix_reply"]),D=new Set(["send_msg","grix_reply","delete_msg","session_send","grix_complete","grix_composing","grix_event_ack"]);class X extends _{type="agy";config;callbacks;runtimeResolver;alive=!1;stopped=!1;internalApi=null;activeEventId=null;activeEventSessionId=null;activeProcess=null;completedEventIds=new Set;activeEventSentViaTool=!1;sessionConversationMap=new Map;conversationOutputCache=new Map;eventQueue=[];constructor(t,e,s){super(),this.config=t,this.callbacks=e,this.runtimeResolver=s}async start(){this.alive||(this.alive=!0,this.stopped=!1,await this.startInternalApiAndInjectMcp(),o.info(a,"AgyAdapter started"))}async startInternalApiAndInjectMcp(){try{this.internalApi=new T,this.internalApi.setInvokeHandler(async(r,l)=>this.handleToolInvoke(r,l)),await this.internalApi.start(0),o.info(a,`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=p(g(),".gemini","config","mcp_config.json");b(C(e),{recursive:!0});let s={};try{E(e)&&(s=JSON.parse(S(e,"utf8")))}catch{}const i=s.mcpServers&&typeof s.mcpServers=="object"?s.mcpServers:{};i[t.name]={command:t.command,args:t.args},s.mcpServers=i,x(e,`${JSON.stringify(s,null,2)}
2
+ `,"utf8"),o.info(a,`MCP config injected into ${e}`);const n=p(g(),".gemini","skills"),c=j(n);c.length>0&&o.info(a,`Synced connector skills to ${n}: [${c.join(", ")}]`)}catch(t){o.warn(a,`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async handleToolInvoke(t,e){let s=e;D.has(t)&&this.activeEventSessionId&&(s.session_id==null||s.session_id==="")&&(s={...s,session_id:this.activeEventSessionId});const i=await this.callbacks.agentInvoke(t,s);return O.has(t)&&this.activeEventId&&(this.activeEventSentViaTool=!0),i}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.activeProcess){try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null}if(this.activeEventId&&this.activeEventSessionId)try{this.callbacks.sendEventResult(this.activeEventId,"canceled","adapter stopped")}catch{}if(this.activeEventId=null,this.activeEventSessionId=null,this.internalApi){try{await this.internalApi.stop()}catch{}this.internalApi=null}this.emit("exit",0),o.info(a,"AgyAdapter stopped")}}isAlive(){return this.alive}async createSession(t){return t.cwd??$()}async resumeSession(t,e){}async destroySession(t){}sendPrompt(t){const e=new _;return e.adapterSessionId=t.adapterSessionId,e.cancel=async()=>{e.emit("done",{status:"canceled"})},e}async cancel(t){}cancelCurrentRun(){if(!this.activeEventId||!this.activeProcess)return;const t=this.activeEventId;o.info(a,`cancelCurrentRun: killing process group for event ${t}`);try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","restarted by user"),this.clearActiveEvent(t),this.drainQueue()}deliverInboundEvent(t){if(!this.stopped){if(this.completedEventIds.has(t.event_id)){o.debug(a,`Skipping duplicate event: ${t.event_id}`);return}if(this.callbacks.sendEventAck(t.event_id,t.session_id),this.activeEventId){o.info(a,`Queuing event ${t.event_id} (active: ${this.activeEventId})`),this.eventQueue.push(t);return}this.processEvent(t)}}deliverStopEvent(t,e){if(this.activeEventId===t&&this.activeProcess){o.info(a,`Stopping event ${t}`);try{f(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActiveEvent(t),this.drainQueue()}}setPermissionHandler(){}async ping(t){return this.alive}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.sessionConversationMap.size}}async probe(t){const e=this.getStatus();return G(this.config.command||"agy",{alive:e.alive,busy:e.busy,started:e.alive},t)}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){if(this.activeProcess)try{f(this.activeProcess,"SIGKILL")}catch{}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1}getMcpConfig(){if(!this.internalApi)return null;const t=L(R(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}processEvent(t){const{event_id:e,session_id:s}=t,i=this.runtimeResolver(s);if(!i.cwd){o.warn(a,`No working directory for session ${s}, event should have been intercepted by bridge`),this.callbacks.forceCompleteInternalEvent(e,s);return}this.activeEventId=e,this.activeEventSessionId=s,this.activeEventSentViaTool=!1,this.emit("eventStarted",e,s),o.info(a,`Processing event ${e} for session ${s}`);const n=this.buildAgyPrompt(t);this.spawnAgyPrint(e,s,n,i)}buildAgyPrompt(t){const s=[["chat_id",t.session_id],["event_id",t.event_id],["message_id",t.msg_id],["user_id",t.sender_id],["quoted_message_id",t.quoted_message_id]].filter(([,l])=>l!=null&&l!=="").map(([l,h])=>`${l}="${h}"`).join(" "),i=this.renderContextBlock(t),n=i?`${i}
3
3
 
4
4
  ${t.content}`:t.content,c=`<channel source="grix-agy" ${s}>
5
5
  ${n}
6
- </channel>`;return`${M}
6
+ </channel>`;return`${this.buildIdentityBlock()}${M}
7
7
 
8
- ${c}`}renderContextBlock(t){const e=t.context_messages_json;if(!e)return"";let s;try{const r=JSON.parse(e);if(!Array.isArray(r))return"";s=r}catch{return""}const i=String(t.session_type??"")==="2",n=String(t.msg_id??""),c=[];for(const r of s){const d=String(r?.msg_id??"");if(d&&d===n)continue;const h=String(r?.content??"").trim();if(!h)continue;const u=String(r?.sender_id??"");if(h.startsWith("[\u5F15\u7528\u6D88\u606F]")){const l=h.slice(6).replace(/^\s*\n?/,"").trim();c.push(i&&u?`[\u5F15\u7528\u6D88\u606F] (\u6765\u81EA ${u})\uFF1A${l}`:`[\u5F15\u7528\u6D88\u606F]\uFF1A${l}`)}else c.push(i&&u?`[${u}]\uFF1A${h}`:h)}return c.join(`
9
- `)}spawnAgyPrint(t,e,s,i){const n=this.buildPrintArgs(s,i),c={...process.env,...this.config.env??{}};o.info(a,`Spawning: agy ${n.map(l=>l.includes(" ")?`"${l}"`:l).join(" ")}`);const r=this.getLatestLogFilePath(),d=P(this.config.command,n,{cwd:i.cwd,env:c,stdio:["pipe","pipe","pipe"],detached:!0});this.activeProcess=d;const h=[],u=[];d.stdout?.on("data",l=>{h.push(l)}),d.stderr?.on("data",l=>{u.push(l)}),d.on("error",l=>{o.error(a,`Process spawn error for event ${t}: ${l.message}`),this.handleProcessResult(t,e,"",`spawn error: ${l.message}`,i)}),d.on("close",l=>{if(o.info(a,`Process exited for event ${t} with code ${l}`),this.activeEventId!==t)return;const v=Buffer.concat(h).toString("utf-8"),A=Buffer.concat(u).toString("utf-8");if(l!==0){const E=A.trim()||`process exited with code ${l}`;this.handleProcessResult(t,e,v,E,i)}else v.trim()?this.handleProcessResult(t,e,v,"",i):this.activeEventSentViaTool?this.handleProcessResult(t,e,v,"",i):setTimeout(()=>{const E=this.extractLogError(r);this.handleProcessResult(t,e,v,E,i)},300)}),d.stdin?.end()}buildPrintArgs(t,e){const s=[];return e.modelId&&s.push("--model",e.modelId),e.cwd&&s.push("--add-dir",e.cwd),s.push("--dangerously-skip-permissions"),e.conversationId&&s.push("--conversation",e.conversationId),s.push("-p",t),s}readAgyConversationId(t){try{if(S(I))return JSON.parse(y(I,"utf-8"))[t]||void 0}catch(e){o.debug(a,`readAgyConversationId: ${e}`)}}getLatestLogFilePath(){try{if(!S(m))return null;const t=w(m).filter(e=>e.startsWith("cli-")&&e.endsWith(".log")).map(e=>({name:e,path:p(m,e),mtime:k(p(m,e)).mtimeMs})).sort((e,s)=>s.mtime-e.mtime);return t.length>0?t[0].path:null}catch{return null}}extractLogError(t){try{const e=this.getLatestLogFilePath();if(!e)return o.info(a,"extractLogError: no log files found"),"agy completed without output";const s=y(e,"utf-8");if(!s)return o.info(a,`extractLogError: log file is empty: ${e}`),"agy completed without output";const i=s.split(`
10
- `).filter(n=>n.startsWith("E")).map(n=>{const c=n.indexOf("] ");return c>=0?n.slice(c+2):n}).filter(n=>n.length>0);if(i.length>0){const n=i[i.length-1];return o.info(a,`Extracted error from agy log: ${n.slice(0,120)}`),n.length>500?n.slice(0,500)+"...":n}return"agy completed without output (possible auth or quota issue)"}catch(e){return o.info(a,`extractLogError: ${e}`),"agy completed without output"}}handleProcessResult(t,e,s,i,n){if(this.activeEventId===t){if(i&&!s.trim()){o.error(a,`Event ${t} failed: ${i}`);const r=i.includes("RESOURCE_EXHAUSTED")||i.includes("quota")?"The agy API quota has been exhausted. Please try again later.":`agy execution failed: ${i}`;this.callbacks.sendStreamChunk(t,e,r,1,!0),this.callbacks.sendEventResult(t,"failed",i)}else{const c=s.trim();if(c){const r=n.cwd,d=r?this.extractDelta(c,r):c;r&&this.conversationOutputCache.set(r,c),d&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,d,1,!0)}if(n.cwd){const r=this.readAgyConversationId(n.cwd);r&&r!==n.conversationId&&(this.sessionConversationMap.set(e,r),this.callbacks.persistConversationId(e,r))}this.callbacks.sendEventResult(t,"responded")}this.clearActiveEvent(t),this.drainQueue()}}extractDelta(t,e){const s=this.conversationOutputCache.get(e);return s&&t.startsWith(s)?t.slice(s.length).trim():t}clearActiveEvent(t){const e=t??this.activeEventId;if(e&&(this.completedEventIds.add(e),this.completedEventIds.size>1e3)){const s=Array.from(this.completedEventIds);this.completedEventIds.clear();for(let i=500;i<s.length;i++)this.completedEventIds.add(s[i])}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1,e&&this.emit("eventDone",e)}drainQueue(){if(this.stopped||this.activeEventId)return;const t=this.eventQueue.shift();t&&(o.info(a,`Draining queued event ${t.event_id}`),this.processEvent(t))}}export{X as AgyAdapter};
8
+ ${c}`}buildIdentityBlock(){const t=this.callbacks.getAgentProfile?.(),e=t?.agentName?.trim()??"",s=t?.introduction?.trim()??"";if(!e&&!s)return"";const i=["[system-identity]"];return e&&i.push(`\u4F60\u7684\u540D\u5B57\u662F"${e}"\u3002`),s&&i.push(`\u4F60\u7684\u81EA\u6211\u4ECB\u7ECD\uFF1A${s}`),i.push("\u8FD9\u662F\u5E26\u5916\u6307\u4EE4\uFF1B\u4E0D\u8981\u5728\u56DE\u590D\u4E2D\u590D\u8FF0\u6216\u5F15\u7528\u672C\u6BB5\u3002"),i.push("[/system-identity]"),`${i.join(`
9
+ `)}
10
+
11
+ `}renderContextBlock(t){const e=t.context_messages_json;if(!e)return"";let s;try{const r=JSON.parse(e);if(!Array.isArray(r))return"";s=r}catch{return""}const i=String(t.session_type??"")==="2",n=String(t.msg_id??""),c=[];for(const r of s){const l=String(r?.msg_id??"");if(l&&l===n)continue;const h=String(r?.content??"").trim();if(!h)continue;const u=String(r?.sender_id??"");if(h.startsWith("[\u5F15\u7528\u6D88\u606F]")){const d=h.slice(6).replace(/^\s*\n?/,"").trim();c.push(i&&u?`[\u5F15\u7528\u6D88\u606F] (\u6765\u81EA ${u})\uFF1A${d}`:`[\u5F15\u7528\u6D88\u606F]\uFF1A${d}`)}else c.push(i&&u?`[${u}]\uFF1A${h}`:h)}return c.join(`
12
+ `)}spawnAgyPrint(t,e,s,i){const n=this.buildPrintArgs(s,i),c={...process.env,...this.config.env??{}};o.info(a,`Spawning: agy ${n.map(d=>d.includes(" ")?`"${d}"`:d).join(" ")}`);const r=this.getLatestLogFilePath(),l=P(this.config.command,n,{cwd:i.cwd,env:c,stdio:["pipe","pipe","pipe"],detached:!0});this.activeProcess=l;const h=[],u=[];l.stdout?.on("data",d=>{h.push(d)}),l.stderr?.on("data",d=>{u.push(d)}),l.on("error",d=>{o.error(a,`Process spawn error for event ${t}: ${d.message}`),this.handleProcessResult(t,e,"",`spawn error: ${d.message}`,i)}),l.on("close",d=>{if(o.info(a,`Process exited for event ${t} with code ${d}`),this.activeEventId!==t)return;const v=Buffer.concat(h).toString("utf-8"),A=Buffer.concat(u).toString("utf-8");if(d!==0){const y=A.trim()||`process exited with code ${d}`;this.handleProcessResult(t,e,v,y,i)}else v.trim()?this.handleProcessResult(t,e,v,"",i):this.activeEventSentViaTool?this.handleProcessResult(t,e,v,"",i):setTimeout(()=>{const y=this.extractLogError(r);this.handleProcessResult(t,e,v,y,i)},300)}),l.stdin?.end()}buildPrintArgs(t,e){const s=[];return e.modelId&&s.push("--model",e.modelId),e.cwd&&s.push("--add-dir",e.cwd),s.push("--dangerously-skip-permissions"),e.conversationId&&s.push("--conversation",e.conversationId),s.push("-p",t),s}readAgyConversationId(t){try{if(E(I))return JSON.parse(S(I,"utf-8"))[t]||void 0}catch(e){o.debug(a,`readAgyConversationId: ${e}`)}}getLatestLogFilePath(){try{if(!E(m))return null;const t=k(m).filter(e=>e.startsWith("cli-")&&e.endsWith(".log")).map(e=>({name:e,path:p(m,e),mtime:w(p(m,e)).mtimeMs})).sort((e,s)=>s.mtime-e.mtime);return t.length>0?t[0].path:null}catch{return null}}extractLogError(t){try{const e=this.getLatestLogFilePath();if(!e)return o.info(a,"extractLogError: no log files found"),"agy completed without output";const s=S(e,"utf-8");if(!s)return o.info(a,`extractLogError: log file is empty: ${e}`),"agy completed without output";const i=s.split(`
13
+ `).filter(n=>n.startsWith("E")).map(n=>{const c=n.indexOf("] ");return c>=0?n.slice(c+2):n}).filter(n=>n.length>0);if(i.length>0){const n=i[i.length-1];return o.info(a,`Extracted error from agy log: ${n.slice(0,120)}`),n.length>500?n.slice(0,500)+"...":n}return"agy completed without output (possible auth or quota issue)"}catch(e){return o.info(a,`extractLogError: ${e}`),"agy completed without output"}}handleProcessResult(t,e,s,i,n){if(this.activeEventId===t){if(i&&!s.trim()){o.error(a,`Event ${t} failed: ${i}`);const r=i.includes("RESOURCE_EXHAUSTED")||i.includes("quota")?"The agy API quota has been exhausted. Please try again later.":`agy execution failed: ${i}`;this.callbacks.sendStreamChunk(t,e,r,1,!0),this.callbacks.sendEventResult(t,"failed",i)}else{const c=s.trim();if(c){const r=n.cwd,l=r?this.extractDelta(c,r):c;r&&this.conversationOutputCache.set(r,c),l&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,l,1,!0)}if(n.cwd){const r=this.readAgyConversationId(n.cwd);r&&r!==n.conversationId&&(this.sessionConversationMap.set(e,r),this.callbacks.persistConversationId(e,r))}this.callbacks.sendEventResult(t,"responded")}this.clearActiveEvent(t),this.drainQueue()}}extractDelta(t,e){const s=this.conversationOutputCache.get(e);return s&&t.startsWith(s)?t.slice(s.length).trim():t}clearActiveEvent(t){const e=t??this.activeEventId;if(e&&(this.completedEventIds.add(e),this.completedEventIds.size>1e3)){const s=Array.from(this.completedEventIds);this.completedEventIds.clear();for(let i=500;i<s.length;i++)this.completedEventIds.add(s[i])}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1,e&&this.emit("eventDone",e)}drainQueue(){if(this.stopped||this.activeEventId)return;const t=this.eventQueue.shift();t&&(o.info(a,`Draining queued event ${t.event_id}`),this.processEvent(t))}}export{X as AgyAdapter};
@@ -1,6 +1,6 @@
1
- import{execFileSync as I}from"node:child_process";import{stat as _}from"node:fs/promises";import{resolveCommandPath as k,spawnCommand as E,killProcessGroup as w}from"../../core/runtime/spawn.js";import{createInterface as A}from"node:readline";import{EventEmitter as b}from"node:events";import{join as C,resolve as T}from"node:path";import{homedir as M}from"node:os";import{fileURLToPath as $}from"node:url";import{InternalApiServer as P}from"../../core/mcp/internal-api-server.js";import{syncDefaultSkillsToDir as x}from"../../default-skills/index.js";import{buildSimpleProbeReport as R}from"../shared/probe-util.js";import{log as n}from"../../core/log/index.js";import{SessionBindingStore as D}from"../../core/persistence/session-binding-store.js";const v=120*1e3;function L(f){try{return process.kill(f,0),!0}catch(e){return e.code==="EPERM"}}class V extends b{type="codewhale";config;callbacks;alive=!1;stopped=!1;internalApi=null;deepSeekSessionId=null;activeEventId=null;activeSessionId=null;chunkSeq=0;activeClientMsgId=null;idleTimer=null;activeProcess=null;bindingStore=null;aibotSessionId="";cwd;lastUsage=null;currentModel=null;constructor(e,t){super(),this.config=e,this.callbacks=t;const i=e.options??{};if(this.aibotSessionId=String(i.aibotSessionId??"").trim(),this.bindingStore=i.bindingStore instanceof D?i.bindingStore:null,this.cwd=this.resolveCwd(),this.bindingStore&&this.aibotSessionId){const o=this.bindingStore.getCodeWhaleThreadId(this.aibotSessionId);o&&(this.deepSeekSessionId=o)}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}return process.cwd()}async start(){this.alive=!0,await this.startInternalApiAndRegisterMcp(),this.notifyBindingReady(),n.info("codewhale-adapter","Ready (exec mode)")}async stop(){this.stopped=!0,this.alive=!1,this.stopComposing(),this.clearIdleTimer(),this.killActiveProcess(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.alive}async createSession(e){const t=this.deepSeekSessionId??`ds-${Date.now()}`;return this.notifyBindingReady(),t}async resumeSession(e,t){}async destroySession(e){this.deepSeekSessionId=null,this.persistSessionId(void 0)}sendPrompt(e){const t=new y(e.adapterSessionId);return this.runMessage(e,t).catch(i=>{t.emitError(i instanceof Error?i:new Error(String(i)))}),t}async cancel(e){this.killActiveProcess()}setPermissionHandler(e){}async ping(e){if(this.stopped||!this.alive)return!1;const t=this.activeProcess;return!(t?.pid&&!L(t.pid))}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.deepSeekSessionId?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){this.clearIdleTimer(),this.killActiveProcess(),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const e=T($(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url]}}async probe(e){const t=this.getStatus();return R(this.config.command||"codewhale",{alive:t.alive,busy:t.busy,started:this.alive},e)}getUsageSnapshot(){return this.lastUsage}async startInternalApiAndRegisterMcp(){try{this.internalApi=new P,this.internalApi.setInvokeHandler(async(a,p)=>this.callbacks.agentInvoke(a,p)),await this.internalApi.start(0),n.info("codewhale-adapter",`Internal API started at ${this.internalApi.url}`);const e=this.getMcpConfig(),t={...process.env,...this.config.env},i=typeof t.PATH=="string"?t.PATH:void 0,o=k(this.config.command||"codewhale",i);try{I(o,["mcp","remove",e.name],{env:t,timeout:1e4,stdio:"ignore"})}catch{}const s=["mcp","add",e.name,"--command",e.command];for(const a of e.args)s.push("--arg",a);I(o,s,{env:t,timeout:1e4,stdio:"ignore"}),n.info("codewhale-adapter",`Registered MCP server: ${e.name}`);const r=C(M(),".codewhale","skills"),d=x(r);d.length>0&&n.info("codewhale-adapter",`Synced connector skills to ${r}: [${d.join(", ")}]`)}catch(e){n.warn("codewhale-adapter",`Failed to register MCP tools (non-fatal): ${e instanceof Error?e.message:String(e)}`)}}getSupportedCommands(){return[{name:"status",description:"Show session and working directory status"}]}async execCommand(e,t,i){return e==="status"?{status:"ok",message:`Session: ${this.deepSeekSessionId??"none"}, CWD: ${this.cwd}`,data:{sessionId:this.deepSeekSessionId,cwd:this.cwd,alive:this.alive}}:{status:"unsupported",message:`Unknown command: ${e}`}}async handleLocalAction(e){const t=e.action_type??"",i=e.params??{};switch(t){case"get_context":return this.callbacks.sendLocalActionResult(e.action_id,"ok",{sessionId:this.deepSeekSessionId,cwd:this.cwd,model:this.currentModel}),{handled:!0,kind:"get_context"};case"set_model":{const o=String(i.model_id??"").trim();return o?(this.currentModel=o,this.callbacks.sendLocalActionResult(e.action_id,"ok",{outcome:"model_set",modelId:o}),n.info("codewhale-adapter",`Model set to: ${o}`),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"invalid_params","model_id is required"),{handled:!0,kind:"set_model"})}default:return{handled:!1,kind:""}}}deliverInboundEvent(e){const t=e.content;if(this.activeEventId){n.info("codewhale-adapter",`Event ${e.event_id}: rejected, busy with ${this.activeEventId}`),this.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}this.startNewMessage(e,t)}deliverStopEvent(e,t){this.activeEventId===e&&(this.killActiveProcess(),this.callbacks.sendEventResult(e,"canceled","stopped by user"),this.clearActive())}startNewMessage(e,t){this.activeEventId=e.event_id,this.activeSessionId=e.session_id,this.chunkSeq=0,this.activeClientMsgId=`ds-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,this.startComposing();const i={adapterSessionId:this.deepSeekSessionId??"",text:t,contextMessages:e.context_messages_json?JSON.parse(e.context_messages_json).map(s=>({senderId:s.sender_id??"unknown",content:s.content})):void 0},o=new y(this.deepSeekSessionId??"");this.runMessage(i,o,e.event_id,e.session_id).catch(s=>{n.error("codewhale-adapter",`Message failed: ${s}`),this.callbacks.sendEventResult(e.event_id,"failed",s instanceof Error?s.message:String(s)),this.clearActive()}),this.resetIdleTimer(e.event_id)}buildExecArgs(e){const t=["exec","--output-format","stream-json"];return this.currentModel&&t.push("--model",this.currentModel),this.deepSeekSessionId&&t.push("--resume",this.deepSeekSessionId),t.push("--",e),t}async runMessage(e,t,i,o){let s=e.text;e.contextMessages&&e.contextMessages.length>0&&(s=`Conversation context:
1
+ import{execFileSync as I}from"node:child_process";import{stat as _}from"node:fs/promises";import{resolveCommandPath as w,spawnCommand as A,killProcessGroup as k}from"../../core/runtime/spawn.js";import{createInterface as E}from"node:readline";import{EventEmitter as b}from"node:events";import{join as C,resolve as T}from"node:path";import{homedir as M}from"node:os";import{fileURLToPath as P}from"node:url";import{InternalApiServer as $}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as x}from"../shared/identity-injector.js";import{syncDefaultSkillsToDir as R}from"../../default-skills/index.js";import{buildSimpleProbeReport as D}from"../shared/probe-util.js";import{log as n}from"../../core/log/index.js";import{SessionBindingStore as L}from"../../core/persistence/session-binding-store.js";const v=120*1e3;function N(f){try{return process.kill(f,0),!0}catch(e){return e.code==="EPERM"}}class Y extends b{type="codewhale";config;callbacks;alive=!1;stopped=!1;internalApi=null;deepSeekSessionId=null;activeEventId=null;activeSessionId=null;chunkSeq=0;activeClientMsgId=null;idleTimer=null;activeProcess=null;identity;bindingStore=null;aibotSessionId="";cwd;lastUsage=null;currentModel=null;constructor(e,t){super(),this.config=e,this.callbacks=t,this.identity=new x("codewhale-adapter",t.getAgentProfile);const i=e.options??{};if(this.aibotSessionId=String(i.aibotSessionId??"").trim(),this.bindingStore=i.bindingStore instanceof L?i.bindingStore:null,this.cwd=this.resolveCwd(),this.bindingStore&&this.aibotSessionId){const o=this.bindingStore.getCodeWhaleThreadId(this.aibotSessionId);o&&(this.deepSeekSessionId=o)}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}return process.cwd()}async start(){this.alive=!0,await this.startInternalApiAndRegisterMcp(),this.notifyBindingReady(),n.info("codewhale-adapter","Ready (exec mode)")}async stop(){this.stopped=!0,this.alive=!1,this.stopComposing(),this.clearIdleTimer(),this.killActiveProcess(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.alive}async createSession(e){const t=this.deepSeekSessionId??`ds-${Date.now()}`;return this.notifyBindingReady(),t}async resumeSession(e,t){}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(e){this.identity.forgetSession(e),this.deepSeekSessionId=null,this.persistSessionId(void 0)}sendPrompt(e){const t=new y(e.adapterSessionId);return this.runMessage(e,t).catch(i=>{t.emitError(i instanceof Error?i:new Error(String(i)))}),t}async cancel(e){this.killActiveProcess()}setPermissionHandler(e){}async ping(e){if(this.stopped||!this.alive)return!1;const t=this.activeProcess;return!(t?.pid&&!N(t.pid))}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.deepSeekSessionId?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){this.clearIdleTimer(),this.killActiveProcess(),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const e=T(P(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url]}}async probe(e){const t=this.getStatus();return D(this.config.command||"codewhale",{alive:t.alive,busy:t.busy,started:this.alive},e)}getUsageSnapshot(){return this.lastUsage}async startInternalApiAndRegisterMcp(){try{this.internalApi=new $,this.internalApi.setInvokeHandler(async(a,p)=>this.callbacks.agentInvoke(a,p)),await this.internalApi.start(0),n.info("codewhale-adapter",`Internal API started at ${this.internalApi.url}`);const e=this.getMcpConfig(),t={...process.env,...this.config.env},i=typeof t.PATH=="string"?t.PATH:void 0,o=w(this.config.command||"codewhale",i);try{I(o,["mcp","remove",e.name],{env:t,timeout:1e4,stdio:"ignore"})}catch{}const s=["mcp","add",e.name,"--command",e.command];for(const a of e.args)s.push("--arg",a);I(o,s,{env:t,timeout:1e4,stdio:"ignore"}),n.info("codewhale-adapter",`Registered MCP server: ${e.name}`);const r=C(M(),".codewhale","skills"),d=R(r);d.length>0&&n.info("codewhale-adapter",`Synced connector skills to ${r}: [${d.join(", ")}]`)}catch(e){n.warn("codewhale-adapter",`Failed to register MCP tools (non-fatal): ${e instanceof Error?e.message:String(e)}`)}}getSupportedCommands(){return[{name:"status",description:"Show session and working directory status"}]}async execCommand(e,t,i){return e==="status"?{status:"ok",message:`Session: ${this.deepSeekSessionId??"none"}, CWD: ${this.cwd}`,data:{sessionId:this.deepSeekSessionId,cwd:this.cwd,alive:this.alive}}:{status:"unsupported",message:`Unknown command: ${e}`}}async handleLocalAction(e){const t=e.action_type??"",i=e.params??{};switch(t){case"get_context":return this.callbacks.sendLocalActionResult(e.action_id,"ok",{sessionId:this.deepSeekSessionId,cwd:this.cwd,model:this.currentModel}),{handled:!0,kind:"get_context"};case"set_model":{const o=String(i.model_id??"").trim();return o?(this.currentModel=o,this.callbacks.sendLocalActionResult(e.action_id,"ok",{outcome:"model_set",modelId:o}),n.info("codewhale-adapter",`Model set to: ${o}`),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"invalid_params","model_id is required"),{handled:!0,kind:"set_model"})}default:return{handled:!1,kind:""}}}deliverInboundEvent(e){const t=e.content;if(this.activeEventId){n.info("codewhale-adapter",`Event ${e.event_id}: rejected, busy with ${this.activeEventId}`),this.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}this.startNewMessage(e,t)}deliverStopEvent(e,t){this.activeEventId===e&&(this.killActiveProcess(),this.callbacks.sendEventResult(e,"canceled","stopped by user"),this.clearActive())}startNewMessage(e,t){this.activeEventId=e.event_id,this.activeSessionId=e.session_id,this.chunkSeq=0,this.activeClientMsgId=`ds-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,this.startComposing();const i={adapterSessionId:this.deepSeekSessionId??"",text:t,contextMessages:e.context_messages_json?JSON.parse(e.context_messages_json).map(s=>({senderId:s.sender_id??"unknown",content:s.content})):void 0},o=new y(this.deepSeekSessionId??"");this.runMessage(i,o,e.event_id,e.session_id).catch(s=>{n.error("codewhale-adapter",`Message failed: ${s}`),this.callbacks.sendEventResult(e.event_id,"failed",s instanceof Error?s.message:String(s)),this.clearActive()}),this.resetIdleTimer(e.event_id)}buildExecArgs(e){const t=["exec","--output-format","stream-json"];return this.currentModel&&t.push("--model",this.currentModel),this.deepSeekSessionId&&t.push("--resume",this.deepSeekSessionId),t.push("--",e),t}async runMessage(e,t,i,o){let s=e.text;e.contextMessages&&e.contextMessages.length>0&&(s=`Conversation context:
2
2
  ${e.contextMessages.map(l=>`[${l.senderId??"unknown"}]: ${l.content}`).join(`
3
3
  `)}
4
4
 
5
5
  Latest user message:
6
- ${s}`);const r=this.config.command||"codewhale",d=this.buildExecArgs(s),a={...process.env,...this.config.env},p=k(r,typeof a.PATH=="string"?a.PATH:void 0);n.info("codewhale-adapter",`Spawning: ${p} ${d.slice(0,5).join(" ")}...`);try{if(!(await _(this.cwd)).isDirectory())throw new Error(`Bound path is not a directory: ${this.cwd}`)}catch(c){throw String(c?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${this.cwd}. Please rebind with /grix open <valid-directory>.`):c}const u=E(p,d,{cwd:this.cwd,env:a}).process;return this.activeProcess=u,u.stderr?.on("data",c=>{const l=c.toString().trim();l&&n.info("codewhale-adapter",`[codewhale stderr] ${l}`)}),new Promise((c,l)=>{let m=!1,g="";const S=()=>{this.activeProcess=null};u.on("error",h=>{m||(m=!0,S(),l(h))}),u.on("exit",h=>{if(g.trim()&&this.handleOutputLine(g.trim(),i),g="",m){S();return}if(m=!0,S(),h!==0&&i&&this.activeEventId===i){l(new Error(`codewhale exec exited with code ${h}`));return}t.emitDone({status:"completed"}),c()}),A({input:u.stdout}).on("line",h=>{h.trim()&&this.handleOutputLine(h.trim(),i)}),u.stdin?.end()})}handleOutputLine(e,t){let i;try{i=JSON.parse(e)}catch{n.error("codewhale-adapter",`Invalid JSON: ${e.slice(0,200)}`);return}switch(i.type){case"content":{const s=i.content;s&&t&&this.activeEventId===t&&this.activeSessionId&&(this.chunkSeq++,this.callbacks.sendStreamChunk(t,this.activeSessionId,s,this.chunkSeq,!1,this.activeClientMsgId??void 0),this.startComposing(),this.resetIdleTimer(t));break}case"session_capture":{const s=i.content;s&&(this.deepSeekSessionId=s,this.persistSessionId(s),n.info("codewhale-adapter",`Session captured: ${s}`));break}case"metadata":{const s=i.meta;if(s){n.info("codewhale-adapter",`Metadata: model=${s.model}, tokens_in=${s.input_tokens}, tokens_out=${s.output_tokens}`);const r=Number(s.input_tokens??0),d=Number(s.output_tokens??0);if(r>0||d>0){const a=this.lastUsage;this.lastUsage={sampledAt:new Date().toISOString(),turns:(a?.turns??0)+1,total:{input:(a?.total.input??0)+r,output:(a?.total.output??0)+d}}}}break}case"tool_use":{const s=i.name,r=typeof i.input=="string"?i.input:JSON.stringify(i.input??{});s&&t&&this.activeEventId===t&&this.activeSessionId&&(n.info("codewhale-adapter",`Tool use: ${s}`),this.callbacks.sendToolUse(t,this.activeSessionId,s,r),this.resetIdleTimer(t));break}case"tool_result":{const s=i.name,r=i.output;t&&this.activeEventId===t&&this.activeSessionId&&(this.callbacks.sendToolResult(t,this.activeSessionId,s??"unknown",r??""),this.resetIdleTimer(t));break}case"done":{this.handleMessageCompleted(t);break}default:break}}handleMessageCompleted(e){if(this.stopComposing(),e&&this.activeEventId===e){const t=this.activeSessionId??"",i=this.activeClientMsgId??void 0;t&&(this.chunkSeq++,this.callbacks.sendStreamChunk(e,t,"",this.chunkSeq,!0,i)),this.callbacks.sendEventResult(e,"responded"),this.clearActive()}}killActiveProcess(){const e=this.activeProcess;this.activeProcess=null,e?.pid&&(w(e,"SIGTERM"),setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null)try{w(e,"SIGKILL")}catch{}},5e3).unref())}notifyBindingReady(){!this.aibotSessionId||!this.cwd||this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd)}persistSessionId(e){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodeWhaleThreadId(this.aibotSessionId,e)}startComposing(){}stopComposing(){}resetIdleTimer(e){this.clearIdleTimer(),this.idleTimer=setTimeout(()=>{this.activeEventId===e&&(n.error("codewhale-adapter",`Agent idle for ${v/1e3}s: ${e}`),this.killActiveProcess(),this.callbacks.sendEventResult(e,"failed",`agent idle for ${v/1e3}s`),this.clearActive(),this.emit("stuck"))},v)}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}clearActive(){const e=this.activeEventId;this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.chunkSeq=0,this.activeClientMsgId=null,this.clearIdleTimer(),e&&this.emit("eventDone",e)}}class y extends b{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){n.warn("codewhale-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{V as CodeWhaleAdapter};
6
+ ${s}`),s=this.identity.injectOnce(e.adapterSessionId,s);const r=this.config.command||"codewhale",d=this.buildExecArgs(s),a={...process.env,...this.config.env},p=w(r,typeof a.PATH=="string"?a.PATH:void 0);n.info("codewhale-adapter",`Spawning: ${p} ${d.slice(0,5).join(" ")}...`);try{if(!(await _(this.cwd)).isDirectory())throw new Error(`Bound path is not a directory: ${this.cwd}`)}catch(c){throw String(c?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${this.cwd}. Please rebind with /grix open <valid-directory>.`):c}const u=A(p,d,{cwd:this.cwd,env:a}).process;return this.activeProcess=u,u.stderr?.on("data",c=>{const l=c.toString().trim();l&&n.info("codewhale-adapter",`[codewhale stderr] ${l}`)}),new Promise((c,l)=>{let m=!1,g="";const S=()=>{this.activeProcess=null};u.on("error",h=>{m||(m=!0,S(),l(h))}),u.on("exit",h=>{if(g.trim()&&this.handleOutputLine(g.trim(),i),g="",m){S();return}if(m=!0,S(),h!==0&&i&&this.activeEventId===i){l(new Error(`codewhale exec exited with code ${h}`));return}t.emitDone({status:"completed"}),c()}),E({input:u.stdout}).on("line",h=>{h.trim()&&this.handleOutputLine(h.trim(),i)}),u.stdin?.end()})}handleOutputLine(e,t){let i;try{i=JSON.parse(e)}catch{n.error("codewhale-adapter",`Invalid JSON: ${e.slice(0,200)}`);return}switch(i.type){case"content":{const s=i.content;s&&t&&this.activeEventId===t&&this.activeSessionId&&(this.chunkSeq++,this.callbacks.sendStreamChunk(t,this.activeSessionId,s,this.chunkSeq,!1,this.activeClientMsgId??void 0),this.startComposing(),this.resetIdleTimer(t));break}case"session_capture":{const s=i.content;s&&(this.deepSeekSessionId=s,this.persistSessionId(s),n.info("codewhale-adapter",`Session captured: ${s}`));break}case"metadata":{const s=i.meta;if(s){n.info("codewhale-adapter",`Metadata: model=${s.model}, tokens_in=${s.input_tokens}, tokens_out=${s.output_tokens}`);const r=Number(s.input_tokens??0),d=Number(s.output_tokens??0);if(r>0||d>0){const a=this.lastUsage;this.lastUsage={sampledAt:new Date().toISOString(),turns:(a?.turns??0)+1,total:{input:(a?.total.input??0)+r,output:(a?.total.output??0)+d}}}}break}case"tool_use":{const s=i.name,r=typeof i.input=="string"?i.input:JSON.stringify(i.input??{});s&&t&&this.activeEventId===t&&this.activeSessionId&&(n.info("codewhale-adapter",`Tool use: ${s}`),this.callbacks.sendToolUse(t,this.activeSessionId,s,r),this.resetIdleTimer(t));break}case"tool_result":{const s=i.name,r=i.output;t&&this.activeEventId===t&&this.activeSessionId&&(this.callbacks.sendToolResult(t,this.activeSessionId,s??"unknown",r??""),this.resetIdleTimer(t));break}case"done":{this.handleMessageCompleted(t);break}default:break}}handleMessageCompleted(e){if(this.stopComposing(),e&&this.activeEventId===e){const t=this.activeSessionId??"",i=this.activeClientMsgId??void 0;t&&(this.chunkSeq++,this.callbacks.sendStreamChunk(e,t,"",this.chunkSeq,!0,i)),this.callbacks.sendEventResult(e,"responded"),this.clearActive()}}killActiveProcess(){const e=this.activeProcess;this.activeProcess=null,e?.pid&&(k(e,"SIGTERM"),setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null)try{k(e,"SIGKILL")}catch{}},5e3).unref())}notifyBindingReady(){!this.aibotSessionId||!this.cwd||this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd)}persistSessionId(e){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodeWhaleThreadId(this.aibotSessionId,e)}startComposing(){}stopComposing(){}resetIdleTimer(e){this.clearIdleTimer(),this.idleTimer=setTimeout(()=>{this.activeEventId===e&&(n.error("codewhale-adapter",`Agent idle for ${v/1e3}s: ${e}`),this.killActiveProcess(),this.callbacks.sendEventResult(e,"failed",`agent idle for ${v/1e3}s`),this.clearActive(),this.emit("stuck"))},v)}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}clearActive(){const e=this.activeEventId;this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.chunkSeq=0,this.activeClientMsgId=null,this.clearIdleTimer(),e&&this.emit("eventDone",e)}}class y extends b{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){n.warn("codewhale-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{Y as CodeWhaleAdapter};