- Rename wizard header from "Create a Maestro Agent" to "New Agent Wizard"
- Add info tip box explaining in-tab wizard alternative (/wizard command, wand button)
- Only show tip box when user has existing agents (first-time users won't see it)
- Update note copy: "The new agent wizard captures application inputs until complete"
- Add final sentence: "The in-tab wizard runs alongside your other work"
- Move "Select the provider" text directly above agent tiles
- Center note box vertically with equal spacing
- Update test mocks with sessions.getAll and sshRemote.getConfigs
- Added IPC handler to fetch earliest stats timestamp for smarter ranges 🕰️
- Implemented StatsDB query to compute oldest event across all tables 🧮
- Introduced IPC stubs for stats initialization/reset notifications groundwork 🧱
- Added “New Agent” action to hamburger menu with proper shortcut hint ➕
- Reordered menu to separate New Agent, Wizard, and Tour actions clearly 🧭
- Upgraded menu links: dedicated Website and Documentation entries with icons 🌐
- Polished external-link UX by showing an explicit external-link indicator 🔗
- Restored “Check for Updates” as a focused menu action at the bottom 📥
When Codex reads prompts from stdin, it outputs "Reading prompt from
stdin..." followed by the actual response to stderr. This caused the
response to appear in red STDERR blocks in the UI.
Fix: Detect and strip the "Reading prompt from stdin..." prefix for
Codex sessions, then emit the actual response content as regular data
instead of stderr.
Added StderrHandler tests covering:
- SSH informational message filtering
- Codex stdin response extraction
- Buffer accumulation
Shows a modal on first launch for Windows users explaining that Windows
support is actively being improved. Features:
- Inline toggle to enable beta updates for latest bug fixes
- Link to report issues on GitHub (with note that vetted PRs welcome)
- Link to join Discord Windows-specific channel for community support
- Create Debug Package button for easy bug reporting
- Checkbox to suppress the modal for future sessions
- Console debug function: window.__showWindowsWarningModal()
- Add shrink-0 to AI Tab and FileTab containers to prevent flex
compression, ensuring tabs maintain natural width for all content
including extension badge and close button
- Use double requestAnimationFrame for scroll-into-view to ensure
DOM has fully rendered (including conditional close button) before
measuring tab width for scrolling
- Replace scrollIntoView with manual scrollLeft calculation to ensure
the ENTIRE tab (including right edge with close button) is visible,
not just enough to show some portion of the tab
- Update scroll behavior tests to match new implementation
The AgentSelectionScreen now uses Info and Wand2 icons from lucide-react
(added for the inline wizard tip box). Update the test mock to include
these icons so tests don't fail with missing export errors.
When pasting content, automatically trim leading and trailing whitespace
to reduce manual cleanup. The trimming only intercepts paste when there
is actual whitespace to remove, otherwise native paste behavior is used.
Input areas updated:
- Main AI/terminal input (App.tsx)
- Group chat input (GroupChatInput.tsx)
- Prompt composer modal (PromptComposerModal.tsx)
- Auto Run document editor (useAutoRunImageHandling.ts)
- Wizard document editor (DocumentEditor.tsx)
- Inline wizard document generation (DocumentGenerationView.tsx)
- Move "Select the provider" text directly above agent tiles for better flow
- Add info box explaining keyboard capture and alternatives (/wizard command, wand button)
- Display inline Wand2 icon for visual clarity
- Center note box vertically with equal spacing above/below
- Update test mocks to include Info, Wand2, and AlertTriangle icons
- Move "Select the provider" text directly above agent tiles for better flow
- Add info box explaining keyboard capture and alternatives (/wizard command, wand button)
- Display inline Wand2 icon for visual clarity
- Update test mocks to include Info, Wand2, and AlertTriangle icons
Add new Custom Configuration section documenting custom CLI arguments and
environment variables with screenshot. Rename navigation group to "Providers
& CLI" and reorder to list provider-notes first.
Enable stream-json mode for SSH stdin script execution. When SSH wraps
agent commands, the args being checked (SSH args like -o BatchMode=yes)
don't contain 'stream-json', causing JSON output to bypass the parser.
Added sshStdinScript to isStreamJsonMode detection so remote agent
output is properly parsed through the JSON output parser.
- Add addBreadcrumb() utility to track user actions before crashes
- Add startMemoryMonitoring() with configurable threshold and interval
- Record breadcrumbs at agent spawn/kill operations
- Auto-start memory monitoring when Sentry initializes (500MB threshold, 1min interval)
This provides context in crash reports to help identify patterns
leading to renderer crashes (MAESTRO-5A/4Y).
Relates to MAESTRO-5A, MAESTRO-4Y
Add try-catch wrapper and isDestroyed() check to prevent "reply was
never sent" errors when the window closes during dialog operations.
Fixes MAESTRO-58
Wrap console output in try-catch to gracefully handle EPIPE errors
that occur when a parent process consuming output dies unexpectedly.
Fixes MAESTRO-5C
- Symphony modal now receives sessions list for richer contribution context 🧩
- Active contributions show clickable session name with new Terminal icon 🖥️
- Added “navigate to session” flow directly from contribution cards 🧭
- Selecting a session updates active session and auto-closes modal ⚡
- Session lookup wires contributions to matching session IDs seamlessly 🔗
- Renamed empty-state action from “Select Folder” to “Change Folder” for clarity 🧭
- Refreshed empty-state helper text to match the new folder-change flow 📝
- Cleaned up `EditGroupChatModal` icon imports by dropping the unused `X` 🎛️
- Symphony IPC now validates active contributions against stored sessions 🧩
- Orphaned contributions auto-filtered when sessions disappear, keeping UI clean 🧹
- `symphony:getState` returns only session-backed active items for accuracy 🎯
- `symphony:getActive` now excludes contributions tied to missing sessions 🚫
- Added reusable `filterOrphanedContributions` helper with detailed logging 🪵
- Wired `sessionsStore` dependency through main + handler registration flow 🔌
- Integration tests now mock sessions store for realistic state scenarios 🧪
- Expanded handler tests to cover missing-session filtering behavior 🛡️
Merges OpenCode fixes and SSH stdin improvements:
- Use stdin passthrough for all SSH prompts (simplifies escaping)
- Add question:deny to OpenCode permission block for robust tool disabling
- Simplify stdin prompt delivery by appending after exec (no heredoc needed)
Per OpenCode GitHub issue workaround, add "question": "deny" to the
permission block in addition to the existing "tools":{"question":false}.
This ensures the question tool is disabled via both configuration methods,
preventing stdin hangs in batch mode.
Config now: {"permission":{"*":"allow","external_directory":"allow","question":"deny"},"tools":{"question":false}}
Replace heredoc-based prompt delivery with simpler stdin passthrough.
How it works:
1. Bash script is sent via stdin to /bin/bash on the remote
2. Script sets up PATH, cd, env vars, then calls `exec <agent>`
3. The `exec` replaces bash with the agent process
4. The agent inherits stdin and reads the remaining content (the prompt)
Benefits:
- No heredoc syntax needed
- No delimiter collision detection
- No prompt escaping required - prompt is never parsed by any shell
- Works with any prompt content (quotes, newlines, $, backticks, etc.)
- Simpler, more maintainable code
Changes:
- Remove heredoc logic from buildSshCommandWithStdin()
- Update process.ts to use stdin passthrough for ALL agents over SSH
(not just OpenCode - all agents benefit from this approach)
- Update tests to verify stdin passthrough behavior
Verified locally that both OpenCode and Claude Code read prompts from stdin.
When Claude Code performs multi-tool turns (many internal API calls),
accumulated token values cause estimateContextUsage to return null,
freezing the context gauge. This adds estimateAccumulatedGrowth which
provides conservative 1-3% per-turn growth estimates so the gauge
keeps moving during tool-heavy sessions.
Safety: App.tsx caps all estimates at yellowThreshold - 5, guaranteeing
that estimates can never trigger compact warnings — only real
measurements from non-accumulated turns can.
Update SummaryCards to accept sessions prop and filter out terminal-only
sessions when calculating agent count for consistent metrics display.
This ensures queries-per-session and total sessions accurately reflect
AI agent sessions rather than including terminal sessions.
Replace process.platform with navigator.platform for Windows detection
in the inline wizard conversation service, as process.platform is not
available in the browser/renderer context.
Fixes an issue in PR #288's Windows shell fix where full paths like
'C:\Program Files\Git\bin\git' weren't recognized as known commands.
Changes:
- Extract command basename using regex for both Unix and Windows separators
- Change from array to Set for O(1) lookup performance
- Add additional common commands: npx, pnpm, pip, pip3
- Export needsWindowsShell for testability
- Add comprehensive test suite for needsWindowsShell function
IMPORTANT: Prompts must be passed via stdin to avoid CLI argument length
limits. Prompts can be huge and contain arbitrary characters that would
break if passed as command-line arguments.
Changes:
- Add stdinInput parameter to buildSshCommandWithStdin for heredoc-based
prompt delivery
- Use MAESTRO_PROMPT_EOF delimiter with collision detection (appends _N
suffix if prompt contains the delimiter)
- OpenCode prompts now always sent via stdin heredoc, not CLI args
- Add comprehensive tests for heredoc behavior and delimiter collision
- Add comment in process.ts documenting this requirement to prevent
regressions
The heredoc approach: exec opencode 'run' <<'MAESTRO_PROMPT_EOF'
ensures prompts of any size with any characters work correctly.
This is a complete rewrite of SSH remote command execution that eliminates
all shell escaping issues by sending the entire script via stdin.
Previously, the SSH command was built as:
ssh host '/bin/bash -c '\''cd /path && VAR='\''value'\'' cmd arg'\'''
This required complex nested escaping that broke with:
- Heredocs (cat << 'EOF')
- Long prompts (command line length limits)
- Special characters in prompts
Now the SSH command is simply:
ssh host /bin/bash
And the entire script is piped via stdin:
export PATH="$HOME/.local/bin:..."
cd '/project/path'
export OPENCODE_CONFIG_CONTENT='{"permission":...}'
exec opencode run --format json 'prompt here'
Benefits:
- No shell escaping layers (stdin is binary-safe)
- No command line length limits
- Works with any remote shell (bash, zsh, fish)
- Handles any prompt content (quotes, newlines, $, etc.)
- Much simpler to debug and maintain
Changes:
- Add buildSshCommandWithStdin() in ssh-command-builder.ts
- Update process.ts to use stdin-based SSH for all agents
- Add sshStdinScript to ProcessConfig type
- Update ChildProcessSpawner to send stdin script
- Add comprehensive tests for new function
The heredoc syntax (cat << 'EOF' ... EOF) was breaking when passed
through buildSshCommand's single-quote escaping. The '\'' escape
pattern was being applied to the heredoc delimiters, producing
invalid shell syntax like cat << '\''EOF'\''.
Solution: Embed OpenCode prompts directly as positional arguments.
The prompt will be properly escaped by buildRemoteCommand using
shellEscape(), which handles the single-quote escaping correctly
for bash -c command execution.
This was the root cause of SSH remote execution failures with
OpenCode - the OPENCODE_CONFIG_CONTENT env var escaping was
correct, but the heredoc escaping was not.
Changed from centering the tab to using scrollIntoView with 'nearest' option.
This ensures the entire tab including the close button is visible, rather than
potentially cutting off the right edge when near container boundaries.
Add symphony:manualCredit IPC handler to allow crediting contributions
made outside the Symphony workflow (e.g., manual PRs, external
contributors). This enables proper tracking of all contributions
regardless of how they were created.
- Add symphony:manualCredit handler with full validation
- Add preload API for manual credit
- Support all contribution fields (tokens, time, merged status, etc.)
- Prevent duplicate PR credits
- Update contributor stats (streak, repos, totals)
- Add comprehensive tests for validation and success cases
When Claude Code performs multi-tool turns (many internal API calls),
accumulated token values cause estimateContextUsage to return null,
freezing the context gauge. This adds estimateAccumulatedGrowth which
provides conservative 1-3% per-turn growth estimates so the gauge
keeps moving during tool-heavy sessions.
Safety: App.tsx caps all estimates at yellowThreshold - 5, guaranteeing
that estimates can never trigger compact warnings — only real
measurements from non-accumulated turns can.
On Windows, when execFile detects 'git' without extension, it enables shell
mode for PATHEXT resolution. However, shell mode interprets '%' characters in
arguments as environment variable expansions, causing 'git log --pretty=format:%an'
to fail with 'Der Befehl "%an" ist entweder falsch geschrieben...'
The fix adds 'git' (and other known .exe commands) to a list of exceptions
that don't require shell mode, allowing the format string to pass through
to git unchanged. This works because these commands have .exe variants on
Windows and don't need PATHEXT resolution.
IMPORTANT: Prompts must be passed via stdin to avoid CLI argument length
limits. Prompts can be huge and contain arbitrary characters that would
break if passed as command-line arguments.
Changes:
- Add stdinInput parameter to buildSshCommandWithStdin for heredoc-based
prompt delivery
- Use MAESTRO_PROMPT_EOF delimiter with collision detection (appends _N
suffix if prompt contains the delimiter)
- OpenCode prompts now always sent via stdin heredoc, not CLI args
- Add comprehensive tests for heredoc behavior and delimiter collision
- Add comment in process.ts documenting this requirement to prevent
regressions
The heredoc approach: exec opencode 'run' <<'MAESTRO_PROMPT_EOF'
ensures prompts of any size with any characters work correctly.
This is a complete rewrite of SSH remote command execution that eliminates
all shell escaping issues by sending the entire script via stdin.
Previously, the SSH command was built as:
ssh host '/bin/bash -c '\''cd /path && VAR='\''value'\'' cmd arg'\'''
This required complex nested escaping that broke with:
- Heredocs (cat << 'EOF')
- Long prompts (command line length limits)
- Special characters in prompts
Now the SSH command is simply:
ssh host /bin/bash
And the entire script is piped via stdin:
export PATH="$HOME/.local/bin:..."
cd '/project/path'
export OPENCODE_CONFIG_CONTENT='{"permission":...}'
exec opencode run --format json 'prompt here'
Benefits:
- No shell escaping layers (stdin is binary-safe)
- No command line length limits
- Works with any remote shell (bash, zsh, fish)
- Handles any prompt content (quotes, newlines, $, etc.)
- Much simpler to debug and maintain
Changes:
- Add buildSshCommandWithStdin() in ssh-command-builder.ts
- Update process.ts to use stdin-based SSH for all agents
- Add sshStdinScript to ProcessConfig type
- Update ChildProcessSpawner to send stdin script
- Add comprehensive tests for new function
The heredoc syntax (cat << 'EOF' ... EOF) was breaking when passed
through buildSshCommand's single-quote escaping. The '\'' escape
pattern was being applied to the heredoc delimiters, producing
invalid shell syntax like cat << '\''EOF'\''.
Solution: Embed OpenCode prompts directly as positional arguments.
The prompt will be properly escaped by buildRemoteCommand using
shellEscape(), which handles the single-quote escaping correctly
for bash -c command execution.
This was the root cause of SSH remote execution failures with
OpenCode - the OPENCODE_CONFIG_CONTENT env var escaping was
correct, but the heredoc escaping was not.
Changed from centering the tab to using scrollIntoView with 'nearest' option.
This ensures the entire tab including the close button is visible, rather than
potentially cutting off the right edge when near container boundaries.