feat: slash system / input service / agent scope

This commit is contained in:
imccyu
2026-07-27 03:17:52 +08:00
parent f3a4833dbf
commit a27be43ac1
210 changed files with 15969 additions and 2213 deletions

View File

@@ -18,7 +18,7 @@ import type { AppFrameProps } from '@deepseek-ai/dsh-client-ui-layout/src/client
import { SIDEBAR_COLLAPSED } from '@deepseek-ai/dsh-client-ui-layout/src/client/columns.ts'
import { createLayoutStore } from '@deepseek-ai/dsh-client-ui-layout/src/client/stores.ts'
import type {
SessionId, SessionListState, WorkspaceId, WorkspaceListState,
SessionId, SessionListState, WorkspaceListState,
} from '@deepseek-ai/dsh-client-runtime/client'
// Session-mode switch for the SessionProvider stub prop.
@@ -61,24 +61,21 @@ function mountFrame() {
if (key === 'sidebar') return <div data-testid="sidebar-content" />
if (key === 'conversation') return <div data-testid="center-content" />
if (key === 'details') return <div data-testid="details-content" />
return <div data-testid="empty-content" />
if (key === 'conversation.empty') return <div data-testid="empty-content" />
return <div data-testid="other-content" />
}) as AppFrameProps['renderSlot']
const sessionId = 's-test' as SessionId
const workspaceId = 'w-test' as WorkspaceId
const sessionState = {
ids: sessionMode.current ? [sessionId] : [],
byId: sessionMode.current
? { [sessionId]: { id: sessionId, displayTitle: 'Test', running: false, updatedAt: 1 } }
? { [sessionId]: { id: sessionId, displayTitle: 'Test', running: false, blank: false, updatedAt: 1 } }
: {},
current: sessionMode.current ? sessionId : undefined,
phase: 'ready',
intent: sessionMode.current
? undefined
: { sessionId: 'intent' as SessionId, target: { kind: 'workspace', workspaceId }, prompt: '', phase: 'connecting' },
} as SessionListState
const useSessions = ((sel: (s: SessionListState) => unknown) => sel(sessionState)) as never
const workspaceState: WorkspaceListState = {
items: [], intent: undefined, state: 'idle', phase: 'ready', error: null,
items: [], state: 'idle', phase: 'ready', error: null,
baselinesReady: baselinesReady.current, recentWorkspaceId: undefined,
}
const utils = render(
@@ -154,14 +151,15 @@ describe('AppFrame', () => {
expect(slotCalls.find((c) => c.key === 'details')!.props).toEqual({})
})
it('keeps a connecting page-local Session intent in conversation.empty', () => {
it('renders the New Session view state through the empty seat while no session is current', () => {
// No current session = the pure view state: the conversation.empty slot
// renders in the center column; no session slot dispatches.
sessionMode.current = false
const { slotCalls, getByTestId, queryByTestId } = mountFrame()
expect(getByTestId('empty-content')).toBeTruthy()
expect(queryByTestId('center-content')).toBeNull()
expect(slotCalls.map((c) => c.key)).toContain('conversation.empty')
expect(slotCalls.map((c) => c.key)).not.toContain('conversation')
expect(slotCalls.find((c) => c.key === 'conversation.empty')!.props).toEqual({})
})
it('keeps the loading branch until both object-layer baselines are ready', () => {
@@ -169,7 +167,6 @@ describe('AppFrame', () => {
const { slotCalls, getByRole } = mountFrame()
expect(getByRole('status').textContent).toContain('Loading workspaces and sessions')
expect(slotCalls.map((c) => c.key)).not.toContain('conversation')
expect(slotCalls.map((c) => c.key)).not.toContain('conversation.empty')
})
it('sidebar slot receives live concession output as owner props', () => {

View File

@@ -1,6 +1,6 @@
// @vitest-environment jsdom
// Client apply wiring under the terminal register form: ctx.layout provided,
// ONE register() call declares the four child slots + seats the store factory
// ONE register() call declares the three child slots + seats the store factory
// + wires the panel actions through the inject hook; teardown cascades
// (service unprovided + declarations gone + registration cleared). Node half
// and the invariant companion ride along — one-line surfaces the aggregate
@@ -31,18 +31,17 @@ describe('ui-layout client apply', () => {
expect(inject).toEqual(['slots', 'theme'])
})
it('provides ctx.layout and registers AppFrame into root with the four child declarations', async () => {
it('provides ctx.layout and registers AppFrame into root with the three child declarations', async () => {
const { ctx, slots } = await bench()
const fiber = ctx.plugin({ inject: [...inject], apply })
await fiber.await()
expect(ctx.get('layout')).toBeInstanceOf(LayoutService)
// The one register() call occupied 'root'…
expect(slots.entries('root')).toHaveLength(1)
// …and declared the four children in the ledger.
// …and declared the three children in the ledger.
expect(slots.spec('sidebar')).toEqual({ kind: 'single', scope: 'root' })
expect(slots.spec('conversation')).toEqual({ kind: 'single', scope: 'session' })
expect(slots.spec('details')).toEqual({ kind: 'single', scope: 'session' })
expect(slots.spec('conversation.empty')).toEqual({ kind: 'single', scope: 'root' })
})
it('injects no business face and attaches the layout actions', async () => {
@@ -84,7 +83,6 @@ describe('ui-layout client apply', () => {
expect(ctx.get('layout')).toBeUndefined()
expect(slots.entries('root')).toHaveLength(0)
expect(slots.spec('sidebar')).toBeUndefined()
expect(slots.spec('conversation.empty')).toBeUndefined()
// The built-in root declaration survives entry teardown (runtime-owned).
expect(slots.spec('root')).toEqual({ kind: 'single', scope: 'root' })
})