- prometheus-prompt.test.ts: close missing }) on the OpenSpec expanded
commands describe block (introduced by d66b6bcbf, parse error).
- agent-sort-shim/agent-config-integration/continuation-injection/
unstable-agent-babysitter/subagent-resolver/sync-executor/
resolve-caller-team-lead tests: expect 'Sisyphus - ultraworker'
(lowercase) to match production after cd39f8858, which lowercased the
display name to dodge a TUI ZWSP rendering glitch. Legacy uppercase
inputs that exercise the normalization path are preserved.
- sync-executor.ts + resolve-caller-team-lead.ts: route legacy display
name inputs through normalizeAgentForPrompt so prompt agent names and
caller team lead lookups produce the canonical lowercase form.
Detect OpenCode promptAsync calls that return before a child session has any durable message, and surface a prompt acceptance error before the generic five-minute sync poll timeout.
Add a failing-first regression for the idle zero-message case and keep the existing durable-message completion path covered.
Debugging-Journal: .debugging
External review on PR #4074 noted that adding setSessionAgent for child
sessions left sessionAgentMap holding entries after the session was
deleted or after the sync call_omo_agent executor cleaned up other
owned state. The map only grows; entries never get reused but they do
accumulate across long-running plugin instances.
Close both gaps:
- BackgroundManager.handleEvent for session.deleted now calls
clearSessionAgent for the deleted session id on both the early-return
no-task branch and the cascade tail. This pairs with the existing
clearDelegatedChildSessionBootstrap and SessionCategoryRegistry.remove
so all owned session state is dropped together.
- sync-executor finally for createdSessionForExecution now calls
clearSessionAgent alongside the existing subagentSessions,
syncSubagentSessions, and deleteSessionTools cleanup so sessions this
executor created cannot leak their agent mapping.
Adds focused tests:
- BackgroundManager.handleEvent - session.deleted cascade > should
clear session agent state for deleted sessions to prevent map leak
- executeSync > registers child-session bootstrap and tracked prompt
state before sync prompt dispatch (extended assertion for cleanup)
call_omo_agent sync path created the child OpenCode session and went
straight into promptAsync without registering child session agent,
session tools, or bootstrap state. If first dispatch failed before any
durable user message persisted, runtime fallback could not reconstruct
the original prompt or the agent identity for that child session.
Bind setSessionAgent and setSessionTools to the child session id with
the same tool restrictions that the prompt body sends, register a
delegated child session bootstrap with the prompt text, fallback chain,
and prompt tools, then clean bootstrap + session tools in finally for
sessions this call created.
Keep prompt reservations briefly after successful dispatch so rapid idle/message/error transitions cannot inject the same follow-up twice.
Route all production session prompt calls through the shared gate, restore skipped background resume state, release holds after abort/recovery paths, and preserve Ralph/ULW loop state when a dispatch is deferred.
Add regression coverage for session routing, static prompt route auditing, team-mode live messaging, model suggestion retries, call-omo-agent reuse, background parent wakes, runtime fallback, compaction recovery, Atlas, and Ralph/ULW loops.
Sub-agent sessions created via the task tool or call_omo_agent tool were
being created without a model. The resolved model was only passed as a
promptAsync body override, which opencode core ignores, causing fallback
to the client's system default model (often a reasoning model that hangs).
OpenCode's session.create API supports model: { id, providerID, variant }
at creation time. This fix ensures the resolved category/agent model is
passed during session creation across all paths:
- createSyncSession() (delegate task / category-based)
- createOrGetSession() (call_omo_agent / direct agent calls)
- BackgroundManager.startTask() (background tasks)
This guarantees each sub-agent session is created with the correct model
regardless of whether promptAsync honors its model override.
Agent names like \hephaestus\ cause 'Agent not found' errors because
sanitizeSubagentType() was only called in subagent-resolver.ts but not
in the direct manager.launch() path or background-executor.ts.
- manager.ts: strip leading/trailing backslash/quote chars from input.agent
before validation so \hephaestus\ → hephaestus
- background-executor.ts: call sanitizeSubagentType(args.subagent_type)
instead of passing raw value to manager.launch()
- agent-display-names.ts: reuse sanitizeSubagentType in stripAgentListSortPrefix
- Add unit tests for all three fix points
Fixes: sessions dying with 'Agent not found: \hephaestus\'
Rename BackgroundTask and attempt ID fields to camelCase across background-agent consumers while moving BackgroundManager construction to a single config object.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Move parseModelString into src/shared so callers can depend on a neutral module instead of reaching into delegate-task internals.
Cross-tool coupling violates module boundaries, and this keeps call-omo-agent plus runtime-fallback from importing through a sibling tool.
Blocking fixes:
- B1: Return empty restrictions for unknown/custom agents instead of
EXPLORATION_AGENT_DENYLIST, allowing custom agents full tool access
- B2: Use Object.create(null) consistently across all 5 agent-loading
result objects to prevent prototype pollution
- B3: Add code comment documenting custom agent bash access trust model
- B4: Mock getOpenCodeConfigDir in opencode-config-agents-reader tests
to prevent global config dir leakage
Non-blocking fixes:
- N1: Use resolveAgentDefinitionPaths with project boundary enforcement
in opencode-config-agents-reader for path containment
- N2: Add session-scoped 30s TTL cache to resolveCallableAgents to
avoid redundant SDK IPC calls per tool invocation
- N3: Extract shared parseToolsConfig into src/shared/parse-tools-config.ts
replacing 4 duplicated local implementations
- N4: Add .min(1) to AgentDefinitionPathSchema rejecting empty paths
- N5: Add resolve-agent-definition-paths.test.ts covering tilde expansion,
relative paths, boundary enforcement, and null containmentDir
- N6: Validate agent mode against allowed values instead of bare type
assertion in opencode-config-agents-reader
Dev added 4 tests (#2852, model variant, category-derived overrides) that
referenced the old mockCtx constant. Our branch replaced it with a
createMockCtx() factory. Replace all 4 references.
- #3354: Coerce data.name to String in loadSkillFromPath/loadSkillFromPathAsync
to prevent crash when YAML parses numeric skill names (e.g., name: 12306)
- #3416: Add required run_in_background parameter to all task() examples in
ultrawork prompts (default, gpt, gemini, planner) to match tool schema
- #3379/#3417/#3418/#3337/#3335: Strip ZWSP (U+200B) before agent name
comparisons in agent-tool-restrictions, sync-prompt-sender, tool-execute-after,
tool-execute-before, oracle-verification-detector, call-omo-agent,
recovery-prompt-config, and agent-variant to prevent ZWSP-prefixed display
names from breaking exact-match lookups
- Replace getAgentRuntimeName with getAgentDisplayName for consistency
- Add stripAgentListSortPrefix helper to normalize agent names
- Strip sort prefixes in subagent-resolver and sync-executor
- Backfill canonical names for core agents when builtin configs omit name
- Update tests to match new behavior
Update various modules to use centralized constants from plugin-identity:
- get-local-version/formatter: Use PUBLISHED_PACKAGE_NAME
- run/session-resolver: Use PUBLISHED_PACKAGE_NAME
- background-agent/task-poller: Use PUBLISHED_PACKAGE_NAME
- mcp-oauth/provider: Use PUBLISHED_PACKAGE_NAME
- auto-update-checker/constants: Use ACCEPTED_PACKAGE_NAMES
- comment-checker/downloader: Use PUBLISHED_PACKAGE_NAME
- legacy-plugin-toast/hook: Use PLUGIN_NAME
- shared/data-path: Use CACHE_DIR_NAME
- shared/external-plugin-detector: Use ACCEPTED_PACKAGE_NAMES
- shared/logger: Use LOG_FILENAME
- tools/ast-grep/downloader: Use PUBLISHED_PACKAGE_NAME
- tools/call-omo-agent/tools: Use PUBLISHED_PACKAGE_NAME
- tools/delegate-task/category-resolver: Use PUBLISHED_PACKAGE_NAME
- tools/grep/constants: Use PUBLISHED_PACKAGE_NAME
- tools/grep/downloader: Use PUBLISHED_PACKAGE_NAME
- tools/lsp/lsp-client-wrapper: Use PUBLISHED_PACKAGE_NAME
🤖 Generated with assistance of OhMyOpenCode
Tool return values contained CTAs ("Use background_output to check") that
caused models to immediately poll instead of waiting for <system-reminder>
notifications. Unified all 5 tool return messages with explicit "Do NOT call
background_output now" guard, added pre-notification warning to BackgroundOutput
tool description, and strengthened Background Result Collection sections across
all 3 Sisyphus prompt variants (default, gpt-5-4, main) with BLOCKING
anti-pattern enforcement.
🤖 Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
- fix(switcher): use lastIndexOf for multi-slash model IDs (e.g. aws/anthropic/claude-sonnet-4)
- fix(model-resolution): same lastIndexOf fix in doctor parseProviderModel
- fix(call-omo-agent): resolve model from agent config and forward to both
background and sync executors via DelegatedModelConfig
- fix(subagent-resolver): inherit category model/variant when agent uses
category reference without explicit model override
- test: add model override forwarding tests for call-omo-agent
- test: add multi-slash model ID test for switcher
Sync call_omo_agent leaked entries in global activeSessionMessages
and activeSessionToolResults Sets when execution threw errors,
since cleanup only ran on success path.
- Wrap session Set operations in try/finally blocks
- Ensure Set.delete() runs regardless of success/failure
- Add guard against double-cleanup
Tests: 2 pass, 14 expects