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:
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+15
-15
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user