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:
@@ -18,7 +18,7 @@ import type {
|
||||
} from './contract/slots.ts'
|
||||
import type { InputNotice } from './input/contract.ts'
|
||||
import { createChatStore } from './stores.ts'
|
||||
import { ConversationService, UnsupportedImageMediaTypeError } from './service.ts'
|
||||
import { ConversationController, UnsupportedImageMediaTypeError } from './service.ts'
|
||||
import type { IConversation } from './service.ts'
|
||||
import { ComposerBlockRegistry } from './input/blocks.ts'
|
||||
import type { ComposerBlock } from './input/blocks.ts'
|
||||
@@ -98,8 +98,8 @@ function scopedConversation(sessions: ISessions, id: SessionId): IConversation {
|
||||
}
|
||||
|
||||
/** Resolve package-internal attachment operations from the public service registration. */
|
||||
function concreteConversation(ctx: Context): ConversationService {
|
||||
const conversation = ctx.get('conversation') as ConversationService | undefined
|
||||
function concreteConversation(ctx: Context): ConversationController {
|
||||
const conversation = ctx.get('conversation') as ConversationController | undefined
|
||||
if (conversation === undefined) throw new Error('ui-conversation: conversation service unavailable')
|
||||
return conversation
|
||||
}
|
||||
@@ -165,12 +165,12 @@ export function apply(ctx: Context): void {
|
||||
version: () => slots.getVersion('conversation.view'),
|
||||
}
|
||||
|
||||
// The per-session input machine registry (InputService face; published as
|
||||
// The per-session input machine registry (SessionInputResolver face; published as
|
||||
// ctx.conversation.input by the service below sharing this one instance).
|
||||
const inputHub = new InputHub(ctx, t)
|
||||
|
||||
// The composer-block registry: a plugin that knows a session cannot send —
|
||||
// ui-model, when no adapter serves the session's route — raises a block
|
||||
// ui-model-selection, when no adapter serves the session's route — raises a block
|
||||
// here, and the bar reads its own session's store. It cannot flow the other
|
||||
// way: this package must not import the plugins that would know.
|
||||
const composerBlocks = new ComposerBlockRegistry()
|
||||
@@ -302,7 +302,7 @@ export function apply(ctx: Context): void {
|
||||
}
|
||||
const conversation = concreteConversation(ctx)
|
||||
const shell = inputHub.shell(sessionId)
|
||||
const slash = inputHub.slash(sessionId)
|
||||
const inputTriggers = inputHub.inputTriggers(sessionId)
|
||||
return {
|
||||
keyboard: shell,
|
||||
addImages: (files) => {
|
||||
@@ -328,12 +328,12 @@ export function apply(ctx: Context): void {
|
||||
draftImages: ids => conversation.draftImages(ids),
|
||||
resolveSubmitMode: (running, gesture, steeringAvailable) =>
|
||||
submissionPolicy.resolve(running, gesture, steeringAvailable),
|
||||
toggleCommandMenu: slash === undefined
|
||||
toggleCommandMenu: inputTriggers === undefined
|
||||
? undefined
|
||||
: (selection) => {
|
||||
shell.dismissPopup()
|
||||
const snapshot = shell.snapshot
|
||||
slash.toggleSource('command', {
|
||||
inputTriggers.toggleSource('command', {
|
||||
trigger: '/',
|
||||
query: '',
|
||||
position: snapshot.draft.slice(0, selection.start).trim() === '' ? 'leading' : 'inline',
|
||||
@@ -354,14 +354,14 @@ export function apply(ctx: Context): void {
|
||||
hooks: {
|
||||
notices: shell.notices,
|
||||
lexicon: shell.lexicon,
|
||||
menuLauncher: slash?.launcher ?? ABSENT_MENU_LAUNCHER,
|
||||
menuLauncher: inputTriggers?.launcher ?? ABSENT_MENU_LAUNCHER,
|
||||
},
|
||||
}
|
||||
},
|
||||
}, InputBar)
|
||||
|
||||
// The approval takeover: a selector-routed entry of the chain this package
|
||||
// just declared (the ui-question registration pattern; the entry lives here
|
||||
// just declared (the ui-user-questions registration pattern; the entry lives here
|
||||
// because approval answering is core conversation UX, not an optional tool).
|
||||
// Zero business face — data and verbs both ride the matched carrier.
|
||||
// priority 1: question takeovers (default 0) win when both kinds are
|
||||
@@ -432,7 +432,7 @@ export function apply(ctx: Context): void {
|
||||
// registers itself as `conversation` and lives on its own child fiber.
|
||||
// Presentation registrants depend directly on their slot declarations;
|
||||
// this service remains only where conversation actions are required.
|
||||
ctx.plugin(ConversationService, { input: inputHub, blocks: composerBlocks })
|
||||
ctx.plugin(ConversationController, { input: inputHub, blocks: composerBlocks })
|
||||
|
||||
// The plan strip rides the input dock above the queue rows (same posture).
|
||||
ctx.plugin(todoDockEntry)
|
||||
|
||||
@@ -395,7 +395,7 @@ export function ChatView({
|
||||
t={t}
|
||||
/>
|
||||
))}
|
||||
{/* No pending placeholders: questions (ui-question) and approvals
|
||||
{/* No pending placeholders: questions (ui-user-questions) and approvals
|
||||
(ApprovalPanel) both take over the composer, so a flow card would
|
||||
double-render the same wait. */}
|
||||
{/* Turn-level loading signal: rides the whole running turn (first-token
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// marker reports where the model stopped seeing that history — it never
|
||||
// replaces it. The framed checkpoint payload is written for the model and is
|
||||
// not rendered; the disclosure shows the summary from the checkpoint's own
|
||||
// cited `compact/summary` event, and a window cut that left that event outside makes the row
|
||||
// cited `compaction/summary` event, and a window cut that left that event outside makes the row
|
||||
// non-expandable rather than empty.
|
||||
|
||||
import { memo, useState } from 'react'
|
||||
|
||||
@@ -118,10 +118,10 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
* the next one rather than applied to a current one.
|
||||
*/
|
||||
'conversation.hero.agentPreset': { kind: 'single'; scope: 'root'; owner: HeroAgentPresetOwnerProps }
|
||||
// 'conversation.input.overlay' merges in ui-slash (the dependency
|
||||
// direction is the hard constraint — ui-slash cannot import
|
||||
// 'conversation.input.overlay' merges in ui-input-trigger (the dependency
|
||||
// direction is the hard constraint — ui-input-trigger cannot import
|
||||
// this package, while this package's input contract already imports
|
||||
// ui-slash, so the type arrives transitively). The runtime declaration
|
||||
// ui-input-trigger, so the type arrives transitively). The runtime declaration
|
||||
// (children table in apply.ts) stays here with the other input slots.
|
||||
/**
|
||||
* Stacked strip above the input (queue rows / GoalBar / attachments;
|
||||
@@ -446,7 +446,7 @@ export interface ComposerBarInjected {
|
||||
gesture: ComposerSubmitGesture,
|
||||
steeringAvailable: boolean,
|
||||
) => InputSubmitMode
|
||||
/** Toggle the shared slash menu with only its command source; absent without ui-slash or a session. */
|
||||
/** Toggle the shared slash menu with only its command source; absent without ui-input-trigger or a session. */
|
||||
toggleCommandMenu: ((selection: EditSelection) => void) | undefined
|
||||
/** Cancel the in-flight turn; absent with the session. */
|
||||
stop: (() => void) | undefined
|
||||
@@ -539,7 +539,7 @@ export type ConversationSessionHeaderSlotProps =
|
||||
export type ApprovalWait = PendingWait<'approval'>
|
||||
|
||||
/**
|
||||
* Approval domain face over the carrier (the ui-question PendingQuestion
|
||||
* Approval domain face over the carrier (the ui-user-questions PendingQuestion
|
||||
* pattern): render identity and question material forwarded transparently;
|
||||
* answer owns the wire encoding — the ApprovalResponsePayload value shape
|
||||
* with the audit correlation the host reconciles — and turns a rejected
|
||||
|
||||
@@ -4,8 +4,8 @@ import type {
|
||||
ConversationNodeDefinition,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { isReplacementSurfaceEvent } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { CompactCheckpointSource } from '@deepseek-ai/dsh-compact/checkpoint'
|
||||
import type {} from '@deepseek-ai/dsh-compact/types'
|
||||
import type { CompactionCheckpointSource } from '@deepseek-ai/dsh-compaction/checkpoint'
|
||||
import type {} from '@deepseek-ai/dsh-compaction/types'
|
||||
import type {} from '@deepseek-ai/dsh-commands/types'
|
||||
import type { ManualCompactionChatData } from '../contract/chat-nodes.ts'
|
||||
import { chatNode } from './common.ts'
|
||||
@@ -21,7 +21,7 @@ declare module '@deepseek-ai/dsh-client-ui-conversation/client' {
|
||||
|
||||
type CommandId = CommandNode['commandId']
|
||||
|
||||
const COMPACT_PLUGIN: CompactCheckpointSource['plugin'] = 'compact'
|
||||
const COMPACT_PLUGIN: CompactionCheckpointSource['plugin'] = 'compact'
|
||||
|
||||
interface CommandState {
|
||||
readonly command: CommandNode
|
||||
@@ -96,7 +96,7 @@ function compactSource(event: Parameters<ConversationNodeDefinition['match']>[0]
|
||||
|
||||
/**
|
||||
* Build the visible summary marker from optional lifecycle evidence.
|
||||
* @param match - compact/summary Match, when loaded.
|
||||
* @param match - compaction/summary Match, when loaded.
|
||||
* @param checkpoint - replacement checkpoint Match.
|
||||
* @returns final compaction summary Node data.
|
||||
*/
|
||||
@@ -104,7 +104,7 @@ function compactSummary(match: ConversationMatch | undefined, checkpoint: Conver
|
||||
let summary: string | null = null
|
||||
let shadowedItemCount: number | null = null
|
||||
let shadowedTokenCount: number | null = null
|
||||
if (match?.event.type === 'compact/summary') {
|
||||
if (match?.event.type === 'compaction/summary') {
|
||||
const data = match.event.data
|
||||
if (Array.isArray(data.summary)) {
|
||||
const text = data.summary
|
||||
@@ -135,7 +135,7 @@ function compactSummary(match: ConversationMatch | undefined, checkpoint: Conver
|
||||
function fallbackState(context: ConversationNodeContext<CommandState>): CommandState | undefined {
|
||||
const done = context.matches.find(match => match.event.type === 'command/done')
|
||||
const checkpoint = context.matches.find(match => compactSource(match.event) !== undefined)
|
||||
const summary = context.matches.find(match => match.event.type === 'compact/summary')
|
||||
const summary = context.matches.find(match => match.event.type === 'compaction/summary')
|
||||
if (checkpoint === undefined) return done === undefined ? undefined : { command: commandFromDone(done) }
|
||||
const source = compactSource(checkpoint.event)
|
||||
if (source?.sourceCommandId === undefined) return done === undefined ? undefined : { command: commandFromDone(done) }
|
||||
@@ -167,7 +167,7 @@ export function updateCompactionState<State extends CompactionEvidence>(
|
||||
state: State,
|
||||
match: ConversationMatch,
|
||||
): State {
|
||||
if (match.event.type === 'compact/summary') return { ...state, summary: match }
|
||||
if (match.event.type === 'compaction/summary') return { ...state, summary: match }
|
||||
if (compactSource(match.event) !== undefined) return { ...state, checkpoint: match }
|
||||
return state
|
||||
}
|
||||
@@ -187,9 +187,9 @@ export const commandDefinition: ConversationNodeDefinition<CommandState> = {
|
||||
if (checkpoint?.sourceCommandId !== undefined) {
|
||||
return { id: String(checkpoint.sourceCommandId), role: 'update' }
|
||||
}
|
||||
if (event.type === 'compact/start'
|
||||
|| event.type === 'compact/summary'
|
||||
|| event.type === 'compact/end') {
|
||||
if (event.type === 'compaction/start'
|
||||
|| event.type === 'compaction/summary'
|
||||
|| event.type === 'compaction/end') {
|
||||
if (event.data.sourceCommandId !== undefined) {
|
||||
return { id: String(event.data.sourceCommandId), role: 'update' }
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {
|
||||
CompactionSummaryNode, ConversationMatch, ConversationNodeContext, ConversationNodeDefinition,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {} from '@deepseek-ai/dsh-compact/types'
|
||||
import type {} from '@deepseek-ai/dsh-compaction/types'
|
||||
import { chatNode } from './common.ts'
|
||||
import { compactSource, compactSummary, updateCompactionState } from './command.ts'
|
||||
|
||||
@@ -19,7 +19,7 @@ interface CompactionState {
|
||||
}
|
||||
|
||||
function fallbackState(context: ConversationNodeContext<CompactionState>): CompactionState {
|
||||
const summary = context.matches.find(match => match.event.type === 'compact/summary')
|
||||
const summary = context.matches.find(match => match.event.type === 'compaction/summary')
|
||||
const checkpoint = context.matches.find(match => compactSource(match.event) !== undefined)
|
||||
return {
|
||||
...summary === undefined ? {} : { summary },
|
||||
@@ -36,13 +36,13 @@ export const compactionDefinition: ConversationNodeDefinition<CompactionState> =
|
||||
if (checkpoint !== undefined && checkpoint.sourceCommandId === undefined) {
|
||||
return { id: checkpoint.compactionId, role: 'update' }
|
||||
}
|
||||
if (event.type === 'compact/start'
|
||||
|| event.type === 'compact/summary'
|
||||
|| event.type === 'compact/end') {
|
||||
if (event.type === 'compaction/start'
|
||||
|| event.type === 'compaction/summary'
|
||||
|| event.type === 'compaction/end') {
|
||||
if (event.data.sourceCommandId !== undefined) return null
|
||||
const compactionId: unknown = event.data.compactionId
|
||||
if (typeof compactionId !== 'string' || compactionId === '') return null
|
||||
return { id: compactionId, role: event.type === 'compact/start' ? 'start' : 'update' }
|
||||
return { id: compactionId, role: event.type === 'compaction/start' ? 'start' : 'update' }
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ export type {} from './conversation-nodes/turn-max-tokens.ts'
|
||||
export type {} from './conversation-nodes/turn-tail.ts'
|
||||
|
||||
export { apply, inject } from './apply.ts'
|
||||
export { ConversationService } from './service.ts'
|
||||
export { ConversationController } from './service.ts'
|
||||
export type { IConversation } from './service.ts'
|
||||
export type { DraftAttachmentId } from './input/contract.ts'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Composer blocks: the one way another plugin stops a session's input.
|
||||
*
|
||||
* The composer cannot read the plugins that would know — the dependency runs
|
||||
* ui-model → ui-conversation, never back — so a blocker pushes here and the
|
||||
* ui-model-selection → ui-conversation, never back — so a blocker pushes here and the
|
||||
* bar reads its own session's store. A block carries the localized reason it
|
||||
* exists, because the plugin that raised it owns that copy; the composer only
|
||||
* knows how to render an inert textarea with a placeholder, exactly as it
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Branded } from '@deepseek-ai/dsh-brand'
|
||||
import type {
|
||||
ArbitrateKey, ArbitrateOutcome, CommandClaim, ConsumeTokenRequest, PickOutcome,
|
||||
ReferenceInsert, SubmitOutcome, TokenSpan,
|
||||
} from '@deepseek-ai/dsh-client-ui-slash/client'
|
||||
} from '@deepseek-ai/dsh-client-ui-input-trigger/client'
|
||||
import type { QueueRow } from '../contract/queue.ts'
|
||||
import type { InputSubmitMode } from '../contract/composer-submission.ts'
|
||||
|
||||
@@ -47,7 +47,7 @@ export interface SessionInput extends InputTarget {
|
||||
/**
|
||||
* Surface a notice outside the machine's own effect stream: detached
|
||||
* command results and business notifications render through here.
|
||||
* Session-routed — resolving the facade via InputService.for(actx) lands
|
||||
* Session-routed — resolving the facade via SessionInputResolver.for(actx) lands
|
||||
* the notice on that session's composer, so a result arriving after a
|
||||
* session switch still reaches its own session.
|
||||
* @param level - severity tier.
|
||||
@@ -59,7 +59,7 @@ export interface SessionInput extends InputTarget {
|
||||
}
|
||||
|
||||
/** Session-addressed access to the per-session input facade. */
|
||||
export interface InputService {
|
||||
export interface SessionInputResolver {
|
||||
/** Resolve the facade for one session-scope ctx. */
|
||||
for(actx: ClientContext): SessionInput
|
||||
}
|
||||
@@ -208,7 +208,7 @@ export interface InputMachineOptions {
|
||||
/** Published input state (the currency; per-session). */
|
||||
export interface InputState {
|
||||
readonly draft: string
|
||||
/** Ordered runtime-only image ids; bytes and URLs stay in ConversationService. */
|
||||
/** Ordered runtime-only image ids; bytes and URLs stay in ConversationController. */
|
||||
readonly imageIds: readonly DraftAttachmentId[]
|
||||
/** Monotonic draft revision (span CAS compares against this). */
|
||||
readonly draftRev: number
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* SessionInput shell over the pure input machine: the sole machine caller
|
||||
* and effect executor. Owns the InputState store (machine state + the queue
|
||||
* overlay), the notice channel, and the submit transaction plumbing
|
||||
* (adjudicate via the session's SlashController; claim.submit; default
|
||||
* (adjudicate via the session's InputTriggerController; claim.submit; default
|
||||
* sink). Package-private; the hub alone constructs it and wires the scoped
|
||||
* event listeners onto it.
|
||||
*/
|
||||
@@ -10,8 +10,8 @@ import type { ClientContext, ObservableSnapshot, SnapshotStore } from '@deepseek
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type {
|
||||
ArbitrateKey, ArbitrateOutcome, CommandClaim, ConsumeTokenRequest, PickOutcome,
|
||||
ReferenceInsert, SlashController, TokenSpan,
|
||||
} from '@deepseek-ai/dsh-client-ui-slash/client'
|
||||
ReferenceInsert, InputTriggerController, TokenSpan,
|
||||
} from '@deepseek-ai/dsh-client-ui-input-trigger/client'
|
||||
import type {
|
||||
DraftAttachmentId, EditRange, EditSelection, InputActions, InputEffect, InputNotice, InputState,
|
||||
PasteComponent, QueuedMessage, SessionInput, SubmitAttempt,
|
||||
@@ -34,7 +34,7 @@ export interface SessionInputDeps {
|
||||
/** Session-scope ctx handed to claim.submit transactions. */
|
||||
actx: ClientContext
|
||||
/** Enter adjudication face resolver; absent/undefined answer = every '/' line falls to the default sink. */
|
||||
slash?: (() => SlashController | undefined) | undefined
|
||||
inputTriggers?: (() => InputTriggerController | undefined) | undefined
|
||||
/** PopupSelect shell face resolver (dismissal on submit lock / escape). */
|
||||
popup?: (() => PopupDismissFace | undefined) | undefined
|
||||
/** Queue read face; overlaid onto InputState.queue (absent = empty). */
|
||||
@@ -204,7 +204,7 @@ export class SessionInputShell implements SessionInput {
|
||||
const phase = this.snapshot.phase
|
||||
if (phase === 'adjudicating' || phase === 'submitting') {
|
||||
this.deps.popup?.()?.dismiss()
|
||||
this.deps.slash?.()?.track(this.snapshot.draft, 0, { tier: 'frozen' }, this.snapshot.draftRev)
|
||||
this.deps.inputTriggers?.()?.track(this.snapshot.draft, 0, { tier: 'frozen' }, this.snapshot.draftRev)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ export class SessionInputShell implements SessionInput {
|
||||
* @param caret - caret position in draft coordinates.
|
||||
*/
|
||||
track(draft: string, caret: number): void {
|
||||
this.deps.slash?.()?.track(draft, caret, { tier: guardOf(this.snapshot.phase) }, this.snapshot.draftRev)
|
||||
this.deps.inputTriggers?.()?.track(draft, caret, { tier: guardOf(this.snapshot.phase) }, this.snapshot.draftRev)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,7 +225,7 @@ export class SessionInputShell implements SessionInput {
|
||||
* @returns the menu's verdict; 'pass' when no pipeline is mounted.
|
||||
*/
|
||||
arbitrate(key: ArbitrateKey, composing: boolean): ArbitrateOutcome {
|
||||
return this.deps.slash?.()?.arbitrate(key, composing) ?? 'pass'
|
||||
return this.deps.inputTriggers?.()?.arbitrate(key, composing) ?? 'pass'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,15 +243,15 @@ export class SessionInputShell implements SessionInput {
|
||||
* @returns true = a claim/insert was applied — the caller preventDefaults.
|
||||
*/
|
||||
space(): boolean {
|
||||
const slash = this.deps.slash?.()
|
||||
if (slash === undefined) return false
|
||||
const consumed = slash.onSpace()
|
||||
const inputTriggers = this.deps.inputTriggers?.()
|
||||
if (inputTriggers === undefined) return false
|
||||
const consumed = inputTriggers.onSpace()
|
||||
// Machine-driven draft replacement never passes through onChange, so
|
||||
// re-track: the caret lands after the token, where detection sees
|
||||
// whitespace and closes the menu.
|
||||
if (consumed) {
|
||||
const next = this.snapshot
|
||||
slash.track(next.draft, next.draft.length, { tier: guardOf(next.phase) }, next.draftRev)
|
||||
inputTriggers.track(next.draft, next.draft.length, { tier: guardOf(next.phase) }, next.draftRev)
|
||||
}
|
||||
return consumed
|
||||
}
|
||||
@@ -270,8 +270,8 @@ export class SessionInputShell implements SessionInput {
|
||||
* subscribers never fire.
|
||||
*/
|
||||
readonly lexicon: ObservableSnapshot<ReadonlyMap<'/' | '@', readonly string[]>> = {
|
||||
getSnapshot: () => this.deps.slash?.()?.lexicon.getSnapshot() ?? EMPTY_LEXICON,
|
||||
subscribe: fn => this.deps.slash?.()?.lexicon.subscribe(fn) ?? (() => {}),
|
||||
getSnapshot: () => this.deps.inputTriggers?.()?.lexicon.getSnapshot() ?? EMPTY_LEXICON,
|
||||
subscribe: fn => this.deps.inputTriggers?.()?.lexicon.subscribe(fn) ?? (() => {}),
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,11 +420,11 @@ export class SessionInputShell implements SessionInput {
|
||||
this.deps.defaultSink(draft.trim(), imageIds, mode)
|
||||
return
|
||||
}
|
||||
const slash = this.deps.slash?.()
|
||||
const inputTriggers = this.deps.inputTriggers?.()
|
||||
const controller = new AbortController()
|
||||
void Promise.all(occurrences.map(async (o) => {
|
||||
if (slash === undefined) throw new Error(`no serializer for reference source "${o.source}"`)
|
||||
return { offset: o.offset, text: await slash.serializeReference(o.source, o.ref, controller.signal) }
|
||||
if (inputTriggers === undefined) throw new Error(`no serializer for reference source "${o.source}"`)
|
||||
return { offset: o.offset, text: await inputTriggers.serializeReference(o.source, o.ref, controller.signal) }
|
||||
})).then(
|
||||
(parts) => {
|
||||
if (this.disposed) return
|
||||
@@ -450,13 +450,13 @@ export class SessionInputShell implements SessionInput {
|
||||
|
||||
/** Enter adjudication: poll the session controller; failure = notice + draft retained (never a silent downgrade). */
|
||||
private adjudicate(attempt: SubmitAttempt, draft: string): void {
|
||||
const slash = this.deps.slash?.()
|
||||
if (slash === undefined) {
|
||||
const inputTriggers = this.deps.inputTriggers?.()
|
||||
if (inputTriggers === undefined) {
|
||||
// No pipeline mounted: the '/' line is an ordinary message.
|
||||
this.run(this.core.dispatch({ type: 'adjudicated', attempt, outcome: undefined }))
|
||||
return
|
||||
}
|
||||
slash.adjudicate(draft.trim(), attempt.signal).then(
|
||||
inputTriggers.adjudicate(draft.trim(), attempt.signal).then(
|
||||
(outcome: PickOutcome) => {
|
||||
if (this.dead(attempt)) return
|
||||
this.run(this.core.dispatch({ type: 'adjudicated', attempt, outcome }))
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/**
|
||||
* InputHub: the InputService implementation (`ctx.conversation.input`) — one
|
||||
* InputHub: the SessionInputResolver implementation (`ctx.conversation.input`) — one
|
||||
* SessionInputShell per session, created inside the sessions provide
|
||||
* materialization (the 'input' standard-kit entry IS the
|
||||
* creation trigger) and torn down by the scope disposer (instance-and-scope
|
||||
* share one lifecycle). The hub registers the three scoped input-mutation
|
||||
* listeners on each session's actx (the sole consumer side of the ui-slash
|
||||
* listeners on each session's actx (the sole consumer side of the ui-input-trigger
|
||||
* bail events) and owns the default-sink choreography: every session is a
|
||||
* real host entity, so the sink is one unconditional prompt path.
|
||||
*/
|
||||
import type { ClientContext, ISessions, SessionBinding, SessionFace, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SlashController } from '@deepseek-ai/dsh-client-ui-slash/client'
|
||||
import type { InputTriggerController } from '@deepseek-ai/dsh-client-ui-input-trigger/client'
|
||||
import type { TranslateNS } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { queueReadFaceOf } from '../queue/store.ts'
|
||||
import type { ComposerKeyboard, DraftAttachmentId, InputService, SessionInput } from './contract.ts'
|
||||
import type { ComposerKeyboard, DraftAttachmentId, SessionInputResolver, SessionInput } from './contract.ts'
|
||||
import type { InputSubmitMode } from '../contract/composer-submission.ts'
|
||||
import type { PopupDismissFace } from './facade.ts'
|
||||
import { SessionInputShell } from './facade.ts'
|
||||
@@ -33,8 +33,8 @@ interface ConversationAttachmentFace {
|
||||
releaseDraftImage(id: DraftAttachmentId): void
|
||||
}
|
||||
|
||||
/** Session-addressed input facade registry (InputService face + composer-layer extras). */
|
||||
export class InputHub implements InputService {
|
||||
/** Session-addressed input facade registry (SessionInputResolver face + composer-layer extras). */
|
||||
export class InputHub implements SessionInputResolver {
|
||||
private readonly shells = new Map<SessionId, SessionInputShell>()
|
||||
|
||||
/**
|
||||
@@ -47,7 +47,7 @@ export class InputHub implements InputService {
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Resolve the facade for one session-scope ctx (InputService face).
|
||||
* Resolve the facade for one session-scope ctx (SessionInputResolver face).
|
||||
* @param actx - session-scope context.
|
||||
* @returns the resident per-session facade.
|
||||
*/
|
||||
@@ -72,7 +72,7 @@ export class InputHub implements InputService {
|
||||
const { sessionId: id, session, ctx: actx } = binding
|
||||
const shell = new SessionInputShell({
|
||||
actx,
|
||||
slash: () => this.controller(actx),
|
||||
inputTriggers: () => this.controller(actx),
|
||||
popup: () => this.popup(actx),
|
||||
queue: queueReadFaceOf(session),
|
||||
defaultSink: (text, imageIds, mode) => { this.sink(session, text, imageIds, mode) },
|
||||
@@ -133,9 +133,9 @@ export class InputHub implements InputService {
|
||||
* Resolve the optional slash controller for composer chrome that launches
|
||||
* the shared candidate menu without typing a trigger.
|
||||
* @param id - session id.
|
||||
* @returns the resident controller, or undefined when ui-slash is absent.
|
||||
* @returns the resident controller, or undefined when ui-input-trigger is absent.
|
||||
*/
|
||||
slash(id: SessionId): SlashController | undefined {
|
||||
inputTriggers(id: SessionId): InputTriggerController | undefined {
|
||||
const actx = this.sessions().scope(id)
|
||||
return actx === undefined ? undefined : this.controller(actx)
|
||||
}
|
||||
@@ -191,13 +191,13 @@ export class InputHub implements InputService {
|
||||
}
|
||||
}
|
||||
|
||||
private controller(actx: ClientContext): SlashController | undefined {
|
||||
const slash = this.rootCtx.get('slash')
|
||||
return slash?.sessionOf(actx)
|
||||
private controller(actx: ClientContext): InputTriggerController | undefined {
|
||||
const inputTriggers = this.rootCtx.get('inputTriggers')
|
||||
return inputTriggers?.sessionOf(actx)
|
||||
}
|
||||
|
||||
private popup(actx: ClientContext): PopupDismissFace | undefined {
|
||||
const command = this.rootCtx.get('command') as CommandFace | undefined
|
||||
const command = this.rootCtx.get('commandUi') as CommandFace | undefined
|
||||
return command?.popupFor(actx)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* as a draftRev advance (begin-command / insert-ref / consume-token /
|
||||
* paste-upgrade all answer their bail events this way).
|
||||
*/
|
||||
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 { InputSubmitMode } from '../contract/composer-submission.ts'
|
||||
import type {
|
||||
ConsumeTokenGuard, EditRange, EditSelection, InputEffect, InputEvent, InputMachineOptions,
|
||||
|
||||
@@ -17,7 +17,7 @@ import type { ImageAttachmentRef, ImageMediaType } from '@deepseek-ai/dsh-attach
|
||||
import type { ComposerAttachment } from './contract/slots.ts'
|
||||
import type { QueueAction, QueueItemId } from './contract/queue.ts'
|
||||
import type { ComposerBlocks } from './input/blocks.ts'
|
||||
import type { DraftAttachmentId, InputService } from './input/contract.ts'
|
||||
import type { DraftAttachmentId, SessionInputResolver } from './input/contract.ts'
|
||||
import type { InputSubmitMode } from './contract/composer-submission.ts'
|
||||
|
||||
/**
|
||||
@@ -26,8 +26,8 @@ import type { InputSubmitMode } from './contract/composer-submission.ts'
|
||||
* test fake must supply.
|
||||
*/
|
||||
export interface IConversation {
|
||||
/** The per-session input machine registry (InputService face). */
|
||||
readonly input: InputService
|
||||
/** The per-session input machine registry (SessionInputResolver face). */
|
||||
readonly input: SessionInputResolver
|
||||
/**
|
||||
* The per-session composer-block registry: how a plugin the composer
|
||||
* cannot import makes a session's input inert with its own reason.
|
||||
@@ -88,9 +88,9 @@ export class UnsupportedImageMediaTypeError extends Error {
|
||||
}
|
||||
|
||||
/** Scope-addressed conversation service (root singleton, provided as `conversation`). */
|
||||
export class ConversationService extends Service implements IConversation {
|
||||
/** The per-session input machine registry (InputService face). */
|
||||
readonly input: InputService
|
||||
export class ConversationController extends Service implements IConversation {
|
||||
/** The per-session input machine registry (SessionInputResolver face). */
|
||||
readonly input: SessionInputResolver
|
||||
/** The per-session composer-block registry. */
|
||||
readonly blocks: ComposerBlocks
|
||||
private readonly draftAttachments = new Map<DraftAttachmentId, ComposerAttachment>()
|
||||
@@ -102,11 +102,11 @@ export class ConversationService extends Service implements IConversation {
|
||||
/**
|
||||
* @param ctx - owning root context (the plugin apply context; the service
|
||||
* registers itself and follows that fiber's lifetime).
|
||||
* @param config - carries the InputService and composer-block registry
|
||||
* @param config - carries the SessionInputResolver and composer-block registry
|
||||
* constructed by the plugin apply (the same instances the slot inject
|
||||
* factories close over).
|
||||
*/
|
||||
constructor(ctx: Context, config: { input: InputService; blocks: ComposerBlocks }) {
|
||||
constructor(ctx: Context, config: { input: SessionInputResolver; blocks: ComposerBlocks }) {
|
||||
super(ctx, 'conversation')
|
||||
this.input = config.input
|
||||
this.blocks = config.blocks
|
||||
|
||||
@@ -24,7 +24,7 @@ export function ConversationRoot({
|
||||
const cwd = useSessions(s => sessionId === undefined ? undefined : s.byId[sessionId]?.cwd)
|
||||
const summaryBlank = useSessions(s => sessionId === undefined ? undefined : s.byId[sessionId]?.blank)
|
||||
const workspaces = useWorkspaces(s => s)
|
||||
// A plugin this package cannot import (ui-model) says this session cannot
|
||||
// A plugin this package cannot import (ui-model-selection) says this session cannot
|
||||
// send; its reason is already localized by whoever raised it.
|
||||
const composerBlock = useComposerBlock(block => block)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import type { PermissionSelect as PermissionSelectValue } from '@deepseek-ai/dsh-permission/client'
|
||||
import type { PermissionSelect as PermissionSelectValue } from '@deepseek-ai/dsh-permission-presets/client'
|
||||
import { IconChevronDownOutline14, Menu, RiskConfirmation } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { MenuEntry } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { ComposerBarProps } from '../contract/slots.ts'
|
||||
|
||||
Reference in New Issue
Block a user