grix-connector 3.2.9 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- import{closeSync as j,createReadStream as E,openSync as M,readFileSync as x,readSync as J,realpathSync as L,statSync as _}from"node:fs";import{createInterface as B}from"node:readline";import R from"node:path";import O from"node:os";import{log as A}from"../../core/log/index.js";function U(o){let r=o;try{r=L(o)}catch{}return r.replace(/[^a-zA-Z0-9]/g,"-")}function S(o,r){const t=R.join(O.homedir(),".claude"),a=R.join(t,"projects",U(r));return R.join(a,`${o}.jsonl`)}function w(){return{inputTokens:0,outputTokens:0,cacheReadInputTokens:0,cacheCreationInputTokens:0}}function I(o,r){o.inputTokens+=r.input_tokens??0,o.outputTokens+=r.output_tokens??0,o.cacheReadInputTokens+=r.cache_read_input_tokens??0,o.cacheCreationInputTokens+=r.cache_creation_input_tokens??0}async function Q(o,r){const t=S(o,r);A.info("usage-parser",`Parsing session usage from ${t}`);const a=new Map,e=w();let s=0;try{const l=B({input:E(t,"utf8"),crlfDelay:1/0});for await(const c of l)if(c.trim())try{const u=JSON.parse(c);if(u.type!=="assistant")continue;const n=u.message?.usage;if(!n)continue;const f=u.message?.model??"unknown";s++,I(e,n);let p=a.get(f);p||(p={turns:0,usage:w()},a.set(f,p)),p.turns++,I(p.usage,n)}catch{}}catch(l){return l.code==="ENOENT"?(A.info("usage-parser",`Session JSONL not found: ${t}`),null):(A.error("usage-parser",`Failed to parse session usage: ${l}`),null)}return s===0?null:{models:[...a.entries()].map(([l,c])=>({model:l,turns:c.turns,total:c.usage})),total:e,turns:s}}const m=64*1024,z=9e4;function C(o,r,t){const a=S(o,r);try{const e=x(a);let s;t!==void 0&&t>0?s=t<e.length?e.subarray(t):Buffer.alloc(0):s=e.length>m?e.subarray(e.length-m):e;const i=s.toString("utf8").split(`
2
- `);for(let l=i.length-1;l>=0;l--){const c=i[l].trim();if(!c)continue;let u;try{u=JSON.parse(c)}catch{continue}if(u.type!=="assistant")continue;const n=u.message;if(!n?.content)continue;const f=Array.isArray(n.content)?n.content:[{type:"text",text:String(n.content)}],p=[];for(const g of f){const y=g;y.type==="text"&&y.text?.trim()&&p.push(y.text)}if(p.length>0)return{text:p.join(`
3
- `),stopReason:n.stop_reason??null}}return null}catch{return null}}function X(o,r,t){return C(o,r,t)?.text??null}function Y(o,r,t){const a=S(o,r),e=t>0?t:0;let s;try{const i=_(a).size;if(e>=i)return{segments:[],newOffset:e};const l=i-e,c=Buffer.alloc(l);s=M(a,"r");const u=J(s,c,0,l,e),n=c.subarray(0,u);let f=-1;for(let h=n.length-1;h>=0;h--)if(n[h]===10){f=h;break}if(f<0)return{segments:[],newOffset:e};const p=n.subarray(0,f+1),g=e+p.length,y=[];for(const h of p.toString("utf8").split(`
4
- `)){const T=h.trim();if(!T)continue;let k;try{k=JSON.parse(T)}catch{continue}if(k.type!=="assistant")continue;const d=k.message;if(!d?.content)continue;const N=Array.isArray(d.content)?d.content:[{type:"text",text:String(d.content)}];for(const P of N){const b=P;b.type==="text"&&b.text?.trim()&&y.push(b.text)}}return{segments:y,newOffset:g}}catch{return{segments:[],newOffset:e}}finally{if(s!==void 0)try{j(s)}catch{}}}function Z(o,r,t){const a=S(o,r);let e=null,s;try{const c=_(a);e=Date.now()-c.mtimeMs,s=x(a)}catch{return{lastStopReason:null,freshMs:null}}let i;t!==void 0&&t>0?i=t<s.length?s.subarray(t):Buffer.alloc(0):i=s.length>m?s.subarray(s.length-m):s;const l=i.toString("utf8").split(`
5
- `);for(let c=l.length-1;c>=0;c--){const u=l[c].trim();if(!u)continue;let n;try{n=JSON.parse(u)}catch{continue}if(n.type!=="assistant")continue;const f=n.message;if(f)return{lastStopReason:f.stop_reason??null,freshMs:e}}return{lastStopReason:null,freshMs:e}}function q(o,r){const t=S(o,r);let a;try{a=x(t)}catch{return 0}const s=(a.length>m?a.subarray(a.length-m):a).toString("utf8").split(`
6
- `),i=new Set,l=new Set,c=/<task-id>\s*([^<\s]+)\s*<\/task-id>/;for(const n of s){const f=n.trim();if(!f)continue;if(f.includes("task-notification")){const y=c.exec(f);y&&l.add(y[1]);continue}let p;try{p=JSON.parse(f)}catch{continue}const g=p.toolUseResult;g&&typeof g.backgroundTaskId=="string"&&i.add(g.backgroundTaskId)}let u=0;for(const n of i)l.has(n)||u++;return u}function G(o,r,t,a=!1){const e=S(o,r);try{const s=_(e);if(a&&Date.now()-s.mtimeMs<z)return!1;const i=x(e),c=(t>0&&t<i.length?i.subarray(t):i.length>m?i.subarray(i.length-m):i).toString("utf8").split(`
7
- `);let u=!1,n=!1;for(const f of c){const p=f.trim();if(!p)continue;let g;try{g=JSON.parse(p)}catch{continue}if(g.type==="user"){const h=g.message?.content??g.content;(Array.isArray(h)?h:[]).some(d=>typeof d=="object"&&d!==null&&d.type==="tool_result")&&(u=!0,n=!1)}else g.type==="assistant"&&u&&(n=!0)}return u&&!n}catch{return!1}}export{q as countPendingBackgroundTasks,C as extractLastAssistantEntry,X as extractLastAssistantText,Y as extractTextBlocksAfterOffset,G as hasTerminalToolResultAfterOffset,Q as parseClaudeSessionUsage,Z as probeSessionTurnState,S as resolveSessionJsonlPath};
1
+ import{closeSync as P,createReadStream as j,openSync as J,readFileSync as b,readSync as L,realpathSync as O,statSync as _}from"node:fs";import{createInterface as B}from"node:readline";import R from"node:path";import C from"node:os";import{log as A}from"../../core/log/index.js";function U(s){let a=s;try{a=O(s)}catch{}return a.replace(/[^a-zA-Z0-9]/g,"-")}function T(s,a){const e=R.join(C.homedir(),".claude"),l=R.join(e,"projects",U(a));return R.join(l,`${s}.jsonl`)}function w(){return{inputTokens:0,outputTokens:0,cacheReadInputTokens:0,cacheCreationInputTokens:0}}function I(s,a){s.inputTokens+=a.input_tokens??0,s.outputTokens+=a.output_tokens??0,s.cacheReadInputTokens+=a.cache_read_input_tokens??0,s.cacheCreationInputTokens+=a.cache_creation_input_tokens??0}async function H(s,a){const e=T(s,a);A.info("usage-parser",`Parsing session usage from ${e}`);const l=new Map,t=w();let n=0;try{const o=B({input:j(e,"utf8"),crlfDelay:1/0});for await(const r of o)if(r.trim())try{const f=JSON.parse(r);if(f.type!=="assistant")continue;const c=f.message?.usage;if(!c)continue;const i=f.message?.model??"unknown";n++,I(t,c);let p=l.get(i);p||(p={turns:0,usage:w()},l.set(i,p)),p.turns++,I(p.usage,c)}catch{}}catch(o){return o.code==="ENOENT"?(A.info("usage-parser",`Session JSONL not found: ${e}`),null):(A.error("usage-parser",`Failed to parse session usage: ${o}`),null)}return n===0?null:{models:[...l.entries()].map(([o,r])=>({model:o,turns:r.turns,total:r.usage})),total:t,turns:n}}const S=64*1024,z=9e4,D=new Set(["No response requested."]);function N(s){return D.has(s.trim())}function Q(s,a,e){const l=T(s,a);try{const t=b(l);let n;e!==void 0&&e>0?n=e<t.length?t.subarray(e):Buffer.alloc(0):n=t.length>S?t.subarray(t.length-S):t;const u=n.toString("utf8").split(`
2
+ `);let o;for(let r=u.length-1;r>=0;r--){const f=u[r].trim();if(!f)continue;let c;try{c=JSON.parse(f)}catch{continue}if(c.type!=="assistant")continue;const i=c.message;if(!i?.content)continue;const p=Array.isArray(i.content)?i.content:[{type:"text",text:String(i.content)}],y=[];let h=0;for(const g of p){const d=g;if(!(d.type!=="text"||!d.text?.trim())){if(N(d.text)){h++;continue}y.push(d.text)}}if(y.length>0)return{text:y.join(`
3
+ `),stopReason:o!==void 0?o:i.stop_reason??null};h>0&&o===void 0&&(o=i.stop_reason??null)}return o!==void 0?{text:"",stopReason:o,metaOnly:!0}:null}catch{return null}}function Y(s,a,e){const l=T(s,a),t=e>0?e:0;let n;try{const u=_(l).size;if(t>=u)return{segments:[],newOffset:t};const o=u-t,r=Buffer.alloc(o);n=J(l,"r");const f=L(n,r,0,o,t),c=r.subarray(0,f);let i=-1;for(let g=c.length-1;g>=0;g--)if(c[g]===10){i=g;break}if(i<0)return{segments:[],newOffset:t};const p=c.subarray(0,i+1),y=t+p.length,h=[];for(const g of p.toString("utf8").split(`
4
+ `)){const d=g.trim();if(!d)continue;let k;try{k=JSON.parse(d)}catch{continue}if(k.type!=="assistant")continue;const m=k.message;if(!m?.content)continue;const E=Array.isArray(m.content)?m.content:[{type:"text",text:String(m.content)}];for(const M of E){const x=M;x.type!=="text"||!x.text?.trim()||N(x.text)||h.push(x.text)}}return{segments:h,newOffset:y}}catch{return{segments:[],newOffset:t}}finally{if(n!==void 0)try{P(n)}catch{}}}function Z(s,a,e){const l=T(s,a);let t=null,n;try{const r=_(l);t=Date.now()-r.mtimeMs,n=b(l)}catch{return{lastStopReason:null,freshMs:null}}let u;e!==void 0&&e>0?u=e<n.length?n.subarray(e):Buffer.alloc(0):u=n.length>S?n.subarray(n.length-S):n;const o=u.toString("utf8").split(`
5
+ `);for(let r=o.length-1;r>=0;r--){const f=o[r].trim();if(!f)continue;let c;try{c=JSON.parse(f)}catch{continue}if(c.type!=="assistant")continue;const i=c.message;if(i)return{lastStopReason:i.stop_reason??null,freshMs:t}}return{lastStopReason:null,freshMs:t}}function G(s,a){const e=T(s,a);let l;try{l=b(e)}catch{return 0}const n=(l.length>S?l.subarray(l.length-S):l).toString("utf8").split(`
6
+ `),u=new Set,o=new Set,r=/<task-id>\s*([^<\s]+)\s*<\/task-id>/;for(const c of n){const i=c.trim();if(!i)continue;if(i.includes("task-notification")){const h=r.exec(i);h&&o.add(h[1]);continue}let p;try{p=JSON.parse(i)}catch{continue}const y=p.toolUseResult;y&&typeof y.backgroundTaskId=="string"&&u.add(y.backgroundTaskId)}let f=0;for(const c of u)o.has(c)||f++;return f}function K(s,a,e,l=!1){const t=T(s,a);try{const n=_(t);if(l&&Date.now()-n.mtimeMs<z)return!1;const u=b(t),r=(e>0&&e<u.length?u.subarray(e):u.length>S?u.subarray(u.length-S):u).toString("utf8").split(`
7
+ `);let f=!1,c=!1;for(const i of r){const p=i.trim();if(!p)continue;let y;try{y=JSON.parse(p)}catch{continue}if(y.type==="user"){const g=y.message?.content??y.content;(Array.isArray(g)?g:[]).some(m=>typeof m=="object"&&m!==null&&m.type==="tool_result")&&(f=!0,c=!1)}else y.type==="assistant"&&f&&(c=!0)}return f&&!c}catch{return!1}}export{G as countPendingBackgroundTasks,Q as extractLastAssistantEntry,Y as extractTextBlocksAfterOffset,K as hasTerminalToolResultAfterOffset,H as parseClaudeSessionUsage,Z as probeSessionTurnState,T as resolveSessionJsonlPath};
@@ -1,11 +1,11 @@
1
- import{execFileSync as A}from"node:child_process";import{createInterface as W}from"node:readline";import{EventEmitter as E}from"node:events";import{stat as M}from"node:fs/promises";import{join as T,resolve as B}from"node:path";import{fileURLToPath as J}from"node:url";import{homedir as G}from"node:os";import{syncDefaultSkillsToDir as X}from"../../default-skills/index.js";import{resolveCommandPath as V,spawnCommand as Y,killProcessGroup as R,hasChildProcesses as K}from"../../core/runtime/spawn.js";import{InternalApiServer as Z}from"../../core/mcp/internal-api-server.js";import{log as r}from"../../core/log/index.js";import{resolveClientVersion as Q}from"../../core/util/client-version.js";import{isUserVisibleAgentMessagePhase as ee}from"../../core/util/codex-output-policy.js";import{SessionBindingStore as te}from"../../core/persistence/session-binding-store.js";import{ensureCodexProjectTrusted as se,isCodexCommand as ie,readCodexProviderSettings as C}from"./codex-trust.js";import{checkCodexSessionActivity as ne}from"./session-activity.js";import{findRolloutFile as oe}from"./rollout-locator.js";import{checkCodexLock as P}from"./lock-monitor.js";import{resolveCliPath as re,getCliVersion as ae}from"../../core/util/cli-probe.js";import{scanSkills as de}from"../claude/skill-scanner.js";const ce=60*1e3,le=600*1e3,he=5*1e3,$=2,ue=20,pe=8e3,L=80,fe=120*1e3,me=[{id:"gpt-5.3-codex",displayName:"GPT-5.3 Codex",defaultReasoningEffort:null,supportedReasoningEfforts:[],isDefault:!0}],N=[{id:"default",displayName:"Default"},{id:"plan",displayName:"Plan"}];function O(a,e){const t=String(a??"").trim()||"codex",i=V(t,e);if(i!==t)return i;const s=process.platform==="win32"?[]:["/opt/homebrew/bin/codex","/usr/local/bin/codex","/usr/bin/codex"],n=process.platform!=="win32"?[]:[T(process.env.LOCALAPPDATA??"","npm","codex.cmd"),T(process.env.APPDATA??"","npm","codex.cmd"),T(process.env.LOCALAPPDATA??"","Programs","codex","codex.exe")],o=[...s,...n];for(const d of o)try{return require("node:fs").accessSync(d,require("node:fs").constants.X_OK),d}catch{continue}return r.warn("codex-adapter",`resolveCodexCommandPath: failed to resolve "${t}". envPath=${e?"provided":"missing (using process.env.PATH)"}. fallbacks tried: [${o.join(", ")}]`),t}class q extends E{type="codex";config;callbacks;process=null;alive=!1;stopped=!1;internalApi=null;bridgeStatus="starting";pendingRequests=new Map;requestId=0;threadId=null;initialized=!1;cwd;approvalPolicy;sandboxMode;model;collaborationMode;reasoningEffort;idleTimer=null;lastProgressAt=0;lastIdleCheckAt=0;idleNoProgressCount=0;inFlightToolOps=0;pendingApprovals=new Map;needsHistoryInjection=!1;lastInjectedSessionId=null;identityInjectedThreadId=null;bindingStore=null;aibotSessionId="";threadResumePending=!1;autoTrustProject="auto";codexHome;codexModelOptions=[];codexModelOptionsCachedAt=0;static MODEL_CACHE_TTL_MS=3600*1e3;rateLimitSnapshot=null;currentThreadTokenUsage=null;currentModelContextWindow=null;constructor(e,t){super(),this.config=e,this.callbacks=t;const i=e.options??{};if(this.approvalPolicy=i.approvalPolicy??"never",this.sandboxMode=typeof i.sandboxMode=="string"&&i.sandboxMode.trim()&&i.sandboxMode.trim()!=="default"?i.sandboxMode.trim():void 0,this.aibotSessionId=String(i.aibotSessionId??"").trim(),this.bindingStore=i.bindingStore instanceof te?i.bindingStore:null,this.model=i.model,this.collaborationMode=i.collaborationMode,this.reasoningEffort=i.reasoningEffort,this.bindingStore&&this.aibotSessionId&&(this.model=this.model??this.bindingStore.getCodexModelId(this.aibotSessionId),this.collaborationMode=this.collaborationMode??this.bindingStore.getCodexModeId(this.aibotSessionId),this.reasoningEffort=this.reasoningEffort??this.bindingStore.getCodexReasoningEffort(this.aibotSessionId),this.sandboxMode=this.sandboxMode??this.bindingStore.getCodexSandboxMode(this.aibotSessionId)),this.autoTrustProject=typeof i.autoTrustProject=="boolean"?i.autoTrustProject:"auto",this.codexHome=typeof i.codexHome=="string"&&i.codexHome.trim()?i.codexHome.trim():void 0,this.cwd=this.resolveCwd(),this.bindingStore&&this.aibotSessionId){const s=this.bindingStore.getCodexThreadId(this.aibotSessionId);s&&(this.threadId=s,this.threadResumePending=!0)}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}return process.cwd()}async start(){await this.ensureProjectTrusted(),await this.startInternalApiAndRegisterMcp(),await this.spawnCodex(),await this.initializeHandshake(),await this.notifyBindingReadyWithContext(),r.info("codex-adapter",`Ready (pid=${this.process?.pid})`)}async ensureProjectTrusted(){if(this.shouldAutoTrustProject())try{const e=await se(this.cwd,{codexHome:this.codexHome});e.changed&&r.info("codex-adapter",`Trusted Codex project ${this.cwd} in ${e.configPath}`)}catch(e){const t=e instanceof Error?e.message:String(e);throw r.error("codex-adapter",`Failed to trust Codex project ${this.cwd}: ${t}`),e}}async startInternalApiAndRegisterMcp(){try{this.internalApi=new Z,this.internalApi.setInvokeHandler(async(d,u)=>this.callbacks.agentInvoke(d,u)),await this.internalApi.start(0),r.info("codex-adapter",`Internal API started at ${this.internalApi.url}`);const e=this.getMcpConfig(),t={...process.env,...this.config.env},i=typeof t.PATH=="string"?t.PATH:void 0,s=O(this.config.command||"codex",i);try{A(s,["mcp","remove",e.name],{env:t,timeout:1e4,stdio:"ignore"})}catch{}A(s,["mcp","add",e.name,"--",e.command,...e.args],{env:t,timeout:1e4,stdio:"ignore"}),r.info("codex-adapter",`Registered MCP server: ${e.name}`);const n=T(G(),".codex","skills"),o=X(n);o.length>0&&r.info("codex-adapter",`Synced connector skills to ${n}: [${o.join(", ")}]`)}catch(e){r.warn("codex-adapter",`Failed to register MCP tools (non-fatal): ${e instanceof Error?e.message:String(e)}`)}}shouldAutoTrustProject(){return this.autoTrustProject===!1?!1:this.autoTrustProject===!0?!0:ie(this.config.command)}notifyBindingReady(){!this.aibotSessionId||!this.cwd||this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd)}async notifyBindingReadyWithContext(){if(!(!this.aibotSessionId||!this.cwd))try{this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd,await this.buildToolbarContextResult("binding_ready"))}catch(e){r.warn("codex-adapter",`Failed to attach toolbar context to binding update: ${e instanceof Error?e.message:String(e)}`),this.notifyBindingReady()}}getEffortMeta(){const e=this.currentModelId(),t=this.getModelOption(e),i=t?.supportedReasoningEfforts??[];return i.length===0?{}:{available_efforts:i,reasoning_effort:this.reasoningEffort??t?.defaultReasoningEffort??null}}async stop(){this.stopped=!0,this.alive=!1,this.stopComposing(),this.clearIdleTimer(),this.rejectAllPending("adapter stopped"),this.pendingAutoCompact=!1,this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null);for(const t of this.compactionDeferred.splice(0))this.callbacks.sendEventResult(t.event_id,"failed","adapter stopped");for(const t of this.steerDeferred.splice(0))this.callbacks.sendEventResult(t.event_id,"failed","adapter stopped");this.internalApi&&(await this.internalApi.stop(),this.internalApi=null);const e=this.process;if(this.process=null,e?.pid&&typeof e.once=="function")R(e,"SIGTERM"),await Promise.race([new Promise(i=>{e.once("exit",()=>i(!0))}),new Promise(i=>{setTimeout(()=>i(!1),5e3)})])||R(e,"SIGKILL");else if(e)try{e.kill("SIGTERM")}catch{}}isAlive(){return this.alive}async createSession(e){if(this.initialized||await this.initializeHandshake(),this.threadId||await this.startNewThread(),!this.threadId)throw new Error("Failed to create session: thread ID is missing");return await this.notifyBindingReadyWithContext(),this.threadId}async resumeSession(e,t){await this.ensureThreadResumed()}async destroySession(e){this.threadId=null,this.threadResumePending=!1,this.persistThreadId(void 0)}sendPrompt(e){const t=new S(e.adapterSessionId);return this.runTurn(e,t).catch(i=>{t.emitError(i instanceof Error?i:new Error(String(i)))}),t}async cancel(e){if(this.threadId)try{await this.sendRequest("turn/interrupt",{threadId:this.threadId,turnId:this.currentTurnId??""},5e3)}catch{}}setPermissionHandler(e){this.permissionHandler=e}permissionHandler=null;async ping(e){if(!this.alive||!this.process)return!1;if(!this.process.pid){const t=!!(this.process.stdin&&!this.process.stdin.destroyed),i=!!(this.process.stdout&&!this.process.stdout.destroyed);return t&&i}if(!this.process.stdin||this.process.stdin.destroyed)return!!(this.process.stdout&&!this.process.stdout.destroyed);try{const t=++this.requestId,i={jsonrpc:"2.0",id:t,method:"ping",params:{}};return await new Promise(s=>{const n=setTimeout(()=>{this.pendingRequests.delete(String(t)),s(!1)},e);this.pendingRequests.set(String(t),{resolve:()=>s(!0),reject:()=>s(!1),timer:n}),this.process.stdin.write(`${JSON.stringify(i)}
2
- `)})}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.currentTurnId!==null||this.compacting,sessions:this.threadId?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){this.clearIdleTimer(),this.activeEventId=null}setModel(e){this.model=e,this.persistCodexContext(),r.info("codex-adapter",`Model set to: ${e}`)}setMode(e){const t=y(e);if(!t){r.info("codex-adapter",`Ignoring unsupported mode: ${e}`);return}this.collaborationMode=t,this.persistCodexContext(),r.info("codex-adapter",`Mode set to: ${t}`)}setReasoningEffort(e){this.reasoningEffort=e,this.persistCodexContext(),r.info("codex-adapter",`Reasoning effort set to: ${e}`)}setSandboxMode(e){this.sandboxMode=e,this.persistCodexContext(),r.info("codex-adapter",`Sandbox mode set to: ${e} (applied on next restart)`)}async threadCompact(){if(!this.threadId)throw new Error("No active thread");return this.sendRequest("thread/compact/start",{threadId:this.threadId},12e4)}async threadRollback(e){if(!this.threadId)throw new Error("No active thread");return this.sendRequest("thread/rollback",{threadId:this.threadId,numTurns:e},1e4)}getThreadId(){return this.threadId}getMcpConfig(){if(!this.internalApi)return null;const e=B(J(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const e=this.process?.pid;return e?K(e,[e]):!1}onAgentProfileChanged(){this.identityInjectedThreadId!==null&&(r.info("codex-adapter",`agent profile changed; clearing identity-injected thread record (was ${this.identityInjectedThreadId})`),this.identityInjectedThreadId=null)}async probe(e){const t=this.config.command||"codex",i=await re(t),s=i!==null;let n=null,o;if(s){const g=await ae(t);n=g.version,g.error&&(o=g.error)}else o={code:"cli_not_found",message:`command not found: ${t}`};const u=C().baseUrl??null,p=this.currentModelId()||null,f=this.getStatus(),h=e?.conversation?{attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"codex does not support minimal prompt probe"}}:{attempted:!1,ok:!1,latency_ms:null},m=await this.probeSessionRecord(),v=P({codexHome:this.codexHome});return{cli:{command:t,installed:s,path:i,version:n,...o?{error:o}:{}},conversation:h,config:{model:p,base_url:u,source:{model:p?"runtime":"unknown",base_url:u?"file":"unknown"}},process:{started:!!this.process,alive:f.alive,busy:f.busy},session:m,lock:{present:v.present,locked:v.locked,stale:v.stale,path:v.path}}}async probeSessionRecord(){const e=this.threadId?oe(this.threadId,this.codexHome):null;if(!e)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const t=await M(e);return{recordPath:e,lastActivityMs:t.mtimeMs,freshMs:Date.now()-t.mtimeMs}}catch{return{recordPath:e,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"compact",description:"Compact thread to reduce context size"},{name:"model",description:"List or set model",args:"[model_id]"},{name:"mode",description:"List or set collaboration mode",args:"[default|plan]"},{name:"rollback",description:"Roll back thread turns",args:"[num_turns]"},{name:"interrupt",description:"Interrupt current turn"},{name:"rate_limits",description:"Show current rate limits"},{name:"status",description:"Show thread and session status"},{name:"skills",description:"List available skills"}]}async execCommand(e,t,i){try{switch(e){case"compact":{if(!this.threadId)return{status:"failed",message:"No active thread"};if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","compacting"),this.clearActive()),await this.ensureThreadResumed(),this.currentTurnId)return{status:"failed",message:"agent busy"};if(this.needsHistoryInjection)return{status:"failed",message:"The session has expired and there is no history to compact. Please send a new message and try again."};this.compacting=!0;const s=new Promise(n=>{this.compactionDoneResolver=n});try{return await this.threadCompact(),this.armCompactionFallback(),await s,{status:"ok",message:"Thread compacted"}}catch(n){this.finishCompaction("compact-error");const o=n instanceof Error?n.message:String(n);if(/thread.*(not found|not exist|unknown|invalid)/i.test(o)||/not found.*thread/i.test(o))return this.threadId=null,this.persistThreadId(void 0),{status:"failed",message:"The session has expired and there is no history to compact. Please send a new message and try again."};throw n}}case"model":{const s=t.trim();return s?this.getModelOptions().some(o=>o.id===s)?(this.setModel(s),{status:"ok",message:`Model set to ${s}`}):{status:"failed",message:`Unknown model: ${s}`}:{status:"ok",message:`Current: ${this.currentModelId()}`,data:{models:this.getModelOptions()}}}case"mode":{const s=t.trim();if(s){const n=y(s);return n?(this.setMode(n),{status:"ok",message:`Mode set to ${n}`}):{status:"failed",message:`Unknown mode: ${s}. Supported: default, plan`}}return{status:"ok",message:`Current: ${this.collaborationMode??"default"}`,data:{modes:N}}}case"rollback":{if(!this.threadId)return{status:"failed",message:"No active thread"};const s=Math.max(1,parseInt(t.trim(),10)||1);return this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","rolled back"),this.clearActive()),await this.threadRollback(s),{status:"ok",message:`Rolled back ${s} turn(s)`}}case"interrupt":return this.threadId?(await this.cancel(this.threadId),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","interrupted"),this.clearActive(),{status:"ok",message:"Turn interrupted"}):{status:"failed",message:"No active thread"};case"rate_limits":return{status:"ok",message:this.rateLimitSnapshot?`Primary: ${this.rateLimitSnapshot.primary.usedPercent.toFixed(1)}%, Secondary: ${this.rateLimitSnapshot.secondary.usedPercent.toFixed(1)}%`:"Rate limits not available",data:{rateLimits:this.rateLimitSnapshot}};case"status":return{status:"ok",message:`Thread: ${this.threadId??"none"}, Model: ${this.currentModelId()}, Mode: ${this.collaborationMode??"default"}`,data:{threadId:this.threadId,model:this.currentModelId(),mode:this.collaborationMode??"default",cwd:this.cwd,alive:this.alive}};case"skills":{const s=de({mode:"codex",projectDir:this.cwd}),n=s.map(o=>`- ${o.name}${o.trigger?` (${o.trigger})`:""}: ${o.description}`);return{status:"ok",message:n.length>0?n.join(`
3
- `):"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)}}}async handleLocalAction(e){const t=e.action_type??"",i=e.params??{};switch(r.info("codex-adapter",`handleLocalAction action_type=${t} action_id=${e.action_id} session_id=${i.session_id??""}`),t){case"set_model":{const s=x(l(i.model_id),l(i.modelId),l(i.value));return s&&this.setModel(s),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("model_set")),{handled:!0,kind:"set_model"}}case"set_mode":{const s=i.mode_id;return s&&this.setMode(s),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("mode_set")),{handled:!0,kind:"set_mode"}}case"set_reasoning_effort":{const s=x(l(i.reasoning_effort),l(i.reasoning_eff),l(i.effort));return s&&this.setReasoningEffort(s),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("effort_set",!1)),{handled:!0,kind:"set_reasoning_effort"}}case"set_sandbox_mode":{const s=x(l(i.sandbox_mode),l(i.sandboxMode),l(i.value));return s&&this.setSandboxMode(s),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("sandbox_mode_set",!1)),{handled:!0,kind:"set_sandbox_mode"}}case"thread_compact":{r.info("codex-adapter",`thread_compact start action_id=${e.action_id} threadId=${this.threadId}`),this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","compacting"),this.clearActive());try{if(await this.ensureThreadResumed(),this.needsHistoryInjection)return r.info("codex-adapter",`thread_compact aborted: thread expired, new thread created action_id=${e.action_id}`),this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"thread_expired","The session has expired and there is no history to compact. Please send a new message and try again."),{handled:!0,kind:"thread_compact"};this.compacting=!0;const s=new Promise(n=>{this.compactionDoneResolver=n});await this.threadCompact(),this.armCompactionFallback(),await s,r.info("codex-adapter",`thread_compact done action_id=${e.action_id}`),this.callbacks.sendLocalActionResult(e.action_id,"ok")}catch(s){this.finishCompaction("compact-error");const n=s instanceof Error?s.message:String(s);/thread.*(not found|not exist|unknown|invalid)/i.test(n)||/not found.*thread/i.test(n)?(r.warn("codex-adapter",`thread_compact thread expired, resetting threadId action_id=${e.action_id} err=${n}`),this.threadId=null,this.persistThreadId(void 0),this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"thread_expired","The session has expired and there is no history to compact. Please send a new message and try again.")):(r.info("codex-adapter",`thread_compact failed action_id=${e.action_id} err=${n}`),this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,void 0,n))}return{handled:!0,kind:"thread_compact"}}case"get_context":return this.callbacks.sendLocalActionResult(e.action_id,"ok",{threadId:this.threadId,model:this.model,mode:this.collaborationMode,approvalPolicy:this.approvalPolicy,cwd:this.cwd,...await this.buildToolbarContextResult("context")}),{handled:!0,kind:"get_context"};case"exec_approve":case"file_approve":{const s=this.resolvePendingApproval(e);if(!s)return this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That approval request is no longer pending."),{handled:!0,kind:t};const n=i.decision||"allow-once",o=ge[n]??"accept";return this.sendApprovalDecision(s.requestId,o),this.pendingApprovals.delete(s.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:s.approvalId,approval_command_id:s.approvalCommandId,decision:n}),this.resumeAfterApproval(e,i,s),{handled:!0,kind:t}}case"exec_reject":case"file_reject":{const s=this.resolvePendingApproval(e);return s?(this.sendApprovalDecision(s.requestId,"deny"),this.pendingApprovals.delete(s.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:s.approvalId,approval_command_id:s.approvalCommandId,decision:"deny"}),this.resumeAfterApproval(e,i,s),{handled:!0,kind:t}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That approval request is no longer pending."),{handled:!0,kind:t})}case"permission_approve":{const s=this.resolvePendingApproval(e);return!s||s.kind!=="permission"?(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That permission approval request is no longer pending."),{handled:!0,kind:t}):(this.sendApprovalDecision(s.requestId,"accept"),this.pendingApprovals.delete(s.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:s.approvalId,decision:"approve"}),this.resumeAfterApproval(e,i,s),{handled:!0,kind:t})}case"permission_reject":{const s=this.resolvePendingApproval(e);return!s||s.kind!=="permission"?(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That permission approval request is no longer pending."),{handled:!0,kind:t}):(this.sendApprovalDecision(s.requestId,"deny"),this.pendingApprovals.delete(s.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:s.approvalId,decision:"deny"}),this.resumeAfterApproval(e,i,s),{handled:!0,kind:t})}case"turn_interrupt":{try{await this.cancel(this.threadId??""),this.callbacks.sendLocalActionResult(e.action_id,"ok")}catch(s){this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,void 0,s instanceof Error?s.message:String(s))}return{handled:!0,kind:"turn_interrupt"}}case"thread_rollback":{const s=Number(i.numTurns??i.num_turns??1);if(!this.threadId)return this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"no_thread","No active thread"),{handled:!0,kind:"thread_rollback"};this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","rolled back"),this.clearActive());try{await this.threadRollback(s),this.callbacks.sendLocalActionResult(e.action_id,"ok",{numTurns:s})}catch(n){this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,void 0,n instanceof Error?n.message:String(n))}return{handled:!0,kind:"thread_rollback"}}case"get_rate_limits":{const s=this.rateLimitSnapshot,n=this.buildContextWindowSnapshot(),o=Date.now(),d=this.currentThreadTokenUsage;return s?r.info("codex-adapter",`[rate-limits] responding: primary=${s.primary.usedPercent.toFixed(1)}% window=${s.primary.windowMinutes}min resetsAt=${s.primary.resetsAt} secondary=${s.secondary.usedPercent.toFixed(1)}% window=${s.secondary.windowMinutes}min resetsAt=${s.secondary.resetsAt}`):r.info("codex-adapter","[rate-limits] responding: no rateLimitSnapshot available"),r.debug("codex-adapter",`[cp-diagnose] get_rate_limits: threadId=${this.threadId??"-"} contextWindow=${JSON.stringify(n)} tokenUsage=${JSON.stringify(d)}`),this.callbacks.sendLocalActionResult(e.action_id,"ok",{adapterType:"codex",available:s!==null,cached:!1,sampledAt:o,rateLimits:s,contextWindow:n,tokenUsage:d}),{handled:!0,kind:"get_rate_limits"}}default:return{handled:!1,kind:""}}}async buildToolbarContextResult(e,t=!0){t&&await this.refreshCodexModelOptions();const i=y(this.collaborationMode)??"default",s=this.currentModelId(),n=this.getModelOption(s),o=this.getModelOptions(),d=N,u=this.reasoningEffort??n?.defaultReasoningEffort??null,p=n?.supportedReasoningEfforts??[],f=C(this.codexHome),h=!!(f.baseUrl&&D(f.baseUrl)),m=h?[]:p,v=h?null:u,g={outcome:e,session_context:{modelId:s,modeId:i,reasoningEffort:v,approvalPolicy:this.approvalPolicy,sandboxMode:this.sandboxMode??null},model_id:s,mode_id:i,currentModelId:s,currentModeId:i,available_models:o,available_modes:d,available_efforts:m,availableModels:o,availableModes:d,reasoning_effort:v,sandbox_mode:this.sandboxMode??null,models:o.map(c=>({modelId:c.id,name:c.displayName})),modes:d.map(c=>({id:c.id,name:c.displayName}))};return this.rateLimitSnapshot&&(g.rate_limit_primary_percent=this.rateLimitSnapshot.primary.usedPercent,g.rate_limit_secondary_percent=this.rateLimitSnapshot.secondary.usedPercent,g.rate_limit_primary_window_min=this.rateLimitSnapshot.primary.windowMinutes,g.rate_limit_secondary_window_min=this.rateLimitSnapshot.secondary.windowMinutes),g}currentModelId(){const e=String(this.model??"").trim();if(e)return e;const t=this.getModelOptions();return t.find(i=>i.isDefault)?.id??t[0]?.id??""}getModelOption(e){const t=e.trim().toLowerCase();return this.getModelOptions().find(i=>i.id.trim().toLowerCase()===t)}getModelOptions(){const e=this.config.options??{},t=e.available_models??e.availableModels??e.models,i=we(t),s=this.codexModelOptions,n=[],o=String(this.model??"").trim(),d=o&&!s.some(h=>h.id.trim().toLowerCase()===o.toLowerCase()),u=s.length>0?s:d?[]:me;if(o){const h=[...s,...i].find(m=>m.id.trim().toLowerCase()===o.toLowerCase());n.push({id:o,displayName:h?.displayName??_(o),defaultReasoningEffort:h?.defaultReasoningEffort??null,supportedReasoningEfforts:h?.supportedReasoningEfforts??[],isDefault:h?.isDefault??!1})}n.push(...i),n.push(...u);const p=[],f=new Set;for(const h of n){const m=h.id.trim(),v=m.toLowerCase();!m||f.has(v)||(f.add(v),p.push({id:m,displayName:h.displayName.trim()||m,defaultReasoningEffort:h.defaultReasoningEffort,supportedReasoningEfforts:[...h.supportedReasoningEfforts],isDefault:h.isDefault}))}return p}async refreshCodexModelOptions(){if(!this.initialized||!this.alive||!this.process?.stdin||Date.now()-this.codexModelOptionsCachedAt<q.MODEL_CACHE_TTL_MS)return;const e=C(this.codexHome);if(e.baseUrl&&D(e.baseUrl)){try{const t=await ye(e.baseUrl,e.apiKey);if(t.length>0){this.codexModelOptions=j(t),this.codexModelOptionsCachedAt=Date.now(),r.info("codex-adapter",`Loaded ${t.length} models from external provider ${e.baseUrl}`);return}}catch(t){r.warn("codex-adapter",`External provider /v1/models failed (${t instanceof Error?t.message:String(t)}), falling back`)}this.codexModelOptions=[],this.codexModelOptionsCachedAt=Date.now();return}try{const t=[];let i;do{const s=await this.sendRequest("model/list",{cursor:i??null,includeHidden:!1,limit:100},15e3),n=Te(s);t.push(...n.models),i=n.nextCursor}while(i);t.length>0&&(this.codexModelOptions=j(t),this.codexModelOptionsCachedAt=Date.now())}catch(t){r.warn("codex-adapter",`Failed to load Codex model list: ${t instanceof Error?t.message:String(t)}`)}}currentTurnId=null;compacting=!1;pendingAutoCompact=!1;compactionDeferred=[];steerDeferred=[];compactingTimer=null;compactionDoneResolver=null;activeEventId=null;activeSessionId=null;visibleAgentMessageIds=new Set;hiddenAgentMessageIds=new Set;agentMessagePhases=new Map;codexSequence=0;turnError=null;deliverInboundEvent(e){if(this.compacting){r.info("codex-adapter",`Event ${e.event_id} deferred: compaction in progress`),this.compactionDeferred.push(e);return}const t=e.content;if(this.currentTurnId&&t.trim()){r.info("codex-adapter",`Event ${e.event_id}: steering active turn ${this.currentTurnId}`),this.steerTurn(t).catch(i=>{const s=i instanceof Error?i.message:String(i);if(r.info("codex-adapter",`Steer failed, falling through: ${s}`),!this.currentTurnId){this.startNewTurn(e,t);return}if(ke(s)){r.info("codex-adapter","Unsteerable turn detected, clearing active state and starting new turn"),this.clearActive(),this.startNewTurn(e,t);return}r.info("codex-adapter",`Steer failed but turn still active; deferring event ${e.event_id} for replay after turn ends`),this.steerDeferred.push(e)});return}if(this.currentTurnId){r.info("codex-adapter",`Event ${e.event_id} rejected: busy and empty content`),this.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}this.startNewTurn(e,t)}startNewTurn(e,t){this.activeEventId=e.event_id,this.activeSessionId=e.session_id,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.codexSequence=0,this.startComposing();const i=this.threadId??"",s=new S(i),n={adapterSessionId:i,text:t,contextMessages:e.context_messages_json?JSON.parse(e.context_messages_json).map(o=>({senderId:o.sender_id??"unknown",content:o.content})):void 0};this.runTurn(n,s,e.event_id,e.session_id).catch(o=>{r.error("codex-adapter",`Turn failed: ${o}`),this.callbacks.sendEventResult(e.event_id,"failed",o instanceof Error?o.message:String(o)),this.clearActive()}),this.resetIdleTimer(e.event_id)}deliverStopEvent(e,t){this.activeEventId===e&&(this.cancel(this.threadId??"").catch(()=>{}),this.callbacks.sendEventResult(e,"canceled","stopped by user"),this.clearActive())}async spawnCodex(){const e={...process.env,...this.config.env},t=typeof e.PATH=="string"?e.PATH:void 0;t||r.warn("codex-adapter",`spawnCodex: env.PATH is missing! process.env.PATH=${process.env.PATH?"set":"missing"}, config.env=${JSON.stringify(this.config.env)}`);const i=O(this.config.command||"codex",t),s=xe(this.config.args);this.sandboxMode&&s.push("-c",`sandbox_mode="${this.sandboxMode}"`),r.info("codex-adapter",`Spawning: ${i} ${s.join(" ")}`);try{if(!(await M(this.cwd)).isDirectory())throw new Error(`Bound path is not a directory: ${this.cwd}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${this.cwd}. Please rebind with /grix open <valid-directory>.`):o}const n=P({codexHome:this.codexHome});if(n.locked&&!n.stale)throw new Error(`Codex session appears locked by another process: ${n.path}`);n.stale&&r.warn("codex-adapter",`Ignoring stale Codex lock: ${n.path}`);try{this.process=Y(i,s,{env:e,cwd:this.cwd}).process}catch(o){throw r.error("codex-adapter",`Codex spawn threw: ${o}`),this.process=null,this.alive=!1,this.bridgeStatus="closed",o}this.process.on("error",o=>{r.error("codex-adapter",`Codex process spawn error: ${o}`),this.process=null,this.alive=!1,this.bridgeStatus="closed",this.clearIdleTimer(),this.rejectAllPending("spawn failed: "+(o instanceof Error?o.message:String(o))),this.stopComposing(),this.activeEventId&&(this.callbacks.sendEventResult(this.activeEventId,"failed","Codex process spawn failed"),this.activeEventId=null),this.emit("exit",1)}),this.process.on("exit",o=>{r.info("codex-adapter",`Codex process exited (code=${o})`),this.alive=!1,this.bridgeStatus="closed",this.clearIdleTimer(),this.rejectAllPending("process exited"),this.stopComposing(),this.emit("exit",o)}),this.process.stderr?.on("data",o=>{const d=o.toString().trim();d&&r.info("codex-adapter",`[codex stderr] ${d}`)}),this.bindStdout(),this.alive=!0}bindStdout(){if(!this.process?.stdout)return;this.process.stdout.setEncoding("utf8"),W({input:this.process.stdout}).on("line",t=>{if(!t.trim())return;let i;try{i=JSON.parse(t)}catch{r.error("codex-adapter",`Invalid JSON from Codex: ${t.slice(0,200)}`);return}if(this.bridgeStatus==="starting"&&(this.bridgeStatus="ready"),i.id!=null){const s=String(i.id),n=this.pendingRequests.get(s);if(n){clearTimeout(n.timer),this.pendingRequests.delete(s),n.resolve(i);return}}this.handleNotification(i)})}handleNotification(e){const t=e.method,i=e.params??{};switch(this.captureContextWindowFromPayload(t,i),this.activeEventId&&this.resetIdleTimer(this.activeEventId),this.activeEventId&&t&&t.startsWith("item/")&&!t.endsWith("/requestApproval")&&this.startComposing(),t){case"item/started":{const s=i.item,n=s?.id,o=s?.type,d=s?.phase;this.isLongRunningToolItemType(o)&&(this.inFlightToolOps+=1),o==="agentMessage"&&n&&(typeof d=="string"&&d.trim()?this.agentMessagePhases.set(n,d.trim().toLowerCase()):this.agentMessagePhases.delete(n),ee(d)?(this.visibleAgentMessageIds.add(n),this.hiddenAgentMessageIds.delete(n)):(this.hiddenAgentMessageIds.add(n),this.visibleAgentMessageIds.delete(n)));break}case"item/agentMessage/delta":{const s=i.delta,n=i.itemId;s&&this.activeEventId&&(!n||!this.hiddenAgentMessageIds.has(n))&&!this.shouldSuppressAgentMessageDeltaDuringToolRun(n)&&this.emitCodexEvent("item/agentMessage/delta",e);break}case"item/completed":{const s=i.item,n=s?.id,o=s?.type;this.isLongRunningToolItemType(o)&&(this.inFlightToolOps=Math.max(0,this.inFlightToolOps-1)),this.activeEventId&&o!=="agentMessage"&&this.emitCodexEvent("item/completed",e),o==="agentMessage"&&n&&(this.visibleAgentMessageIds.delete(n),this.hiddenAgentMessageIds.delete(n),this.agentMessagePhases.delete(n));break}case"turn/started":{const n=i.turn?.id;n&&(this.currentTurnId=n,this.turnError=null,r.info("codex-adapter",`Turn started: ${n}`),this.startComposing());break}case"item/fileChange/requestApproval":{this.handleApprovalRequest(e,"file");break}case"item/commandExecution/requestApproval":{this.handleApprovalRequest(e,"exec");break}case"item/permissions/requestApproval":{this.activeEventId&&this.emitCodexEvent("item/permissions/requestApproval",e),this.handleApprovalRequest(e,"permission");break}case"turn/completed":{this.handleTurnCompleted(i);break}case"error":{const s=i.message??JSON.stringify(i);r.error("codex-adapter",`Codex error: ${s}`),s&&(this.turnError=s),this.activeEventId&&this.callbacks.sendRunError(this.activeEventId,this.activeSessionId??"",s);break}case"account/rateLimits/updated":{this.rateLimitSnapshot=this.parseRateLimitSnapshot(i),this.rateLimitSnapshot&&(r.info("codex-adapter",`Rate limits updated: primary=${this.rateLimitSnapshot.primary.usedPercent.toFixed(1)}% secondary=${this.rateLimitSnapshot.secondary.usedPercent.toFixed(1)}%`),this.callbacks.onRateLimitsUpdated?.(this.rateLimitSnapshot));break}case"thread/tokenUsage/updated":{const s=i.threadId,n=i.tokenUsage,o=n?.last;(!s||!n||!o)&&r.info("codex-adapter",`[cp-diagnose] token_usage payload_incomplete: method=thread/tokenUsage/updated hasThreadId=${String(!!s)} hasTokenUsage=${String(!!n)} hasLast=${String(!!o)} params=${JSON.stringify(i)}`),s&&n&&o&&(this.currentThreadTokenUsage={inputTokens:o.inputTokens??0,outputTokens:o.outputTokens??0,cacheReadInputTokens:o.cachedInputTokens??0,cacheCreationInputTokens:0},r.info("codex-adapter",`[cp-diagnose] token_usage raw thread=${s}: ${JSON.stringify(n)}`),r.info("codex-adapter",`[cp-diagnose] token_usage parsed thread=${s}: ${JSON.stringify(this.currentThreadTokenUsage)}`),this.callbacks.onTokenUsageUpdated?.(this.currentThreadTokenUsage),this.publishContextWindowSnapshot());break}default:break}}captureContextWindowFromPayload(e,t){const i=["model_context_window","modelContextWindow","context_window_size","contextWindowSize"],s=this.findNumericField(t,i);if(s==null||s<=0){const n=this.probeContextWindowFields(t,i);n.foundAnyCandidateField&&r.info("codex-adapter",`[cp-diagnose] context_window missing_or_invalid: method=${e??"-"} matchedFields=${n.matchedFields.join(",")||"-"} raw=${n.rawValues.join(",")||"-"} extracted=${String(s)}`);return}this.currentModelContextWindow!==s&&(this.currentModelContextWindow=s,r.info("codex-adapter",`[cp-diagnose] context_window updated: method=${e??"-"} size=${s}`),this.publishContextWindowSnapshot())}probeContextWindowFields(e,t){const i=[],s=[],n=o=>{if(!o||typeof o!="object")return;if(Array.isArray(o)){for(const u of o)n(u);return}const d=o;for(const u of t)Object.prototype.hasOwnProperty.call(d,u)&&(i.push(u),s.push(String(d[u])));for(const u of Object.values(d))n(u)};return n(e),{foundAnyCandidateField:i.length>0,matchedFields:i,rawValues:s}}findNumericField(e,t){if(!e||typeof e!="object")return null;const i=e;for(const s of t){const n=i[s];if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n=="string"){const o=Number(n);if(Number.isFinite(o))return o}}for(const s of Object.values(i)){if(Array.isArray(s)){for(const o of s){const d=this.findNumericField(o,t);if(d!=null)return d}continue}const n=this.findNumericField(s,t);if(n!=null)return n}return null}buildContextWindowSnapshot(){const e=this.currentModelContextWindow;if(!e||e<=0)return r.debug("codex-adapter",`[cp-diagnose] context_window snapshot skipped: invalid size=${String(e)}`),null;const t=this.currentThreadTokenUsage,i=t?t.inputTokens+t.outputTokens:null,s=i==null?null:Math.max(0,e-i),n=i==null?null:Math.min(100,i/e*100),o=i==null?null:Math.max(0,100-(n??0));return r.info("codex-adapter",`[cp-diagnose] context_window computed: threadId=${this.threadId??"-"} size=${e} usedTokens=${String(i)} usedPercentage=${String(n)}`),{sizeTokens:e,usedTokens:i,remainingTokens:s,usedPercentage:n,remainingPercentage:o,source:"codex:model_context_window"}}publishContextWindowSnapshot(){const e=this.buildContextWindowSnapshot();this.callbacks.onContextWindowUpdated?.(e),this.maybeScheduleAutoCompact(e)}maybeScheduleAutoCompact(e){const t=e?.usedPercentage;typeof t!="number"||t<L||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("codex-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${L}%, scheduling compact`),this.tryRunPendingAutoCompact())}tryRunPendingAutoCompact(){this.pendingAutoCompact&&(this.stopped||!this.alive||this.currentTurnId||this.compacting||(this.pendingAutoCompact=!1,this.execCommand("compact","",this.activeSessionId??"").then(e=>{r.info("codex-adapter",`[auto-compact] compact done status=${e.status} msg=${e.message??""}`),e.status==="failed"&&/agent busy/i.test(e.message??"")&&(this.pendingAutoCompact=!0)}).catch(e=>{r.warn("codex-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:e}`)})))}parseRateLimitSnapshot(e){try{const i=e.rateLimitsByLimitId?.codex??e.rateLimits??e,s=i.primary,n=i.secondary,o=i.credits,d=c=>{if(typeof c=="number"&&Number.isFinite(c))return c;if(typeof c=="string"){const I=Number(c);if(Number.isFinite(I))return I}return 0},u=c=>typeof c=="boolean"?c:typeof c=="string"?c.toLowerCase()==="true":!1,p=c=>{if(c==null)return null;if(typeof c=="string"){const I=c.trim();if(!I)return null;const w=Number(I);return Number.isFinite(w)&&w>1e9?new Date(w*1e3).toISOString():I}if(typeof c=="number"&&Number.isFinite(c)&&c>0){const I=c>1e12?c/1e3:c;return new Date(I*1e3).toISOString()}return null},f=c=>({usedPercent:d(c.used_percent??c.usedPercent),windowMinutes:d(c.window_minutes??c.windowDurationMins??c.windowMinutes),resetsAt:p(c.resets_at??c.resetsAt)}),h=()=>({usedPercent:0,windowMinutes:0,resetsAt:null}),m=()=>{const c=i.windows;if(!Array.isArray(c))return null;const I=c.map(b=>b&&typeof b=="object"?f(b):h()).filter(b=>b.windowMinutes>0);if(I.length===0)return null;const w=[...I].sort((b,z)=>b.windowMinutes-z.windowMinutes),H=w.find(b=>Math.abs(b.windowMinutes-300)<=30)??w[0],U=w.find(b=>Math.abs(b.windowMinutes-10080)<=120)??w[w.length-1];return{primary:H,secondary:U}},g=(s||n?{primary:s?f(s):h(),secondary:n?f(n):h()}:null)??m();return g?{primary:g.primary,secondary:g.secondary,credits:o?{hasCredits:u(o.has_credits??o.hasCredits),unlimited:u(o.unlimited),balance:o.balance==null?null:d(o.balance)}:{hasCredits:!1,unlimited:!1,balance:null}}:null}catch{return null}}handleApprovalRequest(e,t){const i=e.id;if(i==null)return;const s=e.method,n=`${i}`.trim(),o=`codex_${F(this.activeSessionId??"")}_${F(n)}`;if(this.approvalPolicy==="never"){this.sendApprovalDecision(i,"accept");return}this.activeEventId&&this.emitCodexEvent(s,e),this.pendingApprovals.set(o,{approvalId:o,approvalCommandId:n,sourceEventId:this.activeEventId??"",requestId:i,kind:t}),r.info("codex-adapter",`Pending approval stored: ${o} (kind=${t})`),this.clearIdleTimer(),this.stopComposing()}resolvePendingApproval(e){const t=e.params??{},i=x(t.approval_id,t.approvalId);if(i)return this.pendingApprovals.get(i)??null;const s=x(t.approval_command_id,t.approvalCommandId);if(s){for(const[,n]of this.pendingApprovals)if(n.approvalCommandId===s)return n}return null}sendApprovalDecision(e,t){if(e==null||!this.process?.stdin)return;const i={jsonrpc:"2.0",id:e,result:{decision:t}};this.process.stdin.write(`${JSON.stringify(i)}
4
- `)}armCompactionFallback(){this.compacting&&(this.compactingTimer&&clearTimeout(this.compactingTimer),this.compactingTimer=setTimeout(()=>{r.warn("codex-adapter","Compaction fallback fired: turn/completed not received within timeout, treating as stuck and releasing deferred events"),this.currentTurnId=null,this.finishCompaction("fallback-timeout")},fe),this.compactingTimer.unref?.())}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null);const t=this.compactionDeferred.splice(0);r.info("codex-adapter",`Compaction finished (${e}); replaying ${t.length} deferred events`);for(const s of t)this.deliverInboundEvent(s);const i=this.compactionDoneResolver;this.compactionDoneResolver=null,i?.()}handleTurnCompleted(e){if(this.currentTurnId=null,this.stopComposing(),this.activeEventId){const t=this.activeEventId,i={event_id:t,session_id:this.activeSessionId??"",thread_id:this.threadId??void 0,codex_event_type:"codex",codex_method:"turn/completed",codex_sequence:++this.codexSequence,codex_payload:e,codex_at:new Date().toISOString()},s=this.turnError;this.turnError=null;const n=!!s;this.callbacks.sendCodexEventReliable?(this.clearActive({runPendingAutoCompact:!1}),this.callbacks.sendCodexEventReliable(i).then(()=>{this.callbacks.sendEventResult(t,n?"failed":"responded",n?s??void 0:void 0),this.tryRunPendingAutoCompact()}).catch(o=>{r.error("codex-adapter",`sendCodexEventReliable failed event=${t}: ${o}`),this.callbacks.sendCodexEvent(i),this.callbacks.sendEventResult(t,n?"failed":"responded",n?s??void 0:void 0),this.tryRunPendingAutoCompact()})):(this.callbacks.sendCodexEvent(i),this.callbacks.sendEventResult(t,n?"failed":"responded",n?s??void 0:void 0),this.clearActive())}this.compacting&&this.finishCompaction("turn-completed")}emitCodexEvent(e,t){this.activeEventId&&this.callbacks.sendCodexEvent({event_id:this.activeEventId,session_id:this.activeSessionId??"",thread_id:this.threadId??void 0,codex_event_type:"codex",codex_method:e,codex_sequence:++this.codexSequence,codex_payload:t,codex_at:new Date().toISOString()})}startComposing(){}stopComposing(){}async sendRequest(e,t,i=3e4){return new Promise((s,n)=>{if(!this.process?.stdin){n(new Error("Codex process not available"));return}const o=++this.requestId,d={jsonrpc:"2.0",id:o,method:e,params:t??{}},u=setTimeout(()=>{this.pendingRequests.delete(String(o)),n(new Error(`Request timeout: ${e}`))},i);this.pendingRequests.set(String(o),{resolve:f=>{f.error?n(new Error(`JSON-RPC error: ${f.error.message}`)):s(f.result)},reject:n,timer:u});const p=JSON.stringify(d);this.process.stdin.write(`${p}
5
- `)})}sendNotification(e,t){if(!this.process?.stdin)return;const i={jsonrpc:"2.0",method:e,params:t??{}};this.process.stdin.write(`${JSON.stringify(i)}
6
- `)}async initializeHandshake(){if(this.initialized)return;const e=Q(),t=await this.sendRequest("initialize",{clientInfo:{name:"grix-connector",title:"Grix Connector",version:e},capabilities:{experimentalApi:!0}},15e3);r.info("codex-adapter",`Initialized: ${JSON.stringify(t)}`),this.sendNotification("initialized"),this.initialized=!0,await this.refreshCodexModelOptions(),this.fetchRateLimits()}fetchRateLimits(){Promise.resolve(this.sendRequest("account/rateLimits/read",void 0,1e4)).then(e=>{const t=this.parseRateLimitSnapshot(e);t&&(this.rateLimitSnapshot=t,r.info("codex-adapter",`Initial rate limits: primary=${t.primary.usedPercent.toFixed(1)}% secondary=${t.secondary.usedPercent.toFixed(1)}% credits=${t.credits.balance??"N/A"}`),this.callbacks.onRateLimitsUpdated?.(t))}).catch(e=>{r.warn("codex-adapter",`Failed to fetch rate limits: ${e instanceof Error?e.message:e}`)})}async runTurn(e,t,i,s){this.threadId||await this.createSession({cwd:this.cwd}),await this.ensureThreadResumed();const n=[],o=this.callbacks.getAgentProfile?.(),d=o?.agentName?.trim()??"",u=o?.introduction?.trim()??"";if((d||u)&&this.identityInjectedThreadId!==this.threadId){const h=["[system-identity]"];d&&h.push(`Your name is "${d}".`),u&&h.push(`Your self-introduction: ${u}`),h.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),h.push("[/system-identity]"),n.push({type:"text",text:h.join(`
7
- `),text_elements:[]}),this.identityInjectedThreadId=this.threadId,r.info("codex-adapter",`Injected agent identity for thread=${this.threadId} name="${d}"`)}if(this.needsHistoryInjection&&s){this.needsHistoryInjection=!1;const h=await this.loadHistoryForInjection(s);for(const m of h)n.push(m);h.length>0&&r.info("codex-adapter",`Injected ${h.length} history items`)}if(e.contextMessages&&e.contextMessages.length>0){const h=e.contextMessages.map(m=>`[${m.senderId??"unknown"}]: ${m.content}`).join(`
1
+ import{execFileSync as R}from"node:child_process";import{createInterface as B}from"node:readline";import{EventEmitter as P}from"node:events";import{stat as $}from"node:fs/promises";import{join as S,resolve as J}from"node:path";import{fileURLToPath as X}from"node:url";import{homedir as G}from"node:os";import{syncDefaultSkillsToDir as V}from"../../default-skills/index.js";import{resolveCommandPath as Y,spawnCommand as K,killProcessGroup as L,hasChildProcesses as Z}from"../../core/runtime/spawn.js";import{InternalApiServer as Q}from"../../core/mcp/internal-api-server.js";import{log as r}from"../../core/log/index.js";import{resolveClientVersion as ee}from"../../core/util/client-version.js";import{isUserVisibleAgentMessagePhase as te}from"../../core/util/codex-output-policy.js";import{SessionBindingStore as ie}from"../../core/persistence/session-binding-store.js";import{ensureCodexProjectTrusted as se,isCodexCommand as ne,readCodexProviderSettings as C}from"./codex-trust.js";import{checkCodexSessionActivity as oe}from"./session-activity.js";import{findRolloutFile as re}from"./rollout-locator.js";import{checkCodexLock as N}from"./lock-monitor.js";import{resolveCliPath as ae,getCliVersion as de}from"../../core/util/cli-probe.js";import{scanSkills as ce}from"../claude/skill-scanner.js";const le=60*1e3,he=600*1e3,ue=5*1e3,O=2,pe=20,fe=8e3,D=80,me=120*1e3,ge=[{id:"gpt-5.3-codex",displayName:"GPT-5.3 Codex",defaultReasoningEffort:null,supportedReasoningEfforts:[],defaultServiceTier:null,serviceTiers:[],isDefault:!0}],j=[{id:"default",displayName:"Default"},{id:"plan",displayName:"Plan"}];function F(a,e){const t=String(a??"").trim()||"codex",s=Y(t,e);if(s!==t)return s;const i=process.platform==="win32"?[]:["/opt/homebrew/bin/codex","/usr/local/bin/codex","/usr/bin/codex"],n=process.platform!=="win32"?[]:[S(process.env.LOCALAPPDATA??"","npm","codex.cmd"),S(process.env.APPDATA??"","npm","codex.cmd"),S(process.env.LOCALAPPDATA??"","Programs","codex","codex.exe")],o=[...i,...n];for(const d of o)try{return require("node:fs").accessSync(d,require("node:fs").constants.X_OK),d}catch{continue}return r.warn("codex-adapter",`resolveCodexCommandPath: failed to resolve "${t}". envPath=${e?"provided":"missing (using process.env.PATH)"}. fallbacks tried: [${o.join(", ")}]`),t}class M extends P{type="codex";config;callbacks;process=null;alive=!1;stopped=!1;internalApi=null;bridgeStatus="starting";pendingRequests=new Map;requestId=0;threadId=null;initialized=!1;cwd;approvalPolicy;sandboxMode;model;collaborationMode;reasoningEffort;serviceTier;idleTimer=null;lastProgressAt=0;lastIdleCheckAt=0;idleNoProgressCount=0;inFlightToolOps=0;pendingApprovals=new Map;needsHistoryInjection=!1;lastInjectedSessionId=null;identityInjectedThreadId=null;bindingStore=null;aibotSessionId="";threadResumePending=!1;autoTrustProject="auto";codexHome;codexModelOptions=[];codexModelOptionsCachedAt=0;static MODEL_CACHE_TTL_MS=3600*1e3;rateLimitSnapshot=null;currentThreadTokenUsage=null;currentModelContextWindow=null;constructor(e,t){super(),this.config=e,this.callbacks=t;const s=e.options??{};if(this.approvalPolicy=s.approvalPolicy??"never",this.sandboxMode=typeof s.sandboxMode=="string"&&s.sandboxMode.trim()&&s.sandboxMode.trim()!=="default"?s.sandboxMode.trim():void 0,this.aibotSessionId=String(s.aibotSessionId??"").trim(),this.bindingStore=s.bindingStore instanceof ie?s.bindingStore:null,this.model=s.model,this.collaborationMode=s.collaborationMode,this.reasoningEffort=s.reasoningEffort,this.serviceTier=s.serviceTier,this.bindingStore&&this.aibotSessionId&&(this.model=this.model??this.bindingStore.getCodexModelId(this.aibotSessionId),this.collaborationMode=this.collaborationMode??this.bindingStore.getCodexModeId(this.aibotSessionId),this.reasoningEffort=this.reasoningEffort??this.bindingStore.getCodexReasoningEffort(this.aibotSessionId),this.serviceTier=this.serviceTier??this.bindingStore.getCodexServiceTier(this.aibotSessionId),this.sandboxMode=this.sandboxMode??this.bindingStore.getCodexSandboxMode(this.aibotSessionId)),this.autoTrustProject=typeof s.autoTrustProject=="boolean"?s.autoTrustProject:"auto",this.codexHome=typeof s.codexHome=="string"&&s.codexHome.trim()?s.codexHome.trim():void 0,this.cwd=this.resolveCwd(),this.bindingStore&&this.aibotSessionId){const i=this.bindingStore.getCodexThreadId(this.aibotSessionId);i&&(this.threadId=i,this.threadResumePending=!0)}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}return process.cwd()}async start(){await this.ensureProjectTrusted(),await this.startInternalApiAndRegisterMcp(),await this.spawnCodex(),await this.initializeHandshake(),await this.notifyBindingReadyWithContext(),r.info("codex-adapter",`Ready (pid=${this.process?.pid})`)}async ensureProjectTrusted(){if(this.shouldAutoTrustProject())try{const e=await se(this.cwd,{codexHome:this.codexHome});e.changed&&r.info("codex-adapter",`Trusted Codex project ${this.cwd} in ${e.configPath}`)}catch(e){const t=e instanceof Error?e.message:String(e);throw r.error("codex-adapter",`Failed to trust Codex project ${this.cwd}: ${t}`),e}}async startInternalApiAndRegisterMcp(){try{this.internalApi=new Q,this.internalApi.setInvokeHandler(async(d,h)=>this.callbacks.agentInvoke(d,h)),await this.internalApi.start(0),r.info("codex-adapter",`Internal API started at ${this.internalApi.url}`);const e=this.getMcpConfig(),t={...process.env,...this.config.env},s=typeof t.PATH=="string"?t.PATH:void 0,i=F(this.config.command||"codex",s);try{R(i,["mcp","remove",e.name],{env:t,timeout:1e4,stdio:"ignore"})}catch{}R(i,["mcp","add",e.name,"--",e.command,...e.args],{env:t,timeout:1e4,stdio:"ignore"}),r.info("codex-adapter",`Registered MCP server: ${e.name}`);const n=S(G(),".codex","skills"),o=V(n);o.length>0&&r.info("codex-adapter",`Synced connector skills to ${n}: [${o.join(", ")}]`)}catch(e){r.warn("codex-adapter",`Failed to register MCP tools (non-fatal): ${e instanceof Error?e.message:String(e)}`)}}shouldAutoTrustProject(){return this.autoTrustProject===!1?!1:this.autoTrustProject===!0?!0:ne(this.config.command)}notifyBindingReady(){!this.aibotSessionId||!this.cwd||this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd)}async notifyBindingReadyWithContext(){if(!(!this.aibotSessionId||!this.cwd))try{this.callbacks.sendUpdateBindingCard(this.aibotSessionId,"ready",this.cwd,await this.buildToolbarContextResult("binding_ready"))}catch(e){r.warn("codex-adapter",`Failed to attach toolbar context to binding update: ${e instanceof Error?e.message:String(e)}`),this.notifyBindingReady()}}externalProviderCheckedAt=0;externalProviderCached=!1;static EXTERNAL_PROVIDER_CACHE_TTL_MS=60*1e3;isExternalProvider(){const e=Date.now();if(e-this.externalProviderCheckedAt<M.EXTERNAL_PROVIDER_CACHE_TTL_MS)return this.externalProviderCached;const t=C(this.codexHome);return this.externalProviderCached=!!(t.baseUrl&&q(t.baseUrl)),this.externalProviderCheckedAt=e,this.externalProviderCached}effectiveServiceTier(){if(!this.serviceTier||this.isExternalProvider())return;const e=this.getModelOption(this.currentModelId());if(!(e&&!e.serviceTiers.some(t=>t.id===this.serviceTier)))return this.serviceTier}getEffortMeta(){if(this.isExternalProvider())return{};const e=this.currentModelId(),t=this.getModelOption(e),s={},i=t?.supportedReasoningEfforts??[];i.length>0&&(s.available_efforts=i,s.reasoning_effort=this.reasoningEffort??t?.defaultReasoningEffort??null);const n=t?.serviceTiers??[];return n.length>0&&(s.available_service_tiers=n,s.service_tier=this.effectiveServiceTier()??t?.defaultServiceTier??null),s}async stop(){this.stopped=!0,this.alive=!1,this.stopComposing(),this.clearIdleTimer(),this.rejectAllPending("adapter stopped"),this.pendingAutoCompact=!1,this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null);for(const t of this.compactionDeferred.splice(0))this.callbacks.sendEventResult(t.event_id,"failed","adapter stopped");for(const t of this.steerDeferred.splice(0))this.callbacks.sendEventResult(t.event_id,"failed","adapter stopped");this.internalApi&&(await this.internalApi.stop(),this.internalApi=null);const e=this.process;if(this.process=null,e?.pid&&typeof e.once=="function")L(e,"SIGTERM"),await Promise.race([new Promise(s=>{e.once("exit",()=>s(!0))}),new Promise(s=>{setTimeout(()=>s(!1),5e3)})])||L(e,"SIGKILL");else if(e)try{e.kill("SIGTERM")}catch{}}isAlive(){return this.alive}async createSession(e){if(this.initialized||await this.initializeHandshake(),this.threadId||await this.startNewThread(),!this.threadId)throw new Error("Failed to create session: thread ID is missing");return await this.notifyBindingReadyWithContext(),this.threadId}async resumeSession(e,t){await this.ensureThreadResumed()}async destroySession(e){this.threadId=null,this.threadResumePending=!1,this.persistThreadId(void 0)}sendPrompt(e){const t=new E(e.adapterSessionId);return this.runTurn(e,t).catch(s=>{t.emitError(s instanceof Error?s:new Error(String(s)))}),t}async cancel(e){if(this.threadId)try{await this.sendRequest("turn/interrupt",{threadId:this.threadId,turnId:this.currentTurnId??""},5e3)}catch{}}setPermissionHandler(e){this.permissionHandler=e}permissionHandler=null;async ping(e){if(!this.alive||!this.process)return!1;if(!this.process.pid){const t=!!(this.process.stdin&&!this.process.stdin.destroyed),s=!!(this.process.stdout&&!this.process.stdout.destroyed);return t&&s}if(!this.process.stdin||this.process.stdin.destroyed)return!!(this.process.stdout&&!this.process.stdout.destroyed);try{const t=++this.requestId,s={jsonrpc:"2.0",id:t,method:"ping",params:{}};return await new Promise(i=>{const n=setTimeout(()=>{this.pendingRequests.delete(String(t)),i(!1)},e);this.pendingRequests.set(String(t),{resolve:()=>i(!0),reject:()=>i(!1),timer:n}),this.process.stdin.write(`${JSON.stringify(s)}
2
+ `)})}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.currentTurnId!==null||this.compacting,sessions:this.threadId?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}clearActiveEventForShutdown(){this.clearIdleTimer(),this.activeEventId=null}setModel(e){this.model=e,this.persistCodexContext(),r.info("codex-adapter",`Model set to: ${e}`)}setMode(e){const t=T(e);if(!t){r.info("codex-adapter",`Ignoring unsupported mode: ${e}`);return}this.collaborationMode=t,this.persistCodexContext(),r.info("codex-adapter",`Mode set to: ${t}`)}setReasoningEffort(e){this.reasoningEffort=e,this.persistCodexContext(),r.info("codex-adapter",`Reasoning effort set to: ${e}`)}setServiceTier(e){const t=e.trim();this.serviceTier=t.toLowerCase()==="default"?void 0:t,this.persistCodexContext(),r.info("codex-adapter",`Service tier set to: ${this.serviceTier??"default"}`)}setSandboxMode(e){this.sandboxMode=e,this.persistCodexContext(),r.info("codex-adapter",`Sandbox mode set to: ${e} (applied on next restart)`)}async threadCompact(){if(!this.threadId)throw new Error("No active thread");return this.sendRequest("thread/compact/start",{threadId:this.threadId},12e4)}async threadRollback(e){if(!this.threadId)throw new Error("No active thread");return this.sendRequest("thread/rollback",{threadId:this.threadId,numTurns:e},1e4)}getThreadId(){return this.threadId}getMcpConfig(){if(!this.internalApi)return null;const e=J(X(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[e,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const e=this.process?.pid;return e?Z(e,[e]):!1}onAgentProfileChanged(){this.identityInjectedThreadId!==null&&(r.info("codex-adapter",`agent profile changed; clearing identity-injected thread record (was ${this.identityInjectedThreadId})`),this.identityInjectedThreadId=null)}async probe(e){const t=this.config.command||"codex",s=await ae(t),i=s!==null;let n=null,o;if(i){const w=await de(t);n=w.version,w.error&&(o=w.error)}else o={code:"cli_not_found",message:`command not found: ${t}`};const h=C().baseUrl??null,f=this.currentModelId()||null,m=this.getStatus(),u=e?.conversation?{attempted:!0,ok:!1,latency_ms:null,error:{code:"unsupported",message:"codex does not support minimal prompt probe"}}:{attempted:!1,ok:!1,latency_ms:null},p=await this.probeSessionRecord(),g=N({codexHome:this.codexHome});return{cli:{command:t,installed:i,path:s,version:n,...o?{error:o}:{}},conversation:u,config:{model:f,base_url:h,source:{model:f?"runtime":"unknown",base_url:h?"file":"unknown"}},process:{started:!!this.process,alive:m.alive,busy:m.busy},session:p,lock:{present:g.present,locked:g.locked,stale:g.stale,path:g.path}}}async probeSessionRecord(){const e=this.threadId?re(this.threadId,this.codexHome):null;if(!e)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const t=await $(e);return{recordPath:e,lastActivityMs:t.mtimeMs,freshMs:Date.now()-t.mtimeMs}}catch{return{recordPath:e,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"compact",description:"Compact thread to reduce context size"},{name:"model",description:"List or set model",args:"[model_id]"},{name:"mode",description:"List or set collaboration mode",args:"[default|plan]"},{name:"rollback",description:"Roll back thread turns",args:"[num_turns]"},{name:"interrupt",description:"Interrupt current turn"},{name:"rate_limits",description:"Show current rate limits"},{name:"status",description:"Show thread and session status"},{name:"skills",description:"List available skills"}]}async execCommand(e,t,s){try{switch(e){case"compact":{if(!this.threadId)return{status:"failed",message:"No active thread"};if(this.compacting)return{status:"failed",message:"Compaction already in progress"};if(this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","compacting"),this.clearActive()),await this.ensureThreadResumed(),this.currentTurnId)return{status:"failed",message:"agent busy"};if(this.needsHistoryInjection)return{status:"failed",message:"The session has expired and there is no history to compact. Please send a new message and try again."};this.compacting=!0;const i=new Promise(n=>{this.compactionDoneResolver=n});try{return await this.threadCompact(),this.armCompactionFallback(),await i,{status:"ok",message:"Thread compacted"}}catch(n){this.finishCompaction("compact-error");const o=n instanceof Error?n.message:String(n);if(/thread.*(not found|not exist|unknown|invalid)/i.test(o)||/not found.*thread/i.test(o))return this.threadId=null,this.persistThreadId(void 0),{status:"failed",message:"The session has expired and there is no history to compact. Please send a new message and try again."};throw n}}case"model":{const i=t.trim();return i?this.getModelOptions().some(o=>o.id===i)?(this.setModel(i),{status:"ok",message:`Model set to ${i}`}):{status:"failed",message:`Unknown model: ${i}`}:{status:"ok",message:`Current: ${this.currentModelId()}`,data:{models:this.getModelOptions()}}}case"mode":{const i=t.trim();if(i){const n=T(i);return n?(this.setMode(n),{status:"ok",message:`Mode set to ${n}`}):{status:"failed",message:`Unknown mode: ${i}. Supported: default, plan`}}return{status:"ok",message:`Current: ${this.collaborationMode??"default"}`,data:{modes:j}}}case"rollback":{if(!this.threadId)return{status:"failed",message:"No active thread"};const i=Math.max(1,parseInt(t.trim(),10)||1);return this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","rolled back"),this.clearActive()),await this.threadRollback(i),{status:"ok",message:`Rolled back ${i} turn(s)`}}case"interrupt":return this.threadId?(await this.cancel(this.threadId),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","interrupted"),this.clearActive(),{status:"ok",message:"Turn interrupted"}):{status:"failed",message:"No active thread"};case"rate_limits":return{status:"ok",message:this.rateLimitSnapshot?`Primary: ${this.rateLimitSnapshot.primary.usedPercent.toFixed(1)}%, Secondary: ${this.rateLimitSnapshot.secondary.usedPercent.toFixed(1)}%`:"Rate limits not available",data:{rateLimits:this.rateLimitSnapshot}};case"status":return{status:"ok",message:`Thread: ${this.threadId??"none"}, Model: ${this.currentModelId()}, Mode: ${this.collaborationMode??"default"}`,data:{threadId:this.threadId,model:this.currentModelId(),mode:this.collaborationMode??"default",cwd:this.cwd,alive:this.alive}};case"skills":{const i=ce({mode:"codex",projectDir:this.cwd}),n=i.map(o=>`- ${o.name}${o.trigger?` (${o.trigger})`:""}: ${o.description}`);return{status:"ok",message:n.length>0?n.join(`
3
+ `):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${e}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}async handleLocalAction(e){const t=e.action_type??"",s=e.params??{};switch(r.info("codex-adapter",`handleLocalAction action_type=${t} action_id=${e.action_id} session_id=${s.session_id??""}`),t){case"set_model":{const i=x(c(s.model_id),c(s.modelId),c(s.value));return i&&this.setModel(i),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("model_set")),{handled:!0,kind:"set_model"}}case"set_mode":{const i=s.mode_id;return i&&this.setMode(i),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("mode_set")),{handled:!0,kind:"set_mode"}}case"set_reasoning_effort":{const i=x(c(s.reasoning_effort),c(s.reasoning_eff),c(s.effort));return i&&this.setReasoningEffort(i),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("effort_set",!1)),{handled:!0,kind:"set_reasoning_effort"}}case"set_service_tier":{const i=x(c(s.service_tier),c(s.serviceTier),c(s.value));return i&&this.setServiceTier(i),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("service_tier_set",!1)),{handled:!0,kind:"set_service_tier"}}case"set_sandbox_mode":{const i=x(c(s.sandbox_mode),c(s.sandboxMode),c(s.value));return i&&this.setSandboxMode(i),this.callbacks.sendLocalActionResult(e.action_id,"ok",await this.buildToolbarContextResult("sandbox_mode_set",!1)),{handled:!0,kind:"set_sandbox_mode"}}case"thread_compact":{r.info("codex-adapter",`thread_compact start action_id=${e.action_id} threadId=${this.threadId}`),this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","compacting"),this.clearActive());try{if(await this.ensureThreadResumed(),this.needsHistoryInjection)return r.info("codex-adapter",`thread_compact aborted: thread expired, new thread created action_id=${e.action_id}`),this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"thread_expired","The session has expired and there is no history to compact. Please send a new message and try again."),{handled:!0,kind:"thread_compact"};this.compacting=!0;const i=new Promise(n=>{this.compactionDoneResolver=n});await this.threadCompact(),this.armCompactionFallback(),await i,r.info("codex-adapter",`thread_compact done action_id=${e.action_id}`),this.callbacks.sendLocalActionResult(e.action_id,"ok")}catch(i){this.finishCompaction("compact-error");const n=i instanceof Error?i.message:String(i);/thread.*(not found|not exist|unknown|invalid)/i.test(n)||/not found.*thread/i.test(n)?(r.warn("codex-adapter",`thread_compact thread expired, resetting threadId action_id=${e.action_id} err=${n}`),this.threadId=null,this.persistThreadId(void 0),this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"thread_expired","The session has expired and there is no history to compact. Please send a new message and try again.")):(r.info("codex-adapter",`thread_compact failed action_id=${e.action_id} err=${n}`),this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,void 0,n))}return{handled:!0,kind:"thread_compact"}}case"get_context":return this.callbacks.sendLocalActionResult(e.action_id,"ok",{threadId:this.threadId,model:this.model,mode:this.collaborationMode,approvalPolicy:this.approvalPolicy,cwd:this.cwd,...await this.buildToolbarContextResult("context")}),{handled:!0,kind:"get_context"};case"exec_approve":case"file_approve":{const i=this.resolvePendingApproval(e);if(!i)return this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That approval request is no longer pending."),{handled:!0,kind:t};const n=s.decision||"allow-once",o=ve[n]??"accept";return this.sendApprovalDecision(i.requestId,o),this.pendingApprovals.delete(i.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:i.approvalId,approval_command_id:i.approvalCommandId,decision:n}),this.resumeAfterApproval(e,s,i),{handled:!0,kind:t}}case"exec_reject":case"file_reject":{const i=this.resolvePendingApproval(e);return i?(this.sendApprovalDecision(i.requestId,"deny"),this.pendingApprovals.delete(i.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:i.approvalId,approval_command_id:i.approvalCommandId,decision:"deny"}),this.resumeAfterApproval(e,s,i),{handled:!0,kind:t}):(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That approval request is no longer pending."),{handled:!0,kind:t})}case"permission_approve":{const i=this.resolvePendingApproval(e);return!i||i.kind!=="permission"?(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That permission approval request is no longer pending."),{handled:!0,kind:t}):(this.sendApprovalDecision(i.requestId,"accept"),this.pendingApprovals.delete(i.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:i.approvalId,decision:"approve"}),this.resumeAfterApproval(e,s,i),{handled:!0,kind:t})}case"permission_reject":{const i=this.resolvePendingApproval(e);return!i||i.kind!=="permission"?(this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"unknown_or_expired_approval_id","That permission approval request is no longer pending."),{handled:!0,kind:t}):(this.sendApprovalDecision(i.requestId,"deny"),this.pendingApprovals.delete(i.approvalId),this.callbacks.sendLocalActionResult(e.action_id,"ok",{approval_id:i.approvalId,decision:"deny"}),this.resumeAfterApproval(e,s,i),{handled:!0,kind:t})}case"turn_interrupt":{try{await this.cancel(this.threadId??""),this.callbacks.sendLocalActionResult(e.action_id,"ok")}catch(i){this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,void 0,i instanceof Error?i.message:String(i))}return{handled:!0,kind:"turn_interrupt"}}case"thread_rollback":{const i=Number(s.numTurns??s.num_turns??1);if(!this.threadId)return this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,"no_thread","No active thread"),{handled:!0,kind:"thread_rollback"};this.currentTurnId&&(await this.cancel(this.threadId).catch(()=>{}),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","rolled back"),this.clearActive());try{await this.threadRollback(i),this.callbacks.sendLocalActionResult(e.action_id,"ok",{numTurns:i})}catch(n){this.callbacks.sendLocalActionResult(e.action_id,"failed",void 0,void 0,n instanceof Error?n.message:String(n))}return{handled:!0,kind:"thread_rollback"}}case"get_rate_limits":{const i=this.rateLimitSnapshot,n=this.buildContextWindowSnapshot(),o=Date.now(),d=this.currentThreadTokenUsage;return i?r.info("codex-adapter",`[rate-limits] responding: primary=${i.primary.usedPercent.toFixed(1)}% window=${i.primary.windowMinutes}min resetsAt=${i.primary.resetsAt} secondary=${i.secondary.usedPercent.toFixed(1)}% window=${i.secondary.windowMinutes}min resetsAt=${i.secondary.resetsAt}`):r.info("codex-adapter","[rate-limits] responding: no rateLimitSnapshot available"),r.debug("codex-adapter",`[cp-diagnose] get_rate_limits: threadId=${this.threadId??"-"} contextWindow=${JSON.stringify(n)} tokenUsage=${JSON.stringify(d)}`),this.callbacks.sendLocalActionResult(e.action_id,"ok",{adapterType:"codex",available:i!==null,cached:!1,sampledAt:o,rateLimits:i,contextWindow:n,tokenUsage:d}),{handled:!0,kind:"get_rate_limits"}}default:return{handled:!1,kind:""}}}async buildToolbarContextResult(e,t=!0){t&&await this.refreshCodexModelOptions();const s=T(this.collaborationMode)??"default",i=this.currentModelId(),n=this.getModelOption(i),o=this.getModelOptions(),d=j,h=this.reasoningEffort??n?.defaultReasoningEffort??null,f=n?.supportedReasoningEfforts??[],m=this.effectiveServiceTier()??n?.defaultServiceTier??null,u=n?.serviceTiers??[],p=this.isExternalProvider(),g=p?[]:f,w=p?null:h,l=p?[]:u,I=p?null:m,v={outcome:e,session_context:{modelId:i,modeId:s,reasoningEffort:w,serviceTier:I,approvalPolicy:this.approvalPolicy,sandboxMode:this.sandboxMode??null},model_id:i,mode_id:s,currentModelId:i,currentModeId:s,available_models:o,available_modes:d,available_efforts:g,availableModels:o,availableModes:d,reasoning_effort:w,available_service_tiers:l,service_tier:I,sandbox_mode:this.sandboxMode??null,models:o.map(_=>({modelId:_.id,name:_.displayName})),modes:d.map(_=>({id:_.id,name:_.displayName}))};return this.rateLimitSnapshot&&(v.rate_limit_primary_percent=this.rateLimitSnapshot.primary.usedPercent,v.rate_limit_secondary_percent=this.rateLimitSnapshot.secondary.usedPercent,v.rate_limit_primary_window_min=this.rateLimitSnapshot.primary.windowMinutes,v.rate_limit_secondary_window_min=this.rateLimitSnapshot.secondary.windowMinutes),v}currentModelId(){const e=String(this.model??"").trim();if(e)return e;const t=this.getModelOptions();return t.find(s=>s.isDefault)?.id??t[0]?.id??""}getModelOption(e){const t=e.trim().toLowerCase();return this.getModelOptions().find(s=>s.id.trim().toLowerCase()===t)}getModelOptions(){const e=this.config.options??{},t=e.available_models??e.availableModels??e.models,s=_e(t),i=this.codexModelOptions,n=[],o=String(this.model??"").trim(),d=o&&!i.some(u=>u.id.trim().toLowerCase()===o.toLowerCase()),h=i.length>0?i:d?[]:ge;if(o){const u=[...i,...s].find(p=>p.id.trim().toLowerCase()===o.toLowerCase());n.push({id:o,displayName:u?.displayName??y(o),defaultReasoningEffort:u?.defaultReasoningEffort??null,supportedReasoningEfforts:u?.supportedReasoningEfforts??[],defaultServiceTier:u?.defaultServiceTier??null,serviceTiers:u?.serviceTiers??[],isDefault:u?.isDefault??!1})}n.push(...s),n.push(...h);const f=[],m=new Set;for(const u of n){const p=u.id.trim(),g=p.toLowerCase();!p||m.has(g)||(m.add(g),f.push({id:p,displayName:u.displayName.trim()||p,defaultReasoningEffort:u.defaultReasoningEffort,supportedReasoningEfforts:[...u.supportedReasoningEfforts],defaultServiceTier:u.defaultServiceTier,serviceTiers:[...u.serviceTiers],isDefault:u.isDefault}))}return f}async refreshCodexModelOptions(){if(!this.initialized||!this.alive||!this.process?.stdin||Date.now()-this.codexModelOptionsCachedAt<M.MODEL_CACHE_TTL_MS)return;const e=C(this.codexHome);if(e.baseUrl&&q(e.baseUrl)){try{const t=await Te(e.baseUrl,e.apiKey);if(t.length>0){this.codexModelOptions=H(t),this.codexModelOptionsCachedAt=Date.now(),r.info("codex-adapter",`Loaded ${t.length} models from external provider ${e.baseUrl}`);return}}catch(t){r.warn("codex-adapter",`External provider /v1/models failed (${t instanceof Error?t.message:String(t)}), falling back`)}this.codexModelOptions=[],this.codexModelOptionsCachedAt=Date.now();return}try{const t=[];let s;do{const i=await this.sendRequest("model/list",{cursor:s??null,includeHidden:!1,limit:100},15e3),n=Se(i);t.push(...n.models),s=n.nextCursor}while(s);t.length>0&&(this.codexModelOptions=H(t),this.codexModelOptionsCachedAt=Date.now())}catch(t){r.warn("codex-adapter",`Failed to load Codex model list: ${t instanceof Error?t.message:String(t)}`)}}currentTurnId=null;compacting=!1;pendingAutoCompact=!1;compactionDeferred=[];steerDeferred=[];compactingTimer=null;compactionDoneResolver=null;activeEventId=null;activeSessionId=null;visibleAgentMessageIds=new Set;hiddenAgentMessageIds=new Set;agentMessagePhases=new Map;codexSequence=0;turnError=null;deliverInboundEvent(e){if(this.compacting){r.info("codex-adapter",`Event ${e.event_id} deferred: compaction in progress`),this.compactionDeferred.push(e);return}const t=e.content;if(this.currentTurnId&&t.trim()){r.info("codex-adapter",`Event ${e.event_id}: steering active turn ${this.currentTurnId}`),this.steerTurn(t).catch(s=>{const i=s instanceof Error?s.message:String(s);if(r.info("codex-adapter",`Steer failed, falling through: ${i}`),!this.currentTurnId){this.startNewTurn(e,t);return}if(ke(i)){r.info("codex-adapter","Unsteerable turn detected, clearing active state and starting new turn"),this.clearActive(),this.startNewTurn(e,t);return}r.info("codex-adapter",`Steer failed but turn still active; deferring event ${e.event_id} for replay after turn ends`),this.steerDeferred.push(e)});return}if(this.currentTurnId){r.info("codex-adapter",`Event ${e.event_id} rejected: busy and empty content`),this.callbacks.sendEventResult(e.event_id,"failed","agent busy");return}this.startNewTurn(e,t)}startNewTurn(e,t){this.activeEventId=e.event_id,this.activeSessionId=e.session_id,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.codexSequence=0,this.startComposing();const s=this.threadId??"",i=new E(s),n={adapterSessionId:s,text:t,contextMessages:e.context_messages_json?JSON.parse(e.context_messages_json).map(o=>({senderId:o.sender_id??"unknown",content:o.content})):void 0};this.runTurn(n,i,e.event_id,e.session_id).catch(o=>{r.error("codex-adapter",`Turn failed: ${o}`),this.callbacks.sendEventResult(e.event_id,"failed",o instanceof Error?o.message:String(o)),this.clearActive()}),this.resetIdleTimer(e.event_id)}deliverStopEvent(e,t){this.activeEventId===e&&(this.cancel(this.threadId??"").catch(()=>{}),this.callbacks.sendEventResult(e,"canceled","stopped by user"),this.clearActive())}async spawnCodex(){const e={...process.env,...this.config.env},t=typeof e.PATH=="string"?e.PATH:void 0;t||r.warn("codex-adapter",`spawnCodex: env.PATH is missing! process.env.PATH=${process.env.PATH?"set":"missing"}, config.env=${JSON.stringify(this.config.env)}`);const s=F(this.config.command||"codex",t),i=xe(this.config.args);this.sandboxMode&&i.push("-c",`sandbox_mode="${this.sandboxMode}"`),r.info("codex-adapter",`Spawning: ${s} ${i.join(" ")}`);try{if(!(await $(this.cwd)).isDirectory())throw new Error(`Bound path is not a directory: ${this.cwd}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${this.cwd}. Please rebind with /grix open <valid-directory>.`):o}const n=N({codexHome:this.codexHome});if(n.locked&&!n.stale)throw new Error(`Codex session appears locked by another process: ${n.path}`);n.stale&&r.warn("codex-adapter",`Ignoring stale Codex lock: ${n.path}`);try{this.process=K(s,i,{env:e,cwd:this.cwd}).process}catch(o){throw r.error("codex-adapter",`Codex spawn threw: ${o}`),this.process=null,this.alive=!1,this.bridgeStatus="closed",o}this.process.on("error",o=>{r.error("codex-adapter",`Codex process spawn error: ${o}`),this.process=null,this.alive=!1,this.bridgeStatus="closed",this.clearIdleTimer(),this.rejectAllPending("spawn failed: "+(o instanceof Error?o.message:String(o))),this.stopComposing(),this.activeEventId&&(this.callbacks.sendEventResult(this.activeEventId,"failed","Codex process spawn failed"),this.activeEventId=null),this.emit("exit",1)}),this.process.on("exit",o=>{r.info("codex-adapter",`Codex process exited (code=${o})`),this.alive=!1,this.bridgeStatus="closed",this.clearIdleTimer(),this.rejectAllPending("process exited"),this.stopComposing(),this.emit("exit",o)}),this.process.stderr?.on("data",o=>{const d=o.toString().trim();d&&r.info("codex-adapter",`[codex stderr] ${d}`)}),this.bindStdout(),this.alive=!0}bindStdout(){if(!this.process?.stdout)return;this.process.stdout.setEncoding("utf8"),B({input:this.process.stdout}).on("line",t=>{if(!t.trim())return;let s;try{s=JSON.parse(t)}catch{r.error("codex-adapter",`Invalid JSON from Codex: ${t.slice(0,200)}`);return}if(this.bridgeStatus==="starting"&&(this.bridgeStatus="ready"),s.id!=null){const i=String(s.id),n=this.pendingRequests.get(i);if(n){clearTimeout(n.timer),this.pendingRequests.delete(i),n.resolve(s);return}}this.handleNotification(s)})}handleNotification(e){const t=e.method,s=e.params??{};switch(this.captureContextWindowFromPayload(t,s),this.activeEventId&&this.resetIdleTimer(this.activeEventId),this.activeEventId&&t&&t.startsWith("item/")&&!t.endsWith("/requestApproval")&&this.startComposing(),t){case"item/started":{const i=s.item,n=i?.id,o=i?.type,d=i?.phase;this.isLongRunningToolItemType(o)&&(this.inFlightToolOps+=1),o==="agentMessage"&&n&&(typeof d=="string"&&d.trim()?this.agentMessagePhases.set(n,d.trim().toLowerCase()):this.agentMessagePhases.delete(n),te(d)?(this.visibleAgentMessageIds.add(n),this.hiddenAgentMessageIds.delete(n)):(this.hiddenAgentMessageIds.add(n),this.visibleAgentMessageIds.delete(n)));break}case"item/agentMessage/delta":{const i=s.delta,n=s.itemId;i&&this.activeEventId&&(!n||!this.hiddenAgentMessageIds.has(n))&&!this.shouldSuppressAgentMessageDeltaDuringToolRun(n)&&this.emitCodexEvent("item/agentMessage/delta",e);break}case"item/completed":{const i=s.item,n=i?.id,o=i?.type;this.isLongRunningToolItemType(o)&&(this.inFlightToolOps=Math.max(0,this.inFlightToolOps-1)),this.activeEventId&&o!=="agentMessage"&&this.emitCodexEvent("item/completed",e),o==="agentMessage"&&n&&(this.visibleAgentMessageIds.delete(n),this.hiddenAgentMessageIds.delete(n),this.agentMessagePhases.delete(n));break}case"turn/started":{const n=s.turn?.id;n&&(this.currentTurnId=n,this.turnError=null,r.info("codex-adapter",`Turn started: ${n}`),this.startComposing());break}case"item/fileChange/requestApproval":{this.handleApprovalRequest(e,"file");break}case"item/commandExecution/requestApproval":{this.handleApprovalRequest(e,"exec");break}case"item/permissions/requestApproval":{this.activeEventId&&this.emitCodexEvent("item/permissions/requestApproval",e),this.handleApprovalRequest(e,"permission");break}case"turn/completed":{this.handleTurnCompleted(s);break}case"error":{const i=s.message??JSON.stringify(s);r.error("codex-adapter",`Codex error: ${i}`),i&&(this.turnError=i),this.activeEventId&&this.callbacks.sendRunError(this.activeEventId,this.activeSessionId??"",i);break}case"account/rateLimits/updated":{this.rateLimitSnapshot=this.parseRateLimitSnapshot(s),this.rateLimitSnapshot&&(r.info("codex-adapter",`Rate limits updated: primary=${this.rateLimitSnapshot.primary.usedPercent.toFixed(1)}% secondary=${this.rateLimitSnapshot.secondary.usedPercent.toFixed(1)}%`),this.callbacks.onRateLimitsUpdated?.(this.rateLimitSnapshot));break}case"thread/tokenUsage/updated":{const i=s.threadId,n=s.tokenUsage,o=n?.last;(!i||!n||!o)&&r.info("codex-adapter",`[cp-diagnose] token_usage payload_incomplete: method=thread/tokenUsage/updated hasThreadId=${String(!!i)} hasTokenUsage=${String(!!n)} hasLast=${String(!!o)} params=${JSON.stringify(s)}`),i&&n&&o&&(this.currentThreadTokenUsage={inputTokens:o.inputTokens??0,outputTokens:o.outputTokens??0,cacheReadInputTokens:o.cachedInputTokens??0,cacheCreationInputTokens:0},r.info("codex-adapter",`[cp-diagnose] token_usage raw thread=${i}: ${JSON.stringify(n)}`),r.info("codex-adapter",`[cp-diagnose] token_usage parsed thread=${i}: ${JSON.stringify(this.currentThreadTokenUsage)}`),this.callbacks.onTokenUsageUpdated?.(this.currentThreadTokenUsage),this.publishContextWindowSnapshot());break}default:break}}captureContextWindowFromPayload(e,t){const s=["model_context_window","modelContextWindow","context_window_size","contextWindowSize"],i=this.findNumericField(t,s);if(i==null||i<=0){const n=this.probeContextWindowFields(t,s);n.foundAnyCandidateField&&r.info("codex-adapter",`[cp-diagnose] context_window missing_or_invalid: method=${e??"-"} matchedFields=${n.matchedFields.join(",")||"-"} raw=${n.rawValues.join(",")||"-"} extracted=${String(i)}`);return}this.currentModelContextWindow!==i&&(this.currentModelContextWindow=i,r.info("codex-adapter",`[cp-diagnose] context_window updated: method=${e??"-"} size=${i}`),this.publishContextWindowSnapshot())}probeContextWindowFields(e,t){const s=[],i=[],n=o=>{if(!o||typeof o!="object")return;if(Array.isArray(o)){for(const h of o)n(h);return}const d=o;for(const h of t)Object.prototype.hasOwnProperty.call(d,h)&&(s.push(h),i.push(String(d[h])));for(const h of Object.values(d))n(h)};return n(e),{foundAnyCandidateField:s.length>0,matchedFields:s,rawValues:i}}findNumericField(e,t){if(!e||typeof e!="object")return null;const s=e;for(const i of t){const n=s[i];if(typeof n=="number"&&Number.isFinite(n))return n;if(typeof n=="string"){const o=Number(n);if(Number.isFinite(o))return o}}for(const i of Object.values(s)){if(Array.isArray(i)){for(const o of i){const d=this.findNumericField(o,t);if(d!=null)return d}continue}const n=this.findNumericField(i,t);if(n!=null)return n}return null}buildContextWindowSnapshot(){const e=this.currentModelContextWindow;if(!e||e<=0)return r.debug("codex-adapter",`[cp-diagnose] context_window snapshot skipped: invalid size=${String(e)}`),null;const t=this.currentThreadTokenUsage,s=t?t.inputTokens+t.outputTokens:null,i=s==null?null:Math.max(0,e-s),n=s==null?null:Math.min(100,s/e*100),o=s==null?null:Math.max(0,100-(n??0));return r.info("codex-adapter",`[cp-diagnose] context_window computed: threadId=${this.threadId??"-"} size=${e} usedTokens=${String(s)} usedPercentage=${String(n)}`),{sizeTokens:e,usedTokens:s,remainingTokens:i,usedPercentage:n,remainingPercentage:o,source:"codex:model_context_window"}}publishContextWindowSnapshot(){const e=this.buildContextWindowSnapshot();this.callbacks.onContextWindowUpdated?.(e),this.maybeScheduleAutoCompact(e)}maybeScheduleAutoCompact(e){const t=e?.usedPercentage;typeof t!="number"||t<D||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,r.info("codex-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${D}%, scheduling compact`),this.tryRunPendingAutoCompact())}tryRunPendingAutoCompact(){this.pendingAutoCompact&&(this.stopped||!this.alive||this.currentTurnId||this.compacting||(this.pendingAutoCompact=!1,this.execCommand("compact","",this.activeSessionId??"").then(e=>{r.info("codex-adapter",`[auto-compact] compact done status=${e.status} msg=${e.message??""}`),e.status==="failed"&&/agent busy/i.test(e.message??"")&&(this.pendingAutoCompact=!0)}).catch(e=>{r.warn("codex-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:e}`)})))}parseRateLimitSnapshot(e){try{const s=e.rateLimitsByLimitId?.codex??e.rateLimits??e,i=s.primary,n=s.secondary,o=s.credits,d=l=>{if(typeof l=="number"&&Number.isFinite(l))return l;if(typeof l=="string"){const I=Number(l);if(Number.isFinite(I))return I}return 0},h=l=>typeof l=="boolean"?l:typeof l=="string"?l.toLowerCase()==="true":!1,f=l=>{if(l==null)return null;if(typeof l=="string"){const I=l.trim();if(!I)return null;const v=Number(I);return Number.isFinite(v)&&v>1e9?new Date(v*1e3).toISOString():I}if(typeof l=="number"&&Number.isFinite(l)&&l>0){const I=l>1e12?l/1e3:l;return new Date(I*1e3).toISOString()}return null},m=l=>({usedPercent:d(l.used_percent??l.usedPercent),windowMinutes:d(l.window_minutes??l.windowDurationMins??l.windowMinutes),resetsAt:f(l.resets_at??l.resetsAt)}),u=()=>({usedPercent:0,windowMinutes:0,resetsAt:null}),p=()=>{const l=s.windows;if(!Array.isArray(l))return null;const I=l.map(b=>b&&typeof b=="object"?m(b):u()).filter(b=>b.windowMinutes>0);if(I.length===0)return null;const v=[...I].sort((b,W)=>b.windowMinutes-W.windowMinutes),_=v.find(b=>Math.abs(b.windowMinutes-300)<=30)??v[0],z=v.find(b=>Math.abs(b.windowMinutes-10080)<=120)??v[v.length-1];return{primary:_,secondary:z}},w=(i||n?{primary:i?m(i):u(),secondary:n?m(n):u()}:null)??p();return w?{primary:w.primary,secondary:w.secondary,credits:o?{hasCredits:h(o.has_credits??o.hasCredits),unlimited:h(o.unlimited),balance:o.balance==null?null:d(o.balance)}:{hasCredits:!1,unlimited:!1,balance:null}}:null}catch{return null}}handleApprovalRequest(e,t){const s=e.id;if(s==null)return;const i=e.method,n=`${s}`.trim(),o=`codex_${U(this.activeSessionId??"")}_${U(n)}`;if(this.approvalPolicy==="never"){this.sendApprovalDecision(s,"accept");return}this.activeEventId&&this.emitCodexEvent(i,e),this.pendingApprovals.set(o,{approvalId:o,approvalCommandId:n,sourceEventId:this.activeEventId??"",requestId:s,kind:t}),r.info("codex-adapter",`Pending approval stored: ${o} (kind=${t})`),this.clearIdleTimer(),this.stopComposing()}resolvePendingApproval(e){const t=e.params??{},s=x(t.approval_id,t.approvalId);if(s)return this.pendingApprovals.get(s)??null;const i=x(t.approval_command_id,t.approvalCommandId);if(i){for(const[,n]of this.pendingApprovals)if(n.approvalCommandId===i)return n}return null}sendApprovalDecision(e,t){if(e==null||!this.process?.stdin)return;const s={jsonrpc:"2.0",id:e,result:{decision:t}};this.process.stdin.write(`${JSON.stringify(s)}
4
+ `)}armCompactionFallback(){this.compacting&&(this.compactingTimer&&clearTimeout(this.compactingTimer),this.compactingTimer=setTimeout(()=>{r.warn("codex-adapter","Compaction fallback fired: turn/completed not received within timeout, treating as stuck and releasing deferred events"),this.currentTurnId=null,this.finishCompaction("fallback-timeout")},me),this.compactingTimer.unref?.())}finishCompaction(e){if(!this.compacting)return;this.compacting=!1,this.compactingTimer&&(clearTimeout(this.compactingTimer),this.compactingTimer=null);const t=this.compactionDeferred.splice(0);r.info("codex-adapter",`Compaction finished (${e}); replaying ${t.length} deferred events`);for(const i of t)this.deliverInboundEvent(i);const s=this.compactionDoneResolver;this.compactionDoneResolver=null,s?.()}handleTurnCompleted(e){if(this.currentTurnId=null,this.stopComposing(),this.activeEventId){const t=this.activeEventId,s={event_id:t,session_id:this.activeSessionId??"",thread_id:this.threadId??void 0,codex_event_type:"codex",codex_method:"turn/completed",codex_sequence:++this.codexSequence,codex_payload:e,codex_at:new Date().toISOString()},i=this.turnError;this.turnError=null;const n=!!i;this.callbacks.sendCodexEventReliable?(this.clearActive({runPendingAutoCompact:!1}),this.callbacks.sendCodexEventReliable(s).then(()=>{this.callbacks.sendEventResult(t,n?"failed":"responded",n?i??void 0:void 0),this.tryRunPendingAutoCompact()}).catch(o=>{r.error("codex-adapter",`sendCodexEventReliable failed event=${t}: ${o}`),this.callbacks.sendCodexEvent(s),this.callbacks.sendEventResult(t,n?"failed":"responded",n?i??void 0:void 0),this.tryRunPendingAutoCompact()})):(this.callbacks.sendCodexEvent(s),this.callbacks.sendEventResult(t,n?"failed":"responded",n?i??void 0:void 0),this.clearActive())}this.compacting&&this.finishCompaction("turn-completed")}emitCodexEvent(e,t){this.activeEventId&&this.callbacks.sendCodexEvent({event_id:this.activeEventId,session_id:this.activeSessionId??"",thread_id:this.threadId??void 0,codex_event_type:"codex",codex_method:e,codex_sequence:++this.codexSequence,codex_payload:t,codex_at:new Date().toISOString()})}startComposing(){}stopComposing(){}async sendRequest(e,t,s=3e4){return new Promise((i,n)=>{if(!this.process?.stdin){n(new Error("Codex process not available"));return}const o=++this.requestId,d={jsonrpc:"2.0",id:o,method:e,params:t??{}},h=setTimeout(()=>{this.pendingRequests.delete(String(o)),n(new Error(`Request timeout: ${e}`))},s);this.pendingRequests.set(String(o),{resolve:m=>{m.error?n(new Error(`JSON-RPC error: ${m.error.message}`)):i(m.result)},reject:n,timer:h});const f=JSON.stringify(d);this.process.stdin.write(`${f}
5
+ `)})}sendNotification(e,t){if(!this.process?.stdin)return;const s={jsonrpc:"2.0",method:e,params:t??{}};this.process.stdin.write(`${JSON.stringify(s)}
6
+ `)}async initializeHandshake(){if(this.initialized)return;const e=ee(),t=await this.sendRequest("initialize",{clientInfo:{name:"grix-connector",title:"Grix Connector",version:e},capabilities:{experimentalApi:!0}},15e3);r.info("codex-adapter",`Initialized: ${JSON.stringify(t)}`),this.sendNotification("initialized"),this.initialized=!0,await this.refreshCodexModelOptions(),this.fetchRateLimits()}fetchRateLimits(){Promise.resolve(this.sendRequest("account/rateLimits/read",void 0,1e4)).then(e=>{const t=this.parseRateLimitSnapshot(e);t&&(this.rateLimitSnapshot=t,r.info("codex-adapter",`Initial rate limits: primary=${t.primary.usedPercent.toFixed(1)}% secondary=${t.secondary.usedPercent.toFixed(1)}% credits=${t.credits.balance??"N/A"}`),this.callbacks.onRateLimitsUpdated?.(t))}).catch(e=>{r.warn("codex-adapter",`Failed to fetch rate limits: ${e instanceof Error?e.message:e}`)})}async runTurn(e,t,s,i){this.threadId||await this.createSession({cwd:this.cwd}),await this.ensureThreadResumed();const n=[],o=this.callbacks.getAgentProfile?.(),d=o?.agentName?.trim()??"",h=o?.introduction?.trim()??"";if((d||h)&&this.identityInjectedThreadId!==this.threadId){const p=["[system-identity]"];d&&p.push(`Your name is "${d}".`),h&&p.push(`Your self-introduction: ${h}`),p.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),p.push("[/system-identity]"),n.push({type:"text",text:p.join(`
7
+ `),text_elements:[]}),this.identityInjectedThreadId=this.threadId,r.info("codex-adapter",`Injected agent identity for thread=${this.threadId} name="${d}"`)}if(this.needsHistoryInjection&&i){this.needsHistoryInjection=!1;const p=await this.loadHistoryForInjection(i);for(const g of p)n.push(g);p.length>0&&r.info("codex-adapter",`Injected ${p.length} history items`)}if(e.contextMessages&&e.contextMessages.length>0){const p=e.contextMessages.map(g=>`[${g.senderId??"unknown"}]: ${g.content}`).join(`
8
8
  `);n.push({type:"text",text:`Conversation context:
9
- ${h}
9
+ ${p}
10
10
 
11
- Latest user message:`,text_elements:[]})}n.push({type:"text",text:e.text,text_elements:[]});const p={threadId:this.threadId,approvalPolicy:this.approvalPolicy,input:n,model:this.model},f=be(this.collaborationMode,this.model);f&&(p.collaborationMode=f),this.reasoningEffort&&(p.effort=this.reasoningEffort),await this.sendRequest("turn/start",p),t.emitDone({status:"completed"})}async ensureThreadResumed(){if(!(!this.threadId||!this.threadResumePending))try{const e=await this.sendRequest("thread/resume",{threadId:this.threadId,approvalPolicy:this.approvalPolicy,model:this.model,excludeTurns:!0,persistExtendedHistory:!1,...this.reasoningEffort?{effort:this.reasoningEffort}:{}});this.updateModelFromThreadResult(e),this.threadResumePending=!1}catch{this.threadId=null,this.needsHistoryInjection=!0,await this.startNewThread(),this.threadResumePending=!1}}async startNewThread(){try{const e=await this.sendRequest("thread/start",{approvalPolicy:this.approvalPolicy,model:this.model,cwd:this.cwd,experimentalRawEvents:!1,persistExtendedHistory:!1,...this.reasoningEffort?{effort:this.reasoningEffort}:{}}),t=ve(e);if(!t)throw new Error("Codex thread id is missing in thread/start result");this.threadId=t,this.updateModelFromThreadResult(e),this.threadResumePending=!1,this.persistThreadId(t)}catch{throw new Error("Failed to start Codex thread")}}updateModelFromThreadResult(e){if(this.model)return;const t=Ie(e);t&&(this.model=t,this.persistCodexContext())}persistThreadId(e){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodexThreadId(this.aibotSessionId,e)}persistCodexContext(){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodexContext(this.aibotSessionId,{modelId:this.model,modeId:y(this.collaborationMode)??void 0,reasoningEffort:this.reasoningEffort,sandboxMode:this.sandboxMode})}async loadHistoryForInjection(e){const t=this.callbacks.getConversationLog();if(!t)return[];try{const i=await t.readHistory(e,ue),s=[];for(const n of i){const o=(n.content??"").slice(0,pe);n.direction==="inbound"?s.push({type:"text",text:o,text_elements:[]}):n.direction==="outbound"&&s.push({type:"assistant",text:o,text_elements:[]})}return s}catch(i){return r.error("codex-adapter",`Failed to load history: ${i}`),[]}}async steerTurn(e){if(!this.threadId||!this.currentTurnId)throw new Error("No active turn to steer");await this.sendRequest("turn/steer",{threadId:this.threadId,expectedTurnId:this.currentTurnId,input:[{type:"text",text:e,text_elements:[]}]})}resumeAfterApproval(e,t,i){if(!this.threadId)return;const s=e.event_id?.trim()||`local_action_${e.action_id.trim()}`,n=x(t.session_id,t.sessionId)??this.activeSessionId??"";if(!n)return;const o=this.threadId,d=new S(o),u={adapterSessionId:o,text:""};this.activeEventId=s,this.activeSessionId=n,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.codexSequence=0,this.runTurn(u,d,s,n).catch(p=>{r.error("codex-adapter",`Post-approval turn failed: ${p}`),this.callbacks.sendEventResult(s,"failed",p instanceof Error?p.message:String(p)),this.clearActive()}),this.resetIdleTimer(s)}clearActive(e){const t=this.activeEventId;this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.currentTurnId=null,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.inFlightToolOps=0,this.clearIdleTimer(),this.pendingApprovals.clear(),t&&this.emit("eventDone",t),this.replaySteerDeferred(),e?.runPendingAutoCompact!==!1&&this.tryRunPendingAutoCompact()}replaySteerDeferred(){if(this.steerDeferred.length===0)return;const e=this.steerDeferred.splice(0);r.info("codex-adapter",`Replaying ${e.length} steer-deferred event(s) after turn ended`);for(const t of e)this.deliverInboundEvent(t)}resetIdleTimer(e){const t=Date.now();this.lastProgressAt=t,this.lastIdleCheckAt=t,this.idleNoProgressCount=0,this.armIdleTimer(e)}armIdleTimer(e){this.clearIdleTimer();const t=this.inFlightToolOps>0?le:ce;this.idleTimer=setTimeout(()=>{this.onIdleTimeout(e,t)},t)}async onIdleTimeout(e,t){if(this.activeEventId!==e)return;let i=!1;try{i=await this.ping(he)}catch{i=!1}if(this.activeEventId!==e)return;if(!i){r.error("codex-adapter",`Agent idle ${t/1e3}s and ping failed, declaring stuck: ${e}`),this.declareStuck(e,`agent idle ${t/1e3}s, ping failed`);return}const s=this.lastIdleCheckAt,n=this.lastProgressAt>s,o=n?!1:await this.checkSessionActivitySince(s);if(this.activeEventId===e){if(this.lastIdleCheckAt=Date.now(),n||o){o&&r.info("codex-adapter",`Idle timer: no stream output but rollout still being written, continuing: ${e}`),this.idleNoProgressCount=0,this.armIdleTimer(e);return}this.idleNoProgressCount++,this.idleNoProgressCount>=$?(r.error("codex-adapter",`Agent alive but no progress for ${this.idleNoProgressCount} idle cycles, declaring stuck: ${e}`),this.declareStuck(e,`agent alive but no progress for ${this.idleNoProgressCount} idle cycles`)):(r.warn("codex-adapter",`Idle timer fired, ping ok, no progress (${this.idleNoProgressCount}/${$}): ${e}`),this.armIdleTimer(e))}}declareStuck(e,t){this.callbacks.sendEventResult(e,"failed",t),this.clearActive(),this.emit("stuck")}async checkSessionActivitySince(e){if(!this.threadId)return!1;try{return await ne(this.threadId,e,this.codexHome)}catch{return!1}}isLongRunningToolItemType(e){if(!e)return!1;const t=e.toLowerCase();return t==="commandexecution"||t==="command_execution"||t==="item/commandexecution"||t==="filechange"||t==="file_change"||t==="item/filechange"}shouldSuppressAgentMessageDeltaDuringToolRun(e){return this.inFlightToolOps<=0?!1:(r.info("codex-adapter",`Suppressing agentMessage delta during tool run item_id=${e??"unknown"}`),!0)}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}rejectAllPending(e){for(const[,t]of this.pendingRequests)clearTimeout(t.timer),t.reject(new Error(`Request canceled: ${e}`));this.pendingRequests.clear(),this.pendingApprovals.clear()}}const ge={allow:"accept","allow-once":"accept","allow-always":"acceptForSession",deny:"deny"};function xe(a){return a&&a.length>0?[...a]:["app-server"]}function l(a){if(typeof a!="string")return;const e=a.trim();return e||void 0}function ve(a){if(!a||typeof a!="object")return null;const e=a,t=e.thread&&typeof e.thread=="object"?e.thread:void 0;return x(l(e.threadId),l(e.threadID),l(t?.id))??null}function Ie(a){return!a||typeof a!="object"?null:l(a.model)??null}function y(a){const e=a?.trim().toLowerCase();return e==="default"||e==="plan"?e:null}function be(a,e){const t=y(a),i=l(e);if(!(!t||!i))return{mode:t,settings:{model:i}}}function we(a){if(!Array.isArray(a))return[];const e=[];for(const t of a){if(typeof t=="string"){const o=t.trim();o&&e.push(Ce(o,_(o)));continue}if(!t||typeof t!="object")continue;const i=t,s=x(l(i.id),l(i.model_id),l(i.modelId),l(i.value));if(!s)continue;const n=x(l(i.displayName),l(i.display_name),l(i.name),l(i.label))??_(s);e.push({id:s,displayName:n,defaultReasoningEffort:l(i.defaultReasoningEffort)??null,supportedReasoningEfforts:k(i.supportedReasoningEfforts),isDefault:i.isDefault===!0})}return e}function D(a){const e=a.toLowerCase().replace(/\/+$/,"");return!(e==="https://api.openai.com"||e==="https://api.openai.com/v1"||e.endsWith(".openai.com"))}async function ye(a,e){const i=`${a.replace(/\/+$/,"")}/models`,s={Accept:"application/json"};e&&(s.Authorization=`Bearer ${e}`);const n=new AbortController,o=setTimeout(()=>n.abort(),1e4);try{const d=await fetch(i,{headers:s,signal:n.signal});if(!d.ok)return[];const u=await d.json();return _e(u)}finally{clearTimeout(o)}}function _e(a){if(!a||typeof a!="object")return[];const e=a,t=Array.isArray(e.data)?e.data:[],i=[];for(const s of t){if(!s||typeof s!="object")continue;const n=s,o=x(l(n.id),l(n.model));if(!o)continue;const d=x(l(n.displayName),l(n.display_name),l(n.name))??_(o);i.push({id:o,displayName:d,defaultReasoningEffort:l(n.defaultReasoningEffort)??null,supportedReasoningEfforts:k(n.supportedReasoningEfforts),isDefault:n.isDefault===!0})}return i}function Te(a){if(!a||typeof a!="object")return{models:[],nextCursor:null};const e=a,t=Array.isArray(e.data)?e.data:[],i=[];for(const s of t){if(!s||typeof s!="object")continue;const n=s;if(n.hidden===!0)continue;const o=x(l(n.id),l(n.model));if(!o)continue;const d=x(l(n.displayName),l(n.display_name),l(n.name))??_(o);i.push({id:o,displayName:d,defaultReasoningEffort:l(n.defaultReasoningEffort)??null,supportedReasoningEfforts:k(n.supportedReasoningEfforts),isDefault:n.isDefault===!0})}return{models:i,nextCursor:l(e.nextCursor)??null}}function j(a){const e=[],t=new Set;for(const i of a){const s=i.id.trim(),n=s.toLowerCase();!s||t.has(n)||(t.add(n),e.push({id:s,displayName:i.displayName.trim()||s,defaultReasoningEffort:i.defaultReasoningEffort,supportedReasoningEfforts:[...i.supportedReasoningEfforts],isDefault:i.isDefault}))}return e}function Ce(a,e){return{id:a,displayName:e,defaultReasoningEffort:null,supportedReasoningEfforts:[],isDefault:!1}}function k(a){if(!Array.isArray(a))return[];const e=[];for(const t of a){if(typeof t=="string"){const s=t.trim();s&&e.push(s);continue}if(!t||typeof t!="object")continue;const i=l(t.reasoningEffort);i&&e.push(i)}return e}function _(a){return a.split(/[-_\s]+/).filter(Boolean).map(e=>/^(gpt|codex|o\d+)$/i.test(e)?e.toUpperCase():e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function x(...a){for(const e of a)if(e!=null){if(typeof e=="string"&&!e.trim())continue;return e}}function F(a){return a.trim().replace(/[^a-zA-Z0-9._:-]+/g,"_")}class S extends E{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){r.warn("codex-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}function ke(a){return["cannot steer a compact turn","turn not found","turn already completed","no active turn"].some(t=>a.includes(t))}export{q as CodexAdapter};
11
+ Latest user message:`,text_elements:[]})}n.push({type:"text",text:e.text,text_elements:[]});const f={threadId:this.threadId,approvalPolicy:this.approvalPolicy,input:n,model:this.model},m=we(this.collaborationMode,this.model);m&&(f.collaborationMode=m),this.reasoningEffort&&(f.effort=this.reasoningEffort);const u=this.effectiveServiceTier();u&&(f.serviceTier=u),await this.sendRequest("turn/start",f),t.emitDone({status:"completed"})}async ensureThreadResumed(){if(!(!this.threadId||!this.threadResumePending))try{const e=await this.sendRequest("thread/resume",{threadId:this.threadId,approvalPolicy:this.approvalPolicy,model:this.model,excludeTurns:!0,persistExtendedHistory:!1,...this.reasoningEffort?{effort:this.reasoningEffort}:{},...this.effectiveServiceTier()?{serviceTier:this.effectiveServiceTier()}:{}});this.updateModelFromThreadResult(e),this.threadResumePending=!1}catch{this.threadId=null,this.needsHistoryInjection=!0,await this.startNewThread(),this.threadResumePending=!1}}async startNewThread(){try{const e=await this.sendRequest("thread/start",{approvalPolicy:this.approvalPolicy,model:this.model,cwd:this.cwd,experimentalRawEvents:!1,persistExtendedHistory:!1,...this.reasoningEffort?{effort:this.reasoningEffort}:{},...this.effectiveServiceTier()?{serviceTier:this.effectiveServiceTier()}:{}}),t=Ie(e);if(!t)throw new Error("Codex thread id is missing in thread/start result");this.threadId=t,this.updateModelFromThreadResult(e),this.threadResumePending=!1,this.persistThreadId(t)}catch{throw new Error("Failed to start Codex thread")}}updateModelFromThreadResult(e){if(this.model)return;const t=be(e);t&&(this.model=t,this.persistCodexContext())}persistThreadId(e){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodexThreadId(this.aibotSessionId,e)}persistCodexContext(){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setCodexContext(this.aibotSessionId,{modelId:this.model,modeId:T(this.collaborationMode)??void 0,reasoningEffort:this.reasoningEffort,serviceTier:this.serviceTier,sandboxMode:this.sandboxMode})}async loadHistoryForInjection(e){const t=this.callbacks.getConversationLog();if(!t)return[];try{const s=await t.readHistory(e,pe),i=[];for(const n of s){const o=(n.content??"").slice(0,fe);n.direction==="inbound"?i.push({type:"text",text:o,text_elements:[]}):n.direction==="outbound"&&i.push({type:"assistant",text:o,text_elements:[]})}return i}catch(s){return r.error("codex-adapter",`Failed to load history: ${s}`),[]}}async steerTurn(e){if(!this.threadId||!this.currentTurnId)throw new Error("No active turn to steer");await this.sendRequest("turn/steer",{threadId:this.threadId,expectedTurnId:this.currentTurnId,input:[{type:"text",text:e,text_elements:[]}]})}resumeAfterApproval(e,t,s){if(!this.threadId)return;const i=e.event_id?.trim()||`local_action_${e.action_id.trim()}`,n=x(t.session_id,t.sessionId)??this.activeSessionId??"";if(!n)return;const o=this.threadId,d=new E(o),h={adapterSessionId:o,text:""};this.activeEventId=i,this.activeSessionId=n,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.codexSequence=0,this.runTurn(h,d,i,n).catch(f=>{r.error("codex-adapter",`Post-approval turn failed: ${f}`),this.callbacks.sendEventResult(i,"failed",f instanceof Error?f.message:String(f)),this.clearActive()}),this.resetIdleTimer(i)}clearActive(e){const t=this.activeEventId;this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.currentTurnId=null,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.inFlightToolOps=0,this.clearIdleTimer(),this.pendingApprovals.clear(),t&&this.emit("eventDone",t),this.replaySteerDeferred(),e?.runPendingAutoCompact!==!1&&this.tryRunPendingAutoCompact()}replaySteerDeferred(){if(this.steerDeferred.length===0)return;const e=this.steerDeferred.splice(0);r.info("codex-adapter",`Replaying ${e.length} steer-deferred event(s) after turn ended`);for(const t of e)this.deliverInboundEvent(t)}resetIdleTimer(e){const t=Date.now();this.lastProgressAt=t,this.lastIdleCheckAt=t,this.idleNoProgressCount=0,this.armIdleTimer(e)}armIdleTimer(e){this.clearIdleTimer();const t=this.inFlightToolOps>0?he:le;this.idleTimer=setTimeout(()=>{this.onIdleTimeout(e,t)},t)}async onIdleTimeout(e,t){if(this.activeEventId!==e)return;let s=!1;try{s=await this.ping(ue)}catch{s=!1}if(this.activeEventId!==e)return;if(!s){r.error("codex-adapter",`Agent idle ${t/1e3}s and ping failed, declaring stuck: ${e}`),this.declareStuck(e,`agent idle ${t/1e3}s, ping failed`);return}const i=this.lastIdleCheckAt,n=this.lastProgressAt>i,o=n?!1:await this.checkSessionActivitySince(i);if(this.activeEventId===e){if(this.lastIdleCheckAt=Date.now(),n||o){o&&r.info("codex-adapter",`Idle timer: no stream output but rollout still being written, continuing: ${e}`),this.idleNoProgressCount=0,this.armIdleTimer(e);return}this.idleNoProgressCount++,this.idleNoProgressCount>=O?(r.error("codex-adapter",`Agent alive but no progress for ${this.idleNoProgressCount} idle cycles, declaring stuck: ${e}`),this.declareStuck(e,`agent alive but no progress for ${this.idleNoProgressCount} idle cycles`)):(r.warn("codex-adapter",`Idle timer fired, ping ok, no progress (${this.idleNoProgressCount}/${O}): ${e}`),this.armIdleTimer(e))}}declareStuck(e,t){this.callbacks.sendEventResult(e,"failed",t),this.clearActive(),this.emit("stuck")}async checkSessionActivitySince(e){if(!this.threadId)return!1;try{return await oe(this.threadId,e,this.codexHome)}catch{return!1}}isLongRunningToolItemType(e){if(!e)return!1;const t=e.toLowerCase();return t==="commandexecution"||t==="command_execution"||t==="item/commandexecution"||t==="filechange"||t==="file_change"||t==="item/filechange"}shouldSuppressAgentMessageDeltaDuringToolRun(e){return this.inFlightToolOps<=0?!1:(r.info("codex-adapter",`Suppressing agentMessage delta during tool run item_id=${e??"unknown"}`),!0)}clearIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}rejectAllPending(e){for(const[,t]of this.pendingRequests)clearTimeout(t.timer),t.reject(new Error(`Request canceled: ${e}`));this.pendingRequests.clear(),this.pendingApprovals.clear()}}const ve={allow:"accept","allow-once":"accept","allow-always":"acceptForSession",deny:"deny"};function xe(a){return a&&a.length>0?[...a]:["app-server"]}function c(a){if(typeof a!="string")return;const e=a.trim();return e||void 0}function Ie(a){if(!a||typeof a!="object")return null;const e=a,t=e.thread&&typeof e.thread=="object"?e.thread:void 0;return x(c(e.threadId),c(e.threadID),c(t?.id))??null}function be(a){return!a||typeof a!="object"?null:c(a.model)??null}function T(a){const e=a?.trim().toLowerCase();return e==="default"||e==="plan"?e:null}function we(a,e){const t=T(a),s=c(e);if(!(!t||!s))return{mode:t,settings:{model:s}}}function _e(a){if(!Array.isArray(a))return[];const e=[];for(const t of a){if(typeof t=="string"){const o=t.trim();o&&e.push(Ce(o,y(o)));continue}if(!t||typeof t!="object")continue;const s=t,i=x(c(s.id),c(s.model_id),c(s.modelId),c(s.value));if(!i)continue;const n=x(c(s.displayName),c(s.display_name),c(s.name),c(s.label))??y(i);e.push({id:i,displayName:n,defaultReasoningEffort:c(s.defaultReasoningEffort)??null,supportedReasoningEfforts:k(s.supportedReasoningEfforts),defaultServiceTier:c(s.defaultServiceTier)??null,serviceTiers:A(s.serviceTiers),isDefault:s.isDefault===!0})}return e}function q(a){const e=a.toLowerCase().replace(/\/+$/,"");return!(e==="https://api.openai.com"||e==="https://api.openai.com/v1"||e.endsWith(".openai.com"))}async function Te(a,e){const s=`${a.replace(/\/+$/,"")}/models`,i={Accept:"application/json"};e&&(i.Authorization=`Bearer ${e}`);const n=new AbortController,o=setTimeout(()=>n.abort(),1e4);try{const d=await fetch(s,{headers:i,signal:n.signal});if(!d.ok)return[];const h=await d.json();return ye(h)}finally{clearTimeout(o)}}function ye(a){if(!a||typeof a!="object")return[];const e=a,t=Array.isArray(e.data)?e.data:[],s=[];for(const i of t){if(!i||typeof i!="object")continue;const n=i,o=x(c(n.id),c(n.model));if(!o)continue;const d=x(c(n.displayName),c(n.display_name),c(n.name))??y(o);s.push({id:o,displayName:d,defaultReasoningEffort:c(n.defaultReasoningEffort)??null,supportedReasoningEfforts:k(n.supportedReasoningEfforts),defaultServiceTier:c(n.defaultServiceTier)??null,serviceTiers:A(n.serviceTiers),isDefault:n.isDefault===!0})}return s}function Se(a){if(!a||typeof a!="object")return{models:[],nextCursor:null};const e=a,t=Array.isArray(e.data)?e.data:[],s=[];for(const i of t){if(!i||typeof i!="object")continue;const n=i;if(n.hidden===!0)continue;const o=x(c(n.id),c(n.model));if(!o)continue;const d=x(c(n.displayName),c(n.display_name),c(n.name))??y(o);s.push({id:o,displayName:d,defaultReasoningEffort:c(n.defaultReasoningEffort)??null,supportedReasoningEfforts:k(n.supportedReasoningEfforts),defaultServiceTier:c(n.defaultServiceTier)??null,serviceTiers:A(n.serviceTiers),isDefault:n.isDefault===!0})}return{models:s,nextCursor:c(e.nextCursor)??null}}function H(a){const e=[],t=new Set;for(const s of a){const i=s.id.trim(),n=i.toLowerCase();!i||t.has(n)||(t.add(n),e.push({id:i,displayName:s.displayName.trim()||i,defaultReasoningEffort:s.defaultReasoningEffort,supportedReasoningEfforts:[...s.supportedReasoningEfforts],defaultServiceTier:s.defaultServiceTier,serviceTiers:[...s.serviceTiers],isDefault:s.isDefault}))}return e}function Ce(a,e){return{id:a,displayName:e,defaultReasoningEffort:null,supportedReasoningEfforts:[],defaultServiceTier:null,serviceTiers:[],isDefault:!1}}function k(a){if(!Array.isArray(a))return[];const e=[];for(const t of a){if(typeof t=="string"){const i=t.trim();i&&e.push(i);continue}if(!t||typeof t!="object")continue;const s=c(t.reasoningEffort);s&&e.push(s)}return e}function A(a){if(!Array.isArray(a))return[];const e=[];for(const t of a){if(typeof t=="string"){const n=t.trim();n&&e.push({id:n,displayName:n,description:null});continue}if(!t||typeof t!="object")continue;const s=t,i=c(s.id);i&&e.push({id:i,displayName:x(c(s.name),c(s.displayName),c(s.display_name))??i,description:c(s.description)??null})}return e}function y(a){return a.split(/[-_\s]+/).filter(Boolean).map(e=>/^(gpt|codex|o\d+)$/i.test(e)?e.toUpperCase():e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function x(...a){for(const e of a)if(e!=null){if(typeof e=="string"&&!e.trim())continue;return e}}function U(a){return a.trim().replace(/[^a-zA-Z0-9._:-]+/g,"_")}class E extends P{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){r.warn("codex-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}function ke(a){return["cannot steer a compact turn","turn not found","turn already completed","no active turn"].some(t=>a.includes(t))}export{M as CodexAdapter};
@@ -1 +1 @@
1
- import{log as o}from"../core/log/index.js";import{RespawnManager as d}from"./respawn-manager.js";import{EventQueue as p}from"./event-queue.js";class c extends Error{constructor(e){super(`adapter pool full (maxSize=${e})`),this.name="PoolFullError"}}const v=6e4;class m{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;sweepTimer=null;constructor(e,t,s){this.config=e,this.factory=t,this.sendAck=s}setEventStateHandler(e){this.onEventState=e}setQueueComposingHandler(e){this.onQueueComposing=e}setInternalErrorHandler(e){this.onInternalError=e}setEventStartedHandler(e){this.onEventStarted=e}setEventDoneHandler(e){this.onEventDone=e}setSessionActivityHandler(e){this.onSessionActivity=e}async deliverInboundEvent(e){const t=e.session_id,s=this.slots.get(t);if(s){s.startPromise&&await s.startPromise,s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&o.info("adapter-pool",`Holding event ${e.event_id} in paused queue for restarting slot ${t}`),this.submitToSlotQueue(s,e);return}if(this.slots.size>=this.config.maxPoolSize&&!this.evictDeadSlot()&&!this.evictIdleSlot())throw new c(this.config.maxPoolSize);this.sendAck(e.event_id,e.session_id);const r=this.createSlot(t);try{await r.startPromise}catch(n){throw n}this.submitToSlotQueue(r,e)}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&o.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);s&&s.eventQueue.complete(e)}cancelEvent(e,t){const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}removeQueuedEvent(e,t){const s=this.slots.get(e);return s?s.eventQueue.removeQueued(t):!1}clearQueue(e){const t=this.slots.get(e);return t?t.eventQueue.clear(e):[]}drainAllQueuedEvents(){const e=[];for(const t of this.slots.values())e.push(...t.eventQueue.drainQueuedForSession(t.sessionId));return e}drainQueuedForSession(e){if(!this.config.eventQueue)return[];const t=this.slots.get(e);return t?t.eventQueue.drainQueuedForSession(e):[]}getQueueSnapshot(e){if(!this.config.eventQueue)return null;const t=this.slots.get(e);return t?t.eventQueue.snapshot(e):null}deliverStopEvent(e,t){if(t){const s=this.slots.get(t);o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent (broadcast, no session) event=${e} slots=${this.slots.size}`);for(const s of this.slots.values())s.adapter.deliverStopEvent(e)}collectActiveEventIds(){const e=[];for(const t of this.slots.values()){const s=t.adapter.getActiveEventIds;typeof s=="function"&&e.push(...s.call(t.adapter))}return e}clearActiveEventsForShutdown(){for(const e of this.slots.values()){const t=e.adapter.clearActiveEventForShutdown;typeof t=="function"&&t.call(e.adapter)}}async deliverLocalAction(e,t){const s=String((e.params??{}).session_id??"");let r=s?this.slots.get(s):void 0;if(!r&&s&&t?.autoCreateSlot&&!this.stopped&&(this.slots.size>=this.config.maxPoolSize&&(this.evictDeadSlot()||this.evictIdleSlotSync()),this.slots.size<this.config.maxPoolSize)){o.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const i=this.createSlot(s);try{await i.startPromise,r=i}catch(u){o.error("adapter-pool",`Failed to auto-create slot for ${s}: ${u}`)}}return r?.adapter?.handleLocalAction?(r.lastActivityAt=Date.now(),r.adapter.handleLocalAction(e)):(await Promise.allSettled([...this.slots.values()].map(i=>i.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(i=>i.status==="fulfilled"&&i.value.handled)?.value??{handled:!1,kind:""}}getOrCreateSlot(e){const t=this.slots.get(e);if(t)return t;if(!this.stopped)return this.createSlot(e)}getSlot(e){return this.slots.get(e)}getAllSlots(){return[...this.slots.values()]}async removeSlot(e){const t=this.slots.get(e);t&&(this.onSessionActivity?.(e,!1),t.eventQueue.clear(e,"session slot removed"),t.eventQueue.destroy(),t.respawn.stopAll(),this.detachSlotEventListenersExceptDone(t),await t.adapter.stop().catch(()=>{}),t.adapter.removeAllListeners("eventDone"),this.slots.delete(e))}async stop(){this.stopped=!0,this.stopIdleSweep();const e=[...this.slots.values()];this.slots.clear(),await Promise.allSettled(e.map(async t=>{t.eventQueue.destroy(),t.respawn.stopAll(),this.detachAllSlotEventListeners(t),await t.adapter.stop().catch(()=>{})}))}startIdleSweep(){this.stopIdleSweep(),this.sweepTimer=setInterval(()=>this.sweepIdle(),v)}stopIdleSweep(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null)}sweepIdle(){const e=Date.now();for(const[t,s]of this.slots){if(s.state==="stopped"&&s.respawn.exhausted){o.info("adapter-pool",`Removing dead slot for session ${t} (sweep)`),this.removeSlot(t).catch(()=>{});continue}if(s.state==="ready"){if(s.adapter.getStatus().busy){s.lastActivityAt=e;continue}e-s.lastActivityAt<=this.config.idleTimeoutMs||(s.adapter.hasBackgroundWork?s.adapter.hasBackgroundWork().then(r=>{r?(o.info("adapter-pool",`Deferred eviction for session ${t}: background grandchild processes detected`),s.lastActivityAt=Date.now()):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(n=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${n}`)}))}).catch(()=>{o.info("adapter-pool",`Evicting idle slot for session ${t} (background probe failed)`),this.removeSlot(t).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})}):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})))}}}evictDeadSlot(){for(const[e,t]of this.slots)if(t.state==="stopped"&&t.respawn.exhausted)return o.info("adapter-pool",`Removing dead slot for session ${e} (exhausted respawn)`),this.removeSlot(e).catch(()=>{}),!0;return!1}evictIdleSlot(){let e=null;for(const t of this.slots.values())t.state==="ready"&&(t.adapter.getStatus().busy||(!e||t.lastActivityAt<e.lastActivityAt)&&(e=t));return e?(o.info("adapter-pool",`Evicting idle slot for session ${e.sessionId} (pool full)`),this.removeSlot(e.sessionId).catch(()=>{}),!0):!1}evictIdleSlotSync(){let e=null,t="";for(const[s,r]of this.slots)r.state==="ready"&&(r.adapter.getStatus().busy||(!e||r.lastActivityAt<e.lastActivityAt)&&(e=r,t=s));return!e||!t?!1:(o.info("adapter-pool",`Evicting idle slot for session ${t} (pool full, sync)`),this.slots.delete(t),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachAllSlotEventListeners(e),e.adapter.stop().catch(()=>{}),!0)}getStatus(){let e=0,t=0;for(const s of this.slots.values())s.state==="ready"&&(e++,s.adapter.getStatus().busy&&t++);return{total:this.slots.size,ready:e,busy:t}}createSlot(e){const t=this.factory(e),s=new d,r=this.createEventQueue(e),n={sessionId:e,adapter:t,respawn:s,state:"starting",lastActivityAt:Date.now(),startPromise:null,eventQueue:r};this.slots.set(e,n);const a=(async()=>{try{this.wireSlotEvents(n),await t.start(),n.state="ready"}catch(i){throw this.detachAllSlotEventListeners(n),this.slots.delete(e),i}finally{n.startPromise=null}})();return n.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(n)),n}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(o.error("adapter-pool",`Slot ${e.sessionId} adapter exited (code=${t})`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on("stuck",()=>{this.stopped||this.slots.get(e.sessionId)===e&&(o.error("adapter-pool",`Slot ${e.sessionId} adapter stuck, triggering respawn`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on?.("error",t=>{if(this.stopped)return;const s=t instanceof Error?t.message:String(t);o.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){o.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){o.warn("adapter-pool",`Slot ${e.sessionId} emitted internalError but no handler registered: event=${t.eventId} msg=${t.errorMsg}`);return}this.onInternalError(t)}}),e.adapter.on("eventStarted",(t,s)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventStarted?.(t,s)}),e.adapter.on("eventDone",t=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventDone?.(t,e.sessionId)}),e.adapter.on("sessionActivity",(t,s,r)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onSessionActivity?.(t||e.sessionId,s,r)}),e.adapter.on("pauseIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.pause(t)}),e.adapter.on("resumeIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.resume(t)})}detachAllSlotEventListeners(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("eventDone"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}detachSlotEventListenersExceptDone(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}slotRespawnCtx(e){const t=this,s=e.sessionId;return{name:`slot-${s}`,get stopped(){return t.stopped||!t.slots.has(s)},get agentAlive(){return e.adapter.isAlive()},pingAgent:r=>e.adapter.ping(r),onRespawnNeeded:async()=>{e.eventQueue.pause("restart"),this.detachAllSlotEventListeners(e),await e.adapter.stop().catch(()=>{}),e.adapter=t.factory(s),e.state="starting",t.wireSlotEvents(e),await e.adapter.start(),e.state="ready",e.respawn.resetAttempts(),e.respawn.resetHealthFailures(),e.respawn.stopSlowRetry(),e.respawn.startHealthCheck(t.slotRespawnCtx(e)),e.eventQueue.resume("compaction"),e.eventQueue.resume("barrier"),e.eventQueue.resume("restart")},onCleanupAgent:()=>{"cleanup"in e.adapter&&e.adapter.cleanup()},onAbortActiveRun:r=>{}}}createEventQueue(e){const t=this.config.eventQueue??{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5,cancelableQueued:!0,cancelableRunning:!0},s={onDeliver:r=>{const n=this.slots.get(e);n&&n.adapter.deliverInboundEvent(r)},onStateChange:(r,n,a,i)=>{this.onEventState?.(r,n,a,i)},onCancelRunning:r=>{this.deliverStopEvent(r,e)},onRejected:(r,n)=>{this.onEventState?.(r.event_id,r.session_id,"failed",{reason:n})},onComposing:(r,n,a)=>{this.onQueueComposing?.(r,n,a)}};return new p(t,s)}}export{m as AdapterPool,c as PoolFullError};
1
+ import{log as o}from"../core/log/index.js";import{RespawnManager as d}from"./respawn-manager.js";import{EventQueue as p}from"./event-queue.js";class c extends Error{constructor(e){super(`adapter pool full (maxSize=${e})`),this.name="PoolFullError"}}const v=6e4;class g{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;sweepTimer=null;constructor(e,t,s){this.config=e,this.factory=t,this.sendAck=s}setEventStateHandler(e){this.onEventState=e}setQueueComposingHandler(e){this.onQueueComposing=e}setInternalErrorHandler(e){this.onInternalError=e}setEventStartedHandler(e){this.onEventStarted=e}setEventDoneHandler(e){this.onEventDone=e}setSessionActivityHandler(e){this.onSessionActivity=e}async deliverInboundEvent(e){const t=e.session_id,s=this.slots.get(t);if(s){s.startPromise&&await s.startPromise,s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&o.info("adapter-pool",`Holding event ${e.event_id} in paused queue for restarting slot ${t}`),this.submitToSlotQueue(s,e);return}if(this.slots.size>=this.config.maxPoolSize&&!this.evictDeadSlot()&&!this.evictIdleSlot())throw new c(this.config.maxPoolSize);this.sendAck(e.event_id,e.session_id);const r=this.createSlot(t);try{await r.startPromise}catch(n){throw n}this.submitToSlotQueue(r,e)}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&o.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);s&&s.eventQueue.complete(e)}cancelEvent(e,t){const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}removeQueuedEvent(e,t){const s=this.slots.get(e);return s?s.eventQueue.removeQueued(t):!1}clearQueue(e){const t=this.slots.get(e);return t?t.eventQueue.clear(e):[]}drainAllQueuedEvents(){const e=[];for(const t of this.slots.values())e.push(...t.eventQueue.drainQueuedForSession(t.sessionId));return e}drainQueuedForSession(e){if(!this.config.eventQueue)return[];const t=this.slots.get(e);return t?t.eventQueue.drainQueuedForSession(e):[]}getQueueSnapshot(e){if(!this.config.eventQueue)return null;const t=this.slots.get(e);return t?t.eventQueue.snapshot(e):null}deliverStopEvent(e,t){if(t){const s=this.slots.get(t);o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent (broadcast, no session) event=${e} slots=${this.slots.size}`);for(const s of this.slots.values())s.adapter.deliverStopEvent(e)}collectActiveEventIds(){const e=[];for(const t of this.slots.values()){const s=t.adapter.getActiveEventIds;typeof s=="function"&&e.push(...s.call(t.adapter))}return e}clearActiveEventsForShutdown(){for(const e of this.slots.values()){const t=e.adapter.clearActiveEventForShutdown;typeof t=="function"&&t.call(e.adapter)}}async deliverLocalAction(e,t){const s=String((e.params??{}).session_id??"");let r=s?this.slots.get(s):void 0;if(!r&&s&&t?.autoCreateSlot&&!this.stopped&&(this.slots.size>=this.config.maxPoolSize&&(this.evictDeadSlot()||this.evictIdleSlotSync()),this.slots.size<this.config.maxPoolSize)){o.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const i=this.createSlot(s);try{await i.startPromise,r=i}catch(u){o.error("adapter-pool",`Failed to auto-create slot for ${s}: ${u}`)}}return r?.adapter?.handleLocalAction?(r.lastActivityAt=Date.now(),r.adapter.handleLocalAction(e)):(await Promise.allSettled([...this.slots.values()].map(i=>i.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(i=>i.status==="fulfilled"&&i.value.handled)?.value??{handled:!1,kind:""}}getOrCreateSlot(e){const t=this.slots.get(e);if(t)return t;if(!this.stopped)return this.createSlot(e)}getSlot(e){return this.slots.get(e)}getAllSlots(){return[...this.slots.values()]}async removeSlot(e){const t=this.slots.get(e);t&&(this.onSessionActivity?.(e,!1),t.eventQueue.clear(e,"session slot removed"),t.eventQueue.destroy(),t.respawn.stopAll(),this.detachSlotEventListenersExceptDone(t),await t.adapter.stop().catch(()=>{}),t.adapter.removeAllListeners("eventDone"),this.slots.delete(e))}async stop(){this.stopped=!0,this.stopIdleSweep();const e=[...this.slots.values()];this.slots.clear(),await Promise.allSettled(e.map(async t=>{t.eventQueue.destroy(),t.respawn.stopAll(),this.detachAllSlotEventListeners(t),await t.adapter.stop().catch(()=>{})}))}startIdleSweep(){this.stopIdleSweep(),this.sweepTimer=setInterval(()=>this.sweepIdle(),v)}stopIdleSweep(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null)}sweepIdle(){const e=Date.now();for(const[t,s]of this.slots){if(s.state==="stopped"&&s.respawn.exhausted){o.info("adapter-pool",`Removing dead slot for session ${t} (sweep)`),this.removeSlot(t).catch(()=>{});continue}if(s.state==="ready"){if(s.adapter.getStatus().busy){s.lastActivityAt=e;continue}e-s.lastActivityAt<=this.config.idleTimeoutMs||(s.adapter.hasBackgroundWork?s.adapter.hasBackgroundWork().then(r=>{r?(o.info("adapter-pool",`Deferred eviction for session ${t}: background grandchild processes detected`),s.lastActivityAt=Date.now()):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(n=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${n}`)}))}).catch(()=>{o.info("adapter-pool",`Evicting idle slot for session ${t} (background probe failed)`),this.removeSlot(t).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})}):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{o.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})))}}}evictDeadSlot(){for(const[e,t]of this.slots)if(t.state==="stopped"&&t.respawn.exhausted)return o.info("adapter-pool",`Removing dead slot for session ${e} (exhausted respawn)`),this.removeSlot(e).catch(()=>{}),!0;return!1}evictIdleSlot(){let e=null;for(const t of this.slots.values())t.state==="ready"&&(t.adapter.getStatus().busy||(!e||t.lastActivityAt<e.lastActivityAt)&&(e=t));return e?(o.info("adapter-pool",`Evicting idle slot for session ${e.sessionId} (pool full)`),this.removeSlot(e.sessionId).catch(()=>{}),!0):!1}evictIdleSlotSync(){let e=null,t="";for(const[s,r]of this.slots)r.state==="ready"&&(r.adapter.getStatus().busy||(!e||r.lastActivityAt<e.lastActivityAt)&&(e=r,t=s));return!e||!t?!1:(o.info("adapter-pool",`Evicting idle slot for session ${t} (pool full, sync)`),this.slots.delete(t),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachAllSlotEventListeners(e),e.adapter.stop().catch(()=>{}),!0)}getStatus(){let e=0,t=0;for(const s of this.slots.values())s.state==="ready"&&(e++,s.adapter.getStatus().busy&&t++);return{total:this.slots.size,ready:e,busy:t}}hasPendingWork(){for(const e of this.slots.values())if(e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0||e.state==="ready"&&e.adapter.getStatus().busy)return!0;return!1}createSlot(e){const t=this.factory(e),s=new d,r=this.createEventQueue(e),n={sessionId:e,adapter:t,respawn:s,state:"starting",lastActivityAt:Date.now(),startPromise:null,eventQueue:r};this.slots.set(e,n);const a=(async()=>{try{this.wireSlotEvents(n),await t.start(),n.state="ready"}catch(i){throw this.detachAllSlotEventListeners(n),this.slots.delete(e),i}finally{n.startPromise=null}})();return n.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(n)),n}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(o.error("adapter-pool",`Slot ${e.sessionId} adapter exited (code=${t})`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on("stuck",()=>{this.stopped||this.slots.get(e.sessionId)===e&&(o.error("adapter-pool",`Slot ${e.sessionId} adapter stuck, triggering respawn`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on?.("error",t=>{if(this.stopped)return;const s=t instanceof Error?t.message:String(t);o.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){o.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){o.warn("adapter-pool",`Slot ${e.sessionId} emitted internalError but no handler registered: event=${t.eventId} msg=${t.errorMsg}`);return}this.onInternalError(t)}}),e.adapter.on("eventStarted",(t,s)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventStarted?.(t,s)}),e.adapter.on("eventDone",t=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventDone?.(t,e.sessionId)}),e.adapter.on("sessionActivity",(t,s,r)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onSessionActivity?.(t||e.sessionId,s,r)}),e.adapter.on("pauseIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.pause(t)}),e.adapter.on("resumeIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.resume(t)})}detachAllSlotEventListeners(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("eventDone"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}detachSlotEventListenersExceptDone(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}slotRespawnCtx(e){const t=this,s=e.sessionId;return{name:`slot-${s}`,get stopped(){return t.stopped||!t.slots.has(s)},get agentAlive(){return e.adapter.isAlive()},pingAgent:r=>e.adapter.ping(r),onRespawnNeeded:async()=>{e.eventQueue.pause("restart"),this.detachAllSlotEventListeners(e),await e.adapter.stop().catch(()=>{}),e.adapter=t.factory(s),e.state="starting",t.wireSlotEvents(e),await e.adapter.start(),e.state="ready",e.respawn.resetAttempts(),e.respawn.resetHealthFailures(),e.respawn.stopSlowRetry(),e.respawn.startHealthCheck(t.slotRespawnCtx(e)),e.eventQueue.resume("compaction"),e.eventQueue.resume("barrier"),e.eventQueue.resume("restart")},onCleanupAgent:()=>{"cleanup"in e.adapter&&e.adapter.cleanup()},onAbortActiveRun:r=>{}}}createEventQueue(e){const t=this.config.eventQueue??{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5,cancelableQueued:!0,cancelableRunning:!0},s={onDeliver:r=>{const n=this.slots.get(e);n&&n.adapter.deliverInboundEvent(r)},onStateChange:(r,n,a,i)=>{this.onEventState?.(r,n,a,i)},onCancelRunning:r=>{this.deliverStopEvent(r,e)},onRejected:(r,n)=>{this.onEventState?.(r.event_id,r.session_id,"failed",{reason:n})},onComposing:(r,n,a)=>{this.onQueueComposing?.(r,n,a)}};return new p(t,s)}}export{g as AdapterPool,c as PoolFullError};
@@ -1,6 +1,6 @@
1
- import T from"node:path";import{realpath as j,stat as H}from"node:fs/promises";import{tmpdir as V}from"node:os";import{randomUUID as M}from"node:crypto";import{ConnectionManager as Y}from"../core/aibot/index.js";import{ClaudeAdapter as y}from"../adapter/claude/index.js";import{CodexAdapter as J}from"../adapter/codex/index.js";import{PiAdapter as X}from"../adapter/pi/index.js";import{AcpAdapter as A}from"../adapter/acp/index.js";import{OpenHumanAdapter as Z}from"../adapter/openhuman/index.js";import{CursorAdapter as I}from"../adapter/cursor/index.js";import{CodeWhaleAdapter as U}from"../adapter/codewhale/index.js";import{OpenCodeAdapter as ee}from"../adapter/opencode/index.js";import{AgyAdapter as O}from"../adapter/agy/index.js";import{getCachedAgyModels as te}from"../adapter/agy/model-list.js";import{getCachedAgyQuotaInfo as ie}from"../adapter/agy/quota.js";import{LOCAL_ACTION_ERROR_CODES as x,LOCAL_ACTION_TYPES as S,SESSION_CONTROL_ERROR_CODES as g,SESSION_CONTROL_VERBS as p,SESSION_MODE_IDS as C}from"../adapter/claude/protocol-contract.js";import{parseClaudeSessionUsage as ne}from"../adapter/claude/usage-parser.js";import{resolveSkillScanMode as se}from"../adapter/claude/skill-scanner.js";import{buildReportedSkills as oe}from"../default-skills/index.js";import{fetchAvailableModels as D,getCachedModels as Q,readSettingsEnv as re}from"../adapter/claude/model-list.js";import{parseAcpSessionUsage as ae}from"../adapter/acp/usage-parser.js";import{applyProxyEnv as de,getProxyManager as ce}from"../core/proxy/index.js";import{buildProviderEnv as le}from"../core/config/provider-env.js";import{parseCodexSessionUsage as ue}from"../adapter/codex/usage-parser.js";import{readCodexProviderSettings as he}from"../adapter/codex/codex-trust.js";import{scanCodexSessions as ge}from"../adapter/codex/session-scanner.js";import{scanClaudeSessions as me}from"../adapter/claude/session-scanner.js";import{scanAcpSessions as pe}from"../adapter/acp/session-scanner.js";import{parsePiSessionUsage as fe}from"../adapter/pi/usage-parser.js";import{SessionScanCache as L,resolveCodexLeafDirs as _e,resolveClaudeLeafDirs as ve,resolveAcpLeafDirs as be}from"./session-scan-cache.js";import{log as u,ConversationLog as Se,AgentApiPacketLog as we,BridgeEventLog as Ce}from"../core/log/index.js";import{RevokeHandler as Ae}from"./revoke-handler.js";import{AdapterPool as ke,PoolFullError as Re}from"./adapter-pool.js";import{parseSessionControlCommand as Ee,handleSessionControlCommand as q,handleSessionControlLocalAction as $e}from"./session-controller.js";import{handleAcpSetModel as W,handleAcpSetMode as N,resolveAcpInitialDefaults as ye}from"./acp-toolbar-persist.js";import{SessionBindingStore as xe}from"../core/persistence/session-binding-store.js";import{handleFileListAction as Te,handleCreateFolderAction as Le,serveLocalFile as Pe,realHomeDir as K}from"../core/files/index.js";import{getMachineName as He}from"../core/util/index.js";import{uploadReplyFileToAgentMedia as Me}from"../core/protocol/agent-api-media.js";import{AllowlistGate as Ie}from"../core/access/allowlist-gate.js";import{ActiveEventStore as De}from"../core/persistence/active-event-store.js";import{DEFAULT_CONNECTOR_RUNTIME_CONFIG as Qe,applyConnectorRuntimeConfigPatch as Be,extractConnectorRuntimeConfigPatch as Fe}from"./runtime-config.js";import{SendController as Ue}from"./send-controller.js";import{queryProviderQuota as z}from"../core/provider-quota/index.js";import{queryKiroQuota as B}from"../core/provider-quota/kiro.js";import{buildToolUseCard as E,buildToolResultCard as $,buildLocalGrixCardLink as Oe}from"./tool-card-utils.js";import{DeferredEventManager as qe}from"./deferred-events.js";import{buildAgentProbeResult as We,PROBE_CACHE_TTL_STATIC_MS as Ne,PROBE_CACHE_TTL_FULL_MS as Ke}from"./probe-helper.js";const ze=600*1e3,Ge=60*1e3,G=30*1e3,je=10*1e3,P=new Set(["claude","acp","agy","cursor","codex"]),Ve=new Set(["claude","codex","cursor","codewhale","opencode","pi","openhuman","agy","acp"]),F=3;class zt{config;name;aibotHandle;aibotConfig;pool;stopped=!1;revokeHandler=new Ae;sessionBindings=new Map;deferredMgr;sendCtrl=new Ue(Qe);bindingStore;globalConfigStore;upgradeTrigger=null;agentDeletedHandler=null;shareSetHandler=null;providerConfigHandler=null;agentProfile={agentName:"",introduction:""};allowlistGate;activeEventStore;cachedRateLimits=null;cachedRateLimitsSampledAtMs=null;cachedCodexContextWindow=null;cachedCodexTokenUsage=null;cachedCodexUsageSampledAtMs=null;cachedAcpContextWindow=null;cachedAcpContextWindowSampledAtMs=null;cachedClaudeRateLimitState=null;cachedProviderQuota=null;cachedProviderQuotaSampledAtMs=null;claudeWorkerStatus=new Map;lastReportedSkillsHash="";conversationLog=null;packetLog=null;kiroQuotaTimer=null;eventSessionIndex=new Map;inflightEvents=new Map;restartCount=new Map;selfDrivenSessions=new Set;selfDrivenLabels=new Map;probeCache=new Map;sessionScanCache;isRateLimitsCacheFresh(e){if(!Number.isFinite(e))return!1;const o=Number(e);return o>0&&Date.now()-o<=Ge}async maybeQueryProviderQuota(){if(this.config.aibot.clientType==="kiro"){if(this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota)return this.cachedProviderQuota;try{const t=await B();return this.cachedProviderQuota=t,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[provider-quota] kiro queried: success=${t.success}`+(t.balance?` balance=${t.balance.remaining} ${t.balance.unit}`:"")+(t.planName?` plan=${t.planName}`:"")+(t.error?` error=${t.error}`:"")),t}catch(t){return u.warn(this.name,`[provider-quota] kiro query failed: ${t instanceof Error?t.message:String(t)}`),null}}let e=this.config.providerBaseUrl,o=this.config.providerApiKey;if((!e||!o)&&(this.config.adapterType??"acp")==="claude"){const t=re();e||(e=(t.ANTHROPIC_BASE_URL??"").trim()||void 0),o||(o=(t.ANTHROPIC_API_KEY??"").trim()||(t.ANTHROPIC_AUTH_TOKEN??"").trim()||void 0)}if((!e||!o)&&(this.config.adapterType??"acp")==="codex"){const t=he();!e&&t.baseUrl&&(e=t.baseUrl),!o&&t.apiKey&&(o=t.apiKey)}if(!e||!o)return null;if(this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota)return this.cachedProviderQuota;try{const t=await z(e,o);return this.cachedProviderQuota=t,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[provider-quota] queried: provider=${t.provider} success=${t.success}`+(t.tiers.length>0?` tiers=${t.tiers.map(n=>`${n.name}=${n.usedPercent}%`).join(",")}`:"")+(t.balance?` balance=${t.balance.remaining} ${t.balance.unit}`:"")+(t.error?` error=${t.error}`:"")),t}catch(t){return u.warn(this.name,`[provider-quota] query failed: ${t instanceof Error?t.message:String(t)}`),null}}startKiroQuotaTimer(){this.kiroQuotaTimer||(this.kiroQuotaTimer=setInterval(()=>{if(this.stopped){this.stopKiroQuotaTimer();return}this.refreshAndPushKiroQuota().catch(()=>{})},G),u.info(this.name,`[kiro-quota-timer] started (interval=${G}ms)`))}stopKiroQuotaTimer(){this.kiroQuotaTimer&&(clearInterval(this.kiroQuotaTimer),this.kiroQuotaTimer=null,u.info(this.name,"[kiro-quota-timer] stopped"))}async refreshAndPushKiroQuota(){try{const e=await B();this.cachedProviderQuota=e,this.cachedProviderQuotaSampledAtMs=Date.now(),e.success&&this.pushKiroQuotaToBindings(e)}catch{}}pushKiroQuotaToBindings(e){const o=this.providerQuotaToRateLimits(e);for(const[t,n]of this.sessionBindings.entries()){if(!n)continue;const i={provider_quota:e};o&&(i.rate_limits=o);const s=this.cachedAcpContextWindow;s&&("usedPercentage"in s?i.context_window={usedPercentage:s.usedPercentage,remainingPercentage:100-s.usedPercentage}:i.context_window=s),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:n,meta:i})}}getFreshClaudeRateLimitState(){const e=this.cachedClaudeRateLimitState;return e&&this.isRateLimitsCacheFresh(e.sampledAt)?e:null}getFreshCodexGlobalRateLimitCache(){const e=this.cachedRateLimits,o=this.cachedCodexContextWindow,t=this.cachedCodexTokenUsage;return{sampledAt:Math.max(this.cachedRateLimitsSampledAtMs??0,this.cachedCodexUsageSampledAtMs??0)||null,rateLimits:e,contextWindow:o,tokenUsage:t,hasData:!!(e||o||t)}}getStatus(){const e=this.pool?.getStatus()??{total:0,ready:0,busy:0};return{name:this.name,alive:!this.stopped,busy:e.busy>0,exhausted:this.pool?[...this.pool.getAllSlots()].some(o=>o.respawn.exhausted):!1,adapterType:this.config.adapterType??"acp",clientType:this.config.aibot.clientType,pool:e}}async probe(e={}){const o=e.conversation?"full":"static",t=e.conversation?Ke:Ne;if(!e.fresh){const a=this.probeCache.get(o);if(a&&Date.now()-a.sampledAt<t)return{...a.result,cached:!0}}const n=this.config.adapterType??"acp",i=this.createAdapter(n,"__probe__"),s=e.conversation&&n==="acp"?()=>this.runAcpConversationProbe(i,e.timeoutMs??1e4):void 0;let r;try{r=await We({adapter:i,agentName:this.name,clientType:this.config.aibot.clientType,adapterType:n,providerBaseUrl:this.config.providerBaseUrl??null,opts:e,launchConversationProbe:s})}finally{i.stop().catch(()=>{})}return this.probeCache.set(o,{result:r,sampledAt:r.probed_at}),r}async runAcpConversationProbe(e,o){const t=Date.now(),n="__probe__",i=`probe-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,s=this.config.agent.cwd||V(),r=()=>Date.now()-t;try{await e.start()}catch(h){return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_failed",message:`start failed: ${h instanceof Error?h.message:String(h)}`}}}if(!e.isAlive())return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"process_not_started",message:"agent process not alive"}};if(e instanceof A)try{await e.bindSession(n,s)}catch{}let a=null;const d=new Promise(h=>{a=m=>{m===i&&h()},e.on("eventDone",a)});e.deliverInboundEvent({event_id:i,session_id:n,content:"ping",msg_id:i}),e.deliverStopEvent(i,n);let c=null;const l=await Promise.race([d.then(()=>!1),new Promise(h=>{c=setTimeout(()=>h(!0),o)})]);return c&&clearTimeout(c),a&&e.removeListener("eventDone",a),l?{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_timeout",message:`no eventDone within ${o}ms`}}:{attempted:!0,ok:!0,latency_ms:r()}}constructor(e,o){this.config=e,this.name=e.name;const t=e.adapterType??"acp";this.aibotConfig={...e.aibot,...t==="claude"?{localActions:e.aibot.localActions??["session_control","claude_interaction_reply","get_session_usage","get_rate_limits","set_model","set_mode","thread_compact","get_agent_global_config"]}:{}},e.eventQueue&&(this.aibotConfig.concurrency={max_concurrent:e.eventQueue.maxConcurrent,max_queued:e.eventQueue.maxQueued,queue_timeout_ms:e.eventQueue.queueTimeoutMs,cancelable_queued:e.eventQueue.cancelableQueued,cancelable_running:e.eventQueue.cancelableRunning}),this.conversationLog=e.logDir?new Se(e.logDir):null,this.packetLog=e.logDir?new we(e.logDir):null,this.bindingStore=new xe(e.bindingsPath),this.bindingStore.load(),this.globalConfigStore=o??null,this.deferredMgr=new qe(this.name),this.allowlistGate=e.allowlistPath?new Ie(e.allowlistPath):null,this.activeEventStore=e.activeEventStorePath?new De(e.activeEventStorePath):null,t==="codex"?this.sessionScanCache=new L(ge,_e):t==="claude"?this.sessionScanCache=new L(me,ve):t==="agy"?this.sessionScanCache=new L(()=>[],()=>[]):this.sessionScanCache=new L(pe,be)}async start(){(this.config.adapterType??"acp")==="claude"&&(await D().catch(()=>{}),this.maybeQueryProviderQuota().catch(()=>{})),this.config.aibot.clientType==="kiro"&&(this.maybeQueryProviderQuota().catch(()=>{}),this.startKiroQuotaTimer()),(this.config.adapterType??"acp")==="codex"&&this.maybeQueryProviderQuota().catch(()=>{}),await this.connectAibot(),this.sendCtrl.bind(this.aibotHandle);const e=this.config.adapterType??"acp";this.pool=new ke({maxPoolSize:this.config.poolMaxSize??20,idleTimeoutMs:this.config.poolIdleTimeoutMs??18e5,eventQueue:this.config.eventQueue},o=>{const t=this.createAdapter(e,o);return t instanceof A&&t.on("acpSessionReady",n=>{this.bindingStore.setAcpSessionId(o,n),this.sessionScanCache.invalidate()}),t},(o,t)=>{this.aibotHandle.sendEventAck({event_id:o,session_id:t,received_at:Date.now()})}),this.pool.setEventStateHandler((o,t,n,i)=>{u.info(this.name,`[queue-debug] send event_state session=${t} event=${o} state=${n} queue_pos=${i?.queue_position??""} queue_total=${i?.queue_total??""}`),this.aibotHandle.sendEventState({event_id:o,session_id:t,state:n,content_preview:i?.content_preview,queue_position:i?.queue_position,queue_total:i?.queue_total,actions:i?.actions,reason:i?.reason,updated_at:Date.now()}),this.pushQueueSnapshotForSession(t),(n==="canceled"||n==="failed")&&this.aibotHandle.sendEventResult({event_id:o,status:n==="canceled"?"canceled":"failed",msg:i?.reason,updated_at:Date.now()})}),this.pool.setQueueComposingHandler((o,t,n)=>{this.aibotHandle.sendSessionActivitySet({session_id:o,kind:"composing",active:t,...t?{ttl_ms:3e4,ref_event_id:n}:{}})}),this.pool.setInternalErrorHandler(o=>{this.handleSessionInternalError(o).catch(t=>{u.error(this.name,`[recovery] handleSessionInternalError failed event=${o.eventId} session=${o.sessionId}: ${t instanceof Error?t.message:String(t)}`)})}),this.pool.setEventStartedHandler((o,t)=>{if(this.reportSessionSkills(t),this.config.adapterType!=="claude")return;this.claudeWorkerStatus.set(t,"busy");const n=this.bindingStore.get(t);n?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"busy",cwd:n.cwd,meta:this.buildClaudeToolbarMeta(t)})}),this.pool.setEventDoneHandler((o,t)=>{const n=this.config.adapterType??"acp";if(n==="claude"){this.claudeWorkerStatus.set(t,"ready");const i=this.bindingStore.get(t);i?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:i.cwd,meta:this.buildClaudeToolbarMeta(t)})}else if(n==="agy"){const i=this.bindingStore.get(t);if(i?.cwd){const s=this.buildAgyToolbarMeta(t),r=s?.available_models??[];u.info(this.name,`[agy-toolbar-diag] eventDone push binding card session=${t} model_id=${String(s?.model_id??"")} available_models=${r.length} meta_keys=${s?Object.keys(s).join(","):"<none>"}`),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:i.cwd,meta:s})}else u.info(this.name,`[agy-toolbar-diag] eventDone skip binding card: no binding cwd session=${t}`)}}),this.pool.setSessionActivityHandler((o,t,n)=>{const i=this.selfDrivenSessions.has(o),s=this.selfDrivenLabels.get(o);t?(this.selfDrivenSessions.add(o),n&&this.selfDrivenLabels.set(o,n),this.aibotHandle.sendSessionActivitySet({session_id:o,kind:"composing",active:!0,ttl_ms:9e4})):(this.selfDrivenSessions.delete(o),this.selfDrivenLabels.delete(o),i&&this.aibotHandle.sendSessionActivitySet({session_id:o,kind:"composing",active:!1})),(i!==t||t&&n!==void 0&&n!==s)&&this.pushQueueSnapshotForSession(o)}),this.pool.startIdleSweep(),u.info(this.name,`Ready (adapter: ${e}, poolMax: ${this.config.poolMaxSize??20})`)}async stop(){this.stopped=!0,this.pool?.stopIdleSweep(),this.stopKiroQuotaTimer();const e=this.pool?.collectActiveEventIds()??[];e.length>0&&this.activeEventStore&&await this.activeEventStore.save(e);for(const n of e)u.info(this.name,`Canceling active event on shutdown: ${n}`),this.sendEventResultWithCleanup(n,"canceled","process shutting down");e.length>0&&await new Promise(n=>setTimeout(n,100)),this.pool?.clearActiveEventsForShutdown();const o=this.deferredMgr.getAllDeferredEvents();for(const n of o)u.info(this.name,`Failing deferred event on shutdown: ${n.event_id}`),this.sendEventResultWithCleanup(n.event_id,"failed","process shutting down");const t=this.pool?.drainAllQueuedEvents()??[];for(const n of t)u.info(this.name,`Failing queued event on shutdown: ${n.event_id}`),this.sendEventResultWithCleanup(n.event_id,"failed","process shutting down");this.deferredMgr.clearAll(),await this.pool?.stop(),this.aibotHandle?.disconnect(),e.length>0&&this.activeEventStore&&await this.activeEventStore.save([]),this.eventSessionIndex.clear(),this.inflightEvents.clear(),this.restartCount.clear()}resolveSpawnEnv(){const e=le(this.config.agent.clientType,this.config.agent.provider),o=this.config.agent.env,t=Object.keys(e).length>0?{...e,...o}:o;return de(t)}createAdapter(e,o){switch(e){case"claude":return this.createClaudeAdapter(o);case"codex":return this.createCodexAdapter(o);case"pi":return this.createPiAdapter(o);case"openhuman":return this.createOpenHumanAdapter(o);case"codewhale":return this.createCodeWhaleAdapter(o);case"cursor":return this.createCursorAdapter(o);case"opencode":return this.createOpenCodeAdapter(o);case"agy":return this.createAgyAdapter(o);default:return this.createAcpAdapter(o)}}createCursorAdapter(e){const o={...this.config.adapterOptions??{}};o.bindingStore=this.bindingStore,o.aibotSessionId=e;const t=this.bindingStore.get(e),n=t?.modelId??this.globalConfigStore?.get(this.name)?.modelId;n&&(o.model=n),t?.modeId&&(o.mode=t.modeId);const i={sendStreamChunk:(s,r,a,d,c)=>{this.sendStreamChunkByRuntimeConfig(s,r,a,d,c)},sendEventResult:(s,r,a)=>{this.sendEventResultWithCleanup(s,r,a)},sendEventAck:(s,r)=>{this.aibotHandle.sendEventAck({event_id:s,session_id:r,received_at:Date.now()})},sendRawEventEnvelope:(s,r,a)=>{this.aibotHandle.sendMsg({event_id:s,session_id:r,msg_type:1,content:"[cursor] raw_event",extra:{channel_data:{cursor:{raw_event:a}},agent_api_origin:!0}})},agentInvoke:async(s,r)=>this.platformInvoke(s,r),sendLocalActionResult:(s,r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:s,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})}};return new I({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:o},i)}createClaudeAdapter(e){const o={sendReply:(i,s,r,a,d)=>{this.sendReplyByRuntimeConfig(i,s,r,a,d)},sendStreamChunk:(i,s,r,a,d,c,l)=>{this.sendStreamChunkByRuntimeConfig(i,s,r,a,d,c,l)},sendMedia:(i,s,r,a,d,c,l)=>{this.aibotHandle.sendMedia({event_id:i,session_id:s,content:r,msg_type:2,quoted_message_id:d||void 0,client_msg_id:c||void 0,extra:l?{media_caption:a,...l}:{media_caption:a}})},sendEventResult:(i,s,r,a)=>{this.sendEventResultWithCleanup(i,s,r,a)},sendEventAck:(i,s)=>{this.aibotHandle.sendEventAck({event_id:i,session_id:s,received_at:Date.now()})},agentInvoke:async(i,s,r)=>this.platformInvoke(i,s,r),sendLocalActionResult:(i,s,r,a,d)=>{this.aibotHandle.sendLocalActionResult({action_id:i,status:s,...r!==void 0?{result:r}:{},...a?{error_code:a}:{},...d?{error_msg:d}:{}})},sendToolUse:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,E(r,a))},sendToolResult:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,$(r,a))},getWsUrl:()=>this.config.aibot.url,getAgentId:()=>this.config.aibot.agentId,getAgentProfile:()=>this.agentProfile,getApiKey:()=>this.config.aibot.apiKey,getActiveEventCount:()=>0,getPendingPermissionCount:()=>0,getPendingElicitationCount:()=>0,sendAgentQuestionCard:(i,s,r)=>{const a=r.questions.map(c=>c.header).join(", "),d=Oe(`[Agent Question] ${r.request_id}`,"agent_question",r);this.aibotHandle.sendText({event_id:i,session_id:s,content:d,msg_type:1,extra:{card_type:"agent_question",summary_text:a}})},sendPermissionCard:i=>{this.aibotHandle.sendMsg({event_id:i.eventId,session_id:i.sessionId,client_msg_id:`perm_${M()}`,msg_type:1,content:i.toolTitle?`Permission required: ${i.toolTitle}`:"Permission request",extra:{channel_data:{execApproval:{approvalId:i.approvalId,approvalSlug:i.toolName},grix:{execApproval:{approval_command_id:i.approvalId,command:i.toolTitle||i.toolName,host:"claude"}}},agent_api_origin:!0}})},sendDirectMessage:i=>{this.aibotHandle.sendMsg({session_id:i.sessionId,msg_type:1,content:i.content,...i.clientMsgId?{client_msg_id:i.clientMsgId}:{},...i.quotedMessageId?{quoted_message_id:i.quotedMessageId}:{}})},onStatusLineUpdated:i=>{(i.rateLimits?.fiveHour||i.rateLimits?.sevenDay)&&(this.cachedClaudeRateLimitState=i);const s=this.bindingStore.get(e);s?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:this.claudeWorkerStatus.get(e)??"ready",cwd:s.cwd,meta:this.buildClaudeToolbarMeta(e)})},sendMcpFrame:i=>{this.aibotHandle.sendMcpFrame(e,i)}},t=this.config.adapterOptions??{},n={...t,sessionRuntimeResolver:()=>{const i=this.bindingStore.get(e);return{cwd:i?.cwd,modeId:i?.modeId??C.fullAuto,modelId:i?.modelId??this.globalConfigStore?.get(this.name)?.modelId,pluginDir:t.pluginDir,claudeSessionId:i?.claudeSessionId,onSessionIdAssigned:s=>{this.bindingStore.setClaudeSessionId(e,s),this.sessionScanCache.invalidate()}}}};return new y({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:n},o)}createCodexAdapter(e){let o=null;const t={sendEventResult:(s,r,a)=>{this.sendEventResultWithCleanup(s,r,a)},sendEventAck:(s,r)=>this.aibotHandle.sendEventAck({event_id:s,session_id:r,received_at:Date.now()}),sendCodexEvent:s=>{this.shouldDropCodexDisplayEvent(s.event_id,s.codex_method)||(this.aibotHandle.sendCodexEvent(s),this.logCodexEventToConversation(s))},sendCodexEventReliable:async s=>{if(!this.shouldDropCodexDisplayEvent(s.event_id,s.codex_method)){try{await this.aibotHandle.sendCodexEventReliable(s)}catch(r){u.warn("bridge",`[codex] sendCodexEventReliable ACK failed event=${s.event_id}: ${r}`)}u.info("bridge",`[codex] sendCodexEventReliable done event=${s.event_id} method=${s.codex_method}`),this.logCodexEventToConversation(s)}},sendRunError:(s,r,a)=>{this.sendStreamChunkByRuntimeConfig(s,r,`
1
+ import T from"node:path";import{realpath as j,stat as H}from"node:fs/promises";import{tmpdir as V}from"node:os";import{randomUUID as M}from"node:crypto";import{ConnectionManager as Y}from"../core/aibot/index.js";import{ClaudeAdapter as y}from"../adapter/claude/index.js";import{CodexAdapter as J}from"../adapter/codex/index.js";import{PiAdapter as X}from"../adapter/pi/index.js";import{AcpAdapter as A}from"../adapter/acp/index.js";import{OpenHumanAdapter as Z}from"../adapter/openhuman/index.js";import{CursorAdapter as I}from"../adapter/cursor/index.js";import{CodeWhaleAdapter as U}from"../adapter/codewhale/index.js";import{OpenCodeAdapter as ee}from"../adapter/opencode/index.js";import{AgyAdapter as O}from"../adapter/agy/index.js";import{getCachedAgyModels as te}from"../adapter/agy/model-list.js";import{getCachedAgyQuotaInfo as ie}from"../adapter/agy/quota.js";import{LOCAL_ACTION_ERROR_CODES as x,LOCAL_ACTION_TYPES as S,SESSION_CONTROL_ERROR_CODES as g,SESSION_CONTROL_VERBS as p,SESSION_MODE_IDS as C}from"../adapter/claude/protocol-contract.js";import{parseClaudeSessionUsage as ne}from"../adapter/claude/usage-parser.js";import{resolveSkillScanMode as se}from"../adapter/claude/skill-scanner.js";import{buildReportedSkills as oe}from"../default-skills/index.js";import{fetchAvailableModels as D,getCachedModels as Q,readSettingsEnv as re}from"../adapter/claude/model-list.js";import{parseAcpSessionUsage as ae}from"../adapter/acp/usage-parser.js";import{applyProxyEnv as de,getProxyManager as ce}from"../core/proxy/index.js";import{buildProviderEnv as le}from"../core/config/provider-env.js";import{parseCodexSessionUsage as ue}from"../adapter/codex/usage-parser.js";import{readCodexProviderSettings as he}from"../adapter/codex/codex-trust.js";import{scanCodexSessions as ge}from"../adapter/codex/session-scanner.js";import{scanClaudeSessions as me}from"../adapter/claude/session-scanner.js";import{scanAcpSessions as pe}from"../adapter/acp/session-scanner.js";import{parsePiSessionUsage as fe}from"../adapter/pi/usage-parser.js";import{SessionScanCache as L,resolveCodexLeafDirs as _e,resolveClaudeLeafDirs as ve,resolveAcpLeafDirs as be}from"./session-scan-cache.js";import{log as u,ConversationLog as Se,AgentApiPacketLog as we,BridgeEventLog as Ce}from"../core/log/index.js";import{RevokeHandler as Ae}from"./revoke-handler.js";import{AdapterPool as ke,PoolFullError as Re}from"./adapter-pool.js";import{parseSessionControlCommand as Ee,handleSessionControlCommand as q,handleSessionControlLocalAction as $e}from"./session-controller.js";import{handleAcpSetModel as W,handleAcpSetMode as N,resolveAcpInitialDefaults as ye}from"./acp-toolbar-persist.js";import{SessionBindingStore as xe}from"../core/persistence/session-binding-store.js";import{handleFileListAction as Te,handleCreateFolderAction as Le,serveLocalFile as Pe,realHomeDir as K}from"../core/files/index.js";import{getMachineName as He}from"../core/util/index.js";import{uploadReplyFileToAgentMedia as Me}from"../core/protocol/agent-api-media.js";import{AllowlistGate as Ie}from"../core/access/allowlist-gate.js";import{ActiveEventStore as De}from"../core/persistence/active-event-store.js";import{DEFAULT_CONNECTOR_RUNTIME_CONFIG as Qe,applyConnectorRuntimeConfigPatch as Be,extractConnectorRuntimeConfigPatch as Fe}from"./runtime-config.js";import{SendController as Ue}from"./send-controller.js";import{queryProviderQuota as z}from"../core/provider-quota/index.js";import{queryKiroQuota as B}from"../core/provider-quota/kiro.js";import{buildToolUseCard as E,buildToolResultCard as $,buildLocalGrixCardLink as Oe}from"./tool-card-utils.js";import{DeferredEventManager as qe}from"./deferred-events.js";import{buildAgentProbeResult as We,PROBE_CACHE_TTL_STATIC_MS as Ne,PROBE_CACHE_TTL_FULL_MS as Ke}from"./probe-helper.js";const ze=600*1e3,Ge=60*1e3,G=30*1e3,je=10*1e3,P=new Set(["claude","acp","agy","cursor","codex"]),Ve=new Set(["claude","codex","cursor","codewhale","opencode","pi","openhuman","agy","acp"]),F=3;class zt{config;name;aibotHandle;aibotConfig;pool;stopped=!1;revokeHandler=new Ae;sessionBindings=new Map;deferredMgr;sendCtrl=new Ue(Qe);bindingStore;globalConfigStore;upgradeTrigger=null;agentDeletedHandler=null;shareSetHandler=null;providerConfigHandler=null;agentProfile={agentName:"",introduction:""};allowlistGate;activeEventStore;cachedRateLimits=null;cachedRateLimitsSampledAtMs=null;cachedCodexContextWindow=null;cachedCodexTokenUsage=null;cachedCodexUsageSampledAtMs=null;cachedAcpContextWindow=null;cachedAcpContextWindowSampledAtMs=null;cachedClaudeRateLimitState=null;cachedProviderQuota=null;cachedProviderQuotaSampledAtMs=null;claudeWorkerStatus=new Map;lastReportedSkillsHash="";conversationLog=null;packetLog=null;kiroQuotaTimer=null;eventSessionIndex=new Map;inflightEvents=new Map;restartCount=new Map;selfDrivenSessions=new Set;selfDrivenLabels=new Map;probeCache=new Map;sessionScanCache;isRateLimitsCacheFresh(e){if(!Number.isFinite(e))return!1;const o=Number(e);return o>0&&Date.now()-o<=Ge}async maybeQueryProviderQuota(){if(this.config.aibot.clientType==="kiro"){if(this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota)return this.cachedProviderQuota;try{const t=await B();return this.cachedProviderQuota=t,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[provider-quota] kiro queried: success=${t.success}`+(t.balance?` balance=${t.balance.remaining} ${t.balance.unit}`:"")+(t.planName?` plan=${t.planName}`:"")+(t.error?` error=${t.error}`:"")),t}catch(t){return u.warn(this.name,`[provider-quota] kiro query failed: ${t instanceof Error?t.message:String(t)}`),null}}let e=this.config.providerBaseUrl,o=this.config.providerApiKey;if((!e||!o)&&(this.config.adapterType??"acp")==="claude"){const t=re();e||(e=(t.ANTHROPIC_BASE_URL??"").trim()||void 0),o||(o=(t.ANTHROPIC_API_KEY??"").trim()||(t.ANTHROPIC_AUTH_TOKEN??"").trim()||void 0)}if((!e||!o)&&(this.config.adapterType??"acp")==="codex"){const t=he();!e&&t.baseUrl&&(e=t.baseUrl),!o&&t.apiKey&&(o=t.apiKey)}if(!e||!o)return null;if(this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota)return this.cachedProviderQuota;try{const t=await z(e,o);return this.cachedProviderQuota=t,this.cachedProviderQuotaSampledAtMs=Date.now(),u.info(this.name,`[provider-quota] queried: provider=${t.provider} success=${t.success}`+(t.tiers.length>0?` tiers=${t.tiers.map(n=>`${n.name}=${n.usedPercent}%`).join(",")}`:"")+(t.balance?` balance=${t.balance.remaining} ${t.balance.unit}`:"")+(t.error?` error=${t.error}`:"")),t}catch(t){return u.warn(this.name,`[provider-quota] query failed: ${t instanceof Error?t.message:String(t)}`),null}}startKiroQuotaTimer(){this.kiroQuotaTimer||(this.kiroQuotaTimer=setInterval(()=>{if(this.stopped){this.stopKiroQuotaTimer();return}this.refreshAndPushKiroQuota().catch(()=>{})},G),u.info(this.name,`[kiro-quota-timer] started (interval=${G}ms)`))}stopKiroQuotaTimer(){this.kiroQuotaTimer&&(clearInterval(this.kiroQuotaTimer),this.kiroQuotaTimer=null,u.info(this.name,"[kiro-quota-timer] stopped"))}async refreshAndPushKiroQuota(){try{const e=await B();this.cachedProviderQuota=e,this.cachedProviderQuotaSampledAtMs=Date.now(),e.success&&this.pushKiroQuotaToBindings(e)}catch{}}pushKiroQuotaToBindings(e){const o=this.providerQuotaToRateLimits(e);for(const[t,n]of this.sessionBindings.entries()){if(!n)continue;const i={provider_quota:e};o&&(i.rate_limits=o);const s=this.cachedAcpContextWindow;s&&("usedPercentage"in s?i.context_window={usedPercentage:s.usedPercentage,remainingPercentage:100-s.usedPercentage}:i.context_window=s),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:n,meta:i})}}getFreshClaudeRateLimitState(){const e=this.cachedClaudeRateLimitState;return e&&this.isRateLimitsCacheFresh(e.sampledAt)?e:null}getFreshCodexGlobalRateLimitCache(){const e=this.cachedRateLimits,o=this.cachedCodexContextWindow,t=this.cachedCodexTokenUsage;return{sampledAt:Math.max(this.cachedRateLimitsSampledAtMs??0,this.cachedCodexUsageSampledAtMs??0)||null,rateLimits:e,contextWindow:o,tokenUsage:t,hasData:!!(e||o||t)}}getStatus(){const e=this.pool?.getStatus()??{total:0,ready:0,busy:0};return{name:this.name,alive:!this.stopped,busy:e.busy>0,exhausted:this.pool?[...this.pool.getAllSlots()].some(o=>o.respawn.exhausted):!1,adapterType:this.config.adapterType??"acp",clientType:this.config.aibot.clientType,pool:e}}hasPendingWork(){return this.pool?.hasPendingWork()??!1}async probe(e={}){const o=e.conversation?"full":"static",t=e.conversation?Ke:Ne;if(!e.fresh){const a=this.probeCache.get(o);if(a&&Date.now()-a.sampledAt<t)return{...a.result,cached:!0}}const n=this.config.adapterType??"acp",i=this.createAdapter(n,"__probe__"),s=e.conversation&&n==="acp"?()=>this.runAcpConversationProbe(i,e.timeoutMs??1e4):void 0;let r;try{r=await We({adapter:i,agentName:this.name,clientType:this.config.aibot.clientType,adapterType:n,providerBaseUrl:this.config.providerBaseUrl??null,opts:e,launchConversationProbe:s})}finally{i.stop().catch(()=>{})}return this.probeCache.set(o,{result:r,sampledAt:r.probed_at}),r}async runAcpConversationProbe(e,o){const t=Date.now(),n="__probe__",i=`probe-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,s=this.config.agent.cwd||V(),r=()=>Date.now()-t;try{await e.start()}catch(h){return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_failed",message:`start failed: ${h instanceof Error?h.message:String(h)}`}}}if(!e.isAlive())return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"process_not_started",message:"agent process not alive"}};if(e instanceof A)try{await e.bindSession(n,s)}catch{}let a=null;const d=new Promise(h=>{a=m=>{m===i&&h()},e.on("eventDone",a)});e.deliverInboundEvent({event_id:i,session_id:n,content:"ping",msg_id:i}),e.deliverStopEvent(i,n);let c=null;const l=await Promise.race([d.then(()=>!1),new Promise(h=>{c=setTimeout(()=>h(!0),o)})]);return c&&clearTimeout(c),a&&e.removeListener("eventDone",a),l?{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_timeout",message:`no eventDone within ${o}ms`}}:{attempted:!0,ok:!0,latency_ms:r()}}constructor(e,o){this.config=e,this.name=e.name;const t=e.adapterType??"acp";this.aibotConfig={...e.aibot,...t==="claude"?{localActions:e.aibot.localActions??["session_control","claude_interaction_reply","get_session_usage","get_rate_limits","set_model","set_mode","thread_compact","get_agent_global_config"]}:{}},e.eventQueue&&(this.aibotConfig.concurrency={max_concurrent:e.eventQueue.maxConcurrent,max_queued:e.eventQueue.maxQueued,queue_timeout_ms:e.eventQueue.queueTimeoutMs,cancelable_queued:e.eventQueue.cancelableQueued,cancelable_running:e.eventQueue.cancelableRunning}),this.conversationLog=e.logDir?new Se(e.logDir):null,this.packetLog=e.logDir?new we(e.logDir):null,this.bindingStore=new xe(e.bindingsPath),this.bindingStore.load(),this.globalConfigStore=o??null,this.deferredMgr=new qe(this.name),this.allowlistGate=e.allowlistPath?new Ie(e.allowlistPath):null,this.activeEventStore=e.activeEventStorePath?new De(e.activeEventStorePath):null,t==="codex"?this.sessionScanCache=new L(ge,_e):t==="claude"?this.sessionScanCache=new L(me,ve):t==="agy"?this.sessionScanCache=new L(()=>[],()=>[]):this.sessionScanCache=new L(pe,be)}async start(){(this.config.adapterType??"acp")==="claude"&&(await D().catch(()=>{}),this.maybeQueryProviderQuota().catch(()=>{})),this.config.aibot.clientType==="kiro"&&(this.maybeQueryProviderQuota().catch(()=>{}),this.startKiroQuotaTimer()),(this.config.adapterType??"acp")==="codex"&&this.maybeQueryProviderQuota().catch(()=>{}),await this.connectAibot(),this.sendCtrl.bind(this.aibotHandle);const e=this.config.adapterType??"acp";this.pool=new ke({maxPoolSize:this.config.poolMaxSize??20,idleTimeoutMs:this.config.poolIdleTimeoutMs??18e5,eventQueue:this.config.eventQueue},o=>{const t=this.createAdapter(e,o);return t instanceof A&&t.on("acpSessionReady",n=>{this.bindingStore.setAcpSessionId(o,n),this.sessionScanCache.invalidate()}),t},(o,t)=>{this.aibotHandle.sendEventAck({event_id:o,session_id:t,received_at:Date.now()})}),this.pool.setEventStateHandler((o,t,n,i)=>{u.info(this.name,`[queue-debug] send event_state session=${t} event=${o} state=${n} queue_pos=${i?.queue_position??""} queue_total=${i?.queue_total??""}`),this.aibotHandle.sendEventState({event_id:o,session_id:t,state:n,content_preview:i?.content_preview,queue_position:i?.queue_position,queue_total:i?.queue_total,actions:i?.actions,reason:i?.reason,updated_at:Date.now()}),this.pushQueueSnapshotForSession(t),(n==="canceled"||n==="failed")&&this.aibotHandle.sendEventResult({event_id:o,status:n==="canceled"?"canceled":"failed",msg:i?.reason,updated_at:Date.now()})}),this.pool.setQueueComposingHandler((o,t,n)=>{this.aibotHandle.sendSessionActivitySet({session_id:o,kind:"composing",active:t,...t?{ttl_ms:3e4,ref_event_id:n}:{}})}),this.pool.setInternalErrorHandler(o=>{this.handleSessionInternalError(o).catch(t=>{u.error(this.name,`[recovery] handleSessionInternalError failed event=${o.eventId} session=${o.sessionId}: ${t instanceof Error?t.message:String(t)}`)})}),this.pool.setEventStartedHandler((o,t)=>{if(this.reportSessionSkills(t),this.config.adapterType!=="claude")return;this.claudeWorkerStatus.set(t,"busy");const n=this.bindingStore.get(t);n?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"busy",cwd:n.cwd,meta:this.buildClaudeToolbarMeta(t)})}),this.pool.setEventDoneHandler((o,t)=>{const n=this.config.adapterType??"acp";if(n==="claude"){this.claudeWorkerStatus.set(t,"ready");const i=this.bindingStore.get(t);i?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:i.cwd,meta:this.buildClaudeToolbarMeta(t)})}else if(n==="agy"){const i=this.bindingStore.get(t);if(i?.cwd){const s=this.buildAgyToolbarMeta(t),r=s?.available_models??[];u.info(this.name,`[agy-toolbar-diag] eventDone push binding card session=${t} model_id=${String(s?.model_id??"")} available_models=${r.length} meta_keys=${s?Object.keys(s).join(","):"<none>"}`),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:i.cwd,meta:s})}else u.info(this.name,`[agy-toolbar-diag] eventDone skip binding card: no binding cwd session=${t}`)}}),this.pool.setSessionActivityHandler((o,t,n)=>{const i=this.selfDrivenSessions.has(o),s=this.selfDrivenLabels.get(o);t?(this.selfDrivenSessions.add(o),n&&this.selfDrivenLabels.set(o,n),this.aibotHandle.sendSessionActivitySet({session_id:o,kind:"composing",active:!0,ttl_ms:9e4})):(this.selfDrivenSessions.delete(o),this.selfDrivenLabels.delete(o),i&&this.aibotHandle.sendSessionActivitySet({session_id:o,kind:"composing",active:!1})),(i!==t||t&&n!==void 0&&n!==s)&&this.pushQueueSnapshotForSession(o)}),this.pool.startIdleSweep(),u.info(this.name,`Ready (adapter: ${e}, poolMax: ${this.config.poolMaxSize??20})`)}async stop(){this.stopped=!0,this.pool?.stopIdleSweep(),this.stopKiroQuotaTimer();const e=this.pool?.collectActiveEventIds()??[];e.length>0&&this.activeEventStore&&await this.activeEventStore.save(e);for(const n of e)u.info(this.name,`Canceling active event on shutdown: ${n}`),this.sendEventResultWithCleanup(n,"canceled","process shutting down");e.length>0&&await new Promise(n=>setTimeout(n,100)),this.pool?.clearActiveEventsForShutdown();const o=this.deferredMgr.getAllDeferredEvents();for(const n of o)u.info(this.name,`Failing deferred event on shutdown: ${n.event_id}`),this.sendEventResultWithCleanup(n.event_id,"failed","process shutting down");const t=this.pool?.drainAllQueuedEvents()??[];for(const n of t)u.info(this.name,`Failing queued event on shutdown: ${n.event_id}`),this.sendEventResultWithCleanup(n.event_id,"failed","process shutting down");this.deferredMgr.clearAll(),await this.pool?.stop(),this.aibotHandle?.disconnect(),e.length>0&&this.activeEventStore&&await this.activeEventStore.save([]),this.eventSessionIndex.clear(),this.inflightEvents.clear(),this.restartCount.clear()}resolveSpawnEnv(){const e=le(this.config.agent.clientType,this.config.agent.provider),o=this.config.agent.env,t=Object.keys(e).length>0?{...e,...o}:o;return de(t)}createAdapter(e,o){switch(e){case"claude":return this.createClaudeAdapter(o);case"codex":return this.createCodexAdapter(o);case"pi":return this.createPiAdapter(o);case"openhuman":return this.createOpenHumanAdapter(o);case"codewhale":return this.createCodeWhaleAdapter(o);case"cursor":return this.createCursorAdapter(o);case"opencode":return this.createOpenCodeAdapter(o);case"agy":return this.createAgyAdapter(o);default:return this.createAcpAdapter(o)}}createCursorAdapter(e){const o={...this.config.adapterOptions??{}};o.bindingStore=this.bindingStore,o.aibotSessionId=e;const t=this.bindingStore.get(e),n=t?.modelId??this.globalConfigStore?.get(this.name)?.modelId;n&&(o.model=n),t?.modeId&&(o.mode=t.modeId);const i={sendStreamChunk:(s,r,a,d,c)=>{this.sendStreamChunkByRuntimeConfig(s,r,a,d,c)},sendEventResult:(s,r,a)=>{this.sendEventResultWithCleanup(s,r,a)},sendEventAck:(s,r)=>{this.aibotHandle.sendEventAck({event_id:s,session_id:r,received_at:Date.now()})},sendRawEventEnvelope:(s,r,a)=>{this.aibotHandle.sendMsg({event_id:s,session_id:r,msg_type:1,content:"[cursor] raw_event",extra:{channel_data:{cursor:{raw_event:a}},agent_api_origin:!0}})},agentInvoke:async(s,r)=>this.platformInvoke(s,r),sendLocalActionResult:(s,r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:s,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})}};return new I({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:o},i)}createClaudeAdapter(e){const o={sendReply:(i,s,r,a,d)=>{this.sendReplyByRuntimeConfig(i,s,r,a,d)},sendStreamChunk:(i,s,r,a,d,c,l)=>{this.sendStreamChunkByRuntimeConfig(i,s,r,a,d,c,l)},sendMedia:(i,s,r,a,d,c,l)=>{this.aibotHandle.sendMedia({event_id:i,session_id:s,content:r,msg_type:2,quoted_message_id:d||void 0,client_msg_id:c||void 0,extra:l?{media_caption:a,...l}:{media_caption:a}})},sendEventResult:(i,s,r,a)=>{this.sendEventResultWithCleanup(i,s,r,a)},sendEventAck:(i,s)=>{this.aibotHandle.sendEventAck({event_id:i,session_id:s,received_at:Date.now()})},agentInvoke:async(i,s,r)=>this.platformInvoke(i,s,r),sendLocalActionResult:(i,s,r,a,d)=>{this.aibotHandle.sendLocalActionResult({action_id:i,status:s,...r!==void 0?{result:r}:{},...a?{error_code:a}:{},...d?{error_msg:d}:{}})},sendToolUse:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,E(r,a))},sendToolResult:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,$(r,a))},getWsUrl:()=>this.config.aibot.url,getAgentId:()=>this.config.aibot.agentId,getAgentProfile:()=>this.agentProfile,getApiKey:()=>this.config.aibot.apiKey,getActiveEventCount:()=>0,getPendingPermissionCount:()=>0,getPendingElicitationCount:()=>0,sendAgentQuestionCard:(i,s,r)=>{const a=r.questions.map(c=>c.header).join(", "),d=Oe(`[Agent Question] ${r.request_id}`,"agent_question",r);this.aibotHandle.sendText({event_id:i,session_id:s,content:d,msg_type:1,extra:{card_type:"agent_question",summary_text:a}})},sendPermissionCard:i=>{this.aibotHandle.sendMsg({event_id:i.eventId,session_id:i.sessionId,client_msg_id:`perm_${M()}`,msg_type:1,content:i.toolTitle?`Permission required: ${i.toolTitle}`:"Permission request",extra:{channel_data:{execApproval:{approvalId:i.approvalId,approvalSlug:i.toolName},grix:{execApproval:{approval_command_id:i.approvalId,command:i.toolTitle||i.toolName,host:"claude"}}},agent_api_origin:!0}})},sendDirectMessage:i=>{this.aibotHandle.sendMsg({session_id:i.sessionId,msg_type:1,content:i.content,...i.clientMsgId?{client_msg_id:i.clientMsgId}:{},...i.quotedMessageId?{quoted_message_id:i.quotedMessageId}:{}})},onStatusLineUpdated:i=>{(i.rateLimits?.fiveHour||i.rateLimits?.sevenDay)&&(this.cachedClaudeRateLimitState=i);const s=this.bindingStore.get(e);s?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:this.claudeWorkerStatus.get(e)??"ready",cwd:s.cwd,meta:this.buildClaudeToolbarMeta(e)})},sendMcpFrame:i=>{this.aibotHandle.sendMcpFrame(e,i)}},t=this.config.adapterOptions??{},n={...t,sessionRuntimeResolver:()=>{const i=this.bindingStore.get(e);return{cwd:i?.cwd,modeId:i?.modeId??C.fullAuto,modelId:i?.modelId??this.globalConfigStore?.get(this.name)?.modelId,pluginDir:t.pluginDir,claudeSessionId:i?.claudeSessionId,onSessionIdAssigned:s=>{this.bindingStore.setClaudeSessionId(e,s),this.sessionScanCache.invalidate()}}}};return new y({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:n},o)}createCodexAdapter(e){let o=null;const t={sendEventResult:(s,r,a)=>{this.sendEventResultWithCleanup(s,r,a)},sendEventAck:(s,r)=>this.aibotHandle.sendEventAck({event_id:s,session_id:r,received_at:Date.now()}),sendCodexEvent:s=>{this.shouldDropCodexDisplayEvent(s.event_id,s.codex_method)||(this.aibotHandle.sendCodexEvent(s),this.logCodexEventToConversation(s))},sendCodexEventReliable:async s=>{if(!this.shouldDropCodexDisplayEvent(s.event_id,s.codex_method)){try{await this.aibotHandle.sendCodexEventReliable(s)}catch(r){u.warn("bridge",`[codex] sendCodexEventReliable ACK failed event=${s.event_id}: ${r}`)}u.info("bridge",`[codex] sendCodexEventReliable done event=${s.event_id} method=${s.codex_method}`),this.logCodexEventToConversation(s)}},sendRunError:(s,r,a)=>{this.sendStreamChunkByRuntimeConfig(s,r,`
2
2
 
3
- Error: ${a}`,1,!1)},sendUpdateBindingCard:(s,r,a,d)=>{const c={...d??{}};if(!c.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const l=this.providerQuotaToCodexRateLimits(this.cachedProviderQuota);l&&(c.rate_limits=l.rateLimits,c.rate_limit_primary_percent=l.primaryPercent,c.rate_limit_secondary_percent=l.secondaryPercent,c.rate_limit_primary_window_min=l.primaryWindowMin,c.rate_limit_secondary_window_min=l.secondaryWindowMin)}!c.provider_quota&&this.cachedProviderQuota?.success&&(c.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:r,cwd:a,...Object.keys(c).length>0?{meta:c}:{}})},agentInvoke:async(s,r)=>this.platformInvoke(s,r),sendLocalActionResult:(s,r,a,d,c)=>this.aibotHandle.sendLocalActionResult({action_id:s,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}}),sendSessionActivitySet:(s,r,a,d)=>{this.aibotHandle.sendSessionActivitySet({session_id:s,kind:r,active:a,...d??{}})},getConversationLog:()=>this.conversationLog,getAgentProfile:()=>this.agentProfile,onRateLimitsUpdated:s=>{this.cachedRateLimits=s,this.cachedRateLimitsSampledAtMs=Date.now(),this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const r=this.bindingStore.get(e);if(r?.cwd){const a=this.cachedRateLimitsSampledAtMs,d={rate_limits:{primary:s.primary,secondary:s.secondary,sampledAt:a},rate_limit_primary_percent:s.primary.usedPercent,rate_limit_secondary_percent:s.secondary.usedPercent,rate_limit_primary_window_min:s.primary.windowMinutes,rate_limit_secondary_window_min:s.secondary.windowMinutes,...o?.getEffortMeta()};this.cachedProviderQuota?.success&&(d.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:r.cwd,meta:d})}},onContextWindowUpdated:s=>{if(!s)return;this.cachedCodexContextWindow=s,this.cachedCodexUsageSampledAtMs=Date.now();const r=this.bindingStore.get(e);r?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:r.cwd,meta:{context_window:s,...o?.getEffortMeta()}})},onTokenUsageUpdated:s=>{s&&(this.cachedCodexTokenUsage=s,this.cachedCodexUsageSampledAtMs=Date.now())}},n=this.config.adapterOptions??{},i=this.globalConfigStore?.get(this.name);return o=new J({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...n,model:this.bindingStore.getCodexModelId(e)??n.model,collaborationMode:this.bindingStore.getCodexModeId(e)??n.collaborationMode,reasoningEffort:i?.codexReasoningEffort??n.reasoningEffort,sandboxMode:i?.codexSandboxMode??n.sandboxMode,aibotSessionId:e,bindingStore:this.bindingStore}},t),o}createCodeWhaleAdapter(e){const o={sendEventResult:(n,i,s)=>{this.sendEventResultWithCleanup(n,i,s)},sendEventAck:(n,i)=>this.aibotHandle.sendEventAck({event_id:n,session_id:i,received_at:Date.now()}),sendStreamChunk:(n,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(n,i,s,r,a,d)},sendUpdateBindingCard:(n,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:i,cwd:s,...r?{meta:r}:{}}),sendLocalActionResult:(n,i,s,r,a)=>this.aibotHandle.sendLocalActionResult({action_id:n,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}}),sendSessionActivitySet:(n,i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:n,kind:i,active:s,...r??{}})},sendToolUse:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,E(s,r))},sendToolResult:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,$(s,r))},agentInvoke:async(n,i)=>this.platformInvoke(n,i),getConversationLog:()=>this.conversationLog,getAgentProfile:()=>this.agentProfile},t=this.config.adapterOptions??{};return new U({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...t,aibotSessionId:e,bindingStore:this.bindingStore}},o)}createPiAdapter(e){const o={sendEventResult:(n,i,s)=>{this.sendEventResultWithCleanup(n,i,s),u.info("bridge",`[pi] sendEventResult event=${n} status=${i}`)},sendEventAck:(n,i)=>this.aibotHandle.sendEventAck({event_id:n,session_id:i,received_at:Date.now()}),sendUpdateBindingCard:(n,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:i,cwd:s,...r?{meta:r}:{}}),agentInvoke:async(n,i)=>this.platformInvoke(n,i),sendLocalActionResult:(n,i,s,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:n,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})},sendSessionActivitySet:(n,i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:n,kind:i,active:s,...r??{}})},sendToolUse:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,E(s,r))},sendToolResult:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,$(s,r))},sendStreamChunk:(n,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(n,i,s,r,a,d),a&&u.info("bridge",`[pi] sendFinalStreamChunk event=${n} seq=${r}`)},sendFinalStreamChunkReliable:async(n,i,s,r)=>{if(this.finalizeThinking(n,i),this.resolveEventRuntimeConfig(n).responseDelivery==="single_message"&&n){this.bufferStreamChunk(n,i,"",!0);return}try{await this.aibotHandle.sendStreamChunkRequest({event_id:n,session_id:i,delta_content:"",chunk_seq:s,is_finish:!0,...r?{client_msg_id:r}:{}})}catch(d){u.warn("bridge",`[pi] sendFinalStreamChunkReliable ACK failed event=${n}: ${d}`)}u.info("bridge",`[pi] sendFinalStreamChunkReliable done event=${n} seq=${s}`)},sendThinking:(n,i,s)=>{this.sendThinkingByRuntimeConfig(n,i,s)},sendRunError:(n,i,s,r,a)=>{this.sendStreamChunkByRuntimeConfig(n,i,`
3
+ Error: ${a}`,1,!1)},sendUpdateBindingCard:(s,r,a,d)=>{const c={...d??{}};if(!c.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const l=this.providerQuotaToCodexRateLimits(this.cachedProviderQuota);l&&(c.rate_limits=l.rateLimits,c.rate_limit_primary_percent=l.primaryPercent,c.rate_limit_secondary_percent=l.secondaryPercent,c.rate_limit_primary_window_min=l.primaryWindowMin,c.rate_limit_secondary_window_min=l.secondaryWindowMin)}!c.provider_quota&&this.cachedProviderQuota?.success&&(c.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:r,cwd:a,...Object.keys(c).length>0?{meta:c}:{}})},agentInvoke:async(s,r)=>this.platformInvoke(s,r),sendLocalActionResult:(s,r,a,d,c)=>this.aibotHandle.sendLocalActionResult({action_id:s,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}}),sendSessionActivitySet:(s,r,a,d)=>{this.aibotHandle.sendSessionActivitySet({session_id:s,kind:r,active:a,...d??{}})},getConversationLog:()=>this.conversationLog,getAgentProfile:()=>this.agentProfile,onRateLimitsUpdated:s=>{this.cachedRateLimits=s,this.cachedRateLimitsSampledAtMs=Date.now(),this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const r=this.bindingStore.get(e);if(r?.cwd){const a=this.cachedRateLimitsSampledAtMs,d={rate_limits:{primary:s.primary,secondary:s.secondary,sampledAt:a},rate_limit_primary_percent:s.primary.usedPercent,rate_limit_secondary_percent:s.secondary.usedPercent,rate_limit_primary_window_min:s.primary.windowMinutes,rate_limit_secondary_window_min:s.secondary.windowMinutes,...o?.getEffortMeta()};this.cachedProviderQuota?.success&&(d.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:r.cwd,meta:d})}},onContextWindowUpdated:s=>{if(!s)return;this.cachedCodexContextWindow=s,this.cachedCodexUsageSampledAtMs=Date.now();const r=this.bindingStore.get(e);r?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:r.cwd,meta:{context_window:s,...o?.getEffortMeta()}})},onTokenUsageUpdated:s=>{s&&(this.cachedCodexTokenUsage=s,this.cachedCodexUsageSampledAtMs=Date.now())}},n=this.config.adapterOptions??{},i=this.globalConfigStore?.get(this.name);return o=new J({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...n,model:this.bindingStore.getCodexModelId(e)??n.model,collaborationMode:this.bindingStore.getCodexModeId(e)??n.collaborationMode,reasoningEffort:i?.codexReasoningEffort??n.reasoningEffort,serviceTier:this.bindingStore.getCodexServiceTier(e)??n.serviceTier,sandboxMode:i?.codexSandboxMode??n.sandboxMode,aibotSessionId:e,bindingStore:this.bindingStore}},t),o}createCodeWhaleAdapter(e){const o={sendEventResult:(n,i,s)=>{this.sendEventResultWithCleanup(n,i,s)},sendEventAck:(n,i)=>this.aibotHandle.sendEventAck({event_id:n,session_id:i,received_at:Date.now()}),sendStreamChunk:(n,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(n,i,s,r,a,d)},sendUpdateBindingCard:(n,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:i,cwd:s,...r?{meta:r}:{}}),sendLocalActionResult:(n,i,s,r,a)=>this.aibotHandle.sendLocalActionResult({action_id:n,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}}),sendSessionActivitySet:(n,i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:n,kind:i,active:s,...r??{}})},sendToolUse:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,E(s,r))},sendToolResult:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,$(s,r))},agentInvoke:async(n,i)=>this.platformInvoke(n,i),getConversationLog:()=>this.conversationLog,getAgentProfile:()=>this.agentProfile},t=this.config.adapterOptions??{};return new U({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...t,aibotSessionId:e,bindingStore:this.bindingStore}},o)}createPiAdapter(e){const o={sendEventResult:(n,i,s)=>{this.sendEventResultWithCleanup(n,i,s),u.info("bridge",`[pi] sendEventResult event=${n} status=${i}`)},sendEventAck:(n,i)=>this.aibotHandle.sendEventAck({event_id:n,session_id:i,received_at:Date.now()}),sendUpdateBindingCard:(n,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:i,cwd:s,...r?{meta:r}:{}}),agentInvoke:async(n,i)=>this.platformInvoke(n,i),sendLocalActionResult:(n,i,s,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:n,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})},sendSessionActivitySet:(n,i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:n,kind:i,active:s,...r??{}})},sendToolUse:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,E(s,r))},sendToolResult:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,$(s,r))},sendStreamChunk:(n,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(n,i,s,r,a,d),a&&u.info("bridge",`[pi] sendFinalStreamChunk event=${n} seq=${r}`)},sendFinalStreamChunkReliable:async(n,i,s,r)=>{if(this.finalizeThinking(n,i),this.resolveEventRuntimeConfig(n).responseDelivery==="single_message"&&n){this.bufferStreamChunk(n,i,"",!0);return}try{await this.aibotHandle.sendStreamChunkRequest({event_id:n,session_id:i,delta_content:"",chunk_seq:s,is_finish:!0,...r?{client_msg_id:r}:{}})}catch(d){u.warn("bridge",`[pi] sendFinalStreamChunkReliable ACK failed event=${n}: ${d}`)}u.info("bridge",`[pi] sendFinalStreamChunkReliable done event=${n} seq=${s}`)},sendThinking:(n,i,s)=>{this.sendThinkingByRuntimeConfig(n,i,s)},sendRunError:(n,i,s,r,a)=>{this.sendStreamChunkByRuntimeConfig(n,i,`
4
4
 
5
5
  Error: ${s}`,r,!1,a)},getAgentProfile:()=>this.agentProfile},t=this.config.adapterOptions??{};return new X({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...t,aibotSessionId:e,bindingStore:this.bindingStore}},o)}createOpenHumanAdapter(e){const o={sendStreamChunk:(n,i,s,r,a,d)=>{this.finalizeThinking(n,i),this.sendStreamChunkByRuntimeConfig(n,i,s,r,a,d)},sendFinalStreamChunkReliable:async(n,i,s,r)=>{if(this.finalizeThinking(n,i),this.resolveEventRuntimeConfig(n).responseDelivery==="single_message"&&n){this.bufferStreamChunk(n,i,"",!0);return}try{await this.aibotHandle.sendStreamChunkRequest({event_id:n,session_id:i,delta_content:"",chunk_seq:s,is_finish:!0,...r?{client_msg_id:r}:{}})}catch(d){u.warn("bridge",`[openhuman] sendFinalStreamChunkReliable ACK failed event=${n}: ${d}`)}},sendEventResult:(n,i,s)=>{this.sendEventResultWithCleanup(n,i,s),u.info("bridge",`[openhuman] sendEventResult event=${n} status=${i}`)},sendEventAck:(n,i)=>this.aibotHandle.sendEventAck({event_id:n,session_id:i,received_at:Date.now()}),sendToolUse:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,E(s,r))},sendToolResult:(n,i,s,r)=>{this.sendToolExecutionCard(n,i,$(s,r))},sendThinking:(n,i,s)=>{this.sendThinkingByRuntimeConfig(n,i,s)},sendRunError:(n,i,s)=>{this.sendStreamChunkByRuntimeConfig(n,i,`
6
6