From 997ad27a60171b47166663cd25709ee3e4dc312f Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Sun, 23 Aug 2026 22:50:42 +0800 Subject: [PATCH] refactor(client): remove compatibility imports --- .../src/client/contract/sessions.ts | 5 +++- .../session-controller/src/client/index.ts | 24 +------------------ .../tests/client-apply.client.spec.ts | 10 +------- .../src/client/service.ts | 3 ++- .../src/client/contract/input.ts | 24 +++++++++++++++---- .../src/client/skeleton/InputBar.tsx | 3 +-- .../ui-input-trigger/src/client/controller.ts | 5 +++- .../ui-input-trigger/src/client/index.ts | 4 ++-- .../ui-input-trigger/src/core/contract.ts | 3 ++- .../tests/apply.client.spec.ts | 6 ++--- .../ui-model-selection/src/client/index.ts | 6 ++--- .../ui-model-selection/src/client/service.ts | 4 ++-- .../ui-permission-presets/src/client/index.ts | 4 ++-- packages/client/ui-skill/src/client/index.ts | 4 ++-- packages/client/ui-slots/src/index.ts | 4 +++- .../ui-user-questions/src/client/index.ts | 3 ++- .../ui-workspace/src/client/navigation.ts | 5 +++- 17 files changed, 57 insertions(+), 60 deletions(-) diff --git a/packages/api/session-controller/src/client/contract/sessions.ts b/packages/api/session-controller/src/client/contract/sessions.ts index 4e64574681..d2129e7dd6 100644 --- a/packages/api/session-controller/src/client/contract/sessions.ts +++ b/packages/api/session-controller/src/client/contract/sessions.ts @@ -76,7 +76,10 @@ export interface ISessions { noteAgentPreset(sessionId: SessionId, agentPreset: string): void /** Clear the current selection into the no-session view state. */ clear(): void - /** @returns completion of the current or newly started Session-list refresh. */ + /** + * Refresh the Host-authoritative Session list. + * @returns completion of the current or newly started Session-list refresh. + */ refresh(): Promise /** * Search the Host's visible message-content index. Results stay diff --git a/packages/api/session-controller/src/client/index.ts b/packages/api/session-controller/src/client/index.ts index acbc81cae3..86735be6ea 100644 --- a/packages/api/session-controller/src/client/index.ts +++ b/packages/api/session-controller/src/client/index.ts @@ -5,7 +5,6 @@ import type {} from '@deepseek-ai/dsh-agent/types' import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client' import { createSessionControlStream } from './transport.ts' import { ClientSessions } from './sessions/service.ts' -import type { ISessions } from './contract/sessions.ts' import type { SessionRemotes } from './sessions/remotes.ts' import type {} from '../remote-events.ts' @@ -26,7 +25,7 @@ export type { } from './transport.ts' export { createScope, scopeOf } from './scope.ts' export type { AgentContext, AgentScopeHandle } from './scope.ts' -export { SessionCreateError, SessionForkError, workspaceTitleOf } from './sessions/service.ts' +export { SessionCreateError, SessionForkError } from './sessions/service.ts' export type { SessionBinding, SessionListState, SessionSummary } from './sessions/service.ts' export type { SessionListPhase, @@ -52,18 +51,8 @@ export type { SessionSnapshot, } from './contract/snapshot.ts' export type { ClientFailure, ClientResult } from './contract/result.ts' -export { indexSubagentDescendants } from './sessions/subagent-lineage.ts' -export type { SubagentDescendantSummary } from './sessions/subagent-lineage.ts' declare module '@deepseek-ai/cordis' { - interface Events { - /** - * A Host connection generation completed its readiness handshake. - * @mode emit - */ - 'connection/reset'(): void - } - interface Context { /** Client Session object layer and Agent scope owner. */ sessions: import('./contract/sessions.ts').ISessions @@ -79,17 +68,6 @@ export const inject = [ 'remote.session', ] -/** - * Resolve the Client Session service from any Client Cordis context. - * @param ctx - Client root or Agent-scoped context. - * @returns the Client Session object layer. - */ -export function resolveClientSessions(ctx: Context): ISessions { - const sessions = ctx.get('sessions') - if (sessions === undefined) throw new Error('session-controller: Client sessions service unavailable') - return sessions -} - /** * Install Client Session state and its reconnecting control stream. * @param ctx - Client Cordis context. diff --git a/packages/api/session-controller/tests/client-apply.client.spec.ts b/packages/api/session-controller/tests/client-apply.client.spec.ts index 04508378f9..19cbf911f7 100644 --- a/packages/api/session-controller/tests/client-apply.client.spec.ts +++ b/packages/api/session-controller/tests/client-apply.client.spec.ts @@ -87,7 +87,7 @@ async function mount(initialHost?: HostDescription): Promise { ctx.reflect.provide('remote.session', remote.session) const fiber = ctx.plugin(SessionClient) await fiber - const sessions = SessionClient.resolveClientSessions(ctx) as ClientSessions + const sessions = ctx.sessions as ClientSessions return { ctx, api, @@ -108,14 +108,6 @@ async function flush(): Promise { } describe('Session Controller Client apply', () => { - it('requires the installed Session service at the resolver boundary', () => { - const ctx = new Context() - contexts.add(ctx) - - expect(() => SessionClient.resolveClientSessions(ctx)) - .toThrow('session-controller: Client sessions service unavailable') - }) - it('routes Session Remote Events and connection generations into the object layer', async () => { const connected = vi.spyOn(ClientSessions.prototype, 'handleConnected') const error = vi.spyOn(ClientSessions.prototype, 'handleSessionError') diff --git a/packages/api/workspace-controller/src/client/service.ts b/packages/api/workspace-controller/src/client/service.ts index 993ac55ca6..a8511ac61e 100644 --- a/packages/api/workspace-controller/src/client/service.ts +++ b/packages/api/workspace-controller/src/client/service.ts @@ -3,7 +3,8 @@ import { Service, type Context } from '@deepseek-ai/cordis' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { RemoteFailure } from '@deepseek-ai/dsh-typert-protocol' -import type { WorkspaceId, WorkspaceView } from '../types.ts' +import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types' +import type { WorkspaceView } from '../types.ts' import type { ClientWorkspaceModel, WorkspaceSnapshot } from './model.ts' /** Structured create failure for callers that distinguish Host business errors. */ diff --git a/packages/client/ui-conversation/src/client/contract/input.ts b/packages/client/ui-conversation/src/client/contract/input.ts index e3ad336313..7d50fa8124 100644 --- a/packages/client/ui-conversation/src/client/contract/input.ts +++ b/packages/client/ui-conversation/src/client/contract/input.ts @@ -133,13 +133,29 @@ export interface InputTriggerController { declare module '@deepseek-ai/cordis' { interface Events { - /** @param request - command claim and span. @mode bail */ + /** + * Claim a command token for the scoped input machine. + * @param request - command claim and span. + * @mode bail + */ 'slash/input-begin-command'(request: BeginCommandRequest): true | undefined - /** @param request - reference and span. @mode bail */ + /** + * Insert a structured reference into the scoped input machine. + * @param request - reference and span. + * @mode bail + */ 'slash/input-insert-reference'(request: InsertReferenceRequest): true | undefined - /** @param request - token guard. @mode bail */ + /** + * Consume a trigger token without inserting replacement content. + * @param request - token guard. + * @mode bail + */ 'slash/input-consume-token'(request: ConsumeTokenRequest): true | undefined - /** @param request - plain text and span. @mode bail */ + /** + * Insert plain text into the scoped input machine. + * @param request - plain text and span. + * @mode bail + */ 'slash/input-insert-text'(request: InsertTextRequest): true | undefined } } diff --git a/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx b/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx index d22049b370..8bedb2d260 100644 --- a/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx +++ b/packages/client/ui-conversation/src/client/skeleton/InputBar.tsx @@ -10,7 +10,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } fr import type { ChangeEvent, KeyboardEvent, MouseEvent, ReactNode } from 'react' import clsx from 'clsx' import { - IconPlusOutline16, IconWarningOutline16, Toast, Tooltip, + IconPlusOutline16, IconWarningOutline16, ReferenceIcon, Toast, Tooltip, } from '@deepseek-ai/dsh-client-ui-primitives' // Type-only: the `plan` projection key merge (the TodoDock posture — the // composer reads a host-computed value; the domain owns the key). @@ -24,7 +24,6 @@ import { deriveDecorations } from './decorations.ts' import type { DraftDecorations } from './decorations.ts' import type { EditRange } from '../contract/input.ts' import { attachmentErrorText, imageSizeText } from '../image-labels.ts' -import { ReferenceIcon } from './ReferenceIcon.tsx' import { ContextMeter } from './ContextMeter.tsx' import { PermissionSelect } from './PermissionSelect.tsx' import { isSafariBrowser, repairSafariTextareaLayout } from './safari.ts' diff --git a/packages/client/ui-input-trigger/src/client/controller.ts b/packages/client/ui-input-trigger/src/client/controller.ts index b2507cfc12..9a477a91e6 100644 --- a/packages/client/ui-input-trigger/src/client/controller.ts +++ b/packages/client/ui-input-trigger/src/client/controller.ts @@ -9,12 +9,15 @@ */ import type { Context as ClientContext } from '@deepseek-ai/cordis' import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store' +import type { + ArbitrateKey, ArbitrateOutcome, PickOutcome, +} from '@deepseek-ai/dsh-client-ui-conversation/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import { detectTrigger } from '../core/detect.ts' import { MENU_CLOSED, menuReduce, seedGroups } from '../core/menu.ts' import type { MenuEvent, MenuState, TriggerHit } from '../core/contract.ts' import type { - ArbitrateKey, ArbitrateOutcome, ClientSessionContext, PickOutcome, InputTriggerSource, SubmitEnvelope, TriggerChar, TriggerGuard, + ClientSessionContext, InputTriggerSource, SubmitEnvelope, TriggerChar, TriggerGuard, } from '../types.ts' /** Roster access the controller borrows from the root service (registration order preserved). */ diff --git a/packages/client/ui-input-trigger/src/client/index.ts b/packages/client/ui-input-trigger/src/client/index.ts index 64f48ea64a..2c819c6f50 100644 --- a/packages/client/ui-input-trigger/src/client/index.ts +++ b/packages/client/ui-input-trigger/src/client/index.ts @@ -7,7 +7,7 @@ // Type-only: pulls the locale plugin's Context merge (ctx.locale). import type {} from '@deepseek-ai/dsh-client-locale/client' import type { Context as ClientContext } from '@deepseek-ai/cordis' -import { resolveClientSessions } from '@deepseek-ai/dsh-api-session-controller/client' +import type {} from '@deepseek-ai/dsh-api-session-controller/client' import type {} from '@deepseek-ai/dsh-client-ui-renderer/client' import type {} from '@deepseek-ai/dsh-client-ui-session/client' import { InputTriggerService } from './service.ts' @@ -60,7 +60,7 @@ export function apply(ctx: ClientContext): void { ctx.effect(() => ctx.locale.register(MENU_NS, { zh, en }), 'ui-input-trigger: menu dictionaries') ctx.inject(['slots', 'inputTriggers', 'sessions'], (scope: ClientContext) => { const inputTriggers = scope.inputTriggers - const sessions = resolveClientSessions(scope) + const sessions = scope.sessions scope.slots.inject('conversation.input.overlay', () => scope.slots.register({ name: 'conversation.input.overlay', id: 'slash-menu', diff --git a/packages/client/ui-input-trigger/src/core/contract.ts b/packages/client/ui-input-trigger/src/core/contract.ts index 9c11c01324..685364f489 100644 --- a/packages/client/ui-input-trigger/src/core/contract.ts +++ b/packages/client/ui-input-trigger/src/core/contract.ts @@ -4,7 +4,8 @@ * live in sibling modules annotated with these * aliases; the service shell wires them to ctx. */ -import type { InputTriggerCandidate, TokenSpan, TriggerChar, TriggerGuard, TriggerPosition } from '../types.ts' +import type { TokenSpan } from '@deepseek-ai/dsh-client-ui-conversation/client' +import type { InputTriggerCandidate, TriggerChar, TriggerGuard, TriggerPosition } from '../types.ts' /** A detected trigger token under the caret. */ export interface TriggerHit { diff --git a/packages/client/ui-input-trigger/tests/apply.client.spec.ts b/packages/client/ui-input-trigger/tests/apply.client.spec.ts index 43f1012993..9cf41c62a1 100644 --- a/packages/client/ui-input-trigger/tests/apply.client.spec.ts +++ b/packages/client/ui-input-trigger/tests/apply.client.spec.ts @@ -7,9 +7,7 @@ import { Context } from '@deepseek-ai/cordis' import { describe, expect, it } from 'vitest' import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client' -import { - createScope, resolveClientSessions, scopeOf, -} from '@deepseek-ai/dsh-api-session-controller/client' +import { createScope, scopeOf } from '@deepseek-ai/dsh-api-session-controller/client' import { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import { apply, inject, InputTriggerService } from '@deepseek-ai/dsh-client-ui-input-trigger/client' @@ -80,7 +78,7 @@ describe('apply', () => { const injectEntry = entries[0]!.inject as unknown as (sessionId: SessionId) => MenuViewInjected const injected = injectEntry(sid('a')) const controller = inputTriggers.sessionOf( - resolveClientSessions(ctx).scope(sid('a'))!, + ctx.sessions.scope(sid('a'))!, ) expect(injected.menu).toBe(controller.menu) // The pick face routes into the controller pipeline (closed menu → no-op). diff --git a/packages/client/ui-model-selection/src/client/index.ts b/packages/client/ui-model-selection/src/client/index.ts index a2359a87b3..f5fd706fb4 100644 --- a/packages/client/ui-model-selection/src/client/index.ts +++ b/packages/client/ui-model-selection/src/client/index.ts @@ -13,7 +13,7 @@ */ // Type-only: the carrier types, the forwarded Host-event face and the ctx.remote merge. import type { ModelSelection, SessionModels } from '@deepseek-ai/dsh-api-session-controller/types' -import { resolveClientSessions } from '@deepseek-ai/dsh-api-session-controller/client' +import type {} from '@deepseek-ai/dsh-api-session-controller/client' import type { Context as ClientContext } from '@deepseek-ai/cordis' import type { CommandUiContract, SelectOption } from '@deepseek-ai/dsh-client-ui-commands/client' // Type-only: pulls the ui-conversation SlotMap merge (the input.model seat). @@ -125,7 +125,7 @@ export function apply(ctx: ClientContext): void { ctx.inject(['commandUi', 'modelDirectories'], (scope: ClientContext) => { const command = scope.get('commandUi') as CommandUiContract const models = scope.modelDirectories - const sessions = resolveClientSessions(scope) + const sessions = scope.sessions scope.effect(() => command.register({ name: 'model', description: t('command.description'), @@ -156,7 +156,7 @@ export function apply(ctx: ClientContext): void { // Entry 2: the composer's named model seat over the SAME directory. ctx.inject(['slots', 'modelDirectories'], (scope: ClientContext) => { const models = scope.modelDirectories - const sessions = resolveClientSessions(scope) + const sessions = scope.sessions scope.slots.inject('conversation.input.model', () => scope.slots.register({ name: 'conversation.input.model', locale: NS, diff --git a/packages/client/ui-model-selection/src/client/service.ts b/packages/client/ui-model-selection/src/client/service.ts index ce981f6d14..0e1ce576c1 100644 --- a/packages/client/ui-model-selection/src/client/service.ts +++ b/packages/client/ui-model-selection/src/client/service.ts @@ -14,7 +14,7 @@ */ import { Service } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis' -import { resolveClientSessions } from '@deepseek-ai/dsh-api-session-controller/client' +import type {} from '@deepseek-ai/dsh-api-session-controller/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import { ModelDirectory } from './directory.ts' @@ -70,7 +70,7 @@ export class ModelDirectoryResolver extends Service { const { live } = this const existing = live.directories.get(sessionId) if (existing !== undefined) return existing - const sessions = resolveClientSessions(this.ctx) + const sessions = this.ctx.sessions const actx = sessions.scope(sessionId) if (actx === undefined) throw new Error(`ui-model-selection: session "${String(sessionId)}" resolved no scope`) const directory = new ModelDirectory( diff --git a/packages/client/ui-permission-presets/src/client/index.ts b/packages/client/ui-permission-presets/src/client/index.ts index e7d346cfef..404d201039 100644 --- a/packages/client/ui-permission-presets/src/client/index.ts +++ b/packages/client/ui-permission-presets/src/client/index.ts @@ -15,7 +15,7 @@ */ import type { Context as ClientContext } from '@deepseek-ai/cordis' import type { ConnectionHandle } from '@deepseek-ai/dsh-api-remotes/client' -import { resolveClientSessions, type SessionFace } from '@deepseek-ai/dsh-api-session-controller/client' +import type { SessionFace } from '@deepseek-ai/dsh-api-session-controller/client' // Type-only: pulls the locale plugin's Context merge (ctx.locale). import type {} from '@deepseek-ai/dsh-client-locale/client' // Type-only: the settings slot types (this package registers a General row). @@ -83,7 +83,7 @@ function optionsOf(value: PermissionSelect, t: (key: string) => string): SelectO */ export function apply(ctx: ClientContext): void { const command = ctx.get('commandUi') as CommandUiContract - const sessions = resolveClientSessions(ctx) + const sessions = ctx.sessions // This optional bundle and ui-conversation can load independently, so each // owns the same safety copy under its own locale namespace. /* jscpd:ignore-start */ diff --git a/packages/client/ui-skill/src/client/index.ts b/packages/client/ui-skill/src/client/index.ts index c62df1051f..9b66956c2d 100644 --- a/packages/client/ui-skill/src/client/index.ts +++ b/packages/client/ui-skill/src/client/index.ts @@ -32,7 +32,7 @@ // Type-only: the carrier types, the forwarded Host-event face and the ctx.remote merge. import type { Context as ClientContext } from '@deepseek-ai/cordis' import type { ConnectionHandle, SkillEntry } from '@deepseek-ai/dsh-api-remotes/client' -import { resolveClientSessions } from '@deepseek-ai/dsh-api-session-controller/client' +import type {} from '@deepseek-ai/dsh-api-session-controller/client' import type { SessionId } from '@deepseek-ai/dsh-session/types' import type { InputTriggerServiceContract, InputTriggerSource } from '@deepseek-ai/dsh-client-ui-input-trigger/client' // Type-only: pulls the locale plugin's Context merge (ctx.locale). @@ -72,7 +72,7 @@ export function apply(ctx: ClientContext): void { )) const skills = (ctx.get('connection') as ConnectionHandle).api.skills - const sessions = resolveClientSessions(ctx) + const sessions = ctx.sessions // Session-keyed catalog cache; single-flight per key. Plugin-closure state: // the fiber effect below is its teardown boundary. const fetches = new Map() diff --git a/packages/client/ui-slots/src/index.ts b/packages/client/ui-slots/src/index.ts index 8579684fa6..6d63eb64a2 100644 --- a/packages/client/ui-slots/src/index.ts +++ b/packages/client/ui-slots/src/index.ts @@ -14,8 +14,10 @@ * consumer merges keys in and the intersection is what keeps them string-typed. * The rule fires on the empty-map view, not on real redundancy. */ import type { ReactNode } from 'react' +import type { + BoundActions, HandleOf, PropsStore, SnapshotSelectorHook, StoreDecl, +} from '@deepseek-ai/dsh-client-store' import type { HostObservable } from './renderer.ts' -import type { BoundActions, HandleOf, PropsStore, SnapshotSelectorHook, StoreDecl } from './store.ts' export * from './store.ts' export * from './renderer.ts' diff --git a/packages/client/ui-user-questions/src/client/index.ts b/packages/client/ui-user-questions/src/client/index.ts index 5344c80ed7..77956258fb 100644 --- a/packages/client/ui-user-questions/src/client/index.ts +++ b/packages/client/ui-user-questions/src/client/index.ts @@ -14,6 +14,7 @@ */ import type { Context as ClientContext } from '@deepseek-ai/cordis' import type {} from '@deepseek-ai/dsh-api-remotes/client' +import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client' import type { ComposerChainProps } from '@deepseek-ai/dsh-client-ui-conversation/client' import type {} from '@deepseek-ai/dsh-client-ui-renderer/client' import type { PendingInteractionPublisher } from '@deepseek-ai/dsh-client-ui-session/client' @@ -56,7 +57,7 @@ async function answerQuestion( next: ClientQuestionNext, registerPendingInteraction: PendingInteractionPublisher, ): Promise { - const sessionId = ctx.sessions.scopeOf(owner) + const sessionId = (ctx.sessions as ISessions).scopeOf(owner) if (sessionId === undefined) return next() const pending = new PendingQuestion(sessionId, request.questions, request.signal) const completed = Promise.withResolvers() diff --git a/packages/client/ui-workspace/src/client/navigation.ts b/packages/client/ui-workspace/src/client/navigation.ts index 7d209baec9..1135f60b2e 100644 --- a/packages/client/ui-workspace/src/client/navigation.ts +++ b/packages/client/ui-workspace/src/client/navigation.ts @@ -31,7 +31,10 @@ export interface UiWorkspace { * @param sessionId - Session to archive. */ archiveSession(sessionId: SessionId): Promise - /** @returns the Host-native picked directory, or null when cancelled. */ + /** + * Open the Host-native directory picker. + * @returns the selected directory, or null when cancelled. + */ pickDirectory(): Promise /** * List one Host directory level.