botmux 2.83.0 → 2.84.1

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 (211) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +5 -1
  3. package/dist/adapters/backend/sandbox.d.ts +4 -0
  4. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  5. package/dist/adapters/backend/sandbox.js +14 -0
  6. package/dist/adapters/backend/sandbox.js.map +1 -1
  7. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  8. package/dist/adapters/cli/claude-code.js +6 -43
  9. package/dist/adapters/cli/claude-code.js.map +1 -1
  10. package/dist/adapters/cli/mir.d.ts +4 -0
  11. package/dist/adapters/cli/mir.d.ts.map +1 -0
  12. package/dist/adapters/cli/mir.js +81 -0
  13. package/dist/adapters/cli/mir.js.map +1 -0
  14. package/dist/adapters/cli/registry.d.ts +2 -1
  15. package/dist/adapters/cli/registry.d.ts.map +1 -1
  16. package/dist/adapters/cli/registry.js +3 -1
  17. package/dist/adapters/cli/registry.js.map +1 -1
  18. package/dist/adapters/cli/shared-hints.d.ts +17 -0
  19. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  20. package/dist/adapters/cli/shared-hints.js +56 -0
  21. package/dist/adapters/cli/shared-hints.js.map +1 -1
  22. package/dist/adapters/cli/types.d.ts +12 -1
  23. package/dist/adapters/cli/types.d.ts.map +1 -1
  24. package/dist/bot-registry.d.ts +7 -0
  25. package/dist/bot-registry.d.ts.map +1 -1
  26. package/dist/bot-registry.js +27 -0
  27. package/dist/bot-registry.js.map +1 -1
  28. package/dist/cli.d.ts.map +1 -1
  29. package/dist/cli.js +30 -65
  30. package/dist/cli.js.map +1 -1
  31. package/dist/core/command-handler.d.ts.map +1 -1
  32. package/dist/core/command-handler.js +42 -1
  33. package/dist/core/command-handler.js.map +1 -1
  34. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  35. package/dist/core/dashboard-ipc-server.js +58 -1
  36. package/dist/core/dashboard-ipc-server.js.map +1 -1
  37. package/dist/core/passthrough-commands.d.ts.map +1 -1
  38. package/dist/core/passthrough-commands.js +1 -1
  39. package/dist/core/passthrough-commands.js.map +1 -1
  40. package/dist/core/pending-response.d.ts +2 -39
  41. package/dist/core/pending-response.d.ts.map +1 -1
  42. package/dist/core/pending-response.js +5 -99
  43. package/dist/core/pending-response.js.map +1 -1
  44. package/dist/core/session-manager.d.ts.map +1 -1
  45. package/dist/core/session-manager.js +4 -16
  46. package/dist/core/session-manager.js.map +1 -1
  47. package/dist/core/skills/claude-plugin-delivery.d.ts +6 -0
  48. package/dist/core/skills/claude-plugin-delivery.d.ts.map +1 -0
  49. package/dist/core/skills/claude-plugin-delivery.js +21 -0
  50. package/dist/core/skills/claude-plugin-delivery.js.map +1 -0
  51. package/dist/core/skills/cli-admin-command.d.ts +7 -0
  52. package/dist/core/skills/cli-admin-command.d.ts.map +1 -0
  53. package/dist/core/skills/cli-admin-command.js +243 -0
  54. package/dist/core/skills/cli-admin-command.js.map +1 -0
  55. package/dist/core/skills/cli-session-command.d.ts +7 -0
  56. package/dist/core/skills/cli-session-command.d.ts.map +1 -0
  57. package/dist/core/skills/cli-session-command.js +45 -0
  58. package/dist/core/skills/cli-session-command.js.map +1 -0
  59. package/dist/core/skills/delivery.d.ts +11 -0
  60. package/dist/core/skills/delivery.d.ts.map +1 -0
  61. package/dist/core/skills/delivery.js +22 -0
  62. package/dist/core/skills/delivery.js.map +1 -0
  63. package/dist/core/skills/discovery.d.ts +3 -0
  64. package/dist/core/skills/discovery.d.ts.map +1 -0
  65. package/dist/core/skills/discovery.js +34 -0
  66. package/dist/core/skills/discovery.js.map +1 -0
  67. package/dist/core/skills/frontmatter.d.ts +9 -0
  68. package/dist/core/skills/frontmatter.d.ts.map +1 -0
  69. package/dist/core/skills/frontmatter.js +42 -0
  70. package/dist/core/skills/frontmatter.js.map +1 -0
  71. package/dist/core/skills/im-command.d.ts +9 -0
  72. package/dist/core/skills/im-command.d.ts.map +1 -0
  73. package/dist/core/skills/im-command.js +107 -0
  74. package/dist/core/skills/im-command.js.map +1 -0
  75. package/dist/core/skills/manifest-store.d.ts +4 -0
  76. package/dist/core/skills/manifest-store.d.ts.map +1 -0
  77. package/dist/core/skills/manifest-store.js +26 -0
  78. package/dist/core/skills/manifest-store.js.map +1 -0
  79. package/dist/core/skills/package.d.ts +13 -0
  80. package/dist/core/skills/package.d.ts.map +1 -0
  81. package/dist/core/skills/package.js +35 -0
  82. package/dist/core/skills/package.js.map +1 -0
  83. package/dist/core/skills/policy.d.ts +18 -0
  84. package/dist/core/skills/policy.d.ts.map +1 -0
  85. package/dist/core/skills/policy.js +69 -0
  86. package/dist/core/skills/policy.js.map +1 -0
  87. package/dist/core/skills/prompt.d.ts +3 -0
  88. package/dist/core/skills/prompt.d.ts.map +1 -0
  89. package/dist/core/skills/prompt.js +25 -0
  90. package/dist/core/skills/prompt.js.map +1 -0
  91. package/dist/core/skills/references.d.ts +21 -0
  92. package/dist/core/skills/references.d.ts.map +1 -0
  93. package/dist/core/skills/references.js +27 -0
  94. package/dist/core/skills/references.js.map +1 -0
  95. package/dist/core/skills/registry-paths.d.ts +5 -0
  96. package/dist/core/skills/registry-paths.d.ts.map +1 -0
  97. package/dist/core/skills/registry-paths.js +15 -0
  98. package/dist/core/skills/registry-paths.js.map +1 -0
  99. package/dist/core/skills/resource-reader.d.ts +9 -0
  100. package/dist/core/skills/resource-reader.d.ts.map +1 -0
  101. package/dist/core/skills/resource-reader.js +97 -0
  102. package/dist/core/skills/resource-reader.js.map +1 -0
  103. package/dist/core/skills/session-resolver.d.ts +14 -0
  104. package/dist/core/skills/session-resolver.d.ts.map +1 -0
  105. package/dist/core/skills/session-resolver.js +24 -0
  106. package/dist/core/skills/session-resolver.js.map +1 -0
  107. package/dist/core/skills/session-runtime.d.ts +14 -0
  108. package/dist/core/skills/session-runtime.d.ts.map +1 -0
  109. package/dist/core/skills/session-runtime.js +32 -0
  110. package/dist/core/skills/session-runtime.js.map +1 -0
  111. package/dist/core/skills/sources.d.ts +21 -0
  112. package/dist/core/skills/sources.d.ts.map +1 -0
  113. package/dist/core/skills/sources.js +155 -0
  114. package/dist/core/skills/sources.js.map +1 -0
  115. package/dist/core/skills/types.d.ts +71 -0
  116. package/dist/core/skills/types.d.ts.map +1 -0
  117. package/dist/core/skills/types.js +2 -0
  118. package/dist/core/skills/types.js.map +1 -0
  119. package/dist/core/types.d.ts +10 -3
  120. package/dist/core/types.d.ts.map +1 -1
  121. package/dist/core/types.js.map +1 -1
  122. package/dist/core/worker-pool.d.ts +14 -1
  123. package/dist/core/worker-pool.d.ts.map +1 -1
  124. package/dist/core/worker-pool.js +105 -69
  125. package/dist/core/worker-pool.js.map +1 -1
  126. package/dist/daemon.d.ts +2 -2
  127. package/dist/daemon.d.ts.map +1 -1
  128. package/dist/daemon.js +49 -52
  129. package/dist/daemon.js.map +1 -1
  130. package/dist/dashboard/skill-install-request.d.ts +21 -0
  131. package/dist/dashboard/skill-install-request.d.ts.map +1 -0
  132. package/dist/dashboard/skill-install-request.js +62 -0
  133. package/dist/dashboard/skill-install-request.js.map +1 -0
  134. package/dist/dashboard/web/app.d.ts.map +1 -1
  135. package/dist/dashboard/web/app.js +4 -1
  136. package/dist/dashboard/web/app.js.map +1 -1
  137. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  138. package/dist/dashboard/web/i18n.js +138 -0
  139. package/dist/dashboard/web/i18n.js.map +1 -1
  140. package/dist/dashboard/web/skills.d.ts +2 -0
  141. package/dist/dashboard/web/skills.d.ts.map +1 -0
  142. package/dist/dashboard/web/skills.js +539 -0
  143. package/dist/dashboard/web/skills.js.map +1 -0
  144. package/dist/dashboard/web/workflows.js +1 -1
  145. package/dist/dashboard/web/workflows.js.map +1 -1
  146. package/dist/dashboard-web/app.js +594 -451
  147. package/dist/dashboard-web/index.html +1 -0
  148. package/dist/dashboard-web/style.css +793 -0
  149. package/dist/dashboard.js +231 -0
  150. package/dist/dashboard.js.map +1 -1
  151. package/dist/global-config.d.ts +7 -0
  152. package/dist/global-config.d.ts.map +1 -1
  153. package/dist/global-config.js +16 -0
  154. package/dist/global-config.js.map +1 -1
  155. package/dist/i18n/en.d.ts.map +1 -1
  156. package/dist/i18n/en.js +2 -5
  157. package/dist/i18n/en.js.map +1 -1
  158. package/dist/i18n/zh.d.ts.map +1 -1
  159. package/dist/i18n/zh.js +2 -5
  160. package/dist/i18n/zh.js.map +1 -1
  161. package/dist/im/lark/card-builder.d.ts +0 -3
  162. package/dist/im/lark/card-builder.d.ts.map +1 -1
  163. package/dist/im/lark/card-builder.js +1 -33
  164. package/dist/im/lark/card-builder.js.map +1 -1
  165. package/dist/mir-local-runtime.d.ts +20 -0
  166. package/dist/mir-local-runtime.d.ts.map +1 -0
  167. package/dist/mir-local-runtime.js +168 -0
  168. package/dist/mir-local-runtime.js.map +1 -0
  169. package/dist/mir-runner.d.ts +3 -0
  170. package/dist/mir-runner.d.ts.map +1 -0
  171. package/dist/mir-runner.js +482 -0
  172. package/dist/mir-runner.js.map +1 -0
  173. package/dist/services/bot-config-store.d.ts +4 -4
  174. package/dist/services/bot-config-store.d.ts.map +1 -1
  175. package/dist/services/bot-config-store.js +24 -1
  176. package/dist/services/bot-config-store.js.map +1 -1
  177. package/dist/services/session-store.d.ts +1 -0
  178. package/dist/services/session-store.d.ts.map +1 -1
  179. package/dist/services/session-store.js +12 -5
  180. package/dist/services/session-store.js.map +1 -1
  181. package/dist/services/skill-registry-store.d.ts +42 -0
  182. package/dist/services/skill-registry-store.d.ts.map +1 -0
  183. package/dist/services/skill-registry-store.js +343 -0
  184. package/dist/services/skill-registry-store.js.map +1 -0
  185. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  186. package/dist/setup/bot-config-editor.js +2 -0
  187. package/dist/setup/bot-config-editor.js.map +1 -1
  188. package/dist/setup/cli-selection.d.ts.map +1 -1
  189. package/dist/setup/cli-selection.js +74 -6
  190. package/dist/setup/cli-selection.js.map +1 -1
  191. package/dist/skills/installer.d.ts.map +1 -1
  192. package/dist/skills/installer.js +3 -0
  193. package/dist/skills/installer.js.map +1 -1
  194. package/dist/types.d.ts +2 -5
  195. package/dist/types.d.ts.map +1 -1
  196. package/dist/utils/file-lock.d.ts +1 -0
  197. package/dist/utils/file-lock.d.ts.map +1 -1
  198. package/dist/utils/file-lock.js +87 -1
  199. package/dist/utils/file-lock.js.map +1 -1
  200. package/dist/worker.js +5 -3
  201. package/dist/worker.js.map +1 -1
  202. package/dist/workflows/attempt-resume.d.ts.map +1 -1
  203. package/dist/workflows/attempt-resume.js +1 -1
  204. package/dist/workflows/attempt-resume.js.map +1 -1
  205. package/dist/workflows/definition.d.ts +16 -16
  206. package/dist/workflows/events/schema.d.ts +280 -280
  207. package/package.json +1 -1
  208. package/dist/services/pending-response-transaction-store.d.ts +0 -12
  209. package/dist/services/pending-response-transaction-store.d.ts.map +0 -1
  210. package/dist/services/pending-response-transaction-store.js +0 -52
  211. package/dist/services/pending-response-transaction-store.js.map +0 -1
package/dist/daemon.js CHANGED
@@ -13,7 +13,7 @@ import { botmuxWrapperFiles } from './core/botmux-wrapper.js';
13
13
  import { startMaintenance, stopMaintenance } from './core/maintenance.js';
14
14
  import { sendRestartReportIfPending } from './core/restart-report.js';
15
15
  import { statSync } from 'node:fs';
16
- import { getChatMode, listChatMemberOpenIds, replyMessage, resolveAllowedUsersWithMap, sendMessage, sendUserMessage, updateMessage } from './im/lark/client.js';
16
+ import { addReaction, getChatMode, listChatMemberOpenIds, replyMessage, resolveAllowedUsersWithMap, sendMessage, sendUserMessage, updateMessage } from './im/lark/client.js';
17
17
  import { resolveGroupJoinPrompt, waitForAllowedUserInChat } from './core/auto-start.js';
18
18
  import { loadBotConfigs, registerBot, getBot, getAllBots, findOncallChatForAnyBot } from './bot-registry.js';
19
19
  import * as sessionStore from './services/session-store.js';
@@ -36,9 +36,8 @@ import { buildTerminalUrl, setTerminalProxyPort, setTerminalExternalPort } from
36
36
  import { startTerminalProxy } from './core/terminal-proxy.js';
37
37
  import * as scheduler from './core/scheduler.js';
38
38
  import { scanMultipleProjects } from './services/project-scanner.js';
39
- import { buildPendingResponseCard, buildQuotaExhaustedCard, buildRepoSelectCard, buildStreamingCard, getCliDisplayName } from './im/lark/card-builder.js';
40
- import { createPendingResponseQueue, markPendingResponseCardPatched, shouldTreatPendingCardAsPatchedByMarker, startPendingResponseTurn, syncPendingResponseState } from './core/pending-response.js';
41
- import { readPendingResponsePatchMarker } from './services/pending-response-transaction-store.js';
39
+ import { buildQuotaExhaustedCard, buildRepoSelectCard, buildStreamingCard, getCliDisplayName } from './im/lark/card-builder.js';
40
+ import { RECEIVED_REACTION_EMOJI_TYPE } from './core/pending-response.js';
42
41
  import { t as tr, botLocale, localeForBot } from './i18n/index.js';
43
42
  import { createCliAdapterSync } from './adapters/cli/registry.js';
44
43
  import { initWorkerPool, setActiveSessionsRegistry, forkWorker, killWorker, reapOrphanWorkers, scheduleCardPatch, setCurrentCliVersion, CARD_POSTING_SENTINEL, parkStreamCard, closeSession as closeSessionHelper, ensureCliEnv, writableTerminalLinkFor, } from './core/worker-pool.js';
@@ -226,7 +225,6 @@ function startMemoryDiagnostics() {
226
225
  * Lark message ids start with `om_` and chat ids with `oc_`, so the two
227
226
  * address spaces never collide; the lookup just tries both.
228
227
  */
229
- const pendingResponseQueue = createPendingResponseQueue();
230
228
  function streamingCardDisabledFor(ds) {
231
229
  if (ds.streamingCardForced)
232
230
  return false;
@@ -256,43 +254,44 @@ function readSessionFreshFromDisk(sessionId, larkAppId) {
256
254
  }
257
255
  return undefined;
258
256
  }
259
- export async function postPendingResponseCard(ds, replyToMessageId, prompt, sender, turnId) {
260
- // Card-off path (streaming card disabled for this session): post a lightweight
261
- // 「处理中」 placeholder. The final reply / `botmux send` patches this card in
262
- // place and that patch is also what lets the 完成 emoji land on the user's
263
- // original message. When streaming cards are ON the streaming card itself is
264
- // the placeholder, so this is a no-op for those sessions.
257
+ export async function noteTurnReceived(ds, triggerMessageId, _prompt, _sender, _turnId) {
258
+ // Replaces the old 「处理中」 placeholder card. That card existed only to be
259
+ // PATCHed with the final answer, and `im.v1.message.patch` is silent (no Feishu
260
+ // notification / unread) so card-off answers could land unseen. The
261
+ // placeholder + patch-delivery was removed; answers now always go out as a
262
+ // fresh message (deliverFinalOutput / `botmux send`).
263
+ //
264
+ // This call site is the per-message acceptance point, so it also drives the
265
+ // two-phase turn reaction. It's auto-enabled exactly for card-off sessions
266
+ // (streaming card disabled): those have no live status card, so the ✋→✅ on
267
+ // the user's message is the only lightweight progress signal. When the
268
+ // streaming card is on it already shows status, so we stay silent.
269
+ // React 冲! on the triggering message the instant it's accepted. Binding to the
270
+ // message — not a worker status edge — means type-ahead / busy-batched messages
271
+ // each get their own ✋. `finishTurnReactions` flips every pending ✋ to ✅ when
272
+ // the worker next goes idle.
265
273
  if (!streamingCardDisabledFor(ds))
266
274
  return;
267
- await pendingResponseQueue.run(ds.session.sessionId, async () => {
268
- const fresh = readSessionFreshFromDisk(ds.session.sessionId, ds.larkAppId);
269
- syncPendingResponseState(ds, fresh);
270
- if (fresh)
271
- syncReplyTargetState(ds, fresh);
272
- // Reconcile a PATCH that committed at Feishu but whose session save lost the
273
- // race, so a stale prior card isn't left dangling as "open".
274
- const marker = readPendingResponsePatchMarker(ds.session.sessionId);
275
- if (shouldTreatPendingCardAsPatchedByMarker(ds.pendingResponseCardId, marker)) {
276
- markPendingResponseCardPatched(ds);
277
- }
278
- syncPendingResponseState(ds.session, ds);
279
- const card = buildPendingResponseCard(localeForBot(ds.larkAppId));
280
- try {
281
- // Route the placeholder to the same target the final reply will use: a
282
- // topic-alias turn (chat-scope + matching turnId) lands in the topic
283
- // thread; otherwise it's a plain chat message / thread-scope reply.
284
- const target = resolveSessionReplyTarget(ds, turnId);
285
- const messageId = target.mode === 'thread'
286
- ? await replyMessage(ds.larkAppId, target.rootMessageId, card, 'interactive', true)
287
- : await sendMessage(ds.larkAppId, target.chatId, card, 'interactive');
288
- startPendingResponseTurn(ds, messageId);
289
- startPendingResponseTurn(ds.session, messageId);
290
- sessionStore.updateSession(ds.session);
291
- }
292
- catch (err) {
293
- logger.warn(`[${tag(ds)}] failed to post pending response card: ${err instanceof Error ? err.message : String(err)}`);
294
- }
295
- });
275
+ // Only Lark messages carry reactions — doc-comment ids / chat anchors can't.
276
+ if (!triggerMessageId.startsWith('om_'))
277
+ return;
278
+ if ((ds.pendingAckReactions ??= []).some(a => a.messageId === triggerMessageId))
279
+ return;
280
+ // Add the FIRST, register the entry only after it lands. If we pushed the
281
+ // entry before awaiting addReaction, a previous turn's idle edge
282
+ // (finishTurnReactions) could detach this half-formed entry mid-flight —
283
+ // DONE-ing a message that hasn't even reached the worker yet and orphaning its
284
+ // reactionId. Callers await this before dispatching the message to the worker,
285
+ // so a registered entry is always in place before its own turn can go idle.
286
+ let reactionId;
287
+ try {
288
+ reactionId = await addReaction(ds.larkAppId, triggerMessageId, RECEIVED_REACTION_EMOJI_TYPE);
289
+ }
290
+ catch (err) {
291
+ logger.debug(`[reaction] received add failed for ${triggerMessageId}: ${err instanceof Error ? err.message : String(err)}`);
292
+ return;
293
+ }
294
+ (ds.pendingAckReactions ??= []).push({ messageId: triggerMessageId, reactionId });
296
295
  }
297
296
  async function sessionReply(anchor, content, msgType = 'text', larkAppId, turnId) {
298
297
  let ds;
@@ -2060,7 +2059,7 @@ async function handleNewTopic(data, ctx) {
2060
2059
  const selfBot = getBot(larkAppId);
2061
2060
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, chatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), newTopicSender, { larkAppId, chatId });
2062
2061
  rememberLastCliInput(ds, promptContent, prompt);
2063
- await postPendingResponseCard(ds, messageId, content, newTopicSender, messageId);
2062
+ await noteTurnReceived(ds, messageId, content, newTopicSender, messageId);
2064
2063
  forkWorker(ds, prompt);
2065
2064
  const reason = oncallEntry
2066
2065
  ? `oncall-bound chat ${chatId}`
@@ -2092,7 +2091,7 @@ async function handleNewTopic(data, ctx) {
2092
2091
  const selfBot = getBot(larkAppId);
2093
2092
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, chatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), newTopicSender, { larkAppId, chatId });
2094
2093
  rememberLastCliInput(ds, promptContent, prompt);
2095
- await postPendingResponseCard(ds, messageId, content, newTopicSender, messageId);
2094
+ await noteTurnReceived(ds, messageId, content, newTopicSender, messageId);
2096
2095
  forkWorker(ds, prompt);
2097
2096
  logger.info(`Session ${session.sessionId} ready (no projects to select), total active: ${getActiveCount()}`);
2098
2097
  }
@@ -2260,7 +2259,7 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId) {
2260
2259
  return;
2261
2260
  const prompt = await buildPrompt();
2262
2261
  rememberLastCliInput(ds, promptBody, prompt);
2263
- await postPendingResponseCard(ds, anchor, promptBody);
2262
+ await noteTurnReceived(ds, anchor, promptBody);
2264
2263
  forkWorker(ds, prompt);
2265
2264
  logger.info(`[auto-start:入群] ${chatId.substring(0, 12)} 自动开工(${mode}/${scope}),workingDir=${pinnedWorkingDir}`);
2266
2265
  return;
@@ -2281,7 +2280,7 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId) {
2281
2280
  ds.pendingRepo = false;
2282
2281
  const prompt = await buildPrompt();
2283
2282
  rememberLastCliInput(ds, promptBody, prompt);
2284
- await postPendingResponseCard(ds, anchor, promptBody);
2283
+ await noteTurnReceived(ds, anchor, promptBody);
2285
2284
  forkWorker(ds, prompt);
2286
2285
  logger.info(`[auto-start:入群] ${chatId.substring(0, 12)} 无默认目录且无可选项目,直接开工`);
2287
2286
  }
@@ -2620,8 +2619,6 @@ async function handleThreadReply(data, ctx) {
2620
2619
  // reply cards to whoever triggered this turn — matters in oncall groups
2621
2620
  // where the caller is often not the session owner).
2622
2621
  if (ds) {
2623
- syncPendingResponseState(ds, readSessionFreshFromDisk(ds.session.sessionId, ds.larkAppId));
2624
- syncPendingResponseState(ds.session, ds);
2625
2622
  markSessionActivity(ds);
2626
2623
  const callerOpenId = parsed.senderId || data?.sender?.sender_id?.open_id;
2627
2624
  // quoteTargetId changes every inbound message (always a new message_id), so
@@ -2766,7 +2763,7 @@ async function handleThreadReply(data, ctx) {
2766
2763
  const selfBot = getBot(larkAppId);
2767
2764
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, autoCreateChatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), autoCreateSender, { larkAppId, chatId: autoCreateChatId });
2768
2765
  rememberLastCliInput(newDs, promptContent, prompt);
2769
- await postPendingResponseCard(newDs, parsed.messageId, parsed.content, autoCreateSender, parsed.messageId);
2766
+ await noteTurnReceived(newDs, parsed.messageId, parsed.content, autoCreateSender, parsed.messageId);
2770
2767
  forkWorker(newDs, prompt);
2771
2768
  const reason = oncallEntry
2772
2769
  ? `oncall-bound chat ${autoCreateChatId}`
@@ -2798,7 +2795,7 @@ async function handleThreadReply(data, ctx) {
2798
2795
  const selfBot = getBot(larkAppId);
2799
2796
  const prompt = buildNewTopicPrompt(promptContent, session.sessionId, botCfg.cliId, botCfg.cliPathOverride, attachments, parsed.mentions, await getAvailableBots(larkAppId, autoCreateChatId), undefined, { name: selfBot.botName, openId: selfBot.botOpenId }, localeForBot(larkAppId), autoCreateSender, { larkAppId, chatId: autoCreateChatId });
2800
2797
  rememberLastCliInput(newDs, promptContent, prompt);
2801
- await postPendingResponseCard(newDs, parsed.messageId, parsed.content, autoCreateSender, parsed.messageId);
2798
+ await noteTurnReceived(newDs, parsed.messageId, parsed.content, autoCreateSender, parsed.messageId);
2802
2799
  forkWorker(newDs, prompt);
2803
2800
  }
2804
2801
  return;
@@ -2834,7 +2831,7 @@ async function handleThreadReply(data, ctx) {
2834
2831
  });
2835
2832
  beginNewTurn(ds, parsed.content);
2836
2833
  rememberLastCliInput(ds, promptContent, msgContent);
2837
- await postPendingResponseCard(ds, parsed.messageId, parsed.content, await getThreadSender(), parsed.messageId);
2834
+ await noteTurnReceived(ds, parsed.messageId, parsed.content, await getThreadSender(), parsed.messageId);
2838
2835
  ds.worker.send({ type: 'message', content: msgContent, turnId: parsed.messageId });
2839
2836
  }
2840
2837
  else {
@@ -2886,7 +2883,7 @@ async function handleThreadReply(data, ctx) {
2886
2883
  sender: await getThreadSender(),
2887
2884
  });
2888
2885
  rememberLastCliInput(ds, promptContent, wrappedPrompt);
2889
- await postPendingResponseCard(ds, parsed.messageId, parsed.content, await getThreadSender(), parsed.messageId);
2886
+ await noteTurnReceived(ds, parsed.messageId, parsed.content, await getThreadSender(), parsed.messageId);
2890
2887
  sessionStore.updateSession(ds.session);
2891
2888
  forkWorker(ds, wrappedPrompt, ds.hasHistory);
2892
2889
  }
@@ -2946,7 +2943,7 @@ async function handleDocComment(ctx) {
2946
2943
  ds.session.currentDocCommentTarget = docTarget; // beginNewTurn 刚清空,这里设本轮落点
2947
2944
  rememberLastCliInput(ds, promptContent, msgContent);
2948
2945
  sessionStore.updateSession(ds.session); // 先落盘,botmux send 子进程才读得到落点
2949
- await postPendingResponseCard(ds, commentId, text, sender, turnId);
2946
+ await noteTurnReceived(ds, commentId, text, sender, turnId);
2950
2947
  ds.worker.send({ type: 'message', content: msgContent, turnId });
2951
2948
  logger.info(`[${tag(ds)}] doc-comment turn injected (turn ${turnId.slice(0, 8)})`);
2952
2949
  }
@@ -2973,7 +2970,7 @@ async function handleDocComment(ctx) {
2973
2970
  });
2974
2971
  ds.session.currentDocCommentTarget = docTarget;
2975
2972
  rememberLastCliInput(ds, promptContent, wrappedPrompt);
2976
- await postPendingResponseCard(ds, commentId, text, sender, turnId);
2973
+ await noteTurnReceived(ds, commentId, text, sender, turnId);
2977
2974
  sessionStore.updateSession(ds.session);
2978
2975
  forkWorker(ds, wrappedPrompt, ds.hasHistory);
2979
2976
  }