Updates the canonical Anthropic Opus model in every fallback chain
(sisyphus, oracle, prometheus, metis, momus, visual-engineering,
ultrabrain, deep, artistry, unspecified-high), the unspecified-high
category default, the think-mode HIGH_VARIANT_MAP, the Claude Code
alias map, the claude-thinking legacy alias, the context-limit GA
regex, and event.ts fallback strings.
Widens supportsCachedAnthropicLimit to accept both claude-*-4-6 and
claude-*-4-7 so the 1M context cache still applies across the bump.
Regenerates the bundled model-capabilities snapshot from models.dev
and the model-fallback snapshot to match the new source output.
TTL (pruneStaleTasksAndNotifications) now resets on last activity:
- Uses task.progress.lastUpdate as TTL anchor for running tasks
(was always using startedAt, causing 30-min hard deadline)
- Added taskTtlMs config option for user-adjustable TTL
- Error message shows actual TTL duration, not hardcoded '30 minutes'
- 3 new tests for the new behavior
- completedTaskSummaries now includes status and error info
- notifyParentSession: noReply=false for failed tasks so parent reacts
- Batch notification distinguishes successful vs failed/cancelled tasks
- notification-template updated to show task errors
- task-poller: session-gone tests (85 new lines)
- CI: add Bun shim to PATH for legacy plugin migration tests
Tests 'should use default timeout when config not provided' (manager.test.ts)
and 'should use DEFAULT_MESSAGE_STALENESS_TIMEOUT_MS when not configured'
(task-poller.test.ts) failed in CI because Date.now() drifted between
test setup (when creating timestamps like Date.now() - 46*60*1000) and
actual execution inside checkAndInterruptStaleTasks().
On slower CI machines, this drift pushed borderline values across
the threshold, causing tasks that should be stale to remain 'running'.
Fix: Mock Date.now with spyOn to return a fixed time, ensuring
consistent timeout calculations regardless of execution speed.
Previously, pollRunningTasks() and checkAndInterruptStaleTasks() treated
any non-"idle" session status as "still running", which caused tasks with
terminal statuses like "interrupted" to be skipped indefinitely — both
for completion detection AND stale timeout. This made the parent session
hang forever waiting for an ALL COMPLETE notification that never came.
Extract isActiveSessionStatus() and isTerminalSessionStatus() that
classify session statuses explicitly. Only known active statuses
("busy", "retry", "running") protect tasks from completion/stale checks.
Known terminal statuses ("interrupted") trigger immediate completion.
Unknown statuses fall through to the standard idle/gone path with output
validation as a conservative default.
Introduced by: a0c93816 (2026-02-14), dc370f7f (2026-03-08)
OpenCode uses 'busy'/'retry'/'idle' session statuses, not 'running'.
The stale timeout guard checked for type === 'running' which never
matched, leaving all background tasks vulnerable to stale-kill even
when their sessions were actively processing.
Change sessionIsRunning to check type !== 'idle' instead, protecting
busy and retrying sessions from premature termination.
The stale detection was checking lastUpdate timestamps BEFORE
consulting session.status(), causing tasks to be unfairly killed
after 3 minutes even when the session was actively running
(e.g., during long tool executions or extended thinking).
Changes:
- Reorder pollRunningTasks to fetch session.status() before stale check
- Skip stale-kill entirely when session status is 'running'
- Port no-lastUpdate handling from task-poller.ts into manager.ts
(previously manager silently skipped tasks without lastUpdate)
- Add sessionStatuses parameter to checkAndInterruptStaleTasks
- Add 7 new test cases covering session-status-aware stale detection
Tasks with no progress.lastUpdate were silently skipped in
checkAndInterruptStaleTasks, causing them to hang forever when the model
hangs before its first tool call. Now falls back to checking startedAt
against a configurable messageStalenessTimeoutMs (default: 10 minutes).
Closes#1769