Repository navigation
[bug] muse-spark via opencode-go: undeclared client tool "default.apply_patch" kills subagent turns #3402
Description
Client or integration
Codex App
Area
Proxy and routing
Summary
When a Codex subagent runs on opencode-go/muse-spark-1.3-contributor (chain: Codex App -> opencodex proxy on 127.0.0.1:10100 -> opencode-go -> muse-spark) and attempts file-write work, the turn dies with:
stream disconnected before completion: routed provider emitted undeclared client tool "default.apply_patch"; only request-declared tools may be called
I expected the declared apply_patch tool call to be accepted. The tool IS declared - only the wire-name spelling differs: muse-spark echoes the call in dotted form (default.apply_patch) while the proxy guard only accepts the double-underscore form (default__apply_patch) / exact declared name, so it fail-closes a legitimate call.
Reproduction
ocx start --port 10100(bun entrysrc/cli/index.ts start --port 10100), Codex App pointed athttp://127.0.0.1:10100/v1.- Set any subagent model to
opencode-go/muse-spark-1.3-contributor(all 32 custom agents under~/.codex/agentsunified to this model). - Ask the subagent to do file-write work (apply_patch path).
- Observe the stream abort with the
undeclared client tool "default.apply_patch"error. Deterministic on namespace-qualified tool echo; retry does not help. Parent-model turns (e.g. gpt-5.6-sol) are unaffected.
Version
2.41.0
Operating system
Windows 10 Pro 25H2 (build 26200.9168)
Provider and model
opencode-go / muse-spark-1.3-contributor
Logs or error output
stream disconnected before completion: routed provider emitted undeclared client tool "default.apply_patch"; only request-declared tools may be called
Screenshots and supporting files
N/A
Redacted configuration
Chain only, no secrets: Codex App -> http://127.0.0.1:10100/v1 (opencodex proxy) -> opencode-go -> muse-spark-1.3-contributor as subagent model. No tokens or account details involved.
Root cause (analysis)
Three spots assume a single spelling and miss the dotted alias:
src/types/tools.ts-normalizeDeclaredToolName()only normalizes the bareexecdeclaration; it does not map dottedns.nametons__name, and has no helper to find the declared exec wire name for a namespaced call.src/server/responses-undeclared-tool-guard.ts-addWireToolName()registers only the__wire name, sodefault.apply_patchnever lands in the declared set; the namespace-call check then rejects the dotted echo.src/server/responses/collaboration.ts-buildToolBridgeMaps()registers only the__key indeclaredToolNames/toolNsMap, so the dotted echo misses the bridge map too.
Locally patched the installed package (added dottedToolName(), baseToolName(), findDeclaredExecWireName(); dot/__ aliases in guard and bridge maps) and verified with bun-level tests plus ocx restart / health OK. The local patch will be lost on npm update, hence this report.
Ask: please accept the dotted tool-call spelling ({ns}.{name}) as an alias of {ns}__{name} (and bare exec where applicable) in the undeclared-tool guard + bridge maps upstream. Happy to test a pre-release or submit the local patch as a PR if you point me at the preferred branch.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
github-actions commentedon Sep 3, 2026
Issue reopened
The report now contains the information required by the automated check. Thanks for updating it.
lidge-jun commentedon Sep 3, 2026
리뷰 · 우선순위 66 / 80
이 이슈는 Codex App 서브에이전트가
opencode-go/muse-spark-1.3-contributor로 파일 쓰기(apply_patch)를 할 때, opencodex 프록시가 스트림을 잘라 버리는 버그입니다. 체인는 Codex App →127.0.0.1:10100프록시 → opencode-go → muse-spark 입니다. 재현 에러는 정확히 이 한 줄입니다:routed provider emitted undeclared client tool "default.apply_patch"; only request-declared tools may be called. 부모 모델(예: gpt-5.6-sol) 턴은 멀쩡하고, 서브에이전트·네임스페이스 붙는 툴 에코에서만 납니다. 리포터 버전은 2.41.0이지만, 지금devHEAD(ea3231a, #3399 i18n prune 직후, 패키지 2.42.0)에도 같은 가드 로직이 그대로 있습니다. 고치는 커밋은 아직 없습니다.왜 터지는지 쉽게 말하면, “선언한 이름”과 “모델이 부른 이름”의 철자 규칙이 한 칸 어긋난 겁니다. 요청 카탈로그에는 보통 납작한 철자
default__apply_patch(또는 네임스페이스+이름)로 올라갑니다. muse-spark는 같은 도구를 점(.) 철자default.apply_patch로 다시 보냅니다. 프록시는 “요청에 적힌 이름만 허용”하는 가드가 있어서, 점 철자를 못 알아보면 합법적인 호출인데도 undeclared로 끊습니다. 재시도해도 같은 철자로 다시 오기 때문에 계속 실패합니다.지금 HEAD 코드를 보면 리포터가 짚은 세 곳이 맞습니다. 첫째,
src/types/tools.ts의normalizeDeclaredToolName()은 맨몸exec선언이 있을 때만apply_patch/exec_command같은 헬퍼를exec로 바꿔 줍니다.default.apply_patch처럼 점 붙은 이름은 전혀 다루지 않습니다. 같은 파일의toolChoiceAliases()는 이미 네임스페이스 도구에 대해ns__name과ns.name둘 다 인정합니다. 즉 tool_choice 쪽은 점 별칭을 아는데, undeclared 가드/브릿지 쪽은 모릅니다.둘째,
src/server/responses-undeclared-tool-guard.ts의addWireToolName()은 네임스페이스가 있으면namespacedToolName()으로ns__name만 집합에 넣고,exec가 아닐 때 맨몸 이름만 추가로 넣습니다.ns.name점 철자는 넣지 않습니다.undeclaredNameInItem()도 아이템에 별도namespace필드가 있으면ns__name만 대조하고, 이름 필드에default.apply_patch처럼 통째로 오면normalizeDeclaredToolName을 타는데 거기에도 점→밑줄 매핑이 없습니다. 그래서 에러 메시지에 찍힌default.apply_patch가 그대로 나옵니다.셋째,
src/server/responses/collaboration.ts의buildToolBridgeMaps()도declaredToolNames/toolNsMap에ns__name(그리고 아주 좁은 bare tool_choice 예외)만 등록합니다. 점 철자 키는 없습니다. 가드가 통과해도 브릿지 맵이 비면 복원/중계가 또 깨질 수 있어서, 두 경로를 같이 맞춰야 합니다. 리포터는 로컬에서dottedToolName/baseToolName/findDeclaredExecWireName과 점·__별칭을 넣어 bun 테스트와ocx restart로 확인했다고 했고, npm 업데이트 시 패치가 날아가니 업스트림 반영을 요청합니다. 방향은 타당합니다. 이미toolChoiceAliases가 점 별칭을 알고 있으니, 새 철자 체계를 만들기보다 그 규칙과 같은 축으로 가드·브릿지를 맞추는 편이 안전합니다. 관련 맥락으로는 HEAD에 이미 들어간 spark functions 네임스페이스 보존(#3224)과 self-named namespace scrub 범위 축소(#3234)가 있습니다. 이번 이슈는 그 축의 “에코 철자 호환” 구멍입니다.라인 -
src/types/tools.tsnormalizeDeclaredToolName— 점 철자ns.name을ns__name(또는 선언된 exec 와이어)으로 맞추는 경로가 없음라인 -
src/server/responses-undeclared-tool-guard.tsaddWireToolName— 선언 집합에ns.name별칭을 안 넣음 (toolChoiceAliases와 불일치)라인 -
src/server/responses-undeclared-tool-guard.tsundeclaredNameInItem— 이름 필드에 점 철자가 통째로 오면 undeclared로 fail-close라인 -
src/server/responses/collaboration.tsbuildToolBridgeMaps—declaredToolNames/toolNsMap에 점 키 미등록경로/심볼 -
tests/responses-undeclared-tool-guard.test.ts—default.apply_patch↔default__apply_patch별칭 회귀 테스트 없음(확인 필요)메인테이너의 판단이 필요한 지점
default네임스페이스의apply_patch가 bareexec정규화와 겹칠 때 우선순위(네임스페이스 호출은 지금 코드가 legacy-normalize를 의도적으로 막음)dev에 직접 넣을지apply_patch만 허용할지,default.apply_patch까지 반드시 넣을지(재현 로그상 후자가 필요)너의 추천
리포터에게
dev기준 PR을 열어 달라고 하고(또는 메인테이너가 동일 3파일+테스트를 직접),toolChoiceAliases와 같은 규칙으로 undeclared 가드와buildToolBridgeMaps에ns.name↔ns__name별칭을 넣고tests/responses-undeclared-tool-guard.test.ts에 회귀를 추가하세요. types.ts/config.ts 대형 분할과 무관한 독립 버그이므로 닫지 말고 살려 두세요. GUI #3399 직후 트레인과 겹치지 않습니다.이 댓글은 grok-bot이 작성했습니다
fix(proxy): accept dotted ns.name tool echo alongside ns__name (lidge…
fix(proxy): accept dotted ns.name tool echo alongside ns__name (#3403)
lidge-jun commentedon Sep 4, 2026
Fixed by #3403, squash-merged into
devas 43248e4.The proxy now accepts the dotted
ns.namespelling alongside the flattenedns__namewhen a routed provider echoes it back, sodefault.apply_patchfrom muse-spark via opencode-go restores against the declared tool instead of being refused as undeclared.One thing the review turned up and fixed on the same branch: a dotted alias is only unambiguous when it names exactly one tool. Dots are legal inside both a namespace and a name, so
{namespace: "a", name: "b.c"}and{namespace: "a.b", name: "c"}both flatten toa.b.c, and a dotted spelling likex__y.zcan also be read as another identity's canonical wire name. Alias ownership is now resolved across the whole tool catalog before anything is registered, so declaration order — which the caller controls — cannot decide which tool a provider echo dispatches to. Ambiguous spellings fall back to the unambiguousns__name, which every provider can still echo.Closing manually: this PR targeted
dev, and GitHub only auto-closes linked issues on the default branch.Thanks @ianlyoo.
fix(proxy): accept dotted ns.name tool echo alongside ns__name (lidge…