What version of Effect is running?
effect 3.22.0, @effect/ai 0.37.0, @effect/ai-openai 0.41.0
What steps can reproduce the bug?
Prompt.fromResponseParts drops response-part metadata instead of copying it into the reconstructed prompt part's options. This is the same root cause as #5954, and this report adds the OpenAI reasoning leg, which is not yet covered: #5954's body covers tool-call/tool-result (Gemini thoughtSignature), and its follow-up comment covers Bedrock/Anthropic reasoning signature. OpenAI carries its multi-turn reasoning continuity in metadata.openai.encryptedContent, and it is lost the same way.
The round trip is broken in the middle:
OpenAiLanguageModel emits the ciphertext on the streaming path — reasoning-start / reasoning-end parts carry metadata: { openai: { itemId, encryptedContent } } (packages/ai/openai/src/OpenAiLanguageModel.ts, the response.output_item.added / output_item.done handlers).
Prompt.fromResponseParts (packages/ai/ai/src/Prompt.ts) rebuilds every part with no options field at all, so the metadata is discarded.
OpenAiLanguageModel reads it back from options when encoding the next request — const options = part.options.openai → encrypted_content: options.encryptedContent. With options empty, the reasoning item is serialized without its ciphertext.
Minimal shape of the repro (any reasoning model, store: false):
import { LanguageModel, Prompt } from "@effect/ai"
const parts = yield* LanguageModel.streamText({
prompt,
toolkit,
disableToolCallResolution: true
}).pipe(Stream.runCollect, Effect.map(Chunk.toReadonlyArray))
// Present here:
parts.some((p) => (p as any).metadata?.openai?.encryptedContent) // => true
const next = Prompt.merge(prompt, Prompt.fromResponseParts(parts))
// Gone here:
next.content.some((m) =>
Array.isArray(m.content) &&
m.content.some((p: any) => p.options?.openai?.encryptedContent)
) // => false
What is the expected behavior?
Prompt.fromResponseParts should carry each response part's provider metadata into the reconstructed prompt part's options, so the next turn replays what the provider requires — for reasoning as well as text, tool-call and tool-result, and accumulating metadata across the streaming *-start / *-delta / *-end triples (the ciphertext can arrive on a delta with empty text).
Open draft PR #6442 already implements exactly this scope and would fix this report as well as #5954 and #6010. It has had no maintainer review since July.
What do you see instead?
options is empty on every reconstructed part, so the reasoning ciphertext is never replayed.
The OpenAI failure mode is silent, which is why it is worth reporting separately. Google returns 400 Function call is missing a thought_signature and Bedrock returns a messages.N.content.0.type error, so those users find out immediately. OpenAI does not error: with store: false there is no server-side reasoning item to fail to resolve, and an absent reference is not a dangling one, so the request is simply accepted without the reasoning context.
Measured with an A/B where the only variable was the fold — the same captured response parts reconstructed via appendResponse-style metadata-preserving conversion vs. Prompt.fromResponseParts — across four reasoning models (gemini-3-flash-preview, gemini-3.6-flash, and two GPT-5.6 tiers), two full repetitions, 16 legs total:
- metadata-preserving fold: continuity keys present in the prompt in 8/8 legs
Prompt.fromResponseParts: continuity keys absent in 8/8 legs
- round 2 accepted in 8/8 legs either way — no 400, no 404, on either provider
So on OpenAI this bug does not surface as a failure at all. It degrades multi-turn reasoning quality with no error, no warning and nothing in the response to key an assertion on, which makes it invisible to anyone not explicitly inspecting the reconstructed prompt.
Additional information
Related: #5954 (root cause, tool-call/tool-result + a comment covering Bedrock/Anthropic reasoning), #6010 (the Gemini thoughtSignature case), #6442 (open draft PR implementing the fix).
What version of Effect is running?
effect 3.22.0, @effect/ai 0.37.0, @effect/ai-openai 0.41.0
What steps can reproduce the bug?
Prompt.fromResponsePartsdrops response-partmetadatainstead of copying it into the reconstructed prompt part'soptions. This is the same root cause as #5954, and this report adds the OpenAI reasoning leg, which is not yet covered: #5954's body covers tool-call/tool-result (GeminithoughtSignature), and its follow-up comment covers Bedrock/Anthropic reasoningsignature. OpenAI carries its multi-turn reasoning continuity inmetadata.openai.encryptedContent, and it is lost the same way.The round trip is broken in the middle:
OpenAiLanguageModelemits the ciphertext on the streaming path —reasoning-start/reasoning-endparts carrymetadata: { openai: { itemId, encryptedContent } }(packages/ai/openai/src/OpenAiLanguageModel.ts, theresponse.output_item.added/output_item.donehandlers).Prompt.fromResponseParts(packages/ai/ai/src/Prompt.ts) rebuilds every part with nooptionsfield at all, so themetadatais discarded.OpenAiLanguageModelreads it back fromoptionswhen encoding the next request —const options = part.options.openai→encrypted_content: options.encryptedContent. Withoptionsempty, the reasoning item is serialized without its ciphertext.Minimal shape of the repro (any reasoning model,
store: false):What is the expected behavior?
Prompt.fromResponsePartsshould carry each response part's providermetadatainto the reconstructed prompt part'soptions, so the next turn replays what the provider requires — forreasoningas well astext,tool-callandtool-result, and accumulating metadata across the streaming*-start/*-delta/*-endtriples (the ciphertext can arrive on a delta with empty text).Open draft PR #6442 already implements exactly this scope and would fix this report as well as #5954 and #6010. It has had no maintainer review since July.
What do you see instead?
optionsis empty on every reconstructed part, so the reasoning ciphertext is never replayed.The OpenAI failure mode is silent, which is why it is worth reporting separately. Google returns
400 Function call is missing a thought_signatureand Bedrock returns amessages.N.content.0.typeerror, so those users find out immediately. OpenAI does not error: withstore: falsethere is no server-side reasoning item to fail to resolve, and an absent reference is not a dangling one, so the request is simply accepted without the reasoning context.Measured with an A/B where the only variable was the fold — the same captured response parts reconstructed via
appendResponse-style metadata-preserving conversion vs.Prompt.fromResponseParts— across four reasoning models (gemini-3-flash-preview,gemini-3.6-flash, and two GPT-5.6 tiers), two full repetitions, 16 legs total:Prompt.fromResponseParts: continuity keys absent in 8/8 legsSo on OpenAI this bug does not surface as a failure at all. It degrades multi-turn reasoning quality with no error, no warning and nothing in the response to key an assertion on, which makes it invisible to anyone not explicitly inspecting the reconstructed prompt.
Additional information
Related: #5954 (root cause, tool-call/tool-result + a comment covering Bedrock/Anthropic reasoning), #6010 (the Gemini
thoughtSignaturecase), #6442 (open draft PR implementing the fix).