Skip to content

Improve session chat display and desktop workspace links#420

Merged
lefarcen merged 5 commits intomainfrom
fix/session-chat-ui-cleanup
Mar 23, 2026
Merged

Improve session chat display and desktop workspace links#420
lefarcen merged 5 commits intomainfrom
fix/session-chat-ui-cleanup

Conversation

@anthhub
Copy link
Copy Markdown

@anthhub anthhub commented Mar 23, 2026

Relates to #512

Summary

  • return the active OpenClaw agent workspace from /api/internal/desktop/ready and prefer controller shell-open for local file links with a desktop bridge fallback
  • redesign the sessions chat transcript into a centered reading column with compact assistant tool execution chips
  • add regression coverage for desktop link handling and session transcript rendering

Verification

  • pnpm typecheck
  • pnpm lint
  • pnpm test

Summary by CodeRabbit

  • New Features

    • Inline tool-activity cards with formatted summaries, reply-context cards, and updated chat layout/alignment.
    • Workspace detection now prefers agent-specific workspace paths when a preferred bot exists.
  • Bug Fixes

    • Opening local folders prefers controller handling for file:// links with robust fallback to the desktop bridge.
    • Assistant reply markers and channel-specific noise are stripped; reply context is extracted and shown as quote UI.
  • Tests

    • Expanded tests for workspace detection, folder-opening behavior, chat sanitization, reply context, and tool-card rendering.
  • Localization

    • Added English and Chinese strings for tool activity, completion, and reply label.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

Shell-open now prefers the controller API and derives allowed/workspace roots from container.env.openclawStateDir; desktop "ready" selects workspace from configStore bots. Chat ingestion and UI were normalized (assistant reply prefix stripping, reply-context extraction, tool-call summaries), i18n keys added, and tests expanded across controller and web.

Changes

Cohort / File(s) Summary
Controller routes
apps/controller/src/routes/desktop-routes.ts
Shell-open allowlist uses container.env.openclawStateDir; desktop "ready" chooses workspacePath based on container.configStore.listBots() and preferred bot selection.
Sessions runtime
apps/controller/src/runtime/sessions-runtime.ts
Normalized transcript ingestion: normalizeChatMessage, assistant [[reply_to_current]] stripping, user reply-context extraction, Feishu/WeChat channel-specific suffix stripping, tool-call preservation; readMessages accepts channelType.
Controller tests
apps/controller/tests/route-compat.test.ts, apps/controller/tests/session-routes.test.ts, apps/controller/tests/sessions-runtime.test.ts
Added/updated tests for desktop route compatibility, messages API, and extensive transcript normalization scenarios (Feishu/WeChat, tool calls, message filtering).
Web desktop links & tests
apps/web/src/lib/desktop-links.ts, apps/web/tests/desktop-links.test.ts
openLocalFolderUrl prefers POSTing decoded file:// paths to /api/internal/desktop/shell-open, handles non-OK/errors and falls back to host bridge; tests cover success, controller-failure fallback, and non-file URLs.
Sessions UI & tests
apps/web/src/pages/sessions.tsx, apps/web/tests/sessions.test.tsx
Added assistant prefix stripping, reply-context card, formatToolCallSummary(), tool-completion UI changes (inline chip, data-* attributes), chat layout/filter updates; tests assert rendering, attributes, and i18n.
Localization
apps/web/src/i18n/locales/en.ts, apps/web/src/i18n/locales/zh-CN.ts
Added sessions.chat.replyLabel, sessions.chat.toolActivity, and sessions.chat.toolCompleted translation keys.

Sequence Diagram(s)

sequenceDiagram
    participant Web as Web Client
    participant Ctrl as Controller
    participant Bridge as Desktop Bridge

    Web->>Web: user clicks folder link (file://)
    Web->>Web: decode -> folderPath?
    alt folderPath decoded
        Web->>Ctrl: POST /api/internal/desktop/shell-open {"path": folderPath}
        alt response.ok
            Ctrl-->>Web: 200 OK
            Web->>Web: return (no bridge invoked)
        else fetch error or non-OK
            Ctrl-->>Web: error/non-OK
            Web->>Bridge: invoke('shell:open-external', original URL)
            Bridge-->>Web: ack
        end
    else non-file or cannot decode
        Web->>Bridge: invoke('shell:open-external', original URL)
        Bridge-->>Web: ack
    end
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • lefarcen

Poem

🐰 I hopped through code with nimble paws,

Paths now route from tidy laws.
Bots chose homes and replies unwind,
Tool chips bloom and contexts find.
A carrot-click for cleaner chat!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: improving session chat display and desktop workspace links, which align with the extensive modifications across multiple files.
Description check ✅ Passed The description covers the key objectives (workspace endpoint, shell-open preference, chat redesign, tests), verification steps, and includes implementation approach details sufficient for reviewers.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/session-chat-ui-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/pages/sessions.tsx (1)

426-442: ⚠️ Potential issue | 🟠 Major

Handle tool-only assistant messages end-to-end.

Pure toolCall / tool_use frames still never render correctly here: the transcript filter removes them because text is empty, and even if you keep them, ChatBubble always emits an empty message bubble before the chip. That means a valid agent transcript shape either disappears entirely or shows blank chrome.

Suggested fix
-        <div
-          className={cn(
-            "inline-block max-w-full rounded-[20px] px-4 py-3 text-[13px] break-words shadow-[0_10px_24px_rgba(15,23,42,0.04)]",
-            isBot
-              ? "border border-border bg-surface-1 text-text-primary rounded-tl-sm"
-              : "bg-surface-3 text-text-primary rounded-tr-sm",
-          )}
-        >
-          <ChatMarkdown content={text} />
-        </div>
+        {text.trim().length > 0 && (
+          <div
+            className={cn(
+              "inline-block max-w-full rounded-[20px] px-4 py-3 text-[13px] break-words shadow-[0_10px_24px_rgba(15,23,42,0.04)]",
+              isBot
+                ? "border border-border bg-surface-1 text-text-primary rounded-tl-sm"
+                : "bg-surface-3 text-text-primary rounded-tr-sm",
+            )}
+          >
+            <ChatMarkdown content={text} />
+          </div>
+        )}
@@
               {messages
                 .filter((msg) => {
-                  const { text } = extractMessage(
+                  const { text, hasToolCall } = extractMessage(
                     msg as unknown as Record<string, unknown>,
                   );
-                  return text.trim().length > 0;
+                  return text.trim().length > 0 || hasToolCall;
                 })
                 .map((msg) => (

Also applies to: 670-684

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/pages/sessions.tsx` around lines 426 - 442, The UI currently
renders an empty message bubble for agent tool-only frames because the
ChatMarkdown block is always emitted even when text === "" and the transcript
filter drops those frames; update the rendering in this component so that if
hasToolCall is true and text is empty you skip emitting the ChatMarkdown message
div (do not render the rounded message block) and instead render the
ArtifactCard (using toolCallSummary) in its place while preserving the
surrounding layout and isBot alignment; reference ChatMarkdown, ArtifactCard,
and the local flags isBot, hasToolCall, text, and toolCallSummary to locate and
change the conditional rendering logic so tool-only assistant messages show only
the chip/card and no empty bubble.
🧹 Nitpick comments (3)
apps/web/src/lib/desktop-links.ts (1)

90-108: Consider using the generated SDK instead of raw fetch.

The coding guidelines state that frontend code must use the generated SDK from apps/web/lib/api/ rather than raw fetch. If a client for /api/internal/desktop/shell-open doesn't exist yet, consider generating one or adding this endpoint to the SDK.

Additionally, the current logic falls back to the host bridge when the controller returns a non-OK response (e.g., 403 for disallowed paths). This means a security restriction from the controller is silently bypassed via the host bridge. If this is intentional (to support paths outside the allowlist on the desktop), consider adding a comment clarifying the design decision.

As per coding guidelines: "Frontend must use generated SDK from apps/web/lib/api/, never raw fetch"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/desktop-links.ts` around lines 90 - 108, Replace the raw
fetch call to "/api/internal/desktop/shell-open" with the generated SDK client
from apps/web/lib/api (use the SDK method that maps to this POST endpoint)
instead of fetch; keep the same JSON payload ({ path: folderPath }) and await
the SDK response. After calling the SDK, explicitly handle non-OK responses: do
not silently fall back to hostBridge.invoke("shell:open-external", { url }) when
the controller returns a non-OK status (e.g., 403); either return/throw the
error or add a clear comment above the hostBridge fallback that this bypass is
intentional and why. Ensure you reference and update the code paths that use
folderPath, hostBridge, and url so the SDK call replaces the existing fetch and
the fallback behavior is made explicit.
apps/controller/src/routes/desktop-routes.ts (1)

101-112: Redundant allowlist check – allowedWorkspaceRoot is already inside allowedRoot.

Since allowedWorkspaceRoot is defined as path.join(container.env.openclawStateDir, "agents"), any path that passes the workspace root check will also pass the allowedRoot check. The workspace-specific conditions at lines 110-111 are always subsumed by lines 108-109.

If the intent is to restrict shell-open to only the agents subdirectory (not the broader state dir), consider using only allowedWorkspaceRoot. If both should be allowed, the current code works but the workspace checks can be removed for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/controller/src/routes/desktop-routes.ts` around lines 101 - 112, The
checks are redundant because allowedWorkspaceRoot
(path.join(container.env.openclawStateDir, "agents")) is inside allowedRoot;
either remove the workspace-specific conditions (the
resolved.startsWith(allowedWorkspaceRoot + path.sep) and resolved ===
allowedWorkspaceRoot checks) to keep the broader allowlist, or if you intend to
restrict to only the agents subdirectory, change allowedRoot to point to
allowedWorkspaceRoot and remove the separate allowedWorkspaceRoot variable and
checks; update the conditional that checks resolved accordingly (references:
allowedRoot, allowedWorkspaceRoot, resolved, container.env.openclawStateDir).
apps/controller/tests/route-compat.test.ts (1)

294-309: Consider adding a test for the no-bots fallback path.

The test covers the happy path where a bot exists. To fully exercise the updated /api/internal/desktop/ready endpoint, consider adding a second test case that omits the createBot call and asserts workspacePath equals path.join(rootDir, ".openclaw", "agents").

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/controller/tests/route-compat.test.ts` around lines 294 - 309, Add a new
test for the no-bots fallback of the /api/internal/desktop/ready endpoint: copy
the existing it(...) block but do NOT call container.configStore.createBot;
create the app with createApp(container), request "/api/internal/desktop/ready",
assert status 200, parse payload and assert payload.workspacePath ===
path.join(rootDir, ".openclaw", "agents") so the handler returns the default
agents directory when no bot exists (use the same test utilities and names as
the existing test for consistency).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/pages/sessions.tsx`:
- Around line 154-183: formatToolCallSummary currently returns empty strings or
"tool" for separator-only or unnamed tool blocks, preventing ArtifactCard from
falling back to sessions.chat.toolActivity; update formatToolCallSummary to
normalize these cases by returning null when the input yields only separators or
when the computed summary equals (case-insensitive) "tool" or is empty after
trimming. Locate formatToolCallSummary and ensure its logic checks for
separator-only inputs and placeholder tokens (e.g., "tool") and returns null in
those cases; apply the same change to the other instance of this logic mentioned
in the review (the similar block around the second occurrence).

---

Outside diff comments:
In `@apps/web/src/pages/sessions.tsx`:
- Around line 426-442: The UI currently renders an empty message bubble for
agent tool-only frames because the ChatMarkdown block is always emitted even
when text === "" and the transcript filter drops those frames; update the
rendering in this component so that if hasToolCall is true and text is empty you
skip emitting the ChatMarkdown message div (do not render the rounded message
block) and instead render the ArtifactCard (using toolCallSummary) in its place
while preserving the surrounding layout and isBot alignment; reference
ChatMarkdown, ArtifactCard, and the local flags isBot, hasToolCall, text, and
toolCallSummary to locate and change the conditional rendering logic so
tool-only assistant messages show only the chip/card and no empty bubble.

---

Nitpick comments:
In `@apps/controller/src/routes/desktop-routes.ts`:
- Around line 101-112: The checks are redundant because allowedWorkspaceRoot
(path.join(container.env.openclawStateDir, "agents")) is inside allowedRoot;
either remove the workspace-specific conditions (the
resolved.startsWith(allowedWorkspaceRoot + path.sep) and resolved ===
allowedWorkspaceRoot checks) to keep the broader allowlist, or if you intend to
restrict to only the agents subdirectory, change allowedRoot to point to
allowedWorkspaceRoot and remove the separate allowedWorkspaceRoot variable and
checks; update the conditional that checks resolved accordingly (references:
allowedRoot, allowedWorkspaceRoot, resolved, container.env.openclawStateDir).

In `@apps/controller/tests/route-compat.test.ts`:
- Around line 294-309: Add a new test for the no-bots fallback of the
/api/internal/desktop/ready endpoint: copy the existing it(...) block but do NOT
call container.configStore.createBot; create the app with createApp(container),
request "/api/internal/desktop/ready", assert status 200, parse payload and
assert payload.workspacePath === path.join(rootDir, ".openclaw", "agents") so
the handler returns the default agents directory when no bot exists (use the
same test utilities and names as the existing test for consistency).

In `@apps/web/src/lib/desktop-links.ts`:
- Around line 90-108: Replace the raw fetch call to
"/api/internal/desktop/shell-open" with the generated SDK client from
apps/web/lib/api (use the SDK method that maps to this POST endpoint) instead of
fetch; keep the same JSON payload ({ path: folderPath }) and await the SDK
response. After calling the SDK, explicitly handle non-OK responses: do not
silently fall back to hostBridge.invoke("shell:open-external", { url }) when the
controller returns a non-OK status (e.g., 403); either return/throw the error or
add a clear comment above the hostBridge fallback that this bypass is
intentional and why. Ensure you reference and update the code paths that use
folderPath, hostBridge, and url so the SDK call replaces the existing fetch and
the fallback behavior is made explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aea0a524-5e1a-43fb-87f7-c30c9efecb23

📥 Commits

Reviewing files that changed from the base of the PR and between 458c3dc and 55bfcda.

📒 Files selected for processing (8)
  • apps/controller/src/routes/desktop-routes.ts
  • apps/controller/tests/route-compat.test.ts
  • apps/web/src/i18n/locales/en.ts
  • apps/web/src/i18n/locales/zh-CN.ts
  • apps/web/src/lib/desktop-links.ts
  • apps/web/src/pages/sessions.tsx
  • apps/web/tests/desktop-links.test.ts
  • apps/web/tests/sessions.test.tsx

Comment thread apps/web/src/pages/sessions.tsx
lefarcen
lefarcen previously approved these changes Mar 23, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
apps/controller/src/runtime/sessions-runtime.ts (2)

416-425: Clarify intent: unknown block types are pushed but don't contribute to visibility.

Lines 422-423 push unknown block types to normalizedBlocks but don't set hasVisibleContent = true. This means a message containing only unknown block types returns null, even though those blocks are collected.

If this is intentional (only text and tool blocks count as "visible"), consider adding a brief comment. If unknown blocks should also count as visible, add hasVisibleContent = true after line 422.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/controller/src/runtime/sessions-runtime.ts` around lines 416 - 425, The
loop currently pushes blocks with unknown blockType into normalizedBlocks but
doesn't set hasVisibleContent, causing a collection of only-unknown blocks to
return null; decide whether unknown blocks should count as visible and either
(A) add hasVisibleContent = true immediately after the
normalizedBlocks.push(block) for unknown types so they mark the message as
visible, or (B) leave behavior and add an inline comment near the
normalizedBlocks.push(block) explaining that only "text" and tool types are
considered visible and unknown types are intentionally excluded from
hasVisibleContent; refer to blockType, normalizedBlocks, and hasVisibleContent
in your change.

483-496: Consider case-insensitive comparison for channelType.

The comparison channelType !== "feishu" is case-sensitive. While channelTypeSchema enforces lowercase "feishu" for channels, createSessionSchema accepts any string for channelType (as noted in the relevant code snippets). If a session somehow has "Feishu" or "FEISHU", the Feishu-specific stripping won't apply.

Since all current Feishu channels use lowercase (per connectFeishu()), this is a minor robustness concern rather than an active bug.

🛡️ Optional: normalize channelType comparison
   private stripChannelSystemSuffix(
     text: string,
     channelType?: string | null,
   ): string {
-    if (channelType !== "feishu") {
+    if (channelType?.toLowerCase() !== "feishu") {
       return text;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/controller/src/runtime/sessions-runtime.ts` around lines 483 - 496, In
stripChannelSystemSuffix, the channelType comparison is case-sensitive; update
the check to handle case-insensitive values (and null/undefined) by normalizing
channelType before comparison (e.g., use channelType?.toLowerCase() === "feishu"
or similar) so Feishu-specific stripping in stripChannelSystemSuffix applies
even if channelType is "Feishu" or "FEISHU".
🤖 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/controller/src/runtime/sessions-runtime.ts`:
- Around line 416-425: The loop currently pushes blocks with unknown blockType
into normalizedBlocks but doesn't set hasVisibleContent, causing a collection of
only-unknown blocks to return null; decide whether unknown blocks should count
as visible and either (A) add hasVisibleContent = true immediately after the
normalizedBlocks.push(block) for unknown types so they mark the message as
visible, or (B) leave behavior and add an inline comment near the
normalizedBlocks.push(block) explaining that only "text" and tool types are
considered visible and unknown types are intentionally excluded from
hasVisibleContent; refer to blockType, normalizedBlocks, and hasVisibleContent
in your change.
- Around line 483-496: In stripChannelSystemSuffix, the channelType comparison
is case-sensitive; update the check to handle case-insensitive values (and
null/undefined) by normalizing channelType before comparison (e.g., use
channelType?.toLowerCase() === "feishu" or similar) so Feishu-specific stripping
in stripChannelSystemSuffix applies even if channelType is "Feishu" or "FEISHU".

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: abc55fae-7f7d-4e8c-a294-56a903cf274e

📥 Commits

Reviewing files that changed from the base of the PR and between 55bfcda and f7439ad.

📒 Files selected for processing (5)
  • apps/controller/src/runtime/sessions-runtime.ts
  • apps/controller/tests/session-routes.test.ts
  • apps/controller/tests/sessions-runtime.test.ts
  • apps/web/src/pages/sessions.tsx
  • apps/web/tests/sessions.test.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/pages/sessions.tsx
  • apps/web/tests/sessions.test.tsx

lefarcen
lefarcen previously approved these changes Mar 23, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/web/src/pages/sessions.tsx (1)

773-781: extractMessage is invoked twice per displayed message.

The filter calls extractMessage for each message, then ChatBubble calls it again during render. Consider memoizing or pre-extracting once to avoid redundant parsing.

Potential optimization
-              {messages
-                .filter((msg) => {
-                  const { text, replyContextText, hasToolCall } =
-                    extractMessage(msg as unknown as Record<string, unknown>);
-                  return (
-                    text.trim().length > 0 ||
-                    (replyContextText?.trim().length ?? 0) > 0 ||
-                    hasToolCall
-                  );
-                })
-                .map((msg) => (
-                  <ChatBubble key={msg.id} msg={msg} />
-                ))}
+              {messages
+                .map((msg) => ({
+                  msg,
+                  extracted: extractMessage(msg as unknown as Record<string, unknown>),
+                }))
+                .filter(({ extracted }) =>
+                  extracted.text.trim().length > 0 ||
+                  (extracted.replyContextText?.trim().length ?? 0) > 0 ||
+                  extracted.hasToolCall
+                )
+                .map(({ msg, extracted }) => (
+                  <ChatBubble key={msg.id} msg={msg} extracted={extracted} />
+                ))}

This would require updating ChatBubble to accept an optional pre-computed extracted prop.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/pages/sessions.tsx` around lines 773 - 781, The filter currently
calls extractMessage for each message and ChatBubble calls extractMessage again
during render, causing duplicate work; fix by pre-extracting once in the mapping
step (e.g., create an array of {msg, extracted: extractMessage(msg)} before
filtering) and update ChatBubble to accept an optional extracted prop (e.g.,
extracted) so you can pass the precomputed result into ChatBubble instead of
letting it call extractMessage again; ensure all call sites of ChatBubble are
updated to prefer the extracted prop when present and keep extractMessage usage
only where no precomputed value exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/pages/sessions.tsx`:
- Around line 449-451: The "Reply" label in sessions.tsx is hardcoded; add a new
i18n key (e.g., sessions.chat.replyLabel) to your locale files and replace the
literal with a translated string using useTranslation(); locate the div with
className "text-[10px] font-semibold..." in the Sessions component and either
call useTranslation() there to render t('sessions.chat.replyLabel') or pass the
translated label into ReplyContextCard (or the relevant child) as a prop so the
component no longer contains the hardcoded "Reply".

---

Nitpick comments:
In `@apps/web/src/pages/sessions.tsx`:
- Around line 773-781: The filter currently calls extractMessage for each
message and ChatBubble calls extractMessage again during render, causing
duplicate work; fix by pre-extracting once in the mapping step (e.g., create an
array of {msg, extracted: extractMessage(msg)} before filtering) and update
ChatBubble to accept an optional extracted prop (e.g., extracted) so you can
pass the precomputed result into ChatBubble instead of letting it call
extractMessage again; ensure all call sites of ChatBubble are updated to prefer
the extracted prop when present and keep extractMessage usage only where no
precomputed value exists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c695006-121e-4984-af42-abf3b022d640

📥 Commits

Reviewing files that changed from the base of the PR and between f7439ad and 32e935e.

📒 Files selected for processing (5)
  • apps/controller/src/runtime/sessions-runtime.ts
  • apps/controller/tests/session-routes.test.ts
  • apps/controller/tests/sessions-runtime.test.ts
  • apps/web/src/pages/sessions.tsx
  • apps/web/tests/sessions.test.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/controller/tests/session-routes.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/controller/src/runtime/sessions-runtime.ts

Comment thread apps/web/src/pages/sessions.tsx
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/web/tests/sessions.test.tsx (1)

405-417: Consider extracting a shared test harness for repeated QueryClient setup.

The new cases repeat the same initialization boilerplate; a small helper would reduce noise and future maintenance overhead.

Also applies to: 462-476, 528-543

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/tests/sessions.test.tsx` around lines 405 - 417, Extract the
repeated QueryClient initialization and pre-population into a small test helper
(e.g., createTestQueryClient or setupQueryClientWithSessionMeta) that constructs
a QueryClient with defaultOptions.queries.retry = false and calls
queryClient.setQueryData(["session-meta", sessionId], { id: sessionId, title,
channelType, messageCount, ... }) before returning the client; replace the
duplicated blocks that call new QueryClient(...) and setQueryData in tests (the
code using QueryClient and setQueryData) with calls to this helper to reduce
boilerplate and ensure consistent setup across test cases.
🤖 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/web/tests/sessions.test.tsx`:
- Around line 405-417: Extract the repeated QueryClient initialization and
pre-population into a small test helper (e.g., createTestQueryClient or
setupQueryClientWithSessionMeta) that constructs a QueryClient with
defaultOptions.queries.retry = false and calls
queryClient.setQueryData(["session-meta", sessionId], { id: sessionId, title,
channelType, messageCount, ... }) before returning the client; replace the
duplicated blocks that call new QueryClient(...) and setQueryData in tests (the
code using QueryClient and setQueryData) with calls to this helper to reduce
boilerplate and ensure consistent setup across test cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 33bfc6f9-7923-40f9-92f0-2343cdab271a

📥 Commits

Reviewing files that changed from the base of the PR and between 32e935e and 0ad42ed.

📒 Files selected for processing (4)
  • apps/web/src/i18n/locales/en.ts
  • apps/web/src/i18n/locales/zh-CN.ts
  • apps/web/src/pages/sessions.tsx
  • apps/web/tests/sessions.test.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/web/src/i18n/locales/zh-CN.ts
  • apps/web/src/i18n/locales/en.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/pages/sessions.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants