@yuandc/aica 0.1.26 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{spawn as i}from"node:child_process";import{acpChildEnvironment as s}from"../../core/process-identity.js";function l(e,t,n){const r=i(e,t,{cwd:n,stdio:["pipe","pipe","pipe"],env:s(e,t)});return{command:e,args:t,child:r}}async function a(e){e.exitCode!==null||e.signalCode!==null||(e.stdin.end(),!await o(e,2e3)&&(e.kill("SIGTERM"),!await o(e,1e3)&&(e.kill("SIGKILL"),await o(e,1e3))))}function o(e,t){return e.exitCode!==null||e.signalCode!==null?Promise.resolve(!0):new Promise(n=>{const r=setTimeout(()=>n(!1),t);r.unref(),e.once("close",()=>{clearTimeout(r),n(!0)})})}export{a as closeAcpProcess,l as startAcpProcess};
1
+ import{spawn as s}from"node:child_process";import{acpChildEnvironment as u}from"../../core/process-identity.js";function a(e,t,n,r={}){const i=s(e,t,{cwd:n,stdio:["pipe","pipe","pipe"],env:u(e,t,r),windowsHide:!0});return{command:e,args:t,child:i}}async function f(e){e.exitCode!==null||e.signalCode!==null||(e.stdin.end(),!await o(e,2e3)&&(e.kill("SIGTERM"),!await o(e,1e3)&&(e.kill("SIGKILL"),await o(e,1e3))))}function o(e,t){return e.exitCode!==null||e.signalCode!==null?Promise.resolve(!0):new Promise(n=>{const r=setTimeout(()=>n(!1),t);r.unref(),e.once("close",()=>{clearTimeout(r),n(!0)})})}export{f as closeAcpProcess,a as startAcpProcess};
@@ -1 +1 @@
1
- import c from"node:fs";import{randomUUID as m}from"node:crypto";import{AcpClient as p}from"./acp-client.js";import{closeAcpProcess as h,startAcpProcess as v}from"./acp-process.js";const f=300*1e3,I=60*1e3,w=4;class A{runtimes=new Map;idleTtlMs;sweepIntervalMs;maxIdleRuntimes;sweepTimer=null;disposeAllPromise=null;constructor(e={}){this.idleTtlMs=Math.max(1e3,e.idleTtlMs??E()),this.sweepIntervalMs=Math.max(1e3,e.sweepIntervalMs??R()),this.maxIdleRuntimes=Math.max(0,e.maxIdleRuntimes??S())}async acquire(e){this.disposeAllPromise&&await this.disposeAllPromise;const i=c.realpathSync(e.cwd);this.ensureSweep();const s=e.runtimeIdentity||"default",r=e.providerSessionId?d(e.providerId,i,e.providerSessionId,s):null,n=r?this.runtimes.get(r):null;n&&!this.isRuntimeAlive(n)&&await this.disposeEntry(n).catch(()=>{});const o=r?this.runtimes.get(r):null;if(o)return this.activateEntry(o,e),this.createLease(o);const u=e.providerSessionId||`pending:${m()}`,l=d(e.providerId,i,u,s),a=this.createEntry({...e,cwd:i,key:l,providerSessionId:e.providerSessionId??null,runtimeIdentity:s});return this.runtimes.set(l,a),this.createLease(a)}disposeAll(){if(this.disposeAllPromise)return this.disposeAllPromise;const e=this.performDisposeAll();return this.disposeAllPromise=e,e.finally(()=>{this.disposeAllPromise===e&&(this.disposeAllPromise=null)}),e}async performDisposeAll(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null);const e=[...this.runtimes.values()];await Promise.all(e.map(i=>this.disposeEntry(i).catch(()=>{})))}async sweepIdle(e=Date.now()){const i=[...this.runtimes.values()];for(const s of i)s.inUse||s.disposed||e-s.lastUsedAtMs<this.idleTtlMs||await this.disposeEntry(s).catch(()=>{});await this.trimIdleRuntimes()}async retireProviderRuntimes(e){const i=[...this.runtimes.values()].filter(s=>s.providerId===e&&!s.disposed);for(const s of i)s.retired=!0;return await Promise.all(i.filter(s=>!s.inUse).map(s=>this.disposeEntry(s).catch(()=>{}))),i.length}stats(){const e=[...this.runtimes.values()].filter(i=>!i.disposed);return{total:e.length,inUse:e.filter(i=>i.inUse).length,idle:e.filter(i=>!i.inUse).length,pids:e.map(i=>i.started.child.pid).filter(i=>Number.isInteger(i))}}createEntry(e){const i=v(e.command,e.args,e.cwd),s={key:e.key,providerId:e.providerId,cwd:e.cwd,providerSessionId:e.providerSessionId,runtimeIdentity:e.runtimeIdentity,started:i,client:null,initialized:!1,sessionEstablished:!1,runtimeConfigured:!1,inUse:!0,retired:!1,disposed:!1,disposePromise:null,lastUsedAtMs:Date.now(),sessionResponse:null,activeHandlers:{onSessionUpdate:e.onSessionUpdate,onClientRequest:e.onClientRequest}};return s.client=new p(i.child,(r,n)=>s.activeHandlers?.onSessionUpdate(r,n),async(r,n,o)=>{if(!s.activeHandlers?.onClientRequest)throw new Error(`Unsupported ACP client request: ${r}`);return s.activeHandlers.onClientRequest(r,n,o)}),i.child.once("close",()=>{this.runtimes.get(s.key)===s&&this.runtimes.delete(s.key),s.disposed=!0,s.inUse=!1,s.activeHandlers=null}),s}activateEntry(e,i){if(e.inUse)throw new Error(`ACP runtime is already in use: provider=${e.providerId} session=${e.providerSessionId??"unknown"}`);e.inUse=!0,e.activeHandlers={onSessionUpdate:i.onSessionUpdate,onClientRequest:i.onClientRequest}}createLease(e){let i=!1;return{get client(){return e.client},get started(){return e.started},get cwd(){return e.cwd},get providerSessionId(){return e.providerSessionId},get sessionResponse(){return e.sessionResponse},get initialized(){return e.initialized},get sessionEstablished(){return e.sessionEstablished},get runtimeConfigured(){return e.runtimeConfigured},markInitialized:()=>{e.initialized=!0},markRuntimeConfigured:()=>{e.runtimeConfigured=!0},setProviderSession:s=>{e.providerSessionId=s.providerSessionId,e.sessionEstablished=!0,"sessionResponse"in s&&(e.sessionResponse=s.sessionResponse??null),this.rekeyEntry(e,d(e.providerId,e.cwd,s.providerSessionId,e.runtimeIdentity))},setSessionResponse:s=>{e.sessionResponse=s},release:()=>{i||(i=!0,e.inUse=!1,e.activeHandlers=null,e.lastUsedAtMs=Date.now(),e.retired?this.disposeEntry(e):this.trimIdleRuntimes())},dispose:async()=>{i=!0,await this.disposeEntry(e)}}}rekeyEntry(e,i){if(e.key===i)return;const s=this.runtimes.get(i);if(s&&s!==e){if(s.inUse)throw new Error(`ACP runtime key is already active: provider=${s.providerId} session=${s.providerSessionId??"unknown"}`);this.disposeEntry(s)}this.runtimes.get(e.key)===e&&this.runtimes.delete(e.key),e.key=i,this.runtimes.set(i,e)}async disposeEntry(e){if(e.disposePromise)return e.disposePromise;if(e.disposed)return;const i=this.performDisposeEntry(e);return e.disposePromise=i,i}async performDisposeEntry(e){e.disposed=!0,e.inUse=!1,e.activeHandlers=null,this.runtimes.get(e.key)===e&&this.runtimes.delete(e.key),e.providerSessionId&&await e.client.closeSession(e.providerSessionId,5e3).catch(()=>{}),await h(e.started.child)}async trimIdleRuntimes(){const e=[...this.runtimes.values()].filter(s=>!s.inUse&&!s.disposed).sort((s,r)=>s.lastUsedAtMs-r.lastUsedAtMs),i=Math.max(0,e.length-this.maxIdleRuntimes);for(const s of e.slice(0,i))await this.disposeEntry(s).catch(()=>{})}isRuntimeAlive(e){return!e.disposed&&e.started.child.exitCode===null&&e.started.child.signalCode===null}ensureSweep(){this.sweepTimer||(this.sweepTimer=setInterval(()=>{this.sweepIdle()},this.sweepIntervalMs),this.sweepTimer.unref())}}const _=new A;function d(t,e,i,s){return`${t}\0${e}\0${i}\0${s}`}function E(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_IDLE_TTL_MS??"",10);return Number.isInteger(t)&&t>=1e3?t:f}function R(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_SWEEP_INTERVAL_MS??"",10);return Number.isInteger(t)&&t>=1e3?t:I}function S(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_MAX_IDLE??"",10);return Number.isInteger(t)&&t>=0?Math.min(t,32):w}export{A as AcpRuntimePool,_ as defaultAcpRuntimePool};
1
+ import c from"node:fs";import{randomUUID as m}from"node:crypto";import{AcpClient as p}from"./acp-client.js";import{closeAcpProcess as h,startAcpProcess as v}from"./acp-process.js";const f=300*1e3,I=60*1e3,w=4;class A{runtimes=new Map;idleTtlMs;sweepIntervalMs;maxIdleRuntimes;sweepTimer=null;disposeAllPromise=null;constructor(e={}){this.idleTtlMs=Math.max(1e3,e.idleTtlMs??E()),this.sweepIntervalMs=Math.max(1e3,e.sweepIntervalMs??R()),this.maxIdleRuntimes=Math.max(0,e.maxIdleRuntimes??S())}async acquire(e){this.disposeAllPromise&&await this.disposeAllPromise;const i=c.realpathSync(e.cwd);this.ensureSweep();const s=e.runtimeIdentity||"default",r=e.providerSessionId?d(e.providerId,i,e.providerSessionId,s):null,n=r?this.runtimes.get(r):null;n&&!this.isRuntimeAlive(n)&&await this.disposeEntry(n).catch(()=>{});const o=r?this.runtimes.get(r):null;if(o)return this.activateEntry(o,e),this.createLease(o);const u=e.providerSessionId||`pending:${m()}`,l=d(e.providerId,i,u,s),a=this.createEntry({...e,cwd:i,key:l,providerSessionId:e.providerSessionId??null,runtimeIdentity:s});return this.runtimes.set(l,a),this.createLease(a)}disposeAll(){if(this.disposeAllPromise)return this.disposeAllPromise;const e=this.performDisposeAll();return this.disposeAllPromise=e,e.finally(()=>{this.disposeAllPromise===e&&(this.disposeAllPromise=null)}),e}async performDisposeAll(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null);const e=[...this.runtimes.values()];await Promise.all(e.map(i=>this.disposeEntry(i).catch(()=>{})))}async sweepIdle(e=Date.now()){const i=[...this.runtimes.values()];for(const s of i)s.inUse||s.disposed||e-s.lastUsedAtMs<this.idleTtlMs||await this.disposeEntry(s).catch(()=>{});await this.trimIdleRuntimes()}async retireProviderRuntimes(e){const i=[...this.runtimes.values()].filter(s=>s.providerId===e&&!s.disposed);for(const s of i)s.retired=!0;return await Promise.all(i.filter(s=>!s.inUse).map(s=>this.disposeEntry(s).catch(()=>{}))),i.length}stats(){const e=[...this.runtimes.values()].filter(i=>!i.disposed);return{total:e.length,inUse:e.filter(i=>i.inUse).length,idle:e.filter(i=>!i.inUse).length,pids:e.map(i=>i.started.child.pid).filter(i=>Number.isInteger(i))}}createEntry(e){const i=v(e.command,e.args,e.cwd,e.env),s={key:e.key,providerId:e.providerId,cwd:e.cwd,providerSessionId:e.providerSessionId,runtimeIdentity:e.runtimeIdentity,started:i,client:null,initialized:!1,sessionEstablished:!1,runtimeConfigured:!1,inUse:!0,retired:!1,disposed:!1,disposePromise:null,lastUsedAtMs:Date.now(),sessionResponse:null,activeHandlers:{onSessionUpdate:e.onSessionUpdate,onClientRequest:e.onClientRequest}};return s.client=new p(i.child,(r,n)=>s.activeHandlers?.onSessionUpdate(r,n),async(r,n,o)=>{if(!s.activeHandlers?.onClientRequest)throw new Error(`Unsupported ACP client request: ${r}`);return s.activeHandlers.onClientRequest(r,n,o)}),i.child.once("close",()=>{this.runtimes.get(s.key)===s&&this.runtimes.delete(s.key),s.disposed=!0,s.inUse=!1,s.activeHandlers=null}),s}activateEntry(e,i){if(e.inUse)throw new Error(`ACP runtime is already in use: provider=${e.providerId} session=${e.providerSessionId??"unknown"}`);e.inUse=!0,e.activeHandlers={onSessionUpdate:i.onSessionUpdate,onClientRequest:i.onClientRequest}}createLease(e){let i=!1;return{get client(){return e.client},get started(){return e.started},get cwd(){return e.cwd},get providerSessionId(){return e.providerSessionId},get sessionResponse(){return e.sessionResponse},get initialized(){return e.initialized},get sessionEstablished(){return e.sessionEstablished},get runtimeConfigured(){return e.runtimeConfigured},markInitialized:()=>{e.initialized=!0},markRuntimeConfigured:()=>{e.runtimeConfigured=!0},setProviderSession:s=>{e.providerSessionId=s.providerSessionId,e.sessionEstablished=!0,"sessionResponse"in s&&(e.sessionResponse=s.sessionResponse??null),this.rekeyEntry(e,d(e.providerId,e.cwd,s.providerSessionId,e.runtimeIdentity))},setSessionResponse:s=>{e.sessionResponse=s},release:()=>{i||(i=!0,e.inUse=!1,e.activeHandlers=null,e.lastUsedAtMs=Date.now(),e.retired?this.disposeEntry(e):this.trimIdleRuntimes())},dispose:async()=>{i=!0,await this.disposeEntry(e)}}}rekeyEntry(e,i){if(e.key===i)return;const s=this.runtimes.get(i);if(s&&s!==e){if(s.inUse)throw new Error(`ACP runtime key is already active: provider=${s.providerId} session=${s.providerSessionId??"unknown"}`);this.disposeEntry(s)}this.runtimes.get(e.key)===e&&this.runtimes.delete(e.key),e.key=i,this.runtimes.set(i,e)}async disposeEntry(e){if(e.disposePromise)return e.disposePromise;if(e.disposed)return;const i=this.performDisposeEntry(e);return e.disposePromise=i,i}async performDisposeEntry(e){e.disposed=!0,e.inUse=!1,e.activeHandlers=null,this.runtimes.get(e.key)===e&&this.runtimes.delete(e.key),e.providerSessionId&&await e.client.closeSession(e.providerSessionId,5e3).catch(()=>{}),await h(e.started.child)}async trimIdleRuntimes(){const e=[...this.runtimes.values()].filter(s=>!s.inUse&&!s.disposed).sort((s,r)=>s.lastUsedAtMs-r.lastUsedAtMs),i=Math.max(0,e.length-this.maxIdleRuntimes);for(const s of e.slice(0,i))await this.disposeEntry(s).catch(()=>{})}isRuntimeAlive(e){return!e.disposed&&e.started.child.exitCode===null&&e.started.child.signalCode===null}ensureSweep(){this.sweepTimer||(this.sweepTimer=setInterval(()=>{this.sweepIdle()},this.sweepIntervalMs),this.sweepTimer.unref())}}const _=new A;function d(t,e,i,s){return`${t}\0${e}\0${i}\0${s}`}function E(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_IDLE_TTL_MS??"",10);return Number.isInteger(t)&&t>=1e3?t:f}function R(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_SWEEP_INTERVAL_MS??"",10);return Number.isInteger(t)&&t>=1e3?t:I}function S(){const t=Number.parseInt(process.env.ACA_ACP_RUNTIME_MAX_IDLE??"",10);return Number.isInteger(t)&&t>=0?Math.min(t,32):w}export{A as AcpRuntimePool,_ as defaultAcpRuntimePool};
@@ -1 +1 @@
1
- import{spawn as s}from"node:child_process";import{acpChildEnvironment as t}from"../../../core/process-identity.js";import{resolveCodexPath as n}from"./codex-path.js";import{resolveCodexPath as v}from"./codex-path.js";function m(e,o=null){const r=n(),a=d(o),p=s(r,a,{cwd:e,env:t(r,a,{CODEX_PATH:r,...o?{ACA_CODEX_PROVIDER_KEY:o.apiKey}:{}}),stdio:["pipe","pipe","pipe"]});return p.stdout.setEncoding("utf8"),p.stderr.setEncoding("utf8"),{command:r,args:a,child:p}}function d(e){return e?["-c",'model_provider="aca_managed"',"-c",'model_providers.aca_managed.name="ACA Managed"',"-c",`model_providers.aca_managed.base_url=${JSON.stringify(e.baseUrl)}`,"-c",'model_providers.aca_managed.env_key="ACA_CODEX_PROVIDER_KEY"',"-c",'model_providers.aca_managed.wire_api="responses"',"-c","model_providers.aca_managed.requires_openai_auth=false","app-server","--stdio"]:["app-server","--stdio"]}export{d as codexAppServerArgs,v as resolveCodexPath,m as startCodexAppServer};
1
+ import{spawn as s}from"node:child_process";import{acpChildEnvironment as t}from"../../../core/process-identity.js";import{resolveCodexPath as n}from"./codex-path.js";import{resolveCodexPath as v}from"./codex-path.js";function m(e,o=null){const r=n(),a=d(o),p=s(r,a,{cwd:e,env:t(r,a,{CODEX_PATH:r,...o?{ACA_CODEX_PROVIDER_KEY:o.apiKey}:{}}),stdio:["pipe","pipe","pipe"],windowsHide:!0});return p.stdout.setEncoding("utf8"),p.stderr.setEncoding("utf8"),{command:r,args:a,child:p}}function d(e){return e?["-c",'model_provider="aca_managed"',"-c",'model_providers.aca_managed.name="ACA Managed"',"-c",`model_providers.aca_managed.base_url=${JSON.stringify(e.baseUrl)}`,"-c",'model_providers.aca_managed.env_key="ACA_CODEX_PROVIDER_KEY"',"-c",'model_providers.aca_managed.wire_api="responses"',"-c","model_providers.aca_managed.requires_openai_auth=false","app-server","--stdio"]:["app-server","--stdio"]}export{d as codexAppServerArgs,v as resolveCodexPath,m as startCodexAppServer};
@@ -1 +1 @@
1
- import d from"node:fs";import{runAbortableRequest as w}from"../../client/abortable-request.js";import{BoundedItemBuffer as x}from"../../client/bounded-buffer.js";import{promptBlocksToAcpContent as P}from"../../client/acp-content.js";import{eventFromAcpSessionUpdate as k,statusFromAcpSessionUpdate as R}from"../../client/acp-events.js";import{defaultAcpRuntimePool as S}from"../../client/acp-runtime-pool.js";import{resolveCodexAcpLaunch as g}from"./launch.js";import{CodexPromptPerformanceTracker as M,readCodexContextMaintenancePolicy as T}from"./context-maintenance.js";import{ensureCodexProviderRuntimeConfig as y}from"../../../core/codex-provider-runtime-config.js";const q={sessionResume:!0,imageInput:!0,fileAttachment:!0,filesystem:!0,terminal:!0,permissionRequest:!0,configOptions:!0,usage:!0,contextUsage:!0,contextCompaction:!0,plan:!0,diff:!0};class X{id="codex-acp";name="Codex ACP";capabilities=q;async createSession(e){return new _(e.cwd,e.providerSessionId??null,e.timeoutMs)}async forkSession(e){if(!d.existsSync(e.cwd)||!d.statSync(e.cwd).isDirectory())throw new Error(`Project root does not exist or is not a directory: ${e.cwd}`);const s=e.sourceProviderSessionId.trim(),t=await y(),r=await S.acquire({providerId:"codex-acp",cwd:e.cwd,providerSessionId:s,runtimeIdentity:t?.runtimeIdentity||"default",...g(),onSessionUpdate:()=>{}});try{await b(r,t),r.initialized||(await r.client.initialize(6e4),r.markInitialized()),e.onStatus?.({phase:"forking",label:"正在分叉 Codex 会话",detail:s,updateType:"session/fork",atMs:Date.now()});const n=await r.client.forkSession({sessionId:s,cwd:e.cwd,config:C(e)},e.timeoutMs??12e4),i=p(n);if(!i)throw new Error("Codex ACP session/fork did not return sessionId");return{providerSessionId:i}}finally{r.release()}}}class _{cwd;providerSessionId;defaultTimeoutMs;lease=null;sessionResponse=null;statusCallback=null;updateCallback=null;permissionCallback=null;userInputCallback=null;abortHandler=null;statusTimeline=[];updates=new x(16);content="";performanceTracker=null;constructor(e,s,t=D()){this.cwd=e,this.providerSessionId=s,this.defaultTimeoutMs=t}async sendPrompt(e){if(!d.existsSync(e.cwd)||!d.statSync(e.cwd).isDirectory())throw new Error(`Project root does not exist or is not a directory: ${e.cwd}`);const s=Date.now();this.statusCallback=e.onStatus??null,this.updateCallback=e.onUpdate??null,this.permissionCallback=e.onPermissionRequest??null,this.userInputCallback=e.onUserInputRequest??null,this.attachAbortSignal(e.signal),this.content="",this.updates.clear(),this.statusTimeline.length=0,this.performanceTracker=new M(s);let t=null,r=null,n=this.providerSessionId,i,c=null;try{this.emitStatus("initializing","Codex ACP 启动中","codex-acp");const a=await this.acquireRuntime(e.cwd);this.lease=a,t=a.client,r=a.started,await this.initialize(a),n=await this.establishSession(a,e),await this.applyConfig(t,n,e),this.emitStatus("thinking","思考中"),c=this.startSlowStatusTimer(),i=await this.promptWithAbort(t,{sessionId:n,prompt:P(e.prompt,e.promptBlocks),timeoutMs:e.timeoutMs??this.defaultTimeoutMs,signal:e.signal});const u=v(i);u&&u!==n&&(n=u,this.providerSessionId=u,a.setProviderSession({providerSessionId:u,sessionResponse:this.sessionResponse}))}finally{c&&clearInterval(c),this.abortHandler&&e.signal&&(e.signal.removeEventListener("abort",this.abortHandler),this.abortHandler=null),this.releaseLease()}return this.emitStatus("completed","已完成"),{providerSessionId:n,cliType:"builtin",agentType:"codex",effectiveConfig:{model:e.model??null,mode:e.mode??null,configOptionValues:e.configOptionValues??null,command:r.command,processPid:r.child.pid??null,provider:"codex-acp",protocol:"acp"},command:r.command,args:r.args,cwd:e.cwd,exitCode:r.child.exitCode,signal:r.child.signalCode,durationMs:Date.now()-s,stdout:t.rpc.stdout,stderr:t.rpc.stderr,content:this.content.trim(),updateCount:this.updates.count(),updates:this.updates.snapshot(),statusTimeline:[...this.statusTimeline],performance:this.performanceTracker?.snapshot(),contextUsage:z(i),promptResponse:i}}async compactContext(e){if(!d.existsSync(e.cwd)||!d.statSync(e.cwd).isDirectory())throw new Error(`Project root does not exist or is not a directory: ${e.cwd}`);const s=Date.now();this.statusCallback=e.onStatus??null,this.updateCallback=e.onUpdate??null,this.attachAbortSignal(e.signal);let t=this.providerSessionId,r;try{this.emitStatus("initializing","Codex ACP 启动中","codex-acp");const i=await this.acquireRuntime(e.cwd);this.lease=i,await this.initialize(i),t=await this.establishSession(i,e),await this.applyConfig(i.client,t,e);const c=j(e.timeoutMs);r=await this.compactWithAbort(i.client,{sessionId:t,allowRollover:e.allowRollover===!0,timeoutMs:c,signal:e.signal});const a=v(r);a&&a!==t&&(t=a,this.providerSessionId=a,i.setProviderSession({providerSessionId:a,sessionResponse:this.sessionResponse}))}catch(i){if(this.lease&&E(i,e.signal)){const c=this.lease;this.lease=null,await c.dispose().catch(()=>{})}throw this.emitStatus("failed","上下文压缩失败",i instanceof Error?i.message:String(i)),i}finally{this.abortHandler&&e.signal&&(e.signal.removeEventListener("abort",this.abortHandler),this.abortHandler=null),this.releaseLease()}const n=l(r)??{};return{providerSessionId:t||this.providerSessionId||"",contextUsage:A(n.contextUsage),maintenance:l(n.maintenance)??{},durationMs:Date.now()-s}}async cancel(){this.providerSessionId&&this.lease?.client.cancel(this.providerSessionId)}async close(){this.statusCallback=null,this.updateCallback=null,this.permissionCallback=null,this.userInputCallback=null,this.abortHandler&&(this.abortHandler=null),this.releaseLease()}async acquireRuntime(e){const s=g(),t=await y(),r=await S.acquire({providerId:"codex-acp",cwd:e,providerSessionId:this.providerSessionId,runtimeIdentity:t?.runtimeIdentity||"default",command:s.command,args:s.args,onSessionUpdate:n=>this.handleSessionUpdate(n),onClientRequest:async(n,i)=>{if(n==="session/request_permission")return this.requestPermission(i);if(n==="elicitation/create"||n==="session/request_user_input")return this.requestUserInput(i);throw new Error(`Unsupported Codex ACP client request: ${n}`)}});try{return await b(r,t),r}catch(n){throw await r.dispose().catch(()=>{}),n}}async initialize(e){e.initialized||(await e.client.initialize(6e4),e.markInitialized())}async establishSession(e,s){const t=e.client,r=C(s);if(e.providerSessionId&&e.sessionEstablished)return this.providerSessionId=e.providerSessionId,this.sessionResponse=e.sessionResponse,e.providerSessionId;if(this.providerSessionId)try{this.emitStatus("resuming","Codex ACP 会话恢复中","session/load");const c=await t.loadSession({sessionId:this.providerSessionId,cwd:s.cwd,config:r},6e4);this.sessionResponse=l(c);const a=p(c)||this.providerSessionId;return this.providerSessionId=a,e.setProviderSession({providerSessionId:a,sessionResponse:this.sessionResponse}),a}catch(c){if(s.requireSessionResume){const a=c instanceof Error?c.message:String(c);throw new Error(`无法恢复导入的 Codex ACP Session ${this.providerSessionId}:${a}`)}this.providerSessionId=null}this.emitStatus("acp","Codex ACP 会话创建中","session/new");const n=await t.newSession({cwd:s.cwd,config:r},12e4);this.sessionResponse=l(n);const i=p(n);if(!i)throw new Error("Codex ACP session/new did not return sessionId");return this.providerSessionId=i,e.setProviderSession({providerSessionId:i,sessionResponse:this.sessionResponse}),i}async applyConfig(e,s,t){t.model&&await e.setSessionConfigOption({sessionId:s,configId:"model",value:t.model},6e4).catch(()=>{});const r=f(t.mode);r&&await e.setSessionConfigOption({sessionId:s,configId:"approvalPolicy",value:r},6e4).catch(()=>{});const n=h(t.mode);n&&await e.setSessionConfigOption({sessionId:s,configId:"sandbox",value:n},6e4).catch(()=>{});for(const[i,c]of Object.entries(t.configOptionValues??{}))["model","mode","sandbox","approvalPolicy"].includes(i)||await e.setSessionConfigOption({sessionId:s,configId:i,value:c},6e4).catch(()=>{})}handleSessionUpdate(e){this.performanceTracker?.recordEvent(),this.updates.push({method:"session/update",params:e});const s=k(e);s&&(s.type==="agent_message_chunk"&&(this.content+=s.text??""),this.updateCallback?.(s));const t=R(e);t&&this.emitStatus(t.phase,t.label,t.detail,t.updateType)}startSlowStatusTimer(){const e=T().slowEventThresholdMs,s=setInterval(()=>{const t=this.performanceTracker,r=Date.now();if(!t?.shouldReportSlow(r,e))return;const n=t.snapshot(r),i=Math.max(1,Math.round(n.silentForMs/6e4));this.emitStatus("thinking","模型长时间处理中",`已 ${i} 分钟没有收到新的 ACP 事件`,"aca.performance.slow")},Math.min(3e4,Math.max(1e3,Math.floor(e/4))));return s.unref(),s}attachAbortSignal(e){if(e){if(this.abortHandler=()=>{this.cancel()},e.aborted)throw new Error("Codex ACP prompt cancelled");e.addEventListener("abort",this.abortHandler,{once:!0})}}async promptWithAbort(e,s){return w({signal:s.signal,cancelledMessage:"Codex ACP prompt cancelled",startRequest:()=>e.prompt({sessionId:s.sessionId,prompt:s.prompt},s.timeoutMs),onAbort:async()=>{e.cancel(s.sessionId),await this.lease?.dispose()}})}async compactWithAbort(e,s){return w({signal:s.signal,cancelledMessage:"Codex ACP 上下文压缩已取消",startRequest:()=>e.compactSession({sessionId:s.sessionId,allowRollover:s.allowRollover,timeoutMs:s.timeoutMs},s.timeoutMs),onAbort:async()=>{e.cancel(s.sessionId),await this.lease?.dispose()}})}async requestPermission(e){if(!this.permissionCallback)return{outcome:{outcome:"cancelled"}};const s=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=s.request&&typeof s.request=="object"&&!Array.isArray(s.request)?s.request:s,n=(Array.isArray(t.options)?t.options:[]).map((i,c)=>{const a=i&&typeof i=="object"&&!Array.isArray(i)?i:{};return{optionId:String(a.optionId??a.id??c),kind:String(a.kind??"choice"),...typeof a.label=="string"?{label:a.label}:{}}});return this.emitStatus("requestPermission","等待授权",U(t),"session/request_permission"),this.permissionCallback({requestId:String(t.requestId??t.id??`codex-acp-permission-${Date.now()}`),params:e,options:n})}async requestUserInput(e){if(!this.userInputCallback)return{outcome:{outcome:"cancelled"}};const s=e&&typeof e=="object"&&!Array.isArray(e)?e:{},t=s.request&&typeof s.request=="object"&&!Array.isArray(s.request)?s.request:s;return this.emitStatus("requestPermission","等待输入",O(t),"elicitation/create"),this.userInputCallback({requestId:String(t.requestId??t.id??`codex-acp-input-${Date.now()}`),prompt:String(t.prompt??t.message??t.question??""),params:e,...typeof t.defaultValue=="string"?{defaultValue:t.defaultValue}:{}})}emitStatus(e,s,t,r){const n={phase:e,label:s,...t?{detail:t}:{},...r?{updateType:r}:{},atMs:Date.now()},i=this.statusTimeline[this.statusTimeline.length-1];i&&i.phase===n.phase&&i.label===n.label&&i.detail===n.detail&&i.updateType===n.updateType||(this.statusTimeline.push(n),this.statusCallback?.(n))}releaseLease(){this.lease?.release(),this.lease=null}}async function b(o,e){o.runtimeConfigured||(await o.client.configureRuntime({codexProvider:e?{baseUrl:e.baseUrl,apiKey:e.apiKey,revision:e.revision}:null}),o.markRuntimeConfigured())}function E(o,e){if(e?.aborted)return!0;const s=o instanceof Error?o.message:String(o||"");return/compact.*timed out|request timed out.*compact|adapter closed|aborted|cancelled/i.test(s)}function p(o){if(!o||typeof o!="object"||Array.isArray(o))return null;const e=o.sessionId;return typeof e=="string"&&e.trim()?e.trim():null}function l(o){return o&&typeof o=="object"&&!Array.isArray(o)?o:null}function h(o){const e=String(o||"").trim();return e?e==="read-only"?"read-only":["agent-full-access","full-access","bypassPermissions","danger-full-access"].includes(e)?"danger-full-access":"workspace-write":null}function f(o){const e=String(o||"").trim();return e?["agent-full-access","full-access","bypassPermissions","danger-full-access"].includes(e)?"never":e==="acceptEdits"||e==="accept-edits"?"on-failure":"on-request":null}function C(o){return{...o.model?{model:o.model}:{},...f(o.mode)?{approvalPolicy:f(o.mode)}:{},...h(o.mode)?{sandbox:h(o.mode)}:{},...o.configOptionValues??{}}}function U(o){const e=o.params&&typeof o.params=="object"&&!Array.isArray(o.params)?o.params:o;for(const s of["command","cwd","reason","grantRoot","method"]){const t=e[s];if(typeof t=="string"&&t.trim())return I(t)}}function O(o){for(const e of["prompt","message","question","title"]){const s=o[e];if(typeof s=="string"&&s.trim())return I(s)}}function I(o){const e=o.replace(/\s+/g," ").trim();return e.length>120?`${e.slice(0,117)}...`:e}function D(){const o=Number.parseInt(process.env.ACA_CODEX_ACP_PROMPT_TIMEOUT_MS??process.env.ACA_ACP_PROMPT_TIMEOUT_MS??"",10);return Number.isInteger(o)&&o>=3e4?o:360*60*1e3}function j(o){const e=Number.isInteger(o)?Number(o):Number.parseInt(process.env.ACA_CODEX_ACP_COMPACT_TIMEOUT_MS??"",10);return Number.isFinite(e)&&e>=3e4?Math.min(e,6e5):12e4}function v(o){if(!o||typeof o!="object"||Array.isArray(o))return null;const e=o;return typeof e.sessionId=="string"&&e.sessionId.trim()?e.sessionId:null}function z(o){const e=l(o),s=l(e?._meta);return A(s?.acaContextUsage)}function A(o){const e=l(o);if(!e)return null;const s=m(e.contextWindow),t=m(e.usedTokens),r=m(e.inputTokens),n=String(e.state||"unknown"),i=["active","compacted","new_thread"].includes(n)?n:"unknown";return{usedTokens:t,inputTokens:r,contextWindow:s,ratio:s>0?Math.max(0,m(e.ratio)||t/s):0,observedAtMs:m(e.observedAtMs)||Date.now(),state:i,maintenanceAction:typeof e.maintenanceAction=="string"?e.maintenanceAction:null}}function m(o){return typeof o=="number"&&Number.isFinite(o)&&o>=0?o:0}export{X as CodexAcpProvider,q as codexAcpCapabilities};
1
+ import d from"node:fs";import{runAbortableRequest as w}from"../../client/abortable-request.js";import{BoundedItemBuffer as P}from"../../client/bounded-buffer.js";import{promptBlocksToAcpContent as k}from"../../client/acp-content.js";import{eventFromAcpSessionUpdate as R,statusFromAcpSessionUpdate as M}from"../../client/acp-events.js";import{defaultAcpRuntimePool as S}from"../../client/acp-runtime-pool.js";import{resolveCodexAcpLaunch as y}from"./launch.js";import{CodexPromptPerformanceTracker as T,readCodexContextMaintenancePolicy as _}from"./context-maintenance.js";import{ensureCodexProviderRuntimeConfig as g}from"../../../core/codex-provider-runtime-config.js";const q={sessionResume:!0,imageInput:!0,fileAttachment:!0,filesystem:!0,terminal:!0,permissionRequest:!0,configOptions:!0,usage:!0,contextUsage:!0,contextCompaction:!0,plan:!0,diff:!0};class Y{id="codex-acp";name="Codex ACP";capabilities=q;async createSession(e){return new E(e.cwd,e.providerSessionId??null,e.timeoutMs)}async forkSession(e){if(!d.existsSync(e.cwd)||!d.statSync(e.cwd).isDirectory())throw new Error(`Project root does not exist or is not a directory: ${e.cwd}`);const s=e.sourceProviderSessionId.trim(),o=await g(),r=await S.acquire({providerId:"codex-acp",cwd:e.cwd,providerSessionId:s,runtimeIdentity:o?.runtimeIdentity||"default",env:b(o),...y(),onSessionUpdate:()=>{}});try{await I(r,o),r.initialized||(await r.client.initialize(6e4),r.markInitialized()),e.onStatus?.({phase:"forking",label:"正在分叉 Codex 会话",detail:s,updateType:"session/fork",atMs:Date.now()});const n=await r.client.forkSession({sessionId:s,cwd:e.cwd,config:C(e)},e.timeoutMs??12e4),i=p(n);if(!i)throw new Error("Codex ACP session/fork did not return sessionId");return{providerSessionId:i}}finally{r.release()}}}class E{cwd;providerSessionId;defaultTimeoutMs;lease=null;sessionResponse=null;statusCallback=null;updateCallback=null;permissionCallback=null;userInputCallback=null;abortHandler=null;statusTimeline=[];updates=new P(16);content="";performanceTracker=null;constructor(e,s,o=j()){this.cwd=e,this.providerSessionId=s,this.defaultTimeoutMs=o}async sendPrompt(e){if(!d.existsSync(e.cwd)||!d.statSync(e.cwd).isDirectory())throw new Error(`Project root does not exist or is not a directory: ${e.cwd}`);const s=Date.now();this.statusCallback=e.onStatus??null,this.updateCallback=e.onUpdate??null,this.permissionCallback=e.onPermissionRequest??null,this.userInputCallback=e.onUserInputRequest??null,this.attachAbortSignal(e.signal),this.content="",this.updates.clear(),this.statusTimeline.length=0,this.performanceTracker=new T(s);let o=null,r=null,n=this.providerSessionId,i,c=null;try{this.emitStatus("initializing","Codex ACP 启动中","codex-acp");const a=await this.acquireRuntime(e.cwd);this.lease=a,o=a.client,r=a.started,await this.initialize(a),n=await this.establishSession(a,e),await this.applyConfig(o,n,e),this.emitStatus("thinking","思考中"),c=this.startSlowStatusTimer(),i=await this.promptWithAbort(o,{sessionId:n,prompt:k(e.prompt,e.promptBlocks),timeoutMs:e.timeoutMs??this.defaultTimeoutMs,signal:e.signal});const u=A(i);u&&u!==n&&(n=u,this.providerSessionId=u,a.setProviderSession({providerSessionId:u,sessionResponse:this.sessionResponse}))}finally{c&&clearInterval(c),this.abortHandler&&e.signal&&(e.signal.removeEventListener("abort",this.abortHandler),this.abortHandler=null),this.releaseLease()}return this.emitStatus("completed","已完成"),{providerSessionId:n,cliType:"builtin",agentType:"codex",effectiveConfig:{model:e.model??null,mode:e.mode??null,configOptionValues:e.configOptionValues??null,command:r.command,processPid:r.child.pid??null,provider:"codex-acp",protocol:"acp"},command:r.command,args:r.args,cwd:e.cwd,exitCode:r.child.exitCode,signal:r.child.signalCode,durationMs:Date.now()-s,stdout:o.rpc.stdout,stderr:o.rpc.stderr,content:this.content.trim(),updateCount:this.updates.count(),updates:this.updates.snapshot(),statusTimeline:[...this.statusTimeline],performance:this.performanceTracker?.snapshot(),contextUsage:H(i),promptResponse:i}}async compactContext(e){if(!d.existsSync(e.cwd)||!d.statSync(e.cwd).isDirectory())throw new Error(`Project root does not exist or is not a directory: ${e.cwd}`);const s=Date.now();this.statusCallback=e.onStatus??null,this.updateCallback=e.onUpdate??null,this.attachAbortSignal(e.signal);let o=this.providerSessionId,r;try{this.emitStatus("initializing","Codex ACP 启动中","codex-acp");const i=await this.acquireRuntime(e.cwd);this.lease=i,await this.initialize(i),o=await this.establishSession(i,e),await this.applyConfig(i.client,o,e);const c=z(e.timeoutMs);r=await this.compactWithAbort(i.client,{sessionId:o,allowRollover:e.allowRollover===!0,timeoutMs:c,signal:e.signal});const a=A(r);a&&a!==o&&(o=a,this.providerSessionId=a,i.setProviderSession({providerSessionId:a,sessionResponse:this.sessionResponse}))}catch(i){if(this.lease&&U(i,e.signal)){const c=this.lease;this.lease=null,await c.dispose().catch(()=>{})}throw this.emitStatus("failed","上下文压缩失败",i instanceof Error?i.message:String(i)),i}finally{this.abortHandler&&e.signal&&(e.signal.removeEventListener("abort",this.abortHandler),this.abortHandler=null),this.releaseLease()}const n=l(r)??{};return{providerSessionId:o||this.providerSessionId||"",contextUsage:x(n.contextUsage),maintenance:l(n.maintenance)??{},durationMs:Date.now()-s}}async cancel(){this.providerSessionId&&this.lease?.client.cancel(this.providerSessionId)}async close(){this.statusCallback=null,this.updateCallback=null,this.permissionCallback=null,this.userInputCallback=null,this.abortHandler&&(this.abortHandler=null),this.releaseLease()}async acquireRuntime(e){const s=y(),o=await g(),r=await S.acquire({providerId:"codex-acp",cwd:e,providerSessionId:this.providerSessionId,runtimeIdentity:o?.runtimeIdentity||"default",env:b(o),command:s.command,args:s.args,onSessionUpdate:n=>this.handleSessionUpdate(n),onClientRequest:async(n,i)=>{if(n==="session/request_permission")return this.requestPermission(i);if(n==="elicitation/create"||n==="session/request_user_input")return this.requestUserInput(i);throw new Error(`Unsupported Codex ACP client request: ${n}`)}});try{return await I(r,o),r}catch(n){throw await r.dispose().catch(()=>{}),n}}async initialize(e){e.initialized||(await e.client.initialize(6e4),e.markInitialized())}async establishSession(e,s){const o=e.client,r=C(s);if(e.providerSessionId&&e.sessionEstablished)return this.providerSessionId=e.providerSessionId,this.sessionResponse=e.sessionResponse,e.providerSessionId;if(this.providerSessionId)try{this.emitStatus("resuming","Codex ACP 会话恢复中","session/load");const c=await o.loadSession({sessionId:this.providerSessionId,cwd:s.cwd,config:r},6e4);this.sessionResponse=l(c);const a=p(c)||this.providerSessionId;return this.providerSessionId=a,e.setProviderSession({providerSessionId:a,sessionResponse:this.sessionResponse}),a}catch(c){if(s.requireSessionResume){const a=c instanceof Error?c.message:String(c);throw new Error(`无法恢复导入的 Codex ACP Session ${this.providerSessionId}:${a}`)}this.providerSessionId=null}this.emitStatus("acp","Codex ACP 会话创建中","session/new");const n=await o.newSession({cwd:s.cwd,config:r},12e4);this.sessionResponse=l(n);const i=p(n);if(!i)throw new Error("Codex ACP session/new did not return sessionId");return this.providerSessionId=i,e.setProviderSession({providerSessionId:i,sessionResponse:this.sessionResponse}),i}async applyConfig(e,s,o){o.model&&await e.setSessionConfigOption({sessionId:s,configId:"model",value:o.model},6e4).catch(()=>{});const r=f(o.mode);r&&await e.setSessionConfigOption({sessionId:s,configId:"approvalPolicy",value:r},6e4).catch(()=>{});const n=h(o.mode);n&&await e.setSessionConfigOption({sessionId:s,configId:"sandbox",value:n},6e4).catch(()=>{});for(const[i,c]of Object.entries(o.configOptionValues??{}))["model","mode","sandbox","approvalPolicy"].includes(i)||await e.setSessionConfigOption({sessionId:s,configId:i,value:c},6e4).catch(()=>{})}handleSessionUpdate(e){this.performanceTracker?.recordEvent(),this.updates.push({method:"session/update",params:e});const s=R(e);s&&(s.type==="agent_message_chunk"&&(this.content+=s.text??""),this.updateCallback?.(s));const o=M(e);o&&this.emitStatus(o.phase,o.label,o.detail,o.updateType)}startSlowStatusTimer(){const e=_().slowEventThresholdMs,s=setInterval(()=>{const o=this.performanceTracker,r=Date.now();if(!o?.shouldReportSlow(r,e))return;const n=o.snapshot(r),i=Math.max(1,Math.round(n.silentForMs/6e4));this.emitStatus("thinking","模型长时间处理中",`已 ${i} 分钟没有收到新的 ACP 事件`,"aca.performance.slow")},Math.min(3e4,Math.max(1e3,Math.floor(e/4))));return s.unref(),s}attachAbortSignal(e){if(e){if(this.abortHandler=()=>{this.cancel()},e.aborted)throw new Error("Codex ACP prompt cancelled");e.addEventListener("abort",this.abortHandler,{once:!0})}}async promptWithAbort(e,s){return w({signal:s.signal,cancelledMessage:"Codex ACP prompt cancelled",startRequest:()=>e.prompt({sessionId:s.sessionId,prompt:s.prompt},s.timeoutMs),onAbort:async()=>{e.cancel(s.sessionId),await this.lease?.dispose()}})}async compactWithAbort(e,s){return w({signal:s.signal,cancelledMessage:"Codex ACP 上下文压缩已取消",startRequest:()=>e.compactSession({sessionId:s.sessionId,allowRollover:s.allowRollover,timeoutMs:s.timeoutMs},s.timeoutMs),onAbort:async()=>{e.cancel(s.sessionId),await this.lease?.dispose()}})}async requestPermission(e){if(!this.permissionCallback)return{outcome:{outcome:"cancelled"}};const s=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=s.request&&typeof s.request=="object"&&!Array.isArray(s.request)?s.request:s,n=(Array.isArray(o.options)?o.options:[]).map((i,c)=>{const a=i&&typeof i=="object"&&!Array.isArray(i)?i:{};return{optionId:String(a.optionId??a.id??c),kind:String(a.kind??"choice"),...typeof a.label=="string"?{label:a.label}:{}}});return this.emitStatus("requestPermission","等待授权",O(o),"session/request_permission"),this.permissionCallback({requestId:String(o.requestId??o.id??`codex-acp-permission-${Date.now()}`),params:e,options:n})}async requestUserInput(e){if(!this.userInputCallback)return{outcome:{outcome:"cancelled"}};const s=e&&typeof e=="object"&&!Array.isArray(e)?e:{},o=s.request&&typeof s.request=="object"&&!Array.isArray(s.request)?s.request:s;return this.emitStatus("requestPermission","等待输入",D(o),"elicitation/create"),this.userInputCallback({requestId:String(o.requestId??o.id??`codex-acp-input-${Date.now()}`),prompt:String(o.prompt??o.message??o.question??""),params:e,...typeof o.defaultValue=="string"?{defaultValue:o.defaultValue}:{}})}emitStatus(e,s,o,r){const n={phase:e,label:s,...o?{detail:o}:{},...r?{updateType:r}:{},atMs:Date.now()},i=this.statusTimeline[this.statusTimeline.length-1];i&&i.phase===n.phase&&i.label===n.label&&i.detail===n.detail&&i.updateType===n.updateType||(this.statusTimeline.push(n),this.statusCallback?.(n))}releaseLease(){this.lease?.release(),this.lease=null}}async function I(t,e){t.runtimeConfigured||(await t.client.configureRuntime({codexProvider:e?{baseUrl:e.baseUrl,apiKey:e.apiKey,revision:e.revision}:null}),t.markRuntimeConfigured())}function b(t){return t?{OPENAI_API_KEY:t.apiKey,OPENAI_BASE_URL:t.baseUrl}:{}}function U(t,e){if(e?.aborted)return!0;const s=t instanceof Error?t.message:String(t||"");return/compact.*timed out|request timed out.*compact|adapter closed|aborted|cancelled/i.test(s)}function p(t){if(!t||typeof t!="object"||Array.isArray(t))return null;const e=t.sessionId;return typeof e=="string"&&e.trim()?e.trim():null}function l(t){return t&&typeof t=="object"&&!Array.isArray(t)?t:null}function h(t){const e=String(t||"").trim();return e?e==="read-only"?"read-only":["agent-full-access","full-access","bypassPermissions","danger-full-access"].includes(e)?"danger-full-access":"workspace-write":null}function f(t){const e=String(t||"").trim();return e?["agent-full-access","full-access","bypassPermissions","danger-full-access"].includes(e)?"never":e==="acceptEdits"||e==="accept-edits"?"on-failure":"on-request":null}function C(t){return{...t.model?{model:t.model}:{},...f(t.mode)?{approvalPolicy:f(t.mode)}:{},...h(t.mode)?{sandbox:h(t.mode)}:{},...t.configOptionValues??{}}}function O(t){const e=t.params&&typeof t.params=="object"&&!Array.isArray(t.params)?t.params:t;for(const s of["command","cwd","reason","grantRoot","method"]){const o=e[s];if(typeof o=="string"&&o.trim())return v(o)}}function D(t){for(const e of["prompt","message","question","title"]){const s=t[e];if(typeof s=="string"&&s.trim())return v(s)}}function v(t){const e=t.replace(/\s+/g," ").trim();return e.length>120?`${e.slice(0,117)}...`:e}function j(){const t=Number.parseInt(process.env.ACA_CODEX_ACP_PROMPT_TIMEOUT_MS??process.env.ACA_ACP_PROMPT_TIMEOUT_MS??"",10);return Number.isInteger(t)&&t>=3e4?t:360*60*1e3}function z(t){const e=Number.isInteger(t)?Number(t):Number.parseInt(process.env.ACA_CODEX_ACP_COMPACT_TIMEOUT_MS??"",10);return Number.isFinite(e)&&e>=3e4?Math.min(e,6e5):12e4}function A(t){if(!t||typeof t!="object"||Array.isArray(t))return null;const e=t;return typeof e.sessionId=="string"&&e.sessionId.trim()?e.sessionId:null}function H(t){const e=l(t),s=l(e?._meta);return x(s?.acaContextUsage)}function x(t){const e=l(t);if(!e)return null;const s=m(e.contextWindow),o=m(e.usedTokens),r=m(e.inputTokens),n=String(e.state||"unknown"),i=["active","compacted","new_thread"].includes(n)?n:"unknown";return{usedTokens:o,inputTokens:r,contextWindow:s,ratio:s>0?Math.max(0,m(e.ratio)||o/s):0,observedAtMs:m(e.observedAtMs)||Date.now(),state:i,maintenanceAction:typeof e.maintenanceAction=="string"?e.maintenanceAction:null}}function m(t){return typeof t=="number"&&Number.isFinite(t)&&t>=0?t:0}export{Y as CodexAcpProvider,q as codexAcpCapabilities};
@@ -1,3 +1,3 @@
1
- import{Command as i}from"commander";import s from"chalk";import m from"node:fs";import p from"node:path";import{spawn as S}from"node:child_process";import{randomUUID as $}from"node:crypto";import{getLogDir as c,getRuntimeStatePath as y}from"../core/paths.js";import{ensureDir as g,readJsonFile as C}from"../core/fs.js";import{isProcessAlive as r,readPidFile as u,removePidFile as f}from"../core/process.js";import{ACA_WORKER_INSTANCE_ENV as x}from"../core/process-identity.js";import{applyRuntimePathOptions as l}from"../core/runtime-options.js";const h=3e4;function G(){return new i("daemon").description("Run aca as a background daemon service").addCommand(d(new i("start").description("Start aca daemon in the background")).allowUnknownOption(!0).action(e=>{l(e);const o=u();if(o&&r(o)){console.log(`Daemon is already running (PID ${o}).`),process.exitCode=1;return}const n=D(w("start"));n.unref(),console.log(`Daemon started (PID ${n.pid})`),I()})).addCommand(d(new i("stop").description("Stop the running aca daemon")).action(async e=>{l(e);const o=u();if(!o){console.log("No daemon PID file found. Daemon is not running.");return}if(!r(o)){console.log(`Daemon process (PID ${o}) is not running. Cleaning up PID file.`),f();return}await P(o)?(f(),console.log(`Daemon stopped successfully (PID ${o}).`)):(console.log(`Daemon (PID ${o}) is still running. You may need to kill it manually: ${s.yellow(`kill -9 ${o}`)}`),process.exitCode=1)})).addCommand(d(new i("status").description("Show daemon status")).action(e=>{l(e);const o=C(y()),n=u();if(n&&r(n)){console.log(s.green("● Daemon is running")),console.log(` PID: ${n}`),o?.phase&&console.log(` Phase: ${o.phase}`),o?.startupStage&&console.log(` Stage: ${o.startupStage}`);return}n?(console.log(s.red("● Daemon is not running (stale PID file)")),f()):console.log(s.red("● Daemon is not running")),process.exitCode=1})).addCommand(d(new i("logs").description("Show daemon logs")).option("-n, --lines <count>","number of lines to show","50").action(e=>{l(e);const o=Number.parseInt(e.lines,10)||50,n=E();if(!n){console.log(`No log files found in ${c()}`),process.exitCode=1;return}const t=m.readFileSync(n,"utf8").trimEnd().split(`
1
+ import{Command as a}from"commander";import s from"chalk";import m from"node:fs";import p from"node:path";import{spawn as S}from"node:child_process";import{randomUUID as $}from"node:crypto";import{getLogDir as c,getRuntimeStatePath as y}from"../core/paths.js";import{ensureDir as g,readJsonFile as C}from"../core/fs.js";import{isProcessAlive as r,readPidFile as u,removePidFile as f}from"../core/process.js";import{ACA_WORKER_INSTANCE_ENV as x}from"../core/process-identity.js";import{applyRuntimePathOptions as l}from"../core/runtime-options.js";const w=3e4;function G(){return new a("daemon").description("Run aca as a background daemon service").addCommand(d(new a("start").description("Start aca daemon in the background")).allowUnknownOption(!0).action(e=>{l(e);const o=u();if(o&&r(o)){console.log(`Daemon is already running (PID ${o}).`),process.exitCode=1;return}const n=D(h("start"));n.unref(),console.log(`Daemon started (PID ${n.pid})`),I()})).addCommand(d(new a("stop").description("Stop the running aca daemon")).action(async e=>{l(e);const o=u();if(!o){console.log("No daemon PID file found. Daemon is not running.");return}if(!r(o)){console.log(`Daemon process (PID ${o}) is not running. Cleaning up PID file.`),f();return}await P(o)?(f(),console.log(`Daemon stopped successfully (PID ${o}).`)):(console.log(`Daemon (PID ${o}) is still running. You may need to kill it manually: ${s.yellow(`kill -9 ${o}`)}`),process.exitCode=1)})).addCommand(d(new a("status").description("Show daemon status")).action(e=>{l(e);const o=C(y()),n=u();if(n&&r(n)){console.log(s.green("● Daemon is running")),console.log(` PID: ${n}`),o?.phase&&console.log(` Phase: ${o.phase}`),o?.startupStage&&console.log(` Stage: ${o.startupStage}`);return}n?(console.log(s.red("● Daemon is not running (stale PID file)")),f()):console.log(s.red("● Daemon is not running")),process.exitCode=1})).addCommand(d(new a("logs").description("Show daemon logs")).option("-n, --lines <count>","number of lines to show","50").action(e=>{l(e);const o=Number.parseInt(e.lines,10)||50,n=E();if(!n){console.log(`No log files found in ${c()}`),process.exitCode=1;return}const t=m.readFileSync(n,"utf8").trimEnd().split(`
2
2
  `);console.log(s.dim(`--- ${n} (last ${o} lines) ---`)),console.log(t.slice(-o).join(`
3
- `))})).addCommand(d(new i("restart").description("Restart the aca daemon")).allowUnknownOption(!0).action(async e=>{l(e);const o=u();if(o&&r(o)&&!await P(o)){console.error(`Daemon restart aborted because PID ${o} did not stop within ${h}ms.`),process.exitCode=1;return}f();const n=D(w("restart"));n.unref(),console.log(`Daemon started (PID ${n.pid})`),I()}))}function d(e){return e.option("--aica-home <path>","Runtime home directory (defaults to ~/.aica)").option("--aica-config <path>","Config file path").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config")}function w(e){const o=process.argv.findIndex((t,a)=>a>=2&&t==="daemon"),n=process.argv.findIndex((t,a)=>a>o&&t===e);return n>=0?process.argv.slice(n+1):[]}function D(e){const o=v();g(c());const n=new Date().toISOString().slice(0,10),t=p.join(c(),`daemon-stderr-${n}.log`),a=m.openSync(t,"a");try{return S(o.command,[...o.prefixArgs,"start",...e],{detached:!0,stdio:["ignore","ignore",a],env:{...process.env,[x]:$()}})}finally{m.closeSync(a)}}function v(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return{command:process.execPath,prefixArgs:[]};const e=process.argv[1];if(!e)throw new Error("Unable to resolve the current ACA CLI entrypoint");return{command:process.execPath,prefixArgs:[p.resolve(e)]}}function I(){const e=A();console.log(`Use ${s.yellow(`${e} daemon status`)} to check status`),console.log(`Use ${s.yellow(`${e} daemon stop`)} to stop`),console.log(`Use ${s.yellow(`${e} daemon logs`)} to view logs`)}function A(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return process.execPath;const e=p.basename(process.argv[1]||"");return/worker-cli|aica/i.test(e)?"aica":"aca"}function E(){return g(c()),m.readdirSync(c()).filter(o=>/^\d{4}-\d{2}-\d{2}\.log$/.test(o)).map(o=>p.join(c(),o)).sort().at(-1)??null}async function R(e,o){const n=Date.now();for(;Date.now()-n<o;){if(!r(e))return!0;await new Promise(t=>setTimeout(t,100))}return!r(e)}async function P(e,o=h){return r(e)?(process.kill(e,"SIGTERM"),R(e,o)):!0}export{G as createDaemonCommand,I as printStartTips,D as spawnDaemon,P as stopDaemonProcess};
3
+ `))})).addCommand(d(new a("restart").description("Restart the aca daemon")).allowUnknownOption(!0).action(async e=>{l(e);const o=u();if(o&&r(o)&&!await P(o)){console.error(`Daemon restart aborted because PID ${o} did not stop within ${w}ms.`),process.exitCode=1;return}f();const n=D(h("restart"));n.unref(),console.log(`Daemon started (PID ${n.pid})`),I()}))}function d(e){return e.option("--aica-home <path>","Runtime home directory (defaults to ~/.aica)").option("--aica-config <path>","Config file path").option("--aca-home <path>","Deprecated alias for --aica-home").option("--aca-config <path>","Deprecated alias for --aica-config")}function h(e){const o=process.argv.findIndex((t,i)=>i>=2&&t==="daemon"),n=process.argv.findIndex((t,i)=>i>o&&t===e);return n>=0?process.argv.slice(n+1):[]}function D(e){const o=v();g(c());const n=new Date().toISOString().slice(0,10),t=p.join(c(),`daemon-stderr-${n}.log`),i=m.openSync(t,"a");try{return S(o.command,[...o.prefixArgs,"start",...e],{detached:!0,stdio:["ignore","ignore",i],windowsHide:!0,env:{...process.env,[x]:$()}})}finally{m.closeSync(i)}}function v(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return{command:process.execPath,prefixArgs:[]};const e=process.argv[1];if(!e)throw new Error("Unable to resolve the current ACA CLI entrypoint");return{command:process.execPath,prefixArgs:[p.resolve(e)]}}function I(){const e=A();console.log(`Use ${s.yellow(`${e} daemon status`)} to check status`),console.log(`Use ${s.yellow(`${e} daemon stop`)} to stop`),console.log(`Use ${s.yellow(`${e} daemon logs`)} to view logs`)}function A(){if(process.env.ACA_SINGLE_FILE_WORKER==="1")return process.execPath;const e=p.basename(process.argv[1]||"");return/worker-cli|aica/i.test(e)?"aica":"aca"}function E(){return g(c()),m.readdirSync(c()).filter(o=>/^\d{4}-\d{2}-\d{2}\.log$/.test(o)).map(o=>p.join(c(),o)).sort().at(-1)??null}async function R(e,o){const n=Date.now();for(;Date.now()-n<o;){if(!r(e))return!0;await new Promise(t=>setTimeout(t,100))}return!r(e)}async function P(e,o=w){return r(e)?(process.kill(e,"SIGTERM"),R(e,o)):!0}export{G as createDaemonCommand,I as printStartTips,D as spawnDaemon,P as stopDaemonProcess};
@@ -1,3 +1,3 @@
1
- import{execFileSync as g}from"node:child_process";import m from"node:os";import{readJsonFile as I,writeJsonFile as v}from"./fs.js";import{getRuntimeStatePath as y}from"./paths.js";import{acaServerRequest as x}from"./aca-server-client.js";import{loadAcaConfig as C}from"./aca-config.js";import{providerHealthSnapshot as w}from"./provider-health.js";import{readCodexCapabilities as O}from"./codex-capabilities.js";const b=3e4,_=360*60*1e3,k=360*60*1e3;let p=null,u=null;async function E(e=C(),t={}){return e.token?await x("POST","/api/heartbeat",{machineId:e.machineId,machineName:e.machineName,machineInfo:{platform:m.platform(),architecture:m.arch(),osRelease:m.release(),acaVersion:S(),hostname:m.hostname()},workspaceId:e.defaultWorkspaceId,agentConfigs:t.includeProviderDetails===!1?[]:await D(e),projects:e.projects.map(n=>({projectId:n.projectId,workspaceId:n.workspaceId,name:n.name,rootPath:n.rootPath}))}):{ok:!1,skipped:!0,reason:"not_logged_in"}}async function D(e){const t=w(),o=await O(),n=o?.capabilities??N();return[{agentId:`${e.machineId}:builtin:codex`,workspaceId:e.defaultWorkspaceId,name:"Codex",agentType:"codex",cliType:"builtin",machineId:e.machineId,model:o?.defaultModel||"gpt-5.5",mode:"agent-full-access",raw:{providerStatus:t.codex,capabilities:n}},{agentId:`${e.machineId}:builtin:mimo`,workspaceId:e.defaultWorkspaceId,name:"MimoCode",agentType:"mimo",cliType:"builtin",machineId:e.machineId,model:"mimo/mimo-auto",mode:"agent-full-access",raw:{providerStatus:t.mimo,capabilities:j()}},{agentId:`${e.machineId}:builtin:opencode`,workspaceId:e.defaultWorkspaceId,name:"OpenCode",agentType:"opencode",cliType:"builtin",machineId:e.machineId,model:process.env.ACA_OPENCODE_DEFAULT_MODEL||null,mode:"agent-full-access",raw:{providerStatus:t.opencode,capabilities:H()}}]}function S(){return process.env.ACA_VERSION?.trim()||"0.1.0"}function N(){const e=[{id:"read-only",name:"Read-only",description:"Requires approval to edit files and run commands."},{id:"agent",name:"Agent",description:"Read and edit files, and run commands."},{id:"agent-full-access",name:"Agent (full access)",description:"Edit files and run commands with full access."}],t=[{modelId:"gpt-5.5",name:"gpt-5.5",description:"Latest frontier Codex model"},{modelId:"gpt-5.4",name:"gpt-5.4",description:"Frontier Codex model"},{modelId:"gpt-5.4-mini",name:"gpt-5.4-mini",description:"Smaller, faster Codex model"}],o=[{value:"low",name:"Low",description:"Fastest responses"},{value:"medium",name:"Medium",description:"Balanced reasoning"},{value:"high",name:"High",description:"More reasoning for difficult tasks"},{value:"xhigh",name:"X High",description:"Extra reasoning for complex tasks"}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!0,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(n=>({value:n.id,name:n.name,description:n.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:"gpt-5.5",options:t.map(n=>({value:n.modelId,name:n.name,description:n.description}))},{id:"reasoning_effort",name:"Reasoning effort",category:"thought_level",type:"select",currentValue:"medium",options:o}]}}function V(){const e=[{id:"agent",name:"Agent",description:"Run MimoCode with normal workspace permissions."},{id:"agent-full-access",name:"Agent (full access)",description:"Run MimoCode and auto-approve supported local permissions."}],t=[{modelId:"mimo/mimo-auto",name:"MiMo Auto (free)",description:"MiMo Auto free model"},{modelId:"xiaomi/mimo-v2.5",name:"mimo-v2.5",description:"MimoCode v2.5"},{modelId:"xiaomi/mimo-v2.5-pro",name:"mimo-v2.5-pro",description:"MimoCode v2.5 Pro"},{modelId:"xiaomi/mimo-v2.5-pro-ultraspeed",name:"mimo-v2.5-pro-ultraspeed",description:"MimoCode v2.5 Pro UltraSpeed"}],o=[{value:"minimal",name:"Minimal",description:"Fastest responses"},{value:"high",name:"High",description:"More reasoning for difficult tasks"},{value:"max",name:"Max",description:"Maximum reasoning when supported"}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(n=>({value:n.id,name:n.name,description:n.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:"mimo/mimo-auto",options:t.map(n=>({value:n.modelId,name:n.name,description:n.description}))},{id:"variant",name:"Variant",category:"thought_level",type:"select",currentValue:"high",options:o}]}}function j(){const e=Date.now();if(p&&p.expiresAtMs>e)return p.capabilities;const t=B()??V();return p={expiresAtMs:e+_,capabilities:t},t}function R(){const e=[{id:"plan",name:"Plan",description:"Inspect the project and propose changes."},{id:"build",name:"Build",description:"Read and edit files, and run commands when approved."},{id:"agent-full-access",name:"Agent (full access)",description:"Run OpenCode with ACA full-access mode mapping."}],t=[{modelId:"",name:"Default",description:"Use the model selected by OpenCode configuration."}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(o=>({value:o.id,name:o.name,description:o.description}))},{id:"model",name:"Model",category:"model",type:"text",currentValue:process.env.ACA_OPENCODE_DEFAULT_MODEL||"",options:t.map(o=>({value:o.modelId,name:o.name,description:o.description}))}]}}function H(){const e=Date.now();if(u&&u.expiresAtMs>e)return u.capabilities;const t=T()??R();return u={expiresAtMs:e+k,capabilities:t},t}function T(){try{const e=process.env.ACA_OPENCODE_COMMAND||"opencode",t=g(e,["models","--verbose"],{encoding:"utf8",timeout:15e3,maxBuffer:8*1024*1024,stdio:["ignore","pipe","pipe"]}),o=$(t);if(!o.length)return null;const n=[{id:"plan",name:"Plan",description:"Inspect the project and propose changes."},{id:"build",name:"Build",description:"Read and edit files, and run commands when approved."},{id:"agent-full-access",name:"Agent (full access)",description:"Run OpenCode with ACA full-access mode mapping."}],a=L(o);return{source:"opencode-cli",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:n,models:o.map(i=>({modelId:i.modelId,name:i.displayName,description:i.description,metadata:i.metadata})),configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:n.map(i=>({value:i.id,name:i.name,description:i.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:a,options:o.map(i=>({value:i.modelId,name:i.displayName,description:i.description}))}]}}catch{return null}}function $(e){const t=e.split(/\r?\n/),o=[];let n=0;for(;n<t.length;){const a=t[n]?.trim()??"";if(n+=1,!/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/i.test(a))continue;const i=[];let s=0,c=!1;for(;n<t.length;){const d=t[n]??"";if(n+=1,!(!c&&!d.trim().startsWith("{"))&&(c=!0,i.push(d),s+=f(d,"{")-f(d,"}"),c&&s<=0))break}const r=A(i.join(`
2
- `));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:F(l,r),description:U(r),metadata:r})}return o}function L(e){const t=process.env.ACA_OPENCODE_DEFAULT_MODEL?.trim();return t&&e.some(o=>o.modelId===t)?t:e.find(o=>o.modelId==="opencode/big-pickle")?.modelId||e.find(o=>/free$/i.test(o.modelId))?.modelId||e[0]?.modelId||""}function F(e,t){return P(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function P(e){const t=e.cost;if(!t||typeof t!="object"||Array.isArray(t))return!1;const o=t,n=o.cache&&typeof o.cache=="object"&&!Array.isArray(o.cache)?o.cache:{};return[o.input,o.output,n.read,n.write].every(a=>Number(a??0)===0)}function U(e){const t=e.limit&&typeof e.limit=="object"&&!Array.isArray(e.limit)?e.limit:{},o=Number(t.context??0),n=Number(t.output??0);return o>0&&n>0?`Context ${o}, output ${n}`:o>0?`Context ${o}`:"OpenCode model"}function B(){try{const e=process.env.ACA_MIMO_COMMAND||"mimo",t=g(e,["models","--verbose"],{encoding:"utf8",timeout:15e3,maxBuffer:2*1024*1024,stdio:["ignore","pipe","pipe"]}),o=W(t);if(!o.length)return null;const n=[{id:"agent",name:"Agent",description:"Run MimoCode with normal workspace permissions."},{id:"agent-full-access",name:"Agent (full access)",description:"Run MimoCode and auto-approve supported local permissions."}],a=z(o);return{source:"mimo-cli",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:n,models:o.map(i=>({modelId:i.modelId,name:i.displayName,description:i.description,metadata:i.metadata})),configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:n.map(i=>({value:i.id,name:i.name,description:i.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:o.some(i=>i.modelId==="mimo/mimo-auto")?"mimo/mimo-auto":o[0]?.modelId,options:o.map(i=>({value:i.modelId,name:i.displayName,description:i.description}))},{id:"variant",name:"Variant",category:"thought_level",type:"select",currentValue:a.some(i=>i.value==="high")?"high":a[0]?.value,options:a}]}}catch{return null}}function W(e){const t=e.split(/\r?\n/),o=[];let n=0;for(;n<t.length;){const a=t[n]?.trim()??"";if(n+=1,!/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/i.test(a))continue;const i=[];let s=0,c=!1;for(;n<t.length;){const d=t[n]??"";if(n+=1,!(!c&&!d.trim().startsWith("{"))&&(c=!0,i.push(d),s+=f(d,"{")-f(d,"}"),c&&s<=0))break}const r=A(i.join(`
3
- `));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:J(l,r),description:X(r),metadata:r})}return o}function z(e){const t=new Set;for(const a of e){const i=a.metadata.variants;if(!(!i||typeof i!="object"||Array.isArray(i)))for(const s of Object.keys(i))t.add(s)}const o=["minimal","low","medium","high","max"].filter(a=>t.has(a));return(o.length?o:["minimal","high","max"]).map(a=>({value:a,name:G(a),description:a==="high"?"More reasoning for difficult tasks":a==="max"?"Maximum reasoning when supported":"Fastest responses"}))}function J(e,t){return q(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function q(e){const t=e.cost;if(!t||typeof t!="object"||Array.isArray(t))return!1;const o=t,n=o.cache&&typeof o.cache=="object"&&!Array.isArray(o.cache)?o.cache:{};return[o.input,o.output,n.read,n.write].every(a=>Number(a??0)===0)}function X(e){const t=e.limit&&typeof e.limit=="object"&&!Array.isArray(e.limit)?e.limit:{},o=Number(t.context??0);return o>0?`Context ${o}`:"MimoCode model"}function A(e){try{const t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:null}catch{return null}}function M(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim()}function G(e){return e.slice(0,1).toUpperCase()+e.slice(1)}function f(e,t){let o=0;for(const n of e)n===t&&(o+=1);return o}function ie(e){let t=!1;const o=async(i,s="heartbeat")=>{const c=new Date().toISOString();try{const r=await E(C(),{includeProviderDetails:i});if(r.skipped){h({lastHeartbeatAt:c,lastHeartbeatStatus:"skipped",lastHeartbeatReason:r.reason??"unknown"}),e.debug(`${s} skipped: ${r.reason??"unknown"}`);return}h({lastHeartbeatAt:c,lastHeartbeatStatus:"ok",lastHeartbeatProjectCount:r.projectCount??null,presenceExpiresAtMs:r.presenceExpiresAtMs??null}),e.debug(`${s} ok projects=${r.projectCount??0}`)}catch(r){const l=r instanceof Error?r.message:String(r);h({lastHeartbeatAt:c,lastHeartbeatStatus:"error",lastHeartbeatError:l}),e.warn(`${s} failed: ${l}`)}},n=async()=>{if(!t){t=!0;try{await o(!0)}finally{t=!1}}};return(async()=>{if(!t){t=!0;try{await o(!1,"initial presence heartbeat"),await o(!0)}finally{t=!1}}})(),setInterval(()=>{n()},b)}function h(e){const t=I(y())??{};v(y(),{...t,heartbeatIntervalMs:b,...e})}export{b as HEARTBEAT_INTERVAL_MS,E as sendHeartbeat,ie as startHeartbeatLoop};
1
+ import{execFileSync as g}from"node:child_process";import m from"node:os";import{readJsonFile as I,writeJsonFile as v}from"./fs.js";import{getRuntimeStatePath as y}from"./paths.js";import{acaServerRequest as x}from"./aca-server-client.js";import{loadAcaConfig as C}from"./aca-config.js";import{providerHealthSnapshot as w}from"./provider-health.js";import{readCodexCapabilities as O}from"./codex-capabilities.js";import{AICA_VERSION as _}from"../version.js";const b=3e4,k=360*60*1e3,E=360*60*1e3;let p=null,u=null;async function S(e=C(),t={}){return e.token?await x("POST","/api/heartbeat",{machineId:e.machineId,machineName:e.machineName,machineInfo:{platform:m.platform(),architecture:m.arch(),osRelease:m.release(),acaVersion:N(),hostname:m.hostname()},workspaceId:e.defaultWorkspaceId,agentConfigs:t.includeProviderDetails===!1?[]:await D(e),projects:e.projects.map(n=>({projectId:n.projectId,workspaceId:n.workspaceId,name:n.name,rootPath:n.rootPath}))}):{ok:!1,skipped:!0,reason:"not_logged_in"}}async function D(e){const t=w(),o=await O(),n=o?.capabilities??V();return[{agentId:`${e.machineId}:builtin:codex`,workspaceId:e.defaultWorkspaceId,name:"Codex",agentType:"codex",cliType:"builtin",machineId:e.machineId,model:o?.defaultModel||"gpt-5.5",mode:"agent-full-access",raw:{providerStatus:t.codex,capabilities:n}},{agentId:`${e.machineId}:builtin:mimo`,workspaceId:e.defaultWorkspaceId,name:"MimoCode",agentType:"mimo",cliType:"builtin",machineId:e.machineId,model:"mimo/mimo-auto",mode:"agent-full-access",raw:{providerStatus:t.mimo,capabilities:j()}},{agentId:`${e.machineId}:builtin:opencode`,workspaceId:e.defaultWorkspaceId,name:"OpenCode",agentType:"opencode",cliType:"builtin",machineId:e.machineId,model:process.env.ACA_OPENCODE_DEFAULT_MODEL||null,mode:"agent-full-access",raw:{providerStatus:t.opencode,capabilities:T()}}]}function N(e=process.env){return e.ACA_VERSION?.trim()||_}function V(){const e=[{id:"read-only",name:"Read-only",description:"Requires approval to edit files and run commands."},{id:"agent",name:"Agent",description:"Read and edit files, and run commands."},{id:"agent-full-access",name:"Agent (full access)",description:"Edit files and run commands with full access."}],t=[{modelId:"gpt-5.5",name:"gpt-5.5",description:"Latest frontier Codex model"},{modelId:"gpt-5.4",name:"gpt-5.4",description:"Frontier Codex model"},{modelId:"gpt-5.4-mini",name:"gpt-5.4-mini",description:"Smaller, faster Codex model"}],o=[{value:"low",name:"Low",description:"Fastest responses"},{value:"medium",name:"Medium",description:"Balanced reasoning"},{value:"high",name:"High",description:"More reasoning for difficult tasks"},{value:"xhigh",name:"X High",description:"Extra reasoning for complex tasks"}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!0,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(n=>({value:n.id,name:n.name,description:n.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:"gpt-5.5",options:t.map(n=>({value:n.modelId,name:n.name,description:n.description}))},{id:"reasoning_effort",name:"Reasoning effort",category:"thought_level",type:"select",currentValue:"medium",options:o}]}}function R(){const e=[{id:"agent",name:"Agent",description:"Run MimoCode with normal workspace permissions."},{id:"agent-full-access",name:"Agent (full access)",description:"Run MimoCode and auto-approve supported local permissions."}],t=[{modelId:"mimo/mimo-auto",name:"MiMo Auto (free)",description:"MiMo Auto free model"},{modelId:"xiaomi/mimo-v2.5",name:"mimo-v2.5",description:"MimoCode v2.5"},{modelId:"xiaomi/mimo-v2.5-pro",name:"mimo-v2.5-pro",description:"MimoCode v2.5 Pro"},{modelId:"xiaomi/mimo-v2.5-pro-ultraspeed",name:"mimo-v2.5-pro-ultraspeed",description:"MimoCode v2.5 Pro UltraSpeed"}],o=[{value:"minimal",name:"Minimal",description:"Fastest responses"},{value:"high",name:"High",description:"More reasoning for difficult tasks"},{value:"max",name:"Max",description:"Maximum reasoning when supported"}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(n=>({value:n.id,name:n.name,description:n.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:"mimo/mimo-auto",options:t.map(n=>({value:n.modelId,name:n.name,description:n.description}))},{id:"variant",name:"Variant",category:"thought_level",type:"select",currentValue:"high",options:o}]}}function j(){const e=Date.now();if(p&&p.expiresAtMs>e)return p.capabilities;const t=W()??R();return p={expiresAtMs:e+k,capabilities:t},t}function H(){const e=[{id:"plan",name:"Plan",description:"Inspect the project and propose changes."},{id:"build",name:"Build",description:"Read and edit files, and run commands when approved."},{id:"agent-full-access",name:"Agent (full access)",description:"Run OpenCode with ACA full-access mode mapping."}],t=[{modelId:"",name:"Default",description:"Use the model selected by OpenCode configuration."}];return{source:"aca-static",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:e,models:t,configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:e.map(o=>({value:o.id,name:o.name,description:o.description}))},{id:"model",name:"Model",category:"model",type:"text",currentValue:process.env.ACA_OPENCODE_DEFAULT_MODEL||"",options:t.map(o=>({value:o.modelId,name:o.name,description:o.description}))}]}}function T(){const e=Date.now();if(u&&u.expiresAtMs>e)return u.capabilities;const t=$()??H();return u={expiresAtMs:e+E,capabilities:t},t}function $(){try{const e=process.env.ACA_OPENCODE_COMMAND||"opencode",t=g(e,["models","--verbose"],{encoding:"utf8",timeout:15e3,maxBuffer:8*1024*1024,stdio:["ignore","pipe","pipe"]}),o=L(t);if(!o.length)return null;const n=[{id:"plan",name:"Plan",description:"Inspect the project and propose changes."},{id:"build",name:"Build",description:"Read and edit files, and run commands when approved."},{id:"agent-full-access",name:"Agent (full access)",description:"Run OpenCode with ACA full-access mode mapping."}],a=F(o);return{source:"opencode-cli",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:n,models:o.map(i=>({modelId:i.modelId,name:i.displayName,description:i.description,metadata:i.metadata})),configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:n.map(i=>({value:i.id,name:i.name,description:i.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:a,options:o.map(i=>({value:i.modelId,name:i.displayName,description:i.description}))}]}}catch{return null}}function L(e){const t=e.split(/\r?\n/),o=[];let n=0;for(;n<t.length;){const a=t[n]?.trim()??"";if(n+=1,!/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/i.test(a))continue;const i=[];let s=0,c=!1;for(;n<t.length;){const d=t[n]??"";if(n+=1,!(!c&&!d.trim().startsWith("{"))&&(c=!0,i.push(d),s+=f(d,"{")-f(d,"}"),c&&s<=0))break}const r=A(i.join(`
2
+ `));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:P(l,r),description:B(r),metadata:r})}return o}function F(e){const t=process.env.ACA_OPENCODE_DEFAULT_MODEL?.trim();return t&&e.some(o=>o.modelId===t)?t:e.find(o=>o.modelId==="opencode/big-pickle")?.modelId||e.find(o=>/free$/i.test(o.modelId))?.modelId||e[0]?.modelId||""}function P(e,t){return U(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function U(e){const t=e.cost;if(!t||typeof t!="object"||Array.isArray(t))return!1;const o=t,n=o.cache&&typeof o.cache=="object"&&!Array.isArray(o.cache)?o.cache:{};return[o.input,o.output,n.read,n.write].every(a=>Number(a??0)===0)}function B(e){const t=e.limit&&typeof e.limit=="object"&&!Array.isArray(e.limit)?e.limit:{},o=Number(t.context??0),n=Number(t.output??0);return o>0&&n>0?`Context ${o}, output ${n}`:o>0?`Context ${o}`:"OpenCode model"}function W(){try{const e=process.env.ACA_MIMO_COMMAND||"mimo",t=g(e,["models","--verbose"],{encoding:"utf8",timeout:15e3,maxBuffer:2*1024*1024,stdio:["ignore","pipe","pipe"]}),o=z(t);if(!o.length)return null;const n=[{id:"agent",name:"Agent",description:"Run MimoCode with normal workspace permissions."},{id:"agent-full-access",name:"Agent (full access)",description:"Run MimoCode and auto-approve supported local permissions."}],a=J(o);return{source:"mimo-cli",fetchedAt:Date.now(),contextUsage:!0,contextCompaction:!1,modes:n,models:o.map(i=>({modelId:i.modelId,name:i.displayName,description:i.description,metadata:i.metadata})),configOptions:[{id:"mode",name:"Mode",category:"mode",type:"select",currentValue:"agent-full-access",options:n.map(i=>({value:i.id,name:i.name,description:i.description}))},{id:"model",name:"Model",category:"model",type:"select",currentValue:o.some(i=>i.modelId==="mimo/mimo-auto")?"mimo/mimo-auto":o[0]?.modelId,options:o.map(i=>({value:i.modelId,name:i.displayName,description:i.description}))},{id:"variant",name:"Variant",category:"thought_level",type:"select",currentValue:a.some(i=>i.value==="high")?"high":a[0]?.value,options:a}]}}catch{return null}}function z(e){const t=e.split(/\r?\n/),o=[];let n=0;for(;n<t.length;){const a=t[n]?.trim()??"";if(n+=1,!/^[a-z0-9_.-]+\/[a-z0-9_.-]+$/i.test(a))continue;const i=[];let s=0,c=!1;for(;n<t.length;){const d=t[n]??"";if(n+=1,!(!c&&!d.trim().startsWith("{"))&&(c=!0,i.push(d),s+=f(d,"{")-f(d,"}"),c&&s<=0))break}const r=A(i.join(`
3
+ `));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:q(l,r),description:G(r),metadata:r})}return o}function J(e){const t=new Set;for(const a of e){const i=a.metadata.variants;if(!(!i||typeof i!="object"||Array.isArray(i)))for(const s of Object.keys(i))t.add(s)}const o=["minimal","low","medium","high","max"].filter(a=>t.has(a));return(o.length?o:["minimal","high","max"]).map(a=>({value:a,name:K(a),description:a==="high"?"More reasoning for difficult tasks":a==="max"?"Maximum reasoning when supported":"Fastest responses"}))}function q(e,t){return X(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function X(e){const t=e.cost;if(!t||typeof t!="object"||Array.isArray(t))return!1;const o=t,n=o.cache&&typeof o.cache=="object"&&!Array.isArray(o.cache)?o.cache:{};return[o.input,o.output,n.read,n.write].every(a=>Number(a??0)===0)}function G(e){const t=e.limit&&typeof e.limit=="object"&&!Array.isArray(e.limit)?e.limit:{},o=Number(t.context??0);return o>0?`Context ${o}`:"MimoCode model"}function A(e){try{const t=JSON.parse(e);return t&&typeof t=="object"&&!Array.isArray(t)?t:null}catch{return null}}function M(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim()}function K(e){return e.slice(0,1).toUpperCase()+e.slice(1)}function f(e,t){let o=0;for(const n of e)n===t&&(o+=1);return o}function re(e){let t=!1;const o=async(i,s="heartbeat")=>{const c=new Date().toISOString();try{const r=await S(C(),{includeProviderDetails:i});if(r.skipped){h({lastHeartbeatAt:c,lastHeartbeatStatus:"skipped",lastHeartbeatReason:r.reason??"unknown"}),e.debug(`${s} skipped: ${r.reason??"unknown"}`);return}h({lastHeartbeatAt:c,lastHeartbeatStatus:"ok",lastHeartbeatProjectCount:r.projectCount??null,presenceExpiresAtMs:r.presenceExpiresAtMs??null}),e.debug(`${s} ok projects=${r.projectCount??0}`)}catch(r){const l=r instanceof Error?r.message:String(r);h({lastHeartbeatAt:c,lastHeartbeatStatus:"error",lastHeartbeatError:l}),e.warn(`${s} failed: ${l}`)}},n=async()=>{if(!t){t=!0;try{await o(!0)}finally{t=!1}}};return(async()=>{if(!t){t=!0;try{await o(!1,"initial presence heartbeat"),await o(!0)}finally{t=!1}}})(),setInterval(()=>{n()},b)}function h(e){const t=I(y())??{};v(y(),{...t,heartbeatIntervalMs:b,...e})}export{b as HEARTBEAT_INTERVAL_MS,N as reportedAicaVersion,S as sendHeartbeat,re as startHeartbeatLoop};
@@ -0,0 +1 @@
1
+ const o="0.1.28";export{o as AICA_VERSION};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as a}from"commander";import m from"node:path";import{createStartCommand as n}from"./commands/start.js";import{createDaemonCommand as t}from"./commands/daemon.js";import{createWorkerAuthCommand as s}from"./commands/worker-auth.js";import{createWorkerCodexCommand as c}from"./commands/worker-codex.js";import{createWorkerProjectCommand as i}from"./commands/worker-project.js";import{DEFAULT_ACA_SERVER_URL as d}from"./core/aca-config.js";process.env.ACA_DEFAULT_SERVER_URL||=d;async function p(r=process.argv){const o=new a;o.name("aica").description("AICA remote Agent worker").version("0.1.26").enablePositionalOptions(),o.addCommand(n()),o.addCommand(t()),o.addCommand(s()),o.addCommand(i()),o.addCommand(c()),await o.parseAsync(r)}const C=process.argv[1]||"",e=m.basename(C).replace(/\.(?:js|ts|cjs|mjs)$/i,"");(e==="worker-cli"||e==="aica"||e==="aca")&&p().catch(r=>{const o=r instanceof Error?r.message:String(r);console.error(o),process.exitCode=1});export{p as runWorkerCli};
2
+ import{Command as m}from"commander";import a from"node:path";import{createStartCommand as n}from"./commands/start.js";import{createDaemonCommand as t}from"./commands/daemon.js";import{createWorkerAuthCommand as s}from"./commands/worker-auth.js";import{createWorkerCodexCommand as c}from"./commands/worker-codex.js";import{createWorkerProjectCommand as i}from"./commands/worker-project.js";import{DEFAULT_ACA_SERVER_URL as d}from"./core/aca-config.js";import{AICA_VERSION as p}from"./version.js";process.env.ACA_DEFAULT_SERVER_URL||=d;async function C(r=process.argv){const o=new m;o.name("aica").description("AICA remote Agent worker").version(p).enablePositionalOptions(),o.addCommand(n()),o.addCommand(t()),o.addCommand(s()),o.addCommand(i()),o.addCommand(c()),await o.parseAsync(r)}const A=process.argv[1]||"",e=a.basename(A).replace(/\.(?:js|ts|cjs|mjs)$/i,"");(e==="worker-cli"||e==="aica"||e==="aca")&&C().catch(r=>{const o=r instanceof Error?r.message:String(r);console.error(o),process.exitCode=1});export{C as runWorkerCli};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuandc/aica",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "AICA remote Agent worker",
5
5
  "author": "yuandc",
6
6
  "type": "module",