hankweave 0.2.1 → 0.2.2
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/index.js +4 -4
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -285,14 +285,14 @@ return __eta.res;
|
|
|
285
285
|
${"═".repeat(50)}`),console.log(" Hankweave Server Started"),console.log(` WebSocket: ws://localhost:${this.config.port}`),e!==void 0)console.log(` Proxy: http://localhost:${e}`);return console.log(`${"═".repeat(50)}
|
|
286
286
|
`),process.on("SIGINT",()=>this.shutdown("SIGINT")),process.on("SIGTERM",()=>this.shutdown("SIGTERM")),process.on("uncaughtException",(u)=>{if(this.logger.log(`Uncaught exception: ${u.message}`,"error"),u.stack)this.logger.log(`Stack trace:
|
|
287
287
|
${u.stack}`,"error");this.shutdown("uncaughtException")}),process.on("unhandledRejection",(u,a)=>{if(this.logger.log(`Unhandled rejection at: ${a}, reason: ${u}`,"error"),u instanceof Error){if(this.logger.log(`Error name: ${u.name}`,"error"),this.logger.log(`Error message: ${u.message}`,"error"),u.stack)this.logger.log(`Stack trace:
|
|
288
|
-
${u.stack}`,"error")}else if(u&&typeof u==="object")try{this.logger.log(`Reason object: ${JSON.stringify(u,null,2)}`,"error")}catch{this.logger.log(`Reason (unstringifiable): ${String(u)}`,"error")}this.shutdown("unhandledRejection")}),this.config.port}updateLockFileWithPort(t,e){try{if(S.existsSync(this.config.lockFile)){let u=JSON.parse(S.readFileSync(this.config.lockFile,"utf-8"));if(u.port=t,e!==void 0)u.proxyPort=e;S.writeFileSync(this.config.lockFile,JSON.stringify(u)),this.logger.log(`Lock file updated with port ${t}${e!==void 0?`, proxy ${e}`:""}`)}}catch(u){this.logger.log(`Failed to update lock file with port: ${u}`,"error")}}handleConnection(t){let e=t.data.id;this.logger.log(`Client ${e} connected`),this.clients.set(e,t),this.logger.log(`Client ${e} waiting for handshake`)}async handleHandshake(t,e){let{mode:u,sendPreviousEvents:a=!1}=e.data,l=t.data.id,i=u;this.logger.log(`Client ${l} granted ${i} access`),t.data={...t.data,id:l,mode:i,handshakeComplete:!0};let{events:o,totalEvents:d,hasMore:m}=a?await this.eventJournal.getMostRecentEvents(this.config.handshakeHistoryLimit):{events:[],totalEvents:await this.eventJournal.getTotalEvents(),hasMore:!1};this.logger.log(`Sending ${o.length} events (of ${d} total) to client ${l}`+(a?" (limited history)":" (no history)")+(m?" with additional history available via download":""));let r={type:"handshake.response",data:{clientId:l,mode:i,eventHistory:o,totalEvents:d}};t.send(JSON.stringify(r)),this.logger.log(`Handshake complete for client ${l} (${i})`);let n={id:b(X()),timestamp:new Date().toISOString(),type:"server.ready",data:{serverVersion:this.config.version,executionPath:this.config.executionPath,agentRootPath:this.config.agentRootPath,dataPath:this.config.dataPathInExecutionDir,port:this.config.port,proxyPort:this.proxyRunner?.getActualPort()??void 0}};if(this.emit("event",n,t),this.logger.log(`[handleHandshake] config.autostart = ${this.config.autostart}`),this.config.autostart)this.logger.log("[handleHandshake] Calling requestAutostart()"),this.requestAutostart().catch((c)=>{this.logger.log(`[handleHandshake] requestAutostart error: ${c}`,"error")});else{let c={id:b(X()),timestamp:new Date().toISOString(),type:"server.idle",data:{reason:"startup",message:"Server ready. Waiting for commands (autostart disabled)."}};this.emit("event",c)}}async handleMessage(t,e){try{t.data.lastActivity=new Date;let u=JSON.parse(e.toString());if(u.type==="handshake"){this.handleHandshake(t,u);return}if(!t.data.handshakeComplete){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Handshake required before sending commands",fatal:!1}},t);return}let a=B8.safeParse(u);if(!a.success){this.logger.log(`Invalid client command: ${a.error.message}`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Invalid command format",fatal:!1}},t);return}await this.handleCommand(a.data,t)}catch(u){let a=Wt(u);if(this.logger.log(`Error handling command: ${a.message}`,"error"),a.stack)this.logger.log(`Stack trace: ${a.stack}`,"error");this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Command execution failed: ${a.message}`,fatal:!1}},t)}}handleClose(t){let e=t.data.id;this.logger.log(`Client ${e} disconnected`),this.clients.delete(e)}async handleCommand(t,e){if(this.logger.log(`Handling command: ${t.type}`),this.isRollingBack&&!this.READ_ONLY_COMMANDS.has(t.type)){this.logger.log(`Client ${e.data.id} attempted state-modifying command while rollback is in progress`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot execute state-modifying commands while rollback is in progress",context:`Attempted command: ${t.type}`,codon:this.currentCodon?.codon.id,fatal:!1,severity:"operation",code:"ROLLBACK_IN_PROGRESS"}},e);return}if(!this.READ_ONLY_COMMANDS.has(t.type)){if(!e.data.handshakeComplete){this.logger.log(`Client ${e.data.id} attempted state-modifying command without handshake`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot execute state-modifying commands without handshake",context:`Attempted command: ${t.type}`,codon:this.currentCodon?.codon.id,fatal:!1,severity:"operation",code:"HANDSHAKE_REQUIRED"}},e);return}if(e.data.mode==="readonly"){this.logger.log(`Client ${e.data.id} attempted state-modifying command in read-only mode`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot execute state-modifying commands in read-only mode",context:`Attempted command: ${t.type}`,codon:this.currentCodon?.codon.id,fatal:!1,severity:"operation",code:"INSUFFICIENT_PERMISSIONS"}},e);return}}switch(t.type){case"codon.start":{await this.startCodon(t.data.codonId,t.data.skipPreCommands);break}case"codon.next":await this.startNextCodon();break;case"codon.skip":await this.skipCurrentCodon();break;case"codon.redo":await this.redoCurrentCodon();break;case"server.shutdown":await this.shutdown(t.data?.reason||"client request");break;case"checkpoint.list":await this.listCheckpoints(t.data?.runId);break;case"codon.forceStop":await this.forceStopCodon(t.data?.reason);break;case"rollback.toCheckpoint":await this.rollbackToCheckpoint(t.data.checkpointSha,t.data.autoRestart??!1);break;case"rollback.toCodon":await this.rollbackToCodon(t.data.codonId,t.data.checkpointType,t.data.autoRestart??!1);break;case"rollback.toLastSuccess":await this.rollbackToLastSuccess(t.data?.autoRestart??!1);break;case"ping":this.handlePing(t.id,e);break;case"ping.broadcast":this.handlePingBroadcast(t.id,e);break;case"history.sync":await this.handleHistorySync(t,e);break;default:i_(t)}}handlePing(t,e){if(this.logger.log(`Handling ping command: ${t}`),e?.data.handshakeComplete){let u={id:b(X()),timestamp:new Date().toISOString(),type:"pong",data:{message:"pong",timestamp:new Date().toISOString()}};this.emit("event",u,e)}else this.logger.log("Ping command received but no valid sender provided","error")}handlePingBroadcast(t,e){this.logger.log(`Handling ping.broadcast command: ${t}`);let u=e?.data.id||"unknown";for(let[a,l]of this.clients){if(!l.data.handshakeComplete)continue;let i={id:b(X()),timestamp:new Date().toISOString(),type:"pong",data:{message:"pong",timestamp:new Date().toISOString(),clientId:u}};this.emit("event",i,l)}}async handleHistorySync(t,e){if(!e?.data.handshakeComplete){this.logger.log("History sync command received but sender not ready","error");return}this.logger.log(`Handling history.sync command: ${t.id}`);let u=e;if(!u){this.logger.log("History sync command received without sender","error");return}let a=this.eventJournal.getAllEvents()[Symbol.asyncIterator](),l=await a.next();if(l.done){this.sendHistoryBatch(u,[],!1);return}let i=l.value;while(!0){if(l=await a.next(),l.done){this.sendHistoryBatch(u,[i],!1);break}this.sendHistoryBatch(u,[i],!0),i=l.value}}sendHistoryBatch(t,e,u){let a={id:b(X()),timestamp:new Date().toISOString(),type:"history.batch",data:{events:e,hasMore:u}};try{t.send(JSON.stringify(a)),this.logger.log(`Sent ${e.length} events to client ${t.data.id}`)}catch(l){this.logger.log(`Failed to send history batch to client ${t.data.id}: ${l}`,"error"),this.clients.delete(t.data.id)}}emit(t,e,u){if(t!=="event")throw this.logger.log(`Unsupported event type emitted: ${t}`,"error"),Error(`Unsupported event type: ${t}. Can only emit "event" events.`);let a=e,l=v2(a),i=k2(a),o=Ra(a),d=_4(a);if(!l&&!i&&!o&&!d)throw this.logger.log(`Unknown event type: ${a.type}`,"error"),Error(`Unknown event: ${a.type}`);if(d&&!u)return this.logger.log(`Connection state event ${a.type} requires a target client but none provided`,"error"),!1;if((l||i||o)&&!u){if(this.eventJournalAppendQueue=this.eventJournalAppendQueue.then(()=>this.eventJournal.append(a)).catch((m)=>{this.logger.log(`Error appending event to journal: ${m}`,"error")}),this.clients.size>0)for(let[m,r]of this.clients){if(!r.data.handshakeComplete)continue;try{r.send(JSON.stringify(a))}catch(n){this.logger.log(`Failed to send event to client ${r.data.id}: ${n}`,"error")}}}else try{u?.send(JSON.stringify(a))}catch(m){this.logger.log(`Failed to send event to client ${u?.data.id}: ${m}`,"error")}return super.emit(t,e)}async sendStateSnapshot(){let t=this.stateManager.getTotalCost(),e=this.serverStartTime?Date.now()-this.serverStartTime.getTime():0,u=await this.getTerminalCodonsForSnapshot(),a=this.stateManager.getCurrentlyRunningCodon(),l={id:b(X()),timestamp:new Date().toISOString(),type:"state.snapshot",data:{currentCodon:a||void 0,completedCodons:u,fileTree:[],totalCost:t,totalTime:e,recentFileAccess:this.recentFileAccess,isRollingBack:this.isRollingBack}};this.emit("event",l)}async getTerminalCodonsForSnapshot(){return(await this.stateManager.getExecutionThread()).codons.filter((e)=>Rt(e.codon.status)).map((e)=>e.codon)}async startNewRun(t){let e=u0(`${Date.now()}-${Math.random().toString(36).substring(2,7)}`),u=dt.join(this.config.executionPath,".hankweave","runs",e);await S.promises.mkdir(u,{recursive:!0}),this.stateManager.transition({type:"RunStarted",data:{runId:e,runFolder:u,gitBranch:`run-${e}`,startingConditions:t||{type:"fresh"},serverPid:process.pid}}),this.currentRunId=e;let a={pid:process.pid,runId:e,startTime:new Date().toISOString(),lastHeartbeat:new Date().toISOString(),port:this.config.port},l=dt.dirname(this.config.lockFile);if(!S.existsSync(l))S.mkdirSync(l,{recursive:!0});S.writeFileSync(this.config.lockFile,JSON.stringify(a)),this.heartbeatInterval=setInterval(()=>{this.updateHeartbeat()},30000),this.logger.log(`Started new run: ${e}`)}updateHeartbeat(){try{if(S.existsSync(this.config.lockFile)){let t=JSON.parse(S.readFileSync(this.config.lockFile,"utf-8"));t.lastHeartbeat=new Date().toISOString(),S.writeFileSync(this.config.lockFile,JSON.stringify(t))}}catch(t){this.logger.log(`Failed to update heartbeat: ${t}`,"error")}}async startCodon(t,e,u){let a=this.stateManager.getCodonById(t);if(!a){await this.handleError(Error(`Unknown codon: ${t}`),"startCodon","operation");return}let l=a.codon;this.logger.log(`Starting codon: ${l.name}`);let i=await this.stateManager.getCodonHistory(lt(l.id)),o;for(let s of i)if("rigSetupCheckpoint"in s.codon&&s.codon.rigSetupCheckpoint){o=s.codon.rigSetupCheckpoint;break}if(o)this.logger.log(`Found existing rig setup checkpoint: ${o}`);let d=this.stateManager.getCurrentlyRunningCodon();if(d&&!Rt(d.status)){await this.handleError(Error(`Codon already running: ${d.codonId}`),"startCodon","operation");return}let m=this.stateManager.getCurrentRun();if(m&&!u){let s=m.codons.find((x)=>x.codonId===t);if(s&&Rt(s.status))this.logger.log(`Codon ${t} was already attempted in current run, starting new run`),this.stateManager.transition({type:"RunCompleted",data:{runId:m.runId}}),await this.stateManager.waitForPendingTransitions(),await this.startNewRun({type:"continuation",source:{runId:m.runId,afterCodon:null,checkpointSha:""},reason:"retry"})}if(!this.currentRunId){await this.handleError(Error("No active run"),"startCodon","fatal");return}if(this.stateManager.transition({type:"CodonStarted",data:{runId:this.currentRunId,codonId:t,loopContext:a.loopContext}}),!this.currentRunId){await this.handleError(Error("No active run during codon start"),"startCodon","fatal");return}if(!e&&!o&&l.rigSetup&&l.rigSetup.length>0){let s=l.rigSetup.length,x=Date.now(),w=0,g=0;this.logger.log(`Running rig setup for codon: ${l.name}`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Rig setup started for codon '${l.name}': ${s} operation${s!==1?"s":""}`}});let v=null;for(let[z,k]of l.rigSetup.entries()){let G=z+1,f=k.type,Q=k.type==="copy"&&k.copy?`${k.copy.from} → ${k.copy.to}`:k.type==="command"&&k.command?`'${k.command.run}'`:"unknown";this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Rig operation ${G}/${s}: ${f} ${Q}`}});try{if(k.type==="copy"&&k.copy){let h=dt.join(this.config.agentRootPath,k.copy.to);if(this.logger.log(`Copying ${k.copy.from} to ${h}`),S.existsSync(h))this.logger.log(`Warning: Target path already exists: ${h}. Removing it before copying.`),await S.promises.rm(h,{recursive:!0}),this.logger.log(`Removed existing path: ${h}`);await this.copyPath(k.copy.from,h),v=h,this.logger.log(`Copied ${k.copy.from} to ${h}`)}else if(k.type==="command"&&k.command){await this.runCommand(k,v||void 0);let h=k.command.workingDirectory==="lastCopied"&&v?v:this.config.agentRootPath;this.logger.log(`Ran command in ${h}: ${k.command.run}`)}w++}catch(h){let D=Wt(h).message,$=h instanceof ti,J=$?h.exitCode:-1,T=$?h.stdout:"",y=$?h.stderr:"";if(this.logger.log(`[DEBUG] Rig setup error details - Exit code: ${J}`,"error"),T)this.logger.log(`[DEBUG] Rig setup error stdout: ${T}`,"info");if(y)this.logger.log(`[DEBUG] Rig setup error stderr: ${y}`,"error");if(k.allowFailure||(this.config.ignoreRigFailures??!1)){let vt=k.allowFailure?"allowFailure=true":"--ignore-rig-failures";this.logger.log(`Rig setup operation failed (${vt}) at item ${z+1} (${JSON.stringify(k)}): ${D}`,"info"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Rig setup operation failed but continuing (${vt}): ${D}`,context:`Codon ${l.id} - ${k.type} operation (item ${z+1})`,codon:l.id,fatal:!1,severity:"operation"}}),g++;continue}if(this.logger.log(`Rig setup failed at item ${z+1} (${JSON.stringify(k)}): ${D}`,"error"),this.codonFailureReason={type:"unknown",retriable:!0,message:`Rig setup failed at ${k.type} operation: ${D}`},this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"preparing",to:"failed",metadata:{exitCode:J,failedDuring:"preparing",failureReason:this.codonFailureReason}}});this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Rig setup failed: ${D}`,context:`Codon ${l.id} - ${k.type} operation (item ${z+1})`,codon:l.id,fatal:!0,severity:"fatal"}});let K=this.resolveFailurePolicy(t,l,this.codonFailureReason);if(K==="continue"){if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:t,success:!1,cost:0,duration:Date.now()-(this.currentCodon?.startTime?.getTime()||Date.now()),exitStatus:{type:"error",code:J},failureReason:this.codonFailureReason,failureIgnored:!0}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${t} rig setup failed, continuing (onFailure=ignore)`}}),this.cleanupCurrentCodon(),await this.stateManager.waitForPendingTransitions(),await this.stateManager.expandNextIterationForCodon({codonId:lt(t),contextExceeded:!1}),this.config.autostart)await this.autoStartNextCodon();return}if(K==="retry"){let vt=this.retryAttempts.get(t)||0,tt=l.retryConfig?.maxAttempts??3,At=l.retryConfig?.delayMs??1000;if(vt<tt){if(this.retryAttempts.set(t,vt+1),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Retrying codon ${t} after rig setup failure (attempt ${vt+1}/${tt})`}}),this.cleanupCurrentCodon(),await this.delay(At),this.isShuttingDown){this.logger.log(`Server shutting down, skipping retry for ${t}`);return}await this.startCodon(t,!1,!0);return}}this.cleanupCurrentCodon(),await this.handleError(Wt(h),`Rig setup item ${z+1}`,"fatal");return}}let B=Date.now()-x;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Rig setup completed for codon '${l.name}' (${B}ms, ${w} succeeded${g>0?`, ${g} failed`:""})`}})}this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"preparing",to:"starting",metadata:{checkpointSha:o}}});let r=await this.loadSentinelsForCodon(l,t),n=r.errors.filter((s)=>s.fatal);if(n.length>0){let s=n.map((g)=>g.ref).join(", "),x=`Required sentinels failed to load (failCodonIfNotLoaded=true): ${s}`;this.codonFailureReason={type:"sentinel-load-failure",retriable:!1,message:x,sentinelRefs:n.map((g)=>g.ref)},this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:lt(l.id),from:"starting",to:"failed",metadata:{failedDuring:"starting",failureReason:this.codonFailureReason}}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:x,context:`Failed sentinels: ${s}`,codon:l.id,fatal:!0,severity:"codon",code:"SENTINEL_LOAD_FAILURE"}});let w=this.resolveFailurePolicy(t,l,this.codonFailureReason);if(w==="continue"){if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:t,success:!1,cost:0,duration:Date.now()-(this.currentCodon?.startTime?.getTime()||Date.now()),exitStatus:{type:"error",code:-1},failureReason:this.codonFailureReason,failureIgnored:!0}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${t} sentinel load failed, continuing (onFailure=ignore)`}}),this.cleanupCurrentCodon(),await this.stateManager.waitForPendingTransitions(),await this.stateManager.expandNextIterationForCodon({codonId:lt(t),contextExceeded:!1}),this.config.autostart)await this.autoStartNextCodon();return}if(this.cleanupCurrentCodon(),w==="shutdown"){if(this.currentRunId)this.stateManager.transition({type:"RunFailed",data:{runId:this.currentRunId}}),await this.stateManager.waitForPendingTransitions();await this.shutdown("sentinel load failure")}return}for(let s of r.errors.filter((x)=>!x.fatal))this.logger.log(`Non-required sentinel failed to load (${s.ref}): ${s.error}`,"info");let c=this.config.codons.findIndex((s)=>s.id===l.id);if(c>=0){for(let s=0;s<=c;s++){let x=this.config.codons[s];if(x.type!=="loop"&&x.checkpointedFiles&&x.checkpointedFiles.length>0)await this.addCheckpointPatterns(x.checkpointedFiles)}if(!e&&l.rigSetup&&this.checkpointingEnabled)await this.createCheckpoint({status:"rig-setup",codonId:t,codonName:l.name,runId:this.currentRunId||u0("unknown"),timestamp:new Date().toISOString()})}let _=null;if(l.continuationMode==="continue-previous"){let s=this.stateManager.getState(),x=await g0(s,void 0,void 0,this.logger);if(_=G8(x,t,s),_)this.logger.log(`Codon ${t} will continue from previous session: ${_}`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Continuing from previous session: ${_}`}});else{let g=`Codon ${t} requires continuation from previous codon but no valid session found. Previous codon must complete successfully or be skipped with at least one assistant message.`;if(this.logger.log(g,"error"),this.codonFailureReason={type:"unknown",retriable:!1,message:g},this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"starting",to:"failed",metadata:{failedDuring:"starting",failureReason:this.codonFailureReason}}});this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:g,codon:l.id,fatal:!0,severity:"codon"}});let v=this.resolveFailurePolicy(t,l,this.codonFailureReason);if(v==="continue"){if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:t,success:!1,cost:0,duration:Date.now()-(this.currentCodon?.startTime?.getTime()||Date.now()),exitStatus:{type:"error",code:-1},failureReason:this.codonFailureReason,failureIgnored:!0}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${t} continuation session not found, continuing (onFailure=ignore)`}}),this.cleanupCurrentCodon(),await this.stateManager.waitForPendingTransitions(),await this.stateManager.expandNextIterationForCodon({codonId:lt(t),contextExceeded:!1}),this.config.autostart)await this.autoStartNextCodon();return}if(this.cleanupCurrentCodon(),v==="shutdown"){if(this.currentRunId)this.stateManager.transition({type:"RunFailed",data:{runId:this.currentRunId}}),await this.stateManager.waitForPendingTransitions();await this.shutdown("continuation session not found")}return}}if(this.currentCodon={status:"initializing",codonId:t,codon:l,previousSessionId:_?Xu(_):void 0,startTime:new Date,codonCost:0,codonTokens:{inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0}},l.checkpointedFiles&&l.checkpointedFiles.length>0)this.watchedPatterns=l.checkpointedFiles,this.logger.log(`Watching patterns: ${this.watchedPatterns.join(", ")}`);if(l.checkpointedFiles&&l.checkpointedFiles.length>0){let s=await r0.resolveFiles(this.config.agentRootPath,l.checkpointedFiles),x=await Promise.all(s.map(async(w)=>{let g=dt.join(this.config.agentRootPath,w),v=await S.promises.stat(g),B=await S.promises.readFile(g,"utf-8");return{path:w,content:B,lastModified:v.mtime.toISOString()}}));if(x.length>0){for(let g of x)this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"file.updated",data:{path:g.path,filename:dt.basename(g.path),content:g.content,action:"created"}});let w=x.reduce((g,v)=>new Date(v.lastModified)>new Date(g.lastModified)?v:g);this.recentFileAccess={path:w.path,content:w.content,timestamp:new Date(w.lastModified)},await this.sendFileTreeUpdate()}}await this.runCodon(t,l,_)}async runCodon(t,e,u){try{let a=this.stateManager.getCurrentRun();if(!a||!a.runFolder)throw Error("No active run or run folder not found");let l=a.runFolder;await S.promises.mkdir(l,{recursive:!0});let i=`${t.replace(/#/g,"-")}-claude.log`,o=dt.join(l,i),d={codon:e,codonId:t,executionPath:this.config.executionPath,agentRootPath:this.config.agentRootPath,logger:this.logger,logParsingInterval:this.config.logParsingInterval,anthropicBaseUrl:this.proxyRunner?.proxyUrl,logPath:o,globalSystemPrompt:this.config.globalSystemPrompt},m=e.exhaustWithPrompt?new x0({...d,extensionConfig:{exhaustWithPrompt:e.exhaustWithPrompt,maxExtensions:e.maxExtensions??100},shouldInterrupt:()=>this.isSkippingCodon||this.isForceStopping,onExtension:(n)=>{this.handleExtension(t,e,n)}}):new x0(d);if(this.codonRunners.set(t,m),this.logger.log(`[runCodon] Setting up event handlers for codon ${t}`,"debug"),this.setupCodonRunnerEventHandlers(t),this.logger.log(`[runCodon] Starting runner execution for codon ${t}, previousSessionId: ${u||"none"}`,"debug"),await m.run(u?Xu(u):void 0),this.logger.log(`[runCodon] Runner.run() completed for codon ${t}`,"debug"),this.logger.log(`[runCodon] Validating process started for codon ${t}`,"debug"),!this.currentRunId)throw Error("No active run while starting Claude process");let r=m.getPid();if(this.logger.log(`[runCodon] Got PID ${r} for codon ${t}`,"debug"),!r)throw Error("Failed to get process PID");this.logger.log(`[runCodon] Transitioning codon ${t} to initializing (PID: ${r})`,"debug"),this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"starting",to:"initializing",metadata:{claudePid:r,claudeLogPath:dt.relative(this.config.executionPath,o),...u&&{previousSessionId:Xu(u)}}}}),this.logger.log(`[runCodon] Successfully completed runCodon for ${t}, status should be initializing`,"debug")}catch(a){this.logger.log(`[runCodon] CAUGHT ERROR during codon ${t} initialization: ${Wt(a).message}`,"error"),this.logger.log(`[runCodon] Error stack: ${Wt(a).stack}`,"error");let l=this.codonRunners.get(t);if(this.logger.log(`[runCodon] State at error - currentRunId: ${this.currentRunId}, hasRunner: ${!!l}`,"error"),l)this.logger.log(`[runCodon] Calling cleanup on runner for codon ${t} due to error`,"error"),await l.cleanup(),this.codonRunners.delete(t),this.logger.log("[runCodon] CodonRunner cleanup complete, removed from map","error");if(this.currentRunId)this.logger.log(`[runCodon] Transitioning codon ${t} to failed due to error`,"error"),this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"starting",to:"failed",metadata:{failedDuring:"starting",failureReason:{type:"unknown",retriable:!1,message:Wt(a).message}}}});throw this.logger.log("[runCodon] Calling cleanupCurrentCodon()","error"),this.cleanupCurrentCodon(),this.logger.log("[runCodon] Re-throwing error","error"),a}}setupCodonRunnerEventHandlers(t){let e=this.codonRunners.get(t);if(!e)throw Error(`Cannot setup handlers: no runner found for codon ${t}`);e.on("exit",(u,a,l)=>{if(a)this.logger.log(`[HANKWEAVE-SERVER] Context exceeded error detected for codon ${t}`,"error");this.handleCodonComplete(u,a,l)}),e.on("error",(u)=>{this.handleError(u,`Process for codon ${t}`,"fatal")}),e.on("systemMessage",(u)=>{this.handleSystemMessage(u,t)}),e.on("assistantMessage",(u)=>{this.handleAssistantMessage(u,t)}),e.on("userMessage",(u)=>{this.handleUserMessage(u,t)}),e.on("resultMessage",(u)=>{this.handleResultMessage(u,t)})}handleSystemMessage(t,e){if(t.subtype==="init")this.logger.log(`[handleSystemMessage] Received init message for codon ${e}, session: ${t.session_id}`,"debug"),this.logger.log(`[handleSystemMessage] Condition check - subtype=init: true, has_session: ${!!t.session_id}, has_currentCodon: ${!!this.currentCodon}, currentCodon_status: ${this.currentCodon?.status||"N/A"}`,"debug");if(t.subtype==="init"&&t.session_id&&this.currentCodon&&this.currentCodon.status==="initializing"){if(this.logger.log(`[handleSystemMessage] All conditions met, transitioning codon ${e} to running`,"info"),this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:lt(e),from:"initializing",to:"running",metadata:{claudeSessionId:Xu(t.session_id)}}});this.currentCodon={status:"running",codonId:this.currentCodon.codonId,codon:this.currentCodon.codon,sessionId:Xu(t.session_id),previousSessionId:this.currentCodon.previousSessionId,startTime:this.currentCodon.startTime,codonCost:0,codonTokens:{inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0}},this.logger.log(`Started codon ${e} with session ID: ${t.session_id}`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Started codon ${e} with session ID: ${t.session_id}`}})}else if(t.subtype==="init")this.logger.log(`[handleSystemMessage] Init message for codon ${e} did NOT meet all conditions - skipping transition`,"info")}handleAssistantMessage(t,e){if(this.currentRunId){let l=this.stateManager.getCodonInCurrentRun(lt(e)),i=l&&"assistantMessageCount"in l?l.assistantMessageCount??0:0;this.stateManager.transition({type:"AssistantMessageCountUpdated",data:{runId:this.currentRunId,codonId:lt(e),newCount:i+1}})}if(kp(t)){this.logger.log(`API timeout detected in synthetic message for codon ${e}`,"error");let l=new T2(e,{message:"API Error: Request timed out.",timestamp:new Date().toISOString(),synthetic:!0});if(this.codonFailureReason={type:"timeout",retriable:!0,message:"API Error: Request timed out."},this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:l.message,codon:e,fatal:!1,severity:l.severity,context:JSON.stringify(l.context)}}),this.currentCodon){let i=this.codonRunners.get(this.currentCodon.codonId);if(i)i.kill()}return}if(t.message.usage){let l={inputTokens:t.message.usage.input_tokens||0,outputTokens:t.message.usage.output_tokens||0,cacheCreationTokens:t.message.usage.cache_creation_input_tokens||0,cacheReadTokens:t.message.usage.cache_read_input_tokens||0},i=0,o=this.currentCodon?.codon.model.modelId;if(o){let d=this.llmRegistry.calculateCost(o,{inputTokens:l.inputTokens,outputTokens:l.outputTokens,cacheReadTokens:l.cacheReadTokens,cacheCreationTokens:l.cacheCreationTokens});if(d!==null)i=d;else this.logger.log(`Cannot calculate incremental cost for model: ${o}`,"debug")}else this.logger.log("Cannot calculate incremental cost: no model ID in current codon","debug");if(this.currentCodon&&this.currentCodon.status==="running")this.currentCodon.codonCost+=i,this.currentCodon.codonTokens.inputTokens+=l.inputTokens,this.currentCodon.codonTokens.outputTokens+=l.outputTokens,this.currentCodon.codonTokens.cacheCreationTokens+=l.cacheCreationTokens,this.currentCodon.codonTokens.cacheReadTokens+=l.cacheReadTokens;if(this.currentRunId)this.stateManager.transition({type:"CostsIncremented",data:{runId:this.currentRunId,codonId:lt(e),costDelta:i,tokensDelta:l}});this.logger.log(`Codon ${e} token update - Call cost: $${i.toFixed(4)}, Running total: $${this.currentCodon?.codonCost.toFixed(4)||0} (${l.inputTokens} in, ${l.outputTokens} out, ${l.cacheCreationTokens} cache create, ${l.cacheReadTokens} cache read)`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"token.usage",data:{codonId:e,...l,totalCost:i,modelId:this.currentCodon?.codon.model.modelId}})}let u=t.message.content,a=Array.isArray(u)?u:[{type:"text",text:u}];for(let l of a)if("text"in l&&l.type==="text"){let i=l;if(i.text==="API Error: Request timed out."){this.logger.log(`API timeout detected in codon ${e}`,"error");let o=new T2(e,{message:i.text,timestamp:new Date().toISOString()});if(this.codonFailureReason={type:"timeout",retriable:!0,message:"API Error: Request timed out."},this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:o.message,codon:e,fatal:!1,severity:o.severity,context:JSON.stringify(o.context)}}),this.currentCodon){let d=this.codonRunners.get(this.currentCodon.codonId);if(d)d.kill()}return}this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"assistant.action",data:{codonId:e,action:"message",content:i.text}})}else if("thinking"in l&&l.type==="thinking"){let i=l;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"assistant.action",data:{codonId:e,action:"thinking",content:i.thinking}})}else if(l.type==="tool_use"){let i=l;if(this.pendingToolUses.set(i.id,{toolName:i.name,timestamp:Date.now(),codonId:e}),["Read","Write","Edit","MultiEdit"].includes(i.name))this.handleFileToolCall(i.name,i.input).catch((d)=>{this.handleError(Wt(d),`handleFileToolCall(${i.name})`,"operation")});this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"assistant.action",data:{codonId:e,action:"tool_use",content:"",toolName:i.name,toolInput:i.input}})}}handleResultMessage(t,e){if(this.logger.log(`Codon ${e} result message received: ${t.subtype}`),this.resultMessageReceived=!0,t.result==="API Error: Request timed out."&&t.is_error){this.logger.log(`API timeout detected in result message for codon ${e}`,"error");let u=new T2(e,{message:t.result,timestamp:new Date().toISOString(),is_error:t.is_error,duration_ms:t.duration_ms,duration_api_ms:t.duration_api_ms});this.codonFailureReason={type:"timeout",retriable:!0,message:"API Error: Request timed out."},this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:u.message,codon:e,fatal:!1,severity:u.severity,context:JSON.stringify(u.context)}})}if(t.subtype==="success"){if(this.logger.log(`Codon ${e} completed successfully`),t.usage&&this.currentRunId){let u={inputTokens:t.usage.input_tokens||0,outputTokens:t.usage.output_tokens||0,cacheCreationTokens:t.usage.cache_creation_input_tokens||0,cacheReadTokens:t.usage.cache_read_input_tokens||0},a=t.total_cost_usd;if(a===void 0){let i=this.currentCodon?.codon.model.modelId;if(i){let o=this.llmRegistry.calculateCost(i,{inputTokens:u.inputTokens,outputTokens:u.outputTokens,cacheReadTokens:u.cacheReadTokens,cacheCreationTokens:u.cacheCreationTokens});if(o!==null)a=o;else{let d=this.currentCodon?.codonCost||0;this.logger.log(`Cannot calculate final cost for model: ${i}, using accumulated cost: $${d.toFixed(4)}`,"debug"),a=d}}else{let o=this.currentCodon?.codonCost||0;this.logger.log(`Cannot calculate final cost: no model ID, using accumulated cost: $${o.toFixed(4)}`,"debug"),a=o}}let l=this.currentCodon?.codonCost||0;if(Math.abs(l-a)>0.0001)this.logger.log(`Codon ${e} cost discrepancy - Accumulated: $${l.toFixed(4)}, Final: $${a.toFixed(4)} (using final from result message)`);this.stateManager.transition({type:"CodonFinalCostSet",data:{runId:this.currentRunId,codonId:lt(e),finalCost:a,finalTokens:u}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"token.usage",data:{codonId:e,...u,totalCost:a,...t.modelUsage?{modelUsage:t.modelUsage}:{},...!t.modelUsage?{modelId:this.currentCodon?.codon.model.modelId}:{}}})}}}handleUserMessage(t,e){let u=t.message.content,a=Array.isArray(u)?u:[];for(let l of a)if(l.type==="tool_result"){let i=l,o=this.pendingToolUses.get(i.tool_use_id);if(!o){this.logger.log(`Tool result without matching tool use: ${i.tool_use_id}`,"info");continue}let d=Date.now()-o.timestamp,m="",r=!1;if(typeof i.content==="string")m=i.content;else if(Array.isArray(i.content))m=i.content.filter((s)=>s.type==="text").map((s)=>s.text).join(`
|
|
289
|
-
`);else if(i.content&&typeof i.content==="object"){if("is_error"in i.content)r=i.content.is_error===!0;m=JSON.stringify(i.content,null,2)}let n=m.length,c=this.config.toolResultTruncateLength,_=m.length>c;if(_)m=`${m.substring(0,c)}...`;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"tool.result",data:{codonId:o.codonId,toolUseId:i.tool_use_id,toolName:o.toolName,result:m,truncated:_,originalLength:n,executionTimeMs:d,isError:r}}),this.pendingToolUses.delete(i.tool_use_id)}}handleExtension(t,e,u){let a=this.stateManager.getCodonInCurrentRun(t);if(!a||a.status!=="running"){this.logger.log(`Cannot handle extension for ${t}: not in running state`,"error");return}if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.extended",data:{codonId:t,codonName:e.name,extensionNumber:u.extensionNumber,exhaustWithPrompt:u.exhaustWithPrompt,cumulativeTokens:a.currentTokens,cumulativeCost:a.currentCost}}),this.logger.log(`Codon ${t} extending (extension #${u.extensionNumber})`,"info"),this.currentRunId)this.stateManager.transition({type:"ExtensionCountUpdated",data:{runId:this.currentRunId,codonId:t,extensionCount:u.extensionNumber}})}async handleCodonComplete(t,e,u){this.logger.log(`[handleCodonComplete] ======= ENTERED handleCodonComplete - exitCode=${t}, isContextExceeded=${e}, extensionCount=${u} =======`,"info");let a=this.currentCodon?!!this.codonRunners.get(this.currentCodon.codonId):!1;if(this.logger.log(`[handleCodonComplete] currentCodon=${this.currentCodon?.codonId||"none"}, hasRunner=${a}`,"info"),!this.currentCodon){this.logger.log("[handleCodonComplete] No currentCodon, returning early","info");return}let l=this.currentCodon.codonId,i=this.currentCodon.codon,o=this.isSkippingCodon,d=this.stateManager.getCodonInCurrentRun(lt(l));if(!d||Rt(d.status))return;let m=d.status;await new Promise((G)=>setTimeout(G,this.config.logParsingInterval*2));let r=this.stateManager.getCodonInCurrentRun(lt(l));if(!r)return;let n=this.currentCodonSentinels.size;if(n>0&&this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:l,from:r.status,to:"completing-sentinels",metadata:{sentinelCount:n,sentinelIds:Array.from(this.currentCodonSentinels)}}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Completing work for ${n} sentinel(s)...`}}),await this.stateManager.waitForPendingTransitions();if(this.sentinelManager&&n>0)await this.sentinelManager.completeAllWork(),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Sentinel work completed (${n} sentinel(s))`}});if(this.currentRunId&&n>0){let G=this.sentinelManager.getSentinelStates(),f=G.reduce((Q,h)=>Q+h.totalCost,0);this.stateManager.transition({type:"SentinelStatesUpdated",data:{runId:this.currentRunId,codonId:l,sentinelStates:G,totalCost:f}}),this.logger.log(`Updated final state for ${G.length} sentinel(s)`,"debug"),await this.stateManager.waitForPendingTransitions()}let c={};if(this.sentinelManager&&this.currentCodonSentinels.size>0){let G=this.sentinelManager.getSentinelCosts();for(let[Q,h]of G)c[Q]=h;let f=Object.values(c).reduce((Q,h)=>Q+h,0);this.logger.log(`Sentinel costs: ${JSON.stringify(c)} (total: $${f.toFixed(6)})`,"info")}let _;if(this.isForceStopping)_="failed";else if(t===0&&this.resultMessageReceived)_="completed";else if(e&&this.stateManager.isContextExceededAcceptable(l))_="completed",this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:"Codon completed successfully due to context exceeded (loop termination condition met)"}});else if(o&&!this.resultMessageReceived)_="skipped";else if(t!==0)_="failed";else _="failed";let s;if(this.checkpointingEnabled)try{let G=_==="completed"?"completed":_==="skipped"?"skipped":"error";s=await this.createCheckpoint({status:G,codonId:l,codonName:this.currentCodon?.codon.name||l,runId:this.currentRunId||u0("unknown"),timestamp:new Date().toISOString(),duration:Date.now()-new Date(d.startTime).getTime()})||void 0}catch(G){if(this.logger.log(`Checkpoint creation failed: ${G}`,"error"),_==="completed")_="failed",this.codonFailureReason={type:"unknown",retriable:!1,message:`Checkpoint creation failed: ${Wt(G).message}`}}let x=n>0?this.stateManager.getCodonInCurrentRun(lt(l)):r;if(this.currentRunId&&x)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:l,from:x.status,to:_,metadata:{exitCode:t,resultMessageReceived:this.resultMessageReceived,checkpointSha:s||"",contextExceeded:e,extensionCount:u,..._==="failed"&&{failedDuring:o?m:r.status,failureReason:this.codonFailureReason||{type:"unknown",retriable:!1}},..._==="skipped"&&{skippedDuring:m}}}});await this.stateManager.waitForPendingTransitions();let w=this.stateManager.getCodonInCurrentRun(lt(l)),g=0;if(w){if(w.status==="completed")g=w.finalCost;else if(w.status==="failed"||w.status==="skipped")g=w.partialCost}let v=this.retryAccumulatedCost.get(l)||0,B=_==="skipped"?0:g+v,z=_==="failed"&&this.currentCodon?.codon.onFailure==="ignore";if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:l,success:_==="completed",cost:B,duration:Date.now()-new Date(d.startTime).getTime(),exitStatus:_==="skipped"?{type:"error",code:t}:t===0?{type:"success"}:{type:"error",code:t},failureReason:_==="failed"?this.codonFailureReason:void 0,failureIgnored:z?!0:void 0}}),await this.sendStateSnapshot(),_==="completed"&&this.currentCodon.codon.outputFiles&&this.config.outputDirectory)for(let[G,f]of this.currentCodon.codon.outputFiles.entries()){let Q=!1;try{if(f.beforeCopy&&f.beforeCopy.length>0){this.logger.log(`Running ${f.beforeCopy.length} beforeCopy command(s) for codon ${this.currentCodon.codon.id} (group ${G+1})`);for(let[V,D]of f.beforeCopy.entries())this.logger.log(`Running beforeCopy command ${V+1}/${f.beforeCopy.length}: ${D.command.run}`),await this.runCommand(D);this.logger.log(`Completed all beforeCopy commands for codon ${this.currentCodon.codon.id} (group ${G+1})`)}Q=!0;let{conflicts:h}=await p_(this.config.executionPath,f.copy,dt.join(this.config.cwd,this.config.outputDirectory),this.logger);if(h.length>0){this.logger.log(`Output file conflicts resolved: ${h.length} file(s) renamed`,"info"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Output file conflicts: ${h.length} file(s) were renamed to avoid overwriting.`,details:h.map((V)=>({original:dt.basename(V.original),resolved:dt.basename(V.resolved)}))}}),console.log(`
|
|
288
|
+
${u.stack}`,"error")}else if(u&&typeof u==="object")try{this.logger.log(`Reason object: ${JSON.stringify(u,null,2)}`,"error")}catch{this.logger.log(`Reason (unstringifiable): ${String(u)}`,"error")}this.shutdown("unhandledRejection")}),this.config.port}updateLockFileWithPort(t,e){try{if(S.existsSync(this.config.lockFile)){let u=JSON.parse(S.readFileSync(this.config.lockFile,"utf-8"));if(u.port=t,e!==void 0)u.proxyPort=e;S.writeFileSync(this.config.lockFile,JSON.stringify(u)),this.logger.log(`Lock file updated with port ${t}${e!==void 0?`, proxy ${e}`:""}`)}}catch(u){this.logger.log(`Failed to update lock file with port: ${u}`,"error")}}handleConnection(t){let e=t.data.id;this.logger.log(`Client ${e} connected`),this.clients.set(e,t),this.logger.log(`Client ${e} waiting for handshake`)}async handleHandshake(t,e){let{mode:u,sendPreviousEvents:a=!1}=e.data,l=t.data.id,i=u;this.logger.log(`Client ${l} granted ${i} access`),t.data={...t.data,id:l,mode:i,handshakeComplete:!0};let{events:o,totalEvents:d,hasMore:m}=a?await this.eventJournal.getMostRecentEvents(this.config.handshakeHistoryLimit):{events:[],totalEvents:await this.eventJournal.getTotalEvents(),hasMore:!1};this.logger.log(`Sending ${o.length} events (of ${d} total) to client ${l}`+(a?" (limited history)":" (no history)")+(m?" with additional history available via download":""));let r={type:"handshake.response",data:{clientId:l,mode:i,eventHistory:o,totalEvents:d}};t.send(JSON.stringify(r)),this.logger.log(`Handshake complete for client ${l} (${i})`);let n={id:b(X()),timestamp:new Date().toISOString(),type:"server.ready",data:{serverVersion:this.config.version,executionPath:this.config.executionPath,agentRootPath:this.config.agentRootPath,dataPath:this.config.dataPathInExecutionDir,port:this.config.port,proxyPort:this.proxyRunner?.getActualPort()??void 0}};if(this.emit("event",n,t),this.logger.log(`[handleHandshake] config.autostart = ${this.config.autostart}`),this.config.autostart)this.logger.log("[handleHandshake] Calling requestAutostart()"),this.requestAutostart().catch((c)=>{this.logger.log(`[handleHandshake] requestAutostart error: ${c}`,"error")});else{let c={id:b(X()),timestamp:new Date().toISOString(),type:"server.idle",data:{reason:"startup",message:"Server ready. Waiting for commands (autostart disabled)."}};this.emit("event",c)}}async handleMessage(t,e){try{t.data.lastActivity=new Date;let u=JSON.parse(e.toString());if(u.type==="handshake"){this.handleHandshake(t,u);return}if(!t.data.handshakeComplete){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Handshake required before sending commands",fatal:!1}},t);return}let a=B8.safeParse(u);if(!a.success){this.logger.log(`Invalid client command: ${a.error.message}`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Invalid command format",fatal:!1}},t);return}await this.handleCommand(a.data,t)}catch(u){let a=Wt(u);if(this.logger.log(`Error handling command: ${a.message}`,"error"),a.stack)this.logger.log(`Stack trace: ${a.stack}`,"error");this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Command execution failed: ${a.message}`,fatal:!1}},t)}}handleClose(t){let e=t.data.id;this.logger.log(`Client ${e} disconnected`),this.clients.delete(e)}async handleCommand(t,e){if(this.logger.log(`Handling command: ${t.type}`),this.isRollingBack&&!this.READ_ONLY_COMMANDS.has(t.type)){this.logger.log(`Client ${e.data.id} attempted state-modifying command while rollback is in progress`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot execute state-modifying commands while rollback is in progress",context:`Attempted command: ${t.type}`,codon:this.currentCodon?.codon.id,fatal:!1,severity:"operation",code:"ROLLBACK_IN_PROGRESS"}},e);return}if(!this.READ_ONLY_COMMANDS.has(t.type)){if(!e.data.handshakeComplete){this.logger.log(`Client ${e.data.id} attempted state-modifying command without handshake`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot execute state-modifying commands without handshake",context:`Attempted command: ${t.type}`,codon:this.currentCodon?.codon.id,fatal:!1,severity:"operation",code:"HANDSHAKE_REQUIRED"}},e);return}if(e.data.mode==="readonly"){this.logger.log(`Client ${e.data.id} attempted state-modifying command in read-only mode`,"error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot execute state-modifying commands in read-only mode",context:`Attempted command: ${t.type}`,codon:this.currentCodon?.codon.id,fatal:!1,severity:"operation",code:"INSUFFICIENT_PERMISSIONS"}},e);return}}switch(t.type){case"codon.start":{await this.startCodon(t.data.codonId,t.data.skipPreCommands);break}case"codon.next":await this.startNextCodon();break;case"codon.skip":await this.skipCurrentCodon();break;case"codon.redo":await this.redoCurrentCodon();break;case"server.shutdown":await this.shutdown(t.data?.reason||"client request");break;case"checkpoint.list":await this.listCheckpoints(t.data?.runId);break;case"codon.forceStop":await this.forceStopCodon(t.data?.reason);break;case"rollback.toCheckpoint":await this.rollbackToCheckpoint(t.data.checkpointSha,t.data.autoRestart??!1);break;case"rollback.toCodon":await this.rollbackToCodon(t.data.codonId,t.data.checkpointType,t.data.autoRestart??!1);break;case"rollback.toLastSuccess":await this.rollbackToLastSuccess(t.data?.autoRestart??!1);break;case"ping":this.handlePing(t.id,e);break;case"ping.broadcast":this.handlePingBroadcast(t.id,e);break;case"history.sync":await this.handleHistorySync(t,e);break;default:i_(t)}}handlePing(t,e){if(this.logger.log(`Handling ping command: ${t}`),e?.data.handshakeComplete){let u={id:b(X()),timestamp:new Date().toISOString(),type:"pong",data:{message:"pong",timestamp:new Date().toISOString()}};this.emit("event",u,e)}else this.logger.log("Ping command received but no valid sender provided","error")}handlePingBroadcast(t,e){this.logger.log(`Handling ping.broadcast command: ${t}`);let u=e?.data.id||"unknown";for(let[a,l]of this.clients){if(!l.data.handshakeComplete)continue;let i={id:b(X()),timestamp:new Date().toISOString(),type:"pong",data:{message:"pong",timestamp:new Date().toISOString(),clientId:u}};this.emit("event",i,l)}}async handleHistorySync(t,e){if(!e?.data.handshakeComplete){this.logger.log("History sync command received but sender not ready","error");return}this.logger.log(`Handling history.sync command: ${t.id}`);let u=e;if(!u){this.logger.log("History sync command received without sender","error");return}let a=this.eventJournal.getAllEvents()[Symbol.asyncIterator](),l=await a.next();if(l.done){this.sendHistoryBatch(u,[],!1);return}let i=l.value;while(!0){if(l=await a.next(),l.done){this.sendHistoryBatch(u,[i],!1);break}this.sendHistoryBatch(u,[i],!0),i=l.value}}sendHistoryBatch(t,e,u){let a={id:b(X()),timestamp:new Date().toISOString(),type:"history.batch",data:{events:e,hasMore:u}};try{t.send(JSON.stringify(a)),this.logger.log(`Sent ${e.length} events to client ${t.data.id}`)}catch(l){this.logger.log(`Failed to send history batch to client ${t.data.id}: ${l}`,"error"),this.clients.delete(t.data.id)}}emit(t,e,u){if(t!=="event")throw this.logger.log(`Unsupported event type emitted: ${t}`,"error"),Error(`Unsupported event type: ${t}. Can only emit "event" events.`);let a=e,l=v2(a),i=k2(a),o=Ra(a),d=_4(a);if(!l&&!i&&!o&&!d)throw this.logger.log(`Unknown event type: ${a.type}`,"error"),Error(`Unknown event: ${a.type}`);if(d&&!u)return this.logger.log(`Connection state event ${a.type} requires a target client but none provided`,"error"),!1;if((l||i||o)&&!u){if(this.eventJournalAppendQueue=this.eventJournalAppendQueue.then(()=>this.eventJournal.append(a)).catch((m)=>{this.logger.log(`Error appending event to journal: ${m}`,"error")}),this.clients.size>0)for(let[m,r]of this.clients){if(!r.data.handshakeComplete)continue;try{r.send(JSON.stringify(a))}catch(n){this.logger.log(`Failed to send event to client ${r.data.id}: ${n}`,"error")}}}else try{u?.send(JSON.stringify(a))}catch(m){this.logger.log(`Failed to send event to client ${u?.data.id}: ${m}`,"error")}return super.emit(t,e)}async sendStateSnapshot(){let t=this.stateManager.getTotalCost(),e=this.serverStartTime?Date.now()-this.serverStartTime.getTime():0,u=await this.getTerminalCodonsForSnapshot(),a=this.stateManager.getCurrentlyRunningCodon(),l={id:b(X()),timestamp:new Date().toISOString(),type:"state.snapshot",data:{currentCodon:a||void 0,completedCodons:u,fileTree:[],totalCost:t,totalTime:e,recentFileAccess:this.recentFileAccess,isRollingBack:this.isRollingBack}};this.emit("event",l)}async getTerminalCodonsForSnapshot(){return(await this.stateManager.getExecutionThread()).codons.filter((e)=>Rt(e.codon.status)).map((e)=>e.codon)}async startNewRun(t){let e=u0(`${Date.now()}-${Math.random().toString(36).substring(2,7)}`),u=dt.join(this.config.executionPath,".hankweave","runs",e);await S.promises.mkdir(u,{recursive:!0}),this.stateManager.transition({type:"RunStarted",data:{runId:e,runFolder:u,gitBranch:`run-${e}`,startingConditions:t||{type:"fresh"},serverPid:process.pid}}),this.currentRunId=e;let a={pid:process.pid,runId:e,startTime:new Date().toISOString(),lastHeartbeat:new Date().toISOString(),port:this.config.port},l=dt.dirname(this.config.lockFile);if(!S.existsSync(l))S.mkdirSync(l,{recursive:!0});S.writeFileSync(this.config.lockFile,JSON.stringify(a)),this.heartbeatInterval=setInterval(()=>{this.updateHeartbeat()},30000),this.logger.log(`Started new run: ${e}`)}updateHeartbeat(){try{if(S.existsSync(this.config.lockFile)){let t=JSON.parse(S.readFileSync(this.config.lockFile,"utf-8"));t.lastHeartbeat=new Date().toISOString(),S.writeFileSync(this.config.lockFile,JSON.stringify(t))}}catch(t){this.logger.log(`Failed to update heartbeat: ${t}`,"error")}}async startCodon(t,e,u){let a=this.stateManager.getCodonById(t);if(!a){await this.handleError(Error(`Unknown codon: ${t}`),"startCodon","operation");return}let l=a.codon;this.logger.log(`Starting codon: ${l.name}`);let i=await this.stateManager.getCodonHistory(lt(l.id)),o;for(let s of i)if("rigSetupCheckpoint"in s.codon&&s.codon.rigSetupCheckpoint){o=s.codon.rigSetupCheckpoint;break}if(o)this.logger.log(`Found existing rig setup checkpoint: ${o}`);let d=this.stateManager.getCurrentlyRunningCodon();if(d&&!Rt(d.status)){await this.handleError(Error(`Codon already running: ${d.codonId}`),"startCodon","operation");return}let m=this.stateManager.getCurrentRun();if(m&&!u){let s=m.codons.find((x)=>x.codonId===t);if(s&&Rt(s.status))this.logger.log(`Codon ${t} was already attempted in current run, starting new run`),this.stateManager.transition({type:"RunCompleted",data:{runId:m.runId}}),await this.stateManager.waitForPendingTransitions(),await this.startNewRun({type:"continuation",source:{runId:m.runId,afterCodon:null,checkpointSha:""},reason:"retry"})}if(!this.currentRunId){await this.handleError(Error("No active run"),"startCodon","fatal");return}if(this.stateManager.transition({type:"CodonStarted",data:{runId:this.currentRunId,codonId:t,loopContext:a.loopContext}}),!this.currentRunId){await this.handleError(Error("No active run during codon start"),"startCodon","fatal");return}if(!e&&!o&&l.rigSetup&&l.rigSetup.length>0){let s=l.rigSetup.length,x=Date.now(),w=0,g=0;this.logger.log(`Running rig setup for codon: ${l.name}`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Rig setup started for codon '${l.name}': ${s} operation${s!==1?"s":""}`}});let v=null;for(let[z,k]of l.rigSetup.entries()){let G=z+1,f=k.type,Q=k.type==="copy"&&k.copy?`${k.copy.from} → ${k.copy.to}`:k.type==="command"&&k.command?`'${k.command.run}'`:"unknown";this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Rig operation ${G}/${s}: ${f} ${Q}`}});try{if(k.type==="copy"&&k.copy){let h=dt.join(this.config.agentRootPath,k.copy.to);if(this.logger.log(`Copying ${k.copy.from} to ${h}`),S.existsSync(h))this.logger.log(`Warning: Target path already exists: ${h}. Removing it before copying.`),await S.promises.rm(h,{recursive:!0}),this.logger.log(`Removed existing path: ${h}`);await this.copyPath(k.copy.from,h),v=h,this.logger.log(`Copied ${k.copy.from} to ${h}`)}else if(k.type==="command"&&k.command){await this.runCommand(k,v||void 0,l.env);let h=k.command.workingDirectory==="lastCopied"&&v?v:this.config.agentRootPath;this.logger.log(`Ran command in ${h}: ${k.command.run}`)}w++}catch(h){let D=Wt(h).message,$=h instanceof ti,J=$?h.exitCode:-1,T=$?h.stdout:"",y=$?h.stderr:"";if(this.logger.log(`[DEBUG] Rig setup error details - Exit code: ${J}`,"error"),T)this.logger.log(`[DEBUG] Rig setup error stdout: ${T}`,"info");if(y)this.logger.log(`[DEBUG] Rig setup error stderr: ${y}`,"error");if(k.allowFailure||(this.config.ignoreRigFailures??!1)){let vt=k.allowFailure?"allowFailure=true":"--ignore-rig-failures";this.logger.log(`Rig setup operation failed (${vt}) at item ${z+1} (${JSON.stringify(k)}): ${D}`,"info"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Rig setup operation failed but continuing (${vt}): ${D}`,context:`Codon ${l.id} - ${k.type} operation (item ${z+1})`,codon:l.id,fatal:!1,severity:"operation"}}),g++;continue}if(this.logger.log(`Rig setup failed at item ${z+1} (${JSON.stringify(k)}): ${D}`,"error"),this.codonFailureReason={type:"unknown",retriable:!0,message:`Rig setup failed at ${k.type} operation: ${D}`},this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"preparing",to:"failed",metadata:{exitCode:J,failedDuring:"preparing",failureReason:this.codonFailureReason}}});this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Rig setup failed: ${D}`,context:`Codon ${l.id} - ${k.type} operation (item ${z+1})`,codon:l.id,fatal:!0,severity:"fatal"}});let K=this.resolveFailurePolicy(t,l,this.codonFailureReason);if(K==="continue"){if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:t,success:!1,cost:0,duration:Date.now()-(this.currentCodon?.startTime?.getTime()||Date.now()),exitStatus:{type:"error",code:J},failureReason:this.codonFailureReason,failureIgnored:!0}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${t} rig setup failed, continuing (onFailure=ignore)`}}),this.cleanupCurrentCodon(),await this.stateManager.waitForPendingTransitions(),await this.stateManager.expandNextIterationForCodon({codonId:lt(t),contextExceeded:!1}),this.config.autostart)await this.autoStartNextCodon();return}if(K==="retry"){let vt=this.retryAttempts.get(t)||0,tt=l.retryConfig?.maxAttempts??3,At=l.retryConfig?.delayMs??1000;if(vt<tt){if(this.retryAttempts.set(t,vt+1),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Retrying codon ${t} after rig setup failure (attempt ${vt+1}/${tt})`}}),this.cleanupCurrentCodon(),await this.delay(At),this.isShuttingDown){this.logger.log(`Server shutting down, skipping retry for ${t}`);return}await this.startCodon(t,!1,!0);return}}this.cleanupCurrentCodon(),await this.handleError(Wt(h),`Rig setup item ${z+1}`,"fatal");return}}let B=Date.now()-x;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Rig setup completed for codon '${l.name}' (${B}ms, ${w} succeeded${g>0?`, ${g} failed`:""})`}})}this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"preparing",to:"starting",metadata:{checkpointSha:o}}});let r=await this.loadSentinelsForCodon(l,t),n=r.errors.filter((s)=>s.fatal);if(n.length>0){let s=n.map((g)=>g.ref).join(", "),x=`Required sentinels failed to load (failCodonIfNotLoaded=true): ${s}`;this.codonFailureReason={type:"sentinel-load-failure",retriable:!1,message:x,sentinelRefs:n.map((g)=>g.ref)},this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:lt(l.id),from:"starting",to:"failed",metadata:{failedDuring:"starting",failureReason:this.codonFailureReason}}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:x,context:`Failed sentinels: ${s}`,codon:l.id,fatal:!0,severity:"codon",code:"SENTINEL_LOAD_FAILURE"}});let w=this.resolveFailurePolicy(t,l,this.codonFailureReason);if(w==="continue"){if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:t,success:!1,cost:0,duration:Date.now()-(this.currentCodon?.startTime?.getTime()||Date.now()),exitStatus:{type:"error",code:-1},failureReason:this.codonFailureReason,failureIgnored:!0}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${t} sentinel load failed, continuing (onFailure=ignore)`}}),this.cleanupCurrentCodon(),await this.stateManager.waitForPendingTransitions(),await this.stateManager.expandNextIterationForCodon({codonId:lt(t),contextExceeded:!1}),this.config.autostart)await this.autoStartNextCodon();return}if(this.cleanupCurrentCodon(),w==="shutdown"){if(this.currentRunId)this.stateManager.transition({type:"RunFailed",data:{runId:this.currentRunId}}),await this.stateManager.waitForPendingTransitions();await this.shutdown("sentinel load failure")}return}for(let s of r.errors.filter((x)=>!x.fatal))this.logger.log(`Non-required sentinel failed to load (${s.ref}): ${s.error}`,"info");let c=this.config.codons.findIndex((s)=>s.id===l.id);if(c>=0){for(let s=0;s<=c;s++){let x=this.config.codons[s];if(x.type!=="loop"&&x.checkpointedFiles&&x.checkpointedFiles.length>0)await this.addCheckpointPatterns(x.checkpointedFiles)}if(!e&&l.rigSetup&&this.checkpointingEnabled)await this.createCheckpoint({status:"rig-setup",codonId:t,codonName:l.name,runId:this.currentRunId||u0("unknown"),timestamp:new Date().toISOString()})}let _=null;if(l.continuationMode==="continue-previous"){let s=this.stateManager.getState(),x=await g0(s,void 0,void 0,this.logger);if(_=G8(x,t,s),_)this.logger.log(`Codon ${t} will continue from previous session: ${_}`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Continuing from previous session: ${_}`}});else{let g=`Codon ${t} requires continuation from previous codon but no valid session found. Previous codon must complete successfully or be skipped with at least one assistant message.`;if(this.logger.log(g,"error"),this.codonFailureReason={type:"unknown",retriable:!1,message:g},this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"starting",to:"failed",metadata:{failedDuring:"starting",failureReason:this.codonFailureReason}}});this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:g,codon:l.id,fatal:!0,severity:"codon"}});let v=this.resolveFailurePolicy(t,l,this.codonFailureReason);if(v==="continue"){if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:t,success:!1,cost:0,duration:Date.now()-(this.currentCodon?.startTime?.getTime()||Date.now()),exitStatus:{type:"error",code:-1},failureReason:this.codonFailureReason,failureIgnored:!0}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${t} continuation session not found, continuing (onFailure=ignore)`}}),this.cleanupCurrentCodon(),await this.stateManager.waitForPendingTransitions(),await this.stateManager.expandNextIterationForCodon({codonId:lt(t),contextExceeded:!1}),this.config.autostart)await this.autoStartNextCodon();return}if(this.cleanupCurrentCodon(),v==="shutdown"){if(this.currentRunId)this.stateManager.transition({type:"RunFailed",data:{runId:this.currentRunId}}),await this.stateManager.waitForPendingTransitions();await this.shutdown("continuation session not found")}return}}if(this.currentCodon={status:"initializing",codonId:t,codon:l,previousSessionId:_?Xu(_):void 0,startTime:new Date,codonCost:0,codonTokens:{inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0}},l.checkpointedFiles&&l.checkpointedFiles.length>0)this.watchedPatterns=l.checkpointedFiles,this.logger.log(`Watching patterns: ${this.watchedPatterns.join(", ")}`);if(l.checkpointedFiles&&l.checkpointedFiles.length>0){let s=await r0.resolveFiles(this.config.agentRootPath,l.checkpointedFiles),x=await Promise.all(s.map(async(w)=>{let g=dt.join(this.config.agentRootPath,w),v=await S.promises.stat(g),B=await S.promises.readFile(g,"utf-8");return{path:w,content:B,lastModified:v.mtime.toISOString()}}));if(x.length>0){for(let g of x)this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"file.updated",data:{path:g.path,filename:dt.basename(g.path),content:g.content,action:"created"}});let w=x.reduce((g,v)=>new Date(v.lastModified)>new Date(g.lastModified)?v:g);this.recentFileAccess={path:w.path,content:w.content,timestamp:new Date(w.lastModified)},await this.sendFileTreeUpdate()}}await this.runCodon(t,l,_)}async runCodon(t,e,u){try{let a=this.stateManager.getCurrentRun();if(!a||!a.runFolder)throw Error("No active run or run folder not found");let l=a.runFolder;await S.promises.mkdir(l,{recursive:!0});let i=`${t.replace(/#/g,"-")}-claude.log`,o=dt.join(l,i),d={codon:e,codonId:t,executionPath:this.config.executionPath,agentRootPath:this.config.agentRootPath,logger:this.logger,logParsingInterval:this.config.logParsingInterval,anthropicBaseUrl:this.proxyRunner?.proxyUrl,logPath:o,globalSystemPrompt:this.config.globalSystemPrompt},m=e.exhaustWithPrompt?new x0({...d,extensionConfig:{exhaustWithPrompt:e.exhaustWithPrompt,maxExtensions:e.maxExtensions??100},shouldInterrupt:()=>this.isSkippingCodon||this.isForceStopping,onExtension:(n)=>{this.handleExtension(t,e,n)}}):new x0(d);if(this.codonRunners.set(t,m),this.logger.log(`[runCodon] Setting up event handlers for codon ${t}`,"debug"),this.setupCodonRunnerEventHandlers(t),this.logger.log(`[runCodon] Starting runner execution for codon ${t}, previousSessionId: ${u||"none"}`,"debug"),await m.run(u?Xu(u):void 0),this.logger.log(`[runCodon] Runner.run() completed for codon ${t}`,"debug"),this.logger.log(`[runCodon] Validating process started for codon ${t}`,"debug"),!this.currentRunId)throw Error("No active run while starting Claude process");let r=m.getPid();if(this.logger.log(`[runCodon] Got PID ${r} for codon ${t}`,"debug"),!r)throw Error("Failed to get process PID");this.logger.log(`[runCodon] Transitioning codon ${t} to initializing (PID: ${r})`,"debug"),this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"starting",to:"initializing",metadata:{claudePid:r,claudeLogPath:dt.relative(this.config.executionPath,o),...u&&{previousSessionId:Xu(u)}}}}),this.logger.log(`[runCodon] Successfully completed runCodon for ${t}, status should be initializing`,"debug")}catch(a){this.logger.log(`[runCodon] CAUGHT ERROR during codon ${t} initialization: ${Wt(a).message}`,"error"),this.logger.log(`[runCodon] Error stack: ${Wt(a).stack}`,"error");let l=this.codonRunners.get(t);if(this.logger.log(`[runCodon] State at error - currentRunId: ${this.currentRunId}, hasRunner: ${!!l}`,"error"),l)this.logger.log(`[runCodon] Calling cleanup on runner for codon ${t} due to error`,"error"),await l.cleanup(),this.codonRunners.delete(t),this.logger.log("[runCodon] CodonRunner cleanup complete, removed from map","error");if(this.currentRunId)this.logger.log(`[runCodon] Transitioning codon ${t} to failed due to error`,"error"),this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:t,from:"starting",to:"failed",metadata:{failedDuring:"starting",failureReason:{type:"unknown",retriable:!1,message:Wt(a).message}}}});throw this.logger.log("[runCodon] Calling cleanupCurrentCodon()","error"),this.cleanupCurrentCodon(),this.logger.log("[runCodon] Re-throwing error","error"),a}}setupCodonRunnerEventHandlers(t){let e=this.codonRunners.get(t);if(!e)throw Error(`Cannot setup handlers: no runner found for codon ${t}`);e.on("exit",(u,a,l)=>{if(a)this.logger.log(`[HANKWEAVE-SERVER] Context exceeded error detected for codon ${t}`,"error");this.handleCodonComplete(u,a,l)}),e.on("error",(u)=>{this.handleError(u,`Process for codon ${t}`,"fatal")}),e.on("systemMessage",(u)=>{this.handleSystemMessage(u,t)}),e.on("assistantMessage",(u)=>{this.handleAssistantMessage(u,t)}),e.on("userMessage",(u)=>{this.handleUserMessage(u,t)}),e.on("resultMessage",(u)=>{this.handleResultMessage(u,t)})}handleSystemMessage(t,e){if(t.subtype==="init")this.logger.log(`[handleSystemMessage] Received init message for codon ${e}, session: ${t.session_id}`,"debug"),this.logger.log(`[handleSystemMessage] Condition check - subtype=init: true, has_session: ${!!t.session_id}, has_currentCodon: ${!!this.currentCodon}, currentCodon_status: ${this.currentCodon?.status||"N/A"}`,"debug");if(t.subtype==="init"&&t.session_id&&this.currentCodon&&this.currentCodon.status==="initializing"){if(this.logger.log(`[handleSystemMessage] All conditions met, transitioning codon ${e} to running`,"info"),this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:lt(e),from:"initializing",to:"running",metadata:{claudeSessionId:Xu(t.session_id)}}});this.currentCodon={status:"running",codonId:this.currentCodon.codonId,codon:this.currentCodon.codon,sessionId:Xu(t.session_id),previousSessionId:this.currentCodon.previousSessionId,startTime:this.currentCodon.startTime,codonCost:0,codonTokens:{inputTokens:0,outputTokens:0,cacheCreationTokens:0,cacheReadTokens:0}},this.logger.log(`Started codon ${e} with session ID: ${t.session_id}`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Started codon ${e} with session ID: ${t.session_id}`}})}else if(t.subtype==="init")this.logger.log(`[handleSystemMessage] Init message for codon ${e} did NOT meet all conditions - skipping transition`,"info")}handleAssistantMessage(t,e){if(this.currentRunId){let l=this.stateManager.getCodonInCurrentRun(lt(e)),i=l&&"assistantMessageCount"in l?l.assistantMessageCount??0:0;this.stateManager.transition({type:"AssistantMessageCountUpdated",data:{runId:this.currentRunId,codonId:lt(e),newCount:i+1}})}if(kp(t)){this.logger.log(`API timeout detected in synthetic message for codon ${e}`,"error");let l=new T2(e,{message:"API Error: Request timed out.",timestamp:new Date().toISOString(),synthetic:!0});if(this.codonFailureReason={type:"timeout",retriable:!0,message:"API Error: Request timed out."},this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:l.message,codon:e,fatal:!1,severity:l.severity,context:JSON.stringify(l.context)}}),this.currentCodon){let i=this.codonRunners.get(this.currentCodon.codonId);if(i)i.kill()}return}if(t.message.usage){let l={inputTokens:t.message.usage.input_tokens||0,outputTokens:t.message.usage.output_tokens||0,cacheCreationTokens:t.message.usage.cache_creation_input_tokens||0,cacheReadTokens:t.message.usage.cache_read_input_tokens||0},i=0,o=this.currentCodon?.codon.model.modelId;if(o){let d=this.llmRegistry.calculateCost(o,{inputTokens:l.inputTokens,outputTokens:l.outputTokens,cacheReadTokens:l.cacheReadTokens,cacheCreationTokens:l.cacheCreationTokens});if(d!==null)i=d;else this.logger.log(`Cannot calculate incremental cost for model: ${o}`,"debug")}else this.logger.log("Cannot calculate incremental cost: no model ID in current codon","debug");if(this.currentCodon&&this.currentCodon.status==="running")this.currentCodon.codonCost+=i,this.currentCodon.codonTokens.inputTokens+=l.inputTokens,this.currentCodon.codonTokens.outputTokens+=l.outputTokens,this.currentCodon.codonTokens.cacheCreationTokens+=l.cacheCreationTokens,this.currentCodon.codonTokens.cacheReadTokens+=l.cacheReadTokens;if(this.currentRunId)this.stateManager.transition({type:"CostsIncremented",data:{runId:this.currentRunId,codonId:lt(e),costDelta:i,tokensDelta:l}});this.logger.log(`Codon ${e} token update - Call cost: $${i.toFixed(4)}, Running total: $${this.currentCodon?.codonCost.toFixed(4)||0} (${l.inputTokens} in, ${l.outputTokens} out, ${l.cacheCreationTokens} cache create, ${l.cacheReadTokens} cache read)`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"token.usage",data:{codonId:e,...l,totalCost:i,modelId:this.currentCodon?.codon.model.modelId}})}let u=t.message.content,a=Array.isArray(u)?u:[{type:"text",text:u}];for(let l of a)if("text"in l&&l.type==="text"){let i=l;if(i.text==="API Error: Request timed out."){this.logger.log(`API timeout detected in codon ${e}`,"error");let o=new T2(e,{message:i.text,timestamp:new Date().toISOString()});if(this.codonFailureReason={type:"timeout",retriable:!0,message:"API Error: Request timed out."},this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:o.message,codon:e,fatal:!1,severity:o.severity,context:JSON.stringify(o.context)}}),this.currentCodon){let d=this.codonRunners.get(this.currentCodon.codonId);if(d)d.kill()}return}this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"assistant.action",data:{codonId:e,action:"message",content:i.text}})}else if("thinking"in l&&l.type==="thinking"){let i=l;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"assistant.action",data:{codonId:e,action:"thinking",content:i.thinking}})}else if(l.type==="tool_use"){let i=l;if(this.pendingToolUses.set(i.id,{toolName:i.name,timestamp:Date.now(),codonId:e}),["Read","Write","Edit","MultiEdit"].includes(i.name))this.handleFileToolCall(i.name,i.input).catch((d)=>{this.handleError(Wt(d),`handleFileToolCall(${i.name})`,"operation")});this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"assistant.action",data:{codonId:e,action:"tool_use",content:"",toolName:i.name,toolInput:i.input}})}}handleResultMessage(t,e){if(this.logger.log(`Codon ${e} result message received: ${t.subtype}`),this.resultMessageReceived=!0,t.result==="API Error: Request timed out."&&t.is_error){this.logger.log(`API timeout detected in result message for codon ${e}`,"error");let u=new T2(e,{message:t.result,timestamp:new Date().toISOString(),is_error:t.is_error,duration_ms:t.duration_ms,duration_api_ms:t.duration_api_ms});this.codonFailureReason={type:"timeout",retriable:!0,message:"API Error: Request timed out."},this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:u.message,codon:e,fatal:!1,severity:u.severity,context:JSON.stringify(u.context)}})}if(t.subtype==="success"){if(this.logger.log(`Codon ${e} completed successfully`),t.usage&&this.currentRunId){let u={inputTokens:t.usage.input_tokens||0,outputTokens:t.usage.output_tokens||0,cacheCreationTokens:t.usage.cache_creation_input_tokens||0,cacheReadTokens:t.usage.cache_read_input_tokens||0},a=t.total_cost_usd;if(a===void 0){let i=this.currentCodon?.codon.model.modelId;if(i){let o=this.llmRegistry.calculateCost(i,{inputTokens:u.inputTokens,outputTokens:u.outputTokens,cacheReadTokens:u.cacheReadTokens,cacheCreationTokens:u.cacheCreationTokens});if(o!==null)a=o;else{let d=this.currentCodon?.codonCost||0;this.logger.log(`Cannot calculate final cost for model: ${i}, using accumulated cost: $${d.toFixed(4)}`,"debug"),a=d}}else{let o=this.currentCodon?.codonCost||0;this.logger.log(`Cannot calculate final cost: no model ID, using accumulated cost: $${o.toFixed(4)}`,"debug"),a=o}}let l=this.currentCodon?.codonCost||0;if(Math.abs(l-a)>0.0001)this.logger.log(`Codon ${e} cost discrepancy - Accumulated: $${l.toFixed(4)}, Final: $${a.toFixed(4)} (using final from result message)`);this.stateManager.transition({type:"CodonFinalCostSet",data:{runId:this.currentRunId,codonId:lt(e),finalCost:a,finalTokens:u}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"token.usage",data:{codonId:e,...u,totalCost:a,...t.modelUsage?{modelUsage:t.modelUsage}:{},...!t.modelUsage?{modelId:this.currentCodon?.codon.model.modelId}:{}}})}}}handleUserMessage(t,e){let u=t.message.content,a=Array.isArray(u)?u:[];for(let l of a)if(l.type==="tool_result"){let i=l,o=this.pendingToolUses.get(i.tool_use_id);if(!o){this.logger.log(`Tool result without matching tool use: ${i.tool_use_id}`,"info");continue}let d=Date.now()-o.timestamp,m="",r=!1;if(typeof i.content==="string")m=i.content;else if(Array.isArray(i.content))m=i.content.filter((s)=>s.type==="text").map((s)=>s.text).join(`
|
|
289
|
+
`);else if(i.content&&typeof i.content==="object"){if("is_error"in i.content)r=i.content.is_error===!0;m=JSON.stringify(i.content,null,2)}let n=m.length,c=this.config.toolResultTruncateLength,_=m.length>c;if(_)m=`${m.substring(0,c)}...`;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"tool.result",data:{codonId:o.codonId,toolUseId:i.tool_use_id,toolName:o.toolName,result:m,truncated:_,originalLength:n,executionTimeMs:d,isError:r}}),this.pendingToolUses.delete(i.tool_use_id)}}handleExtension(t,e,u){let a=this.stateManager.getCodonInCurrentRun(t);if(!a||a.status!=="running"){this.logger.log(`Cannot handle extension for ${t}: not in running state`,"error");return}if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.extended",data:{codonId:t,codonName:e.name,extensionNumber:u.extensionNumber,exhaustWithPrompt:u.exhaustWithPrompt,cumulativeTokens:a.currentTokens,cumulativeCost:a.currentCost}}),this.logger.log(`Codon ${t} extending (extension #${u.extensionNumber})`,"info"),this.currentRunId)this.stateManager.transition({type:"ExtensionCountUpdated",data:{runId:this.currentRunId,codonId:t,extensionCount:u.extensionNumber}})}async handleCodonComplete(t,e,u){this.logger.log(`[handleCodonComplete] ======= ENTERED handleCodonComplete - exitCode=${t}, isContextExceeded=${e}, extensionCount=${u} =======`,"info");let a=this.currentCodon?!!this.codonRunners.get(this.currentCodon.codonId):!1;if(this.logger.log(`[handleCodonComplete] currentCodon=${this.currentCodon?.codonId||"none"}, hasRunner=${a}`,"info"),!this.currentCodon){this.logger.log("[handleCodonComplete] No currentCodon, returning early","info");return}let l=this.currentCodon.codonId,i=this.currentCodon.codon,o=this.isSkippingCodon,d=this.stateManager.getCodonInCurrentRun(lt(l));if(!d||Rt(d.status))return;let m=d.status;await new Promise((G)=>setTimeout(G,this.config.logParsingInterval*2));let r=this.stateManager.getCodonInCurrentRun(lt(l));if(!r)return;let n=this.currentCodonSentinels.size;if(n>0&&this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:l,from:r.status,to:"completing-sentinels",metadata:{sentinelCount:n,sentinelIds:Array.from(this.currentCodonSentinels)}}}),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Completing work for ${n} sentinel(s)...`}}),await this.stateManager.waitForPendingTransitions();if(this.sentinelManager&&n>0)await this.sentinelManager.completeAllWork(),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Sentinel work completed (${n} sentinel(s))`}});if(this.currentRunId&&n>0){let G=this.sentinelManager.getSentinelStates(),f=G.reduce((Q,h)=>Q+h.totalCost,0);this.stateManager.transition({type:"SentinelStatesUpdated",data:{runId:this.currentRunId,codonId:l,sentinelStates:G,totalCost:f}}),this.logger.log(`Updated final state for ${G.length} sentinel(s)`,"debug"),await this.stateManager.waitForPendingTransitions()}let c={};if(this.sentinelManager&&this.currentCodonSentinels.size>0){let G=this.sentinelManager.getSentinelCosts();for(let[Q,h]of G)c[Q]=h;let f=Object.values(c).reduce((Q,h)=>Q+h,0);this.logger.log(`Sentinel costs: ${JSON.stringify(c)} (total: $${f.toFixed(6)})`,"info")}let _;if(this.isForceStopping)_="failed";else if(t===0&&this.resultMessageReceived)_="completed";else if(e&&this.stateManager.isContextExceededAcceptable(l))_="completed",this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:"Codon completed successfully due to context exceeded (loop termination condition met)"}});else if(o&&!this.resultMessageReceived)_="skipped";else if(t!==0)_="failed";else _="failed";let s;if(this.checkpointingEnabled)try{let G=_==="completed"?"completed":_==="skipped"?"skipped":"error";s=await this.createCheckpoint({status:G,codonId:l,codonName:this.currentCodon?.codon.name||l,runId:this.currentRunId||u0("unknown"),timestamp:new Date().toISOString(),duration:Date.now()-new Date(d.startTime).getTime()})||void 0}catch(G){if(this.logger.log(`Checkpoint creation failed: ${G}`,"error"),_==="completed")_="failed",this.codonFailureReason={type:"unknown",retriable:!1,message:`Checkpoint creation failed: ${Wt(G).message}`}}let x=n>0?this.stateManager.getCodonInCurrentRun(lt(l)):r;if(this.currentRunId&&x)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:l,from:x.status,to:_,metadata:{exitCode:t,resultMessageReceived:this.resultMessageReceived,checkpointSha:s||"",contextExceeded:e,extensionCount:u,..._==="failed"&&{failedDuring:o?m:r.status,failureReason:this.codonFailureReason||{type:"unknown",retriable:!1}},..._==="skipped"&&{skippedDuring:m}}}});await this.stateManager.waitForPendingTransitions();let w=this.stateManager.getCodonInCurrentRun(lt(l)),g=0;if(w){if(w.status==="completed")g=w.finalCost;else if(w.status==="failed"||w.status==="skipped")g=w.partialCost}let v=this.retryAccumulatedCost.get(l)||0,B=_==="skipped"?0:g+v,z=_==="failed"&&this.currentCodon?.codon.onFailure==="ignore";if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"codon.completed",data:{codonId:l,success:_==="completed",cost:B,duration:Date.now()-new Date(d.startTime).getTime(),exitStatus:_==="skipped"?{type:"error",code:t}:t===0?{type:"success"}:{type:"error",code:t},failureReason:_==="failed"?this.codonFailureReason:void 0,failureIgnored:z?!0:void 0}}),await this.sendStateSnapshot(),_==="completed"&&this.currentCodon.codon.outputFiles&&this.config.outputDirectory)for(let[G,f]of this.currentCodon.codon.outputFiles.entries()){let Q=!1;try{if(f.beforeCopy&&f.beforeCopy.length>0){this.logger.log(`Running ${f.beforeCopy.length} beforeCopy command(s) for codon ${this.currentCodon.codon.id} (group ${G+1})`);for(let[V,D]of f.beforeCopy.entries())this.logger.log(`Running beforeCopy command ${V+1}/${f.beforeCopy.length}: ${D.command.run}`),await this.runCommand(D,void 0,this.currentCodon?.codon.env);this.logger.log(`Completed all beforeCopy commands for codon ${this.currentCodon.codon.id} (group ${G+1})`)}Q=!0;let{conflicts:h}=await p_(this.config.executionPath,f.copy,dt.join(this.config.cwd,this.config.outputDirectory),this.logger);if(h.length>0){this.logger.log(`Output file conflicts resolved: ${h.length} file(s) renamed`,"info"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Output file conflicts: ${h.length} file(s) were renamed to avoid overwriting.`,details:h.map((V)=>({original:dt.basename(V.original),resolved:dt.basename(V.resolved)}))}}),console.log(`
|
|
290
290
|
Output files copied to ${this.config.outputDirectory}`),console.log(" Conflicts resolved:");for(let V of h)console.log(` - ${dt.basename(V.original)} → ${dt.basename(V.resolved)}`)}}catch(h){await this.handleError(Error(`Copy group ${G} failed with: ${String(h)}`),Q?"codonOutputCopyFiles":"codonOutputBeforeCopy")}}if(_==="completed"&&this.currentCodon?.codon.archiveOnSuccess){let G=d.loopContext;await this.executeArchiveRigs(this.currentCodon.codon.archiveOnSuccess,l,s||"orphan",G?{loopId:G.loopId,iteration:G.iteration}:void 0,!1)}if(_==="completed"||_==="skipped"){let G=await this.stateManager.expandNextIterationForCodon({codonId:lt(l),contextExceeded:e});if(G.loopTerminated?.archiveOnSuccess?.length){let{loopId:f,archiveOnSuccess:Q,completedIterations:h}=G.loopTerminated;this.logger.log(`Loop '${f}' terminated after ${h} iterations, executing archiveOnSuccess`),await this.executeArchiveRigs(Q,f,s||"orphan",void 0,!0)}}this.logger.log(`[handleCodonComplete] About to cleanup codon ${l} - exitCode=${t}, isContextExceeded=${e}, finalStatus=${_}`,"info"),this.logger.log(`[handleCodonComplete] Stack trace:
|
|
291
291
|
${Error().stack}`,"debug");let k=this.codonRunners.get(l);if(k)this.logger.log(`[handleCodonComplete] Found runner for codon ${l}, cleaning up`,"info"),await k.cleanup().catch((G)=>this.logger.log(`Error cleaning up runner for ${l}: ${G}`,"error")),this.codonRunners.delete(l);else this.logger.log(`[handleCodonComplete] No runner found in map for codon ${l}`,"info");if(this.currentCodon?.codonId===l)this.logger.log(`[handleCodonComplete] Clearing current codon state for ${l}`,"info"),this.cleanupCurrentCodon();else this.logger.log(`[handleCodonComplete] Not clearing current codon state (current is ${this.currentCodon?.codonId||"none"}, completed is ${l})`,"info");if(this.logger.log(`[handleCodonComplete] Cleanup completed for codon ${l}`,"info"),(_==="completed"||_==="skipped")&&!this.isShuttingDown)if(this.retryAttempts.delete(l),this.retryAccumulatedCost.delete(l),this.config.autostart)await this.autoStartNextCodon();else this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"server.idle",data:{reason:"codon-completed",message:`Codon ${l} ${_}. Use 'codon.next' to continue.`}});else if(_==="failed"&&!this.isShuttingDown)switch(this.resolveFailurePolicy(lt(l),i,this.codonFailureReason)){case"shutdown":if(this.currentRunId)this.stateManager.transition({type:"RunFailed",data:{runId:this.currentRunId}}),await this.stateManager.waitForPendingTransitions();await this.shutdown("codon failure");break;case"stay-active":this.logger.log("Codon failed with retriable error. Server remains active.");break;case"retry":{let f=this.retryAttempts.get(l)||0,Q=i.retryConfig?.maxAttempts??3,h=i.retryConfig?.delayMs??1000,V=g,D=(this.retryAccumulatedCost.get(l)||0)+V;if(this.retryAccumulatedCost.set(l,D),this.retryAttempts.set(l,f+1),this.logger.log(`Retry ${f+1}/${Q} for codon ${l} in ${h}ms`),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Retrying codon ${l} (attempt ${f+1}/${Q})`}}),await this.delay(h),this.isShuttingDown){this.logger.log(`Server shutting down, skipping retry for ${l}`);return}await this.startCodon(lt(l),!0,!0);break}case"continue":{if(this.retryAttempts.delete(l),this.retryAccumulatedCost.delete(l),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${l} failed, continuing (onFailure=ignore)`}}),await this.stateManager.expandNextIterationForCodon({codonId:lt(l),contextExceeded:!1}),(await g0(this.stateManager.getState(),void 0,void 0,this.logger)).nextCodonId!==null)if(this.config.autostart)await this.autoStartNextCodon();else this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"server.idle",data:{reason:"codon-completed",message:`Codon ${l} failed (ignored). Use 'codon.next' to continue.`}});else{if(this.currentRunId)this.stateManager.transition({type:"RunCompleted",data:{runId:this.currentRunId}}),await this.stateManager.waitForPendingTransitions();this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"server.idle",data:{reason:"all-codons-completed",message:`Run completed. Last codon ${l} failed (ignored).`}})}break}}}resolveFailurePolicy(t,e,u){let a=e.onFailure||"abort",l=u?.retriable===!0;switch(this.logger.log(`Resolving failure policy for codon ${t}: onFailure=${a}, retriable=${l}`,"info"),a){case"abort":return l?"stay-active":"shutdown";case"retry":{if(!l)return this.logger.log(`Codon ${t} has onFailure=retry but error is not retriable, falling back to abort`,"info"),"shutdown";let i=this.retryAttempts.get(t)||0,o=e.retryConfig?.maxAttempts??3;if(i<o)return"retry";return this.logger.log(`Codon ${t} exhausted ${o} retry attempts, aborting`,"info"),"shutdown"}case"ignore":return this.logger.log(`Ignoring failure for codon ${t} due to onFailure: 'ignore' configuration`,"info"),"continue";default:{let i=a;return"shutdown"}}}delay(t){return new Promise((e)=>setTimeout(e,t))}async handleFileToolCall(t,e){if(this.watchedPatterns.length===0)return;let u=null,a="modified",l="";switch(t){case"Read":{u=e?.file_path||null,a="modified";break}case"Write":{let d=e;if(u=d?.file_path||null,l=d?.content||"",u)a=S.existsSync(dt.join(this.config.agentRootPath,u))?"modified":"created";break}case"Edit":{u=e?.file_path||null,a="modified";break}case"MultiEdit":{u=e?.file_path||null,a="modified";break}}if(!u)return;if(dt.isAbsolute(u))u=dt.relative(this.config.agentRootPath,u);let i=u.replace(/^\.\//g,"");if(!this.watchedPatterns.some((d)=>{let m=d.replace(/^\.\//g,"");return Tt(i,m,{matchBase:!0})}))return;if(!l){let d=dt.join(this.config.agentRootPath,u);if(S.existsSync(d))try{l=S.readFileSync(d,"utf-8")}catch(m){this.logger.log(`Error reading file ${u}: ${Wt(m).message}`,"error");return}}this.recentFileAccess={path:u,content:l,timestamp:new Date},this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"file.updated",data:{path:u,filename:dt.basename(u),content:l,action:a}}),await this.sendFileTreeUpdate()}async sendFileTreeUpdate(){if(this.watchedPatterns.length===0)return;let e=(await Promise.all(this.watchedPatterns.map((u)=>l_(this.config.agentRootPath,u)))).flat();this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"filetree.updated",data:{tree:e}})}async handleError(t,e,u="operation"){switch(this.logger.log(`[${u}] ${e}: ${t.message}`,u==="fatal"?"error":"info"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:t.message,context:e,severity:u,codon:this.currentCodon?.codon.id,fatal:u==="fatal"}}),u){case"fatal":await this.shutdown(`Fatal error: ${e}`);break;case"codon":this.cleanupCurrentCodon();break}}async requestAutostart(){if(this.initialAutostartTriggered){this.logger.log("[requestAutostart] Initial autostart already triggered, ignoring");return}if(!this.config.autostart){this.logger.log("[requestAutostart] Autostart disabled");return}this.initialAutostartTriggered=!0,this.logger.log("[requestAutostart] Triggering initial autostart"),await this.autoStartNextCodon()}async autoStartNextCodon(){let t=await this.stateManager.getExecutionThread();if(this.logger.log(`[autoStartNextCodon] Called - hasRunningCodon: ${t.hasRunningCodon}, isShuttingDown: ${this.isShuttingDown}`),t.hasRunningCodon||this.isShuttingDown){this.logger.log("[autoStartNextCodon] Returning early - codon running or shutting down");return}let e=t.nextCodonId;if(this.logger.log(`[autoStartNextCodon] ExecutionThread returned nextCodonId: ${e}`),!e){if(this.logger.log("[autoStartNextCodon] No more codons to run"),this.config.autostart)this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:"All codons completed successfully. Server shutting down."}}),setTimeout(()=>{this.shutdown("all codons completed")},2000);else this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"server.idle",data:{reason:"all-codons-completed",message:"All codons completed. Server remains active."}});return}this.logger.log(`[autoStartNextCodon] Auto-starting codon: ${e}`),await this.startCodon(e)}async startNextCodon(){let t=await this.stateManager.getExecutionThread();if(t.hasRunningCodon){await this.handleError(Error("Cannot start next codon while current codon is running"),"startNextCodon","operation");return}let e=t.nextCodonId;if(e)this.logger.log(`[startNextCodon] Advancing to next codon: ${e}`),await this.startCodon(e);else await this.handleError(Error("No more codons to run"),"startNextCodon","operation")}async skipCurrentCodon(){if(!this.currentCodon){await this.handleError(Error("No codon is currently running"),"skipCurrentCodon","operation");return}this.logger.log(`Skipping codon ${this.currentCodon.codonId}`),this.isSkippingCodon=!0;let t=this.codonRunners.get(this.currentCodon.codonId);if(t)await t.kill("SIGTERM")}async redoCurrentCodon(){let t=await this.stateManager.getExecutionThread();if(t.hasRunningCodon){await this.handleError(Error("Cannot redo while codon is running"),"redoCurrentCodon","operation");return}if(t.codons.length>0){let e=t.codons[0];this.logger.log(`[redoCurrentCodon] Redoing last codon: ${e.codon.codonId}`),await this.startCodon(e.codon.codonId)}else await this.handleError(Error("No codon has been run yet to redo."),"redoCurrentCodon","operation")}async listCheckpoints(t){let e=this.stateManager.getState(),u=t?this.stateManager.getRun(u0(t)):this.stateManager.getCurrentRun();if(!u){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:t?`Run ${t} not found`:"No active run",fatal:!1}});return}let a=[],l=t?e.runs.filter((i)=>i.runId===t):e.runs;for(let i of[...l].reverse())for(let o of i.codons){let m=this.config.codons.find((r)=>r.id===o.codonId)?.name||o.codonId;if("rigSetupCheckpoint"in o&&o.rigSetupCheckpoint)a.push({codonId:o.codonId,codonName:m,checkpointType:"rig-setup",sha:o.rigSetupCheckpoint,status:o.status,timestamp:o.startTime});if(o.status==="completed"&&o.completionCheckpoint)a.push({codonId:o.codonId,codonName:m,checkpointType:"completed",sha:o.completionCheckpoint,status:o.status,timestamp:o.endTime});if(o.status==="failed"&&"errorCheckpoint"in o&&o.errorCheckpoint)a.push({codonId:o.codonId,codonName:m,checkpointType:"error",sha:o.errorCheckpoint,status:o.status,timestamp:o.endTime});if(o.status==="skipped"&&"skipCheckpoint"in o&&o.skipCheckpoint)a.push({codonId:o.codonId,codonName:m,checkpointType:"skipped",sha:o.skipCheckpoint,status:o.status,timestamp:o.endTime})}a.reverse(),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"checkpoint.list",data:{runId:u.runId,checkpoints:a,currentBranch:u.gitBranch}})}async forceStopCodon(t){let e=this.stateManager.getCurrentlyRunningCodon();if(!e||Rt(e.status)){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"No running codon to stop",fatal:!1}});return}if(this.logger.log(`Force stopping codon ${e.codonId}: ${t||"user request"}`),this.isForceStopping=!0,this.codonFailureReason={type:"unknown",retriable:!0,message:`Force stopped: ${t||"user request"}`},this.currentRunId)this.stateManager.transition({type:"CodonTransitioned",data:{runId:this.currentRunId,codonId:e.codonId,from:e.status,to:"failed",metadata:{exitCode:-1,failureReason:this.codonFailureReason,failedDuring:e.status}}});if(this.currentCodon){let u=this.codonRunners.get(this.currentCodon.codonId);if(u)await u.kill("SIGTERM")}this.cleanupCurrentCodon(),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"info",data:{message:`Codon ${e.codonId} force stopped`}})}async rollbackToCheckpoint(t,e){let u=this.stateManager.getCurrentlyRunningCodon();if(u&&!Rt(u.status)){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot rollback while codon is running. Use 'codon.forceStop' first.",codon:u.codonId,fatal:!1}});return}let a=this.stateManager.getState(),l=await g0(a,void 0,void 0,this.logger),i=[];if(l.codons.forEach((d,m)=>{let r=d.codon;if("rigSetupCheckpoint"in r&&r.rigSetupCheckpoint){if(r.rigSetupCheckpoint.startsWith(t))i.push({threadCodon:d,checkpointType:"rig-setup",fullSha:r.rigSetupCheckpoint,codonIndex:m})}if(r.status==="completed"&&r.completionCheckpoint){if(r.completionCheckpoint.startsWith(t))i.push({threadCodon:d,checkpointType:"completed",fullSha:r.completionCheckpoint,codonIndex:m})}if(r.status==="failed"&&"errorCheckpoint"in r&&r.errorCheckpoint){if(r.errorCheckpoint.startsWith(t))i.push({threadCodon:d,checkpointType:"error",fullSha:r.errorCheckpoint,codonIndex:m})}if(r.status==="skipped"&&"skipCheckpoint"in r&&r.skipCheckpoint){if(r.skipCheckpoint.startsWith(t))i.push({threadCodon:d,checkpointType:"skipped",fullSha:r.skipCheckpoint,codonIndex:m})}}),i.length===0){this.logger.log(`SHA ${t} not found in current thread, searching all historical runs...`);for(let d of a.runs)for(let m=0;m<d.codons.length;m++){let r=d.codons[m],n={codon:r,runId:d.runId,runStatus:d.status,runStartTime:d.startTime,runEndTime:d.endTime||null,gitBranch:d.gitBranch,globalIndex:-1,runIndex:m,codonIndexInRun:m,validatedCheckpoints:[],continuationSessionId:null};if("rigSetupCheckpoint"in r&&r.rigSetupCheckpoint){if(r.rigSetupCheckpoint.startsWith(t))i.push({threadCodon:n,checkpointType:"rig-setup",fullSha:r.rigSetupCheckpoint,codonIndex:m})}if(r.status==="completed"&&r.completionCheckpoint){if(r.completionCheckpoint.startsWith(t))i.push({threadCodon:n,checkpointType:"completed",fullSha:r.completionCheckpoint,codonIndex:m})}if(r.status==="failed"&&"errorCheckpoint"in r&&r.errorCheckpoint){if(r.errorCheckpoint.startsWith(t))i.push({threadCodon:n,checkpointType:"error",fullSha:r.errorCheckpoint,codonIndex:m})}if(r.status==="skipped"&&"skipCheckpoint"in r&&r.skipCheckpoint){if(r.skipCheckpoint.startsWith(t))i.push({threadCodon:n,checkpointType:"skipped",fullSha:r.skipCheckpoint,codonIndex:m})}}if(i.length>0)this.logger.log(`Found ${i.length} match(es) in historical runs`)}if(i.length===0){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Checkpoint ${t} not found in any run (current or historical)`,fatal:!1}});return}if(i.length>1){let d=i.map((m)=>{let n=this.config.codons.find((c)=>c.id===m.threadCodon.codon.codonId)?.name||m.threadCodon.codon.codonId;return` - ${m.fullSha.substring(0,7)}... (${n} - ${m.checkpointType}) in run ${m.threadCodon.runId}`}).join(`
|
|
292
292
|
`);this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Ambiguous checkpoint SHA '${t}'. Multiple checkpoints match:
|
|
293
293
|
${d}
|
|
294
294
|
Please provide more characters to uniquely identify the checkpoint.`,fatal:!1}});return}let o=i[0];try{if(l.codons.some((m)=>m.runId===o.threadCodon.runId&&m.codon.codonId===o.threadCodon.codon.codonId)){let m=l.codons.findIndex((r)=>r.runId===o.threadCodon.runId&&r.codon.codonId===o.threadCodon.codon.codonId);this.logger.log(`Executing rollback to ${o.fullSha.substring(0,7)} (${o.checkpointType}) at thread index ${m}`),await this.executeRollback(l,m,o.fullSha,o.checkpointType,e)}else this.logger.log(`Executing direct rollback to historical checkpoint ${o.fullSha.substring(0,7)} (${o.checkpointType}) from run ${o.threadCodon.runId}`),await this.executeDirectRollback(o.threadCodon,o.fullSha,o.checkpointType,e)}catch(d){let m=Wt(d);if(this.logger.log(`Rollback failed: ${m.message}`,"error"),m.stack)this.logger.log(`Stack trace: ${m.stack}`,"error");throw this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Rollback failed: ${m.message}`,fatal:!1}}),d}}async executeDirectRollback(t,e,u,a){let i=this.config.codons.find((o)=>o.id===t.codon.codonId)?.name||t.codon.codonId;this.logger.log(`Direct rollback to ${u} checkpoint ${e} in codon ${t.codon.codonId} (${i}) from run ${t.runId}`),this.isRollingBack=!0;try{this.cleanupCurrentCodon();let o=this.stateManager.getCurrentRun(),d=o?.runId||t.runId,m=o?.codons[0]?.codonId||t.codon.codonId;if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.started",data:{fromRun:d,fromCodon:m,toCodon:t.codon.codonId,toCheckpoint:e,checkpointType:u,codonsToProcess:[t.codon.codonId]}}),o&&o.status!=="completed")this.stateManager.transition({type:"RunCompleted",data:{runId:o.runId}}),await this.stateManager.waitForPendingTransitions();let r=[];if(this.archiveManifest)r=this.archiveManifest.getEntriesAfterCheckpoint(e),this.logger.log(`Found ${r.length} archive entries to restore during direct rollback`);if(this.checkpointGit)this.logger.log(`Resetting to checkpoint ${e.substring(0,7)}`),await this.checkpointGit.resetToCheckpoint(e),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.codonCheckpoint",data:{codonId:t.codon.codonId,codonName:i,checkpointType:u,checkpoint:e,message:`Reset to ${i} ${u} checkpoint`}});if(r.length>0)await this.restoreArchiveEntries(r,e);if(this.archiveManifest)await this.archiveManifest.reload();let n=u==="rig-setup"?null:t.codon.codonId;await this.startNewRun({type:"continuation",source:{runId:t.runId,afterCodon:n?lt(n):null,checkpointSha:e},reason:"rollback"});let c=this.stateManager.getCurrentRun();if(this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.completed",data:{fromRun:d,toRun:c?.runId||t.runId,codonId:t.codon.codonId,codonName:i,checkpointType:u,checkpoint:e,autoRestart:a}}),a&&c)this.logger.log("Auto-starting next codon after rollback"),await this.autoStartNextCodon()}finally{this.isRollingBack=!1}}async rollbackToCodon(t,e,u){let a=this.stateManager.getCurrentlyRunningCodon();if(a&&!Rt(a.status)){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot rollback while codon is running. Use 'codon.forceStop' first.",codon:a.codonId,fatal:!1}});return}let l=this.stateManager.getState(),i=await g0(l,void 0,void 0,this.logger),o=null,d=-1;for(let c=0;c<i.codons.length;c++)if(i.codons[c].codon.codonId===t){o=i.codons[c],d=c;break}if(!o){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`Codon ${t} not found in execution history`,fatal:!1}});return}let m=o.codon,r,n=null;if(e==="start"){if("rigSetupCheckpoint"in m&&m.rigSetupCheckpoint)n=m.rigSetupCheckpoint,r="rig-setup";else if(m.status==="completed"&&m.completionCheckpoint)n=m.completionCheckpoint,r="completed";else if(m.status==="failed"&&"errorCheckpoint"in m&&m.errorCheckpoint)n=m.errorCheckpoint,r="error";else if(m.status==="skipped"&&"skipCheckpoint"in m&&m.skipCheckpoint)n=m.skipCheckpoint,r="skipped"}else if(e==="end"){if(m.status==="completed"&&m.completionCheckpoint)n=m.completionCheckpoint,r="completed";else if(m.status==="failed"&&"errorCheckpoint"in m&&m.errorCheckpoint)n=m.errorCheckpoint,r="error";else if(m.status==="skipped"&&"skipCheckpoint"in m&&m.skipCheckpoint)n=m.skipCheckpoint,r="skipped";else if("rigSetupCheckpoint"in m&&m.rigSetupCheckpoint)n=m.rigSetupCheckpoint,r="rig-setup"}else switch(r=e,e){case"rig-setup":n="rigSetupCheckpoint"in m?m.rigSetupCheckpoint||null:null;break;case"completed":n=m.status==="completed"?m.completionCheckpoint:null;break;case"error":n=m.status==="failed"&&"errorCheckpoint"in m?m.errorCheckpoint||null:null;break;case"skipped":n=m.status==="skipped"&&"skipCheckpoint"in m?m.skipCheckpoint||null:null;break}if(!n||!r){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:`No ${e} checkpoint found for codon ${t}`,fatal:!1}});return}await this.executeRollback(i,d,n,r,u)}async rollbackToLastSuccess(t){let e=this.stateManager.getCurrentlyRunningCodon();if(e&&!Rt(e.status)){this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"Cannot rollback while codon is running. Use 'codon.forceStop' first.",codon:e.codonId,fatal:!1}});return}let u=this.stateManager.getState(),a=await g0(u,void 0,void 0,this.logger),l=-1;for(let m=0;m<a.codons.length;m++)if(a.codons[m].codon.status==="completed"){l=m;break}if(l>=0){let m=a.codons[l];if(m.codon.status==="completed"){this.logger.log(`Found last successfully completed thread codon to rollback to: ${JSON.stringify(m)}`),await this.executeRollback(a,l,m.codon.completionCheckpoint,"completed",t);return}}this.logger.log("Did not find any successful codon to rollback to. Going to look for a checkpoint in the thread.");let i=-1,o=null,d=null;for(let m=a.codons.length-1;m>=0;m--){let n=a.codons[m].codon;if("rigSetupCheckpoint"in n&&n.rigSetupCheckpoint)i=m,o=n.rigSetupCheckpoint,d="rig-setup",this.logger.log(`Found rig setup checkpoint in codon ${n.codonId}`);else if(n.status==="completed"&&n.completionCheckpoint)i=m,o=n.completionCheckpoint,d="completed",this.logger.log(`Found completion checkpoint in codon ${n.codonId}`);else if(n.status==="failed"&&"errorCheckpoint"in n&&n.errorCheckpoint)i=m,o=n.errorCheckpoint,d="error",this.logger.log(`Found error checkpoint in codon ${n.codonId}`);else if(n.status==="skipped"&&"skipCheckpoint"in n&&n.skipCheckpoint)i=m,o=n.skipCheckpoint,d="skipped",this.logger.log(`Found skipped checkpoint in codon ${n.codonId}`)}if(i>=0&&o&&d)await this.executeRollback(a,i,o,d,t);else this.logger.log("No checkpoints found in execution history","error"),this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"error",data:{message:"No checkpoints found in execution history",fatal:!1}})}async executeRollback(t,e,u,a,l){let i=t.codons[e];if(!i)throw Error(`Invalid target codon index: ${e}`);let d=this.config.codons.find((m)=>m.id===i.codon.codonId)?.name||i.codon.codonId;this.logger.log(`Starting codon-by-codon rollback to ${a} checkpoint ${u} in codon ${i.codon.codonId} (${d})`),this.isRollingBack=!0,await this.executeCodonByCodonRollback(t,e,u,a,d,l).finally(()=>{this.isRollingBack=!1})}async executeCodonByCodonRollback(t,e,u,a,l,i){this.cleanupCurrentCodon();let o=t.codons[e];if(!o)throw Error(`Invalid target codon index: ${e}`);let d=t.codons.slice(0,e),m=t.codons[0]?.runId||o.runId,r=t.codons[0]?.codon.codonId||o.codon.codonId;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.started",data:{fromRun:m,fromCodon:r,toCodon:o.codon.codonId,toCheckpoint:u,checkpointType:a,codonsToProcess:d.map((g)=>g.codon.codonId)}});let n=0,c=d.length+1;for(let g of d){n++,this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.progress",data:{currentStep:n,totalSteps:c,message:`Rolling back through ${g.codon.codonId}`}});let v=this.getLastCheckpointForCodon(g.codon);if(v&&this.checkpointGit){await this.checkpointGit.resetToCheckpoint(v.sha);let B=this.config.codons.find((z)=>z.id===g.codon.codonId);this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.codonCheckpoint",data:{codonId:g.codon.codonId,codonName:B?.name||g.codon.codonId,checkpoint:v.sha,checkpointType:v.type,message:`Reset to ${g.codon.codonId} ${v.type} checkpoint`}})}await this.cleanupCodonRigDirectories(g.codon)}n++,this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.progress",data:{currentStep:n,totalSteps:c,message:"Applying final checkpoint"}});let _=[];if(this.archiveManifest)_=this.archiveManifest.getEntriesAfterCheckpoint(u),this.logger.log(`Found ${_.length} archive entries to restore during rollback`);if(this.checkpointGit)await this.checkpointGit.resetToCheckpoint(u);if(_.length>0)await this.restoreArchiveEntries(_,u);if(this.archiveManifest)await this.archiveManifest.reload();this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.codonCheckpoint",data:{codonId:o.codon.codonId,codonName:l,checkpoint:u,checkpointType:a,message:`Reset to target checkpoint ${o.codon.codonId} (${a})`}});let s=this.stateManager.getCurrentRun();if(s)this.stateManager.transition({type:"RunCompleted",data:{runId:s.runId}});await this.stateManager.waitForPendingTransitions();let x=a==="rig-setup"?null:o.codon.codonId;await this.startNewRun({type:"continuation",source:{runId:o.runId,afterCodon:x?lt(x):null,checkpointSha:u},reason:"rollback"});let w=this.config.codons.findIndex((g)=>g.id===o.codon.codonId);if(w>=0){let v=a==="rig-setup"?w:w-1;for(let B=0;B<=v;B++){let z=this.config.codons[B];if(z.type!=="loop"&&z.checkpointedFiles?.length)await this.addCheckpointPatterns(z.checkpointedFiles)}}if(await this.stateManager.waitForPendingTransitions(),this.isRollingBack=!1,this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.completed",data:{fromRun:m,toRun:this.currentRunId||"",checkpoint:u,codonId:o.codon.codonId,codonName:l,checkpointType:a,autoRestart:i}}),await this.sendStateSnapshot(),i&&this.config.autostart){let g=await this.stateManager.getNextCodonToExecute();if(g)await this.startCodon(g,a==="rig-setup")}}getLastCheckpointForCodon(t){if(t.status==="completed"&&t.completionCheckpoint)return{sha:t.completionCheckpoint,type:"completed"};if(t.status==="failed"&&"errorCheckpoint"in t&&t.errorCheckpoint)return{sha:t.errorCheckpoint,type:"error"};if(t.status==="skipped"&&"skipCheckpoint"in t&&t.skipCheckpoint)return{sha:t.skipCheckpoint,type:"skipped"};if("rigSetupCheckpoint"in t&&t.rigSetupCheckpoint)return{sha:t.rigSetupCheckpoint,type:"rig-setup"};return null}getRigSetupDirectories(t){let e=this.config.codons.find((l)=>l.id===t);if(!e)return[];if(e.type==="loop")return[];let u=e;if(!u.rigSetup)return[];let a=[];for(let l of u.rigSetup)if(l.type==="copy"&&l.copy)a.push(l.copy.to);return a}async executeArchiveRigs(t,e,u,a,l=!1){if(!t||t.length===0)return;if(!this.archiveManifest){this.logger.log("Archive manifest not initialized, skipping archiveOnSuccess","error");return}this.logger.log(`Executing archiveOnSuccess for ${e}: ${t.join(", ")}`);let i=await r0.resolveFiles(this.config.agentRootPath,t);this.logger.log(`Resolved ${i.length} files to archive from patterns`);let o=[];for(let r of i){let n=dt.join(this.config.agentRootPath,r),c;if(l)c=`${e}-loop`;else if(a)c=dt.join(`${a.loopId}-${a.iteration}`,e.replace(/#/g,"-"));else c=e.replace(/#/g,"-");let _=dt.join(this.config.rigArchivePath,c,r);try{if(!S.existsSync(n)){this.logger.log(`Archive source not found (skipping): ${r}`,"info"),o.push({path:r,success:!0});continue}if(await S.promises.mkdir(dt.dirname(_),{recursive:!0}),S.existsSync(_))this.logger.log(`Archive path collision, overwriting: ${_}`,"error"),await S.promises.rm(_,{recursive:!0,force:!0});await S.promises.cp(n,_,{recursive:!0}),await S.promises.rm(n,{recursive:!0,force:!0}),await this.archiveManifest.addEntry({sourcePath:r,archivePath:dt.relative(this.config.executionPath,_),codonId:e,loopContext:l?void 0:a,checkpointSha:u,timestamp:new Date().toISOString()}),this.logger.log(`Archived: ${r} → ${_}`,"info"),o.push({path:r,success:!0})}catch(s){let x=s instanceof Error?s.message:String(s);this.logger.log(`Failed to archive ${r}: ${x}`,"error"),o.push({path:r,success:!1,error:x})}}let d=o.filter((r)=>r.success).map((r)=>r.path),m=o.filter((r)=>!r.success);if(m.length>0)this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"archive.partial",data:{codonId:e,archivedPaths:d,failedPaths:m.map((r)=>({path:r.path,error:r.error||"Unknown error"}))}});else if(d.length>0)this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"archive.completed",data:{codonId:e,archivedPaths:d}})}async restoreArchiveEntries(t,e){if(t.length===0)return;this.logger.log(`Restoring ${t.length} archived files during rollback to ${e}`);let u=[];for(let m of t){let r=dt.join(this.config.executionPath,m.archivePath),n=dt.join(this.config.agentRootPath,m.sourcePath);try{if(!S.existsSync(r)){this.logger.log(`Archive file not found (skipping): ${m.archivePath}`,"error"),u.push({path:m.sourcePath,success:!1,error:"Archive not found"});continue}if(await S.promises.mkdir(dt.dirname(n),{recursive:!0}),S.existsSync(n))await S.promises.rm(n,{recursive:!0,force:!0});await S.promises.cp(r,n,{recursive:!0}),await S.promises.rm(r,{recursive:!0,force:!0}),this.logger.log(`Restored: ${m.archivePath} → ${m.sourcePath}`,"info"),u.push({path:m.sourcePath,success:!0})}catch(c){let _=c instanceof Error?c.message:String(c);this.logger.log(`Failed to restore ${m.sourcePath}: ${_}`,"error"),u.push({path:m.sourcePath,success:!1,error:_})}}let a=u.filter((m)=>m.success).map((m)=>m.path),l=u.filter((m)=>!m.success),i;if(l.length===0)i="completed";else if(a.length>0)i="partial";else i="failed";this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.archiveRestore",data:{codonId:e,restoredPaths:a,failedPaths:l.length>0?l.map((m)=>({path:m.path,error:m.error||"Unknown error"})):void 0,status:i}});let o=new Set;for(let m of t){let r=dt.join(this.config.executionPath,m.archivePath),n=dt.dirname(r);while(n!==this.config.rigArchivePath&&n.startsWith(this.config.rigArchivePath))o.add(n),n=dt.dirname(n)}let d=Array.from(o).sort((m,r)=>r.length-m.length);for(let m of d)try{if(S.existsSync(m)){if((await S.promises.readdir(m)).length===0)await S.promises.rmdir(m),this.logger.log(`Cleaned up empty archive directory: ${dt.relative(this.config.executionPath,m)}`,"info")}}catch(r){this.logger.log(`Could not clean up archive directory ${m}: ${r}`,"debug")}}async cleanupCodonRigDirectories(t){let e=this.getRigSetupDirectories(t.codonId);if(e.length===0)return;let a=this.config.codons.find((o)=>o.id===t.codonId)?.name||t.codonId;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.rigCleanup",data:{codonId:t.codonId,codonName:a,directories:e,status:"started"}});let l=[],i=[];for(let o of e){let d=dt.join(this.config.agentRootPath,o);try{if(S.existsSync(d))await S.promises.rm(d,{recursive:!0,force:!0}),this.logger.log(`Removed rig setup directory: ${o}`),l.push(o);else this.logger.log(`Rig setup directory already absent: ${o}`),l.push(o)}catch(m){let r=Wt(m).message;this.logger.log(`Failed to remove rig directory ${o}: ${r}`,"error"),i.push({directory:o,error:r})}}if(i.length>0){let o=l.length>0?"partial":"failed";this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.rigCleanup",data:{codonId:t.codonId,codonName:a,directories:e,status:o,successfulCleanups:l,failedCleanups:i,error:i.map((d)=>`${d.directory}: ${d.error}`).join(", ")}})}else this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"rollback.rigCleanup",data:{codonId:t.codonId,codonName:a,directories:e,status:"completed",successfulCleanups:l,failedCleanups:[]}})}async loadSentinelsForCodon(t,e){if(this.currentCodonSentinels.clear(),!t.sentinels||t.sentinels.length===0)return{loaded:[],errors:[]};this.logger.log(`Loading ${t.sentinels.length} sentinel config(s) for codon ${e}`,"info");let u=this.config.configPath?dt.dirname(this.config.configPath):this.config.cwd,a=this.sentinelConfigLoader.loadConfigsForCodon(t.sentinels,t.id,u);if(a.errors.length>0)this.logger.log(`${a.errors.length} sentinel config(s) failed to load`,"info");if(a.configs.length===0)return this.logger.log("No sentinels loaded for this codon","info"),{loaded:[],errors:a.errors};try{let l=a.configs.map((r)=>{let n=r.config;if(r.config.reportToWebsocket||r.outputPaths||r.failCodonIfNotLoaded){let c=t.sentinels?.find((_)=>typeof _.sentinelConfig==="object"&&_.sentinelConfig.id===n.id||typeof _.sentinelConfig==="string")?.settings?.reportToWebsocket;if(c){let _={...n.reportToWebsocket||{},...c};return{...n,reportToWebsocket:_}}}return n}),i=a.configs.map((r)=>r.configDirectory),o=new Map;for(let r of a.configs)if(r.outputPaths)o.set(r.config.id,r.outputPaths);let{loadedIds:d}=await this.sentinelManager.loadSentinelsForCodon(l,e,{configDirectory:i[0],runStartTime:new Date,executionPath:this.config.executionPath,agentRootPath:this.config.agentRootPath,outputPathsMap:o.size>0?o:void 0});this.currentCodonSentinels.clear();for(let r of d)this.currentCodonSentinels.add(r);this.logger.log(`Successfully loaded ${d.length} sentinel instance(s)`,"info");let m=a.configs.filter((r)=>d.includes(r.config.id));for(let r of m){let n=r.config;this.emit("event",{id:b(X()),timestamp:new Date().toISOString(),type:"sentinel.loaded",data:{sentinelId:n.id,codonId:e,model:n.model,triggerType:n.trigger.type,executionStrategy:n.execution.strategy,conversational:!!n.conversational,source:r.source,sourcePath:r.sourcePath}})}if(this.currentRunId&&d.length>0){let r=this.sentinelManager.getSentinelStates(),n=r.reduce((c,_)=>c+_.totalCost,0);this.stateManager.transition({type:"SentinelStatesUpdated",data:{runId:this.currentRunId,codonId:t.id,sentinelStates:r,totalCost:n}}),this.logger.log(`Captured initial state for ${r.length} sentinel(s)`,"debug")}return{loaded:d,errors:a.errors}}catch(l){let i=`Failed to instantiate sentinels in SentinelManager: ${l}`,o=a.configs.every((d)=>d.failCodonIfNotLoaded);return{loaded:[],errors:[{ref:"SentinelManager",error:i,fatal:o}]}}}cleanupCurrentCodon(){let t=this.currentCodon?!!this.codonRunners.get(this.currentCodon.codonId):!1;if(this.logger.log(`[cleanupCurrentCodon] Called - currentCodon=${this.currentCodon?.codonId||"none"}, hasRunner=${t}`,"info"),this.logger.log(`[cleanupCurrentCodon] Stack trace:
|
|
295
|
-
${Error().stack}`,"debug"),this.currentCodon?.codonId){let e=this.codonRunners.get(this.currentCodon.codonId);if(e)this.logger.log(`[cleanupCurrentCodon] Calling cleanup() on runner for codon ${this.currentCodon.codonId}`,"info"),e.cleanup().catch((u)=>this.logger.log(`Error cleaning up runner for codon ${this.currentCodon?.codonId}: ${u}`,"error")),this.codonRunners.delete(this.currentCodon.codonId),this.logger.log(`[cleanupCurrentCodon] Runner for codon ${this.currentCodon.codonId} cleaned up and removed from map`,"info");else this.logger.log(`[cleanupCurrentCodon] No runner found in map for codon ${this.currentCodon.codonId}`,"info")}this.watchedPatterns=[],this.recentFileAccess=void 0,this.currentCodon=void 0,this.codonFailureReason=void 0,this.isForceStopping=!1,this.isSkippingCodon=!1,this.resultMessageReceived=!1,this.pendingToolUses.clear()}async runCommand(t,e){let
|
|
295
|
+
${Error().stack}`,"debug"),this.currentCodon?.codonId){let e=this.codonRunners.get(this.currentCodon.codonId);if(e)this.logger.log(`[cleanupCurrentCodon] Calling cleanup() on runner for codon ${this.currentCodon.codonId}`,"info"),e.cleanup().catch((u)=>this.logger.log(`Error cleaning up runner for codon ${this.currentCodon?.codonId}: ${u}`,"error")),this.codonRunners.delete(this.currentCodon.codonId),this.logger.log(`[cleanupCurrentCodon] Runner for codon ${this.currentCodon.codonId} cleaned up and removed from map`,"info");else this.logger.log(`[cleanupCurrentCodon] No runner found in map for codon ${this.currentCodon.codonId}`,"info")}this.watchedPatterns=[],this.recentFileAccess=void 0,this.currentCodon=void 0,this.codonFailureReason=void 0,this.isForceStopping=!1,this.isSkippingCodon=!1,this.resultMessageReceived=!1,this.pendingToolUses.clear()}async runCommand(t,e,u){let a,l=typeof t==="string"?{type:"command",command:{run:t}}:t;if(l.command.workingDirectory==="lastCopied")if(e)a=e;else a=this.config.agentRootPath;else a=this.config.agentRootPath;this.logger.log(`[DEBUG] Running command: ${l.command.run}`,"info"),this.logger.log(`[DEBUG] Working directory: ${a}`,"info");try{let i=await S.promises.readdir(a);this.logger.log(`[DEBUG] Directory contents: ${i.join(", ")}`,"info")}catch(i){this.logger.log(`[DEBUG] Could not read directory contents: ${Wt(i).message}`,"error")}return new Promise((i,o)=>{let d=O8(l.command.run,{shell:!0,cwd:a,env:u?{...process.env,...u}:void 0}),m="",r="";d.stdout?.on("data",(n)=>{let c=n.toString();m+=c,this.logger.log(`[DEBUG] Command stdout: ${c.trim()}`,"info")}),d.stderr?.on("data",(n)=>{let c=n.toString();r+=c,this.logger.log(`[DEBUG] Command stderr: ${c.trim()}`,"error")}),d.on("exit",(n)=>{if(n===0)this.logger.log("[DEBUG] Command completed successfully","info"),i();else{let c=n??-1;this.logger.log(`[DEBUG] Command failed with exit code ${c}`,"error"),this.logger.log(`[DEBUG] Full stdout: ${m}`,"info"),this.logger.log(`[DEBUG] Full stderr: ${r}`,"error");let _=new ti(`Command failed with exit code ${c}`,c,m,r);o(_)}}),d.on("error",(n)=>{this.logger.log(`[DEBUG] Command error: ${n.message}`,"error"),o(n)})})}async copyPath(t,e){if(!await S.promises.stat(t).catch(()=>null))throw Error(`Source path does not exist: ${t}`);let a=dt.dirname(e),l=await S.promises.stat(a).catch(()=>null);if(!l||!l.isDirectory())throw Error(`Target parent directory does not exist: ${a}`);if(await S.promises.stat(e).catch(()=>null))throw Error(`Target path already exists: ${e}`);await this.runCommand(`cp -r ${x2(t)} ${x2(e)}`)}async initializeCheckpoints(){if(!await this.isGitAvailable()){this.logger.log("Git is not available. Checkpointing disabled.","info"),this.checkpointingEnabled=!1;return}this.checkpointGit=new jl(this.config.executionPath,this.config.agentRootPath,this.logger),await this.checkpointGit.initialize(),this.stateManager.setCheckpointGit(this.checkpointGit),this.archiveManifest=new Po(this.config.executionPath,this.logger),await this.archiveManifest.load(),this.logger.log("Checkpoint system initialized")}async isGitAvailable(){return new Promise((t)=>{let e=O8("git",["--version"],{stdio:"ignore"});e.on("error",()=>t(!1)),e.on("exit",(u)=>t(u===0))})}async addCheckpointPatterns(t){if(!this.checkpointingEnabled||t.length===0)return;if(!this.checkpointGit)this.checkpointGit=new jl(this.config.executionPath,this.config.agentRootPath,this.logger),await this.checkpointGit.initialize();await this.checkpointGit.addPatterns(t),this.logger.log(`Added checkpoint patterns: ${t.join(", ")}`)}async createCheckpoint(t){if(!this.checkpointingEnabled||!this.checkpointGit){this.logger.log(`[CHECKPOINT-DEBUG] Checkpoint creation skipped - enabled: ${this.checkpointingEnabled}, git: ${!!this.checkpointGit}`);return}this.logger.log(`[CHECKPOINT-DEBUG] Creating checkpoint for codon ${t.codonId} with status ${t.status}`),this.logger.log(`[CHECKPOINT-DEBUG] Checkpoint info: ${JSON.stringify(t)}`);try{let e=`${t.status}:${t.codonId} [run:${t.runId}] ${t.codonName}`,u=["",`Codon: ${t.codonName}`,`Status: ${t.status}`,`Timestamp: ${t.timestamp}`];if(t.duration!==void 0)u.push(`Duration: ${t.duration}ms`);let a=`${e}
|
|
296
296
|
${u.join(`
|
|
297
297
|
`)}`;this.logger.log(`[CHECKPOINT-DEBUG] Commit message: ${a}`);let i=this.stateManager.getCurrentRun()?.gitBranch||`run-${this.currentRunId}`;this.logger.log(`[CHECKPOINT-DEBUG] Using branch: ${i}`),await this.checkpointGit.switchToBranch(i);let o=await this.checkpointGit.commit(a);if(this.logger.log(`[CHECKPOINT-DEBUG] Checkpoint commit returned: ${o}`),o){this.logger.log(`[CHECKPOINT-DEBUG] Created checkpoint: ${o} (${t.status}) on branch ${i}`);let d=t.status==="rig-setup"?"rig-setup":t.status==="completed"?"completed":t.status==="error"?"error":"skipped";if(this.logger.log(`[CHECKPOINT-DEBUG] Firing CheckpointCreated transition with type: ${d}`),this.currentRunId)this.stateManager.transition({type:"CheckpointCreated",data:{runId:this.currentRunId,codonId:lt(t.codonId),checkpointType:d,sha:o,branch:i}});return o}else this.logger.log("[CHECKPOINT-DEBUG] No commit hash returned from checkpoint.commit()")}catch(e){this.logger.log(`[CHECKPOINT-DEBUG] Checkpoint failed: ${Wt(e).message}. Disabling checkpointing for this session.`,"error"),this.checkpointingEnabled=!1}}async shutdown(t,e=!0,u){if(this.isShuttingDown){this.logger.log(`Shutdown already in progress, ignoring: ${t}`);return}if(this.logger.log(`Shutting down server: ${t}`),this.currentCodon){let a=this.codonRunners.get(this.currentCodon.codonId);if(a)this.logger.log("Killing current codon runner for shutdown"),await a.kill("SIGTERM")}if(this.isShuttingDown=!0,t!=="all codons completed"&&this.checkpointingEnabled&&this.currentCodon)await this.createCheckpoint({status:"exit",codonId:lt(this.currentCodon.codon.id),codonName:this.currentCodon.codon.name,runId:this.currentRunId||u0("unknown"),timestamp:new Date().toISOString()});if(this.cleanupCurrentCodon(),this.sentinelManager)this.logger.log("Shutting down sentinel manager...","info"),await this.sentinelManager.shutdown(),this.logger.log("Sentinel manager shutdown complete","info");if(this.currentRunId&&t==="all codons completed")this.stateManager.transition({type:"RunCompleted",data:{runId:this.currentRunId}});else if(this.currentRunId&&t!=="codon failure")this.stateManager.transition({type:"RunFailed",data:{runId:this.currentRunId}});if(await this.stateManager.waitForPendingTransitions(),this.heartbeatInterval)clearInterval(this.heartbeatInterval),this.heartbeatInterval=void 0;for(let[a,l]of this.clients)try{l.close()}catch(i){this.logger.log(`Error closing client ${a}: ${i}`,"error")}if(this.clients.clear(),this.server)this.server.stop(),this.server=null;if(this.proxyRunner)this.proxyRunner.stop(),this.proxyRunner=null;try{this.logger.log("Event journal closed")}catch(a){this.logger.log(`Error closing event journal: ${a}`,"error")}if(S.existsSync(this.config.lockFile))try{S.unlinkSync(this.config.lockFile),this.logger.log("Lock file removed")}catch(a){this.logger.log(`Failed to remove lock file: ${a}`,"error")}this.logger.log("Server shutdown complete");try{if(S.existsSync(this.config.lockFile))S.unlinkSync(this.config.lockFile)}catch{}if(e&&t!=="running integration test"){let a=u;if(a===void 0)if(t==="all codons completed"){let l=this.stateManager.getCurrentRun();a=l?.status==="failed"||l?.status==="crashed"?1:0}else if(t==="codon failure")a=1;else a=["SIGINT","SIGTERM","client request"].includes(t)?0:1;this.logger.log(`Shutdown: ${t} (exit code: ${a})`),setTimeout(()=>{process.exit(a)},Ke.CODON_CLEANUP_DELAY_MS)}}}import*as ke from"node:fs";import*as Nu from"node:path";var UV={"hank.json":`{
|
|
298
298
|
"$schema": "https://unpkg.com/hankweave@latest/schemas/hank.schema.json",
|
|
@@ -493,4 +493,4 @@ Use --output to copy them elsewhere.
|
|
|
493
493
|
${J.stack}`);if(J instanceof Error&&"cause"in J&&J.cause)console.error(`Cause: ${J.cause}`);process.exit(1)}}if(xt.main==xt.module)try{await fX()}catch(t){if(console.error(`Error: ${t instanceof Error?t.message:String(t)}`),t instanceof Error&&t.stack)console.error(`Stack:
|
|
494
494
|
${t.stack}`);process.exit(1)}
|
|
495
495
|
|
|
496
|
-
//# debugId=
|
|
496
|
+
//# debugId=67D9DFE6D0CD8F4064756E2164756E21
|