aws-runtime-bridge 1.9.125 → 1.9.126

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 (39) hide show
  1. package/dist/adapter/AcodeSdkAdapter.js +4 -4
  2. package/dist/adapter/OpencodeSdkAdapter.js +2 -2
  3. package/dist/config.d.ts +8 -2
  4. package/dist/config.js +3 -1
  5. package/dist/index.js +1 -1
  6. package/dist/routes/acode-todos.js +1 -1
  7. package/dist/routes/background-tasks.js +1 -1
  8. package/dist/routes/terminal.js +19 -19
  9. package/dist/services/aws-client-agent-mcp.js +2 -2
  10. package/dist/services/cc-switch-sdk.js +1 -1
  11. package/dist/services/instance-ai-config-service.js +1 -1
  12. package/package/acode/dist/background-command-store.js +1 -1
  13. package/package/acode/dist/builtins/commandArtifacts.js +3 -3
  14. package/package/acode/dist/paths.d.ts +2 -0
  15. package/package/acode/dist/paths.js +2 -0
  16. package/package/acode/dist/runtime.d.ts +2 -0
  17. package/package/acode/dist/runtime.js +20 -20
  18. package/package/acode/dist/sub-agent-manager.d.ts +1 -0
  19. package/package/acode/dist/sub-agent-manager.js +1 -1
  20. package/package/acode/dist/sub-agent-store.js +3 -3
  21. package/package/acode/dist/sub-agent-tools.js +27 -31
  22. package/package/acode/dist/sub-agent-types.d.ts +1 -1
  23. package/package/acode/dist/todo-store.js +1 -1
  24. package/package/acode/dist/tool-allocation.d.ts +18 -4
  25. package/package/acode/dist/tool-allocation.js +1 -1
  26. package/package/acode/dist/types.d.ts +0 -3
  27. package/package/aws-client-agent-mcp/dist/agent-client.d.ts +5 -0
  28. package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
  29. package/package/aws-client-agent-mcp/dist/agent-client.test.js +72 -0
  30. package/package/aws-client-agent-mcp/dist/config.d.ts +2 -0
  31. package/package/aws-client-agent-mcp/dist/config.js +1 -1
  32. package/package/aws-client-agent-mcp/dist/config.test.js +93 -1
  33. package/package/aws-client-agent-mcp/dist/mcp-server.js +3 -3
  34. package/package/aws-client-agent-mcp/dist/mcp-server.test.js +24 -6
  35. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +14 -0
  36. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +1 -1
  37. package/package/aws-client-agent-mcp/dist/runtime-launch-binding.test.js +39 -1
  38. package/package/aws-client-agent-mcp/package.json +1 -1
  39. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
- import{logger as o}from"./logger.js";const u="ws://localhost:8080/ws/agent",C=3e3,A=9e3,m=200,R=3e4,S=1e3,d=-1,v=12*60*60*1e3,I=12e4,U=`http://127.0.0.1:${process.env.AWS_RUNTIME_BRIDGE_PORT||"18081"}`;function i(n){return n.replace(/^((?:https?|wss?):\/\/(?:\[[^\]]+\]|[^/:?#]+):\d+):\d+(?=\/|$)/i,"$1")}function c(n){const t=n.toLowerCase();return t==="localhost"||t==="::1"||t==="[::1]"?"127.0.0.1":t}const a=process.env.AWS_SCHEDULER_BACKEND_PORT?Number(process.env.AWS_SCHEDULER_BACKEND_PORT):8080;function w(n){const t=n.hostname.toLowerCase();return(t==="localhost"||t==="127.0.0.1"||t==="[::1]")&&n.port!==""&&Number(n.port)!==a}function h(n){const t=String(n||"").trim();if(!t)return;const r=i(t);try{const e=new URL(r);return e.protocol==="ws:"?e.protocol="http:":e.protocol==="wss:"?e.protocol="https:":e.protocol=e.protocol.toLowerCase(),e.hostname=c(e.hostname),e.origin}catch{return r.replace(/\/+$/,"")}}function N(n){const t=String(n||"").trim();if(!t)return;const r=i(t);try{const e=new URL(r);return e.protocol==="http:"?e.protocol="ws:":e.protocol==="https:"?e.protocol="wss:":e.protocol!=="ws:"&&e.protocol!=="wss:"&&(e.protocol="ws:"),e.hostname=c(e.hostname),e.pathname=e.pathname.includes("/ws/agent")?e.pathname:`${e.pathname.replace(/\/$/,"")}/ws/agent`,e.search="",e.hash="",e.toString()}catch{const e=h(r);return e?`${e}/ws/agent`.replace(/^http:/,"ws:").replace(/^https:/,"wss:"):void 0}}function T(n){const t=String(n||"").trim();if(!t)return;const r=i(t);try{const e=new URL(r);if(e.protocol==="ws:"?e.protocol="http:":e.protocol==="wss:"?e.protocol="https:":e.protocol!=="http:"&&e.protocol!=="https:"&&(e.protocol="http:"),e.hostname=c(e.hostname),w(e)){const s=e.port;e.port=String(a),L(t,s,a)}return e.pathname=e.pathname.includes("/mcp/call")?e.pathname:`${e.pathname.replace(/\/$/,"")}/mcp/call`,e.search="",e.hash="",e.toString()}catch{const e=h(r);return e?`${e}/mcp/call`:void 0}}let _=!1;function L(n,t,r){_||(_=!0,o.warn("[Config] \u68C0\u6D4B\u5230\u56DE\u73AF\u5730\u5740\u975E\u540E\u7AEF\u7AEF\u53E3\uFF0CMCP HTTP \u8C03\u7528\u6539\u9053\u76F4\u8FDE\u540E\u7AEF: %s -> \u7AEF\u53E3 %s -> %d\uFF08\u5982\u540E\u7AEF\u5B9E\u9645\u4F7F\u7528\u5176\u5B83\u7AEF\u53E3\u8BF7\u8BBE\u7F6E AWS_SCHEDULER_BACKEND_PORT\uFF09",n,t,r))}class P{agentConfig=null;serverConfig=null;loadConfig(){const t=process.env.AWS_WORKSPACE_PATH||process.cwd();this.agentConfig={roleName:process.env.AWS_ROLE_NAME||"",agentId:process.env.AWS_AGENT_ID||void 0,projectId:process.env.AWS_PROJECT_NAME||"default",workspacePath:t,prompt:process.env.AWS_PROMPT||"",describe:""},o.info("[Config] MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\u5DF2\u52A0\u8F7D"),o.info(`[Config] \u5DE5\u4F5C\u76EE\u5F55: ${this.agentConfig.workspacePath}`),o.info(`[Config] \u9879\u76EE\u540D: ${this.agentConfig.projectId}`),this.agentConfig.agentId&&o.info(`[Config] \u9884\u8BBE AgentID: ${this.agentConfig.agentId}`),this.serverConfig={serverUrl:process.env.AWS_SERVER_URL||u,heartbeatInterval:parseInt(process.env.AWS_HEARTBEAT_INTERVAL||String(C),10),heartbeatTimeout:parseInt(process.env.AWS_HEARTBEAT_TIMEOUT||String(A),10),reconnectInterval:m,reconnectMaxInterval:this.parsePositiveIntegerEnv("AWS_RECONNECT_MAX_INTERVAL",R),reconnectJitterMs:this.parsePositiveIntegerEnv("AWS_RECONNECT_JITTER_MS",S),maxReconnectAttempts:d,userId:process.env.AWS_USER_ID||void 0,runtimeBridgeBaseUrl:process.env.AWS_RUNTIME_BRIDGE_BASE_URL||U,mcpHttpUrl:process.env.AWS_MCP_HTTP_URL||void 0,runtimeTokenRefreshIntervalMs:this.parseRuntimeTokenRefreshInterval(),mcpHttpRequestTimeoutMs:this.parsePositiveIntegerEnv("AWS_MCP_HTTP_REQUEST_TIMEOUT_MS",I)},o.info(`[Config] \u670D\u52A1\u5668\u5730\u5740: ${this.serverConfig.serverUrl}`)}applyRuntimeLaunchBinding(t){if(!this.agentConfig||!this.serverConfig)throw new Error("Configuration is not loaded. Call loadConfig() first.");const r=String(t.agentId||"").trim();r&&(this.agentConfig.agentId=r);const e=String(t.workspacePath||"").trim();e&&(this.agentConfig.workspacePath=e);const s=String(t.runtimeAccessToken||"").trim();s&&(this.serverConfig.runtimeAccessToken=s);const p=String(t.runtimeAccessTokenExpiresAt||"").trim();p&&(this.serverConfig.runtimeAccessTokenExpiresAt=p);const f=String(t.userId||"").trim();f&&(this.serverConfig.userId=f);const l=N(t.serverUrl||t.schedulerBaseUrl);l&&(this.serverConfig.serverUrl=l);const E=T(t.mcpHttpUrl);if(E)this.serverConfig.mcpHttpUrl=E;else if(t.schedulerBaseUrl){const g=T(t.schedulerBaseUrl);g&&(this.serverConfig.mcpHttpUrl=g)}o.info(`[Config] \u5DF2\u5E94\u7528 runtime-bridge \u542F\u52A8\u7ED1\u5B9A: agentId=${this.agentConfig.agentId||"(\u7A7A)"}`)}parseRuntimeTokenRefreshInterval(){return this.parsePositiveIntegerEnv("AWS_RUNTIME_TOKEN_REFRESH_INTERVAL_MS",v)}parsePositiveIntegerEnv(t,r){const e=process.env[t];if(!e||!e.trim())return r;const s=Number.parseInt(e,10);return!Number.isFinite(s)||s<=0?(o.warn(`[Config] ${t} \u65E0\u6548\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C ${r}ms`),r):s}updateRuntimeAccessToken(t){if(!this.serverConfig)return!1;const r=t.trim();return!r||r===this.serverConfig.runtimeAccessToken?!1:(this.serverConfig.runtimeAccessToken=r,o.info("[Config] runtimeAccessToken \u5DF2\u66F4\u65B0\uFF08401 \u6062\u590D\uFF09"),!0)}getAgentConfig(){if(!this.agentConfig)throw new Error("Configuration is not loaded. Call loadConfig() first.");return this.agentConfig}getServerConfig(){if(!this.serverConfig)throw new Error("Configuration is not loaded. Call loadConfig() first.");return this.serverConfig}}const D=new P;export{P as ConfigManager,D as configManager};
1
+ import d from"node:fs";import _ from"node:os";import g from"node:path";import{logger as s}from"./logger.js";const A="ws://localhost:8080/ws/agent",C=3e3,R=9e3,v=200,I=3e4,S=1e3,U=-1,w=12*60*60*1e3,M=12e4,h=18081;function N(){const n=String(process.env.AWS_RUNTIME_BRIDGE_BASE_URL||"").trim();if(n)return n;const t=String(process.env.AWS_RUNTIME_BRIDGE_PORT||"").trim();if(t){const e=Number.parseInt(t,10);if(Number.isFinite(e)&&e>=1&&e<=65535)return`http://127.0.0.1:${e}`}const r=L();return r!==null?`http://127.0.0.1:${r}`:`http://127.0.0.1:${h}`}function P(n,t){for(const r of n.split(/\r?\n/)){const e=r.trim();if(!e||e.startsWith("#"))continue;const o=e.search(/[=:]/);if(o<=0||e.slice(0,o).trim()!==t)continue;return e.slice(o+1).trim()||void 0}}function m(){return String(process.env.AWS_TEST_HOME||"").trim()||String(process.env.AWS_RUNTIME_HOME_DIR||"").trim()||_.homedir()}function $(){return String(process.env.ACODE_HOME||"").trim()||g.join(m(),".acode")}function L(){const n=g.join(m(),".aws-bridge","bridge-runtime.properties");let t;try{t=d.readFileSync(n,"utf8")}catch{return null}const r=P(t,"port");if(r===void 0)return null;const e=Number.parseInt(r,10);return Number.isFinite(e)&&e>=1&&e<=65535?e:(s.warn("[Config] %s \u4E2D port \u65E0\u6548\uFF1A%s\uFF0C\u56DE\u9000\u9ED8\u8BA4\u7AEF\u53E3 %d",n,r,h),null)}function a(n){return n.replace(/^((?:https?|wss?):\/\/(?:\[[^\]]+\]|[^/:?#]+):\d+):\d+(?=\/|$)/i,"$1")}function p(n){const t=n.toLowerCase();return t==="localhost"||t==="::1"||t==="[::1]"?"127.0.0.1":t}function E(n){const t=String(n||"").trim();if(!t)return;const r=a(t);try{const e=new URL(r);return e.protocol==="ws:"?e.protocol="http:":e.protocol==="wss:"?e.protocol="https:":e.protocol=e.protocol.toLowerCase(),e.hostname=p(e.hostname),e.origin}catch{return r.replace(/\/+$/,"")}}function H(n){const t=String(n||"").trim();if(!t)return;const r=a(t);try{const e=new URL(r);return e.protocol==="http:"?e.protocol="ws:":e.protocol==="https:"?e.protocol="wss:":e.protocol!=="ws:"&&e.protocol!=="wss:"&&(e.protocol="ws:"),e.hostname=p(e.hostname),e.pathname=e.pathname.includes("/ws/agent")?e.pathname:`${e.pathname.replace(/\/$/,"")}/ws/agent`,e.search="",e.hash="",e.toString()}catch{const e=E(r);return e?`${e}/ws/agent`.replace(/^http:/,"ws:").replace(/^https:/,"wss:"):void 0}}function T(n){const t=String(n||"").trim();if(!t)return;const r=a(t);try{const e=new URL(r);return e.protocol==="ws:"?e.protocol="http:":e.protocol==="wss:"?e.protocol="https:":e.protocol!=="http:"&&e.protocol!=="https:"&&(e.protocol="http:"),e.hostname=p(e.hostname),e.pathname=e.pathname.includes("/mcp/call")?e.pathname:`${e.pathname.replace(/\/$/,"")}/mcp/call`,e.search="",e.hash="",e.toString()}catch{const e=E(r);return e?`${e}/mcp/call`:void 0}}class k{agentConfig=null;serverConfig=null;loadConfig(){const t=process.env.AWS_WORKSPACE_PATH||process.cwd();this.agentConfig={roleName:process.env.AWS_ROLE_NAME||"",agentId:process.env.AWS_AGENT_ID||void 0,projectId:process.env.AWS_PROJECT_NAME||"default",workspacePath:t,prompt:process.env.AWS_PROMPT||"",describe:""},s.info("[Config] MCP \u5BA2\u6237\u7AEF\u914D\u7F6E\u5DF2\u52A0\u8F7D"),s.info(`[Config] \u5DE5\u4F5C\u76EE\u5F55: ${this.agentConfig.workspacePath}`),s.info(`[Config] \u9879\u76EE\u540D: ${this.agentConfig.projectId}`),this.agentConfig.agentId&&s.info(`[Config] \u9884\u8BBE AgentID: ${this.agentConfig.agentId}`),this.serverConfig={serverUrl:process.env.AWS_SERVER_URL||A,heartbeatInterval:parseInt(process.env.AWS_HEARTBEAT_INTERVAL||String(C),10),heartbeatTimeout:parseInt(process.env.AWS_HEARTBEAT_TIMEOUT||String(R),10),reconnectInterval:v,reconnectMaxInterval:this.parsePositiveIntegerEnv("AWS_RECONNECT_MAX_INTERVAL",I),reconnectJitterMs:this.parsePositiveIntegerEnv("AWS_RECONNECT_JITTER_MS",S),maxReconnectAttempts:U,userId:process.env.AWS_USER_ID||void 0,runtimeBridgeBaseUrl:N(),mcpHttpUrl:process.env.AWS_MCP_HTTP_URL||void 0,runtimeTokenRefreshIntervalMs:this.parseRuntimeTokenRefreshInterval(),mcpHttpRequestTimeoutMs:this.parsePositiveIntegerEnv("AWS_MCP_HTTP_REQUEST_TIMEOUT_MS",M)},s.info(`[Config] \u670D\u52A1\u5668\u5730\u5740: ${this.serverConfig.serverUrl}`),s.info(`[Config] Runtime Bridge \u5730\u5740: ${this.serverConfig.runtimeBridgeBaseUrl}`)}applyRuntimeLaunchBinding(t){if(!this.agentConfig||!this.serverConfig)throw new Error("Configuration is not loaded. Call loadConfig() first.");const r=String(t.agentId||"").trim();r&&(this.agentConfig.agentId=r);const e=String(t.workspacePath||"").trim();e&&(this.agentConfig.workspacePath=e);const o=String(t.runtimeAccessToken||"").trim();o&&(this.serverConfig.runtimeAccessToken=o);const i=String(t.runtimeAccessTokenExpiresAt||"").trim();i&&(this.serverConfig.runtimeAccessTokenExpiresAt=i);const c=String(t.userId||"").trim();c&&(this.serverConfig.userId=c);const l=H(t.serverUrl||t.schedulerBaseUrl);l&&(this.serverConfig.serverUrl=l);const f=T(t.mcpHttpUrl);if(f)this.serverConfig.mcpHttpUrl=f;else if(t.schedulerBaseUrl){const u=T(t.schedulerBaseUrl);u&&(this.serverConfig.mcpHttpUrl=u)}s.info(`[Config] \u5DF2\u5E94\u7528 runtime-bridge \u542F\u52A8\u7ED1\u5B9A: agentId=${this.agentConfig.agentId||"(\u7A7A)"}`)}parseRuntimeTokenRefreshInterval(){return this.parsePositiveIntegerEnv("AWS_RUNTIME_TOKEN_REFRESH_INTERVAL_MS",w)}parsePositiveIntegerEnv(t,r){const e=process.env[t];if(!e||!e.trim())return r;const o=Number.parseInt(e,10);return!Number.isFinite(o)||o<=0?(s.warn(`[Config] ${t} \u65E0\u6548\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u503C ${r}ms`),r):o}updateRuntimeAccessToken(t){if(!this.serverConfig)return!1;const r=t.trim();return!r||r===this.serverConfig.runtimeAccessToken?!1:(this.serverConfig.runtimeAccessToken=r,s.info("[Config] runtimeAccessToken \u5DF2\u66F4\u65B0\uFF08401 \u6062\u590D\uFF09"),!0)}getAgentConfig(){if(!this.agentConfig)throw new Error("Configuration is not loaded. Call loadConfig() first.");return this.agentConfig}getServerConfig(){if(!this.serverConfig)throw new Error("Configuration is not loaded. Call loadConfig() first.");return this.serverConfig}}const F=new k;export{k as ConfigManager,F as configManager,$ as resolveAcodeDataHome};
2
2
 
@@ -1,6 +1,9 @@
1
1
  import assert from 'node:assert/strict';
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
2
5
  import { describe, it, beforeEach, afterEach } from 'node:test';
3
- import { ConfigManager } from './config.js';
6
+ import { ConfigManager, resolveAcodeDataHome } from './config.js';
4
7
  describe('ConfigManager', () => {
5
8
  const originalEnv = { ...process.env };
6
9
  let configManager;
@@ -97,6 +100,73 @@ describe('ConfigManager', () => {
97
100
  assert.equal(serverConfig.mcpHttpRequestTimeoutMs, 30000);
98
101
  });
99
102
  });
103
+ describe('runtimeBridgeBaseUrl discovery', () => {
104
+ let tempHome;
105
+ const createdRoots = [];
106
+ function useTempHome() {
107
+ tempHome = mkdtempSync(path.join(os.tmpdir(), 'aws-mcp-home-'));
108
+ createdRoots.push(tempHome);
109
+ // 主目录解析优先读 AWS_TEST_HOME(JS 层读取,不受外层 afterEach
110
+ // 整体替换 process.env 后与真实 OS 环境断开的影响)
111
+ process.env.AWS_TEST_HOME = tempHome;
112
+ delete process.env.AWS_RUNTIME_HOME_DIR;
113
+ delete process.env.AWS_RUNTIME_BRIDGE_BASE_URL;
114
+ }
115
+ function writeBridgeRuntimeProperties(content) {
116
+ const dir = path.join(tempHome, '.aws-bridge');
117
+ mkdirSync(dir, { recursive: true });
118
+ writeFileSync(path.join(dir, 'bridge-runtime.properties'), content, 'utf8');
119
+ }
120
+ afterEach(() => {
121
+ for (const root of createdRoots.splice(0)) {
122
+ rmSync(root, { recursive: true, force: true });
123
+ }
124
+ });
125
+ it('falls back to default bridge port when env and properties file are absent', () => {
126
+ useTempHome();
127
+ configManager.loadConfig();
128
+ assert.equal(configManager.getServerConfig().runtimeBridgeBaseUrl, 'http://127.0.0.1:18081');
129
+ });
130
+ it('discovers bridge port from bridge-runtime.properties', () => {
131
+ useTempHome();
132
+ writeBridgeRuntimeProperties('port=28081\npid=4321\nstartedAt=2026-08-26T00:00:00.000Z\n');
133
+ configManager.loadConfig();
134
+ assert.equal(configManager.getServerConfig().runtimeBridgeBaseUrl, 'http://127.0.0.1:28081');
135
+ });
136
+ it('accepts colon-separated properties entries', () => {
137
+ useTempHome();
138
+ writeBridgeRuntimeProperties('port: 28100\n');
139
+ configManager.loadConfig();
140
+ assert.equal(configManager.getServerConfig().runtimeBridgeBaseUrl, 'http://127.0.0.1:28100');
141
+ });
142
+ it('falls back to default when port entry is invalid', () => {
143
+ useTempHome();
144
+ writeBridgeRuntimeProperties('port=not-a-number\n');
145
+ configManager.loadConfig();
146
+ assert.equal(configManager.getServerConfig().runtimeBridgeBaseUrl, 'http://127.0.0.1:18081');
147
+ });
148
+ it('prefers explicit AWS_RUNTIME_BRIDGE_BASE_URL over the properties file', () => {
149
+ useTempHome();
150
+ writeBridgeRuntimeProperties('port=28081\n');
151
+ process.env.AWS_RUNTIME_BRIDGE_BASE_URL = 'http://127.0.0.1:29099';
152
+ configManager.loadConfig();
153
+ assert.equal(configManager.getServerConfig().runtimeBridgeBaseUrl, 'http://127.0.0.1:29099');
154
+ });
155
+ it('prefers inherited AWS_RUNTIME_BRIDGE_PORT over the properties file', () => {
156
+ useTempHome();
157
+ // 文件来自最后一次落盘的实例(如生产 18081),继承端口代表本次拉起自己的实例(如 dev 28081)
158
+ writeBridgeRuntimeProperties('port=18081\n');
159
+ process.env.AWS_RUNTIME_BRIDGE_PORT = '28081';
160
+ configManager.loadConfig();
161
+ assert.equal(configManager.getServerConfig().runtimeBridgeBaseUrl, 'http://127.0.0.1:28081');
162
+ });
163
+ it('ignores invalid inherited AWS_RUNTIME_BRIDGE_PORT and falls back to default', () => {
164
+ useTempHome();
165
+ process.env.AWS_RUNTIME_BRIDGE_PORT = 'not-a-port';
166
+ configManager.loadConfig();
167
+ assert.equal(configManager.getServerConfig().runtimeBridgeBaseUrl, 'http://127.0.0.1:18081');
168
+ });
169
+ });
100
170
  describe('getAgentConfig', () => {
101
171
  it('throws error when config not loaded', () => {
102
172
  assert.throws(() => configManager.getAgentConfig(), /Configuration is not loaded/);
@@ -142,6 +212,16 @@ describe('ConfigManager', () => {
142
212
  assert.equal(serverConfig.serverUrl, 'ws://127.0.0.1:8080/ws/agent');
143
213
  assert.equal(serverConfig.mcpHttpUrl, 'http://127.0.0.1:8080/mcp/call');
144
214
  });
215
+ it('keeps handed-down loopback dev proxy port without rewriting', () => {
216
+ configManager.loadConfig();
217
+ configManager.applyRuntimeLaunchBinding({
218
+ agentId: 'agent-a',
219
+ schedulerBaseUrl: 'http://localhost:3000',
220
+ });
221
+ const serverConfig = configManager.getServerConfig();
222
+ assert.equal(serverConfig.serverUrl, 'ws://127.0.0.1:3000/ws/agent');
223
+ assert.equal(serverConfig.mcpHttpUrl, 'http://127.0.0.1:3000/mcp/call');
224
+ });
145
225
  it('preserves MCP HTTP subpaths from runtime bridge bindings', () => {
146
226
  configManager.loadConfig();
147
227
  configManager.applyRuntimeLaunchBinding({
@@ -165,3 +245,15 @@ describe('ConfigManager', () => {
165
245
  });
166
246
  });
167
247
  });
248
+ describe('resolveAcodeDataHome', () => {
249
+ it('prefers explicit ACODE_HOME over the resolved home dir', () => {
250
+ process.env.ACODE_HOME = '/tmp/isolated-acode';
251
+ assert.equal(resolveAcodeDataHome(), '/tmp/isolated-acode');
252
+ });
253
+ it('falls back to {home}/.acode based on AWS_TEST_HOME', () => {
254
+ delete process.env.ACODE_HOME;
255
+ delete process.env.AWS_RUNTIME_HOME_DIR;
256
+ process.env.AWS_TEST_HOME = path.join(os.tmpdir(), 'fake-home-x');
257
+ assert.equal(resolveAcodeDataHome(), path.join(process.env.AWS_TEST_HOME, '.acode'));
258
+ });
259
+ });
@@ -1,4 +1,4 @@
1
- import N from"node:fs";import U from"node:os";import M from"node:path";import{Server as J}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as K}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as V,ListToolsRequestSchema as X}from"@modelcontextprotocol/sdk/types.js";import{AgentClient as Z,PollLongPollTimeoutError as Q}from"./agent-client.js";import{MCP_SERVER_NAME as ee,MCP_SERVER_VERSION as te,REGISTER_MAX_RETRIES as O,REGISTER_RETRY_INTERVAL_MS as re,DEGRADED_MODE_ENABLED as H,DEGRADED_MODE_RETRY_INTERVAL_MS as se,DEGRADED_MODE_MAX_RETRIES as ie}from"./constants.js";import{logger as o}from"./logger.js";import{meetingDebugFingerprints as P,meetingDebugLog as v}from"./meeting-debug-log.js";import{MCP_TOOLS as F,MEMORY_TOOL_NAMES as W,LAUNCH_CONFIG_TOOL_NAMES as q,HIRE_TOOL_NAMES as x}from"./mcp-tools.js";import{MessageBuffer as ne}from"./message-buffer.js";import{ContextManager as oe}from"./context-manager.js";import{MemoryCapacityError as ae,MemoryStore as D}from"./memory-store.js";import{ServerMemoryStore as le}from"./server-memory-store.js";const ce=3e3,j=10,G=(()=>{const L=Number.parseInt(process.env.AWS_MCP_FILE_CHANGE_RENOTIFY_COOLDOWN_MS||"",10);return Number.isFinite(L)&&L>0?L:30*6e4})(),Y=500,me=new Set(["<",">",":",'"',"/","\\","|","?","*"]);class S{server;agentClient;messageBuffer;memoryStore;roleMemoryStore;sessionContextMemoryStore;initialRoleName="";hasRoleName=!1;contextManager;isSubAgent;fileChangeNotifyHistory=new Map;featuresCache=null;static FEATURES_CACHE_TTL_MS=5e3;static LAUNCH_ITEM_FEATURE_KEY="agentSys.launchItem";static LAUNCH_TOOLS_ENV_KEY="AWS_LAUNCH_CONFIG_TOOLS";static HIRE_FEATURE_KEY="agentSys.hire";static HIRE_TOOLS_ENV_KEY="AWS_HIRE_TOOLS";leaseRenewalTimer=null;activeLeaseUris=new Set;static LEASE_DURATION_MINUTES=10;static LEASE_RENEW_INTERVAL_MS=6e4;profileSynced=!1;urgentMeetingHydrateRequested=!1;static MEETING_HYDRATE_RETRY_COUNT=3;static MEETING_HYDRATE_RETRY_DELAY_MS=150;static MEETING_HYDRATE_TIMEOUT_REARM_DELAY_MS=1e4;meetingHydrateTimeoutRearmDelayMs=S.MEETING_HYDRATE_TIMEOUT_REARM_DELAY_MS;static MEETING_HYDRATE_POLL_TIMEOUT_MS=65e3;static MEETING_PREEMPTIVE_JOIN_WAIT_MS=45e3;pendingPollMessage=null;syncRoleMemoryStore(e){if(o.info(`[McpServer] syncRoleMemoryStore \u88AB\u8C03\u7528: displayName=${e.displayName}, roleName="${e.roleName??"(\u7A7A)"}", id=${e.id}`),!e.roleName){o.warn(`[McpServer] syncRoleMemoryStore: profile.roleName \u4E3A\u7A7A\uFF0ChasRoleName \u4FDD\u6301 false\u3002profile=${JSON.stringify({id:e.id,displayName:e.displayName,roleName:e.roleName,instanceName:e.instanceName})}`);return}const t=S.sanitizeRoleDirectoryName(e.roleName);this.hasRoleName=!0,t!==this.initialRoleName&&(o.info(`[McpServer] \u89D2\u8272\u76EE\u5F55\u4E0E\u521D\u59CB\u503C\u4E0D\u4E00\u81F4\uFF0C\u91CD\u5EFA roleMemoryStore: "${this.initialRoleName}" -> "${t}"`),this.roleMemoryStore=new D(S.buildRoleMemoryStorePath(t)),this.initialRoleName=t)}maybeSyncPermanentMemoryToServer(){this.doMaybeSyncPermanentMemoryToServer().catch(e=>{o.warn("[McpServer] maybeSyncPermanentMemoryToServer \u5F02\u5E38: %s, stack=%s",e instanceof Error?e.message:String(e),e instanceof Error?e.stack:"(no stack)")})}async doMaybeSyncPermanentMemoryToServer(){o.info("[McpServer] maybeSyncPermanentMemoryToServer \u5F00\u59CB\uFF0CinitialRoleName="+this.initialRoleName);let e;try{e=await this.memoryStore.getStats()}catch(i){o.warn("[McpServer] \u83B7\u53D6 server \u8BB0\u5FC6\u7EDF\u8BA1\u5931\u8D25\uFF0C\u8DF3\u8FC7\u540C\u6B65:",i instanceof Error?i.message:i);return}const t=e.by_type.project_memory?.count??0;o.info(`[McpServer] server memory \u8BA1\u6570=${t}`);let r=[];try{r=await this.roleMemoryStore.listByType("project_memory"),o.info(`[McpServer] \u89D2\u8272\u5B50\u76EE\u5F55\u8BFB\u53D6\u5230 ${r.length} \u6761\u6C38\u4E45\u8BB0\u5FC6`)}catch(i){o.warn("[McpServer] \u8BFB\u53D6\u89D2\u8272\u5B50\u76EE\u5F55\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25:",i instanceof Error?i.message:i)}if(r.length===0){const i=M.dirname(S.buildRoleMemoryStorePath(this.initialRoleName));o.info(`[McpServer] \u5C1D\u8BD5\u4ECE\u9057\u7559\u76EE\u5F55\u8BFB\u53D6: ${i}`);try{const l=await new D(i).listByType("project_memory");l.length>0&&(o.info(`[McpServer] \u4ECE\u9057\u7559\u76EE\u5F55\u8BFB\u53D6\u5230 ${l.length} \u6761\u6C38\u4E45\u8BB0\u5FC6`),r=l)}catch(a){o.warn("[McpServer] \u8BFB\u53D6\u9057\u7559\u76EE\u5F55\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25:",a instanceof Error?a.message:a)}}if(r.length===0){o.info("[McpServer] \u672C\u5730\u4E5F\u65E0\u6C38\u4E45\u8BB0\u5FC6\uFF0C\u8DF3\u8FC7\u540C\u6B65");return}if(t>=r.length){o.info(`[McpServer] \u670D\u52A1\u7AEF\u5DF2\u6709 ${t} \u6761 >= \u672C\u5730 ${r.length} \u6761\uFF0C\u65E0\u9700\u56DE\u586B`);return}o.info(`[McpServer] \u68C0\u6D4B\u5230\u7F3A\u91CF\uFF1A\u672C\u5730 ${r.length} \u6761 > \u670D\u52A1\u7AEF ${t} \u6761\uFF0C\u5F00\u59CB\u589E\u91CF\u56DE\u586B`);let n=0,s=0;for(const i of r)try{await this.memoryStore.createMemory({memory_type:"project_memory",uri:i.uri,title:i.title,content:i.content,keywords:i.keywords,bound_files:i.bound_files,file_md5_map:i.file_md5_map,last_check_time:i.last_check_time}),n++,o.info(`[McpServer] \u540C\u6B65\u6C38\u4E45\u8BB0\u5FC6\u6210\u529F: ${i.uri}`)}catch(a){const l=a instanceof Error?a.message:String(a);l.includes("already exists")||l.includes("Memory already exists")||o.warn(`[McpServer] \u540C\u6B65\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25 (${i.uri}): ${l}`),s++}o.info(`[McpServer] \u6C38\u4E45\u8BB0\u5FC6\u540C\u6B65\u5B8C\u6210: \u6210\u529F ${n}, \u8DF3\u8FC7 ${s}, \u5171 ${r.length}`),await this.reportMemoryStatsToServer()}normalizeBoundFilePath(e){const t=String(e||"").trim().replace(/\\/g,"/");if(!t)return t;const r=t.startsWith("@/")?t.substring(1):t;return r.startsWith("./")?r:`./${r.replace(/^\/+/,"")}`}normalizeBoundFiles(e){return!e||!Array.isArray(e)?e:e.map(t=>this.normalizeBoundFilePath(String(t)))}async validateBoundFilesExist(e){if(!e||e.length===0)return;const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),{access:r}=await import("node:fs/promises"),n=[];for(const s of e)try{const i=M.resolve(t,s);await r(i)}catch{n.push(s)}if(n.length>0)throw new Error(`\u7ED1\u5B9A\u7684\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u786E\u8BA4\u6587\u4EF6\u8DEF\u5F84\u6B63\u786E\u540E\u518D\u91CD\u65B0\u63D0\u4EA4:
1
+ import N from"node:fs";import M from"node:path";import{Server as J}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as K}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as V,ListToolsRequestSchema as X}from"@modelcontextprotocol/sdk/types.js";import{AgentClient as Z,PollLongPollTimeoutError as Q}from"./agent-client.js";import{MCP_SERVER_NAME as ee,MCP_SERVER_VERSION as te,REGISTER_MAX_RETRIES as O,REGISTER_RETRY_INTERVAL_MS as re,DEGRADED_MODE_ENABLED as U,DEGRADED_MODE_RETRY_INTERVAL_MS as se,DEGRADED_MODE_MAX_RETRIES as ie}from"./constants.js";import{resolveAcodeDataHome as H}from"./config.js";import{logger as o}from"./logger.js";import{meetingDebugFingerprints as P,meetingDebugLog as v}from"./meeting-debug-log.js";import{MCP_TOOLS as F,MEMORY_TOOL_NAMES as W,LAUNCH_CONFIG_TOOL_NAMES as q,HIRE_TOOL_NAMES as x}from"./mcp-tools.js";import{MessageBuffer as ne}from"./message-buffer.js";import{ContextManager as oe}from"./context-manager.js";import{MemoryCapacityError as ae,MemoryStore as D}from"./memory-store.js";import{ServerMemoryStore as le}from"./server-memory-store.js";const ce=3e3,j=10,G=(()=>{const L=Number.parseInt(process.env.AWS_MCP_FILE_CHANGE_RENOTIFY_COOLDOWN_MS||"",10);return Number.isFinite(L)&&L>0?L:30*6e4})(),Y=500,me=new Set(["<",">",":",'"',"/","\\","|","?","*"]);class S{server;agentClient;messageBuffer;memoryStore;roleMemoryStore;sessionContextMemoryStore;initialRoleName="";hasRoleName=!1;contextManager;isSubAgent;fileChangeNotifyHistory=new Map;featuresCache=null;static FEATURES_CACHE_TTL_MS=5e3;static LAUNCH_ITEM_FEATURE_KEY="agentSys.launchItem";static LAUNCH_TOOLS_ENV_KEY="AWS_LAUNCH_CONFIG_TOOLS";static HIRE_FEATURE_KEY="agentSys.hire";static HIRE_TOOLS_ENV_KEY="AWS_HIRE_TOOLS";leaseRenewalTimer=null;activeLeaseUris=new Set;static LEASE_DURATION_MINUTES=10;static LEASE_RENEW_INTERVAL_MS=6e4;profileSynced=!1;urgentMeetingHydrateRequested=!1;static MEETING_HYDRATE_RETRY_COUNT=3;static MEETING_HYDRATE_RETRY_DELAY_MS=150;static MEETING_HYDRATE_TIMEOUT_REARM_DELAY_MS=1e4;meetingHydrateTimeoutRearmDelayMs=S.MEETING_HYDRATE_TIMEOUT_REARM_DELAY_MS;static MEETING_HYDRATE_POLL_TIMEOUT_MS=65e3;static MEETING_PREEMPTIVE_JOIN_WAIT_MS=45e3;pendingPollMessage=null;syncRoleMemoryStore(e){if(o.info(`[McpServer] syncRoleMemoryStore \u88AB\u8C03\u7528: displayName=${e.displayName}, roleName="${e.roleName??"(\u7A7A)"}", id=${e.id}`),!e.roleName){o.warn(`[McpServer] syncRoleMemoryStore: profile.roleName \u4E3A\u7A7A\uFF0ChasRoleName \u4FDD\u6301 false\u3002profile=${JSON.stringify({id:e.id,displayName:e.displayName,roleName:e.roleName,instanceName:e.instanceName})}`);return}const t=S.sanitizeRoleDirectoryName(e.roleName);this.hasRoleName=!0,t!==this.initialRoleName&&(o.info(`[McpServer] \u89D2\u8272\u76EE\u5F55\u4E0E\u521D\u59CB\u503C\u4E0D\u4E00\u81F4\uFF0C\u91CD\u5EFA roleMemoryStore: "${this.initialRoleName}" -> "${t}"`),this.roleMemoryStore=new D(S.buildRoleMemoryStorePath(t)),this.initialRoleName=t)}maybeSyncPermanentMemoryToServer(){this.doMaybeSyncPermanentMemoryToServer().catch(e=>{o.warn("[McpServer] maybeSyncPermanentMemoryToServer \u5F02\u5E38: %s, stack=%s",e instanceof Error?e.message:String(e),e instanceof Error?e.stack:"(no stack)")})}async doMaybeSyncPermanentMemoryToServer(){o.info("[McpServer] maybeSyncPermanentMemoryToServer \u5F00\u59CB\uFF0CinitialRoleName="+this.initialRoleName);let e;try{e=await this.memoryStore.getStats()}catch(i){o.warn("[McpServer] \u83B7\u53D6 server \u8BB0\u5FC6\u7EDF\u8BA1\u5931\u8D25\uFF0C\u8DF3\u8FC7\u540C\u6B65:",i instanceof Error?i.message:i);return}const t=e.by_type.project_memory?.count??0;o.info(`[McpServer] server memory \u8BA1\u6570=${t}`);let r=[];try{r=await this.roleMemoryStore.listByType("project_memory"),o.info(`[McpServer] \u89D2\u8272\u5B50\u76EE\u5F55\u8BFB\u53D6\u5230 ${r.length} \u6761\u6C38\u4E45\u8BB0\u5FC6`)}catch(i){o.warn("[McpServer] \u8BFB\u53D6\u89D2\u8272\u5B50\u76EE\u5F55\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25:",i instanceof Error?i.message:i)}if(r.length===0){const i=M.dirname(S.buildRoleMemoryStorePath(this.initialRoleName));o.info(`[McpServer] \u5C1D\u8BD5\u4ECE\u9057\u7559\u76EE\u5F55\u8BFB\u53D6: ${i}`);try{const l=await new D(i).listByType("project_memory");l.length>0&&(o.info(`[McpServer] \u4ECE\u9057\u7559\u76EE\u5F55\u8BFB\u53D6\u5230 ${l.length} \u6761\u6C38\u4E45\u8BB0\u5FC6`),r=l)}catch(a){o.warn("[McpServer] \u8BFB\u53D6\u9057\u7559\u76EE\u5F55\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25:",a instanceof Error?a.message:a)}}if(r.length===0){o.info("[McpServer] \u672C\u5730\u4E5F\u65E0\u6C38\u4E45\u8BB0\u5FC6\uFF0C\u8DF3\u8FC7\u540C\u6B65");return}if(t>=r.length){o.info(`[McpServer] \u670D\u52A1\u7AEF\u5DF2\u6709 ${t} \u6761 >= \u672C\u5730 ${r.length} \u6761\uFF0C\u65E0\u9700\u56DE\u586B`);return}o.info(`[McpServer] \u68C0\u6D4B\u5230\u7F3A\u91CF\uFF1A\u672C\u5730 ${r.length} \u6761 > \u670D\u52A1\u7AEF ${t} \u6761\uFF0C\u5F00\u59CB\u589E\u91CF\u56DE\u586B`);let n=0,s=0;for(const i of r)try{await this.memoryStore.createMemory({memory_type:"project_memory",uri:i.uri,title:i.title,content:i.content,keywords:i.keywords,bound_files:i.bound_files,file_md5_map:i.file_md5_map,last_check_time:i.last_check_time}),n++,o.info(`[McpServer] \u540C\u6B65\u6C38\u4E45\u8BB0\u5FC6\u6210\u529F: ${i.uri}`)}catch(a){const l=a instanceof Error?a.message:String(a);l.includes("already exists")||l.includes("Memory already exists")||o.warn(`[McpServer] \u540C\u6B65\u6C38\u4E45\u8BB0\u5FC6\u5931\u8D25 (${i.uri}): ${l}`),s++}o.info(`[McpServer] \u6C38\u4E45\u8BB0\u5FC6\u540C\u6B65\u5B8C\u6210: \u6210\u529F ${n}, \u8DF3\u8FC7 ${s}, \u5171 ${r.length}`),await this.reportMemoryStatsToServer()}normalizeBoundFilePath(e){const t=String(e||"").trim().replace(/\\/g,"/");if(!t)return t;const r=t.startsWith("@/")?t.substring(1):t;return r.startsWith("./")?r:`./${r.replace(/^\/+/,"")}`}normalizeBoundFiles(e){return!e||!Array.isArray(e)?e:e.map(t=>this.normalizeBoundFilePath(String(t)))}async validateBoundFilesExist(e){if(!e||e.length===0)return;const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),{access:r}=await import("node:fs/promises"),n=[];for(const s of e)try{const i=M.resolve(t,s);await r(i)}catch{n.push(s)}if(n.length>0)throw new Error(`\u7ED1\u5B9A\u7684\u6587\u4EF6\u4E0D\u5B58\u5728\uFF0C\u8BF7\u786E\u8BA4\u6587\u4EF6\u8DEF\u5F84\u6B63\u786E\u540E\u518D\u91CD\u65B0\u63D0\u4EA4:
2
2
  ${n.map(s=>` - ${s}`).join(`
3
3
  `)}`)}toMemoryWriteResult(e){return{id:e.id,uri:e.uri,domain:e.domain,path:e.path,title:e.title,memory_type:e.memory_type,createdAt:e.createdAt,updatedAt:e.updatedAt,version:e.version,keywords:e.keywords,bound_files:e.bound_files}}toMemoryRecordWithMetadata(e){return{id:e.id,uri:e.uri,domain:e.domain,path:e.path,title:e.title,content:e.content,memory_type:e.memory_type,createdAt:e.createdAt,updatedAt:e.updatedAt,version:e.version,keywords:e.keywords,bound_files:e.bound_files,file_md5_map:e.file_md5_map,memory_status:e.memory_status,updating_agent_id:e.updating_agent_id,last_check_time:e.last_check_time}}constructor(){this.agentClient=new Z,this.messageBuffer=new ne,this.initialRoleName=S.resolveRoleMemoryDirectoryName(),this.hasRoleName=!!process.env.AWS_ROLE_NAME;const e=S.resolveMemoryStorePaths();this.memoryStore=new le(this.agentClient),this.roleMemoryStore=new D(e.rolePath),this.sessionContextMemoryStore=new D(M.join(M.dirname(e.rolePath),"..","session_context")),this.isSubAgent=process.env.AWS_SUB_AGENT==="true",this.contextManager=new oe(this.sessionContextMemoryStore),this.server=new J({name:ee,version:te},{capabilities:{tools:{}}}),this.setupHandlers()}setupHandlers(){this.server.setRequestHandler(X,async()=>{let e=this.isSubAgent?F.filter(t=>W.has(t.name)):F;return!this.isSubAgent&&!await this.isLaunchConfigToolsEnabled()&&(e=e.filter(t=>!q.has(t.name))),!this.isSubAgent&&!await this.isHireToolsEnabled()&&(e=e.filter(t=>!x.has(t.name))),{tools:e}}),this.server.setRequestHandler(V,async(e,t)=>{const{name:r,arguments:n}=e.params;try{const s=await this.handleToolCall(r,n,t?.signal);return{content:this.buildToolResultContent(s)}}catch(s){const i=s instanceof Error?s.message:String(s),a=s instanceof ae?{error:i,...s.details}:{error:i};return o.error("[McpServer] \u5DE5\u5177\u8C03\u7528\u5F02\u5E38: tool=%s, error=%s, stack=%s",r,i,s instanceof Error?s.stack:"(no stack)"),{content:[{type:"text",text:JSON.stringify(a,null,2)}],isError:!0}}})}buildToolResultContent(e){if(e&&typeof e=="object"&&!Array.isArray(e)){const t=e;if(typeof t.image=="string"&&typeof t.mimeType=="string")return[{type:"image",data:t.image,mimeType:t.mimeType}]}return[{type:"text",text:JSON.stringify(e,null,2)}]}async handleToolCall(e,t,r){const n=t?._aws_sub_agent===!0,s=t?._aws_silent_poll===!0,i=t?._aws_force_inject===!0,a={...t};delete a._aws_sub_agent,delete a._aws_silent_poll,delete a._aws_force_inject;const l=n?null:this.agentClient.getStatusReporter();l&&!s&&this.safeReportStatus(()=>l.reportToolUse(e,this.sanitizeToolUseArgs(e,a)));try{const m=await this.executeToolCall(e,a,{forceInject:i,signal:r});if(l&&!s){const u=this.stripAutoDelegateForReporting(m);u!==null&&(this.safeReportStatus(()=>l.reportToolResult(u)),this.safeReportStatus(()=>l.reportThinking()))}return m}catch(m){throw l&&!s&&this.safeReportStatus(()=>l.reportToolError(m)),m}}safeReportStatus(e){try{e()}catch(t){o.warn("[McpServer] \u72B6\u6001\u4E0A\u62A5\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF0C\u4E0D\u5F71\u54CD\u5DE5\u5177\u8C03\u7528\uFF09: %s",t instanceof Error?t.message:String(t))}}async executeToolCall(e,t,r={}){if(this.isSubAgent&&!W.has(e))throw new Error(`Sub-agent is not allowed to call tool: ${e}`);if(q.has(e)&&!await this.isLaunchConfigToolsEnabled())throw new Error("\u542F\u52A8\u9879\u7BA1\u7406\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300CAgent \u7CFB\u7EDF\u7BA1\u7406 \u2192 \u542F\u52A8\u9879\u7BA1\u7406\u300D\u540E\u518D\u8C03\u7528 launch_config_* \u5DE5\u5177\uFF09");if(x.has(e)&&!await this.isHireToolsEnabled())throw new Error("\u62DB\u8058\u5DE5\u5177\u80FD\u529B\u672A\u5F00\u542F\uFF08\u8BF7\u5148\u5728\u80FD\u529B\u914D\u7F6E\u4E2D\u5F00\u542F\u300CAgent \u7CFB\u7EDF\u7BA1\u7406 \u2192 \u62DB\u8058\u5DE5\u5177\u300D\u540E\u518D\u8C03\u7528 hire_colleague / hire_colleagues_batch\uFF09");switch(e){case"get_colleague":return await this.agentClient.discoverColleagues();case"get_group_rooms":return await this.agentClient.discoverGroupRooms();case"get_profile":return await this.agentClient.getProfile();case"hire_colleague":return await this.handleHireColleague(t);case"hire_colleagues_batch":return await this.handleHireColleaguesBatch(t);case"send_group":return await this.handleSendGroup(t);case"send_private":return await this.handleSendPrivate(t);case"poll_message":return await this.handlePollMessage(r.signal);case"get_message":return this.handleGetMessage(r.forceInject===!0);case"get_dm_history":return await this.handleGetDmHistory(t);case"get_group_history":return await this.handleGetGroupHistory(t);case"create_meeting":case"leave_meeting":case"end_meeting":case"meeting_speak":case"get_meeting_info":case"my_meetings":return await this.agentClient.callAgentScopedTool(e,t);case"my_task":return await this.agentClient.getMyTasks();case"create_task_instance_comment":return await this.handleCreateTaskInstanceComment(t);case"submit_task_result":return await this.handleSubmitTaskResult(t);case"start_task_rollback":return await this.handleStartTaskRollback(t);case"finish_task_rollback":return await this.handleFinishTaskRollback(t);case"reject_task":return await this.handleRejectTask(t);case"terminate_task":return await this.handleTerminateTask(t);case"create_memory":return await this.handleCreateMemory(t);case"read_memory":return await this.handleReadMemory(t);case"update_memory":return await this.handleUpdateMemory(t);case"delete_memory":return await this.handleDeleteMemory(t);case"search_memory":return await this.handleSearchMemory(t);case"list_memories":return await this.handleListMemories(t);case"get_memory_stats":return await this.handleGetMemoryStats(t);case"list_memories_by_file":return await this.handleListMemoriesByFile(t);case"abandon_memory_update":return await this.handleAbandonMemoryUpdate(t);case"memory_recurrence":return await this.handleMemoryRecurrence(t);case"memory_recurrence_complete":return await this.handleMemoryRecurrenceComplete(t);case"get_debug_log_config":return await this.agentClient.getDebugLogConfig();case"query_debug_logs":return await this.agentClient.queryDebugLogs(t);case"query_file_changes_history":return await this.agentClient.queryFileChangesHistory(t);case"launch_config_list":return await this.agentClient.listLaunchConfigs();case"launch_config_create":return await this.agentClient.createLaunchConfig(t);case"launch_config_update":return await this.agentClient.updateLaunchConfig(t);case"launch_config_delete":return await this.agentClient.deleteLaunchConfig(String(t?.configId??""));case"launch_config_start":return await this.agentClient.startLaunchConfig(t);case"launch_config_restart":return await this.agentClient.restartLaunchConfig(t);case"launch_config_stop":return await this.agentClient.stopLaunchConfig(String(t?.configId??""));case"launch_config_logs":return await this.agentClient.getLaunchLogs(t);case"launch_config_status":return await this.agentClient.listLaunchRuns();default:throw new Error(`Unknown tool: ${e}`)}}async isLaunchConfigToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t?e[S.LAUNCH_ITEM_FEATURE_KEY]===!0:process.env[S.LAUNCH_TOOLS_ENV_KEY]==="1"}async isHireToolsEnabled(){const{features:e,ok:t}=await this.fetchRuntimeFeaturesWithStatus();return t?e[S.HIRE_FEATURE_KEY]===!0:process.env[S.HIRE_TOOLS_ENV_KEY]==="1"}async fetchRuntimeFeaturesWithStatus(){const e=Date.now();if(this.featuresCache&&e-this.featuresCache.fetchedAt<S.FEATURES_CACHE_TTL_MS)return{features:this.featuresCache.features,ok:this.featuresCache.ok};let t={},r=!1;try{t=await this.agentClient.getRuntimeFeatures(),r=!0}catch(n){o.warn("[McpServer] \u83B7\u53D6\u80FD\u529B\u5F00\u5173\u5931\u8D25\uFF0C\u6309\u542F\u52A8\u671F env \u515C\u5E95: %s",n instanceof Error?n.message:String(n))}return this.featuresCache={features:t,ok:r,fetchedAt:e},{features:t,ok:r}}static MEMORY_TYPES=["recently_memory","agent_memory","project_memory","session_context"];static MEMORY_TOOL_NAMES=new Set(["create_memory","read_memory","update_memory","delete_memory","search_memory","list_memories","get_memory_stats","list_memories_by_file","abandon_memory_update","memory_recurrence","memory_recurrence_complete"]);static findAgentsworkDirSync(e){let t=M.resolve(e);for(;;){if(N.existsSync(M.join(t,".agentswork")))return M.join(t,".agentswork");const r=M.dirname(t);if(r===t)break;t=r}return M.join(M.resolve(e),".agentswork")}static sanitizeRoleDirectoryName(e){return String(e||"").trim().split("").map(r=>me.has(r)||r.charCodeAt(0)<32?"_":r).join("").replace(/^\.+|\.+$/g,"").trim()||"default-role"}static resolveRoleMemoryDirectoryName(){return S.sanitizeRoleDirectoryName(process.env.AWS_ROLE_NAME||"")}static buildRoleMemoryStorePath(e){const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=S.findAgentsworkDirSync(t);return M.join(r,"memory","memory",S.sanitizeRoleDirectoryName(e))}static resolveMemoryStorePaths(){const e=String(process.env.AWS_MEMORY_STORE_PATH||"").trim();if(e)return{instancePath:e,rolePath:e};const t=process.env.AWS_WORKSPACE_PATH||process.cwd(),r=S.findAgentsworkDirSync(t),n=String(process.env.AWS_INSTANCE_MEMORY_STORE_PATH||"").trim()||M.join(r,"memory","server-managed"),s=String(process.env.AWS_ROLE_MEMORY_STORE_PATH||"").trim()||M.join(r,"memory","memory",S.resolveRoleMemoryDirectoryName());return{instancePath:n,rolePath:s}}getRequiredString(e,t){const r=e[t];if(typeof r!="string"||r.trim().length===0)throw new Error(`${t} is required`);return r}getOptionalString(e,t){const r=e[t];return typeof r=="string"?r:void 0}getOptionalNumber(e,t){const r=e[t];return typeof r=="number"?r:void 0}getOptionalStringArray(e,t){const r=e[t];return Array.isArray(r)?r.map(n=>String(n)):void 0}getOptionalMemoryType(e){const t=e.memory_type;if(t!==void 0)return this.parseMemoryType(t)}parseMemoryType(e){if(typeof e!="string")throw new Error(`Invalid memory_type: ${String(e)}`);const t=e.trim();if(!this.isMemoryType(t))throw new Error(`Invalid memory_type: ${e}`);return t}isMemoryType(e){return S.MEMORY_TYPES.some(t=>t===e)}getMemoryStoreForType(e){return this.memoryStore}async readMemoryFromScopedStores(e){if(e.memory_type)return await this.memoryStore.readMemory(e);const t=await this.memoryStore.readMemory({...e,memory_type:"project_memory"}).catch(()=>null);return t||await this.memoryStore.readMemory(e)}async backupProjectMemoryToLocal(e){try{const t=await this.memoryStore.readMemory({uri:e.uri,memory_type:"project_memory"});t&&(await this.roleMemoryStore.deleteMemory({uri:e.uri,memory_type:"project_memory"}).catch(()=>{}),await this.roleMemoryStore.createMemory(t))}catch(t){o.warn("[McpServer] backupProjectMemoryToLocal \u5931\u8D25: uri=%s",e.uri,t)}}async removeProjectMemoryLocalBackup(e){try{await this.roleMemoryStore.deleteMemory({uri:e,memory_type:"project_memory"})}catch(t){o.warn("[McpServer] removeProjectMemoryLocalBackup \u5931\u8D25: uri=%s",e,t)}}async findMemoryStoreForMutation(e){return e.memory_type?{store:this.memoryStore,memory_type:e.memory_type}:await this.memoryStore.readMemory({uri:e.uri,memory_type:"project_memory"}).catch(()=>null)?{store:this.memoryStore,memory_type:"project_memory"}:{store:this.memoryStore}}async reportMemoryStatsToServer(){const e=await this.memoryStore.getStats();try{await this.agentClient.reportMemoryStats(e)}catch(t){o.warn("[McpServer] \u4E0A\u62A5 memory \u7EDF\u8BA1\u5931\u8D25:",t)}}sanitizeToolUseArgs(e,t){if(!S.MEMORY_TOOL_NAMES.has(e))return t;const r={},n=this.getOptionalString(t,"uri"),s=this.getOptionalString(t,"memory_type"),i=this.getOptionalNumber(t,"limit");return n&&(r.uri=n),s&&(r.memory_type=s),typeof i=="number"&&(r.limit=i),e==="search_memory"&&(r.queryProvided=typeof t.query=="string"&&t.query.length>0,t.keyword_mode!==void 0&&(r.keyword_mode=t.keyword_mode)),r}stripAutoDelegateForReporting(e){if(!e||typeof e!="object")return e;const t=e,r=Array.isArray(t.directMessages)?t.directMessages:[],n=Array.isArray(t.groupMessages)?t.groupMessages:[],s=l=>!!l&&typeof l=="object"&&"_acodeAutoDelegate"in l;if(!r.some(s))return e;const a=r.filter(l=>!s(l));return a.length===0&&n.length===0?null:{...t,directMessages:a}}async handleCreateMemory(e){const t=this.normalizeBoundFiles(this.getOptionalStringArray(e,"bound_files"));await this.validateBoundFilesExist(t);let r,n;if(t&&t.length>0){r={};for(const a of t){const l=await this.calculateFileMd5(a);l&&(r[a]=l)}n=new Date().toISOString()}const s={memory_type:this.parseMemoryType(this.getRequiredString(e,"memory_type")),uri:this.getRequiredString(e,"uri"),title:this.getOptionalString(e,"title"),content:this.getRequiredString(e,"content"),keywords:this.getOptionalStringArray(e,"keywords"),bound_files:t,file_md5_map:r,last_check_time:n},i=await this.memoryStore.createMemory(s);return s.memory_type==="project_memory"&&this.backupProjectMemoryToLocal(s).catch(a=>{o.warn("[McpServer] \u5907\u4EFD project_memory \u5230\u672C\u5730\u5931\u8D25:",a)}),await this.reportMemoryStatsToServer(),this.toMemoryWriteResult(i)}async handleReadMemory(e){const t={uri:this.getRequiredString(e,"uri"),memory_type:this.getOptionalMemoryType(e)};return await this.readMemoryFromScopedStores(t)}async handleUpdateMemory(e){const t=typeof e.old_string=="string"||typeof e.new_string=="string",r=typeof e.old_string=="string"&&typeof e.new_string=="string",n=this.getOptionalString(e,"append");if(t&&!r)throw new Error("old_string and new_string must be provided together");const s={uri:this.getRequiredString(e,"uri"),memory_type:this.getOptionalMemoryType(e),title:this.getOptionalString(e,"title"),old_string:this.getOptionalString(e,"old_string"),new_string:this.getOptionalString(e,"new_string"),append:n,keywords:this.getOptionalStringArray(e,"keywords"),bound_files:this.normalizeBoundFiles(this.getOptionalStringArray(e,"bound_files"))},i=await this.findMemoryStoreForMutation(s),a=await i.store.readMemory({uri:s.uri,memory_type:i.memory_type??s.memory_type});s.bound_files!==void 0?await this.validateBoundFilesExist(s.bound_files):a?.bound_files&&a.bound_files.length>0&&await this.validateBoundFilesExist(a.bound_files);const l=s.bound_files??a?.bound_files;let m,u;if(l&&l.length>0){m={};for(const d of l){const _=await this.calculateFileMd5(d);_&&(m[d]=_)}u=new Date().toISOString()}const h=await i.store.updateMemory({...s,memory_type:i.memory_type??s.memory_type,file_md5_map:m,last_check_time:u});if(i.memory_type==="project_memory"&&this.backupProjectMemoryToLocal({...s,memory_type:"project_memory",file_md5_map:m,last_check_time:u}).catch(d=>{o.warn("[McpServer] \u5907\u4EFD project_memory \u66F4\u65B0\u5230\u672C\u5730\u5931\u8D25:",d)}),await this.reportMemoryStatsToServer(),i.memory_type==="project_memory"&&a?.memory_status==="updating")try{await this.memoryStore.releaseMemoryClaim(s.uri),this.activeLeaseUris.delete(s.uri),this.activeLeaseUris.size===0&&this.stopLeaseRenewal()}catch(d){o.warn("[McpServer] \u91CA\u653E memory claim \u5931\u8D25: uri=%s, error=%s",s.uri,d instanceof Error?d.message:String(d))}return this.toMemoryWriteResult(h)}async handleDeleteMemory(e){const t=typeof e.uri=="string"?e.uri:void 0,r=Array.isArray(e.memories)?e.memories:void 0;if(t&&r&&r.length>0)throw new Error("delete_memory \u4E0D\u652F\u6301\u540C\u65F6\u4F20\u5165 uri \u548C memories\uFF0C\u8BF7\u4E8C\u9009\u4E00");if(!t&&(!r||r.length===0))throw new Error("delete_memory \u9700\u8981 uri \u6216 memories \u53C2\u6570");if(t){const a=this.getOptionalMemoryType(e);return{deleted:await this.deleteMemorySingle(t,a)}}let n=0;const s=[],i=[];for(let a=0;a<r.length;a++){const l=r[a];if(typeof l!="object"||l===null){i.push({uri:String(l),reason:`memories[${a}] \u4E0D\u662F\u5BF9\u8C61`});continue}const m=l,u=typeof m.uri=="string"?m.uri:"";if(!u){i.push({uri:"",reason:`memories[${a}].uri \u7F3A\u5931`});continue}const h=typeof m.memory_type=="string"?this.parseMemoryType(m.memory_type):void 0;try{await this.deleteMemorySingle(u,h,!0)?n++:s.push(u)}catch(d){i.push({uri:u,reason:d instanceof Error?d.message:String(d)})}}return n>0&&await this.reportMemoryStatsToServer(),{deleted:n,not_found:s,failed:i}}async deleteMemorySingle(e,t,r=!1){const n={uri:e,memory_type:t},s=await this.findMemoryStoreForMutation(n),i=s.memory_type??n.memory_type,a=await s.store.deleteMemory({...n,memory_type:i});return a&&(i==="project_memory"&&this.removeProjectMemoryLocalBackup(e).catch(l=>{o.warn("[McpServer] \u5220\u9664 project_memory \u672C\u5730\u5907\u4EFD\u5931\u8D25:",l)}),r||await this.reportMemoryStatsToServer()),a}async handleSearchMemory(e){const t=this.getOptionalString(e,"scope"),r=t==="children"||t==="all"?t:"own",n=this.getOptionalString(e,"keyword_mode"),s=n==="all"?"all":n==="any"?"any":void 0,i={query:this.getRequiredString(e,"query"),limit:this.getOptionalNumber(e,"limit"),memory_type:this.getOptionalMemoryType(e),keywords:this.getOptionalStringArray(e,"keywords"),keyword_mode:s,scope:r};if(r==="children")return await this.memoryStore.searchMemory(i);const a=await this.searchOwnMemories(i);if(r==="own")return a;const l=await this.memoryStore.searchMemory({...i,scope:"children"});return this.mergeSearchResults(a,l,i.limit)}async searchOwnMemories(e){return await this.memoryStore.searchMemory(e)}mergeSearchResults(e,t,r){const n=new Set,s=typeof r=="number"&&r>0?r:10,i=[...e.results,...t.results].filter(a=>{const l=a.uri;return n.has(l)?!1:(n.add(l),!0)}).sort((a,l)=>l.updatedAt.localeCompare(a.updatedAt)).slice(0,s);return{query:e.query||t.query,results:i}}async handleListMemories(e){const t=this.getRequiredString(e,"memory_type");if(t==="project_memory"||t==="session_context")throw new Error(`Invalid memory_type: ${t}`);return await this.memoryStore.listByType(t)}async handleGetMemoryStats(e){const t=await this.memoryStore.getStats(),r=this.getOptionalMemoryType(e);return r?{memory_type:r,total:t.by_type[r].count,by_type:{[r]:t.by_type[r]}}:t}async handleListMemoriesByFile(e){const t=this.getRequiredString(e,"file_path"),r=this.normalizeBoundFilePath(t),n=this.getOptionalMemoryType(e),s=this.getOptionalString(e,"scope"),i=s==="own"||s==="children"?s:"all",a=async()=>await this.memoryStore.listByFile(r,{memory_type:n,scope:"children"}).catch(()=>[]);if(i==="children"){const d=await a();return{file_path:r,count:d.length,memories:d.map(_=>this.toMemoryRecordWithMetadata(_))}}const l=await this.collectOwnMemoriesByFile(r,n);if(i==="own")return{file_path:r,count:l.length,memories:l.map(d=>this.toMemoryRecordWithMetadata(d))};const m=await a(),u=new Set(l.map(d=>d.id)),h=[...l];for(const d of m)u.has(d.id)||(h.push(d),u.add(d.id));return h.sort((d,_)=>_.updatedAt.localeCompare(d.updatedAt)),{file_path:r,count:h.length,memories:h.map(d=>this.toMemoryRecordWithMetadata(d))}}mergeIdDedupe(e,t){const r=new Set;for(const s of e)s.id&&r.add(s.id);const n=[...e];for(const s of t)s.id&&!r.has(s.id)&&(n.push(s),r.add(s.id));return n.sort((s,i)=>i.updatedAt.localeCompare(s.updatedAt)),n}async searchChildMemoriesByFileLocal(e,t){const r=process.env.AWS_WORKSPACE_PATH||process.cwd(),n=this.normalizeBoundFilePath(e),i=(n.startsWith("./")?n.substring(2):n).split("/"),a=[],l=new Set,{access:m,readdir:u,readFile:h}=await import("node:fs/promises");for(let d=i.length-1;d>=1;d--){const _=i.slice(0,d),f="./"+i.slice(d).join("/"),y=M.join(r,..._);if(y===r)continue;const c=M.join(y,".agentswork");try{await m(c)}catch{continue}const g=M.join(c,"memory","memory");let T;try{T=(await u(g,{withFileTypes:!0})).filter(w=>w.isDirectory()).map(w=>w.name)}catch{continue}for(const R of T){const w=M.join(g,R);let k;try{k=await u(w)}catch{continue}for(const p of k){if(!p.endsWith(".json"))continue;const b=M.join(w,p);let E;try{E=await h(b,"utf-8")}catch{continue}let C;try{C=JSON.parse(E)}catch{continue}if(C.by_uri)for(const[$,A]of Object.entries(C.by_uri)){if(!A)continue;const B=A.id||$;l.has(B)||t&&A.memory_type!==t||A.bound_files&&A.bound_files.length>0&&A.bound_files.some(z=>this.normalizeBoundFilePath(z)===f)&&(l.add(B),a.push({...A,uri:A.uri||$}))}}}}return a.sort((d,_)=>_.updatedAt.localeCompare(d.updatedAt)),a}async collectOwnMemoriesByFile(e,t){return await this.memoryStore.listByFile(e,{memory_type:t})}async handleAbandonMemoryUpdate(e){const t=this.getRequiredString(e,"uri"),r=this.getOptionalMemoryType(e),n=await this.memoryStore.readMemory({uri:t,memory_type:r});return n?.bound_files&&n.bound_files.length>0&&await this.validateBoundFilesExist(n.bound_files),await this.memoryStore.releaseMemoryClaim(t),n?.memory_type==="project_memory"&&this.backupProjectMemoryToLocal({uri:t,memory_type:"project_memory"}).catch(s=>{o.info(`[McpServer] abandon_memory_update: \u540C\u6B65\u672C\u5730\u5907\u4EFD\u5931\u8D25: ${t}`,s)}),this.activeLeaseUris.delete(t),this.activeLeaseUris.size===0&&this.stopLeaseRenewal(),{success:!0,uri:t,message:"Memory \u66F4\u65B0\u72B6\u6001\u5DF2\u6E05\u9664"}}async handleMemoryRecurrence(e){const t=Math.max(1,e?.limit??10);try{((await this.memoryStore.markExpiredMemoriesRecalling()).uris?.length??0)>0&&this.startLeaseRenewal()}catch(l){o.warn("[McpServer] \u539F\u5B50\u8BA4\u9886\u8FC7\u671F\u8BB0\u5FC6\u5931\u8D25: %s",l instanceof Error?l.message:String(l))}const r=await this.memoryStore.listExpiredRecentlyMemories();if(r.length===0)return{memories:[],count:0,limit:t,remaining:0,totalExpired:0,message:"\u6CA1\u6709\u5F85\u56DE\u5FC6\u7684\u5373\u5C06\u9057\u5FD8\u8BB0\u5FC6\u3002"};const s=[...r].sort((l,m)=>{const u=new Date(l.expiresAt||l.createdAt||0).getTime(),h=new Date(m.expiresAt||m.createdAt||0).getTime();return u-h}).slice(0,t),i=Math.max(0,r.length-s.length);return{memories:s.map(l=>({uri:l.uri,title:l.title,domain:l.domain,path:l.path,memory_status:"recalling",contentPreview:typeof l.content=="string"&&l.content.length>200?l.content.substring(0,200)+"...":l.content,createdAt:l.createdAt,updatedAt:l.updatedAt})),count:s.length,limit:t,remaining:i,totalExpired:r.length,message:i>0?`\u672C\u6B21\u5904\u7406 ${s.length} \u6761\u5373\u5C06\u9057\u5FD8\u7684\u8BB0\u5FC6\uFF08\u6700\u65E9\u8FC7\u671F\u4F18\u5148\uFF09\uFF0C\u5269\u4F59 ${i} \u6761\u5F85\u5904\u7406\u3002
4
4
  \u8BF7\u5BA1\u67E5\u540E\u51B3\u5B9A\uFF08update_memory \u7EED\u671F\uFF0CTTL \xD72 / \u4E0D\u5904\u7406\uFF09\uFF0C
@@ -38,12 +38,12 @@ ${k}
38
38
  `:"",s=this.formatChangeType(e.changeType),i=new Date(e.triggeredAt).toISOString(),a=`\u3010\u6D4F\u89C8\u5668\u76D1\u542C\u5668\u89E6\u53D1 | ${r} | URL: ${e.url}\u3011
39
39
  ${n}\u53D8\u5316\u7C7B\u578B: ${s}
40
40
  \u89E6\u53D1\u65F6\u95F4: ${i}
41
- \u63D0\u793A\u8BCD: ${e.prompt}`;return{msgId:`browser-listener-${e.listenerId}-${e.triggeredAt}`,senderId:"MCP-BrowserListener",senderName:"\u6D4F\u89C8\u5668\u76D1\u542C\u5668",receiverId:t||void 0,content:a,requireReply:!1,role:"system",timestamp:i}}catch{return null}}formatChangeType(e){switch(e){case"added":return"\u65B0\u589E";case"removed":return"\u5220\u9664";case"changed":return"\u6539\u53D8";case"exists":return"\u5B58\u5728";default:return"\u6539\u53D8"}}async handleGetDmHistory(e){return await this.agentClient.getDmHistory(e.targetUserId,e.limit,e.since)}async handleGetGroupHistory(e){return await this.agentClient.getGroupHistory(e.limit,e.roomId,e.since)}async handleSubmitTaskResult(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.outputPayload||typeof e.outputPayload!="string")throw new Error("outputPayload is required");const t=Array.isArray(e.changeLog)?e.changeLog:void 0;return await this.agentClient.submitTaskResult(e.nodeId,e.outputPayload,t)}async handleStartTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId")};return await this.agentClient.startTaskRollback(t)}async handleFinishTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId"),resultMessage:this.getOptionalString(e,"resultMessage")};return await this.agentClient.finishTaskRollback(t)}async handleRejectTask(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.rejectTask(e.nodeId,e.reason)}async handleTerminateTask(e){if(!e.taskInstanceId||typeof e.taskInstanceId!="string")throw new Error("taskInstanceId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.terminateTask(e.taskInstanceId,e.reason)}async handleIncomingMessage(e){if(!this.shouldBufferIncomingMessage(e)){!this.isGroupMessage(e)&&this.isPipelineTaskAssignedNotification(e)&&(o.info("[McpServer] \u6536\u5230 pipeline_task_assigned \u5524\u9192\u4FE1\u53F7\uFF0C\u7ACB\u5373\u5524\u9192 poll \u8D70 hydrate \u91CD\u67E5"),this.messageBuffer.wakeUpWaiters());return}o.info(`[McpServer] \u6536\u5230\u6D88\u606F: ${e.content?.substring(0,50)||"(\u65E0\u5185\u5BB9)"}`);const t=e.channel;t!=="meeting_invite"&&t!=="notification"&&(e._awsServerMessage=!0),this.messageBuffer.push(e),!this.isGroupMessage(e)&&e.force===!0&&this.agentClient.notifyBridgeForceMessage().catch(()=>{})}handleSystemNotify(e){const t=e?.type;if(typeof t!="string"||t!=="meeting_invite"&&t!=="meeting_creator_prompt")return;const r=this.parseNotificationPayload(e.payload);o.info("[McpServer] \u6536\u5230\u4F1A\u8BAE\u57DF\u7CFB\u7EDF\u901A\u77E5(type=%s, competitive=%s, meetingId=%s)\uFF0C\u5524\u9192 poll \u89E6\u53D1\u5F3A\u5236 hydrate",t,r?.competitive===!0?"true(\u89D2\u8272\u5E2D\u4F4D)":"false(\u76F4\u63A5\u6307\u540D)",typeof r?.meetingId=="string"?r.meetingId:typeof e.meetingId=="string"?e.meetingId:"unknown"),this.urgentMeetingHydrateRequested=!0,this.messageBuffer.wakeUpWaiters(),v("systemNotify.meeting-domain",`type=${t} \u5DF2\u7F6E\u4F4D\u5F3A\u5236 hydrate \u5E76\u5524\u9192 poll`)}parseNotificationPayload(e){if(e&&typeof e=="object")return e;if(!(typeof e!="string"||e.length===0))try{const t=JSON.parse(e);return t&&typeof t=="object"?t:void 0}catch{return}}shouldBufferIncomingMessage(e){return!e||typeof e!="object"?(o.warn("[McpServer] \u5FFD\u7565\u7ED3\u6784\u5F02\u5E38\u7684\u6D88\u606F\uFF0C\u907F\u514D\u7F13\u51B2\u533A\u6C61\u67D3"),!1):this.isSelfSentMessage(e)?(o.info("[McpServer] \u5FFD\u7565\u5F53\u524D Agent \u81EA\u5DF1\u53D1\u9001\u7684\u6D88\u606F\uFF0C\u907F\u514D poll_message/get_message \u56DE\u73AF"),!1):this.isGroupMessage(e)?!0:this.isPipelineTaskAssignedNotification(e)?(o.info("[McpServer] \u5FFD\u7565\u6301\u4E45\u5316 pipeline_task_assigned \u79C1\u4FE1\uFF0C\u4EC5\u4F5C\u4E3A poll_message \u5524\u9192\u4FE1\u53F7\u5904\u7406"),!1):!0}isGroupMessage(e){return!!e&&typeof e=="object"&&typeof e.msgId=="number"}isPipelineTaskAssignedNotification(e){const t=typeof e.content=="string"?e.content:"",r=typeof e.prompt=="string"?e.prompt:"";return t.includes("pipeline_task_assigned")||r.includes("pipeline_task_assigned")}isSelfSentMessage(e){if(!e||typeof e!="object")return!1;const t=this.getCurrentAgentId();return!!t&&typeof e.senderId=="string"&&e.senderId===t}getCurrentAgentId(){const e=this.agentClient;return typeof e.getState!="function"?null:e.getState().agentId??null}sanitizeAgentIdForTodoFile(e){return String(e).replace(/[^a-zA-Z0-9_-]/g,"_")}async buildTodoBlockNotification(){const e=this.getCurrentAgentId();if(!e)return null;const t=M.join(U.homedir(),".acode","todos",`${this.sanitizeAgentIdForTodoFile(e)}.json`);let r;try{r=await N.promises.readFile(t,"utf-8")}catch(a){return a?.code==="ENOENT"||o.warn("[McpServer] \u8BFB\u53D6 Todo \u6587\u4EF6\u5931\u8D25\uFF0C\u653E\u884C poll_message: %s",a instanceof Error?a.message:String(a)),null}let n;try{n=JSON.parse(r)}catch(a){return o.warn("[McpServer] \u89E3\u6790 Todo \u6587\u4EF6\u5931\u8D25\uFF0C\u653E\u884C poll_message: %s",a instanceof Error?a.message:String(a)),null}return(Array.isArray(n.todos)?n.todos:[]).filter(a=>a&&typeof a=="object"&&typeof a.content=="string"&&(a.status==="pending"||a.status==="in_progress"||a.status==="cancelled")).length===0?null:{msgId:`todo-block-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:"\u5F53\u524D\u6709\u672A\u5904\u7406\u7684todo list\u4E0D\u80FD\u4F7F\u7528poll_message\u963B\u585E\u83B7\u53D6\u6D88\u606F",requireReply:!1,timestamp:new Date().toISOString()}}async buildBackgroundTaskNotification(){try{const e=this.getCurrentAgentId();if(!e)return null;const t=M.join(U.homedir(),".acode","bg-tasks",this.sanitizeAgentIdForTodoFile(e),"state.json");let r;try{r=await N.promises.readFile(t,"utf-8")}catch(u){if(u?.code==="ENOENT")return null;throw u}const n=JSON.parse(r),s=Array.isArray(n.tasks)?n.tasks:[],i=s.filter(u=>u&&typeof u=="object"&&typeof u.taskId=="string"&&u.status!=="running"&&!u.notified);if(i.length===0)return null;const a=i.slice(0,5),l=a.map(u=>`[bg:${u.taskId}] ${u.status} (exitCode=${u.exitCode??"?"}) ${u.commandPreview??""}
41
+ \u63D0\u793A\u8BCD: ${e.prompt}`;return{msgId:`browser-listener-${e.listenerId}-${e.triggeredAt}`,senderId:"MCP-BrowserListener",senderName:"\u6D4F\u89C8\u5668\u76D1\u542C\u5668",receiverId:t||void 0,content:a,requireReply:!1,role:"system",timestamp:i}}catch{return null}}formatChangeType(e){switch(e){case"added":return"\u65B0\u589E";case"removed":return"\u5220\u9664";case"changed":return"\u6539\u53D8";case"exists":return"\u5B58\u5728";default:return"\u6539\u53D8"}}async handleGetDmHistory(e){return await this.agentClient.getDmHistory(e.targetUserId,e.limit,e.since)}async handleGetGroupHistory(e){return await this.agentClient.getGroupHistory(e.limit,e.roomId,e.since)}async handleSubmitTaskResult(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.outputPayload||typeof e.outputPayload!="string")throw new Error("outputPayload is required");const t=Array.isArray(e.changeLog)?e.changeLog:void 0;return await this.agentClient.submitTaskResult(e.nodeId,e.outputPayload,t)}async handleStartTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId")};return await this.agentClient.startTaskRollback(t)}async handleFinishTaskRollback(e){const t={rollbackTaskId:this.getRequiredString(e,"rollbackTaskId"),resultMessage:this.getOptionalString(e,"resultMessage")};return await this.agentClient.finishTaskRollback(t)}async handleRejectTask(e){if(!e.nodeId||typeof e.nodeId!="string")throw new Error("nodeId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.rejectTask(e.nodeId,e.reason)}async handleTerminateTask(e){if(!e.taskInstanceId||typeof e.taskInstanceId!="string")throw new Error("taskInstanceId is required");if(!e.reason||typeof e.reason!="string")throw new Error("reason is required");return await this.agentClient.terminateTask(e.taskInstanceId,e.reason)}async handleIncomingMessage(e){if(!this.shouldBufferIncomingMessage(e)){!this.isGroupMessage(e)&&this.isPipelineTaskAssignedNotification(e)&&(o.info("[McpServer] \u6536\u5230 pipeline_task_assigned \u5524\u9192\u4FE1\u53F7\uFF0C\u7ACB\u5373\u5524\u9192 poll \u8D70 hydrate \u91CD\u67E5"),this.messageBuffer.wakeUpWaiters());return}o.info(`[McpServer] \u6536\u5230\u6D88\u606F: ${e.content?.substring(0,50)||"(\u65E0\u5185\u5BB9)"}`);const t=e.channel;t!=="meeting_invite"&&t!=="notification"&&(e._awsServerMessage=!0),this.messageBuffer.push(e),!this.isGroupMessage(e)&&e.force===!0&&this.agentClient.notifyBridgeForceMessage().catch(()=>{})}handleSystemNotify(e){const t=e?.type;if(typeof t!="string"||t!=="meeting_invite"&&t!=="meeting_creator_prompt")return;const r=this.parseNotificationPayload(e.payload);o.info("[McpServer] \u6536\u5230\u4F1A\u8BAE\u57DF\u7CFB\u7EDF\u901A\u77E5(type=%s, competitive=%s, meetingId=%s)\uFF0C\u5524\u9192 poll \u89E6\u53D1\u5F3A\u5236 hydrate",t,r?.competitive===!0?"true(\u89D2\u8272\u5E2D\u4F4D)":"false(\u76F4\u63A5\u6307\u540D)",typeof r?.meetingId=="string"?r.meetingId:typeof e.meetingId=="string"?e.meetingId:"unknown"),this.urgentMeetingHydrateRequested=!0,this.messageBuffer.wakeUpWaiters(),v("systemNotify.meeting-domain",`type=${t} \u5DF2\u7F6E\u4F4D\u5F3A\u5236 hydrate \u5E76\u5524\u9192 poll`)}parseNotificationPayload(e){if(e&&typeof e=="object")return e;if(!(typeof e!="string"||e.length===0))try{const t=JSON.parse(e);return t&&typeof t=="object"?t:void 0}catch{return}}shouldBufferIncomingMessage(e){return!e||typeof e!="object"?(o.warn("[McpServer] \u5FFD\u7565\u7ED3\u6784\u5F02\u5E38\u7684\u6D88\u606F\uFF0C\u907F\u514D\u7F13\u51B2\u533A\u6C61\u67D3"),!1):this.isSelfSentMessage(e)?(o.info("[McpServer] \u5FFD\u7565\u5F53\u524D Agent \u81EA\u5DF1\u53D1\u9001\u7684\u6D88\u606F\uFF0C\u907F\u514D poll_message/get_message \u56DE\u73AF"),!1):this.isGroupMessage(e)?!0:this.isPipelineTaskAssignedNotification(e)?(o.info("[McpServer] \u5FFD\u7565\u6301\u4E45\u5316 pipeline_task_assigned \u79C1\u4FE1\uFF0C\u4EC5\u4F5C\u4E3A poll_message \u5524\u9192\u4FE1\u53F7\u5904\u7406"),!1):!0}isGroupMessage(e){return!!e&&typeof e=="object"&&typeof e.msgId=="number"}isPipelineTaskAssignedNotification(e){const t=typeof e.content=="string"?e.content:"",r=typeof e.prompt=="string"?e.prompt:"";return t.includes("pipeline_task_assigned")||r.includes("pipeline_task_assigned")}isSelfSentMessage(e){if(!e||typeof e!="object")return!1;const t=this.getCurrentAgentId();return!!t&&typeof e.senderId=="string"&&e.senderId===t}getCurrentAgentId(){const e=this.agentClient;return typeof e.getState!="function"?null:e.getState().agentId??null}sanitizeAgentIdForTodoFile(e){return String(e).replace(/[^a-zA-Z0-9_-]/g,"_")}async buildTodoBlockNotification(){const e=this.getCurrentAgentId();if(!e)return null;const t=M.join(H(),"todos",`${this.sanitizeAgentIdForTodoFile(e)}.json`);let r;try{r=await N.promises.readFile(t,"utf-8")}catch(a){return a?.code==="ENOENT"||o.warn("[McpServer] \u8BFB\u53D6 Todo \u6587\u4EF6\u5931\u8D25\uFF0C\u653E\u884C poll_message: %s",a instanceof Error?a.message:String(a)),null}let n;try{n=JSON.parse(r)}catch(a){return o.warn("[McpServer] \u89E3\u6790 Todo \u6587\u4EF6\u5931\u8D25\uFF0C\u653E\u884C poll_message: %s",a instanceof Error?a.message:String(a)),null}return(Array.isArray(n.todos)?n.todos:[]).filter(a=>a&&typeof a=="object"&&typeof a.content=="string"&&(a.status==="pending"||a.status==="in_progress"||a.status==="cancelled")).length===0?null:{msgId:`todo-block-${Date.now()}`,senderId:"MCP-System",senderName:"\u7CFB\u7EDF",receiverId:e,content:"\u5F53\u524D\u6709\u672A\u5904\u7406\u7684todo list\u4E0D\u80FD\u4F7F\u7528poll_message\u963B\u585E\u83B7\u53D6\u6D88\u606F",requireReply:!1,timestamp:new Date().toISOString()}}async buildBackgroundTaskNotification(){try{const e=this.getCurrentAgentId();if(!e)return null;const t=M.join(H(),"bg-tasks",this.sanitizeAgentIdForTodoFile(e),"state.json");let r;try{r=await N.promises.readFile(t,"utf-8")}catch(u){if(u?.code==="ENOENT")return null;throw u}const n=JSON.parse(r),s=Array.isArray(n.tasks)?n.tasks:[],i=s.filter(u=>u&&typeof u=="object"&&typeof u.taskId=="string"&&u.status!=="running"&&!u.notified);if(i.length===0)return null;const a=i.slice(0,5),l=a.map(u=>`[bg:${u.taskId}] ${u.status} (exitCode=${u.exitCode??"?"}) ${u.commandPreview??""}
42
42
  lines=${u.outputLineCount??0} bytes=${u.outputBytes??0}
43
43
  Use read_background_output(task_id="${u.taskId}", mode="tail", lines=50) for full output.`).join(`
44
44
 
45
45
  `),m=i.length>a.length?`
46
46
 
47
47
  \uFF08\u53E6\u6709 ${i.length-a.length} \u6761\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u7528 ListBackgroundCommands \u67E5\u770B\uFF09`:"";return await N.promises.writeFile(t,JSON.stringify({...n,updatedAt:Date.now(),tasks:s.map(u=>i.some(h=>h.taskId===u.taskId)?{...u,notified:!0}:u)},null,2),"utf-8").catch(u=>{o.warn("[McpServer] \u6807\u8BB0\u540E\u53F0\u4EFB\u52A1\u5DF2\u901A\u77E5\u5931\u8D25: %s",u?.message??u)}),{msgId:`bg-notif-${Date.now()}`,senderId:"MCP-System",senderName:"ACode Background",receiverId:e,content:`[Background commands completed]
48
- ${l}${m}`,requireReply:!1,timestamp:new Date().toISOString()}}catch(e){return o.warn("[McpServer] \u6784\u5EFA\u540E\u53F0\u547D\u4EE4\u5B8C\u6210\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async start(){const e=new K;await this.server.connect(e),await this.agentClient.initialize(),o.info("[McpServer] MCP Server \u5DF2\u542F\u52A8\uFF0C\u7B49\u5F85\u8FDE\u63A5..."),o.info(`[McpServer] \u53EF\u7528\u5DE5\u5177: ${F.map(t=>t.name).join(", ")}`),o.info(`[McpServer] \u5DE5\u4F5C\u76EE\u5F55: ${process.env.AWS_WORKSPACE_PATH||process.cwd()}`),H&&o.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\u5DF2\u542F\u7528\uFF0C\u6CE8\u518C\u5931\u8D25\u65F6\u5C06\u4EE5\u79BB\u7EBF\u6A21\u5F0F\u7EE7\u7EED\u8FD0\u884C"),this.autoRegisterInBackground(),o.info("[McpServer] MCP Server \u5DF2\u5C31\u7EEA\uFF0C\u5DE5\u5177\u5DF2\u53EF\u7528")}startLeaseRenewal(){this.leaseRenewalTimer||(this.leaseRenewalTimer=setInterval(async()=>{try{if(!this.getCurrentAgentId())return;(await this.memoryStore.renewMemoryLease(S.LEASE_DURATION_MINUTES)).renewed===0&&(o.warn("[McpServer] \u79DF\u7EA6\u5DF2\u4E22\u5931\uFF08renewed=0\uFF09\uFF0C\u505C\u6B62\u7EED\u671F\u5B9A\u65F6\u5668"),this.stopLeaseRenewal())}catch(e){const t=e instanceof Error?e.message:String(e);t.includes("not supported")?(o.info("[McpServer] \u5F53\u524D\u5B58\u50A8\u4E0D\u652F\u6301\u79DF\u7EA6\u7EED\u671F\uFF0C\u505C\u6B62\u7EED\u671F\u5B9A\u65F6\u5668"),this.stopLeaseRenewal()):o.warn("[McpServer] \u7EED\u671F\u79DF\u7EA6\u5931\u8D25: %s",t)}},S.LEASE_RENEW_INTERVAL_MS),o.info("[McpServer] \u79DF\u7EA6\u7EED\u671F\u5B9A\u65F6\u5668\u5DF2\u542F\u52A8\uFF0C\u95F4\u9694: 60s"))}stopLeaseRenewal(){this.leaseRenewalTimer&&(clearInterval(this.leaseRenewalTimer),this.leaseRenewalTimer=null,o.info("[McpServer] \u79DF\u7EA6\u7EED\u671F\u5B9A\u65F6\u5668\u5DF2\u505C\u6B62"))}stop(){this.stopLeaseRenewal(),this.getCurrentAgentId()&&this.memoryStore.releaseAllMemoryClaims().catch(t=>o.warn("[McpServer] \u91CA\u653E claim \u5931\u8D25: %s",t instanceof Error?t.message:String(t))),this.agentClient.stop(),o.info("[McpServer] MCP Server \u5DF2\u505C\u6B62")}async autoRegister(){const e=this.agentClient.getAgentConfig();o.info(`[McpServer] \u89D2\u8272: "${e.roleName}"`),o.info(`[McpServer] AgentID: "${e.agentId||"(\u81EA\u52A8\u5206\u914D)"}"`),o.info("[McpServer] \u6B63\u5728\u8BF7\u6C42\u8C03\u5EA6\u4E2D\u5FC3\u6CE8\u518C...");let t=0;for(;;)try{const r=await this.agentClient.register();o.info(`[McpServer] \u6CE8\u518C\u6210\u529F: ${r.displayName} (${r.agentId})`),o.info(`[McpServer] \u72B6\u6001: ${r.status}`),this.startListeningInternal(),o.info("[McpServer] \u6D88\u606F\u76D1\u542C\u5DF2\u81EA\u52A8\u542F\u52A8");const n=this.agentClient.getStatusReporter();n&&n.reportWaitingInput(),await this.reportMemoryStatsToServer(),this.maybeSyncPermanentMemoryToServer();return}catch(r){const n=r instanceof Error?r.message:String(r);if(t++,O>0&&t>=O){if(H){o.warn(`[McpServer] \u6CE8\u518C\u8D85\u8FC7\u6700\u5927\u91CD\u8BD5\u6B21\u6570 (${O}\u6B21)\uFF0C\u8FDB\u5165\u964D\u7EA7\u6A21\u5F0F`),this.agentClient.enterDegradedMode(),this.startListeningInternal(),o.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u6D88\u606F\u76D1\u542C\u5DF2\u542F\u52A8"),this.agentClient.retryRegistrationInBackground(se,ie);const s=this.agentClient.getStatusReporter();s&&s.reportWaitingInput(),await this.reportMemoryStatsToServer();return}throw new Error(`\u6CE8\u518C\u8D85\u8FC7\u6700\u5927\u91CD\u8BD5\u6B21\u6570 (${O}\u6B21)\uFF0C\u653E\u5F03\u542F\u52A8: ${n}`)}o.warn(`[McpServer] \u6CE8\u518C\u5931\u8D25\uFF0C\u7B49\u5F85\u91CD\u8BD5... (${t}/${O}): ${n}`),await this.delay(re)}}autoRegisterInBackground(){this.autoRegister().catch(e=>{o.error("[McpServer] \u540E\u53F0\u6CE8\u518C\u5F02\u5E38: %s, stack=%s",e instanceof Error?e.message:String(e),e instanceof Error?e.stack:"(no stack)")})}startListeningInternal(){o.info("[McpServer] startListeningInternal \u88AB\u8C03\u7528");const e=t=>{o.info(`[McpServer] profileHandler \u88AB\u8C03\u7528\uFF0CdisplayName: ${t.displayName}`),this.messageBuffer.setProfileMessage(t),this.profileSynced=!0,this.syncRoleMemoryStore(t),this.maybeSyncPermanentMemoryToServer()};if(this.agentClient.isInDegradedMode()){o.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u8DF3\u8FC7 WebSocket \u8FDE\u63A5\uFF0C\u4EC5\u542F\u52A8\u672C\u5730\u6D88\u606F\u76D1\u542C"),this.agentClient.start(this.handleIncomingMessage.bind(this),{pollMode:!1,profileHandler:e,systemNotifyHandler:this.handleSystemNotify.bind(this)}),this.activelySyncProfile();return}this.agentClient.start(this.handleIncomingMessage.bind(this),{pollMode:!1,profileHandler:e,systemNotifyHandler:this.handleSystemNotify.bind(this)}),this.activelySyncProfile()}activelySyncProfile(){this.profileSynced||this.agentClient.getProfile().then(e=>{this.profileSynced||(this.profileSynced=!0,o.info(`[McpServer] \u4E3B\u52A8\u83B7\u53D6 profile \u6210\u529F: displayName=${e.displayName}, roleName="${e.roleName??"(\u7A7A)"}"`),this.messageBuffer.setProfileMessage(e),this.syncRoleMemoryStore(e),this.maybeSyncPermanentMemoryToServer())}).catch(e=>{o.warn("[McpServer] \u4E3B\u52A8\u83B7\u53D6 profile \u5931\u8D25: %s",e instanceof Error?e.message:String(e))})}delay(e){return new Promise(t=>setTimeout(t,e))}}export{S as McpServer};
48
+ ${l}${m}`,requireReply:!1,timestamp:new Date().toISOString()}}catch(e){return o.warn("[McpServer] \u6784\u5EFA\u540E\u53F0\u547D\u4EE4\u5B8C\u6210\u901A\u77E5\u5931\u8D25: %s",e instanceof Error?e.message:String(e)),null}}async start(){const e=new K;await this.server.connect(e),await this.agentClient.initialize(),o.info("[McpServer] MCP Server \u5DF2\u542F\u52A8\uFF0C\u7B49\u5F85\u8FDE\u63A5..."),o.info(`[McpServer] \u53EF\u7528\u5DE5\u5177: ${F.map(t=>t.name).join(", ")}`),o.info(`[McpServer] \u5DE5\u4F5C\u76EE\u5F55: ${process.env.AWS_WORKSPACE_PATH||process.cwd()}`),U&&o.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\u5DF2\u542F\u7528\uFF0C\u6CE8\u518C\u5931\u8D25\u65F6\u5C06\u4EE5\u79BB\u7EBF\u6A21\u5F0F\u7EE7\u7EED\u8FD0\u884C"),this.autoRegisterInBackground(),o.info("[McpServer] MCP Server \u5DF2\u5C31\u7EEA\uFF0C\u5DE5\u5177\u5DF2\u53EF\u7528")}startLeaseRenewal(){this.leaseRenewalTimer||(this.leaseRenewalTimer=setInterval(async()=>{try{if(!this.getCurrentAgentId())return;(await this.memoryStore.renewMemoryLease(S.LEASE_DURATION_MINUTES)).renewed===0&&(o.warn("[McpServer] \u79DF\u7EA6\u5DF2\u4E22\u5931\uFF08renewed=0\uFF09\uFF0C\u505C\u6B62\u7EED\u671F\u5B9A\u65F6\u5668"),this.stopLeaseRenewal())}catch(e){const t=e instanceof Error?e.message:String(e);t.includes("not supported")?(o.info("[McpServer] \u5F53\u524D\u5B58\u50A8\u4E0D\u652F\u6301\u79DF\u7EA6\u7EED\u671F\uFF0C\u505C\u6B62\u7EED\u671F\u5B9A\u65F6\u5668"),this.stopLeaseRenewal()):o.warn("[McpServer] \u7EED\u671F\u79DF\u7EA6\u5931\u8D25: %s",t)}},S.LEASE_RENEW_INTERVAL_MS),o.info("[McpServer] \u79DF\u7EA6\u7EED\u671F\u5B9A\u65F6\u5668\u5DF2\u542F\u52A8\uFF0C\u95F4\u9694: 60s"))}stopLeaseRenewal(){this.leaseRenewalTimer&&(clearInterval(this.leaseRenewalTimer),this.leaseRenewalTimer=null,o.info("[McpServer] \u79DF\u7EA6\u7EED\u671F\u5B9A\u65F6\u5668\u5DF2\u505C\u6B62"))}stop(){this.stopLeaseRenewal(),this.getCurrentAgentId()&&this.memoryStore.releaseAllMemoryClaims().catch(t=>o.warn("[McpServer] \u91CA\u653E claim \u5931\u8D25: %s",t instanceof Error?t.message:String(t))),this.agentClient.stop(),o.info("[McpServer] MCP Server \u5DF2\u505C\u6B62")}async autoRegister(){const e=this.agentClient.getAgentConfig();o.info(`[McpServer] \u89D2\u8272: "${e.roleName}"`),o.info(`[McpServer] AgentID: "${e.agentId||"(\u81EA\u52A8\u5206\u914D)"}"`),o.info("[McpServer] \u6B63\u5728\u8BF7\u6C42\u8C03\u5EA6\u4E2D\u5FC3\u6CE8\u518C...");let t=0;for(;;)try{const r=await this.agentClient.register();o.info(`[McpServer] \u6CE8\u518C\u6210\u529F: ${r.displayName} (${r.agentId})`),o.info(`[McpServer] \u72B6\u6001: ${r.status}`),this.startListeningInternal(),o.info("[McpServer] \u6D88\u606F\u76D1\u542C\u5DF2\u81EA\u52A8\u542F\u52A8");const n=this.agentClient.getStatusReporter();n&&n.reportWaitingInput(),await this.reportMemoryStatsToServer(),this.maybeSyncPermanentMemoryToServer();return}catch(r){const n=r instanceof Error?r.message:String(r);if(t++,O>0&&t>=O){if(U){o.warn(`[McpServer] \u6CE8\u518C\u8D85\u8FC7\u6700\u5927\u91CD\u8BD5\u6B21\u6570 (${O}\u6B21)\uFF0C\u8FDB\u5165\u964D\u7EA7\u6A21\u5F0F`),this.agentClient.enterDegradedMode(),this.startListeningInternal(),o.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u6D88\u606F\u76D1\u542C\u5DF2\u542F\u52A8"),this.agentClient.retryRegistrationInBackground(se,ie);const s=this.agentClient.getStatusReporter();s&&s.reportWaitingInput(),await this.reportMemoryStatsToServer();return}throw new Error(`\u6CE8\u518C\u8D85\u8FC7\u6700\u5927\u91CD\u8BD5\u6B21\u6570 (${O}\u6B21)\uFF0C\u653E\u5F03\u542F\u52A8: ${n}`)}o.warn(`[McpServer] \u6CE8\u518C\u5931\u8D25\uFF0C\u7B49\u5F85\u91CD\u8BD5... (${t}/${O}): ${n}`),await this.delay(re)}}autoRegisterInBackground(){this.autoRegister().catch(e=>{o.error("[McpServer] \u540E\u53F0\u6CE8\u518C\u5F02\u5E38: %s, stack=%s",e instanceof Error?e.message:String(e),e instanceof Error?e.stack:"(no stack)")})}startListeningInternal(){o.info("[McpServer] startListeningInternal \u88AB\u8C03\u7528");const e=t=>{o.info(`[McpServer] profileHandler \u88AB\u8C03\u7528\uFF0CdisplayName: ${t.displayName}`),this.messageBuffer.setProfileMessage(t),this.profileSynced=!0,this.syncRoleMemoryStore(t),this.maybeSyncPermanentMemoryToServer()};if(this.agentClient.isInDegradedMode()){o.info("[McpServer] \u964D\u7EA7\u6A21\u5F0F\uFF1A\u8DF3\u8FC7 WebSocket \u8FDE\u63A5\uFF0C\u4EC5\u542F\u52A8\u672C\u5730\u6D88\u606F\u76D1\u542C"),this.agentClient.start(this.handleIncomingMessage.bind(this),{pollMode:!1,profileHandler:e,systemNotifyHandler:this.handleSystemNotify.bind(this)}),this.activelySyncProfile();return}this.agentClient.start(this.handleIncomingMessage.bind(this),{pollMode:!1,profileHandler:e,systemNotifyHandler:this.handleSystemNotify.bind(this)}),this.activelySyncProfile()}activelySyncProfile(){this.profileSynced||this.agentClient.getProfile().then(e=>{this.profileSynced||(this.profileSynced=!0,o.info(`[McpServer] \u4E3B\u52A8\u83B7\u53D6 profile \u6210\u529F: displayName=${e.displayName}, roleName="${e.roleName??"(\u7A7A)"}"`),this.messageBuffer.setProfileMessage(e),this.syncRoleMemoryStore(e),this.maybeSyncPermanentMemoryToServer())}).catch(e=>{o.warn("[McpServer] \u4E3B\u52A8\u83B7\u53D6 profile \u5931\u8D25: %s",e instanceof Error?e.message:String(e))})}delay(e){return new Promise(t=>setTimeout(t,e))}}export{S as McpServer};
49
49
 
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
2
2
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
- import { describe, it } from "node:test";
5
+ import { after, before, describe, it } from "node:test";
6
6
  import { AgentClient, PollLongPollTimeoutError } from "./agent-client.js";
7
7
  import { McpServer } from "./mcp-server.js";
8
8
  import { MCP_TOOLS } from "./mcp-tools.js";
@@ -2109,13 +2109,31 @@ describe("AgentClient group room aggregation", () => {
2109
2109
  });
2110
2110
  });
2111
2111
  describe("McpServer todo guard on poll_message", () => {
2112
- // 使用唯一 agentId 避免污染开发者真实 todo 文件;每次测试写入并清理
2112
+ // 使用唯一 agentId 避免污染开发者真实 todo 文件;每次测试写入并清理。
2113
+ // ACODE_HOME 指向临时目录:与被测的 resolveAcodeDataHome() 同源,不触碰真实 ~/.acode
2113
2114
  const testAgentId = `test-todo-reminder-${process.pid}-${Date.now()}`;
2114
- const todoDir = path.join(os.homedir(), ".acode", "todos");
2115
- const todoPath = path.join(todoDir, `${testAgentId}.json`);
2115
+ let savedAcodeHome;
2116
+ let acodeHome = "";
2117
+ let todoDir = "";
2118
+ before(async () => {
2119
+ savedAcodeHome = process.env.ACODE_HOME;
2120
+ acodeHome = await mkdtemp(path.join(os.tmpdir(), "acode-home-"));
2121
+ process.env.ACODE_HOME = acodeHome;
2122
+ todoDir = path.join(acodeHome, "todos");
2123
+ });
2124
+ after(async () => {
2125
+ if (savedAcodeHome === undefined) {
2126
+ delete process.env.ACODE_HOME;
2127
+ }
2128
+ else {
2129
+ process.env.ACODE_HOME = savedAcodeHome;
2130
+ }
2131
+ await rm(acodeHome, { recursive: true, force: true });
2132
+ });
2133
+ const todoPath = () => path.join(todoDir, `${testAgentId}.json`);
2116
2134
  async function writeTodoFile(todos) {
2117
2135
  await mkdir(todoDir, { recursive: true });
2118
- await writeFile(todoPath, JSON.stringify({
2136
+ await writeFile(todoPath(), JSON.stringify({
2119
2137
  agentId: testAgentId,
2120
2138
  updatedAt: new Date().toISOString(),
2121
2139
  background: "test background",
@@ -2124,7 +2142,7 @@ describe("McpServer todo guard on poll_message", () => {
2124
2142
  }
2125
2143
  async function cleanupTodoFile() {
2126
2144
  try {
2127
- await rm(todoPath, { force: true });
2145
+ await rm(todoPath(), { force: true });
2128
2146
  }
2129
2147
  catch {
2130
2148
  // 忽略清理失败
@@ -1,2 +1,16 @@
1
1
  import type { AgentConfig, RuntimeLaunchBinding, ServerConfig } from "./types.js";
2
+
3
+ export type RuntimeLaunchBindingClaimOutcome = {
4
+ status: "claimed";
5
+ binding: RuntimeLaunchBinding;
6
+ } | {
7
+ status: "missing";
8
+ } | {
9
+ status: "unreachable";
10
+ message: string;
11
+ } | {
12
+ status: "skipped";
13
+ reason: string;
14
+ };
15
+ export declare function claimRuntimeLaunchBindingDetailed(agentConfig: AgentConfig, serverConfig: ServerConfig): Promise<RuntimeLaunchBindingClaimOutcome>;
2
16
  export declare function claimRuntimeLaunchBinding(agentConfig: AgentConfig, serverConfig: ServerConfig): Promise<RuntimeLaunchBinding | null>;
@@ -1,2 +1,2 @@
1
- import{logger as u}from"./logger.js";const A=5,h=200,L=10,M=5e3;function f(){return process.env.AWS_MCP_CLAIM_LAUNCH_BINDING!=="false"}function I(n){return String(n.runtimeBridgeBaseUrl||"").trim().replace(/\/$/,"")}function _(n,r,e,i){const o=Number.parseInt(String(process.env[n]||""),10);return Number.isFinite(o)?Math.min(Math.max(o,e),i):r}function p(){return _("AWS_MCP_LAUNCH_BINDING_CLAIM_ATTEMPTS",A,1,L)}function w(){return _("AWS_MCP_LAUNCH_BINDING_CLAIM_RETRY_DELAY_MS",h,0,M)}function g(n){return n<=0?Promise.resolve():new Promise(r=>{setTimeout(r,n)})}function B(n,r){const e={workspacePath:String(n.workspacePath||"").trim(),serverUrl:r.serverUrl},i=String(n.agentId||"").trim();return i&&(e.agentId=i),e}function R(n){return n.status===404||n.status>=500}async function S(n,r){if(!f())return null;const e=I(r);if(!e)return u.warn("[RuntimeLaunchBinding] \u5DF2\u8BF7\u6C42\u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A\uFF0C\u4F46\u672A\u914D\u7F6E AWS_RUNTIME_BRIDGE_BASE_URL"),null;const i=String(n.workspacePath||"").trim();if(!i)return u.warn("[RuntimeLaunchBinding] workspacePath \u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A"),null;const o=B(n,r),s=p(),m=w();for(let a=1;a<=s;a+=1){let t;try{t=await fetch(`${e}/runtime/binding/claim-agent`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)})}catch(l){const d=l instanceof Error?l.message:String(l);if(a<s){u.warn(`[RuntimeLaunchBinding] \u65E0\u6CD5\u8FDE\u63A5 runtime-bridge \u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A\uFF0C\u51C6\u5907\u91CD\u8BD5 ${a}/${s}: ${d}`),await g(m);continue}return u.warn(`[RuntimeLaunchBinding] \u65E0\u6CD5\u8FDE\u63A5 runtime-bridge \u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A: ${d}`),null}if(!t.ok){if(R(t)&&a<s){u.warn(`[RuntimeLaunchBinding] \u542F\u52A8\u7ED1\u5B9A\u6682\u4E0D\u53EF\u7528\uFF0C\u51C6\u5907\u91CD\u8BD5 ${a}/${s}: ${t.status} ${t.statusText}`),await g(m);continue}if(t.status===404)return u.warn(`[RuntimeLaunchBinding] \u672A\u627E\u5230\u5F85\u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A: ${i}`),null;throw new Error(`Failed to claim runtime launch binding: ${t.status} ${t.statusText}`)}const c=await t.json();if(!c.agentId)throw new Error(c.error||"runtime-bridge did not return agentId");return c}return null}export{S as claimRuntimeLaunchBinding};
1
+ import{logger as s}from"./logger.js";const p=5,h=200,A=10,f=5e3;function L(){return process.env.AWS_MCP_CLAIM_LAUNCH_BINDING!=="false"}function M(t){return String(t.runtimeBridgeBaseUrl||"").trim().replace(/\/$/,"")}function g(t,n,e,i){const o=Number.parseInt(String(process.env[t]||""),10);return Number.isFinite(o)?Math.min(Math.max(o,e),i):n}function w(){return g("AWS_MCP_LAUNCH_BINDING_CLAIM_ATTEMPTS",p,1,A)}function B(){return g("AWS_MCP_LAUNCH_BINDING_CLAIM_RETRY_DELAY_MS",h,0,f)}function _(t){return t<=0?Promise.resolve():new Promise(n=>{setTimeout(n,t)})}function I(t,n){const e={workspacePath:String(t.workspacePath||"").trim(),serverUrl:n.serverUrl},i=String(t.agentId||"").trim();return i&&(e.agentId=i),e}function R(t){return t.status===404||t.status>=500}async function b(t,n){if(!L())return{status:"skipped",reason:"launch binding claim disabled"};const e=M(n);if(!e)return s.warn("[RuntimeLaunchBinding] \u5DF2\u8BF7\u6C42\u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A\uFF0C\u4F46\u672A\u914D\u7F6E AWS_RUNTIME_BRIDGE_BASE_URL"),{status:"skipped",reason:"runtime bridge base url not configured"};const i=String(t.workspacePath||"").trim();if(!i)return s.warn("[RuntimeLaunchBinding] workspacePath \u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A"),{status:"skipped",reason:"workspace path is empty"};const o=I(t,n),u=w(),l=B();for(let a=1;a<=u;a+=1){let r;try{r=await fetch(`${e}/runtime/binding/claim-agent`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)})}catch(d){const m=d instanceof Error?d.message:String(d);if(a<u){s.warn(`[RuntimeLaunchBinding] \u65E0\u6CD5\u8FDE\u63A5 runtime-bridge \u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A\uFF0C\u51C6\u5907\u91CD\u8BD5 ${a}/${u}: ${m}`),await _(l);continue}return s.warn(`[RuntimeLaunchBinding] \u65E0\u6CD5\u8FDE\u63A5 runtime-bridge \u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A: ${m}`),{status:"unreachable",message:m}}if(!r.ok){if(R(r)&&a<u){s.warn(`[RuntimeLaunchBinding] \u542F\u52A8\u7ED1\u5B9A\u6682\u4E0D\u53EF\u7528\uFF0C\u51C6\u5907\u91CD\u8BD5 ${a}/${u}: ${r.status} ${r.statusText}`),await _(l);continue}if(r.status===404)return s.warn(`[RuntimeLaunchBinding] \u672A\u627E\u5230\u5F85\u8BA4\u9886\u542F\u52A8\u7ED1\u5B9A: ${i}`),{status:"missing"};throw new Error(`Failed to claim runtime launch binding: ${r.status} ${r.statusText}`)}const c=await r.json();if(!c.agentId)throw new Error(c.error||"runtime-bridge did not return agentId");return{status:"claimed",binding:c}}return{status:"missing"}}async function S(t,n){const e=await b(t,n);return e.status==="claimed"?e.binding:null}export{S as claimRuntimeLaunchBinding,b as claimRuntimeLaunchBindingDetailed};
2
2
 
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { afterEach, beforeEach, describe, it } from 'node:test';
3
- import { claimRuntimeLaunchBinding } from './runtime-launch-binding.js';
3
+ import { claimRuntimeLaunchBinding, claimRuntimeLaunchBindingDetailed, } from './runtime-launch-binding.js';
4
4
  describe('claimRuntimeLaunchBinding', () => {
5
5
  const originalEnv = { ...process.env };
6
6
  const originalFetch = global.fetch;
@@ -160,4 +160,42 @@ describe('claimRuntimeLaunchBinding', () => {
160
160
  });
161
161
  assert.equal(result?.agentId, 'agent-a');
162
162
  });
163
+ describe('claimRuntimeLaunchBindingDetailed', () => {
164
+ beforeEach(() => {
165
+ process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'true';
166
+ process.env.AWS_MCP_LAUNCH_BINDING_CLAIM_ATTEMPTS = '2';
167
+ process.env.AWS_MCP_LAUNCH_BINDING_CLAIM_RETRY_DELAY_MS = '0';
168
+ });
169
+ it('reports unreachable when bridge connection keeps failing', async () => {
170
+ let calls = 0;
171
+ global.fetch = async () => {
172
+ calls += 1;
173
+ throw new Error('fetch failed');
174
+ };
175
+ const outcome = await claimRuntimeLaunchBindingDetailed(agentConfig, serverConfig);
176
+ assert.equal(calls, 2);
177
+ assert.equal(outcome.status, 'unreachable');
178
+ assert.equal(outcome.status === 'unreachable' && outcome.message.includes('fetch failed'), true);
179
+ });
180
+ it('reports missing when bridge responds 404 on every attempt', async () => {
181
+ let calls = 0;
182
+ global.fetch = async () => {
183
+ calls += 1;
184
+ return {
185
+ ok: false,
186
+ status: 404,
187
+ statusText: 'Not Found',
188
+ json: async () => ({ ok: false, error: 'launch binding not found' }),
189
+ };
190
+ };
191
+ const outcome = await claimRuntimeLaunchBindingDetailed(agentConfig, serverConfig);
192
+ assert.equal(calls, 2);
193
+ assert.equal(outcome.status, 'missing');
194
+ });
195
+ it('reports skipped when launch binding claim is disabled', async () => {
196
+ process.env.AWS_MCP_CLAIM_LAUNCH_BINDING = 'false';
197
+ const outcome = await claimRuntimeLaunchBindingDetailed(agentConfig, serverConfig);
198
+ assert.equal(outcome.status, 'skipped');
199
+ });
200
+ });
163
201
  });
@@ -15,7 +15,7 @@
15
15
  "build": "tsc && node ../../scripts/minify-dist.mjs",
16
16
  "dev": "tsc --watch",
17
17
  "start": "node dist/index.js",
18
- "test": "npm run build && node --test dist/*.test.js",
18
+ "test": "npm run build && node --test --test-timeout=60000 dist/*.test.js",
19
19
  "prepare": "npm run build",
20
20
  "lint": "eslint src --max-warnings 0",
21
21
  "lint:fix": "eslint src --fix",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.9.125",
3
+ "version": "1.9.126",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",