aws-runtime-bridge 1.9.117 → 1.9.119
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/routes/instance.js +1 -1
- package/dist/services/lifecycle-state.d.ts +5 -0
- package/dist/services/lifecycle-state.js +2 -2
- package/package/acode/dist/runtime.js +24 -16
- package/package/aws-client-agent-mcp/dist/mcp-server.js +22 -22
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +6 -6
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +1 -1
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +3 -2
- package/package/aws-client-agent-mcp/dist/memory-store.js +3 -3
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +122 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.js +9 -6
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +1 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js +1 -1
- package/package.json +1 -1
package/dist/routes/instance.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Router as X}from"express";import _ from"axios";import{spawn as x}from"node:child_process";import{createHash as H,timingSafeEqual as V}from"node:crypto";import{fileURLToPath as W}from"node:url";import I from"node:path";import{getBridgeVersion as R,resolveSchedulerBaseUrlFrom as G}from"../config.js";import{validateToken as S,validateTokenOrPanelSession as v}from"../middleware/auth.js";import{getConfiguredConnectionKeys as A,getRegistrationState as J,requestRuntimeAccessTokenRefreshForServer as Y,resolveSchedulerBaseUrlFromToken as Q}from"../services/auto-register.js";import{discoverCcSwitchConfiguredItems as Z,loadCcSwitchSdk as ee}from"../services/cc-switch-sdk.js";import{initInstance as te,syncLegacyStateFromSdk as re}from"../services/instance-init-service.js";import{setAgentFeatures as se,getAgentFeatures as C}from"../services/instance-features.js";import{createBridgeLifecycleOperation as L,deliverPendingBridgeLifecycleNotifications as U,upsertBridgeLifecycleOperation as b}from"../services/lifecycle-state.js";import{appendUpdateLog as p,readUpdateLog as oe}from"../services/update-log.js";import{checkBridgeUpdate as q}from"../utils/npm-version.js";import{loadInstanceState as P,saveInstanceState as O}from"../services/instance-state.js";import{getRuntimeAccessToken as ne}from"../services/runtime-binding.js";import{detectToolStatuses as j,ensureToolsInstalled as ae,SUPPORTED_INSTALLABLE_TOOLS as E,SUPPORTED_UNINSTALLABLE_TOOLS as $,uninstallTools as ie}from"../services/tool-installer.js";import{createLogger as le}from"../utils/logger.js";const n=le("instance"),ce=["acode","claude","opencode","codex"];let T=null;function De(r){T=r}const d=X();function K(r){return String(r||"").trim()||void 0}function D(r){const t=r.get("host");if(t)return`${r.protocol}://${t}`}function F(){const r=I.resolve(I.dirname(W(import.meta.url)),"..","..");return I.join(r,"dist","index.js")}function de(){return new Promise((r,t)=>{const e=x(process.execPath,[F(),"update","--no-kill"],{shell:!1,windowsHide:!0});let s="",o="";e.stdout.on("data",a=>{s+=a.toString("utf8")}),e.stderr.on("data",a=>{o+=a.toString("utf8")}),e.on("error",t),e.on("close",a=>{r({exitCode:a,stdout:s,stderr:o})})})}function M(){const r=x(process.execPath,[F(),"service","restart"],{shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});r.stdout.on("data",t=>{p(String(t)).catch(()=>{})}),r.stderr.on("data",t=>{p(String(t)).catch(()=>{})})}const N=3e3;async function ue(r){const t={...r,status:"running",message:"Bridge \u66F4\u65B0\u6B63\u5728\u540E\u53F0\u6267\u884C\u3002"};await b(t);try{n.info("[update-bridge] \u6B63\u5728\u540E\u53F0\u6267\u884C awsb update --yes"),await p(`===== Bridge \u66F4\u65B0\u5F00\u59CB\uFF1Av${R()} -> latest\uFF08\u64CD\u4F5C ${r.operationId}\uFF09 =====`);const e=await de();if(e.stdout&&await p(e.stdout),e.stderr&&await p(e.stderr),e.exitCode===0){await p("===== Bridge \u66F4\u65B0\u6210\u529F\uFF0C\u51C6\u5907\u81EA\u52A8\u91CD\u542F ===== ");const s={...t,status:"restarting",message:"Bridge \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u91CD\u542F..."};await b(s),await U(),n.info("[update-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),M(),setTimeout(()=>{if(process.exitCode=42,T){T("BRIDGE_UPDATE_RESTART",!0).catch(o=>{const a=o;n.error("[update-bridge] graceful shutdown failed:",a),process.exit(42)});return}process.exit(42)},N)}else await p(`===== Bridge \u66F4\u65B0\u5931\u8D25\uFF1A\u9000\u51FA\u7801 ${e.exitCode??"unknown"} =====`),await b({...t,status:"failed",message:`Bridge \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${e.exitCode??"unknown"}`,error:e.stderr||e.stdout}),await U()}catch(e){const s=e;n.error("[update-bridge] background update failed:",s),await p(`===== Bridge \u66F4\u65B0\u5F02\u5E38\uFF1A${s.message} =====`).catch(()=>{}),await b({...t,status:"failed",message:"Bridge \u66F4\u65B0\u5931\u8D25\u3002",error:s.message||"update bridge failed"}),await U()}}async function z(r){const t=await import("node:fs/promises"),e=await import("node:path"),s=await import("node:os"),o=e.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");if(r){await t.mkdir(e.dirname(o),{recursive:!0}),await t.writeFile(o,JSON.stringify({preserveSessions:!0,timestamp:new Date().toISOString()}),"utf-8"),n.info(`[prepare-restart] \u5DF2\u521B\u5EFA\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6: ${o}`);return}try{await t.unlink(o),n.info("[prepare-restart] \u5DF2\u5220\u9664\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6")}catch{}}function ge(r){return H("md5").update(r,"utf8").digest("hex")}function fe(r){const t=r.trim().toLowerCase();if(!/^[a-f0-9]{32}$/.test(t))return!1;const e=Buffer.from(t,"hex");return A().map(s=>s.trim()).filter(Boolean).some(s=>{const o=Buffer.from(ge(s),"hex");return o.length===e.length&&V(o,e)})}function pe(r){const e=A().length>0,s=R();return e?fe(r)?{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!0}}:{status:401,body:{ok:!1,error:"connection_key_mismatch",version:s,connectionKeyMatched:!1,connectionKeyRequired:!0}}:{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!1}}}function me(r,t){const s=t.trim()||"http://localhost:8080",o=/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(?:\/|$)/i.test(s);return{ok:!1,error:r.message||"scheduler ping failed",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:s,hint:o?"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u5728 bridge \u542F\u52A8\u73AF\u5883\u4E2D\u5C06 AWS_RUNTIME_SCHEDULER_BASE_URL \u914D\u7F6E\u4E3A\u8BE5\u5B9E\u4F8B\u53EF\u8BBF\u95EE\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u4F8B\u5982 http://<server-host>:7380\u3002":"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u786E\u8BA4 AWS_RUNTIME_SCHEDULER_BASE_URL \u6307\u5411\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\u53EF\u4ECE bridge \u673A\u5668\u8BBF\u95EE\uFF0C\u5E76\u4E14\u8FD0\u884C\u65F6\u8BBF\u95EE\u4EE4\u724C\u4ECD\u6709\u6548\u3002"}}function he(r){if(typeof r!="object"||r===null)return;const t=Reflect.get(r,"response");if(typeof t!="object"||t===null)return;const e=Reflect.get(t,"status");return typeof e=="number"?e:void 0}async function Se(r,t){try{const e=await _.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":t}});return{schedulerReachable:e.status>=200&&e.status<300,tokenRefreshed:!1}}catch(e){if(he(e)!==401)throw e;const s=await Y(r);if(!s.success||!s.runtimeAccessToken)throw e;const o=await _.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":s.runtimeAccessToken}});return{schedulerReachable:o.status>=200&&o.status<300,tokenRefreshed:!0}}}function we(r){return{ok:!1,error:"ambiguous_scheduler_base_url",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:"",autoRegisterTargetUrls:r,hint:"Multiple autoRegisterTargets.serverUrl values were found, so bridge cannot safely infer which scheduler /runtime/ping should call back. Set AWS_RUNTIME_SCHEDULER_BASE_URL explicitly, or use future schedulerId/token-bound multi-scheduler routing."}}function be(r){return Array.from(new Set([...r,...ce].map(t=>String(t||"").trim().toLowerCase()).filter(Boolean)))}function ye(r,t){const e=r.filter(s=>t[s]?.installed);return e.length===0?"":e.map(s=>{const o=t[s],a=o?.error||o?.executable||"command is still available";return`${s}: ${a}`}).join("; ")}function k(r){return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[t,{installed:!!e?.installed,version:e?.version||null,executable:e?.executable||null,error:e?.error||null}]))}d.get("/check-update",async(r,t)=>{t.json(await q())}),d.get("/update-log",v,async(r,t)=>{try{const e=await oe();t.json({ok:!0,...e})}catch(e){const s=e;n.error("[update-log] Error:",s),t.status(500).json({ok:!1,error:s.message||"read update log failed"})}}),d.get("/healthz",(r,t)=>{t.json({ok:!0,runtimeBridge:"healthy"})}),d.get("/connection-check",(r,t)=>{const e=String(r.header("X-Bridge-Connection-Key-MD5")||""),s=pe(e);t.status(s.status).json(s.body)}),d.get("/ping",S,async(r,t)=>{let e=G(r.header("X-Scheduler-Base-Url"),r.header("X-Bridge-Instance-Id"),r.header("X-Bridge-Instance-Name"));if(e.source==="ambiguous-auto-register-targets"){const o=Q(r.header("X-Runtime-Token"));if(o)e={url:o,source:"token-match",ambiguousTargetUrls:[]};else{const a=r.header("X-Bridge-Instance-Id");if(a){const l=J(),i=Object.entries(l.registrations).find(([,c])=>c===a)?.[0];i&&(e={url:i,source:"instance-id-match",ambiguousTargetUrls:[]})}if(!e.url||e.source==="ambiguous-auto-register-targets"){t.status(400).json(we(e.ambiguousTargetUrls));return}}}const s=e.url;try{const o=ne(s);if(!o){t.status(401).json({ok:!1,error:"runtime_access_token_required"});return}const a=await Se(s,o);t.json({ok:!0,runtimeBridge:"healthy",version:R(),schedulerReachable:a.schedulerReachable,tokenRefreshed:a.tokenRefreshed,schedulerBaseUrl:s,instanceId:e.instanceId,instanceName:e.instanceName})}catch(o){const a=o;t.status(502).json(me(a,s))}}),d.post("/init-instance",S,async(r,t)=>{const{agentId:e,workspacePath:s,skillEnabled:o,mcpEnabled:a,ccSwitchEnabledTools:l,skillPackage:i,skillPackages:c,mcpServers:m}=r.body||{};if(!e||!s){t.status(400).json({error:"agentId and workspacePath are required"});return}try{n.info("[init-instance] Request received",{agentId:String(e),workspacePath:String(s),skillEnabled:o,mcpEnabled:a,ccSwitchEnabledTools:l});const u=await te(e,s,{skillEnabled:o,mcpEnabled:a,ccSwitchEnabledTools:l,skillPackage:i,skillPackages:c,mcpServers:m});n.info("[init-instance] Initialization completed",{agentId:String(e),ok:u.ok,logCount:u.logs?.length||0,enabledTools:u.enabledTools}),t.json(u)}catch(u){const g=u;n.error("[init-instance] Initialization failed",{agentId:String(e),workspacePath:String(s),error:g?.message||String(u),stack:g?.stack||null}),t.status(400).json({error:g?.message||"initialize instance failed"})}}),d.post("/features",S,async(r,t)=>{const{agentId:e,features:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}se(String(e),s),n.info("[instance/features] \u80FD\u529B\u5F00\u5173\u5DF2\u66F4\u65B0",{agentId:String(e),features:C(String(e))}),t.json({ok:!0,agentId:String(e),features:C(String(e))})}),d.post("/cc-switch/state",S,async(r,t)=>{const{agentId:e}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}try{n.info("[cc-switch/state] Request received",{agentId:String(e)});const s=await ee(e),o=await Z(s),l=(await re(e,s)).state,i=be(l.enabledTools||[]);n.info("[cc-switch/state] Detecting SDK statuses",{agentId:String(e),detectionTargets:i});const c=await j(i);n.info("[cc-switch/state] SDK status detection completed",{agentId:String(e),toolStatus:k(c),importedMcpCount:o.mcpServers,importedSkillCount:o.skills}),t.json({ok:!0,agentId:String(e),imported:{mcpServers:o.mcpServers,skills:o.skills},state:{...l,toolStatus:c}})}catch(s){const o=s;n.error("[cc-switch/state] Failed to load state",{agentId:String(e),error:o?.message||String(s),stack:o?.stack||null}),t.status(400).json({error:o?.message||"load state failed"})}}),d.post("/cc-switch/install-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const o=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[];n.info("[cc-switch/install-tools] Request received",{agentId:String(e),requestedTools:o});const a=new Set(E),l=o.filter(i=>a.has(i));if(n.info("[cc-switch/install-tools] Request normalized",{agentId:String(e),requestedTools:o,installableTools:l,supportedInstallableTools:E}),l.length===0){n.warn("[cc-switch/install-tools] No supported installable tools requested",{agentId:String(e),requestedTools:o}),t.status(400).json({error:`tools must include ${E.join(", ")}`});return}try{const i=await P(e),c=Array.from(new Set([...i.enabledTools||[],...l]));n.info("[cc-switch/install-tools] Starting installer",{agentId:String(e),installableTools:l,previousEnabledTools:i.enabledTools||[],nextEnabledTools:c});const m=await ae(l);n.info("[cc-switch/install-tools] Installer completed",{agentId:String(e),toolStatus:k(m)});const u=await j(c);n.info("[cc-switch/install-tools] Refreshed enabled SDK statuses",{agentId:String(e),refreshedToolStatus:k(u)});const g={...u,...m},h=await O(e,{...i,enabledTools:c,toolStatus:g}),y=new Set(l),w=Object.entries(g).filter(([f])=>y.has(f)).filter(([,f])=>!f.installed).map(([f,B])=>({tool:f,error:B.error||null}));w.length>0&&n.warn("[cc-switch/install-tools] Some SDKs remain unavailable",{agentId:String(e),failedTools:w}),n.info("[cc-switch/install-tools] State saved",{agentId:String(e),enabledTools:h.enabledTools,toolStatus:k(g)}),t.json({ok:w.length===0,agentId:String(e),toolStatus:g,failedTools:w,state:h})}catch(i){const c=i;n.error("[cc-switch/install-tools] Install route failed",{agentId:String(e),requestedTools:o,installableTools:l,error:c?.message||String(i),stack:c?.stack||null}),t.status(400).json({error:c?.message||"install tools failed"})}}),d.post("/cc-switch/uninstall-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const o=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[],a=new Set($),l=o.filter(i=>a.has(i));if(l.length===0){t.status(400).json({error:`tools must include ${$.join(", ")}`});return}try{const i=await P(e),c=new Set(l),m=(i.enabledTools||[]).filter(B=>!c.has(String(B||"").trim().toLowerCase())),u=await ie(l),h={...await j([...m,...l]),...u},y=ye(l,h),w=y?i.enabledTools||[]:m,f=await O(e,{...i,enabledTools:w,toolStatus:h});if(y){t.status(400).json({ok:!1,agentId:String(e),error:`uninstall incomplete: ${y}`,toolStatus:h,state:f});return}t.json({ok:!0,agentId:String(e),toolStatus:h,state:f})}catch(i){const c=i;t.status(400).json({error:c?.message||"uninstall tools failed"})}}),d.post("/prepare-restart",S,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{};try{n.info(`[prepare-restart] \u6536\u5230\u51C6\u5907\u91CD\u542F\u901A\u77E5\uFF0CpreserveSessions=${e}`),await z(!!e),t.json({ok:!0,preserveSessions:e,message:e?"Bridge will preserve sessions for aws-mcp-server restart":"Bridge will clean up sessions on shutdown"})}catch(s){const o=s;n.error("[prepare-restart] Error:",o),t.status(500).json({error:o.message})}}),d.post("/update-bridge",v,async(r,t)=>{const e=K(r.body?.requestedByUserId);try{if(!e){t.status(400).json({error:"requestedByUserId is required"});return}const s=await q(),o=L({operationType:"update",requestedByUserId:e,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:D(r),status:"accepted",message:"Bridge \u66F4\u65B0\u8BF7\u6C42\u5DF2\u6536\u5230\uFF0C\u5C06\u5728\u540E\u53F0\u6267\u884C\u5E76\u81EA\u52A8\u91CD\u542F\u3002",latestVersion:s.ok&&s.latestVersion||void 0});await b(o),ue(o),t.json({ok:!0,operationId:o.operationId,message:"Bridge \u66F4\u65B0\u5DF2\u5728\u540E\u53F0\u5F00\u59CB\uFF0C\u66F4\u65B0\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u91CD\u542F\u5B9E\u4F8B Bridge\u3002"})}catch(s){const o=s;n.error("[update-bridge] Error:",o),t.status(500).json({error:o.message||"update bridge failed"})}}),d.post("/restart-bridge",v,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{},s=K(r.body?.requestedByUserId);try{if(!s){t.status(400).json({error:"requestedByUserId is required"});return}n.info(`[restart-bridge] \u6536\u5230\u5B9E\u4F8B Bridge \u91CD\u542F\u8BF7\u6C42\uFF0CpreserveSessions=${e}`);const o=L({operationType:"restart",requestedByUserId:s,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:D(r),status:"accepted",message:"Bridge \u5DF2\u6536\u5230\u91CD\u542F\u8BF7\u6C42\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u53D1\u9001\u786E\u8BA4\u901A\u77E5\u3002"});await b(o),await z(!!e),t.json({ok:!0,operationId:o.operationId,preserveSessions:!!e,message:"Bridge \u6B63\u5728\u91CD\u542F\uFF0C\u670D\u52A1\u7BA1\u7406\u5668\u4F1A\u81EA\u52A8\u62C9\u8D77\u5B9E\u4F8B\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002"}),n.info("[restart-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),M(),setTimeout(()=>{if(process.exitCode=42,T){T("PANEL_RESTART",!!e).catch(a=>{const l=a;n.error("[restart-bridge] graceful shutdown failed:",l),process.exit(42)});return}process.exit(42)},N)}catch(o){const a=o;n.error("[restart-bridge] Error:",a),t.status(500).json({error:a.message||"restart bridge failed"})}});export{we as buildAmbiguousSchedulerBaseUrlResponse,pe as buildConnectionCheckResponse,me as buildSchedulerPingFailureResponse,be as buildToolStatusDetectionTargets,ye as buildUninstallFailureMessage,d as instanceRouter,De as setGracefulShutdownFn,k as summarizeToolStatus};
|
|
1
|
+
import{Router as H}from"express";import E from"axios";import{spawn as _}from"node:child_process";import{createHash as W,timingSafeEqual as G}from"node:crypto";import{fileURLToPath as V}from"node:url";import k from"node:path";import{getBridgeVersion as R,resolveSchedulerBaseUrlFrom as J}from"../config.js";import{validateToken as S,validateTokenOrPanelSession as v}from"../middleware/auth.js";import{getConfiguredConnectionKeys as A,getRegistrationState as Y,requestRuntimeAccessTokenRefreshForServer as Q,resolveSchedulerBaseUrlFromToken as Z}from"../services/auto-register.js";import{discoverCcSwitchConfiguredItems as ee,loadCcSwitchSdk as te}from"../services/cc-switch-sdk.js";import{initInstance as re,syncLegacyStateFromSdk as se}from"../services/instance-init-service.js";import{setAgentFeatures as oe,getAgentFeatures as x}from"../services/instance-features.js";import{createBridgeLifecycleOperation as L,deliverPendingBridgeLifecycleNotifications as C,loadLifecycleState as ne,removeBridgeLifecycleOperation as ae,upsertBridgeLifecycleOperation as b}from"../services/lifecycle-state.js";import{appendUpdateLog as p,readUpdateLog as ie}from"../services/update-log.js";import{checkBridgeUpdate as O}from"../utils/npm-version.js";import{loadInstanceState as $,saveInstanceState as P}from"../services/instance-state.js";import{getRuntimeAccessToken as ce}from"../services/runtime-binding.js";import{detectToolStatuses as U,ensureToolsInstalled as le,SUPPORTED_INSTALLABLE_TOOLS as j,SUPPORTED_UNINSTALLABLE_TOOLS as q,uninstallTools as de}from"../services/tool-installer.js";import{createLogger as ue}from"../utils/logger.js";const a=ue("instance"),ge=["acode","claude","opencode","codex"];let I=null;function Xe(r){I=r}const d=H();function D(r){return String(r||"").trim()||void 0}function F(r){const t=r.get("host");if(t)return`${r.protocol}://${t}`}function N(){const r=k.resolve(k.dirname(V(import.meta.url)),"..","..");return k.join(r,"dist","index.js")}function fe(){return new Promise((r,t)=>{const e=_(process.execPath,[N(),"update","--no-kill"],{shell:!1,windowsHide:!0});let s="",o="";e.stdout.on("data",n=>{s+=n.toString("utf8")}),e.stderr.on("data",n=>{o+=n.toString("utf8")}),e.on("error",t),e.on("close",n=>{r({exitCode:n,stdout:s,stderr:o})})})}function K(){const r=_(process.execPath,[N(),"service","restart"],{shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});r.stdout.on("data",t=>{p(String(t)).catch(()=>{})}),r.stderr.on("data",t=>{p(String(t)).catch(()=>{})})}const M=3e3;async function pe(r){const t={...r,status:"running",message:"Bridge \u66F4\u65B0\u6B63\u5728\u540E\u53F0\u6267\u884C\u3002"};await b(t);try{a.info("[update-bridge] \u6B63\u5728\u540E\u53F0\u6267\u884C awsb update --yes"),await p(`===== Bridge \u66F4\u65B0\u5F00\u59CB\uFF1Av${R()} -> latest\uFF08\u64CD\u4F5C ${r.operationId}\uFF09 =====`);const e=await fe();if(e.stdout&&await p(e.stdout),e.stderr&&await p(e.stderr),e.exitCode===0){await p("===== Bridge \u66F4\u65B0\u6210\u529F\uFF0C\u51C6\u5907\u81EA\u52A8\u91CD\u542F ===== ");const s={...t,status:"restarting",message:"Bridge \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u91CD\u542F..."};await b(s),a.info("[update-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),K(),setTimeout(()=>{if(process.exitCode=42,I){I("BRIDGE_UPDATE_RESTART",!0).catch(o=>{const n=o;a.error("[update-bridge] graceful shutdown failed:",n),process.exit(42)});return}process.exit(42)},M)}else await p(`===== Bridge \u66F4\u65B0\u5931\u8D25\uFF1A\u9000\u51FA\u7801 ${e.exitCode??"unknown"} =====`),await b({...t,status:"failed",message:`Bridge \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${e.exitCode??"unknown"}`,error:e.stderr||e.stdout}),await C()}catch(e){const s=e;a.error("[update-bridge] background update failed:",s),await p(`===== Bridge \u66F4\u65B0\u5F02\u5E38\uFF1A${s.message} =====`).catch(()=>{}),await b({...t,status:"failed",message:"Bridge \u66F4\u65B0\u5931\u8D25\u3002",error:s.message||"update bridge failed"}),await C()}}async function z(r){const t=await import("node:fs/promises"),e=await import("node:path"),s=await import("node:os"),o=e.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");if(r){await t.mkdir(e.dirname(o),{recursive:!0}),await t.writeFile(o,JSON.stringify({preserveSessions:!0,timestamp:new Date().toISOString()}),"utf-8"),a.info(`[prepare-restart] \u5DF2\u521B\u5EFA\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6: ${o}`);return}try{await t.unlink(o),a.info("[prepare-restart] \u5DF2\u5220\u9664\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6")}catch{}}function me(r){return W("md5").update(r,"utf8").digest("hex")}function he(r){const t=r.trim().toLowerCase();if(!/^[a-f0-9]{32}$/.test(t))return!1;const e=Buffer.from(t,"hex");return A().map(s=>s.trim()).filter(Boolean).some(s=>{const o=Buffer.from(me(s),"hex");return o.length===e.length&&G(o,e)})}function Se(r){const e=A().length>0,s=R();return e?he(r)?{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!0}}:{status:401,body:{ok:!1,error:"connection_key_mismatch",version:s,connectionKeyMatched:!1,connectionKeyRequired:!0}}:{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!1}}}function we(r,t){const s=t.trim()||"http://localhost:8080",o=/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(?:\/|$)/i.test(s);return{ok:!1,error:r.message||"scheduler ping failed",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:s,hint:o?"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u5728 bridge \u542F\u52A8\u73AF\u5883\u4E2D\u5C06 AWS_RUNTIME_SCHEDULER_BASE_URL \u914D\u7F6E\u4E3A\u8BE5\u5B9E\u4F8B\u53EF\u8BBF\u95EE\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u4F8B\u5982 http://<server-host>:7380\u3002":"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u786E\u8BA4 AWS_RUNTIME_SCHEDULER_BASE_URL \u6307\u5411\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\u53EF\u4ECE bridge \u673A\u5668\u8BBF\u95EE\uFF0C\u5E76\u4E14\u8FD0\u884C\u65F6\u8BBF\u95EE\u4EE4\u724C\u4ECD\u6709\u6548\u3002"}}function be(r){if(typeof r!="object"||r===null)return;const t=Reflect.get(r,"response");if(typeof t!="object"||t===null)return;const e=Reflect.get(t,"status");return typeof e=="number"?e:void 0}async function ye(r,t){try{const e=await E.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":t}});return{schedulerReachable:e.status>=200&&e.status<300,tokenRefreshed:!1}}catch(e){if(be(e)!==401)throw e;const s=await Q(r);if(!s.success||!s.runtimeAccessToken)throw e;const o=await E.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":s.runtimeAccessToken}});return{schedulerReachable:o.status>=200&&o.status<300,tokenRefreshed:!0}}}function Ie(r){return{ok:!1,error:"ambiguous_scheduler_base_url",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:"",autoRegisterTargetUrls:r,hint:"Multiple autoRegisterTargets.serverUrl values were found, so bridge cannot safely infer which scheduler /runtime/ping should call back. Set AWS_RUNTIME_SCHEDULER_BASE_URL explicitly, or use future schedulerId/token-bound multi-scheduler routing."}}function Be(r){return Array.from(new Set([...r,...ge].map(t=>String(t||"").trim().toLowerCase()).filter(Boolean)))}function Te(r,t){const e=r.filter(s=>t[s]?.installed);return e.length===0?"":e.map(s=>{const o=t[s],n=o?.error||o?.executable||"command is still available";return`${s}: ${n}`}).join("; ")}function B(r){return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[t,{installed:!!e?.installed,version:e?.version||null,executable:e?.executable||null,error:e?.error||null}]))}d.get("/check-update",async(r,t)=>{t.json(await O())}),d.get("/update-log",v,async(r,t)=>{try{const e=await ie();t.json({ok:!0,...e})}catch(e){const s=e;a.error("[update-log] Error:",s),t.status(500).json({ok:!1,error:s.message||"read update log failed"})}}),d.get("/healthz",(r,t)=>{t.json({ok:!0,runtimeBridge:"healthy"})}),d.get("/connection-check",(r,t)=>{const e=String(r.header("X-Bridge-Connection-Key-MD5")||""),s=Se(e);t.status(s.status).json(s.body)}),d.get("/ping",S,async(r,t)=>{let e=J(r.header("X-Scheduler-Base-Url"),r.header("X-Bridge-Instance-Id"),r.header("X-Bridge-Instance-Name"));if(e.source==="ambiguous-auto-register-targets"){const o=Z(r.header("X-Runtime-Token"));if(o)e={url:o,source:"token-match",ambiguousTargetUrls:[]};else{const n=r.header("X-Bridge-Instance-Id");if(n){const c=Y(),i=Object.entries(c.registrations).find(([,l])=>l===n)?.[0];i&&(e={url:i,source:"instance-id-match",ambiguousTargetUrls:[]})}if(!e.url||e.source==="ambiguous-auto-register-targets"){t.status(400).json(Ie(e.ambiguousTargetUrls));return}}}const s=e.url;try{const o=ce(s);if(!o){t.status(401).json({ok:!1,error:"runtime_access_token_required"});return}const n=await ye(s,o);t.json({ok:!0,runtimeBridge:"healthy",version:R(),schedulerReachable:n.schedulerReachable,tokenRefreshed:n.tokenRefreshed,schedulerBaseUrl:s,instanceId:e.instanceId,instanceName:e.instanceName})}catch(o){const n=o;t.status(502).json(we(n,s))}}),d.post("/init-instance",S,async(r,t)=>{const{agentId:e,workspacePath:s,skillEnabled:o,mcpEnabled:n,ccSwitchEnabledTools:c,skillPackage:i,skillPackages:l,mcpServers:m}=r.body||{};if(!e||!s){t.status(400).json({error:"agentId and workspacePath are required"});return}try{a.info("[init-instance] Request received",{agentId:String(e),workspacePath:String(s),skillEnabled:o,mcpEnabled:n,ccSwitchEnabledTools:c});const u=await re(e,s,{skillEnabled:o,mcpEnabled:n,ccSwitchEnabledTools:c,skillPackage:i,skillPackages:l,mcpServers:m});a.info("[init-instance] Initialization completed",{agentId:String(e),ok:u.ok,logCount:u.logs?.length||0,enabledTools:u.enabledTools}),t.json(u)}catch(u){const g=u;a.error("[init-instance] Initialization failed",{agentId:String(e),workspacePath:String(s),error:g?.message||String(u),stack:g?.stack||null}),t.status(400).json({error:g?.message||"initialize instance failed"})}}),d.post("/features",S,async(r,t)=>{const{agentId:e,features:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}oe(String(e),s),a.info("[instance/features] \u80FD\u529B\u5F00\u5173\u5DF2\u66F4\u65B0",{agentId:String(e),features:x(String(e))}),t.json({ok:!0,agentId:String(e),features:x(String(e))})}),d.post("/cc-switch/state",S,async(r,t)=>{const{agentId:e}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}try{a.info("[cc-switch/state] Request received",{agentId:String(e)});const s=await te(e),o=await ee(s),c=(await se(e,s)).state,i=Be(c.enabledTools||[]);a.info("[cc-switch/state] Detecting SDK statuses",{agentId:String(e),detectionTargets:i});const l=await U(i);a.info("[cc-switch/state] SDK status detection completed",{agentId:String(e),toolStatus:B(l),importedMcpCount:o.mcpServers,importedSkillCount:o.skills}),t.json({ok:!0,agentId:String(e),imported:{mcpServers:o.mcpServers,skills:o.skills},state:{...c,toolStatus:l}})}catch(s){const o=s;a.error("[cc-switch/state] Failed to load state",{agentId:String(e),error:o?.message||String(s),stack:o?.stack||null}),t.status(400).json({error:o?.message||"load state failed"})}}),d.post("/cc-switch/install-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const o=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[];a.info("[cc-switch/install-tools] Request received",{agentId:String(e),requestedTools:o});const n=new Set(j),c=o.filter(i=>n.has(i));if(a.info("[cc-switch/install-tools] Request normalized",{agentId:String(e),requestedTools:o,installableTools:c,supportedInstallableTools:j}),c.length===0){a.warn("[cc-switch/install-tools] No supported installable tools requested",{agentId:String(e),requestedTools:o}),t.status(400).json({error:`tools must include ${j.join(", ")}`});return}try{const i=await $(e),l=Array.from(new Set([...i.enabledTools||[],...c]));a.info("[cc-switch/install-tools] Starting installer",{agentId:String(e),installableTools:c,previousEnabledTools:i.enabledTools||[],nextEnabledTools:l});const m=await le(c);a.info("[cc-switch/install-tools] Installer completed",{agentId:String(e),toolStatus:B(m)});const u=await U(l);a.info("[cc-switch/install-tools] Refreshed enabled SDK statuses",{agentId:String(e),refreshedToolStatus:B(u)});const g={...u,...m},h=await P(e,{...i,enabledTools:l,toolStatus:g}),y=new Set(c),w=Object.entries(g).filter(([f])=>y.has(f)).filter(([,f])=>!f.installed).map(([f,T])=>({tool:f,error:T.error||null}));w.length>0&&a.warn("[cc-switch/install-tools] Some SDKs remain unavailable",{agentId:String(e),failedTools:w}),a.info("[cc-switch/install-tools] State saved",{agentId:String(e),enabledTools:h.enabledTools,toolStatus:B(g)}),t.json({ok:w.length===0,agentId:String(e),toolStatus:g,failedTools:w,state:h})}catch(i){const l=i;a.error("[cc-switch/install-tools] Install route failed",{agentId:String(e),requestedTools:o,installableTools:c,error:l?.message||String(i),stack:l?.stack||null}),t.status(400).json({error:l?.message||"install tools failed"})}}),d.post("/cc-switch/uninstall-tools",S,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const o=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[],n=new Set(q),c=o.filter(i=>n.has(i));if(c.length===0){t.status(400).json({error:`tools must include ${q.join(", ")}`});return}try{const i=await $(e),l=new Set(c),m=(i.enabledTools||[]).filter(T=>!l.has(String(T||"").trim().toLowerCase())),u=await de(c),h={...await U([...m,...c]),...u},y=Te(c,h),w=y?i.enabledTools||[]:m,f=await P(e,{...i,enabledTools:w,toolStatus:h});if(y){t.status(400).json({ok:!1,agentId:String(e),error:`uninstall incomplete: ${y}`,toolStatus:h,state:f});return}t.json({ok:!0,agentId:String(e),toolStatus:h,state:f})}catch(i){const l=i;t.status(400).json({error:l?.message||"uninstall tools failed"})}}),d.post("/prepare-restart",S,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{};try{a.info(`[prepare-restart] \u6536\u5230\u51C6\u5907\u91CD\u542F\u901A\u77E5\uFF0CpreserveSessions=${e}`),await z(!!e),t.json({ok:!0,preserveSessions:e,message:e?"Bridge will preserve sessions for aws-mcp-server restart":"Bridge will clean up sessions on shutdown"})}catch(s){const o=s;a.error("[prepare-restart] Error:",o),t.status(500).json({error:o.message})}});const ke={update:15*60*1e3,restart:5*60*1e3};async function Re(r){try{const t=await ne(),e=Date.now(),s=ke[r],o=[];for(const n of t.operations){if(n.operationType!==r||!["accepted","running","restarting"].includes(n.status))continue;const c=new Date(n.updatedAt).getTime();if(!Number.isNaN(c)&&e-c<=s)return{operationId:n.operationId};o.push(n.operationId)}for(const n of o)try{await ae(n),a.info(`[${r}-bridge] \u5DF2\u6E05\u7406\u8D85\u65F6\u672A\u4EA4\u4ED8\u7684\u9648\u65E7\u64CD\u4F5C: ${n}`)}catch(c){const i=c;a.warn(`[${r}-bridge] \u6E05\u7406\u9648\u65E7\u64CD\u4F5C\u5931\u8D25\uFF0C\u7EE7\u7EED\u653E\u884C: ${n}, ${i.message}`)}return null}catch(t){const e=t;return a.warn(`[${r}-bridge] \u5728\u9014\u64CD\u4F5C\u68C0\u67E5\u5931\u8D25\uFF0C\u653E\u884C\u8BF7\u6C42: ${e.message}`),null}}async function X(r,t,e){const s=await Re(r);return s?(e.status(409).json({error:t,operationId:s.operationId}),!0):!1}d.post("/update-bridge",v,async(r,t)=>{const e=D(r.body?.requestedByUserId);try{if(!e){t.status(400).json({error:"requestedByUserId is required"});return}if(await X("update","\u5B9E\u4F8B Bridge \u66F4\u65B0\u6B63\u5728\u6267\u884C\u4E2D\uFF0C\u8BF7\u52FF\u91CD\u590D\u89E6\u53D1",t))return;const s=await O(),o=L({operationType:"update",requestedByUserId:e,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:F(r),status:"accepted",message:"Bridge \u66F4\u65B0\u8BF7\u6C42\u5DF2\u6536\u5230\uFF0C\u5C06\u5728\u540E\u53F0\u6267\u884C\u5E76\u81EA\u52A8\u91CD\u542F\u3002",latestVersion:s.ok&&s.latestVersion||void 0});await b(o),pe(o),t.json({ok:!0,operationId:o.operationId,message:"Bridge \u66F4\u65B0\u5DF2\u5728\u540E\u53F0\u5F00\u59CB\uFF0C\u66F4\u65B0\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u91CD\u542F\u5B9E\u4F8B Bridge\u3002"})}catch(s){const o=s;a.error("[update-bridge] Error:",o),t.status(500).json({error:o.message||"update bridge failed"})}}),d.post("/restart-bridge",v,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{},s=D(r.body?.requestedByUserId);try{if(!s){t.status(400).json({error:"requestedByUserId is required"});return}if(await X("restart","\u5B9E\u4F8B Bridge \u6B63\u5728\u91CD\u542F\u4E2D\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5",t))return;a.info(`[restart-bridge] \u6536\u5230\u5B9E\u4F8B Bridge \u91CD\u542F\u8BF7\u6C42\uFF0CpreserveSessions=${e}`);const o=L({operationType:"restart",requestedByUserId:s,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:F(r),status:"accepted",message:"Bridge \u5DF2\u6536\u5230\u91CD\u542F\u8BF7\u6C42\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u53D1\u9001\u786E\u8BA4\u901A\u77E5\u3002"});await b(o),await z(!!e),t.json({ok:!0,operationId:o.operationId,preserveSessions:!!e,message:"Bridge \u6B63\u5728\u91CD\u542F\uFF0C\u670D\u52A1\u7BA1\u7406\u5668\u4F1A\u81EA\u52A8\u62C9\u8D77\u5B9E\u4F8B\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002"}),a.info("[restart-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),K(),setTimeout(()=>{if(process.exitCode=42,I){I("PANEL_RESTART",!!e).catch(n=>{const c=n;a.error("[restart-bridge] graceful shutdown failed:",c),process.exit(42)});return}process.exit(42)},M)}catch(o){const n=o;a.error("[restart-bridge] Error:",n),t.status(500).json({error:n.message||"restart bridge failed"})}});export{Ie as buildAmbiguousSchedulerBaseUrlResponse,Se as buildConnectionCheckResponse,we as buildSchedulerPingFailureResponse,Be as buildToolStatusDetectionTargets,Te as buildUninstallFailureMessage,d as instanceRouter,Xe as setGracefulShutdownFn,B as summarizeToolStatus};
|
|
2
2
|
|
|
@@ -14,6 +14,10 @@ export interface BridgeLifecycleOperation {
|
|
|
14
14
|
|
|
15
15
|
latestVersion?: string;
|
|
16
16
|
}
|
|
17
|
+
interface BridgeLifecycleState {
|
|
18
|
+
operations: BridgeLifecycleOperation[];
|
|
19
|
+
}
|
|
20
|
+
export declare function loadLifecycleState(): Promise<BridgeLifecycleState>;
|
|
17
21
|
export declare function createBridgeLifecycleOperation(input: {
|
|
18
22
|
operationType: BridgeLifecycleOperationType;
|
|
19
23
|
requestedByUserId: string;
|
|
@@ -27,3 +31,4 @@ export declare function upsertBridgeLifecycleOperation(operation: BridgeLifecycl
|
|
|
27
31
|
export declare function removeBridgeLifecycleOperation(operationId: string): Promise<void>;
|
|
28
32
|
|
|
29
33
|
export declare function deliverPendingBridgeLifecycleNotifications(): Promise<void>;
|
|
34
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import s from"node:fs/promises";import
|
|
2
|
-
`,{encoding:"utf8",mode:384})}function
|
|
1
|
+
import s from"node:fs/promises";import o from"node:path";import{randomUUID as p}from"node:crypto";import g from"axios";import{getRuntimeHomeDir as y,resolveSchedulerBaseUrlFrom as c}from"../config.js";import{getRuntimeAccessToken as B,loadRuntimeBinding as w,normalizeSchedulerBaseUrl as n}from"./runtime-binding.js";import{createLogger as U}from"../utils/logger.js";const d=U("bridge-lifecycle");function l(){return o.join(y(),".aws-bridge","lifecycle-state.json")}function u(e){return String(e||"").trim()||void 0}async function a(){try{const e=await s.readFile(l(),"utf8"),t=JSON.parse(e);return{operations:Array.isArray(t.operations)?t.operations:[]}}catch{return{operations:[]}}}async function f(e){const t=l();await s.mkdir(o.dirname(t),{recursive:!0}),await s.writeFile(t,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384})}function D(e){const t=new Date().toISOString(),r=n(e.schedulerBaseUrl)||c(void 0).url;return{operationId:p(),operationType:e.operationType,status:e.status||"accepted",requestedByUserId:e.requestedByUserId,requestedAt:t,updatedAt:t,schedulerBaseUrl:r,runtimeBridgeBaseUrl:u(e.runtimeBridgeBaseUrl),message:e.message,latestVersion:u(e.latestVersion)}}async function V(e){const r=(await a()).operations.filter(i=>i.operationId!==e.operationId);r.push({...e,updatedAt:new Date().toISOString()}),await f({operations:r})}async function I(e){const t=await a();await f({operations:t.operations.filter(r=>r.operationId!==e)})}function h(e){return["succeeded","failed","startup-confirmed"].includes(e.status)}async function S(e){const t=n(e.schedulerBaseUrl)||n(w().schedulerBaseUrl)||c(void 0).url,r=B(t);if(!r)throw new Error("runtime access token is missing");await g.post(`${t}/api/runtime/callback/bridge-lifecycle`,{operationId:e.operationId,operationType:e.operationType,status:e.status,requestedByUserId:e.requestedByUserId,message:e.message,error:e.error,runtimeBridgeBaseUrl:e.runtimeBridgeBaseUrl,latestVersion:e.latestVersion},{headers:{"X-Runtime-Token":r}})}function v(e){return e.operationType==="update"&&(e.status==="accepted"||e.status==="running")?{...e,status:"failed",message:"Bridge \u66F4\u65B0\u4E2D\u65AD\uFF0C\u8FDB\u7A0B\u5DF2\u91CD\u542F\uFF0C\u8BF7\u91CD\u65B0\u53D1\u8D77\u66F4\u65B0\u3002",error:e.error||"update interrupted by process restart"}:{...e,status:"startup-confirmed",message:e.operationType==="update"?e.latestVersion?`\u66F4\u65B0\u5E76\u91CD\u542F\u5B8C\u6210\uFF0C\u5F53\u524D\u7248\u672C v${e.latestVersion}\u3002`:"\u66F4\u65B0\u5E76\u91CD\u542F\u5B8C\u6210\u3002":"Bridge \u91CD\u542F\u5B8C\u6210\uFF0C\u5B9E\u4F8B\u5DF2\u6062\u590D\u8FD0\u884C\u3002"}}async function $(){const e=await a();for(const t of e.operations){const r=v(t);if(h(r))try{await S(r),await I(t.operationId),d.info(`[bridge-lifecycle] notification delivered: ${t.operationId}`)}catch(i){const m=i;d.warn(`[bridge-lifecycle] notification delivery failed: ${t.operationId}, ${m.message}`)}}}export{D as createBridgeLifecycleOperation,$ as deliverPendingBridgeLifecycleNotifications,a as loadLifecycleState,I as removeBridgeLifecycleOperation,V as upsertBridgeLifecycleOperation};
|
|
3
3
|
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import{EventEmitter as Je}from"node:events";import K from"node:fs";import He from"node:os";import Z from"node:path";import{Client as Ke}from"@modelcontextprotocol/sdk/client/index.js";import{SSEClientTransport as Ge}from"@modelcontextprotocol/sdk/client/sse.js";import{StdioClientTransport as Qe}from"@modelcontextprotocol/sdk/client/stdio.js";import{StreamableHTTPClientTransport as Ye}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{ToolListChangedNotificationSchema as Ve}from"@modelcontextprotocol/sdk/types.js";import{createBuiltInFileTools as Xe}from"./built-in-file-tools.js";import{ASK_USER_QUESTION_TOOL_NAME as Ze,createAskUserQuestionTool as et}from"./ask-user-question-tool.js";import{createSkillTool as tt}from"./skill-tool.js";import{createTodoWriteTool as ot,createTodoReadTool as nt}from"./todo-tool.js";import{createSubAgentTools as st}from"./sub-agent-tools.js";import{createAwaitCompleteTool as it}from"./await-complete-tool.js";import{cleanupAcodeCmdDist as rt}from"./builtins/commandArtifacts.js";import{backgroundCommandManager as G}from"./background-command-manager.js";import{createTerminalTool as at}from"./terminal-tool.js";import{loadAcodeCommands as ct}from"./commands/commandLoader.js";import{ACodeCommandRegistry as ge}from"./commands/commandRegistry.js";import{calculateCompactionThreshold as ut,COMPACTION_LLM_PROMPT_TEMPLATE as lt,conversationSizeKB as Q,estimateConversationTokens as ee,performCompaction as ye,pruneToolOutputs as mt,summarizeConversation as Te}from"./compaction.js";import{loadAcodeConfig as dt,loadAcodeSkillGroup as pt}from"./config.js";import{ACodeHookBus as ht,findDeniedHookResult as re}from"./hooks/hookBus.js";import{isMcpPollingToolName as ft,getSemanticToolName as Ce}from"./mcpToolNames.js";function gt(o){return o.type==="text"?{type:"text",text:o.text}:{type:"image_url",image_url:{url:o.image_url.url}}}function yt(o){return{id:o.id,type:"function",function:{name:o.function.name,arguments:o.function.arguments}}}function Tt(o){const e={role:o.role,content:Array.isArray(o.content)?o.content.map(gt):o.content};return o.tool_call_id&&(e.tool_call_id=o.tool_call_id),o.tool_calls&&(e.tool_calls=o.tool_calls.map(yt)),e}function ae(o){return o.map(Tt)}function Ct(o,e){const t=O(o.prompt_cache_hit_tokens),n=O(o.prompt_cache_miss_tokens);if(t!==void 0||n!==void 0)return{cacheHitTokens:t??0,cacheMissTokens:n??0};const s=O(o.cache_read_input_tokens),a=O(o.cache_creation_input_tokens);if(s!==void 0||a!==void 0){const u=O(o.input_tokens)??0;return{cacheHitTokens:s??0,cacheMissTokens:(a??0)+u}}const i=O(o.cache_hit_tokens),c=O(o.cache_miss_tokens);if(i!==void 0||c!==void 0)return{cacheHitTokens:i??0,cacheMissTokens:c??0};const r=O(o.prompt_tokens_details?.cached_tokens);if(r!==void 0){const u=e!==void 0?Math.max(0,e-r):0;return{cacheHitTokens:r,cacheMissTokens:u}}}function O(o){const e=typeof o=="number"?o:typeof o=="string"&&o.trim()?Number(o):NaN;if(!(!Number.isFinite(e)||e<0))return e}function _t(o,e){if(!o)return e;const t=n=>{const s=e[n],a=o[n];return n==="prompt_tokens_details"?{...a,...s}:s??a};return{prompt_tokens:t("prompt_tokens"),completion_tokens:t("completion_tokens"),input_tokens:t("input_tokens"),output_tokens:t("output_tokens"),prompt_cache_hit_tokens:t("prompt_cache_hit_tokens"),prompt_cache_miss_tokens:t("prompt_cache_miss_tokens"),cache_read_input_tokens:t("cache_read_input_tokens"),cache_creation_input_tokens:t("cache_creation_input_tokens"),cache_hit_tokens:t("cache_hit_tokens"),cache_miss_tokens:t("cache_miss_tokens"),prompt_tokens_details:t("prompt_tokens_details")}}function At(){return new Date().toISOString()}function E(o){return!!(o&&typeof o=="object"&&!Array.isArray(o))}function _e(o){return E(o)}function Ae(o,e){return e.map(t=>({serverName:o,toolName:t.name,kind:"mcp",exposedName:lo(o,t.name),description:t.description,inputSchema:_e(t.inputSchema)?t.inputSchema:{type:"object",properties:{}}}))}function I(o){return String(o??"").trim()||void 0}const te=6e4,we=10*6e4,$e=2e4,be=3e4,ke=3,ce=-1,wt=1e3,$t=3e4,oe=6e4,Me=0,bt=2147483647,kt=new Set(["create_memory","read_memory","update_memory","delete_memory","search_memory","list_memories","get_memory_stats","list_memories_by_file","memory_recurrence","memory_recurrence_complete"]),Mt=new Set(["write_file","edit_file","delete_file"]),St=10*6e4+2e3,Rt=1e4,Se=5e3,Re=10*60*1e3,ve=10*1024*1024,xe=800*1024,ne=process.env.ACODE_COMPACTION_DEBUG==="1",Ee=ne?Z.join(He.tmpdir(),"acode-compaction-debug.log"):"";function l(...o){ne&&console.log(...o)}const vt=process.env.ACODE_TOKEN_DEBUG==="1";function j(...o){vt&&console.log("[TokenDebug][Runtime]",...o)}const xt=64e3;function ue(o){const e=o.contextLimit&&o.contextLimit>0?o.contextLimit:xt,t=o.maxOutput&&o.maxOutput>0?o.maxOutput:8192,n=o.inputLimit&&o.inputLimit>0?o.inputLimit:void 0,s=ut(e,t,n);return{contextLimit:e,maxOutput:t,inputLimit:n,threshold:s}}async function N(o,e,t,n){if(e<=0)return await o;let s;try{return await new Promise((a,i)=>{s=setTimeout(()=>{n?.(),i(new Error(`${t} timed out after ${e}ms`))},e),o.then(a,i)})}finally{s&&clearTimeout(s)}}async function Y(o,e,t,n=Rt){try{await N(e,n,o)}catch(s){t(s instanceof Error?s.message:String(s))}}function W(o,e){if(o===0||o==="0")return 0;const t=typeof o=="number"?o:Number(o);return t===ce?ce:Number.isFinite(t)&&t>0?Math.floor(t):e}function z(o,e){const t=W(o,e);return t>0?t:e}const Ne=3;function le(o){return o?Math.ceil(o.length/Ne):0}function Et(o,e){let t=o.length;for(const n of e){const s=(n.kind==="mcp",n.toolName);if(t+=s.length,n.kind==="mcp")n.description&&(t+=n.description.length),t+=JSON.stringify(n.inputSchema).length;else{const a=n;a.inputSchema&&(t+=JSON.stringify(a.inputSchema).length)}}return Math.ceil(t/Ne)}function Ie(o){if(o.length===0)return 0;const e=JSON.stringify(ze(o));return le(e)}async function Nt(o){await new Promise(e=>setTimeout(e,o))}function It(o){return typeof o=="boolean"}function Ot(o,e,t){if(It(t?.supportsImageInput))return t.supportsImageInput;const n=E(e)?e.models:void 0,s=E(n)?n[o]:void 0;if(!E(s))return!1;const a=s.modalities;return!E(a)||!Array.isArray(a.input)?!1:a.input.some(i=>String(i||"").trim().toLowerCase()==="image")}function me(o,e,t,n){if(o!==void 0)return o;if(!e)return;const s=e.models;if(E(s)){const a=s[t];if(E(a)){const i=a[n];if(i!==void 0)return i}}return e[n]}function Dt(o,e){const t=e.provider?.default,n=t?.options,s=o.model||t?.model||e.model||"acode-default",a=I(o.effort)||I(n?.effort)||I(t?.effort)||void 0;return{model:s,baseURL:I(n?.baseURL||n?.baseUrl),apiKey:I(n?.apiKey),effort:a,systemPrompt:o.systemPrompt||e.systemPrompt,stream:n?.stream!==!1,requestTimeoutMs:z(n?.requestTimeoutMs??n?.timeoutMs,te),requestTotalTimeoutMs:z(n?.requestTotalTimeoutMs,we),requestRetryCount:W(n?.requestRetryCount,ke),requestFirstByteTimeoutMs:Math.min(z(n?.requestFirstByteTimeoutMs,$e),z(n?.requestTimeoutMs??n?.timeoutMs,te)),requestStreamIdleTimeoutMs:Math.min(z(n?.requestStreamIdleTimeoutMs,be),z(n?.requestTimeoutMs??n?.timeoutMs,te)),mcpToolTimeoutMs:z(n?.mcpToolTimeoutMs,oe),mcpPollingToolTimeoutMs:W(n?.mcpPollingToolTimeoutMs,Me),supportsImageInput:Ot(s,t,n),contextLimit:W(me(n?.contextLimit,t,s,"contextLimit"),0)||void 0,inputLimit:W(me(n?.inputLimit,t,s,"inputLimit"),0)||void 0,maxOutput:W(me(n?.maxOutput,t,s,"maxOutput"),0)||void 0}}function B(o,e){const t=e.replace(/\s+/g," ").trim();t&&o.push({type:"text",text:t})}function Lt(o,e){const t=e.trim();return/^data:image\/[a-z0-9.+-]+;base64,/i.test(t)?t:`data:${o||"image/png"};base64,${t}`}function Pt(o){return/^data:image\/[a-z0-9.+-]+;base64,/i.test(o.trim())}function Ut(o){let e;try{e=new URL(o)}catch{throw new Error(`ACode image URL is invalid: ${o}`)}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new Error(`ACode image URL protocol is not supported: ${e.protocol}`)}async function Bt(o){const e=await o.arrayBuffer();if(e.byteLength>ve)throw new Error(`ACode image is too large to inline: ${e.byteLength} bytes exceeds ${ve} bytes`);return Buffer.from(e).toString("base64")}async function qt(o){const e=o.trim();if(Pt(e))return e;Ut(e);const t=await fetch(e);if(!t.ok)throw new Error(`ACode image fetch failed: ${t.status} ${await t.text()}`);const n=t.headers.get("content-type")||"",s=n.split(";")[0]?.trim().toLowerCase()||"";if(!s.startsWith("image/"))throw new Error(`ACode image URL did not return an image content type: ${n||"unknown"}`);return`data:${s};base64,${await Bt(t)}`}function zt(o){const e=I(o.base64Data??o.base64??o.data);return e?Lt(I(o.mediaType??o.mimeType)||"image/png",e):I(o.url??o.serverUrl)}function Oe(o,e,t,n){return`[Attached file: ${[o?`name=${o}`:void 0,e?`url=${e}`:void 0,t?`mediaType=${t}`:void 0,n?`size=${n}`:void 0].filter(a=>!!a).join(", ")}]`}function Ft(o){const e=/\[(图片|文件)\s*:\s*([^\]\n]*?)\s*\]/gi,t=[];let n=!1,s=0,a=e.exec(o);for(;a!==null;){B(t,o.slice(s,a.index));const i=a[1],c=(a[2]||"").trim();if(i==="\u56FE\u7247"&&c)t.push({type:"image_url",image_url:{url:c}}),n=!0;else if(i==="\u6587\u4EF6"&&c){const[r="",u="",m="",g=""]=c.split("|").map(y=>y.trim());B(t,Oe(r,u,m,g))}else B(t,a[0]);s=a.index+a[0].length,a=e.exec(o)}return t.length===0?{content:o,hasImages:!1}:(B(t,o.slice(s)),{content:t,hasImages:n})}function jt(o,e){let t=!1;const n=[e.directMessages,e.groupMessages];for(const s of n)if(Array.isArray(s))for(const a of s){if(!E(a))continue;const i=I(a.content);if(!i)continue;const c=I(a.senderName);B(o,c?`Message from ${c}:`:"Message:");const r=de(i);Array.isArray(r.content)?o.push(...r.content):B(o,r.content),t=t||r.hasImages}return t}function Wt(o){let e;try{e=JSON.parse(o)}catch{return}if(!E(e))return;const t=[];let n=jt(t,e);const s=I(e.text)||"",a=Array.isArray(e.attachments)?e.attachments:[];B(t,s);for(const i of a){if(!E(i))continue;const c=i;if(String(c.type??c.kind??"").toLowerCase()==="image"){const m=zt(c);m&&(t.push({type:"image_url",image_url:{url:m}}),n=!0);continue}const u=I(c.url??c.serverUrl)||"";B(t,Oe(I(c.name)||"",u,I(c.mediaType??c.mimeType)||"",I(c.size)||""))}return t.length===0?{content:o,hasImages:!1}:{content:t,hasImages:n}}function de(o){return Wt(o)??Ft(o)}async function De(o){if(!Array.isArray(o))return o;const e=[];for(const t of o){if(t.type==="text"){e.push(t);continue}e.push({type:"image_url",image_url:{url:await qt(t.image_url.url)}})}return e}function Jt(o){return typeof o=="string"?o:Array.isArray(o)?o.map(e=>e.type==="text"?e.text:"[Image]").join(" ").trim():""}function Ht(o,e){const t=o.permissions??e.permissions??{},n=o.permissions?[]:e.allowedTools??[],s=o.permissions?[]:e.deniedTools??[];return{mode:t.mode??"default",allow:[...t.allow??[],...n].map(a=>a.trim()).filter(Boolean),deny:[...t.deny??[],...s].map(a=>a.trim()).filter(Boolean)}}function Kt(o,e){return o==="*"?!0:o.endsWith("*")?e.startsWith(o.slice(0,-1)):o===e}function Gt(o){return o.kind==="builtin"?[o.exposedName,o.toolName,`builtin.${o.toolName}`]:[o.exposedName,o.toolName,`${o.serverName}.${o.toolName}`,`mcp.${o.serverName}.${o.toolName}`]}function Le(o,e){return Gt(e).some(t=>Kt(o,t))}function Pe(o,e,t){if(e.length>0&&!e.some(n=>Le(n,o)))throw new Error(t)}function pe(o,e,t){if(e.mode!=="bypassPermissions"){if(e.deny.some(n=>Le(n,o)))throw new Error(`ACode tool denied by permission rule: ${o.exposedName}`);Pe(o,e.allow,`ACode tool is not in allowed permission rules: ${o.exposedName}`),Pe(o,t??[],`ACode slash command does not allow tool: ${o.exposedName}`)}}function Qt(o,e,t){try{return pe(o,e,t),!0}catch{return!1}}function se(o){const e=o.replace(/\/+$/g,"");return/\/chat\/completions$/i.test(e)?e:`${e}/chat/completions`}function Yt(o){try{const e=new URL(o);e.search="",e.hash="";const t=new URL(se(e.toString()));return`${t.origin}${t.pathname}`}catch{return se(o).replace(/([?&](?:api[_-]?key|token|authorization)=)[^&]+/gi,"$1[redacted]")}}function Vt(o){const e=[...o].reverse().find(n=>n.role==="user"),t=Jt(e?.content??null).trim();if(t)return t.length>300?`${t.slice(0,300)}...`:t}function Ue(o){try{const e=JSON.parse(o);return _e(e)?e:{}}catch{return{}}}function Xt(o,e,t){if(new Set([Ze,"await_complete"]).has(o.toolName))return 0;if(o.toolName!=="Bash")return t;const s=e.timeout_ms??e.timeoutMs,a=typeof s=="number"?s:typeof s=="string"?Number(s):NaN;return!Number.isFinite(a)||a<=0?Math.max(t,St):Math.min(Math.floor(a),10*6e4)+2e3}function Zt(o,e){const t=o.trim();return`ACode \u5DF2\u63A5\u6536\u4EFB\u52A1\u3002\u5F53\u524D\u5DF2\u52A0\u8F7D ${e.configMcpCount} \u4E2A MCP \u914D\u7F6E\u3001${e.exposedTools.length} \u4E2A MCP Tool \u4E0E ${e.skills.length} \u4E2A Skill\u3002${t?`\u8F93\u5165\u6458\u8981\uFF1A${t.slice(0,200)}`:""}`}function D(o){if(!E(o))return String(o??"");const e=o.content;if(Array.isArray(e)){const t=e.map(n=>E(n)?typeof n.text=="string"?n.text:JSON.stringify(n):"").filter(Boolean);if(t.length>0)return t.join(`
|
|
1
|
+
import{EventEmitter as Je}from"node:events";import K from"node:fs";import He from"node:os";import Z from"node:path";import{Client as Ke}from"@modelcontextprotocol/sdk/client/index.js";import{SSEClientTransport as Ge}from"@modelcontextprotocol/sdk/client/sse.js";import{StdioClientTransport as Qe}from"@modelcontextprotocol/sdk/client/stdio.js";import{StreamableHTTPClientTransport as Ye}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{ToolListChangedNotificationSchema as Ve}from"@modelcontextprotocol/sdk/types.js";import{createBuiltInFileTools as Xe}from"./built-in-file-tools.js";import{ASK_USER_QUESTION_TOOL_NAME as Ze,createAskUserQuestionTool as et}from"./ask-user-question-tool.js";import{createSkillTool as tt}from"./skill-tool.js";import{createTodoWriteTool as ot,createTodoReadTool as nt}from"./todo-tool.js";import{createSubAgentTools as st}from"./sub-agent-tools.js";import{createAwaitCompleteTool as it}from"./await-complete-tool.js";import{cleanupAcodeCmdDist as rt}from"./builtins/commandArtifacts.js";import{backgroundCommandManager as G}from"./background-command-manager.js";import{createTerminalTool as at}from"./terminal-tool.js";import{loadAcodeCommands as ct}from"./commands/commandLoader.js";import{ACodeCommandRegistry as ge}from"./commands/commandRegistry.js";import{calculateCompactionThreshold as ut,COMPACTION_LLM_PROMPT_TEMPLATE as lt,conversationSizeKB as Q,estimateConversationTokens as ee,performCompaction as ye,pruneToolOutputs as mt,summarizeConversation as Te}from"./compaction.js";import{loadAcodeConfig as dt,loadAcodeSkillGroup as pt}from"./config.js";import{ACodeHookBus as ht,findDeniedHookResult as re}from"./hooks/hookBus.js";import{isMcpPollingToolName as ft,getSemanticToolName as Ce}from"./mcpToolNames.js";function gt(o){return o.type==="text"?{type:"text",text:o.text}:{type:"image_url",image_url:{url:o.image_url.url}}}function yt(o){return{id:o.id,type:"function",function:{name:o.function.name,arguments:o.function.arguments}}}function Tt(o){const e={role:o.role,content:Array.isArray(o.content)?o.content.map(gt):o.content};return o.tool_call_id&&(e.tool_call_id=o.tool_call_id),o.tool_calls&&(e.tool_calls=o.tool_calls.map(yt)),e}function ae(o){return o.map(Tt)}function Ct(o,e){const t=L(o.prompt_cache_hit_tokens),n=L(o.prompt_cache_miss_tokens);if(t!==void 0||n!==void 0)return{cacheHitTokens:t??0,cacheMissTokens:n??0};const s=L(o.cache_read_input_tokens),a=L(o.cache_creation_input_tokens);if(s!==void 0||a!==void 0){const u=L(o.input_tokens)??0;return{cacheHitTokens:s??0,cacheMissTokens:(a??0)+u}}const i=L(o.cache_hit_tokens),c=L(o.cache_miss_tokens);if(i!==void 0||c!==void 0)return{cacheHitTokens:i??0,cacheMissTokens:c??0};const r=L(o.prompt_tokens_details?.cached_tokens);if(r!==void 0){const u=e!==void 0?Math.max(0,e-r):0;return{cacheHitTokens:r,cacheMissTokens:u}}}function L(o){const e=typeof o=="number"?o:typeof o=="string"&&o.trim()?Number(o):NaN;if(!(!Number.isFinite(e)||e<0))return e}function _t(o,e){if(!o)return e;const t=n=>{const s=e[n],a=o[n];return n==="prompt_tokens_details"?{...a,...s}:s??a};return{prompt_tokens:t("prompt_tokens"),completion_tokens:t("completion_tokens"),input_tokens:t("input_tokens"),output_tokens:t("output_tokens"),prompt_cache_hit_tokens:t("prompt_cache_hit_tokens"),prompt_cache_miss_tokens:t("prompt_cache_miss_tokens"),cache_read_input_tokens:t("cache_read_input_tokens"),cache_creation_input_tokens:t("cache_creation_input_tokens"),cache_hit_tokens:t("cache_hit_tokens"),cache_miss_tokens:t("cache_miss_tokens"),prompt_tokens_details:t("prompt_tokens_details")}}function At(){return new Date().toISOString()}function E(o){return!!(o&&typeof o=="object"&&!Array.isArray(o))}function _e(o){return E(o)}function Ae(o,e){return e.map(t=>({serverName:o,toolName:t.name,kind:"mcp",exposedName:lo(o,t.name),description:t.description,inputSchema:_e(t.inputSchema)?t.inputSchema:{type:"object",properties:{}}}))}function I(o){return String(o??"").trim()||void 0}const te=6e4,we=10*6e4,$e=2e4,be=3e4,ke=3,ce=-1,wt=1e3,$t=3e4,oe=6e4,Me=0,bt=2147483647,kt=new Set(["create_memory","read_memory","update_memory","delete_memory","search_memory","list_memories","get_memory_stats","list_memories_by_file","memory_recurrence","memory_recurrence_complete"]),Mt=new Set(["write_file","edit_file","delete_file"]),St=10*6e4+2e3,Rt=1e4,Se=5e3,Re=10*60*1e3,ve=10*1024*1024,xe=800*1024,ne=process.env.ACODE_COMPACTION_DEBUG==="1",Ee=ne?Z.join(He.tmpdir(),"acode-compaction-debug.log"):"";function l(...o){ne&&console.log(...o)}const vt=process.env.ACODE_TOKEN_DEBUG==="1";function j(...o){vt&&console.log("[TokenDebug][Runtime]",...o)}const xt=64e3;function ue(o){const e=o.contextLimit&&o.contextLimit>0?o.contextLimit:xt,t=o.maxOutput&&o.maxOutput>0?o.maxOutput:8192,n=o.inputLimit&&o.inputLimit>0?o.inputLimit:void 0,s=ut(e,t,n);return{contextLimit:e,maxOutput:t,inputLimit:n,threshold:s}}async function N(o,e,t,n){if(e<=0)return await o;let s;try{return await new Promise((a,i)=>{s=setTimeout(()=>{n?.(),i(new Error(`${t} timed out after ${e}ms`))},e),o.then(a,i)})}finally{s&&clearTimeout(s)}}async function Y(o,e,t,n=Rt){try{await N(e,n,o)}catch(s){t(s instanceof Error?s.message:String(s))}}function W(o,e){if(o===0||o==="0")return 0;const t=typeof o=="number"?o:Number(o);return t===ce?ce:Number.isFinite(t)&&t>0?Math.floor(t):e}function z(o,e){const t=W(o,e);return t>0?t:e}const Ne=3;function le(o){return o?Math.ceil(o.length/Ne):0}function Et(o,e){let t=o.length;for(const n of e){const s=(n.kind==="mcp",n.toolName);if(t+=s.length,n.kind==="mcp")n.description&&(t+=n.description.length),t+=JSON.stringify(n.inputSchema).length;else{const a=n;a.inputSchema&&(t+=JSON.stringify(a.inputSchema).length)}}return Math.ceil(t/Ne)}function Ie(o){if(o.length===0)return 0;const e=JSON.stringify(ze(o));return le(e)}async function Nt(o){await new Promise(e=>setTimeout(e,o))}function It(o){return typeof o=="boolean"}function Lt(o,e,t){if(It(t?.supportsImageInput))return t.supportsImageInput;const n=E(e)?e.models:void 0,s=E(n)?n[o]:void 0;if(!E(s))return!1;const a=s.modalities;return!E(a)||!Array.isArray(a.input)?!1:a.input.some(i=>String(i||"").trim().toLowerCase()==="image")}function me(o,e,t,n){if(o!==void 0)return o;if(!e)return;const s=e.models;if(E(s)){const a=s[t];if(E(a)){const i=a[n];if(i!==void 0)return i}}return e[n]}function Ot(o,e){const t=e.provider?.default,n=t?.options,s=o.model||t?.model||e.model||"acode-default",a=I(o.effort)||I(n?.effort)||I(t?.effort)||void 0;return{model:s,baseURL:I(n?.baseURL||n?.baseUrl),apiKey:I(n?.apiKey),effort:a,systemPrompt:o.systemPrompt||e.systemPrompt,stream:n?.stream!==!1,requestTimeoutMs:z(n?.requestTimeoutMs??n?.timeoutMs,te),requestTotalTimeoutMs:z(n?.requestTotalTimeoutMs,we),requestRetryCount:W(n?.requestRetryCount,ke),requestFirstByteTimeoutMs:Math.min(z(n?.requestFirstByteTimeoutMs,$e),z(n?.requestTimeoutMs??n?.timeoutMs,te)),requestStreamIdleTimeoutMs:Math.min(z(n?.requestStreamIdleTimeoutMs,be),z(n?.requestTimeoutMs??n?.timeoutMs,te)),mcpToolTimeoutMs:z(n?.mcpToolTimeoutMs,oe),mcpPollingToolTimeoutMs:W(n?.mcpPollingToolTimeoutMs,Me),supportsImageInput:Lt(s,t,n),contextLimit:W(me(n?.contextLimit,t,s,"contextLimit"),0)||void 0,inputLimit:W(me(n?.inputLimit,t,s,"inputLimit"),0)||void 0,maxOutput:W(me(n?.maxOutput,t,s,"maxOutput"),0)||void 0}}function B(o,e){const t=e.replace(/\s+/g," ").trim();t&&o.push({type:"text",text:t})}function Dt(o,e){const t=e.trim();return/^data:image\/[a-z0-9.+-]+;base64,/i.test(t)?t:`data:${o||"image/png"};base64,${t}`}function Pt(o){return/^data:image\/[a-z0-9.+-]+;base64,/i.test(o.trim())}function Ut(o){let e;try{e=new URL(o)}catch{throw new Error(`ACode image URL is invalid: ${o}`)}if(e.protocol!=="http:"&&e.protocol!=="https:")throw new Error(`ACode image URL protocol is not supported: ${e.protocol}`)}async function Bt(o){const e=await o.arrayBuffer();if(e.byteLength>ve)throw new Error(`ACode image is too large to inline: ${e.byteLength} bytes exceeds ${ve} bytes`);return Buffer.from(e).toString("base64")}async function qt(o){const e=o.trim();if(Pt(e))return e;Ut(e);const t=await fetch(e);if(!t.ok)throw new Error(`ACode image fetch failed: ${t.status} ${await t.text()}`);const n=t.headers.get("content-type")||"",s=n.split(";")[0]?.trim().toLowerCase()||"";if(!s.startsWith("image/"))throw new Error(`ACode image URL did not return an image content type: ${n||"unknown"}`);return`data:${s};base64,${await Bt(t)}`}function zt(o){const e=I(o.base64Data??o.base64??o.data);return e?Dt(I(o.mediaType??o.mimeType)||"image/png",e):I(o.url??o.serverUrl)}function Le(o,e,t,n){return`[Attached file: ${[o?`name=${o}`:void 0,e?`url=${e}`:void 0,t?`mediaType=${t}`:void 0,n?`size=${n}`:void 0].filter(a=>!!a).join(", ")}]`}function Ft(o){const e=/\[(图片|文件)\s*:\s*([^\]\n]*?)\s*\]/gi,t=[];let n=!1,s=0,a=e.exec(o);for(;a!==null;){B(t,o.slice(s,a.index));const i=a[1],c=(a[2]||"").trim();if(i==="\u56FE\u7247"&&c)t.push({type:"image_url",image_url:{url:c}}),n=!0;else if(i==="\u6587\u4EF6"&&c){const[r="",u="",m="",g=""]=c.split("|").map(y=>y.trim());B(t,Le(r,u,m,g))}else B(t,a[0]);s=a.index+a[0].length,a=e.exec(o)}return t.length===0?{content:o,hasImages:!1}:(B(t,o.slice(s)),{content:t,hasImages:n})}function jt(o,e){let t=!1;const n=[e.directMessages,e.groupMessages];for(const s of n)if(Array.isArray(s))for(const a of s){if(!E(a))continue;const i=I(a.content);if(!i)continue;const c=I(a.senderName);B(o,c?`Message from ${c}:`:"Message:");const r=de(i);Array.isArray(r.content)?o.push(...r.content):B(o,r.content),t=t||r.hasImages}return t}function Wt(o){let e;try{e=JSON.parse(o)}catch{return}if(!E(e))return;const t=[];let n=jt(t,e);const s=I(e.text)||"",a=Array.isArray(e.attachments)?e.attachments:[];B(t,s);for(const i of a){if(!E(i))continue;const c=i;if(String(c.type??c.kind??"").toLowerCase()==="image"){const m=zt(c);m&&(t.push({type:"image_url",image_url:{url:m}}),n=!0);continue}const u=I(c.url??c.serverUrl)||"";B(t,Le(I(c.name)||"",u,I(c.mediaType??c.mimeType)||"",I(c.size)||""))}return t.length===0?{content:o,hasImages:!1}:{content:t,hasImages:n}}function de(o){return Wt(o)??Ft(o)}async function Oe(o){if(!Array.isArray(o))return o;const e=[];for(const t of o){if(t.type==="text"){e.push(t);continue}e.push({type:"image_url",image_url:{url:await qt(t.image_url.url)}})}return e}function Jt(o){return typeof o=="string"?o:Array.isArray(o)?o.map(e=>e.type==="text"?e.text:"[Image]").join(" ").trim():""}function Ht(o,e){const t=o.permissions??e.permissions??{},n=o.permissions?[]:e.allowedTools??[],s=o.permissions?[]:e.deniedTools??[];return{mode:t.mode??"default",allow:[...t.allow??[],...n].map(a=>a.trim()).filter(Boolean),deny:[...t.deny??[],...s].map(a=>a.trim()).filter(Boolean)}}function Kt(o,e){return o==="*"?!0:o.endsWith("*")?e.startsWith(o.slice(0,-1)):o===e}function Gt(o){return o.kind==="builtin"?[o.exposedName,o.toolName,`builtin.${o.toolName}`]:[o.exposedName,o.toolName,`${o.serverName}.${o.toolName}`,`mcp.${o.serverName}.${o.toolName}`]}function De(o,e){return Gt(e).some(t=>Kt(o,t))}function Pe(o,e,t){if(e.length>0&&!e.some(n=>De(n,o)))throw new Error(t)}function pe(o,e,t){if(e.mode!=="bypassPermissions"){if(e.deny.some(n=>De(n,o)))throw new Error(`ACode tool denied by permission rule: ${o.exposedName}`);Pe(o,e.allow,`ACode tool is not in allowed permission rules: ${o.exposedName}`),Pe(o,t??[],`ACode slash command does not allow tool: ${o.exposedName}`)}}function Qt(o,e,t){try{return pe(o,e,t),!0}catch{return!1}}function se(o){const e=o.replace(/\/+$/g,"");return/\/chat\/completions$/i.test(e)?e:`${e}/chat/completions`}function Yt(o){try{const e=new URL(o);e.search="",e.hash="";const t=new URL(se(e.toString()));return`${t.origin}${t.pathname}`}catch{return se(o).replace(/([?&](?:api[_-]?key|token|authorization)=)[^&]+/gi,"$1[redacted]")}}function Vt(o){const e=[...o].reverse().find(n=>n.role==="user"),t=Jt(e?.content??null).trim();if(t)return t.length>300?`${t.slice(0,300)}...`:t}function Ue(o){try{const e=JSON.parse(o);return _e(e)?e:{}}catch{return{}}}function Xt(o,e,t){if(new Set([Ze,"await_complete"]).has(o.toolName))return 0;if(o.toolName!=="Bash")return t;const s=e.timeout_ms??e.timeoutMs,a=typeof s=="number"?s:typeof s=="string"?Number(s):NaN;return!Number.isFinite(a)||a<=0?Math.max(t,St):Math.min(Math.floor(a),10*6e4)+2e3}function Zt(o,e){const t=o.trim();return`ACode \u5DF2\u63A5\u6536\u4EFB\u52A1\u3002\u5F53\u524D\u5DF2\u52A0\u8F7D ${e.configMcpCount} \u4E2A MCP \u914D\u7F6E\u3001${e.exposedTools.length} \u4E2A MCP Tool \u4E0E ${e.skills.length} \u4E2A Skill\u3002${t?`\u8F93\u5165\u6458\u8981\uFF1A${t.slice(0,200)}`:""}`}function O(o){if(!E(o))return String(o??"");const e=o.content;if(Array.isArray(e)){const t=e.map(n=>E(n)?typeof n.text=="string"?n.text:JSON.stringify(n):"").filter(Boolean);if(t.length>0)return t.join(`
|
|
2
2
|
`)}return JSON.stringify(o)}function V(o){return o instanceof Error?o.message:String(o)}function eo(o){let e=16;if(typeof o.content=="string")e+=o.content.length*2;else if(Array.isArray(o.content))for(const t of o.content)typeof t=="object"&&t!==null&&(e+=JSON.stringify(t).length);return o.tool_calls&&(e+=JSON.stringify(o.tool_calls).length),o.tool_call_id&&(e+=o.tool_call_id.length),e}async function to(o,e,t,n,s){let a=0;for(const c of o)a+=eo(c);if(a<=e)return 0;const i=await ye(o,t,n,{llmSummarizer:s});return i.strategy!=="none"?i.prunedToolMessages+i.summarizedMessages:Te(o,{llmSummarizer:s,contextLimit:t})}function Be(o){const e=V(o).toLowerCase();return e.includes("context_length_exceeded")||e.includes("context length")||e.includes("maximum context")||e.includes("max context")||e.includes("token limit")||e.includes("tokens exceed")||e.includes("too many tokens")||e.includes("input is too long")||e.includes("prompt is too long")||e.includes("request too large")||e.includes("413")}function oo(o){const e=V(o).toLowerCase();return Be(o)?!1:e.includes("fetch failed")||e.includes("timed out")||e.includes("timeout")||e.includes("econnreset")||e.includes("econnrefused")||e.includes("etimedout")||e.includes("enotfound")||e.includes("socket hang up")||e.includes("network error")||/\b(408|429|500|502|503|504|520|521|522|523|524)\b/.test(e)}function no(o){if(typeof o!="object"||o===null)return!1;const e=Reflect.get(o,"code"),t=V(o).toLowerCase();return e===-32001||t.includes("request timed out")}function so(o){if(typeof o=="object"&&o!==null){const e=Reflect.get(o,"code");if(typeof e=="string"&&e)return e;const t=Reflect.get(o,"name");if(typeof t=="string"&&t)return t}return"tool_error"}function io(o,e){return JSON.stringify({ok:!1,tool:o.exposedName,error:{type:so(e),message:V(e)}})}function qe(o){const e=JSON.stringify(o);return e.length>500?`${e.slice(0,500)}\u2026`:e}function ro(o){const e=[o?.reasoning_content,o?.reasoning_text,o?.reasoning];for(const t of e){if(typeof t=="string"&&t)return t;if(E(t)){const n=t.text??t.content;if(typeof n=="string"&&n)return n}}return""}function ao(o){return o.length===0?"":["Skills are available for this session.","Use the `skill` tool to load specialized skill instructions when a user task matches one of the available skills listed in the tool description.","Do not attempt to follow skill instructions from memory \u2014 always invoke the `skill` tool first to load the full content."].join(" ")}function co(o){return o.length===0?"":["Available slash commands:",...o.map(e=>`- /${e.name}${e.description?`: ${e.description}`:""}`),"When the user invokes a slash command, ACode expands it before model execution."].join(`
|
|
3
3
|
`)}function uo(o){if(!o||typeof o!="object")return"";const e=o,t=[],n=e.displayName,s=e.roleName,a=e.instanceName,i=e.projectId,c=e.workspacePath,r=e.runtimeMode,u=e.describe;return typeof n=="string"&&n.trim()&&t.push(`Display Name: ${n.trim()}`),typeof s=="string"&&s.trim()&&t.push(`Role: ${s.trim()}`),typeof a=="string"&&a.trim()&&t.push(`Instance: ${a.trim()}`),typeof i=="string"&&i.trim()&&t.push(`Project: ${i.trim()}`),typeof c=="string"&&c.trim()&&t.push(`Workspace: ${c.trim()}`),typeof r=="string"&&r.trim()&&t.push(`Runtime Mode: ${r.trim()}`),typeof u=="string"&&u.trim()&&t.push(`Description: ${u.trim()}`),t.length===0?"":["[User Profile]",...t].join(`
|
|
4
|
-
`)}function ze(o){return o.map(e=>({type:"function",function:{name:e.exposedName,description:e.description||e.toolName,parameters:e.inputSchema}}))}function lo(o,e){return`${o}__${e}`.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,64)||"mcp_tool"}function mo(o){const e={"Content-Type":"application/json"};return o.apiKey&&(e.Authorization=`Bearer ${o.apiKey}`),e}function po(o){return Object.fromEntries(Object.entries(o).map(([e,t])=>[e,ho(e)?fo(t):t]))}function ho(o){const e=o.trim().toLowerCase();return e==="authorization"||e==="api-key"||e==="x-api-key"||e==="openai-api-key"}function fo(o){return o.trim().toLowerCase().startsWith("bearer ")?"Bearer ***":"***"}function go(o){return o.split(/\r?\n/).filter(e=>e.startsWith("data:")).map(e=>e.slice(5).trim()).filter(Boolean)}function yo(o,e){const t=Number.isFinite(e.index)?Number(e.index):0,n=o.get(t)??{id:e.id||`call-${t}`,type:"function",function:{name:"",arguments:""}};e.id&&(n.id=e.id),e.function?.name&&(n.function.name+=e.function.name),e.function?.arguments&&(n.function.arguments+=e.function.arguments),o.set(t,n)}function To(o){return[...o.entries()].sort(([e],[t])=>e-t).map(([,e])=>e).filter(e=>e.function.name.trim().length>0)}class J extends Je{constructor(e){super(),this.config=e,this.initialConversation=[],this.state={started:!1,terminated:!1,configMcpCount:0,skills:[],provider:{model:"acode-default",stream:!1,requestTimeoutMs:te,requestTotalTimeoutMs:we,requestRetryCount:ke,requestFirstByteTimeoutMs:$e,requestStreamIdleTimeoutMs:be,mcpToolTimeoutMs:oe,mcpPollingToolTimeoutMs:Me,supportsImageInput:!1},permissions:{mode:"default",allow:[],deny:[]},mcpConnections:[],ownsMcpConnections:!0,exposedTools:[],commands:[],commandRegistry:new ge([]),conversation:[],accumulatedInputTokens:0,accumulatedOutputTokens:0,accumulatedCacheHitTokens:0,accumulatedCacheMissTokens:0},this.abortController=null,this.exposedToolsInitialized=!1,this.pendingMemorySubAgents=new Map,this.memoryDelegateCooldownUntil=new Map,this.submitQueue=Promise.resolve(),this.activeTurn=!1,this.forceMessagePending=!1,this.abortRequested=!1,this.toolSettleSlot=null,this.toolSettleSeq=0,this.pendingAwaitedSubIds=new Set,this.pendingAwaitedBgTaskIds=new Set,this.sessionCompleteEmitted=!1,this.contextVersion=0,this.stopWaiters=new Set,this.compactionDebugTimer=null,this.skillTool=null,this.skillWatchers=[],this.skillRefreshTimer=null,this.questionWaiters=new Map,this.hookBus=new ht({sessionId:e.sessionId,onError:(t,n)=>{this.emitAcodeEvent("error",{message:`ACode hook failed: ${t.name}: ${n instanceof Error?n.message:String(n)}`})}});for(const t of e.hooks??[])this.hookBus.register(t)}getAvailableTools(){return this.state.exposedTools.map(e=>e.exposedName)}emitAcodeEvent(e,t){const n={type:e,sessionId:this.config.sessionId,timestamp:At(),data:t};this.emit("event",n)}emitCompactionPhase(e,t,n,s){this.emitAcodeEvent("context_compaction",{action:"phase",compactionId:e,parentId:e,phase:t,phaseLabel:n,phaseDetail:s})}handleCompactionPhase(e,t){switch(t.phase){case"prune_start":this.emitCompactionPhase(e,"prune","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA",`\u6B63\u5728\u88C1\u526A ${t.messageCount??0} \u6761\u6D88\u606F\u4E2D\u7684\u5DE5\u5177\u8F93\u51FA...`);break;case"prune_end":this.emitCompactionPhase(e,"prune_done","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA",`\u88C1\u526A\u4E86 ${t.prunedCount??0} \u6761\u5DE5\u5177\u8F93\u51FA, \u91CA\u653E ${t.freedTokens??0} tokens`);break;case"summarize_start":this.emitCompactionPhase(e,"summarize","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD",`\u6B63\u5728\u6458\u8981 ${t.messageCount??0} \u6761\u65E9\u671F\u5BF9\u8BDD...`);break;case"summarize_end":this.emitCompactionPhase(e,"summarize_done","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD",`\u6458\u8981\u5316\u4E86 ${t.summarizedCount??0} \u6761\u6D88\u606F${t.usedLLM?" (LLM \u6458\u8981)":" (\u89C4\u5219\u6458\u8981)"}`);break}}async start(){this.emitAcodeEvent("startup_phase",{phase:"loading_config"});const e=await dt(this.config.configPath);this.emitAcodeEvent("startup_phase",{phase:"loading_skills"});const{skills:t,skillGroups:n,watchedDirs:s}=await this.loadAllSkills(),a=
|
|
4
|
+
`)}function ze(o){return o.map(e=>({type:"function",function:{name:e.exposedName,description:e.description||e.toolName,parameters:e.inputSchema}}))}function lo(o,e){return`${o}__${e}`.replace(/[^a-zA-Z0-9_-]/g,"_").slice(0,64)||"mcp_tool"}function mo(o){const e={"Content-Type":"application/json"};return o.apiKey&&(e.Authorization=`Bearer ${o.apiKey}`),e}function po(o){return Object.fromEntries(Object.entries(o).map(([e,t])=>[e,ho(e)?fo(t):t]))}function ho(o){const e=o.trim().toLowerCase();return e==="authorization"||e==="api-key"||e==="x-api-key"||e==="openai-api-key"}function fo(o){return o.trim().toLowerCase().startsWith("bearer ")?"Bearer ***":"***"}function go(o){return o.split(/\r?\n/).filter(e=>e.startsWith("data:")).map(e=>e.slice(5).trim()).filter(Boolean)}function yo(o,e){const t=Number.isFinite(e.index)?Number(e.index):0,n=o.get(t)??{id:e.id||`call-${t}`,type:"function",function:{name:"",arguments:""}};e.id&&(n.id=e.id),e.function?.name&&(n.function.name+=e.function.name),e.function?.arguments&&(n.function.arguments+=e.function.arguments),o.set(t,n)}function To(o){return[...o.entries()].sort(([e],[t])=>e-t).map(([,e])=>e).filter(e=>e.function.name.trim().length>0)}class J extends Je{constructor(e){super(),this.config=e,this.initialConversation=[],this.state={started:!1,terminated:!1,configMcpCount:0,skills:[],provider:{model:"acode-default",stream:!1,requestTimeoutMs:te,requestTotalTimeoutMs:we,requestRetryCount:ke,requestFirstByteTimeoutMs:$e,requestStreamIdleTimeoutMs:be,mcpToolTimeoutMs:oe,mcpPollingToolTimeoutMs:Me,supportsImageInput:!1},permissions:{mode:"default",allow:[],deny:[]},mcpConnections:[],ownsMcpConnections:!0,exposedTools:[],commands:[],commandRegistry:new ge([]),conversation:[],accumulatedInputTokens:0,accumulatedOutputTokens:0,accumulatedCacheHitTokens:0,accumulatedCacheMissTokens:0},this.abortController=null,this.exposedToolsInitialized=!1,this.pendingMemorySubAgents=new Map,this.memoryDelegateCooldownUntil=new Map,this.submitQueue=Promise.resolve(),this.activeTurn=!1,this.forceMessagePending=!1,this.abortRequested=!1,this.toolSettleSlot=null,this.toolSettleSeq=0,this.pendingAwaitedSubIds=new Set,this.pendingAwaitedBgTaskIds=new Set,this.sessionCompleteEmitted=!1,this.contextVersion=0,this.stopWaiters=new Set,this.compactionDebugTimer=null,this.skillTool=null,this.skillWatchers=[],this.skillRefreshTimer=null,this.questionWaiters=new Map,this.hookBus=new ht({sessionId:e.sessionId,onError:(t,n)=>{this.emitAcodeEvent("error",{message:`ACode hook failed: ${t.name}: ${n instanceof Error?n.message:String(n)}`})}});for(const t of e.hooks??[])this.hookBus.register(t)}getAvailableTools(){return this.state.exposedTools.map(e=>e.exposedName)}emitAcodeEvent(e,t){const n={type:e,sessionId:this.config.sessionId,timestamp:At(),data:t};this.emit("event",n)}emitCompactionPhase(e,t,n,s){this.emitAcodeEvent("context_compaction",{action:"phase",compactionId:e,parentId:e,phase:t,phaseLabel:n,phaseDetail:s})}handleCompactionPhase(e,t){switch(t.phase){case"prune_start":this.emitCompactionPhase(e,"prune","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA",`\u6B63\u5728\u88C1\u526A ${t.messageCount??0} \u6761\u6D88\u606F\u4E2D\u7684\u5DE5\u5177\u8F93\u51FA...`);break;case"prune_end":this.emitCompactionPhase(e,"prune_done","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA",`\u88C1\u526A\u4E86 ${t.prunedCount??0} \u6761\u5DE5\u5177\u8F93\u51FA, \u91CA\u653E ${t.freedTokens??0} tokens`);break;case"summarize_start":this.emitCompactionPhase(e,"summarize","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD",`\u6B63\u5728\u6458\u8981 ${t.messageCount??0} \u6761\u65E9\u671F\u5BF9\u8BDD...`);break;case"summarize_end":this.emitCompactionPhase(e,"summarize_done","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD",`\u6458\u8981\u5316\u4E86 ${t.summarizedCount??0} \u6761\u6D88\u606F${t.usedLLM?" (LLM \u6458\u8981)":" (\u89C4\u5219\u6458\u8981)"}`);break}}async start(){this.emitAcodeEvent("startup_phase",{phase:"loading_config"});const e=await dt(this.config.configPath);this.emitAcodeEvent("startup_phase",{phase:"loading_skills"});const{skills:t,skillGroups:n,watchedDirs:s}=await this.loadAllSkills(),a=Ot(this.config,e);l(`[ACodeRuntime] \u2605\u2605\u2605 DEBUG \u2605\u2605\u2605 resolveProviderConfig \u7ED3\u679C: config.model=${this.config.model??"(undefined)"}, resolvedModel=${a.model}, baseURL=${a.baseURL??"(none)"}, apiKey=${a.apiKey?"[set]":"(none)"}, configPath=${this.config.configPath??"(none)"}`);const i=Ht(this.config,e),c=this.config.sharedMcpConnections?this.filterSharedMcpConnectionsForSubAgent(this.config.sharedMcpConnections):await this.connectMcpServers(e.mcpServers??{},a.mcpToolTimeoutMs),r=!this.config.sharedMcpConnections;this.emitAcodeEvent("startup_phase",{phase:"fetching_profile"});const u=await this.fetchProfilePrompt(c),m=this.config.agentId||"unknown";rt(m),G.reconcileOrphans(m);const g=Xe(this.config.workingDirectory,m,this.config.onFileChange,{manager:G,emitCompleted:h=>{this.emitAcodeEvent("background_command_completed",h)}}),y=et(this.buildAskUserQuestionDeps(),()=>this.contextVersion),$=tt(t);this.skillTool=$;const p=ot(m),b=nt(m),d=this.config.subAgentManager?st(this.buildSubAgentToolDeps(this.config.subAgentManager)):[],f=it(this.buildAwaitCompleteDeps()),_=this.config.terminalManager?at(m,{terminalManager:this.config.terminalManager}):null,w=[...g,y,$,p,b,...d,f,..._?[_]:[],...c.flatMap(h=>h.tools),...this.config.extraBuiltInTools??[]],v=this.config.subagentType==="explore"?w.filter(h=>!Mt.has(h.exposedName)):w,C=this.resolveCommandDirs(e);this.emitAcodeEvent("startup_phase",{phase:"loading_commands"});const k=await ct(C),R=new ge(k),T=ao(t),A=co(k),x=[a.systemPrompt,T,A].map(h=>h?.trim()).filter(h=>!!h).join(`
|
|
5
5
|
|
|
6
6
|
`),S=h=>h?.trim()?[{role:"system",content:h.trim()}]:[],M=[this.config.rolePrompt,u].map(h=>h?.trim()).filter(h=>!!h).join(`
|
|
7
7
|
|
|
8
8
|
`),P=[...S(this.config.globalPrompt),...S(M),...S(this.config.memory),...x?[{role:"system",content:x}]:[]],U=P.map(h=>h.content).filter(Boolean).join(`
|
|
9
9
|
|
|
10
|
-
`);this.initialConversation=ae(P),this.state={started:!0,terminated:!1,configMcpCount:Object.keys(e.mcpServers??{}).length,skills:t,provider:a,permissions:i,mcpConnections:c,ownsMcpConnections:r,exposedTools:v,commands:k,commandRegistry:R,conversation:ae(P),accumulatedInputTokens:0,accumulatedOutputTokens:0,accumulatedCacheHitTokens:0,accumulatedCacheMissTokens:0},this.exposedToolsInitialized=!0,this.emitAcodeEvent("startup_phase",{phase:"session_start_hook"}),await this.hookBus.dispatch("SessionStart",{workingDirectory:this.config.workingDirectory,model:a.model,commandCount:k.length});const
|
|
11
|
-
`,"utf-8").catch(()=>{})}catch{}},5e3)}stopCompactionDebugLog(){this.compactionDebugTimer&&(clearInterval(this.compactionDebugTimer),this.compactionDebugTimer=null)}async submit(e){const t=this.contextVersion;l(`[ACodeRuntime] \u{1F4EC} submit() queued: contextVersion=${t}, activeTurn=${this.activeTurn}, abortRequested=${this.abortRequested}, terminated=${this.state.terminated}, message.length=${e.length}`);const n=this.submitQueue.then(()=>(l(`[ACodeRuntime] \u25B6\uFE0F executeSubmit dequeued: contextVersion=${t}`),this.executeSubmit(e,t)));return this.submitQueue=n.catch(()=>{l("[ACodeRuntime] \u{1F4EC} submit() queued item rejected (catch swallowed)")}),l("[ACodeRuntime] \u{1F4EC} submit() returning queued promise"),n}notifyForceMessage(){this.forceMessagePending=!0,l("[ACodeRuntime] \u{1F514} Force message pending flag set")}async injectForceMessage(){if(!this.forceMessagePending)return!1;this.forceMessagePending=!1;const e=this.state.mcpConnections.find(t=>t.tools.some(n=>n.toolName==="get_message"));if(!e)return l("[ACodeRuntime] injectForceMessage: no connection with get_message found"),!1;try{const t=await N(e.client.callTool({name:"get_message",arguments:{_aws_force_inject:!0}},void 0,{timeout:1e4}),1e4,"injectForceMessage get_message"),n=D(t);let s=[];try{const r=JSON.parse(n);Array.isArray(r?.directMessages)&&(s=r.directMessages.filter(u=>u?.force===!0))}catch{return!1}if(s.length===0)return!1;const a=JSON.stringify({directMessages:s,groupMessages:[]}),i=`forced_get_msg_${Date.now()}`,c=`${e.serverName}__get_message`;return this.state.conversation.push({role:"assistant",content:null,tool_calls:[{id:i,type:"function",function:{name:c,arguments:"{}"}}]}),this.state.conversation.push({role:"tool",tool_call_id:i,content:a}),l(`[ACodeRuntime] injectForceMessage: injected ${s.length} force message(s) into conversation`),!0}catch(t){return l(`[ACodeRuntime] injectForceMessage failed: ${t}`),!1}}async resetContext(){if(!this.state.started||this.state.terminated)throw new Error("ACode runtime is not active");this.contextVersion+=1,this.abortRequested=this.activeTurn,this.abortController?.abort(),this.abortController=null,this.state.conversation=ae(this.initialConversation),this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0}isTurnSuperseded(e){return e!==this.contextVersion}async executeSubmit(e,t){const n=Date.now();if(l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l("[ACodeRuntime] \u25B6\uFE0F executeSubmit STARTED"),l(`[ACodeRuntime] Message: <redacted, length=${e.length}>`),l(`[ACodeRuntime] \u{1F522} Context version: ${t}`),l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),!this.state.started||this.state.terminated)throw console.error(`[ACodeRuntime] \u274C Runtime not active (started: ${this.state.started}, terminated: ${this.state.terminated})`),new Error("ACode runtime is not active");this.activeTurn=!0,this.abortRequested=!1;const s=e.trim();let a=!1;try{l("[ACodeRuntime] \u{1F50D} Dispatching UserPromptSubmit hook...");const i=Date.now(),c=await this.hookBus.dispatch("UserPromptSubmit",{prompt:s}),r=Date.now()-i;if(l(`[ACodeRuntime] \u2705 UserPromptSubmit hook completed in ${r}ms, results: ${c.length}`),this.isTurnSuperseded(t)){l("[ACodeRuntime] \u26A0\uFE0F Turn superseded after hook, aborting");return}const u=re(c);if(u){console.error(`[ACodeRuntime] \u274C Prompt denied by hook: ${u.message}`),this.emitAcodeEvent("error",{message:u.message||"ACode prompt was denied by hook"}),this.emitAcodeEvent("turn_complete",{failed:!0}),a=!0,l("[ACodeRuntime] \u{1F4E4} Emitted turn_complete (failed=true) due to hook deny");return}l("[ACodeRuntime] \u{1F50D} Expanding command if any...");const m=this.state.commandRegistry.expand(s),g=m?.prompt.trim()||s,y=m?.command.allowedTools;if(m&&(l(`[ACodeRuntime] \u{1F4CB} Slash command detected: ${m.command.name}`),this.emitAcodeEvent("command_status",{action:"slash_command",command:m.command.name,arguments:m.argumentsText,sourcePath:m.command.sourcePath})),!this.state.provider.baseURL||!this.state.provider.apiKey){l("[ACodeRuntime] \u26A0\uFE0F No provider configured, using fallback response");const w=Zt(g,this.state);this.emitAcodeEvent("assistant_delta",{text:w}),this.emitAcodeEvent("turn_complete",{usage:{inputTokens:g.length,outputTokens:w.length}}),a=!0,l("[ACodeRuntime] \u{1F4E4} Emitted turn_complete (fallback mode)");return}l("[ACodeRuntime] \u{1F4CE} Parsing attachment message...");const $=de(g);if($.hasImages&&!this.state.provider.supportsImageInput)throw console.error("[ACodeRuntime] \u274C Image input not supported by current provider"),new Error("ACode provider/model is not configured for image input. Enable provider.default.models[model].modalities.input including 'image' or options.supportsImageInput=true.");l("[ACodeRuntime] \u{1F5BC}\uFE0F Materializing image content if any...");const p=await De($.content);if(this.isTurnSuperseded(t)){l("[ACodeRuntime] \uFE0F Turn superseded after materializing content, aborting");return}l("[ACodeRuntime] \u{1F4AC} Adding user message to conversation"),this.state.conversation.push({role:"user",content:p}),l(`[ACodeRuntime] Conversation size: ${this.state.conversation.length} messages`),l("[ACodeRuntime] Starting runModelTurn...");const b=Date.now(),d=await this.runModelTurn(y,t),f=Date.now()-b;if(l(`[ACodeRuntime] \u2705 runModelTurn completed in ${f}ms`),l(`[ACodeRuntime] \u{1F4DD} Result: text=${d.text?d.text.slice(0,80)+(d.text.length>80?"...":""):"(empty)"}, finishReason=${d.finishReason||"(none)"}`),this.isTurnSuperseded(t)){l("[ACodeRuntime] \uFE0F Turn superseded after runModelTurn, aborting");return}d.text&&!this.state.provider.stream&&(l("[ACodeRuntime] \u{1F4E4} Emitting assistant_delta (non-streaming mode)"),this.emitAcodeEvent("assistant_delta",{text:d.text})),l("[ACodeRuntime] \u{1F4E4} Emitting turn_complete..."),this.emitAcodeEvent("turn_complete",{usage:d.usage??{inputTokens:g.length,outputTokens:d.totalOutputTextLength??d.text.length},...d.responseBody?{responseBody:d.responseBody}:{},...d.finishReason?{finishReason:d.finishReason}:{}}),j(`turn_complete emitted: usage=${JSON.stringify(d.usage??{inputTokens:g.length,outputTokens:d.totalOutputTextLength??d.text.length})} (providerUsage=${d.usage?"real":"FALLBACK-text-length"}) textLength=${d.text.length} totalOutputTextLength=${d.totalOutputTextLength}`),l(`[ACodeRuntime] \u{1F4B0} Current context usage: input=${this.state.accumulatedInputTokens}, output=${this.state.accumulatedOutputTokens}`+(d.usage?"":" (estimated, no provider usage)")),a=!0;const _=Date.now()-n;l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l(`[ACodeRuntime] \u2705 executeSubmit COMPLETED in ${_}ms`),l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}catch(i){const c=Date.now()-n;if(console.error(`[ACodeRuntime] \u274C executeSubmit FAILED after ${c}ms:`,i instanceof Error?i.message:String(i)),console.error("[ACodeRuntime] \u{1F50D} Error stack:",i instanceof Error?i.stack:"(no stack)"),this.isTurnSuperseded(t)){l("[ACodeRuntime] \uFE0F Turn superseded in error handler, skipping recovery");return}if(this.abortRequested){l("[ACodeRuntime] \u26A0\uFE0F Abort requested, emitting aborted turn_complete"),this.emitAcodeEvent("turn_complete",{aborted:!0}),a=!0;return}l("[ACodeRuntime] Emitting error and failed turn_complete..."),this.emitAcodeEvent("error",{message:i instanceof Error?i.message:String(i)}),this.emitAcodeEvent("turn_complete",{failed:!0}),a=!0}finally{const i=Date.now()-n;l(`[ACodeRuntime] \u{1F51A} executeSubmit finally block entered after ${i}ms`),l(`[ACodeRuntime] \u{1F50D} turnCompleted=${a}, activeTurn=${this.activeTurn}, abortRequested=${this.abortRequested}, contextVersion=${t}, currentContextVersion=${this.contextVersion}`),this.activeTurn=!1,this.abortRequested=!1,!a&&!this.isTurnSuperseded(t)&&(console.error(`[ACodeRuntime] \u{1F6A8} CRITICAL: executeSubmit finally block reached without turn_complete event! Duration: ${i}ms. This indicates a code path that should have emitted turn_complete but didn't. Forcing failed turn_complete to prevent state stuck.`),this.emitAcodeEvent("error",{message:"ACode turn completed without proper completion event (internal error)"}),this.emitAcodeEvent("turn_complete",{failed:!0}),l("[ACodeRuntime] Emergency turn_complete (failed=true) emitted"))}}async abort(){const e=this.abortController!==null,t=this.activeTurn;this.abortRequested=this.activeTurn,this.abortController?(l(`[ACodeRuntime] \u26D4 abort() calling abortController.abort() (activeTurn=${t})`),this.abortController.abort()):console.warn(`[ACodeRuntime] \u26D4 abort() called but abortController is null! activeTurn=${t}, abortRequested will be set to ${t}. This means abort() CANNOT cancel any running fetch/stream \u2014 if a built-in tool is executing, it will NOT be interrupted.`),this.abortController=null,this.rejectAllQuestionWaiters("aborted"),this.cancelPendingAwaitedSubAgents(),this.cancelPendingAwaitedBgTasks(),this.notifyRuntimeStop()}buildAskUserQuestionDeps(){return{onQuestion:(e,t)=>{this.emitAcodeEvent("question_request",{toolCallId:e,questions:t})},waitForAnswer:(e,t)=>new Promise((n,s)=>{this.questionWaiters.set(e,{resolve:n,reject:s,contextVersion:t})})}}buildSubAgentToolDeps(e){const t=this.config.agentId??"unknown",n=this.config.sessionId,s=this.config.workingDirectory,a=this.config;return{spawnSubAgent:async i=>{const c=await e.spawn({parentAgentId:t,parentSessionId:n,subagentType:i.subagentType,description:i.description,prompt:i.prompt,workingDirectory:s,parentConfig:{...a,sharedMcpConnections:this.state?.mcpConnections}});return await e.start(c),{subId:c}},listSubAgents:async()=>{const i=e.listByParent(t),c=[],r=[];for(const u of i){const m={subId:u.subId,subagentType:u.subagentType,description:u.description,status:u.status,startedAt:u.startedAt,endedAt:u.endedAt,durationMs:u.durationMs};u.status==="pending"||u.status==="running"?c.push(m):r.push(m)}return r.sort((u,m)=>(m.endedAt??0)-(u.endedAt??0)),{running:c,history:r}},getSubAgentStatus:async i=>{const c=e.getSubAgent(i);return c?{subId:c.subId,subagentType:c.subagentType,description:c.description,status:c.status,startedAt:c.startedAt,endedAt:c.endedAt,durationMs:c.durationMs,prompt:c.prompt,result:c.result,errorMessage:c.errorMessage,contextSnapshot:c.contextSnapshot}:null},awaitSubAgents:async(i,c)=>{const r=new Set;for(const u of i)this.pendingAwaitedSubIds.add(u),r.add(u);try{return await e.awaitCompletion(i,c)}finally{for(const u of r)this.pendingAwaitedSubIds.delete(u)}},abortSubAgent:i=>e.abort(i)}}buildAwaitCompleteDeps(){const e=this.config.agentId??"unknown",t=this.config.subAgentManager;return{agentId:e,awaitSubAgents:t?(n,s)=>{for(const a of n)this.pendingAwaitedSubIds.add(a);try{return this.buildSubAgentToolDeps(t).awaitSubAgents(n,s)}finally{for(const a of n)this.pendingAwaitedSubIds.delete(a)}}:null,awaitBgCommands:(n,s)=>{for(const a of n)this.pendingAwaitedBgTaskIds.add(a);try{return G.awaitCompletion(e,n,s)}finally{for(const a of n)this.pendingAwaitedBgTaskIds.delete(a)}}}}submitQuestionAnswer(e,t){const n=this.questionWaiters.get(e);return n?(this.questionWaiters.delete(e),this.isTurnSuperseded(n.contextVersion)?(n.reject(new Error("turn superseded before user answered")),!1):(n.resolve(t),!0)):!1}rejectAllQuestionWaiters(e){if(this.questionWaiters.size!==0){for(const[,t]of this.questionWaiters)t.reject(new Error(`ask_user_question ${e}`));this.questionWaiters.clear()}}cancelPendingAwaitedSubAgents(){if(this.pendingAwaitedSubIds.size===0)return;const e=this.config.subAgentManager;if(!e)return;const t=Array.from(this.pendingAwaitedSubIds);l(`[ACodeRuntime] \u26D4 Cancelling ${t.length} pending awaited sub-agent(s): ${t.join(", ")}`);for(const n of t)e.abort(n).catch(s=>{l(`[ACodeRuntime] \u26A0\uFE0F manager.abort(${n}) threw: ${s instanceof Error?s.message:String(s)}`)})}cancelPendingAwaitedBgTasks(){if(this.pendingAwaitedBgTaskIds.size===0)return;const e=this.config.agentId??"unknown",t=Array.from(this.pendingAwaitedBgTaskIds);l(`[ACodeRuntime] \u26D4 Cancelling ${t.length} pending awaited background command(s): ${t.join(", ")}`),G.interruptAwaits(e,t)}detachCurrentToolExecution(){const e=this.toolSettleSlot;return!e||!e.settlable||e.settled?!1:(e.settled=!0,e.resolveSettle?.(),l(`[ACodeRuntime] \u{1F680} detachCurrentToolExecution: settling tool ${e.toolName} (toolCallId=${e.toolCallId}) to background`),!0)}beginToolSettle(e,t,n){const s=e.toolName==="Bash"&&typeof t.wait_seconds=="number"&&t.wait_seconds>0,a=++this.toolSettleSeq;let i;const c=s?new Promise(r=>{i=r}):void 0;return this.toolSettleSlot={seq:a,toolName:e.toolName,toolCallId:n,settlable:s,settled:!1,resolveSettle:i??null},{seq:a,signal:c}}clearToolSettle(e){this.toolSettleSlot?.seq===e&&(this.toolSettleSlot=null)}async stop(){if(this.state.terminated)return;this.state.terminated=!0,this.notifyRuntimeStop(),this.abortRequested=this.activeTurn,this.abortController?.abort(),this.abortController=null,this.rejectAllQuestionWaiters("session terminated"),this.cancelPendingAwaitedSubAgents(),this.cancelPendingAwaitedBgTasks(),await Y("ACode active turn shutdown",this.submitQueue,t=>{this.emitAcodeEvent("error",{message:t})}),await this.disconnectMcpServers();const e=this.config.agentId||"unknown";if(await Y("ACode background commands shutdown",G.disposeAgent(e),t=>{this.emitAcodeEvent("error",{message:t})}),Y("ACode agent PTY sessions shutdown",Promise.resolve(this.config.terminalManager?.killByAgent?.(e)),t=>{this.emitAcodeEvent("error",{message:t})}),await Y("ACode Stop hooks",this.hookBus.dispatch("Stop",{reason:"session_stop"}),t=>{this.emitAcodeEvent("error",{message:t})}),this.state.terminated=!0,this.sessionCompleteEmitted||(this.sessionCompleteEmitted=!0,this.emitAcodeEvent("session_complete",{exitCode:0})),this.stopCompactionDebugLog(),this.stopSkillWatchers(),ne)try{K.promises.appendFile(Ee,`[${new Date().toISOString()}] runtime stopped | ---
|
|
12
|
-
`,"utf-8").catch(()=>{})}catch{}}resolveCommandDirs(e){const t=[...this.config.commandDirs??[],...e.commandDirs??[],...e.commands?.directories??[]];return[...new Set(t.map(n=>n.trim()).filter(Boolean))]}async runModelTurn(e,t=this.contextVersion){let n="",s,a,i,c="",r=0;this.lastIterationUsage=void 0,l(`[ACodeRuntime] \u25B6\uFE0F runModelTurn started (contextVersion: ${t})`);const u=50;for(let m=0;m<u;m+=1){if(l(`[ACodeRuntime] \u{1F504} Iteration ${m+1} starting...`),this.isTurnSuperseded(t))return l(`[ACodeRuntime] \u26A0\uFE0F Turn superseded at iteration ${m+1}, aborting`),{text:"",totalOutputTextLength:c.length};await this.injectForceMessage();const g=Date.now(),y=await this.requestChatCompletion(e),$=Date.now()-g;if(l(`[ACodeRuntime] \u{1F916} AI request completed in ${$}ms, choices: ${y.choices?.length||0}`),this.isTurnSuperseded(t))return l(`[ACodeRuntime] \u26A0\uFE0F Turn superseded after AI response at iteration ${m+1}`),{text:"",totalOutputTextLength:c.length};if(this.state.terminated)throw l(`[ACodeRuntime] Runtime terminated at iteration ${m+1}`),new Error("ACode runtime was stopped during the active turn");const p=y.choices?.[0];a=JSON.stringify(y,null,2),i=typeof p?.finish_reason=="string"?p.finish_reason:void 0;const b=p?.message,d=typeof b?.content=="string"?b.content:"",f=Array.isArray(b?.tool_calls)?b.tool_calls:[];if(l(`[ACodeRuntime] AI response: content=${d?d.slice(0,100)+(d.length>100?"...":""):"(empty)"}, toolCalls=${f.length}, finishReason=${i||"(none)"}`),y.usage){const C=
|
|
10
|
+
`);this.initialConversation=ae(P),this.state={started:!0,terminated:!1,configMcpCount:Object.keys(e.mcpServers??{}).length,skills:t,provider:a,permissions:i,mcpConnections:c,ownsMcpConnections:r,exposedTools:v,commands:k,commandRegistry:R,conversation:ae(P),accumulatedInputTokens:0,accumulatedOutputTokens:0,accumulatedCacheHitTokens:0,accumulatedCacheMissTokens:0},this.exposedToolsInitialized=!0,this.emitAcodeEvent("startup_phase",{phase:"session_start_hook"}),await this.hookBus.dispatch("SessionStart",{workingDirectory:this.config.workingDirectory,model:a.model,commandCount:k.length});const D=ue(a);this.emitAcodeEvent("session_started",{workingDirectory:this.config.workingDirectory,model:a.model,commandCount:k.length,estimatedContextTokens:Et(U,v),contextLimit:D.contextLimit,maxOutput:D.maxOutput,...D.inputLimit!==void 0?{inputLimit:D.inputLimit}:{},compactionThreshold:D.threshold}),this.emitAcodeEvent("mcp_status",{configPath:this.config.configPath,installedCount:this.state.configMcpCount,servers:Object.keys(e.mcpServers??{}),connectedCount:c.length,builtInToolCount:g.length,toolCount:v.length,permissionMode:i.mode,allowedToolRules:i.allow,deniedToolRules:i.deny,tools:v.map(h=>({name:h.exposedName,source:h.kind==="mcp"?`${h.serverName}.${h.toolName}`:`builtin.${h.toolName}`,description:h.description||""}))}),this.emitAcodeEvent("skill_status",{skillsDir:this.config.skillsDir,installedCount:t.length,skills:t.map(h=>({name:h.name,description:h.description,directory:h.directory})),skillGroups:n.map(h=>({dir:h.dir,exists:h.exists,skills:h.skills.map(F=>({name:F.name,description:F.description,directory:F.directory}))}))}),this.emitAcodeEvent("command_status",{action:"commands_loaded",installedCommandCount:k.length,commandDirs:C,commands:k.map(h=>({name:h.name,description:h.description}))}),this.config.initialPrompt&&await this.submit(this.config.initialPrompt),this.startCompactionDebugLog(),this.startSkillWatchers(s)}async loadAllSkills(){const e=this.config.skillsDir??"",t=Z.join(this.config.workingDirectory,".acode","skills"),n=Z.join(this.config.workingDirectory,".agentswork","skills"),s=new Set,a=[],i=[];for(const u of[e,t,n]){if(!u)continue;const m=Z.resolve(u);if(!s.has(m)){s.add(m),a.push(await pt(u));try{await K.promises.access(m),i.push(m)}catch{}}}const c=new Map;for(const u of a)for(const m of u.skills)c.set(m.name,m);return{skills:[...c.values()],skillGroups:a,watchedDirs:i}}startSkillWatchers(e){for(const t of e){let n=null;try{n=K.watch(t,{recursive:!0},()=>{this.scheduleSkillRefresh()})}catch{try{n=K.watch(t,()=>{this.scheduleSkillRefresh()})}catch{continue}}n&&(n.on("error",()=>{}),this.skillWatchers.push(n))}}scheduleSkillRefresh(){this.skillRefreshTimer&&clearTimeout(this.skillRefreshTimer),this.skillRefreshTimer=setTimeout(()=>{this.skillRefreshTimer=null,this.refreshSkills()},J.SKILL_REFRESH_DEBOUNCE_MS)}async refreshSkills(){if(!(!this.state.started||this.state.terminated))try{const{skills:e,skillGroups:t}=await this.loadAllSkills();this.skillTool&&this.skillTool.refresh(e),this.state.skills=e,this.emitAcodeEvent("skill_status",{skillsDir:this.config.skillsDir,installedCount:e.length,skills:e.map(n=>({name:n.name,description:n.description,directory:n.directory})),skillGroups:t.map(n=>({dir:n.dir,exists:n.exists,skills:n.skills.map(s=>({name:s.name,description:s.description,directory:s.directory}))}))}),l(`[skill-watch] Skills refreshed: ${e.length} skills (${e.map(n=>n.name).join(", ")||"none"})`,{sessionId:this.config.sessionId})}catch(e){console.error("[skill-watch] Failed to refresh skills:",e instanceof Error?e.message:e)}}stopSkillWatchers(){this.skillRefreshTimer&&(clearTimeout(this.skillRefreshTimer),this.skillRefreshTimer=null);for(const e of this.skillWatchers)try{e.close()}catch{}this.skillWatchers=[]}startCompactionDebugLog(){if(!ne||this.compactionDebugTimer)return;const e=this.state.provider,{contextLimit:t,maxOutput:n,inputLimit:s,threshold:a}=ue(e);this.compactionDebugTimer=setInterval(()=>{try{const i=this.state.accumulatedInputTokens+this.state.accumulatedOutputTokens,c=ee(this.state.conversation)+Ie(this.state.exposedTools),r=Math.max(i,c),u=Q(this.state.conversation),m=this.state.started&&!this.state.terminated,g=[`[${new Date().toISOString()}]`,`started=${this.state.started}`,`terminated=${this.state.terminated}`,`activeTurn=${this.activeTurn}`,`conversationSize=${this.state.conversation.length}msgs`,`sizeKB=${u}KB`,`estimation=${c} tokens`,`actualInput=${this.state.accumulatedInputTokens}`,`actualOutput=${this.state.accumulatedOutputTokens}`,`actualUsed=${i}`,`contextLimit=${t}`,`inputLimit=${s??"n/a"}`,`maxOutput=${n}`,`triggerThreshold=${a}`,`remainingToTrigger=${Math.max(0,a-i)}`,`isRunning=${m}`,`shouldCompact=${m?r>=a:!1}`,"---"].join(" | ");K.promises.appendFile(Ee,g+`
|
|
11
|
+
`,"utf-8").catch(()=>{})}catch{}},5e3)}stopCompactionDebugLog(){this.compactionDebugTimer&&(clearInterval(this.compactionDebugTimer),this.compactionDebugTimer=null)}async submit(e){const t=this.contextVersion;l(`[ACodeRuntime] \u{1F4EC} submit() queued: contextVersion=${t}, activeTurn=${this.activeTurn}, abortRequested=${this.abortRequested}, terminated=${this.state.terminated}, message.length=${e.length}`);const n=this.submitQueue.then(()=>(l(`[ACodeRuntime] \u25B6\uFE0F executeSubmit dequeued: contextVersion=${t}`),this.executeSubmit(e,t)));return this.submitQueue=n.catch(()=>{l("[ACodeRuntime] \u{1F4EC} submit() queued item rejected (catch swallowed)")}),l("[ACodeRuntime] \u{1F4EC} submit() returning queued promise"),n}notifyForceMessage(){this.forceMessagePending=!0,l("[ACodeRuntime] \u{1F514} Force message pending flag set")}async injectForceMessage(){if(!this.forceMessagePending)return!1;this.forceMessagePending=!1;const e=this.state.mcpConnections.find(t=>t.tools.some(n=>n.toolName==="get_message"));if(!e)return l("[ACodeRuntime] injectForceMessage: no connection with get_message found"),!1;try{const t=await N(e.client.callTool({name:"get_message",arguments:{_aws_force_inject:!0}},void 0,{timeout:1e4}),1e4,"injectForceMessage get_message"),n=O(t);let s=[];try{const r=JSON.parse(n);Array.isArray(r?.directMessages)&&(s=r.directMessages.filter(u=>u?.force===!0))}catch{return!1}if(s.length===0)return!1;const a=JSON.stringify({directMessages:s,groupMessages:[]}),i=`forced_get_msg_${Date.now()}`,c=`${e.serverName}__get_message`;return this.state.conversation.push({role:"assistant",content:null,tool_calls:[{id:i,type:"function",function:{name:c,arguments:"{}"}}]}),this.state.conversation.push({role:"tool",tool_call_id:i,content:a}),l(`[ACodeRuntime] injectForceMessage: injected ${s.length} force message(s) into conversation`),!0}catch(t){return l(`[ACodeRuntime] injectForceMessage failed: ${t}`),!1}}async resetContext(){if(!this.state.started||this.state.terminated)throw new Error("ACode runtime is not active");this.contextVersion+=1,this.abortRequested=this.activeTurn,this.abortController?.abort(),this.abortController=null,this.state.conversation=ae(this.initialConversation),this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0}isTurnSuperseded(e){return e!==this.contextVersion}async executeSubmit(e,t){const n=Date.now();if(l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l("[ACodeRuntime] \u25B6\uFE0F executeSubmit STARTED"),l(`[ACodeRuntime] Message: <redacted, length=${e.length}>`),l(`[ACodeRuntime] \u{1F522} Context version: ${t}`),l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),!this.state.started||this.state.terminated)throw console.error(`[ACodeRuntime] \u274C Runtime not active (started: ${this.state.started}, terminated: ${this.state.terminated})`),new Error("ACode runtime is not active");this.activeTurn=!0,this.abortRequested=!1;const s=e.trim();let a=!1;try{l("[ACodeRuntime] \u{1F50D} Dispatching UserPromptSubmit hook...");const i=Date.now(),c=await this.hookBus.dispatch("UserPromptSubmit",{prompt:s}),r=Date.now()-i;if(l(`[ACodeRuntime] \u2705 UserPromptSubmit hook completed in ${r}ms, results: ${c.length}`),this.isTurnSuperseded(t)){l("[ACodeRuntime] \u26A0\uFE0F Turn superseded after hook, aborting");return}const u=re(c);if(u){console.error(`[ACodeRuntime] \u274C Prompt denied by hook: ${u.message}`),this.emitAcodeEvent("error",{message:u.message||"ACode prompt was denied by hook"}),this.emitAcodeEvent("turn_complete",{failed:!0}),a=!0,l("[ACodeRuntime] \u{1F4E4} Emitted turn_complete (failed=true) due to hook deny");return}l("[ACodeRuntime] \u{1F50D} Expanding command if any...");const m=this.state.commandRegistry.expand(s),g=m?.prompt.trim()||s,y=m?.command.allowedTools;if(m&&(l(`[ACodeRuntime] \u{1F4CB} Slash command detected: ${m.command.name}`),this.emitAcodeEvent("command_status",{action:"slash_command",command:m.command.name,arguments:m.argumentsText,sourcePath:m.command.sourcePath})),!this.state.provider.baseURL||!this.state.provider.apiKey){l("[ACodeRuntime] \u26A0\uFE0F No provider configured, using fallback response");const w=Zt(g,this.state);this.emitAcodeEvent("assistant_delta",{text:w}),this.emitAcodeEvent("turn_complete",{usage:{inputTokens:g.length,outputTokens:w.length}}),a=!0,l("[ACodeRuntime] \u{1F4E4} Emitted turn_complete (fallback mode)");return}l("[ACodeRuntime] \u{1F4CE} Parsing attachment message...");const $=de(g);if($.hasImages&&!this.state.provider.supportsImageInput)throw console.error("[ACodeRuntime] \u274C Image input not supported by current provider"),new Error("ACode provider/model is not configured for image input. Enable provider.default.models[model].modalities.input including 'image' or options.supportsImageInput=true.");l("[ACodeRuntime] \u{1F5BC}\uFE0F Materializing image content if any...");const p=await Oe($.content);if(this.isTurnSuperseded(t)){l("[ACodeRuntime] \uFE0F Turn superseded after materializing content, aborting");return}l("[ACodeRuntime] \u{1F4AC} Adding user message to conversation"),this.state.conversation.push({role:"user",content:p}),l(`[ACodeRuntime] Conversation size: ${this.state.conversation.length} messages`),l("[ACodeRuntime] Starting runModelTurn...");const b=Date.now(),d=await this.runModelTurn(y,t),f=Date.now()-b;if(l(`[ACodeRuntime] \u2705 runModelTurn completed in ${f}ms`),l(`[ACodeRuntime] \u{1F4DD} Result: text=${d.text?d.text.slice(0,80)+(d.text.length>80?"...":""):"(empty)"}, finishReason=${d.finishReason||"(none)"}`),this.isTurnSuperseded(t)){l("[ACodeRuntime] \uFE0F Turn superseded after runModelTurn, aborting");return}d.text&&!this.state.provider.stream&&(l("[ACodeRuntime] \u{1F4E4} Emitting assistant_delta (non-streaming mode)"),this.emitAcodeEvent("assistant_delta",{text:d.text})),l("[ACodeRuntime] \u{1F4E4} Emitting turn_complete..."),this.emitAcodeEvent("turn_complete",{usage:d.usage??{inputTokens:g.length,outputTokens:d.totalOutputTextLength??d.text.length},...d.responseBody?{responseBody:d.responseBody}:{},...d.finishReason?{finishReason:d.finishReason}:{}}),j(`turn_complete emitted: usage=${JSON.stringify(d.usage??{inputTokens:g.length,outputTokens:d.totalOutputTextLength??d.text.length})} (providerUsage=${d.usage?"real":"FALLBACK-text-length"}) textLength=${d.text.length} totalOutputTextLength=${d.totalOutputTextLength}`),l(`[ACodeRuntime] \u{1F4B0} Current context usage: input=${this.state.accumulatedInputTokens}, output=${this.state.accumulatedOutputTokens}`+(d.usage?"":" (estimated, no provider usage)")),a=!0;const _=Date.now()-n;l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l(`[ACodeRuntime] \u2705 executeSubmit COMPLETED in ${_}ms`),l("[ACodeRuntime] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}catch(i){const c=Date.now()-n;if(console.error(`[ACodeRuntime] \u274C executeSubmit FAILED after ${c}ms:`,i instanceof Error?i.message:String(i)),console.error("[ACodeRuntime] \u{1F50D} Error stack:",i instanceof Error?i.stack:"(no stack)"),this.isTurnSuperseded(t)){l("[ACodeRuntime] \uFE0F Turn superseded in error handler, skipping recovery");return}if(this.abortRequested){l("[ACodeRuntime] \u26A0\uFE0F Abort requested, emitting aborted turn_complete"),this.emitAcodeEvent("turn_complete",{aborted:!0}),a=!0;return}l("[ACodeRuntime] Emitting error and failed turn_complete..."),this.emitAcodeEvent("error",{message:i instanceof Error?i.message:String(i)}),this.emitAcodeEvent("turn_complete",{failed:!0}),a=!0}finally{const i=Date.now()-n;l(`[ACodeRuntime] \u{1F51A} executeSubmit finally block entered after ${i}ms`),l(`[ACodeRuntime] \u{1F50D} turnCompleted=${a}, activeTurn=${this.activeTurn}, abortRequested=${this.abortRequested}, contextVersion=${t}, currentContextVersion=${this.contextVersion}`),this.activeTurn=!1,this.abortRequested=!1,!a&&!this.isTurnSuperseded(t)&&(console.error(`[ACodeRuntime] \u{1F6A8} CRITICAL: executeSubmit finally block reached without turn_complete event! Duration: ${i}ms. This indicates a code path that should have emitted turn_complete but didn't. Forcing failed turn_complete to prevent state stuck.`),this.emitAcodeEvent("error",{message:"ACode turn completed without proper completion event (internal error)"}),this.emitAcodeEvent("turn_complete",{failed:!0}),l("[ACodeRuntime] Emergency turn_complete (failed=true) emitted"))}}async abort(){const e=this.abortController!==null,t=this.activeTurn;this.abortRequested=this.activeTurn,this.abortController?(l(`[ACodeRuntime] \u26D4 abort() calling abortController.abort() (activeTurn=${t})`),this.abortController.abort()):console.warn(`[ACodeRuntime] \u26D4 abort() called but abortController is null! activeTurn=${t}, abortRequested will be set to ${t}. This means abort() CANNOT cancel any running fetch/stream \u2014 if a built-in tool is executing, it will NOT be interrupted.`),this.abortController=null,this.rejectAllQuestionWaiters("aborted"),this.cancelPendingAwaitedSubAgents(),this.cancelPendingAwaitedBgTasks(),this.notifyRuntimeStop()}buildAskUserQuestionDeps(){return{onQuestion:(e,t)=>{this.emitAcodeEvent("question_request",{toolCallId:e,questions:t})},waitForAnswer:(e,t)=>new Promise((n,s)=>{this.questionWaiters.set(e,{resolve:n,reject:s,contextVersion:t})})}}buildSubAgentToolDeps(e){const t=this.config.agentId??"unknown",n=this.config.sessionId,s=this.config.workingDirectory,a=this.config;return{spawnSubAgent:async i=>{const c=await e.spawn({parentAgentId:t,parentSessionId:n,subagentType:i.subagentType,description:i.description,prompt:i.prompt,workingDirectory:s,parentConfig:{...a,sharedMcpConnections:this.state?.mcpConnections}});return await e.start(c),{subId:c}},listSubAgents:async()=>{const i=e.listByParent(t),c=[],r=[];for(const u of i){const m={subId:u.subId,subagentType:u.subagentType,description:u.description,status:u.status,startedAt:u.startedAt,endedAt:u.endedAt,durationMs:u.durationMs};u.status==="pending"||u.status==="running"?c.push(m):r.push(m)}return r.sort((u,m)=>(m.endedAt??0)-(u.endedAt??0)),{running:c,history:r}},getSubAgentStatus:async i=>{const c=e.getSubAgent(i);return c?{subId:c.subId,subagentType:c.subagentType,description:c.description,status:c.status,startedAt:c.startedAt,endedAt:c.endedAt,durationMs:c.durationMs,prompt:c.prompt,result:c.result,errorMessage:c.errorMessage,contextSnapshot:c.contextSnapshot}:null},awaitSubAgents:async(i,c)=>{const r=new Set;for(const u of i)this.pendingAwaitedSubIds.add(u),r.add(u);try{return await e.awaitCompletion(i,c)}finally{for(const u of r)this.pendingAwaitedSubIds.delete(u)}},abortSubAgent:i=>e.abort(i)}}buildAwaitCompleteDeps(){const e=this.config.agentId??"unknown",t=this.config.subAgentManager;return{agentId:e,awaitSubAgents:t?(n,s)=>{for(const a of n)this.pendingAwaitedSubIds.add(a);try{return this.buildSubAgentToolDeps(t).awaitSubAgents(n,s)}finally{for(const a of n)this.pendingAwaitedSubIds.delete(a)}}:null,awaitBgCommands:(n,s)=>{for(const a of n)this.pendingAwaitedBgTaskIds.add(a);try{return G.awaitCompletion(e,n,s)}finally{for(const a of n)this.pendingAwaitedBgTaskIds.delete(a)}}}}submitQuestionAnswer(e,t){const n=this.questionWaiters.get(e);return n?(this.questionWaiters.delete(e),this.isTurnSuperseded(n.contextVersion)?(n.reject(new Error("turn superseded before user answered")),!1):(n.resolve(t),!0)):!1}rejectAllQuestionWaiters(e){if(this.questionWaiters.size!==0){for(const[,t]of this.questionWaiters)t.reject(new Error(`ask_user_question ${e}`));this.questionWaiters.clear()}}cancelPendingAwaitedSubAgents(){if(this.pendingAwaitedSubIds.size===0)return;const e=this.config.subAgentManager;if(!e)return;const t=Array.from(this.pendingAwaitedSubIds);l(`[ACodeRuntime] \u26D4 Cancelling ${t.length} pending awaited sub-agent(s): ${t.join(", ")}`);for(const n of t)e.abort(n).catch(s=>{l(`[ACodeRuntime] \u26A0\uFE0F manager.abort(${n}) threw: ${s instanceof Error?s.message:String(s)}`)})}cancelPendingAwaitedBgTasks(){if(this.pendingAwaitedBgTaskIds.size===0)return;const e=this.config.agentId??"unknown",t=Array.from(this.pendingAwaitedBgTaskIds);l(`[ACodeRuntime] \u26D4 Cancelling ${t.length} pending awaited background command(s): ${t.join(", ")}`),G.interruptAwaits(e,t)}detachCurrentToolExecution(){const e=this.toolSettleSlot;return!e||!e.settlable||e.settled?!1:(e.settled=!0,e.resolveSettle?.(),l(`[ACodeRuntime] \u{1F680} detachCurrentToolExecution: settling tool ${e.toolName} (toolCallId=${e.toolCallId}) to background`),!0)}beginToolSettle(e,t,n){const s=e.toolName==="Bash"&&typeof t.wait_seconds=="number"&&t.wait_seconds>0,a=++this.toolSettleSeq;let i;const c=s?new Promise(r=>{i=r}):void 0;return this.toolSettleSlot={seq:a,toolName:e.toolName,toolCallId:n,settlable:s,settled:!1,resolveSettle:i??null},{seq:a,signal:c}}clearToolSettle(e){this.toolSettleSlot?.seq===e&&(this.toolSettleSlot=null)}async stop(){if(this.state.terminated)return;this.state.terminated=!0,this.notifyRuntimeStop(),this.abortRequested=this.activeTurn,this.abortController?.abort(),this.abortController=null,this.rejectAllQuestionWaiters("session terminated"),this.cancelPendingAwaitedSubAgents(),this.cancelPendingAwaitedBgTasks(),await Y("ACode active turn shutdown",this.submitQueue,t=>{this.emitAcodeEvent("error",{message:t})}),await this.disconnectMcpServers();const e=this.config.agentId||"unknown";if(await Y("ACode background commands shutdown",G.disposeAgent(e),t=>{this.emitAcodeEvent("error",{message:t})}),Y("ACode agent PTY sessions shutdown",Promise.resolve(this.config.terminalManager?.killByAgent?.(e)),t=>{this.emitAcodeEvent("error",{message:t})}),await Y("ACode Stop hooks",this.hookBus.dispatch("Stop",{reason:"session_stop"}),t=>{this.emitAcodeEvent("error",{message:t})}),this.state.terminated=!0,this.sessionCompleteEmitted||(this.sessionCompleteEmitted=!0,this.emitAcodeEvent("session_complete",{exitCode:0})),this.stopCompactionDebugLog(),this.stopSkillWatchers(),ne)try{K.promises.appendFile(Ee,`[${new Date().toISOString()}] runtime stopped | ---
|
|
12
|
+
`,"utf-8").catch(()=>{})}catch{}}resolveCommandDirs(e){const t=[...this.config.commandDirs??[],...e.commandDirs??[],...e.commands?.directories??[]];return[...new Set(t.map(n=>n.trim()).filter(Boolean))]}async runModelTurn(e,t=this.contextVersion){let n="",s,a,i,c="",r=0;this.lastIterationUsage=void 0,l(`[ACodeRuntime] \u25B6\uFE0F runModelTurn started (contextVersion: ${t})`);const u=50;for(let m=0;m<u;m+=1){if(l(`[ACodeRuntime] \u{1F504} Iteration ${m+1} starting...`),this.isTurnSuperseded(t))return l(`[ACodeRuntime] \u26A0\uFE0F Turn superseded at iteration ${m+1}, aborting`),{text:"",totalOutputTextLength:c.length};await this.injectForceMessage();const g=Date.now(),y=await this.requestChatCompletion(e),$=Date.now()-g;if(l(`[ACodeRuntime] \u{1F916} AI request completed in ${$}ms, choices: ${y.choices?.length||0}`),this.isTurnSuperseded(t))return l(`[ACodeRuntime] \u26A0\uFE0F Turn superseded after AI response at iteration ${m+1}`),{text:"",totalOutputTextLength:c.length};if(this.state.terminated)throw l(`[ACodeRuntime] Runtime terminated at iteration ${m+1}`),new Error("ACode runtime was stopped during the active turn");const p=y.choices?.[0];a=JSON.stringify(y,null,2),i=typeof p?.finish_reason=="string"?p.finish_reason:void 0;const b=p?.message,d=typeof b?.content=="string"?b.content:"",f=Array.isArray(b?.tool_calls)?b.tool_calls:[];if(l(`[ACodeRuntime] AI response: content=${d?d.slice(0,100)+(d.length>100?"...":""):"(empty)"}, toolCalls=${f.length}, finishReason=${i||"(none)"}`),y.usage){const C=L(y.usage.prompt_tokens??y.usage.input_tokens),k=L(y.usage.completion_tokens??y.usage.output_tokens),R=C!==void 0,T=k!==void 0;if(j(`provider usage parsed: raw=${JSON.stringify(y.usage)} input=${C} output=${k} hasInput=${R} hasOutput=${T} iteration=${m+1}`),R||T){const A=Ct(y.usage,C),x=s?.outputTokens??c.length+d.length;this.state.accumulatedCacheHitTokens+=A?.cacheHitTokens??0,this.state.accumulatedCacheMissTokens+=A?.cacheMissTokens??0,s={inputTokens:R?C:s?.inputTokens??0,outputTokens:T?k:x,cacheHitTokens:this.state.accumulatedCacheHitTokens,cacheMissTokens:this.state.accumulatedCacheMissTokens},this.lastIterationUsage=s,r+=R?C:0,this.state.accumulatedInputTokens=R?C:this.state.accumulatedInputTokens,this.state.accumulatedOutputTokens=T?k:this.state.accumulatedOutputTokens,j(`finalUsage updated: ${JSON.stringify(s)} (cacheUsage=${JSON.stringify(A??null)})`),l(`[ACodeRuntime] \u{1F4B0} Token usage: input=${s.inputTokens}, output=${s.outputTokens} (turn accumulated cost: input=${r})`),l(A?`[ACodeRuntime] \u{1F4B0} Cache usage (provider reported): hit=${A.cacheHitTokens}, miss=${A.cacheMissTokens}`:'[ACodeRuntime] \u{1F4B0} Cache usage: provider usage \u4E2D\u65E0\u7F13\u5B58\u5B57\u6BB5\uFF08\u9762\u677F\u5C06\u663E\u793A "-"\uFF09')}}else j(`provider response has NO usage field (fallback to text-length estimate), iteration=${m+1}`);if(this.state.conversation.push({role:"assistant",content:d||null,tool_calls:f.length>0?f:void 0}),d&&(n=d,c+=d,l(`[ACodeRuntime] \u2728 Captured assistant text: ${d.slice(0,80)}...`)),f.length===0)return l(`[ACodeRuntime] \u2705 No tool calls at iteration ${m+1}, completing turn`),{text:n,usage:s,responseBody:a,finishReason:i,totalOutputTextLength:c.length};const _=f.map(C=>C.function.name).join(", ");l(`[ACodeRuntime] \u{1F527} Executing ${f.length} tool(s) at iteration ${m+1}: ${_}`);const w=Date.now();await this.handleToolCalls(f,e,t);const v=Date.now()-w;if(l(`[ACodeRuntime] \u2699\uFE0F Tool execution completed in ${v}ms at iteration ${m+1}`),this.isTurnSuperseded(t))return l(`[ACodeRuntime] \u26A0\uFE0F Turn superseded after tool execution at iteration ${m+1}`),{text:"",totalOutputTextLength:c.length};l(`[ACodeRuntime] \u{1F504} Preparing iteration ${m+2}...`)}return console.error(`[ACodeRuntime] \u{1F6A8} CRITICAL: Reached safety limit of ${u} iterations! This indicates a potential infinite loop. Forcing turn completion.`),{text:n||`ACode \u5DF2\u8FBE\u5230\u5B89\u5168\u8FED\u4EE3\u4E0A\u9650\uFF08${u}\u6B21\uFF09\uFF0C\u5F3A\u5236\u7ED3\u675F\u672C\u8F6E\u5BF9\u8BDD\u3002`,usage:s,responseBody:a,finishReason:i||"safety_iteration_limit_reached",totalOutputTextLength:c.length}}async summarizeConversationWithLLM(e){const t=this.state.provider,n=t.baseURL;if(!n)throw new Error("ACode provider baseURL is not configured for LLM summarization");const s={"Content-Type":"application/json"};t.apiKey&&(s.Authorization=`Bearer ${t.apiKey}`);const a={model:t.model,stream:!1,messages:[{role:"system",content:"You are a helpful AI assistant tasked with summarizing conversations. When asked to summarize, provide a detailed but concise summary of the conversation. Focus on information that would be helpful for continuing the conversation, especially what was in progress and what needs to happen next. Do not respond to any questions in the conversation, only output the summary."},{role:"user",content:`Below is an earlier conversation (${e.messageCount} messages) that needs to be summarized to free context space (current context limit: ${e.contextLimit} tokens).
|
|
13
13
|
|
|
14
14
|
--- Conversation Start ---
|
|
15
15
|
${e.conversationText}
|
|
16
16
|
--- Conversation End ---
|
|
17
17
|
|
|
18
|
-
`+lt}]},i=new AbortController;this.abortController=i;let c=!1;try{return await N((async()=>{const r=await N(fetch(se(n),{method:"POST",headers:s,body:JSON.stringify(a),signal:i.signal}),t.requestTimeoutMs,"ACode LLM summarization",()=>{c=!0,i.abort()});if(!r.ok){const g=await N(r.text(),t.requestTimeoutMs,"ACode LLM summarization error response body",()=>{c=!0,i.abort()}),y=g.length>500?`${g.slice(0,500)}...(truncated, total ${g.length} chars)`:g;throw new Error(`ACode LLM summarization request failed: ${r.status} ${y}`)}const m=(await N(r.json(),t.requestTimeoutMs,"ACode LLM summarization response body",()=>{c=!0,i.abort()})).choices?.[0]?.message?.content;if(!m||m.trim().length===0)throw new Error("ACode LLM summarization returned empty content");return m})(),t.requestTotalTimeoutMs,"ACode LLM summarization total",()=>{i.abort()})}catch(r){throw r instanceof Error&&r.name==="AbortError"?c?new Error(`ACode LLM summarization timed out after ${t.requestTimeoutMs}ms`):new Error("ACode LLM summarization aborted"):r}finally{this.abortController===i&&(this.abortController=null)}}async requestChatCompletion(e){const t=this.state.provider,n=t.baseURL;if(!n)throw new Error("ACode provider baseURL is not configured");const{contextLimit:s,maxOutput:a,inputLimit:i,threshold:c}=ue(t),r=this.state.accumulatedInputTokens+this.state.accumulatedOutputTokens,u=ee(this.state.conversation)+Ie(this.state.exposedTools),m=Math.max(r,u),g=m>=c;if(l(`[ACodeRuntime] \u{1F50D} Compaction check: actualUsed=${r}, estimation=${u}, effectiveUsed=${m}, contextLimit=${s}, inputLimit=${i??"n/a"}, maxOutput=${a}, triggerThreshold=${c}, shouldCompact=${g}`),g){const T=Q(this.state.conversation),A=`compaction-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;this.emitAcodeEvent("context_compaction",{action:"start",compactionId:A,sizeKB:T,estimatedTokens:u,actualInputTokens:this.state.accumulatedInputTokens,actualOutputTokens:this.state.accumulatedOutputTokens,messageCount:this.state.conversation.length,model:t.model,trigger:r>=c?"actual_usage":"estimated_overflow"}),this.emitCompactionPhase(A,"detect","\u68C0\u6D4B\u5230\u4E0A\u4E0B\u6587\u6EA2\u51FA",`\u6709\u6548\u7528\u91CF: ${m} tokens, \u9608\u503C: ${c}, \u89E6\u53D1: ${r>=c?"\u5B9E\u9645\u7528\u91CF":"\u4F30\u7B97\u6EA2\u51FA"}`);const x=await ye(this.state.conversation,s,a,{llmSummarizer:M=>this.summarizeConversationWithLLM(M),onPhase:M=>this.handleCompactionPhase(A,M),actualUsedTokens:m}),S=Q(this.state.conversation);this.emitAcodeEvent("context_compaction",{action:"end",compactionId:A,sizeBeforeKB:T,sizeAfterKB:S,freedKB:Math.round((T-S)*10)/10,strategy:x.strategy,freedTokens:x.freedTokens,tokensBefore:x.tokensBefore,tokensAfter:x.tokensAfter,prunedToolMessages:x.prunedToolMessages,summarizedMessages:x.summarizedMessages,messageCountBefore:x.messageCountBefore,messageCountAfter:x.messageCountAfter}),this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0}const y=await to(this.state.conversation,xe,s,a,T=>this.summarizeConversationWithLLM(T));y>0&&(this.emitAcodeEvent("warning",{message:`Conversation truncated: removed ${y} older messages to fit within ${xe} byte request size limit`}),this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0);const $=this.state.exposedTools.filter(T=>Qt(T,this.state.permissions,e)),p=()=>{const T={model:t.model,messages:this.state.conversation,stream:t.stream};return t.effort&&(T.reasoning_effort=t.effort),t.stream&&(T.stream_options={include_usage:!0}),$.length>0&&(T.tools=ze($),T.tool_choice="auto"),T},b="POST",d=Yt(n),f=mo(t),_=po(f),w=T=>{const A=JSON.stringify(T,null,2);l(`[ACodeRuntime] \u2605\u2605\u2605 DEBUG \u2605\u2605\u2605 emitAiRequest: \u53D1\u9001\u8BF7\u6C42 model=${t.model}, endpoint=${d}, messageCount=${this.state.conversation.length}`),this.emitAcodeEvent("ai_request",{model:t.model,endpoint:d,requestMethod:b,requestUrl:d,requestHeaders:_,stream:t.stream,messageCount:this.state.conversation.length,toolCount:$.length,lastUserMessagePreview:Vt(this.state.conversation),estimatedContextTokens:le(A),requestBody:A,...this.lastIterationUsage?{lastUsage:this.lastIterationUsage}:{}}),j(`ai_request emitted: model=${t.model} stream=${t.stream} messageCount=${this.state.conversation.length} toolCount=${$.length} estimatedContextTokens=${le(A)} lastUsage=${JSON.stringify(this.lastIterationUsage??null)}`)};let v=p();w(v);const C=t.requestRetryCount===ce,k=C?1/0:t.requestRetryCount+1;let R=!1;for(let T=1;C||T<=k;T+=1){this.state.terminated||(this.abortRequested=!1),T>1&&w(v);const A=new AbortController;this.abortController=A;let x=!1;try{return await N((async()=>{const S=await N(fetch(se(n),{method:b,headers:f,body:JSON.stringify(v),signal:A.signal}),t.requestFirstByteTimeoutMs,"ACode provider request",()=>{x=!0,A.abort()});if(!S.ok){const U=await N(S.text(),t.requestStreamIdleTimeoutMs,"ACode provider error response body",()=>{x=!0,A.abort()}),L=U.length>500?`${U.slice(0,500)}...(truncated, total ${U.length} chars)`:U;throw new Error(`ACode provider request failed: ${S.status} ${L}`)}const M=S.headers?.get?.("content-type")||"";if(t.stream&&M.toLowerCase().includes("text/event-stream"))return await this.readChatCompletionStream(S,t.requestStreamIdleTimeoutMs,A,t.requestFirstByteTimeoutMs);const P=await N(S.json(),t.requestStreamIdleTimeoutMs,"ACode provider response body",()=>{x=!0,A.abort()});return E(P)?P:(this.emitAcodeEvent("warning",{message:`ACode provider returned invalid response format (expected object, got ${typeof P}); treating as empty response`}),{})})(),t.requestTotalTimeoutMs,"ACode provider request total",()=>{A.abort()})}catch(S){let M;if(S instanceof Error&&S.name==="AbortError"?x?M=new Error(`ACode provider request timed out (firstByte=${t.requestFirstByteTimeoutMs}ms, idle=${t.requestStreamIdleTimeoutMs}ms)`):this.state.terminated?M=new Error("ACode provider request aborted by user"):M=new Error(`ACode provider request timed out (watchdog abort, firstByte=${t.requestFirstByteTimeoutMs}ms, idle=${t.requestStreamIdleTimeoutMs}ms)`):M=S instanceof Error?S:new Error(String(S)),!R&&!this.abortRequested&&!this.state.terminated&&Be(M)){const h=Q(this.state.conversation),F=ee(this.state.conversation),ie=this.state.conversation.length,q=`compaction-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;this.emitAcodeEvent("context_compaction",{action:"start",compactionId:q,sizeKB:h,estimatedTokens:F,messageCount:ie,model:t.model,trigger:"provider_overflow_error"}),this.emitCompactionPhase(q,"detect","Provider \u8FD4\u56DE\u4E0A\u4E0B\u6587\u6EA2\u51FA",`\u6D88\u606F: ${ie}\u6761, \u5927\u5C0F: ${h}KB`),this.emitCompactionPhase(q,"prune","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA","\u6B63\u5728\u88C1\u526A...");const H=mt(this.state.conversation,s);this.emitCompactionPhase(q,"prune_done","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA",`\u88C1\u526A\u4E86 ${H.prunedCount} \u6761\u5DE5\u5177\u8F93\u51FA, \u91CA\u653E ${H.freedTokens} tokens`),this.emitCompactionPhase(q,"summarize","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD","\u6B63\u5728\u6458\u8981...");const X=await Te(this.state.conversation,{llmSummarizer:We=>this.summarizeConversationWithLLM(We),contextLimit:s});this.emitCompactionPhase(q,"summarize_done","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD",`\u6458\u8981\u5316\u4E86 ${X} \u6761\u6D88\u606F`);const he=Q(this.state.conversation),Fe=ee(this.state.conversation),je=this.state.conversation.length;R=!0;const fe=H.prunedCount+X;if(this.emitAcodeEvent("context_compaction",{action:"end",compactionId:q,sizeBeforeKB:h,sizeAfterKB:he,freedKB:Math.round((h-he)*10)/10,tokensBefore:F,tokensAfter:Fe,messageCountBefore:ie,messageCountAfter:je,strategy:X>0?"summarize":"prune",freedTokens:H.freedTokens,prunedToolMessages:H.prunedCount,summarizedMessages:X,trigger:"provider_overflow_error"}),fe===0)throw M;this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0,this.emitAcodeEvent("warning",{message:`ACode provider context limit exceeded; summarized ${fe} older messages and rebuilt the request instead of retrying the same oversized payload`}),v=p(),w(v),T-=1;continue}if(!((C||T<k)&&!this.abortRequested&&!this.state.terminated&&oo(M)))throw M;this.emitAcodeEvent("error",{message:`ACode provider request attempt ${T}${C?"":`/${k}`} failed; retrying: ${M.message}`,nonFatal:!0});const U=Math.min($t,wt*Math.pow(2,T-1)),L=Math.random()*1e3;await Nt(U+L)}finally{this.abortController===A&&(this.abortController=null)}}throw new Error("ACode provider request failed after all retries")}async readChatCompletionStream(e,t,n,s){if(!e.body)throw new Error("ACode provider stream response has no body");const a=e.body.getReader(),i=new TextDecoder,c=new Map;let r="",u="",m;const g=$=>{for(const p of go($)){if(p==="[DONE]")continue;const b=JSON.parse(p);if(!E(b))continue;const d=b.usage;d&&(m=_t(m,d),j(`stream usage chunk received: ${JSON.stringify(d)} -> merged: ${JSON.stringify(m)}`));const _=b.choices?.[0]?.delta,w=ro(_);w&&this.emitAcodeEvent("thinking_delta",{text:w});const v=typeof _?.content=="string"?_.content:"";v&&(r+=v,this.emitAcodeEvent("assistant_delta",{text:v}));const C=Array.isArray(_?.tool_calls)?_.tool_calls:[];for(const k of C)yo(c,k)}};let y=!0;for(;;){const $=y&&s?s:t,{done:p,value:b}=await N(a.read(),$,y?"ACode provider stream first byte":"ACode provider stream idle",()=>n?.abort());if(y=!1,p)break;u+=i.decode(b,{stream:!0});const d=u.split(/\r?\n\r?\n/);u=d.pop()??"";for(const f of d)g(f)}return u+=i.decode(),u.trim()&&g(u),{choices:[{message:{content:r||null,tool_calls:To(c)}}],...m?{usage:m}:{}}}async handleToolCalls(e,t,n=this.contextVersion){l(`[ACodeRuntime] handleToolCalls started with ${e.length} tool(s)`);const s=i=>Ce(i.function.name)==="poll_message",a=[...e.filter(i=>!s(i)),...e.filter(i=>s(i))];e.length>1&&a.some((i,c)=>i!==e[c])&&l(`[ACodeRuntime] \u2195\uFE0F Reordered tool calls (poll_message \u2192 end): ${a.map(i=>i.function.name).join(", ")}`);for(const i of a){if(this.isTurnSuperseded(n)){l("[ACodeRuntime] \uFE0F Turn superseded, skipping remaining tools");return}const c=i.function.name;l(`[ACodeRuntime] \u{1F50D} Looking up tool: ${c}`);let r=this.state.exposedTools.find(u=>u.exposedName===c);if(r||(r=this.state.exposedTools.find(u=>u.toolName===c),r&&l(`[ACodeRuntime] \u21A9\uFE0F \u88F8\u540D\u56DE\u9000\u5339\u914D: ${c} -> ${r.exposedName}`)),!r){console.error(`[ACodeRuntime] \u274C Tool not found: ${c}, available tools: ${this.state.exposedTools.map(u=>u.exposedName).join(", ")}`),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:`ACode MCP tool not found: ${c}`});continue}l(`[ACodeRuntime] Found tool: ${r.toolName} (kind: ${r.kind}, source: ${r.kind==="mcp"?r.serverName:"builtin"})`);try{if(r.kind==="builtin"){const w=Ue(i.function.arguments);l(`[ACodeRuntime] Built-in tool arguments: ${JSON.stringify(w).slice(0,200)}`),pe(r,this.state.permissions,t);const v=await this.hookBus.dispatch("PreToolUse",{source:"builtin",toolName:r.toolName,exposedName:r.exposedName,arguments:w});if(this.isTurnSuperseded(n))return;const C=re(v);if(C)throw console.error(`[ACodeRuntime] \u274C Tool denied by hook: ${r.exposedName}: ${C.message}`),new Error(C.message||`ACode tool denied by hook: ${r.exposedName}`);l(`[ACodeRuntime] Emitting mcp_status call_tool for ${r.exposedName}`),this.emitAcodeEvent("mcp_status",{action:"call_tool",server:"builtin",tool:r.toolName,exposedName:r.exposedName,toolUseId:i.id,arguments:w,argumentsPreview:qe(w)});const k=Date.now(),R=Xt(r,w,this.state.provider.mcpToolTimeoutMs);l(`[ACodeRuntime] \u25B6\uFE0F Executing built-in tool: ${r.exposedName} (timeout: ${R}ms, abortRequested: ${this.abortRequested})`),R<=0&&console.warn(`[ACodeRuntime] \u26A0\uFE0F Built-in tool ${r.exposedName} has NO timeout (timeoutMs=${R})! If this tool hangs, executeSubmit will NEVER resolve, blocking the submit queue permanently.`);const T=this.beginToolSettle(r,w,i.id);try{const A=await N(r.execute(w,{onOutput:M=>{this.emitAcodeEvent("command_output",{server:"builtin",tool:r.toolName,exposedName:r.exposedName,toolUseId:i.id,stream:M.stream,text:M.text,sequence:M.sequence,cwd:M.cwd,commandLineCount:M.commandLineCount,...M.commandFilePath?{commandFilePath:M.commandFilePath}:{}})},toolCallId:i.id,...T.signal?{settleSignal:T.signal}:{}}),R,`ACode built-in tool ${r.toolName}`),x=Date.now()-k;if(l(`[ACodeRuntime] \u2705 Built-in tool ${r.exposedName} completed in ${x}ms`),this.isTurnSuperseded(n)||(await this.hookBus.dispatch("PostToolUse",{source:"builtin",toolName:r.toolName,exposedName:r.exposedName,result:D(A)}),this.isTurnSuperseded(n)))return;const S=D(A);l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status tool_result for ${r.exposedName}, result length: ${S.length} chars`),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:"builtin",tool:r.toolName,result:S}),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:S}),await this.appendSyntheticMultimodalUserMessage(S,i.id,n)}finally{this.clearToolSettle(T.seq)}continue}const u=this.state.mcpConnections.find(w=>w.serverName===r.serverName);if(!u){console.error(`[ACodeRuntime] \u274C MCP server not connected: ${r.serverName}, connected servers: ${this.state.mcpConnections.map(w=>w.serverName).join(", ")}`),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:`ACode MCP server not connected: ${r.serverName}`});continue}const m=Ue(i.function.arguments);l(`[ACodeRuntime] MCP tool arguments: ${JSON.stringify(m).slice(0,200)}`),pe(r,this.state.permissions,t);const g=await this.hookBus.dispatch("PreToolUse",{source:"mcp",serverName:r.serverName,toolName:r.toolName,exposedName:r.exposedName,arguments:m});if(this.isTurnSuperseded(n))return;const y=re(g);if(y)throw console.error(`[ACodeRuntime] \u274C MCP tool denied by hook: ${r.exposedName}: ${y.message}`),new Error(y.message||`ACode tool denied by hook: ${r.exposedName}`);l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status call_tool for MCP ${r.exposedName}`),this.emitAcodeEvent("mcp_status",{action:"call_tool",server:r.serverName,tool:r.toolName,exposedName:r.exposedName,toolUseId:i.id,arguments:m,argumentsPreview:qe(m)});const $=ft(r.toolName),p=$?this.state.provider.mcpPollingToolTimeoutMs:this.state.provider.mcpToolTimeoutMs,b=Date.now();l(`[ACodeRuntime] \u25B6\uFE0F Executing MCP tool: ${r.exposedName} (timeout: ${p}ms, polling: ${$})`);const d=$&&Ce(r.toolName)==="poll_message"?await this.callPollMessageWithSubAgentRace(u,r,m,p):await this.callMcpToolWithTimeout(u,r,m,p,$),f=Date.now()-b;if(l(`[ACodeRuntime] \u2705 MCP tool ${r.exposedName} completed in ${f}ms`),this.isTurnSuperseded(n)||(await this.hookBus.dispatch("PostToolUse",{source:"mcp",serverName:r.serverName,toolName:r.toolName,exposedName:r.exposedName,result:D(d)}),this.isTurnSuperseded(n)))return;const _=D(d);l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status tool_result for MCP ${r.exposedName}, result length: ${_.length} chars`),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:r.serverName,tool:r.toolName,result:_}),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:_}),await this.appendSyntheticMultimodalUserMessage(_,i.id,n)}catch(u){const m=u instanceof Error&&u.message.includes("timed out after"),g=u instanceof Error&&u.name==="AbortError";if(console.error(`[ACodeRuntime] \u274C Tool execution failed: ${i.function.name}`,`isTimeout=${m}, isAbortError=${g}`,u instanceof Error?u.message:String(u)),this.isTurnSuperseded(n)){l("[ACodeRuntime] \uFE0F Turn superseded after tool error, skipping");return}if(this.abortRequested||this.state.terminated)throw l(`[ACodeRuntime] \u26A0\uFE0F Rethrowing error due to abort/termination (abortRequested=${this.abortRequested}, terminated=${this.state.terminated})`),l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status tool_result (failed, aborted) for ${r.exposedName} before rethrow to close timeline entry`),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:r.kind==="mcp"?r.serverName:"builtin",tool:r.toolName,result:`[aborted] ${u instanceof Error?u.message:String(u)}`,failed:!0,aborted:!0}),u instanceof Error?u:new Error(String(u));l(`[ACodeRuntime] Calling appendToolFailureResult for ${r.exposedName} (isTimeout=${m})`),this.appendToolFailureResult(i,r,u)}}l(`[ACodeRuntime] handleToolCalls completed for all ${e.length} tool(s)`)}async callMcpToolWithTimeout(e,t,n,s,a){const i=s>0?s:bt,r=!!this.config.sharedMcpConnections?{...n,_aws_sub_agent:!0}:n;for(;;){const u=this.createRuntimeStopWaiter(`ACode MCP tool ${t.serverName}/${t.toolName}`);try{return await N(Promise.race([e.client.callTool({name:t.toolName,arguments:r},void 0,{timeout:i}),u.promise]),s,`ACode MCP tool ${t.serverName}/${t.toolName}`)}catch(m){if(!a||s>0||!no(m))throw m;this.emitAcodeEvent("warning",{message:`ACode MCP polling tool ${t.serverName}/${t.toolName} reached the MCP SDK timeout window and will continue polling.`})}finally{u.cancel()}}}static formatSubAgentCompletionAsPollResult(e){const t=e.map(n=>{const s=n.error?`Sub-agent ${n.subId} [${n.status}]: ${n.description}
|
|
18
|
+
`+lt}]},i=new AbortController;this.abortController=i;let c=!1;try{return await N((async()=>{const r=await N(fetch(se(n),{method:"POST",headers:s,body:JSON.stringify(a),signal:i.signal}),t.requestTimeoutMs,"ACode LLM summarization",()=>{c=!0,i.abort()});if(!r.ok){const g=await N(r.text(),t.requestTimeoutMs,"ACode LLM summarization error response body",()=>{c=!0,i.abort()}),y=g.length>500?`${g.slice(0,500)}...(truncated, total ${g.length} chars)`:g;throw new Error(`ACode LLM summarization request failed: ${r.status} ${y}`)}const m=(await N(r.json(),t.requestTimeoutMs,"ACode LLM summarization response body",()=>{c=!0,i.abort()})).choices?.[0]?.message?.content;if(!m||m.trim().length===0)throw new Error("ACode LLM summarization returned empty content");return m})(),t.requestTotalTimeoutMs,"ACode LLM summarization total",()=>{i.abort()})}catch(r){throw r instanceof Error&&r.name==="AbortError"?c?new Error(`ACode LLM summarization timed out after ${t.requestTimeoutMs}ms`):new Error("ACode LLM summarization aborted"):r}finally{this.abortController===i&&(this.abortController=null)}}async requestChatCompletion(e){const t=this.state.provider,n=t.baseURL;if(!n)throw new Error("ACode provider baseURL is not configured");const{contextLimit:s,maxOutput:a,inputLimit:i,threshold:c}=ue(t),r=this.state.accumulatedInputTokens+this.state.accumulatedOutputTokens,u=ee(this.state.conversation)+Ie(this.state.exposedTools),m=Math.max(r,u),g=m>=c;if(l(`[ACodeRuntime] \u{1F50D} Compaction check: actualUsed=${r}, estimation=${u}, effectiveUsed=${m}, contextLimit=${s}, inputLimit=${i??"n/a"}, maxOutput=${a}, triggerThreshold=${c}, shouldCompact=${g}`),g){const T=Q(this.state.conversation),A=`compaction-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;this.emitAcodeEvent("context_compaction",{action:"start",compactionId:A,sizeKB:T,estimatedTokens:u,actualInputTokens:this.state.accumulatedInputTokens,actualOutputTokens:this.state.accumulatedOutputTokens,messageCount:this.state.conversation.length,model:t.model,trigger:r>=c?"actual_usage":"estimated_overflow"}),this.emitCompactionPhase(A,"detect","\u68C0\u6D4B\u5230\u4E0A\u4E0B\u6587\u6EA2\u51FA",`\u6709\u6548\u7528\u91CF: ${m} tokens, \u9608\u503C: ${c}, \u89E6\u53D1: ${r>=c?"\u5B9E\u9645\u7528\u91CF":"\u4F30\u7B97\u6EA2\u51FA"}`);const x=await ye(this.state.conversation,s,a,{llmSummarizer:M=>this.summarizeConversationWithLLM(M),onPhase:M=>this.handleCompactionPhase(A,M),actualUsedTokens:m}),S=Q(this.state.conversation);this.emitAcodeEvent("context_compaction",{action:"end",compactionId:A,sizeBeforeKB:T,sizeAfterKB:S,freedKB:Math.round((T-S)*10)/10,strategy:x.strategy,freedTokens:x.freedTokens,tokensBefore:x.tokensBefore,tokensAfter:x.tokensAfter,prunedToolMessages:x.prunedToolMessages,summarizedMessages:x.summarizedMessages,messageCountBefore:x.messageCountBefore,messageCountAfter:x.messageCountAfter}),this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0}const y=await to(this.state.conversation,xe,s,a,T=>this.summarizeConversationWithLLM(T));y>0&&(this.emitAcodeEvent("warning",{message:`Conversation truncated: removed ${y} older messages to fit within ${xe} byte request size limit`}),this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0);const $=this.state.exposedTools.filter(T=>Qt(T,this.state.permissions,e)),p=()=>{const T={model:t.model,messages:this.state.conversation,stream:t.stream};return t.effort&&(T.reasoning_effort=t.effort),t.stream&&(T.stream_options={include_usage:!0}),$.length>0&&(T.tools=ze($),T.tool_choice="auto"),T},b="POST",d=Yt(n),f=mo(t),_=po(f),w=T=>{const A=JSON.stringify(T,null,2);l(`[ACodeRuntime] \u2605\u2605\u2605 DEBUG \u2605\u2605\u2605 emitAiRequest: \u53D1\u9001\u8BF7\u6C42 model=${t.model}, endpoint=${d}, messageCount=${this.state.conversation.length}`),this.emitAcodeEvent("ai_request",{model:t.model,endpoint:d,requestMethod:b,requestUrl:d,requestHeaders:_,stream:t.stream,messageCount:this.state.conversation.length,toolCount:$.length,lastUserMessagePreview:Vt(this.state.conversation),estimatedContextTokens:le(A),requestBody:A,...this.lastIterationUsage?{lastUsage:this.lastIterationUsage}:{}}),j(`ai_request emitted: model=${t.model} stream=${t.stream} messageCount=${this.state.conversation.length} toolCount=${$.length} estimatedContextTokens=${le(A)} lastUsage=${JSON.stringify(this.lastIterationUsage??null)}`)};let v=p();w(v);const C=t.requestRetryCount===ce,k=C?1/0:t.requestRetryCount+1;let R=!1;for(let T=1;C||T<=k;T+=1){this.state.terminated||(this.abortRequested=!1),T>1&&w(v);const A=new AbortController;this.abortController=A;let x=!1;try{return await N((async()=>{const S=await N(fetch(se(n),{method:b,headers:f,body:JSON.stringify(v),signal:A.signal}),t.requestFirstByteTimeoutMs,"ACode provider request",()=>{x=!0,A.abort()});if(!S.ok){const U=await N(S.text(),t.requestStreamIdleTimeoutMs,"ACode provider error response body",()=>{x=!0,A.abort()}),D=U.length>500?`${U.slice(0,500)}...(truncated, total ${U.length} chars)`:U;throw new Error(`ACode provider request failed: ${S.status} ${D}`)}const M=S.headers?.get?.("content-type")||"";if(t.stream&&M.toLowerCase().includes("text/event-stream"))return await this.readChatCompletionStream(S,t.requestStreamIdleTimeoutMs,A,t.requestFirstByteTimeoutMs);const P=await N(S.json(),t.requestStreamIdleTimeoutMs,"ACode provider response body",()=>{x=!0,A.abort()});return E(P)?P:(this.emitAcodeEvent("warning",{message:`ACode provider returned invalid response format (expected object, got ${typeof P}); treating as empty response`}),{})})(),t.requestTotalTimeoutMs,"ACode provider request total",()=>{A.abort()})}catch(S){let M;if(S instanceof Error&&S.name==="AbortError"?x?M=new Error(`ACode provider request timed out (firstByte=${t.requestFirstByteTimeoutMs}ms, idle=${t.requestStreamIdleTimeoutMs}ms)`):this.state.terminated?M=new Error("ACode provider request aborted by user"):M=new Error(`ACode provider request timed out (watchdog abort, firstByte=${t.requestFirstByteTimeoutMs}ms, idle=${t.requestStreamIdleTimeoutMs}ms)`):M=S instanceof Error?S:new Error(String(S)),!R&&!this.abortRequested&&!this.state.terminated&&Be(M)){const h=Q(this.state.conversation),F=ee(this.state.conversation),ie=this.state.conversation.length,q=`compaction-${Date.now()}-${Math.random().toString(36).slice(2,8)}`;this.emitAcodeEvent("context_compaction",{action:"start",compactionId:q,sizeKB:h,estimatedTokens:F,messageCount:ie,model:t.model,trigger:"provider_overflow_error"}),this.emitCompactionPhase(q,"detect","Provider \u8FD4\u56DE\u4E0A\u4E0B\u6587\u6EA2\u51FA",`\u6D88\u606F: ${ie}\u6761, \u5927\u5C0F: ${h}KB`),this.emitCompactionPhase(q,"prune","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA","\u6B63\u5728\u88C1\u526A...");const H=mt(this.state.conversation,s);this.emitCompactionPhase(q,"prune_done","\u9636\u6BB51: \u88C1\u526A\u5DE5\u5177\u8F93\u51FA",`\u88C1\u526A\u4E86 ${H.prunedCount} \u6761\u5DE5\u5177\u8F93\u51FA, \u91CA\u653E ${H.freedTokens} tokens`),this.emitCompactionPhase(q,"summarize","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD","\u6B63\u5728\u6458\u8981...");const X=await Te(this.state.conversation,{llmSummarizer:We=>this.summarizeConversationWithLLM(We),contextLimit:s});this.emitCompactionPhase(q,"summarize_done","\u9636\u6BB52: \u6458\u8981\u5316\u5BF9\u8BDD",`\u6458\u8981\u5316\u4E86 ${X} \u6761\u6D88\u606F`);const he=Q(this.state.conversation),Fe=ee(this.state.conversation),je=this.state.conversation.length;R=!0;const fe=H.prunedCount+X;if(this.emitAcodeEvent("context_compaction",{action:"end",compactionId:q,sizeBeforeKB:h,sizeAfterKB:he,freedKB:Math.round((h-he)*10)/10,tokensBefore:F,tokensAfter:Fe,messageCountBefore:ie,messageCountAfter:je,strategy:X>0?"summarize":"prune",freedTokens:H.freedTokens,prunedToolMessages:H.prunedCount,summarizedMessages:X,trigger:"provider_overflow_error"}),fe===0)throw M;this.state.accumulatedInputTokens=0,this.state.accumulatedOutputTokens=0,this.emitAcodeEvent("warning",{message:`ACode provider context limit exceeded; summarized ${fe} older messages and rebuilt the request instead of retrying the same oversized payload`}),v=p(),w(v),T-=1;continue}if(!((C||T<k)&&!this.abortRequested&&!this.state.terminated&&oo(M)))throw M;this.emitAcodeEvent("error",{message:`ACode provider request attempt ${T}${C?"":`/${k}`} failed; retrying: ${M.message}`,nonFatal:!0});const U=Math.min($t,wt*Math.pow(2,T-1)),D=Math.random()*1e3;await Nt(U+D)}finally{this.abortController===A&&(this.abortController=null)}}throw new Error("ACode provider request failed after all retries")}async readChatCompletionStream(e,t,n,s){if(!e.body)throw new Error("ACode provider stream response has no body");const a=e.body.getReader(),i=new TextDecoder,c=new Map;let r="",u="",m;const g=$=>{for(const p of go($)){if(p==="[DONE]")continue;const b=JSON.parse(p);if(!E(b))continue;const d=b.usage;d&&(m=_t(m,d),j(`stream usage chunk received: ${JSON.stringify(d)} -> merged: ${JSON.stringify(m)}`));const _=b.choices?.[0]?.delta,w=ro(_);w&&this.emitAcodeEvent("thinking_delta",{text:w});const v=typeof _?.content=="string"?_.content:"";v&&(r+=v,this.emitAcodeEvent("assistant_delta",{text:v}));const C=Array.isArray(_?.tool_calls)?_.tool_calls:[];for(const k of C)yo(c,k)}};let y=!0;for(;;){const $=y&&s?s:t,{done:p,value:b}=await N(a.read(),$,y?"ACode provider stream first byte":"ACode provider stream idle",()=>n?.abort());if(y=!1,p)break;u+=i.decode(b,{stream:!0});const d=u.split(/\r?\n\r?\n/);u=d.pop()??"";for(const f of d)g(f)}return u+=i.decode(),u.trim()&&g(u),{choices:[{message:{content:r||null,tool_calls:To(c)}}],...m?{usage:m}:{}}}async handleToolCalls(e,t,n=this.contextVersion){l(`[ACodeRuntime] handleToolCalls started with ${e.length} tool(s)`);const s=i=>Ce(i.function.name)==="poll_message",a=[...e.filter(i=>!s(i)),...e.filter(i=>s(i))];e.length>1&&a.some((i,c)=>i!==e[c])&&l(`[ACodeRuntime] \u2195\uFE0F Reordered tool calls (poll_message \u2192 end): ${a.map(i=>i.function.name).join(", ")}`);for(const i of a){if(this.isTurnSuperseded(n)){l("[ACodeRuntime] \uFE0F Turn superseded, skipping remaining tools");return}const c=i.function.name;l(`[ACodeRuntime] \u{1F50D} Looking up tool: ${c}`);let r=this.state.exposedTools.find(u=>u.exposedName===c);if(r||(r=this.state.exposedTools.find(u=>u.toolName===c),r&&l(`[ACodeRuntime] \u21A9\uFE0F \u88F8\u540D\u56DE\u9000\u5339\u914D: ${c} -> ${r.exposedName}`)),!r){console.error(`[ACodeRuntime] \u274C Tool not found: ${c}, available tools: ${this.state.exposedTools.map(u=>u.exposedName).join(", ")}`),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:`ACode MCP tool not found: ${c}`});continue}l(`[ACodeRuntime] Found tool: ${r.toolName} (kind: ${r.kind}, source: ${r.kind==="mcp"?r.serverName:"builtin"})`);try{if(r.kind==="builtin"){const w=Ue(i.function.arguments);l(`[ACodeRuntime] Built-in tool arguments: ${JSON.stringify(w).slice(0,200)}`),pe(r,this.state.permissions,t);const v=await this.hookBus.dispatch("PreToolUse",{source:"builtin",toolName:r.toolName,exposedName:r.exposedName,arguments:w});if(this.isTurnSuperseded(n))return;const C=re(v);if(C)throw console.error(`[ACodeRuntime] \u274C Tool denied by hook: ${r.exposedName}: ${C.message}`),new Error(C.message||`ACode tool denied by hook: ${r.exposedName}`);l(`[ACodeRuntime] Emitting mcp_status call_tool for ${r.exposedName}`),this.emitAcodeEvent("mcp_status",{action:"call_tool",server:"builtin",tool:r.toolName,exposedName:r.exposedName,toolUseId:i.id,arguments:w,argumentsPreview:qe(w)});const k=Date.now(),R=Xt(r,w,this.state.provider.mcpToolTimeoutMs);l(`[ACodeRuntime] \u25B6\uFE0F Executing built-in tool: ${r.exposedName} (timeout: ${R}ms, abortRequested: ${this.abortRequested})`),R<=0&&console.warn(`[ACodeRuntime] \u26A0\uFE0F Built-in tool ${r.exposedName} has NO timeout (timeoutMs=${R})! If this tool hangs, executeSubmit will NEVER resolve, blocking the submit queue permanently.`);const T=this.beginToolSettle(r,w,i.id);try{const A=await N(r.execute(w,{onOutput:M=>{this.emitAcodeEvent("command_output",{server:"builtin",tool:r.toolName,exposedName:r.exposedName,toolUseId:i.id,stream:M.stream,text:M.text,sequence:M.sequence,cwd:M.cwd,commandLineCount:M.commandLineCount,...M.commandFilePath?{commandFilePath:M.commandFilePath}:{}})},toolCallId:i.id,...T.signal?{settleSignal:T.signal}:{}}),R,`ACode built-in tool ${r.toolName}`),x=Date.now()-k;if(l(`[ACodeRuntime] \u2705 Built-in tool ${r.exposedName} completed in ${x}ms`),this.isTurnSuperseded(n)||(await this.hookBus.dispatch("PostToolUse",{source:"builtin",toolName:r.toolName,exposedName:r.exposedName,result:O(A)}),this.isTurnSuperseded(n)))return;const S=O(A);l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status tool_result for ${r.exposedName}, result length: ${S.length} chars`),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:"builtin",tool:r.toolName,result:S}),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:S}),await this.appendSyntheticMultimodalUserMessage(S,i.id,n)}finally{this.clearToolSettle(T.seq)}continue}const u=this.state.mcpConnections.find(w=>w.serverName===r.serverName);if(!u){console.error(`[ACodeRuntime] \u274C MCP server not connected: ${r.serverName}, connected servers: ${this.state.mcpConnections.map(w=>w.serverName).join(", ")}`),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:`ACode MCP server not connected: ${r.serverName}`});continue}const m=Ue(i.function.arguments);l(`[ACodeRuntime] MCP tool arguments: ${JSON.stringify(m).slice(0,200)}`),pe(r,this.state.permissions,t);const g=await this.hookBus.dispatch("PreToolUse",{source:"mcp",serverName:r.serverName,toolName:r.toolName,exposedName:r.exposedName,arguments:m});if(this.isTurnSuperseded(n))return;const y=re(g);if(y)throw console.error(`[ACodeRuntime] \u274C MCP tool denied by hook: ${r.exposedName}: ${y.message}`),new Error(y.message||`ACode tool denied by hook: ${r.exposedName}`);l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status call_tool for MCP ${r.exposedName}`),this.emitAcodeEvent("mcp_status",{action:"call_tool",server:r.serverName,tool:r.toolName,exposedName:r.exposedName,toolUseId:i.id,arguments:m,argumentsPreview:qe(m)});const $=ft(r.toolName),p=$?this.state.provider.mcpPollingToolTimeoutMs:this.state.provider.mcpToolTimeoutMs,b=Date.now();l(`[ACodeRuntime] \u25B6\uFE0F Executing MCP tool: ${r.exposedName} (timeout: ${p}ms, polling: ${$})`);const d=$&&Ce(r.toolName)==="poll_message"?await this.callPollMessageWithSubAgentRace(u,r,m,p):await this.callMcpToolWithTimeout(u,r,m,p,$),f=Date.now()-b;if(l(`[ACodeRuntime] \u2705 MCP tool ${r.exposedName} completed in ${f}ms`),this.isTurnSuperseded(n)||(await this.hookBus.dispatch("PostToolUse",{source:"mcp",serverName:r.serverName,toolName:r.toolName,exposedName:r.exposedName,result:O(d)}),this.isTurnSuperseded(n)))return;const _=O(d);l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status tool_result for MCP ${r.exposedName}, result length: ${_.length} chars`),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:r.serverName,tool:r.toolName,result:_}),this.state.conversation.push({role:"tool",tool_call_id:i.id,content:_}),await this.appendSyntheticMultimodalUserMessage(_,i.id,n)}catch(u){const m=u instanceof Error&&u.message.includes("timed out after"),g=u instanceof Error&&u.name==="AbortError";if(console.error(`[ACodeRuntime] \u274C Tool execution failed: ${i.function.name}`,`isTimeout=${m}, isAbortError=${g}`,u instanceof Error?u.message:String(u)),this.isTurnSuperseded(n)){l("[ACodeRuntime] \uFE0F Turn superseded after tool error, skipping");return}if(this.abortRequested||this.state.terminated)throw l(`[ACodeRuntime] \u26A0\uFE0F Rethrowing error due to abort/termination (abortRequested=${this.abortRequested}, terminated=${this.state.terminated})`),l(`[ACodeRuntime] \u{1F4E4} Emitting mcp_status tool_result (failed, aborted) for ${r.exposedName} before rethrow to close timeline entry`),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:r.kind==="mcp"?r.serverName:"builtin",tool:r.toolName,result:`[aborted] ${u instanceof Error?u.message:String(u)}`,failed:!0,aborted:!0}),u instanceof Error?u:new Error(String(u));l(`[ACodeRuntime] Calling appendToolFailureResult for ${r.exposedName} (isTimeout=${m})`),this.appendToolFailureResult(i,r,u)}}l(`[ACodeRuntime] handleToolCalls completed for all ${e.length} tool(s)`)}async callMcpToolWithTimeout(e,t,n,s,a){const i=s>0?s:bt,r=!!this.config.sharedMcpConnections?{...n,_aws_sub_agent:!0}:n;for(;;){const u=this.createRuntimeStopWaiter(`ACode MCP tool ${t.serverName}/${t.toolName}`);try{return await N(Promise.race([e.client.callTool({name:t.toolName,arguments:r},void 0,{timeout:i}),u.promise]),s,`ACode MCP tool ${t.serverName}/${t.toolName}`)}catch(m){if(!a||s>0||!no(m))throw m;this.emitAcodeEvent("warning",{message:`ACode MCP polling tool ${t.serverName}/${t.toolName} reached the MCP SDK timeout window and will continue polling.`})}finally{u.cancel()}}}static formatSubAgentCompletionAsPollResult(e){const t=e.map(n=>{const s=n.error?`Sub-agent ${n.subId} [${n.status}]: ${n.description}
|
|
19
19
|
Error: ${n.error}`:`Sub-agent ${n.subId} [${n.status}]: ${n.description}
|
|
20
|
-
Output: ${n.output.length>500?n.output.slice(0,500)+"... (truncated)":n.output}`;return{msgId:`subagent-completion-${n.subId}-${Date.now()}`,senderId:"ACode-Runtime",senderName:"\u7CFB\u7EDF",content:s,requireReply:!1,prompt:`Sub-agent ${n.subId} (${n.description}) has reached terminal state: ${n.status}. Review its output and decide next steps.`,timestamp:new Date().toISOString()}});return{content:[{type:"text",text:JSON.stringify({directMessages:t,groupMessages:[],fromBuffer:!1,systemMessages:[]},null,2)}]}}static mergeSubAgentCompletionsIntoMcpResult(e,t){if(t.length===0)return e;const n=
|
|
20
|
+
Output: ${n.output.length>500?n.output.slice(0,500)+"... (truncated)":n.output}`;return{msgId:`subagent-completion-${n.subId}-${Date.now()}`,senderId:"ACode-Runtime",senderName:"\u7CFB\u7EDF",content:s,requireReply:!1,prompt:`Sub-agent ${n.subId} (${n.description}) has reached terminal state: ${n.status}. Review its output and decide next steps.`,timestamp:new Date().toISOString()}});return{content:[{type:"text",text:JSON.stringify({directMessages:t,groupMessages:[],fromBuffer:!1,systemMessages:[]},null,2)}]}}static mergeSubAgentCompletionsIntoMcpResult(e,t){if(t.length===0)return e;const n=O(e);let s;try{s=JSON.parse(n)}catch{return e}const i=[...t.map(c=>{const r=c.error?`Sub-agent ${c.subId} [${c.status}]: ${c.description}
|
|
21
21
|
Error: ${c.error}`:`Sub-agent ${c.subId} [${c.status}]: ${c.description}
|
|
22
|
-
Output: ${c.output.length>500?c.output.slice(0,500)+"... (truncated)":c.output}`;return{msgId:`subagent-completion-${c.subId}-${Date.now()}`,senderId:"ACode-Runtime",senderName:"\u7CFB\u7EDF",content:r,requireReply:!1,prompt:`Sub-agent ${c.subId} (${c.description}) has reached terminal state: ${c.status}. Review its output and decide next steps.`,timestamp:new Date().toISOString()}}),...s.directMessages??[]];return{content:[{type:"text",text:JSON.stringify({...s,directMessages:i},null,2)}]}}async recheckMemoryDelegate(e,t,n,s){const a=this.state.mcpConnections.find(i=>i.serverName==="aws-client-agent-mcp");if(!a)return l("[ACodeRuntime] recheckMemoryDelegate: aws-client-agent-mcp connection not found, treating as done"),{done:!0};try{return e==="memory_file_change"?await this.recheckMemoryFileChange(a,t):e==="memory_expiration"?await this.recheckMemoryExpiration(a,t):await this.recheckMemoryCleanup(a,t)}catch(i){return l(`[ACodeRuntime] recheckMemoryDelegate failed (type=${e}, turn=${s}): ${i instanceof Error?i.message:String(i)}, treating as NOT done (retry via recheck loop)`),{done:!1,continueMessage:`\u7ECF\u68C0\u67E5\uFF0C\u65E0\u6CD5\u786E\u8BA4\u4EE5\u4E0B memory \u662F\u5426\u5DF2\u5904\u7406\uFF08\u6821\u9A8C\u5F02\u5E38\uFF1A${i instanceof Error?i.message:String(i)}\uFF09\u3002\u8BF7\u91CD\u65B0\u6267\u884C\u9A8C\u8BC1\u6B65\u9AA4\uFF08read_memory / get_memory_stats\uFF09\u786E\u8BA4\u771F\u5B9E\u72B6\u6001\u540E\u518D\u7EE7\u7EED\u3002`}}}async readMemoryViaMcp(e,t){try{const n=await N(e.client.callTool({name:"read_memory",arguments:{uri:t,_aws_sub_agent:!0}},void 0,{timeout:3e4}),3e4,`recheck read_memory ${t}`),s=
|
|
22
|
+
Output: ${c.output.length>500?c.output.slice(0,500)+"... (truncated)":c.output}`;return{msgId:`subagent-completion-${c.subId}-${Date.now()}`,senderId:"ACode-Runtime",senderName:"\u7CFB\u7EDF",content:r,requireReply:!1,prompt:`Sub-agent ${c.subId} (${c.description}) has reached terminal state: ${c.status}. Review its output and decide next steps.`,timestamp:new Date().toISOString()}}),...s.directMessages??[]];return{content:[{type:"text",text:JSON.stringify({...s,directMessages:i},null,2)}]}}async recheckMemoryDelegate(e,t,n,s){const a=this.state.mcpConnections.find(i=>i.serverName==="aws-client-agent-mcp");if(!a)return l("[ACodeRuntime] recheckMemoryDelegate: aws-client-agent-mcp connection not found, treating as done"),{done:!0};try{return e==="memory_file_change"?await this.recheckMemoryFileChange(a,t):e==="memory_expiration"?await this.recheckMemoryExpiration(a,t):await this.recheckMemoryCleanup(a,t)}catch(i){return l(`[ACodeRuntime] recheckMemoryDelegate failed (type=${e}, turn=${s}): ${i instanceof Error?i.message:String(i)}, treating as NOT done (retry via recheck loop)`),{done:!1,continueMessage:`\u7ECF\u68C0\u67E5\uFF0C\u65E0\u6CD5\u786E\u8BA4\u4EE5\u4E0B memory \u662F\u5426\u5DF2\u5904\u7406\uFF08\u6821\u9A8C\u5F02\u5E38\uFF1A${i instanceof Error?i.message:String(i)}\uFF09\u3002\u8BF7\u91CD\u65B0\u6267\u884C\u9A8C\u8BC1\u6B65\u9AA4\uFF08read_memory / get_memory_stats\uFF09\u786E\u8BA4\u771F\u5B9E\u72B6\u6001\u540E\u518D\u7EE7\u7EED\u3002`}}}async readMemoryViaMcp(e,t){try{const n=await N(e.client.callTool({name:"read_memory",arguments:{uri:t,_aws_sub_agent:!0}},void 0,{timeout:3e4}),3e4,`recheck read_memory ${t}`),s=O(n),a=JSON.parse(s);return a==null||typeof a!="object"||Array.isArray(a)?null:a}catch(n){throw n}}async recheckMemoryFileChange(e,t){const n=[];if(await Promise.all(t.map(async a=>{const i=await this.readMemoryViaMcp(e,a);i&&i.memory_status==="updating"&&n.push(a)})),n.length===0)return l(`[ACodeRuntime] recheck memory_file_change: all ${t.length} URIs done (memory_status=normal or deleted)`),{done:!0};const s=n.map(a=>`- ${a}`).join(`
|
|
23
23
|
`);return l(`[ACodeRuntime] recheck memory_file_change: ${n.length}/${t.length} URIs still updating`),{done:!1,continueMessage:`\u7ECF\u68C0\u67E5\uFF0C\u672A\u5B8C\u6210\u5BF9\u4EE5\u4E0B memory \u7684\u66F4\u65B0\uFF0C\u8BF7\u7EE7\u7EED\uFF1A
|
|
24
24
|
${s}
|
|
25
25
|
|
|
@@ -27,7 +27,7 @@ ${s}
|
|
|
27
27
|
`);return l(`[ACodeRuntime] recheck memory_expiration: ${n.length}/${t.length} URIs still expired or recalling`),{done:!1,continueMessage:`\u7ECF\u68C0\u67E5\uFF0C\u672A\u5B8C\u6210\u5BF9\u4EE5\u4E0B memory \u7684\u5904\u7406\uFF0C\u8BF7\u7EE7\u7EED\uFF1A
|
|
28
28
|
${a}
|
|
29
29
|
|
|
30
|
-
\u8FD9\u4E9B memory \u4ECD\u5904\u4E8E\u8FC7\u671F\u6216\u56DE\u5FC6\u4E2D\u72B6\u6001\u3002\u8BF7\u8C03\u7528 memory_recurrence \u83B7\u53D6\u5185\u5BB9\uFF0C\u5BF9\u9700\u8981\u4FDD\u7559\u7684\u8C03\u7528 update_memory \u7EED\u671F\uFF0C\u5BF9\u65E0\u4EF7\u503C\u7684\u8C03\u7528 memory_recurrence_complete \u6E05\u7406\u3002`}}async recheckMemoryCleanup(e,t){let n;try{const c=await N(e.client.callTool({name:"get_memory_stats",arguments:{_aws_sub_agent:!0}},void 0,{timeout:3e4}),3e4,"recheck get_memory_stats"),r=
|
|
30
|
+
\u8FD9\u4E9B memory \u4ECD\u5904\u4E8E\u8FC7\u671F\u6216\u56DE\u5FC6\u4E2D\u72B6\u6001\u3002\u8BF7\u8C03\u7528 memory_recurrence \u83B7\u53D6\u5185\u5BB9\uFF0C\u5BF9\u9700\u8981\u4FDD\u7559\u7684\u8C03\u7528 update_memory \u7EED\u671F\uFF0C\u5BF9\u65E0\u4EF7\u503C\u7684\u8C03\u7528 memory_recurrence_complete \u6E05\u7406\u3002`}}async recheckMemoryCleanup(e,t){let n;try{const c=await N(e.client.callTool({name:"get_memory_stats",arguments:{_aws_sub_agent:!0}},void 0,{timeout:3e4}),3e4,"recheck get_memory_stats"),r=O(c);n=JSON.parse(r)}catch{return{done:!1,continueMessage:"get_memory_stats \u67E5\u8BE2\u5931\u8D25\uFF0C\u65E0\u6CD5\u786E\u8BA4\u5BB9\u91CF\u662F\u5426\u5DF2\u8FBE\u6807\uFF0C\u8BF7\u91CD\u65B0\u9A8C\u8BC1\u540E\u518D\u7EE7\u7EED\u3002"}}const s=n.by_type??{},a=[];for(const c of t){const r=c.indexOf("/");if(r<0)continue;const u=c.slice(0,r),m=c.slice(r+1),y=s[u]?.by_domain?.[m];y?.exceeded&&a.push(`${u}/${m} (\u5F53\u524D ${y.count??"?"} \u6761\uFF0C\u5EFA\u8BAE\u4E0A\u9650 ${y.limit??"?"})`)}if(a.length===0)return l(`[ACodeRuntime] recheck memory_cleanup: all ${t.length} type/domain pairs within limits`),{done:!0};const i=a.map(c=>`- ${c}`).join(`
|
|
31
31
|
`);return l(`[ACodeRuntime] recheck memory_cleanup: ${a.length}/${t.length} type/domain pairs still exceeded`),{done:!1,continueMessage:`\u7ECF\u68C0\u67E5\uFF0C\u4EE5\u4E0B\u7C7B\u578B/\u57DF\u5C1A\u672A\u6574\u7406\u5B8C\u6210\uFF0C\u8BF7\u7EE7\u7EED\uFF1A
|
|
32
32
|
${i}
|
|
33
33
|
|
|
@@ -37,18 +37,26 @@ ${i}
|
|
|
37
37
|
\u5F53\u524D\u6709\u4EE5\u4E0B ${e.uris.length} \u6761\u5373\u5C06\u9057\u5FD8\u7684\u8BB0\u5FC6\u9700\u8981\u6574\u7406\uFF1A
|
|
38
38
|
${t}
|
|
39
39
|
|
|
40
|
-
\u8BF7\u6267\u884C\u4EE5\u4E0B\u6B65\u9AA4\uFF08\u6838\u5FC3\u903B\u8F91\uFF1A\u5904\u7406 \u2192 \u9A8C\u8BC1 \u2192 \u672A\u5B8C\u6210\u5219\u7EE7\u7EED\uFF09\uFF1A
|
|
40
|
+
\u8BF7\u6267\u884C\u4EE5\u4E0B\u6B65\u9AA4\uFF08\u6838\u5FC3\u903B\u8F91\uFF1A\u8BA4\u9886 \u2192 \u5206\u6279\u5904\u7406 \u2192 \u9A8C\u8BC1 \u2192 \u672A\u5B8C\u6210\u5219\u7EE7\u7EED\uFF09\uFF1A
|
|
41
41
|
|
|
42
42
|
=== \u5904\u7406\u9636\u6BB5 ===
|
|
43
|
-
1. \u8C03\u7528 memory_recurrence(limit=10) \u5206\u6279\u83B7\u53D6\u8FD9\u4E9B\u8FC7\u671F\u8BB0\u5FC6\u7684\u5B8C\u6574\u5185\u5BB9\uFF08\u6700\u65E9\u8FC7\u671F\u4F18\u5148\uFF09
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
1. \u8C03\u7528 memory_recurrence(limit=10) \u5206\u6279\u83B7\u53D6\u8FD9\u4E9B\u8FC7\u671F\u8BB0\u5FC6\u7684\u5B8C\u6574\u5185\u5BB9\uFF08\u6700\u65E9\u8FC7\u671F\u4F18\u5148\uFF09\u3002
|
|
44
|
+
\u7CFB\u7EDF\u5DF2\u539F\u5B50\u8BA4\u9886\u8FD9\u4E9B\u8BB0\u5FC6\uFF08\u5355\u6B21\u6700\u591A 50 \u6761\uFF0C\u542B\u8BA4\u9886\u79DF\u7EA6\uFF09\uFF0C\u5176\u4ED6\u5B9E\u4F8B\u4E0D\u4F1A\u91CD\u590D\u5904\u7406\uFF1B
|
|
45
|
+
\u5DF2\u7EED\u671F\u7684\u8BB0\u5FC6\uFF08expiresAt \u5728\u672A\u6765\uFF09\u4E0D\u4F1A\u518D\u6B21\u51FA\u73B0
|
|
46
|
+
2. \u9010\u6761\u5206\u6790\uFF1A\u8FD9\u4E9B\u5185\u5BB9\u662F\u5426\u4ECD\u7136\u6709\u4EF7\u503C\uFF1F\u662F\u5426\u9700\u8981\u4FDD\u7559\u3001\u66F4\u65B0\u6216\u5220\u9664\uFF1F
|
|
47
|
+
3. \u9010\u6761\u51B3\u7B56\uFF1A
|
|
48
|
+
- \u6709\u4EF7\u503C\u7684 \u2192 \u8C03\u7528 update_memory \u7EED\u671F\uFF08TTL \xD72\uFF0C\u7EED\u671F\u540E\u4E0D\u4F1A\u88AB\u5220\u9664\uFF09
|
|
49
|
+
- \u9700\u8981\u957F\u671F\u4FDD\u5B58\u7684 \u2192 \u5148\u8C03\u7528 create_memory \u521B\u5EFA\u6C38\u4E45\u7C7B\u578B\u8BB0\u5FC6\uFF08\u5982 project_memory\uFF09\u4FDD\u5B58\u5185\u5BB9\uFF0C\u65E7\u6761\u76EE\u4E0D\u7EED\u671F
|
|
50
|
+
- \u786E\u65E0\u4EF7\u503C\u7684 \u2192 \u4E0D\u7EED\u671F\uFF08\u4FDD\u6301[\u56DE\u5FC6\u4E2D]\u72B6\u6001\uFF0C\u7B49\u5F85\u7EDF\u4E00\u6E05\u7406\uFF09
|
|
51
|
+
4. \u672C\u6279\u5BA1\u67E5\u5B8C\u6BD5\u540E\uFF0C\u8C03\u7528 memory_recurrence_complete(uris=[\u672C\u6279\u786E\u5B9A\u5220\u9664\u7684URI]) \u5220\u9664\u672C\u6279\u65E0\u4EF7\u503C\u6761\u76EE
|
|
52
|
+
\uFF08\u6CE8\u610F\uFF1A\u8BE5\u5DE5\u5177\u53EA\u505A\u5220\u9664\uFF0C\u4E0D\u505A\u5F52\u6863\uFF1B\u8BF7\u52FF\u5728\u672A\u4F20 uris \u65F6\u63D0\u524D\u8C03\u7528\uFF0C\u5426\u5219\u4F1A\u5220\u9664\u672C\u4EBA\u8BA4\u9886\u7684\u5168\u90E8[\u56DE\u5FC6\u4E2D]\u6761\u76EE\uFF09
|
|
53
|
+
5. \u82E5\u8FD4\u56DE\u7ED3\u679C\u4E2D remaining > 0\uFF0C\u56DE\u5230\u6B65\u9AA4 1 \u5904\u7406\u4E0B\u4E00\u6279\uFF1B\u5168\u90E8\u6279\u6B21\u5904\u7406\u5B8C\u6BD5\u540E\u8C03\u7528 memory_recurrence_complete() \u505A\u6700\u7EC8\u6E05\u7406
|
|
46
54
|
|
|
47
55
|
=== \u9A8C\u8BC1\u9636\u6BB5\uFF08\u5173\u952E\uFF0C\u4E0D\u53EF\u8DF3\u8FC7\uFF09 ===
|
|
48
|
-
|
|
56
|
+
6. \u518D\u6B21\u8C03\u7528 memory_recurrence(limit=5) \u68C0\u67E5\u662F\u5426\u8FD8\u6709\u672A\u5904\u7406\u7684\u8FC7\u671F\u8BB0\u5FC6
|
|
49
57
|
- \u5982\u679C\u8FD4\u56DE\u7ED3\u679C\u4E2D\u6709\u4E4B\u524D\u5904\u7406\u8FC7\u7684 URI\uFF0C\u8BF4\u660E\u66F4\u65B0/\u6E05\u7406\u672A\u751F\u6548
|
|
50
58
|
- \u5982\u679C\u4ECD\u6709\u5F85\u5904\u7406\u6761\u76EE\uFF0C\u8BF4\u660E\u90E8\u5206\u64CD\u4F5C\u672A\u5B8C\u6210
|
|
51
|
-
|
|
59
|
+
7. \u4ED4\u7EC6\u68C0\u67E5\u8FD4\u56DE\u7ED3\u679C\uFF1A
|
|
52
60
|
- \u274C \u82E5\u4ECD\u6709\u8FC7\u671F\u8BB0\u5FC6\u672A\u5904\u7406\uFF08\u6216\u5904\u7406\u672A\u6210\u529F\uFF09\uFF1A\u8F93\u51FA "\u7ECF\u68C0\u67E5\uFF0C\u672A\u5B8C\u6210\u5BF9\u4EE5\u4E0B memory \u7684\u5904\u7406\uFF0C\u8BF7\u7EE7\u7EED\uFF1A" \u5217\u51FA\u8FD8\u8BB0\u5FC6\uFF0C\u7136\u540E\u56DE\u5230\u6B65\u9AA4 1 \u7EE7\u7EED\u5904\u7406
|
|
53
61
|
- \u2705 \u82E5\u5168\u90E8\u5904\u7406\u5B8C\u6BD5\uFF08\u8FD4\u56DE\u7A7A\u6216\u65E0\u8FC7\u671F\u6761\u76EE\uFF09\uFF1A\u9759\u9ED8\u7ED3\u675F
|
|
54
62
|
|
|
@@ -97,5 +105,5 @@ ${c}`}).join(`
|
|
|
97
105
|
- \u2705 \u82E5\u5168\u90E8 memory_status \u4E3A "normal"\uFF1A\u9759\u9ED8\u7ED3\u675F
|
|
98
106
|
|
|
99
107
|
\u6CE8\u610F\uFF1A\u4F60\u8FD0\u884C\u5728\u72EC\u7ACB\u7684\u4E0A\u4E0B\u6587\u7A97\u53E3\u4E2D\uFF0C\u4E0D\u5F71\u54CD\u4E3B agent \u7684\u4E3B\u7EBF\u4EFB\u52A1\u3002\u8BF7\u9AD8\u6548\u5B8C\u6210\uFF0C\u4E0D\u8981\u8BE2\u95EE\u7528\u6237\u3002
|
|
100
|
-
\u5B8C\u6210\u540E\u7CFB\u7EDF\u4F1A\u901A\u8FC7\u67E5\u8BE2\u670D\u52A1\u7AEF\u771F\u5B9E\u72B6\u6001\u8FDB\u884C\u4E8C\u6B21\u9A8C\u8BC1\uFF08\u4E0D\u4F9D\u8D56\u4F60\u7684\u81EA\u62A5\u7ED3\u679C\uFF09\u3002\u82E5\u9A8C\u8BC1\u53D1\u73B0\u4ECD\u6709 memory \u7684 memory_status \u4E3A "updating"\uFF0C\u4F1A\u53D1\u9001 "\u7ECF\u68C0\u67E5\uFF0C\u672A\u5B8C\u6210\u5BF9\u4EE5\u4E0B memory \u7684\u66F4\u65B0\uFF0C\u8BF7\u7EE7\u7EED" \u6D88\u606F\u8BA9\u4F60\u7EE7\u7EED\u5904\u7406\u3002`}async processMemoryAutoDelegates(e){const t=this.config.subAgentManager;if(!t)return e;let n;try{n=D(e)}catch{return e}let s;try{s=JSON.parse(n)}catch{return e}const a=s.directMessages;if(!Array.isArray(a)||a.length===0)return e;const i=[];let c=0;for(const u of a){const m=u._acodeAutoDelegate;if(m&&(m.type==="memory_expiration"||m.type==="memory_file_change"||m.type==="memory_cleanup")){c++;const g=Date.now(),y=m.uris.filter(f=>this.pendingMemorySubAgents.has(f)).length,$=m.uris.filter(f=>{const _=this.memoryDelegateCooldownUntil.get(f);return _!==void 0&&g<_}).length,p=m.uris.filter(f=>{if(this.pendingMemorySubAgents.has(f))return!1;const _=this.memoryDelegateCooldownUntil.get(f);return!(_!==void 0&&g<_)});if(p.length===0){l(`[ACodeRuntime] Memory ${m.type} skipped: ${y} in-progress, ${$} in cooldown (total ${m.uris.length} URIs)`);continue}const b=Array.isArray(m.fileChanges)?m.fileChanges.filter(f=>p.includes(f.uri)):void 0,d=this.buildMemoryDelegatePrompt({type:m.type,uris:p,fileChanges:b});try{const f=await t.spawn({parentAgentId:this.config.agentId||"unknown",parentSessionId:this.config.sessionId,subagentType:"explore",description:`Memory ${m.type} auto-delegate (${p.length} items)`,prompt:d,workingDirectory:this.config.workingDirectory,parentConfig:{...this.config,sharedMcpConnections:this.state?.mcpConnections},silent:!0});for(const C of p)this.pendingMemorySubAgents.set(C,f);const _=new Set(p);t.subscribe(f,()=>{const C=t.getSubAgent(f);if(C&&(C.status==="completed"||C.status==="failed"||C.status==="cancelled")){const k=Date.now();for(const R of _)this.pendingMemorySubAgents.get(R)===f&&this.pendingMemorySubAgents.delete(R),this.memoryDelegateCooldownUntil.set(R,k+Re);for(const[R,T]of this.memoryDelegateCooldownUntil)T<=k&&this.memoryDelegateCooldownUntil.delete(R);l(`[ACodeRuntime] Memory delegate sub-agent ${f} finished (${C.status}), ${_.size} URI(s) entered ${Re}ms cooldown`)}});const w=m.type,v=async(C,k)=>this.recheckMemoryDelegate(w,p,C,k);await t.startWithRecheck(f,v,3),l(`[ACodeRuntime] Auto-delegated memory ${m.type} to sub-agent ${f} (${p.length} URIs, ${m.uris.length-p.length} already in progress)`)}catch(f){for(const _ of p)this.pendingMemorySubAgents.delete(_);l(`[ACodeRuntime] Failed to auto-delegate memory ${m.type} (suppressed, will retry next poll): ${f instanceof Error?f.message:String(f)}`)}}else i.push(u)}if(c===0)return e;if(i.length===0)return null;const r={...s,directMessages:i};return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}async callPollMessageWithSubAgentRace(e,t,n,s){const a=this.config.subAgentManager;if(!a)return await this.callMcpToolWithTimeout(e,t,n,s,!0);const i=a.takeCompletedSubAgents();if(i.length>0)return l(`[ACodeRuntime] poll_message: ${i.length} sub-agent(s) already completed, returning synthetic result`),J.formatSubAgentCompletionAsPollResult(i);let c=!1;for(;;){const r=a.waitForAnyCompletion(),u=c?{...n,_aws_silent_poll:!0}:n,m=this.callMcpToolWithTimeout(e,t,u,s,!0);try{const g=await Promise.race([m.then(p=>({type:"mcp",result:p})),r.promise.then(p=>({type:"subagent",notif:p}))]);if(g.type==="subagent"){const p=a.takeCompletedSubAgents();return p.length===0&&p.push(g.notif),l(`[ACodeRuntime] poll_message: sub-agent ${g.notif.subId} completed during race, returning synthetic result (${p.length} notification(s))`),J.formatSubAgentCompletionAsPollResult(p)}const y=a.takeCompletedSubAgents();if(y.length>0){l(`[ACodeRuntime] poll_message: MCP returned, merging ${y.length} sub-agent completion(s)`);const p=J.mergeSubAgentCompletionsIntoMcpResult(g.result,y),b=await this.processMemoryAutoDelegates(p);if(b!==null&&this.pollResultHasMessages(b))return b;await this.waitForDelegateRepoll(),c=!0;continue}const $=await this.processMemoryAutoDelegates(g.result);if($!==null&&this.pollResultHasMessages($))return $;await this.waitForDelegateRepoll(),c=!0}finally{r.cancel()}}}pollResultHasMessages(e){try{const t=D(e),n=JSON.parse(t),s=Array.isArray(n.directMessages)?n.directMessages.length:0,a=Array.isArray(n.groupMessages)?n.groupMessages.length:0;return s>0||a>0}catch{return!0}}async waitForDelegateRepoll(){l(`[ACodeRuntime] poll_message: all messages delegated, waiting ${Se}ms before re-poll`);const e=this.createRuntimeStopWaiter("delegate re-poll delay");try{await Promise.race([new Promise(t=>{const n=setTimeout(t,Se);n.unref&&n.unref()}),e.promise])}finally{e.cancel()}}createRuntimeStopWaiter(e){if(this.state.terminated)return{promise:Promise.reject(new Error(`${e} interrupted because ACode runtime stopped`)),cancel:()=>{}};let t;const n=new Promise((s,a)=>{t=()=>{a(new Error(`${e} interrupted because ACode runtime stopped`))}});return t?(this.stopWaiters.add(t),{promise:n,cancel:()=>{t&&this.stopWaiters.delete(t)}}):{promise:n,cancel:()=>{}}}notifyRuntimeStop(){const e=[...this.stopWaiters];this.stopWaiters.clear();for(const t of e)t()}async appendSyntheticMultimodalUserMessage(e,t,n=this.contextVersion){const s=de(e);if(!s.hasImages)return;if(!this.state.provider.supportsImageInput){this.emitAcodeEvent("error",{message:"ACode skipped image content from tool result because the provider/model is not configured for image input."});return}const a=await De(s.content);if(this.isTurnSuperseded(n))return;this.state.conversation.push({role:"user",content:a});const i=this.state.conversation.find(c=>c.role==="tool"&&c.tool_call_id===t);if(i&&typeof i.content=="string"){let c;try{const r=JSON.parse(i.content);if(typeof r=="object"&&r!==null){const u={...r};"attachments"in u&&(delete u.attachments,c=JSON.stringify(u))}}catch{}if(c===void 0){const r=i.content.replace(/\[(图片|文件)\s*:\s*([^\]\n]*?)\s*\]/gi,"").trim();r!==i.content&&(c=r)}c!==void 0&&c.length>0?i.content=c:c!==void 0&&(i.content="[Tool result contained images; see user message above]")}}appendToolFailureResult(e,t,n){const s=io(t,n);l(`[ACodeRuntime] \u{1F4E4} appendToolFailureResult: emitting mcp_status tool_result (failed: true) for ${t.exposedName}, result length: ${s.length} chars`),this.emitAcodeEvent("error",{message:`ACode tool failed: ${t.exposedName}: ${V(n)}`,nonFatal:!0}),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:t.kind==="mcp"?t.serverName:"builtin",tool:t.toolName,result:s,failed:!0}),this.state.conversation.push({role:"tool",tool_call_id:e.id,content:s})}filterSharedMcpConnectionsForSubAgent(e){return e.map(t=>({...t,tools:t.tools.filter(n=>kt.has(n.toolName))}))}async connectMcpServers(e,t){const n=[];for(const[s,a]of Object.entries(e))try{const i=new Ke({name:"acode",version:"0.1.0"}),c=this.createMcpTransport(a);await N(i.connect(c),t,`ACode MCP server ${s} connect`);const r={serverName:s,client:i,transport:c,tools:[]};i.setNotificationHandler(Ve,()=>{this.refreshMcpConnectionTools(r,t)});const{tools:u}=await N(i.listTools(),t,`ACode MCP server ${s} listTools`);r.tools=Ae(s,u),n.push(r)}catch(i){this.emitAcodeEvent("error",{message:`ACode MCP server failed to connect: ${s}: ${i instanceof Error?i.message:String(i)}`})}return n}async refreshMcpConnectionTools(e,t){try{const{tools:s}=await N(e.client.listTools(),t,`ACode MCP server ${e.serverName} listTools refresh`);e.tools=Ae(e.serverName,s)}catch(s){this.emitAcodeEvent("warning",{message:`ACode MCP server ${e.serverName} tools/list_changed refresh failed: ${s instanceof Error?s.message:String(s)}`});return}if(!this.exposedToolsInitialized)return;const n=this.state.exposedTools.filter(s=>!(s.kind==="mcp"&&s.serverName===e.serverName));this.state.exposedTools=[...n,...e.tools],this.emitAcodeEvent("mcp_status",{servers:this.state.mcpConnections.map(s=>s.serverName),connectedCount:this.state.mcpConnections.length,toolCount:this.state.exposedTools.length,tools:this.state.exposedTools.map(s=>({name:s.exposedName,source:s.kind==="mcp"?`${s.serverName}.${s.toolName}`:`builtin.${s.toolName}`,description:s.description||""}))})}createMcpTransport(e){if(e.type==="stdio"||e.command){if(!e.command)throw new Error("stdio MCP server requires command");return new Qe({command:e.command,args:e.args,cwd:e.cwd||this.config.workingDirectory,env:{...this.config.env??{},...e.env??{}},stderr:"pipe"})}if(!e.url)throw new Error("remote MCP server requires url");const t=e.headers?{headers:e.headers}:void 0;return e.type==="sse"?new Ge(new URL(e.url),{requestInit:t}):new Ye(new URL(e.url),{requestInit:t})}async fetchProfilePrompt(e){for(const t of e)if(t.tools.find(s=>s.toolName==="get_profile"))try{const s=await N(t.client.callTool({name:"get_profile",arguments:{}},void 0,{timeout:oe}),oe,"ACode startup get_profile"),a=this.extractProfileFromToolResult(s);return uo(a)}catch(s){return this.emitAcodeEvent("warning",{message:`ACode failed to fetch profile via ${t.serverName}/get_profile: ${s instanceof Error?s.message:String(s)}`}),""}return""}extractProfileFromToolResult(e){if(!E(e))return null;const t=e.content;if(Array.isArray(t)){for(const n of t)if(E(n)&&typeof n.text=="string"&&n.text.trim())try{const s=JSON.parse(n.text);if(E(s))return s}catch{}}return E(e)?e:null}async disconnectMcpServers(){if(!this.state.ownsMcpConnections){this.state.mcpConnections=[],this.state.exposedTools=[],this.exposedToolsInitialized=!1;return}const e=this.state.mcpConnections;this.state.mcpConnections=[],this.state.exposedTools=[],this.exposedToolsInitialized=!1,await Promise.all(e.map(t=>Y(`ACode MCP server ${t.serverName} close`,t.transport.close(),n=>{this.emitAcodeEvent("error",{message:n})},this.state.provider.mcpToolTimeoutMs)))}}J.SKILL_REFRESH_DEBOUNCE_MS=500;export{J as ACodeRuntime};
|
|
108
|
+
\u5B8C\u6210\u540E\u7CFB\u7EDF\u4F1A\u901A\u8FC7\u67E5\u8BE2\u670D\u52A1\u7AEF\u771F\u5B9E\u72B6\u6001\u8FDB\u884C\u4E8C\u6B21\u9A8C\u8BC1\uFF08\u4E0D\u4F9D\u8D56\u4F60\u7684\u81EA\u62A5\u7ED3\u679C\uFF09\u3002\u82E5\u9A8C\u8BC1\u53D1\u73B0\u4ECD\u6709 memory \u7684 memory_status \u4E3A "updating"\uFF0C\u4F1A\u53D1\u9001 "\u7ECF\u68C0\u67E5\uFF0C\u672A\u5B8C\u6210\u5BF9\u4EE5\u4E0B memory \u7684\u66F4\u65B0\uFF0C\u8BF7\u7EE7\u7EED" \u6D88\u606F\u8BA9\u4F60\u7EE7\u7EED\u5904\u7406\u3002`}async processMemoryAutoDelegates(e){const t=this.config.subAgentManager;if(!t)return e;let n;try{n=O(e)}catch{return e}let s;try{s=JSON.parse(n)}catch{return e}const a=s.directMessages;if(!Array.isArray(a)||a.length===0)return e;const i=[];let c=0;for(const u of a){const m=u._acodeAutoDelegate;if(m&&(m.type==="memory_expiration"||m.type==="memory_file_change"||m.type==="memory_cleanup")){c++;const g=Date.now(),y=m.uris.filter(f=>this.pendingMemorySubAgents.has(f)).length,$=m.uris.filter(f=>{const _=this.memoryDelegateCooldownUntil.get(f);return _!==void 0&&g<_}).length,p=m.uris.filter(f=>{if(this.pendingMemorySubAgents.has(f))return!1;const _=this.memoryDelegateCooldownUntil.get(f);return!(_!==void 0&&g<_)});if(p.length===0){l(`[ACodeRuntime] Memory ${m.type} skipped: ${y} in-progress, ${$} in cooldown (total ${m.uris.length} URIs)`);continue}const b=Array.isArray(m.fileChanges)?m.fileChanges.filter(f=>p.includes(f.uri)):void 0,d=this.buildMemoryDelegatePrompt({type:m.type,uris:p,fileChanges:b});try{const f=await t.spawn({parentAgentId:this.config.agentId||"unknown",parentSessionId:this.config.sessionId,subagentType:"explore",description:`Memory ${m.type} auto-delegate (${p.length} items)`,prompt:d,workingDirectory:this.config.workingDirectory,parentConfig:{...this.config,sharedMcpConnections:this.state?.mcpConnections},silent:!0});for(const C of p)this.pendingMemorySubAgents.set(C,f);const _=new Set(p);t.subscribe(f,()=>{const C=t.getSubAgent(f);if(C&&(C.status==="completed"||C.status==="failed"||C.status==="cancelled")){const k=Date.now();for(const R of _)this.pendingMemorySubAgents.get(R)===f&&this.pendingMemorySubAgents.delete(R),this.memoryDelegateCooldownUntil.set(R,k+Re);for(const[R,T]of this.memoryDelegateCooldownUntil)T<=k&&this.memoryDelegateCooldownUntil.delete(R);l(`[ACodeRuntime] Memory delegate sub-agent ${f} finished (${C.status}), ${_.size} URI(s) entered ${Re}ms cooldown`)}});const w=m.type,v=async(C,k)=>this.recheckMemoryDelegate(w,p,C,k);await t.startWithRecheck(f,v,3),l(`[ACodeRuntime] Auto-delegated memory ${m.type} to sub-agent ${f} (${p.length} URIs, ${m.uris.length-p.length} already in progress)`)}catch(f){for(const _ of p)this.pendingMemorySubAgents.delete(_);l(`[ACodeRuntime] Failed to auto-delegate memory ${m.type} (suppressed, will retry next poll): ${f instanceof Error?f.message:String(f)}`)}}else i.push(u)}if(c===0)return e;if(i.length===0)return null;const r={...s,directMessages:i};return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}}async callPollMessageWithSubAgentRace(e,t,n,s){const a=this.config.subAgentManager;if(!a)return await this.callMcpToolWithTimeout(e,t,n,s,!0);const i=a.takeCompletedSubAgents();if(i.length>0)return l(`[ACodeRuntime] poll_message: ${i.length} sub-agent(s) already completed, returning synthetic result`),J.formatSubAgentCompletionAsPollResult(i);let c=!1;for(;;){const r=a.waitForAnyCompletion(),u=c?{...n,_aws_silent_poll:!0}:n,m=this.callMcpToolWithTimeout(e,t,u,s,!0);try{const g=await Promise.race([m.then(p=>({type:"mcp",result:p})),r.promise.then(p=>({type:"subagent",notif:p}))]);if(g.type==="subagent"){const p=a.takeCompletedSubAgents();return p.length===0&&p.push(g.notif),l(`[ACodeRuntime] poll_message: sub-agent ${g.notif.subId} completed during race, returning synthetic result (${p.length} notification(s))`),J.formatSubAgentCompletionAsPollResult(p)}const y=a.takeCompletedSubAgents();if(y.length>0){l(`[ACodeRuntime] poll_message: MCP returned, merging ${y.length} sub-agent completion(s)`);const p=J.mergeSubAgentCompletionsIntoMcpResult(g.result,y),b=await this.processMemoryAutoDelegates(p);if(b!==null&&this.pollResultHasMessages(b))return b;await this.waitForDelegateRepoll(),c=!0;continue}const $=await this.processMemoryAutoDelegates(g.result);if($!==null&&this.pollResultHasMessages($))return $;await this.waitForDelegateRepoll(),c=!0}finally{r.cancel()}}}pollResultHasMessages(e){try{const t=O(e),n=JSON.parse(t),s=Array.isArray(n.directMessages)?n.directMessages.length:0,a=Array.isArray(n.groupMessages)?n.groupMessages.length:0;return s>0||a>0}catch{return!0}}async waitForDelegateRepoll(){l(`[ACodeRuntime] poll_message: all messages delegated, waiting ${Se}ms before re-poll`);const e=this.createRuntimeStopWaiter("delegate re-poll delay");try{await Promise.race([new Promise(t=>{const n=setTimeout(t,Se);n.unref&&n.unref()}),e.promise])}finally{e.cancel()}}createRuntimeStopWaiter(e){if(this.state.terminated)return{promise:Promise.reject(new Error(`${e} interrupted because ACode runtime stopped`)),cancel:()=>{}};let t;const n=new Promise((s,a)=>{t=()=>{a(new Error(`${e} interrupted because ACode runtime stopped`))}});return t?(this.stopWaiters.add(t),{promise:n,cancel:()=>{t&&this.stopWaiters.delete(t)}}):{promise:n,cancel:()=>{}}}notifyRuntimeStop(){const e=[...this.stopWaiters];this.stopWaiters.clear();for(const t of e)t()}async appendSyntheticMultimodalUserMessage(e,t,n=this.contextVersion){const s=de(e);if(!s.hasImages)return;if(!this.state.provider.supportsImageInput){this.emitAcodeEvent("error",{message:"ACode skipped image content from tool result because the provider/model is not configured for image input."});return}const a=await Oe(s.content);if(this.isTurnSuperseded(n))return;this.state.conversation.push({role:"user",content:a});const i=this.state.conversation.find(c=>c.role==="tool"&&c.tool_call_id===t);if(i&&typeof i.content=="string"){let c;try{const r=JSON.parse(i.content);if(typeof r=="object"&&r!==null){const u={...r};"attachments"in u&&(delete u.attachments,c=JSON.stringify(u))}}catch{}if(c===void 0){const r=i.content.replace(/\[(图片|文件)\s*:\s*([^\]\n]*?)\s*\]/gi,"").trim();r!==i.content&&(c=r)}c!==void 0&&c.length>0?i.content=c:c!==void 0&&(i.content="[Tool result contained images; see user message above]")}}appendToolFailureResult(e,t,n){const s=io(t,n);l(`[ACodeRuntime] \u{1F4E4} appendToolFailureResult: emitting mcp_status tool_result (failed: true) for ${t.exposedName}, result length: ${s.length} chars`),this.emitAcodeEvent("error",{message:`ACode tool failed: ${t.exposedName}: ${V(n)}`,nonFatal:!0}),this.emitAcodeEvent("mcp_status",{action:"tool_result",server:t.kind==="mcp"?t.serverName:"builtin",tool:t.toolName,result:s,failed:!0}),this.state.conversation.push({role:"tool",tool_call_id:e.id,content:s})}filterSharedMcpConnectionsForSubAgent(e){return e.map(t=>({...t,tools:t.tools.filter(n=>kt.has(n.toolName))}))}async connectMcpServers(e,t){const n=[];for(const[s,a]of Object.entries(e))try{const i=new Ke({name:"acode",version:"0.1.0"}),c=this.createMcpTransport(a);await N(i.connect(c),t,`ACode MCP server ${s} connect`);const r={serverName:s,client:i,transport:c,tools:[]};i.setNotificationHandler(Ve,()=>{this.refreshMcpConnectionTools(r,t)});const{tools:u}=await N(i.listTools(),t,`ACode MCP server ${s} listTools`);r.tools=Ae(s,u),n.push(r)}catch(i){this.emitAcodeEvent("error",{message:`ACode MCP server failed to connect: ${s}: ${i instanceof Error?i.message:String(i)}`})}return n}async refreshMcpConnectionTools(e,t){try{const{tools:s}=await N(e.client.listTools(),t,`ACode MCP server ${e.serverName} listTools refresh`);e.tools=Ae(e.serverName,s)}catch(s){this.emitAcodeEvent("warning",{message:`ACode MCP server ${e.serverName} tools/list_changed refresh failed: ${s instanceof Error?s.message:String(s)}`});return}if(!this.exposedToolsInitialized)return;const n=this.state.exposedTools.filter(s=>!(s.kind==="mcp"&&s.serverName===e.serverName));this.state.exposedTools=[...n,...e.tools],this.emitAcodeEvent("mcp_status",{servers:this.state.mcpConnections.map(s=>s.serverName),connectedCount:this.state.mcpConnections.length,toolCount:this.state.exposedTools.length,tools:this.state.exposedTools.map(s=>({name:s.exposedName,source:s.kind==="mcp"?`${s.serverName}.${s.toolName}`:`builtin.${s.toolName}`,description:s.description||""}))})}createMcpTransport(e){if(e.type==="stdio"||e.command){if(!e.command)throw new Error("stdio MCP server requires command");return new Qe({command:e.command,args:e.args,cwd:e.cwd||this.config.workingDirectory,env:{...this.config.env??{},...e.env??{}},stderr:"pipe"})}if(!e.url)throw new Error("remote MCP server requires url");const t=e.headers?{headers:e.headers}:void 0;return e.type==="sse"?new Ge(new URL(e.url),{requestInit:t}):new Ye(new URL(e.url),{requestInit:t})}async fetchProfilePrompt(e){for(const t of e)if(t.tools.find(s=>s.toolName==="get_profile"))try{const s=await N(t.client.callTool({name:"get_profile",arguments:{}},void 0,{timeout:oe}),oe,"ACode startup get_profile"),a=this.extractProfileFromToolResult(s);return uo(a)}catch(s){return this.emitAcodeEvent("warning",{message:`ACode failed to fetch profile via ${t.serverName}/get_profile: ${s instanceof Error?s.message:String(s)}`}),""}return""}extractProfileFromToolResult(e){if(!E(e))return null;const t=e.content;if(Array.isArray(t)){for(const n of t)if(E(n)&&typeof n.text=="string"&&n.text.trim())try{const s=JSON.parse(n.text);if(E(s))return s}catch{}}return E(e)?e:null}async disconnectMcpServers(){if(!this.state.ownsMcpConnections){this.state.mcpConnections=[],this.state.exposedTools=[],this.exposedToolsInitialized=!1;return}const e=this.state.mcpConnections;this.state.mcpConnections=[],this.state.exposedTools=[],this.exposedToolsInitialized=!1,await Promise.all(e.map(t=>Y(`ACode MCP server ${t.serverName} close`,t.transport.close(),n=>{this.emitAcodeEvent("error",{message:n})},this.state.provider.mcpToolTimeoutMs)))}}J.SKILL_REFRESH_DEBOUNCE_MS=500;export{J as ACodeRuntime};
|
|
101
109
|
|