diff --git a/.github/agent/agent.mjs b/.github/agent/agent.mjs index cada6f3..0e1f569 100644 --- a/.github/agent/agent.mjs +++ b/.github/agent/agent.mjs @@ -1229,16 +1229,16 @@ This is ONE FILE ONLY. Generate the complete diff now:`; if (!filesToModify || allDiffs.length === 0) { console.log(`[AGENT] Using single-pass generation method...`); const apiStartTime = Date.now(); - try { - output = await retryWithBackoff(async () => { - return await callAI(prompt); - }, 3, 1000); - const apiDuration = Date.now() - apiStartTime; - console.log(`[AGENT] API call completed in ${apiDuration}ms`); - } catch (error) { - const errorDetails = formatErrorDetails(error, {}); - await handleError(error, `${provider.toUpperCase()} API Error`, { errorDetails }); - // handleError calls process.exit(1), so we never reach here +try { + output = await retryWithBackoff(async () => { + return await callAI(prompt); + }, 3, 1000); + const apiDuration = Date.now() - apiStartTime; + console.log(`[AGENT] API call completed in ${apiDuration}ms`); +} catch (error) { + const errorDetails = formatErrorDetails(error, {}); + await handleError(error, `${provider.toUpperCase()} API Error`, { errorDetails }); + // handleError calls process.exit(1), so we never reach here } } @@ -1246,31 +1246,31 @@ if (!filesToModify || allDiffs.length === 0) { let diff = output.trim(); if (!filesToModify || allDiffs.length === 0) { // Only do markdown extraction for fallback single-pass method - if (output.includes("```")) { - // Try to extract content between code fences - // Handle both single and multiple code blocks - const matches = output.matchAll(/```(?:diff)?\n([\s\S]*?)```/g); - const extractedDiffs = []; - for (const match of matches) { - extractedDiffs.push(match[1].trim()); - } - // Use the longest extracted diff (likely the actual diff) - if (extractedDiffs.length > 0) { - diff = extractedDiffs.reduce((a, b) => a.length > b.length ? a : b); - } - - // If no code blocks found but output contains diff markers, use the whole output - if (!diff.includes("--- a/") && output.includes("--- a/")) { - // Extract everything after the first "--- a/" line - const diffStart = output.indexOf("--- a/"); - diff = output.substring(diffStart).trim(); - // Remove any trailing markdown or explanations - const diffEnd = diff.indexOf("\n\n```") !== -1 ? diff.indexOf("\n\n```") : - diff.indexOf("\n\n##") !== -1 ? diff.indexOf("\n\n##") : - diff.indexOf("\n\n**") !== -1 ? diff.indexOf("\n\n**") : - diff.length; - diff = diff.substring(0, diffEnd).trim(); - } +if (output.includes("```")) { + // Try to extract content between code fences + // Handle both single and multiple code blocks + const matches = output.matchAll(/```(?:diff)?\n([\s\S]*?)```/g); + const extractedDiffs = []; + for (const match of matches) { + extractedDiffs.push(match[1].trim()); + } + // Use the longest extracted diff (likely the actual diff) + if (extractedDiffs.length > 0) { + diff = extractedDiffs.reduce((a, b) => a.length > b.length ? a : b); + } + + // If no code blocks found but output contains diff markers, use the whole output + if (!diff.includes("--- a/") && output.includes("--- a/")) { + // Extract everything after the first "--- a/" line + const diffStart = output.indexOf("--- a/"); + diff = output.substring(diffStart).trim(); + // Remove any trailing markdown or explanations + const diffEnd = diff.indexOf("\n\n```") !== -1 ? diff.indexOf("\n\n```") : + diff.indexOf("\n\n##") !== -1 ? diff.indexOf("\n\n##") : + diff.indexOf("\n\n**") !== -1 ? diff.indexOf("\n\n**") : + diff.length; + diff = diff.substring(0, diffEnd).trim(); + } } } else { // For one-file-at-a-time, diff is already clean (output from combined diffs) @@ -1372,9 +1372,9 @@ if (validationResult.errors.length > 0 || diff.match(/\+\+\+ b\/[^\n]*$/m)) { } const errorMsg = `${truncationReason}. The AI model may have generated an incomplete diff.\n\nErrors:\n${validationResult.errors.join('\n')}\n\nAPI Response Info:\n- Stop reason: ${stopReason}\n- Output tokens used: ${outputTokens}\n- Was truncated: ${wasTruncated}\n\nDiff preview (last 500 chars):\n\`\`\`\n${diff.substring(Math.max(0, diffLength - 500))}\n\`\`\`\n\nPossible causes:\n- Model hit output token limit (check max_tokens setting)\n- Input context too large, leaving insufficient room for output\n- Model stopped generating for other reasons\n\nSuggestions:\n- Reduce input context size (fewer files) - already reduced to 400KB\n- Break task into smaller parts\n- Verify max_tokens is sufficient (currently 180K)`; - const errorDetails = formatErrorDetails(new Error(errorMsg), { diff: diff.substring(Math.max(0, diffLength - 1000)), files: validationResult.stats.filesChanged }); + const errorDetails = formatErrorDetails(new Error(errorMsg), { diff: diff.substring(Math.max(0, diffLength - 1000)), files: validationResult.stats.filesChanged }); await handleError(new Error(errorMsg), "Incomplete Diff (Truncated)", { diff: diff.substring(Math.max(0, diffLength - 1000)), errorDetails }); - // handleError calls process.exit(1), so we never reach here + // handleError calls process.exit(1), so we never reach here } } } diff --git a/app.manifest.json b/app.manifest.json index 988e207..fda18d4 100644 --- a/app.manifest.json +++ b/app.manifest.json @@ -56,3 +56,7 @@ + + + + diff --git a/docs/DISCORD_DISCOVERY_CONTENT.md b/docs/DISCORD_DISCOVERY_CONTENT.md index 7f42b35..2b673c3 100644 --- a/docs/DISCORD_DISCOVERY_CONTENT.md +++ b/docs/DISCORD_DISCOVERY_CONTENT.md @@ -109,3 +109,7 @@ Join our Discord server for: + + + + diff --git a/index.js b/index.js index b0c62ad..a17e70a 100644 --- a/index.js +++ b/index.js @@ -2340,15 +2340,15 @@ async function getNowPlaying(options = {}) { // Only fetch queue if explicitly needed (skip for status polling to avoid choppy playback) if (!skipQueue) { promises.push( - sonos.getQueue().catch(err => { - // Ignore queue errors for now-playing - return null; - }) + sonos.getQueue().catch(err => { + // Ignore queue errors for now-playing + return null; + }) ); } else { promises.push(Promise.resolve(null)); } - + const [state, volume, queue] = await Promise.all(promises); // Fetch queue (next tracks) - only if we have queue data @@ -4463,9 +4463,18 @@ async function _add(input, channel, userName) { if (state === 'stopped') { logger.info('Player stopped - ensuring queue is active source and flushing'); try { - // Parallel stop and flush (flush is safe even if not stopped) + // Stop any active playback to force Sonos to use queue + try { + await sonos.stop(); + await new Promise(resolve => setTimeout(resolve, 300)); + } catch (stopErr) { + // Ignore stop errors (might already be stopped) + logger.debug('Stop command result (may already be stopped): ' + stopErr.message); + } + + // Flush queue to start fresh await sonos.flush(); - await new Promise(resolve => setTimeout(resolve, 200)); // Reduced from 300ms + await new Promise(resolve => setTimeout(resolve, 300)); logger.info('Queue flushed and ready'); } catch (flushErr) { logger.warn('Could not flush queue: ' + flushErr.message); @@ -4493,8 +4502,9 @@ async function _add(input, channel, userName) { // Try to queue the first valid candidate (most relevant result) let result = null; try { + logger.info(`Attempting to queue: ${firstCandidate.name} by ${firstCandidate.artist} (URI: ${firstCandidate.uri})`); await sonos.queue(firstCandidate.uri); - logger.info('Added track: ' + firstCandidate.name); + logger.info('Successfully queued track: ' + firstCandidate.name); result = firstCandidate; } catch (e) { const errorDetails = e.message || String(e); @@ -4540,7 +4550,72 @@ async function _add(input, channel, userName) { // Start playback in background (don't await) (async () => { try { - await new Promise(resolve => setTimeout(resolve, 300)); // Brief delay for queue to settle + // Ensure queue is the active source before starting playback + // Stop any active playback to force Sonos to use queue + try { + await sonos.stop(); + await new Promise(resolve => setTimeout(resolve, 500)); + } catch (stopErr) { + // Ignore stop errors (might already be stopped) + logger.debug('Stop before play (may already be stopped): ' + stopErr.message); + } + + // Verify queue has items before trying to play (prevents UPnP error 701) + let queueReady = false; + let retries = 0; + while (!queueReady && retries < 5) { + try { + const queue = await sonos.getQueue(); + if (queue && queue.items && queue.items.length > 0) { + queueReady = true; + logger.debug(`Queue verified: ${queue.items.length} items ready`); + } else { + logger.debug(`Queue not ready yet (attempt ${retries + 1}/5), waiting...`); + await new Promise(resolve => setTimeout(resolve, 300)); + retries++; + } + } catch (queueErr) { + logger.debug(`Queue check failed (attempt ${retries + 1}/5): ${queueErr.message}`); + await new Promise(resolve => setTimeout(resolve, 300)); + retries++; + } + } + + if (!queueReady) { + logger.warn('Queue not ready after 5 attempts, attempting playback anyway'); + } + + // Try to activate queue by seeking to position 1 (alternative to SetAVTransportURI) + // This should activate the queue as the transport source + try { + logger.debug('Attempting to seek to queue position 1 to activate queue'); + // Seek to track 1 in the queue to activate it + await sonos.avTransportService().Seek({ + InstanceID: 0, + Unit: 'TRACK_NR', + Target: '1' + }); + logger.debug('Successfully sought to track 1, queue should be active'); + // Wait for seek to complete + await new Promise(resolve => setTimeout(resolve, 500)); + } catch (seekErr) { + // If seek fails, try alternative: use next() to jump to first track + logger.debug('Seek failed, trying next() to activate queue: ' + seekErr.message); + try { + // Jump to first track in queue (this should activate the queue) + await sonos.next(); + logger.debug('Used next() to activate queue'); + await new Promise(resolve => setTimeout(resolve, 300)); + } catch (nextErr) { + logger.debug('next() also failed: ' + nextErr.message); + // Continue anyway - play() might still work + } + } + + // Wait a moment to ensure queue is ready + await new Promise(resolve => setTimeout(resolve, 500)); + + // Start playback from queue await sonos.play(); logger.info('Started playback from queue'); } catch (playErr) { diff --git a/lib/slack-validator.js b/lib/slack-validator.js index 956e031..7e8dbb7 100644 --- a/lib/slack-validator.js +++ b/lib/slack-validator.js @@ -99,3 +99,7 @@ module.exports = { + + + + diff --git a/lib/sonos-discovery.js b/lib/sonos-discovery.js index 54bcb86..0d657a0 100644 --- a/lib/sonos-discovery.js +++ b/lib/sonos-discovery.js @@ -147,3 +147,7 @@ module.exports = { + + + + diff --git a/lib/spotify-validator.js b/lib/spotify-validator.js index f835afc..7da6673 100644 --- a/lib/spotify-validator.js +++ b/lib/spotify-validator.js @@ -63,3 +63,7 @@ module.exports = { + + + +