Skip to content

fix: render web-UI slash commands compactly instead of leaking prompt - #1055

Open
snazzybean wants to merge 1 commit into
siteboon:mainfrom
snazzybean:fix/1009-command-prompt-leak
Open

fix: render web-UI slash commands compactly instead of leaking prompt#1055
snazzybean wants to merge 1 commit into
siteboon:mainfrom
snazzybean:fix/1009-command-prompt-leak

Conversation

@snazzybean

@snazzybean snazzybean commented Jul 22, 2026

Copy link
Copy Markdown

Fixes #1009

Problem

Custom slash commands were expanded client-side and their full body was sent as the chat message, so the internal prompt showed up as a user bubble both live and after a reload.

Reproduction

  1. Open the web UI with a Claude project and open or start a session.
  2. From the composer, invoke a custom slash command with arguments, e.g. /handoff just the essentials.
  3. Before this change: the whole expanded command body renders as your user message, both live and after reloading the session, instead of the command you typed.
  4. After this change: the bubble shows only the compact /handoff just the essentials, live and after reload.

Change

Display text and the sent prompt are now separate. The bubble only shows /name args, while Claude receives the expanded body wrapped in the CLI-native <command-name> tags. On reload the existing normalization folds that back into the compact form. The array-content branch parses the same wrapper, since aborted and streamed runs deliver the prompt as array text. Other providers keep receiving the plain body.

Tests

5 new node:test cases: string and array form, empty args, native tag-only, and a negative case. npm run typecheck, npm run lint, and npm run build are all green.

Review focus

The delicate part is the fingerprint coupling: frontend and backend must format the command identically, otherwise a duplicate bubble appears after the run completes. Please also check the non-Claude path.

Summary by CodeRabbit

  • New Features

    • Custom slash commands now show a compact optimistic command bubble, while the expanded prompt is used for submission.
    • Queued slash-command drafts now preserve and restore the expanded prompt, including when auto-sending.
  • Bug Fixes

    • Improved Claude “local slash command” handling to better recognize wrapper tags only when they start a message.
    • Prevented accidental local-command collapsing when tags appear mid-sentence.
    • Ensured command turns with image attachments render as a single user bubble.
  • Tests

    • Added coverage for normalization across wrapper formats, empty arguments, prose immunity, mid-sentence tags, and attachment rendering.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f176db3-fb47-429d-a700-fccc8616a110

📥 Commits

Reviewing files that changed from the base of the PR and between 4d330c3 and ca10db4.

📒 Files selected for processing (5)
  • server/modules/providers/list/claude/claude-sessions.provider.ts
  • server/modules/providers/tests/claude-local-command-invocation.test.ts
  • src/components/chat/hooks/useChatComposerState.ts
  • src/components/chat/utils/chatStorage.ts
  • src/hooks/useQueuedMessageAutoSend.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/hooks/useQueuedMessageAutoSend.ts
  • src/components/chat/utils/chatStorage.ts
  • server/modules/providers/tests/claude-local-command-invocation.test.ts
  • server/modules/providers/list/claude/claude-sessions.provider.ts
  • src/components/chat/hooks/useChatComposerState.ts

📝 Walkthrough

Walkthrough

Local slash commands now separate compact chat display text from expanded provider prompts across Claude normalization, command submission, queued-draft persistence, and automatic sending.

Changes

Local command flow

Layer / File(s) Summary
Command display contract and Claude normalization
server/modules/providers/list/claude/claude-sessions.provider.ts, server/modules/providers/tests/claude-local-command-invocation.test.ts, src/components/chat/hooks/useChatComposerState.ts
Compact command formatting is aligned between frontend and server paths. Tagged Claude prompts, including array-based content, normalize into compact local-command messages without expanded-body leakage, while embedded tags remain ordinary text.
Command execution and submission
src/components/chat/hooks/useChatComposerState.ts
Custom command execution passes separate display and provider prompt content through CommandSubmitOverride, message construction, summaries, interception, and send options.
Queued command persistence and auto-send
src/components/chat/hooks/useChatComposerState.ts, src/components/chat/utils/chatStorage.ts, src/hooks/useQueuedMessageAutoSend.ts
Queued drafts persist optional expanded prompt content, restore it from storage, and prefer it over compact display content during automatic sending.

Sequence Diagram(s)

sequenceDiagram
  participant Composer
  participant QueueStorage
  participant AutoSend
  participant ClaudeSessionsProvider
  Composer->>Composer: Build displayText and promptContent
  Composer->>QueueStorage: Persist content and promptContent
  QueueStorage->>AutoSend: Restore queued command
  AutoSend->>ClaudeSessionsProvider: Send promptContent
  ClaudeSessionsProvider-->>Composer: Normalize local-command message
Loading

Possibly related PRs

Poem

A rabbit hops where commands flow,
Compact bubbles softly show.
Expanded prompts travel deep,
Queued drafts wake from gentle sleep.
Claude keeps hidden words in flight.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: compact rendering of slash commands without leaking prompts.
Linked Issues check ✅ Passed The changes address #1009 by detecting internal slash-command payloads, normalizing them to compact bubbles, and preventing prompt leakage.
Out of Scope Changes check ✅ Passed The additional composer, storage, and auto-send changes all support the slash-command rendering fix and do not appear unrelated.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@snazzybean
snazzybean force-pushed the fix/1009-command-prompt-leak branch 3 times, most recently from bb23730 to e438c50 Compare July 22, 2026 21:09
@snazzybean
snazzybean marked this pull request as ready for review July 22, 2026 21:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
server/modules/providers/list/claude/claude-sessions.provider.ts (1)

356-384: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Attach images to the compact command bubble.

When this array entry includes images, the local-command branch emits a text message without imageAttachments and leaves imagesAttached false. Lines 412-424 then add a second image-only user bubble on replay. Mirror the normal text branch by attaching the images here and setting imagesAttached = true; add a command-plus-image regression case.

Proposed fix
                 messages.push(createNormalizedMessage({
                   id: `${baseId}_cmd_${partIndex}`,
                   sessionId,
                   timestamp: ts,
                   provider: PROVIDER,
                   kind: 'text',
                   role: 'user',
                   content: displayText,
+                  images: !imagesAttached && imageAttachments.length > 0
+                    ? imageAttachments
+                    : undefined,
                   commandName: localCommandPayload.commandName,
                   commandMessage: localCommandPayload.commandMessage,
                   commandArgs: localCommandPayload.commandArgs,
                   isLocalCommand: true,
                 }));
+                imagesAttached = true;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/modules/providers/list/claude/claude-sessions.provider.ts` around
lines 356 - 384, The local-command branch around buildLocalCommandDisplayText
must attach pending imageAttachments to the emitted command message using the
same imagesAttached guard as the normal text branch, then set imagesAttached =
true after emission. Add a regression case covering a local command with images
and verify replay produces one command-plus-image user message rather than a
separate image-only bubble.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/modules/providers/list/claude/claude-sessions.provider.ts`:
- Around line 352-373: Update the local-command parsing in the array-content
handling around parseLocalCommandPayload so it only accepts a complete CLI
wrapper at the beginning of the text, rather than any embedded command tag.
Preserve surrounding or non-wrapper text as normal content, while continuing to
build the compact display message for valid submitter-emitted wrappers.

---

Outside diff comments:
In `@server/modules/providers/list/claude/claude-sessions.provider.ts`:
- Around line 356-384: The local-command branch around
buildLocalCommandDisplayText must attach pending imageAttachments to the emitted
command message using the same imagesAttached guard as the normal text branch,
then set imagesAttached = true after emission. Add a regression case covering a
local command with images and verify replay produces one command-plus-image user
message rather than a separate image-only bubble.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5aae9bfe-f283-4eea-a722-18cc55383047

📥 Commits

Reviewing files that changed from the base of the PR and between 27eaf01 and e438c50.

📒 Files selected for processing (5)
  • server/modules/providers/list/claude/claude-sessions.provider.ts
  • server/modules/providers/tests/claude-local-command-invocation.test.ts
  • src/components/chat/hooks/useChatComposerState.ts
  • src/components/chat/utils/chatStorage.ts
  • src/hooks/useQueuedMessageAutoSend.ts

Comment thread server/modules/providers/list/claude/claude-sessions.provider.ts
@snazzybean
snazzybean force-pushed the fix/1009-command-prompt-leak branch from 2940f6c to 576746d Compare July 23, 2026 17:27

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/chat/utils/chatStorage.ts`:
- Around line 83-84: Update the parsed queued-message handling around
StoredQueuedMessage to validate promptContent at runtime before returning it:
preserve it only when it is a string, otherwise omit it or set it to the
existing absent-value representation. Keep the content.trim() check and return
behavior unchanged, ensuring malformed persisted promptContent cannot reach
useQueuedMessageAutoSend.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5efb5bb9-e9c9-4cd7-b7e4-53aef65493ab

📥 Commits

Reviewing files that changed from the base of the PR and between 2940f6c and 576746d.

📒 Files selected for processing (5)
  • server/modules/providers/list/claude/claude-sessions.provider.ts
  • server/modules/providers/tests/claude-local-command-invocation.test.ts
  • src/components/chat/hooks/useChatComposerState.ts
  • src/components/chat/utils/chatStorage.ts
  • src/hooks/useQueuedMessageAutoSend.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/hooks/useQueuedMessageAutoSend.ts
  • src/components/chat/hooks/useChatComposerState.ts

Comment thread src/components/chat/utils/chatStorage.ts Outdated
@snazzybean
snazzybean force-pushed the fix/1009-command-prompt-leak branch from 576746d to 4d330c3 Compare July 23, 2026 17:32
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.

fix: skill content rendered as user input in web UI

1 participant