Flatten surface action data into command params
ci / vet-and-test (pull_request) Successful in 47s

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 <cursoragent@cursor.com>
This commit is contained in:
2026-10-12 04:16:34 +00:00
co-authored by Cursor
parent c7b30d2109
commit ed443ed3f6
2 changed files with 36 additions and 3 deletions
+23
View File
@@ -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()
+13 -3
View File
@@ -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 {