(prompt: string, model: "sonnet" | "opus")
| 431 | // Project path selection handled by parent tab controls |
| 432 | |
| 433 | const handleSendPrompt = async (prompt: string, model: "sonnet" | "opus") => { |
| 434 | console.log('[ClaudeCodeSession] handleSendPrompt called with:', { prompt, model, projectPath, claudeSessionId, effectiveSession }); |
| 435 | |
| 436 | if (!projectPath) { |
| 437 | setError("Please select a project directory first"); |
| 438 | return; |
| 439 | } |
| 440 | |
| 441 | // If already loading, queue the prompt |
| 442 | if (isLoading) { |
| 443 | const newPrompt = { |
| 444 | id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, |
| 445 | prompt, |
| 446 | model |
| 447 | }; |
| 448 | setQueuedPrompts(prev => [...prev, newPrompt]); |
| 449 | return; |
| 450 | } |
| 451 | |
| 452 | try { |
| 453 | setIsLoading(true); |
| 454 | setError(null); |
| 455 | hasActiveSessionRef.current = true; |
| 456 | |
| 457 | // For resuming sessions, ensure we have the session ID |
| 458 | if (effectiveSession && !claudeSessionId) { |
| 459 | setClaudeSessionId(effectiveSession.id); |
| 460 | } |
| 461 | |
| 462 | // Only clean up and set up new listeners if not already listening |
| 463 | if (!isListeningRef.current) { |
| 464 | // Clean up previous listeners |
| 465 | unlistenRefs.current.forEach(unlisten => unlisten()); |
| 466 | unlistenRefs.current = []; |
| 467 | |
| 468 | // Mark as setting up listeners |
| 469 | isListeningRef.current = true; |
| 470 | |
| 471 | // -------------------------------------------------------------------- |
| 472 | // 1️⃣ Event Listener Setup Strategy |
| 473 | // -------------------------------------------------------------------- |
| 474 | // Claude Code may emit a *new* session_id even when we pass --resume. If |
| 475 | // we listen only on the old session-scoped channel we will miss the |
| 476 | // stream until the user navigates away & back. To avoid this we: |
| 477 | // • Always start with GENERIC listeners (no suffix) so we catch the |
| 478 | // very first "system:init" message regardless of the session id. |
| 479 | // • Once that init message provides the *actual* session_id, we |
| 480 | // dynamically switch to session-scoped listeners and stop the |
| 481 | // generic ones to prevent duplicate handling. |
| 482 | // -------------------------------------------------------------------- |
| 483 | |
| 484 | console.log('[ClaudeCodeSession] Setting up generic event listeners first'); |
| 485 | |
| 486 | let currentSessionId: string | null = claudeSessionId || effectiveSession?.id || null; |
| 487 | |
| 488 | // Helper to attach session-specific listeners **once we are sure** |
| 489 | const attachSessionSpecificListeners = async (sid: string) => { |
| 490 | console.log('[ClaudeCodeSession] Attaching session-specific listeners for', sid); |
no test coverage detected