Conversation
Return HTTP response from user-message endpoint before dispatching to bots. Previously, the handler awaited WebSocket ACK, allowing bots to call reactions.add before the UI stored the message.
Add support for assistant.threads.setStatus and setSuggestedPrompts API endpoints. Display bot status (e.g., "is thinking...") in the message panel with proper styling and auto-scroll. Status auto-clears when the bot sends a message.
📝 WalkthroughWalkthroughAdds assistant-status handling end-to-end: new state APIs to set/clear per-bot assistantStatus, SSE handling to receive status events and clear status on bot messages, UI rendering and auto-scroll for bots with status, and server endpoints to set thread status and suggested prompts. Changes
Sequence DiagramsequenceDiagram
participant External as External System
participant API as SlackWebAPI
participant Dispatcher as SSE Dispatcher
participant State as Bot State
participant UI as MessagePanel UI
External->>API: POST /api/assistant.threads.setStatus
API->>API: assistantThreadsSetStatus()
API-->>External: Immediate response
External->>Dispatcher: SSE event (assistant_thread_status, botId, status)
Dispatcher->>State: setAssistantStatus(botId, status)
State->>State: Update simulatorState.connectedBots[botId].assistantStatus
State-->>UI: Reactive state update
UI->>UI: derive botsWithStatus
UI->>UI: auto-scroll on new status
UI-->>UI: render bot status list
Dispatcher->>State: on bot message event -> clearAssistantStatus(botId)
State-->>UI: Reactive state update (status cleared)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
apps/ui/src/components/MessagePanel.svelte (1)
193-198: Auto-scroll effect may trigger more often than intended.This effect runs whenever
botsWithStatus.length > 0, which means it will scroll to bottom on every re-render where any bot has a status, not just when a status first appears. If the status text changes (e.g., from "is thinking..." to "is generating..."), this could cause unexpected scrolling.Consider tracking the previous length to only scroll when a new status appears:
♻️ Suggested refinement
+ let prevBotsWithStatusCount = 0 + // Auto-scroll when assistant status appears $effect(() => { - if (botsWithStatus.length > 0) { + const currentCount = botsWithStatus.length + if (currentCount > prevBotsWithStatusCount) { tick().then(() => requestAnimationFrame(scrollToBottom)) } + prevBotsWithStatusCount = currentCount })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/ui/src/components/MessagePanel.svelte` around lines 193 - 198, The $effect that auto-scrolls when botsWithStatus.length > 0 is firing on any re-render while a bot has status; change the $effect to only act when the count increases by tracking the previous count (e.g. a local variable like prevBotsWithStatusCount) and compare prevBotsWithStatusCount < botsWithStatus.length before calling tick().then(() => requestAnimationFrame(scrollToBottom)); after scrolling update prevBotsWithStatusCount = botsWithStatus.length so subsequent status text changes won't retrigger scrollToBottom unnecessarily.packages/slack/src/server/web-api.ts (1)
1046-1051: Consider usingundefinedinstead of empty string for cleared status.Line 1049 uses
status || ''which sends an empty string when status is falsy. However, the frontend'sclearAssistantStatussetsassistantStatus: undefined, and the dispatcher checksif (event.status)which treats empty string as falsy anyway. This works, but using explicitundefinedwould be more consistent:♻️ Suggested refinement for consistency
this.state.emitEvent({ type: 'assistant_thread_status', channel: channel_id, - status: status || '', + status: status || undefined, botId, })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/slack/src/server/web-api.ts` around lines 1046 - 1051, The emitEvent call currently sets status using status || '' which sends an empty string for falsy values; change this to send explicit undefined instead (e.g., replace status || '' with status || undefined or status ? status : undefined) in the this.state.emitEvent payload for the 'assistant_thread_status' event so cleared status matches the frontend's assistantStatus: undefined behavior; update the status property in the emitEvent call accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/ui/src/components/MessagePanel.svelte`:
- Around line 193-198: The $effect that auto-scrolls when botsWithStatus.length
> 0 is firing on any re-render while a bot has status; change the $effect to
only act when the count increases by tracking the previous count (e.g. a local
variable like prevBotsWithStatusCount) and compare prevBotsWithStatusCount <
botsWithStatus.length before calling tick().then(() =>
requestAnimationFrame(scrollToBottom)); after scrolling update
prevBotsWithStatusCount = botsWithStatus.length so subsequent status text
changes won't retrigger scrollToBottom unnecessarily.
In `@packages/slack/src/server/web-api.ts`:
- Around line 1046-1051: The emitEvent call currently sets status using status
|| '' which sends an empty string for falsy values; change this to send explicit
undefined instead (e.g., replace status || '' with status || undefined or status
? status : undefined) in the this.state.emitEvent payload for the
'assistant_thread_status' event so cleared status matches the frontend's
assistantStatus: undefined behavior; update the status property in the emitEvent
call accordingly.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
apps/ui/src/components/MessagePanel.svelteapps/ui/src/lib/dispatcher.svelte.tsapps/ui/src/lib/state.svelte.tsapps/ui/src/lib/state/bots.svelte.tsapps/ui/src/lib/types.tspackages/slack/src/server/types.tspackages/slack/src/server/web-api.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript code
No semicolons in code, enforced by Prettier
Use single quotes instead of double quotes
Use trailing commas in es5 format
Prefix unused variables with underscore (_)
Files:
packages/slack/src/server/types.tsapps/ui/src/lib/state.svelte.tsapps/ui/src/lib/types.tsapps/ui/src/lib/state/bots.svelte.tspackages/slack/src/server/web-api.tsapps/ui/src/lib/dispatcher.svelte.ts
packages/*/src/server/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
packages/*/src/server/*.ts: Platform emulator plugins must implement WebSocket protocol for event broadcasting to the frontend
Organize platform plugin packages withserver/state.tsfor in-memory state,server/web-api.tsfor API handlers, andserver/persistence.tsfor storage
Files:
packages/slack/src/server/types.tspackages/slack/src/server/web-api.ts
packages/slack/src/server/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use SSE (Server-Sent Events) for broadcasting events from the emulator to the frontend
Files:
packages/slack/src/server/types.tspackages/slack/src/server/web-api.ts
apps/ui/src/**/*.{ts,tsx,svelte}
📄 CodeRabbit inference engine (CLAUDE.md)
Use
$libpath alias when importing fromsrc/lib/in Svelte applications
Files:
apps/ui/src/lib/state.svelte.tsapps/ui/src/lib/types.tsapps/ui/src/lib/state/bots.svelte.tsapps/ui/src/components/MessagePanel.svelteapps/ui/src/lib/dispatcher.svelte.ts
**/*.svelte
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.svelte: Use{@html}in Svelte for mrkdwn rendering (ESLint rule disabled for this specific use case)
Use Svelte 5 runes ($state,$derived,$effect) for reactivity instead of legacy stores
Files:
apps/ui/src/components/MessagePanel.svelte
🧠 Learnings (4)
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/slack/src/server/**/*.ts : Use SSE (Server-Sent Events) for broadcasting events from the emulator to the frontend
Applied to files:
packages/slack/src/server/types.tspackages/slack/src/server/web-api.tsapps/ui/src/lib/dispatcher.svelte.ts
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/slack/src/server/socket-mode.ts : Slack API emulator must implement Socket Mode protocol for bot communication
Applied to files:
packages/slack/src/server/types.tspackages/slack/src/server/web-api.ts
📚 Learning: 2026-01-16T23:30:02.205Z
Learnt from: tyom
Repo: tyom/botarium PR: 2
File: packages/create-bot/templates/slack-bot/src/slack/handlers.ts.tmpl:99-110
Timestamp: 2026-01-16T23:30:02.205Z
Learning: In the Slack bot template's handler (`packages/create-bot/templates/slack-bot/src/slack/handlers.ts.tmpl`), the `hasBotParticipatedInThread` function is a fallback path that only runs after bot restarts. The in-memory `threadTracker` handles active threads. The function intentionally limits to 100 messages to avoid multiple API calls and rate limit pressure for the rare edge case of threads with 100+ messages, where the failure mode is minor (bot won't auto-respond until re-mentioned).
Applied to files:
packages/slack/src/server/web-api.ts
📚 Learning: 2026-01-14T21:36:07.933Z
Learnt from: tyom
Repo: tyom/botarium PR: 1
File: apps/ui/src/components/MessagePanel.svelte:10-23
Timestamp: 2026-01-14T21:36:07.933Z
Learning: In the client-only Botarium UI (Vite + Svelte), do not add or rely on SSR-specific guards (e.g., checks for sessionStorage) in components. Since the app runs only in Electron or the browser, code that accesses browser APIs will execute in a non-SSR environment, so SSR-unsafe checks are unnecessary and can be removed for maintainability.
Applied to files:
apps/ui/src/components/MessagePanel.svelte
🧬 Code graph analysis (3)
apps/ui/src/lib/state/bots.svelte.ts (1)
apps/ui/src/lib/state.svelte.ts (3)
setAssistantStatus(114-114)simulatorState(31-66)clearAssistantStatus(115-115)
packages/slack/src/server/web-api.ts (2)
packages/slack/src/lib/logger.ts (1)
webApiLogger(34-34)packages/slack/src/server/types.ts (1)
SimulatorUserMessageResponse(374-378)
apps/ui/src/lib/dispatcher.svelte.ts (1)
apps/ui/src/lib/state/bots.svelte.ts (2)
clearAssistantStatus(159-167)setAssistantStatus(148-156)
🔇 Additional comments (15)
packages/slack/src/server/types.ts (1)
396-414: LGTM!The new
assistant_thread_statusevent type and optionalstatusfield are well-integrated with the existingSimulatorEventtype structure. This properly supports SSE event broadcasting for assistant status updates as per the project's architecture.apps/ui/src/lib/types.ts (1)
586-586: LGTM!The optional
assistantStatusfield is a clean addition toConnectedBotInfo, properly typed and well-positioned within the interface.apps/ui/src/lib/state/bots.svelte.ts (2)
143-167: LGTM!The assistant status state management functions are well-implemented:
- Immutable update pattern with spread operator ensures proper reactivity with
SvelteMap.- Silent no-op when bot doesn't exist is appropriate for handling status updates from disconnected bots.
- The conditional check in
clearAssistantStatusavoids unnecessary map updates.
169-206: LGTM!The bot identity helpers are well-documented and handle both legacy (
U_BOT) and new multi-bot formats (U_{botId}) appropriately.apps/ui/src/lib/state.svelte.ts (1)
114-115: LGTM!Clean re-exports following the established barrel file pattern.
apps/ui/src/components/MessagePanel.svelte (2)
130-134: LGTM!Clean derived state using Svelte 5
$derivedrune to filter bots with active status.
301-333: LGTM!The status indicator UI is well-structured with proper conditional rendering for bot icons and consistent styling with the APP badge.
apps/ui/src/lib/dispatcher.svelte.ts (3)
23-24: LGTM!Imports added for the new assistant status management functions.
340-346: LGTM!Good implementation of auto-clearing assistant status when a bot sends a message, mirroring real Slack behaviour. The botId extraction correctly handles the
U_{botId}format.
466-475: LGTM!The
assistant_thread_statusevent handler correctly sets or clears status based on whetherevent.statusis truthy, aligning with the backend implementation.packages/slack/src/server/web-api.ts (5)
1326-1332: Excellent fix for the race condition.Returning the HTTP response immediately before dispatching to bots ensures the UI stores the message before any bot reactions arrive via SSE. The
voidoperator correctly handles the floating Promise.
1059-1069: Stub endpoint noted.The
assistantThreadsSetSuggestedPromptsis currently a no-op stub that returns success. If this is intentional for future implementation, consider adding a TODO comment for clarity.
1340-1393: LGTM!The asynchronous bot dispatch is well-structured with proper error handling that logs failures without blocking the response path. The app_mention logic correctly targets individual bots based on mentions.
505-520: LGTM!Good addition of warning logs for debugging reactions.add failures - helps diagnose issues when arguments are missing or messages aren't found.
264-274: LGTM!Clean routing additions for the new assistant API endpoints following the existing pattern.
Track previous bot status count so scrollToBottom only fires when new statuses appear, not on every re-render. Also send undefined instead of empty string for cleared assistant status to match frontend convention.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/slack/src/server/web-api.ts (1)
1059-1069: Consider adding input validation for consistency.This endpoint doesn't validate
channel_id, unlikeassistantThreadsSetStatusabove. If this is intentionally a stub (acknowledge-only), consider adding a brief comment to clarify. Otherwise, add validation for consistency:♻️ Suggested validation
private assistantThreadsSetSuggestedPrompts(body: { channel_id?: string thread_ts?: string prompts?: unknown[] title?: string }): Response { + // Stub: acknowledge the request without storing prompts + if (!body.channel_id) { + return Response.json( + { ok: false, error: 'missing_argument' }, + { headers: corsHeaders() } + ) + } + webApiLogger.debug( `assistant.threads.setSuggestedPrompts: ${body.title || '(no title)'}, ${body.prompts?.length ?? 0} prompt(s)` ) return Response.json({ ok: true }, { headers: corsHeaders() }) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/slack/src/server/web-api.ts` around lines 1059 - 1069, The assistantThreadsSetSuggestedPrompts handler lacks the same channel_id validation present in assistantThreadsSetStatus; either add the same input check or explicitly mark this function as an acknowledge-only stub with a comment. Locate assistantThreadsSetSuggestedPrompts and implement the same validation strategy used in assistantThreadsSetStatus: verify body.channel_id (and any other required fields like thread_ts) and return a JSON error Response with corsHeaders when missing/invalid, or add a one-line comment above the function stating it intentionally does no validation and is acknowledge-only so future reviewers know this is deliberate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/slack/src/server/web-api.ts`:
- Around line 1059-1069: The assistantThreadsSetSuggestedPrompts handler lacks
the same channel_id validation present in assistantThreadsSetStatus; either add
the same input check or explicitly mark this function as an acknowledge-only
stub with a comment. Locate assistantThreadsSetSuggestedPrompts and implement
the same validation strategy used in assistantThreadsSetStatus: verify
body.channel_id (and any other required fields like thread_ts) and return a JSON
error Response with corsHeaders when missing/invalid, or add a one-line comment
above the function stating it intentionally does no validation and is
acknowledge-only so future reviewers know this is deliberate.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/ui/src/components/MessagePanel.sveltepackages/slack/src/server/web-api.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/ui/src/components/MessagePanel.svelte
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript code
No semicolons in code, enforced by Prettier
Use single quotes instead of double quotes
Use trailing commas in es5 format
Prefix unused variables with underscore (_)
Files:
packages/slack/src/server/web-api.ts
packages/*/src/server/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
packages/*/src/server/*.ts: Platform emulator plugins must implement WebSocket protocol for event broadcasting to the frontend
Organize platform plugin packages withserver/state.tsfor in-memory state,server/web-api.tsfor API handlers, andserver/persistence.tsfor storage
Files:
packages/slack/src/server/web-api.ts
packages/slack/src/server/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use SSE (Server-Sent Events) for broadcasting events from the emulator to the frontend
Files:
packages/slack/src/server/web-api.ts
🧠 Learnings (3)
📚 Learning: 2026-01-16T23:30:02.205Z
Learnt from: tyom
Repo: tyom/botarium PR: 2
File: packages/create-bot/templates/slack-bot/src/slack/handlers.ts.tmpl:99-110
Timestamp: 2026-01-16T23:30:02.205Z
Learning: In the Slack bot template's handler (`packages/create-bot/templates/slack-bot/src/slack/handlers.ts.tmpl`), the `hasBotParticipatedInThread` function is a fallback path that only runs after bot restarts. The in-memory `threadTracker` handles active threads. The function intentionally limits to 100 messages to avoid multiple API calls and rate limit pressure for the rare edge case of threads with 100+ messages, where the failure mode is minor (bot won't auto-respond until re-mentioned).
Applied to files:
packages/slack/src/server/web-api.ts
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/slack/src/server/socket-mode.ts : Slack API emulator must implement Socket Mode protocol for bot communication
Applied to files:
packages/slack/src/server/web-api.ts
📚 Learning: 2026-02-20T00:31:35.696Z
Learnt from: CR
Repo: tyom/botarium PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-02-20T00:31:35.696Z
Learning: Applies to packages/slack/src/server/**/*.ts : Use SSE (Server-Sent Events) for broadcasting events from the emulator to the frontend
Applied to files:
packages/slack/src/server/web-api.ts
🧬 Code graph analysis (1)
packages/slack/src/server/web-api.ts (2)
packages/slack/src/lib/logger.ts (1)
webApiLogger(34-34)packages/slack/src/server/types.ts (1)
SimulatorUserMessageResponse(374-378)
🔇 Additional comments (3)
packages/slack/src/server/web-api.ts (3)
264-274: LGTM!The new Assistant API routes follow the existing routing pattern and correctly pass the token to
setStatusfor bot identification.
504-524: LGTM!Good debugging additions — logging the specific values helps troubleshoot reaction failures related to the race condition fix.
1326-1393: Good fix for the race condition.Returning the HTTP response immediately before dispatching to bots ensures the UI stores the message before any bot reactions arrive via SSE. The fire-and-forget pattern with internal
try/catchis appropriate here.
Details
assistant.threads.setStatusAPI, displaying bot status in the message panelThe reaction bug was caused by
handleSimulatorUserMessageawaiting the WebSocket bot dispatch before returning the HTTP response. This allowed bots to callreactions.addbefore the UI had stored the message, soaddReactionToMessagesilently failed on lookup. Fixed by returning the response immediately and dispatching to bots asynchronously.The assistant status feature adds a new
assistant_thread_statusSSE event type, state management for per-bot status, and a styled status indicator in the message panel that auto-clears when the bot sends a message.Summary by CodeRabbit