fix(chat): expose idempotency key for message send

This commit is contained in:
长真
2026-08-13 13:28:00 +08:00
parent 891dde7d03
commit d32bbe009d
5 changed files with 49 additions and 6 deletions
+24 -1
View File
@@ -324,7 +324,7 @@ func TestCrossPlatformCoverageParamAliasWriteCommandFinalPayload(t *testing.T) {
"chat", "message", "send",
"--to-user", "D-recipient",
"--text", "hello alias",
"--uuid", "alias-e2e",
"--idempotency-key", "alias-e2e",
)
if err != nil {
t.Fatalf("chat write alias E2E error = %v", err)
@@ -350,6 +350,29 @@ func TestCrossPlatformCoverageParamAliasWriteCommandFinalPayload(t *testing.T) {
}
}
func TestCrossPlatformCoverageChatMessageSendLegacyUUIDAliasFinalPayload(t *testing.T) {
caller := &paramAliasCaptureCaller{}
_, err := executeParamAliasE2E(t, caller,
"chat", "message", "send",
"--group", "fixture-conversation",
"--text", "hello legacy uuid",
"--uuid", "legacy-alias-e2e",
)
if err != nil {
t.Fatalf("chat message send legacy uuid error = %v", err)
}
if len(caller.calls) != 1 || caller.calls[0].tool != "send_personal_message" {
t.Fatalf("chat calls = %#v", caller.calls)
}
payload := caller.calls[0].args
if payload["uuid"] != "legacy-alias-e2e" || payload["openConversationId"] != "fixture-conversation" {
t.Fatalf("chat legacy uuid payload = %#v", payload)
}
if _, exists := payload["idempotency-key"]; exists {
t.Fatalf("chat payload leaked CLI-only idempotency-key: %#v", payload)
}
}
func TestCrossPlatformCoverageChatReactionConversationAliasesReachCanonicalPayload(t *testing.T) {
tests := []struct {
name string
@@ -88,7 +88,7 @@ var paramAliasCompleteCommands = map[string][]string{
"chat message recall": {"chat", "message", "recall", "--conversation-id", "fixture-conversation", "--msg-id", "message-1", "--yes"},
"chat message reply": {"chat", "message", "reply", "--conversation-id", "fixture-conversation", "--ref-msg-id", "message-1", "--ref-sender", "D-sender", "--text", "hello fixture", "--yes"},
"chat message search-advanced": {"chat", "message", "search-advanced", "--conversation-ids", "fixture-conversation", "--query", "fixture"},
"chat message send": {"chat", "message", "send", "--user", "D-recipient", "--text", "hello fixture", "--uuid", "param-alias-equivalence", "--yes"},
"chat message send": {"chat", "message", "send", "--user", "D-recipient", "--text", "hello fixture", "--idempotency-key", "param-alias-equivalence", "--yes"},
"chat message send-by-bot": {"chat", "message", "send-by-bot", "--robot-code", "robot-1", "--group", "fixture-conversation", "--title", "Fixture Alert", "--text", "@user-1 @user-2 fixture", "--at-user-ids", "user-1,user-2", "--yes"},
"chat message send-by-webhook": {"chat", "message", "send-by-webhook", "--token", "fixture-token", "--title", "Fixture Alert", "--text", "fixture", "--at-users", "user-1,user-2", "--yes"},
"contact +dept-members": {"contact", "+dept-members", "--dept", "Fixture Dept"},
@@ -163,8 +163,8 @@ var paramAliasCompleteCommandVariants = map[string]map[string][]string{
"sender-open-dingtalk-id": {"chat", "message", "list-by-sender", "--sender-open-dingtalk-id", "D-sender", "--start", "2026-03-10T00:00:00+08:00", "--end", "2026-03-11T00:00:00+08:00", "--limit", "7", "--cursor", "0"},
},
"chat message send": {
"group": {"chat", "message", "send", "--group", "fixture-conversation", "--text", "hello fixture", "--uuid", "param-alias-equivalence-group", "--yes"},
"file-path": {"chat", "message", "send", "--group", "fixture-conversation", "--msg-type", "file", "--file-path", "../../go.mod", "--dentry-id", "1", "--space-id", "2", "--uuid", "param-alias-equivalence-file", "--yes"},
"group": {"chat", "message", "send", "--group", "fixture-conversation", "--text", "hello fixture", "--idempotency-key", "param-alias-equivalence-group", "--yes"},
"file-path": {"chat", "message", "send", "--group", "fixture-conversation", "--msg-type", "file", "--file-path", "../../go.mod", "--dentry-id", "1", "--space-id", "2", "--idempotency-key", "param-alias-equivalence-file", "--yes"},
},
"chat +conversation-set-top": {
"conversation-ids": {"chat", "+conversation-set-top", "--conversation-ids", "fixture-conversation-1,fixture-conversation-2", "--yes"},
+8
View File
@@ -369,6 +369,14 @@ func TestChatFileUploadDownlinedButMessageFileSendStays(t *testing.T) {
t.Fatalf("chat message send missing --%s", flag)
}
}
idempotencyKey := send.Flags().Lookup("idempotency-key")
if idempotencyKey == nil {
t.Fatal("chat message send missing --idempotency-key")
}
legacyUUID := send.Flags().Lookup("uuid")
if legacyUUID == nil || !legacyUUID.Hidden {
t.Fatalf("chat message send --uuid hidden = %#v, want hidden compatibility flag", legacyUUID)
}
got, err := executeRootCaptureStdout(t, []string{
"chat", "file", "upload",
+9
View File
@@ -1356,6 +1356,7 @@ func TestDeliveryCatalogChatParamDeclsFrom87910880Reviewed(t *testing.T) {
{"chat message edit", "conversation-id", "openConversationId", true, ""},
{"chat message edit", "msg-id", "openMessageId", true, ""},
{"chat message edit", "at-open-dingtalk-ids", "atOpenDingTalkIds", false, "array"},
{"chat message send", "idempotency-key", "uuid", false, ""},
{"chat message send-card", "at-all", "atAll", false, ""},
{"chat message send-card", "at-open-dingtalk-ids", "atOpenDingTalkIds", false, "array"},
{"chat message update-text-emotion", "msg-id", "openMsgId", true, ""},
@@ -1407,6 +1408,14 @@ func TestDeliveryCatalogChatParamDeclsFrom87910880Reviewed(t *testing.T) {
t.Fatalf("chat message edit unexpectedly publishes hidden alias --%s", hidden)
}
}
sendLeaf, err = queryDeliverySchemaPayload([]string{"chat message send"})
if err != nil {
t.Fatal(err)
}
sendParams = schemaMap(sendLeaf["parameters"])
if _, ok := sendParams["uuid"]; ok {
t.Fatal("chat message send unexpectedly publishes hidden alias --uuid")
}
listByConv, err := queryDeliverySchemaPayload([]string{"chat category list-by-conv"})
if err != nil {
t.Fatal(err)
+5 -2
View File
@@ -2835,7 +2835,7 @@ func newChatCommand() *cobra.Command {
groupID := flagOrFallback(cmd, "group", "conversation-id", "id", "chat")
userID, _ := cmd.Flags().GetString("user")
openDingTalkID, _ := cmd.Flags().GetString("open-dingtalk-id")
msgUuid, _ := cmd.Flags().GetString("uuid")
msgUuid := flagOrFallback(cmd, "idempotency-key", "uuid")
specified := 0
if groupID != "" {
specified++
@@ -3050,6 +3050,7 @@ func newChatCommand() *cobra.Command {
{Name: "ai-tag", Property: "clawType", InterfaceType: "string"},
{Name: "at-open-dingtalk-ids", Property: "atOpenDingTalkIds"},
{Name: "group", Property: "openConversationId"},
{Name: "idempotency-key", Property: "uuid"},
{Name: "open-dingtalk-id", Property: "receiverOpenDingTalkId"},
},
},
@@ -4385,7 +4386,9 @@ chat message edit 或 chat message recall 的 --msg-id 和 --conversation-id。
_ = chatMessageSendCmd.Flags().MarkHidden("file-type")
_ = chatMessageSendCmd.Flags().MarkHidden("file-size")
chatMessageSendCmd.Flags().Bool("ai-tag", true, "消息是否带 AI 发送角标(默认 true)")
chatMessageSendCmd.Flags().String("uuid", "", "幂等 UUID,相同 uuid 在 24h 内不会重复发送(可选)")
chatMessageSendCmd.Flags().String("idempotency-key", "", "幂等键,相同 key 在 24h 内不会重复发送(可选)")
chatMessageSendCmd.Flags().String("uuid", "", "--idempotency-key 的兼容别名")
_ = chatMessageSendCmd.Flags().MarkHidden("uuid")
cli.AttachRuntimeSchema(chatMessageSendCmd, "chat", "send_personal_message", "hardcoded:chat")
cli.AnnotateRuntimeConstraints(chatMessageSendCmd, cli.RuntimeSchemaConstraints{
MutuallyExclusive: [][]string{{"group", "user", "open-dingtalk-id"}},