@quantiya/codevibe-codex-plugin 2.0.14 → 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -1,31 +1,32 @@
1
- "use strict";var xt=Object.create;var te=Object.defineProperty;var Ot=Object.getOwnPropertyDescriptor;var Ct=Object.getOwnPropertyNames;var At=Object.getPrototypeOf,Ft=Object.prototype.hasOwnProperty;var kt=(a,t)=>{for(var e in t)te(a,e,{get:t[e],enumerable:!0})},Le=(a,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Ct(t))!Ft.call(a,s)&&s!==e&&te(a,s,{get:()=>t[s],enumerable:!(i=Ot(t,s))||i.enumerable});return a};var E=(a,t,e)=>(e=a!=null?xt(At(a)):{},Le(t||!a||!a.__esModule?te(e,"default",{value:a,enumerable:!0}):e,a)),Dt=a=>Le(te({},"__esModule",{value:!0}),a);var Fi={};kt(Fi,{CodexCompanionServer:()=>ve,extractShellCommandFromPromptText:()=>wt,extractShellCommandPartsFromPromptText:()=>Me,restoreCodexPersistOptionCommand:()=>Rt,shellCommandPartsMatchExpected:()=>Et});module.exports=Dt(Fi);var ee=require("uuid"),ye=E(require("crypto")),V=E(require("fs")),$=E(require("path")),It=E(require("os")),De=require("util"),Se=require("child_process"),Ne=require("shell-quote"),h=require("@quantiya/codevibe-core");var Ke=E(require("os")),Be=E(require("path")),qe=require("@quantiya/codevibe-core"),o=(0,qe.createLogger)({name:"codevibe-codex",logFile:Be.default.join(Ke.default.tmpdir(),"codevibe-codex-mcp.log"),level:"debug"});var Ue=require("events"),w=E(require("fs")),L=E(require("path")),$e=require("string_decoder"),He=require("chokidar"),ze=require("@quantiya/codevibe-core");var be=256*1024,Nt=64*1024*1024,Mt=16*1024*1024,Lt=64*1024,ie=class extends Ue.EventEmitter{constructor(e={}){super();this.watcher=null;this.filePositions=new Map;this.activeLogFile=null;this.sessionId=null;this.isWatching=!1;this.startTime=0;this.sessionsDir=null;this.activeReads=new Map;this.pendingReads=new Set;this.readGeneration=0;this.oversizedRecordFiles=new Set;this.fileIdentities=new Map;this.bindSource=null;this.authoritativeSessionId=null;this.pendingTranscriptBind=null;this.transcriptBindGeneration=0;this.transcriptBindingsClosed=!1;this.activeTranscriptAdmissions=new Set;this.startBaselines=new Map;if(this.maxBacklogBytes=e.maxBacklogBytes??Nt,this.maxRecordBytes=e.maxRecordBytes??Mt,!Number.isSafeInteger(this.maxBacklogBytes)||this.maxBacklogBytes<=0)throw new RangeError("maxBacklogBytes must be a positive safe integer");if(!Number.isSafeInteger(this.maxRecordBytes)||this.maxRecordBytes<=0)throw new RangeError("maxRecordBytes must be a positive safe integer")}start(){if(this.isWatching){o.warn("Session log watcher already running");return}this.transcriptBindGeneration++,this.transcriptBindingsClosed=!1;let e=(0,ze.getConfig)().codex.sessionsDir;this.sessionsDir=e,o.info("Starting Codex session log watcher",{sessionsDir:e}),w.existsSync(e)||(o.info("Codex sessions directory does not exist yet, creating...",{sessionsDir:e}),w.mkdirSync(e,{recursive:!0})),this.startTime=Date.now(),this.readGeneration++,this.bindSource=null,this.authoritativeSessionId=null,this.snapshotStartBaselines(e),this.drainPendingTranscriptBind(),this.watcher=(0,He.watch)(e,{persistent:!0,ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50},depth:4,ignored:i=>{let s=L.basename(i);return w.existsSync(i)&&w.statSync(i).isDirectory()?!1:!s.startsWith("rollout-")||!s.endsWith(".jsonl")}}),this.watcher.on("add",i=>{i.endsWith(".jsonl")&&this.onFileAdded(i)}),this.watcher.on("change",i=>{i.endsWith(".jsonl")&&this.onFileChanged(i)}),this.watcher.on("error",i=>{o.error("Watcher error:",i),this.emit("error",i)}),this.watcher.on("ready",()=>{o.info("Session log watcher ready"),this.activeLogFile||o.debug("No authoritative transcript hook received yet; rollout watcher remains fail-closed")}),this.isWatching=!0,o.info("Session log watcher started")}stop(){this.transcriptBindingsClosed=!0,this.transcriptBindGeneration++,this.watcher&&(this.watcher.close(),this.watcher=null),this.isWatching=!1,this.readGeneration++,this.filePositions.clear(),this.activeReads.clear(),this.pendingReads.clear(),this.oversizedRecordFiles.clear(),this.activeLogFile=null,this.sessionId=null,this.sessionsDir=null,this.bindSource=null,this.authoritativeSessionId=null,this.pendingTranscriptBind&&(this.pendingTranscriptBind.resolve({status:"rejected",reason:"watcher_stopped"}),this.pendingTranscriptBind=null),this.startBaselines.clear(),this.fileIdentities.clear(),o.info("Session log watcher stopped")}async waitForTranscriptAdmissions(){for(;this.activeTranscriptAdmissions.size>0;)await Promise.all(Array.from(this.activeTranscriptAdmissions,e=>e.then(()=>{},()=>{})))}getSessionId(){return this.sessionId}getActiveLogFile(){return this.activeLogFile}getBindSource(){return this.bindSource}getAuthoritativeSessionId(){return this.authoritativeSessionId}tryRealpath(e){try{return w.realpathSync(e)}catch{return null}}isInteractiveRollout(e){return!(!e||e.originator==="codex_exec"||e.threadSource==="subagent"||e.sourceSubagent)}identityOf(e){return{dev:e.dev,ino:e.ino}}identitiesEqual(e,i){return e.dev===i.dev&&e.ino===i.ino}computeSeedOffset(e){let i;try{i=w.statSync(e)}catch{return 0}if(this.getSessionAgeMs(e,i)>=this.startTime)return 0;let r=this.startBaselines.get(e);return r&&this.identitiesEqual(r.identity,this.identityOf(i))?r.size:i.size}applyBind(e,i){this.readGeneration++,this.activeReads.delete(e),this.pendingReads.delete(e),this.activeLogFile=e,this.bindSource=i,i==="heuristic"&&(this.authoritativeSessionId=null),this.filePositions.has(e)||this.filePositions.set(e,this.computeSeedOffset(e));try{this.fileIdentities.set(e,this.identityOf(w.statSync(e)))}catch{this.fileIdentities.delete(e)}this.scheduleReadNewLines(e)}bindToFile(e){let i=this.tryRealpath(e);if(!i){o.warn("bindToFile: could not canonicalize path; skipping",{filePath:e});return}if(i===this.activeLogFile){this.bindSource="authoritative";return}o.info("bindToFile: authoritative bind to session rollout",{filePath:i}),this.applyBind(i,"authoritative")}async bindToSessionTranscript(e,i){if(typeof e!="string"||e.length===0)return{status:"rejected",reason:"missing_transcript_path"};if(this.transcriptBindingsClosed)return{status:"rejected",reason:"watcher_stopped"};if(!this.sessionsDir){let s=this.pendingTranscriptBind;if(s)return s.transcriptPath===e&&s.expectedSessionId===i?{status:"pending",completion:s.completion}:(o.warn("bindToSessionTranscript: conflicting startup bind rejected",{transcriptPath:e,expectedSessionId:i,pendingExpectedSessionId:s.expectedSessionId}),{status:"rejected",reason:"conflicting_startup_bind"});let r,n=new Promise(l=>{r=l});return this.pendingTranscriptBind={transcriptPath:e,expectedSessionId:i,completion:n,resolve:r},o.debug("bindToSessionTranscript: watcher not started yet; queued pending authoritative bind",{transcriptPath:e,expectedSessionId:i}),{status:"pending",completion:n}}return this.trackTranscriptAdmission(this.validateAndBindSessionTranscript(e,i,this.transcriptBindGeneration))}trackTranscriptAdmission(e){let i;return i=e.finally(()=>{this.activeTranscriptAdmissions.delete(i)}),this.activeTranscriptAdmissions.add(i),i}isTranscriptAdmissionCurrent(e,i){return!this.transcriptBindingsClosed&&this.transcriptBindGeneration===e&&this.sessionsDir===i}async validateAndBindSessionTranscript(e,i,s){let r=this.sessionsDir;if(!r||!this.isTranscriptAdmissionCurrent(s,r))return{status:"rejected",reason:"watcher_not_started"};let n=this.tryRealpath(e),l=this.tryRealpath(r);if(!n||!l)return{status:"rejected",reason:"canonicalization_failed"};let p=L.relative(l,n);if(p===".."||p.startsWith(".."+L.sep)||L.isAbsolute(p))return o.warn("bindToSessionTranscript: path not under sessionsDir; rejecting",{transcriptPath:n}),{status:"rejected",reason:"outside_sessions_directory"};let c=L.basename(n);if(!c.startsWith("rollout-")||!c.endsWith(".jsonl"))return{status:"rejected",reason:"invalid_rollout_filename"};let d=null;for(let u=0;u<3;u++){if(!this.isTranscriptAdmissionCurrent(s,r))return{status:"rejected",reason:"watcher_stopped"};if(d=this.readSessionMeta(n),d)break;if(await new Promise(m=>setTimeout(m,20)),!this.isTranscriptAdmissionCurrent(s,r))return{status:"rejected",reason:"watcher_stopped"}}return!d||d.id!==i?(o.debug("bindToSessionTranscript: meta missing or id mismatch; not binding",{transcriptPath:n,metaId:d?.id,expectedSessionId:i}),{status:"rejected",reason:"session_metadata_mismatch"}):this.isInteractiveRollout(d)?this.isTranscriptAdmissionCurrent(s,r)?(this.bindToFile(n),this.authoritativeSessionId=i,{status:"bound"}):{status:"rejected",reason:"watcher_stopped"}:(o.debug("bindToSessionTranscript: non-interactive rollout (codex_exec/subagent); not binding",{transcriptPath:n,originator:d.originator,threadSource:d.threadSource}),{status:"rejected",reason:"non_interactive_rollout"})}async drainPendingTranscriptBind(){let e=this.pendingTranscriptBind;if(!e)return;this.pendingTranscriptBind=null;let i;try{this.transcriptBindingsClosed?i={status:"rejected",reason:"watcher_stopped"}:i=await this.trackTranscriptAdmission(this.validateAndBindSessionTranscript(e.transcriptPath,e.expectedSessionId,this.transcriptBindGeneration))}catch(s){o.warn("bindToSessionTranscript: pending startup bind failed",{expectedSessionId:e.expectedSessionId,error:s instanceof Error?s.message:String(s)}),i={status:"rejected",reason:"startup_bind_failed"}}e.resolve(i)}onFileAdded(e){let i=this.tryRealpath(e);i&&(i===this.activeLogFile?this.scheduleReadNewLines(i):o.debug("Ignoring unowned rollout add; awaiting authoritative hook binding",{filePath:i}))}onFileChanged(e){let i=this.tryRealpath(e);i&&(this.activeLogFile&&i!==this.activeLogFile||this.activeLogFile&&this.scheduleReadNewLines(i))}readSessionMeta(e){let i=this.readFirstLogEntry(e);if(!i)return null;let s=i.payload||{};return{id:typeof s.id=="string"?s.id:void 0,timestamp:typeof i.timestamp=="string"?i.timestamp:void 0,originator:typeof s.originator=="string"?s.originator:void 0,cwd:typeof s.cwd=="string"?s.cwd:void 0,threadSource:typeof s.thread_source=="string"?s.thread_source:void 0,sourceSubagent:!!(s.source&&typeof s.source=="object"&&s.source.subagent)}}readFirstLogEntry(e){let i=null;try{i=w.openSync(e,"r");let s=new $e.StringDecoder("utf8"),r=Buffer.alloc(64*1024),n="",l=0,p=-1;for(;l<be;){let d=w.readSync(i,r,0,r.length,l);if(d<=0||(l+=d,n+=s.write(r.subarray(0,d)),p=n.indexOf(`
2
- `),p!==-1))break}n+=s.end();let c=p===-1?n:n.slice(0,p);return c.trim()?JSON.parse(c):null}catch{return null}finally{if(i!==null)try{w.closeSync(i)}catch{}}}snapshotStartBaselines(e){this.startBaselines.clear();try{for(let{filePath:i,size:s}of this.collectRecentSessionFiles(e,!0)){let r=this.tryRealpath(i);if(r)try{let n=w.statSync(r);this.startBaselines.set(r,{size:s,identity:this.identityOf(n)})}catch{}}}catch{}}getSessionAgeMs(e,i){let s=this.readSessionMeta(e);if(s?.timestamp){let r=Date.parse(s.timestamp);if(!Number.isNaN(r))return r}return i.birthtimeMs||i.ctimeMs}collectRecentSessionFiles(e,i=!1){let s=[],r=[e];for(;r.length>0;){let n=r.pop();if(!n)continue;let l;try{l=w.readdirSync(n,{withFileTypes:!0})}catch{continue}for(let p of l){let c=L.join(n,p.name);if(p.isDirectory()){r.push(c);continue}if(!(!p.isFile()||!p.name.startsWith("rollout-")||!p.name.endsWith(".jsonl")))try{let d=w.statSync(c),u=d.birthtimeMs||d.ctimeMs,m=d.mtimeMs;(i||u>=this.startTime||m>=this.startTime-5e3)&&s.push({filePath:c,createdAt:u,modifiedAt:m,size:d.size})}catch{}}}return s}scheduleReadNewLines(e){let i=this.activeReads.get(e);if(i)return this.pendingReads.add(e),i;let s=this.readGeneration,r=Promise.resolve().then(()=>this.drainReadQueue(e,s));return this.activeReads.set(e,r),r.finally(()=>{this.activeReads.get(e)===r&&(this.activeReads.delete(e),this.pendingReads.delete(e))}),r}async drainReadQueue(e,i){do this.pendingReads.delete(e),await this.readNewLines(e,i);while(i===this.readGeneration&&this.pendingReads.has(e))}async readNewLines(e,i){if(i!==this.readGeneration)return;let s=this.filePositions.get(e)||0,r=null;try{r=w.openSync(e,w.constants.O_RDONLY|(w.constants.O_NOFOLLOW??0));let n=w.fstatSync(r),l=this.identityOf(n),p=this.fileIdentities.get(e);if(p&&!this.identitiesEqual(p,l)){let v=this.readFirstLogEntryFromFd(r),S=v?.type==="session_meta"?v.payload.id:void 0;if(this.authoritativeSessionId&&S!==this.authoritativeSessionId){let P=new Error("Authoritative Codex rollout path was replaced by a different session");o.error("Refusing replaced rollout with mismatched session identity",{filePath:e,expectedSessionId:this.authoritativeSessionId,replacementSessionId:S}),this.emit("error",P);return}o.info("Session log inode replaced; resetting byte and decoder state",{filePath:e,oldPosition:s,oldIdentity:p,newIdentity:l}),s=0,this.filePositions.set(e,0),this.oversizedRecordFiles.delete(e)}if(this.fileIdentities.set(e,l),n.size<s&&(o.info("Session log truncated/rotated; resetting cursor to 0",{filePath:e,oldPosition:s,size:n.size}),s=0,this.filePositions.set(e,0),this.oversizedRecordFiles.delete(e)),n.size<=s)return;if(n.size-s>this.maxBacklogBytes){if(s===0&&this.sessionId===null){let S=this.readFirstLogEntryFromFd(r);S?.type==="session_meta"&&this.processLogEntry(S)}let v=this.findRecentRecordBoundary(r,n.size);if(o.warn("Codex rollout backlog exceeded live-mirror limit; skipping archival prefix",{filePath:e,oldPosition:s,newPosition:v.position,size:n.size,maxBacklogBytes:this.maxBacklogBytes,discardUntilNewline:v.discardUntilNewline}),s=v.position,this.filePositions.set(e,s),v.discardUntilNewline?this.oversizedRecordFiles.add(e):this.oversizedRecordFiles.delete(e),n.size<=s)return}let c=n.size,d=Buffer.allocUnsafe(64*1024),u=s,m=[],y=0,f=this.oversizedRecordFiles.has(e);for(;u<c;){if(i!==this.readGeneration)return;let v=Math.min(d.length,c-u),S=w.readSync(r,d,0,v,u);if(S<=0)break;let P=0;for(;P<S;){if(i!==this.readGeneration)return;let b=d.subarray(0,S).indexOf(10,P),T=b!==-1,F=T?b:S,O=F-P,D=O+(T?1:0);if(f){u+=D,this.filePositions.set(e,u),T&&(f=!1,this.oversizedRecordFiles.delete(e)),P=T?b+1:S;continue}if(O>0&&(m.push(Buffer.from(d.subarray(P,F))),y+=O),u+=D,y>this.maxRecordBytes){o.warn("Discarding oversized Codex rollout JSONL record",{filePath:e,recordEnd:u,maxRecordBytes:this.maxRecordBytes}),this.filePositions.set(e,u),m=[],y=0,f=!T,f?this.oversizedRecordFiles.add(e):this.oversizedRecordFiles.delete(e),P=T?b+1:S;continue}if(!T){P=S;continue}let A=Buffer.concat(m,y);m=[],y=0,A.length>0&&A[A.length-1]===13&&(A=A.subarray(0,A.length-1));let Pe=A.toString("utf8");if(Pe.trim())try{let Ie=JSON.parse(Pe);this.processLogEntry(Ie)}catch(Ie){o.warn("Failed to parse log line",{filePath:e,line:Pe.substring(0,100),error:Ie})}if(i===this.readGeneration&&this.filePositions.set(e,u),i!==this.readGeneration)return;P=b+1}}}catch(n){o.error("Error reading log file",{filePath:e,error:n}),this.emit("error",n)}finally{if(r!==null)try{w.closeSync(r)}catch{}}}findRecentRecordBoundary(e,i){let s=Math.max(0,i-this.maxBacklogBytes),r=s+Math.min(i-s,this.maxRecordBytes);{if(s===0)return{position:0,discardUntilNewline:!1};let n=Buffer.allocUnsafe(1);if(w.readSync(e,n,0,1,s-1)===1&&n[0]===10)return{position:s,discardUntilNewline:!1};let l=Buffer.allocUnsafe(Lt),p=s;for(;p<r;){let c=Math.min(l.length,r-p),d=w.readSync(e,l,0,c,p);if(d<=0)break;let u=l.subarray(0,d).indexOf(10);if(u!==-1)return{position:p+u+1,discardUntilNewline:!1};p+=d}if(p===r&&p<i&&r-s===this.maxRecordBytes){let c=Buffer.allocUnsafe(1);if(w.readSync(e,c,0,1,p)===1&&c[0]===10)return{position:p+1,discardUntilNewline:!1}}return{position:s,discardUntilNewline:!0}}}readFirstLogEntryFromFd(e){try{let i=[],s=0,r=Buffer.allocUnsafe(64*1024);for(;s<be;){let l=Math.min(r.length,be-s),p=w.readSync(e,r,0,l,s);if(p<=0)break;let c=r.subarray(0,p).indexOf(10);if(c!==-1){i.push(Buffer.from(r.subarray(0,c)));break}i.push(Buffer.from(r.subarray(0,p))),s+=p}if(i.length===0)return null;let n=Buffer.concat(i).toString("utf8");return n.trim()?JSON.parse(n):null}catch{return null}}processLogEntry(e){if(o.debug("Processing log entry",{type:e.type}),e.type==="session_meta"){let i=e.payload;this.sessionId=i.id,o.info("Codex session started",{sessionId:i.id,cwd:i.cwd,cliVersion:i.cli_version}),this.emit("session-started",i);return}this.emit("log-entry",e),e.type==="event_msg"&&e.payload?.type?this.emit(`event:${e.payload.type}`,e):e.type==="response_item"&&e.payload?.type&&this.emit(`response:${e.payload.type}`,e)}};var Ee=require("uuid"),C=require("@quantiya/codevibe-core");var H=new Map,W=[],Kt=24;function Q(a){return typeof a=="string"?a.trim():""}function Ge(a){return typeof a=="string"?a:""}function je(a,t,e){let i=Q(e);if(!i)return!1;let s=Ge(t);return W.some(r=>r.text===i&&r.phase===s&&r.carrier!==a)}function Bt(a){let t=Q(a);return t.length>0&&W.some(e=>e.text===t&&e.phase==="final_answer")}function we(a,t,e){let i=Q(e);if(i)for(W.push({carrier:a,phase:Ge(t),text:i});W.length>Kt;)W.shift()}function se(){W.length=0}function qt(a){let t=a?.content;return Array.isArray(t)?t.filter(e=>e&&(e.type==="output_text"||e.type==="text")).map(e=>typeof e?.text=="string"?e.text:"").join(""):""}function Ut(a){let t=a?.summary;return!Array.isArray(t)||t.length===0?"":t.map(e=>typeof e=="string"?e:typeof e?.text=="string"?e.text:"").join(`
3
- `).trim()}function $t(a){if(typeof a!="string")return"";let t=a.match(/"cmd"\s*:\s*"((?:\\.|[^"\\])*)"/);if(t)try{return JSON.parse(`"${t[1]}"`)}catch{return t[1]}return""}function ne(a){if(typeof a!="string"||!a.startsWith(`*** Begin Patch
1
+ "use strict";var kt=Object.create;var te=Object.defineProperty;var Ft=Object.getOwnPropertyDescriptor;var Dt=Object.getOwnPropertyNames;var Nt=Object.getPrototypeOf,Mt=Object.prototype.hasOwnProperty;var Lt=(a,t)=>{for(var e in t)te(a,e,{get:t[e],enumerable:!0})},Be=(a,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Dt(t))!Mt.call(a,s)&&s!==e&&te(a,s,{get:()=>t[s],enumerable:!(i=Ft(t,s))||i.enumerable});return a};var E=(a,t,e)=>(e=a!=null?kt(Nt(a)):{},Be(t||!a||!a.__esModule?te(e,"default",{value:a,enumerable:!0}):e,a)),Kt=a=>Be(te({},"__esModule",{value:!0}),a);var Bi={};Lt(Bi,{CodexCompanionServer:()=>Se,TERMINAL_SHUTDOWN_SIGNALS:()=>Rt,classifyTmuxHasSessionError:()=>_t,extractShellCommandFromPromptText:()=>Tt,extractShellCommandPartsFromPromptText:()=>Ke,restoreCodexPersistOptionCommand:()=>Ot,shellCommandPartsMatchExpected:()=>xt});module.exports=Kt(Bi);var ee=require("uuid"),ve=E(require("crypto")),z=E(require("fs")),K=E(require("path")),De=E(require("os")),Me=require("util"),Pe=require("child_process"),Le=require("shell-quote"),h=require("@quantiya/codevibe-core");var qe=E(require("os")),Ue=E(require("path")),$e=require("@quantiya/codevibe-core"),o=(0,$e.createLogger)({name:"codevibe-codex",logFile:Ue.default.join(qe.default.tmpdir(),"codevibe-codex-mcp.log"),level:"debug"});var He=require("events"),w=E(require("fs")),F=E(require("path")),ze=require("string_decoder"),je=require("chokidar"),We=require("@quantiya/codevibe-core");var we=256*1024,Bt=64*1024*1024,qt=16*1024*1024,Ut=64*1024;function $t(a=process.env){let t=a.CODEX_HOME?.trim();return t?F.resolve(t,"sessions"):(0,We.getConfig)().codex.sessionsDir}var ie=class extends He.EventEmitter{constructor(e={}){super();this.watcher=null;this.filePositions=new Map;this.activeLogFile=null;this.sessionId=null;this.isWatching=!1;this.startTime=0;this.sessionsDir=null;this.activeReads=new Map;this.pendingReads=new Set;this.readGeneration=0;this.oversizedRecordFiles=new Set;this.fileIdentities=new Map;this.bindSource=null;this.authoritativeSessionId=null;this.pendingTranscriptBind=null;this.transcriptBindGeneration=0;this.transcriptBindingsClosed=!1;this.activeTranscriptAdmissions=new Set;this.startBaselines=new Map;if(this.maxBacklogBytes=e.maxBacklogBytes??Bt,this.maxRecordBytes=e.maxRecordBytes??qt,!Number.isSafeInteger(this.maxBacklogBytes)||this.maxBacklogBytes<=0)throw new RangeError("maxBacklogBytes must be a positive safe integer");if(!Number.isSafeInteger(this.maxRecordBytes)||this.maxRecordBytes<=0)throw new RangeError("maxRecordBytes must be a positive safe integer")}start(){if(this.isWatching){o.warn("Session log watcher already running");return}this.transcriptBindGeneration++,this.transcriptBindingsClosed=!1;let e=$t();this.sessionsDir=e,o.info("Starting Codex session log watcher",{sessionsDir:e}),w.existsSync(e)||(o.info("Codex sessions directory does not exist yet, creating...",{sessionsDir:e}),w.mkdirSync(e,{recursive:!0})),this.startTime=Date.now(),this.readGeneration++,this.bindSource=null,this.authoritativeSessionId=null,this.snapshotStartBaselines(e),this.drainPendingTranscriptBind(),this.watcher=(0,je.watch)(e,{persistent:!0,ignoreInitial:!0,awaitWriteFinish:{stabilityThreshold:100,pollInterval:50},depth:4,ignored:i=>{let s=F.basename(i);return w.existsSync(i)&&w.statSync(i).isDirectory()?!1:!s.startsWith("rollout-")||!s.endsWith(".jsonl")}}),this.watcher.on("add",i=>{i.endsWith(".jsonl")&&this.onFileAdded(i)}),this.watcher.on("change",i=>{i.endsWith(".jsonl")&&this.onFileChanged(i)}),this.watcher.on("error",i=>{o.error("Watcher error:",i),this.emit("error",i)}),this.watcher.on("ready",()=>{o.info("Session log watcher ready"),this.activeLogFile||o.debug("No authoritative transcript hook received yet; rollout watcher remains fail-closed")}),this.isWatching=!0,o.info("Session log watcher started")}stop(){this.transcriptBindingsClosed=!0,this.transcriptBindGeneration++,this.watcher&&(this.watcher.close(),this.watcher=null),this.isWatching=!1,this.readGeneration++,this.filePositions.clear(),this.activeReads.clear(),this.pendingReads.clear(),this.oversizedRecordFiles.clear(),this.activeLogFile=null,this.sessionId=null,this.sessionsDir=null,this.bindSource=null,this.authoritativeSessionId=null,this.pendingTranscriptBind&&(this.pendingTranscriptBind.resolve({status:"rejected",reason:"watcher_stopped"}),this.pendingTranscriptBind=null),this.startBaselines.clear(),this.fileIdentities.clear(),o.info("Session log watcher stopped")}async waitForTranscriptAdmissions(){for(;this.activeTranscriptAdmissions.size>0;)await Promise.all(Array.from(this.activeTranscriptAdmissions,e=>e.then(()=>{},()=>{})))}getSessionId(){return this.sessionId}getActiveLogFile(){return this.activeLogFile}getBindSource(){return this.bindSource}getAuthoritativeSessionId(){return this.authoritativeSessionId}tryRealpath(e){try{return w.realpathSync(e)}catch{return null}}isInteractiveRollout(e){return!(!e||e.originator==="codex_exec"||e.threadSource==="subagent"||e.sourceSubagent)}identityOf(e){return{dev:e.dev,ino:e.ino}}identitiesEqual(e,i){return e.dev===i.dev&&e.ino===i.ino}computeSeedOffset(e){let i;try{i=w.statSync(e)}catch{return 0}if(this.getSessionAgeMs(e,i)>=this.startTime)return 0;let r=this.startBaselines.get(e);return r&&this.identitiesEqual(r.identity,this.identityOf(i))?r.size:i.size}applyBind(e,i){this.readGeneration++,this.activeReads.delete(e),this.pendingReads.delete(e),this.activeLogFile=e,this.bindSource=i,i==="heuristic"&&(this.authoritativeSessionId=null),this.filePositions.has(e)||this.filePositions.set(e,this.computeSeedOffset(e));try{this.fileIdentities.set(e,this.identityOf(w.statSync(e)))}catch{this.fileIdentities.delete(e)}this.scheduleReadNewLines(e)}bindToFile(e){let i=this.tryRealpath(e);if(!i){o.warn("bindToFile: could not canonicalize path; skipping",{filePath:e});return}if(i===this.activeLogFile){this.bindSource="authoritative";return}o.info("bindToFile: authoritative bind to session rollout",{filePath:i}),this.applyBind(i,"authoritative")}async bindToSessionTranscript(e,i){if(typeof e!="string"||e.length===0)return{status:"rejected",reason:"missing_transcript_path"};if(this.transcriptBindingsClosed)return{status:"rejected",reason:"watcher_stopped"};if(!this.sessionsDir){let s=this.pendingTranscriptBind;if(s)return s.transcriptPath===e&&s.expectedSessionId===i?{status:"pending",completion:s.completion}:(o.warn("bindToSessionTranscript: conflicting startup bind rejected",{transcriptPath:e,expectedSessionId:i,pendingExpectedSessionId:s.expectedSessionId}),{status:"rejected",reason:"conflicting_startup_bind"});let r,n=new Promise(l=>{r=l});return this.pendingTranscriptBind={transcriptPath:e,expectedSessionId:i,completion:n,resolve:r},o.debug("bindToSessionTranscript: watcher not started yet; queued pending authoritative bind",{transcriptPath:e,expectedSessionId:i}),{status:"pending",completion:n}}return this.trackTranscriptAdmission(this.validateAndBindSessionTranscript(e,i,this.transcriptBindGeneration))}trackTranscriptAdmission(e){let i;return i=e.finally(()=>{this.activeTranscriptAdmissions.delete(i)}),this.activeTranscriptAdmissions.add(i),i}isTranscriptAdmissionCurrent(e,i){return!this.transcriptBindingsClosed&&this.transcriptBindGeneration===e&&this.sessionsDir===i}async validateAndBindSessionTranscript(e,i,s){let r=this.sessionsDir;if(!r||!this.isTranscriptAdmissionCurrent(s,r))return{status:"rejected",reason:"watcher_not_started"};let n=this.tryRealpath(e),l=this.tryRealpath(r);if(!n||!l)return{status:"rejected",reason:"canonicalization_failed"};let p=F.relative(l,n);if(p===".."||p.startsWith(".."+F.sep)||F.isAbsolute(p))return o.warn("bindToSessionTranscript: path not under sessionsDir; rejecting",{transcriptPath:n}),{status:"rejected",reason:"outside_sessions_directory"};let c=F.basename(n);if(!c.startsWith("rollout-")||!c.endsWith(".jsonl"))return{status:"rejected",reason:"invalid_rollout_filename"};let d=null;for(let u=0;u<3;u++){if(!this.isTranscriptAdmissionCurrent(s,r))return{status:"rejected",reason:"watcher_stopped"};if(d=this.readSessionMeta(n),d)break;if(await new Promise(m=>setTimeout(m,20)),!this.isTranscriptAdmissionCurrent(s,r))return{status:"rejected",reason:"watcher_stopped"}}return!d||d.id!==i?(o.debug("bindToSessionTranscript: meta missing or id mismatch; not binding",{transcriptPath:n,metaId:d?.id,expectedSessionId:i}),{status:"rejected",reason:"session_metadata_mismatch"}):this.isInteractiveRollout(d)?this.isTranscriptAdmissionCurrent(s,r)?(this.bindToFile(n),this.authoritativeSessionId=i,{status:"bound"}):{status:"rejected",reason:"watcher_stopped"}:(o.debug("bindToSessionTranscript: non-interactive rollout (codex_exec/subagent); not binding",{transcriptPath:n,originator:d.originator,threadSource:d.threadSource}),{status:"rejected",reason:"non_interactive_rollout"})}async drainPendingTranscriptBind(){let e=this.pendingTranscriptBind;if(!e)return;this.pendingTranscriptBind=null;let i;try{this.transcriptBindingsClosed?i={status:"rejected",reason:"watcher_stopped"}:i=await this.trackTranscriptAdmission(this.validateAndBindSessionTranscript(e.transcriptPath,e.expectedSessionId,this.transcriptBindGeneration))}catch(s){o.warn("bindToSessionTranscript: pending startup bind failed",{expectedSessionId:e.expectedSessionId,error:s instanceof Error?s.message:String(s)}),i={status:"rejected",reason:"startup_bind_failed"}}e.resolve(i)}onFileAdded(e){let i=this.tryRealpath(e);i&&(i===this.activeLogFile?this.scheduleReadNewLines(i):o.debug("Ignoring unowned rollout add; awaiting authoritative hook binding",{filePath:i}))}onFileChanged(e){let i=this.tryRealpath(e);i&&(this.activeLogFile&&i!==this.activeLogFile||this.activeLogFile&&this.scheduleReadNewLines(i))}readSessionMeta(e){let i=this.readFirstLogEntry(e);if(!i)return null;let s=i.payload||{};return{id:typeof s.id=="string"?s.id:void 0,timestamp:typeof i.timestamp=="string"?i.timestamp:void 0,originator:typeof s.originator=="string"?s.originator:void 0,cwd:typeof s.cwd=="string"?s.cwd:void 0,threadSource:typeof s.thread_source=="string"?s.thread_source:void 0,sourceSubagent:!!(s.source&&typeof s.source=="object"&&s.source.subagent)}}readFirstLogEntry(e){let i=null;try{i=w.openSync(e,"r");let s=new ze.StringDecoder("utf8"),r=Buffer.alloc(64*1024),n="",l=0,p=-1;for(;l<we;){let d=w.readSync(i,r,0,r.length,l);if(d<=0||(l+=d,n+=s.write(r.subarray(0,d)),p=n.indexOf(`
2
+ `),p!==-1))break}n+=s.end();let c=p===-1?n:n.slice(0,p);return c.trim()?JSON.parse(c):null}catch{return null}finally{if(i!==null)try{w.closeSync(i)}catch{}}}snapshotStartBaselines(e){this.startBaselines.clear();try{for(let{filePath:i,size:s}of this.collectRecentSessionFiles(e,!0)){let r=this.tryRealpath(i);if(r)try{let n=w.statSync(r);this.startBaselines.set(r,{size:s,identity:this.identityOf(n)})}catch{}}}catch{}}getSessionAgeMs(e,i){let s=this.readSessionMeta(e);if(s?.timestamp){let r=Date.parse(s.timestamp);if(!Number.isNaN(r))return r}return i.birthtimeMs||i.ctimeMs}collectRecentSessionFiles(e,i=!1){let s=[],r=[e];for(;r.length>0;){let n=r.pop();if(!n)continue;let l;try{l=w.readdirSync(n,{withFileTypes:!0})}catch{continue}for(let p of l){let c=F.join(n,p.name);if(p.isDirectory()){r.push(c);continue}if(!(!p.isFile()||!p.name.startsWith("rollout-")||!p.name.endsWith(".jsonl")))try{let d=w.statSync(c),u=d.birthtimeMs||d.ctimeMs,m=d.mtimeMs;(i||u>=this.startTime||m>=this.startTime-5e3)&&s.push({filePath:c,createdAt:u,modifiedAt:m,size:d.size})}catch{}}}return s}scheduleReadNewLines(e){let i=this.activeReads.get(e);if(i)return this.pendingReads.add(e),i;let s=this.readGeneration,r=Promise.resolve().then(()=>this.drainReadQueue(e,s));return this.activeReads.set(e,r),r.finally(()=>{this.activeReads.get(e)===r&&(this.activeReads.delete(e),this.pendingReads.delete(e))}),r}async drainReadQueue(e,i){do this.pendingReads.delete(e),await this.readNewLines(e,i);while(i===this.readGeneration&&this.pendingReads.has(e))}async readNewLines(e,i){if(i!==this.readGeneration)return;let s=this.filePositions.get(e)||0,r=null;try{r=w.openSync(e,w.constants.O_RDONLY|(w.constants.O_NOFOLLOW??0));let n=w.fstatSync(r),l=this.identityOf(n),p=this.fileIdentities.get(e);if(p&&!this.identitiesEqual(p,l)){let v=this.readFirstLogEntryFromFd(r),S=v?.type==="session_meta"?v.payload.id:void 0;if(this.authoritativeSessionId&&S!==this.authoritativeSessionId){let P=new Error("Authoritative Codex rollout path was replaced by a different session");o.error("Refusing replaced rollout with mismatched session identity",{filePath:e,expectedSessionId:this.authoritativeSessionId,replacementSessionId:S}),this.emit("error",P);return}o.info("Session log inode replaced; resetting byte and decoder state",{filePath:e,oldPosition:s,oldIdentity:p,newIdentity:l}),s=0,this.filePositions.set(e,0),this.oversizedRecordFiles.delete(e)}if(this.fileIdentities.set(e,l),n.size<s&&(o.info("Session log truncated/rotated; resetting cursor to 0",{filePath:e,oldPosition:s,size:n.size}),s=0,this.filePositions.set(e,0),this.oversizedRecordFiles.delete(e)),n.size<=s)return;if(n.size-s>this.maxBacklogBytes){if(s===0&&this.sessionId===null){let S=this.readFirstLogEntryFromFd(r);S?.type==="session_meta"&&this.processLogEntry(S)}let v=this.findRecentRecordBoundary(r,n.size);if(o.warn("Codex rollout backlog exceeded live-mirror limit; skipping archival prefix",{filePath:e,oldPosition:s,newPosition:v.position,size:n.size,maxBacklogBytes:this.maxBacklogBytes,discardUntilNewline:v.discardUntilNewline}),s=v.position,this.filePositions.set(e,s),v.discardUntilNewline?this.oversizedRecordFiles.add(e):this.oversizedRecordFiles.delete(e),n.size<=s)return}let c=n.size,d=Buffer.allocUnsafe(64*1024),u=s,m=[],y=0,f=this.oversizedRecordFiles.has(e);for(;u<c;){if(i!==this.readGeneration)return;let v=Math.min(d.length,c-u),S=w.readSync(r,d,0,v,u);if(S<=0)break;let P=0;for(;P<S;){if(i!==this.readGeneration)return;let b=d.subarray(0,S).indexOf(10,P),T=b!==-1,k=T?b:S,O=k-P,N=O+(T?1:0);if(f){u+=N,this.filePositions.set(e,u),T&&(f=!1,this.oversizedRecordFiles.delete(e)),P=T?b+1:S;continue}if(O>0&&(m.push(Buffer.from(d.subarray(P,k))),y+=O),u+=N,y>this.maxRecordBytes){o.warn("Discarding oversized Codex rollout JSONL record",{filePath:e,recordEnd:u,maxRecordBytes:this.maxRecordBytes}),this.filePositions.set(e,u),m=[],y=0,f=!T,f?this.oversizedRecordFiles.add(e):this.oversizedRecordFiles.delete(e),P=T?b+1:S;continue}if(!T){P=S;continue}let A=Buffer.concat(m,y);m=[],y=0,A.length>0&&A[A.length-1]===13&&(A=A.subarray(0,A.length-1));let Ie=A.toString("utf8");if(Ie.trim())try{let be=JSON.parse(Ie);this.processLogEntry(be)}catch(be){o.warn("Failed to parse log line",{filePath:e,line:Ie.substring(0,100),error:be})}if(i===this.readGeneration&&this.filePositions.set(e,u),i!==this.readGeneration)return;P=b+1}}}catch(n){o.error("Error reading log file",{filePath:e,error:n}),this.emit("error",n)}finally{if(r!==null)try{w.closeSync(r)}catch{}}}findRecentRecordBoundary(e,i){let s=Math.max(0,i-this.maxBacklogBytes),r=s+Math.min(i-s,this.maxRecordBytes);{if(s===0)return{position:0,discardUntilNewline:!1};let n=Buffer.allocUnsafe(1);if(w.readSync(e,n,0,1,s-1)===1&&n[0]===10)return{position:s,discardUntilNewline:!1};let l=Buffer.allocUnsafe(Ut),p=s;for(;p<r;){let c=Math.min(l.length,r-p),d=w.readSync(e,l,0,c,p);if(d<=0)break;let u=l.subarray(0,d).indexOf(10);if(u!==-1)return{position:p+u+1,discardUntilNewline:!1};p+=d}if(p===r&&p<i&&r-s===this.maxRecordBytes){let c=Buffer.allocUnsafe(1);if(w.readSync(e,c,0,1,p)===1&&c[0]===10)return{position:p+1,discardUntilNewline:!1}}return{position:s,discardUntilNewline:!0}}}readFirstLogEntryFromFd(e){try{let i=[],s=0,r=Buffer.allocUnsafe(64*1024);for(;s<we;){let l=Math.min(r.length,we-s),p=w.readSync(e,r,0,l,s);if(p<=0)break;let c=r.subarray(0,p).indexOf(10);if(c!==-1){i.push(Buffer.from(r.subarray(0,c)));break}i.push(Buffer.from(r.subarray(0,p))),s+=p}if(i.length===0)return null;let n=Buffer.concat(i).toString("utf8");return n.trim()?JSON.parse(n):null}catch{return null}}processLogEntry(e){if(o.debug("Processing log entry",{type:e.type}),e.type==="session_meta"){let i=e.payload;this.sessionId=i.id,o.info("Codex session started",{sessionId:i.id,cwd:i.cwd,cliVersion:i.cli_version}),this.emit("session-started",i);return}this.emit("log-entry",e),e.type==="event_msg"&&e.payload?.type?this.emit(`event:${e.payload.type}`,e):e.type==="response_item"&&e.payload?.type&&this.emit(`response:${e.payload.type}`,e)}};var Re=require("uuid"),C=require("@quantiya/codevibe-core");var j=new Map,G=[],Ht=24;function Y(a){return typeof a=="string"?a.trim():""}function Xe(a){return typeof a=="string"?a:""}function Ge(a,t,e){let i=Y(e);if(!i)return!1;let s=Xe(t);return G.some(r=>r.text===i&&r.phase===s&&r.carrier!==a)}function zt(a){let t=Y(a);return t.length>0&&G.some(e=>e.text===t&&e.phase==="final_answer")}function Ee(a,t,e){let i=Y(e);if(i)for(G.push({carrier:a,phase:Xe(t),text:i});G.length>Ht;)G.shift()}function se(){G.length=0}function jt(a){let t=a?.content;return Array.isArray(t)?t.filter(e=>e&&(e.type==="output_text"||e.type==="text")).map(e=>typeof e?.text=="string"?e.text:"").join(""):""}function Wt(a){let t=a?.summary;return!Array.isArray(t)||t.length===0?"":t.map(e=>typeof e=="string"?e:typeof e?.text=="string"?e.text:"").join(`
3
+ `).trim()}function Gt(a){if(typeof a!="string")return"";let t=a.match(/"cmd"\s*:\s*"((?:\\.|[^"\\])*)"/);if(t)try{return JSON.parse(`"${t[1]}"`)}catch{return t[1]}return""}function ne(a){if(typeof a!="string"||!a.startsWith(`*** Begin Patch
4
4
  `)||!a.endsWith(`
5
5
  *** End Patch`))return"";let t=a.split(`
6
- `);return t[0]!=="*** Begin Patch"||t[t.length-1]!=="*** End Patch"||t.filter(e=>e==="*** Begin Patch").length!==1||t.filter(e=>e==="*** End Patch").length!==1||!/^\*\*\* (?:Add|Update|Delete) File: [^\r\n]+$/.test(t[1]??"")?"":a}function Y(a){if(typeof a!="string")return"";let t="[A-Za-z_$][\\w$]*",e='"(?:\\\\.|[^"\\\\])*"',i=a.match(new RegExp(`^\\s*(?:const|let|var)\\s+(${t})\\s*=\\s*await\\s+tools\\.apply_patch\\(\\s*(${e})\\s*\\)\\s*;\\s*(?:text\\(\\s*(${t})\\s*\\)\\s*;\\s*)?$`,"s")),s=a.match(new RegExp(`^\\s*(?:const|let|var)\\s+(${t})\\s*=\\s*(${e})\\s*;\\s*(?:const|let|var)\\s+(${t})\\s*=\\s*await\\s+tools\\.apply_patch\\(\\s*(${t})\\s*\\)\\s*;\\s*(?:text\\(\\s*(${t})\\s*\\)\\s*;\\s*)?$`,"s")),r;if(i&&(!i[3]||i[3]===i[1])?r=i[2]:s&&s[4]===s[1]&&(!s[5]||s[5]===s[3])&&(r=s[2]),!r)return"";try{return ne(JSON.parse(r))}catch{return""}}var Ht=new Set(["exec","shell","shell_command","bash","run_command","run_cmd"]);function We(a,t){return typeof t!="string"?"":a==="apply_patch"?t:a==="exec"?Y(t):typeof a=="string"&&Ht.has(a)?"":t.includes("*** Begin Patch")?t:""}function Ve(a,t){let e={sessionId:t,source:C.EventSource.DESKTOP};if(a.type==="event_msg"&&a.payload){let i=a.payload.type;switch(i){case"user_message":return se(),{...e,type:C.EventType.USER_PROMPT,content:a.payload.message||"",metadata:{images:a.payload.images||[]}};case"agent_message":{let s=a.payload.message||"",r=a.payload.phase;return je("agent_message",r,s)?null:(we("agent_message",r,s),{...e,type:C.EventType.ASSISTANT_RESPONSE,content:s,metadata:{phase:r}})}case"agent_reasoning":return{...e,type:C.EventType.REASONING,content:a.payload.text||""};case"task_started":return se(),o.debug("Skipping task_started entry (turn boundary)"),null;case"task_complete":{let s=Q(a.payload.last_agent_message),r=null;return s&&!Bt(s)?(we("task_complete","final_answer",s),r={...e,type:C.EventType.ASSISTANT_RESPONSE,content:s,metadata:{phase:"final_answer"}}):o.debug("Skipping task_complete (final already emitted or empty)"),se(),r}case"token_count":return o.debug("Skipping token_count entry"),null;default:return o.debug("Unknown event_msg type",{type:i}),null}}if(a.type==="response_item"&&a.payload){let i=a.payload.type;if(i==="message"){if(a.payload.role!=="assistant")return o.debug("Skipping non-assistant response_item/message",{role:a.payload.role}),null;let s=qt(a.payload),r=a.payload.phase;return!Q(s)||je("response_item",r,s)?null:(we("response_item",r,s),{...e,type:C.EventType.ASSISTANT_RESPONSE,content:s,metadata:{phase:r}})}if(i==="reasoning"){let s=Ut(a.payload);return s?{...e,type:C.EventType.REASONING,content:s}:(o.debug("Skipping reasoning response_item (no readable summary)"),null)}if(i==="function_call"){let{name:s,arguments:r,call_id:n}=a.payload,l={};try{l=JSON.parse(r||"{}")}catch{l={raw:r}}let p=(0,Ee.v4)();H.set(n,{name:s,input:r,eventId:p});let c=X(s),d=zt(s,l);return{...e,type:C.EventType.TOOL_USE,content:d,metadata:{toolName:c,toolInput:l,callId:n,status:"running"}}}if(i==="function_call_output"){let{call_id:s,output:r}=a.payload,n=H.get(s);H.delete(s);let l=n?.name?X(n.name):"Tool",p=Re(re(r),500);return{...e,type:C.EventType.TOOL_USE,content:`${l} completed:
7
- ${p}`,metadata:{toolName:l,toolOutput:r,callId:s,status:"completed"}}}if(i==="custom_tool_call"){let{name:s,call_id:r,input:n,status:l}=a.payload;H.set(r,{name:s,input:n,eventId:(0,Ee.v4)()});let p=We(s,n);if(p){let u=Wt(p),{oldString:m,newString:y}=jt(p),f=u?`Editing: ${u.filePath}`:"Applying patch";return{...e,type:C.EventType.TOOL_USE,content:f,metadata:{tool_name:"Edit",tool_input:{file_path:u?.filePath||"",old_string:m,new_string:y},callId:r,status:l||"running"}}}let c=$t(n),d=X(s==="exec"?"shell":s||"tool");return{...e,type:C.EventType.TOOL_USE,content:c?`Running: ${c}`:`Running ${d}`,metadata:{toolName:d,toolInput:c?{command:c}:{raw:typeof n=="string"?n:""},callId:r,status:l||"running"}}}if(i==="custom_tool_call_output"){let{call_id:s,output:r}=a.payload,n=H.get(s);if(H.delete(s),We(n?.name,n?.input)){let c={};if(typeof r=="string")try{c=JSON.parse(r||"{}")}catch{c={raw:r}}else if(Array.isArray(r)){let v=re(r);try{c=JSON.parse(v)}catch{c={raw:r,text:v}}}else c=r??{};let d=c!==null&&typeof c=="object"&&!Array.isArray(c)?c:null,u=typeof d?.output=="string"?d.output:void 0,m=d?.error,y=u!==void 0&&u.includes("Success"),f=typeof m=="string"?m:m!==void 0?JSON.stringify(m):"Unknown error";return{...e,type:C.EventType.TOOL_USE,content:y?"File edit applied successfully":`Edit failed: ${f}`,metadata:{toolName:"Edit",toolOutput:c,callId:s,status:"completed",success:y}}}let l=n?.name?X(n.name==="exec"?"shell":n.name):"Tool",p=re(r);return{...e,type:C.EventType.TOOL_USE,content:`${l} completed:
8
- ${Re(p,500)}`,metadata:{toolName:l,toolOutput:r,callId:s,status:"completed"}}}return o.debug("Unknown response_item type",{type:i}),null}return a.type==="turn_context"?(o.debug("Skipping turn_context entry"),null):(o.debug("Unhandled log entry type",{type:a.type}),null)}function X(a){return{shell_command:"Bash",shell:"Bash",apply_patch:"Edit",write_file:"Write",read_file:"Read",list_files:"Glob",search_files:"Grep",web_search:"WebSearch",web_fetch:"WebFetch"}[a]||a}function zt(a,t){switch(a){case"shell_command":case"shell":return`Running: ${t.command||"command"}`;case"read_file":return`Reading: ${t.file_path||t.path||"file"}`;case"write_file":return`Writing: ${t.file_path||t.path||"file"}`;case"list_files":return`Listing: ${t.path||"."}`;case"search_files":return`Searching for: ${t.pattern||t.query||"pattern"}`;case"web_search":return`Searching web: ${t.query||"query"}`;default:return`Running ${X(a)}`}}function jt(a){let t=[],e=[],i=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;for(let s of a.split(`
6
+ `);return t[0]!=="*** Begin Patch"||t[t.length-1]!=="*** End Patch"||t.filter(e=>e==="*** Begin Patch").length!==1||t.filter(e=>e==="*** End Patch").length!==1||!/^\*\*\* (?:Add|Update|Delete) File: [^\r\n]+$/.test(t[1]??"")?"":a}function Q(a){if(typeof a!="string")return"";let t="[A-Za-z_$][\\w$]*",e='"(?:\\\\.|[^"\\\\])*"',i=a.match(new RegExp(`^\\s*(?:const|let|var)\\s+(${t})\\s*=\\s*await\\s+tools\\.apply_patch\\(\\s*(${e})\\s*\\)\\s*;\\s*(?:text\\(\\s*(${t})\\s*\\)\\s*;\\s*)?$`,"s")),s=a.match(new RegExp(`^\\s*(?:const|let|var)\\s+(${t})\\s*=\\s*(${e})\\s*;\\s*(?:const|let|var)\\s+(${t})\\s*=\\s*await\\s+tools\\.apply_patch\\(\\s*(${t})\\s*\\)\\s*;\\s*(?:text\\(\\s*(${t})\\s*\\)\\s*;\\s*)?$`,"s")),r;if(i&&(!i[3]||i[3]===i[1])?r=i[2]:s&&s[4]===s[1]&&(!s[5]||s[5]===s[3])&&(r=s[2]),!r)return"";try{return ne(JSON.parse(r))}catch{return""}}var Vt=new Set(["exec","shell","shell_command","bash","run_command","run_cmd"]);function Ve(a,t){return typeof t!="string"?"":a==="apply_patch"?t:a==="exec"?Q(t):typeof a=="string"&&Vt.has(a)?"":t.includes("*** Begin Patch")?t:""}function Ye(a,t){let e={sessionId:t,source:C.EventSource.DESKTOP};if(a.type==="event_msg"&&a.payload){let i=a.payload.type;switch(i){case"user_message":return se(),{...e,type:C.EventType.USER_PROMPT,content:a.payload.message||"",metadata:{images:a.payload.images||[]}};case"agent_message":{let s=a.payload.message||"",r=a.payload.phase;return Ge("agent_message",r,s)?null:(Ee("agent_message",r,s),{...e,type:C.EventType.ASSISTANT_RESPONSE,content:s,metadata:{phase:r}})}case"agent_reasoning":return{...e,type:C.EventType.REASONING,content:a.payload.text||""};case"task_started":return se(),o.debug("Skipping task_started entry (turn boundary)"),null;case"task_complete":{let s=Y(a.payload.last_agent_message),r=null;return s&&!zt(s)?(Ee("task_complete","final_answer",s),r={...e,type:C.EventType.ASSISTANT_RESPONSE,content:s,metadata:{phase:"final_answer"}}):o.debug("Skipping task_complete (final already emitted or empty)"),se(),r}case"token_count":return o.debug("Skipping token_count entry"),null;default:return o.debug("Unknown event_msg type",{type:i}),null}}if(a.type==="response_item"&&a.payload){let i=a.payload.type;if(i==="message"){if(a.payload.role!=="assistant")return o.debug("Skipping non-assistant response_item/message",{role:a.payload.role}),null;let s=jt(a.payload),r=a.payload.phase;return!Y(s)||Ge("response_item",r,s)?null:(Ee("response_item",r,s),{...e,type:C.EventType.ASSISTANT_RESPONSE,content:s,metadata:{phase:r}})}if(i==="reasoning"){let s=Wt(a.payload);return s?{...e,type:C.EventType.REASONING,content:s}:(o.debug("Skipping reasoning response_item (no readable summary)"),null)}if(i==="function_call"){let{name:s,arguments:r,call_id:n}=a.payload,l={};try{l=JSON.parse(r||"{}")}catch{l={raw:r}}let p=(0,Re.v4)();j.set(n,{name:s,input:r,eventId:p});let c=X(s),d=Xt(s,l);return{...e,type:C.EventType.TOOL_USE,content:d,metadata:{toolName:c,toolInput:l,callId:n,status:"running"}}}if(i==="function_call_output"){let{call_id:s,output:r}=a.payload,n=j.get(s);j.delete(s);let l=n?.name?X(n.name):"Tool",p=_e(re(r),500);return{...e,type:C.EventType.TOOL_USE,content:`${l} completed:
7
+ ${p}`,metadata:{toolName:l,toolOutput:r,callId:s,status:"completed"}}}if(i==="custom_tool_call"){let{name:s,call_id:r,input:n,status:l}=a.payload;j.set(r,{name:s,input:n,eventId:(0,Re.v4)()});let p=Ve(s,n);if(p){let u=Qt(p),{oldString:m,newString:y}=Yt(p),f=u?`Editing: ${u.filePath}`:"Applying patch";return{...e,type:C.EventType.TOOL_USE,content:f,metadata:{tool_name:"Edit",tool_input:{file_path:u?.filePath||"",old_string:m,new_string:y},callId:r,status:l||"running"}}}let c=Gt(n),d=X(s==="exec"?"shell":s||"tool");return{...e,type:C.EventType.TOOL_USE,content:c?`Running: ${c}`:`Running ${d}`,metadata:{toolName:d,toolInput:c?{command:c}:{raw:typeof n=="string"?n:""},callId:r,status:l||"running"}}}if(i==="custom_tool_call_output"){let{call_id:s,output:r}=a.payload,n=j.get(s);if(j.delete(s),Ve(n?.name,n?.input)){let c={};if(typeof r=="string")try{c=JSON.parse(r||"{}")}catch{c={raw:r}}else if(Array.isArray(r)){let v=re(r);try{c=JSON.parse(v)}catch{c={raw:r,text:v}}}else c=r??{};let d=c!==null&&typeof c=="object"&&!Array.isArray(c)?c:null,u=typeof d?.output=="string"?d.output:void 0,m=d?.error,y=u!==void 0&&u.includes("Success"),f=typeof m=="string"?m:m!==void 0?JSON.stringify(m):"Unknown error";return{...e,type:C.EventType.TOOL_USE,content:y?"File edit applied successfully":`Edit failed: ${f}`,metadata:{toolName:"Edit",toolOutput:c,callId:s,status:"completed",success:y}}}let l=n?.name?X(n.name==="exec"?"shell":n.name):"Tool",p=re(r);return{...e,type:C.EventType.TOOL_USE,content:`${l} completed:
8
+ ${_e(p,500)}`,metadata:{toolName:l,toolOutput:r,callId:s,status:"completed"}}}return o.debug("Unknown response_item type",{type:i}),null}return a.type==="turn_context"?(o.debug("Skipping turn_context entry"),null):(o.debug("Unhandled log entry type",{type:a.type}),null)}function X(a){return{shell_command:"Bash",shell:"Bash",apply_patch:"Edit",write_file:"Write",read_file:"Read",list_files:"Glob",search_files:"Grep",web_search:"WebSearch",web_fetch:"WebFetch"}[a]||a}function Xt(a,t){switch(a){case"shell_command":case"shell":return`Running: ${t.command||"command"}`;case"read_file":return`Reading: ${t.file_path||t.path||"file"}`;case"write_file":return`Writing: ${t.file_path||t.path||"file"}`;case"list_files":return`Listing: ${t.path||"."}`;case"search_files":return`Searching for: ${t.pattern||t.query||"pattern"}`;case"web_search":return`Searching web: ${t.query||"query"}`;default:return`Running ${X(a)}`}}function Yt(a){let t=[],e=[],i=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;for(let s of a.split(`
9
9
  `))if(!(i.test(s)||s.startsWith("***")||s.startsWith("---")||s.startsWith("+++"))){if(s.startsWith("-"))t.push(s.slice(1));else if(s.startsWith("+"))e.push(s.slice(1));else if(s.startsWith(" ")){let r=s.slice(1);t.push(r),e.push(r)}}return{oldString:t.join(`
10
10
  `),newString:e.join(`
11
- `)}}function Wt(a){if(!a)return null;let t=a.match(/\*\*\* (?:Update|Add|Delete) File: (.+)/);return t?{filePath:t[1].trim()}:null}function re(a){if(a==null)return"";if(typeof a=="string")return a;if(Array.isArray(a))return a.map(t=>typeof t?.text=="string"?t.text:t?.type==="input_image"||typeof t?.image_url=="string"?"[image]":JSON.stringify(t??null)).join(`
12
- `);if(typeof a=="object"&&typeof a.text=="string")return a.text;try{return JSON.stringify(a)??""}catch{return String(a)}}function Re(a,t){return typeof a!="string"?Re(re(a),t):a?a.length<=t?a:a.substring(0,t)+"...":""}function _e(){H.clear(),se()}var _=require("@quantiya/codevibe-core"),Xe=3e3,Gt=8,Vt=10,Xt=100;function Qe(a){let t=Buffer.byteLength(a.carryover);return{status:"partial",progressKey:`${a.fileIdentity.dev}:${a.fileIdentity.ino}:${a.size}:${a.endOfComplete}:${t}`,size:a.size,carryoverBytes:t,endOfComplete:a.endOfComplete}}var Qt="tool_activity",G=class{constructor(t){this.consolidator=null;this.ledger=null;this.sessionMap=null;this.ownership=null;this.cursor=null;this.backendSessionId=null;this.rolloutId=null;this.transcriptPath=null;this.backstopTimer=null;this.backstopInFlight=null;this.cursorReady=Promise.resolve();this.cursorRepaired=!1;this.deps=t}static mintBackendSessionId(){return _.ToolActivitySessionMap.mintCodexBackendSessionId()}async recordedBackendSessionId(t){return new _.ToolActivitySessionMap({agent:"codex",logger:this.deps.logger,lock:new _.InProcessRolloutLock,root:this.deps.root}).ownerBackendSessionId(t)}get sessionId(){return this.backendSessionId}get rollout(){return this.rolloutId}isActive(){return this.consolidator!==null}async start(t,e,i){this.backendSessionId=t,this.rolloutId=e;let s=(0,_.createRolloutLock)({agent:"codex",logger:this.deps.logger,socketDir:this.deps.socketDir,root:this.deps.root});this.sessionMap=new _.ToolActivitySessionMap({agent:"codex",logger:this.deps.logger,lock:s,root:this.deps.root}),this.ownership=await this.sessionMap.claimOwnership(e,t);let r=new _.ToolActivityOutbox({agent:"codex",logger:this.deps.logger,root:this.deps.root});this.ledger=new _.ToolActivityLedger({agent:"codex",sessionId:t,logger:this.deps.logger,root:this.deps.root}),this.consolidator=new _.ToolActivityConsolidator({agent:"codex",sessionId:t,logger:this.deps.logger,outbox:r,ledger:this.ledger,transport:this.buildTransport()}),await this.consolidator.init(),i&&this.bindTranscript(i),this.deps.logger.info("[tool-activity] codex consolidator started",{sessionId:t,rollout:e,hasTranscript:!!i})}bindTranscript(t){if(!this.consolidator||!t||this.transcriptPath===t)return;this.transcriptPath=t;let e=new _.ToolReplayCursor({agent:"codex",sessionId:this.backendSessionId,canonicalPath:t,logger:this.deps.logger,root:this.deps.root,readBytes:this.deps.replayReadBytes,maxFrameBytes:this.deps.replayMaxFrameBytes});this.cursor=e,this.cursorRepaired=!1,this.cursorReady=e.load().catch(s=>this.deps.logger.warn("[tool-activity] codex cursor load failed (rescanning fresh)",{sessionId:this.backendSessionId,err:String(s)}));let i=this.deps.backstopIntervalMs??3e3;i>0&&!this.backstopTimer&&(this.backstopTimer=setInterval(()=>{this.runTranscriptBackstop().catch(s=>this.deps.logger.warn("[tool-activity] codex transcript backstop failed",{sessionId:this.backendSessionId,err:String(s)}))},i),this.backstopTimer.unref?.())}async observeCall(t,e){if(!this.consolidator||!this.sessionMap||!this.ownership||!t.callId||!t.toolName)return;let i=this.consolidator;await this.sessionMap.bindCall(this.ownership,t.callId);let s={id:t.callId,tool:t.toolName,normalizedTarget:ei(t.toolName,t.toolInput),ts:t.ts??new Date().toISOString(),byteOffset:t.byteOffset??0,...t.toolInput!==void 0?{digest:(0,_.digestOf)(t.toolInput)}:{}},r=await i.observe(s,e);return r==="closed"?await i.captureTerminal(s,this.deps.shutdownDrainMs??Xe)?"sealed-terminal":"closed":r}async runTranscriptBackstop(t=!1){await this.runTranscriptBackstopTurn(t)}runTranscriptBackstopTurn(t=!1){if(this.backstopInFlight)return this.backstopInFlight;let e=this.cursor,i=this.cursorReady,s=this.consolidator;if(!s||!e)return Promise.resolve("caught-up");let n=(async()=>{if(await i,this.cursor!==e)return"more";if(!this.cursorRepaired){if(await s.repairLostWindows(e),this.cursor!==e)return"more";this.cursorRepaired=!0}return this.doTranscriptBackstop(e,t)})().finally(()=>{this.backstopInFlight===n&&(this.backstopInFlight=null)});return this.backstopInFlight=n,n}async doTranscriptBackstop(t,e=!1){let i=this.consolidator;if(!i)return"caught-up";this.ledger&&await this.ledger.reloadLegacyOwned();for(let s=0;s<Gt;s+=1){if(this.cursor!==t)return"caught-up";let r=await t.readFrames();if(r.missing)return"caught-up";if(r.frames.length===0)return r.carryover.length>0?Qe(r):"caught-up";let n=Number.POSITIVE_INFINITY,l=e?{includeInFlight:!0}:void 0;for(let p of r.frames)for(let c of Yt(p.line,p.startOffset))await this.observeCall(c,l)==="deferred-in-flight"&&(n=Math.min(n,p.startOffset));if(await i.flush(),n<r.endOfComplete)return await t.checkpoint(n,{fileIdentity:r.fileIdentity,size:r.size,carryover:""}),"more";if(await t.checkpoint(r.endOfComplete,{fileIdentity:r.fileIdentity,size:r.size,carryover:r.carryover}),!r.hasMore)return r.carryover.length>0?Qe(r):"caught-up"}return"more"}async flush(){await this.consolidator?.flush()}async stop(){this.backstopTimer&&(clearInterval(this.backstopTimer),this.backstopTimer=null),await this.runTranscriptBackstop().catch(()=>{});let t=Math.max(1,Math.trunc(this.deps.shutdownPartialNoProgressTurns??Xt)),e=null,i=0;for(;;){let s=await this.runTranscriptBackstopTurn(!0);if(s==="caught-up")break;if(typeof s!="string"){if(s.progressKey===e?i+=1:(e=s.progressKey,i=1),i>=t){this.deps.logger.warn("[tool-activity] deferring unchanged incomplete rollout suffix during shutdown; the next owner will re-read it from the durable complete-line boundary",{size:s.size,carryoverBytes:s.carryoverBytes,endOfComplete:s.endOfComplete,unchangedTurns:i});break}await new Promise(r=>setTimeout(r,Vt))}else e=null,i=0,await new Promise(r=>setImmediate(r))}await this.consolidator?.drainSends(this.deps.shutdownDrainMs??Xe).catch(()=>{});try{await this.consolidator?.shutdown()}finally{await this.ownership?.release().catch(()=>{}),this.ownership=null,this.consolidator=null,this.ledger=null,this.sessionMap=null,this.cursor=null}}buildTransport(){let t=async(e,i,s)=>{let r=s?s.sessionKey:this.deps.getSessionKey();if(!r)throw new Error("[tool-activity] no session key at send (retryable)");let n=s?s.sessionKeyGen:this.deps.getSessionKeyGen();await this.deps.createEvent({sessionId:e,type:_.EventType.TOOL_ACTIVITY,source:_.EventSource.DESKTOP,content:this.deps.encryptContent(Qt,r),metadata:{encrypted:this.deps.encryptMetadata({manifest:i.manifest},r)},timestamp:i.windowStart,isEncrypted:!0,clientEventId:i.clientEventId,...n?{expectedSessionKeyGen:n}:{}})};return{send:async(e,i)=>{try{await t(e,i)}catch(s){if((0,_.isSessionKeyStaleError)(s)&&this.deps.refreshSessionKey){let r=await this.deps.refreshSessionKey(e);if(!r)throw s;await t(e,i,r);return}throw s}}}}};function Yt(a,t){let e;try{e=JSON.parse(a)}catch{return[]}if(e?.type!=="response_item"||!e.payload)return[];let i=e.payload,s=typeof e.timestamp=="string"?e.timestamp:new Date(0).toISOString();if(i.type==="function_call"&&typeof i.call_id=="string"&&typeof i.name=="string"){let r;try{r=JSON.parse(i.arguments||"{}")}catch{r={raw:i.arguments}}return[{callId:i.call_id,toolName:Jt(i.name),toolInput:r,ts:s,byteOffset:t}]}if(i.type==="custom_tool_call"&&typeof i.call_id=="string"){let r=Zt(typeof i.input=="string"?i.input:""),n=r?{file_path:r,patch:i.input}:{patch:i.input};return[{callId:i.call_id,toolName:"Edit",toolInput:n,ts:s,byteOffset:t}]}return[]}function Jt(a){return a==="shell"||a==="shell_command"||a==="local_shell"?"Bash":a==="apply_patch"?"Edit":a}function Zt(a){let t=a.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/m);return t?t[1].trim():void 0}function ei(a,t){if(!t||typeof t!="object")return;let e=t,i=r=>typeof r=="string"&&r.length>0?r:void 0,s=Array.isArray(e.command)?e.command.filter(r=>typeof r=="string").join(" ").trim().split(/\s+/)[0]:typeof e.command=="string"?e.command.trim().split(/\s+/)[0]:void 0;return i(e.file_path)??i(e.path)??i(e.pattern)??i(e.url)??(s&&s.length>0?s:void 0)}var Ye=require("events"),Je=require("@quantiya/codevibe-core");var oe=class a extends Ye.EventEmitter{constructor(){super();this.pendingCalls=new Map;this.timers=new Map;this.timeoutMs=(0,Je.getConfig)().codex.approvalTimeoutMs,o.info("Approval detector initialized",{timeoutMs:this.timeoutMs})}onToolCallStart(e,i,s){o.debug("Tool call started",{callId:e,name:i});let r=this.parseInput(s),n=this.extractFilePath(i,s,r),l=this.extractDiff(i,s,r),p={callId:e,name:i,input:s,filePath:n,diff:l,parsedInput:r,timestamp:Date.now(),notificationSent:!1};if(this.pendingCalls.set(e,p),!this.shouldScheduleApprovalTimeout(i,r)){o.debug("Skipping approval timeout for non-escalated tool call",{callId:e,name:i});return}let c=setTimeout(()=>{this.checkPendingCall(e)},this.timeoutMs);this.timers.set(e,c)}onToolCallComplete(e){o.debug("Tool call completed",{callId:e}),this.pendingCalls.delete(e);let i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e))}checkPendingCall(e){let i=this.pendingCalls.get(e);if(!i||i.notificationSent)return;let s=Date.now()-i.timestamp;o.info("Tool call still pending after timeout",{callId:e,name:i.name,elapsedMs:s}),i.notificationSent=!0,this.pendingCalls.set(e,i),this.emit("approval-pending",{callId:e,toolName:i.name,hint:this.extractHint(i.name,i.input,i.filePath),filePath:i.filePath,diff:i.diff,toolInput:i.parsedInput,rawInput:i.input,elapsedMs:s})}extractHint(e,i,s){if(s)return`File: ${s}`;if(e==="apply_patch"&&i){let r=i.match(/\*\*\* (?:Update|Add|Delete) File: (.+)/);if(r)return`File: ${r[1].trim()}`}if(a.SHELL_TOOL_NAMES.has(e))try{let r=JSON.parse(i),n=typeof r.command=="string"?r.command:typeof r.cmd=="string"?r.cmd:void 0;if(n)return`Command: ${n.substring(0,50)}${n.length>50?"...":""}`}catch{}return`Tool: ${this.mapToolName(e)}`}mapToolName(e){return{exec_command:"Bash",shell_command:"Bash",shell:"Bash",exec:"Bash",local_shell:"Bash",apply_patch:"File Edit",write_file:"Write File",read_file:"Read File"}[e]||e}parseInput(e){if(e)try{return JSON.parse(e)}catch{return}}static{this.SHELL_TOOL_NAMES=new Set(["exec_command","shell_command","shell","exec","local_shell"])}shouldScheduleApprovalTimeout(e,i){return a.SHELL_TOOL_NAMES.has(e)?i?.sandbox_permissions==="require_escalated":!1}extractFilePath(e,i,s){if(e==="apply_patch"&&i){let n=i.match(/\*\*\* (?:Update|Add|Delete) File: (.+)/);if(n)return n[1].trim()}let r=s?.file_path||s?.path||s?.filePath;if(r&&typeof r=="string")return r}extractDiff(e,i,s){if(e==="apply_patch"&&i)return i;if(s?.diff&&typeof s.diff=="string")return s.diff}getPendingCalls(){return Array.from(this.pendingCalls.values())}hasPendingCalls(){return this.pendingCalls.size>0}clear(){for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),this.pendingCalls.clear(),o.debug("Approval detector cleared")}shutdown(){this.clear(),this.removeAllListeners(),o.info("Approval detector shutdown")}};var Ze=require("child_process"),et=require("util");var Te=(0,et.promisify)(Ze.exec),B="__CODEVIBE_CODEX_KEY_ESCAPE__",ae=class{async sendLiteral(t,e){return this.sendTerminalOperation(t,async i=>{await this.sendLiteralViaTmux(i,e)})}async sendKey(t,e){return this.sendTerminalOperation(t,async i=>{await this.sendKeyViaTmux(i,e)})}async sendInput(t,e){o.info("Attempting to send input to Codex",{sessionId:t,input:e});try{let i=process.env.CODEVIBE_CODEX_TMUX_SESSION;return i?(o.info("Using tmux send-keys",{tmuxSession:i}),e===B?await this.sendKeyViaTmux(i,"Escape"):await this.sendViaTmux(i,e),o.info("Successfully sent input to Codex",{sessionId:t,input:e}),!0):(o.error("No tmux session found - CodeVibe Companion launch is required",{sessionId:t,hint:"Start Codex CLI using `codevibe --agent codex`"}),!1)}catch(i){return o.error("Failed to send input to Codex",{sessionId:t,error:i instanceof Error?i.message:String(i)}),!1}}async sendTerminalOperation(t,e){let i=process.env.CODEVIBE_CODEX_TMUX_SESSION;if(!i)return o.error("No tmux session found - CodeVibe Companion launch is required",{sessionId:t,hint:"Start Codex CLI using `codevibe --agent codex`"}),!1;try{return await e(i),!0}catch(s){return o.error("Failed to send terminal input to Codex",{sessionId:t,error:s instanceof Error?s.message:String(s)}),!1}}async sendViaTmux(t,e){o.info("Sending via tmux",{sessionName:t,inputLength:e.length});try{await this.sendLiteralViaTmux(t,e),await this.delay(500);let i=`tmux send-keys -t "${t}" Enter`;await Te(i),o.info("tmux send-keys completed")}catch(i){throw o.error("tmux send-keys failed",{sessionName:t,error:i}),i}}async sendLiteralViaTmux(t,e){let i=e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\$/g,"\\$").replace(/`/g,"\\`"),s=`tmux send-keys -t "${t}" -l "${i}"`;await Te(s)}async sendKeyViaTmux(t,e){o.info("Sending special key via tmux",{sessionName:t,key:e});try{let i=`tmux send-keys -t "${t}" ${e}`;await Te(i),o.info("tmux special-key send completed")}catch(i){throw o.error("tmux special-key send failed",{sessionName:t,key:e,error:i}),i}}delay(t){return new Promise(e=>setTimeout(e,t))}isApprovalResponse(t){let e=t.trim().toLowerCase();return["y","n","a","q","e","yes","no"].includes(e)||/^[0-9]+$/.test(e)}};var g=E(require("fs")),pe=E(require("os")),k=E(require("path")),J=require("crypto"),it=require("child_process"),st=require("events"),rt=require("util");var xe=(0,rt.promisify)(it.execFile),tt=512*1024,z="writer.state",ti=2e3,ii=10,si=16*1024*1024,ri=10080*60*1e3,le=class extends st.EventEmitter{constructor(e={}){super();this.sessionName=null;this.started=!1;this.pipeDirPath=null;this.pipeDirIdentity=null;this.pipeFilePath=null;this.pipeFileFd=null;this.pipeFileIdentity=null;this.writerStatePath=null;this.writerStateFd=null;this.writerStateIdentity=null;this.writerGeneration=null;this.pipeEnabled=!1;this.filePosition=0;this.pollTimer=null;this.processing=!1;this.pendingRead=!1;this.promptInspectionChain=Promise.resolve();this.lastPromptHash=null;this.lifecycleGeneration=0;this.retainedMirrors=[];this.retainedMirrorContainmentLost=!1;this.maxPaneFileBytes=e.maxPaneFileBytes??si,this.stalePaneRetentionMs=e.stalePaneRetentionMs??ri,this.tempDir=e.tempDir??pe.tmpdir(),this.writerPath=k.resolve(__dirname,"..","libexec","pane-mirror-writer.js");let i=e.runtimePlatform??process.platform,s=e.runtimeRelease??pe.release();if(this.wslDetected=i==="linux"&&(/microsoft/i.test(s)||!!process.env.WSL_DISTRO_NAME||!!process.env.WSL_INTEROP),!Number.isSafeInteger(this.maxPaneFileBytes)||this.maxPaneFileBytes<=0)throw new RangeError("maxPaneFileBytes must be a positive safe integer");if(!Number.isSafeInteger(this.stalePaneRetentionMs)||this.stalePaneRetentionMs<0)throw new RangeError("stalePaneRetentionMs must be a non-negative safe integer")}async start(e){if(this.wslDetected)throw new Error("Tmux pane mirror fallback is disabled on unqualified WSL; using hook-based prompts only");if(this.started&&this.sessionName===e&&this.pipeEnabled){o.debug("Tmux pane observer already started",{sessionName:e});return}this.started&&await this.stop(),this.lifecycleGeneration++,this.sessionName=e,this.filePosition=0,this.lastPromptHash=null;try{if(this.cleanupStalePipeFiles(),this.reapOwnedRetainedMirrors(),this.retainedMirrorContainmentLost||this.retainedMirrors.length>0)throw new Error("Prior bounded tmux pane writer is still unconfirmed; refusing another mirror");this.createSecureMirror(),this.started=!0,await this.enablePipePane(),this.pipeEnabled=!0,this.startPolling()}catch(i){this.started=!1,this.lifecycleGeneration++;let s=this.writerGeneration===null;if(!s)try{await this.disablePipePane(),s=!0}catch(r){o.error("Failed to confirm tmux pane writer quiescence after startup failure",{disableError:r})}throw s&&(this.pipeEnabled=!1),this.clearPolling(),s?this.releaseSecureMirror():this.abandonSecureMirror(),this.resetLifecycleState(),i}o.info("Tmux pane observer started",{sessionName:e})}async stop(){if(!this.started&&this.pipeFileFd===null)return;this.started=!1,this.lifecycleGeneration++;let e=!1;try{await this.disablePipePane(),e=!0}catch(i){o.error("Failed to confirm tmux pane writer quiescence during stop",{error:i}),this.emit("observer-error",i)}e&&(this.pipeEnabled=!1),this.clearPolling(),e?this.releaseSecureMirror():this.abandonSecureMirror(),o.info("Tmux pane observer stopped",{sessionName:this.sessionName,writerQuiesced:e}),this.resetLifecycleState()}identityOf(e){return{dev:e.dev,ino:e.ino}}identitiesEqual(e,i){return e.dev===i.dev&&e.ino===i.ino}openMirrorFile(e){return g.openSync(e,g.constants.O_RDWR|g.constants.O_CREAT|g.constants.O_EXCL|(g.constants.O_NOFOLLOW??0),384)}truncateMirror(e){g.ftruncateSync(e,0)}createSecureMirror(){g.mkdirSync(this.tempDir,{recursive:!0,mode:448});let e=g.mkdtempSync(k.join(this.tempDir,`codevibe-codex-pane-${process.pid}-`));g.chmodSync(e,448);let i=k.join(e,"pane.log"),s=k.join(e,z),r=null,n=null;try{r=this.openMirrorFile(i),g.fchmodSync(r,384);let l=g.fstatSync(r);if(!l.isFile())throw new Error("Tmux pane mirror is not a regular file");n=this.openMirrorFile(s),g.fchmodSync(n,384);let p=g.fstatSync(n);if(!p.isFile())throw new Error("Tmux pane writer state is not a regular file");g.writeSync(n,Buffer.from(`idle
13
- `),0,5,0),this.pipeDirPath=e,this.pipeDirIdentity=this.identityOf(g.lstatSync(e)),this.pipeFilePath=i,this.pipeFileFd=r,this.pipeFileIdentity=this.identityOf(l),this.writerStatePath=s,this.writerStateFd=n,this.writerStateIdentity=this.identityOf(p),r=null,n=null}catch(l){if(r!==null)try{g.closeSync(r)}catch{}if(n!==null)try{g.closeSync(n)}catch{}try{g.unlinkSync(i)}catch{}try{g.unlinkSync(s)}catch{}try{g.rmdirSync(e)}catch{}throw l}}pathMatchesMirrorIdentity(){if(!this.pipeFilePath||!this.pipeFileIdentity)return!1;try{let e=g.lstatSync(this.pipeFilePath);return e.isFile()&&!e.isSymbolicLink()&&this.identitiesEqual(this.pipeFileIdentity,this.identityOf(e))}catch{return!1}}quarantineIfIdentityMatches(e,i){let s=`${e}.reap-${process.pid}-${(0,J.randomBytes)(6).toString("hex")}`;try{g.renameSync(e,s);let r=g.lstatSync(s);return this.identitiesEqual(i,this.identityOf(r))?s:(g.existsSync(e)||g.renameSync(s,e),null)}catch{return null}}restoreQuarantine(e,i){try{g.existsSync(i)||g.renameSync(e,i)}catch{}}pathMatchesIdentity(e,i){if(!e||!i)return!1;try{let s=g.lstatSync(e);return s.isFile()&&!s.isSymbolicLink()&&this.identitiesEqual(i,this.identityOf(s))}catch{return!1}}closeOwnedDescriptors(){if(this.pipeFileFd!==null)try{g.closeSync(this.pipeFileFd)}catch{}if(this.writerStateFd!==null)try{g.closeSync(this.writerStateFd)}catch{}this.pipeFileFd=null,this.writerStateFd=null}unlinkOwnedFile(e,i){if(!e||!i||!this.pathMatchesIdentity(e,i))return;let s=this.quarantineIfIdentityMatches(e,i);s&&g.unlinkSync(s)}releaseSecureMirror(){let e=this.pipeFilePath,i=this.writerStatePath,s=this.pipeDirPath,r=this.pipeFileIdentity,n=this.writerStateIdentity;this.closeOwnedDescriptors();try{this.unlinkOwnedFile(e,r)}catch{}try{this.unlinkOwnedFile(i,n)}catch{}if(s)try{g.rmdirSync(s)}catch{}this.pipeDirPath=null,this.pipeDirIdentity=null,this.pipeFilePath=null,this.pipeFileIdentity=null,this.writerStatePath=null,this.writerStateIdentity=null,this.writerGeneration=null}abandonSecureMirror(){let e=this.pipeDirPath,i=this.pipeFilePath;e&&this.pipeDirIdentity&&i&&this.pipeFileIdentity&&this.writerStatePath&&this.writerStateIdentity&&this.writerGeneration?this.retainedMirrors.push({dirPath:e,dirIdentity:this.pipeDirIdentity,panePath:i,paneIdentity:this.pipeFileIdentity,statePath:this.writerStatePath,stateIdentity:this.writerStateIdentity,writerGeneration:this.writerGeneration}):(e||i)&&(this.retainedMirrorContainmentLost=!0),this.closeOwnedDescriptors(),o.warn("Retaining bounded tmux pane mirror because writer quiescence is unconfirmed",{retainedDir:e,retainedFile:i,maxBytes:this.maxPaneFileBytes}),this.pipeDirPath=null,this.pipeDirIdentity=null,this.pipeFilePath=null,this.pipeFileIdentity=null,this.writerStatePath=null,this.writerStateIdentity=null,this.writerGeneration=null}resetLifecycleState(){this.sessionName=null,this.pipeDirPath=null,this.pipeDirIdentity=null,this.pipeFilePath=null,this.pipeFileFd=null,this.pipeFileIdentity=null,this.writerStatePath=null,this.writerStateFd=null,this.writerStateIdentity=null,this.writerGeneration=null,this.pipeEnabled=!1,this.filePosition=0,this.processing=!1,this.pendingRead=!1,this.lastPromptHash=null}async failClosedPipe(e){let i=!1;try{await this.disablePipePane(),i=!0}catch(s){o.error("Failed to confirm unsafe tmux pane writer quiescence",{disableError:s})}i&&(this.pipeEnabled=!1),this.clearPolling(),o.error("Tmux pane mirror entered fail-closed containment after integrity/cap failure",{error:e,writerQuiesced:i,independentlyBounded:!0}),this.emit("observer-error",e)}resetLastPromptHash(){this.lastPromptHash=null}async captureSnapshot(e=120){if(!this.sessionName)throw new Error("Tmux pane observer is not started");let i=Math.max(1,Math.floor(e));try{let{stdout:s}=await xe("tmux",["capture-pane","-p","-e","-J","-S",`-${i}`,"-t",this.sessionName],{timeout:5e3,maxBuffer:2097152});return s}catch(s){throw o.error("Failed to capture tmux pane snapshot",{sessionName:this.sessionName,error:s}),this.emit("observer-error",s),s}}quoteShellArg(e){return`'${e.replace(/'/g,"'\\''")}'`}writerStatePathMatchesIdentity(){return this.pathMatchesIdentity(this.writerStatePath,this.writerStateIdentity)}writeWriterState(e){if(this.writerStateFd===null||!this.writerStateIdentity||!this.writerStatePathMatchesIdentity())throw new Error("Tmux pane writer state identity is not valid");let i=g.fstatSync(this.writerStateFd);if(!this.identitiesEqual(this.writerStateIdentity,this.identityOf(i)))throw new Error("Tmux pane writer state descriptor identity changed");let s=Buffer.from(e,"utf8");g.ftruncateSync(this.writerStateFd,0);let r=0;for(;r<s.length;){let n=g.writeSync(this.writerStateFd,s,r,s.length-r,r);if(n<=0)throw new Error("Short tmux pane writer-state write");r+=n}g.fsyncSync(this.writerStateFd)}readWriterState(){if(this.writerStateFd===null||!this.writerStateIdentity||!this.writerStatePathMatchesIdentity())throw new Error("Tmux pane writer state identity is not valid");let e=g.fstatSync(this.writerStateFd);if(!this.identitiesEqual(this.writerStateIdentity,this.identityOf(e)))throw new Error("Tmux pane writer state descriptor identity changed");let i=Math.min(e.size,512);if(i===0)return"";let s=Buffer.alloc(i),r=g.readSync(this.writerStateFd,s,0,i,0);return s.subarray(0,r).toString("utf8").trim()}async waitForWriterState(e,i,s=ti){let r=Date.now()+s;do{let n=this.readWriterState();if(i.some(l=>n.startsWith(`${l} ${e} `)))return n;if(Date.now()>=r)return null;await new Promise(l=>setTimeout(l,ii))}while(!0)}async enablePipePane(){if(!this.sessionName||!this.pipeFilePath||!this.writerStatePath)throw new Error("Tmux pane observer is not initialized");if(this.pipeFileFd===null||!this.pipeFileIdentity||!this.pathMatchesMirrorIdentity()||this.writerStateFd===null||!this.writerStateIdentity||!this.writerStatePathMatchesIdentity())throw new Error("Tmux pane mirror identity is not valid");let e=(0,J.randomBytes)(16).toString("hex");this.writerGeneration=e,this.writeWriterState(`starting ${e}
14
- `);let i=[process.execPath,this.writerPath,this.pipeFilePath,String(this.pipeFileIdentity.dev),String(this.pipeFileIdentity.ino),this.writerStatePath,String(this.writerStateIdentity.dev),String(this.writerStateIdentity.ino),e,String(this.maxPaneFileBytes)].map(r=>this.quoteShellArg(r)).join(" ");try{await xe("tmux",["pipe-pane","-O","-t",this.sessionName,i])}catch(r){throw this.writerGeneration=null,r}let s=await this.waitForWriterState(e,["ready","done"]);if(!s)throw new Error("Tmux pane writer did not confirm startup");if(s.startsWith(`done ${e} `))throw new Error("Tmux pane writer terminated during startup");o.debug("Enabled tmux pipe-pane mirroring",{sessionName:this.sessionName,pipeFilePath:this.pipeFilePath,writerGeneration:e})}async disablePipePane(){let e=this.writerGeneration;if(!this.sessionName||!e)return;let i=null;try{await xe("tmux",["pipe-pane","-t",this.sessionName])}catch(n){i=n}if(!e){if(i)throw i;return}if(await this.waitForWriterState(e,["done"])){this.writerGeneration=null;return}let r=i instanceof Error?`: ${i.message}`:"";throw new Error(`Tmux pane writer quiescence was not confirmed${r}`)}startPolling(){this.clearPolling(),this.pollTimer=setInterval(()=>{this.processFileChanges()},100),this.pollTimer.unref?.()}clearPolling(){this.pollTimer&&clearInterval(this.pollTimer),this.pollTimer=null}async processFileChanges(){if(!(!this.started||!this.pipeFilePath)){if(this.processing){this.pendingRead=!0;return}this.processing=!0;try{do{this.pendingRead=!1;let e="";try{e=this.readAppendedChunk()}catch(i){o.error("Failed to read bounded tmux pane mirror",{error:i}),await this.failClosedPipe(i);return}await this.rotatePipeFileIfNeeded(),e&&this.queuePromptInspection(e,this.lifecycleGeneration)}while(this.pendingRead)}catch(e){o.error("Failed to process tmux pane changes",{error:e}),this.emit("observer-error",e)}finally{this.processing=!1}}}queuePromptInspection(e,i){this.promptInspectionChain=this.promptInspectionChain.then(async()=>{if(!(!this.started||this.lifecycleGeneration!==i))try{await this.inspectChunkForPrompt(e)}catch(s){o.error("Failed to inspect tmux pane delta",{error:s}),this.emit("observer-error",s)}})}async inspectChunkForPrompt(e){if(!this.looksLikePromptDelta(e))return;let i=await this.captureSnapshot();this.inspectSnapshotForPrompt(i,e)}inspectSnapshotForPrompt(e,i){let s=e?this.extractActiveApprovalBlock(e):null;if(!s){e&&o.debug("tmux delta cue matched but no active dialog isolated (no candidate emitted)",{snapshotTail:e.split(`
15
- `).slice(-6).join("\\n")});return}let r=this.hashPromptSnapshot(s);r!==this.lastPromptHash&&(this.lastPromptHash=r,this.emit("prompt-candidate",{rawDelta:i,snapshot:s,detectedAt:Date.now()}))}queueCurrentSnapshotInspection(e){this.promptInspectionChain=this.promptInspectionChain.then(async()=>{if(!(!this.started||this.lifecycleGeneration!==e))try{let i=await this.captureSnapshot();this.inspectSnapshotForPrompt(i,"")}catch(i){o.error("Failed to inspect tmux pane snapshot after rotation",{error:i}),this.emit("observer-error",i)}})}async rotatePipeFileIfNeeded(){let e=this.pipeFilePath,i=this.pipeFileFd,s=this.pipeFileIdentity,r=this.sessionName,n=this.lifecycleGeneration;if(!e||i===null||!s||!r||!this.started)return;let l;try{let c=g.fstatSync(i);if(!this.identitiesEqual(s,this.identityOf(c)))throw new Error("Tmux pane mirror descriptor identity changed");l=c.size}catch(c){await this.failClosedPipe(c);return}if(l<this.maxPaneFileBytes)return;let p="";try{await this.disablePipePane(),this.pipeEnabled=!1}catch(c){o.error("Tmux pane mirror rotation deferred because writer quiescence is unconfirmed",{error:c}),this.emit("observer-error",c),this.clearPolling();return}if(!(!this.started||this.lifecycleGeneration!==n||this.pipeFilePath!==e)){try{p=this.readAppendedChunk()}catch(c){o.error("Failed to drain final tmux pane suffix during rotation; mirror remains intact and disabled",{error:c}),this.emit("observer-error",c),this.clearPolling();return}try{this.truncateMirror(i);let c=g.fstatSync(i);if(!this.identitiesEqual(s,this.identityOf(c))||c.size!==0)throw new Error("Tmux pane mirror truncation could not be verified");if(!this.pathMatchesMirrorIdentity())throw new Error("Tmux pane mirror path was replaced during rotation")}catch(c){o.error("Tmux pane mirror remains disabled after truncation verification failure",{error:c}),this.emit("observer-error",c),this.clearPolling();return}if(this.filePosition=0,o.info("Rotated bounded tmux pane mirror",{sessionName:r,priorBytes:l,maxBytes:this.maxPaneFileBytes}),this.started&&this.lifecycleGeneration===n&&this.pipeFilePath===e&&this.sessionName===r)try{await this.enablePipePane(),this.pipeEnabled=!0}catch(c){this.pipeEnabled=!1,o.error("Tmux pane mirror remains disabled after re-enable failure",{error:c}),this.emit("observer-error",c),this.clearPolling()}p&&this.queuePromptInspection(p,n),this.pipeEnabled&&this.queueCurrentSnapshotInspection(n)}}retainedPathMatchesIdentity(e,i,s){try{let r=g.lstatSync(e);return(s==="file"?r.isFile():r.isDirectory())&&!r.isSymbolicLink()&&(typeof process.getuid!="function"||r.uid===process.getuid())&&this.identitiesEqual(i,this.identityOf(r))}catch{return!1}}readRetainedWriterState(e,i){let s=null;try{s=g.openSync(e,g.constants.O_RDONLY|(g.constants.O_NOFOLLOW??0));let r=g.fstatSync(s);if(!r.isFile()||typeof process.getuid=="function"&&r.uid!==process.getuid()||!this.identitiesEqual(i,this.identityOf(r))||r.size>512)return null;let n=Buffer.alloc(r.size),l=r.size===0?0:g.readSync(s,n,0,r.size,0);return n.subarray(0,l).toString("utf8").trim()}catch{return null}finally{if(s!==null)try{g.closeSync(s)}catch{}}}reapOwnedRetainedMirror(e){if(!this.retainedPathMatchesIdentity(e.dirPath,e.dirIdentity,"directory"))return!1;let i;try{i=g.readdirSync(e.dirPath).sort()}catch{return!1}if(i.length!==2||i[0]!=="pane.log"||i[1]!==z||!this.retainedPathMatchesIdentity(e.panePath,e.paneIdentity,"file")||!this.retainedPathMatchesIdentity(e.statePath,e.stateIdentity,"file"))return!1;let s=new RegExp(`^done ${e.writerGeneration} \\d+ \\d+$`);if(!s.test(this.readRetainedWriterState(e.statePath,e.stateIdentity)??""))return!1;let r=this.quarantineIfIdentityMatches(e.dirPath,e.dirIdentity);if(!r)return!1;let n=!1;try{let l=k.join(r,"pane.log"),p=k.join(r,z);if(!this.retainedPathMatchesIdentity(l,e.paneIdentity,"file"))throw new Error("pane identity changed");if(!this.retainedPathMatchesIdentity(p,e.stateIdentity,"file"))throw new Error("state identity changed");if(!s.test(this.readRetainedWriterState(p,e.stateIdentity)??""))throw new Error("writer generation is no longer done");if(this.unlinkOwnedFile(l,e.paneIdentity),this.unlinkOwnedFile(p,e.stateIdentity),g.existsSync(l)||g.existsSync(p))throw new Error("retained mirror unlink was incomplete");g.rmdirSync(r),n=!0,o.info("Removed completed same-owner tmux pane mirror",{retainedDir:e.dirPath})}catch{g.existsSync(r)&&this.restoreQuarantine(r,e.dirPath)}return n}reapOwnedRetainedMirrors(){this.retainedMirrors=this.retainedMirrors.filter(e=>!this.reapOwnedRetainedMirror(e))}cleanupStalePipeFiles(){let e=Date.now()-this.stalePaneRetentionMs,i;try{i=g.readdirSync(this.tempDir,{withFileTypes:!0})}catch{return}for(let s of i){let r=/^codevibe-codex-pane-(\d+)\.log$/.exec(s.name);if(r){let c=k.join(this.tempDir,s.name),d=null;try{let u=g.lstatSync(c);if(!u.isFile()||u.isSymbolicLink()||u.mtimeMs>e||typeof process.getuid=="function"&&u.uid!==process.getuid()||this.isProcessAlive(Number(r[1]))||(d=this.quarantineIfIdentityMatches(c,this.identityOf(u)),!d))continue;g.unlinkSync(d),d=null,o.info("Removed stale legacy tmux pane mirror",{candidate:c,ageMs:Date.now()-u.mtimeMs})}catch{d&&this.restoreQuarantine(d,c)}continue}let n=/^codevibe-codex-pane-(\d+)-[A-Za-z0-9_-]+$/.exec(s.name);if(!n||!s.isDirectory())continue;let l=k.join(this.tempDir,s.name),p=null;try{let c=g.lstatSync(l);if(!c.isDirectory()||c.isSymbolicLink()||c.mtimeMs>e||typeof process.getuid=="function"&&c.uid!==process.getuid())continue;let d=Number(n[1]);if(this.isProcessAlive(d)||(p=this.quarantineIfIdentityMatches(l,this.identityOf(c)),!p))continue;let u=k.join(p,"pane.log"),m=k.join(p,z),y=g.readdirSync(p);if(y.some(f=>f!=="pane.log"&&f!==z)){this.restoreQuarantine(p,l);continue}for(let f of y.map(v=>k.join(p,v))){let v=g.lstatSync(f);if(!v.isFile()||v.isSymbolicLink()||typeof process.getuid=="function"&&v.uid!==process.getuid()){this.restoreQuarantine(p,l),p=null;break}}if(!p)continue;if(y.includes(z)){let f=g.readFileSync(m,"utf8").trim(),v=/^ready [a-f0-9]{32} (\d+)$/.exec(f);if(!(f==="idle"||/^done [a-f0-9]{32} \d+ \d+$/.test(f)||!!v)||v&&this.isProcessAlive(Number(v[1]))){this.restoreQuarantine(p,l);continue}}y.includes("pane.log")&&g.unlinkSync(u),y.includes(z)&&g.unlinkSync(m),g.rmdirSync(p),p=null,o.info("Removed stale tmux pane mirror directory",{candidate:l,ageMs:Date.now()-c.mtimeMs})}catch{p&&this.restoreQuarantine(p,l)}}}isProcessAlive(e){try{return process.kill(e,0),!0}catch(i){return i?.code==="EPERM"}}readAppendedChunk(){if(!this.pipeFilePath||this.pipeFileFd===null||!this.pipeFileIdentity)return"";if(!this.pathMatchesMirrorIdentity())throw new Error("Tmux pane mirror path was replaced");let e=g.fstatSync(this.pipeFileFd);if(!this.identitiesEqual(this.pipeFileIdentity,this.identityOf(e)))throw new Error("Tmux pane mirror descriptor identity changed");if(e.size<=this.filePosition)return"";{let s=e.size-this.filePosition>tt?e.size-tt:this.filePosition,r=e.size-s,n=Buffer.alloc(r),l=0;for(;l<r;){let p=g.readSync(this.pipeFileFd,n,l,r-l,s+l);if(p===0)break;l+=p}return this.filePosition=s+l,n.subarray(0,l).toString("utf-8")}}looksLikePromptDelta(e){return/Would you like to run/i.test(e)||/Press enter to confirm/i.test(e)||/\besc to cancel\b/i.test(e)||/\bYes,\s+proceed\b/i.test(e)||/\bNo,\s+and tell Codex\b/i.test(e)||/don't ask again/i.test(e)||/Requesting permission for:|Do you want to proceed\?/i.test(e)||/\[(?:y\/n|Y\/n|y\/N)\]|\b(?:apply|approve|allow|reject|deny|continue)\b/i.test(e)}looksLikePromptSnapshot(e){return this.extractActiveApprovalBlock(e)!==null}extractActiveApprovalBlock(e){let s=e.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g,"").split(`
11
+ `)}}function Qt(a){if(!a)return null;let t=a.match(/\*\*\* (?:Update|Add|Delete) File: (.+)/);return t?{filePath:t[1].trim()}:null}function re(a){if(a==null)return"";if(typeof a=="string")return a;if(Array.isArray(a))return a.map(t=>typeof t?.text=="string"?t.text:t?.type==="input_image"||typeof t?.image_url=="string"?"[image]":JSON.stringify(t??null)).join(`
12
+ `);if(typeof a=="object"&&typeof a.text=="string")return a.text;try{return JSON.stringify(a)??""}catch{return String(a)}}function _e(a,t){return typeof a!="string"?_e(re(a),t):a?a.length<=t?a:a.substring(0,t)+"...":""}function Te(){j.clear(),se()}var _=require("@quantiya/codevibe-core"),Qe=3e3,Jt=8,Zt=10,ei=100;function Je(a){let t=Buffer.byteLength(a.carryover);return{status:"partial",progressKey:`${a.fileIdentity.dev}:${a.fileIdentity.ino}:${a.size}:${a.endOfComplete}:${t}`,size:a.size,carryoverBytes:t,endOfComplete:a.endOfComplete}}var ti="tool_activity",V=class{constructor(t){this.consolidator=null;this.ledger=null;this.sessionMap=null;this.ownership=null;this.cursor=null;this.backendSessionId=null;this.rolloutId=null;this.transcriptPath=null;this.backstopTimer=null;this.backstopInFlight=null;this.cursorReady=Promise.resolve();this.cursorRepaired=!1;this.deps=t}static mintBackendSessionId(){return _.ToolActivitySessionMap.mintCodexBackendSessionId()}async recordedBackendSessionId(t){return new _.ToolActivitySessionMap({agent:"codex",logger:this.deps.logger,lock:new _.InProcessRolloutLock,root:this.deps.root}).ownerBackendSessionId(t)}get sessionId(){return this.backendSessionId}get rollout(){return this.rolloutId}isActive(){return this.consolidator!==null}async start(t,e,i){this.backendSessionId=t,this.rolloutId=e;let s=(0,_.createRolloutLock)({agent:"codex",logger:this.deps.logger,socketDir:this.deps.socketDir,root:this.deps.root});this.sessionMap=new _.ToolActivitySessionMap({agent:"codex",logger:this.deps.logger,lock:s,root:this.deps.root}),this.ownership=await this.sessionMap.claimOwnership(e,t);let r=new _.ToolActivityOutbox({agent:"codex",logger:this.deps.logger,root:this.deps.root});this.ledger=new _.ToolActivityLedger({agent:"codex",sessionId:t,logger:this.deps.logger,root:this.deps.root}),this.consolidator=new _.ToolActivityConsolidator({agent:"codex",sessionId:t,logger:this.deps.logger,outbox:r,ledger:this.ledger,transport:this.buildTransport()}),await this.consolidator.init(),i&&this.bindTranscript(i),this.deps.logger.info("[tool-activity] codex consolidator started",{sessionId:t,rollout:e,hasTranscript:!!i})}bindTranscript(t){if(!this.consolidator||!t||this.transcriptPath===t)return;this.transcriptPath=t;let e=new _.ToolReplayCursor({agent:"codex",sessionId:this.backendSessionId,canonicalPath:t,logger:this.deps.logger,root:this.deps.root,readBytes:this.deps.replayReadBytes,maxFrameBytes:this.deps.replayMaxFrameBytes});this.cursor=e,this.cursorRepaired=!1,this.cursorReady=e.load().catch(s=>this.deps.logger.warn("[tool-activity] codex cursor load failed (rescanning fresh)",{sessionId:this.backendSessionId,err:String(s)}));let i=this.deps.backstopIntervalMs??3e3;i>0&&!this.backstopTimer&&(this.backstopTimer=setInterval(()=>{this.runTranscriptBackstop().catch(s=>this.deps.logger.warn("[tool-activity] codex transcript backstop failed",{sessionId:this.backendSessionId,err:String(s)}))},i),this.backstopTimer.unref?.())}async observeCall(t,e){if(!this.consolidator||!this.sessionMap||!this.ownership||!t.callId||!t.toolName)return;let i=this.consolidator;await this.sessionMap.bindCall(this.ownership,t.callId);let s={id:t.callId,tool:t.toolName,normalizedTarget:ni(t.toolName,t.toolInput),ts:t.ts??new Date().toISOString(),byteOffset:t.byteOffset??0,...t.toolInput!==void 0?{digest:(0,_.digestOf)(t.toolInput)}:{}},r=await i.observe(s,e);return r==="closed"?await i.captureTerminal(s,this.deps.shutdownDrainMs??Qe)?"sealed-terminal":"closed":r}async runTranscriptBackstop(t=!1){await this.runTranscriptBackstopTurn(t)}runTranscriptBackstopTurn(t=!1){if(this.backstopInFlight)return this.backstopInFlight;let e=this.cursor,i=this.cursorReady,s=this.consolidator;if(!s||!e)return Promise.resolve("caught-up");let n=(async()=>{if(await i,this.cursor!==e)return"more";if(!this.cursorRepaired){if(await s.repairLostWindows(e),this.cursor!==e)return"more";this.cursorRepaired=!0}return this.doTranscriptBackstop(e,t)})().finally(()=>{this.backstopInFlight===n&&(this.backstopInFlight=null)});return this.backstopInFlight=n,n}async doTranscriptBackstop(t,e=!1){let i=this.consolidator;if(!i)return"caught-up";this.ledger&&await this.ledger.reloadLegacyOwned();for(let s=0;s<Jt;s+=1){if(this.cursor!==t)return"caught-up";let r=await t.readFrames();if(r.missing)return"caught-up";if(r.frames.length===0)return r.carryover.length>0?Je(r):"caught-up";let n=Number.POSITIVE_INFINITY,l=e?{includeInFlight:!0}:void 0;for(let p of r.frames)for(let c of ii(p.line,p.startOffset))await this.observeCall(c,l)==="deferred-in-flight"&&(n=Math.min(n,p.startOffset));if(await i.flush(),n<r.endOfComplete)return await t.checkpoint(n,{fileIdentity:r.fileIdentity,size:r.size,carryover:""}),"more";if(await t.checkpoint(r.endOfComplete,{fileIdentity:r.fileIdentity,size:r.size,carryover:r.carryover}),!r.hasMore)return r.carryover.length>0?Je(r):"caught-up"}return"more"}async flush(){await this.consolidator?.flush()}async stop(){this.backstopTimer&&(clearInterval(this.backstopTimer),this.backstopTimer=null),await this.runTranscriptBackstop().catch(()=>{});let t=Math.max(1,Math.trunc(this.deps.shutdownPartialNoProgressTurns??ei)),e=null,i=0;for(;;){let s=await this.runTranscriptBackstopTurn(!0);if(s==="caught-up")break;if(typeof s!="string"){if(s.progressKey===e?i+=1:(e=s.progressKey,i=1),i>=t){this.deps.logger.warn("[tool-activity] deferring unchanged incomplete rollout suffix during shutdown; the next owner will re-read it from the durable complete-line boundary",{size:s.size,carryoverBytes:s.carryoverBytes,endOfComplete:s.endOfComplete,unchangedTurns:i});break}await new Promise(r=>setTimeout(r,Zt))}else e=null,i=0,await new Promise(r=>setImmediate(r))}await this.consolidator?.drainSends(this.deps.shutdownDrainMs??Qe).catch(()=>{});try{await this.consolidator?.shutdown()}finally{await this.ownership?.release().catch(()=>{}),this.ownership=null,this.consolidator=null,this.ledger=null,this.sessionMap=null,this.cursor=null}}buildTransport(){let t=async(e,i,s)=>{let r=s?s.sessionKey:this.deps.getSessionKey();if(!r)throw new Error("[tool-activity] no session key at send (retryable)");let n=s?s.sessionKeyGen:this.deps.getSessionKeyGen();await this.deps.createEvent({sessionId:e,type:_.EventType.TOOL_ACTIVITY,source:_.EventSource.DESKTOP,content:this.deps.encryptContent(ti,r),metadata:{encrypted:this.deps.encryptMetadata({manifest:i.manifest},r)},timestamp:i.windowStart,isEncrypted:!0,clientEventId:i.clientEventId,...n?{expectedSessionKeyGen:n}:{}})};return{send:async(e,i)=>{try{await t(e,i)}catch(s){if((0,_.isSessionKeyStaleError)(s)&&this.deps.refreshSessionKey){let r=await this.deps.refreshSessionKey(e);if(!r)throw s;await t(e,i,r);return}throw s}}}}};function ii(a,t){let e;try{e=JSON.parse(a)}catch{return[]}if(e?.type!=="response_item"||!e.payload)return[];let i=e.payload,s=typeof e.timestamp=="string"?e.timestamp:new Date(0).toISOString();if(i.type==="function_call"&&typeof i.call_id=="string"&&typeof i.name=="string"){let r;try{r=JSON.parse(i.arguments||"{}")}catch{r={raw:i.arguments}}return[{callId:i.call_id,toolName:si(i.name),toolInput:r,ts:s,byteOffset:t}]}if(i.type==="custom_tool_call"&&typeof i.call_id=="string"){let r=ri(typeof i.input=="string"?i.input:""),n=r?{file_path:r,patch:i.input}:{patch:i.input};return[{callId:i.call_id,toolName:"Edit",toolInput:n,ts:s,byteOffset:t}]}return[]}function si(a){return a==="shell"||a==="shell_command"||a==="local_shell"?"Bash":a==="apply_patch"?"Edit":a}function ri(a){let t=a.match(/^\*\*\* (?:Update|Add|Delete) File: (.+)$/m);return t?t[1].trim():void 0}function ni(a,t){if(!t||typeof t!="object")return;let e=t,i=r=>typeof r=="string"&&r.length>0?r:void 0,s=Array.isArray(e.command)?e.command.filter(r=>typeof r=="string").join(" ").trim().split(/\s+/)[0]:typeof e.command=="string"?e.command.trim().split(/\s+/)[0]:void 0;return i(e.file_path)??i(e.path)??i(e.pattern)??i(e.url)??(s&&s.length>0?s:void 0)}var Ze=require("events"),et=require("@quantiya/codevibe-core");var oe=class a extends Ze.EventEmitter{constructor(){super();this.pendingCalls=new Map;this.timers=new Map;this.timeoutMs=(0,et.getConfig)().codex.approvalTimeoutMs,o.info("Approval detector initialized",{timeoutMs:this.timeoutMs})}onToolCallStart(e,i,s){o.debug("Tool call started",{callId:e,name:i});let r=this.parseInput(s),n=this.extractFilePath(i,s,r),l=this.extractDiff(i,s,r),p={callId:e,name:i,input:s,filePath:n,diff:l,parsedInput:r,timestamp:Date.now(),notificationSent:!1};if(this.pendingCalls.set(e,p),!this.shouldScheduleApprovalTimeout(i,r)){o.debug("Skipping approval timeout for non-escalated tool call",{callId:e,name:i});return}let c=setTimeout(()=>{this.checkPendingCall(e)},this.timeoutMs);this.timers.set(e,c)}onToolCallComplete(e){o.debug("Tool call completed",{callId:e}),this.pendingCalls.delete(e);let i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e))}checkPendingCall(e){let i=this.pendingCalls.get(e);if(!i||i.notificationSent)return;let s=Date.now()-i.timestamp;o.info("Tool call still pending after timeout",{callId:e,name:i.name,elapsedMs:s}),i.notificationSent=!0,this.pendingCalls.set(e,i),this.emit("approval-pending",{callId:e,toolName:i.name,hint:this.extractHint(i.name,i.input,i.filePath),filePath:i.filePath,diff:i.diff,toolInput:i.parsedInput,rawInput:i.input,elapsedMs:s})}extractHint(e,i,s){if(s)return`File: ${s}`;if(e==="apply_patch"&&i){let r=i.match(/\*\*\* (?:Update|Add|Delete) File: (.+)/);if(r)return`File: ${r[1].trim()}`}if(a.SHELL_TOOL_NAMES.has(e))try{let r=JSON.parse(i),n=typeof r.command=="string"?r.command:typeof r.cmd=="string"?r.cmd:void 0;if(n)return`Command: ${n.substring(0,50)}${n.length>50?"...":""}`}catch{}return`Tool: ${this.mapToolName(e)}`}mapToolName(e){return{exec_command:"Bash",shell_command:"Bash",shell:"Bash",exec:"Bash",local_shell:"Bash",apply_patch:"File Edit",write_file:"Write File",read_file:"Read File"}[e]||e}parseInput(e){if(e)try{return JSON.parse(e)}catch{return}}static{this.SHELL_TOOL_NAMES=new Set(["exec_command","shell_command","shell","exec","local_shell"])}shouldScheduleApprovalTimeout(e,i){return a.SHELL_TOOL_NAMES.has(e)?i?.sandbox_permissions==="require_escalated":!1}extractFilePath(e,i,s){if(e==="apply_patch"&&i){let n=i.match(/\*\*\* (?:Update|Add|Delete) File: (.+)/);if(n)return n[1].trim()}let r=s?.file_path||s?.path||s?.filePath;if(r&&typeof r=="string")return r}extractDiff(e,i,s){if(e==="apply_patch"&&i)return i;if(s?.diff&&typeof s.diff=="string")return s.diff}getPendingCalls(){return Array.from(this.pendingCalls.values())}hasPendingCalls(){return this.pendingCalls.size>0}clear(){for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),this.pendingCalls.clear(),o.debug("Approval detector cleared")}shutdown(){this.clear(),this.removeAllListeners(),o.info("Approval detector shutdown")}};var tt=require("child_process"),it=require("util");var xe=(0,it.promisify)(tt.exec),q="__CODEVIBE_CODEX_KEY_ESCAPE__",ae=class{async sendLiteral(t,e){return this.sendTerminalOperation(t,async i=>{await this.sendLiteralViaTmux(i,e)})}async sendKey(t,e){return this.sendTerminalOperation(t,async i=>{await this.sendKeyViaTmux(i,e)})}async sendInput(t,e){o.info("Attempting to send input to Codex",{sessionId:t,input:e});try{let i=process.env.CODEVIBE_CODEX_TMUX_SESSION;return i?(o.info("Using tmux send-keys",{tmuxSession:i}),e===q?await this.sendKeyViaTmux(i,"Escape"):await this.sendViaTmux(i,e),o.info("Successfully sent input to Codex",{sessionId:t,input:e}),!0):(o.error("No tmux session found - CodeVibe Companion launch is required",{sessionId:t,hint:"Start Codex CLI using `codevibe --agent codex`"}),!1)}catch(i){return o.error("Failed to send input to Codex",{sessionId:t,error:i instanceof Error?i.message:String(i)}),!1}}async sendTerminalOperation(t,e){let i=process.env.CODEVIBE_CODEX_TMUX_SESSION;if(!i)return o.error("No tmux session found - CodeVibe Companion launch is required",{sessionId:t,hint:"Start Codex CLI using `codevibe --agent codex`"}),!1;try{return await e(i),!0}catch(s){return o.error("Failed to send terminal input to Codex",{sessionId:t,error:s instanceof Error?s.message:String(s)}),!1}}async sendViaTmux(t,e){o.info("Sending via tmux",{sessionName:t,inputLength:e.length});try{await this.sendLiteralViaTmux(t,e),await this.delay(500);let i=`tmux send-keys -t "${t}" Enter`;await xe(i),o.info("tmux send-keys completed")}catch(i){throw o.error("tmux send-keys failed",{sessionName:t,error:i}),i}}async sendLiteralViaTmux(t,e){let i=e.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\$/g,"\\$").replace(/`/g,"\\`"),s=`tmux send-keys -t "${t}" -l "${i}"`;await xe(s)}async sendKeyViaTmux(t,e){o.info("Sending special key via tmux",{sessionName:t,key:e});try{let i=`tmux send-keys -t "${t}" ${e}`;await xe(i),o.info("tmux special-key send completed")}catch(i){throw o.error("tmux special-key send failed",{sessionName:t,key:e,error:i}),i}}delay(t){return new Promise(e=>setTimeout(e,t))}isApprovalResponse(t){let e=t.trim().toLowerCase();return["y","n","a","q","e","yes","no"].includes(e)||/^[0-9]+$/.test(e)}};var g=E(require("fs")),pe=E(require("os")),D=E(require("path")),J=require("crypto"),rt=require("child_process"),nt=require("events"),ot=require("util");var Oe=(0,ot.promisify)(rt.execFile),st=512*1024,W="writer.state",oi=2e3,ai=10,li=16*1024*1024,pi=10080*60*1e3,le=class extends nt.EventEmitter{constructor(e={}){super();this.sessionName=null;this.started=!1;this.pipeDirPath=null;this.pipeDirIdentity=null;this.pipeFilePath=null;this.pipeFileFd=null;this.pipeFileIdentity=null;this.writerStatePath=null;this.writerStateFd=null;this.writerStateIdentity=null;this.writerGeneration=null;this.pipeEnabled=!1;this.filePosition=0;this.pollTimer=null;this.processing=!1;this.pendingRead=!1;this.promptInspectionChain=Promise.resolve();this.lastPromptHash=null;this.lifecycleGeneration=0;this.retainedMirrors=[];this.retainedMirrorContainmentLost=!1;this.maxPaneFileBytes=e.maxPaneFileBytes??li,this.stalePaneRetentionMs=e.stalePaneRetentionMs??pi,this.tempDir=e.tempDir??pe.tmpdir(),this.writerPath=D.resolve(__dirname,"..","libexec","pane-mirror-writer.js");let i=e.runtimePlatform??process.platform,s=e.runtimeRelease??pe.release();if(this.wslDetected=i==="linux"&&(/microsoft/i.test(s)||!!process.env.WSL_DISTRO_NAME||!!process.env.WSL_INTEROP),!Number.isSafeInteger(this.maxPaneFileBytes)||this.maxPaneFileBytes<=0)throw new RangeError("maxPaneFileBytes must be a positive safe integer");if(!Number.isSafeInteger(this.stalePaneRetentionMs)||this.stalePaneRetentionMs<0)throw new RangeError("stalePaneRetentionMs must be a non-negative safe integer")}async start(e){if(this.wslDetected)throw new Error("Tmux pane mirror fallback is disabled on unqualified WSL; using hook-based prompts only");if(this.started&&this.sessionName===e&&this.pipeEnabled){o.debug("Tmux pane observer already started",{sessionName:e});return}this.started&&await this.stop(),this.lifecycleGeneration++,this.sessionName=e,this.filePosition=0,this.lastPromptHash=null;try{if(this.cleanupStalePipeFiles(),this.reapOwnedRetainedMirrors(),this.retainedMirrorContainmentLost||this.retainedMirrors.length>0)throw new Error("Prior bounded tmux pane writer is still unconfirmed; refusing another mirror");this.createSecureMirror(),this.started=!0,await this.enablePipePane(),this.pipeEnabled=!0,this.startPolling()}catch(i){this.started=!1,this.lifecycleGeneration++;let s=this.writerGeneration===null;if(!s)try{await this.disablePipePane(),s=!0}catch(r){o.error("Failed to confirm tmux pane writer quiescence after startup failure",{disableError:r})}throw s&&(this.pipeEnabled=!1),this.clearPolling(),s?this.releaseSecureMirror():this.abandonSecureMirror(),this.resetLifecycleState(),i}o.info("Tmux pane observer started",{sessionName:e})}async stop(){if(!this.started&&this.pipeFileFd===null)return;this.started=!1,this.lifecycleGeneration++;let e=!1;try{await this.disablePipePane(),e=!0}catch(i){o.error("Failed to confirm tmux pane writer quiescence during stop",{error:i}),this.emit("observer-error",i)}e&&(this.pipeEnabled=!1),this.clearPolling(),e?this.releaseSecureMirror():this.abandonSecureMirror(),o.info("Tmux pane observer stopped",{sessionName:this.sessionName,writerQuiesced:e}),this.resetLifecycleState()}identityOf(e){return{dev:e.dev,ino:e.ino}}identitiesEqual(e,i){return e.dev===i.dev&&e.ino===i.ino}openMirrorFile(e){return g.openSync(e,g.constants.O_RDWR|g.constants.O_CREAT|g.constants.O_EXCL|(g.constants.O_NOFOLLOW??0),384)}truncateMirror(e){g.ftruncateSync(e,0)}createSecureMirror(){g.mkdirSync(this.tempDir,{recursive:!0,mode:448});let e=g.mkdtempSync(D.join(this.tempDir,`codevibe-codex-pane-${process.pid}-`));g.chmodSync(e,448);let i=D.join(e,"pane.log"),s=D.join(e,W),r=null,n=null;try{r=this.openMirrorFile(i),g.fchmodSync(r,384);let l=g.fstatSync(r);if(!l.isFile())throw new Error("Tmux pane mirror is not a regular file");n=this.openMirrorFile(s),g.fchmodSync(n,384);let p=g.fstatSync(n);if(!p.isFile())throw new Error("Tmux pane writer state is not a regular file");g.writeSync(n,Buffer.from(`idle
13
+ `),0,5,0),this.pipeDirPath=e,this.pipeDirIdentity=this.identityOf(g.lstatSync(e)),this.pipeFilePath=i,this.pipeFileFd=r,this.pipeFileIdentity=this.identityOf(l),this.writerStatePath=s,this.writerStateFd=n,this.writerStateIdentity=this.identityOf(p),r=null,n=null}catch(l){if(r!==null)try{g.closeSync(r)}catch{}if(n!==null)try{g.closeSync(n)}catch{}try{g.unlinkSync(i)}catch{}try{g.unlinkSync(s)}catch{}try{g.rmdirSync(e)}catch{}throw l}}pathMatchesMirrorIdentity(){if(!this.pipeFilePath||!this.pipeFileIdentity)return!1;try{let e=g.lstatSync(this.pipeFilePath);return e.isFile()&&!e.isSymbolicLink()&&this.identitiesEqual(this.pipeFileIdentity,this.identityOf(e))}catch{return!1}}quarantineIfIdentityMatches(e,i){let s=`${e}.reap-${process.pid}-${(0,J.randomBytes)(6).toString("hex")}`;try{g.renameSync(e,s);let r=g.lstatSync(s);return this.identitiesEqual(i,this.identityOf(r))?s:(g.existsSync(e)||g.renameSync(s,e),null)}catch{return null}}restoreQuarantine(e,i){try{g.existsSync(i)||g.renameSync(e,i)}catch{}}pathMatchesIdentity(e,i){if(!e||!i)return!1;try{let s=g.lstatSync(e);return s.isFile()&&!s.isSymbolicLink()&&this.identitiesEqual(i,this.identityOf(s))}catch{return!1}}closeOwnedDescriptors(){if(this.pipeFileFd!==null)try{g.closeSync(this.pipeFileFd)}catch{}if(this.writerStateFd!==null)try{g.closeSync(this.writerStateFd)}catch{}this.pipeFileFd=null,this.writerStateFd=null}unlinkOwnedFile(e,i){if(!e||!i||!this.pathMatchesIdentity(e,i))return;let s=this.quarantineIfIdentityMatches(e,i);s&&g.unlinkSync(s)}releaseSecureMirror(){let e=this.pipeFilePath,i=this.writerStatePath,s=this.pipeDirPath,r=this.pipeFileIdentity,n=this.writerStateIdentity;this.closeOwnedDescriptors();try{this.unlinkOwnedFile(e,r)}catch{}try{this.unlinkOwnedFile(i,n)}catch{}if(s)try{g.rmdirSync(s)}catch{}this.pipeDirPath=null,this.pipeDirIdentity=null,this.pipeFilePath=null,this.pipeFileIdentity=null,this.writerStatePath=null,this.writerStateIdentity=null,this.writerGeneration=null}abandonSecureMirror(){let e=this.pipeDirPath,i=this.pipeFilePath;e&&this.pipeDirIdentity&&i&&this.pipeFileIdentity&&this.writerStatePath&&this.writerStateIdentity&&this.writerGeneration?this.retainedMirrors.push({dirPath:e,dirIdentity:this.pipeDirIdentity,panePath:i,paneIdentity:this.pipeFileIdentity,statePath:this.writerStatePath,stateIdentity:this.writerStateIdentity,writerGeneration:this.writerGeneration}):(e||i)&&(this.retainedMirrorContainmentLost=!0),this.closeOwnedDescriptors(),o.warn("Retaining bounded tmux pane mirror because writer quiescence is unconfirmed",{retainedDir:e,retainedFile:i,maxBytes:this.maxPaneFileBytes}),this.pipeDirPath=null,this.pipeDirIdentity=null,this.pipeFilePath=null,this.pipeFileIdentity=null,this.writerStatePath=null,this.writerStateIdentity=null,this.writerGeneration=null}resetLifecycleState(){this.sessionName=null,this.pipeDirPath=null,this.pipeDirIdentity=null,this.pipeFilePath=null,this.pipeFileFd=null,this.pipeFileIdentity=null,this.writerStatePath=null,this.writerStateFd=null,this.writerStateIdentity=null,this.writerGeneration=null,this.pipeEnabled=!1,this.filePosition=0,this.processing=!1,this.pendingRead=!1,this.lastPromptHash=null}async failClosedPipe(e){let i=!1;try{await this.disablePipePane(),i=!0}catch(s){o.error("Failed to confirm unsafe tmux pane writer quiescence",{disableError:s})}i&&(this.pipeEnabled=!1),this.clearPolling(),o.error("Tmux pane mirror entered fail-closed containment after integrity/cap failure",{error:e,writerQuiesced:i,independentlyBounded:!0}),this.emit("observer-error",e)}resetLastPromptHash(){this.lastPromptHash=null}async captureSnapshot(e=120){if(!this.sessionName)throw new Error("Tmux pane observer is not started");let i=Math.max(1,Math.floor(e));try{let{stdout:s}=await Oe("tmux",["capture-pane","-p","-e","-J","-S",`-${i}`,"-t",this.sessionName],{timeout:5e3,maxBuffer:2097152});return s}catch(s){throw o.error("Failed to capture tmux pane snapshot",{sessionName:this.sessionName,error:s}),this.emit("observer-error",s),s}}quoteShellArg(e){return`'${e.replace(/'/g,"'\\''")}'`}writerStatePathMatchesIdentity(){return this.pathMatchesIdentity(this.writerStatePath,this.writerStateIdentity)}writeWriterState(e){if(this.writerStateFd===null||!this.writerStateIdentity||!this.writerStatePathMatchesIdentity())throw new Error("Tmux pane writer state identity is not valid");let i=g.fstatSync(this.writerStateFd);if(!this.identitiesEqual(this.writerStateIdentity,this.identityOf(i)))throw new Error("Tmux pane writer state descriptor identity changed");let s=Buffer.from(e,"utf8");g.ftruncateSync(this.writerStateFd,0);let r=0;for(;r<s.length;){let n=g.writeSync(this.writerStateFd,s,r,s.length-r,r);if(n<=0)throw new Error("Short tmux pane writer-state write");r+=n}g.fsyncSync(this.writerStateFd)}readWriterState(){if(this.writerStateFd===null||!this.writerStateIdentity||!this.writerStatePathMatchesIdentity())throw new Error("Tmux pane writer state identity is not valid");let e=g.fstatSync(this.writerStateFd);if(!this.identitiesEqual(this.writerStateIdentity,this.identityOf(e)))throw new Error("Tmux pane writer state descriptor identity changed");let i=Math.min(e.size,512);if(i===0)return"";let s=Buffer.alloc(i),r=g.readSync(this.writerStateFd,s,0,i,0);return s.subarray(0,r).toString("utf8").trim()}async waitForWriterState(e,i,s=oi){let r=Date.now()+s;do{let n=this.readWriterState();if(i.some(l=>n.startsWith(`${l} ${e} `)))return n;if(Date.now()>=r)return null;await new Promise(l=>setTimeout(l,ai))}while(!0)}async enablePipePane(){if(!this.sessionName||!this.pipeFilePath||!this.writerStatePath)throw new Error("Tmux pane observer is not initialized");if(this.pipeFileFd===null||!this.pipeFileIdentity||!this.pathMatchesMirrorIdentity()||this.writerStateFd===null||!this.writerStateIdentity||!this.writerStatePathMatchesIdentity())throw new Error("Tmux pane mirror identity is not valid");let e=(0,J.randomBytes)(16).toString("hex");this.writerGeneration=e,this.writeWriterState(`starting ${e}
14
+ `);let i=[process.execPath,this.writerPath,this.pipeFilePath,String(this.pipeFileIdentity.dev),String(this.pipeFileIdentity.ino),this.writerStatePath,String(this.writerStateIdentity.dev),String(this.writerStateIdentity.ino),e,String(this.maxPaneFileBytes)].map(r=>this.quoteShellArg(r)).join(" ");try{await Oe("tmux",["pipe-pane","-O","-t",this.sessionName,i])}catch(r){throw this.writerGeneration=null,r}let s=await this.waitForWriterState(e,["ready","done"]);if(!s)throw new Error("Tmux pane writer did not confirm startup");if(s.startsWith(`done ${e} `))throw new Error("Tmux pane writer terminated during startup");o.debug("Enabled tmux pipe-pane mirroring",{sessionName:this.sessionName,pipeFilePath:this.pipeFilePath,writerGeneration:e})}async disablePipePane(){let e=this.writerGeneration;if(!this.sessionName||!e)return;let i=null;try{await Oe("tmux",["pipe-pane","-t",this.sessionName])}catch(n){i=n}if(!e){if(i)throw i;return}if(await this.waitForWriterState(e,["done"])){this.writerGeneration=null;return}let r=i instanceof Error?`: ${i.message}`:"";throw new Error(`Tmux pane writer quiescence was not confirmed${r}`)}startPolling(){this.clearPolling(),this.pollTimer=setInterval(()=>{this.processFileChanges()},100),this.pollTimer.unref?.()}clearPolling(){this.pollTimer&&clearInterval(this.pollTimer),this.pollTimer=null}async processFileChanges(){if(!(!this.started||!this.pipeFilePath)){if(this.processing){this.pendingRead=!0;return}this.processing=!0;try{do{this.pendingRead=!1;let e="";try{e=this.readAppendedChunk()}catch(i){o.error("Failed to read bounded tmux pane mirror",{error:i}),await this.failClosedPipe(i);return}await this.rotatePipeFileIfNeeded(),e&&this.queuePromptInspection(e,this.lifecycleGeneration)}while(this.pendingRead)}catch(e){o.error("Failed to process tmux pane changes",{error:e}),this.emit("observer-error",e)}finally{this.processing=!1}}}queuePromptInspection(e,i){this.promptInspectionChain=this.promptInspectionChain.then(async()=>{if(!(!this.started||this.lifecycleGeneration!==i))try{await this.inspectChunkForPrompt(e)}catch(s){o.error("Failed to inspect tmux pane delta",{error:s}),this.emit("observer-error",s)}})}async inspectChunkForPrompt(e){if(!this.looksLikePromptDelta(e))return;let i=await this.captureSnapshot();this.inspectSnapshotForPrompt(i,e)}inspectSnapshotForPrompt(e,i){let s=e?this.extractActiveApprovalBlock(e):null;if(!s){e&&o.debug("tmux delta cue matched but no active dialog isolated (no candidate emitted)",{snapshotTail:e.split(`
15
+ `).slice(-6).join("\\n")});return}let r=this.hashPromptSnapshot(s);r!==this.lastPromptHash&&(this.lastPromptHash=r,this.emit("prompt-candidate",{rawDelta:i,snapshot:s,detectedAt:Date.now()}))}queueCurrentSnapshotInspection(e){this.promptInspectionChain=this.promptInspectionChain.then(async()=>{if(!(!this.started||this.lifecycleGeneration!==e))try{let i=await this.captureSnapshot();this.inspectSnapshotForPrompt(i,"")}catch(i){o.error("Failed to inspect tmux pane snapshot after rotation",{error:i}),this.emit("observer-error",i)}})}async rotatePipeFileIfNeeded(){let e=this.pipeFilePath,i=this.pipeFileFd,s=this.pipeFileIdentity,r=this.sessionName,n=this.lifecycleGeneration;if(!e||i===null||!s||!r||!this.started)return;let l;try{let c=g.fstatSync(i);if(!this.identitiesEqual(s,this.identityOf(c)))throw new Error("Tmux pane mirror descriptor identity changed");l=c.size}catch(c){await this.failClosedPipe(c);return}if(l<this.maxPaneFileBytes)return;let p="";try{await this.disablePipePane(),this.pipeEnabled=!1}catch(c){o.error("Tmux pane mirror rotation deferred because writer quiescence is unconfirmed",{error:c}),this.emit("observer-error",c),this.clearPolling();return}if(!(!this.started||this.lifecycleGeneration!==n||this.pipeFilePath!==e)){try{p=this.readAppendedChunk()}catch(c){o.error("Failed to drain final tmux pane suffix during rotation; mirror remains intact and disabled",{error:c}),this.emit("observer-error",c),this.clearPolling();return}try{this.truncateMirror(i);let c=g.fstatSync(i);if(!this.identitiesEqual(s,this.identityOf(c))||c.size!==0)throw new Error("Tmux pane mirror truncation could not be verified");if(!this.pathMatchesMirrorIdentity())throw new Error("Tmux pane mirror path was replaced during rotation")}catch(c){o.error("Tmux pane mirror remains disabled after truncation verification failure",{error:c}),this.emit("observer-error",c),this.clearPolling();return}if(this.filePosition=0,o.info("Rotated bounded tmux pane mirror",{sessionName:r,priorBytes:l,maxBytes:this.maxPaneFileBytes}),this.started&&this.lifecycleGeneration===n&&this.pipeFilePath===e&&this.sessionName===r)try{await this.enablePipePane(),this.pipeEnabled=!0}catch(c){this.pipeEnabled=!1,o.error("Tmux pane mirror remains disabled after re-enable failure",{error:c}),this.emit("observer-error",c),this.clearPolling()}p&&this.queuePromptInspection(p,n),this.pipeEnabled&&this.queueCurrentSnapshotInspection(n)}}retainedPathMatchesIdentity(e,i,s){try{let r=g.lstatSync(e);return(s==="file"?r.isFile():r.isDirectory())&&!r.isSymbolicLink()&&(typeof process.getuid!="function"||r.uid===process.getuid())&&this.identitiesEqual(i,this.identityOf(r))}catch{return!1}}readRetainedWriterState(e,i){let s=null;try{s=g.openSync(e,g.constants.O_RDONLY|(g.constants.O_NOFOLLOW??0));let r=g.fstatSync(s);if(!r.isFile()||typeof process.getuid=="function"&&r.uid!==process.getuid()||!this.identitiesEqual(i,this.identityOf(r))||r.size>512)return null;let n=Buffer.alloc(r.size),l=r.size===0?0:g.readSync(s,n,0,r.size,0);return n.subarray(0,l).toString("utf8").trim()}catch{return null}finally{if(s!==null)try{g.closeSync(s)}catch{}}}reapOwnedRetainedMirror(e){if(!this.retainedPathMatchesIdentity(e.dirPath,e.dirIdentity,"directory"))return!1;let i;try{i=g.readdirSync(e.dirPath).sort()}catch{return!1}if(i.length!==2||i[0]!=="pane.log"||i[1]!==W||!this.retainedPathMatchesIdentity(e.panePath,e.paneIdentity,"file")||!this.retainedPathMatchesIdentity(e.statePath,e.stateIdentity,"file"))return!1;let s=new RegExp(`^done ${e.writerGeneration} \\d+ \\d+$`);if(!s.test(this.readRetainedWriterState(e.statePath,e.stateIdentity)??""))return!1;let r=this.quarantineIfIdentityMatches(e.dirPath,e.dirIdentity);if(!r)return!1;let n=!1;try{let l=D.join(r,"pane.log"),p=D.join(r,W);if(!this.retainedPathMatchesIdentity(l,e.paneIdentity,"file"))throw new Error("pane identity changed");if(!this.retainedPathMatchesIdentity(p,e.stateIdentity,"file"))throw new Error("state identity changed");if(!s.test(this.readRetainedWriterState(p,e.stateIdentity)??""))throw new Error("writer generation is no longer done");if(this.unlinkOwnedFile(l,e.paneIdentity),this.unlinkOwnedFile(p,e.stateIdentity),g.existsSync(l)||g.existsSync(p))throw new Error("retained mirror unlink was incomplete");g.rmdirSync(r),n=!0,o.info("Removed completed same-owner tmux pane mirror",{retainedDir:e.dirPath})}catch{g.existsSync(r)&&this.restoreQuarantine(r,e.dirPath)}return n}reapOwnedRetainedMirrors(){this.retainedMirrors=this.retainedMirrors.filter(e=>!this.reapOwnedRetainedMirror(e))}cleanupStalePipeFiles(){let e=Date.now()-this.stalePaneRetentionMs,i;try{i=g.readdirSync(this.tempDir,{withFileTypes:!0})}catch{return}for(let s of i){let r=/^codevibe-codex-pane-(\d+)\.log$/.exec(s.name);if(r){let c=D.join(this.tempDir,s.name),d=null;try{let u=g.lstatSync(c);if(!u.isFile()||u.isSymbolicLink()||u.mtimeMs>e||typeof process.getuid=="function"&&u.uid!==process.getuid()||this.isProcessAlive(Number(r[1]))||(d=this.quarantineIfIdentityMatches(c,this.identityOf(u)),!d))continue;g.unlinkSync(d),d=null,o.info("Removed stale legacy tmux pane mirror",{candidate:c,ageMs:Date.now()-u.mtimeMs})}catch{d&&this.restoreQuarantine(d,c)}continue}let n=/^codevibe-codex-pane-(\d+)-[A-Za-z0-9_-]+$/.exec(s.name);if(!n||!s.isDirectory())continue;let l=D.join(this.tempDir,s.name),p=null;try{let c=g.lstatSync(l);if(!c.isDirectory()||c.isSymbolicLink()||c.mtimeMs>e||typeof process.getuid=="function"&&c.uid!==process.getuid())continue;let d=Number(n[1]);if(this.isProcessAlive(d)||(p=this.quarantineIfIdentityMatches(l,this.identityOf(c)),!p))continue;let u=D.join(p,"pane.log"),m=D.join(p,W),y=g.readdirSync(p);if(y.some(f=>f!=="pane.log"&&f!==W)){this.restoreQuarantine(p,l);continue}for(let f of y.map(v=>D.join(p,v))){let v=g.lstatSync(f);if(!v.isFile()||v.isSymbolicLink()||typeof process.getuid=="function"&&v.uid!==process.getuid()){this.restoreQuarantine(p,l),p=null;break}}if(!p)continue;if(y.includes(W)){let f=g.readFileSync(m,"utf8").trim(),v=/^ready [a-f0-9]{32} (\d+)$/.exec(f);if(!(f==="idle"||/^done [a-f0-9]{32} \d+ \d+$/.test(f)||!!v)||v&&this.isProcessAlive(Number(v[1]))){this.restoreQuarantine(p,l);continue}}y.includes("pane.log")&&g.unlinkSync(u),y.includes(W)&&g.unlinkSync(m),g.rmdirSync(p),p=null,o.info("Removed stale tmux pane mirror directory",{candidate:l,ageMs:Date.now()-c.mtimeMs})}catch{p&&this.restoreQuarantine(p,l)}}}isProcessAlive(e){try{return process.kill(e,0),!0}catch(i){return i?.code==="EPERM"}}readAppendedChunk(){if(!this.pipeFilePath||this.pipeFileFd===null||!this.pipeFileIdentity)return"";if(!this.pathMatchesMirrorIdentity())throw new Error("Tmux pane mirror path was replaced");let e=g.fstatSync(this.pipeFileFd);if(!this.identitiesEqual(this.pipeFileIdentity,this.identityOf(e)))throw new Error("Tmux pane mirror descriptor identity changed");if(e.size<=this.filePosition)return"";{let s=e.size-this.filePosition>st?e.size-st:this.filePosition,r=e.size-s,n=Buffer.alloc(r),l=0;for(;l<r;){let p=g.readSync(this.pipeFileFd,n,l,r-l,s+l);if(p===0)break;l+=p}return this.filePosition=s+l,n.subarray(0,l).toString("utf-8")}}looksLikePromptDelta(e){return/Would you like to run/i.test(e)||/Press enter to confirm/i.test(e)||/\besc to cancel\b/i.test(e)||/\bYes,\s+proceed\b/i.test(e)||/\bNo,\s+and tell Codex\b/i.test(e)||/don't ask again/i.test(e)||/Requesting permission for:|Do you want to proceed\?/i.test(e)||/\[(?:y\/n|Y\/n|y\/N)\]|\b(?:apply|approve|allow|reject|deny|continue)\b/i.test(e)}looksLikePromptSnapshot(e){return this.extractActiveApprovalBlock(e)!==null}extractActiveApprovalBlock(e){let s=e.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g,"").split(`
16
16
  `).map(f=>f.replace(/\s+$/,"")),r=f=>f.trim().length>0,n=f=>{let v=f.trim();return/^(?:[>›❯]\s*)?press enter to confirm(?: or esc to cancel)?$/i.test(v)||/^(?:[>›❯]\s*)?(?:press )?esc to cancel$/i.test(v)},l=f=>/Would you like to |Do you want to |The model would like to |Allow .+ to run|Requesting permission for:/i.test(f),p=f=>/^[>›❯\s]*[1-9][.)]\s+\S/.test(f),c=f=>/\[(?:y\/n|Y\/n|y\/N)\]/i.test(f),d=f=>{let v=new Set;for(let S of f)p(S)&&(/\bYes,\s+proceed\b/i.test(S)&&v.add("proceed"),/\bNo,\s+and tell Codex\b/i.test(S)&&v.add("reject"),/don't ask again/i.test(S)&&v.add("dont-ask"));return v.size},u=f=>p(f)&&(/\bYes,\s+proceed\b/i.test(f)||/\bNo,\s+and tell Codex\b/i.test(f)||/don't ask again/i.test(f)),m=-1;for(let f=s.length-1;f>=0;f--)if(r(s[f])){m=f;break}if(m===-1)return null;if(c(s[m])){let f=m;for(let v=m-1;v>=0&&m-v<=8&&!(!r(s[v])||n(s[v])||p(s[v]));v--)f=v;return s.slice(f,m+1).join(`
17
17
  `)}let y=-1;for(let f=s.length-1;f>=0;f--)if(n(s[f])){y=f;break}if(y===m){let f=-1;for(let S=y-1;S>=0&&!n(s[S]);S--)if(l(s[S])){f=S;break}if(f===-1)return null;let v=s.slice(f,y+1);return v.some(p)?v.join(`
18
18
  `):null}if(u(s[m])){let f=-1;for(let b=m-1;b>=0;b--){if(n(s[b]))return null;if(l(s[b])){f=b;break}}if(f===-1)return null;let v=b=>{let T=b.match(/^[>›❯\s]*([1-9])[.)]\s/);return T?parseInt(T[1],10):null},S=[],P=Number.POSITIVE_INFINITY;for(let b=m;b>f&&p(s[b]);b--){let T=v(s[b]);if(T===null||T>=P)break;S.push(s[b]),P=T}return d(S)<2?null:s.slice(f,m+1).join(`
19
19
  `)}return null}hashPromptSnapshot(e){let i=e.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g,"").replace(/\r/g,`
20
20
  `).replace(/[ \t]+\n/g,`
21
- `).trim();return(0,J.createHash)("sha256").update(i).digest("hex")}};var K=require("@quantiya/codevibe-core");var Oe=E(require("express")),U=E(require("fs")),nt=E(require("path")),ot=E(require("os"));var ni=1800*1e3,oi=60*1e3,ai=1440*60*1e3;var ce=class{constructor(){this.assignedPort=0;this.portRefreshTimer=null;this.app=(0,Oe.default)(),this.setupMiddleware(),this.setupRoutes(),this.tmuxSession=process.env.CODEVIBE_CODEX_TMUX_SESSION}getPort(){return this.assignedPort}setupMiddleware(){this.app.use(Oe.default.json({limit:"1mb"})),this.app.use((t,e,i)=>{o.debug(`${t.method} ${t.path}`,{body:t.body}),i()})}setupRoutes(){this.app.get("/health",(t,e)=>{e.json({success:!0,service:"codevibe-codex",pid:process.pid,data:{status:"healthy",uptime:process.uptime()}})}),this.app.post("/event",this.handleEvent.bind(this))}async handleEvent(t,e){try{let i=t.body;if(!i.session_id||!i.hook_event_name){e.status(400).json({success:!1,error:"Missing session_id or hook_event_name"});return}let s=this.transformHookToEvent(i);if(o.info("Received hook event",{sessionId:i.session_id,hookEvent:i.hook_event_name,type:s.type}),this.eventHandler){let r=this.eventHandler(s);if(i.hook_event_name==="PermissionRequest"){r.catch(n=>{o.error("Error resolving PermissionRequest after acknowledgement:",n)}),e.json({success:!0});return}await r}e.json({success:!0})}catch(i){o.error("Error handling event:",i),e.status(500).json({success:!1,error:i instanceof Error?i.message:"Unknown error"})}}transformHookToEvent(t){let e={cwd:t.cwd,hook_event_name:t.hook_event_name,transcript_path:t.transcript_path,...t.metadata||{}},i,s;switch(t.hook_event_name){case"SessionStart":i="NOTIFICATION",s="Session started",e.source=t.source,e.model=t.model;break;case"UserPromptSubmit":i="USER_PROMPT",s=t.prompt||"";break;case"PreToolUse":i="NOTIFICATION",s="PreToolUse observed",e.tool_name=t.tool_name,e.tool_input=t.tool_input,e.tool_use_id=t.tool_use_id,e.approval_status="observed_pre_tool",e.requires_user_action=!1;break;case"PermissionRequest":i="NOTIFICATION",s="PermissionRequest observed",e.tool_name=t.tool_name,e.tool_input=t.tool_input,e.permission_mode=t.permission_mode,e.turn_id=t.turn_id,e.requires_user_action=!0;break;case"PostToolUse":i="TOOL_USE",s=JSON.stringify({tool_name:t.tool_name,tool_input:t.tool_input,tool_response:t.tool_response}),e.tool_name=t.tool_name,e.tool_input=t.tool_input,e.tool_use_id=t.tool_use_id;break;case"Stop":i="ASSISTANT_RESPONSE",s=t.last_assistant_message||"";break;default:i="NOTIFICATION",s=`Hook: ${t.hook_event_name}`}return{session_id:t.session_id,hook_event_name:t.hook_event_name,type:i,source:"DESKTOP",content:s,metadata:e}}onEvent(t){this.eventHandler=t}async start(){return new Promise((t,e)=>{try{this.server=this.app.listen(0,"localhost",()=>{let i=this.server.address();this.assignedPort=i.port,o.info(`HTTP API listening on http://localhost:${this.assignedPort}`),this.writePortFile(this.assignedPort),this.startPortFileKeepalive(),t(this.assignedPort)}),this.server.on("error",i=>{o.error("HTTP server error:",i),e(i)})}catch(i){e(i)}})}portFilePath(){return this.tmuxSession?nt.join(ot.tmpdir(),`codevibe-codex-${this.tmuxSession}.port`):null}writePortFile(t){let e=this.portFilePath();if(!e){o.warn("No CODEVIBE_CODEX_TMUX_SESSION set, skipping port file");return}try{U.writeFileSync(e,t.toString()),o.info(`Port file written: ${e} -> ${t}`)}catch(i){o.error(`Failed to write port file: ${e}`,i)}}removePortFile(){let t=this.portFilePath();if(t)try{U.existsSync(t)&&(U.unlinkSync(t),o.info(`Port file removed: ${t}`))}catch(e){o.warn(`Failed to remove port file: ${t}`,e)}}startPortFileKeepalive(){this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null);let t=Number(process.env.CODEVIBE_PORTFILE_REFRESH_MS),e=Number.isFinite(t)&&t>0?Math.min(ai,Math.max(oi,t)):ni;this.portRefreshTimer=setInterval(()=>this.refreshPortFile(),e)}refreshPortFile(){let t=this.portFilePath();if(t)try{let e=new Date;U.utimesSync(t,e,e)}catch(e){e?.code==="ENOENT"?this.writePortFile(this.assignedPort):o.warn(`Port-file refresh failed: ${t}`,e)}}async stop(){return this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null),this.removePortFile(),new Promise(t=>{this.server?this.server.close(()=>{o.info("HTTP API stopped"),t()}):t()})}};var de=class{constructor(t={}){this.pendingBySession=new Map;this.expiryMs=t.expiryMs??1e4,this.minFuzzyEchoLength=t.minFuzzyEchoLength??16,this.minFuzzyEchoRatio=t.minFuzzyEchoRatio??.35,this.now=t.now??Date.now}track(t,e){let i=this.normalize(e);if(!i)return;let s=this.validEntries(t);s.push({normalized:i,timestamp:this.now()}),this.pendingBySession.set(t,s)}forget(t,e){let i=this.normalize(e);if(!i)return;let s=!1,r=this.validEntries(t).filter(n=>!s&&n.normalized===i?(s=!0,!1):!0);this.replaceEntries(t,r)}consumeIfDuplicate(t,e){let i=this.normalize(e);if(!i)return null;let s=null,r=this.validEntries(t).filter(n=>{if(!s){let l=this.matchType(n.normalized,i);if(l)return s={matchType:l,originalLength:n.normalized.length,echoLength:i.length},!1}return!0});return this.replaceEntries(t,r),s}validEntries(t){let e=this.now()-this.expiryMs;return(this.pendingBySession.get(t)||[]).filter(i=>i.timestamp>=e)}replaceEntries(t,e){e.length>0?this.pendingBySession.set(t,e):this.pendingBySession.delete(t)}matchType(t,e){if(t===e)return"exact";let i=e.length>=this.minFuzzyEchoLength,s=e.length/t.length>=this.minFuzzyEchoRatio;return i&&s&&t.endsWith(e)?"suffix":null}normalize(t){return t.replace(/\s+/g," ").trim()}};var lt=E(require("crypto")),pt=E(require("fs")),ct=E(require("https")),ue=E(require("os")),dt=E(require("path")),li="G-GS74YEQTB8",pi="lAfOF6OxRzSQ-NsLBRjhAg",ci="www.google-analytics.com",di=`/mp/collect?measurement_id=${li}&api_secret=${pi}`,at=800;function ui(){try{let a=dt.resolve(__dirname,"..","package.json"),t=pt.readFileSync(a,"utf-8"),e=JSON.parse(t);if(typeof e.version=="string"&&e.version.length>0&&e.version.length<30)return e.version}catch{}return"unknown"}var hi=ui();function mi(){let a=typeof process.getuid=="function"?process.getuid():0;return lt.createHash("sha256").update(`${ue.hostname()}-${a}`).digest("hex").substring(0,36)}function fi(a){if(!a)return"";let t=ue.homedir(),e=a.replace(/[\n\r\t]/g," ");if(t&&t.length>0){let i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");e=e.replace(new RegExp(i,"g"),"~")}return e=e.replace(/\/Users\/[^/\s"'`]+/g,"/Users/<user>").replace(/\/home\/[^/\s"'`]+/g,"/home/<user>").replace(/[^\x20-\x7E]/g,""),e.trim().substring(0,100)}function R(a,t){return new Promise(e=>{let i,s=!1,r=()=>{s||(s=!0,clearTimeout(n),e())},n=setTimeout(()=>{try{i?.destroy()}catch{}r()},at);typeof n.unref=="function"&&n.unref();try{let l={...t};typeof l.error_message=="string"&&(l.error_message=fi(l.error_message));let p=JSON.stringify({client_id:mi(),events:[{name:a,params:{agent:"codex",plugin_version:hi,platform:process.platform,source:process.env.CODEVIBE_TELEMETRY_SOURCE||"production",...l}}]});i=ct.request({hostname:ci,path:di,method:"POST",headers:{"Content-Type":"application/json"},timeout:at},c=>{c.resume(),c.on("end",r),c.on("close",r),c.on("error",r)}),i.on("error",r),i.on("timeout",()=>{try{i?.destroy()}catch{}r()}),i.on("close",r),i.write(p),i.end()}catch{r()}})}var he=class{constructor(){this.originated=new Set;this.externallyResolved=new Set}markOriginated(t){this.originated.add(t)}clearOriginated(t){this.originated.delete(t)}getOriginated(){return new Set(this.originated)}getExternallyResolved(){return new Set(this.externallyResolved)}isExternallyResolved(t){return this.externallyResolved.has(t)}dispatch(t){let{gateId:e}=t;return this.originated.has(e)?(this.originated.delete(e),this.externallyResolved.add(e),"self_echo"):(this.externallyResolved.add(e),"foreign")}clear(){this.originated.clear(),this.externallyResolved.clear()}};function ut(a){return`[CodeVibe] Decision recorded on another device: ${a} \u2014 type any number to dismiss the open AskUserQuestion`}var Z=E(require("crypto")),I=E(require("fs")),fe=E(require("os")),N=E(require("path")),ft=require("@quantiya/codevibe-core"),Ce=2*1024*1024,gi=600*1e3,q=1e3;function gt(){return typeof process.getuid=="function"?process.getuid():void 0}function Ae(a,t){let e=gt();if(e!==void 0&&a.uid!==e)throw new Error(`E1_STATE_FOREIGN_OWNER: ${t}`)}function yt(a){I.mkdirSync(a,{recursive:!0,mode:448});let t=I.lstatSync(a);if(!t.isDirectory()||t.isSymbolicLink())throw new Error(`E1_STATE_ROOT_NOT_PRIVATE_DIRECTORY: ${a}`);Ae(t,a),process.platform!=="win32"&&I.chmodSync(a,448)}function ht(a,t){let e=N.dirname(a);yt(e);let i=`${JSON.stringify(t)}
22
- `;if(Buffer.byteLength(i,"utf8")>Ce)throw new Error(`E1_STATE_TOO_LARGE: ${a}`);let s=N.join(e,`.${N.basename(a)}.${process.pid}.${Z.randomUUID()}.tmp`),r=I.constants.O_NOFOLLOW??0,n=null;try{n=I.openSync(s,I.constants.O_WRONLY|I.constants.O_CREAT|I.constants.O_EXCL|r,384),I.writeFileSync(n,i,{encoding:"utf8"}),I.fsyncSync(n),I.closeSync(n),n=null,process.platform!=="win32"&&I.chmodSync(s,384),I.renameSync(s,a),process.platform!=="win32"&&I.chmodSync(a,384);try{let l=I.openSync(e,I.constants.O_RDONLY);try{I.fsyncSync(l)}finally{I.closeSync(l)}}catch{}}finally{n!==null&&I.closeSync(n);try{I.unlinkSync(s)}catch(l){if(l.code!=="ENOENT")throw l}}}function mt(a){let t;try{t=I.lstatSync(a)}catch(s){if(s.code==="ENOENT")return null;throw s}if(!t.isFile()||t.isSymbolicLink())throw new Error(`E1_STATE_NOT_REGULAR_FILE: ${a}`);if(Ae(t,a),process.platform!=="win32"&&(t.mode&63)!==0)throw new Error(`E1_STATE_PERMISSIONS_LOOSE: ${a}`);if(t.size>Ce)throw new Error(`E1_STATE_TOO_LARGE: ${a}`);let e=I.constants.O_NOFOLLOW??0,i=I.openSync(a,I.constants.O_RDONLY|e);try{let s=I.fstatSync(i);if(Ae(s,a),!s.isFile()||t.dev!==s.dev||t.ino!==s.ino)throw new Error(`E1_STATE_FILE_CHANGED_DURING_OPEN: ${a}`);if(s.size>Ce)throw new Error(`E1_STATE_TOO_LARGE: ${a}`);return JSON.parse(I.readFileSync(i,{encoding:"utf8"}))}finally{I.closeSync(i)}}function M(a){return a!==null&&typeof a=="object"&&!Array.isArray(a)}function x(a,t=256*1024){return typeof a=="string"&&Buffer.byteLength(a,"utf8")<=t}function vt(a){if(!M(a)||!x(a.promptId,512)||!x(a.ownerToken,512)||!x(a.sessionId,1024)||a.producerKind!=="PLUGIN_APPROVAL"||a.agentType!==void 0&&a.agentType!=="CODEX"||a.title!==void 0&&!x(a.title,4096)||typeof a.mobileActionable!="boolean"||typeof a.raisedAtMs!="number"||!Number.isFinite(a.raisedAtMs))return!1;try{return(0,ft.deriveOpenPromptId)(a.ownerToken)===a.promptId}catch{return!1}}function yi(a){if(!M(a)||!vt(a.record))return!1;for(let t of["contentPlain","resolutionDedupeKey","resolutionCompletionKey","resolutionCallId","suppressedObserverSnapshotKey"])if(a[t]!==void 0&&!x(a[t]))return!1;return!(a.metadataPlain!==void 0&&!M(a.metadataPlain)||a.suppressTmuxObserverUntilResolved!==void 0&&typeof a.suppressTmuxObserverUntilResolved!="boolean")}function St(a){return M(a)&&vt(a.record)&&x(a.resolutionAt,128)&&!Number.isNaN(Date.parse(a.resolutionAt))&&typeof a.attempts=="number"&&Number.isSafeInteger(a.attempts)&&a.attempts>=0}function Pt(a){if(!M(a)||!x(a.promptId,512)||!["yes_no","numbered","text"].includes(String(a.kind))||!Array.isArray(a.options)||a.options.length>100||!a.options.every(t=>M(t)&&x(t.number,32)&&x(t.text,32*1024))||!M(a.submitMap)||!Object.entries(a.submitMap).every(([t,e])=>x(t,128)&&x(e,32*1024))||!x(a.promptText)||typeof a.createdAt!="number"||!Number.isFinite(a.createdAt)||!["permission_hook","tmux","heuristic"].includes(String(a.source)))return!1;for(let t of["callId","dedupeKey","completionKey","ownerSessionId","observerMatchKey"])if(a[t]!==void 0&&!x(a[t],32*1024))return!1;return a.requiresFollowUpText===void 0||typeof a.requiresFollowUpText=="boolean"}function vi(a){return M(a)&&Pt(a.prompt)&&x(a.content)&&M(a.metadata)}function Si(a,t,e){if(!M(a)||a.version!==1)throw new Error("E1_HANDOFF_UNSUPPORTED_VERSION");if(a.tmuxSession!==t||!x(a.backendSessionId,1024))throw new Error("E1_HANDOFF_INSTANCE_MISMATCH");if(typeof a.createdAtMs!="number"||!Number.isFinite(a.createdAtMs))throw new Error("E1_HANDOFF_INVALID_TIMESTAMP");if(a.createdAtMs>e+6e4||e-a.createdAtMs>gi)throw new Error("E1_HANDOFF_STALE");if(!Array.isArray(a.openRaises)||a.openRaises.length>q||!a.openRaises.every(yi))throw new Error("E1_HANDOFF_INVALID_OPEN_RAISES");if(!Array.isArray(a.pendingResolutions)||a.pendingResolutions.length>q||!a.pendingResolutions.every(St))throw new Error("E1_HANDOFF_INVALID_PENDING_RESOLUTIONS");if(!Array.isArray(a.needsReRaisePromptIds)||a.needsReRaisePromptIds.length>q||!a.needsReRaisePromptIds.every(s=>x(s,512)))throw new Error("E1_HANDOFF_INVALID_RERAISE_IDS");if(!Array.isArray(a.permissionOccurrences)||a.permissionOccurrences.length>q)throw new Error("E1_HANDOFF_INVALID_OCCURRENCES");if(!a.permissionOccurrences.every(s=>Array.isArray(s)&&s.length===2&&x(s[0],256*1024)&&Array.isArray(s[1])&&s[1].length<=q&&s[1].every(r=>M(r)&&x(r.occurrenceKey,512)&&typeof r.hookObserved=="boolean"&&typeof r.jsonlObserved=="boolean")))throw new Error("E1_HANDOFF_INVALID_OCCURRENCES");if(a.pendingInteractivePrompt!==null&&!Pt(a.pendingInteractivePrompt))throw new Error("E1_HANDOFF_INVALID_ACTIVE_PROMPT");if(!Array.isArray(a.queuedInteractivePrompts)||a.queuedInteractivePrompts.length>q||!a.queuedInteractivePrompts.every(vi))throw new Error("E1_HANDOFF_INVALID_QUEUED_PROMPTS");if(!Array.isArray(a.resolvedApprovalDedupeKeys)||a.resolvedApprovalDedupeKeys.length>q||!a.resolvedApprovalDedupeKeys.every(s=>Array.isArray(s)&&s.length===2&&x(s[0])&&typeof s[1]=="number"&&Number.isFinite(s[1])))throw new Error("E1_HANDOFF_INVALID_RESOLVED_KEYS");return a}function Pi(a){if(!M(a)||a.version!==1||!St(a.pending))throw new Error("E1_RESOLUTION_OUTBOX_INVALID_RECORD");if(typeof a.updatedAtMs!="number"||!Number.isFinite(a.updatedAtMs))throw new Error("E1_RESOLUTION_OUTBOX_INVALID_TIMESTAMP");return a}var me=class{constructor(t={}){let e=gt()??"nouid";this.handoffRoot=t.handoffRoot??N.join(fe.tmpdir(),`codevibe-codex-handoff-${e}`),this.resolutionRoot=t.resolutionRoot??N.join(fe.homedir(),".codevibe","codex","e1-resolution-outbox"),this.now=t.now??Date.now}handoffPath(t){let e=Z.createHash("sha256").update(t).digest("hex").slice(0,32);return N.join(this.handoffRoot,`${e}.json`)}resolutionPath(t){let e=Z.createHash("sha256").update(t).digest("hex");return N.join(this.resolutionRoot,`${e}.json`)}writeHandoff(t){ht(this.handoffPath(t.tmuxSession),t)}readHandoff(t){let e=mt(this.handoffPath(t));if(e===null)return null;try{return Si(e,t,this.now())}catch(i){if(i.message==="E1_HANDOFF_STALE")return this.removeHandoff(t),null;throw i}}removeHandoff(t){try{I.unlinkSync(this.handoffPath(t))}catch(e){if(e.code!=="ENOENT")throw e}}writeResolution(t){ht(this.resolutionPath(t.record.promptId),{version:1,updatedAtMs:this.now(),pending:t})}removeResolution(t){try{I.unlinkSync(this.resolutionPath(t))}catch(e){if(e.code!=="ENOENT")throw e}}readResolutions(){let t;try{yt(this.resolutionRoot),t=I.readdirSync(this.resolutionRoot).filter(s=>s.endsWith(".json"))}catch(s){if(s.code==="ENOENT")return{records:[],rejected:[]};throw s}let e=[],i=[];for(let s of t.slice(0,q)){let r=N.join(this.resolutionRoot,s);try{let n=mt(r);if(n!==null){let l=Pi(n).pending;if(N.basename(this.resolutionPath(l.record.promptId))!==s)throw new Error("E1_RESOLUTION_OUTBOX_IDENTITY_MISMATCH");e.push(l)}}catch(n){i.push({file:r,reason:n instanceof Error?n.message:String(n)})}}return t.length>q&&i.push({file:this.resolutionRoot,reason:`E1_RESOLUTION_OUTBOX_RECORD_LIMIT: ${t.length}`}),{records:e,rejected:i}}};var bt=(0,De.promisify)(Se.exec),Ii=(0,De.promisify)(Se.execFile),bi="/quit",j="CODEVIBE_CODEX_TMUX_SESSION",ge="CODEVIBE_CODEX_DAEMON_RECOVERY",wi=1e3,Ei=600*1e3,Fe=30*1e3,Ri=/^(?:[a-z0-9]|esc|escape)$/;async function _i(a,t){let e=async(i,s)=>{try{await bt(i)}catch(r){o.warn("tmux send-keys failed during self-terminate",{sessionName:a,label:s,error:String(r)})}};await e(`tmux send-keys -t "${a}" C-c`,"ctrl-c"),await new Promise(i=>setTimeout(i,200)),await e(`tmux send-keys -t "${a}" -l "${t}"`,"quit-text"),await new Promise(i=>setTimeout(i,500)),await e(`tmux send-keys -t "${a}" Enter`,"enter")}function Me(a){let t=a.split(`
23
- `),e=n=>/^[\s>]*\d+\.\s/.test(n),i=-1;for(let n=t.length-1;n>=0;n-=1)if(/^\s*\$\s+/.test(t[n])){i=n;break}if(i===-1)return;let s=t[i].trim().match(/^\$\s+(.+)$/);if(!s?.[1]?.trim())return;let r=[s[1].trim()];for(let n=i+1;n<t.length;n+=1){let l=t[n];if(e(l)||/^\s*$/.test(l)||/^\s*\$\s+/.test(l))break;r.push(l.trim())}return r.length>0?r:void 0}function wt(a){let t=Me(a);if(!t)return;let e=t.join(" ").trim();return e.length>0?e:void 0}function Et(a,t){return Ti(a,t).has(t)}function Ti(a,t){if(a.length===0)return new Set;let e=t,i=new Set([a[0]]);for(let s of a.slice(1)){let r=s,n=new Set;for(let l of i)for(let p of[""," "]){let c=`${l}${p}${r}`;e.startsWith(c)&&n.add(c)}if(n.size===0)return new Set;i=n}return i}function xi(a,t,e){let i=/^\[\u2026\s+\d+\s+lines\]\s+ctrl\s*\+\s*a\s+view\s+all$/i,s=a.split(`
24
- `).map(r=>r.trim()).filter(Boolean);return s.length<2||!i.test(s[s.length-1])||!e?!1:Oi(e,t)}function Oi(a,t){let e=ke(t),i=_t(a,"2");if(!e||!i)return!1;let s=new Set;for(let r of Tt(i)){let n=r.match(/^Yes, and don't ask again for commands that start with `([^`]*)`\s*\(p\)$/);if(!n)continue;let l=ke(n[1]);l&&JSON.stringify(l)===JSON.stringify(e)&&s.add(JSON.stringify(l))}return s.size===1}function Rt(a,t,e){if(!a.find(u=>u.number==="2"&&/\(p\)\s*$/i.test(u.text)))return a;let s="Yes, and don't ask again for commands that start with",r="Yes, and don't ask again (p)",n=_t(e,"2");if(!n)return null;let l=Tt(n);if(l.has(r))return a.map(u=>u.number==="2"?{...u,text:r}:u);if(!t)return null;let p=new Map;for(let u of l){let m=u.match(/^Yes, and don't ask again for commands that start with `([^`]*)`\s*\(p\)$/);if(!m)continue;let y=ke(m[1]);!y||!Ai(y,t)||p.set(JSON.stringify(y),y)}if(p.size!==1)return null;let c=[...p.values()][0],d=Ci(c);return a.map(u=>u.number==="2"?{...u,text:`${s} \`${d}\` (p)`}:u)}function _t(a,t){if(!a)return null;let e=(0,K.normalizeSnapshot)(a).split(`
25
- `).map(l=>l.trim()),i=new RegExp(`^(?:[>\u203A\u276F\u25B8\u25B6\u279C\u27A4*\u25CF]\\s*)?${t}\\.\\s+(.*)$`),s=-1,r="";for(let l=e.length-1;l>=0;l-=1){let p=e[l].match(i);if(p){s=l,r=p[1];break}}if(s<0)return null;let n=[r];for(let l=s+1;l<e.length&&!(/^(?:[>›❯▸▶➜➤*●]\s*)?\d+\.\s+/.test(e[l])||!e[l]||(n.push(e[l]),/\(p\)\s*$/i.test(e[l])));l+=1);return n.some(l=>/\(p\)\s*$/i.test(l))?n:null}function Tt(a){if(a.length===0||a.length>13)return new Set;let t=new Set([a[0]]);for(let e of a.slice(1)){let i=new Set;for(let s of t)i.add(`${s}${e}`),i.add(`${s} ${e}`);t=i}return t}function ke(a){try{let t=(0,Ne.parse)(a,e=>`\${${e}}`);return t.every(e=>typeof e=="string")?t:null}catch{return null}}function Ci(a){return a.map(t=>/^[A-Za-z0-9_@%+=:,./-]+$/.test(t)?t:`'${t.replace(/'/g,`'"'"'`)}'`).join(" ")}function Ai(a,t){if(a.length===0)return!1;let e;try{e=(0,Ne.parse)(t,s=>`\${${s}}`)}catch{return!1}let i=[[]];for(let s of e){if(typeof s=="string"){i[i.length-1].push(s);continue}if("op"in s&&["&&","||",";","|","|&","&"].includes(s.op)){i.push([]);continue}return!1}return i.some(s=>a.every((r,n)=>s[n]===r))}var ve=class a{constructor(){this.sessionState=null;this.unsubscribe=null;this.sessionKey=null;this.sessionIsEncrypted=!1;this.sessionKeyGen=null;this.e1PromptRaises=new Map;this.e1PendingResolutions=new Map;this.e1ResolutionInFlight=new Map;this.e1RaiseInFlight=new Map;this.e1NeedsReRaise=new Set;this.e1RetirementOrphans=new Map;this.e1ContentKeyIndex=new Map;this.e1TtlRefreshTimer=null;this.toolActivity=null;this.toolActivityStartPromise=null;this.toolActivityLegacyRecorders=new Map;this.pendingInteractivePrompt=null;this.queuedInteractivePrompts=[];this.isInitializingSession=!1;this.bufferedLogEntries=[];this.logEntryChain=Promise.resolve();this.hookEventChain=Promise.resolve();this.hooksActive=!1;this.hooksCompatibilityNoticeSent=!1;this.permissionRequestHookInFlight=null;this.queuedPermissionRequestDedupeKeys=new Map;this.queuedPermissionRequestSessionCounts=new Map;this.queuedPermissionRequestRegistrations=new Map;this.permissionRequestOccurrencesByCompletionKey=new Map;this.deferredTmuxPromptCandidates=new Map;this.recoveryPermissionReplay=null;this.resolvedApprovalDedupeKeys=new Map;this.subscribedSessionId=null;this.subscribedSessionState=null;this.terminalInputChain=Promise.resolve();this.terminalInputBlockedStates=new WeakSet;this.mobilePromptDeduper=new de({expiryMs:1e4});this.launchSessionInitPromise=null;this.sessionStartedInitPromise=null;this.bootstrapSessionId=null;this.recoveryBootstrapOwnerBound=!1;this.resumeBackendSession=h.resumeOrCreateSession;this.sessionBootstrapFailureCount=0;this.sessionBootstrapRetryAt=0;this.retiredSessionRecoveryPromise=null;this.lifecycleGeneration=0;this.isStopping=!1;this.tmuxLifecycleTimer=null;this.tmuxLifecycleCheckInFlight=!1;this.tmuxLifecycleObservedAlive=!1;this.v1Bridge=new he;this.orchestrationClient=null;this.userDecisionUnsubscribe=null;this.httpApi=new ce,this.sessionWatcher=new ie,this.approvalDetector=new oe,this.promptResponder=new ae,this.tmuxPaneObserver=new le,this.e1DurableState=new me({handoffRoot:process.env.CODEVIBE_CODEX_HANDOFF_ROOT,resolutionRoot:process.env.CODEVIBE_CODEX_E1_OUTBOX_ROOT})}static{this.E1_TTL_REFRESH_MS=300*1e3}async start(){o.info("Starting CodeVibe Codex companion server",{environment:(0,h.getEnvironment)()}),this.appSyncClient=new h.AppSyncClient,await this.appSyncClient.authenticateWithStoredTokens()||(o.error('Authentication failed. Run "codevibe login" first.'),console.error('Not authenticated. Run "codevibe login" to sign in.'),process.exit(1)),o.info("Authenticated successfully",{userId:this.appSyncClient.getCurrentUserId(),email:this.appSyncClient.getCurrentUserEmail()}),await this.restorePendingE1ResolutionsFromOutbox(),await(0,h.registerDeviceEncryptionKey)(this.appSyncClient,o),(0,h.startDeviceKeyWatcher)(this.appSyncClient,o),(0,h.pushDetectedAgents)(this.appSyncClient,o).catch(i=>{o.warn("Failed to update available agents (non-fatal)",{error:i?.message})});try{let i=await this.appSyncClient.sweepOrphanSessions({agentType:"CODEX"});i>0&&o.info("Orphan sweep: marked stale Codex sessions INACTIVE",{swept:i})}catch(i){o.warn("Orphan sweep failed, continuing startup",{error:i instanceof Error?i.message:String(i)})}this.httpApi.onEvent(this.handleEventFromHook.bind(this));let e=await this.httpApi.start();o.info("HTTP API started for hooks",{port:e}),this.startTmuxLifecycleMonitor(),await this.createLaunchSession(),this.setupEventHandlers(),this.sessionWatcher.start(),o.info("CodeVibe Codex companion server started")}async createLaunchSession(){if(this.isStopping)return;if(process.env[ge]==="1"){o.info("Recovery daemon deferring backend bootstrap until the native rollout is known");return}let t=this.lifecycleGeneration,e=process.env.CODEVIBE_CODEX_TMUX_SESSION;if(!e){o.warn("No CODEVIBE_CODEX_TMUX_SESSION \u2014 skipping launch session");return}let i;this.launchSessionInitPromise=new Promise(s=>{i=s});try{let s=process.env.CODEX_WORKING_DIRECTORY||process.cwd(),r=this.getOrCreateBootstrapSessionId(),n=this.appSyncClient.getCurrentUserId();o.info("Creating launch session",{sessionId:r,projectPath:s});let l=!1;try{let p=await(0,h.resumeOrCreateSession)({sessionId:r,userId:n,agentType:h.AgentType.CODEX,projectPath:s,metadata:{launchSession:!0}},this.appSyncClient,o);if(r=p.sessionId,!this.isLifecycleCurrent(t)){await this.retireCancelledBootstrap(r,"launch_session");return}this.sessionKey=p.sessionKey,this.sessionKeyGen=p.sessionKeyGen,this.sessionIsEncrypted=!!p.sessionKey,l=!0,this.sessionState={sessionId:r,userId:n,projectPath:s,cwd:s,createdAt:new Date,subscriptionActive:!1,metadata:{launchSession:!0},encryptionSnapshot:{sessionKey:p.sessionKey,sessionIsEncrypted:!!p.sessionKey},codexSessionId:e,codexLogFile:void 0},this.clearSessionBootstrapFailure(),this.bootstrapSessionId=null,await R("daemon_init_step_completed",{step:"session_resume_or_create",path:"launch_session"})}catch(p){if(!this.isLifecycleCurrent(t)){let c=this.authoritativeSessionIdFromError(p)??r;await this.retireCancelledBootstrap(c,"launch_session_error");return}await R("daemon_init_step_failed",{step:"session_resume_or_create",path:"launch_session",error_class:p?.name||"Error",error_message:p?.message||String(p)}),p?.code==="ENCRYPTED_SESSION_NO_KEY"&&(this.sessionIsEncrypted=!0,this.sessionKey=null),this.bindBootstrapIdentityFromError(p),this.recordSessionBootstrapFailure(p,"launch_session"),o.error("Failed to create/resume launch session (non-fatal)",{error:p})}if(!l)return;await R("daemon_init_step_completed",{step:"session_state_set",path:"launch_session"});try{this.subscribeToMobileEvents(r)?await R("daemon_init_step_completed",{step:"subscribe_mobile_events",path:"launch_session"}):await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"launch_session",error_class:"SubscriptionSetupFailed",error_message:"subscribeToMobileEvents returned false"})}catch(p){await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"launch_session",error_class:p?.name||"Error",error_message:p?.message||String(p)}),o.error("Failed to subscribe to mobile events for launch session",{error:p})}try{await this.appSyncClient.startHeartbeat(r)?await R("daemon_init_step_completed",{step:"heartbeat_start",path:"launch_session"}):(await R("daemon_init_step_failed",{step:"heartbeat_start",path:"launch_session",error_class:"InitialHeartbeatNotPersisted",error_message:"Backend did not confirm the initial heartbeat mutation"}),o.error("Initial launch-session heartbeat was not persisted",{sessionId:r}))}catch(p){await R("daemon_init_step_failed",{step:"heartbeat_start",path:"launch_session",error_class:p?.name||"Error",error_message:p?.message||String(p)}),o.error("Failed to start heartbeat for launch session",{error:p})}try{await this.subscribeToOnApplyUserDecision(r)}catch(p){o.error("Failed to subscribe to onApplyUserDecision for launch session (non-fatal)",{error:p})}this.startMobileEndWatcher(r),o.info("Launch session created",{sessionId:r})}finally{i()}}encryptForEmit(t,e,i,s){let r=this.sessionState;if(!r||r.sessionId!==s||!r.encryptionSnapshot)return o.warn("Dropping event from stale or unresolved session generation (#638)",{type:i,sessionId:s,currentSessionId:r?.sessionId}),null;let{sessionKey:n,sessionIsEncrypted:l}=r.encryptionSnapshot,p=(0,h.encryptForEmit)(n,l,t,e);return p===null&&o.error("No session key for ENCRYPTED session \u2014 dropping event (fail-closed, #638)",{type:i,sessionId:s}),p}newE1Raise(t,e,i,s){let{record:r}=(0,h.newPromptRaise)({sessionId:t,producerKind:"PLUGIN_APPROVAL",mobileActionable:e,agentType:"CODEX",...i!==void 0&&{title:i}});return this.e1PromptRaises.set(r.promptId,{record:r,...s?.dedupeKey!==void 0?{resolutionDedupeKey:s.dedupeKey}:{},...s?.completionKey!==void 0?{resolutionCompletionKey:s.completionKey}:{},...s?.callId!==void 0?{resolutionCallId:s.callId}:{}}),this.e1TtlRefreshTimer||this.startE1TtlRefresh(),r}stashE1RaiseContent(t,e,i){let s=this.e1PromptRaises.get(t);s&&(s.contentPlain=e,s.metadataPlain=i)}async emitSuppressedPromptCarrier(t,e){let i="\u26A0\uFE0F A prompt is waiting in your desktop terminal, but its options could not be shown here. Please answer it on your desktop.",s=this.encryptForEmit(i,{e1SuppressedPrompt:!0},h.EventType.NOTIFICATION,t);return s?(await this.trackE1Raise(e.promptId,this.appSyncClient.createEvent({sessionId:t,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,content:s.content,metadata:s.metadata,...(0,h.raiseFieldsFromRecord)(e),notificationText:i,timestamp:(0,h.prepareEventTimestamp)({orderingKey:t}),isEncrypted:s.isEncrypted?!0:void 0})),o.info("E1: emitted suppressed-prompt badge carrier (mobileActionable=false)",{sessionId:t,promptId:e.promptId}),!0):!1}raiseSuppressedBadge(t,e,i={}){if(e){if(this.hasRecentlyResolvedApprovalDedupeKey(t,e)){o.debug("Skipping suppressed-prompt carrier; approval completed while producer was in flight",{sessionId:t,dedupeKey:e});return}let r=`${t}::${e}`,n=this.e1ContentKeyIndex.get(r);if(n&&this.e1PromptRaises.has(n)){if(i.suppressTmuxObserverUntilResolved){let l=this.e1PromptRaises.get(n);l&&(l.suppressTmuxObserverUntilResolved=!0,i.observerSnapshot&&(l.suppressedObserverSnapshotKey=this.buildObserverSnapshotMatchKey(i.observerSnapshot)))}return}n&&this.e1ContentKeyIndex.delete(r)}let s=this.newE1Raise(t,!1,void 0,e?{dedupeKey:e,completionKey:i.completionKey}:void 0);if(i.suppressTmuxObserverUntilResolved){let r=this.e1PromptRaises.get(s.promptId);r&&(r.suppressTmuxObserverUntilResolved=!0,i.observerSnapshot&&(r.suppressedObserverSnapshotKey=this.buildObserverSnapshotMatchKey(i.observerSnapshot)))}e&&this.e1ContentKeyIndex.set(`${t}::${e}`,s.promptId),this.emitSuppressedPromptCarrier(t,s).catch(r=>{o.error("E1: failed to emit suppressed-prompt badge carrier",{sessionId:t,promptId:s.promptId,error:r instanceof Error?r.message:String(r)})})}snapshotOpenE1(t){return[...this.e1PromptRaises.values()].filter(e=>e.record.sessionId===t).map(e=>({record:{...e.record},...e.contentPlain!==void 0&&{contentPlain:e.contentPlain},...e.metadataPlain!==void 0&&{metadataPlain:e.metadataPlain},...e.resolutionDedupeKey!==void 0&&{resolutionDedupeKey:e.resolutionDedupeKey},...e.resolutionCompletionKey!==void 0&&{resolutionCompletionKey:e.resolutionCompletionKey},...e.resolutionCallId!==void 0&&{resolutionCallId:e.resolutionCallId},...e.suppressTmuxObserverUntilResolved!==void 0&&{suppressTmuxObserverUntilResolved:e.suppressTmuxObserverUntilResolved},...e.suppressedObserverSnapshotKey!==void 0&&{suppressedObserverSnapshotKey:e.suppressedObserverSnapshotKey}}))}buildDaemonHandoffSnapshot(t){let e=process.env[j];if(!e)throw new Error("E1_DAEMON_HANDOFF_MISSING_TMUX_SESSION");return{version:1,createdAtMs:Date.now(),tmuxSession:e,backendSessionId:t.sessionId,openRaises:this.snapshotOpenE1(t.sessionId),pendingResolutions:[...this.e1PendingResolutions.values()].map(i=>({record:{...i.record},resolutionAt:i.resolutionAt,attempts:i.attempts})),needsReRaisePromptIds:[...this.e1NeedsReRaise],permissionOccurrences:[...this.permissionRequestOccurrencesByCompletionKey.entries()].map(([i,s])=>[i,s.map(r=>({...r}))]),pendingInteractivePrompt:this.pendingInteractivePrompt?{...this.pendingInteractivePrompt}:null,queuedInteractivePrompts:this.queuedInteractivePrompts.map(i=>({prompt:{...i.prompt},content:i.content,metadata:{...i.metadata}})),resolvedApprovalDedupeKeys:[...this.resolvedApprovalDedupeKeys.entries()].filter(([,i])=>i>Date.now()-Fe)}}persistDaemonHandoffState(t){for(let i of this.e1PendingResolutions.values())this.e1DurableState.writeResolution(i);let e=this.buildDaemonHandoffSnapshot(t);this.e1DurableState.writeHandoff(e),o.info("E1: persisted daemon handoff state",{sessionId:t.sessionId,openPrompts:e.openRaises.length,pendingResolutions:e.pendingResolutions.length,completionQueues:e.permissionOccurrences.length})}async adoptDaemonHandoffState(t){if(process.env[ge]!=="1")return!1;let e=process.env[j];if(!e)return!1;let i=this.e1DurableState.readHandoff(e);if(!i)return!1;let s=this.sessionState;if(!s||s.sessionId!==t)throw new Error("E1_DAEMON_HANDOFF_WITHOUT_CURRENT_SESSION");for(let p of i.pendingResolutions)this.e1PendingResolutions.has(p.record.promptId)||this.e1PendingResolutions.set(p.record.promptId,{record:{...p.record},resolutionAt:p.resolutionAt,attempts:p.attempts}),this.e1DurableState.writeResolution(p);for(let p of i.openRaises){let c={...p,record:{...p.record},...p.metadataPlain!==void 0&&{metadataPlain:{...p.metadataPlain}}};this.e1PromptRaises.set(c.record.promptId,c),i.backendSessionId!==t&&this.e1RetirementOrphans.set(c.record.promptId,{...c,record:{...c.record}}),c.resolutionDedupeKey&&this.e1ContentKeyIndex.set(`${t}::${c.resolutionDedupeKey}`,c.record.promptId)}this.permissionRequestOccurrencesByCompletionKey=new Map(i.permissionOccurrences.map(([p,c])=>[p,c.map(d=>({...d}))])),this.e1NeedsReRaise=new Set(i.needsReRaisePromptIds),this.pendingInteractivePrompt=i.pendingInteractivePrompt?{...i.pendingInteractivePrompt,ownerSessionId:t}:null;let r=this.pendingInteractivePrompt,n=r?.completionKey?this.permissionRequestOccurrencesByCompletionKey.get(r.completionKey):void 0;this.recoveryPermissionReplay=r?.source==="permission_hook"&&r.completionKey&&r.dedupeKey&&this.isPermissionRequestOccurrenceKey(r.dedupeKey)&&n?.some(p=>p.occurrenceKey===r.dedupeKey)?{ownerSessionId:t,completionKey:r.completionKey,occurrenceKey:r.dedupeKey}:null,this.queuedInteractivePrompts=i.queuedInteractivePrompts.map(p=>({prompt:{...p.prompt,ownerSessionId:t},content:p.content,metadata:{...p.metadata},ownerState:s}));let l=Date.now()-Fe;for(let[p,c]of i.resolvedApprovalDedupeKeys){if(c<=l)continue;let d=`${i.backendSessionId}::`,u=p.startsWith(d)?`${t}::${p.slice(d.length)}`:p;this.resolvedApprovalDedupeKeys.set(u,c)}return i.backendSessionId!==t&&await this.drainRetirementOrphans(t),await this.drainPendingE1Resolutions(),await this.drainPendingE1ReRaises(),(this.e1PromptRaises.size>0||this.e1PendingResolutions.size>0)&&this.startE1TtlRefresh(),this.e1DurableState.removeHandoff(e),o.info("E1: adopted daemon handoff state",{fromSessionId:i.backendSessionId,replacementSessionId:t,openPrompts:i.openRaises.length,pendingResolutions:i.pendingResolutions.length,completionQueues:i.permissionOccurrences.length}),!0}parkRetirementOrphans(t){for(let e of this.snapshotOpenE1(t))this.e1RetirementOrphans.set(e.record.promptId,e)}async drainRetirementOrphans(t){if(this.e1RetirementOrphans.size===0){this.e1PendingResolutions.size>0&&!this.e1TtlRefreshTimer&&this.startE1TtlRefresh();return}o.info("E1 (\xA76a-4): re-homing parked retirement-orphan prompts onto replacement session",{replacementSessionId:t,count:this.e1RetirementOrphans.size});for(let[e,i]of[...this.e1RetirementOrphans]){let r=this.e1PromptRaises.get(e)??{record:{...i.record},...i.contentPlain!==void 0&&{contentPlain:i.contentPlain},...i.metadataPlain!==void 0&&{metadataPlain:i.metadataPlain},...i.resolutionDedupeKey!==void 0&&{resolutionDedupeKey:i.resolutionDedupeKey},...i.resolutionCompletionKey!==void 0&&{resolutionCompletionKey:i.resolutionCompletionKey},...i.resolutionCallId!==void 0&&{resolutionCallId:i.resolutionCallId}};for(let[l,p]of this.e1ContentKeyIndex.entries())p===e&&this.e1ContentKeyIndex.delete(l);if(r.record.sessionId=t,this.e1PromptRaises.set(e,r),r.resolutionDedupeKey&&this.e1ContentKeyIndex.set(`${t}::${r.resolutionDedupeKey}`,e),!await this.reRaiseOneE1(t,r,{untilAck:!0}))break;this.e1RetirementOrphans.delete(e)}this.e1TtlRefreshTimer||this.startE1TtlRefresh()}isReRaiseTargetCurrent(t){return!this.isStopping&&this.sessionState!==null&&this.sessionState.sessionId===t}async reRaiseOneE1(t,e,i){let s=e.record,r=s.mobileActionable&&e.contentPlain!==void 0,n=i?.untilAck??!1,l=4,p=4e3;for(let c=1;n||c<=l;c++){if(n&&!this.isReRaiseTargetCurrent(t))return this.e1NeedsReRaise.add(s.promptId),o.warn("E1: retirement re-raise aborted (stopping/superseded) \u2014 parked",{sessionId:t,promptId:s.promptId}),!1;try{if(r){let d=this.encryptForEmit(e.contentPlain,e.metadataPlain??{},h.EventType.INTERACTIVE_PROMPT,t);if(!d)return!1;await this.trackE1Raise(s.promptId,this.appSyncClient.createEvent({sessionId:t,type:h.EventType.INTERACTIVE_PROMPT,source:h.EventSource.DESKTOP,content:d.content,metadata:d.metadata,...(0,h.raiseFieldsFromRecord)(s),timestamp:(0,h.prepareEventTimestamp)({orderingKey:t}),...d.isEncrypted?{isEncrypted:!0}:{}}))}else if(s.mobileActionable=!1,!await this.emitSuppressedPromptCarrier(t,s))return!1;return this.e1NeedsReRaise.delete(s.promptId),o.info("E1: re-raised prompt onto replacement session",{sessionId:t,promptId:s.promptId,actionable:r}),!0}catch(d){if(!n&&c===l)return this.e1NeedsReRaise.add(s.promptId),o.error("E1: re-raise unacked after backoff \u2014 parked for retry-until-ack",{sessionId:t,promptId:s.promptId,error:d instanceof Error?d.message:String(d)}),!1;if(n){let u=Date.now()+Math.min(250*2**(c-1),p);for(;Date.now()<u&&this.isReRaiseTargetCurrent(t);)await new Promise(m=>setTimeout(m,Math.min(50,u-Date.now())))}else await new Promise(u=>setTimeout(u,250*c))}}return!1}async drainPendingE1ReRaises(){if(this.e1NeedsReRaise.size!==0)for(let t of[...this.e1NeedsReRaise]){let e=this.e1PromptRaises.get(t);if(!e){this.e1NeedsReRaise.delete(t);continue}!this.sessionState||e.record.sessionId!==this.sessionState.sessionId||await this.reRaiseOneE1(e.record.sessionId,e)}}beginE1Resolution(t,e){if(this.e1PendingResolutions.has(t))return!0;let i=this.e1PromptRaises.get(t);if(!i||i.record.sessionId!==e)return!1;this.e1PromptRaises.delete(t),this.e1NeedsReRaise.delete(t),this.e1RetirementOrphans.delete(t);for(let[s,r]of this.e1ContentKeyIndex.entries())r===t&&this.e1ContentKeyIndex.delete(s);return this.e1PendingResolutions.set(t,{record:{...i.record},resolutionAt:new Date().toISOString(),attempts:0}),this.e1TtlRefreshTimer||this.startE1TtlRefresh(),!0}async emitE1Resolution(t){let e=this.e1ResolutionInFlight.get(t);if(e)return e;if(!this.e1PendingResolutions.has(t))return;let i=(async()=>{let s=this.e1PendingResolutions.get(t);if(s){try{this.e1DurableState.writeResolution(s)}catch(r){o.error("E1: failed to persist exact-clear outbox record",{promptId:t,error:r instanceof Error?r.message:String(r)})}try{let r=this.e1RaiseInFlight.get(t);if(r&&await r,s=this.e1PendingResolutions.get(t),!s)return;if((await this.appSyncClient.createEvent({sessionId:s.record.sessionId,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,resolvedPromptId:s.record.promptId,ownerToken:s.record.ownerToken,resolutionAt:s.resolutionAt,timestamp:(0,h.prepareEventTimestamp)({orderingKey:s.record.sessionId})}))?.promptId!==s.record.promptId)throw new Error("E1_PLUGIN_RESOLUTION_NOT_ACKNOWLEDGED: exact prompt id was not returned");this.e1PendingResolutions.delete(t);try{this.e1DurableState.removeResolution(t)}catch(l){o.warn("E1: exact-clear ACKed but outbox cleanup failed",{promptId:t,error:l instanceof Error?l.message:String(l)})}o.info("E1: plugin approval resolution acknowledged",{sessionId:s.record.sessionId,promptId:t})}catch(r){let n=this.e1PendingResolutions.get(t);o.warn("E1: plugin approval resolution unacknowledged; parked for retry",{sessionId:n?.record.sessionId,promptId:t,error:r instanceof Error?r.message:String(r)})}}})();this.e1ResolutionInFlight.set(t,i);try{await i}finally{this.e1ResolutionInFlight.get(t)===i&&this.e1ResolutionInFlight.delete(t)}}trackE1Raise(t,e){let i=e.then(()=>{},()=>{});return this.e1RaiseInFlight.set(t,i),i.then(()=>{this.e1RaiseInFlight.get(t)===i&&this.e1RaiseInFlight.delete(t)}),e}async drainPendingE1Resolutions(){for(let[t,e]of[...this.e1PendingResolutions.entries()])e.attempts+=1,await this.emitE1Resolution(t)}async restorePendingE1ResolutionsFromOutbox(){let{records:t,rejected:e}=this.e1DurableState.readResolutions();for(let i of e)o.error("E1: rejected unsafe or invalid resolution outbox record",i);for(let i of t){let s=this.e1PendingResolutions.get(i.record.promptId);(!s||i.attempts>s.attempts)&&this.e1PendingResolutions.set(i.record.promptId,{record:{...i.record},resolutionAt:i.resolutionAt,attempts:i.attempts})}t.length>0&&(o.info("E1: recovered pending exact-clear outbox records",{count:t.length}),await this.drainPendingE1Resolutions(),this.e1PendingResolutions.size>0&&!this.e1TtlRefreshTimer&&this.startE1TtlRefresh())}async drainPendingE1ResolutionsForLifecycleEnd(t={}){let e=t.maxWaitMs??4e3,i=t.retryDelayMs??250,s=Date.now()+Math.max(0,e),r=0;do{if(await this.drainPendingE1Resolutions(),this.e1PendingResolutions.size===0)return;if(r+=1,Date.now()>=s)break;await new Promise(n=>setTimeout(n,Math.min(i*r,Math.max(0,s-Date.now()))))}while(Date.now()<s);for(let n of this.e1PendingResolutions.values())this.e1DurableState.writeResolution(n);o.warn("E1: graceful lifecycle ended with durable exact-clear work pending",{count:this.e1PendingResolutions.size})}beginE1PromptResolutions(t,e){let i=[];for(let s of new Set(t))this.beginE1Resolution(s,e)&&i.push(s);return i}launchE1Resolutions(t){Promise.all([...t].map(e=>this.emitE1Resolution(e)))}async exactClearOpenE1ForLifecycleEnd(t){let e=[...this.e1PromptRaises.values()].filter(s=>s.record.sessionId===t).map(s=>s.record.promptId),i=this.beginE1PromptResolutions(e,t);await Promise.all(i.map(s=>this.emitE1Resolution(s)))}async refreshE1Ttls(){await this.drainPendingE1Resolutions(),await this.drainPendingE1ReRaises();let t=this.sessionState?.sessionId;if(!t)return;let e=[...this.e1PromptRaises.values()].filter(i=>i.record.sessionId===t).map(i=>i.record.promptId);if(e.length!==0)try{let i=await this.appSyncClient.refreshOpenPromptTtl(e);for(let s of i){let r=this.e1PromptRaises.get(s);r&&r.record.sessionId===t&&await this.reRaiseOneE1(t,r)}}catch(i){o.warn("E1: TTL-refresh tick failed (non-fatal, retries next tick)",{error:i instanceof Error?i.message:String(i)})}}startE1TtlRefresh(){this.e1TtlRefreshTimer&&clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=setInterval(()=>{this.refreshE1Ttls()},a.E1_TTL_REFRESH_MS)}stopE1TtlRefresh(){this.e1TtlRefreshTimer&&(clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=null)}isExpectedSessionStateCurrent(t){return!this.isStopping&&this.sessionState===t&&!this.terminalInputBlockedStates.has(t)}sendSessionPinnedInput(t,e){if(!this.isExpectedSessionStateCurrent(t)||this.terminalInputBlockedStates.has(t))return Promise.resolve(!1);let i=this.terminalInputChain.then(async()=>!this.isExpectedSessionStateCurrent(t)||this.terminalInputBlockedStates.has(t)||!await this.verifyNoActiveCodexChooser(t.sessionId)||!await this.promptResponder.sendLiteral(t.sessionId,e)||(await new Promise(s=>setTimeout(s,500)),!this.isExpectedSessionStateCurrent(t))||!await this.verifyNoActiveCodexChooser(t.sessionId)?!1:this.promptResponder.sendKey(t.sessionId,"Enter"));return this.terminalInputChain=i.then(()=>{},()=>{}),i}async verifyNoActiveCodexChooser(t){try{let e=await this.tmuxPaneObserver.captureSnapshot();return!this.tmuxPaneObserver.extractActiveApprovalBlock(e)}catch(e){return o.warn("Failed to verify that no Codex chooser is active",{sessionId:t,error:e instanceof Error?e.message:String(e)}),!1}}sendVerifiedPromptInput(t,e,i){if(!this.isExpectedSessionStateCurrent(t))return Promise.resolve("stale");let s=this.terminalInputChain.then(async()=>{if(!this.isExpectedSessionStateCurrent(t)||this.pendingInteractivePrompt!==e||e.ownerSessionId!==t.sessionId)return"stale";if(!e.observerMatchKey)return o.warn("Refusing mobile prompt input without a captured pane identity",{sessionId:t.sessionId,promptId:e.promptId}),"unverified";let r=null;try{let l=await this.tmuxPaneObserver.captureSnapshot(),p=this.tmuxPaneObserver.extractActiveApprovalBlock(l),c=p?(0,K.parseInteractivePrompt)(p):null;r=c?this.buildObserverPromptMatchKey(c):null}catch(l){return o.warn("Failed to re-probe Codex prompt before mobile input",{sessionId:t.sessionId,promptId:e.promptId,error:l instanceof Error?l.message:String(l)}),"unverified"}return!this.isExpectedSessionStateCurrent(t)||this.pendingInteractivePrompt!==e?"stale":!r||r!==e.observerMatchKey?(o.warn("Refusing mobile input because the live Codex prompt changed",{sessionId:t.sessionId,promptId:e.promptId,hasLiveIdentity:!!r}),"unverified"):(i===B?await this.promptResponder.sendKey(t.sessionId,"Escape"):await this.promptResponder.sendLiteral(t.sessionId,i))?"sent":"send_failed"});return this.terminalInputChain=s.then(()=>{},()=>{}),s}sendVerifiedFollowUpAfterDismissal(t,e,i){let s=this.terminalInputChain.then(async()=>{let r=()=>this.isExpectedSessionStateCurrent(t)&&this.pendingInteractivePrompt===e&&e.ownerSessionId===t.sessionId;return r()?!await this.verifyNoActiveCodexChooser(t.sessionId)||!r()?"unverified":await this.promptResponder.sendLiteral(t.sessionId,i)?(await new Promise(n=>setTimeout(n,500)),r()?!await this.verifyNoActiveCodexChooser(t.sessionId)||!r()?"unverified":await this.promptResponder.sendKey(t.sessionId,"Enter")?"sent":"send_failed":"stale"):"send_failed":"stale"});return this.terminalInputChain=s.then(()=>{},()=>{}),s}handleEventFromHook(t){if(this.isStopping)return Promise.resolve();let e=this.lifecycleGeneration,i=this.registerQueuedPermissionRequest(t,e),s=this.hookEventChain.then(()=>this.processHookEvent(t,e,i??void 0)),r=i?s.finally(()=>this.unregisterQueuedPermissionRequest(i)):s;return this.hookEventChain=r.catch(n=>{o.error("[Hooks] event handler failed",{hookEvent:t.hook_event_name,sessionId:t.session_id,error:n instanceof Error?n.message:String(n)})}),r}registerQueuedPermissionRequest(t,e){if(t.hook_event_name!=="PermissionRequest"||!this.sessionState||this.sessionWatcher.getAuthoritativeSessionId()!==t.session_id)return null;let i=this.sessionState.sessionId;this.queuedPermissionRequestSessionCounts.set(i,(this.queuedPermissionRequestSessionCounts.get(i)??0)+1);let s=this.buildApprovalPromptContextFromHookPayload(t),r=this.buildApprovalDedupeKey(s),n=`permission:${(0,ee.v4)()}`,l=r?`${i}::${r}`:void 0;l&&this.queuedPermissionRequestDedupeKeys.set(l,(this.queuedPermissionRequestDedupeKeys.get(l)??0)+1);let p={sessionId:i,lifecycleGeneration:e,context:s,dedupeKey:r,occurrenceKey:n,dedupeRegistryKey:l,disposition:"pending"};if(r){let d=this.permissionRequestOccurrencesByCompletionKey.get(r)??[];d.push({occurrenceKey:n,hookObserved:!1,jsonlObserved:!1}),this.permissionRequestOccurrencesByCompletionKey.set(r,d)}let c=this.queuedPermissionRequestRegistrations.get(i)??[];return c.push(p),this.queuedPermissionRequestRegistrations.set(i,c),p}async unregisterQueuedPermissionRequest(t){let e=this.queuedPermissionRequestSessionCounts.get(t.sessionId)??0;if(e<=1?this.queuedPermissionRequestSessionCounts.delete(t.sessionId):this.queuedPermissionRequestSessionCounts.set(t.sessionId,e-1),t.dedupeRegistryKey){let l=this.queuedPermissionRequestDedupeKeys.get(t.dedupeRegistryKey)??0;l<=1?this.queuedPermissionRequestDedupeKeys.delete(t.dedupeRegistryKey):this.queuedPermissionRequestDedupeKeys.set(t.dedupeRegistryKey,l-1)}let s=(this.queuedPermissionRequestRegistrations.get(t.sessionId)??[]).filter(l=>l!==t);s.length>0?this.queuedPermissionRequestRegistrations.set(t.sessionId,s):this.queuedPermissionRequestRegistrations.delete(t.sessionId),(t.disposition==="pending"||t.disposition==="dropped"||this.sessionState?.sessionId!==t.sessionId)&&this.removePermissionRequestOccurrence(t.dedupeKey,t.occurrenceKey);let r=this.deferredTmuxPromptCandidates.get(t);if(!r)return;if(this.deferredTmuxPromptCandidates.delete(t),!this.isLifecycleCurrent(t.lifecycleGeneration)){t.disposition="dropped",this.removePermissionRequestOccurrence(t.dedupeKey,t.occurrenceKey),o.debug("Discarding deferred tmux prompt after lifecycle invalidation",{sessionId:t.sessionId,lifecycleGeneration:t.lifecycleGeneration,currentLifecycleGeneration:this.lifecycleGeneration});return}let n=this.hasRecentlyResolvedApprovalDedupeKey(t.sessionId,t.occurrenceKey);if(t.disposition==="covered"||t.disposition==="resolved"||n){o.debug("Discarding deferred tmux prompt covered by its PermissionRequest",{sessionId:t.sessionId,dedupeKey:t.dedupeKey,disposition:t.disposition});return}if(!(t.disposition==="dropped"||this.sessionState?.sessionId!==t.sessionId)){o.debug("Replaying deferred tmux prompt after PermissionRequest produced no durable owner",{sessionId:t.sessionId,dedupeKey:t.dedupeKey});try{await this.handleTmuxPromptCandidate(r,t)}catch(l){o.error("Failed to replay deferred tmux prompt candidate",{sessionId:t.sessionId,error:l instanceof Error?l.message:String(l)})}}}removePermissionRequestOccurrence(t,e){if(!t||!e)return;let i=this.permissionRequestOccurrencesByCompletionKey.get(t);if(!i)return;let s=i.filter(r=>r.occurrenceKey!==e);s.length>0?this.permissionRequestOccurrencesByCompletionKey.set(t,s):this.permissionRequestOccurrencesByCompletionKey.delete(t)}observePermissionRequestCompletion(t,e){if(!t)return{shouldResolve:!0};let i=this.permissionRequestOccurrencesByCompletionKey.get(t);if(!i)return{shouldResolve:!0};let s=e==="hook"?"hookObserved":"jsonlObserved",r=i.find(l=>!l[s]);if(!r)return o.debug("Ignoring duplicate interactive-prompt completion observation",{completionKey:t,producer:e}),{shouldResolve:!1};let n=!r.hookObserved&&!r.jsonlObserved;return r[s]=!0,r.hookObserved&&r.jsonlObserved&&this.removePermissionRequestOccurrence(t,r.occurrenceKey),{shouldResolve:n,occurrenceKey:r.occurrenceKey}}isPermissionRequestOccurrenceKey(t){return t.startsWith("permission:")}consumeRecoveryPermissionReplay(t,e,i){let s=this.recoveryPermissionReplay,r=this.pendingInteractivePrompt;return!s||!e||s.ownerSessionId!==t.sessionId||s.completionKey!==e||r?.ownerSessionId!==t.sessionId||r.source!=="permission_hook"||r.dedupeKey!==s.occurrenceKey||r.completionKey!==s.completionKey?!1:(this.recoveryPermissionReplay=null,i&&(this.removePermissionRequestOccurrence(i.dedupeKey,i.occurrenceKey),i.occurrenceKey=s.occurrenceKey,i.disposition="covered"),o.info("[Hooks] Reused adopted PermissionRequest occurrence for one recovery replay",{sessionId:t.sessionId,completionKey:e,occurrenceKey:s.occurrenceKey}),!0)}async processHookEvent(t,e,i){if(!this.isLifecycleCurrent(e))return;let{session_id:s,hook_event_name:r,type:n,content:l,metadata:p}=t;o.info("[Hooks] Received event",{sessionId:s,hookEvent:r,type:n,contentLength:l?.length});let c=this.sessionWatcher.getAuthoritativeSessionId();if(c&&c!==s){o.warn("[Hooks] Rejecting event for foreign native session",{hookEvent:r,sessionId:s,authoritativeSessionId:c});return}if(p?.transcript_path){let m=await this.sessionWatcher.bindToSessionTranscript(p.transcript_path,s);if(!this.isLifecycleCurrent(e))return;let y=m.status==="pending"?await m.completion:m;if(!this.isLifecycleCurrent(e))return;if(y.status!=="bound"){o.warn("[Hooks] Rejecting event whose transcript ownership was not proven",{hookEvent:r,sessionId:s,reason:y.reason});return}}else if(c!==s){o.warn("[Hooks] Rejecting event without an owned transcript",{hookEvent:r,sessionId:s});return}if(!this.isLifecycleCurrent(e)||this.sessionWatcher.getAuthoritativeSessionId()!==s){o.warn("[Hooks] Rejecting event after ownership changed during admission",{hookEvent:r,sessionId:s,authoritativeSessionId:this.sessionWatcher.getAuthoritativeSessionId()});return}if(this.hooksActive=!0,r==="SessionStart"){if(this.launchSessionInitPromise&&(await this.launchSessionInitPromise,!this.isLifecycleCurrent(e)))return;if(this.sessionState){if(o.info("[Hooks] SessionStart \u2014 launch session already exists, updating codexSessionId",{existingSessionId:this.sessionState.sessionId,codexSessionId:s}),this.sessionState.codexSessionId=s,this.sessionState.metadata={...this.sessionState.metadata,codexSessionId:s,cliVersion:p?.model||"unknown",modelProvider:p?.model||"unknown",launchSession:void 0},this.appSyncClient.updateSession({sessionId:this.sessionState.sessionId,metadata:this.sessionState.metadata}).catch(m=>o.warn("Failed to update session metadata",{error:m})),await this.startTmuxObserverWithBeacon("session_start_existing"),!this.isLifecycleCurrent(e))return;await this.ensureToolActivityStarted(s)}else{let m={id:s,timestamp:new Date().toISOString(),cwd:p?.cwd||process.cwd(),originator:"hook",cli_version:p?.model||"unknown",instructions:null,source:p?.source||"startup",model_provider:p?.model||"unknown"};await this.ensureSessionStarted(m)}return}if(!this.sessionState){o.warn("[Hooks] Hook event for un-bootstrapped session \u2014 self-healing via session bootstrap (#638)",{hookEvent:r,sessionId:s});let m={id:s,timestamp:new Date().toISOString(),cwd:p?.cwd||process.cwd(),originator:"hook",cli_version:p?.model||"unknown",instructions:null,source:p?.source||"startup",model_provider:p?.model||"unknown"};if(await this.ensureSessionStarted(m),!this.isLifecycleCurrent(e))return;if(!this.sessionState){o.warn("[Hooks] Session still not initialized after self-heal, dropping event",{hook_event_name:r});return}}let d=this.sessionState,u=d.sessionId;if(n==="USER_PROMPT"&&l){let m=this.consumeRecentMobilePrompt(u,l);if(m){o.info("[Hooks] Skipping duplicate USER_PROMPT from mobile",{sessionId:u,matchType:m.matchType,originalLength:m.originalLength,echoLength:m.echoLength});return}}if(r==="PreToolUse"){o.debug("[Hooks] PreToolUse observed; AppSync emission suppressed",{toolName:p?.tool_name||"unknown",sessionId:u});return}if(r==="PermissionRequest"){let m=i??this.registerQueuedPermissionRequest(t,e);if(!m){o.error("[Hooks] Dropping admitted PermissionRequest without occurrence registration",{sessionId:u,nativeSessionId:s});return}try{await this.handlePermissionRequestHook(t,d,m)}finally{i||await this.unregisterQueuedPermissionRequest(m)}return}if(r==="PostToolUse"){let m=this.buildApprovalPromptContextFromHookPayload(t),y=this.buildApprovalDedupeKey(m),f=this.firstString(p?.tool_use_id,p?.call_id,p?.callId)??"",v=this.observePermissionRequestCompletion(y,"hook");if(v.shouldResolve&&await this.clearResolvedInteractivePrompt(f,v.occurrenceKey??y),!this.isExpectedSessionStateCurrent(d)||this.toolActivity?.isActive()&&await this.toolActivity.observeCall({callId:p?.tool_use_id??p?.call_id??p?.callId,toolName:p?.tool_name,toolInput:p?.tool_input})!=="closed")return;let S=this.encryptForEmit(l,p,h.EventType.TOOL_USE,u);if(!S)return;let P=S.content,b=S.metadata,T=S.isEncrypted,F=p?.tool_use_id??p?.call_id??p?.callId,O=typeof F=="string"&&F.length>0&&!!this.sessionKey,D=O?this.getToolActivityLegacyRecorder(u):void 0;D&&O&&D.markLegacyInFlight(F);try{let A=await this.appSyncClient.createEvent({sessionId:u,type:h.EventType.TOOL_USE,source:h.EventSource.DESKTOP,content:P,metadata:b,isEncrypted:T,timestamp:(0,h.prepareEventTimestamp)({orderingKey:u})});D&&O&&((0,h.isPersistedEventResult)(A)?await D.finalizeLegacyOwned([F]):D.clearLegacyInFlight(F))}catch(A){throw D&&O&&D.clearLegacyInFlight(F),A}return}if(n==="ASSISTANT_RESPONSE"||n==="USER_PROMPT"){if(n==="ASSISTANT_RESPONSE"){this.sessionWatcher.getBindSource()==="authoritative"&&this.sessionWatcher.getAuthoritativeSessionId()===s?o.debug("[Hooks] Suppressing Stop final \u2014 JSONL watcher authoritatively bound to this session (Bug #2)"):o.warn("[Hooks] Dropping Stop final after authoritative ownership changed",{authoritativeSessionId:this.sessionWatcher.getAuthoritativeSessionId()});return}let m=this.encryptForEmit(l,void 0,h.EventType.USER_PROMPT,u);if(!m)return;await this.appSyncClient.createEvent({sessionId:u,type:h.EventType.USER_PROMPT,source:h.EventSource.DESKTOP,content:m.content,isEncrypted:m.isEncrypted,timestamp:(0,h.prepareEventTimestamp)({orderingKey:u})});return}}mapToolName(t){return{shell_command:"Bash",shell:"Bash",apply_patch:"Edit",create_file:"Write",read_file:"Read"}[t]||t}trackMobilePrompt(t,e){this.mobilePromptDeduper.track(t,e),o.debug("Tracking mobile prompt for USER_PROMPT echo deduplication",{sessionId:t,promptLength:e.trim().length})}forgetMobilePrompt(t,e){this.mobilePromptDeduper.forget(t,e)}consumeRecentMobilePrompt(t,e){return this.mobilePromptDeduper.consumeIfDuplicate(t,e)}setupEventHandlers(){this.sessionWatcher.on("session-started",async t=>{try{if(this.launchSessionInitPromise&&await this.launchSessionInitPromise,this.sessionState){o.info("[JSONL] Session already active, skipping",{currentSessionId:this.sessionState.sessionId,codexSessionId:t.id}),await this.ensureToolActivityStarted(t.id);return}await this.ensureSessionStarted(t)}catch(e){o.error("[JSONL] session-started handler failed (non-fatal) \u2014 next event retries",{codexSessionId:t?.id,error:String(e)})}}),this.sessionWatcher.on("log-entry",t=>{this.enqueueLogEntry(t)}),this.approvalDetector.on("approval-pending",async t=>{try{await this.handleApprovalPending(t)}catch(e){o.error("approval-pending handler failed (non-fatal)",{error:String(e)})}}),this.tmuxPaneObserver.on("prompt-candidate",async t=>{try{await this.handleTmuxPromptCandidate(t.snapshot)}catch(e){o.error("prompt-candidate handler failed (non-fatal)",{error:String(e)})}}),this.tmuxPaneObserver.on("observer-error",t=>{o.debug("Tmux pane observer error",{error:t})}),this.sessionWatcher.on("error",t=>{o.error("Session watcher error:",t)})}async ensureSessionStarted(t,e){if(this.isStopping||(this.launchSessionInitPromise&&await this.launchSessionInitPromise,this.isStopping)||this.sessionState)return;process.env[ge]==="1"&&await this.bindRecoveryBootstrapIdentity(t.id);let i=this.sessionBootstrapRetryAt-Date.now();if(i>0){o.warn("Session bootstrap is in retry backoff; hook will be retried later",{codexSessionId:t.id,retryDelayMs:i,failureCount:this.sessionBootstrapFailureCount});return}if(this.sessionStartedInitPromise){try{await this.sessionStartedInitPromise}catch{}return}let s=this.handleSessionStarted(t,e);this.sessionStartedInitPromise=s.catch(()=>{});try{await s}finally{this.sessionStartedInitPromise=null}}async handleSessionStarted(t,e){if(this.isStopping)return;let i=this.lifecycleGeneration;o.info("Handling new Codex session",{codexSessionId:t.id}),this.isInitializingSession=!0,this.bufferedLogEntries=[],this.sessionState&&await this.endActiveSession("new-codex-session-started");let s=process.env.CODEX_WORKING_DIRECTORY||t.cwd||process.cwd(),r=this.getOrCreateBootstrapSessionId(),n=this.appSyncClient.getCurrentUserId(),l={codexSessionId:t.id,cliVersion:t.cli_version,modelProvider:t.model_provider},p={sessionKey:null,sessionIsEncrypted:!0};try{let c={sessionId:r,userId:n,agentType:h.AgentType.CODEX,projectPath:s,metadata:l},d=await this.resumeOrCreateBootstrapSession(c);if(r=d.sessionId,!this.isLifecycleCurrent(i)){this.isInitializingSession=!1,await this.retireCancelledBootstrap(r,"session_started");return}this.sessionKey=d.sessionKey,this.sessionKeyGen=d.sessionKeyGen,this.sessionIsEncrypted=!!d.sessionKey,p={sessionKey:d.sessionKey,sessionIsEncrypted:!!d.sessionKey},await R("daemon_init_step_completed",{step:"session_resume_or_create",path:"session_started"})}catch(c){if(this.isInitializingSession=!1,!this.isLifecycleCurrent(i)){let d=this.authoritativeSessionIdFromError(c)??r;await this.retireCancelledBootstrap(d,"session_started_error");return}throw await R("daemon_init_step_failed",{step:"session_resume_or_create",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),c?.code==="ENCRYPTED_SESSION_NO_KEY"&&(this.sessionIsEncrypted=!0,this.sessionKey=null),this.bindBootstrapIdentityFromError(c),this.recordSessionBootstrapFailure(c,"session_started"),o.error("Failed to create/resume session:",c),c}try{this.sessionState={sessionId:r,userId:n,projectPath:s,cwd:t.cwd,createdAt:new Date,subscriptionActive:!1,metadata:l,encryptionSnapshot:p,codexSessionId:t.id,codexLogFile:this.sessionWatcher.getActiveLogFile()||void 0},this.clearSessionBootstrapFailure(),this.bootstrapSessionId=null,this.recoveryBootstrapOwnerBound=!1,await R("daemon_init_step_completed",{step:"session_state_set",path:"session_started"})}catch(c){await R("daemon_init_step_failed",{step:"session_state_set",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to set session state:",c)}await this.adoptDaemonHandoffState(r),await this.ensureToolActivityStarted(t.id);try{this.subscribeToMobileEvents(r)?await R("daemon_init_step_completed",{step:"subscribe_mobile_events",path:"session_started"}):await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"session_started",error_class:"SubscriptionSetupFailed",error_message:"subscribeToMobileEvents returned false"})}catch(c){await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to subscribe to mobile events:",c)}if(e)try{await e()}catch(c){o.error("E1: pre-heartbeat re-raise hook failed (non-fatal)",{sessionId:r,error:c instanceof Error?c.message:String(c)})}try{await this.drainRetirementOrphans(r)}catch(c){o.error("E1: retirement-orphan drain failed (non-fatal)",{sessionId:r,error:c instanceof Error?c.message:String(c)})}try{await this.appSyncClient.startHeartbeat(r)?await R("daemon_init_step_completed",{step:"heartbeat_start",path:"session_started"}):(await R("daemon_init_step_failed",{step:"heartbeat_start",path:"session_started",error_class:"InitialHeartbeatNotPersisted",error_message:"Backend did not confirm the initial heartbeat mutation"}),o.error("Initial session heartbeat was not persisted",{sessionId:r}))}catch(c){await R("daemon_init_step_failed",{step:"heartbeat_start",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to start heartbeat:",c)}try{await this.subscribeToOnApplyUserDecision(r)}catch(c){o.error("Failed to subscribe to onApplyUserDecision (non-fatal)",{error:c})}this.startMobileEndWatcher(r);try{await this.flushBufferedLogEntries(),await R("daemon_init_step_completed",{step:"flush_buffered_entries",path:"session_started"})}catch(c){await R("daemon_init_step_failed",{step:"flush_buffered_entries",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to flush buffered log entries:",c),this.bufferedLogEntries=[]}await this.startTmuxObserverWithBeacon("session_started"),this.isInitializingSession=!1}getOrCreateBootstrapSessionId(){return this.bootstrapSessionId||(this.bootstrapSessionId=G.mintBackendSessionId()),this.bootstrapSessionId}async bindRecoveryBootstrapIdentity(t){if(!(process.env[ge]!=="1"||this.bootstrapSessionId||this.sessionState||!t))try{let e=await this.buildToolActivityIntegration().recordedBackendSessionId(t);if(!e){o.warn("Recovery daemon found no durable backend owner; a new session will be created",{rolloutId:t});return}if(!/^codex-[A-Za-z0-9_-]{1,180}$/.test(e)){o.warn("Recovery daemon ignored invalid durable backend owner",{rolloutId:t});return}this.bootstrapSessionId=e,this.recoveryBootstrapOwnerBound=!0,o.info("Recovery daemon reclaiming existing backend session",{rolloutId:t,sessionId:e})}catch(e){o.warn("Recovery daemon could not read the durable rollout owner; a new session will be created",{rolloutId:t,error:e instanceof Error?e.message:String(e)})}}isSessionIdReservedRejection(t){return(t instanceof Error?t.message:String(t)).includes("SESSION_ID_RESERVED")}async resumeOrCreateBootstrapSession(t){try{return await this.resumeBackendSession(t,this.appSyncClient,o)}catch(e){if(!this.recoveryBootstrapOwnerBound||!this.isSessionIdReservedRejection(e))throw e;return o.warn("Recovery discarded a permanently reserved pending create; retrying the durable rollout owner",{sessionId:t.sessionId}),this.resumeBackendSession(t,this.appSyncClient,o)}}startTmuxLifecycleMonitor(){if(this.tmuxLifecycleTimer)return;let t=process.env[j];t&&(this.tmuxLifecycleTimer=setInterval(()=>{this.checkTmuxLifecycle(t)},wi),this.tmuxLifecycleTimer.unref?.(),this.checkTmuxLifecycle(t))}async tmuxSessionExists(t){try{return await Ii("tmux",["has-session","-t",t]),!0}catch{return!1}}async checkTmuxLifecycle(t){if(!(this.isStopping||this.tmuxLifecycleCheckInFlight)){this.tmuxLifecycleCheckInFlight=!0;try{if(await this.tmuxSessionExists(t)){this.tmuxLifecycleObservedAlive=!0;return}if(!this.tmuxLifecycleObservedAlive||this.isStopping)return;this.stopTmuxLifecycleMonitor(),o.info("Native Codex tmux session ended; stopping companion daemon",{tmuxSession:t}),this.stop().then(()=>process.exit(0),e=>{o.error("Failed to stop companion daemon after native session end",{error:e instanceof Error?e.message:String(e)}),process.exit(1)})}finally{this.tmuxLifecycleCheckInFlight=!1}}}stopTmuxLifecycleMonitor(){this.tmuxLifecycleTimer&&(clearInterval(this.tmuxLifecycleTimer),this.tmuxLifecycleTimer=null)}isLifecycleCurrent(t){return!this.isStopping&&this.lifecycleGeneration===t}authoritativeSessionIdFromError(t){let e=t?.authoritativeSessionId;return typeof e=="string"&&e.length>0?e:null}async retireCancelledBootstrap(t,e){this.appSyncClient.stopHeartbeat(t),this.appSyncClient.cleanupSubscription(t);try{await this.appSyncClient.updateSession({sessionId:t,status:h.SessionStatus.INACTIVE}),o.info("Cancelled bootstrap row marked INACTIVE",{sessionId:t,path:e})}catch(i){o.warn("Cancelled bootstrap row could not be marked INACTIVE",{sessionId:t,path:e,error:i instanceof Error?i.message:String(i)})}}bindBootstrapIdentityFromError(t){let e=t?.authoritativeSessionId;typeof e!="string"||e.length===0||(this.bootstrapSessionId=e,o.info("Retained authoritative backend identity from failed bootstrap",{sessionId:e}))}recoverRetiredBackendSession(t){if(this.retiredSessionRecoveryPromise)return this.retiredSessionRecoveryPromise;if(this.isStopping)return Promise.resolve();let e=(async()=>{if(this.launchSessionInitPromise&&await this.launchSessionInitPromise,this.sessionStartedInitPromise&&await this.sessionStartedInitPromise,this.isStopping)return;let i=this.sessionState;if(!i||i.sessionId!==t)return;let s={id:i.codexSessionId||t,timestamp:new Date().toISOString(),cwd:i.cwd||i.projectPath,originator:"codevibe-retired-session-recovery",cli_version:String(i.metadata?.cliVersion??"unknown"),instructions:null,source:"codevibe",model_provider:String(i.metadata?.modelProvider??"unknown")};o.warn("Backend retired live Codex session; creating replacement row",{sessionId:t,codexSessionId:s.id}),this.parkRetirementOrphans(t),await this.endActiveSession("backend-generation-retired"),this.bootstrapSessionId=G.mintBackendSessionId(),this.recoveryBootstrapOwnerBound=!1,this.clearSessionBootstrapFailure(),await this.ensureSessionStarted(s)})();return this.retiredSessionRecoveryPromise=e.finally(()=>{this.retiredSessionRecoveryPromise=null}),this.retiredSessionRecoveryPromise}recordSessionBootstrapFailure(t,e){this.sessionBootstrapFailureCount+=1;let i=t instanceof Error?t.message:String(t),r=i.includes("SESSION_LIMIT_EXCEEDED")?6e4:Math.min(5e3*2**(this.sessionBootstrapFailureCount-1),6e4);this.sessionBootstrapRetryAt=Date.now()+r,o.warn("Session bootstrap retry scheduled",{path:e,requestedSessionId:this.bootstrapSessionId,failureCount:this.sessionBootstrapFailureCount,delayMs:r,error:i})}clearSessionBootstrapFailure(){this.sessionBootstrapFailureCount=0,this.sessionBootstrapRetryAt=0}async flushBufferedLogEntries(){if(this.bufferedLogEntries.length===0)return;let t=this.bufferedLogEntries;this.bufferedLogEntries=[],o.info("Flushing buffered log entries after session initialization",{count:t.length,sessionId:this.sessionState?.sessionId});let e=Promise.resolve();for(let i of t)e=this.enqueueLogEntry(i);await e.catch(()=>{})}enqueueLogEntry(t){let e=this.logEntryChain.then(()=>this.handleLogEntry(t));return this.logEntryChain=e.catch(i=>{o.error("[JSONL] log entry handler failed",{type:t.type,err:String(i)})}),e}async handleLogEntry(t){if(!this.sessionState){if(this.isInitializingSession){this.bufferedLogEntries.push(t),o.debug("Buffering log entry until session initialization completes",{type:t.type,bufferedCount:this.bufferedLogEntries.length});return}o.warn("Received log entry but no active session");return}let e=this.sessionState,i=e.sessionId,{sessionKey:s,sessionIsEncrypted:r}=e.encryptionSnapshot;if(t.type==="event_msg"&&t.payload?.type==="turn_aborted"){this.approvalDetector.clear(),this.clearInteractivePromptsForTurnAbort(e);return}if(t.type==="response_item"&&t.payload){let f=t.payload.type;if(f==="function_call"||f==="custom_tool_call")this.approvalDetector.onToolCallStart(t.payload.call_id,t.payload.name,t.payload.arguments||t.payload.input||"");else if(f==="function_call_output"||f==="custom_tool_call_output"){let v=this.approvalDetector.getPendingCalls().find(b=>b.callId===t.payload.call_id),S=v?this.buildApprovalDedupeKey(this.buildApprovalPromptContextFromPendingCall(v)):void 0;this.approvalDetector.onToolCallComplete(t.payload.call_id);let P=this.observePermissionRequestCompletion(S,"jsonl");if(P.shouldResolve&&await this.clearResolvedInteractivePrompt(t.payload.call_id,P.occurrenceKey??S),!this.isExpectedSessionStateCurrent(e))return}}let n=Ve(t,i);if(!n)return;n.timestamp=(0,h.prepareEventTimestamp)({orderingKey:i,agentClock:t.timestamp});let l=t.payload?.type,p=!1;if(this.toolActivity?.isActive()&&n.type===h.EventType.TOOL_USE){let f=l==="function_call"||l==="custom_tool_call",v=l==="function_call_output"||l==="custom_tool_call_output";if(f&&typeof t.payload?.call_id=="string"){let S=this.sessionWatcher.getActiveLogFile();S&&this.toolActivity.bindTranscript(S);let P=await this.toolActivity.observeCall({callId:t.payload.call_id,toolName:n.metadata?.toolName??n.metadata?.tool_name,toolInput:n.metadata?.toolInput??n.metadata?.tool_input,ts:t.timestamp});if(!this.isExpectedSessionStateCurrent(e)||P!=="closed")return;p=!0}if(v)return}if(this.toolActivity?.isActive()&&(l==="user_message"||l==="agent_message")&&(await this.toolActivity.flush().catch(f=>o.warn("[tool-activity] codex boundary flush failed",{sessionId:i,err:String(f)})),!this.isExpectedSessionStateCurrent(e)))return;let c=l==="function_call"||l==="function_call_output",d=n.type===h.EventType.USER_PROMPT||n.type===h.EventType.ASSISTANT_RESPONSE||c&&(n.type===h.EventType.TOOL_USE||n.type===h.EventType.INTERACTIVE_PROMPT);if(!this.hooksActive&&d&&(await this.emitHooksCompatibilityModeNotice(e),!this.isExpectedSessionStateCurrent(e)))return;if(this.hooksActive){if(n.type===h.EventType.USER_PROMPT){o.debug("[JSONL] Skipping USER_PROMPT \u2014 hooks deliver it",{type:n.type});return}if(c&&(n.type===h.EventType.TOOL_USE||n.type===h.EventType.INTERACTIVE_PROMPT)&&!p){o.debug("[JSONL] Skipping function_call \u2014 hooks deliver this",{type:n.type,tool:t.payload?.name});return}}if(n.type===h.EventType.USER_PROMPT&&n.source===h.EventSource.DESKTOP){let f=this.consumeRecentMobilePrompt(i,n.content);if(f){o.info("[JSONL] Skipping duplicate USER_PROMPT from mobile",{sessionId:i,matchType:f.matchType,originalLength:f.originalLength,echoLength:f.echoLength});return}}if(this.sessionState!==e){o.warn("[JSONL] Dropping event from stale session generation (#638)",{sessionId:i,currentSessionId:this.sessionState?.sessionId});return}let u=t.payload?.call_id,m=n.type===h.EventType.TOOL_USE&&typeof u=="string"&&u.length>0&&!!s,y=m?this.getToolActivityLegacyRecorder(i):void 0;y&&m&&y.markLegacyInFlight(u);try{let f=(0,h.encryptForEmit)(s,r,n.content,n.metadata);if(!f){o.error("No session key for ENCRYPTED session \u2014 dropping JSONL event (fail-closed, #638)",{type:n.type,sessionId:i});return}n.content=f.content,n.metadata=f.metadata,f.isEncrypted&&(n.isEncrypted=!0,o.debug("Event encrypted",{type:n.type}));let v=await this.appSyncClient.createEvent(n);o.debug("Event synced to backend",{type:n.type,encrypted:f.isEncrypted}),y&&m&&((0,h.isPersistedEventResult)(v)?await y.finalizeLegacyOwned([u]):y.clearLegacyInFlight(u))}catch(f){y&&m&&y.clearLegacyInFlight(u),o.error("Failed to sync event:",f)}}async handlePermissionRequestHook(t,e,i){if(!this.isExpectedSessionStateCurrent(e)){i&&(i.disposition="dropped");return}let s=i?.context??this.buildApprovalPromptContextFromHookPayload(t),r=i?.dedupeKey??this.buildApprovalDedupeKey(s);if(this.consumeRecoveryPermissionReplay(e,r,i))return;let n=i?.occurrenceKey??`permission:${(0,ee.v4)()}`,l={ownerState:e,context:s,dedupeKey:r,occurrenceKey:n,registration:i};this.permissionRequestHookInFlight=l;try{let p=await this.handlePermissionRequestHookWhileTracked(t,e,s,n,r);i&&(i.disposition=p)}finally{this.permissionRequestHookInFlight===l&&(this.permissionRequestHookInFlight=null)}}buildApprovalPromptContextFromHookPayload(t){let e=t.metadata||{},i=typeof e.tool_name=="string"?e.tool_name:"Tool",s=e.tool_input,r=this.firstString(s?.command,s?.cmd),n=ne(r)||Y(r),p=(i==="apply_patch"?this.firstString(s?.patch,r,typeof s=="string"?s:void 0):void 0)||n,c=p?"apply_patch":i,d=p||this.stringifyToolInput(s);return{toolName:c,toolInput:s,rawInput:d,filePath:this.extractFilePathFromToolInput(c,s,d),diff:c==="apply_patch"?d:void 0,hint:this.buildPermissionRequestHint(c,s,d)}}async handlePermissionRequestHookWhileTracked(t,e,i=this.buildApprovalPromptContextFromHookPayload(t),s=`permission:${(0,ee.v4)()}`,r){let n=t.metadata||{},{toolName:l,toolInput:p,rawInput:c}=i,d=this.buildToolDetailsForInteractivePrompt(i),u=d.tool_name||this.mapToolNameForApproval(l),m=d.tool_input||this.buildFallbackToolInput(i),y=!!(u&&m);if(r??=this.buildApprovalDedupeKey({toolName:l,toolInput:p,filePath:i.filePath,rawInput:c,hint:i.hint}),this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,s))return"resolved";let f=await this.tryParsePermissionRequestPromptFromTmux((D,A)=>this.parsedPromptMatchesApprovalContext(D,i,A),()=>this.isExpectedSessionStateCurrent(e));if(!this.isExpectedSessionStateCurrent(e))return o.warn("[Hooks] Dropping stale PermissionRequest after session generation changed",{sessionId:e.sessionId,currentSessionId:this.sessionState?.sessionId}),"dropped";if(this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,s))return o.info("[Hooks] Dropping PermissionRequest completed while terminal options were being parsed",{sessionId:e.sessionId,toolName:l,occurrenceKey:s}),"resolved";let v=f?.parsedPrompt??null;if(!v)return o.warn("[Hooks] Suppressing PermissionRequest mobile prompt because exact Codex options are unavailable",{sessionId:e.sessionId,toolName:l,hint:i.hint}),this.raiseSuppressedBadge(e.sessionId,s,{suppressTmuxObserverUntilResolved:!0,observerSnapshot:f?.snapshot,completionKey:r}),"covered";let S=this.mapToolNameForApproval(l)==="Bash"?this.firstString(p?.command,p?.cmd):void 0,P=this.buildCodexPromptPresentation(v,S,f?.snapshot);if(!P)return o.warn("[Hooks] Suppressing PermissionRequest mobile prompt because Codex option hotkeys are unavailable",{sessionId:e.sessionId,toolName:l,hint:i.hint}),this.raiseSuppressedBadge(e.sessionId,s,{suppressTmuxObserverUntilResolved:!0,observerSnapshot:f?.snapshot,completionKey:r}),"covered";let b=s,T=this.buildApprovalPromptContent(P.content,{toolName:u,toolInput:m,hint:i.hint,filePath:i.filePath}),F={promptId:b,kind:P.kind,options:P.options,submitMap:P.submitMap,promptText:T,createdAt:Date.now(),source:"permission_hook",dedupeKey:s,completionKey:r,ownerSessionId:e.sessionId,observerMatchKey:this.buildObserverPromptMatchKey(v),requiresFollowUpText:P.requiresFollowUpText},O={isApprovalHint:!0,toolName:l,toolInput:p,hint:i.hint,filePath:i.filePath,diff:i.diff,rawInput:c,tool_name:u,tool_input:m,has_details:y,options:P.options,instructions:P.instructions,prompt_source:v?"permission_hook_tmux":"permission_hook",permission_mode:n.permission_mode,turn_id:n.turn_id,dedupe_key:s};return o.info("[Hooks] Sending PermissionRequest interactive prompt",{sessionId:e.sessionId,toolName:l,promptId:b,promptSource:O.prompt_source,occurrenceKey:s}),await this.enqueueOrEmitInteractivePrompt({prompt:F,content:T,metadata:O,ownerState:e}),this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,s)?"resolved":"covered"}async emitHooksCompatibilityModeNotice(t){if(this.hooksCompatibilityNoticeSent||!this.isExpectedSessionStateCurrent(t))return;this.hooksCompatibilityNoticeSent=!0;let e="Codex hooks are not active. CodeVibe is using compatibility mode: mobile approvals still mirror the desktop prompt, but timeline events may be delayed or incomplete. Open Codex /hooks and trust/enable CodeVibe hooks for full fidelity.",i={compatibility_mode:!0,reason:"codex_hooks_inactive",action:"trust_enable_codevibe_hooks"};o.warn("Codex hooks inactive; running in compatibility mode",{sessionId:t.sessionId});let s=this.encryptForEmit(e,i,h.EventType.NOTIFICATION,t.sessionId);if(s)try{await this.appSyncClient.createEvent({sessionId:t.sessionId,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,content:s.content,metadata:s.metadata,timestamp:(0,h.prepareEventTimestamp)({orderingKey:t.sessionId}),...s.isEncrypted?{isEncrypted:!0}:{}})}catch(r){o.warn("Failed to emit hooks compatibility mode notification",{error:r})}}async handleApprovalPending(t){let e=this.sessionState;if(!e)return;if((this.queuedPermissionRequestSessionCounts.get(e.sessionId)??0)>0){o.debug("Skipping heuristic approval prompt while PermissionRequest owns the session prompt",{sessionId:e.sessionId,callId:t.callId});return}let i=this.buildApprovalDedupeKey(t);if(i&&this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,i)){o.info("Skipping heuristic approval prompt; same approval was already answered recently",{callId:t.callId,toolName:t.toolName,dedupeKey:i});return}if(i&&this.hasDurablePermissionHookFallbackByDedupeKey(e.sessionId,i)){o.debug("Skipping heuristic approval prompt already owned by a hook fallback",{sessionId:e.sessionId,callId:t.callId,dedupeKey:i});return}if(i&&this.hasActiveOrQueuedPermissionHookPrompt(e.sessionId,i)){this.mergeCallIdIntoPromptByDedupeKey(e.sessionId,i,t.callId),o.info("Skipping heuristic approval prompt; PermissionRequest hook already emitted it",{callId:t.callId,toolName:t.toolName,dedupeKey:i});return}o.info("Sending approval pending interactive prompt",t);try{let s=await this.tryParseInteractivePromptFromTmux(),r=s?.parsedPrompt??null;if((this.queuedPermissionRequestSessionCounts.get(e.sessionId)??0)>0||r&&this.hasMatchingDurablePermissionHookFallback(r,e.sessionId,s?.snapshot)||r&&this.hasMatchingPermissionHookPrompt(r,e.sessionId)){o.debug("Skipping heuristic approval prompt after PermissionRequest claimed the terminal dialog",{sessionId:e.sessionId,callId:t.callId});return}let n=this.buildToolDetailsForInteractivePrompt(t,s?.snapshot),l=n.tool_name||this.mapToolNameForApproval(t.toolName),p=n.tool_input||this.buildFallbackToolInput(t),c=!!(l&&p);if(!r){o.warn("Suppressing heuristic approval prompt because exact Codex options are unavailable",{callId:t.callId,toolName:t.toolName,hint:t.hint}),this.raiseSuppressedBadge(e.sessionId,i);return}if(!this.parsedPromptMatchesApprovalContext(r,t,s?.snapshot)){o.warn("Suppressing heuristic approval prompt because parsed tmux prompt does not match the active tool",{callId:t.callId,toolName:t.toolName,hint:t.hint,parsedPromptText:r.promptText}),this.raiseSuppressedBadge(e.sessionId,i);return}let d=this.mapToolNameForApproval(t.toolName)==="Bash"?this.firstString(t.toolInput?.command,t.toolInput?.cmd):void 0,u=this.buildCodexPromptPresentation(r,d,s?.snapshot);if(!u){o.warn("Suppressing heuristic approval prompt because Codex option hotkeys are unavailable",{callId:t.callId,toolName:t.toolName,hint:t.hint}),this.raiseSuppressedBadge(e.sessionId,i);return}let m=u.options,y=this.buildApprovalPromptContent(u.content,{toolName:l,toolInput:p,hint:t.hint,filePath:t.filePath}),f={promptId:t.callId,callId:t.callId,kind:u.kind,options:m,submitMap:u.submitMap,promptText:u.promptText,createdAt:Date.now(),source:r?"tmux":"heuristic",dedupeKey:i,ownerSessionId:e.sessionId,observerMatchKey:this.buildObserverPromptMatchKey(r),requiresFollowUpText:u.requiresFollowUpText},v={isApprovalHint:!0,toolName:t.toolName,toolInput:t.toolInput,hint:t.hint,callId:t.callId,filePath:t.filePath,diff:t.diff,rawInput:t.rawInput,tool_name:l,tool_input:p,has_details:c,options:m,instructions:u.instructions,prompt_source:r?"tmux":"heuristic",dedupe_key:i};o.debug("Interactive prompt (pre-encryption)",{sessionId:e.sessionId,callId:t.callId,contentPreview:y.substring(0,200),toolDetails:n,metadata:v}),await this.enqueueOrEmitInteractivePrompt({prompt:f,content:y,metadata:v,ownerState:e})}catch(s){o.error("Failed to send approval interactive prompt:",s)}}async handleTmuxPromptCandidate(t,e){let i=this.sessionState;if(!i){this.tmuxPaneObserver.resetLastPromptHash();return}if(!e&&this.deferTmuxPromptCandidateForPermissionHook(i.sessionId,t))return;let s=e?.occurrenceKey;if(s&&this.hasRecentlyResolvedApprovalDedupeKey(i.sessionId,s))return;let r=(0,K.parseInteractivePrompt)(t);if(!r){this.raiseSuppressedBadge(i.sessionId,s??null);return}if(this.hasMatchingPermissionHookPrompt(r,i.sessionId)){o.debug("Skipping tmux prompt candidate already owned by PermissionRequest",{sessionId:i.sessionId,promptText:r.promptText});return}let n=this.getMostRecentPendingToolCall();if(!n){if(await new Promise(O=>setTimeout(O,500)),!this.isExpectedSessionStateCurrent(i)){o.warn("Dropping tmux prompt candidate after session generation changed",{sessionId:i.sessionId,currentSessionId:this.sessionState?.sessionId});return}if(!e&&this.deferTmuxPromptCandidateForPermissionHook(i.sessionId,t))return;n=this.getMostRecentPendingToolCall()}let l=e?.context??(n?this.buildApprovalPromptContextFromPendingCall(n):null);if(l&&!this.parsedPromptMatchesApprovalContext(r,l,t)){o.warn("Skipping tmux-detected prompt because parsed prompt does not match pending tool call",{callId:n?.callId,toolName:n?.name,parsedPromptText:r.promptText}),this.raiseSuppressedBadge(i.sessionId,s??null);return}let p=l?null:this.buildApprovalPromptContextFromParsedPrompt(r),c=l||p;if(!c){o.warn("Skipping tmux-detected prompt because no tool context could be derived from the parsed prompt",{parsedPromptText:r.promptText}),this.raiseSuppressedBadge(i.sessionId,s??null);return}let d=this.mapToolNameForApproval(c.toolName)==="Bash"?this.firstString(c.toolInput?.command,c.toolInput?.cmd):void 0,u=this.buildCodexPromptPresentation(r,d,t);if(!u){o.warn("Skipping tmux-detected prompt because Codex option hotkeys are unavailable",{parsedPromptText:r.promptText}),this.raiseSuppressedBadge(i.sessionId,s??null);return}let m=c?this.buildApprovalDedupeKey(c):void 0;if(m&&this.hasRecentlyResolvedApprovalDedupeKey(i.sessionId,m)){o.info("Skipping tmux-detected prompt; same approval was already answered recently",{promptText:r.promptText,dedupeKey:m});return}if(m&&this.hasActiveOrQueuedPermissionHookPrompt(i.sessionId,m)){n?.callId&&this.mergeCallIdIntoPromptByDedupeKey(i.sessionId,m,n.callId),o.info("Skipping tmux-detected prompt; PermissionRequest hook already emitted it",{promptText:r.promptText,dedupeKey:m});return}let y=c?this.buildToolDetailsForInteractivePrompt(c,t):{},f=y.tool_name||this.mapToolNameForApproval(n?.name),v=y.tool_input||(c?this.buildFallbackToolInput(c):void 0),S=!!(f&&v),b={promptId:n?.callId||(0,ee.v4)(),callId:n?.callId,kind:u.kind,options:u.options,submitMap:u.submitMap,promptText:u.promptText,createdAt:Date.now(),source:"tmux",dedupeKey:m,ownerSessionId:i.sessionId,observerMatchKey:this.buildObserverPromptMatchKey(r),requiresFollowUpText:u.requiresFollowUpText},T={options:u.options,instructions:u.instructions,prompt_source:"tmux_live",toolName:c?.toolName,toolInput:c?.toolInput,hint:c?.hint,filePath:c?.filePath,diff:c?.diff,rawInput:c?.rawInput,tool_name:f,tool_input:v,has_details:S,dedupe_key:m},F=this.buildApprovalPromptContent(u.content,{toolName:f,toolInput:v,hint:c?.hint,filePath:c?.filePath});try{await this.enqueueOrEmitInteractivePrompt({prompt:b,content:F,metadata:T,ownerState:i})&&o.info("Sent tmux-detected interactive prompt",{sessionId:i.sessionId,promptText:r.promptText,kind:r.kind})}catch(O){o.error("Failed to send tmux-detected interactive prompt",{error:O})}}async enqueueOrEmitInteractivePrompt(t){if(!this.isExpectedSessionStateCurrent(t.ownerState))return o.warn("Dropping interactive prompt from stale session generation",{sessionId:t.ownerState.sessionId,currentSessionId:this.sessionState?.sessionId,source:t.prompt.source}),!1;if(t.prompt.ownerSessionId??=t.ownerState.sessionId,t.prompt.dedupeKey&&this.hasRecentlyResolvedApprovalDedupeKey(t.ownerState.sessionId,t.prompt.dedupeKey))return o.debug("Skipping interactive prompt emission; same approval was already answered recently",{source:t.prompt.source,callId:t.prompt.callId,dedupeKey:t.prompt.dedupeKey}),!1;let e=this.pendingInteractivePrompt;if(e&&e.ownerSessionId!==t.ownerState.sessionId&&(o.warn("Discarding active prompt from a stale session generation",{activeSessionId:e.ownerSessionId,candidateSessionId:t.ownerState.sessionId}),this.pendingInteractivePrompt=null,e=null),!e){this.pendingInteractivePrompt=t.prompt;try{return await this.emitInteractivePromptEvent(t),!0}catch(i){throw this.pendingInteractivePrompt?.promptId===t.prompt.promptId&&(this.pendingInteractivePrompt=null),i}}if(this.isSameInteractivePrompt(e,t.prompt)){if(t.prompt.source==="permission_hook"&&e.source!=="permission_hook"){e.source="permission_hook",e.completionKey=t.prompt.completionKey??e.dedupeKey,e.dedupeKey=t.prompt.dedupeKey;let i=this.e1PromptRaises.get(e.promptId);i&&(i.resolutionDedupeKey=e.dedupeKey,i.resolutionCompletionKey=e.completionKey)}return!e.callId&&t.prompt.callId&&(e.callId=t.prompt.callId,o.debug("Merged callId into existing interactive prompt",{source:e.source,callId:t.prompt.callId,promptText:e.promptText})),o.debug("Skipping duplicate interactive prompt emission",{existingSource:e.source,candidateSource:t.prompt.source,existingCallId:e.callId,candidateCallId:t.prompt.callId,promptText:t.prompt.promptText}),!1}return this.queuedInteractivePrompts.some(i=>this.isSameInteractivePrompt(i.prompt,t.prompt))?(o.debug("Skipping duplicate queued interactive prompt",{candidateSource:t.prompt.source,candidateCallId:t.prompt.callId,promptText:t.prompt.promptText}),!1):(this.queuedInteractivePrompts.push(t),o.info("Queued interactive prompt behind active prompt",{activeCallId:e.callId,queuedCallId:t.prompt.callId,queueLength:this.queuedInteractivePrompts.length}),!1)}async emitInteractivePromptEvent(t){if(!this.isExpectedSessionStateCurrent(t.ownerState))return;let e=t.ownerState.sessionId,i=this.e1PromptRaises.get(t.prompt.promptId),s=i?i.record:this.newE1Raise(e,!0,void 0,{...t.prompt.dedupeKey!==void 0?{dedupeKey:t.prompt.dedupeKey}:{},...t.prompt.completionKey!==void 0?{completionKey:t.prompt.completionKey}:{},...t.prompt.callId!==void 0?{callId:t.prompt.callId}:{}});t.prompt.promptId=s.promptId;let r=this.e1PromptRaises.get(s.promptId);r&&(r.resolutionDedupeKey??=t.prompt.dedupeKey,r.resolutionCompletionKey??=t.prompt.completionKey,r.resolutionCallId??=t.prompt.callId),t.prompt.dedupeKey&&this.e1ContentKeyIndex.set(`${e}::${t.prompt.dedupeKey}`,s.promptId),this.stashE1RaiseContent(s.promptId,t.content,t.metadata??{});let n=this.encryptForEmit(t.content,t.metadata,h.EventType.INTERACTIVE_PROMPT,e);n&&await this.trackE1Raise(s.promptId,this.appSyncClient.createEvent({sessionId:e,type:h.EventType.INTERACTIVE_PROMPT,source:h.EventSource.DESKTOP,content:n.content,metadata:n.metadata,...(0,h.raiseFieldsFromRecord)(s),timestamp:(0,h.prepareEventTimestamp)({orderingKey:e}),...n.isEncrypted?{isEncrypted:!0}:{}}))}async emitNextQueuedInteractivePrompt(){if(this.pendingInteractivePrompt||this.queuedInteractivePrompts.length===0)return;let t=this.queuedInteractivePrompts.shift();if(t){if(!this.isExpectedSessionStateCurrent(t.ownerState)){o.warn("Discarding queued prompt from stale session generation",{sessionId:t.ownerState.sessionId,currentSessionId:this.sessionState?.sessionId}),await this.emitNextQueuedInteractivePrompt();return}this.pendingInteractivePrompt=t.prompt;try{await this.emitInteractivePromptEvent(t),o.info("Emitted next queued interactive prompt",{callId:t.prompt.callId,queueLength:this.queuedInteractivePrompts.length})}catch(e){this.pendingInteractivePrompt=null,o.error("Failed to emit queued interactive prompt",{error:e}),await this.emitNextQueuedInteractivePrompt()}}}async clearResolvedInteractivePrompt(t,e){if(!e){o.debug("Interactive prompt completion had no comparable dialog identity",{callId:t});return}let i=this.isPermissionRequestOccurrenceKey(e),s=this.pendingInteractivePrompt,r=!!s&&(s.dedupeKey===e||s.completionKey===e||!i&&!!t&&s.callId===t),n=this.sessionState?.sessionId??s?.ownerSessionId,l=this.queuedInteractivePrompts.find(P=>!i&&!!t&&P.prompt.callId===t||P.prompt.dedupeKey===e||P.prompt.completionKey===e),p=this.permissionRequestHookInFlight,c=p?.ownerState.sessionId===n&&(p?.dedupeKey===e||p?.occurrenceKey===e),u=(n?this.queuedPermissionRequestRegistrations.get(n)??[]:[]).find(P=>P.dedupeKey===e||P.occurrenceKey===e),m=r?s?.dedupeKey:l?.prompt.dedupeKey??(c?p?.occurrenceKey:void 0)??u?.occurrenceKey??e,y=new Set;if(r&&s&&y.add(s.promptId),l&&y.add(l.prompt.promptId),n&&m){let P=this.e1ContentKeyIndex.get(`${n}::${m}`);P&&y.add(P)}let f=[...this.e1PromptRaises.entries()].find(([,P])=>P.record.sessionId===n&&(P.resolutionDedupeKey===m||!i&&!!t&&P.resolutionCallId===t||P.resolutionCompletionKey===e));if(f&&y.add(f[0]),(r||c||u||y.size>0)&&this.rememberResolvedApprovalDedupeKey(n,m),r&&s&&(this.pendingInteractivePrompt=null),u&&(u.disposition="resolved"),m)for(let P of this.removeQueuedInteractivePromptByDedupeKey(m))y.add(P);let S=n?this.beginE1PromptResolutions(y,n):[];this.launchE1Resolutions(S),(r||!this.pendingInteractivePrompt&&l)&&await this.emitNextQueuedInteractivePrompt()}clearInteractivePromptsForTurnAbort(t){let e=t.sessionId,i=new Set,s=this.pendingInteractivePrompt;s?.ownerSessionId===e&&(i.add(s.promptId),this.rememberResolvedApprovalDedupeKey(e,s.dedupeKey),this.pendingInteractivePrompt=null);let r=[];for(let l of this.queuedInteractivePrompts)l.prompt.ownerSessionId===e?(i.add(l.prompt.promptId),this.rememberResolvedApprovalDedupeKey(e,l.prompt.dedupeKey)):r.push(l);this.queuedInteractivePrompts=r;for(let[l,p]of this.e1PromptRaises.entries())p.record.sessionId===e&&i.add(l);for(let l of this.queuedPermissionRequestRegistrations.get(e)??[])l.disposition="resolved",this.rememberResolvedApprovalDedupeKey(e,l.occurrenceKey),this.deferredTmuxPromptCandidates.delete(l);this.permissionRequestOccurrencesByCompletionKey.clear();let n=this.beginE1PromptResolutions(i,e);this.launchE1Resolutions(n)}removeQueuedInteractivePromptByDedupeKey(t){let e=this.queuedInteractivePrompts.length,i=this.queuedInteractivePrompts.filter(s=>s.prompt.dedupeKey===t);for(let s of i)this.rememberResolvedApprovalDedupeKey(s.ownerState.sessionId,s.prompt.dedupeKey);return this.queuedInteractivePrompts=this.queuedInteractivePrompts.filter(s=>s.prompt.dedupeKey!==t),this.queuedInteractivePrompts.length!==e&&o.debug("Removed resolved deduped tool call from interactive prompt queue",{dedupeKey:t,removed:e-this.queuedInteractivePrompts.length}),i.map(s=>s.prompt.promptId)}mergeCallIdIntoPromptByDedupeKey(t,e,i){let s=this.pendingInteractivePrompt;if(s?.ownerSessionId===t&&(s.completionKey??s.dedupeKey)===e&&!s.callId){s.callId=i;let n=this.e1PromptRaises.get(s.promptId);n&&(n.resolutionCallId??=i),o.debug("Merged callId into active deduped interactive prompt",{source:s.source,callId:i,dedupeKey:e});return}let r=this.queuedInteractivePrompts.find(n=>n.prompt.ownerSessionId===t&&(n.prompt.completionKey??n.prompt.dedupeKey)===e&&!n.prompt.callId);r&&(r.prompt.callId=i,o.debug("Merged callId into queued deduped interactive prompt",{source:r.prompt.source,callId:i,dedupeKey:e}))}isSameInteractivePrompt(t,e){if(t.ownerSessionId!==e.ownerSessionId||Date.now()-t.createdAt>Ei)return!1;if(t.callId&&e.callId)return t.callId===e.callId;let s=t.completionKey??t.dedupeKey,r=e.completionKey??e.dedupeKey;return t.source!==e.source&&s&&s===r&&t.observerMatchKey&&t.observerMatchKey===e.observerMatchKey?!0:t.dedupeKey&&e.dedupeKey?t.dedupeKey===e.dedupeKey:t.kind===e.kind&&this.normalizePromptDedupeText(t.promptText)===this.normalizePromptDedupeText(e.promptText)}normalizePromptDedupeText(t){return t.replace(/\s+/g," ").trim().toLowerCase()}async startTmuxObserver(){let t=process.env.CODEVIBE_CODEX_TMUX_SESSION;if(!t)return o.debug("Skipping tmux pane observer start - no tmux session in environment"),!0;try{return await this.tmuxPaneObserver.start(t),!0}catch(e){return o.warn("Failed to start tmux pane observer",{tmuxSession:t,error:e}),!1}}async startTmuxObserverWithBeacon(t){try{await this.startTmuxObserver()?await R("daemon_init_step_completed",{step:"tmux_observer_start",path:t}):await R("daemon_init_step_failed",{step:"tmux_observer_start",path:t,error_class:"TmuxObserverStartFailed",error_message:"tmuxPaneObserver.start threw (see plugin log)"})}catch(e){await R("daemon_init_step_failed",{step:"tmux_observer_start",path:t,error_class:e?.name||"Error",error_message:e?.message||String(e)}),o.error("Failed to start tmux observer:",e)}}async tryParseInteractivePromptFromTmux(){try{let t=await this.tmuxPaneObserver.captureSnapshot(),e=this.tmuxPaneObserver.extractActiveApprovalBlock(t),i=e?(0,K.parseInteractivePrompt)(e):null;return o.debug("tmux prompt parse result",{parsed:!!i,kind:i?.kind,promptText:i?.promptText,isolated:!!e,snapshotPreview:this.summarizePromptSnapshot(e??t)}),{parsedPrompt:i,snapshot:e??t}}catch(t){return o.debug("tmux prompt parsing unavailable",{error:t}),null}}async tryParsePermissionRequestPromptFromTmux(t,e){let r=null;for(let n=0;n<25;n+=1){if(e&&!e()||n>0&&(await new Promise(p=>setTimeout(p,125)),e&&!e()))return null;let l=await this.tryParseInteractivePromptFromTmux();if(l&&(r=l),l?.parsedPrompt&&(!t||t(l.parsedPrompt,l.snapshot)))return l;l?.parsedPrompt&&o.warn("Ignoring parsed PermissionRequest prompt because it does not match the active tool",{attempt:n+1,promptText:l.parsedPrompt.promptText})}return r?.snapshot?{parsedPrompt:null,snapshot:r.snapshot}:null}buildPromptPresentation(t){return t?{content:t.promptText,promptText:t.promptText,kind:t.kind,options:t.options,submitMap:t.submitMap,instructions:this.buildPromptInstructions(t),requiresFollowUpText:t.requiresFollowUpText}:{content:"Codex is waiting for approval.",promptText:"Codex is waiting for approval.",kind:"yes_no",options:[{number:"1",text:'Yes (sends "y")'},{number:"2",text:'No, tell Codex what to change (sends "n <instructions>")'}],submitMap:{1:"y",2:"n"},instructions:"Reply with 1 to approve, or 2 followed by what to change",requiresFollowUpText:!0}}buildCodexPromptPresentation(t,e,i){let s=this.buildPromptPresentation(t),r=Rt(s.options,e,i);if(!r)return null;let n=this.buildSubmitMapFromCodexOptionHotkeys(r);return n?{...s,options:r,submitMap:n,instructions:this.buildCodexPermissionInstructions(r),requiresFollowUpText:this.optionsRequireFollowUpText(r)}:null}buildSubmitMapFromCodexOptionHotkeys(t){let e={};for(let i of t){let s=i.text.match(/\(([^)]+)\)\s*$/)?.[1]?.trim().toLowerCase();if(!s||!Ri.test(s))return null;s==="esc"||s==="escape"?e[i.number]=B:e[i.number]=s}return e}buildCodexPermissionInstructions(t){let e=t.find(n=>/\((?:y|yes)\)\s*$/i.test(n.text)),i=t.find(n=>/\(p\)\s*$/i.test(n.text)),s=t.find(n=>/\((?:esc|escape)\)\s*$/i.test(n.text)),r=[];return e&&r.push(`${e.number} to approve`),i&&r.push(`${i.number} to persist the desktop allow rule`),s&&r.push(`${s.number} followed by what to change`),r.length>0?`Reply with ${r.join(", ")}`:"Reply with the number of the option you want"}optionsRequireFollowUpText(t){return t.some(e=>/what to (?:do differently|change)|instructions/i.test(e.text))}getMostRecentPendingToolCall(){let t=this.approvalDetector.getPendingCalls();return t.length===0?null:t.reduce((e,i)=>i.timestamp>e.timestamp?i:e)}hasActiveOrQueuedPermissionHookPrompt(t,e){let i=this.pendingInteractivePrompt;return i?.ownerSessionId===t&&i.source==="permission_hook"&&(i.completionKey??i.dedupeKey)===e?!0:this.queuedInteractivePrompts.some(s=>s.prompt.ownerSessionId===t&&s.prompt.source==="permission_hook"&&(s.prompt.completionKey??s.prompt.dedupeKey)===e)}hasDurablePermissionHookFallback(t){return[...this.e1PromptRaises.values()].some(e=>e.record.sessionId===t&&e.suppressTmuxObserverUntilResolved===!0)}hasDurablePermissionHookFallbackByDedupeKey(t,e){return[...this.e1PromptRaises.values()].some(i=>i.record.sessionId===t&&i.suppressTmuxObserverUntilResolved===!0&&(i.resolutionDedupeKey===e||i.resolutionCompletionKey===e))}hasMatchingDurablePermissionHookFallback(t,e,i){let s=this.buildApprovalPromptContextFromParsedPrompt(t),r=s?this.buildApprovalDedupeKey(s):void 0;if(r&&this.hasDurablePermissionHookFallbackByDedupeKey(e,r))return!0;if(!i)return!1;let n=this.buildObserverSnapshotMatchKey(i);return[...this.e1PromptRaises.values()].some(l=>l.record.sessionId===e&&l.suppressTmuxObserverUntilResolved===!0&&l.suppressedObserverSnapshotKey===n)}deferTmuxPromptCandidateForPermissionHook(t,e){let i=this.queuedPermissionRequestSessionCounts.get(t)??0,s=this.hasDurablePermissionHookFallback(t);if(i<=0&&!s)return!1;let r=(0,K.parseInteractivePrompt)(e);if(r&&this.hasMatchingDurablePermissionHookFallback(r,t,e))return o.debug("Skipping tmux repaint already owned by a durable PermissionRequest fallback",{sessionId:t,promptText:r.promptText}),!0;if(r&&this.hasMatchingPermissionHookPrompt(r,t))return o.debug("Skipping tmux repaint already owned by an actionable PermissionRequest",{sessionId:t,promptText:r.promptText}),!0;if(r&&i<=0)return!1;if(i>0){let n=this.queuedPermissionRequestRegistrations.get(t)??[],l=r?n.find(d=>this.parsedPromptMatchesApprovalContext(r,d.context,e)):void 0,p=!r&&this.permissionRequestHookInFlight?.ownerState.sessionId===t?this.permissionRequestHookInFlight.registration:void 0,c=l??p??(n.length===1&&(!r||!s)?n[0]:void 0);if(c&&this.deferredTmuxPromptCandidates.set(c,e),r&&!c)return!1}return o.debug("Skipping tmux prompt candidate while PermissionRequest owns the session prompt",{sessionId:t,toolName:this.permissionRequestHookInFlight?.context.toolName,ownership:i>0?"queued_or_in_flight":"durable_prompt"}),!0}hasMatchingPermissionHookPrompt(t,e){let i=this.buildObserverPromptMatchKey(t);return[this.pendingInteractivePrompt,...this.queuedInteractivePrompts.map(r=>r.prompt)].some(r=>r?.source==="permission_hook"&&r.ownerSessionId===e&&r.observerMatchKey===i)}buildObserverPromptMatchKey(t){let e=t.options.map(s=>`${s.number}:${this.normalizeObserverPromptText(s.text)}`).join("|"),i=[t.kind,this.normalizeObserverPromptText(t.promptText),e].join("::");return`observer:${ye.createHash("sha256").update(i).digest("hex").slice(0,16)}`}buildObserverSnapshotMatchKey(t){let e=this.normalizeObserverPromptText(t);return`snapshot:${ye.createHash("sha256").update(e).digest("hex").slice(0,16)}`}normalizeObserverPromptText(t){return t.replace(/\r\n?/g,`
21
+ `).trim();return(0,J.createHash)("sha256").update(i).digest("hex")}};var B=require("@quantiya/codevibe-core");var Ce=E(require("express")),$=E(require("fs")),at=E(require("path")),lt=E(require("os"));var ci=1800*1e3,di=60*1e3,ui=1440*60*1e3;var ce=class{constructor(){this.assignedPort=0;this.portRefreshTimer=null;this.app=(0,Ce.default)(),this.setupMiddleware(),this.setupRoutes(),this.tmuxSession=process.env.CODEVIBE_CODEX_TMUX_SESSION}getPort(){return this.assignedPort}setupMiddleware(){this.app.use(Ce.default.json({limit:"1mb"})),this.app.use((t,e,i)=>{o.debug(`${t.method} ${t.path}`,{body:t.body}),i()})}setupRoutes(){this.app.get("/health",(t,e)=>{e.json({success:!0,service:"codevibe-codex",pid:process.pid,data:{status:"healthy",uptime:process.uptime()}})}),this.app.post("/event",this.handleEvent.bind(this))}async handleEvent(t,e){try{let i=t.body;if(!i.session_id||!i.hook_event_name){e.status(400).json({success:!1,error:"Missing session_id or hook_event_name"});return}let s=this.transformHookToEvent(i);if(o.info("Received hook event",{sessionId:i.session_id,hookEvent:i.hook_event_name,type:s.type}),this.eventHandler){let r=this.eventHandler(s);if(i.hook_event_name==="PermissionRequest"){r.catch(n=>{o.error("Error resolving PermissionRequest after acknowledgement:",n)}),e.json({success:!0});return}await r}e.json({success:!0})}catch(i){o.error("Error handling event:",i),e.status(500).json({success:!1,error:i instanceof Error?i.message:"Unknown error"})}}transformHookToEvent(t){let e={cwd:t.cwd,hook_event_name:t.hook_event_name,transcript_path:t.transcript_path,...t.metadata||{}},i,s;switch(t.hook_event_name){case"SessionStart":i="NOTIFICATION",s="Session started",e.source=t.source,e.model=t.model;break;case"UserPromptSubmit":i="USER_PROMPT",s=t.prompt||"";break;case"PreToolUse":i="NOTIFICATION",s="PreToolUse observed",e.tool_name=t.tool_name,e.tool_input=t.tool_input,e.tool_use_id=t.tool_use_id,e.approval_status="observed_pre_tool",e.requires_user_action=!1;break;case"PermissionRequest":i="NOTIFICATION",s="PermissionRequest observed",e.tool_name=t.tool_name,e.tool_input=t.tool_input,e.permission_mode=t.permission_mode,e.turn_id=t.turn_id,e.requires_user_action=!0;break;case"PostToolUse":i="TOOL_USE",s=JSON.stringify({tool_name:t.tool_name,tool_input:t.tool_input,tool_response:t.tool_response}),e.tool_name=t.tool_name,e.tool_input=t.tool_input,e.tool_use_id=t.tool_use_id;break;case"Stop":i="ASSISTANT_RESPONSE",s=t.last_assistant_message||"";break;default:i="NOTIFICATION",s=`Hook: ${t.hook_event_name}`}return{session_id:t.session_id,hook_event_name:t.hook_event_name,type:i,source:"DESKTOP",content:s,metadata:e}}onEvent(t){this.eventHandler=t}async start(){return new Promise((t,e)=>{try{this.server=this.app.listen(0,"localhost",()=>{let i=this.server.address();this.assignedPort=i.port,o.info(`HTTP API listening on http://localhost:${this.assignedPort}`),this.writePortFile(this.assignedPort),this.startPortFileKeepalive(),t(this.assignedPort)}),this.server.on("error",i=>{o.error("HTTP server error:",i),e(i)})}catch(i){e(i)}})}portFilePath(){return this.tmuxSession?at.join(lt.tmpdir(),`codevibe-codex-${this.tmuxSession}.port`):null}writePortFile(t){let e=this.portFilePath();if(!e){o.warn("No CODEVIBE_CODEX_TMUX_SESSION set, skipping port file");return}try{$.writeFileSync(e,t.toString()),o.info(`Port file written: ${e} -> ${t}`)}catch(i){o.error(`Failed to write port file: ${e}`,i)}}removePortFile(){let t=this.portFilePath();if(t)try{$.existsSync(t)&&($.unlinkSync(t),o.info(`Port file removed: ${t}`))}catch(e){o.warn(`Failed to remove port file: ${t}`,e)}}startPortFileKeepalive(){this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null);let t=Number(process.env.CODEVIBE_PORTFILE_REFRESH_MS),e=Number.isFinite(t)&&t>0?Math.min(ui,Math.max(di,t)):ci;this.portRefreshTimer=setInterval(()=>this.refreshPortFile(),e)}refreshPortFile(){let t=this.portFilePath();if(t)try{let e=new Date;$.utimesSync(t,e,e)}catch(e){e?.code==="ENOENT"?this.writePortFile(this.assignedPort):o.warn(`Port-file refresh failed: ${t}`,e)}}async stop(){return this.portRefreshTimer&&(clearInterval(this.portRefreshTimer),this.portRefreshTimer=null),this.removePortFile(),new Promise(t=>{this.server?this.server.close(()=>{o.info("HTTP API stopped"),t()}):t()})}};var de=class{constructor(t={}){this.pendingBySession=new Map;this.expiryMs=t.expiryMs??1e4,this.minFuzzyEchoLength=t.minFuzzyEchoLength??16,this.minFuzzyEchoRatio=t.minFuzzyEchoRatio??.35,this.now=t.now??Date.now}track(t,e){let i=this.normalize(e);if(!i)return;let s=this.validEntries(t);s.push({normalized:i,timestamp:this.now()}),this.pendingBySession.set(t,s)}forget(t,e){let i=this.normalize(e);if(!i)return;let s=!1,r=this.validEntries(t).filter(n=>!s&&n.normalized===i?(s=!0,!1):!0);this.replaceEntries(t,r)}consumeIfDuplicate(t,e){let i=this.normalize(e);if(!i)return null;let s=null,r=this.validEntries(t).filter(n=>{if(!s){let l=this.matchType(n.normalized,i);if(l)return s={matchType:l,originalLength:n.normalized.length,echoLength:i.length},!1}return!0});return this.replaceEntries(t,r),s}validEntries(t){let e=this.now()-this.expiryMs;return(this.pendingBySession.get(t)||[]).filter(i=>i.timestamp>=e)}replaceEntries(t,e){e.length>0?this.pendingBySession.set(t,e):this.pendingBySession.delete(t)}matchType(t,e){if(t===e)return"exact";let i=e.length>=this.minFuzzyEchoLength,s=e.length/t.length>=this.minFuzzyEchoRatio;return i&&s&&t.endsWith(e)?"suffix":null}normalize(t){return t.replace(/\s+/g," ").trim()}};var ct=E(require("crypto")),dt=E(require("fs")),ut=E(require("https")),ue=E(require("os")),ht=E(require("path")),hi="G-GS74YEQTB8",mi="lAfOF6OxRzSQ-NsLBRjhAg",fi="www.google-analytics.com",gi=`/mp/collect?measurement_id=${hi}&api_secret=${mi}`,pt=800;function yi(){try{let a=ht.resolve(__dirname,"..","package.json"),t=dt.readFileSync(a,"utf-8"),e=JSON.parse(t);if(typeof e.version=="string"&&e.version.length>0&&e.version.length<30)return e.version}catch{}return"unknown"}var vi=yi();function Si(){let a=typeof process.getuid=="function"?process.getuid():0;return ct.createHash("sha256").update(`${ue.hostname()}-${a}`).digest("hex").substring(0,36)}function Pi(a){if(!a)return"";let t=ue.homedir(),e=a.replace(/[\n\r\t]/g," ");if(t&&t.length>0){let i=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");e=e.replace(new RegExp(i,"g"),"~")}return e=e.replace(/\/Users\/[^/\s"'`]+/g,"/Users/<user>").replace(/\/home\/[^/\s"'`]+/g,"/home/<user>").replace(/[^\x20-\x7E]/g,""),e.trim().substring(0,100)}function R(a,t){return new Promise(e=>{let i,s=!1,r=()=>{s||(s=!0,clearTimeout(n),e())},n=setTimeout(()=>{try{i?.destroy()}catch{}r()},pt);typeof n.unref=="function"&&n.unref();try{let l={...t};typeof l.error_message=="string"&&(l.error_message=Pi(l.error_message));let p=JSON.stringify({client_id:Si(),events:[{name:a,params:{agent:"codex",plugin_version:vi,platform:process.platform,source:process.env.CODEVIBE_TELEMETRY_SOURCE||"production",...l}}]});i=ut.request({hostname:fi,path:gi,method:"POST",headers:{"Content-Type":"application/json"},timeout:pt},c=>{c.resume(),c.on("end",r),c.on("close",r),c.on("error",r)}),i.on("error",r),i.on("timeout",()=>{try{i?.destroy()}catch{}r()}),i.on("close",r),i.write(p),i.end()}catch{r()}})}var he=class{constructor(){this.originated=new Set;this.externallyResolved=new Set}markOriginated(t){this.originated.add(t)}clearOriginated(t){this.originated.delete(t)}getOriginated(){return new Set(this.originated)}getExternallyResolved(){return new Set(this.externallyResolved)}isExternallyResolved(t){return this.externallyResolved.has(t)}dispatch(t){let{gateId:e}=t;return this.originated.has(e)?(this.originated.delete(e),this.externallyResolved.add(e),"self_echo"):(this.externallyResolved.add(e),"foreign")}clear(){this.originated.clear(),this.externallyResolved.clear()}};function mt(a){return`[CodeVibe] Decision recorded on another device: ${a} \u2014 type any number to dismiss the open AskUserQuestion`}var Z=E(require("crypto")),I=E(require("fs")),fe=E(require("os")),M=E(require("path")),yt=require("@quantiya/codevibe-core"),Ae=2*1024*1024,Ii=600*1e3,U=1e3;function vt(){return typeof process.getuid=="function"?process.getuid():void 0}function ke(a,t){let e=vt();if(e!==void 0&&a.uid!==e)throw new Error(`E1_STATE_FOREIGN_OWNER: ${t}`)}function St(a){I.mkdirSync(a,{recursive:!0,mode:448});let t=I.lstatSync(a);if(!t.isDirectory()||t.isSymbolicLink())throw new Error(`E1_STATE_ROOT_NOT_PRIVATE_DIRECTORY: ${a}`);ke(t,a),process.platform!=="win32"&&I.chmodSync(a,448)}function ft(a,t){let e=M.dirname(a);St(e);let i=`${JSON.stringify(t)}
22
+ `;if(Buffer.byteLength(i,"utf8")>Ae)throw new Error(`E1_STATE_TOO_LARGE: ${a}`);let s=M.join(e,`.${M.basename(a)}.${process.pid}.${Z.randomUUID()}.tmp`),r=I.constants.O_NOFOLLOW??0,n=null;try{n=I.openSync(s,I.constants.O_WRONLY|I.constants.O_CREAT|I.constants.O_EXCL|r,384),I.writeFileSync(n,i,{encoding:"utf8"}),I.fsyncSync(n),I.closeSync(n),n=null,process.platform!=="win32"&&I.chmodSync(s,384),I.renameSync(s,a),process.platform!=="win32"&&I.chmodSync(a,384);try{let l=I.openSync(e,I.constants.O_RDONLY);try{I.fsyncSync(l)}finally{I.closeSync(l)}}catch{}}finally{n!==null&&I.closeSync(n);try{I.unlinkSync(s)}catch(l){if(l.code!=="ENOENT")throw l}}}function gt(a){let t;try{t=I.lstatSync(a)}catch(s){if(s.code==="ENOENT")return null;throw s}if(!t.isFile()||t.isSymbolicLink())throw new Error(`E1_STATE_NOT_REGULAR_FILE: ${a}`);if(ke(t,a),process.platform!=="win32"&&(t.mode&63)!==0)throw new Error(`E1_STATE_PERMISSIONS_LOOSE: ${a}`);if(t.size>Ae)throw new Error(`E1_STATE_TOO_LARGE: ${a}`);let e=I.constants.O_NOFOLLOW??0,i=I.openSync(a,I.constants.O_RDONLY|e);try{let s=I.fstatSync(i);if(ke(s,a),!s.isFile()||t.dev!==s.dev||t.ino!==s.ino)throw new Error(`E1_STATE_FILE_CHANGED_DURING_OPEN: ${a}`);if(s.size>Ae)throw new Error(`E1_STATE_TOO_LARGE: ${a}`);return JSON.parse(I.readFileSync(i,{encoding:"utf8"}))}finally{I.closeSync(i)}}function L(a){return a!==null&&typeof a=="object"&&!Array.isArray(a)}function x(a,t=256*1024){return typeof a=="string"&&Buffer.byteLength(a,"utf8")<=t}function Pt(a){if(!L(a)||!x(a.promptId,512)||!x(a.ownerToken,512)||!x(a.sessionId,1024)||a.producerKind!=="PLUGIN_APPROVAL"||a.agentType!==void 0&&a.agentType!=="CODEX"||a.title!==void 0&&!x(a.title,4096)||typeof a.mobileActionable!="boolean"||typeof a.raisedAtMs!="number"||!Number.isFinite(a.raisedAtMs))return!1;try{return(0,yt.deriveOpenPromptId)(a.ownerToken)===a.promptId}catch{return!1}}function bi(a){if(!L(a)||!Pt(a.record))return!1;for(let t of["contentPlain","resolutionDedupeKey","resolutionCompletionKey","resolutionCallId","suppressedObserverSnapshotKey"])if(a[t]!==void 0&&!x(a[t]))return!1;return!(a.metadataPlain!==void 0&&!L(a.metadataPlain)||a.suppressTmuxObserverUntilResolved!==void 0&&typeof a.suppressTmuxObserverUntilResolved!="boolean")}function It(a){return L(a)&&Pt(a.record)&&x(a.resolutionAt,128)&&!Number.isNaN(Date.parse(a.resolutionAt))&&typeof a.attempts=="number"&&Number.isSafeInteger(a.attempts)&&a.attempts>=0}function bt(a){if(!L(a)||!x(a.promptId,512)||!["yes_no","numbered","text"].includes(String(a.kind))||!Array.isArray(a.options)||a.options.length>100||!a.options.every(t=>L(t)&&x(t.number,32)&&x(t.text,32*1024))||!L(a.submitMap)||!Object.entries(a.submitMap).every(([t,e])=>x(t,128)&&x(e,32*1024))||!x(a.promptText)||typeof a.createdAt!="number"||!Number.isFinite(a.createdAt)||!["permission_hook","tmux","heuristic"].includes(String(a.source)))return!1;for(let t of["callId","dedupeKey","completionKey","ownerSessionId","observerMatchKey"])if(a[t]!==void 0&&!x(a[t],32*1024))return!1;return a.requiresFollowUpText===void 0||typeof a.requiresFollowUpText=="boolean"}function wi(a){return L(a)&&bt(a.prompt)&&x(a.content)&&L(a.metadata)}function Ei(a,t,e){if(!L(a)||a.version!==1)throw new Error("E1_HANDOFF_UNSUPPORTED_VERSION");if(a.tmuxSession!==t||!x(a.backendSessionId,1024))throw new Error("E1_HANDOFF_INSTANCE_MISMATCH");if(typeof a.createdAtMs!="number"||!Number.isFinite(a.createdAtMs))throw new Error("E1_HANDOFF_INVALID_TIMESTAMP");if(a.createdAtMs>e+6e4||e-a.createdAtMs>Ii)throw new Error("E1_HANDOFF_STALE");if(!Array.isArray(a.openRaises)||a.openRaises.length>U||!a.openRaises.every(bi))throw new Error("E1_HANDOFF_INVALID_OPEN_RAISES");if(!Array.isArray(a.pendingResolutions)||a.pendingResolutions.length>U||!a.pendingResolutions.every(It))throw new Error("E1_HANDOFF_INVALID_PENDING_RESOLUTIONS");if(!Array.isArray(a.needsReRaisePromptIds)||a.needsReRaisePromptIds.length>U||!a.needsReRaisePromptIds.every(s=>x(s,512)))throw new Error("E1_HANDOFF_INVALID_RERAISE_IDS");if(!Array.isArray(a.permissionOccurrences)||a.permissionOccurrences.length>U)throw new Error("E1_HANDOFF_INVALID_OCCURRENCES");if(!a.permissionOccurrences.every(s=>Array.isArray(s)&&s.length===2&&x(s[0],256*1024)&&Array.isArray(s[1])&&s[1].length<=U&&s[1].every(r=>L(r)&&x(r.occurrenceKey,512)&&typeof r.hookObserved=="boolean"&&typeof r.jsonlObserved=="boolean")))throw new Error("E1_HANDOFF_INVALID_OCCURRENCES");if(a.pendingInteractivePrompt!==null&&!bt(a.pendingInteractivePrompt))throw new Error("E1_HANDOFF_INVALID_ACTIVE_PROMPT");if(!Array.isArray(a.queuedInteractivePrompts)||a.queuedInteractivePrompts.length>U||!a.queuedInteractivePrompts.every(wi))throw new Error("E1_HANDOFF_INVALID_QUEUED_PROMPTS");if(!Array.isArray(a.resolvedApprovalDedupeKeys)||a.resolvedApprovalDedupeKeys.length>U||!a.resolvedApprovalDedupeKeys.every(s=>Array.isArray(s)&&s.length===2&&x(s[0])&&typeof s[1]=="number"&&Number.isFinite(s[1])))throw new Error("E1_HANDOFF_INVALID_RESOLVED_KEYS");return a}function Ri(a){if(!L(a)||a.version!==1||!It(a.pending))throw new Error("E1_RESOLUTION_OUTBOX_INVALID_RECORD");if(typeof a.updatedAtMs!="number"||!Number.isFinite(a.updatedAtMs))throw new Error("E1_RESOLUTION_OUTBOX_INVALID_TIMESTAMP");return a}var me=class{constructor(t={}){let e=vt()??"nouid";this.handoffRoot=t.handoffRoot??M.join(fe.tmpdir(),`codevibe-codex-handoff-${e}`),this.resolutionRoot=t.resolutionRoot??M.join(fe.homedir(),".codevibe","codex","e1-resolution-outbox"),this.now=t.now??Date.now}handoffPath(t){let e=Z.createHash("sha256").update(t).digest("hex").slice(0,32);return M.join(this.handoffRoot,`${e}.json`)}resolutionPath(t){let e=Z.createHash("sha256").update(t).digest("hex");return M.join(this.resolutionRoot,`${e}.json`)}writeHandoff(t){ft(this.handoffPath(t.tmuxSession),t)}readHandoff(t){let e=gt(this.handoffPath(t));if(e===null)return null;try{return Ei(e,t,this.now())}catch(i){if(i.message==="E1_HANDOFF_STALE")return this.removeHandoff(t),null;throw i}}removeHandoff(t){try{I.unlinkSync(this.handoffPath(t))}catch(e){if(e.code!=="ENOENT")throw e}}writeResolution(t){ft(this.resolutionPath(t.record.promptId),{version:1,updatedAtMs:this.now(),pending:t})}removeResolution(t){try{I.unlinkSync(this.resolutionPath(t))}catch(e){if(e.code!=="ENOENT")throw e}}readResolutions(){let t;try{St(this.resolutionRoot),t=I.readdirSync(this.resolutionRoot).filter(s=>s.endsWith(".json"))}catch(s){if(s.code==="ENOENT")return{records:[],rejected:[]};throw s}let e=[],i=[];for(let s of t.slice(0,U)){let r=M.join(this.resolutionRoot,s);try{let n=gt(r);if(n!==null){let l=Ri(n).pending;if(M.basename(this.resolutionPath(l.record.promptId))!==s)throw new Error("E1_RESOLUTION_OUTBOX_IDENTITY_MISMATCH");e.push(l)}}catch(n){i.push({file:r,reason:n instanceof Error?n.message:String(n)})}}return t.length>U&&i.push({file:this.resolutionRoot,reason:`E1_RESOLUTION_OUTBOX_RECORD_LIMIT: ${t.length}`}),{records:e,rejected:i}}};var Et=(0,Me.promisify)(Pe.exec),_i=(0,Me.promisify)(Pe.execFile),Ti="/quit",H="CODEVIBE_CODEX_TMUX_SESSION",ge="CODEVIBE_CODEX_DAEMON_RECOVERY",xi=1e3,Oi=1e4,ye=3,Ci=100,Rt=["SIGINT","SIGTERM","SIGHUP"],Ai=600*1e3,Fe=30*1e3,ki=/^(?:[a-z0-9]|esc|escape)$/;function wt(a,t){let e=a?.[t];return typeof e=="string"?e:Buffer.isBuffer(e)?e.toString("utf8"):""}function _t(a){if(typeof a?.code!="number")return"unknown";let t=`${wt(a,"stderr")}
23
+ ${wt(a,"stdout")}`;return/^can't find session:/m.test(t)||/^no server running on /m.test(t)||/^error connecting to .* \((?:No such file or directory|Connection refused)\)$/m.test(t)?"absent":"unknown"}async function Fi(a,t){let e=async(i,s)=>{try{await Et(i)}catch(r){o.warn("tmux send-keys failed during self-terminate",{sessionName:a,label:s,error:String(r)})}};await e(`tmux send-keys -t "${a}" C-c`,"ctrl-c"),await new Promise(i=>setTimeout(i,200)),await e(`tmux send-keys -t "${a}" -l "${t}"`,"quit-text"),await new Promise(i=>setTimeout(i,500)),await e(`tmux send-keys -t "${a}" Enter`,"enter")}function Ke(a){let t=a.split(`
24
+ `),e=n=>/^[\s>]*\d+\.\s/.test(n),i=-1;for(let n=t.length-1;n>=0;n-=1)if(/^\s*\$\s+/.test(t[n])){i=n;break}if(i===-1)return;let s=t[i].trim().match(/^\$\s+(.+)$/);if(!s?.[1]?.trim())return;let r=[s[1].trim()];for(let n=i+1;n<t.length;n+=1){let l=t[n];if(e(l)||/^\s*$/.test(l)||/^\s*\$\s+/.test(l))break;r.push(l.trim())}return r.length>0?r:void 0}function Tt(a){let t=Ke(a);if(!t)return;let e=t.join(" ").trim();return e.length>0?e:void 0}function xt(a,t){return Di(a,t).has(t)}function Di(a,t){if(a.length===0)return new Set;let e=t,i=new Set([a[0]]);for(let s of a.slice(1)){let r=s,n=new Set;for(let l of i)for(let p of[""," "]){let c=`${l}${p}${r}`;e.startsWith(c)&&n.add(c)}if(n.size===0)return new Set;i=n}return i}function Ni(a,t,e){let i=/^\[\u2026\s+\d+\s+lines\]\s+ctrl\s*\+\s*a\s+view\s+all$/i,s=a.split(`
25
+ `).map(r=>r.trim()).filter(Boolean);return s.length<2||!i.test(s[s.length-1])||!e?!1:Mi(e,t)}function Mi(a,t){let e=Ne(t),i=Ct(a,"2");if(!e||!i)return!1;let s=new Set;for(let r of At(i)){let n=r.match(/^Yes, and don't ask again for commands that start with `([^`]*)`\s*\(p\)$/);if(!n)continue;let l=Ne(n[1]);l&&JSON.stringify(l)===JSON.stringify(e)&&s.add(JSON.stringify(l))}return s.size===1}function Ot(a,t,e){if(!a.find(u=>u.number==="2"&&/\(p\)\s*$/i.test(u.text)))return a;let s="Yes, and don't ask again for commands that start with",r="Yes, and don't ask again (p)",n=Ct(e,"2");if(!n)return null;let l=At(n);if(l.has(r))return a.map(u=>u.number==="2"?{...u,text:r}:u);if(!t)return null;let p=new Map;for(let u of l){let m=u.match(/^Yes, and don't ask again for commands that start with `([^`]*)`\s*\(p\)$/);if(!m)continue;let y=Ne(m[1]);!y||!Ki(y,t)||p.set(JSON.stringify(y),y)}if(p.size!==1)return null;let c=[...p.values()][0],d=Li(c);return a.map(u=>u.number==="2"?{...u,text:`${s} \`${d}\` (p)`}:u)}function Ct(a,t){if(!a)return null;let e=(0,B.normalizeSnapshot)(a).split(`
26
+ `).map(l=>l.trim()),i=new RegExp(`^(?:[>\u203A\u276F\u25B8\u25B6\u279C\u27A4*\u25CF]\\s*)?${t}\\.\\s+(.*)$`),s=-1,r="";for(let l=e.length-1;l>=0;l-=1){let p=e[l].match(i);if(p){s=l,r=p[1];break}}if(s<0)return null;let n=[r];for(let l=s+1;l<e.length&&!(/^(?:[>›❯▸▶➜➤*●]\s*)?\d+\.\s+/.test(e[l])||!e[l]||(n.push(e[l]),/\(p\)\s*$/i.test(e[l])));l+=1);return n.some(l=>/\(p\)\s*$/i.test(l))?n:null}function At(a){if(a.length===0||a.length>13)return new Set;let t=new Set([a[0]]);for(let e of a.slice(1)){let i=new Set;for(let s of t)i.add(`${s}${e}`),i.add(`${s} ${e}`);t=i}return t}function Ne(a){try{let t=(0,Le.parse)(a,e=>`\${${e}}`);return t.every(e=>typeof e=="string")?t:null}catch{return null}}function Li(a){return a.map(t=>/^[A-Za-z0-9_@%+=:,./-]+$/.test(t)?t:`'${t.replace(/'/g,`'"'"'`)}'`).join(" ")}function Ki(a,t){if(a.length===0)return!1;let e;try{e=(0,Le.parse)(t,s=>`\${${s}}`)}catch{return!1}let i=[[]];for(let s of e){if(typeof s=="string"){i[i.length-1].push(s);continue}if("op"in s&&["&&","||",";","|","|&","&"].includes(s.op)){i.push([]);continue}return!1}return i.some(s=>a.every((r,n)=>s[n]===r))}var Se=class a{constructor(){this.sessionState=null;this.unsubscribe=null;this.sessionKey=null;this.sessionIsEncrypted=!1;this.sessionKeyGen=null;this.e1PromptRaises=new Map;this.e1PendingResolutions=new Map;this.e1ResolutionInFlight=new Map;this.e1RaiseInFlight=new Map;this.e1NeedsReRaise=new Set;this.e1RetirementOrphans=new Map;this.e1ContentKeyIndex=new Map;this.e1TtlRefreshTimer=null;this.toolActivity=null;this.toolActivityStartPromise=null;this.toolActivityLegacyRecorders=new Map;this.pendingInteractivePrompt=null;this.queuedInteractivePrompts=[];this.isInitializingSession=!1;this.bufferedLogEntries=[];this.logEntryChain=Promise.resolve();this.hookEventChain=Promise.resolve();this.hooksActive=!1;this.hooksCompatibilityNoticeSent=!1;this.permissionRequestHookInFlight=null;this.queuedPermissionRequestDedupeKeys=new Map;this.queuedPermissionRequestSessionCounts=new Map;this.queuedPermissionRequestRegistrations=new Map;this.permissionRequestOccurrencesByCompletionKey=new Map;this.deferredTmuxPromptCandidates=new Map;this.recoveryPermissionReplay=null;this.resolvedApprovalDedupeKeys=new Map;this.subscribedSessionId=null;this.subscribedSessionState=null;this.terminalInputChain=Promise.resolve();this.terminalInputBlockedStates=new WeakSet;this.mobilePromptDeduper=new de({expiryMs:1e4});this.launchSessionInitPromise=null;this.sessionStartedInitPromise=null;this.bootstrapSessionId=null;this.recoveryBootstrapOwnerBound=!1;this.resumeBackendSession=h.resumeOrCreateSession;this.sessionBootstrapFailureCount=0;this.sessionBootstrapRetryAt=0;this.retiredSessionRecoveryPromise=null;this.lifecycleGeneration=0;this.isStopping=!1;this.stopPromise=null;this.terminalRetirementRequested=!1;this.tmuxLifecycleTimer=null;this.tmuxLifecycleCheckInFlight=!1;this.tmuxLifecycleObservedAlive=!1;this.tmuxLifecycleInitDeadlineMs=Number.POSITIVE_INFINITY;this.v1Bridge=new he;this.orchestrationClient=null;this.userDecisionUnsubscribe=null;this.httpApi=new ce,this.sessionWatcher=new ie,this.approvalDetector=new oe,this.promptResponder=new ae,this.tmuxPaneObserver=new le,this.e1DurableState=new me({handoffRoot:process.env.CODEVIBE_CODEX_HANDOFF_ROOT,resolutionRoot:process.env.CODEVIBE_CODEX_E1_OUTBOX_ROOT})}static{this.E1_TTL_REFRESH_MS=300*1e3}async start(){o.info("Starting CodeVibe Codex companion server",{environment:(0,h.getEnvironment)()}),this.appSyncClient=new h.AppSyncClient,await this.appSyncClient.authenticateWithStoredTokens()||(o.error('Authentication failed. Run "codevibe login" first.'),console.error('Not authenticated. Run "codevibe login" to sign in.'),process.exit(1)),o.info("Authenticated successfully",{userId:this.appSyncClient.getCurrentUserId(),email:this.appSyncClient.getCurrentUserEmail()}),await this.restorePendingE1ResolutionsFromOutbox(),await(0,h.registerDeviceEncryptionKey)(this.appSyncClient,o),(0,h.startDeviceKeyWatcher)(this.appSyncClient,o),(0,h.pushDetectedAgents)(this.appSyncClient,o).catch(i=>{o.warn("Failed to update available agents (non-fatal)",{error:i?.message})});try{let i=await this.appSyncClient.sweepOrphanSessions({agentType:"CODEX"});i>0&&o.info("Orphan sweep: marked stale Codex sessions INACTIVE",{swept:i})}catch(i){o.warn("Orphan sweep failed, continuing startup",{error:i instanceof Error?i.message:String(i)})}this.httpApi.onEvent(this.handleEventFromHook.bind(this));let e=await this.httpApi.start();o.info("HTTP API started for hooks",{port:e}),this.startTmuxLifecycleMonitor(),await this.createLaunchSession(),this.setupEventHandlers(),this.sessionWatcher.start(),o.info("CodeVibe Codex companion server started")}async createLaunchSession(){if(this.isStopping)return;if(process.env[ge]==="1"){o.info("Recovery daemon deferring backend bootstrap until the native rollout is known");return}let t=this.lifecycleGeneration,e=process.env.CODEVIBE_CODEX_TMUX_SESSION;if(!e){o.warn("No CODEVIBE_CODEX_TMUX_SESSION \u2014 skipping launch session");return}let i;this.launchSessionInitPromise=new Promise(s=>{i=s});try{let s=process.env.CODEX_WORKING_DIRECTORY||process.cwd(),r=this.getOrCreateBootstrapSessionId(),n=this.appSyncClient.getCurrentUserId();o.info("Creating launch session",{sessionId:r,projectPath:s});let l=!1;try{let p=await(0,h.resumeOrCreateSession)({sessionId:r,userId:n,agentType:h.AgentType.CODEX,projectPath:s,metadata:{launchSession:!0}},this.appSyncClient,o);if(r=p.sessionId,!this.isLifecycleCurrent(t)){await this.retireCancelledBootstrap(r,"launch_session");return}this.sessionKey=p.sessionKey,this.sessionKeyGen=p.sessionKeyGen,this.sessionIsEncrypted=!!p.sessionKey,l=!0,this.sessionState={sessionId:r,userId:n,projectPath:s,cwd:s,createdAt:new Date,subscriptionActive:!1,metadata:{launchSession:!0},encryptionSnapshot:{sessionKey:p.sessionKey,sessionIsEncrypted:!!p.sessionKey},codexSessionId:e,codexLogFile:void 0},this.clearSessionBootstrapFailure(),this.bootstrapSessionId=null,await R("daemon_init_step_completed",{step:"session_resume_or_create",path:"launch_session"})}catch(p){if(!this.isLifecycleCurrent(t)){let c=this.authoritativeSessionIdFromError(p)??r;await this.retireCancelledBootstrap(c,"launch_session_error");return}await R("daemon_init_step_failed",{step:"session_resume_or_create",path:"launch_session",error_class:p?.name||"Error",error_message:p?.message||String(p)}),p?.code==="ENCRYPTED_SESSION_NO_KEY"&&(this.sessionIsEncrypted=!0,this.sessionKey=null),this.bindBootstrapIdentityFromError(p),this.recordSessionBootstrapFailure(p,"launch_session"),o.error("Failed to create/resume launch session (non-fatal)",{error:p})}if(!l)return;await R("daemon_init_step_completed",{step:"session_state_set",path:"launch_session"});try{this.subscribeToMobileEvents(r)?await R("daemon_init_step_completed",{step:"subscribe_mobile_events",path:"launch_session"}):await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"launch_session",error_class:"SubscriptionSetupFailed",error_message:"subscribeToMobileEvents returned false"})}catch(p){await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"launch_session",error_class:p?.name||"Error",error_message:p?.message||String(p)}),o.error("Failed to subscribe to mobile events for launch session",{error:p})}try{await this.appSyncClient.startHeartbeat(r)?await R("daemon_init_step_completed",{step:"heartbeat_start",path:"launch_session"}):(await R("daemon_init_step_failed",{step:"heartbeat_start",path:"launch_session",error_class:"InitialHeartbeatNotPersisted",error_message:"Backend did not confirm the initial heartbeat mutation"}),o.error("Initial launch-session heartbeat was not persisted",{sessionId:r}))}catch(p){await R("daemon_init_step_failed",{step:"heartbeat_start",path:"launch_session",error_class:p?.name||"Error",error_message:p?.message||String(p)}),o.error("Failed to start heartbeat for launch session",{error:p})}try{await this.subscribeToOnApplyUserDecision(r)}catch(p){o.error("Failed to subscribe to onApplyUserDecision for launch session (non-fatal)",{error:p})}this.startMobileEndWatcher(r),o.info("Launch session created",{sessionId:r})}finally{i()}}encryptForEmit(t,e,i,s){let r=this.sessionState;if(!r||r.sessionId!==s||!r.encryptionSnapshot)return o.warn("Dropping event from stale or unresolved session generation (#638)",{type:i,sessionId:s,currentSessionId:r?.sessionId}),null;let{sessionKey:n,sessionIsEncrypted:l}=r.encryptionSnapshot,p=(0,h.encryptForEmit)(n,l,t,e);return p===null&&o.error("No session key for ENCRYPTED session \u2014 dropping event (fail-closed, #638)",{type:i,sessionId:s}),p}newE1Raise(t,e,i,s){let{record:r}=(0,h.newPromptRaise)({sessionId:t,producerKind:"PLUGIN_APPROVAL",mobileActionable:e,agentType:"CODEX",...i!==void 0&&{title:i}});return this.e1PromptRaises.set(r.promptId,{record:r,...s?.dedupeKey!==void 0?{resolutionDedupeKey:s.dedupeKey}:{},...s?.completionKey!==void 0?{resolutionCompletionKey:s.completionKey}:{},...s?.callId!==void 0?{resolutionCallId:s.callId}:{}}),this.e1TtlRefreshTimer||this.startE1TtlRefresh(),r}stashE1RaiseContent(t,e,i){let s=this.e1PromptRaises.get(t);s&&(s.contentPlain=e,s.metadataPlain=i)}async emitSuppressedPromptCarrier(t,e){let i="\u26A0\uFE0F A prompt is waiting in your desktop terminal, but its options could not be shown here. Please answer it on your desktop.",s=this.encryptForEmit(i,{e1SuppressedPrompt:!0},h.EventType.NOTIFICATION,t);return s?(await this.trackE1Raise(e.promptId,this.appSyncClient.createEvent({sessionId:t,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,content:s.content,metadata:s.metadata,...(0,h.raiseFieldsFromRecord)(e),notificationText:i,timestamp:(0,h.prepareEventTimestamp)({orderingKey:t}),isEncrypted:s.isEncrypted?!0:void 0})),o.info("E1: emitted suppressed-prompt badge carrier (mobileActionable=false)",{sessionId:t,promptId:e.promptId}),!0):!1}raiseSuppressedBadge(t,e,i={}){if(e){if(this.hasRecentlyResolvedApprovalDedupeKey(t,e)){o.debug("Skipping suppressed-prompt carrier; approval completed while producer was in flight",{sessionId:t,dedupeKey:e});return}let r=`${t}::${e}`,n=this.e1ContentKeyIndex.get(r);if(n&&this.e1PromptRaises.has(n)){if(i.suppressTmuxObserverUntilResolved){let l=this.e1PromptRaises.get(n);l&&(l.suppressTmuxObserverUntilResolved=!0,i.observerSnapshot&&(l.suppressedObserverSnapshotKey=this.buildObserverSnapshotMatchKey(i.observerSnapshot)))}return}n&&this.e1ContentKeyIndex.delete(r)}let s=this.newE1Raise(t,!1,void 0,e?{dedupeKey:e,completionKey:i.completionKey}:void 0);if(i.suppressTmuxObserverUntilResolved){let r=this.e1PromptRaises.get(s.promptId);r&&(r.suppressTmuxObserverUntilResolved=!0,i.observerSnapshot&&(r.suppressedObserverSnapshotKey=this.buildObserverSnapshotMatchKey(i.observerSnapshot)))}e&&this.e1ContentKeyIndex.set(`${t}::${e}`,s.promptId),this.emitSuppressedPromptCarrier(t,s).catch(r=>{o.error("E1: failed to emit suppressed-prompt badge carrier",{sessionId:t,promptId:s.promptId,error:r instanceof Error?r.message:String(r)})})}snapshotOpenE1(t){return[...this.e1PromptRaises.values()].filter(e=>e.record.sessionId===t).map(e=>({record:{...e.record},...e.contentPlain!==void 0&&{contentPlain:e.contentPlain},...e.metadataPlain!==void 0&&{metadataPlain:e.metadataPlain},...e.resolutionDedupeKey!==void 0&&{resolutionDedupeKey:e.resolutionDedupeKey},...e.resolutionCompletionKey!==void 0&&{resolutionCompletionKey:e.resolutionCompletionKey},...e.resolutionCallId!==void 0&&{resolutionCallId:e.resolutionCallId},...e.suppressTmuxObserverUntilResolved!==void 0&&{suppressTmuxObserverUntilResolved:e.suppressTmuxObserverUntilResolved},...e.suppressedObserverSnapshotKey!==void 0&&{suppressedObserverSnapshotKey:e.suppressedObserverSnapshotKey}}))}buildDaemonHandoffSnapshot(t){let e=process.env[H];if(!e)throw new Error("E1_DAEMON_HANDOFF_MISSING_TMUX_SESSION");return{version:1,createdAtMs:Date.now(),tmuxSession:e,backendSessionId:t.sessionId,openRaises:this.snapshotOpenE1(t.sessionId),pendingResolutions:[...this.e1PendingResolutions.values()].map(i=>({record:{...i.record},resolutionAt:i.resolutionAt,attempts:i.attempts})),needsReRaisePromptIds:[...this.e1NeedsReRaise],permissionOccurrences:[...this.permissionRequestOccurrencesByCompletionKey.entries()].map(([i,s])=>[i,s.map(r=>({...r}))]),pendingInteractivePrompt:this.pendingInteractivePrompt?{...this.pendingInteractivePrompt}:null,queuedInteractivePrompts:this.queuedInteractivePrompts.map(i=>({prompt:{...i.prompt},content:i.content,metadata:{...i.metadata}})),resolvedApprovalDedupeKeys:[...this.resolvedApprovalDedupeKeys.entries()].filter(([,i])=>i>Date.now()-Fe)}}persistDaemonHandoffState(t){for(let i of this.e1PendingResolutions.values())this.e1DurableState.writeResolution(i);let e=this.buildDaemonHandoffSnapshot(t);this.e1DurableState.writeHandoff(e),o.info("E1: persisted daemon handoff state",{sessionId:t.sessionId,openPrompts:e.openRaises.length,pendingResolutions:e.pendingResolutions.length,completionQueues:e.permissionOccurrences.length})}async adoptDaemonHandoffState(t){if(process.env[ge]!=="1")return!1;let e=process.env[H];if(!e)return!1;let i=this.e1DurableState.readHandoff(e);if(!i)return!1;let s=this.sessionState;if(!s||s.sessionId!==t)throw new Error("E1_DAEMON_HANDOFF_WITHOUT_CURRENT_SESSION");for(let p of i.pendingResolutions)this.e1PendingResolutions.has(p.record.promptId)||this.e1PendingResolutions.set(p.record.promptId,{record:{...p.record},resolutionAt:p.resolutionAt,attempts:p.attempts}),this.e1DurableState.writeResolution(p);for(let p of i.openRaises){let c={...p,record:{...p.record},...p.metadataPlain!==void 0&&{metadataPlain:{...p.metadataPlain}}};this.e1PromptRaises.set(c.record.promptId,c),i.backendSessionId!==t&&this.e1RetirementOrphans.set(c.record.promptId,{...c,record:{...c.record}}),c.resolutionDedupeKey&&this.e1ContentKeyIndex.set(`${t}::${c.resolutionDedupeKey}`,c.record.promptId)}this.permissionRequestOccurrencesByCompletionKey=new Map(i.permissionOccurrences.map(([p,c])=>[p,c.map(d=>({...d}))])),this.e1NeedsReRaise=new Set(i.needsReRaisePromptIds),this.pendingInteractivePrompt=i.pendingInteractivePrompt?{...i.pendingInteractivePrompt,ownerSessionId:t}:null;let r=this.pendingInteractivePrompt,n=r?.completionKey?this.permissionRequestOccurrencesByCompletionKey.get(r.completionKey):void 0;this.recoveryPermissionReplay=r?.source==="permission_hook"&&r.completionKey&&r.dedupeKey&&this.isPermissionRequestOccurrenceKey(r.dedupeKey)&&n?.some(p=>p.occurrenceKey===r.dedupeKey)?{ownerSessionId:t,completionKey:r.completionKey,occurrenceKey:r.dedupeKey}:null,this.queuedInteractivePrompts=i.queuedInteractivePrompts.map(p=>({prompt:{...p.prompt,ownerSessionId:t},content:p.content,metadata:{...p.metadata},ownerState:s}));let l=Date.now()-Fe;for(let[p,c]of i.resolvedApprovalDedupeKeys){if(c<=l)continue;let d=`${i.backendSessionId}::`,u=p.startsWith(d)?`${t}::${p.slice(d.length)}`:p;this.resolvedApprovalDedupeKeys.set(u,c)}return i.backendSessionId!==t&&await this.drainRetirementOrphans(t),await this.drainPendingE1Resolutions(),await this.drainPendingE1ReRaises(),(this.e1PromptRaises.size>0||this.e1PendingResolutions.size>0)&&this.startE1TtlRefresh(),this.e1DurableState.removeHandoff(e),o.info("E1: adopted daemon handoff state",{fromSessionId:i.backendSessionId,replacementSessionId:t,openPrompts:i.openRaises.length,pendingResolutions:i.pendingResolutions.length,completionQueues:i.permissionOccurrences.length}),!0}parkRetirementOrphans(t){for(let e of this.snapshotOpenE1(t))this.e1RetirementOrphans.set(e.record.promptId,e)}async drainRetirementOrphans(t){if(this.e1RetirementOrphans.size===0){this.e1PendingResolutions.size>0&&!this.e1TtlRefreshTimer&&this.startE1TtlRefresh();return}o.info("E1 (\xA76a-4): re-homing parked retirement-orphan prompts onto replacement session",{replacementSessionId:t,count:this.e1RetirementOrphans.size});for(let[e,i]of[...this.e1RetirementOrphans]){let r=this.e1PromptRaises.get(e)??{record:{...i.record},...i.contentPlain!==void 0&&{contentPlain:i.contentPlain},...i.metadataPlain!==void 0&&{metadataPlain:i.metadataPlain},...i.resolutionDedupeKey!==void 0&&{resolutionDedupeKey:i.resolutionDedupeKey},...i.resolutionCompletionKey!==void 0&&{resolutionCompletionKey:i.resolutionCompletionKey},...i.resolutionCallId!==void 0&&{resolutionCallId:i.resolutionCallId}};for(let[l,p]of this.e1ContentKeyIndex.entries())p===e&&this.e1ContentKeyIndex.delete(l);if(r.record.sessionId=t,this.e1PromptRaises.set(e,r),r.resolutionDedupeKey&&this.e1ContentKeyIndex.set(`${t}::${r.resolutionDedupeKey}`,e),!await this.reRaiseOneE1(t,r,{untilAck:!0}))break;this.e1RetirementOrphans.delete(e)}this.e1TtlRefreshTimer||this.startE1TtlRefresh()}isReRaiseTargetCurrent(t){return!this.isStopping&&this.sessionState!==null&&this.sessionState.sessionId===t}async reRaiseOneE1(t,e,i){let s=e.record,r=s.mobileActionable&&e.contentPlain!==void 0,n=i?.untilAck??!1,l=4,p=4e3;for(let c=1;n||c<=l;c++){if(n&&!this.isReRaiseTargetCurrent(t))return this.e1NeedsReRaise.add(s.promptId),o.warn("E1: retirement re-raise aborted (stopping/superseded) \u2014 parked",{sessionId:t,promptId:s.promptId}),!1;try{if(r){let d=this.encryptForEmit(e.contentPlain,e.metadataPlain??{},h.EventType.INTERACTIVE_PROMPT,t);if(!d)return!1;await this.trackE1Raise(s.promptId,this.appSyncClient.createEvent({sessionId:t,type:h.EventType.INTERACTIVE_PROMPT,source:h.EventSource.DESKTOP,content:d.content,metadata:d.metadata,...(0,h.raiseFieldsFromRecord)(s),timestamp:(0,h.prepareEventTimestamp)({orderingKey:t}),...d.isEncrypted?{isEncrypted:!0}:{}}))}else if(s.mobileActionable=!1,!await this.emitSuppressedPromptCarrier(t,s))return!1;return this.e1NeedsReRaise.delete(s.promptId),o.info("E1: re-raised prompt onto replacement session",{sessionId:t,promptId:s.promptId,actionable:r}),!0}catch(d){if(!n&&c===l)return this.e1NeedsReRaise.add(s.promptId),o.error("E1: re-raise unacked after backoff \u2014 parked for retry-until-ack",{sessionId:t,promptId:s.promptId,error:d instanceof Error?d.message:String(d)}),!1;if(n){let u=Date.now()+Math.min(250*2**(c-1),p);for(;Date.now()<u&&this.isReRaiseTargetCurrent(t);)await new Promise(m=>setTimeout(m,Math.min(50,u-Date.now())))}else await new Promise(u=>setTimeout(u,250*c))}}return!1}async drainPendingE1ReRaises(){if(this.e1NeedsReRaise.size!==0)for(let t of[...this.e1NeedsReRaise]){let e=this.e1PromptRaises.get(t);if(!e){this.e1NeedsReRaise.delete(t);continue}!this.sessionState||e.record.sessionId!==this.sessionState.sessionId||await this.reRaiseOneE1(e.record.sessionId,e)}}beginE1Resolution(t,e){if(this.e1PendingResolutions.has(t))return!0;let i=this.e1PromptRaises.get(t);if(!i||i.record.sessionId!==e)return!1;this.e1PromptRaises.delete(t),this.e1NeedsReRaise.delete(t),this.e1RetirementOrphans.delete(t);for(let[s,r]of this.e1ContentKeyIndex.entries())r===t&&this.e1ContentKeyIndex.delete(s);return this.e1PendingResolutions.set(t,{record:{...i.record},resolutionAt:new Date().toISOString(),attempts:0}),this.e1TtlRefreshTimer||this.startE1TtlRefresh(),!0}async emitE1Resolution(t){let e=this.e1ResolutionInFlight.get(t);if(e)return e;if(!this.e1PendingResolutions.has(t))return;let i=(async()=>{let s=this.e1PendingResolutions.get(t);if(s){try{this.e1DurableState.writeResolution(s)}catch(r){o.error("E1: failed to persist exact-clear outbox record",{promptId:t,error:r instanceof Error?r.message:String(r)})}try{let r=this.e1RaiseInFlight.get(t);if(r&&await r,s=this.e1PendingResolutions.get(t),!s)return;if((await this.appSyncClient.createEvent({sessionId:s.record.sessionId,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,resolvedPromptId:s.record.promptId,ownerToken:s.record.ownerToken,resolutionAt:s.resolutionAt,timestamp:(0,h.prepareEventTimestamp)({orderingKey:s.record.sessionId})}))?.promptId!==s.record.promptId)throw new Error("E1_PLUGIN_RESOLUTION_NOT_ACKNOWLEDGED: exact prompt id was not returned");this.e1PendingResolutions.delete(t);try{this.e1DurableState.removeResolution(t)}catch(l){o.warn("E1: exact-clear ACKed but outbox cleanup failed",{promptId:t,error:l instanceof Error?l.message:String(l)})}o.info("E1: plugin approval resolution acknowledged",{sessionId:s.record.sessionId,promptId:t})}catch(r){let n=this.e1PendingResolutions.get(t);o.warn("E1: plugin approval resolution unacknowledged; parked for retry",{sessionId:n?.record.sessionId,promptId:t,error:r instanceof Error?r.message:String(r)})}}})();this.e1ResolutionInFlight.set(t,i);try{await i}finally{this.e1ResolutionInFlight.get(t)===i&&this.e1ResolutionInFlight.delete(t)}}trackE1Raise(t,e){let i=e.then(()=>{},()=>{});return this.e1RaiseInFlight.set(t,i),i.then(()=>{this.e1RaiseInFlight.get(t)===i&&this.e1RaiseInFlight.delete(t)}),e}async drainPendingE1Resolutions(){for(let[t,e]of[...this.e1PendingResolutions.entries()])e.attempts+=1,await this.emitE1Resolution(t)}async restorePendingE1ResolutionsFromOutbox(){let{records:t,rejected:e}=this.e1DurableState.readResolutions();for(let i of e)o.error("E1: rejected unsafe or invalid resolution outbox record",i);for(let i of t){let s=this.e1PendingResolutions.get(i.record.promptId);(!s||i.attempts>s.attempts)&&this.e1PendingResolutions.set(i.record.promptId,{record:{...i.record},resolutionAt:i.resolutionAt,attempts:i.attempts})}t.length>0&&(o.info("E1: recovered pending exact-clear outbox records",{count:t.length}),await this.drainPendingE1Resolutions(),this.e1PendingResolutions.size>0&&!this.e1TtlRefreshTimer&&this.startE1TtlRefresh())}async drainPendingE1ResolutionsForLifecycleEnd(t={}){let e=t.maxWaitMs??4e3,i=t.retryDelayMs??250,s=Date.now()+Math.max(0,e),r=0;do{if(await this.drainPendingE1Resolutions(),this.e1PendingResolutions.size===0)return;if(r+=1,Date.now()>=s)break;await new Promise(n=>setTimeout(n,Math.min(i*r,Math.max(0,s-Date.now()))))}while(Date.now()<s);for(let n of this.e1PendingResolutions.values())this.e1DurableState.writeResolution(n);o.warn("E1: graceful lifecycle ended with durable exact-clear work pending",{count:this.e1PendingResolutions.size})}beginE1PromptResolutions(t,e){let i=[];for(let s of new Set(t))this.beginE1Resolution(s,e)&&i.push(s);return i}launchE1Resolutions(t){Promise.all([...t].map(e=>this.emitE1Resolution(e)))}async exactClearOpenE1ForLifecycleEnd(t){let e=[...this.e1PromptRaises.values()].filter(s=>s.record.sessionId===t).map(s=>s.record.promptId),i=this.beginE1PromptResolutions(e,t);await Promise.all(i.map(s=>this.emitE1Resolution(s)))}async refreshE1Ttls(){await this.drainPendingE1Resolutions(),await this.drainPendingE1ReRaises();let t=this.sessionState?.sessionId;if(!t)return;let e=[...this.e1PromptRaises.values()].filter(i=>i.record.sessionId===t).map(i=>i.record.promptId);if(e.length!==0)try{let i=await this.appSyncClient.refreshOpenPromptTtl(e);for(let s of i){let r=this.e1PromptRaises.get(s);r&&r.record.sessionId===t&&await this.reRaiseOneE1(t,r)}}catch(i){o.warn("E1: TTL-refresh tick failed (non-fatal, retries next tick)",{error:i instanceof Error?i.message:String(i)})}}startE1TtlRefresh(){this.e1TtlRefreshTimer&&clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=setInterval(()=>{this.refreshE1Ttls()},a.E1_TTL_REFRESH_MS)}stopE1TtlRefresh(){this.e1TtlRefreshTimer&&(clearInterval(this.e1TtlRefreshTimer),this.e1TtlRefreshTimer=null)}isExpectedSessionStateCurrent(t){return!this.isStopping&&this.sessionState===t&&!this.terminalInputBlockedStates.has(t)}sendSessionPinnedInput(t,e){if(!this.isExpectedSessionStateCurrent(t)||this.terminalInputBlockedStates.has(t))return Promise.resolve(!1);let i=this.terminalInputChain.then(async()=>!this.isExpectedSessionStateCurrent(t)||this.terminalInputBlockedStates.has(t)||!await this.verifyNoActiveCodexChooser(t.sessionId)||!await this.promptResponder.sendLiteral(t.sessionId,e)||(await new Promise(s=>setTimeout(s,500)),!this.isExpectedSessionStateCurrent(t))||!await this.verifyNoActiveCodexChooser(t.sessionId)?!1:this.promptResponder.sendKey(t.sessionId,"Enter"));return this.terminalInputChain=i.then(()=>{},()=>{}),i}async verifyNoActiveCodexChooser(t){try{let e=await this.tmuxPaneObserver.captureSnapshot();return!this.tmuxPaneObserver.extractActiveApprovalBlock(e)}catch(e){return o.warn("Failed to verify that no Codex chooser is active",{sessionId:t,error:e instanceof Error?e.message:String(e)}),!1}}sendVerifiedPromptInput(t,e,i){if(!this.isExpectedSessionStateCurrent(t))return Promise.resolve("stale");let s=this.terminalInputChain.then(async()=>{if(!this.isExpectedSessionStateCurrent(t)||this.pendingInteractivePrompt!==e||e.ownerSessionId!==t.sessionId)return"stale";if(!e.observerMatchKey)return o.warn("Refusing mobile prompt input without a captured pane identity",{sessionId:t.sessionId,promptId:e.promptId}),"unverified";let r=null;try{let l=await this.tmuxPaneObserver.captureSnapshot(),p=this.tmuxPaneObserver.extractActiveApprovalBlock(l),c=p?(0,B.parseInteractivePrompt)(p):null;r=c?this.buildObserverPromptMatchKey(c):null}catch(l){return o.warn("Failed to re-probe Codex prompt before mobile input",{sessionId:t.sessionId,promptId:e.promptId,error:l instanceof Error?l.message:String(l)}),"unverified"}return!this.isExpectedSessionStateCurrent(t)||this.pendingInteractivePrompt!==e?"stale":!r||r!==e.observerMatchKey?(o.warn("Refusing mobile input because the live Codex prompt changed",{sessionId:t.sessionId,promptId:e.promptId,hasLiveIdentity:!!r}),"unverified"):(i===q?await this.promptResponder.sendKey(t.sessionId,"Escape"):await this.promptResponder.sendLiteral(t.sessionId,i))?"sent":"send_failed"});return this.terminalInputChain=s.then(()=>{},()=>{}),s}sendVerifiedFollowUpAfterDismissal(t,e,i){let s=this.terminalInputChain.then(async()=>{let r=()=>this.isExpectedSessionStateCurrent(t)&&this.pendingInteractivePrompt===e&&e.ownerSessionId===t.sessionId;return r()?!await this.verifyNoActiveCodexChooser(t.sessionId)||!r()?"unverified":await this.promptResponder.sendLiteral(t.sessionId,i)?(await new Promise(n=>setTimeout(n,500)),r()?!await this.verifyNoActiveCodexChooser(t.sessionId)||!r()?"unverified":await this.promptResponder.sendKey(t.sessionId,"Enter")?"sent":"send_failed":"stale"):"send_failed":"stale"});return this.terminalInputChain=s.then(()=>{},()=>{}),s}handleEventFromHook(t){if(this.isStopping)return Promise.resolve();let e=this.lifecycleGeneration,i=this.registerQueuedPermissionRequest(t,e),s=this.hookEventChain.then(()=>this.processHookEvent(t,e,i??void 0)),r=i?s.finally(()=>this.unregisterQueuedPermissionRequest(i)):s;return this.hookEventChain=r.catch(n=>{o.error("[Hooks] event handler failed",{hookEvent:t.hook_event_name,sessionId:t.session_id,error:n instanceof Error?n.message:String(n)})}),r}registerQueuedPermissionRequest(t,e){if(t.hook_event_name!=="PermissionRequest"||!this.sessionState||this.sessionWatcher.getAuthoritativeSessionId()!==t.session_id)return null;let i=this.sessionState.sessionId;this.queuedPermissionRequestSessionCounts.set(i,(this.queuedPermissionRequestSessionCounts.get(i)??0)+1);let s=this.buildApprovalPromptContextFromHookPayload(t),r=this.buildApprovalDedupeKey(s),n=`permission:${(0,ee.v4)()}`,l=r?`${i}::${r}`:void 0;l&&this.queuedPermissionRequestDedupeKeys.set(l,(this.queuedPermissionRequestDedupeKeys.get(l)??0)+1);let p={sessionId:i,lifecycleGeneration:e,context:s,dedupeKey:r,occurrenceKey:n,dedupeRegistryKey:l,disposition:"pending"};if(r){let d=this.permissionRequestOccurrencesByCompletionKey.get(r)??[];d.push({occurrenceKey:n,hookObserved:!1,jsonlObserved:!1}),this.permissionRequestOccurrencesByCompletionKey.set(r,d)}let c=this.queuedPermissionRequestRegistrations.get(i)??[];return c.push(p),this.queuedPermissionRequestRegistrations.set(i,c),p}async unregisterQueuedPermissionRequest(t){let e=this.queuedPermissionRequestSessionCounts.get(t.sessionId)??0;if(e<=1?this.queuedPermissionRequestSessionCounts.delete(t.sessionId):this.queuedPermissionRequestSessionCounts.set(t.sessionId,e-1),t.dedupeRegistryKey){let l=this.queuedPermissionRequestDedupeKeys.get(t.dedupeRegistryKey)??0;l<=1?this.queuedPermissionRequestDedupeKeys.delete(t.dedupeRegistryKey):this.queuedPermissionRequestDedupeKeys.set(t.dedupeRegistryKey,l-1)}let s=(this.queuedPermissionRequestRegistrations.get(t.sessionId)??[]).filter(l=>l!==t);s.length>0?this.queuedPermissionRequestRegistrations.set(t.sessionId,s):this.queuedPermissionRequestRegistrations.delete(t.sessionId),(t.disposition==="pending"||t.disposition==="dropped"||this.sessionState?.sessionId!==t.sessionId)&&this.removePermissionRequestOccurrence(t.dedupeKey,t.occurrenceKey);let r=this.deferredTmuxPromptCandidates.get(t);if(!r)return;if(this.deferredTmuxPromptCandidates.delete(t),!this.isLifecycleCurrent(t.lifecycleGeneration)){t.disposition="dropped",this.removePermissionRequestOccurrence(t.dedupeKey,t.occurrenceKey),o.debug("Discarding deferred tmux prompt after lifecycle invalidation",{sessionId:t.sessionId,lifecycleGeneration:t.lifecycleGeneration,currentLifecycleGeneration:this.lifecycleGeneration});return}let n=this.hasRecentlyResolvedApprovalDedupeKey(t.sessionId,t.occurrenceKey);if(t.disposition==="covered"||t.disposition==="resolved"||n){o.debug("Discarding deferred tmux prompt covered by its PermissionRequest",{sessionId:t.sessionId,dedupeKey:t.dedupeKey,disposition:t.disposition});return}if(!(t.disposition==="dropped"||this.sessionState?.sessionId!==t.sessionId)){o.debug("Replaying deferred tmux prompt after PermissionRequest produced no durable owner",{sessionId:t.sessionId,dedupeKey:t.dedupeKey});try{await this.handleTmuxPromptCandidate(r,t)}catch(l){o.error("Failed to replay deferred tmux prompt candidate",{sessionId:t.sessionId,error:l instanceof Error?l.message:String(l)})}}}removePermissionRequestOccurrence(t,e){if(!t||!e)return;let i=this.permissionRequestOccurrencesByCompletionKey.get(t);if(!i)return;let s=i.filter(r=>r.occurrenceKey!==e);s.length>0?this.permissionRequestOccurrencesByCompletionKey.set(t,s):this.permissionRequestOccurrencesByCompletionKey.delete(t)}observePermissionRequestCompletion(t,e){if(!t)return{shouldResolve:!0};let i=this.permissionRequestOccurrencesByCompletionKey.get(t);if(!i)return{shouldResolve:!0};let s=e==="hook"?"hookObserved":"jsonlObserved",r=i.find(l=>!l[s]);if(!r)return o.debug("Ignoring duplicate interactive-prompt completion observation",{completionKey:t,producer:e}),{shouldResolve:!1};let n=!r.hookObserved&&!r.jsonlObserved;return r[s]=!0,r.hookObserved&&r.jsonlObserved&&this.removePermissionRequestOccurrence(t,r.occurrenceKey),{shouldResolve:n,occurrenceKey:r.occurrenceKey}}isPermissionRequestOccurrenceKey(t){return t.startsWith("permission:")}consumeRecoveryPermissionReplay(t,e,i){let s=this.recoveryPermissionReplay,r=this.pendingInteractivePrompt;return!s||!e||s.ownerSessionId!==t.sessionId||s.completionKey!==e||r?.ownerSessionId!==t.sessionId||r.source!=="permission_hook"||r.dedupeKey!==s.occurrenceKey||r.completionKey!==s.completionKey?!1:(this.recoveryPermissionReplay=null,i&&(this.removePermissionRequestOccurrence(i.dedupeKey,i.occurrenceKey),i.occurrenceKey=s.occurrenceKey,i.disposition="covered"),o.info("[Hooks] Reused adopted PermissionRequest occurrence for one recovery replay",{sessionId:t.sessionId,completionKey:e,occurrenceKey:s.occurrenceKey}),!0)}async processHookEvent(t,e,i){if(!this.isLifecycleCurrent(e))return;let{session_id:s,hook_event_name:r,type:n,content:l,metadata:p}=t;o.info("[Hooks] Received event",{sessionId:s,hookEvent:r,type:n,contentLength:l?.length});let c=this.sessionWatcher.getAuthoritativeSessionId();if(c&&c!==s){o.warn("[Hooks] Rejecting event for foreign native session",{hookEvent:r,sessionId:s,authoritativeSessionId:c});return}if(p?.transcript_path){let m=await this.sessionWatcher.bindToSessionTranscript(p.transcript_path,s);if(!this.isLifecycleCurrent(e))return;let y=m.status==="pending"?await m.completion:m;if(!this.isLifecycleCurrent(e))return;if(y.status!=="bound"){o.warn("[Hooks] Rejecting event whose transcript ownership was not proven",{hookEvent:r,sessionId:s,reason:y.reason});return}}else if(c!==s){o.warn("[Hooks] Rejecting event without an owned transcript",{hookEvent:r,sessionId:s});return}if(!this.isLifecycleCurrent(e)||this.sessionWatcher.getAuthoritativeSessionId()!==s){o.warn("[Hooks] Rejecting event after ownership changed during admission",{hookEvent:r,sessionId:s,authoritativeSessionId:this.sessionWatcher.getAuthoritativeSessionId()});return}if(this.hooksActive=!0,r==="SessionStart"){if(this.launchSessionInitPromise&&(await this.launchSessionInitPromise,!this.isLifecycleCurrent(e)))return;if(this.sessionState){if(o.info("[Hooks] SessionStart \u2014 launch session already exists, updating codexSessionId",{existingSessionId:this.sessionState.sessionId,codexSessionId:s}),this.sessionState.codexSessionId=s,this.sessionState.metadata={...this.sessionState.metadata,codexSessionId:s,cliVersion:p?.model||"unknown",modelProvider:p?.model||"unknown",launchSession:void 0},this.appSyncClient.updateSession({sessionId:this.sessionState.sessionId,metadata:this.sessionState.metadata}).catch(m=>o.warn("Failed to update session metadata",{error:m})),await this.startTmuxObserverWithBeacon("session_start_existing"),!this.isLifecycleCurrent(e))return;await this.ensureToolActivityStarted(s)}else{let m={id:s,timestamp:new Date().toISOString(),cwd:p?.cwd||process.cwd(),originator:"hook",cli_version:p?.model||"unknown",instructions:null,source:p?.source||"startup",model_provider:p?.model||"unknown"};await this.ensureSessionStarted(m)}return}if(!this.sessionState){o.warn("[Hooks] Hook event for un-bootstrapped session \u2014 self-healing via session bootstrap (#638)",{hookEvent:r,sessionId:s});let m={id:s,timestamp:new Date().toISOString(),cwd:p?.cwd||process.cwd(),originator:"hook",cli_version:p?.model||"unknown",instructions:null,source:p?.source||"startup",model_provider:p?.model||"unknown"};if(await this.ensureSessionStarted(m),!this.isLifecycleCurrent(e))return;if(!this.sessionState){o.warn("[Hooks] Session still not initialized after self-heal, dropping event",{hook_event_name:r});return}}let d=this.sessionState,u=d.sessionId;if(n==="USER_PROMPT"&&l){let m=this.consumeRecentMobilePrompt(u,l);if(m){o.info("[Hooks] Skipping duplicate USER_PROMPT from mobile",{sessionId:u,matchType:m.matchType,originalLength:m.originalLength,echoLength:m.echoLength});return}}if(r==="PreToolUse"){o.debug("[Hooks] PreToolUse observed; AppSync emission suppressed",{toolName:p?.tool_name||"unknown",sessionId:u});return}if(r==="PermissionRequest"){let m=i??this.registerQueuedPermissionRequest(t,e);if(!m){o.error("[Hooks] Dropping admitted PermissionRequest without occurrence registration",{sessionId:u,nativeSessionId:s});return}try{await this.handlePermissionRequestHook(t,d,m)}finally{i||await this.unregisterQueuedPermissionRequest(m)}return}if(r==="PostToolUse"){let m=this.buildApprovalPromptContextFromHookPayload(t),y=this.buildApprovalDedupeKey(m),f=this.firstString(p?.tool_use_id,p?.call_id,p?.callId)??"",v=this.observePermissionRequestCompletion(y,"hook");if(v.shouldResolve&&await this.clearResolvedInteractivePrompt(f,v.occurrenceKey??y),!this.isExpectedSessionStateCurrent(d)||this.toolActivity?.isActive()&&await this.toolActivity.observeCall({callId:p?.tool_use_id??p?.call_id??p?.callId,toolName:p?.tool_name,toolInput:p?.tool_input})!=="closed")return;let S=this.encryptForEmit(l,p,h.EventType.TOOL_USE,u);if(!S)return;let P=S.content,b=S.metadata,T=S.isEncrypted,k=p?.tool_use_id??p?.call_id??p?.callId,O=typeof k=="string"&&k.length>0&&!!this.sessionKey,N=O?this.getToolActivityLegacyRecorder(u):void 0;N&&O&&N.markLegacyInFlight(k);try{let A=await this.appSyncClient.createEvent({sessionId:u,type:h.EventType.TOOL_USE,source:h.EventSource.DESKTOP,content:P,metadata:b,isEncrypted:T,timestamp:(0,h.prepareEventTimestamp)({orderingKey:u})});N&&O&&((0,h.isPersistedEventResult)(A)?await N.finalizeLegacyOwned([k]):N.clearLegacyInFlight(k))}catch(A){throw N&&O&&N.clearLegacyInFlight(k),A}return}if(n==="ASSISTANT_RESPONSE"||n==="USER_PROMPT"){if(n==="ASSISTANT_RESPONSE"){this.sessionWatcher.getBindSource()==="authoritative"&&this.sessionWatcher.getAuthoritativeSessionId()===s?o.debug("[Hooks] Suppressing Stop final \u2014 JSONL watcher authoritatively bound to this session (Bug #2)"):o.warn("[Hooks] Dropping Stop final after authoritative ownership changed",{authoritativeSessionId:this.sessionWatcher.getAuthoritativeSessionId()});return}let m=this.encryptForEmit(l,void 0,h.EventType.USER_PROMPT,u);if(!m)return;await this.appSyncClient.createEvent({sessionId:u,type:h.EventType.USER_PROMPT,source:h.EventSource.DESKTOP,content:m.content,isEncrypted:m.isEncrypted,timestamp:(0,h.prepareEventTimestamp)({orderingKey:u})});return}}mapToolName(t){return{shell_command:"Bash",shell:"Bash",apply_patch:"Edit",create_file:"Write",read_file:"Read"}[t]||t}trackMobilePrompt(t,e){this.mobilePromptDeduper.track(t,e),o.debug("Tracking mobile prompt for USER_PROMPT echo deduplication",{sessionId:t,promptLength:e.trim().length})}forgetMobilePrompt(t,e){this.mobilePromptDeduper.forget(t,e)}consumeRecentMobilePrompt(t,e){return this.mobilePromptDeduper.consumeIfDuplicate(t,e)}setupEventHandlers(){this.sessionWatcher.on("session-started",async t=>{try{if(this.launchSessionInitPromise&&await this.launchSessionInitPromise,this.sessionState){o.info("[JSONL] Session already active, skipping",{currentSessionId:this.sessionState.sessionId,codexSessionId:t.id}),await this.ensureToolActivityStarted(t.id);return}await this.ensureSessionStarted(t)}catch(e){o.error("[JSONL] session-started handler failed (non-fatal) \u2014 next event retries",{codexSessionId:t?.id,error:String(e)})}}),this.sessionWatcher.on("log-entry",t=>{this.enqueueLogEntry(t)}),this.approvalDetector.on("approval-pending",async t=>{try{await this.handleApprovalPending(t)}catch(e){o.error("approval-pending handler failed (non-fatal)",{error:String(e)})}}),this.tmuxPaneObserver.on("prompt-candidate",async t=>{try{await this.handleTmuxPromptCandidate(t.snapshot)}catch(e){o.error("prompt-candidate handler failed (non-fatal)",{error:String(e)})}}),this.tmuxPaneObserver.on("observer-error",t=>{o.debug("Tmux pane observer error",{error:t})}),this.sessionWatcher.on("error",t=>{o.error("Session watcher error:",t)})}async ensureSessionStarted(t,e){if(this.isStopping||(this.launchSessionInitPromise&&await this.launchSessionInitPromise,this.isStopping)||this.sessionState)return;process.env[ge]==="1"&&await this.bindRecoveryBootstrapIdentity(t.id);let i=this.sessionBootstrapRetryAt-Date.now();if(i>0){o.warn("Session bootstrap is in retry backoff; hook will be retried later",{codexSessionId:t.id,retryDelayMs:i,failureCount:this.sessionBootstrapFailureCount});return}if(this.sessionStartedInitPromise){try{await this.sessionStartedInitPromise}catch{}return}let s=this.handleSessionStarted(t,e);this.sessionStartedInitPromise=s.catch(()=>{});try{await s}finally{this.sessionStartedInitPromise=null}}async handleSessionStarted(t,e){if(this.isStopping)return;let i=this.lifecycleGeneration;o.info("Handling new Codex session",{codexSessionId:t.id}),this.isInitializingSession=!0,this.bufferedLogEntries=[],this.sessionState&&await this.endActiveSession("new-codex-session-started");let s=process.env.CODEX_WORKING_DIRECTORY||t.cwd||process.cwd(),r=this.getOrCreateBootstrapSessionId(),n=this.appSyncClient.getCurrentUserId(),l={codexSessionId:t.id,cliVersion:t.cli_version,modelProvider:t.model_provider},p={sessionKey:null,sessionIsEncrypted:!0};try{let c={sessionId:r,userId:n,agentType:h.AgentType.CODEX,projectPath:s,metadata:l},d=await this.resumeOrCreateBootstrapSession(c);if(r=d.sessionId,!this.isLifecycleCurrent(i)){this.isInitializingSession=!1,await this.retireCancelledBootstrap(r,"session_started");return}this.sessionKey=d.sessionKey,this.sessionKeyGen=d.sessionKeyGen,this.sessionIsEncrypted=!!d.sessionKey,p={sessionKey:d.sessionKey,sessionIsEncrypted:!!d.sessionKey},await R("daemon_init_step_completed",{step:"session_resume_or_create",path:"session_started"})}catch(c){if(this.isInitializingSession=!1,!this.isLifecycleCurrent(i)){let d=this.authoritativeSessionIdFromError(c)??r;await this.retireCancelledBootstrap(d,"session_started_error");return}throw await R("daemon_init_step_failed",{step:"session_resume_or_create",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),c?.code==="ENCRYPTED_SESSION_NO_KEY"&&(this.sessionIsEncrypted=!0,this.sessionKey=null),this.bindBootstrapIdentityFromError(c),this.recordSessionBootstrapFailure(c,"session_started"),o.error("Failed to create/resume session:",c),c}try{this.sessionState={sessionId:r,userId:n,projectPath:s,cwd:t.cwd,createdAt:new Date,subscriptionActive:!1,metadata:l,encryptionSnapshot:p,codexSessionId:t.id,codexLogFile:this.sessionWatcher.getActiveLogFile()||void 0},this.clearSessionBootstrapFailure(),this.bootstrapSessionId=null,this.recoveryBootstrapOwnerBound=!1,await R("daemon_init_step_completed",{step:"session_state_set",path:"session_started"})}catch(c){await R("daemon_init_step_failed",{step:"session_state_set",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to set session state:",c)}await this.adoptDaemonHandoffState(r),await this.ensureToolActivityStarted(t.id);try{this.subscribeToMobileEvents(r)?await R("daemon_init_step_completed",{step:"subscribe_mobile_events",path:"session_started"}):await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"session_started",error_class:"SubscriptionSetupFailed",error_message:"subscribeToMobileEvents returned false"})}catch(c){await R("daemon_init_step_failed",{step:"subscribe_mobile_events",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to subscribe to mobile events:",c)}if(e)try{await e()}catch(c){o.error("E1: pre-heartbeat re-raise hook failed (non-fatal)",{sessionId:r,error:c instanceof Error?c.message:String(c)})}try{await this.drainRetirementOrphans(r)}catch(c){o.error("E1: retirement-orphan drain failed (non-fatal)",{sessionId:r,error:c instanceof Error?c.message:String(c)})}try{await this.appSyncClient.startHeartbeat(r)?await R("daemon_init_step_completed",{step:"heartbeat_start",path:"session_started"}):(await R("daemon_init_step_failed",{step:"heartbeat_start",path:"session_started",error_class:"InitialHeartbeatNotPersisted",error_message:"Backend did not confirm the initial heartbeat mutation"}),o.error("Initial session heartbeat was not persisted",{sessionId:r}))}catch(c){await R("daemon_init_step_failed",{step:"heartbeat_start",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to start heartbeat:",c)}try{await this.subscribeToOnApplyUserDecision(r)}catch(c){o.error("Failed to subscribe to onApplyUserDecision (non-fatal)",{error:c})}this.startMobileEndWatcher(r);try{await this.flushBufferedLogEntries(),await R("daemon_init_step_completed",{step:"flush_buffered_entries",path:"session_started"})}catch(c){await R("daemon_init_step_failed",{step:"flush_buffered_entries",path:"session_started",error_class:c?.name||"Error",error_message:c?.message||String(c)}),o.error("Failed to flush buffered log entries:",c),this.bufferedLogEntries=[]}await this.startTmuxObserverWithBeacon("session_started"),this.isInitializingSession=!1}getOrCreateBootstrapSessionId(){return this.bootstrapSessionId||(this.bootstrapSessionId=V.mintBackendSessionId()),this.bootstrapSessionId}async bindRecoveryBootstrapIdentity(t){if(!(process.env[ge]!=="1"||this.bootstrapSessionId||this.sessionState||!t))try{let e=await this.buildToolActivityIntegration().recordedBackendSessionId(t);if(!e){o.warn("Recovery daemon found no durable backend owner; a new session will be created",{rolloutId:t});return}if(!/^codex-[A-Za-z0-9_-]{1,180}$/.test(e)){o.warn("Recovery daemon ignored invalid durable backend owner",{rolloutId:t});return}this.bootstrapSessionId=e,this.recoveryBootstrapOwnerBound=!0,o.info("Recovery daemon reclaiming existing backend session",{rolloutId:t,sessionId:e})}catch(e){o.warn("Recovery daemon could not read the durable rollout owner; a new session will be created",{rolloutId:t,error:e instanceof Error?e.message:String(e)})}}isSessionIdReservedRejection(t){return(t instanceof Error?t.message:String(t)).includes("SESSION_ID_RESERVED")}async resumeOrCreateBootstrapSession(t){try{return await this.resumeBackendSession(t,this.appSyncClient,o)}catch(e){if(!this.recoveryBootstrapOwnerBound||!this.isSessionIdReservedRejection(e))throw e;return o.warn("Recovery discarded a permanently reserved pending create; retrying the durable rollout owner",{sessionId:t.sessionId}),this.resumeBackendSession(t,this.appSyncClient,o)}}startTmuxLifecycleMonitor(){if(this.tmuxLifecycleTimer)return;let t=process.env[H];t&&(this.tmuxLifecycleInitDeadlineMs=Date.now()+Oi,this.tmuxLifecycleTimer=setInterval(()=>{this.checkTmuxLifecycle(t)},xi),this.tmuxLifecycleTimer.unref?.(),this.checkTmuxLifecycle(t))}async tmuxSessionState(t){try{return await _i("tmux",["has-session","-t",t]),"alive"}catch(e){let i=_t(e);return i==="absent"?i:(o.warn("Could not inspect native Codex tmux session; preserving daemon",{tmuxSession:t,error:e instanceof Error?e.message:String(e)}),"unknown")}}async checkTmuxLifecycle(t){if(!(this.isStopping||this.tmuxLifecycleCheckInFlight)){this.tmuxLifecycleCheckInFlight=!0;try{let e=await this.tmuxSessionState(t);if(e==="alive"){this.tmuxLifecycleObservedAlive=!0;return}let i=this.tmuxLifecycleObservedAlive||Date.now()>=this.tmuxLifecycleInitDeadlineMs;if(e==="unknown"||!i||this.isStopping)return;this.stopTmuxLifecycleMonitor(),o.info("Native Codex tmux session ended; stopping companion daemon",{tmuxSession:t}),this.stop().then(()=>process.exit(0),s=>{o.error("Failed to stop companion daemon after native session end",{error:s instanceof Error?s.message:String(s)}),process.exit(1)})}finally{this.tmuxLifecycleCheckInFlight=!1}}}stopTmuxLifecycleMonitor(){this.tmuxLifecycleTimer&&(clearInterval(this.tmuxLifecycleTimer),this.tmuxLifecycleTimer=null)}async stopForTerminalSignal(t){if(t==="SIGHUP"){let e=process.env[H];if(e){let i=await this.tmuxSessionState(e);if(i!=="absent")return o.info("Ignoring SIGHUP while native Codex tmux session remains available",{tmuxSession:e,state:i}),!1}}return await this.stop(),!0}isLifecycleCurrent(t){return!this.isStopping&&this.lifecycleGeneration===t}authoritativeSessionIdFromError(t){let e=t?.authoritativeSessionId;return typeof e=="string"&&e.length>0?e:null}async retireCancelledBootstrap(t,e){this.appSyncClient.stopHeartbeat(t),this.appSyncClient.cleanupSubscription(t);try{await this.appSyncClient.updateSession({sessionId:t,status:h.SessionStatus.INACTIVE}),o.info("Cancelled bootstrap row marked INACTIVE",{sessionId:t,path:e})}catch(i){o.warn("Cancelled bootstrap row could not be marked INACTIVE",{sessionId:t,path:e,error:i instanceof Error?i.message:String(i)})}}bindBootstrapIdentityFromError(t){let e=t?.authoritativeSessionId;typeof e!="string"||e.length===0||(this.bootstrapSessionId=e,o.info("Retained authoritative backend identity from failed bootstrap",{sessionId:e}))}recoverRetiredBackendSession(t){if(this.retiredSessionRecoveryPromise)return this.retiredSessionRecoveryPromise;if(this.isStopping)return Promise.resolve();let e=(async()=>{if(this.launchSessionInitPromise&&await this.launchSessionInitPromise,this.sessionStartedInitPromise&&await this.sessionStartedInitPromise,this.isStopping)return;let i=this.sessionState;if(!i||i.sessionId!==t)return;let s={id:i.codexSessionId||t,timestamp:new Date().toISOString(),cwd:i.cwd||i.projectPath,originator:"codevibe-retired-session-recovery",cli_version:String(i.metadata?.cliVersion??"unknown"),instructions:null,source:"codevibe",model_provider:String(i.metadata?.modelProvider??"unknown")};o.warn("Backend retired live Codex session; creating replacement row",{sessionId:t,codexSessionId:s.id}),this.parkRetirementOrphans(t),await this.endActiveSession("backend-generation-retired"),this.bootstrapSessionId=V.mintBackendSessionId(),this.recoveryBootstrapOwnerBound=!1,this.clearSessionBootstrapFailure(),await this.ensureSessionStarted(s)})();return this.retiredSessionRecoveryPromise=e.finally(()=>{this.retiredSessionRecoveryPromise=null}),this.retiredSessionRecoveryPromise}recordSessionBootstrapFailure(t,e){this.sessionBootstrapFailureCount+=1;let i=t instanceof Error?t.message:String(t),r=i.includes("SESSION_LIMIT_EXCEEDED")?6e4:Math.min(5e3*2**(this.sessionBootstrapFailureCount-1),6e4);this.sessionBootstrapRetryAt=Date.now()+r,o.warn("Session bootstrap retry scheduled",{path:e,requestedSessionId:this.bootstrapSessionId,failureCount:this.sessionBootstrapFailureCount,delayMs:r,error:i})}clearSessionBootstrapFailure(){this.sessionBootstrapFailureCount=0,this.sessionBootstrapRetryAt=0}async flushBufferedLogEntries(){if(this.bufferedLogEntries.length===0)return;let t=this.bufferedLogEntries;this.bufferedLogEntries=[],o.info("Flushing buffered log entries after session initialization",{count:t.length,sessionId:this.sessionState?.sessionId});let e=Promise.resolve();for(let i of t)e=this.enqueueLogEntry(i);await e.catch(()=>{})}enqueueLogEntry(t){let e=this.logEntryChain.then(()=>this.handleLogEntry(t));return this.logEntryChain=e.catch(i=>{o.error("[JSONL] log entry handler failed",{type:t.type,err:String(i)})}),e}async handleLogEntry(t){if(!this.sessionState){if(this.isInitializingSession){this.bufferedLogEntries.push(t),o.debug("Buffering log entry until session initialization completes",{type:t.type,bufferedCount:this.bufferedLogEntries.length});return}o.warn("Received log entry but no active session");return}let e=this.sessionState,i=e.sessionId,{sessionKey:s,sessionIsEncrypted:r}=e.encryptionSnapshot;if(t.type==="event_msg"&&t.payload?.type==="turn_aborted"){this.approvalDetector.clear(),this.clearInteractivePromptsForTurnAbort(e);return}if(t.type==="response_item"&&t.payload){let f=t.payload.type;if(f==="function_call"||f==="custom_tool_call")this.approvalDetector.onToolCallStart(t.payload.call_id,t.payload.name,t.payload.arguments||t.payload.input||"");else if(f==="function_call_output"||f==="custom_tool_call_output"){let v=this.approvalDetector.getPendingCalls().find(b=>b.callId===t.payload.call_id),S=v?this.buildApprovalDedupeKey(this.buildApprovalPromptContextFromPendingCall(v)):void 0;this.approvalDetector.onToolCallComplete(t.payload.call_id);let P=this.observePermissionRequestCompletion(S,"jsonl");if(P.shouldResolve&&await this.clearResolvedInteractivePrompt(t.payload.call_id,P.occurrenceKey??S),!this.isExpectedSessionStateCurrent(e))return}}let n=Ye(t,i);if(!n)return;n.timestamp=(0,h.prepareEventTimestamp)({orderingKey:i,agentClock:t.timestamp});let l=t.payload?.type,p=!1;if(this.toolActivity?.isActive()&&n.type===h.EventType.TOOL_USE){let f=l==="function_call"||l==="custom_tool_call",v=l==="function_call_output"||l==="custom_tool_call_output";if(f&&typeof t.payload?.call_id=="string"){let S=this.sessionWatcher.getActiveLogFile();S&&this.toolActivity.bindTranscript(S);let P=await this.toolActivity.observeCall({callId:t.payload.call_id,toolName:n.metadata?.toolName??n.metadata?.tool_name,toolInput:n.metadata?.toolInput??n.metadata?.tool_input,ts:t.timestamp});if(!this.isExpectedSessionStateCurrent(e)||P!=="closed")return;p=!0}if(v)return}if(this.toolActivity?.isActive()&&(l==="user_message"||l==="agent_message")&&(await this.toolActivity.flush().catch(f=>o.warn("[tool-activity] codex boundary flush failed",{sessionId:i,err:String(f)})),!this.isExpectedSessionStateCurrent(e)))return;let c=l==="function_call"||l==="function_call_output",d=n.type===h.EventType.USER_PROMPT||n.type===h.EventType.ASSISTANT_RESPONSE||c&&(n.type===h.EventType.TOOL_USE||n.type===h.EventType.INTERACTIVE_PROMPT);if(!this.hooksActive&&d&&(await this.emitHooksCompatibilityModeNotice(e),!this.isExpectedSessionStateCurrent(e)))return;if(this.hooksActive){if(n.type===h.EventType.USER_PROMPT){o.debug("[JSONL] Skipping USER_PROMPT \u2014 hooks deliver it",{type:n.type});return}if(c&&(n.type===h.EventType.TOOL_USE||n.type===h.EventType.INTERACTIVE_PROMPT)&&!p){o.debug("[JSONL] Skipping function_call \u2014 hooks deliver this",{type:n.type,tool:t.payload?.name});return}}if(n.type===h.EventType.USER_PROMPT&&n.source===h.EventSource.DESKTOP){let f=this.consumeRecentMobilePrompt(i,n.content);if(f){o.info("[JSONL] Skipping duplicate USER_PROMPT from mobile",{sessionId:i,matchType:f.matchType,originalLength:f.originalLength,echoLength:f.echoLength});return}}if(this.sessionState!==e){o.warn("[JSONL] Dropping event from stale session generation (#638)",{sessionId:i,currentSessionId:this.sessionState?.sessionId});return}let u=t.payload?.call_id,m=n.type===h.EventType.TOOL_USE&&typeof u=="string"&&u.length>0&&!!s,y=m?this.getToolActivityLegacyRecorder(i):void 0;y&&m&&y.markLegacyInFlight(u);try{let f=(0,h.encryptForEmit)(s,r,n.content,n.metadata);if(!f){o.error("No session key for ENCRYPTED session \u2014 dropping JSONL event (fail-closed, #638)",{type:n.type,sessionId:i});return}n.content=f.content,n.metadata=f.metadata,f.isEncrypted&&(n.isEncrypted=!0,o.debug("Event encrypted",{type:n.type}));let v=await this.appSyncClient.createEvent(n);o.debug("Event synced to backend",{type:n.type,encrypted:f.isEncrypted}),y&&m&&((0,h.isPersistedEventResult)(v)?await y.finalizeLegacyOwned([u]):y.clearLegacyInFlight(u))}catch(f){y&&m&&y.clearLegacyInFlight(u),o.error("Failed to sync event:",f)}}async handlePermissionRequestHook(t,e,i){if(!this.isExpectedSessionStateCurrent(e)){i&&(i.disposition="dropped");return}let s=i?.context??this.buildApprovalPromptContextFromHookPayload(t),r=i?.dedupeKey??this.buildApprovalDedupeKey(s);if(this.consumeRecoveryPermissionReplay(e,r,i))return;let n=i?.occurrenceKey??`permission:${(0,ee.v4)()}`,l={ownerState:e,context:s,dedupeKey:r,occurrenceKey:n,registration:i};this.permissionRequestHookInFlight=l;try{let p=await this.handlePermissionRequestHookWhileTracked(t,e,s,n,r);i&&(i.disposition=p)}finally{this.permissionRequestHookInFlight===l&&(this.permissionRequestHookInFlight=null)}}buildApprovalPromptContextFromHookPayload(t){let e=t.metadata||{},i=typeof e.tool_name=="string"?e.tool_name:"Tool",s=e.tool_input,r=this.firstString(s?.command,s?.cmd),n=ne(r)||Q(r),p=(i==="apply_patch"?this.firstString(s?.patch,r,typeof s=="string"?s:void 0):void 0)||n,c=p?"apply_patch":i,d=p||this.stringifyToolInput(s);return{toolName:c,toolInput:s,rawInput:d,filePath:this.extractFilePathFromToolInput(c,s,d),diff:c==="apply_patch"?d:void 0,hint:this.buildPermissionRequestHint(c,s,d)}}async handlePermissionRequestHookWhileTracked(t,e,i=this.buildApprovalPromptContextFromHookPayload(t),s=`permission:${(0,ee.v4)()}`,r){let n=t.metadata||{},{toolName:l,toolInput:p,rawInput:c}=i,d=this.buildToolDetailsForInteractivePrompt(i),u=d.tool_name||this.mapToolNameForApproval(l),m=d.tool_input||this.buildFallbackToolInput(i),y=!!(u&&m);if(r??=this.buildApprovalDedupeKey({toolName:l,toolInput:p,filePath:i.filePath,rawInput:c,hint:i.hint}),this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,s))return"resolved";let f=await this.tryParsePermissionRequestPromptFromTmux((N,A)=>this.parsedPromptMatchesApprovalContext(N,i,A),()=>this.isExpectedSessionStateCurrent(e));if(!this.isExpectedSessionStateCurrent(e))return o.warn("[Hooks] Dropping stale PermissionRequest after session generation changed",{sessionId:e.sessionId,currentSessionId:this.sessionState?.sessionId}),"dropped";if(this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,s))return o.info("[Hooks] Dropping PermissionRequest completed while terminal options were being parsed",{sessionId:e.sessionId,toolName:l,occurrenceKey:s}),"resolved";let v=f?.parsedPrompt??null;if(!v)return o.warn("[Hooks] Suppressing PermissionRequest mobile prompt because exact Codex options are unavailable",{sessionId:e.sessionId,toolName:l,hint:i.hint}),this.raiseSuppressedBadge(e.sessionId,s,{suppressTmuxObserverUntilResolved:!0,observerSnapshot:f?.snapshot,completionKey:r}),"covered";let S=this.mapToolNameForApproval(l)==="Bash"?this.firstString(p?.command,p?.cmd):void 0,P=this.buildCodexPromptPresentation(v,S,f?.snapshot);if(!P)return o.warn("[Hooks] Suppressing PermissionRequest mobile prompt because Codex option hotkeys are unavailable",{sessionId:e.sessionId,toolName:l,hint:i.hint}),this.raiseSuppressedBadge(e.sessionId,s,{suppressTmuxObserverUntilResolved:!0,observerSnapshot:f?.snapshot,completionKey:r}),"covered";let b=s,T=this.buildApprovalPromptContent(P.content,{toolName:u,toolInput:m,hint:i.hint,filePath:i.filePath}),k={promptId:b,kind:P.kind,options:P.options,submitMap:P.submitMap,promptText:T,createdAt:Date.now(),source:"permission_hook",dedupeKey:s,completionKey:r,ownerSessionId:e.sessionId,observerMatchKey:this.buildObserverPromptMatchKey(v),requiresFollowUpText:P.requiresFollowUpText},O={isApprovalHint:!0,toolName:l,toolInput:p,hint:i.hint,filePath:i.filePath,diff:i.diff,rawInput:c,tool_name:u,tool_input:m,has_details:y,options:P.options,instructions:P.instructions,prompt_source:v?"permission_hook_tmux":"permission_hook",permission_mode:n.permission_mode,turn_id:n.turn_id,dedupe_key:s};return o.info("[Hooks] Sending PermissionRequest interactive prompt",{sessionId:e.sessionId,toolName:l,promptId:b,promptSource:O.prompt_source,occurrenceKey:s}),await this.enqueueOrEmitInteractivePrompt({prompt:k,content:T,metadata:O,ownerState:e}),this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,s)?"resolved":"covered"}async emitHooksCompatibilityModeNotice(t){if(this.hooksCompatibilityNoticeSent||!this.isExpectedSessionStateCurrent(t))return;this.hooksCompatibilityNoticeSent=!0;let e="Codex hooks are not active. CodeVibe is using compatibility mode: mobile approvals still mirror the desktop prompt, but timeline events may be delayed or incomplete. Open Codex /hooks and trust/enable CodeVibe hooks for full fidelity.",i={compatibility_mode:!0,reason:"codex_hooks_inactive",action:"trust_enable_codevibe_hooks"};o.warn("Codex hooks inactive; running in compatibility mode",{sessionId:t.sessionId});let s=this.encryptForEmit(e,i,h.EventType.NOTIFICATION,t.sessionId);if(s)try{await this.appSyncClient.createEvent({sessionId:t.sessionId,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,content:s.content,metadata:s.metadata,timestamp:(0,h.prepareEventTimestamp)({orderingKey:t.sessionId}),...s.isEncrypted?{isEncrypted:!0}:{}})}catch(r){o.warn("Failed to emit hooks compatibility mode notification",{error:r})}}async handleApprovalPending(t){let e=this.sessionState;if(!e)return;if((this.queuedPermissionRequestSessionCounts.get(e.sessionId)??0)>0){o.debug("Skipping heuristic approval prompt while PermissionRequest owns the session prompt",{sessionId:e.sessionId,callId:t.callId});return}let i=this.buildApprovalDedupeKey(t);if(i&&this.hasRecentlyResolvedApprovalDedupeKey(e.sessionId,i)){o.info("Skipping heuristic approval prompt; same approval was already answered recently",{callId:t.callId,toolName:t.toolName,dedupeKey:i});return}if(i&&this.hasDurablePermissionHookFallbackByDedupeKey(e.sessionId,i)){o.debug("Skipping heuristic approval prompt already owned by a hook fallback",{sessionId:e.sessionId,callId:t.callId,dedupeKey:i});return}if(i&&this.hasActiveOrQueuedPermissionHookPrompt(e.sessionId,i)){this.mergeCallIdIntoPromptByDedupeKey(e.sessionId,i,t.callId),o.info("Skipping heuristic approval prompt; PermissionRequest hook already emitted it",{callId:t.callId,toolName:t.toolName,dedupeKey:i});return}o.info("Sending approval pending interactive prompt",t);try{let s=await this.tryParseInteractivePromptFromTmux(),r=s?.parsedPrompt??null;if((this.queuedPermissionRequestSessionCounts.get(e.sessionId)??0)>0||r&&this.hasMatchingDurablePermissionHookFallback(r,e.sessionId,s?.snapshot)||r&&this.hasMatchingPermissionHookPrompt(r,e.sessionId)){o.debug("Skipping heuristic approval prompt after PermissionRequest claimed the terminal dialog",{sessionId:e.sessionId,callId:t.callId});return}let n=this.buildToolDetailsForInteractivePrompt(t,s?.snapshot),l=n.tool_name||this.mapToolNameForApproval(t.toolName),p=n.tool_input||this.buildFallbackToolInput(t),c=!!(l&&p);if(!r){o.warn("Suppressing heuristic approval prompt because exact Codex options are unavailable",{callId:t.callId,toolName:t.toolName,hint:t.hint}),this.raiseSuppressedBadge(e.sessionId,i);return}if(!this.parsedPromptMatchesApprovalContext(r,t,s?.snapshot)){o.warn("Suppressing heuristic approval prompt because parsed tmux prompt does not match the active tool",{callId:t.callId,toolName:t.toolName,hint:t.hint,parsedPromptText:r.promptText}),this.raiseSuppressedBadge(e.sessionId,i);return}let d=this.mapToolNameForApproval(t.toolName)==="Bash"?this.firstString(t.toolInput?.command,t.toolInput?.cmd):void 0,u=this.buildCodexPromptPresentation(r,d,s?.snapshot);if(!u){o.warn("Suppressing heuristic approval prompt because Codex option hotkeys are unavailable",{callId:t.callId,toolName:t.toolName,hint:t.hint}),this.raiseSuppressedBadge(e.sessionId,i);return}let m=u.options,y=this.buildApprovalPromptContent(u.content,{toolName:l,toolInput:p,hint:t.hint,filePath:t.filePath}),f={promptId:t.callId,callId:t.callId,kind:u.kind,options:m,submitMap:u.submitMap,promptText:u.promptText,createdAt:Date.now(),source:r?"tmux":"heuristic",dedupeKey:i,ownerSessionId:e.sessionId,observerMatchKey:this.buildObserverPromptMatchKey(r),requiresFollowUpText:u.requiresFollowUpText},v={isApprovalHint:!0,toolName:t.toolName,toolInput:t.toolInput,hint:t.hint,callId:t.callId,filePath:t.filePath,diff:t.diff,rawInput:t.rawInput,tool_name:l,tool_input:p,has_details:c,options:m,instructions:u.instructions,prompt_source:r?"tmux":"heuristic",dedupe_key:i};o.debug("Interactive prompt (pre-encryption)",{sessionId:e.sessionId,callId:t.callId,contentPreview:y.substring(0,200),toolDetails:n,metadata:v}),await this.enqueueOrEmitInteractivePrompt({prompt:f,content:y,metadata:v,ownerState:e})}catch(s){o.error("Failed to send approval interactive prompt:",s)}}async handleTmuxPromptCandidate(t,e){let i=this.sessionState;if(!i){this.tmuxPaneObserver.resetLastPromptHash();return}if(!e&&this.deferTmuxPromptCandidateForPermissionHook(i.sessionId,t))return;let s=e?.occurrenceKey;if(s&&this.hasRecentlyResolvedApprovalDedupeKey(i.sessionId,s))return;let r=(0,B.parseInteractivePrompt)(t);if(!r){this.raiseSuppressedBadge(i.sessionId,s??null);return}if(this.hasMatchingPermissionHookPrompt(r,i.sessionId)){o.debug("Skipping tmux prompt candidate already owned by PermissionRequest",{sessionId:i.sessionId,promptText:r.promptText});return}let n=this.getMostRecentPendingToolCall();if(!n){if(await new Promise(O=>setTimeout(O,500)),!this.isExpectedSessionStateCurrent(i)){o.warn("Dropping tmux prompt candidate after session generation changed",{sessionId:i.sessionId,currentSessionId:this.sessionState?.sessionId});return}if(!e&&this.deferTmuxPromptCandidateForPermissionHook(i.sessionId,t))return;n=this.getMostRecentPendingToolCall()}let l=e?.context??(n?this.buildApprovalPromptContextFromPendingCall(n):null);if(l&&!this.parsedPromptMatchesApprovalContext(r,l,t)){o.warn("Skipping tmux-detected prompt because parsed prompt does not match pending tool call",{callId:n?.callId,toolName:n?.name,parsedPromptText:r.promptText}),this.raiseSuppressedBadge(i.sessionId,s??null);return}let p=l?null:this.buildApprovalPromptContextFromParsedPrompt(r),c=l||p;if(!c){o.warn("Skipping tmux-detected prompt because no tool context could be derived from the parsed prompt",{parsedPromptText:r.promptText}),this.raiseSuppressedBadge(i.sessionId,s??null);return}let d=this.mapToolNameForApproval(c.toolName)==="Bash"?this.firstString(c.toolInput?.command,c.toolInput?.cmd):void 0,u=this.buildCodexPromptPresentation(r,d,t);if(!u){o.warn("Skipping tmux-detected prompt because Codex option hotkeys are unavailable",{parsedPromptText:r.promptText}),this.raiseSuppressedBadge(i.sessionId,s??null);return}let m=c?this.buildApprovalDedupeKey(c):void 0;if(m&&this.hasRecentlyResolvedApprovalDedupeKey(i.sessionId,m)){o.info("Skipping tmux-detected prompt; same approval was already answered recently",{promptText:r.promptText,dedupeKey:m});return}if(m&&this.hasActiveOrQueuedPermissionHookPrompt(i.sessionId,m)){n?.callId&&this.mergeCallIdIntoPromptByDedupeKey(i.sessionId,m,n.callId),o.info("Skipping tmux-detected prompt; PermissionRequest hook already emitted it",{promptText:r.promptText,dedupeKey:m});return}let y=c?this.buildToolDetailsForInteractivePrompt(c,t):{},f=y.tool_name||this.mapToolNameForApproval(n?.name),v=y.tool_input||(c?this.buildFallbackToolInput(c):void 0),S=!!(f&&v),b={promptId:n?.callId||(0,ee.v4)(),callId:n?.callId,kind:u.kind,options:u.options,submitMap:u.submitMap,promptText:u.promptText,createdAt:Date.now(),source:"tmux",dedupeKey:m,ownerSessionId:i.sessionId,observerMatchKey:this.buildObserverPromptMatchKey(r),requiresFollowUpText:u.requiresFollowUpText},T={options:u.options,instructions:u.instructions,prompt_source:"tmux_live",toolName:c?.toolName,toolInput:c?.toolInput,hint:c?.hint,filePath:c?.filePath,diff:c?.diff,rawInput:c?.rawInput,tool_name:f,tool_input:v,has_details:S,dedupe_key:m},k=this.buildApprovalPromptContent(u.content,{toolName:f,toolInput:v,hint:c?.hint,filePath:c?.filePath});try{await this.enqueueOrEmitInteractivePrompt({prompt:b,content:k,metadata:T,ownerState:i})&&o.info("Sent tmux-detected interactive prompt",{sessionId:i.sessionId,promptText:r.promptText,kind:r.kind})}catch(O){o.error("Failed to send tmux-detected interactive prompt",{error:O})}}async enqueueOrEmitInteractivePrompt(t){if(!this.isExpectedSessionStateCurrent(t.ownerState))return o.warn("Dropping interactive prompt from stale session generation",{sessionId:t.ownerState.sessionId,currentSessionId:this.sessionState?.sessionId,source:t.prompt.source}),!1;if(t.prompt.ownerSessionId??=t.ownerState.sessionId,t.prompt.dedupeKey&&this.hasRecentlyResolvedApprovalDedupeKey(t.ownerState.sessionId,t.prompt.dedupeKey))return o.debug("Skipping interactive prompt emission; same approval was already answered recently",{source:t.prompt.source,callId:t.prompt.callId,dedupeKey:t.prompt.dedupeKey}),!1;let e=this.pendingInteractivePrompt;if(e&&e.ownerSessionId!==t.ownerState.sessionId&&(o.warn("Discarding active prompt from a stale session generation",{activeSessionId:e.ownerSessionId,candidateSessionId:t.ownerState.sessionId}),this.pendingInteractivePrompt=null,e=null),!e){this.pendingInteractivePrompt=t.prompt;try{return await this.emitInteractivePromptEvent(t),!0}catch(i){throw this.pendingInteractivePrompt?.promptId===t.prompt.promptId&&(this.pendingInteractivePrompt=null),i}}if(this.isSameInteractivePrompt(e,t.prompt)){if(t.prompt.source==="permission_hook"&&e.source!=="permission_hook"){e.source="permission_hook",e.completionKey=t.prompt.completionKey??e.dedupeKey,e.dedupeKey=t.prompt.dedupeKey;let i=this.e1PromptRaises.get(e.promptId);i&&(i.resolutionDedupeKey=e.dedupeKey,i.resolutionCompletionKey=e.completionKey)}return!e.callId&&t.prompt.callId&&(e.callId=t.prompt.callId,o.debug("Merged callId into existing interactive prompt",{source:e.source,callId:t.prompt.callId,promptText:e.promptText})),o.debug("Skipping duplicate interactive prompt emission",{existingSource:e.source,candidateSource:t.prompt.source,existingCallId:e.callId,candidateCallId:t.prompt.callId,promptText:t.prompt.promptText}),!1}return this.queuedInteractivePrompts.some(i=>this.isSameInteractivePrompt(i.prompt,t.prompt))?(o.debug("Skipping duplicate queued interactive prompt",{candidateSource:t.prompt.source,candidateCallId:t.prompt.callId,promptText:t.prompt.promptText}),!1):(this.queuedInteractivePrompts.push(t),o.info("Queued interactive prompt behind active prompt",{activeCallId:e.callId,queuedCallId:t.prompt.callId,queueLength:this.queuedInteractivePrompts.length}),!1)}async emitInteractivePromptEvent(t){if(!this.isExpectedSessionStateCurrent(t.ownerState))return;let e=t.ownerState.sessionId,i=this.e1PromptRaises.get(t.prompt.promptId),s=i?i.record:this.newE1Raise(e,!0,void 0,{...t.prompt.dedupeKey!==void 0?{dedupeKey:t.prompt.dedupeKey}:{},...t.prompt.completionKey!==void 0?{completionKey:t.prompt.completionKey}:{},...t.prompt.callId!==void 0?{callId:t.prompt.callId}:{}});t.prompt.promptId=s.promptId;let r=this.e1PromptRaises.get(s.promptId);r&&(r.resolutionDedupeKey??=t.prompt.dedupeKey,r.resolutionCompletionKey??=t.prompt.completionKey,r.resolutionCallId??=t.prompt.callId),t.prompt.dedupeKey&&this.e1ContentKeyIndex.set(`${e}::${t.prompt.dedupeKey}`,s.promptId),this.stashE1RaiseContent(s.promptId,t.content,t.metadata??{});let n=this.encryptForEmit(t.content,t.metadata,h.EventType.INTERACTIVE_PROMPT,e);n&&await this.trackE1Raise(s.promptId,this.appSyncClient.createEvent({sessionId:e,type:h.EventType.INTERACTIVE_PROMPT,source:h.EventSource.DESKTOP,content:n.content,metadata:n.metadata,...(0,h.raiseFieldsFromRecord)(s),timestamp:(0,h.prepareEventTimestamp)({orderingKey:e}),...n.isEncrypted?{isEncrypted:!0}:{}}))}async emitNextQueuedInteractivePrompt(){if(this.pendingInteractivePrompt||this.queuedInteractivePrompts.length===0)return;let t=this.queuedInteractivePrompts.shift();if(t){if(!this.isExpectedSessionStateCurrent(t.ownerState)){o.warn("Discarding queued prompt from stale session generation",{sessionId:t.ownerState.sessionId,currentSessionId:this.sessionState?.sessionId}),await this.emitNextQueuedInteractivePrompt();return}this.pendingInteractivePrompt=t.prompt;try{await this.emitInteractivePromptEvent(t),o.info("Emitted next queued interactive prompt",{callId:t.prompt.callId,queueLength:this.queuedInteractivePrompts.length})}catch(e){this.pendingInteractivePrompt=null,o.error("Failed to emit queued interactive prompt",{error:e}),await this.emitNextQueuedInteractivePrompt()}}}async clearResolvedInteractivePrompt(t,e){if(!e){o.debug("Interactive prompt completion had no comparable dialog identity",{callId:t});return}let i=this.isPermissionRequestOccurrenceKey(e),s=this.pendingInteractivePrompt,r=!!s&&(s.dedupeKey===e||s.completionKey===e||!i&&!!t&&s.callId===t),n=this.sessionState?.sessionId??s?.ownerSessionId,l=this.queuedInteractivePrompts.find(P=>!i&&!!t&&P.prompt.callId===t||P.prompt.dedupeKey===e||P.prompt.completionKey===e),p=this.permissionRequestHookInFlight,c=p?.ownerState.sessionId===n&&(p?.dedupeKey===e||p?.occurrenceKey===e),u=(n?this.queuedPermissionRequestRegistrations.get(n)??[]:[]).find(P=>P.dedupeKey===e||P.occurrenceKey===e),m=r?s?.dedupeKey:l?.prompt.dedupeKey??(c?p?.occurrenceKey:void 0)??u?.occurrenceKey??e,y=new Set;if(r&&s&&y.add(s.promptId),l&&y.add(l.prompt.promptId),n&&m){let P=this.e1ContentKeyIndex.get(`${n}::${m}`);P&&y.add(P)}let f=[...this.e1PromptRaises.entries()].find(([,P])=>P.record.sessionId===n&&(P.resolutionDedupeKey===m||!i&&!!t&&P.resolutionCallId===t||P.resolutionCompletionKey===e));if(f&&y.add(f[0]),(r||c||u||y.size>0)&&this.rememberResolvedApprovalDedupeKey(n,m),r&&s&&(this.pendingInteractivePrompt=null),u&&(u.disposition="resolved"),m)for(let P of this.removeQueuedInteractivePromptByDedupeKey(m))y.add(P);let S=n?this.beginE1PromptResolutions(y,n):[];this.launchE1Resolutions(S),(r||!this.pendingInteractivePrompt&&l)&&await this.emitNextQueuedInteractivePrompt()}clearInteractivePromptsForTurnAbort(t){let e=t.sessionId,i=new Set,s=this.pendingInteractivePrompt;s?.ownerSessionId===e&&(i.add(s.promptId),this.rememberResolvedApprovalDedupeKey(e,s.dedupeKey),this.pendingInteractivePrompt=null);let r=[];for(let l of this.queuedInteractivePrompts)l.prompt.ownerSessionId===e?(i.add(l.prompt.promptId),this.rememberResolvedApprovalDedupeKey(e,l.prompt.dedupeKey)):r.push(l);this.queuedInteractivePrompts=r;for(let[l,p]of this.e1PromptRaises.entries())p.record.sessionId===e&&i.add(l);for(let l of this.queuedPermissionRequestRegistrations.get(e)??[])l.disposition="resolved",this.rememberResolvedApprovalDedupeKey(e,l.occurrenceKey),this.deferredTmuxPromptCandidates.delete(l);this.permissionRequestOccurrencesByCompletionKey.clear();let n=this.beginE1PromptResolutions(i,e);this.launchE1Resolutions(n)}removeQueuedInteractivePromptByDedupeKey(t){let e=this.queuedInteractivePrompts.length,i=this.queuedInteractivePrompts.filter(s=>s.prompt.dedupeKey===t);for(let s of i)this.rememberResolvedApprovalDedupeKey(s.ownerState.sessionId,s.prompt.dedupeKey);return this.queuedInteractivePrompts=this.queuedInteractivePrompts.filter(s=>s.prompt.dedupeKey!==t),this.queuedInteractivePrompts.length!==e&&o.debug("Removed resolved deduped tool call from interactive prompt queue",{dedupeKey:t,removed:e-this.queuedInteractivePrompts.length}),i.map(s=>s.prompt.promptId)}mergeCallIdIntoPromptByDedupeKey(t,e,i){let s=this.pendingInteractivePrompt;if(s?.ownerSessionId===t&&(s.completionKey??s.dedupeKey)===e&&!s.callId){s.callId=i;let n=this.e1PromptRaises.get(s.promptId);n&&(n.resolutionCallId??=i),o.debug("Merged callId into active deduped interactive prompt",{source:s.source,callId:i,dedupeKey:e});return}let r=this.queuedInteractivePrompts.find(n=>n.prompt.ownerSessionId===t&&(n.prompt.completionKey??n.prompt.dedupeKey)===e&&!n.prompt.callId);r&&(r.prompt.callId=i,o.debug("Merged callId into queued deduped interactive prompt",{source:r.prompt.source,callId:i,dedupeKey:e}))}isSameInteractivePrompt(t,e){if(t.ownerSessionId!==e.ownerSessionId||Date.now()-t.createdAt>Ai)return!1;if(t.callId&&e.callId)return t.callId===e.callId;let s=t.completionKey??t.dedupeKey,r=e.completionKey??e.dedupeKey;return t.source!==e.source&&s&&s===r&&t.observerMatchKey&&t.observerMatchKey===e.observerMatchKey?!0:t.dedupeKey&&e.dedupeKey?t.dedupeKey===e.dedupeKey:t.kind===e.kind&&this.normalizePromptDedupeText(t.promptText)===this.normalizePromptDedupeText(e.promptText)}normalizePromptDedupeText(t){return t.replace(/\s+/g," ").trim().toLowerCase()}async startTmuxObserver(){let t=process.env.CODEVIBE_CODEX_TMUX_SESSION;if(!t)return o.debug("Skipping tmux pane observer start - no tmux session in environment"),!0;try{return await this.tmuxPaneObserver.start(t),!0}catch(e){return o.warn("Failed to start tmux pane observer",{tmuxSession:t,error:e}),!1}}async startTmuxObserverWithBeacon(t){try{await this.startTmuxObserver()?await R("daemon_init_step_completed",{step:"tmux_observer_start",path:t}):await R("daemon_init_step_failed",{step:"tmux_observer_start",path:t,error_class:"TmuxObserverStartFailed",error_message:"tmuxPaneObserver.start threw (see plugin log)"})}catch(e){await R("daemon_init_step_failed",{step:"tmux_observer_start",path:t,error_class:e?.name||"Error",error_message:e?.message||String(e)}),o.error("Failed to start tmux observer:",e)}}async tryParseInteractivePromptFromTmux(){try{let t=await this.tmuxPaneObserver.captureSnapshot(),e=this.tmuxPaneObserver.extractActiveApprovalBlock(t),i=e?(0,B.parseInteractivePrompt)(e):null;return o.debug("tmux prompt parse result",{parsed:!!i,kind:i?.kind,promptText:i?.promptText,isolated:!!e,snapshotPreview:this.summarizePromptSnapshot(e??t)}),{parsedPrompt:i,snapshot:e??t}}catch(t){return o.debug("tmux prompt parsing unavailable",{error:t}),null}}async tryParsePermissionRequestPromptFromTmux(t,e){let r=null;for(let n=0;n<25;n+=1){if(e&&!e()||n>0&&(await new Promise(p=>setTimeout(p,125)),e&&!e()))return null;let l=await this.tryParseInteractivePromptFromTmux();if(l&&(r=l),l?.parsedPrompt&&(!t||t(l.parsedPrompt,l.snapshot)))return l;l?.parsedPrompt&&o.warn("Ignoring parsed PermissionRequest prompt because it does not match the active tool",{attempt:n+1,promptText:l.parsedPrompt.promptText})}return r?.snapshot?{parsedPrompt:null,snapshot:r.snapshot}:null}buildPromptPresentation(t){return t?{content:t.promptText,promptText:t.promptText,kind:t.kind,options:t.options,submitMap:t.submitMap,instructions:this.buildPromptInstructions(t),requiresFollowUpText:t.requiresFollowUpText}:{content:"Codex is waiting for approval.",promptText:"Codex is waiting for approval.",kind:"yes_no",options:[{number:"1",text:'Yes (sends "y")'},{number:"2",text:'No, tell Codex what to change (sends "n <instructions>")'}],submitMap:{1:"y",2:"n"},instructions:"Reply with 1 to approve, or 2 followed by what to change",requiresFollowUpText:!0}}buildCodexPromptPresentation(t,e,i){let s=this.buildPromptPresentation(t),r=Ot(s.options,e,i);if(!r)return null;let n=this.buildSubmitMapFromCodexOptionHotkeys(r);return n?{...s,options:r,submitMap:n,instructions:this.buildCodexPermissionInstructions(r),requiresFollowUpText:this.optionsRequireFollowUpText(r)}:null}buildSubmitMapFromCodexOptionHotkeys(t){let e={};for(let i of t){let s=i.text.match(/\(([^)]+)\)\s*$/)?.[1]?.trim().toLowerCase();if(!s||!ki.test(s))return null;s==="esc"||s==="escape"?e[i.number]=q:e[i.number]=s}return e}buildCodexPermissionInstructions(t){let e=t.find(n=>/\((?:y|yes)\)\s*$/i.test(n.text)),i=t.find(n=>/\(p\)\s*$/i.test(n.text)),s=t.find(n=>/\((?:esc|escape)\)\s*$/i.test(n.text)),r=[];return e&&r.push(`${e.number} to approve`),i&&r.push(`${i.number} to persist the desktop allow rule`),s&&r.push(`${s.number} followed by what to change`),r.length>0?`Reply with ${r.join(", ")}`:"Reply with the number of the option you want"}optionsRequireFollowUpText(t){return t.some(e=>/what to (?:do differently|change)|instructions/i.test(e.text))}getMostRecentPendingToolCall(){let t=this.approvalDetector.getPendingCalls();return t.length===0?null:t.reduce((e,i)=>i.timestamp>e.timestamp?i:e)}hasActiveOrQueuedPermissionHookPrompt(t,e){let i=this.pendingInteractivePrompt;return i?.ownerSessionId===t&&i.source==="permission_hook"&&(i.completionKey??i.dedupeKey)===e?!0:this.queuedInteractivePrompts.some(s=>s.prompt.ownerSessionId===t&&s.prompt.source==="permission_hook"&&(s.prompt.completionKey??s.prompt.dedupeKey)===e)}hasDurablePermissionHookFallback(t){return[...this.e1PromptRaises.values()].some(e=>e.record.sessionId===t&&e.suppressTmuxObserverUntilResolved===!0)}hasDurablePermissionHookFallbackByDedupeKey(t,e){return[...this.e1PromptRaises.values()].some(i=>i.record.sessionId===t&&i.suppressTmuxObserverUntilResolved===!0&&(i.resolutionDedupeKey===e||i.resolutionCompletionKey===e))}hasMatchingDurablePermissionHookFallback(t,e,i){let s=this.buildApprovalPromptContextFromParsedPrompt(t),r=s?this.buildApprovalDedupeKey(s):void 0;if(r&&this.hasDurablePermissionHookFallbackByDedupeKey(e,r))return!0;if(!i)return!1;let n=this.buildObserverSnapshotMatchKey(i);return[...this.e1PromptRaises.values()].some(l=>l.record.sessionId===e&&l.suppressTmuxObserverUntilResolved===!0&&l.suppressedObserverSnapshotKey===n)}deferTmuxPromptCandidateForPermissionHook(t,e){let i=this.queuedPermissionRequestSessionCounts.get(t)??0,s=this.hasDurablePermissionHookFallback(t);if(i<=0&&!s)return!1;let r=(0,B.parseInteractivePrompt)(e);if(r&&this.hasMatchingDurablePermissionHookFallback(r,t,e))return o.debug("Skipping tmux repaint already owned by a durable PermissionRequest fallback",{sessionId:t,promptText:r.promptText}),!0;if(r&&this.hasMatchingPermissionHookPrompt(r,t))return o.debug("Skipping tmux repaint already owned by an actionable PermissionRequest",{sessionId:t,promptText:r.promptText}),!0;if(r&&i<=0)return!1;if(i>0){let n=this.queuedPermissionRequestRegistrations.get(t)??[],l=r?n.find(d=>this.parsedPromptMatchesApprovalContext(r,d.context,e)):void 0,p=!r&&this.permissionRequestHookInFlight?.ownerState.sessionId===t?this.permissionRequestHookInFlight.registration:void 0,c=l??p??(n.length===1&&(!r||!s)?n[0]:void 0);if(c&&this.deferredTmuxPromptCandidates.set(c,e),r&&!c)return!1}return o.debug("Skipping tmux prompt candidate while PermissionRequest owns the session prompt",{sessionId:t,toolName:this.permissionRequestHookInFlight?.context.toolName,ownership:i>0?"queued_or_in_flight":"durable_prompt"}),!0}hasMatchingPermissionHookPrompt(t,e){let i=this.buildObserverPromptMatchKey(t);return[this.pendingInteractivePrompt,...this.queuedInteractivePrompts.map(r=>r.prompt)].some(r=>r?.source==="permission_hook"&&r.ownerSessionId===e&&r.observerMatchKey===i)}buildObserverPromptMatchKey(t){let e=t.options.map(s=>`${s.number}:${this.normalizeObserverPromptText(s.text)}`).join("|"),i=[t.kind,this.normalizeObserverPromptText(t.promptText),e].join("::");return`observer:${ve.createHash("sha256").update(i).digest("hex").slice(0,16)}`}buildObserverSnapshotMatchKey(t){let e=this.normalizeObserverPromptText(t);return`snapshot:${ve.createHash("sha256").update(e).digest("hex").slice(0,16)}`}normalizeObserverPromptText(t){return t.replace(/\r\n?/g,`
26
27
  `).split(`
27
28
  `).map(e=>e.replace(/[ \t]+$/g,"")).join(`
28
- `).trim()}hasRecentlyResolvedApprovalDedupeKey(t,e){return t?(this.pruneResolvedApprovalDedupeKeys(),this.resolvedApprovalDedupeKeys.has(`${t}::${e}`)):!1}rememberResolvedApprovalDedupeKey(t,e){!t||!e||(this.pruneResolvedApprovalDedupeKeys(),this.resolvedApprovalDedupeKeys.set(`${t}::${e}`,Date.now()))}pruneResolvedApprovalDedupeKeys(){let t=Date.now()-Fe;for(let[e,i]of this.resolvedApprovalDedupeKeys.entries())i<t&&this.resolvedApprovalDedupeKeys.delete(e)}buildApprovalDedupeKey(t){let e=t.toolName||"Tool",i=this.firstString(t.toolInput?.command,t.toolInput?.cmd,t.rawInput);if(i)return`command:${this.hashDedupeValue(`${this.mapToolNameForApproval(e)||e}:${i}`)}`;let s=this.firstString(t.filePath,t.toolInput?.file_path,t.toolInput?.path,t.toolInput?.filePath);if(s)return`file:${this.hashDedupeValue(`${e}:${s}`)}`;let r=this.firstString(t.hint);if(r)return`hint:${this.hashDedupeValue(`${e}:${r}`)}`}hashDedupeValue(t){return ye.createHash("sha256").update(t).digest("hex").slice(0,16)}buildPermissionRequestHint(t,e,i){let s=this.extractFilePathFromToolInput(t,e,i);if(this.mapToolNameForApproval(t)==="Edit"&&s)return`File: ${s}`;let r=this.firstString(e?.command,e?.cmd);return r?`Command: ${this.truncateApprovalDetail(r,80)}`:s?`File: ${s}`:`Tool: ${this.mapToolNameForApproval(t)||t}`}extractFilePathFromToolInput(t,e,i){let s=this.firstString(e?.file_path,e?.path,e?.filePath);if(s)return s;if(t==="apply_patch"){let n=this.firstString(e?.command,e?.cmd,i)?.match(/\*\*\* (?:Update|Add|Delete) File: ([^\r\n]+)/);if(n)return n[1].trim()}}stringifyToolInput(t){if(t!=null){if(typeof t=="string")return t;try{return JSON.stringify(t)}catch{return String(t)}}}buildApprovalPromptContextFromPendingCall(t){let e=this.firstString(t.parsedInput?.command,t.parsedInput?.cmd),i=Y(t.input),s=ne(e)||Y(e),n=(t.name==="apply_patch"?t.input:"")||i||s,l=!!n,p=l?"apply_patch":t.name,c=l?n:t.input,d=l?t.parsedInput||{command:n}:t.parsedInput,u=l?this.extractFilePathFromToolInput(p,d,c):t.filePath;return{toolName:p,filePath:u,diff:l?c:t.diff,toolInput:d,rawInput:c,hint:u?`File: ${u}`:`Tool: ${this.mapToolNameForApproval(p)||p}`}}buildApprovalPromptContextFromParsedPrompt(t){let e=this.extractShellCommandFromPromptText(t.promptText);if(e)return{toolName:"Bash",toolInput:{command:e},rawInput:e,hint:`Command: ${this.truncateApprovalDetail(e,80)}`};let i=t.promptText.match(/^(?:Destination|File):\s*([^\r\n]+)$/mi)?.[1]?.trim();return i?{toolName:"apply_patch",filePath:i,toolInput:{file_path:i},hint:`File: ${i}`}:null}parsedPromptMatchesApprovalContext(t,e,i){let s=this.buildApprovalPromptContextFromParsedPrompt(t),r=this.mapToolNameForApproval(e.toolName)==="Bash"?this.firstString(e.toolInput?.command,e.toolInput?.cmd):void 0;if(r){if(xi(t.promptText,r,i))return!0;if(this.mapToolNameForApproval(s?.toolName)!=="Bash")return!1;let l=Me(t.promptText);return l?Et(l,r):!1}let n=this.firstString(e.filePath,e.toolInput?.file_path,e.toolInput?.path,e.toolInput?.filePath);return n?s?.toolName!=="apply_patch"||!s.filePath?!1:$.normalize(s.filePath)===$.normalize(n):!1}extractShellCommandFromPromptText(t){return wt(t)}buildPromptInstructions(t){return t.kind==="yes_no"&&t.requiresFollowUpText?"Reply with 1 to approve, or 2 followed by what to change":t.kind==="yes_no"?"Reply with 1 for yes or 2 for no":t.kind==="numbered"?"Reply with the number of the option you want":"Reply with your response"}buildApprovalPromptContent(t,e){let i=t.trim(),s=i==="Codex is waiting for approval.",r=/^\$\s+/.test(i),n=e.toolName?`${e.toolName} requires approval.`:"Codex is waiting for approval.",l=[s||r||!i?n:i],p=typeof e.toolInput?.command=="string"?e.toolInput.command.trim():void 0;if(p&&!l.join(`
29
+ `).trim()}hasRecentlyResolvedApprovalDedupeKey(t,e){return t?(this.pruneResolvedApprovalDedupeKeys(),this.resolvedApprovalDedupeKeys.has(`${t}::${e}`)):!1}rememberResolvedApprovalDedupeKey(t,e){!t||!e||(this.pruneResolvedApprovalDedupeKeys(),this.resolvedApprovalDedupeKeys.set(`${t}::${e}`,Date.now()))}pruneResolvedApprovalDedupeKeys(){let t=Date.now()-Fe;for(let[e,i]of this.resolvedApprovalDedupeKeys.entries())i<t&&this.resolvedApprovalDedupeKeys.delete(e)}buildApprovalDedupeKey(t){let e=t.toolName||"Tool",i=this.firstString(t.toolInput?.command,t.toolInput?.cmd,t.rawInput);if(i)return`command:${this.hashDedupeValue(`${this.mapToolNameForApproval(e)||e}:${i}`)}`;let s=this.firstString(t.filePath,t.toolInput?.file_path,t.toolInput?.path,t.toolInput?.filePath);if(s)return`file:${this.hashDedupeValue(`${e}:${s}`)}`;let r=this.firstString(t.hint);if(r)return`hint:${this.hashDedupeValue(`${e}:${r}`)}`}hashDedupeValue(t){return ve.createHash("sha256").update(t).digest("hex").slice(0,16)}buildPermissionRequestHint(t,e,i){let s=this.extractFilePathFromToolInput(t,e,i);if(this.mapToolNameForApproval(t)==="Edit"&&s)return`File: ${s}`;let r=this.firstString(e?.command,e?.cmd);return r?`Command: ${this.truncateApprovalDetail(r,80)}`:s?`File: ${s}`:`Tool: ${this.mapToolNameForApproval(t)||t}`}extractFilePathFromToolInput(t,e,i){let s=this.firstString(e?.file_path,e?.path,e?.filePath);if(s)return s;if(t==="apply_patch"){let n=this.firstString(e?.command,e?.cmd,i)?.match(/\*\*\* (?:Update|Add|Delete) File: ([^\r\n]+)/);if(n)return n[1].trim()}}stringifyToolInput(t){if(t!=null){if(typeof t=="string")return t;try{return JSON.stringify(t)}catch{return String(t)}}}buildApprovalPromptContextFromPendingCall(t){let e=this.firstString(t.parsedInput?.command,t.parsedInput?.cmd),i=Q(t.input),s=ne(e)||Q(e),n=(t.name==="apply_patch"?t.input:"")||i||s,l=!!n,p=l?"apply_patch":t.name,c=l?n:t.input,d=l?t.parsedInput||{command:n}:t.parsedInput,u=l?this.extractFilePathFromToolInput(p,d,c):t.filePath;return{toolName:p,filePath:u,diff:l?c:t.diff,toolInput:d,rawInput:c,hint:u?`File: ${u}`:`Tool: ${this.mapToolNameForApproval(p)||p}`}}buildApprovalPromptContextFromParsedPrompt(t){let e=this.extractShellCommandFromPromptText(t.promptText);if(e)return{toolName:"Bash",toolInput:{command:e},rawInput:e,hint:`Command: ${this.truncateApprovalDetail(e,80)}`};let i=t.promptText.match(/^(?:Destination|File):\s*([^\r\n]+)$/mi)?.[1]?.trim();return i?{toolName:"apply_patch",filePath:i,toolInput:{file_path:i},hint:`File: ${i}`}:null}parsedPromptMatchesApprovalContext(t,e,i){let s=this.buildApprovalPromptContextFromParsedPrompt(t),r=this.mapToolNameForApproval(e.toolName)==="Bash"?this.firstString(e.toolInput?.command,e.toolInput?.cmd):void 0;if(r){if(Ni(t.promptText,r,i))return!0;if(this.mapToolNameForApproval(s?.toolName)!=="Bash")return!1;let l=Ke(t.promptText);return l?xt(l,r):!1}let n=this.firstString(e.filePath,e.toolInput?.file_path,e.toolInput?.path,e.toolInput?.filePath);return n?s?.toolName!=="apply_patch"||!s.filePath?!1:K.normalize(s.filePath)===K.normalize(n):!1}extractShellCommandFromPromptText(t){return Tt(t)}buildPromptInstructions(t){return t.kind==="yes_no"&&t.requiresFollowUpText?"Reply with 1 to approve, or 2 followed by what to change":t.kind==="yes_no"?"Reply with 1 for yes or 2 for no":t.kind==="numbered"?"Reply with the number of the option you want":"Reply with your response"}buildApprovalPromptContent(t,e){let i=t.trim(),s=i==="Codex is waiting for approval.",r=/^\$\s+/.test(i),n=e.toolName?`${e.toolName} requires approval.`:"Codex is waiting for approval.",l=[s||r||!i?n:i],p=typeof e.toolInput?.command=="string"?e.toolInput.command.trim():void 0;if(p&&!l.join(`
29
30
  `).includes(p))return l.push("Command:",this.truncateApprovalDetail(p,240)),l.join(`
30
31
  `);let c=e.filePath||e.toolInput?.file_path;return typeof c=="string"&&c.length>0&&!l.join(`
31
32
  `).includes(c)?(l.push(`File: ${c}`),l.join(`
@@ -33,13 +34,13 @@ ${Re(p,500)}`,metadata:{toolName:l,toolOutput:r,callId:s,status:"completed"}}}re
33
34
  `).includes(e.hint)&&l.push(e.hint),l.join(`
34
35
  `))}truncateApprovalDetail(t,e){return t.length>e?`${t.slice(0,e-3)}...`:t}summarizePromptSnapshot(t){return t.split(`
35
36
  `).map(e=>e.trimEnd()).filter(e=>e.length>0).slice(-12).map(e=>e.slice(0,160)).join(`
36
- `)}translatePromptResponse(t,e=this.pendingInteractivePrompt){if(!e)return{primaryInput:t};let s=t.trim().match(/^(\d+)(?:[,.:;\-\s]+([\s\S]+))?$/);if(!s)return{primaryInput:t};let r=s[1],n=s[2]?.trim(),l=e.submitMap[r];return l?e.requiresFollowUpText&&n?{primaryInput:l,followUpInput:n}:{primaryInput:l}:{primaryInput:t}}getEventPromptId(t){let e=t.promptId;return typeof e=="string"&&e.trim().length>0?e.trim():null}isNumericApprovalResponse(t){return/^\d+(?:[\s,.:;-].*)?$/.test(t.trim())}safeRejectInputForPrompt(t){let e=t.options.find(i=>{let s=t.submitMap[i.number];return s===B?!0:s==="n"&&/^(?:no|reject|deny|cancel)\b/i.test(i.text.trim())});return e?t.submitMap[e.number]??null:null}async emitRejectedPromptResponseNotification(t,e,i){if(!this.isExpectedSessionStateCurrent(i))return;let s=i.sessionId,r=t==="no_safe_reject_option"?"Your message was not sent because the active desktop prompt has no verified reject option. Please resolve it on the desktop and send your message again.":t==="live_prompt_unverified"?"Your response was not sent because the desktop prompt changed or could not be verified. Please reply to the prompt currently shown on your device.":"Response ignored because it was not tied to the current prompt. Please reply to the latest prompt again.",n={prompt_response_rejected:!0,reason:t,eventId:e.eventId,eventPromptId:this.getEventPromptId(e),activePromptId:this.pendingInteractivePrompt?.promptId},l=this.encryptForEmit(r,n,h.EventType.NOTIFICATION,s);if(l)try{await this.appSyncClient.createEvent({sessionId:s,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,content:l.content,metadata:l.metadata,timestamp:(0,h.prepareEventTimestamp)({orderingKey:s}),...l.isEncrypted?{isEncrypted:!0}:{}})}catch(p){o.warn("Failed to emit rejected prompt response notification",{reason:t,error:p})}}buildToolDetailsForInteractivePrompt(t,e){let i=t.toolName,s=t.toolInput&&typeof t.toolInput=="object"?t.toolInput:void 0;if(i==="apply_patch"){let n=t.diff||t.rawInput;if(n){let{oldString:l,newString:p,oldStartLine:c,newStartLine:d}=this.extractOldNewFromPatch(n),u=e?this.extractDiffLineAnchorsFromSnapshot(e):{};return{tool_name:"Edit",tool_input:{file_path:t.filePath,content:n,diff:t.diff,raw_patch:t.rawInput,old_string:l,new_string:p,old_start_line:c??u.oldStartLine,new_start_line:d??u.newStartLine}}}}if(this.mapToolNameForApproval(i)==="Bash"){let n=this.firstString(s?.command,s?.cmd,t.rawInput,t.hint);if(n)return{tool_name:"Bash",tool_input:{command:n,output:s?.output}}}let r={};return t.filePath&&(r.file_path=t.filePath),t.diff&&(r.diff=t.diff),t.rawInput&&(r.raw_input=t.rawInput),Object.keys(r).length>0?{tool_name:i||"Tool",tool_input:r}:{}}firstString(...t){for(let e of t)if(typeof e=="string"&&e.trim().length>0)return e}buildFallbackToolInput(t){let e={};return t.filePath&&(e.file_path=t.filePath),t.diff&&(e.diff=t.diff),t.rawInput&&(e.raw_input=t.rawInput),t.toolInput&&typeof t.toolInput=="object"&&(e.parsed_input=t.toolInput),Object.keys(e).length>0?e:void 0}mapToolNameForApproval(t){return t?{exec_command:"Bash",exec:"Bash",local_shell:"Bash",Bash:"Bash",apply_patch:"Edit",shell_command:"Bash",shell:"Bash",Edit:"Edit",Write:"Write"}[t]||t:void 0}extractOldNewFromPatch(t){let e=[],i=[],s=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/,r=0,n=0,l=0,p,c;for(let d of t.split(`
37
+ `)}translatePromptResponse(t,e=this.pendingInteractivePrompt){if(!e)return{primaryInput:t};let s=t.trim().match(/^(\d+)(?:[,.:;\-\s]+([\s\S]+))?$/);if(!s)return{primaryInput:t};let r=s[1],n=s[2]?.trim(),l=e.submitMap[r];return l?e.requiresFollowUpText&&n?{primaryInput:l,followUpInput:n}:{primaryInput:l}:{primaryInput:t}}getEventPromptId(t){let e=t.promptId;return typeof e=="string"&&e.trim().length>0?e.trim():null}isNumericApprovalResponse(t){return/^\d+(?:[\s,.:;-].*)?$/.test(t.trim())}safeRejectInputForPrompt(t){let e=t.options.find(i=>{let s=t.submitMap[i.number];return s===q?!0:s==="n"&&/^(?:no|reject|deny|cancel)\b/i.test(i.text.trim())});return e?t.submitMap[e.number]??null:null}async emitRejectedPromptResponseNotification(t,e,i){if(!this.isExpectedSessionStateCurrent(i))return;let s=i.sessionId,r=t==="no_safe_reject_option"?"Your message was not sent because the active desktop prompt has no verified reject option. Please resolve it on the desktop and send your message again.":t==="live_prompt_unverified"?"Your response was not sent because the desktop prompt changed or could not be verified. Please reply to the prompt currently shown on your device.":"Response ignored because it was not tied to the current prompt. Please reply to the latest prompt again.",n={prompt_response_rejected:!0,reason:t,eventId:e.eventId,eventPromptId:this.getEventPromptId(e),activePromptId:this.pendingInteractivePrompt?.promptId},l=this.encryptForEmit(r,n,h.EventType.NOTIFICATION,s);if(l)try{await this.appSyncClient.createEvent({sessionId:s,type:h.EventType.NOTIFICATION,source:h.EventSource.DESKTOP,content:l.content,metadata:l.metadata,timestamp:(0,h.prepareEventTimestamp)({orderingKey:s}),...l.isEncrypted?{isEncrypted:!0}:{}})}catch(p){o.warn("Failed to emit rejected prompt response notification",{reason:t,error:p})}}buildToolDetailsForInteractivePrompt(t,e){let i=t.toolName,s=t.toolInput&&typeof t.toolInput=="object"?t.toolInput:void 0;if(i==="apply_patch"){let n=t.diff||t.rawInput;if(n){let{oldString:l,newString:p,oldStartLine:c,newStartLine:d}=this.extractOldNewFromPatch(n),u=e?this.extractDiffLineAnchorsFromSnapshot(e):{};return{tool_name:"Edit",tool_input:{file_path:t.filePath,content:n,diff:t.diff,raw_patch:t.rawInput,old_string:l,new_string:p,old_start_line:c??u.oldStartLine,new_start_line:d??u.newStartLine}}}}if(this.mapToolNameForApproval(i)==="Bash"){let n=this.firstString(s?.command,s?.cmd,t.rawInput,t.hint);if(n)return{tool_name:"Bash",tool_input:{command:n,output:s?.output}}}let r={};return t.filePath&&(r.file_path=t.filePath),t.diff&&(r.diff=t.diff),t.rawInput&&(r.raw_input=t.rawInput),Object.keys(r).length>0?{tool_name:i||"Tool",tool_input:r}:{}}firstString(...t){for(let e of t)if(typeof e=="string"&&e.trim().length>0)return e}buildFallbackToolInput(t){let e={};return t.filePath&&(e.file_path=t.filePath),t.diff&&(e.diff=t.diff),t.rawInput&&(e.raw_input=t.rawInput),t.toolInput&&typeof t.toolInput=="object"&&(e.parsed_input=t.toolInput),Object.keys(e).length>0?e:void 0}mapToolNameForApproval(t){return t?{exec_command:"Bash",exec:"Bash",local_shell:"Bash",Bash:"Bash",apply_patch:"Edit",shell_command:"Bash",shell:"Bash",Edit:"Edit",Write:"Write"}[t]||t:void 0}extractOldNewFromPatch(t){let e=[],i=[],s=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/,r=0,n=0,l=0,p,c;for(let d of t.split(`
37
38
  `)){let u=d.match(s);if(u){r+=1,n=Number.parseInt(u[1],10),l=Number.parseInt(u[2],10);continue}if(!(d.startsWith("***")||d.startsWith("---")||d.startsWith("+++")||d.startsWith("*** End Patch"))){if(d.startsWith("-"))p===void 0&&(p=n),e.push(d.slice(1)),n+=1;else if(d.startsWith("+"))c===void 0&&(c=l),i.push(d.slice(1)),l+=1;else if(d.startsWith(" ")){let m=d.slice(1);e.push(m),i.push(m),n+=1,l+=1}}}return{oldString:e.join(`
38
39
  `),newString:i.join(`
39
- `),oldStartLine:r===1?p:void 0,newStartLine:r===1?c:void 0}}extractDiffLineAnchorsFromSnapshot(t){let e=(0,K.normalizeSnapshot)(t),i,s;for(let r of e.split(`
40
- `)){let n=r.match(/^\s*(\d+)\s+(.*)$/);if(!n)continue;let l=Number.parseInt(n[1],10),p=n[2];if(Number.isFinite(l)){if(p.startsWith("-")){i??=l;continue}if(p.startsWith("+")){s??=l;continue}i??=l,s??=l}}return o.debug("Recovered diff line anchors from tmux snapshot",{oldStartLine:i,newStartLine:s,snapshotPreview:this.summarizePromptSnapshot(t)}),{oldStartLine:i,newStartLine:s}}subscribeToMobileEvents(t){let e=this.sessionState;if(!e||e.sessionId!==t)return o.warn("Refusing mobile subscription without matching session generation",{sessionId:t,currentSessionId:e?.sessionId}),!1;if(this.subscribedSessionId===t&&this.subscribedSessionState===e)return o.info("Already subscribed to mobile events, skipping",{sessionId:t}),!0;if(o.info("Subscribing to mobile events",{sessionId:t}),this.unsubscribe){try{this.unsubscribe()}catch(i){o.warn("Error cleaning up previous subscription (non-fatal)",{error:i})}this.subscribedSessionId=null,this.subscribedSessionState=null}try{this.unsubscribe=this.appSyncClient.subscribeToEvents(t,async i=>{await this.handleMobileEvent(i,e)},i=>{o.error("Subscription error:",i)},{onSessionRetired:()=>this.recoverRetiredBackendSession(t)}),this.subscribedSessionId=t,this.subscribedSessionState=e}catch(i){return o.error("Failed to subscribe to mobile events (non-fatal)",{sessionId:t,error:i}),!1}return this.isExpectedSessionStateCurrent(e)&&(e.subscriptionActive=!0),o.info("Subscribed to mobile events"),!0}async ensureOrchestrationClient(t){let{OrchestrationClient:e}=await import("@quantiya/quorum-core"),i=await h.keychainManager.getTokens((0,h.getEnvironment)());if(!i?.idToken)throw new Error("No Cognito ID token available \u2014 plugin must be logged in");let s=(0,h.getConfig)(),r=this.sessionKey??Buffer.alloc(32).toString("base64"),n=require("ws");return await e.connect({appsyncUrl:s.aws.appsyncUrl,cognitoIdToken:i.idToken,tokenProvider:async()=>(await h.keychainManager.getTokens((0,h.getEnvironment)()))?.idToken??i.idToken,sessionId:t,sessionKeyB64:r,webSocketFactory:((p,c)=>new n(p,c))})}async subscribeToOnApplyUserDecision(t){if(this.userDecisionUnsubscribe){try{this.userDecisionUnsubscribe()}catch(e){o.warn("Error invoking previous onApplyUserDecision unsubscribe (non-fatal)",{error:e})}this.userDecisionUnsubscribe=null}if(this.orchestrationClient){try{await this.orchestrationClient.destroy()}catch(e){o.warn("Error destroying previous OrchestrationClient (non-fatal)",{error:e})}this.orchestrationClient=null}try{let e=await this.ensureOrchestrationClient(t);this.orchestrationClient=e;let i=await e.onApplyUserDecision(s=>{this.handleApplyUserDecisionEvent(s)});return this.userDecisionUnsubscribe=i,o.info("[v1-bridge] Subscribed to onApplyUserDecision via OrchestrationClient",{sessionId:t}),!0}catch(e){if(o.error("Failed to subscribe to onApplyUserDecision (non-fatal)",{sessionId:t,error:e}),this.orchestrationClient){try{await this.orchestrationClient.destroy()}catch{}this.orchestrationClient=null}return!1}}handleApplyUserDecisionEvent(t){let{gateId:e,decision:i,sessionId:s,taskId:r}=t;if(this.v1Bridge.dispatch(t)==="self_echo"){o.debug("[v1-bridge] Self-origin decision echo absorbed",{gateId:e,decision:i,sessionId:s,taskId:r});return}o.info("[v1-bridge] cross-device decision recorded",{gateId:e,decision:i,sessionId:s,taskId:r});let l=String(i).toUpperCase();this.emitOrchestrationBanner(l)}async emitOrchestrationBanner(t){let e=process.env[j];if(!e){o.warn("[v1-bridge] No tmux session set; cannot emit cross-device banner",{expectedEnv:j});return}let s=ut(t).replace(/"/g,'\\"'),r=`tmux display-message -d 8000 -t "${e}" "${s}"`;try{await bt(r),o.info("[v1-bridge] Cross-device decision banner emitted",{decision:t})}catch(n){o.warn("[v1-bridge] tmux display-message failed (non-fatal)",{error:n instanceof Error?n.message:String(n)})}}async originateUserDecision(t){this.v1Bridge.markOriginated(t.gateId);let e=this.sessionState?.sessionId;if(!e)throw this.v1Bridge.clearOriginated(t.gateId),new Error("No active session \u2014 cannot originate user decision");let i=this.orchestrationClient,s=!1;i||(i=await this.ensureOrchestrationClient(e),s=!0);try{await i.applyUserDecision(t),o.info("[v1-bridge] originated user decision",{gateId:t.gateId,decision:t.decision,sessionId:e})}catch(r){throw this.v1Bridge.clearOriginated(t.gateId),o.error("[v1-bridge] applyUserDecision failed (rolled back originator marker)",{gateId:t.gateId,decision:t.decision,error:r instanceof Error?r.message:String(r)}),r}finally{if(s&&i)try{await i.destroy()}catch{}}}_v1BridgeStateForTest(){return this.v1Bridge}_v1BridgeHandleEventForTest(t){this.handleApplyUserDecisionEvent(t)}async downloadAttachment(t,e,i,s){try{let r=t.isEncrypted??s??!1;o.info("Downloading attachment",{id:t.id,type:t.type,filename:t.filename,s3Key:t.s3Key,attachmentIsEncrypted:t.isEncrypted,eventIsEncrypted:s,shouldDecrypt:r});let{downloadUrl:n}=await this.appSyncClient.getAttachmentDownloadUrl(t.s3Key),l=await fetch(n);if(!l.ok)throw new Error(`Failed to download attachment: ${l.status} ${l.statusText}`);let p=Buffer.from(await l.arrayBuffer());if(r&&i)try{o.info("Decrypting attachment",{id:t.id}),p=h.cryptoService.decryptData(p,i),o.info("Attachment decrypted successfully",{id:t.id,decryptedSize:p.length})}catch(y){throw o.error("Failed to decrypt attachment:",{id:t.id,error:y}),new Error("Failed to decrypt attachment")}else if(r&&!i)return o.warn("Cannot decrypt attachment - no session key available",{id:t.id}),null;let c=$.join(It.tmpdir(),"codevibe-codex",e);V.existsSync(c)||V.mkdirSync(c,{recursive:!0});let d="";if(t.filename){let y=$.extname(t.filename);y&&(d=y)}d||(d={"image/jpeg":".jpg","image/png":".png","image/gif":".gif","image/webp":".webp","image/heic":".heic","application/pdf":".pdf"}[t.type]||".bin");let u=`attachment-${t.id}${d}`,m=$.join(c,u);return V.writeFileSync(m,p),o.info("Attachment saved to temp file",{id:t.id,filePath:m,size:p.length}),m}catch(r){return o.error("Failed to download attachment:",{id:t.id,error:r}),null}}async handleMobileEvent(t,e=this.sessionState){if(t.attachments&&t.attachments.length>0&&o.info("DEBUG: Raw attachment data from subscription",{attachments:JSON.stringify(t.attachments),eventIsEncrypted:t.isEncrypted}),o.info("Received mobile event",{eventId:t.eventId,type:t.type,content:t.content?.substring(0,50),attachmentCount:t.attachments?.length||0,isEncrypted:t.isEncrypted}),!e||!this.isExpectedSessionStateCurrent(e)||t.sessionId!==e.sessionId){o.warn("Dropping mobile event for stale or mismatched session generation",{eventSessionId:t.sessionId,expectedSessionId:e?.sessionId,currentSessionId:this.sessionState?.sessionId});return}let i=e.sessionId,{sessionKey:s}=e.encryptionSnapshot,r=t.content||"";if(t.isEncrypted){if(!s){o.error("Dropping encrypted mobile event without its session-pinned key",{eventId:t.eventId,sessionId:i});return}try{r=h.cryptoService.decryptContent(t.content,s),o.debug("Event decrypted successfully",{eventId:t.eventId})}catch(l){o.error("Failed to decrypt event:",{eventId:t.eventId,error:l});return}}let n=this.pendingInteractivePrompt;try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:h.DeliveryStatus.DELIVERED})}catch(l){o.error("Failed to update delivery status:",l)}if(this.isExpectedSessionStateCurrent(e)&&(t.type===h.EventType.USER_PROMPT||t.type===h.EventType.PROMPT_RESPONSE)){let l=r,p=t.attachments||[],c=t.type===h.EventType.PROMPT_RESPONSE,d=null,u=null;if(t.type===h.EventType.PROMPT_RESPONSE){let S=this.getEventPromptId(t),P=n,b=P?.promptId;if(!S||!b||S!==b){o.warn("Rejecting stale or unbound PROMPT_RESPONSE",{eventId:t.eventId,eventPromptId:S,activePromptId:b}),await this.emitRejectedPromptResponseNotification("prompt_id_mismatch",t,e);return}if(d=P,!this.isNumericApprovalResponse(l)&&(u=this.safeRejectInputForPrompt(P),!u)){await this.emitRejectedPromptResponseNotification("no_safe_reject_option",t,e);return}}else if(n&&this.isNumericApprovalResponse(l))c=!0,d=n,o.info("Treating approval-shaped USER_PROMPT as active prompt response",{eventId:t.eventId,activePromptId:n.promptId,promptPreview:l.slice(0,20)});else if(n){if(u=this.safeRejectInputForPrompt(n),!u){await this.emitRejectedPromptResponseNotification("no_safe_reject_option",t,e);return}c=!0,d=n,o.info("Treating free-form USER_PROMPT as reject-then-follow-up",{eventId:t.eventId,activePromptId:n.promptId,attachmentCount:p.length})}let m=[];if(p.length>0){o.info("Downloading attachments for prompt",{count:p.length});for(let S of p){let P=await this.downloadAttachment(S,i,s,t.isEncrypted);if(!this.isExpectedSessionStateCurrent(e))return;P&&m.push(P)}if(m.length>0){let S=m.map(P=>`[Attached file: ${P}]`).join(`
40
+ `),oldStartLine:r===1?p:void 0,newStartLine:r===1?c:void 0}}extractDiffLineAnchorsFromSnapshot(t){let e=(0,B.normalizeSnapshot)(t),i,s;for(let r of e.split(`
41
+ `)){let n=r.match(/^\s*(\d+)\s+(.*)$/);if(!n)continue;let l=Number.parseInt(n[1],10),p=n[2];if(Number.isFinite(l)){if(p.startsWith("-")){i??=l;continue}if(p.startsWith("+")){s??=l;continue}i??=l,s??=l}}return o.debug("Recovered diff line anchors from tmux snapshot",{oldStartLine:i,newStartLine:s,snapshotPreview:this.summarizePromptSnapshot(t)}),{oldStartLine:i,newStartLine:s}}subscribeToMobileEvents(t){let e=this.sessionState;if(!e||e.sessionId!==t)return o.warn("Refusing mobile subscription without matching session generation",{sessionId:t,currentSessionId:e?.sessionId}),!1;if(this.subscribedSessionId===t&&this.subscribedSessionState===e)return o.info("Already subscribed to mobile events, skipping",{sessionId:t}),!0;if(o.info("Subscribing to mobile events",{sessionId:t}),this.unsubscribe){try{this.unsubscribe()}catch(i){o.warn("Error cleaning up previous subscription (non-fatal)",{error:i})}this.subscribedSessionId=null,this.subscribedSessionState=null}try{this.unsubscribe=this.appSyncClient.subscribeToEvents(t,async i=>{await this.handleMobileEvent(i,e)},i=>{o.error("Subscription error:",i)},{onSessionRetired:()=>this.recoverRetiredBackendSession(t)}),this.subscribedSessionId=t,this.subscribedSessionState=e}catch(i){return o.error("Failed to subscribe to mobile events (non-fatal)",{sessionId:t,error:i}),!1}return this.isExpectedSessionStateCurrent(e)&&(e.subscriptionActive=!0),o.info("Subscribed to mobile events"),!0}async ensureOrchestrationClient(t){let{OrchestrationClient:e}=await import("@quantiya/quorum-core"),i=await h.keychainManager.getTokens((0,h.getEnvironment)());if(!i?.idToken)throw new Error("No Cognito ID token available \u2014 plugin must be logged in");let s=(0,h.getConfig)(),r=this.sessionKey??Buffer.alloc(32).toString("base64"),n=require("ws");return await e.connect({appsyncUrl:s.aws.appsyncUrl,cognitoIdToken:i.idToken,tokenProvider:async()=>(await h.keychainManager.getTokens((0,h.getEnvironment)()))?.idToken??i.idToken,sessionId:t,sessionKeyB64:r,webSocketFactory:((p,c)=>new n(p,c))})}async subscribeToOnApplyUserDecision(t){if(this.userDecisionUnsubscribe){try{this.userDecisionUnsubscribe()}catch(e){o.warn("Error invoking previous onApplyUserDecision unsubscribe (non-fatal)",{error:e})}this.userDecisionUnsubscribe=null}if(this.orchestrationClient){try{await this.orchestrationClient.destroy()}catch(e){o.warn("Error destroying previous OrchestrationClient (non-fatal)",{error:e})}this.orchestrationClient=null}try{let e=await this.ensureOrchestrationClient(t);this.orchestrationClient=e;let i=await e.onApplyUserDecision(s=>{this.handleApplyUserDecisionEvent(s)});return this.userDecisionUnsubscribe=i,o.info("[v1-bridge] Subscribed to onApplyUserDecision via OrchestrationClient",{sessionId:t}),!0}catch(e){if(o.error("Failed to subscribe to onApplyUserDecision (non-fatal)",{sessionId:t,error:e}),this.orchestrationClient){try{await this.orchestrationClient.destroy()}catch{}this.orchestrationClient=null}return!1}}handleApplyUserDecisionEvent(t){let{gateId:e,decision:i,sessionId:s,taskId:r}=t;if(this.v1Bridge.dispatch(t)==="self_echo"){o.debug("[v1-bridge] Self-origin decision echo absorbed",{gateId:e,decision:i,sessionId:s,taskId:r});return}o.info("[v1-bridge] cross-device decision recorded",{gateId:e,decision:i,sessionId:s,taskId:r});let l=String(i).toUpperCase();this.emitOrchestrationBanner(l)}async emitOrchestrationBanner(t){let e=process.env[H];if(!e){o.warn("[v1-bridge] No tmux session set; cannot emit cross-device banner",{expectedEnv:H});return}let s=mt(t).replace(/"/g,'\\"'),r=`tmux display-message -d 8000 -t "${e}" "${s}"`;try{await Et(r),o.info("[v1-bridge] Cross-device decision banner emitted",{decision:t})}catch(n){o.warn("[v1-bridge] tmux display-message failed (non-fatal)",{error:n instanceof Error?n.message:String(n)})}}async originateUserDecision(t){this.v1Bridge.markOriginated(t.gateId);let e=this.sessionState?.sessionId;if(!e)throw this.v1Bridge.clearOriginated(t.gateId),new Error("No active session \u2014 cannot originate user decision");let i=this.orchestrationClient,s=!1;i||(i=await this.ensureOrchestrationClient(e),s=!0);try{await i.applyUserDecision(t),o.info("[v1-bridge] originated user decision",{gateId:t.gateId,decision:t.decision,sessionId:e})}catch(r){throw this.v1Bridge.clearOriginated(t.gateId),o.error("[v1-bridge] applyUserDecision failed (rolled back originator marker)",{gateId:t.gateId,decision:t.decision,error:r instanceof Error?r.message:String(r)}),r}finally{if(s&&i)try{await i.destroy()}catch{}}}_v1BridgeStateForTest(){return this.v1Bridge}_v1BridgeHandleEventForTest(t){this.handleApplyUserDecisionEvent(t)}async downloadAttachment(t,e,i,s){try{let r=t.isEncrypted??s??!1;o.info("Downloading attachment",{id:t.id,type:t.type,filename:t.filename,s3Key:t.s3Key,attachmentIsEncrypted:t.isEncrypted,eventIsEncrypted:s,shouldDecrypt:r});let{downloadUrl:n}=await this.appSyncClient.getAttachmentDownloadUrl(t.s3Key),l=await fetch(n);if(!l.ok)throw new Error(`Failed to download attachment: ${l.status} ${l.statusText}`);let p=Buffer.from(await l.arrayBuffer());if(r&&i)try{o.info("Decrypting attachment",{id:t.id}),p=h.cryptoService.decryptData(p,i),o.info("Attachment decrypted successfully",{id:t.id,decryptedSize:p.length})}catch(y){throw o.error("Failed to decrypt attachment:",{id:t.id,error:y}),new Error("Failed to decrypt attachment")}else if(r&&!i)return o.warn("Cannot decrypt attachment - no session key available",{id:t.id}),null;let c=K.join(De.tmpdir(),"codevibe-codex",e);z.existsSync(c)||z.mkdirSync(c,{recursive:!0});let d="";if(t.filename){let y=K.extname(t.filename);y&&(d=y)}d||(d={"image/jpeg":".jpg","image/png":".png","image/gif":".gif","image/webp":".webp","image/heic":".heic","application/pdf":".pdf"}[t.type]||".bin");let u=`attachment-${t.id}${d}`,m=K.join(c,u);return z.writeFileSync(m,p),o.info("Attachment saved to temp file",{id:t.id,filePath:m,size:p.length}),m}catch(r){return o.error("Failed to download attachment:",{id:t.id,error:r}),null}}async handleMobileEvent(t,e=this.sessionState){if(t.attachments&&t.attachments.length>0&&o.info("DEBUG: Raw attachment data from subscription",{attachments:JSON.stringify(t.attachments),eventIsEncrypted:t.isEncrypted}),o.info("Received mobile event",{eventId:t.eventId,type:t.type,content:t.content?.substring(0,50),attachmentCount:t.attachments?.length||0,isEncrypted:t.isEncrypted}),!e||!this.isExpectedSessionStateCurrent(e)||t.sessionId!==e.sessionId){o.warn("Dropping mobile event for stale or mismatched session generation",{eventSessionId:t.sessionId,expectedSessionId:e?.sessionId,currentSessionId:this.sessionState?.sessionId});return}let i=e.sessionId,{sessionKey:s}=e.encryptionSnapshot,r=t.content||"";if(t.isEncrypted){if(!s){o.error("Dropping encrypted mobile event without its session-pinned key",{eventId:t.eventId,sessionId:i});return}try{r=h.cryptoService.decryptContent(t.content,s),o.debug("Event decrypted successfully",{eventId:t.eventId})}catch(l){o.error("Failed to decrypt event:",{eventId:t.eventId,error:l});return}}let n=this.pendingInteractivePrompt;try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:h.DeliveryStatus.DELIVERED})}catch(l){o.error("Failed to update delivery status:",l)}if(this.isExpectedSessionStateCurrent(e)&&(t.type===h.EventType.USER_PROMPT||t.type===h.EventType.PROMPT_RESPONSE)){let l=r,p=t.attachments||[],c=t.type===h.EventType.PROMPT_RESPONSE,d=null,u=null;if(t.type===h.EventType.PROMPT_RESPONSE){let S=this.getEventPromptId(t),P=n,b=P?.promptId;if(!S||!b||S!==b){o.warn("Rejecting stale or unbound PROMPT_RESPONSE",{eventId:t.eventId,eventPromptId:S,activePromptId:b}),await this.emitRejectedPromptResponseNotification("prompt_id_mismatch",t,e);return}if(d=P,!this.isNumericApprovalResponse(l)&&(u=this.safeRejectInputForPrompt(P),!u)){await this.emitRejectedPromptResponseNotification("no_safe_reject_option",t,e);return}}else if(n&&this.isNumericApprovalResponse(l))c=!0,d=n,o.info("Treating approval-shaped USER_PROMPT as active prompt response",{eventId:t.eventId,activePromptId:n.promptId,promptPreview:l.slice(0,20)});else if(n){if(u=this.safeRejectInputForPrompt(n),!u){await this.emitRejectedPromptResponseNotification("no_safe_reject_option",t,e);return}c=!0,d=n,o.info("Treating free-form USER_PROMPT as reject-then-follow-up",{eventId:t.eventId,activePromptId:n.promptId,attachmentCount:p.length})}let m=[];if(p.length>0){o.info("Downloading attachments for prompt",{count:p.length});for(let S of p){let P=await this.downloadAttachment(S,i,s,t.isEncrypted);if(!this.isExpectedSessionStateCurrent(e))return;P&&m.push(P)}if(m.length>0){let S=m.map(P=>`[Attached file: ${P}]`).join(`
41
42
  `);l?l=`${S}
42
43
 
43
44
  ${l}`:l=`${S}
44
45
 
45
- Please analyze the attached file(s).`,o.info("Prompt updated with attachment paths",{attachmentCount:m.length,newPromptLength:l.length})}}if(c&&this.pendingInteractivePrompt!==d){o.info("Ignoring mobile prompt response already settled by a concurrent completion",{eventId:t.eventId,boundPromptId:d?.promptId,activePromptId:this.pendingInteractivePrompt?.promptId});return}let y=u?{primaryInput:u,followUpInput:l.trim()||void 0}:this.translatePromptResponse(l,d);if(!this.isExpectedSessionStateCurrent(e)||(y.primaryInput!==B&&this.trackMobilePrompt(i,y.primaryInput),!this.isExpectedSessionStateCurrent(e)))return;let f=d?await this.sendVerifiedPromptInput(e,d,y.primaryInput):await this.sendSessionPinnedInput(e,y.primaryInput)?"sent":"send_failed",v=f==="sent";if(!this.isExpectedSessionStateCurrent(e))return;if((f==="stale"||f==="unverified")&&await this.emitRejectedPromptResponseNotification("live_prompt_unverified",t,e),!v&&y.primaryInput!==B&&this.forgetMobilePrompt(i,y.primaryInput),v&&y.followUpInput){if(y.primaryInput===B&&(await new Promise(b=>setTimeout(b,200)),!this.isExpectedSessionStateCurrent(e))||(this.trackMobilePrompt(i,y.followUpInput),!this.isExpectedSessionStateCurrent(e)))return;let S=y.primaryInput===B&&d?await this.sendVerifiedFollowUpAfterDismissal(e,d,y.followUpInput):await this.sendSessionPinnedInput(e,y.followUpInput)?"sent":"send_failed",P=S==="sent";if(!this.isExpectedSessionStateCurrent(e))return;(S==="stale"||S==="unverified")&&await this.emitRejectedPromptResponseNotification("live_prompt_unverified",t,e),P||this.forgetMobilePrompt(i,y.followUpInput)}if(v&&c&&d&&this.pendingInteractivePrompt===d){let S=d;this.rememberResolvedApprovalDedupeKey(e.sessionId,S.dedupeKey),this.pendingInteractivePrompt=null;let P=this.beginE1PromptResolutions([S.promptId],e.sessionId);if(this.launchE1Resolutions(P),!this.isExpectedSessionStateCurrent(e)||(await this.emitNextQueuedInteractivePrompt(),!this.isExpectedSessionStateCurrent(e)))return}if(v){if(!this.isExpectedSessionStateCurrent(e))return;try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:h.DeliveryStatus.EXECUTED})}catch(S){o.error("Failed to update executed status:",S)}}}}buildToolActivityIntegration(){return new G({logger:o,createEvent:t=>this.appSyncClient.createEvent(t),encryptContent:(t,e)=>h.cryptoService.encryptContent(t,e),encryptMetadata:(t,e)=>h.cryptoService.encryptMetadata(t,e),getSessionKey:()=>this.sessionKey,getSessionKeyGen:()=>this.sessionKeyGen,refreshSessionKey:t=>this.refreshToolActivitySessionKey(t)})}async refreshToolActivitySessionKey(t){try{let e=await this.appSyncClient.getSession(t);if(e&&e.status===h.SessionStatus.INACTIVE){let n=new Error(`SESSION_TERMINAL: session ${t} is ${e.status}; stop retrying`);throw n.errorType="SessionTerminal",n}if(!e||e.status!==h.SessionStatus.ACTIVE||!e.isEncrypted)return null;let i=e.sessionKeyGen??null,s=e.encryptedKeys??[];if(s.length===0)return null;let r=await h.keychainManager.getSessionKey(t,s,i);return r?(o.info("[tool-activity] refreshed session key for SessionKeyStale retry (live key untouched)",{sessionId:t,sessionKeyGen:i}),{sessionKey:r,sessionKeyGen:i}):null}catch(e){if((0,h.isTerminalSessionError)(e))throw e;return o.warn("[tool-activity] session key refresh failed after SessionKeyStale (staying retryable)",{sessionId:t,error:String(e)}),null}}getToolActivityLegacyRecorder(t){let e=this.toolActivityLegacyRecorders.get(t);return e||(e=new h.ToolActivityLedger({agent:"codex",sessionId:t,logger:o}),this.toolActivityLegacyRecorders.set(t,e)),e}async ensureToolActivityStarted(t){if(!this.sessionState||!t)return;let e=this.sessionWatcher.getActiveLogFile()||this.sessionState.codexLogFile||void 0,i=this.toolActivity;if(i){e&&i.bindTranscript(e);return}if(this.toolActivityStartPromise){await this.toolActivityStartPromise,e&&this.toolActivity?.bindTranscript(e);return}if(!this.sessionKey)return;let s=this.sessionState.sessionId;this.toolActivityStartPromise=(async()=>{let r=this.buildToolActivityIntegration();try{await r.start(s,t,e),this.toolActivity=r}catch(n){throw await r.stop().catch(()=>{}),this.toolActivityStartPromise=null,n}})().catch(r=>{o.warn("[tool-activity] failed to start codex consolidator (non-fatal)",{backendSessionId:s,rolloutId:t,error:String(r)})}),await this.toolActivityStartPromise}startMobileEndWatcher(t){!this.sessionState||this.sessionState.sessionId!==t||(this.sessionState.mobileEndWatcher=this.appSyncClient.watchForMobileEnd(t,async()=>{o.info("Mobile ended session \u2014 sending desktop quit",{sessionId:t}),this.appSyncClient.stopHeartbeat(t),this.appSyncClient.cleanupSubscription(t);try{await this.appSyncClient.updateSession({sessionId:t,status:h.SessionStatus.INACTIVE}),o.info("Marked session INACTIVE on mobile-end (before tmux terminate)",{sessionId:t})}catch(i){o.warn("Failed to mark session INACTIVE on mobile-end",{sessionId:t,error:i})}let e=process.env[j];if(!e){o.warn("No tmux session set; skipping desktop self-terminate",{sessionId:t,expectedEnv:j});return}await _i(e,bi)}))}async endActiveSession(t,e=!0){if(!this.sessionState)return;let i=this.sessionState;if(this.terminalInputBlockedStates.add(i),await this.terminalInputChain.catch(()=>{}),this.sessionState!==i){o.warn("Session changed while waiting for terminal-input teardown barrier",{endingSessionId:i.sessionId,currentSessionId:this.sessionState?.sessionId,reason:t});return}if(o.info("Ending active session",{sessionId:i.sessionId,codexSessionId:i.codexSessionId,reason:t}),this.sessionState.mobileEndWatcher&&(this.sessionState.mobileEndWatcher.stop(),this.sessionState.mobileEndWatcher=void 0),this.userDecisionUnsubscribe){try{this.userDecisionUnsubscribe()}catch(s){o.warn("Error invoking onApplyUserDecision unsubscribe (non-fatal)",{error:s})}this.userDecisionUnsubscribe=null}if(this.orchestrationClient){try{await this.orchestrationClient.destroy()}catch(s){o.warn("Error destroying OrchestrationClient (non-fatal)",{error:s})}this.orchestrationClient=null}this.v1Bridge.clear(),this.appSyncClient.stopHeartbeat(this.sessionState.sessionId),this.stopE1TtlRefresh(),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null,this.subscribedSessionId=null,this.subscribedSessionState=null),await this.tmuxPaneObserver.stop();for(let s of this.deferredTmuxPromptCandidates.keys())s.sessionId===i.sessionId&&this.deferredTmuxPromptCandidates.delete(s);this.queuedPermissionRequestRegistrations.delete(i.sessionId),t==="shutdown"&&this.sessionWatcher.stop(),this.toolActivityStartPromise&&(await this.toolActivityStartPromise.catch(()=>{}),this.toolActivityStartPromise=null),await this.logEntryChain.catch(()=>{}),this.toolActivity&&(await this.toolActivity.stop(),this.toolActivity=null);for(let s of this.toolActivityLegacyRecorders.values())s.disposeLegacyCoordination();if(this.toolActivityLegacyRecorders.clear(),t==="daemon-handoff"&&this.persistDaemonHandoffState(i),(t==="new-codex-session-started"||t==="shutdown")&&(await this.exactClearOpenE1ForLifecycleEnd(i.sessionId),await this.drainPendingE1ResolutionsForLifecycleEnd(),this.stopE1TtlRefresh()),t!=="backend-generation-retired"&&t!=="daemon-handoff"&&this.permissionRequestOccurrencesByCompletionKey.clear(),this.pendingInteractivePrompt=null,this.queuedInteractivePrompts=[],this.isInitializingSession=!1,this.bufferedLogEntries=[],_e(),this.sessionKey&&(h.keychainManager.clearSessionKey(this.sessionState.sessionId),this.sessionKey=null,this.sessionKeyGen=null),e)try{await this.appSyncClient.updateSession({sessionId:this.sessionState.sessionId,status:h.SessionStatus.INACTIVE})}catch(s){o.error("Failed to update session status:",s)}else o.info("Preserving active backend session for daemon handoff",{sessionId:this.sessionState.sessionId});this.sessionState=null}async stop(t={}){o.info("Stopping CodeVibe Codex companion server");let e=t.retireBackendSession!==!1;this.stopTmuxLifecycleMonitor(),this.isStopping=!0,this.lifecycleGeneration+=1,this.sessionWatcher.stop();let i=this.launchSessionInitPromise,s=this.sessionStartedInitPromise,r=this.retiredSessionRecoveryPromise,n=this.hookEventChain,l=typeof this.sessionWatcher.waitForTranscriptAdmissions=="function"?this.sessionWatcher.waitForTranscriptAdmissions():Promise.resolve();await Promise.all([i?.catch(()=>{}),s?.catch(()=>{}),r?.catch(()=>{}),l.catch(()=>{}),n.catch(()=>{})]);try{await this.endActiveSession(e?"shutdown":"daemon-handoff",e),this.sessionWatcher.stop(),this.approvalDetector.shutdown(),_e()}finally{await this.httpApi.stop(),this.appSyncClient.cleanupSubscriptions()}o.info("CodeVibe Codex companion server stopped")}};if(require.main===module){let a=new ve;process.on("SIGINT",async()=>{o.info("Received SIGINT, shutting down..."),await a.stop(),process.exit(0)}),process.on("SIGTERM",async()=>{o.info("Received SIGTERM, shutting down..."),await a.stop(),process.exit(0)}),process.on("SIGUSR2",async()=>{o.info("Received SIGUSR2, handing off active session to replacement daemon..."),await a.stop({retireBackendSession:!1}),process.exit(0)}),(0,h.installDaemonProcessGuards)(o,{onFatal:()=>{a.stop().finally(()=>process.exit(1))}}),a.start().catch(t=>{o.error("Failed to start server:",t),process.exit(1)})}0&&(module.exports={CodexCompanionServer,extractShellCommandFromPromptText,extractShellCommandPartsFromPromptText,restoreCodexPersistOptionCommand,shellCommandPartsMatchExpected});
46
+ Please analyze the attached file(s).`,o.info("Prompt updated with attachment paths",{attachmentCount:m.length,newPromptLength:l.length})}}if(c&&this.pendingInteractivePrompt!==d){o.info("Ignoring mobile prompt response already settled by a concurrent completion",{eventId:t.eventId,boundPromptId:d?.promptId,activePromptId:this.pendingInteractivePrompt?.promptId});return}let y=u?{primaryInput:u,followUpInput:l.trim()||void 0}:this.translatePromptResponse(l,d);if(!this.isExpectedSessionStateCurrent(e)||(y.primaryInput!==q&&this.trackMobilePrompt(i,y.primaryInput),!this.isExpectedSessionStateCurrent(e)))return;let f=d?await this.sendVerifiedPromptInput(e,d,y.primaryInput):await this.sendSessionPinnedInput(e,y.primaryInput)?"sent":"send_failed",v=f==="sent";if(!this.isExpectedSessionStateCurrent(e))return;if((f==="stale"||f==="unverified")&&await this.emitRejectedPromptResponseNotification("live_prompt_unverified",t,e),!v&&y.primaryInput!==q&&this.forgetMobilePrompt(i,y.primaryInput),v&&y.followUpInput){if(y.primaryInput===q&&(await new Promise(b=>setTimeout(b,200)),!this.isExpectedSessionStateCurrent(e))||(this.trackMobilePrompt(i,y.followUpInput),!this.isExpectedSessionStateCurrent(e)))return;let S=y.primaryInput===q&&d?await this.sendVerifiedFollowUpAfterDismissal(e,d,y.followUpInput):await this.sendSessionPinnedInput(e,y.followUpInput)?"sent":"send_failed",P=S==="sent";if(!this.isExpectedSessionStateCurrent(e))return;(S==="stale"||S==="unverified")&&await this.emitRejectedPromptResponseNotification("live_prompt_unverified",t,e),P||this.forgetMobilePrompt(i,y.followUpInput)}if(v&&c&&d&&this.pendingInteractivePrompt===d){let S=d;this.rememberResolvedApprovalDedupeKey(e.sessionId,S.dedupeKey),this.pendingInteractivePrompt=null;let P=this.beginE1PromptResolutions([S.promptId],e.sessionId);if(this.launchE1Resolutions(P),!this.isExpectedSessionStateCurrent(e)||(await this.emitNextQueuedInteractivePrompt(),!this.isExpectedSessionStateCurrent(e)))return}if(v){if(!this.isExpectedSessionStateCurrent(e))return;try{await this.appSyncClient.updateEventStatus({eventId:t.eventId,sessionId:t.sessionId,timestamp:t.timestamp,deliveryStatus:h.DeliveryStatus.EXECUTED})}catch(S){o.error("Failed to update executed status:",S)}}}}buildToolActivityIntegration(){return new V({logger:o,createEvent:t=>this.appSyncClient.createEvent(t),encryptContent:(t,e)=>h.cryptoService.encryptContent(t,e),encryptMetadata:(t,e)=>h.cryptoService.encryptMetadata(t,e),getSessionKey:()=>this.sessionKey,getSessionKeyGen:()=>this.sessionKeyGen,refreshSessionKey:t=>this.refreshToolActivitySessionKey(t)})}async refreshToolActivitySessionKey(t){try{let e=await this.appSyncClient.getSession(t);if(e&&e.status===h.SessionStatus.INACTIVE){let n=new Error(`SESSION_TERMINAL: session ${t} is ${e.status}; stop retrying`);throw n.errorType="SessionTerminal",n}if(!e||e.status!==h.SessionStatus.ACTIVE||!e.isEncrypted)return null;let i=e.sessionKeyGen??null,s=e.encryptedKeys??[];if(s.length===0)return null;let r=await h.keychainManager.getSessionKey(t,s,i);return r?(o.info("[tool-activity] refreshed session key for SessionKeyStale retry (live key untouched)",{sessionId:t,sessionKeyGen:i}),{sessionKey:r,sessionKeyGen:i}):null}catch(e){if((0,h.isTerminalSessionError)(e))throw e;return o.warn("[tool-activity] session key refresh failed after SessionKeyStale (staying retryable)",{sessionId:t,error:String(e)}),null}}getToolActivityLegacyRecorder(t){let e=this.toolActivityLegacyRecorders.get(t);return e||(e=new h.ToolActivityLedger({agent:"codex",sessionId:t,logger:o}),this.toolActivityLegacyRecorders.set(t,e)),e}async ensureToolActivityStarted(t){if(!this.sessionState||!t)return;let e=this.sessionWatcher.getActiveLogFile()||this.sessionState.codexLogFile||void 0,i=this.toolActivity;if(i){e&&i.bindTranscript(e);return}if(this.toolActivityStartPromise){await this.toolActivityStartPromise,e&&this.toolActivity?.bindTranscript(e);return}if(!this.sessionKey)return;let s=this.sessionState.sessionId;this.toolActivityStartPromise=(async()=>{let r=this.buildToolActivityIntegration();try{await r.start(s,t,e),this.toolActivity=r}catch(n){throw await r.stop().catch(()=>{}),this.toolActivityStartPromise=null,n}})().catch(r=>{o.warn("[tool-activity] failed to start codex consolidator (non-fatal)",{backendSessionId:s,rolloutId:t,error:String(r)})}),await this.toolActivityStartPromise}startMobileEndWatcher(t){!this.sessionState||this.sessionState.sessionId!==t||(this.sessionState.mobileEndWatcher=this.appSyncClient.watchForMobileEnd(t,async()=>{o.info("Mobile ended session \u2014 sending desktop quit",{sessionId:t}),this.appSyncClient.stopHeartbeat(t),this.appSyncClient.cleanupSubscription(t);try{await this.appSyncClient.updateSession({sessionId:t,status:h.SessionStatus.INACTIVE}),o.info("Marked session INACTIVE on mobile-end (before tmux terminate)",{sessionId:t})}catch(i){o.warn("Failed to mark session INACTIVE on mobile-end",{sessionId:t,error:i})}let e=process.env[H];if(!e){o.warn("No tmux session set; skipping desktop self-terminate",{sessionId:t,expectedEnv:H});return}await Fi(e,Ti)}))}async endActiveSession(t,e=!0){if(!this.sessionState)return;let i=t,s=e,r=this.sessionState;if(this.terminalInputBlockedStates.add(r),await this.terminalInputChain.catch(()=>{}),this.sessionState!==r){o.warn("Session changed while waiting for terminal-input teardown barrier",{endingSessionId:r.sessionId,currentSessionId:this.sessionState?.sessionId,reason:i});return}if(o.info("Ending active session",{sessionId:r.sessionId,codexSessionId:r.codexSessionId,reason:i}),this.sessionState.mobileEndWatcher&&(this.sessionState.mobileEndWatcher.stop(),this.sessionState.mobileEndWatcher=void 0),this.userDecisionUnsubscribe){try{this.userDecisionUnsubscribe()}catch(n){o.warn("Error invoking onApplyUserDecision unsubscribe (non-fatal)",{error:n})}this.userDecisionUnsubscribe=null}if(this.orchestrationClient){try{await this.orchestrationClient.destroy()}catch(n){o.warn("Error destroying OrchestrationClient (non-fatal)",{error:n})}this.orchestrationClient=null}this.v1Bridge.clear(),this.appSyncClient.stopHeartbeat(this.sessionState.sessionId),this.stopE1TtlRefresh(),this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null,this.subscribedSessionId=null,this.subscribedSessionState=null),await this.tmuxPaneObserver.stop();for(let n of this.deferredTmuxPromptCandidates.keys())n.sessionId===r.sessionId&&this.deferredTmuxPromptCandidates.delete(n);this.queuedPermissionRequestRegistrations.delete(r.sessionId),this.terminalRetirementRequested&&(i="shutdown",s=!0),i==="shutdown"&&this.sessionWatcher.stop(),this.toolActivityStartPromise&&(await this.toolActivityStartPromise.catch(()=>{}),this.toolActivityStartPromise=null),await this.logEntryChain.catch(()=>{}),this.toolActivity&&(await this.toolActivity.stop(),this.toolActivity=null);for(let n of this.toolActivityLegacyRecorders.values())n.disposeLegacyCoordination();this.toolActivityLegacyRecorders.clear(),this.terminalRetirementRequested&&(i="shutdown",s=!0),i==="daemon-handoff"&&this.persistDaemonHandoffState(r),(i==="new-codex-session-started"||i==="shutdown")&&(await this.exactClearOpenE1ForLifecycleEnd(r.sessionId),await this.drainPendingE1ResolutionsForLifecycleEnd(),this.stopE1TtlRefresh()),i!=="backend-generation-retired"&&i!=="daemon-handoff"&&this.permissionRequestOccurrencesByCompletionKey.clear(),this.pendingInteractivePrompt=null,this.queuedInteractivePrompts=[],this.isInitializingSession=!1,this.bufferedLogEntries=[],Te(),this.sessionKey&&(h.keychainManager.clearSessionKey(this.sessionState.sessionId),this.sessionKey=null,this.sessionKeyGen=null),this.terminalRetirementRequested&&(s=!0),s?await this.retireHostedSession(this.sessionState.sessionId):o.info("Preserving active backend session for daemon handoff",{sessionId:this.sessionState.sessionId}),this.sessionState=null}async retireHostedSession(t){let e;for(let i=1;i<=ye;i+=1)try{await this.appSyncClient.updateSession({sessionId:t,status:h.SessionStatus.INACTIVE});return}catch(s){e=s,o.warn("Failed to retire hosted Codex session",{sessionId:t,attempt:i,maxAttempts:ye,error:s instanceof Error?s.message:String(s)}),i<ye&&await new Promise(r=>setTimeout(r,Ci*i))}throw new Error(`Failed to retire hosted Codex session ${t} after ${ye} attempts: ${String(e)}`)}async cleanupDetachedWrapperArtifacts(){let t=process.env.CODEVIBE_CODEX_WRAPPER_PID;if(!t||!/^\d+$/.test(t))return;let e=process.env.TMPDIR||De.tmpdir(),i=[K.join(e,`codevibe-codex-server-${t}.pid`),K.join(e,`codevibe-codex-exit-${t}`),K.join(e,`codevibe-codex-tmux-session-${t}`)];for(let s of i)try{z.unlinkSync(s)}catch(r){r?.code!=="ENOENT"&&o.warn("Failed to remove detached-wrapper artifact",{ownedPath:s,error:String(r)})}}stop(t={}){return t.retireBackendSession!==!1&&(this.terminalRetirementRequested=!0),this.stopPromise?this.stopPromise:(this.stopPromise=this.doStop(),this.stopPromise)}async doStop(){o.info("Stopping CodeVibe Codex companion server"),this.stopTmuxLifecycleMonitor(),this.isStopping=!0,this.lifecycleGeneration+=1,this.sessionWatcher.stop();let t=this.launchSessionInitPromise,e=this.sessionStartedInitPromise,i=this.retiredSessionRecoveryPromise,s=this.hookEventChain,r=typeof this.sessionWatcher.waitForTranscriptAdmissions=="function"?this.sessionWatcher.waitForTranscriptAdmissions():Promise.resolve();await Promise.all([t?.catch(()=>{}),e?.catch(()=>{}),i?.catch(()=>{}),r.catch(()=>{}),s.catch(()=>{})]);try{let n=this.terminalRetirementRequested;await this.endActiveSession(n?"shutdown":"daemon-handoff",n),this.sessionWatcher.stop(),this.approvalDetector.shutdown(),Te()}finally{await this.httpApi.stop(),this.appSyncClient.cleanupSubscriptions(),this.terminalRetirementRequested&&await this.cleanupDetachedWrapperArtifacts()}o.info("CodeVibe Codex companion server stopped")}};if(require.main===module){let a=new Se;for(let t of Rt)process.on(t,()=>{o.info(`Received ${t}, evaluating terminal shutdown...`),a.stopForTerminalSignal(t).then(e=>{e&&process.exit(0)},e=>{o.error(`Failed to stop companion daemon after ${t}`,{error:e instanceof Error?e.message:String(e)}),process.exit(1)})});process.on("SIGUSR2",async()=>{o.info("Received SIGUSR2, handing off active session to replacement daemon..."),await a.stop({retireBackendSession:!1}),process.exit(0)}),(0,h.installDaemonProcessGuards)(o,{onFatal:()=>{a.stop().finally(()=>process.exit(1))}}),a.start().catch(t=>{o.error("Failed to start server:",t),process.exit(1)})}0&&(module.exports={CodexCompanionServer,TERMINAL_SHUTDOWN_SIGNALS,classifyTmuxHasSessionError,extractShellCommandFromPromptText,extractShellCommandPartsFromPromptText,restoreCodexPersistOptionCommand,shellCommandPartsMatchExpected});
@@ -36,6 +36,10 @@ while [ -L "$SOURCE" ]; do
36
36
  done
37
37
  SCRIPT_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
38
38
  PLUGIN_DIR="$(dirname "$SCRIPT_DIR")"
39
+ # Match Codex's documented configuration discovery. This also lets isolated
40
+ # candidate runs keep hooks/config separate without changing HOME, which owns
41
+ # the user's independent CodeVibe authentication state.
42
+ CODEX_CONFIG_HOME="${CODEX_HOME:-$HOME/.codex}"
39
43
 
40
44
  # ─── PATH augmentation ───────────────────────────────────────────────
41
45
  # When the install one-liner runs in a fresh terminal, Homebrew's
@@ -162,8 +166,8 @@ _CV_OS_VER="$(uname -s | cv_sanitize)"
162
166
  [ -z "$_CV_OS_VER" ] && _CV_OS_VER="unknown"
163
167
  _CV_ARCH_VER="$(uname -m | cv_sanitize)"
164
168
  [ -z "$_CV_ARCH_VER" ] && _CV_ARCH_VER="unknown"
165
- _CV_CODEX_AUTH="false"; [ -f "$HOME/.codex/auth.json" ] && _CV_CODEX_AUTH="true"
166
- _CV_CODEX_CONFIG="false"; [ -f "$HOME/.codex/config.toml" ] && _CV_CODEX_CONFIG="true"
169
+ _CV_CODEX_AUTH="false"; [ -f "$CODEX_CONFIG_HOME/auth.json" ] && _CV_CODEX_AUTH="true"
170
+ _CV_CODEX_CONFIG="false"; [ -f "$CODEX_CONFIG_HOME/config.toml" ] && _CV_CODEX_CONFIG="true"
167
171
  _CV_INSIDE_TMUX="false"; [ -n "$TMUX" ] && _CV_INSIDE_TMUX="true"
168
172
  _CV_IS_TTY="false"; { [ -t 0 ] && [ -t 1 ]; } && _CV_IS_TTY="true"
169
173
  cv_telem "wrapper_started" "\"invocation\":\"session\",\"os\":\"$_CV_OS_VER\",\"arch\":\"$_CV_ARCH_VER\",\"codex_version\":\"$_CV_CODEX_VER\",\"node_version\":\"$_CV_NODE_VER\",\"tmux_version\":\"$_CV_TMUX_VER\",\"codex_auth_present\":$_CV_CODEX_AUTH,\"codex_config_present\":$_CV_CODEX_CONFIG,\"inside_tmux\":$_CV_INSIDE_TMUX,\"is_terminal\":$_CV_IS_TTY"
@@ -177,6 +181,27 @@ log() {
177
181
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
178
182
  }
179
183
 
184
+ tmux_session_state() {
185
+ local target="$1"
186
+ local output
187
+ local rc
188
+ output="$(tmux has-session -t "$target" 2>&1)"
189
+ rc=$?
190
+ if [ "$rc" -eq 0 ]; then
191
+ printf '%s\n' "alive"
192
+ return
193
+ fi
194
+ case "$output" in
195
+ "can't find session:"*|"no server running on "*|"error connecting to "*" (No such file or directory)"|"error connecting to "*" (Connection refused)")
196
+ printf '%s\n' "absent"
197
+ ;;
198
+ *)
199
+ log "WARN: tmux inspection failed for $target; preserving daemon: $output"
200
+ printf '%s\n' "unknown"
201
+ ;;
202
+ esac
203
+ }
204
+
180
205
  # Cleanup function to kill server when wrapper exits
181
206
  cleanup() {
182
207
  local wrapper_exit_code=$?
@@ -187,11 +212,17 @@ cleanup() {
187
212
  # that live Codex session disconnected on mobile. The daemon now monitors
188
213
  # this exact tmux session and performs normal cleanup when the native
189
214
  # session actually ends, so a detach transfers lifecycle ownership to it.
190
- if [ "${_CV_TMUX_STARTED:-false}" = "true" ] \
191
- && tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
192
- log "Tmux client detached while Codex remains active; leaving daemon running"
193
- rm -f "${CODEVIBE_TMPDIR}/codevibe-codex-server-$$.pid"
194
- return
215
+ if [ "${_CV_TMUX_STARTED:-false}" = "true" ]; then
216
+ local tmux_state
217
+ tmux_state="$(tmux_session_state "$SESSION_NAME")"
218
+ if [ "$tmux_state" = "alive" ]; then
219
+ log "Tmux client detached while Codex remains active; leaving daemon running"
220
+ return
221
+ fi
222
+ if [ "$tmux_state" = "unknown" ]; then
223
+ log "Tmux state is ambiguous; preserving daemon and native session"
224
+ return
225
+ fi
195
226
  fi
196
227
 
197
228
  # Fire wrapper_exited telemetry BEFORE killing the server so the MCP
@@ -247,7 +278,8 @@ cleanup() {
247
278
  rm -f "${CODEVIBE_TMPDIR}/codevibe-codex-${SESSION_NAME}.port"
248
279
  rm -f "$_CV_CODEX_EXIT_FILE"
249
280
 
250
- # Remove our hooks from ~/.codex/hooks.json (idempotent, concurrent-safe).
281
+ # Remove our hooks from Codex's configured hooks.json (idempotent,
282
+ # concurrent-safe).
251
283
  # Uses Node (always available — required to run the daemon) so users
252
284
  # without jq don't end up with stale CodeVibe hook entries leftover
253
285
  # after the wrapper exits. Ownership check is path-prefix + filename
@@ -255,13 +287,13 @@ cleanup() {
255
287
  # that happen to contain the string codevibe-codex (e.g. a username,
256
288
  # a directory like /Users/codevibe-codex/) cannot trigger removal of
257
289
  # the user's unrelated hooks.
258
- if [ -f "$HOME/.codex/hooks.json" ] && command -v node &> /dev/null; then
290
+ if [ -f "$CODEX_CONFIG_HOME/hooks.json" ] && command -v node &> /dev/null; then
259
291
  # Only remove if no other codevibe-codex sessions are running
260
292
  if [ "$(pgrep -f 'codevibe-codex' | wc -l)" -le 1 ]; then
261
- log "Removing CodeVibe hooks from $HOME/.codex/hooks.json"
293
+ log "Removing CodeVibe hooks from $CODEX_CONFIG_HOME/hooks.json"
262
294
  # The shared helper keeps install and cleanup ownership rules in
263
295
  # lockstep, including task-named CodeVibe agent worktrees.
264
- if CV_HOOKS_FILE="$HOME/.codex/hooks.json" \
296
+ if CV_HOOKS_FILE="$CODEX_CONFIG_HOME/hooks.json" \
265
297
  node "$PLUGIN_DIR/libexec/codex-hook-config.js" cleanup 2>>"$LOG_FILE"; then
266
298
  :
267
299
  else
@@ -313,6 +345,7 @@ fi
313
345
  # Generate a unique session name
314
346
  SESSION_NAME="${TMUX_SESSION_PREFIX}-$$"
315
347
  WORKING_DIR="$(pwd)"
348
+ export CODEVIBE_CODEX_WRAPPER_PID="$$"
316
349
 
317
350
  log "Starting codevibe-codex with session: $SESSION_NAME"
318
351
  log "Working directory: $WORKING_DIR"
@@ -360,7 +393,7 @@ export CODEVIBE_CODEX_PLUGIN_DIR="$PLUGIN_DIR"
360
393
  # Install hooks.json for Codex CLI with absolute paths (idempotent)
361
394
  # Codex CLI does NOT expand env vars in hook commands, so we must
362
395
  # write absolute paths at runtime.
363
- CODEX_HOOKS_FILE="$HOME/.codex/hooks.json"
396
+ CODEX_HOOKS_FILE="$CODEX_CONFIG_HOME/hooks.json"
364
397
  HOOKS_DIR="$PLUGIN_DIR/hooks"
365
398
 
366
399
  generate_hooks_json() {
@@ -379,8 +412,8 @@ HOOKEOF
379
412
  }
380
413
 
381
414
  # Ensure hooks feature flag is enabled in Codex config
382
- CODEX_CONFIG="$HOME/.codex/config.toml"
383
- mkdir -p "$HOME/.codex"
415
+ CODEX_CONFIG="$CODEX_CONFIG_HOME/config.toml"
416
+ mkdir -p "$CODEX_CONFIG_HOME"
384
417
  if [ ! -f "$CODEX_CONFIG" ]; then
385
418
  printf '[features]\nhooks = true\n' > "$CODEX_CONFIG"
386
419
  log "Created config.toml with hooks feature flag"
@@ -331,8 +331,8 @@ ifeq ($(strip $(foreach prefix,$(NO_LOAD),\
331
331
  endif
332
332
 
333
333
  quiet_cmd_regen_makefile = ACTION Regenerating $@
334
- cmd_regen_makefile = cd $(srcdir); /opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/hendryyeh/Library/Caches/node-gyp/24.4.0" "-Dnode_gyp_dir=/opt/homebrew/lib/node_modules/npm/node_modules/node-gyp" "-Dnode_lib_file=/Users/hendryyeh/Library/Caches/node-gyp/24.4.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/hendryyeh/Workspace/CodeVibe/.agent-worktrees/codex-1/p17-retry-containment/codevibe-codex-plugin/node_modules/fs-ext" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/Users/hendryyeh/Workspace/CodeVibe/.agent-worktrees/codex-1/p17-retry-containment/codevibe-codex-plugin/node_modules/fs-ext/build/config.gypi -I/opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/hendryyeh/Library/Caches/node-gyp/24.4.0/include/node/common.gypi "--toplevel-dir=." binding.gyp
335
- Makefile: $(srcdir)/../../../../../../../../Library/Caches/node-gyp/24.4.0/include/node/common.gypi $(srcdir)/build/config.gypi $(srcdir)/../../../../../../../../../../opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/binding.gyp
334
+ cmd_regen_makefile = cd $(srcdir); /opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/hendryyeh/Library/Caches/node-gyp/24.4.0" "-Dnode_gyp_dir=/opt/homebrew/lib/node_modules/npm/node_modules/node-gyp" "-Dnode_lib_file=/Users/hendryyeh/Library/Caches/node-gyp/24.4.0/<(target_arch)/node.lib" "-Dmodule_root_dir=/Users/hendryyeh/Workspace/CodeVibe/codevibe-codex-plugin/node_modules/fs-ext" "-Dnode_engine=v8" "--depth=." "-Goutput_dir=." "--generator-output=build" -I/Users/hendryyeh/Workspace/CodeVibe/codevibe-codex-plugin/node_modules/fs-ext/build/config.gypi -I/opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/addon.gypi -I/Users/hendryyeh/Library/Caches/node-gyp/24.4.0/include/node/common.gypi "--toplevel-dir=." binding.gyp
335
+ Makefile: $(srcdir)/../../../../../../../opt/homebrew/lib/node_modules/npm/node_modules/node-gyp/addon.gypi $(srcdir)/../../../../../Library/Caches/node-gyp/24.4.0/include/node/common.gypi $(srcdir)/binding.gyp $(srcdir)/build/config.gypi
336
336
  $(call do_cmd,regen_makefile)
337
337
 
338
338
  # "all" is a concatenation of the "all" targets from all the included
@@ -490,7 +490,7 @@
490
490
  "python": "/opt/homebrew/opt/python@3.14/bin/python3.14",
491
491
  "standalone_static_library": 1,
492
492
  "global_prefix": "/opt/homebrew",
493
- "local_prefix": "/Users/hendryyeh/Workspace/CodeVibe/.agent-worktrees/codex-1/p17-retry-containment/codevibe-codex-plugin",
493
+ "local_prefix": "/Users/hendryyeh/Workspace/CodeVibe/codevibe-codex-plugin",
494
494
  "globalconfig": "/opt/homebrew/etc/npmrc",
495
495
  "init_module": "/Users/hendryyeh/.npm-init.js",
496
496
  "userconfig": "/Users/hendryyeh/.npmrc",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantiya/codevibe-codex-plugin",
3
- "version": "2.0.14",
3
+ "version": "2.0.15",
4
4
  "description": "Control OpenAI Codex CLI from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
5
5
  "main": "dist/server.js",
6
6
  "codevibe": {