aws-runtime-bridge 1.7.3 → 1.7.5

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.
@@ -46,6 +46,7 @@ export declare class ClaudeSdkAdapter extends EventEmitter implements BaseProvid
46
46
  cleanup(): void;
47
47
  private idleCommands;
48
48
  private idleTimers;
49
+ private idlePollLoops;
49
50
  private lastActivityAt;
50
51
  /**
51
52
  * 设置空闲命令配置
@@ -72,6 +73,11 @@ export declare class ClaudeSdkAdapter extends EventEmitter implements BaseProvid
72
73
  private sendIdlePrompt;
73
74
  private isMessagePollingIdleCommand;
74
75
  private buildMessagePollingIdlePrompt;
76
+ /**
77
+ * 获取当前会话的空闲轮询驱动器。
78
+ * 主流程:惰性创建 -> 绑定会话状态与命令读取 -> 统一派发 poll/普通 idle prompt。
79
+ */
80
+ private getIdlePollLoop;
75
81
  /**
76
82
  * 发送手动快捷键 token 序列,和空闲自动命令共用解析逻辑。
77
83
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ClaudeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/ClaudeSdkAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClaudeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/ClaudeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EAGpB,mBAAmB,EACnB,aAAa,EACd,MAAM,YAAY,CAAC;AA0HpB;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,YAAa,YAAW,mBAAmB;IAC/E,QAAQ,CAAC,UAAU,iBAAiB;IACpC,QAAQ,CAAC,WAAW,iBAAiB;IAErC,OAAO,CAAC,QAAQ,CAA0C;IAC1D,OAAO,CAAC,UAAU,CAAoC;IACtD,OAAO,CAAC,YAAY,CAAuD;IAC3E,OAAO,CAAC,gBAAgB,CAA2C;IACnE,OAAO,CAAC,SAAS,CAAiB;IAGlC,OAAO,CAAC,kBAAkB,CAGZ;IAGd,OAAO,CAAC,gBAAgB,CAGV;IAGd,OAAO,CAAC,gBAAgB,CAAkC;IAG1D,OAAO,CAAC,eAAe,CAA0D;IAGjF,OAAO,CAAC,gBAAgB,CAAkC;IAIpD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyF5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwCpE;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAmDrB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAYnE,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBrF,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIpD,OAAO,IAAI,IAAI;IAoBf,OAAO,CAAC,YAAY,CAAiF;IACrG,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,cAAc,CAAkC;IAExD;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IASzG;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA0B1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IASzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAuCtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,2BAA2B;IAUnC,OAAO,CAAC,6BAA6B;IAIrC;;;OAGG;IACH,OAAO,CAAC,eAAe;IAevB;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1E;;OAEG;YACW,kBAAkB;IA+DhC;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;YAyBF,OAAO;IAarB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA8F5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAMhC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAsCjC,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,+BAA+B;IAIvC,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,qBAAqB;IAO7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAsB/B,OAAO,CAAC,iBAAiB;IAmFzB,OAAO,CAAC,uBAAuB;YAqCjB,aAAa;IAwB7B,OAAO,CAAC,gBAAgB;IAwKtB,OAAO,CAAC,aAAa;IAuErB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAuHzB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,cAAc;CAYvB"}
@@ -15,6 +15,7 @@ import * as fs from 'fs';
15
15
  import * as path from 'path';
16
16
  import { v4 as uuidv4 } from 'uuid';
17
17
  import { getToolActionInfo } from './types.js';
18
+ import { IdlePollLoop } from './idle-poll-loop.js';
18
19
  import { importBridgeSdkPackage } from '../utils/sdk-package-loader.js';
19
20
  // ============ AsyncIterableQueue ============
20
21
  /**
@@ -89,6 +90,7 @@ export class ClaudeSdkAdapter extends EventEmitter {
89
90
  // ============ 空闲命令管理 ============
90
91
  this.idleCommands = new Map();
91
92
  this.idleTimers = new Map();
93
+ this.idlePollLoops = new Map();
92
94
  this.lastActivityAt = new Map();
93
95
  }
94
96
  // ============ 生命周期方法 ============
@@ -369,7 +371,8 @@ export class ClaudeSdkAdapter extends EventEmitter {
369
371
  return;
370
372
  }
371
373
  if (this.isMessagePollingIdleCommand(commands.idleInputCommand)) {
372
- this.sendIdlePrompt(sessionId, this.buildMessagePollingIdlePrompt());
374
+ const idleLoop = this.getIdlePollLoop(sessionId);
375
+ idleLoop.start();
373
376
  return;
374
377
  }
375
378
  this.lastActivityAt.set(sessionId, Date.now());
@@ -383,6 +386,7 @@ export class ClaudeSdkAdapter extends EventEmitter {
383
386
  * 停止空闲检测定时器
384
387
  */
385
388
  stopIdleDetection(sessionId) {
389
+ this.idlePollLoops.get(sessionId)?.stop();
386
390
  const timer = this.idleTimers.get(sessionId);
387
391
  if (timer) {
388
392
  clearInterval(timer);
@@ -428,13 +432,13 @@ export class ClaudeSdkAdapter extends EventEmitter {
428
432
  /**
429
433
  * 发送空闲提示到 SDK
430
434
  */
431
- sendIdlePrompt(sessionId, prompt) {
435
+ sendIdlePrompt(sessionId, prompt, options) {
432
436
  const inputStream = this.inputStreams.get(sessionId);
433
437
  if (!inputStream)
434
438
  return;
435
- // 更新状态为思考中
439
+ // 更新状态。poll_message 是阻塞监听动作,保持 waiting_input,避免 UI 误判为“思考中卡死”。
436
440
  const session = this.sessions.get(sessionId);
437
- if (session) {
441
+ if (session && !options?.keepWaitingStatus) {
438
442
  session.status = 'thinking';
439
443
  this.emit('status-change', sessionId, 'thinking');
440
444
  }
@@ -456,7 +460,25 @@ export class ClaudeSdkAdapter extends EventEmitter {
456
460
  return lower.includes('poll_message') || lower.includes('get_message') || normalized.includes('取消息');
457
461
  }
458
462
  buildMessagePollingIdlePrompt() {
459
- return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。';
463
+ return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理;如果 start_task 失败且提示任务已被其他实例领取,请不要停下,继续调用 poll_message 等待新消息;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
464
+ }
465
+ /**
466
+ * 获取当前会话的空闲轮询驱动器。
467
+ * 主流程:惰性创建 -> 绑定会话状态与命令读取 -> 统一派发 poll/普通 idle prompt。
468
+ */
469
+ getIdlePollLoop(sessionId) {
470
+ const existing = this.idlePollLoops.get(sessionId);
471
+ if (existing)
472
+ return existing;
473
+ const loop = new IdlePollLoop({
474
+ getStatus: () => this.sessions.get(sessionId)?.status,
475
+ getCommand: () => this.idleCommands.get(sessionId)?.idleInputCommand,
476
+ isPollingCommand: (command) => this.isMessagePollingIdleCommand(command),
477
+ dispatchPollingPrompt: () => this.sendIdlePrompt(sessionId, this.buildMessagePollingIdlePrompt(), { keepWaitingStatus: true }),
478
+ dispatchRegularIdleCommand: (command) => this.sendIdlePrompt(sessionId, command),
479
+ });
480
+ this.idlePollLoops.set(sessionId, loop);
481
+ return loop;
460
482
  }
461
483
  /**
462
484
  * 发送手动快捷键 token 序列,和空闲自动命令共用解析逻辑。
@@ -923,14 +945,17 @@ export class ClaudeSdkAdapter extends EventEmitter {
923
945
  // AI 正在调用工具
924
946
  {
925
947
  const actionInfo = { ...getToolActionInfo(msg.tool_name, msg.tool_input), actionId: msg.tool_use_id };
926
- session.status = 'tool_using';
927
- this.emit('status-change', sessionId, 'tool_using', {
948
+ const nextStatus = actionInfo.actionType === 'idle' ? 'waiting_input' : 'tool_using';
949
+ session.status = nextStatus;
950
+ this.emit('status-change', sessionId, nextStatus, {
928
951
  actionType: actionInfo.actionType,
929
952
  actionLabel: actionInfo.actionLabel,
930
953
  actionDetail: actionInfo.actionDetail,
931
954
  actionId: actionInfo.actionId,
932
955
  });
933
- this.stopIdleDetection(sessionId);
956
+ if (actionInfo.actionType !== 'idle') {
957
+ this.stopIdleDetection(sessionId);
958
+ }
934
959
  this.handleToolUse(sessionId, session, msg, timestamp);
935
960
  }
936
961
  break;
@@ -1216,6 +1241,7 @@ export class ClaudeSdkAdapter extends EventEmitter {
1216
1241
  this.pendingPermissions.delete(sessionId);
1217
1242
  this.pendingQuestions.delete(sessionId);
1218
1243
  this.idleCommands.delete(sessionId);
1244
+ this.idlePollLoops.delete(sessionId);
1219
1245
  this.lastActivityAt.delete(sessionId);
1220
1246
  }
1221
1247
  }
@@ -3,6 +3,7 @@ import * as os from 'os';
3
3
  import * as path from 'path';
4
4
  import { afterEach, describe, expect, it, vi } from 'vitest';
5
5
  import { ClaudeSdkAdapter } from './ClaudeSdkAdapter.js';
6
+ const POLL_PROMPT = '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理;如果 start_task 失败且提示任务已被其他实例领取,请不要停下,继续调用 poll_message 等待新消息;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
6
7
  describe('ClaudeSdkAdapter', () => {
7
8
  afterEach(() => {
8
9
  vi.useRealTimers();
@@ -176,10 +177,13 @@ describe('ClaudeSdkAdapter', () => {
176
177
  idleInputCommand: 'system: 请保持工作循环:持续调用 poll_message 阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。',
177
178
  nonInputCommand: '',
178
179
  });
180
+ await vi.advanceTimersByTimeAsync(1499);
181
+ expect(sendIdlePrompt).not.toHaveBeenCalled();
182
+ expect(sentPrompts).toEqual([]);
183
+ await vi.advanceTimersByTimeAsync(1);
179
184
  expect(sendIdlePrompt).toHaveBeenCalledTimes(1);
180
- expect(sentPrompts).toEqual([
181
- '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。',
182
- ]);
185
+ expect(sentPrompts).toEqual([POLL_PROMPT]);
186
+ expect(adapter.getSessionStatus('session-1')).toBe('waiting_input');
183
187
  });
184
188
  it('starts a blocking poll turn for poll_message-style idle commands', async () => {
185
189
  vi.useFakeTimers();
@@ -205,9 +209,9 @@ describe('ClaudeSdkAdapter', () => {
205
209
  idleInputCommand: 'system: 请保持工作循环:持续调用 poll_message 阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。',
206
210
  nonInputCommand: '',
207
211
  });
208
- expect(sentPrompts).toEqual([
209
- '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。',
210
- ]);
212
+ await vi.advanceTimersByTimeAsync(1500);
213
+ expect(sentPrompts).toEqual([POLL_PROMPT]);
214
+ expect(adapter.getSessionStatus('session-1')).toBe('waiting_input');
211
215
  });
212
216
  it('still auto-wakes Claude for regular idle commands', async () => {
213
217
  vi.useFakeTimers();
@@ -1 +1 @@
1
- {"version":3,"file":"CodexSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/CodexSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,OAAO,KAAK,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EAEd,MAAM,YAAY,CAAC;AA0RpB,qBAAa,eAAgB,SAAQ,YAAa,YAAW,mBAAmB;IAC9E,QAAQ,CAAC,UAAU,WAAW;IAC9B,QAAQ,CAAC,WAAW,WAAW;IAE/B,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,SAAS,CAAC,CAAiB;IAE7B,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmE5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC9D,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAanG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA4BhB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrD,OAAO,IAAI,IAAI;IAOf;;;OAGG;YACW,OAAO;IA6BrB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;YAqCZ,OAAO;IAiCrB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,gBAAgB;IA+ExB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,kBAAkB;IAuD1B,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;YAcR,cAAc;IAuB5B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
1
+ {"version":3,"file":"CodexSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/CodexSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,OAAO,KAAK,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EAEd,MAAM,YAAY,CAAC;AA4RpB,qBAAa,eAAgB,SAAQ,YAAa,YAAW,mBAAmB;IAC9E,QAAQ,CAAC,UAAU,WAAW;IAC9B,QAAQ,CAAC,WAAW,WAAW;IAE/B,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,SAAS,CAAC,CAAiB;IAE7B,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmE5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqC9D,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAanG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBlD,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IA4BhB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIrD,OAAO,IAAI,IAAI;IAOf;;;OAGG;YACW,OAAO;IA6BrB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,kBAAkB;YAqCZ,OAAO;IAiCrB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,gBAAgB;IA+ExB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,kBAAkB;IAuD1B,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAyC1B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,cAAc;YAcR,cAAc;IAyB5B,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { EventEmitter } from 'node:events';
8
8
  import { v4 as uuidv4 } from 'uuid';
9
+ import { IdlePollLoop } from './idle-poll-loop.js';
9
10
  import { getToolActionInfo } from './types.js';
10
11
  function hasErrorCode(error) {
11
12
  return error instanceof Error && 'code' in error && typeof error.code === 'string';
@@ -727,9 +728,29 @@ export class CodexSdkAdapter extends EventEmitter {
727
728
  if (!session || !idleInputCommand)
728
729
  return;
729
730
  if (this.isMessagePollingIdleCommand(idleInputCommand)) {
730
- void this.sendIdlePrompt(sessionId, idleInputCommand);
731
+ if (!session.idlePollLoop) {
732
+ session.idlePollLoop = new IdlePollLoop({
733
+ getStatus: () => this.sessions.get(sessionId)?.adapterSession.status,
734
+ getCommand: () => this.sessions.get(sessionId)?.idleCommands?.idleInputCommand,
735
+ isPollingCommand: (command) => this.isMessagePollingIdleCommand(command),
736
+ dispatchPollingPrompt: (command) => this.sendIdlePrompt(sessionId, command, { keepWaitingStatus: true }),
737
+ dispatchRegularIdleCommand: (command) => this.sendIdlePrompt(sessionId, command),
738
+ regularIdleDelayMs: CODEX_IDLE_THRESHOLD_MS,
739
+ });
740
+ }
741
+ session.idlePollLoop.start();
731
742
  return;
732
743
  }
744
+ if (!session.idlePollLoop) {
745
+ session.idlePollLoop = new IdlePollLoop({
746
+ getStatus: () => this.sessions.get(sessionId)?.adapterSession.status,
747
+ getCommand: () => this.sessions.get(sessionId)?.idleCommands?.idleInputCommand,
748
+ isPollingCommand: (command) => this.isMessagePollingIdleCommand(command),
749
+ dispatchPollingPrompt: (command) => this.sendIdlePrompt(sessionId, command, { keepWaitingStatus: true }),
750
+ dispatchRegularIdleCommand: (command) => this.sendIdlePrompt(sessionId, command),
751
+ regularIdleDelayMs: CODEX_IDLE_THRESHOLD_MS,
752
+ });
753
+ }
733
754
  session.lastActivityAt = Date.now();
734
755
  const timer = setInterval(() => {
735
756
  this.checkIdleState(sessionId);
@@ -738,6 +759,7 @@ export class CodexSdkAdapter extends EventEmitter {
738
759
  }
739
760
  stopIdleDetection(sessionId) {
740
761
  const session = this.sessions.get(sessionId);
762
+ session?.idlePollLoop?.stop();
741
763
  if (session?.idleTimer) {
742
764
  clearInterval(session.idleTimer);
743
765
  session.idleTimer = undefined;
@@ -757,7 +779,7 @@ export class CodexSdkAdapter extends EventEmitter {
757
779
  void this.sendIdlePrompt(sessionId, session.idleCommands.idleInputCommand);
758
780
  this.stopIdleDetection(sessionId);
759
781
  }
760
- async sendIdlePrompt(sessionId, command) {
782
+ async sendIdlePrompt(sessionId, command, options) {
761
783
  const session = this.sessions.get(sessionId);
762
784
  if (!session)
763
785
  return;
@@ -766,8 +788,10 @@ export class CodexSdkAdapter extends EventEmitter {
766
788
  const prompt = this.toIdlePrompt(command);
767
789
  if (!prompt)
768
790
  return;
769
- session.adapterSession.status = 'thinking';
770
- this.emit('status-change', sessionId, 'thinking');
791
+ if (!options?.keepWaitingStatus) {
792
+ session.adapterSession.status = 'thinking';
793
+ this.emit('status-change', sessionId, 'thinking');
794
+ }
771
795
  const abortController = new AbortController();
772
796
  session.currentAbortController = abortController;
773
797
  session.runningTurn = this.runTurn(sessionId, session, prompt, abortController);
@@ -782,7 +806,7 @@ export class CodexSdkAdapter extends EventEmitter {
782
806
  toIdlePrompt(command) {
783
807
  const trimmed = command.trim();
784
808
  if (this.isMessagePollingIdleCommand(trimmed)) {
785
- return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。';
809
+ return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理;如果 start_task 失败且提示任务已被其他实例领取,请不要停下,继续调用 poll_message 等待新消息;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
786
810
  }
787
811
  return trimmed;
788
812
  }
@@ -5,6 +5,7 @@ let lastCodexOptions;
5
5
  let lastThreadOptions;
6
6
  let runMessages = [];
7
7
  let nextThreadPendingRun = null;
8
+ const POLL_PROMPT = '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理;如果 start_task 失败且提示任务已被其他实例领取,请不要停下,继续调用 poll_message 等待新消息;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
8
9
  class MockThread {
9
10
  constructor() {
10
11
  this.id = null;
@@ -202,10 +203,35 @@ describe('CodexSdkAdapter', () => {
202
203
  idleInputCommand: 'system: 请保持工作循环:持续调用 poll_message 阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。',
203
204
  nonInputCommand: '',
204
205
  });
205
- expect(runMessages).toEqual(['系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。']);
206
+ await vi.advanceTimersByTimeAsync(1499);
207
+ expect(runMessages).toEqual([]);
208
+ await vi.advanceTimersByTimeAsync(1);
209
+ expect(runMessages).toEqual([POLL_PROMPT]);
210
+ expect(adapter.getSessionStatus('session-1')).toBe('waiting_input');
206
211
  const userMessages = adapter.getConversation('session-1').filter((message) => message.role === 'user');
207
212
  expect(userMessages).toHaveLength(0);
208
213
  });
214
+ it('does not start duplicate poll turns while a Codex turn is already running', async () => {
215
+ const adapter = new CodexSdkAdapter();
216
+ let releaseRun;
217
+ nextThreadPendingRun = new Promise((resolve) => {
218
+ releaseRun = resolve;
219
+ });
220
+ await adapter.startSession('session-1', {
221
+ command: 'codex',
222
+ workingDirectory: process.cwd(),
223
+ autoAccept: true,
224
+ });
225
+ const sendPromise = adapter.sendMessage('session-1', 'long task');
226
+ adapter.setIdleCommands('session-1', {
227
+ idleInputCommand: 'poll_message',
228
+ nonInputCommand: '',
229
+ });
230
+ await new Promise((resolve) => queueMicrotask(resolve));
231
+ expect(runMessages).toEqual(['long task']);
232
+ releaseRun?.();
233
+ await sendPromise;
234
+ });
209
235
  it('maps codex command flags into SDK thread options instead of codexPathOverride', async () => {
210
236
  const adapter = new CodexSdkAdapter();
211
237
  await adapter.startSession('session-1', {
@@ -18,7 +18,9 @@ export declare class OpencodeSdkAdapter extends EventEmitter implements BaseProv
18
18
  private sessions;
19
19
  private sdkModule?;
20
20
  startSession(sessionId: string, config: AdapterSessionConfig): Promise<void>;
21
- sendMessage(sessionId: string, message: string): Promise<void>;
21
+ sendMessage(sessionId: string, message: string, options?: {
22
+ keepWaitingStatus?: boolean;
23
+ }): Promise<void>;
22
24
  sendConfirmation(sessionId: string, accept: boolean): Promise<void>;
23
25
  sendQuestionAnswer(_sessionId: string, _answers: Record<string, string>): Promise<void>;
24
26
  setIdleCommands(sessionId: string, commands: {
@@ -1 +1 @@
1
- {"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,OAAO,KAAK,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EACd,MAAM,YAAY,CAAC;AA2GpB,qBAAa,kBAAmB,SAAQ,YAAa,YAAW,mBAAmB;IACjF,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,WAAW,cAAc;IAElC,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,SAAS,CAAC,CAAoB;IAEhC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+I5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9D,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAUnG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpD,OAAO,IAAI,IAAI;IAaf;;;OAGG;YACW,OAAO;YA2BP,aAAa;IAmB3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAahC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,YAAY;YAYN,UAAU;IAuBxB,OAAO,CAAC,mBAAmB;IA6H3B,OAAO,CAAC,gBAAgB;IA6GxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAiB1B,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
1
+ {"version":3,"file":"OpencodeSdkAdapter.d.ts","sourceRoot":"","sources":["../../src/adapter/OpencodeSdkAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiB3C,OAAO,KAAK,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EAEnB,aAAa,EACd,MAAM,YAAY,CAAC;AA4GpB,qBAAa,kBAAmB,SAAQ,YAAa,YAAW,mBAAmB;IACjF,QAAQ,CAAC,UAAU,cAAc;IACjC,QAAQ,CAAC,WAAW,cAAc;IAElC,OAAO,CAAC,QAAQ,CAA2C;IAC3D,OAAO,CAAC,SAAS,CAAC,CAAoB;IAEhC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+I5E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA0CzG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBnE,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAUnG,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAclD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BxD,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAIzD,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI3D,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI9D,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAOpD,OAAO,IAAI,IAAI;IAaf;;;OAGG;YACW,OAAO;YA2BP,aAAa;IAmB3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAahC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,YAAY;YAYN,UAAU;IAuBxB,OAAO,CAAC,mBAAmB;IA6H3B,OAAO,CAAC,gBAAgB;IA6GxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,2BAA2B;CAOpC"}
@@ -18,6 +18,7 @@ import * as os from 'node:os';
18
18
  import * as path from 'node:path';
19
19
  import { v4 as uuidv4 } from 'uuid';
20
20
  import { BRIDGE_PACKAGE_ROOT, importBridgeSdkPackage } from '../utils/sdk-package-loader.js';
21
+ import { IdlePollLoop } from './idle-poll-loop.js';
21
22
  import { getToolActionInfo } from './types.js';
22
23
  // ============ 可执行文件查找 ============
23
24
  /**
@@ -213,7 +214,7 @@ export class OpencodeSdkAdapter extends EventEmitter {
213
214
  throw err;
214
215
  }
215
216
  }
216
- async sendMessage(sessionId, message) {
217
+ async sendMessage(sessionId, message, options) {
217
218
  const session = this.sessions.get(sessionId);
218
219
  if (!session)
219
220
  throw new Error(`Session ${sessionId} not found`);
@@ -229,9 +230,11 @@ export class OpencodeSdkAdapter extends EventEmitter {
229
230
  };
230
231
  session.adapterSession.messages.push(userMsg);
231
232
  this.emit('conversation-message', sessionId, userMsg);
232
- session.adapterSession.status = 'thinking';
233
- this.emit('status-change', sessionId, 'thinking');
234
- this.stopIdleDetection(sessionId);
233
+ if (!options?.keepWaitingStatus) {
234
+ session.adapterSession.status = 'thinking';
235
+ this.emit('status-change', sessionId, 'thinking');
236
+ this.stopIdleDetection(sessionId);
237
+ }
235
238
  try {
236
239
  await session.client.session.prompt({
237
240
  path: { id: session.opencodeSessionId },
@@ -688,9 +691,27 @@ export class OpencodeSdkAdapter extends EventEmitter {
688
691
  if (!session || !idleInputCommand)
689
692
  return;
690
693
  if (this.isMessagePollingIdleCommand(idleInputCommand)) {
691
- void this.sendMessage(sessionId, this.toIdlePrompt(idleInputCommand));
694
+ if (!session.idlePollLoop) {
695
+ session.idlePollLoop = new IdlePollLoop({
696
+ getStatus: () => this.sessions.get(sessionId)?.adapterSession.status,
697
+ getCommand: () => this.sessions.get(sessionId)?.idleCommands?.idleInputCommand,
698
+ isPollingCommand: (command) => this.isMessagePollingIdleCommand(command),
699
+ dispatchPollingPrompt: (command) => this.sendMessage(sessionId, this.toIdlePrompt(command), { keepWaitingStatus: true }),
700
+ dispatchRegularIdleCommand: (command) => this.sendMessage(sessionId, this.toIdlePrompt(command)),
701
+ });
702
+ }
703
+ session.idlePollLoop.start();
692
704
  return;
693
705
  }
706
+ if (!session.idlePollLoop) {
707
+ session.idlePollLoop = new IdlePollLoop({
708
+ getStatus: () => this.sessions.get(sessionId)?.adapterSession.status,
709
+ getCommand: () => this.sessions.get(sessionId)?.idleCommands?.idleInputCommand,
710
+ isPollingCommand: (command) => this.isMessagePollingIdleCommand(command),
711
+ dispatchPollingPrompt: (command) => this.sendMessage(sessionId, this.toIdlePrompt(command), { keepWaitingStatus: true }),
712
+ dispatchRegularIdleCommand: (command) => this.sendMessage(sessionId, this.toIdlePrompt(command)),
713
+ });
714
+ }
694
715
  const timer = setInterval(() => {
695
716
  this.checkIdleState(sessionId);
696
717
  }, 500);
@@ -698,6 +719,7 @@ export class OpencodeSdkAdapter extends EventEmitter {
698
719
  }
699
720
  stopIdleDetection(sessionId) {
700
721
  const session = this.sessions.get(sessionId);
722
+ session?.idlePollLoop?.stop();
701
723
  if (session?.idleTimer) {
702
724
  clearInterval(session.idleTimer);
703
725
  session.idleTimer = undefined;
@@ -715,7 +737,7 @@ export class OpencodeSdkAdapter extends EventEmitter {
715
737
  toIdlePrompt(command) {
716
738
  const trimmed = command.trim();
717
739
  if (this.isMessagePollingIdleCommand(trimmed)) {
718
- return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。';
740
+ return '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理;如果 start_task 失败且提示任务已被其他实例领取,请不要停下,继续调用 poll_message 等待新消息;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
719
741
  }
720
742
  return trimmed;
721
743
  }
@@ -1,5 +1,6 @@
1
1
  import { afterEach, describe, expect, it, vi } from 'vitest';
2
2
  import { OpencodeSdkAdapter } from './OpencodeSdkAdapter.js';
3
+ const POLL_PROMPT = '系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理;如果 start_task 失败且提示任务已被其他实例领取,请不要停下,继续调用 poll_message 等待新消息;完成任务后调用 submit_task_result 提交结果,然后继续 poll_message。';
3
4
  afterEach(() => {
4
5
  vi.useRealTimers();
5
6
  vi.restoreAllMocks();
@@ -18,7 +19,7 @@ describe('OpencodeSdkAdapter', () => {
18
19
  it('normalizes poll_message-style idle commands into a blocking poll prompt', () => {
19
20
  const adapter = new OpencodeSdkAdapter();
20
21
  const prompt = Reflect.get(adapter, 'toIdlePrompt').call(adapter, 'system: 请保持工作循环:持续调用 poll_message 阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。');
21
- expect(prompt).toBe('系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。');
22
+ expect(prompt).toBe(POLL_PROMPT);
22
23
  });
23
24
  it('starts idle detection when commands are set after OpenCode is already waiting for input', async () => {
24
25
  vi.useFakeTimers();
@@ -57,8 +58,11 @@ describe('OpencodeSdkAdapter', () => {
57
58
  idleInputCommand: 'system: 使用poll_message阻塞获取消息',
58
59
  nonInputCommand: '',
59
60
  });
60
- await vi.advanceTimersByTimeAsync(500);
61
- expect(sentMessages).toEqual(['系统提醒:当前工作已完成或运行时已进入空闲状态。请立即调用 poll_message 工具阻塞等待新消息和流程线任务提醒;收到流程任务提醒后调用 my_task 查看详情,按需调用 start_task 开始处理,完成后调用 submit_task_result 提交结果,然后继续 poll_message。']);
61
+ await vi.advanceTimersByTimeAsync(1499);
62
+ expect(sentMessages).toEqual([]);
63
+ await vi.advanceTimersByTimeAsync(1);
64
+ expect(sentMessages).toEqual([POLL_PROMPT]);
65
+ expect(adapter.getSessionStatus('session-1')).toBe('waiting_input');
62
66
  });
63
67
  it('re-arms polling idle detection after OpenCode returns to waiting for input', async () => {
64
68
  vi.useFakeTimers();
@@ -94,13 +98,51 @@ describe('OpencodeSdkAdapter', () => {
94
98
  idleInputCommand: 'system: 使用poll_message阻塞获取消息',
95
99
  nonInputCommand: '',
96
100
  });
97
- await vi.advanceTimersByTimeAsync(500);
101
+ await vi.advanceTimersByTimeAsync(1500);
98
102
  expect(sentMessages).toHaveLength(1);
99
103
  session.adapterSession.status = 'waiting_input';
100
104
  Reflect.get(adapter, 'startIdleDetection').call(adapter, 'session-1');
101
- await vi.advanceTimersByTimeAsync(500);
105
+ await vi.advanceTimersByTimeAsync(1500);
102
106
  expect(sentMessages).toHaveLength(2);
103
107
  });
108
+ it('does not send polling prompts while OpenCode is busy', () => {
109
+ const adapter = new OpencodeSdkAdapter();
110
+ const sentMessages = [];
111
+ Reflect.set(adapter, 'sessions', new Map([
112
+ [
113
+ 'session-1',
114
+ {
115
+ adapterSession: {
116
+ sessionId: 'session-1',
117
+ status: 'thinking',
118
+ messages: [],
119
+ createdAt: new Date().toISOString(),
120
+ totalUsage: { inputTokens: 0, outputTokens: 0 },
121
+ },
122
+ config: {
123
+ command: 'opencode',
124
+ workingDirectory: process.cwd(),
125
+ autoAccept: true,
126
+ },
127
+ sseActive: false,
128
+ pendingPermissions: new Map(),
129
+ emittedToolStarts: new Set(),
130
+ workingDirectory: process.cwd(),
131
+ userMessageIds: new Set(),
132
+ currentAssistantText: '',
133
+ seenTextPartIds: new Set(),
134
+ },
135
+ ],
136
+ ]));
137
+ vi.spyOn(adapter, 'sendMessage').mockImplementation(async (_sessionId, message) => {
138
+ sentMessages.push(message);
139
+ });
140
+ adapter.setIdleCommands('session-1', {
141
+ idleInputCommand: 'system: 使用poll_message阻塞获取消息',
142
+ nonInputCommand: '',
143
+ });
144
+ expect(sentMessages).toEqual([]);
145
+ });
104
146
  it('includes serve startup output when the OpenCode process exits early', () => {
105
147
  const adapter = new OpencodeSdkAdapter();
106
148
  const message = Reflect.get(adapter, 'formatStartupExitMessage').call(adapter, { code: 1, signal: null }, ['missing auth configuration']);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * SDK 空闲轮询驱动器。
3
+ * 主流程:进入 waiting_input -> 单实例调度 poll 提示 -> 交给 adapter 启动模型 turn -> turn 完成后由 adapter 再次启动本驱动器。
4
+ */
5
+ export interface IdlePollLoopOptions {
6
+ readonly getStatus: () => 'starting' | 'thinking' | 'tool_using' | 'waiting_input' | 'completed' | 'terminated' | 'error' | undefined;
7
+ readonly getCommand: () => string | undefined;
8
+ readonly isPollingCommand: (command: string) => boolean;
9
+ readonly dispatchPollingPrompt: (command: string) => void | Promise<void>;
10
+ readonly dispatchRegularIdleCommand: (command: string) => void | Promise<void>;
11
+ readonly delayMs?: number;
12
+ readonly regularIdleDelayMs?: number;
13
+ }
14
+ /**
15
+ * 管理单个 SDK 会话的空闲自动命令。
16
+ * 具体逻辑:poll_message 类命令由 runtime/adapter 持续重臂;普通命令保留原有延迟触发语义。
17
+ */
18
+ export declare class IdlePollLoop {
19
+ private readonly options;
20
+ private timer;
21
+ private running;
22
+ private readonly delayMs;
23
+ private readonly regularIdleDelayMs;
24
+ constructor(options: IdlePollLoopOptions);
25
+ /**
26
+ * 启动或重臂空闲驱动。
27
+ * 主流程:清理旧定时器 -> 校验会话仍在 waiting_input -> 根据命令类型选择 poll 驱动或普通 idle 命令。
28
+ */
29
+ start(): void;
30
+ /**
31
+ * 停止当前空闲驱动。
32
+ * 具体逻辑:只取消尚未触发的调度;已经进入模型 turn 的任务由 adapter 生命周期负责。
33
+ */
34
+ stop(): void;
35
+ private fire;
36
+ }
37
+ //# sourceMappingURL=idle-poll-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idle-poll-loop.d.ts","sourceRoot":"","sources":["../../src/adapter/idle-poll-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,UAAU,GAAG,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,GAAG,SAAS,CAAC;IACtI,QAAQ,CAAC,UAAU,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IACxD,QAAQ,CAAC,qBAAqB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,QAAQ,CAAC,0BAA0B,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAID;;;GAGG;AACH,qBAAa,YAAY;IAMX,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;gBAEf,OAAO,EAAE,mBAAmB;IAKzD;;;OAGG;IACH,KAAK,IAAI,IAAI;IAmBb;;;OAGG;IACH,IAAI,IAAI,IAAI;YAOE,IAAI;CAgBnB"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 管理单个 SDK 会话的空闲自动命令。
3
+ * 具体逻辑:poll_message 类命令由 runtime/adapter 持续重臂;普通命令保留原有延迟触发语义。
4
+ */
5
+ export class IdlePollLoop {
6
+ constructor(options) {
7
+ this.options = options;
8
+ this.running = false;
9
+ this.delayMs = options.delayMs ?? 1500;
10
+ this.regularIdleDelayMs = options.regularIdleDelayMs ?? 1500;
11
+ }
12
+ /**
13
+ * 启动或重臂空闲驱动。
14
+ * 主流程:清理旧定时器 -> 校验会话仍在 waiting_input -> 根据命令类型选择 poll 驱动或普通 idle 命令。
15
+ */
16
+ start() {
17
+ this.stop();
18
+ const command = this.options.getCommand()?.trim();
19
+ if (!command || this.options.getStatus() !== 'waiting_input') {
20
+ return;
21
+ }
22
+ const delay = this.options.isPollingCommand(command) ? this.delayMs : this.regularIdleDelayMs;
23
+ if (delay <= 0) {
24
+ void this.fire(command);
25
+ return;
26
+ }
27
+ this.timer = setTimeout(() => {
28
+ this.timer = undefined;
29
+ void this.fire(command);
30
+ }, delay);
31
+ }
32
+ /**
33
+ * 停止当前空闲驱动。
34
+ * 具体逻辑:只取消尚未触发的调度;已经进入模型 turn 的任务由 adapter 生命周期负责。
35
+ */
36
+ stop() {
37
+ if (this.timer) {
38
+ clearTimeout(this.timer);
39
+ this.timer = undefined;
40
+ }
41
+ }
42
+ async fire(command) {
43
+ if (this.running || this.options.getStatus() !== 'waiting_input') {
44
+ return;
45
+ }
46
+ this.running = true;
47
+ try {
48
+ if (this.options.isPollingCommand(command)) {
49
+ await this.options.dispatchPollingPrompt(command);
50
+ }
51
+ else {
52
+ await this.options.dispatchRegularIdleCommand(command);
53
+ }
54
+ }
55
+ finally {
56
+ this.running = false;
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=idle-poll-loop.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idle-poll-loop.test.d.ts","sourceRoot":"","sources":["../../src/adapter/idle-poll-loop.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,71 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { IdlePollLoop } from './idle-poll-loop.js';
3
+ afterEach(() => {
4
+ vi.useRealTimers();
5
+ });
6
+ describe('IdlePollLoop', () => {
7
+ it('dispatches poll commands after the startup-safe delay when the session is waiting for input', async () => {
8
+ vi.useFakeTimers();
9
+ const dispatchPollingPrompt = vi.fn();
10
+ const loop = new IdlePollLoop({
11
+ getStatus: () => 'waiting_input',
12
+ getCommand: () => 'poll_message',
13
+ isPollingCommand: (command) => command.includes('poll_message'),
14
+ dispatchPollingPrompt,
15
+ dispatchRegularIdleCommand: vi.fn(),
16
+ });
17
+ loop.start();
18
+ await vi.advanceTimersByTimeAsync(1499);
19
+ expect(dispatchPollingPrompt).not.toHaveBeenCalled();
20
+ await vi.advanceTimersByTimeAsync(1);
21
+ expect(dispatchPollingPrompt).toHaveBeenCalledTimes(1);
22
+ expect(dispatchPollingPrompt).toHaveBeenCalledWith('poll_message');
23
+ });
24
+ it('does not dispatch when the session is busy', () => {
25
+ const dispatchPollingPrompt = vi.fn();
26
+ const loop = new IdlePollLoop({
27
+ getStatus: () => 'thinking',
28
+ getCommand: () => 'poll_message',
29
+ isPollingCommand: (command) => command.includes('poll_message'),
30
+ dispatchPollingPrompt,
31
+ dispatchRegularIdleCommand: vi.fn(),
32
+ });
33
+ loop.start();
34
+ expect(dispatchPollingPrompt).not.toHaveBeenCalled();
35
+ });
36
+ it('keeps only one delayed regular idle command scheduled', async () => {
37
+ vi.useFakeTimers();
38
+ const dispatchRegularIdleCommand = vi.fn();
39
+ const loop = new IdlePollLoop({
40
+ getStatus: () => 'waiting_input',
41
+ getCommand: () => 'continue',
42
+ isPollingCommand: () => false,
43
+ dispatchPollingPrompt: vi.fn(),
44
+ dispatchRegularIdleCommand,
45
+ regularIdleDelayMs: 1500,
46
+ });
47
+ loop.start();
48
+ loop.start();
49
+ await vi.advanceTimersByTimeAsync(1499);
50
+ expect(dispatchRegularIdleCommand).not.toHaveBeenCalled();
51
+ await vi.advanceTimersByTimeAsync(1);
52
+ expect(dispatchRegularIdleCommand).toHaveBeenCalledTimes(1);
53
+ expect(dispatchRegularIdleCommand).toHaveBeenCalledWith('continue');
54
+ });
55
+ it('can cancel a scheduled regular idle command', async () => {
56
+ vi.useFakeTimers();
57
+ const dispatchRegularIdleCommand = vi.fn();
58
+ const loop = new IdlePollLoop({
59
+ getStatus: () => 'waiting_input',
60
+ getCommand: () => 'continue',
61
+ isPollingCommand: () => false,
62
+ dispatchPollingPrompt: vi.fn(),
63
+ dispatchRegularIdleCommand,
64
+ regularIdleDelayMs: 1500,
65
+ });
66
+ loop.start();
67
+ loop.stop();
68
+ await vi.advanceTimersByTimeAsync(1500);
69
+ expect(dispatchRegularIdleCommand).not.toHaveBeenCalled();
70
+ });
71
+ });
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * 参考自 SpectrAI 项目的 adapter 设计,用于支持多种 AI Provider
6
6
  */
7
- import { EventEmitter } from 'events';
7
+ import { EventEmitter } from 'node:events';
8
8
  export type SessionStatus = 'starting' | 'thinking' | 'tool_using' | 'waiting_input' | 'completed' | 'terminated' | 'error';
9
9
  export type ProviderEventType = 'text_delta' | 'thinking' | 'tool_use_start' | 'tool_use_end' | 'permission_request' | 'ask_user_question' | 'exit_plan_mode' | 'turn_complete' | 'session_complete' | 'error';
10
10
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapter/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapter/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,MAAM,aAAa,GACrB,UAAU,GACV,UAAU,GACV,YAAY,GACZ,eAAe,GACf,WAAW,GACX,YAAY,GACZ,OAAO,CAAC;AAIZ,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,UAAU,GACV,gBAAgB,GAChB,cAAc,GACd,oBAAoB,GACpB,mBAAmB,GACnB,gBAAgB,GAChB,eAAe,GACf,kBAAkB,GAClB,OAAO,CAAC;AAIZ;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,KAAK,GACL,MAAM,GACN,WAAW,GACX,YAAY,GACZ,WAAW,GACX,MAAM,GACN,QAAQ,GACR,KAAK,GACL,KAAK,GACL,UAAU,GACV,OAAO,CAAC;AAEZ;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,aAAa;IACb,UAAU,EAAE,cAAc,CAAC;IAC3B,iBAAiB;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AA6BD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,QAAQ,CAAC,EAAE,YAAY,GACtB,cAAc,CAuPhB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QACJ,0CAA0C;QAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,0CAA0C;QAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,kEAAkE;QAClE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,2BAA2B;QAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,6BAA6B;QAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,8BAA8B;QAC9B,KAAK,CAAC,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,4BAA4B;QAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iCAAiC;QACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,4BAA4B;QAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,4BAA4B;QAC5B,SAAS,CAAC,EAAE,KAAK,CAAC;YAChB,QAAQ,EAAE,MAAM,CAAC;YACjB,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,OAAO,CAAC,EAAE,KAAK,CAAC;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,WAAW,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC;YACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;SACpB,CAAC,CAAC;QACH,6BAA6B;QAC7B,UAAU,CAAC,EAAE,cAAc,CAAC;QAC5B,+BAA+B;QAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,6BAA6B;QAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,+CAA+C;QAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,2BAA2B;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAID,MAAM,WAAW,oBAAoB;IACnC,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW;IACX,gBAAgB,EAAE,MAAM,CAAC;IACzB,2BAA2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0BAA0B;IAC1B,uBAAuB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC/C,mBAAmB;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,aAAa;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa;IACb,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,iBAAiB;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,iBAAiB;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe;IACf,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;CAClC;AAID,MAAM,WAAW,cAAc;IAC7B,cAAc;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW;IACX,MAAM,EAAE,aAAa,CAAC;IACtB,aAAa;IACb,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,WAAW;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,UAAU,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAE1D,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAID,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAID,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AAID;;;;;;;;;;;;;GAaG;AACH,8BAAsB,mBAAoB,SAAQ,YAAY;IAC5D,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAErC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAEtC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAErF;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEvE;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAE5E;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/F;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAEnH;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE9E;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE3D;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE3D;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,CACrB,SAAS,EAAE,MAAM,EACjB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAEhB;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAElE;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAE/C;;OAEG;IACH,QAAQ,CAAC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAEpE;;OAEG;IACH,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAEvE;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAE9D;;OAEG;IACH,QAAQ,CAAC,OAAO,IAAI,IAAI;CACzB"}
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * 参考自 SpectrAI 项目的 adapter 设计,用于支持多种 AI Provider
6
6
  */
7
- import { EventEmitter } from 'events';
7
+ import { EventEmitter } from 'node:events';
8
8
  /**
9
9
  * 已知的 MCP 工具名映射表
10
10
  * 用于识别 MCP 工具并确定其来源 server
@@ -208,6 +208,9 @@ export function getToolActionInfo(toolName, toolInput, metadata) {
208
208
  const detail = mcpServer && mcpTool ? `${mcpServer}/${mcpTool}` : (mcpTool || toolName);
209
209
  return { actionType: 'mcp', actionLabel: '调用MCP', actionDetail: detail };
210
210
  }
211
+ if (matchesToolName('poll_message', 'get_message')) {
212
+ return { actionType: 'idle', actionLabel: '监听消息', actionDetail: toolName };
213
+ }
211
214
  // 文件编辑 (Kiro: strReplace, editCode)
212
215
  if (matchesToolName('edit', 'editfile', 'strreplace', 'editcode', 'apply_patch', 'multiedit', 'replace')) {
213
216
  const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws-runtime-bridge",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "description": "AgentsWorkStudio runtime bridge service for machine-level agent runtime integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",