@yuandc/aica 0.1.67 → 0.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp/providers/codex-acp/adapter.js +3 -3
- package/dist/acp/providers/codex-acp/subtask-completion.js +1 -0
- package/dist/acp/providers/codex-acp/subtask-executions.js +1 -1
- package/dist/acp/providers/codex-acp/subtask-routing.js +1 -1
- package/dist/core/heartbeat.js +3 -3
- package/dist/core/project-sync.js +1 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createToolLifecycleTrace as x}from"../../tool-lifecycle-trace.js";import{JsonLineRpcClient as L}from"../../client/json-rpc.js";import{startCodexAppServer as $}from"../codex/codex-process.js";import{CodexConnectionRecoveryTracker as O}from"../codex/connection-events.js";import{codexAcpSessionUpdatesFromNotification as y}from"../codex/events.js";import{CodexPlanDeltaState as k}from"../codex/plan-events.js";import{isCodexApprovalRequest as j,resolveCodexApprovalRequest as B}from"../codex/permissions.js";import{buildCodexContinuityInstructions as K,codexTokenUsageFromRawResponseCompleted as W,codexTokenUsageFromNotification as z,decideCodexContextMaintenance as v,readCodexContextMaintenancePolicy as R,rolloutFileSize as G}from"./context-maintenance.js";import{codexNotificationBelongsToCurrentTurn as J,codexNotificationRouting as A}from"./notification-routing.js";import{completedSubtaskHistoryUpdates as H}from"./subtask-history.js";import{CodexSubtaskRouter as V}from"./subtask-routing.js";import{decorateSubtaskUpdate as P}from"./subtask-executions.js";import{
|
|
1
|
+
import{createToolLifecycleTrace as x}from"../../tool-lifecycle-trace.js";import{JsonLineRpcClient as L}from"../../client/json-rpc.js";import{startCodexAppServer as $}from"../codex/codex-process.js";import{CodexConnectionRecoveryTracker as O}from"../codex/connection-events.js";import{codexAcpSessionUpdatesFromNotification as y}from"../codex/events.js";import{CodexPlanDeltaState as k}from"../codex/plan-events.js";import{isCodexApprovalRequest as j,resolveCodexApprovalRequest as B}from"../codex/permissions.js";import{buildCodexContinuityInstructions as K,codexTokenUsageFromRawResponseCompleted as W,codexTokenUsageFromNotification as z,decideCodexContextMaintenance as v,readCodexContextMaintenancePolicy as R,rolloutFileSize as G}from"./context-maintenance.js";import{codexNotificationBelongsToCurrentTurn as J,codexNotificationRouting as A}from"./notification-routing.js";import{completedSubtaskHistoryUpdates as H}from"./subtask-history.js";import{CodexSubtaskRouter as V}from"./subtask-routing.js";import{decorateSubtaskUpdate as P}from"./subtask-executions.js";import{decorateSubtaskCompletion as X}from"./subtask-completion.js";import{waitForCancellableTurnStart as Q}from"./cancellation.js";import{codexAcpUpdateIsMeaningfulProgress as _,codexRawItemIsMeaningfulProgress as Y,withCodexProgressMetadata as M}from"./progress-evidence.js";import{acpPromptText as Z,acpPromptToCodexInput as tt,turnConfigFromConfig as et}from"./turn-input.js";import{CodexControlToolLoopDetector as st,codexControlToolLoopAcpUpdate as q,controlToolLoopDetectionThresholds as ot,controlToolLoopReady as it,readCodexControlToolLoopPolicy as nt}from"./control-tool-loop.js";import{CodexControlToolSteeringLimiter as rt,codexControlToolSteeringAcpUpdate as U}from"./control-tool-steering.js";class at{toolTrace=x();lineBuffer="";started=null;rpc=null;codexStartupPromise=null;initialized=!1;currentSessionId=null;currentCwd=process.cwd();promptActive=!1;currentTurnId=null;turnStartPromise=null;promptCancelled=!1;nextParentRequestId=1;pendingParentRequests=new Map;completedTurns=new Map;pendingTurnCompletions=new Map;agentMessagePhases=new Map;planDeltas=new k;subtaskRouter=new V;subtaskGeneration=0;subtaskAgentMessagePhases=new Map;subtaskPlanDeltas=new Map;subscribedSubtaskThreadIds=new Set;pendingSubtaskSubscriptions=new Map;hydratedSubtaskThreadIds=new Set;seenSubtaskItemIds=new Set;pendingSubtaskNotifications=[];config={};sessionAliases=new Map;currentAffectedFiles=new Set;connectionRecovery=new O;latestTokenUsage=null;currentRolloutPath=null;threadTurnCount=0;currentPromptText="";currentAssistantText="";previousUserText="";previousAssistantText="";previousAffectedFiles=new Set;maintenanceActive=!1;pendingCompaction=null;shutdownPromise=null;managedProvider=null;controlToolLoop;controlToolPolicy=nt();controlToolSteering=new rt(this.controlToolPolicy.steeringMaxAttempts);constructor(){this.controlToolLoop=new st({hasActiveSubtasks:()=>this.subtaskRouter.hasActiveSubtasks(),shortSleepMaxDurationMs:this.controlToolPolicy.shortSleepMaxDurationMs}),process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>this.handleInput(String(e)));const t=()=>{this.shutdown().finally(()=>process.exit(0))};process.stdin.once("end",t),process.stdin.once("close",t),process.stdin.once("error",t),process.once("SIGTERM",()=>{this.shutdown().finally(()=>process.exit(0))}),process.once("SIGINT",()=>{this.shutdown().finally(()=>process.exit(130))})}handleInput(t){this.lineBuffer+=t;const e=this.lineBuffer.split(`
|
|
2
2
|
`);this.lineBuffer=e.pop()??"";for(const s of e){const o=s.trim();if(!o)continue;let i;try{i=JSON.parse(o)}catch{continue}this.handleMessage(i)}}async handleMessage(t){if("id"in t&&("result"in t||"error"in t)&&typeof t.method!="string"){this.handleParentResponse(t);return}const e=typeof t.method=="string"?t.method:"",s=t.id;if(e){if(s===void 0){await this.handleNotification(e,a(t.params));return}try{const o=await this.handleRequest(e,a(t.params));this.write({jsonrpc:"2.0",id:s,result:o})}catch(o){this.write({jsonrpc:"2.0",id:s,error:{code:-32603,message:o instanceof Error?o.message:String(o)}})}}}async handleNotification(t,e){if(t==="session/cancel")try{await this.cancel(e)}catch(s){process.stderr.write(`Codex cancel failed: ${s instanceof Error?s.message:String(s)}
|
|
3
|
-
`),await this.shutdown(),process.exit(1)}}async handleRequest(t,e){switch(t){case"initialize":return this.initializeResponse();case"aca/runtime_config":return this.applyRuntimeConfig(e);case"session/new":return this.newSession(e);case"session/resume":case"session/load":return this.resumeSession(e);case"session/fork":return this.forkSession(e);case"session/prompt":return this.prompt(e);case"session/compact":return this.manualCompact(e);case"aca/session_recover":return this.recoverSession(e);case"session/cancel":return await this.cancel(e),{};case"session/close":return await this.closeSession(e),{};case"session/set_config_option":return this.setConfigOption(e);case"session/set_mode":return this.setMode(e);case"fs/read_text_file":case"fs/write_text_file":case"terminal/create":case"terminal/output":case"terminal/wait_for_exit":case"terminal/kill":case"terminal/release":return{};default:throw new Error(`Unsupported ACP method: ${t}`)}}initializeResponse(){return{protocolVersion:1,agentCapabilities:{loadSession:!0,sessionCapabilities:{resume:!0,close:!0}}}}applyRuntimeConfig(t){if(this.started)throw new Error("Codex runtime config must be applied before app-server starts");const e=a(t.codexProvider);if(Object.keys(e).length===0)return this.managedProvider=null,{configured:!1};const s=c(e,"baseUrl"),o=c(e,"apiKey"),i=Number(e.revision||0);if(!s||!o||!Number.isSafeInteger(i)||i<1)throw new Error("Invalid Codex managed provider runtime config");return this.managedProvider={baseUrl:s,apiKey:o,revision:i},{configured:!0,revision:i}}async newSession(t){const e=c(t,"cwd")||process.cwd();this.applyInitialConfig(t),await this.ensureCodex(e);const s=await this.requireRpc().request("thread/start",{cwd:e,approvalsReviewer:"user",...this.codexConfig(),ephemeral:!1,experimentalRawEvents:!0,threadSource:"aca-codex-acp"},6e4),o=T(s);if(!o)throw new Error("Codex thread/start did not return thread id");return this.currentSessionId=o,this.currentCwd=e,this.resetSubtaskState(),this.captureThreadMetadata(s),this.sessionResponse(o)}async resumeSession(t){const e=c(t,"sessionId");if(!e)throw new Error("sessionId is required");const s=c(t,"cwd")||this.currentCwd;this.applyInitialConfig(t),await this.ensureCodex(s);const o=await this.requireRpc().request("thread/resume",{threadId:e,cwd:s,approvalsReviewer:"user",...this.codexConfig()},6e4);return this.currentSessionId=T(o)||e,this.currentCwd=s,this.resetSubtaskState(o),await this.subscribeKnownSubtasks(!1),this.captureThreadMetadata(o),this.sessionResponse(this.currentSessionId)}async forkSession(t){const e=c(t,"sessionId");if(!e)throw new Error("sessionId is required");const s=c(t,"cwd")||this.currentCwd;this.applyInitialConfig(t),await this.ensureCodex(s);const o=await this.requireRpc().request("thread/fork",{threadId:e,cwd:s,approvalsReviewer:"user",ephemeral:!1,threadSource:"aca-codex-acp-fork",...this.codexConfig()},12e4),i=T(o);if(!i)throw new Error("Codex thread/fork did not return thread id");return this.currentSessionId=i,this.currentCwd=s,this.resetSubtaskState(o),this.captureThreadMetadata(o),this.sessionResponse(i)}async prompt(t){this.promptCancelled=!1;const e=c(t,"sessionId")||this.currentSessionId;let s=this.resolveSessionAlias(e);if(!s)throw new Error("sessionId is required");const o=Array.isArray(t.prompt)?t.prompt:[],i=this.requireRpc();this.currentSessionId&&this.currentSessionId!==s&&this.resetSubtaskState(),this.currentSessionId=s;const r=await this.maintainContext("before",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{allowRollover:!0});if(dt(r),this.promptCancelled||this.shutdownPromise)throw new Error("Codex prompt cancelled");s=this.currentSessionId||s,this.toolTrace=x(),this.promptActive=!0,this.currentTurnId=null,this.controlToolLoop.reset(),this.controlToolSteering.reset(),this.currentPromptText=Y(o),this.currentAssistantText="",this.currentAffectedFiles.clear(),this.connectionRecovery.reset(),this.agentMessagePhases.clear(),this.planDeltas.reset(),this.pendingSubtaskNotifications=[],this.flushSubtaskStates();let d;try{this.turnStartPromise=i.request("turn/start",{threadId:s,clientUserMessageId:`aca-acp-${Date.now()}-${Math.random().toString(16).slice(2)}`,input:Z(o),cwd:this.currentCwd,...tt(this.config)},D()),d=await this.turnStartPromise;const u=String(d?.turn?.id??"");this.currentTurnId=u||null;const l=u?await this.waitForTurnCompleted(u,D()):d?.turn,h=await this.subtaskRouter.waitForIdle(mt());if(h.timedOut&&h.activeThreadIds.length>0&&this.notifySessionUpdate({sessionUpdate:"aca_codex_subtask_drain",label:"子任务收尾等待超时",detail:`仍有 ${h.activeThreadIds.length} 个子任务未结束`,_meta:{aca:{subtaskDrain:h}}}),this.promptCancelled)throw new Error("Codex prompt cancelled");this.threadTurnCount+=1,this.previousUserText=this.currentPromptText,this.previousAssistantText=this.currentAssistantText,this.previousAffectedFiles.clear();for(const p of this.currentAffectedFiles)this.previousAffectedFiles.add(p);const f=await this.maintainContext("after",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{allowRollover:!0});return{stopReason:"end_turn",sessionId:this.currentSessionId,turn:l,_meta:{acaContextUsage:E(f)??ut(this.latestTokenUsage),acaContextMaintenance:f,acaSubtaskDrain:h}}}catch(u){const l=this.connectionRecovery.fail(u);throw l&&this.notifySessionUpdate(l),u}finally{this.subtaskRouter.executions.loseObservation(this.promptCancelled?"父任务已中断,子执行状态需重新确认":"本轮观测结束,未确认子执行终态"),this.flushSubtaskStates(),this.controlToolLoop.reset(),this.promptActive=!1,this.currentTurnId=null,this.turnStartPromise=null,this.pendingSubtaskNotifications=[]}}resetSubtaskState(t){this.subtaskGeneration+=1,this.subtaskRouter.reset(),this.subtaskAgentMessagePhases.clear(),this.subtaskPlanDeltas.clear(),this.subscribedSubtaskThreadIds.clear(),this.pendingSubtaskSubscriptions.clear(),this.hydratedSubtaskThreadIds.clear(),this.seenSubtaskItemIds.clear(),this.pendingSubtaskNotifications=[],t!==void 0&&this.subtaskRouter.restoreFromSessionSnapshot(t,this.currentSessionId)}async subscribeKnownSubtasks(t=!0){const e=this.subtaskRouter.routesForSubscription().map(s=>this.subscribeSubtask(s,t));await Promise.allSettled(e)}subscribeSubtask(t,e){const s=t.sourceThreadId;if(!s||s===this.currentSessionId||this.subscribedSubtaskThreadIds.has(s))return Promise.resolve();const o=this.pendingSubtaskSubscriptions.get(s);if(o)return o;const i=this.subtaskGeneration,r=this.requireRpc().request("thread/resume",{threadId:s,approvalsReviewer:"user",excludeTurns:!1},6e4).then(d=>{if(i!==this.subtaskGeneration)return;this.subscribedSubtaskThreadIds.add(s);const u=this.subtaskRouter.restoreFromSessionSnapshot(d,this.currentSessionId);e&&this.hydrateSubtaskHistory(d,t),u>0&&this.subscribeKnownSubtasks(e)}).catch(()=>{}).finally(()=>{this.pendingSubtaskSubscriptions.get(s)===r&&this.pendingSubtaskSubscriptions.delete(s)});return this.pendingSubtaskSubscriptions.set(s,r),r}async cancel(t){const e=this.resolveSessionAlias(c(t,"sessionId")||this.currentSessionId);if(!e||!this.rpc)return;if(e!==this.currentSessionId)throw new Error("Cannot cancel a different Codex session");this.promptCancelled=!0,this.subtaskRouter.stopWaiting();const s=this.turnStartPromise?await X(this.turnStartPromise):null,o=this.currentTurnId||c(w(s,"turn"),"id");if(await this.rpc.request("turn/interrupt",{threadId:e,turnId:o},5e3),o){const i=this.pendingTurnCompletions.get(o);i&&(this.pendingTurnCompletions.delete(o),clearTimeout(i.timer),i.reject(new Error("Codex prompt cancelled")))}}async manualCompact(t){const e=c(t,"sessionId")||this.currentSessionId,s=this.resolveSessionAlias(e);if(!s)throw new Error("sessionId is required");if(this.promptActive||this.maintenanceActive)throw new Error("Codex session is busy");this.currentSessionId=s;const o=R(),i=await this.maintainContext("manual",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{force:!0,allowRollover:t.allowRollover===!0,compactTimeoutMs:ct(t,o.compactTimeoutMs)}),r=String(i.action||"");if(r==="compact_failed"||r==="rollover_failed"){const d=String(i.compactError||"Codex 上下文压缩未完成");throw new Error(`Codex 上下文压缩失败:${d}`)}return{sessionId:this.currentSessionId,maintenance:i,contextUsage:E(i)}}async recoverSession(t){const e=c(t,"sessionId")||this.currentSessionId,s=this.resolveSessionAlias(e);if(!s)throw new Error("sessionId is required");const o=c(t,"reason");if(o!=="low_output_loop"&&o!=="control_tool_loop")throw new Error("Unsupported Codex recovery reason");if(this.promptActive||this.maintenanceActive)throw new Error("Codex session is busy");if(s!==this.currentSessionId)throw new Error("Cannot recover a different Codex session");const i=new Set([...this.previousAffectedFiles,...this.currentAffectedFiles]);return await this.rotateThread(s,{userRequest:this.currentPromptText||this.previousUserText,assistantResponse:this.currentAssistantText||this.previousAssistantText,affectedFiles:[...i]}),{...a(this.sessionResponse(this.currentSessionId)),previousSessionId:s,recoveryReason:o}}async closeSession(t){await this.shutdown()}async ensureCodex(t){if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;if(this.codexStartupPromise){await this.codexStartupPromise;return}const e=(async()=>{if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;const s=$(t,this.managedProvider),o=new L(s.child,{peerName:"Codex app-server",onNotification:(i,r)=>this.handleCodexNotification(i,r),onRequest:async(i,r)=>this.handleCodexRequest(i,r)});this.started=s,this.rpc=o,s.child.once("exit",()=>{this.started===s&&(this.subtaskRouter.executions.loseObservation("执行连接断开,未确认子代理终态"),this.flushSubtaskStates(),this.subtaskRouter.stopWaiting())}),this.initialized=!1,await o.request("initialize",{clientInfo:{name:"aca-codex-acp",version:"0.1.0"},capabilities:{experimentalApi:!0,requestAttestation:!1}},6e4),this.initialized=!0})();this.codexStartupPromise=e;try{await e}catch(s){throw this.rpc&&this.started&&!this.initialized&&await C(this.started.child,1e3),this.rpc=null,this.started=null,s}finally{this.codexStartupPromise===e&&(this.codexStartupPromise=null)}}handleCodexNotification(t,e){const s=A(e);if(t==="item/started"||t==="item/completed"){const u=a(a(e).item);this.toolTrace("provider_notification",{method:t,threadId:s.threadId,turnId:s.turnId,toolCallId:u.id,itemType:u.type,status:u.status})}if(this.maintenanceActive&&(!s.threadId||s.threadId===this.currentSessionId)){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,reason:"context_maintenance"}),this.captureContextNotification(t,e);return}if(!J(e,this.currentSessionId,this.currentTurnId)){if(!this.promptActive||!s.threadId||s.threadId===this.currentSessionId){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,reason:"outside_current_turn",threadId:s.threadId,turnId:s.turnId});return}const u=this.subtaskRouter.routeFor(s.threadId);if(!u){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_buffered",{method:t,toolCallId:a(a(e).item).id,threadId:s.threadId,reason:"subtask_route_pending"}),this.bufferPendingSubtaskNotification(t,e,s.threadId);return}this.handleSubtaskNotification(t,e,u),this.flushPendingSubtaskNotifications();return}const o=this.subtaskRouter.observe(e,this.currentSessionId);if(this.flushSubtaskStates(),o>0&&this.subscribeKnownSubtasks(!0),this.captureContextNotification(t,e),this.maintenanceActive)return;if(!this.promptActive&&t!=="turn/completed"){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,reason:"prompt_inactive"});return}this.rpc?.refreshPendingRequestTimeout("turn/start"),this.captureAgentMessagePhase(t,e);const i=this.connectionRecovery.beforeNotification(t,e);t==="turn/completed"&&this.resolveTurnCompleted(e);const r=t==="rawResponseItem/completed"?ht(this.controlToolLoop,e):[],d=t==="rawResponse/completed"?lt(e):t==="rawResponseItem/completed"?r.map(({update:u})=>u):y(t,e,{agentMessagePhases:this.agentMessagePhases,planDeltas:this.planDeltas});for(const u of r)this.maybeSteerControlToolLoop(u.event);for(const u of[...i,...d]){const l=M(u,t,e,this.agentMessagePhases);_(l)&&this.notifyControlToolProgress(),this.captureUpdateEvidence(l),this.notifySessionUpdate(l)}o>0&&this.flushPendingSubtaskNotifications()}handleSubtaskNotification(t,e,s){if(!this.promptActive)return;const o=this.subtaskRouter.acceptNotification(s,t,e);if(this.flushSubtaskStates(),!o){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,threadId:s.sourceThreadId,reason:"subtask_not_current"});return}(t==="rawResponseItem/completed"||t==="item/completed")&&Q(a(a(e).item))&&this.notifyControlToolProgress(),this.rememberSubtaskItem(s.sourceThreadId,e),this.rpc?.refreshPendingRequestTimeout("turn/start");const i=this.subtaskRouter.executions.get(s.sourceThreadId)?.runId||s.sourceThreadId,r=this.subtaskAgentMessagePhases.get(i)??new Map;this.subtaskAgentMessagePhases.set(i,r);const d=this.subtaskPlanDeltas.get(i)??new k;this.subtaskPlanDeltas.set(i,d),this.captureAgentMessagePhase(t,e,r);const u=this.subtaskRouter.observe(e,this.currentSessionId);this.flushSubtaskStates(),u>0&&this.subscribeKnownSubtasks(!0);const l=y(t,e,{agentMessagePhases:r,planDeltas:d}),h=A(e).turnId;for(const f of l){const p=M(f,t,e,r);if(!pt(p))continue;c(p,"title")&&this.subtaskRouter.executions.action(s.sourceThreadId,c(p,"title")),_(p)&&(this.notifyControlToolProgress(),this.subtaskRouter.executions.progress(s.sourceThreadId,c(p,"title")||"收到执行结果"));const g=P(p,s,h,this.subtaskRouter.executions.get(s.sourceThreadId));b(g,this.currentAffectedFiles),this.notifySessionUpdate(g)}}hydrateSubtaskHistory(t,e){if(!(!this.promptActive||this.hydratedSubtaskThreadIds.has(e.sourceThreadId))){this.hydratedSubtaskThreadIds.add(e.sourceThreadId);for(const s of H(t)){const o=N(e.sourceThreadId,s.itemId);if(this.seenSubtaskItemIds.has(o))continue;this.seenSubtaskItemIds.add(o);const i=P(s.update,e,s.sourceTurnId,{runId:`history.${e.sourceThreadId}.${s.sourceTurnId}`,state:"unknown",outputIncomplete:!0,reason:"历史过程回放,不代表当前执行状态"}),r=a(i._meta);i._meta={...r,aca:{...a(r.aca),historyReplay:!0}},b(i,this.currentAffectedFiles),this.notifySessionUpdate(i)}}}rememberSubtaskItem(t,e){const s=a(e),o=a(s.item),i=c(o,"id")||c(s,"itemId");i&&this.seenSubtaskItemIds.add(N(t,i))}bufferPendingSubtaskNotification(t,e,s){this.pendingSubtaskNotifications.push({method:t,params:e,threadId:s}),this.pendingSubtaskNotifications.length>200&&this.pendingSubtaskNotifications.shift()}flushPendingSubtaskNotifications(){if(this.pendingSubtaskNotifications.length===0)return;let t=!0;for(;t&&this.pendingSubtaskNotifications.length>0;){t=!1;const e=[];for(const s of this.pendingSubtaskNotifications){const o=this.subtaskRouter.routeFor(s.threadId);if(!o){e.push(s);continue}t=!0,this.handleSubtaskNotification(s.method,s.params,o)}this.pendingSubtaskNotifications=e}}notifySessionUpdate(t){(t.sessionUpdate==="tool_call"||t.sessionUpdate==="tool_call_update"&&["completed","failed","cancelled"].includes(String(t.status)))&&this.toolTrace("acp_update",{toolCallId:t.toolCallId,status:t.status,threadId:this.currentSessionId,turnId:this.currentTurnId}),this.notify("session/update",{sessionId:this.currentSessionId,update:t})}flushSubtaskStates(){if(this.promptActive)for(const t of this.subtaskRouter.executions.drainUpdates())this.notifySessionUpdate(t)}async handleCodexRequest(t,e){if(j(t))return B(t,e,async s=>{const o=await this.requestParent("session/request_permission",{sessionId:this.currentSessionId,request:{...s,params:e,method:t}}),i=a(o),r=a(i.outcome);return r.outcome==="selected"&&typeof r.optionId=="string"?{outcome:{outcome:"selected",optionId:r.optionId}}:{outcome:{outcome:"cancelled"}}});if(ft(t)){const s=await this.requestParent("elicitation/create",{sessionId:this.currentSessionId,request:{...a(e),method:t}}),o=a(s),i=a(o.outcome);return i.outcome==="submitted"?{outcome:{outcome:"submitted",value:typeof i.value=="string"?i.value:""}}:{outcome:{outcome:"cancelled"}}}return{}}captureAgentMessagePhase(t,e,s=this.agentMessagePhases){if(t!=="item/started"&&t!=="item/completed")return;const o=w(e,"item");if(c(o,"type")!=="agentMessage")return;const i=c(o,"id");i&&s.set(i,c(o,"phase"))}waitForTurnCompleted(t,e){const s=this.completedTurns.get(t);return s?Promise.resolve(s):new Promise((o,i)=>{const r=setTimeout(()=>{this.pendingTurnCompletions.delete(t),i(new Error("Codex app-server request timed out while waiting for turn/completed"))},e);r.unref(),this.pendingTurnCompletions.set(t,{resolve:o,reject:i,timer:r})})}resolveTurnCompleted(t){const e=w(t,"turn"),s=c(e,"id");if(!s)return;this.completedTurns.set(s,e);const o=this.pendingTurnCompletions.get(s);if(o){if(this.pendingTurnCompletions.delete(s),clearTimeout(o.timer),c(e,"status")==="failed"){o.reject(new Error(`Codex turn failed: ${JSON.stringify(e.error??{})}`));return}o.resolve(e)}}async shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.subtaskRouter.executions.loseObservation("执行连接关闭,未确认子代理终态"),this.flushSubtaskStates(),this.subtaskRouter.stopWaiting(),this.pendingCompaction&&(clearTimeout(this.pendingCompaction.timer),this.pendingCompaction.reject(new Error("Codex ACP adapter closed")),this.pendingCompaction=null);for(const e of this.pendingParentRequests.values())clearTimeout(e.timer),e.reject(new Error("Codex ACP adapter closed"));this.pendingParentRequests.clear();for(const e of this.pendingTurnCompletions.values())clearTimeout(e.timer),e.reject(new Error("Codex ACP adapter closed"));this.pendingTurnCompletions.clear();const t=this.started?.child;!t||t.exitCode!==null||t.signalCode!==null||(t.stdin.end(),!await C(t,2e3)&&(t.kill("SIGTERM"),!await C(t,1e3)&&(t.kill("SIGKILL"),await C(t,1e3))))}sessionResponse(t){return{sessionId:t,modes:{currentModeId:"workspace-write",availableModes:[{id:"read-only",name:"Read Only"},{id:"workspace-write",name:"Workspace Write"},{id:"danger-full-access",name:"Full Access"}]},configOptions:[{id:"model",category:"model",type:"string"},{id:"approvalPolicy",category:"approval",type:"string"},{id:"sandbox",category:"mode",type:"string"}]}}requireRpc(){if(!this.rpc)throw new Error("Codex app-server is not started");return this.rpc}notify(t,e){this.write({jsonrpc:"2.0",method:t,params:e})}requestParent(t,e,s=600*1e3){const o=this.nextParentRequestId++;return new Promise((i,r)=>{const d=setTimeout(()=>{this.pendingParentRequests.delete(o),r(new Error(`ACP client request timed out: ${t}`))},s);d.unref(),this.pendingParentRequests.set(o,{resolve:i,reject:r,timer:d}),this.write({jsonrpc:"2.0",id:o,method:t,params:e})})}handleParentResponse(t){const e=Number(t.id),s=this.pendingParentRequests.get(e);if(s){if(this.pendingParentRequests.delete(e),clearTimeout(s.timer),"error"in t){const o=a(t.error);s.reject(new Error(c(o,"message")||"ACP client request failed"));return}s.resolve(t.result)}}setConfigOption(t){const e=c(t,"configId"),s=t.value;if(!e)throw new Error("configId is required");if(typeof s!="string"&&typeof s!="boolean")throw new Error("config value must be string or boolean");return this.config[e]=s,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"config_option_update",configId:e,value:s}}),{}}setMode(t){const e=c(t,"modeId");if(!e)throw new Error("modeId is required");return this.config.sandbox=e,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"current_mode_update",modeId:e}}),{}}applyInitialConfig(t){const e=w(t,"config");for(const[s,o]of Object.entries(e))(typeof o=="string"||typeof o=="boolean")&&(this.config[s]=o)}codexConfig(){return{...typeof this.config.model=="string"&&this.config.model?{model:this.config.model}:{},approvalPolicy:typeof this.config.approvalPolicy=="string"?this.config.approvalPolicy:"on-request",sandbox:typeof this.config.sandbox=="string"?this.config.sandbox:"workspace-write"}}captureContextNotification(t,e){if(t==="thread/tokenUsage/updated"&&(this.latestTokenUsage=z(e)??this.latestTokenUsage),t==="thread/compacted"&&this.pendingCompaction&&this.resolveCompaction(),t==="item/completed"){const s=w(e,"item"),o=c(a(e),"threadId");c(s,"type")==="contextCompaction"&&(!o||o===this.currentSessionId)&&this.resolveCompaction(),c(s,"type")==="agentMessage"&&c(s,"phase")==="final_answer"&&(this.currentAssistantText=S(s)||this.currentAssistantText)}}resolveCompaction(){if(!this.pendingCompaction)return;const t=this.pendingCompaction;this.pendingCompaction=null,clearTimeout(t.timer),t.resolve()}captureUpdateEvidence(t){String(t.sessionUpdate||"")==="agent_message_chunk"&&(this.currentAssistantText+=S(t.content)),b(t,this.currentAffectedFiles)}captureThreadMetadata(t){const e=w(t,"thread");this.currentRolloutPath=c(e,"path")||null,this.threadTurnCount=Array.isArray(e.turns)?e.turns.length:0;const s=at(e.turns);s.userRequest&&(this.previousUserText=s.userRequest),s.assistantResponse&&(this.previousAssistantText=s.assistantResponse),this.previousAffectedFiles.clear();for(const o of s.affectedFiles)this.previousAffectedFiles.add(o);this.latestTokenUsage=null}async maintainContext(t,e,s={}){const o=R(),i=this.contextState(),r=v(i,o);if(!r.compact&&!s.force||!this.currentSessionId)return this.maintenanceResult({stage:t,action:"none",reasons:r.reasons,beforeState:i});const d=this.currentSessionId;this.notifyContextStatus("正在整理会话上下文",F(i)),this.maintenanceActive=!0;let u=!1,l="",h=i,f=r,p=!1,g="";try{const m=i.usage?.observedAtMs??0;await this.compactThread(d,s.compactTimeoutMs??o.compactTimeoutMs),await this.waitForTokenUsageAfter(m,2e3)}catch(m){u=!0,l=m instanceof Error?m.message:String(m)}try{if(t!=="manual"&&this.promptCancelled||this.shutdownPromise)throw new Error("Codex prompt cancelled");if(h=this.contextState(),f=v(h,o,{compactAttempted:!0,compactFailed:u}),f.rollover&&s.allowRollover!==!1){this.notifyContextStatus("正在创建轻量会话",f.reasons.join(", "));try{await this.rotateThread(d,e),p=!0}catch(m){g=m instanceof Error?m.message:String(m),this.notifyContextStatus("轻量会话创建失败",g)}}else u||this.notifyContextStatus("会话上下文已压缩",F(h))}finally{this.maintenanceActive=!1}return this.maintenanceResult({action:p?"rollover":g?"rollover_failed":u?"compact_failed":"compact",stage:t,reasons:f.reasons,beforeState:i,afterState:h,compactError:[l,g].filter(Boolean).join("; "),previousThreadId:d,rolloverRecommended:f.rollover&&s.allowRollover===!1})}compactThread(t,e){return this.pendingCompaction?Promise.reject(new Error("Codex context compaction is already running")):new Promise((s,o)=>{const i=setTimeout(()=>{this.pendingCompaction?.timer===i&&(this.pendingCompaction=null),o(new Error("Codex thread compaction timed out"))},e);i.unref(),this.pendingCompaction={resolve:s,reject:o,timer:i},this.requireRpc().request("thread/compact/start",{threadId:t},e).catch(r=>{this.pendingCompaction?.timer===i&&(this.pendingCompaction=null,clearTimeout(i),o(r instanceof Error?r:new Error(String(r))))})})}async rotateThread(t,e){const s=K({previousThreadId:t,cwd:this.currentCwd,userRequest:e.userRequest,assistantResponse:e.assistantResponse,affectedFiles:e.affectedFiles}),o=await this.requireRpc().request("thread/start",{cwd:this.currentCwd,approvalsReviewer:"user",...this.codexConfig(),developerInstructions:s,ephemeral:!1,experimentalRawEvents:!0,threadSource:"aca-codex-acp-rollover"},6e4),i=T(o);if(!i)throw new Error("Codex rollover thread/start did not return thread id");this.sessionAliases.set(t,i),this.currentSessionId=i,this.resetSubtaskState(o),this.captureThreadMetadata(o)}async waitForTokenUsageAfter(t,e){const s=Date.now()+e;for(;Date.now()<s;){if((this.latestTokenUsage?.observedAtMs??0)>t)return;await gt(50)}}resolveSessionAlias(t){if(!t)return null;let e=t;const s=new Set;for(;this.sessionAliases.has(e)&&!s.has(e);)s.add(e),e=this.sessionAliases.get(e)??e;return e}contextState(){return{usage:this.latestTokenUsage,rolloutBytes:G(this.currentRolloutPath),turnCount:this.threadTurnCount}}maintenanceResult(t){return{stage:t.stage,action:t.action,reasons:t.reasons,previousThreadId:t.previousThreadId??null,sessionId:this.currentSessionId,before:t.beforeState,after:t.afterState??null,compactError:t.compactError||null,rolloverRecommended:t.rolloverRecommended===!0}}notifyContextStatus(t,e){this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"aca_context_maintenance",label:t,detail:e}})}notifyControlToolProgress(){const t=this.controlToolLoop.recordProgress();t&&this.notifySessionUpdate(q(t))}maybeSteerControlToolLoop(t){if(!ot(t.detection,this.controlToolPolicy))return;const e=st(t.detection,this.controlToolPolicy),s=this.controlToolSteering.next(t,e.warningCalls);if(!s)return;const{threadId:o,turnId:i}=t.detection;!o||!i||this.requireRpc().request("turn/steer",{threadId:o,expectedTurnId:i,clientUserMessageId:`aca-loop-steer-${Date.now()}-${Math.random().toString(16).slice(2)}`,input:[{type:"text",text:s.text,text_elements:[]}]},6e4).then(()=>{this.notifySessionUpdate(U(t,s,{succeeded:!0}))}).catch(r=>{this.notifySessionUpdate(U(t,s,{succeeded:!1,error:r}))})}write(t){process.stdout.write(`${JSON.stringify(t)}
|
|
4
|
-
`)}}function T(n){const t=w(n,"thread");return c(t,"id")||null}function w(n,t){const e=a(n);return a(e[t])}function a(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:{}}function S(n){if(typeof n=="string")return n;if(Array.isArray(n))return n.map(S).filter(Boolean).join("");if(!n||typeof n!="object")return"";const t=a(n);for(const e of["text","message","content","output_text"]){const s=S(t[e]);if(s)return s}return""}function b(n,t){if(Array.isArray(n)){for(const i of n)b(i,t);return}const e=a(n),s=c(e,"type").toLowerCase(),o=c(e,"path");if(o&&["diff","resource_link","content"].includes(s)&&t.add(o),Array.isArray(e.locations))for(const i of e.locations){const r=c(a(i),"path");r&&t.add(r)}for(const i of[e.content,e.update,e.rawInput,e.rawOutput])i&&i!==n&&b(i,t)}function
|
|
3
|
+
`),await this.shutdown(),process.exit(1)}}async handleRequest(t,e){switch(t){case"initialize":return this.initializeResponse();case"aca/runtime_config":return this.applyRuntimeConfig(e);case"session/new":return this.newSession(e);case"session/resume":case"session/load":return this.resumeSession(e);case"session/fork":return this.forkSession(e);case"session/prompt":return this.prompt(e);case"session/compact":return this.manualCompact(e);case"aca/session_recover":return this.recoverSession(e);case"session/cancel":return await this.cancel(e),{};case"session/close":return await this.closeSession(e),{};case"session/set_config_option":return this.setConfigOption(e);case"session/set_mode":return this.setMode(e);case"fs/read_text_file":case"fs/write_text_file":case"terminal/create":case"terminal/output":case"terminal/wait_for_exit":case"terminal/kill":case"terminal/release":return{};default:throw new Error(`Unsupported ACP method: ${t}`)}}initializeResponse(){return{protocolVersion:1,agentCapabilities:{loadSession:!0,sessionCapabilities:{resume:!0,close:!0}}}}applyRuntimeConfig(t){if(this.started)throw new Error("Codex runtime config must be applied before app-server starts");const e=a(t.codexProvider);if(Object.keys(e).length===0)return this.managedProvider=null,{configured:!1};const s=c(e,"baseUrl"),o=c(e,"apiKey"),i=Number(e.revision||0);if(!s||!o||!Number.isSafeInteger(i)||i<1)throw new Error("Invalid Codex managed provider runtime config");return this.managedProvider={baseUrl:s,apiKey:o,revision:i},{configured:!0,revision:i}}async newSession(t){const e=c(t,"cwd")||process.cwd();this.applyInitialConfig(t),await this.ensureCodex(e);const s=await this.requireRpc().request("thread/start",{cwd:e,approvalsReviewer:"user",...this.codexConfig(),ephemeral:!1,experimentalRawEvents:!0,threadSource:"aca-codex-acp"},6e4),o=T(s);if(!o)throw new Error("Codex thread/start did not return thread id");return this.currentSessionId=o,this.currentCwd=e,this.resetSubtaskState(),this.captureThreadMetadata(s),this.sessionResponse(o)}async resumeSession(t){const e=c(t,"sessionId");if(!e)throw new Error("sessionId is required");const s=c(t,"cwd")||this.currentCwd;this.applyInitialConfig(t),await this.ensureCodex(s);const o=await this.requireRpc().request("thread/resume",{threadId:e,cwd:s,approvalsReviewer:"user",...this.codexConfig()},6e4);return this.currentSessionId=T(o)||e,this.currentCwd=s,this.resetSubtaskState(o),await this.subscribeKnownSubtasks(!1),this.captureThreadMetadata(o),this.sessionResponse(this.currentSessionId)}async forkSession(t){const e=c(t,"sessionId");if(!e)throw new Error("sessionId is required");const s=c(t,"cwd")||this.currentCwd;this.applyInitialConfig(t),await this.ensureCodex(s);const o=await this.requireRpc().request("thread/fork",{threadId:e,cwd:s,approvalsReviewer:"user",ephemeral:!1,threadSource:"aca-codex-acp-fork",...this.codexConfig()},12e4),i=T(o);if(!i)throw new Error("Codex thread/fork did not return thread id");return this.currentSessionId=i,this.currentCwd=s,this.resetSubtaskState(o),this.captureThreadMetadata(o),this.sessionResponse(i)}async prompt(t){this.promptCancelled=!1;const e=c(t,"sessionId")||this.currentSessionId;let s=this.resolveSessionAlias(e);if(!s)throw new Error("sessionId is required");const o=Array.isArray(t.prompt)?t.prompt:[],i=this.requireRpc();this.currentSessionId&&this.currentSessionId!==s&&this.resetSubtaskState(),this.currentSessionId=s;const r=await this.maintainContext("before",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{allowRollover:!0});if(lt(r),this.promptCancelled||this.shutdownPromise)throw new Error("Codex prompt cancelled");s=this.currentSessionId||s,this.toolTrace=x(),this.promptActive=!0,this.currentTurnId=null,this.controlToolLoop.reset(),this.controlToolSteering.reset(),this.currentPromptText=Z(o),this.currentAssistantText="",this.currentAffectedFiles.clear(),this.connectionRecovery.reset(),this.agentMessagePhases.clear(),this.planDeltas.reset(),this.pendingSubtaskNotifications=[],this.flushSubtaskStates();let d;try{this.turnStartPromise=i.request("turn/start",{threadId:s,clientUserMessageId:`aca-acp-${Date.now()}-${Math.random().toString(16).slice(2)}`,input:tt(o),cwd:this.currentCwd,...et(this.config)},D()),d=await this.turnStartPromise;const u=String(d?.turn?.id??"");this.currentTurnId=u||null;const l=u?await this.waitForTurnCompleted(u,D()):d?.turn,h=await this.subtaskRouter.waitForIdle(gt());if(h.timedOut&&h.activeThreadIds.length>0&&this.notifySessionUpdate({sessionUpdate:"aca_codex_subtask_drain",label:"子任务收尾等待超时",detail:`仍有 ${h.activeThreadIds.length} 个子任务未结束`,_meta:{aca:{subtaskDrain:h}}}),this.promptCancelled)throw new Error("Codex prompt cancelled");this.threadTurnCount+=1,this.previousUserText=this.currentPromptText,this.previousAssistantText=this.currentAssistantText,this.previousAffectedFiles.clear();for(const p of this.currentAffectedFiles)this.previousAffectedFiles.add(p);const f=await this.maintainContext("after",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{allowRollover:!0});return{stopReason:"end_turn",sessionId:this.currentSessionId,turn:l,_meta:{acaContextUsage:E(f)??dt(this.latestTokenUsage),acaContextMaintenance:f,acaSubtaskDrain:h}}}catch(u){const l=this.connectionRecovery.fail(u);throw l&&this.notifySessionUpdate(l),u}finally{this.subtaskRouter.executions.loseObservation(this.promptCancelled?"父任务已中断,子执行状态需重新确认":"本轮观测结束,未确认子执行终态"),this.flushSubtaskStates(),this.controlToolLoop.reset(),this.promptActive=!1,this.currentTurnId=null,this.turnStartPromise=null,this.pendingSubtaskNotifications=[]}}resetSubtaskState(t){this.subtaskGeneration+=1,this.subtaskRouter.reset(),this.subtaskAgentMessagePhases.clear(),this.subtaskPlanDeltas.clear(),this.subscribedSubtaskThreadIds.clear(),this.pendingSubtaskSubscriptions.clear(),this.hydratedSubtaskThreadIds.clear(),this.seenSubtaskItemIds.clear(),this.pendingSubtaskNotifications=[],t!==void 0&&this.subtaskRouter.restoreFromSessionSnapshot(t,this.currentSessionId)}async subscribeKnownSubtasks(t=!0){const e=this.subtaskRouter.routesForSubscription().map(s=>this.subscribeSubtask(s,t));await Promise.allSettled(e)}subscribeSubtask(t,e){const s=t.sourceThreadId;if(!s||s===this.currentSessionId||this.subscribedSubtaskThreadIds.has(s))return Promise.resolve();const o=this.pendingSubtaskSubscriptions.get(s);if(o)return o;const i=this.subtaskGeneration,r=this.requireRpc().request("thread/resume",{threadId:s,approvalsReviewer:"user",excludeTurns:!1},6e4).then(d=>{if(i!==this.subtaskGeneration)return;this.subscribedSubtaskThreadIds.add(s);const u=this.subtaskRouter.restoreFromSessionSnapshot(d,this.currentSessionId);e&&this.hydrateSubtaskHistory(d,t),u>0&&this.subscribeKnownSubtasks(e)}).catch(()=>{}).finally(()=>{this.pendingSubtaskSubscriptions.get(s)===r&&this.pendingSubtaskSubscriptions.delete(s)});return this.pendingSubtaskSubscriptions.set(s,r),r}async cancel(t){const e=this.resolveSessionAlias(c(t,"sessionId")||this.currentSessionId);if(!e||!this.rpc)return;if(e!==this.currentSessionId)throw new Error("Cannot cancel a different Codex session");this.promptCancelled=!0,this.subtaskRouter.stopWaiting();const s=this.turnStartPromise?await Q(this.turnStartPromise):null,o=this.currentTurnId||c(w(s,"turn"),"id");if(await this.rpc.request("turn/interrupt",{threadId:e,turnId:o},5e3),o){const i=this.pendingTurnCompletions.get(o);i&&(this.pendingTurnCompletions.delete(o),clearTimeout(i.timer),i.reject(new Error("Codex prompt cancelled")))}}async manualCompact(t){const e=c(t,"sessionId")||this.currentSessionId,s=this.resolveSessionAlias(e);if(!s)throw new Error("sessionId is required");if(this.promptActive||this.maintenanceActive)throw new Error("Codex session is busy");this.currentSessionId=s;const o=R(),i=await this.maintainContext("manual",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{force:!0,allowRollover:t.allowRollover===!0,compactTimeoutMs:ut(t,o.compactTimeoutMs)}),r=String(i.action||"");if(r==="compact_failed"||r==="rollover_failed"){const d=String(i.compactError||"Codex 上下文压缩未完成");throw new Error(`Codex 上下文压缩失败:${d}`)}return{sessionId:this.currentSessionId,maintenance:i,contextUsage:E(i)}}async recoverSession(t){const e=c(t,"sessionId")||this.currentSessionId,s=this.resolveSessionAlias(e);if(!s)throw new Error("sessionId is required");const o=c(t,"reason");if(o!=="low_output_loop"&&o!=="control_tool_loop")throw new Error("Unsupported Codex recovery reason");if(this.promptActive||this.maintenanceActive)throw new Error("Codex session is busy");if(s!==this.currentSessionId)throw new Error("Cannot recover a different Codex session");const i=new Set([...this.previousAffectedFiles,...this.currentAffectedFiles]);return await this.rotateThread(s,{userRequest:this.currentPromptText||this.previousUserText,assistantResponse:this.currentAssistantText||this.previousAssistantText,affectedFiles:[...i]}),{...a(this.sessionResponse(this.currentSessionId)),previousSessionId:s,recoveryReason:o}}async closeSession(t){await this.shutdown()}async ensureCodex(t){if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;if(this.codexStartupPromise){await this.codexStartupPromise;return}const e=(async()=>{if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;const s=$(t,this.managedProvider),o=new L(s.child,{peerName:"Codex app-server",onNotification:(i,r)=>this.handleCodexNotification(i,r),onRequest:async(i,r)=>this.handleCodexRequest(i,r)});this.started=s,this.rpc=o,s.child.once("exit",()=>{this.started===s&&(this.subtaskRouter.executions.loseObservation("执行连接断开,未确认子代理终态"),this.flushSubtaskStates(),this.subtaskRouter.stopWaiting())}),this.initialized=!1,await o.request("initialize",{clientInfo:{name:"aca-codex-acp",version:"0.1.0"},capabilities:{experimentalApi:!0,requestAttestation:!1}},6e4),this.initialized=!0})();this.codexStartupPromise=e;try{await e}catch(s){throw this.rpc&&this.started&&!this.initialized&&await C(this.started.child,1e3),this.rpc=null,this.started=null,s}finally{this.codexStartupPromise===e&&(this.codexStartupPromise=null)}}handleCodexNotification(t,e){const s=A(e);if(t==="item/started"||t==="item/completed"){const u=a(a(e).item);this.toolTrace("provider_notification",{method:t,threadId:s.threadId,turnId:s.turnId,toolCallId:u.id,itemType:u.type,status:u.status})}if(this.maintenanceActive&&(!s.threadId||s.threadId===this.currentSessionId)){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,reason:"context_maintenance"}),this.captureContextNotification(t,e);return}if(!J(e,this.currentSessionId,this.currentTurnId)){if(!this.promptActive||!s.threadId||s.threadId===this.currentSessionId){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,reason:"outside_current_turn",threadId:s.threadId,turnId:s.turnId});return}const u=this.subtaskRouter.routeFor(s.threadId);if(!u){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_buffered",{method:t,toolCallId:a(a(e).item).id,threadId:s.threadId,reason:"subtask_route_pending"}),this.bufferPendingSubtaskNotification(t,e,s.threadId);return}this.handleSubtaskNotification(t,e,u),this.flushPendingSubtaskNotifications();return}const o=this.subtaskRouter.observe(e,this.currentSessionId);if(this.flushSubtaskStates(),o>0&&this.subscribeKnownSubtasks(!0),this.captureContextNotification(t,e),this.maintenanceActive)return;if(!this.promptActive&&t!=="turn/completed"){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,reason:"prompt_inactive"});return}this.rpc?.refreshPendingRequestTimeout("turn/start"),this.captureAgentMessagePhase(t,e);const i=this.connectionRecovery.beforeNotification(t,e);t==="turn/completed"&&this.resolveTurnCompleted(e);const r=t==="rawResponseItem/completed"?pt(this.controlToolLoop,e):[],d=t==="rawResponse/completed"?ht(e):t==="rawResponseItem/completed"?r.map(({update:u})=>u):y(t,e,{agentMessagePhases:this.agentMessagePhases,planDeltas:this.planDeltas});for(const u of r)this.maybeSteerControlToolLoop(u.event);for(const u of[...i,...d]){const l=M(u,t,e,this.agentMessagePhases);_(l)&&this.notifyControlToolProgress(),this.captureUpdateEvidence(l),this.notifySessionUpdate(l)}o>0&&this.flushPendingSubtaskNotifications()}handleSubtaskNotification(t,e,s){if(!this.promptActive)return;const o=this.subtaskRouter.acceptNotification(s,t,e);if(this.flushSubtaskStates(),!o){(t==="item/started"||t==="item/completed")&&this.toolTrace("notification_filtered",{method:t,toolCallId:a(a(e).item).id,threadId:s.sourceThreadId,reason:"subtask_not_current"});return}(t==="rawResponseItem/completed"||t==="item/completed")&&Y(a(a(e).item))&&this.notifyControlToolProgress(),this.rememberSubtaskItem(s.sourceThreadId,e),this.rpc?.refreshPendingRequestTimeout("turn/start");const i=this.subtaskRouter.executions.get(s.sourceThreadId)?.runId||s.sourceThreadId,r=this.subtaskAgentMessagePhases.get(i)??new Map;this.subtaskAgentMessagePhases.set(i,r);const d=this.subtaskPlanDeltas.get(i)??new k;this.subtaskPlanDeltas.set(i,d),this.captureAgentMessagePhase(t,e,r);const u=this.subtaskRouter.observe(e,this.currentSessionId);this.flushSubtaskStates(),u>0&&this.subscribeKnownSubtasks(!0);const l=y(t,e,{agentMessagePhases:r,planDeltas:d}),h=A(e).turnId;for(const f of l){const p=M(f,t,e,r);if(!ft(p))continue;c(p,"title")&&this.subtaskRouter.executions.action(s.sourceThreadId,c(p,"title")),_(p)&&(this.notifyControlToolProgress(),this.subtaskRouter.executions.progress(s.sourceThreadId,c(p,"title")||"收到执行结果"));const g=P(p,s,h,this.subtaskRouter.executions.get(s.sourceThreadId));b(g,this.currentAffectedFiles),this.notifySessionUpdate(g)}}hydrateSubtaskHistory(t,e){if(!(!this.promptActive||this.hydratedSubtaskThreadIds.has(e.sourceThreadId))){this.hydratedSubtaskThreadIds.add(e.sourceThreadId);for(const s of H(t)){const o=N(e.sourceThreadId,s.itemId);if(this.seenSubtaskItemIds.has(o))continue;this.seenSubtaskItemIds.add(o);const i=P(s.update,e,s.sourceTurnId,{runId:`history.${e.sourceThreadId}.${s.sourceTurnId}`,state:"unknown",name:this.subtaskRouter.executions.nameFor(e.sourceThreadId),outputIncomplete:!0,reason:"历史过程回放,不代表当前执行状态"}),r=a(i._meta);i._meta={...r,aca:{...a(r.aca),historyReplay:!0}},b(i,this.currentAffectedFiles),this.notifySessionUpdate(i)}}}rememberSubtaskItem(t,e){const s=a(e),o=a(s.item),i=c(o,"id")||c(s,"itemId");i&&this.seenSubtaskItemIds.add(N(t,i))}bufferPendingSubtaskNotification(t,e,s){this.pendingSubtaskNotifications.push({method:t,params:e,threadId:s}),this.pendingSubtaskNotifications.length>200&&this.pendingSubtaskNotifications.shift()}flushPendingSubtaskNotifications(){if(this.pendingSubtaskNotifications.length===0)return;let t=!0;for(;t&&this.pendingSubtaskNotifications.length>0;){t=!1;const e=[];for(const s of this.pendingSubtaskNotifications){const o=this.subtaskRouter.routeFor(s.threadId);if(!o){e.push(s);continue}t=!0,this.handleSubtaskNotification(s.method,s.params,o)}this.pendingSubtaskNotifications=e}}notifySessionUpdate(t){t=X(t,this.subtaskRouter.executions),(t.sessionUpdate==="tool_call"||t.sessionUpdate==="tool_call_update"&&["completed","failed","cancelled"].includes(String(t.status)))&&this.toolTrace("acp_update",{toolCallId:t.toolCallId,status:t.status,threadId:this.currentSessionId,turnId:this.currentTurnId}),this.notify("session/update",{sessionId:this.currentSessionId,update:t})}flushSubtaskStates(){if(this.promptActive)for(const t of this.subtaskRouter.executions.drainUpdates())this.notifySessionUpdate(t)}async handleCodexRequest(t,e){if(j(t))return B(t,e,async s=>{const o=await this.requestParent("session/request_permission",{sessionId:this.currentSessionId,request:{...s,params:e,method:t}}),i=a(o),r=a(i.outcome);return r.outcome==="selected"&&typeof r.optionId=="string"?{outcome:{outcome:"selected",optionId:r.optionId}}:{outcome:{outcome:"cancelled"}}});if(mt(t)){const s=await this.requestParent("elicitation/create",{sessionId:this.currentSessionId,request:{...a(e),method:t}}),o=a(s),i=a(o.outcome);return i.outcome==="submitted"?{outcome:{outcome:"submitted",value:typeof i.value=="string"?i.value:""}}:{outcome:{outcome:"cancelled"}}}return{}}captureAgentMessagePhase(t,e,s=this.agentMessagePhases){if(t!=="item/started"&&t!=="item/completed")return;const o=w(e,"item");if(c(o,"type")!=="agentMessage")return;const i=c(o,"id");i&&s.set(i,c(o,"phase"))}waitForTurnCompleted(t,e){const s=this.completedTurns.get(t);return s?Promise.resolve(s):new Promise((o,i)=>{const r=setTimeout(()=>{this.pendingTurnCompletions.delete(t),i(new Error("Codex app-server request timed out while waiting for turn/completed"))},e);r.unref(),this.pendingTurnCompletions.set(t,{resolve:o,reject:i,timer:r})})}resolveTurnCompleted(t){const e=w(t,"turn"),s=c(e,"id");if(!s)return;this.completedTurns.set(s,e);const o=this.pendingTurnCompletions.get(s);if(o){if(this.pendingTurnCompletions.delete(s),clearTimeout(o.timer),c(e,"status")==="failed"){o.reject(new Error(`Codex turn failed: ${JSON.stringify(e.error??{})}`));return}o.resolve(e)}}async shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.subtaskRouter.executions.loseObservation("执行连接关闭,未确认子代理终态"),this.flushSubtaskStates(),this.subtaskRouter.stopWaiting(),this.pendingCompaction&&(clearTimeout(this.pendingCompaction.timer),this.pendingCompaction.reject(new Error("Codex ACP adapter closed")),this.pendingCompaction=null);for(const e of this.pendingParentRequests.values())clearTimeout(e.timer),e.reject(new Error("Codex ACP adapter closed"));this.pendingParentRequests.clear();for(const e of this.pendingTurnCompletions.values())clearTimeout(e.timer),e.reject(new Error("Codex ACP adapter closed"));this.pendingTurnCompletions.clear();const t=this.started?.child;!t||t.exitCode!==null||t.signalCode!==null||(t.stdin.end(),!await C(t,2e3)&&(t.kill("SIGTERM"),!await C(t,1e3)&&(t.kill("SIGKILL"),await C(t,1e3))))}sessionResponse(t){return{sessionId:t,modes:{currentModeId:"workspace-write",availableModes:[{id:"read-only",name:"Read Only"},{id:"workspace-write",name:"Workspace Write"},{id:"danger-full-access",name:"Full Access"}]},configOptions:[{id:"model",category:"model",type:"string"},{id:"approvalPolicy",category:"approval",type:"string"},{id:"sandbox",category:"mode",type:"string"}]}}requireRpc(){if(!this.rpc)throw new Error("Codex app-server is not started");return this.rpc}notify(t,e){this.write({jsonrpc:"2.0",method:t,params:e})}requestParent(t,e,s=600*1e3){const o=this.nextParentRequestId++;return new Promise((i,r)=>{const d=setTimeout(()=>{this.pendingParentRequests.delete(o),r(new Error(`ACP client request timed out: ${t}`))},s);d.unref(),this.pendingParentRequests.set(o,{resolve:i,reject:r,timer:d}),this.write({jsonrpc:"2.0",id:o,method:t,params:e})})}handleParentResponse(t){const e=Number(t.id),s=this.pendingParentRequests.get(e);if(s){if(this.pendingParentRequests.delete(e),clearTimeout(s.timer),"error"in t){const o=a(t.error);s.reject(new Error(c(o,"message")||"ACP client request failed"));return}s.resolve(t.result)}}setConfigOption(t){const e=c(t,"configId"),s=t.value;if(!e)throw new Error("configId is required");if(typeof s!="string"&&typeof s!="boolean")throw new Error("config value must be string or boolean");return this.config[e]=s,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"config_option_update",configId:e,value:s}}),{}}setMode(t){const e=c(t,"modeId");if(!e)throw new Error("modeId is required");return this.config.sandbox=e,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"current_mode_update",modeId:e}}),{}}applyInitialConfig(t){const e=w(t,"config");for(const[s,o]of Object.entries(e))(typeof o=="string"||typeof o=="boolean")&&(this.config[s]=o)}codexConfig(){return{...typeof this.config.model=="string"&&this.config.model?{model:this.config.model}:{},approvalPolicy:typeof this.config.approvalPolicy=="string"?this.config.approvalPolicy:"on-request",sandbox:typeof this.config.sandbox=="string"?this.config.sandbox:"workspace-write"}}captureContextNotification(t,e){if(t==="thread/tokenUsage/updated"&&(this.latestTokenUsage=z(e)??this.latestTokenUsage),t==="thread/compacted"&&this.pendingCompaction&&this.resolveCompaction(),t==="item/completed"){const s=w(e,"item"),o=c(a(e),"threadId");c(s,"type")==="contextCompaction"&&(!o||o===this.currentSessionId)&&this.resolveCompaction(),c(s,"type")==="agentMessage"&&c(s,"phase")==="final_answer"&&(this.currentAssistantText=S(s)||this.currentAssistantText)}}resolveCompaction(){if(!this.pendingCompaction)return;const t=this.pendingCompaction;this.pendingCompaction=null,clearTimeout(t.timer),t.resolve()}captureUpdateEvidence(t){String(t.sessionUpdate||"")==="agent_message_chunk"&&(this.currentAssistantText+=S(t.content)),b(t,this.currentAffectedFiles)}captureThreadMetadata(t){const e=w(t,"thread");this.currentRolloutPath=c(e,"path")||null,this.threadTurnCount=Array.isArray(e.turns)?e.turns.length:0;const s=ct(e.turns);s.userRequest&&(this.previousUserText=s.userRequest),s.assistantResponse&&(this.previousAssistantText=s.assistantResponse),this.previousAffectedFiles.clear();for(const o of s.affectedFiles)this.previousAffectedFiles.add(o);this.latestTokenUsage=null}async maintainContext(t,e,s={}){const o=R(),i=this.contextState(),r=v(i,o);if(!r.compact&&!s.force||!this.currentSessionId)return this.maintenanceResult({stage:t,action:"none",reasons:r.reasons,beforeState:i});const d=this.currentSessionId;this.notifyContextStatus("正在整理会话上下文",F(i)),this.maintenanceActive=!0;let u=!1,l="",h=i,f=r,p=!1,g="";try{const m=i.usage?.observedAtMs??0;await this.compactThread(d,s.compactTimeoutMs??o.compactTimeoutMs),await this.waitForTokenUsageAfter(m,2e3)}catch(m){u=!0,l=m instanceof Error?m.message:String(m)}try{if(t!=="manual"&&this.promptCancelled||this.shutdownPromise)throw new Error("Codex prompt cancelled");if(h=this.contextState(),f=v(h,o,{compactAttempted:!0,compactFailed:u}),f.rollover&&s.allowRollover!==!1){this.notifyContextStatus("正在创建轻量会话",f.reasons.join(", "));try{await this.rotateThread(d,e),p=!0}catch(m){g=m instanceof Error?m.message:String(m),this.notifyContextStatus("轻量会话创建失败",g)}}else u||this.notifyContextStatus("会话上下文已压缩",F(h))}finally{this.maintenanceActive=!1}return this.maintenanceResult({action:p?"rollover":g?"rollover_failed":u?"compact_failed":"compact",stage:t,reasons:f.reasons,beforeState:i,afterState:h,compactError:[l,g].filter(Boolean).join("; "),previousThreadId:d,rolloverRecommended:f.rollover&&s.allowRollover===!1})}compactThread(t,e){return this.pendingCompaction?Promise.reject(new Error("Codex context compaction is already running")):new Promise((s,o)=>{const i=setTimeout(()=>{this.pendingCompaction?.timer===i&&(this.pendingCompaction=null),o(new Error("Codex thread compaction timed out"))},e);i.unref(),this.pendingCompaction={resolve:s,reject:o,timer:i},this.requireRpc().request("thread/compact/start",{threadId:t},e).catch(r=>{this.pendingCompaction?.timer===i&&(this.pendingCompaction=null,clearTimeout(i),o(r instanceof Error?r:new Error(String(r))))})})}async rotateThread(t,e){const s=K({previousThreadId:t,cwd:this.currentCwd,userRequest:e.userRequest,assistantResponse:e.assistantResponse,affectedFiles:e.affectedFiles}),o=await this.requireRpc().request("thread/start",{cwd:this.currentCwd,approvalsReviewer:"user",...this.codexConfig(),developerInstructions:s,ephemeral:!1,experimentalRawEvents:!0,threadSource:"aca-codex-acp-rollover"},6e4),i=T(o);if(!i)throw new Error("Codex rollover thread/start did not return thread id");this.sessionAliases.set(t,i),this.currentSessionId=i,this.resetSubtaskState(o),this.captureThreadMetadata(o)}async waitForTokenUsageAfter(t,e){const s=Date.now()+e;for(;Date.now()<s;){if((this.latestTokenUsage?.observedAtMs??0)>t)return;await wt(50)}}resolveSessionAlias(t){if(!t)return null;let e=t;const s=new Set;for(;this.sessionAliases.has(e)&&!s.has(e);)s.add(e),e=this.sessionAliases.get(e)??e;return e}contextState(){return{usage:this.latestTokenUsage,rolloutBytes:G(this.currentRolloutPath),turnCount:this.threadTurnCount}}maintenanceResult(t){return{stage:t.stage,action:t.action,reasons:t.reasons,previousThreadId:t.previousThreadId??null,sessionId:this.currentSessionId,before:t.beforeState,after:t.afterState??null,compactError:t.compactError||null,rolloverRecommended:t.rolloverRecommended===!0}}notifyContextStatus(t,e){this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"aca_context_maintenance",label:t,detail:e}})}notifyControlToolProgress(){const t=this.controlToolLoop.recordProgress();t&&this.notifySessionUpdate(q(t))}maybeSteerControlToolLoop(t){if(!it(t.detection,this.controlToolPolicy))return;const e=ot(t.detection,this.controlToolPolicy),s=this.controlToolSteering.next(t,e.warningCalls);if(!s)return;const{threadId:o,turnId:i}=t.detection;!o||!i||this.requireRpc().request("turn/steer",{threadId:o,expectedTurnId:i,clientUserMessageId:`aca-loop-steer-${Date.now()}-${Math.random().toString(16).slice(2)}`,input:[{type:"text",text:s.text,text_elements:[]}]},6e4).then(()=>{this.notifySessionUpdate(U(t,s,{succeeded:!0}))}).catch(r=>{this.notifySessionUpdate(U(t,s,{succeeded:!1,error:r}))})}write(t){process.stdout.write(`${JSON.stringify(t)}
|
|
4
|
+
`)}}function T(n){const t=w(n,"thread");return c(t,"id")||null}function w(n,t){const e=a(n);return a(e[t])}function a(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:{}}function S(n){if(typeof n=="string")return n;if(Array.isArray(n))return n.map(S).filter(Boolean).join("");if(!n||typeof n!="object")return"";const t=a(n);for(const e of["text","message","content","output_text"]){const s=S(t[e]);if(s)return s}return""}function b(n,t){if(Array.isArray(n)){for(const i of n)b(i,t);return}const e=a(n),s=c(e,"type").toLowerCase(),o=c(e,"path");if(o&&["diff","resource_link","content"].includes(s)&&t.add(o),Array.isArray(e.locations))for(const i of e.locations){const r=c(a(i),"path");r&&t.add(r)}for(const i of[e.content,e.update,e.rawInput,e.rawOutput])i&&i!==n&&b(i,t)}function ct(n){if(!Array.isArray(n)||n.length===0)return{userRequest:"",assistantResponse:"",affectedFiles:[]};const t=n.at(-1),e=new Set;b(t,e);let s="",o="";const i=r=>{if(Array.isArray(r)){for(const l of r)i(l);return}if(!r||typeof r!="object")return;const d=a(r),u=c(d,"type").toLowerCase();u==="usermessage"&&(s=S(d)||s),u==="agentmessage"&&(o=S(d)||o);for(const l of["items","content","input"])d[l]&&i(d[l])};return i(t),{userRequest:s,assistantResponse:o,affectedFiles:[...e]}}function ut(n,t){const e=I(n,"timeoutMs");return!Number.isFinite(e)||e<=0?t:Math.max(3e4,Math.min(59e4,e-1e4))}function E(n){const t=String(n.action||"none"),e=a(n.after),s=a(n.before),o=a(e.usage),i=a(s.usage),r=Object.keys(o).length>0?o:i,d=I(r,"contextWindow");if(t==="rollover")return{usedTokens:0,inputTokens:0,contextWindow:d,ratio:0,observedAtMs:Date.now(),state:"new_thread",maintenanceAction:t};if(Object.keys(r).length===0)return null;const u=I(r,"totalTokens"),l=I(r,"inputTokens");return{usedTokens:u||l,inputTokens:l,contextWindow:d,ratio:d>0?(u||l)/d:0,observedAtMs:I(r,"observedAtMs")||Date.now(),state:t==="compact"?"compacted":"active",maintenanceAction:t}}function dt(n){return n?{usedTokens:n.totalTokens||n.inputTokens,inputTokens:n.inputTokens,contextWindow:n.contextWindow,ratio:n.contextWindow>0?(n.totalTokens||n.inputTokens)/n.contextWindow:0,observedAtMs:n.observedAtMs,state:"active",maintenanceAction:null}:null}function F(n){const t=n.usage?`${n.usage.inputTokens} tokens`:"token unknown",e=n.rolloutBytes>0?`${Math.round(n.rolloutBytes/1024/1024)}MB rollout`:"rollout unknown";return`${t}, ${e}, ${n.turnCount} turns`}function lt(n){const t=String(n.action||"none");if(t!=="compact_failed"&&t!=="rollover_failed")return;const e=String(n.compactError||"Codex 上下文维护未完成");throw new Error(`Codex 自动上下文维护失败:${e}`)}function I(n,t){const e=a(n)[t];return typeof e=="number"&&Number.isFinite(e)?e:0}function ht(n){return W(n)?[{sessionUpdate:"aca_codex_raw_response_usage",_meta:{codex:{rawResponse:n}}}]:[]}function pt(n,t){const e=n.observeRawResponseItem(t);return e?[{event:e,update:q(e)}]:[]}function ft(n){return["agent_message_chunk","agent_progress_chunk","agent_thought_chunk","tool_call","tool_call_update","plan","plan_update","plan_removed"].includes(String(n.sessionUpdate||""))}function N(n,t){return`${n}:${t}`}function c(n,t){const s=a(n)[t];return typeof s=="string"?s:""}function mt(n){const t=n.toLowerCase();return t.includes("elicitation")||t.includes("requestuserinput")||t.includes("request_user_input")||t.includes("input/request")}function D(){const n=Number.parseInt(process.env.ACA_ACP_PROMPT_TIMEOUT_MS??"",10);return Number.isInteger(n)&&n>=3e4?n:720*60*1e3}function gt(){const n=Number.parseInt(process.env.ACA_CODEX_SUBTASK_DRAIN_TIMEOUT_MS??"",10);return Number.isInteger(n)&&n>=1e3?Math.min(n,600*1e3):600*1e3}function C(n,t){return n.exitCode!==null||n.signalCode!==null?Promise.resolve(!0):new Promise(e=>{const s=setTimeout(()=>e(!1),t);s.unref(),n.once("close",()=>{clearTimeout(s),e(!0)})})}function wt(n){return new Promise(t=>setTimeout(t,n))}function St(){new at}const bt=process.argv[1]||"";/(?:^|[\\/])adapter\.(?:js|ts)$/.test(bt)&&St();export{St as runCodexAcpAdapter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const r=t=>t&&typeof t=="object"?t:{};function n(t,a){const c=r(t._meta),e=r(c.aca),o=r(e.task);if(e.subtask||e.action!=="subAgentActivity"||o.status!=="completed"||typeof o.sourceThreadId!="string")return t;const s=a.completionRunId(o.sourceThreadId);return s?{...t,_meta:{...c,aca:{...e,subtaskCompletion:{threadId:o.sourceThreadId,runId:s}}}}:t}export{n as decorateSubtaskCompletion};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{randomUUID as f}from"node:crypto";import{codexNotificationIsSubtaskMetadata as g}from"./notification-routing.js";class
|
|
1
|
+
import{randomUUID as f}from"node:crypto";import{codexNotificationIsSubtaskMetadata as g}from"./notification-routing.js";class T{runs=new Map;starts=new Set;closedTurns=new Map;pending=new Map;names=new Map;reset(){this.runs.clear(),this.starts.clear(),this.closedTurns.clear(),this.pending.clear(),this.names.clear()}get(s){return this.runs.get(s)?.snapshot}completionRunId(s){const n=this.runs.get(s);return n?.generation===1&&n.snapshot.state==="completed"?n.snapshot.runId:void 0}nameFor(s){return this.names.get(s)}rememberName(s,n){const t=n.trim();if(!t)return;this.names.set(s,t);const r=this.runs.get(s);r&&!r.snapshot.name&&this.change(r,{name:t})}hasStart(s,n){return this.starts.has(`${s}:${n}`)}activeThreadIds(){return[...this.runs].filter(([,s])=>h(s.snapshot.state)).map(([s])=>s)}start(s,n,t,r={}){r.name&&this.rememberName(s.sourceThreadId,r.name);const o={...r};delete o.name;const i=this.nameFor(s.sourceThreadId);i&&(o.name=i);const e=`${s.sourceThreadId}:${n}`;if(this.starts.has(e))return;this.starts.add(e);const u=this.runs.get(s.sourceThreadId);if(u&&h(u.snapshot.state)){this.change(u,{...o,state:u.snapshot.state==="running"?"running":t});return}const d={route:{...s},generation:(u?.generation??0)+1,snapshot:{...o,runId:f(),state:t,startedAtMs:Date.now()}};this.runs.set(s.sourceThreadId,d),this.pending.set(d.snapshot.runId,d)}observeState(s,n){let t=this.runs.get(s.sourceThreadId);if(t){if(c(t.snapshot.state))return}else{const r=this.nameFor(s.sourceThreadId);t={route:{...s},generation:1,snapshot:{runId:f(),state:"unknown",...r?{name:r}:{}}},this.runs.set(s.sourceThreadId,t),this.pending.set(t.snapshot.runId,t)}if(!(t.snapshot.state==="running"&&n==="pending")){if(t.generation>1&&c(n)){this.change(t,{state:"unknown",reason:t.outputRiskReason??"收到无法确认执行归属的结束通知"});return}this.change(t,{state:n,...c(n)?{endedAtMs:Date.now()}:{}})}}accept(s,n,t){if(g(n))return!1;const r=s.sourceThreadId,o=l(t.turn),i=p(t.turnId)||p(o.id);if(i&&this.closedTurns.get(r)?.has(i))return!1;let e=this.runs.get(r);if(n==="turn/started"&&i&&(e?.snapshot.sourceTurnId&&e.snapshot.sourceTurnId!==i&&h(e.snapshot.state)&&(this.closeTurn(r,e.snapshot.sourceTurnId),this.markOutputRisk(e,"新执行开始前未收到旧执行结束事件","unknown")),this.start(s,`turn:${i}`,"running"),e=this.runs.get(r)),e||(this.observeState(s,"unknown"),e=this.runs.get(r)),c(e.snapshot.state)||i&&e.snapshot.sourceTurnId&&i!==e.snapshot.sourceTurnId)return!1;if(e.generation>1&&!e.snapshot.sourceTurnId&&n!=="turn/started")return this.markOutputRisk(e,`部分事件缺少新执行归属,已隔离(${n.slice(0,120)})`),!1;if(!i&&e.generation>1&&n!=="turn/started")return this.markOutputRisk(e,`部分事件缺少新执行归属,已隔离(${n.slice(0,120)})`),!1;if(i&&!e.snapshot.sourceTurnId&&this.change(e,{sourceTurnId:i}),n==="turn/completed"){const u=p(o.status).toLowerCase(),d=u==="failed"?"failed":["interrupted","cancelled","canceled"].includes(u)?"interrupted":u==="completed"?"completed":"unknown";d==="unknown"?this.markOutputRisk(e,"结束通知未提供可识别的执行结果",d):this.change(e,{state:d,endedAtMs:Date.now(),reason:e.outputRiskReason}),i&&this.closeTurn(r,i)}return!0}progress(s,n){const t=this.runs.get(s);!t||c(t.snapshot.state)||(t.snapshot={...t.snapshot,lastProgressAtMs:Date.now(),...n?{lastAction:n}:{}})}action(s,n){const t=this.runs.get(s);t&&!c(t.snapshot.state)&&(t.snapshot={...t.snapshot,lastAction:n})}loseObservation(s){for(const n of this.runs.values())(h(n.snapshot.state)||n.snapshot.state==="unknown"&&n.snapshot.startedAtMs!==void 0)&&this.markOutputRisk(n,s,"unknown")}drainUpdates(){const s=[...this.pending.values()].map(({route:n,snapshot:t})=>I({sessionUpdate:"tool_call_update",toolCallId:`run.${t.runId}`,kind:"other",title:"子代理执行状态",status:t.state==="failed"?"failed":t.state==="interrupted"?"cancelled":t.state==="completed"?"completed":"in_progress",_meta:{aca:{subtaskLifecycle:!0,processKind:"subtask"}}},n,t.sourceTurnId||null,t));return this.pending.clear(),s}markOutputRisk(s,n,t){s.outputRiskReason??=n,this.change(s,{outputIncomplete:!0,reason:s.outputRiskReason,...t?{state:t}:{}})}closeTurn(s,n){const t=this.closedTurns.get(s)??new Set;t.add(n),this.closedTurns.set(s,t)}change(s,n){const t={...s.snapshot,...n};JSON.stringify(t)!==JSON.stringify(s.snapshot)&&(c(t.state)&&t.sourceTurnId&&this.closeTurn(s.route.sourceThreadId,t.sourceTurnId),s.snapshot=t,this.pending.set(t.runId,s))}}function I(a,s,n,t){const r={...a},o=t?.runId||s.sourceThreadId;["tool_call","tool_call_update"].includes(String(a.sessionUpdate))&&a.toolCallId&&(r.toolCallId=`subtask.${o}.${a.toolCallId}`),["plan","plan_update","plan_removed"].includes(String(a.sessionUpdate))&&(r.planId=`subtask.${o}.${a.planId||a.plan_id||a.id||"current"}`,delete r.plan_id);const i=l(a._meta);return r._meta={...i,aca:{...l(i.aca),subtask:{...s,sourceTurnId:n,...t?{runId:t.runId,run:{...t}}:{}}}},r}function c(a){return["completed","failed","interrupted"].includes(a)}function h(a){return a==="pending"||a==="running"}function l(a){return a&&typeof a=="object"?a:{}}function p(a){return typeof a=="string"?a:""}export{T as CodexSubtaskExecutions,I as decorateSubtaskUpdate};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{CodexSubtaskExecutions as A}from"./subtask-executions.js";class O{routes=new Map;executions=new A;idleWaiters=new Set;reset(){this.routes.clear(),this.executions.reset(),this.resolveIdleWaiters(!1)}routeFor(t){return t?this.routes.get(t)??null:null}routesForSubscription(){return[...this.routes.values()]}activeThreadIds(){return this.executions.activeThreadIds()}hasActiveSubtasks(){return this.activeThreadIds().length>0}waitForIdle(t){return this.activeThreadIds().length===0?Promise.resolve({timedOut:!1,activeThreadIds:[]}):new Promise(s=>{const i={resolve:s,timer:setTimeout(()=>{this.idleWaiters.delete(i),s({timedOut:!0,activeThreadIds:this.activeThreadIds()})},t)};i.timer.unref(),this.idleWaiters.add(i)})}stopWaiting(){this.resolveIdleWaiters(!1)}acceptNotification(t,e,s){const i=this.executions.accept(t,e,c(s));return this.hasActiveSubtasks()||this.resolveIdleWaiters(!1),i}restoreFromSessionSnapshot(t,e){const s=c(t),i=c(s.thread),n=a(i,"id")||e,o=c(s.initialTurnsPage),
|
|
1
|
+
import{CodexSubtaskExecutions as A}from"./subtask-executions.js";class O{routes=new Map;executions=new A;idleWaiters=new Set;reset(){this.routes.clear(),this.executions.reset(),this.resolveIdleWaiters(!1)}routeFor(t){return t?this.routes.get(t)??null:null}routesForSubscription(){return[...this.routes.values()]}activeThreadIds(){return this.executions.activeThreadIds()}hasActiveSubtasks(){return this.activeThreadIds().length>0}waitForIdle(t){return this.activeThreadIds().length===0?Promise.resolve({timedOut:!1,activeThreadIds:[]}):new Promise(s=>{const i={resolve:s,timer:setTimeout(()=>{this.idleWaiters.delete(i),s({timedOut:!0,activeThreadIds:this.activeThreadIds()})},t)};i.timer.unref(),this.idleWaiters.add(i)})}stopWaiting(){this.resolveIdleWaiters(!1)}acceptNotification(t,e,s){const i=this.executions.accept(t,e,c(s));return this.hasActiveSubtasks()||this.resolveIdleWaiters(!1),i}restoreFromSessionSnapshot(t,e){const s=c(t),i=c(s.thread),n=a(i,"id")||e,o=c(s.initialTurnsPage),u=[...m(i,"turns"),...m(o,"data")];let l=0;for(const h of u){const f=c(h);for(const p of m(f,"items"))l+=this.observe({threadId:n,item:p},e,!0)}return l}observe(t,e,s=!1){const i=c(t),n=c(i.item);if(a(n,"type")==="subAgentActivity")return this.observeSubAgentActivity(n,a(i,"threadId"),e,s);if(!k(n))return 0;const o=a(n,"id");if(!o)return 0;const u=a(n,"senderThreadId")||a(i,"threadId"),l=this.routeFor(u||null),h=l?.parentToolCallId||o,f=Math.min(8,(l?.depth??0)+1),p=x(n),g=w(n.agentsStates);let T=0;for(const d of p){if(!d||d===e||d===u)continue;this.routes.has(d)||(this.routes.set(d,{parentToolCallId:h,sourceThreadId:d,depth:f}),T+=1);const S=g.get(d);if(!s){if(a(n,"tool")==="spawnAgent"){const v=this.executions.get(d);!this.executions.hasStart(d,o)&&(!v||!["pending","running"].includes(v.state))&&this.routes.set(d,{parentToolCallId:h,sourceThreadId:d,depth:f}),this.executions.start(this.routes.get(d),o,"pending",{description:a(n,"prompt")||void 0})}S?this.updateStatus(d,S):a(n,"tool")==="spawnAgent"&&this.updateStatus(d,"pending")}}return T}observeSubAgentActivity(t,e,s,i){const n=a(t,"kind"),o=a(t,"agentThreadId");if(!o||o===s)return 0;const u=a(t,"agentPath").split("/").filter(Boolean).at(-1);if(u&&this.executions.rememberName(o,u),!i&&n==="started"&&this.executions.hasStart(o,a(t,"id")))return 0;if(n==="completed"||n==="interrupted")return i||this.updateStatus(o,n==="completed"?"completed":"interrupted"),0;if(n!=="started"&&n!=="interacted")return 0;const l=this.routeFor(e||null),h=l?.parentToolCallId||a(t,"id"),f=Math.min(8,(l?.depth??0)+1);if(!h)return 0;const p=this.routes.has(o)?0:1;if(p&&this.routes.set(o,{parentToolCallId:h,sourceThreadId:o,depth:f}),i)return p;if(n==="started"){const g=this.executions.get(o);(!g||!["pending","running"].includes(g.state))&&this.routes.set(o,{parentToolCallId:h,sourceThreadId:o,depth:f}),this.executions.start(this.routes.get(o),a(t,"id"),"running",{...u?{name:u}:{}}),this.updateStatus(o,"running")}return p}updateStatus(t,e){const s=this.routes.get(t);s&&this.executions.observeState(s,e),this.hasActiveSubtasks()||this.resolveIdleWaiters(!1)}resolveIdleWaiters(t){const e=this.activeThreadIds();for(const s of this.idleWaiters)clearTimeout(s.timer),s.resolve({timedOut:t,activeThreadIds:e});this.idleWaiters.clear()}}function w(r){const t=new Map,e=c(r);for(const[s,i]of Object.entries(e)){const n=y(a(c(i),"status"));s.trim()&&n!=="unknown"&&t.set(s.trim(),n)}return t}function y(r){switch(r.toLowerCase()){case"pending":case"pendinginit":return"pending";case"running":case"inprogress":case"in_progress":return"running";case"completed":case"shutdown":return"completed";case"failed":case"errored":case"notfound":return"failed";case"cancelled":case"canceled":case"interrupted":return"interrupted";default:return"unknown"}}function x(r){const t=c(r),e=new Set;I(e,t.newThreadId),I(e,t.receiverThreadId),C(e,t.receiverThreadIds);const s=t.agentsStates;if(Array.isArray(s))for(const i of s)b(e,i);else{const i=c(s);for(const[n,o]of Object.entries(i))b(e,o),W(n)&&e.add(n)}return[...e]}function k(r){const t=a(c(r),"type");return t==="collabAgentToolCall"||t==="collabToolCall"}function b(r,t){const e=c(t);for(const s of["threadId","thread_id","receiverThreadId","newThreadId"])I(r,e[s])}function I(r,t){typeof t=="string"&&t.trim()&&r.add(t.trim())}function C(r,t){if(Array.isArray(t))for(const e of t)I(r,e)}function m(r,t){const e=r[t];return Array.isArray(e)?e:[]}function W(r){return r.length<8?!1:!["pending","running","completed","failed","cancelled","canceled"].includes(r.toLowerCase())}function c(r){return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}function a(r,t){const e=r[t];return typeof e=="string"?e:""}export{O as CodexSubtaskRouter,x as collaborationChildThreadIds,k as isCollaborationItem};
|
package/dist/core/heartbeat.js
CHANGED
|
@@ -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";import{AICA_VERSION as _}from"../version.js";const b=3e4,
|
|
2
|
-
`));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:
|
|
3
|
-
`));if(!r)continue;const l=M(r.name,r.id,a)??a;o.push({modelId:a,displayName:
|
|
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";import{applyProjectSync as k}from"./project-sync.js";const b=3e4,E=360*60*1e3,S=360*60*1e3;let p=null,u=null;async function D(e=C(),t={}){if(!e.token)return{ok:!1,skipped:!0,reason:"not_logged_in"};const o=await x("POST","/api/heartbeat",{machineId:e.machineId,machineName:e.machineName,machineInfo:{platform:m.platform(),architecture:m.arch(),osRelease:m.release(),acaVersion:V(),hostname:m.hostname()},workspaceId:e.defaultWorkspaceId,agentConfigs:t.includeProviderDetails===!1?[]:await N(e),projects:e.projects.map(n=>({projectId:n.projectId,workspaceId:n.workspaceId,name:n.name,rootPath:n.rootPath}))});return k(e,o),o}async function N(e){const t=w(),o=await O(),n=o?.capabilities??j();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:H()}},{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:$()}}]}function V(e=process.env){return e.ACA_VERSION?.trim()||_}function j(){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 H(){const e=Date.now();if(p&&p.expiresAtMs>e)return p.capabilities;const t=z()??R();return p={expiresAtMs:e+E,capabilities:t},t}function T(){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 $(){const e=Date.now();if(u&&u.expiresAtMs>e)return u.capabilities;const t=L()??T();return u={expiresAtMs:e+S,capabilities:t},t}function L(){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=P(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 P(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:U(l,r),description:W(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 U(e,t){return B(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function B(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 W(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 z(){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=J(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=q(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 J(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:X(l,r),description:K(r),metadata:r})}return o}function q(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:Q(a),description:a==="high"?"More reasoning for difficult tasks":a==="max"?"Maximum reasoning when supported":"Fastest responses"}))}function X(e,t){return G(t)&&!/\bfree\b/i.test(e)?`${e} (free)`:e}function G(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 K(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 Q(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 ce(e){let t=!1;const o=async(i,s="heartbeat")=>{const c=new Date().toISOString();try{const r=await D(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,V as reportedAicaVersion,D as sendHeartbeat,ce as startHeartbeatLoop};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{loadAcaConfig as p,saveAcaConfig as u}from"./aca-config.js";function l(a,d){if(!Array.isArray(d.projectSync))return;const t=p();if(t.token!==a.token||t.serverUrl!==a.serverUrl||t.machineId!==a.machineId)return;const n=new Map(a.projects.map(e=>[e.projectId,e])),I=new Map(d.projectSync.filter(e=>n.has(e.projectId)).map(e=>[e.projectId,e])),o=t.projects.flatMap(e=>{const r=I.get(e.projectId),c=n.get(e.projectId);return!r||!c||e.rootPath!==c.rootPath||e.workspaceId!==c.workspaceId||e.name!==c.name?[e]:r.status==="deleted"?[]:r.status!=="active"||typeof r.workspaceId!="string"||!r.workspaceId?[e]:[{...e,workspaceId:r.workspaceId,name:typeof r.name=="string"?r.name:e.name}]}),f=o.some(e=>e.projectId===t.defaultProjectId)?t.defaultProjectId:o[0]?.projectId,s=t.defaultWorkspaceId===a.defaultWorkspaceId&&d.defaultWorkspaceId?d.defaultWorkspaceId:t.defaultWorkspaceId;JSON.stringify(o)===JSON.stringify(t.projects)&&f===t.defaultProjectId&&s===t.defaultWorkspaceId||u({...t,projects:o,defaultProjectId:f,defaultWorkspaceId:s})}export{l as applyProjectSync};
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.1.
|
|
1
|
+
const o="0.1.69";export{o as AICA_VERSION};
|