Repository navigation
fix: gateway tool constraints — Muse schema depth, Beam name length, LongCat 402 hint - #270
Merged
Merged
Conversation
Muse Spark routes to the Responses API, whose gateway rejects tool schemas nested deeper than 10 levels (HTTP 400). Deep MCP tools such as Postman's createCollection exceed that, while chat-completions models accepted the same schemas. Track depth through properties/items/additionalProperties/composition and resolved $ref targets; at MAX_TOOL_SCHEMA_DEPTH emit a childless node with leaf keywords only (an empty child still counts as a level upstream). Covered by three regression tests.
…letions Beam's upstream enforces the OpenAI 64-char function-name limit, but the issue #165 truncation only ran on the Responses API path while beam-free routes to chat-completions — long MCP tool names 400ed the whole request. Add a beam registry row, gate truncation with needsToolNameTruncation (muse + beam) in both tool mappers, rename buildResponsesToolNameMap to buildToolNameMap, and forward the truncated-to-original map through the chat-completions extractor so tool calls round-trip.
longcat-2.5-preview-free 402s with raw 'Upstream request failed: Endpoint is unavailable.' Live probes show the model still passes gateway validation (dummy-key 401), so the failure is at upstream serving time - the Router.Unavailable family via a different status, not the #204 removed-model class, and the model is deliberately not blocklisted. Add describeEndpointUnavailable() to the hint chain and make describeRouterUnavailable() return undefined on mismatch so later detectors actually run (it previously always returned the fallback string, short-circuiting the chain).
Upstream research settled the diagnosis: 'Endpoint is unavailable' is OpenCode's generic per-model incident error (402/429/503 across models - anomalyco/opencode #55607 for this very model, closed fixed; #56796; #57613), tracked on status.opencode.ai. A Free-inference upstream incident ran Oct 21 05:54-19:28 UTC and the reported failures (Oct 21 19:49-50 UTC, one ~1-minute burst, all payload sizes) sit in its aftermath window - a temporary OpenCode-side incident, not a dead model. A dead-model blocklist was drafted and then rejected by that evidence (squashed away here); the model stays selectable. describeEndpointUnavailable() maps the raw upstream text to an actionable hint that points at the status page, in both message and userMessage (the #258 chat-render contract). describeRouterUnavailable() now returns undefined on mismatch so later detectors in the hint chain actually run.
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.
Gateway-quirk fixes: Muse schema depth, Beam tool names, LongCat 402 hint
Three user-reported gateway failures, each verified with live probes before fixing. All three share one theme: protocol constraints enforced upstream that our request layer never modeled (schema nesting depth, function-name length, upstream serving availability).
What's fixed
1. Muse Spark — HTTP 400
JSON schema exceeds the maximum nesting depth of 10 levelsMuse routes to the Responses API, where the gateway rejects any tool schema nested deeper than 10 levels — and deeply nested MCP tools (Postman's
createCollectiontree) crossed that.sanitizeToolSchemaresolved$ref/$defsand guarded cycles but had no depth cap.Fix:
MAX_TOOL_SCHEMA_DEPTH = 10insrc/config.ts; the sanitizer tracks depth throughproperties/items/additionalProperties/anyOf/oneOf/allOfand resolved$reftargets, emitting a childless node with leaf keywords only at the ceiling (an empty child would still count as a level upstream). Shared, so all four request builders inherit it. Closes nothing tracked — reported directly.2. Beam Free — HTTP 400
Invalid value for 'tools[7].function.name': maximum string length is 64Beam's upstream enforces the OpenAI 64-char function-name limit, but the #165 truncation only ran on the Responses API path while
beam-freeroutes to chat-completions. Adds abeamregistry family, gates truncation withneedsToolNameTruncation()(muse + beam) in both tool mappers, and forwards the truncated→originaltoolNameMapthrough the chat-completions extractor so tool calls round-trip.3. LongCat Free — HTTP 402
Endpoint is unavailablesurfaced rawlongcat-2.5-preview-free402s with raw upstream text (misleading "Payment Required" for Go subscribers). Live probes showed the model passes gateway validation (dummy-key → 401 at the credential check), so the failure is upstream serving time. Web research confirmed the reading:Endpoint is unavailableis OpenCode's generic per-model incident error (anomalyco/opencode #55607 for this very model — closed fixed; #56796; #57613), tracked on status.opencode.ai — a Free-inference incident ran Oct 21 05:54–19:28 UTC and the reported failures sit in its aftermath. Temporary incident, not a dead model — the model stays selectable;describeEndpointUnavailable()maps the error to an actionable hint pointing at the status page. Side-fix:describeRouterUnavailable()returned the fallback string on mismatch, silently short-circuiting the whole??hint chain — now returnsundefined.Verification
npm test— 559/559 pass (11 new regression tests across the three fixes)npm run lint— all 7 checks pass (pre-commit gate also ran on every commit)