hammoc 1.7.1 → 1.8.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 (165) hide show
  1. package/README.md +8 -4
  2. package/package.json +1 -1
  3. package/packages/client/dist/assets/{agentExampleHighlight-Du5I7v6o.js → agentExampleHighlight-B2IJxYzN.js} +1 -1
  4. package/packages/client/dist/assets/{commandTokenHighlight-Dfo51Aof.js → commandTokenHighlight-BdGXkDR5.js} +1 -1
  5. package/packages/client/dist/assets/{index-4l4YwxEs.js → index-BFWGN51V.js} +1 -1
  6. package/packages/client/dist/assets/index-BX7y_Qt3.css +32 -0
  7. package/packages/client/dist/assets/{index-CqAHxPRw.js → index-lmx-m-iP.js} +587 -499
  8. package/packages/client/dist/assets/{snippetTokenHighlight-26DWljQA.js → snippetTokenHighlight-B9bH9Npx.js} +1 -1
  9. package/packages/client/dist/index.html +2 -2
  10. package/packages/client/dist/sw.js +1 -1
  11. package/packages/server/dist/app.d.ts.map +1 -1
  12. package/packages/server/dist/app.js +25 -1
  13. package/packages/server/dist/app.js.map +1 -1
  14. package/packages/server/dist/config/index.d.ts +10 -0
  15. package/packages/server/dist/config/index.d.ts.map +1 -1
  16. package/packages/server/dist/config/index.js +10 -0
  17. package/packages/server/dist/config/index.js.map +1 -1
  18. package/packages/server/dist/controllers/accountController.d.ts +19 -0
  19. package/packages/server/dist/controllers/accountController.d.ts.map +1 -0
  20. package/packages/server/dist/controllers/accountController.js +92 -0
  21. package/packages/server/dist/controllers/accountController.js.map +1 -0
  22. package/packages/server/dist/controllers/gitController.d.ts.map +1 -1
  23. package/packages/server/dist/controllers/gitController.js +37 -0
  24. package/packages/server/dist/controllers/gitController.js.map +1 -1
  25. package/packages/server/dist/controllers/projectController.d.ts +1 -1
  26. package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
  27. package/packages/server/dist/controllers/projectController.js +20 -4
  28. package/packages/server/dist/controllers/projectController.js.map +1 -1
  29. package/packages/server/dist/controllers/serverController.d.ts.map +1 -1
  30. package/packages/server/dist/controllers/serverController.js +8 -1
  31. package/packages/server/dist/controllers/serverController.js.map +1 -1
  32. package/packages/server/dist/controllers/sessionController.d.ts.map +1 -1
  33. package/packages/server/dist/controllers/sessionController.js +3 -1
  34. package/packages/server/dist/controllers/sessionController.js.map +1 -1
  35. package/packages/server/dist/handlers/websocket.d.ts +4 -0
  36. package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
  37. package/packages/server/dist/handlers/websocket.js +146 -8
  38. package/packages/server/dist/handlers/websocket.js.map +1 -1
  39. package/packages/server/dist/index.js +8 -0
  40. package/packages/server/dist/index.js.map +1 -1
  41. package/packages/server/dist/locales/en/server.json +2 -0
  42. package/packages/server/dist/locales/es/server.json +2 -0
  43. package/packages/server/dist/locales/ja/server.json +2 -0
  44. package/packages/server/dist/locales/ko/server.json +2 -0
  45. package/packages/server/dist/locales/pt/server.json +2 -0
  46. package/packages/server/dist/locales/zh-CN/server.json +2 -0
  47. package/packages/server/dist/middleware/session.d.ts.map +1 -1
  48. package/packages/server/dist/middleware/session.js +7 -5
  49. package/packages/server/dist/middleware/session.js.map +1 -1
  50. package/packages/server/dist/routes/accounts.d.ts +13 -0
  51. package/packages/server/dist/routes/accounts.d.ts.map +1 -0
  52. package/packages/server/dist/routes/accounts.js +18 -0
  53. package/packages/server/dist/routes/accounts.js.map +1 -0
  54. package/packages/server/dist/routes/config.d.ts +3 -0
  55. package/packages/server/dist/routes/config.d.ts.map +1 -0
  56. package/packages/server/dist/routes/config.js +59 -0
  57. package/packages/server/dist/routes/config.js.map +1 -0
  58. package/packages/server/dist/routes/preferences.d.ts.map +1 -1
  59. package/packages/server/dist/routes/preferences.js +22 -4
  60. package/packages/server/dist/routes/preferences.js.map +1 -1
  61. package/packages/server/dist/services/accountInfoService.js +6 -6
  62. package/packages/server/dist/services/accountInfoService.js.map +1 -1
  63. package/packages/server/dist/services/accountStorageService.d.ts +119 -0
  64. package/packages/server/dist/services/accountStorageService.d.ts.map +1 -0
  65. package/packages/server/dist/services/accountStorageService.js +285 -0
  66. package/packages/server/dist/services/accountStorageService.js.map +1 -0
  67. package/packages/server/dist/services/chatService.d.ts.map +1 -1
  68. package/packages/server/dist/services/chatService.js +4 -8
  69. package/packages/server/dist/services/chatService.js.map +1 -1
  70. package/packages/server/dist/services/claudeLoginService.d.ts +124 -0
  71. package/packages/server/dist/services/claudeLoginService.d.ts.map +1 -0
  72. package/packages/server/dist/services/claudeLoginService.js +417 -0
  73. package/packages/server/dist/services/claudeLoginService.js.map +1 -0
  74. package/packages/server/dist/services/cliChatEngine.d.ts.map +1 -1
  75. package/packages/server/dist/services/cliChatEngine.js +85 -33
  76. package/packages/server/dist/services/cliChatEngine.js.map +1 -1
  77. package/packages/server/dist/services/cliGridCards.d.ts +30 -10
  78. package/packages/server/dist/services/cliGridCards.d.ts.map +1 -1
  79. package/packages/server/dist/services/cliGridCards.js +80 -34
  80. package/packages/server/dist/services/cliGridCards.js.map +1 -1
  81. package/packages/server/dist/services/cliModalDetect.d.ts +15 -7
  82. package/packages/server/dist/services/cliModalDetect.d.ts.map +1 -1
  83. package/packages/server/dist/services/cliModalDetect.js +16 -12
  84. package/packages/server/dist/services/cliModalDetect.js.map +1 -1
  85. package/packages/server/dist/services/cliScreenModel.d.ts +3 -2
  86. package/packages/server/dist/services/cliScreenModel.d.ts.map +1 -1
  87. package/packages/server/dist/services/cliScreenModel.js +2 -0
  88. package/packages/server/dist/services/cliScreenModel.js.map +1 -1
  89. package/packages/server/dist/services/cliService.d.ts.map +1 -1
  90. package/packages/server/dist/services/cliService.js +2 -7
  91. package/packages/server/dist/services/cliService.js.map +1 -1
  92. package/packages/server/dist/services/cliSessionPool.d.ts +13 -1
  93. package/packages/server/dist/services/cliSessionPool.d.ts.map +1 -1
  94. package/packages/server/dist/services/cliSessionPool.js +14 -4
  95. package/packages/server/dist/services/cliSessionPool.js.map +1 -1
  96. package/packages/server/dist/services/dashboardService.d.ts.map +1 -1
  97. package/packages/server/dist/services/dashboardService.js +6 -1
  98. package/packages/server/dist/services/dashboardService.js.map +1 -1
  99. package/packages/server/dist/services/gitService.d.ts +2 -0
  100. package/packages/server/dist/services/gitService.d.ts.map +1 -1
  101. package/packages/server/dist/services/gitService.js +41 -3
  102. package/packages/server/dist/services/gitService.js.map +1 -1
  103. package/packages/server/dist/services/preferencesService.d.ts +1 -0
  104. package/packages/server/dist/services/preferencesService.d.ts.map +1 -1
  105. package/packages/server/dist/services/preferencesService.js +25 -2
  106. package/packages/server/dist/services/preferencesService.js.map +1 -1
  107. package/packages/server/dist/services/queueService.d.ts +1 -0
  108. package/packages/server/dist/services/queueService.d.ts.map +1 -1
  109. package/packages/server/dist/services/queueService.js +14 -3
  110. package/packages/server/dist/services/queueService.js.map +1 -1
  111. package/packages/server/dist/services/rateLimitProbeService.d.ts +7 -0
  112. package/packages/server/dist/services/rateLimitProbeService.d.ts.map +1 -1
  113. package/packages/server/dist/services/rateLimitProbeService.js +10 -0
  114. package/packages/server/dist/services/rateLimitProbeService.js.map +1 -1
  115. package/packages/server/dist/services/workspaceContext.d.ts +31 -4
  116. package/packages/server/dist/services/workspaceContext.d.ts.map +1 -1
  117. package/packages/server/dist/services/workspaceContext.js +110 -10
  118. package/packages/server/dist/services/workspaceContext.js.map +1 -1
  119. package/packages/server/dist/snippets/promote-to-story +86 -9
  120. package/packages/server/dist/utils/cliDebugLog.d.ts +6 -5
  121. package/packages/server/dist/utils/cliDebugLog.d.ts.map +1 -1
  122. package/packages/server/dist/utils/cliDebugLog.js +11 -8
  123. package/packages/server/dist/utils/cliDebugLog.js.map +1 -1
  124. package/packages/server/dist/utils/logger.d.ts +7 -0
  125. package/packages/server/dist/utils/logger.d.ts.map +1 -1
  126. package/packages/server/dist/utils/logger.js +13 -1
  127. package/packages/server/dist/utils/logger.js.map +1 -1
  128. package/packages/server/resources/bmad-method/4.44.3/LICENSE +30 -0
  129. package/packages/server/resources/manual/01-getting-started.md +11 -5
  130. package/packages/server/resources/manual/02-chat.md +2 -2
  131. package/packages/server/resources/manual/03-sessions.md +2 -0
  132. package/packages/server/resources/manual/04-slash-commands-favorites.md +1 -0
  133. package/packages/server/resources/manual/05-projects.md +2 -2
  134. package/packages/server/resources/manual/07-git.md +23 -2
  135. package/packages/server/resources/manual/08-terminal.md +24 -1
  136. package/packages/server/resources/manual/11-bmad-method-integration.md +1 -0
  137. package/packages/server/resources/manual/13-settings.md +52 -20
  138. package/packages/server/resources/manual/16-troubleshooting.md +4 -7
  139. package/packages/shared/dist/index.d.ts +2 -1
  140. package/packages/shared/dist/index.d.ts.map +1 -1
  141. package/packages/shared/dist/index.js +2 -0
  142. package/packages/shared/dist/index.js.map +1 -1
  143. package/packages/shared/dist/types/accounts.d.ts +104 -0
  144. package/packages/shared/dist/types/accounts.d.ts.map +1 -0
  145. package/packages/shared/dist/types/accounts.js +14 -0
  146. package/packages/shared/dist/types/accounts.js.map +1 -0
  147. package/packages/shared/dist/types/dashboard.d.ts +1 -0
  148. package/packages/shared/dist/types/dashboard.d.ts.map +1 -1
  149. package/packages/shared/dist/types/git.d.ts +12 -0
  150. package/packages/shared/dist/types/git.d.ts.map +1 -1
  151. package/packages/shared/dist/types/git.js +10 -0
  152. package/packages/shared/dist/types/git.js.map +1 -1
  153. package/packages/shared/dist/types/preferences.d.ts +40 -0
  154. package/packages/shared/dist/types/preferences.d.ts.map +1 -1
  155. package/packages/shared/dist/types/preferences.js.map +1 -1
  156. package/packages/shared/dist/types/sdk.d.ts +5 -1
  157. package/packages/shared/dist/types/sdk.d.ts.map +1 -1
  158. package/packages/shared/dist/types/sdk.js.map +1 -1
  159. package/packages/shared/dist/types/session.d.ts +1 -0
  160. package/packages/shared/dist/types/session.d.ts.map +1 -1
  161. package/packages/shared/dist/types/session.js.map +1 -1
  162. package/packages/shared/dist/types/websocket.d.ts +28 -1
  163. package/packages/shared/dist/types/websocket.d.ts.map +1 -1
  164. package/packages/client/dist/assets/index-CVy582lU.css +0 -32
  165. package/packages/server/dist/snippets/promote-issue +0 -9
@@ -39,6 +39,9 @@ import { buildRawMessageTree, getActiveRawBranch, getDefaultRawBranchSelections,
39
39
  import { sessionBufferManager } from '../services/sessionBufferManager.js';
40
40
  import { imageStorageService } from '../services/imageStorageService.js';
41
41
  import { isSnippetRef, resolveSnippet, listSnippets, SnippetError } from '../utils/snippetResolver.js';
42
+ import { ClaudeLoginSession, deleteCredentials } from '../services/claudeLoginService.js';
43
+ import { accountInfoService } from '../services/accountInfoService.js';
44
+ import { accountStorageService } from '../services/accountStorageService.js';
42
45
  const log = createLogger('websocket');
43
46
  // Alias for concise usage in guards
44
47
  const queueInstances = getQueueInstances;
@@ -46,6 +49,8 @@ const queueInstances = getQueueInstances;
46
49
  const warnedMissing1M = new Set();
47
50
  // Socket-to-terminal mapping: socket.id → Set of terminalIds (Story 17.1)
48
51
  const socketTerminals = new Map();
52
+ // Story BS-7: one in-flight Claude Code login session per socket (disposable PTY).
53
+ const socketLoginSessions = new Map();
49
54
  // Story 20.1: Session-to-project mapping for dashboard triggers
50
55
  const sessionProjectMap = new Map();
51
56
  // Story 20.1: Per-project debounced dashboard status change broadcaster
@@ -456,6 +461,38 @@ export function isSessionStreaming(sessionId) {
456
461
  const stream = activeStreams.get(sessionId);
457
462
  return !!stream && stream.status === 'running';
458
463
  }
464
+ /** Check if a session has any pending AskUserQuestion interactions */
465
+ export function sessionHasPendingQuestion(sessionId) {
466
+ const stream = activeStreams.get(sessionId);
467
+ if (!stream)
468
+ return false;
469
+ for (const p of stream.pendingPermissions.values()) {
470
+ if (p.interactionType === 'question')
471
+ return true;
472
+ }
473
+ return false;
474
+ }
475
+ /** Get pending AskUserQuestion counts grouped by project slug (in-memory, no I/O) */
476
+ export function getPendingQuestionCountsByProject() {
477
+ const counts = new Map();
478
+ for (const [sessionId, stream] of activeStreams.entries()) {
479
+ if (stream.status !== 'running')
480
+ continue;
481
+ let hasQuestion = false;
482
+ for (const p of stream.pendingPermissions.values()) {
483
+ if (p.interactionType === 'question') {
484
+ hasQuestion = true;
485
+ break;
486
+ }
487
+ }
488
+ if (hasQuestion) {
489
+ const slug = sessionProjectMap.get(sessionId);
490
+ if (slug)
491
+ counts.set(slug, (counts.get(slug) ?? 0) + 1);
492
+ }
493
+ }
494
+ return counts;
495
+ }
459
496
  /** Get session IDs that have active socket connections for a given project */
460
497
  export function getJoinedSessionIdsByProject(projectSlug) {
461
498
  const sessionIds = new Set();
@@ -566,6 +603,15 @@ function cleanupStream(streamKey, expectedStream) {
566
603
  // Only delete from activeStreams and clean up socket mappings if the stream
567
604
  // hasn't been replaced. When replaced, the new stream owns those resources.
568
605
  if (!replaced) {
606
+ // Clear pending question state before removing the stream
607
+ if (current) {
608
+ for (const p of current.pendingPermissions.values()) {
609
+ if (p.interactionType === 'question') {
610
+ emitQuestionChange(streamKey, false);
611
+ break;
612
+ }
613
+ }
614
+ }
569
615
  activeStreams.delete(streamKey);
570
616
  for (const [sockId, sessId] of socketToSession.entries()) {
571
617
  if (sessId === streamKey)
@@ -719,6 +765,17 @@ function emitStreamChange(sessionId, active, projectSlug) {
719
765
  }
720
766
  }
721
767
  }
768
+ /**
769
+ * Emit session:question-change when an AskUserQuestion becomes pending or is resolved.
770
+ * Scoped to the project room so only relevant session list viewers receive it.
771
+ */
772
+ function emitQuestionChange(sessionId, hasPendingQuestion) {
773
+ const projectSlug = sessionProjectMap.get(sessionId);
774
+ if (projectSlug) {
775
+ io.to(`project:${projectSlug}`).emit('session:question-change', { sessionId, hasPendingQuestion, projectSlug });
776
+ triggerDashboardStatusChange(projectSlug);
777
+ }
778
+ }
722
779
  /**
723
780
  * Broadcast session:stream-change to project room (or all clients as fallback).
724
781
  * Used by queue service to signal stream start/end.
@@ -787,11 +844,13 @@ export async function initializeWebSocket(httpServer) {
787
844
  });
788
845
  // Session middleware for WebSocket (Story 2.5 - Task 4)
789
846
  const sessionMiddleware = await createSessionMiddleware();
790
- // Parse session from cookie for Socket.io connections
847
+ // Parse session from cookie for Socket.io connections.
848
+ // cookie-session + on-headers expect a response-like object; a real
849
+ // ServerResponse would try to send headers over the upgraded WebSocket.
791
850
  io.use((socket, next) => {
792
- // Express middleware adapter for Socket.io
793
851
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
794
- sessionMiddleware(socket.request, {}, next);
852
+ const res = { getHeader() { }, setHeader() { }, writeHead() { } };
853
+ sessionMiddleware(socket.request, res, next);
795
854
  });
796
855
  // Authentication middleware (Story 2.5 - Task 4)
797
856
  io.use((socket, next) => {
@@ -1032,9 +1091,15 @@ export async function initializeWebSocket(httpServer) {
1032
1091
  if (!sessionId)
1033
1092
  return;
1034
1093
  const stream = activeStreams.get(sessionId);
1035
- if (stream?.pendingPermissions.has(data.requestId)) {
1036
- stream.pendingPermissions.get(data.requestId).resolve({ approved: data.approved, response: data.response });
1094
+ if (!stream)
1095
+ return;
1096
+ const pending = stream.pendingPermissions.get(data.requestId);
1097
+ if (pending) {
1098
+ const wasQuestion = pending.interactionType === 'question';
1099
+ pending.resolve({ approved: data.approved, response: data.response });
1037
1100
  stream.pendingPermissions.delete(data.requestId);
1101
+ if (wasQuestion)
1102
+ emitQuestionChange(sessionId, sessionHasPendingQuestion(sessionId));
1038
1103
  // Broadcast the actual resolution to all OTHER viewers so their
1039
1104
  // tool/interactive cards can show the correct approve/deny state.
1040
1105
  // Also buffer via createStreamEmit so reconnecting clients see the
@@ -2146,8 +2211,67 @@ export async function initializeWebSocket(httpServer) {
2146
2211
  // Ignore close errors — session may already be gone
2147
2212
  }
2148
2213
  });
2214
+ // Story BS-7: Claude Code in-app login flow over a disposable PTY.
2215
+ // auth:start spawns the login session; the session drives boot → /login and reports each
2216
+ // phase back through these socket emits. One session per socket (a re-start disposes the prior).
2217
+ socket.on('auth:start', async () => {
2218
+ const prior = socketLoginSessions.get(socket.id);
2219
+ if (prior) {
2220
+ prior.dispose();
2221
+ socketLoginSessions.delete(socket.id);
2222
+ }
2223
+ // The login PTY uses the same binary-path override as chat (global preference).
2224
+ let cliBinaryPath;
2225
+ try {
2226
+ cliBinaryPath = (await preferencesService.readPreferences()).cliBinaryPath;
2227
+ }
2228
+ catch {
2229
+ cliBinaryPath = undefined;
2230
+ }
2231
+ const session = new ClaudeLoginSession({
2232
+ onMethodPrompt: () => socket.emit('auth:method-prompt'),
2233
+ onUrl: (url) => socket.emit('auth:url', { url }),
2234
+ onCodePrompt: () => socket.emit('auth:code-prompt'),
2235
+ onComplete: (account) => {
2236
+ socket.emit('auth:complete', { account });
2237
+ socketLoginSessions.delete(socket.id);
2238
+ // Story BS-8: accumulate the just-written credential into the multi-account store.
2239
+ // Login already made this account active, so the credentials file holds its credential
2240
+ // and accountInfoService supplied the email (or null → fallback key, AC1a). Fire-and-forget.
2241
+ accountStorageService.captureActiveCredential(account?.email ?? null).catch((err) => {
2242
+ log.warn(`BS-8: failed to capture credential after login: ${err instanceof Error ? err.message : String(err)}`);
2243
+ });
2244
+ },
2245
+ onError: (message) => {
2246
+ socket.emit('auth:error', { message });
2247
+ socketLoginSessions.delete(socket.id);
2248
+ },
2249
+ });
2250
+ socketLoginSessions.set(socket.id, session);
2251
+ session.start(cliBinaryPath);
2252
+ });
2253
+ socket.on('auth:select-method', (data) => {
2254
+ socketLoginSessions.get(socket.id)?.selectMethod(data.method);
2255
+ });
2256
+ socket.on('auth:submit-code', (data) => {
2257
+ socketLoginSessions.get(socket.id)?.submitCode(data.code);
2258
+ });
2259
+ socket.on('auth:logout', async () => {
2260
+ const ok = deleteCredentials();
2261
+ // Re-read usage credentials freshly (the deleted file → no token) so probing reflects logout.
2262
+ rateLimitProbeService.invalidateTokenCache();
2263
+ if (!ok) {
2264
+ socket.emit('auth:error', { message: 'Failed to delete credentials' });
2265
+ return;
2266
+ }
2267
+ // Refresh the in-memory account cache so a subsequent /account read reflects the logout.
2268
+ // The client transitions to the inline login flow optimistically (no success event needed).
2269
+ accountInfoService.refresh().catch(() => {
2270
+ /* best-effort — the file is gone regardless */
2271
+ });
2272
+ });
2149
2273
  // Disconnect: detach socket from stream, DON'T abort or deny permissions
2150
- socket.on('disconnect', () => {
2274
+ socket.on('disconnect', (reason) => {
2151
2275
  const sessionId = socketToSession.get(socket.id);
2152
2276
  if (sessionId) {
2153
2277
  const stream = activeStreams.get(sessionId);
@@ -2216,8 +2340,15 @@ export async function initializeWebSocket(httpServer) {
2216
2340
  if (terminalIds) {
2217
2341
  socketTerminals.delete(socket.id);
2218
2342
  }
2343
+ // Story BS-7: dispose any in-flight login PTY for this socket (the disposable login session
2344
+ // must not outlive its only client — it has no background-completion semantics like chat).
2345
+ const loginSession = socketLoginSessions.get(socket.id);
2346
+ if (loginSession) {
2347
+ loginSession.dispose();
2348
+ socketLoginSessions.delete(socket.id);
2349
+ }
2219
2350
  connectedClients--;
2220
- log.info(`Client disconnected. Total: ${connectedClients}`);
2351
+ log.info(`Client disconnected (reason=${reason}). Total: ${connectedClients}`);
2221
2352
  // Stop polling when no clients connected
2222
2353
  if (connectedClients === 0) {
2223
2354
  rateLimitProbeService.stopPolling();
@@ -2515,7 +2646,10 @@ async function handleChatSend(stream, data, abortController, lang) {
2515
2646
  // Story 33.3: resolve the effective engine mode (override > global > 'sdk' default).
2516
2647
  // Captured once into a local so the progress-callback gating below reuses the same
2517
2648
  // decision (no second resolve, and `engineMode === 'cli'` stays consistent).
2518
- const engineMode = await projectService.getEffectiveEngineMode(workingDirectory);
2649
+ const [engineMode, isBmadProject] = await Promise.all([
2650
+ projectService.getEffectiveEngineMode(workingDirectory),
2651
+ projectService.checkBmadProject(workingDirectory),
2652
+ ]);
2519
2653
  // Auto-compaction master switch (both engines). Default ON. Gates the engine-internal
2520
2654
  // auto-compact (forwarded into each engine's settings) AND Hammoc's overflow-triggered
2521
2655
  // /compact recovery below, so one toggle controls all auto-compaction.
@@ -2547,6 +2681,8 @@ async function handleChatSend(stream, data, abortController, lang) {
2547
2681
  : { images }),
2548
2682
  // Advanced settings from preferences
2549
2683
  customSystemPrompt: effectivePrefs.customSystemPrompt,
2684
+ displayName: effectivePrefs.displayName,
2685
+ isBmadProject,
2550
2686
  // maxThinkingTokens: legacy path; SDK docs say it takes precedence for backward-compat,
2551
2687
  // which conflicts with adaptive mode on Opus 4.7+ (rejects enabled+budget). Skip on
2552
2688
  // adaptive-capable models so the explicit `thinking` field governs.
@@ -2623,6 +2759,8 @@ async function handleChatSend(stream, data, abortController, lang) {
2623
2759
  resolve,
2624
2760
  interactionType: isAskUserQuestion ? 'question' : 'permission',
2625
2761
  });
2762
+ if (isAskUserQuestion)
2763
+ emitQuestionChange(sessionIdRef.current || '', true);
2626
2764
  });
2627
2765
  if (isAskUserQuestion) {
2628
2766
  const questions = input.questions;