thinkpool-pair 0.7.370 → 0.7.372

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import t from"node:fs";import{CODEX_COMMAND_CATALOG as e}from"./codex-commands.mjs";import{normalizeEditKind as s,summarizeTextDiff as i}from"./edit-diff.mjs";import{evidenceForToolResult as n}from"./evidence-citations.mjs";const o=t=>String(t||"unknown").replace(/[^a-zA-Z0-9_-]/g,"_"),r=t=>`mcp__${o(t?.server)}__${o(t?.tool)}`,a=t=>t?.query||t?.action?.query||t?.action?.url||"",l=t=>{if("string"==typeof t)return t;if(null==t)return"";try{return JSON.stringify(t)}catch{return String(t)}},d=(t,e)=>"string"==typeof t&&t.trim()?t:t&&"object"==typeof t&&"string"==typeof t.message&&t.message.trim()?t.message:l(t)||e;export class CodexEventMapper{constructor({onEvent:e,model:s=null,sessionId:i=null,fsImpl:n=t,usageSnapshotForSession:o=()=>null}){this._onEvent=e,this._model=s||null,this._sessionId=i||null,this._starts=new Map,this._fileBefore=new Map,this._fs=n,this._systemSent=!1,this._usageSnapshotForSession=o,this._usageBaseline=null}_emit(t){try{this._onEvent?.(t)}catch{}}setModel(t){this._model=t||null}setUsageBaseline(t){this._usageBaseline=t||null}push(t){if(t&&"object"==typeof t)switch(t.type){case"thread.started":return this._sessionId=t.thread_id||this._sessionId||null,void(this._systemSent||(this._systemSent=!0,this._emit({kind:"system",sessionId:this._sessionId,model:this._model,commands:e})));case"turn.started":default:return;case"item.started":{const e=t.item;if(!e)return;if(this._starts.set(e.id,Date.now()),"command_execution"===e.type)this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:"Bash",input:{command:e.command||""}}],parentToolUseId:null});else if("file_change"===e.type){const t=(e.changes||[]).map(t=>{const e=s(t.kind);let i="";if("add"!==e&&t.path)try{i=this._fs.readFileSync(t.path,"utf8")}catch{}return{path:t.path||"",kind:e,content:i}});this._fileBefore.set(e.id,t)}else"web_search"===e.type?this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:"WebSearch",input:{query:a(e)}}],parentToolUseId:null}):"mcp_tool_call"===e.type&&this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:r(e),input:e.arguments||{}}],parentToolUseId:null});return}case"item.completed":{const e=t.item;if(!e)return;const s=this._starts.get(e.id),o=null!=s?Date.now()-s:void 0;if(this._starts.delete(e.id),"agent_message"===e.type)this._emit({kind:"assistant",blocks:[{type:"text",text:e.text||""}],parentToolUseId:null});else if("reasoning"===e.type)this._emit({kind:"assistant",blocks:[{type:"thinking",text:e.text||"",reasoningKind:"summary"}],parentToolUseId:null});else if("command_execution"===e.type){const t=null!=e.exit_code&&0!==e.exit_code;this._emit({kind:"tool_result",toolUseId:e.id,content:[{type:"text",text:e.aggregated_output||""}],isError:t,durationMs:o,parentToolUseId:null})}else if("file_change"===e.type){const t=this._fileBefore.get(e.id)||(e.changes||[]).map(t=>({path:t.path||"",kind:t.kind||"update",content:""}));this._fileBefore.delete(e.id);const s=t.map(t=>{let e="";if("delete"!==t.kind&&t.path)try{e=this._fs.readFileSync(t.path,"utf8")}catch{}const s=i(t.content,e,{context:2,maxLines:60});return{file_path:t.path,path:t.path,kind:t.kind,added:s.added,removed:s.removed,diff_preview:s.preview,old_string:t.content,new_string:e}}),n=s[0]||{file_path:"",path:"",old_string:"",new_string:"",kind:"update"},r=s.length>1?"MultiEdit":"add"===n.kind?"Write":"Edit",a="MultiEdit"===r?{edits:s}:"Write"===r?{file_path:n.file_path,path:n.path,content:n.new_string}:n;this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:r,input:a}],parentToolUseId:null}),this._emit({kind:"tool_result",toolUseId:e.id,content:[{type:"text",text:`${"Write"===r?"wrote":"edited"} ${n.path}`}],isError:!1,durationMs:o,parentToolUseId:null})}else if("web_search"===e.type){const t=a(e);this._emit({kind:"tool_result",toolUseId:e.id,toolInput:{query:t},content:[{type:"text",text:t||"search completed"}],isError:!1,durationMs:o,parentToolUseId:null})}else if("mcp_tool_call"===e.type){const t=[{type:"text",text:e.error?.message||l(e.result)}],s=n(r(e),t);this._emit({kind:"tool_result",toolUseId:e.id,content:t,...s?{evidence:s}:{},isError:"failed"===e.status||!!e.error,durationMs:o,parentToolUseId:null})}return}case"turn.completed":{const e=t.usage||{};let s=null;try{s=this._usageSnapshotForSession(this._sessionId)}catch{}const i=Number(s?.context?.used),n=Number(s?.context?.max);Number.isFinite(i)&&i>=0&&Number.isFinite(n)&&n>0&&this._emit({kind:"usage",ctx:{used:i,max:n,pct:Math.min(100,Math.max(0,Math.round(i/n*100))),over:i>n,model:this._model}});const o=s?.total||e,r=this._usageBaseline?.total||{},a=Math.max(0,(Number(o.input_tokens)||0)-(Number(r.input_tokens)||0)),l=Math.min(a,Math.max(0,(Number(o.cached_input_tokens)||0)-(Number(r.cached_input_tokens)||0))),d=Math.max(0,(Number(o.output_tokens)||0)-(Number(r.output_tokens)||0));return this._usageBaseline=s||null,void this._emit({kind:"result",subtype:"success",sessionId:this._sessionId,model:this._model,costUsd:null,usage:{input_tokens:a-l,cache_read_input_tokens:l,cache_creation_input_tokens:0,output_tokens:d},numTurns:1,durationMs:void 0,denials:0,resultText:null})}case"turn.failed":return this._emit({kind:"error",message:d(t.message??t.error,"codex turn failed"),recoverable:!0}),void this._emit({kind:"result",subtype:"error_during_execution",sessionId:this._sessionId,model:this._model,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null});case"error":return void this._emit({kind:"error",message:d(t.message??t.error,"codex error"),recoverable:!0})}}}
1
+ import t from"node:fs";import{CODEX_COMMAND_CATALOG as e}from"./codex-commands.mjs";import{normalizeEditKind as s,summarizeTextDiff as i}from"./edit-diff.mjs";import{evidenceForToolResult as n}from"./evidence-citations.mjs";const o=t=>String(t||"unknown").replace(/[^a-zA-Z0-9_-]/g,"_"),r=t=>`mcp__${o(t?.server)}__${o(t?.tool)}`,a=t=>t?.query||t?.action?.query||t?.action?.url||"",l=t=>{if("string"==typeof t)return t;if(null==t)return"";try{return JSON.stringify(t)}catch{return String(t)}},d=(t,e)=>"string"==typeof t&&t.trim()?t:t&&"object"==typeof t&&"string"==typeof t.message&&t.message.trim()?t.message:l(t)||e;export class CodexEventMapper{constructor({onEvent:e,model:s=null,sessionId:i=null,fsImpl:n=t,usageSnapshotForSession:o=()=>null}){this._onEvent=e,this._model=s||null,this._sessionId=i||null,this._starts=new Map,this._fileBefore=new Map,this._fs=n,this._systemSent=!1,this._usageSnapshotForSession=o,this._usageBaseline=null}_emit(t){try{this._onEvent?.(t)}catch{}}setModel(t){this._model=t||null}setUsageBaseline(t){this._usageBaseline=t||null}push(t){if(t&&"object"==typeof t)switch(t.type){case"thread.started":return this._sessionId=t.thread_id||this._sessionId||null,void(this._systemSent||(this._systemSent=!0,this._emit({kind:"system",sessionId:this._sessionId,model:this._model,commands:e})));case"turn.started":default:return;case"item.started":{const e=t.item;if(!e)return;if(this._starts.set(e.id,Date.now()),"command_execution"===e.type)this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:"Bash",input:{command:e.command||""}}],parentToolUseId:null});else if("file_change"===e.type){const t=(e.changes||[]).map(t=>{const e=s(t.kind);let i="";if("add"!==e&&t.path)try{i=this._fs.readFileSync(t.path,"utf8")}catch{}return{path:t.path||"",kind:e,content:i}});this._fileBefore.set(e.id,t)}else"web_search"===e.type?this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:"WebSearch",input:{query:a(e)}}],parentToolUseId:null}):"mcp_tool_call"===e.type&&this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:r(e),input:e.arguments||{}}],parentToolUseId:null});return}case"item.completed":{const e=t.item;if(!e)return;const o=this._starts.get(e.id),d=null!=o?Date.now()-o:void 0;if(this._starts.delete(e.id),"agent_message"===e.type)this._emit({kind:"assistant",blocks:[{type:"text",text:e.text||""}],parentToolUseId:null});else if("reasoning"===e.type)this._emit({kind:"assistant",blocks:[{type:"thinking",text:e.text||"",reasoningKind:"summary"}],parentToolUseId:null});else if("command_execution"===e.type){const t=null!=e.exit_code&&0!==e.exit_code;this._emit({kind:"tool_result",toolUseId:e.id,content:[{type:"text",text:e.aggregated_output||""}],isError:t,durationMs:d,parentToolUseId:null})}else if("file_change"===e.type){const t=this._fileBefore.get(e.id)||(e.changes||[]).map(t=>({path:t.path||"",kind:s(t.kind),content:""}));this._fileBefore.delete(e.id);const n=t.map(t=>{let e="";if("delete"!==t.kind&&t.path)try{e=this._fs.readFileSync(t.path,"utf8")}catch{}const s=i(t.content,e,{context:2,maxLines:60});return{file_path:t.path,path:t.path,kind:t.kind,added:s.added,removed:s.removed,diff_preview:s.preview,old_string:t.content,new_string:e}}),o=n[0]||{file_path:"",path:"",old_string:"",new_string:"",kind:"update"},r=n.length>1?"MultiEdit":"add"===o.kind?"Write":"Edit",a="MultiEdit"===r?{edits:n}:"Write"===r?{file_path:o.file_path,path:o.path,content:o.new_string}:o;this._emit({kind:"assistant",blocks:[{type:"tool_use",id:e.id,name:r,input:a}],parentToolUseId:null}),this._emit({kind:"tool_result",toolUseId:e.id,content:[{type:"text",text:`${"Write"===r?"wrote":"edited"} ${o.path}`}],isError:!1,durationMs:d,parentToolUseId:null})}else if("web_search"===e.type){const t=a(e);this._emit({kind:"tool_result",toolUseId:e.id,toolInput:{query:t},content:[{type:"text",text:t||"search completed"}],isError:!1,durationMs:d,parentToolUseId:null})}else if("mcp_tool_call"===e.type){const t=[{type:"text",text:e.error?.message||l(e.result)}],s=n(r(e),t);this._emit({kind:"tool_result",toolUseId:e.id,content:t,...s?{evidence:s}:{},isError:"failed"===e.status||!!e.error,durationMs:d,parentToolUseId:null})}return}case"turn.completed":{const e=t.usage||{};let s=null;try{s=this._usageSnapshotForSession(this._sessionId)}catch{}const i=Number(s?.context?.used),n=Number(s?.context?.max);Number.isFinite(i)&&i>=0&&Number.isFinite(n)&&n>0&&this._emit({kind:"usage",ctx:{used:i,max:n,pct:Math.min(100,Math.max(0,Math.round(i/n*100))),over:i>n,model:this._model}});const o=s?.total||e,r=this._usageBaseline?.total||{},a=Math.max(0,(Number(o.input_tokens)||0)-(Number(r.input_tokens)||0)),l=Math.min(a,Math.max(0,(Number(o.cached_input_tokens)||0)-(Number(r.cached_input_tokens)||0))),d=Math.max(0,(Number(o.output_tokens)||0)-(Number(r.output_tokens)||0));return this._usageBaseline=s||null,void this._emit({kind:"result",subtype:"success",sessionId:this._sessionId,model:this._model,costUsd:null,usage:{input_tokens:a-l,cache_read_input_tokens:l,cache_creation_input_tokens:0,output_tokens:d},numTurns:1,durationMs:void 0,denials:0,resultText:null})}case"turn.failed":return this._emit({kind:"error",message:d(t.message??t.error,"codex turn failed"),recoverable:!0}),void this._emit({kind:"result",subtype:"error_during_execution",sessionId:this._sessionId,model:this._model,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null});case"error":return void this._emit({kind:"error",message:d(t.message??t.error,"codex error"),recoverable:!0})}}}
package/codex-session.mjs CHANGED
@@ -1 +1 @@
1
- import{execFileSync as e,spawn as t}from"node:child_process";import r from"node:fs";import n from"node:os";import o from"node:path";import s from"node:readline";import{buildCodexAppServerArgs as a,codexAppServerGate as i,createCodexAppServer as l}from"./codex-app-server.mjs";import{CodexEventMapper as u}from"./codex-event-mapper.mjs";import{startCodexMcpHttp as d}from"./codex-mcp-http.mjs";import{CODEX_THINKPOOL_FIRST_TURN_PREAMBLE as c,buildThinkPoolTurnGuidance as p,createRoomContextSelector as m,usesFullThinkPoolReminder as f}from"./thinkpool-room-prompt.mjs";import{questionAnswerResponse as h}from"./question-response.mjs";import{codexReviewTarget as x,isCodexCompactCommand as g}from"./codex-commands.mjs";import{createCumulativeEventRelay as y}from"./cumulative-event-relay.mjs";import{stallDecision as v,stallEvent as b}from"./turn-stall.mjs";const w=new Set(["read-only","workspace-write","danger-full-access"]);export const EFFORT_LEVELS=new Set(["low","medium","high","xhigh","max"]);export const CODEX_MODE_CONFIG={default:{sandbox:"workspace-write",approvalPolicy:"untrusted"},acceptEdits:{sandbox:"workspace-write",approvalPolicy:"on-request"},plan:{sandbox:"read-only",approvalPolicy:"never"},review:{sandbox:"read-only",approvalPolicy:"never"},bypassPermissions:{sandbox:"danger-full-access",approvalPolicy:"never"}};export function codexConfigForMode(e){return CODEX_MODE_CONFIG[e]||CODEX_MODE_CONFIG.default}export function codexDefaultCollaborationMode(e,t){return e?{mode:"default",settings:{model:String(e),reasoning_effort:EFFORT_LEVELS.has(t)?t:null,developer_instructions:null}}:null}export function defaultModeForRuntime(e){return"codex"===e?"bypassPermissions":"default"}export function codexWritableDirsForSandbox(e,t=[]){return"read-only"===normalizeCodexSandbox(e)?[]:t}export function codexExtraWritableDirsForMode(e,t,r=[]){return"review"===e?[]:codexWritableDirsForSandbox(t,r)}export function readCodexDefaultModel({home:e=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:t=r}={}){try{const r=t.readFileSync(o.join(e,"config.toml"),"utf8").split(/^\s*\[/m,1)[0].match(/^\s*model\s*=\s*["']([^"']+)["']\s*(?:#.*)?$/m);return r?.[1]?.trim()||null}catch{return null}}export function readCodexModels({home:e=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:t=r}={}){try{const r=JSON.parse(t.readFileSync(o.join(e,"models_cache.json"),"utf8"));return(Array.isArray(r?.models)?r.models:[]).filter(e=>e?.slug&&"hide"!==e.visibility).sort((e,t)=>(Number(e.priority)||999)-(Number(t.priority)||999)).map(e=>({value:e.slug,displayName:e.display_name||e.slug,description:e.description||""}))}catch{return[]}}const k=new Map;function I(e,{home:t,fsImpl:r}){if(!e)return null;const n=`${t}\0${e}`,s=k.get(n);if(s)return s;const a=o.join(t,"sessions");try{const t=`${e}.jsonl`,s=r.readdirSync(a,{recursive:!0,withFileTypes:!0}).find(e=>e.isFile()&&e.name.endsWith(t));if(!s)return null;const i=o.join(s.parentPath||s.path||a,s.name);return k.set(n,i),i}catch{return null}}export function codexThreadCanResume(e,{home:t=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:s=r}={}){if(!e)return!1;const a=I(e,{home:t,fsImpl:s});if(!a)return!0;try{return Number(s.statSync(a).size)>0}catch{return!0}}export function readCodexThreadUsage(e,{home:t=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:s=r,tailBytes:a=4194304}={}){const i=I(e,{home:t,fsImpl:s});if(!i)return null;let l;try{const e=Number(s.statSync(i).size)||0,t=Math.min(e,Math.max(65536,a)),r=Buffer.alloc(t);l=s.openSync(i,"r"),s.readSync(l,r,0,t,e-t);const n=r.toString("utf8").split("\n");for(let e=n.length-1;e>=0;e--){let t;try{t=JSON.parse(n[e])}catch{continue}if("token_count"!==t?.payload?.type||!t.payload.info)continue;const r=t.payload.info,o=r.last_token_usage||{},s=r.total_token_usage||{},a=Number(o.total_tokens),i=Number(r.model_context_window);return!Number.isFinite(a)||a<0||!Number.isFinite(i)||i<=0?null:{context:{used:Math.floor(a),max:Math.floor(i)},total:{input_tokens:Math.max(0,Number(s.input_tokens)||0),cached_input_tokens:Math.max(0,Number(s.cached_input_tokens)||0),output_tokens:Math.max(0,Number(s.output_tokens)||0)},rateLimits:t.payload.rate_limits||null}}}catch{return null}finally{if(null!=l)try{s.closeSync(l)}catch{}}return null}export function codexUsageSnapshotFromAppServer(e){const t=Number(e?.last?.totalTokens),r=Number(e?.modelContextWindow);if(!Number.isFinite(t)||t<0||!Number.isFinite(r)||r<=0)return null;const n=e?.total||{};return{context:{used:Math.floor(t),max:Math.floor(r)},total:{input_tokens:Math.max(0,Number(n.inputTokens)||0),cached_input_tokens:Math.max(0,Number(n.cachedInputTokens)||0),output_tokens:Math.max(0,Number(n.outputTokens)||0)}}}export function normalizeCodexSandbox(e){return w.has(e)?e:"workspace-write"}export function codexPublishAccess({cwd:t,env:r=process.env,tmpdir:s=n.tmpdir(),execFile:a=e}={}){const i=[];try{const e=String(a("git",["rev-parse","--git-common-dir"],{cwd:t,encoding:"utf8"})||"").trim();if(e){const r=o.resolve(t,e),n=o.resolve(t);r===n||r.startsWith(n+o.sep)||i.push(r)}}catch{}return{writableDirs:i,env:{...r,NPM_CONFIG_CACHE:o.join(s,"thinkpool-npm-cache")}}}export const CODEX_ROOM_CASCADE_REMINDER=["thinkpool ROOM ROLE ENFORCEMENT: obey the authoritative TERMINAL ROLE above. A person-authored request for a new, separate, independent, top-level, or main terminal uses open_main_terminal — NEVER spawn_terminal — even for ordinary work or a handoff. If the wording includes new or separate terminal, main-terminal routing wins. Use spawn_terminal only for agent-decided bounded worker slices or when the person explicitly asks for a worker, sub-terminal, Ensemble lane, or delegated slice. If open_main_terminal is unavailable, say so; never substitute an Ensemble child. Leaf, worker, Side, and managed Flow lanes work directly and must not delegate. Never use hidden in-process subagents."].join(" ");export function buildCodexPrompt({text:e,terminalRolePrompt:t,rolePrompt:r,roomContext:n,firstTurn:o=!1,promptIndex:s=0,forceFullReminder:a=!1}){const i="function"==typeof n?n():n,l=f({promptIndex:s,forceFull:a}),u=[o?c:"",l?t:"",p({text:e,promptIndex:s,forceFull:a}),l?r||CODEX_ROOM_CASCADE_REMINDER:"",i].map(e=>String(e||"").trim()).filter(Boolean);return u.length?`<thinkpool-context>\n${u.join("\n\n")}\n</thinkpool-context>\n\n${String(e??"")}`:String(e??"")}export function buildCodexExecArgs({sessionId:e,model:t,effort:r,sandbox:n,approvalPolicy:s="untrusted",providerConfig:a,mcpUrl:i,writableDirs:l=[],images:u=[],prompt:d}){const c=normalizeCodexSandbox(n),p=e?["exec","resume","--json","--skip-git-repo-check",..."danger-full-access"===c?["--dangerously-bypass-approvals-and-sandbox"]:["-c",`sandbox_mode="${c}"`]]:["exec","--json","--sandbox",c,"--skip-git-repo-check"];if(t&&p.push("-m",t),p.push("-c",`approval_policy="${s}"`),EFFORT_LEVELS.has(r)&&p.push("-c",`model_reasoning_effort="${r}"`),a&&p.push("-c",a),!e)for(const e of l)p.push("--add-dir",e);i&&(p.push("-c",`mcp_servers.thinkpool.url=${JSON.stringify(i)}`),p.push("-c","mcp_servers.thinkpool.required=true"));let m=!1;for(const e of u)o.isAbsolute(String(e||""))&&(p.push("--image",String(e)),m=!0);return m&&p.push("--"),e&&p.push(e),p.push(String(d??"")),p}export function codexExecFailedBeforeTurn({stderr:e="",eventTypes:t=[]}={}){if((Array.isArray(t)?t.map(e=>String(e||"")):[]).some(e=>e&&"thread.started"!==e))return!1;const r=String(e||"");return/timed out handshaking with MCP server/i.test(r)||/thread\/resume failed/i.test(r)&&/failed to initialize session/i.test(r)}const _=/\brm\s+\S|\brmdir\s+\S|\bgit\s+(push\s+(-f|--force)|reset\s+--hard|clean\s+-[a-z]*f)|\bdrop\s+(table|database)\b|\b(mkfs|dd)\b|\bsudo\b|>\s*\/dev\/|\bchmod\s+-R|\bchown\s+-R|\bkillall\b|\btruncate\b/i;export function codexApprovalCard(e,t={},r=null){const n=String(t.approvalId||t.itemId||`${t.turnId||"turn"}:${e}`);if("item/tool/requestUserInput"===e){const e=Array.isArray(t.questions)?t.questions:[];return{id:n,toolName:"AskUserQuestion",input:{questions:e},risk:"ask",questions:e,asker:"Codex",answerFormat:"codex",...Number.isFinite(t.autoResolutionMs)&&t.autoResolutionMs>0?{autoResolutionMs:t.autoResolutionMs}:{}}}if("item/commandExecution/requestApproval"===e){const e=t.command||r?.command||"";return{id:n,toolName:"Bash",input:{command:e,cwd:t.cwd||r?.cwd||"",...t.reason?{reason:t.reason}:{}},risk:_.test(e)?"high":"medium"}}if("item/fileChange/requestApproval"===e){const e=Array.isArray(r?.changes)?r.changes:[],o=e[0]||{};return{id:n,toolName:e.length>1?"MultiEdit":"add"===o.kind?"Write":"Edit",input:e.length>1?{edits:e.map(e=>({file_path:e.path||"",path:e.path||"",kind:e.kind||"update"})),...t.reason?{reason:t.reason}:{}}:{file_path:o.path||t.grantRoot||"",path:o.path||t.grantRoot||"",kind:o.kind||"update",...t.reason?{reason:t.reason}:{}},risk:"medium"}}return null}export function codexApprovalResponse(e){return{decision:"always"===e?"acceptForSession":"allow"===e?"accept":"decline"}}export function codexUserInputResponse(e,t=[]){return h(e,t)}function M(e){if(!e||"object"!=typeof e)return e;const t={...e};switch(e.type){case"agentMessage":return{...t,type:"agent_message"};case"commandExecution":return{...t,type:"command_execution",aggregated_output:e.aggregatedOutput||"",exit_code:e.exitCode};case"fileChange":return{...t,type:"file_change"};case"webSearch":return{...t,type:"web_search"};case"mcpToolCall":return{...t,type:"mcp_tool_call",arguments:e.arguments||{},result:e.result,error:e.error};case"reasoning":return{...t,type:"reasoning",text:[...e.summary||[],...e.content||[]].join("\n")};default:return t}}export function startCodexSession({cwd:e,model:c,effort:p="high",resume:h,env:w,sandbox:k,mode:I="default",onEvent:_,providerConfig:S,terminalRolePrompt:C,rolePrompt:E,roomContext:T,mcpServers:F,prepareCwd:R=null,requestPermission:O,appServerGate:N=i,appServerFactory:A=l,mcpHttpFactory:P=d,spawnImpl:U=t,admitStart:D=null,stallOptions:$={}}){let j=CODEX_MODE_CONFIG[I]?I:"default",L=codexConfigForMode(j);k=normalizeCodexSandbox(k||L.sandbox);const q=codexPublishAccess({cwd:e,env:{...process.env,...w||{}}}),X=q.env,G=h||null,z=!G||codexThreadCanResume(G,{home:X.CODEX_HOME||o.join(n.homedir(),".codex")});let W=z?G:null,B=W?1:0,H=0,V=!0;const J=m(T);let K=null,Q=!1,Y=!1,Z=!1;const ee="function"==typeof $.now?$.now:Date.now,te="function"==typeof $.setInterval?$.setInterval:setInterval,re="function"==typeof $.clearInterval?$.clearInterval:clearInterval,ne=Number.isFinite($.stallMs)?Math.max(1,Number($.stallMs)):Math.max(3e4,parseInt(X.TP_STALL_MS,10)||9e4),oe=Number.isFinite($.forceStopMs)?Math.max(ne,Number($.forceStopMs)):Math.max(3*ne,parseInt(X.TP_FORCE_STOP_MS,10)||3e5),se=Number.isFinite($.intervalMs)?Math.max(1,Number($.intervalMs)):5e3;let ae=ee(),ie=!1,le=!1,ue=!1,de=!1,ce=!0,pe=0,me=null,fe=c||null,he=EFFORT_LEVELS.has(p)?p:"high",xe=W?readCodexThreadUsage(W):null,ge=null;const ye=N({env:X});let ve=!ye.enabled,be=null,we=!1,ke=null,Ie=null,_e=!1;const Me=new Set,Se=new Map,Ce=new Map;let Ee=Promise.resolve(),Te=Promise.resolve();const Fe=[],Re=y(e=>{try{_?.(e)}catch{}}),Oe=()=>{ae=ee(),ie=!1},Ne=e=>{Oe(),Re(e)};Ne.cancel=()=>Re.cancel();const Ae=new u({onEvent:Ne,model:c||null,usageSnapshotForSession:e=>{const t=readCodexThreadUsage(e);return t&&(xe=t),t||xe}}),Pe=e=>{"turn.completed"!==e?.type&&"turn.failed"!==e?.type||(Z=!1),Ae.push(e)},Ue=e=>{Z=!1,Ne(e)},De=({retry:e=!1}={})=>{Z=!0,ae=ee(),ie=!1,ue=!1,de=!1,ce=!0,e||(le=!1)},$e=e=>Ne({kind:"note",text:e}),je=(e=ke)=>{const t=String(e||"");if(t)for(Me.add(t);Me.size>64;)Me.delete(Me.values().next().value)},Le=(e={})=>{const t=String(e.turnId||"");return!(!t||Y||Q||Me.has(t)||t!==String(ke||""))};async function qe(){const e=F?.thinkpool;return e?(ge||(ge=P({sdkServer:e})),ge):null}function Xe(e,t={}){if("thread/tokenUsage/updated"===e){if(t.threadId&&W&&String(t.threadId)!==String(W))return;const e=codexUsageSnapshotFromAppServer(t.tokenUsage);if(!e)return;if(xe=e,_e){const{used:t,max:r}=e.context;Ne({kind:"usage",ctx:{used:t,max:r,pct:Math.min(100,Math.max(0,Math.round(t/r*100))),over:t>r,model:fe}})}return}if("turn/started"===e&&t.turn?.id){const e=String(t.turn.id);if(Y||Q)return void je(e);if(Ie){const t=Ie;return Ie=null,void t.resolve(e)}return ke||(ke=e),void Oe()}if("turn/plan/updated"!==e&&"item/agentMessage/delta"!==e&&"item/started"!==e&&"item/completed"!==e&&"error"!==e||Le(t)){if(Oe(),"turn/plan/updated"===e){const e=(Array.isArray(t.plan)?t.plan:[]).map(({step:e,status:t})=>({content:e||"",activeForm:e||"",status:"inProgress"===t?"in_progress":"completed"===t?"completed":"pending"})).filter(e=>e.content);return void Ne({kind:"assistant",blocks:[{type:"tool_use",id:`codex-plan:${t.turnId||"active"}`,name:"TodoWrite",input:{todos:e}}],parentToolUseId:null})}if("item/agentMessage/delta"===e){if(!t.delta)return;const e=t.itemId||`turn:${t.turnId||ke||"active"}`,r=Ce.get(e)||{cid:`codex-stream:${W||"thread"}:${e}`,text:""};return r.text+=String(t.delta),Ce.set(e,r),void Ne({kind:"assistant_stream",cid:r.cid,text:r.text,itemId:e,streaming:!0})}if("item/started"===e||"item/completed"===e){const r=t.item;if(!r)return;if("item/started"===e&&Se.set(r.id,r),"item/completed"===e&&(Se.set(r.id,r),"agentMessage"===r.type&&Ce.has(r.id))){const e=Ce.get(r.id);return Ce.delete(r.id),void Ne({kind:"assistant",blocks:[{type:"text",text:r.text||e.text}],parentToolUseId:null,replacesCid:e.cid})}return void Pe({type:"item/started"===e?"item.started":"item.completed",item:M(r)})}if("error"===e){const e=t.error?.message||t.message||"codex app-server error";Pe({type:"error",message:e})}}}async function Ge(e,t={}){if(!Le(t))return"item/tool/requestUserInput"===e?{answers:{}}:{decision:"decline"};const r=codexApprovalCard(e,t,Se.get(t.itemId));if(!r)throw new Error(`Unsupported Codex App Server request: ${e}`);let n="deny";pe++;try{n=await(O?.(r))}catch{}finally{pe=Math.max(0,pe-1),Oe()}return"item/tool/requestUserInput"===e?codexUserInputResponse(n,r.questions):codexApprovalResponse(n)}async function ze(){if(ve)return!1;if(!1===be?.alive)return we=!1,ve=!0,!1;if(be&&we)return!0;let t;try{t=await qe()}catch(e){$e(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{return be||(be=A({cwd:e,env:X,args:a({providerConfig:S,mcpUrl:t?.url,writableDirs:codexExtraWritableDirsForMode(j,k,q.writableDirs)}),onNotification:Xe,onServerRequest:Ge,onClose:e=>{Y||ve||(we=!1,ve=!0,$e(`Codex App Server closed; later turns will use codex exec: ${e?.message||e}`))}}),await be.start(),Oe()),W=await be.startThread({threadId:B>0?W:null,cwd:e,model:fe,sandbox:k,approvalPolicy:L.approvalPolicy,config:{"features.default_mode_request_user_input":!0}}),Oe(),t?.url&&(await be.waitForMcpServer({threadId:W,name:"thinkpool"}),Oe()),we=!0,Pe({type:"thread.started",thread_id:W}),!0}catch{ve=!0;try{be?.end()}catch{}return be=null,we=!1,!1}}function We({force:e=!1}={}){const t=be,r=ke;if(t&&e){je(r),ve=!0,we=!1;try{t.end()}catch{}be===t&&(be=null)}else if(t&&r)je(r),Promise.resolve(t.interrupt({threadId:W,turnId:r})).catch(()=>{ve=!0,we=!1;try{t.end()}catch{}be===t&&(be=null)});else if(t){ve=!0,we=!1;try{t.end()}catch{}be===t&&(be=null)}if(K){const e=K;try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(K===e)try{e.kill("SIGKILL")}catch{}},1500)}}async function Be(e,t={}){if(Y||Q||de)return!0;if(!await ze())return!1;if(Y||Q||de)return!0;Ae.setUsageBaseline(W?readCodexThreadUsage(W):null),Z=!0;try{if(ce=!1,ke=await be.startTurn({threadId:W,input:e,images:Array.isArray(t.images)?t.images:[],model:fe,effort:he,approvalPolicy:L.approvalPolicy,collaborationMode:codexDefaultCollaborationMode(fe,he)}),Oe(),Q||Y){je(ke);try{await be.interrupt({threadId:W,turnId:ke})}catch{}return!0}const r=await be.waitForTurn(ke);je(r?.turn?.id||ke);const n=r?.turn?.status;Q?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ue||de)||("interrupted"===n?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ue=!1,Pe({type:"turn.failed",message:r?.turn?.error?.message||"codex turn failed"})):(ue=!1,Pe({type:"turn.completed"})))}catch(e){ve=!0,we=!1,je(ke);try{be?.end()}catch{}be=null,Y||ue||de||Pe({type:"turn.failed",message:`codex app-server turn failed: ${e?.message||e}`})}finally{for(const e of Ce.values())e.text&&Ne({kind:"assistant",blocks:[{type:"text",text:e.text}],parentToolUseId:null,replacesCid:e.cid});ke=null,Z=!1,Se.clear(),Ce.clear()}return!0}async function He(e){if(Y||Q||de)return!0;if(!await ze())return!1;if(Y||Q||de)return!0;Ae.setUsageBaseline(W?readCodexThreadUsage(W):null),Z=!0;try{ce=!1;const t=await be.startReview({threadId:W,target:x(e)});if(ke=t?.turn?.id||t?.turnId,!ke)throw new Error("Codex review/start returned no turn id");if(Oe(),Q||Y){je(ke);try{await be.interrupt({threadId:W,turnId:ke})}catch{}return!0}const r=await be.waitForTurn(ke);je(r?.turn?.id||ke);const n=r?.turn?.status;Q?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ue||de)||("interrupted"===n?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ue=!1,Pe({type:"turn.failed",message:r?.turn?.error?.message||"codex review failed"})):(ue=!1,Pe({type:"turn.completed"})))}catch(e){ve=!0,we=!1,je(ke);try{be?.end()}catch{}be=null,Y||ue||de||Pe({type:"turn.failed",message:`codex review failed: ${e?.message||e}`})}finally{for(const e of Ce.values())e.text&&Ne({kind:"assistant",blocks:[{type:"text",text:e.text}],parentToolUseId:null,replacesCid:e.cid});ke=null,Z=!1,Se.clear(),Ce.clear()}return!0}async function Ve(t,n={}){if(Y||Q||de)return;let o;try{o=await qe()}catch(e){$e(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{await(o?.handoff?.())}catch(e){$e(`thinkpool peer handoff failed: ${e?.message||e}`)}const a=buildCodexExecArgs({sessionId:0===B?null:W,model:fe,effort:he,sandbox:k,approvalPolicy:L.approvalPolicy,providerConfig:S,mcpUrl:o?.url,writableDirs:codexExtraWritableDirsForMode(j,k,q.writableDirs),images:Array.isArray(n.images)?n.images:[],prompt:t});return Ae.setUsageBaseline(W?readCodexThreadUsage(W):null),new Promise(t=>{let n;try{n=r.openSync("/dev/null","r")}catch{}const o={cwd:e,env:X,stdio:["ignore","pipe","pipe"]};if(null!=n&&(o.stdio=[n,"pipe","pipe"]),Z=!0,ce=!1,K=U("codex",a,o),null!=n)try{r.closeSync(n)}catch{}const i=[];s.createInterface({input:K.stdout,crlfDelay:1/0}).on("line",e=>{if(!e.trim())return;let t;try{t=JSON.parse(e)}catch{return}i.push(t.type),"thread.started"===t.type&&t.thread_id&&(W=W||t.thread_id),"turn.completed"!==t.type&&"turn.failed"!==t.type||(u=!0,ue=!1),Pe(t)});let l="",u=!1,d=!1;K.stderr.on("data",e=>{l+=e.toString(),l.length>800&&(l=l.slice(-800))}),K.on("error",e=>{d||(d=!0,K=null,ue||de||Ue({kind:"error",message:`codex failed to start: ${e.message}`,recoverable:!0}),t({preTurnInitFailure:!1}))}),K.on("close",e=>{if(d)return;d=!0,K=null,Z=!1;const r=`codex exec exited ${e}${l?": "+l.trim().slice(-300):""}`;0===e||null==e||u||!codexExecFailedBeforeTurn({stderr:l,eventTypes:i})||Q||de?((!ue&&!de||u||Q)&&(Q&&!u?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}):Q||u||0===e||null==e||Ue({kind:"error",message:r,recoverable:!0})),t({preTurnInitFailure:!1})):t({preTurnInitFailure:!0,message:r})})})}function Je(){if(Y)return;const e=Fe.shift();e&&(Te=Te.then(async()=>{De();const t=f({promptIndex:e.promptIndex,forceFull:e.forceFullReminder}),r=buildCodexPrompt({text:e.text,terminalRolePrompt:C,rolePrompt:E,roomContext:()=>J({force:t}),firstTurn:0===B,promptIndex:e.promptIndex,forceFullReminder:e.forceFullReminder}),n=/^\s*\/review(?:\s|$)/i.test(String(e.text||""));let o=!1,s=!1;for(;!Y&&!Q&&!de;){if(!(n?await He(e.text):await Be(r,e.options)))if(n)ue=!1,Ue({kind:"error",message:"Codex review is unavailable without the tested App Server runtime.",recoverable:!0});else{ue&&(ue=!1,o=!0,De({retry:!0}),Re({kind:"note",text:"retrying the stalled turn on a fresh connection"}));const t=await Ve(r,e.options);if(t?.preTurnInitFailure){if(!(s||Y||Q||de)){s=!0,De({retry:!0}),Re({kind:"note",text:"Codex initialization failed before the turn started; retrying once with a fresh thinkpool connection."});continue}Ue({kind:"error",message:t.message,recoverable:!0})}}if(!ue||o||de||Y||Q)break;ue=!1,o=!0,De({retry:!0}),Re({kind:"note",text:"retrying the stalled turn on a fresh connection"})}B++,Je()}))}return G&&!z&&queueMicrotask(()=>$e("The stopped Codex turn had no resumable context; continuing in a fresh thread.")),["0","false","off"].includes(String(X.TP_CODEX_APP_SERVER||"").trim().toLowerCase())||ye.enabled||queueMicrotask(()=>$e(`Codex App Server disabled: ${ye.reason}`)),me=te(()=>{const e=ee()-ae,t=v({turnActive:Z,awaitingUser:pe,quietMs:e,stallMs:ne,forceStopMs:oe,stalledSent:ie,stallRetried:le});if("none"===t)return;if("retry"===t&&!ce)return de=!0,ue=!1,Re({kind:"error",recoverable:!0,message:"Codex stopped responding after the turn began. The transport was stopped, but the prompt was not replayed automatically."}),Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),void We({force:!0});const r=b(t,e);if(r&&Re(r),"status"!==t){if("retry"===t)return le=!0,ue=!0,ae=ee(),ie=!1,void We();de=!0,ue=!1,Fe.length=0,Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),We({force:!0})}else ie=!0},se),me.unref?.(),{get sessionId(){return W},get usageSnapshot(){return xe},get turnActive(){return Z||_e},get canSteer(){return!(!be||!1===be.alive||!we||ve)},get started(){return B>0||Z},sendTurn(t,r={}){if(Y)return!1;if(!Z&&0===Fe.length&&!we&&!K){const e="function"==typeof D?D():{ok:!0};if(!1===e?.ok)return Ne({kind:"error",message:e.reason||"Host memory is critically low. This Codex runtime was not started.",recoverable:!0}),!1}const n=H++,o=V;if(V=g(t)||/^\s*\/(?:reset|clear)\b/i.test(String(t||"")),!Z&&0===B&&R)try{e=R()||e}catch{}if(Z&&be&&ke){Oe();const e=f({promptIndex:n,forceFull:o}),s=buildCodexPrompt({text:t,terminalRolePrompt:C,rolePrompt:E,roomContext:()=>J({force:e}),promptIndex:n,forceFullReminder:o}),a=ke;return Ee=Ee.then(()=>be.steer({threadId:W,turnId:a,input:s,images:Array.isArray(r.images)?r.images:[]})).catch(e=>{"rejected"===e?.delivery||"not_sent"===e?.delivery?(Fe.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),Z||1!==Fe.length||Je()):Ne({kind:"error",message:"Codex steering delivery is uncertain; the message was not replayed automatically.",recoverable:!0})}),!0}return Fe.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),Z||_e||(Q=!1,De()),1!==Fe.length||_e||Je(),!0},abort(){const e=Te,t=Z&&!ke&&!K;Q=!0,ue=!1,de=!1,Fe.length=0,je(ke);let r=Promise.resolve();if(be&&ke&&(r=be.interrupt({threadId:W,turnId:ke}).catch(()=>{})),K){try{K.kill("SIGTERM")}catch{}setTimeout(()=>{try{K?.kill("SIGKILL")}catch{}},1500)}else t&&Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:0,durationMs:void 0,denials:0,resultText:null});return r.then(()=>e).catch(()=>e).then(()=>{})},end(){if(Y=!0,Z=!1,me&&re(me),Ne.cancel(),Fe.length=0,je(ke),K)try{K.kill()}catch{}try{be?.end()}catch{}ge?.then(e=>e.close()).catch(()=>{})},setModel:e=>!(!e||Z||(fe=String(e),Ae.setModel(fe),0)),setMode:e=>!(Z||!CODEX_MODE_CONFIG[e]||(j=e,L=codexConfigForMode(j),k=L.sandbox,W=null,B=0,xe=null,we=!1,0)),setEffort:e=>!(Z||!EFFORT_LEVELS.has(e)||(he=e,Ne({kind:"effort",level:he}),0)),clearContext:()=>!Z&&(W=null,B=0,xe=null,we=!1,!0),async compactContext(){if(Z||_e)return!1;Q=!1,_e=!0;let e=null,t=!1;try{if(!await ze()||Z)return!1;const r=new Promise((t,r)=>{e=setTimeout(()=>{Ie=null,r(new Error("Codex did not start the compaction turn"))},5e3),Ie={resolve:t,reject:r}});await be.compact({threadId:W}),t=!0;const n=await r;e&&clearTimeout(e);const o=await be.waitForTurn(n);je(o?.turn?.id||n);const s=o?.turn?.status;if("completed"===s)return!0;throw new Error(o?.turn?.error?.message||`compaction ${s||"failed"}`)}catch(e){return t||!["rejected","not_sent"].includes(e?.delivery)?($e(`Native Codex compaction status is uncertain; existing context was preserved: ${e?.message||e}`),null):($e(`Native Codex compaction unavailable; using bounded recap fallback: ${e?.message||e}`),!1)}finally{e&&clearTimeout(e),Ie=null,_e=!1,Fe.length&&Je()}},async accountUsage(){if(!await ze())return null;const[e,t]=await Promise.all([be.accountUsage().catch(()=>null),be.accountRateLimits().catch(()=>null)]);return{usage:e,limits:t}}}}
1
+ import{execFileSync as e,spawn as t}from"node:child_process";import r from"node:fs";import n from"node:os";import o from"node:path";import s from"node:readline";import{buildCodexAppServerArgs as a,codexAppServerGate as i,createCodexAppServer as l}from"./codex-app-server.mjs";import{CodexEventMapper as u}from"./codex-event-mapper.mjs";import{startCodexMcpHttp as d}from"./codex-mcp-http.mjs";import{CODEX_THINKPOOL_FIRST_TURN_PREAMBLE as c,buildThinkPoolTurnGuidance as p,createRoomContextSelector as m,usesFullThinkPoolReminder as f}from"./thinkpool-room-prompt.mjs";import{questionAnswerResponse as h}from"./question-response.mjs";import{codexReviewTarget as x,isCodexCompactCommand as g}from"./codex-commands.mjs";import{createCumulativeEventRelay as y}from"./cumulative-event-relay.mjs";import{stallDecision as v,stallEvent as b}from"./turn-stall.mjs";const w=new Set(["read-only","workspace-write","danger-full-access"]);export const EFFORT_LEVELS=new Set(["low","medium","high","xhigh","max"]);export const CODEX_MODE_CONFIG={default:{sandbox:"workspace-write",approvalPolicy:"untrusted"},acceptEdits:{sandbox:"workspace-write",approvalPolicy:"on-request"},plan:{sandbox:"read-only",approvalPolicy:"never"},review:{sandbox:"read-only",approvalPolicy:"never"},bypassPermissions:{sandbox:"danger-full-access",approvalPolicy:"never"}};export function codexConfigForMode(e){return CODEX_MODE_CONFIG[e]||CODEX_MODE_CONFIG.default}export function codexDefaultCollaborationMode(e,t){return e?{mode:"default",settings:{model:String(e),reasoning_effort:EFFORT_LEVELS.has(t)?t:null,developer_instructions:null}}:null}export function defaultModeForRuntime(e){return"codex"===e?"bypassPermissions":"default"}export function codexWritableDirsForSandbox(e,t=[]){return"read-only"===normalizeCodexSandbox(e)?[]:t}export function codexExtraWritableDirsForMode(e,t,r=[]){return"review"===e?[]:codexWritableDirsForSandbox(t,r)}export function readCodexDefaultModel({home:e=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:t=r}={}){try{const r=t.readFileSync(o.join(e,"config.toml"),"utf8").split(/^\s*\[/m,1)[0].match(/^\s*model\s*=\s*["']([^"']+)["']\s*(?:#.*)?$/m);return r?.[1]?.trim()||null}catch{return null}}export function readCodexModels({home:e=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:t=r}={}){try{const r=JSON.parse(t.readFileSync(o.join(e,"models_cache.json"),"utf8"));return(Array.isArray(r?.models)?r.models:[]).filter(e=>e?.slug&&"hide"!==e.visibility).sort((e,t)=>(Number(e.priority)||999)-(Number(t.priority)||999)).map(e=>({value:e.slug,displayName:e.display_name||e.slug,description:e.description||""}))}catch{return[]}}const k=new Map;function I(e,{home:t,fsImpl:r}){if(!e)return null;const n=`${t}\0${e}`,s=k.get(n);if(s)return s;const a=o.join(t,"sessions");try{const t=`${e}.jsonl`,s=r.readdirSync(a,{recursive:!0,withFileTypes:!0}).find(e=>e.isFile()&&e.name.endsWith(t));if(!s)return null;const i=o.join(s.parentPath||s.path||a,s.name);return k.set(n,i),i}catch{return null}}export function codexThreadCanResume(e,{home:t=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:s=r}={}){if(!e)return!1;const a=I(e,{home:t,fsImpl:s});if(!a)return!0;try{return Number(s.statSync(a).size)>0}catch{return!0}}export function readCodexThreadUsage(e,{home:t=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:s=r,tailBytes:a=4194304}={}){const i=I(e,{home:t,fsImpl:s});if(!i)return null;let l;try{const e=Number(s.statSync(i).size)||0,t=Math.min(e,Math.max(65536,a)),r=Buffer.alloc(t);l=s.openSync(i,"r"),s.readSync(l,r,0,t,e-t);const n=r.toString("utf8").split("\n");for(let e=n.length-1;e>=0;e--){let t;try{t=JSON.parse(n[e])}catch{continue}if("token_count"!==t?.payload?.type||!t.payload.info)continue;const r=t.payload.info,o=r.last_token_usage||{},s=r.total_token_usage||{},a=Number(o.total_tokens),i=Number(r.model_context_window);return!Number.isFinite(a)||a<0||!Number.isFinite(i)||i<=0?null:{context:{used:Math.floor(a),max:Math.floor(i)},total:{input_tokens:Math.max(0,Number(s.input_tokens)||0),cached_input_tokens:Math.max(0,Number(s.cached_input_tokens)||0),output_tokens:Math.max(0,Number(s.output_tokens)||0)},rateLimits:t.payload.rate_limits||null}}}catch{return null}finally{if(null!=l)try{s.closeSync(l)}catch{}}return null}export function codexUsageSnapshotFromAppServer(e){const t=Number(e?.last?.totalTokens),r=Number(e?.modelContextWindow);if(!Number.isFinite(t)||t<0||!Number.isFinite(r)||r<=0)return null;const n=e?.total||{};return{context:{used:Math.floor(t),max:Math.floor(r)},total:{input_tokens:Math.max(0,Number(n.inputTokens)||0),cached_input_tokens:Math.max(0,Number(n.cachedInputTokens)||0),output_tokens:Math.max(0,Number(n.outputTokens)||0)},rateLimits:e?.rateLimits||null}}export function normalizeCodexSandbox(e){return w.has(e)?e:"workspace-write"}export function codexPublishAccess({cwd:t,env:r=process.env,tmpdir:s=n.tmpdir(),execFile:a=e}={}){const i=[];try{const e=String(a("git",["rev-parse","--git-common-dir"],{cwd:t,encoding:"utf8"})||"").trim();if(e){const r=o.resolve(t,e),n=o.resolve(t);r===n||r.startsWith(n+o.sep)||i.push(r)}}catch{}return{writableDirs:i,env:{...r,NPM_CONFIG_CACHE:o.join(s,"thinkpool-npm-cache")}}}export const CODEX_ROOM_CASCADE_REMINDER=["thinkpool ROOM ROLE ENFORCEMENT: obey the authoritative TERMINAL ROLE above. A person-authored request for a new, separate, independent, top-level, or main terminal uses open_main_terminal — NEVER spawn_terminal — even for ordinary work or a handoff. If the wording includes new or separate terminal, main-terminal routing wins. Use spawn_terminal only for agent-decided bounded worker slices or when the person explicitly asks for a worker, sub-terminal, Ensemble lane, or delegated slice. If open_main_terminal is unavailable, say so; never substitute an Ensemble child. Leaf, worker, Side, and managed Flow lanes work directly and must not delegate. Never use hidden in-process subagents."].join(" ");export function buildCodexPrompt({text:e,terminalRolePrompt:t,rolePrompt:r,roomContext:n,firstTurn:o=!1,promptIndex:s=0,forceFullReminder:a=!1}){const i="function"==typeof n?n():n,l=f({promptIndex:s,forceFull:a}),u=[o?c:"",l?t:"",p({text:e,promptIndex:s,forceFull:a}),l?r||CODEX_ROOM_CASCADE_REMINDER:"",i].map(e=>String(e||"").trim()).filter(Boolean);return u.length?`<thinkpool-context>\n${u.join("\n\n")}\n</thinkpool-context>\n\n${String(e??"")}`:String(e??"")}export function buildCodexExecArgs({sessionId:e,model:t,effort:r,sandbox:n,approvalPolicy:s="untrusted",providerConfig:a,mcpUrl:i,writableDirs:l=[],images:u=[],prompt:d}){const c=normalizeCodexSandbox(n),p=e?["exec","resume","--json","--skip-git-repo-check",..."danger-full-access"===c?["--dangerously-bypass-approvals-and-sandbox"]:["-c",`sandbox_mode="${c}"`]]:["exec","--json","--sandbox",c,"--skip-git-repo-check"];if(t&&p.push("-m",t),p.push("-c",`approval_policy="${s}"`),EFFORT_LEVELS.has(r)&&p.push("-c",`model_reasoning_effort="${r}"`),a&&p.push("-c",a),!e)for(const e of l)p.push("--add-dir",e);i&&(p.push("-c",`mcp_servers.thinkpool.url=${JSON.stringify(i)}`),p.push("-c","mcp_servers.thinkpool.required=true"));let m=!1;for(const e of u)o.isAbsolute(String(e||""))&&(p.push("--image",String(e)),m=!0);return m&&p.push("--"),e&&p.push(e),p.push(String(d??"")),p}export function codexExecFailedBeforeTurn({stderr:e="",eventTypes:t=[]}={}){if((Array.isArray(t)?t.map(e=>String(e||"")):[]).some(e=>e&&"thread.started"!==e))return!1;const r=String(e||"");return/timed out handshaking with MCP server/i.test(r)||/thread\/resume failed/i.test(r)&&/failed to initialize session/i.test(r)}const _=/\brm\s+\S|\brmdir\s+\S|\bgit\s+(push\s+(-f|--force)|reset\s+--hard|clean\s+-[a-z]*f)|\bdrop\s+(table|database)\b|\b(mkfs|dd)\b|\bsudo\b|>\s*\/dev\/|\bchmod\s+-R|\bchown\s+-R|\bkillall\b|\btruncate\b/i;export function codexApprovalCard(e,t={},r=null){const n=String(t.approvalId||t.itemId||`${t.turnId||"turn"}:${e}`);if("item/tool/requestUserInput"===e){const e=Array.isArray(t.questions)?t.questions:[];return{id:n,toolName:"AskUserQuestion",input:{questions:e},risk:"ask",questions:e,asker:"Codex",answerFormat:"codex",...Number.isFinite(t.autoResolutionMs)&&t.autoResolutionMs>0?{autoResolutionMs:t.autoResolutionMs}:{}}}if("item/commandExecution/requestApproval"===e){const e=t.command||r?.command||"";return{id:n,toolName:"Bash",input:{command:e,cwd:t.cwd||r?.cwd||"",...t.reason?{reason:t.reason}:{}},risk:_.test(e)?"high":"medium"}}if("item/fileChange/requestApproval"===e){const e=Array.isArray(r?.changes)?r.changes:[],o=e[0]||{};return{id:n,toolName:e.length>1?"MultiEdit":"add"===o.kind?"Write":"Edit",input:e.length>1?{edits:e.map(e=>({file_path:e.path||"",path:e.path||"",kind:e.kind||"update"})),...t.reason?{reason:t.reason}:{}}:{file_path:o.path||t.grantRoot||"",path:o.path||t.grantRoot||"",kind:o.kind||"update",...t.reason?{reason:t.reason}:{}},risk:"medium"}}return null}export function codexApprovalResponse(e){return{decision:"always"===e?"acceptForSession":"allow"===e?"accept":"decline"}}export function codexUserInputResponse(e,t=[]){return h(e,t)}function M(e){if(!e||"object"!=typeof e)return e;const t={...e};switch(e.type){case"agentMessage":return{...t,type:"agent_message"};case"commandExecution":return{...t,type:"command_execution",aggregated_output:e.aggregatedOutput||"",exit_code:e.exitCode};case"fileChange":return{...t,type:"file_change"};case"webSearch":return{...t,type:"web_search"};case"mcpToolCall":return{...t,type:"mcp_tool_call",arguments:e.arguments||{},result:e.result,error:e.error};case"reasoning":return{...t,type:"reasoning",text:[...e.summary||[],...e.content||[]].join("\n")};default:return t}}export function startCodexSession({cwd:e,model:c,effort:p="high",resume:h,env:w,sandbox:k,mode:I="default",onEvent:_,providerConfig:S,terminalRolePrompt:C,rolePrompt:E,roomContext:T,mcpServers:F,prepareCwd:R=null,requestPermission:O,appServerGate:N=i,appServerFactory:A=l,mcpHttpFactory:P=d,spawnImpl:U=t,admitStart:D=null,stallOptions:$={}}){let j=CODEX_MODE_CONFIG[I]?I:"default",L=codexConfigForMode(j);k=normalizeCodexSandbox(k||L.sandbox);const q=codexPublishAccess({cwd:e,env:{...process.env,...w||{}}}),X=q.env,G=h||null,z=!G||codexThreadCanResume(G,{home:X.CODEX_HOME||o.join(n.homedir(),".codex")});let W=z?G:null,B=W?1:0,H=0,V=!0;const J=m(T);let K=null,Q=!1,Y=!1,Z=!1;const ee="function"==typeof $.now?$.now:Date.now,te="function"==typeof $.setInterval?$.setInterval:setInterval,re="function"==typeof $.clearInterval?$.clearInterval:clearInterval,ne=Number.isFinite($.stallMs)?Math.max(1,Number($.stallMs)):Math.max(3e4,parseInt(X.TP_STALL_MS,10)||9e4),oe=Number.isFinite($.forceStopMs)?Math.max(ne,Number($.forceStopMs)):Math.max(3*ne,parseInt(X.TP_FORCE_STOP_MS,10)||3e5),se=Number.isFinite($.intervalMs)?Math.max(1,Number($.intervalMs)):5e3;let ae=ee(),ie=!1,le=!1,ue=!1,de=!1,ce=!0,pe=0,me=null,fe=c||null,he=EFFORT_LEVELS.has(p)?p:"high",xe=W?readCodexThreadUsage(W):null,ge=null;const ye=N({env:X});let ve=!ye.enabled,be=null,we=!1,ke=null,Ie=null,_e=!1;const Me=new Set,Se=new Map,Ce=new Map;let Ee=Promise.resolve(),Te=Promise.resolve();const Fe=[],Re=y(e=>{try{_?.(e)}catch{}}),Oe=()=>{ae=ee(),ie=!1},Ne=e=>{Oe(),Re(e)};Ne.cancel=()=>Re.cancel();const Ae=new u({onEvent:Ne,model:c||null,usageSnapshotForSession:e=>{const t=readCodexThreadUsage(e);return t&&(xe=t),t||xe}}),Pe=e=>{"turn.completed"!==e?.type&&"turn.failed"!==e?.type||(Z=!1),Ae.push(e)},Ue=e=>{Z=!1,Ne(e)},De=({retry:e=!1}={})=>{Z=!0,ae=ee(),ie=!1,ue=!1,de=!1,ce=!0,e||(le=!1)},$e=e=>Ne({kind:"note",text:e}),je=(e=ke)=>{const t=String(e||"");if(t)for(Me.add(t);Me.size>64;)Me.delete(Me.values().next().value)},Le=(e={})=>{const t=String(e.turnId||"");return!(!t||Y||Q||Me.has(t)||t!==String(ke||""))};async function qe(){const e=F?.thinkpool;return e?(ge||(ge=P({sdkServer:e})),ge):null}function Xe(e,t={}){if("thread/tokenUsage/updated"===e){if(t.threadId&&W&&String(t.threadId)!==String(W))return;const e=codexUsageSnapshotFromAppServer(t.tokenUsage);if(!e)return;if(xe=e,_e){const{used:t,max:r}=e.context;Ne({kind:"usage",ctx:{used:t,max:r,pct:Math.min(100,Math.max(0,Math.round(t/r*100))),over:t>r,model:fe}})}return}if("turn/started"===e&&t.turn?.id){const e=String(t.turn.id);if(Y||Q)return void je(e);if(Ie){const t=Ie;return Ie=null,void t.resolve(e)}return ke||(ke=e),void Oe()}if("turn/plan/updated"!==e&&"item/agentMessage/delta"!==e&&"item/started"!==e&&"item/completed"!==e&&"error"!==e||Le(t)){if(Oe(),"turn/plan/updated"===e){const e=(Array.isArray(t.plan)?t.plan:[]).map(({step:e,status:t})=>({content:e||"",activeForm:e||"",status:"inProgress"===t?"in_progress":"completed"===t?"completed":"pending"})).filter(e=>e.content);return void Ne({kind:"assistant",blocks:[{type:"tool_use",id:`codex-plan:${t.turnId||"active"}`,name:"TodoWrite",input:{todos:e}}],parentToolUseId:null})}if("item/agentMessage/delta"===e){if(!t.delta)return;const e=t.itemId||`turn:${t.turnId||ke||"active"}`,r=Ce.get(e)||{cid:`codex-stream:${W||"thread"}:${e}`,text:""};return r.text+=String(t.delta),Ce.set(e,r),void Ne({kind:"assistant_stream",cid:r.cid,text:r.text,itemId:e,streaming:!0})}if("item/started"===e||"item/completed"===e){const r=t.item;if(!r)return;if("item/started"===e&&Se.set(r.id,r),"item/completed"===e&&(Se.set(r.id,r),"agentMessage"===r.type&&Ce.has(r.id))){const e=Ce.get(r.id);return Ce.delete(r.id),void Ne({kind:"assistant",blocks:[{type:"text",text:r.text||e.text}],parentToolUseId:null,replacesCid:e.cid})}return void Pe({type:"item/started"===e?"item.started":"item.completed",item:M(r)})}if("error"===e){const e=t.error?.message||t.message||"codex app-server error";Pe({type:"error",message:e})}}}async function Ge(e,t={}){if(!Le(t))return"item/tool/requestUserInput"===e?{answers:{}}:{decision:"decline"};const r=codexApprovalCard(e,t,Se.get(t.itemId));if(!r)throw new Error(`Unsupported Codex App Server request: ${e}`);let n="deny";pe++;try{n=await(O?.(r))}catch{}finally{pe=Math.max(0,pe-1),Oe()}return"item/tool/requestUserInput"===e?codexUserInputResponse(n,r.questions):codexApprovalResponse(n)}async function ze(){if(ve)return!1;if(!1===be?.alive)return we=!1,ve=!0,!1;if(be&&we)return!0;let t;try{t=await qe()}catch(e){$e(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{return be||(be=A({cwd:e,env:X,args:a({providerConfig:S,mcpUrl:t?.url,writableDirs:codexExtraWritableDirsForMode(j,k,q.writableDirs)}),onNotification:Xe,onServerRequest:Ge,onClose:e=>{Y||ve||(we=!1,ve=!0,$e(`Codex App Server closed; later turns will use codex exec: ${e?.message||e}`))}}),await be.start(),Oe()),W=await be.startThread({threadId:B>0?W:null,cwd:e,model:fe,sandbox:k,approvalPolicy:L.approvalPolicy,config:{"features.default_mode_request_user_input":!0}}),Oe(),t?.url&&(await be.waitForMcpServer({threadId:W,name:"thinkpool"}),Oe()),we=!0,Pe({type:"thread.started",thread_id:W}),!0}catch{ve=!0;try{be?.end()}catch{}return be=null,we=!1,!1}}function We({force:e=!1}={}){const t=be,r=ke;if(t&&e){je(r),ve=!0,we=!1;try{t.end()}catch{}be===t&&(be=null)}else if(t&&r)je(r),Promise.resolve(t.interrupt({threadId:W,turnId:r})).catch(()=>{ve=!0,we=!1;try{t.end()}catch{}be===t&&(be=null)});else if(t){ve=!0,we=!1;try{t.end()}catch{}be===t&&(be=null)}if(K){const e=K;try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(K===e)try{e.kill("SIGKILL")}catch{}},1500)}}async function Be(e,t={}){if(Y||Q||de)return!0;if(!await ze())return!1;if(Y||Q||de)return!0;Ae.setUsageBaseline(W?readCodexThreadUsage(W):null),Z=!0;try{if(ce=!1,ke=await be.startTurn({threadId:W,input:e,images:Array.isArray(t.images)?t.images:[],model:fe,effort:he,approvalPolicy:L.approvalPolicy,collaborationMode:codexDefaultCollaborationMode(fe,he)}),Oe(),Q||Y){je(ke);try{await be.interrupt({threadId:W,turnId:ke})}catch{}return!0}const r=await be.waitForTurn(ke);je(r?.turn?.id||ke);const n=r?.turn?.status;Q?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ue||de)||("interrupted"===n?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ue=!1,Pe({type:"turn.failed",message:r?.turn?.error?.message||"codex turn failed"})):(ue=!1,Pe({type:"turn.completed"})))}catch(e){ve=!0,we=!1,je(ke);try{be?.end()}catch{}be=null,Y||ue||de||Pe({type:"turn.failed",message:`codex app-server turn failed: ${e?.message||e}`})}finally{for(const e of Ce.values())e.text&&Ne({kind:"assistant",blocks:[{type:"text",text:e.text}],parentToolUseId:null,replacesCid:e.cid});ke=null,Z=!1,Se.clear(),Ce.clear()}return!0}async function He(e){if(Y||Q||de)return!0;if(!await ze())return!1;if(Y||Q||de)return!0;Ae.setUsageBaseline(W?readCodexThreadUsage(W):null),Z=!0;try{ce=!1;const t=await be.startReview({threadId:W,target:x(e)});if(ke=t?.turn?.id||t?.turnId,!ke)throw new Error("Codex review/start returned no turn id");if(Oe(),Q||Y){je(ke);try{await be.interrupt({threadId:W,turnId:ke})}catch{}return!0}const r=await be.waitForTurn(ke);je(r?.turn?.id||ke);const n=r?.turn?.status;Q?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ue||de)||("interrupted"===n?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ue=!1,Pe({type:"turn.failed",message:r?.turn?.error?.message||"codex review failed"})):(ue=!1,Pe({type:"turn.completed"})))}catch(e){ve=!0,we=!1,je(ke);try{be?.end()}catch{}be=null,Y||ue||de||Pe({type:"turn.failed",message:`codex review failed: ${e?.message||e}`})}finally{for(const e of Ce.values())e.text&&Ne({kind:"assistant",blocks:[{type:"text",text:e.text}],parentToolUseId:null,replacesCid:e.cid});ke=null,Z=!1,Se.clear(),Ce.clear()}return!0}async function Ve(t,n={}){if(Y||Q||de)return;let o;try{o=await qe()}catch(e){$e(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{await(o?.handoff?.())}catch(e){$e(`thinkpool peer handoff failed: ${e?.message||e}`)}const a=buildCodexExecArgs({sessionId:0===B?null:W,model:fe,effort:he,sandbox:k,approvalPolicy:L.approvalPolicy,providerConfig:S,mcpUrl:o?.url,writableDirs:codexExtraWritableDirsForMode(j,k,q.writableDirs),images:Array.isArray(n.images)?n.images:[],prompt:t});return Ae.setUsageBaseline(W?readCodexThreadUsage(W):null),new Promise(t=>{let n;try{n=r.openSync("/dev/null","r")}catch{}const o={cwd:e,env:X,stdio:["ignore","pipe","pipe"]};if(null!=n&&(o.stdio=[n,"pipe","pipe"]),Z=!0,ce=!1,K=U("codex",a,o),null!=n)try{r.closeSync(n)}catch{}const i=[];s.createInterface({input:K.stdout,crlfDelay:1/0}).on("line",e=>{if(!e.trim())return;let t;try{t=JSON.parse(e)}catch{return}i.push(t.type),"thread.started"===t.type&&t.thread_id&&(W=W||t.thread_id),"turn.completed"!==t.type&&"turn.failed"!==t.type||(u=!0,ue=!1),Pe(t)});let l="",u=!1,d=!1;K.stderr.on("data",e=>{l+=e.toString(),l.length>800&&(l=l.slice(-800))}),K.on("error",e=>{d||(d=!0,K=null,ue||de||Ue({kind:"error",message:`codex failed to start: ${e.message}`,recoverable:!0}),t({preTurnInitFailure:!1}))}),K.on("close",e=>{if(d)return;d=!0,K=null,Z=!1;const r=`codex exec exited ${e}${l?": "+l.trim().slice(-300):""}`;0===e||null==e||u||!codexExecFailedBeforeTurn({stderr:l,eventTypes:i})||Q||de?((!ue&&!de||u||Q)&&(Q&&!u?Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}):Q||u||0===e||null==e||Ue({kind:"error",message:r,recoverable:!0})),t({preTurnInitFailure:!1})):t({preTurnInitFailure:!0,message:r})})})}function Je(){if(Y)return;const e=Fe.shift();e&&(Te=Te.then(async()=>{De();const t=f({promptIndex:e.promptIndex,forceFull:e.forceFullReminder}),r=buildCodexPrompt({text:e.text,terminalRolePrompt:C,rolePrompt:E,roomContext:()=>J({force:t}),firstTurn:0===B,promptIndex:e.promptIndex,forceFullReminder:e.forceFullReminder}),n=/^\s*\/review(?:\s|$)/i.test(String(e.text||""));let o=!1,s=!1;for(;!Y&&!Q&&!de;){if(!(n?await He(e.text):await Be(r,e.options)))if(n)ue=!1,Ue({kind:"error",message:"Codex review is unavailable without the tested App Server runtime.",recoverable:!0});else{ue&&(ue=!1,o=!0,De({retry:!0}),Re({kind:"note",text:"retrying the stalled turn on a fresh connection"}));const t=await Ve(r,e.options);if(t?.preTurnInitFailure){if(!(s||Y||Q||de)){s=!0,De({retry:!0}),Re({kind:"note",text:"Codex initialization failed before the turn started; retrying once with a fresh thinkpool connection."});continue}Ue({kind:"error",message:t.message,recoverable:!0})}}if(!ue||o||de||Y||Q)break;ue=!1,o=!0,De({retry:!0}),Re({kind:"note",text:"retrying the stalled turn on a fresh connection"})}B++,Je()}))}return G&&!z&&queueMicrotask(()=>$e("The stopped Codex turn had no resumable context; continuing in a fresh thread.")),["0","false","off"].includes(String(X.TP_CODEX_APP_SERVER||"").trim().toLowerCase())||ye.enabled||queueMicrotask(()=>$e(`Codex App Server disabled: ${ye.reason}`)),me=te(()=>{const e=ee()-ae,t=v({turnActive:Z,awaitingUser:pe,quietMs:e,stallMs:ne,forceStopMs:oe,stalledSent:ie,stallRetried:le});if("none"===t)return;if("retry"===t&&!ce)return de=!0,ue=!1,Re({kind:"error",recoverable:!0,message:"Codex stopped responding after the turn began. The transport was stopped, but the prompt was not replayed automatically."}),Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),void We({force:!0});const r=b(t,e);if(r&&Re(r),"status"!==t){if("retry"===t)return le=!0,ue=!0,ae=ee(),ie=!1,void We();de=!0,ue=!1,Fe.length=0,Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),We({force:!0})}else ie=!0},se),me.unref?.(),{get sessionId(){return W},get usageSnapshot(){return xe},get turnActive(){return Z||_e},get canSteer(){return!(!be||!1===be.alive||!we||ve)},get started(){return B>0||Z},sendTurn(t,r={}){if(Y)return!1;if(!Z&&0===Fe.length&&!we&&!K){const e="function"==typeof D?D():{ok:!0};if(!1===e?.ok)return Ne({kind:"error",message:e.reason||"Host memory is critically low. This Codex runtime was not started.",recoverable:!0}),!1}const n=H++,o=V;if(V=g(t)||/^\s*\/(?:reset|clear)\b/i.test(String(t||"")),!Z&&0===B&&R)try{e=R()||e}catch{}if(Z&&be&&ke){Oe();const e=f({promptIndex:n,forceFull:o}),s=buildCodexPrompt({text:t,terminalRolePrompt:C,rolePrompt:E,roomContext:()=>J({force:e}),promptIndex:n,forceFullReminder:o}),a=ke;return Ee=Ee.then(()=>be.steer({threadId:W,turnId:a,input:s,images:Array.isArray(r.images)?r.images:[]})).catch(e=>{"rejected"===e?.delivery||"not_sent"===e?.delivery?(Fe.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),Z||1!==Fe.length||Je()):Ne({kind:"error",message:"Codex steering delivery is uncertain; the message was not replayed automatically.",recoverable:!0})}),!0}return Fe.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),Z||_e||(Q=!1,De()),1!==Fe.length||_e||Je(),!0},abort(){const e=Te,t=Z&&!ke&&!K;Q=!0,ue=!1,de=!1,Fe.length=0,je(ke);let r=Promise.resolve();if(be&&ke&&(r=be.interrupt({threadId:W,turnId:ke}).catch(()=>{})),K){try{K.kill("SIGTERM")}catch{}setTimeout(()=>{try{K?.kill("SIGKILL")}catch{}},1500)}else t&&Ue({kind:"result",subtype:"aborted",sessionId:W,model:fe,costUsd:null,usage:null,numTurns:0,durationMs:void 0,denials:0,resultText:null});return r.then(()=>e).catch(()=>e).then(()=>{})},end(){if(Y=!0,Z=!1,me&&re(me),Ne.cancel(),Fe.length=0,je(ke),K)try{K.kill()}catch{}try{be?.end()}catch{}ge?.then(e=>e.close()).catch(()=>{})},setModel:e=>!(!e||Z||(fe=String(e),Ae.setModel(fe),0)),setMode:e=>!(Z||!CODEX_MODE_CONFIG[e]||(j=e,L=codexConfigForMode(j),k=L.sandbox,W=null,B=0,xe=null,we=!1,0)),setEffort:e=>!(Z||!EFFORT_LEVELS.has(e)||(he=e,Ne({kind:"effort",level:he}),0)),clearContext:()=>!Z&&(W=null,B=0,xe=null,we=!1,!0),async compactContext(){if(Z||_e)return!1;Q=!1,_e=!0;let e=null,t=!1;try{if(!await ze()||Z)return!1;const r=new Promise((t,r)=>{e=setTimeout(()=>{Ie=null,r(new Error("Codex did not start the compaction turn"))},5e3),Ie={resolve:t,reject:r}});await be.compact({threadId:W}),t=!0;const n=await r;e&&clearTimeout(e);const o=await be.waitForTurn(n);je(o?.turn?.id||n);const s=o?.turn?.status;if("completed"===s)return!0;throw new Error(o?.turn?.error?.message||`compaction ${s||"failed"}`)}catch(e){return t||!["rejected","not_sent"].includes(e?.delivery)?($e(`Native Codex compaction status is uncertain; existing context was preserved: ${e?.message||e}`),null):($e(`Native Codex compaction unavailable; using bounded recap fallback: ${e?.message||e}`),!1)}finally{e&&clearTimeout(e),Ie=null,_e=!1,Fe.length&&Je()}},async accountUsage(){if(!await ze())return null;const[e,t]=await Promise.all([be.accountUsage().catch(()=>null),be.accountRateLimits().catch(()=>null)]);return{usage:e,limits:t}}}}
@@ -1 +1 @@
1
- const e=(e,t,n,i,r=["claude","codex","hermes"])=>Object.freeze({name:e,description:t,route:n,...i?{inputHint:i}:{},runtimes:Object.freeze(r)});export const CODE_ROOM_COMMANDS=Object.freeze([e("/side","investigate beside this terminal","side","task"),e("/help","list commands available in this lane","control"),e("/status","runtime, model, permissions, and busy state","control"),e("/usage","session usage and provider limits","control"),e("/context","current context-window usage","control"),e("/diff","Git changes and branch status","control"),e("/find","find relevant files in this repository","control","query"),e("/compact","compact context","runtime"),e("/clear","clear context · confirms","clear"),e("/model","pick model — opens selector","model"),e("/mode","set permission mode","mode","normal | auto-accept | plan | bypass"),e("/effort","set reasoning effort","effort","low | medium | high | xhigh | max"),e("/queue","run a prompt after the active turn","queue","prompt"),e("/steer","guide the active turn","steer","prompt",["codex","hermes"]),e("/credits","provider credit balance","credits",null,["codex","hermes"]),e("/review","review uncommitted changes","runtime",null,["codex"])]);const t=Object.freeze({claude:Object.freeze({accept:!0,hidden:Object.freeze(new Set(["/flow"]))}),codex:Object.freeze({accept:!1,hidden:Object.freeze(new Set(["/flow"]))}),hermes:Object.freeze({accept:!0,hidden:Object.freeze(new Set(["/flow","/reasoning"]))})}),n=e=>{const t=(e=>{const t=`/${String(("string"==typeof e?e:e?.name)||"").trim().replace(/^\/+/,"")}`;return/^\/[A-Za-z0-9][A-Za-z0-9:_-]*$/.test(t)?t:""})(e);if(!t)return null;if("string"==typeof e)return{name:t,description:"runtime command",route:"runtime"};const n=String(e?.description||"").trim()||"runtime command",i=String(e?.inputHint||e?.input_hint||e?.input?.hint||"").trim();return{name:t,description:n,route:String(e?.route||"").trim()||"runtime",...i?{inputHint:i}:{}}};export function commandCatalogForRuntime(e,i=[]){const r=(e=>"thinkpool"===e?"hermes":e)(e),o=t[r]||t.codex,c=new Map;for(const e of CODE_ROOM_COMMANDS)e.runtimes.includes(r)&&c.set(e.name,{name:e.name,description:e.description,route:e.route,...e.inputHint?{inputHint:e.inputHint}:{}});for(const e of Array.isArray(i)?i:[]){const t=n(e);if(!t||!o.accept||o.hidden.has(t.name))continue;const i=c.get(t.name),s="hermes"===r&&["/help","/status","/context","/credits"].includes(t.name);c.set(t.name,i?{...t,...i,description:"runtime command"===t.description?i.description:t.description,...t.inputHint?{inputHint:t.inputHint}:{},...s?{route:"runtime"}:{}}:t)}return[...c.values()]}export const commandNeedsInput=e=>!!String(e?.inputHint||"").trim();export const commandRoute=e=>String(e?.route||"runtime");export function commandHelpLine(e){return(Array.isArray(e)?e:[]).map(e=>`${e.name}${e.inputHint?` <${e.inputHint}>`:""}`).join(" ")}export function normalizePermissionMode(e){const t=String(e||"").trim().toLowerCase().replace(/[\s_-]+/g,"");return["normal","default","manual"].includes(t)?"default":["auto","autoaccept","autoacceptedits","acceptedits"].includes(t)?"acceptEdits":"plan"===t?"plan":["bypass","bypasspermissions","fullauto"].includes(t)?"bypassPermissions":null}export function reconcileCommandCatalog({current:e,incoming:t,onChanged:n,onLifecycle:i}={}){const r=Array.isArray(t)&&t.length>0,o=r&&JSON.stringify(e||[])===JSON.stringify(t);return r&&!o&&n?.(t),i?.({duplicate:o,hasIncoming:r}),o}
1
+ const e=(e,t,n,i,r=["claude","codex","hermes"])=>Object.freeze({name:e,description:t,route:n,...i?{inputHint:i}:{},runtimes:Object.freeze(r)});export const CODE_ROOM_COMMANDS=Object.freeze([e("/side","ask or investigate without interrupting this terminal","side","task"),e("/help","list commands available in this lane","control"),e("/status","runtime, model, permissions, and busy state","control"),e("/usage","session usage and provider limits","control"),e("/context","current context-window usage","control"),e("/diff","Git changes and branch status","control"),e("/find","find relevant files in this repository","control","query"),e("/compact","compact context","runtime"),e("/clear","clear context · confirms","clear"),e("/model","pick model — opens selector","model"),e("/mode","set permission mode","mode","normal | auto-accept | plan | bypass"),e("/effort","set reasoning effort","effort","low | medium | high | xhigh | max"),e("/queue","run a prompt after the active turn","queue","prompt"),e("/steer","guide the active turn","steer","prompt",["codex","hermes"]),e("/credits","provider credit balance","credits",null,["codex","hermes"]),e("/review","review uncommitted changes","runtime",null,["codex"])]);const t=Object.freeze({claude:Object.freeze({accept:!0,hidden:Object.freeze(new Set(["/flow"]))}),codex:Object.freeze({accept:!1,hidden:Object.freeze(new Set(["/flow"]))}),hermes:Object.freeze({accept:!0,hidden:Object.freeze(new Set(["/flow","/reasoning"]))})}),n=e=>{const t=(e=>{const t=`/${String(("string"==typeof e?e:e?.name)||"").trim().replace(/^\/+/,"")}`;return/^\/[A-Za-z0-9][A-Za-z0-9:_-]*$/.test(t)?t:""})(e);if(!t)return null;if("string"==typeof e)return{name:t,description:"runtime command",route:"runtime"};const n=String(e?.description||"").trim()||"runtime command",i=String(e?.inputHint||e?.input_hint||e?.input?.hint||"").trim();return{name:t,description:n,route:String(e?.route||"").trim()||"runtime",...i?{inputHint:i}:{}}};export function commandCatalogForRuntime(e,i=[]){const r=(e=>"thinkpool"===e?"hermes":e)(e),o=t[r]||t.codex,c=new Map;for(const e of CODE_ROOM_COMMANDS)e.runtimes.includes(r)&&c.set(e.name,{name:e.name,description:e.description,route:e.route,...e.inputHint?{inputHint:e.inputHint}:{}});for(const e of Array.isArray(i)?i:[]){const t=n(e);if(!t||!o.accept||o.hidden.has(t.name))continue;const i=c.get(t.name),s="hermes"===r&&["/help","/status","/context","/credits"].includes(t.name);c.set(t.name,i?{...t,...i,description:"runtime command"===t.description?i.description:t.description,...t.inputHint?{inputHint:t.inputHint}:{},...s?{route:"runtime"}:{}}:t)}return[...c.values()]}export const commandNeedsInput=e=>!!String(e?.inputHint||"").trim();export const commandRoute=e=>String(e?.route||"runtime");export function commandHelpLine(e){return(Array.isArray(e)?e:[]).map(e=>`${e.name}${e.inputHint?` <${e.inputHint}>`:""}`).join(" ")}export function normalizePermissionMode(e){const t=String(e||"").trim().toLowerCase().replace(/[\s_-]+/g,"");return["normal","default","manual"].includes(t)?"default":["auto","autoaccept","autoacceptedits","acceptedits"].includes(t)?"acceptEdits":"plan"===t?"plan":["bypass","bypasspermissions","fullauto"].includes(t)?"bypassPermissions":null}export function reconcileCommandCatalog({current:e,incoming:t,onChanged:n,onLifecycle:i}={}){const r=Array.isArray(t)&&t.length>0,o=r&&JSON.stringify(e||[])===JSON.stringify(t);return r&&!o&&n?.(t),i?.({duplicate:o,hasIncoming:r}),o}
@@ -1 +1 @@
1
- import{createHash as e}from"node:crypto";import{BLOCKED_REASON as t,projectLaneLifecycle as r}from"./lane-lifecycle.mjs";export const PEEK={defaultLines:20,maxLines:200,perTurnCap:10,rosterPerTurnCap:1,lineCap:200,rosterPreview:100};export const readTerminalBudgetDecision=({targeted:e=!1,targetedCount:t=0,rosterCount:r=0}={},n=PEEK)=>{const o=Math.max(0,Number(t)||0),i=Math.max(0,Number(r)||0);return e?o>=n.perTurnCap?{ok:!1,targetedCount:o,rosterCount:i,reason:`Cross-terminal transcript read limit reached for this turn (${n.perTurnCap}). Stop polling and continue with the worker results already collected; a fresh accepted room turn resets the allowance.`}:{ok:!0,targetedCount:o+1,rosterCount:i}:i>=n.rosterPerTurnCap?{ok:!1,targetedCount:o,rosterCount:i,reason:`The full terminal roster was already read this turn (${n.rosterPerTurnCap}/${n.rosterPerTurnCap}). Use the current ROOM NOW snapshot or read one relevant terminal by ref or name.`}:{ok:!0,targetedCount:o,rosterCount:i+1}};export const readTerminalTurnBudgetDecision=({targeted:e=!1,targetedCount:t=0,rosterCount:r=0,budgetTurnRev:n=null,currentTurnRev:o=0}={},i=PEEK)=>{const s=Math.max(0,Number(o)||0),a=(null==n?null:Number.isFinite(Number(n))?Number(n):null)===s;return{...readTerminalBudgetDecision({targeted:e,targetedCount:a?t:0,rosterCount:a?r:0},i),budgetTurnRev:s}};export const LANE_STATUS={stuckAfterMs:9e4,repeatedFailureCount:2,failureWindow:12};const n=e=>!!e&&("error"===e.kind||"tool_result"===e.kind&&e.isError||"result"===e.kind&&e.subtype&&"success"!==e.subtype);export const laneBusyOf=(e={})=>"boolean"==typeof e?.busy?e.busy:"boolean"==typeof e?._busyAnn?e._busyAnn:e?.session?.turnActive??!1;export const settleLaneBusy=e=>!(!e||!1===e._busyAnn||(e._busyAnn=!1,0));export const settleLaneControl=(e,t=Date.now())=>!(!e||!0!==e._busyAnn||!settleLaneBusy(e)||(e._settledTurnRev=Number(e._turnRev)||null,e._turnSettledAt=Number(t)||Date.now(),e._turnStart=null,0));export const laneStatusOf=(e={},o=Date.now(),i=LANE_STATUS)=>{const s=Array.isArray(e?.log)?e.log:[],a=s.slice(-i.failureWindow);!e?.lastEvent||e.lastEvent===s[s.length-1]||e.lastEvent.cid&&e.lastEvent.cid===s[s.length-1]?.cid||a.push(e.lastEvent);let l=null;for(let e=a.length-1;e>=0;e--)if(Number.isFinite(a[e]?.ts)){l=a[e].ts;break}const c=Number.isFinite(e?.lastActionAt)?e.lastActionAt:l??null,d=null==c?null:Math.max(0,o-c),p=laneBusyOf(e),u=e?.pending instanceof Map?e.pending.size:Math.max(0,Number(e?.pendingCount)||0),m=e?.pending instanceof Map&&[...e.pending.values()].some(e=>"ask"===e?.payload?.risk)?t.NEEDS_INPUT:t.NEEDS_DECISION,h=((e,t=LANE_STATUS)=>{let r=0,o=0;for(let i=(Array.isArray(e)?e.length:0)-1;i>=0&&o<t.failureWindow;i--){const t=e[i];if(t&&"object"==typeof t){if(o++,"result"===t.kind&&(!t.subtype||"success"===t.subtype)||"you"===t.kind)break;n(t)&&r++}}return r})(a,i);let f=null,g=null;return u>0?(f="BLOCKED",g="waiting for input"):e?.stalled||p&&!e?.compacting&&null!=d&&d>=i.stuckAfterMs?(f="STUCK",g="no events while busy"):h>=i.repeatedFailureCount&&(f="BLOCKED",g="repeated errors"),{busy:!!p,lastActionAt:c,lastActionAgeMs:d,statusFlag:f,statusReason:g,lifecycle:r({events:a,busy:!!p,pendingCount:u,pendingReason:m,stalled:"STUCK"===f,failed:h>=i.repeatedFailureCount&&!p,rawStatus:f||(p?"busy":"idle")})}};export const formatLaneAge=e=>{if(!Number.isFinite(e))return"no activity";const t=Math.max(0,Math.floor(e/1e3));if(t<60)return`${t}s ago`;const r=Math.floor(t/60);if(r<60)return`${r}m ago`;const n=Math.floor(r/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`};const o=e=>`${e.lifecycle?.state||"Blocked"}${e.lifecycle?.reason?` (${e.lifecycle.reason})`:""} · ${formatLaneAge(e.lastActionAgeMs)}`,i=(e,t)=>{const r=String(null==e?"":e).replace(/\s+/g," ").trim();return r.length>t?r.slice(0,t-1)+"…":r};export const digestLogEvent=(e,t=PEEK.lineCap)=>{if(!e||"object"!=typeof e)return"";switch(e.kind){case"you":return i(`${e.by||"someone"}: ${e.text||""}`,t);case"assistant":{const r=[];for(const t of Array.isArray(e.blocks)?e.blocks:[])"text"===t?.type&&String(t.text||"").trim()?r.push(String(t.text).trim()):"tool_use"===t?.type&&t.name&&r.push(`→ ${t.name}`);return r.length?i(`assistant: ${r.join(" ")}`,t):""}case"tool_result":return e.isError?i("⎿ error",t):"";case"result":return e.subtype&&"success"!==e.subtype?i(`turn ended — ${e.subtype}`,t):"";case"error":return i(`error: ${e.message||""}`,t);default:return""}};export const cleanPtyTail=(e,t)=>String(e||"").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g,"").replace(/\x1b[@-_][0-?]*[ -/]*[@-~]/g,"").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"").split(/\r?\n/).map(e=>e.replace(/\s+$/,"")).filter(e=>e.trim()).slice(-Math.max(1,t));export const siblingRef=e=>String(e).slice(0,8);export const siblingsOf=({selfId:e,sessions:t,terms:r,names:n={},now:o=Date.now()}={})=>{const i=e=>n&&n[e]||"",s=[];for(const[r,n]of t||new Map)r!==e&&s.push({id:r,ref:siblingRef(r),name:i(r),kind:"agent",cmd:n?.cmd||"claude",log:Array.isArray(n?.log)?n.log:[],...laneStatusOf(n,o)});for(const[t,n]of r||new Map)t!==e&&s.push({id:t,ref:siblingRef(t),name:i(t),kind:"shell",cmd:n?.cmd||"shell",buf:n?.buf||"",...laneStatusOf(n,o)});return s};export const isOrdinalRef=e=>/^(terminal\s*)?\d+$/i.test(e)||/^t\d+$/i.test(e);export const resolveSibling=(e,t)=>{const r=String(t||"").toLowerCase().trim();if(!r)return null;const n=Array.isArray(e)?e:[];return n.find(e=>String(e.id).toLowerCase()===r)||n.find(e=>String(e.name||"").toLowerCase()===r)||n.find(e=>String(e.ref).toLowerCase()===r)||(isOrdinalRef(r)?null:n.find(e=>r.length>=4&&String(e.id).toLowerCase().startsWith(r))||n.find(e=>e.name&&String(e.name).toLowerCase().includes(r))||n.find(e=>String(e.cmd).toLowerCase().includes(r))||null)};const s=e=>`${e.ref}${e.name?` · "${e.name}"`:""}`;export const formatPeek=({selfId:e,sessions:t,terms:r,names:n={},terminal:a,lines:l,now:c=Date.now()}={})=>{const d=Math.min(PEEK.maxLines,Math.max(1,Number(l)||PEEK.defaultLines)),p=siblingsOf({selfId:e,sessions:t,terms:r,names:n,now:c});if(!p.length)return"No other terminals are open in this room right now.";if(!a)return`Other terminals in this room (call read_terminal with a ref or name to read one):\n${p.map(e=>{let t;if("agent"===e.kind){const r=e.log.map(e=>digestLogEvent(e,PEEK.rosterPreview)).filter(Boolean);t=r[r.length-1]||"(no activity yet)"}else t=cleanPtyTail(e.buf,1)[0]||"(no output yet)";return`- ${s(e)} · ${e.kind} · ${e.cmd} · ${o(e)} — ${i(t,PEEK.rosterPreview)}`}).join("\n")}`;const u=resolveSibling(p,a);if(!u)return`No terminal matches "${a}". Call read_terminal with no argument to list the open terminals (refer to one by its name or ref).${isOrdinalRef(String(a||"").toLowerCase().trim())?" On-screen numbers like \"Terminal N\" shift when a terminal closes, so they can't identify a lane — use a terminal's name or ref instead.":""}`;let m;return m="agent"===u.kind?u.log.map(e=>digestLogEvent(e,PEEK.lineCap)).filter(Boolean).slice(-d).join("\n")||"(no activity yet)":cleanPtyTail(u.buf,d).join("\n")||"(no output yet)",`Terminal ${s(u)} (${u.kind} · ${u.cmd}) · ${o(u)} — last ${d} lines:\n${m}`};export const nativeClaudeProviderAccessFailed=e=>{let t="";try{t="string"==typeof e?e:JSON.stringify(e||"")}catch{t=String(e||"")}return/organization has disabled (?:claude(?: subscription access)?|subscription access)|(?:claude )?subscription access (?:has been |is )?disabled|invalid anthropic (?:api )?key|invalid x-api-key|not logged in to claude|(?:run|use) \/login[^\n]{0,80}claude|no valid anthropic (?:api )?key/i.test(t)};const a=Object.freeze(["nous:anthropic/claude-sonnet-4.6","nous:anthropic/claude-sonnet-4.5","nous:anthropic/claude-sonnet-4"]);export const preferredHermesClaudeReviewModel=(e=[])=>{const t=new Set((Array.isArray(e)?e:[]).map(e=>"string"==typeof e?e:e?.value||e?.slug).filter(Boolean));for(const e of a)if(t.has(e))return e;for(const e of t)if(/^nous:anthropic\/claude-sonnet-/i.test(e))return e;for(const e of t)if(/^nous:anthropic\/claude-haiku-/i.test(e))return e;for(const e of t)if(/^nous:anthropic\/claude-opus-/i.test(e))return e;return null};export const nativeClaudeFallbackHint=({parentRuntime:e,parentModels:t,targetRuntime:r,targetLog:n}={})=>{if("claude"!==r||!nativeClaudeProviderAccessFailed(n))return"";const o="thinkpool provider status: native Claude access failed at the account/provider level. Do not retry Sonnet, Opus, Haiku, or another model alias on the same native Claude provider during this task.",i="hermes"===e?preferredHermesClaudeReviewModel(t):null;return i?`\n\n${o} For a visible manual review fallback, spawn exactly one worker with runtime="hermes", model=${JSON.stringify(i)}, mode="default", and sliceType="review". This Hermes lane is permission-gated, not structurally read-only: instruct it not to edit, deny/stop any write request, and do not treat its review as a Flow safety proof. Never use Hermes delegate_task.`:`\n\n${o} Use a different available runtime/provider once, or report the blocker.`};export const ROOMNOW={laneCap:8,previewLen:70,wtCap:10};export const formatRoomNow=({selfId:e,sessions:t,terms:r,names:n={},worktrees:a=[],now:l=Date.now()}={},c=ROOMNOW)=>{const d=siblingsOf({selfId:e,sessions:t,terms:r,names:n,now:l}),p=[];if(d.length){const e=d.slice(0,c.laneCap),t=e.map(e=>{let t;if("agent"===e.kind){const r=e.log.map(e=>digestLogEvent(e,c.previewLen)).filter(Boolean);t=r[r.length-1]||"(no activity yet)"}else t=cleanPtyTail(e.buf,1)[0]||"(no output yet)";return`- ${s(e)} · ${e.kind} · ${o(e)} — ${i(t,c.previewLen)}`});d.length>e.length&&t.push(`- … +${d.length-e.length} more (read_terminal lists all)`),p.push(`Other lanes in this room right now (this is the roster; use targeted read_terminal only for needed detail):\n${t.join("\n")}`)}const u=Array.isArray(a)?a:[],m=u.slice(0,c.wtCap);if(m.length){const e=m.map(e=>`- ${i(e,c.previewLen+30)}`);u.length>m.length&&e.push(`- … +${u.length-m.length} more (\`git worktree list\` shows all)`),p.push(`Active git worktrees on this host (code edits go in your OWN worktree — never one another lane is on):\n${e.join("\n")}`)}return p.length?`ROOM NOW (live snapshot, this turn):\n${p.join("\n")}`:null};export const CROSSPOST={maxHop:1,perTurnCap:3};export const crossPostDecision=({hop:e=0,postCount:t=0,disabled:r=!1}={},n=CROSSPOST)=>{const o={...CROSSPOST,...n||{}};return r?{ok:!1,reason:"Cross-terminal posting is disabled in this room."}:e>=o.maxHop?{ok:!1,reason:"Cross-terminal hop limit reached — an agent reached via cross-post cannot itself post onward; a person must direct the next cross-terminal action."}:t>=o.perTurnCap?{ok:!1,reason:`Cross-terminal post limit reached for this turn (${o.perTurnCap}).`}:{ok:!0}};export const crossPostNeedsCard=({mode:e="default",spawnedByMe:t=!1}={})=>!("bypassPermissions"===e||!0===t);export const SPAWN={maxHop:1,perWindowCap:6,windowMs:6e5,freeMaxSpawned:3,plusMaxSpawned:6,machineMax:12};export const maxSpawnedFor=(e,t=SPAWN)=>{const r={...SPAWN,...t||{}};return"plus"===e?r.plusMaxSpawned:r.freeMaxSpawned};export const recentSpawnCount=(e,t,r=SPAWN.windowMs)=>(Array.isArray(e)?e:[]).filter(e=>Number.isFinite(e)&&e>t-r).length;export const spawnDecision=({hop:e=0,spawnTimes:t=[],now:r=0,spawnedLive:n=0,totalLive:o=0,plan:i="free",disabled:s=!1}={},a=SPAWN)=>{const l={...SPAWN,...a||{}};if(s)return{ok:!1,reason:"Spawning terminals is disabled in this room."};if(e>=l.maxHop)return{ok:!1,reason:"Spawn limit reached — a terminal reached via cross-terminal cannot itself open further terminals; a person must open the next lane."};if(o>=l.machineMax)return{ok:!1,reason:`Room is at the machine cap (${l.machineMax} live lanes) — close one before opening another.`};const c=maxSpawnedFor(i,l);if(n>=c)return{ok:!1,reason:`Dispatch ceiling reached (${c} lane${1===c?"":"s"} on the ${i} plan)${"plus"!==i?` — Plus raises it to ${l.plusMaxSpawned}.`:"."} Close a dispatched lane to open another.`};if(recentSpawnCount(t,r,l.windowMs)>=l.perWindowCap){const e=Math.round(l.windowMs/6e4);return{ok:!1,reason:`Spawn burst limit reached (${l.perWindowCap} in ${e} min) — this replenishes as the window slides forward, or instantly when a person types into this lane.`}}return{ok:!0}};const l=Object.freeze(["mode","model","name","provider","runtime","sliceType","task"]),c=new Set(["default","acceptEdits","bypassPermissions","plan","review"]),d=new Set(["claude","codex","hermes"]),p=new Set(["scaffold","feature","fix","review"]),u=/((?<![a-z0-9])sk-[a-z0-9_-]{8,}|(?<![a-z0-9])gsk_[a-z0-9_-]{8,}|(?<![a-z0-9])AIza[a-z0-9_-]{8,}|bearer\s+[a-z0-9._-]{8,})/gi,m=/(?:\/home\/|\/users\/|\/private\/|\/tmp\/|[a-z]:\\|\\\\)/gi,h=e=>Array.isArray(e)?e.map(h):e&&"object"==typeof e?Object.fromEntries(Object.keys(e).sort().map(t=>[t,h(e[t])])):e,f=(e,t)=>null==e?null:String(e).slice(0,t),g=(e,t)=>f(e,t)?.replace(u,"[credential redacted]").replace(m,"[host path redacted]")||"";export function normalizeDispatchArgs(e={},t={}){if(!e||"object"!=typeof e||Array.isArray(e)||Object.keys(e).some(e=>!l.includes(e)))throw new TypeError("Invalid dispatch arguments");const r=e.runtime||t.runtime||"claude",n=e.mode||("plan"===t.mode?"default":t.mode)||"default";if(!d.has(r)||!c.has(n)||e.sliceType&&!p.has(e.sliceType))throw new TypeError("Invalid dispatch arguments");return Object.freeze({name:f(e.name,80),task:f(e.task,12e3),model:f(e.model,160),sliceType:f(e.sliceType,24),runtime:r,provider:f(e.provider,160),mode:n})}export function dispatchCapSnapshot({hop:e=0,spawnTimes:t=[],now:r=0,spawnedLive:n=0,totalLive:o=0,plan:i="free",disabled:s=!1}={},a=SPAWN){const l={...SPAWN,...a||{}},c="plus"===i?"plus":"free",d=maxSpawnedFor(c,l);return Object.freeze({killSwitch:!0===s,hop:Object.freeze({used:Math.max(0,Number(e)||0),limit:l.maxHop}),width:Object.freeze({requested:1,live:Math.max(0,Number(n)||0),limit:d}),machine:Object.freeze({live:Math.max(0,Number(o)||0),limit:l.machineMax}),plan:Object.freeze({name:c,price:"plus"===c?"€5/mo":"€0",used:Math.max(0,Number(n)||0),limit:d}),burst:Object.freeze({used:recentSpawnCount(t,r,l.windowMs),limit:l.perWindowCap,windowMinutes:Math.round(l.windowMs/6e4)})})}export function buildDispatchPreview({args:t,initiatorTerminalId:r,roomCode:n,bridgeAuthorityId:o,localAuthorityId:i,context:s={},limits:a=SPAWN}={}){if(!(r&&n&&o&&i))throw new TypeError("Dispatch authority is incomplete");const l=normalizeDispatchArgs(t,{runtime:s.runtime,mode:s.mode}),c=dispatchCapSnapshot(s,a),d=(p={authority:Object.freeze({initiatorTerminalId:String(r),roomCode:String(n).toUpperCase(),bridgeAuthorityId:String(o),localAuthorityId:String(i)}),exactArgs:l,caps:c},e("sha256").update(JSON.stringify(h(p))).digest("hex"));var p;const u=l.provider||("codex"===l.runtime?"OpenAI / Codex login":"hermes"===l.runtime?"Hermes ACP profile":"Anthropic / host default"),m=l.model||s.resolvedModel||"runtime default",f=g(l.name||l.task?.split(/\r?\n/)[0]||"Open one worker lane",120);return Object.freeze({version:1,fingerprint:d,purpose:f,packages:Object.freeze([{title:g(l.name||"Worker package",80),task:g(l.task||"Open idle for a later hand-off.",600)}]),runtime:Object.freeze({name:l.runtime,provider:g(s.providerName||u,120),model:g(m,160),band:"Start: now–1 min · completion: task-dependent",execution:"Runs locally on the bridge machine; no cloud worker is created.",accountAndSpend:`Uses the host’s ${g(s.providerName||u,100)} account/BYOK. Provider usage is the spend source; thinkpool has no credit meter.`}),permissions:Object.freeze({mode:l.mode,consequence:"bypassPermissions"===l.mode?"Unattended local access within this worker’s sandbox and tool policy.":"review"===l.mode?"Read-only inspection of the pinned parent snapshot; writes and approval escapes are unavailable.":"plan"===l.mode?"Planning only; execution remains gated.":"Protected local actions keep their normal permission gates."}),worktree:"Automatically creates one visible lateral worker lane and an isolated git worktree on this machine.",caps:c})}export function authorizeDispatchApproval({preview:e,approvedFingerprint:t,args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a={},decision:l="allow",consumed:c=!1,permissionId:d=null,consumedPermissionId:p=null,limits:u=SPAWN}={}){return"allow"!==l?{ok:!1,code:"dispatch_canceled"}:c?{ok:!1,code:d&&d===p?"idempotent_replay":"approval_replayed"}:y({preview:e,approvedFingerprint:t,args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a,limits:u})}export function authorizeDirectDispatch({preview:e,args:t,initiatorTerminalId:r,roomCode:n,bridgeAuthorityId:o,localAuthorityId:i,context:s={},limits:a=SPAWN}={}){return y({preview:e,approvedFingerprint:e?.fingerprint,args:t,initiatorTerminalId:r,roomCode:n,bridgeAuthorityId:o,localAuthorityId:i,context:s,limits:a})}function y({preview:e,approvedFingerprint:t,args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a={},limits:l=SPAWN}={}){let c;try{c=buildDispatchPreview({args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a,limits:l})}catch{return{ok:!1,code:"invalid_dispatch"}}if(!e||e.fingerprint!==t)return{ok:!1,code:"approval_mismatch"};if(c.fingerprint!==e.fingerprint)return{ok:!1,code:"stale_dispatch"};const d=spawnDecision(a,l);return d.ok?{ok:!0,code:"approved",preview:c}:{ok:!1,code:"cap_denied",reason:d.reason}}export function verifyDurableDispatchAuthority({item:e,events:t=[],expected:r={}}={}){const n=e=>({ok:!1,code:e}),o=String(r.fingerprint||"");if(!e||e.id!==r.controlItemId)return n("control_item_mismatch");if(e.session_code!==String(r.roomCode||"").toUpperCase())return n("wrong_room");if("dispatch"!==e.action_kind||"approval"!==e.item_kind)return n("wrong_action");if("resolved"!==e.status||"approved"!==e.resolution_code||!e.resolved_by)return n("not_approved");if("either_member"!==e.resolution_policy||null!=e.required_actor_id)return n("wrong_resolution_policy");if(e.bridge_authority_id!==r.bridgeAuthorityId)return n("wrong_bridge_authority");if(e.local_authority_id!==r.localAuthorityId)return n("wrong_local_authority");if(!/^[0-9a-f]{64}$/.test(o))return n("invalid_fingerprint");if(!String(e.request_context?.action||"").includes(`fp:${o}`))return n("request_fingerprint_mismatch");if(e.resolution_payload?.dispatch_fingerprint!==o)return n("resolution_fingerprint_mismatch");const i=(Array.isArray(t)?t:[]).filter(t=>t?.item_id===e.id);if(1!==i.filter(t=>"resolved"===t.event_type&&t.actor_id===e.resolved_by&&t.payload?.resolution_payload?.dispatch_fingerprint===o&&"approved"===t.payload?.resolution_code).length)return n("resolution_event_mismatch");const s=i.filter(t=>"delivery_attempted"===t.event_type&&t.actor_id===e.resolved_by&&t.payload?.data?.dispatch_fingerprint===o);return 1!==s.length?n("delivery_attempt_mismatch"):i.some(e=>"delivered"===e.event_type||"delivery_failed"===e.event_type)?n("delivery_already_final"):{ok:!0,code:"durable_dispatch_authorized",fingerprint:o,humanActorId:e.resolved_by,attemptKey:s[0].correlation_key}}export const CLOSABLE_HINT_MAX=6;export const closeLaneDecision=({callerId:e,spawnedBy:t,busy:r}={})=>e&&t===e?r?{ok:!1,reason:"busy"}:{ok:!0}:{ok:!1,reason:"not-owner"};export const closableLanes=(e,t=[])=>(Array.isArray(t)?t:[]).filter(t=>t&&closeLaneDecision({callerId:e,spawnedBy:t.spawnedBy,busy:t.busy}).ok);export const formatClosableHint=(e,t=[],r=6)=>{const n=closableLanes(e,t);if(!n.length)return"";const o=n.slice(0,r),i=o.map(e=>`${e.ref}${e.name?` ("${e.name}")`:""}`).join(", "),s=n.length>o.length?` (+${n.length-o.length} more)`:"";return`\n\nYou have ${n.length} finished lane${1===n.length?"":"s"} of your own still open — collect ${1===n.length?"it":"them"} with read_terminal, then close_terminal: ${i}${s}`};export const CROSSROOM={peekPerTurnCap:5};export const PEER_READ_MCP_TOOLS=Object.freeze(["read_terminal","list_sessions","read_session"]);export const PEER_POST_MCP_TOOLS=Object.freeze(["post_to_terminal","post_to_session"]);export const PEER_MCP_TOOLS=Object.freeze([...PEER_READ_MCP_TOOLS,...PEER_POST_MCP_TOOLS]);export const formatPairRoster=(e,{thisRoom:t,error:r}={})=>{if(r)return String(r);const n=new Set,o=(Array.isArray(e)?e:[]).filter(e=>!(!e||!e.code||e.code===t||n.has(e.code)||(n.add(e.code),0)));return o.length?`Your other thinkpool Code sessions (call read_session with a room code to look inside one):\n${o.map(e=>`- ${e.code}${e.name?` · "${e.name}"`:""}${e.host?` · on ${e.host}`:""}${!1===e.online?" · partner bridge offline":""}`).join("\n")}`:"No other sessions of yours are open right now. (Cross-session reach is limited to rooms this account is running here, plus your partner's rooms in the same pair.)"};export const CROSSROOM_BUS={busTimeoutMs:4e3};export const pairKeyFor=(e,t)=>{const r=String(null==e?"":e).toLowerCase().trim(),n=String(null==t?"":t).toLowerCase().trim();return r&&n&&r!==n?r<n?`${r}.${n}`:`${n}.${r}`:null};export const pairTopic=e=>e?`tppair:${e}`:null;export const CROSSROOM_POST={maxRoomHop:1,crossRoomPostCap:1};export const crossRoomPostDecision=({roomHop:e=0,postCount:t=0,disabled:r=!1}={},n=CROSSROOM_POST)=>{const o={...CROSSROOM_POST,...n||{}};return r?{ok:!1,reason:"Cross-room posting is turned off on this bridge."}:e>=o.maxRoomHop?{ok:!1,reason:"Cross-room hop limit reached — a room reached via a cross-room post cannot itself post into another room; a person must direct the next cross-room action."}:t>=o.crossRoomPostCap?{ok:!1,reason:`Cross-room post limit reached for this turn (${o.crossRoomPostCap}).`}:{ok:!0}};
1
+ import{createHash as e}from"node:crypto";import{BLOCKED_REASON as t,projectLaneLifecycle as r}from"./lane-lifecycle.mjs";export const PEEK={defaultLines:20,maxLines:200,perTurnCap:10,rosterPerTurnCap:1,lineCap:200,rosterPreview:100};export const readTerminalBudgetDecision=({targeted:e=!1,targetedCount:t=0,rosterCount:r=0}={},n=PEEK)=>{const o=Math.max(0,Number(t)||0),i=Math.max(0,Number(r)||0);return e?o>=n.perTurnCap?{ok:!1,targetedCount:o,rosterCount:i,reason:`Cross-terminal transcript read limit reached for this turn (${n.perTurnCap}). Stop polling and continue with the worker results already collected; a fresh accepted room turn resets the allowance.`}:{ok:!0,targetedCount:o+1,rosterCount:i}:i>=n.rosterPerTurnCap?{ok:!1,targetedCount:o,rosterCount:i,reason:`The full terminal roster was already read this turn (${n.rosterPerTurnCap}/${n.rosterPerTurnCap}). Use the current ROOM NOW snapshot or read one relevant terminal by ref or name.`}:{ok:!0,targetedCount:o,rosterCount:i+1}};export const readTerminalTurnBudgetDecision=({targeted:e=!1,targetedCount:t=0,rosterCount:r=0,budgetTurnRev:n=null,currentTurnRev:o=0}={},i=PEEK)=>{const s=Math.max(0,Number(o)||0),a=(null==n?null:Number.isFinite(Number(n))?Number(n):null)===s;return{...readTerminalBudgetDecision({targeted:e,targetedCount:a?t:0,rosterCount:a?r:0},i),budgetTurnRev:s}};export const LANE_STATUS={stuckAfterMs:9e4,repeatedFailureCount:2,failureWindow:12};const n=e=>!!e&&("error"===e.kind||"tool_result"===e.kind&&e.isError||"result"===e.kind&&e.subtype&&"success"!==e.subtype);export const laneBusyOf=(e={})=>"boolean"==typeof e?.busy?e.busy:"boolean"==typeof e?._busyAnn?e._busyAnn:e?.session?.turnActive??!1;export const settleLaneBusy=e=>!(!e||!1===e._busyAnn||(e._busyAnn=!1,0));export const settleLaneControl=(e,t=Date.now())=>!(!e||!0!==e._busyAnn||!settleLaneBusy(e)||(e._settledTurnRev=Number(e._turnRev)||null,e._turnSettledAt=Number(t)||Date.now(),e._turnStart=null,0));export const laneStatusOf=(e={},o=Date.now(),i=LANE_STATUS)=>{const s=Array.isArray(e?.log)?e.log:[],a=s.slice(-i.failureWindow);!e?.lastEvent||e.lastEvent===s[s.length-1]||e.lastEvent.cid&&e.lastEvent.cid===s[s.length-1]?.cid||a.push(e.lastEvent);let l=null;for(let e=a.length-1;e>=0;e--)if(Number.isFinite(a[e]?.ts)){l=a[e].ts;break}const c=Number.isFinite(e?.lastActionAt)?e.lastActionAt:l??null,d=null==c?null:Math.max(0,o-c),p=laneBusyOf(e),u=e?.pending instanceof Map?e.pending.size:Math.max(0,Number(e?.pendingCount)||0),m=e?.pending instanceof Map&&[...e.pending.values()].some(e=>"ask"===e?.payload?.risk)?t.NEEDS_INPUT:t.NEEDS_DECISION,h=((e,t=LANE_STATUS)=>{let r=0,o=0;for(let i=(Array.isArray(e)?e.length:0)-1;i>=0&&o<t.failureWindow;i--){const t=e[i];if(t&&"object"==typeof t){if(o++,"result"===t.kind&&(!t.subtype||"success"===t.subtype)||"you"===t.kind)break;n(t)&&r++}}return r})(a,i);let f=null,g=null;return u>0?(f="BLOCKED",g="waiting for input"):e?.stalled||p&&!e?.compacting&&null!=d&&d>=i.stuckAfterMs?(f="STUCK",g="no events while busy"):h>=i.repeatedFailureCount&&(f="BLOCKED",g="repeated errors"),{busy:!!p,lastActionAt:c,lastActionAgeMs:d,statusFlag:f,statusReason:g,lifecycle:r({events:a,busy:!!p,pendingCount:u,pendingReason:m,stalled:"STUCK"===f,failed:h>=i.repeatedFailureCount&&!p,rawStatus:f||(p?"busy":"idle")})}};export const formatLaneAge=e=>{if(!Number.isFinite(e))return"no activity";const t=Math.max(0,Math.floor(e/1e3));if(t<60)return`${t}s ago`;const r=Math.floor(t/60);if(r<60)return`${r}m ago`;const n=Math.floor(r/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`};const o=e=>`${e.lifecycle?.state||"Blocked"}${e.lifecycle?.reason?` (${e.lifecycle.reason})`:""} · ${formatLaneAge(e.lastActionAgeMs)}`,i=(e,t)=>{const r=String(null==e?"":e).replace(/\s+/g," ").trim();return r.length>t?r.slice(0,t-1)+"…":r};export const digestLogEvent=(e,t=PEEK.lineCap)=>{if(!e||"object"!=typeof e)return"";switch(e.kind){case"you":return i(`${e.by||"someone"}: ${e.text||""}`,t);case"assistant":{const r=[];for(const t of Array.isArray(e.blocks)?e.blocks:[])"text"===t?.type&&String(t.text||"").trim()?r.push(String(t.text).trim()):"tool_use"===t?.type&&t.name&&r.push(`→ ${t.name}`);return r.length?i(`assistant: ${r.join(" ")}`,t):""}case"tool_result":return e.isError?i("⎿ error",t):"";case"result":return e.subtype&&"success"!==e.subtype?i(`turn ended — ${e.subtype}`,t):"";case"error":return i(`error: ${e.message||""}`,t);default:return""}};export const cleanPtyTail=(e,t)=>String(e||"").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g,"").replace(/\x1b[@-_][0-?]*[ -/]*[@-~]/g,"").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"").split(/\r?\n/).map(e=>e.replace(/\s+$/,"")).filter(e=>e.trim()).slice(-Math.max(1,t));export const siblingRef=e=>String(e).slice(0,8);export const siblingsOf=({selfId:e,sessions:t,terms:r,names:n={},now:o=Date.now()}={})=>{const i=e=>n&&n[e]||"",s=[];for(const[r,n]of t||new Map)r!==e&&s.push({id:r,ref:siblingRef(r),name:i(r),kind:"agent",cmd:n?.cmd||"claude",log:Array.isArray(n?.log)?n.log:[],...laneStatusOf(n,o)});for(const[t,n]of r||new Map)t!==e&&s.push({id:t,ref:siblingRef(t),name:i(t),kind:"shell",cmd:n?.cmd||"shell",buf:n?.buf||"",...laneStatusOf(n,o)});return s};export const isOrdinalRef=e=>/^(terminal\s*)?\d+$/i.test(e)||/^t\d+$/i.test(e);export const resolveSibling=(e,t)=>{const r=String(t||"").toLowerCase().trim();if(!r)return null;const n=Array.isArray(e)?e:[];return n.find(e=>String(e.id).toLowerCase()===r)||n.find(e=>String(e.name||"").toLowerCase()===r)||n.find(e=>String(e.ref).toLowerCase()===r)||(isOrdinalRef(r)?null:n.find(e=>r.length>=4&&String(e.id).toLowerCase().startsWith(r))||n.find(e=>e.name&&String(e.name).toLowerCase().includes(r))||n.find(e=>String(e.cmd).toLowerCase().includes(r))||null)};const s=e=>`${e.ref}${e.name?` · "${e.name}"`:""}`;export const formatPeek=({selfId:e,sessions:t,terms:r,names:n={},terminal:a,lines:l,now:c=Date.now()}={})=>{const d=Math.min(PEEK.maxLines,Math.max(1,Number(l)||PEEK.defaultLines)),p=siblingsOf({selfId:e,sessions:t,terms:r,names:n,now:c});if(!p.length)return"No other terminals are open in this room right now.";if(!a)return`Other terminals in this room (call read_terminal with a ref or name to read one):\n${p.map(e=>{let t;if("agent"===e.kind){const r=e.log.map(e=>digestLogEvent(e,PEEK.rosterPreview)).filter(Boolean);t=r[r.length-1]||"(no activity yet)"}else t=cleanPtyTail(e.buf,1)[0]||"(no output yet)";return`- ${s(e)} · ${e.kind} · ${e.cmd} · ${o(e)} — ${i(t,PEEK.rosterPreview)}`}).join("\n")}`;const u=resolveSibling(p,a);if(!u)return`No terminal matches "${a}". Call read_terminal with no argument to list the open terminals (refer to one by its name or ref).${isOrdinalRef(String(a||"").toLowerCase().trim())?" On-screen numbers like \"Terminal N\" shift when a terminal closes, so they can't identify a lane — use a terminal's name or ref instead.":""}`;let m;return m="agent"===u.kind?u.log.map(e=>digestLogEvent(e,PEEK.lineCap)).filter(Boolean).slice(-d).join("\n")||"(no activity yet)":cleanPtyTail(u.buf,d).join("\n")||"(no output yet)",`Terminal ${s(u)} (${u.kind} · ${u.cmd}) · ${o(u)} — last ${d} lines:\n${m}`};export const nativeClaudeProviderAccessFailed=e=>{let t="";try{t="string"==typeof e?e:JSON.stringify(e||"")}catch{t=String(e||"")}return/organization has disabled (?:claude(?: subscription access)?|subscription access)|(?:claude )?subscription access (?:has been |is )?disabled|invalid anthropic (?:api )?key|invalid x-api-key|not logged in to claude|(?:run|use) \/login[^\n]{0,80}claude|no valid anthropic (?:api )?key/i.test(t)};const a=Object.freeze(["nous:anthropic/claude-sonnet-4.6","nous:anthropic/claude-sonnet-4.5","nous:anthropic/claude-sonnet-4"]);export const preferredHermesClaudeReviewModel=(e=[])=>{const t=new Set((Array.isArray(e)?e:[]).map(e=>"string"==typeof e?e:e?.value||e?.slug).filter(Boolean));for(const e of a)if(t.has(e))return e;for(const e of t)if(/^nous:anthropic\/claude-sonnet-/i.test(e))return e;for(const e of t)if(/^nous:anthropic\/claude-haiku-/i.test(e))return e;for(const e of t)if(/^nous:anthropic\/claude-opus-/i.test(e))return e;return null};export const nativeClaudeFallbackHint=({parentRuntime:e,parentModels:t,targetRuntime:r,targetLog:n}={})=>{if("claude"!==r||!nativeClaudeProviderAccessFailed(n))return"";const o="thinkpool provider status: native Claude access failed at the account/provider level. Do not retry Sonnet, Opus, Haiku, or another model alias on the same native Claude provider during this task.",i="hermes"===e?preferredHermesClaudeReviewModel(t):null;return i?`\n\n${o} For a visible manual review fallback, spawn exactly one worker with runtime="hermes", model=${JSON.stringify(i)}, mode="default", and sliceType="review". This Hermes lane is permission-gated, not structurally read-only: instruct it not to edit, deny/stop any write request, and do not treat its review as a Flow safety proof. Never use Hermes delegate_task.`:`\n\n${o} Use a different available runtime/provider once, or report the blocker.`};export const ROOMNOW={laneCap:8,previewLen:70,wtCap:10};export const formatRoomNow=({selfId:e,sessions:t,terms:r,names:n={},worktrees:a=[],now:l=Date.now()}={},c=ROOMNOW)=>{const d=siblingsOf({selfId:e,sessions:t,terms:r,names:n,now:l}),p=[];if(d.length){const e=d.slice(0,c.laneCap),t=e.map(e=>{let t;if("agent"===e.kind){const r=e.log.map(e=>digestLogEvent(e,c.previewLen)).filter(Boolean);t=r[r.length-1]||"(no activity yet)"}else t=cleanPtyTail(e.buf,1)[0]||"(no output yet)";return`- ${s(e)} · ${e.kind} · ${o(e)} — ${i(t,c.previewLen)}`});d.length>e.length&&t.push(`- … +${d.length-e.length} more (read_terminal lists all)`),p.push(`Other lanes in this room right now (this is the roster; use targeted read_terminal only for needed detail):\n${t.join("\n")}`)}const u=Array.isArray(a)?a:[],m=u.slice(0,c.wtCap);if(m.length){const e=m.map(e=>`- ${i(e,c.previewLen+30)}`);u.length>m.length&&e.push(`- … +${u.length-m.length} more (\`git worktree list\` shows all)`),p.push(`Active git worktrees on this host (code edits go in your OWN worktree — never one another lane is on):\n${e.join("\n")}`)}return p.length?`ROOM NOW (live snapshot, this turn):\n${p.join("\n")}`:null};export const CROSSPOST={maxHop:1,perTurnCap:3};export const crossPostDecision=({hop:e=0,postCount:t=0,disabled:r=!1}={},n=CROSSPOST)=>{const o={...CROSSPOST,...n||{}};return r?{ok:!1,reason:"Cross-terminal posting is disabled in this room."}:e>=o.maxHop?{ok:!1,reason:"Cross-terminal hop limit reached — an agent reached via cross-post cannot itself post onward; a person must direct the next cross-terminal action."}:t>=o.perTurnCap?{ok:!1,reason:`Cross-terminal post limit reached for this turn (${o.perTurnCap}).`}:{ok:!0}};export const crossPostNeedsCard=({mode:e="default",spawnedByMe:t=!1}={})=>!("bypassPermissions"===e||!0===t);export const SPAWN={maxHop:1,perWindowCap:6,windowMs:6e5,freeMaxSpawned:3,plusMaxSpawned:6,machineMax:12};export const maxSpawnedFor=(e,t=SPAWN)=>{const r={...SPAWN,...t||{}};return"plus"===e?r.plusMaxSpawned:r.freeMaxSpawned};export const isActiveDispatchedWorker=e=>!(!e?.spawnedBy||"conductor"===e.flowRole||e.flowTaskKey&&e.flowDone);export const countActiveDispatchedWorkers=(e=[])=>[...e].filter(isActiveDispatchedWorker).length;export const machineCapacityDecision=({totalLive:e=0,disabled:t=!1}={},r=SPAWN)=>{const n={...SPAWN,...r||{}};return t?{ok:!1,reason:"Spawning terminals is disabled in this room."}:e>=n.machineMax?{ok:!1,reason:`Room is at the machine cap (${n.machineMax} live lanes) — close one before opening another.`}:{ok:!0}};export const activeWorkerCapacityDecision=({spawnedLive:e=0,totalLive:t=0,plan:r="free",disabled:n=!1}={},o=SPAWN)=>{const i={...SPAWN,...o||{}},s=machineCapacityDecision({totalLive:t,disabled:n},i);if(!s.ok)return s;const a=maxSpawnedFor(r,i);return e>=a?{ok:!1,reason:`Dispatch ceiling reached (${a} lane${1===a?"":"s"} on the ${r} plan)${"plus"!==r?` — Plus raises it to ${i.plusMaxSpawned}.`:"."} Close a dispatched lane to open another.`}:{ok:!0}};export const recentSpawnCount=(e,t,r=SPAWN.windowMs)=>(Array.isArray(e)?e:[]).filter(e=>Number.isFinite(e)&&e>t-r).length;export const spawnDecision=({hop:e=0,spawnTimes:t=[],now:r=0,spawnedLive:n=0,totalLive:o=0,plan:i="free",disabled:s=!1}={},a=SPAWN)=>{const l={...SPAWN,...a||{}};if(s)return{ok:!1,reason:"Spawning terminals is disabled in this room."};if(e>=l.maxHop)return{ok:!1,reason:"Spawn limit reached — a terminal reached via cross-terminal cannot itself open further terminals; a person must open the next lane."};const c=activeWorkerCapacityDecision({spawnedLive:n,totalLive:o,plan:i},l);if(!c.ok)return c;if(recentSpawnCount(t,r,l.windowMs)>=l.perWindowCap){const e=Math.round(l.windowMs/6e4);return{ok:!1,reason:`Spawn burst limit reached (${l.perWindowCap} in ${e} min) — this replenishes as the window slides forward, or instantly when a person types into this lane.`}}return{ok:!0}};const l=Object.freeze(["mode","model","name","provider","runtime","sliceType","task"]),c=new Set(["default","acceptEdits","bypassPermissions","plan","review"]),d=new Set(["claude","codex","hermes"]),p=new Set(["scaffold","feature","fix","review"]),u=/((?<![a-z0-9])sk-[a-z0-9_-]{8,}|(?<![a-z0-9])gsk_[a-z0-9_-]{8,}|(?<![a-z0-9])AIza[a-z0-9_-]{8,}|bearer\s+[a-z0-9._-]{8,})/gi,m=/(?:\/home\/|\/users\/|\/private\/|\/tmp\/|[a-z]:\\|\\\\)/gi,h=e=>Array.isArray(e)?e.map(h):e&&"object"==typeof e?Object.fromEntries(Object.keys(e).sort().map(t=>[t,h(e[t])])):e,f=(e,t)=>null==e?null:String(e).slice(0,t),g=(e,t)=>f(e,t)?.replace(u,"[credential redacted]").replace(m,"[host path redacted]")||"";export function normalizeDispatchArgs(e={},t={}){if(!e||"object"!=typeof e||Array.isArray(e)||Object.keys(e).some(e=>!l.includes(e)))throw new TypeError("Invalid dispatch arguments");const r=e.runtime||t.runtime||"claude",n=e.mode||("plan"===t.mode?"default":t.mode)||"default";if(!d.has(r)||!c.has(n)||e.sliceType&&!p.has(e.sliceType))throw new TypeError("Invalid dispatch arguments");return Object.freeze({name:f(e.name,80),task:f(e.task,12e3),model:f(e.model,160),sliceType:f(e.sliceType,24),runtime:r,provider:f(e.provider,160),mode:n})}export function dispatchCapSnapshot({hop:e=0,spawnTimes:t=[],now:r=0,spawnedLive:n=0,totalLive:o=0,plan:i="free",disabled:s=!1}={},a=SPAWN){const l={...SPAWN,...a||{}},c="plus"===i?"plus":"free",d=maxSpawnedFor(c,l);return Object.freeze({killSwitch:!0===s,hop:Object.freeze({used:Math.max(0,Number(e)||0),limit:l.maxHop}),width:Object.freeze({requested:1,live:Math.max(0,Number(n)||0),limit:d}),machine:Object.freeze({live:Math.max(0,Number(o)||0),limit:l.machineMax}),plan:Object.freeze({name:c,price:"plus"===c?"€5/mo":"€0",used:Math.max(0,Number(n)||0),limit:d}),burst:Object.freeze({used:recentSpawnCount(t,r,l.windowMs),limit:l.perWindowCap,windowMinutes:Math.round(l.windowMs/6e4)})})}export function buildDispatchPreview({args:t,initiatorTerminalId:r,roomCode:n,bridgeAuthorityId:o,localAuthorityId:i,context:s={},limits:a=SPAWN}={}){if(!(r&&n&&o&&i))throw new TypeError("Dispatch authority is incomplete");const l=normalizeDispatchArgs(t,{runtime:s.runtime,mode:s.mode}),c=dispatchCapSnapshot(s,a),d=(p={authority:Object.freeze({initiatorTerminalId:String(r),roomCode:String(n).toUpperCase(),bridgeAuthorityId:String(o),localAuthorityId:String(i)}),exactArgs:l,caps:c},e("sha256").update(JSON.stringify(h(p))).digest("hex"));var p;const u=l.provider||("codex"===l.runtime?"OpenAI / Codex login":"hermes"===l.runtime?"Hermes ACP profile":"Anthropic / host default"),m=l.model||s.resolvedModel||"runtime default",f=g(l.name||l.task?.split(/\r?\n/)[0]||"Open one worker lane",120);return Object.freeze({version:1,fingerprint:d,purpose:f,packages:Object.freeze([{title:g(l.name||"Worker package",80),task:g(l.task||"Open idle for a later hand-off.",600)}]),runtime:Object.freeze({name:l.runtime,provider:g(s.providerName||u,120),model:g(m,160),band:"Start: now–1 min · completion: task-dependent",execution:"Runs locally on the bridge machine; no cloud worker is created.",accountAndSpend:`Uses the host’s ${g(s.providerName||u,100)} account/BYOK. Provider usage is the spend source; thinkpool has no credit meter.`}),permissions:Object.freeze({mode:l.mode,consequence:"bypassPermissions"===l.mode?"Unattended local access within this worker’s sandbox and tool policy.":"review"===l.mode?"Read-only inspection of the pinned parent snapshot; writes and approval escapes are unavailable.":"plan"===l.mode?"Planning only; execution remains gated.":"Protected local actions keep their normal permission gates."}),worktree:"Automatically creates one visible lateral worker lane and an isolated git worktree on this machine.",caps:c})}export function authorizeDispatchApproval({preview:e,approvedFingerprint:t,args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a={},decision:l="allow",consumed:c=!1,permissionId:d=null,consumedPermissionId:p=null,limits:u=SPAWN}={}){return"allow"!==l?{ok:!1,code:"dispatch_canceled"}:c?{ok:!1,code:d&&d===p?"idempotent_replay":"approval_replayed"}:y({preview:e,approvedFingerprint:t,args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a,limits:u})}export function authorizeDirectDispatch({preview:e,args:t,initiatorTerminalId:r,roomCode:n,bridgeAuthorityId:o,localAuthorityId:i,context:s={},limits:a=SPAWN}={}){return y({preview:e,approvedFingerprint:e?.fingerprint,args:t,initiatorTerminalId:r,roomCode:n,bridgeAuthorityId:o,localAuthorityId:i,context:s,limits:a})}function y({preview:e,approvedFingerprint:t,args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a={},limits:l=SPAWN}={}){let c;try{c=buildDispatchPreview({args:r,initiatorTerminalId:n,roomCode:o,bridgeAuthorityId:i,localAuthorityId:s,context:a,limits:l})}catch{return{ok:!1,code:"invalid_dispatch"}}if(!e||e.fingerprint!==t)return{ok:!1,code:"approval_mismatch"};if(c.fingerprint!==e.fingerprint)return{ok:!1,code:"stale_dispatch"};const d=spawnDecision(a,l);return d.ok?{ok:!0,code:"approved",preview:c}:{ok:!1,code:"cap_denied",reason:d.reason}}export function verifyDurableDispatchAuthority({item:e,events:t=[],expected:r={}}={}){const n=e=>({ok:!1,code:e}),o=String(r.fingerprint||"");if(!e||e.id!==r.controlItemId)return n("control_item_mismatch");if(e.session_code!==String(r.roomCode||"").toUpperCase())return n("wrong_room");if("dispatch"!==e.action_kind||"approval"!==e.item_kind)return n("wrong_action");if("resolved"!==e.status||"approved"!==e.resolution_code||!e.resolved_by)return n("not_approved");if("either_member"!==e.resolution_policy||null!=e.required_actor_id)return n("wrong_resolution_policy");if(e.bridge_authority_id!==r.bridgeAuthorityId)return n("wrong_bridge_authority");if(e.local_authority_id!==r.localAuthorityId)return n("wrong_local_authority");if(!/^[0-9a-f]{64}$/.test(o))return n("invalid_fingerprint");if(!String(e.request_context?.action||"").includes(`fp:${o}`))return n("request_fingerprint_mismatch");if(e.resolution_payload?.dispatch_fingerprint!==o)return n("resolution_fingerprint_mismatch");const i=(Array.isArray(t)?t:[]).filter(t=>t?.item_id===e.id);if(1!==i.filter(t=>"resolved"===t.event_type&&t.actor_id===e.resolved_by&&t.payload?.resolution_payload?.dispatch_fingerprint===o&&"approved"===t.payload?.resolution_code).length)return n("resolution_event_mismatch");const s=i.filter(t=>"delivery_attempted"===t.event_type&&t.actor_id===e.resolved_by&&t.payload?.data?.dispatch_fingerprint===o);return 1!==s.length?n("delivery_attempt_mismatch"):i.some(e=>"delivered"===e.event_type||"delivery_failed"===e.event_type)?n("delivery_already_final"):{ok:!0,code:"durable_dispatch_authorized",fingerprint:o,humanActorId:e.resolved_by,attemptKey:s[0].correlation_key}}export const CLOSABLE_HINT_MAX=6;export const closeLaneDecision=({callerId:e,spawnedBy:t,busy:r}={})=>e&&t===e?r?{ok:!1,reason:"busy"}:{ok:!0}:{ok:!1,reason:"not-owner"};export const closableLanes=(e,t=[])=>(Array.isArray(t)?t:[]).filter(t=>t&&closeLaneDecision({callerId:e,spawnedBy:t.spawnedBy,busy:t.busy}).ok);export const formatClosableHint=(e,t=[],r=6)=>{const n=closableLanes(e,t);if(!n.length)return"";const o=n.slice(0,r),i=o.map(e=>`${e.ref}${e.name?` ("${e.name}")`:""}`).join(", "),s=n.length>o.length?` (+${n.length-o.length} more)`:"";return`\n\nYou have ${n.length} finished lane${1===n.length?"":"s"} of your own still open — collect ${1===n.length?"it":"them"} with read_terminal, then close_terminal: ${i}${s}`};export const CROSSROOM={peekPerTurnCap:5};export const PEER_READ_MCP_TOOLS=Object.freeze(["read_terminal","list_sessions","read_session"]);export const PEER_POST_MCP_TOOLS=Object.freeze(["post_to_terminal","post_to_session"]);export const PEER_MCP_TOOLS=Object.freeze([...PEER_READ_MCP_TOOLS,...PEER_POST_MCP_TOOLS]);export const formatPairRoster=(e,{thisRoom:t,error:r}={})=>{if(r)return String(r);const n=new Set,o=(Array.isArray(e)?e:[]).filter(e=>!(!e||!e.code||e.code===t||n.has(e.code)||(n.add(e.code),0)));return o.length?`Your other thinkpool Code sessions (call read_session with a room code to look inside one):\n${o.map(e=>`- ${e.code}${e.name?` · "${e.name}"`:""}${e.host?` · on ${e.host}`:""}${!1===e.online?" · partner bridge offline":""}`).join("\n")}`:"No other sessions of yours are open right now. (Cross-session reach is limited to rooms this account is running here, plus your partner's rooms in the same pair.)"};export const CROSSROOM_BUS={busTimeoutMs:4e3};export const pairKeyFor=(e,t)=>{const r=String(null==e?"":e).toLowerCase().trim(),n=String(null==t?"":t).toLowerCase().trim();return r&&n&&r!==n?r<n?`${r}.${n}`:`${n}.${r}`:null};export const pairTopic=e=>e?`tppair:${e}`:null;export const CROSSROOM_POST={maxRoomHop:1,crossRoomPostCap:1};export const crossRoomPostDecision=({roomHop:e=0,postCount:t=0,disabled:r=!1}={},n=CROSSROOM_POST)=>{const o={...CROSSROOM_POST,...n||{}};return r?{ok:!1,reason:"Cross-room posting is turned off on this bridge."}:e>=o.maxRoomHop?{ok:!1,reason:"Cross-room hop limit reached — a room reached via a cross-room post cannot itself post into another room; a person must direct the next cross-room action."}:t>=o.crossRoomPostCap?{ok:!1,reason:`Cross-room post limit reached for this turn (${o.crossRoomPostCap}).`}:{ok:!0}};
package/flow-budget.mjs CHANGED
@@ -1 +1 @@
1
- export const FLOW_LIMITS={maxConcurrentLanes:8,maxHop:1,escalateAfterFails:2};export function makeBudget({capTokens:e=null}={}){return{capTokens:e,spentTokens:0,killed:!1}}export function recordSpend(e,n){const o=e.spentTokens+n,t=null!=e.capTokens?e.killed||o>=e.capTokens:e.killed;return{...e,spentTokens:o,killed:t}}export function budgetExceeded(e){return null!=e.capTokens&&e.spentTokens>=e.capTokens}export function remainingBudget(e){return null==e.capTokens?1/0:Math.max(0,e.capTokens-e.spentTokens)}export function canDispatch({mode:n,liveLanes:o,budget:t}){return o>=FLOW_LIMITS.maxConcurrentLanes?{ok:!1,reason:"lane cap"}:n===e&&(t.killed||budgetExceeded(t))?{ok:!1,reason:"budget cap"}:{ok:!0,reason:""}}export function shouldEscalate({mode:n,failCount:o}){return n!==e||o>=FLOW_LIMITS.escalateAfterFails}export function killSwitchEnv(){return{disabled:"1"===process.env.TP_FLOW_OFF,capFromEnv:process.env.TP_FLOW_BUDGET_CAP?Number(process.env.TP_FLOW_BUDGET_CAP):null}}const e="autopilot";
1
+ export const FLOW_LIMITS={maxHop:1,escalateAfterFails:2};export function makeBudget({capTokens:e=null}={}){return{capTokens:e,spentTokens:0,killed:!1}}export function recordSpend(e,n){const o=e.spentTokens+n,t=null!=e.capTokens?e.killed||o>=e.capTokens:e.killed;return{...e,spentTokens:o,killed:t}}export function budgetExceeded(e){return null!=e.capTokens&&e.spentTokens>=e.capTokens}export function remainingBudget(e){return null==e.capTokens?1/0:Math.max(0,e.capTokens-e.spentTokens)}export function canDispatch({mode:n,budget:o}){return n===e&&(o.killed||budgetExceeded(o))?{ok:!1,reason:"budget cap"}:{ok:!0,reason:""}}export function shouldEscalate({mode:n,failCount:o}){return n!==e||o>=FLOW_LIMITS.escalateAfterFails}export function killSwitchEnv(){return{disabled:"1"===process.env.TP_FLOW_OFF,capFromEnv:process.env.TP_FLOW_BUDGET_CAP?Number(process.env.TP_FLOW_BUDGET_CAP):null}}const e="autopilot";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkpool-pair",
3
- "version": "0.7.370",
3
+ "version": "0.7.372",
4
4
  "description": "Connect Claude Code, Codex, or Hermes on your computer to a thinkpool Code room.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "format": 1,
3
3
  "package": "thinkpool-pair",
4
- "version": "0.7.370",
4
+ "version": "0.7.372",
5
5
  "kind": "minified",
6
6
  "minifiedModules": 111,
7
- "sourceModuleBytes": 1619227,
8
- "artifactModuleBytes": 717665,
7
+ "sourceModuleBytes": 1621887,
8
+ "artifactModuleBytes": 718959,
9
9
  "files": {
10
- "package.json": "e5f52dc048be293095a9e6d4c5b4a0dbd4585062d7c543abba3958d6c2fe75bb",
10
+ "package.json": "89fd7d523f30a432ba24a2d87992e3e49f0f92d2940df627afe69b21ca8284cb",
11
11
  "LICENSE": "8a030197ca1cad9669909ca14c9ad15cf48d9fe551da526ccaf1fca993eddb23",
12
- "bridge.mjs": "2578eca3b5cb01cb7ee2e2ad1507e137e5c6fae28fa56c8286946405dc47bef2",
12
+ "bridge.mjs": "74e37eb1e69d819cd1d85e5748e41a5b75fbd24c5b98d31a1633f9ec84a3a573",
13
13
  "bridge-service.sh": "b64f27c473ca8f3a49190fd08c23947ab14ca9958d70b6fa53c7bddc2be91403",
14
14
  "command-guidance.mjs": "b9b967b59ff4e77c9613abb0f28858bf3ac5b106a509e471039777b53bec3e29",
15
15
  "abort-turn-barrier.mjs": "3734a5b7e2e940ec6d7fa8b5b04935e1429a23d1bd9e92b2ac2a2ecd9f77c75e",
@@ -26,7 +26,7 @@
26
26
  "claude-session.mjs": "a8e8d2db7dc350a53a5af3a7df1ee96cfa00a327c1e5b344d6aceda4d307600a",
27
27
  "terminal-name.mjs": "f39bb8d1e148ab9a74cbb96438d31e524d6597b8035f00d7a906c28c306c0cf8",
28
28
  "claude-command-catalog.mjs": "36bd4342b2ae94b43a0072533ef2d1648c7473003964bbad433fdb1a8b2bfec1",
29
- "codex-session.mjs": "54a6b23978e1e726f181d990a9ef4933067aa3a88a32fcbf3343be2dd1489120",
29
+ "codex-session.mjs": "553f18703987b23471112c1d61059e50980746e61eaee42d4e8ccd536567283a",
30
30
  "question-response.mjs": "35a7ba53d3eb117717c18004d2a8441f8f37665b231a3c44f4bd9321ff59dd57",
31
31
  "thinkpool-room-prompt.mjs": "9175d5720f53127d0af797d176c648c5f134a55a0f34063e774f98e946978c39",
32
32
  "thinkpool-prompt-contracts.mjs": "051602d7d2fe9b34df92957bd3922c5eeebedbcc60c892adf415243887d8c62f",
@@ -35,7 +35,7 @@
35
35
  "codex-images.mjs": "31605d6d8b3dd6b73395da63d1b0c513445c95ffa2f4306ee466f201d633ffc0",
36
36
  "codex-mcp-http.mjs": "7472543648a9bb575ce20dc3e3270c2da84174c6ea6e9faf87dfaafb95b6212a",
37
37
  "lane-worktree.mjs": "37583757ab82843412ba565dfba3d7cab517e94782eb33115decd5438d092d8f",
38
- "codex-event-mapper.mjs": "88d6b085170de748ccde943e5b596f0240ffe4e5c4bd64705e7e8dce8775b549",
38
+ "codex-event-mapper.mjs": "7e7b878b24cb9208af517a195cc4089de87eda68086355f9e8e3b7e308825dee",
39
39
  "edit-diff.mjs": "9a1bef6bb2d853d630ad38aba374cb2a120dd75b9aa08ab42ffc83cb0e1c57b6",
40
40
  "cumulative-event-relay.mjs": "cf6e6533e33b3c5bb0b462d15ccc86106f2b56f8b1e66fc1f4867dd8f44d7ae6",
41
41
  "codex-commands.mjs": "cae3f617ac42e1316d8980ef97e4255227fe5df5e8e9b556630a3bfb83bb7c2b",
@@ -51,7 +51,7 @@
51
51
  "hermes-delegation-guard.mjs": "c2a6311320d2ff26137c4394c27561e5c8bee7a712bbb1188307bd55bfecc38e",
52
52
  "runtime-registry.mjs": "4606e104310a755be6a8c587e1050e8b0aa5efe56e7b71f9f13983f2685fc31b",
53
53
  "runtime-contract.mjs": "eb24b177002ba33353ec0335f4f830fb57fe313552b1a8d7aef8163cc743f6c1",
54
- "command-catalog.mjs": "8bdeba1dda874c68f4fa273cb03e6c634dfa22ffae88dec582398220ed2be8bf",
54
+ "command-catalog.mjs": "d5f0e6bb21d280c3420fa89c03ee7ed7ba648d86eeea3f6a17f6e156da4e7d6f",
55
55
  "repo-search.mjs": "10cbce1d86655a7add0d7350ea8862a57165ccb02c2afa406348f4669050c304",
56
56
  "git-diff-report.mjs": "a428b3129c90e207a125a86c4485ec16c8ee7e81d432060a070e37172d85ce7a",
57
57
  "runtime-session.mjs": "1a149012375047f0c031e210cba973e64ce2e6de427a951dfc937fa7c3c402ee",
@@ -77,7 +77,7 @@
77
77
  "session-store.mjs": "adeb5d7fe8b18b10e5c795877f797696bbb4f2f222916710173ada32bd433c1e",
78
78
  "side-lane.mjs": "306d451e2d163ecdf40030d886a338770b11bc57952543a107e422f95ca49d45",
79
79
  "worker-completion.mjs": "7fad56bd9d3bb88f3e7bbeaa027508abad3465d3a33240cf82370104defad301",
80
- "cross-terminal.mjs": "8dd07a9cde6f07f43a9d0c5be981bd61f9057558de2d30f3ee3981fb46fafc3f",
80
+ "cross-terminal.mjs": "57fbd5c13b7d8c1ca671b13381e0ccd3c96cb75ebc13a8f466aff9727bba5460",
81
81
  "pair-bus.mjs": "cbc28cee7f4f06b2213091e0b7b07d61c1d01f0ff207c5c6d808cd616ece221e",
82
82
  "direct-pair-room.mjs": "92557b54307da2ca31f0a0d090e44b019a23b1ab32a7dfd6cbcb9d55fd761736",
83
83
  "lane-lifecycle.mjs": "9b5ae3d105c30e3ad37f1f1f792200da2dd4d2bb5c4979a2a69618373e58f1c8",
@@ -103,7 +103,7 @@
103
103
  "flow-review-gate.mjs": "0ef351065269bf2cc6112707064024cf2b916890a80fb94404ebca3a7905754b",
104
104
  "flow-review-reflect.mjs": "61b39e32ad21501b76c1d9267dd5cdf35017afe7e1333b305e72392bc0304628",
105
105
  "flow-assembly.mjs": "5241097f9084d77fdb72f34b41681e03d063c513d126907f5cbb9becb7972e2c",
106
- "flow-budget.mjs": "d406ecbdd5d4acc08c60c641d7ad12553675cf4bbf35a9600bc96300c1af3404",
106
+ "flow-budget.mjs": "37f6f0684a3e3eec43c4af059c6d6ebf826f2c6639d85178b69cdf91d72f765d",
107
107
  "flow-context-store.mjs": "949c20aeac1984d265f0445547a7d248565e61270cf9fed509047a1dd2c44f02",
108
108
  "flow-redispatch.mjs": "4fa3bf3dd9461fa1c447d12b5db5a0f1befb3427abaed1ee1cec86134ad3eb95",
109
109
  "flow-skill-registry.mjs": "33de95aab75cb70bc145da065a460ec6351c46c7d5bbdfe73650258943cc570f",