Skip to content

Commit a7f1fca

Browse files
author
袁慎亨
committed
fix(core): preserve raw agent request overlays
1 parent 813377d commit a7f1fca

3 files changed

Lines changed: 57 additions & 15 deletions

File tree

‎packages/core/src/session/model-request.ts‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ export const layer = Layer.effect(
259259
messages: input.messages,
260260
options:
261261
kind === "primary" || kind === "compaction"
262-
? { maxTokens: outputLimit(model.limit, kind, input.inputTokens), ...input.agentRequest?.body }
263-
: { ...input.agentRequest?.body },
262+
? { maxTokens: outputLimit(model.limit, kind, input.inputTokens) }
263+
: {},
264264
},
265265
Object.fromEntries(Array.from(given, ([d, t]) => [t.name, d])),
266266
)
@@ -280,8 +280,24 @@ export const layer = Layer.effect(
280280
const base = LLM.request({
281281
model: model.model,
282282
http: {
283+
body: input.agentRequest?.body,
283284
headers: {
284-
...input.agentRequest?.headers,
285+
...Object.fromEntries(
286+
Object.entries(input.agentRequest?.headers ?? {}).filter(
287+
([name]) =>
288+
![
289+
"x-opencode-session-id",
290+
"x-opencode-parent-session-id",
291+
"x-session-affinity",
292+
"x-session-id",
293+
"x-parent-session-id",
294+
"user-agent",
295+
"x-opencode-project",
296+
"x-opencode-session",
297+
"x-opencode-client",
298+
].includes(name.toLowerCase()),
299+
),
300+
),
285301
"x-opencode-session-id": session.id,
286302
...(session.parentID ? { "x-opencode-parent-session-id": session.parentID } : {}),
287303
"x-session-affinity": affinity,

‎packages/core/test/session-model-request-hooks.test.ts‎

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { describe, expect } from "bun:test"
2+
import { HttpOptions, LLMRequest, mergeHttpOptions } from "@opencode/ai"
3+
import { Auth } from "@opencode/ai/route/auth"
4+
import { Endpoint } from "@opencode/ai/route/endpoint"
5+
import { jsonRequestParts } from "@opencode/ai/route/transport/http"
26
import { OpenAIChat } from "@opencode/ai/protocols"
37
import { Agent } from "@opencode/schema/agent"
48
import { Money } from "@opencode/schema/money"
@@ -40,12 +44,12 @@ const transport = SessionModelTransport.Service.of({
4044
})
4145

4246
describe("SessionModelRequest HTTP hooks", () => {
43-
it.effect("applies agent request options before hooks for every request kind", () =>
47+
it.effect("applies raw agent body overlays and protects runtime headers for every request kind", () =>
4448
Effect.gen(function* () {
4549
const hooks = yield* PluginHooks.Service
4650
yield* hooks.register("session", "context", (event) =>
4751
Effect.sync(() => {
48-
expect(event.options.temperature).toBe(0.7)
52+
expect(event.options.temperature).toBeUndefined()
4953
event.options.temperature = 0.2
5054
}),
5155
)
@@ -62,21 +66,41 @@ describe("SessionModelRequest HTTP hooks", () => {
6266
agent: Agent.ID.make("reviewer"),
6367
agentRequest: {
6468
settings: {},
65-
body: { temperature: 0.7, topP: 0.8, custom_option: "agent" },
66-
headers: { "x-agent": "reviewer", "x-opencode-session-id": "wrong" },
69+
body: { temperature: 0.7, top_p: 0.8, service_tier: "flex", maxTokens: 5, nested: { agent: true } },
70+
headers: { "x-agent": "reviewer", "X-OPENCODE-SESSION-ID": "wrong", "X-Session-Affinity": "wrong" },
6771
},
6872
model,
6973
system: [],
7074
messages: [],
7175
})
72-
expect(prepared.request.generation?.temperature).toBe(kind === "primary" ? 0.2 : 0.7)
73-
expect(prepared.request.generation?.topP).toBe(0.8)
74-
const body = yield* OpenAIChat.fromRequest(prepared.request)
75-
expect(body.temperature).toBe(kind === "primary" ? 0.2 : 0.7)
76-
expect(body.top_p).toBe(0.8)
77-
expect(prepared.request.providerOptions?.custom_option).toBe("agent")
76+
expect(prepared.request.generation?.temperature).toBe(kind === "primary" ? 0.2 : undefined)
77+
expect(prepared.request.generation?.maxTokens).toBe(
78+
kind === "primary" || kind === "compaction" ? 32_000 : undefined,
79+
)
80+
const request = LLMRequest.update(prepared.request, {
81+
http: mergeHttpOptions(
82+
new HttpOptions({ body: { temperature: 0.9, nested: { model: true } } }),
83+
prepared.request.http,
84+
),
85+
})
86+
const body = yield* OpenAIChat.fromRequest(request)
87+
const sent = yield* jsonRequestParts({
88+
request,
89+
body,
90+
endpoint: Endpoint.path("https://example.test/v1/chat/completions"),
91+
auth: Auth.bearer("test"),
92+
encodeBody: JSON.stringify,
93+
})
94+
const json = JSON.parse(sent.bodyText)
95+
expect(json.temperature).toBe(0.7)
96+
expect(json.top_p).toBe(0.8)
97+
expect(json.service_tier).toBe("flex")
98+
expect(json.nested).toEqual({ model: true, agent: true })
7899
expect(prepared.request.http?.headers?.["x-agent"]).toBe("hook")
79100
expect(prepared.request.http?.headers?.["x-opencode-session-id"]).toBe(session.id)
101+
expect(prepared.request.http?.headers?.["X-OPENCODE-SESSION-ID"]).toBeUndefined()
102+
expect(prepared.request.http?.headers?.["X-Session-Affinity"]).toBeUndefined()
103+
80104
}
81105
}).pipe(Effect.provideService(SessionModelTransport.Service, transport)),
82106
)

‎services/www/src/docs/content/agents.mdx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,10 @@ color: "#ff6b6b"
335335
```
336336

337337
<Callout type="warning">
338-
Agent request values override model and variant defaults. Session hooks may override the agent body options, and
339-
`model.request` hooks may override headers. OpenCode-owned session headers retain their runtime values.
338+
Agent `body` is a raw JSON overlay, applied after provider, model, and variant body defaults and after protocol
339+
encoding. Use provider field names such as `temperature` and `top_p`. Model-request hooks may override headers;
340+
OpenCode-owned session headers are protected from agent overrides regardless of casing. Agent `request.settings`
341+
is not yet applied.
340342
</Callout>
341343

342344
Do not use legacy top-level fields such as `temperature`, `top_p`, `prompt`, `permission`, `tools`, `disable`, or `maxSteps` in new V2 agent configuration.

0 commit comments

Comments
 (0)