Category-based delegation (task(category='quick')) was broken because
SISYPHUS_JUNIOR_AGENT sent 'sisyphus-junior' to session.prompt but
config.agent keys are now display names ('Sisyphus-Junior').
- Use getAgentDisplayName() for SISYPHUS_JUNIOR_AGENT constant
- Replace hardcoded 'sisyphus-junior' strings in tools.ts with constant
- Update background-output local constants to use display names
- Add remapCommandAgentFields() to translate command agent fields
- Add raw-key fallback in tool-config-handler agentByKey()
The messages() mock in 'session_id with background=false' test did not
filter by session ID, causing resolveParentContext's SDK calls for
parent-session to increment messagesCallCount. This inflated
anchorMessageCount to 4 (matching total messages), so the poll loop
could never detect new messages and always hit MAX_POLL_TIME_MS.
Fix: filter messages() mock by path.id so only target session
(ses_continue_test) increments the counter. Restore MAX_POLL_TIME_MS
from 8000 back to 2000.
- runner.test.ts: waitForEventProcessorShutdown timeout 50ms → 500ms
(50ms was consistently too tight for CI runners)
- tools.test.ts: MAX_POLL_TIME_MS 2000ms → 8000ms
(polling timed out at ~2009ms on CI due to resource contention)
The polling loop in executeUnstableAgentTask only checked session status
and message stability, never checking if the background task itself had
been interrupted. This caused the tool call to hang until MAX_POLL_TIME_MS
(10 minutes) when a task was interrupted by prompt errors.
Add manager.getTask() check at each poll iteration to break immediately
on terminal statuses (interrupt, error, cancelled), returning a clear
failure message instead of hanging.
Pass parentTools from session-tools-store through the background task
lifecycle (launch → task → notify) so that when notifyParentSession
sends promptAsync, the original tool restrictions (e.g., question: false)
are preserved. This prevents the Question tool from re-enabling after
call_omo_agent background tasks complete.
Call setSessionTools(sessionID, tools) before every prompt dispatch so
the tools object is captured and available for later retrieval when
background tasks complete.
LLMs sometimes pass load_skills as a serialized JSON string instead
of an array. Add defensive JSON.parse before validation to handle
this gracefully.
Fixes#1701
Community-reported-by: @omarmciver
Replace PreToolUse hook-based question tool blocking with the existing
tools parameter approach (tools: { question: false }) which physically
removes the tool from the LLM's toolset before inference.
The hook was redundant because every session.prompt() call already passes
question: false via the tools parameter. OpenCode converts this to a
PermissionNext deny rule and deletes the tool from the toolset, preventing
the LLM from even seeing it. The hook only fired after the LLM already
called the tool, wasting tokens.
Changes:
- Remove subagent-question-blocker hook invocation from PreToolUse chain
- Remove hook registration from create-session-hooks.ts
- Delete src/hooks/subagent-question-blocker/ directory (dead code)
- Remove hook from HookNameSchema and barrel export
- Fix sync-executor.ts missing question: false in tools parameter
- Add regression tests for both the removal and the tools parameter
When delegate-task resumes a session via session_id, the response
task_metadata now includes a subagent field identifying which agent
was running in the resumed session. This allows the parent agent to
know what type of subagent it is continuing.
- sync-continuation: uses resumeAgent extracted from session messages
- background-continuation: uses task.agent from BackgroundTask object
- Gracefully omits subagent when agent info is unavailable
Allow individual categories to be disabled via `disable: true` in
config. Introduce shared `mergeCategories()` utility to centralize
category merging and disabled filtering across all 7 consumption sites.
Child session creation was injecting permission: { question: 'deny' } which
conflicted with OpenCode's child session permission handling, causing subagent
sessions to hang with 0 messages after creation (zombie state).
Remove the permission override from all session creators (BackgroundManager,
sync-session-creator, call-omo-agent) and rely on prompt-level tool restrictions
(tools.question=false) to maintain the intended policy.
Closes#1711
- fix(delegate-task): return error on poll timeout instead of silent null
- fix(delegate-task): ensure toast and session cleanup on all error paths with try/finally
- fix(delegate-task): apply agent tool restrictions in sync-prompt-sender
- fix(plugin): add symmetric idle dedup to prevent double hook triggers
- fix(cli): replace regex-based JSONC editing with jsonc-parser in auth-plugins
- fix(cli): abort event stream after completion and restore no-timeout default
All changes verified with tests and typecheck.
- Add comprehensive TODO SYNC section documenting automatic
bidirectional sync between tasks and OpenCode todo system
- Improve sync-continuation.test.ts with proper mock modules
for pollSyncSession and fetchSyncResult dependencies
- Add tests for sync-continuation error paths and toast cleanup
- Add tests for sync-result-fetcher with anchor message support
- Expand sync-session-poller tests for edge cases and completion detection
- Add bulk cleanup test for recent-synthetic-idles
- Add proper error handling in executeSyncContinuation with try-catch blocks
- Ensure toast cleanup happens in all error paths via finally block
- Add anchorMessageCount tracking for accurate result fetching after continuation
- Improve fetchSyncResult to filter messages after anchor point
- Add silent failure detection when no new response is generated
OpenCode SDK does not expose client.model.list API. This caused the
provider-models cache to always be empty (models: {}), which in turn
caused delegate-task categories with requiresModel (e.g., 'deep',
'artistry') to fail with misleading 'Unknown category' errors.
Changes:
- connected-providers-cache.ts: Extract models from provider.list()
response's .all array instead of calling non-existent client.model.list
- category-resolver.ts: Distinguish between 'unknown category' and
'model not available' errors with clearer error messages
- Add comprehensive tests for both fixes
Bug chain:
client.model?.list is undefined -> empty cache -> isModelAvailable
returns false for requiresModel categories -> null returned from
resolveCategoryConfig -> 'Unknown category' error (wrong message)
Category-based delegation should always route to sisyphus-junior even if
subagent_type is mistakenly provided, matching the original behavior and
preventing accidental bypass of category routing.
Sync task completion was fragile — detecting premature stability during
brief idle periods between tool calls. Now mirrors opencode's native
SessionPrompt.loop() logic: checks assistant finish reason is terminal
(not tool-calls/unknown) and assistant.id > user.id.
Also switches sync prompt sender from blocking HTTP (promptSync) to
async fire-and-forget (promptAsync) to avoid JSON parse errors in ACP.
Dev removed task-continuation-enforcer entirely. Remove all remaining
references from plugin hooks, event handler, tool-execute-before, and
config schema to align with origin/dev.
- Add improved type definitions for skill resolution
- Enhance executor with better type safety for delegation flows
- Add comprehensive test coverage for delegation tool behavior
- Improve code organization for skill resolver integration
🤖 Generated with assistance of OhMyOpenCode
- Create available-models.ts for model availability checking
- Create model-selection.ts for category-to-model resolution logic
- Update category-resolver, subagent-resolver, and sync modules to import
from new focused modules instead of monolithic sources
- Add skill-resolver.ts for resolving skill configurations
- Handles skill loading and configuration resolution
- Part of modular delegate-task refactoring effort
🤖 Generated with assistance of OhMyOpenCode
Revert .default([]) on load_skills schema back to required, restore the runtime error for missing load_skills, and add explicit load_skills=[] to all task() examples in agent prompts that were missing it.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>