@yuandc/aica 0.1.17 → 0.1.19
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/client/acp-events.js +1 -1
- package/dist/acp/error-display.js +1 -0
- package/dist/acp/providers/codex/connection-events.js +1 -0
- package/dist/acp/providers/codex/events.js +15 -13
- package/dist/acp/providers/codex-acp/adapter.js +4 -4
- package/dist/core/job-worker.js +2 -2
- package/dist/worker-cli.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createPlanEvent as h,normalizePlanSessionUpdate as
|
|
1
|
+
import{createPlanEvent as h,normalizePlanSessionUpdate as y}from"../plan-events.js";import{STANDARD_AGENT_EVENT_TYPES as _,createAgentMessageChunk as m,createAgentProgressChunk as A,createAgentThoughtChunk as b,createToolCall as x,createToolCallUpdate as k,firstString as o,normalizeToolKind as C,normalizeToolStatus as f}from"../standard-events.js";function g(n){if(!n||typeof n!="object"||Array.isArray(n))return null;const t=n,e=t.update;return!e||typeof e!="object"||Array.isArray(e)?null:{sessionId:typeof t.sessionId=="string"?t.sessionId:void 0,update:e}}function E(n){const t=g(n);if(!t)return null;const e=String(t.update.sessionUpdate||"");if(e==="agent_message_chunk")return m({text:s(t.update.content),method:"session/update",params:n});if(e==="agent_progress_chunk")return A({text:s(t.update.content),method:"session/update",params:n});if(e==="agent_thought_chunk")return b({text:s(t.update.content),method:"session/update",params:n});if(e==="tool_call"||e==="tool_call_update")return I(e,t.update,n);if(e==="plan"||e==="plan_update"||e==="plan_removed"){const r=y(t.update);return r?h({update:r,method:"session/update",params:n}):null}return _.has(e)?{type:e,label:w(e),...s(t.update.content)?{text:s(t.update.content)}:{},raw:{method:"session/update",params:n,update:t.update},atMs:Date.now()}:null}function F(n){const t=g(n);if(!t)return null;const e=String(t.update.sessionUpdate||"");if(e==="agent_message_chunk")return{phase:"thinking",label:"回复中",detail:p(s(t.update.content)),updateType:e};if(e==="agent_progress_chunk")return{phase:"thinking",label:"处理中",detail:p(s(t.update.content)),updateType:e};if(e==="agent_thought_chunk")return{phase:"thinking",label:"思考中",detail:p(s(t.update.content)),updateType:e};if(e==="tool_call"||e==="tool_call_update"){const r=o(t.update.toolCallId,t.update.id),a=f(o(t.update.status)),i=o(t.update.title,t.update.kind),u=S(t.update.rawOutput);if(r==="codex.connection")return{phase:a==="failed"?"failed":a==="completed"?"exploring":"resuming",label:i||(a==="completed"?"Codex 连接已恢复":"Codex 正在重连"),detail:u,updateType:e};if(r==="codex.error")return{phase:"failed",label:i||"Codex 执行异常",detail:u,updateType:e};const c=T(t.update.rawInput),l=o(c?.command);return i==="压缩上下文"?{phase:"thinking",label:"压缩上下文",updateType:e}:/^(?:启动|发送|恢复|等待|结束|处理)子任务/.test(i||"")?{phase:"exploring",label:i||"处理子任务",updateType:e}:l?{phase:"exploring",label:"执行命令",detail:i||l,updateType:e}:{phase:"exploring",label:"调用工具",detail:i,updateType:e}}return e==="plan"||e==="plan_update"?{phase:"thinking",label:"规划中",updateType:e}:e==="aca_context_maintenance"?{phase:"thinking",label:o(t.update.label)||"正在整理会话上下文",detail:o(t.update.detail),updateType:e}:null}function s(n){if(typeof n=="string")return n;if(!n||typeof n!="object"||Array.isArray(n))return"";const t=n;return typeof t.text=="string"?t.text:""}function I(n,t,e){const r=o(t.toolCallId,t.id)||`acp.${Date.now()}`,a=C(o(t.kind,t.title)),i=f(o(t.status)),u=t.rawInput??t.input,c=t.rawOutput??t.output,l={method:"session/update",params:e,toolCallId:r,kind:a,title:o(t.title,t.kind),status:i,rawInput:u,rawOutput:c,content:t.content,locations:Array.isArray(t.locations)?t.locations.filter(d=>!!(d&&typeof d=="object"&&!Array.isArray(d))):void 0,meta:t._meta&&typeof t._meta=="object"&&!Array.isArray(t._meta)?t._meta:void 0};return n==="tool_call"?x(l):k(l)}function w(n){return n.replace(/_/g," ")}function p(n){const t=n.replace(/\s+/g," ").trim();if(t)return t.length>120?`${t.slice(0,117)}...`:t}function S(n){if(!n||typeof n!="object"||Array.isArray(n))return;const t=n,e=t.error&&typeof t.error=="object"&&!Array.isArray(t.error)?t.error:null,r=o(e?.message,t.message),a=/reconnecting\.{3}\s*(\d+)\s*\/\s*(\d+)/i.exec(r||"");return a?`第 ${a[1]}/${a[2]} 次尝试`:/selected model is at capacity/i.test(r||"")?"所选模型当前繁忙,请稍后重试":r}function T(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:null}export{g as acpSessionUpdateFromParams,E as eventFromAcpSessionUpdate,F as statusFromAcpSessionUpdate,s as textFromAcpContent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const o="所选模型当前繁忙,请稍后重试。";function c(r){return/ACP request timed out/i.test(r)?"ACP 长时间没有完成,请稍后重试,或点击停止后重新发送。":/MimoCode request timed out/i.test(r)?"MimoCode 长时间没有完成,请稍后重试,或点击停止后重新发送。":s(r)?o:r.replace(/\bsession\/prompt\b/g,"ACP 请求")}function s(r){const t=String(r||"").trim();if(!t)return!1;if(/selected model is at capacity/i.test(t)||/"codexErrorInfo"\s*:\s*"serverOverloaded"/i.test(t))return!0;const e=u(t);return String(e?.codexErrorInfo||"")==="serverOverloaded"||/selected model is at capacity/i.test(String(e?.message||""))}function u(r){const t="Codex turn failed:",e=r.indexOf(t);if(e<0)return null;const n=r.slice(e+t.length).trim();if(!n.startsWith("{"))return null;try{const i=JSON.parse(n);return i&&typeof i=="object"&&!Array.isArray(i)?i:null}catch{return null}}export{s as isCodexCapacityFailure,c as userFacingAcpError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const l="codex.connection",f="codex.error";class _{reconnecting=!1;reset(){this.reconnecting=!1}beforeNotification(t,n){if(t==="error"){const r=u(n);return r?.reconnecting&&(this.reconnecting=!0),r?.disconnected&&!r.reconnecting&&(this.reconnecting=!1),[]}return!this.reconnecting||!p(t)?[]:(this.reconnecting=!1,t==="turn/completed"&&g(n)?[s("failed",x(n))]:[s("completed")])}fail(t){return this.reconnecting?(this.reconnecting=!1,s("failed",t)):null}}function m(e){const t=u(e);if(t){const i=t.reconnecting?"reconnecting":"failed";return{sessionUpdate:"tool_call_update",toolCallId:l,kind:"other",status:t.reconnecting?"in_progress":"failed",title:t.reconnecting?"Codex 正在重连":"Codex 连接失败",rawOutput:e,_meta:{aca:{processKind:"connection",connectionState:i,attempt:t.attempt,maxAttempts:t.maxAttempts}}}}const n=d(e),r=String(n?.codexErrorInfo||"")==="serverOverloaded";return{sessionUpdate:"tool_call_update",toolCallId:f,kind:"other",status:"failed",title:r?"Codex 模型繁忙":"Codex 执行异常",rawOutput:e,_meta:{aca:{processKind:"provider_error",errorCode:a(n?.codexErrorInfo)||null}}}}function s(e,t){const n=e==="failed";return{sessionUpdate:"tool_call_update",toolCallId:l,kind:"other",status:e,title:n?"Codex 连接失败":"Codex 连接已恢复",rawOutput:n?C(t):{message:"Codex 连接已恢复"},_meta:{aca:{processKind:"connection",connectionState:n?"failed":"recovered"}}}}function u(e){const t=d(e);if(!t)return null;const n=o(t.codexErrorInfo),r=!!o(n?.responseStreamDisconnected);if(!r)return null;const i=a(t.message),c=/reconnecting\.{3}\s*(\d+)\s*\/\s*(\d+)/i.exec(i);return{message:i,disconnected:r,reconnecting:!!c,attempt:c?Number.parseInt(c[1]||"",10):null,maxAttempts:c?Number.parseInt(c[2]||"",10):null}}function p(e){return e==="turn/completed"||e.startsWith("item/")||e.startsWith("turn/plan/")||e.startsWith("turn/diff/")||e.startsWith("fuzzyFileSearch/")||e==="model/rerouted"||e==="warning"}function g(e){return a(o(o(e)?.turn)?.status).toLowerCase()==="failed"}function x(e){return o(o(e)?.turn)?.error}function d(e){const t=o(e);return o(t?.error)??t}function C(e){return e instanceof Error?{message:e.message}:typeof e=="string"?{message:e}:e??{message:"Codex 连接未能恢复"}}function o(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:null}function a(e){return typeof e=="string"?e.trim():""}export{l as CODEX_CONNECTION_TOOL_CALL_ID,f as CODEX_ERROR_TOOL_CALL_ID,_ as CodexConnectionRecoveryTracker,m as codexErrorSessionUpdate};
|
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
import{readFileSync as
|
|
1
|
+
import{readFileSync as ut}from"node:fs";import{applyPatch as lt,parsePatch as it,reversePatch as dt}from"diff";import{createPlanEvent as O}from"../../plan-events.js";import{codexErrorSessionUpdate as pt}from"./connection-events.js";import{codexPlanUpdateFromNotification as F}from"./plan-events.js";const v=new Set,T=new Set;function ce(t,e,n={}){switch(t){case"item/agentMessage/delta":{const r=a(e,"delta"),o=a(e,"itemId"),s=o?n.agentMessagePhases?.get(o):"";return r?s==="commentary"?[h("agent_progress_chunk",r)]:s&&s!=="final_answer"?[]:[h("agent_message_chunk",r)]:[]}case"item/reasoning/summaryTextDelta":case"item/reasoning/textDelta":{const r=a(e,"delta");return r?[h("agent_thought_chunk",r)]:[]}case"turn/plan/updated":{const r=n.planDeltas?.fromSnapshot(e)??F(e);return r?[r]:[]}case"item/plan/delta":{const r=n.planDeltas?.fromDelta(e);return r?[r]:[]}case"item/started":case"item/completed":{const r=m(e,"item"),o=a(r,"type");if(!r||!o||o==="userMessage"||o==="agentMessage"||o==="reasoning")return[];if(o==="exitedReviewMode"){const c=a(r,"review");return t==="item/completed"&&c?[h("agent_message_chunk",`${c}
|
|
2
|
+
|
|
3
|
+
`)]:[]}const s=E(t,r);return s?[s]:Rt(o)?[]:[t==="item/started"?vt(r):Tt(r)]}case"item/commandExecution/outputDelta":{const r=a(e,"itemId")||a(e,"id")||a(e,"toolCallId"),o=i(e,["delta","data","output","text"]);return!r&&!o?[]:[{sessionUpdate:"tool_call_update",toolCallId:r,kind:"execute",status:"in_progress",_meta:U(r,o)}]}case"item/commandExecution/terminalInteraction":{const r=a(e,"itemId")||a(e,"id")||a(e,"toolCallId"),o=a(e,"stdin");return r?[{sessionUpdate:"tool_call_update",toolCallId:r,kind:"execute",status:"in_progress",_meta:U(r,`
|
|
2
4
|
${o}
|
|
3
|
-
`)}]:[]}case"item/mcpToolCall/progress":{const r=a(e,"itemId")||a(e,"id")||a(e,"toolCallId"),o=a(e,"message").trim();return!r||!o?[]:[{sessionUpdate:"tool_call_update",toolCallId:r,kind:"execute",status:"in_progress",_meta:{mcp_output_delta:{data:o,terminal_id:r}}}]}case"turn/diff/updated":{const r=a(e,"diff");return r?[{sessionUpdate:"tool_call_update",toolCallId:a(e,"turnId")||"codex.diff",kind:"edit",title:"Diff updated",status:"completed",content:[{type:"diff",oldText:null,newText:r}],rawOutput:{diff:r}}]:[]}case"thread/tokenUsage/updated":return[{sessionUpdate:"usage_update",_meta:{codex:e}}];case"thread/name/updated":return[{sessionUpdate:"session_info_update",title:a(e,"threadName")||null}];case"thread/status/changed":return[
|
|
5
|
+
`)}]:[]}case"item/mcpToolCall/progress":{const r=a(e,"itemId")||a(e,"id")||a(e,"toolCallId"),o=a(e,"message").trim();return!r||!o?[]:[{sessionUpdate:"tool_call_update",toolCallId:r,kind:"execute",status:"in_progress",_meta:{mcp_output_delta:{data:o,terminal_id:r}}}]}case"turn/diff/updated":{const r=a(e,"diff");return r?[{sessionUpdate:"tool_call_update",toolCallId:a(e,"turnId")||"codex.diff",kind:"edit",title:"Diff updated",status:"completed",content:[{type:"diff",oldText:null,newText:r}],rawOutput:{diff:r}}]:[]}case"thread/tokenUsage/updated":return[{sessionUpdate:"usage_update",_meta:{codex:e}}];case"thread/name/updated":return[{sessionUpdate:"session_info_update",title:a(e,"threadName")||null}];case"thread/status/changed":return[b({threadStatus:a(e,"status")})];case"thread/archived":return[b({archived:!0})];case"thread/unarchived":return[b({archived:!1})];case"thread/closed":return[b({closed:!0})];case"account/rateLimits/updated":return[V(e)];case"account/updated":return[b({account:d(e)??e})];case"configWarning":{const r=a(e,"summary"),o=a(e,"details");return[h("agent_message_chunk",`Config warning: ${r}${o?`
|
|
4
6
|
|
|
5
7
|
${o}`:""}
|
|
6
8
|
|
|
7
|
-
`)]}case"warning":return[
|
|
9
|
+
`)]}case"warning":return[h("agent_message_chunk",`Warning: ${a(e,"message")}
|
|
8
10
|
|
|
9
|
-
`)];case"model/rerouted":return[
|
|
11
|
+
`)];case"model/rerouted":return[h("agent_thought_chunk",`Model rerouted from ${a(e,"fromModel")} to ${a(e,"toModel")} (${a(e,"reason")}).
|
|
10
12
|
|
|
11
|
-
`)];case"thread/compacted":return[
|
|
13
|
+
`)];case"thread/compacted":return[h("agent_message_chunk",`*Context compacted to fit the model's context window.*
|
|
12
14
|
|
|
13
|
-
`)];case"fuzzyFileSearch/sessionUpdated":return[N(e)];case"fuzzyFileSearch/sessionCompleted":return[
|
|
15
|
+
`)];case"fuzzyFileSearch/sessionUpdated":return[N(e)];case"fuzzyFileSearch/sessionCompleted":return[B(e)];case"item/autoApprovalReview/started":return[k(e,"started")];case"item/autoApprovalReview/completed":return[k(e,"completed")];case"error":return[pt(e)];default:return R(t,e),[]}}function ue(t,e,n,r={}){switch(t){case"item/agentMessage/delta":{const o=a(e,"delta"),s=a(e,"itemId"),c=s?r.agentMessagePhases?.get(s):"";if(!o||c&&c!=="commentary"&&c!=="final_answer")break;const l=c==="commentary"?"agent_progress_chunk":"agent_message_chunk",f=c==="commentary"?"progress":"message";n.pushStatus("thinking",c==="commentary"?"处理中":"生成回复中",C(o),t),n.pushUpdate({type:l,label:f,text:o,raw:{method:t,params:e,update:h(l,o)},atMs:Date.now()});break}case"item/reasoning/summaryTextDelta":case"item/reasoning/textDelta":{const o=a(e,"delta");n.pushStatus("thinking","思考中",C(o),t),n.pushUpdate({type:"agent_thought_chunk",label:"thought",...o?{text:o}:{},raw:{method:t,params:e},atMs:Date.now()});break}case"turn/plan/updated":{const o=r.planDeltas?.fromSnapshot(e)??F(e);if(!o)break;n.pushStatus("thinking","规划中",void 0,t),n.pushUpdate(O({update:o,method:t,params:e}));break}case"item/plan/delta":{const o=r.planDeltas?.fromDelta(e);if(!o)break;n.pushStatus("thinking","规划中",void 0,t),n.pushUpdate(O({update:o,method:t,params:e}));break}case"item/started":case"item/completed":{const o=m(e,"item"),s=a(o,"type"),c=E(t,o);if(!c){const _=$(s);(s==="reasoning"||s==="agentMessage")&&n.pushStatus(j(s),_,gt(o),t);break}const l=ht(t,e,o),f=ft(s,c);n.pushStatus(j(s),x(s),void 0,t),n.pushUpdate({type:t==="item/started"?"tool_call":"tool_call_update",label:f,status:i(c,["status"])||(t==="item/started"?"in_progress":"completed"),toolCallId:a(o,"id"),raw:{method:t,params:e,tool:l,update:c},atMs:Date.now()});break}case"item/commandExecution/outputDelta":{const o=a(e,"itemId")||a(e,"id")||a(e,"toolCallId"),s=i(e,["delta","data","output","text"]);if(!o&&!s)break;n.pushStatus("exploring","执行命令",void 0,t),n.pushUpdate({type:"tool_call_update",label:"tool update",toolCallId:o,raw:{method:t,params:e,update:{sessionUpdate:"tool_call_update",toolCallId:o,_meta:U(o,s)}},atMs:Date.now()});break}case"item/commandExecution/terminalInteraction":{const o=a(e,"itemId")||a(e,"id")||a(e,"toolCallId"),c=`
|
|
14
16
|
${a(e,"stdin")}
|
|
15
|
-
`;if(!o)break;n.pushStatus("exploring","执行命令",void 0,t),n.pushUpdate({type:"tool_call_update",label:"tool update",toolCallId:o,raw:{method:t,params:e,update:{sessionUpdate:"tool_call_update",toolCallId:o,_meta:U(o,
|
|
17
|
+
`;if(!o)break;n.pushStatus("exploring","执行命令",void 0,t),n.pushUpdate({type:"tool_call_update",label:"tool update",toolCallId:o,raw:{method:t,params:e,update:{sessionUpdate:"tool_call_update",toolCallId:o,_meta:U(o,c)}},atMs:Date.now()});break}case"item/mcpToolCall/progress":{const o=a(e,"itemId")||a(e,"id")||a(e,"toolCallId"),s=a(e,"message").trim();if(!o||!s)break;n.pushStatus("exploring","调用工具",void 0,t),n.pushUpdate({type:"tool_call_update",label:"tool update",toolCallId:o,raw:{method:t,params:e,update:{sessionUpdate:"tool_call_update",toolCallId:o,_meta:{mcp_output_delta:{data:s,terminal_id:o}}}},atMs:Date.now()});break}case"turn/diff/updated":n.pushStatus("exploring","编辑文件中","diff",t),n.pushUpdate({type:"diff",label:"diff",text:a(e,"diff"),raw:{method:t,params:e},atMs:Date.now()});break;case"thread/tokenUsage/updated":n.pushUpdate({type:"usage_update",label:"usage",raw:{method:t,params:e},atMs:Date.now()});break;case"thread/name/updated":M(n,{method:t,params:e,type:"session_info_update",label:"session_info",update:{sessionUpdate:"session_info_update",title:a(e,"threadName")||null}});break;case"thread/status/changed":I(n,t,e,{threadStatus:a(e,"status")});break;case"thread/archived":I(n,t,e,{archived:!0});break;case"thread/unarchived":I(n,t,e,{archived:!1});break;case"thread/closed":I(n,t,e,{closed:!0});break;case"account/rateLimits/updated":M(n,{method:t,params:e,type:"session_info_update",label:"rate_limit",update:V(e)});break;case"configWarning":{const o=a(e,"summary"),s=a(e,"details"),c=`Config warning: ${o}${s?`
|
|
16
18
|
|
|
17
19
|
${s}`:""}
|
|
18
20
|
|
|
19
|
-
`;n.pushUpdate({type:"agent_message_chunk",label:"message",text:
|
|
21
|
+
`;n.pushUpdate({type:"agent_message_chunk",label:"message",text:c,raw:{method:t,params:e,update:h("agent_message_chunk",c)},atMs:Date.now()});break}case"warning":{const o=`Warning: ${a(e,"message")}
|
|
20
22
|
|
|
21
|
-
`;n.pushUpdate({type:"agent_message_chunk",label:"message",text:o,raw:{method:t,params:e,update:
|
|
23
|
+
`;n.pushUpdate({type:"agent_message_chunk",label:"message",text:o,raw:{method:t,params:e,update:h("agent_message_chunk",o)},atMs:Date.now()});break}case"model/rerouted":{const o=`Model rerouted from ${a(e,"fromModel")} to ${a(e,"toModel")} (${a(e,"reason")}).
|
|
22
24
|
|
|
23
|
-
`;n.pushStatus("thinking","模型已切换",
|
|
25
|
+
`;n.pushStatus("thinking","模型已切换",C(o),t),n.pushUpdate({type:"agent_thought_chunk",label:"thought",text:o,raw:{method:t,params:e,update:h("agent_thought_chunk",o)},atMs:Date.now()});break}case"thread/compacted":{const o=`*Context compacted to fit the model's context window.*
|
|
24
26
|
|
|
25
|
-
`;n.pushStatus("thinking","上下文已压缩",void 0,t),n.pushUpdate({type:"agent_message_chunk",label:"message",text:o,raw:{method:t,params:e,update:
|
|
26
|
-
`).trim()}function $(t){switch(t){case"commandExecution":return"执行命令中";case"fileChange":return"编辑文件中";case"mcpToolCall":case"dynamicToolCall":return"调用工具中";case"webSearch":return"搜索中";case"imageGeneration":return"生成图片中";case"reasoning":return"思考中";case"agentMessage":return"生成回复中";default:return"处理中"}}function
|
|
27
|
-
`)}}]}function B(t){switch(t){case"inProgress":return"In progress";case"approved":return"Approved";case"denied":return"Denied";case"aborted":return"Aborted";case"timedOut":return"Timed out";default:return t||"Unknown"}}function W(t){if(!t)return"";const e=a(t,"type");if(e==="command")return[q(a(t,"source")),a(t,"command")].filter(Boolean).join(" ");if(e==="execve"){const n=h(t,"argv")?.map(s=>p(s)).filter(Boolean)??[],r=a(t,"program"),o=n.length>0?n:[r].filter(Boolean);return[q(a(t,"source")),mt(o)].filter(Boolean).join(" ")}if(e==="applyPatch"){const n=h(t,"files")?.map(r=>p(r)).filter(Boolean)??[];return n.length===1?`apply_patch touching ${n[0]}`:`apply_patch touching ${n.length} files`}if(e==="networkAccess"){const n=a(t,"target")||a(t,"host");return n?`network access to ${n}`:"network access"}if(e==="mcpToolCall"){const n=a(t,"connectorName")||a(t,"server");return`MCP ${a(t,"toolName")} on ${n}`.trim()}return e==="requestPermissions"?a(t,"reason")||"request additional permissions":e}function q(t){return t==="shell"?"shell":t==="unifiedExec"?"exec":t}function mt(t){return t.map(wt).join(" ")}function wt(t){return t.length===0?"''":/^[A-Za-z0-9_/:=+.,@%-]+$/.test(t)?t:`'${t.replace(/'/g,"'\\''")}'`}function Ct(t){const e=y(t,"review"),n=e?B(a(e,"status")):"",r=W(y(t,"action"));return[n,r].filter(Boolean).join(" · ")||void 0}function V(t){const e=y(t,"rateLimits")??{},n=y(e,"primary"),r=y(e,"secondary");return i({sessionUpdate:"session_info_update",_meta:{rateLimits:i({planName:a(e,"planType"),limitName:a(e,"limitName"),limitId:a(e,"limitId"),fiveHour:z(n,"usedPercent")??null,sevenDay:z(r,"usedPercent")??null,fiveHourResetAt:a(n,"resetsAt")||null,sevenDayResetAt:a(r,"resetsAt")||null,raw:e})}})}function bt(t){const e=a(t,"id"),n=a(t,"cwd"),r=a(t,"command"),o=X(t),s=o.length===1?d(o[0]):null;if(s){const u=a(s,"type");if(u==="read"){const l=a(s,"path")||a(s,"name");return i({sessionUpdate:"tool_call",toolCallId:e,status:m(a(t,"status")),kind:"read",title:`Read file '${l}'`,locations:l?[{path:l}]:void 0})}if(u==="search"){const l=a(s,"query"),f=a(s,"path");return i({sessionUpdate:"tool_call",toolCallId:e,status:m(a(t,"status")),kind:"search",title:Z(l,f)})}if(u==="listFiles"){const l=a(s,"path");return i({sessionUpdate:"tool_call",toolCallId:e,status:m(a(t,"status")),kind:"read",title:l?`List files in '${l}'`:"List files"})}}return Et({sessionUpdate:"tool_call",toolCallId:e,status:m(a(t,"status")),kind:"execute",title:Nt(r),rawInput:i({command:r,cwd:n})},e,n)}function It(t){const e=a(t,"id"),n=c(t,["aggregatedOutput","aggregated_output","formattedOutput","formatted_output","output","stdout","stderr"]),r=z(t,"exitCode"),o={sessionUpdate:"tool_call_update",toolCallId:e,status:C(a(t,"status"),r),rawOutput:i({formatted_output:n,exit_code:r})};return Gt(t)&&(o._meta={...n?U(e,n):{},terminal_exit:{exit_code:r,signal:null,terminal_id:e}}),i(o)}function Ut(t){const e=a(t,"id"),n=Y(t);return i({sessionUpdate:"tool_call",toolCallId:e,title:"Editing files",kind:"edit",status:m(a(t,"status")),content:n,locations:Lt(t),rawInput:{changes:P(t)}})}function xt(t){return i({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:C(a(t,"status")),content:Y(t)})}function kt(t){const e=c(t,["tool","name","server"]);return i({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"execute",title:e,status:m(a(t,"status")),rawInput:d(t)})}function St(t){return i({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:C(a(t,"status")),rawOutput:d(t)})}function At(t){const e=a(t,"server"),n=a(t,"tool");return i({...J(t,`mcp.${e}.${n}`,H(e,n,t.arguments),Q(t.result,t.error)),_meta:{is_mcp_tool_call:!0}})}function vt(t){const e=a(t,"server"),n=a(t,"tool");return i({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:C(a(t,"status")),rawInput:H(e,n,t.arguments),rawOutput:Q(t.result,t.error)})}function $t(t){return J(t,a(t,"tool"),{arguments:t.arguments},void 0)}function Tt(t){return i({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:C(a(t,"status"))})}function J(t,e,n,r){return i({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"execute",title:e,status:m(a(t,"status")),rawInput:n,rawOutput:r})}function H(t,e,n){return{server:t,tool:e,arguments:n}}function Q(t,e){if(!(t===null&&e===null)&&!(t===void 0&&e===void 0))return{result:t,error:e}}function Dt(t){return i({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"search",title:K(t),status:m(a(t,"status")),rawInput:d(t)})}function Mt(t){return i({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),title:K(t),status:C(a(t,"status")),rawInput:d(t)})}function Pt(t){return i({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"other",title:"Image generation",status:"in_progress",rawInput:{id:a(t,"id")}})}function zt(t){return i({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:Ot(a(t,"status")),content:Rt(t),rawOutput:jt(t)})}function Ft(t){const e=a(t,"path");return i({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"read",title:`View Image ${e}`,status:"completed",content:[{type:"content",content:{type:"resource_link",name:e,uri:e}}],locations:[{path:e}],rawInput:{path:e}})}function Ot(t){return C(t)}function Rt(t){const e=d(t.result),n=c(e,["url","uri","path"]);if(n)return[{type:"content",content:{type:"resource_link",name:n,uri:n}}]}function jt(t){const e={status:t.status,revisedPrompt:t.revisedPrompt,result:t.result};return"savedPath"in t&&(e.savedPath=t.savedPath??null),e}function Et(t,e,n){return i({...t,content:[{type:"terminal",terminalId:e}],_meta:{terminal_info:{cwd:n,terminal_id:e}}})}function Gt(t){const e=X(t),n=e.length===1?d(e[0]):null;return!n||a(n,"type")==="unknown"}function X(t){return h(t,"commandActions")||h(t,"command_actions")||h(t,"actions")||[]}function m(t){switch(t){case"inProgress":case"in_progress":case"started":case"":return"in_progress";case"completed":return"completed";case"failed":case"declined":return"failed";case"cancelled":case"canceled":return"cancelled";default:return t}}function C(t,e){const n=m(t);return n==="failed"||n==="cancelled"?n:e!=null?e===0?"completed":"failed":n==="in_progress"?"completed":n}function Nt(t){const e=t.replace(/^(?:\/bin\/)?(?:bash|zsh|sh)\s+(?:-[lc]+\s+)?/,"");return e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function U(t,e){return{terminal_output_delta:{data:e,terminal_id:t}}}function Z(t,e){return t&&e?`Search for '${t}' in ${e}`:t?`Search for '${t}'`:e?`Search in '${e}'`:"Search"}function K(t){const e=d(t.action);if(!e){const r=a(t,"query");return r?`Web search: ${r}`:"Web search"}const n=a(e,"type");if(n==="search"){const r=h(e,"queries")?.map(s=>p(s)).filter(Boolean)??[],o=a(e,"query")||(r.length>0?r.join(", "):"")||a(t,"query");return o?`Web search: ${o}`:"Web search"}if(n==="openPage"){const r=a(e,"url");return r?`Open page: ${r}`:"Open page"}if(n==="findInPage"){const r=a(e,"pattern")?` for '${a(e,"pattern")}'`:"",o=a(e,"url")?` in ${a(e,"url")}`:"";return`Find in page${r}${o}`.trim()}return"Web search"}function Lt(t){const e=P(t).map(n=>d(n)).map(n=>a(n,"path")).filter(Boolean).map(n=>({path:n}));return e.length>0?e:void 0}function Y(t){const n=(h(t,"changes")??[]).map(r=>{const o=d(r);if(!o)return null;const s=p(o.path),u=o.kind&&typeof o.kind=="object"&&!Array.isArray(o.kind)?p(o.kind.type):p(o.kind),l=p(o.diff);return u==="add"?i({type:"diff",oldText:null,newText:l,path:s,_meta:{kind:"add"}}):u==="delete"?i({type:"diff",oldText:l,newText:"",path:s,_meta:{kind:"delete"}}):Bt(o,s,l,u)}).filter(Boolean);return n.length>0?n:void 0}function Bt(t,e,n,r){const o=t.kind&&typeof t.kind=="object"&&!Array.isArray(t.kind)?p(t.kind.move_path):"",s=Wt(n),u=tt(e);if(u!==null){const l=M(u,s);if(l!==!1)return k(o||e,u,l,r||"update");const f=et(s);if(f){const g=M(u,f);if(g!==!1)return k(e,g,u,r||"update")}}if(o){const l=tt(o),f=et(s);if(l!==null&&f){const g=M(l,f);if(g!==!1)return k(o,g,l,r||"update")}}return k(o||e,null,s,r||"update")}function k(t,e,n,r){return i({type:"diff",oldText:e,newText:n,path:t,_meta:{kind:r}})}function tt(t){try{return st(t,"utf8")}catch{return null}}function Wt(t){return t.replace(/\n\nMoved to: .*$/,"")}function et(t){try{const[e]=it(t);return e?lt(e):null}catch{return null}}function M(t,e){try{return ut(t,e)}catch{return!1}}function qt(t,e){if(t==="commandExecution")return"执行命令";if(t==="fileChange")return e.path?`编辑文件: ${Ht(e.path)}`:"编辑文件";if(t==="webSearch")return e.rawInput?`搜索: ${w(e.rawInput)}`:"搜索";if(t==="mcpToolCall"||t==="dynamicToolCall"){const n=c(e.item,["tool","name","server"]);return n?`调用工具: ${n}`:"调用工具"}return $(t)}function Vt(t,e,n){if(e)return e;if(t==="fileChange"){const r=nt(n),o=c(n,["path","filePath","file_path","relativePath","absolutePath"])||r.path;return[c(n,["action","changeType","operation"])||r.action,o].filter(Boolean).join(" ")}return""}function nt(t){const e=h(t,"changes")??[];for(const n of e){if(!n||typeof n!="object"||Array.isArray(n))continue;const r=n,o=p(r.path),s=r.kind&&typeof r.kind=="object"&&!Array.isArray(r.kind)?p(r.kind.type):p(r.kind);if(o||s)return{path:o,action:s}}return{path:"",action:""}}function P(t){return(h(t,"changes")??[]).map(n=>{if(!n||typeof n!="object"||Array.isArray(n))return null;const r=n,o=p(r.path),s=r.kind&&typeof r.kind=="object"&&!Array.isArray(r.kind)?p(r.kind.type):p(r.kind);return!o&&!s?null:i({type:s||"edit",path:o})}).filter(Boolean).slice(0,50)}function c(t,e){if(!t)return"";for(const n of e){const r=p(t[n]);if(r)return r}return""}function p(t){if(typeof t=="string"&&t.trim())return t.trim();if(typeof t=="number"||typeof t=="boolean")return String(t);if(t&&typeof t=="object")try{const e=JSON.stringify(t);return e.length>2?e:""}catch{return""}return""}function h(t,e){const n=t?.[e];return Array.isArray(n)?n.slice(0,50):null}function Jt(...t){return t.find(e=>e.trim())||""}function Ht(t){return t.replace(/\\/g,"/").split("/").filter(Boolean).pop()||t}function i(t){for(const e of Object.keys(t)){const n=t[e];(n===void 0||n===""||Array.isArray(n)&&n.length===0)&&delete t[e]}return t}function y(t,e){if(!t||typeof t!="object"||Array.isArray(t))return null;const n=t[e];return!n||typeof n!="object"||Array.isArray(n)?null:n}function d(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function a(t,e){if(!t||typeof t!="object"||Array.isArray(t))return"";const n=t[e];return typeof n=="string"?n:""}function z(t,e){if(!t||typeof t!="object"||Array.isArray(t))return;const n=t[e];return typeof n=="number"&&Number.isFinite(n)?n:void 0}function w(t){if(typeof t!="string")return;const e=t.replace(/\s+/g," ").trim();if(e)return e.length>120?`${e.slice(0,117)}...`:e}export{Yt as codexAcpSessionUpdatesFromNotification,ee as extractAssistantTextFromTurn,te as mapCodexNotification};
|
|
27
|
+
`;n.pushStatus("thinking","上下文已压缩",void 0,t),n.pushUpdate({type:"agent_message_chunk",label:"message",text:o,raw:{method:t,params:e,update:h("agent_message_chunk",o)},atMs:Date.now()});break}case"fuzzyFileSearch/sessionUpdated":{const o=N(e);n.pushStatus("exploring",a(o,"title")||"Search",yt(e),t),n.pushUpdate({type:o.sessionUpdate==="tool_call"?"tool_call":"tool_call_update",label:a(o,"title")||"Search",status:a(o,"status"),toolCallId:a(o,"toolCallId"),raw:{method:t,params:e,update:o},atMs:Date.now()});break}case"fuzzyFileSearch/sessionCompleted":{const o=B(e);n.pushUpdate({type:"tool_call_update",label:"Search",status:a(o,"status"),toolCallId:a(o,"toolCallId"),raw:{method:t,params:e,update:o},atMs:Date.now()});break}case"item/autoApprovalReview/started":{const o=k(e,"started");n.pushStatus("exploring","Guardian Review",Ut(e),t),n.pushUpdate({type:o.sessionUpdate==="tool_call"?"tool_call":"tool_call_update",label:"Guardian Review",status:a(o,"status"),toolCallId:a(o,"toolCallId"),raw:{method:t,params:e,update:o},atMs:Date.now()});break}case"item/autoApprovalReview/completed":{const o=k(e,"completed");n.pushUpdate({type:o.sessionUpdate==="tool_call"?"tool_call":"tool_call_update",label:"Guardian Review",status:a(o,"status"),toolCallId:a(o,"toolCallId"),raw:{method:t,params:e,update:o},atMs:Date.now()});break}case"account/updated":I(n,t,e,{account:d(e)??e});break;case"turn/started":n.pushStatus("thinking","思考中",void 0,t);break;case"turn/completed":n.pushStatus("completed","已完成",void 0,t);break;case"error":n.pushStatus("failed","执行失败",C(JSON.stringify(e)),t),n.pushUpdate({type:"error",label:"error",text:C(JSON.stringify(e)),raw:{method:t,params:e},atMs:Date.now()});break;default:R(t,e);break}}function R(t,e){if(process.env.ACA_CODEX_PROTOCOL_DEBUG!=="1")return;const n=d(e);console.debug("[codex] unhandled notification",{method:t,keys:n?Object.keys(n).slice(0,20):[],itemType:a(m(n,"item"),"type")||void 0})}function le(t){return(Array.isArray(t?.items)?t.items:[]).map(n=>a(n,"type")!=="agentMessage"?"":a(n,"text")).filter(Boolean).join(`
|
|
28
|
+
`).trim()}function $(t){switch(t){case"commandExecution":return"执行命令中";case"fileChange":return"编辑文件中";case"mcpToolCall":case"dynamicToolCall":return"调用工具中";case"collabAgentToolCall":return"处理子任务中";case"contextCompaction":return"压缩上下文中";case"webSearch":return"搜索中";case"imageGeneration":return"生成图片中";case"reasoning":return"思考中";case"agentMessage":return"生成回复中";default:return"处理中"}}function x(t){switch(t){case"commandExecution":return"执行命令";case"fileChange":return"编辑文件";case"mcpToolCall":case"dynamicToolCall":return"调用工具";case"collabAgentToolCall":return"处理子任务";case"contextCompaction":return"压缩上下文";case"webSearch":case"fuzzyFileSearch":return"搜索";case"imageView":return"查看图片";case"imageGeneration":return"生成图片";default:return $(t)}}function ft(t,e){const n=i(e,["kind"]);if(n==="read")return i(e,["title"])||"读取文件";if(n==="search")return i(e,["title"])||"搜索";if(n==="edit")return"编辑文件";if(n==="execute"){const r=d(e.rawInput);return a(r,"command")?"执行命令":i(e,["title"])||x(t)}return n==="think"?"思考中":n==="other"&&i(e,["title"])||x(t)}function j(t){return t==="imageGeneration"?"image_generation":t==="reasoning"||t==="agentMessage"||t==="plan"?"thinking":"exploring"}function gt(t){if(t)for(const e of["command","path","tool","server","query","text"]){const n=a(t,e);if(n)return C(n)}}function ht(t,e,n){const r=a(n,"type"),o=a(n,"id")||a(e,"itemId"),s=i(n,["rawInput","raw_input","command","query","path","text","input"]),c=r==="commandExecution"?i(n,["command","rawInput","raw_input"]):"",l=ot(n),f=i(n,["path","filePath","file_path","relativePath","absolutePath"])||l.path,_=i(n,["aggregatedOutput","aggregated_output","formattedOutput","formatted_output","output","stdout","stderr"]),A=p(n,"commandActions")||p(n,"command_actions")||p(n,"actions")||P(n),rt=Yt(r,_,n),st=Zt(r,{command:c,path:f,rawInput:s,item:n}),ct=C(te(c,f,s,_));return u({id:o,type:r,method:t,displayName:st,rawInput:s,raw_input:s,command:c,path:f,action:i(n,["action","changeType","operation"])||l.action,commandActions:A,command_actions:A.length>0?A:void 0,aggregatedOutput:_,aggregated_output:_,formatted_output:rt,summary:ct})}function E(t,e){if(!e)return null;switch(a(e,"type")){case"commandExecution":return t==="item/started"?xt(e):kt(e);case"fileChange":return t==="item/started"?St(e):At(e);case"mcpToolCall":return t==="item/started"?$t(e):Mt(e);case"dynamicToolCall":return t==="item/started"?Dt(e):Pt(e);case"collabAgentToolCall":return t==="item/started"?zt(e):Ot(e);case"contextCompaction":return Ft(t,e);case"webSearch":return t==="item/started"?jt(e):Et(e);case"imageView":return Bt(e);case"imageGeneration":return t==="item/started"?Gt(e):Nt(e);default:return null}}function M(t,e){t.pushUpdate({type:e.type,label:e.label,raw:{method:e.method,params:e.params,update:e.update},atMs:Date.now()})}function I(t,e,n,r){M(t,{method:e,params:n,type:"session_info_update",label:"session_info",update:{sessionUpdate:"session_info_update",_meta:{codex:r}}})}function b(t){return{sessionUpdate:"session_info_update",_meta:{codex:t}}}function h(t,e){return{sessionUpdate:t,content:{type:"text",text:e}}}function G(t){return`fuzzyFileSearch.${t}`}function N(t){const e=a(t,"sessionId"),n=G(e),r=!v.has(n);v.add(n);const o=a(t,"query"),s=Y(o,""),c=_t(t);return u(r?{sessionUpdate:"tool_call",toolCallId:n,kind:"search",title:s,status:"in_progress",locations:c,rawInput:{query:o}}:{sessionUpdate:"tool_call_update",toolCallId:n,title:s,status:"in_progress",locations:c})}function B(t){const e=a(t,"sessionId"),n=G(e);return v.delete(n),{sessionUpdate:"tool_call_update",toolCallId:n,status:"completed"}}function _t(t){const n=(p(d(t),"files")??[]).map(r=>{const o=d(r);if(!o)return"";const s=a(o,"path"),c=a(o,"root");return s?s.startsWith("/")||!c?s:`${c.replace(/\/+$/,"")}/${s.replace(/^\/+/,"")}`:""}).filter(Boolean).map(r=>({path:r}));return n.length>0?n:void 0}function yt(t){const e=a(t,"query"),r=(p(d(t),"files")??[]).length;if(e&&r)return`${e} · ${r} files`;if(e)return e;if(r)return`${r} files`}function mt(t){return`guardian_assessment:${t}`}function k(t,e){const n=a(t,"reviewId"),r=mt(n),o=e==="started"&&!T.has(n);e==="started"&&T.add(n),e==="completed"&&T.delete(n);const s=m(t,"review")??{},c={sessionUpdate:o?"tool_call":"tool_call_update",toolCallId:r,status:wt(a(s,"status")),content:Ct(s,m(t,"action")),...o?{kind:"think",title:"Guardian Review",rawInput:t}:{rawOutput:t}};return u(c)}function wt(t){switch(t){case"inProgress":return"in_progress";case"approved":return"completed";case"denied":case"aborted":case"timedOut":return"failed";default:return t||"in_progress"}}function Ct(t,e){const n=[`Status: ${L(a(t,"status"))}`],r=W(e);r&&n.push(`Action: ${r}`);const o=a(t,"riskLevel");o&&n.push(`Risk: ${o}`);const s=a(t,"userAuthorization");s&&n.push(`Authorization: ${s}`);const c=a(t,"rationale").trim();return c&&n.push(`Rationale: ${c}`),[{type:"content",content:{type:"text",text:n.join(`
|
|
29
|
+
`)}}]}function L(t){switch(t){case"inProgress":return"In progress";case"approved":return"Approved";case"denied":return"Denied";case"aborted":return"Aborted";case"timedOut":return"Timed out";default:return t||"Unknown"}}function W(t){if(!t)return"";const e=a(t,"type");if(e==="command")return[q(a(t,"source")),a(t,"command")].filter(Boolean).join(" ");if(e==="execve"){const n=p(t,"argv")?.map(s=>g(s)).filter(Boolean)??[],r=a(t,"program"),o=n.length>0?n:[r].filter(Boolean);return[q(a(t,"source")),It(o)].filter(Boolean).join(" ")}if(e==="applyPatch"){const n=p(t,"files")?.map(r=>g(r)).filter(Boolean)??[];return n.length===1?`apply_patch touching ${n[0]}`:`apply_patch touching ${n.length} files`}if(e==="networkAccess"){const n=a(t,"target")||a(t,"host");return n?`network access to ${n}`:"network access"}if(e==="mcpToolCall"){const n=a(t,"connectorName")||a(t,"server");return`MCP ${a(t,"toolName")} on ${n}`.trim()}return e==="requestPermissions"?a(t,"reason")||"request additional permissions":e}function q(t){return t==="shell"?"shell":t==="unifiedExec"?"exec":t}function It(t){return t.map(bt).join(" ")}function bt(t){return t.length===0?"''":/^[A-Za-z0-9_/:=+.,@%-]+$/.test(t)?t:`'${t.replace(/'/g,"'\\''")}'`}function Ut(t){const e=m(t,"review"),n=e?L(a(e,"status")):"",r=W(m(t,"action"));return[n,r].filter(Boolean).join(" · ")||void 0}function V(t){const e=m(t,"rateLimits")??{},n=m(e,"primary"),r=m(e,"secondary");return u({sessionUpdate:"session_info_update",_meta:{rateLimits:u({planName:a(e,"planType"),limitName:a(e,"limitName"),limitId:a(e,"limitId"),fiveHour:z(n,"usedPercent")??null,sevenDay:z(r,"usedPercent")??null,fiveHourResetAt:a(n,"resetsAt")||null,sevenDayResetAt:a(r,"resetsAt")||null,raw:e})}})}function xt(t){const e=a(t,"id"),n=a(t,"cwd"),r=a(t,"command"),o=Z(t),s=o.length===1?d(o[0]):null;if(s){const c=a(s,"type");if(c==="read"){const l=a(s,"path")||a(s,"name");return u({sessionUpdate:"tool_call",toolCallId:e,status:y(a(t,"status")),kind:"read",title:`Read file '${l}'`,locations:l?[{path:l}]:void 0})}if(c==="search"){const l=a(s,"query"),f=a(s,"path");return u({sessionUpdate:"tool_call",toolCallId:e,status:y(a(t,"status")),kind:"search",title:Y(l,f)})}if(c==="listFiles"){const l=a(s,"path");return u({sessionUpdate:"tool_call",toolCallId:e,status:y(a(t,"status")),kind:"read",title:l?`List files in '${l}'`:"List files"})}}return Vt({sessionUpdate:"tool_call",toolCallId:e,status:y(a(t,"status")),kind:"execute",title:Kt(r),rawInput:u({command:r,cwd:n})},e,n)}function kt(t){const e=a(t,"id"),n=i(t,["aggregatedOutput","aggregated_output","formattedOutput","formatted_output","output","stdout","stderr"]),r=z(t,"exitCode"),o={sessionUpdate:"tool_call_update",toolCallId:e,status:w(a(t,"status"),r),rawOutput:u({formatted_output:n,exit_code:r})};return Jt(t)&&(o._meta={...n?U(e,n):{},terminal_exit:{exit_code:r,signal:null,terminal_id:e}}),u(o)}function St(t){const e=a(t,"id"),n=et(t);return u({sessionUpdate:"tool_call",toolCallId:e,title:"Editing files",kind:"edit",status:y(a(t,"status")),content:n,locations:Ht(t),rawInput:{changes:P(t)}})}function At(t){return u({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:w(a(t,"status")),content:et(t)})}function vt(t){const e=i(t,["tool","name","server"]);return u({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"execute",title:e,status:y(a(t,"status")),rawInput:d(t)})}function Tt(t){return u({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:w(a(t,"status")),rawOutput:d(t)})}function $t(t){const e=a(t,"server"),n=a(t,"tool");return u({...H(t,`mcp.${e}.${n}`,Q(e,n,t.arguments),X(t.result,t.error)),_meta:{is_mcp_tool_call:!0}})}function Mt(t){const e=a(t,"server"),n=a(t,"tool");return u({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:w(a(t,"status")),rawInput:Q(e,n,t.arguments),rawOutput:X(t.result,t.error)})}function Dt(t){const e=a(t,"namespace"),n=a(t,"tool");return H(t,J(e,n),{namespace:e,tool:n,arguments:t.arguments},void 0)}function Pt(t){const e=a(t,"namespace"),n=a(t,"tool");return u({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),kind:"execute",title:J(e,n),status:w(a(t,"status")),rawInput:{namespace:e,tool:n,arguments:t.arguments},rawOutput:u({success:t.success,contentItems:t.contentItems,durationMs:t.durationMs})})}function zt(t){const e=a(t,"tool");return u({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"other",title:K(e),status:y(a(t,"status")),rawInput:{tool:e,arguments:u({receiverThreadIds:p(t,"receiverThreadIds"),prompt:a(t,"prompt"),model:a(t,"model"),reasoningEffort:a(t,"reasoningEffort")})},_meta:{aca:{processKind:"subtask",action:e}}})}function Ot(t){const e=a(t,"tool");return u({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),kind:"other",title:K(e),status:w(a(t,"status")),rawOutput:u({agentsStates:t.agentsStates,receiverThreadIds:p(t,"receiverThreadIds")}),_meta:{aca:{processKind:"subtask",action:e}}})}function Ft(t,e){return{sessionUpdate:t==="item/started"?"tool_call":"tool_call_update",toolCallId:a(e,"id")||"codex.context_compaction",kind:"think",title:"压缩上下文",status:t==="item/started"?"in_progress":w(a(e,"status")),_meta:{aca:{processKind:"context_compaction"}}}}function J(t,e){return[t,e].filter(Boolean).join(".")||"内部工具"}function K(t){switch(t){case"spawnAgent":return"启动子任务";case"sendInput":return"发送子任务指令";case"resumeAgent":return"恢复子任务";case"wait":return"等待子任务";case"closeAgent":return"结束子任务";default:return"处理子任务"}}function Rt(t){return["hookPrompt","sleep","subAgentActivity","enteredReviewMode","exitedReviewMode","plan"].includes(t)}function H(t,e,n,r){return u({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"execute",title:e,status:y(a(t,"status")),rawInput:n,rawOutput:r})}function Q(t,e,n){return{server:t,tool:e,arguments:n}}function X(t,e){if(!(t===null&&e===null)&&!(t===void 0&&e===void 0))return{result:t,error:e}}function jt(t){return u({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"search",title:tt(t),status:y(a(t,"status")),rawInput:d(t)})}function Et(t){return u({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),title:tt(t),status:w(a(t,"status")),rawInput:d(t)})}function Gt(t){return u({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"other",title:"Image generation",status:"in_progress",rawInput:{id:a(t,"id")}})}function Nt(t){return u({sessionUpdate:"tool_call_update",toolCallId:a(t,"id"),status:Lt(a(t,"status")),content:Wt(t),rawOutput:qt(t)})}function Bt(t){const e=a(t,"path");return u({sessionUpdate:"tool_call",toolCallId:a(t,"id"),kind:"read",title:`View Image ${e}`,status:"completed",content:[{type:"content",content:{type:"resource_link",name:e,uri:e}}],locations:[{path:e}],rawInput:{path:e}})}function Lt(t){return w(t)}function Wt(t){const e=d(t.result),n=i(e,["url","uri","path"]);if(n)return[{type:"content",content:{type:"resource_link",name:n,uri:n}}]}function qt(t){const e={status:t.status,revisedPrompt:t.revisedPrompt,result:t.result};return"savedPath"in t&&(e.savedPath=t.savedPath??null),e}function Vt(t,e,n){return u({...t,content:[{type:"terminal",terminalId:e}],_meta:{terminal_info:{cwd:n,terminal_id:e}}})}function Jt(t){const e=Z(t),n=e.length===1?d(e[0]):null;return!n||a(n,"type")==="unknown"}function Z(t){return p(t,"commandActions")||p(t,"command_actions")||p(t,"actions")||[]}function y(t){switch(t){case"inProgress":case"in_progress":case"started":case"":return"in_progress";case"completed":return"completed";case"failed":case"declined":return"failed";case"cancelled":case"canceled":return"cancelled";default:return t}}function w(t,e){const n=y(t);return n==="failed"||n==="cancelled"?n:e!=null?e===0?"completed":"failed":n==="in_progress"?"completed":n}function Kt(t){const e=t.replace(/^(?:\/bin\/)?(?:bash|zsh|sh)\s+(?:-[lc]+\s+)?/,"");return e.startsWith("'")&&e.endsWith("'")?e.slice(1,-1):e}function U(t,e){return{terminal_output_delta:{data:e,terminal_id:t}}}function Y(t,e){return t&&e?`Search for '${t}' in ${e}`:t?`Search for '${t}'`:e?`Search in '${e}'`:"Search"}function tt(t){const e=d(t.action);if(!e){const r=a(t,"query");return r?`Web search: ${r}`:"Web search"}const n=a(e,"type");if(n==="search"){const r=p(e,"queries")?.map(s=>g(s)).filter(Boolean)??[],o=a(e,"query")||(r.length>0?r.join(", "):"")||a(t,"query");return o?`Web search: ${o}`:"Web search"}if(n==="openPage"){const r=a(e,"url");return r?`Open page: ${r}`:"Open page"}if(n==="findInPage"){const r=a(e,"pattern")?` for '${a(e,"pattern")}'`:"",o=a(e,"url")?` in ${a(e,"url")}`:"";return`Find in page${r}${o}`.trim()}return"Web search"}function Ht(t){const e=P(t).map(n=>d(n)).map(n=>a(n,"path")).filter(Boolean).map(n=>({path:n}));return e.length>0?e:void 0}function et(t){const n=(p(t,"changes")??[]).map(r=>{const o=d(r);if(!o)return null;const s=g(o.path),c=o.kind&&typeof o.kind=="object"&&!Array.isArray(o.kind)?g(o.kind.type):g(o.kind),l=g(o.diff);return c==="add"?u({type:"diff",oldText:null,newText:l,path:s,_meta:{kind:"add"}}):c==="delete"?u({type:"diff",oldText:l,newText:"",path:s,_meta:{kind:"delete"}}):Qt(o,s,l,c)}).filter(Boolean);return n.length>0?n:void 0}function Qt(t,e,n,r){const o=t.kind&&typeof t.kind=="object"&&!Array.isArray(t.kind)?g(t.kind.move_path):"",s=Xt(n),c=nt(e);if(c!==null){const l=D(c,s);if(l!==!1)return S(o||e,c,l,r||"update");const f=at(s);if(f){const _=D(c,f);if(_!==!1)return S(e,_,c,r||"update")}}if(o){const l=nt(o),f=at(s);if(l!==null&&f){const _=D(l,f);if(_!==!1)return S(o,_,l,r||"update")}}return S(o||e,null,s,r||"update")}function S(t,e,n,r){return u({type:"diff",oldText:e,newText:n,path:t,_meta:{kind:r}})}function nt(t){try{return ut(t,"utf8")}catch{return null}}function Xt(t){return t.replace(/\n\nMoved to: .*$/,"")}function at(t){try{const[e]=it(t);return e?dt(e):null}catch{return null}}function D(t,e){try{return lt(t,e)}catch{return!1}}function Zt(t,e){if(t==="commandExecution")return"执行命令";if(t==="fileChange")return e.path?`编辑文件: ${ee(e.path)}`:"编辑文件";if(t==="webSearch")return e.rawInput?`搜索: ${C(e.rawInput)}`:"搜索";if(t==="mcpToolCall"||t==="dynamicToolCall"){const n=i(e.item,["tool","name","server"]);return n?`调用工具: ${n}`:"调用工具"}return $(t)}function Yt(t,e,n){if(e)return e;if(t==="fileChange"){const r=ot(n),o=i(n,["path","filePath","file_path","relativePath","absolutePath"])||r.path;return[i(n,["action","changeType","operation"])||r.action,o].filter(Boolean).join(" ")}return""}function ot(t){const e=p(t,"changes")??[];for(const n of e){if(!n||typeof n!="object"||Array.isArray(n))continue;const r=n,o=g(r.path),s=r.kind&&typeof r.kind=="object"&&!Array.isArray(r.kind)?g(r.kind.type):g(r.kind);if(o||s)return{path:o,action:s}}return{path:"",action:""}}function P(t){return(p(t,"changes")??[]).map(n=>{if(!n||typeof n!="object"||Array.isArray(n))return null;const r=n,o=g(r.path),s=r.kind&&typeof r.kind=="object"&&!Array.isArray(r.kind)?g(r.kind.type):g(r.kind);return!o&&!s?null:u({type:s||"edit",path:o})}).filter(Boolean).slice(0,50)}function i(t,e){if(!t)return"";for(const n of e){const r=g(t[n]);if(r)return r}return""}function g(t){if(typeof t=="string"&&t.trim())return t.trim();if(typeof t=="number"||typeof t=="boolean")return String(t);if(t&&typeof t=="object")try{const e=JSON.stringify(t);return e.length>2?e:""}catch{return""}return""}function p(t,e){const n=t?.[e];return Array.isArray(n)?n.slice(0,50):null}function te(...t){return t.find(e=>e.trim())||""}function ee(t){return t.replace(/\\/g,"/").split("/").filter(Boolean).pop()||t}function u(t){for(const e of Object.keys(t)){const n=t[e];(n===void 0||n===""||Array.isArray(n)&&n.length===0)&&delete t[e]}return t}function m(t,e){if(!t||typeof t!="object"||Array.isArray(t))return null;const n=t[e];return!n||typeof n!="object"||Array.isArray(n)?null:n}function d(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function a(t,e){if(!t||typeof t!="object"||Array.isArray(t))return"";const n=t[e];return typeof n=="string"?n:""}function z(t,e){if(!t||typeof t!="object"||Array.isArray(t))return;const n=t[e];return typeof n=="number"&&Number.isFinite(n)?n:void 0}function C(t){if(typeof t!="string")return;const e=t.replace(/\s+/g," ").trim();if(e)return e.length>120?`${e.slice(0,117)}...`:e}export{ce as codexAcpSessionUpdatesFromNotification,le as extractAssistantTextFromTurn,ue as mapCodexNotification};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{JsonLineRpcClient as P}from"../../client/json-rpc.js";import{startCodexAppServer as q}from"../codex/codex-process.js";import{
|
|
2
|
-
`);this.lineBuffer=t.pop()??"";for(const s of t){const r=s.trim();if(!r)continue;let o;try{o=JSON.parse(r)}catch{continue}this.handleMessage(o)}}async handleMessage(e){if("id"in e&&("result"in e||"error"in e)&&typeof e.method!="string"){this.handleParentResponse(e);return}const t=typeof e.method=="string"?e.method:"",s=e.id;if(t){if(s===void 0){await this.handleNotification(t,u(e.params));return}try{const r=await this.handleRequest(t,u(e.params));this.write({jsonrpc:"2.0",id:s,result:r})}catch(r){this.write({jsonrpc:"2.0",id:s,error:{code:-32603,message:r instanceof Error?r.message:String(r)}})}}}async handleNotification(e,t){e==="session/cancel"&&await this.cancel(t)}async handleRequest(e,t){switch(e){case"initialize":return this.initializeResponse();case"session/new":return this.newSession(t);case"session/resume":case"session/load":return this.resumeSession(t);case"session/fork":return this.forkSession(t);case"session/prompt":return this.prompt(t);case"session/compact":return this.manualCompact(t);case"session/cancel":return await this.cancel(t),{};case"session/close":return await this.closeSession(t),{};case"session/set_config_option":return this.setConfigOption(t);case"session/set_mode":return this.setMode(t);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: ${e}`)}}initializeResponse(){return{protocolVersion:1,agentCapabilities:{loadSession:!0,sessionCapabilities:{resume:!0,close:!0}}}}async newSession(e){const t=a(e,"cwd")||process.cwd();this.applyInitialConfig(e),await this.ensureCodex(t);const s=await this.requireRpc().request("thread/start",{cwd:t,approvalsReviewer:"user",...this.codexConfig(),ephemeral:!1,threadSource:"aca-codex-acp"},6e4),r=C(s);if(!r)throw new Error("Codex thread/start did not return thread id");return this.currentSessionId=r,this.currentCwd=t,this.captureThreadMetadata(s),this.sessionResponse(r)}async resumeSession(e){const t=a(e,"sessionId");if(!t)throw new Error("sessionId is required");const s=a(e,"cwd")||this.currentCwd;this.applyInitialConfig(e),await this.ensureCodex(s);const r=await this.requireRpc().request("thread/resume",{threadId:t,cwd:s,approvalsReviewer:"user",...this.codexConfig()},6e4);return this.currentSessionId=C(r)||t,this.currentCwd=s,this.captureThreadMetadata(r),this.sessionResponse(this.currentSessionId)}async forkSession(e){const t=a(e,"sessionId");if(!t)throw new Error("sessionId is required");const s=a(e,"cwd")||this.currentCwd;this.applyInitialConfig(e),await this.ensureCodex(s);const r=await this.requireRpc().request("thread/fork",{threadId:t,cwd:s,approvalsReviewer:"user",ephemeral:!1,threadSource:"aca-codex-acp-fork",...this.codexConfig()},12e4),o=C(r);if(!o)throw new Error("Codex thread/fork did not return thread id");return this.currentSessionId=o,this.currentCwd=s,this.captureThreadMetadata(r),this.sessionResponse(o)}async prompt(e){const t=a(e,"sessionId")||this.currentSessionId;let s=this.resolveSessionAlias(t);if(!s)throw new Error("sessionId is required");const r=Array.isArray(e.prompt)?e.prompt:[],o=this.requireRpc();this.currentSessionId=s,this.promptActive=!0,this.currentTurnId=null,this.currentPromptText=$(r),this.currentAssistantText="",this.currentAffectedFiles.clear(),this.agentMessagePhases.clear(),this.planDeltas.reset();let i;try{i=await o.request("turn/start",{threadId:s,clientUserMessageId:`aca-acp-${Date.now()}-${Math.random().toString(16).slice(2)}`,input:N(r),cwd:this.currentCwd,...D(this.config)},R());const c=String(i?.turn?.id??"");this.currentTurnId=c||null;const l=c?await this.waitForTurnCompleted(c,R()):i?.turn;this.threadTurnCount+=1,this.previousUserText=this.currentPromptText,this.previousAssistantText=this.currentAssistantText,this.previousAffectedFiles.clear();for(const d of this.currentAffectedFiles)this.previousAffectedFiles.add(d);return{stopReason:"end_turn",sessionId:this.currentSessionId,turn:l,_meta:{acaContextUsage:W(this.latestTokenUsage)}}}finally{this.promptActive=!1,this.currentTurnId=null}}async cancel(e){const t=a(e,"sessionId")||this.currentSessionId;if(!(!t||!this.rpc)&&(await this.rpc.request("turn/interrupt",{threadId:t}).catch(()=>{}),this.currentTurnId)){const s=this.pendingTurnCompletions.get(this.currentTurnId);s&&(this.pendingTurnCompletions.delete(this.currentTurnId),clearTimeout(s.timer),s.reject(new Error("Codex prompt cancelled")))}}async manualCompact(e){const t=a(e,"sessionId")||this.currentSessionId,s=this.resolveSessionAlias(t);if(!s)throw new Error("sessionId is required");if(this.promptActive||this.maintenanceActive)throw new Error("Codex session is busy");this.currentSessionId=s;const r=A(),o=await this.maintainContext("manual",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{force:!0,allowRollover:e.allowRollover===!0,compactTimeoutMs:B(e,r.compactTimeoutMs)}),i=String(o.action||"");if(i==="compact_failed"||i==="rollover_failed"){const c=String(o.compactError||"Codex 上下文压缩未完成");throw new Error(`Codex 上下文压缩失败:${c}`)}return{sessionId:this.currentSessionId,maintenance:o,contextUsage:z(o)}}async closeSession(e){await this.shutdown()}async ensureCodex(e){if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;if(this.codexStartupPromise){await this.codexStartupPromise;return}const t=(async()=>{if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;const s=q(e),r=new P(s.child,{peerName:"Codex app-server",onNotification:(o,i)=>this.handleCodexNotification(o,i),onRequest:async(o,i)=>this.handleCodexRequest(o,i)});this.started=s,this.rpc=r,this.initialized=!1,await r.request("initialize",{clientInfo:{name:"aca-codex-acp",version:"0.1.0"},capabilities:{experimentalApi:!0,requestAttestation:!1}},6e4),this.initialized=!0})();this.codexStartupPromise=t;try{await t}catch(s){throw this.rpc&&this.started&&!this.initialized&&await T(this.started.child,1e3),this.rpc=null,this.started=null,s}finally{this.codexStartupPromise===t&&(this.codexStartupPromise=null)}}handleCodexNotification(e,t){if(this.captureContextNotification(e,t),!this.maintenanceActive&&!(!this.promptActive&&e!=="turn/completed")){this.rpc?.refreshPendingRequestTimeout("turn/start"),this.captureAgentMessagePhase(e,t),e==="turn/completed"&&this.resolveTurnCompleted(t);for(const s of _(e,t,{agentMessagePhases:this.agentMessagePhases,planDeltas:this.planDeltas}))this.captureUpdateEvidence(s),this.notify("session/update",{sessionId:this.currentSessionId,update:s})}}async handleCodexRequest(e,t){if(k(e))return b(e,t,async s=>{const r=await this.requestParent("session/request_permission",{sessionId:this.currentSessionId,request:{...s,params:t,method:e}}),o=u(r),i=u(o.outcome);return i.outcome==="selected"&&typeof i.optionId=="string"?{outcome:{outcome:"selected",optionId:i.optionId}}:{outcome:{outcome:"cancelled"}}});if(L(e)){const s=await this.requestParent("elicitation/create",{sessionId:this.currentSessionId,request:{...u(t),method:e}}),r=u(s),o=u(r.outcome);return o.outcome==="submitted"?{outcome:{outcome:"submitted",value:typeof o.value=="string"?o.value:""}}:{outcome:{outcome:"cancelled"}}}return{}}captureAgentMessagePhase(e,t){if(e!=="item/started"&&e!=="item/completed")return;const s=h(t,"item");if(a(s,"type")!=="agentMessage")return;const r=a(s,"id");r&&this.agentMessagePhases.set(r,a(s,"phase"))}waitForTurnCompleted(e,t){const s=this.completedTurns.get(e);return s?Promise.resolve(s):new Promise((r,o)=>{const i=setTimeout(()=>{this.pendingTurnCompletions.delete(e),o(new Error("Codex app-server request timed out while waiting for turn/completed"))},t);i.unref(),this.pendingTurnCompletions.set(e,{resolve:r,reject:o,timer:i})})}resolveTurnCompleted(e){const t=h(e,"turn"),s=a(t,"id");if(!s)return;this.completedTurns.set(s,t);const r=this.pendingTurnCompletions.get(s);if(r){if(this.pendingTurnCompletions.delete(s),clearTimeout(r.timer),a(t,"status")==="failed"){r.reject(new Error(`Codex turn failed: ${JSON.stringify(t.error??{})}`));return}r.resolve(t)}}async shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.pendingCompaction&&(clearTimeout(this.pendingCompaction.timer),this.pendingCompaction.reject(new Error("Codex ACP adapter closed")),this.pendingCompaction=null);for(const t of this.pendingParentRequests.values())clearTimeout(t.timer),t.reject(new Error("Codex ACP adapter closed"));this.pendingParentRequests.clear();for(const t of this.pendingTurnCompletions.values())clearTimeout(t.timer),t.reject(new Error("Codex ACP adapter closed"));this.pendingTurnCompletions.clear();const e=this.started?.child;!e||e.exitCode!==null||e.signalCode!==null||(e.stdin.end(),!await T(e,2e3)&&(e.kill("SIGTERM"),!await T(e,1e3)&&(e.kill("SIGKILL"),await T(e,1e3))))}sessionResponse(e){return{sessionId:e,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(e,t){this.write({jsonrpc:"2.0",method:e,params:t})}requestParent(e,t,s=600*1e3){const r=this.nextParentRequestId++;return new Promise((o,i)=>{const c=setTimeout(()=>{this.pendingParentRequests.delete(r),i(new Error(`ACP client request timed out: ${e}`))},s);c.unref(),this.pendingParentRequests.set(r,{resolve:o,reject:i,timer:c}),this.write({jsonrpc:"2.0",id:r,method:e,params:t})})}handleParentResponse(e){const t=Number(e.id),s=this.pendingParentRequests.get(t);if(s){if(this.pendingParentRequests.delete(t),clearTimeout(s.timer),"error"in e){const r=u(e.error);s.reject(new Error(a(r,"message")||"ACP client request failed"));return}s.resolve(e.result)}}setConfigOption(e){const t=a(e,"configId"),s=e.value;if(!t)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[t]=s,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"config_option_update",configId:t,value:s}}),{}}setMode(e){const t=a(e,"modeId");if(!t)throw new Error("modeId is required");return this.config.sandbox=t,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"current_mode_update",modeId:t}}),{}}applyInitialConfig(e){const t=h(e,"config");for(const[s,r]of Object.entries(t))(typeof r=="string"||typeof r=="boolean")&&(this.config[s]=r)}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(e,t){if(e==="thread/tokenUsage/updated"&&(this.latestTokenUsage=F(t)??this.latestTokenUsage),e==="thread/compacted"&&this.pendingCompaction&&this.resolveCompaction(),e==="item/completed"){const s=h(t,"item"),r=a(u(t),"threadId");a(s,"type")==="contextCompaction"&&(!r||r===this.currentSessionId)&&this.resolveCompaction(),a(s,"type")==="agentMessage"&&a(s,"phase")==="final_answer"&&(this.currentAssistantText=f(s)||this.currentAssistantText)}}resolveCompaction(){if(!this.pendingCompaction)return;const e=this.pendingCompaction;this.pendingCompaction=null,clearTimeout(e.timer),e.resolve()}captureUpdateEvidence(e){String(e.sessionUpdate||"")==="agent_message_chunk"&&(this.currentAssistantText+=f(e.content)),x(e,this.currentAffectedFiles)}captureThreadMetadata(e){const t=h(e,"thread");this.currentRolloutPath=a(t,"path")||null,this.threadTurnCount=Array.isArray(t.turns)?t.turns.length:0;const s=O(t.turns);s.userRequest&&(this.previousUserText=s.userRequest),s.assistantResponse&&(this.previousAssistantText=s.assistantResponse),this.previousAffectedFiles.clear();for(const r of s.affectedFiles)this.previousAffectedFiles.add(r);this.latestTokenUsage=null}async maintainContext(e,t,s={}){const r=A(),o=this.contextState(),i=v(o,r);if(!i.compact&&!s.force||!this.currentSessionId)return this.maintenanceResult({stage:e,action:"none",reasons:i.reasons,beforeState:o});const c=this.currentSessionId;this.notifyContextStatus("正在整理会话上下文",S(o)),this.maintenanceActive=!0;let l=!1,d="",g=o,m=i,I=!1,y="";try{const p=o.usage?.observedAtMs??0;await this.compactThread(c,s.compactTimeoutMs??r.compactTimeoutMs),await this.waitForTokenUsageAfter(p,2e3)}catch(p){l=!0,d=p instanceof Error?p.message:String(p)}try{if(g=this.contextState(),m=v(g,r,{compactAttempted:!0,compactFailed:l}),m.rollover&&s.allowRollover!==!1){this.notifyContextStatus("正在创建轻量会话",m.reasons.join(", "));try{await this.rotateThread(c,t),I=!0}catch(p){y=p instanceof Error?p.message:String(p),this.notifyContextStatus("轻量会话创建失败",y)}}else l||this.notifyContextStatus("会话上下文已压缩",S(g))}finally{this.maintenanceActive=!1}return this.maintenanceResult({action:I?"rollover":y?"rollover_failed":l?"compact_failed":"compact",stage:e,reasons:m.reasons,beforeState:o,afterState:g,compactError:[d,y].filter(Boolean).join("; "),previousThreadId:c,rolloverRecommended:m.rollover&&s.allowRollover===!1})}compactThread(e,t){return this.pendingCompaction?Promise.reject(new Error("Codex context compaction is already running")):new Promise((s,r)=>{const o=setTimeout(()=>{this.pendingCompaction?.timer===o&&(this.pendingCompaction=null),r(new Error("Codex thread compaction timed out"))},t);o.unref(),this.pendingCompaction={resolve:s,reject:r,timer:o},this.requireRpc().request("thread/compact/start",{threadId:e},t).catch(i=>{this.pendingCompaction?.timer===o&&(this.pendingCompaction=null,clearTimeout(o),r(i instanceof Error?i:new Error(String(i))))})})}async rotateThread(e,t){const s=E({previousThreadId:e,cwd:this.currentCwd,userRequest:t.userRequest,assistantResponse:t.assistantResponse,affectedFiles:t.affectedFiles}),r=await this.requireRpc().request("thread/start",{cwd:this.currentCwd,approvalsReviewer:"user",...this.codexConfig(),developerInstructions:s,ephemeral:!1,threadSource:"aca-codex-acp-rollover"},6e4),o=C(r);if(!o)throw new Error("Codex rollover thread/start did not return thread id");this.sessionAliases.set(e,o),this.currentSessionId=o,this.captureThreadMetadata(r)}async waitForTokenUsageAfter(e,t){const s=Date.now()+t;for(;Date.now()<s;){if((this.latestTokenUsage?.observedAtMs??0)>e)return;await G(50)}}resolveSessionAlias(e){if(!e)return null;let t=e;const s=new Set;for(;this.sessionAliases.has(t)&&!s.has(t);)s.add(t),t=this.sessionAliases.get(t)??t;return t}contextState(){return{usage:this.latestTokenUsage,rolloutBytes:U(this.currentRolloutPath),turnCount:this.threadTurnCount}}maintenanceResult(e){return{stage:e.stage,action:e.action,reasons:e.reasons,previousThreadId:e.previousThreadId??null,sessionId:this.currentSessionId,before:e.beforeState,after:e.afterState??null,compactError:e.compactError||null,rolloverRecommended:e.rolloverRecommended===!0}}notifyContextStatus(e,t){this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"aca_context_maintenance",label:e,detail:t}})}write(e){process.stdout.write(`${JSON.stringify(e)}
|
|
3
|
-
`)}}function
|
|
1
|
+
import{JsonLineRpcClient as P}from"../../client/json-rpc.js";import{startCodexAppServer as q}from"../codex/codex-process.js";import{CodexConnectionRecoveryTracker as _}from"../codex/connection-events.js";import{codexAcpSessionUpdatesFromNotification as k}from"../codex/events.js";import{CodexPlanDeltaState as M}from"../codex/plan-events.js";import{isCodexApprovalRequest as b,resolveCodexApprovalRequest as E}from"../codex/permissions.js";import{buildCodexContinuityInstructions as U,codexTokenUsageFromNotification as F,decideCodexContextMaintenance as I,readCodexContextMaintenancePolicy as S,rolloutFileSize as j}from"./context-maintenance.js";class N{lineBuffer="";started=null;rpc=null;codexStartupPromise=null;initialized=!1;currentSessionId=null;currentCwd=process.cwd();promptActive=!1;currentTurnId=null;nextParentRequestId=1;pendingParentRequests=new Map;completedTurns=new Map;pendingTurnCompletions=new Map;agentMessagePhases=new Map;planDeltas=new M;config={};sessionAliases=new Map;currentAffectedFiles=new Set;connectionRecovery=new _;latestTokenUsage=null;currentRolloutPath=null;threadTurnCount=0;currentPromptText="";currentAssistantText="";previousUserText="";previousAssistantText="";previousAffectedFiles=new Set;maintenanceActive=!1;pendingCompaction=null;shutdownPromise=null;constructor(){process.stdin.setEncoding("utf8"),process.stdin.on("data",t=>this.handleInput(String(t)));const e=()=>{this.shutdown().finally(()=>process.exit(0))};process.stdin.once("end",e),process.stdin.once("close",e),process.stdin.once("error",e),process.once("SIGTERM",()=>{this.shutdown().finally(()=>process.exit(0))}),process.once("SIGINT",()=>{this.shutdown().finally(()=>process.exit(130))})}handleInput(e){this.lineBuffer+=e;const t=this.lineBuffer.split(`
|
|
2
|
+
`);this.lineBuffer=t.pop()??"";for(const s of t){const r=s.trim();if(!r)continue;let o;try{o=JSON.parse(r)}catch{continue}this.handleMessage(o)}}async handleMessage(e){if("id"in e&&("result"in e||"error"in e)&&typeof e.method!="string"){this.handleParentResponse(e);return}const t=typeof e.method=="string"?e.method:"",s=e.id;if(t){if(s===void 0){await this.handleNotification(t,u(e.params));return}try{const r=await this.handleRequest(t,u(e.params));this.write({jsonrpc:"2.0",id:s,result:r})}catch(r){this.write({jsonrpc:"2.0",id:s,error:{code:-32603,message:r instanceof Error?r.message:String(r)}})}}}async handleNotification(e,t){e==="session/cancel"&&await this.cancel(t)}async handleRequest(e,t){switch(e){case"initialize":return this.initializeResponse();case"session/new":return this.newSession(t);case"session/resume":case"session/load":return this.resumeSession(t);case"session/fork":return this.forkSession(t);case"session/prompt":return this.prompt(t);case"session/compact":return this.manualCompact(t);case"session/cancel":return await this.cancel(t),{};case"session/close":return await this.closeSession(t),{};case"session/set_config_option":return this.setConfigOption(t);case"session/set_mode":return this.setMode(t);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: ${e}`)}}initializeResponse(){return{protocolVersion:1,agentCapabilities:{loadSession:!0,sessionCapabilities:{resume:!0,close:!0}}}}async newSession(e){const t=a(e,"cwd")||process.cwd();this.applyInitialConfig(e),await this.ensureCodex(t);const s=await this.requireRpc().request("thread/start",{cwd:t,approvalsReviewer:"user",...this.codexConfig(),ephemeral:!1,threadSource:"aca-codex-acp"},6e4),r=C(s);if(!r)throw new Error("Codex thread/start did not return thread id");return this.currentSessionId=r,this.currentCwd=t,this.captureThreadMetadata(s),this.sessionResponse(r)}async resumeSession(e){const t=a(e,"sessionId");if(!t)throw new Error("sessionId is required");const s=a(e,"cwd")||this.currentCwd;this.applyInitialConfig(e),await this.ensureCodex(s);const r=await this.requireRpc().request("thread/resume",{threadId:t,cwd:s,approvalsReviewer:"user",...this.codexConfig()},6e4);return this.currentSessionId=C(r)||t,this.currentCwd=s,this.captureThreadMetadata(r),this.sessionResponse(this.currentSessionId)}async forkSession(e){const t=a(e,"sessionId");if(!t)throw new Error("sessionId is required");const s=a(e,"cwd")||this.currentCwd;this.applyInitialConfig(e),await this.ensureCodex(s);const r=await this.requireRpc().request("thread/fork",{threadId:t,cwd:s,approvalsReviewer:"user",ephemeral:!1,threadSource:"aca-codex-acp-fork",...this.codexConfig()},12e4),o=C(r);if(!o)throw new Error("Codex thread/fork did not return thread id");return this.currentSessionId=o,this.currentCwd=s,this.captureThreadMetadata(r),this.sessionResponse(o)}async prompt(e){const t=a(e,"sessionId")||this.currentSessionId;let s=this.resolveSessionAlias(t);if(!s)throw new Error("sessionId is required");const r=Array.isArray(e.prompt)?e.prompt:[],o=this.requireRpc();this.currentSessionId=s,this.promptActive=!0,this.currentTurnId=null,this.currentPromptText=D(r),this.currentAssistantText="",this.currentAffectedFiles.clear(),this.connectionRecovery.reset(),this.agentMessagePhases.clear(),this.planDeltas.reset();let i;try{i=await o.request("turn/start",{threadId:s,clientUserMessageId:`aca-acp-${Date.now()}-${Math.random().toString(16).slice(2)}`,input:$(r),cwd:this.currentCwd,...O(this.config)},R());const c=String(i?.turn?.id??"");this.currentTurnId=c||null;const d=c?await this.waitForTurnCompleted(c,R()):i?.turn;this.threadTurnCount+=1,this.previousUserText=this.currentPromptText,this.previousAssistantText=this.currentAssistantText,this.previousAffectedFiles.clear();for(const l of this.currentAffectedFiles)this.previousAffectedFiles.add(l);return{stopReason:"end_turn",sessionId:this.currentSessionId,turn:d,_meta:{acaContextUsage:L(this.latestTokenUsage)}}}catch(c){const d=this.connectionRecovery.fail(c);throw d&&this.notifySessionUpdate(d),c}finally{this.promptActive=!1,this.currentTurnId=null}}async cancel(e){const t=a(e,"sessionId")||this.currentSessionId;if(!(!t||!this.rpc)&&(await this.rpc.request("turn/interrupt",{threadId:t}).catch(()=>{}),this.currentTurnId)){const s=this.pendingTurnCompletions.get(this.currentTurnId);s&&(this.pendingTurnCompletions.delete(this.currentTurnId),clearTimeout(s.timer),s.reject(new Error("Codex prompt cancelled")))}}async manualCompact(e){const t=a(e,"sessionId")||this.currentSessionId,s=this.resolveSessionAlias(t);if(!s)throw new Error("sessionId is required");if(this.promptActive||this.maintenanceActive)throw new Error("Codex session is busy");this.currentSessionId=s;const r=S(),o=await this.maintainContext("manual",{userRequest:this.previousUserText,assistantResponse:this.previousAssistantText,affectedFiles:[...this.previousAffectedFiles]},{force:!0,allowRollover:e.allowRollover===!0,compactTimeoutMs:z(e,r.compactTimeoutMs)}),i=String(o.action||"");if(i==="compact_failed"||i==="rollover_failed"){const c=String(o.compactError||"Codex 上下文压缩未完成");throw new Error(`Codex 上下文压缩失败:${c}`)}return{sessionId:this.currentSessionId,maintenance:o,contextUsage:W(o)}}async closeSession(e){await this.shutdown()}async ensureCodex(e){if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;if(this.codexStartupPromise){await this.codexStartupPromise;return}const t=(async()=>{if(this.rpc&&this.started&&this.started.child.exitCode===null&&this.started.child.signalCode===null)return;const s=q(e),r=new P(s.child,{peerName:"Codex app-server",onNotification:(o,i)=>this.handleCodexNotification(o,i),onRequest:async(o,i)=>this.handleCodexRequest(o,i)});this.started=s,this.rpc=r,this.initialized=!1,await r.request("initialize",{clientInfo:{name:"aca-codex-acp",version:"0.1.0"},capabilities:{experimentalApi:!0,requestAttestation:!1}},6e4),this.initialized=!0})();this.codexStartupPromise=t;try{await t}catch(s){throw this.rpc&&this.started&&!this.initialized&&await T(this.started.child,1e3),this.rpc=null,this.started=null,s}finally{this.codexStartupPromise===t&&(this.codexStartupPromise=null)}}handleCodexNotification(e,t){if(this.captureContextNotification(e,t),this.maintenanceActive||!this.promptActive&&e!=="turn/completed")return;this.rpc?.refreshPendingRequestTimeout("turn/start"),this.captureAgentMessagePhase(e,t);const s=this.connectionRecovery.beforeNotification(e,t);e==="turn/completed"&&this.resolveTurnCompleted(t);const r=k(e,t,{agentMessagePhases:this.agentMessagePhases,planDeltas:this.planDeltas});for(const o of[...s,...r])this.captureUpdateEvidence(o),this.notifySessionUpdate(o)}notifySessionUpdate(e){this.notify("session/update",{sessionId:this.currentSessionId,update:e})}async handleCodexRequest(e,t){if(b(e))return E(e,t,async s=>{const r=await this.requestParent("session/request_permission",{sessionId:this.currentSessionId,request:{...s,params:t,method:e}}),o=u(r),i=u(o.outcome);return i.outcome==="selected"&&typeof i.optionId=="string"?{outcome:{outcome:"selected",optionId:i.optionId}}:{outcome:{outcome:"cancelled"}}});if(G(e)){const s=await this.requestParent("elicitation/create",{sessionId:this.currentSessionId,request:{...u(t),method:e}}),r=u(s),o=u(r.outcome);return o.outcome==="submitted"?{outcome:{outcome:"submitted",value:typeof o.value=="string"?o.value:""}}:{outcome:{outcome:"cancelled"}}}return{}}captureAgentMessagePhase(e,t){if(e!=="item/started"&&e!=="item/completed")return;const s=h(t,"item");if(a(s,"type")!=="agentMessage")return;const r=a(s,"id");r&&this.agentMessagePhases.set(r,a(s,"phase"))}waitForTurnCompleted(e,t){const s=this.completedTurns.get(e);return s?Promise.resolve(s):new Promise((r,o)=>{const i=setTimeout(()=>{this.pendingTurnCompletions.delete(e),o(new Error("Codex app-server request timed out while waiting for turn/completed"))},t);i.unref(),this.pendingTurnCompletions.set(e,{resolve:r,reject:o,timer:i})})}resolveTurnCompleted(e){const t=h(e,"turn"),s=a(t,"id");if(!s)return;this.completedTurns.set(s,t);const r=this.pendingTurnCompletions.get(s);if(r){if(this.pendingTurnCompletions.delete(s),clearTimeout(r.timer),a(t,"status")==="failed"){r.reject(new Error(`Codex turn failed: ${JSON.stringify(t.error??{})}`));return}r.resolve(t)}}async shutdown(){return this.shutdownPromise?this.shutdownPromise:(this.shutdownPromise=this.performShutdown(),this.shutdownPromise)}async performShutdown(){this.pendingCompaction&&(clearTimeout(this.pendingCompaction.timer),this.pendingCompaction.reject(new Error("Codex ACP adapter closed")),this.pendingCompaction=null);for(const t of this.pendingParentRequests.values())clearTimeout(t.timer),t.reject(new Error("Codex ACP adapter closed"));this.pendingParentRequests.clear();for(const t of this.pendingTurnCompletions.values())clearTimeout(t.timer),t.reject(new Error("Codex ACP adapter closed"));this.pendingTurnCompletions.clear();const e=this.started?.child;!e||e.exitCode!==null||e.signalCode!==null||(e.stdin.end(),!await T(e,2e3)&&(e.kill("SIGTERM"),!await T(e,1e3)&&(e.kill("SIGKILL"),await T(e,1e3))))}sessionResponse(e){return{sessionId:e,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(e,t){this.write({jsonrpc:"2.0",method:e,params:t})}requestParent(e,t,s=600*1e3){const r=this.nextParentRequestId++;return new Promise((o,i)=>{const c=setTimeout(()=>{this.pendingParentRequests.delete(r),i(new Error(`ACP client request timed out: ${e}`))},s);c.unref(),this.pendingParentRequests.set(r,{resolve:o,reject:i,timer:c}),this.write({jsonrpc:"2.0",id:r,method:e,params:t})})}handleParentResponse(e){const t=Number(e.id),s=this.pendingParentRequests.get(t);if(s){if(this.pendingParentRequests.delete(t),clearTimeout(s.timer),"error"in e){const r=u(e.error);s.reject(new Error(a(r,"message")||"ACP client request failed"));return}s.resolve(e.result)}}setConfigOption(e){const t=a(e,"configId"),s=e.value;if(!t)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[t]=s,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"config_option_update",configId:t,value:s}}),{}}setMode(e){const t=a(e,"modeId");if(!t)throw new Error("modeId is required");return this.config.sandbox=t,this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"current_mode_update",modeId:t}}),{}}applyInitialConfig(e){const t=h(e,"config");for(const[s,r]of Object.entries(t))(typeof r=="string"||typeof r=="boolean")&&(this.config[s]=r)}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(e,t){if(e==="thread/tokenUsage/updated"&&(this.latestTokenUsage=F(t)??this.latestTokenUsage),e==="thread/compacted"&&this.pendingCompaction&&this.resolveCompaction(),e==="item/completed"){const s=h(t,"item"),r=a(u(t),"threadId");a(s,"type")==="contextCompaction"&&(!r||r===this.currentSessionId)&&this.resolveCompaction(),a(s,"type")==="agentMessage"&&a(s,"phase")==="final_answer"&&(this.currentAssistantText=f(s)||this.currentAssistantText)}}resolveCompaction(){if(!this.pendingCompaction)return;const e=this.pendingCompaction;this.pendingCompaction=null,clearTimeout(e.timer),e.resolve()}captureUpdateEvidence(e){String(e.sessionUpdate||"")==="agent_message_chunk"&&(this.currentAssistantText+=f(e.content)),x(e,this.currentAffectedFiles)}captureThreadMetadata(e){const t=h(e,"thread");this.currentRolloutPath=a(t,"path")||null,this.threadTurnCount=Array.isArray(t.turns)?t.turns.length:0;const s=B(t.turns);s.userRequest&&(this.previousUserText=s.userRequest),s.assistantResponse&&(this.previousAssistantText=s.assistantResponse),this.previousAffectedFiles.clear();for(const r of s.affectedFiles)this.previousAffectedFiles.add(r);this.latestTokenUsage=null}async maintainContext(e,t,s={}){const r=S(),o=this.contextState(),i=I(o,r);if(!i.compact&&!s.force||!this.currentSessionId)return this.maintenanceResult({stage:e,action:"none",reasons:i.reasons,beforeState:o});const c=this.currentSessionId;this.notifyContextStatus("正在整理会话上下文",A(o)),this.maintenanceActive=!0;let d=!1,l="",g=o,m=i,v=!1,y="";try{const p=o.usage?.observedAtMs??0;await this.compactThread(c,s.compactTimeoutMs??r.compactTimeoutMs),await this.waitForTokenUsageAfter(p,2e3)}catch(p){d=!0,l=p instanceof Error?p.message:String(p)}try{if(g=this.contextState(),m=I(g,r,{compactAttempted:!0,compactFailed:d}),m.rollover&&s.allowRollover!==!1){this.notifyContextStatus("正在创建轻量会话",m.reasons.join(", "));try{await this.rotateThread(c,t),v=!0}catch(p){y=p instanceof Error?p.message:String(p),this.notifyContextStatus("轻量会话创建失败",y)}}else d||this.notifyContextStatus("会话上下文已压缩",A(g))}finally{this.maintenanceActive=!1}return this.maintenanceResult({action:v?"rollover":y?"rollover_failed":d?"compact_failed":"compact",stage:e,reasons:m.reasons,beforeState:o,afterState:g,compactError:[l,y].filter(Boolean).join("; "),previousThreadId:c,rolloverRecommended:m.rollover&&s.allowRollover===!1})}compactThread(e,t){return this.pendingCompaction?Promise.reject(new Error("Codex context compaction is already running")):new Promise((s,r)=>{const o=setTimeout(()=>{this.pendingCompaction?.timer===o&&(this.pendingCompaction=null),r(new Error("Codex thread compaction timed out"))},t);o.unref(),this.pendingCompaction={resolve:s,reject:r,timer:o},this.requireRpc().request("thread/compact/start",{threadId:e},t).catch(i=>{this.pendingCompaction?.timer===o&&(this.pendingCompaction=null,clearTimeout(o),r(i instanceof Error?i:new Error(String(i))))})})}async rotateThread(e,t){const s=U({previousThreadId:e,cwd:this.currentCwd,userRequest:t.userRequest,assistantResponse:t.assistantResponse,affectedFiles:t.affectedFiles}),r=await this.requireRpc().request("thread/start",{cwd:this.currentCwd,approvalsReviewer:"user",...this.codexConfig(),developerInstructions:s,ephemeral:!1,threadSource:"aca-codex-acp-rollover"},6e4),o=C(r);if(!o)throw new Error("Codex rollover thread/start did not return thread id");this.sessionAliases.set(e,o),this.currentSessionId=o,this.captureThreadMetadata(r)}async waitForTokenUsageAfter(e,t){const s=Date.now()+t;for(;Date.now()<s;){if((this.latestTokenUsage?.observedAtMs??0)>e)return;await J(50)}}resolveSessionAlias(e){if(!e)return null;let t=e;const s=new Set;for(;this.sessionAliases.has(t)&&!s.has(t);)s.add(t),t=this.sessionAliases.get(t)??t;return t}contextState(){return{usage:this.latestTokenUsage,rolloutBytes:j(this.currentRolloutPath),turnCount:this.threadTurnCount}}maintenanceResult(e){return{stage:e.stage,action:e.action,reasons:e.reasons,previousThreadId:e.previousThreadId??null,sessionId:this.currentSessionId,before:e.beforeState,after:e.afterState??null,compactError:e.compactError||null,rolloverRecommended:e.rolloverRecommended===!0}}notifyContextStatus(e,t){this.notify("session/update",{sessionId:this.currentSessionId,update:{sessionUpdate:"aca_context_maintenance",label:e,detail:t}})}write(e){process.stdout.write(`${JSON.stringify(e)}
|
|
3
|
+
`)}}function $(n){return n.map(e=>{const t=u(e);if(t.type==="image"){const s=a(t,"uri");if(s)return{type:"localImage",path:s};const r=a(t,"data");return{type:"image",url:`data:${a(t,"mimeType")||"image/png"};base64,${r}`}}return{type:"text",text:a(t,"text"),text_elements:[]}})}function D(n){return n.map(e=>a(u(e),"text")).filter(Boolean).join(`
|
|
4
4
|
|
|
5
|
-
`)}function
|
|
5
|
+
`)}function O(n){const e={};typeof n.model=="string"&&n.model&&(e.model=n.model),typeof n.approvalPolicy=="string"&&n.approvalPolicy&&(e.approvalPolicy=n.approvalPolicy),typeof n.sandbox=="string"&&n.sandbox&&(e.sandbox=n.sandbox);const t=n.reasoning_effort??n.reasoningEffort??n.model_reasoning_effort??n.modelReasoningEffort??n.effort;return typeof t=="string"&&t&&(e.effort=t),e}function C(n){const e=h(n,"thread");return a(e,"id")||null}function h(n,e){const t=u(n);return u(t[e])}function u(n){return n&&typeof n=="object"&&!Array.isArray(n)?n:{}}function f(n){if(typeof n=="string")return n;if(Array.isArray(n))return n.map(f).filter(Boolean).join("");if(!n||typeof n!="object")return"";const e=u(n);for(const t of["text","message","content","output_text"]){const s=f(e[t]);if(s)return s}return""}function x(n,e){if(Array.isArray(n)){for(const o of n)x(o,e);return}const t=u(n),s=a(t,"type").toLowerCase(),r=a(t,"path");if(r&&["diff","resource_link","content"].includes(s)&&e.add(r),Array.isArray(t.locations))for(const o of t.locations){const i=a(u(o),"path");i&&e.add(i)}for(const o of[t.content,t.update,t.rawInput,t.rawOutput])o&&o!==n&&x(o,e)}function B(n){if(!Array.isArray(n)||n.length===0)return{userRequest:"",assistantResponse:"",affectedFiles:[]};const e=n.at(-1),t=new Set;x(e,t);let s="",r="";const o=i=>{if(Array.isArray(i)){for(const l of i)o(l);return}if(!i||typeof i!="object")return;const c=u(i),d=a(c,"type").toLowerCase();d==="usermessage"&&(s=f(c)||s),d==="agentmessage"&&(r=f(c)||r);for(const l of["items","content","input"])c[l]&&o(c[l])};return o(e),{userRequest:s,assistantResponse:r,affectedFiles:[...t]}}function z(n,e){const t=w(n,"timeoutMs");return!Number.isFinite(t)||t<=0?e:Math.max(3e4,Math.min(59e4,t-1e4))}function W(n){const e=String(n.action||"none"),t=u(n.after),s=u(n.before),r=u(t.usage),o=u(s.usage),i=Object.keys(r).length>0?r:o,c=w(i,"contextWindow");if(e==="rollover")return{usedTokens:0,inputTokens:0,contextWindow:c,ratio:0,observedAtMs:Date.now(),state:"new_thread",maintenanceAction:e};if(Object.keys(i).length===0)return null;const d=w(i,"totalTokens"),l=w(i,"inputTokens");return{usedTokens:d||l,inputTokens:l,contextWindow:c,ratio:c>0?(d||l)/c:0,observedAtMs:w(i,"observedAtMs")||Date.now(),state:e==="compact"?"compacted":"active",maintenanceAction:e}}function L(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 A(n){const e=n.usage?`${n.usage.inputTokens} tokens`:"token unknown",t=n.rolloutBytes>0?`${Math.round(n.rolloutBytes/1024/1024)}MB rollout`:"rollout unknown";return`${e}, ${t}, ${n.turnCount} turns`}function w(n,e){const t=u(n)[e];return typeof t=="number"&&Number.isFinite(t)?t:0}function a(n,e){const s=u(n)[e];return typeof s=="string"?s:""}function G(n){const e=n.toLowerCase();return e.includes("elicitation")||e.includes("requestuserinput")||e.includes("request_user_input")||e.includes("input/request")}function R(){const n=Number.parseInt(process.env.ACA_ACP_PROMPT_TIMEOUT_MS??"",10);return Number.isInteger(n)&&n>=3e4?n:360*60*1e3}function T(n,e){return n.exitCode!==null||n.signalCode!==null?Promise.resolve(!0):new Promise(t=>{const s=setTimeout(()=>t(!1),e);s.unref(),n.once("close",()=>{clearTimeout(s),t(!0)})})}function J(n){return new Promise(e=>setTimeout(e,n))}function K(){new N}const V=process.argv[1]||"";/(?:^|[\\/])adapter\.(?:js|ts)$/.test(V)&&K();export{K as runCodexAcpAdapter};
|
package/dist/core/job-worker.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import L from"node:crypto";import E from"node:fs";import x from"node:path";import{compactAgentContext as W,forkAgentSession as V,sendAgentPrompt as q}from"../acp/agent.js";import{acaServerDownload as D,acaServerRequest as y}from"./aca-server-client.js";import{loadAcaConfig as H}from"./aca-config.js";import{prepareLocalWorktree as G}from"./worktree.js";import{expandWorkerRuntimePath as K}from"./worker-runtime-paths.js";import{collectAffectedFiles as M,extractAffectedFilePathsFromAcpRaw as X}from"./affected-files.js";import{resolveJobPermissionPolicy as Z}from"./job-permission-policy.js";import{appendCoalescedAcpEvent as Q}from"./acp-event-coalescer.js";import{shouldUploadAcpEvent as Y}from"./acp-event-upload-filter.js";const ee=1500,te=5e3,N=1e3,ne=200,oe=64,O=900,ae=1e4,ie=0,re=12e3;function se(e){return K(e)}function ce(e){const t=e.project_metadata;return t&&typeof t=="object"&&!Array.isArray(t)&&t.kind==="chat"?!0:typeof e.project_root_path=="string"&&(e.project_root_path.startsWith("~/.aica/chats/")||e.project_root_path.startsWith("~/.aca/chats/")||/(^|\/)\.aica\/chats\//.test(e.project_root_path)||/(^|\/)\.aca\/chats\//.test(e.project_root_path))}function He(e,t={}){let n=!1,a=null,o=null;const r=new Set,l=new Set,u=new Map,i=async()=>{if(n)return;const s=le();if(U(r.size,s))try{const p=t.loadConfig?.()??H();if(!p.token)return;for(;!n&&U(r.size,s);){const A=t.claimNextJob?await t.claimNextJob(p):await y("POST","/api/client/jobs/claim",{machineId:p.machineId,workspaceId:p.defaultWorkspaceId});if(!A.item)break;const f=A.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(f,p):k(f.job_id,p.machineId,"cancelled")).catch(()=>{});break}const m=f.session_id;l.has(m)&&e.warn(`claimed job=${f.job_id} for already active session=${m}; running anyway to avoid leaving it stuck`),r.add(f.job_id),l.add(m);const _=new AbortController,P=(t.runJob??pe)(f,p,e,_).catch(d=>{e.warn(`job ${f.job_id} failed outside handler: ${d instanceof Error?d.message:String(d)}`)}).finally(()=>{r.delete(f.job_id),l.delete(m),u.delete(f.job_id)});u.set(f.job_id,{controller:_,promise:P})}}catch(p){n||e.warn(`job worker failed: ${p instanceof Error?p.message:String(p)}`)}},h=()=>{if(n||a)return;const s=i();a=s,s.finally(()=>{a===s&&(a=null)})};h();const S=setInterval(()=>{h()},t.claimIntervalMs??ee);return{timer:S,stop:()=>o||(o=(async()=>{if(n)return;n=!0,clearInterval(S);for(const p of u.values())p.controller.abort();await a?.catch(()=>{});for(const p of u.values())p.controller.abort();const s=[...u.values()].map(p=>p.promise);s.length!==0&&await Promise.race([Promise.allSettled(s).then(()=>{}),j(re)])})(),o)}}function le(){const e=process.env.ACA_MAX_CONCURRENT_JOBS||process.env.ACA_MAX_CONCURRENT_PROJECT_JOBS||"",t=Number.parseInt(e,10);return!e.trim()||!Number.isFinite(t)||t<0?ie:t===0?0:Math.max(1,Math.min(t,8))}function U(e,t){return t===0||e<t}function ue(){const e=Number.parseInt(process.env.ACA_ACP_EVENT_BATCH_INTERVAL_MS||"",10);return Number.isFinite(e)?Math.max(40,Math.min(e,2e3)):ne}async function pe(e,t,n,a){const o=e.job_id,r=e.session_id,l=e.workspace_id,u=e.project_id??null,i=e.input??{},h=String(i.control?.type||"").toLowerCase();if(!h&&!i.message?.trim()&&(i.attachments??[]).length===0){await k(o,t.machineId,"job input missing message");return}const S=t.projects.find(d=>d.projectId===u),s=se(S?.rootPath||e.project_root_path||"");if(!s){await k(o,t.machineId,"job project root is unknown");return}ce(e)&&E.mkdirSync(s,{recursive:!0}),n.info(`claimed job=${o} session=${r} project=${S?.name??u??"unknown"}`);const p=setInterval(()=>{y("POST",`/api/client/jobs/${encodeURIComponent(o)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}),je(o,a).catch(()=>{})},te);let A=0;const f=ye(o,t.machineId),m=_e(o,t.machineId),_=new Set;let P=null;try{await m.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const d=G({projectId:u,sessionId:r,projectRoot:s,worktree:e});P=d,await Re(o,t.machineId,d);const I=fe(i.acpRun);if(h==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const c=await W({agentType:C(I.agentType,"codex"),cliType:C(I.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,allowRollover:i.control?.allowRollover===!0,signal:a.signal,onStatus:b=>{m.report({phase:b.phase,label:b.label,detail:b.detail,updateType:b.updateType,raw:b})}});await m.flush(),await y("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:c.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:i.control?.allowRollover===!0},acpSessionId:c.acpSessionId,durationMs:c.durationMs,usage:c.contextUsage,maintenance:c.maintenance,worktree:v(d)}}),n.info(`completed context compact job=${o}`);return}if(h==="fork"){const c=String(i.control?.sourceAcpSessionId||"").trim();if(!c)throw new Error("Source session does not have a Codex ACP context to fork");await m.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:i.control?.sourceSessionId?`源会话 ${i.control.sourceSessionId}`:void 0}).catch(()=>{});const b=await V({agentType:C(I.agentType,"codex"),cliType:C(I.cliType,"builtin"),sourceProviderSessionId:c,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,signal:a.signal,onStatus:g=>{m.report({phase:g.phase,label:g.label,detail:g.detail,updateType:g.updateType,raw:g})}});await m.flush(),await y("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:b.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:i.control?.sourceSessionId||null},acpSessionId:b.acpSessionId,worktree:v(d)}}),n.info(`completed Codex session fork job=${o}`);return}const T=await Ae({cwd:d.cwd,sessionId:r,jobId:o,attachments:i.attachments??[]});T.length>0&&(n.info(`materialized ${T.length} attachment(s) for job=${o}`),await m.sendNow({phase:"initializing",label:"附件已写入本机",detail:T.map(c=>c.relativePath).join(", "),raw:{attachments:z(T)}}).catch(()=>{}));const $=Se(i,T),F=ge($,T),w=await q({agentType:C(I.agentType,"codex"),cliType:C(I.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:de(e.session_raw),prompt:$,promptBlocks:F,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,canExecuteTools:i.canExecuteTools,timeoutMs:i.timeoutMs,contextWindow:he(I.contextWindow),signal:a.signal,onStatus:c=>{m.report({phase:c.phase,label:c.label,detail:c.detail,updateType:c.updateType,raw:c})},onUpdate:c=>{A+=1;for(const g of X(c.raw))_.add(g);const b={turnId:e.user_turn_id??null,eventType:c.type,label:c.label,text:c.text,status:c.status,toolCallId:c.toolCallId,raw:Te(c.raw,A)};Y(b)&&f.push(b)},onPermissionRequest:c=>{const b=Z(i,c);return b?Promise.resolve(b):xe(o,t.machineId,c,a.signal)},onUserInputRequest:c=>Ee(o,t.machineId,c,a.signal)});await f.flush(),await m.flush();const J=M({cwd:d.cwd,evidencePaths:_}),R=we(w.promptResponse);if(!(be(R)||!!w.content)){await k(o,t.machineId,`${w.agentType} stopped: ${R??"unknown"}`,{acpSessionId:w.acpSessionId,stopReason:R,exitCode:w.exitCode,stderr:$e(w.stderr,4e3),worktree:v(d)});return}await y("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:w.acpSessionId,assistantText:w.content,result:{ok:!0,acpSessionId:w.acpSessionId,stopReason:R,durationMs:w.durationMs,acpRun:i.acpRun??null,effectiveConfig:w.effectiveConfig??null,performance:w.performance??null,usage:w.contextUsage??null,updateCount:w.updates.length,statusTimeline:w.statusTimeline,worktree:v(d),attachments:z(T),files:J}}),n.info(`completed job=${o}`)}catch(d){await f.flush(),await m.flush();const I=a.signal.aborted?"cancelled":d instanceof Error?d.message:String(d),T=P?M({cwd:P.cwd,evidencePaths:_}):[];await k(o,t.machineId,me(I),{rawError:I,files:T})}finally{await f.flush(),await m.flush(),clearInterval(p)}}function de(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e.importedProviderSession;return!!(t&&typeof t=="object"&&!Array.isArray(t)&&t.strictResume===!0)}function me(e){return/ACP request timed out/i.test(e)?"ACP 长时间没有完成,请稍后重试,或点击停止后重新发送。":/MimoCode request timed out/i.test(e)?"MimoCode 长时间没有完成,请稍后重试,或点击停止后重新发送。":e.replace(/\bsession\/prompt\b/g,"ACP 请求")}function fe(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function he(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function C(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function we(e){if(!e||typeof e!="object"||Array.isArray(e))return;const t=e;if(typeof t.stopReason=="string")return t.stopReason;if(typeof t.reason=="string")return t.reason;const n=t.part;if(n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.reason=="string")return n.reason}function be(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function ye(e,t){let n=[],a=null,o=Promise.resolve();const r=async i=>{i.length!==0&&await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/events/batch`,{machineId:t,events:i})},l=()=>{a&&(clearTimeout(a),a=null);const i=n;return n=[],o=o.then(()=>r(i)).catch(()=>{}),o},u=()=>{a||(a=setTimeout(()=>{l()},ue()),a.unref())};return{push(i){if(Q(n,i),Ie(i)||n.length>=oe){l();return}u()},flush:l}}function Ie(e){return e.eventType==="tool_call"?!0:e.eventType!=="tool_call_update"?!1:["completed","failed","cancelled"].includes(String(e.status||"").toLowerCase())}function _e(e,t){let n=null,a=0,o="",r=null,l=Promise.resolve();const u=s=>JSON.stringify({phase:s.phase??"",label:s.label??"",detail:s.detail??"",updateType:s.updateType??""}),i=async s=>{await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:s.phase,label:s.label,detail:s.detail,updateType:s.updateType,raw:s.raw}),a=Date.now(),o=u(s)},h=()=>{if(r&&(clearTimeout(r),r=null),!n)return l;const s=n;return n=null,l=l.then(()=>i(s)).catch(()=>{}),l},S=s=>{r||(r=setTimeout(()=>{h()},s),r.unref())};return{report(s){const p=Date.now(),f=u(s)!==o,m=p-a>=ae;if(!f&&!m)return;n=s;const _=p-a;if(_>=O||a===0){h();return}S(O-_)},sendNow(s){return n=s,h()},flush:h}}function Te(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function Se(e,t=[]){const n=e?.message?.trim()??"";if(t.length===0)return n;const o=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((r,l)=>`${l+1}. ${r.name} (${r.kind}, ${r.mimeType}, ${ke(r.size)})
|
|
1
|
+
import L from"node:crypto";import E from"node:fs";import x from"node:path";import{compactAgentContext as W,forkAgentSession as V,sendAgentPrompt as q}from"../acp/agent.js";import{acaServerDownload as D,acaServerRequest as y}from"./aca-server-client.js";import{loadAcaConfig as H}from"./aca-config.js";import{prepareLocalWorktree as G}from"./worktree.js";import{expandWorkerRuntimePath as K}from"./worker-runtime-paths.js";import{collectAffectedFiles as N,extractAffectedFilePathsFromAcpRaw as X}from"./affected-files.js";import{resolveJobPermissionPolicy as Z}from"./job-permission-policy.js";import{appendCoalescedAcpEvent as Q}from"./acp-event-coalescer.js";import{shouldUploadAcpEvent as Y}from"./acp-event-upload-filter.js";import{userFacingAcpError as ee}from"../acp/error-display.js";const te=1500,ne=5e3,M=1e3,oe=200,ae=64,O=900,ie=1e4,re=0,se=12e3;function ce(e){return K(e)}function le(e){const t=e.project_metadata;return t&&typeof t=="object"&&!Array.isArray(t)&&t.kind==="chat"?!0:typeof e.project_root_path=="string"&&(e.project_root_path.startsWith("~/.aica/chats/")||e.project_root_path.startsWith("~/.aca/chats/")||/(^|\/)\.aica\/chats\//.test(e.project_root_path)||/(^|\/)\.aca\/chats\//.test(e.project_root_path))}function Ge(e,t={}){let n=!1,a=null,o=null;const r=new Set,l=new Set,u=new Map,i=async()=>{if(n)return;const s=ue();if(U(r.size,s))try{const p=t.loadConfig?.()??H();if(!p.token)return;for(;!n&&U(r.size,s);){const A=t.claimNextJob?await t.claimNextJob(p):await y("POST","/api/client/jobs/claim",{machineId:p.machineId,workspaceId:p.defaultWorkspaceId});if(!A.item)break;const f=A.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(f,p):P(f.job_id,p.machineId,"cancelled")).catch(()=>{});break}const m=f.session_id;l.has(m)&&e.warn(`claimed job=${f.job_id} for already active session=${m}; running anyway to avoid leaving it stuck`),r.add(f.job_id),l.add(m);const _=new AbortController,C=(t.runJob??de)(f,p,e,_).catch(d=>{e.warn(`job ${f.job_id} failed outside handler: ${d instanceof Error?d.message:String(d)}`)}).finally(()=>{r.delete(f.job_id),l.delete(m),u.delete(f.job_id)});u.set(f.job_id,{controller:_,promise:C})}}catch(p){n||e.warn(`job worker failed: ${p instanceof Error?p.message:String(p)}`)}},h=()=>{if(n||a)return;const s=i();a=s,s.finally(()=>{a===s&&(a=null)})};h();const g=setInterval(()=>{h()},t.claimIntervalMs??te);return{timer:g,stop:()=>o||(o=(async()=>{if(n)return;n=!0,clearInterval(g);for(const p of u.values())p.controller.abort();await a?.catch(()=>{});for(const p of u.values())p.controller.abort();const s=[...u.values()].map(p=>p.promise);s.length!==0&&await Promise.race([Promise.allSettled(s).then(()=>{}),j(se)])})(),o)}}function ue(){const e=process.env.ACA_MAX_CONCURRENT_JOBS||process.env.ACA_MAX_CONCURRENT_PROJECT_JOBS||"",t=Number.parseInt(e,10);return!e.trim()||!Number.isFinite(t)||t<0?re:t===0?0:Math.max(1,Math.min(t,8))}function U(e,t){return t===0||e<t}function pe(){const e=Number.parseInt(process.env.ACA_ACP_EVENT_BATCH_INTERVAL_MS||"",10);return Number.isFinite(e)?Math.max(40,Math.min(e,2e3)):oe}async function de(e,t,n,a){const o=e.job_id,r=e.session_id,l=e.workspace_id,u=e.project_id??null,i=e.input??{},h=String(i.control?.type||"").toLowerCase();if(!h&&!i.message?.trim()&&(i.attachments??[]).length===0){await P(o,t.machineId,"job input missing message");return}const g=t.projects.find(d=>d.projectId===u),s=ce(g?.rootPath||e.project_root_path||"");if(!s){await P(o,t.machineId,"job project root is unknown");return}le(e)&&E.mkdirSync(s,{recursive:!0}),n.info(`claimed job=${o} session=${r} project=${g?.name??u??"unknown"}`);const p=setInterval(()=>{y("POST",`/api/client/jobs/${encodeURIComponent(o)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}),je(o,a).catch(()=>{})},ne);let A=0;const f=ye(o,t.machineId),m=_e(o,t.machineId),_=new Set;let C=null;try{await m.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const d=G({projectId:u,sessionId:r,projectRoot:s,worktree:e});C=d,await Re(o,t.machineId,d);const I=fe(i.acpRun);if(h==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const c=await W({agentType:v(I.agentType,"codex"),cliType:v(I.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,allowRollover:i.control?.allowRollover===!0,signal:a.signal,onStatus:b=>{m.report({phase:b.phase,label:b.label,detail:b.detail,updateType:b.updateType,raw:b})}});await m.flush(),await y("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:c.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:i.control?.allowRollover===!0},acpSessionId:c.acpSessionId,durationMs:c.durationMs,usage:c.contextUsage,maintenance:c.maintenance,worktree:k(d)}}),n.info(`completed context compact job=${o}`);return}if(h==="fork"){const c=String(i.control?.sourceAcpSessionId||"").trim();if(!c)throw new Error("Source session does not have a Codex ACP context to fork");await m.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:i.control?.sourceSessionId?`源会话 ${i.control.sourceSessionId}`:void 0}).catch(()=>{});const b=await V({agentType:v(I.agentType,"codex"),cliType:v(I.cliType,"builtin"),sourceProviderSessionId:c,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,timeoutMs:i.timeoutMs,signal:a.signal,onStatus:S=>{m.report({phase:S.phase,label:S.label,detail:S.detail,updateType:S.updateType,raw:S})}});await m.flush(),await y("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:b.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:i.control?.sourceSessionId||null},acpSessionId:b.acpSessionId,worktree:k(d)}}),n.info(`completed Codex session fork job=${o}`);return}const T=await Ae({cwd:d.cwd,sessionId:r,jobId:o,attachments:i.attachments??[]});T.length>0&&(n.info(`materialized ${T.length} attachment(s) for job=${o}`),await m.sendNow({phase:"initializing",label:"附件已写入本机",detail:T.map(c=>c.relativePath).join(", "),raw:{attachments:z(T)}}).catch(()=>{}));const $=ge(i,T),F=Se($,T),w=await q({agentType:v(I.agentType,"codex"),cliType:v(I.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:me(e.session_raw),prompt:$,promptBlocks:F,cwd:d.cwd,model:i.model,mode:i.mode,configOptionValues:i.configOptionValues,canExecuteTools:i.canExecuteTools,timeoutMs:i.timeoutMs,contextWindow:he(I.contextWindow),signal:a.signal,onStatus:c=>{m.report({phase:c.phase,label:c.label,detail:c.detail,updateType:c.updateType,raw:c})},onUpdate:c=>{A+=1;for(const S of X(c.raw))_.add(S);const b={turnId:e.user_turn_id??null,eventType:c.type,label:c.label,text:c.text,status:c.status,toolCallId:c.toolCallId,raw:Te(c.raw,A)};Y(b)&&f.push(b)},onPermissionRequest:c=>{const b=Z(i,c);return b?Promise.resolve(b):xe(o,t.machineId,c,a.signal)},onUserInputRequest:c=>Ee(o,t.machineId,c,a.signal)});await f.flush(),await m.flush();const J=N({cwd:d.cwd,evidencePaths:_}),R=we(w.promptResponse);if(!(be(R)||!!w.content)){await P(o,t.machineId,`${w.agentType} stopped: ${R??"unknown"}`,{acpSessionId:w.acpSessionId,stopReason:R,exitCode:w.exitCode,stderr:$e(w.stderr,4e3),worktree:k(d)});return}await y("POST",`/api/client/jobs/${encodeURIComponent(o)}/complete`,{machineId:t.machineId,acpSessionId:w.acpSessionId,assistantText:w.content,result:{ok:!0,acpSessionId:w.acpSessionId,stopReason:R,durationMs:w.durationMs,acpRun:i.acpRun??null,effectiveConfig:w.effectiveConfig??null,performance:w.performance??null,usage:w.contextUsage??null,updateCount:w.updates.length,statusTimeline:w.statusTimeline,worktree:k(d),attachments:z(T),files:J}}),n.info(`completed job=${o}`)}catch(d){await f.flush(),await m.flush();const I=a.signal.aborted?"cancelled":d instanceof Error?d.message:String(d),T=C?N({cwd:C.cwd,evidencePaths:_}):[];await P(o,t.machineId,ee(I),{rawError:I,files:T})}finally{await f.flush(),await m.flush(),clearInterval(p)}}function me(e){if(!e||typeof e!="object"||Array.isArray(e))return!1;const t=e.importedProviderSession;return!!(t&&typeof t=="object"&&!Array.isArray(t)&&t.strictResume===!0)}function fe(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function he(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function v(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function we(e){if(!e||typeof e!="object"||Array.isArray(e))return;const t=e;if(typeof t.stopReason=="string")return t.stopReason;if(typeof t.reason=="string")return t.reason;const n=t.part;if(n&&typeof n=="object"&&!Array.isArray(n)&&typeof n.reason=="string")return n.reason}function be(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function ye(e,t){let n=[],a=null,o=Promise.resolve();const r=async i=>{i.length!==0&&await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/events/batch`,{machineId:t,events:i})},l=()=>{a&&(clearTimeout(a),a=null);const i=n;return n=[],o=o.then(()=>r(i)).catch(()=>{}),o},u=()=>{a||(a=setTimeout(()=>{l()},pe()),a.unref())};return{push(i){if(Q(n,i),Ie(i)||n.length>=ae){l();return}u()},flush:l}}function Ie(e){return e.eventType==="tool_call"?!0:e.eventType!=="tool_call_update"?!1:["completed","failed","cancelled"].includes(String(e.status||"").toLowerCase())}function _e(e,t){let n=null,a=0,o="",r=null,l=Promise.resolve();const u=s=>JSON.stringify({phase:s.phase??"",label:s.label??"",detail:s.detail??"",updateType:s.updateType??""}),i=async s=>{await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:s.phase,label:s.label,detail:s.detail,updateType:s.updateType,raw:s.raw}),a=Date.now(),o=u(s)},h=()=>{if(r&&(clearTimeout(r),r=null),!n)return l;const s=n;return n=null,l=l.then(()=>i(s)).catch(()=>{}),l},g=s=>{r||(r=setTimeout(()=>{h()},s),r.unref())};return{report(s){const p=Date.now(),f=u(s)!==o,m=p-a>=ie;if(!f&&!m)return;n=s;const _=p-a;if(_>=O||a===0){h();return}g(O-_)},sendNow(s){return n=s,h()},flush:h}}function Te(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}function ge(e,t=[]){const n=e?.message?.trim()??"";if(t.length===0)return n;const o=["用户随消息上传了以下附件,aca 已将附件写入本机工作区。需要读取附件时请直接使用下面的 localPath;图片附件也已作为 ACP image block 随本轮请求传入。",...t.map((r,l)=>`${l+1}. ${r.name} (${r.kind}, ${r.mimeType}, ${Pe(r.size)})
|
|
2
2
|
localPath: ${r.path}
|
|
3
3
|
workspacePath: ${r.relativePath}`)].join(`
|
|
4
4
|
`);return n?`${n}
|
|
5
5
|
|
|
6
|
-
${o}`:o}function
|
|
6
|
+
${o}`:o}function Se(e,t){const n=[{type:"text",text:e}];for(const a of t)a.kind!=="image"||!a.mimeType.startsWith("image/")||n.push({type:"image",data:a.base64,mimeType:a.mimeType,uri:a.path});return n}async function Ae(e){if(e.attachments.length===0)return[];const t=x.join(e.cwd,".aca","attachments",B(e.sessionId),B(e.jobId));E.mkdirSync(t,{recursive:!0});const n=[];for(const[a,o]of e.attachments.entries()){const r=o.attachmentId?{mimeType:o.mimeType||"application/octet-stream",bytes:await D(`/api/client/attachments/${encodeURIComponent(o.attachmentId)}`)}:ve(o.dataUrl??""),l=o.mimeType||r.mimeType||"application/octet-stream",u=o.kind==="image"||l.startsWith("image/")?"image":"file",i=Ce(o.name||`attachment-${a+1}`,l),h=x.join(t,`${String(a+1).padStart(2,"0")}-${i}`);E.writeFileSync(h,r.bytes),n.push({name:o.name||i,mimeType:l,size:r.bytes.byteLength,kind:u,path:h,relativePath:x.relative(e.cwd,h),base64:r.bytes.toString("base64"),sha256:L.createHash("sha256").update(r.bytes).digest("hex")})}return n}function ve(e){const t=e.indexOf(",");if(!e.startsWith("data:")||t<0)throw new Error("attachment dataUrl is not a data URL");const n=e.slice(5,t),a=e.slice(t+1),o=n.split(";").filter(Boolean),r=o[0]?.includes("/")?o[0]:"application/octet-stream";if(!o.includes("base64"))throw new Error("attachment dataUrl is not base64 encoded");return{mimeType:r,bytes:Buffer.from(a,"base64")}}function Ce(e,t){const n=x.basename(e).replace(/[<>:"/\\|?*\x00-\x1F]/g,"_").trim(),a=`attachment${ke(t)}`;return(!n||n==="."||n===".."?a:n).slice(0,180)}function ke(e){switch(e.toLowerCase()){case"image/png":return".png";case"image/jpeg":return".jpg";case"image/webp":return".webp";case"image/gif":return".gif";case"text/plain":return".txt";case"application/json":return".json";case"text/markdown":case"text/x-markdown":return".md";case"application/pdf":return".pdf";case"application/msword":return".doc";case"application/vnd.openxmlformats-officedocument.wordprocessingml.document":return".docx";case"application/vnd.ms-excel":return".xls";case"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":return".xlsx";case"application/vnd.ms-powerpoint":return".ppt";case"application/vnd.openxmlformats-officedocument.presentationml.presentation":return".pptx";case"application/zip":return".zip";case"application/x-7z-compressed":return".7z";case"application/x-rar-compressed":case"application/vnd.rar":return".rar";case"application/gzip":return".gz";case"application/x-tar":return".tar";default:return""}}function B(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)||"item"}function z(e){return e.map(t=>({name:t.name,kind:t.kind,mimeType:t.mimeType,size:t.size,path:t.path,relativePath:t.relativePath,sha256:t.sha256}))}function Pe(e){if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KB","MB","GB"];let n=e,a=0;for(;n>=1024&&a<t.length-1;)n/=1024,a+=1;return`${n>=10||a===0?n.toFixed(0):n.toFixed(1)} ${t[a]}`}async function Re(e,t,n){!n.usedWorktree||!n.worktreeId||await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??k(n)})}function k(e){return{usedWorktree:e.usedWorktree,worktreeId:e.worktreeId??null,cwd:e.cwd,status:e.status??null,baseCommit:e.baseCommit??null,branchName:e.branchName??null,baseBranch:e.baseBranch??null}}async function xe(e,t,n,a){const r=(await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:n})).item.permission_id;for(;!a.aborted;){await j(M);const l=await y("GET",`/api/client/permissions/${encodeURIComponent(r)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="selected"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function Ee(e,t,n,a){const r=(await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/permissions`,{machineId:t,request:{kind:"elicitation",...n}})).item.permission_id;for(;!a.aborted;){await j(M);const l=await y("GET",`/api/client/permissions/${encodeURIComponent(r)}`);if(l.item.status==="resolved"&&l.item.response){const u=l.item.response.outcome;return u.outcome==="submitted"?{outcome:u}:{outcome:{outcome:"cancelled"}}}}return{outcome:{outcome:"cancelled"}}}async function je(e,t){if(t.signal.aborted)return;const n=await y("GET",`/api/client/jobs/${encodeURIComponent(e)}`);Number(n.item.cancel_requested??0)===1&&t.abort()}async function P(e,t,n,a){await y("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:a})}function j(e){return new Promise(t=>setTimeout(t,e))}function $e(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}export{Ge as startJobWorkerLoop};
|
package/dist/worker-cli.js
CHANGED
|
@@ -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.
|
|
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.19").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};
|