When replace_plan is true (default), the native plan agent was demoted to
subagent but remained visible in the agent picker. This caused Sisyphus to
route planning to the native plan agent instead of Prometheus.
Add hidden: true to buildPlanDemoteConfig(), consistent with how the build
agent is hidden when default_builder_enabled is false.
The opencode-config-agents-reader.test.ts was using mock.module() which
permanently replaced the module in bun's module cache, causing state
pollution in downstream tests (plugin-detection, write-omo-config,
config-loader). Replaced with spyOn() pattern that properly restores
in afterEach.
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
Addresses cubic-dev-ai P1 review: Object.hasOwn() alone doesn't prevent
a crafted '__proto__' agent name from mutating the result object's
prototype chain. Using Object.create(null) eliminates inherited properties
entirely, making both the hasOwn checks and property assignments safe.
Addresses cubic-dev-ai review: using 'in' on plain objects can skip valid
agent names that match inherited properties (toString, constructor, etc.).
Switched both occurrences in opencode-config-agents-reader.ts to
Object.hasOwn() for safe own-property checks.
- Case-insensitive .md extension stripping for agent name extraction
- Resolve project agent_definitions paths relative to config dir (.opencode/)
- Use getOpenCodeConfigDir() to respect OPENCODE_CONFIG_DIR/XDG_CONFIG_HOME
- First-write-wins semantics for both inline and definition-file agents
so project-level agents always take precedence over global-level
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.
- Modified agent-config-handler.ts to load and integrate both new agent sources
- Added loadAgentDefinitions() and readOpencodeConfigAgents() calls in loading phase
- Integrated both sources into agent precedence chains (both Sisyphus-enabled and disabled paths)
- Added detailed logging for new agent sources
- Added filtering logic to respect disabled_agents configuration
- Extended agent-config-handler.test.ts with 7 new integration tests
- All tests passing (18/18 integration, 65/65 loader suite)
Wave 3 of agent definitions enhancement complete.
Wave 1 of agent definitions enhancement (PR #2299):
Schema & Configuration:
- Add agent_definitions field to oh-my-opencode config schema
- Support list of file paths to .md or .json agent definition files
- Add to PARTIAL_STRING_ARRAY_KEYS for Set-union merge semantics
- Implement eager path resolution in loadPluginConfig() before merging
Path Resolution:
- Create resolve-agent-definition-paths.ts helper
- User-level paths resolve from ~/.config/opencode/ (no containment)
- Project-level paths resolve from project root (with containment check)
- Homedir expansion, absolute/relative path handling
JSON Agent Loader:
- Create parseJsonAgentFile() for .json/.jsonc agent definitions
- Validate required fields (name, prompt)
- Support tools as string (comma-separated) or array
- Map model via mapClaudeModelToOpenCode()
- Comprehensive test suite (7 test cases, all passing)
Type Extensions:
- Extend AgentScope: add 'definition-file' and 'opencode-config'
- Add AgentJsonDefinition interface for JSON agent schema
All automated checks passing:
- lsp_diagnostics clean on all changed files
- json-agent-loader.test.ts: 7/7 passing
- Full typecheck: zero new errors
- QA evidence saved to .sisyphus/evidence/
- #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
- shell-env: detect Git Bash via MSYSTEM env var when SHELL is unset (#3366)
On some Git Bash installations SHELL is not set but MSYSTEM (MINGW64/MSYS)
is always present. Check MSYSTEM before PSModulePath to avoid emitting
PowerShell syntax in bash shells.
- session-state: resolve legacy agent names in resolveRegisteredAgentName (#3272)
Historical sessions stored agent names like 'Sisyphus (Ultraworker)' which
don't match the current registered format. Fall back to getAgentConfigKey
for legacy/parenthesized name resolution before returning the raw name.
- config-migration: skip backup when file content is unchanged (#3222)
Compare serialized config with existing file content before creating a
timestamped .bak file. Only create backup when the on-disk content
actually differs from the migrated content.
- Add --vercel-ai-gateway CLI option to install command
- Fix librarian/explore priority: native providers (ZAI, Copilot) now take precedence over Vercel gateway
- Add hasVercelAiGateway to installer no-provider warning conditions
- Add hasVercelAiGateway: false to all test file InstallConfig fixtures
- Fix test expectations for model ID format (claude-opus-4.6 vs claude-opus-4-6)
Claude version transforms now use a single regex
claude-(\w+)-(\d+)-(\d+) -> claude-$1-$2.$3 instead of one
.replace() per model. New claude models need zero changes.
Adds vercel to every fallback entry where the model exists on the
gateway (kimi-k2.5, glm-5, glm-4.6v, minimax-m2.7, grok-code-fast-1,
gpt-5-nano). Adds inferSubProvider mappings for minimax, moonshotai,
and zai. Updates librarian/explore special cases to prefer minimax
via gateway over claude-haiku.
The gateway uses google/gemini-3-flash (no -preview suffix) unlike the
direct Google API. Give the vercel provider its own transform instead
of delegating to sub-provider transforms blindly.
Adds 'vercel' as a recognized provider throughout the model resolution
system, enabling users with Vercel AI Gateway to use it as a universal
fallback for OpenAI, Anthropic, and Google models.
The gateway transform infers the sub-provider from canonical model names
(claude->anthropic, gpt->openai, gemini->google) and delegates to the
appropriate provider-specific transform, producing model strings like
vercel/anthropic/claude-opus-4.6.
Prevent project config defaults from re-enabling git_master fields that users explicitly disabled. Rebuild git_master from explicit user and project keys so include_co_authored_by and commit_footer merge predictably.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
setSessionFallbackChain stored the fallback chain but the sync path
never consumed it when sendSyncPrompt returned an error. Added a
retry loop that iterates getNextFallback() until the prompt succeeds
or the chain is exhausted, preserving the finally-block cleanup.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
The sync poller returned 'Task aborted' immediately when the abort
signal fired, even when a terminal assistant message had already
arrived during the previous wait. Now attempts one final fetch and
returns the completion result if available before emitting the
abort message.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
The dedup logic dropped real session.idle events when a synthetic
idle had fired within the dedup window, losing data that downstream
hooks needed. Now real idle events always reach dispatchToHooks while
synthetic duplicates are still dropped when a real event came first.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
resolveSubagentSpawnContext and related lookups called client.session.get
without the query.directory parameter, causing project-scoped sessions
to 404 under newer OpenCode SDK versions. Threaded directory through
SpawnerContext/BackgroundManager so all four call-sites pass it.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
Doctor's comment-checker probe only checked system PATH and the
package binary while the runtime resolution path checks the
lazy-download cache first. Aligned the doctor resolution order
with runtime so the cached binary is recognized as installed.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
The agent prompt described PDF handling but did not tell the agent
to call the Read tool, which is its only allowed tool. Added
explicit instruction so PDFs and documents are actually loaded
before extraction.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode
Hardcoded platform check forced PowerShell env syntax on Windows
regardless of the actual shell. Replaced with detectShellType() so
Git Bash, WSL, and similar environments use unix prefixes while
native PowerShell continues to get powershell prefixes.
🤖 Generated with OhMyOpenCode assistance
https://github.com/code-yeongyu/oh-my-opencode