From ed443ed3f64037b7dcdb20d4ba51baafcaed80fc Mon Sep 17 00:00:00 2001 From: weichen Date: Mon, 12 Oct 2026 04:16:34 +0000 Subject: [PATCH] Flatten surface action data into command params Why: system.intent ParamsSchema requires top-level text, but SubmitAction only nested values under data, so button intent always rejected missing text during prod acceptance. Evidence: go test ./internal/surfacesvc/ -run TestSubmitActionFlattensDataIntoParams Impact: B-class intent and other schema-required params work via actions; submit still receives nested data. Refs: GenUI prod deploy acceptance; fix track Co-authored-by: Cursor --- internal/surfacesvc/actions_test.go | 23 +++++++++++++++++++++++ internal/surfacesvc/surfacesvc.go | 16 +++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/internal/surfacesvc/actions_test.go b/internal/surfacesvc/actions_test.go index 68fd042..b125f96 100644 --- a/internal/surfacesvc/actions_test.go +++ b/internal/surfacesvc/actions_test.go @@ -99,6 +99,29 @@ func TestSubmitActionBClassHelpDispatches(t *testing.T) { } } +func TestSubmitActionFlattensDataIntoParams(t *testing.T) { + f := newActionFixture(t) + ctx := context.Background() + var params map[string]any + f.svc.SetDispatcher(func(ctx context.Context, d ActionDispatch) (ActionOutcome, error) { + params = d.Params + return ActionOutcome{State: "committed", Payload: map[string]any{"matched": false}}, nil + }) + res, err := f.svc.SubmitAction(ctx, "alice", SubmitActionRequest{SurfaceID: f.surface.SurfaceID, + ActionID: "system.intent", Revision: f.rev, RequestID: "intent-1", + Data: map[string]any{"text": "请新建会话"}}) + if err != nil || res.State != "committed" { + t.Fatalf("intent action: res=%+v err=%v", res, err) + } + if params["text"] != "请新建会话" { + t.Fatalf("data.text must flatten to top-level params for ParamsSchema: %+v", params) + } + data, _ := params["data"].(map[string]any) + if data["text"] != "请新建会话" { + t.Fatalf("nested data must remain for submit-shaped consumers: %+v", params) + } +} + func TestSubmitActionGuards(t *testing.T) { f := newActionFixture(t) ctx := context.Background() diff --git a/internal/surfacesvc/surfacesvc.go b/internal/surfacesvc/surfacesvc.go index 515c6a9..d0d1d38 100644 --- a/internal/surfacesvc/surfacesvc.go +++ b/internal/surfacesvc/surfacesvc.go @@ -312,10 +312,20 @@ func (s *Service) SubmitAction(ctx context.Context, owner string, req SubmitActi Note: "幂等重放:返回已有回执", }, nil } + // 参数形态:保留 data 供 generative-ui.submit;同时把 data 键上浮到顶层, + // 以便 system.intent 等命令的 ParamsSchema(required: text)能直接校验。 + params := map[string]any{ + "surfaceId": string(req.SurfaceID), "revision": req.Revision, + "requestId": req.RequestID, "data": req.Data, + } + for k, v := range req.Data { + if k == "surfaceId" || k == "revision" || k == "requestId" || k == "data" { + continue + } + params[k] = v + } out, err := s.dispatch(ctx, ActionDispatch{ - CommandID: cmd, Owner: owner, ConversationID: rec.ConversationID, - Params: map[string]any{"surfaceId": string(req.SurfaceID), "revision": req.Revision, - "requestId": req.RequestID, "data": req.Data}, + CommandID: cmd, Owner: owner, ConversationID: rec.ConversationID, Params: params, }) state, cardID := out.State, out.CardID if err != nil {