Merge remote-tracking branch 'origin/master' into codex/basic-session-search

# Conflicts:
#	apps/web/tests/snapshots/question-composer/answered.expected.md
#	packages/client/connection/tests/fake-api.ts
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/tests/fake-api.ts
#	packages/client/ui-workspace/README.i18n.yaml
#	packages/client/ui-workspace/README.md
#	packages/client/ui-workspace/README.zh.md
#	packages/client/ui-workspace/src/client/WorkspaceBrowser.tsx
#	packages/client/ui-workspace/tests/apply.spec.ts
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
#	packages/host/apiproxy/src/api-proxy.ts
#	packages/host/apiproxy/src/api/index.ts
#	packages/host/apiproxy/src/api/sessions.schema.ts
#	packages/host/apiproxy/src/fetch/client.ts
#	packages/host/apiproxy/src/fetch/handler.ts
#	packages/host/apiproxy/tests/fetch-carrier.spec.ts
#	packages/host/apiproxy/tests/rpc-schemas.spec.ts
#	packages/session-query/session-query/tests/search-helpers.spec.ts
This commit is contained in:
Hypatia May
2026-07-28 09:27:28 +08:00
1288 changed files with 40508 additions and 19164 deletions
@@ -56,6 +56,36 @@ function fakeApi(overrides: Partial<{ muxFrames: MuxFrame[]; hostFrames: HostFra
result: { ok: false, error: { code: 'session-not-found', message: 'nope', details: { sessionId: request.payload.sessionId } } },
}
},
async models(request) {
return {
rpcId: request.rpcId,
result: {
ok: true,
value: {
current: { provider: 'deepseek', model: 'deepseek-v4-flash' },
groups: [],
failures: [],
},
},
}
},
async selectModel(request) {
return {
rpcId: request.rpcId,
result: {
ok: true,
value: {
selected: {
provider: request.payload.provider,
model: request.payload.model,
...request.payload.reasoningEffort === undefined
? {}
: { reasoningEffort: request.payload.reasoningEffort },
},
},
},
}
},
async prompt(request) {
return { rpcId: request.rpcId, result: { ok: true, value: { accepted: true as const } } }
},
@@ -67,6 +97,9 @@ function fakeApi(overrides: Partial<{ muxFrames: MuxFrame[]; hostFrames: HostFra
async describe(request) {
return { rpcId: request.rpcId, result: { ok: true, value: { version: 'v', cwd: '/w', attachedSessions: 0 } } }
},
async pickDirectory(request) {
return { rpcId: request.rpcId, result: { ok: true, value: { path: null } } }
},
},
workspace: {
async list(request) {
@@ -128,8 +161,8 @@ function fakeApi(overrides: Partial<{ muxFrames: MuxFrame[]; hostFrames: HostFra
}
}
function client(api: ApiProxy = fakeApi()): InProcessApiClient {
return new InProcessApiClient(toFetchHandler(api))
function client(api: ApiProxy = fakeApi(), timeoutMs?: number): InProcessApiClient {
return new InProcessApiClient(toFetchHandler(api), timeoutMs)
}
async function collect<F>(stream: AsyncIterable<RpcRequest<F>>): Promise<RpcRequest<F>[]> {
@@ -164,11 +197,38 @@ describe('unary round trip (handler ⇄ client, no network)', () => {
value: { items: [{ sessionId: 's1', snippet: 'fixture match' }], hasMore: false },
})
expect((await c.sessions.create({})).result.ok).toBe(true)
expect((await c.sessions.models({ sessionId: 's' as never })).result.ok).toBe(true)
const selected = await c.sessions.selectModel({
sessionId: 's' as never,
provider: 'deepseek',
model: 'deepseek-v4-flash',
reasoningEffort: 'max',
})
expect(selected.result).toMatchObject({
ok: true,
value: {
selected: {
provider: 'deepseek',
model: 'deepseek-v4-flash',
reasoningEffort: 'max',
},
},
})
expect((await c.sessions.prompt({ sessionId: 's' as never, mode: 'queue', content: [{ type: 'text', text: 'x' }] })).result.ok).toBe(true)
expect((await c.sessions.cancel({ sessionId: 's' as never })).result.ok).toBe(true)
expect((await c.host.describe({})).result.ok).toBe(true)
})
it('round-trips the native picker without the default unary timeout', async () => {
const api = fakeApi()
api.host.pickDirectory = async (request) => {
await new Promise(resolve => setTimeout(resolve, 15))
return { rpcId: request.rpcId, result: { ok: true, value: { path: '/tmp/project' } } }
}
const response = await client(api, 1).host.pickDirectory({})
expect(response.result).toEqual({ ok: true, value: { path: '/tmp/project' } })
})
it('round-trips command.list / command.execute / skill.list through the wire form', async () => {
const c = client()
const list = await c.commands.list({ sessionId: 's' as never })
@@ -217,6 +277,30 @@ describe('unary round trip (handler ⇄ client, no network)', () => {
expect(parsed.rpcId).toBe('r-search-sig')
expect(parsed.result.error?.code).toBe('cancelled')
})
it('propagates the carrier Request signal into host.pickDirectory', async () => {
const api = fakeApi()
api.host.pickDirectory = async (request, signal) => {
if (!signal.aborted) {
await new Promise<void>((resolve) => {
signal.addEventListener('abort', () => { resolve() }, { once: true })
})
}
return {
rpcId: request.rpcId,
result: { ok: false, error: { code: 'cancelled', message: 'aborted', details: {} } },
}
}
const handler = toFetchHandler(api)
const controller = new AbortController()
const body = JSON.stringify({ type: 'client-request', rpcId: 'r-picker', method: 'host.pickDirectory', payload: {} })
const pending = handler.fetch(new Request('http://x/api/host.pickDirectory', {
method: 'POST', body, signal: controller.signal,
}))
controller.abort()
const parsed = await (await pending).json() as { result: { error?: { code: string } } }
expect(parsed.result.error?.code).toBe('cancelled')
})
})
describe('handler carrier-layer statuses', () => {