← Files DXIRARCHIVED FILE

scripts/journal.mjs

28.5 KB · Nov 9, 2026 · 01:16 UTC

↓ Download file

import{createHash as H,randomUUID as G}from"node:crypto";import{saveDecisionMaterials as V,verifyDecisionMaterials as K}from"./decision-materials.mjs";import*as g from"node:fs";import p from"node:path";const P=1e3,B=2048,q=8*1024*1024,b=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,Y=/^[A-Za-z0-9_-]{1,160}$/,U=/(?:https?:\/\/|\b[A-Za-z]:[\\/]|(?:^|\s)\/(?:Users|home|tmp|var|workspace)\/|-----BEGIN |\b(?:Bearer\s|sk-[A-Za-z0-9]|(?:password|api[_-]?key|access[_-]?token)\s*[:=]))/i,Q={PreToolUse:["tool_started","Codex started a tool."],PostToolUse:["tool_finished","Codex returned from a tool; task completion is not implied."],Stop:["turn_stopped","Codex ended a response; task completion is not implied."],Interrupt:["turn_interrupted","The user interrupted this Codex turn."]};function s(t,i){if(!t)throw new Error(i)}function T(t){return H("sha256").update(t).digest("hex")}function ee(t){const i=Buffer.from(T(t).slice(0,32),"hex");i[6]=i[6]&15|80,i[8]=i[8]&63|128;const e=i.toString("hex");return`${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20)}`}function L(t,i){s(typeof i=="string"&&Y.test(i),"A host-provided Codex session ID is required.");let e=g.realpathSync(t);process.platform==="win32"&&(e=e.toLowerCase());const n=T(e);return{codex_session_id:i,project_key:n,scope_key:T(`${i}\0${n}`)}}function w(t,i){return s(typeof t=="string"&&p.isAbsolute(t),"The hook-provided plugin data directory is required."),s(/^[0-9a-f]{64}$/.test(i.scope_key??""),"Invalid project scope."),p.join(t,"scopes",i.scope_key)}function k(t){try{return JSON.parse(g.readFileSync(t,"utf8"))}catch(i){if(i.code==="ENOENT")return null;throw i}}function v(t,i){const e=`${t}.${G()}.tmp`;try{const n=g.openSync(e,"wx",384);try{g.writeFileSync(n,`${JSON.stringify(i)}
`),g.fsyncSync(n)}finally{g.closeSync(n)}g.renameSync(e,t)}finally{try{g.unlinkSync(e)}catch(n){if(n.code!=="ENOENT")throw n}}}async function S(t,i){g.mkdirSync(t,{recursive:!0,mode:448});const e=p.join(t,".lock"),n=Date.now()+750;let r;for(;r===void 0;)try{r=g.openSync(e,"wx",384)}catch(a){const d=c=>process.platform==="win32"&&["EPERM","EACCES"].includes(c);if(a.code!=="EEXIST"&&!d(a.code))throw a;try{Date.now()-g.statSync(e).mtimeMs>3e4&&g.unlinkSync(e)}catch(c){if(c.code!=="ENOENT"&&!d(c.code))throw c}s(Date.now()<n,"Activity journal is busy; synchronization was not acknowledged."),await new Promise(c=>setTimeout(c,15))}try{const a=i();return a&&typeof a.then=="function"?await a:a}finally{g.closeSync(r),g.unlinkSync(e)}}function M(t,i){return t?.active===!0&&t.codex_session_id===i.codex_session_id&&t.project_key===i.project_key&&b.test(t.binding_id??"")&&b.test(t.room_id??"")}function A(t,i){const e=k(p.join(t,"binding.json"));return s(M(e,i),"This Codex conversation and project have no active local Action Room binding."),e}async function ne(t,i,e){const n=e?.binding;s(e?.ok===!0&&n?.status==="active","An active successful server binding is required."),s(n.codex_session_id===i.codex_session_id,"Server binding belongs to another Codex conversation.");const r=e.room_id??e.session?.action_room_id??e.session?.room_id;s(b.test(r??"")&&b.test(n.id??""),"The server must return a Room ID and binding ID.");const a=w(t,i);return S(a,()=>{const d=k(p.join(a,"binding.json"));s(!d?.active||d.binding_id===n.id&&d.room_id===r,"Disconnect the existing Room before binding a different Room.");const c={version:1,active:!0,binding_id:n.id,room_id:r,codex_session_id:i.codex_session_id,project_key:i.project_key,...d?.layout_required||Array.isArray(e.tasks)?{layout_required:!0}:{}};return v(p.join(a,"binding.json"),c),c})}function J(t,i,e=60){let n=String(t??"").normalize("NFC").replace(/[<>:"/\\|?*\u0000-\u001f\u007f]/g,"_").replace(/\s+/g," ").trim().replace(/^[. ]+|[. ]+$/g,"");return n||(n=i),/^(?:con|prn|aux|nul|com[1-9¹²³]|lpt[1-9¹²³])(?:\.|$)/i.test(n)&&(n=`_${n}`),n=Array.from(n).reduce((r,a)=>r.length+a.length<=e?r+a:r,""),n.replace(/[. ]+$/g,"")||i}function z(t){s(typeof t=="string"&&t.length>0&&t.length<=500&&!/[\u0000-\u001f:]/.test(t)&&!p.win32.isAbsolute(t)&&!p.posix.isAbsolute(t),"Use a project-relative task path.");const i=t.replaceAll("\\","/").split("/");return s(i.every(e=>e&&e!=="."&&e!==".."),"Task path must not escape the project."),i}function E(t,i){return s(L(t,i.codex_session_id).project_key===i.project_key,"Current project differs from the bound project."),g.realpathSync(t)}function x(t,i,e=!1){let n=t;for(const r of z(i)){if(n=p.join(n,r),e)try{g.mkdirSync(n)}catch(d){if(d.code!=="EEXIST")throw d}const a=g.lstatSync(n);s(a.isDirectory()&&!a.isSymbolicLink(),"Task directories must not be files, symlinks, or junctions.")}return n}function I(t,i){const e=x(t,".action-room/layouts"),n=p.join(e,`${i.room_id}.json`);s(!g.lstatSync(n).isSymbolicLink(),"The folder mapping must not be a symlink.");const r=k(n);s(r?.version===1&&r.room_id===i.room_id&&r.project_key===i.project_key&&Array.isArray(r.tasks)&&r.tasks.length>0&&r.tasks.length<=200,"The saved folder mapping does not match this Room and project."),s(z(r.project_dir).length===1&&r.project_dir!==".action-room","Invalid project folder mapping."),s(new Set(r.tasks.map(a=>a.task_id)).size===r.tasks.length&&new Set(r.tasks.map(a=>a.relative_dir.toLowerCase())).size===r.tasks.length,"Duplicate task folder mapping.");for(const a of r.tasks)s(b.test(a.task_id??"")&&Number.isInteger(a.sort_order)&&z(a.relative_dir).length===2&&a.relative_dir.startsWith(`${r.project_dir}/`),"Invalid task folder mapping.");return r}function F(t,i){return`.action-room/work/${t}/${i}`}async function ae(t,i,e,n=process.cwd()){const r=w(t,i);return S(r,()=>{const a=A(r,i),d=e?.room_id??e?.session?.action_room_id??e?.session?.room_id;s(e?.ok===!0&&e.binding?.status==="active"&&e.binding.id===a.binding_id&&e.binding.codex_session_id===a.codex_session_id&&d===a.room_id,"Read the current active server binding before preparing folders."),s(Array.isArray(e.tasks)&&e.tasks.length>0&&e.tasks.length<=200,"The immutable report tasks are required to prepare folders.");const c=[...e.tasks].sort((u,y)=>u.sort_order-y.sort_order);s(new Set(c.map(u=>u.id)).size===c.length&&c.every((u,y)=>b.test(u.id??"")&&u.sort_order===y&&typeof u.title=="string"&&u.title.trim().length>0),"Invalid report task identities or order.");const _=E(n,i),l=x(_,".action-room/layouts",!0);return S(l,()=>{const u=p.join(l,`${a.room_id}.json`);let y;if(g.existsSync(u))y=I(_,a),s(y.tasks.length===c.length&&y.tasks.every((m,o)=>m.task_id===c[o].id&&m.sort_order===c[o].sort_order),"Report tasks changed; preserve the existing folder mapping.");else{const m=new Set(g.readdirSync(_).map($=>$.normalize("NFC").toLowerCase()));for(const $ of g.readdirSync(l).filter(C=>b.test(C.slice(0,-5))&&C.endsWith(".json"))){const C=I(_,{...a,room_id:$.slice(0,-5)});m.add(C.project_dir.normalize("NFC").toLowerCase())}const o=J(e.workspace?.title,"Action Room");let f=o,h=0;for(;m.has(f.toLowerCase());)h+=1,f=`${o} (${a.room_id.slice(0,8)}${h>1?`-${h}`:""})`;const j=Math.max(2,String(c.length).length),N=c.map($=>({task_id:$.id,sort_order:$.sort_order,relative_dir:`${f}/${String($.sort_order+1).padStart(j,"0")}_${J($.title,"Task")}`}));s(N.every($=>p.join(_,$.relative_dir).length<=220),"The selected project path is too long; select a shorter base folder before creating results.");const D=k(p.join(r,`checkpoint-${a.binding_id}.json`));y={version:1,room_id:a.room_id,project_key:a.project_key,project_dir:f,tasks:N,legacy_artifacts:D?.artifacts?.length?[{task_id:D.task_id,artifacts:D.artifacts}]:[]},v(u,y)}x(_,y.project_dir,!0);for(const m of y.tasks)x(_,m.relative_dir,!0),x(_,`${m.relative_dir}/근거`,!0),x(_,`${m.relative_dir}/설명자료`,!0);return v(p.join(r,"binding.json"),{...a,layout_required:!0}),{...y,legacy_artifacts:void 0}})})}async function ce(t,i,e,n=process.cwd()){s(b.test(e??""),"A report task UUID is required.");const r=w(t,i);return S(r,()=>{const a=A(r,i),d=E(n,i),c=I(d,a),_=c.tasks.find(u=>u.task_id===e);s(_,"Task does not belong to this Room folder mapping."),x(d,_.relative_dir);const l=F(a.room_id,e);return x(d,l,!0),x(d,`${_.relative_dir}/근거`,!0),x(d,`${_.relative_dir}/설명자료`,!0),{task_id:e,relative_dir:_.relative_dir,support_dir:l,evidence_dir:`${_.relative_dir}/근거`,explanation_dir:`${_.relative_dir}/설명자료`,legacy_artifacts:c.legacy_artifacts?.find(u=>u.task_id===e)?.artifacts??[]}})}async function de(t,i,e,n,r=process.cwd()){s(b.test(e??""),"A report task UUID is required.");const a=w(t,i);return S(a,()=>{const d=A(a,i),c=E(r,i),_=I(c,d),l=_.tasks.find(j=>j.task_id===e);if(s(l,"Preserve the existing task folder mapping."),n?.verify_receipts)return K({base:c,task:l.relative_dir,receipts:n.verify_receipts});const u=F(d.room_id,e),y=x(c,u,!0),m=p.join(y,"decision-stages.json");g.existsSync(m)&&s(!g.lstatSync(m).isSymbolicLink(),"Stage mapping cannot be a link.");const o=k(m)??{stages:[]},f=n?.stage;s(f&&/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,159}$/.test(f.id)&&typeof f.label=="string","Use a stable stage ID and label.");let h=o.stages.find(j=>j.id===f.id);if(!h){s(o.stages.length<30,"Stage capacity reached.");let j=`${String(o.stages.length+1).padStart(2,"0")}_${J(f.label,"판단 단계",45)}`,N=`${l.relative_dir}/${j}`,D=1;for(;g.existsSync(p.join(c,N))||o.stages.some($=>$.relative_dir.toLowerCase()===N.toLowerCase());)N=`${l.relative_dir}/${j} (${++D})`;h={id:f.id,label:f.label,relative_dir:N},o.stages.push(h),v(m,o)}return s(h.relative_dir.startsWith(`${l.relative_dir}/`)&&z(h.relative_dir).length===3,"Stage must stay inside its original task."),x(c,`${h.relative_dir}/근거`,!0),x(c,`${h.relative_dir}/설명자료`,!0),x(c,`${l.relative_dir}/근거`,!0),x(c,`${l.relative_dir}/설명자료`,!0),V({base:c,task:l.relative_dir,support:u,stage:h,input:n})})}function W(t,i,e,n,r,a){const d=z(n).join("/");let c;try{c=I(t,i)}catch(o){if(o.code!=="ENOENT")throw o}if((c?.legacy_artifacts?.find(o=>o.task_id===e)?.artifacts??(a?.task_id===e?a.artifacts:[])??[]).find(o=>o.relative_path.replaceAll("\\","/")===d&&o.file_name===r.file_name&&o.size_bytes===r.size_bytes&&o.sha256===r.sha256&&(r.artifact_id===void 0||o.artifact_id===r.artifact_id)&&(r.content_type===void 0||o.content_type===r.content_type))){const o=R(d,t);s(o.file_name===r.file_name&&o.size_bytes===r.size_bytes&&o.sha256===r.sha256,"Legacy result bytes do not match the saved manifest.");return}s(c,"Prepare the Room folders before saving new task results.");const u=c.tasks.find(o=>o.task_id===e);s(u&&d.startsWith(`${u.relative_dir}/`),"Save the result inside the assigned task folder.");const y=p.posix.dirname(d);x(t,y),s(!g.lstatSync(p.join(t,d)).isSymbolicLink(),"Result files must not be symlinks.");const m=R(d,t);s(m.file_name===r.file_name&&m.size_bytes===r.size_bytes&&m.sha256===r.sha256,"Task result bytes do not match the saved manifest.")}async function _e(t,i,e,n,r=process.cwd()){s(b.test(e??""),"A report task UUID is required.");const a=w(t,i);return S(a,()=>{const d=A(a,i),c=E(r,i),_=z(n.replaceAll("\\","/").replace(/^\.\//,"")).join("/"),l=R(_,c),u=k(p.join(a,`checkpoint-${d.binding_id}.json`));return W(c,d,e,_,l,u),l})}async function le(t,i){const e=w(t,i);return S(e,()=>{const n=A(e,i);return v(p.join(e,"binding.json"),{...n,active:!1}),{disconnected:!0,pending_events_preserved:!0,execution_stopped:!1}})}function te(t){return["Bash","exec_command","shell","shell_command","write_stdin"].includes(t)?"shell":["apply_patch","Edit","Write"].includes(t)?"file_edit":typeof t=="string"&&t.startsWith("mcp__")?"mcp":"local_tool"}function X(t){const i=t?.tool_input?.command??t?.tool_input?.cmd;return typeof i=="string"&&/action-room\.mjs["']?\s+(?:identity|bind|layout|task-dir|pending|ack|event|checkpoint|goal-checkpoint|unbind|artifact|upload)\b/.test(i)?i:null}function ie(t,i){const e=Q[t?.hook_event_name];if(!e||X(t)||typeof t.tool_name=="string"&&/(?:^|__)action_room_(?:read|bind|events|submit|prepare_artifact|complete_artifact|disconnect|goal|goal_measure)$/.test(t.tool_name))return null;const n=typeof t.turn_id=="string"?t.turn_id.slice(0,160):"",r=typeof t.tool_use_id=="string"?t.tool_use_id.slice(0,160):"";if(!n||t.hook_event_name.endsWith("ToolUse")&&!r)return null;const[a,d]=e,c={};if(t.hook_event_name.endsWith("ToolUse")&&(c.tool=te(t.tool_name)),t.hook_event_name==="PostToolUse"){const _=t.tool_response;typeof _?.isError=="boolean"?c.status=_.isError?"failed":"returned":c.status="returned",Number.isSafeInteger(_?.exit_code)&&(c.exit_code=_.exit_code)}return{event_id:ee(`${i.binding_id}\0${a}\0${n}\0${r}`),type:a,summary:d,occurred_at:new Date().toISOString(),metadata:c}}function O(t,i){return p.join(t,"outbox",i.binding_id)}async function ue(t,i,e=i?.cwd){const n=L(e,i?.session_id),r=w(t,n),a=k(p.join(r,"binding.json"));if(!M(a,n))return{recorded:!1};const d=ie(i,a);return d?S(r,()=>{const c=A(r,n);s(c.binding_id===a.binding_id,"The Room binding changed during activity collection.");const _=O(r,c);g.mkdirSync(_,{recursive:!0,mode:448});const l=p.join(_,`${d.event_id}.json`);return g.existsSync(l)?{recorded:!0,duplicate:!0}:g.readdirSync(_).filter(y=>b.test(y.slice(0,-5))&&y.endsWith(".json")).length>=P?(v(p.join(_,"overflow.json"),{incomplete:!0,reason:"journal_capacity",last_observed_at:d.occurred_at}),{recorded:!1,incomplete:!0}):(s(Buffer.byteLength(JSON.stringify(d))<=B,"Activity exceeds the local journal limit."),v(l,d),{recorded:!0,event_id:d.event_id})}):{recorded:!1}}async function fe(t,i,e){s(b.test(e?.event_id??"")&&b.test(e?.task_id??""),"Progress event and task UUIDs are required."),s(["progress","needs_input"].includes(e.type),"Only progress and needs_input can be reported manually."),s(typeof e.summary=="string"&&e.summary.length>0&&e.summary.length<=1e3,"A concise progress summary is required."),s(!U.test(e.summary),"Remove credentials, URLs, and absolute paths from the progress summary.");const n=w(t,i);return S(n,()=>{const r=A(n,i),a=O(n,r);g.mkdirSync(a,{recursive:!0,mode:448});const d=p.join(a,`${e.event_id}.json`);if(g.existsSync(d))return{recorded:!0,duplicate:!0};s(g.readdirSync(a).filter(_=>b.test(_.slice(0,-5))).length<P,"Activity journal is full. Synchronize the existing batch first.");const c={event_id:e.event_id,task_id:e.task_id,type:e.type,summary:e.summary,occurred_at:new Date().toISOString(),metadata:{status:"reported"}};return s(Buffer.byteLength(JSON.stringify(c))<=B,"Progress summary exceeds the journal byte limit."),v(d,c),{recorded:!0,event_id:c.event_id}})}function pe(t,i){const e=X(t);if(!e||!/action-room\.mjs["']?\s+identity\b/.test(e))return null;const n=L(t.cwd,t.session_id);return s(typeof i=="string"&&p.isAbsolute(i),"Plugin data directory unavailable."),{hookSpecificOutput:{hookEventName:t.hook_event_name,additionalContext:`DXIR host identity for the current helper invocation: ${JSON.stringify({...n,data_dir:i})}. Use this exact session and project only; do not infer identity from another conversation.`}}}async function ye(t,i,e=50){s(Number.isInteger(e)&&e>=1&&e<=50,"Batch size must be between 1 and 50.");const n=w(t,i);return S(n,()=>{const r=A(n,i),a=O(n,r),c=(g.existsSync(a)?g.readdirSync(a).filter(_=>_.endsWith(".json")&&b.test(_.slice(0,-5))):[]).map(_=>k(p.join(a,_))).sort((_,l)=>_.occurred_at.localeCompare(l.occurred_at)||_.event_id.localeCompare(l.event_id));return{room_id:r.room_id,binding_id:r.binding_id,codex_session_id:r.codex_session_id,events:c.slice(0,e),remaining:Math.max(0,c.length-e),incomplete:!!k(p.join(a,"overflow.json"))}})}async function me(t,i,e){const n=w(t,i);return S(n,()=>{const r=A(n,i);s(e?.ok===!0&&(e.binding_id??e.binding?.id)===r.binding_id,"An acknowledged server response for this binding is required."),s(Array.isArray(e.accepted_event_ids)&&e.accepted_event_ids.length<=50&&e.accepted_event_ids.every(d=>b.test(d)),"Accepted event IDs are required.");let a=0;for(const d of new Set(e.accepted_event_ids))try{g.unlinkSync(p.join(O(n,r),`${d}.json`)),a+=1}catch(c){if(c.code!=="ENOENT")throw c}return{acknowledged:a}})}async function ge(t,i,e,n=process.cwd(),r){const a=w(t,i);return S(a,()=>{const d=A(a,i);s(r===void 0||b.test(r),"Invalid goal work ID.");const c=p.join(a,r?`goal-work-${d.binding_id}-${r}.json`:`checkpoint-${d.binding_id}.json`);if(e===void 0)return k(c);s(b.test(e?.task_id??""),"Checkpoint task ID is required."),s(e.submission_id===void 0||b.test(e.submission_id),"Invalid submission ID."),s(Array.isArray(e.artifact_ids)&&e.artifact_ids.length<=20&&e.artifact_ids.every(o=>b.test(o)),"Invalid artifact IDs."),s(new Set(e.artifact_ids).size===e.artifact_ids.length,"Duplicate artifact IDs are not allowed."),s(["working","external_effect_done","artifact_pending","submission_pending","submitted"].includes(e.stage),"Invalid checkpoint stage.");const _=e.artifacts??[];s(Array.isArray(_)&&_.length===e.artifact_ids.length,"Save an exact local file manifest for every artifact ID.");const l=_.map(o=>{s(o&&b.test(o.artifact_id??"")&&e.artifact_ids.includes(o.artifact_id),"Artifact manifest ID does not match the checkpoint.");const f=o.relative_path;s(typeof f=="string"&&f.length>0&&f.length<=500&&!/[\u0000-\u001f:]/.test(f)&&!p.win32.isAbsolute(f)&&!p.posix.isAbsolute(f),"Artifact checkpoint path must be relative to the selected project.");const h=f.replaceAll("\\","/").replace(/^\.\//,"");return s(h.split("/").every(j=>j&&j!=="."&&j!==".."),"Artifact checkpoint path must not escape the project."),s(typeof o.file_name=="string"&&o.file_name.length>0&&o.file_name.length<=255&&!/[\u0000-\u001f\\/:]/.test(o.file_name)&&p.posix.basename(h)===o.file_name,"Artifact filename does not match the relative file path."),s(typeof o.content_type=="string"&&/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i.test(o.content_type),"Artifact content type is required."),s(Number.isSafeInteger(o.size_bytes)&&o.size_bytes>=0&&o.size_bytes<=q,"Artifact size exceeds the local upload limit."),s(typeof o.sha256=="string"&&/^[0-9a-f]{64}$/.test(o.sha256),"Artifact SHA-256 is required."),{artifact_id:o.artifact_id,relative_path:h,file_name:o.file_name,content_type:o.content_type,size_bytes:o.size_bytes,sha256:o.sha256}});s(new Set(l.map(o=>o.artifact_id)).size===l.length,"Duplicate artifact manifest IDs are not allowed."),e.submission_summary!==void 0&&s(typeof e.submission_summary=="string"&&e.submission_summary.trim().length>0&&e.submission_summary.length<=8e3&&!U.test(e.submission_summary)&&!/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/.test(e.submission_summary),"Use a bounded redacted submission summary without credentials, URLs, absolute paths, or raw terminal output.");const u=e.completion_policy==="outcome_actions_v1";if(u){s(Array.isArray(e.outputs)&&e.outputs.length<=100,"Save the exact natural result outputs.");for(const o of e.outputs)if(s(o&&typeof o=="object"&&!Array.isArray(o),"Invalid result output."),o.artifact_id)s(Object.keys(o).length===1&&e.artifact_ids.includes(o.artifact_id),"Result artifact must match the saved file manifest.");else if(s(Object.keys(o).every(f=>["kind","label","status","reference","url"].includes(f))&&o.kind==="action"&&o.status==="succeeded"&&typeof o.label=="string"&&o.label.trim().length>0&&o.label.length<=2e3&&!U.test(o.label),"Save only an actual successful action result."),s(o.reference===void 0||typeof o.reference=="string"&&o.reference.length<=500&&!U.test(o.reference),"Invalid result reference."),o.url!==void 0){const f=new URL(o.url);s(f.protocol==="https:"&&!f.username&&!f.password&&!f.search&&!f.hash&&!/[\\\s]/.test(o.url)&&o.url.length<=2e3,"Use a result URL without credentials or query parameters.")}s(e.artifact_ids.every(o=>e.outputs.some(f=>f.artifact_id===o)),"Every saved artifact must be included in the result outputs.")}["submission_pending","submitted"].includes(e.stage)&&s(e.submission_id&&e.submission_summary!==void 0&&(u?e.criteria_met===!0:l.length>0),"Save the submission ID, exact summary, and complete artifact manifest before submitting.");const y={task_id:e.task_id,artifact_ids:e.artifact_ids,stage:e.stage,...u?{completion_policy:e.completion_policy,criteria_met:e.criteria_met===!0,outputs:e.outputs}:{},...r?{goal_work_id:r}:{},...e.submission_id?{submission_id:e.submission_id}:{},...e.artifacts!==void 0?{artifacts:l}:{},...e.submission_summary!==void 0?{submission_summary:e.submission_summary}:{}},m=k(c);if(d.layout_required&&l.length>0){const o=E(n,i);for(const f of l)W(o,d,y.task_id,f.relative_path,f,m)}if(m?.task_id===y.task_id){for(const o of l){const f=m.artifacts?.find(h=>h.artifact_id===o.artifact_id);s(!f||JSON.stringify(f)===JSON.stringify(o),"Do not change an existing artifact ID reservation; preserve the exact file manifest for recovery.")}m.submission_id&&m.submission_id===y.submission_id&&["submission_pending","submitted"].includes(m.stage)&&s(m.submission_summary===y.submission_summary&&JSON.stringify(m.artifact_ids)===JSON.stringify(y.artifact_ids)&&m.completion_policy===y.completion_policy&&m.criteria_met===y.criteria_met&&JSON.stringify(m.outputs)===JSON.stringify(y.outputs),"Do not change a pending submission payload; retry the saved summary and artifact IDs.")}return s(Buffer.byteLength(JSON.stringify(y))<=64*1024,"Checkpoint exceeds the local size limit."),v(c,y),y})}async function he(t,i,e){const n=w(t,i);return S(n,()=>{const r=A(n,i),a=p.join(n,`goal-pending-${r.binding_id}.json`),d=k(a);if(e===void 0)return d;if(e.rejected){s(d&&e.rejected.mutation_id===d.payload.mutation_id&&["strategy_codex_goal_revision_conflict","strategy_codex_goal_work_pending","strategy_codex_goal_not_due","strategy_codex_goal_limit_reached","strategy_codex_goal_not_active","strategy_codex_goal_terminal","strategy_codex_goal_work_invalid","strategy_codex_goal_payload_invalid","strategy_codex_goal_configuration_invalid","strategy_codex_goal_evidence_required","strategy_codex_goal_measurement_required","strategy_codex_execution_assessment_required","strategy_codex_execution_work_remaining","strategy_codex_output_invalid"].includes(e.rejected.code),"Only a definitive server rejection can release this pending mutation.");const o={...d,rejected:e.rejected.code};return v(a,o),o}if(e.receipt){const o=e.receipt;s(d&&o.ok===!0&&o.goal_mutation_id===d.payload.mutation_id&&o.binding?.id===r.binding_id&&o.binding?.codex_session_id===i.codex_session_id&&(o.session?.room_id??o.room_id)===r.room_id,"Goal receipt does not acknowledge the pending mutation.");const f={...d,acknowledged:!0};return v(a,f),f}s(["plan","submit","wait","wake","block","schedule","propose_measurement","measure","report_outcome"].includes(e.action),"Invalid goal action.");const c=e.payload;s(c&&b.test(c.mutation_id??"")&&c.room_id===r.room_id&&c.binding_id===r.binding_id&&c.codex_session_id===i.codex_session_id&&Number.isSafeInteger(c.expected_revision)&&c.expected_revision>=0,"Goal payload does not match this binding.");const _=new Set(["room_id","binding_id","codex_session_id","mutation_id","expected_revision","work_id","source_task_id","kind","instruction","completion_criteria","rationale","summary","artifact_ids","next_run_at","scheduler_id","measurement_rules","outputs","criteria_met","outcome_status"]);s(Object.keys(c).every(o=>_.has(o)),"Unknown goal payload field.");const l=JSON.stringify(c);s(Buffer.byteLength(l)<=60*1024&&!/(?:bearer\s+[\w.-]+|sk-[\w-]{12,}|(?:api[_-]?key|access[_-]?token|password|secret)\s*[=:]\s*\S+|(?:^|[^A-Za-z])\b[A-Za-z]:[\\/])/i.test(l),"Keep credentials, absolute paths and raw logs out of goal checkpoints.");const u={action:e.action,payload:c,acknowledged:!1},y=p.join(n,`goal-mutation-${r.binding_id}-${c.mutation_id}.json`),m=k(y);return s(!m||JSON.stringify(m)===JSON.stringify(u),"Do not change a saved goal mutation; replay its exact payload."),s(!d||d.acknowledged||d.rejected||d.payload.mutation_id===c.mutation_id,"Reconcile the pending goal mutation before saving another."),m||v(y,u),v(a,u),u})}async function be(t,i,e,n){return Z("research",t,i,e,n)}async function ve(t,i,e,n){return Z("execution",t,i,e,n)}async function Z(t,i,e,n,r){s(b.test(n??""),"A research task ID is required.");const a=w(i,e);return S(a,()=>{const d=A(a,e),c=p.join(a,`${t}-pending-${d.binding_id}-${n}.json`),_=k(c);if(r===void 0)return _;if(r.receipt){const h=r.receipt;s(_&&h.ok===!0&&h[`${t}_mutation_id`]===_.payload.mutation_id&&!h.review_pending&&h.task_id===n&&h.room_id===d.room_id&&h.binding?.id===d.binding_id&&h.binding?.codex_session_id===e.codex_session_id,"Research receipt does not acknowledge the pending mutation.");const j={..._,acknowledged:!0,revision:h.revision};return v(c,j),j}if(r.rejected){s(_&&r.rejected.mutation_id===_.payload.mutation_id&&["strategy_codex_research_revision_conflict","strategy_codex_research_payload_invalid","strategy_codex_research_fit_status_invalid","strategy_codex_research_fit_status_required","strategy_codex_research_selection_evidence_incomplete","strategy_codex_research_review_payload_invalid","strategy_codex_research_criteria_review_required","strategy_codex_research_material_unverified","strategy_codex_research_review_budget_exhausted","strategy_codex_research_duplicate","strategy_codex_research_duplicate_round","strategy_codex_research_capacity_reached","strategy_codex_research_task_terminal","strategy_codex_execution_revision_conflict","strategy_codex_execution_payload_invalid","strategy_codex_execution_scope_reduced","strategy_codex_execution_incomplete","strategy_codex_execution_task_terminal","strategy_codex_goal_not_active","strategy_codex_binding_stale"].includes(r.rejected.code),"Only a definitive rejection releases the research mutation.");const h={..._,rejected:r.rejected.code};return v(c,h),h}const l=r.payload;s(l&&(l.action==="save"||t==="research"&&l.action==="review")&&l.task_id===n&&l.room_id===d.room_id&&l.binding_id===d.binding_id&&l.codex_session_id===e.codex_session_id&&b.test(l.mutation_id??"")&&Number.isSafeInteger(l.expected_revision)&&l.expected_revision>=0&&typeof l.patch_json=="string","Research payload does not match the binding."),s(Object.keys(l).every(h=>["action","room_id","task_id","binding_id","codex_session_id","mutation_id","expected_revision","patch_json"].includes(h)),"Unknown research payload field.");const u=JSON.stringify(l);s(Buffer.byteLength(u)<=250*1024&&!/(?:bearer\s+[\w.-]+|sk-[\w-]{12,}|(?:api[_-]?key|access[_-]?token|password|secret)\s*[=:]\s*\S+|\b[A-Za-z]:[\\/])/i.test(u),"Keep credentials and absolute paths out of research checkpoints.");const y=JSON.parse(l.patch_json);s(y&&typeof y=="object"&&!Array.isArray(y),"Invalid research patch.");const m={payload:l,acknowledged:!1},o=p.join(a,`${t}-mutation-${d.binding_id}-${l.mutation_id}.json`),f=k(o);return s(!f||JSON.stringify(f)===JSON.stringify(m),"Replay the exact payload of a saved research mutation."),s(!_||_.acknowledged||_.rejected||_.payload.mutation_id===l.mutation_id,"Reconcile the pending research mutation before saving another."),f||v(o,m),v(c,m),m})}function R(t,i=process.cwd()){const e=g.realpathSync(i),n=g.realpathSync(p.resolve(e,t)),r=p.relative(e,n);s(r!==""&&!r.startsWith(`..${p.sep}`)&&r!==".."&&!p.isAbsolute(r),"Choose an artifact inside the current project.");const a=g.statSync(n);s(a.isFile()&&a.size<=q,"Artifact must be a regular file of at most 8 MiB.");const d=g.readFileSync(n);return s(d.length<=q,"Artifact exceeds 8 MiB."),{file_name:p.basename(n),size_bytes:d.length,sha256:T(d)}}async function ke({file:t,cwd:i,sha256:e,contentType:n,uploadUrl:r,fetchImpl:a=fetch}){const d=R(t,i);s(/^[0-9a-f]{64}$/.test(e??"")&&e===d.sha256,"Artifact changed after upload preparation. Prepare the current bytes again."),s(typeof n=="string"&&/^[a-z0-9.+-]+\/[a-z0-9.+-]+$/i.test(n),"A valid content type is required.");const c=k(new URL("../.mcp.json",import.meta.url));let _;try{_=new URL(c?.mcpServers?.["dxir-action-room"]?.url)}catch{}s(_?.protocol==="https:"&&!_.port&&!_.username&&!_.password&&_.pathname==="/functions/v1/action-room-codex"&&!_.search&&!_.hash,"A valid DXIR MCP configuration is required for upload.");const l=new Set(["https://weufexgfzkdzqkfjxdpj.supabase.co",_.origin]),u=new URL(r);s(u.protocol==="https:"&&l.has(u.origin)&&!u.port&&!u.username&&!u.password&&u.pathname.startsWith("/storage/v1/object/upload/sign/")&&u.searchParams.has("token"),"Upload URL must be a scoped DXIR Supabase signed upload URL.");const y=g.readFileSync(p.resolve(i,t));s(T(y)===e&&y.length===d.size_bytes,"Artifact changed before upload.");const m=await a(u.href,{method:"PUT",redirect:"error",signal:AbortSignal.timeout(12e4),headers:{"Content-Type":n,"x-upsert":"false"},body:y});return s(m.ok,`Artifact upload was not accepted (HTTP ${m.status}). Check server state before retrying.`),{uploaded:!0,...d,server_verification_pending:!0}}export{q as MAX_ARTIFACT_BYTES,B as MAX_EVENT_BYTES,P as MAX_PENDING_EVENTS,me as acknowledge,R as artifact,ne as bind,ge as checkpoint,de as decisionMaterials,ve as executionCheckpoint,he as goalCheckpoint,pe as hookIdentity,L as identity,ye as pending,ae as prepareLayout,ue as recordHook,fe as recordProgress,be as researchCheckpoint,ie as sanitizeHook,_e as taskArtifact,ce as taskDirectory,le as unbind,ke as uploadArtifact};

SHA-256: 5ae131aedca663128b3da214e0583cf7633032629aa0a6d04a420659533b86b5