aws-runtime-bridge 1.9.146 → 1.9.148
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/CHANGELOG.md +56 -0
- package/dist/adapter/AcodeSdkAdapter.d.ts +6 -1
- package/dist/adapter/AcodeSdkAdapter.js +5 -5
- package/dist/browser/browser-pool.d.ts +4 -0
- package/dist/browser/browser-pool.js +1 -1
- package/dist/desktop/built-in-desktop-tools.d.ts +6 -0
- package/dist/desktop/built-in-desktop-tools.js +1 -1
- package/dist/routes/background-tasks.js +4 -4
- package/dist/routes/instance.js +1 -1
- package/dist/routes/terminal.js +19 -19
- package/dist/services/builtin-commands/create-plugin.md +44 -0
- package/dist/services/changelog.d.ts +8 -0
- package/dist/services/changelog.js +17 -0
- package/dist/services/instance-features.d.ts +2 -0
- package/dist/services/instance-features.js +1 -1
- package/dist/services/plugin-manager.js +1 -1
- package/dist/utils/npm-version.d.ts +2 -0
- package/dist/utils/npm-version.js +1 -1
- package/package/acode/dist/background-command-manager.d.ts +4 -0
- package/package/acode/dist/background-command-manager.js +2 -2
- package/package/acode/dist/background-command-store.d.ts +2 -0
- package/package/acode/dist/built-in-file-tools.js +2 -2
- package/package/acode/dist/image-utils.d.ts +30 -1
- package/package/acode/dist/image-utils.js +1 -1
- package/package/acode/dist/runtime-environment.d.ts +3 -0
- package/package/acode/dist/runtime-environment.js +3 -0
- package/package/acode/dist/runtime.js +14 -14
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +16 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +16 -16
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +16 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +7 -7
- package/package/aws-client-agent-mcp/dist/types.d.ts +10 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本文件记录 aws-runtime-bridge 各版本的发布说明,随 npm 包一并分发(package.json
|
|
4
|
+
files 白名单),供调度中心「查看本次更新内容」弹窗按 `## v<version>` 精确切片
|
|
5
|
+
展示。发布新版本时须在仓库中先写好 `## v<version>` 段(prepack-guard 校验缺段
|
|
6
|
+
即阻断发布);Bridge 自更新成功后的运行时追加仅作为包内缺段时的兜底。
|
|
7
|
+
|
|
8
|
+
## v1.9.148
|
|
9
|
+
|
|
10
|
+
_发布日期:2026-09-06_
|
|
11
|
+
|
|
12
|
+
- Pencil 导出 savePath 不再限制在工作区内:绝对路径(如 D:\exports\hero.svg 或
|
|
13
|
+
用户 Downloads)直接使用,相对路径仍基于工作区根解析——导出交付物可落盘到
|
|
14
|
+
任意位置。
|
|
15
|
+
- Pencil 渲染缓存升级为原始分辨率(最长边 ≤8192 内不缩放,此前 1280),agent
|
|
16
|
+
pencil_export_image 与人工导出拿到原图;导出等待超时 8s→15s;升级规格后对旧
|
|
17
|
+
低分辨率缓存自动补渲一次。
|
|
18
|
+
- Pencil 画板编辑锁:有写权限时默认锁定只读,点击解锁才可编辑;编辑中画布被
|
|
19
|
+
agent 工具/协作端推进版本时自动回锁并加载最新快照,防止基于旧版编辑整份覆盖
|
|
20
|
+
他人写入(脏写);写权限被收回时强制回锁。
|
|
21
|
+
- 输入框新增终端式 ↑/↓ 发送历史回溯(按会话隔离,Esc 退出并恢复进入前的草稿);
|
|
22
|
+
「限制操作」入口从「+」菜单移到输入框左侧盾牌常驻图标,存在授权/禁止档位时
|
|
23
|
+
显示绿色。
|
|
24
|
+
- 修复:侧板拖拽经过 iframe(画布/终端)宽度冻结且无法结束的问题(拖拽期间禁用
|
|
25
|
+
容器内 iframe 鼠标事件,窗口失焦自动结束拖拽);全局设置菜单横向滚动两端留白
|
|
26
|
+
被吞掉的问题;项目头像弹窗标题显示项目名而非 projectId。
|
|
27
|
+
- 更新健壮性:包内 CHANGELOG 缺目标版本段时继续尝试 ~/.aws-bridge 下运行时追加
|
|
28
|
+
的候选文件;更新成功后的发布说明追加改为 await 在重启前完成,版本号优先取 npm
|
|
29
|
+
刚落盘的 package.json。
|
|
30
|
+
- 发布护栏:prepack-guard 校验 CHANGELOG.md 必须含当前版本段(缺段阻断发布),
|
|
31
|
+
verify-pack-contents 将 CHANGELOG.md 列为必需 payload。
|
|
32
|
+
|
|
33
|
+
## v1.9.147
|
|
34
|
+
|
|
35
|
+
_发布日期:2026-09-06_
|
|
36
|
+
|
|
37
|
+
- Pencil 设计画布全栈接入:t_pencil_doc 权威存储(迁移 v1.26.0~v1.26.3)与协作者
|
|
38
|
+
权限、MCP pencil_* 工具(AWS_PENCIL_TOOLS 兜底可见性过滤)。
|
|
39
|
+
- 桌面自动化增强:nuphus-mcp 0.1.12 的 desktop_window_activate 先还原再激活,修复
|
|
40
|
+
托盘/最小化窗口无法激活的问题;新增 som.rs 与 UIA 安全增强。
|
|
41
|
+
- acode 新增 runtime-environment;后台命令/文件/图片工具优化,AcodeSdkAdapter 与
|
|
42
|
+
浏览器池稳定性改进。
|
|
43
|
+
- 修复:CHANGELOG.md 自本版本起随包分发。此前发布说明依赖"执行更新的旧进程"运行
|
|
44
|
+
时追加,旧版 Bridge(≤1.9.145 无此代码)执行更新会导致新版本弹窗无发布说明可
|
|
45
|
+
展示,只能回退安装过程日志。
|
|
46
|
+
|
|
47
|
+
## v1.9.146
|
|
48
|
+
|
|
49
|
+
- 新增「本次更新内容」展示:将更新弹窗从安装过程日志(updates.log)切换为版本
|
|
50
|
+
发布说明(CHANGELOG.md),让用户看到本次版本相对变更的要点,而非 npm 安装
|
|
51
|
+
过程噪声。
|
|
52
|
+
- 更新成功重启后,由 Bridge 自动在 CHANGELOG.md 顶部追加当前版本段。
|
|
53
|
+
- 当目标版本未找到发布说明时,弹窗自动回退展示 updates.log(安装过程日志)并
|
|
54
|
+
给出提示。
|
|
55
|
+
|
|
56
|
+
<!-- append-new-version-above -->
|
|
@@ -148,8 +148,13 @@ export declare class AcodeSdkAdapter extends EventEmitter implements BaseProvide
|
|
|
148
148
|
|
|
149
149
|
private scheduleAutoPoll;
|
|
150
150
|
private cancelScheduledAutoPoll;
|
|
151
|
+
|
|
152
|
+
private scheduleRequestRetry;
|
|
153
|
+
|
|
154
|
+
private retryFailedRequest;
|
|
155
|
+
|
|
156
|
+
private cancelRequestRetry;
|
|
151
157
|
private isTerminalStatus;
|
|
152
|
-
private emitRuntimeError;
|
|
153
158
|
private emitRuntimeErrorEvent;
|
|
154
159
|
|
|
155
160
|
private estimateOutputTokensFromMessages;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{EventEmitter as X}from"node:events";import V from"node:fs/promises";import U from"node:path";import{fileURLToPath as Z,pathToFileURL as tt}from"node:url";import{v4 as R}from"uuid";import{getBridgeInstancePluginsDir as et,getBridgeInstanceSkillsDir as ot,getBridgeTemplateConfigFile as x,getBridgeTemplateSkillsDir as st}from"../config.js";import{getReleasedAcodeEntryPath as it}from"../services/acode-package.js";import{syncAgentSkillFarm as F}from"../services/agent-capability-sync.js";import{agentPtyManager as at}from"../services/agent-pty-manager.js";import{getBrowserTools as v,closeBrowserSession as nt,registerListenerWakeHook as rt,unregisterListenerWakeHook as q}from"../services/browser-session-manager.js";import{getDesktopTools as D,getDesktopToolsIfReady as ut}from"../services/desktop-mcp-manager.js";import{redactForLog as B}from"../services/instance-ai-config-service.js";import{BROWSER_FEATURE_KEY as W,DESKTOP_FEATURE_KEY as O,getAgentToolPermissions as lt,isFeatureEnabled as P,resolveAgentToolPermissions as dt}from"../services/instance-features.js";import{pluginManager as L}from"../services/plugin-manager.js";import{subAgentManager as ct}from"../services/sub-agent-manager-instance.js";import{recordAgentFileChange as mt}from"../services/workspaceTaskChangeWatcher.js";import{getInstanceAiConfigDebugLogger as E}from"../utils/instance-ai-config-debug-logger.js";import{isMessagePollingIdleCommand as $,isPollMessageToolDescriptor as pt}from"./acode-tool-names.js";import{IdlePollLoop as gt}from"./idle-poll-loop.js";import{MESSAGE_POLLING_IDLE_PROMPT as ft}from"./prompts.js";import{resolveAcodeProviderConfig as ht}from"./instance-ai-config-resolver.js";import{getCanonicalMcpDetail as N,getToolActionInfo as Tt}from"./types.js";const At=process.env.ACODE_COMPACTION_DEBUG==="1";function l(...u){At&&console.log(...u)}const _t=process.env.ACODE_TOKEN_DEBUG==="1";function C(...u){_t&&console.log("[TokenDebug][Adapter]",...u)}const H=["Continue the AgentsWork message loop now.","You MUST call the exact MCP tool named aws-mcp__poll_message as your next action and wait for the next assigned message or task.","Do not answer in plain text before calling aws-mcp__poll_message.","After handling any received work, reply through the appropriate aws-mcp messaging tool, then continue listening again."].join(" "),St=5e3;function M(u,t){const e=process.env[u];if(e===void 0||e.trim()==="")return t;const o=Number(e.trim());return Number.isFinite(o)&&o>=0?Math.floor(o):t}const K=M("ACODE_DATA_FLOW_WATCHDOG_MS",12e4),yt=5*6e4,bt=M("ACODE_STUCK_AUTOPOLL_TIMEOUT_MS",5*6e4),kt=250,Ct=2e3,j=M("ACODE_SUBMIT_TIMEOUT_MS",12e4),Q=3e3,wt=ft;function Pt(){return U.resolve(it())}function Lt(u){return tt(U.resolve(u)).href}const G=128e3,w={"claude-4":{contextLimit:2e5,maxOutput:8192},"claude-sonnet-4":{contextLimit:2e5,maxOutput:8192},"claude-3-5-sonnet":{contextLimit:2e5,maxOutput:8192},"claude-3-opus":{contextLimit:2e5,maxOutput:8192},"claude-3-haiku":{contextLimit:2e5,maxOutput:8192},"claude-2":{contextLimit:1e5,maxOutput:4096},"gpt-4o":{contextLimit:128e3,maxOutput:16384},"gpt-4-turbo":{contextLimit:128e3,maxOutput:4096},"gpt-4":{contextLimit:8192,maxOutput:4096},"gpt-3.5-turbo":{contextLimit:16384,maxOutput:4096},o1:{contextLimit:2e5,maxOutput:1e5},"o3-mini":{contextLimit:2e5,maxOutput:1e5},"gemini-2.0-flash":{contextLimit:1048576,maxOutput:8192},"gemini-2.5-pro":{contextLimit:1048576,maxOutput:8192},"gemini-1.5-pro":{contextLimit:2097152,maxOutput:8192},"gemini-1.5-flash":{contextLimit:1048576,maxOutput:8192},deepseek:{contextLimit:64e3,maxOutput:8192},"deepseek-chat":{contextLimit:64e3,maxOutput:8192},"deepseek-reasoner":{contextLimit:64e3,maxOutput:8192},"qwen-max":{contextLimit:32e3,maxOutput:8192},"qwen-plus":{contextLimit:128e3,maxOutput:8192},"qwen-turbo":{contextLimit:1e6,maxOutput:8192},"glm-4":{contextLimit:128e3,maxOutput:4096},"yi-":{contextLimit:2e5,maxOutput:4096},"llama-3":{contextLimit:8192,maxOutput:4096},"llama-2":{contextLimit:4096,maxOutput:4096},mistral:{contextLimit:32e3,maxOutput:4096},mixtral:{contextLimit:32e3,maxOutput:4096},codestral:{contextLimit:256e3,maxOutput:8192},grok:{contextLimit:131072,maxOutput:4096},"nova-":{contextLimit:2e5,maxOutput:8192}};function $t(u){if(!u)return G;const t=u.toLowerCase().trim(),e=w[t];if(e)return e.contextLimit;const o=Object.keys(w).filter(a=>t.startsWith(a)).sort((a,i)=>i.length-a.length),s=o[0]!==void 0?w[o[0]]:void 0;return s?s.contextLimit:G}const J=4096;function xt(u){if(!u)return J;const t=u.toLowerCase().trim(),e=w[t];if(e)return e.maxOutput;const o=Object.keys(w).filter(a=>t.startsWith(a)).sort((a,i)=>i.length-a.length),s=o[0]!==void 0?w[o[0]]:void 0;return s?s.maxOutput:J}function Dt(u){if(!u||typeof u!="object")return;const t=u,e=h(t.inputTokens,t.input_tokens,t.promptTokens,t.prompt_tokens),o=h(t.outputTokens,t.output_tokens,t.completionTokens,t.completion_tokens);if(e===0&&o===0)return;let s=h(t.cacheHitTokens,t.promptCacheHitTokens,t.prompt_cache_hit_tokens),a=h(t.cacheMissTokens,t.promptCacheMissTokens,t.prompt_cache_miss_tokens),i=t.cacheHitTokens!==void 0||t.cacheMissTokens!==void 0||t.promptCacheHitTokens!==void 0||t.promptCacheMissTokens!==void 0||t.prompt_cache_hit_tokens!==void 0||t.prompt_cache_miss_tokens!==void 0;if(s===0&&a===0){const n=h(t.cache_read_input_tokens),r=h(t.cache_creation_input_tokens);n>0||r>0?(s=n,a=r+h(t.input_tokens),i=!0):(s=h(t.cache_hit_tokens),a=h(t.cache_miss_tokens),i=i||t.cache_hit_tokens!==void 0||t.cache_miss_tokens!==void 0)}if(s===0&&a===0){const n=h(t.prompt_tokens_details?.cached_tokens);n>0&&(s=n,a=e>n?e-n:0,i=!0)}return{inputTokens:e,outputTokens:o,...s>0||a>0?{cacheHitTokens:s,cacheMissTokens:a}:{},...i?{hasCache:!0}:{}}}function h(...u){for(const t of u){const e=Number(t);if(Number.isFinite(e)&&e>0)return e}return 0}function z(u){return h(u.estimatedContextTokens,u.contextTokens,u.context_tokens,u.contextUsedTokens,u.context_used_tokens)}function Ot(u){return u==="stderr"||u==="mixed"?u:"stdout"}function Et(u){if(!u||typeof u!="object"||Array.isArray(u))return;const t={};for(const[e,o]of Object.entries(u))typeof o=="string"&&(t[e]=o);return Object.keys(t).length>0?t:void 0}function Mt(u){return u.failed===!0}function Y(u,t,e){if(!u)return e?`${t}/${e}`:t;const o=`${t}__`;return u.startsWith(o)?N(`${t}__${u.slice(o.length)}`):N(u.replace("/","__"))}function It(u){return u==="read_file"||u==="edit_file"||u==="write_file"||u==="bash"}function Ut(u){const t=Rt(u);return t?[t.directMessages,t.groupMessages,t.messages].some(e=>Array.isArray(e)&&e.length>0):typeof u=="string"&&u.trim().length>0}function Rt(u){if(u&&typeof u=="object"&&!Array.isArray(u))return u;if(!(typeof u!="string"||!u.trim()))try{const t=JSON.parse(u);return t&&typeof t=="object"&&!Array.isArray(t)?t:void 0}catch{return}}class ae extends X{constructor(){super(),this.providerId="acode",this.displayName="ACode",this.sessions=new Map,this.listenerWakeHooks=new Map,L.setOnPluginsChanged(t=>{this.syncPluginCommands(t)})}async syncPluginCommands(t){try{const e=await L.getPluginCommands(t);for(const o of this.sessions.values())o.config.agentId===t&&o.runtime.addPluginCommands?.(e)}catch(e){l(`[acode-adapter] syncPluginCommands failed for agent ${t}:`,e.message)}}async startSession(t,e){const o=e.mcpConfigPath||x(),{resolved:s,configContextLimit:a,supportsImage:i}=await this.resolveAcodeProvider(e,o),n=s.model,r=a??$t(n),d=xt(n),p={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0,contextLimit:r,maxOutput:d,cacheHitTokens:0,cacheMissTokens:0}};this.sessions.set(t,{adapterSession:p,config:e,runtime:await this.createRuntime(t,n,e,s,i),autoPollActive:!1}),this.emit("status-change",t,"starting");const m=this.sessions.get(t);if(!m)throw new Error("ACode session was not initialized");const c=()=>{const g=this.sessions.get(t);g&&this.getIdlePollLoop(g).start()};this.listenerWakeHooks.set(t,c),rt(e.agentId,c);try{await m.runtime.start(),p.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:m.adapterSession.totalUsage}),e.initialPrompt?.trim()?this.startInitialPromptTurn(t,e.initialPrompt):this.armIdleLoopOrAutoPoll(t)}catch(g){p.status="error",this.emitRuntimeErrorEvent(t,g),this.emit("status-change",t,"error");const f=this.sessions.get(t);throw f?.capabilitiesFallbackTimer&&clearTimeout(f.capabilitiesFallbackTimer),this.sessions.delete(t),q(e.agentId,this.listenerWakeHooks.get(t)),this.listenerWakeHooks.delete(t),g}}async sendMessage(t,e){const o=Date.now();l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l("[ACode] \u25B6\uFE0F sendMessage STARTED"),l(`[ACode] Session: ${t}`),l(`[ACode] Message: ${e.slice(0,100)}${e.length>100?"...":""}`),l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");const s=this.requireSession(t),a=s.adapterSession.status;l(`[ACode] Previous status: ${a}`),this.cancelScheduledAutoPoll(s),s.failedTurnRecoveryScheduled=!1,s.idlePollLoop?.stop();const i={id:R(),sessionId:t,role:"user",content:e,timestamp:new Date().toISOString()};s.adapterSession.messages.push(i),this.emit("conversation-message",t,i),l(`[ACode] Changing status: ${a} \u2192 thinking`),s.adapterSession.status="thinking",this.emit("status-change",t,"thinking"),this.startSubmitTimeoutDetection(t,s,"sendMessage");try{l("[ACode] \u25B6\uFE0F Calling runtime.submit()...");const n=Date.now();await s.runtime.submit(e);const r=Date.now()-n;this.cancelSubmitTimeoutDetection(s),l(`[ACode] \u2705 runtime.submit() completed in ${r}ms`),l(`[ACode] Current status after submit: ${s.adapterSession.status}`),l("[ACode] \u25B6\uFE0F Calling recoverResolvedTurnWithoutTerminalEvent()..."),this.recoverResolvedTurnWithoutTerminalEvent(t),l(`[ACode] Status after recovery: ${s.adapterSession.status}`);const d=Date.now()-o;l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l(`[ACode] \u2705 sendMessage COMPLETED in ${d}ms`),l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}catch(n){const r=Date.now()-o;console.error(`[ACode] \u274C sendMessage FAILED after ${r}ms:`,n instanceof Error?n.message:String(n)),this.cancelSubmitTimeoutDetection(s),l("[ACode] \u25B6\uFE0F Calling emitRuntimeError with recover=true..."),this.emitRuntimeError(t,n,{recover:!0}),l(`[ACode] Status after error recovery: ${s.adapterSession.status}`)}}async sendConfirmation(t,e){}submitToolPermissionDecision(t,e,o){const s=this.sessions.get(t);if(!s)return!1;s.pendingToolAuthRequests?.delete(e);const a=s.runtime.submitToolPermissionDecision?.(e,o)??!1;return a&&this.startSubmitTimeoutDetection(t,s,"submitToolPermissionDecision"),a}getPendingToolAuthRequests(t){const e=this.sessions.get(t);return e?.pendingToolAuthRequests?Array.from(e.pendingToolAuthRequests.entries()).map(([o,s])=>({requestId:o,capability:s.capability,detail:s.detail,requestedAt:s.requestedAt})):[]}async sendQuestionAnswer(t,e){const o=this.sessions.get(t);if(!o)return;const s=o.pendingQuestionToolCallId;if(!s){l(`[AcodeSdkAdapter] sendQuestionAnswer: no pending question for session ${t}`);return}o.pendingQuestionToolCallId=void 0,o.pendingQuestions=void 0,o.runtime.submitQuestionAnswer&&o.runtime.submitQuestionAnswer(s,e),this.startSubmitTimeoutDetection(t,o,"sendQuestionAnswer")}async abortCurrentTurn(t){await this.requireSession(t).runtime.abort()}async resetContext(t){const e=this.requireSession(t);if(this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.autoPollActive=!1,e.autoPollObservedPollingTool=!1,e.autoPollObservedAssistantText=!1,e.autoPollObservedAnyTool=!1,e.stopAutoPollAfterCurrentTurn=!1,e.idlePollLoop?.stop(),!e.runtime.resetContext)throw new Error("ACode runtime does not support context reset");await e.runtime.resetContext(),e.adapterSession.messages=[];const o=e.adapterSession.totalUsage;if(e.adapterSession.totalUsage={inputTokens:0,outputTokens:0,contextLimit:o.contextLimit,maxOutput:o.maxOutput,inputLimit:o.inputLimit,compactionThreshold:o.compactionThreshold,cacheHitTokens:o.cacheHitTokens,cacheMissTokens:o.cacheMissTokens},e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage}),e.config.initialPrompt?.trim()){this.startInitialPromptTurn(t,e.config.initialPrompt);return}this.armIdleLoopOrAutoPoll(t)}async terminateSession(t){const e=this.sessions.get(t);if(!e)return;this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.idlePollLoop?.stop(),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),await e.runtime.stop();const o=e.config.agentId;o&&(q(o,this.listenerWakeHooks.get(t)),nt(o)),this.listenerWakeHooks.delete(t),e.adapterSession.status="terminated",this.sessions.delete(t),this.emit("status-change",t,"terminated")}getConversation(t){return this.sessions.get(t)?.adapterSession.messages??[]}hasSession(t){return this.sessions.has(t)}getProviderSessionId(t){return this.sessions.has(t)?t:void 0}getSessionStatus(t){return this.sessions.get(t)?.adapterSession.status}notifyForceMessage(t){const e=this.sessions.get(t);return e?.runtime?.notifyForceMessage?(e.runtime.notifyForceMessage(),!0):!1}getCapabilities(t){return this.sessions.get(t)?.lastCombinedCapabilities}async resolveAgentAiInfo(t){try{const e=[...this.sessions.values()].find(i=>i.config.agentId===t);if(!e)return;const o=e.config.mcpConfigPath||x(),{resolved:s,supportsImage:a}=await this.resolveAcodeProvider(e.config,o);return s?.model?{model:s.model,supportsImage:a===!0,...s.baseURL?{baseURL:s.baseURL}:{},...s.apiKey?{apiKey:s.apiKey}:{}}:void 0}catch{return}}resolveAgentSkillsDir(t){if(t){try{const e=F(t);if(e)return e}catch(e){l(`[acode-adapter] skill farm sync failed for agent ${t}:`,e.message)}return ot(t)}return st()}async applyAgentFeatures(t){if(!t)return;const e=P(t,O),o=P(t,W);for(const[a,i]of this.sessions){if(i.config.agentId!==t)continue;const n=i.runtime;try{if(e){const r=await D(t,await this.resolveAgentAiInfo(t)),d=n.addBuiltInTools?.(r)??0;d>0&&l(`[acode-adapter] session ${a}: ${d} desktop tools hot-added for agent ${t}`)}else{const r=n.removeBuiltInToolsByPrefix?.("desktop_")??0;r>0&&l(`[acode-adapter] session ${a}: ${r} desktop tools hot-removed for agent ${t}`)}}catch(r){l(`[acode-adapter] session ${a}: desktop feature hot-sync failed for agent ${t}:`,r.message)}if(o)try{const r=await v(t,i.config.scopeKey,typeof i.config.browserAllowPrivate=="boolean"?{allowPrivate:i.config.browserAllowPrivate}:void 0),d=n.addBuiltInTools?.(r)??0;d>0&&l(`[acode-adapter] session ${a}: ${d} browser tools hot-added for agent ${t}`)}catch(r){l(`[acode-adapter] session ${a}: browser feature hot-sync failed for agent ${t}:`,r.message)}}this.syncMcpToolsForAgent(t);try{F(t)}catch(a){l(`[acode-adapter] skill farm hot-sync failed for agent ${t}:`,a.message)}try{await L.applyCapabilities(t)}catch(a){l(`[acode-adapter] plugin capability hot-sync failed for agent ${t}:`,a.message)}const s=dt(t);for(const[a,i]of this.sessions)if(i.config.agentId===t)try{i.runtime.setToolPermissionPolicy?.(s)}catch(n){l(`[acode-adapter] session ${a}: tool permission policy hot-sync failed for agent ${t}:`,n.message)}}syncMcpToolsForAgent(t){if(t){for(const[e,o]of this.sessions)if(o.config.agentId===t)try{o.runtime.requestMcpToolsRefresh?.()}catch(s){l(`[acode-adapter] session ${e}: mcp tools refresh failed for agent ${t}:`,s.message)}}}getPendingQuestion(t){const e=this.sessions.get(t);if(e?.pendingQuestionToolCallId)return{toolCallId:e.pendingQuestionToolCallId,questions:e.pendingQuestions??[]}}cleanup(){for(const t of this.sessions.keys())this.terminateSession(t)}setIdleCommands(t,e){const o=this.sessions.get(t);if(o){if(o.idleCommands=e,this.cancelScheduledAutoPoll(o),e.idleInputCommand&&$(e.idleInputCommand)){this.getIdlePollLoop(o).start();return}o.adapterSession.status==="waiting_input"&&(e.idleInputCommand||e.nonInputCommand)&&this.getIdlePollLoop(o).start()}}setRuntimeConstructorForTest(t){this.runtimeConstructor=t}async loadAcodeProviderSection(t){try{const e=await V.readFile(t,"utf-8"),o=JSON.parse(e),a=o.provider?.default,i=a?.options,n=h(o.contextLimit),r={},d={},p=a?.models;if(p&&typeof p=="object"){for(const[m,c]of Object.entries(p))if(c&&typeof c=="object"){const g=h(c.contextLimit);g>0&&(r[m.trim()]=g);const f=c.modalities?.input,_=Array.isArray(f)&&f.includes("image")||c.supportsImageInput===!0;d[m.trim()]=_===!0}}return{...a?.model?{model:a.model}:{},...i?.baseURL||i?.baseUrl?{baseURL:i?.baseURL||i?.baseUrl}:{},...i?.apiKey?{apiKey:i.apiKey}:{},...n>0?{contextLimit:n}:{},...Object.keys(r).length>0?{modelContextLimits:r}:{},...Object.keys(d).length>0?{modelImageSupport:d}:{}}}catch(e){const o=e instanceof Error?e.message:String(e);return E().log({level:"warn",category:"load",message:`loadAcodeProviderSection failed: ${t}: ${o}`,source:"bridge"}),{}}}async resolveAcodeProvider(t,e){const o=typeof t.model=="string"&&t.model.trim()?t.model.trim():void 0,s=E();s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u8F93\u5165",source:"bridge",details:{configModel:t.model??null,configInstanceAiConfig:t.instanceAiConfig?B(t.instanceAiConfig):null,configPath:e}});const a={...t.instanceAiConfig?.model?{model:t.instanceAiConfig.model}:{},...t.instanceAiConfig?.baseURL?{baseURL:t.instanceAiConfig.baseURL}:{},...t.instanceAiConfig?.apiKey?{apiKey:t.instanceAiConfig.apiKey}:{},...t.instanceAiConfig?.effort?{effort:t.instanceAiConfig.effort}:{},...o?{model:o}:{}},i=await this.loadAcodeProviderSection(e);let n=i.contextLimit;if(!n&&i.modelContextLimits){const d=o||t.instanceAiConfig?.model||i.model;d&&i.modelContextLimits[d]&&(n=i.modelContextLimits[d])}const r=ht(a,i,"acode-default");return s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u89E3\u6790\u7ED3\u679C",source:"bridge",details:{instanceConfig:B(a),globalConfigModel:i?.model??null,resolvedModel:r.model,resolvedBaseURL:r.baseURL??null,resolvedApiKey:r.apiKey?"[set]":null,configContextLimit:n,resolvedSource:r.source}}),{resolved:r,configPath:e,perSessionModel:o,configContextLimit:n,supportsImage:i.modelImageSupport?.[r.model]===!0}}async createRuntime(t,e,o,s,a){const i=await this.loadRuntimeConstructor(),n=o.mcpConfigPath||x();let r=[];try{r=await v(o.agentId,o.scopeKey,typeof o.browserAllowPrivate=="boolean"?{allowPrivate:o.browserAllowPrivate}:void 0)}catch(c){l(`[acode-adapter] browser tools injection skipped for agent ${o.agentId}:`,c.message)}let d=[];if(P(o.agentId??"",W)&&(d=r),o.agentId&&P(o.agentId,O))try{const c=s?.model?{model:s.model,supportsImage:a===!0,...s.baseURL?{baseURL:s.baseURL}:{},...s.apiKey?{apiKey:s.apiKey}:{}}:void 0,g=await D(o.agentId,c);g.length>0&&(d=[...d,...g])}catch(c){l(`[acode-adapter] desktop tools injection skipped for agent ${o.agentId}:`,c.message)}const p=new i({sessionId:t,agentId:o.agentId,workingDirectory:o.workingDirectory,configPath:n,skillsDir:this.resolveAgentSkillsDir(o.agentId),pluginsDir:o.agentId?et(o.agentId):void 0,model:e,systemPrompt:typeof o.systemPrompt=="string"?o.systemPrompt:void 0,globalPrompt:typeof o.globalPrompt=="string"?o.globalPrompt:void 0,rolePrompt:typeof o.rolePrompt=="string"?o.rolePrompt:void 0,memory:typeof o.memory=="string"?o.memory:void 0,env:o.envOverrides,subAgentManager:ct,terminalManager:at,pluginBridge:o.agentId?L.forAgent(o.agentId):void 0,commandDirs:[Z(new URL("../services/builtin-commands/",import.meta.url))],...o.subAgentModel?{subAgentModel:o.subAgentModel}:{},...o.subAgentEffort?{subAgentEffort:o.subAgentEffort}:{},...o.instanceAiConfig?.effort?{effort:o.instanceAiConfig.effort}:{},onFileChange:c=>mt({...c,agentId:o.agentId,workspacePath:o.workingDirectory}),extraBuiltInTools:d,...o.agentId?{toolPermissionPolicy:lt(o.agentId)??void 0}:{},subAgentSuiteTools:r,subAgentSuiteToolProvider:this.buildSubAgentSuiteToolProvider(o.agentId)});return E().log({level:"info",category:"resolve",message:"ACode provider resolved",source:"bridge",sessionId:t,details:{resolvedModel:e,perInstanceModel:o.instanceAiConfig?.model||null,perInstanceBaseURL:o.instanceAiConfig?.baseURL?"[set]":null,perInstanceApiKey:o.instanceAiConfig?.apiKey?"[set]":null,configPath:n}}),p.on("event",c=>this.forwardAcodeEvent(c)),p}buildSubAgentSuiteToolProvider(t){return async e=>{if(!t)return[];if(!P(t,O))return[];if(e.length===0)return ut();if(!e.some(a=>{const i=a.trim().replace(/[.*]+$/,"");return i==="desktop"||i.startsWith("desktop.")}))return[];const s=await this.resolveAgentAiInfo(t);return D(t,s)}}async loadRuntimeConstructor(){if(this.runtimeConstructor)return this.runtimeConstructor;const t=Pt(),e=await import(Lt(t));if(!e.ACodeRuntime)throw new Error(`ACode runtime export not found: ${t}`);return this.runtimeConstructor=e.ACodeRuntime,this.runtimeConstructor}requireSession(t){const e=this.sessions.get(t);if(!e)throw new Error("ACode session not found");return e}startInitialPromptTurn(t,e){const o=this.sessions.get(t);o&&(o.adapterSession.status="thinking",this.emit("status-change",t,"thinking"),this.startSubmitTimeoutDetection(t,o,"startInitialPromptTurn"),o.runtime.submit(e).then(()=>{this.cancelSubmitTimeoutDetection(o),this.recoverResolvedTurnWithoutTerminalEvent(t)}).catch(s=>{const a=this.sessions.get(t);a&&(this.cancelSubmitTimeoutDetection(a),this.emitRuntimeError(t,s,{recover:!0}))}))}forwardAcodeEvent(t){const e=this.sessions.get(t.sessionId);if(e){if(e.autoPollActive&&(e.autoPollLastActivityAt=Date.now()),t.type==="assistant_delta"){const o=typeof t.data.text=="string"?t.data.text:"";e.autoPollActive&&(e.autoPollObservedAssistantText=!0),e.adapterSession.messages.push({id:R(),sessionId:t.sessionId,role:"assistant",content:o,timestamp:t.timestamp}),this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"text_delta",{text:o});return}if(t.type==="thinking_delta"){const o=typeof t.data.text=="string"?t.data.text:"";this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"thinking",{text:o});return}if(t.type==="command_output"){this.emitProviderEvent(t,"command_output",{text:typeof t.data.text=="string"?t.data.text:"",stream:Ot(t.data.stream),sequence:typeof t.data.sequence=="number"?t.data.sequence:void 0,cwd:typeof t.data.cwd=="string"?t.data.cwd:void 0,commandLineCount:typeof t.data.commandLineCount=="number"?t.data.commandLineCount:void 0,commandFilePath:typeof t.data.commandFilePath=="string"?t.data.commandFilePath:void 0});return}if(t.type==="background_command_completed"){this.emitProviderEvent(t,"background_command_completed",t.data);return}if(t.type==="ai_request"){const o=z(t.data),s=t.data.lastUsage,a=h(s?.inputTokens),i=h(s?.outputTokens),n=h(s?.cacheHitTokens),r=h(s?.cacheMissTokens),d=!!(s&&(a>0||i>0));let p;if(d&&(a>0&&(e.adapterSession.totalUsage.inputTokens=a),i>0&&(e.adapterSession.totalUsage.outputTokens=i),(s?.cacheHitTokens!==void 0||s?.cacheMissTokens!==void 0)&&(e.adapterSession.totalUsage.cacheHitTokens=n,e.adapterSession.totalUsage.cacheMissTokens=r),p={...e.adapterSession.totalUsage},C(`ai_request lastUsage applied: ${JSON.stringify(s)} -> totalUsage=${JSON.stringify(e.adapterSession.totalUsage)}`)),!d&&o>0){const g=e.adapterSession.totalUsage.inputTokens;g>0?e.adapterSession.totalUsage.inputTokens=Math.min(o,Math.floor(g*1.5)):e.adapterSession.totalUsage.inputTokens=o,p={...e.adapterSession.totalUsage}}e.adapterSession.status==="compacting"&&(e.adapterSession.status="thinking"),e.adapterSession.status==="waiting_input"&&(e.adapterSession.status="thinking");const c={actionType:"ai_request",actionLabel:"AI\u8BF7\u6C42",actionDetail:this.formatAiRequestActionDetail(t.data)};p&&(c.usage=p),C(`ai_request handled: estimatedContextTokens=${t.data.estimatedContextTokens??"n/a"} totalUsage=${JSON.stringify(e.adapterSession.totalUsage)} displayUsage=${JSON.stringify(p??null)}`),this.emit("status-change",t.sessionId,e.adapterSession.status,c),this.startDataFlowWatchdog(t.sessionId,e),e.adapterSession.status==="thinking"&&e.watchdogSubmitStartTime&&(e.watchdogSubmitStartTime=Date.now()),this.emitProviderEvent(t,"ai_request",{model:typeof t.data.model=="string"?t.data.model:void 0,endpoint:typeof t.data.endpoint=="string"?t.data.endpoint:void 0,requestMethod:typeof t.data.requestMethod=="string"?t.data.requestMethod:void 0,requestUrl:typeof t.data.requestUrl=="string"?t.data.requestUrl:void 0,requestHeaders:Et(t.data.requestHeaders),stream:typeof t.data.stream=="boolean"?t.data.stream:void 0,messageCount:typeof t.data.messageCount=="number"?t.data.messageCount:void 0,toolCount:typeof t.data.toolCount=="number"?t.data.toolCount:void 0,lastUserMessagePreview:typeof t.data.lastUserMessagePreview=="string"?t.data.lastUserMessagePreview:void 0,requestBody:typeof t.data.requestBody=="string"?t.data.requestBody:void 0});return}if(t.type==="turn_complete"){const o=Dt(t.data.usage);if(o){const a=e.adapterSession.totalUsage.inputTokens,i=a>0&&o.inputTokens<Math.floor(a*.2)&&!o.hasCache;C(`turn_complete usage: raw=${JSON.stringify(t.data.usage)} parsed=${JSON.stringify(o)} currentInput=${a} threshold20pct=${Math.floor(a*.2)} isFallbackUsage=${i}`),i?o.outputTokens>0&&(e.adapterSession.totalUsage.outputTokens=o.outputTokens):(e.adapterSession.totalUsage.inputTokens=o.inputTokens,e.adapterSession.totalUsage.outputTokens=o.outputTokens),(o.cacheHitTokens||o.cacheMissTokens)&&(e.adapterSession.totalUsage.cacheHitTokens=o.cacheHitTokens,e.adapterSession.totalUsage.cacheMissTokens=o.cacheMissTokens,l(`[ACode] \u{1F4B0} Cache usage (overwrite from runtime cumulative): hit=${e.adapterSession.totalUsage.cacheHitTokens}, miss=${e.adapterSession.totalUsage.cacheMissTokens} (turn delta: hit=${o.cacheHitTokens||0}, miss=${o.cacheMissTokens||0})`)),C(`totalUsage after turn_complete: ${JSON.stringify(e.adapterSession.totalUsage)}`)}else C(`turn_complete has NO parseable usage: raw=${JSON.stringify(t.data.usage)} failed=${t.data.failed===!0} aborted=${t.data.aborted===!0}`);const s=e.adapterSession.status;if(l(`[ACode] turn_complete event received (session: ${t.sessionId}), previous status: ${s}, failed: ${t.data.failed===!0}, aborted: ${t.data.aborted===!0}`),Mt(t.data)){l(`[ACode] Recovering from failed turn_complete (session: ${t.sessionId})`),this.cancelDataFlowWatchdog(e),this.recoverTurnFailure(t.sessionId,e);return}if(t.data.aborted===!0&&e.autoPollActive){l(`[ACode] Recovering from aborted turn_complete (session: ${t.sessionId}), using R2 2s backoff instead of immediate rearm`),this.cancelDataFlowWatchdog(e),this.recoverTurnFailure(t.sessionId,e);return}if(this.cancelDataFlowWatchdog(e),this.emitProviderEvent(t,"turn_complete",{usage:e.adapterSession.totalUsage,responseBody:typeof t.data.responseBody=="string"?t.data.responseBody:void 0,finishReason:typeof t.data.finishReason=="string"?t.data.finishReason:void 0}),e.adapterSession.status="waiting_input",this.emit("status-change",t.sessionId,"waiting_input",{usage:e.adapterSession.totalUsage}),C(`status-change emitted (waiting_input after turn_complete): usage=${JSON.stringify(e.adapterSession.totalUsage)}`),l(`[ACode] Status changed to waiting_input after turn_complete (session: ${t.sessionId})`),e.autoPollActive&&e.autoPollObservedAssistantText&&!e.autoPollObservedPollingTool&&!e.autoPollObservedAnyTool){e.stopAutoPollAfterCurrentTurn=!0,console.warn(`[${this.displayName}] Auto-poll turn produced assistant text without calling any tool; stopping auto-poll rearm for session ${t.sessionId}`);return}this.armIdleLoopOrAutoPoll(t.sessionId);return}if(t.type==="session_complete"){this.cancelDataFlowWatchdog(e),this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.idlePollLoop?.stop(),e.adapterSession.status="completed",this.emitProviderEvent(t,"session_complete",{exitCode:Number(t.data.exitCode)||0}),this.emit("status-change",t.sessionId,"completed");return}if(t.type==="question_request"){this.cancelDataFlowWatchdog(e),this.cancelSubmitTimeoutDetection(e);const o=typeof t.data.toolCallId=="string"?t.data.toolCallId:"",s=Array.isArray(t.data.questions)?t.data.questions:[];o&&(e.pendingQuestionToolCallId=o,e.pendingQuestions=s),this.emit("ask-user-question",{sessionId:t.sessionId,toolCallId:o||void 0,questions:s});return}if(t.type==="tool_permission_request"){this.cancelDataFlowWatchdog(e),this.cancelSubmitTimeoutDetection(e);const o=typeof t.data.requestId=="string"?t.data.requestId:"";o&&(e.pendingToolAuthRequests||(e.pendingToolAuthRequests=new Map),e.pendingToolAuthRequests.set(o,{capability:String(t.data.capability||""),detail:{path:t.data.path,command:t.data.command,cwd:t.data.cwd,operation:t.data.operation},requestedAt:Date.now()})),this.emit("tool-permission-request",{sessionId:t.sessionId,requestId:o,capability:String(t.data.capability||""),agentId:e.config.agentId,detail:{path:t.data.path,command:t.data.command,cwd:t.data.cwd,operation:t.data.operation}});return}if(t.type==="error"){if(t.data.nonFatal){this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")});return}this.cancelDataFlowWatchdog(e),e.adapterSession.status="error",this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")}),this.emit("status-change",t.sessionId,"error"),e.autoPollActive||this.recoverTurnFailure(t.sessionId,e);return}if(t.type==="mcp_status"){if(!t.data.action&&Array.isArray(t.data.tools)){const f=t.data.tools;f.some(T=>pt(T))||console.warn(`[${this.displayName}] ACode runtime is not exposing a poll_message tool. The LLM cannot call poll_message. Please configure an MCP server that provides poll_message (e.g. aws-mcp) in the dashboard panel for this runtime.`);const A=Array.isArray(t.data.servers)?t.data.servers:[],y=f.map(T=>({name:String(T.name??""),source:String(T.source??""),description:String(T.description??"")})).filter(T=>T.name),b={kind:"mcp",mcpServers:A,mcpTools:y};e.cachedMcpCapabilities=b,e.lastMcpCapabilities=b,e.lastCombinedCapabilities={kind:"combined",mcpServers:A,mcpTools:y,skills:e.lastCombinedCapabilities?.skills??[],skillsDir:e.lastCombinedCapabilities?.skillsDir,skillGroups:e.lastCombinedCapabilities?.skillGroups??[]},l(`[cap-diag] MCP capabilities cached: ${A.length} servers, ${y.length} tools`,{sessionId:t.sessionId,servers:A,toolNames:y.map(T=>T.name)}),e.capabilitiesFallbackTimer&&clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=setTimeout(()=>{const T=e.cachedMcpCapabilities;if(!T)return;const k={...T,skills:[],skillGroups:[]};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:k,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(k)}),l(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${Q}ms)`,{sessionId:t.sessionId,mcpServers:T.mcpServers,mcpToolsCount:T.mcpTools?.length??0}),e.cachedMcpCapabilities=void 0,e.capabilitiesFallbackTimer=void 0},Q);return}const o=String(t.data.action??"");l(`[ACode] \u{1F4E1} mcp_status event: action=${o}, server=${typeof t.data.server=="string"?t.data.server:"?"}, tool=${typeof t.data.tool=="string"?t.data.tool:"?"}, toolUseId=${typeof t.data.toolUseId=="string"?t.data.toolUseId.slice(0,16)+"\u2026":"none"}, failed=${t.data.failed===!0}, session=${t.sessionId}, status=${e.adapterSession.status}`);const s=typeof t.data.server=="string"?t.data.server:"mcp",a=typeof t.data.tool=="string"?t.data.tool:"",i=s&&a?`${s}__${a}`:a||s,n=t.data.arguments&&typeof t.data.arguments=="object"&&!Array.isArray(t.data.arguments)?t.data.arguments:void 0,r=Tt(i,n);e.autoPollActive&&(e.autoPollObservedAnyTool=!0,r.actionType==="idle"&&(e.autoPollObservedPollingTool=!0));const d=It(r.actionType)?r.actionDetail||Y(void 0,s,a):Y(r.actionDetail,s,a),p=typeof t.data.toolUseId=="string"?t.data.toolUseId:void 0,m=n?JSON.stringify(n,null,2):void 0,c={toolName:d,rawToolName:i,actionType:r.actionType,actionLabel:r.actionLabel,actionDetail:d,...m?{actionInput:m}:{},...n?{toolInput:n}:{},...p?{toolUseId:p,actionId:p}:{},metadata:{mcpServer:s}};if(t.data.action==="tool_result"){this.emitProviderEvent(t,"tool_use_end",{...c,toolResult:typeof t.data.result=="string"?t.data.result:JSON.stringify(t.data.result??"")});const f=r.actionType==="idle"&&!Ut(t.data.result)?"waiting_input":"thinking";e.adapterSession.status=f,r.actionType==="idle"&&(e.idleToolActiveAt=void 0),this.cancelDataFlowWatchdog(e),e.pendingToolEventData=void 0;const _=r.actionType==="idle";this.emit("status-change",t.sessionId,f,_||f==="thinking"?void 0:c);return}const g=r.actionType==="idle"?"waiting_input":"tool_using";e.adapterSession.status=g,r.actionType==="idle"&&(e.idleToolActiveAt=Date.now()),g==="tool_using"&&(this.resetDataFlowWatchdog(t.sessionId,e),e.pendingToolEventData={...c}),this.emitProviderEvent(t,"tool_use_start",c),this.emit("status-change",t.sessionId,g,c);return}if(t.type==="skill_status"){const s=(Array.isArray(t.data.skills)?t.data.skills:[]).map(m=>({name:String(m.name??""),description:typeof m.description=="string"?m.description:void 0,directory:typeof m.directory=="string"?m.directory:void 0})).filter(m=>m.name),a=typeof t.data.skillsDir=="string"?t.data.skillsDir:void 0,n=(Array.isArray(t.data.skillGroups)?t.data.skillGroups:[]).map(m=>{const c=Array.isArray(m.skills)?m.skills:[];return{dir:typeof m.dir=="string"?m.dir:"",exists:m.exists===!0,skills:c.map(g=>({name:String(g.name??""),description:typeof g.description=="string"?g.description:void 0,directory:typeof g.directory=="string"?g.directory:void 0})).filter(g=>g.name)}}).filter(m=>m.dir),r=e.cachedMcpCapabilities??e.lastMcpCapabilities,d=e.cachedMcpCapabilities!==void 0,p={kind:"combined",mcpServers:r?.mcpServers??[],mcpTools:r?.mcpTools??[],skills:s,skillsDir:a,skillGroups:n};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:p,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(p)}),e.lastCombinedCapabilities=p,l(`[cap-diag] Combined capabilities emitted ${d?"(startup merge)":"(runtime refresh)"}: ${(r?.mcpTools??[]).length} MCP tools, ${s.length} skills`,{sessionId:t.sessionId,mcpServers:r?.mcpServers??[],skillsDir:a}),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),e.cachedMcpCapabilities=void 0;return}if(t.type==="startup_phase"){l(`[ACode] \u{1F680} startup_phase swallowed: ${String(t.data.phase??"unknown")}`,{sessionId:t.sessionId});return}if(t.type==="command_status"){if(t.data.action==="commands_loaded"){l(`[ACode] \u{1F4CB} command_status commands_loaded swallowed: ${typeof t.data.installedCommandCount=="number"?t.data.installedCommandCount:"?"} commands`,{sessionId:t.sessionId});return}this.emitProviderEvent(t,"thinking",{text:JSON.stringify(t.data)});return}if(t.type==="session_started"){const o=z(t.data),s=h(t.data.contextLimit)||void 0,a=h(t.data.maxOutput)||void 0,i=h(t.data.inputLimit)||void 0,n=h(t.data.compactionThreshold)||void 0;let r=!1;o>0&&(e.adapterSession.totalUsage.inputTokens=o,e.adapterSession.totalUsage.outputTokens=0,r=!0),s!==void 0&&(e.adapterSession.totalUsage.contextLimit=s,r=!0),a!==void 0&&(e.adapterSession.totalUsage.maxOutput=a,r=!0),i!==void 0&&(e.adapterSession.totalUsage.inputLimit=i,r=!0),n!==void 0&&(e.adapterSession.totalUsage.compactionThreshold=n,r=!0),r&&this.emit("status-change",t.sessionId,e.adapterSession.status,{usage:e.adapterSession.totalUsage});return}if(t.type==="exit_plan_mode"){e.adapterSession.status="waiting_input";const o=typeof t.data.plan=="string"?t.data.plan:void 0;this.emitProviderEvent(t,"exit_plan_mode",{toolInput:t.data.toolInput&&typeof t.data.toolInput=="object"?t.data.toolInput:void 0,text:o||"\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"}),this.emit("status-change",t.sessionId,"waiting_input",{actionLabel:"\u5BA1\u6279\u8BA1\u5212",actionDetail:o||"\u7B49\u5F85\u7528\u6237\u5BA1\u6279\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"});return}if(t.type==="context_compaction"){const o=t.data,s=o.action,a=s==="start",i=s==="end",n=s==="phase",r=o.parentId,d=o.compactionId;let p,m;if(a){const g=String(o.sizeKB??o.sizeBeforeKB??"?"),f=String(o.messageCount??o.messageCountBefore??"?"),_=o.estimatedTokens,A=o.actualInputTokens?`input=${o.actualInputTokens}`:"",y=o.actualOutputTokens?`output=${o.actualOutputTokens}`:"",b=o.trigger?`, \u89E6\u53D1: ${o.trigger}`:"";p="\u4E0A\u4E0B\u6587\u538B\u7F29\u5F00\u59CB",_!==void 0?m=`\u538B\u7F29\u524D: ~${_} tokens, \u6D88\u606F: ${f}\u6761`+(A?`, \u5B9E\u9645 tokens: ${A} ${y}`:"")+b:m=`\u538B\u7F29\u524D: ${g}KB, \u6D88\u606F: ${f}\u6761`+(A?`, \u5B9E\u9645 tokens: ${A} ${y}`:"")+b}else if(n)p=String(o.phaseLabel??"\u538B\u7F29\u9636\u6BB5"),m=String(o.phaseDetail??"");else{const g=o.tokensBefore,f=o.tokensAfter,_=o.freedTokens,A=String(o.sizeBeforeKB??"?"),y=String(o.sizeAfterKB??"?"),b=String(o.freedKB??"?"),T=String(o.strategy??"?"),k=Number(o.prunedToolMessages??0),S=Number(o.summarizedMessages??0);p="\u538B\u7F29\u5B8C\u6210",g!==void 0&&f!==void 0?m=`\u91CA\u653E: ${_??g-f} tokens, ${g} tokens \u2192 ${f} tokens, \u7B56\u7565: ${T}`+(k?`, \u88C1\u526A\u5DE5\u5177: ${k}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:""):m=`\u91CA\u653E: ${b}KB, ${A}KB \u2192 ${y}KB, \u7B56\u7565: ${T}`+(k?`, \u88C1\u526A\u5DE5\u5177: ${k}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:"")}a&&(e.adapterSession.status="compacting");const c={actionType:"context_compaction",actionLabel:p,actionDetail:m};if(a&&d)c.actionId=d;else if((n||i)&&d)if(c.parentId=d,n){const g=String(o.phase??"unknown");c.actionId=`${d}-phase-${g}`}else c.actionId=`${d}-end`;n&&r&&!c.parentId&&(c.parentId=r),i&&(e.adapterSession.totalUsage.inputTokens=h(o.tokensAfter),e.adapterSession.totalUsage.outputTokens=0,e.config.agentId&&L.markAgentCompacted(e.config.agentId),c.usage={...e.adapterSession.totalUsage}),this.emitProviderEvent(t,"context_compaction",c),this.emit("status-change",t.sessionId,"compacting",c);return}this.emitProviderEvent(t,"thinking",{text:JSON.stringify(t.data)})}}emitProviderEvent(t,e,o){this.emit("event",{type:e,sessionId:t.sessionId,timestamp:t.timestamp,data:o})}armIdleLoopOrAutoPoll(t){const e=this.sessions.get(t);if(!(!e||this.isTerminalStatus(e.adapterSession.status))){if(e.idleCommands?.idleInputCommand){this.getIdlePollLoop(e).start();return}this.scheduleAutoPoll(t)}}getIdlePollLoop(t){if(t.idlePollLoop)return t.idlePollLoop;const e=t.adapterSession.sessionId;return t.idlePollLoop=new gt({getStatus:()=>this.sessions.get(e)?.adapterSession.status,getCommand:()=>this.sessions.get(e)?.idleCommands?.idleInputCommand,isPollingCommand:o=>$(o),preDispatchHook:()=>null,dispatchPollingPrompt:(o,s)=>this.submitAutoPollPrompt(e,s?o:wt,{keepWaitingStatus:!0}),dispatchRegularIdleCommand:o=>this.submitAutoPollPrompt(e,o)}),t.idlePollLoop}submitAutoPollPrompt(t,e,o){const s=this.sessions.get(t);if(!s||this.isTerminalStatus(s.adapterSession.status))return;const a=(s.autoPollToken??0)+1;s.autoPollToken=a,s.autoPollActive=!0,s.autoPollObservedPollingTool=!1,s.autoPollObservedAssistantText=!1,s.autoPollObservedAnyTool=!1,s.stopAutoPollAfterCurrentTurn=!1,s.autoPollQueuedAt=Date.now(),s.autoPollLastActivityAt=Date.now(),s.idleToolActiveAt=void 0,o?.keepWaitingStatus||(s.adapterSession.status="thinking",this.emit("status-change",t,"thinking")),this.startSubmitTimeoutDetection(t,s,"submitAutoPollPrompt"),s.runtime.submit(e).catch(i=>{this.emitRuntimeError(t,i)}).finally(()=>{const i=this.sessions.get(t);if(i){if(i.autoPollToken!==a){l(`[ACode] submitAutoPollPrompt.finally: token mismatch (my=${a}, current=${i.autoPollToken}), skipping cleanup \u2014 newer turn has taken over`);return}if(this.cancelSubmitTimeoutDetection(i),i.autoPollActive=!1,i.failedTurnRecoveryScheduled||i.autoPollTimer){i.failedTurnRecoveryScheduled=!1;return}if(i.stopAutoPollAfterCurrentTurn){i.stopAutoPollAfterCurrentTurn=!1,i.autoPollObservedPollingTool=!1,i.autoPollObservedAssistantText=!1,i.autoPollObservedAnyTool=!1;return}if(i.adapterSession.status==="error"){this.recoverTurnFailure(t,i);return}if(i.adapterSession.status==="thinking"||i.adapterSession.status==="tool_using"){this.recoverResolvedTurnWithoutTerminalEvent(t);return}i.adapterSession.status==="waiting_input"&&this.armIdleLoopOrAutoPoll(t)}})}scheduleAutoPoll(t,e=kt){const o=this.sessions.get(t);!o||o.autoPollActive||o.autoPollTimer||this.isTerminalStatus(o.adapterSession.status)||(o.autoPollTimer=setTimeout(()=>{const s=this.sessions.get(t);!s||s.autoPollActive||this.isTerminalStatus(s.adapterSession.status)||(s.autoPollTimer=void 0,this.submitAutoPollPrompt(t,H,{keepWaitingStatus:!0}))},e))}cancelScheduledAutoPoll(t){t.autoPollTimer&&(clearTimeout(t.autoPollTimer),t.autoPollTimer=void 0)}isTerminalStatus(t){return t==="completed"||t==="terminated"}emitRuntimeError(t,e,o){const s=this.sessions.get(t);s&&(s.adapterSession.status="error",this.emitRuntimeErrorEvent(t,e),this.emit("status-change",t,"error"),o?.recover&&this.recoverTurnFailure(t,s))}emitRuntimeErrorEvent(t,e){const o=new Date().toISOString();this.emit("event",{type:"error",sessionId:t,timestamp:o,data:{text:e instanceof Error?e.message:String(e)}})}estimateOutputTokensFromMessages(t){const e=t.filter(o=>o.role==="assistant").map(o=>o.content).join("");return Math.max(0,Math.round(e.length/4))}recoverTurnFailure(t,e){if(this.isTerminalStatus(e.adapterSession.status))return;if(e.idleToolActiveAt=void 0,e.pendingToolAuthRequests?.size&&e.pendingToolAuthRequests.clear(),e.adapterSession.status==="tool_using"&&e.pendingToolEventData){const n=e.pendingToolEventData;l(`[ACode] recoverTurnFailure: \u8865\u53D1 tool_use_end (tool: ${n.toolName??"unknown"}) \u5173\u95ED\u65F6\u95F4\u7EBF\u6761\u76EE\uFF0C\u907F\u514D\u6C38\u4E45\u5361\u5728\u5DE5\u5177\u6267\u884C\u72B6\u6001`),this.emit("event",{type:"tool_use_end",sessionId:t,timestamp:new Date().toISOString(),data:{...n,toolResult:"[aborted] \u5DE5\u5177\u6267\u884C\u88AB\u6570\u636E\u6D41\u770B\u95E8\u72D7\u4E2D\u65AD"}}),e.pendingToolEventData=void 0}const o=this.estimateOutputTokensFromMessages(e.adapterSession.messages);if(o>0){const n=e.adapterSession.totalUsage.outputTokens;e.adapterSession.totalUsage.outputTokens=o,l(`[ACode] recoverTurnFailure: \u4F30\u7B97\u8986\u76D6 output tokens ${n} -> ${o} (totalUsage now: input=${e.adapterSession.totalUsage.inputTokens}, output=${e.adapterSession.totalUsage.outputTokens})`)}const s=e.idleCommands?.idleInputCommand,i=!!(s&&$(s))?{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:"aws-mcp/poll_message"}:void 0;if(e.autoPollTimer||e.failedTurnRecoveryScheduled){e.adapterSession.status!=="waiting_input"&&(e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage,...i}));return}this.cancelScheduledAutoPoll(e),e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage,...i}),e.failedTurnRecoveryScheduled=!0,e.autoPollTimer=setTimeout(()=>{const n=this.sessions.get(t);if(!n||n.autoPollActive||this.isTerminalStatus(n.adapterSession.status)){n&&(n.failedTurnRecoveryScheduled=!1);return}n.autoPollTimer=void 0,n.failedTurnRecoveryScheduled=!1,n.idleCommands?.idleInputCommand?this.getIdlePollLoop(n).start():this.submitAutoPollPrompt(t,H,{keepWaitingStatus:!0})},Ct)}recoverResolvedTurnWithoutTerminalEvent(t){l(`[ACode] \u25B6\uFE0F recoverResolvedTurnWithoutTerminalEvent called for session ${t}`);const e=this.sessions.get(t);if(!e||this.isTerminalStatus(e.adapterSession.status)){l(`[ACode] Skipping recovery: entry=${!!e}, isTerminal=${e?this.isTerminalStatus(e.adapterSession.status):"N/A"}`);return}if(l(`[ACode] Current status: ${e.adapterSession.status}`),e.adapterSession.status!=="thinking"&&e.adapterSession.status!=="tool_using"){l(`[ACode] Skipping recovery: status is ${e.adapterSession.status} (not thinking/tool_using)`);return}console.warn(`[ACode] \u26A0\uFE0F Detected stuck state: ${e.adapterSession.status} after submit resolved! Recovering to waiting_input. If runtime.submit() is still pending (e.g., built-in tool execution not interruptible by abort()), the submitQueue is deadlocked and subsequent auto-poll/user submits will never execute.`),l("[ACode] Recovering to waiting_input..."),e.adapterSession.status="waiting_input";const o=e.idleCommands?.idleInputCommand,a=!!(o&&$(o))?{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:"aws-mcp/poll_message"}:void 0;this.emit("status-change",t,"waiting_input",a),l(`[ACode] \u2705 Recovery complete, status now: ${e.adapterSession.status}`),l("[ACode] \u25B6\uFE0F Arming idle loop or auto-poll..."),this.armIdleLoopOrAutoPoll(t),l("[ACode] \u2705 Idle loop armed, recovery process complete")}startSubmitTimeoutDetection(t,e,o){this.cancelSubmitTimeoutDetection(e),e.watchdogSubmitStartTime=Date.now(),e.watchdogLastDataFlowTime=Date.now(),e.watchdogTimer=setInterval(()=>{const s=this.sessions.get(t);if(!s){this.cancelSubmitTimeoutDetection(e);return}const a=s.adapterSession.status,i=Date.now();if(a==="waiting_input"){if(s.autoPollActive&&s.autoPollLastActivityAt&&s.idleToolActiveAt===void 0&&i-s.autoPollLastActivityAt>=bt){console.warn(`[ACode] CRITICAL: stuck auto-poll detected after ${Math.round((i-s.autoPollLastActivityAt)/1e3)}s (source: ${o}, session: ${t}): submit queued but no runtime events and no active poll_message tool. Forcing recovery (runtime force-settle will unblock submitQueue).`),this.executeWatchdogRecovery(t,s,o,"stuck_autopoll",i-s.autoPollLastActivityAt);return}s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(a==="tool_using"){const d=i-(s.watchdogLastDataFlowTime||i);if(d>=yt){const p=String(s.pendingToolEventData?.rawToolName??"");if((s.pendingToolAuthRequests?.size??0)>0){const m=s.pendingToolAuthRequests?.size??0;console.warn(`[ACode] tool_using elapsed ${Math.round(d/1e3)}s but ${m} tool permission request(s) awaiting user decision (session: ${t}), skipping recovery`),s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(p.endsWith("await_complete")){console.warn(`[ACode] tool_using elapsed ${Math.round(d/1e3)}s but await_complete is running (session: ${t}, tool: ${p}), skipping recovery (sub-agents have their own watchdogs, bg commands have timeout limits)`),s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(console.warn(`[ACode] CRITICAL: tool_using slow timeout triggered after ${Math.round(d/1e3)}s (source: ${o}, session: ${t}). Tool execution likely hung. Forcing recovery via recoverTurnFailure.`),s.runtime.detachCurrentToolExecution?.()){console.warn(`[ACode] tool_using slow (${Math.round(d/1e3)}s): detached tool ${p} to background (session: ${t}); waiting for the real tool_result to continue the turn`),s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}this.executeWatchdogRecovery(t,s,o,"tool_using_slow",d);return}s.watchdogSubmitStartTime=i;return}if(a!=="thinking")return;const n=i-(s.watchdogSubmitStartTime||i),r=i-(s.watchdogLastDataFlowTime||i);if(K>0&&r>=K){console.warn(`[ACode] CRITICAL: Data flow watchdog triggered after ${Math.round(r/1e3)}s (source: ${o}, session: ${t}, status: ${a}). No stream data received. Forcing recovery via recoverTurnFailure.`),this.executeWatchdogRecovery(t,s,o,"data_flow",r);return}if(j>0&&n>=j){console.warn(`[ACode] CRITICAL: submit timeout detected after ${Math.round(n/1e3)}s (source: ${o}, session: ${t}, status: ${a}). Forcing recovery to prevent permanent state stuck.`),this.executeWatchdogRecovery(t,s,o,"submit_timeout",n);return}},St)}executeWatchdogRecovery(t,e,o,s,a){l(`[ACode] Watchdog (${s}): calling runtime.abort() after ${Math.round(a/1e3)}s...`);try{e.runtime.abort(),l(`[ACode] Watchdog (${s}): runtime.abort() called`)}catch(i){console.warn(`[ACode] \u26A0\uFE0F Watchdog (${s}): runtime.abort() threw: ${i instanceof Error?i.message:String(i)}`)}this.cancelSubmitTimeoutDetection(e),this.recoverTurnFailure(t,e),console.warn(`[ACode] \u26A0\uFE0F Watchdog (${s}) recovery complete. If abort() could not cancel the underlying operation, submitQueue may still be deadlocked.`)}cancelSubmitTimeoutDetection(t){t.watchdogTimer&&(clearInterval(t.watchdogTimer),t.watchdogTimer=void 0),t.watchdogSubmitStartTime=void 0,t.watchdogLastDataFlowTime=void 0}resetDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now(),e.watchdogSubmitStartTime=Date.now()}startDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now()}cancelDataFlowWatchdog(t){t.watchdogLastDataFlowTime=Date.now()}formatAiRequestActionDetail(t){const e=`
|
|
1
|
+
import{EventEmitter as X}from"node:events";import V from"node:fs/promises";import U from"node:path";import{fileURLToPath as Z,pathToFileURL as tt}from"node:url";import{v4 as I}from"uuid";import{getBridgeInstancePluginsDir as et,getBridgeInstanceSkillsDir as st,getBridgeTemplateConfigFile as L,getBridgeTemplateSkillsDir as ot}from"../config.js";import{getReleasedAcodeEntryPath as it}from"../services/acode-package.js";import{syncAgentSkillFarm as F}from"../services/agent-capability-sync.js";import{agentPtyManager as at}from"../services/agent-pty-manager.js";import{getBrowserTools as v,closeBrowserSession as nt,registerListenerWakeHook as rt,unregisterListenerWakeHook as q}from"../services/browser-session-manager.js";import{getDesktopTools as D,getDesktopToolsIfReady as ut}from"../services/desktop-mcp-manager.js";import{redactForLog as B}from"../services/instance-ai-config-service.js";import{BROWSER_FEATURE_KEY as W,DESKTOP_FEATURE_KEY as x,getAgentToolPermissions as lt,isFeatureEnabled as P,resolveAgentToolPermissions as dt}from"../services/instance-features.js";import{pluginManager as $}from"../services/plugin-manager.js";import{subAgentManager as ct}from"../services/sub-agent-manager-instance.js";import{recordAgentFileChange as mt}from"../services/workspaceTaskChangeWatcher.js";import{getInstanceAiConfigDebugLogger as O}from"../utils/instance-ai-config-debug-logger.js";import{isMessagePollingIdleCommand as E,isPollMessageToolDescriptor as pt}from"./acode-tool-names.js";import{IdlePollLoop as gt}from"./idle-poll-loop.js";import{MESSAGE_POLLING_IDLE_PROMPT as ft}from"./prompts.js";import{resolveAcodeProviderConfig as ht}from"./instance-ai-config-resolver.js";import{getCanonicalMcpDetail as N,getToolActionInfo as Tt}from"./types.js";const At=process.env.ACODE_COMPACTION_DEBUG==="1";function l(...u){At&&console.log(...u)}const yt=process.env.ACODE_TOKEN_DEBUG==="1";function C(...u){yt&&console.log("[TokenDebug][Adapter]",...u)}const St=["Continue the AgentsWork message loop now.","You MUST call the exact MCP tool named aws-mcp__poll_message as your next action and wait for the next assigned message or task.","Do not answer in plain text before calling aws-mcp__poll_message.","After handling any received work, reply through the appropriate aws-mcp messaging tool, then continue listening again."].join(" "),_t=5e3;function M(u,t){const e=process.env[u];if(e===void 0||e.trim()==="")return t;const s=Number(e.trim());return Number.isFinite(s)&&s>=0?Math.floor(s):t}const H=M("ACODE_DATA_FLOW_WATCHDOG_MS",12e4),bt=5*6e4,kt=M("ACODE_STUCK_AUTOPOLL_TIMEOUT_MS",5*6e4),Ct=250,K=[0,3e3,1e4,3e4,6e4],j=M("ACODE_SUBMIT_TIMEOUT_MS",12e4),Q=3e3,wt=ft;function Pt(){return U.resolve(it())}function $t(u){return tt(U.resolve(u)).href}const G=128e3,w={"claude-4":{contextLimit:2e5,maxOutput:8192},"claude-sonnet-4":{contextLimit:2e5,maxOutput:8192},"claude-3-5-sonnet":{contextLimit:2e5,maxOutput:8192},"claude-3-opus":{contextLimit:2e5,maxOutput:8192},"claude-3-haiku":{contextLimit:2e5,maxOutput:8192},"claude-2":{contextLimit:1e5,maxOutput:4096},"gpt-4o":{contextLimit:128e3,maxOutput:16384},"gpt-4-turbo":{contextLimit:128e3,maxOutput:4096},"gpt-4":{contextLimit:8192,maxOutput:4096},"gpt-3.5-turbo":{contextLimit:16384,maxOutput:4096},o1:{contextLimit:2e5,maxOutput:1e5},"o3-mini":{contextLimit:2e5,maxOutput:1e5},"gemini-2.0-flash":{contextLimit:1048576,maxOutput:8192},"gemini-2.5-pro":{contextLimit:1048576,maxOutput:8192},"gemini-1.5-pro":{contextLimit:2097152,maxOutput:8192},"gemini-1.5-flash":{contextLimit:1048576,maxOutput:8192},deepseek:{contextLimit:64e3,maxOutput:8192},"deepseek-chat":{contextLimit:64e3,maxOutput:8192},"deepseek-reasoner":{contextLimit:64e3,maxOutput:8192},"qwen-max":{contextLimit:32e3,maxOutput:8192},"qwen-plus":{contextLimit:128e3,maxOutput:8192},"qwen-turbo":{contextLimit:1e6,maxOutput:8192},"glm-4":{contextLimit:128e3,maxOutput:4096},"yi-":{contextLimit:2e5,maxOutput:4096},"llama-3":{contextLimit:8192,maxOutput:4096},"llama-2":{contextLimit:4096,maxOutput:4096},mistral:{contextLimit:32e3,maxOutput:4096},mixtral:{contextLimit:32e3,maxOutput:4096},codestral:{contextLimit:256e3,maxOutput:8192},grok:{contextLimit:131072,maxOutput:4096},"nova-":{contextLimit:2e5,maxOutput:8192}};function Lt(u){if(!u)return G;const t=u.toLowerCase().trim(),e=w[t];if(e)return e.contextLimit;const s=Object.keys(w).filter(a=>t.startsWith(a)).sort((a,i)=>i.length-a.length),o=s[0]!==void 0?w[s[0]]:void 0;return o?o.contextLimit:G}const J=4096;function Dt(u){if(!u)return J;const t=u.toLowerCase().trim(),e=w[t];if(e)return e.maxOutput;const s=Object.keys(w).filter(a=>t.startsWith(a)).sort((a,i)=>i.length-a.length),o=s[0]!==void 0?w[s[0]]:void 0;return o?o.maxOutput:J}function xt(u){if(!u||typeof u!="object")return;const t=u,e=h(t.inputTokens,t.input_tokens,t.promptTokens,t.prompt_tokens),s=h(t.outputTokens,t.output_tokens,t.completionTokens,t.completion_tokens);if(e===0&&s===0)return;let o=h(t.cacheHitTokens,t.promptCacheHitTokens,t.prompt_cache_hit_tokens),a=h(t.cacheMissTokens,t.promptCacheMissTokens,t.prompt_cache_miss_tokens),i=t.cacheHitTokens!==void 0||t.cacheMissTokens!==void 0||t.promptCacheHitTokens!==void 0||t.promptCacheMissTokens!==void 0||t.prompt_cache_hit_tokens!==void 0||t.prompt_cache_miss_tokens!==void 0;if(o===0&&a===0){const r=h(t.cache_read_input_tokens),n=h(t.cache_creation_input_tokens);r>0||n>0?(o=r,a=n+h(t.input_tokens),i=!0):(o=h(t.cache_hit_tokens),a=h(t.cache_miss_tokens),i=i||t.cache_hit_tokens!==void 0||t.cache_miss_tokens!==void 0)}if(o===0&&a===0){const r=h(t.prompt_tokens_details?.cached_tokens);r>0&&(o=r,a=e>r?e-r:0,i=!0)}return{inputTokens:e,outputTokens:s,...o>0||a>0?{cacheHitTokens:o,cacheMissTokens:a}:{},...i?{hasCache:!0}:{}}}function h(...u){for(const t of u){const e=Number(t);if(Number.isFinite(e)&&e>0)return e}return 0}function z(u){return h(u.estimatedContextTokens,u.contextTokens,u.context_tokens,u.contextUsedTokens,u.context_used_tokens)}function Ot(u){return u==="stderr"||u==="mixed"?u:"stdout"}function Et(u){if(!u||typeof u!="object"||Array.isArray(u))return;const t={};for(const[e,s]of Object.entries(u))typeof s=="string"&&(t[e]=s);return Object.keys(t).length>0?t:void 0}function Mt(u){return u.failed===!0}function Y(u,t,e){if(!u)return e?`${t}/${e}`:t;const s=`${t}__`;return u.startsWith(s)?N(`${t}__${u.slice(s.length)}`):N(u.replace("/","__"))}function Rt(u){return u==="read_file"||u==="edit_file"||u==="write_file"||u==="bash"}function Ut(u){const t=It(u);return t?[t.directMessages,t.groupMessages,t.messages].some(e=>Array.isArray(e)&&e.length>0):typeof u=="string"&&u.trim().length>0}function It(u){if(u&&typeof u=="object"&&!Array.isArray(u))return u;if(!(typeof u!="string"||!u.trim()))try{const t=JSON.parse(u);return t&&typeof t=="object"&&!Array.isArray(t)?t:void 0}catch{return}}class ae extends X{constructor(){super(),this.providerId="acode",this.displayName="ACode",this.sessions=new Map,this.listenerWakeHooks=new Map,$.setOnPluginsChanged(t=>{this.syncPluginCommands(t)})}async syncPluginCommands(t){try{const e=await $.getPluginCommands(t);for(const s of this.sessions.values())s.config.agentId===t&&s.runtime.addPluginCommands?.(e)}catch(e){l(`[acode-adapter] syncPluginCommands failed for agent ${t}:`,e.message)}}async startSession(t,e){const s=e.mcpConfigPath||L(),{resolved:o,configContextLimit:a,supportsImage:i}=await this.resolveAcodeProvider(e,s),r=o.model,n=a??Lt(r),d=Dt(r),p={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0,contextLimit:n,maxOutput:d,cacheHitTokens:0,cacheMissTokens:0}};this.sessions.set(t,{adapterSession:p,config:e,runtime:await this.createRuntime(t,r,e,o,i),autoPollActive:!1,retryAttempt:0,retryActive:!1}),this.emit("status-change",t,"starting");const m=this.sessions.get(t);if(!m)throw new Error("ACode session was not initialized");const c=()=>{const g=this.sessions.get(t);g&&this.getIdlePollLoop(g).start()};this.listenerWakeHooks.set(t,c),rt(e.agentId,c);try{await m.runtime.start(),p.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:m.adapterSession.totalUsage}),e.initialPrompt?.trim()?this.startInitialPromptTurn(t,e.initialPrompt):this.armIdleLoopOrAutoPoll(t)}catch(g){p.status="error",this.emitRuntimeErrorEvent(t,g),this.emit("status-change",t,"error");const f=this.sessions.get(t);throw f?.capabilitiesFallbackTimer&&clearTimeout(f.capabilitiesFallbackTimer),this.sessions.delete(t),q(e.agentId,this.listenerWakeHooks.get(t)),this.listenerWakeHooks.delete(t),g}}async sendMessage(t,e){const s=Date.now();l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l("[ACode] \u25B6\uFE0F sendMessage STARTED"),l(`[ACode] Session: ${t}`),l(`[ACode] Message: ${e.slice(0,100)}${e.length>100?"...":""}`),l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");const o=this.requireSession(t),a=o.adapterSession.status;l(`[ACode] Previous status: ${a}`),this.cancelScheduledAutoPoll(o),o.failedTurnRecoveryScheduled=!1,o.idlePollLoop?.stop(),this.cancelRequestRetry(o),o.retryAttempt=0;const i={id:I(),sessionId:t,role:"user",content:e,timestamp:new Date().toISOString()};o.adapterSession.messages.push(i),this.emit("conversation-message",t,i),l(`[ACode] Changing status: ${a} \u2192 thinking`),o.adapterSession.status="thinking",this.emit("status-change",t,"thinking"),o.lastSubmittedPrompt=e,this.startSubmitTimeoutDetection(t,o,"sendMessage");try{l("[ACode] \u25B6\uFE0F Calling runtime.submit()...");const r=Date.now();await o.runtime.submit(e);const n=Date.now()-r;this.cancelSubmitTimeoutDetection(o),l(`[ACode] \u2705 runtime.submit() completed in ${n}ms`),l(`[ACode] Current status after submit: ${o.adapterSession.status}`),l("[ACode] \u25B6\uFE0F Calling recoverResolvedTurnWithoutTerminalEvent()..."),this.recoverResolvedTurnWithoutTerminalEvent(t),l(`[ACode] Status after recovery: ${o.adapterSession.status}`);const d=Date.now()-s;l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l(`[ACode] \u2705 sendMessage COMPLETED in ${d}ms`),l("[ACode] \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\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}catch(r){const n=Date.now()-s;console.error(`[ACode] \u274C sendMessage FAILED after ${n}ms:`,r instanceof Error?r.message:String(r)),this.cancelSubmitTimeoutDetection(o),l("[ACode] \u25B6\uFE0F sendMessage failed, scheduling request retry..."),this.emitRuntimeErrorEvent(t,r),this.scheduleRequestRetry(t,"sendMessage"),l(`[ACode] Status after failure: ${o.adapterSession.status}`)}}async sendConfirmation(t,e){}submitToolPermissionDecision(t,e,s){const o=this.sessions.get(t);if(!o)return!1;o.pendingToolAuthRequests?.delete(e);const a=o.runtime.submitToolPermissionDecision?.(e,s)??!1;return a&&this.startSubmitTimeoutDetection(t,o,"submitToolPermissionDecision"),a}getPendingToolAuthRequests(t){const e=this.sessions.get(t);return e?.pendingToolAuthRequests?Array.from(e.pendingToolAuthRequests.entries()).map(([s,o])=>({requestId:s,capability:o.capability,detail:o.detail,requestedAt:o.requestedAt})):[]}async sendQuestionAnswer(t,e){const s=this.sessions.get(t);if(!s)return;const o=s.pendingQuestionToolCallId;if(!o){l(`[AcodeSdkAdapter] sendQuestionAnswer: no pending question for session ${t}`);return}s.pendingQuestionToolCallId=void 0,s.pendingQuestions=void 0,s.runtime.submitQuestionAnswer&&s.runtime.submitQuestionAnswer(o,e),this.startSubmitTimeoutDetection(t,s,"sendQuestionAnswer")}async abortCurrentTurn(t){await this.requireSession(t).runtime.abort()}async resetContext(t){const e=this.requireSession(t);if(this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.autoPollActive=!1,e.autoPollObservedPollingTool=!1,e.autoPollObservedAssistantText=!1,e.autoPollObservedAnyTool=!1,e.stopAutoPollAfterCurrentTurn=!1,e.idlePollLoop?.stop(),!e.runtime.resetContext)throw new Error("ACode runtime does not support context reset");await e.runtime.resetContext(),e.adapterSession.messages=[];const s=e.adapterSession.totalUsage;if(e.adapterSession.totalUsage={inputTokens:0,outputTokens:0,contextLimit:s.contextLimit,maxOutput:s.maxOutput,inputLimit:s.inputLimit,compactionThreshold:s.compactionThreshold,cacheHitTokens:s.cacheHitTokens,cacheMissTokens:s.cacheMissTokens},e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage}),e.config.initialPrompt?.trim()){this.startInitialPromptTurn(t,e.config.initialPrompt);return}this.armIdleLoopOrAutoPoll(t)}async terminateSession(t){const e=this.sessions.get(t);if(!e)return;this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.idlePollLoop?.stop(),this.cancelRequestRetry(e),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),await e.runtime.stop();const s=e.config.agentId;s&&(q(s,this.listenerWakeHooks.get(t)),nt(s)),this.listenerWakeHooks.delete(t),e.adapterSession.status="terminated",this.sessions.delete(t),this.emit("status-change",t,"terminated")}getConversation(t){return this.sessions.get(t)?.adapterSession.messages??[]}hasSession(t){return this.sessions.has(t)}getProviderSessionId(t){return this.sessions.has(t)?t:void 0}getSessionStatus(t){return this.sessions.get(t)?.adapterSession.status}notifyForceMessage(t){const e=this.sessions.get(t);return e?.runtime?.notifyForceMessage?(e.runtime.notifyForceMessage(),!0):!1}getCapabilities(t){return this.sessions.get(t)?.lastCombinedCapabilities}async resolveAgentAiInfo(t){try{const e=[...this.sessions.values()].find(i=>i.config.agentId===t);if(!e)return;const s=e.config.mcpConfigPath||L(),{resolved:o,supportsImage:a}=await this.resolveAcodeProvider(e.config,s);return o?.model?{model:o.model,supportsImage:a===!0,...o.baseURL?{baseURL:o.baseURL}:{},...o.apiKey?{apiKey:o.apiKey}:{}}:void 0}catch{return}}resolveAgentSkillsDir(t){if(t){try{const e=F(t);if(e)return e}catch(e){l(`[acode-adapter] skill farm sync failed for agent ${t}:`,e.message)}return st(t)}return ot()}async applyAgentFeatures(t){if(!t)return;const e=P(t,x),s=P(t,W);for(const[a,i]of this.sessions){if(i.config.agentId!==t)continue;const r=i.runtime;try{if(e){const n=await D(t,await this.resolveAgentAiInfo(t)),d=r.addBuiltInTools?.(n)??0;d>0&&l(`[acode-adapter] session ${a}: ${d} desktop tools hot-added for agent ${t}`)}else{const n=r.removeBuiltInToolsByPrefix?.("desktop_")??0;n>0&&l(`[acode-adapter] session ${a}: ${n} desktop tools hot-removed for agent ${t}`)}}catch(n){l(`[acode-adapter] session ${a}: desktop feature hot-sync failed for agent ${t}:`,n.message)}if(s)try{const n=await v(t,i.config.scopeKey,typeof i.config.browserAllowPrivate=="boolean"?{allowPrivate:i.config.browserAllowPrivate}:void 0),d=r.addBuiltInTools?.(n)??0;d>0&&l(`[acode-adapter] session ${a}: ${d} browser tools hot-added for agent ${t}`)}catch(n){l(`[acode-adapter] session ${a}: browser feature hot-sync failed for agent ${t}:`,n.message)}}this.syncMcpToolsForAgent(t);try{F(t)}catch(a){l(`[acode-adapter] skill farm hot-sync failed for agent ${t}:`,a.message)}try{await $.applyCapabilities(t)}catch(a){l(`[acode-adapter] plugin capability hot-sync failed for agent ${t}:`,a.message)}const o=dt(t);for(const[a,i]of this.sessions)if(i.config.agentId===t)try{i.runtime.setToolPermissionPolicy?.(o)}catch(r){l(`[acode-adapter] session ${a}: tool permission policy hot-sync failed for agent ${t}:`,r.message)}}syncMcpToolsForAgent(t){if(t){for(const[e,s]of this.sessions)if(s.config.agentId===t)try{s.runtime.requestMcpToolsRefresh?.()}catch(o){l(`[acode-adapter] session ${e}: mcp tools refresh failed for agent ${t}:`,o.message)}}}getPendingQuestion(t){const e=this.sessions.get(t);if(e?.pendingQuestionToolCallId)return{toolCallId:e.pendingQuestionToolCallId,questions:e.pendingQuestions??[]}}cleanup(){for(const t of this.sessions.keys())this.terminateSession(t)}setIdleCommands(t,e){const s=this.sessions.get(t);if(s){if(s.idleCommands=e,this.cancelScheduledAutoPoll(s),e.idleInputCommand&&E(e.idleInputCommand)){this.getIdlePollLoop(s).start();return}s.adapterSession.status==="waiting_input"&&(e.idleInputCommand||e.nonInputCommand)&&this.getIdlePollLoop(s).start()}}setRuntimeConstructorForTest(t){this.runtimeConstructor=t}async loadAcodeProviderSection(t){try{const e=await V.readFile(t,"utf-8"),s=JSON.parse(e),a=s.provider?.default,i=a?.options,r=h(s.contextLimit),n={},d={},p=a?.models;if(p&&typeof p=="object"){for(const[m,c]of Object.entries(p))if(c&&typeof c=="object"){const g=h(c.contextLimit);g>0&&(n[m.trim()]=g);const f=c.modalities?.input,y=Array.isArray(f)&&f.includes("image")||c.supportsImageInput===!0;d[m.trim()]=y===!0}}return{...a?.model?{model:a.model}:{},...i?.baseURL||i?.baseUrl?{baseURL:i?.baseURL||i?.baseUrl}:{},...i?.apiKey?{apiKey:i.apiKey}:{},...r>0?{contextLimit:r}:{},...Object.keys(n).length>0?{modelContextLimits:n}:{},...Object.keys(d).length>0?{modelImageSupport:d}:{}}}catch(e){const s=e instanceof Error?e.message:String(e);return O().log({level:"warn",category:"load",message:`loadAcodeProviderSection failed: ${t}: ${s}`,source:"bridge"}),{}}}async resolveAcodeProvider(t,e){const s=typeof t.model=="string"&&t.model.trim()?t.model.trim():void 0,o=O();o.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u8F93\u5165",source:"bridge",details:{configModel:t.model??null,configInstanceAiConfig:t.instanceAiConfig?B(t.instanceAiConfig):null,configPath:e}});const a={...t.instanceAiConfig?.model?{model:t.instanceAiConfig.model}:{},...t.instanceAiConfig?.baseURL?{baseURL:t.instanceAiConfig.baseURL}:{},...t.instanceAiConfig?.apiKey?{apiKey:t.instanceAiConfig.apiKey}:{},...t.instanceAiConfig?.effort?{effort:t.instanceAiConfig.effort}:{},...s?{model:s}:{}},i=await this.loadAcodeProviderSection(e);let r=i.contextLimit;if(!r&&i.modelContextLimits){const d=s||t.instanceAiConfig?.model||i.model;d&&i.modelContextLimits[d]&&(r=i.modelContextLimits[d])}const n=ht(a,i,"acode-default");return o.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u89E3\u6790\u7ED3\u679C",source:"bridge",details:{instanceConfig:B(a),globalConfigModel:i?.model??null,resolvedModel:n.model,resolvedBaseURL:n.baseURL??null,resolvedApiKey:n.apiKey?"[set]":null,configContextLimit:r,resolvedSource:n.source}}),{resolved:n,configPath:e,perSessionModel:s,configContextLimit:r,supportsImage:i.modelImageSupport?.[n.model]===!0}}async createRuntime(t,e,s,o,a){const i=await this.loadRuntimeConstructor(),r=s.mcpConfigPath||L();let n=[];try{n=await v(s.agentId,s.scopeKey,typeof s.browserAllowPrivate=="boolean"?{allowPrivate:s.browserAllowPrivate}:void 0)}catch(c){l(`[acode-adapter] browser tools injection skipped for agent ${s.agentId}:`,c.message)}let d=[];if(P(s.agentId??"",W)&&(d=n),s.agentId&&P(s.agentId,x))try{const c=o?.model?{model:o.model,supportsImage:a===!0,...o.baseURL?{baseURL:o.baseURL}:{},...o.apiKey?{apiKey:o.apiKey}:{}}:void 0,g=await D(s.agentId,c);g.length>0&&(d=[...d,...g])}catch(c){l(`[acode-adapter] desktop tools injection skipped for agent ${s.agentId}:`,c.message)}const p=new i({sessionId:t,agentId:s.agentId,workingDirectory:s.workingDirectory,configPath:r,skillsDir:this.resolveAgentSkillsDir(s.agentId),pluginsDir:s.agentId?et(s.agentId):void 0,model:e,systemPrompt:typeof s.systemPrompt=="string"?s.systemPrompt:void 0,globalPrompt:typeof s.globalPrompt=="string"?s.globalPrompt:void 0,rolePrompt:typeof s.rolePrompt=="string"?s.rolePrompt:void 0,memory:typeof s.memory=="string"?s.memory:void 0,env:s.envOverrides,subAgentManager:ct,terminalManager:at,pluginBridge:s.agentId?$.forAgent(s.agentId):void 0,commandDirs:[Z(new URL("../services/builtin-commands/",import.meta.url))],...s.subAgentModel?{subAgentModel:s.subAgentModel}:{},...s.subAgentEffort?{subAgentEffort:s.subAgentEffort}:{},...s.instanceAiConfig?.effort?{effort:s.instanceAiConfig.effort}:{},onFileChange:c=>mt({...c,agentId:s.agentId,workspacePath:s.workingDirectory}),extraBuiltInTools:d,...s.agentId?{toolPermissionPolicy:lt(s.agentId)??void 0}:{},subAgentSuiteTools:n,subAgentSuiteToolProvider:this.buildSubAgentSuiteToolProvider(s.agentId)});return O().log({level:"info",category:"resolve",message:"ACode provider resolved",source:"bridge",sessionId:t,details:{resolvedModel:e,perInstanceModel:s.instanceAiConfig?.model||null,perInstanceBaseURL:s.instanceAiConfig?.baseURL?"[set]":null,perInstanceApiKey:s.instanceAiConfig?.apiKey?"[set]":null,configPath:r}}),p.on("event",c=>this.forwardAcodeEvent(c)),p}buildSubAgentSuiteToolProvider(t){return async e=>{if(!t)return[];if(!P(t,x))return[];if(e.length===0)return ut();if(!e.some(a=>{const i=a.trim().replace(/[.*]+$/,"");return i==="desktop"||i.startsWith("desktop.")}))return[];const o=await this.resolveAgentAiInfo(t);return D(t,o)}}async loadRuntimeConstructor(){if(this.runtimeConstructor)return this.runtimeConstructor;const t=Pt(),e=await import($t(t));if(!e.ACodeRuntime)throw new Error(`ACode runtime export not found: ${t}`);return this.runtimeConstructor=e.ACodeRuntime,this.runtimeConstructor}requireSession(t){const e=this.sessions.get(t);if(!e)throw new Error("ACode session not found");return e}startInitialPromptTurn(t,e){const s=this.sessions.get(t);s&&(s.adapterSession.status="thinking",this.emit("status-change",t,"thinking"),s.lastSubmittedPrompt=e,this.startSubmitTimeoutDetection(t,s,"startInitialPromptTurn"),s.runtime.submit(e).then(()=>{this.cancelSubmitTimeoutDetection(s),this.recoverResolvedTurnWithoutTerminalEvent(t)}).catch(o=>{const a=this.sessions.get(t);a&&(this.cancelSubmitTimeoutDetection(a),this.emitRuntimeErrorEvent(t,o),this.scheduleRequestRetry(t,"startInitialPromptTurn"))}))}forwardAcodeEvent(t){const e=this.sessions.get(t.sessionId);if(e){if(e.autoPollActive&&(e.autoPollLastActivityAt=Date.now()),t.type==="assistant_delta"){const s=typeof t.data.text=="string"?t.data.text:"";e.autoPollActive&&(e.autoPollObservedAssistantText=!0),e.adapterSession.messages.push({id:I(),sessionId:t.sessionId,role:"assistant",content:s,timestamp:t.timestamp}),this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"text_delta",{text:s});return}if(t.type==="thinking_delta"){const s=typeof t.data.text=="string"?t.data.text:"";this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"thinking",{text:s});return}if(t.type==="command_output"){this.emitProviderEvent(t,"command_output",{text:typeof t.data.text=="string"?t.data.text:"",stream:Ot(t.data.stream),sequence:typeof t.data.sequence=="number"?t.data.sequence:void 0,cwd:typeof t.data.cwd=="string"?t.data.cwd:void 0,commandLineCount:typeof t.data.commandLineCount=="number"?t.data.commandLineCount:void 0,commandFilePath:typeof t.data.commandFilePath=="string"?t.data.commandFilePath:void 0});return}if(t.type==="background_command_completed"){this.emitProviderEvent(t,"background_command_completed",t.data);return}if(t.type==="ai_request"){const s=z(t.data),o=t.data.lastUsage,a=h(o?.inputTokens),i=h(o?.outputTokens),r=h(o?.cacheHitTokens),n=h(o?.cacheMissTokens),d=!!(o&&(a>0||i>0));let p;if(d&&(a>0&&(e.adapterSession.totalUsage.inputTokens=a),i>0&&(e.adapterSession.totalUsage.outputTokens=i),(o?.cacheHitTokens!==void 0||o?.cacheMissTokens!==void 0)&&(e.adapterSession.totalUsage.cacheHitTokens=r,e.adapterSession.totalUsage.cacheMissTokens=n),p={...e.adapterSession.totalUsage},C(`ai_request lastUsage applied: ${JSON.stringify(o)} -> totalUsage=${JSON.stringify(e.adapterSession.totalUsage)}`)),!d&&s>0){const g=e.adapterSession.totalUsage.inputTokens;g>0?e.adapterSession.totalUsage.inputTokens=Math.min(s,Math.floor(g*1.5)):e.adapterSession.totalUsage.inputTokens=s,p={...e.adapterSession.totalUsage}}e.adapterSession.status==="compacting"&&(e.adapterSession.status="thinking"),e.adapterSession.status==="waiting_input"&&(e.adapterSession.status="thinking");const c={actionType:"ai_request",actionLabel:"AI\u8BF7\u6C42",actionDetail:this.formatAiRequestActionDetail(t.data)};p&&(c.usage=p),C(`ai_request handled: estimatedContextTokens=${t.data.estimatedContextTokens??"n/a"} totalUsage=${JSON.stringify(e.adapterSession.totalUsage)} displayUsage=${JSON.stringify(p??null)}`),this.emit("status-change",t.sessionId,e.adapterSession.status,c),this.startDataFlowWatchdog(t.sessionId,e),e.adapterSession.status==="thinking"&&e.watchdogSubmitStartTime&&(e.watchdogSubmitStartTime=Date.now()),this.emitProviderEvent(t,"ai_request",{model:typeof t.data.model=="string"?t.data.model:void 0,endpoint:typeof t.data.endpoint=="string"?t.data.endpoint:void 0,requestMethod:typeof t.data.requestMethod=="string"?t.data.requestMethod:void 0,requestUrl:typeof t.data.requestUrl=="string"?t.data.requestUrl:void 0,requestHeaders:Et(t.data.requestHeaders),stream:typeof t.data.stream=="boolean"?t.data.stream:void 0,messageCount:typeof t.data.messageCount=="number"?t.data.messageCount:void 0,toolCount:typeof t.data.toolCount=="number"?t.data.toolCount:void 0,lastUserMessagePreview:typeof t.data.lastUserMessagePreview=="string"?t.data.lastUserMessagePreview:void 0,requestBody:typeof t.data.requestBody=="string"?t.data.requestBody:void 0});return}if(t.type==="turn_complete"){const s=xt(t.data.usage);if(s){const a=e.adapterSession.totalUsage.inputTokens,i=a>0&&s.inputTokens<Math.floor(a*.2)&&!s.hasCache;C(`turn_complete usage: raw=${JSON.stringify(t.data.usage)} parsed=${JSON.stringify(s)} currentInput=${a} threshold20pct=${Math.floor(a*.2)} isFallbackUsage=${i}`),i?s.outputTokens>0&&(e.adapterSession.totalUsage.outputTokens=s.outputTokens):(e.adapterSession.totalUsage.inputTokens=s.inputTokens,e.adapterSession.totalUsage.outputTokens=s.outputTokens),(s.cacheHitTokens||s.cacheMissTokens)&&(e.adapterSession.totalUsage.cacheHitTokens=s.cacheHitTokens,e.adapterSession.totalUsage.cacheMissTokens=s.cacheMissTokens,l(`[ACode] \u{1F4B0} Cache usage (overwrite from runtime cumulative): hit=${e.adapterSession.totalUsage.cacheHitTokens}, miss=${e.adapterSession.totalUsage.cacheMissTokens} (turn delta: hit=${s.cacheHitTokens||0}, miss=${s.cacheMissTokens||0})`)),C(`totalUsage after turn_complete: ${JSON.stringify(e.adapterSession.totalUsage)}`)}else C(`turn_complete has NO parseable usage: raw=${JSON.stringify(t.data.usage)} failed=${t.data.failed===!0} aborted=${t.data.aborted===!0}`);const o=e.adapterSession.status;if(l(`[ACode] turn_complete event received (session: ${t.sessionId}), previous status: ${o}, failed: ${t.data.failed===!0}, aborted: ${t.data.aborted===!0}`),Mt(t.data)){l(`[ACode] Scheduling retry from failed turn_complete (session: ${t.sessionId})`),this.cancelDataFlowWatchdog(e),this.recoverTurnFailure(t.sessionId,e);return}if(t.data.aborted===!0&&(e.autoPollActive||e.retryActive||e.retryTimer)){l(`[ACode] Scheduling retry from aborted turn_complete (session: ${t.sessionId})`),this.cancelDataFlowWatchdog(e),this.recoverTurnFailure(t.sessionId,e);return}if(this.cancelDataFlowWatchdog(e),this.emitProviderEvent(t,"turn_complete",{usage:e.adapterSession.totalUsage,responseBody:typeof t.data.responseBody=="string"?t.data.responseBody:void 0,finishReason:typeof t.data.finishReason=="string"?t.data.finishReason:void 0}),e.adapterSession.status="waiting_input",this.emit("status-change",t.sessionId,"waiting_input",{usage:e.adapterSession.totalUsage}),C(`status-change emitted (waiting_input after turn_complete): usage=${JSON.stringify(e.adapterSession.totalUsage)}`),l(`[ACode] Status changed to waiting_input after turn_complete (session: ${t.sessionId})`),e.autoPollActive&&e.autoPollObservedAssistantText&&!e.autoPollObservedPollingTool&&!e.autoPollObservedAnyTool){e.stopAutoPollAfterCurrentTurn=!0,console.warn(`[${this.displayName}] Auto-poll turn produced assistant text without calling any tool; stopping auto-poll rearm for session ${t.sessionId}`);return}this.armIdleLoopOrAutoPoll(t.sessionId);return}if(t.type==="session_complete"){this.cancelDataFlowWatchdog(e),this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.idlePollLoop?.stop(),e.adapterSession.status="completed",this.emitProviderEvent(t,"session_complete",{exitCode:Number(t.data.exitCode)||0}),this.emit("status-change",t.sessionId,"completed");return}if(t.type==="question_request"){this.cancelDataFlowWatchdog(e),this.cancelSubmitTimeoutDetection(e);const s=typeof t.data.toolCallId=="string"?t.data.toolCallId:"",o=Array.isArray(t.data.questions)?t.data.questions:[];s&&(e.pendingQuestionToolCallId=s,e.pendingQuestions=o),this.emit("ask-user-question",{sessionId:t.sessionId,toolCallId:s||void 0,questions:o});return}if(t.type==="tool_permission_request"){this.cancelDataFlowWatchdog(e),this.cancelSubmitTimeoutDetection(e);const s=typeof t.data.requestId=="string"?t.data.requestId:"";s&&(e.pendingToolAuthRequests||(e.pendingToolAuthRequests=new Map),e.pendingToolAuthRequests.set(s,{capability:String(t.data.capability||""),detail:{path:t.data.path,command:t.data.command,cwd:t.data.cwd,operation:t.data.operation},requestedAt:Date.now()})),this.emit("tool-permission-request",{sessionId:t.sessionId,requestId:s,capability:String(t.data.capability||""),agentId:e.config.agentId,detail:{path:t.data.path,command:t.data.command,cwd:t.data.cwd,operation:t.data.operation}});return}if(t.type==="error"){if(t.data.nonFatal){this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")});return}this.cancelDataFlowWatchdog(e),e.adapterSession.status="error",this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")}),this.emit("status-change",t.sessionId,"error"),e.autoPollActive||this.recoverTurnFailure(t.sessionId,e);return}if(t.type==="mcp_status"){if(!t.data.action&&Array.isArray(t.data.tools)){const f=t.data.tools;f.some(T=>pt(T))||console.warn(`[${this.displayName}] ACode runtime is not exposing a poll_message tool. The LLM cannot call poll_message. Please configure an MCP server that provides poll_message (e.g. aws-mcp) in the dashboard panel for this runtime.`);const A=Array.isArray(t.data.servers)?t.data.servers:[],_=f.map(T=>({name:String(T.name??""),source:String(T.source??""),description:String(T.description??"")})).filter(T=>T.name),b={kind:"mcp",mcpServers:A,mcpTools:_};e.cachedMcpCapabilities=b,e.lastMcpCapabilities=b,e.lastCombinedCapabilities={kind:"combined",mcpServers:A,mcpTools:_,skills:e.lastCombinedCapabilities?.skills??[],skillsDir:e.lastCombinedCapabilities?.skillsDir,skillGroups:e.lastCombinedCapabilities?.skillGroups??[]},l(`[cap-diag] MCP capabilities cached: ${A.length} servers, ${_.length} tools`,{sessionId:t.sessionId,servers:A,toolNames:_.map(T=>T.name)}),e.capabilitiesFallbackTimer&&clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=setTimeout(()=>{const T=e.cachedMcpCapabilities;if(!T)return;const k={...T,skills:[],skillGroups:[]};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:k,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(k)}),l(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${Q}ms)`,{sessionId:t.sessionId,mcpServers:T.mcpServers,mcpToolsCount:T.mcpTools?.length??0}),e.cachedMcpCapabilities=void 0,e.capabilitiesFallbackTimer=void 0},Q);return}const s=String(t.data.action??"");l(`[ACode] \u{1F4E1} mcp_status event: action=${s}, server=${typeof t.data.server=="string"?t.data.server:"?"}, tool=${typeof t.data.tool=="string"?t.data.tool:"?"}, toolUseId=${typeof t.data.toolUseId=="string"?t.data.toolUseId.slice(0,16)+"\u2026":"none"}, failed=${t.data.failed===!0}, session=${t.sessionId}, status=${e.adapterSession.status}`);const o=typeof t.data.server=="string"?t.data.server:"mcp",a=typeof t.data.tool=="string"?t.data.tool:"",i=o&&a?`${o}__${a}`:a||o,r=t.data.arguments&&typeof t.data.arguments=="object"&&!Array.isArray(t.data.arguments)?t.data.arguments:void 0,n=Tt(i,r);e.autoPollActive&&(e.autoPollObservedAnyTool=!0,n.actionType==="idle"&&(e.autoPollObservedPollingTool=!0));const d=Rt(n.actionType)?n.actionDetail||Y(void 0,o,a):Y(n.actionDetail,o,a),p=typeof t.data.toolUseId=="string"?t.data.toolUseId:void 0,m=r?JSON.stringify(r,null,2):void 0,c={toolName:d,rawToolName:i,actionType:n.actionType,actionLabel:n.actionLabel,actionDetail:d,...m?{actionInput:m}:{},...r?{toolInput:r}:{},...p?{toolUseId:p,actionId:p}:{},metadata:{mcpServer:o}};if(t.data.action==="tool_result"){this.emitProviderEvent(t,"tool_use_end",{...c,toolResult:typeof t.data.result=="string"?t.data.result:JSON.stringify(t.data.result??"")});const f=n.actionType==="idle"&&!Ut(t.data.result)?"waiting_input":"thinking";e.adapterSession.status=f,n.actionType==="idle"&&(e.idleToolActiveAt=void 0),this.cancelDataFlowWatchdog(e),e.pendingToolEventData=void 0;const y=n.actionType==="idle";this.emit("status-change",t.sessionId,f,y||f==="thinking"?void 0:c);return}const g=n.actionType==="idle"?"waiting_input":"tool_using";e.adapterSession.status=g,n.actionType==="idle"&&(e.idleToolActiveAt=Date.now()),g==="tool_using"&&(this.resetDataFlowWatchdog(t.sessionId,e),e.pendingToolEventData={...c}),this.emitProviderEvent(t,"tool_use_start",c),this.emit("status-change",t.sessionId,g,c);return}if(t.type==="skill_status"){const o=(Array.isArray(t.data.skills)?t.data.skills:[]).map(m=>({name:String(m.name??""),description:typeof m.description=="string"?m.description:void 0,directory:typeof m.directory=="string"?m.directory:void 0})).filter(m=>m.name),a=typeof t.data.skillsDir=="string"?t.data.skillsDir:void 0,r=(Array.isArray(t.data.skillGroups)?t.data.skillGroups:[]).map(m=>{const c=Array.isArray(m.skills)?m.skills:[];return{dir:typeof m.dir=="string"?m.dir:"",exists:m.exists===!0,skills:c.map(g=>({name:String(g.name??""),description:typeof g.description=="string"?g.description:void 0,directory:typeof g.directory=="string"?g.directory:void 0})).filter(g=>g.name)}}).filter(m=>m.dir),n=e.cachedMcpCapabilities??e.lastMcpCapabilities,d=e.cachedMcpCapabilities!==void 0,p={kind:"combined",mcpServers:n?.mcpServers??[],mcpTools:n?.mcpTools??[],skills:o,skillsDir:a,skillGroups:r};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:p,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(p)}),e.lastCombinedCapabilities=p,l(`[cap-diag] Combined capabilities emitted ${d?"(startup merge)":"(runtime refresh)"}: ${(n?.mcpTools??[]).length} MCP tools, ${o.length} skills`,{sessionId:t.sessionId,mcpServers:n?.mcpServers??[],skillsDir:a}),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),e.cachedMcpCapabilities=void 0;return}if(t.type==="startup_phase"){l(`[ACode] \u{1F680} startup_phase swallowed: ${String(t.data.phase??"unknown")}`,{sessionId:t.sessionId});return}if(t.type==="command_status"){if(t.data.action==="commands_loaded"){l(`[ACode] \u{1F4CB} command_status commands_loaded swallowed: ${typeof t.data.installedCommandCount=="number"?t.data.installedCommandCount:"?"} commands`,{sessionId:t.sessionId});return}this.emitProviderEvent(t,"thinking",{text:JSON.stringify(t.data)});return}if(t.type==="session_started"){const s=z(t.data),o=h(t.data.contextLimit)||void 0,a=h(t.data.maxOutput)||void 0,i=h(t.data.inputLimit)||void 0,r=h(t.data.compactionThreshold)||void 0;let n=!1;s>0&&(e.adapterSession.totalUsage.inputTokens=s,e.adapterSession.totalUsage.outputTokens=0,n=!0),o!==void 0&&(e.adapterSession.totalUsage.contextLimit=o,n=!0),a!==void 0&&(e.adapterSession.totalUsage.maxOutput=a,n=!0),i!==void 0&&(e.adapterSession.totalUsage.inputLimit=i,n=!0),r!==void 0&&(e.adapterSession.totalUsage.compactionThreshold=r,n=!0),n&&this.emit("status-change",t.sessionId,e.adapterSession.status,{usage:e.adapterSession.totalUsage});return}if(t.type==="exit_plan_mode"){e.adapterSession.status="waiting_input";const s=typeof t.data.plan=="string"?t.data.plan:void 0;this.emitProviderEvent(t,"exit_plan_mode",{toolInput:t.data.toolInput&&typeof t.data.toolInput=="object"?t.data.toolInput:void 0,text:s||"\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"}),this.emit("status-change",t.sessionId,"waiting_input",{actionLabel:"\u5BA1\u6279\u8BA1\u5212",actionDetail:s||"\u7B49\u5F85\u7528\u6237\u5BA1\u6279\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"});return}if(t.type==="context_compaction"){const s=t.data,o=s.action,a=o==="start",i=o==="end",r=o==="phase",n=s.parentId,d=s.compactionId;let p,m;if(a){const g=String(s.sizeKB??s.sizeBeforeKB??"?"),f=String(s.messageCount??s.messageCountBefore??"?"),y=s.estimatedTokens,A=s.actualInputTokens?`input=${s.actualInputTokens}`:"",_=s.actualOutputTokens?`output=${s.actualOutputTokens}`:"",b=s.trigger?`, \u89E6\u53D1: ${s.trigger}`:"";p="\u4E0A\u4E0B\u6587\u538B\u7F29\u5F00\u59CB",y!==void 0?m=`\u538B\u7F29\u524D: ~${y} tokens, \u6D88\u606F: ${f}\u6761`+(A?`, \u5B9E\u9645 tokens: ${A} ${_}`:"")+b:m=`\u538B\u7F29\u524D: ${g}KB, \u6D88\u606F: ${f}\u6761`+(A?`, \u5B9E\u9645 tokens: ${A} ${_}`:"")+b}else if(r)p=String(s.phaseLabel??"\u538B\u7F29\u9636\u6BB5"),m=String(s.phaseDetail??"");else{const g=s.tokensBefore,f=s.tokensAfter,y=s.freedTokens,A=String(s.sizeBeforeKB??"?"),_=String(s.sizeAfterKB??"?"),b=String(s.freedKB??"?"),T=String(s.strategy??"?"),k=Number(s.prunedToolMessages??0),S=Number(s.summarizedMessages??0);p="\u538B\u7F29\u5B8C\u6210",g!==void 0&&f!==void 0?m=`\u91CA\u653E: ${y??g-f} tokens, ${g} tokens \u2192 ${f} tokens, \u7B56\u7565: ${T}`+(k?`, \u88C1\u526A\u5DE5\u5177: ${k}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:""):m=`\u91CA\u653E: ${b}KB, ${A}KB \u2192 ${_}KB, \u7B56\u7565: ${T}`+(k?`, \u88C1\u526A\u5DE5\u5177: ${k}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:"")}a&&(e.adapterSession.status="compacting");const c={actionType:"context_compaction",actionLabel:p,actionDetail:m};if(a&&d)c.actionId=d;else if((r||i)&&d)if(c.parentId=d,r){const g=String(s.phase??"unknown");c.actionId=`${d}-phase-${g}`}else c.actionId=`${d}-end`;r&&n&&!c.parentId&&(c.parentId=n),i&&(e.adapterSession.totalUsage.inputTokens=h(s.tokensAfter),e.adapterSession.totalUsage.outputTokens=0,e.config.agentId&&$.markAgentCompacted(e.config.agentId),c.usage={...e.adapterSession.totalUsage}),this.emitProviderEvent(t,"context_compaction",c),this.emit("status-change",t.sessionId,"compacting",c);return}this.emitProviderEvent(t,"thinking",{text:JSON.stringify(t.data)})}}emitProviderEvent(t,e,s){this.emit("event",{type:e,sessionId:t.sessionId,timestamp:t.timestamp,data:s})}armIdleLoopOrAutoPoll(t){const e=this.sessions.get(t);if(!(!e||this.isTerminalStatus(e.adapterSession.status))){if(this.cancelRequestRetry(e),e.retryAttempt=0,e.idleCommands?.idleInputCommand){this.getIdlePollLoop(e).start();return}this.scheduleAutoPoll(t)}}getIdlePollLoop(t){if(t.idlePollLoop)return t.idlePollLoop;const e=t.adapterSession.sessionId;return t.idlePollLoop=new gt({getStatus:()=>this.sessions.get(e)?.adapterSession.status,getCommand:()=>this.sessions.get(e)?.idleCommands?.idleInputCommand,isPollingCommand:s=>E(s),preDispatchHook:()=>null,dispatchPollingPrompt:(s,o)=>this.submitAutoPollPrompt(e,o?s:wt,{keepWaitingStatus:!0}),dispatchRegularIdleCommand:s=>this.submitAutoPollPrompt(e,s)}),t.idlePollLoop}submitAutoPollPrompt(t,e,s){const o=this.sessions.get(t);if(!o||this.isTerminalStatus(o.adapterSession.status))return;const a=(o.autoPollToken??0)+1;o.autoPollToken=a,o.autoPollActive=!0,o.autoPollObservedPollingTool=!1,o.autoPollObservedAssistantText=!1,o.autoPollObservedAnyTool=!1,o.stopAutoPollAfterCurrentTurn=!1,o.autoPollQueuedAt=Date.now(),o.autoPollLastActivityAt=Date.now(),o.idleToolActiveAt=void 0,o.lastSubmittedPrompt=e,s?.keepWaitingStatus||(o.adapterSession.status="thinking",this.emit("status-change",t,"thinking")),this.startSubmitTimeoutDetection(t,o,"submitAutoPollPrompt"),o.runtime.submit(e).catch(i=>{this.emitRuntimeErrorEvent(t,i),this.scheduleRequestRetry(t,"submitAutoPollPrompt")}).finally(()=>{const i=this.sessions.get(t);if(i){if(i.autoPollToken!==a){l(`[ACode] submitAutoPollPrompt.finally: token mismatch (my=${a}, current=${i.autoPollToken}), skipping cleanup \u2014 newer turn has taken over`);return}if(this.cancelSubmitTimeoutDetection(i),i.autoPollActive=!1,i.failedTurnRecoveryScheduled||i.autoPollTimer){i.failedTurnRecoveryScheduled=!1;return}if(i.stopAutoPollAfterCurrentTurn){i.stopAutoPollAfterCurrentTurn=!1,i.autoPollObservedPollingTool=!1,i.autoPollObservedAssistantText=!1,i.autoPollObservedAnyTool=!1;return}if(i.adapterSession.status==="error"){this.recoverTurnFailure(t,i);return}if(i.adapterSession.status==="thinking"||i.adapterSession.status==="tool_using"){this.recoverResolvedTurnWithoutTerminalEvent(t);return}i.adapterSession.status==="waiting_input"&&this.armIdleLoopOrAutoPoll(t)}})}scheduleAutoPoll(t,e=Ct){const s=this.sessions.get(t);!s||s.autoPollActive||s.autoPollTimer||this.isTerminalStatus(s.adapterSession.status)||(s.autoPollTimer=setTimeout(()=>{const o=this.sessions.get(t);!o||o.autoPollActive||this.isTerminalStatus(o.adapterSession.status)||(o.autoPollTimer=void 0,this.submitAutoPollPrompt(t,St,{keepWaitingStatus:!0}))},e))}cancelScheduledAutoPoll(t){t.autoPollTimer&&(clearTimeout(t.autoPollTimer),t.autoPollTimer=void 0)}scheduleRequestRetry(t,e){const s=this.sessions.get(t);if(!s||this.isTerminalStatus(s.adapterSession.status)||s.retryActive||s.retryTimer)return;if(!s.lastSubmittedPrompt){s.adapterSession.status="error",this.emit("status-change",t,"error");return}s.retryAttempt+=1,s.retryActive=!0,this.cancelScheduledAutoPoll(s),s.idlePollLoop?.stop();const o=K[Math.min(s.retryAttempt-1,K.length-1)];s.adapterSession.status="error",this.emit("status-change",t,"error",{actionType:"retry",actionLabel:"\u91CD\u8BD5\u4E2D",actionDetail:`\u7B2C${s.retryAttempt}\u6B21 \xB7 ${Math.round(o/1e3)}s\u540E`}),l(`[ACode] scheduleRequestRetry(${e}): attempt=${s.retryAttempt} delay=${o}ms`),s.retryTimer=setTimeout(()=>{s.retryTimer=void 0,this.retryFailedRequest(t,e)},o)}retryFailedRequest(t,e){const s=this.sessions.get(t);if(!s)return;if(s.retryTimer=void 0,this.isTerminalStatus(s.adapterSession.status)||!s.lastSubmittedPrompt){s.retryActive=!1;return}s.retryActive=!1;const o=s.lastSubmittedPrompt;l(`[ACode] retryFailedRequest(${e}): attempt=${s.retryAttempt} re-submitting...`),s.adapterSession.status="thinking",this.emit("status-change",t,"thinking",{usage:s.adapterSession.totalUsage}),this.startSubmitTimeoutDetection(t,s,"retryFailedRequest"),s.runtime.submit(o).then(()=>{const a=this.sessions.get(t);!a||a.retryTimer||(this.cancelSubmitTimeoutDetection(a),a.retryAttempt=0,a.retryActive=!1,this.recoverResolvedTurnWithoutTerminalEvent(t))}).catch(a=>{const i=this.sessions.get(t);i&&(this.cancelSubmitTimeoutDetection(i),this.emitRuntimeErrorEvent(t,a),this.scheduleRequestRetry(t,`retry#${i.retryAttempt+1}`))})}cancelRequestRetry(t){t.retryTimer&&(clearTimeout(t.retryTimer),t.retryTimer=void 0),t.retryActive=!1}isTerminalStatus(t){return t==="completed"||t==="terminated"}emitRuntimeErrorEvent(t,e){const s=new Date().toISOString();this.emit("event",{type:"error",sessionId:t,timestamp:s,data:{text:e instanceof Error?e.message:String(e)}})}estimateOutputTokensFromMessages(t){const e=t.filter(s=>s.role==="assistant").map(s=>s.content).join("");return Math.max(0,Math.round(e.length/4))}recoverTurnFailure(t,e){if(this.isTerminalStatus(e.adapterSession.status))return;if(e.idleToolActiveAt=void 0,e.pendingToolAuthRequests?.size&&e.pendingToolAuthRequests.clear(),e.adapterSession.status==="tool_using"&&e.pendingToolEventData){const o=e.pendingToolEventData;l(`[ACode] recoverTurnFailure: \u8865\u53D1 tool_use_end (tool: ${o.toolName??"unknown"}) \u5173\u95ED\u65F6\u95F4\u7EBF\u6761\u76EE\uFF0C\u907F\u514D\u6C38\u4E45\u5361\u5728\u5DE5\u5177\u6267\u884C\u72B6\u6001`),this.emit("event",{type:"tool_use_end",sessionId:t,timestamp:new Date().toISOString(),data:{...o,toolResult:"[aborted] \u5DE5\u5177\u6267\u884C\u88AB\u6570\u636E\u6D41\u770B\u95E8\u72D7\u4E2D\u65AD"}}),e.pendingToolEventData=void 0}const s=this.estimateOutputTokensFromMessages(e.adapterSession.messages);if(s>0){const o=e.adapterSession.totalUsage.outputTokens;e.adapterSession.totalUsage.outputTokens=s,l(`[ACode] recoverTurnFailure: \u4F30\u7B97\u8986\u76D6 output tokens ${o} -> ${s} (totalUsage now: input=${e.adapterSession.totalUsage.inputTokens}, output=${e.adapterSession.totalUsage.outputTokens})`)}this.cancelScheduledAutoPoll(e),this.scheduleRequestRetry(t,"recoverTurnFailure")}recoverResolvedTurnWithoutTerminalEvent(t){l(`[ACode] \u25B6\uFE0F recoverResolvedTurnWithoutTerminalEvent called for session ${t}`);const e=this.sessions.get(t);if(!e||this.isTerminalStatus(e.adapterSession.status)){l(`[ACode] Skipping recovery: entry=${!!e}, isTerminal=${e?this.isTerminalStatus(e.adapterSession.status):"N/A"}`);return}if(l(`[ACode] Current status: ${e.adapterSession.status}`),e.adapterSession.status!=="thinking"&&e.adapterSession.status!=="tool_using"){l(`[ACode] Skipping recovery: status is ${e.adapterSession.status} (not thinking/tool_using)`);return}console.warn(`[ACode] \u26A0\uFE0F Detected stuck state: ${e.adapterSession.status} after submit resolved! Recovering to waiting_input. If runtime.submit() is still pending (e.g., built-in tool execution not interruptible by abort()), the submitQueue is deadlocked and subsequent auto-poll/user submits will never execute.`),l("[ACode] Recovering to waiting_input..."),e.adapterSession.status="waiting_input";const s=e.idleCommands?.idleInputCommand,a=!!(s&&E(s))?{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:"aws-mcp/poll_message"}:void 0;this.emit("status-change",t,"waiting_input",a),l(`[ACode] \u2705 Recovery complete, status now: ${e.adapterSession.status}`),l("[ACode] \u25B6\uFE0F Arming idle loop or auto-poll..."),this.armIdleLoopOrAutoPoll(t),l("[ACode] \u2705 Idle loop armed, recovery process complete")}startSubmitTimeoutDetection(t,e,s){this.cancelSubmitTimeoutDetection(e),e.watchdogSubmitStartTime=Date.now(),e.watchdogLastDataFlowTime=Date.now(),e.watchdogTimer=setInterval(()=>{const o=this.sessions.get(t);if(!o){this.cancelSubmitTimeoutDetection(e);return}const a=o.adapterSession.status,i=Date.now();if(a==="waiting_input"){if(o.autoPollActive&&o.autoPollLastActivityAt&&o.idleToolActiveAt===void 0&&i-o.autoPollLastActivityAt>=kt){console.warn(`[ACode] CRITICAL: stuck auto-poll detected after ${Math.round((i-o.autoPollLastActivityAt)/1e3)}s (source: ${s}, session: ${t}): submit queued but no runtime events and no active poll_message tool. Forcing recovery (runtime force-settle will unblock submitQueue).`),this.executeWatchdogRecovery(t,o,s,"stuck_autopoll",i-o.autoPollLastActivityAt);return}o.watchdogSubmitStartTime=i,o.watchdogLastDataFlowTime=i;return}if(a==="tool_using"){const d=i-(o.watchdogLastDataFlowTime||i);if(d>=bt){const p=String(o.pendingToolEventData?.rawToolName??"");if((o.pendingToolAuthRequests?.size??0)>0){const m=o.pendingToolAuthRequests?.size??0;console.warn(`[ACode] tool_using elapsed ${Math.round(d/1e3)}s but ${m} tool permission request(s) awaiting user decision (session: ${t}), skipping recovery`),o.watchdogSubmitStartTime=i,o.watchdogLastDataFlowTime=i;return}if(p.endsWith("await_complete")){console.warn(`[ACode] tool_using elapsed ${Math.round(d/1e3)}s but await_complete is running (session: ${t}, tool: ${p}), skipping recovery (sub-agents have their own watchdogs, bg commands have timeout limits)`),o.watchdogSubmitStartTime=i,o.watchdogLastDataFlowTime=i;return}if(console.warn(`[ACode] CRITICAL: tool_using slow timeout triggered after ${Math.round(d/1e3)}s (source: ${s}, session: ${t}). Tool execution likely hung. Forcing recovery via recoverTurnFailure.`),o.runtime.detachCurrentToolExecution?.()){console.warn(`[ACode] tool_using slow (${Math.round(d/1e3)}s): detached tool ${p} to background (session: ${t}); waiting for the real tool_result to continue the turn`),o.watchdogSubmitStartTime=i,o.watchdogLastDataFlowTime=i;return}this.executeWatchdogRecovery(t,o,s,"tool_using_slow",d);return}o.watchdogSubmitStartTime=i;return}if(a!=="thinking")return;const r=i-(o.watchdogSubmitStartTime||i),n=i-(o.watchdogLastDataFlowTime||i);if(H>0&&n>=H){console.warn(`[ACode] CRITICAL: Data flow watchdog triggered after ${Math.round(n/1e3)}s (source: ${s}, session: ${t}, status: ${a}). No stream data received. Forcing recovery via recoverTurnFailure.`),this.executeWatchdogRecovery(t,o,s,"data_flow",n);return}if(j>0&&r>=j){console.warn(`[ACode] CRITICAL: submit timeout detected after ${Math.round(r/1e3)}s (source: ${s}, session: ${t}, status: ${a}). Forcing recovery to prevent permanent state stuck.`),this.executeWatchdogRecovery(t,o,s,"submit_timeout",r);return}},_t)}executeWatchdogRecovery(t,e,s,o,a){l(`[ACode] Watchdog (${o}): calling runtime.abort() after ${Math.round(a/1e3)}s...`);try{e.runtime.abort(),l(`[ACode] Watchdog (${o}): runtime.abort() called`)}catch(i){console.warn(`[ACode] \u26A0\uFE0F Watchdog (${o}): runtime.abort() threw: ${i instanceof Error?i.message:String(i)}`)}this.cancelSubmitTimeoutDetection(e),this.recoverTurnFailure(t,e),console.warn(`[ACode] \u26A0\uFE0F Watchdog (${o}) recovery complete. If abort() could not cancel the underlying operation, submitQueue may still be deadlocked.`)}cancelSubmitTimeoutDetection(t){t.watchdogTimer&&(clearInterval(t.watchdogTimer),t.watchdogTimer=void 0),t.watchdogSubmitStartTime=void 0,t.watchdogLastDataFlowTime=void 0}resetDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now(),e.watchdogSubmitStartTime=Date.now()}startDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now()}cancelDataFlowWatchdog(t){t.watchdogLastDataFlowTime=Date.now()}formatAiRequestActionDetail(t){const e=`
|
|
2
2
|
|
|
3
3
|
\u8BF7\u6C42\u5934:
|
|
4
|
-
`,
|
|
4
|
+
`,s=`
|
|
5
5
|
|
|
6
6
|
\u8BF7\u6C42\u4F53:
|
|
7
|
-
`,a=typeof t.model=="string"?t.model:"",i=typeof t.endpoint=="string"?t.endpoint:typeof t.requestUrl=="string"?t.requestUrl:"",
|
|
8
|
-
`)||"ACode \u6B63\u5728\u53D1\u9001 AI \u8BF7\u6C42",y
|
|
7
|
+
`,a=typeof t.model=="string"?t.model:"",i=typeof t.endpoint=="string"?t.endpoint:typeof t.requestUrl=="string"?t.requestUrl:"",r=typeof t.requestMethod=="string"?t.requestMethod:"",n=typeof t.stream=="boolean"?t.stream:void 0,d=typeof t.messageCount=="number"?t.messageCount:void 0,p=typeof t.toolCount=="number"?t.toolCount:void 0,m=typeof t.lastUserMessagePreview=="string"?t.lastUserMessagePreview:"",c=[a?`\u6A21\u578B: ${a}`:void 0,i?`\u63A5\u53E3: ${i}`:void 0,r?`\u65B9\u6CD5: ${r}`:void 0,n!==void 0?`\u6D41\u5F0F: ${n?"\u662F":"\u5426"}`:void 0,d!==void 0?`\u4E0A\u4E0B\u6587\u6D88\u606F: ${d}`:void 0,p!==void 0?`\u53EF\u7528\u5DE5\u5177: ${p}`:void 0,m?`\u6700\u540E\u8BF7\u6C42: ${m}`:void 0].filter(S=>!!S),g=/^(authorization|x-api-key|api-key|cookie|set-cookie|x-auth-token|token)$/i,f=t.requestHeaders,y=f?Object.entries(f).filter(([S])=>!g.test(S)).map(([S,R])=>`${S}: ${R}`).filter(S=>S.trim().length>0):[],A=c.join(`
|
|
8
|
+
`)||"ACode \u6B63\u5728\u53D1\u9001 AI \u8BF7\u6C42",_=y.length>0?`${e}${y.join(`
|
|
9
9
|
`)}`:"",b=typeof t.requestBody=="string"?t.requestBody:"",T=b.length>6e4?`${b.slice(0,6e4)}
|
|
10
|
-
...(\u8BF7\u6C42\u4F53\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD)`:b,k=T?`${
|
|
10
|
+
...(\u8BF7\u6C42\u4F53\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD)`:b,k=T?`${s}${T}`:"";return`${A}${_}${k}`}}export{ae as AcodeSdkAdapter,Mt as isFailedAcodeTurnComplete,$t as toAcodeEsmImportSpecifier};
|
|
11
11
|
|
|
@@ -19,6 +19,8 @@ export interface SessionSummary {
|
|
|
19
19
|
export declare class BrowserPool {
|
|
20
20
|
private readonly config;
|
|
21
21
|
private readonly sessions;
|
|
22
|
+
|
|
23
|
+
private readonly closingKeys;
|
|
22
24
|
private idleTimer;
|
|
23
25
|
|
|
24
26
|
private readonly sessionCloseHandlers;
|
|
@@ -48,6 +50,8 @@ export declare class BrowserPool {
|
|
|
48
50
|
|
|
49
51
|
close(agentId: string, browserId?: string): Promise<void>;
|
|
50
52
|
|
|
53
|
+
private registerPageClose;
|
|
54
|
+
|
|
51
55
|
closeByAgent(agentId: string): Promise<void>;
|
|
52
56
|
|
|
53
57
|
collectListenerEvents(agentId: string): Promise<ListenerTriggerEvent[]>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{chromium as $}from"playwright";import{formatBrowserLaunchError as _,installPlaywrightChromium as D,isBrowserMissingError as b}from"./browser-bootstrap.js";import{BrowserProfileStore as H}from"./browser-profile-store.js";import{ListenerStore as k}from"./listener-store.js";import{PageListenerManager as B}from"./page-listener-manager.js";import{ScreencastManager as L}from"./screencast-manager.js";import{buildStealthInitScript as N,STEALTH_LAUNCH_ARGS as O,STEALTH_IGNORE_ARGS as R}from"./stealth.js";import{StepArchive as K}from"./step-archive.js";import{DEFAULT_BROWSER_ID as l,IDLE_CHECK_INTERVAL_MS as z,IDLE_TIMEOUT_MS as U,MAX_PAGES_PER_SESSION as W,MAX_SESSIONS as I,MAX_SESSIONS_PER_AGENT as M,SESSION_TIMEOUT_MS as G,makeBrowserKey as q}from"./types.js";const V=1280,X=720;class F{onSessionClose(e){this.sessionCloseHandlers.push(e)}getSsrfConfig(){return this.config.ssrfConfig}setListenerTriggerHook(e){this.listenerTriggerHook=e}resolveKey(e,s){if(!e||typeof e!="string")throw new Error("scopeKey must be a non-empty string");const i=s&&s.trim()?s.trim():l;if(i.includes("::"))throw new Error(`browserId must not contain "::" (reserved separator): ${i}`);return q(e,i)}constructor(e){this.sessions=new Map,this.idleTimer=null,this.sessionCloseHandlers=[],this.listenerTriggerHook=null,this.config={headless:e?.headless??!0,executablePath:e?.executablePath,viewportWidth:e?.viewportWidth??V,viewportHeight:e?.viewportHeight??X,maxPagesPerSession:e?.maxPagesPerSession??W,maxSessionDurationMs:e?.maxSessionDurationMs??G,idleTimeoutMs:e?.idleTimeoutMs??U,launchArgs:e?.launchArgs,screencastConfig:e?.screencastConfig,archiveConfig:e?.archiveConfig,ssrfConfig:e?.ssrfConfig,userAgent:e?.userAgent,locale:e?.locale,timezone:e?.timezone,profileDir:e?.profileDir,stealth:e?.stealth??!0},this.profileStore=e?.profileDir?new H({profileDir:e.profileDir}):null,this.listenerStore=e?.profileDir?new k({listenerDir:e.profileDir}):null}async launch(e,s,i,t){const r=t??`agent:${e}`,o=this.resolveKey(r,s);if(this.sessions.has(o))throw new Error(`Browser session already exists for ${e}/${s??l}`);if(this.sessions.size>=I)throw new Error(`BrowserPool reached MAX_SESSIONS=${I}; close existing sessions before launching new ones`);let c=0;for(const n of this.sessions.values())n.agentId===e&&(c+=1);if(c>=M)throw new Error(`BrowserPool reached MAX_SESSIONS_PER_AGENT=${M} for agent ${e}; close some browser sessions first`);const h={headless:this.config.headless};this.config.executablePath&&(h.executablePath=this.config.executablePath);const S=[`--window-size=${this.config.viewportWidth},${this.config.viewportHeight}`];this.config.stealth?(h.ignoreDefaultArgs=[...R],h.args=[...S,...O,...this.config.launchArgs??[]]):this.config.launchArgs&&this.config.launchArgs.length>0?h.args=[...S,...this.config.launchArgs]:h.args=[...S];const w=await this.launchChromium(h);let m=this.config.userAgent,y;if(this.config.stealth)try{const n=await w.newBrowserCDPSession(),f=await n.send("Browser.getVersion"),g=f.product?.match(/Chrome\/(\d+)/);g&&(y=g[1]),!m&&f.userAgent&&(m=String(f.userAgent).replace(/HeadlessChrome\/([\d.]+)/g,"Chrome/$1")),await n.detach().catch(()=>{})}catch{}const p={viewport:{width:this.config.viewportWidth,height:this.config.viewportHeight}};if(m&&(p.userAgent=m),this.config.locale&&(p.locale=this.config.locale),this.config.timezone&&(p.timezoneId=this.config.timezone),this.profileStore)try{let n=await this.profileStore.load(r);!n&&r!==e&&(n=await this.profileStore.load(e),n&&await this.profileStore.save(r,n)),n&&(p.storageState=n)}catch{}const d=await w.newContext(p).catch(async n=>{throw await w.close().catch(()=>{}),n});this.config.stealth&&await d.addInitScript(N(y??"126")).catch(()=>{});const v=await d.newPage().catch(async n=>{throw await w.close().catch(()=>{}),n}),A=new L(this.config.screencastConfig),C=new K(this.config.archiveConfig),T=new B(e,s?.trim()||l,{onTrigger:(n,f)=>{const g=this.listenerTriggerHook;if(g)try{g(e,s?.trim()||l,n,f)}catch{}},store:this.listenerStore,scopeKey:r});try{await A.start(v)}catch{}try{const n=v;n.screencast?.showActions&&await n.screencast.showActions({cursor:"pointer"})}catch{}const E=Date.now(),a={agentId:e,scopeKey:r,browserId:s?.trim()||l,title:i?.trim()||"\u6D4F\u89C8\u5668\u4F1A\u8BDD",browser:w,context:d,pages:[v],activePageIndex:0,screencast:A,archive:C,listenerManager:T,launchedAt:E,lastActivityAt:E,humanTakeover:!1,qualityPreset:"original"};d.on("page",n=>{if(a.pages.length>=this.config.maxPagesPerSession){n.close().catch(()=>{});return}a.pages.push(n);const f=a.pages.length-1;this.setActivePage(e,f,a.browserId).catch(()=>{}),n.on("close",()=>{const g=a.pages.indexOf(n);if(g!==-1){if(a.pages.splice(g,1),a.pages.length===0){this.close(e,a.browserId).catch(()=>{});return}a.activePageIndex>=a.pages.length?a.activePageIndex=a.pages.length-1:g<a.activePageIndex&&(a.activePageIndex-=1)}})});try{await T.attach(v)}catch{}this.sessions.set(o,a),this.ensureIdleTimer();const x=this.profileStore;return x&&(a.autoSaveTimer=setInterval(async()=>{try{const n=await a.context.storageState();await x.save(a.scopeKey,n)}catch{}},3e4),a.autoSaveTimer.unref&&a.autoSaveTimer.unref()),a}async launchChromium(e){try{return await $.launch(e)}catch(s){if(this.config.executablePath||!b(s))throw s;const i=await D();try{return await $.launch(e)}catch(t){throw _(t instanceof Error?t:new Error(String(t)),i)}}}get(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t}async getOrLaunch(e,s,i,t){const r=t??`agent:${e}`,o=this.resolveKey(r,s),c=this.sessions.get(o);return c||this.launch(e,s,i,t)}async close(e,s){const i=s&&s.trim()?s.trim():l;let t,r;for(const[c,h]of this.sessions)if(h.agentId===e&&h.browserId===i){t=c,r=h;break}if(!t||!r)return;const o=r;o.autoSaveTimer&&(clearInterval(o.autoSaveTimer),o.autoSaveTimer=void 0);try{await o.screencast.dispose()}catch{}try{await o.listenerManager.dispose()}catch{}if(o.archive.clear(),this.profileStore)try{const c=await o.context.storageState();await this.profileStore.save(o.scopeKey,c)}catch{}try{await o.browser.close()}catch{}this.sessions.delete(t);for(const c of this.sessionCloseHandlers)try{c(e,i)}catch{}this.sessions.size===0&&this.stopIdleTimer()}async closeByAgent(e){if(!e)return;const s=[];for(const[i,t]of this.sessions)t.agentId===e&&s.push(i);await Promise.all(s.map(i=>{const r=this.sessions.get(i)?.browserId;return this.close(e,r)}))}async collectListenerEvents(e){if(!e)return[];const s=[];for(const i of this.sessions.values()){if(i.agentId!==e)continue;await i.listenerManager.scanExistsAtPoll();const t=i.listenerManager.collectEvents();t.length>0&&s.push(...t)}return s.sort((i,t)=>i.triggeredAt-t.triggeredAt),s}getActivePage(e,s){const i=this.requireSession(e,s),t=i.pages[i.activePageIndex];if(!t||t.isClosed())throw new Error(`No active page for agent ${e}/${i.browserId} (index ${i.activePageIndex})`);return t}async setActivePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);if(t.activePageIndex===s){const c=t.pages[s];if(c==null)throw new Error(`Page index ${s} out of range for agent ${e}/${t.browserId}`);return c}const r=t.pages[t.activePageIndex];if(r&&!r.isClosed())try{await t.screencast.stop(r)}catch{}t.activePageIndex=s;const o=t.pages[s];if(o==null)throw new Error(`Page index ${s} out of range for agent ${e}/${t.browserId}`);try{await t.screencast.start(o)}catch{}try{await t.listenerManager.reattach(o)}catch{}return this.touchActivity(e,i),o}async newPage(e,s){const i=this.requireSession(e,s);if(i.pages.length>=this.config.maxPagesPerSession)throw new Error(`Agent ${e}/${i.browserId} reached maxPagesPerSession=${this.config.maxPagesPerSession}; close a tab first`);const t=await i.context.newPage();i.pages.push(t);const r=i.pages.length-1;return this.touchActivity(e,s),this.setActivePage(e,r,s)}async closePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);const r=t.pages[s];if(r==null)throw new Error(`Page index ${s} out of range for agent ${e}/${t.browserId}`);if(s===t.activePageIndex)try{await t.screencast.stop(r)}catch{}try{await r.close()}catch{}if(t.pages.splice(s,1),t.pages.length===0){await this.close(e,i);return}t.activePageIndex>=t.pages.length?t.activePageIndex=t.pages.length-1:s<t.activePageIndex&&(t.activePageIndex-=1);const o=t.pages[t.activePageIndex];if(o==null)throw new Error(`No active page for agent ${e}/${t.browserId}`);if(!o.isClosed()){try{await t.screencast.start(o)}catch{}try{await t.listenerManager.reattach(o)}catch{}}this.touchActivity(e,i)}listSessions(e){const s=[];for(const i of this.sessions.values()){if(e&&i.agentId!==e)continue;const t=i.pages[i.activePageIndex];let r="";const o="";if(t&&!t.isClosed())try{r=t.url()}catch{}s.push({agentId:i.agentId,browserId:i.browserId,title:i.title,pageCount:i.pages.length,activePageIndex:i.activePageIndex,launchedAt:i.launchedAt,lastActivityAt:i.lastActivityAt,currentUrl:r,currentTitle:o})}return s}touchActivity(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i){t.lastActivityAt=Date.now();return}}async dispose(){this.stopIdleTimer();const e=Array.from(this.sessions.keys());await Promise.all(e.map(s=>{const i=this.sessions.get(s);return i?this.close(i.agentId,i.browserId):Promise.resolve()}))}requireSession(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t;throw new Error(`Browser session not launched for ${e}/${s??l}`)}ensureIdleTimer(){this.idleTimer||this.config.idleTimeoutMs<=0&&this.config.maxSessionDurationMs<=0||(this.idleTimer=setInterval(()=>{this.checkIdleSessions()},z),this.idleTimer.unref&&this.idleTimer.unref())}stopIdleTimer(){this.idleTimer&&(clearInterval(this.idleTimer),this.idleTimer=null)}async checkIdleSessions(){const e=Date.now(),s=[];for(const i of this.sessions.values()){if(i.humanTakeover)continue;const t=e-i.lastActivityAt,r=e-i.launchedAt;if(this.config.idleTimeoutMs>0&&t>this.config.idleTimeoutMs){s.push(i);continue}this.config.maxSessionDurationMs>0&&r>this.config.maxSessionDurationMs&&s.push(i)}await Promise.all(s.map(i=>this.close(i.agentId,i.browserId)))}}let u=null;function re(P){return u||(u=new F(P)),u}async function oe(){u&&(await u.dispose(),u=null)}export{F as BrowserPool,oe as disposeBrowserPool,re as getBrowserPool};
|
|
1
|
+
import{chromium as I}from"playwright";import{formatBrowserLaunchError as _,installPlaywrightChromium as D,isBrowserMissingError as b}from"./browser-bootstrap.js";import{BrowserProfileStore as H}from"./browser-profile-store.js";import{ListenerStore as k}from"./listener-store.js";import{PageListenerManager as B}from"./page-listener-manager.js";import{ScreencastManager as L}from"./screencast-manager.js";import{buildStealthInitScript as N,STEALTH_LAUNCH_ARGS as O,STEALTH_IGNORE_ARGS as K}from"./stealth.js";import{StepArchive as R}from"./step-archive.js";import{DEFAULT_BROWSER_ID as l,IDLE_CHECK_INTERVAL_MS as z,IDLE_TIMEOUT_MS as U,MAX_PAGES_PER_SESSION as W,MAX_SESSIONS as $,MAX_SESSIONS_PER_AGENT as C,SESSION_TIMEOUT_MS as G,makeBrowserKey as q}from"./types.js";const V=1280,X=720;class F{onSessionClose(e){this.sessionCloseHandlers.push(e)}getSsrfConfig(){return this.config.ssrfConfig}setListenerTriggerHook(e){this.listenerTriggerHook=e}resolveKey(e,s){if(!e||typeof e!="string")throw new Error("scopeKey must be a non-empty string");const i=s&&s.trim()?s.trim():l;if(i.includes("::"))throw new Error(`browserId must not contain "::" (reserved separator): ${i}`);return q(e,i)}constructor(e){this.sessions=new Map,this.closingKeys=new Set,this.idleTimer=null,this.sessionCloseHandlers=[],this.listenerTriggerHook=null,this.config={headless:e?.headless??!0,executablePath:e?.executablePath,viewportWidth:e?.viewportWidth??V,viewportHeight:e?.viewportHeight??X,maxPagesPerSession:e?.maxPagesPerSession??W,maxSessionDurationMs:e?.maxSessionDurationMs??G,idleTimeoutMs:e?.idleTimeoutMs??U,launchArgs:e?.launchArgs,screencastConfig:e?.screencastConfig,archiveConfig:e?.archiveConfig,ssrfConfig:e?.ssrfConfig,userAgent:e?.userAgent,locale:e?.locale,timezone:e?.timezone,profileDir:e?.profileDir,stealth:e?.stealth??!0},this.profileStore=e?.profileDir?new H({profileDir:e.profileDir}):null,this.listenerStore=e?.profileDir?new k({listenerDir:e.profileDir}):null}async launch(e,s,i,t){const r=t??`agent:${e}`,o=this.resolveKey(r,s);if(this.sessions.has(o))throw new Error(`Browser session already exists for ${e}/${s??l}`);if(this.sessions.size>=$)throw new Error(`BrowserPool reached MAX_SESSIONS=${$}; close existing sessions before launching new ones`);let c=0;for(const n of this.sessions.values())n.agentId===e&&(c+=1);if(c>=C)throw new Error(`BrowserPool reached MAX_SESSIONS_PER_AGENT=${C} for agent ${e}; close some browser sessions first`);const h={headless:this.config.headless};this.config.executablePath&&(h.executablePath=this.config.executablePath);const S=[`--window-size=${this.config.viewportWidth},${this.config.viewportHeight}`];this.config.stealth?(h.ignoreDefaultArgs=[...K],h.args=[...S,...O,...this.config.launchArgs??[]]):this.config.launchArgs&&this.config.launchArgs.length>0?h.args=[...S,...this.config.launchArgs]:h.args=[...S];const f=await this.launchChromium(h);let v=this.config.userAgent,P;if(this.config.stealth)try{const n=await f.newBrowserCDPSession(),g=await n.send("Browser.getVersion"),m=g.product?.match(/Chrome\/(\d+)/);m&&(P=m[1]),!v&&g.userAgent&&(v=String(g.userAgent).replace(/HeadlessChrome\/([\d.]+)/g,"Chrome/$1")),await n.detach().catch(()=>{})}catch{}const w={viewport:{width:this.config.viewportWidth,height:this.config.viewportHeight}};if(v&&(w.userAgent=v),this.config.locale&&(w.locale=this.config.locale),this.config.timezone&&(w.timezoneId=this.config.timezone),this.profileStore)try{let n=await this.profileStore.load(r);!n&&r!==e&&(n=await this.profileStore.load(e),n&&await this.profileStore.save(r,n)),n&&(w.storageState=n)}catch{}const p=await f.newContext(w).catch(async n=>{throw await f.close().catch(()=>{}),n});this.config.stealth&&await p.addInitScript(N(P??"126")).catch(()=>{});const d=await p.newPage().catch(async n=>{throw await f.close().catch(()=>{}),n}),A=new L(this.config.screencastConfig),M=new R(this.config.archiveConfig),T=new B(e,s?.trim()||l,{onTrigger:(n,g)=>{const m=this.listenerTriggerHook;if(m)try{m(e,s?.trim()||l,n,g)}catch{}},store:this.listenerStore,scopeKey:r});try{await A.start(d)}catch{}try{const n=d;n.screencast?.showActions&&await n.screencast.showActions({cursor:"pointer"})}catch{}const E=Date.now(),a={agentId:e,scopeKey:r,browserId:s?.trim()||l,title:i?.trim()||"\u6D4F\u89C8\u5668\u4F1A\u8BDD",browser:f,context:p,pages:[d],activePageIndex:0,screencast:A,archive:M,listenerManager:T,launchedAt:E,lastActivityAt:E,humanTakeover:!1,qualityPreset:"original"};p.on("page",n=>{if(a.pages.length>=this.config.maxPagesPerSession){n.close().catch(()=>{});return}a.pages.push(n);const g=a.pages.length-1;this.setActivePage(e,g,a.browserId).catch(()=>{}),this.registerPageClose(a,n)});try{await T.attach(d)}catch{}this.sessions.set(o,a),this.ensureIdleTimer(),this.registerPageClose(a,d),f.on("disconnected",()=>{this.sessions.get(o)===a&&this.close(a.agentId,a.browserId).catch(()=>{})}),p.on("close",()=>{this.sessions.get(o)===a&&this.close(a.agentId,a.browserId).catch(()=>{})});const x=this.profileStore;return x&&(a.autoSaveTimer=setInterval(async()=>{try{const n=await a.context.storageState();await x.save(a.scopeKey,n)}catch{}},3e4),a.autoSaveTimer.unref&&a.autoSaveTimer.unref()),a}async launchChromium(e){try{return await I.launch(e)}catch(s){if(this.config.executablePath||!b(s))throw s;const i=await D();try{return await I.launch(e)}catch(t){throw _(t instanceof Error?t:new Error(String(t)),i)}}}get(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t}async getOrLaunch(e,s,i,t){const r=t??`agent:${e}`,o=this.resolveKey(r,s),c=this.sessions.get(o);return c||this.launch(e,s,i,t)}async close(e,s){const i=s&&s.trim()?s.trim():l;let t,r;for(const[c,h]of this.sessions)if(h.agentId===e&&h.browserId===i){t=c,r=h;break}if(!t||!r||this.closingKeys.has(t))return;this.closingKeys.add(t);const o=r;try{o.autoSaveTimer&&(clearInterval(o.autoSaveTimer),o.autoSaveTimer=void 0);try{await o.screencast.dispose()}catch{}try{await o.listenerManager.dispose()}catch{}if(o.archive.clear(),this.profileStore)try{const c=await o.context.storageState();await this.profileStore.save(o.scopeKey,c)}catch{}try{await o.browser.close()}catch{}this.sessions.delete(t);for(const c of this.sessionCloseHandlers)try{c(e,i)}catch{}this.sessions.size===0&&this.stopIdleTimer()}finally{this.closingKeys.delete(t)}}registerPageClose(e,s){s.on("close",()=>{const i=e.pages.indexOf(s);if(i!==-1){if(e.pages.splice(i,1),e.pages.length===0){this.close(e.agentId,e.browserId).catch(()=>{});return}e.activePageIndex>=e.pages.length?e.activePageIndex=e.pages.length-1:i<e.activePageIndex&&(e.activePageIndex-=1)}})}async closeByAgent(e){if(!e)return;const s=[];for(const[i,t]of this.sessions)t.agentId===e&&s.push(i);await Promise.all(s.map(i=>{const r=this.sessions.get(i)?.browserId;return this.close(e,r)}))}async collectListenerEvents(e){if(!e)return[];const s=[];for(const i of this.sessions.values()){if(i.agentId!==e)continue;await i.listenerManager.scanExistsAtPoll();const t=i.listenerManager.collectEvents();t.length>0&&s.push(...t)}return s.sort((i,t)=>i.triggeredAt-t.triggeredAt),s}getActivePage(e,s){const i=this.requireSession(e,s),t=i.pages[i.activePageIndex];if(!t||t.isClosed())throw new Error(`No active page for agent ${e}/${i.browserId} (index ${i.activePageIndex})`);return t}async setActivePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);if(t.activePageIndex===s){const c=t.pages[s];if(c==null)throw new Error(`Page index ${s} out of range for agent ${e}/${t.browserId}`);return c}const r=t.pages[t.activePageIndex];if(r&&!r.isClosed())try{await t.screencast.stop(r)}catch{}t.activePageIndex=s;const o=t.pages[s];if(o==null)throw new Error(`Page index ${s} out of range for agent ${e}/${t.browserId}`);try{await t.screencast.start(o)}catch{}try{await t.listenerManager.reattach(o)}catch{}return this.touchActivity(e,i),o}async newPage(e,s){const i=this.requireSession(e,s);if(i.pages.length>=this.config.maxPagesPerSession)throw new Error(`Agent ${e}/${i.browserId} reached maxPagesPerSession=${this.config.maxPagesPerSession}; close a tab first`);const t=await i.context.newPage();i.pages.push(t);const r=i.pages.length-1;return this.touchActivity(e,s),this.setActivePage(e,r,s)}async closePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);const r=t.pages[s];if(r==null)throw new Error(`Page index ${s} out of range for agent ${e}/${t.browserId}`);if(s===t.activePageIndex)try{await t.screencast.stop(r)}catch{}try{await r.close()}catch{}if(t.pages.splice(s,1),t.pages.length===0){await this.close(e,i);return}t.activePageIndex>=t.pages.length?t.activePageIndex=t.pages.length-1:s<t.activePageIndex&&(t.activePageIndex-=1);const o=t.pages[t.activePageIndex];if(o==null)throw new Error(`No active page for agent ${e}/${t.browserId}`);if(!o.isClosed()){try{await t.screencast.start(o)}catch{}try{await t.listenerManager.reattach(o)}catch{}}this.touchActivity(e,i)}listSessions(e){const s=[];for(const i of this.sessions.values()){if(e&&i.agentId!==e)continue;const t=i.pages[i.activePageIndex];let r="";const o="";if(t&&!t.isClosed())try{r=t.url()}catch{}s.push({agentId:i.agentId,browserId:i.browserId,title:i.title,pageCount:i.pages.length,activePageIndex:i.activePageIndex,launchedAt:i.launchedAt,lastActivityAt:i.lastActivityAt,currentUrl:r,currentTitle:o})}return s}touchActivity(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i){t.lastActivityAt=Date.now();return}}async dispose(){this.stopIdleTimer();const e=Array.from(this.sessions.keys());await Promise.all(e.map(s=>{const i=this.sessions.get(s);return i?this.close(i.agentId,i.browserId):Promise.resolve()}))}requireSession(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t;throw new Error(`Browser session not launched for ${e}/${s??l}`)}ensureIdleTimer(){this.idleTimer||this.config.idleTimeoutMs<=0&&this.config.maxSessionDurationMs<=0||(this.idleTimer=setInterval(()=>{this.checkIdleSessions()},z),this.idleTimer.unref&&this.idleTimer.unref())}stopIdleTimer(){this.idleTimer&&(clearInterval(this.idleTimer),this.idleTimer=null)}async checkIdleSessions(){const e=Date.now(),s=[];for(const i of this.sessions.values()){if(i.humanTakeover)continue;const t=e-i.lastActivityAt,r=e-i.launchedAt;if(this.config.idleTimeoutMs>0&&t>this.config.idleTimeoutMs){s.push(i);continue}this.config.maxSessionDurationMs>0&&r>this.config.maxSessionDurationMs&&s.push(i)}await Promise.all(s.map(i=>this.close(i.agentId,i.browserId)))}}let u=null;function re(y){return u||(u=new F(y)),u}async function oe(){u&&(await u.dispose(),u=null)}export{F as BrowserPool,oe as disposeBrowserPool,re as getBrowserPool};
|
|
2
2
|
|
|
@@ -39,6 +39,12 @@ export interface CreateBuiltInDesktopToolsOptions {
|
|
|
39
39
|
|
|
40
40
|
export declare function createBuiltInDesktopTools(options: CreateBuiltInDesktopToolsOptions): ACodeBuiltInToolLike[];
|
|
41
41
|
|
|
42
|
+
export declare function injectRememberedOrigin(args: DesktopJsonObject): DesktopJsonObject;
|
|
43
|
+
|
|
44
|
+
export declare function rememberScreenshotOrigin(toolName: string, args: DesktopJsonObject, result: string): void;
|
|
45
|
+
|
|
46
|
+
export declare function clearScreenshotOrigins(): void;
|
|
47
|
+
|
|
42
48
|
export declare function injectScreenshotPath(toolName: string, args: DesktopJsonObject, screenshotDir: string): Promise<DesktopJsonObject>;
|
|
43
49
|
|
|
44
50
|
export declare function pruneOldScreenshots(dir: string, options?: {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{mkdir as
|
|
1
|
+
import{mkdir as S,readdir as _,rm as w,stat as N}from"node:fs/promises";import{dirname as O,join as f}from"node:path";const l=new Set(["desktop_screenshot","desktop_window_screenshot"]),x=" Note: when no `path` is given, the bridge auto-saves the PNG to a local file and returns its path (NOT base64). COORDINATES: the bridge remembers each auto-saved screenshot's screen origin \u2014 running desktop_perceive on that file returns SCREEN coordinates directly, ready for desktop_mouse. Never do coordinate arithmetic yourself. For native windows prefer desktop_uia_tree + desktop_uia_click instead \u2014 precise and vision-free.";function C(e){const{tools:t,forwarder:o}=e,i=e.screenshotDir??f(process.cwd(),".desktop-mcp","screenshots");return t.filter(r=>r.name.startsWith("desktop_")).map(r=>{const s=l.has(r.name),c=r.name==="desktop_perceive",p=s?`${r.description??r.name}${x}`:r.description??r.name;return{kind:"builtin",toolName:r.name,exposedName:r.name,description:p,inputSchema:D(r.inputSchema),category:"desktop",async execute(n){let u=n;s?u=await b(r.name,n,i):c&&(u=E(n));const h=await o(r.name,u);return s&&k(r.name,u,h),h}}})}const a=new Map,g=500;function d(e){return e.trim().replace(/\//g,"\\").toLowerCase()}function E(e){const t=typeof e.path=="string"?e.path.trim():"";if(!t||e.origin_x!=null||e.origin_y!=null)return e;const o=a.get(d(t));return o?{...e,origin_x:o.x,origin_y:o.y}:e}function k(e,t,o){let i=null;try{i=JSON.parse(o)}catch{i=null}const s=(typeof i?.path=="string"?i.path:"")||(typeof t.path=="string"?t.path:"");if(!s.trim())return;let c=null;if(e==="desktop_window_screenshot"){const n=i?.origin;n&&typeof n.x=="number"&&typeof n.y=="number"&&(c={x:n.x,y:n.y,at:Date.now()})}else{const n=t.region;n&&typeof n.x=="number"&&typeof n.y=="number"?c={x:n.x,y:n.y,at:Date.now()}:n||(c={x:0,y:0,at:Date.now()})}if(!c)return;const p=d(s);for(a.delete(p),a.set(p,c);a.size>g;){const n=a.keys().next().value;if(n===void 0)break;a.delete(n)}}function j(){a.clear()}async function b(e,t,o){if(!l.has(e)||typeof t.path=="string"&&t.path.trim())return t;const i=R(o);return await S(O(i),{recursive:!0}).catch(()=>{}),v(o).catch(()=>{}),{...t,path:i}}const m=(()=>{const e=Number.parseInt(process.env.AWS_DESKTOP_MCP_SCREENSHOT_RETENTION_DAYS??"",10);return Number.isFinite(e)&&e>=0?e:7})(),T=2*36e5;let y=0;async function v(e,t){const o=t?.now??Date.now();if(m===0||!t?.force&&o-y<T)return;y=o;const i=o-m*864e5;let r;try{r=await _(e)}catch{return}await Promise.all(r.filter(s=>s.startsWith("shot-")&&s.endsWith(".png")).map(async s=>{const c=f(e,s);try{(await N(c)).mtimeMs<i&&await w(c,{force:!0})}catch{}}))}let P=0;function R(e){const t=(++P).toString(36).padStart(3,"0"),o=new Date().toISOString().replace(/[:.]/g,"-");return f(e,`shot-${o}-${t}.png`)}function D(e){if(!e||typeof e!="object")return{type:"object",properties:{}};try{return JSON.parse(JSON.stringify(e))}catch{return{type:"object",properties:{}}}}export{j as clearScreenshotOrigins,C as createBuiltInDesktopTools,E as injectRememberedOrigin,b as injectScreenshotPath,R as nextScreenshotFilePath,v as pruneOldScreenshots,k as rememberScreenshotOrigin};
|
|
2
2
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{promises as h}from"node:fs";import y from"node:path";import{pathToFileURL as b}from"node:url";import{Router as j}from"express";import{getAcodeDataHome as B}from"../config.js";import{validateToken as
|
|
2
|
-
`);return n>0&&e.shift(),{text:e.slice(-t),fileExists:!0,truncated:d<s}}finally{await i.close()}}
|
|
1
|
+
import{promises as h}from"node:fs";import y from"node:path";import{pathToFileURL as b}from"node:url";import{Router as j}from"express";import{getAcodeDataHome as B}from"../config.js";import{validateToken as p}from"../middleware/auth.js";import{getAcodeBgManager as I}from"../services/acode-bg-manager.js";import{getReleasedAcodeEntryPath as T}from"../services/acode-package.js";import{createLogger as E}from"../utils/logger.js";import{listPtySessions as _}from"./pty.js";let m=null;function S(){return m||(m=(async()=>{try{const a=T(),t=y.join(y.dirname(a),"background-command-store.js"),r=await import(b(t).href);return r.BackgroundCommandStore?new r.BackgroundCommandStore:(g.warn("[acode-bg-tasks] BackgroundCommandStore \u4E0D\u53EF\u7528,\u78C1\u76D8\u515C\u5E95\u7981\u7528"),null)}catch(a){return g.warn("[acode-bg-tasks] \u52A0\u8F7D BackgroundCommandStore \u5931\u8D25: %s",a?.message),null}})().then(a=>(a||(m=null),a)),m)}const g=E("acode-bg-tasks"),k=j();function L(a){return String(a).replace(/[^a-zA-Z0-9_-]/g,"_")}function v(a){return y.join(B(),"bg-tasks",L(a),"state.json")}k.get("/acode-bg-tasks/:agentId",p,async(a,t)=>{const r=a.params.agentId,i=Array.isArray(r)?r[0]:r;if(!i||!String(i).trim()){t.status(400).json({ok:!1,error:"agentId is required"});return}const o=v(String(i));try{const s=await h.readFile(o,"utf-8"),d=JSON.parse(s),u=(Array.isArray(d.tasks)?d.tasks:[]).map(c=>{const e=c;return{taskId:e.taskId,status:e.status,command:e.commandPreview??e.command,commandPreview:e.commandPreview,description:typeof e.description=="string"?e.description:void 0,cwd:e.cwd,startedAt:e.startedAt,finishedAt:e.finishedAt,exitCode:e.exitCode,signal:e.signal,outputLineCount:e.outputLineCount,outputBytes:e.outputBytes}});t.json({ok:!0,agentId:String(i),updatedAt:typeof d.updatedAt=="number"?d.updatedAt:null,tasks:u})}catch(s){if((typeof s=="object"&&s!==null?Reflect.get(s,"code"):void 0)==="ENOENT"){t.json({ok:!0,agentId:String(i),updatedAt:null,tasks:[]});return}if(s instanceof SyntaxError){g.warn("[acode-bg-tasks] \u6587\u4EF6\u635F\u574F\uFF08JSON \u89E3\u6790\u5931\u8D25\uFF09: %s",o),t.json({ok:!0,agentId:String(i),updatedAt:null,tasks:[]});return}const n=s;g.error("[acode-bg-tasks] \u8BFB\u53D6\u5931\u8D25: %s",n.message),t.status(500).json({ok:!1,error:n.message})}}),k.get("/acode-agent-pty-sessions/:agentId",p,(a,t)=>{const r=a.params.agentId,i=Array.isArray(r)?r[0]:r;if(!i||!String(i).trim()){t.status(400).json({ok:!1,error:"agentId is required"});return}const o=String(i),d=_().filter(n=>n.ownerUserId===o);t.json({ok:!0,agentId:o,sessions:d})});const P=15e3;function x(a){return{taskId:a.taskId,status:a.status,exitCode:a.exitCode,signal:a.signal,startedAt:a.startedAt,finishedAt:a.finishedAt,command:a.command,lines:a.lines,nextSeq:a.nextSeq,totalLines:a.totalLines,totalBytes:a.totalBytes,truncated:a.truncated}}async function A(a,t){let i=null;try{i=await h.open(a,"r")}catch(o){if(o?.code==="ENOENT")return{text:[],fileExists:!1,truncated:!1};throw o}try{const s=(await i.stat()).size;if(s===0)return{text:[],fileExists:!0,truncated:!1};const d=Math.min(s,2097152),n=s-d,u=Buffer.alloc(d);await i.read(u,0,d,n);const e=u.toString("utf-8").split(`
|
|
2
|
+
`);return n>0&&e.shift(),{text:e.slice(-t),fileExists:!0,truncated:d<s}}finally{await i.close()}}k.get("/acode-bg-tasks/:agentId/:taskId/output",p,(a,t)=>{(async()=>{const r=String(a.params.agentId),i=String(a.params.taskId),o=Math.min(2e3,Math.max(100,Number(a.query.tailLines)||500));try{const s=await I();if(s){const d=s.getTaskOutputSnapshot(r,i);if(d){t.json({ok:!0,source:"memory",...x(d)});return}}}catch(s){g.warn("[acode-bg-tasks] \u5185\u5B58\u5FEB\u7167\u8BFB\u53D6\u5931\u8D25,\u964D\u7EA7\u8D70\u78C1\u76D8: %s",s?.message)}try{const s=await S();if(!s){t.status(503).json({ok:!1,error:"store unavailable"});return}const d=await s.readSession(r);if(!d){t.status(404).json({ok:!1,error:"task_not_found"});return}const n=d.tasks.find(f=>f.taskId===i);if(!n){t.status(404).json({ok:!1,error:"task_not_found"});return}const{text:u,fileExists:c,truncated:e}=await A(n.outputPath,o);t.json({ok:!0,source:"file",taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:u,nextSeq:0,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:e||!c})}catch(s){g.error("[acode-bg-tasks] \u78C1\u76D8\u515C\u5E95\u8BFB\u53D6\u5931\u8D25: %s",s?.message),t.status(500).json({ok:!1,error:"internal_error"})}})().catch(r=>{g.error("[acode-bg-tasks] output \u8BFB\u53D6\u5931\u8D25: %s",r?.message),t.status(500).json({ok:!1,error:"internal_error"})})}),k.get("/acode-bg-tasks/:agentId/:taskId/stream",p,(a,t)=>{(async()=>{const r=String(a.params.agentId),i=String(a.params.taskId);t.status(200),t.set({"Content-Type":"text/event-stream","Cache-Control":"no-cache, no-transform",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.flushHeaders?.();const o=(u,c)=>{t.writableEnded||t.destroyed||(c!==void 0&&t.write(`id: ${c}
|
|
3
3
|
`),t.write(`data: ${JSON.stringify(u)}
|
|
4
4
|
|
|
5
|
-
`))};let s,d=null,n=null;try{s=await I(),s&&(d=s,n=s.getTaskOutputSnapshot(r,i))}catch(u){g.warn("[acode-bg-tasks] \u5185\u5B58\u8BFB\u53D6\u5931\u8D25,\u964D\u7EA7\u8D70\u78C1\u76D8: %s",u?.message)}if(n){if(!d){o({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}if(o({type:"snapshot",source:"memory",...x(n)}),n.status!=="running"){o({type:"done",status:n.status,exitCode:n.exitCode,signal:n.signal}),t.end();return}let u=null,
|
|
5
|
+
`))};let s,d=null,n=null;try{s=await I(),s&&(d=s,n=s.getTaskOutputSnapshot(r,i))}catch(u){g.warn("[acode-bg-tasks] \u5185\u5B58\u8BFB\u53D6\u5931\u8D25,\u964D\u7EA7\u8D70\u78C1\u76D8: %s",u?.message)}if(n){if(!d){o({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}if(o({type:"snapshot",source:"memory",...x(n)}),n.status!=="running"){o({type:"done",status:n.status,exitCode:n.exitCode,signal:n.signal}),t.end();return}let u=null,c=!1;const e=()=>{c||(c=!0,u?.(),clearInterval(f))},f=setInterval(()=>{if(t.writableEnded||t.destroyed){e();return}try{t.write(`: ping
|
|
6
6
|
|
|
7
|
-
`)}catch{e()}},P);if(f.unref?.(),u=d.subscribeTaskOutput(r,i,{onLine:
|
|
7
|
+
`)}catch{e()}},P);if(f.unref?.(),u=d.subscribeTaskOutput(r,i,{onLine:l=>o({type:"output",stream:l.stream,text:l.text,seq:l.seq},l.seq),onDone:l=>{o({type:"done",status:l.status,exitCode:l.exitCode,signal:l.signal}),e(),t.end()}}),!u){o({type:"done",status:n.status,exitCode:n.exitCode,signal:n.signal}),e(),t.end();return}a.on("close",()=>{e(),t.writableEnded||t.end()}),a.on("aborted",()=>e());return}try{const u=await S();if(!u){o({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}const c=await u.readSession(r);if(!c){o({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}const e=c.tasks.find(C=>C.taskId===i);if(!e){o({type:"done",status:"killed",exitCode:null,signal:null}),t.end();return}const{text:f,fileExists:l,truncated:w}=await A(e.outputPath,500);o({type:"snapshot",source:"file",taskId:e.taskId,status:e.status,exitCode:e.exitCode,signal:e.signal,startedAt:e.startedAt,finishedAt:e.finishedAt,command:e.command,lines:f,nextSeq:0,totalLines:e.outputLineCount,totalBytes:e.outputBytes,truncated:w||!l}),o({type:"done",status:e.status,exitCode:e.exitCode,signal:e.signal}),t.end()}catch(u){g.error("[acode-bg-tasks] \u78C1\u76D8\u515C\u5E95 stream \u5931\u8D25: %s",u?.message),t.headersSent?t.end():t.status(500).json({ok:!1,error:"internal_error"})}})().catch(r=>{g.error("[acode-bg-tasks] stream \u5EFA\u7ACB\u5931\u8D25: %s",r?.message),t.headersSent?t.end():t.status(500).json({ok:!1,error:"internal_error"})})});export{k as backgroundTasksRouter};
|
|
8
8
|
|