fix(web): expose Codex Fast and Plan on Create Session (#1017)

* fix(web): expose Codex Fast and Plan on Create Session

Wire serviceTier and collaborationMode through spawn so Create can set
the same Codex options chat Settings already supports (#1015).

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): forward collaborationMode through machine spawn RPC

Create Session Plan was accepted by the hub but dropped in apiMachine
before buildCliArgs; also preserve collaborationMode on resume spawn.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cli): correct stopSession mock type in spawn RPC test

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): keep Fast mode across Create draft restore while models load

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(web): preserve pending Fast selection

* fix: apply Fast and Plan to imported Codex sessions

* test: narrow imported Codex session id

* fix: forward explicit Standard service tier

* fix: integrate create-session controls with current main

* test: close Codex RPC suite

* fix: preserve existing session spawn field

* fix(web): integrate Codex controls with current New Session form

* fix(web): reconcile draft types and submit state

* fix(hub): integrate spawn arguments with current resume flow

* test(cli): isolate spawn RPC suite

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
SSU-WEI HUANG
2026-07-28 12:13:29 +08:00
committed by GitHub
co-authored by Cursor
parent 5396d5f097
commit 07db10f86d
25 changed files with 395 additions and 17 deletions
+60
View File
@@ -421,6 +421,66 @@ describe('ApiMachineClient Codex transcript handlers', () => {
client.shutdown()
}
})
})
describe('ApiMachineClient SpawnHappySession handler', () => {
let workspaceRoot: string
beforeEach(() => {
ioMock.mockReset()
workspaceRoot = mkdtempSync(join(tmpdir(), 'hapi-machine-spawn-'))
})
afterEach(() => {
rmSync(workspaceRoot, { recursive: true, force: true })
})
async function callSpawnHappySession(
client: ApiMachineClient,
machineId: string,
params: Record<string, unknown>
): 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}:spawn-happy-session`,
params: JSON.stringify(params)
})
return JSON.parse(raw) as unknown
}
it('forwards collaborationMode and serviceTier to spawnSession', async () => {
const machine = makeMachine('machine-spawn-1')
const client = new ApiMachineClient('cli-token', machine, [workspaceRoot])
const spawnSession = vi.fn(async () => ({ type: 'success' as const, sessionId: 'session-1' }))
client.setRPCHandlers({
spawnSession,
stopSession: vi.fn(() => true),
requestShutdown: vi.fn()
})
try {
const result = await callSpawnHappySession(client, machine.id, {
directory: workspaceRoot,
agent: 'codex',
serviceTier: 'fast',
collaborationMode: 'plan'
})
expect(result).toEqual({ type: 'success', sessionId: 'session-1' })
expect(spawnSession).toHaveBeenCalledWith(expect.objectContaining({
directory: workspaceRoot,
agent: 'codex',
serviceTier: 'fast',
collaborationMode: 'plan'
}))
} finally {
client.shutdown()
}
})
})
describe('ApiMachineClient keepAlive lifecycle', () => {
+2 -1
View File
@@ -357,7 +357,7 @@ export class ApiMachineClient {
setRPCHandlers({ spawnSession, stopSession, requestShutdown }: MachineRpcHandlers): void {
this.rpcHandlerManager.registerHandler(RPC_METHODS.SpawnHappySession, async (params: any) => {
const { directory, sessionId, existingSessionId, resumeSessionId, machineId, approvedNewDirectoryCreation, agent, model, effort, modelReasoningEffort, yolo, permissionMode, serviceTier, token, sessionType, worktreeName } = params || {}
const { directory, sessionId, existingSessionId, resumeSessionId, machineId, approvedNewDirectoryCreation, agent, model, effort, modelReasoningEffort, yolo, permissionMode, serviceTier, collaborationMode, token, sessionType, worktreeName } = params || {}
if (!directory) {
throw new Error('Directory is required')
@@ -382,6 +382,7 @@ export class ApiMachineClient {
yolo,
permissionMode,
serviceTier,
collaborationMode,
token,
sessionType,
worktreeName