diff --git a/packages/llm/src/protocols/openai-responses.ts b/packages/llm/src/protocols/openai-responses.ts index be395b053f61..00100c1a0a6c 100644 --- a/packages/llm/src/protocols/openai-responses.ts +++ b/packages/llm/src/protocols/openai-responses.ts @@ -53,7 +53,7 @@ const OpenAIResponsesReasoningSummaryText = Schema.Struct({ const OpenAIResponsesReasoningItem = Schema.Struct({ type: Schema.tag("reasoning"), - id: Schema.String, + id: Schema.optionalKey(Schema.String), summary: Schema.Array(OpenAIResponsesReasoningSummaryText), encrypted_content: optionalNull(Schema.String), }) @@ -101,6 +101,7 @@ type OpenAIResponsesReasoningInput = { summary: Array<{ type: "summary_text"; text: string }> encrypted_content?: string | null } +type OpenAIResponsesReasoningReplay = Omit const OpenAIResponsesTool = Schema.Struct({ type: Schema.tag("function"), @@ -366,7 +367,7 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ if (message.role === "assistant") { const content: TextPart[] = [] - const reasoningItems: Record = {} + const reasoningItems: Record = {} const reasoningReferences = new Set() const hostedToolReferences = new Set() const flushText = () => { @@ -383,7 +384,7 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ flushText() const reasoning = lowerReasoning(part) if (!reasoning) continue - if (store !== false && reasoning.id) { + if (store !== false) { if (!reasoningReferences.has(reasoning.id)) input.push({ type: "item_reference", id: reasoning.id }) reasoningReferences.add(reasoning.id) continue @@ -395,8 +396,13 @@ const lowerMessages = Effect.fn("OpenAIResponses.lowerMessages")(function* (requ existing.encrypted_content = reasoning.encrypted_content continue } - reasoningItems[reasoning.id] = reasoning - input.push(reasoning) + const replay = { + type: reasoning.type, + summary: reasoning.summary, + encrypted_content: reasoning.encrypted_content, + } + reasoningItems[reasoning.id] = replay + input.push(replay) continue } if (part.type === "tool-call") { @@ -974,6 +980,7 @@ export const route = Route.make({ endpoint, auth, transport: httpTransport, + defaults: { providerOptions: { openai: { store: false } } }, }) const decodeWebSocketMessage = ProviderShared.validateWith(Schema.decodeUnknownEffect(OpenAIResponsesWebSocketMessage)) @@ -1001,6 +1008,7 @@ export const webSocketRoute = Route.make({ endpoint, auth, transport: webSocketTransport, + defaults: { providerOptions: { openai: { store: false } } }, }) export * as OpenAIResponses from "./openai-responses" diff --git a/packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-reasoning-continuation.json b/packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-reasoning-continuation.json index 850e381cafbd..47670c81272d 100644 --- a/packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-reasoning-continuation.json +++ b/packages/llm/test/fixtures/recordings/openai-responses/openai-responses-gpt-5-5-reasoning-continuation.json @@ -44,7 +44,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Think briefly, then reply exactly with: Hello!\"}]},{\"type\":\"reasoning\",\"id\":\"rs_0a0794dab3b8ec7d016a1235e7ce3881958a5eca32a36a14c5\",\"summary\":[],\"encrypted_content\":\"gAAAAABqEjXoGMCw3WDXpoD9151PEr2Lt8raW7KBKefQhZJGWx5f8jy152bApO6oE-Mr1BhUtfZNq3OPBVfSL4ioQ9bHREfujIBXgk9LUDBAz2Sle7KjOr9HaUV16A4HBiaFIRFjsHPS9G8yEySp1m6F1CD_WR6apyUGgugRh_y39EcOJmxPOzmiac5DVM6fraA1VpcGbqrZ1x2ANHFDOfnYTycPtPNTgzE7LjkYjDDWbT03uN1YxfP4pqjDVRzY14pA8bSZ8ys-pDv5kUFCAsw-OlU4jYKUXp-M8_6KTaRQP71LPwppt__zG_NJPfy-qUil4pOU8_NoxtxerHgLLXbfExZdzfpoGinoEjn7nj7BJDEtl-LNeNEb5c-1ZymNfVMp-Cs3fLEPkAV8rtHFtZ0MhE_07GKbGo7hTrOmkM4DydxmHsdWGNbXAG35cprslEA5P7p3GHFKnRs5hGs2eq-XcZ3yki64ZBOU_Tv6UR7nUH09gF1rdrJo3dpre6M00COwwdZ02zUP5KxCuI8FKu2jsZu9zgMVXDALsdtM5orTCVLXsn4rddWd111zE-vMjNmMMmktW2cHMjH7j1ooA-9P083koNVYiLi4UhMA64gTqgyl8MxkZekl7eFSMa7qk295NaHOKtFxzYYcZ9jdioCwSPSZ0ZZWLoNgrK7SWfRh0uaTHNcMZ3wq8ae6CguktIeVTCPTQAqJLQqd7AU0oOCKCJ7BWnC-L8UC6m7Pm9ZS958uUVeWBhgKHzMAGq9UeQB7IEeAcbMn3EDgOSfd8qCb8iwU9iG9dcu9axQwWU7pd7kd-T-He61W7z5wWgpx1KehWCxrN6kuKSo6p-uUfwVnJukreOn8BJNAzADQgz68bhmN9VGih7YcKVnLgwDwKditrjSd6-tfE0Baarj3jWENvT6ohY17R9FDrKS-2v8IIX6tGjoKJw8SRhaWLNv4vWlmxRgR0gdac3qumd0GKqsWSveNz01naA==\"},{\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Hello!\"}]},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Now reply exactly with: Done.\"}]}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"low\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":40,\"stream\":true}" + "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Think briefly, then reply exactly with: Hello!\"}]},{\"type\":\"reasoning\",\"summary\":[],\"encrypted_content\":\"gAAAAABqEjXoGMCw3WDXpoD9151PEr2Lt8raW7KBKefQhZJGWx5f8jy152bApO6oE-Mr1BhUtfZNq3OPBVfSL4ioQ9bHREfujIBXgk9LUDBAz2Sle7KjOr9HaUV16A4HBiaFIRFjsHPS9G8yEySp1m6F1CD_WR6apyUGgugRh_y39EcOJmxPOzmiac5DVM6fraA1VpcGbqrZ1x2ANHFDOfnYTycPtPNTgzE7LjkYjDDWbT03uN1YxfP4pqjDVRzY14pA8bSZ8ys-pDv5kUFCAsw-OlU4jYKUXp-M8_6KTaRQP71LPwppt__zG_NJPfy-qUil4pOU8_NoxtxerHgLLXbfExZdzfpoGinoEjn7nj7BJDEtl-LNeNEb5c-1ZymNfVMp-Cs3fLEPkAV8rtHFtZ0MhE_07GKbGo7hTrOmkM4DydxmHsdWGNbXAG35cprslEA5P7p3GHFKnRs5hGs2eq-XcZ3yki64ZBOU_Tv6UR7nUH09gF1rdrJo3dpre6M00COwwdZ02zUP5KxCuI8FKu2jsZu9zgMVXDALsdtM5orTCVLXsn4rddWd111zE-vMjNmMMmktW2cHMjH7j1ooA-9P083koNVYiLi4UhMA64gTqgyl8MxkZekl7eFSMa7qk295NaHOKtFxzYYcZ9jdioCwSPSZ0ZZWLoNgrK7SWfRh0uaTHNcMZ3wq8ae6CguktIeVTCPTQAqJLQqd7AU0oOCKCJ7BWnC-L8UC6m7Pm9ZS958uUVeWBhgKHzMAGq9UeQB7IEeAcbMn3EDgOSfd8qCb8iwU9iG9dcu9axQwWU7pd7kd-T-He61W7z5wWgpx1KehWCxrN6kuKSo6p-uUfwVnJukreOn8BJNAzADQgz68bhmN9VGih7YcKVnLgwDwKditrjSd6-tfE0Baarj3jWENvT6ohY17R9FDrKS-2v8IIX6tGjoKJw8SRhaWLNv4vWlmxRgR0gdac3qumd0GKqsWSveNz01naA==\"},{\"role\":\"assistant\",\"content\":[{\"type\":\"output_text\",\"text\":\"Hello!\"}]},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"Now reply exactly with: Done.\"}]}],\"store\":false,\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"low\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"max_output_tokens\":40,\"stream\":true}" }, "response": { "status": 200, diff --git a/packages/llm/test/provider/openai-responses.test.ts b/packages/llm/test/provider/openai-responses.test.ts index 06434e1c0291..d72611285cdb 100644 --- a/packages/llm/test/provider/openai-responses.test.ts +++ b/packages/llm/test/provider/openai-responses.test.ts @@ -50,6 +50,7 @@ describe("OpenAI Responses route", () => { { role: "system", content: "You are concise." }, { role: "user", content: [{ type: "input_text", text: "Say hello." }] }, ], + store: false, stream: true, max_output_tokens: 20, temperature: 0, @@ -161,16 +162,16 @@ describe("OpenAI Responses route", () => { Effect.gen(function* () { const prepared = yield* LLMClient.prepare( LLM.updateRequest(request, { - model: OpenAI.configure({ baseURL: "https://api.openai.test/v1/", apiKey: "test" }).responsesWebSocket( - "gpt-4.1-mini", - ), + model: OpenAIResponses.webSocketRoute + .with({ endpoint: { baseURL: "https://api.openai.test/v1/" }, auth: Auth.bearer("test") }) + .model({ id: "gpt-4.1-mini" }), }), ) expect(prepared.route).toBe("openai-responses-websocket") expect(prepared.protocol).toBe("openai-responses") expect(prepared.metadata).toEqual({ transport: "websocket-json" }) - expect(prepared.body).toMatchObject({ model: "gpt-4.1-mini", stream: true }) + expect(prepared.body).toMatchObject({ model: "gpt-4.1-mini", store: false, stream: true }) }), ) @@ -356,7 +357,13 @@ describe("OpenAI Responses route", () => { { type: "function_call", call_id: "call_1", name: "lookup", arguments: '{"query":"weather"}' }, { type: "function_call_output", call_id: "call_1", output: '{"forecast":"sunny"}' }, ], + store: false, stream: true, + max_output_tokens: undefined, + temperature: undefined, + tool_choice: undefined, + tools: undefined, + top_p: undefined, }) }), ) @@ -519,7 +526,6 @@ describe("OpenAI Responses route", () => { }, { type: "reasoning", - id: "rs_continuation_1", encrypted_content: "encrypted-continuation-state", summary: [{ type: "summary_text", text: "I inspected the previous turn." }], }, @@ -864,7 +870,9 @@ describe("OpenAI Responses route", () => { it.effect("closes reasoning summary parts when storage is not disabled", () => Effect.gen(function* () { - const response = yield* LLMClient.generate(request).pipe( + const response = yield* LLMClient.generate( + LLM.updateRequest(request, { providerOptions: { openai: { store: true } } }), + ).pipe( Effect.provide( fixedResponse( sseEvents( @@ -925,12 +933,12 @@ describe("OpenAI Responses route", () => { dynamicResponse((input) => Effect.gen(function* () { const web = yield* HttpClientRequest.toWeb(input.request).pipe(Effect.orDie) - expect(yield* Effect.promise(() => web.json())).toMatchObject({ + const body = yield* Effect.promise(() => web.json()) + expect(body).toMatchObject({ input: [ { role: "user", content: [{ type: "input_text", text: "What changed?" }] }, { type: "reasoning", - id: "rs_1", encrypted_content: "encrypted-state", summary: [{ type: "summary_text", text: "Checked the previous diff." }], }, @@ -938,6 +946,7 @@ describe("OpenAI Responses route", () => { { role: "user", content: [{ type: "input_text", text: "Summarize it." }] }, ], }) + expect(body.input[1]).not.toHaveProperty("id") return input.respond( sseEvents( { type: "response.output_text.delta", item_id: "msg_1", delta: "Parser now round-trips reasoning." }, @@ -984,7 +993,6 @@ describe("OpenAI Responses route", () => { { role: "assistant", content: [{ type: "output_text", text: "Before." }] }, { type: "reasoning", - id: "rs_1", encrypted_content: "encrypted-state", summary: [{ type: "summary_text", text: "Checked order." }], }, @@ -1078,7 +1086,6 @@ describe("OpenAI Responses route", () => { expect(prepared.body.input).toEqual([ { type: "reasoning", - id: "rs_1", encrypted_content: "encrypted-state", summary: [ { type: "summary_text", text: "First" }, diff --git a/packages/llm/test/tool-runtime.test.ts b/packages/llm/test/tool-runtime.test.ts index c69456e425d1..c03a18bd8aa2 100644 --- a/packages/llm/test/tool-runtime.test.ts +++ b/packages/llm/test/tool-runtime.test.ts @@ -598,7 +598,7 @@ describe("LLMClient tools", () => { include: ["reasoning.encrypted_content"], input: [ { role: "user" }, - { type: "reasoning", id: "rs_1", summary: [], encrypted_content: "encrypted-state" }, + { type: "reasoning", summary: [], encrypted_content: "encrypted-state" }, { type: "function_call", call_id: "call_1", name: "get_weather" }, { type: "function_call_output", call_id: "call_1" }, ], diff --git a/packages/opencode/test/fixtures/recordings/session/native-openai-oauth-tool-loop.json b/packages/opencode/test/fixtures/recordings/session/native-openai-oauth-tool-loop.json index 380a572b0b2e..7194987fce94 100644 --- a/packages/opencode/test/fixtures/recordings/session/native-openai-oauth-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/session/native-openai-oauth-tool-loop.json @@ -33,7 +33,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"reasoning\",\"id\":\"rs_0812d6cbe7a2b19b016a1214d32f6881998bcd9ff2e739d7f2\",\"summary\":[],\"encrypted_content\":\"gAAAAABqEhTUCQT4XELlBu6r5VHqqtu5Il5WdX4m1upE8li0mPmIwgIykAmUTZWiE0213kmviuAgIrmhhiL4B8DXbWQD2vOEkQMhpZq_UCqc22SOg-4DpQLrebMWkzgAPL618VPu9mXNUIH9BW1sRhPdDSbbtK5_bitzsn-FMJGcO3UN7Ga2RW1Rdvt1M3m7J4MRlTutH8cwY8SthzgvOFEBS-_IrAhiwKVz4Se9Jlu3pVNMqhPF7kdrQOfDYui0v-AT8VrHBVomqekJl_dWESww0eWo6bS1PxZB4cLQHWp9JJi5pEECvU9Ntcz3GxuGJEtTKq5mFcRvCanXHOwZGmbBcWMNdVyikk3fxgIE2g9t8rCKJmhNXznMERtrfG2tey19qWbsVbo2YmBbg_5N02AA4NmEVvdfgHJx58nOfEEc2OZYk0YQ1fHBOkpBnwY61hxtrWFdj48QnTEKuvjAyNpX-KKFmMzL4531yLbEEzpaERlr11fDeoMpKofUoMsg3Jz8aTaZ1CpzI3O7iFzGDEV6gKh8vQYGrKOaOXnfBVDXDo8iJhZywpcQY6xB4NNf4pyyjFkR-vjgvBYV2hejlq2V1j8vQHgy8CsZJ6lW5oaTNMfP76MAHlwUwyMYj-cFmuX0epJdDWv8GDznUpOS-v2X5eNsvyx9qvvcTEMLsKJ--3_odisilj4vPhw16P9fB8eLmvESZmJRYmWM4mO7hPTVXOooOa-zxRHGhRQH9ouUea9UHSuH1A0o54qTEPr-JqYlQggugW449IuYW4HSMNMyeGdUNJfodWRu5cL0VPgk6zwTU3ArBq28FDgG7NZMk3njfCId351GZ8VRlTMA6U522_6FFaZ8-5gxsidOm0WULOwyTTo54tJsJFv2pgYUKs0VFWSwi3rvNMVMOgwOVIdSgZt1hFTxBImZh8HUIXUPvdOVKZzQmWT5M6uOTUsm5xsufhj8m79RuYZh2J0bkVOBzZ1As8zH-4v_r9d7e8464EuWXCln_6LAJdrTYgE2gVfHK0zeUaAMbIKhirOf0AVQZyfVsGvJ_CPqrPE_QSECeSA2D4TSa5Tc_IRY-Fb2_HKNCMEP2uvy\"},{\"type\":\"function_call\",\"call_id\":\"call_Ix5Urx04RtKsUJ75K0vTTgFF\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_Ix5Urx04RtKsUJ75K0vTTgFF\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"Answer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"prompt_cache_key\":\"session-recorded-openai-oauth-loop\",\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"stream\":true}" + "body": "{\"model\":\"gpt-5.5\",\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"reasoning\",\"summary\":[],\"encrypted_content\":\"gAAAAABqEhTUCQT4XELlBu6r5VHqqtu5Il5WdX4m1upE8li0mPmIwgIykAmUTZWiE0213kmviuAgIrmhhiL4B8DXbWQD2vOEkQMhpZq_UCqc22SOg-4DpQLrebMWkzgAPL618VPu9mXNUIH9BW1sRhPdDSbbtK5_bitzsn-FMJGcO3UN7Ga2RW1Rdvt1M3m7J4MRlTutH8cwY8SthzgvOFEBS-_IrAhiwKVz4Se9Jlu3pVNMqhPF7kdrQOfDYui0v-AT8VrHBVomqekJl_dWESww0eWo6bS1PxZB4cLQHWp9JJi5pEECvU9Ntcz3GxuGJEtTKq5mFcRvCanXHOwZGmbBcWMNdVyikk3fxgIE2g9t8rCKJmhNXznMERtrfG2tey19qWbsVbo2YmBbg_5N02AA4NmEVvdfgHJx58nOfEEc2OZYk0YQ1fHBOkpBnwY61hxtrWFdj48QnTEKuvjAyNpX-KKFmMzL4531yLbEEzpaERlr11fDeoMpKofUoMsg3Jz8aTaZ1CpzI3O7iFzGDEV6gKh8vQYGrKOaOXnfBVDXDo8iJhZywpcQY6xB4NNf4pyyjFkR-vjgvBYV2hejlq2V1j8vQHgy8CsZJ6lW5oaTNMfP76MAHlwUwyMYj-cFmuX0epJdDWv8GDznUpOS-v2X5eNsvyx9qvvcTEMLsKJ--3_odisilj4vPhw16P9fB8eLmvESZmJRYmWM4mO7hPTVXOooOa-zxRHGhRQH9ouUea9UHSuH1A0o54qTEPr-JqYlQggugW449IuYW4HSMNMyeGdUNJfodWRu5cL0VPgk6zwTU3ArBq28FDgG7NZMk3njfCId351GZ8VRlTMA6U522_6FFaZ8-5gxsidOm0WULOwyTTo54tJsJFv2pgYUKs0VFWSwi3rvNMVMOgwOVIdSgZt1hFTxBImZh8HUIXUPvdOVKZzQmWT5M6uOTUsm5xsufhj8m79RuYZh2J0bkVOBzZ1As8zH-4v_r9d7e8464EuWXCln_6LAJdrTYgE2gVfHK0zeUaAMbIKhirOf0AVQZyfVsGvJ_CPqrPE_QSECeSA2D4TSa5Tc_IRY-Fb2_HKNCMEP2uvy\"},{\"type\":\"function_call\",\"call_id\":\"call_Ix5Urx04RtKsUJ75K0vTTgFF\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_Ix5Urx04RtKsUJ75K0vTTgFF\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"instructions\":\"Answer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\",\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"prompt_cache_key\":\"session-recorded-openai-oauth-loop\",\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"text\":{\"verbosity\":\"low\"},\"stream\":true}" }, "response": { "status": 200, diff --git a/packages/opencode/test/fixtures/recordings/session/native-zen-tool-loop.json b/packages/opencode/test/fixtures/recordings/session/native-zen-tool-loop.json index e25bc9fae6f9..dfb8632919c4 100644 --- a/packages/opencode/test/fixtures/recordings/session/native-zen-tool-loop.json +++ b/packages/opencode/test/fixtures/recordings/session/native-zen-tool-loop.json @@ -35,7 +35,7 @@ "headers": { "content-type": "application/json" }, - "body": "{\"model\":\"gpt-5.2-codex\",\"input\":[{\"role\":\"system\",\"content\":\"Answer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"reasoning\",\"id\":\"rs_0fdce240b46054ad016a1214d326848196b269feebe1844759\",\"summary\":[],\"encrypted_content\":\"gAAAAABqEhTTGeallj_mC3ciDydiTVJLA6bjJfitoj4ftFfWwlxekFNaf_cDNWP3pE6qsvK9gKJNRfbAbpaEVf1qjAhQx53witrmt6H3KaaNJm3wXHG5sEi9gp3nLWK4T76tcVYHG1x6mbbTjEjCvhIuEkn_7Q7lJ1BErkEURYBBMPmkKya2-YuL8XP14Yrko9BA1t56BkwK5U3TFse4nwHI1qi82hdkX_aYAtz6YgbTpf-dvOCBGfeApxWLFotkt355Qy2b6MmPaH6cQwrvLJXOqEzGkwxFcs3mLEKLV103gd8Z5e_OapjJHTv_LarN-WN9C7nCQ0BBHClk4ND3SDdGb-XV665r23RB40GJ3Q9brJALGaJhij4uceXZNYbakZVOxgqLuDnX6EgABwEzrZb7vhVAKCewVYkLDu0LiS1rIvcFT8HpovxaBU2F2kVG7TRvzYewCW9zXWnAR048p5pUvi6zfMzapk8bnl4uM_uD45gp1sMzeSHryai1U0AUO2cLeQV1pA7KJoJBwWlHxo0YNPbDidI2KfByIoI0A7oiKoZ32vJkiwx3BEGePnzb-JQnv1eDXwlimICVKEVPk1BxpUZ2XBoWdUGYR77u5NGmZ2sKh4OM-qIaB0VaChGsCsJLyQ5_MCkeOm9EMjg1cXbIHDzs9jpF2BXlowY1Vw_L-Ve6nzwK7ZcyHM3ij27wEXYO2On6zbN_AqOvX_CFAjI7ktCYF2guftXuVpFCuiqRyDZ6i2RHXMhR77CoPT97sAvXDejN8feNtidqq4OH5uLa3BHYvW0UKfNlBCOL6A6927l4iTKURZznq_mVjLgTHWv9k-ByxP0hC5sIQHyB5hJaD8_svMr4Aqz_vH9Z8HShgjK47NsMQKxGGgaXdnq3xEdwydM-hTG4Pi35o6Kt0bbJ5KTRQ2ObjmnVTG7J__QTKMTrK2S6Ro4VIMrYzaai7BTLa8MGNotj\"},{\"type\":\"function_call\",\"call_id\":\"call_hwPdXfzZmrdySXU2ZmrL51Ln\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_hwPdXfzZmrdySXU2ZmrL51Ln\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"prompt_cache_key\":\"session-recorded-opencode-loop\",\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"max_output_tokens\":32000,\"stream\":true}" + "body": "{\"model\":\"gpt-5.2-codex\",\"input\":[{\"role\":\"system\",\"content\":\"Answer using tools when appropriate.\\nUse the get_weather tool exactly once to look up Paris, then reply with exactly: Paris is sunny.\"},{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"What is the weather in Paris?\"}]},{\"type\":\"reasoning\",\"summary\":[],\"encrypted_content\":\"gAAAAABqEhTTGeallj_mC3ciDydiTVJLA6bjJfitoj4ftFfWwlxekFNaf_cDNWP3pE6qsvK9gKJNRfbAbpaEVf1qjAhQx53witrmt6H3KaaNJm3wXHG5sEi9gp3nLWK4T76tcVYHG1x6mbbTjEjCvhIuEkn_7Q7lJ1BErkEURYBBMPmkKya2-YuL8XP14Yrko9BA1t56BkwK5U3TFse4nwHI1qi82hdkX_aYAtz6YgbTpf-dvOCBGfeApxWLFotkt355Qy2b6MmPaH6cQwrvLJXOqEzGkwxFcs3mLEKLV103gd8Z5e_OapjJHTv_LarN-WN9C7nCQ0BBHClk4ND3SDdGb-XV665r23RB40GJ3Q9brJALGaJhij4uceXZNYbakZVOxgqLuDnX6EgABwEzrZb7vhVAKCewVYkLDu0LiS1rIvcFT8HpovxaBU2F2kVG7TRvzYewCW9zXWnAR048p5pUvi6zfMzapk8bnl4uM_uD45gp1sMzeSHryai1U0AUO2cLeQV1pA7KJoJBwWlHxo0YNPbDidI2KfByIoI0A7oiKoZ32vJkiwx3BEGePnzb-JQnv1eDXwlimICVKEVPk1BxpUZ2XBoWdUGYR77u5NGmZ2sKh4OM-qIaB0VaChGsCsJLyQ5_MCkeOm9EMjg1cXbIHDzs9jpF2BXlowY1Vw_L-Ve6nzwK7ZcyHM3ij27wEXYO2On6zbN_AqOvX_CFAjI7ktCYF2guftXuVpFCuiqRyDZ6i2RHXMhR77CoPT97sAvXDejN8feNtidqq4OH5uLa3BHYvW0UKfNlBCOL6A6927l4iTKURZznq_mVjLgTHWv9k-ByxP0hC5sIQHyB5hJaD8_svMr4Aqz_vH9Z8HShgjK47NsMQKxGGgaXdnq3xEdwydM-hTG4Pi35o6Kt0bbJ5KTRQ2ObjmnVTG7J__QTKMTrK2S6Ro4VIMrYzaai7BTLa8MGNotj\"},{\"type\":\"function_call\",\"call_id\":\"call_hwPdXfzZmrdySXU2ZmrL51Ln\",\"name\":\"get_weather\",\"arguments\":\"{\\\"city\\\":{}}\"},{\"type\":\"function_call_output\",\"call_id\":\"call_hwPdXfzZmrdySXU2ZmrL51Ln\",\"output\":\"{\\\"temperature\\\":22,\\\"condition\\\":\\\"sunny\\\"}\"}],\"tools\":[{\"type\":\"function\",\"name\":\"get_weather\",\"description\":\"Get the current weather for a city.\",\"parameters\":{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"],\"additionalProperties\":false},\"strict\":false}],\"store\":false,\"prompt_cache_key\":\"session-recorded-opencode-loop\",\"include\":[\"reasoning.encrypted_content\"],\"reasoning\":{\"effort\":\"medium\",\"summary\":\"auto\"},\"max_output_tokens\":32000,\"stream\":true}" }, "response": { "status": 200, diff --git a/packages/opencode/test/session/llm-native.test.ts b/packages/opencode/test/session/llm-native.test.ts index 702bb67e390d..2bb15567af3a 100644 --- a/packages/opencode/test/session/llm-native.test.ts +++ b/packages/opencode/test/session/llm-native.test.ts @@ -115,10 +115,9 @@ const storedSession = { const openAIResponses = { user: (text: string) => ({ role: "user", content: [{ type: "input_text", text }] }), assistant: (text: string) => ({ role: "assistant", content: [{ type: "output_text", text }] }), - openaiReasoning: (text: string, options: { readonly itemId: string; readonly encryptedContent: string }) => ({ + openaiReasoning: (text: string, encryptedContent: string) => ({ type: "reasoning", - id: options.itemId, - encrypted_content: options.encryptedContent, + encrypted_content: encryptedContent, summary: [{ type: "summary_text", text }], }), } @@ -657,10 +656,7 @@ describe("session.llm-native.request", () => { expectedBody: { input: [ openAIResponses.user("What changed?"), - openAIResponses.openaiReasoning("Checked the previous diff.", { - itemId: "rs_1", - encryptedContent: "encrypted-state", - }), + openAIResponses.openaiReasoning("Checked the previous diff.", "encrypted-state"), openAIResponses.assistant("The parser changed."), openAIResponses.user("Summarize it."), ], @@ -683,7 +679,7 @@ describe("session.llm-native.request", () => { ], providerOptions: { openai: { store: false, include: ["reasoning.encrypted_content"] } }, expectedBody: { - input: [{ type: "reasoning", id: "rs_1", summary: [], encrypted_content: "encrypted-state" }], + input: [{ type: "reasoning", summary: [], encrypted_content: "encrypted-state" }], include: ["reasoning.encrypted_content"], store: false, },