Handle OpenCode session events that carry the session ID under properties.info.id or properties.info.sessionID so background tasks and continuation hooks do not miss idle/error/delete events.
Add regression coverage for nested session.idle events completing background tasks and waking continuation hooks.
When using `opencode run`, the process exits prematurely if the main
agent dispatches background subtasks. The CLI completion checker relies
on `session.children()` + `session.status()` APIs which cannot see
tasks in "pending" state (no session created yet) or tasks whose
sessions are momentarily idle between operations.
This fix bridges BackgroundManager state to the CLI completion checker
using the existing run-continuation-state marker system:
- Add "background-task" continuation marker source
- BackgroundManager writes/clears markers on task lifecycle events
(launch, cancel, complete, crash)
- CLI completion checker blocks exit when marker is active
- Fix todo-continuation-enforcer to also check "pending" task status
Closes#3452
handler.ts now catches ContextLengthError/prompt-too-long errors and
stops continuation instead of retrying with an even larger context.
91 tests pass, tsc clean.
Closes#2462
All 5 continuation paths now send variant as top-level body field:
- boulder-continuation-injector.ts
- ralph-loop/continuation-prompt-injector.ts
- todo-continuation-enforcer/continuation-injection.ts
- unstable-agent-babysitter-hook.ts
- session-recovery/resume.ts
Plus type/helper updates in atlas, todo-continuation-enforcer,
unstable-agent-babysitter, and session-recovery.
TDD: 18 regression tests added, all pass. tsc clean.
Display names with parentheses like 'Atlas (Plan Executor)' cause HTTP
header validation errors in x-opencode-agent-name. This was blocking
Atlas/Prometheus from working via /start-work and auto-retry.
Changes:
- Display names: parens -> dashes ('Atlas - Plan Executor')
- Hooks (start-work, no-hephaestus-non-gpt, no-sisyphus-gpt): use
config keys ('atlas', 'sisyphus', 'hephaestus') for agent API fields
- auto-retry: use config key instead of display name for promptAsync
- agent-override-protection: handle dash-suffix normalization
- Updated all test expectations to match new format
Closes#3138
The todo-continuation-enforcer was passing raw agent names (which could be
display names like 'Sisyphus (Ultraworker)') to promptAsync. These names
contain spaces/parentheses that violate HTTP header specs, causing the
x-opencode-agent-name header validation to fail with 'unknown error' toast.
Added normalizeAgentForPromptKey() that converts display names to config keys
(e.g., 'Sisyphus (Ultraworker)' -> 'sisyphus') before API calls.
TDD: Added regression test that verifies config key is sent to promptAsync.
When a user cancels a generation (ESC x2), all three idle hooks could
enter a permanently broken state:
1. **todo-continuation-enforcer**: consecutiveFailures accumulated from
abort-caused promptAsync failures, eventually hitting MAX_CONSECUTIVE_FAILURES
and permanently stopping continuation injection.
2. **unstable-agent-babysitter**: no abort awareness at all — would keep
firing reminders after user cancelled the session.
3. **runtime-fallback**: retry dedupe keys and pending fallback state
persisted across cancellation, blocking legitimate error recovery.
Fix:
- Add shared `isAbortError()` utility for consistent abort detection
- Reset consecutiveFailures and clear stale state on AbortError in all hooks
- Track `lastCancelledAt` in todo-continuation-enforcer for abort window
- Add abort-awareness to unstable-agent-babysitter (skip if recently cancelled)
- Clear runtime-fallback retry state on abort errors
Tests: 61 pass, 0 fail across all 3 affected hook test suites.
Closes#2984
Add [TODO-DIAG] console.error statements throughout the todo continuation
enforcer to help diagnose why continuation prompts aren't being injected.
Changes:
- Add session.idle event handler diagnostic in handler.ts
- Add detailed blocking reason logging in idle-event.ts for all gate checks
- Update JSON schema to reflect circuit breaker config changes
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
The todo-continuation-enforcer injects continuation prompts when
sessions go idle with pending todos. When Plan Mode agents (which are
read-only) create todo items, the continuation prompt contradicts
Plan Mode's STRICTLY FORBIDDEN directive, causing an infinite loop
where the agent acknowledges the conflict then goes idle, triggering
another injection.
Adding 'plan' to DEFAULT_SKIP_AGENTS prevents continuation injection
into Plan Mode sessions, matching the same exclusion pattern already
used for prometheus and compaction agents.
Fixes#2526
Refine continuation agent resolution to prefer session-state agent fallback while keeping compaction-specific protection. Replace sticky boolean compaction flag with a short-lived timestamp guard so unresolved agents are blocked only during the immediate post-compaction window, avoiding long-lived suppression and preserving existing continuation behavior.
After compaction, message history is truncated and the original agent
(e.g. Prometheus) can no longer be resolved from messages. The todo
continuation enforcer would then inject a continuation prompt with
agent=undefined, causing the host to default to General -- which has
write permissions Prometheus should never have.
Root cause chain:
1. handler.ts had no session.compacted handler (unlike Atlas)
2. idle-event.ts relied on finding a compaction marker in truncated
message history -- the marker disappears after real compaction
3. continuation-injection.ts proceeded when agentName was undefined
because the skipAgents check only matched truthy agent names
4. prometheus-md-only/agent-resolution.ts did not filter compaction
agent from message history fallback results
Fixes:
- Add session.compacted handler that sets hasRecentCompaction state flag
- Replace fragile history-based compaction detection with state flag
- Block continuation injection when agent is unknown post-compaction
- Filter compaction agent in Prometheus agent resolution fallback
Prune interval created inside hook was not exposed for disposal,
preventing cleanup on plugin unload.
- Add dispose() method that clears the prune interval
- Export dispose in hook return type
Tests: 2 pass, 6 expects
Resume GPT sessions when the last assistant reply ends in a permission-seeking tail, while honoring stop-continuation and avoiding duplicate continuation across todo and atlas flows.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>