Added await for session ready state before sending prompt in
background-agent/manager.ts. Also improved image resizer error handling.
132 tests pass, tsc clean.
Closes#2932
OpenCode's fromPlugin wrapper skips Zod validation for plugin tools, so
LLMs can omit required args like description without getting an error.
When Atlas orchestrates and the model omits description, it flows through
as undefined to manager.launch() and background task notifications show
'undefined' for all completed tasks.
Two fixes:
- Add runtime validation for description in delegate-task tool (matches
existing run_in_background and load_skills validation pattern)
- Defensive fallback in notification template: use task ID when
description is missing instead of rendering 'undefined'
The (message.includes("agent") && message.includes("undefined"))
pattern could match unrelated errors like "The agent returned undefined
for the configuration", triggering a false fallback that hides the real
failure.
The two precise patterns are sufficient:
- "Agent not found" — canonical SDK validation error
- "agent.name" — property access error on undefined agent config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. isAgentNotFoundError now handles:
- Plain objects with .message field (not just Error instances)
- "agent.name"/"undefined" error variants from SDK validation
- The original "Agent not found" format
2. New tests:
- agent.name/undefined error variant triggers fallback
- Plain object errors with .message field trigger fallback
- "fallback also fails" test now verifies retry was attempted (callCount=2)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. [HIGH] Tool restrictions recomputed for fallback agent via
buildFallbackBody() — no longer inherits original agent's restrictions.
2. [HIGH] Double-retry race prevented — handleSessionErrorEvent now
returns early for agent-not-found errors, since the prompt catch
block already handles them with agent fallback. This prevents
tryFallbackRetry from racing with a model-level retry on the same
error (the "not found" pattern in RETRYABLE_MESSAGE_PATTERNS).
3. [MEDIUM] task.agent updated to FALLBACK_AGENT after successful
fallback — notifications, toast, and logging reflect actual agent.
4. [MEDIUM] FALLBACK_AGENT exported from spawner.ts and imported into
manager.ts — single source of truth.
5. [LOW] resumeTask fallback now uses promptWithModelSuggestionRetry
(consistent with startTask), getting timeout protection.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a model/mode switch happens while a background task is in-flight,
the oh-my-openagent agent registry can be rebuilt without custom agents
(e.g., Sisyphus-Junior). The SDK then rejects the promptAsync call with
"Agent not found", killing the task.
This adds retry logic: when promptAsync fails with "Agent not found",
retry with the "general" agent (always available in opencode). The
original prompt, model, and skill content are preserved — only the
agent routing changes.
Fixes both the spawner (startTask/resumeTask) and manager (inline
launch) code paths. Also improves the error message detection in
manager.ts to recognize "Agent not found" alongside the existing
"agent.name"/"undefined" checks.
Related: #2052, #2875, #2882
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three test files were mocking the entire '../../shared' barrel, which
corrupted exports for subsequent test files in the same batch run.
Narrow mocks to specific submodules (logger, connected-providers-cache).
Also reverts Bun version pin since the root cause was mock scope, not Bun.
- Add afterAll(() => { mock.restore() }) to 52 test files missing cleanup
- Rewrite create-tool-guard-hooks.test.ts to use spyOn instead of barrel mock
- Fix skill-mcp-manager OAuth tests with missing mockTokens/mockLogin definitions
- Fix start-work hook: show worktree active block on resume with existing worktree_path
- Extract createWorktreeActiveBlock to worktree-block.ts to avoid circular import
- Replace 80-line isolated test runner CI config with single `bun test` command
3 failures in fallback.cliproxyapi-matrix.test.ts:
- Root cause: leaked spyOn(getMainSessionID) in
tool-execute-before-session-notification.test.ts was never restored,
poisoning module state for subsequent tests in the same worker
- Added mockRestore() call and _resetModelFallbackForTesting in afterEach
3 failures in background-agent/manager.test.ts:
- Root cause: connected-providers-cache memConnected/memProviderModels
persisted across test files, making isReachable() skip fallback
candidates in retry tests
- Added mock.module for connected-providers-cache at file level
- Added _resetMemCacheForTesting export and global beforeEach cleanup
Full suite: 4638/4638 pass, 0 fail
Fire-and-forget session.abort() calls during subagent completion left
dangling promises that raced with parent session teardown. In Bun on
WSL2/Linux, this triggered a StringImplShape assertion (SIGABRT) as
WebKit GC collected string data still referenced by the inflight request.
Fix: await session.abort() in all four completion/error paths:
- startTask promptAsync error handler (launch path)
- resume promptAsync error handler (resume path)
- cancelTask (explicit cancel path)
- tryCompleteTask (normal completion path)
Also marks the two .catch() error callbacks as async so the await is
valid.
Test: update session.deleted cascade test to flush two microtask rounds
since cancelTask now awaits abort before cleanupPendingByParent.
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
When a subagent session disappears from the status registry (process
crashed), the main agent was waiting the full stale timeout before
acting. Fix:
- Add sessionGoneTimeoutMs config option (default 60s, vs 30min normal)
- task-poller: use shorter timeout when session is gone from status
- manager: verify session existence when gone, fail crashed tasks
immediately with descriptive error
- Add legacy-plugin-toast hook for #2823 migration warnings
- Update schema with new config option
Two issues fixed:
1. process-cleanup.ts used fire-and-forget void Promise for shutdown
handlers — now properly collects and awaits all cleanup promises
via Promise.allSettled, with dedup guard to prevent double cleanup
2. TmuxSessionManager was never registered for process cleanup —
now registered in create-managers.ts via registerManagerForCleanup
Also fixed setTimeout().unref() which could let the process exit
before cleanup completes.
- model-fallback hook: mock selectFallbackProvider and add _resetForTesting()
to test-setup.ts to clear module-level state between files
- fallback-retry-handler: add afterAll(mock.restore) and use mockReturnValueOnce
to prevent connected-providers mock leaking to subsequent test files
- opencode-config-dir: use win32.join for Windows APPDATA path construction
so tests pass on macOS (path.join uses POSIX semantics regardless of
process.platform override)
- system-loaded-version: use resolveSymlink from file-utils instead of
realpathSync to handle macOS /var -> /private/var symlink consistently
All 4456 tests pass (0 failures) on full bun test suite.