grix-connector 3.19.5 → 3.19.7

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,12 +1,12 @@
1
- import{execFileSync as G}from"node:child_process";import{createInterface as ce}from"node:readline";import{EventEmitter as J}from"node:events";import{stat as X}from"node:fs/promises";import{join as P,resolve as le}from"node:path";import{fileURLToPath as ue}from"node:url";import{homedir as he}from"node:os";import{syncDefaultSkillsToDir as pe}from"../../default-skills/index.js";import{resolveCommandPath as fe,spawnCommand as me,killProcessGroup as Y,hasChildProcesses as ge}from"../../core/runtime/spawn.js";import{InternalApiServer as ve}from"../../core/mcp/internal-api-server.js";import{log as a}from"../../core/log/index.js";import{resolveClientVersion as Ie}from"../../core/util/client-version.js";import{isUserVisibleAgentMessagePhase as $}from"../../core/util/codex-output-policy.js";import{SessionBindingStore as xe}from"../../core/persistence/session-binding-store.js";import{ensureCodexProjectTrusted as we,isCodexCommand as ye,readCodexProviderSettings as D}from"./codex-trust.js";import{checkCodexSessionActivity as be,inspectCodexTurnProgress as Te}from"./session-activity.js";import{findRolloutFile as _e}from"./rollout-locator.js";import{checkCodexLock as K}from"./lock-monitor.js";import{resolveCliPath as Ce,getCliVersion as Ae}from"../../core/util/cli-probe.js";import{scanSkills as Se}from"../claude/skill-scanner.js";import{normalizeCodexConfigOverrides as Ee}from"./config-overrides.js";import{prepareCodexRawCapture as Re}from"./raw-capture-hook.js";import{unavailableRawApiCaptureBoundary as ke}from"../../audit/raw-capture/capture-relay.js";const Me=60*1e3,Pe=600*1e3,$e=5*1e3,Oe=15*1e3,Z=3;function Ne(d,e){const t=Number(process.env[d]);return Number.isFinite(t)&&t>0?t:e}function Le(d){const e=Number(process.env[d]);return Number.isFinite(e)&&e>0?e:null}function De(d){if(!d||typeof d!="object"||Array.isArray(d))return;const e=d,t=typeof e.auditId=="string"?e.auditId.trim():"",i=typeof e.sessionId=="string"?e.sessionId.trim():"",s=typeof e.spoolRootDir=="string"?e.spoolRootDir.trim():"";return t&&i&&s?{auditId:t,sessionId:i,spoolRootDir:s}:void 0}const F=Ne("GRIX_CODEX_PREFLIGHT_STALL_TIMEOUT_MS",120*1e3),Fe=20,je=8e3,Q=2*1024,ee=`
2
- \u2026(truncated)`,te=80,He=120*1e3,Ue=[{id:"gpt-5.3-codex",displayName:"GPT-5.3 Codex",defaultReasoningEffort:null,supportedReasoningEfforts:[],defaultServiceTier:null,serviceTiers:[],isDefault:!0}],ie=[{id:"default",displayName:"Default"},{id:"plan",displayName:"Plan"}],qe=new Set(["rawResponse/completed","codex/rawResponse/completed","rawResponseItem/completed","codex/rawResponseItem/completed","thread/tokenUsage/updated","codex/thread/tokenUsage/updated","thread/compacted","codex/thread/compacted","turn/started","codex/turn/started","turn/completed","codex/turn/completed","item/started","codex/item/started","item/completed","codex/item/completed","error","codex/error"]);function se(d,e){const t=String(d??"").trim()||"codex",i=fe(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"?[]:[P(process.env.LOCALAPPDATA??"","npm","codex.cmd"),P(process.env.APPDATA??"","npm","codex.cmd"),P(process.env.LOCALAPPDATA??"","Programs","codex","codex.exe")],r=[...s,...n];for(const o of r)try{return require("node:fs").accessSync(o,require("node:fs").constants.X_OK),o}catch{continue}return a.warn("codex-adapter",`resolveCodexCommandPath: failed to resolve "${t}". envPath=${e?"provided":"missing (using process.env.PATH)"}. fallbacks tried: [${r.join(", ")}]`),t}class O extends J{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;activityGeneration=0;lastIdleCheckGeneration=0;idleNoProgressCount=0;inFlightToolOps=0;consecutivePingFailures=0;currentTurnStartedAt=0;preflightCheckComplete=!1;turnInactivityTimeoutMs=Le("GRIX_CODEX_TURN_INACTIVITY_TIMEOUT_MS");pendingApprovals=new Map;needsHistoryInjection=!1;lastInjectedSessionId=null;identityInputInjectedThreadId=null;legacyDeveloperInstructionsUnsupported=!1;profileIdentityRefreshPending=!1;bindingStore=null;aibotSessionId="";threadResumePending=!1;autoTrustProject="auto";codexHome;configOverrideArgs=[];rawCaptureOptions;rawCaptureRelay;rawCaptureConfigOverrides=[];rawCaptureUnavailableReason;codexModelOptions=[];codexModelOptionsCachedAt=0;static MODEL_CACHE_TTL_MS=3600*1e3;rateLimitSnapshot=null;lastNonDefaultLimitRefreshAt=0;static NON_DEFAULT_LIMIT_REFRESH_INTERVAL_MS=6e4;currentThreadTokenUsage=null;currentModelContextWindow=null;auditCaptureByEventId=new Map;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 xe?i.bindingStore:null,this.model=i.model,this.collaborationMode=i.collaborationMode,this.reasoningEffort=i.reasoningEffort,this.serviceTier=i.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 i.autoTrustProject=="boolean"?i.autoTrustProject:"auto",this.codexHome=typeof i.codexHome=="string"&&i.codexHome.trim()?i.codexHome.trim():void 0,this.configOverrideArgs=Ee(i.codexConfigOverrides,"codexConfigOverrides"),this.rawCaptureOptions=De(i.rawApiCapture),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.prepareRawApiCapture();try{await this.startInternalApiAndRegisterMcp(),await this.spawnCodex(),await this.initializeHandshake(),await this.notifyBindingReadyWithContext(),a.info("codex-adapter",`Ready (pid=${this.process?.pid})`)}catch(e){throw await this.stopRawApiCapture(),e}}async prepareRawApiCapture(){if(this.rawCaptureOptions)try{const e=await Re(this.rawCaptureOptions,{adapterType:"codex",sessionId:this.rawCaptureOptions.sessionId,cwd:this.cwd,env:{...process.env,...this.config.env},configOverrides:this.configOverrideArgs,codexHome:this.codexHome});this.rawCaptureRelay=e.relay,this.rawCaptureConfigOverrides=e.overlay.configOverrides??[],this.rawCaptureUnavailableReason=void 0,a.info("codex-adapter","Raw API audit capture relay is ready")}catch(e){this.rawCaptureRelay=void 0,this.rawCaptureConfigOverrides=[],this.rawCaptureUnavailableReason=e instanceof Error?e.message:String(e),a.warn("codex-adapter",`Raw API audit capture unavailable: ${this.rawCaptureUnavailableReason}`)}}async stopRawApiCapture(){const e=this.rawCaptureRelay;this.rawCaptureRelay=void 0,this.rawCaptureConfigOverrides=[],e&&await e.stop()}async ensureProjectTrusted(){if(this.shouldAutoTrustProject())try{const e=await we(this.cwd,{codexHome:this.codexHome});e.changed&&a.info("codex-adapter",`Trusted Codex project ${this.cwd} in ${e.configPath}`)}catch(e){const t=e instanceof Error?e.message:String(e);throw a.error("codex-adapter",`Failed to trust Codex project ${this.cwd}: ${t}`),e}}async startInternalApiAndRegisterMcp(){try{this.internalApi=new ve,this.internalApi.setInvokeHandler(async(o,c,p,h)=>this.callbacks.agentInvoke(o,c,h)),await this.internalApi.start(0),a.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=se(this.config.command||"codex",i);try{G(s,["mcp","remove",e.name],{env:t,timeout:1e4,stdio:"ignore"})}catch{}G(s,["mcp","add",e.name,"--",e.command,...e.args],{env:t,timeout:1e4,stdio:"ignore"}),a.info("codex-adapter",`Registered MCP server: ${e.name}`);const n=P(he(),".codex","skills"),r=pe(n);r.length>0&&a.info("codex-adapter",`Synced connector skills to ${n}: [${r.join(", ")}]`)}catch(e){a.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:ye(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){a.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<O.EXTERNAL_PROVIDER_CACHE_TTL_MS)return this.externalProviderCached;const t=D(this.codexHome);return this.externalProviderCached=!!(t.baseUrl&&re(t.baseUrl)),this.externalProviderCheckedAt=e,this.externalProviderCached}effectiveServiceTier(){if(!this.serviceTier||this.serviceTier.trim().toLowerCase()==="default"||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),i={},s=t?.supportedReasoningEfforts??[];s.length>0&&(i.available_efforts=s,i.reasoning_effort=this.reasoningEffort??t?.defaultReasoningEffort??null);const n=t?.serviceTiers??[];return n.length>0&&(i.available_service_tiers=n,i.service_tier=this.effectiveServiceTier()??t?.defaultServiceTier??null),i}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")Y(e,"SIGTERM"),await Promise.race([new Promise(i=>{e.once("exit",()=>i(!0))}),new Promise(i=>{setTimeout(()=>i(!1),5e3)})])||Y(e,"SIGKILL");else if(e)try{e.kill("SIGTERM")}catch{}await this.stopRawApiCapture()}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 U(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)}
3
- `)})}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(),a.info("codex-adapter",`Model set to: ${e}`)}setMode(e){const t=k(e);if(!t){a.info("codex-adapter",`Ignoring unsupported mode: ${e}`);return}this.collaborationMode=t,this.persistCodexContext(),a.info("codex-adapter",`Mode set to: ${t}`)}setReasoningEffort(e){this.reasoningEffort=e,this.persistCodexContext(),a.info("codex-adapter",`Reasoning effort set to: ${e}`)}setServiceTier(e){const t=e.trim();this.serviceTier=t.toLowerCase()==="default"?"default":t,this.persistCodexContext(),a.info("codex-adapter",`Service tier set to: ${this.serviceTier}`)}setSandboxMode(e){this.sandboxMode=e,this.persistCodexContext(),a.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}hasRawApiCaptureRelay(){return!!this.rawCaptureRelay}enableAuditCapture(e,t={}){const i=e.trim();if(!i||this.auditCaptureByEventId.has(i))return;const s=t.rawProviderBody??!!this.rawCaptureOptions;this.auditCaptureByEventId.set(i,{threadId:this.threadId??void 0,modelId:this.currentModelId()??void 0,rawProviderBody:s,sequence:0,events:[]}),s&&this.rawCaptureRelay?.beginEvent(i,t.auditId)}takeAuditBoundary(e){const t=this.auditCaptureByEventId.get(e);if(t)return this.auditCaptureByEventId.delete(e),{adapterType:this.type,providerTurnId:t.providerTurnId,threadId:t.threadId??this.threadId??void 0,codexEvidence:{events:t.events,...t.modelId?{modelId:t.modelId}:{},...t.rawProviderBody?{rawApiCapture:this.rawCaptureRelay?this.rawCaptureRelay.takeEvent(e):ke(this.rawCaptureUnavailableReason??"raw_capture_relay_not_prepared_before_adapter_start")}:{}}}}getMcpConfig(){if(!this.internalApi)return null;const e=le(ue(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?ge(e,[e]):!1}onAgentProfileChanged(){this.threadId&&(this.profileIdentityRefreshPending=!0,this.identityInputInjectedThreadId=null,a.info("codex-adapter",`agent profile changed; scheduling identity refresh for thread=${this.threadId}`))}async probe(e){const t=this.config.command||"codex",i=await Ce(t),s=i!==null;let n=null,r;if(s){const w=await Ae(t);n=w.version,w.error&&(r=w.error)}else r={code:"cli_not_found",message:`command not found: ${t}`};const c=D().baseUrl??null,p=this.currentModelId()||null,h=this.getStatus(),m=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},v=await this.probeSessionRecord(),x=K({codexHome:this.codexHome});return{cli:{command:t,installed:s,path:i,version:n,...r?{error:r}:{}},conversation:m,config:{model:p,base_url:c,source:{model:p?"runtime":"unknown",base_url:c?"file":"unknown"}},process:{started:!!this.process,alive:h.alive,busy:h.busy},session:v,lock:{present:x.present,locked:x.locked,stale:x.stale,path:x.path}}}async probeSessionRecord(){const e=this.threadId?_e(this.threadId,this.codexHome):null;if(!e)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const t=await X(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 r=n instanceof Error?n.message:String(n);if(/thread.*(not found|not exist|unknown|invalid)/i.test(r)||/not found.*thread/i.test(r))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(r=>r.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=k(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:ie}}}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=Se({mode:"codex",projectDir:this.cwd}),n=s.map(r=>`- ${r.name}${r.trigger?` (${r.trigger})`:""}: ${r.description}`);return{status:"ok",message:n.length>0?n.join(`
4
- `):"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(a.info("codex-adapter",`handleLocalAction action_type=${t} action_id=${e.action_id} session_id=${i.session_id??""}`),t){case"set_model":{const s=I(u(i.model_id),u(i.modelId),u(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=I(u(i.reasoning_effort),u(i.reasoning_eff),u(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_service_tier":{const s=I(u(i.service_tier),u(i.serviceTier),u(i.value));return s&&this.setServiceTier(s),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 s=I(u(i.sandbox_mode),u(i.sandboxMode),u(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":{a.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 a.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,a.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)?(a.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.")):(a.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",r=ze[n]??"accept";return this.sendApprovalDecision(s.requestId,r),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(),r=Date.now(),o=this.currentThreadTokenUsage;return s?a.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}`):a.info("codex-adapter","[rate-limits] responding: no rateLimitSnapshot available"),a.debug("codex-adapter",`[cp-diagnose] get_rate_limits: threadId=${this.threadId??"-"} contextWindow=${JSON.stringify(n)} tokenUsage=${JSON.stringify(o)}`),this.callbacks.sendLocalActionResult(e.action_id,"ok",{adapterType:"codex",available:s!==null,cached:!1,sampledAt:r,rateLimits:s,contextWindow:n,tokenUsage:o}),{handled:!0,kind:"get_rate_limits"}}default:return{handled:!1,kind:""}}}async buildToolbarContextResult(e,t=!0){t&&await this.refreshCodexModelOptions();const i=k(this.collaborationMode)??"default",s=this.currentModelId(),n=this.getModelOption(s),r=this.getModelOptions(),o=ie,c=this.reasoningEffort??n?.defaultReasoningEffort??null,p=n?.supportedReasoningEfforts??[],h=this.effectiveServiceTier()??n?.defaultServiceTier??null,m=n?.serviceTiers??[],v=this.isExternalProvider(),x=v?[]:p,w=v?null:c,S=v?[]:m,E=v?null:h,T={outcome:e,session_context:{modelId:s,modeId:i,reasoningEffort:w,serviceTier:E,approvalPolicy:this.approvalPolicy,sandboxMode:this.sandboxMode??null},model_id:s,mode_id:i,currentModelId:s,currentModeId:i,available_models:r,available_modes:o,available_efforts:x,availableModels:r,availableModes:o,reasoning_effort:w,available_service_tiers:S,service_tier:E,sandbox_mode:this.sandboxMode??null,models:r.map(y=>({modelId:y.id,name:y.displayName})),modes:o.map(y=>({id:y.id,name:y.displayName}))};return this.rateLimitSnapshot&&(T.rate_limit_primary_percent=this.rateLimitSnapshot.primary.usedPercent,T.rate_limit_secondary_percent=this.rateLimitSnapshot.secondary.usedPercent,T.rate_limit_primary_window_min=this.rateLimitSnapshot.primary.windowMinutes,T.rate_limit_secondary_window_min=this.rateLimitSnapshot.secondary.windowMinutes),T}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=Xe(t),s=this.codexModelOptions,n=[],r=String(this.model??"").trim(),o=r&&!s.some(m=>m.id.trim().toLowerCase()===r.toLowerCase()),c=s.length>0?s:o?[]:Ue;if(r){const m=[...s,...i].find(v=>v.id.trim().toLowerCase()===r.toLowerCase());n.push({id:r,displayName:m?.displayName??M(r),defaultReasoningEffort:m?.defaultReasoningEffort??null,supportedReasoningEfforts:m?.supportedReasoningEfforts??[],defaultServiceTier:m?.defaultServiceTier??null,serviceTiers:m?.serviceTiers??[],isDefault:m?.isDefault??!1})}n.push(...i),n.push(...c);const p=[],h=new Set;for(const m of n){const v=m.id.trim(),x=v.toLowerCase();!v||h.has(x)||(h.add(x),p.push({id:v,displayName:m.displayName.trim()||v,defaultReasoningEffort:m.defaultReasoningEffort,supportedReasoningEfforts:[...m.supportedReasoningEfforts],defaultServiceTier:m.defaultServiceTier,serviceTiers:[...m.serviceTiers],isDefault:m.isDefault}))}return p}async refreshCodexModelOptions(){if(!this.initialized||!this.alive||!this.process?.stdin||Date.now()-this.codexModelOptionsCachedAt<O.MODEL_CACHE_TTL_MS)return;const e=D(this.codexHome);if(e.baseUrl&&re(e.baseUrl)){try{const t=await Ye(e.baseUrl,e.apiKey);if(t.length>0){this.codexModelOptions=oe(t),this.codexModelOptionsCachedAt=Date.now(),a.info("codex-adapter",`Loaded ${t.length} models from external provider ${e.baseUrl}`);return}}catch(t){a.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=Ze(s);t.push(...n.models),i=n.nextCursor}while(i);t.length>0&&(this.codexModelOptions=oe(t),this.codexModelOptionsCachedAt=Date.now())}catch(t){a.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;deliveredAgentMessageIds=new Set;codexSequence=0;turnError=null;turnErrorIsRetryableOnly=!0;turnHasVisibleOutput=!1;turnSawVisibleFinalAnswer=!1;turnHasDeliveredFinalAnswer=!1;deliverInboundEvent(e){if(this.compacting){a.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()){a.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(a.info("codex-adapter",`Steer failed, falling through: ${s}`),!this.currentTurnId){this.startNewTurn(e,t);return}if(tt(s)){a.info("codex-adapter","Unsteerable turn detected, clearing active state and starting new turn"),this.clearActive(),this.startNewTurn(e,t);return}a.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){a.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,e.audit?.enabled===!0&&this.enableAuditCapture(e.event_id,{auditId:e.audit.auditId,rawProviderBody:e.audit.rawProviderBody}),this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.deliveredAgentMessageIds.clear(),this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.codexSequence=0,this.startComposing();const i=this.threadId??"",s=new U(i),n={adapterSessionId:i,text:t,contextMessages:e.context_messages_json?JSON.parse(e.context_messages_json).map(r=>({senderId:r.sender_id??"unknown",content:r.content})):void 0};this.runTurn(n,s,e.event_id,e.session_id).catch(r=>{a.error("codex-adapter",`Turn failed: ${r}`),this.callbacks.sendEventResult(e.event_id,"failed",r instanceof Error?r.message:String(r)),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||a.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=se(this.config.command||"codex",t),s=Be(this.config.args);for(const o of this.configOverrideArgs)s.push("-c",o);for(const o of this.rawCaptureConfigOverrides)s.push("-c",o);this.sandboxMode&&s.push("-c",`sandbox_mode="${this.sandboxMode}"`);const n=s.map(o=>this.rawCaptureConfigOverrides.includes(o)?"<raw-api-capture-overlay>":o);a.info("codex-adapter",`Spawning: ${i} ${n.join(" ")}`);try{if(!(await X(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 r=K({codexHome:this.codexHome});if(r.locked&&!r.stale)throw new Error(`Codex session appears locked by another process: ${r.path}`);r.stale&&a.warn("codex-adapter",`Ignoring stale Codex lock: ${r.path}`);try{this.process=me(i,s,{env:e,cwd:this.cwd}).process}catch(o){throw a.error("codex-adapter",`Codex spawn threw: ${o}`),this.process=null,this.alive=!1,this.bridgeStatus="closed",o}this.process.on("error",o=>{a.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=>{a.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 c=o.toString().trim();c&&a.info("codex-adapter",`[codex stderr] ${c}`)}),this.bindStdout(),this.alive=!0}bindStdout(){if(!this.process?.stdout)return;this.process.stdout.setEncoding("utf8"),ce({input:this.process.stdout}).on("line",t=>{if(!t.trim())return;let i;try{i=JSON.parse(t)}catch{a.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??{};if(this.shouldIgnoreOutOfScopeNotification(e,t,i)){this.activeEventId&&this.resetIdleTimer(this.activeEventId);return}switch(this.captureAuditNotification(t,i),this.captureContextWindowFromPayload(t,i),this.activeEventId&&t&&t.startsWith("item/")&&!t.endsWith("/requestApproval")&&this.startComposing(),t){case"item/started":{const n=i.item,r=n?.id,o=n?.type,c=this.normalizeAgentMessagePhase(n?.phase);this.isLongRunningToolItemType(o)&&(this.inFlightToolOps+=1),o==="agentMessage"&&r&&(c?this.agentMessagePhases.set(r,c):this.agentMessagePhases.delete(r),$(c)?(this.visibleAgentMessageIds.add(r),this.hiddenAgentMessageIds.delete(r)):(this.hiddenAgentMessageIds.add(r),this.visibleAgentMessageIds.delete(r)),c==="final_answer"&&$(c)&&(this.turnSawVisibleFinalAnswer=!0));break}case"item/agentMessage/delta":{const n=i.delta,r=i.itemId,o=this.resolveAgentMessagePhase(r,i.phase),c=(!r||!this.hiddenAgentMessageIds.has(r))&&$(o);o==="final_answer"&&c&&(this.turnSawVisibleFinalAnswer=!0),n&&this.activeEventId&&c&&!this.shouldSuppressAgentMessageDeltaDuringToolRun(r,o)&&(this.recordDeliveredAgentMessage(r,o),this.emitCodexEvent("item/agentMessage/delta",o?{...e,params:{...i,phase:o}}:e));break}case"item/completed":{const n=i.item,r=n?.id,o=n?.type;if(this.isLongRunningToolItemType(o)&&(this.inFlightToolOps=Math.max(0,this.inFlightToolOps-1)),this.activeEventId&&o==="commandExecution"&&(this.turnHasVisibleOutput=!0,this.emitCodexEvent("item/completed",e)),o==="agentMessage"){const c=this.resolveAgentMessagePhase(r,n?.phase),p=(!r||!this.hiddenAgentMessageIds.has(r))&&$(c);if(c==="final_answer"&&p){this.turnSawVisibleFinalAnswer=!0;const h=this.extractAgentMessageText(n),m=r?this.deliveredAgentMessageIds.has(r):!1;this.activeEventId&&h&&!m&&(a.warn("codex-adapter",`Recovering final_answer from item/completed item_id=${r??"unknown"}`),this.recordDeliveredAgentMessage(r,c),this.emitCodexEvent("item/agentMessage/delta",{method:"item/agentMessage/delta",params:{...r?{itemId:r}:{},delta:h,phase:c}}))}r&&(this.visibleAgentMessageIds.delete(r),this.hiddenAgentMessageIds.delete(r),this.agentMessagePhases.delete(r))}break}case"turn/started":{const r=i.turn?.id;r&&(this.currentTurnId=r,this.turnError=null,this.turnErrorIsRetryableOnly=!0,this.turnHasVisibleOutput=!1,this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.deliveredAgentMessageIds.clear(),this.currentTurnStartedAt=Date.now(),this.preflightCheckComplete=!1,a.info("codex-adapter",`Turn started: ${r}`),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 n=i.message??JSON.stringify(i);a.error("codex-adapter",`Codex error: ${n}`);const r=i.willRetry===!0;n&&(this.turnError=n,r||(this.turnErrorIsRetryableOnly=!1)),this.activeEventId&&!r&&this.callbacks.sendRunError(this.activeEventId,this.activeSessionId??"",n);break}case"account/rateLimits/updated":{const n=this.extractRateLimitPushLimitId(i),r=this.parseRateLimitSnapshot(i,this.rateLimitSnapshot);r&&(this.rateLimitSnapshot=r,a.info("codex-adapter",`Rate limits updated: primary=${r.primary.usedPercent.toFixed(1)}% secondary=${r.secondary.usedPercent.toFixed(1)}% extras=${r.extras?.length??0}`),this.callbacks.onRateLimitsUpdated?.(r)),n&&n!=="codex"&&this.refreshMainRateLimitsThrottled();break}case"thread/tokenUsage/updated":{const n=i.threadId,r=i.tokenUsage,o=r?.last;(!n||!r||!o)&&a.info("codex-adapter",`[cp-diagnose] token_usage payload_incomplete: method=thread/tokenUsage/updated hasThreadId=${String(!!n)} hasTokenUsage=${String(!!r)} hasLast=${String(!!o)} params=${JSON.stringify(i)}`),n&&r&&o&&(this.currentThreadTokenUsage={inputTokens:o.inputTokens??0,outputTokens:o.outputTokens??0,cacheReadInputTokens:o.cachedInputTokens??0,cacheCreationInputTokens:0},a.info("codex-adapter",`[cp-diagnose] token_usage raw thread=${n}: ${JSON.stringify(r)}`),a.info("codex-adapter",`[cp-diagnose] token_usage parsed thread=${n}: ${JSON.stringify(this.currentThreadTokenUsage)}`),this.callbacks.onTokenUsageUpdated?.(this.currentThreadTokenUsage),this.publishContextWindowSnapshot());break}default:break}const s=t?.endsWith("/requestApproval")===!0;t&&t!=="turn/started"&&(t.startsWith("item/")||t==="error"||t==="turn/completed")&&(this.preflightCheckComplete=!0),this.activeEventId&&(!s||this.pendingApprovals.size===0)&&this.resetIdleTimer(this.activeEventId)}shouldIgnoreOutOfScopeNotification(e,t,i){if(!t||e.id!=null)return!1;const s=I(u(i.threadId),u(i.threadID));if(s&&this.threadId&&s!==this.threadId)return this.logIgnoredNotificationScope(t,s,this.readNotificationTurnId(i)),!0;const n=this.readNotificationTurnId(i);return n&&this.currentTurnId&&n!==this.currentTurnId?(this.logIgnoredNotificationScope(t,s,n),!0):!1}readNotificationTurnId(e){const t=e.turn&&typeof e.turn=="object"?e.turn:void 0;return I(u(e.turnId),u(e.turnID),u(t?.id))}logIgnoredNotificationScope(e,t,i){e!=="error"&&!e.startsWith("turn/")||a.info("codex-adapter",`[scope] ignored ${e} thread=${t??"<none>"} turn=${i??"<none>"} active_thread=${this.threadId??"<none>"} active_turn=${this.currentTurnId??"<none>"}`)}captureAuditNotification(e,t){const i=this.activeEventId;if(!e||!i)return;const s=this.auditCaptureByEventId.get(i);if(!s)return;const n=t.turn,r=typeof t.turnId=="string"&&t.turnId?t.turnId:typeof t.turn_id=="string"&&t.turn_id?t.turn_id:typeof n?.id=="string"&&n.id?n.id:this.currentTurnId;r&&(s.providerTurnId=r);const o=typeof t.threadId=="string"&&t.threadId?t.threadId:typeof t.thread_id=="string"&&t.thread_id?t.thread_id:this.threadId;if(o&&(s.threadId=o),!!qe.has(e))try{const c=JSON.parse(JSON.stringify(t));s.events.push({sequence:++s.sequence,method:e,params:c,observedAt:new Date().toISOString()})}catch(c){const p=c instanceof Error?c.message:String(c);a.warn("codex-adapter",`Audit evidence capture skipped event=${i} method=${e}: ${p}`)}}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&&a.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,a.info("codex-adapter",`[cp-diagnose] context_window updated: method=${e??"-"} size=${s}`),this.publishContextWindowSnapshot())}probeContextWindowFields(e,t){const i=[],s=[],n=r=>{if(!r||typeof r!="object")return;if(Array.isArray(r)){for(const c of r)n(c);return}const o=r;for(const c of t)Object.prototype.hasOwnProperty.call(o,c)&&(i.push(c),s.push(String(o[c])));for(const c of Object.values(o))n(c)};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 r=Number(n);if(Number.isFinite(r))return r}}for(const s of Object.values(i)){if(Array.isArray(s)){for(const r of s){const o=this.findNumericField(r,t);if(o!=null)return o}continue}const n=this.findNumericField(s,t);if(n!=null)return n}return null}buildContextWindowSnapshot(){const e=this.currentModelContextWindow;if(!e||e<=0)return a.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),r=i==null?null:Math.max(0,100-(n??0));return a.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:r,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<te||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,a.info("codex-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${te}%, 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=>{a.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=>{a.warn("codex-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:e}`)})))}parseRateLimitSnapshot(e,t=null){try{const i=e.rateLimitsByLimitId,s=this.extractRateLimitPushLimitId(e),n=!i?.codex&&!!s&&s!=="codex",r=i?.codex??(n?{}:e.rateLimits??e),o=r.primary,c=r.secondary,p=r.credits,h=l=>{if(typeof l=="number"&&Number.isFinite(l))return l;if(typeof l=="string"){const f=Number(l);if(Number.isFinite(f))return f}return 0},m=l=>typeof l=="boolean"?l:typeof l=="string"?l.toLowerCase()==="true":!1,v=l=>{if(l==null)return null;if(typeof l=="string"){const f=l.trim();if(!f)return null;const g=Number(f);return Number.isFinite(g)&&g>1e9?new Date(g*1e3).toISOString():f}if(typeof l=="number"&&Number.isFinite(l)&&l>0){const f=l>1e12?l/1e3:l;return new Date(f*1e3).toISOString()}return null},x=l=>{const f=l.resets_in_seconds??l.resetsInSeconds,g=typeof f=="number"||typeof f=="string"?Number(f):NaN;return!Number.isFinite(g)||g<=0?null:new Date(Date.now()+g*1e3).toISOString()},w=l=>({usedPercent:h(l.used_percent??l.usedPercent),windowMinutes:h(l.window_minutes??l.windowDurationMins??l.windowMinutes),resetsAt:v(l.resets_at??l.resetsAt)??x(l)}),S=()=>({usedPercent:0,windowMinutes:0,resetsAt:null}),E=(l,f)=>l??f??S(),T=()=>{const l=[];if(o&&l.push(o),c&&l.push(c),Array.isArray(r.windows))for(const f of r.windows)f&&typeof f=="object"&&l.push(f);return l.map(w).filter(f=>f.windowMinutes>0)},y=(l,f,g)=>{const _=l.filter(C=>g(C.windowMinutes));return _.length===0?null:_.reduce((C,A)=>Math.abs(A.windowMinutes-f)<Math.abs(C.windowMinutes-f)?A:C)},N=T(),L=n?t?{primary:t.primary,secondary:t.secondary}:null:N.length>0?{primary:E(y(N,300,l=>l<1440),t?.primary),secondary:E(y(N,10080,l=>l>=1440),t?.secondary)}:o||c?{primary:o?w(o):t?.primary??S(),secondary:c?w(c):t?.secondary??S()}:null;if(!L)return null;const q=(l,f)=>{if(!f||typeof f!="object"||Array.isArray(f))return null;const g=f,_=g.limitName??g.limit_name,C=typeof _=="string"&&_.trim()?_.trim():l,A=[],z=g.primary,W=g.secondary;z&&A.push(z),W&&A.push(W);const B=A.map(w).filter(R=>R.windowMinutes>0),V=y(B,10080,R=>R>=1440)??y(B,300,R=>R<1440);return V?{id:l,label:C,...V}:null};let b;if(i&&typeof i=="object"){b=[];for(const[l,f]of Object.entries(i)){if(l==="codex")continue;const g=q(l,f);g&&b.push(g)}}else if(b=t?.extras?[...t.extras]:[],n&&s){const l=q(s,e.rateLimits??e);if(l){const f=b.findIndex(g=>g.id===l.id);f>=0?b[f]=l:b.push(l)}}return{primary:L.primary,secondary:L.secondary,credits:p?{hasCredits:m(p.has_credits??p.hasCredits),unlimited:m(p.unlimited),balance:p.balance==null?null:h(p.balance)}:t?.credits??{hasCredits:!1,unlimited:!1,balance:null},...b.length>0?{extras:b}:{}}}catch{return null}}handleApprovalRequest(e,t){const i=e.id;if(i==null)return;const s=e.method,n=`${i}`.trim(),r=`codex_${ae(this.activeSessionId??"")}_${ae(n)}`;if(this.approvalPolicy==="never"){this.sendApprovalDecision(i,"accept");return}this.activeEventId&&this.emitCodexEvent(s,e),this.pendingApprovals.set(r,{approvalId:r,approvalCommandId:n,sourceEventId:this.activeEventId??"",requestId:i,kind:t}),a.info("codex-adapter",`Pending approval stored: ${r} (kind=${t})`),this.clearIdleTimer(),this.stopComposing()}resolvePendingApproval(e){const t=e.params??{},i=I(t.approval_id,t.approvalId);if(i)return this.pendingApprovals.get(i)??null;const s=I(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)}
5
- `)}armCompactionFallback(){this.compacting&&(this.compactingTimer&&clearTimeout(this.compactingTimer),this.compactingTimer=setTimeout(()=>{a.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")},He),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);a.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:de("turn/completed",e),codex_at:new Date().toISOString()},s=this.turnError,n=this.turnErrorIsRetryableOnly,r=this.turnHasVisibleOutput,o=this.turnSawVisibleFinalAnswer&&!this.turnHasDeliveredFinalAnswer;this.turnError=null,this.turnErrorIsRetryableOnly=!0,this.turnHasVisibleOutput=!1,this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1;const p=(s&&!(n&&r)?s:null)??(o?"Codex final answer was observed but not delivered":null),h=p!==null;this.callbacks.sendCodexEvent(i),this.callbacks.sendEventResult(t,h?"failed":"responded",p??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:de(e,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 r=++this.requestId,o={jsonrpc:"2.0",id:r,method:e,params:t??{}},c=setTimeout(()=>{this.pendingRequests.delete(String(r)),n(new Error(`Request timeout: ${e}`))},i);this.pendingRequests.set(String(r),{resolve:h=>{h.error?n(new Error(`JSON-RPC error: ${h.error.message}`)):s(h.result)},reject:n,timer:c});const p=JSON.stringify(o);this.process.stdin.write(`${p}
1
+ import{execFileSync as G}from"node:child_process";import{createInterface as ce}from"node:readline";import{EventEmitter as X}from"node:events";import{stat as J}from"node:fs/promises";import{join as P,resolve as le}from"node:path";import{fileURLToPath as ue}from"node:url";import{homedir as he}from"node:os";import{syncDefaultSkillsToDir as pe}from"../../default-skills/index.js";import{resolveCommandPath as fe,spawnCommand as me,killProcessGroup as Y,hasChildProcesses as ge}from"../../core/runtime/spawn.js";import{InternalApiServer as ve}from"../../core/mcp/internal-api-server.js";import{log as a}from"../../core/log/index.js";import{resolveClientVersion as Ie}from"../../core/util/client-version.js";import{isUserVisibleAgentMessagePhase as $}from"../../core/util/codex-output-policy.js";import{SessionBindingStore as xe}from"../../core/persistence/session-binding-store.js";import{ensureCodexProjectTrusted as we,isCodexCommand as ye,readCodexProviderSettings as D}from"./codex-trust.js";import{checkCodexSessionActivity as be,inspectCodexTurnProgress as _e}from"./session-activity.js";import{findRolloutFile as Te}from"./rollout-locator.js";import{checkCodexLock as K}from"./lock-monitor.js";import{resolveCliPath as Ce,getCliVersion as Ae}from"../../core/util/cli-probe.js";import{scanSkills as Se}from"../claude/skill-scanner.js";import{normalizeCodexConfigOverrides as Ee}from"./config-overrides.js";import{buildCodexOpenAiCompatibleProviderOverrides as Re,CODEX_RELAY_OPENAI_PROVIDER_ID as ke,prepareCodexRawCapture as Me,resolveCodexProviderName as Pe,resolveCodexRawCaptureTarget as $e,resolveCodexProviderSupportsWebsocketsConfigKey as Oe}from"./raw-capture-hook.js";import{unavailableRawApiCaptureBoundary as Ne}from"../../audit/raw-capture/capture-relay.js";const Le=60*1e3,De=600*1e3,Fe="GRIX_RELAY_PROXY",je=5*1e3,He=15*1e3,Z=3;function Ue(d,e){const t=Number(process.env[d]);return Number.isFinite(t)&&t>0?t:e}function qe(d){const e=Number(process.env[d]);return Number.isFinite(e)&&e>0?e:null}function ze(d){if(!d||typeof d!="object"||Array.isArray(d))return;const e=d,t=typeof e.auditId=="string"?e.auditId.trim():"",i=typeof e.sessionId=="string"?e.sessionId.trim():"",s=typeof e.spoolRootDir=="string"?e.spoolRootDir.trim():"";return t&&i&&s?{auditId:t,sessionId:i,spoolRootDir:s}:void 0}const F=Ue("GRIX_CODEX_PREFLIGHT_STALL_TIMEOUT_MS",120*1e3),We=20,Be=8e3,Q=2*1024,ee=`
2
+ \u2026(truncated)`,te=80,Ve=120*1e3,Ge=[{id:"gpt-5.3-codex",displayName:"GPT-5.3 Codex",defaultReasoningEffort:null,supportedReasoningEfforts:[],defaultServiceTier:null,serviceTiers:[],isDefault:!0}],ie=[{id:"default",displayName:"Default"},{id:"plan",displayName:"Plan"}],Xe=new Set(["rawResponse/completed","codex/rawResponse/completed","rawResponseItem/completed","codex/rawResponseItem/completed","thread/tokenUsage/updated","codex/thread/tokenUsage/updated","thread/compacted","codex/thread/compacted","turn/started","codex/turn/started","turn/completed","codex/turn/completed","item/started","codex/item/started","item/completed","codex/item/completed","error","codex/error"]);function se(d,e){const t=String(d??"").trim()||"codex",i=fe(t,e);if(i!==t)return i;if(t!=="codex")return t;const s=process.platform==="win32"?[]:["/opt/homebrew/bin/codex","/usr/local/bin/codex","/usr/bin/codex"],n=process.platform!=="win32"?[]:[P(process.env.LOCALAPPDATA??"","npm","codex.cmd"),P(process.env.APPDATA??"","npm","codex.cmd"),P(process.env.LOCALAPPDATA??"","Programs","codex","codex.exe")],r=[...s,...n];for(const o of r)try{return require("node:fs").accessSync(o,require("node:fs").constants.X_OK),o}catch{continue}return a.warn("codex-adapter",`resolveCodexCommandPath: failed to resolve "${t}". envPath=${e?"provided":"missing (using process.env.PATH)"}. fallbacks tried: [${r.join(", ")}]`),t}class O extends X{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;activityGeneration=0;lastIdleCheckGeneration=0;idleNoProgressCount=0;inFlightToolOps=0;consecutivePingFailures=0;currentTurnStartedAt=0;preflightCheckComplete=!1;turnInactivityTimeoutMs=qe("GRIX_CODEX_TURN_INACTIVITY_TIMEOUT_MS");pendingApprovals=new Map;needsHistoryInjection=!1;lastInjectedSessionId=null;identityInputInjectedThreadId=null;legacyDeveloperInstructionsUnsupported=!1;profileIdentityRefreshPending=!1;bindingStore=null;aibotSessionId="";threadResumePending=!1;autoTrustProject="auto";codexHome;configOverrideArgs=[];rawCaptureOptions;rawCaptureRelay;rawCaptureConfigOverrides=[];rawCaptureUnavailableReason;codexModelOptions=[];codexModelOptionsCachedAt=0;static MODEL_CACHE_TTL_MS=3600*1e3;rateLimitSnapshot=null;lastNonDefaultLimitRefreshAt=0;static NON_DEFAULT_LIMIT_REFRESH_INTERVAL_MS=6e4;currentThreadTokenUsage=null;currentModelContextWindow=null;auditCaptureByEventId=new Map;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 xe?i.bindingStore:null,this.model=i.model,this.collaborationMode=i.collaborationMode,this.reasoningEffort=i.reasoningEffort,this.serviceTier=i.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 i.autoTrustProject=="boolean"?i.autoTrustProject:"auto",this.codexHome=typeof i.codexHome=="string"&&i.codexHome.trim()?i.codexHome.trim():void 0,this.configOverrideArgs=Ee(i.codexConfigOverrides,"codexConfigOverrides"),this.rawCaptureOptions=ze(i.rawApiCapture),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.prepareRawApiCapture();try{await this.startInternalApiAndRegisterMcp(),await this.spawnCodex(),await this.initializeHandshake(),await this.notifyBindingReadyWithContext(),a.info("codex-adapter",`Ready (pid=${this.process?.pid})`)}catch(e){throw await this.stopRawApiCapture(),e}}async prepareRawApiCapture(){if(this.rawCaptureOptions)try{const e=await Me(this.rawCaptureOptions,{adapterType:"codex",sessionId:this.rawCaptureOptions.sessionId,cwd:this.cwd,env:{...process.env,...this.config.env},configOverrides:this.configOverrideArgs,codexHome:this.codexHome});this.rawCaptureRelay=e.relay,this.rawCaptureConfigOverrides=e.overlay.configOverrides??[],this.rawCaptureUnavailableReason=void 0,a.info("codex-adapter","Raw API audit capture relay is ready")}catch(e){this.rawCaptureRelay=void 0,this.rawCaptureConfigOverrides=[],this.rawCaptureUnavailableReason=e instanceof Error?e.message:String(e),a.warn("codex-adapter",`Raw API audit capture unavailable: ${this.rawCaptureUnavailableReason}`)}}async stopRawApiCapture(){const e=this.rawCaptureRelay;this.rawCaptureRelay=void 0,this.rawCaptureConfigOverrides=[],e&&await e.stop()}async ensureProjectTrusted(){if(this.shouldAutoTrustProject())try{const e=await we(this.cwd,{codexHome:this.codexHome});e.changed&&a.info("codex-adapter",`Trusted Codex project ${this.cwd} in ${e.configPath}`)}catch(e){const t=e instanceof Error?e.message:String(e);throw a.error("codex-adapter",`Failed to trust Codex project ${this.cwd}: ${t}`),e}}async startInternalApiAndRegisterMcp(){try{this.internalApi=new ve,this.internalApi.setInvokeHandler(async(o,c,p,h)=>this.callbacks.agentInvoke(o,c,h)),await this.internalApi.start(0),a.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=se(this.config.command||"codex",i);try{G(s,["mcp","remove",e.name],{env:t,timeout:1e4,stdio:"ignore"})}catch{}G(s,["mcp","add",e.name,"--",e.command,...e.args],{env:t,timeout:1e4,stdio:"ignore"}),a.info("codex-adapter",`Registered MCP server: ${e.name}`);const n=P(he(),".codex","skills"),r=pe(n);r.length>0&&a.info("codex-adapter",`Synced connector skills to ${n}: [${r.join(", ")}]`)}catch(e){a.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:ye(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){a.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<O.EXTERNAL_PROVIDER_CACHE_TTL_MS)return this.externalProviderCached;const t=D(this.codexHome);return this.externalProviderCached=!!(t.baseUrl&&re(t.baseUrl)),this.externalProviderCheckedAt=e,this.externalProviderCached}effectiveServiceTier(){if(!this.serviceTier||this.serviceTier.trim().toLowerCase()==="default"||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),i={},s=t?.supportedReasoningEfforts??[];s.length>0&&(i.available_efforts=s,i.reasoning_effort=this.reasoningEffort??t?.defaultReasoningEffort??null);const n=t?.serviceTiers??[];return n.length>0&&(i.available_service_tiers=n,i.service_tier=this.effectiveServiceTier()??t?.defaultServiceTier??null),i}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")Y(e,"SIGTERM"),await Promise.race([new Promise(i=>{e.once("exit",()=>i(!0))}),new Promise(i=>{setTimeout(()=>i(!1),5e3)})])||Y(e,"SIGKILL");else if(e)try{e.kill("SIGTERM")}catch{}await this.stopRawApiCapture()}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 U(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)}
3
+ `)})}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(),a.info("codex-adapter",`Model set to: ${e}`)}setMode(e){const t=k(e);if(!t){a.info("codex-adapter",`Ignoring unsupported mode: ${e}`);return}this.collaborationMode=t,this.persistCodexContext(),a.info("codex-adapter",`Mode set to: ${t}`)}setReasoningEffort(e){this.reasoningEffort=e,this.persistCodexContext(),a.info("codex-adapter",`Reasoning effort set to: ${e}`)}setServiceTier(e){const t=e.trim();this.serviceTier=t.toLowerCase()==="default"?"default":t,this.persistCodexContext(),a.info("codex-adapter",`Service tier set to: ${this.serviceTier}`)}setSandboxMode(e){this.sandboxMode=e,this.persistCodexContext(),a.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}hasRawApiCaptureRelay(){return!!this.rawCaptureRelay}enableAuditCapture(e,t={}){const i=e.trim();if(!i||this.auditCaptureByEventId.has(i))return;const s=t.rawProviderBody??!!this.rawCaptureOptions;this.auditCaptureByEventId.set(i,{threadId:this.threadId??void 0,modelId:this.currentModelId()??void 0,rawProviderBody:s,sequence:0,events:[]}),s&&this.rawCaptureRelay?.beginEvent(i,t.auditId)}takeAuditBoundary(e){const t=this.auditCaptureByEventId.get(e);if(t)return this.auditCaptureByEventId.delete(e),{adapterType:this.type,providerTurnId:t.providerTurnId,threadId:t.threadId??this.threadId??void 0,codexEvidence:{events:t.events,...t.modelId?{modelId:t.modelId}:{},...t.rawProviderBody?{rawApiCapture:this.rawCaptureRelay?this.rawCaptureRelay.takeEvent(e):Ne(this.rawCaptureUnavailableReason??"raw_capture_relay_not_prepared_before_adapter_start")}:{}}}}getMcpConfig(){if(!this.internalApi)return null;const e=le(ue(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?ge(e,[e]):!1}onAgentProfileChanged(){this.threadId&&(this.profileIdentityRefreshPending=!0,this.identityInputInjectedThreadId=null,a.info("codex-adapter",`agent profile changed; scheduling identity refresh for thread=${this.threadId}`))}async probe(e){const t=this.config.command||"codex",i=await Ce(t),s=i!==null;let n=null,r;if(s){const w=await Ae(t);n=w.version,w.error&&(r=w.error)}else r={code:"cli_not_found",message:`command not found: ${t}`};const c=D().baseUrl??null,p=this.currentModelId()||null,h=this.getStatus(),m=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},v=await this.probeSessionRecord(),x=K({codexHome:this.codexHome});return{cli:{command:t,installed:s,path:i,version:n,...r?{error:r}:{}},conversation:m,config:{model:p,base_url:c,source:{model:p?"runtime":"unknown",base_url:c?"file":"unknown"}},process:{started:!!this.process,alive:h.alive,busy:h.busy},session:v,lock:{present:x.present,locked:x.locked,stale:x.stale,path:x.path}}}async probeSessionRecord(){const e=this.threadId?Te(this.threadId,this.codexHome):null;if(!e)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const t=await J(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 r=n instanceof Error?n.message:String(n);if(/thread.*(not found|not exist|unknown|invalid)/i.test(r)||/not found.*thread/i.test(r))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(r=>r.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=k(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:ie}}}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=Se({mode:"codex",projectDir:this.cwd}),n=s.map(r=>`- ${r.name}${r.trigger?` (${r.trigger})`:""}: ${r.description}`);return{status:"ok",message:n.length>0?n.join(`
4
+ `):"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(a.info("codex-adapter",`handleLocalAction action_type=${t} action_id=${e.action_id} session_id=${i.session_id??""}`),t){case"set_model":{const s=I(u(i.model_id),u(i.modelId),u(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=I(u(i.reasoning_effort),u(i.reasoning_eff),u(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_service_tier":{const s=I(u(i.service_tier),u(i.serviceTier),u(i.value));return s&&this.setServiceTier(s),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 s=I(u(i.sandbox_mode),u(i.sandboxMode),u(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":{a.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 a.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,a.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)?(a.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.")):(a.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",r=Je[n]??"accept";return this.sendApprovalDecision(s.requestId,r),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(),r=Date.now(),o=this.currentThreadTokenUsage;return s?a.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}`):a.info("codex-adapter","[rate-limits] responding: no rateLimitSnapshot available"),a.debug("codex-adapter",`[cp-diagnose] get_rate_limits: threadId=${this.threadId??"-"} contextWindow=${JSON.stringify(n)} tokenUsage=${JSON.stringify(o)}`),this.callbacks.sendLocalActionResult(e.action_id,"ok",{adapterType:"codex",available:s!==null,cached:!1,sampledAt:r,rateLimits:s,contextWindow:n,tokenUsage:o}),{handled:!0,kind:"get_rate_limits"}}default:return{handled:!1,kind:""}}}async buildToolbarContextResult(e,t=!0){t&&await this.refreshCodexModelOptions();const i=k(this.collaborationMode)??"default",s=this.currentModelId(),n=this.getModelOption(s),r=this.getModelOptions(),o=ie,c=this.reasoningEffort??n?.defaultReasoningEffort??null,p=n?.supportedReasoningEfforts??[],h=this.effectiveServiceTier()??n?.defaultServiceTier??null,m=n?.serviceTiers??[],v=this.isExternalProvider(),x=v?[]:p,w=v?null:c,S=v?[]:m,E=v?null:h,_={outcome:e,session_context:{modelId:s,modeId:i,reasoningEffort:w,serviceTier:E,approvalPolicy:this.approvalPolicy,sandboxMode:this.sandboxMode??null},model_id:s,mode_id:i,currentModelId:s,currentModeId:i,available_models:r,available_modes:o,available_efforts:x,availableModels:r,availableModes:o,reasoning_effort:w,available_service_tiers:S,service_tier:E,sandbox_mode:this.sandboxMode??null,models:r.map(y=>({modelId:y.id,name:y.displayName})),modes:o.map(y=>({id:y.id,name:y.displayName}))};return this.rateLimitSnapshot&&(_.rate_limit_primary_percent=this.rateLimitSnapshot.primary.usedPercent,_.rate_limit_secondary_percent=this.rateLimitSnapshot.secondary.usedPercent,_.rate_limit_primary_window_min=this.rateLimitSnapshot.primary.windowMinutes,_.rate_limit_secondary_window_min=this.rateLimitSnapshot.secondary.windowMinutes),_}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=tt(t),s=this.codexModelOptions,n=[],r=String(this.model??"").trim(),o=r&&!s.some(m=>m.id.trim().toLowerCase()===r.toLowerCase()),c=s.length>0?s:o?[]:Ge;if(r){const m=[...s,...i].find(v=>v.id.trim().toLowerCase()===r.toLowerCase());n.push({id:r,displayName:m?.displayName??M(r),defaultReasoningEffort:m?.defaultReasoningEffort??null,supportedReasoningEfforts:m?.supportedReasoningEfforts??[],defaultServiceTier:m?.defaultServiceTier??null,serviceTiers:m?.serviceTiers??[],isDefault:m?.isDefault??!1})}n.push(...i),n.push(...c);const p=[],h=new Set;for(const m of n){const v=m.id.trim(),x=v.toLowerCase();!v||h.has(x)||(h.add(x),p.push({id:v,displayName:m.displayName.trim()||v,defaultReasoningEffort:m.defaultReasoningEffort,supportedReasoningEfforts:[...m.supportedReasoningEfforts],defaultServiceTier:m.defaultServiceTier,serviceTiers:[...m.serviceTiers],isDefault:m.isDefault}))}return p}async refreshCodexModelOptions(){if(!this.initialized||!this.alive||!this.process?.stdin||Date.now()-this.codexModelOptionsCachedAt<O.MODEL_CACHE_TTL_MS)return;const e=D(this.codexHome);if(e.baseUrl&&re(e.baseUrl)){try{const t=await it(e.baseUrl,e.apiKey);if(t.length>0){this.codexModelOptions=oe(t),this.codexModelOptionsCachedAt=Date.now(),a.info("codex-adapter",`Loaded ${t.length} models from external provider ${e.baseUrl}`);return}}catch(t){a.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=nt(s);t.push(...n.models),i=n.nextCursor}while(i);t.length>0&&(this.codexModelOptions=oe(t),this.codexModelOptionsCachedAt=Date.now())}catch(t){a.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;deliveredAgentMessageIds=new Set;codexSequence=0;turnError=null;turnErrorIsRetryableOnly=!0;turnHasVisibleOutput=!1;turnSawVisibleFinalAnswer=!1;turnHasDeliveredFinalAnswer=!1;deliverInboundEvent(e){if(this.compacting){a.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()){a.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(a.info("codex-adapter",`Steer failed, falling through: ${s}`),!this.currentTurnId){this.startNewTurn(e,t);return}if(at(s)){a.info("codex-adapter","Unsteerable turn detected, clearing active state and starting new turn"),this.clearActive(),this.startNewTurn(e,t);return}a.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){a.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,e.audit?.enabled===!0&&this.enableAuditCapture(e.event_id,{auditId:e.audit.auditId,rawProviderBody:e.audit.rawProviderBody}),this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.deliveredAgentMessageIds.clear(),this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.codexSequence=0,this.startComposing();const i=this.threadId??"",s=new U(i),n={adapterSessionId:i,text:t,contextMessages:e.context_messages_json?JSON.parse(e.context_messages_json).map(r=>({senderId:r.sender_id??"unknown",content:r.content})):void 0};this.runTurn(n,s,e.event_id,e.session_id).catch(r=>{a.error("codex-adapter",`Turn failed: ${r}`),this.callbacks.sendEventResult(e.event_id,"failed",r instanceof Error?r.message:String(r)),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||a.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=se(this.config.command||"codex",t),s=Ke(this.config.args);for(const o of this.configOverrideArgs)s.push("-c",o);for(const o of this.resolveRelayTransportConfigOverrides())s.push("-c",o);for(const o of this.rawCaptureConfigOverrides)s.push("-c",o);this.sandboxMode&&s.push("-c",`sandbox_mode="${this.sandboxMode}"`);const n=s.map(o=>this.rawCaptureConfigOverrides.includes(o)?"<raw-api-capture-overlay>":o);a.info("codex-adapter",`Spawning: ${i} ${n.join(" ")}`);try{if(!(await J(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 r=K({codexHome:this.codexHome});if(r.locked&&!r.stale)throw new Error(`Codex session appears locked by another process: ${r.path}`);r.stale&&a.warn("codex-adapter",`Ignoring stale Codex lock: ${r.path}`);try{this.process=me(i,s,{env:e,cwd:this.cwd}).process}catch(o){throw a.error("codex-adapter",`Codex spawn threw: ${o}`),this.process=null,this.alive=!1,this.bridgeStatus="closed",o}this.process.on("error",o=>{a.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=>{a.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 c=o.toString().trim();c&&a.info("codex-adapter",`[codex stderr] ${c}`)}),this.bindStdout(),this.alive=!0}resolveRelayTransportConfigOverrides(){if(this.config.env?.[Fe]!=="1")return[];const e=Pe(this.codexHome,this.configOverrideArgs);if(e==="openai"){const i=$e(this.codexHome,this.configOverrideArgs);return Re(ke,"Grix OpenAI Relay",i.targetBaseUrl)}const t=Oe(e);return t?[`${t}=false`]:[]}bindStdout(){if(!this.process?.stdout)return;this.process.stdout.setEncoding("utf8"),ce({input:this.process.stdout}).on("line",t=>{if(!t.trim())return;let i;try{i=JSON.parse(t)}catch{a.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??{};if(this.shouldIgnoreOutOfScopeNotification(e,t,i)){this.activeEventId&&this.resetIdleTimer(this.activeEventId);return}switch(this.captureAuditNotification(t,i),this.captureContextWindowFromPayload(t,i),this.activeEventId&&t&&t.startsWith("item/")&&!t.endsWith("/requestApproval")&&this.startComposing(),t){case"item/started":{const n=i.item,r=n?.id,o=n?.type,c=this.normalizeAgentMessagePhase(n?.phase);this.isLongRunningToolItemType(o)&&(this.inFlightToolOps+=1),o==="agentMessage"&&r&&(c?this.agentMessagePhases.set(r,c):this.agentMessagePhases.delete(r),$(c)?(this.visibleAgentMessageIds.add(r),this.hiddenAgentMessageIds.delete(r)):(this.hiddenAgentMessageIds.add(r),this.visibleAgentMessageIds.delete(r)),c==="final_answer"&&$(c)&&(this.turnSawVisibleFinalAnswer=!0));break}case"item/agentMessage/delta":{const n=i.delta,r=i.itemId,o=this.resolveAgentMessagePhase(r,i.phase),c=(!r||!this.hiddenAgentMessageIds.has(r))&&$(o);o==="final_answer"&&c&&(this.turnSawVisibleFinalAnswer=!0),n&&this.activeEventId&&c&&!this.shouldSuppressAgentMessageDeltaDuringToolRun(r,o)&&(this.recordDeliveredAgentMessage(r,o),this.emitCodexEvent("item/agentMessage/delta",o?{...e,params:{...i,phase:o}}:e));break}case"item/completed":{const n=i.item,r=n?.id,o=n?.type;if(this.isLongRunningToolItemType(o)&&(this.inFlightToolOps=Math.max(0,this.inFlightToolOps-1)),this.activeEventId&&o==="commandExecution"&&(this.turnHasVisibleOutput=!0,this.emitCodexEvent("item/completed",e)),o==="agentMessage"){const c=this.resolveAgentMessagePhase(r,n?.phase),p=(!r||!this.hiddenAgentMessageIds.has(r))&&$(c);if(c==="final_answer"&&p){this.turnSawVisibleFinalAnswer=!0;const h=this.extractAgentMessageText(n),m=r?this.deliveredAgentMessageIds.has(r):!1;this.activeEventId&&h&&!m&&(a.warn("codex-adapter",`Recovering final_answer from item/completed item_id=${r??"unknown"}`),this.recordDeliveredAgentMessage(r,c),this.emitCodexEvent("item/agentMessage/delta",{method:"item/agentMessage/delta",params:{...r?{itemId:r}:{},delta:h,phase:c}}))}r&&(this.visibleAgentMessageIds.delete(r),this.hiddenAgentMessageIds.delete(r),this.agentMessagePhases.delete(r))}break}case"turn/started":{const r=i.turn?.id;r&&(this.currentTurnId=r,this.turnError=null,this.turnErrorIsRetryableOnly=!0,this.turnHasVisibleOutput=!1,this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.deliveredAgentMessageIds.clear(),this.currentTurnStartedAt=Date.now(),this.preflightCheckComplete=!1,a.info("codex-adapter",`Turn started: ${r}`),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 n=i.message??JSON.stringify(i);a.error("codex-adapter",`Codex error: ${n}`);const r=i.willRetry===!0;n&&(this.turnError=n,r||(this.turnErrorIsRetryableOnly=!1)),this.activeEventId&&!r&&this.callbacks.sendRunError(this.activeEventId,this.activeSessionId??"",n);break}case"account/rateLimits/updated":{const n=this.extractRateLimitPushLimitId(i),r=this.parseRateLimitSnapshot(i,this.rateLimitSnapshot);r&&(this.rateLimitSnapshot=r,a.info("codex-adapter",`Rate limits updated: primary=${r.primary.usedPercent.toFixed(1)}% secondary=${r.secondary.usedPercent.toFixed(1)}% extras=${r.extras?.length??0}`),this.callbacks.onRateLimitsUpdated?.(r)),n&&n!=="codex"&&this.refreshMainRateLimitsThrottled();break}case"thread/tokenUsage/updated":{const n=i.threadId,r=i.tokenUsage,o=r?.last;(!n||!r||!o)&&a.info("codex-adapter",`[cp-diagnose] token_usage payload_incomplete: method=thread/tokenUsage/updated hasThreadId=${String(!!n)} hasTokenUsage=${String(!!r)} hasLast=${String(!!o)} params=${JSON.stringify(i)}`),n&&r&&o&&(this.currentThreadTokenUsage={inputTokens:o.inputTokens??0,outputTokens:o.outputTokens??0,cacheReadInputTokens:o.cachedInputTokens??0,cacheCreationInputTokens:0},a.info("codex-adapter",`[cp-diagnose] token_usage raw thread=${n}: ${JSON.stringify(r)}`),a.info("codex-adapter",`[cp-diagnose] token_usage parsed thread=${n}: ${JSON.stringify(this.currentThreadTokenUsage)}`),this.callbacks.onTokenUsageUpdated?.(this.currentThreadTokenUsage),this.publishContextWindowSnapshot());break}default:break}const s=t?.endsWith("/requestApproval")===!0;t&&t!=="turn/started"&&(t.startsWith("item/")||t==="error"||t==="turn/completed")&&(this.preflightCheckComplete=!0),this.activeEventId&&(!s||this.pendingApprovals.size===0)&&this.resetIdleTimer(this.activeEventId)}shouldIgnoreOutOfScopeNotification(e,t,i){if(!t||e.id!=null)return!1;const s=I(u(i.threadId),u(i.threadID));if(s&&this.threadId&&s!==this.threadId)return this.logIgnoredNotificationScope(t,s,this.readNotificationTurnId(i)),!0;const n=this.readNotificationTurnId(i);return n&&this.currentTurnId&&n!==this.currentTurnId?(this.logIgnoredNotificationScope(t,s,n),!0):!1}readNotificationTurnId(e){const t=e.turn&&typeof e.turn=="object"?e.turn:void 0;return I(u(e.turnId),u(e.turnID),u(t?.id))}logIgnoredNotificationScope(e,t,i){e!=="error"&&!e.startsWith("turn/")||a.info("codex-adapter",`[scope] ignored ${e} thread=${t??"<none>"} turn=${i??"<none>"} active_thread=${this.threadId??"<none>"} active_turn=${this.currentTurnId??"<none>"}`)}captureAuditNotification(e,t){const i=this.activeEventId;if(!e||!i)return;const s=this.auditCaptureByEventId.get(i);if(!s)return;const n=t.turn,r=typeof t.turnId=="string"&&t.turnId?t.turnId:typeof t.turn_id=="string"&&t.turn_id?t.turn_id:typeof n?.id=="string"&&n.id?n.id:this.currentTurnId;r&&(s.providerTurnId=r);const o=typeof t.threadId=="string"&&t.threadId?t.threadId:typeof t.thread_id=="string"&&t.thread_id?t.thread_id:this.threadId;if(o&&(s.threadId=o),!!Xe.has(e))try{const c=JSON.parse(JSON.stringify(t));s.events.push({sequence:++s.sequence,method:e,params:c,observedAt:new Date().toISOString()})}catch(c){const p=c instanceof Error?c.message:String(c);a.warn("codex-adapter",`Audit evidence capture skipped event=${i} method=${e}: ${p}`)}}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&&a.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,a.info("codex-adapter",`[cp-diagnose] context_window updated: method=${e??"-"} size=${s}`),this.publishContextWindowSnapshot())}probeContextWindowFields(e,t){const i=[],s=[],n=r=>{if(!r||typeof r!="object")return;if(Array.isArray(r)){for(const c of r)n(c);return}const o=r;for(const c of t)Object.prototype.hasOwnProperty.call(o,c)&&(i.push(c),s.push(String(o[c])));for(const c of Object.values(o))n(c)};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 r=Number(n);if(Number.isFinite(r))return r}}for(const s of Object.values(i)){if(Array.isArray(s)){for(const r of s){const o=this.findNumericField(r,t);if(o!=null)return o}continue}const n=this.findNumericField(s,t);if(n!=null)return n}return null}buildContextWindowSnapshot(){const e=this.currentModelContextWindow;if(!e||e<=0)return a.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),r=i==null?null:Math.max(0,100-(n??0));return a.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:r,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<te||this.pendingAutoCompact||this.compacting||(this.pendingAutoCompact=!0,a.info("codex-adapter",`[auto-compact] context_window usedPercentage=${t.toFixed(1)}% >= ${te}%, 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=>{a.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=>{a.warn("codex-adapter",`[auto-compact] compact error: ${e instanceof Error?e.message:e}`)})))}parseRateLimitSnapshot(e,t=null){try{const i=e.rateLimitsByLimitId,s=this.extractRateLimitPushLimitId(e),n=!i?.codex&&!!s&&s!=="codex",r=i?.codex??(n?{}:e.rateLimits??e),o=r.primary,c=r.secondary,p=r.credits,h=l=>{if(typeof l=="number"&&Number.isFinite(l))return l;if(typeof l=="string"){const f=Number(l);if(Number.isFinite(f))return f}return 0},m=l=>typeof l=="boolean"?l:typeof l=="string"?l.toLowerCase()==="true":!1,v=l=>{if(l==null)return null;if(typeof l=="string"){const f=l.trim();if(!f)return null;const g=Number(f);return Number.isFinite(g)&&g>1e9?new Date(g*1e3).toISOString():f}if(typeof l=="number"&&Number.isFinite(l)&&l>0){const f=l>1e12?l/1e3:l;return new Date(f*1e3).toISOString()}return null},x=l=>{const f=l.resets_in_seconds??l.resetsInSeconds,g=typeof f=="number"||typeof f=="string"?Number(f):NaN;return!Number.isFinite(g)||g<=0?null:new Date(Date.now()+g*1e3).toISOString()},w=l=>({usedPercent:h(l.used_percent??l.usedPercent),windowMinutes:h(l.window_minutes??l.windowDurationMins??l.windowMinutes),resetsAt:v(l.resets_at??l.resetsAt)??x(l)}),S=()=>({usedPercent:0,windowMinutes:0,resetsAt:null}),E=(l,f)=>l??f??S(),_=()=>{const l=[];if(o&&l.push(o),c&&l.push(c),Array.isArray(r.windows))for(const f of r.windows)f&&typeof f=="object"&&l.push(f);return l.map(w).filter(f=>f.windowMinutes>0)},y=(l,f,g)=>{const T=l.filter(C=>g(C.windowMinutes));return T.length===0?null:T.reduce((C,A)=>Math.abs(A.windowMinutes-f)<Math.abs(C.windowMinutes-f)?A:C)},N=_(),L=n?t?{primary:t.primary,secondary:t.secondary}:null:N.length>0?{primary:E(y(N,300,l=>l<1440),t?.primary),secondary:E(y(N,10080,l=>l>=1440),t?.secondary)}:o||c?{primary:o?w(o):t?.primary??S(),secondary:c?w(c):t?.secondary??S()}:null;if(!L)return null;const q=(l,f)=>{if(!f||typeof f!="object"||Array.isArray(f))return null;const g=f,T=g.limitName??g.limit_name,C=typeof T=="string"&&T.trim()?T.trim():l,A=[],z=g.primary,W=g.secondary;z&&A.push(z),W&&A.push(W);const B=A.map(w).filter(R=>R.windowMinutes>0),V=y(B,10080,R=>R>=1440)??y(B,300,R=>R<1440);return V?{id:l,label:C,...V}:null};let b;if(i&&typeof i=="object"){b=[];for(const[l,f]of Object.entries(i)){if(l==="codex")continue;const g=q(l,f);g&&b.push(g)}}else if(b=t?.extras?[...t.extras]:[],n&&s){const l=q(s,e.rateLimits??e);if(l){const f=b.findIndex(g=>g.id===l.id);f>=0?b[f]=l:b.push(l)}}return{primary:L.primary,secondary:L.secondary,credits:p?{hasCredits:m(p.has_credits??p.hasCredits),unlimited:m(p.unlimited),balance:p.balance==null?null:h(p.balance)}:t?.credits??{hasCredits:!1,unlimited:!1,balance:null},...b.length>0?{extras:b}:{}}}catch{return null}}handleApprovalRequest(e,t){const i=e.id;if(i==null)return;const s=e.method,n=`${i}`.trim(),r=`codex_${ae(this.activeSessionId??"")}_${ae(n)}`;if(this.approvalPolicy==="never"){this.sendApprovalDecision(i,"accept");return}this.activeEventId&&this.emitCodexEvent(s,e),this.pendingApprovals.set(r,{approvalId:r,approvalCommandId:n,sourceEventId:this.activeEventId??"",requestId:i,kind:t}),a.info("codex-adapter",`Pending approval stored: ${r} (kind=${t})`),this.clearIdleTimer(),this.stopComposing()}resolvePendingApproval(e){const t=e.params??{},i=I(t.approval_id,t.approvalId);if(i)return this.pendingApprovals.get(i)??null;const s=I(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)}
5
+ `)}armCompactionFallback(){this.compacting&&(this.compactingTimer&&clearTimeout(this.compactingTimer),this.compactingTimer=setTimeout(()=>{a.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")},Ve),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);a.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:de("turn/completed",e),codex_at:new Date().toISOString()},s=this.turnError,n=this.turnErrorIsRetryableOnly,r=this.turnHasVisibleOutput,o=this.turnSawVisibleFinalAnswer&&!this.turnHasDeliveredFinalAnswer;this.turnError=null,this.turnErrorIsRetryableOnly=!0,this.turnHasVisibleOutput=!1,this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1;const p=(s&&!(n&&r)?s:null)??(o?"Codex final answer was observed but not delivered":null),h=p!==null;this.callbacks.sendCodexEvent(i),this.callbacks.sendEventResult(t,h?"failed":"responded",p??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:de(e,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 r=++this.requestId,o={jsonrpc:"2.0",id:r,method:e,params:t??{}},c=setTimeout(()=>{this.pendingRequests.delete(String(r)),n(new Error(`Request timeout: ${e}`))},i);this.pendingRequests.set(String(r),{resolve:h=>{h.error?n(new Error(`JSON-RPC error: ${h.error.message}`)):s(h.result)},reject:n,timer:c});const p=JSON.stringify(o);this.process.stdin.write(`${p}
6
6
  `)})}sendNotification(e,t){if(!this.process?.stdin)return;const i={jsonrpc:"2.0",method:e,params:t??{}};this.process.stdin.write(`${JSON.stringify(i)}
7
7
  `)}async initializeHandshake(){if(this.initialized)return;const e=Ie(),t=await this.sendRequest("initialize",{clientInfo:{name:"grix-connector",title:"Grix Connector",version:e},capabilities:{experimentalApi:!0}},15e3);a.info("codex-adapter",`Initialized: ${JSON.stringify(t)}`),this.sendNotification("initialized"),this.initialized=!0,await this.refreshCodexModelOptions(),this.fetchRateLimits()}extractRateLimitPushLimitId(e){const t=s=>{if(!s||typeof s!="object"||Array.isArray(s))return null;const n=s.limitId??s.limit_id;return typeof n=="string"&&n.trim()?n.trim():null},i=e.rateLimitsByLimitId;if(i&&typeof i=="object"&&!Array.isArray(i)){const s=i;return s.codex?"codex":t(Object.values(s)[0])}return t(e.rateLimits??e)}refreshMainRateLimitsThrottled(){const e=Date.now();e-this.lastNonDefaultLimitRefreshAt<O.NON_DEFAULT_LIMIT_REFRESH_INTERVAL_MS||(this.lastNonDefaultLimitRefreshAt=e,this.fetchRateLimits())}fetchRateLimits(){Promise.resolve(this.sendRequest("account/rateLimits/read",void 0,1e4)).then(e=>{const t=this.parseRateLimitSnapshot(e);t&&(this.rateLimitSnapshot=t,a.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=>{a.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=[];if((this.legacyDeveloperInstructionsUnsupported||this.profileIdentityRefreshPending)&&this.identityInputInjectedThreadId!==this.threadId){const h=ne(this.callbacks.getAgentProfile?.());h&&(n.push({type:"text",text:h.instructions,text_elements:[]}),a.info("codex-adapter",`Injected agent identity through compatibility input for thread=${this.threadId} name="${h.agentName}"`)),this.identityInputInjectedThreadId=this.threadId,this.profileIdentityRefreshPending=!1}if(this.needsHistoryInjection&&s){this.needsHistoryInjection=!1;const h=await this.loadHistoryForInjection(s);for(const m of h)n.push(m);h.length>0&&a.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(`
8
8
  `);n.push({type:"text",text:`Conversation context:
9
9
  ${h}
10
10
 
11
- Latest user message:`,text_elements:[]})}n.push({type:"text",text:e.text,text_elements:[]});const o={threadId:this.threadId,approvalPolicy:this.approvalPolicy,input:n,model:this.model},c=Je(this.collaborationMode,this.model);c&&(o.collaborationMode=c),this.reasoningEffort&&(o.effort=this.reasoningEffort);const p=this.effectiveServiceTier();p&&(o.serviceTier=p),await this.sendRequest("turn/start",o),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(){const e=ne(this.callbacks.getAgentProfile?.()),t={approvalPolicy:this.approvalPolicy,model:this.model,cwd:this.cwd,experimentalRawEvents:!1,persistExtendedHistory:!1,...this.reasoningEffort?{effort:this.reasoningEffort}:{},...this.effectiveServiceTier()?{serviceTier:this.effectiveServiceTier()}:{}};try{let i;if(e&&!this.legacyDeveloperInstructionsUnsupported)try{i=await this.sendRequest("thread/start",{...t,developerInstructions:e.instructions})}catch(n){if(!We(n))throw n;this.legacyDeveloperInstructionsUnsupported=!0,a.warn("codex-adapter","Codex app-server does not support thread/start.developerInstructions; falling back to first-turn identity input"),i=await this.sendRequest("thread/start",t)}else i=await this.sendRequest("thread/start",t);const s=Ve(i);if(!s)throw new Error("Codex thread id is missing in thread/start result");this.threadId=s,this.identityInputInjectedThreadId=null,this.profileIdentityRefreshPending=!1,this.updateModelFromThreadResult(i),this.threadResumePending=!1,this.persistThreadId(s)}catch(i){const s=i instanceof Error?i.message:String(i);throw new Error(`Failed to start Codex thread: ${s}`)}}updateModelFromThreadResult(e){if(this.model)return;const t=Ge(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:k(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 i=await t.readHistory(e,Fe),s=[];for(const n of i){const r=(n.content??"").slice(0,je);n.direction==="inbound"?s.push({type:"text",text:r,text_elements:[]}):n.direction==="outbound"&&s.push({type:"assistant",text:r,text_elements:[]})}return s}catch(i){return a.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=I(t.session_id,t.sessionId)??this.activeSessionId??"";if(!n)return;const r=this.threadId,o=new U(r),c={adapterSessionId:r,text:""};this.activeEventId=s,this.activeSessionId=n,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.deliveredAgentMessageIds.clear(),this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.codexSequence=0,this.runTurn(c,o,s,n).catch(p=>{a.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;if(this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.currentTurnId=null,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.deliveredAgentMessageIds.clear(),this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.inFlightToolOps=0,this.consecutivePingFailures=0,this.currentTurnStartedAt=0,this.preflightCheckComplete=!1,this.clearIdleTimer(),this.pendingApprovals.clear(),t&&e?.emitEventDone!==!1){const i=this.rawCaptureRelay;i?i.completeEvent(t).catch(s=>{a.warn("codex-adapter",`Raw API audit capture did not settle cleanly: ${s instanceof Error?s.message:String(s)}`)}).finally(()=>this.emit("eventDone",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);a.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.activityGeneration+=1,this.lastIdleCheckGeneration=this.activityGeneration,this.idleNoProgressCount=0,this.consecutivePingFailures=0,this.armIdleTimer(e)}armIdleTimer(e,t=Number.POSITIVE_INFINITY){this.clearIdleTimer();const i=Date.now(),s=this.inFlightToolOps>0?Pe:Me,n=this.turnInactivityTimeoutMs===null?Number.POSITIVE_INFINITY:this.lastProgressAt+this.turnInactivityTimeoutMs-i,r=this.currentTurnStartedAt>0&&!this.preflightCheckComplete?this.currentTurnStartedAt+F-i:Number.POSITIVE_INFINITY,o=Math.max(0,Math.min(s,n,r,t));this.idleTimer=setTimeout(()=>{this.onIdleTimeout(e,o)},o)}async onIdleTimeout(e,t){if(this.activeEventId!==e)return;const i=this.lastIdleCheckAt,s=this.lastIdleCheckGeneration,n=this.ping($e).catch(()=>!1),r=this.checkSessionActivitySince(i),[o,c]=await Promise.all([n,r]);if(this.activeEventId!==e)return;const p=this.activityGeneration>s;if(this.lastIdleCheckAt=Date.now(),this.lastIdleCheckGeneration=this.activityGeneration,!this.preflightCheckComplete&&this.currentTurnStartedAt>0&&Date.now()-this.currentTurnStartedAt>=F){const m=this.activityGeneration,v=await this.inspectCurrentTurnProgress();if(this.activeEventId!==e)return;if(this.activityGeneration>m)this.preflightCheckComplete=!0;else if(v==="task_started_only"){this.declareRecoverablePreflightStuck(e,`Codex preflight stalled for ${Math.round(F/1e3)}s before model execution`);return}else v==="unknown"?this.currentTurnStartedAt=Date.now():this.preflightCheckComplete=!0}if(p||c){c&&(a.info("codex-adapter",`Idle timer: no stream output but rollout still being written, continuing: ${e}`),this.lastProgressAt=Date.now()),this.idleNoProgressCount=0,this.consecutivePingFailures=0,this.armIdleTimer(e);return}const h=Date.now()-this.lastProgressAt;if(this.turnInactivityTimeoutMs!==null&&h>=this.turnInactivityTimeoutMs){a.error("codex-adapter",`Turn inactive ${Math.round(h/1e3)}s \u2265 cap ${Math.round(this.turnInactivityTimeoutMs/1e3)}s, declaring stuck: ${e}`),this.declareStuck(e,`turn inactivity timeout after ${Math.round(h/1e3)}s with no Codex activity`);return}if(!o){if(this.consecutivePingFailures+=1,this.consecutivePingFailures<Z){a.warn("codex-adapter",`Agent idle ${t/1e3}s and ping failed (${this.consecutivePingFailures}/${Z}); retrying liveness probe: ${e}`),this.armIdleTimer(e,Oe);return}a.error("codex-adapter",`Agent idle ${t/1e3}s and ping failed ${this.consecutivePingFailures}x, declaring stuck: ${e}`),this.declareStuck(e,`agent idle ${t/1e3}s, ping failed ${this.consecutivePingFailures}x`);return}this.consecutivePingFailures=0,this.idleNoProgressCount++,a.info("codex-adapter",`Idle timer: ping ok, no stream progress (cycle=${this.idleNoProgressCount}), keeping active turn alive${this.turnInactivityTimeoutMs===null?"":` until inactivity cap (${Math.round(this.turnInactivityTimeoutMs/6e4)}min)`}: ${e}`),this.armIdleTimer(e)}declareStuck(e,t){this.callbacks.sendEventResult(e,"failed",t),this.clearActive(),this.emit("stuck")}declareRecoverablePreflightStuck(e,t){const i=this.activeSessionId??"";a.error("codex-adapter",`${t}; restarting adapter and replaying event: ${e}`),this.clearActive({runPendingAutoCompact:!1,emitEventDone:!1}),this.emit("internalError",{eventId:e,sessionId:i,errorMsg:t})}async inspectCurrentTurnProgress(){if(!this.threadId||!this.currentTurnId)return"unknown";try{return await Te(this.threadId,this.currentTurnId,this.codexHome)}catch{return"unknown"}}async checkSessionActivitySince(e){if(!this.threadId)return!1;try{return await be(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"}normalizeAgentMessagePhase(e){return typeof e!="string"?void 0:e.trim().toLowerCase()||void 0}resolveAgentMessagePhase(e,t){return this.normalizeAgentMessagePhase(t)??(e?this.agentMessagePhases.get(e):void 0)}extractAgentMessageText(e){return e?typeof e.text=="string"&&e.text.length>0?e.text:typeof e.message=="string"&&e.message.length>0?e.message:Array.isArray(e.content)&&e.content.map(i=>{if(typeof i=="string")return i;if(!i||typeof i!="object"||Array.isArray(i))return"";const s=i;return typeof s.text=="string"?s.text:""}).join("")||void 0:void 0}recordDeliveredAgentMessage(e,t){this.turnHasVisibleOutput=!0,e&&this.deliveredAgentMessageIds.add(e),t==="final_answer"&&(this.turnSawVisibleFinalAnswer=!0,this.turnHasDeliveredFinalAnswer=!0)}shouldSuppressAgentMessageDeltaDuringToolRun(e,t){return t==="final_answer"||this.inFlightToolOps<=0?!1:(a.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 ze={allow:"accept","allow-once":"accept","allow-always":"acceptForSession",deny:"deny"};function ne(d){const e=d?.agentName?.trim()??"",t=d?.introduction?.trim()??"";if(!e&&!t)return;const i=["[system-identity]"];return e&&i.push(`Your name is "${e}".`),t&&i.push(`Your self-introduction: ${t}`),i.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),i.push("[/system-identity]"),{agentName:e,instructions:i.join(`
12
- `)}}function We(d){return d instanceof Error?/unknown\s+(?:field|parameter|property)[^\n]*(?:developerInstructions|developer_instructions)/i.test(d.message):!1}function Be(d){return d&&d.length>0?[...d]:["app-server"]}function u(d){if(typeof d!="string")return;const e=d.trim();return e||void 0}function Ve(d){if(!d||typeof d!="object")return null;const e=d,t=e.thread&&typeof e.thread=="object"?e.thread:void 0;return I(u(e.threadId),u(e.threadID),u(t?.id))??null}function Ge(d){return!d||typeof d!="object"?null:u(d.model)??null}function k(d){const e=d?.trim().toLowerCase();return e==="default"||e==="plan"?e:null}function Je(d,e){const t=k(d),i=u(e);if(!(!t||!i))return{mode:t,settings:{model:i}}}function Xe(d){if(!Array.isArray(d))return[];const e=[];for(const t of d){if(typeof t=="string"){const r=t.trim();r&&e.push(Qe(r,M(r)));continue}if(!t||typeof t!="object")continue;const i=t,s=I(u(i.id),u(i.model_id),u(i.modelId),u(i.value));if(!s)continue;const n=I(u(i.displayName),u(i.display_name),u(i.name),u(i.label))??M(s);e.push({id:s,displayName:n,defaultReasoningEffort:u(i.defaultReasoningEffort)??null,supportedReasoningEfforts:j(i.supportedReasoningEfforts),defaultServiceTier:u(i.defaultServiceTier)??null,serviceTiers:H(i.serviceTiers),isDefault:i.isDefault===!0})}return e}function re(d){const e=d.toLowerCase().replace(/\/+$/,"");return!(e==="https://api.openai.com"||e==="https://api.openai.com/v1"||e.endsWith(".openai.com"))}async function Ye(d,e){const i=`${d.replace(/\/+$/,"")}/models`,s={Accept:"application/json"};e&&(s.Authorization=`Bearer ${e}`);const n=new AbortController,r=setTimeout(()=>n.abort(),1e4);try{const o=await fetch(i,{headers:s,signal:n.signal});if(!o.ok)return[];const c=await o.json();return Ke(c)}finally{clearTimeout(r)}}function Ke(d){if(!d||typeof d!="object")return[];const e=d,t=Array.isArray(e.data)?e.data:[],i=[];for(const s of t){if(!s||typeof s!="object")continue;const n=s,r=I(u(n.id),u(n.model));if(!r)continue;const o=I(u(n.displayName),u(n.display_name),u(n.name))??M(r);i.push({id:r,displayName:o,defaultReasoningEffort:u(n.defaultReasoningEffort)??null,supportedReasoningEfforts:j(n.supportedReasoningEfforts),defaultServiceTier:u(n.defaultServiceTier)??null,serviceTiers:H(n.serviceTiers),isDefault:n.isDefault===!0})}return i}function Ze(d){if(!d||typeof d!="object")return{models:[],nextCursor:null};const e=d,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 r=I(u(n.id),u(n.model));if(!r)continue;const o=I(u(n.displayName),u(n.display_name),u(n.name))??M(r);i.push({id:r,displayName:o,defaultReasoningEffort:u(n.defaultReasoningEffort)??null,supportedReasoningEfforts:j(n.supportedReasoningEfforts),defaultServiceTier:u(n.defaultServiceTier)??null,serviceTiers:H(n.serviceTiers),isDefault:n.isDefault===!0})}return{models:i,nextCursor:u(e.nextCursor)??null}}function oe(d){const e=[],t=new Set;for(const i of d){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],defaultServiceTier:i.defaultServiceTier,serviceTiers:[...i.serviceTiers],isDefault:i.isDefault}))}return e}function Qe(d,e){return{id:d,displayName:e,defaultReasoningEffort:null,supportedReasoningEfforts:[],defaultServiceTier:null,serviceTiers:[],isDefault:!1}}function j(d){if(!Array.isArray(d))return[];const e=[];for(const t of d){if(typeof t=="string"){const s=t.trim();s&&e.push(s);continue}if(!t||typeof t!="object")continue;const i=u(t.reasoningEffort);i&&e.push(i)}return e}function H(d){if(!Array.isArray(d))return[];const e=[];for(const t of d){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 i=t,s=u(i.id);s&&e.push({id:s,displayName:I(u(i.name),u(i.displayName),u(i.display_name))??s,description:u(i.description)??null})}return e}function M(d){return d.split(/[-_\s]+/).filter(Boolean).map(e=>/^(gpt|codex|o\d+)$/i.test(e)?e.toUpperCase():e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function I(...d){for(const e of d)if(e!=null){if(typeof e=="string"&&!e.trim())continue;return e}}function ae(d){return d.trim().replace(/[^a-zA-Z0-9._:-]+/g,"_")}function de(d,e){switch(d){case"turn/completed":return;case"item/agentMessage/delta":{const t=e?.params??{};return{params:{delta:t.delta,...t.itemId!==void 0?{itemId:t.itemId}:{},...t.phase!==void 0?{phase:t.phase}:{}}}}case"item/completed":{const i=(e?.params??{}).item;if(!i||typeof i!="object"||i.type!=="commandExecution")return;const s=et(i.command);return{params:{item:{type:"commandExecution",...s!==void 0?{command:s}:{},exitCode:i.exitCode,durationMs:i.durationMs}}}}default:return e}}function et(d){if(typeof d!="string")return;const e=d.trim();if(e)return e.length<=Q?e:e.slice(0,Q-ee.length)+ee}class U extends J{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){a.warn("codex-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}function tt(d){return["cannot steer a compact turn","turn not found","turn already completed","no active turn"].some(t=>d.includes(t))}export{O as CodexAdapter,de as trimCodexPayloadForWire};
11
+ Latest user message:`,text_elements:[]})}n.push({type:"text",text:e.text,text_elements:[]});const o={threadId:this.threadId,approvalPolicy:this.approvalPolicy,input:n,model:this.model},c=et(this.collaborationMode,this.model);c&&(o.collaborationMode=c),this.reasoningEffort&&(o.effort=this.reasoningEffort);const p=this.effectiveServiceTier();p&&(o.serviceTier=p),await this.sendRequest("turn/start",o),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(){const e=ne(this.callbacks.getAgentProfile?.()),t={approvalPolicy:this.approvalPolicy,model:this.model,cwd:this.cwd,experimentalRawEvents:!1,persistExtendedHistory:!1,...this.reasoningEffort?{effort:this.reasoningEffort}:{},...this.effectiveServiceTier()?{serviceTier:this.effectiveServiceTier()}:{}};try{let i;if(e&&!this.legacyDeveloperInstructionsUnsupported)try{i=await this.sendRequest("thread/start",{...t,developerInstructions:e.instructions})}catch(n){if(!Ye(n))throw n;this.legacyDeveloperInstructionsUnsupported=!0,a.warn("codex-adapter","Codex app-server does not support thread/start.developerInstructions; falling back to first-turn identity input"),i=await this.sendRequest("thread/start",t)}else i=await this.sendRequest("thread/start",t);const s=Ze(i);if(!s)throw new Error("Codex thread id is missing in thread/start result");this.threadId=s,this.identityInputInjectedThreadId=null,this.profileIdentityRefreshPending=!1,this.updateModelFromThreadResult(i),this.threadResumePending=!1,this.persistThreadId(s)}catch(i){const s=i instanceof Error?i.message:String(i);throw new Error(`Failed to start Codex thread: ${s}`)}}updateModelFromThreadResult(e){if(this.model)return;const t=Qe(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:k(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 i=await t.readHistory(e,We),s=[];for(const n of i){const r=(n.content??"").slice(0,Be);n.direction==="inbound"?s.push({type:"text",text:r,text_elements:[]}):n.direction==="outbound"&&s.push({type:"assistant",text:r,text_elements:[]})}return s}catch(i){return a.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=I(t.session_id,t.sessionId)??this.activeSessionId??"";if(!n)return;const r=this.threadId,o=new U(r),c={adapterSessionId:r,text:""};this.activeEventId=s,this.activeSessionId=n,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.deliveredAgentMessageIds.clear(),this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.codexSequence=0,this.runTurn(c,o,s,n).catch(p=>{a.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;if(this.stopComposing(),this.activeEventId=null,this.activeSessionId=null,this.currentTurnId=null,this.visibleAgentMessageIds.clear(),this.hiddenAgentMessageIds.clear(),this.agentMessagePhases.clear(),this.deliveredAgentMessageIds.clear(),this.turnSawVisibleFinalAnswer=!1,this.turnHasDeliveredFinalAnswer=!1,this.inFlightToolOps=0,this.consecutivePingFailures=0,this.currentTurnStartedAt=0,this.preflightCheckComplete=!1,this.clearIdleTimer(),this.pendingApprovals.clear(),t&&e?.emitEventDone!==!1){const i=this.rawCaptureRelay;i?i.completeEvent(t).catch(s=>{a.warn("codex-adapter",`Raw API audit capture did not settle cleanly: ${s instanceof Error?s.message:String(s)}`)}).finally(()=>this.emit("eventDone",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);a.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.activityGeneration+=1,this.lastIdleCheckGeneration=this.activityGeneration,this.idleNoProgressCount=0,this.consecutivePingFailures=0,this.armIdleTimer(e)}armIdleTimer(e,t=Number.POSITIVE_INFINITY){this.clearIdleTimer();const i=Date.now(),s=this.inFlightToolOps>0?De:Le,n=this.turnInactivityTimeoutMs===null?Number.POSITIVE_INFINITY:this.lastProgressAt+this.turnInactivityTimeoutMs-i,r=this.currentTurnStartedAt>0&&!this.preflightCheckComplete?this.currentTurnStartedAt+F-i:Number.POSITIVE_INFINITY,o=Math.max(0,Math.min(s,n,r,t));this.idleTimer=setTimeout(()=>{this.onIdleTimeout(e,o)},o)}async onIdleTimeout(e,t){if(this.activeEventId!==e)return;const i=this.lastIdleCheckAt,s=this.lastIdleCheckGeneration,n=this.ping(je).catch(()=>!1),r=this.checkSessionActivitySince(i),[o,c]=await Promise.all([n,r]);if(this.activeEventId!==e)return;const p=this.activityGeneration>s;if(this.lastIdleCheckAt=Date.now(),this.lastIdleCheckGeneration=this.activityGeneration,!this.preflightCheckComplete&&this.currentTurnStartedAt>0&&Date.now()-this.currentTurnStartedAt>=F){const m=this.activityGeneration,v=await this.inspectCurrentTurnProgress();if(this.activeEventId!==e)return;if(this.activityGeneration>m)this.preflightCheckComplete=!0;else if(v==="task_started_only"){this.declareRecoverablePreflightStuck(e,`Codex preflight stalled for ${Math.round(F/1e3)}s before model execution`);return}else v==="unknown"?this.currentTurnStartedAt=Date.now():this.preflightCheckComplete=!0}if(p||c){c&&(a.info("codex-adapter",`Idle timer: no stream output but rollout still being written, continuing: ${e}`),this.lastProgressAt=Date.now()),this.idleNoProgressCount=0,this.consecutivePingFailures=0,this.armIdleTimer(e);return}const h=Date.now()-this.lastProgressAt;if(this.turnInactivityTimeoutMs!==null&&h>=this.turnInactivityTimeoutMs){a.error("codex-adapter",`Turn inactive ${Math.round(h/1e3)}s \u2265 cap ${Math.round(this.turnInactivityTimeoutMs/1e3)}s, declaring stuck: ${e}`),this.declareStuck(e,`turn inactivity timeout after ${Math.round(h/1e3)}s with no Codex activity`);return}if(!o){if(this.consecutivePingFailures+=1,this.consecutivePingFailures<Z){a.warn("codex-adapter",`Agent idle ${t/1e3}s and ping failed (${this.consecutivePingFailures}/${Z}); retrying liveness probe: ${e}`),this.armIdleTimer(e,He);return}a.error("codex-adapter",`Agent idle ${t/1e3}s and ping failed ${this.consecutivePingFailures}x, declaring stuck: ${e}`),this.declareStuck(e,`agent idle ${t/1e3}s, ping failed ${this.consecutivePingFailures}x`);return}this.consecutivePingFailures=0,this.idleNoProgressCount++,a.info("codex-adapter",`Idle timer: ping ok, no stream progress (cycle=${this.idleNoProgressCount}), keeping active turn alive${this.turnInactivityTimeoutMs===null?"":` until inactivity cap (${Math.round(this.turnInactivityTimeoutMs/6e4)}min)`}: ${e}`),this.armIdleTimer(e)}declareStuck(e,t){this.callbacks.sendEventResult(e,"failed",t),this.clearActive(),this.emit("stuck")}declareRecoverablePreflightStuck(e,t){const i=this.activeSessionId??"";a.error("codex-adapter",`${t}; restarting adapter and replaying event: ${e}`),this.clearActive({runPendingAutoCompact:!1,emitEventDone:!1}),this.emit("internalError",{eventId:e,sessionId:i,errorMsg:t})}async inspectCurrentTurnProgress(){if(!this.threadId||!this.currentTurnId)return"unknown";try{return await _e(this.threadId,this.currentTurnId,this.codexHome)}catch{return"unknown"}}async checkSessionActivitySince(e){if(!this.threadId)return!1;try{return await be(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"}normalizeAgentMessagePhase(e){return typeof e!="string"?void 0:e.trim().toLowerCase()||void 0}resolveAgentMessagePhase(e,t){return this.normalizeAgentMessagePhase(t)??(e?this.agentMessagePhases.get(e):void 0)}extractAgentMessageText(e){return e?typeof e.text=="string"&&e.text.length>0?e.text:typeof e.message=="string"&&e.message.length>0?e.message:Array.isArray(e.content)&&e.content.map(i=>{if(typeof i=="string")return i;if(!i||typeof i!="object"||Array.isArray(i))return"";const s=i;return typeof s.text=="string"?s.text:""}).join("")||void 0:void 0}recordDeliveredAgentMessage(e,t){this.turnHasVisibleOutput=!0,e&&this.deliveredAgentMessageIds.add(e),t==="final_answer"&&(this.turnSawVisibleFinalAnswer=!0,this.turnHasDeliveredFinalAnswer=!0)}shouldSuppressAgentMessageDeltaDuringToolRun(e,t){return t==="final_answer"||this.inFlightToolOps<=0?!1:(a.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 Je={allow:"accept","allow-once":"accept","allow-always":"acceptForSession",deny:"deny"};function ne(d){const e=d?.agentName?.trim()??"",t=d?.introduction?.trim()??"";if(!e&&!t)return;const i=["[system-identity]"];return e&&i.push(`Your name is "${e}".`),t&&i.push(`Your self-introduction: ${t}`),i.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),i.push("[/system-identity]"),{agentName:e,instructions:i.join(`
12
+ `)}}function Ye(d){return d instanceof Error?/unknown\s+(?:field|parameter|property)[^\n]*(?:developerInstructions|developer_instructions)/i.test(d.message):!1}function Ke(d){return d&&d.length>0?[...d]:["app-server"]}function u(d){if(typeof d!="string")return;const e=d.trim();return e||void 0}function Ze(d){if(!d||typeof d!="object")return null;const e=d,t=e.thread&&typeof e.thread=="object"?e.thread:void 0;return I(u(e.threadId),u(e.threadID),u(t?.id))??null}function Qe(d){return!d||typeof d!="object"?null:u(d.model)??null}function k(d){const e=d?.trim().toLowerCase();return e==="default"||e==="plan"?e:null}function et(d,e){const t=k(d),i=u(e);if(!(!t||!i))return{mode:t,settings:{model:i}}}function tt(d){if(!Array.isArray(d))return[];const e=[];for(const t of d){if(typeof t=="string"){const r=t.trim();r&&e.push(rt(r,M(r)));continue}if(!t||typeof t!="object")continue;const i=t,s=I(u(i.id),u(i.model_id),u(i.modelId),u(i.value));if(!s)continue;const n=I(u(i.displayName),u(i.display_name),u(i.name),u(i.label))??M(s);e.push({id:s,displayName:n,defaultReasoningEffort:u(i.defaultReasoningEffort)??null,supportedReasoningEfforts:j(i.supportedReasoningEfforts),defaultServiceTier:u(i.defaultServiceTier)??null,serviceTiers:H(i.serviceTiers),isDefault:i.isDefault===!0})}return e}function re(d){const e=d.toLowerCase().replace(/\/+$/,"");return!(e==="https://api.openai.com"||e==="https://api.openai.com/v1"||e.endsWith(".openai.com"))}async function it(d,e){const i=`${d.replace(/\/+$/,"")}/models`,s={Accept:"application/json"};e&&(s.Authorization=`Bearer ${e}`);const n=new AbortController,r=setTimeout(()=>n.abort(),1e4);try{const o=await fetch(i,{headers:s,signal:n.signal});if(!o.ok)return[];const c=await o.json();return st(c)}finally{clearTimeout(r)}}function st(d){if(!d||typeof d!="object")return[];const e=d,t=Array.isArray(e.data)?e.data:[],i=[];for(const s of t){if(!s||typeof s!="object")continue;const n=s,r=I(u(n.id),u(n.model));if(!r)continue;const o=I(u(n.displayName),u(n.display_name),u(n.name))??M(r);i.push({id:r,displayName:o,defaultReasoningEffort:u(n.defaultReasoningEffort)??null,supportedReasoningEfforts:j(n.supportedReasoningEfforts),defaultServiceTier:u(n.defaultServiceTier)??null,serviceTiers:H(n.serviceTiers),isDefault:n.isDefault===!0})}return i}function nt(d){if(!d||typeof d!="object")return{models:[],nextCursor:null};const e=d,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 r=I(u(n.id),u(n.model));if(!r)continue;const o=I(u(n.displayName),u(n.display_name),u(n.name))??M(r);i.push({id:r,displayName:o,defaultReasoningEffort:u(n.defaultReasoningEffort)??null,supportedReasoningEfforts:j(n.supportedReasoningEfforts),defaultServiceTier:u(n.defaultServiceTier)??null,serviceTiers:H(n.serviceTiers),isDefault:n.isDefault===!0})}return{models:i,nextCursor:u(e.nextCursor)??null}}function oe(d){const e=[],t=new Set;for(const i of d){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],defaultServiceTier:i.defaultServiceTier,serviceTiers:[...i.serviceTiers],isDefault:i.isDefault}))}return e}function rt(d,e){return{id:d,displayName:e,defaultReasoningEffort:null,supportedReasoningEfforts:[],defaultServiceTier:null,serviceTiers:[],isDefault:!1}}function j(d){if(!Array.isArray(d))return[];const e=[];for(const t of d){if(typeof t=="string"){const s=t.trim();s&&e.push(s);continue}if(!t||typeof t!="object")continue;const i=u(t.reasoningEffort);i&&e.push(i)}return e}function H(d){if(!Array.isArray(d))return[];const e=[];for(const t of d){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 i=t,s=u(i.id);s&&e.push({id:s,displayName:I(u(i.name),u(i.displayName),u(i.display_name))??s,description:u(i.description)??null})}return e}function M(d){return d.split(/[-_\s]+/).filter(Boolean).map(e=>/^(gpt|codex|o\d+)$/i.test(e)?e.toUpperCase():e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}function I(...d){for(const e of d)if(e!=null){if(typeof e=="string"&&!e.trim())continue;return e}}function ae(d){return d.trim().replace(/[^a-zA-Z0-9._:-]+/g,"_")}function de(d,e){switch(d){case"turn/completed":return;case"item/agentMessage/delta":{const t=e?.params??{};return{params:{delta:t.delta,...t.itemId!==void 0?{itemId:t.itemId}:{},...t.phase!==void 0?{phase:t.phase}:{}}}}case"item/completed":{const i=(e?.params??{}).item;if(!i||typeof i!="object"||i.type!=="commandExecution")return;const s=ot(i.command);return{params:{item:{type:"commandExecution",...s!==void 0?{command:s}:{},exitCode:i.exitCode,durationMs:i.durationMs}}}}default:return e}}function ot(d){if(typeof d!="string")return;const e=d.trim();if(e)return e.length<=Q?e:e.slice(0,Q-ee.length)+ee}class U extends X{adapterSessionId;constructor(e){super(),this.adapterSessionId=e}emitDone(e){this.emit("done",e)}emitError(e){if(this.listenerCount("error")===0){a.warn("codex-adapter",`Prompt handle error (no listeners): ${e.message}`);return}this.emit("error",e)}async cancel(){}}function at(d){return["cannot steer a compact turn","turn not found","turn already completed","no active turn"].some(t=>d.includes(t))}export{O as CodexAdapter,de as trimCodexPayloadForWire};
@@ -1 +1 @@
1
- import{RawApiCaptureRelay as d}from"../../audit/raw-capture/capture-relay.js";import{readCodexProviderSettings as p}from"./codex-trust.js";const u=/^[A-Za-z0-9_-]+$/;function c(a,e){for(let o=a.length-1;o>=0;o-=1){const r=a[o];if(!r.startsWith(`${e}=`))continue;const t=r.slice(e.length+1).trim();try{const n=JSON.parse(t);if(typeof n=="string"&&n.trim())return n.trim()}catch{if(t)return t.replace(/^['"]|['"]$/g,"").trim()||void 0}}}function l(a,e){const o=p(a),r=c(e,"model_provider")??o.providerName;if(!u.test(r))throw new Error(`Unsupported Codex provider id for raw capture: ${r}`);if(r==="amazon-bedrock")throw new Error("Amazon Bedrock signed requests cannot use the raw capture relay");const t=r==="openai"?"openai_base_url":`model_providers.${r}.base_url`,i=c(e,t)??(r===o.providerName?o.resolvedBaseUrl:void 0);if(!i)throw new Error(`Codex provider base URL is unavailable for ${r}`);const s=new URL(i);if(s.protocol!=="http:"&&s.protocol!=="https:")throw new Error(`Codex provider base URL is not HTTP(S): ${r}`);return{providerName:r,targetBaseUrl:s.toString().replace(/\/$/,""),configKey:t}}class f{configKey;constructor(e){this.configKey=e}canEnableRawCapture(e){return e.adapterType==="codex"&&!!e.sessionId}async prepareRawCapture(e,o){if(!this.canEnableRawCapture(e))throw new Error("Codex raw capture hook received an incompatible runtime");return{configOverrides:[`${this.configKey}=${JSON.stringify(o.baseUrl)}`]}}}async function v(a,e){const o=l(e.codexHome,e.configOverrides??[]),r={auditId:a.auditId,sessionId:a.sessionId,provider:"codex",targetBaseUrl:o.targetBaseUrl,spoolRootDir:a.spoolRootDir,transportEnv:e.env},t=await new d(r).start(),n=new f(o.configKey);try{const i=await n.prepareRawCapture(e,t);return{relay:t,overlay:i}}catch(i){throw await t.stop(),i}}export{f as CodexRawCaptureHook,v as prepareCodexRawCapture,l as resolveCodexRawCaptureTarget};
1
+ import{RawApiCaptureRelay as u}from"../../audit/raw-capture/capture-relay.js";import{readCodexProviderSettings as d}from"./codex-trust.js";const p=/^[A-Za-z0-9_-]+$/,l=new Set(["openai","ollama","lmstudio"]),_="grix-connector-openai-relay",f="grix-connector-openai-raw-capture";function c(e,r){for(let o=e.length-1;o>=0;o-=1){const t=e[o];if(!t.startsWith(`${r}=`))continue;const i=t.slice(r.length+1).trim();try{const n=JSON.parse(i);if(typeof n=="string"&&n.trim())return n.trim()}catch{if(i)return i.replace(/^['"]|['"]$/g,"").trim()||void 0}}}function w(e,r){const o=d(e),t=c(r,"model_provider")??o.providerName;if(!p.test(t))throw new Error(`Unsupported Codex provider id: ${t}`);return t}function g(e){if(!p.test(e))throw new Error(`Unsupported Codex provider id: ${e}`);if(!l.has(e))return`model_providers.${e}.supports_websockets`}function v(e,r,o){if(!p.test(e))throw new Error(`Unsupported Codex provider id: ${e}`);return[`model_provider=${JSON.stringify(e)}`,`model_providers.${e}={ name = ${JSON.stringify(r)}, base_url = ${JSON.stringify(o)}, requires_openai_auth = true, supports_websockets = false }`]}function C(e,r){const o=d(e),t=w(e,r);if(t==="amazon-bedrock")throw new Error("Amazon Bedrock signed requests cannot use the raw capture relay");const i=t==="openai"?"openai_base_url":`model_providers.${t}.base_url`,s=c(r,i)??(t===o.providerName?o.resolvedBaseUrl:void 0);if(!s)throw new Error(`Codex provider base URL is unavailable for ${t}`);const a=new URL(s);if(a.protocol!=="http:"&&a.protocol!=="https:")throw new Error(`Codex provider base URL is not HTTP(S): ${t}`);return{providerName:t,targetBaseUrl:a.toString().replace(/\/$/,""),configKey:i}}class h{configKey;supportsWebsocketsConfigKey;openAiCompatibleProvider;constructor(r,o,t){this.configKey=r,this.supportsWebsocketsConfigKey=o,this.openAiCompatibleProvider=t}canEnableRawCapture(r){return r.adapterType==="codex"&&!!r.sessionId}async prepareRawCapture(r,o){if(!this.canEnableRawCapture(r))throw new Error("Codex raw capture hook received an incompatible runtime");return this.openAiCompatibleProvider?{configOverrides:v(this.openAiCompatibleProvider.providerId,this.openAiCompatibleProvider.displayName,o.baseUrl)}:{configOverrides:[`${this.configKey}=${JSON.stringify(o.baseUrl)}`,...this.supportsWebsocketsConfigKey?[`${this.supportsWebsocketsConfigKey}=false`]:[]]}}}async function x(e,r){const o=C(r.codexHome,r.configOverrides??[]),t={auditId:e.auditId,sessionId:e.sessionId,provider:"codex",targetBaseUrl:o.targetBaseUrl,spoolRootDir:e.spoolRootDir,transportEnv:r.env},i=await new u(t).start(),n=new h(o.configKey,g(o.providerName),o.providerName==="openai"?{providerId:f,displayName:"Grix OpenAI Raw Capture"}:void 0);try{const s=await n.prepareRawCapture(r,i);return{relay:i,overlay:s}}catch(s){throw await i.stop(),s}}export{f as CODEX_RAW_CAPTURE_OPENAI_PROVIDER_ID,_ as CODEX_RELAY_OPENAI_PROVIDER_ID,h as CodexRawCaptureHook,v as buildCodexOpenAiCompatibleProviderOverrides,x as prepareCodexRawCapture,w as resolveCodexProviderName,g as resolveCodexProviderSupportsWebsocketsConfigKey,C as resolveCodexRawCaptureTarget};
@@ -1,10 +1,10 @@
1
- import{execFile as w}from"node:child_process";import{promisify as P}from"node:util";import{EventEmitter as S}from"node:events";import{stat as b}from"node:fs/promises";import{mkdirSync as T,writeFileSync as $,unlinkSync as A,statSync as m,readFileSync as k,existsSync as C}from"node:fs";import{join as u,resolve as R}from"node:path";import{fileURLToPath as M}from"node:url";import{tmpdir as F,homedir as f}from"node:os";import{resolveCommandPath as B,spawnCommand as D,killProcessGroup as I,hasChildProcesses as N}from"../../core/runtime/spawn.js";import{InternalApiServer as G}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as L}from"../shared/identity-injector.js";import{compactToolInputForWire as O}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as j}from"../../default-skills/index.js";import{PiTransport as q}from"./pi-transport.js";import{log as a}from"../../core/log/index.js";import{scanSkills as U}from"../claude/skill-scanner.js";import{SessionBindingStore as z}from"../../core/persistence/session-binding-store.js";import{splitTextForAibotProtocol as H}from"../../core/protocol/index.js";import{buildSimpleProbeReport as W}from"../shared/probe-util.js";import{piGrixProviderId as Y,removePiGrixProvider as J,writePiGrixProvider as V}from"./pi-provider-config.js";const x=new Map;function X(){const d=u(f(),".pi","agent","mcp.json");try{if(!C(d))return{};const e=JSON.parse(k(d,"utf8"))?.mcpServers;return!e||typeof e!="object"||Array.isArray(e)?{}:e}catch(t){return a.warn("pi-adapter",`Failed to read Pi user MCP config ${d}: ${t instanceof Error?t.message:String(t)}`),{}}}async function K(d){const t=x.get(d);if(t!==void 0)return t;let e=!1;try{const s=P(w),i=process.platform==="win32",n=i&&/\s/.test(d)&&!d.startsWith('"')?`"${d}"`:d,{stdout:r,stderr:c}=await s(n,["--help"],{timeout:5e3,encoding:"utf-8",...i?{shell:!0}:{}});e=/--mcp-config\b/.test(`${r??""}${c??""}`)}catch{e=!1}return x.set(d,e),a.info("pi-adapter",`Pi --mcp-config \u652F\u6301\u63A2\u6D4B: ${e?"\u652F\u6301":"\u4E0D\u652F\u6301"} (${d})`),e}class Q extends S{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitDone(t){this.emit("done",t)}emitError(t){if(this.listenerCount("error")===0){a.warn("pi-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const Z=12e4,tt=90*1e3,E=1800*1e3;function y(d){try{return process.kill(d,0),!0}catch(t){return t.code==="EPERM"}}const et=500,st=2e3,g=3,it=500;function nt(d){return new Promise(t=>setTimeout(t,d))}function p(d){if(d===void 0)return"undefined";if(d===null)return"null";if(typeof d=="string")return d.length>500?`${d.slice(0,500)}...`:d;try{const t=JSON.stringify(d);return t.length>500?`${t.slice(0,500)}...`:t}catch{return String(d)}}class _ extends S{type="pi";config;callbacks;process=null;transport=new q;alive=!1;stopped=!1;internalApi=null;mcpConfigPath=null;identity;piSessionPath=null;activeEventId=null;activeSessionId=null;activeAuditCapture=null;completedAuditBoundaries=new Map;isStreaming=!1;streamSeq=0;clientMsgSeq=0;activeClientMsgId=null;thinkingSeq=0;textBuffer="";emittedTextByIndex=new Map;textFlushTimer=null;activeRunError=null;idleTimer=null;lastRpcAt=0;livenessExtendStartAt;toolCallsInFlight=new Set;composingSessionId=null;composingInterval=null;doneGuardTimer=null;bindingStore=null;aibotSessionId="";sessionReadyPromise=null;provider;agentName="";constructor(t,e){super(),this.config=t,this.callbacks=e,this.identity=new L("pi-adapter",e.getAgentProfile);const s=t.options??{};this.aibotSessionId=String(s.aibotSessionId??"").trim(),this.agentName=String(s.agentName??"").trim(),this.provider=s.provider,this.bindingStore=s.bindingStore instanceof z?s.bindingStore:null,this.bindingStore&&this.aibotSessionId&&(this.piSessionPath=this.bindingStore.getPiSessionPath(this.aibotSessionId)??null)}async start(){await this.startInternalApi(),await this.spawnPi(),await this.ensureSessionReady(),a.info("pi-adapter",`Ready (pid=${this.process?.pid})`),await this.syncModelBinding()}async syncModelBinding(){if(this.aibotSessionId)for(let t=1;t<=g;t++)try{const[e,s]=await Promise.all([this.transport.send("get_available_models"),this.transport.send("get_state")]),i=e.data?.models;if(!i||i.length===0){if(t<g){await nt(it);continue}a.warn("pi-adapter",`syncModelBinding: empty model list after ${g} attempts, giving up`);return}const n=s.data?.model,r={available_models:i.map(c=>({id:c.id,display_name:c.name??c.id,...c.provider?{provider:c.provider}:{}}))};n?.id&&(r.model_id=n.id),n?.provider&&(r.model_provider=n.provider),this.callbacks.sendUpdateBindingCard(this.aibotSessionId,this.isStreaming?"composing":"ready",this.resolveCwd(),r),a.info("pi-adapter",`synced model binding: ${i.length} models, current=${n?.id??"unknown"} (attempt ${t})`);return}catch(e){a.warn("pi-adapter",`syncModelBinding failed (non-fatal): ${e instanceof Error?e.message:String(e)}`);return}}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.cancelDoneGuard(),this.transport.close(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.mcpConfigPath){try{A(this.mcpConfigPath)}catch{}this.mcpConfigPath=null}if(this.process){const t=this.process;try{I(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{I(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){this.sessionReadyPromise=null,await this.createNewSession();const e=this.piSessionPath||`pi-${Date.now()}`;return a.info("pi-adapter",`Session created: ${e} (path=${this.piSessionPath})`),e}async resumeSession(t,e){await this.switchSession()}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.piSessionPath=null,this.sessionReadyPromise=null,this.persistPiSessionPath(void 0)}sendPrompt(t){const e=new Q(t.adapterSessionId),s=this.buildPromptTextFromRequest(t);return this.ensureSessionReady().then(()=>this.transport.send("prompt",{message:s})).then(i=>{i.success||e.emitDone({status:"failed",error:i.error})}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){try{await this.transport.send("abort")}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t,n=this.buildPromptText(t);if(this.isStreaming){if(this.activeEventId&&this.activeEventId!==e){const r=this.activeEventId;a.info("pi-adapter",`steer: cancel ${r} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(r,"canceled","steered to new event"),this.activeAuditCapture?.eventId===r&&(this.snapshotAuditBoundary(r),this.emit("eventDone",r))}this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n,streamingBehavior:"steer"}).catch(r=>{a.error("pi-adapter",`steer failed: ${r}`),this.callbacks.sendEventResult(e,"failed",String(r))})}else a.info("pi-adapter",`prompt: event=${e} session=${s}`),this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.isStreaming=!0,this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n}).then(r=>{r.success||(a.error("pi-adapter",`prompt rejected: ${r.error}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",r.error),this.clearActive())}).catch(r=>{a.error("pi-adapter",`prompt error: ${r}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",String(r)),this.clearActive()})}deliverStopEvent(t,e){if(this.activeEventId===t){a.info("pi-adapter",`stop: event=${t}, releasing busy immediately`),this.transport.send("abort").catch(()=>{}),this.flushTextBuffer();const s=this.nextStreamSeq(),i=this.activeClientMsgId??void 0;this.callbacks.sendStreamChunk(t,this.activeSessionId??"","",s,!0,i),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActive()}}async handleLocalAction(t){const e=t.action_id;switch(t.action_type){case"set_model":{const s=t.params??{},i=(s.modelId??s.model_id??"").trim();if(!i)return this.callbacks.sendLocalActionResult(e,"failed",void 0,"invalid_model","model_id is required"),{handled:!0,kind:"set_model_error"};try{let n=s.provider?.trim();return n||(n=(await this.transport.send("get_available_models")).data?.models?.find(l=>l.id===i)?.provider?.trim()),n?(await this.transport.send("set_model",{provider:n,modelId:i}),this.callbacks.sendLocalActionResult(e,"ok",{outcome:"model_set",provider:n,modelId:i}),this.syncModelBinding(),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e,"failed",void 0,"model_not_found",`No provider for model: ${i}`),{handled:!0,kind:"set_model_error"})}catch(n){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"set_model_error",n instanceof Error?n.message:String(n)),{handled:!0,kind:"set_model_error"}}}case"get_context":try{const s=await this.transport.send("get_state");return this.callbacks.sendLocalActionResult(e,"ok",{state:s}),{handled:!0,kind:"get_context"}}catch(s){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"get_context_error",s instanceof Error?s.message:String(s)),{handled:!1,kind:""}}case"pi_extension_ui_response":return this.transport.sendNoWait({type:"extension_ui_response",...t.params??{}}),this.callbacks.sendLocalActionResult(e,"ok"),{handled:!0,kind:"extension_ui_response"};default:return{handled:!1,kind:""}}}setPermissionHandler(t){}async ping(t){try{return await this.transport.send("get_state"),!0}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.isStreaming,sessions:this.piSessionPath?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}clearActiveEventForShutdown(){this.stopIdleTimer(),this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const t=R(M(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?N(t,[t]):!1}async probe(t){const e=this.getStatus();return{...await W(this.config.command||"pi",{alive:e.alive,busy:e.busy,started:!!this.process},t),session:this.probeSessionRecord()}}probeSessionRecord(){const t=this.piSessionPath;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const e=m(t);return{recordPath:t,lastActivityMs:e.mtimeMs,freshMs:Date.now()-e.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"model",description:"List or set model",args:"[provider:model_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(t,e,s){try{if(!this.alive)return{status:"failed",message:"Pi process not running"};switch(t){case"model":{const i=e.trim();if(i){const c=i.indexOf(":");if(c<1)return{status:"failed",message:"Format: provider:model_id"};const l=i.slice(0,c),h=i.slice(c+1);if(!h)return{status:"failed",message:"Format: provider:model_id"};const o=await this.transport.send("set_model",{provider:l,modelId:h});return o?.success?{status:"ok",message:`Model set to ${l}:${h}`}:{status:"failed",message:`Failed to set model: ${o?.error??"unknown error"}`}}const r=(await this.transport.send("get_available_models"))?.data?.models;return r&&r.length>0?{status:"ok",message:`Available models:
1
+ import{execFile as P}from"node:child_process";import{promisify as w}from"node:util";import{EventEmitter as S}from"node:events";import{stat as b}from"node:fs/promises";import{mkdirSync as T,writeFileSync as $,unlinkSync as A,statSync as m,readFileSync as k,existsSync as R}from"node:fs";import{join as u,resolve as C}from"node:path";import{fileURLToPath as M}from"node:url";import{tmpdir as F,homedir as f}from"node:os";import{resolveCommandPath as B,spawnCommand as D,killProcessGroup as I,hasChildProcesses as N}from"../../core/runtime/spawn.js";import{InternalApiServer as G}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as L}from"../shared/identity-injector.js";import{compactToolInputForWire as O}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as j}from"../../default-skills/index.js";import{PiTransport as q}from"./pi-transport.js";import{log as a}from"../../core/log/index.js";import{scanSkills as U}from"../claude/skill-scanner.js";import{SessionBindingStore as z}from"../../core/persistence/session-binding-store.js";import{splitTextForAibotProtocol as H}from"../../core/protocol/index.js";import{buildSimpleProbeReport as W}from"../shared/probe-util.js";import{piGrixProviderId as Y,removePiGrixProvider as J,writePiGrixProvider as V}from"./pi-provider-config.js";const E=new Map;function X(){const d=u(f(),".pi","agent","mcp.json");try{if(!R(d))return{};const e=JSON.parse(k(d,"utf8"))?.mcpServers;return!e||typeof e!="object"||Array.isArray(e)?{}:e}catch(t){return a.warn("pi-adapter",`Failed to read Pi user MCP config ${d}: ${t instanceof Error?t.message:String(t)}`),{}}}async function K(d){const t=E.get(d);if(t!==void 0)return t;let e=!1;try{const s=w(P),i=process.platform==="win32",n=i&&/\s/.test(d)&&!d.startsWith('"')?`"${d}"`:d,{stdout:r,stderr:c}=await s(n,["--help"],{timeout:5e3,encoding:"utf-8",...i?{shell:!0}:{}});e=/--mcp-config\b/.test(`${r??""}${c??""}`)}catch{e=!1}return E.set(d,e),a.info("pi-adapter",`Pi --mcp-config \u652F\u6301\u63A2\u6D4B: ${e?"\u652F\u6301":"\u4E0D\u652F\u6301"} (${d})`),e}class Q extends S{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitDone(t){this.emit("done",t)}emitError(t){if(this.listenerCount("error")===0){a.warn("pi-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const Z=12e4,tt=90*1e3,x=1800*1e3;function y(d){try{return process.kill(d,0),!0}catch(t){return t.code==="EPERM"}}const et=500,st=2e3,g=3,it=500;function nt(d){return new Promise(t=>setTimeout(t,d))}function p(d){if(d===void 0)return"undefined";if(d===null)return"null";if(typeof d=="string")return d.length>500?`${d.slice(0,500)}...`:d;try{const t=JSON.stringify(d);return t.length>500?`${t.slice(0,500)}...`:t}catch{return String(d)}}class _ extends S{type="pi";config;callbacks;process=null;transport=new q;alive=!1;stopped=!1;internalApi=null;mcpConfigPath=null;identity;piSessionPath=null;activeEventId=null;activeSessionId=null;activeAuditCapture=null;completedAuditBoundaries=new Map;isStreaming=!1;streamSeq=0;clientMsgSeq=0;activeClientMsgId=null;thinkingSeq=0;textBuffer="";emittedTextByIndex=new Map;textFlushTimer=null;activeRunError=null;runErrorEmitted=!1;idleTimer=null;lastRpcAt=0;livenessExtendStartAt;toolCallsInFlight=new Set;composingSessionId=null;composingInterval=null;doneGuardTimer=null;bindingStore=null;aibotSessionId="";sessionReadyPromise=null;provider;agentName="";constructor(t,e){super(),this.config=t,this.callbacks=e,this.identity=new L("pi-adapter",e.getAgentProfile);const s=t.options??{};this.aibotSessionId=String(s.aibotSessionId??"").trim(),this.agentName=String(s.agentName??"").trim(),this.provider=s.provider,this.bindingStore=s.bindingStore instanceof z?s.bindingStore:null,this.bindingStore&&this.aibotSessionId&&(this.piSessionPath=this.bindingStore.getPiSessionPath(this.aibotSessionId)??null)}async start(){await this.startInternalApi(),await this.spawnPi(),await this.ensureSessionReady(),a.info("pi-adapter",`Ready (pid=${this.process?.pid})`),await this.syncModelBinding()}async syncModelBinding(){if(this.aibotSessionId)for(let t=1;t<=g;t++)try{const[e,s]=await Promise.all([this.transport.send("get_available_models"),this.transport.send("get_state")]),i=e.data?.models;if(!i||i.length===0){if(t<g){await nt(it);continue}a.warn("pi-adapter",`syncModelBinding: empty model list after ${g} attempts, giving up`);return}const n=s.data?.model,r={available_models:i.map(c=>({id:c.id,display_name:c.name??c.id,...c.provider?{provider:c.provider}:{}}))};n?.id&&(r.model_id=n.id),n?.provider&&(r.model_provider=n.provider),this.callbacks.sendUpdateBindingCard(this.aibotSessionId,this.isStreaming?"composing":"ready",this.resolveCwd(),r),a.info("pi-adapter",`synced model binding: ${i.length} models, current=${n?.id??"unknown"} (attempt ${t})`);return}catch(e){a.warn("pi-adapter",`syncModelBinding failed (non-fatal): ${e instanceof Error?e.message:String(e)}`);return}}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.cancelDoneGuard(),this.transport.close(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.mcpConfigPath){try{A(this.mcpConfigPath)}catch{}this.mcpConfigPath=null}if(this.process){const t=this.process;try{I(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{I(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){this.sessionReadyPromise=null,await this.createNewSession();const e=this.piSessionPath||`pi-${Date.now()}`;return a.info("pi-adapter",`Session created: ${e} (path=${this.piSessionPath})`),e}async resumeSession(t,e){await this.switchSession()}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.piSessionPath=null,this.sessionReadyPromise=null,this.persistPiSessionPath(void 0)}sendPrompt(t){const e=new Q(t.adapterSessionId),s=this.buildPromptTextFromRequest(t);return this.ensureSessionReady().then(()=>this.transport.send("prompt",{message:s})).then(i=>{i.success||e.emitDone({status:"failed",error:i.error})}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){try{await this.transport.send("abort")}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t,n=this.buildPromptText(t);if(this.isStreaming){if(this.activeEventId&&this.activeEventId!==e){const r=this.activeEventId;a.info("pi-adapter",`steer: cancel ${r} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(r,"canceled","steered to new event"),this.activeAuditCapture?.eventId===r&&(this.snapshotAuditBoundary(r),this.emit("eventDone",r))}this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n,streamingBehavior:"steer"}).catch(r=>{a.error("pi-adapter",`steer failed: ${r}`),this.callbacks.sendEventResult(e,"failed",String(r))})}else a.info("pi-adapter",`prompt: event=${e} session=${s}`),this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.isStreaming=!0,this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n}).then(r=>{r.success||(a.error("pi-adapter",`prompt rejected: ${r.error}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",r.error),this.clearActive())}).catch(r=>{a.error("pi-adapter",`prompt error: ${r}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",String(r)),this.clearActive()})}deliverStopEvent(t,e){if(this.activeEventId===t){a.info("pi-adapter",`stop: event=${t}, releasing busy immediately`),this.transport.send("abort").catch(()=>{}),this.flushTextBuffer();const s=this.nextStreamSeq(),i=this.activeClientMsgId??void 0;this.callbacks.sendStreamChunk(t,this.activeSessionId??"","",s,!0,i),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActive()}}async handleLocalAction(t){const e=t.action_id;switch(t.action_type){case"set_model":{const s=t.params??{},i=(s.modelId??s.model_id??"").trim();if(!i)return this.callbacks.sendLocalActionResult(e,"failed",void 0,"invalid_model","model_id is required"),{handled:!0,kind:"set_model_error"};try{let n=s.provider?.trim();return n||(n=(await this.transport.send("get_available_models")).data?.models?.find(l=>l.id===i)?.provider?.trim()),n?(await this.transport.send("set_model",{provider:n,modelId:i}),this.callbacks.sendLocalActionResult(e,"ok",{outcome:"model_set",provider:n,modelId:i}),this.syncModelBinding(),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e,"failed",void 0,"model_not_found",`No provider for model: ${i}`),{handled:!0,kind:"set_model_error"})}catch(n){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"set_model_error",n instanceof Error?n.message:String(n)),{handled:!0,kind:"set_model_error"}}}case"get_context":try{const s=await this.transport.send("get_state");return this.callbacks.sendLocalActionResult(e,"ok",{state:s}),{handled:!0,kind:"get_context"}}catch(s){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"get_context_error",s instanceof Error?s.message:String(s)),{handled:!1,kind:""}}case"pi_extension_ui_response":return this.transport.sendNoWait({type:"extension_ui_response",...t.params??{}}),this.callbacks.sendLocalActionResult(e,"ok"),{handled:!0,kind:"extension_ui_response"};default:return{handled:!1,kind:""}}}setPermissionHandler(t){}async ping(t){try{return await this.transport.send("get_state"),!0}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.isStreaming,sessions:this.piSessionPath?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}clearActiveEventForShutdown(){this.stopIdleTimer(),this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const t=C(M(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?N(t,[t]):!1}async probe(t){const e=this.getStatus();return{...await W(this.config.command||"pi",{alive:e.alive,busy:e.busy,started:!!this.process},t),session:this.probeSessionRecord()}}probeSessionRecord(){const t=this.piSessionPath;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const e=m(t);return{recordPath:t,lastActivityMs:e.mtimeMs,freshMs:Date.now()-e.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"model",description:"List or set model",args:"[provider:model_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(t,e,s){try{if(!this.alive)return{status:"failed",message:"Pi process not running"};switch(t){case"model":{const i=e.trim();if(i){const c=i.indexOf(":");if(c<1)return{status:"failed",message:"Format: provider:model_id"};const l=i.slice(0,c),h=i.slice(c+1);if(!h)return{status:"failed",message:"Format: provider:model_id"};const o=await this.transport.send("set_model",{provider:l,modelId:h});return o?.success?{status:"ok",message:`Model set to ${l}:${h}`}:{status:"failed",message:`Failed to set model: ${o?.error??"unknown error"}`}}const r=(await this.transport.send("get_available_models"))?.data?.models;return r&&r.length>0?{status:"ok",message:`Available models:
2
2
  ${r.map(l=>` ${l.provider??"unknown"}:${l.id} (${l.name??l.id})`).join(`
3
3
  `)}`,data:r}:{status:"ok",message:"No models available",data:[]}}case"interrupt":return this.isStreaming?(await this.transport.send("abort"),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","interrupted"),this.clearActive(),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};case"status":{const i=this.getStatus();return{status:"ok",message:`Alive: ${i.alive}, Busy: ${i.busy}, Session: ${this.piSessionPath??"none"}`,data:{alive:i.alive,busy:i.busy,sessions:i.sessions,sessionPath:this.piSessionPath}}}case"skills":{const i=U({mode:"pi"}),n=i.map(r=>`- ${r.name}${r.trigger?` (${r.trigger})`:""} [${r.source}]: ${r.description}`);return{status:"ok",message:n.length>0?n.join(`
4
- `):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${t}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}async startInternalApi(){try{this.internalApi=new G,this.internalApi.setInvokeHandler(async(r,c,l,h)=>this.callbacks.agentInvoke(r,c,h)),await this.internalApi.start(0),a.info("pi-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=X(),s=u(F(),"grix-pi-mcp");T(s,{recursive:!0}),this.mcpConfigPath=u(s,`mcp-${process.pid}-${Date.now()}.json`),$(this.mcpConfigPath,JSON.stringify({mcpServers:{...e,[t.name]:{command:t.command,args:t.args,directTools:!0,lifecycle:"eager"}}}),"utf8"),a.info("pi-adapter",`MCP config written to ${this.mcpConfigPath}`);const i=u(f(),".pi","agent","skills"),n=j(i);n.length>0&&a.info("pi-adapter",`Synced connector skills to ${i}: [${n.join(", ")}]`)}catch(t){a.warn("pi-adapter",`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnPi(){const t=this.config.command||"pi",e=B(t,typeof process.env.PATH=="string"?process.env.PATH:void 0),s=this.config.args??[],n=s.some(o=>o.startsWith("--mode"))?[...s]:["--mode","rpc",...s],r=String(this.config.env?.PI_CODING_AGENT_DIR??"").trim()||u(f(),".pi","agent"),c=Y(this.agentName);this.provider?await V(r,c,this.provider)?(n.includes("--provider")||n.push("--provider",c),!n.includes("--model")&&this.provider.model?.trim()&&n.push("--model",this.provider.model.trim())):a.warn("pi-adapter","provider config incomplete (baseUrl/model required); Grix provider not applied"):await J(r,c).then(o=>{o&&a.info("pi-adapter",`Removed stale Grix provider "${c}" from models.json`)}).catch(o=>{a.warn("pi-adapter",`Failed to clean up Grix provider config: ${o instanceof Error?o.message:String(o)}`)}),this.mcpConfigPath&&!n.some(o=>o==="--mcp-config")&&(await K(e)?n.push("--mcp-config",this.mcpConfigPath):a.warn("pi-adapter","\u5F53\u524D Pi \u4E0D\u652F\u6301 --mcp-config\uFF08\u672A\u68C0\u6D4B\u5230 pi-mcp-adapter \u6269\u5C55\uFF09\uFF0C\u8DF3\u8FC7 MCP \u6CE8\u5165\u4EE5\u907F\u514D\u542F\u52A8\u5931\u8D25"));const l={...process.env,...this.config.env},h=this.resolveCwd();a.info("pi-adapter",`Spawning: ${e} ${n.join(" ")}`);try{if(!(await b(h)).isDirectory())throw new Error(`Bound path is not a directory: ${h}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${h}. Please rebind with /grix open <valid-directory>.`):o}try{this.process=D(e,n,{env:l,cwd:h}).process}catch(o){throw a.error("pi-adapter",`PI spawn threw: ${o}`),this.alive=!1,o}this.process.on("error",o=>{a.error("pi-adapter",`Spawn error: ${o.message}`),this.alive=!1,this.transport.close(),this.activeEventId&&(this.callbacks.sendEventResult(this.activeEventId,"failed",`Spawn error: ${o.message}`),this.clearActive()),this.stopped||this.emit("exit",1)}),this.process.on("exit",o=>{a.info("pi-adapter",`PI process exited (code=${o})`),this.alive=!1,this.transport.close(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"failed",`PI process exited (code=${o})`),this.isStreaming=!1,this.clearActive(),this.stopped||this.emit("exit",o??1)}),this.process.stderr?.on("data",o=>{const v=o.toString().trim();v&&a.info("pi-adapter",`[pi stderr] ${v}`)}),this.transport.on("event",o=>this.handlePiEvent(o)),this.transport.bind(this.process.stdin,this.process.stdout),this.alive=!0}handlePiEvent(t){if(this.stopped)return;if(this.lastRpcAt=Date.now(),t.type==="tool_execution_start"){const s=t;s.toolCallId&&this.toolCallsInFlight.add(s.toolCallId)}else if(t.type==="tool_execution_end"){const s=t;s.toolCallId&&this.toolCallsInFlight.delete(s.toolCallId)}switch(this.resetIdleTimer(),t.type){case"message_update":{const s=t.assistantMessageEvent;if(!s)break;const i=s.type;if(i==="text_delta"){const n=s.delta;if(n){const r=typeof s.contentIndex=="number"?s.contentIndex:0;this.rememberEmittedText(r,n),this.appendText(n)}}else if(i==="text_end"){const n=s.content,r=typeof s.contentIndex=="number"?s.contentIndex:0;n&&this.appendMissingText(r,n)}else if(i==="thinking_delta"){const n=s.delta;n&&this.activeEventId&&this.activeSessionId&&(this.thinkingSeq++,this.callbacks.sendThinking(this.activeEventId,this.activeSessionId,n))}else if(i==="done"||i==="error"){if(this.flushTextBuffer(),i==="error"){const n=this.piMessageError(s.error,s.reason);a.warn("pi-adapter",`terminal message_update error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${p(s.error)} reason=${p(s.reason)}`),this.recordRunError(n)}this.scheduleDoneGuard()}break}case"message_end":{const s=t.message;s?.role==="assistant"&&s.stopReason==="error"&&(a.warn("pi-adapter",`terminal message_end error event=${this.activeEventId} sessionId=${this.activeSessionId} errorMessage=${p(s.errorMessage)} message=${p(s)}`),this.recordRunError(this.piMessageError(s,s.errorMessage)),this.scheduleDoneGuard());break}case"tool_execution_start":{if(this.cancelDoneGuard(),this.flushTextBuffer(),!this.activeEventId||!this.activeSessionId)break;const s=t;if(s.toolName){const i=O(s.args);this.callbacks.sendToolUse(this.activeEventId,this.activeSessionId,s.toolName,i)}break}case"tool_execution_end":{if(!this.activeEventId||!this.activeSessionId)break;const s=t;s.isError&&this.callbacks.sendToolResult(this.activeEventId,this.activeSessionId,s.toolName,"(failed)");break}case"agent_end":{a.info("pi-adapter",`agent_end event=${this.activeEventId} sessionId=${this.activeSessionId}`),this.cancelDoneGuard(),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent();break}case"agent_start":{this.cancelDoneGuard(),this.activeEventId&&(a.info("pi-adapter",`agent_start event=${this.activeEventId}`),this.isStreaming=!0);break}default:break}}appendText(t){if(this.textBuffer+=t,this.textBuffer.length>=st){this.flushTextBuffer();return}this.scheduleTextFlush()}scheduleTextFlush(){this.textFlushTimer||(this.textFlushTimer=setTimeout(()=>{this.textFlushTimer=null,this.flushTextBuffer()},et))}flushTextBuffer(){this.stopTextFlush(),!(!this.textBuffer||!this.activeEventId||!this.activeSessionId)&&(this.sendTextChunks(this.textBuffer),this.textBuffer="")}sendTextChunks(t){if(!(!this.activeEventId||!this.activeSessionId))for(const e of H(t))this.callbacks.sendStreamChunk(this.activeEventId,this.activeSessionId,e,this.nextStreamSeq(),!1,this.activeClientMsgId??void 0)}stopTextFlush(){this.textFlushTimer&&(clearTimeout(this.textFlushTimer),this.textFlushTimer=null)}buildPromptText(t){let e=t.content||"";if(t.context_messages_json)try{const s=JSON.parse(t.context_messages_json);Array.isArray(s)&&s.length>0&&(e=s.map(n=>`[context] ${n.sender_id??"unknown"}: ${n.content}`).join(`
4
+ `):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${t}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}async startInternalApi(){try{this.internalApi=new G,this.internalApi.setInvokeHandler(async(r,c,l,h)=>this.callbacks.agentInvoke(r,c,h)),await this.internalApi.start(0),a.info("pi-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=X(),s=u(F(),"grix-pi-mcp");T(s,{recursive:!0}),this.mcpConfigPath=u(s,`mcp-${process.pid}-${Date.now()}.json`),$(this.mcpConfigPath,JSON.stringify({mcpServers:{...e,[t.name]:{command:t.command,args:t.args,directTools:!0,lifecycle:"eager"}}}),"utf8"),a.info("pi-adapter",`MCP config written to ${this.mcpConfigPath}`);const i=u(f(),".pi","agent","skills"),n=j(i);n.length>0&&a.info("pi-adapter",`Synced connector skills to ${i}: [${n.join(", ")}]`)}catch(t){a.warn("pi-adapter",`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnPi(){const t=this.config.command||"pi",e=B(t,typeof process.env.PATH=="string"?process.env.PATH:void 0),s=this.config.args??[],n=s.some(o=>o.startsWith("--mode"))?[...s]:["--mode","rpc",...s],r=String(this.config.env?.PI_CODING_AGENT_DIR??"").trim()||u(f(),".pi","agent"),c=Y(this.agentName);this.provider?await V(r,c,this.provider)?(n.includes("--provider")||n.push("--provider",c),!n.includes("--model")&&this.provider.model?.trim()&&n.push("--model",this.provider.model.trim())):a.warn("pi-adapter","provider config incomplete (baseUrl/model required); Grix provider not applied"):await J(r,c).then(o=>{o&&a.info("pi-adapter",`Removed stale Grix provider "${c}" from models.json`)}).catch(o=>{a.warn("pi-adapter",`Failed to clean up Grix provider config: ${o instanceof Error?o.message:String(o)}`)}),this.mcpConfigPath&&!n.some(o=>o==="--mcp-config")&&(await K(e)?n.push("--mcp-config",this.mcpConfigPath):a.warn("pi-adapter","\u5F53\u524D Pi \u4E0D\u652F\u6301 --mcp-config\uFF08\u672A\u68C0\u6D4B\u5230 pi-mcp-adapter \u6269\u5C55\uFF09\uFF0C\u8DF3\u8FC7 MCP \u6CE8\u5165\u4EE5\u907F\u514D\u542F\u52A8\u5931\u8D25"));const l={...process.env,...this.config.env},h=this.resolveCwd();a.info("pi-adapter",`Spawning: ${e} ${n.join(" ")}`);try{if(!(await b(h)).isDirectory())throw new Error(`Bound path is not a directory: ${h}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${h}. Please rebind with /grix open <valid-directory>.`):o}try{this.process=D(e,n,{env:l,cwd:h}).process}catch(o){throw a.error("pi-adapter",`PI spawn threw: ${o}`),this.alive=!1,o}this.process.on("error",o=>{a.error("pi-adapter",`Spawn error: ${o.message}`),this.alive=!1,this.transport.close(),this.activeEventId&&(this.callbacks.sendEventResult(this.activeEventId,"failed",`Spawn error: ${o.message}`),this.clearActive()),this.stopped||this.emit("exit",1)}),this.process.on("exit",o=>{a.info("pi-adapter",`PI process exited (code=${o})`),this.alive=!1,this.transport.close(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"failed",`PI process exited (code=${o})`),this.isStreaming=!1,this.clearActive(),this.stopped||this.emit("exit",o??1)}),this.process.stderr?.on("data",o=>{const v=o.toString().trim();v&&a.info("pi-adapter",`[pi stderr] ${v}`)}),this.transport.on("event",o=>this.handlePiEvent(o)),this.transport.bind(this.process.stdin,this.process.stdout),this.alive=!0}handlePiEvent(t){if(this.stopped)return;if(this.lastRpcAt=Date.now(),t.type==="tool_execution_start"){const s=t;s.toolCallId&&this.toolCallsInFlight.add(s.toolCallId)}else if(t.type==="tool_execution_end"){const s=t;s.toolCallId&&this.toolCallsInFlight.delete(s.toolCallId)}switch(this.resetIdleTimer(),t.type){case"message_update":{const s=t.assistantMessageEvent;if(!s)break;const i=s.type;if(i==="text_delta"){const n=s.delta;if(n){const r=typeof s.contentIndex=="number"?s.contentIndex:0;this.rememberEmittedText(r,n),this.appendText(n)}}else if(i==="text_end"){const n=s.content,r=typeof s.contentIndex=="number"?s.contentIndex:0;n&&this.appendMissingText(r,n)}else if(i==="thinking_delta"){const n=s.delta;n&&this.activeEventId&&this.activeSessionId&&(this.thinkingSeq++,this.callbacks.sendThinking(this.activeEventId,this.activeSessionId,n))}else if(i==="done"||i==="error"){if(this.flushTextBuffer(),i==="error"){const n=this.piMessageError(s.error,s.reason);a.warn("pi-adapter",`terminal message_update error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${p(s.error)} reason=${p(s.reason)}`),this.recordRunError(n)}this.scheduleDoneGuard()}break}case"message_end":{const s=t.message;s?.role==="assistant"&&s.stopReason==="error"&&(a.warn("pi-adapter",`terminal message_end error event=${this.activeEventId} sessionId=${this.activeSessionId} errorMessage=${p(s.errorMessage)} message=${p(s)}`),this.recordRunError(this.piMessageError(s,s.errorMessage)),this.scheduleDoneGuard());break}case"tool_execution_start":{if(this.cancelDoneGuard(),this.flushTextBuffer(),!this.activeEventId||!this.activeSessionId)break;const s=t;if(s.toolName){const i=O(s.args);this.callbacks.sendToolUse(this.activeEventId,this.activeSessionId,s.toolName,i)}break}case"tool_execution_end":{if(!this.activeEventId||!this.activeSessionId)break;const s=t;s.isError&&this.callbacks.sendToolResult(this.activeEventId,this.activeSessionId,s.toolName,"(failed)");break}case"agent_end":{if(a.info("pi-adapter",`agent_end event=${this.activeEventId} sessionId=${this.activeSessionId}`),this.cancelDoneGuard(),t.willRetry===!0&&this.activeEventId){a.info("pi-adapter",`agent_end willRetry=true \u2014 holding event=${this.activeEventId} for auto-retry`),this.activeRunError=null,this.emittedTextByIndex.clear(),this.resetIdleTimer();break}this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent();break}case"agent_start":{this.cancelDoneGuard(),this.activeEventId&&(a.info("pi-adapter",`agent_start event=${this.activeEventId}`),this.isStreaming=!0);break}default:break}}appendText(t){if(this.textBuffer+=t,this.textBuffer.length>=st){this.flushTextBuffer();return}this.scheduleTextFlush()}scheduleTextFlush(){this.textFlushTimer||(this.textFlushTimer=setTimeout(()=>{this.textFlushTimer=null,this.flushTextBuffer()},et))}flushTextBuffer(){this.stopTextFlush(),!(!this.textBuffer||!this.activeEventId||!this.activeSessionId)&&(this.sendTextChunks(this.textBuffer),this.textBuffer="")}sendTextChunks(t){if(!(!this.activeEventId||!this.activeSessionId))for(const e of H(t))this.callbacks.sendStreamChunk(this.activeEventId,this.activeSessionId,e,this.nextStreamSeq(),!1,this.activeClientMsgId??void 0)}stopTextFlush(){this.textFlushTimer&&(clearTimeout(this.textFlushTimer),this.textFlushTimer=null)}buildPromptText(t){let e=t.content||"";if(t.context_messages_json)try{const s=JSON.parse(t.context_messages_json);Array.isArray(s)&&s.length>0&&(e=s.map(n=>`[context] ${n.sender_id??"unknown"}: ${n.content}`).join(`
5
5
  `)+`
6
6
 
7
7
  `+e)}catch{}return this.identity.injectOnce(t.session_id,e)}buildPromptTextFromRequest(t){let e=t.text;return t.contextMessages&&t.contextMessages.length>0&&(e=t.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
8
8
  `)+`
9
9
 
10
- `+e),this.identity.injectOnce(t.adapterSessionId,e)}clearActive(){const t=this.activeEventId;t&&this.snapshotAuditBoundary(t),this.activeEventId=null,this.activeSessionId=null,this.activeClientMsgId=null,this.activeRunError=null,this.textBuffer="",this.emittedTextByIndex.clear(),this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}beginAuditCapture(t){if(this.activeAuditCapture?.eventId===t.event_id||(this.activeAuditCapture=null,t.audit?.enabled!==!0))return;const e=this.piSessionPath??void 0;let s;if(e)try{s=m(e).size}catch{s=0}this.activeAuditCapture={eventId:t.event_id,...e?{transcriptPath:e}:{},...s===void 0?{}:{transcriptStartOffset:s}}}snapshotAuditBoundary(t){const e=this.activeAuditCapture;if(!e||e.eventId!==t)return;this.activeAuditCapture=null;const s={adapterType:"pi"};if(e.transcriptPath){let i=e.transcriptStartOffset??0;try{i=m(e.transcriptPath).size}catch{}s.transcriptPath=e.transcriptPath,s.transcriptStartOffset=e.transcriptStartOffset??0,s.transcriptEndOffset=i,s.providerSessionId=this.piSessionPath??e.transcriptPath}this.completedAuditBoundaries.set(t,s)}resetRunStreamState(){this.streamSeq=0,this.thinkingSeq=0,this.textBuffer="",this.activeRunError=null,this.emittedTextByIndex.clear(),this.activeClientMsgId=`pi_${++this.clientMsgSeq}_${Date.now()}`}nextStreamSeq(){return this.streamSeq++,this.streamSeq}completeActiveEvent(){if(!this.activeEventId){this.clearActive();return}const t=this.activeEventId,e=this.activeSessionId??"",s=this.activeClientMsgId??void 0,i=this.activeRunError??void 0;this.clearActive(),this.finalizeEvent(t,e,s,i)}finalizeEvent(t,e,s,i){const n=this.nextStreamSeq(),r=i?"failed":"responded";a.info("pi-adapter",`finalize event=${t} sessionId=${e} status=${r} clientMsgId=${s??""} terminalError=${i?p(i):""}`),this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(t,e,s).then(()=>{this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} (reliable) event=${t}`)}).catch(c=>{a.error("pi-adapter",`finalStreamChunk ACK failed event=${t}: ${c}`),this.callbacks.sendEventResult(t,"failed",`final output delivery failed: ${c instanceof Error?c.message:String(c)}`),a.info("pi-adapter",`event failed (final delivery) event=${t}`)}):(this.callbacks.sendStreamChunk(t,e,"",n,!0,s),this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} event=${t}`))}recordRunError(t){if(this.activeRunError||!this.activeEventId||!this.activeSessionId)return;const e=t.trim()||"Pi request failed";this.activeRunError=e,a.warn("pi-adapter",`recorded terminal error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${p(e)}`),this.flushTextBuffer(),this.callbacks.sendRunError(this.activeEventId,this.activeSessionId,e,this.nextStreamSeq(),this.activeClientMsgId??void 0)}piMessageError(t,e){if(t&&typeof t=="object"){const s=t.errorMessage;if(typeof s=="string"&&s.trim())return s.trim()}return typeof t=="string"&&t.trim()?t.trim():typeof e=="string"&&e.trim()&&e.trim()!=="error"?e.trim():"Pi request failed"}rememberEmittedText(t,e){this.emittedTextByIndex.set(t,(this.emittedTextByIndex.get(t)??"")+e)}appendMissingText(t,e){const s=this.emittedTextByIndex.get(t)??"";if(e!==s){if(e.startsWith(s)){const i=e.slice(s.length);i&&(this.rememberEmittedText(t,i),this.appendText(i));return}if(!s){this.rememberEmittedText(t,e),this.appendText(e);return}a.info("pi-adapter",`text_end content mismatch at index=${t}, keeping streamed deltas`)}}startComposing(t,e){this.stopComposing(),this.composingSessionId=t,this.callbacks.sendSessionActivitySet(t,"composing",!0,{ref_event_id:e,ttl_ms:12e4}),this.composingInterval=setInterval(()=>{this.composingSessionId&&this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!0,{ttl_ms:12e4})},3e4)}stopComposing(){this.composingInterval&&(clearInterval(this.composingInterval),this.composingInterval=null),this.composingSessionId&&(this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!1),this.composingSessionId=null)}static DONE_GUARD_MS=5e3;scheduleDoneGuard(){this.cancelDoneGuard(),!(!this.activeEventId||!this.activeSessionId)&&(this.doneGuardTimer=setTimeout(()=>{this.doneGuardTimer=null,!(!this.activeEventId||!this.isStreaming)&&(a.info("pi-adapter",`done guard triggered \u2014 no agent_end received, ending event=${this.activeEventId}`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent())},_.DONE_GUARD_MS))}cancelDoneGuard(){this.doneGuardTimer&&(clearTimeout(this.doneGuardTimer),this.doneGuardTimer=null)}shouldExtendByLiveness(){if(!this.isStreaming||!this.activeEventId)return!1;const t=this.process?.pid;if(!this.alive||!t||!y(t))return!1;const e=Date.now(),s=this.lastRpcAt>0?e-this.lastRpcAt:Number.POSITIVE_INFINITY;if(s<tt)return this.livenessExtendStartAt=e,a.info("pi-adapter",`Liveness check: fresh RPC for ${this.activeEventId} (rpcAge=${s}ms, toolsInFlight=${this.toolCallsInFlight.size}), extending`),!0;const n=this.livenessExtendStartAt??e;return this.livenessExtendStartAt===void 0&&(this.livenessExtendStartAt=n),e-n>E?(a.warn("pi-adapter",`Liveness extension budget exhausted for ${this.activeEventId} (no progress for ${Math.round((e-n)/6e4)}min), allowing idle fail`),!1):(a.info("pi-adapter",`Liveness check: turn in progress for ${this.activeEventId} (toolsInFlight=${this.toolCallsInFlight.size}, rpcAge=${Number.isFinite(s)?`${s}ms`:"n/a"}), extending`),!0)}resetIdleTimer(){this.stopIdleTimer(),!(this.stopped||!this.isStreaming)&&(this.idleTimer=setTimeout(()=>{if(this.stopped||!this.isStreaming)return;if(this.shouldExtendByLiveness()){this.resetIdleTimer();return}const t=this.process?.pid,s=!!(this.alive&&t&&y(t))?`liveness budget exhausted after ${E/6e4}min`:"process not alive";a.error("pi-adapter",`Idle timeout (${s}, toolsInFlight=${this.toolCallsInFlight.size}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"failed",`idle timeout: ${s}`),this.clearActive(),this.emit("exit",-1)},Z))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}async ensureSessionReady(){return this.sessionReadyPromise?this.sessionReadyPromise:(this.sessionReadyPromise=this.restoreOrCreateSession().finally(()=>{this.sessionReadyPromise=null}),this.sessionReadyPromise)}async restoreOrCreateSession(){try{await this.switchSession();return}catch(t){this.piSessionPath&&a.error("pi-adapter",`switch_session failed, creating new session: ${t}`)}await this.createNewSession()}async switchSession(){if(!this.piSessionPath)throw new Error("no PI session path");const t=await this.transport.send("switch_session",{sessionPath:this.piSessionPath});if(!t.success)throw this.piSessionPath=null,this.persistPiSessionPath(void 0),new Error(`switch_session failed: ${t.error}`)}async createNewSession(){const t=await this.transport.send("new_session");if(!t.success)throw new Error(`new_session failed: ${t.error}`);let e=null;if(e=t.data?.sessionFile??null,!e)try{const i=await this.transport.send("get_state");i.success&&(e=i.data?.sessionFile??null)}catch{}this.piSessionPath=e,this.persistPiSessionPath(this.piSessionPath??void 0)}persistPiSessionPath(t){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setPiSessionPath(this.aibotSessionId,t)}}export{_ as PiAdapter};
10
+ `+e),this.identity.injectOnce(t.adapterSessionId,e)}clearActive(){const t=this.activeEventId;t&&this.snapshotAuditBoundary(t),this.activeEventId=null,this.activeSessionId=null,this.activeClientMsgId=null,this.activeRunError=null,this.textBuffer="",this.emittedTextByIndex.clear(),this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}beginAuditCapture(t){if(this.activeAuditCapture?.eventId===t.event_id||(this.activeAuditCapture=null,t.audit?.enabled!==!0))return;const e=this.piSessionPath??void 0;let s;if(e)try{s=m(e).size}catch{s=0}this.activeAuditCapture={eventId:t.event_id,...e?{transcriptPath:e}:{},...s===void 0?{}:{transcriptStartOffset:s}}}snapshotAuditBoundary(t){const e=this.activeAuditCapture;if(!e||e.eventId!==t)return;this.activeAuditCapture=null;const s={adapterType:"pi"};if(e.transcriptPath){let i=e.transcriptStartOffset??0;try{i=m(e.transcriptPath).size}catch{}s.transcriptPath=e.transcriptPath,s.transcriptStartOffset=e.transcriptStartOffset??0,s.transcriptEndOffset=i,s.providerSessionId=this.piSessionPath??e.transcriptPath}this.completedAuditBoundaries.set(t,s)}resetRunStreamState(){this.streamSeq=0,this.thinkingSeq=0,this.textBuffer="",this.activeRunError=null,this.runErrorEmitted=!1,this.emittedTextByIndex.clear(),this.activeClientMsgId=`pi_${++this.clientMsgSeq}_${Date.now()}`}nextStreamSeq(){return this.streamSeq++,this.streamSeq}completeActiveEvent(){if(!this.activeEventId){this.clearActive();return}const t=this.activeEventId,e=this.activeSessionId??"",s=this.activeClientMsgId??void 0,i=this.activeRunError??void 0;i&&this.emitPendingRunError(),this.clearActive(),this.finalizeEvent(t,e,s,i)}finalizeEvent(t,e,s,i){const n=this.nextStreamSeq(),r=i?"failed":"responded";a.info("pi-adapter",`finalize event=${t} sessionId=${e} status=${r} clientMsgId=${s??""} terminalError=${i?p(i):""}`),this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(t,e,s).then(()=>{this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} (reliable) event=${t}`)}).catch(c=>{a.error("pi-adapter",`finalStreamChunk ACK failed event=${t}: ${c}`),this.callbacks.sendEventResult(t,"failed",`final output delivery failed: ${c instanceof Error?c.message:String(c)}`),a.info("pi-adapter",`event failed (final delivery) event=${t}`)}):(this.callbacks.sendStreamChunk(t,e,"",n,!0,s),this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} event=${t}`))}recordRunError(t){if(this.activeRunError||!this.activeEventId||!this.activeSessionId)return;const e=t.trim()||"Pi request failed";this.activeRunError=e,a.warn("pi-adapter",`recorded terminal error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${p(e)}`),this.flushTextBuffer()}emitPendingRunError(){this.runErrorEmitted||!this.activeRunError||!this.activeEventId||!this.activeSessionId||(this.runErrorEmitted=!0,this.callbacks.sendRunError(this.activeEventId,this.activeSessionId,this.activeRunError,this.nextStreamSeq(),this.activeClientMsgId??void 0))}piMessageError(t,e){if(t&&typeof t=="object"){const s=t.errorMessage;if(typeof s=="string"&&s.trim())return s.trim()}return typeof t=="string"&&t.trim()?t.trim():typeof e=="string"&&e.trim()&&e.trim()!=="error"?e.trim():"Pi request failed"}rememberEmittedText(t,e){this.emittedTextByIndex.set(t,(this.emittedTextByIndex.get(t)??"")+e)}appendMissingText(t,e){const s=this.emittedTextByIndex.get(t)??"";if(e!==s){if(e.startsWith(s)){const i=e.slice(s.length);i&&(this.rememberEmittedText(t,i),this.appendText(i));return}if(!s){this.rememberEmittedText(t,e),this.appendText(e);return}a.info("pi-adapter",`text_end content mismatch at index=${t}, keeping streamed deltas`)}}startComposing(t,e){this.stopComposing(),this.composingSessionId=t,this.callbacks.sendSessionActivitySet(t,"composing",!0,{ref_event_id:e,ttl_ms:12e4}),this.composingInterval=setInterval(()=>{this.composingSessionId&&this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!0,{ttl_ms:12e4})},3e4)}stopComposing(){this.composingInterval&&(clearInterval(this.composingInterval),this.composingInterval=null),this.composingSessionId&&(this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!1),this.composingSessionId=null)}static DONE_GUARD_MS=5e3;scheduleDoneGuard(){this.cancelDoneGuard(),!(!this.activeEventId||!this.activeSessionId)&&(this.doneGuardTimer=setTimeout(()=>{this.doneGuardTimer=null,!(!this.activeEventId||!this.isStreaming)&&(a.info("pi-adapter",`done guard triggered \u2014 no agent_end received, ending event=${this.activeEventId}`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent())},_.DONE_GUARD_MS))}cancelDoneGuard(){this.doneGuardTimer&&(clearTimeout(this.doneGuardTimer),this.doneGuardTimer=null)}shouldExtendByLiveness(){if(!this.isStreaming||!this.activeEventId)return!1;const t=this.process?.pid;if(!this.alive||!t||!y(t))return!1;const e=Date.now(),s=this.lastRpcAt>0?e-this.lastRpcAt:Number.POSITIVE_INFINITY;if(s<tt)return this.livenessExtendStartAt=e,a.info("pi-adapter",`Liveness check: fresh RPC for ${this.activeEventId} (rpcAge=${s}ms, toolsInFlight=${this.toolCallsInFlight.size}), extending`),!0;const n=this.livenessExtendStartAt??e;return this.livenessExtendStartAt===void 0&&(this.livenessExtendStartAt=n),e-n>x?(a.warn("pi-adapter",`Liveness extension budget exhausted for ${this.activeEventId} (no progress for ${Math.round((e-n)/6e4)}min), allowing idle fail`),!1):(a.info("pi-adapter",`Liveness check: turn in progress for ${this.activeEventId} (toolsInFlight=${this.toolCallsInFlight.size}, rpcAge=${Number.isFinite(s)?`${s}ms`:"n/a"}), extending`),!0)}resetIdleTimer(){this.stopIdleTimer(),!(this.stopped||!this.isStreaming)&&(this.idleTimer=setTimeout(()=>{if(this.stopped||!this.isStreaming)return;if(this.shouldExtendByLiveness()){this.resetIdleTimer();return}const t=this.process?.pid,s=!!(this.alive&&t&&y(t))?`liveness budget exhausted after ${x/6e4}min`:"process not alive";a.error("pi-adapter",`Idle timeout (${s}, toolsInFlight=${this.toolCallsInFlight.size}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.activeEventId&&(this.emitPendingRunError(),this.callbacks.sendEventResult(this.activeEventId,"failed",`idle timeout: ${s}`)),this.clearActive(),this.emit("exit",-1)},Z))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}async ensureSessionReady(){return this.sessionReadyPromise?this.sessionReadyPromise:(this.sessionReadyPromise=this.restoreOrCreateSession().finally(()=>{this.sessionReadyPromise=null}),this.sessionReadyPromise)}async restoreOrCreateSession(){try{await this.switchSession();return}catch(t){this.piSessionPath&&a.error("pi-adapter",`switch_session failed, creating new session: ${t}`)}await this.createNewSession()}async switchSession(){if(!this.piSessionPath)throw new Error("no PI session path");const t=await this.transport.send("switch_session",{sessionPath:this.piSessionPath});if(!t.success)throw this.piSessionPath=null,this.persistPiSessionPath(void 0),new Error(`switch_session failed: ${t.error}`)}async createNewSession(){const t=await this.transport.send("new_session");if(!t.success)throw new Error(`new_session failed: ${t.error}`);let e=null;if(e=t.data?.sessionFile??null,!e)try{const i=await this.transport.send("get_state");i.success&&(e=i.data?.sessionFile??null)}catch{}this.piSessionPath=e,this.persistPiSessionPath(this.piSessionPath??void 0)}persistPiSessionPath(t){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setPiSessionPath(this.aibotSessionId,t)}}export{_ as PiAdapter};
@@ -1 +1 @@
1
- import{open as x,readdir as S,stat as P}from"node:fs/promises";import{basename as b,join as E}from"node:path";function m(t,r){return typeof t=="number"&&Number.isSafeInteger(t)&&t>=0?t:r}function h(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}async function I(t,r,s,n){const p=[],a=[],e=[];try{const o=await P(t),u=Math.min(r,o.size),c=Math.min(Math.max(s??o.size,u),o.size)-u;if(c===0)return{lines:p,anomalies:a,gaps:e};const l=await x(t,"r");let i;try{i=Buffer.allocUnsafe(c);const{bytesRead:d}=await l.read(i,0,c,u);i=i.subarray(0,d)}finally{await l.close()}let g=0,O;for(;g<i.length;){const d=i.indexOf(10,g),$=d===-1?i.length:d+1,y=i.subarray(g,d===-1?i.length:d),w=u+g;if(g=$,y.length!==0)try{const f=JSON.parse(y.toString("utf8"));if(!h(f))throw new Error("JSONL row is not an object");typeof f.promptId=="string"&&f.promptId.length>0&&(O=f.promptId),p.push({record:f,sourcePath:t,sourceOffset:w,sourceLength:y.length,isSubagent:n,promptId:O})}catch(f){a.push({code:"claude_transcript_row_malformed",message:`${t}:${w}: ${f instanceof Error?f.message:String(f)}`}),e.push({category:"transcript_row",reason:"correlation_failed",detail:`${t}:${w}`})}}}catch(o){a.push({code:"claude_transcript_source_unreadable",message:`${t}: ${o instanceof Error?o.message:String(o)}`}),e.push({category:n?"subagent_transcript":"main_transcript",reason:"source_log_missing",detail:t})}return{lines:p,anomalies:a,gaps:e}}async function R(t){const r=[];async function s(n){const p=await S(n,{withFileTypes:!0});for(const a of p){const e=E(n,a.name);a.isDirectory()?await s(e):a.isFile()&&/^agent-.*\.jsonl$/u.test(b(a.name))&&r.push(e)}}return await s(t),r.sort((n,p)=>n.localeCompare(p))}function j(t){for(const r of t){const{record:s}=r;if(s.type!=="user"||typeof s.promptId!="string")continue;const n=h(s.origin)?s.origin:void 0;if(n?.kind==="channel"&&n.server==="grix")return s.promptId}}function z(t){if(typeof t=="number")return{startOffset:m(t,0)};if(!h(t))return{startOffset:0};const r=m(t.endOffset,-1);return{startOffset:m(t.startOffset,0),endOffset:r>=0?r:void 0}}async function F(t,r){const s=typeof t.transcriptPath=="string"?t.transcriptPath:void 0;if(!s)return{lines:[],anomalies:[{code:"claude_transcript_path_missing",message:"boundary.transcriptPath is required"}],gaps:[{category:"main_transcript",reason:"source_log_missing",detail:"boundary.transcriptPath is missing"}],sourcePaths:[]};const n=await I(s,m(t.transcriptStartOffset,0),typeof t.transcriptEndOffset=="number"?m(t.transcriptEndOffset,0):void 0,!1),a=(typeof t.promptId=="string"?t.promptId:void 0)??j(n.lines),e={targetPromptId:a,lines:[...n.lines],anomalies:[...n.anomalies],gaps:[...n.gaps],sourcePaths:[s]};a||(e.anomalies.push({code:"claude_prompt_id_missing",message:"Unable to identify the audited prompt from the transcript boundary"}),e.gaps.push({category:"prompt_correlation",reason:"correlation_failed"}));const o=typeof t.subagentRoot=="string"?t.subagentRoot:void 0;if(!r||!o)return e;let u;try{u=await R(o)}catch(c){return e.anomalies.push({code:"claude_subagent_root_unreadable",message:`${o}: ${c instanceof Error?c.message:String(c)}`}),e.gaps.push({category:"subagent_transcript",reason:"source_log_missing",detail:o}),e}const _=h(t.subagentOffsets)?t.subagentOffsets:{};for(const c of u){const l=z(_[c]??_[b(c)]),i=await I(c,l.startOffset,l.endOffset,!0);e.sourcePaths.push(c),e.lines.push(...i.lines),e.anomalies.push(...i.anomalies),e.gaps.push(...i.gaps)}return e}export{F as collectClaudeTranscript};
1
+ import{open as S,readdir as $,stat as P}from"node:fs/promises";import{basename as O,join as E}from"node:path";function m(t,r){return typeof t=="number"&&Number.isSafeInteger(t)&&t>=0?t:r}function h(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}async function b(t,r,e,s){const p=[],o=[],n=[];try{const a=await P(t),u=Math.min(r,a.size),c=Math.min(Math.max(e??a.size,u),a.size)-u;if(c===0)return{lines:p,anomalies:o,gaps:n};const l=await S(t,"r");let i;try{i=Buffer.allocUnsafe(c);const{bytesRead:d}=await l.read(i,0,c,u);i=i.subarray(0,d)}finally{await l.close()}let g=0,I;for(;g<i.length;){const d=i.indexOf(10,g),x=d===-1?i.length:d+1,y=i.subarray(g,d===-1?i.length:d),w=u+g;if(g=x,y.length!==0)try{const f=JSON.parse(y.toString("utf8"));if(!h(f))throw new Error("JSONL row is not an object");typeof f.promptId=="string"&&f.promptId.length>0&&(I=f.promptId),p.push({record:f,sourcePath:t,sourceOffset:w,sourceLength:y.length,isSubagent:s,promptId:I})}catch(f){o.push({code:"claude_transcript_row_malformed",message:`${t}:${w}: ${f instanceof Error?f.message:String(f)}`}),n.push({category:"transcript_row",reason:"correlation_failed",detail:`${t}:${w}`})}}}catch(a){o.push({code:"claude_transcript_source_unreadable",message:`${t}: ${a instanceof Error?a.message:String(a)}`}),n.push({category:s?"subagent_transcript":"main_transcript",reason:"source_log_missing",detail:t})}return{lines:p,anomalies:o,gaps:n}}async function R(t){const r=[];async function e(s){const p=await $(s,{withFileTypes:!0});for(const o of p){const n=E(s,o.name);o.isDirectory()?await e(n):o.isFile()&&/^agent-.*\.jsonl$/u.test(O(o.name))&&r.push(n)}}return await e(t),r.sort((s,p)=>s.localeCompare(p))}function F(t){for(const r of t){const{record:e}=r;if(e.type!=="user"||typeof e.promptId!="string")continue;const s=h(e.origin)?e.origin:void 0;if(s?.kind==="channel"&&s.server==="grix")return e.promptId}}function j(t){for(let r=t.length-1;r>=0;r-=1){const{record:e}=t[r];if(e.type==="user"&&typeof e.promptId=="string"&&e.promptId.length>0)return e.promptId}}function T(t){if(typeof t=="number")return{startOffset:m(t,0)};if(!h(t))return{startOffset:0};const r=m(t.endOffset,-1);return{startOffset:m(t.startOffset,0),endOffset:r>=0?r:void 0}}async function B(t,r){const e=typeof t.transcriptPath=="string"?t.transcriptPath:void 0;if(!e)return{lines:[],anomalies:[{code:"claude_transcript_path_missing",message:"boundary.transcriptPath is required"}],gaps:[{category:"main_transcript",reason:"source_log_missing",detail:"boundary.transcriptPath is missing"}],sourcePaths:[]};const s=await b(e,m(t.transcriptStartOffset,0),typeof t.transcriptEndOffset=="number"?m(t.transcriptEndOffset,0):void 0,!1),o=(typeof t.promptId=="string"?t.promptId:void 0)??F(s.lines)??(t.singleTurnFallback===!0?j(s.lines):void 0),n={targetPromptId:o,lines:[...s.lines],anomalies:[...s.anomalies],gaps:[...s.gaps],sourcePaths:[e]};o||(n.anomalies.push({code:"claude_prompt_id_missing",message:"Unable to identify the audited prompt from the transcript boundary"}),n.gaps.push({category:"prompt_correlation",reason:"correlation_failed"}));const a=typeof t.subagentRoot=="string"?t.subagentRoot:void 0;if(!r||!a)return n;let u;try{u=await R(a)}catch(c){return n.anomalies.push({code:"claude_subagent_root_unreadable",message:`${a}: ${c instanceof Error?c.message:String(c)}`}),n.gaps.push({category:"subagent_transcript",reason:"source_log_missing",detail:a}),n}const _=h(t.subagentOffsets)?t.subagentOffsets:{};for(const c of u){const l=T(_[c]??_[O(c)]),i=await b(c,l.startOffset,l.endOffset,!0);n.sourcePaths.push(c),n.lines.push(...i.lines),n.anomalies.push(...i.anomalies),n.gaps.push(...i.gaps)}return n}export{B as collectClaudeTranscript};