grix-connector 4.0.1 → 4.0.2

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.
@@ -5,7 +5,7 @@ import{fileURLToPath as H}from"node:url";import p from"node:path";import{homedir
5
5
  ${i}
6
6
  [/recovery-context]`)):t}sendPromptWithRetry(e,t){this.acpClient.send(t).then(()=>{this.activeRun===e&&(e.busyWaiting=!1)}).catch(i=>{if(we(i)){this.scheduleAgentBusyRetry(e,t,I(i));return}if(r.error("acp-adapter",`Prompt failed: ${I(i)}`),_e(i)){const n=I(i),o=e.eventId,a=e.sessionId;r.info("acp-adapter",`Internal error escalated to bridge: event=${o} session=${a} err=${n}`),this.silentlyDiscardActiveRun(e),this.emit("internalError",{eventId:o,sessionId:a,errorMsg:n});return}const s=i instanceof Error?i.message:String(i);this.finishRun("failed",s)})}scheduleAgentBusyRetry(e,t,i){if(this.activeRun!==e)return;e.busyWaiting=!0;const s=Date.now();e.busyWaitSince||(e.busyWaitSince=s);const n=s-e.busyWaitSince;if(n>=L){r.error("acp-adapter",`agent busy wait exceeded ${Math.round(L/6e4)}min, giving up event=${e.eventId}: ${i}`),e.buffer+=`
7
7
 
8
- The agent's own background turn kept the session busy past the wait limit, so this message was not delivered. Please resend it later.`,this.finishRun("failed");return}const o=Math.min(Ce*2**e.busyRetryCount,Ae);e.busyRetryCount+=1,r.info("acp-adapter",`agent busy, keeping event queued event=${e.eventId} waited=${Math.round(n/1e3)}s retry_in=${o}ms`),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),e.lastProgressAt=s,e.busyRetryTimer=setTimeout(()=>{if(e.busyRetryTimer=null,this.activeRun===e){if(!this.acpClient?.isAlive){this.finishRun("failed","agent unreachable");return}this.resetIdleTimer(e),this.sendPromptWithRetry(e,t)}},o),e.busyRetryTimer.unref?.()}silentlyDiscardActiveRun(e){this.activeRun===e&&(this.snapshotAuditBoundary(e.eventId),this.activeRun=null,this.acpClient?.clearSettleTimer(),this.clearRunTimers(e),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.reasonixAuditTranscriptPath&&!e.includes("--transcript")&&(await g.mkdir(p.dirname(this.reasonixAuditTranscriptPath),{recursive:!0,mode:448}),await(await g.open(this.reasonixAuditTranscriptPath,"a",384)).close(),await g.chmod(this.reasonixAuditTranscriptPath,384),e.push("--transcript",this.reasonixAuditTranscriptPath)),this.agentProcess=new G(this.bridgeLog),this.baselineGroupPids=[];const i=this.resolveCwd();try{if(!(await g.stat(i)).isDirectory())throw new Error(`Bound path is not a directory: ${i}`)}catch(s){throw String(s?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${i}. Please rebind with /grix open <valid-directory>.`):s}await Y(this.config.command,i,this.config.env,this.nativeProviderScope).catch(s=>{r.warn("acp-adapter",`native provider config skipped: ${s instanceof Error?s.message:String(s)}`)}),r.info("acp-adapter",`Spawning ACP agent command=${this.config.command} args=${JSON.stringify(e)} cwd=${i} envKeys=${JSON.stringify(Object.keys(this.config.env??{}).sort())}`);try{if(this.isKimi){const s=ee({...process.env,...this.config.env});r.info("acp-adapter",`Prepared Kimi Layer plugin at ${s.pluginRoot} (registrationChanged=${s.registrationChanged})`)}await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:i,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 t;this.acpMcpTools&&(t=await this.startInternalApiAndMcp(e||this.resolveCwd())),this.acpClient=new te,this.acpClient.on("event",l=>this.handleAcpEvent(l)),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 i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=!!i&&i!=="__continue__",n=async l=>{await this.acpClient.connect({transport:this.agentProcess.transport,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:this.isKimi,cwd:e||this.resolveCwd(),mcpServers:t,sessionId:l})};let o=!1,a;try{await n(i)}catch(l){if(l instanceof ie){await this.handleAuthRequired(l);return}if(!i)throw this.handleConnectFailure(l),l;if(r.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${I(l)}`),o=!0,a=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${i}`);const d=await this.buildKiroRecoveryContext(this.currentAibotSessionId,i,e||this.resolveCwd());d?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,d),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${d.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${i}`)}await n(void 0)}this.sessionConnected=!0,this.sessionContextPending=!s||o,this.captureBaselineGroupPids(),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[l,d]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(l,this.acpClient.sessionId);const h=o?"binding_recreated":"binding_ready",u=this.buildToolbarContext(h,d);o&&a&&(u.previous_session_id=a,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(l,"ready",d,u)}}async buildKiroRecoveryContext(e,t,i){try{const s=p.join(v(),".kiro","sessions","cli",`${t}.json`);await g.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,i);if(!n)return;const o=p.join(v(),".grix","data","session-summaries");await g.mkdir(o,{recursive:!0});const a=p.join(o,`${e}.md`),l=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
8
+ The agent's own background turn kept the session busy past the wait limit, so this message was not delivered. Please resend it later.`,this.finishRun("failed");return}const o=Math.min(Ce*2**e.busyRetryCount,Ae);e.busyRetryCount+=1,r.info("acp-adapter",`agent busy, keeping event queued event=${e.eventId} waited=${Math.round(n/1e3)}s retry_in=${o}ms`),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),e.lastProgressAt=s,e.busyRetryTimer=setTimeout(()=>{if(e.busyRetryTimer=null,this.activeRun===e){if(!this.acpClient?.isAlive){this.finishRun("failed","agent unreachable");return}this.resetIdleTimer(e),this.sendPromptWithRetry(e,t)}},o),e.busyRetryTimer.unref?.()}silentlyDiscardActiveRun(e){this.activeRun===e&&(this.snapshotAuditBoundary(e.eventId),this.activeRun=null,this.acpClient?.clearSettleTimer(),this.clearRunTimers(e),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.reasonixAuditTranscriptPath&&!e.includes("--transcript")&&(await g.mkdir(p.dirname(this.reasonixAuditTranscriptPath),{recursive:!0,mode:448}),await(await g.open(this.reasonixAuditTranscriptPath,"a",384)).close(),await g.chmod(this.reasonixAuditTranscriptPath,384),e.push("--transcript",this.reasonixAuditTranscriptPath)),this.agentProcess=new G(this.bridgeLog),this.baselineGroupPids=[];const i=this.resolveCwd();try{if(!(await g.stat(i)).isDirectory())throw new Error(`Bound path is not a directory: ${i}`)}catch(s){throw String(s?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${i}. Please rebind with /grix open <valid-directory>.`):s}await Y(this.config.command,i,this.config.env,this.nativeProviderScope).catch(s=>{r.warn("acp-adapter",`native provider config skipped: ${s instanceof Error?s.message:String(s)}`)}),r.info("acp-adapter",`Spawning ACP agent command=${this.config.command} args=${JSON.stringify(e)} cwd=${i} envKeys=${JSON.stringify(Object.keys(this.config.env??{}).sort())}`);try{if(this.isKimi){const s=ee({...process.env,...this.config.env});r.info("acp-adapter",`Prepared Kimi Layer plugin at ${s.pluginRoot} (registrationChanged=${s.registrationChanged})`)}await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:i,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 t;this.acpMcpTools&&(t=await this.startInternalApiAndMcp(e||this.resolveCwd())),this.acpClient=new te,this.acpClient.on("event",l=>this.handleAcpEvent(l)),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 i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=!!i&&i!=="__continue__",n=async l=>{await this.acpClient.connect({transport:this.agentProcess.transport,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:!0,cwd:e||this.resolveCwd(),mcpServers:t,sessionId:l})};let o=!1,a;try{await n(i)}catch(l){if(l instanceof ie){await this.handleAuthRequired(l);return}if(!i)throw this.handleConnectFailure(l),l;if(r.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${I(l)}`),o=!0,a=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${i}`);const d=await this.buildKiroRecoveryContext(this.currentAibotSessionId,i,e||this.resolveCwd());d?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,d),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${d.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${i}`)}await n(void 0)}this.sessionConnected=!0,this.sessionContextPending=!s||o,this.captureBaselineGroupPids(),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[l,d]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(l,this.acpClient.sessionId);const h=o?"binding_recreated":"binding_ready",u=this.buildToolbarContext(h,d);o&&a&&(u.previous_session_id=a,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(l,"ready",d,u)}}async buildKiroRecoveryContext(e,t,i){try{const s=p.join(v(),".kiro","sessions","cli",`${t}.json`);await g.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,i);if(!n)return;const o=p.join(v(),".grix","data","session-summaries");await g.mkdir(o,{recursive:!0});const a=p.join(o,`${e}.md`),l=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
9
9
  `);return await g.writeFile(a,l,"utf8"),r.info("acp-adapter",`Recovery summary saved: ${a} (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: ${a}`,"",n.trim()].join(`
10
10
  `)}catch(s){r.warn("acp-adapter",`Failed to build kiro recovery context: ${I(s)}`);return}}async generateSummaryByKiroCli(e,t){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(`
11
11
  `)],n=await this.runCommandCapture("kiro-cli",s,t,45e3);if(!n)return;const o=Re(n).trim(),a=o.indexOf("> "),l=a>=0?o.slice(a+2).trim():o;if(l)return r.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${o.length}, body_chars=${l.length})`),l.split(`
@@ -13,7 +13,7 @@ The agent's own background turn kept the session busy past the wait limit, so th
13
13
  `).trim()}runCommandCapture(e,t,i,s){return new Promise((n,o)=>{const a=E(e,t,{cwd:i,stdio:["ignore","pipe","pipe"],windowsHide:!0});let l="",d="";const h=setTimeout(()=>{a.kill("SIGTERM"),o(new Error(`${e} timed out after ${s}ms`))},s);a.stdout.on("data",u=>{l+=String(u)}),a.stderr.on("data",u=>{d+=String(u)}),a.on("error",u=>{clearTimeout(h),o(u)}),a.on("close",u=>{if(clearTimeout(h),u===0){n(l||d);return}o(new Error(`${e} exited with code ${u}: ${d||l}`))})})}handleConnectFailure(e){r.error("acp-adapter",`ACP session creation failed: ${I(e)}`),this.acpClient?.dispose(),this.acpClient?.removeAllListeners(),this.acpClient=null,this.emit("exit",null)}async startInternalApiAndMcp(e=this.resolveCwd()){try{this.internalApi||(this.internalApi=new ne,this.internalApi.setInvokeHandler(async(a,l,d,h)=>this.callbacks.agentInvoke(a,l,h)),await this.internalApi.start(0),r.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(a=>this.onMcpBridgeUp(a));const t=this.config.command??"",i=t.includes("qwen")?p.join(v(),".qwen","skills"):t.includes("gemini")?p.join(v(),".gemini","skills"):t.includes("kiro")?p.join(v(),".kiro","skills"):this.isReasonix?p.join(v(),".reasonix","skills"):this.isKimi?p.join(J({...process.env,...this.config.env}),"skills"):this.auditAgentType==="copilot"||/^copilot(?:\.cmd)?$/i.test(p.basename(t).toLowerCase())||/^gh(?:\.exe)?$/i.test(p.basename(t).toLowerCase())&&(this.config.args??[]).includes("copilot")?p.join(v(),".copilot","skills"):this.auditAgentType==="hermes"?p.join(this.config.env?.HERMES_HOME?.trim()||Z({...process.env,...this.config.env},this.nativeProviderScope),"skills"):void 0;if(i){const a=X(i);a.length>0&&r.info("acp-adapter",`Synced connector skills to ${i}: [${a.join(", ")}]`)}const s=p.resolve(w,"../../mcp/mcp-bridge-server.js"),n=this.internalApi.mcpBridgeWsUrl,o=p.resolve(w,"../../mcp/acp-mcp-server.js");if(this.isKimi){await this.internalApi.setCodebaseNavigatorCwd(e);const a={Authorization:`Bearer ${this.internalApi.token}`};return[{type:"http",name:"grix-app-bridge",url:this.internalApi.mcpBridgeHttpUrl,headers:a},{type:"http",name:"grix-connector-tools",url:this.internalApi.mcpToolsHttpUrl,headers:a},{type:"http",name:"codebase-navigator",url:this.internalApi.codebaseNavigatorHttpUrl,headers:a}]}return[{name:"grix-app-bridge",command:process.execPath,args:[s,"--ws-url",n],...!this.isReasonix&&{env:{GRIX_CONNECTOR_MCP_BRIDGE_WS:n}}},{name:"grix-connector-tools",command:process.execPath,args:[o,"--api-url",this.internalApi.url],...!this.isReasonix&&{env:{}}}]}catch(t){r.error("acp-adapter",`Failed to start MCP tools: ${t}`);return}}onMcpBridgeUp(e){this.callbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}cleanup(){this.flushOrphanBuffer(),this.activeRun,this.pendingApprovals.clear(),this.pendingQuestions.clear(),this.pendingAutoCompact=!1,this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.dispose(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null),this.baselineGroupPids=[],this.terminalAuthChild&&(this.terminalAuthChild.kill("SIGKILL"),this.terminalAuthChild=null)}async handleAuthRequired(e){r.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(s=>s.id).join(", ")}`);const t=e.authMethods.find(s=>/oauth|browser/i.test(s.id))??e.authMethods[0];if(!t)throw e;const i=this.currentAibotSessionId??"";this.callbacks.sendAuthNotification(i,`Authentication required (${t.id}). Initiating auth flow...`);for(const[s,n]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(s,"failed",n);if(t.type==="terminal")await this.runTerminalAuth(t,i);else{const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(i,`Please open this URL to authenticate:
14
14
  ${s}
15
15
 
16
- Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(t.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(i,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:this.isKimi,cwd:this.resolveCwd(),mcpServers:s}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.captureBaselineGroupPids(),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[n,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(n,"ready",o,this.buildToolbarContext("binding_ready",o))}catch(s){throw r.error("acp-adapter",`Auth retry failed: ${s}`),s}}runTerminalAuth(e,t){return new Promise((i,s)=>{const n=E(this.config.command,e.args??[],{env:{...process.env,...this.config.env??{}},stdio:["ignore","pipe","pipe"],windowsHide:!0});this.terminalAuthChild=n;const o=/https?:\/\/[^\s"')\]]+/;let a=!1,l=!1;const d=u=>{if(a)return;const y=u.toString().replace(/\x1b\[[0-9;]*m/g,"").match(o);y&&(a=!0,r.info("acp-adapter",`Terminal auth URL captured: ${y[0]}`),this.callbacks.sendAuthNotification(t,`Please open this URL on any device to authenticate:
16
+ Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(t.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(i,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:!0,cwd:this.resolveCwd(),mcpServers:s}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.captureBaselineGroupPids(),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[n,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(n,"ready",o,this.buildToolbarContext("binding_ready",o))}catch(s){throw r.error("acp-adapter",`Auth retry failed: ${s}`),s}}runTerminalAuth(e,t){return new Promise((i,s)=>{const n=E(this.config.command,e.args??[],{env:{...process.env,...this.config.env??{}},stdio:["ignore","pipe","pipe"],windowsHide:!0});this.terminalAuthChild=n;const o=/https?:\/\/[^\s"')\]]+/;let a=!1,l=!1;const d=u=>{if(a)return;const y=u.toString().replace(/\x1b\[[0-9;]*m/g,"").match(o);y&&(a=!0,r.info("acp-adapter",`Terminal auth URL captured: ${y[0]}`),this.callbacks.sendAuthNotification(t,`Please open this URL on any device to authenticate:
17
17
  ${y[0]}
18
18
 
19
19
  Waiting for authentication to complete...`))};n.stdout?.on("data",d),n.stderr?.on("data",d);const h=setTimeout(()=>{l||(l=!0,n.kill("SIGKILL"),this.terminalAuthChild=null,s(new Error(`terminal auth timed out after ${j}ms`)))},j);n.on("error",u=>{l||(l=!0,clearTimeout(h),this.terminalAuthChild=null,s(u))}),n.on("exit",u=>{l||(l=!0,clearTimeout(h),this.terminalAuthChild=null,u===0?i():s(new Error(`terminal auth process exited with code ${u}`)))})})}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const t=/https?:\/\/[^\s"')\]]+/;let i=!1;const s=n=>{if(i)return;const a=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(t);a&&(i=!0,this.agentProcess.removeListener("stderr",s),e(a[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{i||(i=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){try{r.info("acp-adapter","incoming acp event",{eventType:e.type,sessionId:this.activeRun?.sessionId??null,eventKeys:Object.keys(e),rawEvent:x(e,2e3)})}catch{}if(e.type===f.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===f.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting?this.finishCompaction("context-window-update"):e.contextWindow&&this.maybeScheduleAutoCompact(e.contextWindow);return}const t=this.activeRun;if(!t){this.handleOrphanAcpEvent(e);return}if(t.busyWaiting){this.handleOrphanAcpEvent(e);return}this.captureAuditEvent(e),t.responded||(t.responded=!0);let i=!1;switch(e.type){case f.Text:{if(t.lastToolUse=null,t.lastToolResult=null,e.content){i=!0;const s=t.quotedStream.consume(e.content);s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(t,s.deltaContent)}break}case f.ToolUse:{if(i=!0,t.awaitingToolResult=!0,t.lastToolResult=null,e.toolName){if(e.toolCallId&&(t.toolNamesById.set(e.toolCallId,e.toolName),t.sentToolUseIds.has(e.toolCallId)))break;const s=e.toolInput??"",n=S(s);if(t.lastToolUse?.toolName===e.toolName&&S(t.lastToolUse.toolInput)===n)break;t.lastToolUse={toolName:e.toolName,toolInput:s},e.toolCallId&&t.sentToolUseIds.add(e.toolCallId);const o=this.trimToolWirePayload?M(s):s;this.emitRawEventEnvelope(t,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:o}})||this.callbacks.sendToolUse(t.eventId,t.sessionId,e.toolName,o)}break}case f.ToolResult:{t.awaitingToolResult=!1;{const s=e.toolCallId||e.toolName||"";t.lastToolProgress.delete(s),t.lastToolProgressSentAt.delete(s)}if(t.lastToolUse=null,e.content){i=!0;const s=N(t,e),n=e.content,o=S(n);if(t.lastToolResult?.toolName===s&&S(t.lastToolResult.content)===o)break;if(t.lastToolResult={toolName:s,content:n},!this.trimToolWirePayload||e.toolStatus==="failed"){const a=this.trimToolWirePayload?"(failed)":n;this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:s,content:a}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,s,a)}}break}case f.ToolProgress:{if(i=!0,e.content){const s=e.toolCallId||e.toolName||"";if(t.lastToolProgress.get(s)===e.content||(t.lastToolProgress.set(s,e.content),this.trimToolWirePayload))break;const n=Date.now(),o=t.lastToolProgressSentAt.get(s);if(o!==void 0&&n-o<ce)break;t.lastToolProgressSentAt.set(s,n);const a=N(t,e);this.emitRawEventEnvelope(t,{type:"tool_progress",payload:{tool_name:a,content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,a,e.content)}break}case f.Thinking:{t.lastToolUse=null,t.lastToolResult=null,e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(t.eventId,t.sessionId,e.content));break}case f.Error:{t.lastToolUse=null,t.lastToolResult=null;const s=String(e.error??"unknown error");this.emitRawEventEnvelope(t,{type:"error",payload:{message:s}}),r.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(t.eventId,t.sessionId,s);break}case f.Result:{if(e.synthetic&&this.shouldDeferSyntheticResultForDetachedKimiTask(t,e)){r.info("acp-adapter",`Synthetic ACP Result deferred: kimi detached task still running event=${t.eventId} session=${t.sessionId}`),this.acpClient?.deferSettledResult();return}this.emitRawEventEnvelope(t,{type:"result",payload:{done:e.done??!0}});const s=t.quotedStream.flush();s.deltaContent&&this.appendToStream(t,s.deltaContent),s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),this.flushStream(),this.finishRun("responded");break}}i&&(t.lastProgressAt=Date.now(),t.idleNoProgressCount=0),this.resetIdleTimer(t)}handlePermissionRequest(e){const t=e.permissionRequest;if(r.info("acp-adapter","permission_request received",{sessionId:this.activeRun?.sessionId,eventId:this.activeRun?.eventId,requestId:e.requestId??null,toolCallId:t?.toolCallId??null,toolName:t?.toolName??null,toolTitle:t?.toolTitle??null,optionsCount:Array.isArray(t?.options)?t.options.length:0,hasActiveRun:this.activeRun!==null,approvalMode:this.approvalMode,rawTransport:this.rawTransport,hasRawEventEnvelopeCb:typeof this.callbacks.sendRawEventEnvelope=="function",hasSendPermissionCardCb:typeof this.callbacks.sendPermissionCard=="function",rawPr:x(t,3e3)}),!t||!e.requestId||!this.acpClient){r.warn("acp-adapter","permission_request dropped: missing prerequisite",{hasPr:t!=null,hasRequestId:!!e.requestId,hasAcpClient:!!this.acpClient});return}const i=this.activeRun,s=ue(t);if(s&&i&&this.callbacks.sendAgentQuestionCard){i.idleTimer&&(clearTimeout(i.idleTimer),i.idleTimer=null),this.pendingQuestions.set(e.requestId,{options:[...t.options]}),this.callbacks.sendAgentQuestionCard(i.eventId,i.sessionId,{request_id:e.requestId,mode:"form",questions:[{header:s.prompt,prompt:s.prompt,options:s.answerOptions.map(a=>a.name)}]}),r.info("acp-adapter","AskUserQuestion forwarded via agent_question card",{sessionId:i.sessionId,eventId:i.eventId,requestId:e.requestId,optionsCount:s.answerOptions.length});return}if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){r.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${t.toolName}`),this.acpClient.respondPermission(t.requestId,{behavior:"allow"}).catch(()=>{});return}const n=t.toolCallId,o=de(t.rawParams,e.toolInput,t.toolTitle);this.pendingApprovals.set(n,e.requestId),i?(i.idleTimer&&(clearTimeout(i.idleTimer),i.idleTimer=null),this.emitRawEventEnvelope(i,{type:"permission_request",payload:{request_id:t.requestId,tool_call_id:n,tool_name:t.toolName,tool_title:t.toolTitle,...o?{tool_input:o}:{},options:t.options}})?r.info("acp-adapter","permission_request forwarded via rawEventEnvelope",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}):(r.info("acp-adapter","permission_request falling back to sendPermissionCard",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}),this.callbacks.sendPermissionCard({eventId:i.eventId,sessionId:i.sessionId,toolCallId:n,toolName:t.toolName,toolTitle:t.toolTitle,toolInput:o,options:t.options}))):(r.info("acp-adapter",`Permission request without active run, auto-approving: ${t.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(n))}emitRawEventEnvelope(e,t){return!e||!this.rawTransport||!this.callbacks.sendRawEventEnvelope?!1:(this.callbacks.sendRawEventEnvelope(e.eventId,e.sessionId,{type:t.type,payload:t.payload,seq:++this.rawEventSeq,at:new Date().toISOString()}),!0)}resetIdleTimer(e,t){e.idleTimer&&clearTimeout(e.idleTimer);const i=t??(e.awaitingToolResult?ye:Ie),s=e.lastProgressAt+T-Date.now(),n=Math.max(0,Math.min(i,s));e.idleTimer=setTimeout(()=>this.onIdleTimeout(e),n)}onIdleTimeout(e){if(this.activeRun?.eventId!==e.eventId)return;const t=Date.now()-e.lastProgressAt;if(t>=T){r.error("acp-adapter",`Turn inactive ${Math.round(t/1e3)}s \u2265 cap ${Math.round(T/1e3)}s, forcing timeout: ${e.eventId}`),this.finishRun("failed",`turn inactivity timeout after ${Math.round(t/1e3)}s with no ACP activity`),this.declareStuck();return}const i=this.acpClient;i?.isAlive?i.ping(5e3).then(async s=>{if(this.activeRun?.eventId!==e.eventId)return;if(!s){await this.handlePingFailure(e,i);return}if(e.consecutivePingFailures=0,e.awaitingToolResult){const d=e.lastIdleCheckAt,h=await this.checkSessionFileActivity(i.sessionId,d);if(this.activeRun?.eventId!==e.eventId)return;e.lastIdleCheckAt=Date.now(),h?(e.lastProgressAt=Date.now(),r.info("acp-adapter",`Idle timer: tool running, ping ok, session file active, refreshing watchdog: ${e.eventId}`)):r.info("acp-adapter",`Idle timer: tool running, ping ok, continuing until inactivity cap: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e);return}const n=e.lastIdleCheckAt,o=e.lastProgressAt>n,a=o?!1:await this.checkSessionFileActivity(i.sessionId,n);if(this.activeRun?.eventId!==e.eventId)return;const l=o||a;e.lastIdleCheckAt=Date.now(),l?(a&&(r.info("acp-adapter",`Idle timer: no stream progress but session file active, continuing: ${e.eventId}`),e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,r.info("acp-adapter",`Idle timer: ping ok, no stream progress (cycle=${e.idleNoProgressCount}), extending until inactivity cap (${Math.round(T/6e4)}min): ${e.eventId}`),this.resetIdleTimer(e))}).catch(()=>{this.activeRun?.eventId===e.eventId&&this.handlePingFailure(e,i)}):(r.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())}async handlePingFailure(e,t){const i=e.lastIdleCheckAt,s=e.lastProgressAt>i,n=s?!1:await this.checkSessionFileActivity(t.sessionId,i);if(this.activeRun?.eventId===e.eventId){if(s||n||e.awaitingToolResult){const o=s?"stream progress":n?"session file activity":"tool in flight";r.info("acp-adapter",`Idle timer: ping failed but ${o}, continuing: ${e.eventId}`),e.consecutivePingFailures=0,e.lastIdleCheckAt=Date.now(),n&&(e.lastProgressAt=Date.now()),this.resetIdleTimer(e);return}if(e.consecutivePingFailures++,e.lastIdleCheckAt=Date.now(),e.consecutivePingFailures<D){r.warn("acp-adapter",`Idle timer: ping failed (${e.consecutivePingFailures}/${D}), rechecking in ${F/1e3}s: ${e.eventId}`),this.resetIdleTimer(e,F);return}r.error("acp-adapter",`Ping failed ${e.consecutivePingFailures}x with no activity evidence, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck()}}async checkSessionFileActivity(e,t){if(!e)return!1;try{return this.isKimi?await pe(e,t):this.config.command==="kiro-cli"?await he(e,t):!1}catch{return!1}}shouldDeferSyntheticResultForDetachedKimiTask(e,t){if(!this.isKimi)return!1;try{return me(t.sessionId||this.acpClient?.sessionId||e.sessionId)}catch{return!1}}declareStuck(){this.acpClient&&(this.acpClient.dispose(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.close().catch(()=>{}),this.agentProcess=null),this.emit("stuck")}appendToStream(e,t){e.buffer+=t,e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushStream(),ve))}emitSegmentedStream(e,t){if(!t)return;const i=e.markdownSegmenter.push(t);for(const s of i)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"}`)))}handleOrphanAcpEvent(e){if(!(!this.sessionConnected||this.compacting||Date.now()<this.orphanSuppressedUntil)){if(e.type===f.Text&&e.content){this.orphanSessionId===null&&(this.orphanSessionId=this.currentAibotSessionId??"");const t=this.orphanQuotedStream.consume(e.content);t.deltaContent&&(this.orphanBuffer+=t.deltaContent),this.orphanBuffer.length>=Te?this.flushOrphanBuffer():this.scheduleOrphanFlush();return}e.type===f.Result&&this.flushOrphanBuffer()}}scheduleOrphanFlush(){this.orphanFlushTimer&&clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=setTimeout(()=>{this.orphanFlushTimer=null,this.flushOrphanBuffer()},Se),this.orphanFlushTimer.unref?.()}flushOrphanBuffer(){if(this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanSessionId===null&&!this.orphanBuffer)return;const e=this.orphanQuotedStream.flush();e.deltaContent&&(this.orphanBuffer+=e.deltaContent),this.orphanQuotedStream=new C;const t=this.orphanSessionId||this.currentAibotSessionId;this.orphanSessionId=null;const i=this.orphanBuffer.trim();if(this.orphanBuffer="",!!i){if(!t||!this.callbacks.sendAgentMessage){r.warn("acp-adapter",`orphan agent output dropped (${t?"no sendAgentMessage callback":"no session id"}) chars=${i.length}`);return}r.info("acp-adapter",`delivering orphan agent output session=${t} chars=${i.length}`);for(let s=0;s<i.length;s+=K)this.callbacks.sendAgentMessage(t,i.slice(s,s+K))}}suppressOrphanOutput(e){this.orphanSuppressedUntil=Date.now()+e,this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanBuffer="",this.orphanQuotedStream=new C,this.orphanSessionId=null}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const t=e.buffer;e.buffer="",this.emitSegmentedStream(e,t)}finishRun(e,t){const i=this.activeRun;if(!i)return;this.snapshotAuditBoundary(i.eventId),this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",i.eventId),this.clearRunTimers(i);const s=i.quotedStream.flush();s.deltaContent&&(i.buffer+=s.deltaContent),s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),t&&(e==="failed"?(r.error("acp-adapter",`finishRun failed: ${t} event=${i.eventId}`),i.buffer+=`
@@ -1,18 +1,18 @@
1
- import{spawn as x}from"node:child_process";import{randomUUID as O}from"node:crypto";import{EventEmitter as C}from"node:events";import{existsSync as w,readFileSync as y,readdirSync as R,statSync as I,mkdirSync as M,writeFileSync as G,realpathSync as j}from"node:fs";import{homedir as P}from"node:os";import{join as f,dirname as B,resolve as D}from"node:path";import{fileURLToPath as N}from"node:url";import{log as c}from"../../core/log/index.js";import{killProcessGroup as S}from"../../core/runtime/spawn.js";import{InternalApiServer as W}from"../../core/mcp/internal-api-server.js";import{resolveMcpServerName as K,pruneLegacyMcpServerEntries as F}from"../../core/mcp/server-name.js";import{syncDefaultSkillsToDir as Q}from"../../default-skills/index.js";import{buildSimpleProbeReport as V}from"../shared/probe-util.js";import{getAgyModelCatalog as Y,resolveAgyModelFlag as q}from"./model-list.js";const d="agy-adapter";function H(p){try{return process.kill(p,0),!0}catch(t){return t.code==="EPERM"}}function U(p){try{return j(p)}catch{return p}}const _=f(P(),".gemini","antigravity-cli","log"),z="CLI store manager shutting down",J=5e3,T=15e3,X="30m",$=f(P(),".gemini","antigravity-cli","cache","last_conversations.json"),Z=["\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\u3001agent_id\uFF08\u5F53\u524D Agent\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(""),tt=new Set(["send_msg","grix_reply"]),et=new Set(["send_msg","grix_reply","delete_msg","session_send","grix_complete","grix_composing","grix_event_ack"]);function L(p){try{return structuredClone(p)}catch{return}}class vt extends C{type="agy";config;callbacks;runtimeResolver;alive=!1;stopped=!1;internalApi=null;activeEventId=null;activeEventSessionId=null;activeProcess=null;completedEventIds=new Set;activeEventSentViaTool=!1;auditCaptures=new Map;completedAuditBoundaries=new Map;watchdogPollTimer=null;watchdogGraceTimer=null;watchdogKilledActiveProcess=!1;watchdogLogPath=null;watchdogLogOffset=0;sessionConversationMap=new Map;conversationOutputCache=new Map;eventQueue=[];systemInstructionInjectedSessions=new Set;identityInjectedSessions=new Set;constructor(t,e,i){super(),this.config=t,this.callbacks=e,this.runtimeResolver=i}async start(){this.alive||(this.alive=!0,this.stopped=!1,await this.startInternalApiAndInjectMcp(),c.info(d,"AgyAdapter started"))}async startInternalApiAndInjectMcp(){try{this.internalApi=new W,this.internalApi.setInvokeHandler(async(n,a,h,u)=>this.handleToolInvoke(n,a,u)),await this.internalApi.start(0),c.info(d,`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=f(P(),".gemini","config","mcp_config.json");M(B(e),{recursive:!0});let i={};try{w(e)&&(i=JSON.parse(y(e,"utf8")))}catch{}const s=i.mcpServers&&typeof i.mcpServers=="object"?i.mcpServers:{};F(s,this.internalApi.url),s[t.name]={command:t.command,args:t.args},i.mcpServers=s,G(e,`${JSON.stringify(i,null,2)}
2
- `,"utf8"),c.info(d,`MCP config injected into ${e}`);const r=f(P(),".gemini","skills"),o=Q(r);o.length>0&&c.info(d,`Synced connector skills to ${r}: [${o.join(", ")}]`)}catch(t){c.warn(d,`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async handleToolInvoke(t,e,i){let s=e;et.has(t)&&this.activeEventSessionId&&(s.session_id==null||s.session_id==="")&&(s={...s,session_id:this.activeEventSessionId});const r=this.activeEventId?this.auditCaptures.get(this.activeEventId):void 0,o=r?{sequence:r.toolInvocations.length,observedAt:new Date().toISOString(),action:t,params:L(s)}:void 0;r&&o&&r.toolInvocations.push(o);try{const n=await this.callbacks.agentInvoke(t,s,i);return o&&(o.completedAt=new Date().toISOString(),o.result=L(n)),tt.has(t)&&this.activeEventId&&(this.activeEventSentViaTool=!0),n}catch(n){throw o&&(o.completedAt=new Date().toISOString(),o.error=n instanceof Error?n.message:String(n)),n}}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.stopTurnWatchdog(),this.watchdogKilledActiveProcess=!1,this.activeProcess){try{S(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&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null,this.activeEventSessionId=null,this.internalApi){try{await this.internalApi.stop()}catch{}this.internalApi=null}this.emit("exit",0),c.info(d,"AgyAdapter stopped")}}isAlive(){return this.alive}async createSession(t){return t.cwd??O()}async resumeSession(t,e){}async destroySession(t){this.systemInstructionInjectedSessions.delete(t),this.identityInjectedSessions.delete(t)}onAgentProfileChanged(){this.identityInjectedSessions.clear()}sendPrompt(t){const e=new C;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;c.info(d,`cancelCurrentRun: killing process group for event ${t}`);try{S(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)){c.debug(d,`Skipping duplicate event: ${t.event_id}`);return}if(this.callbacks.sendEventAck(t.event_id,t.session_id),this.activeEventId){c.info(d,`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){c.info(d,`Stopping event ${t}`);try{S(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActiveEvent(t),this.drainQueue()}}setPermissionHandler(){}async ping(t){if(this.stopped||!this.alive||!this.internalApi)return!1;const e=this.activeProcess;if(!e)return!0;if(!e.pid||!H(e.pid))return!1;const i=!!(e.stdout&&!e.stdout.destroyed),s=!!(e.stderr&&!e.stderr.destroyed);return i&&s}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.sessionConversationMap.size}}async probe(t){const e=this.getStatus();return V(this.config.command||"agy",{alive:e.alive,busy:e.busy,started:e.alive},t)}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}snapshotAuditBoundary(t){const e=this.auditCaptures.get(t);if(!e)return;this.auditCaptures.delete(t);let i;if(e.logPath)try{i=I(e.logPath).size}catch{}const s=this.sessionConversationMap.get(e.sessionId)??e.conversationId;this.completedAuditBoundaries.set(t,{adapterType:"agy",...s?{providerSessionId:s}:{},...e.cwd?{cwd:e.cwd}:{},...e.logPath?{transcriptPath:e.logPath,transcriptStartOffset:e.logStartOffset??0,transcriptEndOffset:i??0}:{},agyEvidence:{...e.prompt===void 0?{}:{prompt:e.prompt},...e.stdout===void 0?{}:{stdout:e.stdout},...e.stderr===void 0?{}:{stderr:e.stderr},...e.exitCode===void 0?{}:{exitCode:e.exitCode},...e.response===void 0?{}:{response:e.response},toolInvocations:e.toolInvocations}})}clearActiveEventForShutdown(){if(this.activeProcess)try{S(this.activeProcess,"SIGKILL")}catch{}this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.stopTurnWatchdog(),this.watchdogKilledActiveProcess=!1,this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1}getMcpConfig(){if(!this.internalApi)return null;const t=D(N(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:K(this.callbacks.getAgentId?.()),command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}processEvent(t){const{event_id:e,session_id:i}=t,s=this.runtimeResolver(i);if(!s.cwd){c.warn(d,`No working directory for session ${i}, event should have been intercepted by bridge`),this.callbacks.forceCompleteInternalEvent(e,i);return}this.activeEventId=e,this.activeEventSessionId=i,this.activeEventSentViaTool=!1,this.emit("eventStarted",e,i),c.info(d,`Processing event ${e} for session ${i}`);const r=this.buildAgyPrompt(t);t.audit?.enabled===!0&&this.auditCaptures.set(e,{sessionId:i,...s.cwd?{cwd:s.cwd}:{},...s.conversationId?{conversationId:s.conversationId}:{},prompt:r,toolInvocations:[]}),this.spawnAgyPrint(e,i,r,s)}buildAgyPrompt(t){const i=[["chat_id",t.session_id],["agent_id",this.callbacks.getAgentId?.()],["event_id",t.event_id],["message_id",t.msg_id],["user_id",t.sender_id],["quoted_message_id",t.quoted_message_id]].filter(([,a])=>a!=null&&a!=="").map(([a,h])=>`${a}="${h}"`).join(" "),s=this.renderContextBlock(t),r=s?`${t.content}
1
+ import{spawn as R}from"node:child_process";import{randomUUID as P}from"node:crypto";import{EventEmitter as T}from"node:events";import{existsSync as A,readFileSync as I,statSync as G,mkdirSync as $,writeFileSync as M,realpathSync as L}from"node:fs";import{homedir as _}from"node:os";import{join as m,dirname as O,resolve as j}from"node:path";import{fileURLToPath as D}from"node:url";import{log as a}from"../../core/log/index.js";import{killProcessGroup as y}from"../../core/runtime/spawn.js";import{InternalApiServer as B}from"../../core/mcp/internal-api-server.js";import{resolveMcpServerName as N,pruneLegacyMcpServerEntries as K}from"../../core/mcp/server-name.js";import{syncDefaultSkillsToDir as W}from"../../default-skills/index.js";import{buildSimpleProbeReport as Q}from"../shared/probe-util.js";import{getAgyModelCatalog as V,resolveAgyModelFlag as Y}from"./model-list.js";const c="agy-adapter";function q(g){try{return process.kill(g,0),!0}catch(t){return t.code==="EPERM"}}function F(g){try{return L(g)}catch{return g}}const k=m(_(),".gemini","antigravity-cli","log"),H="CLI store manager shutting down",U=5e3,w=15e3,J="30m",C=m(_(),".gemini","antigravity-cli","cache","last_conversations.json"),z=["\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\u3001agent_id\uFF08\u5F53\u524D Agent\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","\u8FDB\u7A0B\u5B89\u5168\uFF1A\u542F\u52A8\u4E34\u65F6\u6216\u6F14\u793A\u670D\u52A1\u65F6\u5FC5\u987B\u8BB0\u5F55\u5176 PID\uFF0C\u505C\u6B62\u65F6\u53EA\u7EC8\u6B62\u5DF2\u786E\u8BA4\u5C5E\u4E8E\u672C\u4EFB\u52A1\u7684\u8BE5 PID \u6216\u5176\u4E13\u5C5E\u8FDB\u7A0B\u7EC4\u3002","\u7981\u6B62\u4F7F\u7528 `pkill`\u3001`killall` \u6216\u5176\u4ED6\u6309\u540D\u79F0/\u547D\u4EE4\u884C\u6A21\u5F0F\u6279\u91CF\u6740\u8FDB\u7A0B\u7684\u547D\u4EE4\uFF1B\u7EDD\u4E0D\u80FD\u7EC8\u6B62 Grix MCP\u3001\u5F53\u524D agy \u6216\u5176\u7236\u8FDB\u7A0B\u3001connector \u6216\u5176\u4ED6\u65E0\u5173\u8FDB\u7A0B\u3002"].join(""),X=new Set(["send_msg","grix_reply"]),Z=new Set(["send_msg","grix_reply","delete_msg","session_send","grix_complete","grix_composing","grix_event_ack"]);function b(g){try{return structuredClone(g)}catch{return}}class gt extends T{type="agy";config;callbacks;runtimeResolver;alive=!1;stopped=!1;internalApi=null;activeEventId=null;activeEventSessionId=null;activeProcess=null;completedEventIds=new Set;activeEventSentViaTool=!1;auditCaptures=new Map;completedAuditBoundaries=new Map;watchdogPollTimer=null;watchdogGraceTimer=null;watchdogKilledActiveProcess=!1;sessionConversationMap=new Map;conversationOutputCache=new Map;eventQueue=[];systemInstructionInjectedSessions=new Set;identityInjectedSessions=new Set;constructor(t,e,i){super(),this.config=t,this.callbacks=e,this.runtimeResolver=i}async start(){this.alive||(this.alive=!0,this.stopped=!1,await this.startInternalApiAndInjectMcp(),a.info(c,"AgyAdapter started"))}async startInternalApiAndInjectMcp(){try{this.internalApi=new B,this.internalApi.setInvokeHandler(async(n,d,h,p)=>this.handleToolInvoke(n,d,p)),await this.internalApi.start(0),a.info(c,`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=m(_(),".gemini","config","mcp_config.json");$(O(e),{recursive:!0});let i={};try{A(e)&&(i=JSON.parse(I(e,"utf8")))}catch{}const s=i.mcpServers&&typeof i.mcpServers=="object"?i.mcpServers:{};K(s,this.internalApi.url),s[t.name]={command:t.command,args:t.args},i.mcpServers=s,M(e,`${JSON.stringify(i,null,2)}
2
+ `,"utf8"),a.info(c,`MCP config injected into ${e}`);const r=m(_(),".gemini","skills"),o=W(r);o.length>0&&a.info(c,`Synced connector skills to ${r}: [${o.join(", ")}]`)}catch(t){a.warn(c,`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async handleToolInvoke(t,e,i){let s=e;Z.has(t)&&this.activeEventSessionId&&(s.session_id==null||s.session_id==="")&&(s={...s,session_id:this.activeEventSessionId});const r=this.activeEventId?this.auditCaptures.get(this.activeEventId):void 0,o=r?{sequence:r.toolInvocations.length,observedAt:new Date().toISOString(),action:t,params:b(s)}:void 0;r&&o&&r.toolInvocations.push(o);try{const n=await this.callbacks.agentInvoke(t,s,i);return o&&(o.completedAt=new Date().toISOString(),o.result=b(n)),X.has(t)&&this.activeEventId&&(this.activeEventSentViaTool=!0),n}catch(n){throw o&&(o.completedAt=new Date().toISOString(),o.error=n instanceof Error?n.message:String(n)),n}}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.stopTurnWatchdog(),this.watchdogKilledActiveProcess=!1,this.activeProcess){try{y(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&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null,this.activeEventSessionId=null,this.internalApi){try{await this.internalApi.stop()}catch{}this.internalApi=null}this.emit("exit",0),a.info(c,"AgyAdapter stopped")}}isAlive(){return this.alive}async createSession(t){return t.cwd??P()}async resumeSession(t,e){}async destroySession(t){this.systemInstructionInjectedSessions.delete(t),this.identityInjectedSessions.delete(t)}onAgentProfileChanged(){this.identityInjectedSessions.clear()}sendPrompt(t){const e=new T;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;a.info(c,`cancelCurrentRun: killing process group for event ${t}`);try{y(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)){a.debug(c,`Skipping duplicate event: ${t.event_id}`);return}if(this.callbacks.sendEventAck(t.event_id,t.session_id),this.activeEventId){a.info(c,`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){a.info(c,`Stopping event ${t}`);try{y(this.activeProcess,"SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActiveEvent(t),this.drainQueue()}}setPermissionHandler(){}async ping(t){if(this.stopped||!this.alive||!this.internalApi)return!1;const e=this.activeProcess;if(!e)return!0;if(!e.pid||!q(e.pid))return!1;const i=!!(e.stdout&&!e.stdout.destroyed),s=!!(e.stderr&&!e.stderr.destroyed);return i&&s}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.sessionConversationMap.size}}async probe(t){const e=this.getStatus();return Q(this.config.command||"agy",{alive:e.alive,busy:e.busy,started:e.alive},t)}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}snapshotAuditBoundary(t){const e=this.auditCaptures.get(t);if(!e)return;this.auditCaptures.delete(t);let i;if(e.logPath)try{i=G(e.logPath).size}catch{}const s=this.sessionConversationMap.get(e.sessionId)??e.conversationId;this.completedAuditBoundaries.set(t,{adapterType:"agy",...s?{providerSessionId:s}:{},...e.cwd?{cwd:e.cwd}:{},...e.logPath?{transcriptPath:e.logPath,transcriptStartOffset:e.logStartOffset??0,transcriptEndOffset:i??0}:{},agyEvidence:{...e.prompt===void 0?{}:{prompt:e.prompt},...e.stdout===void 0?{}:{stdout:e.stdout},...e.stderr===void 0?{}:{stderr:e.stderr},...e.exitCode===void 0?{}:{exitCode:e.exitCode},...e.response===void 0?{}:{response:e.response},toolInvocations:e.toolInvocations}})}clearActiveEventForShutdown(){if(this.activeProcess)try{y(this.activeProcess,"SIGKILL")}catch{}this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.stopTurnWatchdog(),this.watchdogKilledActiveProcess=!1,this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1}getMcpConfig(){if(!this.internalApi)return null;const t=j(D(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:N(this.callbacks.getAgentId?.()),command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}processEvent(t){const{event_id:e,session_id:i}=t,s=this.runtimeResolver(i);if(!s.cwd){a.warn(c,`No working directory for session ${i}, event should have been intercepted by bridge`),this.callbacks.forceCompleteInternalEvent(e,i);return}this.activeEventId=e,this.activeEventSessionId=i,this.activeEventSentViaTool=!1,this.emit("eventStarted",e,i),a.info(c,`Processing event ${e} for session ${i}`);const r=this.buildAgyPrompt(t);t.audit?.enabled===!0&&this.auditCaptures.set(e,{sessionId:i,...s.cwd?{cwd:s.cwd}:{},...s.conversationId?{conversationId:s.conversationId}:{},prompt:r,toolInvocations:[]}),this.spawnAgyPrint(e,i,r,s)}buildAgyPrompt(t){const i=[["chat_id",t.session_id],["agent_id",this.callbacks.getAgentId?.()],["event_id",t.event_id],["message_id",t.msg_id],["user_id",t.sender_id],["quoted_message_id",t.quoted_message_id]].filter(([,d])=>d!=null&&d!=="").map(([d,h])=>`${d}="${h}"`).join(" "),s=this.renderContextBlock(t),r=s?`${t.content}
3
3
 
4
4
  [Conversation context]
5
5
  ${s}`:t.content,o=`<channel source="grix-agy" ${i}>
6
6
  ${r}
7
7
  </channel>`,n=[];if((!t.session_id||!this.systemInstructionInjectedSessions.has(t.session_id))&&(t.session_id&&this.systemInstructionInjectedSessions.add(t.session_id),n.push(`[grix-system-instructions]
8
- ${Z}
9
- [/grix-system-instructions]`)),!t.session_id||!this.identityInjectedSessions.has(t.session_id)){const a=this.buildIdentityBlock().trim();a&&(t.session_id&&this.identityInjectedSessions.add(t.session_id),n.push(a))}return n.length>0?`${o}
8
+ ${z}
9
+ [/grix-system-instructions]`)),!t.session_id||!this.identityInjectedSessions.has(t.session_id)){const d=this.buildIdentityBlock().trim();d&&(t.session_id&&this.identityInjectedSessions.add(t.session_id),n.push(d))}return n.length>0?`${o}
10
10
 
11
11
  ${n.join(`
12
12
 
13
13
  `)}`:o}buildIdentityBlock(){const t=this.callbacks.getAgentProfile?.(),e=t?.agentName?.trim()??"",i=t?.introduction?.trim()??"";if(!e&&!i)return"";const s=["[system-identity]"];return e&&s.push(`\u4F60\u7684\u540D\u5B57\u662F"${e}"\u3002`),i&&s.push(`\u4F60\u7684\u81EA\u6211\u4ECB\u7ECD\uFF1A${i}`),s.push("\u8FD9\u662F\u5E26\u5916\u6307\u4EE4\uFF1B\u4E0D\u8981\u5728\u56DE\u590D\u4E2D\u590D\u8FF0\u6216\u5F15\u7528\u672C\u6BB5\u3002"),s.push("[/system-identity]"),`${s.join(`
14
14
  `)}
15
15
 
16
- `}renderContextBlock(t){const e=t.context_messages_json;if(!e)return"";let i;try{const n=JSON.parse(e);if(!Array.isArray(n))return"";i=n}catch{return""}const s=String(t.session_type??"")==="2",r=String(t.msg_id??""),o=[];for(const n of i){const a=String(n?.msg_id??"");if(a&&a===r)continue;const h=String(n?.content??"").trim();if(!h)continue;const u=String(n?.sender_id??"");if(h.startsWith("[\u5F15\u7528\u6D88\u606F]")){const v=h.slice(6).replace(/^\s*\n?/,"").trim();o.push(s&&u?`[\u5F15\u7528\u6D88\u606F] (\u6765\u81EA ${u})\uFF1A${v}`:`[\u5F15\u7528\u6D88\u606F]\uFF1A${v}`)}else o.push(s&&u?`[${u}]\uFF1A${h}`:h)}return o.join(`
17
- `)}spawnAgyPrint(t,e,i,s){const r=this.buildPrintArgs(i,s),o={...process.env,...this.config.env??{}};c.info(d,`Spawning: agy ${r.map(l=>l.includes(" ")?`"${l}"`:l).join(" ")}`);const n=this.getLatestLogFilePath(),a=x(this.config.command,r,{cwd:s.cwd,env:o,stdio:["pipe","pipe","pipe"],detached:process.platform!=="win32"});this.activeProcess=a;const h=[],u=[];a.stdout?.on("data",l=>{h.push(l)}),a.stderr?.on("data",l=>{u.push(l)}),a.on("error",l=>{c.error(d,`Process spawn error for event ${t}: ${l.message}`),this.handleProcessResult(t,e,"",`spawn error: ${l.message}`,s)}),a.on("close",l=>{if(c.info(d,`Process exited for event ${t} with code ${l}`),this.activeEventId!==t)return;this.stopTurnWatchdog();const g=Buffer.concat(h).toString("utf-8"),k=Buffer.concat(u).toString("utf-8"),E=this.auditCaptures.get(t);E&&(E.stdout=g,E.stderr=k,E.exitCode=l);const b=this.watchdogKilledActiveProcess&&l===null;if(this.watchdogKilledActiveProcess=!1,b)g.trim()||this.activeEventSentViaTool?this.handleProcessResult(t,e,g,"",s):setTimeout(()=>{const m=this.extractLogError(n);this.handleProcessResult(t,e,g,m,s)},300);else if(l!==0){const m=k.trim()||`process exited with code ${l}`;this.handleProcessResult(t,e,g,m,s)}else g.trim()?this.handleProcessResult(t,e,g,"",s):this.activeEventSentViaTool?this.handleProcessResult(t,e,g,"",s):setTimeout(()=>{const m=this.extractLogError(n);this.handleProcessResult(t,e,g,m,s)},300)}),a.stdin?.end();const v=n&&w(n)?I(n).size:0,A=this.auditCaptures.get(t);A&&n&&(A.logPath=n,A.logStartOffset=v),this.startTurnWatchdog(t,n,v,Date.now(),s.conversationId)}startTurnWatchdog(t,e,i,s,r){this.stopTurnWatchdog(),this.watchdogPollTimer=setInterval(()=>{if(this.activeEventId!==t||!this.activeProcess){this.stopTurnWatchdog();return}this.watchLogHasTurnEndMarker(e,i,s,r)&&(this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),c.warn(d,`Turn end marker logged for event ${t} but process still alive; grace ${T}ms before kill`),this.watchdogGraceTimer=setTimeout(()=>{if(this.watchdogGraceTimer=null,!(this.activeEventId!==t||!this.activeProcess)){c.warn(d,`agy process still alive ${T}ms after turn end (shutdown deadlock); killing process group for event ${t}`),this.watchdogKilledActiveProcess=!0;try{S(this.activeProcess,"SIGKILL")}catch{}}},T),this.watchdogGraceTimer.unref())},J),this.watchdogPollTimer.unref()}stopTurnWatchdog(){this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),this.watchdogGraceTimer&&(clearTimeout(this.watchdogGraceTimer),this.watchdogGraceTimer=null),this.watchdogLogPath=null,this.watchdogLogOffset=0}watchLogHasTurnEndMarker(t,e,i,s){try{if(!this.watchdogLogPath){const n=this.getLatestLogFilePath();if(!n||I(n).mtimeMs<i)return!1;if(n===t)this.watchdogLogPath=n,this.watchdogLogOffset=e;else{if(s&&!y(n,"utf-8").slice(0,8192).includes(s))return!1;this.watchdogLogPath=n,this.watchdogLogOffset=0}const a=this.activeEventId?this.auditCaptures.get(this.activeEventId):void 0;a&&(a.logPath=this.watchdogLogPath,a.logStartOffset=this.watchdogLogOffset)}const r=y(this.watchdogLogPath),o=r.length<this.watchdogLogOffset?0:this.watchdogLogOffset;return r.subarray(o).toString("utf-8").includes(z)}catch{return!1}}buildPrintArgs(t,e){const i=["--print-timeout",X],s=e.modelId?q(e.modelId,Y()):"";return s&&i.push("--model",s),e.cwd&&i.push("--add-dir",e.cwd),i.push("--dangerously-skip-permissions"),e.conversationId&&i.push("--conversation",e.conversationId),i.push("-p",t),i}readAgyConversationId(t){try{if(w($)){const e=JSON.parse(y($,"utf-8"));return e[U(t)]||e[t]||void 0}}catch(e){c.debug(d,`readAgyConversationId: ${e}`)}}getLatestLogFilePath(){try{if(!w(_))return null;const t=R(_).filter(e=>e.startsWith("cli-")&&e.endsWith(".log")).map(e=>({name:e,path:f(_,e),mtime:I(f(_,e)).mtimeMs})).sort((e,i)=>i.mtime-e.mtime);return t.length>0?t[0].path:null}catch{return null}}extractLogError(t){try{const e=this.getLatestLogFilePath();if(!e)return c.info(d,"extractLogError: no log files found"),"agy completed without output";const i=y(e,"utf-8");if(!i)return c.info(d,`extractLogError: log file is empty: ${e}`),"agy completed without output";const s=i.split(`
18
- `).filter(r=>r.startsWith("E")).map(r=>{const o=r.indexOf("] ");return o>=0?r.slice(o+2):r}).filter(r=>r.length>0);if(s.length>0){const r=s[s.length-1];return c.info(d,`Extracted error from agy log: ${r.slice(0,120)}`),r.length>500?r.slice(0,500)+"...":r}return"agy completed without output (possible auth or quota issue)"}catch(e){return c.info(d,`extractLogError: ${e}`),"agy completed without output"}}handleProcessResult(t,e,i,s,r){if(this.activeEventId===t){if(s&&!i.trim()){c.error(d,`Event ${t} failed: ${s}`);const n=s.includes("RESOURCE_EXHAUSTED")||s.includes("quota")?"The agy API quota has been exhausted. Please try again later.":`agy execution failed: ${s}`;this.callbacks.sendStreamChunk(t,e,n,1,!0,`agy-${t}`),this.callbacks.sendEventResult(t,"failed",s)}else{const o=i.trim();if(o){const n=r.cwd,a=n?this.extractDelta(o,n):o;n&&this.conversationOutputCache.set(n,o);const h=this.auditCaptures.get(t);h&&(h.response=a),a&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,a,1,!0,`agy-${t}`)}if(r.cwd){const n=this.readAgyConversationId(r.cwd);n&&n!==r.conversationId&&(this.sessionConversationMap.set(e,n),this.callbacks.persistConversationId(e,n))}this.callbacks.sendEventResult(t,"responded")}this.clearActiveEvent(t),this.drainQueue()}}extractDelta(t,e){const i=this.conversationOutputCache.get(e);return i&&t.startsWith(i)?t.slice(i.length).trim():t}clearActiveEvent(t){const e=t??this.activeEventId;if(e){if(this.completedEventIds.add(e),this.completedEventIds.size>1e3){const i=Array.from(this.completedEventIds);this.completedEventIds.clear();for(let s=500;s<i.length;s++)this.completedEventIds.add(i[s])}this.snapshotAuditBoundary(e)}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1,this.stopTurnWatchdog(),this.watchdogKilledActiveProcess=!1,e&&this.emit("eventDone",e)}drainQueue(){if(this.stopped||this.activeEventId)return;const t=this.eventQueue.shift();t&&(c.info(d,`Draining queued event ${t.event_id}`),this.processEvent(t))}}export{vt as AgyAdapter};
16
+ `}renderContextBlock(t){const e=t.context_messages_json;if(!e)return"";let i;try{const n=JSON.parse(e);if(!Array.isArray(n))return"";i=n}catch{return""}const s=String(t.session_type??"")==="2",r=String(t.msg_id??""),o=[];for(const n of i){const d=String(n?.msg_id??"");if(d&&d===r)continue;const h=String(n?.content??"").trim();if(!h)continue;const p=String(n?.sender_id??"");if(h.startsWith("[\u5F15\u7528\u6D88\u606F]")){const f=h.slice(6).replace(/^\s*\n?/,"").trim();o.push(s&&p?`[\u5F15\u7528\u6D88\u606F] (\u6765\u81EA ${p})\uFF1A${f}`:`[\u5F15\u7528\u6D88\u606F]\uFF1A${f}`)}else o.push(s&&p?`[${p}]\uFF1A${h}`:h)}return o.join(`
17
+ `)}spawnAgyPrint(t,e,i,s){try{$(k,{recursive:!0})}catch(l){const u=l instanceof Error?l.message:String(l),S=`unable to prepare agy log directory: ${u}`;a.error(c,`Log setup failed for event ${t}: ${u}`),this.handleProcessResult(t,e,"",S,s);return}const r=m(k,`grix-${P()}.log`),o=this.buildPrintArgs(i,s,r),n={...process.env,...this.config.env??{}};a.info(c,`Spawning: agy ${o.map(l=>l.includes(" ")?`"${l}"`:l).join(" ")}`);const d=R(this.config.command,o,{cwd:s.cwd,env:n,stdio:["pipe","pipe","pipe"],detached:process.platform!=="win32"});this.activeProcess=d;const h=[],p=[];d.stdout?.on("data",l=>{h.push(l)}),d.stderr?.on("data",l=>{p.push(l)}),d.on("error",l=>{a.error(c,`Process spawn error for event ${t}: ${l.message}`),this.handleProcessResult(t,e,"",`spawn error: ${l.message}`,s)}),d.on("close",l=>{if(a.info(c,`Process exited for event ${t} with code ${l}`),this.activeEventId!==t)return;this.stopTurnWatchdog();const u=Buffer.concat(h).toString("utf-8"),S=Buffer.concat(p).toString("utf-8"),E=this.auditCaptures.get(t);E&&(E.stdout=u,E.stderr=S,E.exitCode=l);const x=this.watchdogKilledActiveProcess&&l===null;if(this.watchdogKilledActiveProcess=!1,x)u.trim()||this.activeEventSentViaTool?this.handleProcessResult(t,e,u,"",s):setTimeout(()=>{const v=this.extractLogError(r);this.handleProcessResult(t,e,u,v,s)},300);else if(l!==0){const v=S.trim()||`process exited with code ${l}`;this.handleProcessResult(t,e,u,v,s)}else u.trim()?this.handleProcessResult(t,e,u,"",s):this.activeEventSentViaTool?this.handleProcessResult(t,e,u,"",s):setTimeout(()=>{const v=this.extractLogError(r);this.handleProcessResult(t,e,u,v,s)},300)}),d.stdin?.end();const f=this.auditCaptures.get(t);f&&(f.logPath=r,f.logStartOffset=0),this.startTurnWatchdog(t,r)}startTurnWatchdog(t,e){this.stopTurnWatchdog(),this.watchdogPollTimer=setInterval(()=>{if(this.activeEventId!==t||!this.activeProcess){this.stopTurnWatchdog();return}this.watchLogHasTurnEndMarker(e)&&(this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),a.warn(c,`Turn end marker logged for event ${t} but process still alive; grace ${w}ms before kill`),this.watchdogGraceTimer=setTimeout(()=>{if(this.watchdogGraceTimer=null,!(this.activeEventId!==t||!this.activeProcess)){a.warn(c,`agy process still alive ${w}ms after turn end (shutdown deadlock); killing process group for event ${t}`),this.watchdogKilledActiveProcess=!0;try{y(this.activeProcess,"SIGKILL")}catch{}}},w),this.watchdogGraceTimer.unref())},U),this.watchdogPollTimer.unref()}stopTurnWatchdog(){this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),this.watchdogGraceTimer&&(clearTimeout(this.watchdogGraceTimer),this.watchdogGraceTimer=null)}watchLogHasTurnEndMarker(t){try{return I(t,"utf-8").includes(H)}catch{return!1}}buildPrintArgs(t,e,i){const s=["--print-timeout",J,"--log-file",i],r=e.modelId?Y(e.modelId,V()):"";return r&&s.push("--model",r),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(A(C)){const e=JSON.parse(I(C,"utf-8"));return e[F(t)]||e[t]||void 0}}catch(e){a.debug(c,`readAgyConversationId: ${e}`)}}extractLogError(t){try{if(!A(t))return a.info(c,`extractLogError: log file not found: ${t}`),"agy completed without output";const e=I(t,"utf-8");if(!e)return a.info(c,`extractLogError: log file is empty: ${t}`),"agy completed without output";const i=e.split(`
18
+ `).filter(s=>s.startsWith("E")).map(s=>{const r=s.indexOf("] ");return r>=0?s.slice(r+2):s}).filter(s=>s.length>0);if(i.length>0){const s=i[i.length-1];return a.info(c,`Extracted error from agy log: ${s.slice(0,120)}`),s.length>500?s.slice(0,500)+"...":s}return"agy completed without output (possible auth or quota issue)"}catch(e){return a.info(c,`extractLogError: ${e}`),"agy completed without output"}}handleProcessResult(t,e,i,s,r){if(this.activeEventId===t){if(s&&!i.trim()){a.error(c,`Event ${t} failed: ${s}`);const n=s.includes("RESOURCE_EXHAUSTED")||s.includes("quota")?"The agy API quota has been exhausted. Please try again later.":`agy execution failed: ${s}`;this.callbacks.sendStreamChunk(t,e,n,1,!0,`agy-${t}`),this.callbacks.sendEventResult(t,"failed",s)}else{const o=i.trim();if(o){const n=r.cwd,d=n?this.extractDelta(o,n):o;n&&this.conversationOutputCache.set(n,o);const h=this.auditCaptures.get(t);h&&(h.response=d),d&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,d,1,!0,`agy-${t}`)}if(r.cwd){const n=this.readAgyConversationId(r.cwd);n&&n!==r.conversationId&&(this.sessionConversationMap.set(e,n),this.callbacks.persistConversationId(e,n))}this.callbacks.sendEventResult(t,"responded")}this.clearActiveEvent(t),this.drainQueue()}}extractDelta(t,e){const i=this.conversationOutputCache.get(e);return i&&t.startsWith(i)?t.slice(i.length).trim():t}clearActiveEvent(t){const e=t??this.activeEventId;if(e){if(this.completedEventIds.add(e),this.completedEventIds.size>1e3){const i=Array.from(this.completedEventIds);this.completedEventIds.clear();for(let s=500;s<i.length;s++)this.completedEventIds.add(i[s])}this.snapshotAuditBoundary(e)}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1,this.stopTurnWatchdog(),this.watchdogKilledActiveProcess=!1,e&&this.emit("eventDone",e)}drainQueue(){if(this.stopped||this.activeEventId)return;const t=this.eventQueue.shift();t&&(a.info(c,`Draining queued event ${t.event_id}`),this.processEvent(t))}}export{gt as AgyAdapter};
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "bridgeVersion": "4.0.1",
4
- "tarball": "grix-dsh-bridge-4.0.1.tgz",
5
- "size": 22008,
3
+ "bridgeVersion": "4.0.2",
4
+ "tarball": "grix-dsh-bridge-4.0.2.tgz",
5
+ "size": 22009,
6
6
  "unpackedSize": 87631,
7
- "shasum": "2b87cc453a861de9e491e5fe37d96bbf782b544a",
8
- "integrity": "sha512-48Kx3cTARnuY5+o8/f1me/f122v91dmhuOyurHi9hORe/jGncPtBvQy3ZDpTjBpccLch1YcI9WXxG+PIi1jErA=="
7
+ "shasum": "71ff5ab505ab6879d00d62571235dbca68276b59",
8
+ "integrity": "sha512-GC7a/e0X1Thaj5kUmjx00/Fbf8cS0klllCrrekV0wcF4xYD1lyDmY/Xaktxp9SyNN6G7diGeu4/hne03x/qyDQ=="
9
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",