@deepseek-ai/dsh-message-feedback 0.1.5-alpha.1 → 0.1.5-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/feedback/message-feedback/README.md
5
- README.md: e1defa8d8fd6149f124267ffa0c382f4a82cf2ab
6
- README.zh.md: d16f8f4416288e5373e3221e8f43a8b2d84e5f80
5
+ README.md: 4e75d167fe39c96002b32def24f447efdbc2be70
6
+ README.zh.md: 0c21e26f9a4ffa40ce29412c652029d3af4e9fe0
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Canonical Session-log ratings and notes for finalized assistant messages."
2
+ description: "Canonical Session-log ratings, categories, and notes for finalized assistant messages."
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- This service records positive or negative ratings and optional verbatim notes for finalized assistant messages. The canonical Session log owns every creation, edit, and deletion; `list`, `put`, and `delete` expose current feedback without constructing or waking an Agent. Feedback is log-only and does not enter model history.
12
+ This service records positive or negative ratings, an optional category from the fixed feedback taxonomy, and optional verbatim notes for finalized assistant messages. The canonical Session log owns every creation, edit, and deletion; `list`, `put`, and `delete` expose current feedback without constructing or waking an Agent. Feedback is log-only and does not enter model history.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -30,17 +30,17 @@ Mount `dsh-message-feedback` alongside `sessions` and `sessionPersistence`. It n
30
30
  |---|---|---|
31
31
  | `maxNoteBytes` | required | Positive safe-integer maximum UTF-8 bytes in one optional note. |
32
32
 
33
- A supplied note must contain a non-whitespace character and fit the configured byte limit. Blank notes return `note-blank`; oversized notes return `note-too-large`. Accepted text is preserved exactly, including surrounding whitespace. Omitting a note clears it. Note validation precedes Session lookup.
33
+ A supplied note must contain a non-whitespace character and fit the configured byte limit. Blank notes return `note-blank`; oversized notes return `note-too-large`. Accepted text is preserved exactly, including surrounding whitespace. Omitting a note clears it. Note validation precedes Session lookup. A supplied category must be one of the [fixed feedback categories](../command-feedback/README.md#the-web-feedback-dialog); the Remote schema rejects any other value, and omitting the category clears it.
34
34
 
35
35
  ### Reading and changing feedback
36
36
 
37
37
  | Operation | Request | Success | Business failures |
38
38
  |---|---|---|---|
39
39
  | `list` | Session id | Current items in creation order | Session not found |
40
- | `put` | Session, message, rating, optional note, expected version | Current item | Session or target not found, version conflict, invalid note |
40
+ | `put` | Session, message, rating, optional note, optional category, expected version | Current item | Session or target not found, version conflict, invalid note |
41
41
  | `delete` | Session, message, expected version | Item absent | Session not found, version conflict |
42
42
 
43
- Create with `ifVersion: null`; edit or delete with the returned version. Stale mutations return `version-conflict` and the current item. Each material put mints a fresh token and preserves the original creation time. A matching no-op put returns the same item without appending an event. Deleting an absent item succeeds regardless of the supplied version, without appending an event. Recreating a deleted item starts a new creation time and ordering position.
43
+ Create with `ifVersion: null`; edit or delete with the returned version. Stale mutations return `version-conflict` and the current item. Each material put mints a fresh token and preserves the original creation time. A put that repeats the stored rating, note, and category is a no-op: it returns the same item without appending an event. Deleting an absent item succeeds regardless of the supplied version, without appending an event. Recreating a deleted item starts a new creation time and ordering position.
44
44
 
45
45
  Targets must be non-empty assistant messages produced by append-origin events. User messages, empty assistant placeholders, and replacement-origin messages return `target-not-found`. Feedback survives restart; a fork starts without owned feedback even when its inherited prefix contains parent feedback.
46
46
 
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "在权威 Session 日志中保存已完成 assistant 消息的评分与备注。"
2
+ description: "在权威 Session 日志中保存已完成 assistant 消息的评分、分类与备注。"
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- 本服务为已完成的 assistant 消息记录好评、差评及可选的原样备注。每次创建、编辑和删除都由权威 Session 日志保存;`list`、`put` 和 `delete` 提供当前反馈,不会构造或唤醒 Agent。反馈仅写入日志,不进入模型历史。
12
+ 本服务为已完成的 assistant 消息记录好评、差评、固定反馈分类表中的可选分类,以及可选的原样备注。每次创建、编辑和删除都由权威 Session 日志保存;`list`、`put` 和 `delete` 提供当前反馈,不会构造或唤醒 Agent。反馈仅写入日志,不进入模型历史。
13
13
 
14
14
  ## 目录
15
15
 
@@ -30,17 +30,17 @@ kind: "package-reference"
30
30
  |---|---|---|
31
31
  | `maxNoteBytes` | 必填 | 单条可选备注的 UTF-8 字节上限,必须为正安全整数。 |
32
32
 
33
- 提交的备注必须包含非空白字符,且不超过配置的字节上限。空白备注返回 `note-blank`;过长备注返回 `note-too-large`。通过校验的文本会完整保留,包括首尾空白。省略备注会清除它。备注校验先于 Session 查找。
33
+ 提交的备注必须包含非空白字符,且不超过配置的字节上限。空白备注返回 `note-blank`;过长备注返回 `note-too-large`。通过校验的文本会完整保留,包括首尾空白。省略备注会清除它。备注校验先于 Session 查找。提交的分类必须是[固定反馈分类](../command-feedback/README.zh.md#the-web-feedback-dialog)之一;Remote schema 拒绝其他值,省略分类会清除它。
34
34
 
35
35
  ### 读取与修改反馈
36
36
 
37
37
  | 操作 | 请求 | 成功 | 业务失败 |
38
38
  |---|---|---|---|
39
39
  | `list` | Session id | 按创建顺序返回当前条目 | Session 不存在 |
40
- | `put` | Session、消息、评分、可选备注、预期版本 | 当前条目 | Session 或目标不存在、版本冲突、备注无效 |
40
+ | `put` | Session、消息、评分、可选备注、可选分类、预期版本 | 当前条目 | Session 或目标不存在、版本冲突、备注无效 |
41
41
  | `delete` | Session、消息、预期版本 | 条目不存在 | Session 不存在、版本冲突 |
42
42
 
43
- 创建时传入 `ifVersion: null`;编辑或删除时使用返回的版本。陈旧修改返回 `version-conflict` 及当前条目。每次实质 put 都生成新 token,并保留原始创建时间。匹配的无变化 put 返回相同条目,不追加事件。删除不存在的条目始终成功,不受所传版本影响,也不追加事件。重新创建已删除条目会产生新的创建时间和排序位置。
43
+ 创建时传入 `ifVersion: null`;编辑或删除时使用返回的版本。陈旧修改返回 `version-conflict` 及当前条目。每次实质 put 都生成新 token,并保留原始创建时间。重复已存评分、备注与分类的 put 是无变化操作:返回相同条目,不追加事件。删除不存在的条目始终成功,不受所传版本影响,也不追加事件。重新创建已删除条目会产生新的创建时间和排序位置。
44
44
 
45
45
  目标必须是由 append 来源事件产生的非空 assistant 消息。用户消息、空 assistant 占位及 replacement 来源消息返回 `target-not-found`。反馈跨重启保留;fork 即使继承了包含父会话反馈的前缀,也从没有自有反馈开始。
46
46
 
package/lib/index.js CHANGED
@@ -4,6 +4,7 @@ import { isDeepStrictEqual } from "node:util";
4
4
  import { Service } from "@deepseek-ai/cordis";
5
5
  import s from "@deepseek-ai/schemastery";
6
6
  import { z } from "zod";
7
+ import { FEEDBACK_CATEGORIES } from "@deepseek-ai/dsh-command-feedback";
7
8
  import { SessionSeq } from "@deepseek-ai/dsh-session/types";
8
9
  import { deriveEventMessage, isAppendSurfaceEvent } from "@deepseek-ai/dsh-session/surface";
9
10
  import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
@@ -55,6 +56,7 @@ const itemSchema = z.object({
55
56
  messageId: z.string().min(1),
56
57
  rating: z.enum(["positive", "negative"]),
57
58
  note: z.string().refine((note) => note.trim().length > 0).optional(),
59
+ category: z.enum(FEEDBACK_CATEGORIES).optional(),
58
60
  version: z.uuid(),
59
61
  createdAt: timestamp,
60
62
  updatedAt: timestamp
@@ -199,7 +201,7 @@ let MessageFeedbackService = (() => {
199
201
  });
200
202
  const existing = items.find((item) => item.messageId === request.messageId);
201
203
  if (request.ifVersion !== (existing?.version ?? null)) return rejected(this.versionConflict(existing ?? null));
202
- if (existing !== void 0 && existing.rating === request.rating && existing.note === note.value) {
204
+ if (existing !== void 0 && existing.rating === request.rating && existing.note === note.value && existing.category === request.category) {
203
205
  await append();
204
206
  return success(snapshotItem(existing));
205
207
  }
@@ -208,6 +210,7 @@ let MessageFeedbackService = (() => {
208
210
  messageId: request.messageId,
209
211
  rating: request.rating,
210
212
  ...note.value === void 0 ? {} : { note: note.value },
213
+ ...request.category === void 0 ? {} : { category: request.category },
211
214
  version: randomUUID(),
212
215
  createdAt: existing?.createdAt ?? now,
213
216
  updatedAt: existing === void 0 ? now : Math.max(now, existing.updatedAt)
@@ -22,6 +22,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_delete_result$schema = z
22
22
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
23
23
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
24
24
  'note': z.string().readonly().optional(),
25
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
25
26
  'version': z.intersection(z.string(), z.unknown()).readonly(),
26
27
  'createdAt': z.number().readonly(),
27
28
  'updatedAt': z.number().readonly(),
@@ -38,6 +39,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_list_result$schema = z.u
38
39
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
39
40
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
40
41
  'note': z.string().readonly().optional(),
42
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
41
43
  'version': z.intersection(z.string(), z.unknown()).readonly(),
42
44
  'createdAt': z.number().readonly(),
43
45
  'updatedAt': z.number().readonly(),
@@ -55,6 +57,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_put_parameter_0$schema =
55
57
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
56
58
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
57
59
  'note': z.string().readonly().optional(),
60
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
58
61
  'ifVersion': z.union([z.literal(null), z.intersection(z.string(), z.unknown())]).readonly(),
59
62
  })
60
63
  const _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema = z.union([z.object({
@@ -63,6 +66,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema = z.un
63
66
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
64
67
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
65
68
  'note': z.string().readonly().optional(),
69
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
66
70
  'version': z.intersection(z.string(), z.unknown()).readonly(),
67
71
  'createdAt': z.number().readonly(),
68
72
  'updatedAt': z.number().readonly(),
@@ -82,6 +86,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema = z.un
82
86
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
83
87
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
84
88
  'note': z.string().readonly().optional(),
89
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
85
90
  'version': z.intersection(z.string(), z.unknown()).readonly(),
86
91
  'createdAt': z.number().readonly(),
87
92
  'updatedAt': z.number().readonly(),
@@ -124,7 +129,7 @@ export const TYPERT = {
124
129
  typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackDeleteResult',
125
130
  schema: _deepseek_ai_dsh_message_feedback_messageFeedback_delete_result$schema,
126
131
  },
127
- sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":203,"column":3},
132
+ sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":207,"column":3},
128
133
  },
129
134
  {
130
135
  id: '@deepseek-ai/dsh-message-feedback#messageFeedback/list',
@@ -149,7 +154,7 @@ export const TYPERT = {
149
154
  typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackListResult',
150
155
  schema: _deepseek_ai_dsh_message_feedback_messageFeedback_list_result$schema,
151
156
  },
152
- sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":153,"column":3},
157
+ sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":155,"column":3},
153
158
  },
154
159
  {
155
160
  id: '@deepseek-ai/dsh-message-feedback#messageFeedback/put',
@@ -174,7 +179,7 @@ export const TYPERT = {
174
179
  typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackPutResult',
175
180
  schema: _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema,
176
181
  },
177
- sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":165,"column":3},
182
+ sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":167,"column":3},
178
183
  },
179
184
  ],
180
185
  model: {
@@ -214,6 +219,10 @@ export const TYPERT = {
214
219
  "name": "Branded",
215
220
  "declaration": "export type Branded<B extends string> = string & { readonly [BRAND]: B; };"
216
221
  },
222
+ {
223
+ "name": "FeedbackCategory",
224
+ "declaration": "export type FeedbackCategory = 'task-result' | 'instruction-following' | 'product-interaction' | 'service-stability' | 'resource-cost' | 'security-privacy-permission' | 'other';"
225
+ },
217
226
  {
218
227
  "name": "MessageFeedbackDeleteRequest",
219
228
  "declaration": "export interface MessageFeedbackDeleteRequest {\n readonly sessionId: SessionId;\n readonly messageId: MessageId;\n readonly ifVersion: MessageFeedbackVersion;\n}"
@@ -232,7 +241,7 @@ export const TYPERT = {
232
241
  },
233
242
  {
234
243
  "name": "MessageFeedbackItem",
235
- "declaration": "export interface MessageFeedbackItem {\n readonly messageId: MessageId;\n readonly rating: MessageFeedbackRating;\n readonly note?: string;\n readonly version: MessageFeedbackVersion;\n readonly createdAt: number;\n readonly updatedAt: number;\n}"
244
+ "declaration": "export interface MessageFeedbackItem {\n readonly messageId: MessageId;\n readonly rating: MessageFeedbackRating;\n readonly note?: string;\n readonly category?: FeedbackCategory;\n readonly version: MessageFeedbackVersion;\n readonly createdAt: number;\n readonly updatedAt: number;\n}"
236
245
  },
237
246
  {
238
247
  "name": "MessageFeedbackListRequest",
@@ -256,7 +265,7 @@ export const TYPERT = {
256
265
  },
257
266
  {
258
267
  "name": "MessageFeedbackPutRequest",
259
- "declaration": "export interface MessageFeedbackPutRequest {\n readonly sessionId: SessionId;\n readonly messageId: MessageId;\n readonly rating: MessageFeedbackRating;\n readonly note?: string;\n readonly ifVersion: MessageFeedbackVersion | null;\n}"
268
+ "declaration": "export interface MessageFeedbackPutRequest {\n readonly sessionId: SessionId;\n readonly messageId: MessageId;\n readonly rating: MessageFeedbackRating;\n readonly note?: string;\n readonly category?: FeedbackCategory;\n readonly ifVersion: MessageFeedbackVersion | null;\n}"
260
269
  },
261
270
  {
262
271
  "name": "MessageFeedbackPutResult",
@@ -22,6 +22,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_delete_result$schema = z
22
22
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
23
23
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
24
24
  'note': z.string().readonly().optional(),
25
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
25
26
  'version': z.intersection(z.string(), z.unknown()).readonly(),
26
27
  'createdAt': z.number().readonly(),
27
28
  'updatedAt': z.number().readonly(),
@@ -38,6 +39,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_list_result$schema = z.u
38
39
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
39
40
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
40
41
  'note': z.string().readonly().optional(),
42
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
41
43
  'version': z.intersection(z.string(), z.unknown()).readonly(),
42
44
  'createdAt': z.number().readonly(),
43
45
  'updatedAt': z.number().readonly(),
@@ -55,6 +57,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_put_parameter_0$schema =
55
57
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
56
58
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
57
59
  'note': z.string().readonly().optional(),
60
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
58
61
  'ifVersion': z.union([z.literal(null), z.intersection(z.string(), z.unknown())]).readonly(),
59
62
  })
60
63
  const _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema = z.union([z.object({
@@ -63,6 +66,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema = z.un
63
66
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
64
67
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
65
68
  'note': z.string().readonly().optional(),
69
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
66
70
  'version': z.intersection(z.string(), z.unknown()).readonly(),
67
71
  'createdAt': z.number().readonly(),
68
72
  'updatedAt': z.number().readonly(),
@@ -82,6 +86,7 @@ const _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema = z.un
82
86
  'messageId': z.intersection(z.string(), z.unknown()).readonly(),
83
87
  'rating': z.union([z.literal("positive"), z.literal("negative")]).readonly(),
84
88
  'note': z.string().readonly().optional(),
89
+ 'category': z.union([z.literal("other"), z.literal("task-result"), z.literal("instruction-following"), z.literal("product-interaction"), z.literal("service-stability"), z.literal("resource-cost"), z.literal("security-privacy-permission")]).readonly().optional(),
85
90
  'version': z.intersection(z.string(), z.unknown()).readonly(),
86
91
  'createdAt': z.number().readonly(),
87
92
  'updatedAt': z.number().readonly(),
@@ -121,7 +126,7 @@ export const TYPERT_REMOTE = {
121
126
  typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackDeleteResult',
122
127
  schema: _deepseek_ai_dsh_message_feedback_messageFeedback_delete_result$schema,
123
128
  },
124
- sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":203,"column":3},
129
+ sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":207,"column":3},
125
130
  },
126
131
  {
127
132
  id: '@deepseek-ai/dsh-message-feedback#messageFeedback/list',
@@ -146,7 +151,7 @@ export const TYPERT_REMOTE = {
146
151
  typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackListResult',
147
152
  schema: _deepseek_ai_dsh_message_feedback_messageFeedback_list_result$schema,
148
153
  },
149
- sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":153,"column":3},
154
+ sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":155,"column":3},
150
155
  },
151
156
  {
152
157
  id: '@deepseek-ai/dsh-message-feedback#messageFeedback/put',
@@ -171,7 +176,7 @@ export const TYPERT_REMOTE = {
171
176
  typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackPutResult',
172
177
  schema: _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema,
173
178
  },
174
- sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":165,"column":3},
179
+ sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":167,"column":3},
175
180
  },
176
181
  ],
177
182
  }
@@ -42,6 +42,7 @@ import { isDeepStrictEqual } from 'node:util';
42
42
  import { Service } from '@deepseek-ai/cordis';
43
43
  import s from '@deepseek-ai/schemastery';
44
44
  import { z } from 'zod';
45
+ import { FEEDBACK_CATEGORIES } from '@deepseek-ai/dsh-command-feedback';
45
46
  import { SessionSeq } from '@deepseek-ai/dsh-session/types';
46
47
  import { deriveEventMessage, isAppendSurfaceEvent } from '@deepseek-ai/dsh-session/surface';
47
48
  import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol';
@@ -50,6 +51,7 @@ const itemSchema = z.object({
50
51
  messageId: z.string().min(1),
51
52
  rating: z.enum(['positive', 'negative']),
52
53
  note: z.string().refine(note => note.trim().length > 0).optional(),
54
+ category: z.enum(FEEDBACK_CATEGORIES).optional(),
53
55
  version: z.uuid(),
54
56
  createdAt: timestamp,
55
57
  updatedAt: timestamp,
@@ -160,7 +162,8 @@ let MessageFeedbackService = (() => {
160
162
  if (request.ifVersion !== (existing?.version ?? null)) {
161
163
  return rejected(this.versionConflict(existing ?? null));
162
164
  }
163
- if (existing !== undefined && existing.rating === request.rating && existing.note === note.value) {
165
+ if (existing !== undefined && existing.rating === request.rating && existing.note === note.value
166
+ && existing.category === request.category) {
164
167
  await append();
165
168
  return success(snapshotItem(existing));
166
169
  }
@@ -169,6 +172,7 @@ let MessageFeedbackService = (() => {
169
172
  messageId: request.messageId,
170
173
  rating: request.rating,
171
174
  ...(note.value === undefined ? {} : { note: note.value }),
175
+ ...(request.category === undefined ? {} : { category: request.category }),
172
176
  version: randomUUID(),
173
177
  createdAt: existing?.createdAt ?? now,
174
178
  updatedAt: existing === undefined ? now : Math.max(now, existing.updatedAt),
@@ -7,6 +7,7 @@
7
7
  import type { Branded } from '@deepseek-ai/dsh-brand';
8
8
  import type { MessageId } from '@deepseek-ai/dsh-llm/brand';
9
9
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
10
+ import type { FeedbackCategory } from '@deepseek-ai/dsh-command-feedback/types';
10
11
  /** Opaque compare-and-set token for one exact feedback item revision. */
11
12
  export type MessageFeedbackVersion = Branded<'MessageFeedbackVersion'>;
12
13
  /** The human's overall judgment of one assistant message. */
@@ -19,6 +20,8 @@ export interface MessageFeedbackItem {
19
20
  readonly rating: MessageFeedbackRating;
20
21
  /** Optional explanation, preserved verbatim after validation. */
21
22
  readonly note?: string;
23
+ /** Category the human filed a negative judgment under. */
24
+ readonly category?: FeedbackCategory;
22
25
  /** Equality-only token replaced by every material create or update. */
23
26
  readonly version: MessageFeedbackVersion;
24
27
  /** Host-assigned creation time in Unix epoch milliseconds. */
@@ -68,6 +71,8 @@ export interface MessageFeedbackPutRequest {
68
71
  readonly rating: MessageFeedbackRating;
69
72
  /** Optional non-blank explanation. */
70
73
  readonly note?: string;
74
+ /** Optional category; absent keeps the item uncategorized. */
75
+ readonly category?: FeedbackCategory;
71
76
  /** Observed item version, or `null` to require that no item exists. */
72
77
  readonly ifVersion: MessageFeedbackVersion | null;
73
78
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-message-feedback",
3
3
  "description": "Canonical Session-log ratings and notes for finalized assistant messages",
4
- "version": "0.1.5-alpha.1",
4
+ "version": "0.1.5-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -44,12 +44,13 @@
44
44
  ],
45
45
  "license": "MIT",
46
46
  "peerDependencies": {
47
- "@deepseek-ai/dsh-brand": "^0.1.5-alpha.1",
48
- "@deepseek-ai/dsh-llm": "^0.1.5-alpha.1",
49
- "@deepseek-ai/dsh-session-persistence": "^0.1.5-alpha.1",
50
- "@deepseek-ai/dsh-session": "^0.1.5-alpha.1",
51
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.1",
52
- "@deepseek-ai/cordis": "^4.0.2"
47
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
48
+ "@deepseek-ai/dsh-brand": "^0.1.5-alpha.2",
49
+ "@deepseek-ai/dsh-session-persistence": "^0.1.5-alpha.2",
50
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
51
+ "@deepseek-ai/dsh-command-feedback": "^0.1.5-alpha.2",
52
+ "@deepseek-ai/cordis": "^4.0.2",
53
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.2"
53
54
  },
54
55
  "dependencies": {
55
56
  "zod": "^4.4.3",
@@ -58,12 +59,13 @@
58
59
  "devDependencies": {
59
60
  "@deepseek-ai/cordis-plugin-include": "^1.0.7",
60
61
  "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
61
- "@deepseek-ai/dsh-llm": "^0.1.5-alpha.1",
62
- "@deepseek-ai/dsh-session-persistence": "^0.1.5-alpha.1",
63
- "@deepseek-ai/dsh-session": "^0.1.5-alpha.1",
64
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.1",
62
+ "@deepseek-ai/dsh-llm": "^0.1.5-alpha.2",
63
+ "@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
64
+ "@deepseek-ai/dsh-brand": "^0.1.5-alpha.2",
65
+ "@deepseek-ai/dsh-session-persistence": "^0.1.5-alpha.2",
66
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-alpha.2",
65
67
  "@deepseek-ai/cordis": "^4.0.2",
66
- "@deepseek-ai/dsh-brand": "^0.1.5-alpha.1",
67
- "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.5-alpha.1"
68
+ "@deepseek-ai/dsh-command-feedback": "^0.1.5-alpha.2",
69
+ "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.5-alpha.2"
68
70
  }
69
71
  }