feat(devapp): cards opt-in via template; default replies are plain text + reactions

Mirror hermes' semantics: without a configured card template, replies go as
plain text/markdown with the Thinking/Done reaction chips (which need no
template and always render); cards activate only when --card-template /
DWS_CARD_TEMPLATE is set ('public' opts into the openclaw shared template
explicitly). This removes the silent-failure trap where every card API call
succeeds but the client cannot render a template it is not authorized for —
the previous default left users staring at 内容加载失败 instead of falling
back to text.
This commit is contained in:
修雨 committed 2026-06-10 21:43:42 +08:00
1 parent 99893e473a
commit f9abc79ecc
7 files changed
+38 -16

No files matched your search

+8 -6
View File
@@ -95,7 +95,7 @@ func TestAICardCreateFinalizeSequence(t *testing.T) {
rec, srv := newCardAPIServer(t)
withCardAPIBase(t, srv.URL)
c := newAICardClient("ding-client", "ding-secret", "")
c := newAICardClient("ding-client", "ding-secret", defaultAICardTemplateID)
card, err := c.createAndDeliver(context.Background(), groupCallback())
if err != nil {
t.Fatalf("createAndDeliver: %v", err)
@@ -164,7 +164,7 @@ func TestAICardDeliverOneToOne(t *testing.T) {
rec, srv := newCardAPIServer(t)
withCardAPIBase(t, srv.URL)
c := newAICardClient("ding-client", "ding-secret", "")
c := newAICardClient("ding-client", "ding-secret", defaultAICardTemplateID)
data := &chatbot.BotCallbackDataModel{ConversationType: "1", SenderStaffId: "staff-9"}
if _, err := c.createAndDeliver(context.Background(), data); err != nil {
t.Fatalf("createAndDeliver: %v", err)
@@ -186,7 +186,7 @@ func TestAICardCreateFailure(t *testing.T) {
withCardAPIBase(t, srv.URL)
rec.fail["POST /v1.0/card/instances"] = 500
c := newAICardClient("ding-client", "ding-secret", "")
c := newAICardClient("ding-client", "ding-secret", defaultAICardTemplateID)
if _, err := c.createAndDeliver(context.Background(), groupCallback()); err == nil {
t.Fatal("want error when card create fails")
}
@@ -204,7 +204,7 @@ func TestAICardMarkFailed(t *testing.T) {
rec, srv := newCardAPIServer(t)
withCardAPIBase(t, srv.URL)
c := newAICardClient("ding-client", "ding-secret", "")
c := newAICardClient("ding-client", "ding-secret", defaultAICardTemplateID)
c.markFailed(context.Background(), &aiCardInstance{outTrackID: "dws_x"})
last := rec.bodies[len(rec.bodies)-1]
pm := last["cardData"].(map[string]any)["cardParamMap"].(map[string]any)
@@ -220,7 +220,9 @@ func TestRobotConnectReplyCardFlag(t *testing.T) {
args []string
want string
}{
{"default on", nil, `"replyStyle": "ai-card"`},
{"default: no template -> text + emotions", nil, "thinking/done表态"},
{"template configured -> ai-card", []string{"--card-template", "tpl-1.schema"}, `"replyStyle": "ai-card"`},
{"public alias -> ai-card", []string{"--card-template", "public"}, `"replyStyle": "ai-card"`},
{"explicit off", []string{"--reply-card=false"}, `"replyStyle": "text/markdown"`},
} {
t.Run(tc.name, func(t *testing.T) {
@@ -248,7 +250,7 @@ func TestAICardEmotions(t *testing.T) {
rec, srv := newCardAPIServer(t)
withCardAPIBase(t, srv.URL)
c := newAICardClient("ding-client", "ding-secret", "")
c := newAICardClient("ding-client", "ding-secret", defaultAICardTemplateID)
if err := c.markThinking(context.Background(), "cid-1", "msg-1"); err != nil {
t.Fatalf("markThinking: %v", err)
}