thinkpool-pair 0.7.434 → 0.7.436
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/bridge.mjs +1 -1
- package/codex-session.mjs +1 -1
- package/durable-command-journal.mjs +1 -0
- package/event-delivery-queue.mjs +1 -1
- package/package.json +4 -1
- package/publish-manifest.json +13 -10
- package/replay-transport.mjs +1 -1
- package/session-store.mjs +1 -1
- package/transport-frame.mjs +1 -0
- package/turn-admission.mjs +1 -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 c}from"./codex-mcp-http.mjs";import{CODEX_THINKPOOL_FIRST_TURN_PREAMBLE as d,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{retryCodexSpawnAfterHostHeal as S}from"./codex-host-heal.mjs";import{codexNativeReasoningEffort as _,codexReasoningEfforts as C,normalizeCodexReasoningEffort as k,normalizeReasoningEffortList as T,STANDARD_REASONING_EFFORT_IDS as M}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 E=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=_(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:c=r,spawnImpl:d=t,codexHost:p={}}={}){const m=String(e||"").trim();if(!m)throw new Error("codex one-shot requires a prompt");const f=!l,h=l||c.mkdtempSync(o.join(u,"thinkpool-codex-one-shot-"));let x=null;try{i&&(x=o.join(h,"output-schema.json"),c.writeFileSync(x,`${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)]:[],...x?["--output-schema",x]:[],m];return await S(()=>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=d("codex",e,{cwd:h,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))}),p)}finally{if(f)try{c.rmSync(h,{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 normalizeCodexServiceTier(e){const t=String(e||"").trim().toLowerCase();return"priority"===t?"fast":"fast"===t||"default"===t?t:null}export function readCodexDefaultServiceTier({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*service_tier\s*=\s*["']([^"']+)["']\s*(?:#.*)?$/m);return normalizeCodexServiceTier(r?.[1])}catch{return null}}const F=e=>[...new Set((Array.isArray(e)?e:[]).map(e=>normalizeCodexServiceTier("string"==typeof e?e:e?.id)).filter(Boolean))],O=Object.freeze([Object.freeze({value:"gpt-6-astra",displayName:"GPT-6 Astra",description:"OpenAI’s most intelligent model for complex agentic work. Availability depends on your account.",reasoningEfforts:Object.freeze([...M]),serviceTiers:Object.freeze([])})]),N=e=>{const t=Array.isArray(e)?e:[],r=new Set(t.map(e=>e?.value).filter(Boolean));return[...O.filter(e=>!r.has(e.value)),...t].map(e=>({...e,reasoningEfforts:[...e.reasoningEfforts||[]],serviceTiers:[...e.serviceTiers||[]]}))};export function codexModelSupportsFastMode({models:e=[],model:t}={}){const r=(Array.isArray(e)?e:[]).find(e=>e?.value===t);return F(r?.serviceTiers).includes("fast")}const A=e=>"fast"===normalizeCodexServiceTier(e)?"priority":normalizeCodexServiceTier(e);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")),n=(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:T(e.supported_reasoning_levels),serviceTiers:F(e.service_tiers||e.additional_speed_tiers)}));return N(n)}catch{return N([])}}const R=new Map;function P(e,{home:t,fsImpl:r}){if(!e)return null;const n=`${t}\0${e}`,s=R.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 R.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=P(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=P(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}),c=[o?d:"",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 c.length?`<thinkpool-context>\n${c.join("\n\n")}\n</thinkpool-context>\n\n${String(e??"")}`:String(e??"")}export function buildCodexExecArgs({sessionId:e,model:t,serviceTier:r,effort:n,sandbox:s,approvalPolicy:a="untrusted",providerConfig:i,mcpUrl:l,writableDirs:u=[],images:c=[],prompt:d}){const p=normalizeCodexSandbox(s),m=[...w(),...e?["exec","resume","--json","--skip-git-repo-check",..."danger-full-access"===p?["--dangerously-bypass-approvals-and-sandbox"]:["-c",`sandbox_mode="${p}"`]]:["exec","--json","--sandbox",p,"--skip-git-repo-check"]];t&&m.push("-m",t);const f=normalizeCodexServiceTier(r);f&&(m.push("-c",`service_tier=${JSON.stringify(f)}`),"fast"===f&&m.push("-c","features.fast_mode=true")),m.push("-c",`approval_policy="${a}"`);const h=_(n);if(h&&m.push("-c",`model_reasoning_effort="${h}"`),i&&m.push("-c",i),!e)for(const e of u)m.push("--add-dir",e);l&&(m.push("-c",`mcp_servers.thinkpool.url=${JSON.stringify(l)}`),m.push("-c","mcp_servers.thinkpool.required=true"));let x=!1;for(const e of c)o.isAbsolute(String(e||""))&&(m.push("--image",String(e)),x=!0);return x&&m.push("--"),e&&m.push(e),m.push(String(d??"")),m}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 D=/\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:D.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 j(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:d,models:p=[],serviceTier:h=null,effort:w="high",resume:T,env:M,sandbox:I,mode:F="default",onEvent:O,providerConfig:N,terminalRolePrompt:R,rolePrompt:P,roomContext:D,mcpServers:U,prepareCwd:$=null,requestPermission:L,appServerGate:q=i,appServerFactory:z=l,mcpHttpFactory:G=c,spawnImpl:X=t,admitStart:H=null,stallOptions:B={},codexHost:W={}}){let J=CODEX_MODE_CONFIG[F]?F:"default",K=codexConfigForMode(J);I=normalizeCodexSandbox(I||K.sandbox);const V=codexPublishAccess({cwd:e,env:{...process.env,...M||{}}}),Y=V.env,Q=T||null,Z=!Q||codexThreadCanResume(Q,{home:Y.CODEX_HOME||o.join(n.homedir(),".codex")});let ee=Z?Q:null,te=ee?1:0,re=0,ne=!0;const oe=m(D);let se=null,ae=!1,ie=!1,le=!1;const ue="function"==typeof B.now?B.now:Date.now,ce="function"==typeof B.setInterval?B.setInterval:setInterval,de="function"==typeof B.clearInterval?B.clearInterval:clearInterval,pe=Number.isFinite(B.stallMs)?Math.max(1,Number(B.stallMs)):Math.max(3e4,parseInt(Y.TP_STALL_MS,10)||9e4),me=Number.isFinite(B.forceStopMs)?Math.max(pe,Number(B.forceStopMs)):Math.max(3*pe,parseInt(Y.TP_FORCE_STOP_MS,10)||3e5),fe=Number.isFinite(B.longReasoningForceStopMs)?Math.max(me,Number(B.longReasoningForceStopMs)):Math.max(me,parseInt(Y.TP_LONG_REASONING_FORCE_STOP_MS,10)||12e5),he=Number.isFinite(B.intervalMs)?Math.max(1,Number(B.intervalMs)):5e3;let xe=ue(),ge=!1,ye=!1,ve=!1,be=!1,we=!0,Se=0,_e=0,Ce=null,ke=d||null,Te=normalizeCodexServiceTier(h);"fast"!==Te||codexModelSupportsFastMode({models:p,model:ke})||(Te="default");let Me=k(w,{models:p,model:ke}),Ie=ee?readCodexThreadUsage(ee):null,Ee=null;const Fe=q({env:Y});let Oe=!Fe.enabled,Ne=null,Ae=!1;const Re=new WeakSet;let Pe=null,De=null,je=!1;const Ue=new Set,$e=new Map,Le=new Set,qe=new Map;let ze=Promise.resolve(),Ge=Promise.resolve();const Xe=[],He=y(e=>{try{O?.(e)}catch{}}),Be=()=>{xe=ue(),ge=!1},We=e=>{Be(),He(e)};We.cancel=()=>He.cancel();const Je=new u({onEvent:We,model:d||null,usageSnapshotForSession:e=>{const t=readCodexThreadUsage(e);return t&&(Ie=t),t||Ie}}),Ke=()=>{const e=[...qe.values()];qe.clear();for(const t of e)t.text&&We({kind:"assistant",blocks:[{type:"text",text:t.text}],parentToolUseId:null,replacesCid:t.cid})},Ve=e=>{"turn.completed"!==e?.type&&"turn.failed"!==e?.type||(Ke(),le=!1),Je.push(e)},Ye=e=>{Ke(),le=!1,We(e)},Qe=({retry:e=!1,recoveryAttempt:t=0}={})=>{le=!0,xe=ue(),ge=!1,ve=!1,be=!1,we=!0,Se=Math.max(0,Number(t)||0),e||(ye=!1)},Ze=e=>We({kind:"note",text:e}),et=(e=Pe)=>{const t=String(e||"");if(t)for(Ue.add(t);Ue.size>64;)Ue.delete(Ue.values().next().value)},tt=(e={})=>{const t=String(e.turnId||"");return!(!t||ie||ae||Ue.has(t)||t!==String(Pe||""))};async function rt(){const e=U?.thinkpool;return e?(Ee||(Ee=G({sdkServer:e})),Ee):null}function nt(e,t={}){if("thread/tokenUsage/updated"===e){if(t.threadId&&ee&&String(t.threadId)!==String(ee))return;(le||je)&&Be();const e=codexUsageSnapshotFromAppServer(t.tokenUsage);if(!e)return;if(Ie=e,je){const{used:t,max:r}=e.context;We({kind:"usage",ctx:{used:t,max:r,pct:Math.min(100,Math.max(0,Math.round(t/r*100))),over:t>r,model:ke}})}return}if("turn/started"===e&&t.turn?.id){const e=String(t.turn.id);if(ie||ae)return void et(e);if(De){const t=De;return De=null,void t.resolve(e)}return Pe||(Pe=e),void Be()}if("turn/plan/updated"!==e&&"item/agentMessage/delta"!==e&&"item/started"!==e&&"item/completed"!==e&&"error"!==e||tt(t)){if(Be(),"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 We({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||Pe||"active"}`,r=qe.get(e)||{cid:`codex-stream:${ee||"thread"}:${e}`,text:""};return r.text+=String(t.delta),qe.set(e,r),void We({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&&($e.set(r.id,r),r.id&&E(r)&&Le.add(r.id)),"item/completed"===e&&(r.id&&Le.delete(r.id),$e.set(r.id,r),"agentMessage"===r.type&&qe.has(r.id))){const e=qe.get(r.id);return qe.delete(r.id),void We({kind:"assistant",blocks:[{type:"text",text:r.text||e.text}],parentToolUseId:null,replacesCid:e.cid})}return void Ve({type:"item/started"===e?"item.started":"item.completed",item:j(r)})}if("error"===e){const e=t.error?.message||t.message||"codex app-server error";Ve({type:"error",message:e})}}}async function ot(e,t={}){if(!tt(t))return"item/tool/requestUserInput"===e?{answers:{}}:{decision:"decline"};const r=codexApprovalCard(e,t,$e.get(t.itemId));if(!r)throw new Error(`Unsupported Codex App Server request: ${e}`);let n="deny";_e++;try{n=await(L?.(r))}catch{}finally{_e=Math.max(0,_e-1),Be()}return"item/tool/requestUserInput"===e?codexUserInputResponse(n,r.questions):codexApprovalResponse(n)}async function st(){if(Oe)return!1;if(!1===Ne?.alive)return Ae=!1,Oe=!0,!1;if(Ne&&Ae)return!0;let t;try{t=await rt()}catch(e){Ze(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{if(!Ne){const r=z({cwd:e,env:Y,args:a({providerConfig:N,mcpUrl:t?.url,writableDirs:codexExtraWritableDirsForMode(J,I,V.writableDirs),serviceTier:Te}),onNotification:nt,onServerRequest:ot,codexHost:{...W,log:Ze},onClose:e=>{ie||Oe||Re.has(r)||(Ae=!1,Oe=!0,Ze(`Codex App Server closed; later turns will use codex exec: ${e?.message||e}`))}});Ne=r,await Ne.start(),Be()}return ee=await Ne.startThread({threadId:te>0?ee:null,cwd:e,model:ke,serviceTier:A(Te),sandbox:I,approvalPolicy:K.approvalPolicy,config:{"features.default_mode_request_user_input":!0}}),Be(),t?.url&&(await Ne.waitForMcpServer({threadId:ee,name:"thinkpool"}),Be()),Ae=!0,Ve({type:"thread.started",thread_id:ee}),!0}catch{Oe=!0;try{Ne?.end()}catch{}return Ne=null,Ae=!1,!1}}function at({force:e=!1}={}){const t=Ne,r=Pe;if(t&&e){et(r),Oe=!0,Ae=!1;try{t.end()}catch{}Ne===t&&(Ne=null)}else if(t&&r)et(r),Promise.resolve(t.interrupt({threadId:ee,turnId:r})).catch(()=>{Oe=!0,Ae=!1;try{t.end()}catch{}Ne===t&&(Ne=null)});else if(t){Oe=!0,Ae=!1;try{t.end()}catch{}Ne===t&&(Ne=null)}if(se){const e=se;try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(se===e)try{e.kill("SIGKILL")}catch{}},1500)}}async function it(e,t={}){if(ie||ae||be)return!0;if(!await st())return!1;if(ie||ae||be)return!0;Je.setUsageBaseline(ee?readCodexThreadUsage(ee):null),le=!0;try{if(we=!1,Pe=await Ne.startTurn({threadId:ee,input:e,images:Array.isArray(t.images)?t.images:[],model:ke,serviceTier:A(Te),effort:_(Me),approvalPolicy:K.approvalPolicy,collaborationMode:codexDefaultCollaborationMode(ke,Me)}),Be(),ae||ie){et(Pe);try{await Ne.interrupt({threadId:ee,turnId:Pe})}catch{}return!0}const r=await Ne.waitForTurn(Pe);et(r?.turn?.id||Pe);const n=r?.turn?.status;ae?Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ve||be)||("interrupted"===n?Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ve=!1,Ve({type:"turn.failed",message:r?.turn?.error?.message||"codex turn failed"})):(ve=!1,Ve({type:"turn.completed"})))}catch(e){Oe=!0,Ae=!1,et(Pe);try{Ne?.end()}catch{}Ne=null,ie||ve||be||Ve({type:"turn.failed",message:`codex app-server turn failed: ${e?.message||e}`})}finally{Ke(),Pe=null,le=!1,Le.clear(),$e.clear(),qe.clear()}return!0}async function lt(e){if(ie||ae||be)return!0;if(!await st())return!1;if(ie||ae||be)return!0;Je.setUsageBaseline(ee?readCodexThreadUsage(ee):null),le=!0;try{we=!1;const t=await Ne.startReview({threadId:ee,target:x(e)});if(Pe=t?.turn?.id||t?.turnId,!Pe)throw new Error("Codex review/start returned no turn id");if(Be(),ae||ie){et(Pe);try{await Ne.interrupt({threadId:ee,turnId:Pe})}catch{}return!0}const r=await Ne.waitForTurn(Pe);et(r?.turn?.id||Pe);const n=r?.turn?.status;ae?Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ve||be)||("interrupted"===n?Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ve=!1,Ve({type:"turn.failed",message:r?.turn?.error?.message||"codex review failed"})):(ve=!1,Ve({type:"turn.completed"})))}catch(e){Oe=!0,Ae=!1,et(Pe);try{Ne?.end()}catch{}Ne=null,ie||ve||be||Ve({type:"turn.failed",message:`codex review failed: ${e?.message||e}`})}finally{Ke(),Pe=null,le=!1,Le.clear(),$e.clear(),qe.clear()}return!0}async function ut(t,n={}){if(ie||ae||be)return;let o;try{o=await rt()}catch(e){Ze(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{await(o?.handoff?.())}catch(e){Ze(`thinkpool peer handoff failed: ${e?.message||e}`)}const a=buildCodexExecArgs({sessionId:0===te?null:ee,model:ke,serviceTier:Te,effort:Me,sandbox:I,approvalPolicy:K.approvalPolicy,providerConfig:N,mcpUrl:o?.url,writableDirs:codexExtraWritableDirsForMode(J,I,V.writableDirs),images:Array.isArray(n.images)?n.images:[],prompt:t});Je.setUsageBaseline(ee?readCodexThreadUsage(ee):null);const i=()=>new Promise((t,n)=>{let o;try{o=r.openSync("/dev/null","r")}catch{}const i={cwd:e,env:Y,stdio:["ignore","pipe","pipe"]};null!=o&&(i.stdio=[o,"pipe","pipe"]),le=!0,we=!1;try{se=X("codex",a,i)}catch(e){if(null!=o)try{r.closeSync(o)}catch{}return le=!1,e.codexExecFailure={spawnFailure:!0,hostRetrySafe:!0,message:`codex failed to start: ${e.message}`,sawTerminalResult:!1},void n(e)}if(null!=o)try{r.closeSync(o)}catch{}const l=se,u=[];s.createInterface({input:l.stdout,crlfDelay:1/0}).on("line",e=>{if(!e.trim())return;let t;try{t=JSON.parse(e)}catch{return}Be(),u.push(t.type),"item.started"===t.type&&t.item?.id&&E(t.item)&&Le.add(t.item.id),"item.completed"===t.type&&t.item?.id&&Le.delete(t.item.id),"thread.started"===t.type&&t.thread_id&&(ee=ee||t.thread_id),"turn.completed"!==t.type&&"turn.failed"!==t.type||(d=!0,ve=!1),Ve(t)});let c="",d=!1,p=!1;l.stderr.on("data",e=>{c+=e.toString(),c.length>800&&(c=c.slice(-800))}),l.on("error",e=>{p||(p=!0,se===l&&(se=null),le=!1,Le.clear(),e.codexExecFailure={spawnFailure:!0,hostRetrySafe:0===u.length,message:`codex failed to start: ${e.message}`,sawTerminalResult:!1},n(e))}),l.on("close",e=>{if(p)return;p=!0,se===l&&(se=null),le=!1,Le.clear();const r=`codex exec exited ${e}${c?": "+c.trim().slice(-300):""}`,o=0!==e&&null!=e&&!d&&codexExecFailedBeforeTurn({stderr:c,eventTypes:u});if(!d&&0!==e&&null!=e){const e=new Error(r);return e.codexExecFailure={preTurnInitFailure:o,hostRetrySafe:0===u.length,message:r,sawTerminalResult:!1},void n(e)}ae&&!d&&Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),t({preTurnInitFailure:!1})})});try{return await S(i,{...W,log:Ze,shouldHeal:e=>!0===e?.codexExecFailure?.hostRetrySafe&&!0!==e?.codexExecFailure?.preTurnInitFailure})}catch(e){se=null,le=!1,Le.clear();const t=e?.codexExecFailure||e?.cause?.codexExecFailure||{};if("TP_CODEX_HOST_MISSING"===e?.code)Ye({kind:"error",message:e.message,recoverable:!0});else{if(t.preTurnInitFailure&&!ae&&!be)return{preTurnInitFailure:!0,message:t.message};(!ve&&!be||ae)&&Ye(ae?{kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}:{kind:"error",message:t.message||e?.message||"codex failed to start",recoverable:!0})}return{preTurnInitFailure:!1}}}function ct(){if(ie)return;const e=Xe.shift();e&&(Ge=Ge.then(async()=>{const t=Math.max(0,Number(e.options?._stallRecoveryAttempt)||0);Qe({recoveryAttempt:t}),t>0&&He({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:R,rolePrompt:P,roomContext:()=>oe({force:n}),firstTurn:0===te,promptIndex:e.promptIndex,forceFullReminder:e.forceFullReminder,turnCause:r}),s=/^\s*\/review(?:\s|$)/i.test(String(e.text||""));let a=!1,i=!1;for(;!ie&&!ae&&!be;){if(!(s?await lt(e.text):await it(o,e.options)))if(s)ve=!1,Ye({kind:"error",message:"Codex review is unavailable without the tested App Server runtime.",recoverable:!0});else{ve&&(ve=!1,a=!0,Qe({retry:!0,recoveryAttempt:t}),He({kind:"note",text:"retrying the stalled turn on a fresh connection"}));const r=await ut(o,e.options);if(r?.preTurnInitFailure){if(!(i||ie||ae||be)){i=!0,Qe({retry:!0,recoveryAttempt:t}),He({kind:"note",text:"Codex initialization failed before the turn started; retrying once with a fresh thinkpool connection."});continue}Ye({kind:"error",message:r.message,recoverable:!0})}}if(!ve||a||be||ie||ae)break;ve=!1,a=!0,Qe({retry:!0,recoveryAttempt:t}),He({kind:"note",text:"retrying the stalled turn on a fresh connection"})}te++,ct()}))}return Q&&!Z&&queueMicrotask(()=>Ze("The stopped Codex turn had no resumable context; continuing in a fresh thread.")),["0","false","off"].includes(String(Y.TP_CODEX_APP_SERVER||"").trim().toLowerCase())||Fe.enabled||queueMicrotask(()=>Ze(`Codex App Server disabled: ${Fe.reason}`)),Ce=ce(()=>{const e=ue()-xe,t=v({turnActive:le,awaitingUser:_e,quietMs:e,stallMs:pe,forceStopMs:["max","ultra"].includes(Me)?fe:me,stalledSent:ge,stallRetried:ye});if("none"===t)return;if("retry"===t&&!we)return be=!0,ve=!1,Se>0?(He({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."}),He({kind:"suggestion",text:CODEX_STALL_RETRY_SUGGESTION})):Le.size>0?He({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."}):(Xe.unshift({text:CODEX_STALL_CONTINUATION_PROMPT,options:{_stallRecoveryAttempt:1},promptIndex:re++,forceFullReminder:!0}),He({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."})),Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),void at({force:!0});const r=b(t,e);if(r&&He(r),"status"!==t){if("retry"===t)return ye=!0,ve=!0,xe=ue(),ge=!1,void at();be=!0,ve=!1,Xe.length=0,Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),at({force:!0})}else ge=!0},he),Ce.unref?.(),{get sessionId(){return ee},get usageSnapshot(){return Ie},get turnActive(){return le||je},get canSteer(){return!(!Ne||!1===Ne.alive||!Ae||Oe)},get started(){return le||je||!!se||!(!Ne||!1===Ne.alive)},sendTurn(t,r={}){if(ie)return!1;if(!le&&0===Xe.length&&!Ae&&!se){const e="function"==typeof H?H():{ok:!0};if(!1===e?.ok)return We({kind:"error",message:e.reason||"Host memory is critically low. This Codex runtime was not started.",recoverable:!0}),!1}const n=re++,o=ne;if(ne=g(t)||/^\s*\/(?:reset|clear)\b/i.test(String(t||"")),!le&&0===te&&$)try{e=$()||e}catch{}if(le&&Ne&&Pe){Be();const e=r?.turnCause||null,s=f({promptIndex:n,forceFull:o,turnCause:e}),a=buildCodexPrompt({text:t,terminalRolePrompt:R,rolePrompt:P,roomContext:()=>oe({force:s}),promptIndex:n,forceFullReminder:o,turnCause:e}),i=Pe;return ze=ze.then(()=>Ne.steer({threadId:ee,turnId:i,input:a,images:Array.isArray(r.images)?r.images:[]})).catch(e=>{"rejected"===e?.delivery||"not_sent"===e?.delivery?(Xe.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),le||1!==Xe.length||ct()):We({kind:"error",message:"Codex steering delivery is uncertain; the message was not replayed automatically.",recoverable:!0})}),!0}return Xe.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),le||je||(ae=!1,Qe({recoveryAttempt:0})),1!==Xe.length||je||ct(),!0},async park(){if(ie||le||je||Xe.length)return!1;const e=Ge;try{await e}catch{}try{await ze}catch{}if(ie||le||je||Xe.length||se||Pe)return!1;const t=Ne;if(t){Re.add(t),Ne=null,Ae=!1,$e.clear(),qe.clear();try{t.end()}catch{return!1}}return!0},abort(){const e=Ge,t=le&&!Pe&&!se;ae=!0,ve=!1,be=!1,Xe.length=0,et(Pe);let r=Promise.resolve();if(Ne&&Pe&&(r=Ne.interrupt({threadId:ee,turnId:Pe}).catch(()=>{})),se){try{se.kill("SIGTERM")}catch{}setTimeout(()=>{try{se?.kill("SIGKILL")}catch{}},1500)}else t&&Ye({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:0,durationMs:void 0,denials:0,resultText:null});return r.then(()=>e).catch(()=>e).then(()=>{})},end(){if(ie=!0,le=!1,Ce&&de(Ce),We.cancel(),Xe.length=0,et(Pe),se)try{se.kill()}catch{}try{Ne?.end()}catch{}Ee?.then(e=>e.close()).catch(()=>{})},setModel(e){if(!e||le)return!1;ke=String(e),Je.setModel(ke);const t=k(Me,{models:p,model:ke});return t!==Me&&(Me=t,We({kind:"effort",level:Me})),"fast"!==Te||codexModelSupportsFastMode({models:p,model:ke})||(Te="default"),!0},setServiceTier(e){if(le||je||Xe.length)return!1;const t=normalizeCodexServiceTier(e);if(!t)return!1;if("fast"===t&&!codexModelSupportsFastMode({models:p,model:ke}))return!1;if(t===Te)return!0;Te=t;const r=Ne;if(r){Re.add(r),Ne=null,Ae=!1,$e.clear(),qe.clear();try{r.end()}catch{}}return!0},setMode:e=>!(le||!CODEX_MODE_CONFIG[e]||(J=e,K=codexConfigForMode(J),I=K.sandbox,ee=null,te=0,Ie=null,Ae=!1,0)),setEffort:e=>!(le||!C({models:p,model:ke}).includes(e)||(Me=e,We({kind:"effort",level:Me}),0)),get serviceTier(){return Te},get supportsFastMode(){return codexModelSupportsFastMode({models:p,model:ke})},clearContext:()=>!le&&(ee=null,te=0,Ie=null,Ae=!1,!0),async compactContext(){if(le||je)return!1;ae=!1,je=!0;let e=null,t=!1;try{if(!await st()||le)return!1;const r=new Promise((t,r)=>{e=setTimeout(()=>{De=null,r(new Error("Codex did not start the compaction turn"))},5e3),De={resolve:t,reject:r}});await Ne.compact({threadId:ee}),t=!0;const n=await r;e&&clearTimeout(e);const o=await Ne.waitForTurn(n);et(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)?(Ze(`Native Codex compaction status is uncertain; existing context was preserved: ${e?.message||e}`),null):(Ze(`Native Codex compaction unavailable; using bounded recap fallback: ${e?.message||e}`),!1)}finally{e&&clearTimeout(e),De=null,je=!1,Xe.length&&ct()}},async accountUsage(){if(!await st())return null;const[e,t]=await Promise.all([Ne.accountUsage().catch(()=>null),Ne.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 c}from"./codex-mcp-http.mjs";import{CODEX_THINKPOOL_FIRST_TURN_PREAMBLE as d,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 w}from"./turn-stall.mjs";import{codexCatastrophicHookCliArgs as b}from"./catastrophic-command-guard.mjs";import{retryCodexSpawnAfterHostHeal as S}from"./codex-host-heal.mjs";import{codexNativeReasoningEffort as _,codexReasoningEfforts as C,normalizeCodexReasoningEffort as k,normalizeReasoningEffortList as M}from"./reasoning-effort.mjs";const T=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 I=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=_(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:c=r,spawnImpl:d=t,codexHost:p={}}={}){const m=String(e||"").trim();if(!m)throw new Error("codex one-shot requires a prompt");const f=!l,h=l||c.mkdtempSync(o.join(u,"thinkpool-codex-one-shot-"));let x=null;try{i&&(x=o.join(h,"output-schema.json"),c.writeFileSync(x,`${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)]:[],...x?["--output-schema",x]:[],m];return await S(()=>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=d("codex",e,{cwd:h,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))}),p)}finally{if(f)try{c.rmSync(h,{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 normalizeCodexServiceTier(e){const t=String(e||"").trim().toLowerCase();return"priority"===t?"fast":"fast"===t||"default"===t?t:null}export function readCodexDefaultServiceTier({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*service_tier\s*=\s*["']([^"']+)["']\s*(?:#.*)?$/m);return normalizeCodexServiceTier(r?.[1])}catch{return null}}const E=e=>[...new Set((Array.isArray(e)?e:[]).map(e=>normalizeCodexServiceTier("string"==typeof e?e:e?.id)).filter(Boolean))];export function codexModelSupportsFastMode({models:e=[],model:t}={}){const r=(Array.isArray(e)?e:[]).find(e=>e?.value===t);return E(r?.serviceTiers).includes("fast")}const F=e=>"fast"===normalizeCodexServiceTier(e)?"priority":normalizeCodexServiceTier(e);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:M(e.supported_reasoning_levels),serviceTiers:E(e.service_tiers||e.additional_speed_tiers)}))}catch{return[]}}const N=new Map;function O(e,{home:t,fsImpl:r}){if(!e)return null;const n=`${t}\0${e}`,s=N.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 N.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=O(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=O(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 T.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}),c=[o?d:"",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 c.length?`<thinkpool-context>\n${c.join("\n\n")}\n</thinkpool-context>\n\n${String(e??"")}`:String(e??"")}export function buildCodexExecArgs({sessionId:e,model:t,serviceTier:r,effort:n,sandbox:s,approvalPolicy:a="untrusted",providerConfig:i,mcpUrl:l,writableDirs:u=[],images:c=[],prompt:d}){const p=normalizeCodexSandbox(s),m=[...b(),...e?["exec","resume","--json","--skip-git-repo-check",..."danger-full-access"===p?["--dangerously-bypass-approvals-and-sandbox"]:["-c",`sandbox_mode="${p}"`]]:["exec","--json","--sandbox",p,"--skip-git-repo-check"]];t&&m.push("-m",t);const f=normalizeCodexServiceTier(r);f&&(m.push("-c",`service_tier=${JSON.stringify(f)}`),"fast"===f&&m.push("-c","features.fast_mode=true")),m.push("-c",`approval_policy="${a}"`);const h=_(n);if(h&&m.push("-c",`model_reasoning_effort="${h}"`),i&&m.push("-c",i),!e)for(const e of u)m.push("--add-dir",e);l&&(m.push("-c",`mcp_servers.thinkpool.url=${JSON.stringify(l)}`),m.push("-c","mcp_servers.thinkpool.required=true"));let x=!1;for(const e of c)o.isAbsolute(String(e||""))&&(m.push("--image",String(e)),x=!0);return x&&m.push("--"),e&&m.push(e),m.push(String(d??"")),m}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 R=/\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:R.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 A(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:d,models:p=[],serviceTier:h=null,effort:b="high",resume:M,env:T,sandbox:E,mode:N="default",onEvent:O,providerConfig:R,terminalRolePrompt:P,rolePrompt:D,roomContext:U,mcpServers:j,prepareCwd:$=null,requestPermission:L,appServerGate:q=i,appServerFactory:z=l,mcpHttpFactory:G=c,spawnImpl:X=t,admitStart:H=null,stallOptions:B={},codexHost:W={}}){let J=CODEX_MODE_CONFIG[N]?N:"default",K=codexConfigForMode(J);E=normalizeCodexSandbox(E||K.sandbox);const V=codexPublishAccess({cwd:e,env:{...process.env,...T||{}}}),Y=V.env,Q=M||null,Z=!Q||codexThreadCanResume(Q,{home:Y.CODEX_HOME||o.join(n.homedir(),".codex")});let ee=Z?Q:null,te=ee?1:0,re=0,ne=!0;const oe=m(U);let se=null,ae=!1,ie=!1,le=!1;const ue="function"==typeof B.now?B.now:Date.now,ce="function"==typeof B.setInterval?B.setInterval:setInterval,de="function"==typeof B.clearInterval?B.clearInterval:clearInterval,pe=Number.isFinite(B.stallMs)?Math.max(1,Number(B.stallMs)):Math.max(3e4,parseInt(Y.TP_STALL_MS,10)||9e4),me=Number.isFinite(B.forceStopMs)?Math.max(pe,Number(B.forceStopMs)):Math.max(3*pe,parseInt(Y.TP_FORCE_STOP_MS,10)||3e5),fe=Number.isFinite(B.longReasoningForceStopMs)?Math.max(me,Number(B.longReasoningForceStopMs)):Math.max(me,parseInt(Y.TP_LONG_REASONING_FORCE_STOP_MS,10)||12e5),he=Number.isFinite(B.intervalMs)?Math.max(1,Number(B.intervalMs)):5e3;let xe=ue(),ge=!1,ye=!1,ve=!1,we=!1,be=!0,Se=0,_e=0,Ce=null,ke=d||null;const Me=new Set((Array.isArray(p)?p:[]).map(e=>"string"==typeof e?e:e?.value||e?.slug).filter(Boolean));let Te=normalizeCodexServiceTier(h);"fast"!==Te||codexModelSupportsFastMode({models:p,model:ke})||(Te="default");let Ie=k(b,{models:p,model:ke}),Ee=ee?readCodexThreadUsage(ee):null,Fe=null;const Ne=q({env:Y});let Oe=!Ne.enabled,Re=null,Ae=!1;const Pe=new WeakSet;let De=null,Ue=null,je=!1;const $e=new Set,Le=new Map,qe=new Set,ze=new Map;let Ge=Promise.resolve(),Xe=Promise.resolve();const He=[],Be=y(e=>{try{O?.(e)}catch{}}),We=()=>{xe=ue(),ge=!1},Je=e=>{We(),Be(e)};Je.cancel=()=>Be.cancel();const Ke=new u({onEvent:Je,model:d||null,usageSnapshotForSession:e=>{const t=readCodexThreadUsage(e);return t&&(Ee=t),t||Ee}}),Ve=()=>{const e=[...ze.values()];ze.clear();for(const t of e)t.text&&Je({kind:"assistant",blocks:[{type:"text",text:t.text}],parentToolUseId:null,replacesCid:t.cid})},Ye=e=>{"turn.completed"!==e?.type&&"turn.failed"!==e?.type||(Ve(),le=!1),Ke.push(e)},Qe=e=>{Ve(),le=!1,Je(e)},Ze=({retry:e=!1,recoveryAttempt:t=0}={})=>{le=!0,xe=ue(),ge=!1,ve=!1,we=!1,be=!0,Se=Math.max(0,Number(t)||0),e||(ye=!1)},et=e=>Je({kind:"note",text:e}),tt=(e=De)=>{const t=String(e||"");if(t)for($e.add(t);$e.size>64;)$e.delete($e.values().next().value)},rt=(e={})=>{const t=String(e.turnId||"");return!(!t||ie||ae||$e.has(t)||t!==String(De||""))};async function nt(){const e=j?.thinkpool;return e?(Fe||(Fe=G({sdkServer:e})),Fe):null}function ot(e,t={}){if("thread/tokenUsage/updated"===e){if(t.threadId&&ee&&String(t.threadId)!==String(ee))return;(le||je)&&We();const e=codexUsageSnapshotFromAppServer(t.tokenUsage);if(!e)return;if(Ee=e,je){const{used:t,max:r}=e.context;Je({kind:"usage",ctx:{used:t,max:r,pct:Math.min(100,Math.max(0,Math.round(t/r*100))),over:t>r,model:ke}})}return}if("turn/started"===e&&t.turn?.id){const e=String(t.turn.id);if(ie||ae)return void tt(e);if(Ue){const t=Ue;return Ue=null,void t.resolve(e)}return De||(De=e),void We()}if("turn/plan/updated"!==e&&"item/agentMessage/delta"!==e&&"item/started"!==e&&"item/completed"!==e&&"error"!==e||rt(t)){if(We(),"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 Je({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||De||"active"}`,r=ze.get(e)||{cid:`codex-stream:${ee||"thread"}:${e}`,text:""};return r.text+=String(t.delta),ze.set(e,r),void Je({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&&(Le.set(r.id,r),r.id&&I(r)&&qe.add(r.id)),"item/completed"===e&&(r.id&&qe.delete(r.id),Le.set(r.id,r),"agentMessage"===r.type&&ze.has(r.id))){const e=ze.get(r.id);return ze.delete(r.id),void Je({kind:"assistant",blocks:[{type:"text",text:r.text||e.text}],parentToolUseId:null,replacesCid:e.cid})}return void Ye({type:"item/started"===e?"item.started":"item.completed",item:A(r)})}if("error"===e){const e=t.error?.message||t.message||"codex app-server error";Ye({type:"error",message:e})}}}async function st(e,t={}){if(!rt(t))return"item/tool/requestUserInput"===e?{answers:{}}:{decision:"decline"};const r=codexApprovalCard(e,t,Le.get(t.itemId));if(!r)throw new Error(`Unsupported Codex App Server request: ${e}`);let n="deny";_e++;try{n=await(L?.(r))}catch{}finally{_e=Math.max(0,_e-1),We()}return"item/tool/requestUserInput"===e?codexUserInputResponse(n,r.questions):codexApprovalResponse(n)}async function at(){if(Oe)return!1;if(!1===Re?.alive)return Ae=!1,Oe=!0,!1;if(Re&&Ae)return!0;let t;try{t=await nt()}catch(e){et(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{if(!Re){const r=z({cwd:e,env:Y,args:a({providerConfig:R,mcpUrl:t?.url,writableDirs:codexExtraWritableDirsForMode(J,E,V.writableDirs),serviceTier:Te}),onNotification:ot,onServerRequest:st,codexHost:{...W,log:et},onClose:e=>{ie||Oe||Pe.has(r)||(Ae=!1,Oe=!0,et(`Codex App Server closed; later turns will use codex exec: ${e?.message||e}`))}});Re=r,await Re.start(),We()}return ee=await Re.startThread({threadId:te>0?ee:null,cwd:e,model:ke,serviceTier:F(Te),sandbox:E,approvalPolicy:K.approvalPolicy,config:{"features.default_mode_request_user_input":!0}}),We(),t?.url&&(await Re.waitForMcpServer({threadId:ee,name:"thinkpool"}),We()),Ae=!0,Ye({type:"thread.started",thread_id:ee}),!0}catch{Oe=!0;try{Re?.end()}catch{}return Re=null,Ae=!1,!1}}function it({force:e=!1}={}){const t=Re,r=De;if(t&&e){tt(r),Oe=!0,Ae=!1;try{t.end()}catch{}Re===t&&(Re=null)}else if(t&&r)tt(r),Promise.resolve(t.interrupt({threadId:ee,turnId:r})).catch(()=>{Oe=!0,Ae=!1;try{t.end()}catch{}Re===t&&(Re=null)});else if(t){Oe=!0,Ae=!1;try{t.end()}catch{}Re===t&&(Re=null)}if(se){const e=se;try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(se===e)try{e.kill("SIGKILL")}catch{}},1500)}}async function lt(e,t={}){if(ie||ae||we)return!0;if(!await at())return!1;if(ie||ae||we)return!0;Ke.setUsageBaseline(ee?readCodexThreadUsage(ee):null),le=!0;try{if(be=!1,De=await Re.startTurn({threadId:ee,input:e,images:Array.isArray(t.images)?t.images:[],model:ke,serviceTier:F(Te),effort:_(Ie),approvalPolicy:K.approvalPolicy,collaborationMode:codexDefaultCollaborationMode(ke,Ie)}),We(),ae||ie){tt(De);try{await Re.interrupt({threadId:ee,turnId:De})}catch{}return!0}const r=await Re.waitForTurn(De);tt(r?.turn?.id||De);const n=r?.turn?.status;ae?Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ve||we)||("interrupted"===n?Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ve=!1,Ye({type:"turn.failed",message:r?.turn?.error?.message||"codex turn failed"})):(ve=!1,Ye({type:"turn.completed"})))}catch(e){Oe=!0,Ae=!1,tt(De);try{Re?.end()}catch{}Re=null,ie||ve||we||Ye({type:"turn.failed",message:`codex app-server turn failed: ${e?.message||e}`})}finally{Ve(),De=null,le=!1,qe.clear(),Le.clear(),ze.clear()}return!0}async function ut(e){if(ie||ae||we)return!0;if(!await at())return!1;if(ie||ae||we)return!0;Ke.setUsageBaseline(ee?readCodexThreadUsage(ee):null),le=!0;try{be=!1;const t=await Re.startReview({threadId:ee,target:x(e)});if(De=t?.turn?.id||t?.turnId,!De)throw new Error("Codex review/start returned no turn id");if(We(),ae||ie){tt(De);try{await Re.interrupt({threadId:ee,turnId:De})}catch{}return!0}const r=await Re.waitForTurn(De);tt(r?.turn?.id||De);const n=r?.turn?.status;ae?Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"interrupted"===n&&(ve||we)||("interrupted"===n?Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:r?.turn?.durationMs,denials:0,resultText:null}):"failed"===n?(ve=!1,Ye({type:"turn.failed",message:r?.turn?.error?.message||"codex review failed"})):(ve=!1,Ye({type:"turn.completed"})))}catch(e){Oe=!0,Ae=!1,tt(De);try{Re?.end()}catch{}Re=null,ie||ve||we||Ye({type:"turn.failed",message:`codex review failed: ${e?.message||e}`})}finally{Ve(),De=null,le=!1,qe.clear(),Le.clear(),ze.clear()}return!0}async function ct(t,n={}){if(ie||ae||we)return;let o;try{o=await nt()}catch(e){et(`thinkpool peer tools unavailable: ${e?.message||e}`)}try{await(o?.handoff?.())}catch(e){et(`thinkpool peer handoff failed: ${e?.message||e}`)}const a=buildCodexExecArgs({sessionId:0===te?null:ee,model:ke,serviceTier:Te,effort:Ie,sandbox:E,approvalPolicy:K.approvalPolicy,providerConfig:R,mcpUrl:o?.url,writableDirs:codexExtraWritableDirsForMode(J,E,V.writableDirs),images:Array.isArray(n.images)?n.images:[],prompt:t});Ke.setUsageBaseline(ee?readCodexThreadUsage(ee):null);const i=()=>new Promise((t,n)=>{let o;try{o=r.openSync("/dev/null","r")}catch{}const i={cwd:e,env:Y,stdio:["ignore","pipe","pipe"]};null!=o&&(i.stdio=[o,"pipe","pipe"]),le=!0,be=!1;try{se=X("codex",a,i)}catch(e){if(null!=o)try{r.closeSync(o)}catch{}return le=!1,e.codexExecFailure={spawnFailure:!0,hostRetrySafe:!0,message:`codex failed to start: ${e.message}`,sawTerminalResult:!1},void n(e)}if(null!=o)try{r.closeSync(o)}catch{}const l=se,u=[];s.createInterface({input:l.stdout,crlfDelay:1/0}).on("line",e=>{if(!e.trim())return;let t;try{t=JSON.parse(e)}catch{return}We(),u.push(t.type),"item.started"===t.type&&t.item?.id&&I(t.item)&&qe.add(t.item.id),"item.completed"===t.type&&t.item?.id&&qe.delete(t.item.id),"thread.started"===t.type&&t.thread_id&&(ee=ee||t.thread_id),"turn.completed"!==t.type&&"turn.failed"!==t.type||(d=!0,ve=!1),Ye(t)});let c="",d=!1,p=!1;l.stderr.on("data",e=>{c+=e.toString(),c.length>800&&(c=c.slice(-800))}),l.on("error",e=>{p||(p=!0,se===l&&(se=null),le=!1,qe.clear(),e.codexExecFailure={spawnFailure:!0,hostRetrySafe:0===u.length,message:`codex failed to start: ${e.message}`,sawTerminalResult:!1},n(e))}),l.on("close",e=>{if(p)return;p=!0,se===l&&(se=null),le=!1,qe.clear();const r=`codex exec exited ${e}${c?": "+c.trim().slice(-300):""}`,o=0!==e&&null!=e&&!d&&codexExecFailedBeforeTurn({stderr:c,eventTypes:u});if(!d&&0!==e&&null!=e){const e=new Error(r);return e.codexExecFailure={preTurnInitFailure:o,hostRetrySafe:0===u.length,message:r,sawTerminalResult:!1},void n(e)}ae&&!d&&Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),t({preTurnInitFailure:!1})})});try{return await S(i,{...W,log:et,shouldHeal:e=>!0===e?.codexExecFailure?.hostRetrySafe&&!0!==e?.codexExecFailure?.preTurnInitFailure})}catch(e){se=null,le=!1,qe.clear();const t=e?.codexExecFailure||e?.cause?.codexExecFailure||{};if("TP_CODEX_HOST_MISSING"===e?.code)Qe({kind:"error",message:e.message,recoverable:!0});else{if(t.preTurnInitFailure&&!ae&&!we)return{preTurnInitFailure:!0,message:t.message};(!ve&&!we||ae)&&Qe(ae?{kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}:{kind:"error",message:t.message||e?.message||"codex failed to start",recoverable:!0})}return{preTurnInitFailure:!1}}}function dt(){if(ie)return;const e=He.shift();e&&(Xe=Xe.then(async()=>{const t=Math.max(0,Number(e.options?._stallRecoveryAttempt)||0);Ze({recoveryAttempt:t}),t>0&&Be({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:P,rolePrompt:D,roomContext:()=>oe({force:n}),firstTurn:0===te,promptIndex:e.promptIndex,forceFullReminder:e.forceFullReminder,turnCause:r}),s=/^\s*\/review(?:\s|$)/i.test(String(e.text||""));let a=!1,i=!1;for(;!ie&&!ae&&!we;){if(!(s?await ut(e.text):await lt(o,e.options)))if(s)ve=!1,Qe({kind:"error",message:"Codex review is unavailable without the tested App Server runtime.",recoverable:!0});else{ve&&(ve=!1,a=!0,Ze({retry:!0,recoveryAttempt:t}),Be({kind:"note",text:"retrying the stalled turn on a fresh connection"}));const r=await ct(o,e.options);if(r?.preTurnInitFailure){if(!(i||ie||ae||we)){i=!0,Ze({retry:!0,recoveryAttempt:t}),Be({kind:"note",text:"Codex initialization failed before the turn started; retrying once with a fresh thinkpool connection."});continue}Qe({kind:"error",message:r.message,recoverable:!0})}}if(!ve||a||we||ie||ae)break;ve=!1,a=!0,Ze({retry:!0,recoveryAttempt:t}),Be({kind:"note",text:"retrying the stalled turn on a fresh connection"})}te++,dt()}))}return Q&&!Z&&queueMicrotask(()=>et("The stopped Codex turn had no resumable context; continuing in a fresh thread.")),["0","false","off"].includes(String(Y.TP_CODEX_APP_SERVER||"").trim().toLowerCase())||Ne.enabled||queueMicrotask(()=>et(`Codex App Server disabled: ${Ne.reason}`)),Ce=ce(()=>{const e=ue()-xe,t=v({turnActive:le,awaitingUser:_e,quietMs:e,stallMs:pe,forceStopMs:["max","ultra"].includes(Ie)?fe:me,stalledSent:ge,stallRetried:ye});if("none"===t)return;if("retry"===t&&!be)return we=!0,ve=!1,Se>0?(Be({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."}),Be({kind:"suggestion",text:CODEX_STALL_RETRY_SUGGESTION})):qe.size>0?Be({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."}):(He.unshift({text:CODEX_STALL_CONTINUATION_PROMPT,options:{_stallRecoveryAttempt:1},promptIndex:re++,forceFullReminder:!0}),Be({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."})),Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),void it({force:!0});const r=w(t,e);if(r&&Be(r),"status"!==t){if("retry"===t)return ye=!0,ve=!0,xe=ue(),ge=!1,void it();we=!0,ve=!1,He.length=0,Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:1,durationMs:void 0,denials:0,resultText:null}),it({force:!0})}else ge=!0},he),Ce.unref?.(),{get sessionId(){return ee},get usageSnapshot(){return Ee},get turnActive(){return le||je},get canSteer(){return!(!Re||!1===Re.alive||!Ae||Oe)},get started(){return le||je||!!se||!(!Re||!1===Re.alive)},sendTurn(t,r={}){if(ie)return!1;if(!le&&0===He.length&&!Ae&&!se){const e="function"==typeof H?H():{ok:!0};if(!1===e?.ok)return Je({kind:"error",message:e.reason||"Host memory is critically low. This Codex runtime was not started.",recoverable:!0}),!1}const n=re++,o=ne;if(ne=g(t)||/^\s*\/(?:reset|clear)\b/i.test(String(t||"")),!le&&0===te&&$)try{e=$()||e}catch{}if(le&&Re&&De){We();const e=r?.turnCause||null,s=f({promptIndex:n,forceFull:o,turnCause:e}),a=buildCodexPrompt({text:t,terminalRolePrompt:P,rolePrompt:D,roomContext:()=>oe({force:s}),promptIndex:n,forceFullReminder:o,turnCause:e}),i=De;return Ge=Ge.then(()=>Re.steer({threadId:ee,turnId:i,input:a,images:Array.isArray(r.images)?r.images:[]})).catch(e=>{"rejected"===e?.delivery||"not_sent"===e?.delivery?(He.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),le||1!==He.length||dt()):Je({kind:"error",message:"Codex steering delivery is uncertain; the message was not replayed automatically.",recoverable:!0})}),!0}return He.push({text:t,options:r,promptIndex:n,forceFullReminder:o}),le||je||(ae=!1,Ze({recoveryAttempt:0})),1!==He.length||je||dt(),!0},async park(){if(ie||le||je||He.length)return!1;const e=Xe;try{await e}catch{}try{await Ge}catch{}if(ie||le||je||He.length||se||De)return!1;const t=Re;if(t){Pe.add(t),Re=null,Ae=!1,Le.clear(),ze.clear();try{t.end()}catch{return!1}}return!0},abort(){const e=Xe,t=le&&!De&&!se;ae=!0,ve=!1,we=!1,He.length=0,tt(De);let r=Promise.resolve();if(Re&&De&&(r=Re.interrupt({threadId:ee,turnId:De}).catch(()=>{})),se){try{se.kill("SIGTERM")}catch{}setTimeout(()=>{try{se?.kill("SIGKILL")}catch{}},1500)}else t&&Qe({kind:"result",subtype:"aborted",sessionId:ee,model:ke,costUsd:null,usage:null,numTurns:0,durationMs:void 0,denials:0,resultText:null});return r.then(()=>e).catch(()=>e).then(()=>{})},end(){if(ie=!0,le=!1,Ce&&de(Ce),Je.cancel(),He.length=0,tt(De),se)try{se.kill()}catch{}try{Re?.end()}catch{}Fe?.then(e=>e.close()).catch(()=>{})},setModel(e){if(!e||le)return!1;const t=String(e);if(!Me.has(t))return!1;ke=t,Ke.setModel(ke);const r=k(Ie,{models:p,model:ke});return r!==Ie&&(Ie=r,Je({kind:"effort",level:Ie})),"fast"!==Te||codexModelSupportsFastMode({models:p,model:ke})||(Te="default"),!0},setServiceTier(e){if(le||je||He.length)return!1;const t=normalizeCodexServiceTier(e);if(!t)return!1;if("fast"===t&&!codexModelSupportsFastMode({models:p,model:ke}))return!1;if(t===Te)return!0;Te=t;const r=Re;if(r){Pe.add(r),Re=null,Ae=!1,Le.clear(),ze.clear();try{r.end()}catch{}}return!0},setMode:e=>!(le||!CODEX_MODE_CONFIG[e]||(J=e,K=codexConfigForMode(J),E=K.sandbox,ee=null,te=0,Ee=null,Ae=!1,0)),setEffort:e=>!(le||!C({models:p,model:ke}).includes(e)||(Ie=e,Je({kind:"effort",level:Ie}),0)),get serviceTier(){return Te},get supportsFastMode(){return codexModelSupportsFastMode({models:p,model:ke})},clearContext:()=>!le&&(ee=null,te=0,Ee=null,Ae=!1,!0),async compactContext(){if(le||je)return!1;ae=!1,je=!0;let e=null,t=!1;try{if(!await at()||le)return!1;const r=new Promise((t,r)=>{e=setTimeout(()=>{Ue=null,r(new Error("Codex did not start the compaction turn"))},5e3),Ue={resolve:t,reject:r}});await Re.compact({threadId:ee}),t=!0;const n=await r;e&&clearTimeout(e);const o=await Re.waitForTurn(n);tt(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)?(et(`Native Codex compaction status is uncertain; existing context was preserved: ${e?.message||e}`),null):(et(`Native Codex compaction unavailable; using bounded recap fallback: ${e?.message||e}`),!1)}finally{e&&clearTimeout(e),Ue=null,je=!1,He.length&&dt()}},async accountUsage(){if(!await at())return null;const[e,t]=await Promise.all([Re.accountUsage().catch(()=>null),Re.accountRateLimits().catch(()=>null)]);return{usage:e,limits:t}}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{claimTurnReceipt as e}from"./turn-receipt.mjs";const t=4194304,n=new Set(["received","prepared","dispatching","dispatched","completed","recovery_required"]),r=e=>Buffer.byteLength(JSON.stringify(e)),o=()=>new Error("Command journal capacity is full; pending commands must be resolved first.");export function restoreCommandJournal(e){const a=(Array.isArray(e)?e:[]).filter(e=>e&&"string"==typeof e.cid&&n.has(e.state));if(a.length>128||a.reduce((e,t)=>e+r(t.payload),0)>t)throw o();return a}export function recordCommand(e,n){const a="string"==typeof n?.cid?n.cid:"";if(!a)return null;const i=e.commandJournal||(e.commandJournal=[]),d=i.find(e=>e.cid===a);if(d)return d;const s=JSON.stringify(n),c=Buffer.byteLength(s);if(c>1048576)throw new Error("Command is too large to persist safely.");let m=i.reduce((e,t)=>e+r(t.payload),0);for(;i.length>=128||m+c>t;){const e=i.findIndex(e=>"completed"===e.state);if(e<0)throw o();m-=r(i[e].payload),i.splice(e,1)}const u={cid:a,state:"received",payload:JSON.parse(s),createdAt:Date.now(),updatedAt:Date.now()};return i.push(u),u}export function transitionCommand(e,t,r){if(!n.has(r))throw new TypeError(`invalid command journal state: ${r}`);const o=e?.commandJournal?.find(e=>e.cid===t);return o?("completed"===o.state&&"dispatched"===r||(o.state=r,o.updatedAt=Date.now()),o):null}export function acceptDurableCommand(t,n){if(t.receivedTurnCids?.has(n.cid))return{duplicate:!0,claimed:!1};const r=[...t.commandJournal||[]],o=new Set(t.receivedTurnCids||[]);try{recordCommand(t,n);const r=e(t,n.cid);if(!0!==t.flush?.())throw new Error("Command persistence failed; no receipt was issued.");return r}catch(e){throw t.commandJournal=r,t.receivedTurnCids=o,e}}export function beginCommandDispatch(e,t){if(!t)return;const n=e.commandJournal?.find(e=>e.cid===t);if(!n)throw new Error("Command has no durable receipt.");const r=n.state;if(transitionCommand(e,t,"dispatching"),!0!==e.flush?.())throw n.state=r,new Error("Command dispatch persistence failed; agent was not called.");const o=e._dispatchingCommandCids||(e._dispatchingCommandCids=[]);o.includes(t)||o.push(t)}export function takeCommandResult(e){return e._dispatchingCommandCids?.shift()||null}export function completeCommand(e,t){if(!t)return!1;const n=e.commandJournal?.find(e=>e.cid===t);if(!n)return!1;const r=n.state;return transitionCommand(e,t,"completed"),!0!==e.flush?.()?(n.state=r,!1):(e._dispatchingCommandCids&&(e._dispatchingCommandCids=e._dispatchingCommandCids.filter(e=>e!==t)),!0)}export function recoverableCommands(e){const t=[];for(const n of e?.commandJournal||[])"received"===n.state||"prepared"===n.state?t.push({...n,recovery:"safe_redelivery"}):"completed"!==n.state&&(n.state="recovery_required",n.updatedAt=Date.now(),t.push({...n,recovery:"dispatch_uncertain"}));return t}
|
package/event-delivery-queue.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=e=>new Promise(t=>setTimeout(t,e));export const acceptedRealtimeDelivery=e=>"ok"===e||!0===e?.success;export function createEventDeliveryQueue({send:
|
|
1
|
+
const e=e=>new Promise(t=>setTimeout(t,e)),t=e=>{try{return Buffer.byteLength(JSON.stringify(e))}catch{return 1/0}};export const acceptedRealtimeDelivery=e=>"ok"===e||!0===e?.success;export function createEventDeliveryQueue({send:r,wait:n=e,retryDelay:l=e=>Math.min(1e4,250*2**Math.min(e,6)),maxFrames:c=4096,maxBytes:a=8388608,sizeOf:s=t,onDrained:i=null}={}){if("function"!=typeof r)throw new TypeError("event delivery queue requires send(frame)");const o=[];let u=0,y=!1,f=!1,h=Promise.resolve(),p=null;const m=e=>{const[t]=o.splice(e,1);return t&&(u-=t.bytes),t},d=async()=>{if(!y&&!f){y=!0;try{for(;!f&&o.length;){const e=o[0];let t=null;try{t=await r(e.frame)}catch{t=null}acceptedRealtimeDelivery(t)||!1===e.retry?m(0):(e.attempt+=1,await n(l(e.attempt)))}}finally{if(y=!1,!o.length){p&&(p(),p=null);try{i?.()}catch{}}!f&&o.length&&d()}}};return{enqueue(e,{coalesceKey:t=null,retry:r=!0}={}){if(f)return{accepted:!1,reason:"closed"};let n=1/0;try{n=s(e)}catch{n=1/0}if(!Number.isFinite(n)||n>a)return{accepted:!1,reason:"frame_too_large"};if(o.length||y||(h=new Promise(e=>{p=e})),t){const e=(e=>{for(let t=o.length-1;t>=(y?1:0)&&!o[t].barrier;t--)if(o[t].coalesceKey===e)return t;return-1})(t);e>=0&&m(e)}for(;o.length>=c||u+n>a;){const e=o.findIndex((e,t)=>t>=(y?1:0)&&e.coalesceKey);if(e<0)return{accepted:!1,reason:"backpressure",pending:o.length,bytes:u};m(e)}return o.push({frame:e,bytes:n,attempt:0,coalesceKey:t,retry:r,barrier:!t}),u+=n,d(),{accepted:!0,pending:o.length,bytes:u}},pending:()=>o.length,pendingBytes:()=>u,whenIdle:()=>h,close(){f=!0,o.length=0,u=0,p&&(p(),p=null)}}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinkpool-pair",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.436",
|
|
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",
|
|
@@ -96,6 +96,9 @@
|
|
|
96
96
|
"event-bounds.mjs",
|
|
97
97
|
"replay-transport.mjs",
|
|
98
98
|
"event-delivery-queue.mjs",
|
|
99
|
+
"transport-frame.mjs",
|
|
100
|
+
"turn-admission.mjs",
|
|
101
|
+
"durable-command-journal.mjs",
|
|
99
102
|
"mcp-flight-recorder.mjs",
|
|
100
103
|
"plan-meters.mjs",
|
|
101
104
|
"recap.mjs",
|
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.436",
|
|
5
5
|
"kind": "minified",
|
|
6
|
-
"minifiedModules":
|
|
7
|
-
"sourceModuleBytes":
|
|
8
|
-
"artifactModuleBytes":
|
|
6
|
+
"minifiedModules": 147,
|
|
7
|
+
"sourceModuleBytes": 1962774,
|
|
8
|
+
"artifactModuleBytes": 901772,
|
|
9
9
|
"files": {
|
|
10
|
-
"package.json": "
|
|
10
|
+
"package.json": "5833ab9ac6e1d72990fb74a057b182e70ce11ce5f213b5b0b6b7c8cf252473b6",
|
|
11
11
|
"LICENSE": "8a030197ca1cad9669909ca14c9ad15cf48d9fe551da526ccaf1fca993eddb23",
|
|
12
|
-
"bridge.mjs": "
|
|
12
|
+
"bridge.mjs": "da36ec2fb269640e7b391f46a69b2d1e9f4649ba94201cc1e95e2503d22b45c4",
|
|
13
13
|
"bridge-service.sh": "b64f27c473ca8f3a49190fd08c23947ab14ca9958d70b6fa53c7bddc2be91403",
|
|
14
14
|
"command-guidance.mjs": "b9b967b59ff4e77c9613abb0f28858bf3ac5b106a509e471039777b53bec3e29",
|
|
15
15
|
"abort-turn-barrier.mjs": "3734a5b7e2e940ec6d7fa8b5b04935e1429a23d1bd9e92b2ac2a2ecd9f77c75e",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"code-automations.mjs": "03c70058bc6d5a0b245f3fb8f5e2cb629c5ef7c1c6cd7d7fa018c40523b91036",
|
|
31
31
|
"terminal-name.mjs": "f39bb8d1e148ab9a74cbb96438d31e524d6597b8035f00d7a906c28c306c0cf8",
|
|
32
32
|
"claude-command-catalog.mjs": "36bd4342b2ae94b43a0072533ef2d1648c7473003964bbad433fdb1a8b2bfec1",
|
|
33
|
-
"codex-session.mjs": "
|
|
33
|
+
"codex-session.mjs": "95455ac269263ad91638d116bc8e157f590b7f93c90c08ef106631b003502ffd",
|
|
34
34
|
"codex-host-heal.mjs": "14ea191ed2072203be2edd40a41272f176aafc044753919f65b1071ce48f060f",
|
|
35
35
|
"question-response.mjs": "35a7ba53d3eb117717c18004d2a8441f8f37665b231a3c44f4bd9321ff59dd57",
|
|
36
36
|
"thinkpool-room-prompt.mjs": "b8cc82ccf4262f6431c2047bbff31ac29c47a0d6c68a64640a2c24d94df4a746",
|
|
@@ -94,15 +94,18 @@
|
|
|
94
94
|
"scheduled-run-admission.mjs": "3a2098d904c3aff571bf314e0182d5636d25d242cef52207e70f9dc1618c94f5",
|
|
95
95
|
"event-id.mjs": "09c75e7fc8da282fc00651add58e5d2f82d3020e0c4dddb9921250cf9a6fa8c0",
|
|
96
96
|
"event-bounds.mjs": "aa8eb8517c4aa81b3378f71401ed39c550cdc80e28e3a57ab46d0a5ef5c7c9c1",
|
|
97
|
-
"replay-transport.mjs": "
|
|
98
|
-
"event-delivery-queue.mjs": "
|
|
97
|
+
"replay-transport.mjs": "f17bd7ac0aaaaf096d7969ea8c54ee9d084e366b62a561bf5f656ed30c010465",
|
|
98
|
+
"event-delivery-queue.mjs": "0ed00938cca30627896a944fbdf8da21f91341e6d96a3ca190777843b302025f",
|
|
99
|
+
"transport-frame.mjs": "05ea57f34699d17131c0a2917d8e43c77e60186c1c2105bc89520d2e889e8aea",
|
|
100
|
+
"turn-admission.mjs": "d327a8a1226dfde72c71a2585f9e69230cb8cdfc978e78556241c3403156d06e",
|
|
101
|
+
"durable-command-journal.mjs": "787142debc5472299155b32d0684d594e9c577b669e50554b9b3b38619355e2c",
|
|
99
102
|
"mcp-flight-recorder.mjs": "30bf77f4d567f0449856d6da09f7a0a9c8d7e12c5dcaf200dc9e511a331425be",
|
|
100
103
|
"plan-meters.mjs": "f7f5aeb3d6a3462b754d3724367bbb331643ba58108e29e91b2aca2f613ab499",
|
|
101
104
|
"recap.mjs": "10e67aa933fa8bac4059878167d578f87db3184d7affd168c46c5134c6e97c8b",
|
|
102
105
|
"transcript-sanitize.mjs": "c7f4718f25f97c05bc7b4ad8e09f18cbda152c53ad6593af1ab99f0c132dbf01",
|
|
103
106
|
"agent-output-hygiene.mjs": "914b04daa5b60342b5e44737cfba99f2c8ac292826db1fe501f653814454c835",
|
|
104
107
|
"session-search.mjs": "d606fbab5c47357071e4919107aa00ad090f6e7d7e6dc929e0ac5a4d0cb2c538",
|
|
105
|
-
"session-store.mjs": "
|
|
108
|
+
"session-store.mjs": "b4b07ead290a74014b8669e2b86d79d781f34ee890265bc6b9476481f575e1a4",
|
|
106
109
|
"side-lane.mjs": "6a6bc6d8041872ba4472d70fd1cece62607417982a72b8721f4fbed77655cc6d",
|
|
107
110
|
"worker-completion.mjs": "3eab6abf9cde32e62b8bef676023bd59759836f88887d578d2db2e9fa092a151",
|
|
108
111
|
"cross-terminal.mjs": "08d3889516f0b7edd7628dccb61b32731ab1e033efcc19f597a3d29a053fe020",
|
package/replay-transport.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export function requestedReplayIds(
|
|
1
|
+
export function requestedReplayIds(e,r,t){const n=Array.isArray(r)?new Set(r.filter(Boolean)):null;return[...e].filter(([e])=>!n||n.has(e)).sort(([e],[r])=>e===t?-1:r===t?1:0)}export function replayChunkRanges(e,{budget:t=15e4,maxChunks:n=14,sizeOf:o=r}={}){if(!Array.isArray(e)||!e.length)return[];const a=[];let i=e.length,l=i,s=0;for(let r=e.length-1;r>=0;r--){let u=1/0;try{u=o(e[r])}catch{u=1/0}if(!Number.isFinite(u))return[];if(l<i&&s+u>t){if(a.push([l,i]),a.length>=n)break;i=r+1,l=i,s=0}l=r,s+=u}return l<i&&a.length<n&&a.push([l,i]),a.reverse()}const e=()=>new Promise(e=>setTimeout(e,0)),r=e=>{try{return Buffer.byteLength(JSON.stringify(e))}catch{return 1/0}},t=e=>"ok"===e||!0===e?.success;export function createLatestReplayPump({send:n,yieldTurn:o=e,maxViewers:a=32,maxQueuedBytes:i=4194304,maxConcurrent:l=2,maxFrameBytes:s=262144,maxRetries:u=2,operationTimeoutMs:c=1e4,identifyViewer:d=e=>"symbol"==typeof e?e:String(e||"*"),onError:f=null}={}){if("function"!=typeof n)throw new TypeError("replay pump requires send(frame)");const g=new Map;let y=0,m=0,h=0;const p=Math.min(s,i),w=(e,r)=>new Promise((t,n)=>{const o=new AbortController;let a;const i=(e,o)=>{clearTimeout(a),r.removeEventListener("abort",l),e?n(e):t(o)},l=()=>{o.abort(),i(new Error("replay_cancelled"))};r.aborted?l():(r.addEventListener("abort",l,{once:!0}),a=setTimeout(()=>{o.abort(),i(new Error("replay_deadline"))},Math.max(1,c)),Promise.resolve().then(()=>{if(o.signal.aborted)throw new Error("replay_cancelled");return e(o.signal)}).then(e=>i(null,e),e=>i(e)))}),b=async(e,r,t,n=null)=>{if(g.get(e)===r){g.delete(e);try{await(f?.({viewer:e,reason:t,error:n}))}catch{}}},v=()=>{if(!(m>=l||h+p>i))for(const[e,r]of g)r.running||r.scheduled||!r.source||(r.scheduled=!0,Promise.resolve().then(()=>x(e,r)))},x=async(e,a)=>{if(a.scheduled=!1,g.get(e)!==a||!a.source||a.running||m>=l||h+p>i)return;a.running=!0,m+=1,h+=p;let s=0;const c=a.generation,d=a.controller.signal;try{const i=a.source,l=await w(()=>i.next(),d);if(c!==a.generation||g.get(e)!==a)return;if(l.done){const r=a.onDone;return a.source=null,a.onDone=null,r&&await w(()=>r(),d),void(g.get(e)===a&&c===a.generation&&g.delete(e))}const f=l.value,m=r(f);if(!Number.isFinite(m)||m>p)return void await b(e,a,"frame_too_large");s=m,y+=s;let h=!1,v=null;for(let e=0;e<=u&&c===a.generation;e++){try{h=t(await w(e=>n(f,{signal:e}),d))}catch(e){v=e}if(h)break;await o()}if(c!==a.generation||g.get(e)!==a)return;if(!h)return void await b(e,a,"send_failed",v);await o()}catch(r){c===a.generation&&await b(e,a,"source_failed",r)}finally{y-=s,h-=p,a.running=!1,m-=1,g.get(e)!==a||a.source||g.delete(e),v()}};return{canAccept(e){const r=d(e);return!!r&&(g.has(r)||g.size<a)},enqueue(e,r,t=null){const n=d(e);if(!n)return{accepted:!1,reason:"invalid_viewer"};let o,i=g.get(n);if(!i&&g.size>=a)return{accepted:!1,reason:"viewer_limit"};try{o=(e=>{const r="function"==typeof e?e():e;return r?.[Symbol.asyncIterator]?r[Symbol.asyncIterator]():r?.[Symbol.iterator]?r[Symbol.iterator]():[][Symbol.iterator]()})(r)}catch{return{accepted:!1,reason:"invalid_source"}}return i||(i={generation:0,source:null,onDone:null,running:!1,scheduled:!1},g.set(n,i)),i.controller?.abort(),i.controller=new AbortController,i.generation+=1,i.source=o,i.onDone="function"==typeof t?t:null,v(),{accepted:!0,generation:i.generation}},cancel(e){const r=d(e),t=g.get(r);return!!t&&(t.generation+=1,t.controller?.abort(),t.source=null,t.onDone=null,t.running||g.delete(r),!0)},pending:e=>g.has(d(e))?1:0,stats:()=>({viewers:g.size,queuedBytes:y,active:m,reservedBytes:h})}}
|
package/session-store.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"node:os";import t from"node:fs";import n from"node:path";import{matchSessionEvents as r}from"./session-search.mjs";import{SPAWN as o}from"./cross-terminal.mjs";import{boundStructuredLog as c}from"./event-bounds.mjs";const s=t=>{const r=n.resolve(process.env.TP_PAIR_ROOT||n.join(e.homedir(),".thinkpool-pair")),o=n.resolve(r,t||"default");if(o!==r&&!o.startsWith(r+n.sep))throw new Error(`refused: room path escapes sandbox: ${JSON.stringify(t)}`);return o},i=e=>n.join(s(e),".archive");export const LIVE_SESSION_MAX=o.machineMax;function a(e){t.mkdirSync(s(e),{recursive:!0})}let u=0,l=null;function d(e,r){const o=n.join(n.dirname(e),`.${n.basename(e)}.tmp.${process.pid}.${Date.now()}.${u++}`);let c=null;try{c=t.openSync(o,"wx",384);const n=Buffer.from(r);for(let r=0;r<n.length;){const o=t.writeSync(c,n,r,n.length-r);if(o<=0)throw new Error(`short write while committing ${e}`);r+=o}return t.fsyncSync(c),t.closeSync(c),c=null,l?.({file:e,tmp:o}),t.renameSync(o,e),!0}catch(e){try{null!=c&&t.closeSync(c)}catch{}try{t.rmSync(o,{force:!0})}catch{}throw e}}export function setAtomicCommitHookForTest(e){l="function"==typeof e?e:null}function f(e,t,n){console.error(`[thinkpool-pair session-store] ${e} failed for ${t}: ${n?.message||n}`)}function m(e,r){try{t.mkdirSync(i(e),{recursive:!0});const o=n.basename(r),c=n.join(i(e),`${o}.${Date.now()}`);return t.renameSync(r,c),!0}catch(e){return f("archive session snapshot",r,e),!1}}function h(e){try{return t.readdirSync(s(e)).filter(e=>e.endsWith(".json")).map(r=>{const o=n.join(s(e),r);try{const e=JSON.parse(t.readFileSync(o,"utf8"));try{const n=t.statSync(o);e._bt=n.birthtimeMs||n.mtimeMs||0}catch{e._bt=0}return e}catch(e){return f("parse session snapshot",o,e),null}}).filter(Boolean)}catch{return[]}}const y=(e,t)=>n.join(s(e),`${t}.events.jsonl`);export function appendDurableEvents(e,n,r){if(e&&n&&Array.isArray(r)&&r.length)try{a(e),t.appendFileSync(y(e,n),r.map(e=>JSON.stringify(e)).join("\n")+"\n")}catch{}}export function seedDurableEvents(e,n,r){if(e&&n&&Array.isArray(r)&&r.length)try{a(e);const o=y(e,n);if(t.existsSync(o))return;t.writeFileSync(o,r.map(e=>JSON.stringify(e)).join("\n")+"\n")}catch{}}export function hasDurableArchive(e,n){try{return!!e&&!!n&&t.existsSync(y(e,n))}catch{return!1}}export function servesHistoryPage({payloadHost:e,bridgeName:t,hasSession:n,hasArchive:r}){return!(e&&e!==t||!n&&!r)}function p(e){let t=0,n=-1/0;for(let r=0;r<e.length;r++){const o=e[r]?.seq;"number"==typeof o&&(o<=n?(t=r,n=o):n=o)}return t}function S(e,n){const r=y(e,n);if(!t.existsSync(r))return null;const o=[];for(const e of t.readFileSync(r,"utf8").split("\n"))if(e)try{o.push(JSON.parse(e))}catch{}return o}export function readDurableOldestSeq(e,t){try{const n=S(e,t);if(!n)return null;for(let e=p(n);e<n.length;e++)if(n[e]&&null!=n[e].seq)return n[e].seq;return null}catch{return null}}export function readDurablePage(e,t,n,r=200){try{const o=S(e,t);if(!o)return{events:[],hasMore:!1};const c=p(o);let s=o.length;if(null!=n){const e=o.findIndex((e,t)=>t>=c&&e&&null!=e.seq&&e.seq>=n);e>=0&&(s=e)}const i=Math.max(c,s-r);return{events:o.slice(i,Math.max(i,s)),hasMore:i>c}}catch{return{events:[],hasMore:!1}}}export function searchDurableSessionEvents(e,t,n,{limit:o=200}={}){try{const c=[];for(const n of t||[]){const t=String(n?.term||"");if(!t)continue;const r=S(e,t);r&&c.push({term:t,termName:n.termName||"terminal",events:r.slice(p(r))})}return r(c,n,{limit:o})}catch{return{results:[],truncated:!1}}}const g=new Map;function x(e,r,o){const i=n.join(s(e),`${r}.json`);try{a(e);const u=Array.isArray(o?.log)?c(o.log):o?.log;return d(i,JSON.stringify({...o,...u?{log:u}:{},id:r,savedAt:Date.now()})),function(e){try{const r=loadNames(e),o=e=>{try{return JSON.parse(t.readFileSync(e,"utf8")).id}catch{return null}};t.readdirSync(s(e)).filter(e=>e.endsWith(".json")).map(r=>{const o=n.join(s(e),r);let c=0;try{c=JSON.parse(t.readFileSync(o,"utf8")).savedAt||0}catch{}return{p:o,f:r,m:c}}).sort((e,t)=>t.m-e.m).filter(e=>{const t=o(e.p);return!t||!r[t]}).slice(LIVE_SESSION_MAX).forEach(t=>m(e,t.p))}catch(t){f("prune live session snapshots",s(e),t)}}(e),!0}catch(e){return f("write session snapshot",i,e),!1}}export function saveSession(e,t,n){clearTimeout(g.get(t)),g.set(t,setTimeout(()=>x(e,t,n),1500))}export function flushSession(e,t,n){return clearTimeout(g.get(t)),x(e,t,n)}export function deleteSession(e,r){clearTimeout(g.get(r)),g.delete(r);const o=n.join(s(e),`${r}.json`);try{return!t.existsSync(o)||m(e,o)}catch(e){return f("archive closed session snapshot",o,e),!1}}const A=e=>n.join(s(e),".scheduled-outcomes"),v=e=>"string"==typeof e&&/^[A-Za-z0-9][A-Za-z0-9._:@-]{0,159}$/.test(e),O=e=>"string"==typeof e&&/^[A-Za-z0-9][A-Za-z0-9._:@-]{0,159}$/.test(e),w=e=>{try{const n=JSON.parse(t.readFileSync(A(e),"utf8"));return n&&"object"==typeof n&&!Array.isArray(n)?n:{}}catch{return{}}},b=e=>{if(!e||"object"!=typeof e||Array.isArray(e))return null;if(1===e.v&&"acknowledged"===e.state){const t=e.intent;return t&&"object"==typeof t&&!Array.isArray(t)&&O(t.runId)&&e.runId===t.runId?{intent:t,acknowledged:!0}:null}return O(e.runId)?{intent:e,acknowledged:!1}:null};export function savePendingScheduledOutcome(e,t,n){if(!v(t)||!n||"object"!=typeof n||Array.isArray(n)||!O(n.runId))return!1;try{a(e);const r=w(e);return r[t]=n,d(A(e),JSON.stringify(r)),!0}catch(t){return f("write scheduled outcome outbox",A(e),t),!1}}export function loadPendingScheduledOutcomes(e){return Object.entries(w(e)).map(([e,t])=>({terminalId:e,record:b(t)})).filter(({terminalId:e,record:t})=>v(e)&&t).map(({terminalId:e,record:t})=>({terminalId:e,intent:t.intent,...t.acknowledged?{acknowledged:!0}:{}})).slice(0,LIVE_SESSION_MAX)}export function loadPendingScheduledOutcome(e,t){return v(t)&&b(w(e)[t])?.intent||null}export function acknowledgePendingScheduledOutcome(e,t,n){if(!v(t)||!O(n))return!1;try{const r=w(e),o=b(r[t]);return!(!o||o.intent.runId!==n||(o.acknowledged||(r[t]={v:1,state:"acknowledged",runId:n,intent:o.intent},d(A(e),JSON.stringify(r))),0))}catch(t){return f("acknowledge scheduled outcome outbox",A(e),t),!1}}export function deletePendingScheduledOutcome(e,n){if(!v(n))return!1;try{const r=w(e);return!(n in r)||(delete r[n],Object.keys(r).length?d(A(e),JSON.stringify(r)):t.rmSync(A(e),{force:!0}),!0)}catch(t){return f("delete scheduled outcome outbox",A(e),t),!1}}export function commitRecordedScheduledOutcome(e,t,r){let o=!1;try{o="function"==typeof r&&!0===r()}catch(r){f("record scheduled outcome in session snapshot",n.join(s(e),`${t}.json`),r)}if(!o)return{ok:!1,snapshotRecorded:!1,outboxAcknowledged:!1,outboxDeleted:!1};const c=loadPendingScheduledOutcome(e,t);if(!acknowledgePendingScheduledOutcome(e,t,c?.runId))return{ok:!1,snapshotRecorded:!0,outboxAcknowledged:!1,outboxDeleted:!1};const i=deletePendingScheduledOutcome(e,t);return{ok:i,snapshotRecorded:!0,outboxAcknowledged:!0,outboxDeleted:i}}export function loadLatest(e){return h(e).sort((e,t)=>(t.savedAt||0)-(e.savedAt||0))[0]||null}export function canResume(e){return!!(e&&e.sessionId&&e.savedAt&&Date.now()-e.savedAt<432e5)}export function loadAll(e){return h(e).sort((e,t)=>(e.savedAt||0)-(t.savedAt||0))}export function loadArchived(e,r=40){try{return t.readdirSync(i(e)).map(r=>{const o=n.join(i(e),r);try{const e=t.statSync(o);if(!e.isFile())return null;const n=JSON.parse(t.readFileSync(o,"utf8"));return n&&Array.isArray(n.log)?{...n,_archivedAt:e.mtimeMs||0}:null}catch{return null}}).filter(Boolean).sort((e,t)=>(t._archivedAt||t.savedAt||0)-(e._archivedAt||e.savedAt||0)).slice(0,Math.max(1,Math.min(100,Number(r)||40)))}catch{return[]}}const j=e=>n.join(s(e),".pty-id");export function loadPtyId(e){try{return t.readFileSync(j(e),"utf8").trim()||null}catch{return null}}export function savePtyId(e,t){const n=j(e);try{return a(e),d(n,String(t)),!0}catch(e){return f("write PTY id",n,e),!1}}const N=e=>n.join(s(e),".names");export function loadNames(e){const n=N(e);try{return JSON.parse(t.readFileSync(n,"utf8"))||{}}catch(e){return"ENOENT"!==e?.code&&f("parse terminal names",n,e),{}}}export function saveNames(e,t){const n=N(e);try{return a(e),d(n,JSON.stringify(t||{})),!0}catch(e){return f("write terminal names",n,e),!1}}
|
|
1
|
+
import e from"node:os";import t from"node:fs";import n from"node:path";import{searchableSessionEventText as r}from"./session-search.mjs";import{SPAWN as s}from"./cross-terminal.mjs";import{boundStructuredLog as o}from"./event-bounds.mjs";const i=t=>{const r=n.resolve(process.env.TP_PAIR_ROOT||n.join(e.homedir(),".thinkpool-pair")),s=n.resolve(r,t||"default");if(s!==r&&!s.startsWith(r+n.sep))throw new Error(`refused: room path escapes sandbox: ${JSON.stringify(t)}`);return s},a=e=>n.join(i(e),".archive");export const LIVE_SESSION_MAX=s.machineMax;function c(e){t.mkdirSync(i(e),{recursive:!0})}let l=0,u=null;function f(e,r){const s=n.join(n.dirname(e),`.${n.basename(e)}.tmp.${process.pid}.${Date.now()}.${l++}`);let o=null;try{o=t.openSync(s,"wx",384);const n=Buffer.from(r);for(let r=0;r<n.length;){const s=t.writeSync(o,n,r,n.length-r);if(s<=0)throw new Error(`short write while committing ${e}`);r+=s}return t.fsyncSync(o),t.closeSync(o),o=null,u?.({file:e,tmp:s}),t.renameSync(s,e),!0}catch(e){try{null!=o&&t.closeSync(o)}catch{}try{t.rmSync(s,{force:!0})}catch{}throw e}}export function setAtomicCommitHookForTest(e){u="function"==typeof e?e:null}function d(e,t,n){console.error(`[thinkpool-pair session-store] ${e} failed for ${t}: ${n?.message||n}`)}function m(e,r){try{t.mkdirSync(a(e),{recursive:!0});const s=n.basename(r),o=n.join(a(e),`${s}.${Date.now()}`);return t.renameSync(r,o),!0}catch(e){return d("archive session snapshot",r,e),!1}}function y(e){try{return t.readdirSync(i(e)).filter(e=>e.endsWith(".json")&&!e.endsWith(".events.index.json")).map(r=>{const s=n.join(i(e),r);try{const e=JSON.parse(t.readFileSync(s,"utf8"));try{const n=t.statSync(s);e._bt=n.birthtimeMs||n.mtimeMs||0}catch{e._bt=0}return e}catch(e){return d("parse session snapshot",s,e),null}}).filter(Boolean)}catch{return[]}}const h=(e,t)=>n.join(i(e),`${t}.events.jsonl`),p=(e,t)=>n.join(i(e),`${t}.events.index.json`),S=1048576,g=8388608,b=262144,v=131072,x=new Map;function N(e,n,r,s){try{const o=p(e,n);let i=null;try{i=JSON.parse(t.readFileSync(o,"utf8"))}catch{}if(0!==s&&(!i||3!==i.v||i.size!==s))return;let a=s,c=Number.isFinite(i?.lastSeq)?i.lastSeq:null,l=Number.isFinite(i?.epochStart)?i.epochStart:s,u=Number.isFinite(i?.oldestSeq)?i.oldestSeq:null,d=Array.isArray(i?.points)?i.points.filter(e=>Number.isFinite(e?.seq)&&Number.isFinite(e?.offset)):[];for(const e of r){const t=Buffer.byteLength(JSON.stringify(e))+1,n=e?.seq;if("number"==typeof n){null!=c&&n<=c?(l=a,u=n,d=[]):null==u&&(u=n);const e=d.at(-1);(!e||n-e.seq>=64||a-e.offset>=v)&&d.push({seq:n,offset:a}),c=n}a+=t}const m=t.statSync(h(e,n));if(m.size!==a)return;f(o,JSON.stringify({v:3,size:a,epochStart:l,oldestSeq:u,lastSeq:c,points:d,dev:m.dev,ino:m.ino,mtimeMs:m.mtimeMs,ctimeMs:m.ctimeMs}))}catch{}}export function appendDurableEvents(e,n,r){if(e&&n&&Array.isArray(r)&&r.length)try{c(e);const s=h(e,n),o=t.existsSync(s)?t.statSync(s).size:0;t.appendFileSync(s,r.map(e=>JSON.stringify(e)).join("\n")+"\n"),N(e,n,r,o)}catch{}}export function seedDurableEvents(e,n,r){if(e&&n&&Array.isArray(r)&&r.length)try{c(e);const s=h(e,n);if(t.existsSync(s))return;t.writeFileSync(s,r.map(e=>JSON.stringify(e)).join("\n")+"\n"),N(e,n,r,0)}catch{}}export function hasDurableArchive(e,n){try{return!!e&&!!n&&t.existsSync(h(e,n))}catch{return!1}}export function servesHistoryPage({payloadHost:e,bridgeName:t,hasSession:n,hasArchive:r}){return!(e&&e!==t||!n&&!r)}function O(e,n){try{const r=h(e,n),s=JSON.parse(t.readFileSync(p(e,n),"utf8")),o=t.statSync(r);return 3!==s?.v||s.size!==o.size||s.dev!==o.dev||s.ino!==o.ino||s.mtimeMs!==o.mtimeMs||s.ctimeMs!==o.ctimeMs||!Number.isFinite(s.epochStart)||s.epochStart<0||s.epochStart>o.size||!Array.isArray(s.points)?null:s}catch{return null}}const w=e=>`${e.dev}:${e.ino}:${e.size}:${e.mtimeMs}:${e.ctimeMs}`;function A(e,t,n,r){let s=t,o=n;if(e.discarding){const t=s.indexOf(10);if(t<0)return;s=s.subarray(t+1),o+=t+1,e.discarding=!1}e.carry.length&&(s=Buffer.concat([e.carry,s]),o=e.carryOffset,e.carry=Buffer.alloc(0));let i=0;for(;;){const e=s.indexOf(10,i);if(e<0)break;const t=s.subarray(i,e);t.length<=b&&r(t,o+i),i=e+1}const a=s.subarray(i);a.length>b?(e.carry=Buffer.alloc(0),e.discarding=!0):(e.carry=Buffer.from(a),e.carryOffset=o+i)}function q(e,n){let r;try{r=t.statSync(h(e,n))}catch{return}const s=w(r),o=`${e}\0${n}`,i=x.get(o);if(i?.signature===s&&(i.active||i.attempts>=2))return;const a=i?.signature===s?i.attempts+1:1;x.set(o,{signature:s,attempts:a,active:!0}),j().then(async()=>{const r=await async function(e,n,r){const s=h(e,n);let o;try{const i=await t.promises.stat(s);if(w(i)!==r)return;o=await t.promises.open(s,"r");const a={carry:Buffer.alloc(0),carryOffset:0,discarding:!1};let c=0,l=0,u=null,d=null,m=[];const y=(e,t)=>{let n;try{n=JSON.parse(e.toString("utf8"))}catch{return}if("number"!=typeof n?.seq)return;null!=d&&n.seq<=d?(l=t,u=n.seq,m=[]):null==u&&(u=n.seq);const r=m.at(-1);(!r||n.seq-r.seq>=64||t-r.offset>=v)&&m.push({seq:n.seq,offset:t}),d=n.seq};for(;c<i.size;){const e=Math.min(65536,i.size-c),t=Buffer.allocUnsafe(e),{bytesRead:n}=await o.read(t,0,e,c);if(!n)break;A(a,t.subarray(0,n),c,y),c+=n,await j()}!a.discarding&&a.carry.length&&y(a.carry,a.carryOffset);const h=await t.promises.stat(s);if(w(h)!==r)return;return f(p(e,n),JSON.stringify({v:3,size:h.size,epochStart:l,oldestSeq:u,lastSeq:d,points:m,dev:h.dev,ino:h.ino,mtimeMs:h.mtimeMs,ctimeMs:h.ctimeMs})),!0}catch{}finally{try{await(o?.close())}catch{}}}(e,n,s),i=x.get(o);i?.signature===s&&(r?x.delete(o):x.set(o,{...i,active:!1}))})}function M(e,t,n=!1){if(n){const t=e.lastIndexOf(10);return t<0?{carry:Buffer.alloc(0),lines:[],discarding:!0}:M(e.subarray(0,t+1),Buffer.alloc(0))}const r=t.length?Buffer.concat([e,t]):e,s=r.indexOf(10);if(s<0)return r.length>b?{carry:Buffer.alloc(0),lines:[],discarding:!0}:{carry:Buffer.from(r),lines:[],discarding:!1};const o=Buffer.from(r.subarray(0,s)),i=[];let a=s+1;for(;;){const e=r.indexOf(10,a);if(e<0)break;i.push(r.subarray(a,e)),a=e+1}return i.push(r.subarray(a)),{carry:o.length>b?Buffer.alloc(0):o,lines:i,discarding:o.length>b}}export function readDurableOldestSeq(e,n){try{const r=O(e,n);return r?r.oldestSeq??null:(t.existsSync(h(e,n))&&q(e,n),null)}catch{return null}}export function readDurablePage(e,n,r,s=200){try{const o=O(e,n);return t.existsSync(h(e,n))?(o||q(e,n),function(e,n,r,s,o){const i=(e=>Math.max(1,Math.min(200,Number(e)||200)))(s),a=h(e,n),c=t.statSync(a).size,l=o?.epochStart||0,u=null==r?null:o?.points.find(e=>e.seq>r);let f=u?.offset??c,d=Buffer.alloc(0),m=!1,y=0,p=0,g=[],v=null,x=!1,N=!1;const O=t.openSync(a,"r");try{for(;f>l&&y<S&&p<5e3&&g.length<=i;){const e=Math.max(l,f-Math.min(65536,S-y)),n=Buffer.alloc(f-e);t.readSync(O,n,0,n.length,e),y+=n.length;const s=M(n,d,m);d=s.carry,m=s.discarding;for(let e=s.lines.length-1;e>=0;e--){const t=s.lines[e];if(t.length){try{if(t.length>b){N=!0;continue}const e=JSON.parse(t.toString("utf8"));if(p++,"number"==typeof e?.seq){if(null!=v&&e.seq>=v){x=!0;break}v=e.seq}(null==r||null==e?.seq||e.seq<r)&&g.push(e)}catch{}if(g.length>i)break}}if(x||g.length>i)break;f=e}if(f===l&&m&&(N=!0),f===l&&d.length&&!x&&g.length<=i)try{if(d.length>b)N=!0;else{const e=JSON.parse(d.toString("utf8"));p++,"number"==typeof e?.seq&&null!=v&&e.seq>=v?x=!0:(null==r||null==e?.seq||e.seq<r)&&g.push(e)}}catch{}}finally{t.closeSync(O)}f>l&&(y>=S||p>=5e3)&&(N=!0);const w=N||g.length>i||!x&&f>l;return{events:g.slice(0,i).reverse(),hasMore:w,...N?{partial:!0,bytesRead:y,eventsRead:p}:{}}}(e,n,r,s,o)):{events:[],hasMore:!1}}catch{return{events:[],hasMore:!1}}}const k=(e,t)=>(Number(e.ts)||0)-(Number(t.ts)||0)||String(e.termName||"").localeCompare(String(t.termName||""))||(Number(e.seq)||0)-(Number(t.seq)||0),j=()=>new Promise(e=>setImmediate(e));export async function searchDurableSessionEventsAsync(e,n,s,o={}){const i=String(s||"").trim().toLocaleLowerCase();if(!i)return{results:[],truncated:!1,partial:!1,bytesRead:0,eventsRead:0};const a=Math.max(1,Math.min(200,Number(o.limit)||200)),c=Math.max(16384,Math.min(g,Number(o.maxBytes)||g)),l=Math.max(1,Math.min(5e4,Number(o.maxEvents)||5e4)),u=Math.max(1,Math.min(5e3,Number(o.deadlineMs)||100)),f=Date.now()+u,d={bytes:0,events:0,partial:!1,truncated:!1},m=[],y=new Set;let p=2;const S=(e,t,n)=>{const s=r(e);if(!s||!s.toLocaleLowerCase().includes(i))return;const o=e.cid||(Number.isSafeInteger(e.seq)?`seq:${e.epoch||0}:${e.seq}`:`row:${n}`),c=`${t.term}:${o}`;if(y.has(c))return;y.add(c);const l=s.toLocaleLowerCase().indexOf(i),u=s.slice(Math.max(0,l-240),Math.min(s.length,l+i.length+560)),f={key:c,term:t.term,termName:t.termName||"terminal",cid:e.cid||null,seq:Number.isSafeInteger(e.seq)?e.seq:null,epoch:Number.isSafeInteger(e.epoch)?e.epoch:0,ts:Number(e.ts)||0,kind:e.kind,by:e.by||null,text:u},h=Buffer.byteLength(JSON.stringify(f))+1;if(p+h>12e4)return d.truncated=!0,void(d.partial=!0);p+=h,m.push(f),m.sort(k),m.length>a&&(p-=Buffer.byteLength(JSON.stringify(m.pop()))+1,d.truncated=!0)};try{for(const r of n||[]){const n=String(r?.term||"");if(!n||d.partial)continue;const s={term:n,termName:r.termName||"terminal"},o=h(e,n);let i;try{i=await t.promises.stat(o)}catch{continue}const a=O(e,n);a||q(e,n);const u=a?.epochStart||0;let m,y=i.size,p=Buffer.alloc(0),g=!1,v=null,x=!1,N=0;try{for(m=await t.promises.open(o,"r");y>u&&!x;){if(d.bytes>=c||d.events>=l||Date.now()>=f){d.partial=!0;break}const e=Math.min(16384,y-u,c-d.bytes);if(e<=0){d.partial=!0;break}const t=y-e,n=Buffer.allocUnsafe(e),{bytesRead:r}=await m.read(n,0,e,t);d.bytes+=r;const o=M(n.subarray(0,r),p,g);p=o.carry,g=o.discarding;for(let e=o.lines.length-1;e>=0;e--){const t=o.lines[e];if(!t.length)continue;if(t.length>b){d.partial=!0;continue}let n;try{n=JSON.parse(t.toString("utf8"))}catch{continue}if(d.events++,N++,"number"==typeof n?.seq){if(null!=v&&n.seq>=v){x=!0;break}v=n.seq}if(S(n,s,N),d.events>=l){d.partial=!0;break}}y=t,await j()}if(g&&(d.partial=!0),!d.partial&&!x&&y===u&&p.length)if(p.length>b)d.partial=!0;else try{const e=JSON.parse(p.toString("utf8"));d.events++,N++,"number"==typeof e?.seq&&null!=v&&e.seq>=v?x=!0:S(e,s,N)}catch{}}finally{await(m?.close())}!x&&y>u&&(d.partial=!0)}}catch{d.partial=!0}return d.partial&&(d.truncated=!0),{results:m,truncated:d.truncated,partial:d.partial,bytesRead:d.bytes,eventsRead:d.events,deadlineMs:u}}export function searchDurableSessionEvents(){return{results:[],truncated:!0,partial:!0,bytesRead:0,eventsRead:0}}const $=new Map;function I(e,r,s){const a=n.join(i(e),`${r}.json`);try{c(e);const l=Array.isArray(s?.log)?o(s.log):s?.log;return f(a,JSON.stringify({...s,...l?{log:l}:{},id:r,savedAt:Date.now()})),function(e){try{const r=loadNames(e),s=e=>{try{return JSON.parse(t.readFileSync(e,"utf8")).id}catch{return null}};t.readdirSync(i(e)).filter(e=>e.endsWith(".json")).map(r=>{const s=n.join(i(e),r);let o=0;try{o=JSON.parse(t.readFileSync(s,"utf8")).savedAt||0}catch{}return{p:s,f:r,m:o}}).sort((e,t)=>t.m-e.m).filter(e=>{const t=s(e.p);return!t||!r[t]}).slice(LIVE_SESSION_MAX).forEach(t=>m(e,t.p))}catch(t){d("prune live session snapshots",i(e),t)}}(e),!0}catch(e){return d("write session snapshot",a,e),!1}}export function saveSession(e,t,n){clearTimeout($.get(t)),$.set(t,setTimeout(()=>I(e,t,n),1500))}export function flushSession(e,t,n){return clearTimeout($.get(t)),I(e,t,n)}export function deleteSession(e,r){clearTimeout($.get(r)),$.delete(r);const s=n.join(i(e),`${r}.json`);try{return!t.existsSync(s)||m(e,s)}catch(e){return d("archive closed session snapshot",s,e),!1}}const J=e=>n.join(i(e),".scheduled-outcomes"),B=e=>"string"==typeof e&&/^[A-Za-z0-9][A-Za-z0-9._:@-]{0,159}$/.test(e),F=e=>"string"==typeof e&&/^[A-Za-z0-9][A-Za-z0-9._:@-]{0,159}$/.test(e),z=e=>{try{const n=JSON.parse(t.readFileSync(J(e),"utf8"));return n&&"object"==typeof n&&!Array.isArray(n)?n:{}}catch{return{}}},R=e=>{if(!e||"object"!=typeof e||Array.isArray(e))return null;if(1===e.v&&"acknowledged"===e.state){const t=e.intent;return t&&"object"==typeof t&&!Array.isArray(t)&&F(t.runId)&&e.runId===t.runId?{intent:t,acknowledged:!0}:null}return F(e.runId)?{intent:e,acknowledged:!1}:null};export function savePendingScheduledOutcome(e,t,n){if(!B(t)||!n||"object"!=typeof n||Array.isArray(n)||!F(n.runId))return!1;try{c(e);const r=z(e);return r[t]=n,f(J(e),JSON.stringify(r)),!0}catch(t){return d("write scheduled outcome outbox",J(e),t),!1}}export function loadPendingScheduledOutcomes(e){return Object.entries(z(e)).map(([e,t])=>({terminalId:e,record:R(t)})).filter(({terminalId:e,record:t})=>B(e)&&t).map(({terminalId:e,record:t})=>({terminalId:e,intent:t.intent,...t.acknowledged?{acknowledged:!0}:{}})).slice(0,LIVE_SESSION_MAX)}export function loadPendingScheduledOutcome(e,t){return B(t)&&R(z(e)[t])?.intent||null}export function acknowledgePendingScheduledOutcome(e,t,n){if(!B(t)||!F(n))return!1;try{const r=z(e),s=R(r[t]);return!(!s||s.intent.runId!==n||(s.acknowledged||(r[t]={v:1,state:"acknowledged",runId:n,intent:s.intent},f(J(e),JSON.stringify(r))),0))}catch(t){return d("acknowledge scheduled outcome outbox",J(e),t),!1}}export function deletePendingScheduledOutcome(e,n){if(!B(n))return!1;try{const r=z(e);return!(n in r)||(delete r[n],Object.keys(r).length?f(J(e),JSON.stringify(r)):t.rmSync(J(e),{force:!0}),!0)}catch(t){return d("delete scheduled outcome outbox",J(e),t),!1}}export function commitRecordedScheduledOutcome(e,t,r){let s=!1;try{s="function"==typeof r&&!0===r()}catch(r){d("record scheduled outcome in session snapshot",n.join(i(e),`${t}.json`),r)}if(!s)return{ok:!1,snapshotRecorded:!1,outboxAcknowledged:!1,outboxDeleted:!1};const o=loadPendingScheduledOutcome(e,t);if(!acknowledgePendingScheduledOutcome(e,t,o?.runId))return{ok:!1,snapshotRecorded:!0,outboxAcknowledged:!1,outboxDeleted:!1};const a=deletePendingScheduledOutcome(e,t);return{ok:a,snapshotRecorded:!0,outboxAcknowledged:!0,outboxDeleted:a}}export function loadLatest(e){return y(e).sort((e,t)=>(t.savedAt||0)-(e.savedAt||0))[0]||null}export function canResume(e){return!!(e&&e.sessionId&&e.savedAt&&Date.now()-e.savedAt<432e5)}export function loadAll(e){return y(e).sort((e,t)=>(e.savedAt||0)-(t.savedAt||0))}export function loadArchived(e,r=40){try{return t.readdirSync(a(e)).map(r=>{const s=n.join(a(e),r);try{const e=t.statSync(s);if(!e.isFile())return null;const n=JSON.parse(t.readFileSync(s,"utf8"));return n&&Array.isArray(n.log)?{...n,_archivedAt:e.mtimeMs||0}:null}catch{return null}}).filter(Boolean).sort((e,t)=>(t._archivedAt||t.savedAt||0)-(e._archivedAt||e.savedAt||0)).slice(0,Math.max(1,Math.min(100,Number(r)||40)))}catch{return[]}}const D=e=>n.join(i(e),".pty-id");export function loadPtyId(e){try{return t.readFileSync(D(e),"utf8").trim()||null}catch{return null}}export function savePtyId(e,t){const n=D(e);try{return c(e),f(n,String(t)),!0}catch(e){return d("write PTY id",n,e),!1}}const E=e=>n.join(i(e),".names");export function loadNames(e){const n=E(e);try{return JSON.parse(t.readFileSync(n,"utf8"))||{}}catch(e){return"ENOENT"!==e?.code&&d("parse terminal names",n,e),{}}}export function saveNames(e,t){const n=E(e);try{return c(e),f(n,JSON.stringify(t||{})),!0}catch(e){return d("write terminal names",n,e),!1}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const makeRoutedRealtimeFrame=(e,t,a)=>({event:e,payload:t,channel:a});export const realtimeWireBytes=({event:e,payload:t})=>{try{return Buffer.byteLength(JSON.stringify({event:e,payload:t}))}catch{return 1/0}};export async function sendAbortableReplayFrame(e,{event:t,payload:a},{signal:o,fetchImpl:n=fetch}={}){const r=new URL(e.broadcastEndpointURL),c={apikey:e.socket.apiKey||"","Content-Type":"application/json"};e.socket.accessTokenValue&&(c.Authorization=`Bearer ${e.socket.accessTokenValue}`);const s=await n(r,{method:"POST",headers:c,body:JSON.stringify({messages:[{topic:e.subTopic,event:t,payload:a,private:e.private}]}),signal:o});try{await(s.body?.cancel())}catch{}return s.ok?"ok":"error"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function createTurnAdmissionQueue({maxPending:e=128}={}){const t=new WeakMap;return(n,r)=>{if(!n||"function"!=typeof r)return Promise.resolve();let o=t.get(n);if(o||(o={tail:Promise.resolve(),count:0},t.set(n,o)),o.count>=e)return Promise.reject(new Error("Too many pending turns; wait for this lane to finish preparing."));o.count++;const i=o.tail.then(r),u=()=>{o.count--,o.count||t.get(n)!==o||t.delete(n)};return o.tail=i.then(u,u),i}}
|