## CHANGES

- 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 🚀
This commit is contained in:
Pedram Amini
2025-12-29 12:46:53 -06:00
parent 7f223ec9e2
commit fa62d78b1c
5 changed files with 198 additions and 8 deletions

View File

@@ -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..."

View File

@@ -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": {

View File

@@ -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,
});
}
}
}

View File

@@ -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

View File

@@ -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
}
);