botmux 2.91.1 → 2.93.0

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 (60) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/bot-registry.d.ts +1 -1
  4. package/dist/bot-registry.d.ts.map +1 -1
  5. package/dist/bot-registry.js +14 -11
  6. package/dist/bot-registry.js.map +1 -1
  7. package/dist/cli/dashboard-endpoint.d.ts +9 -6
  8. package/dist/cli/dashboard-endpoint.d.ts.map +1 -1
  9. package/dist/cli/dashboard-endpoint.js.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +11 -1
  12. package/dist/config.js.map +1 -1
  13. package/dist/core/session-manager.js +2 -2
  14. package/dist/core/session-manager.js.map +1 -1
  15. package/dist/daemon.d.ts +5 -1
  16. package/dist/daemon.d.ts.map +1 -1
  17. package/dist/daemon.js +24 -6
  18. package/dist/daemon.js.map +1 -1
  19. package/dist/dashboard/bot-payload.js +1 -1
  20. package/dist/dashboard/bot-payload.js.map +1 -1
  21. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  22. package/dist/dashboard/web/bot-defaults.js +3 -2
  23. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  24. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  25. package/dist/dashboard/web/i18n.js +6 -4
  26. package/dist/dashboard/web/i18n.js.map +1 -1
  27. package/dist/dashboard-web/app.js +6 -5
  28. package/dist/dashboard.js +82 -23
  29. package/dist/dashboard.js.map +1 -1
  30. package/dist/i18n/en.js +9 -9
  31. package/dist/i18n/en.js.map +1 -1
  32. package/dist/i18n/zh.js +9 -9
  33. package/dist/i18n/zh.js.map +1 -1
  34. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  35. package/dist/im/lark/event-dispatcher.js +12 -7
  36. package/dist/im/lark/event-dispatcher.js.map +1 -1
  37. package/dist/im/lark/relay-target-routing.d.ts.map +1 -1
  38. package/dist/im/lark/relay-target-routing.js +7 -2
  39. package/dist/im/lark/relay-target-routing.js.map +1 -1
  40. package/dist/im/lark/reply-mode-command.d.ts.map +1 -1
  41. package/dist/im/lark/reply-mode-command.js +3 -1
  42. package/dist/im/lark/reply-mode-command.js.map +1 -1
  43. package/dist/platform/tunnel-client.d.ts +10 -0
  44. package/dist/platform/tunnel-client.d.ts.map +1 -1
  45. package/dist/platform/tunnel-client.js +2 -0
  46. package/dist/platform/tunnel-client.js.map +1 -1
  47. package/dist/services/card-prefs-store.d.ts +1 -1
  48. package/dist/services/card-prefs-store.d.ts.map +1 -1
  49. package/dist/services/card-prefs-store.js +5 -5
  50. package/dist/services/card-prefs-store.js.map +1 -1
  51. package/dist/services/chat-reply-mode-store.d.ts +1 -1
  52. package/dist/services/chat-reply-mode-store.d.ts.map +1 -1
  53. package/dist/services/chat-reply-mode-store.js +17 -3
  54. package/dist/services/chat-reply-mode-store.js.map +1 -1
  55. package/dist/skills/definitions.js +1 -1
  56. package/dist/utils/listen-with-probe.d.ts +20 -1
  57. package/dist/utils/listen-with-probe.d.ts.map +1 -1
  58. package/dist/utils/listen-with-probe.js +48 -6
  59. package/dist/utils/listen-with-probe.js.map +1 -1
  60. package/package.json +1 -1
package/dist/daemon.d.ts CHANGED
@@ -6,13 +6,17 @@ import type { WorkflowRuntimeContext } from './workflows/runtime.js';
6
6
  export declare function noteTurnReceived(ds: DaemonSession, triggerMessageId: string, _prompt?: string, _sender?: {
7
7
  name?: string;
8
8
  }, _turnId?: string): Promise<void>;
9
+ declare function sessionReply(anchor: string, content: string, msgType?: string, larkAppId?: string, turnId?: string): Promise<string>;
10
+ export declare const __testOnly_sessionReply: typeof sessionReply;
11
+ export declare const __testOnly_activeSessions: Map<string, DaemonSession>;
9
12
  export declare function enforceMessageQuotaForCliInput(larkAppId: string, chatId: string, senderOpenId: string | undefined, messageId: string, anchor: string): Promise<boolean>;
10
13
  export declare function grantRestrictedCommandText(larkAppId: string, chatId: string | undefined, senderOpenId: string | undefined, cmd: string): string | undefined;
11
14
  export declare function grantRestrictedSlashCommandText(larkAppId: string, chatId: string | undefined, senderOpenId: string | undefined, cmd: string): string | undefined;
12
15
  export declare function attachWorkflowEventWatcher(runId: string, ctx?: WorkflowRuntimeContext): WorkflowEventWatcher;
13
16
  /**
14
17
  * Resolve the pinned working dir for a brand-new topic via the layered lookup:
15
- * 1) an existing oncall binding (this bot or a sibling)
18
+ * 1) this bot's OWN oncall binding (per-bot: another bot's binding never pins
19
+ * this bot — cross-bot dir alignment is handled by layer 3 inherit-peer)
16
20
  * 2) this bot's defaultOncall — auto-binds a brand-new chat when the flag is on
17
21
  * (this WRITES state, so it must run identically on every spawn path)
18
22
  * 3) a sibling session's workingDir (cross-bot / chat-scope inheritance)
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAkBA,OAAO,EAA2F,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAkB7I,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAiFrD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAiB,MAAM,wBAAwB,CAAC;AAqPpF,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmClK;AA0FD,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA6ClB;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAMpB;AAoKD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,sBAAsB,GAAG,oBAAoB,CA0C5G;AAgtCD;;;;;;;;;;GAUG;AACH,iBAAe,uBAAuB,CAAC,GAAG,EAAE;IAC1C,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;;;;GAaA;AAED,eAAO,MAAM,kCAAkC,gCAA0B,CAAC;AA88C1E,wBAAsB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8blE"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAkF,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAkBpI,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAiFrD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,sBAAsB,EAAiB,MAAM,wBAAwB,CAAC;AAqPpF,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmClK;AAED,iBAAe,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAe,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8D3I;AAMD,eAAO,MAAM,uBAAuB,qBAAe,CAAC;AACpD,eAAO,MAAM,yBAAyB,4BAAiB,CAAC;AAqCxD,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA6ClB;AAED,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,SAAS,CAMpB;AAoKD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,sBAAsB,GAAG,oBAAoB,CA0C5G;AAgtCD;;;;;;;;;;;GAWG;AACH,iBAAe,uBAAuB,CAAC,GAAG,EAAE;IAC1C,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;;;;GAaA;AAED,eAAO,MAAM,kCAAkC,gCAA0B,CAAC;AA88C1E,wBAAsB,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8blE"}
package/dist/daemon.js CHANGED
@@ -15,7 +15,7 @@ import { sendRestartReportIfPending } from './core/restart-report.js';
15
15
  import { statSync } from 'node:fs';
16
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
- import { loadBotConfigs, registerBot, getBot, getAllBots, findOncallChatForAnyBot } from './bot-registry.js';
18
+ import { loadBotConfigs, registerBot, getBot, getAllBots, findOncallChat } from './bot-registry.js';
19
19
  import * as sessionStore from './services/session-store.js';
20
20
  import * as chatFirstSeenStore from './services/chat-first-seen-store.js';
21
21
  import { ensureDefaultOncallBound } from './services/oncall-store.js';
@@ -50,7 +50,7 @@ import { isCallbackUrl, handleCallbackUrl } from './utils/user-token.js';
50
50
  import { consumeQuota, removeChatGrant, removeGlobalGrant } from './services/grant-store.js';
51
51
  import { abortCharge, commitCharge, beginCharge } from './services/quota-dedup.js';
52
52
  import { getSessionWorkingDir, getProjectScanDirs, expandHome, downloadResources, formatAttachmentsHint, buildNewTopicPrompt, buildFollowUpContent, buildBridgeInputContent, buildReforkPrompt, getAvailableBots, restoreActiveSessions, executeScheduledTask, persistStreamCardState, rememberLastCliInput, ensureTerminalWorkerPort, ensureSessionWhiteboard, } from './core/session-manager.js';
53
- import { beginReplyTargetTurn, resolveSessionReplyTarget, syncReplyTargetState } from './core/reply-target.js';
53
+ import { beginReplyTargetTurn, fallbackTurnId, resolveSessionReplyTarget, syncReplyTargetState } from './core/reply-target.js';
54
54
  import { sweepOrphanSandboxes } from './adapters/backend/sandbox.js';
55
55
  import { sweepIdleWorkers, DEFAULT_MAX_LIVE_WORKERS } from './core/idle-worker-sweeper.js';
56
56
  import { handleCardAction } from './im/lark/card-handler.js';
@@ -344,7 +344,18 @@ async function sessionReply(anchor, content, msgType = 'text', larkAppId, turnId
344
344
  const fresh = readSessionFreshFromDisk(ds.session.sessionId, ds.larkAppId);
345
345
  if (fresh)
346
346
  syncReplyTargetState(ds, fresh);
347
- const target = resolveSessionReplyTarget(ds, turnId);
347
+ // Resolve through fallbackTurnId so daemon-side sends that carry no turn of
348
+ // their own (the repo-select card, skip/switch confirmations, crash notices)
349
+ // still anchor into the current shared fold-back topic instead of leaking to
350
+ // the chat top level. Callers that DO pass an explicit turnId are unchanged —
351
+ // fallbackTurnId returns it verbatim, so the stale-turn hijack guard stays
352
+ // authoritative. Baking the fallback in HERE (the single chat-scope send
353
+ // chokepoint) means no individual send site can re-introduce the leak by
354
+ // forgetting to thread — the recurring failure mode behind this and the
355
+ // earlier e619250d fix. Guarded by test/session-reply-thread-anchor.test.ts
356
+ // (drives the real sessionReply) and test/reply-target-fallback.test.ts
357
+ // (the resolveSessionReplyTarget × fallbackTurnId composition it relies on).
358
+ const target = resolveSessionReplyTarget(ds, fallbackTurnId(ds, turnId));
348
359
  if (target.mode === 'thread')
349
360
  return replyMessage(appId, target.rootMessageId, content, msgType, true, undefined, hookContext);
350
361
  if (ds.session.rootMessageId) {
@@ -360,6 +371,12 @@ async function sessionReply(anchor, content, msgType = 'text', larkAppId, turnId
360
371
  // Thread-scope (or unknown / legacy): reply in thread.
361
372
  return replyMessage(appId, anchor, content, msgType, true, undefined, hookContext);
362
373
  }
374
+ // Test seams: drive the real sessionReply (the chat-scope thread/top-level
375
+ // chokepoint) against a seeded session, so the shared fold-back leak is guarded
376
+ // at the function that actually routes — not just the resolveSessionReplyTarget
377
+ // composition it relies on. See test/reply-target-fallback.test.ts.
378
+ export const __testOnly_sessionReply = sessionReply;
379
+ export const __testOnly_activeSessions = activeSessions;
363
380
  async function revokeQuotaGrant(larkAppId, chatId, senderOpenId, ev) {
364
381
  const result = ev.reason === 'chatGrant'
365
382
  ? await removeChatGrant(larkAppId, chatId, senderOpenId)
@@ -1628,7 +1645,7 @@ function parseTriggerChatBinding(raw) {
1628
1645
  * Default-oncall is a uniform forward-only policy: whenever the toggle is
1629
1646
  * on, ANY chat the bot is currently in — old or newly added, doesn't matter —
1630
1647
  * gets auto-bound to the configured workingDir on its next observed topic,
1631
- * unless it's already bound (`findOncallChatForAnyBot` upstream) or the user
1648
+ * unless it's already bound (`findOncallChat`, per-bot, upstream) or the user
1632
1649
  * has opted out via tombstone.
1633
1650
  *
1634
1651
  * Thin wrapper around the shared `ensureDefaultOncallBound` in oncall-store,
@@ -1666,7 +1683,8 @@ function resolveBotDefaultWorkingDir(larkAppId) {
1666
1683
  }
1667
1684
  /**
1668
1685
  * Resolve the pinned working dir for a brand-new topic via the layered lookup:
1669
- * 1) an existing oncall binding (this bot or a sibling)
1686
+ * 1) this bot's OWN oncall binding (per-bot: another bot's binding never pins
1687
+ * this bot — cross-bot dir alignment is handled by layer 3 inherit-peer)
1670
1688
  * 2) this bot's defaultOncall — auto-binds a brand-new chat when the flag is on
1671
1689
  * (this WRITES state, so it must run identically on every spawn path)
1672
1690
  * 3) a sibling session's workingDir (cross-bot / chat-scope inheritance)
@@ -1676,7 +1694,7 @@ function resolveBotDefaultWorkingDir(larkAppId) {
1676
1694
  * both honor the defaultOncall auto-bind the same way.
1677
1695
  */
1678
1696
  async function resolvePinnedWorkingDir(ctx) {
1679
- let oncallEntry = findOncallChatForAnyBot(ctx.chatId);
1697
+ let oncallEntry = findOncallChat(ctx.larkAppId, ctx.chatId);
1680
1698
  if (!oncallEntry) {
1681
1699
  oncallEntry = await maybeAutoBindDefaultOncall(ctx.larkAppId, ctx.chatId, ctx.chatType);
1682
1700
  }