Skip to content

[bug] muse-spark via opencode-go: undeclared client tool "default.apply_patch" kills subagent turns #3402

Closed

Description

@ianlyoo

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

  1. ocx start --port 10100 (bun entry src/cli/index.ts start --port 10100), Codex App pointed at http://127.0.0.1:10100/v1.
  2. Set any subagent model to opencode-go/muse-spark-1.3-contributor (all 32 custom agents under ~/.codex/agents unified to this model).
  3. Ask the subagent to do file-write work (apply_patch path).
  4. 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:

  1. src/types/tools.ts - normalizeDeclaredToolName() only normalizes the bare exec declaration; it does not map dotted ns.name to ns__name, and has no helper to find the declared exec wire name for a namespaced call.
  2. src/server/responses-undeclared-tool-guard.ts - addWireToolName() registers only the __ wire name, so default.apply_patch never lands in the declared set; the namespace-call check then rejects the dotted echo.
  3. src/server/responses/collaboration.ts - buildToolBridgeMaps() registers only the __ key in declaredToolNames / 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

  1. github-actions commented on Sep 3, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  2. added
    bugSomething isn't working
    proxyHTTP proxy, routing, reverse-proxy / management auth
    toolstool_calls, MCP, web-search / sidecar tools
    on Sep 3, 2026
  3. lidge-jun commented on Sep 3, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 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이지만, 지금 dev HEAD(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.ts normalizeDeclaredToolName — 점 철자 ns.name을 ns__name(또는 선언된 exec 와이어)으로 맞추는 경로가 없음
    라인 - src/server/responses-undeclared-tool-guard.ts addWireToolName — 선언 집합에 ns.name 별칭을 안 넣음 (toolChoiceAliases와 불일치)
    라인 - src/server/responses-undeclared-tool-guard.ts undeclaredNameInItem — 이름 필드에 점 철자가 통째로 오면 undeclared로 fail-close
    라인 - src/server/responses/collaboration.ts buildToolBridgeMaps — declaredToolNames/toolNsMap에 점 키 미등록
    경로/심볼 - tests/responses-undeclared-tool-guard.test.ts — default.apply_patch ↔ default__apply_patch 별칭 회귀 테스트 없음(확인 필요)

    메인테이너의 판단이 필요한 지점

    • 점 별칭을 전 네임스페이스에 열지, muse-spark/opencode-go처럼 관찰된 제공자만 열지
    • default 네임스페이스의 apply_patch가 bare exec 정규화와 겹칠 때 우선순위(네임스페이스 호출은 지금 코드가 legacy-normalize를 의도적으로 막음)
    • 리포터 패치를 PR로 받을지, 메인테이너가 dev에 직접 넣을지
    • bare 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이 작성했습니다

  4. lidge-jun commented on Sep 4, 2026

    @lidge-jun
    Owner

    Fixed by #3403, squash-merged into dev as 43248e4.

    The proxy now accepts the dotted ns.name spelling alongside the flattened ns__name when a routed provider echoes it back, so default.apply_patch from 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 to a.b.c, and a dotted spelling like x__y.z can 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 unambiguous ns__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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingproxyHTTP proxy, routing, reverse-proxy / management authtoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions