aws-runtime-bridge 1.9.109 → 1.9.111

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.
@@ -1153,7 +1153,7 @@ describe("McpServer memory tools", () => {
1153
1153
  assert.equal(result.directMessages[0]?.receiverId, "agent-self");
1154
1154
  assert.match(result.directMessages[0]?.content ?? "", /本地 memory 已超过建议容量/);
1155
1155
  assert.match(result.directMessages[0]?.content ?? "", /get_memory_stats/);
1156
- assert.match(result.directMessages[0]?.prompt ?? "", /无需持久化/);
1156
+ assert.equal(result.directMessages[0]?.prompt, undefined);
1157
1157
  // 非紧急类在初始路径直接返回,不进入 while 循环
1158
1158
  assert.equal(pollCallCount, 0);
1159
1159
  });
@@ -1224,7 +1224,7 @@ describe("McpServer memory tools", () => {
1224
1224
  assert.equal(result.directMessages[0]?.receiverId, "agent-self");
1225
1225
  assert.match(result.directMessages[0]?.content ?? "", /本地 memory 已超过建议容量/);
1226
1226
  assert.match(result.directMessages[0]?.content ?? "", /agent_memory\/recent 类型下当前 33 条 memory/);
1227
- assert.match(result.directMessages[0]?.prompt ?? "", /无需持久化/);
1227
+ assert.equal(result.directMessages[0]?.prompt, undefined);
1228
1228
  });
1229
1229
  });
1230
1230
  it("rejects incomplete memory update arguments", async () => {
@@ -1241,6 +1241,48 @@ describe("McpServer memory tools", () => {
1241
1241
  }), /old_string and new_string must be provided together/);
1242
1242
  });
1243
1243
  });
1244
+ it("get_message with _aws_force_inject returns only force messages and keeps normal ones", async () => {
1245
+ await withMemoryServer(async (server) => {
1246
+ const handleToolCall = getHandleToolCall(server);
1247
+ Reflect.set(server, "agentClient", {
1248
+ fetchUnreadMessages: async () => ({
1249
+ directMessages: [],
1250
+ groupMessages: [],
1251
+ }),
1252
+ getState: () => ({ agentId: "agent-self" }),
1253
+ getStatusReporter: () => null,
1254
+ });
1255
+ const messageBuffer = getMessageBuffer(server);
1256
+ messageBuffer.push({
1257
+ msgId: "dm-force-1",
1258
+ senderId: "human-1",
1259
+ senderName: "Human One",
1260
+ content: "紧急消息",
1261
+ requireReply: false,
1262
+ force: true,
1263
+ timestamp: new Date().toISOString(),
1264
+ });
1265
+ messageBuffer.push({
1266
+ msgId: "dm-normal-1",
1267
+ senderId: "agent-2",
1268
+ senderName: "Agent Two",
1269
+ content: "普通消息",
1270
+ requireReply: false,
1271
+ timestamp: new Date().toISOString(),
1272
+ });
1273
+ // runtime 迭代边界强制注入调用:只取 force=true 的私信
1274
+ const result = (await handleToolCall.call(server, "get_message", {
1275
+ _aws_force_inject: true,
1276
+ }));
1277
+ assert.equal(result.directMessages.length, 1);
1278
+ assert.equal(result.directMessages[0]?.msgId, "dm-force-1");
1279
+ assert.equal(result.groupMessages.length, 0);
1280
+ // 普通消息留在缓冲区,下一次 get_message 正常返回
1281
+ const rest = (await handleToolCall.call(server, "get_message", {}));
1282
+ assert.equal(rest.directMessages.length, 1);
1283
+ assert.equal(rest.directMessages[0]?.msgId, "dm-normal-1");
1284
+ });
1285
+ });
1244
1286
  it("redacts memory content from status reporting", async () => {
1245
1287
  await withMemoryServer(async (server) => {
1246
1288
  const reported = [];
@@ -1976,7 +2018,7 @@ describe("AgentClient group room aggregation", () => {
1976
2018
  assert.equal(result.count, 2);
1977
2019
  });
1978
2020
  });
1979
- describe("McpServer todo reminder on poll_message", () => {
2021
+ describe("McpServer todo guard on poll_message", () => {
1980
2022
  // 使用唯一 agentId 避免污染开发者真实 todo 文件;每次测试写入并清理
1981
2023
  const testAgentId = `test-todo-reminder-${process.pid}-${Date.now()}`;
1982
2024
  const todoDir = path.join(os.homedir(), ".acode", "todos");
@@ -2013,7 +2055,7 @@ describe("McpServer todo reminder on poll_message", () => {
2013
2055
  timestamp: new Date().toISOString(),
2014
2056
  };
2015
2057
  }
2016
- it("returns todo alone when incomplete todos exist and no unread messages", async () => {
2058
+ it("returns todo block message when incomplete todos exist", async () => {
2017
2059
  await writeTodoFile([
2018
2060
  { content: "任务A", status: "pending", priority: "high" },
2019
2061
  { content: "任务B", status: "completed", priority: "low" },
@@ -2021,18 +2063,17 @@ describe("McpServer todo reminder on poll_message", () => {
2021
2063
  try {
2022
2064
  const server = createServerForTodo();
2023
2065
  const handleToolCall = getHandleToolCall(server);
2066
+ // 有未完成 todo 时 poll_message 正常 JSON 返回提示消息(非异常)
2024
2067
  const result = (await handleToolCall.call(server, "poll_message", {}));
2025
- // 无未读消息时,todo 单独返回(组合2)
2026
2068
  assert.equal(result.directMessages.length, 1);
2027
2069
  assert.equal(result.directMessages[0].senderId, "MCP-System");
2028
- assert.equal(result.directMessages[0].senderName, "系统");
2029
- assert.match(result.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2070
+ assert.equal(result.directMessages[0].content, "当前有未处理的todo list不能使用poll_message阻塞获取消息");
2030
2071
  }
2031
2072
  finally {
2032
2073
  await cleanupTodoFile();
2033
2074
  }
2034
2075
  });
2035
- it("returns messages + todo when both unread messages and incomplete todos exist", async () => {
2076
+ it("returns todo block message even with unread messages", async () => {
2036
2077
  await writeTodoFile([
2037
2078
  { content: "任务A", status: "pending", priority: "high" },
2038
2079
  ]);
@@ -2043,18 +2084,16 @@ describe("McpServer todo reminder on poll_message", () => {
2043
2084
  const msg = makeDirectMessage("msg-before-poll");
2044
2085
  buffer.push(msg);
2045
2086
  const handleToolCall = getHandleToolCall(server);
2087
+ // todo 前置检查在 hydrate 之前,即使有未读消息也先返回提示
2046
2088
  const result = (await handleToolCall.call(server, "poll_message", {}));
2047
- // 有未读消息 + todo -> 组合4: 未读消息 + todo
2048
- assert.equal(result.directMessages.length, 2);
2049
- assert.equal(result.directMessages[0].senderId, "MCP-System");
2050
- assert.match(result.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2051
- assert.equal(result.directMessages[1].msgId, "msg-before-poll");
2089
+ assert.equal(result.directMessages.length, 1);
2090
+ assert.equal(result.directMessages[0].content, "当前有未处理的todo list不能使用poll_message阻塞获取消息");
2052
2091
  }
2053
2092
  finally {
2054
2093
  await cleanupTodoFile();
2055
2094
  }
2056
2095
  });
2057
- it("does not inject todo reminder when all todos are completed", async () => {
2096
+ it("allows poll_message when all todos are completed", async () => {
2058
2097
  await writeTodoFile([
2059
2098
  { content: "任务A", status: "completed", priority: "high" },
2060
2099
  ]);
@@ -2065,7 +2104,7 @@ describe("McpServer todo reminder on poll_message", () => {
2065
2104
  setTimeout(() => buffer.push(wake), 50);
2066
2105
  const handleToolCall = getHandleToolCall(server);
2067
2106
  const result = (await handleToolCall.call(server, "poll_message", {}));
2068
- // 全完成时无 todo,进入 while 循环等待消息后返回
2107
+ // 全完成时无未完成 todo,进入 while 循环等待消息后返回
2069
2108
  assert.equal(result.directMessages.length, 1);
2070
2109
  assert.equal(result.directMessages[0].msgId, "wake-completed");
2071
2110
  }
@@ -2073,119 +2112,58 @@ describe("McpServer todo reminder on poll_message", () => {
2073
2112
  await cleanupTodoFile();
2074
2113
  }
2075
2114
  });
2076
- it("does not re-inject todo reminder on subsequent poll_message when todo fingerprint unchanged", async () => {
2077
- await writeTodoFile([
2078
- { content: "任务A", status: "pending", priority: "high" },
2079
- ]);
2080
- try {
2081
- const server = createServerForTodo();
2082
- const handleToolCall = getHandleToolCall(server);
2083
- // 第一次 poll:无未读消息 -> todo 单独返回(组合2),指纹被记录
2084
- const result1 = (await handleToolCall.call(server, "poll_message", {}));
2085
- assert.equal(result1.directMessages.length, 1);
2086
- assert.match(result1.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2087
- // 第二次 poll(待办指纹未变):不注入 todo 提醒,进入 while 阻塞等待真实消息
2088
- // 模拟外部消息到达后唤醒
2089
- const buffer = getMessageBuffer(server);
2090
- const wake = makeDirectMessage("wake-after-dedup");
2091
- setTimeout(() => buffer.push(wake), 50);
2092
- const result2 = (await handleToolCall.call(server, "poll_message", {}));
2093
- // 指纹去重生效:只返回真实消息,不再附带 todo 提醒
2094
- assert.equal(result2.directMessages.length, 1);
2095
- assert.equal(result2.directMessages[0].msgId, "wake-after-dedup");
2096
- }
2097
- finally {
2098
- await cleanupTodoFile();
2099
- }
2100
- });
2101
- it("re-injects todo reminder when todo fingerprint changes (new item added)", async () => {
2102
- await writeTodoFile([
2103
- { content: "任务A", status: "pending", priority: "high" },
2104
- ]);
2105
- try {
2106
- const server = createServerForTodo();
2107
- const handleToolCall = getHandleToolCall(server);
2108
- // 第一次 poll:1 项未完成,返回 todo 提醒
2109
- const result1 = (await handleToolCall.call(server, "poll_message", {}));
2110
- assert.equal(result1.directMessages.length, 1);
2111
- assert.match(result1.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2112
- // 修改 todo:新增一项未完成,指纹变化
2113
- await writeTodoFile([
2114
- { content: "任务A", status: "pending", priority: "high" },
2115
- { content: "任务B", status: "in_progress", priority: "medium" },
2116
- ]);
2117
- // 第二次 poll:指纹变化,重新注入 todo 提醒
2118
- const result2 = (await handleToolCall.call(server, "poll_message", {}));
2119
- assert.equal(result2.directMessages.length, 1);
2120
- assert.match(result2.directMessages[0].content, /Todo 列表存在 2 项未完成/);
2121
- }
2122
- finally {
2123
- await cleanupTodoFile();
2124
- }
2115
+ it("allows poll_message when todo file is absent", async () => {
2116
+ // 确保文件不存在
2117
+ await cleanupTodoFile();
2118
+ const server = createServerForTodo();
2119
+ const buffer = getMessageBuffer(server);
2120
+ const wake = makeDirectMessage("wake-no-file");
2121
+ setTimeout(() => buffer.push(wake), 50);
2122
+ const handleToolCall = getHandleToolCall(server);
2123
+ const result = (await handleToolCall.call(server, "poll_message", {}));
2124
+ // 无 todo 文件 -> 进入 while 循环等待消息后返回
2125
+ assert.equal(result.directMessages.length, 1);
2126
+ assert.equal(result.directMessages[0].msgId, "wake-no-file");
2125
2127
  });
2126
- it("re-injects todo reminder when todo fingerprint changes (status changed)", async () => {
2128
+ it("returns todo block message when todo has in_progress status", async () => {
2127
2129
  await writeTodoFile([
2128
- { content: "任务A", status: "pending", priority: "high" },
2130
+ { content: "任务A", status: "in_progress", priority: "high" },
2129
2131
  ]);
2130
2132
  try {
2131
2133
  const server = createServerForTodo();
2132
2134
  const handleToolCall = getHandleToolCall(server);
2133
- // 第一次 poll:pending 状态,返回 todo 提醒
2134
- const result1 = (await handleToolCall.call(server, "poll_message", {}));
2135
- assert.match(result1.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2136
- // 修改 todo:状态从 pending -> in_progress,指纹变化
2137
- await writeTodoFile([
2138
- { content: "任务A", status: "in_progress", priority: "high" },
2139
- ]);
2140
- // 第二次 poll:状态变化导致指纹变化,重新注入
2141
- const result2 = (await handleToolCall.call(server, "poll_message", {}));
2142
- assert.match(result2.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2135
+ const result = (await handleToolCall.call(server, "poll_message", {}));
2136
+ assert.equal(result.directMessages.length, 1);
2137
+ assert.equal(result.directMessages[0].content, "当前有未处理的todo list不能使用poll_message阻塞获取消息");
2143
2138
  }
2144
2139
  finally {
2145
2140
  await cleanupTodoFile();
2146
2141
  }
2147
2142
  });
2148
- it("resets fingerprint when all todos become completed", async () => {
2143
+ it("allows poll_message again after todos are all completed", async () => {
2149
2144
  await writeTodoFile([
2150
2145
  { content: "任务A", status: "pending", priority: "high" },
2151
2146
  ]);
2152
2147
  try {
2153
2148
  const server = createServerForTodo();
2154
2149
  const handleToolCall = getHandleToolCall(server);
2155
- // 第一次 poll:有未完成项,返回 todo 提醒,记录指纹
2150
+ // 第一次 poll:有未完成项 -> 返回提示消息(正常 JSON)
2156
2151
  const result1 = (await handleToolCall.call(server, "poll_message", {}));
2157
- assert.match(result1.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2158
- // 全部完成:指纹重置
2152
+ assert.equal(result1.directMessages.length, 1);
2153
+ assert.equal(result1.directMessages[0].content, "当前有未处理的todo list不能使用poll_message阻塞获取消息");
2154
+ // 全部完成后:poll_message 恢复正常阻塞,等消息返回
2159
2155
  await writeTodoFile([
2160
2156
  { content: "任务A", status: "completed", priority: "high" },
2161
2157
  ]);
2162
2158
  const buffer = getMessageBuffer(server);
2163
- const wake1 = makeDirectMessage("wake-all-completed");
2164
- setTimeout(() => buffer.push(wake1), 50);
2165
- const result2 = (await handleToolCall.call(server, "poll_message", {}));
2166
- assert.equal(result2.directMessages[0].msgId, "wake-all-completed");
2167
- // 再次新增未完成项:指纹与已重置的 null 不同,应重新提醒
2168
- await writeTodoFile([
2169
- { content: "任务B", status: "pending", priority: "medium" },
2170
- ]);
2171
- const result3 = (await handleToolCall.call(server, "poll_message", {}));
2172
- assert.match(result3.directMessages[0].content, /Todo 列表存在 1 项未完成/);
2159
+ const wake = makeDirectMessage("wake-after-complete");
2160
+ setTimeout(() => buffer.push(wake), 50);
2161
+ const result = (await handleToolCall.call(server, "poll_message", {}));
2162
+ assert.equal(result.directMessages.length, 1);
2163
+ assert.equal(result.directMessages[0].msgId, "wake-after-complete");
2173
2164
  }
2174
2165
  finally {
2175
2166
  await cleanupTodoFile();
2176
2167
  }
2177
2168
  });
2178
- it("does not inject todo reminder when todo file is absent", async () => {
2179
- // 确保文件不存在
2180
- await cleanupTodoFile();
2181
- const server = createServerForTodo();
2182
- const buffer = getMessageBuffer(server);
2183
- const wake = makeDirectMessage("wake-no-file");
2184
- setTimeout(() => buffer.push(wake), 50);
2185
- const handleToolCall = getHandleToolCall(server);
2186
- const result = (await handleToolCall.call(server, "poll_message", {}));
2187
- // 无 todo 文件 -> 进入 while 循环等待消息后返回
2188
- assert.equal(result.directMessages.length, 1);
2189
- assert.equal(result.directMessages[0].msgId, "wake-no-file");
2190
- });
2191
2169
  });
@@ -43,6 +43,8 @@ export declare class MessageBuffer {
43
43
 
44
44
  get(): ClassifiedMessages;
45
45
 
46
+ takeForceDirectMessages(): ClassifiedMessages;
47
+
46
48
  size(): number;
47
49
 
48
50
  clear(): void;
@@ -1,2 +1,2 @@
1
- import{logger as r}from"./logger.js";const g=500;function h(l){if(!(l.msgId===void 0||l.msgId===null))return l.msgId}class u{buffer=[];resolvers=[];onMessageArrived=null;isPolling=!1;seenMsgIds=new Map;setOnMessageArrived(e){this.onMessageArrived=e}setIsPolling(e){this.isPolling=e}markSeen(e){const s=h(e);s!==void 0&&(this.seenMsgIds.set(s,Date.now()),this.cleanupSeenMsgIds())}isDuplicate(e){const s=h(e);return s===void 0?!1:this.seenMsgIds.has(s)?!0:(this.seenMsgIds.set(s,Date.now()),this.cleanupSeenMsgIds(),!1)}cleanupSeenMsgIds(){if(this.seenMsgIds.size<=g)return;let e=0;const s=this.seenMsgIds.size-g;for(const t of this.seenMsgIds.keys()){if(e>=s)break;this.seenMsgIds.delete(t),e++}}push(e){if(this.isDuplicate(e)){r.info(`[MessageBuffer] \u5FFD\u7565\u91CD\u590D\u6D88\u606F\uFF0CmsgId: ${e.msgId}`);return}this.buffer.push(e),r.info(`[MessageBuffer] \u6D88\u606F\u5165\u7F13\u51B2\u533A\uFF0C\u5F53\u524D\u6570\u91CF: ${this.buffer.length}`),this.isPolling&&this.onMessageArrived&&(r.info("[MessageBuffer] \u68C0\u6D4B\u5230 poll \u7B49\u5F85\u4E2D\uFF0C\u89E6\u53D1\u6D88\u606F\u5230\u8FBE\u56DE\u8C03"),this.onMessageArrived()),this.tryDispatch()}setProfileMessage(e){r.info(`[MessageBuffer] \u6536\u5230 Profile \u63A8\u9001\uFF0CdisplayName: ${e.displayName}\u3002\u8EAB\u4EFD\u4FE1\u606F\u8BF7\u901A\u8FC7 get_profile \u83B7\u53D6\u3002`)}removeProfileMessage(){r.info("[MessageBuffer] removeProfileMessage \u88AB\u8C03\u7528\uFF0C\u5F53\u524D\u65E0\u9700\u79FB\u9664\u672A\u8BFB\u8EAB\u4EFD\u6D88\u606F")}hasProfileMessage(){return!1}wakeUpWaiters(){if(this.tryDispatch(),this.buffer.length===0&&this.resolvers.length>0){const e=this.resolvers.shift();e&&(this.updatePollingState(),r.info("[MessageBuffer] wakeUpWaiters: \u5524\u9192 poll \u8D70 hydrate \u91CD\u67E5"),e({directMessages:[],groupMessages:[]}))}}getProfileMessage(){return null}updatePollingState(){this.isPolling=this.resolvers.length>0}poll(e){if(r.info(`[MessageBuffer] poll() \u88AB\u8C03\u7528\uFF0Cbuffer: ${this.buffer.length}${e?`\uFF0C\u8D85\u65F6: ${e}ms`:""}`),this.buffer.length>0){const s=this.consumeAll();return r.info(`[MessageBuffer] poll \u7ACB\u5373\u8FD4\u56DE\uFF0C\u79C1\u804A: ${s.directMessages.length}\uFF0C\u7FA4\u804A: ${s.groupMessages.length}`),Promise.resolve(s)}return this.resolvers.length===0&&(this.isPolling=!0),new Promise(s=>{let t=null,n=!1;const f=i=>{if(n)return;n=!0;const o=this.resolvers.indexOf(f);o>-1&&this.resolvers.splice(o,1),t&&(clearTimeout(t),t=null),this.updatePollingState(),r.info(`[MessageBuffer] poll \u6536\u5230\u6D88\u606F\uFF0C\u79C1\u804A: ${i.directMessages.length}\uFF0C\u7FA4\u804A: ${i.groupMessages.length}`),s(i)};this.resolvers.push(f),this.tryDispatch(),e&&e>0&&(t=setTimeout(()=>{if(n)return;n=!0;const i=this.resolvers.indexOf(f);i>-1&&this.resolvers.splice(i,1),this.updatePollingState(),r.info(`[MessageBuffer] poll \u8D85\u65F6\uFF08${e}ms\uFF09\uFF0C\u8FD4\u56DE\u7A7A\u6D88\u606F`),s({directMessages:[],groupMessages:[]})},e))})}get(){if(this.buffer.length>0){const e=this.consumeAll();return r.info(`[MessageBuffer] get \u8FD4\u56DE\u6D88\u606F\uFF0C\u79C1\u804A: ${e.directMessages.length}\uFF0C\u7FA4\u804A: ${e.groupMessages.length}`),e}return{directMessages:[],groupMessages:[]}}size(){return this.buffer.length}clear(){this.buffer=[];for(const e of this.resolvers)e({directMessages:[],groupMessages:[]});this.resolvers=[],this.isPolling=!1,this.seenMsgIds.clear()}tryDispatch(){for(;this.resolvers.length>0&&this.buffer.length>0;){const e=this.resolvers.shift();if(e){const s=this.consumeAll();e(s)}}this.updatePollingState()}consumeAll(){const e=[],s=[];for(;this.buffer.length>0;){const t=this.buffer.shift();t&&(this.markSeen(t),this.isDirectMessage(t)?e.push(t):s.push(t))}return{directMessages:e,groupMessages:s}}isDirectMessage(e){return typeof e.msgId=="string"}}export{u as MessageBuffer};
1
+ import{logger as r}from"./logger.js";const o=500;function h(n){if(!(n.msgId===void 0||n.msgId===null))return n.msgId}class u{buffer=[];resolvers=[];onMessageArrived=null;isPolling=!1;seenMsgIds=new Map;setOnMessageArrived(e){this.onMessageArrived=e}setIsPolling(e){this.isPolling=e}markSeen(e){const s=h(e);s!==void 0&&(this.seenMsgIds.set(s,Date.now()),this.cleanupSeenMsgIds())}isDuplicate(e){const s=h(e);return s===void 0?!1:this.seenMsgIds.has(s)?!0:(this.seenMsgIds.set(s,Date.now()),this.cleanupSeenMsgIds(),!1)}cleanupSeenMsgIds(){if(this.seenMsgIds.size<=o)return;let e=0;const s=this.seenMsgIds.size-o;for(const t of this.seenMsgIds.keys()){if(e>=s)break;this.seenMsgIds.delete(t),e++}}push(e){if(this.isDuplicate(e)){r.info(`[MessageBuffer] \u5FFD\u7565\u91CD\u590D\u6D88\u606F\uFF0CmsgId: ${e.msgId}`);return}this.buffer.push(e),r.info(`[MessageBuffer] \u6D88\u606F\u5165\u7F13\u51B2\u533A\uFF0C\u5F53\u524D\u6570\u91CF: ${this.buffer.length}`),this.isPolling&&this.onMessageArrived&&(r.info("[MessageBuffer] \u68C0\u6D4B\u5230 poll \u7B49\u5F85\u4E2D\uFF0C\u89E6\u53D1\u6D88\u606F\u5230\u8FBE\u56DE\u8C03"),this.onMessageArrived()),this.tryDispatch()}setProfileMessage(e){r.info(`[MessageBuffer] \u6536\u5230 Profile \u63A8\u9001\uFF0CdisplayName: ${e.displayName}\u3002\u8EAB\u4EFD\u4FE1\u606F\u8BF7\u901A\u8FC7 get_profile \u83B7\u53D6\u3002`)}removeProfileMessage(){r.info("[MessageBuffer] removeProfileMessage \u88AB\u8C03\u7528\uFF0C\u5F53\u524D\u65E0\u9700\u79FB\u9664\u672A\u8BFB\u8EAB\u4EFD\u6D88\u606F")}hasProfileMessage(){return!1}wakeUpWaiters(){if(this.tryDispatch(),this.buffer.length===0&&this.resolvers.length>0){const e=this.resolvers.shift();e&&(this.updatePollingState(),r.info("[MessageBuffer] wakeUpWaiters: \u5524\u9192 poll \u8D70 hydrate \u91CD\u67E5"),e({directMessages:[],groupMessages:[]}))}}getProfileMessage(){return null}updatePollingState(){this.isPolling=this.resolvers.length>0}poll(e){if(r.info(`[MessageBuffer] poll() \u88AB\u8C03\u7528\uFF0Cbuffer: ${this.buffer.length}${e?`\uFF0C\u8D85\u65F6: ${e}ms`:""}`),this.buffer.length>0){const s=this.consumeAll();return r.info(`[MessageBuffer] poll \u7ACB\u5373\u8FD4\u56DE\uFF0C\u79C1\u804A: ${s.directMessages.length}\uFF0C\u7FA4\u804A: ${s.groupMessages.length}`),Promise.resolve(s)}return this.resolvers.length===0&&(this.isPolling=!0),new Promise(s=>{let t=null,l=!1;const f=i=>{if(l)return;l=!0;const g=this.resolvers.indexOf(f);g>-1&&this.resolvers.splice(g,1),t&&(clearTimeout(t),t=null),this.updatePollingState(),r.info(`[MessageBuffer] poll \u6536\u5230\u6D88\u606F\uFF0C\u79C1\u804A: ${i.directMessages.length}\uFF0C\u7FA4\u804A: ${i.groupMessages.length}`),s(i)};this.resolvers.push(f),this.tryDispatch(),e&&e>0&&(t=setTimeout(()=>{if(l)return;l=!0;const i=this.resolvers.indexOf(f);i>-1&&this.resolvers.splice(i,1),this.updatePollingState(),r.info(`[MessageBuffer] poll \u8D85\u65F6\uFF08${e}ms\uFF09\uFF0C\u8FD4\u56DE\u7A7A\u6D88\u606F`),s({directMessages:[],groupMessages:[]})},e))})}get(){if(this.buffer.length>0){const e=this.consumeAll();return r.info(`[MessageBuffer] get \u8FD4\u56DE\u6D88\u606F\uFF0C\u79C1\u804A: ${e.directMessages.length}\uFF0C\u7FA4\u804A: ${e.groupMessages.length}`),e}return{directMessages:[],groupMessages:[]}}takeForceDirectMessages(){const e=[],s=[];for(const t of this.buffer)this.isDirectMessage(t)&&t.force===!0?(this.markSeen(t),e.push(t)):s.push(t);return this.buffer=s,e.length>0&&(r.info(`[MessageBuffer] takeForceDirectMessages \u53D6\u8D70\u5F3A\u5236\u79C1\u4FE1 ${e.length} \u6761\uFF0C\u5269\u4F59 ${s.length} \u6761`),this.tryDispatch()),{directMessages:e,groupMessages:[]}}size(){return this.buffer.length}clear(){this.buffer=[];for(const e of this.resolvers)e({directMessages:[],groupMessages:[]});this.resolvers=[],this.isPolling=!1,this.seenMsgIds.clear()}tryDispatch(){for(;this.resolvers.length>0&&this.buffer.length>0;){const e=this.resolvers.shift();if(e){const s=this.consumeAll();e(s)}}this.updatePollingState()}consumeAll(){const e=[],s=[];for(;this.buffer.length>0;){const t=this.buffer.shift();t&&(this.markSeen(t),this.isDirectMessage(t)?e.push(t):s.push(t))}return{directMessages:e,groupMessages:s}}isDirectMessage(e){return typeof e.msgId=="string"}}export{u as MessageBuffer};
2
2
 
@@ -40,4 +40,43 @@ describe('MessageBuffer poll', () => {
40
40
  assert.equal(messages.groupMessages.length, 1);
41
41
  assert.deepEqual(messages.groupMessages[0], groupMessage);
42
42
  });
43
+ it('takeForceDirectMessages only takes force=true direct messages and keeps others', () => {
44
+ const buffer = new MessageBuffer();
45
+ const forcedMessage = {
46
+ msgId: 'dm-force-1',
47
+ senderId: 'human-1',
48
+ senderName: 'Human One',
49
+ content: '紧急:先停下处理这个',
50
+ requireReply: false,
51
+ force: true,
52
+ timestamp: new Date().toISOString()
53
+ };
54
+ const normalMessage = {
55
+ msgId: 'dm-normal-1',
56
+ senderId: 'agent-2',
57
+ senderName: 'Agent Two',
58
+ content: '普通消息',
59
+ requireReply: false,
60
+ timestamp: new Date().toISOString()
61
+ };
62
+ const groupMessage = {
63
+ msgId: 2,
64
+ senderId: 'human-1',
65
+ senderName: 'Human One',
66
+ content: '群消息',
67
+ timestamp: new Date().toISOString()
68
+ };
69
+ buffer.push(normalMessage);
70
+ buffer.push(groupMessage);
71
+ buffer.push(forcedMessage);
72
+ const taken = buffer.takeForceDirectMessages();
73
+ assert.equal(taken.directMessages.length, 1);
74
+ assert.deepEqual(taken.directMessages[0], forcedMessage);
75
+ assert.deepEqual(taken.groupMessages, []);
76
+ const rest = buffer.get();
77
+ assert.equal(rest.directMessages.length, 1);
78
+ assert.deepEqual(rest.directMessages[0], normalMessage);
79
+ assert.equal(rest.groupMessages.length, 1);
80
+ assert.deepEqual(rest.groupMessages[0], groupMessage);
81
+ });
43
82
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.9.109",
3
+ "version": "1.9.111",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",