feat(feedback): open one feedback dialog from /feedback and Dislike, with categories and a toast

Dislike and a bare /feedback open the Session's feedback dialog, which
collects one of seven fixed categories and an optional description and
records through the messageFeedback put or the new sessionFeedback
Remote; Like records at once. A recorded rating shows the filled glyph
and every submission is acknowledged by a toast. feedback/record and
MessageFeedbackItem carry the optional category; the note popover, its
layout scenario, and the clearNote/clear verbs are removed. ui-commands
gains the action command kind the /feedback decoration runs as.

Closes #3515
This commit is contained in:
creatixchu
2026-09-09 10:20:24 +08:00
parent e9b3f80f0b
commit f2f9700de2
102 changed files with 2321 additions and 1874 deletions
+3
View File
@@ -85,6 +85,7 @@ export const SERVICE_PAGE: Record<string, string> = {
llm: 'llm-streaming.md',
lsp: 'lsp.md',
messageFeedback: 'feedback.md',
sessionFeedback: 'feedback.md',
permissionPresets: 'permission-presets.md',
planMode: 'plan.md',
terminals: 'terminal.md',
@@ -288,6 +289,8 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
MessageFeedbackFailure: 'feedback.md',
MessageFeedbackItem: 'feedback.md',
MessageFeedbackListRequest: 'feedback.md',
SessionFeedbackRecordRequest: 'feedback.md',
SessionFeedbackRecordResult: 'feedback.md',
MessageFeedbackListResult: 'feedback.md',
MessageFeedbackListValue: 'feedback.md',
MessageFeedbackNoteBlank: 'feedback.md',
+7
View File
@@ -313,6 +313,13 @@ const SERVICE_ROLES: ServiceRole[] = [
mode: 'core',
note: 'Owns per-assistant-message feedback in the canonical Session log, target validation, per-item compare-and-set, and the Host unary Remote contract. Feedback stays outside model history; log export follows the consumer policy.',
},
{
key: 'sessionFeedback',
pkg: 'command-feedback',
title: 'Session-level feedback recorder',
mode: 'core',
note: 'Records one Session-level remark with its category as a log-only feedback/record event on a live Session through the Host unary Remote contract; the /feedback command shares the same producer.',
},
{
key: 'workspaceRegistry',
pkg: 'workspace',
+1 -1
View File
@@ -1321,7 +1321,7 @@ def smoke_sdk_snapshot(base_url: str, executable: Path, update_snapshots: bool)
assert result.final_response == SNAPSHOT_FINAL_TEXT, result.final_response
feedback_types = [event.get("type") for event in result.events
if str(event.get("type")).startswith("feedback/")]
if feedback_types != ["feedback/record", "feedback/message-put", "feedback/message-put", "feedback/message-delete"]:
if feedback_types != ["feedback/record", "feedback/record", "feedback/message-put", "feedback/message-put", "feedback/message-delete"]:
raise AssertionError(f"advanced snapshot did not exercise all feedback mutations: {feedback_types}")
methods = [notification.method for notification in result.notifications]
if methods.count("subagent.started") != 2 or methods.count("subagent.finished") != 2:
@@ -61,7 +61,8 @@
{"type":"command/run","data":{"commandId":"{{command:1}}","name":"feedback","source":{"kind":"user"}}}
{"type":"feedback/record","data":{"text":"The session needs a clearer explanation."}}
{"type":"command/done","data":{"commandId":"{{command:1}}","kind":"success","text":"Feedback recorded for session {{parent}}\nAnonymous user: {{anonymous-user}}."}}
{"type":"feedback/message-put","data":{"sessionId":"{{parent}}","item":{"messageId":"{{message:1}}","rating":"negative","note":"Explain the result.","version":"{{feedback-version:1}}","createdAt":0,"updatedAt":0}}}
{"type":"feedback/record","data":{"category":"other"}}
{"type":"feedback/message-put","data":{"sessionId":"{{parent}}","item":{"messageId":"{{message:1}}","rating":"negative","note":"Explain the result.","category":"task-result","version":"{{feedback-version:1}}","createdAt":0,"updatedAt":0}}}
{"type":"feedback/message-put","data":{"sessionId":"{{parent}}","item":{"messageId":"{{message:1}}","rating":"positive","note":"The explanation is clear now.","version":"{{feedback-version:2}}","createdAt":0,"updatedAt":0}}}
{"type":"feedback/message-delete","data":{"sessionId":"{{parent}}","messageId":"{{message:1}}"}}
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
+30
View File
@@ -2071,6 +2071,36 @@
"symbol": "MessageFeedbackDeleteResult",
"source": "packages/feedback/message-feedback/src/types.ts"
},
{
"doc": "docs/subsystems/feedback.md",
"symbol": "FeedbackCategory",
"source": "packages/feedback/command-feedback/src/types.ts"
},
{
"doc": "docs/subsystems/feedback.md",
"symbol": "FeedbackRecord",
"source": "packages/feedback/command-feedback/src/types.ts"
},
{
"doc": "docs/subsystems/feedback.md",
"symbol": "SessionFeedbackRecordRequest",
"source": "packages/feedback/command-feedback/src/types.ts"
},
{
"doc": "docs/subsystems/feedback.md",
"symbol": "SessionFeedbackRecordValue",
"source": "packages/feedback/command-feedback/src/types.ts"
},
{
"doc": "docs/subsystems/feedback.md",
"symbol": "SessionFeedbackSessionNotFound",
"source": "packages/feedback/command-feedback/src/types.ts"
},
{
"doc": "docs/subsystems/feedback.md",
"symbol": "SessionFeedbackRecordResult",
"source": "packages/feedback/command-feedback/src/types.ts"
},
{
"doc": "packages/client/ui-conversation/README.md",
"symbol": "ComposerChainProps",