botmux 2.95.0 → 2.96.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 (175) hide show
  1. package/README.en.md +4 -4
  2. package/README.md +3 -3
  3. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  4. package/dist/adapters/backend/tmux-backend.js +6 -10
  5. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  6. package/dist/adapters/cli/kimi.d.ts +4 -0
  7. package/dist/adapters/cli/kimi.d.ts.map +1 -0
  8. package/dist/adapters/cli/kimi.js +54 -0
  9. package/dist/adapters/cli/kimi.js.map +1 -0
  10. package/dist/adapters/cli/registry.d.ts +2 -1
  11. package/dist/adapters/cli/registry.d.ts.map +1 -1
  12. package/dist/adapters/cli/registry.js +6 -20
  13. package/dist/adapters/cli/registry.js.map +1 -1
  14. package/dist/adapters/cli/types.d.ts +1 -1
  15. package/dist/adapters/cli/types.d.ts.map +1 -1
  16. package/dist/adapters/hook-installer.d.ts.map +1 -1
  17. package/dist/adapters/hook-installer.js +138 -28
  18. package/dist/adapters/hook-installer.js.map +1 -1
  19. package/dist/bot-registry.d.ts +61 -4
  20. package/dist/bot-registry.d.ts.map +1 -1
  21. package/dist/bot-registry.js +164 -3
  22. package/dist/bot-registry.js.map +1 -1
  23. package/dist/cli/stdin-encoding.d.ts +21 -0
  24. package/dist/cli/stdin-encoding.d.ts.map +1 -1
  25. package/dist/cli/stdin-encoding.js +37 -0
  26. package/dist/cli/stdin-encoding.js.map +1 -1
  27. package/dist/cli.d.ts.map +1 -1
  28. package/dist/cli.js +14 -4
  29. package/dist/cli.js.map +1 -1
  30. package/dist/config.d.ts +18 -0
  31. package/dist/config.d.ts.map +1 -1
  32. package/dist/config.js +27 -0
  33. package/dist/config.js.map +1 -1
  34. package/dist/core/ask-hook/opencode.d.ts +15 -17
  35. package/dist/core/ask-hook/opencode.d.ts.map +1 -1
  36. package/dist/core/ask-hook/opencode.js +17 -19
  37. package/dist/core/ask-hook/opencode.js.map +1 -1
  38. package/dist/core/command-handler.d.ts.map +1 -1
  39. package/dist/core/command-handler.js +1 -0
  40. package/dist/core/command-handler.js.map +1 -1
  41. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  42. package/dist/core/dashboard-ipc-server.js +193 -5
  43. package/dist/core/dashboard-ipc-server.js.map +1 -1
  44. package/dist/core/dashboard-rows.d.ts +2 -0
  45. package/dist/core/dashboard-rows.d.ts.map +1 -1
  46. package/dist/core/dashboard-rows.js +2 -0
  47. package/dist/core/dashboard-rows.js.map +1 -1
  48. package/dist/core/trigger-session.d.ts.map +1 -1
  49. package/dist/core/trigger-session.js +99 -7
  50. package/dist/core/trigger-session.js.map +1 -1
  51. package/dist/core/types.d.ts +16 -0
  52. package/dist/core/types.d.ts.map +1 -1
  53. package/dist/core/types.js.map +1 -1
  54. package/dist/core/worker-pool.d.ts +1 -1
  55. package/dist/core/worker-pool.d.ts.map +1 -1
  56. package/dist/core/worker-pool.js +68 -11
  57. package/dist/core/worker-pool.js.map +1 -1
  58. package/dist/dashboard/bot-payload.d.ts +7 -0
  59. package/dist/dashboard/bot-payload.d.ts.map +1 -1
  60. package/dist/dashboard/bot-payload.js +7 -1
  61. package/dist/dashboard/bot-payload.js.map +1 -1
  62. package/dist/dashboard/session-cleanup.d.ts +1 -0
  63. package/dist/dashboard/session-cleanup.d.ts.map +1 -1
  64. package/dist/dashboard/session-cleanup.js +2 -0
  65. package/dist/dashboard/session-cleanup.js.map +1 -1
  66. package/dist/dashboard/settings-write-applier.d.ts +2 -1
  67. package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
  68. package/dist/dashboard/settings-write-applier.js +6 -0
  69. package/dist/dashboard/settings-write-applier.js.map +1 -1
  70. package/dist/dashboard/trigger-api.d.ts +4 -0
  71. package/dist/dashboard/trigger-api.d.ts.map +1 -1
  72. package/dist/dashboard/trigger-api.js +20 -0
  73. package/dist/dashboard/trigger-api.js.map +1 -1
  74. package/dist/dashboard/web/bot-defaults.d.ts +1 -0
  75. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  76. package/dist/dashboard/web/bot-defaults.js +247 -4
  77. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  78. package/dist/dashboard/web/connectors.d.ts +11 -0
  79. package/dist/dashboard/web/connectors.d.ts.map +1 -1
  80. package/dist/dashboard/web/connectors.js +45 -1
  81. package/dist/dashboard/web/connectors.js.map +1 -1
  82. package/dist/dashboard/web/groups.d.ts +7 -0
  83. package/dist/dashboard/web/groups.d.ts.map +1 -1
  84. package/dist/dashboard/web/groups.js +62 -15
  85. package/dist/dashboard/web/groups.js.map +1 -1
  86. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  87. package/dist/dashboard/web/i18n.js +58 -2
  88. package/dist/dashboard/web/i18n.js.map +1 -1
  89. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  90. package/dist/dashboard/web/sessions.js +149 -5
  91. package/dist/dashboard/web/sessions.js.map +1 -1
  92. package/dist/dashboard/web/settings.d.ts.map +1 -1
  93. package/dist/dashboard/web/settings.js +10 -0
  94. package/dist/dashboard/web/settings.js.map +1 -1
  95. package/dist/dashboard/webhook-routes.d.ts.map +1 -1
  96. package/dist/dashboard/webhook-routes.js +126 -6
  97. package/dist/dashboard/webhook-routes.js.map +1 -1
  98. package/dist/dashboard-web/app.js +827 -751
  99. package/dist/dashboard-web/style.css +47 -0
  100. package/dist/dashboard.js +85 -6
  101. package/dist/dashboard.js.map +1 -1
  102. package/dist/global-config.d.ts +4 -0
  103. package/dist/global-config.d.ts.map +1 -1
  104. package/dist/global-config.js +2 -0
  105. package/dist/global-config.js.map +1 -1
  106. package/dist/i18n/en.d.ts.map +1 -1
  107. package/dist/i18n/en.js +1 -0
  108. package/dist/i18n/en.js.map +1 -1
  109. package/dist/i18n/zh.d.ts.map +1 -1
  110. package/dist/i18n/zh.js +1 -0
  111. package/dist/i18n/zh.js.map +1 -1
  112. package/dist/im/lark/card-builder.d.ts.map +1 -1
  113. package/dist/im/lark/card-builder.js +1 -0
  114. package/dist/im/lark/card-builder.js.map +1 -1
  115. package/dist/im/lark/client.d.ts +12 -0
  116. package/dist/im/lark/client.d.ts.map +1 -1
  117. package/dist/im/lark/client.js +154 -9
  118. package/dist/im/lark/client.js.map +1 -1
  119. package/dist/im/lark/event-dispatcher.d.ts +13 -0
  120. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  121. package/dist/im/lark/event-dispatcher.js +200 -16
  122. package/dist/im/lark/event-dispatcher.js.map +1 -1
  123. package/dist/im/lark/message-parser.d.ts +11 -0
  124. package/dist/im/lark/message-parser.d.ts.map +1 -1
  125. package/dist/im/lark/message-parser.js +2 -1
  126. package/dist/im/lark/message-parser.js.map +1 -1
  127. package/dist/im/lark/summary-command.d.ts +19 -0
  128. package/dist/im/lark/summary-command.d.ts.map +1 -0
  129. package/dist/im/lark/summary-command.js +276 -0
  130. package/dist/im/lark/summary-command.js.map +1 -0
  131. package/dist/mir-local-runtime.d.ts +40 -0
  132. package/dist/mir-local-runtime.d.ts.map +1 -1
  133. package/dist/mir-local-runtime.js +246 -52
  134. package/dist/mir-local-runtime.js.map +1 -1
  135. package/dist/mir-runner.js +26 -1
  136. package/dist/mir-runner.js.map +1 -1
  137. package/dist/services/card-prefs-store.d.ts +2 -2
  138. package/dist/services/card-prefs-store.d.ts.map +1 -1
  139. package/dist/services/card-prefs-store.js +5 -5
  140. package/dist/services/card-prefs-store.js.map +1 -1
  141. package/dist/services/chat-reply-mode-store.d.ts +6 -4
  142. package/dist/services/chat-reply-mode-store.d.ts.map +1 -1
  143. package/dist/services/chat-reply-mode-store.js +6 -4
  144. package/dist/services/chat-reply-mode-store.js.map +1 -1
  145. package/dist/services/pi-transcript.d.ts +19 -0
  146. package/dist/services/pi-transcript.d.ts.map +1 -0
  147. package/dist/services/pi-transcript.js +222 -0
  148. package/dist/services/pi-transcript.js.map +1 -0
  149. package/dist/services/seen-message-store.d.ts +12 -0
  150. package/dist/services/seen-message-store.d.ts.map +1 -0
  151. package/dist/services/seen-message-store.js +119 -0
  152. package/dist/services/seen-message-store.js.map +1 -0
  153. package/dist/services/summary-range-store.d.ts +21 -0
  154. package/dist/services/summary-range-store.d.ts.map +1 -0
  155. package/dist/services/summary-range-store.js +89 -0
  156. package/dist/services/summary-range-store.js.map +1 -0
  157. package/dist/services/trigger-types.d.ts +14 -2
  158. package/dist/services/trigger-types.d.ts.map +1 -1
  159. package/dist/services/trigger-types.js +15 -1
  160. package/dist/services/trigger-types.js.map +1 -1
  161. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  162. package/dist/setup/bot-config-editor.js +2 -0
  163. package/dist/setup/bot-config-editor.js.map +1 -1
  164. package/dist/types.d.ts +20 -1
  165. package/dist/types.d.ts.map +1 -1
  166. package/dist/utils/executable.d.ts +3 -0
  167. package/dist/utils/executable.d.ts.map +1 -0
  168. package/dist/utils/executable.js +24 -0
  169. package/dist/utils/executable.js.map +1 -0
  170. package/dist/worker.js +75 -6
  171. package/dist/worker.js.map +1 -1
  172. package/dist/workflows/definition.d.ts +8 -8
  173. package/dist/workflows/events/payloads.d.ts +10 -10
  174. package/dist/workflows/events/schema.d.ts +28 -28
  175. package/package.json +1 -1
@@ -18,10 +18,11 @@ import * as observedBotsStore from '../services/observed-bots-store.js';
18
18
  import { getDeploymentIdentity } from '../services/deployment-identity.js';
19
19
  import * as grantPrefsStore from '../services/grant-prefs-store.js';
20
20
  import { findConfigField, applyConfigField, coerceConfigValue } from '../services/bot-config-store.js';
21
+ import { summaryRangeFromBotConfig, updateDashboardSummaryRange } from '../services/summary-range-store.js';
21
22
  import { config } from '../config.js';
22
23
  import { computeSandboxDiff, applySandboxDiff } from '../services/sandbox-land.js';
23
24
  import { buildSafeInsightConversation, buildSafeInsightOverview, buildSafeInsightReport, buildSafeInsightTurnDetail } from '../services/insight/report.js';
24
- import { readRawConfig, findEntryIndex, requireConfigPath } from '../services/config-store.js';
25
+ import { readRawConfig, findEntryIndex, requireConfigPath, rmwBotEntry } from '../services/config-store.js';
25
26
  import { setDefaultLocale, localeForBot, t } from '../i18n/index.js';
26
27
  import { isLocale } from '../i18n/types.js';
27
28
  import { readGlobalConfig } from '../global-config.js';
@@ -44,6 +45,7 @@ import { deleteRoleProfileEntry, deleteRoleProfileIfEmpty, isValidRoleProfileId,
44
45
  import { triggerSessionTurn } from './trigger-session.js';
45
46
  import { triggerWorkflowFromEnvelope } from '../workflows/trigger-from-envelope.js';
46
47
  import { validateTriggerRequest } from '../services/trigger-types.js';
48
+ import { resolveCliSelection, selectionKeyForBot } from '../setup/cli-selection.js';
47
49
  // Workflow runner is wired by the daemon (it owns the heavy triggerWorkflowRun
48
50
  // deps). Until set, workflow-targeted triggers report not-implemented.
49
51
  let workflowRunner = null;
@@ -209,6 +211,46 @@ ipcRoute('POST', '/api/sessions/:sessionId/restart', (_req, res, params) => {
209
211
  function findSessionRecord(sessionId) {
210
212
  return findActiveBySessionId(sessionId)?.session ?? sessionStore.getSession(sessionId);
211
213
  }
214
+ function buildAsyncTriggerLookupResponse(sessionId, triggerId) {
215
+ const ds = findActiveBySessionId(sessionId);
216
+ if (!ds) {
217
+ return { ok: false, errorCode: 'session_not_found', error: `active session not found: ${sessionId}` };
218
+ }
219
+ const resolvedTriggerId = triggerId || ds.latestAsyncTriggerId;
220
+ if (!resolvedTriggerId) {
221
+ return { ok: false, errorCode: 'bad_request', error: 'no async trigger recorded for this session' };
222
+ }
223
+ const state = ds.asyncTriggerResults?.get(resolvedTriggerId);
224
+ if (!state) {
225
+ return { ok: false, errorCode: 'bad_request', error: `async trigger not found for session: ${resolvedTriggerId}` };
226
+ }
227
+ if (state.status === 'completed') {
228
+ return {
229
+ ok: true,
230
+ triggerId: resolvedTriggerId,
231
+ action: 'completed',
232
+ target: { kind: 'turn', sessionId, chatId: ds.chatId },
233
+ output: state.content ? { content: state.content } : undefined,
234
+ async: {
235
+ status: 'completed',
236
+ sessionId,
237
+ completedAt: state.completedAt ? new Date(state.completedAt).toISOString() : undefined,
238
+ },
239
+ message: 'async trigger completed',
240
+ };
241
+ }
242
+ return {
243
+ ok: true,
244
+ triggerId: resolvedTriggerId,
245
+ action: 'queued',
246
+ target: { kind: 'turn', sessionId, chatId: ds.chatId },
247
+ async: {
248
+ status: 'pending',
249
+ sessionId,
250
+ },
251
+ message: 'async trigger pending',
252
+ };
253
+ }
212
254
  // 看板放置:dashboard 看板视图拖拽卡片后持久化列 + 列内排序位置。
213
255
  // 改完广播 session.update,所有打开的 dashboard 实时同步。
214
256
  ipcRoute('POST', '/api/sessions/:sessionId/board', async (req, res, params) => {
@@ -352,6 +394,17 @@ ipcRoute('GET', '/api/sessions/:sessionId/history', async (req, res, params) =>
352
394
  jsonRes(res, 502, { ok: false, error: String(err?.message ?? err) });
353
395
  }
354
396
  });
397
+ ipcRoute('GET', '/api/sessions/:sessionId/trigger-result', (req, res, params) => {
398
+ const url = new URL(req.url ?? '/', 'http://localhost');
399
+ const triggerId = url.searchParams.get('triggerId') ?? undefined;
400
+ const result = buildAsyncTriggerLookupResponse(params.sessionId, triggerId);
401
+ const status = result.ok
402
+ ? 200
403
+ : result.errorCode === 'session_not_found'
404
+ ? 404
405
+ : 400;
406
+ jsonRes(res, status, result);
407
+ });
355
408
  // 会话 insight:只读解析本会话的 transcript,产出动作 span / 失败聚合 / 规则建议
356
409
  // (SafeInsightReport)。底层 services/insight 已做 fail-closed 脱敏投影——raw 命令
357
410
  // 与输出永不进结构。detail=summary 只返聚合+建议(/insight 卡片、抽屉概览用);
@@ -487,6 +540,33 @@ ipcRoute('POST', '/api/sessions/:sessionId/rename', async (req, res, params) =>
487
540
  });
488
541
  jsonRes(res, 200, { ok: true, title });
489
542
  });
543
+ // 会话锁定:保护被锁定会话不被 dashboard「清理空闲」批量关闭。锁定是会话元数据,
544
+ // 不影响用户显式点击关闭/批量关闭,避免把会话变成不可管理状态。
545
+ ipcRoute('POST', '/api/sessions/:sessionId/lock', async (req, res, params) => {
546
+ let body;
547
+ try {
548
+ body = await readJsonBody(req);
549
+ }
550
+ catch {
551
+ return jsonRes(res, 400, { ok: false, error: 'bad_json' });
552
+ }
553
+ if (typeof body.locked !== 'boolean')
554
+ return jsonRes(res, 400, { ok: false, error: 'bad_locked' });
555
+ const session = findSessionRecord(params.sessionId);
556
+ if (!session)
557
+ return jsonRes(res, 404, { ok: false, error: 'session_not_found' });
558
+ if (body.locked)
559
+ session.locked = true;
560
+ else
561
+ delete session.locked;
562
+ sessionStore.updateSession(session);
563
+ const locked = !!session.locked;
564
+ dashboardEventBus.publish({
565
+ type: 'session.update',
566
+ body: { sessionId: params.sessionId, patch: { locked } },
567
+ });
568
+ jsonRes(res, 200, { ok: true, locked });
569
+ });
490
570
  /**
491
571
  * Mint the WRITABLE web-terminal link for a live session — the dashboard
492
572
  * counterpart to the Lark card's "🔑 获取操作链接" button. Returns the URL with
@@ -863,9 +943,11 @@ ipcRoute('POST', '/api/trigger', async (req, res) => {
863
943
  ? 403
864
944
  : result.errorCode === 'session_not_found'
865
945
  ? 404
866
- : result.errorCode === 'target_required' || result.errorCode === 'bad_request'
867
- ? 400
868
- : 500;
946
+ : result.errorCode === 'wait_timeout'
947
+ ? 504
948
+ : result.errorCode === 'target_required' || result.errorCode === 'bad_request'
949
+ ? 400
950
+ : 500;
869
951
  return jsonRes(res, status, result);
870
952
  }
871
953
  catch (e) {
@@ -1180,6 +1262,18 @@ ipcRoute('GET', '/api/bot-default-oncall', async (_req, res) => {
1180
1262
  p2pMode = 'chat';
1181
1263
  }
1182
1264
  catch { /* default thread */ }
1265
+ let cliId = '';
1266
+ let wrapperCli = null;
1267
+ let model = null;
1268
+ let agentSelectionKey = '';
1269
+ try {
1270
+ const cfg = getBot(cachedLarkAppId).config;
1271
+ cliId = cfg.cliId;
1272
+ wrapperCli = typeof cfg.wrapperCli === 'string' && cfg.wrapperCli.trim() ? cfg.wrapperCli : null;
1273
+ model = typeof cfg.model === 'string' && cfg.model.trim() ? cfg.model : null;
1274
+ agentSelectionKey = selectionKeyForBot(cliId, wrapperCli ?? undefined);
1275
+ }
1276
+ catch { /* no registered bot */ }
1183
1277
  let maxLiveWorkers = null;
1184
1278
  try {
1185
1279
  const m = getBot(cachedLarkAppId).config.maxLiveWorkers;
@@ -1218,6 +1312,10 @@ ipcRoute('GET', '/api/bot-default-oncall', async (_req, res) => {
1218
1312
  jsonRes(res, 200, {
1219
1313
  larkAppId: cachedLarkAppId,
1220
1314
  botName: getBotName(),
1315
+ cliId,
1316
+ wrapperCli,
1317
+ model,
1318
+ agentSelectionKey,
1221
1319
  defaultOncall: defaultOncall ?? { enabled: false, workingDir: '', since: 0 },
1222
1320
  defaultWorkingDir,
1223
1321
  autoboundChatCount: autoboundChats.length,
@@ -1242,6 +1340,7 @@ ipcRoute('GET', '/api/bot-default-oncall', async (_req, res) => {
1242
1340
  startupCommands,
1243
1341
  launchShell: getBot(cachedLarkAppId).config.launchShell ?? '',
1244
1342
  env,
1343
+ summaryRange: summaryRangeFromBotConfig(getBot(cachedLarkAppId).config),
1245
1344
  skills: getBot(cachedLarkAppId).config.skills ?? null,
1246
1345
  });
1247
1346
  });
@@ -1279,7 +1378,7 @@ ipcRoute('PUT', '/api/bot-card-prefs', async (req, res) => {
1279
1378
  if (m)
1280
1379
  patch.regularGroupReplyMode = m;
1281
1380
  }
1282
- if (body.regularGroupMentionMode === 'always' || body.regularGroupMentionMode === 'topic' || body.regularGroupMentionMode === 'never') {
1381
+ if (body.regularGroupMentionMode === 'always' || body.regularGroupMentionMode === 'topic' || body.regularGroupMentionMode === 'never' || body.regularGroupMentionMode === 'ambient') {
1283
1382
  patch.regularGroupMentionMode = body.regularGroupMentionMode;
1284
1383
  }
1285
1384
  if (body.docSubscribeDefaultMode === 'mention-only' || body.docSubscribeDefaultMode === 'all') {
@@ -1292,6 +1391,41 @@ ipcRoute('PUT', '/api/bot-card-prefs', async (req, res) => {
1292
1391
  return jsonRes(res, 400, { ok: false, error: r.reason });
1293
1392
  jsonRes(res, 200, { ok: true, ...r.prefs });
1294
1393
  });
1394
+ // Per-bot explicit `/summary` history range. Body `{ limit, sinceHours }`.
1395
+ ipcRoute('PUT', '/api/bot-summary-range', async (req, res) => {
1396
+ if (!cachedLarkAppId)
1397
+ return jsonRes(res, 503, { error: 'larkAppId_not_set' });
1398
+ let raw;
1399
+ try {
1400
+ raw = await readJsonBody(req);
1401
+ }
1402
+ catch {
1403
+ return jsonRes(res, 400, { ok: false, error: 'bad_json' });
1404
+ }
1405
+ const r = await updateDashboardSummaryRange(cachedLarkAppId, raw);
1406
+ if (!r.ok)
1407
+ return jsonRes(res, 400, { ok: false, error: r.reason });
1408
+ jsonRes(res, 200, { ok: true, summaryRange: r.summaryRange });
1409
+ });
1410
+ // Backward-compatible dashboard endpoint from the short-lived keyword-trigger UI.
1411
+ ipcRoute('PUT', '/api/bot-summary-trigger', async (req, res) => {
1412
+ if (!cachedLarkAppId)
1413
+ return jsonRes(res, 503, { error: 'larkAppId_not_set' });
1414
+ let raw;
1415
+ try {
1416
+ raw = await readJsonBody(req);
1417
+ }
1418
+ catch {
1419
+ return jsonRes(res, 400, { ok: false, error: 'bad_json' });
1420
+ }
1421
+ const body = raw && typeof raw === 'object' && !Array.isArray(raw)
1422
+ ? { limit: raw.limit, sinceHours: raw.sinceHours }
1423
+ : raw;
1424
+ const r = await updateDashboardSummaryRange(cachedLarkAppId, body);
1425
+ if (!r.ok)
1426
+ return jsonRes(res, 400, { ok: false, error: r.reason });
1427
+ jsonRes(res, 200, { ok: true, summaryRange: r.summaryRange });
1428
+ });
1295
1429
  // Per-bot 授权偏好。Body 任意子集:
1296
1430
  // • restrictGrantCommands: boolean — 限制被授权人只能纯对话
1297
1431
  // • autoGrantRequestCards: boolean — 未授权 @ 被挡住时是否发 grant 申请卡
@@ -1347,6 +1481,60 @@ ipcRoute('PUT', '/api/bot-brand-label', async (req, res) => {
1347
1481
  return jsonRes(res, 400, { ok: false, error: r.reason });
1348
1482
  jsonRes(res, 200, { ok: true, brandLabel: r.brandLabel });
1349
1483
  });
1484
+ // Per-bot agent launch settings. Body `{ cliId, model }` where `cliId` is the
1485
+ // dashboard selection key (plain adapter id or a wrapper option such as
1486
+ // `ttadk-x-codex`). Changes affect the next spawned CLI session.
1487
+ ipcRoute('PUT', '/api/bot-agent', async (req, res) => {
1488
+ if (!cachedLarkAppId)
1489
+ return jsonRes(res, 503, { error: 'larkAppId_not_set' });
1490
+ let body;
1491
+ try {
1492
+ body = await readJsonBody(req);
1493
+ }
1494
+ catch {
1495
+ return jsonRes(res, 400, { ok: false, error: 'bad_json' });
1496
+ }
1497
+ const key = typeof body.cliId === 'string' && body.cliId.trim() ? body.cliId.trim() : '';
1498
+ if (!key)
1499
+ return jsonRes(res, 400, { ok: false, error: 'cli_required' });
1500
+ let selected;
1501
+ try {
1502
+ selected = resolveCliSelection(key);
1503
+ }
1504
+ catch (err) {
1505
+ return jsonRes(res, 400, { ok: false, error: 'invalid_cli', message: err?.message ?? String(err) });
1506
+ }
1507
+ const model = typeof body.model === 'string' ? body.model.trim() : '';
1508
+ const r = await rmwBotEntry(cachedLarkAppId, (entry) => {
1509
+ entry.cliId = selected.cliId;
1510
+ if (selected.wrapperCli)
1511
+ entry.wrapperCli = selected.wrapperCli;
1512
+ else
1513
+ delete entry.wrapperCli;
1514
+ if (model)
1515
+ entry.model = model;
1516
+ else
1517
+ delete entry.model;
1518
+ return { write: true, result: null };
1519
+ });
1520
+ if (!r.ok)
1521
+ return jsonRes(res, 400, { ok: false, error: r.reason });
1522
+ const bot = getBot(cachedLarkAppId);
1523
+ bot.config.cliId = selected.cliId;
1524
+ if (selected.wrapperCli)
1525
+ bot.config.wrapperCli = selected.wrapperCli;
1526
+ else
1527
+ bot.config.wrapperCli = undefined;
1528
+ bot.config.model = model || undefined;
1529
+ const selectionKey = selectionKeyForBot(selected.cliId, selected.wrapperCli);
1530
+ jsonRes(res, 200, {
1531
+ ok: true,
1532
+ cliId: selected.cliId,
1533
+ wrapperCli: selected.wrapperCli ?? null,
1534
+ model: model || null,
1535
+ selectionKey,
1536
+ });
1537
+ });
1350
1538
  // Per-bot 私聊单聊模式 p2pMode。Body `{ p2pMode: 'chat' | 'thread' }`:
1351
1539
  // • 'chat' → 私聊走扁平连续 chat-scope 会话
1352
1540
  // • 'thread'(默认) → 清回每条 DM 独立 thread-scope 会话