botmux 2.81.0 → 2.82.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.
@@ -0,0 +1,16 @@
1
+ import { type DaemonSession } from './types.js';
2
+ import type { Locale } from '../i18n/index.js';
3
+ /**
4
+ * Build the same "session closed" card `/close` emits for a session that is
5
+ * about to be displaced (e.g. a mid-session `/repo` switch reuses the SAME
6
+ * anchor for a fresh session). Without this trace the old context vanishes —
7
+ * relay/adopt/resume all hit `anchor_occupied` once the new session holds the
8
+ * anchor — so the card keeps it visible and carries the terminal
9
+ * `claude --resume` command as the real recovery path.
10
+ *
11
+ * MUST be called BEFORE killWorker/closeSession: it reads the live session's
12
+ * identity (sessionId, cliSessionId, title, workingDir, anchor) straight off
13
+ * `ds`. Returns the card JSON; the caller decides how to deliver it.
14
+ */
15
+ export declare function buildClosedSessionCard(ds: DaemonSession, locale: Locale): string;
16
+ //# sourceMappingURL=closed-session-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closed-session-card.d.ts","sourceRoot":"","sources":["../../src/core/closed-session-card.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAyBhF"}
@@ -0,0 +1,39 @@
1
+ import { getBot } from '../bot-registry.js';
2
+ import { createCliAdapterSync } from '../adapters/cli/registry.js';
3
+ import { decorateResumeForWrapper } from '../setup/cli-selection.js';
4
+ import { buildSessionClosedCard } from '../im/lark/card-builder.js';
5
+ import { sessionAnchorId } from './types.js';
6
+ /**
7
+ * Build the same "session closed" card `/close` emits for a session that is
8
+ * about to be displaced (e.g. a mid-session `/repo` switch reuses the SAME
9
+ * anchor for a fresh session). Without this trace the old context vanishes —
10
+ * relay/adopt/resume all hit `anchor_occupied` once the new session holds the
11
+ * anchor — so the card keeps it visible and carries the terminal
12
+ * `claude --resume` command as the real recovery path.
13
+ *
14
+ * MUST be called BEFORE killWorker/closeSession: it reads the live session's
15
+ * identity (sessionId, cliSessionId, title, workingDir, anchor) straight off
16
+ * `ds`. Returns the card JSON; the caller decides how to deliver it.
17
+ */
18
+ export function buildClosedSessionCard(ds, locale) {
19
+ const botCfg = getBot(ds.larkAppId).config;
20
+ const closedSessionId = ds.session.sessionId;
21
+ const closedCliId = ds.session.cliId ?? botCfg.cliId;
22
+ const cliResumeCommand = (() => {
23
+ try {
24
+ const adapter = createCliAdapterSync(closedCliId, botCfg.cliPathOverride);
25
+ const raw = adapter.buildResumeCommand?.({
26
+ sessionId: closedSessionId,
27
+ cliSessionId: ds.session.cliSessionId,
28
+ }) ?? null;
29
+ // ttadk 网关:resume 命令必须带 `-m <model> --skip-check`(模型取 bot.model),
30
+ // 否则用户复制粘贴这条命令会卡在 ttadk 的交互式选模型菜单(CoCo 不带 -m)。
31
+ return raw ? decorateResumeForWrapper(raw, botCfg.wrapperCli, { ttadkModel: botCfg.model }) : null;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ })();
37
+ return buildSessionClosedCard(closedSessionId, sessionAnchorId(ds), ds.session.title, closedCliId, ds.session.workingDir, cliResumeCommand, locale);
38
+ }
39
+ //# sourceMappingURL=closed-session-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closed-session-card.js","sourceRoot":"","sources":["../../src/core/closed-session-card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAsB,MAAM,YAAY,CAAC;AAGjE;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAiB,EAAE,MAAc;IACtE,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,eAAe,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IAC7C,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC;IACrD,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE;QAC7B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;YAC1E,MAAM,GAAG,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;gBACvC,SAAS,EAAE,eAAe;gBAC1B,YAAY,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY;aACtC,CAAC,IAAI,IAAI,CAAC;YACX,kEAAkE;YAClE,+CAA+C;YAC/C,OAAO,GAAG,CAAC,CAAC,CAAC,wBAAwB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACrG,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;IAC1B,CAAC,CAAC,EAAE,CAAC;IACL,OAAO,sBAAsB,CAC3B,eAAe,EACf,eAAe,CAAC,EAAE,CAAC,EACnB,EAAE,CAAC,OAAO,CAAC,KAAK,EAChB,WAAW,EACX,EAAE,CAAC,OAAO,CAAC,UAAU,EACrB,gBAAgB,EAChB,MAAM,CACP,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@ import * as scheduleStore from '../services/schedule-store.js';
13
13
  import * as scheduler from './scheduler.js';
14
14
  import { scanMultipleProjects, describeProjectDir } from '../services/project-scanner.js';
15
15
  import { createRepoWorktree } from '../services/git-worktree.js';
16
- import { buildRepoSelectCard, buildAdoptSelectCard, buildCodexAppThreadSelectCard, buildSessionClosedCard, buildSlashListCard, getCliDisplayName, buildConfigCard, buildLandCard } from '../im/lark/card-builder.js';
16
+ import { buildRepoSelectCard, buildAdoptSelectCard, buildCodexAppThreadSelectCard, buildSlashListCard, getCliDisplayName, buildConfigCard, buildLandCard } from '../im/lark/card-builder.js';
17
17
  import { computeSandboxDiff } from '../services/sandbox-land.js';
18
18
  import { createCliAdapterSync } from '../adapters/cli/registry.js';
19
19
  import { deleteMessage, sendMessage, sendUserMessage, listChatBotMembers, resolveUserUnionId, getChatModeStrict, uploadFile } from '../im/lark/client.js';
@@ -34,7 +34,8 @@ import { putDocSubscription, removeDocSubscription, listDocSubscriptionsForSessi
34
34
  import { bindOncall, unbindOncall, getOncallStatus } from '../services/oncall-store.js';
35
35
  import { CONFIG_FIELDS, findConfigField, settableFieldKeys, parseBooleanValue, applyConfigField, setBotAllowedUsers, getConfigSnapshot, getConfigCardData, } from '../services/bot-config-store.js';
36
36
  import { resolveCliId, findInvalidAllowedUserEntries } from '../setup/bot-config-editor.js';
37
- import { decorateResumeForWrapper } from '../setup/cli-selection.js';
37
+ import { buildClosedSessionCard } from './closed-session-card.js';
38
+ import { ttadkConfigModelChoices } from '../setup/cli-selection.js';
38
39
  import { publishAttentionPatch, announcePendingRepoSession } from './session-activity.js';
39
40
  import { setCardMode } from '../services/card-mode-store.js';
40
41
  import { canOperate } from '../im/lark/event-dispatcher.js';
@@ -618,11 +619,17 @@ async function handleConfigCommand(message, rootId, larkAppId, deps) {
618
619
  const cardLoc = cardLocaleArg(sub);
619
620
  if (!sub || cardLoc) {
620
621
  const renderLoc = cardLoc ?? loc;
621
- let modelChoices = [];
622
- try {
623
- modelChoices = createCliAdapterSync(bot.config.cliId, bot.config.cliPathOverride).modelChoices ?? [];
622
+ // ttadk 网关 bot:模型候选用 ttadk 网关模型(glm-5.1…),不是底层适配器的
623
+ // opus/gpt-5(那会被 worker 注入成 `ttadk -m opus` 用错模型启动失败);CoCo 无候选。
624
+ // ttadk(返回 null)才回落底层适配器自己的 modelChoices
625
+ const ttadkChoices = ttadkConfigModelChoices(bot.config.wrapperCli);
626
+ let modelChoices = ttadkChoices ?? [];
627
+ if (ttadkChoices === null) {
628
+ try {
629
+ modelChoices = createCliAdapterSync(bot.config.cliId, bot.config.cliPathOverride).modelChoices ?? [];
630
+ }
631
+ catch { /* 无候选 → 不渲染 model 下拉 */ }
624
632
  }
625
- catch { /* 无候选 → 不渲染 model 下拉 */ }
626
633
  const data = getConfigCardData(larkAppId, modelChoices);
627
634
  if (!data) {
628
635
  await reply(buildConfigHelp(renderLoc));
@@ -875,29 +882,12 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
875
882
  switch (cmd) {
876
883
  case '/close': {
877
884
  if (ds) {
878
- const closedSessionId = ds.session.sessionId;
879
- const closedTitle = ds.session.title;
880
- const botCfg = getBot(ds.larkAppId).config;
881
- const closedCliId = ds.session.cliId ?? botCfg.cliId;
882
- const closedAnchor = sessionAnchorId(ds);
883
- const closedWorkingDir = ds.session.workingDir;
884
- const cliResumeCommand = (() => {
885
- try {
886
- const adapter = createCliAdapterSync(closedCliId, botCfg.cliPathOverride);
887
- const raw = adapter.buildResumeCommand?.({
888
- sessionId: closedSessionId,
889
- cliSessionId: ds.session.cliSessionId,
890
- }) ?? null;
891
- return raw ? decorateResumeForWrapper(raw, botCfg.wrapperCli) : null;
892
- }
893
- catch {
894
- return null;
895
- }
896
- })();
885
+ // Capture the closed-session card BEFORE killWorker/closeSession —
886
+ // it reads the live session's identity off `ds`.
887
+ const card = buildClosedSessionCard(ds, loc);
897
888
  killWorker(ds);
898
- sessionStore.closeSession(closedSessionId);
889
+ sessionStore.closeSession(ds.session.sessionId);
899
890
  activeSessions.delete(sessionKey(rootId, larkAppId));
900
- const card = buildSessionClosedCard(closedSessionId, closedAnchor, closedTitle, closedCliId, closedWorkingDir, cliResumeCommand, loc);
901
891
  // 「会话已关闭」卡片优先「仅自己可见」:普通群里走 ephemeral 只发给执行
902
892
  // /close 的本人;话题群不支持 ephemeral(18053) 时回退为正常的群内可见回复
903
893
  // ——与流式卡片上「关闭会话」按钮的送达方式保持一致。
@@ -1094,19 +1084,39 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
1094
1084
  // close + recreate the session (mid-session switch). Used by both the
1095
1085
  // numeric `/repo <N>` form and the `/repo <path|name>` form.
1096
1086
  const commitRepoSelection = async (selectedPath, displayName, how) => {
1097
- ds.workingDir = selectedPath;
1098
- ds.session.workingDir = selectedPath;
1099
- sessionStore.updateSession(ds.session);
1100
1087
  if (ds.pendingRepo) {
1088
+ // First spawn: pin the new cwd onto the CURRENT session, then fork.
1089
+ ds.workingDir = selectedPath;
1090
+ ds.session.workingDir = selectedPath;
1091
+ sessionStore.updateSession(ds.session);
1101
1092
  await forkPendingCli(t('cmd.repo.selected_in_pending', { name: displayName }, loc));
1102
1093
  }
1103
1094
  else {
1095
+ // Safety net: a mid-session `/repo` switch closes the running
1096
+ // session and spawns a fresh one on the SAME anchor. Without a
1097
+ // trace, the old context silently vanishes (relay/adopt/resume all
1098
+ // hit `anchor_occupied` once the new session holds the anchor).
1099
+ // So, before displacing it, post the same "session closed" card
1100
+ // `/close` emits — it keeps the old session visible and carries the
1101
+ // terminal `claude --resume` command. (Its in-card resume button
1102
+ // still hits anchor_occupied while the new session occupies this
1103
+ // anchor — expected; `/close` the new one first, or use the
1104
+ // command.) Mirrors the `/close` case above.
1105
+ //
1106
+ // The new cwd is NOT written onto the old session here — it would
1107
+ // pollute the displaced session's stored workingDir (and the closed
1108
+ // card), so `claude --resume` later would reopen the old context in
1109
+ // the new repo's cwd. The new repo is pinned onto the fresh session
1110
+ // below instead.
1111
+ const closedCard = buildClosedSessionCard(ds, loc);
1104
1112
  killWorker(ds);
1105
1113
  sessionStore.closeSession(ds.session.sessionId);
1114
+ await deliverEphemeralOrReply(ds, message.senderId, closedCard, 'interactive', () => sessionReply(rootId, closedCard, 'interactive'));
1106
1115
  const session = sessionStore.createSession(ds.chatId, rootId, displayName, ds.chatType);
1107
1116
  ds.session = session;
1108
1117
  ds.lastUserPrompt = undefined;
1109
1118
  ds.lastCliInput = undefined;
1119
+ ds.workingDir = selectedPath;
1110
1120
  ds.session.workingDir = selectedPath;
1111
1121
  ds.session.larkAppId = ds.larkAppId;
1112
1122
  sessionStore.updateSession(ds.session);