diff --git a/src/responses/code-mode-helper-compat.ts b/src/responses/code-mode-helper-compat.ts index d390bb76cb4..0af986fe212 100644 --- a/src/responses/code-mode-helper-compat.ts +++ b/src/responses/code-mode-helper-compat.ts @@ -43,8 +43,12 @@ export function compileCodeModeHelperInput( // is an apply_patch wrapper and is not an `exec` fallback field, and the recognizer already // declines it under `exec`; reading it here would compile a body that recognition rejected, // which is exactly the drift a second, looser unwrap introduces. + const bodyToolName = wireToolName ?? helperName; + const normalizedBodyToolName = bodyToolName.startsWith("default.") + ? bodyToolName.slice("default.".length) + : bodyToolName; const patch = normalizeApplyPatchDelimiters( - unwrapFreeformToolInput(argumentsText, wireToolName ?? helperName), + unwrapFreeformToolInput(argumentsText, normalizedBodyToolName), ); return `const result = await tools.apply_patch(${JSON.stringify(patch)});\ntext(result);`; } diff --git a/structure/providers/chat-compat.md b/structure/providers/chat-compat.md index cf601215ed8..4670e75300a 100644 --- a/structure/providers/chat-compat.md +++ b/structure/providers/chat-compat.md @@ -41,7 +41,7 @@ compatibility layer. Its endpoint profile and privacy boundary are specified in Chat models sometimes return a freeform call body under a common alternate field or wrap the whole body in a Markdown fence. Restoration in `src/responses/apply-patch-envelope.ts` is deliberately -narrow: only bare `exec` and `apply_patch` accept one recognized alternate field or one complete +narrow: only bare or `default.`-prefixed `exec` and `apply_patch` accept one recognized alternate field or one complete outer fence, while ambiguous wrappers and provider-owned freeform grammars remain byte-exact. Kiro groups only consecutive original-message tool results whose raw call ID exactly matches diff --git a/structure/providers/kiro.md b/structure/providers/kiro.md index b1d0b503d55..1fb17b688a2 100644 --- a/structure/providers/kiro.md +++ b/structure/providers/kiro.md @@ -26,7 +26,7 @@ reserves the private completion tool. Meta Muse 64-character MCP aliases live in Kiro shares the Responses freeform restoration boundary in `src/responses/apply-patch-envelope.ts`: contractual `input` wrappers are unwrapped, while alternate -field and outer-fence recovery is limited to unambiguous bare `exec` and `apply_patch` bodies. +field and outer-fence recovery is limited to unambiguous bare or `default.`-prefixed `exec` and `apply_patch` bodies. Kiro refuses structured output and tolerates every other Responses `text` member. `text.format` of type `json_schema` or `json_object` is a contract the CodeWhisperer wire cannot honour, so the diff --git a/structure/providers/xai-grok.md b/structure/providers/xai-grok.md index 4dfa27723f0..fcef52b4dc8 100644 --- a/structure/providers/xai-grok.md +++ b/structure/providers/xai-grok.md @@ -28,7 +28,7 @@ Shared parsing and streaming follow the [request-copy](../transports/byte-accoun Grok's Responses path shares `src/responses/apply-patch-envelope.ts` for freeform restoration. The declared `input` field remains authoritative; alternate-field and outer-fence recovery is -limited to unambiguous bare `exec` and `apply_patch` calls and does not rewrite foreign grammars. +limited to unambiguous bare or `default.`-prefixed `exec` and `apply_patch` calls and does not rewrite foreign grammars. Grounded in the open-sourced official client (xai-org/grok-build); unit + evidence: `devlog/_fin/260716_grok_build_hardening/`. diff --git a/structure/runtime.md b/structure/runtime.md index ec03516b8cc..935966339ed 100644 --- a/structure/runtime.md +++ b/structure/runtime.md @@ -319,7 +319,8 @@ On `error` / incomplete / stall / EOF — and when assembled non-freeform tool a an open tool call is cancelled as `status: "incomplete"` without `function_call_arguments.done`, so the client never sees a completed call ahead of `response.failed` / `response.incomplete`. At the freeform boundary, `src/responses/apply-patch-envelope.ts` unwraps the contractual `input` -field for every tool. Only bare `exec` and `apply_patch` calls may recover one recognized alternate +field for every tool. Only bare or `default.`-prefixed `exec` and `apply_patch` calls may recover +one recognized alternate body field or remove one complete outer Markdown fence; ambiguous alternate fields and every other freeform grammar pass through unchanged. diff --git a/structure/transports/responses.md b/structure/transports/responses.md index 09bf0d1ccb7..36e68abcde7 100644 --- a/structure/transports/responses.md +++ b/structure/transports/responses.md @@ -121,7 +121,7 @@ discarded to manufacture a bare name. Function-call wrappers around freeform bodies are restored by `src/responses/apply-patch-envelope.ts`. The declared `input` field is authoritative. For bare -`exec` and `apply_patch`, one tool-specific alternate field or one complete outer Markdown fence +`exec` and `apply_patch` (including their `default.`-prefixed provider aliases), one tool-specific alternate field or one complete outer Markdown fence is recoverable because the wrapper is otherwise unusable; two alternate fields are ambiguous and therefore remain untouched. Foreign freeform grammars never receive that compatibility rewrite. diff --git a/tests/adapters/openai/openai-chat-dangling-toolcalls.test.ts b/tests/adapters/openai/openai-chat-dangling-toolcalls.test.ts index 41a61c21023..075e3c33186 100644 --- a/tests/adapters/openai/openai-chat-dangling-toolcalls.test.ts +++ b/tests/adapters/openai/openai-chat-dangling-toolcalls.test.ts @@ -12,6 +12,10 @@ const provider: OcxProviderConfig = { baseUrl: "https://example.test/v1", apiKey: "sk-test", authMode: "key", + // The wire role folds to `system` unless a destination is recorded as accepting + // `developer`; this suite is about tool-result repair ordering, so it declares the + // destination rather than asserting the default. + foldDeveloperRoleToSystem: false, }; interface ChatMsg { diff --git a/tests/responses/chat-inline-document-bytes.test.ts b/tests/responses/chat-inline-document-bytes.test.ts index dd88fa05788..a718c1c376a 100644 --- a/tests/responses/chat-inline-document-bytes.test.ts +++ b/tests/responses/chat-inline-document-bytes.test.ts @@ -28,6 +28,10 @@ const chatProvider: OcxProviderConfig = { adapter: "openai-chat", baseUrl: "https://gateway.example.internal/v1", apiKey: "k", + // The wire role folds to `system` unless a destination is recorded as accepting + // `developer`; the document test asserts the role a turn keeps, so it declares the + // destination rather than asserting the default. + foldDeveloperRoleToSystem: false, }; const anthropicProvider = { adapter: "anthropic", diff --git a/tests/responses/responses-code-mode-patch-compile.test.ts b/tests/responses/responses-code-mode-patch-compile.test.ts index aa12e7c63fd..2f3b174628b 100644 --- a/tests/responses/responses-code-mode-patch-compile.test.ts +++ b/tests/responses/responses-code-mode-patch-compile.test.ts @@ -52,6 +52,16 @@ describe("code-mode apply_patch compiles the body recognition accepted", () => { expect(compileAsBridge(JSON.stringify({ patch: PATCH }))).toBeUndefined(); }); + test("a default.apply_patch alias keeps the native apply_patch vocabulary", () => { + for (const key of ["patch", "content"]) { + expect(compileCodeModeHelperInput( + JSON.stringify({ [key]: PATCH }), + "default.apply_patch", + "default.apply_patch", + )).toBe(EXPECTED); + } + }); + test("a normal code-mode JavaScript body is left alone", () => { for (const body of [ 'const result = await tools.exec_command({ cmd: "ls" });\ntext(result);',