feat(web): wire startup Workspace selection and sync docs
- Mount WorkspacesService.startInitialSelection in the runtime apply (the one-shot baseline follower shipped in 98633b5aa without a caller): a restored current session wins, an explicit clear stays cleared, a failed connect retries on the next baseline projection. - Cover the policy in client-apply and the assembled workspace-flow snapshot; startup now lands in the recent Workspace's blank session, so the draft-carry scenario starts from the hero directly. - Bring docs along: startup-selection paragraphs in the session-scope RFC note (both languages), bilingual README pairs for the four new client packages, doc-graph regeneration with client-declared events exempt from the dispatcher requirement (client dispatch sites are structurally invisible to the host-side ts.Program), and pairing re-records.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
README.md: 7776a5c2cf1d0990c9c339c6e5fc66401f935810
|
||||
README.zh.md: 8a0b7394c07878b8de958eae43d11203c92b5827
|
||||
README.md: 4724ebc75d441252245a0e811a4ae34f8b529a98
|
||||
README.zh.md: 6a0076742efccaf946910c77c77a9b74194b9dc5
|
||||
|
||||
@@ -115,6 +115,10 @@ export function apply(ctx: Context): void {
|
||||
const connection = ctx.get('connection') as ConnectionHandle
|
||||
const sessions = new SessionsService(ctx, connection.api)
|
||||
const workspaces = new WorkspacesService(ctx, connection.api, sessions)
|
||||
ctx.effect(
|
||||
() => workspaces.startInitialSelection(),
|
||||
'runtime: initial Workspace selection',
|
||||
)
|
||||
const loop = connection.start({
|
||||
onMuxEnvelope: (envelope) => { sessions.handleMuxEnvelope(envelope) },
|
||||
onHostEnvelope: (envelope) => {
|
||||
|
||||
@@ -8,7 +8,9 @@ import { describe, expect, it } from 'vitest'
|
||||
import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { ConnectionSinks } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import * as RuntimeClient from '../src/client/index.ts'
|
||||
import { FakeApiClient } from './fake-api.ts'
|
||||
import type { SessionsService } from '../src/client/sessions/service.ts'
|
||||
import type { WorkspacesService } from '../src/client/workspaces/service.ts'
|
||||
import { FakeApiClient, ok } from './fake-api.ts'
|
||||
|
||||
interface Bench {
|
||||
ctx: Context
|
||||
@@ -33,6 +35,10 @@ async function mount(): Promise<Bench> {
|
||||
return bench
|
||||
}
|
||||
|
||||
async function flushMicrotasks(): Promise<void> {
|
||||
for (let i = 0; i < 12; i++) await Promise.resolve()
|
||||
}
|
||||
|
||||
describe('runtime client apply', () => {
|
||||
it('mounts slots, Sessions, and Workspaces and fans host frames into both managers', async () => {
|
||||
const bench = await mount()
|
||||
@@ -71,6 +77,31 @@ describe('runtime client apply', () => {
|
||||
bench.sinks?.onConnected?.()
|
||||
})
|
||||
|
||||
it('selects the recent Workspace once when the first baselines have no current session', async () => {
|
||||
const bench = await mount()
|
||||
bench.api.onWorkspaceList = () => Promise.resolve(ok({
|
||||
items: [{
|
||||
workspaceId: 'w-recent', path: '/w/recent', title: 'recent', sessionIds: [],
|
||||
createdAt: '2026-01-01T00:00:00.000Z', updatedAt: '2026-01-01T00:00:00.000Z',
|
||||
}] as never[],
|
||||
}))
|
||||
bench.api.onList = () => Promise.resolve(ok({ items: [] }))
|
||||
|
||||
bench.sinks?.onConnected?.()
|
||||
await flushMicrotasks()
|
||||
|
||||
const sessions = bench.ctx.get('sessions') as SessionsService
|
||||
const workspaces = bench.ctx.get('workspaces') as WorkspacesService
|
||||
expect(bench.api.callsOf('session.create')).toEqual([{ workspaceId: 'w-recent' }])
|
||||
expect(sessions.list.getSnapshot().current).toBe('fk-new')
|
||||
|
||||
sessions.clear()
|
||||
await workspaces.refresh()
|
||||
await flushMicrotasks()
|
||||
expect(sessions.list.getSnapshot().current).toBeUndefined()
|
||||
expect(bench.api.callsOf('session.create')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('stops the stream loop when the plugin fiber unloads', async () => {
|
||||
const bench = await mount()
|
||||
const fiber = [...bench.ctx.registry.values()].find(f => f.name?.includes('client'))
|
||||
|
||||
Reference in New Issue
Block a user