When injecting continuation prompts, extract and pass the model field
(providerID + modelID) from the nearest stored message, matching the
pattern used in background-agent/manager.ts and session-recovery.
Also updated tests to capture the model field for verification.
Replace the time-based ERROR_COOLDOWN_MS mechanism with an event-order based
lastEventWasAbortError flag. This fixes the bug where abort errors permanently
block todo continuation since session.idle only fires once during the cooldown.
Now abort errors only skip continuation when they occur IMMEDIATELY before
session.idle event. Any intervening event (message, tool execution) clears the
abort state, allowing normal continuation flow on the next idle.
Comprehensive tests added to verify:
- Abort detection correctly blocks continuation when immediately before idle
- Intervening events (assistant message, tool execution) clear abort state
- Non-abort errors don't block continuation
- Multiple abort events (only last one matters)
- No time-based throttle preventing consecutive injections
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Background task sessions registered in subagentSessions were not receiving
todo-continuation prompts, causing a deadlock: background tasks waited for
continuation that never came.
Changes:
- Allow both main session and background task sessions to receive continuation
- Add test for background task session continuation behavior
- Cleanup subagentSessions in test setup/teardown
This fixes the deadlock introduced in commit 116a90d which added todo waiting
logic to background-agent/manager.ts.
🤖 Generated with assistance of OhMyOpenCode
Removes the complex state machine and 10-second throttle (MIN_INJECTION_INTERVAL_MS)
that was causing background task completion to hang. The hook now:
- Uses straightforward error cooldown logic instead of complex injection throttling
- Removes unnecessary state tracking that was delaying continuation injection
- Maintains all safety checks (recovery mode, running tasks, error state)
- Keeps countdown behavior with toast notifications
Fixes#312 - Resolves v2.7.0 issue where background task completion would freeze
the agent due to injection delays.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Previously, errorBypass mode was cleared on session.idle, causing continuation
to fire again on next idle event. This led to unwanted task resumption after
user abort.
Changes:
- Don't clear errorBypass on session.idle - stay in errorBypass mode
- Clear errorBypass to idle only when user sends a new message
This ensures that once user aborts, the enforcer respects that decision until
the user explicitly sends a message to resume.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Fixes race conditions, enables continuous enforcement, and eliminates false positives/negatives.
- Complete redesign using version-token state machine for race condition prevention
- Replaced 5 separate Sets with single Map<sessionID, SessionState>
- Changed cancelCountdown() to invalidate() that ALWAYS bumps version regardless of mode
- Added background task check BEFORE starting countdown (prevents toast spam when bg tasks running)
- Added lastAttemptedAt throttling (10s minimum between attempts, set BEFORE API call)
- Removed non-interactive preemptive injection (all paths now use countdown)
- Added 3 version checks in executeInjection (start, after todo fetch, before API call)
- Removed remindedSessions flag for continuous enforcement
Fixes:
1. Race condition where session.idle fired before message.updated cleared reminded state
2. Single-shot behavior that prevented multiple reminders
3. Phantom reminders sent even after agent started working
4. Toast spam when background tasks are running
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
* fix: disable todo-continuation for plan mode agents
Plan mode agents (e.g., 'plan', 'Planner-Sisyphus') only analyze and plan,
they don't implement. The todo-continuation hook was incorrectly triggering
for these agents because the existing write permission check only looked at
the stored message's tools field, not the agent's permission configuration.
This fix adds an explicit check for plan mode agents by name to skip the
todo continuation prompt injection.
Fixes#293
* chore: changes by sisyphus-dev-ai
* fix: address review comments for plan mode agent check
- Use exact match for plan mode agents instead of substring match to
prevent false positives on agents like 'deployment-planner'
- Add plan mode agent check to preemptive injection path (non-interactive
mode) which was missing from the initial fix
---------
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Refactored state management to use a single source of truth per-session using
a state machine pattern with versioning. Key improvements:
- Replace multiple Sets with unified SessionState map for cleaner logic
- Add version tokens to invalidate pending callbacks on state changes
- Improve countdown timer management with proper cleanup
- Add throttle check to prevent rapid injection spam (10s minimum interval)
- Enhance injection checks: re-verify todos before injection, check bg tasks
- Handle message.part.updated events for streaming activity detection
- Add isMainSession() helper for consistent session filtering
- Clearer event handler logic with inline comments explaining state transitions
- Better logging for debugging state changes and decision points
State modes: idle → countingDown → injecting → idle (with recovery/errorBypass)
Prevents race conditions from async operations and UI state changes during countdown.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Fixes the race condition where the todo continuation hook would inject a
continuation prompt even when all todos had been completed during the
countdown period.
The root cause was that executeAfterCountdown() used stale todo data from
the initial session.idle check without re-verifying that incomplete todos
still existed after the countdown finished.
Changes:
- Add fresh todo verification in executeAfterCountdown() before prompt injection
- Use fresh todo data in the continuation prompt message
- Abort injection if no incomplete todos remain after countdown
This properly handles the case where:
1. session.idle fires (e.g., user enters shell mode in TUI)
2. Initial check finds incomplete todos, starts countdown
3. During countdown, todos get completed
4. Countdown ends, fresh check detects no incomplete todos
5. Hook aborts instead of injecting stale prompt
Fixes#234
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
- Add new 'run' command using @opencode-ai/sdk to manage agent sessions
- Implement recursive descendant session checking (waits for ALL nested child sessions)
- Add completion conditions: all todos done + all descendant sessions idle
- Add SSE event processing for session state tracking
- Fix todo-continuation-enforcer to clean up session tracking
- Comprehensive test coverage with memory-safe test patterns
Unlike 'opencode run', this command ensures the agent completes all tasks
by recursively waiting for nested background agent sessions before exiting.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Detects non-interactive environments (CI, opencode run) and prevents session idle when:
- Background tasks are still running
- Incomplete todos remain in the queue
Changes:
- Add isNonInteractive() detector for CI/headless environment detection
- Export detector from non-interactive-env hook module
- Enhance todo-continuation-enforcer to inject prompts BEFORE session.idle
- Pass BackgroundManager to todo-continuation-enforcer for task status checks
This fix prevents `opencode run` from exiting prematurely when work is pending.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Previously, remindedSessions was only cleared when assistant finished
with finish=true. If agent stopped mid-task (ESC, error), the session
stayed 'reminded' forever, preventing future continuations.
Now also clears remindedSessions when user sends a new message,
allowing continuation to trigger again after user interaction.
- Fixed bug where remindedSessions was never cleared after assistant response
- Now clears reminded state when assistant finishes (finish: true)
- Allows TODO continuation to trigger again after each assistant response
- Ensures continuation prompt can be injected multiple times if needed in long sessions
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Reduce COUNTDOWN_SECONDS from 5 to 2 for faster reminder display
- Remove logic that clears remindedSessions on assistant response to prevent re-triggering
- Ensures todo continuation reminder displays exactly once per session
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Changes:
- Add main session check: skip toast for subagent sessions
- Move todo validation BEFORE countdown: only start countdown when incomplete todos actually exist
- Improve toast message to show remaining task count
This fixes the issue where countdown toast was showing on every idle event, even when no todos existed or in subagent sessions.
Implement countdown toast feature showing visual feedback before todo continuation:
- Changed from 5-second timeout to interval-based countdown
- Shows toast every second: "Resuming in 5s...", "Resuming in 4s...", etc.
- Toast duration set to 900ms to prevent overlap
- Countdown cancels on user message, session error, or session deletion
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Increase delay from 200ms to 5000ms to prevent firing too quickly before users can respond
- Add write permission check to skip continuation when previous agent lacks write/edit permissions
- Fixes destructive behavior where hook was overriding user wait commands
Resolves#89🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Fixed bug where remindedSessions was only cleared on user messages. Now also
clears on assistant response, enabling the todo continuation reminder to be
re-triggered on the next idle period after the assistant provides a response.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Message hooks like todo-continuation-enforcer and background-notification
now preserve the agent mode from the previous message when sending follow-up
prompts. This ensures that continuation messages and task completion
notifications use the same agent that was active in the conversation.
- Export findNearestMessageWithFields and MESSAGE_STORAGE from hook-message-injector
- Add getMessageDir helper to locate session message directories
- Pass agent field to session.prompt in todo-continuation-enforcer
- Pass agent field to session.prompt in BackgroundManager.notifyParentSession
Closes#59🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Replace blocking await with non-blocking timer scheduling to handle race
condition between session.idle and session.error events. When ESC abort
occurs, session.error immediately cancels the pending timer, preventing
unwanted continuation prompts.
Changes:
- Add pendingTimers Map to track scheduled continuation checks
- Cancel timer on session.error (especially abort cases)
- Cancel timer on message.updated and session.deleted for cleanup
- Reduce delay to 200ms for faster response
- Maintain existing Set-based flag logic for compatibility
This fixes the issue where ESC abort would not prevent continuation
prompts due to event ordering (idle before error)
- Replace multiple Set-based tracking with explicit SessionStatus state machine
- Implement setTimeout+clearTimeout pattern for robust race condition handling
- SessionStatus tracks: idle → continuation-sent or aborted states
- Increase grace period to 500ms to accommodate event ordering delays
- Add cleanupSession utility for proper resource cleanup
This addresses ESC abort not canceling continuation prompts when session.idle
arrives before session.error event, which can occur due to async event processing
in OpenCode plugin system