grix-connector 4.13.0 → 4.14.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.
Files changed (42) hide show
  1. package/dist/adapter/acp/acp-adapter.js +13 -13
  2. package/dist/adapter/acp/grok-liveness.js +3 -0
  3. package/dist/adapter/agy/agy-adapter.js +10 -10
  4. package/dist/adapter/agy/stream-parser.js +2 -2
  5. package/dist/adapter/claude/claude-adapter.js +22 -21
  6. package/dist/adapter/claude/model-list.js +1 -1
  7. package/dist/adapter/claude/session-recovery.js +1 -0
  8. package/dist/adapter/codex/codex-bridge.js +12 -10
  9. package/dist/adapter/codex/wait-loop.js +1 -0
  10. package/dist/adapter/cursor/cursor-adapter.js +1 -1
  11. package/dist/adapter/cursor/fetch-usage.js +1 -1
  12. package/dist/adapter/cursor/index.js +1 -1
  13. package/dist/adapter/pi/pi-adapter.js +4 -4
  14. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.14.1.tgz +0 -0
  15. package/dist/assets/dsh-bridge/manifest.json +5 -5
  16. package/dist/audit/adapters/codex/codex-audit-adapter.js +1 -1
  17. package/dist/bridge/adapter-pool.js +1 -1
  18. package/dist/bridge/bridge.js +2 -2
  19. package/dist/bridge/event-queue.js +1 -1
  20. package/dist/bridge/event-stop.js +1 -1
  21. package/dist/bridge/provider-quota.js +1 -1
  22. package/dist/bridge/rate-limits.js +1 -1
  23. package/dist/bridge/send-controller.js +3 -3
  24. package/dist/bridge/session-control-router.js +3 -3
  25. package/dist/bridge/toolbar-meta.js +1 -1
  26. package/dist/bridge/toolbar-selection.js +1 -1
  27. package/dist/core/aibot/client.js +2 -2
  28. package/dist/core/persistence/session-binding-store.js +2 -2
  29. package/dist/core/protocol/protocol-send.js +1 -0
  30. package/dist/core/provider-quota/grix-relay.js +1 -0
  31. package/dist/core/provider-quota/index.js +1 -1
  32. package/dist/core/util/quoted-message-stream.js +3 -3
  33. package/dist/default-skills/grix-agent-dispatch/SKILL.md +37 -7
  34. package/dist/default-skills/grix-agent-dispatch/scripts/callback-dedupe.mjs +12 -0
  35. package/dist/grix.js +6 -6
  36. package/dist/manager.js +1 -1
  37. package/openclaw-plugin/audit-recorder-worker.js +37 -0
  38. package/openclaw-plugin/index.js +9 -3
  39. package/openclaw-plugin/skills/grix-agent-dispatch/SKILL.md +37 -7
  40. package/openclaw-plugin/skills/grix-agent-dispatch/scripts/callback-dedupe.mjs +12 -0
  41. package/package.json +1 -1
  42. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.13.0.tgz +0 -0
@@ -1,20 +1,20 @@
1
- import{fileURLToPath as Y}from"node:url";import p from"node:path";import{homedir as g}from"node:os";import{promises as y,statSync as $}from"node:fs";import{EventEmitter as M}from"node:events";import{spawn as x}from"node:child_process";import{AgentProcess as J}from"../../agent/process.js";import{hasChildProcesses as Z,listGroupPids as ee}from"../../core/runtime/spawn.js";import{applyNativeProviderConfig as te,agentIdOf as C}from"../shared/native-provider-config.js";import{applyMcodeProviderConfig as ie}from"./mcode-provider-config.js";import{buildSessionContextBlock as se}from"../shared/session-context.js";import{IdentityInjector as ne}from"../shared/identity-injector.js";import{compactToolInputForWire as q}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as oe,resolveKimiCodeHome as re}from"../../default-skills/index.js";import{resolveHermesHomeDir as ae}from"../shared/native-provider-config.js";import{prepareKimiLayerPlugin as le}from"../../kimi-plugins/index.js";import{AcpClient as ce,AcpAuthRequiredError as de,isAuthRequiredError as ue}from"../../protocol/acp-client.js";import{AgentEventType as m}from"../../types/events.js";import{InternalApiServer as he}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as pe}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as S}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as me}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as fe}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as ge}from"../../core/protocol/message-metadata.js";import{insertBeforeTrailingMessageReferences as T}from"../../core/protocol/message-reference.js";import{log as a}from"../../core/log/index.js";function N(d,e=2e3){let t;try{const i=new WeakSet;t=JSON.stringify(d,(s,n)=>{if(n instanceof Error)return{name:n.name,message:n.message};if(typeof n=="function")return"[function]";if(typeof n=="bigint"||typeof n=="symbol")return n.toString();if(typeof n=="object"&&n!==null){if(i.has(n))return"[circular]";i.add(n)}return n})}catch(i){t=`[stringify failed: ${i instanceof Error?i.message:String(i)}]`}return t===void 0?"[undefined]":t.length<=e?t:`${t.slice(0,e)}...[+${t.length-e}b]`}const ve=5e3;function O(d,e){const t=e.toolName??"";return e.toolCallId&&(!t||t===e.toolCallId)?d.toolNamesById.get(e.toolCallId)??t:t}function _(d){const e=String(d??"").trim();if(!e)return"";try{const t=JSON.parse(e);return JSON.stringify(b(t))}catch{return e}}function b(d){if(d===null||typeof d!="object")return d;if(Array.isArray(d))return d.map(b);const e=d,t=Object.keys(e).sort(),i={};for(const s of t)i[s]=b(e[s]);return i}function D(d){const e=Array.isArray(d)?d:[d],t=[];for(const i of e){if(!i||typeof i!="object")continue;const s=i;if(typeof s.text=="string"&&s.text.trim()){t.push(s.text.trim());continue}const n=s.content;if(n&&typeof n=="object"&&!Array.isArray(n)){const o=n.text;typeof o=="string"&&o.trim()&&t.push(o.trim())}}return t.join(`
2
- `)}function Ie(d,e,t){const i=d&&typeof d=="object"?d:{},s=i.toolCall&&typeof i.toolCall=="object"?i.toolCall:{},n=[s.rawInput,s.raw_input,s.input,s.arguments,s.args,D(s.content),s.content,s.description,i.tool_input,i.toolInput,e];for(const o of n){const r=q(o);if(r&&r!==t)return r}return""}const F=/^q\d+_opt_\d+$/,R=/^q\d+_skip$/,ye=new Set(["allow_once","allow_always","reject_once","reject_always"]);function Ae(d){if(!`${d.toolName} ${d.toolTitle}`.toLowerCase().replace(/[^a-z]/g,"").includes("askuserquestion"))return null;const t=d.options;if(t.length===0)return null;const i=t.some(l=>F.test(l.optionId)),s=i?t.filter(l=>F.test(l.optionId)):t.filter(l=>!R.test(l.optionId));if(s.length===0||!i&&s.every(l=>ye.has(l.kind)))return null;const n=d.rawParams&&typeof d.rawParams=="object"?d.rawParams:{},o=n.toolCall&&typeof n.toolCall=="object"?n.toolCall:{},r=D(o.content).trim();return r?{prompt:r,answerOptions:s}:null}import{scanSkills as U}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as Ce,checkKimiSessionActivity as Se,checkQoderSessionActivity as B,checkDimSessionActivity as _e,hasRunningKimiDetachedTask as we}from"./session-activity.js";import{resolveCliPath as Te,getCliVersion as be}from"../../core/util/cli-probe.js";const k=p.dirname(Y(import.meta.url)),j=[{modelId:"deepseek-v4-pro",name:"DeepSeek V4 Pro"},{modelId:"deepseek-v4-flash",name:"DeepSeek V4 Flash"}],P=[{modelId:"kimi-code/kimi-for-coding",name:"K2.7 Coding"},{modelId:"kimi-code/kimi-for-coding-highspeed",name:"K2.7 Coding Highspeed"},{modelId:"kimi-code/k3",name:"K3"}];function E(d,e){const t=Number(process.env[d]);return Number.isFinite(t)&&t>0?t:e}const Re=200,ke=60*1e3,Pe=600*1e3,w=E("GRIX_ACP_TURN_INACTIVITY_TIMEOUT_MS",900*1e3),L=2,K=5e3,W=80,H=1200*1e3,Ee=E("GRIX_ACP_BUSY_RETRY_INTERVAL_MS",5e3),Q=E("GRIX_ACP_BUSY_WAIT_MAX_MS",1800*1e3),$e=6e4,Me=2e3,xe=12e3,G=3e3,z=3e4,qe=1e3,Ne=1e4;function Oe(d){return new Promise(e=>setTimeout(e,d))}const De=["[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 Fe(d){return!(!(d instanceof Error)||d.code!==-32603||ue(d))}function X(d){if(!(d instanceof Error))return!1;const e=d.data;return e&&typeof e=="object"&&e.code==="turn.agent_busy"?!0:/cannot launch a new turn/i.test(d.message)}function A(d){if(!(d instanceof Error))return String(d);let e=d.message;const t=d.data;return t&&(e+=` data=${JSON.stringify(t)}`),e}function Ue(d){return d.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class V extends M{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;pendingQuestions=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;newSessionParams;acpMcpTools;terminalAuthChild=null;rawTransport;approvalMode;autoInjectArgs;nativeProviderScope;reasonixAuditTranscriptPath;auditAgentType;trimToolWirePayload;activeAuditEventId=null;auditCaptures=new Map;completedAuditBoundaries=new Map;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;bridgeLog;sessionConnected=!1;sessionContextPending=!1;baselineGroupPids=[];rawEventSeq=0;recoveryContextBySessionId=new Map;quoteProtocolTaughtSessions=new Set;identity;orphanBuffer="";orphanQuotedStream=new S;orphanFlushTimer=null;orphanSessionId=null;orphanSuppressedUntil=0;constructor(e,t,i){super(),this.config=e,this.callbacks=t,this.identity=new ne("acp-adapter",t.getAgentProfile),this.bridgeLog=i?.bridgeLog??null,this.acpAuthMethod=i?.acpAuthMethod,this.acpInitialMode=i?.acpInitialMode,this.acpInitialModel=i?.acpInitialModel,this.newSessionParams=i?.newSessionParams,this.acpMcpTools=i?.acpMcpTools??!0,this.rawTransport=i?.rawTransport??!1,this.approvalMode=i?.approvalMode??"default",this.autoInjectArgs=i?.autoInjectArgs,this.nativeProviderScope=i?.nativeProviderScope,this.reasonixAuditTranscriptPath=i?.reasonixAuditTranscriptPath,this.auditAgentType=String(i?.agentType??p.basename(e.command)).trim().toLowerCase();const s=p.basename(e.command).toLowerCase(),n=this.auditAgentType==="copilot"||/^copilot(?:\.cmd)?$/i.test(s)||/^gh(?:\.exe)?$/i.test(s)&&(e.args??[]).includes("copilot");if(this.trimToolWirePayload=["kimi","kiro","qwen","reasonix","copilot"].includes(this.auditAgentType)||s.includes("kimi")||s==="kiro-cli"||/^qwen(?:\.cmd)?$/i.test(s)||s.includes("reasonix")||n,this.bindingStore=i?.bindingStore??null,this.currentAibotSessionId=i?.aibotSessionId?String(i.aibotSessionId).trim():void 0,i?.eventResultsPath&&(this.eventResults=new pe(i.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const o=this.bindingStore.get(this.currentAibotSessionId);o?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,o.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.finishRun("canceled","adapter stopped"),this.flushOrphanBuffer(),this.acpClient&&(this.acpClient.dispose(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.baselineGroupPids=[],this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.terminalAuthChild&&(this.terminalAuthChild.kill("SIGKILL"),this.terminalAuthChild=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,t){}async destroySession(e){this.quoteProtocolTaughtSessions.delete(e),this.identity.forgetSession(e)}onAgentProfileChanged(){this.identity.onProfileChanged()}sendPrompt(e){const t=new Be(e.adapterSessionId);return this.acpClient?.isAlive&&this.acpClient.send(e.text).catch(i=>{t.emitError(i instanceof Error?i:new Error(String(i)))}),t}async cancel(e){this.activeRun&&this.acpClient&&(await this.acpClient.cancel(),this.flushStream(),this.finishRun("canceled","stopped by user"),this.suppressOrphanOutput(z))}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.snapshotAuditBoundary(this.activeRun.eventId),this.clearRunTimers(this.activeRun),this.activeRun=null)}takeAuditBoundary(e){const t=this.completedAuditBoundaries.get(e);return t&&this.completedAuditBoundaries.delete(e),t}clearRunTimers(e){e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),e.busyRetryTimer&&(clearTimeout(e.busyRetryTimer),e.busyRetryTimer=null)}getMcpConfig(){if(!this.internalApi)return null;this.internalApi.setMcpBridgeUpHandler(t=>this.onMcpBridgeUp(t));const e=p.resolve(k,"../../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?Z(e,[e,...this.baselineGroupPids]):!1}captureBaselineGroupPids(){const e=this.agentProcess?.pid;e&&ee(e).then(t=>{this.baselineGroupPids=t}).catch(()=>{})}async probe(e){const t=this.config.command||"gemini",i=await Te(t),s=i!==null;let n=null,o;if(s){const h=await be(t);n=h.version,h.error&&(o=h.error)}else o={code:"cli_not_found",message:`command not found: ${t}`};const r=this.acpClient?.model??null,l=this.getStatus();let c;return e?.conversation?this.acpClient?.isAlive?c={attempted:!0,ok:!0,latency_ms:null}:c={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:c={attempted:!1,ok:!1,latency_ms:null},{cli:{command:t,installed:s,path:i,version:n,...o?{error:o}:{}},conversation:c,config:{model:r,base_url:null,source:{model:r?"runtime":"unknown",base_url:"unknown"}},process:{started:!!this.agentProcess,alive:l.alive,busy:l.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,t,i){return this.acpClient?.supportsCommandsExecute?this.execCommandViaAcp(e,t):this.execCommandFallback(e,t)}async execCommandViaAcp(e,t){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 i=await this.acpClient.executeCommand(e,t||void 0);return this.finishCompaction("acp-commands-execute"),{status:i.status==="failed"?"failed":"ok",message:i.message??"Compacted",data:i.data}}catch(i){return this.finishCompaction("error"),i?.code===-32601?this.execCommandFallback(e,t):{status:"failed",message:`compact failed: ${i instanceof Error?i.message:i}`}}}try{const i=await this.acpClient.executeCommand(e,t||void 0);return{status:i.status??"ok",message:i.message,data:i.options?{command:e,options:i.options}:i.data}}catch(i){return i?.code===-32601?this.execCommandFallback(e,t):{status:"failed",message:`Command failed: ${i instanceof Error?i.message:i}`}}}async execCommandFallback(e,t){try{switch(e){case"model":{const i=t.trim();if(i)return await this.setModel(i)?{status:"ok",message:`Model set to ${i}`}:{status:"failed",message:`Failed to set model: ${i}`};const s=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${s.currentModelId||"unknown"}`,data:s}}case"mode":{const i=t.trim();if(i)return await this.setMode(i)?{status:"ok",message:`Mode set to ${i}`}:{status:"failed",message:`Failed to set mode: ${i}`};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 i=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 i,{status:"ok",message:"Compacted"}}case"status":{const i=this.getStatus(),s=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${i.alive}, Busy: ${i.busy}, Model: ${s?.currentModelId??"unknown"}, Mode: ${s?.currentModeId??"unknown"}`,data:{alive:i.alive,busy:i.busy,sessions:i.sessions,model:s?.currentModelId??"",mode:s?.currentModeId??""}}}case"skills":{const i=U({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),s=i.map(n=>`- ${n.name}${n.trigger?` (${n.trigger})`:""} [${n.source}]: ${n.description}`);return{status:"ok",message:s.length>0?s.join(`
3
- `):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}get acpSessionOptions(){const e=this.acpClient?.sessionOptions??null,t=this.isReasonix?j:this.isKimi?P:null;if(!t||e&&e.models.length>0)return e;const i=this.resolveFallbackCurrentModelId(t,e?.currentModelId??"");return{modes:e?.modes??[],currentModeId:e?.currentModeId??"",models:[...t],currentModelId:i}}resolveFallbackCurrentModelId(e,t){if(e.some(s=>s.modelId===t))return t;const i=(this.autoInjectArgs?.model??this.acpInitialModel??"").trim();return i&&e.some(s=>s.modelId===i)?i:e[0]?.modelId??""}get isReasonix(){return(this.config.command??"").includes("reasonix")}get isKimi(){return(this.config.command??"").includes("kimi")}buildToolbarContext(e,t){const i=this.acpClient?.sessionOptions,s=i?.currentModeId??"";let n=i?.currentModelId??"";const o=i?.modes.map(f=>({id:f.id,name:f.name}))??[],r=i?.models&&i.models.length>0,l=r?i.models:this.isReasonix?j:this.isKimi?P:[],c=r?"acp":this.isReasonix?"reasonix_fallback":this.isKimi?"kimi_fallback":"empty";this.isKimi&&!r&&a.warn("acp-adapter",`[toolbar-diag] kimi ACP session returned no models; using static fallback. command=${this.config.command} fallbackIds=${JSON.stringify(P.map(f=>f.modelId))}`),a.info("acp-adapter",`[toolbar-diag] buildToolbarContext outcome=${e} agent=${this.config.command} modelSource=${c} acpModelCount=${i?.models?.length??0} finalModelCount=${l.length}`);const h=l.map(f=>({modelId:f.modelId,name:f.name})),u=l.map(f=>({id:f.modelId,displayName:f.name})),I=i?.modes.map(f=>({id:f.id,displayName:f.name}))??[];u.length>0&&!u.some(f=>f.id===n)&&(n=this.resolveFallbackCurrentModelId(l,n));const v={outcome:e,...t?{cwd:t}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:u,available_modes:I,availableModels:u,availableModes:I,models:h,modes:o};return a.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(u.map(f=>f.id))} currentModelId=${n}`),v}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,t){const i=this.pendingApprovals.get(e);return i?(a.info("acp-adapter","handleAcpApprovalAction resolving",{toolCallId:e,requestId:i,behavior:t}),this.pendingApprovals.delete(e),this.acpClient&&this.acpClient.respondPermission(i,{behavior:t}).catch(s=>{a.error("acp-adapter",`Failed to respond to permission: ${s}`)}),this.captureAuditEvent({type:m.PermissionResponse,requestId:i,toolCallId:e,permissionDecision:t}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun)),!0):(a.warn("acp-adapter","handleAcpApprovalAction: no pending approval",{toolCallId:e,behavior:t,pendingCount:this.pendingApprovals.size}),!1)}handleExecApprovalEvent(e){const t=String(e.tool_call_id??e.approval_command_id??e.approval_id??e.exec_context_id??"");return t?this.handleAcpApprovalAction(t,e.decision)?{delivered:!0}:{delivered:!1,errorCode:"approval_not_found",errorMsg:`no pending approval for tool_call_id: ${t}`}:{delivered:!1,errorCode:"approval_id_required",errorMsg:"approval id is required"}}respondToPermission(e,t){this.acpClient&&this.acpClient.respondPermission(e,t).catch(i=>{a.error("acp-adapter",`Failed to respond to permission: ${i}`)}),this.captureAuditEvent({type:m.PermissionResponse,requestId:e,permissionDecision:t.behavior}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun))}async handleLocalAction(e){const t=e.action_type??"",i=e.params??{};if(t==="claude_interaction_reply"&&String(i.kind??"")==="permission"){const s=i.resolution&&typeof i.resolution=="object"?i.resolution:{},n=String(s.value??i.decision??"");return["allow","allow-once","allow-always","deny"].includes(n)?this.handleLocalAction({...e,action_type:n==="deny"?"permission_reject":"permission_approve",params:{...i,approval_command_id:i.request_id,decision:n}}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"permission_decision_invalid","permission decision is invalid"),{handled:!0,kind:"approval"})}if(t==="claude_interaction_reply")return this.handleAcpQuestionReply(e);if(t==="exec_approve"||t==="exec_reject"||t==="permission_approve"||t==="permission_reject"){const s=String(i.tool_call_id??i.approval_command_id??i.approval_id??i.exec_context_id??""),n=t==="exec_approve"||t==="permission_approve",o=String(i.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:""}}async handleQuestionReplyEvent(e){let t;if(e.action)t={type:"action",value:e.action};else if(e.response?.type==="single")t={type:"text",value:e.response.value};else if(e.response?.type==="map")t={type:"map",entries:e.response.entries};else return{delivered:!1,errorCode:"resolution_invalid",errorMsg:"Question reply response is invalid."};return this.deliverQuestionReply({request_id:e.request_id,resolution:t})}async handleAcpQuestionReply(e){const t=await this.deliverQuestionReply(e.params??{});return t.delivered?(this.callbacks.sendLocalActionResult(e.action_id,"ok",t.result),{handled:!0,kind:t.kind??"question_reply"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,t.errorCode,t.errorMsg),{handled:!0,kind:t.kind??"question_reply_failed"})}async deliverQuestionReply(e){const t=String(e.request_id??"").trim(),i=this.pendingQuestions.get(t);if(!t||!i||!this.acpClient)return{delivered:!1,errorCode:"interaction_request_not_pending",errorMsg:"The question is no longer pending; the reply was not delivered.",kind:"question_reply_not_pending"};const s=e.resolution&&typeof e.resolution=="object"?e.resolution:{},n=String(s.type??"");let o,r="";if(n==="action"&&String(s.value??"")==="cancel")o=i.options.find(c=>/_skip$/.test(c.optionId)||c.kind.toLowerCase().includes("reject"));else{if(n==="text")r=String(s.value??"").trim();else if(n==="map"){const c=Array.isArray(s.entries)?s.entries:[];r=String(c[0]?.value??"").trim()}o=i.options.find(c=>c.name.trim()===r||c.optionId===r)}if(!o){const c=i.options.find(h=>R.test(h.optionId));return r&&c?this.deliverFreeTextAnswer(t,c.optionId,r):{delivered:!1,errorCode:"question_answer_invalid",errorMsg:"The selected answer does not match a pending question option. The question is still open; please tap one of the options on the card (free-text answers are not supported).",kind:"question_reply_invalid"}}try{await this.acpClient.respondPermissionOption(t,o.optionId)}catch(c){return{delivered:!1,errorCode:"question_reply_failed",errorMsg:c instanceof Error?c.message:String(c),kind:"question_reply_failed"}}this.pendingQuestions.delete(t);const l=/_skip$/.test(o.optionId);return this.captureAuditEvent({type:m.PermissionResponse,requestId:t,permissionDecision:l?"deny":"allow"}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun)),{delivered:!0,result:{request_id:t,resolution:l?"cancel":"answer"},kind:l?"question_reply_cancel":"question_reply"}}async deliverFreeTextAnswer(e,t,i){const s=this.acpClient;if(!s)return{delivered:!1,errorCode:"interaction_request_not_pending",errorMsg:"The question is no longer pending; the reply was not delivered.",kind:"question_reply_not_pending"};try{await s.respondPermissionOption(e,t)}catch(o){return{delivered:!1,errorCode:"question_reply_failed",errorMsg:o instanceof Error?o.message:String(o),kind:"question_reply_failed"}}this.pendingQuestions.delete(e),this.captureAuditEvent({type:m.PermissionResponse,requestId:e,permissionDecision:"deny"});const n=Date.now()+Ne;for(;;)try{await s.send(i);break}catch(o){if(!X(o)||Date.now()>=n)return a.warn("acp-adapter","free-text answer delivery failed after question dismissed",{requestId:e,error:A(o)}),{delivered:!1,errorCode:"question_free_text_delivery_failed",errorMsg:"The question was dismissed, but the free-text answer could not be delivered while the agent was busy. Please resend it as a normal message.",kind:"question_reply_failed"};await Oe(qe)}return a.info("acp-adapter","free-text answer delivered via skip + follow-up prompt",{requestId:e}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun)),{delivered:!0,result:{request_id:e,resolution:"answer"},kind:"question_reply_free_text"}}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,t){const i=this.sessionBindings.get(e);if(i)try{return await y.stat(i),!1}catch{a.info("acp-adapter",`Stale binding for session ${e}: ${i} no longer exists, allowing rebind to ${t}`),this.sessionBindings.delete(e)}return this.sessionBindings.set(e,t),this.bindingStore&&this.bindingStore.set(e,t),this.maybeRefreshKiroSkills(t),!this.sessionConnected&&this.agentProcess?.alive?this.connectSession(t).then(()=>!0).catch(s=>{a.error("acp-adapter",`Failed to create session on bind: ${A(s)}`);const n=this.buildToolbarContext("bind_failed",t),o=Array.isArray(n.available_models)&&n.available_models.length>0;return this.callbacks.sendUpdateBindingCard(e,"failed",t,o?n:void 0),!0}):(this.acpClient?.isAlive&&(this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(e,this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(e,"connected",t,this.buildToolbarContext("binding_ready",t))),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 t=String(e??"").trim()||void 0;let i=[];try{i=U({mode:"kiro",projectDir:t})}catch(s){a.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(i,t)}catch(s){a.warn("acp-adapter",`onSkillsUpdate failed: ${s instanceof Error?s.message:String(s)}`)}}replayDeferredEvents(e){const t=this.deferredEvents.get(e);if(!t||t.length===0)return;if(this.activeRun){a.info("acp-adapter",`Cannot replay deferred events for session ${e}: agent busy`);return}const i=t.shift();i&&(t.length===0?(this.deferredEvents.delete(e),this.cancelDeferredTimer(e)):this.deferredEvents.set(e,t),a.info("acp-adapter",`Replaying deferred event ${i.event.event_id} for session ${e} (remaining: ${t.length})`),this.startRun(i.event,!1))}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.suppressOrphanOutput(z),a.info("acp-adapter",`Compaction finished (${e}); replaying deferred events`),this.replayNextDeferredEvent();const t=this.compactionDoneResolver;this.compactionDoneResolver=null,t?.()}replayNextDeferredEvent(){if(this.activeRun||this.compacting)return;const e=this.deferredEvents.keys().next().value;e&&this.replayDeferredEvents(e)}announceDeferredComposing(e){}sendTerminalEventWithoutRun(e,t,i){this.emit("eventDone",e),this.callbacks.sendEventResult(e,t,i)}deliverInboundEvent(e){if(this.eventResults?.has(e.session_id,e.event_id)){const t=this.eventResults.get(e.session_id,e.event_id);a.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${t.status})`),this.sendTerminalEventWithoutRun(e.event_id,t.status,t.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.sendTerminalEventWithoutRun(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){a.warn("acp-adapter",`Event ${e.event_id} cannot start: agent process not alive; requesting session recovery`),this.emit("internalError",{eventId:e.event_id,sessionId:e.session_id,errorMsg:"agent not alive",replayOriginalContent:!0});return}e.session_id&&e.session_id!==this.currentAibotSessionId&&(this.currentAibotSessionId=e.session_id),this.startRun(e,!1)}deliverStopEvent(e,t){this.acpClient?(this.acpClient.cancel().catch(()=>{}),this.flushStream()):this.flushStream(),this.activeRun?.eventId===e&&this.finishRun("canceled","stopped by user")}deferEvent(e){const t=this.deferredEvents.get(e.session_id)??[];t.some(i=>i.event.event_id===e.event_id)||(t.push({event:e,queuedAt:Date.now()}),this.deferredEvents.set(e.session_id,t),a.info("acp-adapter",`Deferred event ${e.event_id} for session ${e.session_id} (queue: ${t.length})`),this.scheduleDeferredTimeout(e.session_id))}deferredTimers=new Map;rejectDeferredEvents(e){for(const[t,i]of this.deferredEvents)for(const{event:s}of i)a.info("acp-adapter",`Rejecting deferred event ${s.event_id}: ${e}`),this.sendTerminalEventWithoutRun(s.event_id,"failed",e);this.deferredEvents.clear(),this.cancelAllDeferredTimers()}cancelDeferredTimer(e){const t=this.deferredTimers.get(e);t&&(clearTimeout(t),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 t=3e4,i=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 ${t/1e3}s (no ACP client)`),this.deferredEvents.delete(e);for(const{event:n}of s)this.sendTerminalEventWithoutRun(n.event_id,"failed","agent initialization timed out")}},t);this.deferredTimers.set(e,i)}startRun(e,t){if(!this.acpClient?.isAlive){this.deferEvent(e);return}const i=`acp_${++this.clientMsgSeq}_${Date.now()}`;this.activeRun={eventId:e.event_id,sessionId:e.session_id,threadId:e.thread_id,clientMsgIdBase:i,currentClientMsgId:i,currentSegmentIndex:0,chunkSeq:0,buffer:"",quotedStream:new S,markdownSegmenter:new me,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:t,lastProgressAt:Date.now(),lastIdleCheckAt:Date.now(),idleNoProgressCount:0,consecutivePingFailures:0,lastToolProgress:new Map,lastToolProgressSentAt:new Map,toolNamesById:new Map,sentToolUseIds:new Set,lastToolUse:null,lastToolResult:null,busyRetryTimer:null,busyWaitSince:0,busyRetryCount:0,busyWaiting:!1};const s=this.activeRun;this.beginAuditCapture(e);const n=fe(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=ge(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),r=this.injectQuoteProtocolOnce(s.sessionId,o),l=this.identity.injectOnce(s.sessionId,r);if(this.bindingStore&&s.sessionId){const u=this.callbacks.getAgentProfile?.();this.bindingStore.setIdentitySnapshot(s.sessionId,u?.agentName?.trim()??"",u?.introduction?.trim()??"")}const c=this.injectSessionContextOnce(s.sessionId,l),h=this.injectRecoveryContext(s.sessionId,c);this.sendPromptWithRetry(s,h)}injectQuoteProtocolOnce(e,t){return!e||this.quoteProtocolTaughtSessions.has(e)?t:(this.quoteProtocolTaughtSessions.add(e),T(t,De))}injectSessionContextOnce(e,t){return!e||!this.sessionContextPending?t:(this.sessionContextPending=!1,a.info("acp-adapter",`Injected Grix session context for session=${e}`),T(t,se(e,this.callbacks.getAgentId?.())))}injectRecoveryContext(e,t){const i=this.recoveryContextBySessionId.get(e);return i?(this.recoveryContextBySessionId.delete(e),a.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${i.length})`),T(t,`[recovery-context]
1
+ import{fileURLToPath as J}from"node:url";import p from"node:path";import{homedir as I}from"node:os";import{promises as A,statSync as M}from"node:fs";import{EventEmitter as $}from"node:events";import{spawn as x}from"node:child_process";import{AgentProcess as Z}from"../../agent/process.js";import{hasChildProcesses as ee,listGroupPids as te}from"../../core/runtime/spawn.js";import{applyNativeProviderConfig as ie,agentIdOf as y}from"../shared/native-provider-config.js";import{applyMcodeProviderConfig as se}from"./mcode-provider-config.js";import{buildSessionContextBlock as ne}from"../shared/session-context.js";import{IdentityInjector as oe}from"../shared/identity-injector.js";import{compactToolInputForWire as q}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as re,resolveKimiCodeHome as ae}from"../../default-skills/index.js";import{resolveHermesHomeDir as le}from"../shared/native-provider-config.js";import{prepareKimiLayerPlugin as de}from"../../kimi-plugins/index.js";import{AcpClient as ce,AcpAuthRequiredError as ue,isAuthRequiredError as he}from"../../protocol/acp-client.js";import{AgentEventType as f}from"../../types/events.js";import{InternalApiServer as pe}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as fe}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as S}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as me}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as ge}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as ve}from"../../core/protocol/message-metadata.js";import{insertBeforeTrailingMessageReferences as T}from"../../core/protocol/message-reference.js";import{log as a}from"../../core/log/index.js";function N(c,e=2e3){let t;try{const i=new WeakSet;t=JSON.stringify(c,(s,n)=>{if(n instanceof Error)return{name:n.name,message:n.message};if(typeof n=="function")return"[function]";if(typeof n=="bigint"||typeof n=="symbol")return n.toString();if(typeof n=="object"&&n!==null){if(i.has(n))return"[circular]";i.add(n)}return n})}catch(i){t=`[stringify failed: ${i instanceof Error?i.message:String(i)}]`}return t===void 0?"[undefined]":t.length<=e?t:`${t.slice(0,e)}...[+${t.length-e}b]`}const Ie=5e3;function O(c,e){const t=e.toolName??"";return e.toolCallId&&(!t||t===e.toolCallId)?c.toolNamesById.get(e.toolCallId)??t:t}function _(c){const e=String(c??"").trim();if(!e)return"";try{const t=JSON.parse(e);return JSON.stringify(b(t))}catch{return e}}function b(c){if(c===null||typeof c!="object")return c;if(Array.isArray(c))return c.map(b);const e=c,t=Object.keys(e).sort(),i={};for(const s of t)i[s]=b(e[s]);return i}function D(c){const e=Array.isArray(c)?c:[c],t=[];for(const i of e){if(!i||typeof i!="object")continue;const s=i;if(typeof s.text=="string"&&s.text.trim()){t.push(s.text.trim());continue}const n=s.content;if(n&&typeof n=="object"&&!Array.isArray(n)){const o=n.text;typeof o=="string"&&o.trim()&&t.push(o.trim())}}return t.join(`
2
+ `)}function ye(c,e,t){const i=c&&typeof c=="object"?c:{},s=i.toolCall&&typeof i.toolCall=="object"?i.toolCall:{},n=[s.rawInput,s.raw_input,s.input,s.arguments,s.args,D(s.content),s.content,s.description,i.tool_input,i.toolInput,e];for(const o of n){const r=q(o);if(r&&r!==t)return r}return""}const U=/^q\d+_opt_\d+$/,k=/^q\d+_skip$/,Ae=new Set(["allow_once","allow_always","reject_once","reject_always"]);function Ce(c){if(!`${c.toolName} ${c.toolTitle}`.toLowerCase().replace(/[^a-z]/g,"").includes("askuserquestion"))return null;const t=c.options;if(t.length===0)return null;const i=t.some(l=>U.test(l.optionId)),s=i?t.filter(l=>U.test(l.optionId)):t.filter(l=>!k.test(l.optionId));if(s.length===0||!i&&s.every(l=>Ae.has(l.kind)))return null;const n=c.rawParams&&typeof c.rawParams=="object"?c.rawParams:{},o=n.toolCall&&typeof n.toolCall=="object"?n.toolCall:{},r=D(o.content).trim();return r?{prompt:r,answerOptions:s}:null}import{scanSkills as B}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as Se,checkKimiSessionActivity as _e,checkQoderSessionActivity as F,checkDimSessionActivity as we,hasRunningKimiDetachedTask as Te}from"./session-activity.js";import{evaluateGrokLiveness as L,grokEventsByteSize as be}from"./grok-liveness.js";import{resolveCliPath as ke,getCliVersion as Re}from"../../core/util/cli-probe.js";const R=p.dirname(J(import.meta.url)),j=[{modelId:"deepseek-v4-pro",name:"DeepSeek V4 Pro"},{modelId:"deepseek-v4-flash",name:"DeepSeek V4 Flash"}],P=[{modelId:"kimi-code/kimi-for-coding",name:"K2.7 Coding"},{modelId:"kimi-code/kimi-for-coding-highspeed",name:"K2.7 Coding Highspeed"},{modelId:"kimi-code/k3",name:"K3"}];function E(c,e){const t=Number(process.env[c]);return Number.isFinite(t)&&t>0?t:e}const Pe=200,Ee=60*1e3,Me=600*1e3,w=E("GRIX_ACP_TURN_INACTIVITY_TIMEOUT_MS",900*1e3),K=2,H=5e3,G=80,W=1200*1e3,$e=E("GRIX_ACP_BUSY_RETRY_INTERVAL_MS",5e3),Q=E("GRIX_ACP_BUSY_WAIT_MAX_MS",1800*1e3),xe=6e4,qe=2e3,Ne=12e3,z=3e3,X=3e4,Oe=1e3,De=1e4;function Ue(c){return new Promise(e=>setTimeout(e,c))}const Be=["[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 Fe(c){return!(!(c instanceof Error)||c.code!==-32603||he(c))}function V(c){if(!(c instanceof Error))return!1;const e=c.data;return e&&typeof e=="object"&&e.code==="turn.agent_busy"?!0:/cannot launch a new turn/i.test(c.message)}function C(c){if(!(c instanceof Error))return String(c);let e=c.message;const t=c.data;return t&&(e+=` data=${JSON.stringify(t)}`),e}function Le(c){return c.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class Y extends ${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;pendingQuestions=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;newSessionParams;acpMcpTools;terminalAuthChild=null;rawTransport;approvalMode;autoInjectArgs;nativeProviderScope;reasonixAuditTranscriptPath;auditAgentType;trimToolWirePayload;activeAuditEventId=null;auditCaptures=new Map;completedAuditBoundaries=new Map;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;bridgeLog;sessionConnected=!1;sessionContextPending=!1;baselineGroupPids=[];rawEventSeq=0;recoveryContextBySessionId=new Map;quoteProtocolTaughtSessions=new Set;identity;orphanBuffer="";orphanQuotedStream=new S;orphanFlushTimer=null;orphanSessionId=null;orphanSuppressedUntil=0;constructor(e,t,i){super(),this.config=e,this.callbacks=t,this.identity=new oe("acp-adapter",t.getAgentProfile),this.bridgeLog=i?.bridgeLog??null,this.acpAuthMethod=i?.acpAuthMethod,this.acpInitialMode=i?.acpInitialMode,this.acpInitialModel=i?.acpInitialModel,this.newSessionParams=i?.newSessionParams,this.acpMcpTools=i?.acpMcpTools??!0,this.rawTransport=i?.rawTransport??!1,this.approvalMode=i?.approvalMode??"default",this.autoInjectArgs=i?.autoInjectArgs,this.nativeProviderScope=i?.nativeProviderScope,this.reasonixAuditTranscriptPath=i?.reasonixAuditTranscriptPath,this.auditAgentType=String(i?.agentType??p.basename(e.command)).trim().toLowerCase();const s=p.basename(e.command).toLowerCase(),n=this.auditAgentType==="copilot"||/^copilot(?:\.cmd)?$/i.test(s)||/^gh(?:\.exe)?$/i.test(s)&&(e.args??[]).includes("copilot");if(this.trimToolWirePayload=["kimi","kiro","qwen","reasonix","copilot"].includes(this.auditAgentType)||s.includes("kimi")||s==="kiro-cli"||/^qwen(?:\.cmd)?$/i.test(s)||s.includes("reasonix")||n,this.bindingStore=i?.bindingStore??null,this.currentAibotSessionId=i?.aibotSessionId?String(i.aibotSessionId).trim():void 0,i?.eventResultsPath&&(this.eventResults=new fe(i.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const o=this.bindingStore.get(this.currentAibotSessionId);o?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,o.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.finishRun("canceled","adapter stopped"),this.flushOrphanBuffer(),this.acpClient&&(this.acpClient.dispose(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.baselineGroupPids=[],this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.terminalAuthChild&&(this.terminalAuthChild.kill("SIGKILL"),this.terminalAuthChild=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,t){}async destroySession(e){this.quoteProtocolTaughtSessions.delete(e),this.identity.forgetSession(e)}onAgentProfileChanged(){this.identity.onProfileChanged()}sendPrompt(e){const t=new je(e.adapterSessionId);return this.acpClient?.isAlive&&this.acpClient.send(e.text).catch(i=>{t.emitError(i instanceof Error?i:new Error(String(i)))}),t}async cancel(e){this.activeRun&&this.acpClient&&(await this.acpClient.cancel(),this.flushStream(),this.finishRun("canceled","stopped by user"),this.suppressOrphanOutput(X))}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.snapshotAuditBoundary(this.activeRun.eventId),this.clearRunTimers(this.activeRun),this.activeRun=null)}takeAuditBoundary(e){const t=this.completedAuditBoundaries.get(e);return t&&this.completedAuditBoundaries.delete(e),t}clearRunTimers(e){e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),e.busyRetryTimer&&(clearTimeout(e.busyRetryTimer),e.busyRetryTimer=null)}getMcpConfig(){if(!this.internalApi)return null;this.internalApi.setMcpBridgeUpHandler(t=>this.onMcpBridgeUp(t));const e=p.resolve(R,"../../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?ee(e,[e,...this.baselineGroupPids]):!1}captureBaselineGroupPids(){const e=this.agentProcess?.pid;e&&te(e).then(t=>{this.baselineGroupPids=t}).catch(()=>{})}async probe(e){const t=this.config.command||"gemini",i=await ke(t),s=i!==null;let n=null,o;if(s){const h=await Re(t);n=h.version,h.error&&(o=h.error)}else o={code:"cli_not_found",message:`command not found: ${t}`};const r=this.acpClient?.model??null,l=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:t,installed:s,path:i,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:l.alive,busy:l.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,t,i){return this.acpClient?.supportsCommandsExecute?this.execCommandViaAcp(e,t):this.execCommandFallback(e,t)}async execCommandViaAcp(e,t){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 i=await this.acpClient.executeCommand(e,t||void 0);return this.finishCompaction("acp-commands-execute"),{status:i.status==="failed"?"failed":"ok",message:i.message??"Compacted",data:i.data}}catch(i){return this.finishCompaction("error"),i?.code===-32601?this.execCommandFallback(e,t):{status:"failed",message:`compact failed: ${i instanceof Error?i.message:i}`}}}try{const i=await this.acpClient.executeCommand(e,t||void 0);return{status:i.status??"ok",message:i.message,data:i.options?{command:e,options:i.options}:i.data}}catch(i){return i?.code===-32601?this.execCommandFallback(e,t):{status:"failed",message:`Command failed: ${i instanceof Error?i.message:i}`}}}async execCommandFallback(e,t){try{switch(e){case"model":{const i=t.trim();if(i)return await this.setModel(i)?{status:"ok",message:`Model set to ${i}`}:{status:"failed",message:`Failed to set model: ${i}`};const s=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${s.currentModelId||"unknown"}`,data:s}}case"mode":{const i=t.trim();if(i)return await this.setMode(i)?{status:"ok",message:`Mode set to ${i}`}:{status:"failed",message:`Failed to set mode: ${i}`};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 i=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 i,{status:"ok",message:"Compacted"}}case"status":{const i=this.getStatus(),s=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${i.alive}, Busy: ${i.busy}, Model: ${s?.currentModelId??"unknown"}, Mode: ${s?.currentModeId??"unknown"}`,data:{alive:i.alive,busy:i.busy,sessions:i.sessions,model:s?.currentModelId??"",mode:s?.currentModeId??""}}}case"skills":{const i=B({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),s=i.map(n=>`- ${n.name}${n.trigger?` (${n.trigger})`:""} [${n.source}]: ${n.description}`);return{status:"ok",message:s.length>0?s.join(`
3
+ `):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}get acpSessionOptions(){const e=this.acpClient?.sessionOptions??null,t=this.isReasonix?j:this.isKimi?P:null;if(!t||e&&e.models.length>0)return e;const i=this.resolveFallbackCurrentModelId(t,e?.currentModelId??"");return{modes:e?.modes??[],currentModeId:e?.currentModeId??"",models:[...t],currentModelId:i}}resolveFallbackCurrentModelId(e,t){if(e.some(s=>s.modelId===t))return t;const i=(this.autoInjectArgs?.model??this.acpInitialModel??"").trim();return i&&e.some(s=>s.modelId===i)?i:e[0]?.modelId??""}get isReasonix(){return(this.config.command??"").includes("reasonix")}get isKimi(){return(this.config.command??"").includes("kimi")}buildToolbarContext(e,t){const i=this.acpClient?.sessionOptions,s=i?.currentModeId??"";let n=i?.currentModelId??"";const o=i?.modes.map(m=>({id:m.id,name:m.name}))??[],r=i?.models&&i.models.length>0,l=r?i.models:this.isReasonix?j:this.isKimi?P:[],d=r?"acp":this.isReasonix?"reasonix_fallback":this.isKimi?"kimi_fallback":"empty";this.isKimi&&!r&&a.warn("acp-adapter",`[toolbar-diag] kimi ACP session returned no models; using static fallback. command=${this.config.command} fallbackIds=${JSON.stringify(P.map(m=>m.modelId))}`),a.info("acp-adapter",`[toolbar-diag] buildToolbarContext outcome=${e} agent=${this.config.command} modelSource=${d} acpModelCount=${i?.models?.length??0} finalModelCount=${l.length}`);const h=l.map(m=>({modelId:m.modelId,name:m.name})),u=l.map(m=>({id:m.modelId,displayName:m.name})),g=i?.modes.map(m=>({id:m.id,displayName:m.name}))??[];u.length>0&&!u.some(m=>m.id===n)&&(n=this.resolveFallbackCurrentModelId(l,n));const v={outcome:e,...t?{cwd:t}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:u,available_modes:g,availableModels:u,availableModes:g,models:h,modes:o};return a.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(u.map(m=>m.id))} currentModelId=${n}`),v}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,t){const i=this.pendingApprovals.get(e);return i?(a.info("acp-adapter","handleAcpApprovalAction resolving",{toolCallId:e,requestId:i,behavior:t}),this.pendingApprovals.delete(e),this.acpClient&&this.acpClient.respondPermission(i,{behavior:t}).catch(s=>{a.error("acp-adapter",`Failed to respond to permission: ${s}`)}),this.captureAuditEvent({type:f.PermissionResponse,requestId:i,toolCallId:e,permissionDecision:t}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun)),!0):(a.warn("acp-adapter","handleAcpApprovalAction: no pending approval",{toolCallId:e,behavior:t,pendingCount:this.pendingApprovals.size}),!1)}handleExecApprovalEvent(e){const t=String(e.tool_call_id??e.approval_command_id??e.approval_id??e.exec_context_id??"");return t?this.handleAcpApprovalAction(t,e.decision)?{delivered:!0}:{delivered:!1,errorCode:"approval_not_found",errorMsg:`no pending approval for tool_call_id: ${t}`}:{delivered:!1,errorCode:"approval_id_required",errorMsg:"approval id is required"}}respondToPermission(e,t){this.acpClient&&this.acpClient.respondPermission(e,t).catch(i=>{a.error("acp-adapter",`Failed to respond to permission: ${i}`)}),this.captureAuditEvent({type:f.PermissionResponse,requestId:e,permissionDecision:t.behavior}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun))}async handleLocalAction(e){const t=e.action_type??"",i=e.params??{};if(t==="claude_interaction_reply"&&String(i.kind??"")==="permission"){const s=i.resolution&&typeof i.resolution=="object"?i.resolution:{},n=String(s.value??i.decision??"");return["allow","allow-once","allow-always","deny"].includes(n)?this.handleLocalAction({...e,action_type:n==="deny"?"permission_reject":"permission_approve",params:{...i,approval_command_id:i.request_id,decision:n}}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"permission_decision_invalid","permission decision is invalid"),{handled:!0,kind:"approval"})}if(t==="claude_interaction_reply")return this.handleAcpQuestionReply(e);if(t==="exec_approve"||t==="exec_reject"||t==="permission_approve"||t==="permission_reject"){const s=String(i.tool_call_id??i.approval_command_id??i.approval_id??i.exec_context_id??""),n=t==="exec_approve"||t==="permission_approve",o=String(i.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:""}}async handleQuestionReplyEvent(e){let t;if(e.action)t={type:"action",value:e.action};else if(e.response?.type==="single")t={type:"text",value:e.response.value};else if(e.response?.type==="map")t={type:"map",entries:e.response.entries};else return{delivered:!1,errorCode:"resolution_invalid",errorMsg:"Question reply response is invalid."};return this.deliverQuestionReply({request_id:e.request_id,resolution:t})}async handleAcpQuestionReply(e){const t=await this.deliverQuestionReply(e.params??{});return t.delivered?(this.callbacks.sendLocalActionResult(e.action_id,"ok",t.result),{handled:!0,kind:t.kind??"question_reply"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,t.errorCode,t.errorMsg),{handled:!0,kind:t.kind??"question_reply_failed"})}async deliverQuestionReply(e){const t=String(e.request_id??"").trim(),i=this.pendingQuestions.get(t);if(!t||!i||!this.acpClient)return{delivered:!1,errorCode:"interaction_request_not_pending",errorMsg:"The question is no longer pending; the reply was not delivered.",kind:"question_reply_not_pending"};const s=e.resolution&&typeof e.resolution=="object"?e.resolution:{},n=String(s.type??"");let o,r="";if(n==="action"&&String(s.value??"")==="cancel")o=i.options.find(d=>/_skip$/.test(d.optionId)||d.kind.toLowerCase().includes("reject"));else{if(n==="text")r=String(s.value??"").trim();else if(n==="map"){const d=Array.isArray(s.entries)?s.entries:[];r=String(d[0]?.value??"").trim()}o=i.options.find(d=>d.name.trim()===r||d.optionId===r)}if(!o){const d=i.options.find(h=>k.test(h.optionId));return r&&d?this.deliverFreeTextAnswer(t,d.optionId,r):{delivered:!1,errorCode:"question_answer_invalid",errorMsg:"The selected answer does not match a pending question option. The question is still open; please tap one of the options on the card (free-text answers are not supported).",kind:"question_reply_invalid"}}try{await this.acpClient.respondPermissionOption(t,o.optionId)}catch(d){return{delivered:!1,errorCode:"question_reply_failed",errorMsg:d instanceof Error?d.message:String(d),kind:"question_reply_failed"}}this.pendingQuestions.delete(t);const l=/_skip$/.test(o.optionId);return this.captureAuditEvent({type:f.PermissionResponse,requestId:t,permissionDecision:l?"deny":"allow"}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun)),{delivered:!0,result:{request_id:t,resolution:l?"cancel":"answer"},kind:l?"question_reply_cancel":"question_reply"}}async deliverFreeTextAnswer(e,t,i){const s=this.acpClient;if(!s)return{delivered:!1,errorCode:"interaction_request_not_pending",errorMsg:"The question is no longer pending; the reply was not delivered.",kind:"question_reply_not_pending"};try{await s.respondPermissionOption(e,t)}catch(o){return{delivered:!1,errorCode:"question_reply_failed",errorMsg:o instanceof Error?o.message:String(o),kind:"question_reply_failed"}}this.pendingQuestions.delete(e),this.captureAuditEvent({type:f.PermissionResponse,requestId:e,permissionDecision:"deny"});const n=Date.now()+De;for(;;)try{await s.send(i);break}catch(o){if(!V(o)||Date.now()>=n)return a.warn("acp-adapter","free-text answer delivery failed after question dismissed",{requestId:e,error:C(o)}),{delivered:!1,errorCode:"question_free_text_delivery_failed",errorMsg:"The question was dismissed, but the free-text answer could not be delivered while the agent was busy. Please resend it as a normal message.",kind:"question_reply_failed"};await Ue(Oe)}return a.info("acp-adapter","free-text answer delivered via skip + follow-up prompt",{requestId:e}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun)),{delivered:!0,result:{request_id:e,resolution:"answer"},kind:"question_reply_free_text"}}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,t){const i=this.sessionBindings.get(e);if(i)try{return await A.stat(i),!1}catch{a.info("acp-adapter",`Stale binding for session ${e}: ${i} no longer exists, allowing rebind to ${t}`),this.sessionBindings.delete(e)}return this.sessionBindings.set(e,t),this.bindingStore&&this.bindingStore.set(e,t),this.maybeRefreshKiroSkills(t),!this.sessionConnected&&this.agentProcess?.alive?this.connectSession(t).then(()=>!0).catch(s=>{a.error("acp-adapter",`Failed to create session on bind: ${C(s)}`);const n=this.buildToolbarContext("bind_failed",t),o=Array.isArray(n.available_models)&&n.available_models.length>0;return this.callbacks.sendUpdateBindingCard(e,"failed",t,o?n:void 0),!0}):(this.acpClient?.isAlive&&(this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(e,this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(e,"connected",t,this.buildToolbarContext("binding_ready",t))),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 t=String(e??"").trim()||void 0;let i=[];try{i=B({mode:"kiro",projectDir:t})}catch(s){a.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(i,t)}catch(s){a.warn("acp-adapter",`onSkillsUpdate failed: ${s instanceof Error?s.message:String(s)}`)}}replayDeferredEvents(e){const t=this.deferredEvents.get(e);if(!t||t.length===0)return;if(this.activeRun){a.info("acp-adapter",`Cannot replay deferred events for session ${e}: agent busy`);return}const i=t.shift();i&&(t.length===0?(this.deferredEvents.delete(e),this.cancelDeferredTimer(e)):this.deferredEvents.set(e,t),a.info("acp-adapter",`Replaying deferred event ${i.event.event_id} for session ${e} (remaining: ${t.length})`),this.startRun(i.event,!1))}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.suppressOrphanOutput(X),a.info("acp-adapter",`Compaction finished (${e}); replaying deferred events`),this.replayNextDeferredEvent();const t=this.compactionDoneResolver;this.compactionDoneResolver=null,t?.()}replayNextDeferredEvent(){if(this.activeRun||this.compacting)return;const e=this.deferredEvents.keys().next().value;e&&this.replayDeferredEvents(e)}announceDeferredComposing(e){}sendTerminalEventWithoutRun(e,t,i){this.emit("eventDone",e),this.callbacks.sendEventResult(e,t,i)}deliverInboundEvent(e){if(this.eventResults?.has(e.session_id,e.event_id)){const t=this.eventResults.get(e.session_id,e.event_id);a.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${t.status})`),this.sendTerminalEventWithoutRun(e.event_id,t.status,t.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.sendTerminalEventWithoutRun(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){a.warn("acp-adapter",`Event ${e.event_id} cannot start: agent process not alive; requesting session recovery`),this.emit("internalError",{eventId:e.event_id,sessionId:e.session_id,errorMsg:"agent not alive",replayOriginalContent:!0});return}e.session_id&&e.session_id!==this.currentAibotSessionId&&(this.currentAibotSessionId=e.session_id),this.startRun(e,!1)}deliverStopEvent(e,t){this.acpClient?(this.acpClient.cancel().catch(()=>{}),this.flushStream()):this.flushStream(),this.activeRun?.eventId===e&&this.finishRun("canceled","stopped by user")}deferEvent(e){const t=this.deferredEvents.get(e.session_id)??[];t.some(i=>i.event.event_id===e.event_id)||(t.push({event:e,queuedAt:Date.now()}),this.deferredEvents.set(e.session_id,t),a.info("acp-adapter",`Deferred event ${e.event_id} for session ${e.session_id} (queue: ${t.length})`),this.scheduleDeferredTimeout(e.session_id))}deferredTimers=new Map;rejectDeferredEvents(e){for(const[t,i]of this.deferredEvents)for(const{event:s}of i)a.info("acp-adapter",`Rejecting deferred event ${s.event_id}: ${e}`),this.sendTerminalEventWithoutRun(s.event_id,"failed",e);this.deferredEvents.clear(),this.cancelAllDeferredTimers()}cancelDeferredTimer(e){const t=this.deferredTimers.get(e);t&&(clearTimeout(t),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 t=3e4,i=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 ${t/1e3}s (no ACP client)`),this.deferredEvents.delete(e);for(const{event:n}of s)this.sendTerminalEventWithoutRun(n.event_id,"failed","agent initialization timed out")}},t);this.deferredTimers.set(e,i)}startRun(e,t){if(!this.acpClient?.isAlive){this.deferEvent(e);return}const i=`acp_${++this.clientMsgSeq}_${Date.now()}`;this.activeRun={eventId:e.event_id,sessionId:e.session_id,threadId:e.thread_id,clientMsgIdBase:i,currentClientMsgId:i,currentSegmentIndex:0,chunkSeq:0,buffer:"",quotedStream:new S,markdownSegmenter:new me,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:t,lastProgressAt:Date.now(),lastIdleCheckAt:Date.now(),idleNoProgressCount:0,consecutivePingFailures:0,lastToolProgress:new Map,lastToolProgressSentAt:new Map,toolNamesById:new Map,sentToolUseIds:new Set,lastToolUse:null,lastToolResult:null,busyRetryTimer:null,busyWaitSince:0,busyRetryCount:0,busyWaiting:!1};const s=this.activeRun;this.beginAuditCapture(e);const n=ge(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=ve(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),r=this.injectQuoteProtocolOnce(s.sessionId,o),l=this.identity.injectOnce(s.sessionId,r);if(this.bindingStore&&s.sessionId){const u=this.callbacks.getAgentProfile?.();this.bindingStore.setIdentitySnapshot(s.sessionId,u?.agentName?.trim()??"",u?.introduction?.trim()??"")}const d=this.injectSessionContextOnce(s.sessionId,l),h=this.injectRecoveryContext(s.sessionId,d);y(this.config.command)==="grok"&&(s.grokEventsOffset=be(this.acpClient?.sessionId)),this.sendPromptWithRetry(s,h)}injectQuoteProtocolOnce(e,t){return!e||this.quoteProtocolTaughtSessions.has(e)?t:(this.quoteProtocolTaughtSessions.add(e),T(t,Be))}injectSessionContextOnce(e,t){return!e||!this.sessionContextPending?t:(this.sessionContextPending=!1,a.info("acp-adapter",`Injected Grix session context for session=${e}`),T(t,ne(e,this.callbacks.getAgentId?.())))}injectRecoveryContext(e,t){const i=this.recoveryContextBySessionId.get(e);return i?(this.recoveryContextBySessionId.delete(e),a.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${i.length})`),T(t,`[recovery-context]
4
4
  ${i}
5
- [/recovery-context]`)):t}sendPromptWithRetry(e,t){this.acpClient.send(t).then(()=>{this.activeRun===e&&(e.busyWaiting=!1)}).catch(i=>{if(X(i)){this.scheduleAgentBusyRetry(e,t,A(i));return}if(a.error("acp-adapter",`Prompt failed: ${A(i)}`),Fe(i)){const n=A(i),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=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>=Q){a.error("acp-adapter",`agent busy wait exceeded ${Math.round(Q/6e4)}min, giving up event=${e.eventId}: ${i}`),e.buffer+=`
5
+ [/recovery-context]`)):t}sendPromptWithRetry(e,t){this.acpClient.send(t).then(()=>{this.activeRun===e&&(e.busyWaiting=!1)}).catch(i=>{if(V(i)){this.scheduleAgentBusyRetry(e,t,C(i));return}if(a.error("acp-adapter",`Prompt failed: ${C(i)}`),Fe(i)){const n=C(i),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=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>=Q){a.error("acp-adapter",`agent busy wait exceeded ${Math.round(Q/6e4)}min, giving up event=${e.eventId}: ${i}`),e.buffer+=`
6
6
 
7
- 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(Ee*2**e.busyRetryCount,$e);e.busyRetryCount+=1,a.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",this.describeUnreachable());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"),C(this.config.command)==="qwenpaw"&&this.config.env?.OPENAI_BASE_URL&&!e.includes("--runtime-provider")&&e.push("--runtime-provider","openai-env"),C(this.config.command)==="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 y.mkdir(p.dirname(this.reasonixAuditTranscriptPath),{recursive:!0,mode:448}),await(await y.open(this.reasonixAuditTranscriptPath,"a",384)).close(),await y.chmod(this.reasonixAuditTranscriptPath,384),e.push("--transcript",this.reasonixAuditTranscriptPath)),this.agentProcess=new J(this.bridgeLog),this.baselineGroupPids=[];const i=this.resolveCwd();try{if(!(await y.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}if(C(this.config.command)==="qwenpaw"&&!e.includes("--workspace")&&e.push("--workspace",i),await te(this.config.command,i,this.config.env,this.nativeProviderScope).catch(s=>{a.warn("acp-adapter",`native provider config skipped: ${s instanceof Error?s.message:String(s)}`)}),C(this.config.command)==="mcode"){const s=this.config.env??{};await ie(this.config.command,{baseUrl:s.GRIX_MCODE_BASE_URL,apiKey:s.GRIX_MCODE_API_KEY,model:s.GRIX_MCODE_MODEL}).catch(n=>{a.warn("acp-adapter",`mcode provider config skipped: ${n instanceof Error?n.message:String(n)}`)})}a.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=le({...process.env,...this.config.env});a.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 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.lastExitCode=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 t;this.acpMcpTools&&(t=await this.startInternalApiAndMcp(e||this.resolveCwd())),this.acpClient=new ce,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&&(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 i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=!!i&&i!=="__continue__",n=async l=>{const c=this.agentProcess?.transport;if(!c)throw new Error("agent process exited before the ACP session could connect");await this.acpClient.connect({transport:c,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:!0,cwd:e||this.resolveCwd(),mcpServers:t,sessionId:l,newSessionParams:this.newSessionParams})};let o=!1,r;try{await n(i)}catch(l){if(l instanceof de){await this.handleAuthRequired(l);return}if(!i)throw this.handleConnectFailure(l),l;if(a.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${A(l)}`),o=!0,r=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){a.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${i}`);const c=await this.buildKiroRecoveryContext(this.currentAibotSessionId,i,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=${i}`)}await n(void 0)}if(this.sessionConnected=!0,this.sessionContextPending=!s||o,!this.sessionContextPending){const l=this.callbacks.getAgentProfile?.(),c=l?.agentName?.trim()??"",h=l?.introduction?.trim()??"",u=I=>{const v=this.bindingStore?.getIdentitySnapshot(I);v&&v.name===c&&v.intro===h&&this.identity.markInjected(I)};this.currentAibotSessionId&&u(this.currentAibotSessionId);for(const I of this.sessionBindings.keys())u(I)}this.captureBaselineGroupPids(),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[l,c]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,c);o&&r&&(u.previous_session_id=r,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(l,"ready",c,u)}}async buildKiroRecoveryContext(e,t,i){try{const s=p.join(g(),".kiro","sessions","cli",`${t}.json`);await y.access(s),a.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,i);if(!n)return;const o=p.join(g(),".grix","data","session-summaries");await y.mkdir(o,{recursive:!0});const r=p.join(o,`${e}.md`),l=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
8
- `);return await y.writeFile(r,l,"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: ${A(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(`
10
- `)],n=await this.runCommandCapture("kiro-cli",s,t,45e3);if(!n)return;const o=Ue(n).trim(),r=o.indexOf("> "),l=r>=0?o.slice(r+2).trim():o;if(l)return a.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${o.length}, body_chars=${l.length})`),l.split(`
11
- `).filter(c=>!c.includes("Credits:")&&!c.includes("Time:")).join(`
12
- `).trim()}runCommandCapture(e,t,i,s){return new Promise((n,o)=>{const r=x(e,t,{cwd:i,stdio:["ignore","pipe","pipe"],windowsHide:!0});let l="",c="";const h=setTimeout(()=>{r.kill("SIGTERM"),o(new Error(`${e} timed out after ${s}ms`))},s);r.stdout.on("data",u=>{l+=String(u)}),r.stderr.on("data",u=>{c+=String(u)}),r.on("error",u=>{clearTimeout(h),o(u)}),r.on("close",u=>{if(clearTimeout(h),u===0){n(l||c);return}o(new Error(`${e} exited with code ${u}: ${c||l}`))})})}handleConnectFailure(e){a.error("acp-adapter",`ACP session creation failed: ${A(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 he,this.internalApi.setInvokeHandler(async(r,l,c,h)=>{if(r==="toolbar_set"&&!String(l?.session_id??"").trim()){const u=String(this.currentAibotSessionId??"").trim();u&&(l={...l,session_id:u})}return this.callbacks.agentInvoke(r,l,h)}),await this.internalApi.start(0),a.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(r=>this.onMcpBridgeUp(r));const t=this.config.command??"",i=C(t)==="qwen"?p.join(g(),".qwen","skills"):t.includes("gemini")?p.join(g(),".gemini","skills"):t.includes("kiro")?p.join(g(),".kiro","skills"):this.isReasonix?p.join(g(),".reasonix","skills"):this.isKimi?p.join(re({...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(g(),".copilot","skills"):this.auditAgentType==="hermes"?p.join(this.config.env?.HERMES_HOME?.trim()||ae({...process.env,...this.config.env},this.nativeProviderScope),"skills"):this.auditAgentType==="qodercli"?p.join(g(),".qoder","skills"):this.auditAgentType==="qoderclicn"?p.join(g(),".qoder-cn","skills"):this.auditAgentType==="traecli"?p.join(g(),".trae","skills"):this.auditAgentType==="codebuddy"?p.join(g(),".codebuddy","skills"):void 0;if(i){const r=oe(i);r.length>0&&a.info("acp-adapter",`Synced connector skills to ${i}: [${r.join(", ")}]`)}const s=p.resolve(k,"../../mcp/mcp-bridge-server.js"),n=this.internalApi.mcpBridgeWsUrl,o=p.resolve(k,"../../mcp/acp-mcp-server.js");if(this.isKimi){await this.internalApi.setCodebaseNavigatorCwd(e);const r={Authorization:`Bearer ${this.internalApi.token}`};return[{type:"http",name:"grix-app-bridge",url:this.internalApi.mcpBridgeHttpUrl,headers:r},{type:"http",name:"grix-connector-tools",url:this.internalApi.mcpToolsHttpUrl,headers:r},{type:"http",name:"codebase-navigator",url:this.internalApi.codebaseNavigatorHttpUrl,headers:r}]}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){a.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){a.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:
7
+ 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($e*2**e.busyRetryCount,xe);e.busyRetryCount+=1,a.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,this.refreshGrokLivenessBudget(e,s),e.busyRetryTimer=setTimeout(()=>{if(e.busyRetryTimer=null,this.activeRun===e){if(!this.acpClient?.isAlive){this.finishRun("failed",this.describeUnreachable());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"),y(this.config.command)==="qwenpaw"&&this.config.env?.OPENAI_BASE_URL&&!e.includes("--runtime-provider")&&e.push("--runtime-provider","openai-env"),y(this.config.command)==="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 A.mkdir(p.dirname(this.reasonixAuditTranscriptPath),{recursive:!0,mode:448}),await(await A.open(this.reasonixAuditTranscriptPath,"a",384)).close(),await A.chmod(this.reasonixAuditTranscriptPath,384),e.push("--transcript",this.reasonixAuditTranscriptPath)),this.agentProcess=new Z(this.bridgeLog),this.baselineGroupPids=[];const i=this.resolveCwd();try{if(!(await A.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}if(y(this.config.command)==="qwenpaw"&&!e.includes("--workspace")&&e.push("--workspace",i),await ie(this.config.command,i,this.config.env,this.nativeProviderScope).catch(s=>{a.warn("acp-adapter",`native provider config skipped: ${s instanceof Error?s.message:String(s)}`)}),y(this.config.command)==="mcode"){const s=this.config.env??{};await se(this.config.command,{baseUrl:s.GRIX_MCODE_BASE_URL,apiKey:s.GRIX_MCODE_API_KEY,model:s.GRIX_MCODE_MODEL}).catch(n=>{a.warn("acp-adapter",`mcode provider config skipped: ${n instanceof Error?n.message:String(n)}`)})}a.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=de({...process.env,...this.config.env});a.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 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.lastExitCode=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 t;this.acpMcpTools&&(t=await this.startInternalApiAndMcp(e||this.resolveCwd())),this.acpClient=new ce,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&&(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 i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=!!i&&i!=="__continue__",n=async l=>{const d=this.agentProcess?.transport;if(!d)throw new Error("agent process exited before the ACP session could connect");await this.acpClient.connect({transport:d,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:!0,cwd:e||this.resolveCwd(),mcpServers:t,sessionId:l,newSessionParams:this.newSessionParams})};let o=!1,r;try{await n(i)}catch(l){if(l instanceof ue){await this.handleAuthRequired(l);return}if(!i)throw this.handleConnectFailure(l),l;if(a.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${C(l)}`),o=!0,r=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){a.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),a.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${d.length})`)):a.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${i}`)}await n(void 0)}if(this.sessionConnected=!0,this.sessionContextPending=!s||o,!this.sessionContextPending){const l=this.callbacks.getAgentProfile?.(),d=l?.agentName?.trim()??"",h=l?.introduction?.trim()??"",u=g=>{const v=this.bindingStore?.getIdentitySnapshot(g);v&&v.name===d&&v.intro===h&&this.identity.markInjected(g)};this.currentAibotSessionId&&u(this.currentAibotSessionId);for(const g of this.sessionBindings.keys())u(g)}this.captureBaselineGroupPids(),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[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&&r&&(u.previous_session_id=r,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(l,"ready",d,u)}}async buildKiroRecoveryContext(e,t,i){try{const s=p.join(I(),".kiro","sessions","cli",`${t}.json`);await A.access(s),a.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,i);if(!n)return;const o=p.join(I(),".grix","data","session-summaries");await A.mkdir(o,{recursive:!0});const r=p.join(o,`${e}.md`),l=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
8
+ `);return await A.writeFile(r,l,"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: ${C(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(`
10
+ `)],n=await this.runCommandCapture("kiro-cli",s,t,45e3);if(!n)return;const o=Le(n).trim(),r=o.indexOf("> "),l=r>=0?o.slice(r+2).trim():o;if(l)return a.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${o.length}, body_chars=${l.length})`),l.split(`
11
+ `).filter(d=>!d.includes("Credits:")&&!d.includes("Time:")).join(`
12
+ `).trim()}runCommandCapture(e,t,i,s){return new Promise((n,o)=>{const r=x(e,t,{cwd:i,stdio:["ignore","pipe","pipe"],windowsHide:!0});let l="",d="";const h=setTimeout(()=>{r.kill("SIGTERM"),o(new Error(`${e} timed out after ${s}ms`))},s);r.stdout.on("data",u=>{l+=String(u)}),r.stderr.on("data",u=>{d+=String(u)}),r.on("error",u=>{clearTimeout(h),o(u)}),r.on("close",u=>{if(clearTimeout(h),u===0){n(l||d);return}o(new Error(`${e} exited with code ${u}: ${d||l}`))})})}handleConnectFailure(e){a.error("acp-adapter",`ACP session creation failed: ${C(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 pe,this.internalApi.setInvokeHandler(async(r,l,d,h)=>{if(r==="toolbar_set"&&!String(l?.session_id??"").trim()){const u=String(this.currentAibotSessionId??"").trim();u&&(l={...l,session_id:u})}return this.callbacks.agentInvoke(r,l,h)}),await this.internalApi.start(0),a.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(r=>this.onMcpBridgeUp(r));const t=this.config.command??"",i=y(t)==="qwen"?p.join(I(),".qwen","skills"):t.includes("gemini")?p.join(I(),".gemini","skills"):t.includes("kiro")?p.join(I(),".kiro","skills"):this.isReasonix?p.join(I(),".reasonix","skills"):this.isKimi?p.join(ae({...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(I(),".copilot","skills"):this.auditAgentType==="hermes"?p.join(this.config.env?.HERMES_HOME?.trim()||le({...process.env,...this.config.env},this.nativeProviderScope),"skills"):this.auditAgentType==="qodercli"?p.join(I(),".qoder","skills"):this.auditAgentType==="qoderclicn"?p.join(I(),".qoder-cn","skills"):this.auditAgentType==="traecli"?p.join(I(),".trae","skills"):this.auditAgentType==="codebuddy"?p.join(I(),".codebuddy","skills"):void 0;if(i){const r=re(i);r.length>0&&a.info("acp-adapter",`Synced connector skills to ${i}: [${r.join(", ")}]`)}const s=p.resolve(R,"../../mcp/mcp-bridge-server.js"),n=this.internalApi.mcpBridgeWsUrl,o=p.resolve(R,"../../mcp/acp-mcp-server.js");if(this.isKimi){await this.internalApi.setCodebaseNavigatorCwd(e);const r={Authorization:`Bearer ${this.internalApi.token}`};return[{type:"http",name:"grix-app-bridge",url:this.internalApi.mcpBridgeHttpUrl,headers:r},{type:"http",name:"grix-connector-tools",url:this.internalApi.mcpToolsHttpUrl,headers:r},{type:"http",name:"codebase-navigator",url:this.internalApi.codebaseNavigatorHttpUrl,headers:r}]}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){a.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){a.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:
13
13
  ${s}
14
14
 
15
- Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(t.id),a.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(i,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0,n=this.agentProcess?.transport;if(!n||!this.acpClient)throw new Error("agent process exited during authentication");await this.acpClient.connect({transport:n,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:!0,cwd:this.resolveCwd(),mcpServers:s,newSessionParams:this.newSessionParams}),a.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.captureBaselineGroupPids(),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(s){throw a.error("acp-adapter",`Auth retry failed: ${s}`),s}}runTerminalAuth(e,t){return new Promise((i,s)=>{const n=x(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 r=!1,l=!1;const c=u=>{if(r)return;const v=u.toString().replace(/\x1b\[[0-9;]*m/g,"").match(o);v&&(r=!0,a.info("acp-adapter",`Terminal auth URL captured: ${v[0]}`),this.callbacks.sendAuthNotification(t,`Please open this URL on any device to authenticate:
15
+ Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(t.id),a.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(i,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0,n=this.agentProcess?.transport;if(!n||!this.acpClient)throw new Error("agent process exited during authentication");await this.acpClient.connect({transport:n,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,terminal:!0,cwd:this.resolveCwd(),mcpServers:s,newSessionParams:this.newSessionParams}),a.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.captureBaselineGroupPids(),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(s){throw a.error("acp-adapter",`Auth retry failed: ${s}`),s}}runTerminalAuth(e,t){return new Promise((i,s)=>{const n=x(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 r=!1,l=!1;const d=u=>{if(r)return;const v=u.toString().replace(/\x1b\[[0-9;]*m/g,"").match(o);v&&(r=!0,a.info("acp-adapter",`Terminal auth URL captured: ${v[0]}`),this.callbacks.sendAuthNotification(t,`Please open this URL on any device to authenticate:
16
16
  ${v[0]}
17
17
 
18
- Waiting for authentication to complete...`))};n.stdout?.on("data",c),n.stderr?.on("data",c);const h=setTimeout(()=>{l||(l=!0,n.kill("SIGKILL"),this.terminalAuthChild=null,s(new Error(`terminal auth timed out after ${H}ms`)))},H);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 r=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(t);r&&(i=!0,this.agentProcess.removeListener("stderr",s),e(r[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{i||(i=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){try{a.info("acp-adapter","incoming acp event",{eventType:e.type,sessionId:this.activeRun?.sessionId??null,eventKeys:Object.keys(e),rawEvent:N(e,2e3)})}catch{}if(e.type===m.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.captureAuditEvent(e),this.handlePermissionRequest(e);return}if(e.type===m.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 m.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 m.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);if(t.lastToolUse?.toolName===e.toolName&&_(t.lastToolUse.toolInput)===n)break;t.lastToolUse={toolName:e.toolName,toolInput:s},e.toolCallId&&t.sentToolUseIds.add(e.toolCallId);const o=this.trimToolWirePayload?q(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 m.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=O(t,e),n=e.content,o=_(n);if(t.lastToolResult?.toolName===s&&_(t.lastToolResult.content)===o)break;if(t.lastToolResult={toolName:s,content:n},!this.trimToolWirePayload||e.toolStatus==="failed"){const r=this.trimToolWirePayload?"(failed)":n;this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:s,content:r}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,s,r)}}break}case m.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<ve)break;t.lastToolProgressSentAt.set(s,n);const r=O(t,e);this.emitRawEventEnvelope(t,{type:"tool_progress",payload:{tool_name:r,content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,r,e.content)}break}case m.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 m.Error:{t.lastToolUse=null,t.lastToolResult=null;const s=String(e.error??"unknown error");this.emitRawEventEnvelope(t,{type:"error",payload:{message:s}}),a.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(t.eventId,t.sessionId,s);break}case m.Result:{if(e.synthetic&&this.shouldDeferSyntheticResultForDetachedKimiTask(t,e)){a.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(a.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:N(t,3e3)}),!t||!e.requestId||!this.acpClient){a.warn("acp-adapter","permission_request dropped: missing prerequisite",{hasPr:t!=null,hasRequestId:!!e.requestId,hasAcpClient:!!this.acpClient}),e.requestId&&this.captureAuditEvent({type:m.PermissionResponse,requestId:e.requestId,permissionDecision:"dropped"});return}if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){a.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${t.toolName}`),this.acpClient.respondPermission(t.requestId,{behavior:"allow"}).catch(()=>{}),this.captureAuditEvent({type:m.PermissionResponse,requestId:t.requestId,toolCallId:t.toolCallId,permissionDecision:"allow"});return}const i=this.activeRun,s=Ae(t);if(s&&i&&this.callbacks.sendAgentQuestionCard){this.resetIdleTimer(i),this.pendingQuestions.set(e.requestId,{options:[...t.options]});const r=t.options.some(l=>R.test(l.optionId));this.callbacks.sendAgentQuestionCard(i.eventId,i.sessionId,{request_id:e.requestId,mode:"form",questions:[{header:s.prompt,prompt:s.prompt,options:s.answerOptions.map(l=>l.name),allow_free_text:r}]}),a.info("acp-adapter","AskUserQuestion forwarded via agent_question card",{sessionId:i.sessionId,eventId:i.eventId,requestId:e.requestId,optionsCount:s.answerOptions.length});return}const n=t.toolCallId,o=Ie(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}})?a.info("acp-adapter","permission_request forwarded via rawEventEnvelope",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}):(a.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}))):(a.info("acp-adapter",`Permission request without active run, auto-approving: ${t.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(n),this.captureAuditEvent({type:m.PermissionResponse,requestId:e.requestId,toolCallId:n,permissionDecision:"allow"}))}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?Pe:ke),s=e.lastProgressAt+w-Date.now(),n=Math.max(0,Math.min(i,s));e.idleTimer=setTimeout(()=>{this.onIdleTimeout(e)},n)}async onIdleTimeout(e){if(this.activeRun?.eventId!==e.eventId)return;const t=this.acpClient,i=Date.now()-e.lastProgressAt;if(i>=w){if(t?.isAlive){const s=e.lastProgressAt,n=await this.checkSessionFileActivity(t.sessionId,e.lastIdleCheckAt);if(this.activeRun?.eventId!==e.eventId)return;const o=e.lastProgressAt>s;if(e.lastIdleCheckAt=Date.now(),o||n){n&&(e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,a.info("acp-adapter",`Hard cap recheck found ${o?"stream progress":"session file activity"}, refreshing watchdog: ${e.eventId}`),this.resetIdleTimer(e);return}}a.error("acp-adapter",`Turn inactive ${Math.round(i/1e3)}s \u2265 cap ${Math.round(w/1e3)}s, forcing timeout: ${e.eventId}`),this.finishRun("failed",`turn inactivity timeout after ${Math.round(i/1e3)}s with no ACP activity`),this.declareStuck();return}t?.isAlive?t.ping(5e3).then(async s=>{if(this.activeRun?.eventId!==e.eventId)return;if(!s){await this.handlePingFailure(e,t);return}if(e.consecutivePingFailures=0,e.awaitingToolResult){const c=e.lastIdleCheckAt,h=await this.checkSessionFileActivity(t.sessionId,c);if(this.activeRun?.eventId!==e.eventId)return;e.lastIdleCheckAt=Date.now(),h?(e.lastProgressAt=Date.now(),a.info("acp-adapter",`Idle timer: tool running, ping ok, session file active, refreshing watchdog: ${e.eventId}`)):a.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,r=o?!1:await this.checkSessionFileActivity(t.sessionId,n);if(this.activeRun?.eventId!==e.eventId)return;const l=o||r;e.lastIdleCheckAt=Date.now(),l?(r&&(a.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++,a.info("acp-adapter",`Idle timer: ping ok, no stream progress (cycle=${e.idleNoProgressCount}), extending until inactivity cap (${Math.round(w/6e4)}min): ${e.eventId}`),this.resetIdleTimer(e))}).catch(()=>{this.activeRun?.eventId===e.eventId&&this.handlePingFailure(e,t)}):(a.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed",this.describeUnreachable()),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||this.pendingQuestions.size>0){const o=s?"stream progress":n?"session file activity":e.awaitingToolResult?"tool in flight":"question pending user answer";a.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<L){a.warn("acp-adapter",`Idle timer: ping failed (${e.consecutivePingFailures}/${L}), rechecking in ${K/1e3}s: ${e.eventId}`),this.resetIdleTimer(e,K);return}a.error("acp-adapter",`Ping failed ${e.consecutivePingFailures}x with no activity evidence, declaring stuck: ${e.eventId}`),this.finishRun("failed",this.describeUnreachable()),this.declareStuck()}}async checkSessionFileActivity(e,t){if(!e)return!1;try{if(this.isKimi)return await Se(e,t);const i=C(this.config.command);return i==="kiro-cli"?await Ce(e,t):i==="qodercli"?await B(".qoder",e,t):i==="qoderclicn"?await B(".qoder-cn",e,t):i==="dim"?await _e(e,t):!1}catch{return!1}}shouldDeferSyntheticResultForDetachedKimiTask(e,t){if(!this.isKimi)return!1;try{return we(t.sessionId||this.acpClient?.sessionId||e.sessionId)}catch{return!1}}declareStuck(){this.pendingQuestions.clear(),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(),Re))}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,a.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===m.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>=xe?this.flushOrphanBuffer():this.scheduleOrphanFlush();return}e.type===m.Result&&this.flushOrphanBuffer()}}scheduleOrphanFlush(){this.orphanFlushTimer&&clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=setTimeout(()=>{this.orphanFlushTimer=null,this.flushOrphanBuffer()},Me),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 S;const t=this.orphanSessionId||this.currentAibotSessionId;this.orphanSessionId=null;const i=this.orphanBuffer.trim();if(this.orphanBuffer="",!!i){if(!t||!this.callbacks.sendAgentMessage){a.warn("acp-adapter",`orphan agent output dropped (${t?"no sendAgentMessage callback":"no session id"}) chars=${i.length}`);return}a.info("acp-adapter",`delivering orphan agent output session=${t} chars=${i.length}`);for(let s=0;s<i.length;s+=G)this.callbacks.sendAgentMessage(t,i.slice(s,s+G))}}suppressOrphanOutput(e){this.orphanSuppressedUntil=Date.now()+e,this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanBuffer="",this.orphanQuotedStream=new S,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)}lastExitCode=null;describeUnreachable(){return this.agentProcess?.alive?"agent unreachable: no response while its process is still running":this.lastExitCode===null?"agent unreachable: its process is no longer running":`agent unreachable: its process exited (code=${this.lastExitCode})`}static sanitizeFailureReasonForUser(e){return e.replace(/\bunreachable\b/gi,"unresponsive").replace(/\bidle cycles?\b/gi,"consecutive checks")}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);let n=!1;if(t)if(e==="failed"){a.error("acp-adapter",`finishRun failed: ${t} event=${i.eventId}`);const r=V.sanitizeFailureReasonForUser(t);i.buffer+=`
18
+ 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 ${W}ms`)))},W);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 r=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(t);r&&(i=!0,this.agentProcess.removeListener("stderr",s),e(r[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{i||(i=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){try{a.info("acp-adapter","incoming acp event",{eventType:e.type,sessionId:this.activeRun?.sessionId??null,eventKeys:Object.keys(e),rawEvent:N(e,2e3)})}catch{}if(e.type===f.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.captureAuditEvent(e),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);if(t.lastToolUse?.toolName===e.toolName&&_(t.lastToolUse.toolInput)===n)break;t.lastToolUse={toolName:e.toolName,toolInput:s},e.toolCallId&&t.sentToolUseIds.add(e.toolCallId);const o=this.trimToolWirePayload?q(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=O(t,e),n=e.content,o=_(n);if(t.lastToolResult?.toolName===s&&_(t.lastToolResult.content)===o)break;if(t.lastToolResult={toolName:s,content:n},!this.trimToolWirePayload||e.toolStatus==="failed"){const r=this.trimToolWirePayload?"(failed)":n;this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:s,content:r}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,s,r)}}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<Ie)break;t.lastToolProgressSentAt.set(s,n);const r=O(t,e);this.emitRawEventEnvelope(t,{type:"tool_progress",payload:{tool_name:r,content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,r,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}}),a.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)){a.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.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(t,s.deltaContent),this.flushStream(),this.finishRun("responded");break}}i&&(t.lastProgressAt=Date.now(),t.idleNoProgressCount=0),this.resetIdleTimer(t)}handlePermissionRequest(e){const t=e.permissionRequest;if(a.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:N(t,3e3)}),!t||!e.requestId||!this.acpClient){a.warn("acp-adapter","permission_request dropped: missing prerequisite",{hasPr:t!=null,hasRequestId:!!e.requestId,hasAcpClient:!!this.acpClient}),e.requestId&&this.captureAuditEvent({type:f.PermissionResponse,requestId:e.requestId,permissionDecision:"dropped"});return}if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){a.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${t.toolName}`),this.acpClient.respondPermission(t.requestId,{behavior:"allow"}).catch(()=>{}),this.captureAuditEvent({type:f.PermissionResponse,requestId:t.requestId,toolCallId:t.toolCallId,permissionDecision:"allow"});return}const i=this.activeRun,s=Ce(t);if(s&&i&&this.callbacks.sendAgentQuestionCard){this.resetIdleTimer(i),this.pendingQuestions.set(e.requestId,{options:[...t.options]});const r=t.options.some(l=>k.test(l.optionId));this.callbacks.sendAgentQuestionCard(i.eventId,i.sessionId,{request_id:e.requestId,mode:"form",questions:[{header:s.prompt,prompt:s.prompt,options:s.answerOptions.map(l=>l.name),allow_free_text:r}]}),a.info("acp-adapter","AskUserQuestion forwarded via agent_question card",{sessionId:i.sessionId,eventId:i.eventId,requestId:e.requestId,optionsCount:s.answerOptions.length});return}const n=t.toolCallId,o=ye(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}})?a.info("acp-adapter","permission_request forwarded via rawEventEnvelope",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}):(a.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}))):(a.info("acp-adapter",`Permission request without active run, auto-approving: ${t.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(n),this.captureAuditEvent({type:f.PermissionResponse,requestId:e.requestId,toolCallId:n,permissionDecision:"allow"}))}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),Date.now()-e.lastProgressAt<2e3&&this.refreshGrokLivenessBudget(e,e.lastProgressAt);const i=t??(e.awaitingToolResult?Me:Ee),s=Date.now(),n=e.lastProgressAt+w-s,o=e.grokLivenessHoldUntil?e.grokLivenessHoldUntil-s:0,r=Math.max(0,Math.min(i,Math.max(n,o)));e.idleTimer=setTimeout(()=>{this.onIdleTimeout(e)},r)}async readIdleEvidence(e,t,i){if(y(this.config.command)==="grok"){const n=await L({sessionId:t,fromOffset:e.grokEventsOffset,extendStartAt:e.grokLivenessExtendStartAt});return n.kind==="progress"?(e.grokLivenessHoldUntil=void 0,e.grokLivenessExtendStartAt=n.extendStartAt,{fileActivity:!0,grokExtend:!1}):n.kind==="extend"?(e.grokLivenessExtendStartAt=n.extendStartAt,e.grokLivenessHoldUntil=n.extendUntil,a.info("acp-adapter",`Grok liveness: turn open for ${e.eventId} (freshMs=${n.freshMs??"none"}), holding until budget`),{fileActivity:!1,grokExtend:!0}):(e.grokLivenessHoldUntil=void 0,n.turn==="open"&&a.warn("acp-adapter",`Grok liveness budget exhausted for ${e.eventId} (turn still open, session files stale), allowing close`),{fileActivity:!1,grokExtend:!1})}return{fileActivity:await this.checkSessionFileActivity(t,i),grokExtend:!1}}refreshGrokLivenessBudget(e,t){y(this.config.command)==="grok"&&(e.grokLivenessExtendStartAt=t,e.grokLivenessHoldUntil=void 0)}async onIdleTimeout(e){if(this.activeRun?.eventId!==e.eventId)return;const t=this.acpClient,i=Date.now()-e.lastProgressAt;if(i>=w){if(t?.isAlive){const s=e.lastProgressAt,n=await this.readIdleEvidence(e,t.sessionId,e.lastIdleCheckAt);if(this.activeRun?.eventId!==e.eventId)return;const o=e.lastProgressAt>s;if(e.lastIdleCheckAt=Date.now(),o||n.fileActivity){n.fileActivity&&(e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,a.info("acp-adapter",`Hard cap recheck found ${o?"stream progress":"session file activity"}, refreshing watchdog: ${e.eventId}`),this.resetIdleTimer(e);return}if(n.grokExtend){this.resetIdleTimer(e);return}}a.error("acp-adapter",`Turn inactive ${Math.round(i/1e3)}s \u2265 cap ${Math.round(w/1e3)}s, forcing timeout: ${e.eventId}`),this.finishRun("failed",`turn inactivity timeout after ${Math.round(i/1e3)}s with no ACP activity`),this.declareStuck();return}t?.isAlive?t.ping(5e3).then(async s=>{if(this.activeRun?.eventId!==e.eventId)return;if(!s){await this.handlePingFailure(e,t);return}if(e.consecutivePingFailures=0,e.awaitingToolResult){const d=e.lastIdleCheckAt,h=await this.readIdleEvidence(e,t.sessionId,d);if(this.activeRun?.eventId!==e.eventId)return;e.lastIdleCheckAt=Date.now(),h.fileActivity?(e.lastProgressAt=Date.now(),a.info("acp-adapter",`Idle timer: tool running, ping ok, session file active, refreshing watchdog: ${e.eventId}`)):h.grokExtend||a.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,r=o?{fileActivity:!1,grokExtend:!1}:await this.readIdleEvidence(e,t.sessionId,n);if(this.activeRun?.eventId!==e.eventId)return;const l=o||r.fileActivity;e.lastIdleCheckAt=Date.now(),l?(r.fileActivity&&(a.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)):r.grokExtend?(e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,a.info("acp-adapter",`Idle timer: ping ok, no stream progress (cycle=${e.idleNoProgressCount}), extending until inactivity cap (${Math.round(w/6e4)}min): ${e.eventId}`),this.resetIdleTimer(e))}).catch(()=>{this.activeRun?.eventId===e.eventId&&this.handlePingFailure(e,t)}):(a.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed",this.describeUnreachable()),this.declareStuck())}async handlePingFailure(e,t){const i=e.lastIdleCheckAt,s=e.lastProgressAt>i,n=s?{fileActivity:!1,grokExtend:!1}:await this.readIdleEvidence(e,t.sessionId,i);if(this.activeRun?.eventId===e.eventId){if(s||n.fileActivity||n.grokExtend||e.awaitingToolResult||this.pendingQuestions.size>0){const o=s?"stream progress":n.fileActivity?"session file activity":n.grokExtend?"grok turn still open":e.awaitingToolResult?"tool in flight":"question pending user answer";a.info("acp-adapter",`Idle timer: ping failed but ${o}, continuing: ${e.eventId}`),e.consecutivePingFailures=0,e.lastIdleCheckAt=Date.now(),n.fileActivity&&(e.lastProgressAt=Date.now()),this.resetIdleTimer(e);return}if(e.consecutivePingFailures++,e.lastIdleCheckAt=Date.now(),e.consecutivePingFailures<K){a.warn("acp-adapter",`Idle timer: ping failed (${e.consecutivePingFailures}/${K}), rechecking in ${H/1e3}s: ${e.eventId}`),this.resetIdleTimer(e,H);return}a.error("acp-adapter",`Ping failed ${e.consecutivePingFailures}x with no activity evidence, declaring stuck: ${e.eventId}`),this.finishRun("failed",this.describeUnreachable()),this.declareStuck()}}async checkSessionFileActivity(e,t){if(!e)return!1;try{if(this.isKimi)return await _e(e,t);const i=y(this.config.command);return i==="kiro-cli"?await Se(e,t):i==="qodercli"?await F(".qoder",e,t):i==="qoderclicn"?await F(".qoder-cn",e,t):i==="dim"?await we(e,t):i==="grok"?(await L({sessionId:e})).kind==="progress":!1}catch{return!1}}shouldDeferSyntheticResultForDetachedKimiTask(e,t){if(!this.isKimi)return!1;try{return Te(t.sessionId||this.acpClient?.sessionId||e.sessionId)}catch{return!1}}declareStuck(){this.pendingQuestions.clear(),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(),Pe))}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,a.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>=Ne?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()},qe),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 S;const t=this.orphanSessionId||this.currentAibotSessionId;this.orphanSessionId=null;const i=this.orphanBuffer.trim();if(this.orphanBuffer="",!!i){if(!t||!this.callbacks.sendAgentMessage){a.warn("acp-adapter",`orphan agent output dropped (${t?"no sendAgentMessage callback":"no session id"}) chars=${i.length}`);return}a.info("acp-adapter",`delivering orphan agent output session=${t} chars=${i.length}`);for(let s=0;s<i.length;s+=z)this.callbacks.sendAgentMessage(t,i.slice(s,s+z))}}suppressOrphanOutput(e){this.orphanSuppressedUntil=Date.now()+e,this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanBuffer="",this.orphanQuotedStream=new S,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)}lastExitCode=null;describeUnreachable(){return this.agentProcess?.alive?"agent unreachable: no response while its process is still running":this.lastExitCode===null?"agent unreachable: its process is no longer running":`agent unreachable: its process exited (code=${this.lastExitCode})`}static sanitizeFailureReasonForUser(e){return e.replace(/\bunreachable\b/gi,"unresponsive").replace(/\bidle cycles?\b/gi,"consecutive checks")}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.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),s.deltaContent&&(i.buffer+=s.deltaContent);let n=!1;if(t)if(e==="failed"){a.error("acp-adapter",`finishRun failed: ${t} event=${i.eventId}`);const r=Y.sanitizeFailureReasonForUser(t);i.buffer+=`
19
19
 
20
- The task was interrupted (${r}). Please resend your instruction.`,n=!0}else a.info("acp-adapter",`finishRun ${e}: ${t} event=${i.eventId}`);i.buffer&&(this.emitSegmentedStream(i,i.buffer),i.buffer="");const o=++i.chunkSeq;this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(i.eventId,i.sessionId,i.currentClientMsgId).then(()=>{i.silent||this.callbacks.sendEventResult(i.eventId,e,t,void 0,n),this.persistEventResult(i,e,t),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(r=>{const l=`final output delivery failed: ${r instanceof Error?r.message:String(r)}`,c=e==="responded"?"failed":e,h=e==="responded"?l:t,u=e==="responded"?!1:n;a.warn("acp-adapter",`finalStreamChunk rejected/failed event=${i.eventId}: ${l}`),i.silent||this.callbacks.sendEventResult(i.eventId,c,h,void 0,u),this.persistEventResult(i,c,h),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}):(this.callbacks.sendStreamChunk(i.eventId,i.sessionId,"",o,!0,i.currentClientMsgId),i.silent||this.callbacks.sendEventResult(i.eventId,e,t,void 0,n),this.persistEventResult(i,e,t),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact())}beginAuditCapture(e){if(!e.audit?.enabled||this.auditCaptures.has(e.event_id))return;let t;if(this.reasonixAuditTranscriptPath)try{t=$(this.reasonixAuditTranscriptPath).size}catch{t=0}this.auditCaptures.set(e.event_id,{...t===void 0?{}:{transcriptStartOffset:t},events:[]}),this.activeAuditEventId=e.event_id}captureAuditEvent(e){if(this.isKimi)return;const t=this.activeRun?.eventId??this.activeAuditEventId;if(!t)return;const i=this.auditCaptures.get(t);i&&i.events.push({sequence:i.events.length,observedAt:new Date().toISOString(),event:structuredClone(e)})}snapshotAuditBoundary(e){const t=this.auditCaptures.get(e);if(!t)return;let i=t.transcriptStartOffset;if(this.reasonixAuditTranscriptPath)try{i=$(this.reasonixAuditTranscriptPath).size}catch{}this.completedAuditBoundaries.set(e,{adapterType:"acp",agentType:this.auditAgentType,cwd:this.resolveCwd(),...this.reasonixAuditTranscriptPath?{transcriptPath:this.reasonixAuditTranscriptPath,transcriptStartOffset:t.transcriptStartOffset??0,transcriptEndOffset:i??0}:{},providerSessionId:this.acpClient?.sessionId,...this.isKimi?{}:{acpEvidence:{events:t.events}}}),this.auditCaptures.delete(e),this.activeAuditEventId===e&&(this.activeAuditEventId=null)}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 t=this.getContextWindowUsedPercentage(e);t===null||t<W||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,a.info("acp-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${W}%, 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,t,i){this.eventResults&&!e.silent&&this.eventResults.set({sessionId:e.sessionId,eventId:e.eventId,status:t,msg:i,updatedAt:Date.now()})}}class Be extends M{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{V as AcpAdapter};
20
+ The task was interrupted (${r}). Please resend your instruction.`,n=!0}else a.info("acp-adapter",`finishRun ${e}: ${t} event=${i.eventId}`);i.buffer&&(this.emitSegmentedStream(i,i.buffer),i.buffer="");const o=++i.chunkSeq;if(this.callbacks.sendFinalStreamChunkReliable){const r=i.quotedMessageId;(r?this.callbacks.sendFinalStreamChunkReliable(i.eventId,i.sessionId,i.currentClientMsgId,r):this.callbacks.sendFinalStreamChunkReliable(i.eventId,i.sessionId,i.currentClientMsgId)).then(()=>{i.silent||this.callbacks.sendEventResult(i.eventId,e,t,void 0,n),this.persistEventResult(i,e,t),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(d=>{const h=`final output delivery failed: ${d instanceof Error?d.message:String(d)}`,u=e==="responded"?"failed":e,g=e==="responded"?h:t,v=e==="responded"?!1:n;a.warn("acp-adapter",`finalStreamChunk rejected/failed event=${i.eventId}: ${h}`),i.silent||this.callbacks.sendEventResult(i.eventId,u,g,void 0,v),this.persistEventResult(i,u,g),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()})}else this.callbacks.sendStreamChunk(i.eventId,i.sessionId,"",o,!0,i.currentClientMsgId,i.quotedMessageId),i.silent||this.callbacks.sendEventResult(i.eventId,e,t,void 0,n),this.persistEventResult(i,e,t),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}beginAuditCapture(e){if(!e.audit?.enabled||this.auditCaptures.has(e.event_id))return;let t;if(this.reasonixAuditTranscriptPath)try{t=M(this.reasonixAuditTranscriptPath).size}catch{t=0}this.auditCaptures.set(e.event_id,{...t===void 0?{}:{transcriptStartOffset:t},events:[]}),this.activeAuditEventId=e.event_id}captureAuditEvent(e){if(this.isKimi)return;const t=this.activeRun?.eventId??this.activeAuditEventId;if(!t)return;const i=this.auditCaptures.get(t);i&&i.events.push({sequence:i.events.length,observedAt:new Date().toISOString(),event:structuredClone(e)})}snapshotAuditBoundary(e){const t=this.auditCaptures.get(e);if(!t)return;let i=t.transcriptStartOffset;if(this.reasonixAuditTranscriptPath)try{i=M(this.reasonixAuditTranscriptPath).size}catch{}this.completedAuditBoundaries.set(e,{adapterType:"acp",agentType:this.auditAgentType,cwd:this.resolveCwd(),...this.reasonixAuditTranscriptPath?{transcriptPath:this.reasonixAuditTranscriptPath,transcriptStartOffset:t.transcriptStartOffset??0,transcriptEndOffset:i??0}:{},providerSessionId:this.acpClient?.sessionId,...this.isKimi?{}:{acpEvidence:{events:t.events}}}),this.auditCaptures.delete(e),this.activeAuditEventId===e&&(this.activeAuditEventId=null)}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 t=this.getContextWindowUsedPercentage(e);t===null||t<G||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,a.info("acp-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${G}%, 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,t,i){this.eventResults&&!e.silent&&this.eventResults.set({sessionId:e.sessionId,eventId:e.eventId,status:t,msg:i,updatedAt:Date.now()})}}class je extends ${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{Y as AcpAdapter};
@@ -0,0 +1,3 @@
1
+ import p from"node:path";import{homedir as g}from"node:os";import{open as k}from"node:fs/promises";import{readdirSync as O,statSync as d}from"node:fs";const T=9e4,x=1800*1e3,v=512*1024,P=new Set(["mcp_config_resolved","mcp_server_starting","mcp_server_connected","mcp_init_completed"]),m=new Map;function R(t){return t??p.join(g(),".grok","sessions")}function j(t,e){return`${t}\0${e}`}function E(t,e){if(!t)return null;const n=R(e),r=j(n,t),s=m.get(r);if(s)try{return d(s),s}catch{m.delete(r)}let c;try{c=O(n,{withFileTypes:!0})}catch{return null}for(const o of c){if(!o.isDirectory())continue;const u=p.join(n,o.name,t,"events.jsonl");try{return d(u),m.set(r,u),u}catch{continue}}return null}function V(t,e){if(!t)return 0;const n=E(t,e);if(!n)return 0;try{return d(n).size}catch{return 0}}function A(t){let e=0;for(const n of["events.jsonl","updates.jsonl","chat_history.jsonl"])try{const r=d(p.join(t,n)).mtimeMs;r>e&&(e=r)}catch{continue}return e>0?e:null}async function M(t,e,n){const r=Math.max(0,n-e);if(r===0)return Buffer.alloc(0);const s=await k(t,"r");try{const c=Buffer.alloc(r),{bytesRead:o}=await s.read(c,0,r,e);return o===r?c:c.subarray(0,o)}finally{await s.close()}}function b(t){let e="none",n=!1;for(const r of t.split(`
2
+ `)){const s=r.trim();if(!s)continue;let c;try{c=JSON.parse(s)}catch{continue}const o=typeof c.type=="string"?c.type:"";if(o==="turn_started"){e="open",n=!1;continue}if(o==="turn_ended"){e="ended",n=!1;continue}o&&!P.has(o)&&(n=!0)}return e==="open"||n?"open":e}async function D(t){const e=t.now??Date.now(),n=(w,a)=>({kind:"none",turn:w,freshMs:a});if(!t.sessionId)return n("none",null);const r=E(t.sessionId,t.sessionsRoot);if(!r)return n("none",null);let s=0;try{s=d(r).size}catch{return n("none",null)}const c=A(p.dirname(r)),o=c===null?null:Math.max(0,e-c),u=Math.max(0,t.fromOffset??0);let i=Math.min(u,s);const _=s-i>v;_&&(i=s-v);let S="";try{let a=(await M(r,i,s)).toString("utf8"),y=_;if(!y&&i>0&&i<s){const l=await M(r,i-1,i);y=l.length===1&&l[0]!==10}if(y){const l=a.indexOf(`
3
+ `);a=l<0?"":a.slice(l+1)}S=a}catch{return n("none",o)}const f=b(S);if(f==="ended")return n("ended",o);if(o!==null&&o<T)return{kind:"progress",turn:f,freshMs:o,extendStartAt:e};if(!(f==="open"))return n(f,o);const h=t.extendStartAt??e;return e-h>=x?n("open",o):{kind:"extend",turn:f,freshMs:o,extendStartAt:h,extendUntil:h+x}}export{v as GROK_EVENTS_TAIL_MAX_BYTES,T as GROK_LIVENESS_FRESH_MS,x as GROK_LIVENESS_MAX_EXTEND_MS,D as evaluateGrokLiveness,V as grokEventsByteSize,E as resolveGrokEventsPath};
@@ -1,19 +1,19 @@
1
- import{spawn as D}from"node:child_process";import{randomUUID as x}from"node:crypto";import{EventEmitter as M}from"node:events";import{existsSync as k,readFileSync as T,statSync as W,mkdirSync as R,writeFileSync as F,realpathSync as B}from"node:fs";import{homedir as I}from"node:os";import{join as w,dirname as K,resolve as Y}from"node:path";import{fileURLToPath as V}from"node:url";import{log as a}from"../../core/log/index.js";import{killProcessGroup as E}from"../../core/runtime/spawn.js";import{InternalApiServer as Q}from"../../core/mcp/internal-api-server.js";import{resolveMcpServerName as U,pruneLegacyMcpServerEntries as q}from"../../core/mcp/server-name.js";import{syncDefaultSkillsToDir as H}from"../../default-skills/index.js";import{buildSimpleProbeReport as J}from"../shared/probe-util.js";import{getAgyModelCatalog as z,resolveAgyModelFlag as X}from"./model-list.js";import{isGrixInternalToolName as P}from"../../core/mcp/tools.js";import{AgyStreamParser as Z}from"./stream-parser.js";const c="agy-adapter";function tt(y){try{return process.kill(y,0),!0}catch(t){return t.code==="EPERM"}}function et(y){try{return B(y)}catch{return y}}const G=w(I(),".gemini","antigravity-cli","log"),st="CLI store manager shutting down",it=5e3,_=15e3,L=480*1e3,nt=["fetchAvailableModels","loadCodeAssist","token refreshed","ListExperiments","experiment refresh","Experiments refreshed","Reloading system slash commands","Slash commands unchanged","doRefreshQuota"];function rt(y,t){const e=Number(process.env[y]);return Number.isFinite(e)&&e>0?e:t}const ot="30m",N=w(I(),".gemini","antigravity-cli","cache","last_conversations.json"),at=["\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(""),ct=new Set(["send_msg","grix_reply"]),lt=new Set(["send_msg","grix_reply","delete_msg","session_send","grix_complete","grix_composing","grix_event_ack"]);function O(y){try{return structuredClone(y)}catch{return}}class Pt extends M{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;lastActivityAt=0;watchLogOffset=0;inactivityTimeoutMs=L;inactivityWarned=!1;inFlightToolName=null;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 Q,this.internalApi.setInvokeHandler(async(o,d,h,p)=>this.handleToolInvoke(o,d,p)),await this.internalApi.start(0),a.info(c,`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=w(I(),".gemini","config","mcp_config.json");R(K(e),{recursive:!0});let i={};try{k(e)&&(i=JSON.parse(T(e,"utf8")))}catch{}const s=i.mcpServers&&typeof i.mcpServers=="object"?i.mcpServers:{};q(s,this.internalApi.url),s[t.name]={command:t.command,args:t.args},i.mcpServers=s,F(e,`${JSON.stringify(i,null,2)}
2
- `,"utf8"),a.info(c,`MCP config injected into ${e}`);const r=w(I(),".gemini","skills"),l=H(r);l.length>0&&a.info(c,`Synced connector skills to ${r}: [${l.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){this.touchActivity();let s=e;lt.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,l=r?{sequence:r.toolInvocations.length,observedAt:new Date().toISOString(),action:t,params:O(s)}:void 0;r&&l&&r.toolInvocations.push(l);try{const o=await this.callbacks.agentInvoke(t,s,i);return l&&(l.completedAt=new Date().toISOString(),l.result=O(o)),ct.has(t)&&this.activeEventId&&(this.activeEventSentViaTool=!0),o}catch(o){throw l&&(l.completedAt=new Date().toISOString(),l.error=o instanceof Error?o.message:String(o)),o}}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.stopTurnWatchdog(),this.inFlightToolName=null,this.watchdogKilledActiveProcess=!1,this.inactivityWarned=!1,this.activeProcess){try{E(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??x()}async resumeSession(t,e){}async destroySession(t){this.systemInstructionInjectedSessions.delete(t),this.identityInjectedSessions.delete(t)}onAgentProfileChanged(){this.identityInjectedSessions.clear()}sendPrompt(t){const e=new M;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{E(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{E(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||!tt(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 J(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=W(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{E(this.activeProcess,"SIGKILL")}catch{}this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.stopTurnWatchdog(),this.inFlightToolName=null,this.watchdogKilledActiveProcess=!1,this.inactivityWarned=!1,this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1}getMcpConfig(){if(!this.internalApi)return null;const t=Y(V(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:U(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}
1
+ import{spawn as F}from"node:child_process";import{randomUUID as M}from"node:crypto";import{EventEmitter as L}from"node:events";import{existsSync as b,readFileSync as w,statSync as B,mkdirSync as G,writeFileSync as K,realpathSync as Y}from"node:fs";import{homedir as P}from"node:os";import{join as I,dirname as V,resolve as Q}from"node:path";import{fileURLToPath as U}from"node:url";import{log as a}from"../../core/log/index.js";import{killProcessGroup as _}from"../../core/runtime/spawn.js";import{InternalApiServer as q}from"../../core/mcp/internal-api-server.js";import{resolveMcpServerName as H,pruneLegacyMcpServerEntries as J}from"../../core/mcp/server-name.js";import{syncDefaultSkillsToDir as z}from"../../default-skills/index.js";import{buildSimpleProbeReport as X}from"../shared/probe-util.js";import{getAgyModelCatalog as Z,resolveAgyModelFlag as tt}from"./model-list.js";import{isGrixInternalToolName as $}from"../../core/mcp/tools.js";import{AgyStreamParser as et}from"./stream-parser.js";const c="agy-adapter";function st(m){try{return process.kill(m,0),!0}catch(t){return t.code==="EPERM"}}function it(m){try{return Y(m)}catch{return m}}const N=I(P(),".gemini","antigravity-cli","log"),nt="CLI store manager shutting down",rt=5e3,T=15e3,O=480*1e3,ot=["fetchAvailableModels","loadCodeAssist","token refreshed","ListExperiments","experiment refresh","Experiments refreshed","Reloading system slash commands","Slash commands unchanged","doRefreshQuota"];function at(m,t){const e=Number(process.env[m]);return Number.isFinite(e)&&e>0?e:t}const ct="30m",j=I(P(),".gemini","antigravity-cli","cache","last_conversations.json"),lt=["\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(""),dt=new Set(["send_msg","grix_reply"]),ht=new Set(["send_msg","grix_reply","delete_msg","session_send","grix_complete","grix_composing","grix_event_ack"]);function D(m){try{return structuredClone(m)}catch{return}}class kt extends L{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;lastActivityAt=0;watchLogOffset=0;inactivityTimeoutMs=O;inactivityWarned=!1;inFlightToolName=null;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 q,this.internalApi.setInvokeHandler(async(o,h,l,f)=>this.handleToolInvoke(o,h,f)),await this.internalApi.start(0),a.info(c,`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=I(P(),".gemini","config","mcp_config.json");G(V(e),{recursive:!0});let i={};try{b(e)&&(i=JSON.parse(w(e,"utf8")))}catch{}const s=i.mcpServers&&typeof i.mcpServers=="object"?i.mcpServers:{};J(s,this.internalApi.url),s[t.name]={command:t.command,args:t.args},i.mcpServers=s,K(e,`${JSON.stringify(i,null,2)}
2
+ `,"utf8"),a.info(c,`MCP config injected into ${e}`);const r=I(P(),".gemini","skills"),d=z(r);d.length>0&&a.info(c,`Synced connector skills to ${r}: [${d.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){this.touchActivity();let s=e;ht.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,d=r?{sequence:r.toolInvocations.length,observedAt:new Date().toISOString(),action:t,params:D(s)}:void 0;r&&d&&r.toolInvocations.push(d);try{const o=await this.callbacks.agentInvoke(t,s,i);return d&&(d.completedAt=new Date().toISOString(),d.result=D(o)),dt.has(t)&&this.activeEventId&&(this.activeEventSentViaTool=!0),o}catch(o){throw d&&(d.completedAt=new Date().toISOString(),d.error=o instanceof Error?o.message:String(o)),o}}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.stopTurnWatchdog(),this.inFlightToolName=null,this.watchdogKilledActiveProcess=!1,this.inactivityWarned=!1,this.activeProcess){try{_(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??M()}async resumeSession(t,e){}async destroySession(t){this.systemInstructionInjectedSessions.delete(t),this.identityInjectedSessions.delete(t)}onAgentProfileChanged(){this.identityInjectedSessions.clear()}sendPrompt(t){const e=new L;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{_(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{_(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||!st(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 X(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=B(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{_(this.activeProcess,"SIGKILL")}catch{}this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.stopTurnWatchdog(),this.inFlightToolName=null,this.watchdogKilledActiveProcess=!1,this.inactivityWarned=!1,this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,this.activeEventSentViaTool=!1}getMcpConfig(){if(!this.internalApi)return null;const t=Q(U(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:H(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(([,h])=>h!=null&&h!=="").map(([h,l])=>`${h}="${l}"`).join(" "),s=this.renderContextBlock(t),r=s?`${t.content}
3
3
 
4
4
  [Conversation context]
5
- ${s}`:t.content,l=`<channel source="grix-agy" ${i}>
5
+ ${s}`:t.content,d=`<channel source="grix-agy" ${i}>
6
6
  ${r}
7
7
  </channel>`,o=[];if((!t.session_id||!this.systemInstructionInjectedSessions.has(t.session_id))&&(t.session_id&&this.systemInstructionInjectedSessions.add(t.session_id),o.push(`[grix-system-instructions]
8
- ${at}
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),o.push(d))}return o.length>0?`${l}
8
+ ${lt}
9
+ [/grix-system-instructions]`)),!t.session_id||!this.identityInjectedSessions.has(t.session_id)){const h=this.buildIdentityBlock().trim();h&&(t.session_id&&this.identityInjectedSessions.add(t.session_id),o.push(h))}return o.length>0?`${d}
10
10
 
11
11
  ${o.join(`
12
12
 
13
- `)}`:l}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(`
13
+ `)}`:d}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 o=JSON.parse(e);if(!Array.isArray(o))return"";i=o}catch{return""}const s=String(t.session_type??"")==="2",r=String(t.msg_id??""),l=[];for(const o of i){const d=String(o?.msg_id??"");if(d&&d===r)continue;const h=String(o?.content??"").trim();if(!h)continue;const p=String(o?.sender_id??"");if(h.startsWith("[\u5F15\u7528\u6D88\u606F]")){const v=h.slice(6).replace(/^\s*\n?/,"").trim();l.push(s&&p?`[\u5F15\u7528\u6D88\u606F] (\u6765\u81EA ${p})\uFF1A${v}`:`[\u5F15\u7528\u6D88\u606F]\uFF1A${v}`)}else l.push(s&&p?`[${p}]\uFF1A${h}`:h)}return l.join(`
17
- `)}spawnAgyPrint(t,e,i,s){try{R(G,{recursive:!0})}catch(n){const u=n instanceof Error?n.message:String(n),A=`unable to prepare agy log directory: ${u}`;a.error(c,`Log setup failed for event ${t}: ${u}`),this.handleProcessResult(t,e,"",A,s);return}const r=w(G,`grix-${x()}.log`),l=this.buildPrintArgs(i,s,r),o={...process.env,...this.config.env??{}};let d="",h;const p=new Z({onInit:n=>{h=n,this.sessionConversationMap.set(e,n),this.callbacks.persistConversationId(e,n)},onToolStart:(n,u)=>{this.touchActivity();let A=P(n),f=n,m=u;if(n==="call_mcp_tool"){const g=typeof u.ToolName=="string"?u.ToolName:"";(typeof u.ServerName=="string"?u.ServerName:"").startsWith("grix")||P(g)?A=!0:g&&(f=g,m=u.Arguments||u)}if(A)return;this.inFlightToolName=f;const S=JSON.stringify(m??{});this.callbacks.sendToolUse?.(t,e,f,S)},onToolDone:(n,u,A,f)=>{this.touchActivity();let m=P(n),S=n;if(n==="call_mcp_tool"){const g=typeof u.ToolName=="string"?u.ToolName:"";(typeof u.ServerName=="string"?u.ServerName:"").startsWith("grix")||P(g)?m=!0:g&&(S=g)}(this.inFlightToolName===S||this.inFlightToolName===n)&&(this.inFlightToolName=null),!m&&f&&this.callbacks.sendToolResult?.(t,e,S,`(failed: ${f})`)},onTextDelta:n=>{this.touchActivity()},onResult:n=>{this.touchActivity(),this.inFlightToolName=null,n.response!==void 0&&(d=n.response),n.conversationId&&(h=n.conversationId,this.sessionConversationMap.set(e,n.conversationId),this.callbacks.persistConversationId(e,n.conversationId)),this.handleTurnResultReceived(t)},onLine:n=>{this.touchActivity()}});a.info(c,`Spawning: agy ${l.map(n=>n.includes(" ")?`"${n}"`:n).join(" ")}`);const v=D(this.config.command,l,{cwd:s.cwd,env:o,stdio:["pipe","pipe","pipe"],detached:process.platform!=="win32"});this.activeProcess=v,this.lastActivityAt=Date.now(),this.watchLogOffset=0,this.inactivityWarned=!1,this.inactivityTimeoutMs=rt("GRIX_AGY_INACTIVITY_TIMEOUT_MS",L);const b=[],C=[];v.stdout?.on("data",n=>{b.push(n),p.feed(n),this.touchActivity()}),v.stderr?.on("data",n=>{C.push(n),this.touchActivity()}),v.on("error",n=>{a.error(c,`Process spawn error for event ${t}: ${n.message}`),this.handleProcessResult(t,e,"",`spawn error: ${n.message}`,s)}),v.on("close",n=>{if(a.info(c,`Process exited for event ${t} with code ${n}`),this.activeEventId!==t)return;p.flush(),this.stopTurnWatchdog();const u=Buffer.concat(b).toString("utf-8"),A=Buffer.concat(C).toString("utf-8"),f=d.trim()?d:u,m=this.auditCaptures.get(t);m&&(m.stdout=u,m.stderr=A,m.exitCode=n);const S=this.watchdogKilledActiveProcess&&n===null;if(this.watchdogKilledActiveProcess=!1,S)f.trim()||this.activeEventSentViaTool?this.handleProcessResult(t,e,f,"",s,h):setTimeout(()=>{const g=this.extractLogError(r);this.handleProcessResult(t,e,f,g,s,h)},300);else if(n!==0){const g=A.trim()||`process exited with code ${n}`;this.handleProcessResult(t,e,f,g,s,h)}else f.trim()?this.handleProcessResult(t,e,f,"",s,h):this.activeEventSentViaTool?this.handleProcessResult(t,e,f,"",s,h):setTimeout(()=>{const g=this.extractLogError(r);this.handleProcessResult(t,e,f,g,s,h)},300)}),v.stdin?.end();const $=this.auditCaptures.get(t);$&&($.logPath=r,$.logStartOffset=0),this.startTurnWatchdog(t,r)}startTurnWatchdog(t,e){this.stopTurnWatchdog(),this.watchdogPollTimer=setInterval(()=>{if(this.activeEventId!==t||!this.activeProcess){this.stopTurnWatchdog();return}if(this.watchLogHasTurnEndMarker(e)){this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),this.watchdogGraceTimer||(a.warn(c,`Turn end marker logged for event ${t} but process still alive; grace ${_}ms before kill`),this.watchdogGraceTimer=setTimeout(()=>{if(this.watchdogGraceTimer=null,!(this.activeEventId!==t||!this.activeProcess)){a.warn(c,`agy process still alive ${_}ms after turn end (shutdown deadlock); killing process group for event ${t}`),this.watchdogKilledActiveProcess=!0;try{E(this.activeProcess,"SIGKILL")}catch{}}},_),this.watchdogGraceTimer.unref());return}if(this.scanTurnLogActivity(e),this.inFlightToolName||this.inactivityWarned)return;const i=Date.now()-this.lastActivityAt;if(i<this.inactivityTimeoutMs)return;this.inactivityWarned=!0;const s=Math.max(1,Math.round(i/6e4));a.warn(c,`No activity for ${Math.round(i/1e3)}s on event ${t} (threshold ${Math.round(this.inactivityTimeoutMs/1e3)}s); upstream turn appears stalled, notifying user (process left running)`),this.callbacks.sendStreamChunk(t,this.activeEventSessionId??"",`\u26A0\uFE0F \u672C\u8F6E\u5DF2\u7EA6 ${s} \u5206\u949F\u6CA1\u6709\u4EFB\u4F55\u54CD\u5E94\uFF0C\u4E0A\u6E38\u7591\u4F3C\u6302\u6B7B\u3002\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF08\u4E0A\u6E38\u6062\u590D\u540E\u4F1A\u7EE7\u7EED\uFF09\uFF0C\u4F60\u53EF\u4EE5\u56DE\u590D /stop \u7EC8\u6B62\uFF0C\u6216\u7EE7\u7EED\u7B49\u5F85\uFF0830 \u5206\u949F\u4E0A\u9650\u540E\u5C06\u81EA\u52A8\u5224\u5931\u8D25\uFF09\u3002`,1,!1,`agy-stall-${t}`)},it),this.watchdogPollTimer.unref()}handleTurnResultReceived(t){this.activeEventId!==t||!this.activeProcess||this.watchdogGraceTimer||(this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),a.info(c,`Stream-json result received for event ${t}; grace ${_}ms before kill`),this.watchdogGraceTimer=setTimeout(()=>{if(this.watchdogGraceTimer=null,!(this.activeEventId!==t||!this.activeProcess)){a.warn(c,`agy process still alive ${_}ms after result event (shutdown deadlock); killing process group for event ${t}`),this.watchdogKilledActiveProcess=!0;try{E(this.activeProcess,"SIGKILL")}catch{}}},_),this.watchdogGraceTimer.unref())}stopTurnWatchdog(){this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),this.watchdogGraceTimer&&(clearTimeout(this.watchdogGraceTimer),this.watchdogGraceTimer=null)}touchActivity(){this.lastActivityAt=Date.now()}scanTurnLogActivity(t){let e;try{e=T(t)}catch{return}if(e.length<=this.watchLogOffset){this.watchLogOffset=e.length;return}const i=e.subarray(this.watchLogOffset).toString("utf-8");this.watchLogOffset=e.length;for(const s of i.split(`
18
- `))if(s.trim()&&!nt.some(r=>s.includes(r))){a.debug(c,`turn log activity: ${s.slice(0,120)}`),this.touchActivity();return}}watchLogHasTurnEndMarker(t){try{return T(t,"utf-8").includes(st)}catch{return!1}}buildPrintArgs(t,e,i){const s=["--print-timeout",ot,"--log-file",i],r=e.modelId?X(e.modelId,z()):"";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("--output-format","stream-json"),s.push("-p",t),s}readAgyConversationId(t){try{if(k(N)){const e=JSON.parse(T(N,"utf-8"));return e[et(t)]||e[t]||void 0}}catch(e){a.debug(c,`readAgyConversationId: ${e}`)}}extractLogError(t){try{if(!k(t))return a.info(c,`extractLogError: log file not found: ${t}`),"agy completed without output";const e=T(t,"utf-8");if(!e)return a.info(c,`extractLogError: log file is empty: ${t}`),"agy completed without output";const i=e.split(`
19
- `).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,l){if(this.activeEventId===t){if(this.inactivityWarned&&this.callbacks.sendStreamChunk(t,e,"",2,!0,`agy-stall-${t}`),s&&!i.trim()){a.error(c,`Event ${t} failed: ${s}`);const d=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,d,1,!0,`agy-${t}`),this.callbacks.sendEventResult(t,"failed",s)}else{const o=i.trim();if(o){const h=r.cwd,p=h?this.extractDelta(o,h):o;h&&this.conversationOutputCache.set(h,o);const v=this.auditCaptures.get(t);v&&(v.response=p),p&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,p,1,!0,`agy-${t}`)}const d=l||(r.cwd?this.readAgyConversationId(r.cwd):void 0);d&&d!==r.conversationId&&(this.sessionConversationMap.set(e,d),this.callbacks.persistConversationId(e,d)),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,this.inactivityWarned=!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{Pt as AgyAdapter};
16
+ `}renderContextBlock(t){const e=t.context_messages_json;if(!e)return"";let i;try{const o=JSON.parse(e);if(!Array.isArray(o))return"";i=o}catch{return""}const s=String(t.session_type??"")==="2",r=String(t.msg_id??""),d=[];for(const o of i){const h=String(o?.msg_id??"");if(h&&h===r)continue;const l=String(o?.content??"").trim();if(!l)continue;const f=String(o?.sender_id??"");if(l.startsWith("[\u5F15\u7528\u6D88\u606F]")){const y=l.slice(6).replace(/^\s*\n?/,"").trim();d.push(s&&f?`[\u5F15\u7528\u6D88\u606F] (\u6765\u81EA ${f})\uFF1A${y}`:`[\u5F15\u7528\u6D88\u606F]\uFF1A${y}`)}else d.push(s&&f?`[${f}]\uFF1A${l}`:l)}return d.join(`
17
+ `)}spawnAgyPrint(t,e,i,s){try{G(N,{recursive:!0})}catch(n){const u=n instanceof Error?n.message:String(n),A=`unable to prepare agy log directory: ${u}`;a.error(c,`Log setup failed for event ${t}: ${u}`),this.handleProcessResult(t,e,"",A,s);return}const r=I(N,`grix-${M()}.log`),d=this.buildPrintArgs(i,s,r),o={...process.env,...this.config.env??{}};let h="",l,f="",y=!1;const C=new et({onInit:n=>{l=n,this.sessionConversationMap.set(e,n),this.callbacks.persistConversationId(e,n)},onToolStart:(n,u)=>{this.touchActivity();let A=$(n),p=n,v=u;if(n==="call_mcp_tool"){const g=typeof u.ToolName=="string"?u.ToolName:"";(typeof u.ServerName=="string"?u.ServerName:"").startsWith("grix")||$(g)?A=!0:g&&(p=g,v=u.Arguments||u)}if(A)return;this.inFlightToolName=p;const S=JSON.stringify(v??{});this.callbacks.sendToolUse?.(t,e,p,S)},onToolDone:(n,u,A,p)=>{this.touchActivity();let v=$(n),S=n;if(n==="call_mcp_tool"){const g=typeof u.ToolName=="string"?u.ToolName:"";(typeof u.ServerName=="string"?u.ServerName:"").startsWith("grix")||$(g)?v=!0:g&&(S=g)}(this.inFlightToolName===S||this.inFlightToolName===n)&&(this.inFlightToolName=null),!v&&p&&this.callbacks.sendToolResult?.(t,e,S,`(failed: ${p})`)},onTextDelta:n=>{this.touchActivity()},onResult:n=>{this.touchActivity(),this.inFlightToolName=null,y=!0,n.status==="ERROR"||n.error!==void 0&&n.error!==""?f=n.error?.trim()||`agy turn failed with status ${n.status??"ERROR"}`:n.response!==void 0&&(h=n.response),n.conversationId&&(l=n.conversationId,this.sessionConversationMap.set(e,n.conversationId),this.callbacks.persistConversationId(e,n.conversationId)),this.handleTurnResultReceived(t)},onLine:n=>{this.touchActivity()}});a.info(c,`Spawning: agy ${d.map(n=>n.includes(" ")?`"${n}"`:n).join(" ")}`);const E=F(this.config.command,d,{cwd:s.cwd,env:o,stdio:["pipe","pipe","pipe"],detached:process.platform!=="win32"});this.activeProcess=E,this.lastActivityAt=Date.now(),this.watchLogOffset=0,this.inactivityWarned=!1,this.inactivityTimeoutMs=at("GRIX_AGY_INACTIVITY_TIMEOUT_MS",O);const x=[],R=[];E.stdout?.on("data",n=>{x.push(n),C.feed(n),this.touchActivity()}),E.stderr?.on("data",n=>{R.push(n),this.touchActivity()}),E.on("error",n=>{a.error(c,`Process spawn error for event ${t}: ${n.message}`),this.handleProcessResult(t,e,"",`spawn error: ${n.message}`,s)}),E.on("close",n=>{if(a.info(c,`Process exited for event ${t} with code ${n}`),this.activeEventId!==t)return;C.flush(),this.stopTurnWatchdog();const u=Buffer.concat(x).toString("utf-8"),A=Buffer.concat(R).toString("utf-8"),p=h.trim()?h:y?"":u,v=this.auditCaptures.get(t);v&&(v.stdout=u,v.stderr=A,v.exitCode=n);const S=this.watchdogKilledActiveProcess&&n===null;if(this.watchdogKilledActiveProcess=!1,S)p.trim()||this.activeEventSentViaTool?this.handleProcessResult(t,e,p,"",s,l):setTimeout(()=>{const g=f||this.extractLogError(r);this.handleProcessResult(t,e,p,g,s,l)},300);else if(f)this.handleProcessResult(t,e,"",f,s,l);else if(n!==0){const g=A.trim()||`process exited with code ${n}`;this.handleProcessResult(t,e,p,g,s,l)}else p.trim()?this.handleProcessResult(t,e,p,"",s,l):this.activeEventSentViaTool?this.handleProcessResult(t,e,p,"",s,l):setTimeout(()=>{const g=this.extractLogError(r);this.handleProcessResult(t,e,p,g,s,l)},300)}),E.stdin?.end();const k=this.auditCaptures.get(t);k&&(k.logPath=r,k.logStartOffset=0),this.startTurnWatchdog(t,r)}startTurnWatchdog(t,e){this.stopTurnWatchdog(),this.watchdogPollTimer=setInterval(()=>{if(this.activeEventId!==t||!this.activeProcess){this.stopTurnWatchdog();return}if(this.watchLogHasTurnEndMarker(e)){this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),this.watchdogGraceTimer||(a.warn(c,`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)){a.warn(c,`agy process still alive ${T}ms after turn end (shutdown deadlock); killing process group for event ${t}`),this.watchdogKilledActiveProcess=!0;try{_(this.activeProcess,"SIGKILL")}catch{}}},T),this.watchdogGraceTimer.unref());return}if(this.scanTurnLogActivity(e),this.inFlightToolName||this.inactivityWarned)return;const i=Date.now()-this.lastActivityAt;if(i<this.inactivityTimeoutMs)return;this.inactivityWarned=!0;const s=Math.max(1,Math.round(i/6e4));a.warn(c,`No activity for ${Math.round(i/1e3)}s on event ${t} (threshold ${Math.round(this.inactivityTimeoutMs/1e3)}s); upstream turn appears stalled, notifying user (process left running)`),this.callbacks.sendStreamChunk(t,this.activeEventSessionId??"",`\u26A0\uFE0F \u672C\u8F6E\u5DF2\u7EA6 ${s} \u5206\u949F\u6CA1\u6709\u4EFB\u4F55\u54CD\u5E94\uFF0C\u4E0A\u6E38\u7591\u4F3C\u6302\u6B7B\u3002\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF08\u4E0A\u6E38\u6062\u590D\u540E\u4F1A\u7EE7\u7EED\uFF09\uFF0C\u4F60\u53EF\u4EE5\u56DE\u590D /stop \u7EC8\u6B62\uFF0C\u6216\u7EE7\u7EED\u7B49\u5F85\uFF0830 \u5206\u949F\u4E0A\u9650\u540E\u5C06\u81EA\u52A8\u5224\u5931\u8D25\uFF09\u3002`,1,!1,`agy-stall-${t}`)},rt),this.watchdogPollTimer.unref()}handleTurnResultReceived(t){this.activeEventId!==t||!this.activeProcess||this.watchdogGraceTimer||(this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),a.info(c,`Stream-json result received for event ${t}; grace ${T}ms before kill`),this.watchdogGraceTimer=setTimeout(()=>{if(this.watchdogGraceTimer=null,!(this.activeEventId!==t||!this.activeProcess)){a.warn(c,`agy process still alive ${T}ms after result event (shutdown deadlock); killing process group for event ${t}`),this.watchdogKilledActiveProcess=!0;try{_(this.activeProcess,"SIGKILL")}catch{}}},T),this.watchdogGraceTimer.unref())}stopTurnWatchdog(){this.watchdogPollTimer&&(clearInterval(this.watchdogPollTimer),this.watchdogPollTimer=null),this.watchdogGraceTimer&&(clearTimeout(this.watchdogGraceTimer),this.watchdogGraceTimer=null)}touchActivity(){this.lastActivityAt=Date.now()}scanTurnLogActivity(t){let e;try{e=w(t)}catch{return}if(e.length<=this.watchLogOffset){this.watchLogOffset=e.length;return}const i=e.subarray(this.watchLogOffset).toString("utf-8");this.watchLogOffset=e.length;for(const s of i.split(`
18
+ `))if(s.trim()&&!ot.some(r=>s.includes(r))){a.debug(c,`turn log activity: ${s.slice(0,120)}`),this.touchActivity();return}}watchLogHasTurnEndMarker(t){try{return w(t,"utf-8").includes(nt)}catch{return!1}}buildPrintArgs(t,e,i){const s=["--print-timeout",ct,"--log-file",i],r=e.modelId?tt(e.modelId,Z()):"";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("--output-format","stream-json"),s.push("-p",t),s}readAgyConversationId(t){try{if(b(j)){const e=JSON.parse(w(j,"utf-8"));return e[it(t)]||e[t]||void 0}}catch(e){a.debug(c,`readAgyConversationId: ${e}`)}}extractLogError(t){try{if(!b(t))return a.info(c,`extractLogError: log file not found: ${t}`),"agy completed without output";const e=w(t,"utf-8");if(!e)return a.info(c,`extractLogError: log file is empty: ${t}`),"agy completed without output";const i=e.split(`
19
+ `).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,d){if(this.activeEventId===t){if(this.inactivityWarned&&this.callbacks.sendStreamChunk(t,e,"",2,!0,`agy-stall-${t}`),s&&!i.trim()){a.error(c,`Event ${t} failed: ${s}`);const o=s.includes("RESOURCE_EXHAUSTED")||s.includes("quota"),h=s.toLowerCase().includes("authentication failed"),l=o?"The agy API quota has been exhausted. Please try again later.":h?"agy authentication failed or timed out. Please check the agy login status and try again.":`agy execution failed: ${s}`;this.callbacks.sendStreamChunk(t,e,l,1,!0,`agy-${t}`),this.callbacks.sendEventResult(t,"failed",s)}else{const o=i.trim();if(o){const l=r.cwd,f=l?this.extractDelta(o,l):o;l&&this.conversationOutputCache.set(l,o);const y=this.auditCaptures.get(t);y&&(y.response=f),f&&!this.activeEventSentViaTool&&this.callbacks.sendStreamChunk(t,e,f,1,!0,`agy-${t}`)}const h=d||(r.cwd?this.readAgyConversationId(r.cwd):void 0);h&&h!==r.conversationId&&(this.sessionConversationMap.set(e,h),this.callbacks.persistConversationId(e,h)),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,this.inactivityWarned=!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{kt as AgyAdapter};
@@ -1,2 +1,2 @@
1
- import{log as l}from"../../core/log/index.js";const u="agy-stream-parser";class h{buffer="";callbacks;constructor(e){this.callbacks=e}feed(e){const s=typeof e=="string"?e:e.toString("utf-8");this.buffer+=s;let n;for(;(n=this.buffer.indexOf(`
2
- `))!==-1;){const t=this.buffer.slice(0,n).trim();this.buffer=this.buffer.slice(n+1),t&&this.processLine(t)}}flush(){const e=this.buffer.trim();this.buffer="",e&&this.processLine(e)}processLine(e){if(this.callbacks.onLine?.(e),!e.startsWith("{"))return;let s;try{s=JSON.parse(e)}catch{return}const n=s.event;if(typeof n=="string")switch(n){case"init":{const t=typeof s.conversation_id=="string"?s.conversation_id:"";t&&this.callbacks.onInit?.(t);break}case"step_update":{const t=s.step_update;if(!t)break;const i=t.step_type,a=t.state;if(i==="tool"){const o=t.tool_name||"",r=t.tool_info||{},c=r.parameters||{},f=typeof r.output=="string"?r.output:void 0,d=typeof r.error=="string"?r.error:void 0;a==="ACTIVE"?this.callbacks.onToolStart?.(o,c):a==="DONE"&&this.callbacks.onToolDone?.(o,c,f,d)}else if(i==="agent_response"){const o=typeof t.text_delta=="string"?t.text_delta:"";o&&this.callbacks.onTextDelta?.(o)}break}case"result":{const t=s.result;if(!t)break;const i=typeof t.conversation_id=="string"?t.conversation_id:void 0,a=typeof t.status=="string"?t.status:void 0,o=typeof t.response=="string"?t.response:void 0,r=typeof t.duration_seconds=="number"?t.duration_seconds:void 0,c=Array.isArray(t.denied_actions)?t.denied_actions:void 0,f=t.usage||void 0;this.callbacks.onResult?.({conversationId:i,status:a,response:o,durationSeconds:r,deniedActions:c,usage:f});break}default:l.debug(u,`Unhandled stream-json event: ${n}`);break}}}export{h as AgyStreamParser};
1
+ import{log as l}from"../../core/log/index.js";const u="agy-stream-parser";class h{buffer="";callbacks;constructor(t){this.callbacks=t}feed(t){const s=typeof t=="string"?t:t.toString("utf-8");this.buffer+=s;let n;for(;(n=this.buffer.indexOf(`
2
+ `))!==-1;){const e=this.buffer.slice(0,n).trim();this.buffer=this.buffer.slice(n+1),e&&this.processLine(e)}}flush(){const t=this.buffer.trim();this.buffer="",t&&this.processLine(t)}processLine(t){if(this.callbacks.onLine?.(t),!t.startsWith("{"))return;let s;try{s=JSON.parse(t)}catch{return}const n=s.event;if(typeof n=="string")switch(n){case"init":{const e=typeof s.conversation_id=="string"?s.conversation_id:"";e&&this.callbacks.onInit?.(e);break}case"step_update":{const e=s.step_update;if(!e)break;const i=e.step_type,a=e.state;if(i==="tool"){const o=e.tool_name||"",r=e.tool_info||{},c=r.parameters||{},f=typeof r.output=="string"?r.output:void 0,d=typeof r.error=="string"?r.error:void 0;a==="ACTIVE"?this.callbacks.onToolStart?.(o,c):a==="DONE"&&this.callbacks.onToolDone?.(o,c,f,d)}else if(i==="agent_response"){const o=typeof e.text_delta=="string"?e.text_delta:"";o&&this.callbacks.onTextDelta?.(o)}break}case"result":{const e=s.result;if(!e)break;const i=typeof e.conversation_id=="string"?e.conversation_id:void 0,a=typeof e.status=="string"?e.status:void 0,o=typeof e.response=="string"?e.response:void 0,r=typeof e.error=="string"?e.error:void 0,c=typeof e.duration_seconds=="number"?e.duration_seconds:void 0,f=Array.isArray(e.denied_actions)?e.denied_actions:void 0,d=e.usage||void 0;this.callbacks.onResult?.({conversationId:i,status:a,response:o,error:r,durationSeconds:c,deniedActions:f,usage:d});break}default:l.debug(u,`Unhandled stream-json event: ${n}`);break}}}export{h as AgyStreamParser};