mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fixup! refactor(interaction): move Approval and Question into UI owners
This commit is contained in:
@@ -36,7 +36,7 @@ async function answerApproval(
|
||||
owner: ClientContext,
|
||||
request: ClientApprovalRequest,
|
||||
next: ClientApprovalNext,
|
||||
attend: (pending: PendingApproval) => () => void,
|
||||
registerPendingInteraction: (pending: PendingApproval) => () => void,
|
||||
): Promise<ClientApprovalOutcome> {
|
||||
const sessionId = ctx.sessions.scopeOf(owner)
|
||||
if (sessionId === undefined) return next()
|
||||
@@ -48,7 +48,7 @@ async function answerApproval(
|
||||
...(request.reason === undefined ? {} : { reason: request.reason }),
|
||||
...(request.signal === undefined ? {} : { signal: request.signal }),
|
||||
})
|
||||
const remove = attend(pending)
|
||||
const remove = registerPendingInteraction(pending)
|
||||
try {
|
||||
return await pending.result
|
||||
} finally {
|
||||
@@ -62,7 +62,9 @@ async function answerApproval(
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-approval: dictionaries')
|
||||
const attend = ctx.uiSession.attend<PendingApproval>(() => 0)
|
||||
const registerPendingInteraction = ctx.uiSession.registerPendingInteraction<PendingApproval>(
|
||||
() => 0,
|
||||
)
|
||||
ctx.slots.inject('conversation.composer', () => ctx.slots.register({
|
||||
name: 'conversation.composer',
|
||||
priority: 1,
|
||||
@@ -74,6 +76,6 @@ export function apply(ctx: ClientContext): void {
|
||||
},
|
||||
}, ApprovalPanel))
|
||||
ctx.remote.$on('approval/request', function (request, next) {
|
||||
return answerApproval(ctx, this, request, next, attend)
|
||||
return answerApproval(ctx, this, request, next, registerPendingInteraction)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ interface PluginBench {
|
||||
readonly ctx: Context
|
||||
readonly listener: ApprovalListener
|
||||
readonly pending: { getSnapshot(): readonly PendingApproval[] }
|
||||
readonly attend: ReturnType<typeof vi.fn>
|
||||
readonly registerPendingInteraction: ReturnType<typeof vi.fn>
|
||||
readonly disposeSlot: ReturnType<typeof vi.fn>
|
||||
readonly disposeLocale: ReturnType<typeof vi.fn>
|
||||
readonly register: ReturnType<typeof vi.fn>
|
||||
@@ -53,7 +53,7 @@ function setupPlugin(): PluginBench {
|
||||
const disposeSlot = vi.fn()
|
||||
const disposeLocale = vi.fn()
|
||||
let pending: readonly PendingApproval[] = []
|
||||
const attend = vi.fn((_precedence: (value: PendingApproval) => number) => (
|
||||
const registerPendingInteraction = vi.fn((_precedence: (value: PendingApproval) => number) => (
|
||||
value: PendingApproval,
|
||||
) => {
|
||||
pending = [...pending, value]
|
||||
@@ -77,7 +77,7 @@ function setupPlugin(): PluginBench {
|
||||
},
|
||||
} as never)
|
||||
ctx.provide('sessions', { scopeOf } as never)
|
||||
ctx.provide('uiSession', { attend } as never)
|
||||
ctx.provide('uiSession', { registerPendingInteraction } as never)
|
||||
ctx.provide('slots', { inject: injectSlot, register } as never)
|
||||
ctx.provide('locale', {
|
||||
register: vi.fn(() => disposeLocale),
|
||||
@@ -89,7 +89,7 @@ function setupPlugin(): PluginBench {
|
||||
ctx,
|
||||
listener,
|
||||
pending: { getSnapshot: () => pending },
|
||||
attend,
|
||||
registerPendingInteraction,
|
||||
disposeSlot,
|
||||
disposeLocale,
|
||||
register,
|
||||
|
||||
@@ -282,7 +282,7 @@ export class UiSession extends Service {
|
||||
* @param precedence - deterministic cross-domain precedence; larger values win.
|
||||
* @returns a function that publishes one exact interaction until its disposer runs.
|
||||
*/
|
||||
attend<T extends SessionPendingInteractionBase>(
|
||||
registerPendingInteraction<T extends SessionPendingInteractionBase>(
|
||||
precedence: (interaction: T) => number,
|
||||
): (interaction: T) => () => void {
|
||||
const domain = new PendingInteractionDomain(precedence, () => {
|
||||
@@ -297,7 +297,7 @@ export class UiSession extends Service {
|
||||
if (index !== -1) this.pendingDomains.splice(index, 1)
|
||||
this.publishPendingInteractions()
|
||||
}
|
||||
}, 'uiSession.attend()')
|
||||
}, 'uiSession.registerPendingInteraction()')
|
||||
return interaction => domain.publish(interaction)
|
||||
}
|
||||
|
||||
|
||||
@@ -380,8 +380,10 @@ describe('UiSession pending interactions', () => {
|
||||
const id = sessionId('s1')
|
||||
const listener = vi.fn()
|
||||
const off = service.pendingInteractions.subscribe(listener)
|
||||
const attendApproval = service.attend<SessionPendingInteractionBase>(() => 0)
|
||||
const attendQuestion = service.attend<SessionPendingInteractionBase>(
|
||||
const registerApproval = service.registerPendingInteraction<SessionPendingInteractionBase>(
|
||||
() => 0,
|
||||
)
|
||||
const registerQuestion = service.registerPendingInteraction<SessionPendingInteractionBase>(
|
||||
interaction => interaction.kind === 'plan-review' ? 2 : 1,
|
||||
)
|
||||
listener.mockClear()
|
||||
@@ -390,13 +392,13 @@ describe('UiSession pending interactions', () => {
|
||||
const duplicate = { key: 'approval:2', kind: 'approval', sessionId: id }
|
||||
const question = { key: 'question:1', kind: 'question', sessionId: id }
|
||||
const plan = { key: 'question:2', kind: 'plan-review', sessionId: id }
|
||||
const removeApproval = attendApproval(approval)
|
||||
const removeApproval = registerApproval(approval)
|
||||
expect(service.pendingInteractions.getSnapshot().get(id)).toBe(approval)
|
||||
const removeDuplicate = attendApproval(duplicate)
|
||||
const removeDuplicate = registerApproval(duplicate)
|
||||
expect(service.pendingInteractions.getSnapshot().get(id)).toBe(duplicate)
|
||||
const removeQuestion = attendQuestion(question)
|
||||
const removeQuestion = registerQuestion(question)
|
||||
expect(service.pendingInteractions.getSnapshot().get(id)).toBe(question)
|
||||
const removePlan = attendQuestion(plan)
|
||||
const removePlan = registerQuestion(plan)
|
||||
expect(service.pendingInteractions.getSnapshot().get(id)).toBe(plan)
|
||||
|
||||
removeQuestion()
|
||||
@@ -414,10 +416,12 @@ describe('UiSession pending interactions', () => {
|
||||
const ctx = new Context()
|
||||
const bench = createSessionsBench(ctx)
|
||||
const service = createUiSession(ctx, bench)
|
||||
const attend = service.attend<SessionPendingInteractionBase>(() => 1)
|
||||
const registerPendingInteraction = service.registerPendingInteraction<SessionPendingInteractionBase>(
|
||||
() => 1,
|
||||
)
|
||||
const interaction = { key: 'question:1', kind: 'question', sessionId: sessionId('s1') }
|
||||
const remove = attend(interaction)
|
||||
expect(() => { attend(interaction) })
|
||||
const remove = registerPendingInteraction(interaction)
|
||||
expect(() => { registerPendingInteraction(interaction) })
|
||||
.toThrow("ui-session: duplicate pending interaction key 'question:1'")
|
||||
|
||||
const failure = new Error('pending subscriber failed')
|
||||
|
||||
@@ -55,12 +55,12 @@ async function answerQuestion(
|
||||
owner: ClientContext,
|
||||
request: ClientQuestionRequest,
|
||||
next: ClientQuestionNext,
|
||||
attend: (pending: PendingQuestion) => () => void,
|
||||
registerPendingInteraction: (pending: PendingQuestion) => () => void,
|
||||
): Promise<ClientQuestionAnswer> {
|
||||
const sessionId = ctx.sessions.scopeOf(owner)
|
||||
if (sessionId === undefined) return next()
|
||||
const pending = new PendingQuestion(sessionId, request.questions, request.signal)
|
||||
const remove = attend(pending)
|
||||
const remove = registerPendingInteraction(pending)
|
||||
try {
|
||||
return await pending.result
|
||||
} finally {
|
||||
@@ -76,7 +76,7 @@ async function answerQuestion(
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-user-questions: dictionaries')
|
||||
const attend = ctx.uiSession.attend<PendingQuestion>(
|
||||
const registerPendingInteraction = ctx.uiSession.registerPendingInteraction<PendingQuestion>(
|
||||
pending => pending.kind === 'plan-review' ? 2 : 1,
|
||||
)
|
||||
ctx.slots.inject('conversation.composer', () => ctx.slots.register(
|
||||
@@ -89,6 +89,6 @@ export function apply(ctx: ClientContext): void {
|
||||
QuestionComposer,
|
||||
))
|
||||
ctx.remote.$on('user-questions/request', function (request, next) {
|
||||
return answerQuestion(ctx, this, request, next, attend)
|
||||
return answerQuestion(ctx, this, request, next, registerPendingInteraction)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,13 +50,13 @@ async function bench(declare = true) {
|
||||
)[SESSION_SCOPE])
|
||||
ctx.provide('sessions', { scopeOf } as never)
|
||||
let pending: readonly PendingQuestion[] = []
|
||||
const attend = vi.fn((_precedence: (value: PendingQuestion) => number) => (
|
||||
const registerPendingInteraction = vi.fn((_precedence: (value: PendingQuestion) => number) => (
|
||||
value: PendingQuestion,
|
||||
) => {
|
||||
pending = [...pending, value]
|
||||
return () => { pending = pending.filter(candidate => candidate !== value) }
|
||||
})
|
||||
ctx.provide('uiSession', { attend } as never)
|
||||
ctx.provide('uiSession', { registerPendingInteraction } as never)
|
||||
let listener: QuestionListener | undefined
|
||||
const on = vi.fn((event: string, value: QuestionListener) => {
|
||||
expect(event).toBe('user-questions/request')
|
||||
@@ -81,7 +81,7 @@ async function bench(declare = true) {
|
||||
agent,
|
||||
scopeOf,
|
||||
pending: { getSnapshot: () => pending },
|
||||
attend,
|
||||
registerPendingInteraction,
|
||||
on,
|
||||
fiber,
|
||||
invoke,
|
||||
|
||||
Reference in New Issue
Block a user