- Add session.status re-check in stability detection before completing
- Reset stablePolls if session is not idle (agent still working)
- Fix statusText to show CANCELLED instead of COMPLETED for non-completed tasks
- Root AGENTS.md: Updated timestamp, commit hash, line counts
- src/agents/AGENTS.md: Updated to 50 lines, current structure
- src/cli/AGENTS.md: Updated to 57 lines, current structure
- src/features/AGENTS.md: Updated to 65 lines, current structure
- src/hooks/AGENTS.md: Updated to 53 lines, current structure
- src/shared/AGENTS.md: Updated to 52 lines, core utilities
- src/tools/AGENTS.md: Updated to 50 lines, tool categories
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- Added withOperationRetry<T>() helper method that retries operations up to 3 times
- Catches "Not connected" errors (case-insensitive)
- Cleans up stale client before retry
- Modified callTool, readResource, getPrompt to use retry logic
- Added tests for retry behavior (3 new test cases)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
fix: pass model parameter when resuming background tasks
Ensure resumed tasks maintain their original model configuration
from category settings, preventing unexpected model switching.
Previous test was setting mainSessionID to 'main-session-123' and the
next test expected undefined. The outer beforeEach wasn't properly
resetting state between tests in the nested describe block.
Adding a nested beforeEach ensures proper test isolation.
category-default is the intended behavior for builtin categories,
not a fallback. Only show toast warning when:
- inherited: model from parent session (custom category without model)
- system-default: OpenCode's global default model
User-defined and category-default are both expected behaviors,
so no warning is needed.
The transform hook was failing to inject synthetic parts because
message.info.sessionID is not always available in the OpenCode SDK.
Fix: Use getMainSessionID() as fallback when message.info.sessionID is undefined.
This ensures keyword-detector and claude-code-hooks content (like ulw/ultrawork)
is properly injected even when the SDK doesn't provide sessionID in message.info.
- Remove injectPendingContext function (no longer needed)
- Remove createContextInjectorHook function (chat.message hook removed)
- Change transform hook from prepend to synthetic part insertion
- Follow empty-message-sanitizer pattern (minimal field set)
- synthetic: true flag hides content from UI but passes to model
- Synthetic part inserted BEFORE user text part
The resume method was not passing the stored model from the task,
causing Sisyphus-Junior to revert to the default model when resumed.
This fix adds the model to the prompt body in resume(), matching
the existing behavior in launch().
Fixes#826
OpenCode API returns different structures for user vs assistant messages:
- User: info.model = { providerID, modelID } (nested)
- Assistant: info.modelID, info.providerID (flat top-level)
Previous code only checked nested format, causing model info loss when
continuation hooks fired after assistant messages.
Files modified:
- todo-continuation-enforcer.ts
- ralph-loop/index.ts
- sisyphus-task/tools.ts
- background-agent/manager.ts
Added test for assistant message model extraction.
The prometheus-md-only hook was not enforcing file restrictions because
getSessionAgent() returned undefined - setSessionAgent was only called
in message.updated event which doesn't always provide agent info.
- Add setSessionAgent call in chat.message hook when input.agent exists
- Add session state tests for setSessionAgent/getSessionAgent
- Add clearSessionAgent cleanup to prometheus-md-only tests
This ensures prometheus-md-only hook can reliably identify Prometheus
sessions and enforce .sisyphus/*.md write restrictions.
The watermark (commit footer and co-author) was inconsistently applied because:
1. The skill tool didn't receive gitMasterConfig
2. The approach was 'default ON, inject DISABLED override' which LLMs sometimes ignored
This refactors to 'inject only when enabled' approach:
- Remove hardcoded watermark section from base templates
- Dynamically inject section 5.5 based on config values
- Default is still ON (both true when no config)
- When both disabled, no injection occurs (clean prompt)
Also fixes missing config propagation to skill tool and createBuiltinAgents.
feat(concurrency): prevent background task races and leaks
Summary
Fixes race conditions and memory leaks in the background task system that could cause "all tasks complete" notifications to never fire, leaving parent sessions waiting indefinitely.
Why This Change?
When background tasks are tracked for completion notifications, the system maintains a pendingByParent map to know when all tasks for a parent session are done. Several edge cases caused "stale entries" to accumulate in this map:
1. Re-registering completed tasks added them back to pending tracking, but they'd never complete again
2. Changing a task's parent session left orphan entries in the old parent's tracking set
3. Concurrent task operations could cause double-acquisition of concurrency slots
These bugs meant the system would sometimes wait forever for tasks that were already done.
What Changed
- Concurrency management: Added proper acquire/release lifecycle with cleanup on process exit (SIGINT, SIGTERM)
- Parent session tracking: Fixed cleanup order. Now clears old parent's tracking before updating parent ID
- Stale entry prevention: Only tracks tasks that are actually running; actively cleans up completed tasks
- Renamed registerExternalTask → trackTask: Clearer name (the old name implied external API consumers, but it's internal)
Update BackgroundManager to rename the method for tracking external tasks, improving clarity and consistency in task management. Adjust related tests to reflect the new method name.
Previously, continuation hooks (todo-continuation, boulder-continuation, ralph-loop)
and background tasks resolved model info from filesystem cache, which could be stale
or missing. This caused session.prompt to fallback to default model (Sonnet) instead
of using the originally configured model (e.g., Opus).
Now all session.prompt calls first try API (session.messages) to get current model
info, with filesystem as fallback if API fails.
Affected files:
- todo-continuation-enforcer.ts
- sisyphus-orchestrator/index.ts
- ralph-loop/index.ts
- background-agent/manager.ts
- sisyphus-task/tools.ts
- hook-message-injector/index.ts (export ToolPermission type)
Enhance the BackgroundManager to properly clean up pending tasks when the parent session ID changes. This prevents stale entries in the pending notifications and ensures that the cleanup process is only executed when necessary, improving overall task management reliability.
Update BackgroundManager to track batched notifications only for running tasks. Implement cleanup for completed or cancelled tasks to avoid stale entries in pending notifications. Enhance logging to include task status for better debugging.
Add functionality to manage process cleanup by registering and unregistering signal listeners. This ensures that BackgroundManager instances properly shut down and remove their listeners on process exit. Introduce tests to verify listener removal after shutdown.
- Update generated timestamp and commit hash metadata
- Normalize agent model names with provider prefixes (anthropic/, opencode/, google/)
- Remove deprecated Google OAuth/Antigravity references
- Update line counts and complexity hotspot entries
- Adjust test count from 82 to 80+ files and assertions from 2559+ to 2500+
🤖 Generated with assistance of oh-my-opencode
Ensure queue waiters settle once, centralize completion with status guards, and release slots before async work so shutdown and cancellations don’t leak concurrency. Internal hardening only.
Add mocks for HTTP transport to avoid real network calls during tests.
This addresses reviewer feedback about test reliability:
- Tests are now faster (no network latency)
- Tests are deterministic across environments
- Test intent is clearer (unit testing error handling logic)
The mock throws immediately with a controlled error message,
allowing tests to validate error handling without network dependencies.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove hardcoded "anthropic/claude-sonnet-4-5" fallback
- Fetch systemDefaultModel from client.config.get() at tool boundary
- Add 'category-default' and 'system-default' fallback types
- Use switch(actualModel) for cleaner type detection
- Add guard clauses and fail-loud validation for invalid models
- Wrap config fetch in try/catch for graceful degradation
- Update toast messages with typed suffixMap