@yuandc/aica 0.1.64 → 0.1.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import{execFileSync as p}from"node:child_process";import h from"node:fs";import c from"node:path";import d from"node:crypto";const g=50*1024*1024,m=["path","file","filePath","file_path","filename","targetPath","target_path","move_path"];function T(t){if(!x(t))return null;const e=w(t);if(!e)return null;const r=new Map;for(const n of e)r.set(n.path,{status:n.status,objectHash:n.status.includes("D")?null:A(t,n.path)});return r}function R(t){const e=new Map;for(const r of t.evidencePaths)P(e,t.cwd,r,"unknown","acp_tool");return[...e.values()].filter(r=>r.relativePath&&!r.relativePath.startsWith(".git/")).sort((r,n)=>r.relativePath.localeCompare(n.relativePath))}function G(t){const e=new Set;return u(t,(r,n)=>{if(typeof r=="string"&&(S(n)&&l(e,r),n==="command"))for(const s of F(r))l(e,s)}),v(t,e),j(t,e),[...e]}function P(t,e,r,n,s){const i=b(e,r);if(!i)return;const a=t.get(i.relativePath),o=C(i.absolutePath),f={path:i.absolutePath,absolutePath:i.absolutePath,relativePath:i.relativePath,changeType:y(a?.changeType,n),source:a?.source==="acp_tool"||a?.source==="acp_diff"?a.source:s,exists:!!o?.isFile(),downloadable:!!o?.isFile(),...o?.isFile()?{size:o.size,mime:z(i.absolutePath),sha256:o.size<=g?_(i.absolutePath):void 0}:{}};t.set(i.relativePath,f)}function y(t,e){return!t||t==="unknown"?e:e==="unknown"||t===e?t:t==="created"&&e==="modified"?"created":e}function b(t,e){if(!e||e.includes("\0"))return null;const r=c.resolve(t),n=c.isAbsolute(e)?c.resolve(e):c.resolve(r,e),s=c.relative(r,n).replace(/\\/g,"/");return!s||s.startsWith("..")||c.isAbsolute(s)?null:{absolutePath:n,relativePath:s}}function x(t){try{return p("git",["-C",t,"rev-parse","--is-inside-work-tree"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}),!0}catch{return!1}}function w(t){try{const r=p("git",["-C",t,"status","--porcelain=v1","-z","--untracked-files=all"],{encoding:"buffer",maxBuffer:20971520}).toString("utf8").split("\0").filter(Boolean),n=[];for(let s=0;s<r.length;s+=1){const i=r[s],a=i.slice(0,2),o=i.slice(3);if(o)if(a.startsWith("R")||a.startsWith("C")){const f=r[s+1];f&&(n.push({status:a,path:f}),s+=1)}else n.push({status:a,path:o})}return n}catch{return null}}function A(t,e){try{const r=p("git",["-C",t,"hash-object","--",e],{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:1048576}).trim();return/^[0-9a-f]{40,64}$/i.test(r)?r:null}catch{return null}}function u(t,e,r,n=0){if(!(n>8)&&(e(t,r),!(!t||typeof t!="object"))){if(Array.isArray(t)){for(const s of t)u(s,e,void 0,n+1);return}for(const[s,i]of Object.entries(t))u(i,e,s,n+1)}}function v(t,e){u(t,r=>{if(!r||typeof r!="object"||Array.isArray(r))return;const n=r;n.type==="diff"&&l(e,n.path)})}function j(t,e){u(t,(r,n)=>{if(!(n!=="changes"||!r||typeof r!="object"||Array.isArray(r)))for(const s of Object.keys(r))l(e,s)})}function S(t){return t?m.includes(t):!1}function l(t,e){if(typeof e!="string")return;const r=k(e);!r||r.length>500||r.includes(`
2
- `)||r==="."||r===".."||t.add(r)}function k(t){let e=t.trim();e=e.replace(/\s+\+\d+\s*\/\s*-\d+(?:\s+.*)?$/,""),e=e.replace(/\s+\(\s*\+\d+\s*[,/ ]\s*-\d+\s*\)(?:\s+.*)?$/,""),e=e.replace(/\s+(?:source|event|workspace|session|last)=\S+.*$/,"");const r=e.split(/\s+/)[0]||"";return r&&/\.[A-Za-z][A-Za-z0-9+-]{0,12}$/.test(r)&&(e=r),e.replace(/^["']|["']$/g,"").trim()}function F(t){const e=[],r=/(?:^|\s)(?:\d?>|>>)\s*(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;let n;for(;n=r.exec(t);){const i=n[1]||n[2]||n[3];i&&e.push(i)}const s=/(?:^|\s)touch\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;for(;n=s.exec(t);){const i=n[1]||n[2]||n[3];i&&!i.startsWith("-")&&e.push(i)}return e}function C(t){try{return h.statSync(t)}catch{return null}}function _(t){try{return d.createHash("sha256").update(h.readFileSync(t)).digest("hex")}catch{return}}function z(t){switch(c.extname(t).toLowerCase()){case".pdf":return"application/pdf";case".md":case".markdown":return"text/markdown; charset=utf-8";case".txt":case".log":return"text/plain; charset=utf-8";case".json":return"application/json; charset=utf-8";case".csv":return"text/csv; charset=utf-8";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".zip":return"application/zip";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";default:return"application/octet-stream"}}export{T as captureGitSnapshot,R as collectAffectedFiles,G as extractAffectedFilePathsFromAcpRaw};
1
+ import{execFileSync as l}from"node:child_process";import d from"node:fs";import c from"node:path";import h from"node:crypto";const m=50*1024*1024,g=["path","file","filePath","file_path","filename","targetPath","target_path","move_path"];function T(t){if(!b(t))return null;const e=v(t);if(!e)return null;const n=new Map;for(const r of e)n.set(r.path,{status:r.status,objectHash:r.status.includes("D")?null:w(t,r.path)});return n}function R(t){const e=new Map;for(const n of t.evidencePaths)x(e,t.cwd,n,"unknown","acp_tool");return[...e.values()].filter(n=>n.relativePath&&!n.relativePath.startsWith(".git/")).sort((n,r)=>n.relativePath.localeCompare(r.relativePath))}function E(t){const e=new Set;return u(t,(n,r)=>{if(typeof n=="string"&&(S(r)&&p(e,n),r==="command"))for(const s of F(n))p(e,s)}),A(t,e),j(t,e),[...e]}function x(t,e,n,r,s){const a=y(e,n);if(!a)return;const i=t.get(a.relativePath),o=C(a.absolutePath),f={path:a.absolutePath,absolutePath:a.absolutePath,relativePath:a.relativePath,changeType:P(i?.changeType,r),source:i?.source==="acp_tool"||i?.source==="acp_diff"?i.source:s,exists:!!o?.isFile(),downloadable:!!o?.isFile(),...o?.isFile()?{size:o.size,mime:z(a.absolutePath),sha256:o.size<=m?_(a.absolutePath):void 0}:{}};t.set(a.relativePath,f)}function P(t,e){return!t||t==="unknown"?e:e==="unknown"||t===e?t:t==="created"&&e==="modified"?"created":e}function y(t,e){if(!e||e.includes("\0"))return null;const n=c.resolve(t),r=c.isAbsolute(e)?c.resolve(e):c.resolve(n,e),s=c.relative(n,r).replace(/\\/g,"/");return!s||s.startsWith("..")||c.isAbsolute(s)?null:{absolutePath:r,relativePath:s}}function b(t){try{return l("git",["-C",t,"rev-parse","--is-inside-work-tree"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}),!0}catch{return!1}}function v(t){try{const n=l("git",["-C",t,"status","--porcelain=v1","-z","--untracked-files=all"],{encoding:"buffer",maxBuffer:20971520}).toString("utf8").split("\0").filter(Boolean),r=[];for(let s=0;s<n.length;s+=1){const a=n[s],i=a.slice(0,2),o=a.slice(3);if(o)if(i.startsWith("R")||i.startsWith("C")){const f=n[s+1];f&&(r.push({status:i,path:f}),s+=1)}else r.push({status:i,path:o})}return r}catch{return null}}function w(t,e){try{const n=l("git",["-C",t,"hash-object","--",e],{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:1048576}).trim();return/^[0-9a-f]{40,64}$/i.test(n)?n:null}catch{return null}}function u(t,e,n,r=0){if(!(r>8)&&(e(t,n),!(!t||typeof t!="object"))){if(Array.isArray(t)){for(const s of t)u(s,e,void 0,r+1);return}for(const[s,a]of Object.entries(t))u(a,e,s,r+1)}}function A(t,e){u(t,n=>{if(!n||typeof n!="object"||Array.isArray(n))return;const r=n;r.type==="diff"&&p(e,r.path)})}function j(t,e){u(t,(n,r)=>{if(!(r!=="changes"||!n||typeof n!="object"||Array.isArray(n)))for(const s of Object.keys(n))p(e,s)})}function S(t){return t?g.includes(t):!1}function p(t,e){if(typeof e!="string")return;const n=k(e);!n||n.length>500||n.includes(`
2
+ `)||n==="."||n===".."||t.add(n)}function k(t){let e=t.trim();e=e.replace(/\s+\+\d+\s*\/\s*-\d+(?:\s+.*)?$/,""),e=e.replace(/\s+\(\s*\+\d+\s*[,/ ]\s*-\d+\s*\)(?:\s+.*)?$/,""),e=e.replace(/\s+(?:source|event|workspace|session|last)=\S+.*$/,"");const n=e.split(/\s+/)[0]||"";return n&&/\.[A-Za-z][A-Za-z0-9+-]{0,12}$/.test(n)&&(e=n),e.replace(/^["']|["']$/g,"").trim()}function F(t){const e=[],n=/(?:^|\s)(?:\d?>|>>)\s*(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;let r;for(;r=n.exec(t);){const a=r[1]||r[2]||r[3];a&&e.push(a)}const s=/(?:^|\s)touch\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;for(;r=s.exec(t);){const a=r[1]||r[2]||r[3];a&&!a.startsWith("-")&&e.push(a)}return e}function C(t){try{return d.statSync(t)}catch{return null}}function _(t){try{return h.createHash("sha256").update(d.readFileSync(t)).digest("hex")}catch{return}}function z(t){switch(c.extname(t).toLowerCase()){case".doc":return"application/msword";case".xls":return"application/vnd.ms-excel";case".xlsm":return"application/vnd.ms-excel.sheet.macroEnabled.12";case".ppt":return"application/vnd.ms-powerpoint";case".pptx":return"application/vnd.openxmlformats-officedocument.presentationml.presentation";case".odt":return"application/vnd.oasis.opendocument.text";case".ods":return"application/vnd.oasis.opendocument.spreadsheet";case".odp":return"application/vnd.oasis.opendocument.presentation";case".rtf":return"application/rtf";case".svg":return"image/svg+xml";case".bmp":return"image/bmp";case".tif":case".tiff":return"image/tiff";case".avif":return"image/avif";case".pdf":return"application/pdf";case".md":case".markdown":return"text/markdown; charset=utf-8";case".txt":case".log":return"text/plain; charset=utf-8";case".json":return"application/json; charset=utf-8";case".csv":return"text/csv; charset=utf-8";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".zip":return"application/zip";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";default:return"application/octet-stream"}}export{T as captureGitSnapshot,R as collectAffectedFiles,E as extractAffectedFilePathsFromAcpRaw,z as mimeForPath};
@@ -0,0 +1 @@
1
+ import l from"node:fs";import n from"node:path";import{mimeForPath as x}from"../affected-files.js";const A=new Set([".pdf",".doc",".docx",".odt",".rtf",".xls",".xlsx",".xlsm",".ods",".csv",".ppt",".pptx",".odp",".png",".jpg",".jpeg",".gif",".webp",".bmp",".tif",".tiff",".svg",".avif"]),_=new Set([".git","node_modules",".venv","venv","__pycache__"]),z=[".aca/attachments",".aica/attachments",".aca/artifacts",".aica/artifacts",".aca/logs",".aica/logs",".aca/server-data",".aica/server-data"],M=8192,S=1e4,u=new Map;function m(o){const t=n.basename(o);return!f(o)&&!t.startsWith("~$")&&A.has(n.extname(t).toLowerCase())}function f(o){const t=o.split(n.sep).join("/");return t.split("/").some(e=>_.has(e))||z.some(e=>t===e||t.startsWith(`${e}/`))}class N{root;warn;watches=new Map;subscribers=new Set;pending=new Set;timer;closed=!1;warned=!1;draining=!1;constructor(t,e){this.root=t,this.warn=e}degraded(t){this.warned||this.closed||(this.warned=!0,this.warn(`文档监听部分不可用,将以结束扫描补偿(仍可能遗漏瞬时变化):${String(t)}`))}watchDirectory(t){if(!(this.closed||this.watches.has(t))){if(this.watches.size>=M){this.degraded("目录监听数量达到上限");return}try{const e=l.watch(t,{persistent:!1},(i,s)=>{if(this.closed||!s)return;const a=n.join(t,s.toString()),r=n.relative(this.root,a);if(!f(r)){if(m(r))for(const c of this.subscribers)c.size<S?c.add(r):this.degraded("文件事件候选数量达到上限");i==="rename"&&this.schedule(a)}});e.on("error",i=>{e.close(),this.watches.get(t)===e&&this.watches.delete(t),this.degraded(i)}),this.watches.set(t,e)}catch(e){this.degraded(e)}}}schedule(t){if(this.pending.size>=S){this.degraded("目录事件积压");return}this.pending.add(t),!(this.timer||this.draining)&&(this.timer=setTimeout(()=>{this.timer=void 0,this.drain()},50),this.timer.unref())}async drain(){this.draining=!0;try{for(;!this.closed&&this.pending.size>0;){const t=this.pending.values().next().value;if(this.pending.delete(t),this.watches.has(t))for(const[e,i]of this.watches)(e===t||e.startsWith(`${t}${n.sep}`))&&(i.close(),this.watches.delete(e));await this.walk(t,new Map)}}finally{this.draining=!1}}async snapshot(){const t=new Map;return await this.walk(this.root,t),t}async walk(t,e){if(this.closed)return;const i=n.relative(this.root,t);if(!f(i))try{const s=await l.promises.lstat(t);if(s.isSymbolicLink())return;if(s.isDirectory()){this.watchDirectory(t);const a=await l.promises.opendir(t);for await(const r of a){if(this.closed)break;if(r.isSymbolicLink())continue;const c=n.join(t,r.name),d=n.relative(this.root,c);f(d)||(r.isDirectory()||m(d))&&await this.walk(c,e)}}else if(s.isFile()&&m(i)){const a=await l.promises.realpath(t),r=n.relative(this.root,a);if(r.startsWith(`..${n.sep}`)||n.isAbsolute(r)||f(r))return;e.set(i,{size:s.size,signature:`${s.dev}:${s.ino}:${s.size}:${s.mtimeMs}:${s.ctimeMs}`})}}catch(s){const a=s.code;a!=="ENOENT"&&a!=="ENOTDIR"&&this.degraded(s)}}close(){this.closed=!0,this.timer&&clearTimeout(this.timer);for(const t of this.watches.values())t.close();this.watches.clear(),this.pending.clear()}}async function j(o,t){const e=await l.promises.realpath(o);let i=u.get(e);i||(i=new N(e,t),u.set(e,i));const s=i,a=new Set;s.subscribers.add(a);let r=!1;const c=()=>{r||(r=!0,s.subscribers.delete(a),s.subscribers.size===0&&(s.close(),u.delete(e)))};let d;try{d=await s.snapshot(),a.clear()}catch(w){throw c(),w}let b;return{dispose:c,finish(){return b??=(async()=>{if(r)return[];try{const w=await s.snapshot(),g=[];for(const[h,p]of w){const v=d.get(h);if(!a.has(h)&&v?.signature===p.signature)continue;const y=n.join(e,h);g.push({path:y,absolutePath:y,relativePath:h.split(n.sep).join("/"),changeType:v?"modified":"created",source:"job_time_window",exists:!0,downloadable:!0,size:p.size,mime:x(h)})}return g.sort((h,p)=>h.relativePath.localeCompare(p.relativePath))}finally{c()}})()}}}function k(o,t){const e=new Map(o.map(i=>[i.relativePath,i]));for(const i of t)e.set(i.relativePath,i);return[...e.values()]}export{m as isArtifactPath,f as isIgnoredArtifactPath,k as mergeArtifactFiles,j as startJobArtifactMonitor};
@@ -1,6 +1,6 @@
1
- import X from"node:crypto";import{appendSessionReferenceContext as Z}from"./session-reference-prompt.js";import N from"node:fs";import M from"node:path";import{compactAgentContext as Q,forkAgentSession as Y,sendAgentPrompt as ee}from"../acp/agent.js";import{acaServerDownload as te,acaServerRequest as S}from"./aca-server-client.js";import{loadAcaConfig as ne}from"./aca-config.js";import{prepareLocalWorktree as oe}from"./worktree.js";import{expandWorkerRuntimePath as ie}from"./worker-runtime-paths.js";import{collectAffectedFiles as J,extractAffectedFilePathsFromAcpRaw as ae}from"./affected-files.js";import{resolveJobPermissionPolicy as re}from"./job-permission-policy.js";import{createAcpEventBatcher as se}from"./acp-event-batcher.js";import{createToolLifecycleTrace as ce}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as le}from"./acp-event-upload-filter.js";import{userFacingAcpError as de}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as ue}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as pe}from"../acp/session-errors.js";const me=1500,B=5e3,L=1e3,W=900,fe=1e4,he=0,we=12e3;function Ie(e){return ie(e)}function ye(e){const t=e.project_metadata;return t&&typeof t=="object"&&!Array.isArray(t)&&t.kind==="chat"?!0:typeof e.project_root_path=="string"&&(e.project_root_path.startsWith("~/.aica/chats/")||e.project_root_path.startsWith("~/.aca/chats/")||/(^|\/)\.aica\/chats\//.test(e.project_root_path)||/(^|\/)\.aca\/chats\//.test(e.project_root_path))}function lt(e,t={}){let n=!1,i=null,o=null,s=0,l=0;const u=new Set,r=new Map,b=new Map,A=async()=>{if(n||Date.now()<l)return;const m=ve();if(D(u.size,m))try{const p=t.loadConfig?.()??ne();if(!p.token)return;for(;!n&&D(u.size,m);){const v=t.claimNextJob?await t.claimNextJob(p,[...r.keys()]):await S("POST","/api/client/jobs/claim",{machineId:p.machineId,workspaceId:p.defaultWorkspaceId,activeSessionIds:[...r.keys()]});if(s=0,l=0,!v.item)break;const f=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(f,p):j(f.job_id,p.machineId,"cancelled")).catch(()=>{});break}const h=f.session_id,w=r.get(h);u.add(f.job_id);const P=new AbortController;let x;x=(async()=>{w&&(e.warn(`claimed job=${f.job_id} for already active session=${h}; waiting for the previous run to release its ACP runtime`),await be(w,f,p,P)),!P.signal.aborted&&await(t.runJob??ge)(f,p,e,P)})().catch(c=>{e.warn(`job ${f.job_id} failed outside handler: ${c instanceof Error?c.message:String(c)}`)}).finally(()=>{u.delete(f.job_id),r.get(h)===x&&r.delete(h),b.delete(f.job_id)}),r.set(h,x),b.set(f.job_id,{controller:P,promise:x})}}catch(p){if(!n){s+=1;const v=We(s);l=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${p instanceof Error?p.message:String(p)}`)}}},C=()=>{if(n||i)return;const m=A();i=m,m.finally(()=>{i===m&&(i=null)})};C();const d=setInterval(()=>{C()},t.claimIntervalMs??me);return{timer:d,stop:()=>o||(o=(async()=>{if(n)return;n=!0,clearInterval(d);for(const p of b.values())p.controller.abort();await i?.catch(()=>{});for(const p of b.values())p.controller.abort();const m=[...b.values()].map(p=>p.promise);m.length!==0&&await Promise.race([Promise.allSettled(m).then(()=>{}),F(we)])})(),o)}}async function be(e,t,n,i){let o=!1;const l=setInterval(()=>{o||i.signal.aborted||(o=!0,Promise.allSettled([S("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),G(t.job_id,i)]).finally(()=>{o=!1}))},B);try{await Promise.race([e,Se(i.signal)])}finally{clearInterval(l)}}function Se(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function ve(){const e=process.env.ACA_MAX_CONCURRENT_JOBS||process.env.ACA_MAX_CONCURRENT_PROJECT_JOBS||"",t=Number.parseInt(e,10);return!e.trim()||!Number.isFinite(t)||t<0?he:t===0?0:Math.max(1,Math.min(t,8))}function D(e,t){return t===0||e<t}async function ge(e,t,n,i){const o=e.job_id,s=e.session_id,l=e.workspace_id,u=e.project_id??null,r=e.input??{},b=String(r.control?.type||"").toLowerCase();if(!b&&!r.message?.trim()&&(r.attachments??[]).length===0&&!r.sessionReferences?.length){await j(o,t.machineId,"job input missing message");return}const A=t.projects.find(c=>c.projectId===u),C=Ie(A?.rootPath||e.project_root_path||"");if(!C){await j(o,t.machineId,"job project root is unknown");return}ye(e)&&N.mkdirSync(C,{recursive:!0}),n.info(`claimed job=${o} session=${s} project=${A?.name??u??"unknown"}`);let d=!1,m=!1;const p=setInterval(()=>{d||(d=!0,S("POST",`/api/client/jobs/${encodeURIComponent(o)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{d=!1})),m||(m=!0,G(o,i).catch(()=>{}).finally(()=>{m=!1}))},B);let v=0;const f=ce(c=>n.info(c)),h=se(o,t.machineId,{warn:c=>n.warn(c),trace:f}),w=Re(o,t.machineId),P=new Set;let x=null;try{await w.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const c=oe({projectId:u,sessionId:s,projectRoot:C,worktree:e});x=c,await ze(o,t.machineId,c);const g=Te(r.acpRun);if(Ae(g.agentType)&&await ue(e.codex_provider_revision),b==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const a=await Q({agentType:k(g.agentType,"codex"),cliType:k(g.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,allowRollover:r.control?.allowRollover===!0,signal:i.signal,onStatus:y=>{w.report({phase:y.phase,label:y.label,detail:y.detail,updateType:y.updateType,raw:y})}});await w.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:a.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:r.control?.allowRollover===!0},acpSessionId:a.acpSessionId,durationMs:a.durationMs,usage:a.contextUsage,maintenance:a.maintenance,worktree:$(c)}}),n.info(`completed context compact job=${o}`);return}if(b==="fork"){const a=String(r.control?.sourceAcpSessionId||"").trim();if(!a)throw new Error("Source session does not have a Codex ACP context to fork");await w.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:r.control?.sourceSessionId?`源会话 ${r.control.sourceSessionId}`:void 0}).catch(()=>{});const y=await Y({agentType:k(g.agentType,"codex"),cliType:k(g.cliType,"builtin"),sourceProviderSessionId:a,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,signal:i.signal,onStatus:R=>{w.report({phase:R.phase,label:R.label,detail:R.detail,updateType:R.updateType,raw:R})}});await w.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:y.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:r.control?.sourceSessionId||null},acpSessionId:y.acpSessionId,worktree:$(c)}}),n.info(`completed Codex session fork job=${o}`);return}const _=await Ee({cwd:c.cwd,sessionId:s,jobId:o,attachments:r.attachments??[]});_.length>0&&(n.info(`materialized ${_.length} attachment(s) for job=${o}`),await w.sendNow({phase:"initializing",label:"附件已写入本机",detail:_.map(a=>a.relativePath).join(", "),raw:{attachments:q(_)}}).catch(()=>{}));const T=$e(r,_),O=je(T,_),I=await ee({agentType:k(g.agentType,"codex"),cliType:k(g.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:_e(e.session_raw),prompt:T,promptBlocks:O,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,canExecuteTools:r.canExecuteTools,timeoutMs:r.timeoutMs,contextWindow:Ce(g.contextWindow),signal:i.signal,onStatus:a=>{w.report({phase:a.phase,label:a.label,detail:a.detail,updateType:a.updateType,raw:a})},onUpdate:a=>{v+=1,(a.type==="tool_call"||a.type==="tool_call_update"&&["completed","failed","cancelled"].includes(a.status||""))&&f("worker_event",{jobId:o,uploadRunId:h.uploadRunId,toolCallId:a.toolCallId,status:a.status,sequence:v});for(const R of ae(a.raw))P.add(R);const y={turnId:e.user_turn_id??null,eventType:a.type,label:a.label,text:a.text,status:a.status,toolCallId:a.toolCallId,raw:ke(a.raw,v)};le(y)&&h.push(y)},onPermissionRequest:a=>{const y=re(r,a);return y?Promise.resolve(y):Ue(o,t.machineId,a,i.signal)},onUserInputRequest:a=>Je(o,t.machineId,a,i.signal),onProviderSessionMigration:async a=>{const y=await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:a.previousProviderSessionId,acpSessionId:a.providerSessionId,reason:a.reason,migratedAtMs:a.migratedAtMs});e.acp_session_id=a.providerSessionId,n.info(`migrated ACP session for job=${o} ${a.previousProviderSessionId} -> ${a.providerSessionId}`),await h.flush().catch(()=>{}),await w.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:a.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:a,server:{migrated:y.migrated===!0}}}).catch(()=>{})}}),U=await h.finish();await w.flush();const H=J({cwd:c.cwd,evidencePaths:P}),E=Pe(I.promptResponse);if(!(xe(E)||!!I.content)){await j(o,t.machineId,`${I.agentType} stopped: ${E??"unknown"}`,{acpSessionId:I.acpSessionId,stopReason:E,eventUpload:U,exitCode:I.exitCode,stderr:De(I.stderr,4e3),worktree:$(c)});return}await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:I.acpSessionId,assistantText:I.content,result:{ok:!0,eventUpload:U,acpSessionId:I.acpSessionId,stopReason:E,durationMs:I.durationMs,acpRun:r.acpRun??null,effectiveConfig:I.effectiveConfig??null,performance:I.performance??null,usage:I.contextUsage??null,providerDiagnostics:I.providerDiagnostics??null,updateCount:I.updateCount,statusTimeline:I.statusTimeline,worktree:$(c),attachments:q(_),files:H}}),n.info(`completed job=${o}`)}catch(c){if(Le(i.signal)){h.discard(),w.discard(),n.warn(`stopped local job=${o} because ACA Server already finalized it`);return}const g=await h.finish();await w.flush();const _=i.signal.aborted?"cancelled":c instanceof Error?c.message:String(c),T=pe(c),O=x?J({cwd:x.cwd,evidencePaths:P}):[];await j(o,t.machineId,de(_),{rawError:_,eventUpload:g,files:O,providerDiagnostics:z(c,"providerDiagnostics"),statusTimeline:z(c,"statusTimeline"),performance:z(c,"performance"),...T?{acpSessionInvalidated:!0,invalidatedAcpSessionId:T.providerSessionId,failure:{category:T.category,actions:T.actions,details:T.details}}:{}},T?{invalidateAcpSession:!0,invalidatedAcpSessionId:T.providerSessionId}:void 0)}finally{h.discard(),clearInterval(p),await w.flush()}}function _e(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e.importedProviderSession;return!!(t&&typeof t=="object"&&!Array.isArray(t)&&t.strictResume===!0)}function Te(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function Ae(e){const t=String(e||"codex").toLowerCase().replace(/[^a-z0-9]+/g,"");return t==="codex"||t==="codexacp"||t==="codexstandard"}function Ce(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function k(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function Pe(e){if(!e||typeof e!="object"||Array.isArray(e))return;const t=e;if(typeof t.stopReason=="string")return t.stopReason;if(typeof t.reason=="string")return t.reason;const n=t.part;if(n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.reason=="string")return n.reason}function xe(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Re(e,t){let n=null,i=0,o="",s=null,l=Promise.resolve(),u=!1;const r=d=>JSON.stringify({phase:d.phase??"",label:d.label??"",detail:d.detail??"",updateType:d.updateType??""}),b=async d=>{await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:d.phase,label:d.label,detail:d.detail,updateType:d.updateType,raw:d.raw}),i=Date.now(),o=r(d)},A=()=>{if(s&&(clearTimeout(s),s=null),u)return n=null,l;if(!n)return l;const d=n;return n=null,l=l.then(()=>b(d)).catch(()=>{}),l},C=d=>{s||(s=setTimeout(()=>{A()},d),s.unref())};return{report(d){if(u)return;const m=Date.now(),v=r(d)!==o,f=m-i>=fe;if(!v&&!f)return;n=d;const h=m-i;if(h>=W||i===0){A();return}C(W-h)},sendNow(d){return u?l:(n=d,A())},flush:A,discard(){u=!0,n=null,s&&clearTimeout(s),s=null}}}function ke(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function $e(e,t=[]){const n=Z(e?.message?.trim()??"",e?.sessionReferences);if(t.length===0)return n;const o=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((s,l)=>`${l+1}. ${s.name} (${s.kind}, ${s.mimeType}, ${Fe(s.size)})
1
+ import ee from"node:crypto";import{startJobArtifactMonitor as te,mergeArtifactFiles as W}from"./artifacts/job-artifact-monitor.js";import{appendSessionReferenceContext as ne}from"./session-reference-prompt.js";import F from"node:fs";import N from"node:path";import{compactAgentContext as oe,forkAgentSession as ie,sendAgentPrompt as ae}from"../acp/agent.js";import{acaServerDownload as re,acaServerRequest as S}from"./aca-server-client.js";import{loadAcaConfig as se}from"./aca-config.js";import{prepareLocalWorktree as ce}from"./worktree.js";import{expandWorkerRuntimePath as le}from"./worker-runtime-paths.js";import{collectAffectedFiles as D,extractAffectedFilePathsFromAcpRaw as de}from"./affected-files.js";import{resolveJobPermissionPolicy as ue}from"./job-permission-policy.js";import{createAcpEventBatcher as pe}from"./acp-event-batcher.js";import{createToolLifecycleTrace as me}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as fe}from"./acp-event-upload-filter.js";import{userFacingAcpError as he}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as we}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as be}from"../acp/session-errors.js";const ye=1500,V=5e3,q=1e3,G=900,Ie=1e4,Se=0,ve=12e3;function ge(e){return le(e)}function _e(e){const t=e.project_metadata;return t&&typeof t=="object"&&!Array.isArray(t)&&t.kind==="chat"?!0:typeof e.project_root_path=="string"&&(e.project_root_path.startsWith("~/.aica/chats/")||e.project_root_path.startsWith("~/.aca/chats/")||/(^|\/)\.aica\/chats\//.test(e.project_root_path)||/(^|\/)\.aca\/chats\//.test(e.project_root_path))}function ht(e,t={}){let n=!1,i=null,o=null,s=0,l=0;const u=new Set,r=new Map,I=new Map,_=async()=>{if(n||Date.now()<l)return;const f=Ce();if(K(u.size,f))try{const p=t.loadConfig?.()??se();if(!p.token)return;for(;!n&&K(u.size,f);){const v=t.claimNextJob?await t.claimNextJob(p,[...r.keys()]):await S("POST","/api/client/jobs/claim",{machineId:p.machineId,workspaceId:p.defaultWorkspaceId,activeSessionIds:[...r.keys()]});if(s=0,l=0,!v.item)break;const h=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(h,p):M(h.job_id,p.machineId,"cancelled")).catch(()=>{});break}const w=h.session_id,b=r.get(w);u.add(h.job_id);const P=new AbortController;let x;x=(async()=>{b&&(e.warn(`claimed job=${h.job_id} for already active session=${w}; waiting for the previous run to release its ACP runtime`),await Te(b,h,p,P)),!P.signal.aborted&&await(t.runJob??Pe)(h,p,e,P)})().catch(R=>{e.warn(`job ${h.job_id} failed outside handler: ${R instanceof Error?R.message:String(R)}`)}).finally(()=>{u.delete(h.job_id),r.get(w)===x&&r.delete(w),I.delete(h.job_id)}),r.set(w,x),I.set(h.job_id,{controller:P,promise:x})}}catch(p){if(!n){s+=1;const v=Ke(s);l=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${p instanceof Error?p.message:String(p)}`)}}},C=()=>{if(n||i)return;const f=_();i=f,f.finally(()=>{i===f&&(i=null)})};C();const d=setInterval(()=>{C()},t.claimIntervalMs??ye);return{timer:d,stop:()=>o||(o=(async()=>{if(n)return;n=!0,clearInterval(d);for(const p of I.values())p.controller.abort();await i?.catch(()=>{});for(const p of I.values())p.controller.abort();const f=[...I.values()].map(p=>p.promise);f.length!==0&&await Promise.race([Promise.allSettled(f).then(()=>{}),z(ve)])})(),o)}}async function Te(e,t,n,i){let o=!1;const l=setInterval(()=>{o||i.signal.aborted||(o=!0,Promise.allSettled([S("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),Z(t.job_id,i)]).finally(()=>{o=!1}))},V);try{await Promise.race([e,Ae(i.signal)])}finally{clearInterval(l)}}function Ae(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function Ce(){const e=process.env.ACA_MAX_CONCURRENT_JOBS||process.env.ACA_MAX_CONCURRENT_PROJECT_JOBS||"",t=Number.parseInt(e,10);return!e.trim()||!Number.isFinite(t)||t<0?Se:t===0?0:Math.max(1,Math.min(t,8))}function K(e,t){return t===0||e<t}async function Pe(e,t,n,i){const o=e.job_id,s=e.session_id,l=e.workspace_id,u=e.project_id??null,r=e.input??{},I=String(r.control?.type||"").toLowerCase();if(!I&&!r.message?.trim()&&(r.attachments??[]).length===0&&!r.sessionReferences?.length){await M(o,t.machineId,"job input missing message");return}const _=t.projects.find(c=>c.projectId===u),C=ge(_?.rootPath||e.project_root_path||"");if(!C){await M(o,t.machineId,"job project root is unknown");return}_e(e)&&F.mkdirSync(C,{recursive:!0}),n.info(`claimed job=${o} session=${s} project=${_?.name??u??"unknown"}`);let d=!1,f=!1;const p=setInterval(()=>{d||(d=!0,S("POST",`/api/client/jobs/${encodeURIComponent(o)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{d=!1})),f||(f=!0,Z(o,i).catch(()=>{}).finally(()=>{f=!1}))},V);let v=0;const h=me(c=>n.info(c)),w=pe(o,t.machineId,{warn:c=>n.warn(c),trace:h}),b=Me(o,t.machineId),P=new Set;let x=null,R;const J=async()=>R?.finish().catch(c=>(n.warn(`artifact collection failed job=${o}: ${String(c)}`),[]))??[];try{await b.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const c=ce({projectId:u,sessionId:s,projectRoot:C,worktree:e});x=c,await We(o,t.machineId,c);const g=Re(r.acpRun);if(ke(g.agentType)&&await we(e.codex_provider_revision),I==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const a=await oe({agentType:$(g.agentType,"codex"),cliType:$(g.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,allowRollover:r.control?.allowRollover===!0,signal:i.signal,onStatus:y=>{b.report({phase:y.phase,label:y.label,detail:y.detail,updateType:y.updateType,raw:y})}});await b.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:a.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:r.control?.allowRollover===!0},acpSessionId:a.acpSessionId,durationMs:a.durationMs,usage:a.contextUsage,maintenance:a.maintenance,worktree:E(c)}}),n.info(`completed context compact job=${o}`);return}if(I==="fork"){const a=String(r.control?.sourceAcpSessionId||"").trim();if(!a)throw new Error("Source session does not have a Codex ACP context to fork");await b.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:r.control?.sourceSessionId?`源会话 ${r.control.sourceSessionId}`:void 0}).catch(()=>{});const y=await ie({agentType:$(g.agentType,"codex"),cliType:$(g.cliType,"builtin"),sourceProviderSessionId:a,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,timeoutMs:r.timeoutMs,signal:i.signal,onStatus:k=>{b.report({phase:k.phase,label:k.label,detail:k.detail,updateType:k.updateType,raw:k})}});await b.flush(),await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:y.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:r.control?.sourceSessionId||null},acpSessionId:y.acpSessionId,worktree:E(c)}}),n.info(`completed Codex session fork job=${o}`);return}const T=await ze({cwd:c.cwd,sessionId:s,jobId:o,attachments:r.attachments??[]});T.length>0&&(n.info(`materialized ${T.length} attachment(s) for job=${o}`),await b.sendNow({phase:"initializing",label:"附件已写入本机",detail:T.map(a=>a.relativePath).join(", "),raw:{attachments:X(T)}}).catch(()=>{}));const j=Ne(r,T),A=Fe(j,T);R=await te(c.cwd,a=>n.warn(a)).catch(a=>{n.warn(`artifact monitor unavailable job=${o}: ${String(a)}`)});const m=await ae({agentType:$(g.agentType,"codex"),cliType:$(g.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:xe(e.session_raw),prompt:j,promptBlocks:A,cwd:c.cwd,model:r.model,mode:r.mode,configOptionValues:r.configOptionValues,canExecuteTools:r.canExecuteTools,timeoutMs:r.timeoutMs,contextWindow:$e(g.contextWindow),signal:i.signal,onStatus:a=>{b.report({phase:a.phase,label:a.label,detail:a.detail,updateType:a.updateType,raw:a})},onUpdate:a=>{v+=1,(a.type==="tool_call"||a.type==="tool_call_update"&&["completed","failed","cancelled"].includes(a.status||""))&&h("worker_event",{jobId:o,uploadRunId:w.uploadRunId,toolCallId:a.toolCallId,status:a.status,sequence:v});for(const k of de(a.raw))P.add(k);const y={turnId:e.user_turn_id??null,eventType:a.type,label:a.label,text:a.text,status:a.status,toolCallId:a.toolCallId,raw:Oe(a.raw,v)};fe(y)&&w.push(y)},onPermissionRequest:a=>{const y=ue(r,a);return y?Promise.resolve(y):De(o,t.machineId,a,i.signal)},onUserInputRequest:a=>Ve(o,t.machineId,a,i.signal),onProviderSessionMigration:async a=>{const y=await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:a.previousProviderSessionId,acpSessionId:a.providerSessionId,reason:a.reason,migratedAtMs:a.migratedAtMs});e.acp_session_id=a.providerSessionId,n.info(`migrated ACP session for job=${o} ${a.previousProviderSessionId} -> ${a.providerSessionId}`),await w.flush().catch(()=>{}),await b.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:a.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:a,server:{migrated:y.migrated===!0}}}).catch(()=>{})}}),Y=await J(),B=await w.finish();await b.flush();const L=W(D({cwd:c.cwd,evidencePaths:P}),Y),O=je(m.promptResponse);if(!(Ee(O)||!!m.content)){await M(o,t.machineId,`${m.agentType} stopped: ${O??"unknown"}`,{acpSessionId:m.acpSessionId,stopReason:O,eventUpload:B,files:L,exitCode:m.exitCode,stderr:He(m.stderr,4e3),worktree:E(c)});return}await S("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:m.acpSessionId,assistantText:m.content,result:{ok:!0,eventUpload:B,acpSessionId:m.acpSessionId,stopReason:O,durationMs:m.durationMs,acpRun:r.acpRun??null,effectiveConfig:m.effectiveConfig??null,performance:m.performance??null,usage:m.contextUsage??null,providerDiagnostics:m.providerDiagnostics??null,updateCount:m.updateCount,statusTimeline:m.statusTimeline,worktree:E(c),attachments:X(T),files:L}}),n.info(`completed job=${o}`)}catch(c){if(Ge(i.signal)){w.discard(),b.discard(),n.warn(`stopped local job=${o} because ACA Server already finalized it`);return}const g=await J(),T=await w.finish();await b.flush();const j=i.signal.aborted?"cancelled":c instanceof Error?c.message:String(c),A=be(c),m=W(x?D({cwd:x.cwd,evidencePaths:P}):[],g);await M(o,t.machineId,he(j),{rawError:j,eventUpload:T,files:m,providerDiagnostics:U(c,"providerDiagnostics"),statusTimeline:U(c,"statusTimeline"),performance:U(c,"performance"),...A?{acpSessionInvalidated:!0,invalidatedAcpSessionId:A.providerSessionId,failure:{category:A.category,actions:A.actions,details:A.details}}:{}},A?{invalidateAcpSession:!0,invalidatedAcpSessionId:A.providerSessionId}:void 0)}finally{R?.dispose(),w.discard(),clearInterval(p),await b.flush()}}function xe(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e.importedProviderSession;return!!(t&&typeof t=="object"&&!Array.isArray(t)&&t.strictResume===!0)}function Re(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function ke(e){const t=String(e||"codex").toLowerCase().replace(/[^a-z0-9]+/g,"");return t==="codex"||t==="codexacp"||t==="codexstandard"}function $e(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function $(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function je(e){if(!e||typeof e!="object"||Array.isArray(e))return;const t=e;if(typeof t.stopReason=="string")return t.stopReason;if(typeof t.reason=="string")return t.reason;const n=t.part;if(n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.reason=="string")return n.reason}function Ee(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Me(e,t){let n=null,i=0,o="",s=null,l=Promise.resolve(),u=!1;const r=d=>JSON.stringify({phase:d.phase??"",label:d.label??"",detail:d.detail??"",updateType:d.updateType??""}),I=async d=>{await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:d.phase,label:d.label,detail:d.detail,updateType:d.updateType,raw:d.raw}),i=Date.now(),o=r(d)},_=()=>{if(s&&(clearTimeout(s),s=null),u)return n=null,l;if(!n)return l;const d=n;return n=null,l=l.then(()=>I(d)).catch(()=>{}),l},C=d=>{s||(s=setTimeout(()=>{_()},d),s.unref())};return{report(d){if(u)return;const f=Date.now(),v=r(d)!==o,h=f-i>=Ie;if(!v&&!h)return;n=d;const w=f-i;if(w>=G||i===0){_();return}C(G-w)},sendNow(d){return u?l:(n=d,_())},flush:_,discard(){u=!0,n=null,s&&clearTimeout(s),s=null}}}function Oe(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function Ne(e,t=[]){const n=ne(e?.message?.trim()??"",e?.sessionReferences);if(t.length===0)return n;const o=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((s,l)=>`${l+1}. ${s.name} (${s.kind}, ${s.mimeType}, ${Le(s.size)})
2
2
  localPath: ${s.path}
3
3
  workspacePath: ${s.relativePath}`)].join(`
4
4
  `);return n?`${n}
5
5
 
6
- ${o}`:o}function je(e,t){const n=[{type:"text",text:e}];for(const i of t)i.kind!=="image"||!i.mimeType.startsWith("image/")||n.push({type:"image",data:i.base64,mimeType:i.mimeType,uri:i.path});return n}async function Ee(e){if(e.attachments.length===0)return[];const t=M.join(e.cwd,".aca","attachments",V(e.sessionId),V(e.jobId));N.mkdirSync(t,{recursive:!0});const n=[];for(const[i,o]of e.attachments.entries()){const s=o.attachmentId?{mimeType:o.mimeType||"application/octet-stream",bytes:await te(`/api/client/attachments/${encodeURIComponent(o.attachmentId)}`)}:Me(o.dataUrl??""),l=o.mimeType||s.mimeType||"application/octet-stream",u=o.kind==="image"||l.startsWith("image/")?"image":"file",r=Oe(o.name||`attachment-${i+1}`,l),b=M.join(t,`${String(i+1).padStart(2,"0")}-${r}`);N.writeFileSync(b,s.bytes),n.push({name:o.name||r,mimeType:l,size:s.bytes.byteLength,kind:u,path:b,relativePath:M.relative(e.cwd,b),base64:s.bytes.toString("base64"),sha256:X.createHash("sha256").update(s.bytes).digest("hex")})}return n}function Me(e){const t=e.indexOf(",");if(!e.startsWith("data:")||t<0)throw new Error("attachment dataUrl is not a data URL");const n=e.slice(5,t),i=e.slice(t+1),o=n.split(";").filter(Boolean),s=o[0]?.includes("/")?o[0]:"application/octet-stream";if(!o.includes("base64"))throw new Error("attachment dataUrl is not base64 encoded");return{mimeType:s,bytes:Buffer.from(i,"base64")}}function Oe(e,t){const n=M.basename(e).replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").trim(),i=`attachment${Ne(t)}`;return(!n||n==="."||n===".."?i:n).slice(0,180)}function Ne(e){switch(e.toLowerCase()){case"image/png":return".png";case"image/jpeg":return".jpg";case"image/webp":return".webp";case"image/gif":return".gif";case"text/plain":return".txt";case"application/json":return".json";case"text/markdown":case"text/x-markdown":return".md";case"application/pdf":return".pdf";case"application/msword":return".doc";case"application/vnd.openxmlformats-officedocument.wordprocessingml.document":return".docx";case"application/vnd.ms-excel":return".xls";case"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":return".xlsx";case"application/vnd.ms-powerpoint":return".ppt";case"application/vnd.openxmlformats-officedocument.presentationml.presentation":return".pptx";case"application/zip":return".zip";case"application/x-7z-compressed":return".7z";case"application/x-rar-compressed":case"application/vnd.rar":return".rar";case"application/gzip":return".gz";case"application/x-tar":return".tar";default:return""}}function V(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"item"}function q(e){return e.map(t=>({name:t.name,kind:t.kind,mimeType:t.mimeType,size:t.size,path:t.path,relativePath:t.relativePath,sha256:t.sha256}))}function Fe(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,i=0;for(;n>=1024&&i<t.length-1;)n/=1024,i+=1;return`${n>=10||i===0?n.toFixed(0):n.toFixed(1)} ${t[i]}`}async function ze(e,t,n){!n.usedWorktree||!n.worktreeId||await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??$(n)})}function $(e){return{usedWorktree:e.usedWorktree,worktreeId:e.worktreeId??null,cwd:e.cwd,status:e.status??null,baseCommit:e.baseCommit??null,branchName:e.branchName??null,baseBranch:e.baseBranch??null}}async function Ue(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:n})).item.permission_id;for(;!i.aborted;){await F(L);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="selected"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Je(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:{kind:"elicitation",...n}})).item.permission_id;for(;!i.aborted;){await F(L);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="submitted"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function G(e,t){if(t.signal.aborted)return;const n=await S("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const i=Be(n.item);i&&t.abort(new K(e,i))}function Be(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class K extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function Le(e){return e.aborted&&e.reason instanceof K}async function j(e,t,n,i,o){await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:i,...o?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:o.invalidatedAcpSessionId}:{}})}function F(e){return new Promise(t=>setTimeout(t,e))}function We(e){return Math.min(1e4,500*2**Math.min(5,Math.max(0,e-1)))+Math.floor(Math.random()*250)}function De(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}function z(e,t){return!e||typeof e!="object"||Array.isArray(e)?null:e[t]??null}export{Be as serverFinalizedJobStatus,lt as startJobWorkerLoop};
6
+ ${o}`:o}function Fe(e,t){const n=[{type:"text",text:e}];for(const i of t)i.kind!=="image"||!i.mimeType.startsWith("image/")||n.push({type:"image",data:i.base64,mimeType:i.mimeType,uri:i.path});return n}async function ze(e){if(e.attachments.length===0)return[];const t=N.join(e.cwd,".aca","attachments",H(e.sessionId),H(e.jobId));F.mkdirSync(t,{recursive:!0});const n=[];for(const[i,o]of e.attachments.entries()){const s=o.attachmentId?{mimeType:o.mimeType||"application/octet-stream",bytes:await re(`/api/client/attachments/${encodeURIComponent(o.attachmentId)}`)}:Ue(o.dataUrl??""),l=o.mimeType||s.mimeType||"application/octet-stream",u=o.kind==="image"||l.startsWith("image/")?"image":"file",r=Je(o.name||`attachment-${i+1}`,l),I=N.join(t,`${String(i+1).padStart(2,"0")}-${r}`);F.writeFileSync(I,s.bytes),n.push({name:o.name||r,mimeType:l,size:s.bytes.byteLength,kind:u,path:I,relativePath:N.relative(e.cwd,I),base64:s.bytes.toString("base64"),sha256:ee.createHash("sha256").update(s.bytes).digest("hex")})}return n}function Ue(e){const t=e.indexOf(",");if(!e.startsWith("data:")||t<0)throw new Error("attachment dataUrl is not a data URL");const n=e.slice(5,t),i=e.slice(t+1),o=n.split(";").filter(Boolean),s=o[0]?.includes("/")?o[0]:"application/octet-stream";if(!o.includes("base64"))throw new Error("attachment dataUrl is not base64 encoded");return{mimeType:s,bytes:Buffer.from(i,"base64")}}function Je(e,t){const n=N.basename(e).replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").trim(),i=`attachment${Be(t)}`;return(!n||n==="."||n===".."?i:n).slice(0,180)}function Be(e){switch(e.toLowerCase()){case"image/png":return".png";case"image/jpeg":return".jpg";case"image/webp":return".webp";case"image/gif":return".gif";case"text/plain":return".txt";case"application/json":return".json";case"text/markdown":case"text/x-markdown":return".md";case"application/pdf":return".pdf";case"application/msword":return".doc";case"application/vnd.openxmlformats-officedocument.wordprocessingml.document":return".docx";case"application/vnd.ms-excel":return".xls";case"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":return".xlsx";case"application/vnd.ms-powerpoint":return".ppt";case"application/vnd.openxmlformats-officedocument.presentationml.presentation":return".pptx";case"application/zip":return".zip";case"application/x-7z-compressed":return".7z";case"application/x-rar-compressed":case"application/vnd.rar":return".rar";case"application/gzip":return".gz";case"application/x-tar":return".tar";default:return""}}function H(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"item"}function X(e){return e.map(t=>({name:t.name,kind:t.kind,mimeType:t.mimeType,size:t.size,path:t.path,relativePath:t.relativePath,sha256:t.sha256}))}function Le(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,i=0;for(;n>=1024&&i<t.length-1;)n/=1024,i+=1;return`${n>=10||i===0?n.toFixed(0):n.toFixed(1)} ${t[i]}`}async function We(e,t,n){!n.usedWorktree||!n.worktreeId||await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??E(n)})}function E(e){return{usedWorktree:e.usedWorktree,worktreeId:e.worktreeId??null,cwd:e.cwd,status:e.status??null,baseCommit:e.baseCommit??null,branchName:e.branchName??null,baseBranch:e.baseBranch??null}}async function De(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:n})).item.permission_id;for(;!i.aborted;){await z(q);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="selected"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Ve(e,t,n,i){const s=(await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:{kind:"elicitation",...n}})).item.permission_id;for(;!i.aborted;){await z(q);const l=await S("GET",`/api/client/permissions/${encodeURIComponent(s)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="submitted"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Z(e,t){if(t.signal.aborted)return;const n=await S("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const i=qe(n.item);i&&t.abort(new Q(e,i))}function qe(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class Q extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function Ge(e){return e.aborted&&e.reason instanceof Q}async function M(e,t,n,i,o){await S("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:i,...o?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:o.invalidatedAcpSessionId}:{}})}function z(e){return new Promise(t=>setTimeout(t,e))}function Ke(e){return Math.min(1e4,500*2**Math.min(5,Math.max(0,e-1)))+Math.floor(Math.random()*250)}function He(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}function U(e,t){return!e||typeof e!="object"||Array.isArray(e)?null:e[t]??null}export{qe as serverFinalizedJobStatus,ht as startJobWorkerLoop};
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- const o="0.1.64";export{o as AICA_VERSION};
1
+ const o="0.1.65";export{o as AICA_VERSION};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuandc/aica",
3
- "version": "0.1.64",
3
+ "version": "0.1.65",
4
4
  "description": "AICA remote Agent worker",
5
5
  "author": "yuandc",
6
6
  "type": "module",