Merge remote-tracking branch 'origin/master' into worktree/custom-deepseek-models

# Conflicts:
#	apps/web/tests/snapshots/message-actions/ui.expected.md
#	apps/web/tests/snapshots/seeded-history/command-row.expected.md
#	apps/web/tests/snapshots/seeded-history/ui.expected.md
#	packages/client/ui-model/README.i18n.yaml
#	packages/client/ui-model/README.md
#	packages/client/ui-model/README.zh.md
#	packages/client/ui-model/tests/model-select.spec.tsx
#	packages/host/apiproxy/README.i18n.yaml
This commit is contained in:
Yichen Jiang
2026-08-03 16:20:55 +08:00
1371 changed files with 125172 additions and 10229 deletions
@@ -93,7 +93,13 @@ async function bench() {
ctx.provide('conversation', {})
ctx.provide('locale', new LocaleService(ctx))
const scopes = new Map<SessionId, Context>()
ctx.provide('sessions', { scope: (id: SessionId) => scopes.get(id) })
const addressed = new Set<SessionId>()
ctx.provide('sessions', {
scope: (id: SessionId) => scopes.get(id),
subagentAddress: (id: SessionId) => addressed.has(id)
? { parentSessionId: sid('parent'), childSessionId: id, mode: 'continuable' as const }
: undefined,
})
const fiber = ctx.plugin({ inject: [...inject], apply })
await fiber.await()
await ctx.plugin(function probe() {}).await()
@@ -108,6 +114,7 @@ async function bench() {
seat: () => seats.get('conversation.input.model')!,
hostCurrent: () => current,
setHostCurrent: (target: ModelTarget) => { current = target },
address: (id: SessionId) => { addressed.add(id) },
}
}
@@ -214,4 +221,30 @@ describe('ui-model dual entry', () => {
const b = await bench()
expect(() => b.seat().inject!(sid('ghost'))).toThrow(/resolved no scope/)
})
it('withholds both model entries from addressed subagent sessions without Agent-bound RPCs', async () => {
const b = await bench()
b.mint('child')
b.address(sid('child'))
expect(b.contribution().available(projection('child'))).toBe(false)
await expect(b.contribution().ui.options(
projection('child'),
new AbortController().signal,
)).rejects.toThrow(/unavailable for addressed subagent/)
const face = b.seat().inject!(sid('child'))
expect(face.available).toBe(false)
face.load()
await expect(face.select({ provider: 'deepseek', model: 'deepseek-v4-pro' })).resolves.toBe(false)
await expect(b.ctx.models.directoryFor(sid('child')).load())
.rejects.toThrow(/unavailable for addressed subagent/)
await expect(b.ctx.models.directoryFor(sid('child')).select({
provider: 'deepseek',
model: 'deepseek-v4-pro',
})).rejects.toThrow(/unavailable for addressed subagent/)
b.ctx.emit('connection/reset')
await Promise.resolve()
expect(b.calls).toEqual({ models: 0, select: 0 })
})
})
@@ -55,6 +55,7 @@ describe('ModelSelect reasoning effort', () => {
})
render(<ModelSelect
locked={false}
available
directory={directory}
load={vi.fn()}
select={select}
@@ -95,6 +96,7 @@ describe('ModelSelect reasoning effort', () => {
}))
render(<ModelSelect
locked={false}
available
directory={directory}
load={vi.fn()}
select={vi.fn().mockResolvedValue(true)}
@@ -116,6 +118,7 @@ describe('ModelSelect reasoning effort', () => {
const select = vi.fn().mockResolvedValue(true)
render(<ModelSelect
locked={false}
available
directory={directory}
load={vi.fn()}
select={select}
@@ -130,4 +133,19 @@ describe('ModelSelect reasoning effort', () => {
expect(screen.queryByText('removed-model')).toBeNull()
expect(screen.getByRole('menuitemradio', { name: 'DeepSeek-V4-Flash' })).toBeTruthy()
})
it('renders no Agent-bound control for an addressed subagent session', () => {
const load = vi.fn()
render(<ModelSelect
locked={false}
available={false}
directory={createSnapshotStore(state())}
load={load}
select={vi.fn().mockResolvedValue(false)}
t={t}
/>)
expect(screen.queryByRole('button')).toBeNull()
expect(load).not.toHaveBeenCalled()
})
})