fix(opencode): keep invalid repair sink under DFIR deny-all - #16
Conversation
SessionPrompt tool_choice=required still failed FORCE_AGENT when models invented MCP names: experimental_repairToolCall remapped to `invalid`, but DFIR `"*": deny` stripped that sink so AI SDK reported "unavailable tool 'invalid'". Keep the sink executable (hidden via activeTools), remap underscore/hyphen drift onto real tools, and cover with unit tests.
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bd75e8435c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // "unavailable tool 'invalid'". | ||
| return Record.filter( | ||
| input.tools, | ||
| (_, k) => k === "invalid" || (input.user.tools?.[k] !== false && !disabled.has(k)), |
There was a problem hiding this comment.
Keep invalid out of native tool definitions
In OPENCODE_EXPERIMENTAL_NATIVE_LLM sessions with DFIR-style "*": "deny" permissions, this now leaves the internal invalid tool in prepared.tools; the native branch passes all of prepared.tools into nativeTools(...)/toDefinitions(...) and has no activeTools filter like the AI SDK branch. That means the repair sink is advertised to the provider rather than only kept executable, so the model can select invalid directly and loop on repair hints. Filter the sink from native tool definitions while keeping it available for repair execution.
Useful? React with 👍 / 👎.
Summary
FORCE_AGENT seal reliability gap on the live SessionPrompt path (post-#15
tool_choice: required):"*": "deny"with onlyfindevil-mcp_*/findevil-agent-mcp_*allowed. That stripped the internalinvalidtool fromprepared.tools.experimental_repairToolCallremapped unknown/garbled names toinvalid, then AI SDK failed withModel tried to call unavailable tool 'invalid'(m24 seal-smoke evidence).invalidrepair sink executable under deny-all (still hidden viaactiveTools), and remap common underscore/hyphen MCP name drift (e.g.findevil-agent_mcp_*→findevil-agent-mcp_*) before falling back to the sink.Does not invent
used_fallback=0. Residual model prose/incomplete seal loops can still force caseforge fallback; this only fixes the broken self-correction path for invalid tool names.Binary
$HOME/.local/bin/verdict0.0.0-agent-m25-seal-reliability-202607092026bd75e8435ce5418e5065927f87c4b3e53f8c1af5Test plan
bun test test/session/tool-repair.test.ts(9 pass) inpackages/opencodebun typecheckinpackages/opencode