From fa62d78b1c138553b201af66293ee6c030f27b19 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Mon, 29 Dec 2025 12:46:53 -0600 Subject: [PATCH] ## CHANGES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Hardened Linux releases with architecture-safe npm caching to prevent cross-contamination ๐Ÿ›ก๏ธ - Added Linux-only prebuild cache purges to avoid wrong-arch native binaries ๐Ÿงน - Force rebuild native modules per platform, with explicit x64/arm64 env targeting ๐Ÿงฌ - Cleaned node-pty and better-sqlite3 prebuild directories before Linux packaging ๐Ÿงฑ - Added x64 better-sqlite3 binary architecture verification step in CI ๐Ÿ” - Improved Auto Run error handling with rich history entries and recovery guidance ๐Ÿงพ - Added toast notifications for Auto Run errors with friendly titles and details ๐Ÿ”” - Extended history entry API to track success/failure for better visibility โœ… - Enhanced Auto Run pause logging with recoverability flags and raw error context ๐Ÿงฐ - Bumped version to 0.13.1 for this release rollout ๐Ÿš€ --- .github/workflows/release.yml | 117 +++++++++++++++++- package.json | 2 +- src/renderer/App.tsx | 74 +++++++++++ .../hooks/agent/useAgentSessionManagement.ts | 6 +- src/renderer/hooks/batch/useBatchProcessor.ts | 7 +- 5 files changed, 198 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a266f08..5c013a85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,12 +39,42 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Setup Node.js + # CRITICAL: Do NOT use setup-node's built-in npm cache for Linux builds. + # The cache key doesn't include architecture, so x64 and arm64 share the same cache. + # This causes ARM64 prebuilds to contaminate x64 builds (GitHub issue #116). + - name: Setup Node.js (Linux - no cache) + if: matrix.platform == 'linux' || matrix.platform == 'linux-arm64' + uses: actions/setup-node@v4 + with: + node-version: '20' + # Explicitly no cache to prevent cross-architecture contamination + + - name: Setup Node.js (non-Linux - with cache) + if: matrix.platform != 'linux' && matrix.platform != 'linux-arm64' uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' + # Use architecture-specific cache for Linux builds + - name: Cache npm dependencies (Linux) + if: matrix.platform == 'linux' || matrix.platform == 'linux-arm64' + uses: actions/cache@v4 + with: + path: ~/.npm + key: npm-linux-${{ matrix.arch }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + npm-linux-${{ matrix.arch }}- + + # Extra safety: Clear any prebuild cache that might have wrong-arch binaries + - name: Clear prebuild cache (Linux) + if: matrix.platform == 'linux' || matrix.platform == 'linux-arm64' + run: | + echo "Clearing any cached prebuilds to prevent architecture contamination..." + rm -rf ~/.npm/_prebuilds 2>/dev/null || true + rm -rf ~/.cache/prebuild-install 2>/dev/null || true + echo "Prebuild caches cleared" + # Linux x64: Install build dependencies for native modules and electron-builder # Requires build-essential for compiling node-pty native module - name: Install Linux x64 build dependencies @@ -123,8 +153,48 @@ jobs: - name: Install dependencies run: npm ci + # Clean any prebuilt binaries that may have been downloaded for wrong architecture + # This is critical: prebuild-install downloads binaries during npm ci, which may be wrong + - name: Clean prebuilt binaries (Linux x64) + if: matrix.platform == 'linux' + run: | + echo "Cleaning prebuilt binaries to force rebuild from source..." + rm -rf node_modules/node-pty/prebuilds + rm -rf node_modules/node-pty/build + rm -rf node_modules/better-sqlite3/prebuilds + rm -rf node_modules/better-sqlite3/build + echo "Prebuilt directories cleaned" + + # Clean any prebuilt binaries that may have been downloaded for wrong architecture + - name: Clean prebuilt binaries (Linux ARM64) + if: matrix.platform == 'linux-arm64' + run: | + echo "Cleaning prebuilt binaries to force rebuild from source..." + rm -rf node_modules/node-pty/prebuilds + rm -rf node_modules/node-pty/build + rm -rf node_modules/better-sqlite3/prebuilds + rm -rf node_modules/better-sqlite3/build + echo "Prebuilt directories cleaned" + # Rebuild native modules for the target platform - - name: Rebuild native modules + - name: Rebuild native modules (Linux x64) + if: matrix.platform == 'linux' + run: npm run postinstall + env: + npm_config_build_from_source: true + npm_config_arch: x64 + npm_config_target_arch: x64 + + - name: Rebuild native modules (Linux ARM64) + if: matrix.platform == 'linux-arm64' + run: npm run postinstall + env: + npm_config_build_from_source: true + npm_config_arch: arm64 + npm_config_target_arch: arm64 + + - name: Rebuild native modules (non-Linux) + if: matrix.platform != 'linux' && matrix.platform != 'linux-arm64' run: npm run postinstall env: npm_config_build_from_source: true @@ -171,8 +241,29 @@ jobs: exit 1 fi - # Verify better-sqlite3 architecture (Linux ARM64 only) - - name: Verify better-sqlite3 architecture + # Verify better-sqlite3 architecture (Linux x64) + - name: Verify better-sqlite3 architecture (Linux x64) + if: matrix.platform == 'linux' + run: | + echo "Checking better-sqlite3 binary architecture..." + SQLITE_PATH=$(find node_modules/better-sqlite3 -name "better_sqlite3.node" -type f 2>/dev/null | head -1) + if [ -n "$SQLITE_PATH" ]; then + file "$SQLITE_PATH" + # Verify it's x64, not ARM64 + if file "$SQLITE_PATH" | grep -q "x86-64\|x86_64\|AMD64"; then + echo "โœ“ better-sqlite3 is correctly built for x64" + else + echo "โœ— ERROR: better-sqlite3 is NOT built for x64!" + file "$SQLITE_PATH" + exit 1 + fi + else + echo "โœ— ERROR: better-sqlite3 binary not found!" + exit 1 + fi + + # Verify better-sqlite3 architecture (Linux ARM64) + - name: Verify better-sqlite3 architecture (Linux ARM64) if: matrix.platform == 'linux-arm64' run: | echo "Checking better-sqlite3 binary architecture..." @@ -251,8 +342,17 @@ jobs: DEBUG: electron-builder npm_config_arch: x64 npm_config_target_arch: x64 + npm_config_build_from_source: true BUILD_VERSION: ${{ steps.version.outputs.VERSION }} run: | + # Critical: Clean build directories to force fresh compilation + # This prevents any cached/prebuilt ARM binaries from being used + echo "Cleaning native module build directories..." + rm -rf node_modules/node-pty/build + rm -rf node_modules/node-pty/prebuilds + rm -rf node_modules/better-sqlite3/build + rm -rf node_modules/better-sqlite3/prebuilds + # Explicitly rebuild native modules for x64 architecture echo "Rebuilding native modules for x64..." npx electron-rebuild --arch=x64 --force @@ -304,9 +404,18 @@ jobs: DEBUG: electron-builder npm_config_arch: arm64 npm_config_target_arch: arm64 + npm_config_build_from_source: true USE_SYSTEM_FPM: "true" BUILD_VERSION: ${{ steps.version.outputs.VERSION }} run: | + # Critical: Clean build directories to force fresh compilation + # This prevents any cached/prebuilt x64 binaries from being used + echo "Cleaning native module build directories..." + rm -rf node_modules/node-pty/build + rm -rf node_modules/node-pty/prebuilds + rm -rf node_modules/better-sqlite3/build + rm -rf node_modules/better-sqlite3/prebuilds + # Explicitly rebuild native modules for ARM64 architecture # This ensures node-pty is correctly compiled before packaging echo "Rebuilding native modules for ARM64..." diff --git a/package.json b/package.json index b13f69a4..d0ac06c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maestro", - "version": "0.13.0", + "version": "0.13.1", "description": "Maestro hones fractured attention into focused intent.", "main": "dist/main/index.js", "author": { diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 83ba7a43..f0bc643d 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -120,6 +120,31 @@ import { isLikelyConcatenatedToolNames, getSlashCommandDescription } from './con // Note: DEFAULT_IMAGE_ONLY_PROMPT is now imported from useInputProcessing hook +/** + * Get a human-readable title for an agent error type. + * Used for toast notifications and history entries. + */ +function getErrorTitleForType(type: AgentError['type']): string { + switch (type) { + case 'auth_expired': + return 'Authentication Required'; + case 'token_exhaustion': + return 'Context Limit Reached'; + case 'rate_limited': + return 'Rate Limit Exceeded'; + case 'network_error': + return 'Connection Error'; + case 'agent_crashed': + return 'Agent Error'; + case 'permission_denied': + return 'Permission Denied'; + case 'session_not_found': + return 'Session Not Found'; + default: + return 'Error'; + } +} + function MaestroConsoleInner() { // --- LAYER STACK (for blocking shortcuts when modals are open) --- const { hasOpenLayers, hasOpenModal } = useLayerStack(); @@ -2103,6 +2128,7 @@ function MaestroConsoleInner() { })); // Phase 5.10: Check if there's an active batch run for this session and pause it + // Also add history entry and toast for Auto Run errors if (getBatchStateRef.current && pauseBatchOnErrorRef.current) { const batchState = getBatchStateRef.current(actualSessionId); if (batchState.isRunning && !batchState.errorPaused) { @@ -2114,6 +2140,54 @@ function MaestroConsoleInner() { batchState.currentDocumentIndex, currentDoc ? `Processing ${currentDoc}` : undefined ); + + // Get session for history entry + const session = sessionsRef.current.find(s => s.id === actualSessionId); + + // Add history entry for Auto Run error (similar to stalled document entries) + if (addHistoryEntryRef.current && session) { + const errorTitle = getErrorTitleForType(agentError.type); + const errorExplanation = [ + `**Auto Run Error: ${errorTitle}**`, + '', + `Auto Run encountered an error while processing:`, + currentDoc ? `- Document: ${currentDoc}` : '', + `- Error: ${agentError.message}`, + '', + '**What to do:**', + agentError.type === 'auth_expired' + ? '- Re-authenticate with the provider (e.g., run `claude login` in terminal)' + : agentError.type === 'token_exhaustion' + ? '- Start a new session to reset the context window' + : agentError.type === 'rate_limited' + ? '- Wait a few minutes before retrying' + : agentError.type === 'network_error' + ? '- Check your internet connection and try again' + : '- Review the error message and take appropriate action', + '', + 'After resolving the issue, you can resume, skip, or abort the Auto Run.', + ].filter(Boolean).join('\n'); + + addHistoryEntryRef.current({ + type: 'AUTO', + summary: `Auto Run error: ${errorTitle}${currentDoc ? ` (${currentDoc})` : ''}`, + fullResponse: errorExplanation, + projectPath: session.cwd, + sessionId: actualSessionId, + success: false, + }); + } + + // Show toast notification for Auto Run error + if (addToastRef.current) { + const errorTitle = getErrorTitleForType(agentError.type); + addToastRef.current({ + type: 'error', + title: `Auto Run: ${errorTitle}`, + message: agentError.message, + sessionId: actualSessionId, + }); + } } } diff --git a/src/renderer/hooks/agent/useAgentSessionManagement.ts b/src/renderer/hooks/agent/useAgentSessionManagement.ts index 0291ca60..97d60e4f 100644 --- a/src/renderer/hooks/agent/useAgentSessionManagement.ts +++ b/src/renderer/hooks/agent/useAgentSessionManagement.ts @@ -19,6 +19,8 @@ export interface HistoryEntryInput { projectPath?: string; /** Optional override for background operations (prevents cross-agent bleed) */ sessionName?: string; + /** Whether the operation succeeded (false for errors/failures) */ + success?: boolean; } /** @@ -121,7 +123,9 @@ export function useAgentSessionManagement( ...(shouldIncludeContextUsage ? { contextUsage: activeSession?.contextUsage } : {}), // Only include usageStats if explicitly provided (per-task tracking) // Never use cumulative session stats - they're lifetime totals - usageStats: entry.usageStats + usageStats: entry.usageStats, + // Pass through success field for error/failure tracking + success: entry.success, }); // Refresh history panel to show the new entry diff --git a/src/renderer/hooks/batch/useBatchProcessor.ts b/src/renderer/hooks/batch/useBatchProcessor.ts index 74ad6618..e4425e9d 100644 --- a/src/renderer/hooks/batch/useBatchProcessor.ts +++ b/src/renderer/hooks/batch/useBatchProcessor.ts @@ -1358,14 +1358,17 @@ export function useBatchProcessor({ const pauseBatchOnError = useCallback((sessionId: string, error: AgentError, documentIndex: number, taskDescription?: string) => { if (!isMountedRef.current) return; + // Log detailed error to system logs with full context window.maestro.logger.autorun( - `Auto Run paused due to error: ${error.type}`, + `Auto Run paused due to ${error.type}: ${error.message}`, sessionId, { errorType: error.type, errorMessage: error.message, + recoverable: error.recoverable, documentIndex, - taskDescription + taskDescription, + rawError: error.raw } );