grix-connector 4.3.6 → 4.4.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/acp/acp-adapter.js +7 -7
- package/dist/adapter/claude/claude-adapter.js +1 -1
- package/dist/adapter/codex/codex-bridge.js +1 -1
- package/dist/assets/dsh-bridge/grix-dsh-bridge-4.4.0-beta.1.tgz +0 -0
- package/dist/assets/dsh-bridge/manifest.json +6 -6
- package/dist/bridge/bridge.js +4 -4
- package/dist/core/upgrade/npm-upgrader.js +2 -2
- package/dist/core/upgrade/upgrade-checker.js +1 -1
- package/dist/core/upgrade/version-store.js +2 -0
- package/dist/grix.js +7 -7
- package/dist/launcher.mjs +188 -0
- package/dist/service/service-manager.js +3 -3
- package/dist/service/windows-wrapper-migration.js +1 -1
- package/openclaw-plugin/index.js +1 -1
- package/package.json +3 -2
- package/scripts/launcher.mjs +188 -0
- package/dist/adapter/claude/claude-bridge-server.js +0 -1
- package/dist/adapter/claude/claude-tools.js +0 -1
- package/dist/adapter/claude/claude-worker-client.js +0 -1
- package/dist/adapter/claude/mcp-http-launcher.js +0 -2
- package/dist/adapter/claude/result-timeout.js +0 -1
- package/dist/adapter/deepseek/deepseek-adapter.js +0 -6
- package/dist/adapter/deepseek/index.js +0 -1
- package/dist/adapter/qwen/index.js +0 -1
- package/dist/adapter/qwen/qwen-adapter.js +0 -4
- package/dist/aibot/client.js +0 -1
- package/dist/aibot/index.js +0 -1
- package/dist/aibot/types.js +0 -0
- package/dist/assets/dsh-bridge/grix-dsh-bridge-4.3.6.tgz +0 -0
- package/dist/core/access/allowlist-gate.js +0 -1
- package/dist/core/access/allowlist-store.js +0 -1
- package/dist/core/access/index.js +0 -1
- package/dist/core/file-ops/handler.js +0 -1
- package/dist/core/file-ops/list-files.js +0 -1
- package/dist/core/file-ops/types.js +0 -0
- package/dist/log.js +0 -3
- package/dist/main.js +0 -31
- package/dist/mcp/stream-http/config.js +0 -1
- package/dist/mcp/stream-http/connection-binding.js +0 -1
- package/dist/mcp/stream-http/event-tool-executor.js +0 -1
- package/dist/mcp/stream-http/gateway.js +0 -1
- package/dist/mcp/stream-http/index.js +0 -1
- package/dist/mcp/stream-http/security.js +0 -1
- package/dist/mcp/stream-http/session-manager.js +0 -1
- package/dist/mcp/stream-http/tool-executor.js +0 -1
- package/dist/mcp/stream-http/tool-registry.js +0 -1
- package/dist/mcp/stream-http/tool-schemas.js +0 -1
- package/dist/session/index.js +0 -1
- package/dist/session/manager.js +0 -1
- package/dist/transport/index.js +0 -1
- package/dist/transport/json-rpc.js +0 -3
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import{fileURLToPath as H}from"node:url";import p from"node:path";import{homedir as v}from"node:os";import{promises as g,statSync as P}from"node:fs";import{EventEmitter as
|
|
2
|
-
`)}function de(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,q(s.content),s.content,s.description,i.tool_input,i.toolInput,e];for(const o of n){const a=M(o);if(a&&a!==t)return a}return""}function ue(c){if(!`${c.toolName} ${c.toolTitle}`.toLowerCase().replace(/[^a-z]/g,"").includes("askuserquestion"))return null;const t=c.options.filter(o=>/^q\d+_opt_\d+$/.test(o.optionId));if(t.length===0)return null;const i=c.rawParams&&typeof c.rawParams=="object"?c.rawParams:{},s=i.toolCall&&typeof i.toolCall=="object"?i.toolCall:{},n=q(s.content).trim();return n?{prompt:n,answerOptions:t}:null}import{scanSkills as O}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as he,checkKimiSessionActivity as pe,hasRunningKimiDetachedTask as me}from"./session-activity.js";import{resolveCliPath as fe,getCliVersion as ge}from"../../core/util/cli-probe.js";const w=p.dirname(H(import.meta.url)),D=[{modelId:"deepseek-v4-pro",name:"DeepSeek V4 Pro"},{modelId:"deepseek-v4-flash",name:"DeepSeek V4 Flash"}],R=[{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 k(c,e){const t=Number(process.env[c]);return Number.isFinite(t)&&t>0?t:e}const ve=200,Ie=60*1e3,ye=600*1e3,T=k("GRIX_ACP_TURN_INACTIVITY_TIMEOUT_MS",900*1e3),B=2,F=5e3,U=80,j=1200*1e3,Ce=k("GRIX_ACP_BUSY_RETRY_INTERVAL_MS",5e3),L=k("GRIX_ACP_BUSY_WAIT_MAX_MS",1800*1e3),Ae=6e4,Se=2e3,Te=12e3,K=3e3,W=3e4,be=["[grix protocol] Markers like [[message_id:<id>]] and [[quoted_message_id:<id>]] are metadata, not user prose \u2014 never repeat them verbatim except as instructed here.","The message you are replying to carries its id in a [[message_id:<id>]] marker.","If (and only if) you want your reply to quote/thread onto that specific message, include [[quoted_message_id:<id>]] anywhere in your reply using that id; otherwise omit it."].join(" ");function _e(c){return!(!(c instanceof Error)||c.code!==-32603||se(c))}function we(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 I(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 Re(c){return c.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class it 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;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;identityInjectedSessions=new Set;orphanBuffer="";orphanQuotedStream=new C;orphanFlushTimer=null;orphanSessionId=null;orphanSuppressedUntil=0;constructor(e,t,i){super(),this.config=e,this.callbacks=t,this.bridgeLog=i?.bridgeLog??null,this.acpAuthMethod=i?.acpAuthMethod,this.acpInitialMode=i?.acpInitialMode,this.acpInitialModel=i?.acpInitialModel,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 oe(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.identityInjectedSessions.delete(e)}onAgentProfileChanged(){this.identityInjectedSessions.size>0&&(r.info("acp-adapter",`agent profile changed; clearing ${this.identityInjectedSessions.size} identity-injected session(s)`),this.identityInjectedSessions.clear())}sendPrompt(e){const t=new ke(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(W))}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(w,"../../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?Q(e,[e,...this.baselineGroupPids]):!1}captureBaselineGroupPids(){const e=this.agentProcess?.pid;e&&z(e).then(t=>{this.baselineGroupPids=t}).catch(()=>{})}async probe(e){const t=this.config.command||"gemini",i=await fe(t),s=i!==null;let n=null,o;if(s){const h=await ge(t);n=h.version,h.error&&(o=h.error)}else o={code:"cli_not_found",message:`command not found: ${t}`};const a=this.acpClient?.model??null,l=this.getStatus();let d;return e?.conversation?this.acpClient?.isAlive?d={attempted:!0,ok:!0,latency_ms:null}:d={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:d={attempted:!1,ok:!1,latency_ms:null},{cli:{command:t,installed:s,path:i,version:n,...o?{error:o}:{}},conversation:d,config:{model:a,base_url:null,source:{model:a?"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=O({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(`
|
|
1
|
+
import{fileURLToPath as H}from"node:url";import p from"node:path";import{homedir as v}from"node:os";import{promises as g,statSync as P}from"node:fs";import{EventEmitter as E}from"node:events";import{spawn as $}from"node:child_process";import{AgentProcess as G}from"../../agent/process.js";import{hasChildProcesses as Q,listGroupPids as z}from"../../core/runtime/spawn.js";import{applyNativeProviderConfig as Y}from"../shared/native-provider-config.js";import{buildSessionContextBlock as V}from"../shared/session-context.js";import{compactToolInputForWire as M}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as X,resolveKimiCodeHome as J}from"../../default-skills/index.js";import{resolveHermesHomeDir as Z}from"../shared/native-provider-config.js";import{prepareKimiLayerPlugin as ee}from"../../kimi-plugins/index.js";import{AcpClient as te,AcpAuthRequiredError as ie,isAuthRequiredError as se}from"../../protocol/acp-client.js";import{AgentEventType as f}from"../../types/events.js";import{InternalApiServer as ne}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as oe}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as C}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as re}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as ae}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as le}from"../../core/protocol/message-metadata.js";import{insertBeforeTrailingMessageReferences as A}from"../../core/protocol/message-reference.js";import{log as r}from"../../core/log/index.js";function x(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 ce=5e3;function N(c,e){const t=e.toolName??"";return e.toolCallId&&(!t||t===e.toolCallId)?c.toolNamesById.get(e.toolCallId)??t:t}function S(c){const e=String(c??"").trim();if(!e)return"";try{const t=JSON.parse(e);return JSON.stringify(_(t))}catch{return e}}function _(c){if(c===null||typeof c!="object")return c;if(Array.isArray(c))return c.map(_);const e=c,t=Object.keys(e).sort(),i={};for(const s of t)i[s]=_(e[s]);return i}function q(c){const e=Array.isArray(c)?c:[c],t=[];for(const i of e){if(!i||typeof i!="object")continue;const s=i;if(typeof s.text=="string"&&s.text.trim()){t.push(s.text.trim());continue}const n=s.content;if(n&&typeof n=="object"&&!Array.isArray(n)){const o=n.text;typeof o=="string"&&o.trim()&&t.push(o.trim())}}return t.join(`
|
|
2
|
+
`)}function de(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,q(s.content),s.content,s.description,i.tool_input,i.toolInput,e];for(const o of n){const a=M(o);if(a&&a!==t)return a}return""}function ue(c){if(!`${c.toolName} ${c.toolTitle}`.toLowerCase().replace(/[^a-z]/g,"").includes("askuserquestion"))return null;const t=c.options.filter(o=>/^q\d+_opt_\d+$/.test(o.optionId));if(t.length===0)return null;const i=c.rawParams&&typeof c.rawParams=="object"?c.rawParams:{},s=i.toolCall&&typeof i.toolCall=="object"?i.toolCall:{},n=q(s.content).trim();return n?{prompt:n,answerOptions:t}:null}import{scanSkills as O}from"../claude/skill-scanner.js";import{checkKiroSessionActivity as he,checkKimiSessionActivity as pe,hasRunningKimiDetachedTask as me}from"./session-activity.js";import{resolveCliPath as fe,getCliVersion as ge}from"../../core/util/cli-probe.js";const w=p.dirname(H(import.meta.url)),D=[{modelId:"deepseek-v4-pro",name:"DeepSeek V4 Pro"},{modelId:"deepseek-v4-flash",name:"DeepSeek V4 Flash"}],R=[{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 k(c,e){const t=Number(process.env[c]);return Number.isFinite(t)&&t>0?t:e}const ve=200,Ie=60*1e3,ye=600*1e3,T=k("GRIX_ACP_TURN_INACTIVITY_TIMEOUT_MS",900*1e3),B=2,F=5e3,U=80,j=1200*1e3,Ce=k("GRIX_ACP_BUSY_RETRY_INTERVAL_MS",5e3),L=k("GRIX_ACP_BUSY_WAIT_MAX_MS",1800*1e3),Ae=6e4,Se=2e3,Te=12e3,K=3e3,W=3e4,be=["[grix protocol] Markers like [[message_id:<id>]] and [[quoted_message_id:<id>]] are metadata, not user prose \u2014 never repeat them verbatim except as instructed here.","The message you are replying to carries its id in a [[message_id:<id>]] marker.","If (and only if) you want your reply to quote/thread onto that specific message, include [[quoted_message_id:<id>]] anywhere in your reply using that id; otherwise omit it."].join(" ");function _e(c){return!(!(c instanceof Error)||c.code!==-32603||se(c))}function we(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 I(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 Re(c){return c.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class it extends E{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;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;identityInjectedSessions=new Set;orphanBuffer="";orphanQuotedStream=new C;orphanFlushTimer=null;orphanSessionId=null;orphanSuppressedUntil=0;constructor(e,t,i){super(),this.config=e,this.callbacks=t,this.bridgeLog=i?.bridgeLog??null,this.acpAuthMethod=i?.acpAuthMethod,this.acpInitialMode=i?.acpInitialMode,this.acpInitialModel=i?.acpInitialModel,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 oe(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.identityInjectedSessions.delete(e)}onAgentProfileChanged(){this.identityInjectedSessions.size>0&&(r.info("acp-adapter",`agent profile changed; clearing ${this.identityInjectedSessions.size} identity-injected session(s)`),this.identityInjectedSessions.clear())}sendPrompt(e){const t=new ke(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(W))}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(w,"../../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?Q(e,[e,...this.baselineGroupPids]):!1}captureBaselineGroupPids(){const e=this.agentProcess?.pid;e&&z(e).then(t=>{this.baselineGroupPids=t}).catch(()=>{})}async probe(e){const t=this.config.command||"gemini",i=await fe(t),s=i!==null;let n=null,o;if(s){const h=await ge(t);n=h.version,h.error&&(o=h.error)}else o={code:"cli_not_found",message:`command not found: ${t}`};const a=this.acpClient?.model??null,l=this.getStatus();let d;return e?.conversation?this.acpClient?.isAlive?d={attempted:!0,ok:!0,latency_ms:null}:d={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:d={attempted:!1,ok:!1,latency_ms:null},{cli:{command:t,installed:s,path:i,version:n,...o?{error:o}:{}},conversation:d,config:{model:a,base_url:null,source:{model:a?"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=O({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
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?D:this.isKimi?R:null;if(!t||e&&e.models.length>0)return e;const i=this.resolveFallbackCurrentModelId(t,e?.currentModelId??"");return{modes:e?.modes??[],currentModeId:e?.currentModeId??"",models:[...t],currentModelId:i}}resolveFallbackCurrentModelId(e,t){if(e.some(s=>s.modelId===t))return t;const i=(this.autoInjectArgs?.model??this.acpInitialModel??"").trim();return i&&e.some(s=>s.modelId===i)?i:e[0]?.modelId??""}get isReasonix(){return(this.config.command??"").includes("reasonix")}get isKimi(){return(this.config.command??"").includes("kimi")}buildToolbarContext(e,t){const i=this.acpClient?.sessionOptions,s=i?.currentModeId??"";let n=i?.currentModelId??"";const o=i?.modes.map(m=>({id:m.id,name:m.name}))??[],a=i?.models&&i.models.length>0,l=a?i.models:this.isReasonix?D:this.isKimi?R:[],d=a?"acp":this.isReasonix?"reasonix_fallback":this.isKimi?"kimi_fallback":"empty";this.isKimi&&!a&&r.warn("acp-adapter",`[toolbar-diag] kimi ACP session returned no models; using static fallback. command=${this.config.command} fallbackIds=${JSON.stringify(R.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})),b=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 y={outcome:e,...t?{cwd:t}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:u,available_modes:b,availableModels:u,availableModes:b,models:h,modes:o};return r.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(u.map(m=>m.id))} currentModelId=${n}`),y}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.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.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 a;return n&&(o==="allow-once"||o==="allow-always")?a=o:n?a="allow":a="deny",s?this.handleAcpApprovalAction(s,a)?(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;if(n==="action"&&String(s.value??"")==="cancel")o=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()}o=i.options.find(d=>d.name.trim()===l||d.optionId===l)}if(!o)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,o.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),this.activeRun&&(this.activeRun.lastProgressAt=Date.now(),this.resetIdleTimer(this.activeRun));const a=/_skip$/.test(o.optionId);return{delivered:!0,result:{request_id:t,resolution:a?"cancel":"answer"},kind:a?"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 g.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: ${I(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=O({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(W),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 C,markdownSegmenter:new re,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=ae(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=le(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),a=this.injectQuoteProtocolOnce(s.sessionId,o),l=this.injectIdentityOnce(s.sessionId,a),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),A(t,be))}injectIdentityOnce(e,t){if(!e||this.identityInjectedSessions.has(e))return t;const i=this.callbacks.getAgentProfile?.(),s=i?.agentName?.trim()??"",n=i?.introduction?.trim()??"";if(!s&&!n)return t;this.identityInjectedSessions.add(e);const o=["[system-identity]"];return s&&o.push(`Your name is "${s}".`),n&&o.push(`Your self-introduction: ${n}`),o.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),o.push("[/system-identity]"),r.info("acp-adapter",`Injected agent identity for session=${e} name="${s}"`),A(t,o.join(`
|
|
4
4
|
`))}injectSessionContextOnce(e,t){return!e||!this.sessionContextPending?t:(this.sessionContextPending=!1,r.info("acp-adapter",`Injected Grix session context for session=${e}`),A(t,V(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})`),A(t,`[recovery-context]
|
|
5
5
|
${i}
|
|
6
6
|
[/recovery-context]`)):t}sendPromptWithRetry(e,t){this.acpClient.send(t).then(()=>{this.activeRun===e&&(e.busyWaiting=!1)}).catch(i=>{if(we(i)){this.scheduleAgentBusyRetry(e,t,I(i));return}if(r.error("acp-adapter",`Prompt failed: ${I(i)}`),_e(i)){const n=I(i),o=e.eventId,a=e.sessionId;r.info("acp-adapter",`Internal error escalated to bridge: event=${o} session=${a} err=${n}`),this.silentlyDiscardActiveRun(e),this.emit("internalError",{eventId:o,sessionId:a,errorMsg:n});return}const s=i instanceof Error?i.message:String(i);this.finishRun("failed",s)})}scheduleAgentBusyRetry(e,t,i){if(this.activeRun!==e)return;e.busyWaiting=!0;const s=Date.now();e.busyWaitSince||(e.busyWaitSince=s);const n=s-e.busyWaitSince;if(n>=L){r.error("acp-adapter",`agent busy wait exceeded ${Math.round(L/6e4)}min, giving up event=${e.eventId}: ${i}`),e.buffer+=`
|
|
7
7
|
|
|
8
|
-
The agent's own background turn kept the session busy past the wait limit, so this message was not delivered. Please resend it later.`,this.finishRun("failed");return}const o=Math.min(Ce*2**e.busyRetryCount,Ae);e.busyRetryCount+=1,r.info("acp-adapter",`agent busy, keeping event queued event=${e.eventId} waited=${Math.round(n/1e3)}s retry_in=${o}ms`),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),e.lastProgressAt=s,e.busyRetryTimer=setTimeout(()=>{if(e.busyRetryTimer=null,this.activeRun===e){if(!this.acpClient?.isAlive){this.finishRun("failed",
|
|
8
|
+
The agent's own background turn kept the session busy past the wait limit, so this message was not delivered. Please resend it later.`,this.finishRun("failed");return}const o=Math.min(Ce*2**e.busyRetryCount,Ae);e.busyRetryCount+=1,r.info("acp-adapter",`agent busy, keeping event queued event=${e.eventId} waited=${Math.round(n/1e3)}s retry_in=${o}ms`),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),e.lastProgressAt=s,e.busyRetryTimer=setTimeout(()=>{if(e.busyRetryTimer=null,this.activeRun===e){if(!this.acpClient?.isAlive){this.finishRun("failed",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"),this.config.command.includes("qwen")&&!e.includes("--experimental-skills")&&e.push("--experimental-skills"),this.config.command==="kiro-cli"&&!e.includes("--trust-all-tools")&&e.push("--trust-all-tools"),(this.config.command==="copilot"||this.config.command.endsWith("/copilot")||this.config.command==="gh"&&e.includes("copilot"))&&(e.includes("--allow-all-tools")||e.push("--allow-all-tools"),e.includes("--allow-all-paths")||e.push("--allow-all-paths")),this.reasonixAuditTranscriptPath&&!e.includes("--transcript")&&(await g.mkdir(p.dirname(this.reasonixAuditTranscriptPath),{recursive:!0,mode:448}),await(await g.open(this.reasonixAuditTranscriptPath,"a",384)).close(),await g.chmod(this.reasonixAuditTranscriptPath,384),e.push("--transcript",this.reasonixAuditTranscriptPath)),this.agentProcess=new G(this.bridgeLog),this.baselineGroupPids=[];const i=this.resolveCwd();try{if(!(await g.stat(i)).isDirectory())throw new Error(`Bound path is not a directory: ${i}`)}catch(s){throw String(s?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${i}. Please rebind with /grix open <valid-directory>.`):s}await Y(this.config.command,i,this.config.env,this.nativeProviderScope).catch(s=>{r.warn("acp-adapter",`native provider config skipped: ${s instanceof Error?s.message:String(s)}`)}),r.info("acp-adapter",`Spawning ACP agent command=${this.config.command} args=${JSON.stringify(e)} cwd=${i} envKeys=${JSON.stringify(Object.keys(this.config.env??{}).sort())}`);try{if(this.isKimi){const s=ee({...process.env,...this.config.env});r.info("acp-adapter",`Prepared Kimi Layer plugin at ${s.pluginRoot} (registrationChanged=${s.registrationChanged})`)}await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:i,env:this.config.env})}catch(s){throw r.error("acp-adapter",`Failed to spawn agent process: ${s}`),this.rejectDeferredEvents(`agent spawn failed: ${s instanceof Error?s.message:String(s)}`),this.emit("exit",null),s}r.info("acp-adapter","ACP agent process started"),this.agentProcess.on("error",s=>{this.stopped||(r.error("acp-adapter",`Agent process error: ${s.message}`),this.activeRun&&this.finishRun("failed",`agent process error: ${s.message}`),this.rejectDeferredEvents(`agent process error: ${s.message}`),this.emit("exit",null))}),this.agentProcess.on("exit",s=>{this.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 te,this.acpClient.on("event",l=>this.handleAcpEvent(l)),this.acpClient.on("activity",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)}),this.acpClient.on("session-lost",()=>{this.stopped||this.agentProcess?.alive&&(r.error("acp-adapter","ACP transport closed while agent process still alive, triggering respawn"),this.activeRun&&(this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,"ACP transport closed"),this.finishRun("failed","ACP transport closed")),this.emit("exit",null))});const i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=!!i&&i!=="__continue__",n=async l=>{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})};let o=!1,a;try{await n(i)}catch(l){if(l instanceof ie){await this.handleAuthRequired(l);return}if(!i)throw this.handleConnectFailure(l),l;if(r.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${I(l)}`),o=!0,a=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${i}`);const d=await this.buildKiroRecoveryContext(this.currentAibotSessionId,i,e||this.resolveCwd());d?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,d),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${d.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${i}`)}await n(void 0)}this.sessionConnected=!0,this.sessionContextPending=!s||o,this.captureBaselineGroupPids(),r.info("acp-adapter",`ACP session ready: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId),this.currentAibotSessionId&&this.bindingStore&&this.bindingStore.setAcpSessionId(this.currentAibotSessionId,this.acpClient.sessionId);for(const[l,d]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(l,this.acpClient.sessionId);const h=o?"binding_recreated":"binding_ready",u=this.buildToolbarContext(h,d);o&&a&&(u.previous_session_id=a,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(l,"ready",d,u)}}async buildKiroRecoveryContext(e,t,i){try{const s=p.join(v(),".kiro","sessions","cli",`${t}.json`);await g.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,i);if(!n)return;const o=p.join(v(),".grix","data","session-summaries");await g.mkdir(o,{recursive:!0});const a=p.join(o,`${e}.md`),l=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
|
|
9
9
|
`);return await g.writeFile(a,l,"utf8"),r.info("acp-adapter",`Recovery summary saved: ${a} (chars=${n.length})`),["[\u5386\u53F2\u4E0A\u4E0B\u6587\u56DE\u704C\u8BF4\u660E]","\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u65E7\u4F1A\u8BDD\u81EA\u52A8\u6458\u8981\uFF0C\u8BF7\u5728\u540E\u7EED\u56DE\u7B54\u4E2D\u5EF6\u7EED\u5176\u4E2D\u7684\u76EE\u6807\u3001\u7EA6\u675F\u548C\u7ED3\u8BBA\u3002",`SOURCE_KIRO_SESSION_FILE: ${s}`,`SUMMARY_FILE: ${a}`,"",n.trim()].join(`
|
|
10
10
|
`)}catch(s){r.warn("acp-adapter",`Failed to build kiro recovery context: ${I(s)}`);return}}async generateSummaryByKiroCli(e,t){const s=["chat","--no-interactive","--trust-tools=read,grep",["\u8BF7\u57FA\u4E8E\u4E0B\u9762\u5F15\u7528\u7684 Kiro \u539F\u59CB\u4F1A\u8BDD\u6587\u4EF6\u751F\u6210\u6458\u8981\uFF0C\u8F93\u51FA\u5FC5\u987B\u4E3A Markdown\u3002","\u8981\u6C42\uFF1A1) \u7B80\u660E\u51C6\u786E\uFF1B2) \u5305\u542B goals/constraints/decisions/open_items \u56DB\u90E8\u5206\uFF1B3) \u4E0D\u8981\u8F93\u51FA\u4EE3\u7801\u5757\u56F4\u680F\u3002",`\u4F1A\u8BDD\u6587\u4EF6\uFF1A@"${e}"`].join(`
|
|
11
11
|
`)],n=await this.runCommandCapture("kiro-cli",s,t,45e3);if(!n)return;const o=Re(n).trim(),a=o.indexOf("> "),l=a>=0?o.slice(a+2).trim():o;if(l)return r.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${o.length}, body_chars=${l.length})`),l.split(`
|
|
12
12
|
`).filter(d=>!d.includes("Credits:")&&!d.includes("Time:")).join(`
|
|
13
|
-
`).trim()}runCommandCapture(e,t,i,s){return new Promise((n,o)=>{const a
|
|
13
|
+
`).trim()}runCommandCapture(e,t,i,s){return new Promise((n,o)=>{const a=$(e,t,{cwd:i,stdio:["ignore","pipe","pipe"],windowsHide:!0});let l="",d="";const h=setTimeout(()=>{a.kill("SIGTERM"),o(new Error(`${e} timed out after ${s}ms`))},s);a.stdout.on("data",u=>{l+=String(u)}),a.stderr.on("data",u=>{d+=String(u)}),a.on("error",u=>{clearTimeout(h),o(u)}),a.on("close",u=>{if(clearTimeout(h),u===0){n(l||d);return}o(new Error(`${e} exited with code ${u}: ${d||l}`))})})}handleConnectFailure(e){r.error("acp-adapter",`ACP session creation failed: ${I(e)}`),this.acpClient?.dispose(),this.acpClient?.removeAllListeners(),this.acpClient=null,this.emit("exit",null)}async startInternalApiAndMcp(e=this.resolveCwd()){try{this.internalApi||(this.internalApi=new ne,this.internalApi.setInvokeHandler(async(a,l,d,h)=>this.callbacks.agentInvoke(a,l,h)),await this.internalApi.start(0),r.info("acp-adapter",`Internal API started at ${this.internalApi.url}`)),this.internalApi.setMcpBridgeUpHandler(a=>this.onMcpBridgeUp(a));const t=this.config.command??"",i=t.includes("qwen")?p.join(v(),".qwen","skills"):t.includes("gemini")?p.join(v(),".gemini","skills"):t.includes("kiro")?p.join(v(),".kiro","skills"):this.isReasonix?p.join(v(),".reasonix","skills"):this.isKimi?p.join(J({...process.env,...this.config.env}),"skills"):this.auditAgentType==="copilot"||/^copilot(?:\.cmd)?$/i.test(p.basename(t).toLowerCase())||/^gh(?:\.exe)?$/i.test(p.basename(t).toLowerCase())&&(this.config.args??[]).includes("copilot")?p.join(v(),".copilot","skills"):this.auditAgentType==="hermes"?p.join(this.config.env?.HERMES_HOME?.trim()||Z({...process.env,...this.config.env},this.nativeProviderScope),"skills"):void 0;if(i){const a=X(i);a.length>0&&r.info("acp-adapter",`Synced connector skills to ${i}: [${a.join(", ")}]`)}const s=p.resolve(w,"../../mcp/mcp-bridge-server.js"),n=this.internalApi.mcpBridgeWsUrl,o=p.resolve(w,"../../mcp/acp-mcp-server.js");if(this.isKimi){await this.internalApi.setCodebaseNavigatorCwd(e);const a={Authorization:`Bearer ${this.internalApi.token}`};return[{type:"http",name:"grix-app-bridge",url:this.internalApi.mcpBridgeHttpUrl,headers:a},{type:"http",name:"grix-connector-tools",url:this.internalApi.mcpToolsHttpUrl,headers:a},{type:"http",name:"codebase-navigator",url:this.internalApi.codebaseNavigatorHttpUrl,headers:a}]}return[{name:"grix-app-bridge",command:process.execPath,args:[s,"--ws-url",n],...!this.isReasonix&&{env:{GRIX_CONNECTOR_MCP_BRIDGE_WS:n}}},{name:"grix-connector-tools",command:process.execPath,args:[o,"--api-url",this.internalApi.url],...!this.isReasonix&&{env:{}}}]}catch(t){r.error("acp-adapter",`Failed to start MCP tools: ${t}`);return}}onMcpBridgeUp(e){this.callbacks.sendMcpFrame?.(e)}deliverMcpFrameToAgent(e){this.internalApi?.sendMcpFrameToBridge(e)}cleanup(){this.flushOrphanBuffer(),this.activeRun,this.pendingApprovals.clear(),this.pendingQuestions.clear(),this.pendingAutoCompact=!1,this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.dispose(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null),this.baselineGroupPids=[],this.terminalAuthChild&&(this.terminalAuthChild.kill("SIGKILL"),this.terminalAuthChild=null)}async handleAuthRequired(e){r.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(s=>s.id).join(", ")}`);const t=e.authMethods.find(s=>/oauth|browser/i.test(s.id))??e.authMethods[0];if(!t)throw e;const i=this.currentAibotSessionId??"";this.callbacks.sendAuthNotification(i,`Authentication required (${t.id}). Initiating auth flow...`);for(const[s,n]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(s,"failed",n);if(t.type==="terminal")await this.runTerminalAuth(t,i);else{const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(i,`Please open this URL to authenticate:
|
|
14
14
|
${s}
|
|
15
15
|
|
|
16
|
-
Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(t.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(i,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0;await this.acpClient.connect({transport:
|
|
16
|
+
Waiting for authentication to complete...`)}try{await this.acpClient.authenticate(t.id),r.info("acp-adapter","Authentication successful"),this.callbacks.sendAuthNotification(i,"Authentication successful. Resuming...");const s=this.acpMcpTools?await this.startInternalApiAndMcp():void 0,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}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.captureBaselineGroupPids(),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[o,a]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(o,"ready",a,this.buildToolbarContext("binding_ready",a))}catch(s){throw r.error("acp-adapter",`Auth retry failed: ${s}`),s}}runTerminalAuth(e,t){return new Promise((i,s)=>{const n=$(this.config.command,e.args??[],{env:{...process.env,...this.config.env??{}},stdio:["ignore","pipe","pipe"],windowsHide:!0});this.terminalAuthChild=n;const o=/https?:\/\/[^\s"')\]]+/;let a=!1,l=!1;const d=u=>{if(a)return;const y=u.toString().replace(/\x1b\[[0-9;]*m/g,"").match(o);y&&(a=!0,r.info("acp-adapter",`Terminal auth URL captured: ${y[0]}`),this.callbacks.sendAuthNotification(t,`Please open this URL on any device to authenticate:
|
|
17
17
|
${y[0]}
|
|
18
18
|
|
|
19
|
-
Waiting for authentication to complete...`))};n.stdout?.on("data",d),n.stderr?.on("data",d);const h=setTimeout(()=>{l||(l=!0,n.kill("SIGKILL"),this.terminalAuthChild=null,s(new Error(`terminal auth timed out after ${j}ms`)))},j);n.on("error",u=>{l||(l=!0,clearTimeout(h),this.terminalAuthChild=null,s(u))}),n.on("exit",u=>{l||(l=!0,clearTimeout(h),this.terminalAuthChild=null,u===0?i():s(new Error(`terminal auth process exited with code ${u}`)))})})}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const t=/https?:\/\/[^\s"')\]]+/;let i=!1;const s=n=>{if(i)return;const a=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(t);a&&(i=!0,this.agentProcess.removeListener("stderr",s),e(a[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{i||(i=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){try{r.info("acp-adapter","incoming acp event",{eventType:e.type,sessionId:this.activeRun?.sessionId??null,eventKeys:Object.keys(e),rawEvent:x(e,2e3)})}catch{}if(e.type===f.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===f.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting?this.finishCompaction("context-window-update"):e.contextWindow&&this.maybeScheduleAutoCompact(e.contextWindow);return}const t=this.activeRun;if(!t){this.handleOrphanAcpEvent(e);return}if(t.busyWaiting){this.handleOrphanAcpEvent(e);return}this.captureAuditEvent(e),t.responded||(t.responded=!0);let i=!1;switch(e.type){case f.Text:{if(t.lastToolUse=null,t.lastToolResult=null,e.content){i=!0;const s=t.quotedStream.consume(e.content);s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(t,s.deltaContent)}break}case f.ToolUse:{if(i=!0,t.awaitingToolResult=!0,t.lastToolResult=null,e.toolName){if(e.toolCallId&&(t.toolNamesById.set(e.toolCallId,e.toolName),t.sentToolUseIds.has(e.toolCallId)))break;const s=e.toolInput??"",n=S(s);if(t.lastToolUse?.toolName===e.toolName&&S(t.lastToolUse.toolInput)===n)break;t.lastToolUse={toolName:e.toolName,toolInput:s},e.toolCallId&&t.sentToolUseIds.add(e.toolCallId);const o=this.trimToolWirePayload?M(s):s;this.emitRawEventEnvelope(t,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:o}})||this.callbacks.sendToolUse(t.eventId,t.sessionId,e.toolName,o)}break}case f.ToolResult:{t.awaitingToolResult=!1;{const s=e.toolCallId||e.toolName||"";t.lastToolProgress.delete(s),t.lastToolProgressSentAt.delete(s)}if(t.lastToolUse=null,e.content){i=!0;const s=N(t,e),n=e.content,o=S(n);if(t.lastToolResult?.toolName===s&&S(t.lastToolResult.content)===o)break;if(t.lastToolResult={toolName:s,content:n},!this.trimToolWirePayload||e.toolStatus==="failed"){const a=this.trimToolWirePayload?"(failed)":n;this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:s,content:a}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,s,a)}}break}case f.ToolProgress:{if(i=!0,e.content){const s=e.toolCallId||e.toolName||"";if(t.lastToolProgress.get(s)===e.content||(t.lastToolProgress.set(s,e.content),this.trimToolWirePayload))break;const n=Date.now(),o=t.lastToolProgressSentAt.get(s);if(o!==void 0&&n-o<ce)break;t.lastToolProgressSentAt.set(s,n);const a=N(t,e);this.emitRawEventEnvelope(t,{type:"tool_progress",payload:{tool_name:a,content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,a,e.content)}break}case f.Thinking:{t.lastToolUse=null,t.lastToolResult=null,e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(t.eventId,t.sessionId,e.content));break}case f.Error:{t.lastToolUse=null,t.lastToolResult=null;const s=String(e.error??"unknown error");this.emitRawEventEnvelope(t,{type:"error",payload:{message:s}}),r.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(t.eventId,t.sessionId,s);break}case f.Result:{if(e.synthetic&&this.shouldDeferSyntheticResultForDetachedKimiTask(t,e)){r.info("acp-adapter",`Synthetic ACP Result deferred: kimi detached task still running event=${t.eventId} session=${t.sessionId}`),this.acpClient?.deferSettledResult();return}this.emitRawEventEnvelope(t,{type:"result",payload:{done:e.done??!0}});const s=t.quotedStream.flush();s.deltaContent&&this.appendToStream(t,s.deltaContent),s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),this.flushStream(),this.finishRun("responded");break}}i&&(t.lastProgressAt=Date.now(),t.idleNoProgressCount=0),this.resetIdleTimer(t)}handlePermissionRequest(e){const t=e.permissionRequest;if(r.info("acp-adapter","permission_request received",{sessionId:this.activeRun?.sessionId,eventId:this.activeRun?.eventId,requestId:e.requestId??null,toolCallId:t?.toolCallId??null,toolName:t?.toolName??null,toolTitle:t?.toolTitle??null,optionsCount:Array.isArray(t?.options)?t.options.length:0,hasActiveRun:this.activeRun!==null,approvalMode:this.approvalMode,rawTransport:this.rawTransport,hasRawEventEnvelopeCb:typeof this.callbacks.sendRawEventEnvelope=="function",hasSendPermissionCardCb:typeof this.callbacks.sendPermissionCard=="function",rawPr:x(t,3e3)}),!t||!e.requestId||!this.acpClient){r.warn("acp-adapter","permission_request dropped: missing prerequisite",{hasPr:t!=null,hasRequestId:!!e.requestId,hasAcpClient:!!this.acpClient});return}const i=this.activeRun,s=ue(t);if(s&&i&&this.callbacks.sendAgentQuestionCard){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(a=>a.name)}]}),r.info("acp-adapter","AskUserQuestion forwarded via agent_question card",{sessionId:i.sessionId,eventId:i.eventId,requestId:e.requestId,optionsCount:s.answerOptions.length});return}if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){r.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${t.toolName}`),this.acpClient.respondPermission(t.requestId,{behavior:"allow"}).catch(()=>{});return}const n=t.toolCallId,o=de(t.rawParams,e.toolInput,t.toolTitle);this.pendingApprovals.set(n,e.requestId),i?(i.idleTimer&&(clearTimeout(i.idleTimer),i.idleTimer=null),this.emitRawEventEnvelope(i,{type:"permission_request",payload:{request_id:t.requestId,tool_call_id:n,tool_name:t.toolName,tool_title:t.toolTitle,...o?{tool_input:o}:{},options:t.options}})?r.info("acp-adapter","permission_request forwarded via rawEventEnvelope",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}):(r.info("acp-adapter","permission_request falling back to sendPermissionCard",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}),this.callbacks.sendPermissionCard({eventId:i.eventId,sessionId:i.sessionId,toolCallId:n,toolName:t.toolName,toolTitle:t.toolTitle,toolInput:o,options:t.options}))):(r.info("acp-adapter",`Permission request without active run, auto-approving: ${t.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(n))}emitRawEventEnvelope(e,t){return!e||!this.rawTransport||!this.callbacks.sendRawEventEnvelope?!1:(this.callbacks.sendRawEventEnvelope(e.eventId,e.sessionId,{type:t.type,payload:t.payload,seq:++this.rawEventSeq,at:new Date().toISOString()}),!0)}resetIdleTimer(e,t){e.idleTimer&&clearTimeout(e.idleTimer);const i=t??(e.awaitingToolResult?ye:Ie),s=e.lastProgressAt+T-Date.now(),n=Math.max(0,Math.min(i,s));e.idleTimer=setTimeout(()=>{this.onIdleTimeout(e)},n)}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 o=e.lastProgressAt>s;if(e.lastIdleCheckAt=Date.now(),o||n){n&&(e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,r.info("acp-adapter",`Hard cap recheck found ${o?"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,o=e.lastProgressAt>n,a=o?!1:await this.checkSessionFileActivity(t.sessionId,n);if(this.activeRun?.eventId!==e.eventId)return;const l=o||a;e.lastIdleCheckAt=Date.now(),l?(a&&(r.info("acp-adapter",`Idle timer: no stream progress but session file active, continuing: ${e.eventId}`),e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,r.info("acp-adapter",`Idle timer: ping ok, no stream progress (cycle=${e.idleNoProgressCount}), extending until inactivity cap (${Math.round(T/6e4)}min): ${e.eventId}`),this.resetIdleTimer(e))}).catch(()=>{this.activeRun?.eventId===e.eventId&&this.handlePingFailure(e,t)}):(r.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())}async handlePingFailure(e,t){const i=e.lastIdleCheckAt,s=e.lastProgressAt>i,n=s?!1:await this.checkSessionFileActivity(t.sessionId,i);if(this.activeRun?.eventId===e.eventId){if(s||n||e.awaitingToolResult||this.pendingQuestions.size>0){const o=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 ${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<B){r.warn("acp-adapter",`Idle timer: ping failed (${e.consecutivePingFailures}/${B}), rechecking in ${F/1e3}s: ${e.eventId}`),this.resetIdleTimer(e,F);return}r.error("acp-adapter",`Ping failed ${e.consecutivePingFailures}x with no activity evidence, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck()}}async checkSessionFileActivity(e,t){if(!e)return!1;try{return this.isKimi?await pe(e,t):this.config.command==="kiro-cli"?await he(e,t):!1}catch{return!1}}shouldDeferSyntheticResultForDetachedKimiTask(e,t){if(!this.isKimi)return!1;try{return me(t.sessionId||this.acpClient?.sessionId||e.sessionId)}catch{return!1}}declareStuck(){this.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(),ve))}emitSegmentedStream(e,t){if(!t)return;const i=e.markdownSegmenter.push(t);for(const s of i)s.text&&(this.callbacks.sendStreamChunk(e.eventId,e.sessionId,s.text,++e.chunkSeq,s.closeAfter===!0,e.currentClientMsgId,e.quotedMessageId),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,r.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}handleOrphanAcpEvent(e){if(!(!this.sessionConnected||this.compacting||Date.now()<this.orphanSuppressedUntil)){if(e.type===f.Text&&e.content){this.orphanSessionId===null&&(this.orphanSessionId=this.currentAibotSessionId??"");const t=this.orphanQuotedStream.consume(e.content);t.deltaContent&&(this.orphanBuffer+=t.deltaContent),this.orphanBuffer.length>=Te?this.flushOrphanBuffer():this.scheduleOrphanFlush();return}e.type===f.Result&&this.flushOrphanBuffer()}}scheduleOrphanFlush(){this.orphanFlushTimer&&clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=setTimeout(()=>{this.orphanFlushTimer=null,this.flushOrphanBuffer()},Se),this.orphanFlushTimer.unref?.()}flushOrphanBuffer(){if(this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanSessionId===null&&!this.orphanBuffer)return;const e=this.orphanQuotedStream.flush();e.deltaContent&&(this.orphanBuffer+=e.deltaContent),this.orphanQuotedStream=new C;const t=this.orphanSessionId||this.currentAibotSessionId;this.orphanSessionId=null;const i=this.orphanBuffer.trim();if(this.orphanBuffer="",!!i){if(!t||!this.callbacks.sendAgentMessage){r.warn("acp-adapter",`orphan agent output dropped (${t?"no sendAgentMessage callback":"no session id"}) chars=${i.length}`);return}r.info("acp-adapter",`delivering orphan agent output session=${t} chars=${i.length}`);for(let s=0;s<i.length;s+=K)this.callbacks.sendAgentMessage(t,i.slice(s,s+K))}}suppressOrphanOutput(e){this.orphanSuppressedUntil=Date.now()+e,this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanBuffer="",this.orphanQuotedStream=new C,this.orphanSessionId=null}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const t=e.buffer;e.buffer="",this.emitSegmentedStream(e,t)}finishRun(e,t){const i=this.activeRun;if(!i)return;this.snapshotAuditBoundary(i.eventId),this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",i.eventId),this.clearRunTimers(i);const s=i.quotedStream.flush();s.deltaContent&&(i.buffer+=s.deltaContent),s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),t&&(e==="failed"?(r.error("acp-adapter",`finishRun failed: ${t} event=${i.eventId}`),i.buffer+=`
|
|
19
|
+
Waiting for authentication to complete...`))};n.stdout?.on("data",d),n.stderr?.on("data",d);const h=setTimeout(()=>{l||(l=!0,n.kill("SIGKILL"),this.terminalAuthChild=null,s(new Error(`terminal auth timed out after ${j}ms`)))},j);n.on("error",u=>{l||(l=!0,clearTimeout(h),this.terminalAuthChild=null,s(u))}),n.on("exit",u=>{l||(l=!0,clearTimeout(h),this.terminalAuthChild=null,u===0?i():s(new Error(`terminal auth process exited with code ${u}`)))})})}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const t=/https?:\/\/[^\s"')\]]+/;let i=!1;const s=n=>{if(i)return;const a=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(t);a&&(i=!0,this.agentProcess.removeListener("stderr",s),e(a[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{i||(i=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){try{r.info("acp-adapter","incoming acp event",{eventType:e.type,sessionId:this.activeRun?.sessionId??null,eventKeys:Object.keys(e),rawEvent:x(e,2e3)})}catch{}if(e.type===f.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===f.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting?this.finishCompaction("context-window-update"):e.contextWindow&&this.maybeScheduleAutoCompact(e.contextWindow);return}const t=this.activeRun;if(!t){this.handleOrphanAcpEvent(e);return}if(t.busyWaiting){this.handleOrphanAcpEvent(e);return}this.captureAuditEvent(e),t.responded||(t.responded=!0);let i=!1;switch(e.type){case f.Text:{if(t.lastToolUse=null,t.lastToolResult=null,e.content){i=!0;const s=t.quotedStream.consume(e.content);s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(t,s.deltaContent)}break}case f.ToolUse:{if(i=!0,t.awaitingToolResult=!0,t.lastToolResult=null,e.toolName){if(e.toolCallId&&(t.toolNamesById.set(e.toolCallId,e.toolName),t.sentToolUseIds.has(e.toolCallId)))break;const s=e.toolInput??"",n=S(s);if(t.lastToolUse?.toolName===e.toolName&&S(t.lastToolUse.toolInput)===n)break;t.lastToolUse={toolName:e.toolName,toolInput:s},e.toolCallId&&t.sentToolUseIds.add(e.toolCallId);const o=this.trimToolWirePayload?M(s):s;this.emitRawEventEnvelope(t,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:o}})||this.callbacks.sendToolUse(t.eventId,t.sessionId,e.toolName,o)}break}case f.ToolResult:{t.awaitingToolResult=!1;{const s=e.toolCallId||e.toolName||"";t.lastToolProgress.delete(s),t.lastToolProgressSentAt.delete(s)}if(t.lastToolUse=null,e.content){i=!0;const s=N(t,e),n=e.content,o=S(n);if(t.lastToolResult?.toolName===s&&S(t.lastToolResult.content)===o)break;if(t.lastToolResult={toolName:s,content:n},!this.trimToolWirePayload||e.toolStatus==="failed"){const a=this.trimToolWirePayload?"(failed)":n;this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:s,content:a}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,s,a)}}break}case f.ToolProgress:{if(i=!0,e.content){const s=e.toolCallId||e.toolName||"";if(t.lastToolProgress.get(s)===e.content||(t.lastToolProgress.set(s,e.content),this.trimToolWirePayload))break;const n=Date.now(),o=t.lastToolProgressSentAt.get(s);if(o!==void 0&&n-o<ce)break;t.lastToolProgressSentAt.set(s,n);const a=N(t,e);this.emitRawEventEnvelope(t,{type:"tool_progress",payload:{tool_name:a,content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,a,e.content)}break}case f.Thinking:{t.lastToolUse=null,t.lastToolResult=null,e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(t.eventId,t.sessionId,e.content));break}case f.Error:{t.lastToolUse=null,t.lastToolResult=null;const s=String(e.error??"unknown error");this.emitRawEventEnvelope(t,{type:"error",payload:{message:s}}),r.error("acp-adapter",`ACP error: ${s}`),this.callbacks.sendRunError(t.eventId,t.sessionId,s);break}case f.Result:{if(e.synthetic&&this.shouldDeferSyntheticResultForDetachedKimiTask(t,e)){r.info("acp-adapter",`Synthetic ACP Result deferred: kimi detached task still running event=${t.eventId} session=${t.sessionId}`),this.acpClient?.deferSettledResult();return}this.emitRawEventEnvelope(t,{type:"result",payload:{done:e.done??!0}});const s=t.quotedStream.flush();s.deltaContent&&this.appendToStream(t,s.deltaContent),s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),this.flushStream(),this.finishRun("responded");break}}i&&(t.lastProgressAt=Date.now(),t.idleNoProgressCount=0),this.resetIdleTimer(t)}handlePermissionRequest(e){const t=e.permissionRequest;if(r.info("acp-adapter","permission_request received",{sessionId:this.activeRun?.sessionId,eventId:this.activeRun?.eventId,requestId:e.requestId??null,toolCallId:t?.toolCallId??null,toolName:t?.toolName??null,toolTitle:t?.toolTitle??null,optionsCount:Array.isArray(t?.options)?t.options.length:0,hasActiveRun:this.activeRun!==null,approvalMode:this.approvalMode,rawTransport:this.rawTransport,hasRawEventEnvelopeCb:typeof this.callbacks.sendRawEventEnvelope=="function",hasSendPermissionCardCb:typeof this.callbacks.sendPermissionCard=="function",rawPr:x(t,3e3)}),!t||!e.requestId||!this.acpClient){r.warn("acp-adapter","permission_request dropped: missing prerequisite",{hasPr:t!=null,hasRequestId:!!e.requestId,hasAcpClient:!!this.acpClient});return}const i=this.activeRun,s=ue(t);if(s&&i&&this.callbacks.sendAgentQuestionCard){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(a=>a.name)}]}),r.info("acp-adapter","AskUserQuestion forwarded via agent_question card",{sessionId:i.sessionId,eventId:i.eventId,requestId:e.requestId,optionsCount:s.answerOptions.length});return}if(this.approvalMode==="yolo"||this.approvalMode==="autoEdit"){r.info("acp-adapter",`Auto-approving (${this.approvalMode}): ${t.toolName}`),this.acpClient.respondPermission(t.requestId,{behavior:"allow"}).catch(()=>{});return}const n=t.toolCallId,o=de(t.rawParams,e.toolInput,t.toolTitle);this.pendingApprovals.set(n,e.requestId),i?(i.idleTimer&&(clearTimeout(i.idleTimer),i.idleTimer=null),this.emitRawEventEnvelope(i,{type:"permission_request",payload:{request_id:t.requestId,tool_call_id:n,tool_name:t.toolName,tool_title:t.toolTitle,...o?{tool_input:o}:{},options:t.options}})?r.info("acp-adapter","permission_request forwarded via rawEventEnvelope",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}):(r.info("acp-adapter","permission_request falling back to sendPermissionCard",{sessionId:i.sessionId,eventId:i.eventId,toolCallId:n,toolName:t.toolName}),this.callbacks.sendPermissionCard({eventId:i.eventId,sessionId:i.sessionId,toolCallId:n,toolName:t.toolName,toolTitle:t.toolTitle,toolInput:o,options:t.options}))):(r.info("acp-adapter",`Permission request without active run, auto-approving: ${t.toolName}`),this.acpClient.respondPermission(e.requestId,{behavior:"allow"}),this.pendingApprovals.delete(n))}emitRawEventEnvelope(e,t){return!e||!this.rawTransport||!this.callbacks.sendRawEventEnvelope?!1:(this.callbacks.sendRawEventEnvelope(e.eventId,e.sessionId,{type:t.type,payload:t.payload,seq:++this.rawEventSeq,at:new Date().toISOString()}),!0)}resetIdleTimer(e,t){e.idleTimer&&clearTimeout(e.idleTimer);const i=t??(e.awaitingToolResult?ye:Ie),s=e.lastProgressAt+T-Date.now(),n=Math.max(0,Math.min(i,s));e.idleTimer=setTimeout(()=>{this.onIdleTimeout(e)},n)}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 o=e.lastProgressAt>s;if(e.lastIdleCheckAt=Date.now(),o||n){n&&(e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,r.info("acp-adapter",`Hard cap recheck found ${o?"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,o=e.lastProgressAt>n,a=o?!1:await this.checkSessionFileActivity(t.sessionId,n);if(this.activeRun?.eventId!==e.eventId)return;const l=o||a;e.lastIdleCheckAt=Date.now(),l?(a&&(r.info("acp-adapter",`Idle timer: no stream progress but session file active, continuing: ${e.eventId}`),e.lastProgressAt=Date.now()),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,r.info("acp-adapter",`Idle timer: ping ok, no stream progress (cycle=${e.idleNoProgressCount}), extending until inactivity cap (${Math.round(T/6e4)}min): ${e.eventId}`),this.resetIdleTimer(e))}).catch(()=>{this.activeRun?.eventId===e.eventId&&this.handlePingFailure(e,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 o=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 ${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<B){r.warn("acp-adapter",`Idle timer: ping failed (${e.consecutivePingFailures}/${B}), rechecking in ${F/1e3}s: ${e.eventId}`),this.resetIdleTimer(e,F);return}r.error("acp-adapter",`Ping failed ${e.consecutivePingFailures}x with no activity evidence, declaring stuck: ${e.eventId}`),this.finishRun("failed",this.describeUnreachable()),this.declareStuck()}}async checkSessionFileActivity(e,t){if(!e)return!1;try{return this.isKimi?await pe(e,t):this.config.command==="kiro-cli"?await he(e,t):!1}catch{return!1}}shouldDeferSyntheticResultForDetachedKimiTask(e,t){if(!this.isKimi)return!1;try{return me(t.sessionId||this.acpClient?.sessionId||e.sessionId)}catch{return!1}}declareStuck(){this.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(),ve))}emitSegmentedStream(e,t){if(!t)return;const i=e.markdownSegmenter.push(t);for(const s of i)s.text&&(this.callbacks.sendStreamChunk(e.eventId,e.sessionId,s.text,++e.chunkSeq,s.closeAfter===!0,e.currentClientMsgId,e.quotedMessageId),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,r.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}handleOrphanAcpEvent(e){if(!(!this.sessionConnected||this.compacting||Date.now()<this.orphanSuppressedUntil)){if(e.type===f.Text&&e.content){this.orphanSessionId===null&&(this.orphanSessionId=this.currentAibotSessionId??"");const t=this.orphanQuotedStream.consume(e.content);t.deltaContent&&(this.orphanBuffer+=t.deltaContent),this.orphanBuffer.length>=Te?this.flushOrphanBuffer():this.scheduleOrphanFlush();return}e.type===f.Result&&this.flushOrphanBuffer()}}scheduleOrphanFlush(){this.orphanFlushTimer&&clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=setTimeout(()=>{this.orphanFlushTimer=null,this.flushOrphanBuffer()},Se),this.orphanFlushTimer.unref?.()}flushOrphanBuffer(){if(this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanSessionId===null&&!this.orphanBuffer)return;const e=this.orphanQuotedStream.flush();e.deltaContent&&(this.orphanBuffer+=e.deltaContent),this.orphanQuotedStream=new C;const t=this.orphanSessionId||this.currentAibotSessionId;this.orphanSessionId=null;const i=this.orphanBuffer.trim();if(this.orphanBuffer="",!!i){if(!t||!this.callbacks.sendAgentMessage){r.warn("acp-adapter",`orphan agent output dropped (${t?"no sendAgentMessage callback":"no session id"}) chars=${i.length}`);return}r.info("acp-adapter",`delivering orphan agent output session=${t} chars=${i.length}`);for(let s=0;s<i.length;s+=K)this.callbacks.sendAgentMessage(t,i.slice(s,s+K))}}suppressOrphanOutput(e){this.orphanSuppressedUntil=Date.now()+e,this.orphanFlushTimer&&(clearTimeout(this.orphanFlushTimer),this.orphanFlushTimer=null),this.orphanBuffer="",this.orphanQuotedStream=new C,this.orphanSessionId=null}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const t=e.buffer;e.buffer="",this.emitSegmentedStream(e,t)}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})`}finishRun(e,t){const i=this.activeRun;if(!i)return;this.snapshotAuditBoundary(i.eventId),this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",i.eventId),this.clearRunTimers(i);const s=i.quotedStream.flush();s.deltaContent&&(i.buffer+=s.deltaContent),s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),t&&(e==="failed"?(r.error("acp-adapter",`finishRun failed: ${t} event=${i.eventId}`),i.buffer+=`
|
|
20
20
|
|
|
21
|
-
The task was interrupted. Please resend your instruction.`):r.info("acp-adapter",`finishRun ${e}: ${t} event=${i.eventId}`)),i.buffer&&(this.emitSegmentedStream(i,i.buffer),i.buffer="");const n=++i.chunkSeq;this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(i.eventId,i.sessionId,i.currentClientMsgId).then(()=>{i.silent||this.callbacks.sendEventResult(i.eventId,e,t),this.persistEventResult(i,e,t),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(o=>{const a=`final output delivery failed: ${o instanceof Error?o.message:String(o)}`,l=e==="responded"?"failed":e,d=e==="responded"?a:t;r.warn("acp-adapter",`finalStreamChunk rejected/failed event=${i.eventId}: ${a}`),i.silent||this.callbacks.sendEventResult(i.eventId,l,d),this.persistEventResult(i,l,d),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}):(this.callbacks.sendStreamChunk(i.eventId,i.sessionId,"",n,!0,i.currentClientMsgId),i.silent||this.callbacks.sendEventResult(i.eventId,e,t),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=P(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=P(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<U||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("acp-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${U}%, 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 ke extends
|
|
21
|
+
The task was interrupted. Please resend your instruction.`):r.info("acp-adapter",`finishRun ${e}: ${t} event=${i.eventId}`)),i.buffer&&(this.emitSegmentedStream(i,i.buffer),i.buffer="");const n=++i.chunkSeq;this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(i.eventId,i.sessionId,i.currentClientMsgId).then(()=>{i.silent||this.callbacks.sendEventResult(i.eventId,e,t),this.persistEventResult(i,e,t),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(o=>{const a=`final output delivery failed: ${o instanceof Error?o.message:String(o)}`,l=e==="responded"?"failed":e,d=e==="responded"?a:t;r.warn("acp-adapter",`finalStreamChunk rejected/failed event=${i.eventId}: ${a}`),i.silent||this.callbacks.sendEventResult(i.eventId,l,d),this.persistEventResult(i,l,d),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}):(this.callbacks.sendStreamChunk(i.eventId,i.sessionId,"",n,!0,i.currentClientMsgId),i.silent||this.callbacks.sendEventResult(i.eventId,e,t),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=P(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=P(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<U||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("acp-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${U}%, 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 ke extends E{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{it as AcpAdapter};
|
|
@@ -5,7 +5,7 @@ import{spawn as ee,spawnSync as te,execSync as ie,execFile as Qe}from"node:child
|
|
|
5
5
|
`)}extractSingleTurnDelta(e){const t=e.trim();if(!t)return null;let i;try{i=JSON.parse(t)}catch{return o.warn("claude-adapter",`Single-turn fallback: dropping non-JSON stdout noise: ${t.slice(0,200)}`),null}const s=i.type==="stream_event"&&i.event&&typeof i.event=="object"?i.event:i,n=String(s.type??"");if(n==="content_block_delta"){const r=s.delta;if(r&&typeof r=="object"){const a=String(r.text??"");if(a)return{text:a,final:!1}}return null}if(n==="result"){const r=String(s.subtype??""),a=String(s.terminal_reason??""),c=s.is_error===!0||a==="api_error";if(r&&r!=="success"||c){const u=Array.isArray(s.errors)?s.errors.join("; "):"",h=String(s.result??"").trim(),f=u||h||a;throw new Error(`claude single-turn result ${r||"error"}${f?`: ${f}`:""}`)}const l=String(s.result??"");return l?{text:l,final:!0}:null}return null}resolveEventAuditConfig(e,t){if(e.audit?.enabled===!0)return e.audit;const i=t?.audit;if(i?.enabled!==!0)return;const s=i.profile??"full";return{enabled:!0,profile:s,capture:i.capture??(s==="usage"?{inputOutput:!1,tools:!1,subagents:!0,thinking:!1,rawProviderBody:i.rawProviderBody===!0}:{inputOutput:!0,tools:!0,subagents:!0,thinking:!1,rawProviderBody:i.rawProviderBody===!0})}}buildClaudeRuntimeEnv(e=this.resolveSessionRuntime()){const t={...process.env,...this.config.env};if(delete t.CLAUDECODE,t.CLAUDE_PLUGIN_DATA=E().dataDir,t.GRIX_HOOK_SIGNALS_PATH=this.resolveHookSignalsPath(),this.internalApi&&(t.GRIX_CLAUDE_INTERNAL_API_URL=this.internalApi.baseUrl,t.GRIX_CLAUDE_INTERNAL_API_TOKEN=this.internalApi.token),e.audit?.enabled===!0&&!this.internalApi&&o.error("claude-adapter","Audit enabled but internal API not started; OTEL capture env will not be injected"),e.audit?.enabled===!0&&this.internalApi){const s=this.ensureClaudeAuditEvidence(e.audit);t.CLAUDE_CODE_ENABLE_TELEMETRY="1",t.CLAUDE_CODE_ENHANCED_TELEMETRY_BETA="1",t.OTEL_LOGS_EXPORTER="otlp",t.OTEL_TRACES_EXPORTER="otlp",t.OTEL_METRICS_EXPORTER="none",t.OTEL_EXPORTER_OTLP_PROTOCOL="http/json",t.OTEL_EXPORTER_OTLP_ENDPOINT=this.internalApi.baseUrl,t.OTEL_EXPORTER_OTLP_HEADERS=`Authorization=Bearer ${this.internalApi.token}`,t.OTEL_LOGS_EXPORT_INTERVAL="1000",t.OTEL_TRACES_EXPORT_INTERVAL="1000",t.OTEL_LOG_USER_PROMPTS=e.audit.capture.inputOutput?"1":"0",t.OTEL_LOG_ASSISTANT_RESPONSES=e.audit.capture.inputOutput?"1":"0",t.OTEL_LOG_TOOL_DETAILS=e.audit.capture.tools?"1":"0",t.OTEL_LOG_TOOL_CONTENT=e.audit.capture.tools?"1":"0",e.audit.capture.rawProviderBody&&(t.OTEL_LOG_RAW_API_BODIES=`file:${s.rawBodyDir}`)}const i=ht(e.effort);if(i==="auto")delete t.CLAUDE_CODE_EFFORT_LEVEL;else{const s=ft(i);s&&(t.CLAUDE_CODE_EFFORT_LEVEL=s)}return t}ensureClaudeAuditEvidence(e){if(this.auditEvidencePaths)return this.auditEvidencePaths;const t=this.config.options??{},s=String(t.auditEvidenceRoot??"").trim()||p(E().dataDir,"audit-evidence"),n=Re(this.sessionId),r=Re(this.claudeCliSessionId||"pending"),a=p(s,"claude",n,r),c=p(a,"raw-api-bodies");ge(c,{recursive:!0,mode:448}),se(a,448),se(c,448);const l={rootDir:a,logsPath:p(a,"otel-logs.jsonl"),tracesPath:p(a,"otel-traces.jsonl"),rawBodyDir:c,rawProviderBody:e.capture.rawProviderBody};return this.internalApi?.setOtelHandler(async(u,h,f)=>{const m=u==="logs"?l.logsPath:l.tracesPath;await at(m,`${JSON.stringify({observedAt:new Date().toISOString(),signal:u,payload:h,rawBody:f})}
|
|
6
6
|
`,{encoding:"utf8",mode:384}),se(m,384)}),this.auditEvidencePaths=l,l}async runSingleTurnFallback(e,t){const i=this.resolveSessionRuntime(),s=this.resolveEventAuditConfig(i,e);if(s&&!this.internalApi){o.error("claude-adapter",`Audit capture unavailable for event ${e.event_id}: internal API not started`),this.bridgeCallbacks.sendEventResult(e.event_id,"failed","audit capture unavailable: internal API not started","audit_capture_unavailable");return}if(this.activeEvent?.eventId===e.event_id&&(this.activeEvent.singleTurnFallback=!0,s&&(this.activeEvent.auditSingleTurnFallback=!0)),this.claudeProcess||this.claudePty){const O=this.mcpChannelBroken,g=this.channelGateClosed;this.killClaudeProcess("single-turn fallback isolation"),this.mcpChannelBroken=O,this.channelGateClosed=g}const n=this.config.options??{},r=this.config.command||"claude",a=(this.config.args??[]).filter(O=>{const g=String(O).trim().toLowerCase();return g!=="--dangerously-load-development-channels"&&g!=="--session-id"&&g!=="--resume"}),c=String(i.cwd??"").trim(),l=String(i.modelId??"").trim(),u=He(i.modeId);await this.ensureWorkspaceTrust(c),await this.ensureClaudeOnboardingFlags(c),await this.ensureSkipDangerousPermissionPrompt(),await this.injectStatusLineSettings(c),await this.ensureStdioMcpServer();const h=String(i.claudeSessionId??"").trim()||this.claudeCliSessionId||G();this.claudeCliSessionId=h,this.claudeSessionCwd=c,!i.claudeSessionId&&i.onSessionIdAssigned&&i.onSessionIdAssigned(h);const f=this.buildClaudeRuntimeEnv(s?{...i,audit:s}:i),m=i.pluginDir??n.pluginDir??await this.ensureClaudePluginDir();let v=Je(h,c||void 0);v&&Ge(h,c||void 0)&&(v=!1);const y=String(i.claudeSessionId??"").trim()!==""&&!v;v&&We(h,c||void 0),this.captureSingleTurnFallbackJsonlBaseOffset(e.event_id),y&&o.warn("claude-adapter",`Claude local transcript missing for session ${h}; continuing silently with platform context`);const j=this.buildSingleTurnPrompt(e,!v),w=[...a,"-p","--output-format","stream-json","--verbose","--include-partial-messages",...v?["--resume",h]:["--session-id",h]];w.push("--plugin-dir",m,"--strict-mcp-config","--mcp-config",this.claudeMcpConfigPath,"--permission-mode",u===q.fullAuto?"bypassPermissions":"default"),l&&w.push("--model",l),w.push(j),o.warn("claude-adapter",`Running single-turn fallback for event ${e.event_id}: ${t}`),this.bridgeCallbacks.sendEventAck(e.event_id,e.session_id),this.startComposing(e.session_id,"fallback_single_turn"),await new Promise((O,g)=>{const k=ee(r,w,{cwd:c||process.cwd(),env:f,stdio:["ignore","pipe","pipe"],detached:process.platform!=="win32",windowsHide:!0});this.singleTurnFallbackChild=k;const P=()=>{this.singleTurnFallbackChild===k&&(this.singleTurnFallbackChild=null)};let S="",$=0,L=!1,Z=null;const fe=`fallback_${e.event_id}_${Date.now()}`,pe=String(e.msg_id??"").trim()||void 0;let x="";const me=R=>{let A;try{A=this.extractSingleTurnDelta(R)}catch(H){Z=H instanceof Error?H:new Error(String(H));return}A&&(A.final&&L||this.activeEvent?.eventId===e.event_id&&this.isEventAnswered()||($+=1,L=!0,this.markActiveEventActivity(e.event_id,e.session_id),this.bridgeCallbacks.sendStreamChunk(e.event_id,e.session_id,A.text,$,!1,fe,pe)))};k.stdout?.setEncoding("utf8"),k.stdout?.on("data",R=>{x+=R;let A=x.indexOf(`
|
|
7
7
|
`);for(;A>=0;){const H=x.slice(0,A);x=x.slice(A+1),me(H),A=x.indexOf(`
|
|
8
|
-
`)}}),k.stderr?.setEncoding("utf8"),k.stderr?.on("data",R=>{S.length>=le||(S+=R,S.length>le&&(S=S.slice(0,le)))}),k.on("error",R=>{P(),g(R)}),k.on("close",R=>{if(P(),x.trim()&&me(x),Z){g(Z);return}if(R!==0&&!L){const A=S.trim();g(new Error(A||`claude single-turn exited with code ${R}`));return}if(!L&&this.activeEvent?.eventId===e.event_id&&!this.isEventAnswered()){const A=S.trim();g(new Error(A||"claude single-turn produced no output"));return}L&&($+=1,this.bridgeCallbacks.sendStreamChunk(e.event_id,e.session_id,"",$,!0,fe,pe)),O()})}),this.activeEvent?.eventId===e.event_id&&(this.bridgeCallbacks.sendEventResult(e.event_id,"responded"),this.clearActiveEvent())}async waitForChannelReady(){if(!this.mcpServerReady)throw new Error("MCP stdio server not available");await this.waitForNotifyPort(3e4),process.platform==="win32"?await this.waitForWindowsChannelReady(Ne):await this.waitForChannelListening(Ne)}async ensureClaudeProcessReady(){this.claudeProcess||this.claudePty||(this.spawnPromise||(this.spawnPromise=this.spawnClaude().finally(()=>{this.spawnPromise=null})),await this.spawnPromise)}async spawnClaude(){if(this.claudeProcess)return;const e=this.lifecycleVersion,t=()=>{if(this.stopped||e!==this.lifecycleVersion)throw new Error("adapter stopped")},i=this.config.options??{};this.sessionIdConflictDetected=!1;const s=this.resolveSessionRuntime(),n=this.config.command||"claude",r=this.config.args??[],a=He(s.modeId),c=String(s.modelId??"").trim(),l=String(s.cwd??"").trim();this.claudeSessionCwd=l;const u=String(s.claudeSessionId??"").trim()||G();this.claudeCliSessionId=u,!s.claudeSessionId&&s.onSessionIdAssigned&&s.onSessionIdAssigned(u);const h=this.buildClaudeRuntimeEnv((()=>{const w=this.resolveEventAuditConfig(s,this.activeEvent?.rawEvent);return w?{...s,audit:w}:s})());let f=Je(u,l||void 0);if(f&&Ge(u,l||void 0)&&(f=!1),String(s.claudeSessionId??"").trim()!==""&&!f&&o.warn("claude-adapter",`Claude local transcript missing for session ${u}; continuing silently with platform context`),f?We(u,l||void 0):(de(u,[]),ue(u)),this.runtimeResolver&&!l)throw new Error("Claude session binding missing cwd \u2014 run /grix open <working-directory> first");if(!l)throw new Error("Claude runtime cwd is required");await this.ensureWorkspaceTrust(l),await this.ensureClaudeOnboardingFlags(l),await this.ensureSkipDangerousPermissionPrompt(),await this.injectStatusLineSettings(l);let m=!1,v="",y=null;try{t(),this.notifyPort=await ii([this.internalApiPort]),m=!0,t(),o.info("claude-adapter",`Allocated MCP notify port ${this.notifyPort} (internal API ${this.internalApiPort})`),await this.ensureStdioMcpServer(),t();const w=i.pluginDir||await this.ensureClaudePluginDir(),O=f?["--resume",u]:["--session-id",u],g=[...r,"--name",`grix-${this.sessionId}`,...O,"--plugin-dir",w,"--strict-mcp-config","--mcp-config",this.claudeMcpConfigPath];c&&(/^claude/i.test(c)?g.push("--model",c):(o.warn("claude-adapter",`Skipping --model ${c}: non-Anthropic models are incompatible with development channels. Using default model so channels can load. Switch models after startup via /model if needed.`),this.deferredModelId=c)),a===q.fullAuto&&g.push("--dangerously-skip-permissions"),g.push("--dangerously-load-development-channels",`server:${F}`);const k=ri(this.bridgeCallbacks.getAgentProfile?.(),f?void 0:this.sessionId,this.bridgeCallbacks.getAgentId?.());if(process.platform==="win32"){const P=k.replace(/</g,"[").replace(/>/g,"]").replace(/"/g,"'"),S=pi(this.claudeCliSessionId);await yi(S,P),g.push("--append-system-prompt-file",S)}else g.push("--append-system-prompt",k);if(process.platform==="win32")if(W){const P=Si(),S=["/c",n,...g];o.info("claude-adapter",`Spawning Claude via PTY on win32: cwd=${l} mode=${a} ${f?"resume":"new"} ${n} ${g.join(" ")}`);const $=W.spawn(P,S,{name:"xterm-256color",cols:120,rows:30,cwd:l,env:h,useConpty:!0,conptyInheritCursor:!1});this.claudePty=$,this.claudeChildPid=$.pid,this.startPtyAutoConfirm($),y=null}else o.info("claude-adapter",`Spawning Claude via shell on win32 (node-pty unavailable): cwd=${l} mode=${a} ${f?"resume":"new"} ${n} ${g.join(" ")}`),y=ee(n,g,{cwd:l,env:h,stdio:["pipe","pipe","pipe"],detached:process.platform!=="win32",shell:!0,windowsHide:!0});else{o.info("claude-adapter",`Spawning via expect PTY: cwd=${l} mode=${a} ${f?"resume":"new"} ${n} ${g.join(" ")}`);const P=Bt(typeof h.PATH=="string"?h.PATH:void 0);if(!P)throw new Error("expect not found in PATH. Install it with: apt install expect / dnf install expect / brew install expect");const S=p(dt(),`grix-claude-${G()}`);await M(S,{recursive:!0}),this.expectRunDir=S;const{expectPath:$,pidPath:L}=await di(S,n,g);v=L,t(),y=ee(P,[$],{cwd:l,env:h,stdio:["pipe","pipe","pipe"],detached:!0})}if(this.claudeProcess=y,this.clearPendingMcpFailureTimer(),this.mcpStartupFailureHandled=!1,this.mcpChannelBroken=!1,this.channelGateClosed=!1,this.startupChannelListening=!1,this.startupChannelListeningAt=0,y&&y.on("error",P=>{o.error("claude-adapter","Claude process spawn error: "+P),this.claudeProcess=null,this.claudeChildPid=0,this.alive=!1,this.releaseNotifyPortReservation(),this.stopMcpServer(),this.clearPendingMcpFailureTimer(),this.activeEvent&&(this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,"failed","Claude process spawn failed"),this.clearActiveEvent())}),t(),!this.claudePty)if(process.platform==="win32")this.claudeChildPid=y?.pid??0;else{const P=await hi(y,v);if(t(),!Number.isFinite(P)||P<=0)throw new Error("failed to determine spawned Claude pid");this.claudeChildPid=P}if(!this.claudeChildPid||this.claudeChildPid<=0)throw new Error("failed to determine spawned Claude pid")}catch(w){if(y?.pid&&U(y,"SIGTERM"),this.claudePty){try{this.claudePty.kill()}catch{}this.claudePty=null}throw this.claudeProcess===y&&(this.claudeProcess=null),this.claudeChildPid=0,m&&this.releaseNotifyPortReservation(),this.stopMcpServer(),w}const j=this.claudeChildPid;o.info("claude-adapter","Claude child PID: "+j+(this.claudePty?" (via PTY)":"")),this.setupProcessOrPtyHandlers(),this.alive=!0}setupProcessOrPtyHandlers(){if(this.claudeProcess){const e=this.claudeProcess;e.on("exit",(t,i)=>{if(o.info("claude-adapter",`Claude process exited (code=${t}, signal=${i})`),this.singleTurnFallbackProcesses.delete(e)){o.info("claude-adapter","Ignoring stale Claude process exit after lifecycle ownership transfer");return}if(this.claudeProcess=null,this.claudeChildPid=0,this.alive=!1,this.releaseNotifyPortReservation(),this.stopMcpServer(),this.clearPendingMcpFailureTimer(),!this.tryRecoverSessionIdConflict()){if(this.activeEvent&&!this.stopped){const s=this.isEventAnswered(),n=s?"responded":"failed",r=s?void 0:"Claude process exited";o.error("claude-adapter",`Claude process exited with active event ${this.activeEvent.eventId}, answered=${s}, sending ${n}`),this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,n,r),this.clearActiveEvent()}this.stopped||this.emit("exit",t),this.compacting&&this.finishCompaction("process-exit")}}),e.stdout?.on("data",t=>{this.claudeProcess===e&&this.handleClaudeOutput(t.toString())}),e.stderr?.on("data",t=>{if(this.claudeProcess!==e)return;const i=t.toString().trim();i&&o.info("claude-adapter",`[claude stderr] ${i}`),this.checkFailurePatterns(i)})}if(this.claudePty){const e=this.claudePty;e.onExit(({exitCode:t,signal:i})=>{if(o.info("claude-adapter",`Claude PTY process exited (code=${t}, signal=${i})`),this.singleTurnFallbackPtys.delete(e)){o.info("claude-adapter","Ignoring stale Claude PTY exit after lifecycle ownership transfer");return}if(this.claudePty=null,this.claudeChildPid=0,this.alive=!1,this.releaseNotifyPortReservation(),this.stopMcpServer(),this.clearPendingMcpFailureTimer(),!this.tryRecoverSessionIdConflict()){if(this.activeEvent&&!this.stopped){const s=this.isEventAnswered(),n=s?"responded":"failed",r=s?void 0:"Claude process exited";o.error("claude-adapter",`Claude PTY exited with active event ${this.activeEvent.eventId}, answered=${s}, sending ${n}`),this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,n,r),this.clearActiveEvent()}this.stopped||this.emit("exit",t),this.compacting&&this.finishCompaction("process-exit")}}),e.onData(t=>{this.claudePty===e&&(this.handleClaudeOutput(t),this.checkFailurePatterns(t))})}}lastPtyOutputAt=0;ptyProbe={title:0,spinner:0,meaningful:0,lastLogAt:0,lastMeaningfulAt:0};recordPtyProbe(e){const t=Date.now();this.lastPtyOutputAt=t;const i=e.includes("]0;"),s=e.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/[\x00-\x1f\x7f-\x9f]/g,"").trim(),n=s.length<=3||/^[✳✶✻✽✵❋✺✴❈❖✦✧✢◉◎⬥⬦◇◆▸▹►▻→←↑↓⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏·…\d]*still\s+thinking/i.test(s);if(i?this.ptyProbe.title++:!s||n?this.ptyProbe.spinner++:(this.ptyProbe.meaningful++,this.ptyProbe.lastMeaningfulAt=t),t-this.ptyProbe.lastLogAt>=2e3){const r=this.activeEvent?this.isEventAnswered()?"replied":"running":"none",a=this.ptyProbe.lastMeaningfulAt?t-this.ptyProbe.lastMeaningfulAt:-1;o.debug("claude-adapter",`[pty-probe] session=${this.sessionId??"?"} event=${r} win2s title=${this.ptyProbe.title} spinner=${this.ptyProbe.spinner} meaningful=${this.ptyProbe.meaningful} meaningfulGapMs=${a}`),this.ptyProbe.title=0,this.ptyProbe.spinner=0,this.ptyProbe.meaningful=0,this.ptyProbe.lastLogAt=t}}handleClaudeOutput(e){this.recordPtyProbe(e),this.compacting&&this.resetCompactingActivityTimer();const t=e.trim();if(t){const a=t.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202f\ufeff]/g,"").trim();a?a.length<=3||/^[✳✶✻✽✵❋✺✴❈❖✦✧✢◉◎⬥⬦◇◆▸▹►▻→←↑↓⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏·…\d]*still\s+thinking/i.test(a)?o.debug("claude-adapter",`[claude output] ${a}`):o.info("claude-adapter",`[claude output] ${a.slice(0,500)}`):o.debug("claude-adapter",`[claude output] ${t.slice(0,200).replace(/[^\x20-\x7e]/g,c=>`\\x${c.charCodeAt(0).toString(16).padStart(2,"0")}`)}`)}this.activeEvent&&this.markActiveEventActivity(this.activeEvent.eventId,this.activeEvent.sessionId);const i=t.match(/Session ID (\S+) is already in use/i);if(i){const a=i[1];this.sessionIdConflictDetected=!0;const c=[this.claudeProcess?.pid,this.claudeChildPid,this.claudePty?.pid].filter(l=>!!l&&l>0);de(a,c)}const s=t.replace(/\[[0-9;?]*[ -/]*[@-~]/g," ").replace(/[^a-zA-Z]+/g," ").toLowerCase(),n=s.replace(/ /g,"");if(this.claudePty&&!this.startupChannelListening&&/trust.*folder|quick.*safety.*check/.test(n)&&(o.info("claude-adapter","Auto-accepting workspace trust dialog"),this.claudePty.write("1\r")),(/listeningforch\w*nelmessages/.test(n)||/nnelmessagesfrom/.test(n)||/inboundmessageswillbepushedintothissession/.test(n))&&(this.startupChannelListening||(this.startupChannelListeningAt=Date.now(),this.startupChannelListening=!0,this.sendDeferredModelSwitch()),this.clearPendingMcpFailureTimer()),!this.startupChannelListening&&/channels?arenotcurrentlyavailable/.test(n)&&(this.channelGateClosed=!0,o.warn("claude-adapter",'Claude reports "Channels are not currently available" \u2014 channel gate (tengu_harbor) is closed for this account/model; switching to single-turn fallback')),!this.mcpStartupFailureHandled&&s.includes("mcp server failed")){if(this.startupChannelListening){o.warn("claude-adapter","Claude reported MCP server failed for one channel, but channel listening is active; ignoring non-blocking failure");return}this.pendingMcpFailureTimer||(this.pendingMcpFailureTimer=setTimeout(()=>{this.pendingMcpFailureTimer=null,!(this.startupChannelListening||this.mcpStartupFailureHandled)&&this.markMcpStartupFailure()},ei))}}checkFailurePatterns(e){/Session ID (\S+) is already in use/i.test(e)&&(this.sessionIdConflictDetected=!0),Ot.some(t=>t.test(e))&&(o.error("claude-adapter",`Auth failure: ${e}`),this.authFailureUntil=Date.now()+Te,this.activeEvent&&(this.bridgeCallbacks.sendStreamChunk(this.activeEvent.eventId,this.activeEvent.sessionId,`
|
|
8
|
+
`)}}),k.stderr?.setEncoding("utf8"),k.stderr?.on("data",R=>{S.length>=le||(S+=R,S.length>le&&(S=S.slice(0,le)))}),k.on("error",R=>{P(),g(R)}),k.on("close",R=>{if(P(),x.trim()&&me(x),Z){g(Z);return}if(R!==0&&!L){const A=S.trim();g(new Error(A||`claude single-turn exited with code ${R}`));return}if(!L&&this.activeEvent?.eventId===e.event_id&&!this.isEventAnswered()){const A=S.trim();g(new Error(A||"claude single-turn produced no output"));return}L&&($+=1,this.bridgeCallbacks.sendStreamChunk(e.event_id,e.session_id,"",$,!0,fe,pe)),O()})}),this.activeEvent?.eventId===e.event_id&&(this.bridgeCallbacks.sendEventResult(e.event_id,"responded"),this.clearActiveEvent())}async waitForChannelReady(){if(!this.mcpServerReady)throw new Error("MCP stdio server not available");await this.waitForNotifyPort(3e4),process.platform==="win32"?await this.waitForWindowsChannelReady(Ne):await this.waitForChannelListening(Ne)}async ensureClaudeProcessReady(){this.claudeProcess||this.claudePty||(this.spawnPromise||(this.spawnPromise=this.spawnClaude().finally(()=>{this.spawnPromise=null})),await this.spawnPromise)}async spawnClaude(){if(this.claudeProcess)return;const e=this.lifecycleVersion,t=()=>{if(this.stopped||e!==this.lifecycleVersion)throw new Error("adapter stopped")},i=this.config.options??{};this.sessionIdConflictDetected=!1;const s=this.resolveSessionRuntime(),n=this.config.command||"claude",r=this.config.args??[],a=He(s.modeId),c=String(s.modelId??"").trim(),l=String(s.cwd??"").trim();this.claudeSessionCwd=l;const u=String(s.claudeSessionId??"").trim()||G();this.claudeCliSessionId=u,!s.claudeSessionId&&s.onSessionIdAssigned&&s.onSessionIdAssigned(u);const h=this.buildClaudeRuntimeEnv((()=>{const w=this.resolveEventAuditConfig(s,this.activeEvent?.rawEvent);return w?{...s,audit:w}:s})());let f=Je(u,l||void 0);if(f&&Ge(u,l||void 0)&&(f=!1),String(s.claudeSessionId??"").trim()!==""&&!f&&o.warn("claude-adapter",`Claude local transcript missing for session ${u}; continuing silently with platform context`),f?We(u,l||void 0):(de(u,[]),ue(u)),this.runtimeResolver&&!l)throw new Error("Claude session binding missing cwd \u2014 run /grix open <working-directory> first");if(!l)throw new Error("Claude runtime cwd is required");await this.ensureWorkspaceTrust(l),await this.ensureClaudeOnboardingFlags(l),await this.ensureSkipDangerousPermissionPrompt(),await this.injectStatusLineSettings(l);let m=!1,v="",y=null;try{t(),this.notifyPort=await ii([this.internalApiPort]),m=!0,t(),o.info("claude-adapter",`Allocated MCP notify port ${this.notifyPort} (internal API ${this.internalApiPort})`),await this.ensureStdioMcpServer(),t();const w=i.pluginDir||await this.ensureClaudePluginDir(),O=f?["--resume",u]:["--session-id",u],g=[...r,"--name",`grix-${this.sessionId}`,...O,"--plugin-dir",w,"--strict-mcp-config","--mcp-config",this.claudeMcpConfigPath];c&&(/^claude/i.test(c)?g.push("--model",c):(o.warn("claude-adapter",`Skipping --model ${c}: non-Anthropic models are incompatible with development channels. Using default model so channels can load. Switch models after startup via /model if needed.`),this.deferredModelId=c)),a===q.fullAuto&&g.push("--dangerously-skip-permissions"),g.push("--dangerously-load-development-channels",`server:${F}`);const k=ri(this.bridgeCallbacks.getAgentProfile?.(),f?void 0:this.sessionId,this.bridgeCallbacks.getAgentId?.());if(process.platform==="win32"){const P=k.replace(/</g,"[").replace(/>/g,"]").replace(/"/g,"'"),S=pi(this.claudeCliSessionId);await yi(S,P),g.push("--append-system-prompt-file",S)}else g.push("--append-system-prompt",k);if(process.platform==="win32")if(W){const P=Si(),S=["/c",n,...g];o.info("claude-adapter",`Spawning Claude via PTY on win32: cwd=${l} mode=${a} ${f?"resume":"new"} ${n} ${g.join(" ")}`);const $=W.spawn(P,S,{name:"xterm-256color",cols:120,rows:30,cwd:l,env:h,useConpty:!0,conptyInheritCursor:!1});this.claudePty=$,this.claudeChildPid=$.pid,this.startPtyAutoConfirm($),y=null}else o.info("claude-adapter",`Spawning Claude via shell on win32 (node-pty unavailable): cwd=${l} mode=${a} ${f?"resume":"new"} ${n} ${g.join(" ")}`),y=ee(n,g,{cwd:l,env:h,stdio:["pipe","pipe","pipe"],detached:process.platform!=="win32",shell:!0,windowsHide:!0});else{o.info("claude-adapter",`Spawning via expect PTY: cwd=${l} mode=${a} ${f?"resume":"new"} ${n} ${g.join(" ")}`);const P=Bt(typeof h.PATH=="string"?h.PATH:void 0);if(!P)throw new Error("expect not found in PATH. Install it with: apt install expect / dnf install expect / brew install expect");const S=p(dt(),`grix-claude-${G()}`);await M(S,{recursive:!0}),this.expectRunDir=S;const{expectPath:$,pidPath:L}=await di(S,n,g);v=L,t(),y=ee(P,[$],{cwd:l,env:h,stdio:["pipe","pipe","pipe"],detached:!0})}if(this.claudeProcess=y,this.clearPendingMcpFailureTimer(),this.mcpStartupFailureHandled=!1,this.mcpChannelBroken=!1,this.channelGateClosed=!1,this.startupChannelListening=!1,this.startupChannelListeningAt=0,y&&y.on("error",P=>{o.error("claude-adapter","Claude process spawn error: "+P),this.claudeProcess=null,this.claudeChildPid=0,this.alive=!1,this.releaseNotifyPortReservation(),this.stopMcpServer(),this.clearPendingMcpFailureTimer(),this.activeEvent&&(this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,"failed","Claude process spawn failed"),this.clearActiveEvent())}),t(),!this.claudePty)if(process.platform==="win32")this.claudeChildPid=y?.pid??0;else{const P=await hi(y,v);if(t(),!Number.isFinite(P)||P<=0)throw new Error("failed to determine spawned Claude pid");this.claudeChildPid=P}if(!this.claudeChildPid||this.claudeChildPid<=0)throw new Error("failed to determine spawned Claude pid")}catch(w){if(y?.pid&&U(y,"SIGTERM"),this.claudePty){try{this.claudePty.kill()}catch{}this.claudePty=null}throw this.claudeProcess===y&&(this.claudeProcess=null),this.claudeChildPid=0,m&&this.releaseNotifyPortReservation(),this.stopMcpServer(),w}const j=this.claudeChildPid;o.info("claude-adapter","Claude child PID: "+j+(this.claudePty?" (via PTY)":"")),this.setupProcessOrPtyHandlers(),this.alive=!0}setupProcessOrPtyHandlers(){if(this.claudeProcess){const e=this.claudeProcess;e.on("exit",(t,i)=>{if(o.info("claude-adapter",`Claude process exited (code=${t}, signal=${i})`),this.singleTurnFallbackProcesses.delete(e)){o.info("claude-adapter","Ignoring stale Claude process exit after lifecycle ownership transfer");return}if(this.claudeProcess=null,this.claudeChildPid=0,this.alive=!1,this.releaseNotifyPortReservation(),this.stopMcpServer(),this.clearPendingMcpFailureTimer(),!this.tryRecoverSessionIdConflict()){if(this.activeEvent&&!this.stopped){const s=this.isEventAnswered(),n=s?"responded":"failed",r=s?void 0:`Claude process exited before completing its reply (code=${t}, signal=${i})`;o.error("claude-adapter",`Claude process exited with active event ${this.activeEvent.eventId}, answered=${s}, sending ${n}`),this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,n,r),this.clearActiveEvent()}this.stopped||this.emit("exit",t),this.compacting&&this.finishCompaction("process-exit")}}),e.stdout?.on("data",t=>{this.claudeProcess===e&&this.handleClaudeOutput(t.toString())}),e.stderr?.on("data",t=>{if(this.claudeProcess!==e)return;const i=t.toString().trim();i&&o.info("claude-adapter",`[claude stderr] ${i}`),this.checkFailurePatterns(i)})}if(this.claudePty){const e=this.claudePty;e.onExit(({exitCode:t,signal:i})=>{if(o.info("claude-adapter",`Claude PTY process exited (code=${t}, signal=${i})`),this.singleTurnFallbackPtys.delete(e)){o.info("claude-adapter","Ignoring stale Claude PTY exit after lifecycle ownership transfer");return}if(this.claudePty=null,this.claudeChildPid=0,this.alive=!1,this.releaseNotifyPortReservation(),this.stopMcpServer(),this.clearPendingMcpFailureTimer(),!this.tryRecoverSessionIdConflict()){if(this.activeEvent&&!this.stopped){const s=this.isEventAnswered(),n=s?"responded":"failed",r=s?void 0:`Claude process exited before completing its reply (code=${t}, signal=${i})`;o.error("claude-adapter",`Claude PTY exited with active event ${this.activeEvent.eventId}, answered=${s}, sending ${n}`),this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,n,r),this.clearActiveEvent()}this.stopped||this.emit("exit",t),this.compacting&&this.finishCompaction("process-exit")}}),e.onData(t=>{this.claudePty===e&&(this.handleClaudeOutput(t),this.checkFailurePatterns(t))})}}lastPtyOutputAt=0;ptyProbe={title:0,spinner:0,meaningful:0,lastLogAt:0,lastMeaningfulAt:0};recordPtyProbe(e){const t=Date.now();this.lastPtyOutputAt=t;const i=e.includes("]0;"),s=e.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/[\x00-\x1f\x7f-\x9f]/g,"").trim(),n=s.length<=3||/^[✳✶✻✽✵❋✺✴❈❖✦✧✢◉◎⬥⬦◇◆▸▹►▻→←↑↓⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏·…\d]*still\s+thinking/i.test(s);if(i?this.ptyProbe.title++:!s||n?this.ptyProbe.spinner++:(this.ptyProbe.meaningful++,this.ptyProbe.lastMeaningfulAt=t),t-this.ptyProbe.lastLogAt>=2e3){const r=this.activeEvent?this.isEventAnswered()?"replied":"running":"none",a=this.ptyProbe.lastMeaningfulAt?t-this.ptyProbe.lastMeaningfulAt:-1;o.debug("claude-adapter",`[pty-probe] session=${this.sessionId??"?"} event=${r} win2s title=${this.ptyProbe.title} spinner=${this.ptyProbe.spinner} meaningful=${this.ptyProbe.meaningful} meaningfulGapMs=${a}`),this.ptyProbe.title=0,this.ptyProbe.spinner=0,this.ptyProbe.meaningful=0,this.ptyProbe.lastLogAt=t}}handleClaudeOutput(e){this.recordPtyProbe(e),this.compacting&&this.resetCompactingActivityTimer();const t=e.trim();if(t){const a=t.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202f\ufeff]/g,"").trim();a?a.length<=3||/^[✳✶✻✽✵❋✺✴❈❖✦✧✢◉◎⬥⬦◇◆▸▹►▻→←↑↓⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏·…\d]*still\s+thinking/i.test(a)?o.debug("claude-adapter",`[claude output] ${a}`):o.info("claude-adapter",`[claude output] ${a.slice(0,500)}`):o.debug("claude-adapter",`[claude output] ${t.slice(0,200).replace(/[^\x20-\x7e]/g,c=>`\\x${c.charCodeAt(0).toString(16).padStart(2,"0")}`)}`)}this.activeEvent&&this.markActiveEventActivity(this.activeEvent.eventId,this.activeEvent.sessionId);const i=t.match(/Session ID (\S+) is already in use/i);if(i){const a=i[1];this.sessionIdConflictDetected=!0;const c=[this.claudeProcess?.pid,this.claudeChildPid,this.claudePty?.pid].filter(l=>!!l&&l>0);de(a,c)}const s=t.replace(/\[[0-9;?]*[ -/]*[@-~]/g," ").replace(/[^a-zA-Z]+/g," ").toLowerCase(),n=s.replace(/ /g,"");if(this.claudePty&&!this.startupChannelListening&&/trust.*folder|quick.*safety.*check/.test(n)&&(o.info("claude-adapter","Auto-accepting workspace trust dialog"),this.claudePty.write("1\r")),(/listeningforch\w*nelmessages/.test(n)||/nnelmessagesfrom/.test(n)||/inboundmessageswillbepushedintothissession/.test(n))&&(this.startupChannelListening||(this.startupChannelListeningAt=Date.now(),this.startupChannelListening=!0,this.sendDeferredModelSwitch()),this.clearPendingMcpFailureTimer()),!this.startupChannelListening&&/channels?arenotcurrentlyavailable/.test(n)&&(this.channelGateClosed=!0,o.warn("claude-adapter",'Claude reports "Channels are not currently available" \u2014 channel gate (tengu_harbor) is closed for this account/model; switching to single-turn fallback')),!this.mcpStartupFailureHandled&&s.includes("mcp server failed")){if(this.startupChannelListening){o.warn("claude-adapter","Claude reported MCP server failed for one channel, but channel listening is active; ignoring non-blocking failure");return}this.pendingMcpFailureTimer||(this.pendingMcpFailureTimer=setTimeout(()=>{this.pendingMcpFailureTimer=null,!(this.startupChannelListening||this.mcpStartupFailureHandled)&&this.markMcpStartupFailure()},ei))}}checkFailurePatterns(e){/Session ID (\S+) is already in use/i.test(e)&&(this.sessionIdConflictDetected=!0),Ot.some(t=>t.test(e))&&(o.error("claude-adapter",`Auth failure: ${e}`),this.authFailureUntil=Date.now()+Te,this.activeEvent&&(this.bridgeCallbacks.sendStreamChunk(this.activeEvent.eventId,this.activeEvent.sessionId,`
|
|
9
9
|
|
|
10
10
|
Error: Claude authentication failed -- please re-login`,1,!1),this.bridgeCallbacks.sendEventResult(this.activeEvent.eventId,"failed","Claude authentication failed -- please re-login"),this.clearActiveEvent())),Lt.some(t=>t.test(e))&&(o.error("claude-adapter",`Usage limit: ${e}`),this.usageLimitUntil=Date.now()+Te,this.activeEvent&&(this.bridgeCallbacks.sendStreamChunk(this.activeEvent.eventId,this.activeEvent.sessionId,`
|
|
11
11
|
|