Repository navigation
fix(harness): let a host tool replace an unallowed built-in tool of the same name - #615
Merged
Merged
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Reopened unrestricted sessions bypass collision validation, and the binding specification contains contradictory name-validation language.
2 open findings
What changed in this PR
Enables host tools to replace same-named WorkDir built-ins when an explicit allowlist is used.
Changes:
- Adds built-in shadowing and collision validation.
- Updates architecture documentation and changelog.
- Adds Go, Node/Wasm, and browser coverage.
| File | Description |
|---|---|
tools.go |
Removes shadowed built-ins from session sources. |
runtime.go |
Permits shadowable host tool names. |
models.go |
Rejects unrestricted collisions. |
e2e/contract_runtime_toolcall_test.go |
Tests runtime behavior. |
e2e/browser_test.go |
Adds browser coverage. |
e2e/acp_node_test.go |
Adds Node/Wasm coverage. |
sdk/embed/test/browser/page.mjs |
Defines the browser host tool. |
cmd/harness-wasm/node/host.mjs |
Defines the Node host tool. |
docs/architecture.md |
Specifies replacement semantics. |
CHANGELOG.md |
Records the user-visible change. |
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…he same name A host tool named like a built-in tool, such as bash, failed New. A host that cannot run bash (sdk/embed in a browser or Node, with page tools) saw session/new fail with `internal`, even though its allowed list left the built-in out of reach. Design: New no longer rejects an Options.Tools name from read_file, write_file, edit_file, glob, grep, ls, or bash. The session source drops the built-in tool of that name, so the model sees one tool with the schema of the host. Create fails with invalid_request, naming the tool, when AllowedTools is nil, because the built-in tool is then allowed too. read_tool_result, session_info, process, task, model, plugin tools, and MCP tools keep their rules. Semantic change: a list in AllowedTools that names bash selects the host tool; a nil list with a host tool named bash fails Create. Verification: runtime rows for both cases; Node wasm rows through HARNESS_E2E_NODE=1; a browser row through HARNESS_E2E_BROWSER=1; the handoff check.
andybons
force-pushed
the
fix/host-tool-builtin-name
branch
from
October 14, 2026 03:05
f6971b6 to
42f46ea
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Problem
A host tool named like a built-in tool, for example
bash, failsNew. An sdk/embed page tool or a GoOptions.Toolstool with that name makessession/newfail withinternal, also when the allowed list leaves the built-in out. The browser default set excludesbash, so a page that wants its ownbashtool cannot have one.Design
Spec: docs/architecture.md
Tool names(Four rules, AllowedTools bullet),Runtime(New),Built-in tools, and the wasmToolsrow. Design: docs/design/acp-wasm.md section 5.1, 5.6.Newno longer rejects anOptions.Toolsname fromread_file,write_file,edit_file,glob,grep,ls,bash(Runtime.shadowable, the one owner of the rule).checkModelfailsCreatewithinvalid_request(ErrInvalidRequest, ACP invalid params) and names the tool whenAllowedToolsis nil, because the built-in tool is then allowed too.read_tool_result,session_info,process,task,modelkeep their rules. The spec is silent on plugin and MCP tools, so only host tools change.Semantic change
AllowedTools is a flat list of names, so a name in an explicit list cannot tell the built-in from the host tool. With a list, the name selects the host tool (the sdk/embed default list always holds the page tool names, so this is the only reading that works there). With nil, the collision is the clear error.
Verification
TestContractRuntimeEmbedderToolReplacesAnUnlistedBuiltinOfTheSameName,...FailsCreateWhenEveryToolIsAllowed,TestNodeWasmPageToolReplacesABuiltinTool,TestNodeWasmPageToolWithABuiltinNameFailsWhenEveryToolIsAllowed(HARNESS_E2E_NODE=1), browser rowpage tool named bash replaces the built-in bash(HARNESS_E2E_BROWSER=1). Before the fix:tool name "bash" is empty, repeated, or reservedandinternal(-32603).go build ./... && go vet ./... && go test -race ./... && gofmt: pass (one unrelated CLI timing test flaked under load, passes alone with -count=3).CI job times
No job grew by more than 6 s. The new rows run in parallel inside existing jobs and take under 2 s each (the browser row shares one Chromium run).