DCP was failing to find session messages because it was looking in
~/.config/opencode/sessions instead of ~/.local/share/opencode/storage.
Unified all hooks to use getOpenCodeStorageDir() for cross-platform consistency.
🤖 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>
- Remove eager SG_CLI_PATH constant; use getSgCliPath() lazily in checkEnvironment()
- Move setInterval to inside createCommentCheckerHooks() with guard flag
These changes eliminate module-level side effects that could trigger segfaults
during plugin initialization on Bun 1.3.5 + macOS 15 due to createRequire()
being called during module evaluation.
Fixes#292
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)
The DCP pruning modules were using a hardcoded path (~/.config/opencode/sessions) that doesn't exist.
Sessions are actually stored at ~/.local/share/opencode/storage/message.
All pruning modules now import MESSAGE_STORAGE from hook-message-injector, which uses the correct path via getOpenCodeStorageDir().
This fixes the issue where DCP would fail with 'message dir not found' when trying to recover from token limit errors.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
- Add customPrompt parameter to runCommentChecker function
- Pass --prompt flag to comment-checker CLI when custom_prompt is configured
- Wire up config from plugin initialization
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Add finally block in notifyParentSession() to ensure task cleanup
- Call tasks.delete(taskId) after notification sent or on error
- Prevents memory accumulation when tasks complete or fail
- taskId captured before setTimeout to ensure proper cleanup in async context
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Previously, the validator only checked for tool_use parts, causing 'Expected thinking but found text' errors when messages had text content. Renamed hasToolParts to hasContentParts to include both tool_use and text types.
Also added CommentCheckerConfigSchema support for custom prompt configuration.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Expand the search range when finding empty messages by index to better handle API index vs storage index mismatches. This increases robustness when searching for messages to sanitize with more fallback indices.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Convert session-manager storage layer from synchronous blocking I/O (readdirSync, readFileSync) to non-blocking async I/O (readdir, readFile from fs/promises). This fixes hanging issues in session_search and other tools caused by blocking filesystem operations.
Changes:
- storage.ts: getAllSessions, readSessionMessages, getSessionInfo now async
- utils.ts: Updated utility functions to be async-compatible
- tools.ts: Added await calls for async storage functions
- storage.test.ts, utils.test.ts: Updated tests with async/await patterns
This resolves the session_search tool hang issue and improves overall responsiveness.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Pre-emptively fix empty messages in sessions before running document compression to prevent summarization failures. This prevents accumulation of empty message placeholders that can interfere with context management.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
* feat(cli): librarian uses gemini-3-flash when hasGemini (antigravity auth)
Closes#294
* feat(cli): add explore to gemini-3-flash when hasGemini + update docs
* feat(cli): fix explore agent fallback logic to use haiku for max20 Claude users
- Use gemini-3-flash for both librarian and explore when hasGemini
- Use haiku for explore when Claude max20 is available (hasClaude && isMax20)
- Fall back to big-pickle for both when other models unavailable
- Updated all README files to document the fallback precedence
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Refactored DCP (Dynamic Context Pruning) to execute FIRST when token limit errors occur
- Previously, DCP only ran as a fallback after compaction failed
- Now DCP runs first to prune redundant context, then compaction executes immediately
- Simplified config flag: dcp_on_compaction_failure → dcp_for_compaction
- Updated documentation in all 4 README files (EN, KO, JA, ZH-CN)
- Updated schema.ts with new config field name and documentation
- Updated executor.ts with new DCP-first logic flow
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Add AntigravityTokenRefreshError custom error class with code, description, and status fields
- Implement parseOAuthErrorPayload() for parsing Google's various OAuth error response formats
- Add retry logic with exponential backoff (3 retries, 1s→2s→4s delay) for transient failures
- Add special handling for invalid_grant error - immediately throws without retry and clears caches
- Add invalidateProjectContextByRefreshToken() for selective cache invalidation
- Update fetch.ts error handling to work with new error class and cache invalidation
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
The installed_plugins.json file has two versions:
- v1: plugins stored as direct objects
- v2: plugins stored as arrays
Use discriminated union types (InstalledPluginsDatabaseV1/V2) for proper
type narrowing based on version field.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* fix: defer config error toast to session.created for TUI readiness
Removed showToast calls from loadConfigFromPath() function. Error notifications were not visible during plugin initialization because the TUI was not ready yet.
Changes:
- Removed immediate showToast calls from validation error handler
- Removed immediate showToast calls from file load error handler
- Errors are still captured via addConfigLoadError() for later display
- auto-update-checker hook will display errors via showConfigErrorsIfAny() after session.created event
This ensures error messages are displayed when the TUI is fully ready and able to render them properly.
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
* fix: await config error toast before showing startup toast
Ensure config errors are awaited and displayed before the startup spinner toast is shown. Changed showConfigErrorsIfAny(ctx).catch(() => {}) to await showConfigErrorsIfAny(ctx) to guarantee proper error handling order.
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
Make DCP behavior opt-in via new 'dcp_on_compaction_failure' experimental flag (disabled by default).
When enabled, Dynamic Context Pruning only executes after summarization fails, then retries compaction. By default, DCP runs before truncation as before.
Changes:
- Add 'dcp_on_compaction_failure' boolean flag to experimental config (default: false)
- Update executor.ts to check flag before running DCP behavior
- Add corresponding documentation to all 4 README files (EN, KO, JA, ZH-CN)
- Update JSON schema
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
When getContextWindowUsage returns null (no assistant messages yet, API
failure, or first request in session), the truncator was returning
untruncated output. This caused context overflow crashes on early
requests or when usage lookup failed.
Now applies conservative truncation (50k tokens) as fallback, preventing
prompt-too-long errors that crash sessions.
Fixes#276 - The look_at tool was constructing invalid file:// URLs on Windows
by using template literals. Now uses Node.js pathToFileURL() which correctly
handles backslashes, spaces, and the triple-slash prefix required on Windows.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
The test mock for ctx.$ was not handling tagged template literals correctly,
causing it to ignore interpolated values. Additionally, utility functions that
check for command availability (osascript, notify-send, etc.) were returning
null in test environments, causing sendNotification to exit early.
Changes:
- Fixed template literal reconstruction in mock $ function
- Added spyOn mocks for all utility path functions
- All session-notification tests now passing (11/11)
Fixes#273
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
- Add process.exit(0) in runner.ts for immediate termination
- Fix Timer type to ReturnType<typeof setInterval> in manager.ts
- Add .unref() to BackgroundManager polling interval
- Add cleanup() method to BackgroundManager
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Add new CLI command 'get-local-version' to display current version and check for updates
- Reuses existing version checking infrastructure from auto-update-checker
- Supports both human-readable and JSON output formats
- Handles edge cases: local dev mode, pinned versions, network errors
- Provides colored terminal output with picocolors
- Closes#260
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Fixes#255
- Add getClaudeConfigDir() utility function that respects CLAUDE_CONFIG_DIR env var
- Update all hardcoded ~/.claude paths to use the new utility
- Add comprehensive tests for getClaudeConfigDir()
- Maintain backward compatibility with default ~/.claude when env var is not set
Files updated:
- src/shared/claude-config-dir.ts (new utility)
- src/shared/claude-config-dir.test.ts (tests)
- src/hooks/claude-code-hooks/config.ts
- src/hooks/claude-code-hooks/todo.ts
- src/hooks/claude-code-hooks/transcript.ts
- src/features/claude-code-command-loader/loader.ts
- src/features/claude-code-agent-loader/loader.ts
- src/features/claude-code-skill-loader/loader.ts
- src/features/claude-code-mcp-loader/loader.ts
- src/tools/session-manager/constants.ts
- src/tools/slashcommand/tools.ts
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
- Add thinking-block-validator hook for proactive prevention before API calls
- Enhance session-recovery to include previous thinking content
- Fix hook registration to actually invoke the validator
Addresses extended thinking errors with Claude Opus/Sonnet 4.5 using tool calls.
Related: https://github.com/vercel/ai/issues/7729
Related: https://github.com/sst/opencode/issues/2599
Include output tokens from last response in getContextWindowUsage calculation.
Output tokens become part of next request's input (conversation history), so
they must be counted to avoid overestimating remainingTokens. This aligns with
preemptive-compaction's calculation which already includes output tokens correctly.
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Apply dynamic truncation to rules-injector, directory-readme-injector, and directory-agents-injector
- Add truncation notice encouraging users to read full content
- Save context window space while maintaining awareness of complete documentation
- Resolves#221 (part 1)
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
- Renamed sisyphus_agent.builder_enabled to default_builder_enabled for clarity
- Removed sisyphus_agent.replace_build option entirely
- Default build agent is now always demoted to subagent mode when Sisyphus is enabled
- Updated schema and regenerated JSON schema
- Updated all documentation (EN, KO, JA, ZH-CN)
BREAKING CHANGE: Configuration migration required for users using builder_enabled or replace_build options.
Closes#250
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
* fix: show error messages when oh-my-opencode.json config fails to load
- Add console.error output for config parse errors (syntax errors)
- Add console.error output for config validation errors (schema violations)
- Display helpful hints for JSON syntax errors
- List all validation errors clearly with proper formatting
- Errors now shown immediately regardless of hook configuration
Fixes#241
* refactor: replace console.error with toast notifications for config errors
- Replace console.error with ctx.client.tui.showToast() for better UX
- Show toast notifications for both syntax errors and validation errors
- Toast notifications persist for 10 seconds for visibility
- Display error details with bullet points for validation errors
- Include helpful hints for JSON syntax errors
This provides a more user-friendly notification system that integrates
with OpenCode's UI instead of just logging to console.
---------
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Fixes#191
This commit ensures that the user's selected model is preserved when
background tasks complete and notify their parent sessions.
Changes:
- Add parentModel field to BackgroundTask and LaunchInput interfaces
- Capture model context when launching background tasks
- Pass model context when notifying parent sessions after task completion
Impact:
- Users with OAuth providers (Google, Anthropic) will now have their
model selection preserved across background task continuations
- Background agents no longer revert to hardcoded defaults
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
When session.error occurs with incomplete todos, the run command now:
- Captures the error via handleSessionError()
- Exits with code 1 instead of waiting indefinitely
- Shows clear error message to user
Previously, run command ignored session.error events, causing infinite
'Waiting: N todos remaining' loop when agent errors occurred.
🤖 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>
- logEventVerbose() logs all event types including message content, tool calls, and results
- Session tags distinguish main vs child sessions for multi-session tracking
- completion.ts error logging instead of silently swallowing API errors
- Helps diagnose realtime streaming behavior in CI environments
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
- Remove DEFAULT_TIMEOUT_MS (set to 0) to allow CI agent runs to complete without timeout
- Add stdbuf -oL -eL for unbuffered realtime output in GitHub Actions
- Update timeout logic to only set timeout when value > 0
This fixes CI agent runs that were timing out after 30 minutes and not showing realtime output.
🤖 Generated with assistance of OhMyOpenCode
Previously, the condition '&&plannerEnabled&&replacePlan' caused agents to be
completely removed instead of demoted to subagent mode. The logic incorrectly
prevented agents from being added back as subagents when Sisyphus is enabled
with default config.
Fixed by simplifying to just 'replacePlan' condition - agents are now properly
demoted to subagent mode when replacement is enabled, which is the intended
behavior per the README.
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Renamed agent from 'Builder-Sisyphus' to 'OpenCode-Builder' in schema and config
- Removed BUILD_SYSTEM_PROMPT and BUILD_PERMISSION custom overrides
- Now uses OpenCode's default build agent configuration exactly
- Simplified agent configuration to rely on OpenCode defaults
🤖 Generated with assistance of OhMyOpenCode
Fixes#200
## Problem
When executeCompact() recovery fails unexpectedly or gets interrupted,
the compactionInProgress lock is never cleared, permanently blocking both
auto-compact AND manual /compact for the session.
## Root Cause
- No try/finally around lock acquisition (line 261)
- Silent blocking when lock held - no user feedback
- Lock cleanup scattered across 7 manual deletion points
- Any unexpected exception bypasses cleanup, leaving lock stuck forever
## Solution
1. **Try/Finally Lock Guarantee**: Wrapped entire executeCompact body in
try/finally block to guarantee lock cleanup, following the pattern
used in preemptive-compaction hook
2. **User Feedback**: Added toast notification when compact attempt is
blocked by existing lock, replacing silent failure with clear warning
3. **Removed Redundancy**: Removed 6 redundant manual lock deletions
(kept only clearSessionState and finally block)
## Testing Evidence
✅ 10/10 comprehensive tests pass
✅ Lock cleared on successful completion
✅ Lock cleared when summarize throws
✅ Lock cleared when revert throws
✅ Lock cleared when fixEmptyMessages executes
✅ Lock cleared when truncation is sufficient
✅ Lock cleared after max recovery attempts
✅ Lock cleared when toast fails
✅ Lock cleared when prompt_async throws
✅ Toast shown when lock already held
✅ TypeScript type check passes with zero errors
## Files Changed
- executor.ts: Added try/finally, toast notification, removed 6 redundant deletions
- executor.test.ts: New comprehensive test suite (10 tests, 13 assertions)
## Impact
- Severity: High → Fixed
- User Experience: No more stuck sessions requiring restart
- Behavior: Identical except lock now guaranteed to clear
Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
- Filter out original 'build' and 'plan' agents when Builder-Sisyphus/Planner-Sisyphus are enabled with replacement
- Previously both agents could coexist even with replace_build/replace_plan: true
- Now only the replacement agent exists when both enabled and replacement flags are true
- Maintains backward compatibility for all configuration combinations
Fixes#231
The SDK yields events directly as the payload without wrapping in { payload: ... }.
Changed processEvents to treat event as the payload directly instead of looking
for event.payload. This fixes the 'Waiting for completion...' hang in GitHub
Actions where all events were being silently skipped.
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Added message.part.updated event handling for incremental text streaming
- Added tool.execute event to display tool calls with input previews
- Added tool.result event to show truncated tool result outputs
- Enhanced EventState with lastPartText and currentTool tracking
- Defined MessagePartUpdatedProps, ToolExecuteProps, ToolResultProps types
- Updated event tests to cover new state fields
This enables the CLI run command to display real-time agent output similar to the native opencode run command, improving user experience with immediate feedback on tool execution.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
Add missing mock exports (SESSION_LIST_DESCRIPTION, SESSION_READ_DESCRIPTION,
SESSION_SEARCH_DESCRIPTION, SESSION_INFO_DESCRIPTION, SESSION_DELETE_DESCRIPTION,
TOOL_NAME_PREFIX) to fix test failures when other test files import from constants.
🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)