aws-runtime-bridge 1.9.130 → 1.9.134

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.
Files changed (94) hide show
  1. package/dist/adapter/AcodeSdkAdapter.d.ts +15 -0
  2. package/dist/adapter/AcodeSdkAdapter.js +5 -5
  3. package/dist/adapter/types.d.ts +9 -0
  4. package/dist/browser/browser-frame-encode.d.ts +16 -0
  5. package/dist/browser/browser-frame-encode.js +2 -0
  6. package/dist/browser/browser-pool.js +1 -1
  7. package/dist/browser/built-in-browser-tools.js +1 -1
  8. package/dist/browser/screencast-differ.d.ts +35 -0
  9. package/dist/browser/screencast-differ.js +2 -0
  10. package/dist/browser/screencast-manager.d.ts +32 -0
  11. package/dist/browser/screencast-manager.js +1 -1
  12. package/dist/browser/types.d.ts +48 -1
  13. package/dist/browser/types.js +1 -1
  14. package/dist/browser/ws-server.d.ts +1 -1
  15. package/dist/browser/ws-server.js +1 -1
  16. package/dist/routes/background-tasks.js +7 -1
  17. package/dist/routes/instance-tool-status.d.ts +4 -0
  18. package/dist/routes/instance-tool-status.js +2 -0
  19. package/dist/routes/instance.d.ts +0 -2
  20. package/dist/routes/instance.js +1 -1
  21. package/dist/routes/system-metrics.js +1 -1
  22. package/dist/routes/terminal.js +19 -19
  23. package/dist/services/acode-bg-manager.d.ts +36 -0
  24. package/dist/services/acode-bg-manager.js +2 -0
  25. package/dist/services/browser-session-manager.js +1 -1
  26. package/dist/services/instance-features.d.ts +5 -0
  27. package/dist/services/instance-features.js +1 -1
  28. package/dist/services/process-detail.d.ts +77 -0
  29. package/dist/services/process-detail.js +32 -0
  30. package/dist/services/process-top.d.ts +53 -0
  31. package/dist/services/process-top.js +50 -0
  32. package/dist/services/session-output.d.ts +10 -0
  33. package/dist/services/session-output.js +3 -3
  34. package/package/acode/dist/await-complete-tool.js +4 -4
  35. package/package/acode/dist/background-command-manager.d.ts +39 -0
  36. package/package/acode/dist/background-command-manager.js +5 -5
  37. package/package/acode/dist/background-command-tools.js +1 -1
  38. package/package/acode/dist/built-in-file-tools.d.ts +25 -1
  39. package/package/acode/dist/built-in-file-tools.js +2 -2
  40. package/package/acode/dist/builtins/shellCommandResult.d.ts +3 -0
  41. package/package/acode/dist/builtins/shellCommandResult.js +1 -1
  42. package/package/acode/dist/builtins/shellCommandRunner.d.ts +2 -0
  43. package/package/acode/dist/builtins/shellCommandRunner.js +1 -1
  44. package/package/acode/dist/image-utils.d.ts +52 -0
  45. package/package/acode/dist/image-utils.js +2 -0
  46. package/package/acode/dist/index.d.ts +1 -0
  47. package/package/acode/dist/index.js +1 -1
  48. package/package/acode/dist/runtime.d.ts +15 -1
  49. package/package/acode/dist/runtime.js +26 -26
  50. package/package/acode/dist/sub-agent-tools.js +30 -26
  51. package/package/acode/dist/sub-agent-types.d.ts +1 -1
  52. package/package/acode/dist/tool-allocation.d.ts +4 -4
  53. package/package/acode/dist/tool-allocation.js +1 -1
  54. package/package/acode/dist/types.d.ts +3 -1
  55. package/package/aws-client-agent-mcp/dist/http-client.d.ts +4 -0
  56. package/package/aws-client-agent-mcp/dist/http-client.js +1 -1
  57. package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +8 -0
  58. package/package/aws-client-agent-mcp/dist/mcp-server.js +28 -25
  59. package/package/aws-client-agent-mcp/dist/mcp-tools.js +6 -6
  60. package/package/aws-client-agent-mcp/dist/memory-store.d.ts +6 -0
  61. package/package/aws-client-agent-mcp/dist/memory-tools.js +2 -2
  62. package/package/aws-client-agent-mcp/dist/types.d.ts +4 -0
  63. package/package/aws-client-agent-mcp/package.json +1 -1
  64. package/package.json +3 -3
  65. package/package/aws-client-agent-mcp/dist/agent-client.test.d.ts +0 -1
  66. package/package/aws-client-agent-mcp/dist/agent-client.test.js +0 -1066
  67. package/package/aws-client-agent-mcp/dist/config.test.d.ts +0 -1
  68. package/package/aws-client-agent-mcp/dist/config.test.js +0 -259
  69. package/package/aws-client-agent-mcp/dist/context-manager.test.d.ts +0 -2
  70. package/package/aws-client-agent-mcp/dist/context-manager.test.js +0 -312
  71. package/package/aws-client-agent-mcp/dist/http-client.test.d.ts +0 -1
  72. package/package/aws-client-agent-mcp/dist/http-client.test.js +0 -808
  73. package/package/aws-client-agent-mcp/dist/launch-config-tools.test.d.ts +0 -1
  74. package/package/aws-client-agent-mcp/dist/launch-config-tools.test.js +0 -110
  75. package/package/aws-client-agent-mcp/dist/logger.test.d.ts +0 -1
  76. package/package/aws-client-agent-mcp/dist/logger.test.js +0 -27
  77. package/package/aws-client-agent-mcp/dist/mcp-server.test.d.ts +0 -1
  78. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +0 -2798
  79. package/package/aws-client-agent-mcp/dist/mcp-tools.test.d.ts +0 -1
  80. package/package/aws-client-agent-mcp/dist/mcp-tools.test.js +0 -152
  81. package/package/aws-client-agent-mcp/dist/memory-store.test.d.ts +0 -1
  82. package/package/aws-client-agent-mcp/dist/memory-store.test.js +0 -664
  83. package/package/aws-client-agent-mcp/dist/message-buffer.test.d.ts +0 -1
  84. package/package/aws-client-agent-mcp/dist/message-buffer.test.js +0 -142
  85. package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.d.ts +0 -1
  86. package/package/aws-client-agent-mcp/dist/parent-lifecycle.test.js +0 -81
  87. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.d.ts +0 -1
  88. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +0 -201
  89. package/package/aws-client-agent-mcp/dist/status-reporter.test.d.ts +0 -1
  90. package/package/aws-client-agent-mcp/dist/status-reporter.test.js +0 -157
  91. package/package/aws-client-agent-mcp/dist/watchdog.test.d.ts +0 -1
  92. package/package/aws-client-agent-mcp/dist/watchdog.test.js +0 -124
  93. package/package/aws-client-agent-mcp/dist/websocket-client.test.d.ts +0 -1
  94. package/package/aws-client-agent-mcp/dist/websocket-client.test.js +0 -725
@@ -27,6 +27,10 @@ type ACodeRuntimeInstance = {
27
27
  removeBuiltInToolsByPrefix?(prefix: string): number;
28
28
 
29
29
  requestMcpToolsRefresh?(serverName?: string): void;
30
+
31
+ setToolPermissionPolicy?(policy: unknown): void;
32
+
33
+ submitToolPermissionDecision?(requestId: string, allowed: boolean): boolean;
30
34
  };
31
35
  type ACodeRuntimeConstructor = new (config: {
32
36
  sessionId: string;
@@ -44,6 +48,8 @@ type ACodeRuntimeConstructor = new (config: {
44
48
  subAgentManager?: SubAgentManager;
45
49
 
46
50
  terminalManager?: TerminalManager;
51
+
52
+ toolPermissionPolicy?: unknown;
47
53
  onFileChange?: (record: {
48
54
  filePath: string;
49
55
  operation: 'CREATE' | 'UPDATE' | 'DELETE';
@@ -71,6 +77,15 @@ export declare class AcodeSdkAdapter extends EventEmitter implements BaseProvide
71
77
  sendMessage(sessionId: string, message: string): Promise<void>;
72
78
  sendConfirmation(_sessionId: string, _accept: boolean): Promise<void>;
73
79
 
80
+ submitToolPermissionDecision(sessionId: string, requestId: string, allowed: boolean): boolean;
81
+
82
+ getPendingToolAuthRequests(sessionId: string): Array<{
83
+ requestId: string;
84
+ capability: string;
85
+ detail: Record<string, unknown>;
86
+ requestedAt: number;
87
+ }>;
88
+
74
89
  sendQuestionAnswer(sessionId: string, answers: Record<string, string>): Promise<void>;
75
90
  abortCurrentTurn(sessionId: string): Promise<void>;
76
91
  resetContext(sessionId: string): Promise<void>;
@@ -1,11 +1,11 @@
1
- import{EventEmitter as z}from"node:events";import I from"node:path";import Y from"node:fs/promises";import{pathToFileURL as X}from"node:url";import{v4 as U}from"uuid";import{getAcodeConfigFile as $,getAcodeSkillsDir as V}from"../config.js";import{getReleasedAcodeEntryPath as Z}from"../services/acode-package.js";import{getInstanceAiConfigDebugLogger as x}from"../utils/instance-ai-config-debug-logger.js";import{redactForLog as R}from"../services/instance-ai-config-service.js";import{subAgentManager as tt}from"../services/sub-agent-manager-instance.js";import{agentPtyManager as et}from"../services/agent-pty-manager.js";import{resolveAcodeProviderConfig as ot}from"./instance-ai-config-resolver.js";import{recordAgentFileChange as st}from"../services/workspaceTaskChangeWatcher.js";import{getBrowserTools as v,closeBrowserSession as it,registerListenerWakeHook as at,unregisterListenerWakeHook as F}from"../services/browser-session-manager.js";import{getDesktopTools as O,getDesktopToolsIfReady as nt}from"../services/desktop-mcp-manager.js";import{BROWSER_FEATURE_KEY as B,DESKTOP_FEATURE_KEY as E,isFeatureEnabled as P}from"../services/instance-features.js";import{isMessagePollingIdleCommand as L,isPollMessageToolDescriptor as rt}from"./acode-tool-names.js";import{IdlePollLoop as ut}from"./idle-poll-loop.js";import{getCanonicalMcpDetail as W,getToolActionInfo as lt}from"./types.js";const dt=process.env.ACODE_COMPACTION_DEBUG==="1";function l(...u){dt&&console.log(...u)}const ct=process.env.ACODE_TOKEN_DEBUG==="1";function w(...u){ct&&console.log("[TokenDebug][Adapter]",...u)}const q=["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(" "),mt=5e3;function D(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 N=D("ACODE_DATA_FLOW_WATCHDOG_MS",12e4),pt=5*6e4,gt=D("ACODE_STUCK_AUTOPOLL_TIMEOUT_MS",5*6e4),ft=250,ht=2e3,H=D("ACODE_SUBMIT_TIMEOUT_MS",12e4),K=3e3,Tt="\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 _t(){return I.resolve(Z())}function At(u){return X(I.resolve(u)).href}const j=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 St(u){if(!u)return j;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:j}const Q=4096;function kt(u){if(!u)return Q;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:Q}function bt(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),n=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&&n===0){const a=h(t.cache_read_input_tokens),r=h(t.cache_creation_input_tokens);a>0||r>0?(s=a,n=r+h(t.input_tokens),i=!0):(s=h(t.cache_hit_tokens),n=h(t.cache_miss_tokens),i=i||t.cache_hit_tokens!==void 0||t.cache_miss_tokens!==void 0)}if(s===0&&n===0){const a=h(t.prompt_tokens_details?.cached_tokens);a>0&&(s=a,n=e>a?e-a:0,i=!0)}return{inputTokens:e,outputTokens:o,...s>0||n>0?{cacheHitTokens:s,cacheMissTokens:n}:{},...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 G(u){return h(u.estimatedContextTokens,u.contextTokens,u.context_tokens,u.contextUsedTokens,u.context_used_tokens)}function Ct(u){return u==="stderr"||u==="mixed"?u:"stdout"}function yt(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 wt(u){return u.failed===!0}function J(u,t,e){if(!u)return e?`${t}/${e}`:t;const o=`${t}__`;return u.startsWith(o)?W(`${t}__${u.slice(o.length)}`):W(u.replace("/","__"))}function Pt(u){return u==="read_file"||u==="edit_file"||u==="write_file"||u==="bash"}function Lt(u){const t=$t(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 $t(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 Jt extends z{constructor(){super(...arguments),this.providerId="acode",this.displayName="ACode",this.sessions=new Map,this.listenerWakeHooks=new Map}async startSession(t,e){const o=e.mcpConfigPath||$(),{resolved:s,configContextLimit:n,supportsImage:i}=await this.resolveAcodeProvider(e,o),a=s.model,r=n??St(a),c=kt(a),p={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0,contextLimit:r,maxOutput:c,cacheHitTokens:0,cacheMissTokens:0}};this.sessions.set(t,{adapterSession:p,config:e,runtime:await this.createRuntime(t,a,e,s,i),autoPollActive:!1}),this.emit("status-change",t,"starting");const m=this.sessions.get(t);if(!m)throw new Error("ACode session was not initialized");const d=()=>{const g=this.sessions.get(t);g&&this.getIdlePollLoop(g).start()};this.listenerWakeHooks.set(t,d),at(e.agentId,d);try{await m.runtime.start(),p.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:m.adapterSession.totalUsage}),e.initialPrompt?.trim()?this.startInitialPromptTurn(t,e.initialPrompt):this.armIdleLoopOrAutoPoll(t)}catch(g){p.status="error",this.emitRuntimeErrorEvent(t,g),this.emit("status-change",t,"error");const f=this.sessions.get(t);throw f?.capabilitiesFallbackTimer&&clearTimeout(f.capabilitiesFallbackTimer),this.sessions.delete(t),F(e.agentId,this.listenerWakeHooks.get(t)),this.listenerWakeHooks.delete(t),g}}async sendMessage(t,e){const o=Date.now();l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l("[ACode] \u25B6\uFE0F sendMessage STARTED"),l(`[ACode] Session: ${t}`),l(`[ACode] Message: ${e.slice(0,100)}${e.length>100?"...":""}`),l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");const s=this.requireSession(t),n=s.adapterSession.status;l(`[ACode] Previous status: ${n}`),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: ${n} \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 a=Date.now();await s.runtime.submit(e);const r=Date.now()-a;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(a){const r=Date.now()-o;console.error(`[ACode] \u274C sendMessage FAILED after ${r}ms:`,a instanceof Error?a.message:String(a)),this.cancelSubmitTimeoutDetection(s),l("[ACode] \u25B6\uFE0F Calling emitRuntimeError with recover=true..."),this.emitRuntimeError(t,a,{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&&(F(o,this.listenerWakeHooks.get(t)),it(o)),this.listenerWakeHooks.delete(t),e.adapterSession.status="terminated",this.sessions.delete(t),this.emit("status-change",t,"terminated")}getConversation(t){return this.sessions.get(t)?.adapterSession.messages??[]}hasSession(t){return this.sessions.has(t)}getProviderSessionId(t){return this.sessions.has(t)?t:void 0}getSessionStatus(t){return this.sessions.get(t)?.adapterSession.status}notifyForceMessage(t){const e=this.sessions.get(t);return e?.runtime?.notifyForceMessage?(e.runtime.notifyForceMessage(),!0):!1}getCapabilities(t){return this.sessions.get(t)?.lastCombinedCapabilities}async resolveAgentAiInfo(t){try{const e=[...this.sessions.values()].find(i=>i.config.agentId===t);if(!e)return;const o=e.config.mcpConfigPath||$(),{resolved:s,supportsImage:n}=await this.resolveAcodeProvider(e.config,o);return s?.model?{model:s.model,supportsImage:n===!0,...s.baseURL?{baseURL:s.baseURL}:{},...s.apiKey?{apiKey:s.apiKey}:{}}:void 0}catch{return}}async applyAgentFeatures(t){if(!t)return;const e=P(t,E),o=P(t,B);for(const[s,n]of this.sessions){if(n.config.agentId!==t)continue;const i=n.runtime;try{if(e){const a=await O(t,await this.resolveAgentAiInfo(t)),r=i.addBuiltInTools?.(a)??0;r>0&&l(`[acode-adapter] session ${s}: ${r} desktop tools hot-added for agent ${t}`)}else{const a=i.removeBuiltInToolsByPrefix?.("desktop_")??0;a>0&&l(`[acode-adapter] session ${s}: ${a} desktop tools hot-removed for agent ${t}`)}}catch(a){l(`[acode-adapter] session ${s}: desktop feature hot-sync failed for agent ${t}:`,a.message)}if(o)try{const a=await v(t,n.config.scopeKey,typeof n.config.browserAllowPrivate=="boolean"?{allowPrivate:n.config.browserAllowPrivate}:void 0),r=i.addBuiltInTools?.(a)??0;r>0&&l(`[acode-adapter] session ${s}: ${r} browser tools hot-added for agent ${t}`)}catch(a){l(`[acode-adapter] session ${s}: browser feature hot-sync failed for agent ${t}:`,a.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 Y.readFile(t,"utf-8"),o=JSON.parse(e),n=o.provider?.default,i=n?.options,a=h(o.contextLimit),r={},c={},p=n?.models;if(p&&typeof p=="object"){for(const[m,d]of Object.entries(p))if(d&&typeof d=="object"){const g=h(d.contextLimit);g>0&&(r[m.trim()]=g);const f=d.modalities?.input,A=Array.isArray(f)&&f.includes("image")||d.supportsImageInput===!0;c[m.trim()]=A===!0}}return{...n?.model?{model:n.model}:{},...i?.baseURL||i?.baseUrl?{baseURL:i?.baseURL||i?.baseUrl}:{},...i?.apiKey?{apiKey:i.apiKey}:{},...a>0?{contextLimit:a}:{},...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?R(t.instanceAiConfig):null,configPath:e}});const n={...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 a=i.contextLimit;if(!a&&i.modelContextLimits){const c=o||t.instanceAiConfig?.model||i.model;c&&i.modelContextLimits[c]&&(a=i.modelContextLimits[c])}const r=ot(n,i,"acode-default");return s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u89E3\u6790\u7ED3\u679C",source:"bridge",details:{instanceConfig:R(n),globalConfigModel:i?.model??null,resolvedModel:r.model,resolvedBaseURL:r.baseURL??null,resolvedApiKey:r.apiKey?"[set]":null,configContextLimit:a,resolvedSource:r.source}}),{resolved:r,configPath:e,perSessionModel:o,configContextLimit:a,supportsImage:i.modelImageSupport?.[r.model]===!0}}async createRuntime(t,e,o,s,n){const i=await this.loadRuntimeConstructor(),a=o.mcpConfigPath||$();let r=[];try{r=await v(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 c=[];if(P(o.agentId??"",B)&&(c=r),o.agentId&&P(o.agentId,E))try{const d=s?.model?{model:s.model,supportsImage:n===!0,...s.baseURL?{baseURL:s.baseURL}:{},...s.apiKey?{apiKey:s.apiKey}:{}}:void 0,g=await O(o.agentId,d);g.length>0&&(c=[...c,...g])}catch(d){l(`[acode-adapter] desktop tools injection skipped for agent ${o.agentId}:`,d.message)}const p=new i({sessionId:t,agentId:o.agentId,workingDirectory:o.workingDirectory,configPath:a,skillsDir:V(),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:tt,terminalManager:et,...o.subAgentModel?{subAgentModel:o.subAgentModel}:{},...o.subAgentEffort?{subAgentEffort:o.subAgentEffort}:{},...o.instanceAiConfig?.effort?{effort:o.instanceAiConfig.effort}:{},onFileChange:d=>st({...d,agentId:o.agentId,workspacePath:o.workingDirectory}),extraBuiltInTools:c,subAgentSuiteTools:r,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:a}}),p.on("event",d=>this.forwardAcodeEvent(d)),p}buildSubAgentSuiteToolProvider(t){return async e=>{if(!t)return[];if(!P(t,E))return[];if(e.length===0)return nt();if(!e.some(n=>{const i=n.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=_t(),e=await import(At(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 n=this.sessions.get(t);n&&(this.cancelSubmitTimeoutDetection(n),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:Ct(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=G(t.data),s=t.data.lastUsage,n=h(s?.inputTokens),i=h(s?.outputTokens),a=h(s?.cacheHitTokens),r=h(s?.cacheMissTokens),c=!!(s&&(n>0||i>0));let p;if(c&&(n>0&&(e.adapterSession.totalUsage.inputTokens=n),i>0&&(e.adapterSession.totalUsage.outputTokens=i),(s?.cacheHitTokens!==void 0||s?.cacheMissTokens!==void 0)&&(e.adapterSession.totalUsage.cacheHitTokens=a,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 g=e.adapterSession.totalUsage.inputTokens;g>0?e.adapterSession.totalUsage.inputTokens=Math.min(o,Math.floor(g*1.5)):e.adapterSession.totalUsage.inputTokens=o,p={...e.adapterSession.totalUsage}}e.adapterSession.status==="compacting"&&(e.adapterSession.status="thinking"),e.adapterSession.status==="waiting_input"&&(e.adapterSession.status="thinking");const d={actionType:"ai_request",actionLabel:"AI\u8BF7\u6C42",actionDetail:this.formatAiRequestActionDetail(t.data)};p&&(d.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,d),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:yt(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=bt(t.data.usage);if(o){const n=e.adapterSession.totalUsage.inputTokens,i=n>0&&o.inputTokens<Math.floor(n*.2)&&!o.hasCache;w(`turn_complete usage: raw=${JSON.stringify(t.data.usage)} parsed=${JSON.stringify(o)} currentInput=${n} threshold20pct=${Math.floor(n*.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}`),wt(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=>rt(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 _=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:_,mcpTools:k};e.cachedMcpCapabilities=b,e.lastMcpCapabilities=b,e.lastCombinedCapabilities={kind:"combined",mcpServers:_,mcpTools:k,skills:e.lastCombinedCapabilities?.skills??[],skillsDir:e.lastCombinedCapabilities?.skillsDir,skillGroups:e.lastCombinedCapabilities?.skillGroups??[]},l(`[cap-diag] MCP capabilities cached: ${_.length} servers, ${k.length} tools`,{sessionId:t.sessionId,servers:_,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 ${K}ms)`,{sessionId:t.sessionId,mcpServers:T.mcpServers,mcpToolsCount:T.mcpTools?.length??0}),e.cachedMcpCapabilities=void 0,e.capabilitiesFallbackTimer=void 0},K);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",n=typeof t.data.tool=="string"?t.data.tool:"",i=s&&n?`${s}__${n}`:n||s,a=t.data.arguments&&typeof t.data.arguments=="object"&&!Array.isArray(t.data.arguments)?t.data.arguments:void 0,r=lt(i,a);e.autoPollActive&&(e.autoPollObservedAnyTool=!0,r.actionType==="idle"&&(e.autoPollObservedPollingTool=!0));const c=Pt(r.actionType)?r.actionDetail||J(void 0,s,n):J(r.actionDetail,s,n),p=typeof t.data.toolUseId=="string"?t.data.toolUseId:void 0,m=a?JSON.stringify(a,null,2):void 0,d={toolName:c,rawToolName:i,actionType:r.actionType,actionLabel:r.actionLabel,actionDetail:c,...m?{actionInput:m}:{},...a?{toolInput:a}:{},...p?{toolUseId:p,actionId:p}:{},metadata:{mcpServer:s}};if(t.data.action==="tool_result"){this.emitProviderEvent(t,"tool_use_end",{...d,toolResult:typeof t.data.result=="string"?t.data.result:JSON.stringify(t.data.result??"")});const f=r.actionType==="idle"&&!Lt(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 A=r.actionType==="idle";this.emit("status-change",t.sessionId,f,A||f==="thinking"?void 0:d);return}const g=r.actionType==="idle"?"waiting_input":"tool_using";e.adapterSession.status=g,r.actionType==="idle"&&(e.idleToolActiveAt=Date.now()),g==="tool_using"&&(this.resetDataFlowWatchdog(t.sessionId,e),e.pendingToolEventData={...d}),this.emitProviderEvent(t,"tool_use_start",d),this.emit("status-change",t.sessionId,g,d);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),n=typeof t.data.skillsDir=="string"?t.data.skillsDir:void 0,a=(Array.isArray(t.data.skillGroups)?t.data.skillGroups:[]).map(m=>{const d=Array.isArray(m.skills)?m.skills:[];return{dir:typeof m.dir=="string"?m.dir:"",exists:m.exists===!0,skills:d.map(g=>({name:String(g.name??""),description:typeof g.description=="string"?g.description:void 0,directory:typeof g.directory=="string"?g.directory:void 0})).filter(g=>g.name)}}).filter(m=>m.dir),r=e.cachedMcpCapabilities??e.lastMcpCapabilities,c=e.cachedMcpCapabilities!==void 0,p={kind:"combined",mcpServers:r?.mcpServers??[],mcpTools:r?.mcpTools??[],skills:s,skillsDir:n,skillGroups:a};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:n}),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=G(t.data),s=h(t.data.contextLimit)||void 0,n=h(t.data.maxOutput)||void 0,i=h(t.data.inputLimit)||void 0,a=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),n!==void 0&&(e.adapterSession.totalUsage.maxOutput=n,r=!0),i!==void 0&&(e.adapterSession.totalUsage.inputLimit=i,r=!0),a!==void 0&&(e.adapterSession.totalUsage.compactionThreshold=a,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,n=s==="start",i=s==="end",a=s==="phase",r=o.parentId,c=o.compactionId;let p,m;if(n){const g=String(o.sizeKB??o.sizeBeforeKB??"?"),f=String(o.messageCount??o.messageCountBefore??"?"),A=o.estimatedTokens,_=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",A!==void 0?m=`\u538B\u7F29\u524D: ~${A} tokens, \u6D88\u606F: ${f}\u6761`+(_?`, \u5B9E\u9645 tokens: ${_} ${k}`:"")+b:m=`\u538B\u7F29\u524D: ${g}KB, \u6D88\u606F: ${f}\u6761`+(_?`, \u5B9E\u9645 tokens: ${_} ${k}`:"")+b}else if(a)p=String(o.phaseLabel??"\u538B\u7F29\u9636\u6BB5"),m=String(o.phaseDetail??"");else{const g=o.tokensBefore,f=o.tokensAfter,A=o.freedTokens,_=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",g!==void 0&&f!==void 0?m=`\u91CA\u653E: ${A??g-f} tokens, ${g} 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, ${_}KB \u2192 ${k}KB, \u7B56\u7565: ${T}`+(C?`, \u88C1\u526A\u5DE5\u5177: ${C}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:"")}n&&(e.adapterSession.status="compacting");const d={actionType:"context_compaction",actionLabel:p,actionDetail:m};if(n&&c)d.actionId=c;else if((a||i)&&c)if(d.parentId=c,a){const g=String(o.phase??"unknown");d.actionId=`${c}-phase-${g}`}else d.actionId=`${c}-end`;a&&r&&!d.parentId&&(d.parentId=r),i&&(e.adapterSession.totalUsage.inputTokens=h(o.tokensAfter),e.adapterSession.totalUsage.outputTokens=0,d.usage={...e.adapterSession.totalUsage}),this.emitProviderEvent(t,"context_compaction",d),this.emit("status-change",t.sessionId,"compacting",d);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 ut({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:Tt,{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 n=(s.autoPollToken??0)+1;s.autoPollToken=n,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!==n){l(`[ACode] submitAutoPollPrompt.finally: token mismatch (my=${n}, 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=ft){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,q,{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 a=e.pendingToolEventData;l(`[ACode] recoverTurnFailure: \u8865\u53D1 tool_use_end (tool: ${a.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:{...a,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 a=e.adapterSession.totalUsage.outputTokens;e.adapterSession.totalUsage.outputTokens=o,l(`[ACode] recoverTurnFailure: \u4F30\u7B97\u8986\u76D6 output tokens ${a} -> ${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 a=this.sessions.get(t);if(!a||a.autoPollActive||this.isTerminalStatus(a.adapterSession.status)){a&&(a.failedTurnRecoveryScheduled=!1);return}a.autoPollTimer=void 0,a.failedTurnRecoveryScheduled=!1,a.idleCommands?.idleInputCommand?this.getIdlePollLoop(a).start():this.submitAutoPollPrompt(t,q,{keepWaitingStatus:!0})},ht)}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,n=!!(o&&L(o))?{actionType:"idle",actionLabel:"\u76D1\u542C\u6D88\u606F",actionDetail:"aws-mcp/poll_message"}:void 0;this.emit("status-change",t,"waiting_input",n),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 n=s.adapterSession.status,i=Date.now();if(n==="waiting_input"){if(s.autoPollActive&&s.autoPollLastActivityAt&&s.idleToolActiveAt===void 0&&i-s.autoPollLastActivityAt>=gt){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(n==="tool_using"){const c=i-(s.watchdogLastDataFlowTime||i);if(c>=pt){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(n!=="thinking")return;const a=i-(s.watchdogSubmitStartTime||i),r=i-(s.watchdogLastDataFlowTime||i);if(N>0&&r>=N){console.warn(`[ACode] CRITICAL: Data flow watchdog triggered after ${Math.round(r/1e3)}s (source: ${o}, session: ${t}, status: ${n}). No stream data received. Forcing recovery via recoverTurnFailure.`),this.executeWatchdogRecovery(t,s,o,"data_flow",r);return}if(H>0&&a>=H){console.warn(`[ACode] CRITICAL: submit timeout detected after ${Math.round(a/1e3)}s (source: ${o}, session: ${t}, status: ${n}). Forcing recovery to prevent permanent state stuck.`),this.executeWatchdogRecovery(t,s,o,"submit_timeout",a);return}},mt)}executeWatchdogRecovery(t,e,o,s,n){l(`[ACode] Watchdog (${s}): calling runtime.abort() after ${Math.round(n/1e3)}s...`);try{e.runtime.abort(),l(`[ACode] Watchdog (${s}): runtime.abort() called`)}catch(i){console.warn(`[ACode] \u26A0\uFE0F Watchdog (${s}): runtime.abort() threw: ${i instanceof Error?i.message:String(i)}`)}this.cancelSubmitTimeoutDetection(e),this.recoverTurnFailure(t,e),console.warn(`[ACode] \u26A0\uFE0F Watchdog (${s}) recovery complete. If abort() could not cancel the underlying operation, submitQueue may still be deadlocked.`)}cancelSubmitTimeoutDetection(t){t.watchdogTimer&&(clearInterval(t.watchdogTimer),t.watchdogTimer=void 0),t.watchdogSubmitStartTime=void 0,t.watchdogLastDataFlowTime=void 0}resetDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now(),e.watchdogSubmitStartTime=Date.now()}startDataFlowWatchdog(t,e){e.watchdogLastDataFlowTime=Date.now()}cancelDataFlowWatchdog(t){t.watchdogLastDataFlowTime=Date.now()}formatAiRequestActionDetail(t){const e=`
1
+ import{EventEmitter as z}from"node:events";import I from"node:path";import Y from"node:fs/promises";import{pathToFileURL as X}from"node:url";import{v4 as U}from"uuid";import{getAcodeConfigFile as $,getAcodeSkillsDir as V}from"../config.js";import{getReleasedAcodeEntryPath as Z}from"../services/acode-package.js";import{getInstanceAiConfigDebugLogger as x}from"../utils/instance-ai-config-debug-logger.js";import{redactForLog as R}from"../services/instance-ai-config-service.js";import{subAgentManager as tt}from"../services/sub-agent-manager-instance.js";import{agentPtyManager as et}from"../services/agent-pty-manager.js";import{resolveAcodeProviderConfig as ot}from"./instance-ai-config-resolver.js";import{recordAgentFileChange as st}from"../services/workspaceTaskChangeWatcher.js";import{getBrowserTools as F,closeBrowserSession as it,registerListenerWakeHook as at,unregisterListenerWakeHook as v}from"../services/browser-session-manager.js";import{getDesktopTools as D,getDesktopToolsIfReady as nt}from"../services/desktop-mcp-manager.js";import{BROWSER_FEATURE_KEY as q,DESKTOP_FEATURE_KEY as O,getAgentToolPermissions as rt,isFeatureEnabled as P,resolveAgentToolPermissions as ut}from"../services/instance-features.js";import{isMessagePollingIdleCommand as L,isPollMessageToolDescriptor as lt}from"./acode-tool-names.js";import{IdlePollLoop as dt}from"./idle-poll-loop.js";import{getCanonicalMcpDetail as B,getToolActionInfo as ct}from"./types.js";const mt=process.env.ACODE_COMPACTION_DEBUG==="1";function l(...u){mt&&console.log(...u)}const pt=process.env.ACODE_TOKEN_DEBUG==="1";function w(...u){pt&&console.log("[TokenDebug][Adapter]",...u)}const W=["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(" "),gt=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 N=E("ACODE_DATA_FLOW_WATCHDOG_MS",12e4),ft=5*6e4,ht=E("ACODE_STUCK_AUTOPOLL_TIMEOUT_MS",5*6e4),Tt=250,At=2e3,H=E("ACODE_SUBMIT_TIMEOUT_MS",12e4),K=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 St(){return I.resolve(Z())}function bt(u){return X(I.resolve(u)).href}const j=128e3,C={"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 yt(u){if(!u)return j;const t=u.toLowerCase().trim();if(C[t])return C[t].contextLimit;const e=Object.keys(C).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?C[e[0]].contextLimit:j}const Q=4096;function kt(u){if(!u)return Q;const t=u.toLowerCase().trim();if(C[t])return C[t].maxOutput;const e=Object.keys(C).filter(o=>t.startsWith(o)).sort((o,s)=>s.length-o.length);return e.length>0?C[e[0]].maxOutput:Q}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 G(u){return h(u.estimatedContextTokens,u.contextTokens,u.context_tokens,u.contextUsedTokens,u.context_used_tokens)}function wt(u){return u==="stderr"||u==="mixed"?u:"stdout"}function Pt(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 Lt(u){return u.failed===!0}function J(u,t,e){if(!u)return e?`${t}/${e}`:t;const o=`${t}__`;return u.startsWith(o)?B(`${t}__${u.slice(o.length)}`):B(u.replace("/","__"))}function $t(u){return u==="read_file"||u==="edit_file"||u==="write_file"||u==="bash"}function xt(u){const t=Dt(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 Dt(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 Yt extends z{constructor(){super(...arguments),this.providerId="acode",this.displayName="ACode",this.sessions=new Map,this.listenerWakeHooks=new Map}async startSession(t,e){const o=e.mcpConfigPath||$(),{resolved:s,configContextLimit:a,supportsImage:i}=await this.resolveAcodeProvider(e,o),n=s.model,r=a??yt(n),d=kt(n),p={sessionId:t,status:"starting",messages:[],createdAt:new Date().toISOString(),totalUsage:{inputTokens:0,outputTokens:0,contextLimit:r,maxOutput:d,cacheHitTokens:0,cacheMissTokens:0}};this.sessions.set(t,{adapterSession:p,config:e,runtime:await this.createRuntime(t,n,e,s,i),autoPollActive:!1}),this.emit("status-change",t,"starting");const m=this.sessions.get(t);if(!m)throw new Error("ACode session was not initialized");const c=()=>{const g=this.sessions.get(t);g&&this.getIdlePollLoop(g).start()};this.listenerWakeHooks.set(t,c),at(e.agentId,c);try{await m.runtime.start(),p.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:m.adapterSession.totalUsage}),e.initialPrompt?.trim()?this.startInitialPromptTurn(t,e.initialPrompt):this.armIdleLoopOrAutoPoll(t)}catch(g){p.status="error",this.emitRuntimeErrorEvent(t,g),this.emit("status-change",t,"error");const f=this.sessions.get(t);throw f?.capabilitiesFallbackTimer&&clearTimeout(f.capabilitiesFallbackTimer),this.sessions.delete(t),v(e.agentId,this.listenerWakeHooks.get(t)),this.listenerWakeHooks.delete(t),g}}async sendMessage(t,e){const o=Date.now();l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l("[ACode] \u25B6\uFE0F sendMessage STARTED"),l(`[ACode] Session: ${t}`),l(`[ACode] Message: ${e.slice(0,100)}${e.length>100?"...":""}`),l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");const s=this.requireSession(t),a=s.adapterSession.status;l(`[ACode] Previous status: ${a}`),this.cancelScheduledAutoPoll(s),s.failedTurnRecoveryScheduled=!1,s.idlePollLoop?.stop();const i={id: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 d=Date.now()-o;l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"),l(`[ACode] \u2705 sendMessage COMPLETED in ${d}ms`),l("[ACode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550")}catch(n){const r=Date.now()-o;console.error(`[ACode] \u274C sendMessage FAILED after ${r}ms:`,n instanceof Error?n.message:String(n)),this.cancelSubmitTimeoutDetection(s),l("[ACode] \u25B6\uFE0F Calling emitRuntimeError with recover=true..."),this.emitRuntimeError(t,n,{recover:!0}),l(`[ACode] Status after error recovery: ${s.adapterSession.status}`)}}async sendConfirmation(t,e){}submitToolPermissionDecision(t,e,o){const s=this.sessions.get(t);if(!s)return!1;s.pendingToolAuthRequests?.delete(e);const a=s.runtime.submitToolPermissionDecision?.(e,o)??!1;return a&&this.startSubmitTimeoutDetection(t,s,"submitToolPermissionDecision"),a}getPendingToolAuthRequests(t){const e=this.sessions.get(t);return e?.pendingToolAuthRequests?Array.from(e.pendingToolAuthRequests.entries()).map(([o,s])=>({requestId:o,capability:s.capability,detail:s.detail,requestedAt:s.requestedAt})):[]}async sendQuestionAnswer(t,e){const o=this.sessions.get(t);if(!o)return;const s=o.pendingQuestionToolCallId;if(!s){l(`[AcodeSdkAdapter] sendQuestionAnswer: no pending question for session ${t}`);return}o.pendingQuestionToolCallId=void 0,o.pendingQuestions=void 0,o.runtime.submitQuestionAnswer&&o.runtime.submitQuestionAnswer(s,e),this.startSubmitTimeoutDetection(t,o,"sendQuestionAnswer")}async abortCurrentTurn(t){await this.requireSession(t).runtime.abort()}async resetContext(t){const e=this.requireSession(t);if(this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.autoPollActive=!1,e.autoPollObservedPollingTool=!1,e.autoPollObservedAssistantText=!1,e.autoPollObservedAnyTool=!1,e.stopAutoPollAfterCurrentTurn=!1,e.idlePollLoop?.stop(),!e.runtime.resetContext)throw new Error("ACode runtime does not support context reset");await e.runtime.resetContext(),e.adapterSession.messages=[];const o=e.adapterSession.totalUsage;if(e.adapterSession.totalUsage={inputTokens:0,outputTokens:0,contextLimit:o.contextLimit,maxOutput:o.maxOutput,inputLimit:o.inputLimit,compactionThreshold:o.compactionThreshold,cacheHitTokens:o.cacheHitTokens,cacheMissTokens:o.cacheMissTokens},e.adapterSession.status="waiting_input",this.emit("status-change",t,"waiting_input",{usage:e.adapterSession.totalUsage}),e.config.initialPrompt?.trim()){this.startInitialPromptTurn(t,e.config.initialPrompt);return}this.armIdleLoopOrAutoPoll(t)}async terminateSession(t){const e=this.sessions.get(t);if(!e)return;this.cancelScheduledAutoPoll(e),e.failedTurnRecoveryScheduled=!1,e.idlePollLoop?.stop(),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),await e.runtime.stop();const o=e.config.agentId;o&&(v(o,this.listenerWakeHooks.get(t)),it(o)),this.listenerWakeHooks.delete(t),e.adapterSession.status="terminated",this.sessions.delete(t),this.emit("status-change",t,"terminated")}getConversation(t){return this.sessions.get(t)?.adapterSession.messages??[]}hasSession(t){return this.sessions.has(t)}getProviderSessionId(t){return this.sessions.has(t)?t:void 0}getSessionStatus(t){return this.sessions.get(t)?.adapterSession.status}notifyForceMessage(t){const e=this.sessions.get(t);return e?.runtime?.notifyForceMessage?(e.runtime.notifyForceMessage(),!0):!1}getCapabilities(t){return this.sessions.get(t)?.lastCombinedCapabilities}async resolveAgentAiInfo(t){try{const e=[...this.sessions.values()].find(i=>i.config.agentId===t);if(!e)return;const o=e.config.mcpConfigPath||$(),{resolved:s,supportsImage:a}=await this.resolveAcodeProvider(e.config,o);return s?.model?{model:s.model,supportsImage:a===!0,...s.baseURL?{baseURL:s.baseURL}:{},...s.apiKey?{apiKey:s.apiKey}:{}}:void 0}catch{return}}async applyAgentFeatures(t){if(!t)return;const e=P(t,O),o=P(t,q);for(const[a,i]of this.sessions){if(i.config.agentId!==t)continue;const n=i.runtime;try{if(e){const r=await D(t,await this.resolveAgentAiInfo(t)),d=n.addBuiltInTools?.(r)??0;d>0&&l(`[acode-adapter] session ${a}: ${d} desktop tools hot-added for agent ${t}`)}else{const r=n.removeBuiltInToolsByPrefix?.("desktop_")??0;r>0&&l(`[acode-adapter] session ${a}: ${r} desktop tools hot-removed for agent ${t}`)}}catch(r){l(`[acode-adapter] session ${a}: desktop feature hot-sync failed for agent ${t}:`,r.message)}if(o)try{const r=await F(t,i.config.scopeKey,typeof i.config.browserAllowPrivate=="boolean"?{allowPrivate:i.config.browserAllowPrivate}:void 0),d=n.addBuiltInTools?.(r)??0;d>0&&l(`[acode-adapter] session ${a}: ${d} browser tools hot-added for agent ${t}`)}catch(r){l(`[acode-adapter] session ${a}: browser feature hot-sync failed for agent ${t}:`,r.message)}}this.syncMcpToolsForAgent(t);const s=ut(t);for(const[a,i]of this.sessions)if(i.config.agentId===t)try{i.runtime.setToolPermissionPolicy?.(s)}catch(n){l(`[acode-adapter] session ${a}: tool permission policy hot-sync failed for agent ${t}:`,n.message)}}syncMcpToolsForAgent(t){if(t){for(const[e,o]of this.sessions)if(o.config.agentId===t)try{o.runtime.requestMcpToolsRefresh?.()}catch(s){l(`[acode-adapter] session ${e}: mcp tools refresh failed for agent ${t}:`,s.message)}}}getPendingQuestion(t){const e=this.sessions.get(t);if(e?.pendingQuestionToolCallId)return{toolCallId:e.pendingQuestionToolCallId,questions:e.pendingQuestions??[]}}cleanup(){for(const t of this.sessions.keys())this.terminateSession(t)}setIdleCommands(t,e){const o=this.sessions.get(t);if(o){if(o.idleCommands=e,this.cancelScheduledAutoPoll(o),e.idleInputCommand&&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 Y.readFile(t,"utf-8"),o=JSON.parse(e),a=o.provider?.default,i=a?.options,n=h(o.contextLimit),r={},d={},p=a?.models;if(p&&typeof p=="object"){for(const[m,c]of Object.entries(p))if(c&&typeof c=="object"){const g=h(c.contextLimit);g>0&&(r[m.trim()]=g);const f=c.modalities?.input,_=Array.isArray(f)&&f.includes("image")||c.supportsImageInput===!0;d[m.trim()]=_===!0}}return{...a?.model?{model:a.model}:{},...i?.baseURL||i?.baseUrl?{baseURL:i?.baseURL||i?.baseUrl}:{},...i?.apiKey?{apiKey:i.apiKey}:{},...n>0?{contextLimit:n}:{},...Object.keys(r).length>0?{modelContextLimits:r}:{},...Object.keys(d).length>0?{modelImageSupport:d}:{}}}catch(e){const o=e instanceof Error?e.message:String(e);return 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?R(t.instanceAiConfig):null,configPath:e}});const a={...t.instanceAiConfig?.model?{model:t.instanceAiConfig.model}:{},...t.instanceAiConfig?.baseURL?{baseURL:t.instanceAiConfig.baseURL}:{},...t.instanceAiConfig?.apiKey?{apiKey:t.instanceAiConfig.apiKey}:{},...t.instanceAiConfig?.effort?{effort:t.instanceAiConfig.effort}:{},...o?{model:o}:{}},i=await this.loadAcodeProviderSection(e);let n=i.contextLimit;if(!n&&i.modelContextLimits){const d=o||t.instanceAiConfig?.model||i.model;d&&i.modelContextLimits[d]&&(n=i.modelContextLimits[d])}const r=ot(a,i,"acode-default");return s.log({level:"info",category:"resolve",message:"resolveAcodeProvider \u89E3\u6790\u7ED3\u679C",source:"bridge",details:{instanceConfig:R(a),globalConfigModel:i?.model??null,resolvedModel:r.model,resolvedBaseURL:r.baseURL??null,resolvedApiKey:r.apiKey?"[set]":null,configContextLimit:n,resolvedSource:r.source}}),{resolved:r,configPath:e,perSessionModel:o,configContextLimit:n,supportsImage:i.modelImageSupport?.[r.model]===!0}}async createRuntime(t,e,o,s,a){const i=await this.loadRuntimeConstructor(),n=o.mcpConfigPath||$();let r=[];try{r=await F(o.agentId,o.scopeKey,typeof o.browserAllowPrivate=="boolean"?{allowPrivate:o.browserAllowPrivate}:void 0)}catch(c){l(`[acode-adapter] browser tools injection skipped for agent ${o.agentId}:`,c.message)}let d=[];if(P(o.agentId??"",q)&&(d=r),o.agentId&&P(o.agentId,O))try{const c=s?.model?{model:s.model,supportsImage:a===!0,...s.baseURL?{baseURL:s.baseURL}:{},...s.apiKey?{apiKey:s.apiKey}:{}}:void 0,g=await D(o.agentId,c);g.length>0&&(d=[...d,...g])}catch(c){l(`[acode-adapter] desktop tools injection skipped for agent ${o.agentId}:`,c.message)}const p=new i({sessionId:t,agentId:o.agentId,workingDirectory:o.workingDirectory,configPath:n,skillsDir:V(),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:tt,terminalManager:et,...o.subAgentModel?{subAgentModel:o.subAgentModel}:{},...o.subAgentEffort?{subAgentEffort:o.subAgentEffort}:{},...o.instanceAiConfig?.effort?{effort:o.instanceAiConfig.effort}:{},onFileChange:c=>st({...c,agentId:o.agentId,workspacePath:o.workingDirectory}),extraBuiltInTools:d,...o.agentId?{toolPermissionPolicy:rt(o.agentId)??void 0}:{},subAgentSuiteTools:r,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:n}}),p.on("event",c=>this.forwardAcodeEvent(c)),p}buildSubAgentSuiteToolProvider(t){return async e=>{if(!t)return[];if(!P(t,O))return[];if(e.length===0)return nt();if(!e.some(a=>{const i=a.trim().replace(/[.*]+$/,"");return i==="desktop"||i.startsWith("desktop.")}))return[];const s=await this.resolveAgentAiInfo(t);return D(t,s)}}async loadRuntimeConstructor(){if(this.runtimeConstructor)return this.runtimeConstructor;const t=St(),e=await import(bt(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:wt(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=G(t.data),s=t.data.lastUsage,a=h(s?.inputTokens),i=h(s?.outputTokens),n=h(s?.cacheHitTokens),r=h(s?.cacheMissTokens),d=!!(s&&(a>0||i>0));let p;if(d&&(a>0&&(e.adapterSession.totalUsage.inputTokens=a),i>0&&(e.adapterSession.totalUsage.outputTokens=i),(s?.cacheHitTokens!==void 0||s?.cacheMissTokens!==void 0)&&(e.adapterSession.totalUsage.cacheHitTokens=n,e.adapterSession.totalUsage.cacheMissTokens=r),p={...e.adapterSession.totalUsage},w(`ai_request lastUsage applied: ${JSON.stringify(s)} -> totalUsage=${JSON.stringify(e.adapterSession.totalUsage)}`)),!d&&o>0){const g=e.adapterSession.totalUsage.inputTokens;g>0?e.adapterSession.totalUsage.inputTokens=Math.min(o,Math.floor(g*1.5)):e.adapterSession.totalUsage.inputTokens=o,p={...e.adapterSession.totalUsage}}e.adapterSession.status==="compacting"&&(e.adapterSession.status="thinking"),e.adapterSession.status==="waiting_input"&&(e.adapterSession.status="thinking");const c={actionType:"ai_request",actionLabel:"AI\u8BF7\u6C42",actionDetail:this.formatAiRequestActionDetail(t.data)};p&&(c.usage=p),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,c),this.startDataFlowWatchdog(t.sessionId,e),e.adapterSession.status==="thinking"&&e.watchdogSubmitStartTime&&(e.watchdogSubmitStartTime=Date.now()),this.emitProviderEvent(t,"ai_request",{model:typeof t.data.model=="string"?t.data.model:void 0,endpoint:typeof t.data.endpoint=="string"?t.data.endpoint:void 0,requestMethod:typeof t.data.requestMethod=="string"?t.data.requestMethod:void 0,requestUrl:typeof t.data.requestUrl=="string"?t.data.requestUrl:void 0,requestHeaders:Pt(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}`),Lt(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==="tool_permission_request"){this.cancelDataFlowWatchdog(e),this.cancelSubmitTimeoutDetection(e);const o=typeof t.data.requestId=="string"?t.data.requestId:"";o&&(e.pendingToolAuthRequests||(e.pendingToolAuthRequests=new Map),e.pendingToolAuthRequests.set(o,{capability:String(t.data.capability||""),detail:{path:t.data.path,command:t.data.command,cwd:t.data.cwd,operation:t.data.operation},requestedAt:Date.now()})),this.emit("tool-permission-request",{sessionId:t.sessionId,requestId:o,capability:String(t.data.capability||""),agentId:e.config.agentId,detail:{path:t.data.path,command:t.data.command,cwd:t.data.cwd,operation:t.data.operation}});return}if(t.type==="error"){if(t.data.nonFatal){this.resetDataFlowWatchdog(t.sessionId,e),this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")});return}this.cancelDataFlowWatchdog(e),e.adapterSession.status="error",this.emitProviderEvent(t,"error",{text:String(t.data.message||"ACode runtime error")}),this.emit("status-change",t.sessionId,"error"),e.autoPollActive||this.recoverTurnFailure(t.sessionId,e);return}if(t.type==="mcp_status"){if(!t.data.action&&Array.isArray(t.data.tools)){const f=t.data.tools;f.some(T=>lt(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:[],b=f.map(T=>({name:String(T.name??""),source:String(T.source??""),description:String(T.description??"")})).filter(T=>T.name),y={kind:"mcp",mcpServers:A,mcpTools:b};e.cachedMcpCapabilities=y,e.lastMcpCapabilities=y,e.lastCombinedCapabilities={kind:"combined",mcpServers:A,mcpTools:b,skills:e.lastCombinedCapabilities?.skills??[],skillsDir:e.lastCombinedCapabilities?.skillsDir,skillGroups:e.lastCombinedCapabilities?.skillGroups??[]},l(`[cap-diag] MCP capabilities cached: ${A.length} servers, ${b.length} tools`,{sessionId:t.sessionId,servers:A,toolNames:b.map(T=>T.name)}),e.capabilitiesFallbackTimer&&clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=setTimeout(()=>{const T=e.cachedMcpCapabilities;if(!T)return;const k={...T,skills:[],skillGroups:[]};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:k,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(k)}),l(`[cap-diag] MCP capabilities emitted via fallback (skill_status not received within ${K}ms)`,{sessionId:t.sessionId,mcpServers:T.mcpServers,mcpToolsCount:T.mcpTools?.length??0}),e.cachedMcpCapabilities=void 0,e.capabilitiesFallbackTimer=void 0},K);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=ct(i,n);e.autoPollActive&&(e.autoPollObservedAnyTool=!0,r.actionType==="idle"&&(e.autoPollObservedPollingTool=!0));const d=$t(r.actionType)?r.actionDetail||J(void 0,s,a):J(r.actionDetail,s,a),p=typeof t.data.toolUseId=="string"?t.data.toolUseId:void 0,m=n?JSON.stringify(n,null,2):void 0,c={toolName:d,rawToolName:i,actionType:r.actionType,actionLabel:r.actionLabel,actionDetail:d,...m?{actionInput:m}:{},...n?{toolInput:n}:{},...p?{toolUseId:p,actionId:p}:{},metadata:{mcpServer:s}};if(t.data.action==="tool_result"){this.emitProviderEvent(t,"tool_use_end",{...c,toolResult:typeof t.data.result=="string"?t.data.result:JSON.stringify(t.data.result??"")});const f=r.actionType==="idle"&&!xt(t.data.result)?"waiting_input":"thinking";e.adapterSession.status=f,r.actionType==="idle"&&(e.idleToolActiveAt=void 0),this.cancelDataFlowWatchdog(e),e.pendingToolEventData=void 0;const _=r.actionType==="idle";this.emit("status-change",t.sessionId,f,_||f==="thinking"?void 0:c);return}const g=r.actionType==="idle"?"waiting_input":"tool_using";e.adapterSession.status=g,r.actionType==="idle"&&(e.idleToolActiveAt=Date.now()),g==="tool_using"&&(this.resetDataFlowWatchdog(t.sessionId,e),e.pendingToolEventData={...c}),this.emitProviderEvent(t,"tool_use_start",c),this.emit("status-change",t.sessionId,g,c);return}if(t.type==="skill_status"){const s=(Array.isArray(t.data.skills)?t.data.skills:[]).map(m=>({name:String(m.name??""),description:typeof m.description=="string"?m.description:void 0,directory:typeof m.directory=="string"?m.directory:void 0})).filter(m=>m.name),a=typeof t.data.skillsDir=="string"?t.data.skillsDir:void 0,n=(Array.isArray(t.data.skillGroups)?t.data.skillGroups:[]).map(m=>{const c=Array.isArray(m.skills)?m.skills:[];return{dir:typeof m.dir=="string"?m.dir:"",exists:m.exists===!0,skills:c.map(g=>({name:String(g.name??""),description:typeof g.description=="string"?g.description:void 0,directory:typeof g.directory=="string"?g.directory:void 0})).filter(g=>g.name)}}).filter(m=>m.dir),r=e.cachedMcpCapabilities??e.lastMcpCapabilities,d=e.cachedMcpCapabilities!==void 0,p={kind:"combined",mcpServers:r?.mcpServers??[],mcpTools:r?.mcpTools??[],skills:s,skillsDir:a,skillGroups:n};this.emitProviderEvent(t,"runtime_capabilities",{capabilities:p,actionType:"runtime_capabilities",actionLabel:"\u8FD0\u884C\u65F6\u80FD\u529B",actionDetail:JSON.stringify(p)}),e.lastCombinedCapabilities=p,l(`[cap-diag] Combined capabilities emitted ${d?"(startup merge)":"(runtime refresh)"}: ${(r?.mcpTools??[]).length} MCP tools, ${s.length} skills`,{sessionId:t.sessionId,mcpServers:r?.mcpServers??[],skillsDir:a}),e.capabilitiesFallbackTimer&&(clearTimeout(e.capabilitiesFallbackTimer),e.capabilitiesFallbackTimer=void 0),e.cachedMcpCapabilities=void 0;return}if(t.type==="startup_phase"){l(`[ACode] \u{1F680} startup_phase swallowed: ${String(t.data.phase??"unknown")}`,{sessionId:t.sessionId});return}if(t.type==="command_status"){if(t.data.action==="commands_loaded"){l(`[ACode] \u{1F4CB} command_status commands_loaded swallowed: ${typeof t.data.installedCommandCount=="number"?t.data.installedCommandCount:"?"} commands`,{sessionId:t.sessionId});return}this.emitProviderEvent(t,"thinking",{text:JSON.stringify(t.data)});return}if(t.type==="session_started"){const o=G(t.data),s=h(t.data.contextLimit)||void 0,a=h(t.data.maxOutput)||void 0,i=h(t.data.inputLimit)||void 0,n=h(t.data.compactionThreshold)||void 0;let r=!1;o>0&&(e.adapterSession.totalUsage.inputTokens=o,e.adapterSession.totalUsage.outputTokens=0,r=!0),s!==void 0&&(e.adapterSession.totalUsage.contextLimit=s,r=!0),a!==void 0&&(e.adapterSession.totalUsage.maxOutput=a,r=!0),i!==void 0&&(e.adapterSession.totalUsage.inputLimit=i,r=!0),n!==void 0&&(e.adapterSession.totalUsage.compactionThreshold=n,r=!0),r&&this.emit("status-change",t.sessionId,e.adapterSession.status,{usage:e.adapterSession.totalUsage});return}if(t.type==="exit_plan_mode"){e.adapterSession.status="waiting_input";const o=typeof t.data.plan=="string"?t.data.plan:void 0;this.emitProviderEvent(t,"exit_plan_mode",{toolInput:t.data.toolInput&&typeof t.data.toolInput=="object"?t.data.toolInput:void 0,text:o||"\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"}),this.emit("status-change",t.sessionId,"waiting_input",{actionLabel:"\u5BA1\u6279\u8BA1\u5212",actionDetail:o||"\u7B49\u5F85\u7528\u6237\u5BA1\u6279\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"});return}if(t.type==="context_compaction"){const o=t.data,s=o.action,a=s==="start",i=s==="end",n=s==="phase",r=o.parentId,d=o.compactionId;let p,m;if(a){const g=String(o.sizeKB??o.sizeBeforeKB??"?"),f=String(o.messageCount??o.messageCountBefore??"?"),_=o.estimatedTokens,A=o.actualInputTokens?`input=${o.actualInputTokens}`:"",b=o.actualOutputTokens?`output=${o.actualOutputTokens}`:"",y=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} ${b}`:"")+y:m=`\u538B\u7F29\u524D: ${g}KB, \u6D88\u606F: ${f}\u6761`+(A?`, \u5B9E\u9645 tokens: ${A} ${b}`:"")+y}else if(n)p=String(o.phaseLabel??"\u538B\u7F29\u9636\u6BB5"),m=String(o.phaseDetail??"");else{const g=o.tokensBefore,f=o.tokensAfter,_=o.freedTokens,A=String(o.sizeBeforeKB??"?"),b=String(o.sizeAfterKB??"?"),y=String(o.freedKB??"?"),T=String(o.strategy??"?"),k=Number(o.prunedToolMessages??0),S=Number(o.summarizedMessages??0);p="\u538B\u7F29\u5B8C\u6210",g!==void 0&&f!==void 0?m=`\u91CA\u653E: ${_??g-f} tokens, ${g} tokens \u2192 ${f} tokens, \u7B56\u7565: ${T}`+(k?`, \u88C1\u526A\u5DE5\u5177: ${k}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:""):m=`\u91CA\u653E: ${y}KB, ${A}KB \u2192 ${b}KB, \u7B56\u7565: ${T}`+(k?`, \u88C1\u526A\u5DE5\u5177: ${k}\u6761`:"")+(S?`, \u6458\u8981\u5316: ${S}\u6761`:"")}a&&(e.adapterSession.status="compacting");const c={actionType:"context_compaction",actionLabel:p,actionDetail:m};if(a&&d)c.actionId=d;else if((n||i)&&d)if(c.parentId=d,n){const g=String(o.phase??"unknown");c.actionId=`${d}-phase-${g}`}else c.actionId=`${d}-end`;n&&r&&!c.parentId&&(c.parentId=r),i&&(e.adapterSession.totalUsage.inputTokens=h(o.tokensAfter),e.adapterSession.totalUsage.outputTokens=0,c.usage={...e.adapterSession.totalUsage}),this.emitProviderEvent(t,"context_compaction",c),this.emit("status-change",t.sessionId,"compacting",c);return}this.emitProviderEvent(t,"thinking",{text:JSON.stringify(t.data)})}}emitProviderEvent(t,e,o){this.emit("event",{type:e,sessionId:t.sessionId,timestamp:t.timestamp,data:o})}armIdleLoopOrAutoPoll(t){const e=this.sessions.get(t);if(!(!e||this.isTerminalStatus(e.adapterSession.status))){if(e.idleCommands?.idleInputCommand){this.getIdlePollLoop(e).start();return}this.scheduleAutoPoll(t)}}getIdlePollLoop(t){if(t.idlePollLoop)return t.idlePollLoop;const e=t.adapterSession.sessionId;return t.idlePollLoop=new dt({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=Tt){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,W,{keepWaitingStatus:!0}))},e))}cancelScheduledAutoPoll(t){t.autoPollTimer&&(clearTimeout(t.autoPollTimer),t.autoPollTimer=void 0)}isTerminalStatus(t){return t==="completed"||t==="terminated"}emitRuntimeError(t,e,o){const s=this.sessions.get(t);s&&(s.adapterSession.status="error",this.emitRuntimeErrorEvent(t,e),this.emit("status-change",t,"error"),o?.recover&&this.recoverTurnFailure(t,s))}emitRuntimeErrorEvent(t,e){const o=new Date().toISOString();this.emit("event",{type:"error",sessionId:t,timestamp:o,data:{text:e instanceof Error?e.message:String(e)}})}estimateOutputTokensFromMessages(t){const e=t.filter(o=>o.role==="assistant").map(o=>o.content).join("");return Math.max(0,Math.round(e.length/4))}recoverTurnFailure(t,e){if(this.isTerminalStatus(e.adapterSession.status))return;if(e.idleToolActiveAt=void 0,e.pendingToolAuthRequests?.size&&e.pendingToolAuthRequests.clear(),e.adapterSession.status==="tool_using"&&e.pendingToolEventData){const n=e.pendingToolEventData;l(`[ACode] recoverTurnFailure: \u8865\u53D1 tool_use_end (tool: ${n.toolName??"unknown"}) \u5173\u95ED\u65F6\u95F4\u7EBF\u6761\u76EE\uFF0C\u907F\u514D\u6C38\u4E45\u5361\u5728\u5DE5\u5177\u6267\u884C\u72B6\u6001`),this.emit("event",{type:"tool_use_end",sessionId:t,timestamp:new Date().toISOString(),data:{...n,toolResult:"[aborted] \u5DE5\u5177\u6267\u884C\u88AB\u6570\u636E\u6D41\u770B\u95E8\u72D7\u4E2D\u65AD"}}),e.pendingToolEventData=void 0}const o=this.estimateOutputTokensFromMessages(e.adapterSession.messages);if(o>0){const n=e.adapterSession.totalUsage.outputTokens;e.adapterSession.totalUsage.outputTokens=o,l(`[ACode] recoverTurnFailure: \u4F30\u7B97\u8986\u76D6 output tokens ${n} -> ${o} (totalUsage now: input=${e.adapterSession.totalUsage.inputTokens}, output=${e.adapterSession.totalUsage.outputTokens})`)}const s=e.idleCommands?.idleInputCommand,i=!!(s&&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,W,{keepWaitingStatus:!0})},At)}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>=ht){console.warn(`[ACode] CRITICAL: stuck auto-poll detected after ${Math.round((i-s.autoPollLastActivityAt)/1e3)}s (source: ${o}, session: ${t}): submit queued but no runtime events and no active poll_message tool. Forcing recovery (runtime force-settle will unblock submitQueue).`),this.executeWatchdogRecovery(t,s,o,"stuck_autopoll",i-s.autoPollLastActivityAt);return}s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(a==="tool_using"){const d=i-(s.watchdogLastDataFlowTime||i);if(d>=ft){const p=String(s.pendingToolEventData?.rawToolName??"");if((s.pendingToolAuthRequests?.size??0)>0){console.warn(`[ACode] tool_using elapsed ${Math.round(d/1e3)}s but ${s.pendingToolAuthRequests.size} tool permission request(s) awaiting user decision (session: ${t}), skipping recovery`),s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(p.endsWith("await_complete")){console.warn(`[ACode] tool_using elapsed ${Math.round(d/1e3)}s but await_complete is running (session: ${t}, tool: ${p}), skipping recovery (sub-agents have their own watchdogs, bg commands have timeout limits)`),s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}if(console.warn(`[ACode] CRITICAL: tool_using slow timeout triggered after ${Math.round(d/1e3)}s (source: ${o}, session: ${t}). Tool execution likely hung. Forcing recovery via recoverTurnFailure.`),s.runtime.detachCurrentToolExecution?.()){console.warn(`[ACode] tool_using slow (${Math.round(d/1e3)}s): detached tool ${p} to background (session: ${t}); waiting for the real tool_result to continue the turn`),s.watchdogSubmitStartTime=i,s.watchdogLastDataFlowTime=i;return}this.executeWatchdogRecovery(t,s,o,"tool_using_slow",d);return}s.watchdogSubmitStartTime=i;return}if(a!=="thinking")return;const n=i-(s.watchdogSubmitStartTime||i),r=i-(s.watchdogLastDataFlowTime||i);if(N>0&&r>=N){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(H>0&&n>=H){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}},gt)}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
- `,n=typeof t.model=="string"?t.model:"",i=typeof t.endpoint=="string"?t.endpoint:typeof t.requestUrl=="string"?t.requestUrl:"",a=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:"",d=[n?`\u6A21\u578B: ${n}`:void 0,i?`\u63A5\u53E3: ${i}`:void 0,a?`\u65B9\u6CD5: ${a}`: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),g=/^(authorization|x-api-key|api-key|cookie|set-cookie|x-auth-token|token)$/i,f=t.requestHeaders,A=f?Object.entries(f).filter(([S])=>!g.test(S)).map(([S,M])=>`${S}: ${M}`).filter(S=>S.trim().length>0):[],_=d.join(`
8
- `)||"ACode \u6B63\u5728\u53D1\u9001 AI \u8BF7\u6C42",k=A.length>0?`${e}${A.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`${_}${k}${C}`}}export{Jt as AcodeSdkAdapter,wt as isFailedAcodeTurnComplete,At as toAcodeEsmImportSpecifier};
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,d=typeof t.messageCount=="number"?t.messageCount:void 0,p=typeof t.toolCount=="number"?t.toolCount:void 0,m=typeof t.lastUserMessagePreview=="string"?t.lastUserMessagePreview:"",c=[a?`\u6A21\u578B: ${a}`:void 0,i?`\u63A5\u53E3: ${i}`:void 0,n?`\u65B9\u6CD5: ${n}`:void 0,r!==void 0?`\u6D41\u5F0F: ${r?"\u662F":"\u5426"}`:void 0,d!==void 0?`\u4E0A\u4E0B\u6587\u6D88\u606F: ${d}`:void 0,p!==void 0?`\u53EF\u7528\u5DE5\u5177: ${p}`:void 0,m?`\u6700\u540E\u8BF7\u6C42: ${m}`:void 0].filter(S=>!!S),g=/^(authorization|x-api-key|api-key|cookie|set-cookie|x-auth-token|token)$/i,f=t.requestHeaders,_=f?Object.entries(f).filter(([S])=>!g.test(S)).map(([S,M])=>`${S}: ${M}`).filter(S=>S.trim().length>0):[],A=c.join(`
8
+ `)||"ACode \u6B63\u5728\u53D1\u9001 AI \u8BF7\u6C42",b=_.length>0?`${e}${_.join(`
9
+ `)}`:"",y=typeof t.requestBody=="string"?t.requestBody:"",T=y.length>6e4?`${y.slice(0,6e4)}
10
+ ...(\u8BF7\u6C42\u4F53\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD)`:y,k=T?`${o}${T}`:"";return`${A}${b}${k}`}}export{Yt as AcodeSdkAdapter,Lt as isFailedAcodeTurnComplete,bt as toAcodeEsmImportSpecifier};
11
11
 
@@ -314,6 +314,15 @@ export declare abstract class BaseProviderAdapter extends EventEmitter {
314
314
 
315
315
  abstract sendQuestionAnswer?(sessionId: string, answers: Record<string, string>): Promise<void>;
316
316
 
317
+ abstract submitToolPermissionDecision?(sessionId: string, requestId: string, allowed: boolean): boolean;
318
+
319
+ abstract getPendingToolAuthRequests?(sessionId: string): Array<{
320
+ requestId: string;
321
+ capability: string;
322
+ detail: Record<string, unknown>;
323
+ requestedAt: number;
324
+ }>;
325
+
317
326
  abstract setIdleCommands?(sessionId: string, commands: {
318
327
  idleInputCommand: string;
319
328
  nonInputCommand: string;
@@ -0,0 +1,16 @@
1
+
2
+ import type { ScreencastDiffFrame } from "./types.js";
3
+ export declare const BROWSER_FRAME_MAGIC = 16979;
4
+ export declare const BROWSER_FRAME_VERSION = 1;
5
+ export declare const BROWSER_FRAME_FLAG_TILE = 1;
6
+ export declare const BROWSER_FRAME_FLAG_FULL = 2;
7
+
8
+ export declare const BROWSER_FRAME_FLAG_META = 4;
9
+
10
+ export interface BrowserFrameMeta {
11
+
12
+ url?: string;
13
+ }
14
+
15
+ export declare function isIncrementalBinaryFrame(buf: Buffer | undefined | null): boolean;
16
+ export declare function encodeBrowserFrame(frame: ScreencastDiffFrame, seq: number, meta?: BrowserFrameMeta): Buffer;
@@ -0,0 +1,2 @@
1
+ const f=16979,u=1,a=1,A=2,F=4;function s(t){const i=t?.url?.trim();if(!i)return null;try{const r=Buffer.from(JSON.stringify({url:i}),"utf-8");if(r.length===0||r.length>65535)return null;const e=Buffer.allocUnsafe(2+r.length);return e.writeUInt16BE(r.length,0),r.copy(e,2),e}catch{return null}}function h(t){return!t||t.length<4?!1:t.readUInt16BE(0)===16979&&(t[3]&1)!==0&&(t[3]&2)===0}function d(t,i,r){t.kind==="tile"&&t.tiles.length===0&&(t={kind:"full",width:t.width,height:t.height,data:Buffer.alloc(0)});const e=s(r),B=(t.kind==="full"?2:1)|(e?4:0),_=8+(e?e.length:0),E=Buffer.allocUnsafe(_);if(E.writeUInt16BE(16979,0),E[2]=1,E[3]=B,E.writeUInt32BE(i>>>0,4),e&&e.copy(E,8),t.kind==="full"){const n=Buffer.allocUnsafe(8);return n.writeUInt32BE(t.width,0),n.writeUInt32BE(t.height,4),Buffer.concat([E,n,t.data])}const R=t.tiles.length;let c=0;for(const n of t.tiles)c+=n.data.length;const o=Buffer.allocUnsafe(2+R*12+c);o.writeUInt16BE(R,0);let l=2;for(const n of t.tiles)o.writeUInt16BE(n.x,l),o.writeUInt16BE(n.y,l+2),o.writeUInt16BE(n.w,l+4),o.writeUInt16BE(n.h,l+6),o.writeUInt32BE(n.data.length,l+8),l+=12;for(const n of t.tiles)n.data.copy(o,l),l+=n.data.length;return Buffer.concat([E,o])}export{A as BROWSER_FRAME_FLAG_FULL,F as BROWSER_FRAME_FLAG_META,a as BROWSER_FRAME_FLAG_TILE,f as BROWSER_FRAME_MAGIC,u as BROWSER_FRAME_VERSION,d as encodeBrowserFrame,h as isIncrementalBinaryFrame};
2
+
@@ -1,2 +1,2 @@
1
- import{chromium as E}from"playwright";import{formatBrowserLaunchError as C,installPlaywrightChromium as _,isBrowserMissingError as D}from"./browser-bootstrap.js";import{BrowserProfileStore as $}from"./browser-profile-store.js";import{ListenerStore as b}from"./listener-store.js";import{PageListenerManager as k}from"./page-listener-manager.js";import{ScreencastManager as H}from"./screencast-manager.js";import{buildStealthInitScript as B,STEALTH_LAUNCH_ARGS as L,STEALTH_IGNORE_ARGS as O}from"./stealth.js";import{StepArchive as N}from"./step-archive.js";import{DEFAULT_BROWSER_ID as h,IDLE_CHECK_INTERVAL_MS as R,IDLE_TIMEOUT_MS as K,MAX_PAGES_PER_SESSION as U,MAX_SESSIONS as x,MAX_SESSIONS_PER_AGENT as I,SESSION_TIMEOUT_MS as z,makeBrowserKey as G}from"./types.js";const W=1280,q=800;class V{onSessionClose(e){this.sessionCloseHandlers.push(e)}getSsrfConfig(){return this.config.ssrfConfig}setListenerTriggerHook(e){this.listenerTriggerHook=e}resolveKey(e,s){if(!e||typeof e!="string")throw new Error("scopeKey must be a non-empty string");const i=s&&s.trim()?s.trim():h;if(i.includes("::"))throw new Error(`browserId must not contain "::" (reserved separator): ${i}`);return G(e,i)}constructor(e){this.sessions=new Map,this.idleTimer=null,this.sessionCloseHandlers=[],this.listenerTriggerHook=null,this.config={headless:e?.headless??!0,executablePath:e?.executablePath,viewportWidth:e?.viewportWidth??W,viewportHeight:e?.viewportHeight??q,maxPagesPerSession:e?.maxPagesPerSession??U,maxSessionDurationMs:e?.maxSessionDurationMs??z,idleTimeoutMs:e?.idleTimeoutMs??K,launchArgs:e?.launchArgs,screencastConfig:e?.screencastConfig,archiveConfig:e?.archiveConfig,ssrfConfig:e?.ssrfConfig,userAgent:e?.userAgent,locale:e?.locale,timezone:e?.timezone,profileDir:e?.profileDir,stealth:e?.stealth??!0},this.profileStore=e?.profileDir?new $({profileDir:e.profileDir}):null,this.listenerStore=e?.profileDir?new b({listenerDir:e.profileDir}):null}async launch(e,s,i,t){const r=t??`agent:${e}`,n=this.resolveKey(r,s);if(this.sessions.has(n))throw new Error(`Browser session already exists for ${e}/${s??h}`);if(this.sessions.size>=x)throw new Error(`BrowserPool reached MAX_SESSIONS=${x}; close existing sessions before launching new ones`);let c=0;for(const o of this.sessions.values())o.agentId===e&&(c+=1);if(c>=I)throw new Error(`BrowserPool reached MAX_SESSIONS_PER_AGENT=${I} for agent ${e}; close some browser sessions first`);const l={headless:this.config.headless};this.config.executablePath&&(l.executablePath=this.config.executablePath),this.config.stealth?(l.ignoreDefaultArgs=[...O],l.args=[...L,...this.config.launchArgs??[]]):this.config.launchArgs&&this.config.launchArgs.length>0&&(l.args=this.config.launchArgs);const p=await this.launchChromium(l);let m=this.config.userAgent,y;if(this.config.stealth)try{const o=await p.newBrowserCDPSession(),f=await o.send("Browser.getVersion"),g=f.product?.match(/Chrome\/(\d+)/);g&&(y=g[1]),!m&&f.userAgent&&(m=String(f.userAgent).replace(/HeadlessChrome\/([\d.]+)/g,"Chrome/$1")),await o.detach().catch(()=>{})}catch{}const w={viewport:{width:this.config.viewportWidth,height:this.config.viewportHeight}};if(m&&(w.userAgent=m),this.config.locale&&(w.locale=this.config.locale),this.config.timezone&&(w.timezoneId=this.config.timezone),this.profileStore)try{let o=await this.profileStore.load(r);!o&&r!==e&&(o=await this.profileStore.load(e),o&&await this.profileStore.save(r,o)),o&&(w.storageState=o)}catch{}const S=await p.newContext(w).catch(async o=>{throw await p.close().catch(()=>{}),o});this.config.stealth&&await S.addInitScript(B(y??"126")).catch(()=>{});const v=await S.newPage().catch(async o=>{throw await p.close().catch(()=>{}),o}),P=new H(this.config.screencastConfig),M=new N(this.config.archiveConfig),A=new k(e,s?.trim()||h,{onTrigger:(o,f)=>{const g=this.listenerTriggerHook;if(g)try{g(e,s?.trim()||h,o,f)}catch{}},store:this.listenerStore,scopeKey:r});try{await P.start(v)}catch{}try{const o=v;o.screencast?.showActions&&await o.screencast.showActions({cursor:"pointer"})}catch{}const T=Date.now(),a={agentId:e,scopeKey:r,browserId:s?.trim()||h,title:i?.trim()||"\u6D4F\u89C8\u5668\u4F1A\u8BDD",browser:p,context:S,pages:[v],activePageIndex:0,screencast:P,archive:M,listenerManager:A,launchedAt:T,lastActivityAt:T,humanTakeover:!1,qualityPreset:"original"};S.on("page",o=>{if(a.pages.length>=this.config.maxPagesPerSession){o.close().catch(()=>{});return}a.pages.push(o);const f=a.pages.length-1;this.setActivePage(e,f,a.browserId).catch(()=>{}),o.on("close",()=>{const g=a.pages.indexOf(o);if(g!==-1){if(a.pages.splice(g,1),a.pages.length===0){this.close(e,a.browserId).catch(()=>{});return}a.activePageIndex>=a.pages.length?a.activePageIndex=a.pages.length-1:g<a.activePageIndex&&(a.activePageIndex-=1)}})});try{await A.attach(v)}catch{}return this.sessions.set(n,a),this.ensureIdleTimer(),this.profileStore&&(a.autoSaveTimer=setInterval(async()=>{try{const o=await a.context.storageState();await this.profileStore.save(a.scopeKey,o)}catch{}},3e4),a.autoSaveTimer.unref&&a.autoSaveTimer.unref()),a}async launchChromium(e){try{return await E.launch(e)}catch(s){if(this.config.executablePath||!D(s))throw s;const i=await _();try{return await E.launch(e)}catch(t){throw C(t instanceof Error?t:new Error(String(t)),i)}}}get(e,s){const i=s&&s.trim()?s.trim():h;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t}async getOrLaunch(e,s,i,t){const r=t??`agent:${e}`,n=this.resolveKey(r,s),c=this.sessions.get(n);return c||this.launch(e,s,i,t)}async close(e,s){const i=s&&s.trim()?s.trim():h;let t,r;for(const[c,l]of this.sessions)if(l.agentId===e&&l.browserId===i){t=c,r=l;break}if(!t||!r)return;const n=r;n.autoSaveTimer&&(clearInterval(n.autoSaveTimer),n.autoSaveTimer=void 0);try{await n.screencast.dispose()}catch{}try{await n.listenerManager.dispose()}catch{}if(n.archive.clear(),this.profileStore)try{const c=await n.context.storageState();await this.profileStore.save(n.scopeKey,c)}catch{}try{await n.browser.close()}catch{}this.sessions.delete(t);for(const c of this.sessionCloseHandlers)try{c(e,i)}catch{}this.sessions.size===0&&this.stopIdleTimer()}async closeByAgent(e){if(!e)return;const s=[];for(const[i,t]of this.sessions)t.agentId===e&&s.push(i);await Promise.all(s.map(i=>{const r=this.sessions.get(i)?.browserId;return this.close(e,r)}))}async collectListenerEvents(e){if(!e)return[];const s=[];for(const i of this.sessions.values()){if(i.agentId!==e)continue;await i.listenerManager.scanExistsAtPoll();const t=i.listenerManager.collectEvents();t.length>0&&s.push(...t)}return s.sort((i,t)=>i.triggeredAt-t.triggeredAt),s}getActivePage(e,s){const i=this.requireSession(e,s),t=i.pages[i.activePageIndex];if(!t||t.isClosed())throw new Error(`No active page for agent ${e}/${i.browserId} (index ${i.activePageIndex})`);return t}async setActivePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);if(t.activePageIndex===s)return t.pages[s];const r=t.pages[t.activePageIndex];if(r&&!r.isClosed())try{await t.screencast.stop(r)}catch{}t.activePageIndex=s;const n=t.pages[s];try{await t.screencast.start(n)}catch{}try{await t.listenerManager.reattach(n)}catch{}return this.touchActivity(e,i),n}async newPage(e,s){const i=this.requireSession(e,s);if(i.pages.length>=this.config.maxPagesPerSession)throw new Error(`Agent ${e}/${i.browserId} reached maxPagesPerSession=${this.config.maxPagesPerSession}; close a tab first`);const t=await i.context.newPage();i.pages.push(t);const r=i.pages.length-1;return this.touchActivity(e,s),this.setActivePage(e,r,s)}async closePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);const r=t.pages[s];if(s===t.activePageIndex)try{await t.screencast.stop(r)}catch{}try{await r.close()}catch{}if(t.pages.splice(s,1),t.pages.length===0){await this.close(e,i);return}t.activePageIndex>=t.pages.length?t.activePageIndex=t.pages.length-1:s<t.activePageIndex&&(t.activePageIndex-=1);const n=t.pages[t.activePageIndex];if(!n.isClosed()){try{await t.screencast.start(n)}catch{}try{await t.listenerManager.reattach(n)}catch{}}this.touchActivity(e,i)}listSessions(e){const s=[];for(const i of this.sessions.values()){if(e&&i.agentId!==e)continue;const t=i.pages[i.activePageIndex];let r="",n="";if(t&&!t.isClosed())try{r=t.url()}catch{}s.push({agentId:i.agentId,browserId:i.browserId,title:i.title,pageCount:i.pages.length,activePageIndex:i.activePageIndex,launchedAt:i.launchedAt,lastActivityAt:i.lastActivityAt,currentUrl:r,currentTitle:n})}return s}touchActivity(e,s){const i=s&&s.trim()?s.trim():h;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i){t.lastActivityAt=Date.now();return}}async dispose(){this.stopIdleTimer();const e=Array.from(this.sessions.keys());await Promise.all(e.map(s=>{const i=this.sessions.get(s);return i?this.close(i.agentId,i.browserId):Promise.resolve()}))}requireSession(e,s){const i=s&&s.trim()?s.trim():h;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t;throw new Error(`Browser session not launched for ${e}/${s??h}`)}ensureIdleTimer(){this.idleTimer||this.config.idleTimeoutMs<=0&&this.config.maxSessionDurationMs<=0||(this.idleTimer=setInterval(()=>{this.checkIdleSessions()},R),this.idleTimer.unref&&this.idleTimer.unref())}stopIdleTimer(){this.idleTimer&&(clearInterval(this.idleTimer),this.idleTimer=null)}async checkIdleSessions(){const e=Date.now(),s=[];for(const i of this.sessions.values()){if(i.humanTakeover)continue;const t=e-i.lastActivityAt,r=e-i.launchedAt;if(this.config.idleTimeoutMs>0&&t>this.config.idleTimeoutMs){s.push(i);continue}this.config.maxSessionDurationMs>0&&r>this.config.maxSessionDurationMs&&s.push(i)}await Promise.all(s.map(i=>this.close(i.agentId,i.browserId)))}}let u=null;function se(d){return u||(u=new V(d)),u}async function ie(){u&&(await u.dispose(),u=null)}export{V as BrowserPool,ie as disposeBrowserPool,se as getBrowserPool};
1
+ import{chromium as x}from"playwright";import{formatBrowserLaunchError as _,installPlaywrightChromium as $,isBrowserMissingError as D}from"./browser-bootstrap.js";import{BrowserProfileStore as H}from"./browser-profile-store.js";import{ListenerStore as b}from"./listener-store.js";import{PageListenerManager as k}from"./page-listener-manager.js";import{ScreencastManager as B}from"./screencast-manager.js";import{buildStealthInitScript as L,STEALTH_LAUNCH_ARGS as O,STEALTH_IGNORE_ARGS as N}from"./stealth.js";import{StepArchive as R}from"./step-archive.js";import{DEFAULT_BROWSER_ID as l,IDLE_CHECK_INTERVAL_MS as K,IDLE_TIMEOUT_MS as z,MAX_PAGES_PER_SESSION as U,MAX_SESSIONS as I,MAX_SESSIONS_PER_AGENT as M,SESSION_TIMEOUT_MS as W,makeBrowserKey as G}from"./types.js";const q=1280,V=720;class X{onSessionClose(e){this.sessionCloseHandlers.push(e)}getSsrfConfig(){return this.config.ssrfConfig}setListenerTriggerHook(e){this.listenerTriggerHook=e}resolveKey(e,s){if(!e||typeof e!="string")throw new Error("scopeKey must be a non-empty string");const i=s&&s.trim()?s.trim():l;if(i.includes("::"))throw new Error(`browserId must not contain "::" (reserved separator): ${i}`);return G(e,i)}constructor(e){this.sessions=new Map,this.idleTimer=null,this.sessionCloseHandlers=[],this.listenerTriggerHook=null,this.config={headless:e?.headless??!0,executablePath:e?.executablePath,viewportWidth:e?.viewportWidth??q,viewportHeight:e?.viewportHeight??V,maxPagesPerSession:e?.maxPagesPerSession??U,maxSessionDurationMs:e?.maxSessionDurationMs??W,idleTimeoutMs:e?.idleTimeoutMs??z,launchArgs:e?.launchArgs,screencastConfig:e?.screencastConfig,archiveConfig:e?.archiveConfig,ssrfConfig:e?.ssrfConfig,userAgent:e?.userAgent,locale:e?.locale,timezone:e?.timezone,profileDir:e?.profileDir,stealth:e?.stealth??!0},this.profileStore=e?.profileDir?new H({profileDir:e.profileDir}):null,this.listenerStore=e?.profileDir?new b({listenerDir:e.profileDir}):null}async launch(e,s,i,t){const r=t??`agent:${e}`,n=this.resolveKey(r,s);if(this.sessions.has(n))throw new Error(`Browser session already exists for ${e}/${s??l}`);if(this.sessions.size>=I)throw new Error(`BrowserPool reached MAX_SESSIONS=${I}; close existing sessions before launching new ones`);let c=0;for(const o of this.sessions.values())o.agentId===e&&(c+=1);if(c>=M)throw new Error(`BrowserPool reached MAX_SESSIONS_PER_AGENT=${M} for agent ${e}; close some browser sessions first`);const h={headless:this.config.headless};this.config.executablePath&&(h.executablePath=this.config.executablePath);const d=[`--window-size=${this.config.viewportWidth},${this.config.viewportHeight}`];this.config.stealth?(h.ignoreDefaultArgs=[...N],h.args=[...d,...O,...this.config.launchArgs??[]]):this.config.launchArgs&&this.config.launchArgs.length>0?h.args=[...d,...this.config.launchArgs]:h.args=[...d];const p=await this.launchChromium(h);let m=this.config.userAgent,P;if(this.config.stealth)try{const o=await p.newBrowserCDPSession(),f=await o.send("Browser.getVersion"),g=f.product?.match(/Chrome\/(\d+)/);g&&(P=g[1]),!m&&f.userAgent&&(m=String(f.userAgent).replace(/HeadlessChrome\/([\d.]+)/g,"Chrome/$1")),await o.detach().catch(()=>{})}catch{}const w={viewport:{width:this.config.viewportWidth,height:this.config.viewportHeight}};if(m&&(w.userAgent=m),this.config.locale&&(w.locale=this.config.locale),this.config.timezone&&(w.timezoneId=this.config.timezone),this.profileStore)try{let o=await this.profileStore.load(r);!o&&r!==e&&(o=await this.profileStore.load(e),o&&await this.profileStore.save(r,o)),o&&(w.storageState=o)}catch{}const v=await p.newContext(w).catch(async o=>{throw await p.close().catch(()=>{}),o});this.config.stealth&&await v.addInitScript(L(P??"126")).catch(()=>{});const S=await v.newPage().catch(async o=>{throw await p.close().catch(()=>{}),o}),A=new B(this.config.screencastConfig),C=new R(this.config.archiveConfig),T=new k(e,s?.trim()||l,{onTrigger:(o,f)=>{const g=this.listenerTriggerHook;if(g)try{g(e,s?.trim()||l,o,f)}catch{}},store:this.listenerStore,scopeKey:r});try{await A.start(S)}catch{}try{const o=S;o.screencast?.showActions&&await o.screencast.showActions({cursor:"pointer"})}catch{}const E=Date.now(),a={agentId:e,scopeKey:r,browserId:s?.trim()||l,title:i?.trim()||"\u6D4F\u89C8\u5668\u4F1A\u8BDD",browser:p,context:v,pages:[S],activePageIndex:0,screencast:A,archive:C,listenerManager:T,launchedAt:E,lastActivityAt:E,humanTakeover:!1,qualityPreset:"original"};v.on("page",o=>{if(a.pages.length>=this.config.maxPagesPerSession){o.close().catch(()=>{});return}a.pages.push(o);const f=a.pages.length-1;this.setActivePage(e,f,a.browserId).catch(()=>{}),o.on("close",()=>{const g=a.pages.indexOf(o);if(g!==-1){if(a.pages.splice(g,1),a.pages.length===0){this.close(e,a.browserId).catch(()=>{});return}a.activePageIndex>=a.pages.length?a.activePageIndex=a.pages.length-1:g<a.activePageIndex&&(a.activePageIndex-=1)}})});try{await T.attach(S)}catch{}return this.sessions.set(n,a),this.ensureIdleTimer(),this.profileStore&&(a.autoSaveTimer=setInterval(async()=>{try{const o=await a.context.storageState();await this.profileStore.save(a.scopeKey,o)}catch{}},3e4),a.autoSaveTimer.unref&&a.autoSaveTimer.unref()),a}async launchChromium(e){try{return await x.launch(e)}catch(s){if(this.config.executablePath||!D(s))throw s;const i=await $();try{return await x.launch(e)}catch(t){throw _(t instanceof Error?t:new Error(String(t)),i)}}}get(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t}async getOrLaunch(e,s,i,t){const r=t??`agent:${e}`,n=this.resolveKey(r,s),c=this.sessions.get(n);return c||this.launch(e,s,i,t)}async close(e,s){const i=s&&s.trim()?s.trim():l;let t,r;for(const[c,h]of this.sessions)if(h.agentId===e&&h.browserId===i){t=c,r=h;break}if(!t||!r)return;const n=r;n.autoSaveTimer&&(clearInterval(n.autoSaveTimer),n.autoSaveTimer=void 0);try{await n.screencast.dispose()}catch{}try{await n.listenerManager.dispose()}catch{}if(n.archive.clear(),this.profileStore)try{const c=await n.context.storageState();await this.profileStore.save(n.scopeKey,c)}catch{}try{await n.browser.close()}catch{}this.sessions.delete(t);for(const c of this.sessionCloseHandlers)try{c(e,i)}catch{}this.sessions.size===0&&this.stopIdleTimer()}async closeByAgent(e){if(!e)return;const s=[];for(const[i,t]of this.sessions)t.agentId===e&&s.push(i);await Promise.all(s.map(i=>{const r=this.sessions.get(i)?.browserId;return this.close(e,r)}))}async collectListenerEvents(e){if(!e)return[];const s=[];for(const i of this.sessions.values()){if(i.agentId!==e)continue;await i.listenerManager.scanExistsAtPoll();const t=i.listenerManager.collectEvents();t.length>0&&s.push(...t)}return s.sort((i,t)=>i.triggeredAt-t.triggeredAt),s}getActivePage(e,s){const i=this.requireSession(e,s),t=i.pages[i.activePageIndex];if(!t||t.isClosed())throw new Error(`No active page for agent ${e}/${i.browserId} (index ${i.activePageIndex})`);return t}async setActivePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);if(t.activePageIndex===s)return t.pages[s];const r=t.pages[t.activePageIndex];if(r&&!r.isClosed())try{await t.screencast.stop(r)}catch{}t.activePageIndex=s;const n=t.pages[s];try{await t.screencast.start(n)}catch{}try{await t.listenerManager.reattach(n)}catch{}return this.touchActivity(e,i),n}async newPage(e,s){const i=this.requireSession(e,s);if(i.pages.length>=this.config.maxPagesPerSession)throw new Error(`Agent ${e}/${i.browserId} reached maxPagesPerSession=${this.config.maxPagesPerSession}; close a tab first`);const t=await i.context.newPage();i.pages.push(t);const r=i.pages.length-1;return this.touchActivity(e,s),this.setActivePage(e,r,s)}async closePage(e,s,i){const t=this.requireSession(e,i);if(!Number.isInteger(s)||s<0||s>=t.pages.length)throw new Error(`Page index ${s} out of range (0..${t.pages.length-1}) for agent ${e}/${t.browserId}`);const r=t.pages[s];if(s===t.activePageIndex)try{await t.screencast.stop(r)}catch{}try{await r.close()}catch{}if(t.pages.splice(s,1),t.pages.length===0){await this.close(e,i);return}t.activePageIndex>=t.pages.length?t.activePageIndex=t.pages.length-1:s<t.activePageIndex&&(t.activePageIndex-=1);const n=t.pages[t.activePageIndex];if(!n.isClosed()){try{await t.screencast.start(n)}catch{}try{await t.listenerManager.reattach(n)}catch{}}this.touchActivity(e,i)}listSessions(e){const s=[];for(const i of this.sessions.values()){if(e&&i.agentId!==e)continue;const t=i.pages[i.activePageIndex];let r="",n="";if(t&&!t.isClosed())try{r=t.url()}catch{}s.push({agentId:i.agentId,browserId:i.browserId,title:i.title,pageCount:i.pages.length,activePageIndex:i.activePageIndex,launchedAt:i.launchedAt,lastActivityAt:i.lastActivityAt,currentUrl:r,currentTitle:n})}return s}touchActivity(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i){t.lastActivityAt=Date.now();return}}async dispose(){this.stopIdleTimer();const e=Array.from(this.sessions.keys());await Promise.all(e.map(s=>{const i=this.sessions.get(s);return i?this.close(i.agentId,i.browserId):Promise.resolve()}))}requireSession(e,s){const i=s&&s.trim()?s.trim():l;for(const t of this.sessions.values())if(t.agentId===e&&t.browserId===i)return t;throw new Error(`Browser session not launched for ${e}/${s??l}`)}ensureIdleTimer(){this.idleTimer||this.config.idleTimeoutMs<=0&&this.config.maxSessionDurationMs<=0||(this.idleTimer=setInterval(()=>{this.checkIdleSessions()},K),this.idleTimer.unref&&this.idleTimer.unref())}stopIdleTimer(){this.idleTimer&&(clearInterval(this.idleTimer),this.idleTimer=null)}async checkIdleSessions(){const e=Date.now(),s=[];for(const i of this.sessions.values()){if(i.humanTakeover)continue;const t=e-i.lastActivityAt,r=e-i.launchedAt;if(this.config.idleTimeoutMs>0&&t>this.config.idleTimeoutMs){s.push(i);continue}this.config.maxSessionDurationMs>0&&r>this.config.maxSessionDurationMs&&s.push(i)}await Promise.all(s.map(i=>this.close(i.agentId,i.browserId)))}}let u=null;function ie(y){return u||(u=new X(y)),u}async function re(){u&&(await u.dispose(),u=null)}export{X as BrowserPool,re as disposeBrowserPool,ie as getBrowserPool};
2
2
 
@@ -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)}}}].map(e=>({...e,category:"browser"}))}export{oe as createBuiltInBrowserTools};
1
+ import{resolveSsrfConfig as E,validateUrlWithDns as M}from"./ssrf-guard.js";import{locateElement as q}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,U=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)??U;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 S=new Map;function A(e,t){return`${e}:${t}`}function C(e,t,r){return S.get(A(e,t))?.get(r)}function R(e,t,r){S.set(A(e,t),j(r))}return v.pool.onSessionClose((e,t)=>{S.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),l=o.pages[o.activePageIndex].viewportSize(),c={ok:!0,agentId:t,browserId:o.browserId,title:o.title,viewport:{width:l?.width??0,height:l?.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),l=s.url(),c=Date.now();await s.goto(i,{waitUntil:"domcontentloaded",timeout:X});const a=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:l,afterUrl:a,durationMs:Date.now()-c}),{ok:!0,url:i,finalUrl:a,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,l=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,c=typeof e.name=="string"&&e.name?e.name:void 0,a=typeof e.selector=="string"?e.selector:void 0;if(o===void 0&&i===void 0&&l===void 0&&!a)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 q(s,{ref:o,text:i,role:l,name:c,selector:a,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}`:l!==void 0?`role=${l}`:`selector=${a}`;return I(n,{tool:"browser_click",args:x({ref:o,text:i,role:l,name:c,selector:a,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,l=typeof e.name=="string"&&e.name?e.name:void 0,c=typeof e.selector=="string"?e.selector:void 0,a=typeof e.text=="string"?e.text:"";if(!a)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 q(s,{ref:o,role:i,name:l,selector:c,refFallback:w}),f=s.url(),g=Date.now();d&&await y.fill(""),await y.type(a,{delay:h});const T=s.url();return I(n,{tool:"browser_type",args:x({ref:o,role:i,name:l,selector:c,text:a,clear:d,delay:h,browserId:r}),beforeUrl:f,afterUrl:T,durationMs:Date.now()-g}),{ok:!0,typed:a.length,url:T,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,l=typeof e.role=="string"&&e.role.trim()?e.role.trim():void 0,c=typeof e.name=="string"&&e.name?e.name:void 0,a=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&&l===void 0&&!a)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 q(s,{ref:o,text:i,role:l,name:c,selector:a,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:l,name:c,selector:a,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(),l=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()-l}),{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 l=s.url(),c=Date.now();let a=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,T])=>globalThis.scrollTo(g,T),[w,y]);const f=await s.evaluate(()=>globalThis);a=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);a=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:l,afterUrl:h,durationMs:Date.now()-c}),{ok:!0,scrollX:a,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. The screenshot is returned as an image attachment (inlined into the conversation so you can see it; requires a model with image input). 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"),a=n.viewportSize();return{ok:!0,attachments:[{type:"image",name:`screenshot.${o}`,mediaType:`image/${o}`,base64Data:c}],width:a?.width??0,height:a?.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,35 @@
1
+
2
+ import { type ScreencastDiffFrame } from "./types.js";
3
+
4
+ export declare function getSharpLoadStatus(): string;
5
+ export declare class ScreencastDiffer {
6
+
7
+ private lastRaw;
8
+ private lastWidth;
9
+ private lastHeight;
10
+
11
+ private lastJpeg;
12
+
13
+ private lastJpegWidth;
14
+ private lastJpegHeight;
15
+
16
+ private consecutiveFullFrames;
17
+
18
+ private highDynamicMode;
19
+
20
+ private highDynamicFrameCount;
21
+
22
+ private sharpReady;
23
+ private sharpCheckDone;
24
+ constructor();
25
+
26
+ isSharpReady(): boolean;
27
+
28
+ processFrame(jpegBuffer: Buffer, width: number, height: number, quality: number): Promise<ScreencastDiffFrame | null>;
29
+
30
+ private emitFullFrame;
31
+
32
+ reset(): void;
33
+
34
+ dispose(): void;
35
+ }
@@ -0,0 +1,2 @@
1
+ import{TILE_DIFF_CONFIG as E}from"./types.js";const _=3,I=15;let R=null,S="pending",W=null;async function b(){return R||(W||(W=(async()=>{try{const s=await import("sharp");return R=s.default??s,S="ok",R}catch(s){return S="failed",console.warn("[browser-screencast] sharp \u52A0\u8F7D\u5931\u8D25\uFF0C\u964D\u7EA7\u5168\u5E27\u6A21\u5F0F:",s instanceof Error?s.message:s),null}})()),W)}async function k(){return b()}function P(){return S}function A(s,t,a,e,f,m,o,d){if(!t||t.length!==s.length||a<=0||e<=0)return Number.MAX_SAFE_INTEGER;const h=Math.max(0,f),r=Math.max(0,m),c=Math.min(a,f+o),M=Math.min(e,m+d),H=a*4,{sampleStep:w,diffThreshold:p}=E;let y=0;for(let J=r;J<M;J+=w){const C=J*H;for(let x=h;x<c;x+=w){const F=C+x*4;Math.abs(s[F]-t[F])+Math.abs(s[F+1]-t[F+1])+Math.abs(s[F+2]-t[F+2])>p&&y++}}return y}function N(s,t,a,e,f,m){if(!s||t<=0||f<=0||m<=0)return null;const o=Math.max(0,a),d=Math.max(0,e),h=Math.min(t,a+f),r=Math.min(Math.floor(s.length/4/t),e+m),c=h-o,M=r-d;if(c<=0||M<=0)return null;const H=t*4,w=Buffer.allocUnsafe(c*M*4);for(let p=0;p<M;p++){const y=(d+p)*H+o*4;s.copy(w,p*c*4,y,y+c*4)}return w}async function O(s,t,a){const e=new Array(s.length);let f=0;const m=Math.max(1,Math.min(t,s.length));return await Promise.all(Array.from({length:m},async()=>{for(;;){const o=f++;if(o>=s.length)return;e[o]=await a(s[o])}})),e}class Y{constructor(){this.lastRaw=null,this.lastWidth=0,this.lastHeight=0,this.lastJpeg=null,this.lastJpegWidth=0,this.lastJpegHeight=0,this.consecutiveFullFrames=0,this.highDynamicMode=!1,this.highDynamicFrameCount=0,this.sharpReady=!1,this.sharpCheckDone=!1,k().then(t=>{this.sharpReady=t!==null,this.sharpCheckDone=!0})}isSharpReady(){return this.sharpReady}async processFrame(t,a,e,f){if(!this.sharpCheckDone||!this.sharpReady)return this.emitFullFrame(t,a,e);const m=R;if(!m)return this.emitFullFrame(t,a,e);try{if(this.lastRaw&&this.lastJpeg&&this.lastJpegWidth===a&&this.lastJpegHeight===e&&this.lastJpeg.length===t.length&&this.lastJpeg.equals(t))return null;if(this.highDynamicMode)if(this.highDynamicFrameCount++,this.highDynamicFrameCount<I&&!(this.lastJpegWidth===a&&this.lastJpegHeight===e))this.highDynamicMode=!1;else{if(this.highDynamicFrameCount<I)return this.lastRaw=null,this.lastJpeg=t,this.lastJpegWidth=a,this.lastJpegHeight=e,this.emitFullFrame(t,this.lastWidth||a,this.lastHeight||e);this.highDynamicMode=!1,this.highDynamicFrameCount=0}const o=await m(t).ensureAlpha().raw().toBuffer({resolveWithObject:!0}),d=o.data,h=o.info.width,r=o.info.height;if(!this.lastRaw||this.lastWidth!==h||this.lastHeight!==r)return this.lastRaw=d,this.lastWidth=h,this.lastHeight=r,this.lastJpeg=t,this.lastJpegWidth=a,this.lastJpegHeight=e,this.emitFullFrame(t,h,r);const{tileSize:c,minChangedSamples:M,fullFrameFallbackRatio:H,encodeConcurrency:w}=E,p=Math.max(1,Math.ceil(h/c)),y=Math.max(1,Math.ceil(r/c)),J=p*y,C=[];let x=0;for(let i=0;i<y;i++)for(let u=0;u<p;u++){const l=u*c,n=i*c,g=Math.min(c,h-l),D=Math.min(c,r-n);A(d,this.lastRaw,h,r,l,n,g,D)>=M&&(C.push({x:l,y:n,w:g,h:D}),x++)}if(x===0)return null;if(x>=Math.ceil(J*H))return this.lastRaw=d,this.lastWidth=h,this.lastHeight=r,this.lastJpeg=t,this.lastJpegWidth=a,this.lastJpegHeight=e,this.consecutiveFullFrames++,this.consecutiveFullFrames>=_&&(this.highDynamicMode=!0,this.highDynamicFrameCount=0),this.emitFullFrame(t,h,r);this.consecutiveFullFrames=0,this.highDynamicMode=!1,this.highDynamicFrameCount=0;const F=[];for(let i=0;i<y;i++){let u=0;const l=C.filter(n=>Math.floor(n.y/c)===i);for(l.sort((n,g)=>n.x-g.x);u<l.length;){let n=u;for(;n+1<l.length&&l[n+1].x===l[n].x+l[n].w;)n++;const g=l[u],D=l[n];F.push({x:g.x,y:g.y,w:D.x+D.w-g.x,h:g.h}),u=n+1}}const T=(await O(F,w,async i=>{const u=Math.min(i.w,h-i.x),l=Math.min(i.h,r-i.y),n=N(d,h,i.x,i.y,u,l);if(!n)return null;try{const g=await m(n,{raw:{width:u,height:l,channels:4}}).jpeg({quality:f}).toBuffer();return{x:i.x,y:i.y,w:u,h:l,data:g}}catch{return null}})).filter(i=>i!==null);return T.length===0?null:(this.lastRaw=d,this.lastWidth=h,this.lastHeight=r,this.lastJpeg=t,this.lastJpegWidth=a,this.lastJpegHeight=e,{kind:"tile",width:h,height:r,tiles:T})}catch(o){return console.warn("[browser-screencast] tile diff \u5F02\u5E38\uFF0C\u56DE\u9000\u5168\u5E27:",o),this.emitFullFrame(t,a,e)}}emitFullFrame(t,a,e){return{kind:"full",width:a,height:e,data:t}}reset(){this.lastRaw=null,this.lastWidth=0,this.lastHeight=0,this.lastJpeg=null,this.lastJpegWidth=0,this.lastJpegHeight=0,this.consecutiveFullFrames=0,this.highDynamicMode=!1,this.highDynamicFrameCount=0}dispose(){this.reset()}}export{Y as ScreencastDiffer,P as getSharpLoadStatus};
2
+
@@ -14,7 +14,31 @@ export declare class ScreencastManager {
14
14
  private readonly subscribers;
15
15
 
16
16
  private lastFrame;
17
+
18
+ private differ;
19
+
20
+ private tileMode;
21
+
22
+ private frameSeq;
23
+
24
+ private pipeline;
25
+
26
+ private latestEvent;
27
+
28
+ private draining;
29
+
30
+ private lastProcessStartedAt;
31
+
32
+ private readonly frameIntervalMs;
33
+
34
+ private seedCaptureInFlight;
35
+
36
+ private lastDeviceWidth;
37
+ private lastDeviceHeight;
38
+
39
+ private lastGeometryWarnAt;
17
40
  constructor(config?: ScreencastConfig);
41
+ private enqueue;
18
42
 
19
43
  subscribe(fn: FrameSubscriber): () => void;
20
44
 
@@ -22,6 +46,12 @@ export declare class ScreencastManager {
22
46
 
23
47
  private handleScreencastFrame;
24
48
 
49
+ private drainPendingFrames;
50
+
51
+ private ackFrame;
52
+
53
+ private processScreencastFrame;
54
+
25
55
  stop(page: Page): Promise<void>;
26
56
 
27
57
  stopAll(): Promise<void>;
@@ -38,5 +68,7 @@ export declare class ScreencastManager {
38
68
 
39
69
  forceCaptureAndPush(page: Page): Promise<void>;
40
70
 
71
+ private doForceCaptureAndPush;
72
+
41
73
  dispose(): Promise<void>;
42
74
  }