返回源码地图

packages/feedback/command-feedback/src/index.ts

main snapshot · da00f7f5358f · 正文引用章节 17;完整原文可核对,不声称全文件人工逐行审计

完整原文供逐行核对;页面收录不代表每行都经过人工语义审核。MIT 许可见 许可证。

1/**
2 * Session feedback: the `feedback/record` event, its command-independent
3 * producer, the `sessionFeedback` Host Remote a product surface records
4 * through, and the human-facing `/feedback` command. Recording appends one
5 * authoritative log-only event and does not start model work. The append is
6 * eager but unflushed, so acknowledgement reports that the entry is logged,
7 * not that it reached disk.
8 * @module @deepseek-ai/dsh-command-feedback
9 */
10
11import type { Context } from '@deepseek-ai/cordis'
12import { CommandDefinitionId } from '@deepseek-ai/dsh-commands/brand'
13import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands'
14import type { Session } from '@deepseek-ai/dsh-session'
15import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id'
16import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol'
17import type {
18 FeedbackCategory,
19 FeedbackRecord,
20 SessionFeedbackRecordRequest,
21 SessionFeedbackRecordResult,
22} from './types.ts'
23
24export type * from './types.ts'
25
26/**
27 * Every feedback category in the order product surfaces present them; each
28 * surface owns its localized labels.
29 */
30export const FEEDBACK_CATEGORIES = [
31 'task-result',
32 'instruction-following',
33 'product-interaction',
34 'service-stability',
35 'resource-cost',
36 'security-privacy-permission',
37 'other',
38] as const satisfies readonly FeedbackCategory[]
39
40export const name = 'command-feedback'
41export const inject = ['commands']
42
43const USAGE = 'Usage: /feedback <text>'
44
45declare module '@deepseek-ai/cordis' {
46 interface Context {
47 sessionFeedback: SessionFeedbackService
48 }
49}
50
51/**
52 * Record feedback independently of any UI trigger. Surrounding whitespace is
53 * discarded and a blank text is recorded as absent; an entry with neither
54 * text nor category is still recorded.
55 * @param session - session the feedback describes.
56 * @param entry - human-authored remark and its category.
57 */
58export function recordFeedback(session: Session, entry: FeedbackRecord): void {
59 const text = entry.text?.trim() ?? ''
60 session.append('feedback/record', {
61 ...(text.length === 0 ? {} : { text }),
62 ...(entry.category === undefined ? {} : { category: entry.category }),
63 })
64}
65
66/**
67 * Validate, record, and acknowledge one feedback entry. Returning an error
68 * leaves no `feedback/record` event.
69 * @param invocation - receiving agent, raw command input, and UI cancellation.
70 * @returns an acknowledgement containing the receiving session and anonymous
71 * user ids, or a usage error when no feedback text was supplied.
72 */
73function executeFeedbackCommand(invocation: CommandInvocation): CommandResult {
74 if (invocation.rawInput.trim().length === 0) {
75 return { kind: 'error', text: `Feedback text is required. ${USAGE}` }
76 }
77 recordFeedback(invocation.agent.session, { text: invocation.rawInput })
78 return {
79 kind: 'success',
80 text: `Feedback recorded for session ${invocation.agent.session.id}\nAnonymous user: ${getOrCreateAnonymousUserId()}.`,
81 }
82}
83
84/** Host Remote through which a product surface records a Session-level remark. */
85export class SessionFeedbackService extends TypertRemoteService {
86 static inject = ['sessions']
87
88 /**
89 * @param ctx - Host context carrying the live Session store.
90 */
91 constructor(ctx: Context) {
92 super(ctx, 'sessionFeedback')
93 }
94
95 /**
96 * Record one remark on a live Session.
97 * @param request - target Session plus the optional text and category.
98 * @returns the recorded postcondition, or `session-not-found` when no live
99 * Session carries the id.
100 */
101 @Remote('record')
102 record(request: SessionFeedbackRecordRequest): Promise<SessionFeedbackRecordResult> {
103 const session = this.ctx.sessions.get(request.sessionId)
104 if (session === undefined) {
105 return Promise.resolve({ ok: false, error: { code: 'session-not-found', sessionId: request.sessionId } })
106 }
107 recordFeedback(session, request)
108 return Promise.resolve({ ok: true, value: { recorded: true } })
109 }
110}
111
112/**
113 * Register the global `/feedback` command for every composed command adapter
114 * and mount the `sessionFeedback` Remote.
115 * @param ctx - Host context.
116 */
117export function apply(ctx: Context): void {
118 ctx.plugin(SessionFeedbackService)
119 ctx.commands.register({
120 definitionId: CommandDefinitionId('@deepseek-ai/dsh-command-feedback'),
121 name: 'feedback',
122 description: 'Record feedback about this session',
123 input: { hint: '<text>' },
124 recordInput: false,
125 handler: executeFeedbackCommand,
126 })
127}