@yuandc/aica 0.1.89 → 0.1.91
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/json-rpc.js +1 -1
- package/dist/acp/providers/workbuddy/commands.js +1 -0
- package/dist/acp/providers/workbuddy/config.js +1 -1
- package/dist/acp/providers/workbuddy/errors.js +1 -0
- package/dist/acp/providers/workbuddy/fork.js +1 -0
- package/dist/acp/providers/workbuddy/provider.js +1 -1
- package/dist/acp/providers/workbuddy/session-lock.js +1 -0
- package/dist/acp/providers/workbuddy/supplements.js +1 -1
- package/dist/acp/providers/workbuddy/usage.js +1 -0
- package/dist/core/job-execution-config.js +1 -0
- package/dist/core/job-worker.js +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import{appendBoundedText as u}from"./bounded-buffer.js";class s{child;handlers;static MAX_STDOUT_BYTES=1024*1024;static MAX_STDERR_BYTES=1024*1024;nextId=1;lineBuffer="";pending=new Map;peerName;includeJsonRpc;stdoutBuffer="";stderrBuffer="";notificationCount=0;exitCode=null;signal=null;constructor(e,t={}){this.child=e,this.handlers=t,this.peerName=t.peerName||"JSON-RPC peer",this.includeJsonRpc=t.includeJsonRpc===!0,e.stdout.on("data",r=>this.handleStdout(String(r))),e.stderr.on("data",r=>{this.stderr=this.stderr+String(r)}),e.on("close",(r,i)=>{this.exitCode=r,this.signal=i;for(const[n,o]of this.pending)clearTimeout(o.timer),o.reject(new Error(`${this.peerName} exited before ${o.method} completed (code=${r}, signal=${i})`)),this.pending.delete(n)}),e.on("error",r=>{for(const[i,n]of this.pending)clearTimeout(n.timer),n.reject(r),this.pending.delete(i)})}get stdout(){return this.stdoutBuffer}get stderr(){return this.stderrBuffer}set stderr(e){this.stderrBuffer=u("",e,s.MAX_STDERR_BYTES)}request(e,t,r=12e4){const i=this.nextId++;return new Promise((n,o)=>{const d=this.createRequestTimeout(i,e,r,o);d.unref(),this.pending.set(i,{method:e,resolve:n,reject:o,timer:d,timeoutMs:r}),this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},id:i,method:e,...t===void 0?{}:{params:t}})})}notify(e,t){this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},method:e,...t===void 0?{}:{params:t}})}refreshPendingRequestTimeout(e){for(const[t,r]of this.pending)r.method===e&&(clearTimeout(r.timer),r.timer=this.createRequestTimeout(t,r.method,r.timeoutMs,r.reject),r.timer.unref())}respond(e,t){this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},id:e,result:t})}respondError(e,t){this.write({...this.includeJsonRpc?{jsonrpc:"2.0"}:{},id:e,error:{code:-32603,message:t.message}})}createRequestTimeout(e,t,r,i){return setTimeout(()=>{this.pending.delete(e),i(new Error(`${this.peerName} request timed out while waiting for ${t}`))},r)}handleStdout(e){this.stdoutBuffer=u(this.stdoutBuffer,e,s.MAX_STDOUT_BYTES),this.lineBuffer+=e;const t=this.lineBuffer.split(`
|
|
2
2
|
`);this.lineBuffer=t.pop()??"";for(const r of t){const i=r.trim();if(!i)continue;let n;try{n=JSON.parse(i)}catch{this.stderr+=`
|
|
3
|
-
[non-json stdout] ${i}`;continue}this.handleMessage(n)}}async handleMessage(e){if("id"in e&&("result"in e||"error"in e)&&typeof e.method!="string"){const t=Number(e.id),r=this.pending.get(t);if(!r)return;if(this.pending.delete(t),clearTimeout(r.timer),"error"in e){const i=e.error;r.reject(Object.assign(new Error(i?.message||`${this.peerName} error ${i?.code??"unknown"}`),{rpcResponse:!0,rpcCode:i?.code}))}else r.resolve(e.result);return}if(typeof e.method=="string"){if("id"in e){try{const t=await this.handlers.onRequest?.(e.method,e.params,e);this.respond(e.id,t??null)}catch(t){this.respondError(e.id,t instanceof Error?t:new Error(String(t)))}return}this.notificationCount+=1,await this.handlers.onNotification?.(e.method,e.params,e)}}write(e){this.child.stdin.write(`${JSON.stringify(e)}
|
|
3
|
+
[non-json stdout] ${i}`;continue}this.handleMessage(n)}}async handleMessage(e){if("id"in e&&("result"in e||"error"in e)&&typeof e.method!="string"){const t=Number(e.id),r=this.pending.get(t);if(!r)return;if(this.pending.delete(t),clearTimeout(r.timer),"error"in e){const i=e.error;r.reject(Object.assign(new Error(i?.message||`${this.peerName} error ${i?.code??"unknown"}`),{rpcResponse:!0,rpcCode:i?.code,rpcData:i?.data}))}else r.resolve(e.result);return}if(typeof e.method=="string"){if("id"in e){try{const t=await this.handlers.onRequest?.(e.method,e.params,e);this.respond(e.id,t??null)}catch(t){this.respondError(e.id,t instanceof Error?t:new Error(String(t)))}return}this.notificationCount+=1,await this.handlers.onNotification?.(e.method,e.params,e)}}write(e){this.child.stdin.write(`${JSON.stringify(e)}
|
|
4
4
|
`)}}export{s as JsonLineRpcClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{record as n}from"./config.js";function a(o){if(!Array.isArray(o))return[];const i=new Set,c=[];for(const p of o.slice(0,500)){const e=n(p),t=String(e.name||"").replace(/^\/+/,"");if(!/^[\p{L}\p{N}_][\p{L}\p{N}_.:/-]{0,127}$/u.test(t)||i.has(t.toLowerCase()))continue;i.add(t.toLowerCase());const r=n(e.input).hint,s=n(e._meta);c.push({name:t,description:String(e.description||"").slice(0,500),...typeof r=="string"&&r?{input:{hint:r.slice(0,200)}}:{},...s.type==="skill"?{_meta:{type:"skill",...typeof s.source=="string"?{source:s.source.slice(0,100)}:{}}}:{}})}return c}export{a as workBuddyCommands};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveWorkBuddyLaunch as _}from"./launch.js";function s(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}const m=[{id:"default",name:"询问授权"},{id:"acceptEdits",name:"自动接受文件修改"},{id:"plan",name:"规划"},{id:"bypassPermissions",name:"完全访问"}];let
|
|
1
|
+
import{resolveWorkBuddyLaunch as _}from"./launch.js";function s(e){return e&&typeof e=="object"&&!Array.isArray(e)?e:{}}const m=[{id:"default",name:"询问授权"},{id:"acceptEdits",name:"自动接受文件修改"},{id:"plan",name:"规划"},{id:"bypassPermissions",name:"完全访问"}];let d=null;function O(){d=null}function b(){return(d?.identity===g()&&d.expiresAtMs>Date.now()?d.value:null)??{source:"aca-static",contextUsage:!0,contextCompaction:!0,liveSupplement:!0,sessionResume:!0,sessionFork:!0,sessionForkScope:"same-directory",modelCatalogAuthoritative:!0,models:[{modelId:process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"default",name:"默认模型"}],modes:m,configOptions:[{id:"model",category:"model",type:"select",currentValue:process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"default",options:[{value:process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"default",name:"默认模型"}]},{id:"mode",category:"mode",type:"select",currentValue:"default",options:m.map(r=>({value:r.id,name:r.name}))},{id:"thought_level",category:"thought_level",type:"select",currentValue:"enabled",options:[{value:"enabled",name:"模型默认"}]}]}}function v(e){const r=p(e).filter(o=>["model","mode","thought_level","context_window"].includes(String(o.id)));if(!r.length)return;const a=s(e.models).availableModels,i=r.find(o=>o.category==="model"||o.id==="model");d={identity:g(),expiresAtMs:Date.now()+360*60*1e3,value:{source:"codebuddy-acp",fetchedAt:Date.now(),modelCatalogAuthoritative:!0,contextUsage:!0,contextCompaction:!0,liveSupplement:!0,sessionResume:!0,sessionFork:!0,sessionForkScope:"same-directory",models:Array.isArray(a)?a:Array.isArray(i?.options)?i.options:[],modes:s(e.modes).availableModes||m,configOptions:r}}}function g(){try{return _().runtimeIdentity}catch{return"unavailable"}}function p(e){return Array.isArray(e.configOptions)?e.configOptions.map(s):[]}function D(e){return!e||e==="agent"?"default":e==="read-only"?"plan":["agent-full-access","full-access","danger-full-access"].includes(e)?"bypassPermissions":e}async function w(e,r,a,i){let o=a;const t={...i.configOptionValues},A=i.canExecuteTools===!1?"default":D(i.mode||String(t.mode||"default"));await e.setSessionMode({sessionId:r,modeId:A});const u=i.model||String(t.model||process.env.ACA_WORKBUDDY_DEFAULT_MODEL||"");u?t.model=u==="default"?"default-model":u:delete t.model,delete t.mode;const y=t.thought_level??t.reasoning_effort??t.effort;if(delete t.reasoning_effort,delete t.effort,y!==void 0&&(t.thought_level=y),t.multitask===!0||t.delegate===!0)throw new Error("WorkBuddy 当前不支持后台多任务或委派模式。");p(o).some(n=>n.id==="multitask"&&n.currentValue===!0)&&(t.multitask=!1);for(const[n,l]of Object.entries(t)){const c=p(o).find(f=>f.id===n);if(!c&&n!=="model")throw new Error(`WorkBuddy 不支持配置项:${n}`);if(!(n==="model"&&l==="default-model")&&Array.isArray(c?.options)&&!c.options.some(f=>s(f).value===l))throw new Error(`WorkBuddy 配置值不可用:${n}=${l}`);const h=s(await e.setSessionConfigOption({sessionId:r,configId:n,value:l}));o={...o,...h}}return v(o),o}export{w as applyWorkBuddyConfig,O as clearWorkBuddyCapabilities,p as configOptions,s as record,v as rememberWorkBuddyCapabilities,b as workBuddyHeartbeatCapabilities,D as workBuddyMode,m as workBuddyModes};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{record as u}from"./config.js";class s extends Error{failure;constructor(e,o,t){super(o),this.name="WorkBuddyRunError",this.failure={provider:"workbuddy",category:e,...t!==void 0?{code:t}:{}}}}function a(n){if(typeof n=="string")try{return u(JSON.parse(n))}catch{return{message:n}}return u(n)}function C(n){const e=u(n),o=u(e._meta),t=o["codebuddy.ai/error"]??o["codebuddy.ai/errorMessage"];if(t!=null&&t!=="")throw m(a(t));if(e.stopReason!=="end_turn")throw e.stopReason==="cancelled"?new s("cancelled","腾讯 Code Buddy 任务已取消。"):e.stopReason==="refusal"?new s("refusal","腾讯 Code Buddy 模型拒绝了本次请求,请调整请求内容后重试。"):["max_tokens","max_turn_requests"].includes(String(e.stopReason))?new s("incomplete","腾讯 Code Buddy 已达到本轮输出或请求次数上限,任务尚未完成;请检查已有结果后继续。"):new s("incomplete","腾讯 Code Buddy 未返回有效的完成状态,请检查已有结果后再继续。")}function m(n){if(n instanceof s)return n;const e=u(n),o=a(e.rpcData??e.data),t=a(o["codebuddy.ai/error"]??e["codebuddy.ai/error"]??(typeof e.code=="string"?e:void 0)),i=t.code??e.rpcCode??e.code,r=[t.code,t.message,t.additionalDetails,e.code,e.message,e.additionalDetails,o.category,o.details,o.code,u(o.quotaExceed).subject,u(t.quotaExceed).subject].filter(c=>typeof c=="string"||typeof c=="number").join(" "),f=t.httpStatusCode??e.httpStatusCode??o.statusCode,d=(c,y)=>new s(c,y,typeof i=="string"||typeof i=="number"?i:void 0);if(i==="ENOENT")return d("internal","未找到 CodeBuddy CLI,请安装 @tencent-ai/codebuddy-code,或设置 ACA_WORKBUDDY_COMMAND。");if(/session not found|resource not found/i.test(r))return d("internal","腾讯 Code Buddy 历史会话不存在或已失效,无法恢复;请新建会话。");if(/invalid mode|config rejected|unknown configuration/i.test(r))return d("internal","腾讯 Code Buddy 配置被拒绝,请重新选择可用模型、思考深度或权限配置。");if(/provider_authentication_failed|custom_model_authentication_failed/.test(r))return d("model","腾讯 Code Buddy 的模型凭据无效,请检查模型接口的认证配置。");if(i===-32e3||/authentication.required|not.authenticated|unauthorized|尚未认证|authentication_required/.test(r.toLowerCase()))return d("auth","腾讯 Code Buddy 尚未认证:请到设置 → 我的设备 → 腾讯 Code Buddy 登录;也可在 Worker 的系统账号下执行 codebuddy 登录,或配置 CODEBUDDY_API_KEY。");const l=u(t.quotaExceed??o.quotaExceed).subject;return/(insufficient|not enough).{0,24}(credit|balance)|credit.{0,24}(exhaust|insufficient|not enough)|积分不足|余额不足|credit_balance/i.test(r)||(i===-32003||i==="usage_limit_exceeded")&&/^(credit|credits|balance|积分)$/i.test(String(l||""))?d("credits","腾讯 Code Buddy 积分不足,请查看积分详情并补充积分或切换免费模型后重试。"):/server_overloaded|overload|at capacity|model.{0,20}busy|排队|模型繁忙/i.test(r)?d("model","腾讯 Code Buddy 模型服务繁忙,请稍后重试或切换模型。"):/rate.?limit|too many requests|请求过于频繁/i.test(r)?d("rate_limit","腾讯 Code Buddy 请求过于频繁,请稍后重试。"):i===-32003||f===429||/usage_limit_exceeded|quota|额度|用量上限/i.test(r)?d("quota","腾讯 Code Buddy 可用额度或用量配额已耗尽,请查看积分详情与账号配额。"):/context_window_exceeded|maximum context|context.{0,24}(length|window)|上下文.*上限/i.test(r)?d("context","腾讯 Code Buddy 上下文已超过模型限制,请压缩上下文或新建会话后重试。"):o.category==="cancelled"||/\bcancelled\b|已取消|\baborted\b/i.test(r)?d("cancelled","腾讯 Code Buddy 任务已取消。"):i===-32001||[408,502,503,504].includes(Number(f))||/network|connection|econn|enet|etimedout|fetch failed|timed? ?out|socket|disconnected/i.test(r)?d("network","腾讯 Code Buddy 网络连接异常或响应超时,请检查 Worker 网络后重试;已有执行结果可能仍然有效。"):i===-32004||/model_service|empty_model_response|empty stream|invalid response|malformed tool|custom_model_config/i.test(r)?d("model","腾讯 Code Buddy 模型服务返回异常,请检查模型配置或切换模型后重试。"):/^(WorkBuddy|当前 CodeBuddy|腾讯 Code Buddy|未找到 CodeBuddy)/.test(String(e.message||""))&&e.rpcResponse!==!0?d("internal",String(e.message)):d("internal","腾讯 Code Buddy 执行失败,请检查已有结果、Worker 状态及模型配置后重试。")}export{s as WorkBuddyRunError,C as assertWorkBuddyPromptCompleted,m as workBuddyError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import A from"node:fs";import{createHash as R,randomUUID as W}from"node:crypto";import{AcpClient as M}from"../../client/acp-client.js";import{closeAcpProcess as P,startAcpProcess as U}from"../../client/acp-process.js";import{defaultAcpRuntimePool as x}from"../../client/acp-runtime-pool.js";import{runAbortableRequest as z}from"../../client/abortable-request.js";import{resolveWorkBuddyLaunch as E}from"./launch.js";import{applyWorkBuddyConfig as q,record as r}from"./config.js";import{workBuddyCommands as Z}from"./commands.js";import{assertWorkBuddyPromptCompleted as $,WorkBuddyRunError as g,workBuddyError as H}from"./errors.js";import{enterWorkBuddySession as J}from"./session-lock.js";function v(e=1/0){const o=R("sha256");let l=0;return{observe(a){const d=r(r(a).update);l>=e||!["user_message_chunk","agent_message_chunk"].includes(String(d.sessionUpdate))||(o.update(JSON.stringify([d.sessionUpdate,d.content])),l++)},get count(){return l},finish:()=>o.digest("hex")}}async function Y(e){if(!A.statSync(e.cwd).isDirectory())throw new Error("CodeBuddy 项目目录不可用。");const o=e.sourceProviderSessionId.trim();if(!o)throw new Error("CodeBuddy 分叉缺少源会话 ID。");const l=J(o);let a;const d=async()=>{a&&await P(a.child)},D=Date.now()+(e.timeoutMs??12e4),c=t=>new g("internal",t),n=()=>{if(e.signal?.aborted)throw new g("cancelled","CodeBuddy 会话分叉已取消。");const t=D-Date.now();if(t<=0)throw new g("network","CodeBuddy 会话分叉响应超时。");return t};try{return await z({signal:e.signal,cancelledMessage:"CodeBuddy 会话分叉已取消。",onAbort:d,startRequest:async()=>{const t=E(!1);n(),await x.retireSessionRuntimes({providerId:"workbuddy",cwd:e.cwd,providerSessionId:o,keepRuntimeIdentity:`fork:${W()}`});const k=async s=>{n(),a=U(t.command,t.args,e.cwd,t.env);const m=new M(a.child,s),u=r(await m.initialize(n()));if(u.protocolVersion!==1||r(u.agentCapabilities).loadSession!==!0)throw c("当前 CodeBuddy 版本不支持恢复分叉会话。");return m},f=v();let B=[],i=null,y=!1;const C=await k(s=>{const m=r(s),u=r(m.update),S=r(u._meta);if(m.sessionId===o&&(y||f.observe(s),u.sessionUpdate==="available_commands_update"&&(B=Z(u.availableCommands)),y&&S["codebuddy.ai/sessionReset"]===!0)){const w=S["codebuddy.ai/newSessionId"];typeof w=="string"&&/^[a-zA-Z0-9][a-zA-Z0-9_:-]{0,255}$/.test(w)&&w!==o&&(i=w)}});e.onStatus?.({phase:"forking",label:"正在恢复 CodeBuddy 源会话",atMs:Date.now()});const I=r(await C.loadSession({sessionId:o,cwd:e.cwd},n()));if(I.sessionId&&I.sessionId!==o)throw c("CodeBuddy 恢复返回了不同的源会话 ID。");if(!f.count)throw c("CodeBuddy 源会话没有可恢复的历史,请确认仍在原工作目录中。");if(!B.some(s=>s.name==="fork"&&s._meta?.type!=="skill"))throw c("当前 CodeBuddy 未公布原生 /fork 命令,请更新 CodeBuddy 后重试。");const _=f.finish();if(y=!0,e.onStatus?.({phase:"forking",label:"正在分叉 CodeBuddy 会话",atMs:Date.now()}),$(await C.prompt({sessionId:o,prompt:[{type:"text",text:"/fork"}]},n())),!i)throw c("CodeBuddy 未确认生成独立分叉会话,无法完成分叉。");await d();const p=v(f.count),b=await k(s=>{r(s).sessionId===i&&p.observe(s)});e.onStatus?.({phase:"forking",label:"正在验证 CodeBuddy 分叉历史",atMs:Date.now()});const h=r(await b.loadSession({sessionId:i,cwd:e.cwd},n()));if(h.sessionId&&h.sessionId!==i||p.count!==f.count||p.finish()!==_)throw c("CodeBuddy 分叉历史校验失败,未将此会话标记为成功。");return await q(b,i,h,{...e,prompt:""}),n(),{providerSessionId:i}}})}catch(t){throw H(t)}finally{try{await d()}finally{l.release()}}}export{Y as forkWorkBuddySession};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{workBuddyCredits as B}from"../../../core/workbuddy-credits/worker.js";import c from"node:fs";import{enterWorkBuddyTask as A}from"./auth-gate.js";import{runAbortableRequest as b}from"../../client/abortable-request.js";import{defaultAcpRuntimePool as w}from"../../client/acp-runtime-pool.js";import{BoundedItemBuffer as f}from"../../client/bounded-buffer.js";import{contextUsageFromAcpSessionUpdate as E}from"../../client/context-usage.js";import{providerEventFromAcpSessionUpdate as C,providerStatusFromAcpSessionUpdate as W}from"../standard-acp-events.js";import{resolveWorkBuddyLaunch as M,workBuddyTimeout as R}from"./launch.js";import{applyWorkBuddyConfig as D,record as o,rememberWorkBuddyCapabilities as y}from"./config.js";import{handleWorkBuddyRequest as q}from"./interactions.js";import{WorkBuddyEventRouter as v}from"./events.js";import{WorkBuddySupplements as U,workBuddyPromptContent as _}from"./supplements.js";const l=new WeakMap,p=new Set,O={sessionResume:!0,liveSupplement:!0,imageInput:!0,fileAttachment:!0,filesystem:!0,terminal:!0,permissionRequest:!0,configOptions:!0,usage:!0,contextUsage:!0,contextCompaction:!0,plan:!0,diff:!0};class J{id="workbuddy";name="腾讯 Code Buddy";capabilities=O;async createSession(s){return new x(s.cwd,s.providerSessionId??null,s.timeoutMs)}}class x{cwd;providerSessionId;timeoutMs;lease=null;input=null;acceptingOutput=!1;busy=!1;content="";usage=null;response={};commands=new Set;router=new v;supplements=null;updates=new f(16);statuses=new f(64);firstEventAtMs=null;lastEventAtMs=null;constructor(s,e,t=R()){this.cwd=s,this.providerSessionId=e,this.timeoutMs=t}async sendPrompt(s){return this.run(s,!1)}async compactContext(s){if(!this.providerSessionId)throw new Error("WorkBuddy 当前没有可压缩的会话。");const e=await this.run({...s,cwd:s.cwd,prompt:"/compact",requireSessionResume:!0},!0);return{providerSessionId:e.providerSessionId,contextUsage:e.contextUsage??null,maintenance:{action:"compact",provider:"workbuddy",protocol:"session/prompt",stopReason:o(e.promptResponse).stopReason},durationMs:e.durationMs}}async run(s,e){if(this.busy)throw new Error("WorkBuddy 会话正在执行另一轮任务。");if(c.realpathSync(s.cwd)!==c.realpathSync(this.cwd))throw new Error("WorkBuddy 会话不能切换工作目录。");if(s.requireSessionResume&&!this.providerSessionId)throw new Error("WorkBuddy 历史会话缺少 Session ID,禁止新建会话。");let t=this.providerSessionId?`${c.realpathSync(this.cwd)}\0${this.providerSessionId}`:null;if(t&&p.has(t))throw new Error("WorkBuddy 会话正在执行另一轮任务。");const a=A();t&&p.add(t),this.busy=!0,this.input=s,this.content="",this.usage=null,this.response={},this.commands.clear(),this.router=new v,this.updates.clear(),this.statuses.clear(),this.firstEventAtMs=this.lastEventAtMs=null;const n=Date.now();let d;try{return await b({signal:s.signal,cancelledMessage:"WorkBuddy 任务已取消",onAbort:()=>this.cancelAndDispose(),startRequest:async()=>{this.status("initializing","WorkBuddy ACP 启动中");const r=M(s.canExecuteTools!==!1);if(this.providerSessionId&&await w.retireSessionRuntimes({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,keepRuntimeIdentity:r.runtimeIdentity}),s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");const i=await w.acquire({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,...r,onSessionUpdate:u=>this.handleUpdate(u),onClientRequest:(u,I,k)=>this.handleRequest(u,I,k.id)});if(this.lease=i,s.signal?.aborted)throw await i.dispose(),new Error("WorkBuddy 任务已取消");if(await this.initialize(i),await this.establishSession(i),!t){if(t=`${c.realpathSync(this.cwd)}\0${this.providerSessionId}`,p.has(t))throw t=null,new Error("WorkBuddy 会话正在执行另一轮任务。");p.add(t)}const g=o(o(l.get(i.client)?.agentCapabilities).promptCapabilities);if(s.promptBlocks?.some(u=>u.type==="image")&&g.image!==!0)throw new Error("当前 CodeBuddy ACP 版本未声明图片输入支持。");if(e||(this.response=await D(i.client,this.providerSessionId,this.response,s)),i.setSessionResponse(this.response),e&&!this.commands.has("compact"))throw new Error("当前 WorkBuddy 会话未提供 /compact 命令,无法压缩上下文。");this.acceptingOutput=!0,this.status("thinking",e?"正在压缩上下文":"思考中"),d=B.beginUsage();const m=await i.client.prompt({sessionId:this.providerSessionId,prompt:_(s.prompt,s.promptBlocks)},s.timeoutMs??this.timeoutMs);if(s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");const h=o(m).stopReason;if(h==="cancelled")throw new Error("WorkBuddy 任务已取消");if(e&&h!=="end_turn")throw new Error(`WorkBuddy 压缩未完成:${String(h||"未知状态")}`);return e&&this.usage&&(this.usage={...this.usage,state:"compacted",maintenanceAction:"compact"}),this.status("completed",e?"上下文压缩完成":"已完成"),{providerSessionId:this.providerSessionId,cliType:"builtin",agentType:"workbuddy",effectiveConfig:{model:s.model||null,mode:s.mode||"default",configOptions:this.response.configOptions,provider:"workbuddy",protocol:"acp",command:i.started.command,processPid:i.started.child.pid},command:i.started.command,args:i.started.args,cwd:this.cwd,exitCode:i.started.child.exitCode,signal:i.started.child.signalCode,durationMs:Date.now()-n,stdout:i.client.rpc.stdout,stderr:i.client.rpc.stderr,content:this.content.trim(),updateCount:this.updates.count(),updates:this.updates.snapshot(),statusTimeline:this.statuses.snapshot(),contextUsage:this.usage,promptResponse:m,performance:{startedAtMs:n,firstEventAtMs:this.firstEventAtMs,lastEventAtMs:this.lastEventAtMs,eventCount:this.updates.count(),firstEventLatencyMs:this.firstEventAtMs===null?null:this.firstEventAtMs-n,silentForMs:Date.now()-(this.lastEventAtMs??n),durationMs:Date.now()-n}}}})}catch(r){await this.lease?.dispose();const i=r instanceof Error?r.message:String(r);throw this.status("failed","WorkBuddy 执行失败",i),/authentication required|not authenticated|unauthorized/i.test(i)?new Error("腾讯 Code Buddy 尚未认证:请到设置 → 我的设备 → 腾讯 Code Buddy 登录;也可在运行 ACA Worker 的系统账号下执行 codebuddy 登录,或配置 CODEBUDDY_API_KEY。"):o(r).code==="ENOENT"?new Error("未找到 CodeBuddy CLI,请安装 @tencent-ai/codebuddy-code,或设置 ACA_WORKBUDDY_COMMAND。"):r}finally{d?.(),this.acceptingOutput=!1,s.onSupplementReady?.(null);try{await this.supplements?.close()}finally{a()}this.supplements=null,this.lease?.release(),this.lease=null,this.input=null,this.busy=!1,t&&p.delete(t)}}async initialize(s){if(s.initialized)return;const e=o(await s.client.rpc.request("initialize",{protocolVersion:1,clientInfo:{name:"aca",version:"0.1.0"},clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1,elicitation:{form:{}},_meta:{"codebuddy.ai":{question:!0}}}},6e4));if(e.protocolVersion!==1)throw new Error("WorkBuddy ACP 协议版本不兼容。");l.set(s.client,e),s.markInitialized()}async establishSession(s){if(s.sessionEstablished&&s.providerSessionId){this.providerSessionId=s.providerSessionId,this.response=s.sessionResponse??{};const e=this.response.acaAvailableCommands;Array.isArray(e)&&e.forEach(t=>this.commands.add(String(t)));return}if(this.providerSessionId){if(o(l.get(s.client)?.agentCapabilities).loadSession!==!0)throw new Error("当前 WorkBuddy 版本不支持恢复会话,禁止静默新建。");if(this.status("resuming","WorkBuddy 会话恢复中"),this.response=o(await s.client.loadSession({sessionId:this.providerSessionId,cwd:this.cwd},12e4)),this.response.sessionId&&this.response.sessionId!==this.providerSessionId)throw new Error("WorkBuddy 恢复返回了不同的会话 ID。")}else{if(this.status("acp","WorkBuddy 会话创建中"),this.response=o(await s.client.newSession({cwd:this.cwd},12e4)),typeof this.response.sessionId!="string"||!this.response.sessionId.trim())throw new Error("WorkBuddy session/new 未返回会话 ID。");this.providerSessionId=this.response.sessionId}this.response.acaAvailableCommands=[...this.commands],y(this.response),s.setProviderSession({providerSessionId:this.providerSessionId,sessionResponse:this.response})}handleUpdate(s){const e=o(s),t=o(e.update);if(this.providerSessionId&&e.sessionId!==this.providerSessionId||(t.sessionUpdate==="available_commands_update"&&Array.isArray(t.availableCommands)&&(this.commands=new Set(t.availableCommands.map(r=>String(o(r).name||"").replace(/^\//,""))),this.response.acaAvailableCommands=[...this.commands],this.lease?.setSessionResponse(this.response)),t.sessionUpdate==="config_option_update"&&Array.isArray(t.configOptions)&&(this.response={...this.response,configOptions:t.configOptions},this.lease?.setSessionResponse(this.response),y(this.response)),!this.acceptingOutput||!this.input))return;const a=this.router.route(s);if(this.updates.push({method:"session/update",params:a.params}),this.firstEventAtMs??=Date.now(),this.lastEventAtMs=Date.now(),!a.child){this.usage=E(s,this.input.contextWindow)??this.usage;const r=o(t._meta)["codebuddy.ai/requestId"]||o(e._meta)["codebuddy.ai/requestId"];!this.supplements&&typeof r=="string"&&r&&this.lease&&this.providerSessionId&&(this.supplements=new U(this.lease.client,this.providerSessionId,r),this.input.onSupplementReady?.(this.supplements.channel))}const n=C("workbuddy",a.params);n&&(!a.child&&n.type==="agent_message_chunk"&&(this.content+=n.text??""),this.input.onUpdate?.(n));const d=W("workbuddy",a.params);d&&this.status(d.phase,d.label,d.detail,d.updateType)}async handleRequest(s,e,t){if(!this.input||!this.acceptingOutput)throw new Error("WorkBuddy 当前没有可交互的执行轮次。");const a=o(e).sessionId;if((s!=="elicitation/create"||a!==void 0)&&a!==this.providerSessionId)throw new Error("WorkBuddy 交互请求会话不匹配。");this.status("requestPermission",s==="session/request_permission"?"等待授权":"等待回答",void 0,s);try{return await q(s,e,t,this.input)}finally{this.acceptingOutput&&this.status("thinking","处理中")}}status(s,e,t,a){const n={phase:s,label:e,detail:t,updateType:a,atMs:Date.now()};this.statuses.push(n),this.input?.onStatus?.(n)}async cancel(){await this.cancelAndDispose()}async cancelAndDispose(){this.acceptingOutput=!1,this.input?.onSupplementReady?.(null),this.providerSessionId&&this.lease?.client.cancel(this.providerSessionId),await this.lease?.dispose()}async close(){this.busy?await this.cancelAndDispose():this.lease?.release()}}export{J as WorkBuddyProvider,O as workBuddyCapabilities};
|
|
1
|
+
import{workBuddyCredits as B}from"../../../core/workbuddy-credits/worker.js";import h from"node:fs";import{enterWorkBuddySession as A}from"./session-lock.js";import{forkWorkBuddySession as E}from"./fork.js";import{runAbortableRequest as W}from"../../client/abortable-request.js";import{defaultAcpRuntimePool as m}from"../../client/acp-runtime-pool.js";import{BoundedItemBuffer as f}from"../../client/bounded-buffer.js";import{contextUsageFromAcpSessionUpdate as M}from"../../client/context-usage.js";import{providerEventFromAcpSessionUpdate as w,providerStatusFromAcpSessionUpdate as R}from"../standard-acp-events.js";import{resolveWorkBuddyLaunch as U,workBuddyTimeout as x}from"./launch.js";import{applyWorkBuddyConfig as q,record as n,rememberWorkBuddyCapabilities as y}from"./config.js";import{handleWorkBuddyRequest as D}from"./interactions.js";import{WorkBuddyEventRouter as g}from"./events.js";import{WorkBuddySupplementIdentity as v,WorkBuddySupplements as _,workBuddyPromptContent as O}from"./supplements.js";import{assertWorkBuddyPromptCompleted as P,workBuddyError as S}from"./errors.js";import{WorkBuddyTurnCredits as I,workBuddyContextUsage as T}from"./usage.js";import{workBuddyCommands as k}from"./commands.js";const c=new WeakMap,z={sessionResume:!0,liveSupplement:!0,imageInput:!0,fileAttachment:!0,filesystem:!0,terminal:!0,permissionRequest:!0,configOptions:!0,usage:!0,contextUsage:!0,contextCompaction:!0,plan:!0,diff:!0};class os{id="workbuddy";name="腾讯 Code Buddy";capabilities=z;async createSession(s){return new F(s.cwd,s.providerSessionId??null,s.timeoutMs)}async forkSession(s){return E(s)}}class F{cwd;providerSessionId;timeoutMs;lease=null;input=null;acceptingOutput=!1;busy=!1;content="";usage=null;response={};commands=new Set;commandCatalog=null;credits=new I;technicalError=null;router=new g;supplements=null;supplementIdentity=new v;updates=new f(16);statuses=new f(64);firstEventAtMs=null;lastEventAtMs=null;constructor(s,t,o=x()){this.cwd=s,this.providerSessionId=t,this.timeoutMs=o}async sendPrompt(s){return this.run(s,!1)}async compactContext(s){if(!this.providerSessionId)throw new Error("WorkBuddy 当前没有可压缩的会话。");const t=await this.run({...s,cwd:s.cwd,prompt:"/compact",requireSessionResume:!0},!0);return{providerSessionId:t.providerSessionId,contextUsage:t.contextUsage??null,maintenance:{action:"compact",provider:"workbuddy",protocol:"session/prompt",stopReason:n(t.promptResponse).stopReason},durationMs:t.durationMs}}async run(s,t){if(this.busy)throw new Error("WorkBuddy 会话正在执行另一轮任务。");if(h.realpathSync(s.cwd)!==h.realpathSync(this.cwd))throw new Error("WorkBuddy 会话不能切换工作目录。");if(s.requireSessionResume&&!this.providerSessionId)throw new Error("WorkBuddy 历史会话缺少 Session ID,禁止新建会话。");if(/^\s*\/(fork|branch|fork-bg)(?:\s|$)/i.test(s.prompt))throw new Error("请使用会话菜单中的“从当前会话分叉”,以便 ACA 正确保存新会话。");const o=A(this.providerSessionId);this.busy=!0,this.input=s,this.content="",this.usage=null,this.response={},this.commands.clear(),this.commandCatalog=null,this.credits=new I,this.technicalError=null,this.router=new g,this.supplementIdentity=new v,this.updates.clear(),this.statuses.clear(),this.firstEventAtMs=this.lastEventAtMs=null;const i=Date.now();let a;try{return await W({signal:s.signal,cancelledMessage:"WorkBuddy 任务已取消",onAbort:()=>this.cancelAndDispose(),startRequest:async()=>{this.status("initializing","WorkBuddy ACP 启动中");const l=U(s.canExecuteTools!==!1);if(this.providerSessionId&&await m.retireSessionRuntimes({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,keepRuntimeIdentity:l.runtimeIdentity}),s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");const e=await m.acquire({providerId:"workbuddy",cwd:this.cwd,providerSessionId:this.providerSessionId,...l,onSessionUpdate:r=>this.handleUpdate(r),onClientRequest:(r,u,C)=>this.handleRequest(r,u,C.id)});if(this.lease=e,s.signal?.aborted)throw await e.dispose(),new Error("WorkBuddy 任务已取消");await this.initialize(e),await this.establishSession(e),o.bind(this.providerSessionId);const d=n(n(c.get(e.client)?.agentCapabilities).promptCapabilities);if(s.promptBlocks?.some(r=>r.type==="image")&&d.image!==!0)throw new Error("当前 CodeBuddy ACP 版本未声明图片输入支持。");if(t||(this.response=await q(e.client,this.providerSessionId,this.response,s)),e.setSessionResponse(this.response),t&&!this.commands.has("compact"))throw new Error("当前 WorkBuddy 会话未提供 /compact 命令,无法压缩上下文。");this.acceptingOutput=!0,this.publishCommands(),this.status("thinking",t?"正在压缩上下文":"思考中"),a=B.beginUsage();const p=await e.client.prompt({sessionId:this.providerSessionId,prompt:O(s.prompt,s.promptBlocks)},s.timeoutMs??this.timeoutMs);if(s.signal?.aborted)throw new Error("WorkBuddy 任务已取消");if(this.credits.identifyResponse(p),this.technicalError)throw S(this.technicalError);return P(p),t&&this.usage&&(this.usage={...this.usage,state:"compacted",maintenanceAction:"compact"}),this.status("completed",t?"上下文压缩完成":"已完成"),{providerSessionId:this.providerSessionId,cliType:"builtin",agentType:"workbuddy",effectiveConfig:{model:s.model||null,mode:s.mode||"default",configOptions:this.response.configOptions,provider:"workbuddy",protocol:"acp",command:e.started.command,processPid:e.started.child.pid},command:e.started.command,args:e.started.args,cwd:this.cwd,exitCode:e.started.child.exitCode,signal:e.started.child.signalCode,durationMs:Date.now()-i,stdout:e.client.rpc.stdout,stderr:e.client.rpc.stderr,content:this.content.trim(),updateCount:this.updates.count(),updates:this.updates.snapshot(),statusTimeline:this.statuses.snapshot(),contextUsage:this.usage,promptResponse:p,providerDiagnostics:this.diagnostics(),performance:{startedAtMs:i,firstEventAtMs:this.firstEventAtMs,lastEventAtMs:this.lastEventAtMs,eventCount:this.updates.count(),firstEventLatencyMs:this.firstEventAtMs===null?null:this.firstEventAtMs-i,silentForMs:Date.now()-(this.lastEventAtMs??i),durationMs:Date.now()-i}}}})}catch(l){await this.lease?.dispose();const e=S(l);throw this.status(e.failure.category==="cancelled"?"cancelled":"failed",e.message),Object.assign(e,{providerDiagnostics:this.diagnostics(),statusTimeline:this.statuses.snapshot()})}finally{a?.(),this.acceptingOutput=!1,s.onSupplementReady?.(null);try{await this.supplements?.close()}finally{o.release()}this.supplements=null,this.lease?.release(),this.lease=null,this.input=null,this.busy=!1}}async initialize(s){if(s.initialized)return;const t=n(await s.client.rpc.request("initialize",{protocolVersion:1,clientInfo:{name:"aca",version:"0.1.0"},clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1,elicitation:{form:{}},_meta:{"codebuddy.ai":{question:!0}}}},6e4));if(t.protocolVersion!==1)throw new Error("WorkBuddy ACP 协议版本不兼容。");c.set(s.client,t),s.markInitialized()}async establishSession(s){if(s.sessionEstablished&&s.providerSessionId){this.providerSessionId=s.providerSessionId,this.response=s.sessionResponse??{};const t=this.response.acaAvailableCommands;Array.isArray(t)&&(this.commandCatalog=k(t),this.commands=new Set(this.commandCatalog.map(o=>o.name)));return}if(this.providerSessionId){if(n(c.get(s.client)?.agentCapabilities).loadSession!==!0)throw new Error("当前 WorkBuddy 版本不支持恢复会话,禁止静默新建。");if(this.status("resuming","WorkBuddy 会话恢复中"),this.response=n(await s.client.loadSession({sessionId:this.providerSessionId,cwd:this.cwd},12e4)),this.response.sessionId&&this.response.sessionId!==this.providerSessionId)throw new Error("WorkBuddy 恢复返回了不同的会话 ID。")}else{if(this.status("acp","WorkBuddy 会话创建中"),this.response=n(await s.client.newSession({cwd:this.cwd},12e4)),typeof this.response.sessionId!="string"||!this.response.sessionId.trim())throw new Error("WorkBuddy session/new 未返回会话 ID。");this.providerSessionId=this.response.sessionId}this.commandCatalog&&(this.response.acaAvailableCommands=this.commandCatalog),y(this.response),s.setProviderSession({providerSessionId:this.providerSessionId,sessionResponse:this.response})}handleUpdate(s){const t=n(s),o=n(t.update);if(this.providerSessionId&&t.sessionId!==this.providerSessionId)return;const i=this.router.route(s),a=i.child?null:this.supplementIdentity.observe(s);if(!i.child&&o.sessionUpdate==="available_commands_update"&&Array.isArray(o.availableCommands)&&(this.commandCatalog=k(o.availableCommands),this.commands=new Set(this.commandCatalog.map(p=>p.name)),this.response.acaAvailableCommands=this.commandCatalog,this.lease?.setSessionResponse(this.response)),!i.child&&o.sessionUpdate==="config_option_update"&&Array.isArray(o.configOptions)&&(this.response={...this.response,configOptions:o.configOptions},this.lease?.setSessionResponse(this.response),y(this.response)),!this.acceptingOutput||!this.input)return;if(this.updates.push({method:"session/update",params:i.params}),this.firstEventAtMs??=Date.now(),this.lastEventAtMs=Date.now(),!i.child){this.credits.observe(s);const p=M(s,this.input.contextWindow);this.usage=T(s,p)??this.usage;const r=n(o._meta)["codebuddy.ai/error"];r&&(this.technicalError=r);const u=a;!this.supplements&&typeof u=="string"&&u&&this.lease&&this.providerSessionId&&(this.supplements=new _(this.lease.client,this.providerSessionId,u),this.input.onSupplementReady?.(this.supplements.channel))}const l=!i.child&&o.sessionUpdate==="available_commands_update"?{...i.params,update:{...o,availableCommands:this.commandCatalog??[]}}:i.params,e=w("workbuddy",l);e&&(!i.child&&e.type==="agent_message_chunk"&&(this.content+=e.text??""),this.input.onUpdate?.(e));const d=R("workbuddy",i.params);d&&this.status(d.phase,d.label,d.detail,d.updateType)}diagnostics(){return{workbuddy:{creditUsage:this.credits.snapshot(),...this.commandCatalog!==null?{availableCommands:this.commandCatalog}:{}}}}publishCommands(){if(this.commandCatalog===null)return;const s=w("workbuddy",{sessionId:this.providerSessionId,update:{sessionUpdate:"available_commands_update",availableCommands:this.commandCatalog}});s&&this.input?.onUpdate?.(s)}async handleRequest(s,t,o){if(!this.input||!this.acceptingOutput)throw new Error("WorkBuddy 当前没有可交互的执行轮次。");const i=n(t).sessionId;if((s!=="elicitation/create"||i!==void 0)&&i!==this.providerSessionId)throw new Error("WorkBuddy 交互请求会话不匹配。");this.status("requestPermission",s==="session/request_permission"?"等待授权":"等待回答",void 0,s);try{return await D(s,t,o,this.input)}finally{this.acceptingOutput&&this.status("thinking","处理中")}}status(s,t,o,i){const a={phase:s,label:t,detail:o,updateType:i,atMs:Date.now()};this.statuses.push(a),this.input?.onStatus?.(a)}async cancel(){await this.cancelAndDispose()}async cancelAndDispose(){this.acceptingOutput=!1,this.input?.onSupplementReady?.(null),this.providerSessionId&&this.lease?.client.cancel(this.providerSessionId),await this.lease?.dispose()}async close(){this.busy?await this.cancelAndDispose():this.lease?.release()}}export{os as WorkBuddyProvider,z as workBuddyCapabilities};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{enterWorkBuddyTask as a}from"./auth-gate.js";const t=new Set;function c(n){const o=a();let r=null,u=!1;const s=e=>{if(r!==e){if(r||t.has(e))throw new Error("CodeBuddy 会话正在执行另一轮任务,请完成后重试。");t.add(e),r=e}};try{n&&s(n)}catch(e){throw o(),e}return{bind:s,release(){u||(u=!0,r&&t.delete(r),o())}}}export{c as enterWorkBuddySession};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{record as
|
|
1
|
+
import{record as a}from"./config.js";const l=new Set(["user_message_chunk","agent_message_chunk","agent_thought_chunk","tool_call","tool_call_update"]);class g{replaying=!1;observe(n){const t=a(n),i=a(t.update),e={...a(t._meta),...a(i._meta)},d=e["codebuddy.ai/historyReplay"];if(d==="start"&&(this.replaying=!0),d==="end"&&(this.replaying=!1),d||this.replaying||e["codebuddy.ai/isCompactInternal"]===!0||e["codebuddy.ai/subagent"]||e["codebuddy.ai/memberEvent"]!==void 0||e["codebuddy.ai/parentToolCallId"]!==void 0||!l.has(String(i.sessionUpdate)))return null;for(const o of["codebuddy.ai/conversationRequestId","codebuddy.ai/requestId"]){const s=e[o];if(typeof s=="string"&&s.trim()&&s.length<=256)return s}return null}}function p(c,n){return(n?.length?n:[{type:"text",text:c}]).map(t=>t.type==="image"?{type:"image",data:t.data,mimeType:t.mimeType}:{type:"text",text:t.text})}class h{active=!0;receipts=new Map;pending=new Set;channel;constructor(n,t,i){this.channel={threadId:t,turnId:i,receipt:async e=>this.receipts.get(e)??null,send:(e,d)=>{const o=this.receipts.get(e);if(o)return Promise.resolve(o);if(!this.active)return Promise.resolve({status:"failed",error:"执行轮次已结束"});this.receipts.set(e,{status:"sending"});const s=(async()=>{let u;try{const r=a(await n.rpc.request("session/steer",{sessionId:t,expectedRequestId:i,clientUserMessageId:e,contentBlocks:p("",d)},15e3));u=r.steered===!0?{status:"accepted"}:{status:"failed",error:r.reason==="stale"?"CodeBuddy 未接受补充:目标执行轮次已变化(stale),可作为新消息发送":r.reason==="idle"?"CodeBuddy 未接受补充:当前轮次已结束或暂不支持补充(idle),可作为新消息发送":`CodeBuddy 未接受补充:${String(r.reason||"当前状态不支持")}`}}catch(r){u={status:a(r).rpcResponse===!0?"failed":"uncertain",error:r instanceof Error?r.message:String(r)}}return this.receipts.set(e,u),u})();return this.pending.add(s),s.finally(()=>this.pending.delete(s)),s}}}async close(){this.active=!1,await Promise.allSettled(this.pending)}}export{g as WorkBuddySupplementIdentity,h as WorkBuddySupplements,p as workBuddyPromptContent};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{record as s}from"./config.js";const a=["systemPrompt","conversation","tools","mcp","skills"];function m(o,t){if(!t)return null;const n=s(s(s(o).update)._meta)["codebuddy.ai/usageByCategory"],e=s(n);if(e.version!==1||!a.every(d=>c(e[d])))return t;const i=a.reduce((d,r)=>d+Number(e[r]),0);return Math.abs(i-t.usedTokens)>1?t:{...t,workbuddyUsageByCategory:{version:1,systemPrompt:Number(e.systemPrompt),conversation:Number(e.conversation),tools:Number(e.tools),mcp:Number(e.mcp),skills:Number(e.skills)}}}class h{totals=new Map;requestIds=new Set;observe(t){const n=s(t),e=s(n.update),i=s(e._meta),d=i["codebuddy.ai/requestId"]??s(n._meta)["codebuddy.ai/requestId"];if(this.identify(d),e.sessionUpdate==="usage_update")for(const[r,u]of Object.entries(s(i["codebuddy.ai/requestCredits"])))!r||r.length>256||!c(u)||this.totals.size>=4096&&!this.totals.has(r)||this.totals.set(r,Math.max(this.totals.get(r)??0,u))}identifyResponse(t){this.identify(s(s(t)._meta)["codebuddy.ai/requestId"])}identify(t){typeof t=="string"&&t&&t.length<=256&&this.requestIds.size<64&&this.requestIds.add(t)}snapshot(){const t=[...this.requestIds].filter(e=>this.totals.has(e));return t.length?{credits:Math.round(t.reduce((e,i)=>e+this.totals.get(i),0)*100)/100,requestCount:t.length,partial:t.length<this.requestIds.size}:null}}function c(o){return typeof o=="number"&&Number.isFinite(o)&&o>=0}export{h as WorkBuddyTurnCredits,m as workBuddyContextUsage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function m(o){const e=f(o.acpRun),t=s(e.configOptionValues),i=s(o.configOptionValues),n={...t,...i},r=d(o.model,i.model,e.model,t.model),c=d(o.mode,i.mode,e.mode,t.mode);return r&&"model"in n&&(n.model=r),c&&"mode"in n&&(n.mode=c),{model:r,mode:c,configOptionValues:o.configOptionValues!==void 0||Object.keys(t).length?n:void 0}}function f(o){return o&&typeof o=="object"&&!Array.isArray(o)?o:{}}function s(o){return Object.fromEntries(Object.entries(f(o)).filter(e=>typeof e[1]=="string"||typeof e[1]=="boolean"))}function d(...o){return o.find(e=>typeof e=="string"&&!!e.trim())?.trim()}export{m as resolveJobExecutionConfig};
|
package/dist/core/job-worker.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createJobInteractions as Y}from"../interactions/job-interactions.js";import{createJobSupplementReceiver as Z}from"./job-supplements.js";import{buildPromptWithAttachments as ee,buildPromptBlocks as te,materializeJobAttachments as ne,summarizeMaterializedAttachments as L}from"./job-attachments.js";import{managementActivity as g}from"./management/activity.js";import{managedClaim as oe}from"./management/claims.js";import{startJobArtifactMonitor as ie,mergeArtifactFiles as D}from"./artifacts/job-artifact-monitor.js";import ae from"node:fs";import{compactAgentContext as re,forkAgentSession as se,sendAgentPrompt as ce}from"../acp/agent.js";import{acaServerRequest as A}from"./aca-server-client.js";import{loadAcaConfig as le}from"./aca-config.js";import{prepareLocalWorktree as de}from"./worktree.js";import{expandWorkerRuntimePath as ue}from"./worker-runtime-paths.js";import{collectAffectedFiles as V,extractAffectedFilePathsFromAcpRaw as pe}from"./affected-files.js";import{resolveJobPermissionPolicy as me}from"./job-permission-policy.js";import{createAcpEventBatcher as fe}from"./acp-event-batcher.js";import{createToolLifecycleTrace as he}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as we}from"./acp-event-upload-filter.js";import{userFacingAcpError as Ie}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as Se}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as be}from"../acp/session-errors.js";const ye=1500,q=5e3,K=900,ve=1e4,Ae=0,_e=12e3;function Ce(e){return ue(e)}function Te(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 ut(e,t={}){let n=!1,s=null,i=null,l=0,w=0;const I=new Set,a=new Map,y=new Map,_=async()=>{if(n||g.paused||g.stopping||Date.now()<w)return;const m=Pe();if(!X(I.size,m))return;const S=g.enter("poll");if(S)try{const u=t.loadConfig?.()??le();if(!u.token)return;for(;!n&&!g.paused&&!g.stopping&&X(I.size,m);){const v=t.claimNextJob?await t.claimNextJob(u,[...a.keys()]):await oe("job",()=>A("POST","/api/client/jobs/claim",{machineId:u.machineId,workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]}),{workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]});if(l=0,w=0,!v.item)break;const p=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(p,u):J(p.job_id,u.machineId,"cancelled")).catch(()=>{});break}const b=p.session_id,f=a.get(b),$=g.track("job");I.add(p.job_id);const k=new AbortController;let j;j=(async()=>{f&&(e.warn(`claimed job=${p.job_id} for already active session=${b}; waiting for the previous run to release its ACP runtime`),await ge(f,p,u,k)),!k.signal.aborted&&await(t.runJob??ke)(p,u,e,k)})().catch(R=>{g.uncertain.add("job-finalize"),e.warn(`job ${p.job_id} failed outside handler: ${R instanceof Error?R.message:String(R)}`)}).finally(()=>{$(),I.delete(p.job_id),a.get(b)===j&&a.delete(b),y.delete(p.job_id)}),a.set(b,j),y.set(p.job_id,{controller:k,promise:j})}}catch(u){if(!n){l+=1;const v=Be(l);w=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${u instanceof Error?u.message:String(u)}`)}}finally{S()}},M=()=>{if(n||s)return;const m=_();s=m,m.finally(()=>{s===m&&(s=null)})};M();const c=setInterval(()=>{M()},t.claimIntervalMs??ye);return{timer:c,stop:()=>i||(i=(async()=>{if(n)return;n=!0,clearInterval(c);for(const S of y.values())S.controller.abort();await s?.catch(()=>{});for(const S of y.values())S.controller.abort();const m=[...y.values()].map(S=>S.promise);m.length!==0&&await Promise.race([Promise.allSettled(m).then(()=>{}),We(_e)])})(),i)}}async function ge(e,t,n,s){let i=!1;const w=setInterval(()=>{i||s.signal.aborted||(i=!0,Promise.allSettled([A("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),H(t.job_id,s)]).finally(()=>{i=!1}))},q);try{await Promise.race([e,Re(s.signal)])}finally{clearInterval(w)}}function Re(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function Pe(){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?Ae:t===0?0:Math.max(1,Math.min(t,8))}function X(e,t){return t===0||e<t}async function ke(e,t,n,s){const i=e.job_id,l=e.session_id,w=e.workspace_id,I=e.project_id??null,a=e.input??{},y=Y(i,t.machineId,String(a.acpRun?.agentType||"codex"),s.signal),_=String(a.control?.type||"").toLowerCase();if(!_&&!a.message?.trim()&&(a.attachments??[]).length===0&&!a.sessionReferences?.length){await J(i,t.machineId,"job input missing message");return}const M=t.projects.find(r=>r.projectId===I),c=Ce(M?.rootPath||e.project_root_path||"");if(!c){await J(i,t.machineId,"job project root is unknown");return}Te(e)&&ae.mkdirSync(c,{recursive:!0}),n.info(`claimed job=${i} session=${l} project=${M?.name??I??"unknown"}`);let m=!1,S=!1;const u=setInterval(()=>{m||(m=!0,A("POST",`/api/client/jobs/${encodeURIComponent(i)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{m=!1})),S||(S=!0,H(i,s).catch(()=>{}).finally(()=>{S=!1}))},q);let v=0;const p=he(r=>n.info(r)),b=fe(i,t.machineId,{warn:r=>n.warn(r),trace:p}),f=Ne(i,t.machineId),$=new Set;let k=null,j,R;const W=async()=>j?.finish().catch(r=>(g.uncertain.add("job-artifacts"),n.warn(`artifact collection failed job=${i}: ${String(r)}`),[]))??[];try{await f.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const r=de({projectId:I,sessionId:l,projectRoot:c,worktree:e});k=r,await Fe(i,t.machineId,r);const C=Me(a.acpRun);if(await y.initialize(),xe(C.agentType)&&await Se(e.codex_provider_revision),_==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const o=await re({agentType:E(C.agentType,"codex"),cliType:E(C.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,allowRollover:a.control?.allowRollover===!0,signal:s.signal,onStatus:h=>{f.report({phase:h.phase,label:h.label,detail:h.detail,updateType:h.updateType,raw:h})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:o.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:a.control?.allowRollover===!0},acpSessionId:o.acpSessionId,durationMs:o.durationMs,usage:o.contextUsage,maintenance:o.maintenance,worktree:N(r)}}),n.info(`completed context compact job=${i}`);return}if(_==="fork"){const o=String(a.control?.sourceAcpSessionId||"").trim();if(!o)throw new Error("Source session does not have a Codex ACP context to fork");await f.sendNow({phase:"forking",label:"正在分叉 Codex 会话",detail:a.control?.sourceSessionId?`源会话 ${a.control.sourceSessionId}`:void 0}).catch(()=>{});const h=await se({agentType:E(C.agentType,"codex"),cliType:E(C.cliType,"builtin"),sourceProviderSessionId:o,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,signal:s.signal,onStatus:x=>{f.report({phase:x.phase,label:x.label,detail:x.detail,updateType:x.updateType,raw:x})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:h.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:a.control?.sourceSessionId||null},acpSessionId:h.acpSessionId,worktree:N(r)}}),n.info(`completed Codex session fork job=${i}`);return}const T=await ne({cwd:r.cwd,sessionId:l,jobId:i,attachments:a.attachments??[]});T.length>0&&(n.info(`materialized ${T.length} attachment(s) for job=${i}`),await f.sendNow({phase:"initializing",label:"附件已写入本机",detail:T.map(o=>o.relativePath).join(", "),raw:{attachments:L(T)}}).catch(()=>{}));const O=ee(a,T),P=te(O,T);j=await ie(r.cwd,o=>n.warn(o)).catch(o=>{n.warn(`artifact monitor unavailable job=${i}: ${String(o)}`)}),a.automation||(R=Z({jobId:i,sessionId:l,cwd:r.cwd,logger:n,signal:s.signal}));const d=await ce({agentType:E(C.agentType,"codex"),cliType:E(C.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:je(e.session_raw),prompt:O,promptBlocks:P,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,canExecuteTools:a.canExecuteTools,timeoutMs:a.timeoutMs,contextWindow:Ee(C.contextWindow),signal:s.signal,onSupplementReady:o=>R?.setChannel(o),onStatus:o=>{f.report({phase:o.phase,label:o.label,detail:o.detail,updateType:o.updateType,raw:o})},onUpdate:o=>{v+=1,(o.type==="tool_call"||o.type==="tool_call_update"&&["completed","failed","cancelled"].includes(o.status||""))&&p("worker_event",{jobId:i,uploadRunId:b.uploadRunId,toolCallId:o.toolCallId,status:o.status,sequence:v});for(const x of pe(o.raw))$.add(x);const h={turnId:e.user_turn_id??null,eventType:o.type,label:o.label,text:o.text,status:o.status,toolCallId:o.toolCallId,raw:Je(o.raw,v)};we(h)&&b.push(h)},onPermissionRequest:o=>{const h=me(a,o);return h?Promise.resolve(h):y.permission(o)},onUserInputRequest:o=>y.input(o),onProviderSessionMigration:async o=>{const h=await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:o.previousProviderSessionId,acpSessionId:o.providerSessionId,reason:o.reason,migratedAtMs:o.migratedAtMs});e.acp_session_id=o.providerSessionId,n.info(`migrated ACP session for job=${i} ${o.previousProviderSessionId} -> ${o.providerSessionId}`),await b.flush().catch(()=>{}),await f.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:o.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:o,server:{migrated:h.migrated===!0}}}).catch(()=>{})}}),Q=await W(),U=await b.finish();U.complete||g.uncertain.add("job-event-upload"),await f.flush(),await R?.stop();const B=D(V({cwd:r.cwd,evidencePaths:$}),Q),F=$e(d.promptResponse);if(!(Oe(F)||!!d.content)){await J(i,t.machineId,`${d.agentType} stopped: ${F??"unknown"}`,{acpSessionId:d.acpSessionId,stopReason:F,eventUpload:U,files:B,exitCode:d.exitCode,stderr:Le(d.stderr,4e3),worktree:N(r)});return}await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:d.acpSessionId,assistantText:d.content,result:{ok:!0,eventUpload:U,acpSessionId:d.acpSessionId,stopReason:F,durationMs:d.durationMs,acpRun:a.acpRun??null,effectiveConfig:d.effectiveConfig??null,performance:d.performance??null,usage:d.contextUsage??null,providerDiagnostics:d.providerDiagnostics??null,updateCount:d.updateCount,statusTimeline:d.statusTimeline,worktree:N(r),attachments:L(T),files:B}}),n.info(`completed job=${i}`)}catch(r){if(await R?.stop(),ze(s.signal)){b.discard(),f.discard(),n.warn(`stopped local job=${i} because ACA Server already finalized it`);return}const C=await W(),T=await b.finish();T.complete||g.uncertain.add("job-event-upload"),await f.flush();const O=s.signal.aborted?"cancelled":r instanceof Error?r.message:String(r),P=be(r),d=D(k?V({cwd:k.cwd,evidencePaths:$}):[],C);await J(i,t.machineId,Ie(O),{rawError:O,eventUpload:T,files:d,providerDiagnostics:z(r,"providerDiagnostics"),statusTimeline:z(r,"statusTimeline"),performance:z(r,"performance"),...P?{acpSessionInvalidated:!0,invalidatedAcpSessionId:P.providerSessionId,failure:{category:P.category,actions:P.actions,details:P.details}}:{}},P?{invalidateAcpSession:!0,invalidatedAcpSessionId:P.providerSessionId}:void 0)}finally{await y.close(),await R?.stop(),j?.dispose(),b.discard(),clearInterval(u),await f.flush()}}function je(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!e||typeof e!="object"||Array.isArray(e)?{}:e}function xe(e){const t=String(e||"codex").toLowerCase().replace(/[^a-z0-9]+/g,"");return t==="codex"||t==="codexacp"||t==="codexstandard"}function Ee(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function E(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function $e(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 Oe(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Ne(e,t){let n=null,s=0,i="",l=null,w=Promise.resolve(),I=!1;const a=c=>JSON.stringify({phase:c.phase??"",label:c.label??"",detail:c.detail??"",updateType:c.updateType??""}),y=async c=>{await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:c.phase,label:c.label,detail:c.detail,updateType:c.updateType,raw:c.raw}),s=Date.now(),i=a(c)},_=()=>{if(l&&(clearTimeout(l),l=null),I)return n=null,w;if(!n)return w;const c=n;return n=null,w=w.then(()=>y(c)).catch(()=>{}),w},M=c=>{l||(l=setTimeout(()=>{_()},c),l.unref())};return{report(c){if(I)return;const m=Date.now(),u=a(c)!==i,v=m-s>=ve;if(!u&&!v)return;n=c;const p=m-s;if(p>=K||s===0){_();return}M(K-p)},sendNow(c){return I?w:(n=c,_())},flush:_,discard(){I=!0,n=null,l&&clearTimeout(l),l=null}}}function Je(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}async function Fe(e,t,n){!n.usedWorktree||!n.worktreeId||await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??N(n)})}function N(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 H(e,t){if(t.signal.aborted)return;const n=await A("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const s=Ue(n.item);s&&t.abort(new G(e,s))}function Ue(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class G extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function ze(e){return e.aborted&&e.reason instanceof G}async function J(e,t,n,s,i){await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:s,...i?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:i.invalidatedAcpSessionId}:{}})}function We(e){return new Promise(t=>setTimeout(t,e))}function Be(e){return Math.min(1e4,500*2**Math.min(5,Math.max(0,e-1)))+Math.floor(Math.random()*250)}function Le(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}function z(e,t){return!e||typeof e!="object"||Array.isArray(e)?null:e[t]??null}export{Ue as serverFinalizedJobStatus,ut as startJobWorkerLoop};
|
|
1
|
+
import{createJobInteractions as Y}from"../interactions/job-interactions.js";import{createJobSupplementReceiver as Z}from"./job-supplements.js";import{buildPromptWithAttachments as ee,buildPromptBlocks as te,materializeJobAttachments as ne,summarizeMaterializedAttachments as L}from"./job-attachments.js";import{managementActivity as g}from"./management/activity.js";import{managedClaim as oe}from"./management/claims.js";import{startJobArtifactMonitor as ie,mergeArtifactFiles as D}from"./artifacts/job-artifact-monitor.js";import ae from"node:fs";import{compactAgentContext as re,forkAgentSession as se,sendAgentPrompt as ce}from"../acp/agent.js";import{acaServerRequest as A}from"./aca-server-client.js";import{loadAcaConfig as le}from"./aca-config.js";import{prepareLocalWorktree as de}from"./worktree.js";import{expandWorkerRuntimePath as ue}from"./worker-runtime-paths.js";import{collectAffectedFiles as V,extractAffectedFilePathsFromAcpRaw as pe}from"./affected-files.js";import{resolveJobPermissionPolicy as me}from"./job-permission-policy.js";import{resolveJobExecutionConfig as fe}from"./job-execution-config.js";import{createAcpEventBatcher as he}from"./acp-event-batcher.js";import{createToolLifecycleTrace as we}from"../acp/tool-lifecycle-trace.js";import{shouldBufferAcpEvent as Ie}from"./acp-event-upload-filter.js";import{userFacingAcpError as Se}from"../acp/error-display.js";import{ensureCodexProviderRuntimeRevision as be}from"./codex-provider-runtime-config.js";import{acpSessionInvalidationFromError as ye}from"../acp/session-errors.js";const ve=1500,q=5e3,K=900,Ae=1e4,_e=0,Te=12e3;function Ce(e){return ue(e)}function ge(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 mt(e,t={}){let n=!1,s=null,i=null,l=0,w=0;const I=new Set,a=new Map,y=new Map,_=async()=>{if(n||g.paused||g.stopping||Date.now()<w)return;const m=ke();if(!X(I.size,m))return;const S=g.enter("poll");if(S)try{const u=t.loadConfig?.()??le();if(!u.token)return;for(;!n&&!g.paused&&!g.stopping&&X(I.size,m);){const v=t.claimNextJob?await t.claimNextJob(u,[...a.keys()]):await oe("job",()=>A("POST","/api/client/jobs/claim",{machineId:u.machineId,workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]}),{workspaceId:u.defaultWorkspaceId,activeSessionIds:[...a.keys()]});if(l=0,w=0,!v.item)break;const p=v.item;if(n){await(t.cancelClaimedJob?t.cancelClaimedJob(p,u):N(p.job_id,u.machineId,"cancelled")).catch(()=>{});break}const b=p.session_id,f=a.get(b),$=g.track("job");I.add(p.job_id);const k=new AbortController;let j;j=(async()=>{f&&(e.warn(`claimed job=${p.job_id} for already active session=${b}; waiting for the previous run to release its ACP runtime`),await Re(f,p,u,k)),!k.signal.aborted&&await(t.runJob??je)(p,u,e,k)})().catch(R=>{g.uncertain.add("job-finalize"),e.warn(`job ${p.job_id} failed outside handler: ${R instanceof Error?R.message:String(R)}`)}).finally(()=>{$(),I.delete(p.job_id),a.get(b)===j&&a.delete(b),y.delete(p.job_id)}),a.set(b,j),y.set(p.job_id,{controller:k,promise:j})}}catch(u){if(!n){l+=1;const v=Le(l);w=Date.now()+v,e.warn(`job worker failed, retry in ${v}ms: ${u instanceof Error?u.message:String(u)}`)}}finally{S()}},E=()=>{if(n||s)return;const m=_();s=m,m.finally(()=>{s===m&&(s=null)})};E();const c=setInterval(()=>{E()},t.claimIntervalMs??ve);return{timer:c,stop:()=>i||(i=(async()=>{if(n)return;n=!0,clearInterval(c);for(const S of y.values())S.controller.abort();await s?.catch(()=>{});for(const S of y.values())S.controller.abort();const m=[...y.values()].map(S=>S.promise);m.length!==0&&await Promise.race([Promise.allSettled(m).then(()=>{}),Be(Te)])})(),i)}}async function Re(e,t,n,s){let i=!1;const w=setInterval(()=>{i||s.signal.aborted||(i=!0,Promise.allSettled([A("POST",`/api/client/jobs/${encodeURIComponent(t.job_id)}/heartbeat`,{machineId:n.machineId}),H(t.job_id,s)]).finally(()=>{i=!1}))},q);try{await Promise.race([e,Pe(s.signal)])}finally{clearInterval(w)}}function Pe(e){return e.aborted?Promise.resolve():new Promise(t=>e.addEventListener("abort",()=>t(),{once:!0}))}function ke(){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?_e:t===0?0:Math.max(1,Math.min(t,8))}function X(e,t){return t===0||e<t}async function je(e,t,n,s){const i=e.job_id,l=e.session_id,w=e.workspace_id,I=e.project_id??null,a={...e.input,...fe(e.input??{})},y=Y(i,t.machineId,String(a.acpRun?.agentType||"codex"),s.signal),_=String(a.control?.type||"").toLowerCase();if(!_&&!a.message?.trim()&&(a.attachments??[]).length===0&&!a.sessionReferences?.length){await N(i,t.machineId,"job input missing message");return}const E=t.projects.find(r=>r.projectId===I),c=Ce(E?.rootPath||e.project_root_path||"");if(!c){await N(i,t.machineId,"job project root is unknown");return}ge(e)&&ae.mkdirSync(c,{recursive:!0}),n.info(`claimed job=${i} session=${l} project=${E?.name??I??"unknown"}`);let m=!1,S=!1;const u=setInterval(()=>{m||(m=!0,A("POST",`/api/client/jobs/${encodeURIComponent(i)}/heartbeat`,{machineId:t.machineId}).catch(()=>{}).finally(()=>{m=!1})),S||(S=!0,H(i,s).catch(()=>{}).finally(()=>{S=!1}))},q);let v=0;const p=we(r=>n.info(r)),b=he(i,t.machineId,{warn:r=>n.warn(r),trace:p}),f=Ne(i,t.machineId),$=new Set;let k=null,j,R;const W=async()=>j?.finish().catch(r=>(g.uncertain.add("job-artifacts"),n.warn(`artifact collection failed job=${i}: ${String(r)}`),[]))??[];try{await f.sendNow({phase:"initializing",label:"准备工作区",detail:e.worktree_id?"worktree":"project root"}).catch(()=>{});const r=de({projectId:I,sessionId:l,projectRoot:c,worktree:e});k=r,await Ue(i,t.machineId,r);const T=Me(a.acpRun);if(await y.initialize(),xe(T.agentType)&&await be(e.codex_provider_revision),_==="compact"){if(!e.acp_session_id)throw new Error("Current session does not have an ACP context to compact");const o=await re({agentType:x(T.agentType,"codex"),cliType:x(T.cliType,"builtin"),acpSessionId:e.acp_session_id,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,allowRollover:a.control?.allowRollover===!0,signal:s.signal,onStatus:h=>{f.report({phase:h.phase,label:h.label,detail:h.detail,updateType:h.updateType,raw:h})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:o.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"compact",allowRollover:a.control?.allowRollover===!0},acpSessionId:o.acpSessionId,durationMs:o.durationMs,usage:o.contextUsage,maintenance:o.maintenance,worktree:J(r)}}),n.info(`completed context compact job=${i}`);return}if(_==="fork"){const o=String(a.control?.sourceAcpSessionId||"").trim();if(!o)throw new Error("Source session does not have an ACP context to fork");await f.sendNow({phase:"forking",label:"正在分叉会话",detail:a.control?.sourceSessionId?`源会话 ${a.control.sourceSessionId}`:void 0}).catch(()=>{});const h=await se({agentType:x(T.agentType,"codex"),cliType:x(T.cliType,"builtin"),sourceProviderSessionId:o,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,timeoutMs:a.timeoutMs,signal:s.signal,onStatus:M=>{f.report({phase:M.phase,label:M.label,detail:M.detail,updateType:M.updateType,raw:M})}});await f.flush(),await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:h.acpSessionId,suppressTurn:!0,result:{ok:!0,control:{type:"fork",sourceSessionId:a.control?.sourceSessionId||null},acpSessionId:h.acpSessionId,worktree:J(r)}}),n.info(`completed session fork job=${i}`);return}const C=await ne({cwd:r.cwd,sessionId:l,jobId:i,attachments:a.attachments??[]});C.length>0&&(n.info(`materialized ${C.length} attachment(s) for job=${i}`),await f.sendNow({phase:"initializing",label:"附件已写入本机",detail:C.map(o=>o.relativePath).join(", "),raw:{attachments:L(C)}}).catch(()=>{}));const O=ee(a,C),P=te(O,C);j=await ie(r.cwd,o=>n.warn(o)).catch(o=>{n.warn(`artifact monitor unavailable job=${i}: ${String(o)}`)}),a.automation||(R=Z({jobId:i,sessionId:l,cwd:r.cwd,logger:n,signal:s.signal}));const d=await ce({agentType:x(T.agentType,"codex"),cliType:x(T.cliType,"builtin"),acpSessionId:e.acp_session_id??null,requireSessionResume:Ee(e.session_raw),prompt:O,promptBlocks:P,cwd:r.cwd,model:a.model,mode:a.mode,configOptionValues:a.configOptionValues,canExecuteTools:a.canExecuteTools,timeoutMs:a.timeoutMs,contextWindow:$e(T.contextWindow),signal:s.signal,onSupplementReady:o=>R?.setChannel(o),onStatus:o=>{f.report({phase:o.phase,label:o.label,detail:o.detail,updateType:o.updateType,raw:o})},onUpdate:o=>{v+=1,(o.type==="tool_call"||o.type==="tool_call_update"&&["completed","failed","cancelled"].includes(o.status||""))&&p("worker_event",{jobId:i,uploadRunId:b.uploadRunId,toolCallId:o.toolCallId,status:o.status,sequence:v});for(const M of pe(o.raw))$.add(M);const h={turnId:e.user_turn_id??null,eventType:o.type,label:o.label,text:o.text,status:o.status,toolCallId:o.toolCallId,raw:Fe(o.raw,v)};Ie(h)&&b.push(h)},onPermissionRequest:o=>{const h=me(a,o);return h?Promise.resolve(h):y.permission(o)},onUserInputRequest:o=>y.input(o),onProviderSessionMigration:async o=>{const h=await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/acp-session`,{machineId:t.machineId,previousAcpSessionId:o.previousProviderSessionId,acpSessionId:o.providerSessionId,reason:o.reason,migratedAtMs:o.migratedAtMs});e.acp_session_id=o.providerSessionId,n.info(`migrated ACP session for job=${i} ${o.previousProviderSessionId} -> ${o.providerSessionId}`),await b.flush().catch(()=>{}),await f.sendNow({phase:"resuming",label:"Codex 会话已迁移",detail:o.providerSessionId,updateType:"aca.performance.provider_session_migrated",raw:{migration:o,server:{migrated:h.migrated===!0}}}).catch(()=>{})}}),Q=await W(),z=await b.finish();z.complete||g.uncertain.add("job-event-upload"),await f.flush(),await R?.stop();const B=D(V({cwd:r.cwd,evidencePaths:$}),Q),F=Oe(d.promptResponse);if(!(Je(F)||!!d.content)){await N(i,t.machineId,`${d.agentType} stopped: ${F??"unknown"}`,{acpSessionId:d.acpSessionId,stopReason:F,eventUpload:z,files:B,exitCode:d.exitCode,stderr:De(d.stderr,4e3),worktree:J(r)});return}await A("POST",`/api/client/jobs/${encodeURIComponent(i)}/complete`,{machineId:t.machineId,acpSessionId:d.acpSessionId,assistantText:d.content,result:{ok:!0,eventUpload:z,acpSessionId:d.acpSessionId,stopReason:F,durationMs:d.durationMs,acpRun:a.acpRun??null,effectiveConfig:d.effectiveConfig??null,performance:d.performance??null,usage:d.contextUsage??null,providerDiagnostics:d.providerDiagnostics??null,updateCount:d.updateCount,statusTimeline:d.statusTimeline,worktree:J(r),attachments:L(C),files:B}}),n.info(`completed job=${i}`)}catch(r){if(await R?.stop(),We(s.signal)){b.discard(),f.discard(),n.warn(`stopped local job=${i} because ACA Server already finalized it`);return}const T=await W(),C=await b.finish();C.complete||g.uncertain.add("job-event-upload"),await f.flush();const O=s.signal.aborted?"cancelled":r instanceof Error?r.message:String(r),P=ye(r),d=D(k?V({cwd:k.cwd,evidencePaths:$}):[],T);await N(i,t.machineId,Se(O),{rawError:O,eventUpload:C,files:d,providerDiagnostics:U(r,"providerDiagnostics"),failure:U(r,"failure"),statusTimeline:U(r,"statusTimeline"),performance:U(r,"performance"),...P?{acpSessionInvalidated:!0,invalidatedAcpSessionId:P.providerSessionId,failure:{category:P.category,actions:P.actions,details:P.details}}:{}},P?{invalidateAcpSession:!0,invalidatedAcpSessionId:P.providerSessionId}:void 0)}finally{await y.close(),await R?.stop(),j?.dispose(),b.discard(),clearInterval(u),await f.flush()}}function Ee(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!e||typeof e!="object"||Array.isArray(e)?{}:e}function xe(e){const t=String(e||"codex").toLowerCase().replace(/[^a-z0-9]+/g,"");return t==="codex"||t==="codexacp"||t==="codexstandard"}function $e(e){return typeof e=="number"&&Number.isFinite(e)&&e>0?e:void 0}function x(...e){for(const t of e)if(typeof t=="string"&&t.trim())return t.trim();return""}function Oe(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 Je(e){return e?["end_turn","stop","completed","complete"].includes(e):!1}function Ne(e,t){let n=null,s=0,i="",l=null,w=Promise.resolve(),I=!1;const a=c=>JSON.stringify({phase:c.phase??"",label:c.label??"",detail:c.detail??"",updateType:c.updateType??""}),y=async c=>{await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/presence`,{machineId:t,phase:c.phase,label:c.label,detail:c.detail,updateType:c.updateType,raw:c.raw}),s=Date.now(),i=a(c)},_=()=>{if(l&&(clearTimeout(l),l=null),I)return n=null,w;if(!n)return w;const c=n;return n=null,w=w.then(()=>y(c)).catch(()=>{}),w},E=c=>{l||(l=setTimeout(()=>{_()},c),l.unref())};return{report(c){if(I)return;const m=Date.now(),u=a(c)!==i,v=m-s>=Ae;if(!u&&!v)return;n=c;const p=m-s;if(p>=K||s===0){_();return}E(K-p)},sendNow(c){return I?w:(n=c,_())},flush:_,discard(){I=!0,n=null,l&&clearTimeout(l),l=null}}}function Fe(e,t){return e&&typeof e=="object"&&!Array.isArray(e)?{...e,acaSequence:t}:{value:e,acaSequence:t}}async function Ue(e,t,n){!n.usedWorktree||!n.worktreeId||await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/worktree`,{machineId:t,baseCommit:n.baseCommit??null,worktreePath:n.cwd,status:n.status??"active",raw:n.raw??J(n)})}function J(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 H(e,t){if(t.signal.aborted)return;const n=await A("GET",`/api/client/jobs/${encodeURIComponent(e)}`);if(Number(n.item.cancel_requested??0)===1){t.abort();return}const s=ze(n.item);s&&t.abort(new G(e,s))}function ze(e){const t=String(e.status||"").toLowerCase();return t&&!["claimed","running"].includes(t)?t:null}class G extends Error{constructor(t,n){super(`ACA Server already finalized job=${t} status=${n}`),this.name="ServerFinalizedJobError"}}function We(e){return e.aborted&&e.reason instanceof G}async function N(e,t,n,s,i){await A("POST",`/api/client/jobs/${encodeURIComponent(e)}/fail`,{machineId:t,error:n,result:s,...i?.invalidateAcpSession?{invalidateAcpSession:!0,invalidatedAcpSessionId:i.invalidatedAcpSessionId}:{}})}function Be(e){return new Promise(t=>setTimeout(t,e))}function Le(e){return Math.min(1e4,500*2**Math.min(5,Math.max(0,e-1)))+Math.floor(Math.random()*250)}function De(e,t){return e.length<=t?e:`${e.slice(0,t)}...`}function U(e,t){return!e||typeof e!="object"||Array.isArray(e)?null:e[t]??null}export{ze as serverFinalizedJobStatus,mt as startJobWorkerLoop};
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o="0.1.
|
|
1
|
+
const o="0.1.91";export{o as AICA_VERSION};
|