mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-09 19:29:41 +00:00
fix(opencode): expose model-specific reasoning effort options (#1716)
* fix(cli): discover opencode thought_level via set_config_option on model switch * fix(cli): apply and refresh opencode model switches so thought_level stays discoverable * fix(web): track opencode effort options across model switches * feat(cli,hub,web): dynamic opencode effort options in new-session form * test(cli): avoid platform-specific process event narrowing * fix(opencode): address variant discovery review findings * fix(opencode): synchronize effort options with model targets * fix(opencode): roll back rejected model targets * fix(cli): guard opencode variant probe workspace paths * fix(web): clear stale opencode effort on model switch * fix(cli): clear stale opencode effort metadata * fix(web): reset stale effort options on model switch * fix(web): reset opencode effort poll budget * test(web): enforce opencode effort poll budget
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { existsSync, mkdtempSync, rmSync, mkdirSync, realpathSync, writeFileSync } from 'node:fs'
|
||||
import { existsSync, mkdtempSync, rmSync, mkdirSync, realpathSync, symlinkSync, writeFileSync } from 'node:fs'
|
||||
import { homedir, tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
|
||||
const ioMock = vi.hoisted(() => vi.fn())
|
||||
const listOpencodeModelsForCwdMock = vi.hoisted(() => vi.fn())
|
||||
const listOpencodeModelVariantsMock = vi.hoisted(() => vi.fn())
|
||||
const listGrokModelsForCwdMock = vi.hoisted(() => vi.fn())
|
||||
const listCopilotModelsForCwdMock = vi.hoisted(() => vi.fn())
|
||||
const inspectCursorChatStoreMock = vi.hoisted(() => vi.fn())
|
||||
@@ -21,6 +22,10 @@ vi.mock('../modules/common/opencodeModels', () => ({
|
||||
listOpencodeModelsForCwd: listOpencodeModelsForCwdMock
|
||||
}))
|
||||
|
||||
vi.mock('../modules/common/opencodeModelVariants', () => ({
|
||||
listOpencodeModelVariants: listOpencodeModelVariantsMock
|
||||
}))
|
||||
|
||||
vi.mock('../modules/common/grokModels', () => ({
|
||||
listGrokModelsForCwd: listGrokModelsForCwdMock
|
||||
}))
|
||||
@@ -75,6 +80,15 @@ async function callListOpencodeModels(client: ApiMachineClient, machineId: strin
|
||||
return JSON.parse(raw) as unknown
|
||||
}
|
||||
|
||||
async function callListOpencodeModelVariants(client: ApiMachineClient, machineId: string, cwd: string): Promise<unknown> {
|
||||
const manager = (client as unknown as { rpcHandlerManager: { handleRequest: (req: { method: string; params: string }) => Promise<string> } }).rpcHandlerManager
|
||||
const raw = await manager.handleRequest({
|
||||
method: `${machineId}:listOpencodeModelVariants`,
|
||||
params: JSON.stringify({ cwd })
|
||||
})
|
||||
return JSON.parse(raw) as unknown
|
||||
}
|
||||
|
||||
async function callListGrokModels(client: ApiMachineClient, machineId: string, cwd: string): Promise<unknown> {
|
||||
const manager = (client as unknown as { rpcHandlerManager: { handleRequest: (req: { method: string; params: string }) => Promise<string> } }).rpcHandlerManager
|
||||
const raw = await manager.handleRequest({
|
||||
@@ -311,6 +325,56 @@ describe('ApiMachineClient listOpencodeModelsForCwd handler', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('ApiMachineClient listOpencodeModelVariants handler', () => {
|
||||
let workspaceRoot: string
|
||||
|
||||
beforeEach(() => {
|
||||
ioMock.mockReset()
|
||||
listOpencodeModelVariantsMock.mockReset()
|
||||
workspaceRoot = mkdtempSync(join(tmpdir(), 'hapi-opencode-variants-ws-'))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(workspaceRoot, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('rejects cwd outside workspace roots before running the variant probe', async () => {
|
||||
const machine = makeMachine('opencode-variants-machine-1')
|
||||
const client = new ApiMachineClient('cli-token', machine, [workspaceRoot])
|
||||
const outsideCwd = mkdtempSync(join(tmpdir(), 'hapi-opencode-variants-outside-'))
|
||||
|
||||
try {
|
||||
expect(await callListOpencodeModelVariants(client, machine.id, outsideCwd)).toEqual({
|
||||
success: false,
|
||||
error: 'Path is outside workspace roots'
|
||||
})
|
||||
expect(listOpencodeModelVariantsMock).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
rmSync(outsideCwd, { recursive: true, force: true })
|
||||
client.shutdown()
|
||||
}
|
||||
})
|
||||
|
||||
it('rejects a workspace symlink that escapes the configured roots', async () => {
|
||||
const machine = makeMachine('opencode-variants-machine-2')
|
||||
const client = new ApiMachineClient('cli-token', machine, [workspaceRoot])
|
||||
const outsideCwd = mkdtempSync(join(tmpdir(), 'hapi-opencode-variants-symlink-outside-'))
|
||||
const escape = join(workspaceRoot, 'escape')
|
||||
symlinkSync(outsideCwd, escape, 'dir')
|
||||
|
||||
try {
|
||||
expect(await callListOpencodeModelVariants(client, machine.id, escape)).toEqual({
|
||||
success: false,
|
||||
error: 'Path is outside workspace roots'
|
||||
})
|
||||
expect(listOpencodeModelVariantsMock).not.toHaveBeenCalled()
|
||||
} finally {
|
||||
rmSync(outsideCwd, { recursive: true, force: true })
|
||||
client.shutdown()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('ApiMachineClient listCopilotModelsForCwd handler', () => {
|
||||
let workspaceRoot: string
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ import {
|
||||
type ListOpencodeModelsForCwdRequest,
|
||||
type ListOpencodeModelsForCwdResponse
|
||||
} from '../modules/common/opencodeModels'
|
||||
import {
|
||||
listOpencodeModelVariants,
|
||||
type ListOpencodeModelVariantsResponse
|
||||
} from '../modules/common/opencodeModelVariants'
|
||||
import {
|
||||
listGrokModelsForCwd,
|
||||
type ListGrokModelsForCwdRequest,
|
||||
@@ -256,6 +260,23 @@ export class ApiMachineClient {
|
||||
}
|
||||
)
|
||||
|
||||
this.rpcHandlerManager.registerHandler<{ cwd?: string | null }, ListOpencodeModelVariantsResponse>(
|
||||
RPC_METHODS.ListOpencodeModelVariants,
|
||||
async (params) => {
|
||||
const rawCwd = typeof params?.cwd === 'string' ? params.cwd.trim() : ''
|
||||
if (!rawCwd) {
|
||||
return { success: false, error: 'cwd is required' }
|
||||
}
|
||||
|
||||
const resolvedCwd = await this.pathPolicy.resolveForCheck(rawCwd)
|
||||
if (!this.pathPolicy.isWithinSpawnRoots(resolvedCwd)) {
|
||||
return { success: false, error: 'Path is outside workspace roots' }
|
||||
}
|
||||
|
||||
return await listOpencodeModelVariants({ cwd: resolvedCwd })
|
||||
}
|
||||
)
|
||||
|
||||
this.rpcHandlerManager.registerHandler<ListGrokModelsForCwdRequest, ListGrokModelsForCwdResponse>(
|
||||
RPC_METHODS.ListGrokModelsForCwd,
|
||||
async (params) => {
|
||||
|
||||
Reference in New Issue
Block a user