diff --git a/packages/client/connection/src/client/index.ts b/packages/client/connection/src/client/index.ts index 026fc1bf5f..b017d1c9e2 100644 --- a/packages/client/connection/src/client/index.ts +++ b/packages/client/connection/src/client/index.ts @@ -21,16 +21,13 @@ export type { ClientRequest, ServerResponse, ServerRequest, ClientResponse, RpcMessage, RpcReceipt, IApiClient, SessionId, SessionEvent, ContentBlock, StreamChunk, } from './api.ts' -export { RpcId, AbstractApiClient, resultOf, transportError } from './api.ts' +export { RpcId, AbstractApiClient, transportError } from './api.ts' -// ---- Connection loop ---- -export { ConnectionController } from './connection.ts' +// ---- Connection loop types (part of the ConnectionHandle.start contract; +// the controller class itself stays package-internal — apply owns the loop, +// tests reach it via src) ---- export type { ConnectionConfig, ConnectionSinks, ConnectionState } -// ---- Platform client subclasses ---- -export { WebApiClient } from './web-api-client.ts' -export { FixtureApiClient, createFixtureApi } from './fixture.ts' - /** Required services (none — this is the wire root). */ export const inject: string[] = [] diff --git a/packages/client/runtime/src/client/index.ts b/packages/client/runtime/src/client/index.ts index 6c012fa410..a8ac9d4e51 100644 --- a/packages/client/runtime/src/client/index.ts +++ b/packages/client/runtime/src/client/index.ts @@ -15,16 +15,16 @@ import { SlotsService } from './slots.ts' import { SessionsService } from './sessions/service.ts' import type { ConversationSnapshot, RunningToolCall, ToolResultNode } from './sessions/conversation.ts' +// Contract face only: implementation internals (SessionManager, Session, +// PAGE_MESSAGES, the lineage/list-snapshot shapes, the block classifier) +// stay package-internal — tests reach them via src, and downstream packages +// consume sessions exclusively through the service + snapshot types below. export { SlotsService } from './slots.ts' export { SessionsService, scopeOf } from './sessions/service.ts' export type { SessionBinding, SessionListState, SessionSummary } from './sessions/service.ts' -export { SessionManager } from './sessions/manager.ts' -export type { SessionListSnapshot } from './sessions/manager.ts' -export { Session, PAGE_MESSAGES } from './sessions/session.ts' -export type { SessionListEntry } from './sessions/lineage.ts' export type { AssistantBlock, AssistantMessageNode, ContextMessageNode, ConversationNode, ConversationSnapshot, - OpenState, PartialAssistant, PendingInteraction, PromptError, RunningToolCall, SteeringMessageNode, + PendingInteraction, RunningToolCall, SteeringMessageNode, ToolResultNode, UnknownSurfaceNode, UserMessageNode, } from './sessions/conversation.ts' export type { SessionId } from '@deepseek-ai/dsh-client-connection/client' diff --git a/packages/client/ui-conversation/README.md b/packages/client/ui-conversation/README.md index c75077d11d..425122958a 100644 --- a/packages/client/ui-conversation/README.md +++ b/packages/client/ui-conversation/README.md @@ -2,7 +2,7 @@ Conversation domain: skeleton (header/tabs/composer/empty state), chat view (grouped step-summary flow, streaming tail isolation), ctx.toolviews named registry with bash samples, minimal details panel, scope-addressed ConversationService. Contract: api-contracts v3 §7. -`src/client/` is organized for the future package split: `contract/` is the sole inter-domain shared face (`slots.ts` composed slot props, `views.ts` view ring, `toolview.ts` tool ring, `tool-call-model.ts`); the `skeleton/`, `chat/`, and `toolviews/` domain directories import contract files and never each other; `apply.ts` is the only assembly point allowed to import all three domains. +`src/client/` is organized for the future package split: `contract/` is the sole inter-domain shared face (`slots.ts` composed slot props, `views.ts` view ring, `toolview.ts` tool ring, `tool-call-model.ts`); the `skeleton/`, `chat/`, and `toolviews/` domain directories import contract files and never each other; `apply.ts` is the only assembly point allowed to import all three domains. The `/client` export surface is the contract only — `apply`/`inject`, the two service classes, and the `contract/` type families; implementation components (skeleton, chat rows) stay internal and reach the page exclusively through apply's slot registrations (tests take them via the `./src/*` subpath). ## Model Experience diff --git a/packages/client/ui-conversation/src/client/index.ts b/packages/client/ui-conversation/src/client/index.ts index 17e9ed88a8..d34b12b0d5 100644 --- a/packages/client/ui-conversation/src/client/index.ts +++ b/packages/client/ui-conversation/src/client/index.ts @@ -24,15 +24,9 @@ export type { ConversationInjected, ConversationSlotProps, DetailsInjected, DetailsSlotProps, EmptyStateInjected, EmptyStateSlotProps, } from './contract/slots.ts' - -export { ConversationRoot } from './skeleton/ConversationRoot.tsx' -export type { ConversationRootProps } from './skeleton/ConversationRoot.tsx' -export { InputBar } from './skeleton/InputBar.tsx' -export type { InputBarError, InputBarProps } from './skeleton/InputBar.tsx' -export { EmptyState } from './skeleton/EmptyState.tsx' -export type { EmptyStateProps } from './skeleton/EmptyState.tsx' -export { DetailsPanel } from './skeleton/DetailsPanel.tsx' -export type { DetailsPanelProps } from './skeleton/DetailsPanel.tsx' +// Skeleton components are implementation detail, not contract: they reach the +// page exclusively through apply's slot registrations. Tests take them via the +// "./src/*" subpath (repo convention for internal symbols). declare module 'cordis' { interface Context { diff --git a/packages/client/ui-conversation/tests/skeleton-branches.spec.tsx b/packages/client/ui-conversation/tests/skeleton-branches.spec.tsx index 25808e17c5..799a1e1c7f 100644 --- a/packages/client/ui-conversation/tests/skeleton-branches.spec.tsx +++ b/packages/client/ui-conversation/tests/skeleton-branches.spec.tsx @@ -9,8 +9,11 @@ import { cleanup, fireEvent, render, waitFor } from '@testing-library/react' import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react' import type { UseSession } from '@deepseek-ai/dsh-client-web-react' import type { ConversationSnapshot, SessionId, SessionSummary } from '@deepseek-ai/dsh-client-runtime/client' -import { ConversationRoot, DetailsPanel, EmptyState } from '@deepseek-ai/dsh-client-ui-conversation/client' import type { SelectionTarget, ViewEntry } from '@deepseek-ai/dsh-client-ui-conversation/client' +// Internal skeleton components: src-subpath imports (not part of the /client contract). +import { ConversationRoot } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/ConversationRoot.tsx' +import { DetailsPanel } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/DetailsPanel.tsx' +import { EmptyState } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/EmptyState.tsx' afterEach(cleanup) diff --git a/packages/client/ui-conversation/tests/skeleton.spec.tsx b/packages/client/ui-conversation/tests/skeleton.spec.tsx index ab23a64389..0d54efe1e0 100644 --- a/packages/client/ui-conversation/tests/skeleton.spec.tsx +++ b/packages/client/ui-conversation/tests/skeleton.spec.tsx @@ -12,10 +12,11 @@ import type { FC } from 'react' import { bindSnapshotSelector, createSnapshotStore } from '@deepseek-ai/dsh-client-web-react' import type { UseSession } from '@deepseek-ai/dsh-client-web-react' import type { SessionId, SessionSummary } from '@deepseek-ai/dsh-client-runtime/client' -import { - ConversationRoot, DetailsPanel, EmptyState, -} from '@deepseek-ai/dsh-client-ui-conversation/client' import type { SelectionTarget, ViewEntry, ViewId } from '@deepseek-ai/dsh-client-ui-conversation/client' +// Internal skeleton components: src-subpath imports (not part of the /client contract). +import { ConversationRoot } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/ConversationRoot.tsx' +import { DetailsPanel } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/DetailsPanel.tsx' +import { EmptyState } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/EmptyState.tsx' const sid = (s: string): SessionId => s as SessionId diff --git a/packages/client/ui-layout/README.md b/packages/client/ui-layout/README.md index 6c935f4b9d..69f9d2fa2e 100644 --- a/packages/client/ui-layout/README.md +++ b/packages/client/ui-layout/README.md @@ -2,7 +2,9 @@ Shell plugin: three-column AppFrame (drag handles, concession chain) + ctx.layout viewing-state service (nav, panel widths, persist); defines the sidebar/conversation/details/conversation.empty slots. Contract: api-contracts v3 §5. -Slot declarations use the composed-props entry form (`owner` share, no full `props`): the exported OwnerShare contracts are `SidebarOwnerProps` / `ConvOwnerProps` / `DetailsOwnerProps` / `EmptyOwnerProps` — registrants reference them via `OwnerOf<'sidebar' | ...>` and compose their own injected share locally. The `conversation` entry authorizes `conversation.empty` delegation through `children`. +Slot declarations use the composed-props entry form (`owner` share, no full `props`): the exported OwnerShare contracts are `SidebarOwnerProps` / `ConvOwnerProps` / `DetailsOwnerProps` / `EmptyOwnerProps` — registrants reference them via `OwnerOf<'sidebar' | ...>` and compose their own injected share locally. No entry declares `children` (declaring it requires the registered component to carry the slots face): no P-I slot component delegates — `conversation.empty` is rendered by the shell's assembly closure, not handed down by ConversationRoot. + +The export surface is the cross-package contract only: the AppFrame trio (+ `AppFrameProps`) consumed by the web shell's assembly, `LayoutService` with its store shapes (`NavState`/`PanelState`/`ViewId`), and the OwnerShare contracts. The concession-chain solver (`computeColumns`) and its geometry constants are package-internal; tests import them from `/src`. ## Model Experience diff --git a/packages/client/ui-layout/src/client/index.ts b/packages/client/ui-layout/src/client/index.ts index e07c5d9896..ceb4ff0b06 100644 --- a/packages/client/ui-layout/src/client/index.ts +++ b/packages/client/ui-layout/src/client/index.ts @@ -10,12 +10,13 @@ import type { Context } from 'cordis' import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client' import { LayoutService } from './service.ts' +// Contract surface only (export-convergence rule: cross-package consumers +// keep a symbol exported; test-only/package-internal symbols live off /src). +// AppFrame trio + AppFrameProps: consumed by the web shell's assembly closure. +// LayoutService: the ctx.layout service class (consumers type against it). +// PanelState rides AppFrameProps' hooks; NavState/ViewId are service-store +// shapes referenced through LayoutService's members. export { AppFrame, CenterColumn, DetailsColumn, type AppFrameProps } from './AppFrame.tsx' -export { - clampWidth, computeColumns, - CENTER_MIN, DETAILS_DEFAULT, DETAILS_MAX, DETAILS_MIN, SIDEBAR_DEFAULT, SIDEBAR_MAX, SIDEBAR_MIN, - type Columns, type PanelInput, -} from './columns.ts' export { LayoutService, type NavState, type PanelState, type ViewId } from './service.ts' declare module 'cordis' { diff --git a/packages/client/ui-layout/tests/app-frame.spec.tsx b/packages/client/ui-layout/tests/app-frame.spec.tsx index dfc49be327..9fae776dc4 100644 --- a/packages/client/ui-layout/tests/app-frame.spec.tsx +++ b/packages/client/ui-layout/tests/app-frame.spec.tsx @@ -10,7 +10,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { act, cleanup, render } from '@testing-library/react' import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react' import { AppFrame, CenterColumn, DetailsColumn, type PanelState } from '@deepseek-ai/dsh-client-ui-layout/client' -import { clampWidth } from '@deepseek-ai/dsh-client-ui-layout/client' +import { clampWidth } from '@deepseek-ai/dsh-client-ui-layout/src/client/columns.ts' /** Observer stub: captures the callback so tests can fire resizes manually. */ let fireResize: (() => void) | null = null diff --git a/packages/client/ui-layout/tests/columns.spec.ts b/packages/client/ui-layout/tests/columns.spec.ts index 49d351a6d4..66a785e114 100644 --- a/packages/client/ui-layout/tests/columns.spec.ts +++ b/packages/client/ui-layout/tests/columns.spec.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest' import { CENTER_MIN, clampWidth, computeColumns, DETAILS_DEFAULT, DETAILS_MIN, SIDEBAR_DEFAULT, SIDEBAR_MIN, -} from '@deepseek-ai/dsh-client-ui-layout/client' +} from '@deepseek-ai/dsh-client-ui-layout/src/client/columns.ts' const open = (width: number) => ({ open: true, width }) const closed = (width: number) => ({ open: false, width }) diff --git a/packages/client/ui-layout/tests/service.spec.ts b/packages/client/ui-layout/tests/service.spec.ts index 85d458cf7b..d5f784fa0c 100644 --- a/packages/client/ui-layout/tests/service.spec.ts +++ b/packages/client/ui-layout/tests/service.spec.ts @@ -9,7 +9,8 @@ import { beforeEach, describe, expect, it } from 'vitest' import type { Context } from 'cordis' import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react' import type { SessionId, SessionListState } from '@deepseek-ai/dsh-client-runtime/client' -import { LayoutService, DETAILS_DEFAULT, SIDEBAR_DEFAULT } from '@deepseek-ai/dsh-client-ui-layout/client' +import { LayoutService } from '@deepseek-ai/dsh-client-ui-layout/client' +import { DETAILS_DEFAULT, SIDEBAR_DEFAULT } from '@deepseek-ai/dsh-client-ui-layout/src/client/columns.ts' function makeCtx() { const list = createSnapshotStore({ ids: [], byId: {} }) diff --git a/packages/client/ui-sidebar/README.md b/packages/client/ui-sidebar/README.md index 25f840ab3d..3e1bfaa11e 100644 --- a/packages/client/ui-sidebar/README.md +++ b/packages/client/ui-sidebar/README.md @@ -4,6 +4,8 @@ Sidebar plugin: session multi-level tree (cwd grouping + parentId nesting), sear `src/client/contract/slots.ts` is the single-domain contract file: `SidebarRootInjected` (the registrant's own injected share — tree hook, current-session hook, actions) and `SidebarRootComponentProps = OwnerOf<'sidebar'> & SidebarRootInjected` (the owner share referenced from ui-layout's slot declaration, never re-stated). `apply` registers SidebarRoot cast-free against that composition; the inject factory binds layout/sessions off `RootBinding`. +The `/client` export surface is the plugin body (`apply`/`inject`) plus the contract types only — SidebarRoot, the row components, and the tree/store implementation are internal (the slot registration closes over them; tests import src paths directly). + ## Model Experience None, as the sidebar renders the browser session list; nothing here reaches a model request. diff --git a/packages/client/ui-sidebar/src/client/index.ts b/packages/client/ui-sidebar/src/client/index.ts index fab3bc4836..8fd86396db 100644 --- a/packages/client/ui-sidebar/src/client/index.ts +++ b/packages/client/ui-sidebar/src/client/index.ts @@ -3,6 +3,10 @@ * sidebar slot; tree derivation materialized in a plugin-owned snapshot * store (pure consumer — no ctx service). Contract: api-contracts v3 * section 6; props composition in contract/slots.ts. + * + * Export discipline: the public surface is the plugin body (apply/inject) + * plus the contract types. Implementation components and derivation live in + * their modules; tests reach them via src paths. */ import type { RootBinding } from '@deepseek-ai/dsh-client-ui-slots' import type { ClientContext, SessionId } from '@deepseek-ai/dsh-client-runtime/client' @@ -10,17 +14,6 @@ import type { SidebarRootInjected } from './contract/slots.ts' import { createSidebarTreeStore } from './store.ts' import { SidebarRoot } from './SidebarRoot.tsx' -export { - deriveRows, formatRelativeTime, projectLabel, - UNGROUPED_KEY, UNGROUPED_LABEL, - type ProjectRow, type SessionRow, type SidebarRow, type TreeView, -} from './tree.ts' -export { - createSidebarTreeStore, - type GroupBy, type SidebarTreeState, type SidebarTreeStore, -} from './store.ts' -export { ProjectRowItem, SessionRowItem } from './Rows.tsx' -export { SidebarRoot } from './SidebarRoot.tsx' export type { SidebarActions, SidebarRootComponentProps, SidebarRootInjected, SidebarTreeActions, } from './contract/slots.ts' diff --git a/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx b/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx index db03c672dd..cdad00f982 100644 --- a/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx +++ b/packages/client/ui-sidebar/tests/sidebar-root.spec.tsx @@ -10,10 +10,9 @@ import { cleanup, fireEvent, render, screen } from '@testing-library/react' import { act } from 'react' import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react' import type { SessionId, SessionListState, SessionSummary } from '@deepseek-ai/dsh-client-runtime/client' -import { - createSidebarTreeStore, SidebarRoot, - type SidebarActions, type SidebarTreeStore, -} from '@deepseek-ai/dsh-client-ui-sidebar/client' +import { createSidebarTreeStore, type SidebarTreeStore } from '../src/client/store.ts' +import { SidebarRoot } from '../src/client/SidebarRoot.tsx' +import type { SidebarActions } from '@deepseek-ai/dsh-client-ui-sidebar/client' const sid = (s: string) => s as SessionId diff --git a/packages/client/ui-sidebar/tests/store.spec.ts b/packages/client/ui-sidebar/tests/store.spec.ts index d0bb3b386b..8ded70400c 100644 --- a/packages/client/ui-sidebar/tests/store.spec.ts +++ b/packages/client/ui-sidebar/tests/store.spec.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest' import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react' import type { SessionId, SessionListState, SessionSummary } from '@deepseek-ai/dsh-client-runtime/client' -import { createSidebarTreeStore } from '@deepseek-ai/dsh-client-ui-sidebar/client' +import { createSidebarTreeStore } from '../src/client/store.ts' const sid = (s: string) => s as SessionId diff --git a/packages/client/ui-sidebar/tests/tree.spec.ts b/packages/client/ui-sidebar/tests/tree.spec.ts index 1b29d460cf..3919a16d23 100644 --- a/packages/client/ui-sidebar/tests/tree.spec.ts +++ b/packages/client/ui-sidebar/tests/tree.spec.ts @@ -3,7 +3,7 @@ import type { SessionId, SessionListState, SessionSummary } from '@deepseek-ai/d import { deriveRows, formatRelativeTime, projectLabel, UNGROUPED_KEY, UNGROUPED_LABEL, type SessionRow, type TreeView, -} from '@deepseek-ai/dsh-client-ui-sidebar/client' +} from '../src/client/tree.ts' const sid = (s: string) => s as SessionId diff --git a/packages/client/ui-trajectory/src/client/index.ts b/packages/client/ui-trajectory/src/client/index.ts index b65eb3d3ab..8c8ebc330d 100644 --- a/packages/client/ui-trajectory/src/client/index.ts +++ b/packages/client/ui-trajectory/src/client/index.ts @@ -9,10 +9,8 @@ import { TrajectoryStatsHeader, type TrajectoryChromeProps } from './TrajectoryS import { TrajectoryView } from './TrajectoryView.tsx' import { WaterfallView, type WaterfallExtraProps } from './WaterfallView.tsx' -export { deriveSpans, deriveSpanStats, type SpanStats, type TurnSpan } from './spans.ts' -export { TrajectoryStatsHeader, type TrajectoryChromeProps } from './TrajectoryStatsHeader.tsx' -export { TrajectoryView } from './TrajectoryView.tsx' -export { WaterfallView, type WaterfallExtraProps } from './WaterfallView.tsx' +export type { TrajectoryChromeProps } from './TrajectoryStatsHeader.tsx' +export type { WaterfallExtraProps } from './WaterfallView.tsx' declare module '@deepseek-ai/dsh-client-ui-conversation/client' { interface ConversationViewMap { diff --git a/packages/client/ui-trajectory/tests/views.spec.tsx b/packages/client/ui-trajectory/tests/views.spec.tsx index 41a3c0c95b..a49f77baf2 100644 --- a/packages/client/ui-trajectory/tests/views.spec.tsx +++ b/packages/client/ui-trajectory/tests/views.spec.tsx @@ -13,11 +13,14 @@ import { createElement, Fragment, type FC, type ReactNode } from 'react' import { bindSnapshotSelector, createSnapshotStore } from '@deepseek-ai/dsh-client-web-react' import type { UseSession } from '@deepseek-ai/dsh-client-web-react' import type { ConversationSnapshot, SessionId, SessionSummary } from '@deepseek-ai/dsh-client-runtime/client' -import { ConversationRoot, ConversationService } from '@deepseek-ai/dsh-client-ui-conversation/client' +import { ConversationService } from '@deepseek-ai/dsh-client-ui-conversation/client' +import { ConversationRoot } from '@deepseek-ai/dsh-client-ui-conversation/src/client/skeleton/ConversationRoot.tsx' import type { ConvViewProps, ViewEntry, ViewId } from '@deepseek-ai/dsh-client-ui-conversation/client' -import { - apply, deriveSpans, deriveSpanStats, inject, TrajectoryStatsHeader, TrajectoryView, WaterfallView, -} from '@deepseek-ai/dsh-client-ui-trajectory/client' +import { apply, inject } from '@deepseek-ai/dsh-client-ui-trajectory/client' +import { deriveSpans, deriveSpanStats } from '@deepseek-ai/dsh-client-ui-trajectory/src/client/spans.ts' +import { TrajectoryStatsHeader } from '@deepseek-ai/dsh-client-ui-trajectory/src/client/TrajectoryStatsHeader.tsx' +import { TrajectoryView } from '@deepseek-ai/dsh-client-ui-trajectory/src/client/TrajectoryView.tsx' +import { WaterfallView } from '@deepseek-ai/dsh-client-ui-trajectory/src/client/WaterfallView.tsx' import { apply as nodeApply } from '@deepseek-ai/dsh-client-ui-trajectory' const SID = 's1' as SessionId