grix-connector 1.0.10 → 1.1.0

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 (39) hide show
  1. package/dist/adapter/acp/acp-adapter.js +1 -1
  2. package/dist/adapter/acp/session-scanner.js +2 -2
  3. package/dist/adapter/agy/agy-adapter.js +2 -0
  4. package/dist/adapter/agy/index.js +1 -0
  5. package/dist/adapter/agy/model-list.js +2 -0
  6. package/dist/adapter/agy/quota.js +5 -0
  7. package/dist/adapter/claude/claude-adapter.js +19 -19
  8. package/dist/adapter/claude/claude-bridge-server.js +1 -1
  9. package/dist/adapter/claude/claude-tools.js +1 -1
  10. package/dist/adapter/claude/claude-worker-client.js +1 -1
  11. package/dist/adapter/claude/mcp-http-launcher.js +2 -2
  12. package/dist/adapter/claude/model-list.js +1 -1
  13. package/dist/adapter/claude/result-timeout.js +1 -1
  14. package/dist/adapter/claude/session-scanner.js +1 -1
  15. package/dist/adapter/claude/skill-scanner.js +2 -2
  16. package/dist/adapter/claude/usage-parser.js +3 -3
  17. package/dist/adapter/codex/codex-bridge.js +5 -5
  18. package/dist/adapter/codex/session-scanner.js +1 -1
  19. package/dist/bridge/acp-toolbar-persist.js +1 -1
  20. package/dist/bridge/bridge.js +10 -10
  21. package/dist/bridge/deferred-events.js +1 -1
  22. package/dist/bridge/send-controller.js +1 -1
  23. package/dist/core/aibot/client.js +2 -2
  24. package/dist/core/file-ops/list-files.js +1 -1
  25. package/dist/core/files/file-serve.js +2 -1
  26. package/dist/core/installer/registry.js +1 -1
  27. package/dist/default-skills/index.js +1 -0
  28. package/dist/default-skills/index.ts +68 -0
  29. package/dist/default-skills/tailnet-file-share/SKILL.md +16 -0
  30. package/dist/log.js +2 -2
  31. package/dist/manager.js +2 -2
  32. package/dist/mcp/stream-http/config.js +1 -1
  33. package/dist/mcp/stream-http/connection-binding.js +1 -1
  34. package/dist/mcp/stream-http/security.js +1 -1
  35. package/dist/mcp/stream-http/tool-executor.js +1 -1
  36. package/dist/mcp/stream-http/tool-registry.js +1 -1
  37. package/dist/mcp/stream-http/tool-schemas.js +1 -1
  38. package/openclaw-plugin/index.js +73 -32
  39. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
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 I}from"node:events";import{spawn as R}from"node:child_process";import{AgentProcess as T}from"../../agent/process.js";import{AcpClient as w,AcpAuthRequiredError as y,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 D}from"../../core/util/cli-probe.js";const C=m.dirname(A(import.meta.url)),q=200,B=60*1e3,U=600*1e3,b=80;function j(h){return!(!(h instanceof Error)||h.code!==-32603||k(h))}function g(h){if(!(h instanceof Error))return String(h);let e=h.message;const i=h.data;return i&&(e+=` data=${JSON.stringify(i)}`),e}async function F(h,e){if(!h)return!1;const i=m.join(v(),".kiro","sessions","cli",`${h}.jsonl`);try{return(await f.stat(i)).mtimeMs>e}catch{return!1}}function L(h){return h.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g,"")}class oe extends I{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,i,t){if(super(),this.config=e,this.callbacks=i,this.bridgeLog=t?.bridgeLog??null,this.acpAuthMethod=t?.acpAuthMethod,this.acpInitialMode=t?.acpInitialMode,this.acpInitialModel=t?.acpInitialModel,this.acpMcpTools=t?.acpMcpTools??!0,this.rawTransport=t?.rawTransport??!1,this.approvalMode=t?.approvalMode??"default",this.autoInjectArgs=t?.autoInjectArgs,this.bindingStore=t?.bindingStore??null,this.currentAibotSessionId=t?.aibotSessionId?String(t.aibotSessionId).trim():void 0,t?.eventResultsPath&&(this.eventResults=new $(t.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,i){}async destroySession(e){}sendPrompt(e){const i=new O(e.adapterSessionId);return this.acpClient?.isAlive&&this.acpClient.send(e.text).catch(t=>{i.emitError(t instanceof Error?t:new Error(String(t)))}),i}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;this.internalApi.setMcpBridgeUpHandler(i=>this.onMcpBridgeUp(i));const e=m.resolve(C,"../../mcp/mcp-bridge-server.js");return{name:"grix-app-bridge",command:process.execPath,args:[e,"--ws-url",this.internalApi.mcpBridgeWsUrl]}}async probe(e){const i=this.config.command||"gemini",t=await N(i),s=t!==null;let n=null,o;if(s){const u=await D(i);n=u.version,u.error&&(o=u.error)}else o={code:"cli_not_found",message:`command not found: ${i}`};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:i,installed:s,path:t,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 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,i,t){return this.acpClient?.supportsCommandsExecute?this.execCommandViaAcp(e,i):this.execCommandFallback(e,i)}async execCommandViaAcp(e,i){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 t=await this.acpClient.executeCommand(e,i||void 0);return this.finishCompaction("acp-commands-execute"),{status:t.status==="failed"?"failed":"ok",message:t.message??"Compacted",data:t.data}}catch(t){return this.finishCompaction("error"),t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`compact failed: ${t instanceof Error?t.message:t}`}}}try{const t=await this.acpClient.executeCommand(e,i||void 0);return{status:t.status??"ok",message:t.message,data:t.options?{command:e,options:t.options}:t.data}}catch(t){return t?.code===-32601?this.execCommandFallback(e,i):{status:"failed",message:`Command failed: ${t instanceof Error?t.message:t}`}}}async execCommandFallback(e,i){try{switch(e){case"model":{const t=i.trim();if(t)return await this.setModel(t)?{status:"ok",message:`Model set to ${t}`}:{status:"failed",message:`Failed to set model: ${t}`};const s=this.buildToolbarContext("model_list");return{status:"ok",message:`Current: ${s.currentModelId||"unknown"}`,data:s}}case"mode":{const t=i.trim();if(t)return await this.setMode(t)?{status:"ok",message:`Mode set to ${t}`}:{status:"failed",message:`Failed to set mode: ${t}`};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 t=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 t,{status:"ok",message:"Compacted"}}case"status":{const t=this.getStatus(),s=this.acpClient?.sessionOptions;return{status:"ok",message:`Alive: ${t.alive}, Busy: ${t.busy}, Model: ${s?.currentModelId??"unknown"}, Mode: ${s?.currentModeId??"unknown"}`,data:{alive:t.alive,busy:t.busy,sessions:t.sessions,model:s?.currentModelId??"",mode:s?.currentModeId??""}}}case"skills":{const t=S({mode:this.config.command==="kiro-cli"?"kiro":"gemini",projectDir:process.cwd()}),s=t.map(n=>`- ${n.name}${n.trigger?` (${n.trigger})`:""} [${n.source}]: ${n.description}`);return{status:"ok",message:s.length>0?s.join(`
2
- `):"No skills found",data:t}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(t){return{status:"failed",message:t instanceof Error?t.message:String(t)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}buildToolbarContext(e,i){const t=this.acpClient?.sessionOptions,s=t?.currentModeId??"",n=t?.currentModelId??"",o=t?.modes.map(l=>({id:l.id,name:l.name}))??[],a=t?.models.map(l=>({modelId:l.modelId,name:l.name}))??[],c=t?.models.map(l=>({id:l.modelId,displayName:l.name}))??[],d=t?.modes.map(l=>({id:l.id,displayName:l.name}))??[],u={outcome:e,...i?{cwd:i}:{},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,i){const t=this.pendingApprovals.get(e);return t?(this.pendingApprovals.delete(e),this.acpClient&&this.acpClient.respondPermission(t,{behavior:i}).catch(s=>{r.error("acp-adapter",`Failed to respond to permission: ${s}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun),!0):!1}respondToPermission(e,i){this.acpClient&&this.acpClient.respondPermission(e,i).catch(t=>{r.error("acp-adapter",`Failed to respond to permission: ${t}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun)}async handleLocalAction(e){const i=e.action_type??"",t=e.params??{};if(i==="exec_approve"||i==="exec_reject"||i==="permission_approve"||i==="permission_reject"){const s=String(t.tool_call_id??t.approval_command_id??t.approval_id??t.exec_context_id??""),n=i==="exec_approve"||i==="permission_approve",o=String(t.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,i){const t=this.sessionBindings.get(e);if(t)try{return await f.stat(t),!1}catch{r.info("acp-adapter",`Stale binding for session ${e}: ${t} no longer exists, allowing rebind to ${i}`),this.sessionBindings.delete(e)}return this.sessionBindings.set(e,i),this.bindingStore&&this.bindingStore.set(e,i),this.maybeRefreshKiroSkills(i),!this.sessionConnected&&this.agentProcess?.alive?this.connectSession(i).then(()=>!0).catch(s=>(r.error("acp-adapter",`Failed to create session on bind: ${g(s)}`),this.callbacks.sendUpdateBindingCard(e,"failed",i),!0)):(this.acpClient?.isAlive&&(this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(e,this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(e,"connected",i,this.buildToolbarContext("binding_ready",i))),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 i=String(e??"").trim()||void 0;let t=[];try{t=S({mode:"kiro",projectDir:i})}catch(s){r.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(t)}catch(s){r.warn("acp-adapter",`onSkillsUpdate failed: ${s instanceof Error?s.message:String(s)}`)}}replayDeferredEvents(e){const i=this.deferredEvents.get(e);if(!i||i.length===0)return;if(this.activeRun){r.info("acp-adapter",`Cannot replay deferred events for session ${e}: agent busy`);return}const t=i.shift();t&&(i.length===0?(this.deferredEvents.delete(e),this.cancelDeferredTimer(e)):this.deferredEvents.set(e,i),r.info("acp-adapter",`Replaying deferred event ${t.event.event_id} for session ${e} (remaining: ${i.length})`),this.startRun(t.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 i=this.compactionDoneResolver;this.compactionDoneResolver=null,i?.()}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 i=this.eventResults.get(e.session_id,e.event_id);r.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${i.status})`),this.callbacks.sendEventResult(e.event_id,i.status,i.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,i){this.acpClient?(this.acpClient.cancel().catch(()=>{}),this.flushStream()):this.flushStream(),this.activeRun?.eventId===e&&this.finishRun("canceled","stopped by user")}deferEvent(e){const i=this.deferredEvents.get(e.session_id)??[];i.some(t=>t.event.event_id===e.event_id)||(i.push({event:e,queuedAt:Date.now()}),this.deferredEvents.set(e.session_id,i),r.info("acp-adapter",`Deferred event ${e.event_id} for session ${e.session_id} (queue: ${i.length})`),this.scheduleDeferredTimeout(e.session_id))}deferredTimers=new Map;rejectDeferredEvents(e){for(const[i,t]of this.deferredEvents)for(const{event:s}of t)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 i=this.deferredTimers.get(e);i&&(clearTimeout(i),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 i=3e4,t=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 ${i/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")}},i);this.deferredTimers.set(e,t)}startRun(e,i){if(!this.acpClient?.isAlive){this.deferEvent(e);return}const t=`acp_${++this.clientMsgSeq}_${Date.now()}`;this.activeRun={eventId:e.event_id,sessionId:e.session_id,threadId:e.thread_id,clientMsgIdBase:t,currentClientMsgId:t,currentSegmentIndex:0,chunkSeq:0,buffer:"",quotedStream:new E,markdownSegmenter:new P,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:i,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=x(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),a=this.injectRecoveryContext(s.sessionId,o);this.sendPromptWithRetry(s,a)}injectRecoveryContext(e,i){const t=this.recoveryContextBySessionId.get(e);return t?(this.recoveryContextBySessionId.delete(e),r.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${t.length})`),`${t}
2
+ `):"No skills found",data:t}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(t){return{status:"failed",message:t instanceof Error?t.message:String(t)}}}get acpSessionOptions(){return this.acpClient?.sessionOptions??null}buildToolbarContext(e,i){const t=this.acpClient?.sessionOptions,s=t?.currentModeId??"";let n=t?.currentModelId??"";const o=t?.modes.map(l=>({id:l.id,name:l.name}))??[],a=t?.models.map(l=>({modelId:l.modelId,name:l.name}))??[],c=t?.models.map(l=>({id:l.modelId,displayName:l.name}))??[],d=t?.modes.map(l=>({id:l.id,displayName:l.name}))??[];c.length>0&&!c.some(l=>l.id===n)&&(n=c[0].id);const u={outcome:e,...i?{cwd:i}:{},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,i){const t=this.pendingApprovals.get(e);return t?(this.pendingApprovals.delete(e),this.acpClient&&this.acpClient.respondPermission(t,{behavior:i}).catch(s=>{r.error("acp-adapter",`Failed to respond to permission: ${s}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun),!0):!1}respondToPermission(e,i){this.acpClient&&this.acpClient.respondPermission(e,i).catch(t=>{r.error("acp-adapter",`Failed to respond to permission: ${t}`)}),this.activeRun&&this.resetIdleTimer(this.activeRun)}async handleLocalAction(e){const i=e.action_type??"",t=e.params??{};if(i==="exec_approve"||i==="exec_reject"||i==="permission_approve"||i==="permission_reject"){const s=String(t.tool_call_id??t.approval_command_id??t.approval_id??t.exec_context_id??""),n=i==="exec_approve"||i==="permission_approve",o=String(t.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,i){const t=this.sessionBindings.get(e);if(t)try{return await f.stat(t),!1}catch{r.info("acp-adapter",`Stale binding for session ${e}: ${t} no longer exists, allowing rebind to ${i}`),this.sessionBindings.delete(e)}return this.sessionBindings.set(e,i),this.bindingStore&&this.bindingStore.set(e,i),this.maybeRefreshKiroSkills(i),!this.sessionConnected&&this.agentProcess?.alive?this.connectSession(i).then(()=>!0).catch(s=>(r.error("acp-adapter",`Failed to create session on bind: ${g(s)}`),this.callbacks.sendUpdateBindingCard(e,"failed",i),!0)):(this.acpClient?.isAlive&&(this.bindingStore&&this.acpClient.sessionId&&this.bindingStore.setAcpSessionId(e,this.acpClient.sessionId),this.callbacks.sendUpdateBindingCard(e,"connected",i,this.buildToolbarContext("binding_ready",i))),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 i=String(e??"").trim()||void 0;let t=[];try{t=S({mode:"kiro",projectDir:i})}catch(s){r.warn("acp-adapter",`Kiro skills scan failed: ${s instanceof Error?s.message:String(s)}`);return}try{this.callbacks.onSkillsUpdate(t)}catch(s){r.warn("acp-adapter",`onSkillsUpdate failed: ${s instanceof Error?s.message:String(s)}`)}}replayDeferredEvents(e){const i=this.deferredEvents.get(e);if(!i||i.length===0)return;if(this.activeRun){r.info("acp-adapter",`Cannot replay deferred events for session ${e}: agent busy`);return}const t=i.shift();t&&(i.length===0?(this.deferredEvents.delete(e),this.cancelDeferredTimer(e)):this.deferredEvents.set(e,i),r.info("acp-adapter",`Replaying deferred event ${t.event.event_id} for session ${e} (remaining: ${i.length})`),this.startRun(t.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 i=this.compactionDoneResolver;this.compactionDoneResolver=null,i?.()}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 i=this.eventResults.get(e.session_id,e.event_id);r.info("acp-adapter",`Deduplicating event ${e.event_id} (cached: ${i.status})`),this.callbacks.sendEventResult(e.event_id,i.status,i.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,i){this.acpClient?(this.acpClient.cancel().catch(()=>{}),this.flushStream()):this.flushStream(),this.activeRun?.eventId===e&&this.finishRun("canceled","stopped by user")}deferEvent(e){const i=this.deferredEvents.get(e.session_id)??[];i.some(t=>t.event.event_id===e.event_id)||(i.push({event:e,queuedAt:Date.now()}),this.deferredEvents.set(e.session_id,i),r.info("acp-adapter",`Deferred event ${e.event_id} for session ${e.session_id} (queue: ${i.length})`),this.scheduleDeferredTimeout(e.session_id))}deferredTimers=new Map;rejectDeferredEvents(e){for(const[i,t]of this.deferredEvents)for(const{event:s}of t)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 i=this.deferredTimers.get(e);i&&(clearTimeout(i),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 i=3e4,t=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 ${i/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")}},i);this.deferredTimers.set(e,t)}startRun(e,i){if(!this.acpClient?.isAlive){this.deferEvent(e);return}const t=`acp_${++this.clientMsgSeq}_${Date.now()}`;this.activeRun={eventId:e.event_id,sessionId:e.session_id,threadId:e.thread_id,clientMsgIdBase:t,currentClientMsgId:t,currentSegmentIndex:0,chunkSeq:0,buffer:"",quotedStream:new E,markdownSegmenter:new P,quotedMessageId:void 0,flushTimer:null,idleTimer:null,awaitingToolResult:!1,responded:!1,silent:i,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=x(n.prompt,{messageId:e.msg_id,quotedMessageId:e.quoted_message_id}),a=this.injectRecoveryContext(s.sessionId,o);this.sendPromptWithRetry(s,a)}injectRecoveryContext(e,i){const t=this.recoveryContextBySessionId.get(e);return t?(this.recoveryContextBySessionId.delete(e),r.info("acp-adapter",`Injecting recovery context for session ${e} (chars=${t.length})`),`${t}
3
3
 
4
4
  [\u5F53\u524D\u7528\u6237\u6D88\u606F]
5
5
  ${i}`):i}sendPromptWithRetry(e,i){this.acpClient.send(i).catch(t=>{if(r.error("acp-adapter",`Prompt failed: ${g(t)}`),j(t)){const n=g(t),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=t instanceof Error?t.message:String(t);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 T(this.bridgeLog);const t=this.resolveCwd();try{if(!(await f.stat(t)).isDirectory())throw new Error(`Bound path is not a directory: ${t}`)}catch(s){throw String(s?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${t}. 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:t,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 i;this.acpMcpTools&&(i=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.activeRun&&(this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,"ACP transport closed"),this.finishRun("failed","ACP transport closed")),this.emit("exit",null))});const t=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:i,sessionId:a})};let n=!1,o;try{await s(t)}catch(a){if(a instanceof y){await this.handleAuthRequired(a);return}if(!t)throw this.handleConnectFailure(a),a;if(r.warn("acp-adapter",`Failed to load persisted session ${t}, fallback to session/new: ${g(a)}`),n=!0,o=t,this.config.command==="kiro-cli"&&this.currentAibotSessionId){r.info("acp-adapter",`Building kiro recovery summary: aibotSession=${this.currentAibotSessionId} acpSession=${t}`);const c=await this.buildKiroRecoveryContext(this.currentAibotSessionId,t,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=${t}`)}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,i,t){try{const s=m.join(v(),".kiro","sessions","cli",`${i}.json`);await f.access(s),r.info("acp-adapter",`Reading kiro source session file: ${s}`);const n=await this.generateSummaryByKiroCli(s,t);if(!n)return;const o=m.join(v(),".grix","data","session-summaries");await f.mkdir(o,{recursive:!0});const a=m.join(o,`${e}.md`),c=[`SOURCE_KIRO_SESSION_FILE: ${s}`,"",n.trim(),""].join(`
@@ -1,2 +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
+ 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(i){const n=i.replace(/\[\[message_id:[^\]]+\]\]/g,"").replace(/\s+/g," ").trim();if(n)return n.length>j?n.slice(0,j)+"\u2026":n}function A(i){for(const n of i){if(!n||typeof n!="object")continue;const t=n;if(t.type!=="user")continue;const s=t.content;if(typeof s=="string")return m(s);if(Array.isArray(s)){for(const e of s)if(e&&typeof e=="object"&&"text"in e&&typeof e.text=="string")return m(e.text)}}}function M(i){for(const n of i)try{const t=JSON.parse(n);if(t.type!=="user")continue;const s=t.message?.parts;if(Array.isArray(s)){for(const e of s)if(e&&typeof e=="object"&&"text"in e&&typeof e.text=="string")return m(e.text)}}catch{}}function x(i){if(typeof i=="string")return i;if(Array.isArray(i))for(const n of i){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(i){const n=g(i,131072);for(const t of n)try{const s=JSON.parse(t);if(s.kind!=="Prompt")continue;const e=x(s.data?.content);if(!e)continue;const o=m(e);if(o)return o}catch{}}function k(i){try{const n=Math.round(y(i).mtimeMs),t=JSON.parse(w(i,"utf8")),s=t.session_id;if(!s)return null;const e=t.updated_at?new Date(t.updated_at).getTime():n,o=t.created_at?new Date(t.created_at).getTime():void 0;return{sessionId:s,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:n}}catch{return null}}function S(i,n){try{const t=w(f.join(i,".gemini","history",n,".project_root"),"utf8").trim();return f.isAbsolute(t)?t:""}catch{return""}}function b(i){try{const n=Math.round(y(i).mtimeMs),s=g(i,32768).join(`
2
+ `),e=JSON.parse(s);if(!e.sessionId)return null;const o=A(e.messages||[]);return{sessionId:e.sessionId,startTime:e.startTime,title:o,mtimeMs:n}}catch{return null}}function I(i){try{const n=Math.round(y(i).mtimeMs),t=g(i,16384);if(t.length===0)return null;const s=JSON.parse(t[0]),e=s.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:s.startTime,title:o,mtimeMs:n}}catch{return null}}function J(i){try{const n=Math.round(y(i).mtimeMs),t=g(i,16384);if(t.length===0)return null;const s=JSON.parse(t[0]),e=s.sessionId;if(!e)return null;const o=M(t);return{sessionId:e,cwd:s.cwd,title:o,mtimeMs:n}}catch{return null}}function D(i=h()){const n=f.join(i,".gemini","tmp"),t=[];let s;try{s=p(n,{withFileTypes:!0})}catch{return t}for(const e of s){if(!e.isDirectory())continue;const o=f.join(n,e.name,"chats"),c=e.name,u=S(i,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 d=f.join(o,r.name);let l=null;r.name.endsWith(".json")?l=b(d):r.name.endsWith(".jsonl")&&(l=I(d)),l&&t.push({sessionId:l.sessionId,cwd:u,title:l.title,agentType:"gemini",createdAt:l.startTime?new Date(l.startTime).getTime():void 0,updatedAt:l.mtimeMs,filePath:d})}}return t}function N(i=h()){const n=f.join(i,".qwen","projects"),t=[];let s;try{s=p(n,{withFileTypes:!0})}catch{return t}for(const e of s){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),d=J(r);d&&t.push({sessionId:d.sessionId,cwd:d.cwd||c,title:d.title,agentType:"qwen",updatedAt:d.mtimeMs,filePath:r})}}return t}function _(i=h()){const n=f.join(i,".kiro","sessions","cli"),t=[];let s;try{s=p(n,{withFileTypes:!0})}catch{return t}for(const e of s){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,Math.round(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(i=h()){const n=D(i),t=N(i),s=_(i),e=[...n,...t,...s];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=${s.length})`),e}export{C as scanAcpSessions};
@@ -0,0 +1,2 @@
1
+ import{spawn as b}from"node:child_process";import{randomUUID as w}from"node:crypto";import{EventEmitter as m}from"node:events";import{existsSync as E,readFileSync as y,readdirSync as A,statSync as k}from"node:fs";import{homedir as S}from"node:os";import{join as u}from"node:path";import{log as r}from"../../core/log/index.js";import{buildSimpleProbeReport as $}from"../shared/probe-util.js";const o="agy-adapter",v=u(S(),".gemini","antigravity-cli","log"),I=u(S(),".gemini","antigravity-cli","cache","last_conversations.json");class O extends m{type="agy";config;callbacks;runtimeResolver;alive=!1;stopped=!1;activeEventId=null;activeEventSessionId=null;activeProcess=null;completedEventIds=new Set;sessionConversationMap=new Map;conversationOutputCache=new Map;eventQueue=[];constructor(t,e,s){super(),this.config=t,this.callbacks=e,this.runtimeResolver=s}async start(){this.alive||(this.alive=!0,this.stopped=!1,r.info(o,"AgyAdapter started"))}async stop(){if(!this.stopped){if(this.stopped=!0,this.alive=!1,this.activeProcess){try{this.activeProcess.kill("SIGKILL")}catch{}this.activeProcess=null}if(this.activeEventId&&this.activeEventSessionId)try{this.callbacks.sendEventResult(this.activeEventId,"canceled","adapter stopped")}catch{}this.activeEventId=null,this.activeEventSessionId=null,this.emit("exit",0),r.info(o,"AgyAdapter stopped")}}isAlive(){return this.alive}async createSession(t){return t.cwd??w()}async resumeSession(t,e){}async destroySession(t){}sendPrompt(t){const e=new m;return e.adapterSessionId=t.adapterSessionId,e.cancel=async()=>{e.emit("done",{status:"canceled"})},e}async cancel(t){}cancelCurrentRun(){if(!this.activeEventId||!this.activeProcess)return;const t=this.activeEventId;r.info(o,`cancelCurrentRun: killing process for event ${t}`);try{this.activeProcess.kill("SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","restarted by user"),this.clearActiveEvent(t),this.drainQueue()}deliverInboundEvent(t){if(!this.stopped){if(this.completedEventIds.has(t.event_id)){r.debug(o,`Skipping duplicate event: ${t.event_id}`);return}if(this.callbacks.sendEventAck(t.event_id,t.session_id),this.activeEventId){r.info(o,`Queuing event ${t.event_id} (active: ${this.activeEventId})`),this.eventQueue.push(t);return}this.processEvent(t)}}deliverStopEvent(t,e){if(this.activeEventId===t&&this.activeProcess){r.info(o,`Stopping event ${t}`);try{this.activeProcess.kill("SIGKILL")}catch{}this.activeProcess=null,this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActiveEvent(t),this.drainQueue()}}setPermissionHandler(){}async ping(t){return this.alive}getStatus(){return{alive:this.alive,busy:this.activeEventId!==null,sessions:this.sessionConversationMap.size}}async probe(t){const e=this.getStatus();return $(this.config.command||"agy",{alive:e.alive,busy:e.busy,started:e.alive},t)}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null}getMcpConfig(){return null}processEvent(t){const{event_id:e,session_id:s,content:i}=t,n=this.runtimeResolver(s);if(!n.cwd){r.warn(o,`No working directory for session ${s}, event should have been intercepted by bridge`),this.callbacks.forceCompleteInternalEvent(e,s);return}this.activeEventId=e,this.activeEventSessionId=s,this.emit("eventStarted",e,s),r.info(o,`Processing event ${e} for session ${s}`),this.spawnAgyPrint(e,s,i,n)}spawnAgyPrint(t,e,s,i){const n=this.buildPrintArgs(s,i),c={...process.env,...this.config.env??{}};r.info(o,`Spawning: agy ${n.map(a=>a.includes(" ")?`"${a}"`:a).join(" ")}`);const d=this.getLatestLogFilePath(),l=b(this.config.command,n,{cwd:i.cwd,env:c,stdio:["pipe","pipe","pipe"]});this.activeProcess=l;const f=[],g=[];l.stdout?.on("data",a=>{f.push(a)}),l.stderr?.on("data",a=>{g.push(a)}),l.on("error",a=>{r.error(o,`Process spawn error for event ${t}: ${a.message}`),this.handleProcessResult(t,e,"",`spawn error: ${a.message}`,i)}),l.on("close",a=>{if(r.info(o,`Process exited for event ${t} with code ${a}`),this.activeEventId!==t)return;const h=Buffer.concat(f).toString("utf-8"),P=Buffer.concat(g).toString("utf-8");if(a!==0){const p=P.trim()||`process exited with code ${a}`;this.handleProcessResult(t,e,h,p,i)}else h.trim()?this.handleProcessResult(t,e,h,"",i):setTimeout(()=>{const p=this.extractLogError(d);this.handleProcessResult(t,e,h,p,i)},300)}),l.stdin?.end()}buildPrintArgs(t,e){const s=[];e.modelId&&s.push("--model",e.modelId),e.cwd&&s.push("--add-dir",e.cwd),s.push("--dangerously-skip-permissions");const i=e.cwd?this.resolveConversationId(e.cwd):void 0;return i&&s.push("--conversation",i),s.push("-p",t),s}resolveConversationId(t){try{if(E(I)){const s=JSON.parse(y(I,"utf-8"))[t];if(s)return this.sessionConversationMap.set(t,s),s}}catch(e){r.debug(o,`resolveConversationId: ${e}`)}return this.sessionConversationMap.get(t)}getLatestLogFilePath(){try{if(!E(v))return null;const t=A(v).filter(e=>e.startsWith("cli-")&&e.endsWith(".log")).map(e=>({name:e,path:u(v,e),mtime:k(u(v,e)).mtimeMs})).sort((e,s)=>s.mtime-e.mtime);return t.length>0?t[0].path:null}catch{return null}}extractLogError(t){try{const e=this.getLatestLogFilePath();if(!e)return r.info(o,"extractLogError: no log files found"),"agy completed without output";const s=y(e,"utf-8");if(!s)return r.info(o,`extractLogError: log file is empty: ${e}`),"agy completed without output";const i=s.split(`
2
+ `).filter(n=>n.startsWith("E")).map(n=>{const c=n.indexOf("] ");return c>=0?n.slice(c+2):n}).filter(n=>n.length>0);if(i.length>0){const n=i[i.length-1];return r.info(o,`Extracted error from agy log: ${n.slice(0,120)}`),n.length>500?n.slice(0,500)+"...":n}return"agy completed without output (possible auth or quota issue)"}catch(e){return r.info(o,`extractLogError: ${e}`),"agy completed without output"}}handleProcessResult(t,e,s,i,n){if(this.activeEventId===t){if(i&&!s.trim()){r.error(o,`Event ${t} failed: ${i}`);const d=i.includes("RESOURCE_EXHAUSTED")||i.includes("quota")?"agy API \u914D\u989D\u5DF2\u7528\u5C3D\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5":`agy \u6267\u884C\u5931\u8D25: ${i}`;this.callbacks.sendStreamChunk(t,e,d,1,!0),this.callbacks.sendEventResult(t,"failed",i)}else{const c=s.trim();if(c){const d=n.cwd,l=d?this.extractDelta(c,d):c;d&&this.conversationOutputCache.set(d,c),l&&this.callbacks.sendStreamChunk(t,e,l,1,!0)}this.callbacks.sendEventResult(t,"responded")}this.clearActiveEvent(t),this.drainQueue()}}extractDelta(t,e){const s=this.conversationOutputCache.get(e);return s&&t.startsWith(s)?t.slice(s.length).trim():t}clearActiveEvent(t){const e=t??this.activeEventId;if(e&&(this.completedEventIds.add(e),this.completedEventIds.size>1e3)){const s=Array.from(this.completedEventIds);this.completedEventIds.clear();for(let i=500;i<s.length;i++)this.completedEventIds.add(s[i])}this.activeEventId=null,this.activeEventSessionId=null,this.activeProcess=null,e&&this.emit("eventDone",e)}drainQueue(){if(this.stopped||this.activeEventId)return;const t=this.eventQueue.shift();t&&(r.info(o,`Draining queued event ${t.event_id}`),this.processEvent(t))}}export{O as AgyAdapter};
@@ -0,0 +1 @@
1
+ import{AgyAdapter as o}from"./agy-adapter.js";export{o as AgyAdapter};
@@ -0,0 +1,2 @@
1
+ import{spawn as c}from"node:child_process";import{log as l}from"../../core/log/index.js";const s="agy-model-list",p=3600*1e3,h=1e4,y=["Gemini 3.5 Flash (Medium)","Gemini 3.5 Flash (High)","Gemini 3.5 Flash (Low)","Gemini 3.1 Pro (Low)","Gemini 3.1 Pro (High)","Claude Sonnet 4.6 (Thinking)","Claude Opus 4.6 (Thinking)","GPT-OSS 120B (Medium)"].map(t=>({id:t,displayName:t}));let a=null,f=0,r=null;function A(t){return new Promise((e,n)=>{const o=c(t,["models"],{stdio:["ignore","pipe","pipe"]});let d="",u="";const g=setTimeout(()=>{o.kill("SIGKILL"),n(new Error("agy models timed out"))},h);o.stdout?.on("data",i=>{d+=i.toString("utf-8")}),o.stderr?.on("data",i=>{u+=i.toString("utf-8")}),o.on("error",i=>{clearTimeout(g),n(i)}),o.on("close",i=>{clearTimeout(g),i===0?e(d):n(new Error(u.trim()||`agy models exited with code ${i}`))})})}function M(t){return t.split(`
2
+ `).map(e=>e.trim()).filter(e=>e.length>0).filter(e=>!/^(Usage|Error|Flags|Available)/i.test(e)).map(e=>({id:e,displayName:e}))}function m(t){return r||(r=(async()=>{try{const e=await A(t),n=M(e);n.length>0?(a=n,f=Date.now(),l.info(s,`refreshed agy models: ${n.length} entries`)):l.info(s,"agy models returned no parsable entries, keeping fallback")}catch(e){l.info(s,`agy models failed, using fallback: ${e instanceof Error?e.message:e}`)}finally{r=null}})(),r)}function G(t){return(!a||Date.now()-f>=p)&&t&&m(t),a??y}async function S(t){t&&await m(t)}export{G as getCachedAgyModels,S as primeAgyModels};
@@ -0,0 +1,5 @@
1
+ import{execFile as C}from"node:child_process";import{existsSync as U,readdirSync as q,readFileSync as R}from"node:fs";import*as F from"node:https";import{homedir as A}from"node:os";import{join as y}from"node:path";import{promisify as M}from"node:util";import{log as w}from"../../core/log/index.js";const g=M(C),T="agy-quota",G=300*1e3,O=5e3;let x=null,I=0,d=null;function he(){return(!x||Date.now()-I>=G)&&P(),x??{}}async function de(){await P()}async function P(){return d||(d=(async()=>{try{x=await Q(),I=Date.now()}catch(t){w.info(T,`fetch failed: ${t instanceof Error?t.message:t}`)}finally{d=null}})(),d)}async function Q(){const t=await $();if(t)try{const n=await j(t.port,t.csrfToken);if(n){w.info(T,`language server: plan=${n.plan}, reset_at=${n.quota_reset_at}`);const i=L();return{...n,quota_exhausted:i.exhausted}}}catch(n){w.info(T,`language server query failed: ${n instanceof Error?n.message:n}`)}const e=L(),s={};return e.exhausted&&(s.quota_exhausted=!0),e.resetAt&&(s.quota_reset_at=e.resetAt),s}async function $(){if(process.platform==="win32")return null;try{const t=await B();if(!t)return null;const e=await Y(t);if(!e)return null;const s=e.match(/--csrf_token\s+([0-9a-f-]+)/i);if(!s)return null;const n=s[1],i=await H(t);return i?{pid:t,port:i,csrfToken:n}:null}catch{return null}}async function B(){try{const{stdout:t}=await g("pgrep",["-f","language_server.*--csrf_token"],{timeout:3e3}),e=parseInt(t.trim().split(`
2
+ `)[0],10);return isNaN(e)?null:e}catch{return null}}async function Y(t){try{const{stdout:e}=await g("ps",["-p",String(t),"-o","args="],{timeout:3e3});return e.trim()||null}catch{return null}}async function H(t){return process.platform==="darwin"?b(t):await W(t)??b(t)}async function b(t){try{const{stdout:e}=await g("lsof",["-p",String(t),"-a","-i","TCP","-s","TCP:LISTEN"],{timeout:3e3}),s=[];for(const n of e.split(`
3
+ `)){const i=n.match(/:(\d+)\s+\(LISTEN\)/);i&&s.push(parseInt(i[1],10))}return s[0]??null}catch{return null}}async function W(t){try{const{stdout:e}=await g("ss",["-tlnp"],{timeout:3e3});for(const s of e.split(`
4
+ `)){if(!s.includes(`pid=${t}`))continue;const n=s.match(/[*\d.]+:(\d+)\s/);if(n)return parseInt(n[1],10)}return null}catch{return null}}async function j(t,e){const s=V(new Uint8Array(0));return new Promise(n=>{const i=setTimeout(()=>{r.destroy(),n(null)},O),r=F.request({hostname:"localhost",port:t,path:"/exa.language_server_pb.LanguageServerService/GetUserStatus",method:"POST",headers:{"Content-Type":"application/grpc-web+proto",Accept:"application/grpc-web+proto","x-codeium-csrf-token":e,"Content-Length":s.length},rejectUnauthorized:!1},o=>{const c=[];o.on("data",l=>c.push(l)),o.on("end",()=>{if(clearTimeout(i),o.statusCode!==200){n(null);return}const l=new Uint8Array(Buffer.concat(c));n(X(l))}),o.on("error",()=>{clearTimeout(i),n(null)})});r.on("error",()=>{clearTimeout(i),n(null)}),r.write(Buffer.from(s)),r.end()})}function V(t){const e=new Uint8Array(5+t.length);return e[0]=0,new DataView(e.buffer).setUint32(1,t.length,!1),e.set(t,5),e}function X(t){try{if(t.length<6)return null;const e=t[1]<<24|t[2]<<16|t[3]<<8|t[4];if(e<=0||5+e>t.length)return null;const s=t.slice(5,5+e),[n,i,r]=p(s,0);if(n!==1||i!==2)return null;const[o,c]=f(s,r),l=s.slice(c,c+o);return z(l)}catch{return null}}function z(t){const e={};let s=0;for(;s<t.length;){const[n,i,r]=p(t,s);if(s=r,s>=t.length&&i!==0)break;if(i===0){const[,o]=f(t,s);s=o}else if(i===2){const[o,c]=f(t,s),l=t.slice(c,c+o);if(s=c+o,n===36){const u=J(l);u[3]&&(e.plan=u[3])}else if(n===33){if(!e.quota_reset_at){const a=K(l);a&&(e.quota_reset_at=a)}const u=te(l);u!==void 0&&(e.available_credits=u)}}else if(i===1)s+=8;else if(i===5)s+=4;else break}return e}function J(t){const e={};let s=0;for(;s<t.length;){const[n,i,r]=p(t,s);if(s=r,i===2){const[o,c]=f(t,s),l=t.slice(c,c+o);s=c+o;try{e[n]=new TextDecoder().decode(l)}catch{}}else if(i===0){const[,o]=f(t,s);s=o}else if(i===1)s+=8;else if(i===5)s+=4;else break}return e}function K(t){let e=0;for(;e<t.length;){const[s,n,i]=p(t,e);if(e=i,n===2){const[r,o]=f(t,e),c=t.slice(o,o+r);if(e=o+r,s===1){const l=Z(c);if(l)return l}}else if(n===0){const[,r]=f(t,e);e=r}else if(n===1)e+=8;else if(n===5)e+=4;else break}}function Z(t){let e=0;for(;e<t.length;){const[s,n,i]=p(t,e);if(e=i,n===2){const[r,o]=f(t,e),c=t.slice(o,o+r);if(e=o+r,s===15)return ee(c)}else if(n===0){const[,r]=f(t,e);e=r}else if(n===1)e+=8;else if(n===5)e+=4;else break}}function ee(t){let e=0;for(;e<t.length;){const[s,n,i]=p(t,e);if(e=i,n===2){const[r,o]=f(t,e),c=t.slice(o,o+r);if(e=o+r,s===2){const[l,u,a]=p(c,0);if(l===1&&u===0){const[h]=f(c,a);if(h>1e9)return h}}}else if(n===0){const[,r]=f(t,e);e=r}else if(n===5)e+=4;else if(n===1)e+=8;else break}}function te(t){let e=0;for(;e<t.length;){const[s,n,i]=p(t,e);if(e=i,n===2){const[r,o]=f(t,e),c=t.slice(o,o+r);if(e=o+r,s===3){const l=ne(c);if(l!==void 0)return l}}else if(n===0){const[,r]=f(t,e);e=r}else if(n===1)e+=8;else if(n===5)e+=4;else break}}function ne(t){let e=0;for(;e<t.length;){const[s,n,i]=p(t,e);if(e=i,n===2){const[r,o]=f(t,e),c=t.slice(o,o+r);if(e=o+r,s===1){const{creditType:l,creditAmount:u}=se(c);if(l===1&&u!==void 0)return u}}else if(n===0){const[,r]=f(t,e);e=r}else if(n===1)e+=8;else if(n===5)e+=4;else break}}function se(t){let e,s,n=0;for(;n<t.length;){const[i,r,o]=p(t,n);if(n=o,r===0){const[c,l]=f(t,n);n=l,i===1?e=c:i===2&&(s=c)}else if(r===2){const[c,l]=f(t,n);n=l+c}else if(r===1)n+=8;else if(r===5)n+=4;else break}return{creditType:e,creditAmount:s}}function f(t,e){let s=0,n=0;for(;e<t.length;){const i=t[e++];if(s|=(i&127)<<n,n+=7,!(i&128))break}return[s,e]}function p(t,e){const[s,n]=f(t,e);return[s>>3,s&7,n]}const _=process.platform==="win32"?y(process.env.APPDATA??A(),".gemini","antigravity-cli","log"):y(A(),".gemini","antigravity-cli","log"),re=/RESOURCE_EXHAUSTED.*?Resets in (?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?/,ie=/^[EWID](\d{2})(\d{2})\s+(\d{2}):(\d{2}):(\d{2})/;function L(){try{if(!U(_))return{exhausted:!1};const t=q(_).filter(e=>e.endsWith(".log")).sort().reverse().slice(0,3);for(const e of t){const s=oe(y(_,e));if(s.exhausted)return s}}catch{}return{exhausted:!1}}function oe(t){try{const s=R(t,"utf-8").split(`
5
+ `);for(let n=s.length-1;n>=0;n--){const i=s[n];if(!i.includes("RESOURCE_EXHAUSTED"))continue;const r=i.match(re);if(!r)continue;const o=parseInt(r[1]??"0",10),c=parseInt(r[2]??"0",10),l=parseInt(r[3]??"0",10),u=o*3600+c*60+l,a=i.match(ie);let h;if(a&&u>0){const E=parseInt(a[1],10),D=parseInt(a[2],10),k=parseInt(a[3],10),v=parseInt(a[4],10),N=parseInt(a[5],10),S=new Date,m=new Date(S.getFullYear(),E-1,D,k,v,N);m.getTime()>S.getTime()+6e4&&m.setFullYear(m.getFullYear()-1),h=Math.floor(m.getTime()/1e3)+u}return h!==void 0&&h<=Math.floor(Date.now()/1e3)?{exhausted:!1}:{exhausted:!0,resetAt:h}}}catch{}return{exhausted:!1}}export{he as getCachedAgyQuotaInfo,de as primeAgyQuotaInfo};