grix-connector 4.12.1 → 4.12.3

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 (35) hide show
  1. package/dist/adapter/acp/acp-adapter.js +13 -13
  2. package/dist/adapter/claude/claude-adapter.js +1 -1
  3. package/dist/adapter/cursor/cursor-adapter.js +1 -1
  4. package/dist/adapter/opencode/opencode-adapter.js +1 -1
  5. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.12.3.tgz +0 -0
  6. package/dist/assets/dsh-bridge/manifest.json +5 -5
  7. package/dist/bridge/adapter-pool.js +1 -1
  8. package/dist/bridge/bridge.js +2 -2
  9. package/dist/bridge/event-stop.js +1 -1
  10. package/dist/bridge/send-controller.js +3 -3
  11. package/dist/default-skills/grix-access-control/SKILL.md +1 -1
  12. package/dist/default-skills/grix-admin/SKILL.md +1 -1
  13. package/dist/default-skills/grix-agent-dispatch/SKILL.md +1 -1
  14. package/dist/default-skills/grix-audit-data/SKILL.md +1 -1
  15. package/dist/default-skills/grix-chat-state/SKILL.md +1 -1
  16. package/dist/default-skills/grix-egg/SKILL.md +1 -1
  17. package/dist/default-skills/grix-group/SKILL.md +1 -1
  18. package/dist/default-skills/grix-owner-relay/SKILL.md +1 -1
  19. package/dist/default-skills/grix-query/SKILL.md +1 -1
  20. package/dist/default-skills/grix-scheduled-trigger/SKILL.md +1 -1
  21. package/dist/default-skills/grix-skill-library/SKILL.md +1 -1
  22. package/dist/default-skills/grix-task-flow/SKILL.md +12 -524
  23. package/dist/default-skills/grix-task-flow/references/planning-and-flowcharts.md +173 -0
  24. package/dist/default-skills/grix-task-flow/references/receipts-and-watchdogs.md +171 -0
  25. package/dist/default-skills/grix-task-flow/references/recursion-and-examples.md +192 -0
  26. package/dist/default-skills/grix-widget-visitor-ban/SKILL.md +1 -1
  27. package/dist/default-skills/index.js +1 -1
  28. package/dist/default-skills/message-edit/SKILL.md +1 -1
  29. package/dist/default-skills/message-send/SKILL.md +1 -1
  30. package/dist/default-skills/message-unsend/SKILL.md +1 -1
  31. package/dist/default-skills/tailnet-file-share/SKILL.md +1 -1
  32. package/dist/mcp/stream-http/security.js +1 -1
  33. package/docs/guide-acp-agent-integration.md +6 -2
  34. package/package.json +1 -1
  35. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.12.1.tgz +0 -0
@@ -1,20 +1,20 @@
1
- import{fileURLToPath as X}from"node:url";import p from"node:path";import{homedir as g}from"node:os";import{promises as y,statSync as E}from"node:fs";import{EventEmitter as $}from"node:events";import{spawn as M}from"node:child_process";import{AgentProcess as V}from"../../agent/process.js";import{hasChildProcesses as Y,listGroupPids as J}from"../../core/runtime/spawn.js";import{applyNativeProviderConfig as Z,agentIdOf as C}from"../shared/native-provider-config.js";import{applyMcodeProviderConfig as ee}from"./mcode-provider-config.js";import{buildSessionContextBlock as te}from"../shared/session-context.js";import{IdentityInjector as ie}from"../shared/identity-injector.js";import{compactToolInputForWire as x}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as se,resolveKimiCodeHome as ne}from"../../default-skills/index.js";import{resolveHermesHomeDir as oe}from"../shared/native-provider-config.js";import{prepareKimiLayerPlugin as re}from"../../kimi-plugins/index.js";import{AcpClient as ae,AcpAuthRequiredError as le,isAuthRequiredError as ce}from"../../protocol/acp-client.js";import{AgentEventType as f}from"../../types/events.js";import{InternalApiServer as de}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as ue}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as S}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as he}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as pe}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as me}from"../../core/protocol/message-metadata.js";import{insertBeforeTrailingMessageReferences as _}from"../../core/protocol/message-reference.js";import{log as r}from"../../core/log/index.js";function q(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 fe=5e3;function N(c,e){const t=e.toolName??"";return e.toolCallId&&(!t||t===e.toolCallId)?c.toolNamesById.get(e.toolCallId)??t:t}function b(c){const e=String(c??"").trim();if(!e)return"";try{const t=JSON.parse(e);return JSON.stringify(w(t))}catch{return e}}function w(c){if(c===null||typeof c!="object")return c;if(Array.isArray(c))return c.map(w);const e=c,t=Object.keys(e).sort(),i={};for(const s of t)i[s]=w(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 a=n.text;typeof a=="string"&&a.trim()&&t.push(a.trim())}}return t.join(`
2
- `)}function ge(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 a of n){const o=x(a);if(o&&o!==t)return o}return""}const O=/^q\d+_opt_\d+$/,ve=/^q\d+_skip$/,Ie=new Set(["allow_once","allow_always","reject_once","reject_always"]);function ye(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=>O.test(l.optionId)),s=i?t.filter(l=>O.test(l.optionId)):t.filter(l=>!ve.test(l.optionId));if(s.length===0||!i&&s.every(l=>Ie.has(l.kind)))return null;const n=c.rawParams&&typeof c.rawParams=="object"?c.rawParams:{},a=n.toolCall&&typeof n.toolCall=="object"?n.toolCall:{},o=D(a.content).trim();return o?{prompt:o,answerOptions:s}:null}import{scanSkills as B}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as Ae,checkKimiSessionActivity as Ce,checkQoderSessionActivity as F,checkDimSessionActivity as Se,hasRunningKimiDetachedTask as be}from"./session-activity.js";import{resolveCliPath as Te,getCliVersion as _e}from"../../core/util/cli-probe.js";const R=p.dirname(X(import.meta.url)),U=[{modelId:"deepseek-v4-pro",name:"DeepSeek V4 Pro"},{modelId:"deepseek-v4-flash",name:"DeepSeek V4 Flash"}],k=[{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 P(c,e){const t=Number(process.env[c]);return Number.isFinite(t)&&t>0?t:e}const we=200,Re=60*1e3,ke=600*1e3,T=P("GRIX_ACP_TURN_INACTIVITY_TIMEOUT_MS",900*1e3),j=2,L=5e3,K=80,W=1200*1e3,Pe=P("GRIX_ACP_BUSY_RETRY_INTERVAL_MS",5e3),H=P("GRIX_ACP_BUSY_WAIT_MAX_MS",1800*1e3),Ee=6e4,$e=2e3,Me=12e3,G=3e3,Q=3e4,xe=["[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 qe(c){return!(!(c instanceof Error)||c.code!==-32603||ce(c))}function Ne(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 A(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 De(c){return c.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class z 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 ie("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 ue(i.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const a=this.bindingStore.get(this.currentAibotSessionId);a?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,a.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 Oe(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(Q))}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?Y(e,[e,...this.baselineGroupPids]):!1}captureBaselineGroupPids(){const e=this.agentProcess?.pid;e&&J(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,a;if(s){const h=await _e(t);n=h.version,h.error&&(a=h.error)}else a={code:"cli_not_found",message:`command not found: ${t}`};const o=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,...a?{error:a}:{}},conversation:d,config:{model:o,base_url:null,source:{model:o?"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?U:this.isKimi?k: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 a=i?.modes.map(m=>({id:m.id,name:m.name}))??[],o=i?.models&&i.models.length>0,l=o?i.models:this.isReasonix?U:this.isKimi?k:[],d=o?"acp":this.isReasonix?"reasonix_fallback":this.isKimi?"kimi_fallback":"empty";this.isKimi&&!o&&r.warn("acp-adapter",`[toolbar-diag] kimi ACP session returned no models; using static fallback. command=${this.config.command} fallbackIds=${JSON.stringify(k.map(m=>m.modelId))}`),r.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})),I=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:I,availableModels:u,availableModes:I,models:h,modes:a};return r.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?(r.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=>{r.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):(r.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=>{r.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",a=String(i.decision??"");let o;return n&&(a==="allow-once"||a==="allow-always")?o=a:n?o="allow":o="deny",s?this.handleAcpApprovalAction(s,o)?(this.callbacks.sendLocalActionResult(e.action_id,"ok"),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"approval_not_found",`no pending approval for tool_call_id: ${s}`),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"tool_call_id_required","tool_call_id is required"),{handled:!0,kind:"approval"})}return{handled:!1,kind:""}}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 a;if(n==="action"&&String(s.value??"")==="cancel")a=i.options.find(l=>/_skip$/.test(l.optionId)||l.kind.toLowerCase().includes("reject"));else{let l="";if(n==="text")l=String(s.value??"").trim();else if(n==="map"){const d=Array.isArray(s.entries)?s.entries:[];l=String(d[0]?.value??"").trim()}a=i.options.find(d=>d.name.trim()===l||d.optionId===l)}if(!a)return{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,a.optionId)}catch(l){return{delivered:!1,errorCode:"question_reply_failed",errorMsg:l instanceof Error?l.message:String(l),kind:"question_reply_failed"}}this.pendingQuestions.delete(t);const o=/_skip$/.test(a.optionId);return this.captureAuditEvent({type:f.PermissionResponse,requestId:t,permissionDecision:o?"deny":"allow"}),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun)),{delivered:!0,result:{request_id:t,resolution:o?"cancel":"answer"},kind:o?"question_reply_cancel":"question_reply"}}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{r.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=>{r.error("acp-adapter",`Failed to create session on bind: ${A(s)}`);const n=this.buildToolbarContext("bind_failed",t),a=Array.isArray(n.available_models)&&n.available_models.length>0;return this.callbacks.sendUpdateBindingCard(e,"failed",t,a?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){r.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(i,t)}catch(s){r.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){r.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),r.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(Q),r.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);r.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${t.status})`),this.sendTerminalEventWithoutRun(e.event_id,t.status,t.msg);return}if(this.compacting){r.info("acp-adapter",`Event ${e.event_id} deferred: compaction in progress`),this.deferEvent(e);return}if(this.activeRun){r.info("acp-adapter",`Event ${e.event_id} rejected: busy`),this.sendTerminalEventWithoutRun(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){r.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),r.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)r.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){r.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 he,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=pe(e,this.resolveCwd());this.resetIdleTimer(s),n.modeId&&this.acpClient&&this.acpClient.setLiveMode(n.modeId).catch(()=>{}),n.modelId&&this.acpClient&&this.acpClient.setModel(n.modelId).catch(()=>{});const a=me(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),o=this.injectQuoteProtocolOnce(s.sessionId,a),l=this.identity.injectOnce(s.sessionId,o);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);this.sendPromptWithRetry(s,h)}injectQuoteProtocolOnce(e,t){return!e||this.quoteProtocolTaughtSessions.has(e)?t:(this.quoteProtocolTaughtSessions.add(e),_(t,xe))}injectSessionContextOnce(e,t){return!e||!this.sessionContextPending?t:(this.sessionContextPending=!1,r.info("acp-adapter",`Injected Grix session context for session=${e}`),_(t,te(e,this.callbacks.getAgentId?.())))}injectRecoveryContext(e,t){const i=this.recoveryContextBySessionId.get(e);return i?(this.recoveryContextBySessionId.delete(e),r.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${i.length})`),_(t,`[recovery-context]
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]
4
4
  ${i}
5
- [/recovery-context]`)):t}sendPromptWithRetry(e,t){this.acpClient.send(t).then(()=>{this.activeRun===e&&(e.busyWaiting=!1)}).catch(i=>{if(Ne(i)){this.scheduleAgentBusyRetry(e,t,A(i));return}if(r.error("acp-adapter",`Prompt failed: ${A(i)}`),qe(i)){const n=A(i),a=e.eventId,o=e.sessionId;r.info("acp-adapter",`Internal error escalated to bridge: event=${a} session=${o} err=${n}`),this.silentlyDiscardActiveRun(e),this.emit("internalError",{eventId:a,sessionId:o,errorMsg:n});return}const s=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>=H){r.error("acp-adapter",`agent busy wait exceeded ${Math.round(H/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(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+=`
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 a=Math.min(Pe*2**e.busyRetryCount,Ee);e.busyRetryCount+=1,r.info("acp-adapter",`agent busy, keeping event queued event=${e.eventId} waited=${Math.round(n/1e3)}s retry_in=${a}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)}},a),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 V(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 Z(this.config.command,i,this.config.env,this.nativeProviderScope).catch(s=>{r.warn("acp-adapter",`native provider config skipped: ${s instanceof Error?s.message:String(s)}`)}),C(this.config.command)==="mcode"){const s=this.config.env??{};await ee(this.config.command,{baseUrl:s.GRIX_MCODE_BASE_URL,apiKey:s.GRIX_MCODE_API_KEY,model:s.GRIX_MCODE_MODEL}).catch(n=>{r.warn("acp-adapter",`mcode provider config skipped: ${n instanceof Error?n.message:String(n)}`)})}r.info("acp-adapter",`Spawning ACP agent command=${this.config.command} args=${JSON.stringify(e)} cwd=${i} envKeys=${JSON.stringify(Object.keys(this.config.env??{}).sort())}`);try{if(this.isKimi){const s=re({...process.env,...this.config.env});r.info("acp-adapter",`Prepared Kimi Layer plugin at ${s.pluginRoot} (registrationChanged=${s.registrationChanged})`)}await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:i,env:this.config.env})}catch(s){throw r.error("acp-adapter",`Failed to spawn agent process: ${s}`),this.rejectDeferredEvents(`agent spawn failed: ${s instanceof Error?s.message:String(s)}`),this.emit("exit",null),s}r.info("acp-adapter","ACP agent process started"),this.agentProcess.on("error",s=>{this.stopped||(r.error("acp-adapter",`Agent process error: ${s.message}`),this.activeRun&&this.finishRun("failed",`agent process error: ${s.message}`),this.rejectDeferredEvents(`agent process error: ${s.message}`),this.emit("exit",null))}),this.agentProcess.on("exit",s=>{this.lastExitCode=s,!this.stopped&&(r.error("acp-adapter",`Agent process exited unexpectedly (code=${s})`),this.activeRun&&this.finishRun("failed",`agent process exited (code=${s})`),this.rejectDeferredEvents(`agent process exited (code=${s})`),this.emit("exit",s))}),this.agentProcess.on("stderr",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)})}async connectSession(e){if(this.sessionConnected||!this.agentProcess?.transport)return;let t;this.acpMcpTools&&(t=await this.startInternalApiAndMcp(e||this.resolveCwd())),this.acpClient=new ae,this.acpClient.on("event",l=>this.handleAcpEvent(l)),this.acpClient.on("activity",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)}),this.acpClient.on("session-lost",()=>{this.stopped||this.agentProcess?.alive&&(r.error("acp-adapter","ACP transport closed while agent process still alive, triggering respawn"),this.activeRun&&(this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,"ACP transport closed"),this.finishRun("failed","ACP transport closed")),this.emit("exit",null))});const i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=!!i&&i!=="__continue__",n=async l=>{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 a=!1,o;try{await n(i)}catch(l){if(l instanceof le){await this.handleAuthRequired(l);return}if(!i)throw this.handleConnectFailure(l),l;if(r.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${A(l)}`),a=!0,o=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${i}`);const d=await this.buildKiroRecoveryContext(this.currentAibotSessionId,i,e||this.resolveCwd());d?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,d),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${d.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${i}`)}await n(void 0)}if(this.sessionConnected=!0,this.sessionContextPending=!s||a,!this.sessionContextPending){const l=this.callbacks.getAgentProfile?.(),d=l?.agentName?.trim()??"",h=l?.introduction?.trim()??"",u=I=>{const v=this.bindingStore?.getIdentitySnapshot(I);v&&v.name===d&&v.intro===h&&this.identity.markInjected(I)};this.currentAibotSessionId&&u(this.currentAibotSessionId);for(const I of this.sessionBindings.keys())u(I)}this.captureBaselineGroupPids(),r.info("acp-adapter",`ACP session ready: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId),this.currentAibotSessionId&&this.bindingStore&&this.bindingStore.setAcpSessionId(this.currentAibotSessionId,this.acpClient.sessionId);for(const[l,d]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(l,this.acpClient.sessionId);const h=a?"binding_recreated":"binding_ready",u=this.buildToolbarContext(h,d);a&&o&&(u.previous_session_id=o,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(l,"ready",d,u)}}async buildKiroRecoveryContext(e,t,i){try{const s=p.join(g(),".kiro","sessions","cli",`${t}.json`);await y.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,i);if(!n)return;const a=p.join(g(),".grix","data","session-summaries");await y.mkdir(a,{recursive:!0});const o=p.join(a,`${e}.md`),l=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
8
- `);return await y.writeFile(o,l,"utf8"),r.info("acp-adapter",`Recovery summary saved: ${o} (chars=${n.length})`),["[\u5386\u53F2\u4E0A\u4E0B\u6587\u56DE\u704C\u8BF4\u660E]","\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u65E7\u4F1A\u8BDD\u81EA\u52A8\u6458\u8981\uFF0C\u8BF7\u5728\u540E\u7EED\u56DE\u7B54\u4E2D\u5EF6\u7EED\u5176\u4E2D\u7684\u76EE\u6807\u3001\u7EA6\u675F\u548C\u7ED3\u8BBA\u3002",`SOURCE_KIRO_SESSION_FILE: ${s}`,`SUMMARY_FILE: ${o}`,"",n.trim()].join(`
9
- `)}catch(s){r.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 a=De(n).trim(),o=a.indexOf("> "),l=o>=0?a.slice(o+2).trim():a;if(l)return r.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${a.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,a)=>{const o=M(e,t,{cwd:i,stdio:["ignore","pipe","pipe"],windowsHide:!0});let l="",d="";const h=setTimeout(()=>{o.kill("SIGTERM"),a(new Error(`${e} timed out after ${s}ms`))},s);o.stdout.on("data",u=>{l+=String(u)}),o.stderr.on("data",u=>{d+=String(u)}),o.on("error",u=>{clearTimeout(h),a(u)}),o.on("close",u=>{if(clearTimeout(h),u===0){n(l||d);return}a(new Error(`${e} exited with code ${u}: ${d||l}`))})})}handleConnectFailure(e){r.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 de,this.internalApi.setInvokeHandler(async(o,l,d,h)=>{if(o==="toolbar_set"&&!String(l?.session_id??"").trim()){const u=String(this.currentAibotSessionId??"").trim();u&&(l={...l,session_id:u})}return this.callbacks.agentInvoke(o,l,h)}),await this.internalApi.start(0),r.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(o=>this.onMcpBridgeUp(o));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(ne({...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()||oe({...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 o=se(i);o.length>0&&r.info("acp-adapter",`Synced connector skills to ${i}: [${o.join(", ")}]`)}const s=p.resolve(R,"../../mcp/mcp-bridge-server.js"),n=this.internalApi.mcpBridgeWsUrl,a=p.resolve(R,"../../mcp/acp-mcp-server.js");if(this.isKimi){await this.internalApi.setCodebaseNavigatorCwd(e);const o={Authorization:`Bearer ${this.internalApi.token}`};return[{type:"http",name:"grix-app-bridge",url:this.internalApi.mcpBridgeHttpUrl,headers:o},{type:"http",name:"grix-connector-tools",url:this.internalApi.mcpToolsHttpUrl,headers:o},{type:"http",name:"codebase-navigator",url:this.internalApi.codebaseNavigatorHttpUrl,headers:o}]}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:[a,"--api-url",this.internalApi.url],...!this.isReasonix&&{env:{}}}]}catch(t){r.error("acp-adapter",`Failed to start MCP tools: ${t}`);return}}onMcpBridgeUp(e){this.callbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}cleanup(){this.flushOrphanBuffer(),this.activeRun,this.pendingApprovals.clear(),this.pendingQuestions.clear(),this.pendingAutoCompact=!1,this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.dispose(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null),this.baselineGroupPids=[],this.terminalAuthChild&&(this.terminalAuthChild.kill("SIGKILL"),this.terminalAuthChild=null)}async handleAuthRequired(e){r.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(s=>s.id).join(", ")}`);const t=e.authMethods.find(s=>/oauth|browser/i.test(s.id))??e.authMethods[0];if(!t)throw e;const i=this.currentAibotSessionId??"";this.callbacks.sendAuthNotification(i,`Authentication required (${t.id}). Initiating auth flow...`);for(const[s,n]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(s,"failed",n);if(t.type==="terminal")await this.runTerminalAuth(t,i);else{const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(i,`Please open this URL to authenticate:
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:
13
13
  ${s}
14
14
 
15
- Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(t.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(i,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0,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}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.captureBaselineGroupPids(),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[a,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(a,"ready",o,this.buildToolbarContext("binding_ready",o))}catch(s){throw r.error("acp-adapter",`Auth retry failed: ${s}`),s}}runTerminalAuth(e,t){return new Promise((i,s)=>{const n=M(this.config.command,e.args??[],{env:{...process.env,...this.config.env??{}},stdio:["ignore","pipe","pipe"],windowsHide:!0});this.terminalAuthChild=n;const a=/https?:\/\/[^\s"')\]]+/;let o=!1,l=!1;const d=u=>{if(o)return;const v=u.toString().replace(/\x1b\[[0-9;]*m/g,"").match(a);v&&(o=!0,r.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 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:
16
16
  ${v[0]}
17
17
 
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 o=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(t);o&&(i=!0,this.agentProcess.removeListener("stderr",s),e(o[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{i||(i=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){try{r.info("acp-adapter","incoming acp event",{eventType:e.type,sessionId:this.activeRun?.sessionId??null,eventKeys:Object.keys(e),rawEvent:q(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=b(s);if(t.lastToolUse?.toolName===e.toolName&&b(t.lastToolUse.toolInput)===n)break;t.lastToolUse={toolName:e.toolName,toolInput:s},e.toolCallId&&t.sentToolUseIds.add(e.toolCallId);const a=this.trimToolWirePayload?x(s):s;this.emitRawEventEnvelope(t,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:a}})||this.callbacks.sendToolUse(t.eventId,t.sessionId,e.toolName,a)}break}case f.ToolResult:{t.awaitingToolResult=!1;{const s=e.toolCallId||e.toolName||"";t.lastToolProgress.delete(s),t.lastToolProgressSentAt.delete(s)}if(t.lastToolUse=null,e.content){i=!0;const s=N(t,e),n=e.content,a=b(n);if(t.lastToolResult?.toolName===s&&b(t.lastToolResult.content)===a)break;if(t.lastToolResult={toolName:s,content:n},!this.trimToolWirePayload||e.toolStatus==="failed"){const o=this.trimToolWirePayload?"(failed)":n;this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:s,content:o}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,s,o)}}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(),a=t.lastToolProgressSentAt.get(s);if(a!==void 0&&n-a<fe)break;t.lastToolProgressSentAt.set(s,n);const o=N(t,e);this.emitRawEventEnvelope(t,{type:"tool_progress",payload:{tool_name:o,content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,o,e.content)}break}case f.Thinking:{t.lastToolUse=null,t.lastToolResult=null,e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(t.eventId,t.sessionId,e.content));break}case f.Error:{t.lastToolUse=null,t.lastToolResult=null;const s=String(e.error??"unknown error");this.emitRawEventEnvelope(t,{type:"error",payload:{message:s}}),r.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(t.eventId,t.sessionId,s);break}case f.Result:{if(e.synthetic&&this.shouldDeferSyntheticResultForDetachedKimiTask(t,e)){r.info("acp-adapter",`Synthetic ACP Result deferred: kimi detached task still running event=${t.eventId} session=${t.sessionId}`),this.acpClient?.deferSettledResult();return}this.emitRawEventEnvelope(t,{type:"result",payload:{done:e.done??!0}});const s=t.quotedStream.flush();s.deltaContent&&this.appendToStream(t,s.deltaContent),s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),this.flushStream(),this.finishRun("responded");break}}i&&(t.lastProgressAt=Date.now(),t.idleNoProgressCount=0),this.resetIdleTimer(t)}handlePermissionRequest(e){const t=e.permissionRequest;if(r.info("acp-adapter","permission_request received",{sessionId:this.activeRun?.sessionId,eventId:this.activeRun?.eventId,requestId:e.requestId??null,toolCallId:t?.toolCallId??null,toolName:t?.toolName??null,toolTitle:t?.toolTitle??null,optionsCount:Array.isArray(t?.options)?t.options.length:0,hasActiveRun:this.activeRun!==null,approvalMode:this.approvalMode,rawTransport:this.rawTransport,hasRawEventEnvelopeCb:typeof this.callbacks.sendRawEventEnvelope=="function",hasSendPermissionCardCb:typeof this.callbacks.sendPermissionCard=="function",rawPr:q(t,3e3)}),!t||!e.requestId||!this.acpClient){r.warn("acp-adapter","permission_request dropped: missing prerequisite",{hasPr:t!=null,hasRequestId:!!e.requestId,hasAcpClient:!!this.acpClient}),e.requestId&&this.captureAuditEvent({type:f.PermissionResponse,requestId:e.requestId,permissionDecision:"dropped"});return}if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){r.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${t.toolName}`),this.acpClient.respondPermission(t.requestId,{behavior:"allow"}).catch(()=>{}),this.captureAuditEvent({type:f.PermissionResponse,requestId:t.requestId,toolCallId:t.toolCallId,permissionDecision:"allow"});return}const i=this.activeRun,s=ye(t);if(s&&i&&this.callbacks.sendAgentQuestionCard){this.resetIdleTimer(i),this.pendingQuestions.set(e.requestId,{options:[...t.options]}),this.callbacks.sendAgentQuestionCard(i.eventId,i.sessionId,{request_id:e.requestId,mode:"form",questions:[{header:s.prompt,prompt:s.prompt,options:s.answerOptions.map(o=>o.name)}]}),r.info("acp-adapter","AskUserQuestion forwarded via agent_question card",{sessionId:i.sessionId,eventId:i.eventId,requestId:e.requestId,optionsCount:s.answerOptions.length});return}const n=t.toolCallId,a=ge(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,...a?{tool_input:a}:{},options:t.options}})?r.info("acp-adapter","permission_request forwarded via rawEventEnvelope",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}):(r.info("acp-adapter","permission_request falling back to sendPermissionCard",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}),this.callbacks.sendPermissionCard({eventId:i.eventId,sessionId:i.sessionId,toolCallId:n,toolName:t.toolName,toolTitle:t.toolTitle,toolInput:a,options:t.options}))):(r.info("acp-adapter",`Permission request without active run, auto-approving: ${t.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(n),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);const i=t??(e.awaitingToolResult?ke:Re),s=e.lastProgressAt+T-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>=T){if(t?.isAlive){const s=e.lastProgressAt,n=await this.checkSessionFileActivity(t.sessionId,e.lastIdleCheckAt);if(this.activeRun?.eventId!==e.eventId)return;const a=e.lastProgressAt>s;if(e.lastIdleCheckAt=Date.now(),a||n){n&&(e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,r.info("acp-adapter",`Hard cap recheck found ${a?"stream progress":"session file activity"}, refreshing watchdog: ${e.eventId}`),this.resetIdleTimer(e);return}}r.error("acp-adapter",`Turn inactive ${Math.round(i/1e3)}s \u2265 cap ${Math.round(T/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.checkSessionFileActivity(t.sessionId,d);if(this.activeRun?.eventId!==e.eventId)return;e.lastIdleCheckAt=Date.now(),h?(e.lastProgressAt=Date.now(),r.info("acp-adapter",`Idle timer: tool running, ping ok, session file active, refreshing watchdog: ${e.eventId}`)):r.info("acp-adapter",`Idle timer: tool running, ping ok, continuing until inactivity cap: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e);return}const n=e.lastIdleCheckAt,a=e.lastProgressAt>n,o=a?!1:await this.checkSessionFileActivity(t.sessionId,n);if(this.activeRun?.eventId!==e.eventId)return;const l=a||o;e.lastIdleCheckAt=Date.now(),l?(o&&(r.info("acp-adapter",`Idle timer: no stream progress but session file active, continuing: ${e.eventId}`),e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,r.info("acp-adapter",`Idle timer: ping ok, no stream progress (cycle=${e.idleNoProgressCount}), extending until inactivity cap (${Math.round(T/6e4)}min): ${e.eventId}`),this.resetIdleTimer(e))}).catch(()=>{this.activeRun?.eventId===e.eventId&&this.handlePingFailure(e,t)}):(r.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 a=s?"stream progress":n?"session file activity":e.awaitingToolResult?"tool in flight":"question pending user answer";r.info("acp-adapter",`Idle timer: ping failed but ${a}, 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<j){r.warn("acp-adapter",`Idle timer: ping failed (${e.consecutivePingFailures}/${j}), rechecking in ${L/1e3}s: ${e.eventId}`),this.resetIdleTimer(e,L);return}r.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 Ce(e,t);const i=C(this.config.command);return i==="kiro-cli"?await Ae(e,t):i==="qodercli"?await F(".qoder",e,t):i==="qoderclicn"?await F(".qoder-cn",e,t):i==="dim"?await Se(e,t):!1}catch{return!1}}shouldDeferSyntheticResultForDetachedKimiTask(e,t){if(!this.isKimi)return!1;try{return be(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(),we))}emitSegmentedStream(e,t){if(!t)return;const i=e.markdownSegmenter.push(t);for(const s of i)s.text&&(this.callbacks.sendStreamChunk(e.eventId,e.sessionId,s.text,++e.chunkSeq,s.closeAfter===!0,e.currentClientMsgId,e.quotedMessageId),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,r.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}handleOrphanAcpEvent(e){if(!(!this.sessionConnected||this.compacting||Date.now()<this.orphanSuppressedUntil)){if(e.type===f.Text&&e.content){this.orphanSessionId===null&&(this.orphanSessionId=this.currentAibotSessionId??"");const t=this.orphanQuotedStream.consume(e.content);t.deltaContent&&(this.orphanBuffer+=t.deltaContent),this.orphanBuffer.length>=Me?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()},$e),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){r.warn("acp-adapter",`orphan agent output dropped (${t?"no sendAgentMessage callback":"no session id"}) chars=${i.length}`);return}r.info("acp-adapter",`delivering orphan agent output session=${t} chars=${i.length}`);for(let s=0;s<i.length;s+=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"){r.error("acp-adapter",`finishRun failed: ${t} event=${i.eventId}`);const o=z.sanitizeFailureReasonForUser(t);i.buffer+=`
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+=`
19
19
 
20
- The task was interrupted (${o}). Please resend your instruction.`,n=!0}else r.info("acp-adapter",`finishRun ${e}: ${t} event=${i.eventId}`);i.buffer&&(this.emitSegmentedStream(i,i.buffer),i.buffer="");const a=++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(o=>{const l=`final output delivery failed: ${o instanceof Error?o.message:String(o)}`,d=e==="responded"?"failed":e,h=e==="responded"?l:t,u=e==="responded"?!1:n;r.warn("acp-adapter",`finalStreamChunk rejected/failed event=${i.eventId}: ${l}`),i.silent||this.callbacks.sendEventResult(i.eventId,d,h,void 0,u),this.persistEventResult(i,d,h),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}):(this.callbacks.sendStreamChunk(i.eventId,i.sessionId,"",a,!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=E(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=E(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<K||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("acp-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${K}%, scheduling compact`),this.tryRunPendingAutoCompact())}tryRunPendingAutoCompact(){this.pendingAutoCompact&&(this.stopped||!this.acpClient?.isAlive||this.activeRun||this.compacting||(this.pendingAutoCompact=!1,this.execCommand("compact","",this.currentAibotSessionId??"").then(e=>{r.info("acp-adapter",`[auto-compact] compact done status=${e.status} msg=${e.message??""}`)}).catch(e=>{r.warn("acp-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:String(e)}`)})))}persistEventResult(e,t,i){this.eventResults&&!e.silent&&this.eventResults.set({sessionId:e.sessionId,eventId:e.eventId,status:t,msg:i,updatedAt:Date.now()})}}class Oe extends ${adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){r.warn("acp-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{z 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;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};
@@ -1,4 +1,4 @@
1
- import{spawn as re,spawnSync as ae,execSync as oe,execFile as pt}from"node:child_process";import{promisify as mt}from"node:util";import{randomUUID as K}from"node:crypto";import{killProcessGroup as N,hasChildProcesses as vt,resolveCommandPath as gt,resolveWindowsExecInvocation as Ce}from"../../core/runtime/spawn.js";import{chmodSync as le,readdirSync as ce,readFileSync as B,realpathSync as Ae,rmSync as Et,statSync as k,truncateSync as _t,existsSync as P,openSync as St,writeSync as yt,closeSync as wt,renameSync as Ie,mkdirSync as be,constants as ke,watch as Pt}from"node:fs";import{appendFile as Tt,mkdir as O,readFile as I,rm as Re,stat as De,writeFile as R}from"node:fs/promises";import{dirname as $e,isAbsolute as Ct,join as f,resolve as At}from"node:path";import{homedir as D,tmpdir as It}from"node:os";import de from"node:net";import{EventEmitter as Oe}from"node:events";import{fileURLToPath as bt}from"node:url";let Y=null;if(process.platform==="win32")try{Y=await import("node-pty")}catch{Y=null}import{resolveRuntimePaths as S}from"../../core/config/index.js";import{normalizeClaudeEffort as kt,normalizeClaudeEnvEffort as Rt}from"../../core/config/claude-direct-env.js";import{SESSION_MODE_IDS as U}from"./protocol-contract.js";import{buildSessionContextBlock as Dt}from"../shared/session-context.js";import{log as o}from"../../core/log/index.js";import{ActivityStatusManager as $t}from"./activity-status-manager.js";import{HookSignalStore as Ot}from"../../core/hooks/hook-signal-store.js";import{QuestionStore as xe}from"../../core/persistence/question-store.js";import{PermissionStore as W}from"../../core/persistence/permission-store.js";import{InternalApiServer as xt}from"../../core/mcp/internal-api-server.js";import{executeEventTool as Le,isEventTool as Lt}from"../../core/mcp/event-tool-executor.js";import{validateToolArgs as Mt}from"../../core/mcp/tool-schemas.js";import{isGrixInternalToolName as Ft,normalizeEventToolArgs as Nt}from"../../core/mcp/tools.js";import{scanSkills as jt}from"./skill-scanner.js";import{syncDefaultSkillsToDir as Bt}from"../../default-skills/index.js";import{clearSessionJsonlDriftCache as Ut,countPendingBackgroundTasks as Ht,noteSessionJsonlPath as qt,extractLastAssistantEntry as z,extractTextBlocksAfterOffset as Me,hasTerminalToolResultAfterOffset as Fe,probeSessionTurnState as ue,resolveSessionJsonlPath as x}from"./usage-parser.js";import{readSettingsEnv as Gt}from"./model-list.js";import{resolveCliPath as Wt,getCliVersion as zt}from"../../core/util/cli-probe.js";import{GRIX_RELAY_ANTHROPIC_API_KEY as Ne}from"../../core/proxy/index.js";const j="grix",V={developmentChannels:!0,maxTurns:!0};function Jt(d){if(!Array.isArray(d))return;const e=d.map(t=>{if(typeof t=="string")return t.trim();if(!t||typeof t!="object")return"";const i=t,s=String(i.label??"").trim();if(s)return s;const n=String(i.value??"").trim();if(n)return n;const r=String(i.text??"").trim();return r||""}).filter(t=>t.length>0);return e.length>0?e:void 0}function je(d){let e;try{e=JSON.parse(d)}catch{return null}const t=e.questions;if(!Array.isArray(t)||t.length===0)return null;const i=[];for(let s=0;s<t.length;s++){const n=t[s];if(!n||typeof n!="object")continue;const r=n,a=`Question ${s+1}`,l=String(r.header??r.question??a).trim()||a,c=String(r.prompt??r.question??"").trim();if(!c)continue;const u=Jt(r.options),h=r.multiSelect===!0||r.multi_select===!0;i.push({header:l,prompt:c,...u?{options:u}:{},...h?{multi_select:!0}:{}})}return i.length>0?i:null}function Kt(d){if(!d||typeof d!="string")return null;try{const e=JSON.parse(d);return(typeof e.plan=="string"?e.plan.trim():"")||null}catch{return null}}const Yt=[/Please run \/login/,/API Error:\s*401/,/authentication_error/,/OAuth token has expired/],Vt=[/API Error:\s*401/,/authentication_error/],Qt=[/You're out of extra usage/i,/Stop and wait for limit to reset/i,/Add funds to continue with extra usage/i],Xt=/API Error:\s*400.*(server_tool_use\.id|messages\.\d+\.content)/;function he(d){const e=d?.trim();return e&&!e.startsWith("API Error:")?e:"Claude hit an API format error. Please start a new conversation (/grix open <dir>)."}const Be=300*1e3,Ue=300*1e3,He=Ue+30*1e3,qe=3e3,Zt=50,ei=1800*1e3,ti=["CLAUDE_CODE_ENABLE_TELEMETRY","CLAUDE_CODE_ENHANCED_TELEMETRY_BETA","OTEL_LOGS_EXPORTER","OTEL_TRACES_EXPORTER","OTEL_METRICS_EXPORTER","OTEL_EXPORTER_OTLP_PROTOCOL","OTEL_EXPORTER_OTLP_ENDPOINT","OTEL_EXPORTER_OTLP_HEADERS","OTEL_LOGS_EXPORT_INTERVAL","OTEL_TRACES_EXPORT_INTERVAL","OTEL_LOG_USER_PROMPTS","OTEL_LOG_ASSISTANT_RESPONSES","OTEL_LOG_TOOL_DETAILS","OTEL_LOG_TOOL_CONTENT","OTEL_LOG_RAW_API_BODIES"];function Q(d=process.env){const e={...d},t=new Set(ti.map(i=>i.toLowerCase()));for(const i of Object.keys(e))t.has(i.toLowerCase())&&delete e[i];return e}const Ge=60*1e3,We="lost injection respawn",ze=600*1e3,fe=10800*1e3,ii=5e3,si=250;function Je(d,e){const t=Number(process.env[d]);return Number.isFinite(t)&&t>0?t:e}function Ke(d){return d.trim().replace(/[^a-zA-Z0-9._-]+/gu,"_")||"unknown"}function ni(d){const e=gt("expect",d);return Ct(e)?e:null}function X(d){try{return P(d)?k(d).size:0}catch{return 0}}function Ye(d){const e=[],t=i=>{let s;try{s=ce(i,{withFileTypes:!0})}catch{return}for(const n of s){const r=f(i,n.name);n.isDirectory()?t(r):n.isFile()&&e.push(r)}};return t(d),e.sort((i,s)=>Ve(i)-Ve(s)||i.localeCompare(s))}function Ve(d){try{return k(d).mtimeMs}catch{return 0}}const Qe=Je("GRIX_RESCUE_SETTLE_POLL_MS",2e3),Xe=Je("GRIX_RESCUE_SETTLE_BUDGET_MS",12e4),ri=300*1e3,ai="The model's tool call could not be parsed (retry also failed).",oi="[\u7CFB\u7EDF\u81EA\u52A8\u91CD\u8BD5] \u4F60\u4E0A\u4E00\u6761\u56DE\u590D\u7684\u5DE5\u5177\u8C03\u7528\u89E3\u6790\u5931\u8D25\uFF0C\u6CA1\u6709\u53D1\u51FA\u53BB\u3002\u8BF7\u4E0D\u8981\u91CD\u590D\u6267\u884C\u5DF2\u7ECF\u5B8C\u6210\u7684\u64CD\u4F5C\uFF0C\u76F4\u63A5\u91CD\u65B0\u8C03\u7528 reply \u5DE5\u5177\uFF0C\u628A\u4F60\u4E0A\u4E00\u6761\u60F3\u53D1\u7ED9\u7528\u6237\u7684\u7ED3\u8BBA\u539F\u6837\u91CD\u65B0\u53D1\u9001\u4E00\u6B21\u3002",li="Sorry, the previous reply could not be generated properly (the model's tool call failed to parse). Please send it again, and use /grix restart to restart the session if needed.",ci=2*1e3,pe=90*1e3,Ze=1800*1e3,et=15*1e3,di=60*1e3,tt=10*1e3,ui=30*1e3,it=30*1e3,hi=2e3,fi=3e4;function pi(d){const e=d.tool_name||"";if(e==="mcp__grix__grix_dispatch_agent")return"\u540E\u53F0\u534F\u8C03\u5B50 agent";if(e.startsWith("mcp__grix__"))return"\u540E\u53F0\u8C03\u7528 Grix \u5DE5\u5177";if(e.startsWith("mcp__"))return"\u540E\u53F0\u8C03\u7528\u5DE5\u5177";switch(e){case"Bash":return"\u540E\u53F0\u6267\u884C\u547D\u4EE4";case"Read":case"Edit":case"Write":case"NotebookEdit":return"\u540E\u53F0\u8BFB\u5199\u6587\u4EF6";case"Task":return"\u540E\u53F0\u8FD0\u884C\u5B50\u4EFB\u52A1";default:return e?`\u540E\u53F0\u8FD0\u884C\uFF1A${e}`:"\u540E\u53F0\u4EFB\u52A1\u8FDB\u884C\u4E2D"}}function mi(d){try{return process.kill(d,0),!0}catch(e){return e.code==="EPERM"}}const vi=12e4,gi=3e4,Ei=6e4,_i=3e3,Z=1e3,st=45e3,Si=18e3,me=8192,yi=1e3,wi=2e3,nt=300,Pi=3e4,Ti=2e3,Ci=2e3,Ai=/(do you want to proceed\?[\s\S]*?\d\.\s*yes)|(only you can approve)/i,Ii=/(do you want to proceed)|(only you can approve)|(\d\.\s*yes)|(\d\.\s*no)|(esc to cancel)|(tab to amend)/i,bi=/^[✳✶✻✽✵❋✺✴❈❖✦✧✢◉◎⬥⬦◇◆▸▹►▻→←↑↓⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏·…\d]*still\s+thinking/i,ve="__grix_key__ ";let rt=!1;const ge=new Set;async function ki(d=[]){const e=new Set(d.filter(t=>Number.isInteger(t)&&t>0));for(let t=0;t<20;t++){const i=await new Promise((s,n)=>{const r=de.createServer();r.once("error",n),r.listen(0,"127.0.0.1",()=>{const a=r.address(),l=typeof a=="object"&&a?a.port:0;r.close(c=>{if(c){n(c);return}s(l)})})});if(i>0&&!e.has(i)&&!ge.has(i))return ge.add(i),i}throw new Error("\u65E0\u6CD5\u5206\u914D MCP \u901A\u77E5\u7AEF\u53E3")}function Ri(d){d>0&&ge.delete(d)}const Di=["You are connected to a chat via the grix-claude MCP server.",'Messages arrive as <channel source="grix-claude" chat_id="..." event_id="..." message_id="..." user_id="...">text</channel>.',"When present, the channel metadata includes context_messages_json \u2014 a JSON array of earlier visible messages for this trigger; each entry carries sender_id and content. An entry whose content starts with [\u5F15\u7528\u6D88\u606F] is the quoted (replied-to) message being responded to. Use these records as context (prefer them over inferring from quoted_message_id alone), in group chats use sender_id to tell who said what, and only answer the current message.","Local transcript availability, session binding, recovery mode, and connector history-loss diagnostics are internal operational details. Do not mention them, ask the user to choose a recovery mode, or say that history/context was lost unless the user explicitly asks to debug that mechanism.",'Both your plain text and the reply tool are delivered to the user. You are talking directly to the user, so address them in the second person ("you"); do not slip into third-person narration about them (e.g. "\u5DF2\u7ECF\u628A\u7ED3\u8BBA\u56DE\u590D\u7ED9\u8001\u90ED\u4E86") \u2014 say it straight to them ("\u7ED3\u8BBA\u662F\u2026\u2026").',"While you work, use plain text for short progress notes. Deliver your final conclusion \u2014 the answer the user is waiting for \u2014 through the reply tool; that is the message the user treats as your result and the one the connector quotes. Your plain text reaches the user too \u2014 it is not private scratch space, so it is a real channel for progress and for anything the user needs mid-turn; you just need not restate the full conclusion as plain text once it is in the reply. The connector automatically quotes the message you are answering, so you normally do not need to set reply_to (set it only to quote a different earlier message).","Make the reply tool call the last thing you do in a turn and put your whole conclusion inside it: once you have called reply, any further text you write in the same turn is dropped, not delivered. Progress notes go before the reply; the reply ends the turn.",'Never explain the delivery mechanics to the user or tell them you "already sent it via the reply tool" \u2014 that is internal plumbing they neither see nor care about. If the user says they did not receive or cannot read something, do not insist you already sent it; assume the delivery or the format failed and send it again, switching format when that is the likely cause (e.g. render a diagram to an image, strip code fences, upload a file).',"If you intentionally do not want to send any visible message, call the complete tool with event_id and a final status.",'Do not send a bare acknowledgement or "got it / on it / starting now" message before doing the work; do the work first, then deliver the actual result through the reply tool. During a long-running task, write a short plain-text progress note when you reach a meaningful milestone so the user can see progress as it happens.',"You only run while handling a turn; once your turn ends you are frozen and cannot send anything until the user messages you again.",'Unbreakable rule: never end a turn having promised something you cannot deliver. In particular, never start a task, detach it (nohup, a trailing &, any background process or watcher), end the turn, and promise to "notify when it finishes" \u2014 that notification can never happen, so do not promise it.',"For any task whose result the user is waiting on (deploy, build, rollout, tests), run it in the foreground and wait for it to finish within this same turn, then report the real outcome. You may launch several in the background at once for parallelism, but you must still collect all of their results within this same turn before you reply and finish.","Two cases where leaving a process running is correct: a long-lived service that is meant to keep running (dev server, daemon) \u2014 background it, then report that it started plus how to check or stop it; and a task too long to finish in one turn \u2014 you may leave it running, but state plainly that you cannot auto-notify and give the user a concrete way to retrieve the result later (a log path, a PID, or asking them to ping you to re-check)."];function $i(d,e,t){const i=[...Di];e&&i.unshift(Dt(e,t));try{const s=d?.agentName?.trim().slice(0,200),n=d?.introduction?.trim().slice(0,2e3);if(s||n){const r=[s?`Your name is "${s}".`:"",n?`Your self-introduction: ${n}`:""].filter(Boolean).join(" ");i.unshift(r)}}catch{}return i.join(" ")}function at(d){return String(d??"").trim().toLowerCase()===U.approval?U.approval:U.fullAuto}class As extends Oe{type="claude";config;bridgeCallbacks;mcpServerProcess=null;internalApi=null;claudeProcess=null;claudePty=null;singleTurnFallbackProcesses=new WeakSet;singleTurnFallbackPtys=new WeakSet;lostInjectionKilledProcesses=new WeakSet;lostInjectionKilledPtys=new WeakSet;singleTurnFallbackChild=null;singleTurnFallbackEventId=null;spawnPromise=null;lifecycleVersion=0;sessionId="";alive=!1;stopped=!1;activeEvent=null;completedAuditBoundaries=new Map;activeEventIdleTimer=null;activeEventHardTimer=null;activeEventPostReplyTimer=null;activeEventPostReplyWatcher=null;activeEventPostReplyPoll=null;rescueSettleTimer=null;stopHookBarrierSessionId=null;stopHookBarrierTimer=null;selfDrivenActive=!1;selfDrivenLastSignalAt=0;selfDrivenSweepTimer=null;selfDrivenReported=!1;selfDrivenReportTimer=null;selfDrivenLabel=null;selfDrivenTextOffset=null;selfDrivenDeliverTimer=null;selfDrivenReplied=!1;compacting=!1;compactingTimer=null;compactionDoneResolver=null;mcpServerReady=!1;mcpStartupFailureHandled=!1;mcpChannelBroken=!1;channelGateClosed=!1;startupChannelListening=!1;startupChannelListeningAt=0;startupDialogSeenAt=0;windowsTrustDialogAnswered=!1;windowsTrustDialogTimer=null;devChannelsConfirmTimer=null;ptyDialogBusy=!1;pendingMcpFailureTimer=null;sessionIdConflictDetected=!1;sessionIdConflictRetriedEventIds=new Set;malformedToolRetriedEventIds=new Set;lostInjectionRetriedEventIds=new Set;runtimeResolver=null;activityManager=null;deferredModelId=null;claudeCliCapabilities=null;claudeChildPid=0;claudeCliSessionId="";claudeSessionCwd="";claudeMcpConfigPath="";expectRunDir="";auditEvidencePaths=null;sessionState=null;authFailureUntil=0;usageLimitUntil=0;completedEventIds=new Map;pendingQuestion=new Map;pendingPermissions=new Map;lastClearedEvent=null;textForwardSeq=0;constructor(e,t){super(),this.config=e,this.bridgeCallbacks=t;const s=(e.options??{}).sessionRuntimeResolver;typeof s=="function"&&(this.runtimeResolver=s)}async start(){this.lifecycleVersion+=1,this.stopped=!1,this.alive=!0,this.internalApi=new xt,this.internalApi.setInvokeHandler(async(e,t,i,s)=>this.handleInternalInvoke(e,t,i,s)),this.internalApi.setStatusLineHandler(async e=>{this.handleStatusLineUpdate(e)}),await this.internalApi.start(0),this.internalApi.setMcpBridgeUpHandler(e=>this.onMcpBridgeUp(e)),this.internalApiPort=parseInt(new URL(this.internalApi.baseUrl).port,10),this.notifyPort=0,o.info("claude-adapter",`Adapter started (stdio MCP mode, internal API at ${this.internalApi.baseUrl})`)}onMcpBridgeUp(e){this.bridgeCallbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}async stop(){if(o.info("claude-adapter",`Stopping adapter (sessionId=${this.sessionId}, alive=${this.alive}, activeEvent=${this.activeEvent?.eventId??"none"})`),this.lifecycleVersion+=1,this.stopped=!0,this.alive=!1,this.singleTurnFallbackChild&&this.killSingleTurnFallbackChild(this.singleTurnFallbackChild),this.activeEvent&&(this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,"canceled","adapter stopped"),this.clearActiveEvent()),this.stopComposing(),this.claudeCliSessionId&&Ut(this.claudeCliSessionId),this.stopSelfDriven(),this.clearSelfDrivenDeliverTimer(),this.clearStopHookBarrier(),this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver("stopped"),this.compactionDoneResolver=null),this.mcpChannelBroken=!1,this.channelGateClosed=!1,this.startupChannelListening=!1,this.startupChannelListeningAt=0,this.startupDialogSeenAt=0,this.deferredModelId=null,this.clearPendingMcpFailureTimer(),this.clearWindowsTrustDialogTimer(),this.windowsTrustDialogAnswered=!1,this.clearDevChannelsConfirmTimer(),this.ptyDialogBusy=!1,this.terminalPermissionKeyTimer&&(clearTimeout(this.terminalPermissionKeyTimer),this.terminalPermissionKeyTimer=null),this.terminalPermissionPromptTail="",this.terminalPermissionPromptVisible=!1,this.pendingPermissions.size>0){const s=S(),n=new W(s.permissionRequestsDir);for(const[r]of this.pendingPermissions)n.resolveRequest(r,"deny").catch(()=>{});this.pendingPermissions.clear()}if(this.activityManager&&(this.activityManager.stop(),this.activityManager=null),this.sessionId){const s=this.resolveHookSignalsPath();Re(s).catch(()=>{})}this.stopMcpServer();const e=this.claudeChildPid;this.claudeChildPid=0;const t=this.claudeProcess,i=this.claudePty;if(this.claudeProcess=null,this.claudePty=null,this.spawnPromise=null,i)try{i.kill()}catch{}if(e>0)try{process.kill(e,"SIGTERM")}catch{}if(t?.pid&&(N(t,"SIGTERM"),!await Promise.race([new Promise(n=>{t.once("exit",()=>n(!0))}),new Promise(n=>{setTimeout(()=>n(!1),5e3)})]))){if(e>0)try{process.kill(e,"SIGKILL")}catch{}N(t,"SIGKILL")}if(e>0){const s=Date.now();for(;Date.now()-s<5e3;)try{process.kill(e,0),await new Promise(n=>setTimeout(n,100))}catch{break}try{process.kill(e,"SIGKILL")}catch{}}this.releaseNotifyPortReservation(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.alive}async createSession(e){return`claude-session-${Date.now()}`}async resumeSession(e,t){}async destroySession(e){}sendPrompt(e){const t=new Oi(e.adapterSessionId),i={event_id:e.adapterSessionId,session_id:e.adapterSessionId,content:e.text,context_messages_json:e.contextMessages?JSON.stringify(e.contextMessages):void 0};return this.deliverInboundEvent(i),this.once(`reply:${e.adapterSessionId}`,s=>{t.emitDone(s.status==="completed"?{status:"completed"}:{status:"failed",error:"failed"})}),t}async cancel(e){}setPermissionHandler(e){}async ping(e){if(this.stopped)return!1;if(!this.claudeProcess&&!this.claudePty)return this.alive&&this.internalApi!==null;if(!(this.alive&&this.mcpServerReady&&(this.startupChannelListening||process.platform==="win32")))return!1;const t=this.claudeProcess?.pid??this.claudePty?.pid;if(t&&!mi(t))return!1;const i=this.claudeProcess;if(i){const s=!!(i.stdin&&!i.stdin.destroyed),n=!!(i.stdout&&!i.stdout.destroyed);if(!s||!n)return!1}return!0}getStatus(){return{alive:this.alive,busy:this.activeEvent!==null||this.compacting,backgroundBusy:this.selfDrivenReported,sessions:0,details:this.sessionState?{sessionState:this.sessionState}:void 0}}getActiveEventIds(){return this.activeEvent?[this.activeEvent.eventId]:[]}takeAuditBoundary(e){const t=this.completedAuditBoundaries.get(e);return t&&this.completedAuditBoundaries.delete(e),t}clearActiveEventForShutdown(){this.activeEvent&&this.clearActiveEvent()}getSessionState(){return this.sessionState}getMcpConfig(){return null}async hasBackgroundWork(){const e=this.claudeProcess?.pid??this.claudePty?.pid;if(!e)return!1;const t=[this.claudeProcess?.pid,this.claudeChildPid||void 0,this.claudePty?.pid].filter(i=>!!i&&i>0);return vt(e,t)}async probe(e){const t=this.config.command||"claude",i=await Wt(t),s=i!==null;let n=null,r;if(s){const h=await zt(t);n=h.version,h.error&&(r=h.error)}else r={code:"cli_not_found",message:`command not found: ${t}`};const l=(Gt().ANTHROPIC_BASE_URL??process.env.ANTHROPIC_BASE_URL??"").trim()||null,c=(this.sessionState?.model?.id??(process.env.ANTHROPIC_MODEL??"").trim())||null;let u={attempted:!1,ok:!1,latency_ms:null};if(e?.conversation&&s){const h=Date.now();try{const p=mt(pt),m=["-p","ping","--output-format","json"];this.getClaudeCliCapabilities(t).maxTurns&&m.push("--max-turns","1");const v=Ce(t,m);await p(v.command,v.args,{timeout:e.timeoutMs??8e3,encoding:"utf-8",windowsVerbatimArguments:v.windowsVerbatimArguments,env:Q()}),u={attempted:!0,ok:!0,latency_ms:Date.now()-h}}catch(p){const m=p,v=m.killed?"conversation_timeout":"conversation_failed";u={attempted:!0,ok:!1,latency_ms:Date.now()-h,error:{code:v,message:m.message??String(p)}}}}return{cli:{command:t,installed:s,path:i,version:n,...r?{error:r}:{}},conversation:u,config:{model:c,base_url:l,source:{model:this.sessionState?.model?.id?"runtime":process.env.ANTHROPIC_MODEL?"env":"unknown",base_url:l?"env":"unknown"}},process:{started:!!(this.claudeProcess||this.claudePty),alive:this.alive,busy:this.activeEvent!==null},...this.probeSessionRecord()}}probeSessionRecord(){const e=this.claudeCliSessionId;if(!e||!this.claudeSessionCwd)return{session:{recordPath:null,lastActivityMs:null,freshMs:null}};const t=x(e,this.claudeSessionCwd);try{const i=k(t);return{session:{recordPath:t,lastActivityMs:i.mtimeMs,freshMs:Date.now()-i.mtimeMs}}}catch{return{session:{recordPath:t,lastActivityMs:null,freshMs:null}}}}getSupportedCommands(){return[{name:"compact",description:"\u538B\u7F29\u4E0A\u4E0B\u6587",args:"[instructions]"},{name:"clear",description:"\u6E05\u9664\u5BF9\u8BDD"},{name:"model",description:"\u5207\u6362\u6A21\u578B",args:"<model-id>"},{name:"cost",description:"\u663E\u793A\u8D39\u7528"},{name:"rewind",description:"\u56DE\u9000\u5BF9\u8BDD"},{name:"memory",description:"\u8BB0\u5FC6\u7BA1\u7406"},{name:"doctor",description:"\u8BCA\u65AD"},{name:"status",description:"\u72B6\u6001\u663E\u793A"},{name:"skills",description:"\u83B7\u53D6 skills \u6E05\u5355"}]}async execCommand(e,t,i){if(e==="skills")try{const n=jt({mode:"claude",projectDir:this.claudeSessionCwd}),r=n.map(a=>{const l=a.trigger?` (${a.trigger})`:"",c=a.pluginName?` [plugin:${a.pluginName}]`:` [${a.source}]`;return`- ${a.name}${l}${c}: ${a.description}`});return{status:"ok",message:r.length>0?r.join(`
1
+ import{spawn as re,spawnSync as ae,execSync as oe,execFile as pt}from"node:child_process";import{promisify as mt}from"node:util";import{randomUUID as K}from"node:crypto";import{killProcessGroup as N,hasChildProcesses as vt,resolveCommandPath as gt,resolveWindowsExecInvocation as Ce}from"../../core/runtime/spawn.js";import{chmodSync as le,readdirSync as ce,readFileSync as B,realpathSync as Ae,rmSync as Et,statSync as k,truncateSync as _t,existsSync as P,openSync as St,writeSync as yt,closeSync as wt,renameSync as Ie,mkdirSync as be,constants as ke,watch as Pt}from"node:fs";import{appendFile as Tt,mkdir as O,readFile as I,rm as Re,stat as De,writeFile as R}from"node:fs/promises";import{dirname as $e,isAbsolute as Ct,join as f,resolve as At}from"node:path";import{homedir as D,tmpdir as It}from"node:os";import de from"node:net";import{EventEmitter as Oe}from"node:events";import{fileURLToPath as bt}from"node:url";let Y=null;if(process.platform==="win32")try{Y=await import("node-pty")}catch{Y=null}import{resolveRuntimePaths as S}from"../../core/config/index.js";import{normalizeClaudeEffort as kt,normalizeClaudeEnvEffort as Rt}from"../../core/config/claude-direct-env.js";import{SESSION_MODE_IDS as U}from"./protocol-contract.js";import{buildSessionContextBlock as Dt}from"../shared/session-context.js";import{log as o}from"../../core/log/index.js";import{ActivityStatusManager as $t}from"./activity-status-manager.js";import{HookSignalStore as Ot}from"../../core/hooks/hook-signal-store.js";import{QuestionStore as xe}from"../../core/persistence/question-store.js";import{PermissionStore as W}from"../../core/persistence/permission-store.js";import{InternalApiServer as xt}from"../../core/mcp/internal-api-server.js";import{executeEventTool as Le,isEventTool as Lt}from"../../core/mcp/event-tool-executor.js";import{validateToolArgs as Mt}from"../../core/mcp/tool-schemas.js";import{isGrixInternalToolName as Ft,normalizeEventToolArgs as Nt}from"../../core/mcp/tools.js";import{scanSkills as jt}from"./skill-scanner.js";import{syncDefaultSkillsToDir as Bt}from"../../default-skills/index.js";import{clearSessionJsonlDriftCache as Ut,countPendingBackgroundTasks as Ht,noteSessionJsonlPath as qt,extractLastAssistantEntry as z,extractTextBlocksAfterOffset as Me,hasTerminalToolResultAfterOffset as Fe,probeSessionTurnState as ue,resolveSessionJsonlPath as x}from"./usage-parser.js";import{readSettingsEnv as Gt}from"./model-list.js";import{resolveCliPath as Wt,getCliVersion as zt}from"../../core/util/cli-probe.js";import{GRIX_RELAY_ANTHROPIC_API_KEY as Ne}from"../../core/proxy/index.js";const j="grix",V={developmentChannels:!0,maxTurns:!0};function Jt(d){if(!Array.isArray(d))return;const e=d.map(t=>{if(typeof t=="string")return t.trim();if(!t||typeof t!="object")return"";const i=t,s=String(i.label??"").trim();if(s)return s;const n=String(i.value??"").trim();if(n)return n;const r=String(i.text??"").trim();return r||""}).filter(t=>t.length>0);return e.length>0?e:void 0}function je(d){let e;try{e=JSON.parse(d)}catch{return null}const t=e.questions;if(!Array.isArray(t)||t.length===0)return null;const i=[];for(let s=0;s<t.length;s++){const n=t[s];if(!n||typeof n!="object")continue;const r=n,a=`Question ${s+1}`,l=String(r.header??r.question??a).trim()||a,c=String(r.prompt??r.question??"").trim();if(!c)continue;const u=Jt(r.options),h=r.multiSelect===!0||r.multi_select===!0;i.push({header:l,prompt:c,...u?{options:u}:{},...h?{multi_select:!0}:{},allow_free_text:!0})}return i.length>0?i:null}function Kt(d){if(!d||typeof d!="string")return null;try{const e=JSON.parse(d);return(typeof e.plan=="string"?e.plan.trim():"")||null}catch{return null}}const Yt=[/Please run \/login/,/API Error:\s*401/,/authentication_error/,/OAuth token has expired/],Vt=[/API Error:\s*401/,/authentication_error/],Qt=[/You're out of extra usage/i,/Stop and wait for limit to reset/i,/Add funds to continue with extra usage/i],Xt=/API Error:\s*400.*(server_tool_use\.id|messages\.\d+\.content)/;function he(d){const e=d?.trim();return e&&!e.startsWith("API Error:")?e:"Claude hit an API format error. Please start a new conversation (/grix open <dir>)."}const Be=300*1e3,Ue=300*1e3,He=Ue+30*1e3,qe=3e3,Zt=50,ei=1800*1e3,ti=["CLAUDE_CODE_ENABLE_TELEMETRY","CLAUDE_CODE_ENHANCED_TELEMETRY_BETA","OTEL_LOGS_EXPORTER","OTEL_TRACES_EXPORTER","OTEL_METRICS_EXPORTER","OTEL_EXPORTER_OTLP_PROTOCOL","OTEL_EXPORTER_OTLP_ENDPOINT","OTEL_EXPORTER_OTLP_HEADERS","OTEL_LOGS_EXPORT_INTERVAL","OTEL_TRACES_EXPORT_INTERVAL","OTEL_LOG_USER_PROMPTS","OTEL_LOG_ASSISTANT_RESPONSES","OTEL_LOG_TOOL_DETAILS","OTEL_LOG_TOOL_CONTENT","OTEL_LOG_RAW_API_BODIES"];function Q(d=process.env){const e={...d},t=new Set(ti.map(i=>i.toLowerCase()));for(const i of Object.keys(e))t.has(i.toLowerCase())&&delete e[i];return e}const Ge=60*1e3,We="lost injection respawn",ze=600*1e3,fe=10800*1e3,ii=5e3,si=250;function Je(d,e){const t=Number(process.env[d]);return Number.isFinite(t)&&t>0?t:e}function Ke(d){return d.trim().replace(/[^a-zA-Z0-9._-]+/gu,"_")||"unknown"}function ni(d){const e=gt("expect",d);return Ct(e)?e:null}function X(d){try{return P(d)?k(d).size:0}catch{return 0}}function Ye(d){const e=[],t=i=>{let s;try{s=ce(i,{withFileTypes:!0})}catch{return}for(const n of s){const r=f(i,n.name);n.isDirectory()?t(r):n.isFile()&&e.push(r)}};return t(d),e.sort((i,s)=>Ve(i)-Ve(s)||i.localeCompare(s))}function Ve(d){try{return k(d).mtimeMs}catch{return 0}}const Qe=Je("GRIX_RESCUE_SETTLE_POLL_MS",2e3),Xe=Je("GRIX_RESCUE_SETTLE_BUDGET_MS",12e4),ri=300*1e3,ai="The model's tool call could not be parsed (retry also failed).",oi="[\u7CFB\u7EDF\u81EA\u52A8\u91CD\u8BD5] \u4F60\u4E0A\u4E00\u6761\u56DE\u590D\u7684\u5DE5\u5177\u8C03\u7528\u89E3\u6790\u5931\u8D25\uFF0C\u6CA1\u6709\u53D1\u51FA\u53BB\u3002\u8BF7\u4E0D\u8981\u91CD\u590D\u6267\u884C\u5DF2\u7ECF\u5B8C\u6210\u7684\u64CD\u4F5C\uFF0C\u76F4\u63A5\u91CD\u65B0\u8C03\u7528 reply \u5DE5\u5177\uFF0C\u628A\u4F60\u4E0A\u4E00\u6761\u60F3\u53D1\u7ED9\u7528\u6237\u7684\u7ED3\u8BBA\u539F\u6837\u91CD\u65B0\u53D1\u9001\u4E00\u6B21\u3002",li="Sorry, the previous reply could not be generated properly (the model's tool call failed to parse). Please send it again, and use /grix restart to restart the session if needed.",ci=2*1e3,pe=90*1e3,Ze=1800*1e3,et=15*1e3,di=60*1e3,tt=10*1e3,ui=30*1e3,it=30*1e3,hi=2e3,fi=3e4;function pi(d){const e=d.tool_name||"";if(e==="mcp__grix__grix_dispatch_agent")return"\u540E\u53F0\u534F\u8C03\u5B50 agent";if(e.startsWith("mcp__grix__"))return"\u540E\u53F0\u8C03\u7528 Grix \u5DE5\u5177";if(e.startsWith("mcp__"))return"\u540E\u53F0\u8C03\u7528\u5DE5\u5177";switch(e){case"Bash":return"\u540E\u53F0\u6267\u884C\u547D\u4EE4";case"Read":case"Edit":case"Write":case"NotebookEdit":return"\u540E\u53F0\u8BFB\u5199\u6587\u4EF6";case"Task":return"\u540E\u53F0\u8FD0\u884C\u5B50\u4EFB\u52A1";default:return e?`\u540E\u53F0\u8FD0\u884C\uFF1A${e}`:"\u540E\u53F0\u4EFB\u52A1\u8FDB\u884C\u4E2D"}}function mi(d){try{return process.kill(d,0),!0}catch(e){return e.code==="EPERM"}}const vi=12e4,gi=3e4,Ei=6e4,_i=3e3,Z=1e3,st=45e3,Si=18e3,me=8192,yi=1e3,wi=2e3,nt=300,Pi=3e4,Ti=2e3,Ci=2e3,Ai=/(do you want to proceed\?[\s\S]*?\d\.\s*yes)|(only you can approve)/i,Ii=/(do you want to proceed)|(only you can approve)|(\d\.\s*yes)|(\d\.\s*no)|(esc to cancel)|(tab to amend)/i,bi=/^[✳✶✻✽✵❋✺✴❈❖✦✧✢◉◎⬥⬦◇◆▸▹►▻→←↑↓⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏·…\d]*still\s+thinking/i,ve="__grix_key__ ";let rt=!1;const ge=new Set;async function ki(d=[]){const e=new Set(d.filter(t=>Number.isInteger(t)&&t>0));for(let t=0;t<20;t++){const i=await new Promise((s,n)=>{const r=de.createServer();r.once("error",n),r.listen(0,"127.0.0.1",()=>{const a=r.address(),l=typeof a=="object"&&a?a.port:0;r.close(c=>{if(c){n(c);return}s(l)})})});if(i>0&&!e.has(i)&&!ge.has(i))return ge.add(i),i}throw new Error("\u65E0\u6CD5\u5206\u914D MCP \u901A\u77E5\u7AEF\u53E3")}function Ri(d){d>0&&ge.delete(d)}const Di=["You are connected to a chat via the grix-claude MCP server.",'Messages arrive as <channel source="grix-claude" chat_id="..." event_id="..." message_id="..." user_id="...">text</channel>.',"When present, the channel metadata includes context_messages_json \u2014 a JSON array of earlier visible messages for this trigger; each entry carries sender_id and content. An entry whose content starts with [\u5F15\u7528\u6D88\u606F] is the quoted (replied-to) message being responded to. Use these records as context (prefer them over inferring from quoted_message_id alone), in group chats use sender_id to tell who said what, and only answer the current message.","Local transcript availability, session binding, recovery mode, and connector history-loss diagnostics are internal operational details. Do not mention them, ask the user to choose a recovery mode, or say that history/context was lost unless the user explicitly asks to debug that mechanism.",'Both your plain text and the reply tool are delivered to the user. You are talking directly to the user, so address them in the second person ("you"); do not slip into third-person narration about them (e.g. "\u5DF2\u7ECF\u628A\u7ED3\u8BBA\u56DE\u590D\u7ED9\u8001\u90ED\u4E86") \u2014 say it straight to them ("\u7ED3\u8BBA\u662F\u2026\u2026").',"While you work, use plain text for short progress notes. Deliver your final conclusion \u2014 the answer the user is waiting for \u2014 through the reply tool; that is the message the user treats as your result and the one the connector quotes. Your plain text reaches the user too \u2014 it is not private scratch space, so it is a real channel for progress and for anything the user needs mid-turn; you just need not restate the full conclusion as plain text once it is in the reply. The connector automatically quotes the message you are answering, so you normally do not need to set reply_to (set it only to quote a different earlier message).","Make the reply tool call the last thing you do in a turn and put your whole conclusion inside it: once you have called reply, any further text you write in the same turn is dropped, not delivered. Progress notes go before the reply; the reply ends the turn.",'Never explain the delivery mechanics to the user or tell them you "already sent it via the reply tool" \u2014 that is internal plumbing they neither see nor care about. If the user says they did not receive or cannot read something, do not insist you already sent it; assume the delivery or the format failed and send it again, switching format when that is the likely cause (e.g. render a diagram to an image, strip code fences, upload a file).',"If you intentionally do not want to send any visible message, call the complete tool with event_id and a final status.",'Do not send a bare acknowledgement or "got it / on it / starting now" message before doing the work; do the work first, then deliver the actual result through the reply tool. During a long-running task, write a short plain-text progress note when you reach a meaningful milestone so the user can see progress as it happens.',"You only run while handling a turn; once your turn ends you are frozen and cannot send anything until the user messages you again.",'Unbreakable rule: never end a turn having promised something you cannot deliver. In particular, never start a task, detach it (nohup, a trailing &, any background process or watcher), end the turn, and promise to "notify when it finishes" \u2014 that notification can never happen, so do not promise it.',"For any task whose result the user is waiting on (deploy, build, rollout, tests), run it in the foreground and wait for it to finish within this same turn, then report the real outcome. You may launch several in the background at once for parallelism, but you must still collect all of their results within this same turn before you reply and finish.","Two cases where leaving a process running is correct: a long-lived service that is meant to keep running (dev server, daemon) \u2014 background it, then report that it started plus how to check or stop it; and a task too long to finish in one turn \u2014 you may leave it running, but state plainly that you cannot auto-notify and give the user a concrete way to retrieve the result later (a log path, a PID, or asking them to ping you to re-check)."];function $i(d,e,t){const i=[...Di];e&&i.unshift(Dt(e,t));try{const s=d?.agentName?.trim().slice(0,200),n=d?.introduction?.trim().slice(0,2e3);if(s||n){const r=[s?`Your name is "${s}".`:"",n?`Your self-introduction: ${n}`:""].filter(Boolean).join(" ");i.unshift(r)}}catch{}return i.join(" ")}function at(d){return String(d??"").trim().toLowerCase()===U.approval?U.approval:U.fullAuto}class As extends Oe{type="claude";config;bridgeCallbacks;mcpServerProcess=null;internalApi=null;claudeProcess=null;claudePty=null;singleTurnFallbackProcesses=new WeakSet;singleTurnFallbackPtys=new WeakSet;lostInjectionKilledProcesses=new WeakSet;lostInjectionKilledPtys=new WeakSet;singleTurnFallbackChild=null;singleTurnFallbackEventId=null;spawnPromise=null;lifecycleVersion=0;sessionId="";alive=!1;stopped=!1;activeEvent=null;completedAuditBoundaries=new Map;activeEventIdleTimer=null;activeEventHardTimer=null;activeEventPostReplyTimer=null;activeEventPostReplyWatcher=null;activeEventPostReplyPoll=null;rescueSettleTimer=null;stopHookBarrierSessionId=null;stopHookBarrierTimer=null;selfDrivenActive=!1;selfDrivenLastSignalAt=0;selfDrivenSweepTimer=null;selfDrivenReported=!1;selfDrivenReportTimer=null;selfDrivenLabel=null;selfDrivenTextOffset=null;selfDrivenDeliverTimer=null;selfDrivenReplied=!1;compacting=!1;compactingTimer=null;compactionDoneResolver=null;mcpServerReady=!1;mcpStartupFailureHandled=!1;mcpChannelBroken=!1;channelGateClosed=!1;startupChannelListening=!1;startupChannelListeningAt=0;startupDialogSeenAt=0;windowsTrustDialogAnswered=!1;windowsTrustDialogTimer=null;devChannelsConfirmTimer=null;ptyDialogBusy=!1;pendingMcpFailureTimer=null;sessionIdConflictDetected=!1;sessionIdConflictRetriedEventIds=new Set;malformedToolRetriedEventIds=new Set;lostInjectionRetriedEventIds=new Set;runtimeResolver=null;activityManager=null;deferredModelId=null;claudeCliCapabilities=null;claudeChildPid=0;claudeCliSessionId="";claudeSessionCwd="";claudeMcpConfigPath="";expectRunDir="";auditEvidencePaths=null;sessionState=null;authFailureUntil=0;usageLimitUntil=0;completedEventIds=new Map;pendingQuestion=new Map;pendingPermissions=new Map;lastClearedEvent=null;textForwardSeq=0;constructor(e,t){super(),this.config=e,this.bridgeCallbacks=t;const s=(e.options??{}).sessionRuntimeResolver;typeof s=="function"&&(this.runtimeResolver=s)}async start(){this.lifecycleVersion+=1,this.stopped=!1,this.alive=!0,this.internalApi=new xt,this.internalApi.setInvokeHandler(async(e,t,i,s)=>this.handleInternalInvoke(e,t,i,s)),this.internalApi.setStatusLineHandler(async e=>{this.handleStatusLineUpdate(e)}),await this.internalApi.start(0),this.internalApi.setMcpBridgeUpHandler(e=>this.onMcpBridgeUp(e)),this.internalApiPort=parseInt(new URL(this.internalApi.baseUrl).port,10),this.notifyPort=0,o.info("claude-adapter",`Adapter started (stdio MCP mode, internal API at ${this.internalApi.baseUrl})`)}onMcpBridgeUp(e){this.bridgeCallbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}async stop(){if(o.info("claude-adapter",`Stopping adapter (sessionId=${this.sessionId}, alive=${this.alive}, activeEvent=${this.activeEvent?.eventId??"none"})`),this.lifecycleVersion+=1,this.stopped=!0,this.alive=!1,this.singleTurnFallbackChild&&this.killSingleTurnFallbackChild(this.singleTurnFallbackChild),this.activeEvent&&(this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,"canceled","adapter stopped"),this.clearActiveEvent()),this.stopComposing(),this.claudeCliSessionId&&Ut(this.claudeCliSessionId),this.stopSelfDriven(),this.clearSelfDrivenDeliverTimer(),this.clearStopHookBarrier(),this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver("stopped"),this.compactionDoneResolver=null),this.mcpChannelBroken=!1,this.channelGateClosed=!1,this.startupChannelListening=!1,this.startupChannelListeningAt=0,this.startupDialogSeenAt=0,this.deferredModelId=null,this.clearPendingMcpFailureTimer(),this.clearWindowsTrustDialogTimer(),this.windowsTrustDialogAnswered=!1,this.clearDevChannelsConfirmTimer(),this.ptyDialogBusy=!1,this.terminalPermissionKeyTimer&&(clearTimeout(this.terminalPermissionKeyTimer),this.terminalPermissionKeyTimer=null),this.terminalPermissionPromptTail="",this.terminalPermissionPromptVisible=!1,this.pendingPermissions.size>0){const s=S(),n=new W(s.permissionRequestsDir);for(const[r]of this.pendingPermissions)n.resolveRequest(r,"deny").catch(()=>{});this.pendingPermissions.clear()}if(this.activityManager&&(this.activityManager.stop(),this.activityManager=null),this.sessionId){const s=this.resolveHookSignalsPath();Re(s).catch(()=>{})}this.stopMcpServer();const e=this.claudeChildPid;this.claudeChildPid=0;const t=this.claudeProcess,i=this.claudePty;if(this.claudeProcess=null,this.claudePty=null,this.spawnPromise=null,i)try{i.kill()}catch{}if(e>0)try{process.kill(e,"SIGTERM")}catch{}if(t?.pid&&(N(t,"SIGTERM"),!await Promise.race([new Promise(n=>{t.once("exit",()=>n(!0))}),new Promise(n=>{setTimeout(()=>n(!1),5e3)})]))){if(e>0)try{process.kill(e,"SIGKILL")}catch{}N(t,"SIGKILL")}if(e>0){const s=Date.now();for(;Date.now()-s<5e3;)try{process.kill(e,0),await new Promise(n=>setTimeout(n,100))}catch{break}try{process.kill(e,"SIGKILL")}catch{}}this.releaseNotifyPortReservation(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.alive}async createSession(e){return`claude-session-${Date.now()}`}async resumeSession(e,t){}async destroySession(e){}sendPrompt(e){const t=new Oi(e.adapterSessionId),i={event_id:e.adapterSessionId,session_id:e.adapterSessionId,content:e.text,context_messages_json:e.contextMessages?JSON.stringify(e.contextMessages):void 0};return this.deliverInboundEvent(i),this.once(`reply:${e.adapterSessionId}`,s=>{t.emitDone(s.status==="completed"?{status:"completed"}:{status:"failed",error:"failed"})}),t}async cancel(e){}setPermissionHandler(e){}async ping(e){if(this.stopped)return!1;if(!this.claudeProcess&&!this.claudePty)return this.alive&&this.internalApi!==null;if(!(this.alive&&this.mcpServerReady&&(this.startupChannelListening||process.platform==="win32")))return!1;const t=this.claudeProcess?.pid??this.claudePty?.pid;if(t&&!mi(t))return!1;const i=this.claudeProcess;if(i){const s=!!(i.stdin&&!i.stdin.destroyed),n=!!(i.stdout&&!i.stdout.destroyed);if(!s||!n)return!1}return!0}getStatus(){return{alive:this.alive,busy:this.activeEvent!==null||this.compacting,backgroundBusy:this.selfDrivenReported,sessions:0,details:this.sessionState?{sessionState:this.sessionState}:void 0}}getActiveEventIds(){return this.activeEvent?[this.activeEvent.eventId]:[]}takeAuditBoundary(e){const t=this.completedAuditBoundaries.get(e);return t&&this.completedAuditBoundaries.delete(e),t}clearActiveEventForShutdown(){this.activeEvent&&this.clearActiveEvent()}getSessionState(){return this.sessionState}getMcpConfig(){return null}async hasBackgroundWork(){const e=this.claudeProcess?.pid??this.claudePty?.pid;if(!e)return!1;const t=[this.claudeProcess?.pid,this.claudeChildPid||void 0,this.claudePty?.pid].filter(i=>!!i&&i>0);return vt(e,t)}async probe(e){const t=this.config.command||"claude",i=await Wt(t),s=i!==null;let n=null,r;if(s){const h=await zt(t);n=h.version,h.error&&(r=h.error)}else r={code:"cli_not_found",message:`command not found: ${t}`};const l=(Gt().ANTHROPIC_BASE_URL??process.env.ANTHROPIC_BASE_URL??"").trim()||null,c=(this.sessionState?.model?.id??(process.env.ANTHROPIC_MODEL??"").trim())||null;let u={attempted:!1,ok:!1,latency_ms:null};if(e?.conversation&&s){const h=Date.now();try{const p=mt(pt),m=["-p","ping","--output-format","json"];this.getClaudeCliCapabilities(t).maxTurns&&m.push("--max-turns","1");const v=Ce(t,m);await p(v.command,v.args,{timeout:e.timeoutMs??8e3,encoding:"utf-8",windowsVerbatimArguments:v.windowsVerbatimArguments,env:Q()}),u={attempted:!0,ok:!0,latency_ms:Date.now()-h}}catch(p){const m=p,v=m.killed?"conversation_timeout":"conversation_failed";u={attempted:!0,ok:!1,latency_ms:Date.now()-h,error:{code:v,message:m.message??String(p)}}}}return{cli:{command:t,installed:s,path:i,version:n,...r?{error:r}:{}},conversation:u,config:{model:c,base_url:l,source:{model:this.sessionState?.model?.id?"runtime":process.env.ANTHROPIC_MODEL?"env":"unknown",base_url:l?"env":"unknown"}},process:{started:!!(this.claudeProcess||this.claudePty),alive:this.alive,busy:this.activeEvent!==null},...this.probeSessionRecord()}}probeSessionRecord(){const e=this.claudeCliSessionId;if(!e||!this.claudeSessionCwd)return{session:{recordPath:null,lastActivityMs:null,freshMs:null}};const t=x(e,this.claudeSessionCwd);try{const i=k(t);return{session:{recordPath:t,lastActivityMs:i.mtimeMs,freshMs:Date.now()-i.mtimeMs}}}catch{return{session:{recordPath:t,lastActivityMs:null,freshMs:null}}}}getSupportedCommands(){return[{name:"compact",description:"\u538B\u7F29\u4E0A\u4E0B\u6587",args:"[instructions]"},{name:"clear",description:"\u6E05\u9664\u5BF9\u8BDD"},{name:"model",description:"\u5207\u6362\u6A21\u578B",args:"<model-id>"},{name:"cost",description:"\u663E\u793A\u8D39\u7528"},{name:"rewind",description:"\u56DE\u9000\u5BF9\u8BDD"},{name:"memory",description:"\u8BB0\u5FC6\u7BA1\u7406"},{name:"doctor",description:"\u8BCA\u65AD"},{name:"status",description:"\u72B6\u6001\u663E\u793A"},{name:"skills",description:"\u83B7\u53D6 skills \u6E05\u5355"}]}async execCommand(e,t,i){if(e==="skills")try{const n=jt({mode:"claude",projectDir:this.claudeSessionCwd}),r=n.map(a=>{const l=a.trigger?` (${a.trigger})`:"",c=a.pluginName?` [plugin:${a.pluginName}]`:` [${a.source}]`;return`- ${a.name}${l}${c}: ${a.description}`});return{status:"ok",message:r.length>0?r.join(`
2
2
  `):"No skills found",data:n}}catch(n){return{status:"failed",message:`Failed to scan skills: ${n instanceof Error?n.message:n}`}}if(!this.claudeProcess&&!this.claudePty)if(e==="compact"){try{await this.ensureClaudeProcessReady()}catch(n){return{status:"failed",message:`Failed to start Claude for compact: ${n instanceof Error?n.message:n}`}}if(await new Promise(n=>setTimeout(n,2e3)),!this.claudeProcess&&!this.claudePty)return{status:"failed",message:"Claude process exited during startup"};try{await this.waitForChannelReady()}catch(n){return{status:"failed",message:`Claude channel not ready for compact: ${n instanceof Error?n.message:n}`}}}else return{status:"failed",message:"Claude process is not running"};if(this.getStatus().busy)return{status:"failed",message:"Claude is busy, try again later"};const s=t?`/${e} ${t}`:`/${e}`;try{if(this.claudePty?this.claudePty.write(`${s}\r`):this.expectRunDir?this.writeExpectFifoLine(this.expectRunDir,s):this.claudeProcess?.stdin?.write(`${s}
3
3
  `),e==="compact"){const n=new Promise(a=>{this.compactionDoneResolver=a});this.beginCompaction();const r=await n;return r==="process-exit"?{status:"failed",message:"Claude exited during compaction"}:r==="stopped"?{status:"failed",message:"Adapter stopped during compaction"}:{status:"ok",message:"Compacted"}}return{status:"ok",message:`Sent: ${s}`}}catch(n){return{status:"failed",message:`Failed to send: ${n instanceof Error?n.message:n}`}}}writeExpectFifoLine(e,t){const i=f(e,"cmd.fifo");let s;try{s=St(i,ke.O_WRONLY|ke.O_NONBLOCK)}catch(n){const r=n.code;throw new Error(r==="ENXIO"?"expect process is not running \u2014 cannot inject command":`FIFO open failed: ${n instanceof Error?n.message:n}`)}try{yt(s,`${t}
4
4
  `)}finally{wt(s)}}handleStatusLineUpdate(e){try{const t=e.context_window,i=e.cost,s=e.model,n=e.rate_limits??e.rateLimits,r=t?.current_usage;if(this.sessionState={contextWindow:{usedPercentage:t?.used_percentage!=null?T(t.used_percentage):null,remainingPercentage:t?.remaining_percentage!=null?T(t.remaining_percentage):null,totalInputTokens:T(t?.total_input_tokens),totalOutputTokens:T(t?.total_output_tokens),contextWindowSize:T(t?.context_window_size)||2e5,currentUsage:r?{inputTokens:T(r.input_tokens),outputTokens:T(r.output_tokens),cacheCreationInputTokens:T(r.cache_creation_input_tokens),cacheReadInputTokens:T(r.cache_read_input_tokens)}:null},cost:{totalCostUsd:T(i?.total_cost_usd),totalDurationMs:T(i?.total_duration_ms),totalApiDurationMs:T(i?.total_api_duration_ms),totalLinesAdded:T(i?.total_lines_added),totalLinesRemoved:T(i?.total_lines_removed)},rateLimits:this.parseStatusRateLimits(n),model:{id:String(s?.id??""),displayName:String(s?.display_name??"")},fastMode:e.fast_mode===!0,effort:e.effort!=null?String(e.effort):void 0,thinkingEnabled:e.thinking_enabled===!0,exceeds200kTokens:e.exceeds_200k_tokens===!0,version:String(e.version??""),sampledAt:Date.now()},n){const a=this.sessionState.rateLimits?.fiveHour,l=this.sessionState.rateLimits?.sevenDay;o.info("claude-adapter",`[rate-limits] statusLine parsed: fiveHour=${a?`${a.usedPercentage}% resetsAt=${a.resetsAt}`:"n/a"} sevenDay=${l?`${l.usedPercentage}% resetsAt=${l.resetsAt}`:"n/a"}`)}else o.debug("claude-adapter","[rate-limits] statusLine: no rate_limits in payload");try{this.bridgeCallbacks.onStatusLineUpdated?.(this.sessionState)}catch(a){o.warn("claude-adapter",`onStatusLineUpdated callback error: ${a instanceof Error?a.message:a}`)}}catch(t){o.warn("claude-adapter",`Failed to parse statusLine payload: ${t instanceof Error?t.message:t}`)}}parseStatusRateLimits(e){if(!e)return;const t=n=>{if(!n||typeof n!="object")return;const r=n,a=T(r.used_percentage??r.usedPercent),l=T(r.resets_at??r.resetsAt);if(!(a<=0&&l<=0))return{usedPercentage:a,resetsAt:l}},i=t(e.five_hour??e.fiveHour),s=t(e.seven_day??e.sevenDay);if(!(!i&&!s))return{...i?{fiveHour:i}:{},...s?{sevenDay:s}:{}}}deliverInboundEvent(e){if(this.sessionId||(this.sessionId=e.session_id),this.pruneCompletedEvents(),this.completedEventIds.has(e.event_id)){o.info("claude-adapter",`Event ${e.event_id} rejected: duplicate`),this.bridgeCallbacks.sendEventResult(e.event_id,"responded","duplicate event");return}if(Date.now()<this.authFailureUntil){o.info("claude-adapter",`Event ${e.event_id} rejected: auth cooldown`),this.bridgeCallbacks.sendEventResult(e.event_id,"failed","Claude authentication failed \u2014 please re-login");return}if(Date.now()<this.usageLimitUntil){o.info("claude-adapter",`Event ${e.event_id} rejected: usage limit cooldown`),this.bridgeCallbacks.sendEventResult(e.event_id,"failed","Claude usage limit reached \u2014 waiting for reset");return}if(this.activeEvent?.eventId===e.event_id){o.info("claude-adapter",`Event ${e.event_id} ignored: same as active event`);return}if(this.activeEvent||this.compacting||this.stopHookBarrierSessionId===e.session_id){o.error("claude-adapter",`Scheduler invariant violated: event ${e.event_id} delivered while busy (active=${this.activeEvent?.eventId??"none"}, compacting=${this.compacting}, barrier=${this.stopHookBarrierSessionId===e.session_id}) \u2014 failing event to release slot`),this.bridgeCallbacks.sendEventResult(e.event_id,"failed","agent busy (scheduler invariant violated)");return}this.activeEvent={eventId:e.event_id,sessionId:e.session_id,rawEvent:e},this.stopSelfDriven(),this.clearSelfDrivenDeliverTimer(),this.lastClearedEvent=null,this.markActiveEventActivity(e.event_id,e.session_id),this.resetActiveEventHardTimer(e.event_id),this.emit("eventStarted",e.event_id,e.session_id),this.ensureClaudeAndPushEvent(e)}deliverStopEvent(e,t){if(this.activeEvent?.eventId!==e){o.info("claude-adapter",`Stop for non-active event=${e} (active=${this.activeEvent?.eventId??"none"}, compacting=${this.compacting}) \u2014 emitting eventDone to unblock platform`),this.emit("eventDone",e);return}const i=this.activeEvent.sessionId;o.info("claude-adapter",`Stop requested for active event=${e} \u2014 killing Claude process`),this.mcpServerReady&&this.pushNotification("notifications/event_stop",{event_id:e,session_id:i,stop_id:K()}),(this.claudeProcess||this.claudePty)&&this.emit("pauseIntake","restart"),this.bridgeCallbacks.sendEventResult(e,"canceled","stopped by user"),this.clearActiveEvent(),this.killClaudeProcess("stop")}async handleLocalAction(e){if(!this.mcpServerReady)return{handled:!1,kind:""};const t=String(e.action_type??"");if(t==="claude_interaction_reply"){const i=e.params??{};if(String(i.kind??"")==="permission"){const n=i.resolution??{},r=String(n.value??""),a={...e,action_type:r==="allow"?"exec_approve":"exec_reject",params:{...i,approval_command_id:i.request_id}};return this.handlePermissionApproval(a)}return this.handleQuestionReply(e)}return t==="exec_approve"||t==="exec_reject"?this.handlePermissionApproval(e):(this.pushNotification("notifications/local_action",e),{handled:!0,kind:""})}async handleQuestionReply(e){const t=e.params??{},i=String(t.request_id??"");if(!i)return o.warn("claude-adapter","Question reply missing request_id"),this.bridgeCallbacks.sendLocalActionResult(e.action_id,"failed",void 0,"request_id_required","request_id is required"),{handled:!0,kind:"question_reply_no_id"};const s=S(),n=new xe(s.questionRequestsDir),r=await n.getRequest(i),a=Number(r?.created_at??0),l=a>0&&Date.now()-a>He;if(!r||r.resolved||l){o.warn("claude-adapter",`Question reply has no pending waiter: request_id=${i} exists=${!!r} resolved=${r?.resolved===!0} expired=${l}`);const v=this.pendingQuestion.get(i);return v&&this.activeEvent?.eventId===v.eventId&&(this.activeEvent.awaitingUserQuestion=!1),this.pendingQuestion.delete(i),this.bridgeCallbacks.sendLocalActionResult(e.action_id,"failed",void 0,"interaction_request_not_pending","The question is no longer pending; the reply was not delivered."),{handled:!0,kind:"question_reply_not_pending"}}const c=t.resolution??{},u=String(c.type??""),h=c;if(u==="action"&&String(c.value??"")==="cancel"){o.info("claude-adapter",`Question cancelled by user: request_id=${i}`),await n.resolveRequest(i,"cancel",void 0,h);const E=this.pendingQuestion.get(i);return E&&this.activeEvent?.eventId===E.eventId&&(this.activeEvent.awaitingUserQuestion=!1),this.pendingQuestion.delete(i),this.bridgeCallbacks.sendLocalActionResult(e.action_id,"ok",{request_id:i,resolution:"cancel"}),{handled:!0,kind:"question_reply_cancel"}}let p="";if(u==="text"?p=String(c.value??""):u==="map"&&(p=(Array.isArray(c.entries)?c.entries:[]).map(E=>E.value).join(", ")),!p){o.warn("claude-adapter",`Empty answer for question reply request_id=${i}`),await n.resolveRequest(i,"cancel",void 0,h);const v=this.pendingQuestion.get(i);return v&&this.activeEvent?.eventId===v.eventId&&(this.activeEvent.awaitingUserQuestion=!1),this.pendingQuestion.delete(i),this.bridgeCallbacks.sendLocalActionResult(e.action_id,"ok",{request_id:i,resolution:"cancel"}),{handled:!0,kind:"question_reply_empty"}}o.info("claude-adapter",`Resolving question in store: request_id=${i} answer=${p.slice(0,80)}`),await n.resolveRequest(i,"answer",p,h);const m=this.pendingQuestion.get(i);return m&&this.activeEvent?.eventId===m.eventId&&(this.activeEvent.awaitingUserQuestion=!1),this.pendingQuestion.delete(i),this.bridgeCallbacks.sendLocalActionResult(e.action_id,"ok",{request_id:i,resolution:"answer"}),{handled:!0,kind:"question_reply"}}async handlePermissionApproval(e){const t=e.params??{},i=String(t.approval_id??t.approval_command_id??t.tool_call_id??""),s=String(e.action_type??"")==="exec_approve";if(!i)return this.bridgeCallbacks.sendLocalActionResult(e.action_id,"failed",void 0,"approval_id_required","approval_id is required"),{handled:!0,kind:"permission_approval"};if(!this.pendingPermissions.get(i))return this.bridgeCallbacks.sendLocalActionResult(e.action_id,"failed",void 0,"approval_not_found",`no pending permission for approval_id: ${i}`),{handled:!0,kind:"permission_approval"};const r=S(),a=new W(r.permissionRequestsDir),l=s?"allow":"deny";return await a.resolveRequest(i,l),this.pendingPermissions.delete(i),this.scheduleTerminalPermissionKey(i,l),this.bridgeCallbacks.sendLocalActionResult(e.action_id,"ok"),o.info("claude-adapter",`Permission ${l}: approvalId=${i}`),{handled:!0,kind:"permission_approval"}}async handleExecApprovalEvent(e){const t=String(e.approval_id??e.approval_command_id??e.tool_call_id??"");if(!t)return{delivered:!1,errorCode:"approval_id_required",errorMsg:"approval_id is required"};if(!this.pendingPermissions.get(t))return{delivered:!1,errorCode:"approval_not_found",errorMsg:`no pending permission for approval_id: ${t}`};const i=e.decision==="deny"?"deny":"allow",s=S();return await new W(s.permissionRequestsDir).resolveRequest(t,i),this.pendingPermissions.delete(t),this.scheduleTerminalPermissionKey(t,i),o.info("claude-adapter",`Permission ${i} (text event): approvalId=${t}`),{delivered:!0}}async ensureClaudeAndPushEvent(e,t=0){t===0&&!this.claudeProcess&&this.startComposing(e.session_id,"preparing");try{if(this.mcpChannelBroken){await this.runSingleTurnFallback(e,"mcp channel unavailable");return}if(await this.isChannelGateClosed()){await this.runSingleTurnFallback(e,"channel gate closed: tengu_harbor not enabled in cachedGrowthBookFeatures");return}if(!this.getClaudeCliCapabilities(this.config.command||"claude").developmentChannels){await this.runSingleTurnFallback(e,"claude CLI does not support development channels");return}if(await this.ensureClaudeProcessReady(),await this.waitForChannelReady(),this.activeEvent?.eventId!==e.event_id||this.mcpChannelBroken)return;const s={},n=[["chat_id",e.session_id],["event_id",e.event_id],["event_type","user_chat"],["message_id",e.msg_id],["sender_id",e.sender_id],["user",e.sender_id],["user_id",e.sender_id],["msg_id",e.msg_id],["session_type",e.session_type!=null?String(e.session_type):void 0],["msg_type",e.msg_type!=null?String(e.msg_type):void 0],["quoted_message_id",e.quoted_message_id],["ts",e.created_at!=null?new Date(Number(e.created_at)).toISOString():void 0],["owner_id",e.owner_id],["agent_id",this.bridgeCallbacks.getAgentId?.()],["attachments_json",e.attachments_json],["context_messages_json",ot(e.context_messages_json)??e.context_messages_json]];for(const[a,l]of n)l!=null&&l!==""&&(s[a]=l);this.captureEventJsonlBaseOffset(e.event_id,this.activeEvent?.textForwardOffset===void 0?{force:!0}:void 0);const r=e.content??"";this.pushNotification("notifications/claude/channel",{content:r,meta:s}),this.activeEvent?.eventId===e.event_id&&(this.activeEvent.channelPushedAt=Date.now()),o.info("claude-adapter",`Event ${e.event_id} pushed to MCP stdio server`)}catch(s){if(this.stopped){this.activeEvent?.eventId===e.event_id&&(this.bridgeCallbacks.sendEventResult(e.event_id,"canceled","adapter stopped"),this.clearActiveEvent());return}if(s instanceof Error&&(s.message.includes("exited before")||s.message.includes("timeout")||s.message.includes("spawn failed"))&&t<3)return o.warn("claude-adapter",`Spawn failed (attempt ${t+1}/4), retrying: ${s}`),this.claudeProcess=null,this.claudeChildPid=0,this.alive=!1,this.releaseNotifyPortReservation(),this.stopMcpServer(),Ee(this.claudeCliSessionId,[]),await new Promise(a=>setTimeout(a,(t+1)*2e3)),this.ensureClaudeAndPushEvent(e,t+1);o.error("claude-adapter",`Failed to deliver event ${e.event_id}: ${s}`);let r=s;if(this.shouldFallbackToSingleTurn(s)){o.warn("claude-adapter",`Channel path failed, switching to single-turn fallback: ${s instanceof Error?s.message:String(s)}`);try{await this.runSingleTurnFallback(e,s instanceof Error?s.message:String(s));return}catch(a){o.error("claude-adapter",`Single-turn fallback failed for ${e.event_id}: ${a instanceof Error?a.message:String(a)}`),r=a}}this.activeEvent?.eventId===e.event_id&&(this.bridgeCallbacks.sendEventResult(e.event_id,"failed",r instanceof Error?r.message:String(r)),this.clearActiveEvent())}}shouldFallbackToSingleTurn(e){const t=(e instanceof Error?e.message:String(e)).toLowerCase();return t?t.includes("mcp server startup failed")||t.includes("claude channel listener not ready")||t.includes("channel ready check timed out")||t.includes("channels are not currently available")||t.includes("does not support development channels")||t.includes("unknown option")||t.includes("unknown or unexpected option")||t.includes("mcp stdio server not available")||/notify port\b.*\bnot ready/.test(t)||t.includes("file not found:"):!1}getClaudeCliCapabilities(e){return this.claudeCliCapabilities?this.claudeCliCapabilities:(this.claudeCliCapabilities=Wi(e),this.claudeCliCapabilities)}buildSingleTurnPrompt(e,t=!0){const i=t&&e.context_messages_json?ot(e.context_messages_json)??String(e.context_messages_json):"",s=[e.content??""];return i&&(s.push("","Conversation context (JSON):"),s.push(i)),s.join(`
@@ -1,4 +1,4 @@
1
- import{execFileSync as Z}from"node:child_process";import{createInterface as q}from"node:readline";import{EventEmitter as L}from"node:events";import{randomUUID as ee}from"node:crypto";import{mkdirSync as E,readFileSync as P,writeFileSync as D,readdirSync as te,statSync as N}from"node:fs";import{join as A,resolve as se}from"node:path";import{fileURLToPath as ne}from"node:url";import{homedir as F}from"node:os";import{GRIX_PATHS as ie,log as u}from"../../core/log/index.js";import{IdentityInjector as re}from"../shared/identity-injector.js";import{buildSimpleProbeReport as oe}from"../shared/probe-util.js";import{InternalApiServer as ae}from"../../core/mcp/internal-api-server.js";import{resolveMcpServerName as de,pruneLegacyMcpServerEntries as le}from"../../core/mcp/server-name.js";import{InboundMessageIdInjector as ce}from"../shared/inbound-message-id-injector.js";import{insertBeforeTrailingMessageReferences as R}from"../../core/protocol/message-reference.js";import{syncDefaultSkillsToDir as ue}from"../../default-skills/index.js";import{spawnCommand as B,killProcessGroup as I}from"../../core/runtime/spawn.js";import{countLivingDescendantsOutsideBaseline as he,listLivingDescendants as me}from"./living-descendants.js";import{SessionBindingStore as fe}from"../../core/persistence/session-binding-store.js";import{fetchCursorParameterizedModels as pe}from"./fetch-models.js";import{resolveCursorSpawn as M}from"./resolve-command.js";import{fetchCursorAccountUsage as ge}from"./fetch-usage.js";import{buildCursorContextWindow as Se,buildCursorContextWindowFromUsage as ye,resolveCursorContextWindowSize as ve}from"./context-window.js";const _e=12e4,x=300*1e3,j=90*1e3,O=1800*1e3,Ae=8192,Q="Summarize this conversation for handoff to a fresh chat. Include: goals, key decisions, important file paths, current status, and next steps. Be concise but complete. Output ONLY the summary text.",W=12e4,U=new Set(["completed","failed","error","errored","canceled","cancelled","rejected"]);function be(d){const e=d.replace(/ToolCall$/i,"").trim();return e?e.charAt(0).toUpperCase()+e.slice(1):d||"tool"}function z(d){const e=String(d.call_id??d.callId??"").trim(),t=d.tool_call??d.toolCall;if(t&&typeof t=="object"&&!Array.isArray(t)){const n=Object.keys(t);if(n.length>0){const s=n[0],i=t[s],r=i&&typeof i=="object"&&!Array.isArray(i)?i:null;return{toolName:be(s),toolInput:r?.args??r?.arguments??{},callId:e}}}return{toolName:String(d.name??d.tool_name??d.toolName??"tool").trim()||"tool",toolInput:d.args??d.tool_input??d.toolInput??{},callId:e}}function H(d){return String(d??"").trim().toLowerCase().replace(/[^a-z0-9]/g,"")}function J(d){const e=H(d);return e==="askquestion"||e==="askuserquestion"}function G(d){const e=H(d);return e==="createplan"||e==="exitplanmode"}function we(d){if(!Array.isArray(d))return;const e=d.map(t=>{if(typeof t=="string")return t.trim();if(!t||typeof t!="object")return"";const n=t,s=String(n.label??"").trim();if(s)return s;const i=String(n.value??"").trim();if(i)return i;const r=String(n.text??"").trim();return r||""}).filter(t=>t.length>0);return e.length>0?e:void 0}function ke(d){if(!d||typeof d!="object"||Array.isArray(d))return null;const e=d,t=String(e.title??"").trim(),n=e.questions;if(!Array.isArray(n)||n.length===0)return null;const s=[];for(let i=0;i<n.length;i++){const r=n[i];if(!r||typeof r!="object")continue;const o=r,a=t||`Question ${i+1}`,l=String(o.header??o.id??o.question??a).trim()||a,c=String(o.prompt??o.question??"").trim();if(!c)continue;const h=we(o.options),p=o.multiSelect===!0||o.multi_select===!0;s.push({header:l,prompt:c,...h?{options:h}:{},...p?{multi_select:!0}:{}})}return s.length>0?s:null}function Ie(d){if(!d||typeof d!="object"||Array.isArray(d))return null;const e=d,t=String(e.name??e.title??"").trim(),n=String(e.overview??"").trim(),s=String(e.plan??"").trim();if(!s&&!n&&!t)return null;const i=[];return t&&i.push(`# ${t}`),n&&i.push(n),s&&i.push(s),i.join(`
1
+ import{execFileSync as Z}from"node:child_process";import{createInterface as q}from"node:readline";import{EventEmitter as L}from"node:events";import{randomUUID as ee}from"node:crypto";import{mkdirSync as E,readFileSync as P,writeFileSync as D,readdirSync as te,statSync as N}from"node:fs";import{join as A,resolve as se}from"node:path";import{fileURLToPath as ne}from"node:url";import{homedir as F}from"node:os";import{GRIX_PATHS as ie,log as u}from"../../core/log/index.js";import{IdentityInjector as re}from"../shared/identity-injector.js";import{buildSimpleProbeReport as oe}from"../shared/probe-util.js";import{InternalApiServer as ae}from"../../core/mcp/internal-api-server.js";import{resolveMcpServerName as de,pruneLegacyMcpServerEntries as le}from"../../core/mcp/server-name.js";import{InboundMessageIdInjector as ce}from"../shared/inbound-message-id-injector.js";import{insertBeforeTrailingMessageReferences as R}from"../../core/protocol/message-reference.js";import{syncDefaultSkillsToDir as ue}from"../../default-skills/index.js";import{spawnCommand as B,killProcessGroup as I}from"../../core/runtime/spawn.js";import{countLivingDescendantsOutsideBaseline as he,listLivingDescendants as me}from"./living-descendants.js";import{SessionBindingStore as fe}from"../../core/persistence/session-binding-store.js";import{fetchCursorParameterizedModels as pe}from"./fetch-models.js";import{resolveCursorSpawn as M}from"./resolve-command.js";import{fetchCursorAccountUsage as ge}from"./fetch-usage.js";import{buildCursorContextWindow as Se,buildCursorContextWindowFromUsage as ye,resolveCursorContextWindowSize as ve}from"./context-window.js";const _e=12e4,x=300*1e3,j=90*1e3,O=1800*1e3,Ae=8192,Q="Summarize this conversation for handoff to a fresh chat. Include: goals, key decisions, important file paths, current status, and next steps. Be concise but complete. Output ONLY the summary text.",W=12e4,U=new Set(["completed","failed","error","errored","canceled","cancelled","rejected"]);function be(d){const e=d.replace(/ToolCall$/i,"").trim();return e?e.charAt(0).toUpperCase()+e.slice(1):d||"tool"}function z(d){const e=String(d.call_id??d.callId??"").trim(),t=d.tool_call??d.toolCall;if(t&&typeof t=="object"&&!Array.isArray(t)){const n=Object.keys(t);if(n.length>0){const s=n[0],i=t[s],r=i&&typeof i=="object"&&!Array.isArray(i)?i:null;return{toolName:be(s),toolInput:r?.args??r?.arguments??{},callId:e}}}return{toolName:String(d.name??d.tool_name??d.toolName??"tool").trim()||"tool",toolInput:d.args??d.tool_input??d.toolInput??{},callId:e}}function H(d){return String(d??"").trim().toLowerCase().replace(/[^a-z0-9]/g,"")}function J(d){const e=H(d);return e==="askquestion"||e==="askuserquestion"}function G(d){const e=H(d);return e==="createplan"||e==="exitplanmode"}function we(d){if(!Array.isArray(d))return;const e=d.map(t=>{if(typeof t=="string")return t.trim();if(!t||typeof t!="object")return"";const n=t,s=String(n.label??"").trim();if(s)return s;const i=String(n.value??"").trim();if(i)return i;const r=String(n.text??"").trim();return r||""}).filter(t=>t.length>0);return e.length>0?e:void 0}function ke(d){if(!d||typeof d!="object"||Array.isArray(d))return null;const e=d,t=String(e.title??"").trim(),n=e.questions;if(!Array.isArray(n)||n.length===0)return null;const s=[];for(let i=0;i<n.length;i++){const r=n[i];if(!r||typeof r!="object")continue;const o=r,a=t||`Question ${i+1}`,l=String(o.header??o.id??o.question??a).trim()||a,c=String(o.prompt??o.question??"").trim();if(!c)continue;const h=we(o.options),p=o.multiSelect===!0||o.multi_select===!0;s.push({header:l,prompt:c,...h?{options:h}:{},...p?{multi_select:!0}:{},allow_free_text:!0})}return s.length>0?s:null}function Ie(d){if(!d||typeof d!="object"||Array.isArray(d))return null;const e=d,t=String(e.name??e.title??"").trim(),n=String(e.overview??"").trim(),s=String(e.plan??"").trim();if(!s&&!n&&!t)return null;const i=[];return t&&i.push(`# ${t}`),n&&i.push(n),s&&i.push(s),i.join(`
2
2
 
3
3
  `)}function Ce(d){if(!d||typeof d!="object"||Array.isArray(d))return null;const e=d,t=String(e.type??"").trim().toLowerCase();if(!t)return null;switch(t){case"tool_call":{const n=String(e.subtype??"").trim().toLowerCase(),s=z(e);return J(s.toolName)||G(s.toolName)?null:n==="started"||n==="start"?{type:"tool_call",payload:{tool_name:s.toolName,...s.callId?{tool_call_id:s.callId}:{}}}:(U.has(n),null)}case"error":return{type:"error",payload:{message:String(e.message??e.error??e.result??"cursor error").trim()||"cursor error"}};case"result":{const n=String(e.subtype??"").trim().toLowerCase();return e.is_error===!0||n==="error"?{type:"error",payload:{message:String(e.result??e.message??"cursor run failed").trim()||"cursor run failed"}}:null}default:return null}}function Re(d,e){if(!d||typeof d!="object"||Array.isArray(d))return"";const t=d;if(String(t.type??"")!=="assistant")return"";const n=t.model_call_id??t.modelCallId;if(n!=null&&String(n).trim()!=="")return"";const s=t.message,i=s&&typeof s=="object"&&!Array.isArray(s)?s.content:void 0;if(!Array.isArray(i))return"";const r=i.map(o=>{if(!o||typeof o!="object"||Array.isArray(o))return"";const a=o;return a.type==="text"?String(a.text??""):""}).filter(Boolean).join("");if(!r)return"";if(e){if(r===e||e.startsWith(r)&&r.length<e.length)return"";if(r.startsWith(e))return r.slice(e.length)}return r}const X=[{id:"approval",displayName:"\u4EBA\u5DE5\u786E\u8BA4"},{id:"full_auto",displayName:"\u81EA\u7531\u6A21\u5F0F"},{id:"plan",displayName:"\u8BA1\u5212\u6A21\u5F0F"}];function C(d){const e=String(d??"").trim().toLowerCase();return X.some(t=>t.id===e)?e:null}function T(d){try{return process.kill(d,0),!0}catch(e){return e.code==="EPERM"}}function K(d,e,t){if(t<0)return null;let n;try{n=te(d,{withFileTypes:!0})}catch{return null}for(const s of n){const i=A(d,s.name);if(s.isDirectory()){const r=K(i,e,t-1);if(r)return r}else if(s.isFile()&&s.name.endsWith(e))return i}return null}function V(d,e){if(!d)return null;const t=e||A(F(),".cursor","projects");return K(t,`${d}.jsonl`,6)}function Be(d){const e=d.context_messages_json;if(e)try{const t=JSON.parse(e);return!Array.isArray(t)||t.length===0?void 0:t.map(n=>({senderId:String(n.sender_id??"unknown"),content:String(n.content??"")}))}catch{return}}function Me(d,e){const t=[String(d??"")],n=String(e.quoted_message_id??"").trim();n&&t.push(`[quoted_message_id] ${n}`);const s=String(e.attachments_json??"").trim();return s&&t.push(`[attachments_json]
4
4
  ${s}`),t.filter(i=>i.length>0).join(`