th-82ad57: coding-tool parity across all polyglot LocalServers (go/ts/python/dotnet)#329
Open
brentrager wants to merge 5 commits into
Open
th-82ad57: coding-tool parity across all polyglot LocalServers (go/ts/python/dotnet)#329brentrager wants to merge 5 commits into
brentrager wants to merge 5 commits into
Conversation
…edit files The go/ts/python/dotnet `cmd/serve` binaries called ServeLocal WITHOUT WithTools, so the local agent was chat-only and replied "I don't have file editing tools" — structural 0% on the parity bench. Fix GO first (prove the pattern, unlock a second engine in the matrix): - New go/server/coding_tools.go: CodingTools(workspace) builds the workspace-confined set read_file / write_file / edit_file / list_files / grep / bash as core.FuncTool, mirroring the Rust daemon's smooth-tools. Every path routes through resolveWorkspacePath (lexical, no symlink follow) so a prompt-injected agent can't escape the workspace; bash runs sh -c rooted at the workspace with a catastrophic-command deny + timeout. - Wire it in cmd/serve/main.go via WithTools, confined to SMOOTH_WORKSPACE (default: cwd, which is what the bench launches the server in). Opt out with SMOOTH_NO_TOOLS=1. CodingTools is the reusable pattern ts/python/dotnet copy. - coding_tools_test.go covers write→read round-trip, edit uniqueness/replace_all, list/grep skipping node_modules, bash cwd + catastrophic block, and path confinement (absolute + relative escapes rejected). - Fix a pre-existing compile break: tool_hooks_test.go called CreateSession with the old 4-arg signature (merge collision on main; Go dir lacks PR CI), which stopped the whole server test package from compiling. Verified live: built cmd/serve against llm.smoo.ai (deepseek-v4-flash), drove one canonical WS turn (create_conversation_session → send_message "Create a file hello.txt containing exactly: WORLD" → eventual_response). The agent emitted a write_file tool call, the tool wrote the file, and hello.txt landed on disk as exactly 5 bytes "WORLD". Follow-ups (same fix, sibling hosts): ts / python / dotnet cmd/serve. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ith Go) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFjpPZwCqFyBxhZba1fQzk
The Python serve binary booted a chat-only agent — no coding tools — so on any coding task it replied "I don't have file editing tools available" and scored a structural 0% on the parity bench. Ports go/server/coding_tools.go faithfully: the same six tools (read_file, write_file, edit_file, list_files, grep, bash), the same JSON-schema parameters, the same constants (read limit 2000, list cap 200 / walk budget 50000, grep cap 200, bash 50000 bytes / 120s), and the same catastrophic-command circuit breaker. Every model-supplied path routes through resolve_workspace_path, which confines it to the workspace root lexically — a prompt-injected agent can't read or write outside the workspace. Tested with absolute, relative, and dip-out-and-back escapes against every path-taking tool. Wired into serve_local on the sibling hosts' env contract: SMOOTH_WORKSPACE picks the root (default cwd), SMOOTH_NO_TOOLS=1 opts back out to chat-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFjpPZwCqFyBxhZba1fQzk
The .NET `serve` host booted a chat-only agent — no tools were registered on the IReadOnlyList<AITool> DI seam the FrameDispatcher reads from — so on any coding task the agent replied "I don't have file editing tools available" and scored a structural 0% on the parity bench. Faithful port of go/server/coding_tools.go: the same six tools (read_file, write_file, edit_file, list_files, grep, bash), the same argument names, the same limits (read 2000 lines, list 200 entries / 50000 walk budget, grep 200 matches, bash 50000 bytes / 120s), and the same catastrophic-command deny. Every path the model supplies routes through CodingTools.ResolveWorkspacePath, which confines it to the workspace root lexically — an escape (`../`, an absolute path outside the root, a sibling sharing the root's name prefix) is refused before any filesystem access. That guard is the trust boundary and is covered by tests that fail without it. Wired in the host on the Go env contract: SMOOTH_WORKSPACE (default: process cwd) sets the root, SMOOTH_NO_TOOLS=1 opts back out to chat-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFjpPZwCqFyBxhZba1fQzk
|
The TS server imports ToolHook/toolHooks from @smooai/smooth-operator-core (th-56aa52 IToolHook work), but the dep pinned ^0.20.4 which predates those exports — a pre-existing typecheck-test red on main. Published core 1.7.1 exports ToolHook/toolHooks/PermissionHook; bumping consumes it. No engine publish needed. All 233 server tests pass, typecheck clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QkUKdJ4Xbx7WXV2FAg555
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
Only the Rust engine wired a coding toolset. The go/ts/python/dotnet
servebinaries booted a chat-only agent — no file tools — so on any coding task the agent replied "I don't have file editing tools available" and scored a structural 0% on the parity bench. Shipping Go alone would leave the engine lopsided; parity means all four land together.What this does
Ports the same six workspace-confined tools Rust has —
read_file,write_file,edit_file,list_files,grep,bash— into every remaining LocalServer, each wired via that server'sWithToolsanalogue and gated on the same env contract (SMOOTH_NO_TOOLS,SMOOTH_WORKSPACEdefault cwd). Go (already merged-pattern here) is the reference; the other three mirror it faithfully, including identical constants (read limit 2000, list cap 200 / walk 50000, grep cap 200, bash output cap 50000, bash timeout 120s).Every filesystem path routes through a
resolveWorkspacePathequivalent confining it to the workspace root — a trust boundary, tested against..//absolute/symlink escapes in each language.coding_tools.gocmd/serve/main.gocodingTools.tsmain.tscoding_tools.pyserver.py/__main__.pyCodingTools.cshost/Program.csAll four verified green locally (the polyglot dirs lack PR CI, so local verification is the gate).
Known pre-existing caveat (NOT from this PR)
The TS server's full
pnpm buildcurrently fails onToolHook/toolHooksmissing from the published@smooai/smooth-operator-corenpm — the th-56aa52 IToolHook bump landed in the code but the core package exporting it isn't published yet (core→server publish-ordering gap). This is already the state onmain; the TS coding-tools themselves pass in isolation (15/15). The TS server won't fully run until core is published.🤖 Generated with Claude Code