refactor: apply repository naming contract

Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
Tianyi Cui
2026-08-13 00:36:22 +08:00
parent 101df7cf58
commit a2d0f7f411
3281 changed files with 21730 additions and 21592 deletions
@@ -17,7 +17,7 @@
import { describe, expect, it, vi } from 'vitest'
import { SlotTestRuntime, usePinnedBrowserLanguages, stubSettingsScope } from '@deepseek-ai/dsh-client-test-runtime'
import type { SessionBehaviorOverrides } from '@deepseek-ai/dsh-client-test-runtime'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
import type { ISession, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
import { apply, inject } from '@deepseek-ai/dsh-client-ui-conversation/client'
import type {
@@ -59,7 +59,7 @@ async function bench() {
})
const layoutFake = { openDetails: vi.fn(), closeDetails: vi.fn() }
runtime.provide('layout', layoutFake)
const locale = new LocaleService(runtime.ctx)
const locale = new LocaleRuntime(runtime.ctx)
runtime.provide('locale', locale)
runtime.slots.installLocale(locale)
@@ -3,7 +3,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { cleanup, fireEvent, waitFor, within } from '@testing-library/react'
import { useState } from 'react'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
import type { ISession, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
import type { PropsRenderSlots } from '@deepseek-ai/dsh-client-ui-slots'
import { SlotTestRuntime, usePinnedBrowserLanguages, stubSettingsScope } from '@deepseek-ai/dsh-client-test-runtime'
@@ -55,7 +55,7 @@ async function bench(opts?: { blank?: boolean }) {
runtime.provide('remote', { $on: () => () => {} })
runtime.provide('settingsScope', { bind: () => stubSettingsScope().scope } as never)
runtime.provide('layout', { openDetails: vi.fn(), closeDetails: vi.fn() })
const locale = new LocaleService(runtime.ctx)
const locale = new LocaleRuntime(runtime.ctx)
runtime.provide('locale', locale)
runtime.slots.installLocale(locale)
await runtime.sessions.add({
@@ -83,7 +83,7 @@ describe('resident composer', () => {
runtime.provide('remote', { $on: () => () => {} })
runtime.provide('settingsScope', { bind: () => stubSettingsScope().scope } as never)
runtime.provide('layout', { openDetails: vi.fn(), closeDetails: vi.fn() })
const locale = new LocaleService(runtime.ctx)
const locale = new LocaleRuntime(runtime.ctx)
runtime.provide('locale', locale)
runtime.slots.installLocale(locale)
await runtime.root.declare(LAYOUT_CHILDREN, AppRoot)
@@ -113,7 +113,7 @@ describe('resident composer', () => {
runtime.provide('remote', { $on: () => () => {} })
runtime.provide('settingsScope', { bind: () => stubSettingsScope().scope } as never)
runtime.provide('layout', { openDetails: vi.fn(), closeDetails: vi.fn() })
const locale = new LocaleService(runtime.ctx)
const locale = new LocaleRuntime(runtime.ctx)
runtime.provide('locale', locale)
runtime.slots.installLocale(locale)
await runtime.workspaces.update((draft) => {
@@ -182,7 +182,7 @@ describe('prompt rejection through the assembled composer', () => {
runtime.provide('remote', { $on: () => () => {} })
runtime.provide('settingsScope', { bind: () => stubSettingsScope().scope } as never)
runtime.provide('layout', { openDetails: vi.fn(), closeDetails: vi.fn() })
const locale = new LocaleService(runtime.ctx)
const locale = new LocaleRuntime(runtime.ctx)
runtime.provide('locale', locale)
runtime.slots.installLocale(locale)
const prompt = vi.fn<ISession['prompt']>(async () => ({
@@ -8,7 +8,7 @@
import { describe, expect, it, vi } from 'vitest'
import { SlotTestRuntime, usePinnedBrowserLanguages, stubSettingsScope } from '@deepseek-ai/dsh-client-test-runtime'
import { resolveSlotLabel } from '@deepseek-ai/dsh-client-ui-slots'
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client'
import { apply, inject } from '@deepseek-ai/dsh-client-ui-conversation/client'
@@ -29,7 +29,7 @@ async function bench() {
await runtime.sessions.add(
{ id: CHILD, summary: { title: 'C', displayTitle: 'C', parentId: ROOT } }, { current: false })
runtime.provide('layout', { openDetails: vi.fn(), closeDetails: vi.fn() })
const locale = new LocaleService(runtime.ctx)
const locale = new LocaleRuntime(runtime.ctx)
runtime.provide('locale', locale)
runtime.slots.installLocale(locale)
@@ -288,7 +288,7 @@ describe('MessageItem arms', () => {
seq: 3,
content: [{ type: 'text', text: '<system-reminder>\nInstructions from: AGENTS.md\n</system-reminder>' }],
source: {
kind: 'workspace-instructions',
kind: 'agent-instructions',
form: 'instructions',
baseline: true,
changes: [
@@ -318,7 +318,7 @@ describe('MessageItem arms', () => {
seq: 3,
content: [{ type: 'text', text: 'delta' }],
source: {
kind: 'workspace-instructions',
kind: 'agent-instructions',
form: 'instructions',
changes: [
{ action: 'set', scope: 'a', path: 'new/AGENTS.md' },
@@ -433,13 +433,13 @@ describe('MessageItem arms', () => {
kind: 'context',
seq: 3,
content: [{ type: 'text', text: 'instruction prose' }],
source: { kind: 'workspace-instructions', form: 'instructions', changes: [{ action: 'set' }] },
provenance: { role: 'inject', label: 'workspace-instructions' },
source: { kind: 'agent-instructions', form: 'instructions', changes: [{ action: 'set' }] },
provenance: { role: 'inject', label: 'agent-instructions' },
form: 'instructions',
} as never}
/>,
)
fireEvent.click(view.getByRole('button', { name: /^上下文注入\s*workspace-instructions$/ }))
fireEvent.click(view.getByRole('button', { name: /^上下文注入\s*agent-instructions$/ }))
expect(view.container.querySelector('[data-context-files]')).toBeNull()
expect(view.container.querySelector('[data-context-text]')?.textContent).toBe('instruction prose')
expect(view.container.querySelector('[data-context-fields]')).not.toBeNull()
@@ -558,13 +558,13 @@ describe('MessageItem arms', () => {
kind: 'context',
seq: 3,
content: [{ type: 'text', text: 'instruction prose' }],
source: { kind: 'workspace-instructions', form: 'instructions', changes: [{ action: 'merge', path: 'A.md' }] },
provenance: { role: 'inject', label: 'workspace-instructions' },
source: { kind: 'agent-instructions', form: 'instructions', changes: [{ action: 'merge', path: 'A.md' }] },
provenance: { role: 'inject', label: 'agent-instructions' },
form: 'instructions',
} as never}
/>,
)
fireEvent.click(view.getByRole('button', { name: /^上下文注入\s*workspace-instructions$/ }))
fireEvent.click(view.getByRole('button', { name: /^上下文注入\s*agent-instructions$/ }))
expect(view.container.querySelector('[data-context-files]')).toBeNull()
expect(view.container.querySelector('[data-context-text]')?.textContent).toBe('instruction prose')
})
@@ -613,9 +613,9 @@ describe('MessageItem arms', () => {
<MessageItem t={t} node={{
kind: 'context',
seq: 3,
content: [{ type: 'text', text: 'background task bash-1 finished.' }],
source: { kind: 'plugin', plugin: 'tool-tasks', form: 'notice', summary: 'bash pnpm test [status: completed]' },
provenance: { role: 'inject', label: 'tool-tasks' },
content: [{ type: 'text', text: 'background job bash-1 finished.' }],
source: { kind: 'plugin', plugin: 'tool-jobs', form: 'notice', summary: 'bash pnpm test [status: completed]' },
provenance: { role: 'inject', label: 'tool-jobs' },
form: 'notice',
} as never}
/>,
@@ -629,14 +629,14 @@ describe('MessageItem arms', () => {
const view = render(
<MessageItem t={t} node={{
kind: 'context', seq: 3, content: [{ type: 'text', text: 'notice prose' }],
source: { kind: 'plugin', plugin: 'tool-tasks', form: 'notice' },
provenance: { role: 'inject', label: 'tool-tasks' },
source: { kind: 'plugin', plugin: 'tool-jobs', form: 'notice' },
provenance: { role: 'inject', label: 'tool-jobs' },
form: 'notice',
} as never}
/>,
)
expect(view.container.querySelector('[data-context-summary]')).toBeNull()
fireEvent.click(view.getByRole('button', { name: /^上下文注入\s*tool-tasks$/ }))
fireEvent.click(view.getByRole('button', { name: /^上下文注入\s*tool-jobs$/ }))
expect(view.container.querySelector('[data-context-fields]')).not.toBeNull()
})
@@ -137,7 +137,7 @@ const compaction = (over: Partial<CompactionSummaryNode> = {}): CompactionSummar
/** Empty sessions-list hook for the global standard-kit seat. */
function emptySessions() {
const store = createSnapshotStore<SessionListState>(
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined })
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined })
return bindSnapshotSelector(store)
}
@@ -588,12 +588,12 @@ describe('built-in conversation node Definitions', () => {
name: 'compact',
source: { kind: 'user' },
}),
at(11, 'compact/start', {
at(11, 'compaction/start', {
compactionId: 'manual-1',
sourceCommandId: 'command-1',
turn: null,
}),
at(12, 'compact/summary', {
at(12, 'compaction/summary', {
compactionId: 'manual-1',
sourceCommandId: 'command-1',
summary: [{ type: 'text', text: 'manual summary' }],
@@ -609,7 +609,7 @@ describe('built-in conversation node Definitions', () => {
sourceCommandId: 'command-1',
},
}, { surfaceOp: { op: 'replace', start: 1, end: 2 } }),
at(14, 'compact/end', {
at(14, 'compaction/end', {
compactionId: 'manual-1',
sourceCommandId: 'command-1',
turn: null,
@@ -619,8 +619,8 @@ describe('built-in conversation node Definitions', () => {
kind: 'success',
sourceEventSeq: 12,
}),
at(20, 'compact/start', { compactionId: 'automatic-1', turn: null }),
at(21, 'compact/summary', {
at(20, 'compaction/start', { compactionId: 'automatic-1', turn: null }),
at(21, 'compaction/summary', {
compactionId: 'automatic-1',
summary: [{ type: 'text', text: 'automatic summary' }],
shadowedSeqs: [3, 4],
@@ -630,7 +630,7 @@ describe('built-in conversation node Definitions', () => {
...textMessage('automatic-checkpoint', 'checkpoint'),
source: { kind: 'plugin', plugin: 'compact', compactionId: 'automatic-1' },
}, { surfaceOp: { op: 'replace', start: 3, end: 4 } }),
at(23, 'compact/end', { compactionId: 'automatic-1', turn: null }),
at(23, 'compaction/end', { compactionId: 'automatic-1', turn: null }),
])
const manual = node(snapshot(compactions), 'manual-compaction')
@@ -654,8 +654,8 @@ describe('built-in conversation node Definitions', () => {
expect(before?.data).toMatchObject({ summary: null, summaryEventSeq: null })
value.prepend([
at(9, 'compact/start', { compactionId: 'compact-1', turn: null }),
at(10, 'compact/summary', {
at(9, 'compaction/start', { compactionId: 'compact-1', turn: null }),
at(10, 'compaction/summary', {
compactionId: 'compact-1',
summary: [
{ type: 'text', text: 'older ' },
@@ -680,7 +680,7 @@ describe('built-in conversation node Definitions', () => {
it('renders a historical compaction when its start remains outside the loaded window', () => {
const value = assembler([
at(10, 'compact/summary', {
at(10, 'compaction/summary', {
compactionId: 'compact-windowed',
summary: [{ type: 'text', text: 'loaded summary' }],
shadowedSeqs: [1, 2, 3],
@@ -702,10 +702,10 @@ describe('built-in conversation node Definitions', () => {
it('ignores legacy compaction transactions without correlation ids', () => {
const value = assembler([
at(10, 'compact/start', { turn: null }),
at(11, 'compact/end', { turn: null, error: 'This operation was aborted' }),
at(20, 'compact/start', { turn: null }),
at(21, 'compact/summary', {
at(10, 'compaction/start', { turn: null }),
at(11, 'compaction/end', { turn: null, error: 'This operation was aborted' }),
at(20, 'compaction/start', { turn: null }),
at(21, 'compaction/summary', {
summary: [{ type: 'text', text: 'legacy summary' }],
shadowedSeqs: [1, 2, 3],
shadowedTokenCount: 42,
@@ -714,7 +714,7 @@ describe('built-in conversation node Definitions', () => {
...textMessage('legacy-checkpoint', 'checkpoint'),
source: { kind: 'plugin', plugin: 'compact' },
}, { surfaceOp: { op: 'replace', start: 1, end: 3 } }),
at(23, 'compact/end', { turn: null }),
at(23, 'compaction/end', { turn: null }),
], true)
expect(node(snapshot(value), 'compaction')).toBeUndefined()
@@ -897,7 +897,7 @@ describe('built-in conversation node Definitions', () => {
step: 1,
message: toolResult('root', 'root result'),
}, { surfaceOp: 'append' }),
at(20, 'compact/summary', {
at(20, 'compaction/summary', {
compactionId: 'manual-1',
sourceCommandId: 'command-1',
summary: [{ type: 'text', text: 'manual summary' }],
@@ -16,7 +16,7 @@ afterEach(() => {
function emptySessions() {
return bindSnapshotSelector(createSnapshotStore<SessionListState>({
ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined,
ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined,
}))
}
@@ -111,7 +111,7 @@ describe('render branch tails', () => {
const chat = createChatStore().create()
chat.actions.select({ turnSeq: 1, callId: 'ghost' } satisfies SelectionTarget)
const emptyList = createSnapshotStore<SessionListState>(
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined })
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined })
const emptyWorkspaces = createSnapshotStore<WorkspaceListState>({
items: [], archivedSessionIds: [], state: 'idle', phase: 'ready', error: null,
baselinesReady: true, recentWorkspaceId: undefined,
@@ -167,7 +167,7 @@ describe('render branch tails', () => {
const chat = createChatStore().create()
chat.actions.select({ turnSeq: 9, callId: 'p1:code:1:code:1', toolName: 'read' } satisfies SelectionTarget)
const emptyList = createSnapshotStore<SessionListState>(
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined })
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined })
const emptyWorkspaces = createSnapshotStore<WorkspaceListState>({
items: [], archivedSessionIds: [], state: 'idle', phase: 'ready', error: null,
baselinesReady: true, recentWorkspaceId: undefined,
@@ -1,11 +1,11 @@
import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest'
import { Settings, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
import { SettingsProvider, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
import {
CONVERSATION_SETTINGS_NAMESPACE, DEFAULT_BUSY_ENTER_BEHAVIOR, apply,
} from '@deepseek-ai/dsh-client-ui-conversation'
class MemorySettings extends Settings {
class MemorySettings extends SettingsProvider {
readonly writable = true
protected load(): Promise<Record<string, unknown>> { return Promise.resolve({}) }
protected persist(_ns: SettingsNamespace, _section: Record<string, unknown>): Promise<void> {
@@ -123,9 +123,9 @@ function bench(over?: BenchOptions) {
// never reached — these benches drive plain-draft flows only).
...(lex !== undefined
? {
slash: (() => ({
inputTriggers: (() => ({
lexicon: { getSnapshot: () => lex, subscribe: () => () => {} },
})) as unknown as NonNullable<ShellDeps['slash']>,
})) as unknown as NonNullable<ShellDeps['inputTriggers']>,
}
: {}),
})
@@ -147,7 +147,7 @@ function bench(over?: BenchOptions) {
useSession: bindSnapshotSelector(session),
useSessions: bindSnapshotSelector(createSnapshotStore({
ids: [], byId: {}, current: undefined, phase: 'ready',
subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined,
subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined,
})),
useWorkspaces: bindSnapshotSelector(createSnapshotStore({
items: [], archivedSessionIds: [], state: 'idle', phase: 'ready', error: null,
@@ -8,7 +8,7 @@
* Pure event sequences — no React, no DOM, no ambient clock.
*/
import { describe, expect, it } from 'vitest'
import type { CommandClaim, ReferenceInsert, TokenSpan } from '@deepseek-ai/dsh-client-ui-slash/client'
import type { CommandClaim, ReferenceInsert, TokenSpan } from '@deepseek-ai/dsh-client-ui-input-trigger/client'
import type { InputEffect, SubmitAttempt } from '../src/client/input/contract.ts'
import { InputMachine, PLACEHOLDER, projectClipboard } from '../src/client/input/machine.ts'
import { deriveDecorations, scanTextRefs } from '../src/client/input/decorations.ts'
@@ -12,7 +12,7 @@ import {
createSnapshotStore, EMPTY_CHAT_SNAPSHOT, EMPTY_CONVERSATION_VIEWS,
} from '@deepseek-ai/dsh-client-runtime/client'
import type { ClientContext, ConversationSnapshot, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
import type { SubmitOutcome } from '@deepseek-ai/dsh-client-ui-slash/client'
import type { SubmitOutcome } from '@deepseek-ai/dsh-client-ui-input-trigger/client'
import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
import { zh as commonZh } from '@deepseek-ai/dsh-client-locale/src/locales/zh.ts'
import { SessionInputShell } from '../src/client/input/facade.ts'
@@ -40,7 +40,7 @@ function mountBar(shell: SessionInputShell, over?: { running?: boolean; disabled
useSession: bindSnapshotSelector(session),
useSessions: bindSnapshotSelector(createSnapshotStore({
ids: [], byId: {}, current: undefined, phase: 'ready',
subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined,
subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined,
})),
useWorkspaces: bindSnapshotSelector(createSnapshotStore({
items: [], archivedSessionIds: [], state: 'idle', phase: 'ready', error: null,
@@ -1,21 +1,21 @@
// @vitest-environment jsdom
/**
* Scenario-chain integration (scenarios A/C/D/H/I): the real per-session
* SlashController pipeline over a real session scope (SessionsService over
* InputTriggerController pipeline over a real session scope (SessionRuntime over
* a listed host session) + a command source implementing the decision
* table's relevant cells + the real SessionInput machine (scoped-event
* listeners wired the way the hub does) + the real InputBar. ui-command
* listeners wired the way the hub does) + the real InputBar. ui-commands
* itself is not a dependency of this package; the source below is the
* decision-table contract at the `SlashSource` boundary.
* decision-table contract at the `InputTriggerSource` boundary.
*/
import { Context } from '@deepseek-ai/cordis'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { act, cleanup, fireEvent, render } from '@testing-library/react'
import {
EMPTY_CHAT_SNAPSHOT, EMPTY_CONVERSATION_VIEWS, SessionsService,
EMPTY_CHAT_SNAPSHOT, EMPTY_CONVERSATION_VIEWS, SessionRuntime,
} from '@deepseek-ai/dsh-client-runtime/client'
import { SlashService } from '@deepseek-ai/dsh-client-ui-slash/client'
import type { ClientSessionContext, CommandClaim, PickOutcome, SubmitOutcome } from '@deepseek-ai/dsh-client-ui-slash/client'
import { InputTriggerService } from '@deepseek-ai/dsh-client-ui-input-trigger/client'
import type { ClientSessionContext, CommandClaim, PickOutcome, SubmitOutcome } from '@deepseek-ai/dsh-client-ui-input-trigger/client'
import { FakeApiClient, fakeRemote, ok } from '../../runtime/tests/fake-api.client.ts'
import { makeTranslate } from '@deepseek-ai/dsh-client-test-runtime'
import { zh as commonZh } from '@deepseek-ai/dsh-client-locale/src/locales/zh.ts'
@@ -89,25 +89,25 @@ const COMMANDS: FakeCommand[] = [
{ name: 'compact', description: '压缩上下文' },
]
/** Real scope bench: SessionsService over one listed session + SlashController + shell listeners (the hub wiring shape). */
async function scopedBench(register?: (slash: SlashService) => void) {
/** Real scope bench: SessionRuntime over one listed session + InputTriggerController + shell listeners (the hub wiring shape). */
async function scopedBench(register?: (inputTriggers: InputTriggerService) => void) {
const ctx = new Context()
const api = new FakeApiClient()
api.onWorkspaceList = () => Promise.resolve(ok({ items: [] }))
const sessionId = 'scenario-s1' as Parameters<SessionsService['open']>[0]
const sessionId = 'scenario-s1' as Parameters<SessionRuntime['open']>[0]
api.onList = () => Promise.resolve(ok({
items: [{ sessionId, updatedAt: 1, running: false, blank: false, cwd: '/w/a' }],
}) as never)
const sessions = new SessionsService(ctx, api, fakeRemote()) // provides 'sessions' itself
const sessions = new SessionRuntime(ctx, api, fakeRemote()) // provides 'sessions' itself
await sessions.refresh()
await Promise.resolve() // manager notifier flush
await ctx.plugin(SlashService).await()
const slash = ctx.get('slash') as SlashService
register?.(slash)
await ctx.plugin(InputTriggerService).await()
const inputTriggers = ctx.get('inputTriggers') as InputTriggerService
register?.(inputTriggers)
const actx = sessions.scope(sessionId)!
const controller = slash.sessionOf(actx)
const controller = inputTriggers.sessionOf(actx)
const sink = vi.fn()
const shell = new SessionInputShell({ actx, slash: () => controller, defaultSink: sink })
const shell = new SessionInputShell({ actx, inputTriggers: () => controller, defaultSink: sink })
// The hub's listener wiring, verbatim.
actx.on('slash/input-begin-command', req => shell.beginCommand(req.claim, req.span) ? true : undefined)
actx.on('slash/input-insert-reference', req => shell.insertReference(req.reference, req.span) ? true : undefined)
@@ -126,7 +126,7 @@ async function scopedBench(register?: (slash: SlashService) => void) {
useSession: bindSnapshotSelector(sessionStore),
useSessions: bindSnapshotSelector(createSnapshotStore({
ids: [], byId: {}, current: undefined, phase: 'ready',
subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined,
subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined,
})),
useWorkspaces: bindSnapshotSelector(createSnapshotStore({
items: [], archivedSessionIds: [], state: 'idle', phase: 'ready', error: null,
@@ -164,14 +164,14 @@ async function scopedBench(register?: (slash: SlashService) => void) {
const type = (text: string): void => {
fireEvent.change(textarea, { target: { value: text } })
}
return { ctx, slash, controller, shell, wiring, view, textarea, type, sink }
return { ctx, inputTriggers, controller, shell, wiring, view, textarea, type, sink }
}
async function bench(executeImpl?: (line: string) => Promise<SubmitOutcome>) {
const execute = vi.fn(executeImpl ?? ((line: string) =>
Promise.resolve({ kind: 'success' as const, text: `已执行 ${line}` })))
const { source, executed } = commandSource(COMMANDS, execute)
const base = await scopedBench((slash) => { slash.registerSource(source) })
const base = await scopedBench((inputTriggers) => { inputTriggers.registerSource(source) })
return { ...base, execute, executed }
}
@@ -266,8 +266,8 @@ describe('scenario: reference decoration lights up when the lexicon settles', ()
it('a typed /name token gains the text-ref mark without further input once the roll goes hot', async () => {
let roll: readonly string[] | undefined
let notify: (() => void) | undefined
const b = await scopedBench((slash) => {
slash.registerSource({
const b = await scopedBench((inputTriggers) => {
inputTriggers.registerSource({
trigger: '/', name: 'skill',
candidates: () => Promise.resolve([]),
onPick: () => undefined,
@@ -302,8 +302,8 @@ describe('scenario I: unknown /xyz + enter', () => {
})
it('adjudication failure (source warmup throw) notices and keeps the draft', async () => {
const b = await scopedBench((slash) => {
slash.registerSource({
const b = await scopedBench((inputTriggers) => {
inputTriggers.registerSource({
trigger: '/', name: 'command',
candidates: () => Promise.resolve([]),
onPick: () => undefined,
@@ -1,6 +1,6 @@
// @vitest-environment jsdom
/**
* Exercises selection persistence through the real SlotsService store axis;
* Exercises selection persistence through the real SlotRegistry store axis;
* component stubs cannot prove per-session identity or disposal.
*/
import { beforeEach, describe, expect, it } from 'vitest'
@@ -96,7 +96,7 @@ describe('selection survives on the store seat', () => {
expect(localStorage.getItem('dsh.conversation.chat.s1')).not.toBeNull()
// TestSessions.remove drives the same public slot lifecycle contract the
// production SessionsService calls when the scope dies (pruneStoreScope).
// production SessionRuntime calls when the scope dies (pruneStoreScope).
await b.runtime.sessions.remove('s1')
// Persisted residue is gone with the session...
@@ -1,5 +1,5 @@
// @vitest-environment jsdom
// ConversationService scope addressing over the runtime's real scope tag:
// ConversationController scope addressing over the runtime's real scope tag:
// TestSessions mints tagged scopes through the production createScope, so the
// service's scopeOf/binding path runs against production resolution (no local
// tag probe).
@@ -10,7 +10,7 @@ import { makeTranslate, SlotTestRuntime } from '@deepseek-ai/dsh-client-test-run
import type { QueuedMessage, SessionFace } from '@deepseek-ai/dsh-client-runtime/client'
import { ComposerBlockRegistry } from '../src/client/input/blocks.ts'
import { InputHub } from '../src/client/input/hub.ts'
import { ConversationService, UnsupportedImageMediaTypeError } from '../src/client/service.ts'
import { ConversationController, UnsupportedImageMediaTypeError } from '../src/client/service.ts'
import { zh } from '../src/client/locales.ts'
async function bench(readAttachment?: SessionFace['readAttachment']) {
@@ -26,18 +26,18 @@ async function bench(readAttachment?: SessionFace['readAttachment']) {
// config.input is required (the apply shares its hub with the inject
// factories); the bench passes its own instance explicitly.
const hub = new InputHub(runtime.ctx, makeTranslate(zh, {}))
const fiber = runtime.ctx.plugin(ConversationService, {
const fiber = runtime.ctx.plugin(ConversationController, {
input: hub,
blocks: new ComposerBlockRegistry(),
})
await fiber.await()
const root = runtime.ctx.get('conversation') as ConversationService
const scoped = runtime.sessions.scope('s1')!.get('conversation') as ConversationService
const root = runtime.ctx.get('conversation') as ConversationController
const scoped = runtime.sessions.scope('s1')!.get('conversation') as ConversationController
const shell = hub.shellFor(runtime.sessions.binding('s1')!)
return { runtime, fiber, root, scoped, hub, shell, prompt, updateQueue, cancel, loadOlder }
}
describe('ConversationService', () => {
describe('ConversationController', () => {
it('routes operations through the public Session binding', async () => {
const b = await bench()
await b.scoped.send('hello')
@@ -129,19 +129,19 @@ describe('ConversationService', () => {
await b.runtime.dispose()
})
it('fails loudly from the root scope, on an unbound session, or without SessionsService', async () => {
it('fails loudly from the root scope, on an unbound session, or without SessionRuntime', async () => {
const b = await bench()
await expect(b.root.send('x')).rejects.toThrow(/requires a session scope/)
await b.runtime.sessions.remove('s1')
await expect(b.scoped.send('x')).rejects.toThrow(/resolved no binding/)
await b.runtime.dispose()
// No SessionsService at all: a bare context (the runtime always provides one).
// No SessionRuntime at all: a bare context (the runtime always provides one).
const bare = new Context()
await bare.plugin(ConversationService, {
await bare.plugin(ConversationController, {
input: new InputHub(bare, makeTranslate(zh, {})),
blocks: new ComposerBlockRegistry(),
}).await()
const orphan = bare.get('conversation') as ConversationService
const orphan = bare.get('conversation') as ConversationController
await expect(orphan.send('x')).rejects.toThrow(/sessions service unavailable/)
})
})
@@ -110,7 +110,7 @@ function mount(
ids: listed ? [root, SID] : [root],
byId: { [root]: rootRow, ...listed && { [SID]: childRow } },
current: SID,
phase: 'ready', subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined,
phase: 'ready', subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined,
})
const workspaces = createSnapshotStore<WorkspaceListState>(workspaceState(workspaceRows))
const session = createSnapshotStore<ConversationSnapshot>(snapshot)
@@ -4,12 +4,12 @@
import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest'
import type { ReactNode } from 'react'
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
import { SlotRegistry } from '@deepseek-ai/dsh-client-runtime/client'
import type { ChatViewSlotProps, ConvViewProps } from '../src/client/contract/slots.ts'
describe('view-ring type negatives (compile-time; body never runs)', () => {
it('holds the negative samples as expect-error sites', () => {
const negatives = (slots: SlotsService) => {
const negatives = (slots: SlotRegistry) => {
// 1. List-kind registration requires the id shape field.
// @ts-expect-error missing `id` on a list-slot registration
slots.register({ name: 'conversation.view', order: 1 }, (_p: ConvViewProps) => null)
@@ -58,7 +58,7 @@ describe('view-ring type negatives (compile-time; body never runs)', () => {
describe('view-ring runtime dual (real ledger)', () => {
function bench() {
const ctx = new Context()
const slots = new SlotsService(ctx)
const slots = new SlotRegistry(ctx)
// The conversation entry's role: declare the ring (declaring is claiming).
slots.register({
name: 'root',