aws-runtime-bridge 1.9.120 → 1.9.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/AcodeSdkAdapter.d.ts +18 -0
- package/dist/adapter/AcodeSdkAdapter.js +5 -5
- package/dist/adapter/types.d.ts +4 -0
- package/dist/adapter/types.js +1 -1
- package/dist/browser/browser-ws-bridge.js +3 -3
- package/dist/browser/built-in-browser-tools.js +1 -1
- package/dist/desktop/built-in-desktop-tools.d.ts +49 -0
- package/dist/desktop/built-in-desktop-tools.js +2 -0
- package/dist/desktop/desktop-mcp-client.d.ts +47 -0
- package/dist/desktop/desktop-mcp-client.js +3 -0
- package/dist/index.js +1 -1
- package/dist/remote-desktop/index.js +2 -2
- package/dist/routes/dashboard.js +521 -17
- package/dist/routes/git.d.ts +39 -0
- package/dist/routes/git.js +33 -25
- package/dist/routes/instance.js +1 -1
- package/dist/routes/jdks.d.ts +2 -0
- package/dist/routes/jdks.js +2 -0
- package/dist/routes/launch.d.ts +6 -0
- package/dist/routes/launch.js +30 -0
- package/dist/routes/pty.js +3 -3
- package/dist/routes/runtime-binding.js +1 -1
- package/dist/routes/terminal.js +30 -30
- package/dist/services/access-control.d.ts +18 -0
- package/dist/services/access-control.js +5 -0
- package/dist/services/agent-process-manager.js +1 -1
- package/dist/services/auto-register.js +1 -1
- package/dist/services/change-cache-retention.d.ts +5 -0
- package/dist/services/change-cache-retention.js +2 -0
- package/dist/services/desktop-mcp-manager.d.ts +47 -0
- package/dist/services/desktop-mcp-manager.js +2 -0
- package/dist/services/instance-features.d.ts +6 -0
- package/dist/services/instance-features.js +1 -1
- package/dist/services/jdk-scanner.d.ts +20 -0
- package/dist/services/jdk-scanner.js +2 -0
- package/dist/services/launch-manager.d.ts +181 -0
- package/dist/services/launch-manager.js +12 -0
- package/dist/services/launch-metrics.d.ts +89 -0
- package/dist/services/launch-metrics.js +4 -0
- package/dist/services/launch-streams.d.ts +24 -0
- package/dist/services/launch-streams.js +11 -0
- package/dist/services/mcp-launch-binding-queue.js +1 -1
- package/dist/services/panel-auth.d.ts +11 -1
- package/dist/services/panel-auth.js +1 -1
- package/dist/services/panel-env.d.ts +5 -0
- package/dist/services/panel-env.js +2 -0
- package/dist/services/port-detector.d.ts +3 -0
- package/dist/services/port-detector.js +6 -0
- package/dist/services/runtime-lifecycle-policy.d.ts +1 -0
- package/dist/services/runtime-lifecycle-policy.js +1 -1
- package/dist/services/system-metrics.js +1 -1
- package/dist/services/tunnel-client.d.ts +4 -0
- package/dist/services/tunnel-client.js +1 -1
- package/dist/services/workspace-files.d.ts +3 -0
- package/dist/services/workspace-files.js +1 -1
- package/dist/services/workspaceTaskChangeWatcher.d.ts +2 -0
- package/dist/services/workspaceTaskChangeWatcher.js +3 -3
- package/dist/utils/config-write-queue.d.ts +3 -0
- package/dist/utils/config-write-queue.js +2 -0
- package/package/acode/dist/built-in-file-tools.d.ts +2 -0
- package/package/acode/dist/runtime.d.ts +20 -0
- package/package/acode/dist/runtime.js +27 -27
- package/package/acode/dist/sub-agent-manager.d.ts +4 -0
- package/package/acode/dist/sub-agent-manager.js +1 -1
- package/package/acode/dist/sub-agent-tools.d.ts +6 -0
- package/package/acode/dist/sub-agent-tools.js +39 -10
- package/package/acode/dist/sub-agent-types.d.ts +2 -0
- package/package/acode/dist/tool-allocation.d.ts +55 -0
- package/package/acode/dist/tool-allocation.js +2 -0
- package/package/acode/dist/types.d.ts +6 -0
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +24 -0
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.test.js +19 -5
- package/package/aws-client-agent-mcp/dist/context-manager.test.js +6 -6
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/index.js +1 -1
- package/package/aws-client-agent-mcp/dist/launch-config-tools.test.d.ts +1 -0
- package/package/aws-client-agent-mcp/dist/launch-config-tools.test.js +106 -0
- package/package/aws-client-agent-mcp/dist/logger.d.ts +4 -0
- package/package/aws-client-agent-mcp/dist/logger.js +2 -2
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +21 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +25 -25
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +24 -16
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +20 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +8 -8
- package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +55 -1
- package/package/aws-client-agent-mcp/dist/memory-store.test.js +5 -1
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.d.ts +10 -0
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.js +2 -0
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.d.ts +1 -0
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.js +81 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.js +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +4 -0
- package/package/aws-client-agent-mcp/dist/watchdog-worker.d.ts +1 -0
- package/package/aws-client-agent-mcp/dist/watchdog-worker.js +3 -0
- package/package/aws-client-agent-mcp/dist/watchdog.d.ts +17 -0
- package/package/aws-client-agent-mcp/dist/watchdog.js +2 -0
- package/package/aws-client-agent-mcp/dist/watchdog.test.d.ts +1 -0
- package/package/aws-client-agent-mcp/dist/watchdog.test.js +124 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +4 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +83 -6
- package/package/aws-client-agent-mcp/package.json +1 -1
- package/package.json +2 -1
|
@@ -21,6 +21,12 @@ type ACodeRuntimeInstance = {
|
|
|
21
21
|
detachCurrentToolExecution?(): boolean;
|
|
22
22
|
|
|
23
23
|
notifyForceMessage?(): void;
|
|
24
|
+
|
|
25
|
+
addBuiltInTools?(tools: unknown[]): number;
|
|
26
|
+
|
|
27
|
+
removeBuiltInToolsByPrefix?(prefix: string): number;
|
|
28
|
+
|
|
29
|
+
requestMcpToolsRefresh?(serverName?: string): void;
|
|
24
30
|
};
|
|
25
31
|
type ACodeRuntimeConstructor = new (config: {
|
|
26
32
|
sessionId: string;
|
|
@@ -46,6 +52,10 @@ type ACodeRuntimeConstructor = new (config: {
|
|
|
46
52
|
}) => void | Promise<void>;
|
|
47
53
|
|
|
48
54
|
extraBuiltInTools?: unknown[];
|
|
55
|
+
|
|
56
|
+
subAgentSuiteTools?: unknown[];
|
|
57
|
+
|
|
58
|
+
subAgentSuiteToolProvider?: (patterns: string[]) => Promise<unknown[]>;
|
|
49
59
|
}) => ACodeRuntimeInstance;
|
|
50
60
|
export declare function toAcodeEsmImportSpecifier(modulePath: string): string;
|
|
51
61
|
|
|
@@ -74,6 +84,12 @@ export declare class AcodeSdkAdapter extends EventEmitter implements BaseProvide
|
|
|
74
84
|
|
|
75
85
|
getCapabilities(sessionId: string): RuntimeCapabilities | undefined;
|
|
76
86
|
|
|
87
|
+
private resolveAgentAiInfo;
|
|
88
|
+
|
|
89
|
+
applyAgentFeatures(agentId: string): Promise<void>;
|
|
90
|
+
|
|
91
|
+
private syncMcpToolsForAgent;
|
|
92
|
+
|
|
77
93
|
getPendingQuestion(sessionId: string): {
|
|
78
94
|
toolCallId: string;
|
|
79
95
|
questions: unknown[];
|
|
@@ -91,6 +107,8 @@ export declare class AcodeSdkAdapter extends EventEmitter implements BaseProvide
|
|
|
91
107
|
|
|
92
108
|
private resolveAcodeProvider;
|
|
93
109
|
private createRuntime;
|
|
110
|
+
|
|
111
|
+
private buildSubAgentSuiteToolProvider;
|
|
94
112
|
private loadRuntimeConstructor;
|
|
95
113
|
private requireSession;
|
|
96
114
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import{EventEmitter as q}from"node:events";import $ from"node:path";import N from"node:fs/promises";import{pathToFileURL as H}from"node:url";import{v4 as O}from"uuid";import{getAcodeConfigFile as D,getAcodeSkillsDir as K,getRuntimeHomeDir as j}from"../config.js";import{getReleasedAcodeEntryPath as Q}from"../services/acode-package.js";import{getInstanceAiConfigDebugLogger as x}from"../utils/instance-ai-config-debug-logger.js";import{redactForLog as E}from"../services/instance-ai-config-service.js";import{subAgentManager as G}from"../services/sub-agent-manager-instance.js";import{agentPtyManager as J}from"../services/agent-pty-manager.js";import{resolveAcodeProviderConfig as z}from"./instance-ai-config-resolver.js";import{recordAgentFileChange as Y}from"../services/workspaceTaskChangeWatcher.js";import{getBrowserTools as X,closeBrowserSession as V,registerListenerWakeHook as Z,unregisterListenerWakeHook as M}from"../services/browser-session-manager.js";import{isMessagePollingIdleCommand as P,isPollMessageToolDescriptor as tt}from"./acode-tool-names.js";import{IdlePollLoop as et}from"./idle-poll-loop.js";import{getCanonicalMcpDetail as I,getToolActionInfo as ot}from"./types.js";const st=process.env.ACODE_COMPACTION_DEBUG==="1";function l(...r){st&&console.log(...r)}const it=process.env.ACODE_TOKEN_DEBUG==="1";function w(...r){it&&console.log("[TokenDebug][Adapter]",...r)}const U=["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(" "),at=5e3,nt=4e4,rt=5*6e4,ut=250,lt=2e3,dt=12e4,R=3e3,ct="\u7CFB\u7EDF\u63D0\u9192\uFF1A\u5982\u679C\u4EFB\u52A1\u5DF2\u5B8C\u6210\u6216\u6CA1\u6709\u5176\u5B83\u8981\u505A\u7684\uFF0C\u5148\u8C03\u7528 poll_message \u5DE5\u5177\u963B\u585E\u7B49\u5F85\u65B0\u6D88\u606F\u548C\u6D41\u7A0B\u7EBF\u4EFB\u52A1\u63D0\u9192\uFF1B\u6536\u5230\u6D41\u7A0B\u4EFB\u52A1\u63D0\u9192\u540E\u8C03\u7528 my_task \u67E5\u770B\u5DF2\u7531\u540E\u53F0\u9759\u9ED8\u62A2\u5360\u7684\u4EFB\u52A1\u8BE6\u60C5\u5E76\u76F4\u63A5\u5904\u7406\uFF1B\u5B8C\u6210\u4EFB\u52A1\u540E\u8C03\u7528 submit_task_result \u63D0\u4EA4\u7ED3\u679C\uFF0C\u7136\u540E\u7EE7\u7EED poll_message\u3002 \u8C03\u7528 submit_task_result \u65F6\u8BF7\u4E00\u5E76\u4F20\u5165 changeLog\uFF08\u672C\u6B21\u5904\u7406\u7684\u6587\u4EF6/\u5185\u5BB9\u53D8\u66F4\u65E5\u5FD7\uFF0C\u5305\u542B operation \u4E0E path\uFF09\uFF0C\u7528\u4E8E AI \u56DE\u9000\u3002";function mt(){return $.resolve(Q())}function pt(r){return H($.resolve(r)).href}const F=128e3,y={"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 gt(r){if(!r)return F;const t=r.toLowerCase().trim();if(y[t])return y[t].contextLimit;const e=Object.keys(y).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?y[e[0]].contextLimit:F}const v=4096;function ft(r){if(!r)return v;const t=r.toLowerCase().trim();if(y[t])return y[t].maxOutput;const e=Object.keys(y).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?y[e[0]].maxOutput:v}function ht(r){if(!r||typeof r!="object")return;const t=r,e=f(t.inputTokens,t.input_tokens,t.promptTokens,t.prompt_tokens),o=f(t.outputTokens,t.output_tokens,t.completionTokens,t.completion_tokens);if(e===0&&o===0)return;let s=f(t.cacheHitTokens,t.promptCacheHitTokens,t.prompt_cache_hit_tokens),a=f(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=f(t.cache_read_input_tokens),u=f(t.cache_creation_input_tokens);n>0||u>0?(s=n,a=u+f(t.input_tokens),i=!0):(s=f(t.cache_hit_tokens),a=f(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=f(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 f(...r){for(const t of r){const e=Number(t);if(Number.isFinite(e)&&e>0)return e}return 0}function B(r){return f(r.estimatedContextTokens,r.contextTokens,r.context_tokens,r.contextUsedTokens,r.context_used_tokens)}function Tt(r){return r==="stderr"||r==="mixed"?r:"stdout"}function _t(r){if(!r||typeof r!="object"||Array.isArray(r))return;const t={};for(const[e,o]of Object.entries(r))typeof o=="string"&&(t[e]=o);return Object.keys(t).length>0?t:void 0}function St(r){return r.failed===!0}function W(r,t,e){if(!r)return e?`${t}/${e}`:t;const o=`${t}__`;return r.startsWith(o)?I(`${t}__${r.slice(o.length)}`):I(r.replace("/","__"))}function At(r){return r==="read_file"||r==="edit_file"||r==="write_file"||r==="bash"}function kt(r){const t=Ct(r);return t?[t.directMessages,t.groupMessages,t.messages].some(e=>Array.isArray(e)&&e.length>0):typeof r=="string"&&r.trim().length>0}function Ct(r){if(r&&typeof r=="object"&&!Array.isArray(r))return r;if(!(typeof r!="string"||!r.trim()))try{const t=JSON.parse(r);return t&&typeof t=="object"&&!Array.isArray(t)?t:void 0}catch{return}}class Wt extends q{constructor(){super(...arguments),this.providerId="acode",this.displayName="ACode",this.sessions=new Map,this.listenerWakeHooks=new Map}async startSession(t,e){const o=j(),s=e.mcpConfigPath||D(o),{resolved:a,configContextLimit:i}=await this.resolveAcodeProvider(e,s),n=a.model,u=i??gt(n),m=ft(n),d={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0,contextLimit:u,maxOutput:m,cacheHitTokens:0,cacheMissTokens:0}};this.sessions.set(t,{adapterSession:d,config:e,runtime:await this.createRuntime(t,n,e,o),autoPollActive:!1}),this.emit("status-change",t,"starting");const c=this.sessions.get(t);if(!c)throw new Error("ACode session was not initialized");const g=()=>{const p=this.sessions.get(t);p&&this.getIdlePollLoop(p).start()};this.listenerWakeHooks.set(t,g),Z(e.agentId,g);try{await c.runtime.start(),d.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:c.adapterSession.totalUsage}),e.initialPrompt?.trim()?this.startInitialPromptTurn(t,e.initialPrompt):this.armIdleLoopOrAutoPoll(t)}catch(p){d.status="error",this.emitRuntimeErrorEvent(t,p),this.emit("status-change",t,"error");const T=this.sessions.get(t);throw T?.capabilitiesFallbackTimer&&clearTimeout(T.capabilitiesFallbackTimer),this.sessions.delete(t),M(e.agentId,this.listenerWakeHooks.get(t)),this.listenerWakeHooks.delete(t),p}}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:O(),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 u=Date.now()-n;this.cancelSubmitTimeoutDetection(s),l(`[ACode] \u2705 runtime.submit() completed in ${u}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 m=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 ${m}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 u=Date.now()-o;console.error(`[ACode] \u274C sendMessage FAILED after ${u}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){}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&&(M(o,this.listenerWakeHooks.get(t)),V(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}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&&P(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 N.readFile(t,"utf-8"),o=JSON.parse(e),a=o.provider?.default,i=a?.options,n=f(o.contextLimit),u={},m=a?.models;if(m&&typeof m=="object"){for(const[d,c]of Object.entries(m))if(c&&typeof c=="object"){const g=f(c.contextLimit);g>0&&(u[d.trim()]=g)}}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(u).length>0?{modelContextLimits:u}:{}}}catch(e){const o=e instanceof Error?e.message:String(e);return x().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=x();s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u8F93\u5165",source:"bridge",details:{configModel:t.model??null,configInstanceAiConfig:t.instanceAiConfig?E(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 m=o||t.instanceAiConfig?.model||i.model;m&&i.modelContextLimits[m]&&(n=i.modelContextLimits[m])}const u=z(a,i,"acode-default");return s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u89E3\u6790\u7ED3\u679C",source:"bridge",details:{instanceConfig:E(a),globalConfigModel:i?.model??null,resolvedModel:u.model,resolvedBaseURL:u.baseURL??null,resolvedApiKey:u.apiKey?"[set]":null,configContextLimit:n,resolvedSource:u.source}}),{resolved:u,configPath:e,perSessionModel:o,configContextLimit:n}}async createRuntime(t,e,o,s){const a=await this.loadRuntimeConstructor(),i=o.mcpConfigPath||D(s);let n=[];try{n=await X(o.agentId,o.scopeKey,typeof o.browserAllowPrivate=="boolean"?{allowPrivate:o.browserAllowPrivate}:void 0)}catch(d){l(`[acode-adapter] browser tools injection skipped for agent ${o.agentId}:`,d.message)}const u=new a({sessionId:t,agentId:o.agentId,workingDirectory:o.workingDirectory,configPath:i,skillsDir:K(s),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:G,terminalManager:J,...o.subAgentModel?{subAgentModel:o.subAgentModel}:{},...o.subAgentEffort?{subAgentEffort:o.subAgentEffort}:{},...o.instanceAiConfig?.effort?{effort:o.instanceAiConfig.effort}:{},onFileChange:d=>Y({...d,agentId:o.agentId,workspacePath:o.workingDirectory}),extraBuiltInTools:n});return x().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:i}}),u.on("event",d=>this.forwardAcodeEvent(d)),u}async loadRuntimeConstructor(){if(this.runtimeConstructor)return this.runtimeConstructor;const t=mt(),e=await import(pt(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(t.type==="assistant_delta"){const o=typeof t.data.text=="string"?t.data.text:"";e.autoPollActive&&(e.autoPollObservedAssistantText=!0),e.adapterSession.messages.push({id:O(),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:Tt(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==="ai_request"){const o=B(t.data),s=t.data.lastUsage,a=f(s?.inputTokens),i=f(s?.outputTokens),n=f(s?.cacheHitTokens),u=f(s?.cacheMissTokens),m=!!(s&&(a>0||i>0));let d;if(m&&(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=u),d={...e.adapterSession.totalUsage},w(`ai_request lastUsage applied: ${JSON.stringify(s)} -> totalUsage=${JSON.stringify(e.adapterSession.totalUsage)}`)),!m&&o>0){const p=e.adapterSession.totalUsage.inputTokens;p>0?e.adapterSession.totalUsage.inputTokens=Math.min(o,Math.floor(p*1.5)):e.adapterSession.totalUsage.inputTokens=o,d={...e.adapterSession.totalUsage}}e.adapterSession.status==="compacting"&&(e.adapterSession.status="thinking"),e.adapterSession.status==="waiting_input"&&(e.adapterSession.status="thinking");const g={actionType:"ai_request",actionLabel:"AI\u8BF7\u6C42",actionDetail:this.formatAiRequestActionDetail(t.data)};d&&(g.usage=d),w(`ai_request handled: estimatedContextTokens=${t.data.estimatedContextTokens??"n/a"} totalUsage=${JSON.stringify(e.adapterSession.totalUsage)} displayUsage=${JSON.stringify(d??null)}`),this.emit("status-change",t.sessionId,e.adapterSession.status,g),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:_t(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=ht(t.data.usage);if(o){const a=e.adapterSession.totalUsage.inputTokens,i=a>0&&o.inputTokens<Math.floor(a*.2)&&!o.hasCache;w(`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})`)),w(`totalUsage after turn_complete: ${JSON.stringify(e.adapterSession.totalUsage)}`)}else w(`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}`),St(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}),w(`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==="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 T=t.data.tools;T.some(h=>tt(h))||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 _=Array.isArray(t.data.servers)?t.data.servers:[],A=T.map(h=>({name:String(h.name??""),source:String(h.source??""),description:String(h.description??"")})).filter(h=>h.name),k={kind:"mcp",mcpServers:_,mcpTools:A};e.cachedMcpCapabilities=k,e.lastMcpCapabilities=k,e.lastCombinedCapabilities={kind:"combined",mcpServers:_,mcpTools:A,skills:e.lastCombinedCapabilities?.skills??[],skillsDir:e.lastCombinedCapabilities?.skillsDir,skillGroups:e.lastCombinedCapabilities?.skillGroups??[]},l(`[cap-diag] MCP capabilities cached: ${_.length} servers, ${A.length} tools`,{sessionId:t.sessionId,servers:_,toolNames:A.map(h=>h.name)}),e.capabilitiesFallbackTimer&&clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=setTimeout(()=>{const h=e.cachedMcpCapabilities;if(!h)return;const b={...h,skills:[],skillGroups:[]};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:b,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(b)}),l(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${R}ms)`,{sessionId:t.sessionId,mcpServers:h.mcpServers,mcpToolsCount:h.mcpTools?.length??0}),e.cachedMcpCapabilities=void 0,e.capabilitiesFallbackTimer=void 0},R);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,u=ot(i,n);e.autoPollActive&&(e.autoPollObservedAnyTool=!0,u.actionType==="idle"&&(e.autoPollObservedPollingTool=!0));const m=At(u.actionType)?u.actionDetail||W(void 0,s,a):W(u.actionDetail,s,a),d=typeof t.data.toolUseId=="string"?t.data.toolUseId:void 0,c=n?JSON.stringify(n,null,2):void 0,g={toolName:m,rawToolName:i,actionType:u.actionType,actionLabel:u.actionLabel,actionDetail:m,...c?{actionInput:c}:{},...n?{toolInput:n}:{},...d?{toolUseId:d,actionId:d}:{},metadata:{mcpServer:s}};if(t.data.action==="tool_result"){this.emitProviderEvent(t,"tool_use_end",{...g,toolResult:typeof t.data.result=="string"?t.data.result:JSON.stringify(t.data.result??"")});const T=u.actionType==="idle"&&!kt(t.data.result)?"waiting_input":"thinking";e.adapterSession.status=T,this.cancelDataFlowWatchdog(e),e.pendingToolEventData=void 0;const C=u.actionType==="idle";this.emit("status-change",t.sessionId,T,C||T==="thinking"?void 0:g);return}const p=u.actionType==="idle"?"waiting_input":"tool_using";e.adapterSession.status=p,p==="tool_using"&&(this.resetDataFlowWatchdog(t.sessionId,e),e.pendingToolEventData={...g}),this.emitProviderEvent(t,"tool_use_start",g),this.emit("status-change",t.sessionId,p,g);return}if(t.type==="skill_status"){const s=(Array.isArray(t.data.skills)?t.data.skills:[]).map(c=>({name:String(c.name??""),description:typeof c.description=="string"?c.description:void 0,directory:typeof c.directory=="string"?c.directory:void 0})).filter(c=>c.name),a=typeof t.data.skillsDir=="string"?t.data.skillsDir:void 0,n=(Array.isArray(t.data.skillGroups)?t.data.skillGroups:[]).map(c=>{const g=Array.isArray(c.skills)?c.skills:[];return{dir:typeof c.dir=="string"?c.dir:"",exists:c.exists===!0,skills:g.map(p=>({name:String(p.name??""),description:typeof p.description=="string"?p.description:void 0,directory:typeof p.directory=="string"?p.directory:void 0})).filter(p=>p.name)}}).filter(c=>c.dir),u=e.cachedMcpCapabilities??e.lastMcpCapabilities,m=e.cachedMcpCapabilities!==void 0,d={kind:"combined",mcpServers:u?.mcpServers??[],mcpTools:u?.mcpTools??[],skills:s,skillsDir:a,skillGroups:n};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:d,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(d)}),e.lastCombinedCapabilities=d,l(`[cap-diag] Combined capabilities emitted ${m?"(startup merge)":"(runtime refresh)"}: ${(u?.mcpTools??[]).length} MCP tools, ${s.length} skills`,{sessionId:t.sessionId,mcpServers:u?.mcpServers??[],skillsDir:a}),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),e.cachedMcpCapabilities=void 0;return}if(t.type==="session_started"){const o=B(t.data),s=f(t.data.contextLimit)||void 0,a=f(t.data.maxOutput)||void 0,i=f(t.data.inputLimit)||void 0,n=f(t.data.compactionThreshold)||void 0;let u=!1;o>0&&(e.adapterSession.totalUsage.inputTokens=o,e.adapterSession.totalUsage.outputTokens=0,u=!0),s!==void 0&&(e.adapterSession.totalUsage.contextLimit=s,u=!0),a!==void 0&&(e.adapterSession.totalUsage.maxOutput=a,u=!0),i!==void 0&&(e.adapterSession.totalUsage.inputLimit=i,u=!0),n!==void 0&&(e.adapterSession.totalUsage.compactionThreshold=n,u=!0),u&&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",u=o.parentId,m=o.compactionId;let d,c;if(a){const p=String(o.sizeKB??o.sizeBeforeKB??"?"),T=String(o.messageCount??o.messageCountBefore??"?"),C=o.estimatedTokens,_=o.actualInputTokens?`input=${o.actualInputTokens}`:"",A=o.actualOutputTokens?`output=${o.actualOutputTokens}`:"",k=o.trigger?`, \u89E6\u53D1: ${o.trigger}`:"";d="\u4E0A\u4E0B\u6587\u538B\u7F29\u5F00\u59CB",C!==void 0?c=`\u538B\u7F29\u524D: ~${C} tokens, \u6D88\u606F: ${T}\u6761`+(_?`, \u5B9E\u9645 tokens: ${_} ${A}`:"")+k:c=`\u538B\u7F29\u524D: ${p}KB, \u6D88\u606F: ${T}\u6761`+(_?`, \u5B9E\u9645 tokens: ${_} ${A}`:"")+k}else if(n)d=String(o.phaseLabel??"\u538B\u7F29\u9636\u6BB5"),c=String(o.phaseDetail??"");else{const p=o.tokensBefore,T=o.tokensAfter,C=o.freedTokens,_=String(o.sizeBeforeKB??"?"),A=String(o.sizeAfterKB??"?"),k=String(o.freedKB??"?"),h=String(o.strategy??"?"),b=Number(o.prunedToolMessages??0),S=Number(o.summarizedMessages??0);d="\u538B\u7F29\u5B8C\u6210",p!==void 0&&T!==void 0?c=`\u91CA\u653E: ${C??p-T} tokens, ${p} tokens \u2192 ${T} tokens, \u7B56\u7565: ${h}`+(b?`, \u88C1\u526A\u5DE5\u5177: ${b}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:""):c=`\u91CA\u653E: ${k}KB, ${_}KB \u2192 ${A}KB, \u7B56\u7565: ${h}`+(b?`, \u88C1\u526A\u5DE5\u5177: ${b}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:"")}a&&(e.adapterSession.status="compacting");const g={actionType:"context_compaction",actionLabel:d,actionDetail:c};if(a&&m)g.actionId=m;else if((n||i)&&m)if(g.parentId=m,n){const p=String(o.phase??"unknown");g.actionId=`${m}-phase-${p}`}else g.actionId=`${m}-end`;n&&u&&!g.parentId&&(g.parentId=u),i&&(e.adapterSession.totalUsage.inputTokens=f(o.tokensAfter),e.adapterSession.totalUsage.outputTokens=0,g.usage={...e.adapterSession.totalUsage}),this.emitProviderEvent(t,"context_compaction",g),this.emit("status-change",t.sessionId,"compacting",g);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,o=t.config.agentId;return t.idlePollLoop=new et({getStatus:()=>this.sessions.get(e)?.adapterSession.status,getCommand:()=>this.sessions.get(e)?.idleCommands?.idleInputCommand,isPollingCommand:s=>P(s),preDispatchHook:()=>null,dispatchPollingPrompt:(s,a)=>this.submitAutoPollPrompt(e,a?s:ct,{keepWaitingStatus:!0}),dispatchRegularIdleCommand:s=>this.submitAutoPollPrompt(e,s)}),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,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=ut){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,U,{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.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&&P(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,U,{keepWaitingStatus:!0})},lt)}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&&P(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"){s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(a==="tool_using"){const m=i-(s.watchdogLastDataFlowTime||i);if(m>=rt){const d=String(s.pendingToolEventData?.rawToolName??"");if(d.endsWith("await_complete")){console.warn(`[ACode] tool_using elapsed ${Math.round(m/1e3)}s but await_complete is running (session: ${t}, tool: ${d}), 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(m/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(m/1e3)}s): detached tool ${d} 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",m);return}s.watchdogSubmitStartTime=i;return}if(a!=="thinking")return;const n=i-(s.watchdogSubmitStartTime||i),u=i-(s.watchdogLastDataFlowTime||i);if(u>=nt){console.warn(`[ACode] CRITICAL: Data flow watchdog triggered after ${Math.round(u/1e3)}s (source: ${o}, session: ${t}, status: ${a}). No stream data received. Forcing recovery via recoverTurnFailure.`),this.executeWatchdogRecovery(t,s,o,"data_flow",u);return}if(n>=dt){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}},at)}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()}startDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now()}cancelDataFlowWatchdog(t){t.watchdogLastDataFlowTime=Date.now()}formatAiRequestActionDetail(t){const e=`
|
|
1
|
+
import{EventEmitter as Y}from"node:events";import I from"node:path";import X from"node:fs/promises";import{pathToFileURL as V}from"node:url";import{v4 as U}from"uuid";import{getAcodeConfigFile as $,getAcodeSkillsDir as Z,getRuntimeHomeDir as R}from"../config.js";import{getReleasedAcodeEntryPath as tt}from"../services/acode-package.js";import{getInstanceAiConfigDebugLogger as x}from"../utils/instance-ai-config-debug-logger.js";import{redactForLog as v}from"../services/instance-ai-config-service.js";import{subAgentManager as et}from"../services/sub-agent-manager-instance.js";import{agentPtyManager as ot}from"../services/agent-pty-manager.js";import{resolveAcodeProviderConfig as st}from"./instance-ai-config-resolver.js";import{recordAgentFileChange as it}from"../services/workspaceTaskChangeWatcher.js";import{getBrowserTools as F,closeBrowserSession as at,registerListenerWakeHook as nt,unregisterListenerWakeHook as B}from"../services/browser-session-manager.js";import{getDesktopTools as O,getDesktopToolsIfReady as rt}from"../services/desktop-mcp-manager.js";import{BROWSER_FEATURE_KEY as W,DESKTOP_FEATURE_KEY as D,isFeatureEnabled as P}from"../services/instance-features.js";import{isMessagePollingIdleCommand as L,isPollMessageToolDescriptor as ut}from"./acode-tool-names.js";import{IdlePollLoop as lt}from"./idle-poll-loop.js";import{getCanonicalMcpDetail as q,getToolActionInfo as dt}from"./types.js";const ct=process.env.ACODE_COMPACTION_DEBUG==="1";function l(...u){ct&&console.log(...u)}const mt=process.env.ACODE_TOKEN_DEBUG==="1";function w(...u){mt&&console.log("[TokenDebug][Adapter]",...u)}const N=["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(" "),pt=5e3;function E(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 H=E("ACODE_DATA_FLOW_WATCHDOG_MS",12e4),gt=5*6e4,ft=E("ACODE_STUCK_AUTOPOLL_TIMEOUT_MS",5*6e4),ht=250,Tt=2e3,K=E("ACODE_SUBMIT_TIMEOUT_MS",12e4),j=3e3,_t="\u7CFB\u7EDF\u63D0\u9192\uFF1A\u5982\u679C\u4EFB\u52A1\u5DF2\u5B8C\u6210\u6216\u6CA1\u6709\u5176\u5B83\u8981\u505A\u7684\uFF0C\u5148\u8C03\u7528 poll_message \u5DE5\u5177\u963B\u585E\u7B49\u5F85\u65B0\u6D88\u606F\u548C\u6D41\u7A0B\u7EBF\u4EFB\u52A1\u63D0\u9192\uFF1B\u6536\u5230\u6D41\u7A0B\u4EFB\u52A1\u63D0\u9192\u540E\u8C03\u7528 my_task \u67E5\u770B\u5DF2\u7531\u540E\u53F0\u9759\u9ED8\u62A2\u5360\u7684\u4EFB\u52A1\u8BE6\u60C5\u5E76\u76F4\u63A5\u5904\u7406\uFF1B\u5B8C\u6210\u4EFB\u52A1\u540E\u8C03\u7528 submit_task_result \u63D0\u4EA4\u7ED3\u679C\uFF0C\u7136\u540E\u7EE7\u7EED poll_message\u3002 \u8C03\u7528 submit_task_result \u65F6\u8BF7\u4E00\u5E76\u4F20\u5165 changeLog\uFF08\u672C\u6B21\u5904\u7406\u7684\u6587\u4EF6/\u5185\u5BB9\u53D8\u66F4\u65E5\u5FD7\uFF0C\u5305\u542B operation \u4E0E path\uFF09\uFF0C\u7528\u4E8E AI \u56DE\u9000\u3002";function At(){return I.resolve(tt())}function St(u){return V(I.resolve(u)).href}const Q=128e3,y={"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 kt(u){if(!u)return Q;const t=u.toLowerCase().trim();if(y[t])return y[t].contextLimit;const e=Object.keys(y).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?y[e[0]].contextLimit:Q}const G=4096;function bt(u){if(!u)return G;const t=u.toLowerCase().trim();if(y[t])return y[t].maxOutput;const e=Object.keys(y).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?y[e[0]].maxOutput:G}function Ct(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 J(u){return h(u.estimatedContextTokens,u.contextTokens,u.context_tokens,u.contextUsedTokens,u.context_used_tokens)}function yt(u){return u==="stderr"||u==="mixed"?u:"stdout"}function wt(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 Pt(u){return u.failed===!0}function z(u,t,e){if(!u)return e?`${t}/${e}`:t;const o=`${t}__`;return u.startsWith(o)?q(`${t}__${u.slice(o.length)}`):q(u.replace("/","__"))}function Lt(u){return u==="read_file"||u==="edit_file"||u==="write_file"||u==="bash"}function $t(u){const t=xt(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 xt(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 zt extends Y{constructor(){super(...arguments),this.providerId="acode",this.displayName="ACode",this.sessions=new Map,this.listenerWakeHooks=new Map}async startSession(t,e){const o=R(),s=e.mcpConfigPath||$(o),{resolved:a,configContextLimit:i,supportsImage:n}=await this.resolveAcodeProvider(e,s),r=a.model,c=i??kt(r),p=bt(r),m={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0,contextLimit:c,maxOutput:p,cacheHitTokens:0,cacheMissTokens:0}};this.sessions.set(t,{adapterSession:m,config:e,runtime:await this.createRuntime(t,r,e,o,a,n),autoPollActive:!1}),this.emit("status-change",t,"starting");const g=this.sessions.get(t);if(!g)throw new Error("ACode session was not initialized");const d=()=>{const f=this.sessions.get(t);f&&this.getIdlePollLoop(f).start()};this.listenerWakeHooks.set(t,d),nt(e.agentId,d);try{await g.runtime.start(),m.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:g.adapterSession.totalUsage}),e.initialPrompt?.trim()?this.startInitialPromptTurn(t,e.initialPrompt):this.armIdleLoopOrAutoPoll(t)}catch(f){m.status="error",this.emitRuntimeErrorEvent(t,f),this.emit("status-change",t,"error");const _=this.sessions.get(t);throw _?.capabilitiesFallbackTimer&&clearTimeout(_.capabilitiesFallbackTimer),this.sessions.delete(t),B(e.agentId,this.listenerWakeHooks.get(t)),this.listenerWakeHooks.delete(t),f}}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:U(),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 c=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 ${c}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){}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&&(B(o,this.listenerWakeHooks.get(t)),at(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(n=>n.config.agentId===t);if(!e)return;const o=R(),s=e.config.mcpConfigPath||$(o),{resolved:a,supportsImage:i}=await this.resolveAcodeProvider(e.config,s);return a?.model?{model:a.model,supportsImage:i===!0,...a.baseURL?{baseURL:a.baseURL}:{},...a.apiKey?{apiKey:a.apiKey}:{}}:void 0}catch{return}}async applyAgentFeatures(t){if(!t)return;const e=P(t,D),o=P(t,W);for(const[s,a]of this.sessions){if(a.config.agentId!==t)continue;const i=a.runtime;try{if(e){const n=await O(t,await this.resolveAgentAiInfo(t)),r=i.addBuiltInTools?.(n)??0;r>0&&l(`[acode-adapter] session ${s}: ${r} desktop tools hot-added for agent ${t}`)}else{const n=i.removeBuiltInToolsByPrefix?.("desktop_")??0;n>0&&l(`[acode-adapter] session ${s}: ${n} desktop tools hot-removed for agent ${t}`)}}catch(n){l(`[acode-adapter] session ${s}: desktop feature hot-sync failed for agent ${t}:`,n.message)}if(o)try{const n=await F(t,a.config.scopeKey,typeof a.config.browserAllowPrivate=="boolean"?{allowPrivate:a.config.browserAllowPrivate}:void 0),r=i.addBuiltInTools?.(n)??0;r>0&&l(`[acode-adapter] session ${s}: ${r} browser tools hot-added for agent ${t}`)}catch(n){l(`[acode-adapter] session ${s}: browser feature hot-sync failed for agent ${t}:`,n.message)}}this.syncMcpToolsForAgent(t)}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&&L(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 X.readFile(t,"utf-8"),o=JSON.parse(e),a=o.provider?.default,i=a?.options,n=h(o.contextLimit),r={},c={},p=a?.models;if(p&&typeof p=="object"){for(const[m,g]of Object.entries(p))if(g&&typeof g=="object"){const d=h(g.contextLimit);d>0&&(r[m.trim()]=d);const f=g.modalities?.input,_=Array.isArray(f)&&f.includes("image")||g.supportsImageInput===!0;c[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(c).length>0?{modelImageSupport:c}:{}}}catch(e){const o=e instanceof Error?e.message:String(e);return x().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=x();s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u8F93\u5165",source:"bridge",details:{configModel:t.model??null,configInstanceAiConfig:t.instanceAiConfig?v(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 c=o||t.instanceAiConfig?.model||i.model;c&&i.modelContextLimits[c]&&(n=i.modelContextLimits[c])}const r=st(a,i,"acode-default");return s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u89E3\u6790\u7ED3\u679C",source:"bridge",details:{instanceConfig:v(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,i){const n=await this.loadRuntimeConstructor(),r=o.mcpConfigPath||$(s);let c=[];try{c=await F(o.agentId,o.scopeKey,typeof o.browserAllowPrivate=="boolean"?{allowPrivate:o.browserAllowPrivate}:void 0)}catch(d){l(`[acode-adapter] browser tools injection skipped for agent ${o.agentId}:`,d.message)}let p=[];if(P(o.agentId??"",W)&&(p=c),o.agentId&&P(o.agentId,D))try{const d=a?.model?{model:a.model,supportsImage:i===!0,...a.baseURL?{baseURL:a.baseURL}:{},...a.apiKey?{apiKey:a.apiKey}:{}}:void 0,f=await O(o.agentId,d);f.length>0&&(p=[...p,...f])}catch(d){l(`[acode-adapter] desktop tools injection skipped for agent ${o.agentId}:`,d.message)}const m=new n({sessionId:t,agentId:o.agentId,workingDirectory:o.workingDirectory,configPath:r,skillsDir:Z(s),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:et,terminalManager:ot,...o.subAgentModel?{subAgentModel:o.subAgentModel}:{},...o.subAgentEffort?{subAgentEffort:o.subAgentEffort}:{},...o.instanceAiConfig?.effort?{effort:o.instanceAiConfig.effort}:{},onFileChange:d=>it({...d,agentId:o.agentId,workspacePath:o.workingDirectory}),extraBuiltInTools:p,subAgentSuiteTools:c,subAgentSuiteToolProvider:this.buildSubAgentSuiteToolProvider(o.agentId)});return x().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:r}}),m.on("event",d=>this.forwardAcodeEvent(d)),m}buildSubAgentSuiteToolProvider(t){return async e=>{if(!t)return[];if(!P(t,D))return[];if(e.length===0)return rt();if(!e.some(a=>{const i=a.trim().replace(/[.*]+$/,"");return i==="desktop"||i.startsWith("desktop.")}))return[];const s=await this.resolveAgentAiInfo(t);return O(t,s)}}async loadRuntimeConstructor(){if(this.runtimeConstructor)return this.runtimeConstructor;const t=At(),e=await import(St(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:U(),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:yt(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=J(t.data),s=t.data.lastUsage,a=h(s?.inputTokens),i=h(s?.outputTokens),n=h(s?.cacheHitTokens),r=h(s?.cacheMissTokens),c=!!(s&&(a>0||i>0));let p;if(c&&(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},w(`ai_request lastUsage applied: ${JSON.stringify(s)} -> totalUsage=${JSON.stringify(e.adapterSession.totalUsage)}`)),!c&&o>0){const d=e.adapterSession.totalUsage.inputTokens;d>0?e.adapterSession.totalUsage.inputTokens=Math.min(o,Math.floor(d*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 g={actionType:"ai_request",actionLabel:"AI\u8BF7\u6C42",actionDetail:this.formatAiRequestActionDetail(t.data)};p&&(g.usage=p),w(`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,g),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:wt(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=Ct(t.data.usage);if(o){const a=e.adapterSession.totalUsage.inputTokens,i=a>0&&o.inputTokens<Math.floor(a*.2)&&!o.hasCache;w(`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})`)),w(`totalUsage after turn_complete: ${JSON.stringify(e.adapterSession.totalUsage)}`)}else w(`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}`),Pt(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}),w(`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==="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=>ut(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:[],k=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:k};e.cachedMcpCapabilities=b,e.lastMcpCapabilities=b,e.lastCombinedCapabilities={kind:"combined",mcpServers:A,mcpTools:k,skills:e.lastCombinedCapabilities?.skills??[],skillsDir:e.lastCombinedCapabilities?.skillsDir,skillGroups:e.lastCombinedCapabilities?.skillGroups??[]},l(`[cap-diag] MCP capabilities cached: ${A.length} servers, ${k.length} tools`,{sessionId:t.sessionId,servers:A,toolNames:k.map(T=>T.name)}),e.capabilitiesFallbackTimer&&clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=setTimeout(()=>{const T=e.cachedMcpCapabilities;if(!T)return;const C={...T,skills:[],skillGroups:[]};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:C,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(C)}),l(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${j}ms)`,{sessionId:t.sessionId,mcpServers:T.mcpServers,mcpToolsCount:T.mcpTools?.length??0}),e.cachedMcpCapabilities=void 0,e.capabilitiesFallbackTimer=void 0},j);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=dt(i,n);e.autoPollActive&&(e.autoPollObservedAnyTool=!0,r.actionType==="idle"&&(e.autoPollObservedPollingTool=!0));const c=Lt(r.actionType)?r.actionDetail||z(void 0,s,a):z(r.actionDetail,s,a),p=typeof t.data.toolUseId=="string"?t.data.toolUseId:void 0,m=n?JSON.stringify(n,null,2):void 0,g={toolName:c,rawToolName:i,actionType:r.actionType,actionLabel:r.actionLabel,actionDetail:c,...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",{...g,toolResult:typeof t.data.result=="string"?t.data.result:JSON.stringify(t.data.result??"")});const f=r.actionType==="idle"&&!$t(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:g);return}const d=r.actionType==="idle"?"waiting_input":"tool_using";e.adapterSession.status=d,r.actionType==="idle"&&(e.idleToolActiveAt=Date.now()),d==="tool_using"&&(this.resetDataFlowWatchdog(t.sessionId,e),e.pendingToolEventData={...g}),this.emitProviderEvent(t,"tool_use_start",g),this.emit("status-change",t.sessionId,d,g);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 g=Array.isArray(m.skills)?m.skills:[];return{dir:typeof m.dir=="string"?m.dir:"",exists:m.exists===!0,skills:g.map(d=>({name:String(d.name??""),description:typeof d.description=="string"?d.description:void 0,directory:typeof d.directory=="string"?d.directory:void 0})).filter(d=>d.name)}}).filter(m=>m.dir),r=e.cachedMcpCapabilities??e.lastMcpCapabilities,c=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 ${c?"(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=J(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,c=o.compactionId;let p,m;if(a){const d=String(o.sizeKB??o.sizeBeforeKB??"?"),f=String(o.messageCount??o.messageCountBefore??"?"),_=o.estimatedTokens,A=o.actualInputTokens?`input=${o.actualInputTokens}`:"",k=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} ${k}`:"")+b:m=`\u538B\u7F29\u524D: ${d}KB, \u6D88\u606F: ${f}\u6761`+(A?`, \u5B9E\u9645 tokens: ${A} ${k}`:"")+b}else if(n)p=String(o.phaseLabel??"\u538B\u7F29\u9636\u6BB5"),m=String(o.phaseDetail??"");else{const d=o.tokensBefore,f=o.tokensAfter,_=o.freedTokens,A=String(o.sizeBeforeKB??"?"),k=String(o.sizeAfterKB??"?"),b=String(o.freedKB??"?"),T=String(o.strategy??"?"),C=Number(o.prunedToolMessages??0),S=Number(o.summarizedMessages??0);p="\u538B\u7F29\u5B8C\u6210",d!==void 0&&f!==void 0?m=`\u91CA\u653E: ${_??d-f} tokens, ${d} tokens \u2192 ${f} tokens, \u7B56\u7565: ${T}`+(C?`, \u88C1\u526A\u5DE5\u5177: ${C}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:""):m=`\u91CA\u653E: ${b}KB, ${A}KB \u2192 ${k}KB, \u7B56\u7565: ${T}`+(C?`, \u88C1\u526A\u5DE5\u5177: ${C}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:"")}a&&(e.adapterSession.status="compacting");const g={actionType:"context_compaction",actionLabel:p,actionDetail:m};if(a&&c)g.actionId=c;else if((n||i)&&c)if(g.parentId=c,n){const d=String(o.phase??"unknown");g.actionId=`${c}-phase-${d}`}else g.actionId=`${c}-end`;n&&r&&!g.parentId&&(g.parentId=r),i&&(e.adapterSession.totalUsage.inputTokens=h(o.tokensAfter),e.adapterSession.totalUsage.outputTokens=0,g.usage={...e.adapterSession.totalUsage}),this.emitProviderEvent(t,"context_compaction",g),this.emit("status-change",t.sessionId,"compacting",g);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 lt({getStatus:()=>this.sessions.get(e)?.adapterSession.status,getCommand:()=>this.sessions.get(e)?.idleCommands?.idleInputCommand,isPollingCommand:o=>L(o),preDispatchHook:()=>null,dispatchPollingPrompt:(o,s)=>this.submitAutoPollPrompt(e,s?o:_t,{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=ht){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,N,{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.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&&L(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,N,{keepWaitingStatus:!0})},Tt)}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&&L(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>=ft){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 c=i-(s.watchdogLastDataFlowTime||i);if(c>=gt){const p=String(s.pendingToolEventData?.rawToolName??"");if(p.endsWith("await_complete")){console.warn(`[ACode] tool_using elapsed ${Math.round(c/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(c/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(c/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",c);return}s.watchdogSubmitStartTime=i;return}if(a!=="thinking")return;const n=i-(s.watchdogSubmitStartTime||i),r=i-(s.watchdogLastDataFlowTime||i);if(H>0&&r>=H){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(K>0&&n>=K){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}},pt)}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=`
|
|
2
2
|
|
|
3
3
|
\u8BF7\u6C42\u5934:
|
|
4
4
|
`,o=`
|
|
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:"",n=typeof t.requestMethod=="string"?t.requestMethod:"",
|
|
8
|
-
`)||"ACode \u6B63\u5728\u53D1\u9001 AI \u8BF7\u6C42",
|
|
9
|
-
`)}`:"",
|
|
10
|
-
...(\u8BF7\u6C42\u4F53\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD)`:
|
|
7
|
+
`,a=typeof t.model=="string"?t.model:"",i=typeof t.endpoint=="string"?t.endpoint:typeof t.requestUrl=="string"?t.requestUrl:"",n=typeof t.requestMethod=="string"?t.requestMethod:"",r=typeof t.stream=="boolean"?t.stream:void 0,c=typeof t.messageCount=="number"?t.messageCount:void 0,p=typeof t.toolCount=="number"?t.toolCount:void 0,m=typeof t.lastUserMessagePreview=="string"?t.lastUserMessagePreview:"",g=[a?`\u6A21\u578B: ${a}`:void 0,i?`\u63A5\u53E3: ${i}`:void 0,n?`\u65B9\u6CD5: ${n}`:void 0,r!==void 0?`\u6D41\u5F0F: ${r?"\u662F":"\u5426"}`:void 0,c!==void 0?`\u4E0A\u4E0B\u6587\u6D88\u606F: ${c}`:void 0,p!==void 0?`\u53EF\u7528\u5DE5\u5177: ${p}`:void 0,m?`\u6700\u540E\u8BF7\u6C42: ${m}`:void 0].filter(S=>!!S),d=/^(authorization|x-api-key|api-key|cookie|set-cookie|x-auth-token|token)$/i,f=t.requestHeaders,_=f?Object.entries(f).filter(([S])=>!d.test(S)).map(([S,M])=>`${S}: ${M}`).filter(S=>S.trim().length>0):[],A=g.join(`
|
|
8
|
+
`)||"ACode \u6B63\u5728\u53D1\u9001 AI \u8BF7\u6C42",k=_.length>0?`${e}${_.join(`
|
|
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,C=T?`${o}${T}`:"";return`${A}${k}${C}`}}export{zt as AcodeSdkAdapter,Pt as isFailedAcodeTurnComplete,St as toAcodeEsmImportSpecifier};
|
|
11
11
|
|
package/dist/adapter/types.d.ts
CHANGED
|
@@ -88,6 +88,8 @@ export interface ProviderEvent {
|
|
|
88
88
|
|
|
89
89
|
toolName?: string;
|
|
90
90
|
|
|
91
|
+
rawToolName?: string;
|
|
92
|
+
|
|
91
93
|
toolInput?: Record<string, unknown>;
|
|
92
94
|
|
|
93
95
|
toolResult?: string;
|
|
@@ -321,6 +323,8 @@ export declare abstract class BaseProviderAdapter extends EventEmitter {
|
|
|
321
323
|
|
|
322
324
|
abstract abortCurrentTurn(sessionId: string): Promise<void>;
|
|
323
325
|
|
|
326
|
+
abstract applyAgentFeatures?(agentId: string): Promise<void>;
|
|
327
|
+
|
|
324
328
|
abstract resetContext?(sessionId: string): Promise<void>;
|
|
325
329
|
|
|
326
330
|
abstract terminateSession(sessionId: string): Promise<void>;
|
package/dist/adapter/types.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{EventEmitter as
|
|
1
|
+
import{EventEmitter as D}from"node:events";const m="[temp clipboard paste file]",y=/[A-Z]:(?:\\\\|\\)[^\r\n"'`]*?(?:\\\\|\\)mini-term-clipboard(?:\\\\|\\)paste-\d+\.txt/gi,P=/\/[^\r\n"'`]*?\/mini-term-clipboard\/paste-\d+\.txt/gi,w=/mini-term-clipboard[\\/]paste-\d+\.txt/gi;function d(a){return a.replace(y,m).replace(P,m).replace(w,m)}function $(a){const e=a.trim().replace(/^(["'])(.*)\1$/,"$2");return[y,P,w].some(c=>{c.lastIndex=0;const i=c.exec(e);return!!(i&&i[0]===e)})}function u(a){return typeof a=="string"?d(a):Array.isArray(a)?a.map(e=>u(e)):a&&typeof a=="object"?Object.fromEntries(Object.entries(a).map(([e,c])=>[e,u(c)])):a}const h={get_colleague:"aws-mcp",get_group_rooms:"aws-mcp",get_profile:"aws-mcp",hire_colleague:"aws-mcp",hire_colleagues_batch:"aws-mcp",send_group:"aws-mcp",send_private:"aws-mcp",poll_message:"aws-mcp",get_message:"aws-mcp",get_dm_history:"aws-mcp",get_group_history:"aws-mcp",my_task:"aws-mcp",submit_task_result:"aws-mcp",reject_task:"aws-mcp",terminate_task:"aws-mcp"},S=new Set(["aws-mcp","agents-work-studio"]);function g(a,e){const c=h[e.toLowerCase()];return c&&S.has(a.toLowerCase())?c:a}function C(a){const e=a.trim();if(!e)return e;if(e.includes("__")){const i=e.split("__").filter(Boolean);if(i.length>=3&&i[0].toLowerCase()==="mcp"){const f=i[1],o=i.slice(2).join("__");return`${g(f,o)}/${o}`}if(i.length>=2){const f=i[0],o=i.slice(1).join("__");return`${g(f,o)}/${o}`}}const c=h[e.toLowerCase()];return c?`${c}/${e}`:e}function L(a,e){return a?e?`${g(a,e)}/${e}`:a:e}const E=500;function p(a,e=E){return!a||a.length<=e?a:`${a.slice(0,e)}...\uFF08\u5185\u5BB9\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF09`}function j(a,e,c){if(!a)return{actionType:"other",actionLabel:"\u8C03\u7528\u5DE5\u5177"};const i=a.toLowerCase(),f=i.replace(/[_-]/g,""),k=(i.startsWith("mcp__")?i.split("__").slice(2).join("__"):i.startsWith("builtin__")?i.split("__").slice(1).join("__"):i.includes("-mcp__")?i.slice(i.indexOf("-mcp__")+6):i.includes("/")?i.split("/").filter(Boolean).at(-1)||i:i.includes("__")&&i.split("__").pop()||i).replace(/[_-]/g,""),A=t=>Array.isArray(t)?t.map(r=>String(r??"").trim()).filter(r=>r.length>0):[],n=(...t)=>{for(const r of t){if(typeof r=="string"&&r.trim())return d(r);if(Array.isArray(r)&&r.length>0){const l=r[0];if(typeof l=="string"&&l.trim())return d(l)}}},s=(...t)=>{const r=t.map(l=>l.replace(/[_-]/g,""));return r.includes(f)||r.includes(k)},T=A(e?.load_skills);if((i==="task"||i==="task()")&&T.length>0)return{actionType:"skill",actionLabel:"\u52A0\u8F7D\u6280\u80FD",actionDetail:T.join(", ")};if(i==="skill"||i==="load_skill"||i==="disclosecontext"||i.includes("skill")&&!i.startsWith("mcp__")&&!i.includes("mcp_")){let t="";if(Array.isArray(e?.load_skills)&&e.load_skills.length>0)t=e.load_skills.join(", ");else if(typeof e?.skill=="string")t=e.skill;else if(typeof e?.name=="string")t=e.name;else if(e?.args&&typeof e.args=="object"){const r=e.args;typeof r.name=="string"?t=r.name:typeof r.skill=="string"&&(t=r.skill)}return{actionType:"skill",actionLabel:"\u4F7F\u7528\u6280\u80FD",actionDetail:t||void 0}}if(s("edit","editfile","strreplace","editcode","apply_patch","multiedit","replace"))return{actionType:"edit_file",actionLabel:"\u7F16\u8F91\u6587\u4EF6",actionDetail:n(e?.file_path,e?.filePath,e?.path,e?.target_file,e?.targetFile)};if(s("write","writefile","fswrite"))return{actionType:"write_file",actionLabel:"\u5199\u5165\u6587\u4EF6",actionDetail:n(e?.file_path,e?.filePath,e?.path,e?.target_file,e?.targetFile)};if(s("read","readfile","readcode","readmultiplefiles","read_file"))return Array.isArray(e?.paths)?{actionType:"read_file",actionLabel:"\u8BFB\u53D6\u6587\u4EF6",actionDetail:`${e.paths.length}\u4E2A\u6587\u4EF6`}:{actionType:"read_file",actionLabel:"\u8BFB\u53D6\u6587\u4EF6",actionDetail:n(e?.file_path,e?.filePath,e?.path,e?.paths,e?.target_file,e?.targetFile)};if(s("bash","shell","command","executepwsh","controlpwshprocess","execute","run_command","execute_command")){const t=n(e?.command,e?.cmd,e?.argv);return{actionType:"bash",actionLabel:"\u6267\u884C\u547D\u4EE4",actionDetail:p(t)}}if(s("poll_message","get_message"))return{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:C(a)};if(s("ask_user_question","askuserquestion"))return{actionType:"other",actionLabel:"\u7B49\u5F85\u56DE\u7B54"};if(c?.mcpServer){const t=c.mcpServer;return{actionType:"mcp",actionLabel:"\u8C03\u7528MCP",actionDetail:L(t,a||"")}}const b=h[a];if(b)return{actionType:"mcp",actionLabel:"\u8C03\u7528MCP",actionDetail:`${b}/${a}`};if(i.startsWith("mcp__")||i.includes("-mcp__")||i.includes("mcp_")&&!i.includes("skill")||i==="kiropowers"){let t="",r="";if(i.startsWith("mcp__")){const _=a.split("__");t=_[1]||"",r=_.slice(2).join("__")||""}else if(i.includes("-mcp__")){const _=i.indexOf("-mcp__");t=i.substring(0,_+4),r=i.substring(_+6)}else i==="kiropowers"?(t="kiro",r=e?.powerName||e?.action||""):(t="mcp",r=a);return{actionType:"mcp",actionLabel:"\u8C03\u7528MCP",actionDetail:L(t,r)||a}}if(s("edit","editfile","strreplace","editcode","apply_patch","multiedit","replace"))return{actionType:"edit_file",actionLabel:"\u7F16\u8F91\u6587\u4EF6",actionDetail:n(e?.file_path,e?.filePath,e?.path,e?.target_file,e?.targetFile)};if(s("write","writefile","fswrite"))return{actionType:"write_file",actionLabel:"\u5199\u5165\u6587\u4EF6",actionDetail:n(e?.file_path,e?.filePath,e?.path,e?.target_file,e?.targetFile)};if(s("read","readfile","readcode","readmultiplefiles","read_file"))return Array.isArray(e?.paths)?{actionType:"read_file",actionLabel:"\u8BFB\u53D6\u6587\u4EF6",actionDetail:`${e.paths.length}\u4E2A\u6587\u4EF6`}:{actionType:"read_file",actionLabel:"\u8BFB\u53D6\u6587\u4EF6",actionDetail:n(e?.file_path,e?.filePath,e?.path,e?.paths,e?.target_file,e?.targetFile)};if(s("bash","shell","command","executepwsh","controlpwshprocess","execute","run_command","execute_command")){const t=n(e?.command,e?.cmd,e?.argv);return{actionType:"bash",actionLabel:"\u6267\u884C\u547D\u4EE4",actionDetail:p(t)}}if(i==="glob"||i==="grep"||i==="find"||i==="search"||i==="grepsearch"||i==="filesearch"){const t=e?.query||e?.pattern;return{actionType:"search",actionLabel:"\u641C\u7D22\u6587\u4EF6",actionDetail:p(t)}}if(i==="webfetch"||i==="websearch"||i==="fetch"||i==="http"||i==="remote_web_search"){const t=e?.url||e?.query;return{actionType:"web",actionLabel:"\u7F51\u7EDC\u8BF7\u6C42",actionDetail:p(t)}}return i.startsWith("lsp")||i.includes("language_server")?{actionType:"lsp",actionLabel:"LSP\u64CD\u4F5C"}:i==="notebookedit"||i.includes("notebook")?{actionType:"notebook",actionLabel:"\u7F16\u8F91Notebook"}:{actionType:"mcp",actionLabel:"\u8C03\u7528MCP",actionDetail:a}}class R extends D{}export{R as BaseProviderAdapter,C as getCanonicalMcpDetail,j as getToolActionInfo,$ as isTransientClipboardPasteFilePath,d as redactTransientClipboardPastePaths,u as redactTransientClipboardPastePathsInValue};
|
|
2
2
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import i,{WebSocketServer as
|
|
1
|
+
import i,{WebSocketServer as U}from"ws";import{isRuntimeTokenValid as R}from"../middleware/auth.js";import{isUpgradeAllowed as T}from"../services/access-control.js";import{getBrowserWsPort as E}from"../services/browser-session-manager.js";import{createLogger as k}from"../utils/logger.js";import{consumeBrowserConnectToken as O}from"./browser-connect-token.js";import{DEFAULT_BROWSER_ID as S}from"./types.js";const p=k("browser-ws-bridge"),W=/^\/browser\/agent\/([^/]+)(?:\/([^/]+))?$/;function B(c){const o=W.exec(c);return o?{agentId:decodeURIComponent(o[1]||""),browserId:o[2]?decodeURIComponent(o[2]):S}:null}function C(c,o){const s=o.searchParams.get("token")||"";if(s.trim())return s.trim();const t=c.headers["x-runtime-token"];return Array.isArray(t)?t[0]?.trim()||"":typeof t=="string"?t.trim():String(c.headers.authorization||"").match(/^Bearer\s+(.+)$/i)?.[1]?.trim()||""}function D(c){const o=new U({noServer:!0});c.on("upgrade",(s,t,b)=>{if(!T(s,t))return;const h=s.headers.host||"localhost",$=new URL(s.url||"/",`http://${h}`),f=B($.pathname);if(!f)return;const d=C(s,$),a=f.agentId,g=f.browserId,y=d?d.length>12?`${d.substring(0,12)}...`:d:"empty";if(p.info(`[browser-ws] upgrade \u8BF7\u6C42: agentId=${a}, browserId=${g}, token=${y}`),!R(d)&&!O(a,g,d)){p.warn(`[browser-ws] token \u6821\u9A8C\u5931\u8D25: agentId=${a}, browserId=${g}`),t.write(`HTTP/1.1 401 Unauthorized\r
|
|
2
2
|
\r
|
|
3
|
-
`),t.destroy();return}const I=
|
|
3
|
+
`),t.destroy();return}const I=E();if(!I){p.warn(`[browser-ws] browser \u5DE5\u5177\u6808\u672A\u542F\u52A8: agentId=${a}`),t.write(`HTTP/1.1 503 Service Unavailable\r
|
|
4
4
|
\r
|
|
5
|
-
`),t.destroy();return}const P=g===S?`/agent/${encodeURIComponent(
|
|
5
|
+
`),t.destroy();return}const P=g===S?`/agent/${encodeURIComponent(a)}`:`/agent/${encodeURIComponent(a)}/${encodeURIComponent(g)}`,l=`ws://127.0.0.1:${I}${P}`;let e=null;const w=[];o.handleUpgrade(s,t,b,n=>{try{e=new i(l)}catch(r){p.warn(`[browser-ws] \u672C\u5730 WS \u8FDE\u63A5\u5931\u8D25: ${l}, err=${r.message}`),n.close(1011,"local ws connect failed");return}const u=()=>{e&&e.readyState!==i.CLOSED&&e.close()};e.on("open",()=>{p.info(`[browser-ws] \u5DF2\u8F6C\u53D1: agentId=${a}, browserId=${g}, upstream=${l}`);for(const r of w)e&&e.readyState===i.OPEN&&e.send(r.data,{binary:r.binary});w.length=0}),e.on("message",(r,m)=>{n.readyState===i.OPEN&&n.send(r,{binary:m})}),e.on("close",(r,m)=>{n.readyState===i.OPEN&&n.close(r,m.toString())}),e.on("error",r=>{p.warn(`[browser-ws] \u672C\u5730 WS \u9519\u8BEF: agentId=${a}, err=${r.message}`),u(),n.readyState===i.OPEN&&n.close(1011,r.message)}),n.on("message",(r,m)=>{e&&e.readyState===i.OPEN?e.send(r,{binary:m}):w.length<200&&w.push({data:r,binary:m})}),n.on("close",()=>{u()}),n.on("error",()=>{u()})})})}export{D as attachBrowserWebSocketServer,B as parseBrowserBridgePath};
|
|
6
6
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{resolveSsrfConfig as E,validateUrlWithDns as M}from"./ssrf-guard.js";import{locateElement as U}from"./element-locator.js";import{serializeSnapshot as P,buildRefMap as j}from"./snapshot-serializer.js";import{DEFAULT_BROWSER_ID as k}from"./types.js";const X=3e4,$=1e4;function oe(v){const{pool:b,wsServer:_}=v,q=v.agentId,N=v.scopeKey,D=E({perAgent:v.ssrfConfig,pool:b.getSsrfConfig()});function u(e){const r=(typeof e.agentId=="string"&&e.agentId.trim()?e.agentId.trim():void 0)??q;if(!r)throw new Error("agentId is required: pass via createBuiltInBrowserTools({agentId}) or tool args.agentId");const n=typeof e.browserId=="string"&&e.browserId.trim()?e.browserId.trim():k,s=typeof e.title=="string"&&e.title.trim()?e.title.trim():void 0,o=N??`agent:${r}`;return{agentId:r,browserId:n,title:s,scopeKey:o}}function B(e,t){const r=b.get(e,t);if(!r)throw new Error(`Browser session not launched for ${e}/${t}. Call browser_launch first.`);if(r.humanTakeover)throw new Error(`Human takeover active for ${e}/${t}. Release takeover before calling browser tools.`);return b.touchActivity(e,t),r}function m(e,t){return{session:B(e,t),page:b.getActivePage(e,t)}}function p(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}function I(e,t){if(e)try{e.archive.record(t)}catch{}}function x(e){const t={};for(const[r,n]of Object.entries(e))n!==void 0&&(t[r]=n);return t}const T=new Map;function A(e,t){return`${e}:${t}`}function C(e,t,r){return T.get(A(e,t))?.get(r)}function R(e,t,r){T.set(A(e,t),j(r))}return v.pool.onSessionClose((e,t)=>{T.delete(A(e,t))}),[{kind:"builtin",toolName:"browser_launch",exposedName:"browser_launch",description:"Launch a headless browser session for the current agent. Returns wsUrl for live JPEG frame streaming (open in browser to watch the agent operate). Pass browserId to start a separate browser instance (independent cookies/cache/storage). Default browserId is 'default'. Must be called before any other browser_* tool. The session is automatically reclaimed after 10 minutes of inactivity. title is required and should briefly describe the intent of this browser session (e.g. '\u641C\u7D22\u7ADE\u54C1\u4EF7\u683C', '\u767B\u5F55\u7BA1\u7406\u540E\u53F0').",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent identifier (optional if configured at factory time). Used to isolate browser sessions across agents."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default'). Same agent can have multiple browserIds (each = independent chromium process)."},title:{type:"string",description:"Brief human-readable title describing the intent of this browser session (e.g. '\u641C\u7D22\u7ADE\u54C1\u4EF7\u683C', '\u767B\u5F55\u7BA1\u7406\u540E\u53F0'). Required \u2014 used as tab label in dashboard."}},required:["title"]},async execute(e){try{const{agentId:t,browserId:r,title:n,scopeKey:s}=u(e),o=await b.getOrLaunch(t,r,n,s),a=o.pages[o.activePageIndex].viewportSize(),c={ok:!0,agentId:t,browserId:o.browserId,title:o.title,viewport:{width:a?.width??0,height:a?.height??0}};return _&&(c.wsUrl=_.getUrl(t,o.browserId)),c}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_navigate",exposedName:"browser_navigate",description:"Navigate the active page to a URL. Waits for DOMContentLoaded (30s timeout). Access to private/loopback addresses is allowed by default (configurable).",inputSchema:{type:"object",properties:{url:{type:"string",description:"Target URL (http/https only). Private IP ranges are allowed by default, can be disabled via ssrfConfig."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["url"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.url=="string"?e.url:"";if(!o)return{ok:!1,error:"url is required"};const i=await M(o,D),a=s.url(),c=Date.now();await s.goto(i,{waitUntil:"domcontentloaded",timeout:X});const l=s.url();try{await n.listenerManager.reattach(s)}catch{}let d="";try{d=await s.title()}catch{}return I(n,{tool:"browser_navigate",args:x({url:i,browserId:r}),beforeUrl:a,afterUrl:l,durationMs:Date.now()-c}),{ok:!0,url:i,finalUrl:l,title:d,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_snapshot",exposedName:"browser_snapshot",description:"Capture the active page's accessibility tree as YAML (Playwright ariaSnapshot mode=ai). Interactive elements get [ref=eXX] markers \u2014 pass the eXX string to browser_click / browser_type / browser_select. Re-snapshot after navigation (refs are invalidated on page change). Output truncated to maxChars (default 16000).",inputSchema:{type:"object",properties:{maxChars:{type:"number",description:"Max chars to return (default 16000)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{page:n}=m(t,r),s=typeof e.maxChars=="number"&&e.maxChars>0?Math.floor(e.maxChars):void 0,o=await P(n,{maxChars:s});R(t,r,o);let i="";try{i=await n.title()}catch{}return{ok:!0,snapshot:o,url:n.url(),title:i,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_click",exposedName:"browser_click",description:"Click an element identified by ref (from browser_snapshot), text (getByText), role+name (getByRole), or CSS selector. ref is preferred, but text/role are more stable on SPA pages where refs invalidate frequently. If ref fails, the system auto-falls back to the cached role+name from the last snapshot and retries the snapshot once. 10s timeout.",inputSchema:{type:"object",properties:{ref:{type:"string",description:"Element ref from browser_snapshot, e.g. 'e5' (preferred)."},text:{type:"string",description:"Locate element by visible text (getByText). More stable than ref on SPA pages, e.g. 'javaok'."},role:{type:"string",description:"Locate element by ARIA role (e.g. button, link, textbox, listitem). Use with 'name'."},name:{type:"string",description:"Accessible name to combine with 'role' (e.g. role=button + name='Submit')."},selector:{type:"string",description:"CSS selector (subset: tag/.class/#id/[attr]/descendant/>)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.ref=="string"&&e.ref.trim()?e.ref.trim():void 0,i=typeof e.text=="string"&&e.text.trim()?e.text.trim():void 0,a=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,c=typeof e.name=="string"&&e.name?e.name:void 0,l=typeof e.selector=="string"?e.selector:void 0;if(o===void 0&&i===void 0&&a===void 0&&!l)return{ok:!1,error:"Either 'ref', 'text', 'role', or 'selector' is required for browser_click"};const d=o!==void 0?C(t,r,o):void 0,h=await U(s,{ref:o,text:i,role:a,name:c,selector:l,refFallback:d}),w=s.url(),y=Date.now();await h.click({timeout:$});const f=s.url(),g=o!==void 0?`ref=${o}`:i!==void 0?`text=${i}`:a!==void 0?`role=${a}`:`selector=${l}`;return I(n,{tool:"browser_click",args:x({ref:o,text:i,role:a,name:c,selector:l,browserId:r}),beforeUrl:w,afterUrl:f,durationMs:Date.now()-y}),{ok:!0,clicked:g,url:f,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_type",exposedName:"browser_type",description:"Type text into an input identified by ref, role+name (getByRole), or CSS selector. Note: 'text' param is the text to type (not for locating); use 'role'+'name' to locate the input. clear=true (default) clears the field first. delay adds per-keystroke delay (ms).",inputSchema:{type:"object",properties:{ref:{type:"string",description:"Element ref from browser_snapshot, e.g. 'e5'."},role:{type:"string",description:"Locate input by ARIA role (e.g. textbox, searchbox). Use with 'name'."},name:{type:"string",description:"Accessible name to combine with 'role'."},selector:{type:"string",description:"CSS selector subset."},text:{type:"string",description:"Text to type (required)."},clear:{type:"boolean",description:"Clear field before typing (default true)."},delay:{type:"number",description:"Per-keystroke delay in ms (default 0)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["text"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.ref=="string"&&e.ref.trim()?e.ref.trim():void 0,i=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,a=typeof e.name=="string"&&e.name?e.name:void 0,c=typeof e.selector=="string"?e.selector:void 0,l=typeof e.text=="string"?e.text:"";if(!l)return{ok:!1,error:"text is required for browser_type"};if(o===void 0&&i===void 0&&!c)return{ok:!1,error:"Either 'ref', 'role', or 'selector' is required for browser_type"};const d=e.clear!==!1,h=typeof e.delay=="number"&&e.delay>=0?e.delay:0,w=o!==void 0?C(t,r,o):void 0,y=await U(s,{ref:o,role:i,name:a,selector:c,refFallback:w}),f=s.url(),g=Date.now();d&&await y.fill(""),await y.type(l,{delay:h});const S=s.url();return I(n,{tool:"browser_type",args:x({ref:o,role:i,name:a,selector:c,text:l,clear:d,delay:h,browserId:r}),beforeUrl:f,afterUrl:S,durationMs:Date.now()-g}),{ok:!0,typed:l.length,url:S,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_select",exposedName:"browser_select",description:"Select an option in a <select> element identified by ref, text (getByText), role+name (getByRole), or CSS selector. value is the option value (string).",inputSchema:{type:"object",properties:{ref:{type:"string",description:"Element ref from browser_snapshot, e.g. 'e5'."},text:{type:"string",description:"Locate element by visible text (getByText)."},role:{type:"string",description:"Locate element by ARIA role (e.g. combobox, listbox). Use with 'name'."},name:{type:"string",description:"Accessible name to combine with 'role'."},selector:{type:"string",description:"CSS selector subset."},value:{type:"string",description:"Option value to select (required)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["value"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.ref=="string"&&e.ref.trim()?e.ref.trim():void 0,i=typeof e.text=="string"&&e.text.trim()?e.text.trim():void 0,a=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,c=typeof e.name=="string"&&e.name?e.name:void 0,l=typeof e.selector=="string"?e.selector:void 0,d=typeof e.value=="string"?e.value:e.value!==void 0&&e.value!==null?String(e.value):"";if(!d)return{ok:!1,error:"value is required for browser_select"};if(o===void 0&&i===void 0&&a===void 0&&!l)return{ok:!1,error:"Either 'ref', 'text', 'role', or 'selector' is required for browser_select"};const h=o!==void 0?C(t,r,o):void 0,w=await U(s,{ref:o,text:i,role:a,name:c,selector:l,refFallback:h}),y=s.url(),f=Date.now();await w.selectOption(d);const g=s.url();return I(n,{tool:"browser_select",args:x({ref:o,text:i,role:a,name:c,selector:l,value:d,browserId:r}),beforeUrl:y,afterUrl:g,durationMs:Date.now()-f}),{ok:!0,selected:d,url:g,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_press_key",exposedName:"browser_press_key",description:"Press a keyboard key on the active page. Examples: 'Enter', 'Escape', 'Tab', 'Control+a', 'ArrowDown'. See Playwright key names for full list.",inputSchema:{type:"object",properties:{key:{type:"string",description:"Key or chord (e.g. 'Enter', 'Control+a')."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["key"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.key=="string"?e.key:"";if(!o)return{ok:!1,error:"key is required for browser_press_key"};const i=s.url(),a=Date.now();await s.keyboard.press(o);const c=s.url();return I(n,{tool:"browser_press_key",args:x({key:o,browserId:r}),beforeUrl:i,afterUrl:c,durationMs:Date.now()-a}),{ok:!0,key:o,url:c,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_scroll",exposedName:"browser_scroll",description:"Scroll the active page. Provide x/y for absolute scroll-to, OR deltaX/deltaY for relative wheel scroll. If both provided, x/y wins.",inputSchema:{type:"object",properties:{x:{type:"number",description:"Absolute scrollX (window.scrollTo)."},y:{type:"number",description:"Absolute scrollY (window.scrollTo)."},deltaX:{type:"number",description:"Relative wheel delta X."},deltaY:{type:"number",description:"Relative wheel delta Y."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.x=="number"&&Number.isFinite(e.x)||typeof e.y=="number"&&Number.isFinite(e.y),i=typeof e.deltaX=="number"&&Number.isFinite(e.deltaX)||typeof e.deltaY=="number"&&Number.isFinite(e.deltaY);if(!o&&!i)return{ok:!1,error:"Provide x/y (absolute) or deltaX/deltaY (relative) for browser_scroll"};const a=s.url(),c=Date.now();let l=0,d=0;if(o){const w=typeof e.x=="number"?e.x:0,y=typeof e.y=="number"?e.y:0;await s.evaluate(([g,S])=>globalThis.scrollTo(g,S),[w,y]);const f=await s.evaluate(()=>globalThis);l=f.scrollX,d=f.scrollY}else{const w=typeof e.deltaX=="number"?e.deltaX:0,y=typeof e.deltaY=="number"?e.deltaY:0;await s.mouse.wheel(w,y);const f=await s.evaluate(()=>globalThis);l=f.scrollX,d=f.scrollY}const h=s.url();return I(n,{tool:"browser_scroll",args:x({x:e.x,y:e.y,deltaX:e.deltaX,deltaY:e.deltaY,browserId:r}),beforeUrl:a,afterUrl:h,durationMs:Date.now()-c}),{ok:!0,scrollX:l,scrollY:d,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_screenshot",exposedName:"browser_screenshot",description:"Capture a single screenshot of the active page. Returns dataUrl (base64-encoded). fullPage=true captures the entire scrollable page (may be large).",inputSchema:{type:"object",properties:{fullPage:{type:"boolean",description:"Capture entire scrollable page (default false)."},format:{type:"string",enum:["jpeg","png"],description:"Image format (default 'jpeg')."},quality:{type:"number",description:"JPEG quality 0-100 (default 80, ignored for png)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{page:n}=m(t,r),s=e.fullPage===!0,o=e.format==="png"||e.format==="jpeg"?e.format:"jpeg",i=typeof e.quality=="number"&&e.quality>=0&&e.quality<=100?e.quality:80,c=(await n.screenshot({fullPage:s,type:o,quality:o==="jpeg"?i:void 0})).toString("base64"),l=`data:image/${o};base64,${c}`,d=n.viewportSize();return{ok:!0,dataUrl:l,width:d?.width??0,height:d?.height??0,url:n.url(),browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_close",exposedName:"browser_close",description:"Close the default browser session (browserId='default') for the current agent. Returns the step archive (recent operations). To close a non-default browser session, use browser_session_close({browserId}).",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent identifier (optional)."}},required:[]},async execute(e){try{const{agentId:t}=u(e),r=b.get(t,k);if(!r)return{ok:!1,error:`Browser session not launched for ${t}/${k}. Call browser_launch first.`};const n=r.archive.toJSON();return await b.close(t,k),{ok:!0,browserId:k,archive:n}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_list_sessions",exposedName:"browser_list_sessions",description:"List all browser sessions for the current agent. Returns an array of {browserId, pageCount, currentUrl, currentTitle, launchedAt, lastActivityAt}. Use this to discover which browserIds are currently active before calling browser_session_close.",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent identifier (optional)."}},required:[]},async execute(e){try{const{agentId:t}=u(e),r=b.listSessions(t);return{ok:!0,agentId:t,sessions:r.map(n=>({browserId:n.browserId,pageCount:n.pageCount,activePageIndex:n.activePageIndex,currentUrl:n.currentUrl,currentTitle:n.currentTitle,launchedAt:n.launchedAt,lastActivityAt:n.lastActivityAt,wsUrl:_?_.getUrl(t,n.browserId):void 0}))}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_session_close",exposedName:"browser_session_close",description:"Close a specific browser session by browserId for the current agent. Returns the step archive of that session. Use browser_list_sessions to enumerate active browserIds. For the default session, you can also call browser_close (alias for browser_session_close with browserId='default').",inputSchema:{type:"object",properties:{browserId:{type:"string",description:"Browser instance ID to close (default 'default'). Required for non-default sessions."},agentId:{type:"string",description:"Agent identifier (optional)."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),n=b.get(t,r);if(!n)return{ok:!1,error:`Browser session not launched for ${t}/${r}. Call browser_launch first.`};const s=n.archive.toJSON();return await b.close(t,r),{ok:!0,browserId:r,archive:s}}catch(t){return p(t)}}}]}export{oe as createBuiltInBrowserTools};
|
|
1
|
+
import{resolveSsrfConfig as E,validateUrlWithDns as M}from"./ssrf-guard.js";import{locateElement as U}from"./element-locator.js";import{serializeSnapshot as P,buildRefMap as j}from"./snapshot-serializer.js";import{DEFAULT_BROWSER_ID as k}from"./types.js";const X=3e4,$=1e4;function oe(v){const{pool:b,wsServer:_}=v,q=v.agentId,N=v.scopeKey,D=E({perAgent:v.ssrfConfig,pool:b.getSsrfConfig()});function u(e){const r=(typeof e.agentId=="string"&&e.agentId.trim()?e.agentId.trim():void 0)??q;if(!r)throw new Error("agentId is required: pass via createBuiltInBrowserTools({agentId}) or tool args.agentId");const n=typeof e.browserId=="string"&&e.browserId.trim()?e.browserId.trim():k,s=typeof e.title=="string"&&e.title.trim()?e.title.trim():void 0,o=N??`agent:${r}`;return{agentId:r,browserId:n,title:s,scopeKey:o}}function B(e,t){const r=b.get(e,t);if(!r)throw new Error(`Browser session not launched for ${e}/${t}. Call browser_launch first.`);if(r.humanTakeover)throw new Error(`Human takeover active for ${e}/${t}. Release takeover before calling browser tools.`);return b.touchActivity(e,t),r}function m(e,t){return{session:B(e,t),page:b.getActivePage(e,t)}}function p(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}function I(e,t){if(e)try{e.archive.record(t)}catch{}}function x(e){const t={};for(const[r,n]of Object.entries(e))n!==void 0&&(t[r]=n);return t}const T=new Map;function A(e,t){return`${e}:${t}`}function C(e,t,r){return T.get(A(e,t))?.get(r)}function R(e,t,r){T.set(A(e,t),j(r))}return v.pool.onSessionClose((e,t)=>{T.delete(A(e,t))}),[{kind:"builtin",toolName:"browser_launch",exposedName:"browser_launch",description:"Launch a headless browser session for the current agent. Returns wsUrl for live JPEG frame streaming (open in browser to watch the agent operate). Pass browserId to start a separate browser instance (independent cookies/cache/storage). Default browserId is 'default'. Must be called before any other browser_* tool. The session is automatically reclaimed after 10 minutes of inactivity. title is required and should briefly describe the intent of this browser session (e.g. '\u641C\u7D22\u7ADE\u54C1\u4EF7\u683C', '\u767B\u5F55\u7BA1\u7406\u540E\u53F0').",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent identifier (optional if configured at factory time). Used to isolate browser sessions across agents."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default'). Same agent can have multiple browserIds (each = independent chromium process)."},title:{type:"string",description:"Brief human-readable title describing the intent of this browser session (e.g. '\u641C\u7D22\u7ADE\u54C1\u4EF7\u683C', '\u767B\u5F55\u7BA1\u7406\u540E\u53F0'). Required \u2014 used as tab label in dashboard."}},required:["title"]},async execute(e){try{const{agentId:t,browserId:r,title:n,scopeKey:s}=u(e),o=await b.getOrLaunch(t,r,n,s),a=o.pages[o.activePageIndex].viewportSize(),c={ok:!0,agentId:t,browserId:o.browserId,title:o.title,viewport:{width:a?.width??0,height:a?.height??0}};return _&&(c.wsUrl=_.getUrl(t,o.browserId)),c}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_navigate",exposedName:"browser_navigate",description:"Navigate the active page to a URL. Waits for DOMContentLoaded (30s timeout). Access to private/loopback addresses is allowed by default (configurable).",inputSchema:{type:"object",properties:{url:{type:"string",description:"Target URL (http/https only). Private IP ranges are allowed by default, can be disabled via ssrfConfig."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["url"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.url=="string"?e.url:"";if(!o)return{ok:!1,error:"url is required"};const i=await M(o,D),a=s.url(),c=Date.now();await s.goto(i,{waitUntil:"domcontentloaded",timeout:X});const l=s.url();try{await n.listenerManager.reattach(s)}catch{}let d="";try{d=await s.title()}catch{}return I(n,{tool:"browser_navigate",args:x({url:i,browserId:r}),beforeUrl:a,afterUrl:l,durationMs:Date.now()-c}),{ok:!0,url:i,finalUrl:l,title:d,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_snapshot",exposedName:"browser_snapshot",description:"Capture the active page's accessibility tree as YAML (Playwright ariaSnapshot mode=ai). Interactive elements get [ref=eXX] markers \u2014 pass the eXX string to browser_click / browser_type / browser_select. Re-snapshot after navigation (refs are invalidated on page change). Output truncated to maxChars (default 16000).",inputSchema:{type:"object",properties:{maxChars:{type:"number",description:"Max chars to return (default 16000)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{page:n}=m(t,r),s=typeof e.maxChars=="number"&&e.maxChars>0?Math.floor(e.maxChars):void 0,o=await P(n,{maxChars:s});R(t,r,o);let i="";try{i=await n.title()}catch{}return{ok:!0,snapshot:o,url:n.url(),title:i,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_click",exposedName:"browser_click",description:"Click an element identified by ref (from browser_snapshot), text (getByText), role+name (getByRole), or CSS selector. ref is preferred, but text/role are more stable on SPA pages where refs invalidate frequently. If ref fails, the system auto-falls back to the cached role+name from the last snapshot and retries the snapshot once. 10s timeout.",inputSchema:{type:"object",properties:{ref:{type:"string",description:"Element ref from browser_snapshot, e.g. 'e5' (preferred)."},text:{type:"string",description:"Locate element by visible text (getByText). More stable than ref on SPA pages, e.g. 'javaok'."},role:{type:"string",description:"Locate element by ARIA role (e.g. button, link, textbox, listitem). Use with 'name'."},name:{type:"string",description:"Accessible name to combine with 'role' (e.g. role=button + name='Submit')."},selector:{type:"string",description:"CSS selector (subset: tag/.class/#id/[attr]/descendant/>)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.ref=="string"&&e.ref.trim()?e.ref.trim():void 0,i=typeof e.text=="string"&&e.text.trim()?e.text.trim():void 0,a=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,c=typeof e.name=="string"&&e.name?e.name:void 0,l=typeof e.selector=="string"?e.selector:void 0;if(o===void 0&&i===void 0&&a===void 0&&!l)return{ok:!1,error:"Either 'ref', 'text', 'role', or 'selector' is required for browser_click"};const d=o!==void 0?C(t,r,o):void 0,h=await U(s,{ref:o,text:i,role:a,name:c,selector:l,refFallback:d}),w=s.url(),y=Date.now();await h.click({timeout:$});const f=s.url(),g=o!==void 0?`ref=${o}`:i!==void 0?`text=${i}`:a!==void 0?`role=${a}`:`selector=${l}`;return I(n,{tool:"browser_click",args:x({ref:o,text:i,role:a,name:c,selector:l,browserId:r}),beforeUrl:w,afterUrl:f,durationMs:Date.now()-y}),{ok:!0,clicked:g,url:f,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_type",exposedName:"browser_type",description:"Type text into an input identified by ref, role+name (getByRole), or CSS selector. Note: 'text' param is the text to type (not for locating); use 'role'+'name' to locate the input. clear=true (default) clears the field first. delay adds per-keystroke delay (ms).",inputSchema:{type:"object",properties:{ref:{type:"string",description:"Element ref from browser_snapshot, e.g. 'e5'."},role:{type:"string",description:"Locate input by ARIA role (e.g. textbox, searchbox). Use with 'name'."},name:{type:"string",description:"Accessible name to combine with 'role'."},selector:{type:"string",description:"CSS selector subset."},text:{type:"string",description:"Text to type (required)."},clear:{type:"boolean",description:"Clear field before typing (default true)."},delay:{type:"number",description:"Per-keystroke delay in ms (default 0)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["text"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.ref=="string"&&e.ref.trim()?e.ref.trim():void 0,i=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,a=typeof e.name=="string"&&e.name?e.name:void 0,c=typeof e.selector=="string"?e.selector:void 0,l=typeof e.text=="string"?e.text:"";if(!l)return{ok:!1,error:"text is required for browser_type"};if(o===void 0&&i===void 0&&!c)return{ok:!1,error:"Either 'ref', 'role', or 'selector' is required for browser_type"};const d=e.clear!==!1,h=typeof e.delay=="number"&&e.delay>=0?e.delay:0,w=o!==void 0?C(t,r,o):void 0,y=await U(s,{ref:o,role:i,name:a,selector:c,refFallback:w}),f=s.url(),g=Date.now();d&&await y.fill(""),await y.type(l,{delay:h});const S=s.url();return I(n,{tool:"browser_type",args:x({ref:o,role:i,name:a,selector:c,text:l,clear:d,delay:h,browserId:r}),beforeUrl:f,afterUrl:S,durationMs:Date.now()-g}),{ok:!0,typed:l.length,url:S,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_select",exposedName:"browser_select",description:"Select an option in a <select> element identified by ref, text (getByText), role+name (getByRole), or CSS selector. value is the option value (string).",inputSchema:{type:"object",properties:{ref:{type:"string",description:"Element ref from browser_snapshot, e.g. 'e5'."},text:{type:"string",description:"Locate element by visible text (getByText)."},role:{type:"string",description:"Locate element by ARIA role (e.g. combobox, listbox). Use with 'name'."},name:{type:"string",description:"Accessible name to combine with 'role'."},selector:{type:"string",description:"CSS selector subset."},value:{type:"string",description:"Option value to select (required)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["value"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.ref=="string"&&e.ref.trim()?e.ref.trim():void 0,i=typeof e.text=="string"&&e.text.trim()?e.text.trim():void 0,a=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,c=typeof e.name=="string"&&e.name?e.name:void 0,l=typeof e.selector=="string"?e.selector:void 0,d=typeof e.value=="string"?e.value:e.value!==void 0&&e.value!==null?String(e.value):"";if(!d)return{ok:!1,error:"value is required for browser_select"};if(o===void 0&&i===void 0&&a===void 0&&!l)return{ok:!1,error:"Either 'ref', 'text', 'role', or 'selector' is required for browser_select"};const h=o!==void 0?C(t,r,o):void 0,w=await U(s,{ref:o,text:i,role:a,name:c,selector:l,refFallback:h}),y=s.url(),f=Date.now();await w.selectOption(d);const g=s.url();return I(n,{tool:"browser_select",args:x({ref:o,text:i,role:a,name:c,selector:l,value:d,browserId:r}),beforeUrl:y,afterUrl:g,durationMs:Date.now()-f}),{ok:!0,selected:d,url:g,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_press_key",exposedName:"browser_press_key",description:"Press a keyboard key on the active page. Examples: 'Enter', 'Escape', 'Tab', 'Control+a', 'ArrowDown'. See Playwright key names for full list.",inputSchema:{type:"object",properties:{key:{type:"string",description:"Key or chord (e.g. 'Enter', 'Control+a')."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:["key"]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.key=="string"?e.key:"";if(!o)return{ok:!1,error:"key is required for browser_press_key"};const i=s.url(),a=Date.now();await s.keyboard.press(o);const c=s.url();return I(n,{tool:"browser_press_key",args:x({key:o,browserId:r}),beforeUrl:i,afterUrl:c,durationMs:Date.now()-a}),{ok:!0,key:o,url:c,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_scroll",exposedName:"browser_scroll",description:"Scroll the active page. Provide x/y for absolute scroll-to, OR deltaX/deltaY for relative wheel scroll. If both provided, x/y wins.",inputSchema:{type:"object",properties:{x:{type:"number",description:"Absolute scrollX (window.scrollTo)."},y:{type:"number",description:"Absolute scrollY (window.scrollTo)."},deltaX:{type:"number",description:"Relative wheel delta X."},deltaY:{type:"number",description:"Relative wheel delta Y."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{session:n,page:s}=m(t,r),o=typeof e.x=="number"&&Number.isFinite(e.x)||typeof e.y=="number"&&Number.isFinite(e.y),i=typeof e.deltaX=="number"&&Number.isFinite(e.deltaX)||typeof e.deltaY=="number"&&Number.isFinite(e.deltaY);if(!o&&!i)return{ok:!1,error:"Provide x/y (absolute) or deltaX/deltaY (relative) for browser_scroll"};const a=s.url(),c=Date.now();let l=0,d=0;if(o){const w=typeof e.x=="number"?e.x:0,y=typeof e.y=="number"?e.y:0;await s.evaluate(([g,S])=>globalThis.scrollTo(g,S),[w,y]);const f=await s.evaluate(()=>globalThis);l=f.scrollX,d=f.scrollY}else{const w=typeof e.deltaX=="number"?e.deltaX:0,y=typeof e.deltaY=="number"?e.deltaY:0;await s.mouse.wheel(w,y);const f=await s.evaluate(()=>globalThis);l=f.scrollX,d=f.scrollY}const h=s.url();return I(n,{tool:"browser_scroll",args:x({x:e.x,y:e.y,deltaX:e.deltaX,deltaY:e.deltaY,browserId:r}),beforeUrl:a,afterUrl:h,durationMs:Date.now()-c}),{ok:!0,scrollX:l,scrollY:d,browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_screenshot",exposedName:"browser_screenshot",description:"Capture a single screenshot of the active page. Returns dataUrl (base64-encoded). fullPage=true captures the entire scrollable page (may be large).",inputSchema:{type:"object",properties:{fullPage:{type:"boolean",description:"Capture entire scrollable page (default false)."},format:{type:"string",enum:["jpeg","png"],description:"Image format (default 'jpeg')."},quality:{type:"number",description:"JPEG quality 0-100 (default 80, ignored for png)."},agentId:{type:"string",description:"Agent identifier (optional)."},browserId:{type:"string",description:"Browser instance ID (optional, default 'default')."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),{page:n}=m(t,r),s=e.fullPage===!0,o=e.format==="png"||e.format==="jpeg"?e.format:"jpeg",i=typeof e.quality=="number"&&e.quality>=0&&e.quality<=100?e.quality:80,c=(await n.screenshot({fullPage:s,type:o,quality:o==="jpeg"?i:void 0})).toString("base64"),l=`data:image/${o};base64,${c}`,d=n.viewportSize();return{ok:!0,dataUrl:l,width:d?.width??0,height:d?.height??0,url:n.url(),browserId:r}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_close",exposedName:"browser_close",description:"Close the default browser session (browserId='default') for the current agent. Returns the step archive (recent operations). To close a non-default browser session, use browser_session_close({browserId}).",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent identifier (optional)."}},required:[]},async execute(e){try{const{agentId:t}=u(e),r=b.get(t,k);if(!r)return{ok:!1,error:`Browser session not launched for ${t}/${k}. Call browser_launch first.`};const n=r.archive.toJSON();return await b.close(t,k),{ok:!0,browserId:k,archive:n}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_list_sessions",exposedName:"browser_list_sessions",description:"List all browser sessions for the current agent. Returns an array of {browserId, pageCount, currentUrl, currentTitle, launchedAt, lastActivityAt}. Use this to discover which browserIds are currently active before calling browser_session_close.",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent identifier (optional)."}},required:[]},async execute(e){try{const{agentId:t}=u(e),r=b.listSessions(t);return{ok:!0,agentId:t,sessions:r.map(n=>({browserId:n.browserId,pageCount:n.pageCount,activePageIndex:n.activePageIndex,currentUrl:n.currentUrl,currentTitle:n.currentTitle,launchedAt:n.launchedAt,lastActivityAt:n.lastActivityAt,wsUrl:_?_.getUrl(t,n.browserId):void 0}))}}catch(t){return p(t)}}},{kind:"builtin",toolName:"browser_session_close",exposedName:"browser_session_close",description:"Close a specific browser session by browserId for the current agent. Returns the step archive of that session. Use browser_list_sessions to enumerate active browserIds. For the default session, you can also call browser_close (alias for browser_session_close with browserId='default').",inputSchema:{type:"object",properties:{browserId:{type:"string",description:"Browser instance ID to close (default 'default'). Required for non-default sessions."},agentId:{type:"string",description:"Agent identifier (optional)."}},required:[]},async execute(e){try{const{agentId:t,browserId:r}=u(e),n=b.get(t,r);if(!n)return{ok:!1,error:`Browser session not launched for ${t}/${r}. Call browser_launch first.`};const s=n.archive.toJSON();return await b.close(t,r),{ok:!0,browserId:r,archive:s}}catch(t){return p(t)}}}].map(e=>({...e,category:"browser"}))}export{oe as createBuiltInBrowserTools};
|
|
2
2
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export type DesktopJsonValue = string | number | boolean | null | DesktopJsonValue[] | {
|
|
4
|
+
[key: string]: DesktopJsonValue;
|
|
5
|
+
};
|
|
6
|
+
export type DesktopJsonObject = {
|
|
7
|
+
[key: string]: DesktopJsonValue;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export interface DesktopToolExecutionContext {
|
|
11
|
+
readonly toolCallId?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ACodeBuiltInToolLike {
|
|
15
|
+
readonly kind: "builtin";
|
|
16
|
+
readonly toolName: string;
|
|
17
|
+
readonly exposedName: string;
|
|
18
|
+
readonly description: string;
|
|
19
|
+
readonly inputSchema: DesktopJsonObject;
|
|
20
|
+
|
|
21
|
+
readonly category?: string;
|
|
22
|
+
execute(args: DesktopJsonObject, context?: DesktopToolExecutionContext): Promise<string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type DesktopToolForwarder = (name: string, args: Record<string, unknown>) => Promise<string>;
|
|
26
|
+
|
|
27
|
+
export interface CreateBuiltInDesktopToolsOptions {
|
|
28
|
+
|
|
29
|
+
tools: Array<{
|
|
30
|
+
name: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
inputSchema: Record<string, unknown>;
|
|
33
|
+
}>;
|
|
34
|
+
|
|
35
|
+
forwarder: DesktopToolForwarder;
|
|
36
|
+
|
|
37
|
+
screenshotDir?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export declare function createBuiltInDesktopTools(options: CreateBuiltInDesktopToolsOptions): ACodeBuiltInToolLike[];
|
|
41
|
+
|
|
42
|
+
export declare function injectScreenshotPath(toolName: string, args: DesktopJsonObject, screenshotDir: string): Promise<DesktopJsonObject>;
|
|
43
|
+
|
|
44
|
+
export declare function pruneOldScreenshots(dir: string, options?: {
|
|
45
|
+
now?: number;
|
|
46
|
+
force?: boolean;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
|
|
49
|
+
export declare function nextScreenshotFilePath(screenshotDir: string): string;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{mkdir as f,readdir as S,rm as m,stat as _}from"node:fs/promises";import{dirname as l,join as a}from"node:path";const p=new Set(["desktop_screenshot","desktop_window_screenshot"]),w=" Note: when no `path` is given, the bridge auto-saves the PNG to a local file and returns its path (NOT base64). IMPORTANT COORDINATES: coordinates derived from a window screenshot are IMAGE-relative \u2014 to click, either re-run desktop_perceive with hwnd (returns SCREEN coords directly), or pass the screenshot's returned origin to desktop_perceive/desktop_mouse. For native windows prefer desktop_uia_tree + desktop_uia_click instead \u2014 precise and vision-free.";function R(e){const{tools:r,forwarder:n}=e,s=e.screenshotDir??a(process.cwd(),".desktop-mcp","screenshots");return r.filter(t=>t.name.startsWith("desktop_")).map(t=>{const o=p.has(t.name),i=o?`${t.description??t.name}${w}`:t.description??t.name;return{kind:"builtin",toolName:t.name,exposedName:t.name,description:i,inputSchema:y(t.inputSchema),category:"desktop",async execute(c){const h=o?await N(t.name,c,s):c;return n(t.name,h)}}})}async function N(e,r,n){if(!p.has(e)||typeof r.path=="string"&&r.path.trim())return r;const s=k(n);return await f(l(s),{recursive:!0}).catch(()=>{}),O(n).catch(()=>{}),{...r,path:s}}const u=(()=>{const e=Number.parseInt(process.env.AWS_DESKTOP_MCP_SCREENSHOT_RETENTION_DAYS??"",10);return Number.isFinite(e)&&e>=0?e:7})(),E=2*36e5;let d=0;async function O(e,r){const n=r?.now??Date.now();if(u===0||!r?.force&&n-d<E)return;d=n;const s=n-u*864e5;let t;try{t=await S(e)}catch{return}await Promise.all(t.filter(o=>o.startsWith("shot-")&&o.endsWith(".png")).map(async o=>{const i=a(e,o);try{(await _(i)).mtimeMs<s&&await m(i,{force:!0})}catch{}}))}let T=0;function k(e){const r=(++T).toString(36).padStart(3,"0"),n=new Date().toISOString().replace(/[:.]/g,"-");return a(e,`shot-${n}-${r}.png`)}function y(e){if(!e||typeof e!="object")return{type:"object",properties:{}};try{return JSON.parse(JSON.stringify(e))}catch{return{type:"object",properties:{}}}}export{R as createBuiltInDesktopTools,N as injectScreenshotPath,k as nextScreenshotFilePath,O as pruneOldScreenshots};
|
|
2
|
+
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
|
|
4
|
+
export interface DesktopMcpConnectionOptions {
|
|
5
|
+
|
|
6
|
+
command: string;
|
|
7
|
+
|
|
8
|
+
args?: string[];
|
|
9
|
+
|
|
10
|
+
cwd?: string;
|
|
11
|
+
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
|
|
14
|
+
initTimeoutMs?: number;
|
|
15
|
+
|
|
16
|
+
maxStderrLines?: number;
|
|
17
|
+
|
|
18
|
+
onUnexpectedClose?: (code: number | null) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DesktopMcpToolInfo {
|
|
22
|
+
name: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
inputSchema: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export declare class DesktopMcpConnection {
|
|
28
|
+
private client;
|
|
29
|
+
private transport;
|
|
30
|
+
private stderrLines;
|
|
31
|
+
private readonly options;
|
|
32
|
+
private closed;
|
|
33
|
+
private closing;
|
|
34
|
+
constructor(options: DesktopMcpConnectionOptions);
|
|
35
|
+
|
|
36
|
+
isConnected(): boolean;
|
|
37
|
+
|
|
38
|
+
getRecentStderr(lines?: number): string[];
|
|
39
|
+
|
|
40
|
+
start(): Promise<DesktopMcpToolInfo[]>;
|
|
41
|
+
|
|
42
|
+
callTool(name: string, args: Record<string, unknown>, timeoutMs: number): Promise<string>;
|
|
43
|
+
|
|
44
|
+
close(): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export declare function callToolResultToText(result: CallToolResult): string;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{Client as T}from"@modelcontextprotocol/sdk/client/index.js";import{StdioClientTransport as g}from"@modelcontextprotocol/sdk/client/stdio.js";const w=100,y=15e3;class _{constructor(n){this.client=null,this.transport=null,this.stderrLines=[],this.closed=!1,this.closing=!1,this.options=n}isConnected(){return!this.closed&&this.client!==null}getRecentStderr(n=20){return this.stderrLines.slice(-n)}async start(){const{command:n,args:s,cwd:t}=this.options,e=new g({command:n,args:s??[],...t?{cwd:t}:{},env:S({...process.env,...this.options.env??{}}),stderr:"pipe"}),c=this.options.maxStderrLines??w;try{const r=e.stderr;if(r){let l="";r.setEncoding?.("utf8"),r.on("data",a=>{const m=typeof a=="string"?a:Buffer.from(a).toString("utf8");l+=m;const h=l.split(/\r?\n/);l=h.pop()??"";for(const d of h)d.trim()&&(this.stderrLines.push(d),this.stderrLines.length>c&&this.stderrLines.splice(0,this.stderrLines.length-c))})}}catch{}e.onclose=()=>{this.closed||this.closing||(this.closed=!0,this.client=null,this.transport=null,this.options.onUnexpectedClose?.(null))},e.onerror=()=>{};const o=new T({name:"aws-runtime-bridge",version:"1.0.0"}),p=this.options.initTimeoutMs??y;await u(o.connect(e),p,"nuphus-mcp initialize timeout");const{tools:f}=await u(o.listTools(),p,"nuphus-mcp listTools timeout");return this.client=o,this.transport=e,this.closed=!1,f.map(r=>({name:r.name,...r.description?{description:r.description}:{},inputSchema:r.inputSchema??{type:"object",properties:{}}}))}async callTool(n,s,t){const e=this.client;if(!e||this.closed)throw new Error("nuphus-mcp connection is not active");const c=await u(e.callTool({name:n,arguments:s},void 0,{timeout:t}),t+5e3,`nuphus-mcp tool ${n} timeout after ${t}ms`);return E(c)}async close(){if(this.closing||this.closed)return;this.closing=!0,this.closed=!0;const n=this.client;this.client=null,this.transport=null;try{await n?.close()}catch{}this.closing=!1}}function E(i){const s=(Array.isArray(i?.content)?i.content:[]).filter(t=>t?.type==="text").map(t=>t.text);if(s.length>0){const t=s.join(`
|
|
2
|
+
`);return i?.isError?`nuphus-mcp tool error: ${t}`:t}return JSON.stringify(i??{})}function S(i){const n={};for(const[s,t]of Object.entries(i))t!==void 0&&(n[s]=t);return n}function u(i,n,s){return new Promise((t,e)=>{const c=setTimeout(()=>e(new Error(s)),n);i.then(o=>{clearTimeout(c),t(o)},o=>{clearTimeout(c),e(o instanceof Error?o:new Error(String(o)))})})}export{_ as DesktopMcpConnection,E as callToolResultToText};
|
|
3
|
+
|