We want to add a cancel button to the web UI that allows users to interrupt Claude while it's thinking, using tools, or performing any time-consuming operation.
The codebase currently supports two modes:
-
SDK Mode (
claude-code-session.ts)- Uses
@anthropic-ai/claude-agent-sdkpackage - Has built-in
query.interrupt()method for cancellation ✅ - Works with Max subscription (confirmed via Reddit thread)
- Just needs
ANTHROPIC_API_KEYenvironment variable to be unset
- Uses
-
CLI Mode (
claude-cli-session.ts) - Currently Active- Spawns
claudeCLI binary directly withspawn() - Uses
stream-jsoninput/output format - No clean cancellation mechanism - signals either kill the process or cause JSON parsing errors
- Currently defaults to this mode to use Max subscription
- Spawns
From Reddit thread: https://www.reddit.com/r/ClaudeAI/comments/1leigee/claude_sdk_usage_with_claude_max_subscription/
Multiple users confirmed:
- SDK works with Max subscription
- Just need to ensure
ANTHROPIC_API_KEYis not set:unset ANTHROPIC_API_KEY - Uses existing Claude login credentials
-
SDK Cancel Method (claude-code-session.ts:282-295)
async cancel(): Promise<void> { await this.currentQuery.interrupt(); this.emit('cancelled', { message: 'Operation cancelled by user' }); }
-
Cancel API Endpoint (src/app/api/claude-code/cancel/route.ts)
- POST endpoint that calls
session.cancel() - Works with both SDK and CLI modes
- POST endpoint that calls
-
Cancel Button UI (ChatInput.tsx:55-76)
- Red X button appears when
isLoading && onCancelis true - Replaces send button during operation
- Connected to cancel handler in ChatPanelClaudeCode
- Red X button appears when
-
Frontend Cancel Handler (ChatPanelClaudeCode.tsx:202-223)
- Calls
/api/claude-code/cancel - Updates UI state
- Shows cancellation message
- Calls
CLI Mode Cancellation (claude-cli-session.ts:307-323)
- Current implementation sends SIGTERM (kills process)
- Alternative attempts:
- Escape character (
\x1B) → breaks JSON stream parser - SIGINT → likely kills process or causes errors
- Escape character (
- Process restart takes 2-3 seconds
Why:
- ✅ SDK has proper
query.interrupt()support - ✅ Works with Max subscription (no API credits needed)
- ✅ Session survives cancellation (no restart delay)
- ✅ All UI components already implemented and ready
Changes Needed:
-
Update Mode Selection (src/app/api/claude-code/route.ts:13)
// Change from: const USE_CLI_MODE = process.env.USE_SDK !== 'true'; // To: const USE_CLI_MODE = process.env.USE_CLI === 'true';
This makes SDK the default, CLI opt-in
-
Ensure Environment Variable Not Set
- Check that
ANTHROPIC_API_KEYis not in.envor environment - SDK will use Max subscription login automatically
- Check that
-
Test Cancellation
- Start long operation (e.g., "Write a 500-word story")
- Click cancel button during generation
- Verify operation stops immediately
- Verify session remains active for new messages
If we must use CLI mode, options are limited:
-
Accept Process Restart (current SIGTERM approach)
- Pro: Guaranteed to stop operation
- Con: 2-3 second restart delay
- Con: Loses any partial response
-
Test SIGINT Behavior
- Created test script: test-cli-cancel.ts
- Run:
npx tsx test-cli-cancel.ts - Will determine if SIGINT cancels without killing
-
Custom Control Message (speculative)
- Investigate if
stream-jsonmode accepts control messages - No documentation found yet
- Investigate if
- Change default mode to SDK in route.ts
- Verify
ANTHROPIC_API_KEYnot set - Restart dev server
- Test cancel button
- Run CLI test script to understand SIGINT behavior
- Document actual behavior
- Implement appropriate fallback
- ✅ src/components/ChatInput.tsx - Cancel button UI
- ✅ src/components/ChatPanelClaudeCode.tsx - Cancel handler
- ✅ src/app/api/claude-code/cancel/route.ts - Cancel API
- ✅ src/lib/claude-code-session.ts - SDK cancel method
- ⏳ src/app/api/claude-code/route.ts - Mode selection (pending)
⚠️ src/lib/claude-cli-session.ts - CLI cancel (problematic)
Created test-cli-cancel.ts to verify CLI SIGINT behavior:
- Spawns CLI in stream-json mode
- Sends long-running request
- Sends SIGINT after 2 seconds
- Monitors if process survives
- Reddit: SDK with Max subscription works - https://www.reddit.com/r/ClaudeAI/comments/1leigee/claude_sdk_usage_with_claude_max_subscription/
- SDK Query.interrupt() docs: node_modules/@anthropic-ai/claude-agent-sdk/sdk.d.ts:396
- Interactive mode Ctrl+C docs: Cancels current operation without killing session
- Review this plan
- Decide: SDK mode (recommended) or test CLI SIGINT behavior
- Implement chosen approach
- Test thoroughly
- Document final solution
Status: Ready for implementation Blocker: None - all code is written, just need to switch mode Risk: Low - can revert to CLI mode if SDK issues arise