thinkpool-pair 0.7.406 → 0.7.408
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/account.mjs +1 -1
- package/agent-output-hygiene.mjs +1 -1
- package/bridge.mjs +1 -1
- package/codex-session.mjs +1 -1
- package/launcher.mjs +1 -1
- package/package.json +1 -1
- package/publish-manifest.json +10 -10
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";import{codexCatastrophicHookCliArgs as w}from"./catastrophic-command-guard.mjs";import{codexNativeReasoningEffort as k,codexReasoningEfforts as _,normalizeCodexReasoningEffort as C,normalizeReasoningEffortList as S}from"./reasoning-effort.mjs";const I=new Set(["read-only","workspace-write","danger-full-access"]);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 const CODEX_STALL_CONTINUATION_PROMPT=["The previous Codex turn was force-stopped after becoming unresponsive.","Continue the same person-authored task from the current conversation and workspace state.","First inspect the current workspace state and any partial work.","Do not repeat commands, edits, external writes, messages, pushes, deployments, or other actions that may already have completed.","Finish only the remaining work, verify the result, and report any uncertainty."].join(" ");export const CODEX_STALL_RETRY_SUGGESTION="Continue from the current workspace state. Inspect partial work first and do not repeat completed actions.";const M=e=>{const t=String(e?.type||"");return["commandExecution","command_execution","fileChange","file_change","mcpToolCall","mcp_tool_call","webSearch","web_search"].includes(t)};export function codexConfigForMode(e){return CODEX_MODE_CONFIG[e]||CODEX_MODE_CONFIG.default}export function codexDefaultCollaborationMode(e,t){if(!e)return null;const r=k(t);return{mode:"default",settings:{model:String(e),reasoning_effort:r,developer_instructions:null}}}export function defaultModeForRuntime(e){return"codex"===e?"bypassPermissions":"default"}export async function codexOneShot({prompt:e,model:s,timeoutMs:a=22e3,outputSchema:i,cwd:l,tmpDir:u=n.tmpdir(),fsImpl:d=r,spawnImpl:c=t}={}){const p=String(e||"").trim();if(!p)throw new Error("codex one-shot requires a prompt");const m=!l,f=l||d.mkdtempSync(o.join(u,"thinkpool-codex-one-shot-"));let h=null;try{i&&(h=o.join(f,"output-schema.json"),d.writeFileSync(h,`${JSON.stringify(i)}\n`,{mode:384}));const e=["exec","--json","--ephemeral","--ignore-rules","--sandbox","read-only","--skip-git-repo-check","--color","never","-c",'approval_policy="never"',"-c",'model_reasoning_effort="low"',...s?["-m",String(s)]:[],...h?["--output-schema",h]:[],p];return await new Promise((t,r)=>{let n,o,s="",i="",l=!1;const u=(e,n)=>{l||(l=!0,o&&clearTimeout(o),e?r(e):t(n))};try{n=c("codex",e,{cwd:f,env:process.env,stdio:["ignore","pipe","pipe"]})}catch(e){return void u(e)}n.stdout.on("data",e=>{s+=e.toString(),s.length>1e6&&(s=s.slice(-1e6))}),n.stderr.on("data",e=>{i+=e.toString(),i.length>2e3&&(i=i.slice(-2e3))}),n.on("error",e=>u(e)),n.on("close",e=>{if(l)return;const t=s.split(/\r?\n/).filter(Boolean).flatMap(e=>{try{const t=JSON.parse(e);return"item.completed"===t?.type&&"agent_message"===t?.item?.type&&"string"==typeof t.item.text?[t.item.text.trim()]:[]}catch{return[]}}).filter(Boolean).at(-1);0===e&&t?u(null,t):u(new Error(`Codex cleanup failed${null==e?"":` (${e})`}${i.trim()?`: ${i.trim().slice(-240)}`:""}`))}),o=setTimeout(()=>{try{n.kill("SIGTERM")}catch{}u(new Error("Codex cleanup timed out"))},Math.max(1e3,Number(a)||22e3))})}finally{if(m)try{d.rmSync(f,{recursive:!0,force:!0})}catch{}}}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||"",reasoningEfforts:S(e.supported_reasoning_levels)}))}catch{return[]}}const T=new Map;function E(e,{home:t,fsImpl:r}){if(!e)return null;const n=`${t}\0${e}`,s=T.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 T.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=E(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=E(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 I.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. Never claim a Cascade or visible lane started without a successful thinkpool opener result containing its room-visible terminal ref. Internal Codex collaboration workers and hidden in-process subagents are not thinkpool terminals and can never substitute for spawn_terminal or open_main_terminal. If the required opener is unavailable or fails, say that nothing started and report the blocker. Leaf, worker, Side, and managed Flow lanes work directly and must not delegate."].join(" ");export function buildCodexPrompt({text:e,terminalRolePrompt:t,rolePrompt:r,roomContext:n,firstTurn:o=!1,promptIndex:s=0,forceFullReminder:a=!1,turnCause:i=null}){const l="function"==typeof n?n():n,u=f({promptIndex:s,forceFull:a,turnCause:i}),d=[o?c:"",u?t:"",p({text:e,promptIndex:s,forceFull:a,turnCause:i}),u?r||CODEX_ROOM_CASCADE_REMINDER:"",l].map(e=>String(e||"").trim()).filter(Boolean);return d.length?`<thinkpool-context>\n${d.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=[...w(),...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"]];t&&p.push("-m",t),p.push("-c",`approval_policy="${s}"`);const m=k(r);if(m&&p.push("-c",`model_reasoning_effort="${m}"`),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 f=!1;for(const e of u)o.isAbsolute(String(e||""))&&(p.push("--image",String(e)),f=!0);return f&&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 F=/\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:F.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 N(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,models:p=[],effort:h="high",resume:w,env:S,sandbox:I,mode:T="default",onEvent:E,providerConfig:F,terminalRolePrompt:O,rolePrompt:A,roomContext:R,mcpServers:P,prepareCwd:D=null,requestPermission:U,appServerGate:j=i,appServerFactory:$=l,mcpHttpFactory:q=d,spawnImpl:L=t,admitStart:X=null,stallOptions:G={}}){let z=CODEX_MODE_CONFIG[T]?T:"default",B=codexConfigForMode(z);I=normalizeCodexSandbox(I||B.sandbox);const W=codexPublishAccess({cwd:e,env:{...process.env,...S||{}}}),H=W.env,J=w||null,K=!J||codexThreadCanResume(J,{home:H.CODEX_HOME||o.join(n.homedir(),".codex")});let V=K?J:null,Y=V?1:0,Q=0,Z=!0;const ee=m(R);let te=null,re=!1,ne=!1,oe=!1;const se="function"==typeof G.now?G.now:Date.now,ae="function"==typeof G.setInterval?G.setInterval:setInterval,ie="function"==typeof G.clearInterval?G.clearInterval:clearInterval,le=Number.isFinite(G.stallMs)?Math.max(1,Number(G.stallMs)):Math.max(3e4,parseInt(H.TP_STALL_MS,10)||9e4),ue=Number.isFinite(G.forceStopMs)?Math.max(le,Number(G.forceStopMs)):Math.max(3*le,parseInt(H.TP_FORCE_STOP_MS,10)||3e5),de=Number.isFinite(G.intervalMs)?Math.max(1,Number(G.intervalMs)):5e3;let ce=se(),pe=!1,me=!1,fe=!1,he=!1,xe=!0,ge=0,ye=0,ve=null,be=c||null,we=C(h,{models:p,model:be}),ke=V?readCodexThreadUsage(V):null,_e=null;const Ce=j({env:H});let Se=!Ce.enabled,Ie=null,Me=!1;const Te=new WeakSet;let Ee=null,Fe=null,Ne=!1;const Oe=new Set,Ae=new Map,Re=new Set,Pe=new Map;let De=Promise.resolve(),Ue=Promise.resolve();const je=[],$e=y(e=>{try{E?.(e)}catch{}}),qe=()=>{ce=se(),pe=!1},Le=e=>{qe(),$e(e)};Le.cancel=()=>$e.cancel();const Xe=new u({onEvent:Le,model:c||null,usageSnapshotForSession:e=>{const t=readCodexThreadUsage(e);return t&&(ke=t),t||ke}}),Ge=()=>{const e=[...Pe.values()];Pe.clear();for(const t of e)t.text&&Le({kind:"assistant",blocks:[{type:"text",text:t.text}],parentToolUseId:null,replacesCid:t.cid})},ze=e=>{"turn.completed"!==e?.type&&"turn.failed"!==e?.type||(Ge(),oe=!1),Xe.push(e)},Be=e=>{Ge(),oe=!1,Le(e)},We=({retry:e=!1,recoveryAttempt:t=0}={})=>{oe=!0,ce=se(),pe=!1,fe=!1,he=!1,xe=!0,ge=Math.max(0,Number(t)||0),e||(me=!1)},He=e=>Le({kind:"note",text:e}),Je=(e=Ee)=>{const t=String(e||"");if(t)for(Oe.add(t);Oe.size>64;)Oe.delete(Oe.values().next().value)},Ke=(e={})=>{const t=String(e.turnId||"");return!(!t||ne||re||Oe.has(t)||t!==String(Ee||""))};async function Ve(){const e=P?.thinkpool;return e?(_e||(_e=q({sdkServer:e})),_e):null}function Ye(e,t={}){if("thread/tokenUsage/updated"===e){if(t.threadId&&V&&String(t.threadId)!==String(V))return;const e=codexUsageSnapshotFromAppServer(t.tokenUsage);if(!e)return;if(ke=e,Ne){const{used:t,max:r}=e.context;Le({kind:"usage",ctx:{used:t,max:r,pct:Math.min(100,Math.max(0,Math.round(t/r*100))),over:t>r,model:be}})}return}if("turn/started"===e&&t.turn?.id){const e=String(t.turn.id);if(ne||re)return void Je(e);if(Fe){const t=Fe;return Fe=null,void t.resolve(e)}return Ee||(Ee=e),void qe()}if("turn/plan/updated"!==e&&"item/agentMessage/delta"!==e&&"item/started"!==e&&"item/completed"!==e&&"error"!==e||Ke(t)){if(qe(),"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 Le({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||Ee||"active"}`,r=Pe.get(e)||{cid:`codex-stream:${V||"thread"}:${e}`,text:""};return r.text+=String(t.delta),Pe.set(e,r),void Le({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&&(Ae.set(r.id,r),r.id&&M(r)&&Re.add(r.id)),"item/completed"===e&&(r.id&&Re.delete(r.id),Ae.set(r.id,r),"agentMessage"===r.type&&Pe.has(r.id))){const e=Pe.get(r.id);return Pe.delete(r.id),void Le({kind:"assistant",blocks:[{type:"text",text:r.text||e.text}],parentToolUseId:null,replacesCid:e.cid})}return void ze({type:"item/started"===e?"item.started":"item.completed",item:N(r)})}if("error"===e){const e=t.error?.message||t.message||"codex app-server error";ze({type:"error",message:e})}}}async function Qe(e,t={}){if(!Ke(t))return"item/tool/requestUserInput"===e?{answers:{}}:{decision:"decline"};const r=codexApprovalCard(e,t,Ae.get(t.itemId));if(!r)throw new Error(`Unsupported Codex App Server request: ${e}`);let n="deny";ye++;try{n=await(U?.(r))}catch{}finally{ye=Math.max(0,ye-1),qe()}return"item/tool/requestUserInput"===e?codexUserInputResponse(n,r.questions):codexApprovalResponse(n)}async function Ze(){if(Se)return!1;if(!1===Ie?.alive)return Me=!1,Se=!0,!1;if(Ie&&Me)return!0;let t;try{t=await Ve()}catch(e){He(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{if(!Ie){const r=$({cwd:e,env:H,args:a({providerConfig:F,mcpUrl:t?.url,writableDirs:codexExtraWritableDirsForMode(z,I,W.writableDirs)}),onNotification:Ye,onServerRequest:Qe,onClose:e=>{ne||Se||Te.has(r)||(Me=!1,Se=!0,He(`Codex App Server closed; later turns will use codex exec: ${e?.message||e}`))}});Ie=r,await Ie.start(),qe()}return V=await Ie.startThread({threadId:Y>0?V:null,cwd:e,model:be,sandbox:I,approvalPolicy:B.approvalPolicy,config:{"features.default_mode_request_user_input":!0}}),qe(),t?.url&&(await Ie.waitForMcpServer({threadId:V,name:"thinkpool"}),qe()),Me=!0,ze({type:"thread.started",thread_id:V}),!0}catch{Se=!0;try{Ie?.end()}catch{}return Ie=null,Me=!1,!1}}function et({force:e=!1}={}){const t=Ie,r=Ee;if(t&&e){Je(r),Se=!0,Me=!1;try{t.end()}catch{}Ie===t&&(Ie=null)}else if(t&&r)Je(r),Promise.resolve(t.interrupt({threadId:V,turnId:r})).catch(()=>{Se=!0,Me=!1;try{t.end()}catch{}Ie===t&&(Ie=null)});else if(t){Se=!0,Me=!1;try{t.end()}catch{}Ie===t&&(Ie=null)}if(te){const e=te;try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(te===e)try{e.kill("SIGKILL")}catch{}},1500)}}async function tt(e,t={}){if(ne||re||he)return!0;if(!await Ze())return!1;if(ne||re||he)return!0;Xe.setUsageBaseline(V?readCodexThreadUsage(V):null),oe=!0;try{if(xe=!1,Ee=await Ie.startTurn({threadId:V,input:e,images:Array.isArray(t.images)?t.images:[],model:be,effort:k(we),approvalPolicy:B.approvalPolicy,collaborationMode:codexDefaultCollaborationMode(be,we)}),qe(),re||ne){Je(Ee);try{await Ie.interrupt({threadId:V,turnId:Ee})}catch{}return!0}const r=await Ie.waitForTurn(Ee);Je(r?.turn?.id||Ee);const n=r?.turn?.status;re?Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(fe||he)||("interrupted"===n?Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(fe=!1,ze({type:"turn.failed",message:r?.turn?.error?.message||"codex turn failed"})):(fe=!1,ze({type:"turn.completed"})))}catch(e){Se=!0,Me=!1,Je(Ee);try{Ie?.end()}catch{}Ie=null,ne||fe||he||ze({type:"turn.failed",message:`codex app-server turn failed: ${e?.message||e}`})}finally{Ge(),Ee=null,oe=!1,Re.clear(),Ae.clear(),Pe.clear()}return!0}async function rt(e){if(ne||re||he)return!0;if(!await Ze())return!1;if(ne||re||he)return!0;Xe.setUsageBaseline(V?readCodexThreadUsage(V):null),oe=!0;try{xe=!1;const t=await Ie.startReview({threadId:V,target:x(e)});if(Ee=t?.turn?.id||t?.turnId,!Ee)throw new Error("Codex review/start returned no turn id");if(qe(),re||ne){Je(Ee);try{await Ie.interrupt({threadId:V,turnId:Ee})}catch{}return!0}const r=await Ie.waitForTurn(Ee);Je(r?.turn?.id||Ee);const n=r?.turn?.status;re?Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(fe||he)||("interrupted"===n?Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(fe=!1,ze({type:"turn.failed",message:r?.turn?.error?.message||"codex review failed"})):(fe=!1,ze({type:"turn.completed"})))}catch(e){Se=!0,Me=!1,Je(Ee);try{Ie?.end()}catch{}Ie=null,ne||fe||he||ze({type:"turn.failed",message:`codex review failed: ${e?.message||e}`})}finally{Ge(),Ee=null,oe=!1,Re.clear(),Ae.clear(),Pe.clear()}return!0}async function nt(t,n={}){if(ne||re||he)return;let o;try{o=await Ve()}catch(e){He(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{await(o?.handoff?.())}catch(e){He(`thinkpool peer handoff failed: ${e?.message||e}`)}const a=buildCodexExecArgs({sessionId:0===Y?null:V,model:be,effort:we,sandbox:I,approvalPolicy:B.approvalPolicy,providerConfig:F,mcpUrl:o?.url,writableDirs:codexExtraWritableDirsForMode(z,I,W.writableDirs),images:Array.isArray(n.images)?n.images:[],prompt:t});return Xe.setUsageBaseline(V?readCodexThreadUsage(V):null),new Promise(t=>{let n;try{n=r.openSync("/dev/null","r")}catch{}const o={cwd:e,env:H,stdio:["ignore","pipe","pipe"]};if(null!=n&&(o.stdio=[n,"pipe","pipe"]),oe=!0,xe=!1,te=L("codex",a,o),null!=n)try{r.closeSync(n)}catch{}const i=[];s.createInterface({input:te.stdout,crlfDelay:1/0}).on("line",e=>{if(!e.trim())return;let t;try{t=JSON.parse(e)}catch{return}i.push(t.type),"item.started"===t.type&&t.item?.id&&M(t.item)&&Re.add(t.item.id),"item.completed"===t.type&&t.item?.id&&Re.delete(t.item.id),"thread.started"===t.type&&t.thread_id&&(V=V||t.thread_id),"turn.completed"!==t.type&&"turn.failed"!==t.type||(u=!0,fe=!1),ze(t)});let l="",u=!1,d=!1;te.stderr.on("data",e=>{l+=e.toString(),l.length>800&&(l=l.slice(-800))}),te.on("error",e=>{d||(d=!0,te=null,Re.clear(),fe||he||Be({kind:"error",message:`codex failed to start: ${e.message}`,recoverable:!0}),t({preTurnInitFailure:!1}))}),te.on("close",e=>{if(d)return;d=!0,te=null,oe=!1,Re.clear();const r=`codex exec exited ${e}${l?": "+l.trim().slice(-300):""}`;0===e||null==e||u||!codexExecFailedBeforeTurn({stderr:l,eventTypes:i})||re||he?((!fe&&!he||u||re)&&(re&&!u?Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}):re||u||0===e||null==e||Be({kind:"error",message:r,recoverable:!0})),t({preTurnInitFailure:!1})):t({preTurnInitFailure:!0,message:r})})})}function ot(){if(ne)return;const e=je.shift();e&&(Ue=Ue.then(async()=>{const t=Math.max(0,Number(e.options?._stallRecoveryAttempt)||0);We({recoveryAttempt:t}),t>0&&$e({kind:"active-status",sinceMs:0,message:"recovering the stalled turn from current state"});const r=e.options?.turnCause||null,n=f({promptIndex:e.promptIndex,forceFull:e.forceFullReminder,turnCause:r}),o=buildCodexPrompt({text:e.text,terminalRolePrompt:O,rolePrompt:A,roomContext:()=>ee({force:n}),firstTurn:0===Y,promptIndex:e.promptIndex,forceFullReminder:e.forceFullReminder,turnCause:r}),s=/^\s*\/review(?:\s|$)/i.test(String(e.text||""));let a=!1,i=!1;for(;!ne&&!re&&!he;){if(!(s?await rt(e.text):await tt(o,e.options)))if(s)fe=!1,Be({kind:"error",message:"Codex review is unavailable without the tested App Server runtime.",recoverable:!0});else{fe&&(fe=!1,a=!0,We({retry:!0,recoveryAttempt:t}),$e({kind:"note",text:"retrying the stalled turn on a fresh connection"}));const r=await nt(o,e.options);if(r?.preTurnInitFailure){if(!(i||ne||re||he)){i=!0,We({retry:!0,recoveryAttempt:t}),$e({kind:"note",text:"Codex initialization failed before the turn started; retrying once with a fresh thinkpool connection."});continue}Be({kind:"error",message:r.message,recoverable:!0})}}if(!fe||a||he||ne||re)break;fe=!1,a=!0,We({retry:!0,recoveryAttempt:t}),$e({kind:"note",text:"retrying the stalled turn on a fresh connection"})}Y++,ot()}))}return J&&!K&&queueMicrotask(()=>He("The stopped Codex turn had no resumable context; continuing in a fresh thread.")),["0","false","off"].includes(String(H.TP_CODEX_APP_SERVER||"").trim().toLowerCase())||Ce.enabled||queueMicrotask(()=>He(`Codex App Server disabled: ${Ce.reason}`)),ve=ae(()=>{const e=se()-ce,t=v({turnActive:oe,awaitingUser:ye,quietMs:e,stallMs:le,forceStopMs:ue,stalledSent:pe,stallRetried:me});if("none"===t)return;if("retry"===t&&!xe)return he=!0,fe=!1,ge>0?($e({kind:"error",recoverable:!0,message:"The safe recovery continuation also stopped responding. The original prompt was not replayed again. Switch to another model or provider, then continue from the current workspace state."}),$e({kind:"suggestion",text:CODEX_STALL_RETRY_SUGGESTION})):Re.size>0?$e({kind:"error",recoverable:!0,message:"Codex stopped responding while a tool was still running. The transport was stopped, but no automatic continuation was started because that could overlap an uncertain side effect. Inspect the current state before continuing manually."}):(je.unshift({text:CODEX_STALL_CONTINUATION_PROMPT,options:{_stallRecoveryAttempt:1},promptIndex:Q++,forceFullReminder:!0}),$e({kind:"error",recoverable:!0,message:"Codex stopped responding after the turn began. The transport was stopped. A new continuation turn will inspect the current state; the original prompt was not replayed."})),Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),void et({force:!0});const r=b(t,e);if(r&&$e(r),"status"!==t){if("retry"===t)return me=!0,fe=!0,ce=se(),pe=!1,void et();he=!0,fe=!1,je.length=0,Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),et({force:!0})}else pe=!0},de),ve.unref?.(),{get sessionId(){return V},get usageSnapshot(){return ke},get turnActive(){return oe||Ne},get canSteer(){return!(!Ie||!1===Ie.alive||!Me||Se)},get started(){return oe||Ne||!!te||!(!Ie||!1===Ie.alive)},sendTurn(t,r={}){if(ne)return!1;if(!oe&&0===je.length&&!Me&&!te){const e="function"==typeof X?X():{ok:!0};if(!1===e?.ok)return Le({kind:"error",message:e.reason||"Host memory is critically low. This Codex runtime was not started.",recoverable:!0}),!1}const n=Q++,o=Z;if(Z=g(t)||/^\s*\/(?:reset|clear)\b/i.test(String(t||"")),!oe&&0===Y&&D)try{e=D()||e}catch{}if(oe&&Ie&&Ee){qe();const e=r?.turnCause||null,s=f({promptIndex:n,forceFull:o,turnCause:e}),a=buildCodexPrompt({text:t,terminalRolePrompt:O,rolePrompt:A,roomContext:()=>ee({force:s}),promptIndex:n,forceFullReminder:o,turnCause:e}),i=Ee;return De=De.then(()=>Ie.steer({threadId:V,turnId:i,input:a,images:Array.isArray(r.images)?r.images:[]})).catch(e=>{"rejected"===e?.delivery||"not_sent"===e?.delivery?(je.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),oe||1!==je.length||ot()):Le({kind:"error",message:"Codex steering delivery is uncertain; the message was not replayed automatically.",recoverable:!0})}),!0}return je.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),oe||Ne||(re=!1,We({recoveryAttempt:0})),1!==je.length||Ne||ot(),!0},async park(){if(ne||oe||Ne||je.length)return!1;const e=Ue;try{await e}catch{}try{await De}catch{}if(ne||oe||Ne||je.length||te||Ee)return!1;const t=Ie;if(t){Te.add(t),Ie=null,Me=!1,Ae.clear(),Pe.clear();try{t.end()}catch{return!1}}return!0},abort(){const e=Ue,t=oe&&!Ee&&!te;re=!0,fe=!1,he=!1,je.length=0,Je(Ee);let r=Promise.resolve();if(Ie&&Ee&&(r=Ie.interrupt({threadId:V,turnId:Ee}).catch(()=>{})),te){try{te.kill("SIGTERM")}catch{}setTimeout(()=>{try{te?.kill("SIGKILL")}catch{}},1500)}else t&&Be({kind:"result",subtype:"aborted",sessionId:V,model:be,costUsd:null,usage:null,numTurns:0,durationMs:void 0,denials:0,resultText:null});return r.then(()=>e).catch(()=>e).then(()=>{})},end(){if(ne=!0,oe=!1,ve&&ie(ve),Le.cancel(),je.length=0,Je(Ee),te)try{te.kill()}catch{}try{Ie?.end()}catch{}_e?.then(e=>e.close()).catch(()=>{})},setModel(e){if(!e||oe)return!1;be=String(e),Xe.setModel(be);const t=C(we,{models:p,model:be});return t!==we&&(we=t,Le({kind:"effort",level:we})),!0},setMode:e=>!(oe||!CODEX_MODE_CONFIG[e]||(z=e,B=codexConfigForMode(z),I=B.sandbox,V=null,Y=0,ke=null,Me=!1,0)),setEffort:e=>!(oe||!_({models:p,model:be}).includes(e)||(we=e,Le({kind:"effort",level:we}),0)),clearContext:()=>!oe&&(V=null,Y=0,ke=null,Me=!1,!0),async compactContext(){if(oe||Ne)return!1;re=!1,Ne=!0;let e=null,t=!1;try{if(!await Ze()||oe)return!1;const r=new Promise((t,r)=>{e=setTimeout(()=>{Fe=null,r(new Error("Codex did not start the compaction turn"))},5e3),Fe={resolve:t,reject:r}});await Ie.compact({threadId:V}),t=!0;const n=await r;e&&clearTimeout(e);const o=await Ie.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)?(He(`Native Codex compaction status is uncertain; existing context was preserved: ${e?.message||e}`),null):(He(`Native Codex compaction unavailable; using bounded recap fallback: ${e?.message||e}`),!1)}finally{e&&clearTimeout(e),Fe=null,Ne=!1,je.length&&ot()}},async accountUsage(){if(!await Ze())return null;const[e,t]=await Promise.all([Ie.accountUsage().catch(()=>null),Ie.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 a from"node:readline";import{buildCodexAppServerArgs as s,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";import{codexCatastrophicHookCliArgs as w}from"./catastrophic-command-guard.mjs";import{codexNativeReasoningEffort as k,codexReasoningEfforts as _,normalizeCodexReasoningEffort as S,normalizeReasoningEffortList as C}from"./reasoning-effort.mjs";const I=new Set(["read-only","workspace-write","danger-full-access"]);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 const CODEX_STALL_CONTINUATION_PROMPT=["The previous Codex turn was force-stopped after becoming unresponsive.","Continue the same person-authored task from the current conversation and workspace state.","First inspect the current workspace state and any partial work.","Do not repeat commands, edits, external writes, messages, pushes, deployments, or other actions that may already have completed.","Finish only the remaining work, verify the result, and report any uncertainty."].join(" ");export const CODEX_STALL_RETRY_SUGGESTION="Continue from the current workspace state. Inspect partial work first and do not repeat completed actions.";const M=e=>{const t=String(e?.type||"");return["commandExecution","command_execution","fileChange","file_change","mcpToolCall","mcp_tool_call","webSearch","web_search"].includes(t)};export function codexConfigForMode(e){return CODEX_MODE_CONFIG[e]||CODEX_MODE_CONFIG.default}export function codexDefaultCollaborationMode(e,t){if(!e)return null;const r=k(t);return{mode:"default",settings:{model:String(e),reasoning_effort:r,developer_instructions:null}}}export function defaultModeForRuntime(e){return"codex"===e?"bypassPermissions":"default"}export async function codexOneShot({prompt:e,model:a,timeoutMs:s=22e3,outputSchema:i,cwd:l,tmpDir:u=n.tmpdir(),fsImpl:d=r,spawnImpl:c=t}={}){const p=String(e||"").trim();if(!p)throw new Error("codex one-shot requires a prompt");const m=!l,f=l||d.mkdtempSync(o.join(u,"thinkpool-codex-one-shot-"));let h=null;try{i&&(h=o.join(f,"output-schema.json"),d.writeFileSync(h,`${JSON.stringify(i)}\n`,{mode:384}));const e=["exec","--json","--ephemeral","--ignore-rules","--sandbox","read-only","--skip-git-repo-check","--color","never","-c",'approval_policy="never"',"-c",'model_reasoning_effort="low"',...a?["-m",String(a)]:[],...h?["--output-schema",h]:[],p];return await new Promise((t,r)=>{let n,o,a="",i="",l=!1;const u=(e,n)=>{l||(l=!0,o&&clearTimeout(o),e?r(e):t(n))};try{n=c("codex",e,{cwd:f,env:process.env,stdio:["ignore","pipe","pipe"]})}catch(e){return void u(e)}n.stdout.on("data",e=>{a+=e.toString(),a.length>1e6&&(a=a.slice(-1e6))}),n.stderr.on("data",e=>{i+=e.toString(),i.length>2e3&&(i=i.slice(-2e3))}),n.on("error",e=>u(e)),n.on("close",e=>{if(l)return;const t=a.split(/\r?\n/).filter(Boolean).flatMap(e=>{try{const t=JSON.parse(e);return"item.completed"===t?.type&&"agent_message"===t?.item?.type&&"string"==typeof t.item.text?[t.item.text.trim()]:[]}catch{return[]}}).filter(Boolean).at(-1);0===e&&t?u(null,t):u(new Error(`Codex cleanup failed${null==e?"":` (${e})`}${i.trim()?`: ${i.trim().slice(-240)}`:""}`))}),o=setTimeout(()=>{try{n.kill("SIGTERM")}catch{}u(new Error("Codex cleanup timed out"))},Math.max(1e3,Number(s)||22e3))})}finally{if(m)try{d.rmSync(f,{recursive:!0,force:!0})}catch{}}}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||"",reasoningEfforts:C(e.supported_reasoning_levels)}))}catch{return[]}}const T=new Map;function E(e,{home:t,fsImpl:r}){if(!e)return null;const n=`${t}\0${e}`,a=T.get(n);if(a)return a;const s=o.join(t,"sessions");try{const t=`${e}.jsonl`,a=r.readdirSync(s,{recursive:!0,withFileTypes:!0}).find(e=>e.isFile()&&e.name.endsWith(t));if(!a)return null;const i=o.join(a.parentPath||a.path||s,a.name);return T.set(n,i),i}catch{return null}}export function codexThreadCanResume(e,{home:t=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:a=r}={}){if(!e)return!1;const s=E(e,{home:t,fsImpl:a});if(!s)return!0;try{return Number(a.statSync(s).size)>0}catch{return!0}}export function readCodexThreadUsage(e,{home:t=process.env.CODEX_HOME||o.join(n.homedir(),".codex"),fsImpl:a=r,tailBytes:s=4194304}={}){const i=E(e,{home:t,fsImpl:a});if(!i)return null;let l;try{const e=Number(a.statSync(i).size)||0,t=Math.min(e,Math.max(65536,s)),r=Buffer.alloc(t);l=a.openSync(i,"r"),a.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||{},a=r.total_token_usage||{},s=Number(o.total_tokens),i=Number(r.model_context_window);return!Number.isFinite(s)||s<0||!Number.isFinite(i)||i<=0?null:{context:{used:Math.floor(s),max:Math.floor(i)},total:{input_tokens:Math.max(0,Number(a.input_tokens)||0),cached_input_tokens:Math.max(0,Number(a.cached_input_tokens)||0),output_tokens:Math.max(0,Number(a.output_tokens)||0)},rateLimits:t.payload.rate_limits||null}}}catch{return null}finally{if(null!=l)try{a.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 I.has(e)?e:"workspace-write"}export function codexPublishAccess({cwd:t,env:r=process.env,tmpdir:a=n.tmpdir(),execFile:s=e}={}){const i=[];try{const e=String(s("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(a,"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. Never claim a Cascade or visible lane started without a successful thinkpool opener result containing its room-visible terminal ref. Internal Codex collaboration workers and hidden in-process subagents are not thinkpool terminals and can never substitute for spawn_terminal or open_main_terminal. If the required opener is unavailable or fails, say that nothing started and report the blocker. Leaf, worker, Side, and managed Flow lanes work directly and must not delegate."].join(" ");export function buildCodexPrompt({text:e,terminalRolePrompt:t,rolePrompt:r,roomContext:n,firstTurn:o=!1,promptIndex:a=0,forceFullReminder:s=!1,turnCause:i=null}){const l="function"==typeof n?n():n,u=f({promptIndex:a,forceFull:s,turnCause:i}),d=[o?c:"",u?t:"",p({text:e,promptIndex:a,forceFull:s,turnCause:i}),u?r||CODEX_ROOM_CASCADE_REMINDER:"",l].map(e=>String(e||"").trim()).filter(Boolean);return d.length?`<thinkpool-context>\n${d.join("\n\n")}\n</thinkpool-context>\n\n${String(e??"")}`:String(e??"")}export function buildCodexExecArgs({sessionId:e,model:t,effort:r,sandbox:n,approvalPolicy:a="untrusted",providerConfig:s,mcpUrl:i,writableDirs:l=[],images:u=[],prompt:d}){const c=normalizeCodexSandbox(n),p=[...w(),...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"]];t&&p.push("-m",t),p.push("-c",`approval_policy="${a}"`);const m=k(r);if(m&&p.push("-c",`model_reasoning_effort="${m}"`),s&&p.push("-c",s),!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 f=!1;for(const e of u)o.isAbsolute(String(e||""))&&(p.push("--image",String(e)),f=!0);return f&&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 F=/\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:F.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 N(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,models:p=[],effort:h="high",resume:w,env:C,sandbox:I,mode:T="default",onEvent:E,providerConfig:F,terminalRolePrompt:O,rolePrompt:A,roomContext:R,mcpServers:P,prepareCwd:D=null,requestPermission:U,appServerGate:j=i,appServerFactory:$=l,mcpHttpFactory:L=d,spawnImpl:q=t,admitStart:G=null,stallOptions:X={}}){let z=CODEX_MODE_CONFIG[T]?T:"default",B=codexConfigForMode(z);I=normalizeCodexSandbox(I||B.sandbox);const W=codexPublishAccess({cwd:e,env:{...process.env,...C||{}}}),H=W.env,J=w||null,K=!J||codexThreadCanResume(J,{home:H.CODEX_HOME||o.join(n.homedir(),".codex")});let V=K?J:null,Y=V?1:0,Q=0,Z=!0;const ee=m(R);let te=null,re=!1,ne=!1,oe=!1;const ae="function"==typeof X.now?X.now:Date.now,se="function"==typeof X.setInterval?X.setInterval:setInterval,ie="function"==typeof X.clearInterval?X.clearInterval:clearInterval,le=Number.isFinite(X.stallMs)?Math.max(1,Number(X.stallMs)):Math.max(3e4,parseInt(H.TP_STALL_MS,10)||9e4),ue=Number.isFinite(X.forceStopMs)?Math.max(le,Number(X.forceStopMs)):Math.max(3*le,parseInt(H.TP_FORCE_STOP_MS,10)||3e5),de=Number.isFinite(X.longReasoningForceStopMs)?Math.max(ue,Number(X.longReasoningForceStopMs)):Math.max(ue,parseInt(H.TP_LONG_REASONING_FORCE_STOP_MS,10)||12e5),ce=Number.isFinite(X.intervalMs)?Math.max(1,Number(X.intervalMs)):5e3;let pe=ae(),me=!1,fe=!1,he=!1,xe=!1,ge=!0,ye=0,ve=0,be=null,we=c||null,ke=S(h,{models:p,model:we}),_e=V?readCodexThreadUsage(V):null,Se=null;const Ce=j({env:H});let Ie=!Ce.enabled,Me=null,Te=!1;const Ee=new WeakSet;let Fe=null,Ne=null,Oe=!1;const Ae=new Set,Re=new Map,Pe=new Set,De=new Map;let Ue=Promise.resolve(),je=Promise.resolve();const $e=[],Le=y(e=>{try{E?.(e)}catch{}}),qe=()=>{pe=ae(),me=!1},Ge=e=>{qe(),Le(e)};Ge.cancel=()=>Le.cancel();const Xe=new u({onEvent:Ge,model:c||null,usageSnapshotForSession:e=>{const t=readCodexThreadUsage(e);return t&&(_e=t),t||_e}}),ze=()=>{const e=[...De.values()];De.clear();for(const t of e)t.text&&Ge({kind:"assistant",blocks:[{type:"text",text:t.text}],parentToolUseId:null,replacesCid:t.cid})},Be=e=>{"turn.completed"!==e?.type&&"turn.failed"!==e?.type||(ze(),oe=!1),Xe.push(e)},We=e=>{ze(),oe=!1,Ge(e)},He=({retry:e=!1,recoveryAttempt:t=0}={})=>{oe=!0,pe=ae(),me=!1,he=!1,xe=!1,ge=!0,ye=Math.max(0,Number(t)||0),e||(fe=!1)},Je=e=>Ge({kind:"note",text:e}),Ke=(e=Fe)=>{const t=String(e||"");if(t)for(Ae.add(t);Ae.size>64;)Ae.delete(Ae.values().next().value)},Ve=(e={})=>{const t=String(e.turnId||"");return!(!t||ne||re||Ae.has(t)||t!==String(Fe||""))};async function Ye(){const e=P?.thinkpool;return e?(Se||(Se=L({sdkServer:e})),Se):null}function Qe(e,t={}){if("thread/tokenUsage/updated"===e){if(t.threadId&&V&&String(t.threadId)!==String(V))return;(oe||Oe)&&qe();const e=codexUsageSnapshotFromAppServer(t.tokenUsage);if(!e)return;if(_e=e,Oe){const{used:t,max:r}=e.context;Ge({kind:"usage",ctx:{used:t,max:r,pct:Math.min(100,Math.max(0,Math.round(t/r*100))),over:t>r,model:we}})}return}if("turn/started"===e&&t.turn?.id){const e=String(t.turn.id);if(ne||re)return void Ke(e);if(Ne){const t=Ne;return Ne=null,void t.resolve(e)}return Fe||(Fe=e),void qe()}if("turn/plan/updated"!==e&&"item/agentMessage/delta"!==e&&"item/started"!==e&&"item/completed"!==e&&"error"!==e||Ve(t)){if(qe(),"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 Ge({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||Fe||"active"}`,r=De.get(e)||{cid:`codex-stream:${V||"thread"}:${e}`,text:""};return r.text+=String(t.delta),De.set(e,r),void Ge({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&&(Re.set(r.id,r),r.id&&M(r)&&Pe.add(r.id)),"item/completed"===e&&(r.id&&Pe.delete(r.id),Re.set(r.id,r),"agentMessage"===r.type&&De.has(r.id))){const e=De.get(r.id);return De.delete(r.id),void Ge({kind:"assistant",blocks:[{type:"text",text:r.text||e.text}],parentToolUseId:null,replacesCid:e.cid})}return void Be({type:"item/started"===e?"item.started":"item.completed",item:N(r)})}if("error"===e){const e=t.error?.message||t.message||"codex app-server error";Be({type:"error",message:e})}}}async function Ze(e,t={}){if(!Ve(t))return"item/tool/requestUserInput"===e?{answers:{}}:{decision:"decline"};const r=codexApprovalCard(e,t,Re.get(t.itemId));if(!r)throw new Error(`Unsupported Codex App Server request: ${e}`);let n="deny";ve++;try{n=await(U?.(r))}catch{}finally{ve=Math.max(0,ve-1),qe()}return"item/tool/requestUserInput"===e?codexUserInputResponse(n,r.questions):codexApprovalResponse(n)}async function et(){if(Ie)return!1;if(!1===Me?.alive)return Te=!1,Ie=!0,!1;if(Me&&Te)return!0;let t;try{t=await Ye()}catch(e){Je(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{if(!Me){const r=$({cwd:e,env:H,args:s({providerConfig:F,mcpUrl:t?.url,writableDirs:codexExtraWritableDirsForMode(z,I,W.writableDirs)}),onNotification:Qe,onServerRequest:Ze,onClose:e=>{ne||Ie||Ee.has(r)||(Te=!1,Ie=!0,Je(`Codex App Server closed; later turns will use codex exec: ${e?.message||e}`))}});Me=r,await Me.start(),qe()}return V=await Me.startThread({threadId:Y>0?V:null,cwd:e,model:we,sandbox:I,approvalPolicy:B.approvalPolicy,config:{"features.default_mode_request_user_input":!0}}),qe(),t?.url&&(await Me.waitForMcpServer({threadId:V,name:"thinkpool"}),qe()),Te=!0,Be({type:"thread.started",thread_id:V}),!0}catch{Ie=!0;try{Me?.end()}catch{}return Me=null,Te=!1,!1}}function tt({force:e=!1}={}){const t=Me,r=Fe;if(t&&e){Ke(r),Ie=!0,Te=!1;try{t.end()}catch{}Me===t&&(Me=null)}else if(t&&r)Ke(r),Promise.resolve(t.interrupt({threadId:V,turnId:r})).catch(()=>{Ie=!0,Te=!1;try{t.end()}catch{}Me===t&&(Me=null)});else if(t){Ie=!0,Te=!1;try{t.end()}catch{}Me===t&&(Me=null)}if(te){const e=te;try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(te===e)try{e.kill("SIGKILL")}catch{}},1500)}}async function rt(e,t={}){if(ne||re||xe)return!0;if(!await et())return!1;if(ne||re||xe)return!0;Xe.setUsageBaseline(V?readCodexThreadUsage(V):null),oe=!0;try{if(ge=!1,Fe=await Me.startTurn({threadId:V,input:e,images:Array.isArray(t.images)?t.images:[],model:we,effort:k(ke),approvalPolicy:B.approvalPolicy,collaborationMode:codexDefaultCollaborationMode(we,ke)}),qe(),re||ne){Ke(Fe);try{await Me.interrupt({threadId:V,turnId:Fe})}catch{}return!0}const r=await Me.waitForTurn(Fe);Ke(r?.turn?.id||Fe);const n=r?.turn?.status;re?We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(he||xe)||("interrupted"===n?We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(he=!1,Be({type:"turn.failed",message:r?.turn?.error?.message||"codex turn failed"})):(he=!1,Be({type:"turn.completed"})))}catch(e){Ie=!0,Te=!1,Ke(Fe);try{Me?.end()}catch{}Me=null,ne||he||xe||Be({type:"turn.failed",message:`codex app-server turn failed: ${e?.message||e}`})}finally{ze(),Fe=null,oe=!1,Pe.clear(),Re.clear(),De.clear()}return!0}async function nt(e){if(ne||re||xe)return!0;if(!await et())return!1;if(ne||re||xe)return!0;Xe.setUsageBaseline(V?readCodexThreadUsage(V):null),oe=!0;try{ge=!1;const t=await Me.startReview({threadId:V,target:x(e)});if(Fe=t?.turn?.id||t?.turnId,!Fe)throw new Error("Codex review/start returned no turn id");if(qe(),re||ne){Ke(Fe);try{await Me.interrupt({threadId:V,turnId:Fe})}catch{}return!0}const r=await Me.waitForTurn(Fe);Ke(r?.turn?.id||Fe);const n=r?.turn?.status;re?We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(he||xe)||("interrupted"===n?We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(he=!1,Be({type:"turn.failed",message:r?.turn?.error?.message||"codex review failed"})):(he=!1,Be({type:"turn.completed"})))}catch(e){Ie=!0,Te=!1,Ke(Fe);try{Me?.end()}catch{}Me=null,ne||he||xe||Be({type:"turn.failed",message:`codex review failed: ${e?.message||e}`})}finally{ze(),Fe=null,oe=!1,Pe.clear(),Re.clear(),De.clear()}return!0}async function ot(t,n={}){if(ne||re||xe)return;let o;try{o=await Ye()}catch(e){Je(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{await(o?.handoff?.())}catch(e){Je(`thinkpool peer handoff failed: ${e?.message||e}`)}const s=buildCodexExecArgs({sessionId:0===Y?null:V,model:we,effort:ke,sandbox:I,approvalPolicy:B.approvalPolicy,providerConfig:F,mcpUrl:o?.url,writableDirs:codexExtraWritableDirsForMode(z,I,W.writableDirs),images:Array.isArray(n.images)?n.images:[],prompt:t});return Xe.setUsageBaseline(V?readCodexThreadUsage(V):null),new Promise(t=>{let n;try{n=r.openSync("/dev/null","r")}catch{}const o={cwd:e,env:H,stdio:["ignore","pipe","pipe"]};if(null!=n&&(o.stdio=[n,"pipe","pipe"]),oe=!0,ge=!1,te=q("codex",s,o),null!=n)try{r.closeSync(n)}catch{}const i=[];a.createInterface({input:te.stdout,crlfDelay:1/0}).on("line",e=>{if(!e.trim())return;let t;try{t=JSON.parse(e)}catch{return}qe(),i.push(t.type),"item.started"===t.type&&t.item?.id&&M(t.item)&&Pe.add(t.item.id),"item.completed"===t.type&&t.item?.id&&Pe.delete(t.item.id),"thread.started"===t.type&&t.thread_id&&(V=V||t.thread_id),"turn.completed"!==t.type&&"turn.failed"!==t.type||(u=!0,he=!1),Be(t)});let l="",u=!1,d=!1;te.stderr.on("data",e=>{l+=e.toString(),l.length>800&&(l=l.slice(-800))}),te.on("error",e=>{d||(d=!0,te=null,Pe.clear(),he||xe||We({kind:"error",message:`codex failed to start: ${e.message}`,recoverable:!0}),t({preTurnInitFailure:!1}))}),te.on("close",e=>{if(d)return;d=!0,te=null,oe=!1,Pe.clear();const r=`codex exec exited ${e}${l?": "+l.trim().slice(-300):""}`;0===e||null==e||u||!codexExecFailedBeforeTurn({stderr:l,eventTypes:i})||re||xe?((!he&&!xe||u||re)&&(re&&!u?We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}):re||u||0===e||null==e||We({kind:"error",message:r,recoverable:!0})),t({preTurnInitFailure:!1})):t({preTurnInitFailure:!0,message:r})})})}function at(){if(ne)return;const e=$e.shift();e&&(je=je.then(async()=>{const t=Math.max(0,Number(e.options?._stallRecoveryAttempt)||0);He({recoveryAttempt:t}),t>0&&Le({kind:"active-status",sinceMs:0,message:"recovering the stalled turn from current state"});const r=e.options?.turnCause||null,n=f({promptIndex:e.promptIndex,forceFull:e.forceFullReminder,turnCause:r}),o=buildCodexPrompt({text:e.text,terminalRolePrompt:O,rolePrompt:A,roomContext:()=>ee({force:n}),firstTurn:0===Y,promptIndex:e.promptIndex,forceFullReminder:e.forceFullReminder,turnCause:r}),a=/^\s*\/review(?:\s|$)/i.test(String(e.text||""));let s=!1,i=!1;for(;!ne&&!re&&!xe;){if(!(a?await nt(e.text):await rt(o,e.options)))if(a)he=!1,We({kind:"error",message:"Codex review is unavailable without the tested App Server runtime.",recoverable:!0});else{he&&(he=!1,s=!0,He({retry:!0,recoveryAttempt:t}),Le({kind:"note",text:"retrying the stalled turn on a fresh connection"}));const r=await ot(o,e.options);if(r?.preTurnInitFailure){if(!(i||ne||re||xe)){i=!0,He({retry:!0,recoveryAttempt:t}),Le({kind:"note",text:"Codex initialization failed before the turn started; retrying once with a fresh thinkpool connection."});continue}We({kind:"error",message:r.message,recoverable:!0})}}if(!he||s||xe||ne||re)break;he=!1,s=!0,He({retry:!0,recoveryAttempt:t}),Le({kind:"note",text:"retrying the stalled turn on a fresh connection"})}Y++,at()}))}return J&&!K&&queueMicrotask(()=>Je("The stopped Codex turn had no resumable context; continuing in a fresh thread.")),["0","false","off"].includes(String(H.TP_CODEX_APP_SERVER||"").trim().toLowerCase())||Ce.enabled||queueMicrotask(()=>Je(`Codex App Server disabled: ${Ce.reason}`)),be=se(()=>{const e=ae()-pe,t=v({turnActive:oe,awaitingUser:ve,quietMs:e,stallMs:le,forceStopMs:["max","ultra"].includes(ke)?de:ue,stalledSent:me,stallRetried:fe});if("none"===t)return;if("retry"===t&&!ge)return xe=!0,he=!1,ye>0?(Le({kind:"error",recoverable:!0,message:"The safe recovery continuation also stopped responding. The original prompt was not replayed again. Switch to another model or provider, then continue from the current workspace state."}),Le({kind:"suggestion",text:CODEX_STALL_RETRY_SUGGESTION})):Pe.size>0?Le({kind:"error",recoverable:!0,message:"Codex stopped responding while a tool was still running. The transport was stopped, but no automatic continuation was started because that could overlap an uncertain side effect. Inspect the current state before continuing manually."}):($e.unshift({text:CODEX_STALL_CONTINUATION_PROMPT,options:{_stallRecoveryAttempt:1},promptIndex:Q++,forceFullReminder:!0}),Le({kind:"error",recoverable:!0,message:"Codex stopped responding after the turn began. The transport was stopped. A new continuation turn will inspect the current state; the original prompt was not replayed."})),We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),void tt({force:!0});const r=b(t,e);if(r&&Le(r),"status"!==t){if("retry"===t)return fe=!0,he=!0,pe=ae(),me=!1,void tt();xe=!0,he=!1,$e.length=0,We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),tt({force:!0})}else me=!0},ce),be.unref?.(),{get sessionId(){return V},get usageSnapshot(){return _e},get turnActive(){return oe||Oe},get canSteer(){return!(!Me||!1===Me.alive||!Te||Ie)},get started(){return oe||Oe||!!te||!(!Me||!1===Me.alive)},sendTurn(t,r={}){if(ne)return!1;if(!oe&&0===$e.length&&!Te&&!te){const e="function"==typeof G?G():{ok:!0};if(!1===e?.ok)return Ge({kind:"error",message:e.reason||"Host memory is critically low. This Codex runtime was not started.",recoverable:!0}),!1}const n=Q++,o=Z;if(Z=g(t)||/^\s*\/(?:reset|clear)\b/i.test(String(t||"")),!oe&&0===Y&&D)try{e=D()||e}catch{}if(oe&&Me&&Fe){qe();const e=r?.turnCause||null,a=f({promptIndex:n,forceFull:o,turnCause:e}),s=buildCodexPrompt({text:t,terminalRolePrompt:O,rolePrompt:A,roomContext:()=>ee({force:a}),promptIndex:n,forceFullReminder:o,turnCause:e}),i=Fe;return Ue=Ue.then(()=>Me.steer({threadId:V,turnId:i,input:s,images:Array.isArray(r.images)?r.images:[]})).catch(e=>{"rejected"===e?.delivery||"not_sent"===e?.delivery?($e.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),oe||1!==$e.length||at()):Ge({kind:"error",message:"Codex steering delivery is uncertain; the message was not replayed automatically.",recoverable:!0})}),!0}return $e.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),oe||Oe||(re=!1,He({recoveryAttempt:0})),1!==$e.length||Oe||at(),!0},async park(){if(ne||oe||Oe||$e.length)return!1;const e=je;try{await e}catch{}try{await Ue}catch{}if(ne||oe||Oe||$e.length||te||Fe)return!1;const t=Me;if(t){Ee.add(t),Me=null,Te=!1,Re.clear(),De.clear();try{t.end()}catch{return!1}}return!0},abort(){const e=je,t=oe&&!Fe&&!te;re=!0,he=!1,xe=!1,$e.length=0,Ke(Fe);let r=Promise.resolve();if(Me&&Fe&&(r=Me.interrupt({threadId:V,turnId:Fe}).catch(()=>{})),te){try{te.kill("SIGTERM")}catch{}setTimeout(()=>{try{te?.kill("SIGKILL")}catch{}},1500)}else t&&We({kind:"result",subtype:"aborted",sessionId:V,model:we,costUsd:null,usage:null,numTurns:0,durationMs:void 0,denials:0,resultText:null});return r.then(()=>e).catch(()=>e).then(()=>{})},end(){if(ne=!0,oe=!1,be&&ie(be),Ge.cancel(),$e.length=0,Ke(Fe),te)try{te.kill()}catch{}try{Me?.end()}catch{}Se?.then(e=>e.close()).catch(()=>{})},setModel(e){if(!e||oe)return!1;we=String(e),Xe.setModel(we);const t=S(ke,{models:p,model:we});return t!==ke&&(ke=t,Ge({kind:"effort",level:ke})),!0},setMode:e=>!(oe||!CODEX_MODE_CONFIG[e]||(z=e,B=codexConfigForMode(z),I=B.sandbox,V=null,Y=0,_e=null,Te=!1,0)),setEffort:e=>!(oe||!_({models:p,model:we}).includes(e)||(ke=e,Ge({kind:"effort",level:ke}),0)),clearContext:()=>!oe&&(V=null,Y=0,_e=null,Te=!1,!0),async compactContext(){if(oe||Oe)return!1;re=!1,Oe=!0;let e=null,t=!1;try{if(!await et()||oe)return!1;const r=new Promise((t,r)=>{e=setTimeout(()=>{Ne=null,r(new Error("Codex did not start the compaction turn"))},5e3),Ne={resolve:t,reject:r}});await Me.compact({threadId:V}),t=!0;const n=await r;e&&clearTimeout(e);const o=await Me.waitForTurn(n);Ke(o?.turn?.id||n);const a=o?.turn?.status;if("completed"===a)return!0;throw new Error(o?.turn?.error?.message||`compaction ${a||"failed"}`)}catch(e){return t||!["rejected","not_sent"].includes(e?.delivery)?(Je(`Native Codex compaction status is uncertain; existing context was preserved: ${e?.message||e}`),null):(Je(`Native Codex compaction unavailable; using bounded recap fallback: ${e?.message||e}`),!1)}finally{e&&clearTimeout(e),Ne=null,Oe=!1,$e.length&&at()}},async accountUsage(){if(!await et())return null;const[e,t]=await Promise.all([Me.accountUsage().catch(()=>null),Me.accountRateLimits().catch(()=>null)]);return{usage:e,limits:t}}}}
|
package/launcher.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"node:os";import n from"node:fs";import t from"node:path";import{execSync as i}from"node:child_process";import{detectProvider as r,fetchModels as o,ANTHROPIC_COMPATIBLE as a,gatewayHint as s}from"./byok-detect.mjs";import{probeHermesRuntime as l}from"./hermes-probe.mjs";import{probeOpenCodeRuntime as c}from"./opencode-probe.mjs";import{darwinServiceRunning as p,serviceRuntimeVersion as d}from"./service.mjs";import{loadKeepAwakePreference as u}from"./keep-awake.mjs";import{loadAgentOutputHygienePreference as m}from"./agent-output-hygiene.mjs";const h=e.homedir(),f=t.join(h,".thinkpool-pair"),k=(()=>{try{return JSON.parse(n.readFileSync(new URL("./package.json",import.meta.url),"utf8")).version}catch{return null}})();export const KNOWN_AGENTS=[{label:"Claude Code",cmd:"claude",resume:["--continue"]},{label:"Codex",cmd:"codex"},{label:"Hermes",cmd:"thinkpool"},{label:"OpenCode",cmd:"opencode"}];function g(e){const i="win32"===process.platform?(process.env.PATHEXT||".EXE;.CMD;.BAT;.COM").split(";"):[""];for(const r of(process.env.PATH||"").split(t.delimiter))if(r)for(const o of i)try{if(n.existsSync(t.join(r,e+o)))return!0}catch{}return!1}function y(e){try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return null}}export function detectState(){const e=y(t.join(f,"auth.json")),r=y(t.join(f,"provider.json")),o=!!r?.provider&&"anthropic"!==r.provider,a=g("hermes"),s=g("thinkpool")&&l().available,v=g("opencode")&&c().available,w=function(e){const r=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";try{if("darwin"===process.platform){const e=i(`launchctl print gui/$(id -u)/${r}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"]});return p(e)}return"linux"===process.platform?(i(`systemctl --user is-active --quiet ${r}.service`),!0):n.existsSync(function(e){const n=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";if("darwin"===process.platform)return t.join(h,"Library","LaunchAgents",`${n}.plist`);if("linux"===process.platform)return t.join(h,".config","systemd","user",`${n}.service`);const i=t.join(process.env.APPDATA||t.join(h,"AppData","Roaming"),"Microsoft","Windows","Start Menu","Programs","Startup");return t.join(i,`thinkpool-pair-${e||"account"}.cmd`)}(e))}catch{return!1}}(null),b=!!e?.reconnect_required;return{loggedIn:!!e?.refresh_token&&!b,reconnectRequired:b,email:e?.email||e?.user?.email||null,provider:o?`Custom (${r.baseUrl||"endpoint"})`:"Anthropic (default)",providerModel:o&&r.model||null,agents:KNOWN_AGENTS.filter(e=>g(e.cmd)&&("thinkpool"!==e.cmd||s)&&("opencode"!==e.cmd||v)),hermesInstalled:a,hermesReady:s,opencodeReady:v,accountSvc:w,keepAwake:u(),agentOutputHygiene:m(),serviceVersion:w?d(null):null,platform:process.platform,cwd:process.cwd(),version:k}}const v=e=>`[2m${e}[0m`,w=e=>`[1m${e}[0m`,b=e=>`[36m${e}[0m`,$=e=>`[32m${e}[0m`,A=e=>`[33m${e}[0m`;export async function runLauncher({actions:e,io:n,state:t=detectState(),refresh:i=null}){const l=e=>({...e,agents:Array.isArray(e?.agents)?e.agents:[],loggedIn:!!e?.loggedIn,reconnectRequired:!!e?.reconnectRequired,accountSvc:!!e?.accountSvc,keepAwake:!0===e?.keepAwake,agentOutputHygiene:!0===e?.agentOutputHygiene,serviceVersion:"string"==typeof e?.serviceVersion&&e.serviceVersion?e.serviceVersion:null,platform:e?.platform||process.platform,provider:e?.provider||"Anthropic (default)",cwd:e?.cwd||process.cwd(),version:e?.version||k});t=l(t);const c=()=>{i&&(t=l(i()))},p=async(e,t,i=1)=>{t.forEach((e,t)=>n.print(` ${b(String(t+1))}) ${e.label}${e.hint?" "+v(e.hint):""}`));const r=await n.ask(`\n ${e} ${v(`[${i}]`)} ${b("▸")} `),o=parseInt(r,10);return Number.isInteger(o)&&o>=1&&o<=t.length?o-1:i-1},d=async(e,t=!0)=>{const i=(await n.ask(` ${e} ${v(t?"[Y/n]":"[y/N]")} ${b("▸")} `)).toLowerCase();return i?"y"===i[0]:t},u=()=>{n.print(""),n.print(" "+v("┌ ")+w("thinkpool-pair")+v(" "+"─".repeat(40))),n.print(` ${v("│ launcher ")} ${t.version?`v${t.version}`:"version unavailable"}`),n.print(` ${v("│ account ")} ${t.reconnectRequired?A("reconnect required"):t.loggedIn?$((t.email||"linked")+" ✓"):A("not linked")}`),n.print(` ${v("│ agents ")} ${t.agents.length?t.agents.map(e=>e.label).join(", "):A("none ready: install Claude Code, Codex, or Hermes")}`),n.print(` ${v("│ directory")} ${v(t.cwd)}`);const e=t.accountSvc?"win32"===t.platform?$("startup entry installed"):$("running"+(t.serviceVersion?` v${t.serviceVersion}`:" (version unavailable)")):"not installed";n.print(` ${v("│ bridge ")} ${e}`),n.print(` ${v("│ keep awake")} ${t.keepAwake?$("on")+v(", display may still dim"):"off"}`),n.print(` ${v("│ text hygiene")} ${t.agentOutputHygiene?$("on"):"off"}`),n.print(" "+v("└"+"─".repeat(54))+"\n")},m=async()=>!!t.loggedIn||(n.print("\n "+A(t.reconnectRequired?"This bridge lost its thinkpool account link.":"You are not linked to a thinkpool account yet.")),await d(" Link this device now?",!0)&&(await e.login(),c()),!!t.loggedIn||(n.print(" "+A("login needed to serve your sessions — back to menu")),!1)),h=async()=>!!t.agents.length||(n.print("\n "+A("No coding agent is ready on this computer")),n.print(" thinkpool connects an agent already installed on this machine. It does not"),n.print(" include one. Install and configure at least one of:"),n.print(`\n ${b("Claude Code")} command: claude`),n.print(` ${b("Codex")} command: codex`),n.print(` ${b("Hermes")} command: hermes, then set up its thinkpool profile here`),n.print("\n "+w("BYOK does not replace the agent. It only gives an installed agent")),n.print(" credentials for a model provider."),n.print("\n Install one, then reopen this launcher."),!1),f=async()=>{const i=t.provider+(t.providerModel?`, model ${t.providerModel}`:"");n.print("\n "+v("Provider settings supply model credentials. Claude Code, Codex, or Hermes must still be installed locally."));const l=await p("\n provider",[{label:"Anthropic (default)",hint:"regular Claude login"},{label:"Paste a key, pick a model",hint:"auto-detects the provider · OpenRouter = any model"},{label:"Custom endpoint",hint:"enter a base url + token by hand"},{label:"← Back",hint:`current: ${i}`}],4);if(3===l)return;if(0===l)return await e.setProvider({kind:"anthropic"}),c(),void n.print("\n "+$("✓ back on regular Claude."));if(1===l){const t=(await n.ask(`\n Paste your API key ${v("(blank to cancel)")} ${b("▸")} `)).trim();if(!t)return void n.print(" "+A("cancelled — provider unchanged"));const i=r(t);if(!i.compatible)return void n.print("\n "+A(s(i.name)));const l=a[i.id];n.print(`\n ${$("✓ detected "+l.name)} ${v("— fetching models…")}`);const d=await o({modelsUrl:l.models,key:t,auth:l.auth});let u;if(0===d.length)u=(await n.ask(` Couldn't list models. Model id ${v("(blank = endpoint default)")} ${b("▸")} `)).trim()||void 0;else if(d.length<=40){const e=await p("\n model",d.map(e=>({label:e})),1);u=d[e]}else for(n.print(` ${v(d.length+" models available, e.g. "+d.slice(0,3).join(", ")+"…")}`);;){const e=(await n.ask(` Type a model id or part of it ${v("(blank to cancel)")} ${b("▸")} `)).trim();if(!e)return void n.print(" "+A("cancelled — provider unchanged"));const t=d.filter(n=>n.toLowerCase().includes(e.toLowerCase()));if(0!==t.length){if(1===t.length){u=t[0];break}if(t.length<=40){const e=await p("\n pick",t.map(e=>({label:e})),1);u=t[e];break}n.print(` ${A(t.length+" matches — narrow it down")}`)}else n.print(" "+A("no match — try again"))}return await e.setProvider({kind:"custom",base:l.base,token:t,model:u}),c(),void n.print(`\n ${$("✓ provider set.")}\n ${l.name}\n model: ${u||"(endpoint default)"}`)}const d=(await n.ask(`\n Base url ${v("(blank to cancel · SDK appends /v1/messages)")} ${b("▸")} `)).trim();if(!d)return void n.print(" "+A("cancelled — provider unchanged"));const u=(await n.ask(` API token ${v("(blank to cancel)")} ${b("▸")} `)).trim();if(!u)return void n.print(" "+A("cancelled — provider unchanged"));const m=(await n.ask(` Model ${v("(blank = endpoint default · e.g. glm-4.6)")} ${b("▸")} `)).trim();await e.setProvider({kind:"custom",base:d,token:u,model:m||void 0}),c(),n.print(`\n ${$("✓ provider set to custom.")}\n base url: ${t.provider}\n model: ${t.providerModel||"(endpoint default)"}`)},g=async()=>{for(;;){const i=[{key:"provider",label:"Provider",hint:`current: ${t.provider}`},{key:"account",label:"Account",hint:t.reconnectRequired?"reconnect required":t.loggedIn?`linked: ${t.email||"yes"}`:"not linked"},{key:"awake",label:"Keep computer awake",hint:(t.keepAwake?"ON":"off")+" · the screen can still dim, lock, and turn off"},{key:"hygiene",label:"Clean invisible agent text",hint:(t.agentOutputHygiene?"ON":"off")+" · hidden Unicode only"}];t.hermesInstalled&&i.push({key:"hermes",label:t.hermesReady?"Hermes profile":"Set up Hermes",hint:t.hermesReady?"isolated profile ready":"set up isolated profile + delegation guard"}),i.push({key:"back",label:"Back to main menu",hint:""});const r=i[await p("\n change",i,i.length)];if("back"===r.key)return;if("provider"===r.key)await f();else{if("account"===r.key)return await e.login(),void c();if("awake"===r.key)if(t.keepAwake){if(!1===await e.setKeepAwake({enabled:!1}))continue;t={...t,keepAwake:!1},c(),n.print("\n "+$("✓ keep-awake is off. The computer may sleep normally."))}else if(n.print("\n "+A("Important: keep-awake changes this computer’s power behavior")),n.print(" While the bridge runs, thinkpool will prevent the computer itself from"),n.print(" automatically sleeping. This can use substantially more battery and may"),n.print(" also block suspend requests on some Linux systems."),n.print("\n "+$("It does NOT keep the screen on. Display dimming, screen locking, and")),n.print(" display sleep continue to work normally. Closing a laptop lid may still"),n.print(" sleep it, depending on the operating system and power settings."),await d("\n Enable keep-awake while the thinkpool bridge runs?",!1)){if(!1===await e.setKeepAwake({enabled:!0}))continue;t={...t,keepAwake:!0},c(),n.print("\n "+$("✓ keep-awake is on. Your screen is still free to dim and turn off."))}else n.print("\n "+A("unchanged: keep-awake remains off."));else if("hygiene"===r.key)if(t.agentOutputHygiene){if(!1===await e.setAgentOutputHygiene({enabled:!1}))continue;t={...t,agentOutputHygiene:!1},c(),n.print("\n "+$("✓ invisible agent-text cleanup is off."))}else if(n.print("\n This removes soft hyphens, zero-width spaces, BOM characters, and"),n.print(" Unicode Tags from agent prose before thinkpool stores or broadcasts it."),n.print(" It does not remove statistical watermarks or file provenance."),await d("\n Enable invisible agent-text cleanup?",!1)){if(!1===await e.setAgentOutputHygiene({enabled:!0}))continue;t={...t,agentOutputHygiene:!0},c(),n.print("\n "+$("✓ invisible agent-text cleanup is on."))}else n.print("\n "+A("unchanged: invisible agent-text cleanup remains off."));else if("hermes"===r.key){const n=await p("\n Hermes profile",[{label:"Clone active profile",hint:"copies provider/config explicitly; fresh thinkpool session history"},{label:"Create clean profile",hint:"no copied credentials or bundled skills; configure provider afterward"},{label:"Back",hint:""}],3);2!==n&&(await e.setupHermes({mode:0===n?"clone":"clean"}),c())}}}};for(;;){u(),n.print(" "+w("What do you want to do?")+"\n");const i=t.accountSvc?["win32"===t.platform?{key:"restart",label:"Update startup entry",hint:"installs the latest version; relaunch the bridge window to apply"}:{key:"restart",label:"Restart & update bridge",hint:"install the latest published version; sessions resume"},{key:"uninstall",label:"Remove background service",hint:"stop serving your sessions on this device"}]:[{key:"serve",label:"Serve all my sessions",hint:"runs here, Ctrl-C stops it"},{key:"service",label:"Always-on background service",hint:"recommended — survives reboot, restarts on crash"}];i.push({key:"settings",label:"Settings",hint:`provider · account · keep-awake ${t.keepAwake?"ON":"off"} · text hygiene ${t.agentOutputHygiene?"ON":"off"}${t.hermesInstalled?" · Hermes profile":""}`}),i.push({key:"quit",label:"Quit",hint:""});const r=i[await p("choose",i)];if("quit"===r.key)return;if("serve"===r.key)await h()&&await m()&&await e.serveAccountForeground();else if("service"===r.key){if(await h()&&await m()){if(!0===await e.installService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}}else if("restart"===r.key){if(!0===await e.restartUpdateService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}else"uninstall"===r.key?(await e.uninstallService({room:null}),c()):"settings"===r.key&&await g();await n.ask("\n "+v("press Enter to return to the menu…"))}}
|
|
1
|
+
import e from"node:os";import n from"node:fs";import t from"node:path";import{execSync as r}from"node:child_process";import{detectProvider as o,fetchModels as i,ANTHROPIC_COMPATIBLE as a,gatewayHint as s}from"./byok-detect.mjs";import{probeHermesRuntime as l}from"./hermes-probe.mjs";import{probeOpenCodeRuntime as c}from"./opencode-probe.mjs";import{darwinServiceRunning as p,serviceRuntimeVersion as d}from"./service.mjs";import{loadKeepAwakePreference as u}from"./keep-awake.mjs";const m=e.homedir(),h=t.join(m,".thinkpool-pair"),k=(()=>{try{return JSON.parse(n.readFileSync(new URL("./package.json",import.meta.url),"utf8")).version}catch{return null}})();export const KNOWN_AGENTS=[{label:"Claude Code",cmd:"claude",resume:["--continue"]},{label:"Codex",cmd:"codex"},{label:"Hermes",cmd:"thinkpool"},{label:"OpenCode",cmd:"opencode"}];function f(e){const r="win32"===process.platform?(process.env.PATHEXT||".EXE;.CMD;.BAT;.COM").split(";"):[""];for(const o of(process.env.PATH||"").split(t.delimiter))if(o)for(const i of r)try{if(n.existsSync(t.join(o,e+i)))return!0}catch{}return!1}function v(e){try{return JSON.parse(n.readFileSync(e,"utf8"))}catch{return null}}export function detectState(){const e=v(t.join(h,"auth.json")),o=v(t.join(h,"provider.json")),i=!!o?.provider&&"anthropic"!==o.provider,a=f("hermes"),s=f("thinkpool")&&l().available,g=f("opencode")&&c().available,y=function(e){const o=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";try{if("darwin"===process.platform){const e=r(`launchctl print gui/$(id -u)/${o}`,{encoding:"utf8",stdio:["ignore","pipe","ignore"]});return p(e)}return"linux"===process.platform?(r(`systemctl --user is-active --quiet ${o}.service`),!0):n.existsSync(function(e){const n=e?`io.thinkpool.pair.${e.toLowerCase()}`:"io.thinkpool.pair.account";if("darwin"===process.platform)return t.join(m,"Library","LaunchAgents",`${n}.plist`);if("linux"===process.platform)return t.join(m,".config","systemd","user",`${n}.service`);const r=t.join(process.env.APPDATA||t.join(m,"AppData","Roaming"),"Microsoft","Windows","Start Menu","Programs","Startup");return t.join(r,`thinkpool-pair-${e||"account"}.cmd`)}(e))}catch{return!1}}(null),w=!!e?.reconnect_required;return{loggedIn:!!e?.refresh_token&&!w,reconnectRequired:w,email:e?.email||e?.user?.email||null,provider:i?`Custom (${o.baseUrl||"endpoint"})`:"Anthropic (default)",providerModel:i&&o.model||null,agents:KNOWN_AGENTS.filter(e=>f(e.cmd)&&("thinkpool"!==e.cmd||s)&&("opencode"!==e.cmd||g)),hermesInstalled:a,hermesReady:s,opencodeReady:g,accountSvc:y,keepAwake:u(),serviceVersion:y?d(null):null,platform:process.platform,cwd:process.cwd(),version:k}}const g=e=>`[2m${e}[0m`,y=e=>`[1m${e}[0m`,w=e=>`[36m${e}[0m`,b=e=>`[32m${e}[0m`,$=e=>`[33m${e}[0m`;export async function runLauncher({actions:e,io:n,state:t=detectState(),refresh:r=null}){const l=e=>({...e,agents:Array.isArray(e?.agents)?e.agents:[],loggedIn:!!e?.loggedIn,reconnectRequired:!!e?.reconnectRequired,accountSvc:!!e?.accountSvc,keepAwake:!0===e?.keepAwake,serviceVersion:"string"==typeof e?.serviceVersion&&e.serviceVersion?e.serviceVersion:null,platform:e?.platform||process.platform,provider:e?.provider||"Anthropic (default)",cwd:e?.cwd||process.cwd(),version:e?.version||k});t=l(t);const c=()=>{r&&(t=l(r()))},p=async(e,t,r=1)=>{t.forEach((e,t)=>n.print(` ${w(String(t+1))}) ${e.label}${e.hint?" "+g(e.hint):""}`));const o=await n.ask(`\n ${e} ${g(`[${r}]`)} ${w("▸")} `),i=parseInt(o,10);return Number.isInteger(i)&&i>=1&&i<=t.length?i-1:r-1},d=async(e,t=!0)=>{const r=(await n.ask(` ${e} ${g(t?"[Y/n]":"[y/N]")} ${w("▸")} `)).toLowerCase();return r?"y"===r[0]:t},u=()=>{n.print(""),n.print(" "+g("┌ ")+y("thinkpool-pair")+g(" "+"─".repeat(40))),n.print(` ${g("│ launcher ")} ${t.version?`v${t.version}`:"version unavailable"}`),n.print(` ${g("│ account ")} ${t.reconnectRequired?$("reconnect required"):t.loggedIn?b((t.email||"linked")+" ✓"):$("not linked")}`),n.print(` ${g("│ agents ")} ${t.agents.length?t.agents.map(e=>e.label).join(", "):$("none ready: install Claude Code, Codex, or Hermes")}`),n.print(` ${g("│ directory")} ${g(t.cwd)}`);const e=t.accountSvc?"win32"===t.platform?b("startup entry installed"):b("running"+(t.serviceVersion?` v${t.serviceVersion}`:" (version unavailable)")):"not installed";n.print(` ${g("│ bridge ")} ${e}`),n.print(` ${g("│ keep awake")} ${t.keepAwake?b("on")+g(", display may still dim"):"off"}`),n.print(" "+g("└"+"─".repeat(54))+"\n")},m=async()=>!!t.loggedIn||(n.print("\n "+$(t.reconnectRequired?"This bridge lost its thinkpool account link.":"You are not linked to a thinkpool account yet.")),await d(" Link this device now?",!0)&&(await e.login(),c()),!!t.loggedIn||(n.print(" "+$("login needed to serve your sessions — back to menu")),!1)),h=async()=>!!t.agents.length||(n.print("\n "+$("No coding agent is ready on this computer")),n.print(" thinkpool connects an agent already installed on this machine. It does not"),n.print(" include one. Install and configure at least one of:"),n.print(`\n ${w("Claude Code")} command: claude`),n.print(` ${w("Codex")} command: codex`),n.print(` ${w("Hermes")} command: hermes, then set up its thinkpool profile here`),n.print("\n "+y("BYOK does not replace the agent. It only gives an installed agent")),n.print(" credentials for a model provider."),n.print("\n Install one, then reopen this launcher."),!1),f=async()=>{const r=t.provider+(t.providerModel?`, model ${t.providerModel}`:"");n.print("\n "+g("Provider settings supply model credentials. Claude Code, Codex, or Hermes must still be installed locally."));const l=await p("\n provider",[{label:"Anthropic (default)",hint:"regular Claude login"},{label:"Paste a key, pick a model",hint:"auto-detects the provider · OpenRouter = any model"},{label:"Custom endpoint",hint:"enter a base url + token by hand"},{label:"← Back",hint:`current: ${r}`}],4);if(3===l)return;if(0===l)return await e.setProvider({kind:"anthropic"}),c(),void n.print("\n "+b("✓ back on regular Claude."));if(1===l){const t=(await n.ask(`\n Paste your API key ${g("(blank to cancel)")} ${w("▸")} `)).trim();if(!t)return void n.print(" "+$("cancelled — provider unchanged"));const r=o(t);if(!r.compatible)return void n.print("\n "+$(s(r.name)));const l=a[r.id];n.print(`\n ${b("✓ detected "+l.name)} ${g("— fetching models…")}`);const d=await i({modelsUrl:l.models,key:t,auth:l.auth});let u;if(0===d.length)u=(await n.ask(` Couldn't list models. Model id ${g("(blank = endpoint default)")} ${w("▸")} `)).trim()||void 0;else if(d.length<=40){const e=await p("\n model",d.map(e=>({label:e})),1);u=d[e]}else for(n.print(` ${g(d.length+" models available, e.g. "+d.slice(0,3).join(", ")+"…")}`);;){const e=(await n.ask(` Type a model id or part of it ${g("(blank to cancel)")} ${w("▸")} `)).trim();if(!e)return void n.print(" "+$("cancelled — provider unchanged"));const t=d.filter(n=>n.toLowerCase().includes(e.toLowerCase()));if(0!==t.length){if(1===t.length){u=t[0];break}if(t.length<=40){const e=await p("\n pick",t.map(e=>({label:e})),1);u=t[e];break}n.print(` ${$(t.length+" matches — narrow it down")}`)}else n.print(" "+$("no match — try again"))}return await e.setProvider({kind:"custom",base:l.base,token:t,model:u}),c(),void n.print(`\n ${b("✓ provider set.")}\n ${l.name}\n model: ${u||"(endpoint default)"}`)}const d=(await n.ask(`\n Base url ${g("(blank to cancel · SDK appends /v1/messages)")} ${w("▸")} `)).trim();if(!d)return void n.print(" "+$("cancelled — provider unchanged"));const u=(await n.ask(` API token ${g("(blank to cancel)")} ${w("▸")} `)).trim();if(!u)return void n.print(" "+$("cancelled — provider unchanged"));const m=(await n.ask(` Model ${g("(blank = endpoint default · e.g. glm-4.6)")} ${w("▸")} `)).trim();await e.setProvider({kind:"custom",base:d,token:u,model:m||void 0}),c(),n.print(`\n ${b("✓ provider set to custom.")}\n base url: ${t.provider}\n model: ${t.providerModel||"(endpoint default)"}`)},v=async()=>{for(;;){const r=[{key:"provider",label:"Provider",hint:`current: ${t.provider}`},{key:"account",label:"Account",hint:t.reconnectRequired?"reconnect required":t.loggedIn?`linked: ${t.email||"yes"}`:"not linked"},{key:"awake",label:"Keep computer awake",hint:(t.keepAwake?"ON":"off")+" · the screen can still dim, lock, and turn off"}];t.hermesInstalled&&r.push({key:"hermes",label:t.hermesReady?"Hermes profile":"Set up Hermes",hint:t.hermesReady?"isolated profile ready":"set up isolated profile + delegation guard"}),r.push({key:"back",label:"Back to main menu",hint:""});const o=r[await p("\n change",r,r.length)];if("back"===o.key)return;if("provider"===o.key)await f();else{if("account"===o.key)return await e.login(),void c();if("awake"===o.key)if(t.keepAwake){if(!1===await e.setKeepAwake({enabled:!1}))continue;t={...t,keepAwake:!1},c(),n.print("\n "+b("✓ keep-awake is off. The computer may sleep normally."))}else if(n.print("\n "+$("Important: keep-awake changes this computer’s power behavior")),n.print(" While the bridge runs, thinkpool will prevent the computer itself from"),n.print(" automatically sleeping. This can use substantially more battery and may"),n.print(" also block suspend requests on some Linux systems."),n.print("\n "+b("It does NOT keep the screen on. Display dimming, screen locking, and")),n.print(" display sleep continue to work normally. Closing a laptop lid may still"),n.print(" sleep it, depending on the operating system and power settings."),await d("\n Enable keep-awake while the thinkpool bridge runs?",!1)){if(!1===await e.setKeepAwake({enabled:!0}))continue;t={...t,keepAwake:!0},c(),n.print("\n "+b("✓ keep-awake is on. Your screen is still free to dim and turn off."))}else n.print("\n "+$("unchanged: keep-awake remains off."));else if("hermes"===o.key){const n=await p("\n Hermes profile",[{label:"Clone active profile",hint:"copies provider/config explicitly; fresh thinkpool session history"},{label:"Create clean profile",hint:"no copied credentials or bundled skills; configure provider afterward"},{label:"Back",hint:""}],3);2!==n&&(await e.setupHermes({mode:0===n?"clone":"clean"}),c())}}}};for(;;){u(),n.print(" "+y("What do you want to do?")+"\n");const r=t.accountSvc?["win32"===t.platform?{key:"restart",label:"Update startup entry",hint:"installs the latest version; relaunch the bridge window to apply"}:{key:"restart",label:"Restart & update bridge",hint:"install the latest published version; sessions resume"},{key:"uninstall",label:"Remove background service",hint:"stop serving your sessions on this device"}]:[{key:"serve",label:"Serve all my sessions",hint:"runs here, Ctrl-C stops it"},{key:"service",label:"Always-on background service",hint:"recommended — survives reboot, restarts on crash"}];r.push({key:"settings",label:"Settings",hint:`provider · account · keep-awake ${t.keepAwake?"ON":"off"}${t.hermesInstalled?" · Hermes profile":""}`}),r.push({key:"quit",label:"Quit",hint:""});const o=r[await p("choose",r)];if("quit"===o.key)return;if("serve"===o.key)await h()&&await m()&&await e.serveAccountForeground();else if("service"===o.key){if(await h()&&await m()){if(!0===await e.installService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}}else if("restart"===o.key){if(!0===await e.restartUpdateService({room:null})&&(!e.relaunchLauncher||await e.relaunchLauncher()))return;c()}else"uninstall"===o.key?(await e.uninstallService({room:null}),c()):"settings"===o.key&&await v();await n.ask("\n "+g("press Enter to return to the menu…"))}}
|
package/package.json
CHANGED
package/publish-manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": 1,
|
|
3
3
|
"package": "thinkpool-pair",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.408",
|
|
5
5
|
"kind": "minified",
|
|
6
6
|
"minifiedModules": 135,
|
|
7
|
-
"sourceModuleBytes":
|
|
8
|
-
"artifactModuleBytes":
|
|
7
|
+
"sourceModuleBytes": 1822618,
|
|
8
|
+
"artifactModuleBytes": 832691,
|
|
9
9
|
"files": {
|
|
10
|
-
"package.json": "
|
|
10
|
+
"package.json": "3ad56b7420ecfb37a4b4a68c5bb9719af7ab12b22d85a3b43d7428e709683213",
|
|
11
11
|
"LICENSE": "8a030197ca1cad9669909ca14c9ad15cf48d9fe551da526ccaf1fca993eddb23",
|
|
12
|
-
"bridge.mjs": "
|
|
12
|
+
"bridge.mjs": "4041fed2633ada658ab6b44d24161ec48ff16c4eb31e9a45c4dab45c9f1a9ca3",
|
|
13
13
|
"bridge-service.sh": "b64f27c473ca8f3a49190fd08c23947ab14ca9958d70b6fa53c7bddc2be91403",
|
|
14
14
|
"command-guidance.mjs": "b9b967b59ff4e77c9613abb0f28858bf3ac5b106a509e471039777b53bec3e29",
|
|
15
15
|
"abort-turn-barrier.mjs": "3734a5b7e2e940ec6d7fa8b5b04935e1429a23d1bd9e92b2ac2a2ecd9f77c75e",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"keep-awake.mjs": "6c0a51409321f2564082b414b00696d22a740ca98180b1f628828c5fe19995c7",
|
|
20
20
|
"sdk-smoke.mjs": "dfe509863ede8a12780914939740f7f9fee36d3980f8fcb57803a33593ac4056",
|
|
21
21
|
"sdk-admission.mjs": "eb33725cc33347b900c75289eae7917f6ce3526203711515aecd48d43b3c895d",
|
|
22
|
-
"launcher.mjs": "
|
|
22
|
+
"launcher.mjs": "a63c63492be0e97b41aae9c0ae24045903207a768c2cb86f8d7a9297d2741f1b",
|
|
23
23
|
"privacy-report.mjs": "b444610be6de8b3bcf0086d585594286ef000dad1e075df87013003a315bf211",
|
|
24
24
|
"agent-visibility.mjs": "6afed5ce9120c4aaa7582d3cfb30c0658186a936ad1f87b1b01dfd20e988f839",
|
|
25
25
|
"byok-detect.mjs": "f31f2ca426737581a97c391b3cecfc75f0207cfa31f51c6bf4a9148309c9625d",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"claude-session.mjs": "8992a67c66cb5745f7a72c373ea2bf06f3dcc77a68a3735f21dcbe8968623edc",
|
|
29
29
|
"terminal-name.mjs": "f39bb8d1e148ab9a74cbb96438d31e524d6597b8035f00d7a906c28c306c0cf8",
|
|
30
30
|
"claude-command-catalog.mjs": "36bd4342b2ae94b43a0072533ef2d1648c7473003964bbad433fdb1a8b2bfec1",
|
|
31
|
-
"codex-session.mjs": "
|
|
31
|
+
"codex-session.mjs": "38c84d43680066b1a5f724575a9bade87bd14a8020bad030ec9990f75fdaa3be",
|
|
32
32
|
"question-response.mjs": "35a7ba53d3eb117717c18004d2a8441f8f37665b231a3c44f4bd9321ff59dd57",
|
|
33
33
|
"thinkpool-room-prompt.mjs": "b8cc82ccf4262f6431c2047bbff31ac29c47a0d6c68a64640a2c24d94df4a746",
|
|
34
34
|
"thinkpool-prompt-contracts.mjs": "051602d7d2fe9b34df92957bd3922c5eeebedbcc60c892adf415243887d8c62f",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"plan-meters.mjs": "f7f5aeb3d6a3462b754d3724367bbb331643ba58108e29e91b2aca2f613ab499",
|
|
93
93
|
"recap.mjs": "17bbc73916381cb70758f2605567a21b1c4290c31f94213ab05b23e28d05a3fd",
|
|
94
94
|
"transcript-sanitize.mjs": "c7f4718f25f97c05bc7b4ad8e09f18cbda152c53ad6593af1ab99f0c132dbf01",
|
|
95
|
-
"agent-output-hygiene.mjs": "
|
|
95
|
+
"agent-output-hygiene.mjs": "914b04daa5b60342b5e44737cfba99f2c8ac292826db1fe501f653814454c835",
|
|
96
96
|
"session-search.mjs": "d606fbab5c47357071e4919107aa00ad090f6e7d7e6dc929e0ac5a4d0cb2c538",
|
|
97
97
|
"session-store.mjs": "fa3f7a27a8a7c49bb89364fb28c9c2680f7f487d4b4125329ab4a0ba9362bd8c",
|
|
98
98
|
"side-lane.mjs": "306d451e2d163ecdf40030d886a338770b11bc57952543a107e422f95ca49d45",
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"service.mjs": "412ee48ad7b1dd281063f30f2519b077aa4cc92305e6780ac226293756f6e6f8",
|
|
140
140
|
"supervisor-ready.mjs": "7d5e4ebe3c7a93e0888dd335fb4556a670e55a80a89b861c8a44feac159cd840",
|
|
141
141
|
"presence.mjs": "2516ebb4474a8f45dd881530335c2174af9cde44be8eed6896ff8c62497b6e2b",
|
|
142
|
-
"account.mjs": "
|
|
142
|
+
"account.mjs": "9db39d24e69ff33a47a86a01bd82bbf596abcafc6953f6bb3d5a8048f8d71e04",
|
|
143
143
|
"auth-store.mjs": "c4d41ee7cdc34c2002c6dd50d32e53ace0a3f03a116ffa930ed94a437e247625",
|
|
144
144
|
"key-shape.mjs": "a4113293d52eeba39b05e70a3e0345abc1ddc181b4f236b413bde0ae11a3f646",
|
|
145
145
|
"supabase-key.mjs": "041bde6cf50b15f6426daaf738374abc9647861d8c1dd3d85cedc45684271888",
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"providers.mjs": "c0757433d30bf2f22e80f85e779cdeeeb10b412d568965f1115d4de2c8aa0314",
|
|
148
148
|
"provider-resilience.mjs": "a5a3921040d1860bb82f1d3b53c6ec25a5e230345c0131f2213c535e07fc497e",
|
|
149
149
|
"model-prices.mjs": "91f0bd8688f59ce6bff7743710d3b1139f4f0823d6b95fa68cd9b6fdcdba6ab4",
|
|
150
|
-
"README.md": "
|
|
150
|
+
"README.md": "3e243a1b9c3ad9e7a2b824ccac56e447e6de7f2616b8d3d1c19cc63438f9915b",
|
|
151
151
|
"publish-guard.mjs": "569dd788790e68ea822129f588fd16c1bb435324666552fe9dfe7c435ab3f617"
|
|
152
152
|
}
|
|
153
153
|
}
|