2026-08-07 16:38:54 +08:00
import { beforeEach , describe , expect , it , vi } from 'vitest'
2026-08-10 22:04:06 +08:00
import { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
2026-08-06 16:02:25 +08:00
import AgentRegistry , { Inbox } from '@deepseek-ai/dsh-agent'
2026-07-29 13:34:45 +08:00
import type { Agent , AgentStatus } from '@deepseek-ai/dsh-agent'
import CommandService from '@deepseek-ai/dsh-commands'
import SessionStore , { foldSurface , Session , SessionId } from '@deepseek-ai/dsh-session'
2026-08-08 02:27:37 +08:00
import { Telemetry , type TelemetrySharingStatus } from '@deepseek-ai/dsh-session-telemetry'
2026-07-29 13:34:45 +08:00
import * as commandFeedback from '@deepseek-ai/dsh-command-feedback'
2026-08-07 16:38:54 +08:00
const { USER_ID , getOrCreateAnonymousUserId } = vi . hoisted ( ( ) = > {
const USER_ID = '01234567-89ab-4cde-8f01-23456789abcd'
return { USER_ID , getOrCreateAnonymousUserId : vi.fn ( ( ) = > USER_ID ) }
} )
vi . mock ( '@deepseek-ai/dsh-user-id' , ( ) = > ( {
getOrCreateAnonymousUserId ,
} ) )
beforeEach ( ( ) = > getOrCreateAnonymousUserId . mockClear ( ) )
2026-07-29 13:34:45 +08:00
interface Harness {
readonly ctx : Context
readonly agent : Agent
readonly session : Session
readonly plugin : Awaited < ReturnType < Context [ 'plugin' ] > >
}
2026-08-08 02:27:37 +08:00
/** Minimal mounted backend disclosing one sharing policy. */
class FakeTelemetry extends Telemetry {
override readonly sharing : TelemetrySharingStatus
constructor ( ctx : Context , config : { sharing : TelemetrySharingStatus } ) {
super ( ctx )
this . sharing = config . sharing
}
emit ( ) : void { }
async shutdown ( ) : Promise < void > { }
}
2026-07-29 13:34:45 +08:00
/** Build a live idle agent over a store-owned session, as an app's spine does. */
function stubAgent ( ctx : Context , id : string ) : { agent : Agent ; session : Session } {
const session = ctx . sessions . create ( SessionId ( id ) )
2026-08-06 16:02:25 +08:00
const inbox = new Inbox ( session , { inserted : ( ) = > { } , discarded : ( ) = > { } , claimed : ( ) = > { } } )
2026-07-29 13:34:45 +08:00
let status : AgentStatus = 'idle'
const agent : Agent = {
id : session.id ,
options : { } ,
session ,
2026-08-06 16:02:25 +08:00
inbox ,
2026-07-29 13:34:45 +08:00
ctx : new Context ( ) ,
get status() { return status } ,
send : ( ) = > { } ,
followup : ( ) = > { } ,
steer : ( ) = > { } ,
inject : ( ) = > { } ,
cancel() { status = 'idle' } ,
2026-08-06 16:02:25 +08:00
runMaintenance : task = > task ( new AbortController ( ) . signal ) ,
2026-07-29 13:34:45 +08:00
whenIdle() { return Promise . resolve ( ) } ,
}
return { agent , session }
}
2026-08-08 02:27:37 +08:00
/**
* Mount the real command registry, this producer, and optionally a telemetry
* backend disclosing one sharing policy. Without `sharing`, no telemetry
* service exists and the acknowledgement reports "not configured".
*/
async function harness ( sharing? : TelemetrySharingStatus ) : Promise < Harness > {
2026-07-29 13:34:45 +08:00
const ctx = new Context ( )
await ctx . plugin ( CommandService )
await ctx . plugin ( AgentRegistry )
await ctx . plugin ( SessionStore )
2026-08-08 02:27:37 +08:00
if ( sharing !== undefined ) await ctx . plugin ( FakeTelemetry , { sharing } )
2026-07-29 13:34:45 +08:00
const plugin = await ctx . plugin ( commandFeedback )
const { agent , session } = stubAgent ( ctx , ` command-feedback- ${ Math . random ( ) } ` )
ctx . agents . register ( agent )
return { ctx , agent , session , plugin }
}
/** Execute `/feedback` through the same registry boundary as a UI adapter. */
async function run ( test : Harness , suffix = '' ) : Promise < { kind : string ; text? : string } > {
const settled = await test . ctx . commands . execute (
test . agent ,
` /feedback ${ suffix } ` ,
new AbortController ( ) . signal ,
)
if ( settled === undefined ) throw new Error ( 'feedback command was not registered' )
return settled . result
}
2026-07-29 21:33:53 +08:00
/** Authoritative feedback payloads in log order. */
function feedbackTexts ( session : Session ) : string [ ] {
return session . events
. filter ( event = > event . type === 'feedback/record' )
. map ( event = > event . data . text )
2026-07-29 13:34:45 +08:00
}
describe ( '@deepseek-ai/dsh-command-feedback registration' , ( ) = > {
it ( 'registers one global command with Loader-safe exports and disposes it' , async ( ) = > {
const test = await harness ( )
expect ( commandFeedback . name ) . toBe ( 'command-feedback' )
expect ( commandFeedback . inject ) . toEqual ( [ 'commands' ] )
expect ( 'default' in commandFeedback ) . toBe ( false )
const loader = Object . create ( Loader . prototype ) as Loader
expect ( loader . unwrapExports ( commandFeedback ) ) . toBe ( commandFeedback )
expect ( test . ctx . commands . list ( test . agent ) ) . toContainEqual ( {
name : 'feedback' ,
description : 'record feedback about this session' ,
input : { hint : '<text>' } ,
} )
2026-07-29 21:33:53 +08:00
expect ( test . ctx . commands . find ( test . agent , 'feedback' ) ) . toMatchObject ( { recordInput : false } )
2026-07-29 13:34:45 +08:00
await test . plugin . dispose ( )
expect ( test . ctx . commands . find ( test . agent , 'feedback' ) ) . toBeUndefined ( )
} )
} )
describe ( '/feedback human command' , ( ) = > {
2026-07-29 21:33:53 +08:00
it ( 'acknowledges feedback and records its payload exactly once in the domain event' , async ( ) = > {
2026-07-29 13:34:45 +08:00
const test = await harness ( )
await expect ( run ( test , ' the diff view is unreadable' ) ) . resolves . toEqual ( {
kind : 'success' ,
2026-08-08 02:27:37 +08:00
text : ` Feedback recorded for session ${ test . session . id } \ nUser: ${ USER_ID } . Session sharing is not configured. ` ,
2026-07-29 13:34:45 +08:00
} )
2026-07-29 21:33:53 +08:00
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'the diff view is unreadable' ] )
const commandRun = test . session . events . find ( event = > event . type === 'command/run' )
expect ( commandRun ? . type === 'command/run' && Object . hasOwn ( commandRun . data , 'args' ) ) . toBe ( false )
expect ( JSON . stringify ( test . session . events ) . match ( / t h e d i f f v i e w i s u n r e a d a b l e / g u ) ) . t o H a v e L e n g t h ( 1 )
} )
it ( 'exports a command-independent feedback producer' , async ( ) = > {
const test = await harness ( )
commandFeedback . recordFeedback ( test . session , ' recorded outside a command ' )
expect ( test . session . events . map ( event = > event . type ) ) . toEqual ( [ 'feedback/record' ] )
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'recorded outside a command' ] )
expect ( ( ) = > { commandFeedback . recordFeedback ( test . session , ' \n\t ' ) } )
. toThrow ( 'feedback text must not be empty' )
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'recorded outside a command' ] )
2026-07-29 13:34:45 +08:00
} )
2026-07-29 21:33:53 +08:00
it ( 'keeps command bookkeeping around the authoritative feedback event' , async ( ) = > {
2026-07-29 13:34:45 +08:00
const test = await harness ( )
await run ( test , ' nothing else happens' )
2026-07-29 21:33:53 +08:00
expect ( test . session . events . map ( event = > event . type ) ) . toEqual ( [
'command/run' , 'feedback/record' , 'command/done' ,
] )
2026-07-29 13:34:45 +08:00
} )
2026-07-29 21:33:53 +08:00
it ( 'normalizes surrounding whitespace without parsing command-like content' , async ( ) = > {
2026-07-29 13:34:45 +08:00
const test = await harness ( )
await run ( test , ' /plan felt SLOW\n\ttwice today ' )
2026-07-29 21:33:53 +08:00
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ '/plan felt SLOW\n\ttwice today' ] )
2026-07-29 13:34:45 +08:00
} )
it ( 'records each entry separately without replacing earlier ones' , async ( ) = > {
const test = await harness ( )
await run ( test , ' first' )
await run ( test , ' second' )
2026-07-29 21:33:53 +08:00
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'first' , 'second' ] )
2026-07-29 13:34:45 +08:00
} )
it ( 'records concurrent submissions in dispatch order' , async ( ) = > {
const test = await harness ( )
const signal = new AbortController ( ) . signal
2026-08-06 16:02:25 +08:00
// Command adapters may dispatch concurrent requests without awaiting one another.
2026-07-29 13:34:45 +08:00
const settled = await Promise . all ( [
test . ctx . commands . execute ( test . agent , '/feedback first' , signal ) ,
test . ctx . commands . execute ( test . agent , '/feedback second' , signal ) ,
] )
expect ( settled . map ( item = > item ? . result ) ) . toEqual ( [
2026-08-08 02:27:37 +08:00
{ kind : 'success' , text : ` Feedback recorded for session ${ test . session . id } \ nUser: ${ USER_ID } . Session sharing is not configured. ` } ,
{ kind : 'success' , text : ` Feedback recorded for session ${ test . session . id } \ nUser: ${ USER_ID } . Session sharing is not configured. ` } ,
2026-07-29 13:34:45 +08:00
] )
2026-07-29 21:33:53 +08:00
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'first' , 'second' ] )
2026-07-29 13:34:45 +08:00
} )
2026-08-08 02:27:37 +08:00
it ( 'discloses full session sharing in the acknowledgement' , async ( ) = > {
const test = await harness ( 'full' )
await expect ( run ( test , ' everything shared' ) ) . resolves . toEqual ( {
kind : 'success' ,
text : ` Feedback recorded for session ${ test . session . id } \ nUser: ${ USER_ID } . Session sharing is enabled. ` ,
} )
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'everything shared' ] )
} )
it ( 'discloses feedback-gated session sharing in the acknowledgement' , async ( ) = > {
const test = await harness ( 'feedback-only' )
await expect ( run ( test , ' gated sharing' ) ) . resolves . toEqual ( {
kind : 'success' ,
text : ` Feedback recorded for session ${ test . session . id } \ nUser: ${ USER_ID } . Session sharing is feedback-gated; recording feedback releases the session prefix for sharing. ` ,
} )
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'gated sharing' ] )
} )
it ( 'discloses disabled session sharing in the acknowledgement' , async ( ) = > {
const test = await harness ( 'disabled' )
await expect ( run ( test , ' local only' ) ) . resolves . toEqual ( {
kind : 'success' ,
text : ` Feedback recorded for session ${ test . session . id } \ nUser: ${ USER_ID } . Session sharing is disabled. ` ,
} )
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ 'local only' ] )
} )
2026-07-24 19:54:25 +08:00
it ( 'keeps every recorded event out of model context and derived history' , async ( ) = > {
2026-07-29 13:34:45 +08:00
const test = await harness ( )
await run ( test , ' invisible to the model' )
for ( const event of test . session . events ) {
expect ( 'surfaceOp' in event ) . toBe ( false )
expect ( test . session . deriveEventMessage ( event ) ) . toBeNull ( )
}
expect ( foldSurface ( test . session . events ) . nodes ) . toEqual ( [ ] )
expect ( test . session . surface . nodes ) . toEqual ( [ ] )
expect ( test . session . deriveMessages ( ) ) . toEqual ( [ ] )
} )
it ( 'rejects empty and whitespace-only input as a failed command record' , async ( ) = > {
const test = await harness ( )
const expected = {
kind : 'error' ,
text : 'Feedback text is required. Usage: /feedback <text>' ,
}
await expect ( run ( test ) ) . resolves . toEqual ( expected )
await expect ( run ( test , ' \n\t ' ) ) . resolves . toEqual ( expected )
2026-08-07 16:38:54 +08:00
expect ( getOrCreateAnonymousUserId ) . not . toHaveBeenCalled ( )
2026-07-29 21:33:53 +08:00
expect ( feedbackTexts ( test . session ) ) . toEqual ( [ ] )
const done = test . session . events . filter ( event = > event . type === 'command/done' )
expect ( done . map ( event = > event . data . kind ) ) . toEqual ( [ 'error' , 'error' ] )
for ( const event of test . session . events ) {
if ( event . type === 'command/run' ) expect ( Object . hasOwn ( event . data , 'args' ) ) . toBe ( false )
}
2026-07-29 13:34:45 +08:00
} )
it ( 'records nothing when dispatch rejects an already-cancelled request' , async ( ) = > {
const test = await harness ( )
const controller = new AbortController ( )
controller . abort ( new Error ( 'user cancelled the command' ) )
await expect ( test . ctx . commands . execute ( test . agent , '/feedback too late' , controller . signal ) )
. rejects . toThrow ( 'user cancelled the command' )
expect ( test . session . events ) . toEqual ( [ ] )
} )
} )