@@ -80,25 +80,11 @@ const INTERNAL_PROMPT_CACHE_KEY_HEADER = "x-opencode-kimi-prompt-cache-key"
8080const INTERNAL_REASONING_EFFORT_HEADER = "x-opencode-kimi-reasoning-effort"
8181const INTERNAL_THINKING_TYPE_HEADER = "x-opencode-kimi-thinking-type"
8282
83- // Minimal shape for the chat hook input/output we care about. Mirrors the
84- // runtime shape opencode actually passes (see
85- // research/opencode/packages/opencode/src/session/llm.ts::stream — `model`
86- // has `.providerID`, `.id`, `.options`, `.variants`; the selected variant
87- // rides on `message.model.variant`).
88- type HookInput = {
89- agent : string
90- provider : { id : string }
91- model : {
92- providerID : string
93- id : string
94- options ?: Record < string , unknown >
95- variants ?: Record < string , Record < string , unknown > >
96- }
97- message : { model : { variant ?: string } }
98- sessionID : string
99- }
100- type ParamsOutput = { options : Record < string , unknown > }
101- type HeadersOutput = { headers : Record < string , string > }
83+ type Hooks = Awaited < ReturnType < typeof plugin > >
84+ type ChatParamsHook = NonNullable < Hooks [ "chat.params" ] >
85+ type ChatHeadersHook = NonNullable < Hooks [ "chat.headers" ] >
86+ type ParamsOutput = Parameters < ChatParamsHook > [ 1 ]
87+ type HeadersOutput = Parameters < ChatHeadersHook > [ 1 ]
10288
10389type HookInputOptions = {
10490 providerID ?: string
@@ -109,7 +95,7 @@ type HookInputOptions = {
10995 variant ?: string
11096}
11197
112- function makeHookInput ( options : HookInputOptions = { } ) : HookInput {
98+ function makeHookInput ( options : HookInputOptions = { } ) {
11399 const providerID = options . providerID ?? PROVIDER_ID
114100 return {
115101 agent : "test-agent" ,
@@ -129,36 +115,44 @@ function makeHookInput(options: HookInputOptions = {}): HookInput {
129115 }
130116}
131117
132- function callParams (
133- hook : ( i : HookInput , o : ParamsOutput ) => Promise < void > | void ,
118+ async function callParams (
119+ hook : ChatParamsHook ,
134120 input : HookInputOptions = { } ,
135121 options : Record < string , unknown > = { } ,
136122) {
137- const output : ParamsOutput = { options : { ...options } }
138- const res = hook ( makeHookInput ( input ) , output )
139- return { res, output }
123+ const output : ParamsOutput = {
124+ temperature : 0 ,
125+ topP : 1 ,
126+ topK : 0 ,
127+ maxOutputTokens : undefined ,
128+ options : { ...options } ,
129+ }
130+ await hook ( makeHookInput ( input ) as any , output )
131+ return { output }
140132}
141133
142- function callHeaders ( hook : ( i : HookInput , o : HeadersOutput ) => Promise < void > | void , input : HookInputOptions = { } ) {
134+ async function callHeaders ( hook : ChatHeadersHook , input : HookInputOptions = { } ) {
143135 const output : HeadersOutput = { headers : { } }
144- const res = hook ( makeHookInput ( input ) , output )
145- return { res , output }
136+ await hook ( makeHookInput ( input ) as any , output )
137+ return { output }
146138}
147139
148140test ( "chat.params: no-op for other providers (AGENTS.md rule: gated on PROVIDER_ID)" , async ( ) => {
149141 const { hooks } = await getHooks ( )
150142 const hook = hooks [ "chat.params" ] !
151- const { output } = callParams ( hook , { providerID : "some-other-provider" , modelOptions : { reasoning_effort : "high" } } , {
152- reasoning_effort : "high" ,
153- } )
143+ const { output } = await callParams (
144+ hook ,
145+ { providerID : "some-other-provider" , modelOptions : { reasoning_effort : "high" } } ,
146+ { reasoning_effort : "high" } ,
147+ )
154148 // Untouched — no prompt_cache_key, no thinking added.
155149 expect ( output . options ) . toEqual ( { reasoning_effort : "high" } )
156150} )
157151
158152test ( "chat.params: no-op for other models under our provider (rule 5 gating)" , async ( ) => {
159153 const { hooks } = await getHooks ( )
160154 const hook = hooks [ "chat.params" ] !
161- const { output } = callParams ( hook , { modelID : "kimi-something-else" } )
155+ const { output } = await callParams ( hook , { modelID : "kimi-something-else" } )
162156 expect ( output . options . prompt_cache_key ) . toBeUndefined ( )
163157 expect ( output . options . thinking ) . toBeUndefined ( )
164158} )
@@ -320,6 +314,7 @@ test("provider.models: fills limit.context from discovery when config still has
320314 const { hooks, writes } = await getHooks ( )
321315 const provider = makeProviderState ( )
322316 const next = await hooks . provider ! . models ! ( provider as any , { auth : validAuth ( ) } as any )
317+ expect ( mock . calls [ 0 ] ! . hasSignal ) . toBe ( true )
323318 expect ( next [ MODEL_ID ] ! . limit ?. context ) . toBe ( 262144 )
324319 expect ( next [ "some-other-model" ] ! . limit ?. context ) . toBe ( 1234 )
325320 expect ( provider . models [ MODEL_ID ] ! . limit ?. context ) . toBe ( 0 )
0 commit comments