grix-connector 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/adapter/acp/acp-adapter.js +9 -7
  2. package/dist/adapter/acp/session-scanner.js +2 -0
  3. package/dist/adapter/acp/usage-parser.js +1 -1
  4. package/dist/adapter/claude/claude-adapter.js +22 -14
  5. package/dist/adapter/claude/protocol-contract.js +1 -1
  6. package/dist/adapter/claude/session-scanner.js +1 -0
  7. package/dist/adapter/codex/codex-bridge.js +7 -7
  8. package/dist/adapter/codex/codex-trust.js +4 -4
  9. package/dist/adapter/codex/session-scanner.js +1 -0
  10. package/dist/agent/process.js +2 -2
  11. package/dist/bridge/adapter-pool.js +1 -1
  12. package/dist/bridge/bridge.js +12 -6
  13. package/dist/bridge/send-controller.js +3 -3
  14. package/dist/bridge/session-controller.js +5 -5
  15. package/dist/bridge/session-scan-cache.js +1 -0
  16. package/dist/core/admin/admin-server.js +1 -1
  17. package/dist/core/aibot/client.js +2 -2
  18. package/dist/core/aibot/connection-handle.js +1 -1
  19. package/dist/core/aibot/connection-manager.js +1 -1
  20. package/dist/core/files/create-folder.js +1 -1
  21. package/dist/core/files/index.js +1 -1
  22. package/dist/core/files/list-handler.js +1 -1
  23. package/dist/core/files/utils.js +1 -1
  24. package/dist/core/installer/env-detect.js +2 -0
  25. package/dist/core/installer/index.js +1 -0
  26. package/dist/core/installer/installer.js +8 -0
  27. package/dist/core/installer/manual-guide.js +2 -0
  28. package/dist/core/installer/npm-registry.js +2 -0
  29. package/dist/core/installer/preflight.js +2 -0
  30. package/dist/core/installer/prereq-installer.js +3 -0
  31. package/dist/core/installer/registry.js +1 -0
  32. package/dist/core/installer/speed-test.js +1 -0
  33. package/dist/core/installer/types.js +0 -0
  34. package/dist/core/log/index.js +1 -1
  35. package/dist/core/log/logger.js +6 -6
  36. package/dist/core/mcp/internal-api-server.js +1 -1
  37. package/dist/core/protocol/message-reference.js +1 -1
  38. package/dist/core/protocol/protocol-descriptor.js +1 -1
  39. package/dist/core/provider-quota/kiro.js +1 -1
  40. package/dist/core/provider-quota/providers.js +1 -1
  41. package/dist/core/transport/json-rpc.js +3 -3
  42. package/dist/core/upgrade/npm-upgrader.js +2 -2
  43. package/dist/core/util/read-first-lines.js +5 -0
  44. package/dist/grix.js +3 -3
  45. package/dist/manager.js +2 -2
  46. package/dist/mcp/mcp-bridge-server.js +6 -0
  47. package/dist/mcp/stream-http/security.js +1 -1
  48. package/dist/protocol/acp-client.js +1 -1
  49. package/dist/protocol/event-mapper.js +1 -1
  50. package/dist/service/platform-adapter.js +13 -12
  51. package/dist/service/process-control.js +1 -1
  52. package/dist/service/service-manager.js +4 -1
  53. package/dist/types/events.js +1 -1
  54. package/openclaw-plugin/index.js +264 -60
  55. package/package.json +1 -1
@@ -1,13 +1,15 @@
1
- import{fileURLToPath as b}from"node:url";import p from"node:path";import{homedir as v}from"node:os";import{promises as f}from"node:fs";import{EventEmitter as C}from"node:events";import{spawn as A}from"node:child_process";import{AgentProcess as R}from"../../agent/process.js";import{AcpClient as w,AcpAuthRequiredError as T,isAuthRequiredError as y}from"../../protocol/acp-client.js";import{AgentEventType as m}from"../../types/events.js";import{InternalApiServer as k}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as _}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as $}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as E}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as M}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as P}from"../../core/protocol/message-metadata.js";import{log as r}from"../../core/log/index.js";import{scanSkills as S}from"../claude/skill-scanner.js";import{resolveCliPath as x,getCliVersion as q}from"../../core/util/cli-probe.js";const I=p.dirname(b(import.meta.url)),D=200,N=60*1e3,j=600*1e3;function B(h){return!(!(h instanceof Error)||h.code!==-32603||y(h))}function g(h){if(!(h instanceof Error))return String(h);let e=h.message;const t=h.data;return t&&(e+=` data=${JSON.stringify(t)}`),e}async function L(h,e){if(!h)return!1;const t=p.join(v(),".kiro","sessions","cli",`${h}.jsonl`);try{return(await f.stat(t)).mtimeMs>e}catch{return!1}}function U(h){return h.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class oe extends C{type="acp";config;callbacks;agentProcess=null;acpClient=null;internalApi=null;activeRun=null;compacting=!1;compactingTimer=null;compactionDoneResolver=null;eventResults=null;pendingApprovals=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;acpMcpTools;rawTransport;approvalMode;autoInjectArgs;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;sessionConnected=!1;rawEventSeq=0;recoveryContextBySessionId=new Map;constructor(e,t,i){if(super(),this.config=e,this.callbacks=t,this.acpAuthMethod=i?.acpAuthMethod,this.acpInitialMode=i?.acpInitialMode,this.acpInitialModel=i?.acpInitialModel,this.acpMcpTools=!1,this.rawTransport=i?.rawTransport??!1,this.approvalMode=i?.approvalMode??"default",this.autoInjectArgs=i?.autoInjectArgs,this.bindingStore=i?.bindingStore??null,this.currentAibotSessionId=i?.aibotSessionId?String(i.aibotSessionId).trim():void 0,i?.eventResultsPath&&(this.eventResults=new _(i.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const s=this.bindingStore.get(this.currentAibotSessionId);s?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,s.cwd)}}async start(){if(await this.spawnProcess(),!this.bindingStore){await this.connectSession(this.resolveCwd());return}const e=this.currentAibotSessionId?this.sessionBindings.get(this.currentAibotSessionId):void 0;e&&await this.connectSession(e)}async stop(){this.stopped=!0,this.rejectDeferredEvents("adapter stopped"),this.cancelAllDeferredTimers(),this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver(),this.compactionDoneResolver=null),this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null),this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,t){}async destroySession(e){}sendPrompt(e){const t=new O(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"))}setPermissionHandler(e){}async ping(e){return this.acpClient?.isAlive?this.acpClient.ping(e):this.agentProcess?.alive??!1}getStatus(){return{alive:this.agentProcess?.alive??!1,busy:this.activeRun!==null||this.compacting,sessions:this.sessionConnected?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null)}getMcpConfig(){if(!this.internalApi)return null;const e=p.resolve(I,"../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url]}}async probe(e){const t=this.config.command||"gemini",i=await x(t),s=i!==null;let n=null,o;if(s){const u=await q(t);n=u.version,u.error&&(o=u.error)}else o={code:"cli_not_found",message:`command not found: ${t}`};const a=this.acpClient?.model??null,c=this.getStatus();let d;return e?.conversation?this.acpClient?.isAlive?d={attempted:!0,ok:!0,latency_ms:null}:d={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:d={attempted:!1,ok:!1,latency_ms:null},{cli:{command: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:c.alive,busy:c.busy}}}getSupportedCommands(){return[{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){try{switch(e){case"model":{const s=t.trim();if(s)return await this.setModel(s)?{status:"ok",message:`Model set to ${s}`}:{status:"failed",message:`Failed to set model: ${s}`};const n=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${n.currentModelId||"unknown"}`,data:n}}case"mode":{const s=t.trim();if(s)return await this.setMode(s)?{status:"ok",message:`Mode set to ${s}`}:{status:"failed",message:`Failed to set mode: ${s}`};const n=this.buildToolbarContext("mode_list");return{status:"ok",message:`Current: ${n.currentModeId||"unknown"}`,data:n}}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 s=new Promise(n=>{this.compactionDoneResolver=n});try{await this.acpClient.send("/compact")}catch(n){throw this.finishCompaction("send-failed"),n}return this.compactingTimer=setTimeout(()=>this.finishCompaction("timeout"),15e3),this.compactingTimer.unref?.(),await s,{status:"ok",message:"Compacted"}}case"status":{const s=this.getStatus(),n=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${s.alive}, Busy: ${s.busy}, Model: ${n?.currentModelId??"unknown"}, Mode: ${n?.currentModeId??"unknown"}`,data:{alive:s.alive,busy:s.busy,sessions:s.sessions,model:n?.currentModelId??"",mode:n?.currentModeId??""}}}case"skills":{const s=S({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),n=s.map(o=>`- ${o.name}${o.trigger?` (${o.trigger})`:""} [${o.source}]: ${o.description}`);return{status:"ok",message:n.length>0?n.join(`
2
- `):"No skills found",data:s}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(s){return{status:"failed",message:s instanceof Error?s.message:String(s)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}buildToolbarContext(e,t){const i=this.acpClient?.sessionOptions,s=i?.currentModeId??"",n=i?.currentModelId??"",o=i?.modes.map(l=>({id:l.id,name:l.name}))??[],a=i?.models.map(l=>({modelId:l.modelId,name:l.name}))??[],c=i?.models.map(l=>({id:l.modelId,displayName:l.name}))??[],d=i?.modes.map(l=>({id:l.id,displayName:l.name}))??[],u={outcome:e,...t?{cwd:t}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:c,available_modes:d,availableModels:c,availableModes:d,models:a,modes:o};return r.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(c.map(l=>l.id))} currentModelId=${n}`),u}get pendingApprovalEntries(){return this.pendingApprovals}get hasSessionBinding(){return this.bindingStore!==null}setMode(e){return this.acpClient?this.acpClient.setLiveMode(e):Promise.resolve(!1)}setModel(e){return this.acpClient?this.acpClient.setModel(e):Promise.resolve(!1)}handleAcpApprovalAction(e,t){const i=this.pendingApprovals.get(e);return i?(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.resetIdleTimer(this.activeRun),!0):!1}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.resetIdleTimer(this.activeRun)}async handleLocalAction(e){const t=e.action_type??"",i=e.params??{};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:""}}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 f.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: ${g(s)}`),this.callbacks.sendUpdateBindingCard(e,"failed",t),!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=S({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)}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)){this.deferredEvents.delete(e),this.cancelDeferredTimer(e),r.info("acp-adapter",`Replaying ${t.length} deferred events for session ${e}`);for(const{event:i}of t){if(this.activeRun){r.info("acp-adapter",`Cannot replay ${i.event_id}: agent busy, dropping`);continue}this.startRun(i,!1)}}}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),r.info("acp-adapter",`Compaction finished (${e}); replaying deferred events`);for(const i of[...this.deferredEvents.keys()])this.replayDeferredEvents(i);const t=this.compactionDoneResolver;this.compactionDoneResolver=null,t?.()}announceDeferredComposing(e){}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.callbacks.sendEventResult(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.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){this.callbacks.sendEventResult(e.event_id,"failed","agent not alive");return}e.session_id&&e.session_id!==this.currentAibotSessionId&&(this.currentAibotSessionId=e.session_id),this.startRun(e,!1)}deliverStopEvent(e,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.callbacks.sendEventResult(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.callbacks.sendEventResult(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 $,markdownSegmenter:new E,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:t,lastProgressAt:Date.now(),lastIdleCheckAt:Date.now(),idleNoProgressCount:0};const s=this.activeRun,n=M(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=P(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),a=this.injectRecoveryContext(s.sessionId,o);this.sendPromptWithRetry(s,a)}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})`),`${i}
1
+ import{fileURLToPath as A}from"node:url";import m from"node:path";import{homedir as v}from"node:os";import{promises as f}from"node:fs";import{EventEmitter as C}from"node:events";import{spawn as R}from"node:child_process";import{AgentProcess as y}from"../../agent/process.js";import{AcpClient as w,AcpAuthRequiredError as T,isAuthRequiredError as k}from"../../protocol/acp-client.js";import{AgentEventType as p}from"../../types/events.js";import{InternalApiServer as _}from"../../core/mcp/internal-api-server.js";import{EventResultsStore as $}from"../../core/persistence/event-results-store.js";import{QuotedMessageStream as E}from"../../core/util/quoted-message-stream.js";import{SafeMarkdownStreamSegmenter as P}from"../../core/text-segmentation/index.js";import{extractAcpTurnInput as M}from"../../core/protocol/payload-parser.js";import{injectMessageMetadata as x}from"../../core/protocol/message-metadata.js";import{log as r}from"../../core/log/index.js";import{scanSkills as S}from"../claude/skill-scanner.js";import{resolveCliPath as N,getCliVersion as q}from"../../core/util/cli-probe.js";const I=m.dirname(A(import.meta.url)),D=200,j=60*1e3,B=600*1e3,b=80;function L(u){return!(!(u instanceof Error)||u.code!==-32603||k(u))}function g(u){if(!(u instanceof Error))return String(u);let e=u.message;const t=u.data;return t&&(e+=` data=${JSON.stringify(t)}`),e}async function F(u,e){if(!u)return!1;const t=m.join(v(),".kiro","sessions","cli",`${u}.jsonl`);try{return(await f.stat(t)).mtimeMs>e}catch{return!1}}function O(u){return u.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class oe extends C{type="acp";config;callbacks;agentProcess=null;acpClient=null;internalApi=null;activeRun=null;compacting=!1;compactingTimer=null;compactionDoneResolver=null;pendingAutoCompact=!1;eventResults=null;pendingApprovals=new Map;clientMsgSeq=0;stopped=!1;acpAuthMethod;acpInitialMode;acpInitialModel;acpMcpTools;rawTransport;approvalMode;autoInjectArgs;bindingStore;sessionBindings=new Map;deferredEvents=new Map;currentAibotSessionId;bridgeLog;sessionConnected=!1;rawEventSeq=0;recoveryContextBySessionId=new Map;constructor(e,t,i){if(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.bindingStore=i?.bindingStore??null,this.currentAibotSessionId=i?.aibotSessionId?String(i.aibotSessionId).trim():void 0,i?.eventResultsPath&&(this.eventResults=new $(i.eventResultsPath)),this.bindingStore&&this.currentAibotSessionId){const s=this.bindingStore.get(this.currentAibotSessionId);s?.cwd&&this.sessionBindings.set(this.currentAibotSessionId,s.cwd)}}async start(){if(await this.spawnProcess(),!this.bindingStore){await this.connectSession(this.resolveCwd());return}const e=this.currentAibotSessionId?this.sessionBindings.get(this.currentAibotSessionId):void 0;e&&await this.connectSession(e)}async stop(){this.stopped=!0,this.rejectDeferredEvents("adapter stopped"),this.cancelAllDeferredTimers(),this.pendingAutoCompact=!1,this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null),this.compactionDoneResolver&&(this.compactionDoneResolver(),this.compactionDoneResolver=null),this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null),this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(await this.agentProcess.close(),this.agentProcess=null),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null)}isAlive(){return this.agentProcess?.alive??!1}async createSession(e){return this.acpClient?.sessionId??""}async resumeSession(e,t){}async destroySession(e){}sendPrompt(e){const t=new U(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"))}setPermissionHandler(e){}async ping(e){return this.acpClient?.isAlive?this.acpClient.ping(e):this.agentProcess?.alive??!1}getStatus(){return{alive:this.agentProcess?.alive??!1,busy:this.activeRun!==null||this.compacting,sessions:this.sessionConnected?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.activeRun&&(this.activeRun.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null),this.activeRun.idleTimer&&(clearTimeout(this.activeRun.idleTimer),this.activeRun.idleTimer=null),this.activeRun=null)}getMcpConfig(){if(!this.internalApi)return null;const e=m.resolve(I,"../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url]}}async probe(e){const t=this.config.command||"gemini",i=await N(t),s=i!==null;let n=null,a;if(s){const h=await q(t);n=h.version,h.error&&(a=h.error)}else a={code:"cli_not_found",message:`command not found: ${t}`};const o=this.acpClient?.model??null,c=this.getStatus();let d;return e?.conversation?this.acpClient?.isAlive?d={attempted:!0,ok:!0,latency_ms:null}:d={attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"ACP session not connected, cannot probe conversation"}}:d={attempted:!1,ok:!1,latency_ms:null},{cli:{command:t,installed:s,path:i,version:n,...a?{error:a}:{}},conversation:d,config:{model:o,base_url:null,source:{model:o?"runtime":"unknown",base_url:"unknown"}},process:{started:!!this.agentProcess,alive:c.alive,busy:c.busy}}}getSupportedCommands(){return[{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){try{switch(e){case"model":{const s=t.trim();if(s)return await this.setModel(s)?{status:"ok",message:`Model set to ${s}`}:{status:"failed",message:`Failed to set model: ${s}`};const n=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${n.currentModelId||"unknown"}`,data:n}}case"mode":{const s=t.trim();if(s)return await this.setMode(s)?{status:"ok",message:`Mode set to ${s}`}:{status:"failed",message:`Failed to set mode: ${s}`};const n=this.buildToolbarContext("mode_list");return{status:"ok",message:`Current: ${n.currentModeId||"unknown"}`,data:n}}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 s=new Promise(n=>{this.compactionDoneResolver=n});try{await this.acpClient.send("/compact")}catch(n){throw this.finishCompaction("send-failed"),n}return this.compactingTimer=setTimeout(()=>this.finishCompaction("timeout"),15e3),this.compactingTimer.unref?.(),await s,{status:"ok",message:"Compacted"}}case"status":{const s=this.getStatus(),n=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${s.alive}, Busy: ${s.busy}, Model: ${n?.currentModelId??"unknown"}, Mode: ${n?.currentModeId??"unknown"}`,data:{alive:s.alive,busy:s.busy,sessions:s.sessions,model:n?.currentModelId??"",mode:n?.currentModeId??""}}}case"skills":{const s=S({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),n=s.map(a=>`- ${a.name}${a.trigger?` (${a.trigger})`:""} [${a.source}]: ${a.description}`);return{status:"ok",message:n.length>0?n.join(`
2
+ `):"No skills found",data:s}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(s){return{status:"failed",message:s instanceof Error?s.message:String(s)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}buildToolbarContext(e,t){const i=this.acpClient?.sessionOptions,s=i?.currentModeId??"",n=i?.currentModelId??"",a=i?.modes.map(l=>({id:l.id,name:l.name}))??[],o=i?.models.map(l=>({modelId:l.modelId,name:l.name}))??[],c=i?.models.map(l=>({id:l.modelId,displayName:l.name}))??[],d=i?.modes.map(l=>({id:l.id,displayName:l.name}))??[],h={outcome:e,...t?{cwd:t}:{},model_id:n,mode_id:s,currentModelId:n,currentModeId:s,available_models:c,available_modes:d,availableModels:c,availableModes:d,models:o,modes:a};return r.info("acp-adapter",`[toolbar] buildToolbarContext outcome=${e} model_id="${n}" available_models=${JSON.stringify(c.map(l=>l.id))} currentModelId=${n}`),h}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?(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.resetIdleTimer(this.activeRun),!0):!1}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.resetIdleTimer(this.activeRun)}async handleLocalAction(e){const t=e.action_type??"",i=e.params??{};if(t==="exec_approve"||t==="exec_reject"||t==="permission_approve"||t==="permission_reject"){const s=String(i.tool_call_id??i.approval_command_id??i.approval_id??i.exec_context_id??""),n=t==="exec_approve"||t==="permission_approve",a=String(i.decision??"");let o;return n&&(a==="allow-once"||a==="allow-always")?o=a:n?o="allow":o="deny",s?this.handleAcpApprovalAction(s,o)?(this.callbacks.sendLocalActionResult(e.action_id,"ok"),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"approval_not_found",`no pending approval for tool_call_id: ${s}`),{handled:!0,kind:"approval"}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"tool_call_id_required","tool_call_id is required"),{handled:!0,kind:"approval"})}return{handled:!1,kind:""}}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 f.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: ${g(s)}`),this.callbacks.sendUpdateBindingCard(e,"failed",t),!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=S({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)}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),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){}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.callbacks.sendEventResult(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.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}if(!this.agentProcess?.alive){this.callbacks.sendEventResult(e.event_id,"failed","agent not alive");return}e.session_id&&e.session_id!==this.currentAibotSessionId&&(this.currentAibotSessionId=e.session_id),this.startRun(e,!1)}deliverStopEvent(e,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.callbacks.sendEventResult(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.callbacks.sendEventResult(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 E,markdownSegmenter:new P,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:t,lastProgressAt:Date.now(),lastIdleCheckAt:Date.now(),idleNoProgressCount:0};const s=this.activeRun,n=M(e,this.resolveCwd());this.resetIdleTimer(s),n.modeId&&this.acpClient&&this.acpClient.setLiveMode(n.modeId).catch(()=>{}),n.modelId&&this.acpClient&&this.acpClient.setModel(n.modelId).catch(()=>{});const a=x(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),o=this.injectRecoveryContext(s.sessionId,a);this.sendPromptWithRetry(s,o)}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})`),`${i}
3
3
 
4
4
  [\u5F53\u524D\u7528\u6237\u6D88\u606F]
5
- ${t}`):t}sendPromptWithRetry(e,t){this.acpClient.send(t).catch(i=>{if(r.error("acp-adapter",`Prompt failed: ${g(i)}`),B(i)){const n=i instanceof Error?i.message:String(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)})}silentlyDiscardActiveRun(e){this.activeRun===e&&(this.activeRun=null,this.acpClient?.clearSettleTimer(),e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),this.emit("eventDone",e.eventId))}async spawnProcess(){const e=[...this.config.args??[]];this.autoInjectArgs?.acp&&e.push("--acp"),this.autoInjectArgs?.model&&e.push("--model",this.autoInjectArgs.model),this.config.command==="gemini"&&!e.includes("--skip-trust")&&e.push("--skip-trust"),this.config.command==="kiro-cli"&&!e.includes("--trust-all-tools")&&e.push("--trust-all-tools"),(this.config.command==="copilot"||this.config.command.endsWith("/copilot")||this.config.command==="gh"&&e.includes("copilot"))&&(e.includes("--allow-all-tools")||e.push("--allow-all-tools"),e.includes("--allow-all-paths")||e.push("--allow-all-paths")),this.agentProcess=new R;const i=this.resolveCwd();try{if(!(await f.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}try{await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:i,env:this.config.env})}catch(s){throw r.error("acp-adapter",`Failed to spawn agent process: ${s}`),this.rejectDeferredEvents(`agent spawn failed: ${s instanceof Error?s.message:String(s)}`),this.emit("exit",null),s}r.info("acp-adapter","ACP agent process started"),this.agentProcess.on("error",s=>{this.stopped||(r.error("acp-adapter",`Agent process error: ${s.message}`),this.activeRun&&this.finishRun("failed",`agent process error: ${s.message}`),this.rejectDeferredEvents(`agent process error: ${s.message}`),this.emit("exit",null))}),this.agentProcess.on("exit",s=>{this.stopped||(r.error("acp-adapter",`Agent process exited unexpectedly (code=${s})`),this.activeRun&&this.finishRun("failed",`agent process exited (code=${s})`),this.rejectDeferredEvents(`agent process exited (code=${s})`),this.emit("exit",s))}),this.agentProcess.on("stderr",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)})}async connectSession(e){if(this.sessionConnected||!this.agentProcess?.transport)return;let t;this.acpMcpTools&&(t=await this.startInternalApiAndMcp()),this.acpClient=new w,this.acpClient.on("event",a=>this.handleAcpEvent(a)),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.emit("exit",null))});const i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=async a=>{await this.acpClient.connect({transport:this.agentProcess.transport,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:e||this.resolveCwd(),mcpServers:t,sessionId:a})};let n=!1,o;try{await s(i)}catch(a){if(a instanceof T){await this.handleAuthRequired(a);return}if(!i)throw this.handleConnectFailure(a),a;if(r.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${g(a)}`),n=!0,o=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${i}`);const c=await this.buildKiroRecoveryContext(this.currentAibotSessionId,i,e||this.resolveCwd());c?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,c),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${c.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${i}`)}await s(void 0)}this.sessionConnected=!0,r.info("acp-adapter",`ACP session ready: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId),this.currentAibotSessionId&&this.bindingStore&&this.bindingStore.setAcpSessionId(this.currentAibotSessionId,this.acpClient.sessionId);for(const[a,c]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(a,this.acpClient.sessionId);const d=n?"binding_recreated":"binding_ready",u=this.buildToolbarContext(d,c);n&&o&&(u.previous_session_id=o,u.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(a,"ready",c,u)}}async buildKiroRecoveryContext(e,t,i){try{const s=p.join(v(),".kiro","sessions","cli",`${t}.json`);await f.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 f.mkdir(o,{recursive:!0});const a=p.join(o,`${e}.md`),c=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
6
- `);return await f.writeFile(a,c,"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(`
5
+ ${t}`):t}sendPromptWithRetry(e,t){this.acpClient.send(t).catch(i=>{if(r.error("acp-adapter",`Prompt failed: ${g(i)}`),L(i)){const n=g(i),a=e.eventId,o=e.sessionId;r.info("acp-adapter",`Internal error escalated to bridge: event=${a} session=${o} err=${n}`),this.silentlyDiscardActiveRun(e),this.emit("internalError",{eventId:a,sessionId:o,errorMsg:n});return}const s=i instanceof Error?i.message:String(i);this.finishRun("failed",s)})}silentlyDiscardActiveRun(e){this.activeRun===e&&(this.activeRun=null,this.acpClient?.clearSettleTimer(),e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null),e.idleTimer&&(clearTimeout(e.idleTimer),e.idleTimer=null),this.emit("eventDone",e.eventId))}async spawnProcess(){const e=[...this.config.args??[]];this.autoInjectArgs?.acp&&e.push("--acp"),this.autoInjectArgs?.model&&e.push("--model",this.autoInjectArgs.model),this.config.command==="gemini"&&!e.includes("--skip-trust")&&e.push("--skip-trust"),this.config.command==="kiro-cli"&&!e.includes("--trust-all-tools")&&e.push("--trust-all-tools"),(this.config.command==="copilot"||this.config.command.endsWith("/copilot")||this.config.command==="gh"&&e.includes("copilot"))&&(e.includes("--allow-all-tools")||e.push("--allow-all-tools"),e.includes("--allow-all-paths")||e.push("--allow-all-paths")),this.agentProcess=new y(this.bridgeLog);const i=this.resolveCwd();try{if(!(await f.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}try{await this.agentProcess.start({command:this.config.command,args:e.length>0?e:void 0,cwd:i,env:this.config.env})}catch(s){throw r.error("acp-adapter",`Failed to spawn agent process: ${s}`),this.rejectDeferredEvents(`agent spawn failed: ${s instanceof Error?s.message:String(s)}`),this.emit("exit",null),s}r.info("acp-adapter","ACP agent process started"),this.agentProcess.on("error",s=>{this.stopped||(r.error("acp-adapter",`Agent process error: ${s.message}`),this.activeRun&&this.finishRun("failed",`agent process error: ${s.message}`),this.rejectDeferredEvents(`agent process error: ${s.message}`),this.emit("exit",null))}),this.agentProcess.on("exit",s=>{this.stopped||(r.error("acp-adapter",`Agent process exited unexpectedly (code=${s})`),this.activeRun&&this.finishRun("failed",`agent process exited (code=${s})`),this.rejectDeferredEvents(`agent process exited (code=${s})`),this.emit("exit",s))}),this.agentProcess.on("stderr",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)})}async connectSession(e){if(this.sessionConnected||!this.agentProcess?.transport)return;let t;this.acpMcpTools&&(t=await this.startInternalApiAndMcp()),this.acpClient=new w,this.acpClient.on("event",o=>this.handleAcpEvent(o)),this.acpClient.on("activity",()=>{this.activeRun&&this.resetIdleTimer(this.activeRun)}),this.acpClient.on("session-lost",()=>{this.stopped||this.agentProcess?.alive&&(r.error("acp-adapter","ACP transport closed while agent process still alive, triggering respawn"),this.activeRun&&(this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,"ACP transport closed"),this.finishRun("failed","ACP transport closed")),this.emit("exit",null))});const i=this.currentAibotSessionId&&this.bindingStore?this.bindingStore.getAcpSessionId(this.currentAibotSessionId):void 0,s=async o=>{await this.acpClient.connect({transport:this.agentProcess.transport,authMethod:this.acpAuthMethod,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:e||this.resolveCwd(),mcpServers:t,sessionId:o})};let n=!1,a;try{await s(i)}catch(o){if(o instanceof T){await this.handleAuthRequired(o);return}if(!i)throw this.handleConnectFailure(o),o;if(r.warn("acp-adapter",`Failed to load persisted session ${i}, fallback to session/new: ${g(o)}`),n=!0,a=i,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${i}`);const c=await this.buildKiroRecoveryContext(this.currentAibotSessionId,i,e||this.resolveCwd());c?(this.recoveryContextBySessionId.set(this.currentAibotSessionId,c),r.info("acp-adapter",`Recovery context prepared for session ${this.currentAibotSessionId} (chars=${c.length})`)):r.warn("acp-adapter",`Recovery context skipped: no summary generated for acpSession=${i}`)}await s(void 0)}this.sessionConnected=!0,r.info("acp-adapter",`ACP session ready: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId),this.currentAibotSessionId&&this.bindingStore&&this.bindingStore.setAcpSessionId(this.currentAibotSessionId,this.acpClient.sessionId);for(const[o,c]of this.sessionBindings){this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(o,this.acpClient.sessionId);const d=n?"binding_recreated":"binding_ready",h=this.buildToolbarContext(d,c);n&&a&&(h.previous_session_id=a,h.recreated_session_id=this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(o,"ready",c,h)}}async buildKiroRecoveryContext(e,t,i){try{const s=m.join(v(),".kiro","sessions","cli",`${t}.json`);await f.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,i);if(!n)return;const a=m.join(v(),".grix","data","session-summaries");await f.mkdir(a,{recursive:!0});const o=m.join(a,`${e}.md`),c=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
6
+ `);return await f.writeFile(o,c,"utf8"),r.info("acp-adapter",`Recovery summary saved: ${o} (chars=${n.length})`),["[\u5386\u53F2\u4E0A\u4E0B\u6587\u56DE\u704C\u8BF4\u660E]","\u4EE5\u4E0B\u5185\u5BB9\u6765\u81EA\u65E7\u4F1A\u8BDD\u81EA\u52A8\u6458\u8981\uFF0C\u8BF7\u5728\u540E\u7EED\u56DE\u7B54\u4E2D\u5EF6\u7EED\u5176\u4E2D\u7684\u76EE\u6807\u3001\u7EA6\u675F\u548C\u7ED3\u8BBA\u3002",`SOURCE_KIRO_SESSION_FILE: ${s}`,`SUMMARY_FILE: ${o}`,"",n.trim()].join(`
7
7
  `)}catch(s){r.warn("acp-adapter",`Failed to build kiro recovery context: ${g(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(`
8
- `)],n=await this.runCommandCapture("kiro-cli",s,t,45e3);if(!n)return;const o=U(n).trim(),a=o.indexOf("> "),c=a>=0?o.slice(a+2).trim():o;if(c)return r.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${o.length}, body_chars=${c.length})`),c.split(`
8
+ `)],n=await this.runCommandCapture("kiro-cli",s,t,45e3);if(!n)return;const a=O(n).trim(),o=a.indexOf("> "),c=o>=0?a.slice(o+2).trim():a;if(c)return r.info("acp-adapter",`Kiro summary generated from ${e} (raw_chars=${a.length}, body_chars=${c.length})`),c.split(`
9
9
  `).filter(d=>!d.includes("Credits:")&&!d.includes("Time:")).join(`
10
- `).trim()}runCommandCapture(e,t,i,s){return new Promise((n,o)=>{const a=A(e,t,{cwd:i,stdio:["ignore","pipe","pipe"]});let c="",d="";const u=setTimeout(()=>{a.kill("SIGTERM"),o(new Error(`${e} timed out after ${s}ms`))},s);a.stdout.on("data",l=>{c+=String(l)}),a.stderr.on("data",l=>{d+=String(l)}),a.on("error",l=>{clearTimeout(u),o(l)}),a.on("close",l=>{if(clearTimeout(u),l===0){n(c||d);return}o(new Error(`${e} exited with code ${l}: ${d||c}`))})})}handleConnectFailure(e){r.error("acp-adapter",`ACP session creation failed: ${g(e)}`),this.acpClient?.removeAllListeners(),this.acpClient=null,this.emit("exit",null)}async startInternalApiAndMcp(){try{this.internalApi||(this.internalApi=new k,this.internalApi.setInvokeHandler(async(t,i)=>this.callbacks.agentInvoke(t,i)),await this.internalApi.start(0),r.info("acp-adapter",`Internal API started at ${this.internalApi.url}`));const e=p.resolve(I,"../../mcp/acp-mcp-server.js");return[{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url],env:{GRIX_CONNECTOR_INTERNAL_API:this.internalApi.url}}]}catch(e){r.error("acp-adapter",`Failed to start MCP tools: ${e}`);return}}cleanup(){this.activeRun,this.pendingApprovals.clear(),this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null)}async handleAuthRequired(e){r.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(n=>n.id).join(", ")}`);const t=e.authMethods.find(n=>/oauth|browser/i.test(n.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[n,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(n,"failed",o);const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(i,`Please open this URL to authenticate:
10
+ `).trim()}runCommandCapture(e,t,i,s){return new Promise((n,a)=>{const o=R(e,t,{cwd:i,stdio:["ignore","pipe","pipe"]});let c="",d="";const h=setTimeout(()=>{o.kill("SIGTERM"),a(new Error(`${e} timed out after ${s}ms`))},s);o.stdout.on("data",l=>{c+=String(l)}),o.stderr.on("data",l=>{d+=String(l)}),o.on("error",l=>{clearTimeout(h),a(l)}),o.on("close",l=>{if(clearTimeout(h),l===0){n(c||d);return}a(new Error(`${e} exited with code ${l}: ${d||c}`))})})}handleConnectFailure(e){r.error("acp-adapter",`ACP session creation failed: ${g(e)}`),this.acpClient?.removeAllListeners(),this.acpClient=null,this.emit("exit",null)}async startInternalApiAndMcp(){try{this.internalApi||(this.internalApi=new _,this.internalApi.setInvokeHandler(async(t,i)=>this.callbacks.agentInvoke(t,i)),this.internalApi.setMcpRelayHandler(async t=>this.relayMcpFrame(t)),await this.internalApi.start(0),r.info("acp-adapter",`Internal API started at ${this.internalApi.url}`));const e=m.resolve(I,"../../mcp/mcp-bridge-server.js");return[{name:"grix-app-bridge",command:process.execPath,args:[e,"--api-url",this.internalApi.url],env:{GRIX_CONNECTOR_INTERNAL_API:this.internalApi.url}}]}catch(e){r.error("acp-adapter",`Failed to start MCP tools: ${e}`);return}}async relayMcpFrame(e){if(this.callbacks.sendMcpFrame)return this.callbacks.sendMcpFrame(e);throw new Error("sendMcpFrame callback not available")}cleanup(){this.activeRun,this.pendingApprovals.clear(),this.pendingAutoCompact=!1,this.rejectDeferredEvents("adapter cleaned up"),this.cancelAllDeferredTimers(),this.sessionConnected=!1,this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.removeAllListeners(),this.agentProcess=null)}async handleAuthRequired(e){r.info("acp-adapter",`Auth required, methods: ${e.authMethods.map(n=>n.id).join(", ")}`);const t=e.authMethods.find(n=>/oauth|browser/i.test(n.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[n,a]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(n,"failed",a);const s=await this.captureAuthUrl();s&&this.callbacks.sendAuthNotification(i,`Please open this URL to authenticate:
11
11
  ${s}
12
12
 
13
- 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 n=this.internalApi?[{name:"grix-connector-tools",command:process.execPath,args:[p.resolve(I,"../../mcp/acp-mcp-server.js"),"--api-url",this.internalApi.url],env:{GRIX_CONNECTOR_INTERNAL_API:this.internalApi.url}}]:void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:this.resolveCwd(),mcpServers:n}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[o,a]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(o,"ready",a,this.buildToolbarContext("binding_ready",a))}catch(n){throw r.error("acp-adapter",`Auth retry failed: ${n}`),n}}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){if(e.type===m.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===m.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting&&this.finishCompaction("context-window-update");return}const t=this.activeRun;if(!t)return;t.responded||(t.responded=!0);let i=!1;switch(e.type){case m.Text:{if(e.content){i=!0;const s=t.quotedStream.consume(e.content);s.quotedMessageId&&(t.quotedMessageId=s.quotedMessageId),s.deltaContent&&this.appendToStream(t,s.deltaContent)}break}case m.ToolUse:{i=!0,t.awaitingToolResult=!0,e.toolName&&(this.emitRawEventEnvelope(t,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:e.toolInput??""}})||this.callbacks.sendToolUse(t.eventId,t.sessionId,e.toolName,e.toolInput??""));break}case m.ToolResult:{t.awaitingToolResult=!1,e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,e.toolName??"",e.content));break}case m.Thinking:{e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(t.eventId,t.sessionId,e.content));break}case m.Error:{this.emitRawEventEnvelope(t,{type:"error",payload:{message:String(e.error??"unknown error")}}),r.error("acp-adapter",`ACP error: ${e.error}`);break}case m.Result:{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(!t||!e.requestId||!this.acpClient)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 i=t.toolCallId;this.pendingApprovals.set(i,e.requestId);const s=this.activeRun;s?(s.idleTimer&&(clearTimeout(s.idleTimer),s.idleTimer=null),this.emitRawEventEnvelope(s,{type:"permission_request",payload:{request_id:t.requestId,tool_call_id:i,tool_name:t.toolName,tool_title:t.toolTitle,options:t.options}})||this.callbacks.sendPermissionCard({eventId:s.eventId,sessionId:s.sessionId,toolCallId:i,toolName:t.toolName,toolTitle:t.toolTitle,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(i))}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){e.idleTimer&&clearTimeout(e.idleTimer);const t=e.awaitingToolResult?j:N;e.idleTimer=setTimeout(()=>{if(this.activeRun?.eventId!==e.eventId)return;const i=this.acpClient;i?.isAlive?i.ping(5e3).then(async s=>{if(this.activeRun?.eventId===e.eventId)if(s){const n=e.lastIdleCheckAt,o=e.lastProgressAt>n,a=o?!1:await L(i.sessionId,n),c=o||a;e.lastIdleCheckAt=Date.now(),c?(a&&r.info("acp-adapter",`Idle timer: no stream progress but kiro session active, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,e.idleNoProgressCount>=2?(r.error("acp-adapter",`Agent alive but no progress for ${e.idleNoProgressCount} idle cycles, declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent alive but no progress for ${e.idleNoProgressCount} idle cycles`),this.emit("stuck")):(r.warn("acp-adapter",`Idle timer fired, ping ok, no progress (${e.idleNoProgressCount}/2): ${e.eventId}`),this.resetIdleTimer(e)))}else r.error("acp-adapter",`Agent idle for ${t/1e3}s and ping failed, declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent idle for ${t/1e3}s`),this.emit("stuck")}).catch(()=>{this.activeRun?.eventId===e.eventId&&(r.error("acp-adapter",`Agent idle for ${t/1e3}s and ping error, declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent idle for ${t/1e3}s`),this.emit("stuck"))}):(r.error("acp-adapter",`Agent idle for ${t/1e3}s (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent idle for ${t/1e3}s`),this.emit("stuck"))},t)}appendToStream(e,t){e.buffer+=t,e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushStream(),D))}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),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,r.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const t=e.buffer;e.buffer="",this.emitSegmentedStream(e,t)}finishRun(e,t){const i=this.activeRun;if(!i)return;this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",i.eventId),i.flushTimer&&(clearTimeout(i.flushTimer),i.flushTimer=null),i.idleTimer&&(clearTimeout(i.idleTimer),i.idleTimer=null);const s=i.quotedStream.flush();s.deltaContent&&(i.buffer+=s.deltaContent),s.quotedMessageId&&(i.quotedMessageId=s.quotedMessageId),i.buffer&&(this.emitSegmentedStream(i,i.buffer),i.buffer=""),t&&this.callbacks.sendRunError(i.eventId,i.sessionId,t);const n=++i.chunkSeq;this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(i.eventId,i.sessionId,n,i.currentClientMsgId).then(()=>{i.silent||this.callbacks.sendEventResult(i.eventId,e,t),this.persistEventResult(i,e,t)}).catch(o=>{r.error("acp-adapter",`finalStreamChunk ACK failed event=${i.eventId}: ${o}`),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.callbacks.sendStreamChunk(i.eventId,i.sessionId,"",n,!0,i.currentClientMsgId),i.silent||this.callbacks.sendEventResult(i.eventId,e,t),this.persistEventResult(i,e,t))}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 O extends C{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){r.warn("acp-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{oe as AcpAdapter};
13
+ 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 n=this.internalApi?[{name:"grix-connector-tools",command:process.execPath,args:[m.resolve(I,"../../mcp/acp-mcp-server.js"),"--api-url",this.internalApi.url],env:{GRIX_CONNECTOR_INTERNAL_API:this.internalApi.url}}]:void 0;await this.acpClient.connect({transport:this.agentProcess.transport,initialMode:this.acpInitialMode,initialModel:this.acpInitialModel,cwd:this.resolveCwd(),mcpServers:n}),r.info("acp-adapter",`ACP session ready after auth: ${this.acpClient.sessionId}`),this.emit("acpSessionReady",this.acpClient.sessionId);for(const[a,o]of this.sessionBindings)this.callbacks.sendUpdateBindingCard(a,"ready",o,this.buildToolbarContext("binding_ready",o))}catch(n){throw r.error("acp-adapter",`Auth retry failed: ${n}`),n}}captureAuthUrl(){return new Promise(e=>{if(!this.agentProcess){e(null);return}const t=/https?:\/\/[^\s"')\]]+/;let i=!1;const s=n=>{if(i)return;const o=n.toString().replace(/\x1b\[[0-9;]*m/g,"").match(t);o&&(i=!0,this.agentProcess.removeListener("stderr",s),e(o[0]))};this.agentProcess.on("stderr",s),setTimeout(()=>{i||(i=!0,this.agentProcess.removeListener("stderr",s),e(null))},3e4)})}handleAcpEvent(e){if(e.type===p.PermissionRequest){this.activeRun&&this.resetIdleTimer(this.activeRun),this.handlePermissionRequest(e);return}if(e.type===p.ContextWindowUpdate){e.contextWindow&&this.callbacks.onContextWindowUpdated?.(e.contextWindow),this.compacting?this.finishCompaction("context-window-update"):e.contextWindow&&this.maybeScheduleAutoCompact(e.contextWindow);return}const t=this.activeRun;if(!t)return;t.responded||(t.responded=!0);let i=!1;switch(e.type){case p.Text:{if(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 p.ToolUse:{i=!0,t.awaitingToolResult=!0,e.toolName&&(this.emitRawEventEnvelope(t,{type:"tool_use",payload:{tool_name:e.toolName,tool_input:e.toolInput??""}})||this.callbacks.sendToolUse(t.eventId,t.sessionId,e.toolName,e.toolInput??""));break}case p.ToolResult:{t.awaitingToolResult=!1,e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"tool_result",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,e.toolName??"",e.content));break}case p.ToolProgress:{i=!0,e.content&&(this.emitRawEventEnvelope(t,{type:"tool_progress",payload:{tool_name:e.toolName??"",content:e.content}})||this.callbacks.sendToolResult(t.eventId,t.sessionId,e.toolName??"",e.content));break}case p.Thinking:{e.content&&(i=!0,this.emitRawEventEnvelope(t,{type:"thinking",payload:{content:e.content}})||this.callbacks.sendThinking(t.eventId,t.sessionId,e.content));break}case p.Error:{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 p.Result:{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(!t||!e.requestId||!this.acpClient)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 i=t.toolCallId;this.pendingApprovals.set(i,e.requestId);const s=this.activeRun;s?(s.idleTimer&&(clearTimeout(s.idleTimer),s.idleTimer=null),this.emitRawEventEnvelope(s,{type:"permission_request",payload:{request_id:t.requestId,tool_call_id:i,tool_name:t.toolName,tool_title:t.toolTitle,options:t.options}})||this.callbacks.sendPermissionCard({eventId:s.eventId,sessionId:s.sessionId,toolCallId:i,toolName:t.toolName,toolTitle:t.toolTitle,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(i))}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){e.idleTimer&&clearTimeout(e.idleTimer);const t=e.awaitingToolResult?B:j;e.idleTimer=setTimeout(()=>{if(this.activeRun?.eventId!==e.eventId)return;const i=this.acpClient;i?.isAlive?i.ping(5e3).then(async s=>{if(this.activeRun?.eventId!==e.eventId)return;if(!s){r.error("acp-adapter",`Ping failed, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck();return}if(e.awaitingToolResult){r.info("acp-adapter",`Idle timer: tool running, ping ok, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e);return}const n=e.lastIdleCheckAt,a=e.lastProgressAt>n,o=a?!1:await F(i.sessionId,n),c=a||o;e.lastIdleCheckAt=Date.now(),c?(o&&r.info("acp-adapter",`Idle timer: no stream progress but kiro session active, continuing: ${e.eventId}`),e.idleNoProgressCount=0,this.resetIdleTimer(e)):(e.idleNoProgressCount++,e.idleNoProgressCount>=2?(r.error("acp-adapter",`Agent alive but no progress for ${e.idleNoProgressCount} idle cycles, declaring stuck: ${e.eventId}`),this.finishRun("failed",`agent alive but no progress for ${e.idleNoProgressCount} idle cycles`),this.declareStuck()):(r.warn("acp-adapter",`Idle timer fired, ping ok, no progress (${e.idleNoProgressCount}/2): ${e.eventId}`),this.resetIdleTimer(e)))}).catch(()=>{this.activeRun?.eventId===e.eventId&&(r.error("acp-adapter",`Ping error, declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())}):(r.error("acp-adapter",`Agent idle (no client), declaring stuck: ${e.eventId}`),this.finishRun("failed","agent unreachable"),this.declareStuck())},t)}declareStuck(){this.acpClient&&(this.acpClient.clearSettleTimer(),this.acpClient.removeAllListeners(),this.acpClient=null),this.agentProcess&&(this.agentProcess.close().catch(()=>{}),this.agentProcess=null),this.emit("stuck")}appendToStream(e,t){e.buffer+=t,e.flushTimer||(e.flushTimer=setTimeout(()=>this.flushStream(),D))}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),s.closeAfter&&(e.currentSegmentIndex+=1,e.currentClientMsgId=`${e.clientMsgIdBase}_seg_${e.currentSegmentIndex}`,e.chunkSeq=0,r.info("acp-adapter",`stream segment rollover event=${e.eventId} segment=${e.currentSegmentIndex} reason=${s.reason??"threshold"}`)))}flushStream(){const e=this.activeRun;if(!e||!e.buffer)return;e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);const t=e.buffer;e.buffer="",this.emitSegmentedStream(e,t)}finishRun(e,t){const i=this.activeRun;if(!i)return;this.activeRun=null,this.acpClient?.clearSettleTimer(),this.emit("eventDone",i.eventId),i.flushTimer&&(clearTimeout(i.flushTimer),i.flushTimer=null),i.idleTimer&&(clearTimeout(i.idleTimer),i.idleTimer=null);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+=`
14
+
15
+ \u4EFB\u52A1\u6267\u884C\u4E2D\u65AD\uFF0C\u8BF7\u91CD\u65B0\u53D1\u9001\u6307\u4EE4\u3002`):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,n,i.currentClientMsgId).then(()=>{i.silent||this.callbacks.sendEventResult(i.eventId,e,t),this.persistEventResult(i,e,t),this.replayNextDeferredEvent(),this.tryRunPendingAutoCompact()}).catch(a=>{r.error("acp-adapter",`finalStreamChunk ACK failed event=${i.eventId}: ${a}`),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()}):(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())}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<b||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("acp-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${b}%, scheduling compact`),this.tryRunPendingAutoCompact())}tryRunPendingAutoCompact(){this.pendingAutoCompact&&(this.stopped||!this.acpClient?.isAlive||this.activeRun||this.compacting||(this.pendingAutoCompact=!1,this.execCommand("compact","",this.currentAibotSessionId??"").then(e=>{r.info("acp-adapter",`[auto-compact] compact done status=${e.status} msg=${e.message??""}`)}).catch(e=>{r.warn("acp-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:String(e)}`)})))}persistEventResult(e,t,i){this.eventResults&&!e.silent&&this.eventResults.set({sessionId:e.sessionId,eventId:e.eventId,status:t,msg:i,updatedAt:Date.now()})}}class U extends C{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){r.warn("acp-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}export{oe as AcpAdapter};
@@ -0,0 +1,2 @@
1
+ import{readFileSync as w,readdirSync as p,statSync as y}from"node:fs";import f from"node:path";import{homedir as h}from"node:os";import{log as T}from"../../core/log/index.js";import{readFirstLines as g}from"../../core/util/read-first-lines.js";const j=100;function m(s){const n=s.replace(/\[\[message_id:[^\]]+\]\]/g,"").replace(/\s+/g," ").trim();if(n)return n.length>j?n.slice(0,j)+"\u2026":n}function A(s){for(const n of s){if(!n||typeof n!="object")continue;const t=n;if(t.type!=="user")continue;const i=t.content;if(typeof i=="string")return m(i);if(Array.isArray(i)){for(const e of i)if(e&&typeof e=="object"&&"text"in e&&typeof e.text=="string")return m(e.text)}}}function M(s){for(const n of s)try{const t=JSON.parse(n);if(t.type!=="user")continue;const i=t.message?.parts;if(Array.isArray(i)){for(const e of i)if(e&&typeof e=="object"&&"text"in e&&typeof e.text=="string")return m(e.text)}}catch{}}function x(s){if(typeof s=="string")return s;if(Array.isArray(s))for(const n of s){if(!n||typeof n!="object")continue;const t=n;if(typeof t.text=="string")return t.text;if(typeof t.data=="string")return t.data}}function F(s){const n=g(s,131072);for(const t of n)try{const i=JSON.parse(t);if(i.kind!=="Prompt")continue;const e=x(i.data?.content);if(!e)continue;const o=m(e);if(o)return o}catch{}}function k(s){try{const n=y(s),t=JSON.parse(w(s,"utf8")),i=t.session_id;if(!i)return null;const e=t.updated_at?new Date(t.updated_at).getTime():n.mtimeMs,o=t.created_at?new Date(t.created_at).getTime():void 0;return{sessionId:i,cwd:t.cwd||"",title:typeof t.title=="string"?m(t.title):void 0,createdAt:Number.isFinite(o)?o:void 0,updatedAt:Number.isFinite(e)?e:n.mtimeMs,mtimeMs:n.mtimeMs}}catch{return null}}function S(s,n){try{const t=w(f.join(s,".gemini","history",n,".project_root"),"utf8").trim();return f.isAbsolute(t)?t:""}catch{return""}}function b(s){try{const n=y(s),i=g(s,32768).join(`
2
+ `),e=JSON.parse(i);if(!e.sessionId)return null;const o=A(e.messages||[]);return{sessionId:e.sessionId,startTime:e.startTime,title:o,mtimeMs:n.mtimeMs}}catch{return null}}function I(s){try{const n=y(s),t=g(s,16384);if(t.length===0)return null;const i=JSON.parse(t[0]),e=i.sessionId;if(!e)return null;let o;for(const c of t.slice(1))try{const u=JSON.parse(c);if(u.type==="user"){const a=u.content;if(typeof a=="string"){o=m(a);break}if(Array.isArray(a)){for(const r of a)if(r&&typeof r=="object"&&"text"in r&&typeof r.text=="string"){o=m(r.text);break}}if(o)break}}catch{}return{sessionId:e,startTime:i.startTime,title:o,mtimeMs:n.mtimeMs}}catch{return null}}function J(s){try{const n=y(s),t=g(s,16384);if(t.length===0)return null;const i=JSON.parse(t[0]),e=i.sessionId;if(!e)return null;const o=M(t);return{sessionId:e,cwd:i.cwd,title:o,mtimeMs:n.mtimeMs}}catch{return null}}function D(s=h()){const n=f.join(s,".gemini","tmp"),t=[];let i;try{i=p(n,{withFileTypes:!0})}catch{return t}for(const e of i){if(!e.isDirectory())continue;const o=f.join(n,e.name,"chats"),c=e.name,u=S(s,c);let a;try{a=p(o,{withFileTypes:!0})}catch{continue}for(const r of a){if(!r.isFile()||!r.name.startsWith("session-"))continue;const l=f.join(o,r.name);let d=null;r.name.endsWith(".json")?d=b(l):r.name.endsWith(".jsonl")&&(d=I(l)),d&&t.push({sessionId:d.sessionId,cwd:u,title:d.title,agentType:"gemini",createdAt:d.startTime?new Date(d.startTime).getTime():void 0,updatedAt:d.mtimeMs,filePath:l})}}return t}function N(s=h()){const n=f.join(s,".qwen","projects"),t=[];let i;try{i=p(n,{withFileTypes:!0})}catch{return t}for(const e of i){if(!e.isDirectory())continue;const o=f.join(n,e.name,"chats"),c=e.name;let u;try{u=p(o,{withFileTypes:!0})}catch{continue}for(const a of u){if(!a.isFile()||!a.name.endsWith(".jsonl"))continue;const r=f.join(o,a.name),l=J(r);l&&t.push({sessionId:l.sessionId,cwd:l.cwd||c,title:l.title,agentType:"qwen",updatedAt:l.mtimeMs,filePath:r})}}return t}function _(s=h()){const n=f.join(s,".kiro","sessions","cli"),t=[];let i;try{i=p(n,{withFileTypes:!0})}catch{return t}for(const e of i){if(!e.isFile()||!e.name.endsWith(".json"))continue;const o=f.join(n,e.name),c=k(o);if(!c)continue;const u=o.slice(0,-5)+".jsonl";let a=F(u)||c.title,r=Math.max(c.updatedAt,c.mtimeMs);try{r=Math.max(r,y(u).mtimeMs)}catch{}t.push({sessionId:c.sessionId,cwd:c.cwd,title:a,agentType:"kiro",createdAt:c.createdAt,updatedAt:r,filePath:o})}return t}function C(s=h()){const n=D(s),t=N(s),i=_(s),e=[...n,...t,...i];return e.sort((o,c)=>c.updatedAt-o.updatedAt),T.info("acp-session-scanner",`Scanned ${e.length} ACP sessions (gemini=${n.length}, qwen=${t.length}, kiro=${i.length})`),e}export{C as scanAcpSessions};
@@ -1 +1 @@
1
- import{createReadStream as T,readFileSync as y,readdirSync as w,existsSync as j}from"node:fs";import{createInterface as I}from"node:readline";import p from"node:path";import g from"node:os";import{log as f}from"../../core/log/index.js";function m(){return{inputTokens:0,outputTokens:0,cacheReadInputTokens:0,cacheCreationInputTokens:0}}function d(e,n){e.inputTokens+=n.inputTokens,e.outputTokens+=n.outputTokens,e.cacheReadInputTokens+=n.cacheReadInputTokens,e.cacheCreationInputTokens+=n.cacheCreationInputTokens}function C(e,n){const s=p.join(g.homedir(),".gemini","tmp"),r=p.basename(n),o=e.slice(0,8),u=p.join(s,r,"chats");try{const l=w(u);for(const c of[".jsonl",".json"]){const a=l.find(t=>t.startsWith("session-")&&t.includes(o)&&t.endsWith(c));if(a)return p.join(u,a)}}catch{}return null}function h(e){if(e.type!=="gemini"||!e.tokens)return null;const n=e.tokens;return{model:e.model??"unknown",usage:{inputTokens:n.input??0,outputTokens:n.output??0,cacheReadInputTokens:n.cached??0,cacheCreationInputTokens:0}}}function M(e,n){const s=p.join(g.homedir(),".qwen","projects"),r=p.join(s,n.replace(/[/\\]/g,"-")),o=p.join(r,"chats",`${e}.jsonl`);return j(o)?o:null}function S(e){if(e.type!=="assistant"||!e.usageMetadata)return null;const n=e.usageMetadata;return{model:e.model??"unknown",usage:{inputTokens:n.promptTokenCount??0,outputTokens:n.candidatesTokenCount??0,cacheReadInputTokens:n.cachedContentTokenCount??0,cacheCreationInputTokens:0}}}async function k(e,n){const s=new Map,r=m();let o=0;const u=I({input:T(e,"utf8"),crlfDelay:1/0});for await(const c of u)if(c.trim())try{const a=JSON.parse(c),t=n(a);if(!t)continue;o++,d(r,t.usage);let i=s.get(t.model);i||(i={turns:0,usage:m()},s.set(t.model,i)),i.turns++,d(i.usage,t.usage)}catch{}return o===0?null:{models:[...s.entries()].map(([c,a])=>({model:c,turns:a.turns,total:a.usage})),total:r,turns:o}}function $(e){const n=y(e,"utf8"),s=JSON.parse(n),r=Array.isArray(s)?s:s.messages??[],o=new Map,u=m();let l=0;for(const a of r){const t=h(a);if(!t)continue;l++,d(u,t.usage);let i=o.get(t.model);i||(i={turns:0,usage:m()},o.set(t.model,i)),i.turns++,d(i.usage,t.usage)}return l===0?null:{models:[...o.entries()].map(([a,t])=>({model:a,turns:t.turns,total:t.usage})),total:u,turns:l}}async function Q(e,n){const s=C(e,n);if(s){f.info("acp-usage-parser",`Parsing Gemini session from ${s}`);try{return s.endsWith(".jsonl")?await k(s,h):$(s)}catch(o){f.error("acp-usage-parser",`Gemini parse failed: ${o}`)}}const r=M(e,n);if(r){f.info("acp-usage-parser",`Parsing Qwen session from ${r}`);try{return await k(r,S)}catch(o){f.error("acp-usage-parser",`Qwen parse failed: ${o}`)}}return f.info("acp-usage-parser",`No session file found for ${e} in ${n}`),null}export{Q as parseAcpSessionUsage};
1
+ import{createReadStream as I,readFileSync as h,readdirSync as $,existsSync as T}from"node:fs";import{createInterface as C}from"node:readline";import p from"node:path";import g from"node:os";import{log as f}from"../../core/log/index.js";function m(){return{inputTokens:0,outputTokens:0,cacheReadInputTokens:0,cacheCreationInputTokens:0}}function d(n,e){n.inputTokens+=e.inputTokens,n.outputTokens+=e.outputTokens,n.cacheReadInputTokens+=e.cacheReadInputTokens,n.cacheCreationInputTokens+=e.cacheCreationInputTokens}function S(n,e){const i=p.join(g.homedir(),".gemini","tmp"),o=p.basename(e),s=n.slice(0,8),r=p.join(i,o,"chats");try{const l=$(r);for(const c of[".jsonl",".json"]){const a=l.find(t=>t.startsWith("session-")&&t.includes(s)&&t.endsWith(c));if(a)return p.join(r,a)}}catch{}return null}function y(n){if(n.type!=="gemini"||!n.tokens)return null;const e=n.tokens;return{model:n.model??"unknown",usage:{inputTokens:e.input??0,outputTokens:e.output??0,cacheReadInputTokens:e.cached??0,cacheCreationInputTokens:0}}}function F(n,e){const i=p.join(g.homedir(),".qwen","projects"),o=p.join(i,e.replace(/[/\\]/g,"-")),s=p.join(o,"chats",`${n}.jsonl`);return T(s)?s:null}function M(n){if(n.type!=="assistant"||!n.usageMetadata)return null;const e=n.usageMetadata;return{model:n.model??"unknown",usage:{inputTokens:e.promptTokenCount??0,outputTokens:e.candidatesTokenCount??0,cacheReadInputTokens:e.cachedContentTokenCount??0,cacheCreationInputTokens:0}}}async function w(n,e){const i=new Map,o=m();let s=0;const r=C({input:I(n,"utf8"),crlfDelay:1/0});for await(const c of r)if(c.trim())try{const a=JSON.parse(c),t=e(a);if(!t)continue;s++,d(o,t.usage);let u=i.get(t.model);u||(u={turns:0,usage:m()},i.set(t.model,u)),u.turns++,d(u.usage,t.usage)}catch{}return s===0?null:{models:[...i.entries()].map(([c,a])=>({model:c,turns:a.turns,total:a.usage})),total:o,turns:s}}function J(n){const e=h(n,"utf8"),i=JSON.parse(e),o=Array.isArray(i)?i:i.messages??[],s=new Map,r=m();let l=0;for(const a of o){const t=y(a);if(!t)continue;l++,d(r,t.usage);let u=s.get(t.model);u||(u={turns:0,usage:m()},s.set(t.model,u)),u.turns++,d(u.usage,t.usage)}return l===0?null:{models:[...s.entries()].map(([a,t])=>({model:a,turns:t.turns,total:t.usage})),total:r,turns:l}}function P(n){const e=p.join(g.homedir(),".kiro","sessions","cli",`${n}.json`);return T(e)?e:null}function R(n){const e=h(n,"utf8"),o=JSON.parse(e).session_state;if(!o)return null;const r=o.rts_model_state?.model_info?.model_id??"auto",c=o.conversation_metadata?.user_turn_metadatas??[];if(c.length===0)return null;const a=m();let t=0;for(const k of c){const j=k.input_token_count??0,_=k.output_token_count??0;t++,a.inputTokens+=j,a.outputTokens+=_}return{models:[{model:r,turns:t,total:{...a}}],total:a,turns:t}}async function b(n,e,i){if(!i||i==="kiro"){const r=P(n);if(r){f.info("acp-usage-parser",`Parsing Kiro session from ${r}`);try{return R(r)}catch(l){f.error("acp-usage-parser",`Kiro parse failed: ${l}`)}}}const o=S(n,e);if(o){f.info("acp-usage-parser",`Parsing Gemini session from ${o}`);try{return o.endsWith(".jsonl")?await w(o,y):J(o)}catch(r){f.error("acp-usage-parser",`Gemini parse failed: ${r}`)}}const s=F(n,e);if(s){f.info("acp-usage-parser",`Parsing Qwen session from ${s}`);try{return await w(s,M)}catch(r){f.error("acp-usage-parser",`Qwen parse failed: ${r}`)}}return f.info("acp-usage-parser",`No session file found for ${n} in ${e}`),null}export{b as parseAcpSessionUsage};