fix(im): harden shortcut review follow-ups

This commit is contained in:
Dennis committed 2026-07-29 23:35:52 +08:00
1 parent bf79a67efe
commit 2b48f27a4b
18 files changed
+290 -242

No files matched your search

+28 -28
View File
@@ -10380,14 +10380,14 @@
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"candidates": [
{
"value": "创建流式卡片,可在同一次调用中写入内容并结束",
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting."
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting."
}
]
},
@@ -10396,14 +10396,14 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": "available",
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
}
]
},
@@ -10414,7 +10414,7 @@
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"candidates": [
{
"value": [
@@ -10423,7 +10423,7 @@
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting."
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting."
}
]
},
@@ -10432,14 +10432,14 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": "user_required",
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
}
]
},
@@ -10448,14 +10448,14 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": "write",
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
}
]
},
@@ -10467,7 +10467,7 @@
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"candidates": [
{
"value": [
@@ -10477,7 +10477,7 @@
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting."
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting."
}
]
},
@@ -10486,14 +10486,14 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": "unknown",
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
}
]
},
@@ -10502,14 +10502,14 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": "composite",
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
}
]
},
@@ -10518,14 +10518,14 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": "Reviewed card lifecycle adapter: it can call create_and_send_card alone or compose it with update_streaming_card after extracting the returned bizId.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
}
]
},
@@ -10550,21 +10550,21 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
},
{
"value": true,
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"selected": false,
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting."
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting."
}
]
},
@@ -10573,34 +10573,34 @@
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"candidates": [
{
"value": "medium",
"source": "internal/cli/schema_hints/metadata/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId."
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property."
}
]
},
"use_when": {
"value": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
],
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"candidates": [
{
"value": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
],
"source": "internal/cli/schema_hints/selection/chat.json",
"precedence": "reviewed_explicit",
"selected": true,
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting."
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting."
}
]
}
@@ -10618,7 +10618,7 @@
"internal/cli/schema_hints/selection/chat.json"
],
"use_when": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
]
},
"chat +messages-update-card": {
@@ -1,6 +1,6 @@
{
"version": 1,
"source_hash": "sha256:f48d04b4c2f9494460cba8d38ef6b6b8dd5a0172450f6f06123f1b696aaef497",
"source_hash": "sha256:f7afa1d0abaa0c3d4ead4c6262948b7565787abb0860bb724276d1d3fe52178b",
"surface_hash": "sha256:60eee8e2f37d6d9d60689efce85082798eb9ad38b7ba7c0b471c3de676a85a16",
"coverage": {
"surface_products": 26,
@@ -1,6 +1,6 @@
{
"version": 1,
"source_hash": "sha256:f48d04b4c2f9494460cba8d38ef6b6b8dd5a0172450f6f06123f1b696aaef497",
"source_hash": "sha256:f7afa1d0abaa0c3d4ead4c6262948b7565787abb0860bb724276d1d3fe52178b",
"surface_hash": "sha256:60eee8e2f37d6d9d60689efce85082798eb9ad38b7ba7c0b471c3de676a85a16",
"source_files": 160,
"hint_files": 54,
+4 -4
View File
@@ -1,12 +1,12 @@
{
"version": 1,
"surface_hash": "sha256:60eee8e2f37d6d9d60689efce85082798eb9ad38b7ba7c0b471c3de676a85a16",
"source_hash": "sha256:46e9ac44ad12175deac0891886039e6a0a2436208b29b6393fd39452ba285a25",
"source_hash": "sha256:8fcebd5100f2727898290118b2b381cc3edc325bc1c8526e61036da5482e025b",
"catalog": {
"agent_metadata": {
"products_with_metadata": 26,
"source": "embedded-skill-metadata",
"source_hash": "sha256:f48d04b4c2f9494460cba8d38ef6b6b8dd5a0172450f6f06123f1b696aaef497",
"source_hash": "sha256:f7afa1d0abaa0c3d4ead4c6262948b7565787abb0860bb724276d1d3fe52178b",
"surface_hash": "sha256:60eee8e2f37d6d9d60689efce85082798eb9ad38b7ba7c0b471c3de676a85a16",
"surface_products": 26,
"surface_tools": 845,
@@ -10840,7 +10840,7 @@
"cli_name": "+messages-send-card",
"cli_path": "chat +messages-send-card",
"confirmation": "user_required",
"description": "当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content",
"description": "当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver、--receiver-open-dingtalk-id 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content",
"effect": "write",
"idempotency": "unknown",
"interface_mode": "composite",
@@ -10851,7 +10851,7 @@
"risk": "medium",
"title": "创建流式卡片,可在同一次调用中写入内容并结束",
"use_when": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
]
},
{
+156 -40
View File
@@ -77349,17 +77349,19 @@
"mutually_exclusive": [
[
"group",
"receiver"
"receiver",
"receiver-open-dingtalk-id"
]
],
"require_one_of": [
[
"group",
"receiver"
"receiver",
"receiver-open-dingtalk-id"
]
]
},
"description": "当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content",
"description": "当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver、--receiver-open-dingtalk-id 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content",
"display": "群聊 / 消息 / 机器人",
"effect": "write",
"effect_source": "agent-hint",
@@ -77372,7 +77374,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"selected": true,
"source": "internal/cli/schema_hints/selection/chat.json",
"value": "创建流式卡片,可在同一次调用中写入内容并结束"
@@ -77380,7 +77382,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"source": "internal/cli/schema_hints/selection/chat.json",
"value": "创建流式卡片,可在同一次调用中写入内容并结束"
},
@@ -77388,7 +77390,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "available"
@@ -77396,7 +77398,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "available"
},
@@ -77404,7 +77406,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"selected": true,
"source": "internal/cli/schema_hints/selection/chat.json",
"value": [
@@ -77414,7 +77416,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"source": "internal/cli/schema_hints/selection/chat.json",
"value": [
"已有 bizId、只需要追加或更新现有卡片内容时使用 +messages-update-card"
@@ -77428,6 +77430,14 @@
"source": "reviewed_command_registry",
"source_ref": "chat +messages-send-card",
"value": "chat.shortcut_messages_send_card"
},
{
"precedence": "reviewed_manual",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": false,
"source": "reviewed_manual_hint",
"source_ref": "chat +messages-send-card",
"value": "chat.shortcut_messages_send_card"
}
],
"precedence": "command_registry",
@@ -77440,7 +77450,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "user_required"
@@ -77448,7 +77458,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "user_required"
},
@@ -77458,19 +77468,19 @@
"precedence": "cobra_help",
"selected": true,
"source": "cobra_help",
"value": "当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content"
"value": "当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver、--receiver-open-dingtalk-id 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content"
}
],
"precedence": "cobra_help",
"resolution": "highest_precedence",
"source": "cobra_help",
"value": "当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content"
"value": "当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。\n\n参数约束:\n - --group、--receiver、--receiver-open-dingtalk-id 必须且只能指定一个\n - --flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content"
},
"effect": {
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "write"
@@ -77478,7 +77488,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "write"
},
@@ -77486,7 +77496,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"selected": true,
"source": "internal/cli/schema_hints/selection/chat.json",
"value": [
@@ -77497,7 +77507,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"source": "internal/cli/schema_hints/selection/chat.json",
"value": [
"dws chat +messages-send-card --group \u003copenConversationId\u003e --content \"任务已完成\"",
@@ -77508,7 +77518,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "unknown"
@@ -77516,7 +77526,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "unknown"
},
@@ -77524,7 +77534,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "composite"
@@ -77532,7 +77542,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "composite"
},
@@ -77540,7 +77550,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "Reviewed card lifecycle adapter: it can call create_and_send_card alone or compose it with update_streaming_card after extracting the returned bizId."
@@ -77548,7 +77558,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "Reviewed card lifecycle adapter: it can call create_and_send_card alone or compose it with update_streaming_card after extracting the returned bizId."
},
@@ -77572,14 +77582,14 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": true
},
{
"precedence": "reviewed_explicit",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"selected": false,
"source": "internal/cli/schema_hints/selection/chat.json",
"value": true
@@ -77587,7 +77597,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": true
},
@@ -77595,7 +77605,7 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "medium"
@@ -77603,7 +77613,7 @@
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "internal/cli/schema_hints/metadata/chat.json",
"value": "medium"
},
@@ -77625,20 +77635,20 @@
"candidates": [
{
"precedence": "reviewed_explicit",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"selected": true,
"source": "internal/cli/schema_hints/selection/chat.json",
"value": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
]
}
],
"precedence": "reviewed_explicit",
"resolution": "highest_precedence",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"source": "internal/cli/schema_hints/selection/chat.json",
"value": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
]
}
},
@@ -77648,7 +77658,7 @@
"interface_reason": "Reviewed card lifecycle adapter: it can call create_and_send_card alone or compose it with update_streaming_card after extracting the returned bizId.",
"is_alias": false,
"name": "shortcut_messages_send_card",
"parameter_count": 4,
"parameter_count": 5,
"parameters": {
"content": {
"description": "创建后立即写入的卡片内容;省略时仅创建并返回 bizId",
@@ -77826,7 +77836,7 @@
"type": "integer"
},
"group": {
"description": "群 openConversationId(与 --receiver 互斥)",
"description": "群 openConversationId(与两个单聊接收者参数互斥)",
"field_provenance": {
"description": {
"candidates": [
@@ -77834,7 +77844,7 @@
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "群 openConversationId(与 --receiver 互斥)"
"value": "群 openConversationId(与两个单聊接收者参数互斥)"
},
{
"precedence": "default",
@@ -77846,7 +77856,7 @@
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "群 openConversationId(与 --receiver 互斥)"
"value": "群 openConversationId(与两个单聊接收者参数互斥)"
},
"property": {
"candidates": [
@@ -77916,7 +77926,7 @@
"type": "string"
},
"receiver": {
"description": "单聊接收者 userId 或 openDingTalkId(与 --group 互斥);userId 包括在 --dry-run 时也会先只读解析",
"description": "单聊接收者 userId(与 --group/--receiver-open-dingtalk-id 互斥);始终只读解析为 openDingTalkId,包括 --dry-run 和 D/d 开头的 userId",
"field_provenance": {
"description": {
"candidates": [
@@ -77924,7 +77934,7 @@
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "单聊接收者 userId 或 openDingTalkId(与 --group 互斥);userId 包括在 --dry-run 时也会先只读解析"
"value": "单聊接收者 userId(与 --group/--receiver-open-dingtalk-id 互斥);始终只读解析为 openDingTalkId,包括 --dry-run 和 D/d 开头的 userId"
},
{
"precedence": "default",
@@ -77936,7 +77946,7 @@
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "单聊接收者 userId 或 openDingTalkId(与 --group 互斥);userId 包括在 --dry-run 时也会先只读解析"
"value": "单聊接收者 userId(与 --group/--receiver-open-dingtalk-id 互斥);始终只读解析为 openDingTalkId,包括 --dry-run 和 D/d 开头的 userId"
},
"property": {
"candidates": [
@@ -78004,6 +78014,112 @@
"property": "receiver",
"required": false,
"type": "string"
},
"receiver-open-dingtalk-id": {
"description": "单聊接收者 openDingTalkId(与 --group/--receiver 互斥);显式直传且不做通讯录解析",
"field_provenance": {
"description": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_usage",
"value": "单聊接收者 openDingTalkId(与 --group/--receiver 互斥);显式直传且不做通讯录解析"
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": ""
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_usage",
"value": "单聊接收者 openDingTalkId(与 --group/--receiver 互斥);显式直传且不做通讯录解析"
},
"property": {
"candidates": [
{
"precedence": "reviewed_manual",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "reviewed_manual_hint",
"value": "receiverOpenDingTalkId"
},
{
"precedence": "inference",
"selected": false,
"source": "flag_name_inference",
"value": "receiverOpenDingtalkId"
}
],
"precedence": "reviewed_manual",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "reviewed_manual_hint",
"value": "receiverOpenDingTalkId"
},
"required": {
"candidates": [
{
"precedence": "reviewed_manual",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"selected": true,
"source": "reviewed_manual_hint",
"value": false
},
{
"precedence": "command_constraint",
"selected": false,
"source": "require_one_of_constraint",
"value": false
},
{
"precedence": "default",
"selected": false,
"source": "default",
"value": false
}
],
"precedence": "reviewed_manual",
"resolution": "highest_precedence",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"source": "reviewed_manual_hint",
"value": false
},
"required_when": {
"candidates": [
{
"precedence": "default",
"selected": true,
"source": "default",
"value": ""
}
],
"precedence": "default",
"resolution": "highest_precedence",
"source": "default",
"value": ""
},
"type": {
"candidates": [
{
"precedence": "cobra_contract",
"selected": true,
"source": "cobra_flag_type",
"value": "string"
}
],
"precedence": "cobra_contract",
"resolution": "highest_precedence",
"source": "cobra_flag_type",
"value": "string"
}
},
"property": "receiverOpenDingTalkId",
"required": false,
"type": "string"
}
},
"path": "chat.shortcut_messages_send_card",
@@ -78014,7 +78130,7 @@
"source": "reviewed_command_registry",
"title": "创建流式卡片,可在同一次调用中写入内容并结束",
"use_when": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
]
},
"chat.shortcut_messages_update_card": {
+7 -1
View File
@@ -1199,8 +1199,14 @@
"interface_mode": "composite",
"availability": "available",
"interface_reason": "Reviewed card lifecycle adapter: it can call create_and_send_card alone or compose it with update_streaming_card after extracting the returned bizId.",
"parameters": {
"receiver-open-dingtalk-id": {
"property": "receiverOpenDingTalkId",
"required": false
}
},
"reviewed": true,
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId.",
"review_reason": "Reviewed against create-only, two-action dry-run, create/update success, missing bizId, and partial-failure behavior that preserves the created bizId; the explicit openDingTalkId flag is bound to the lower interface's exact receiverOpenDingTalkId property.",
"cli_path": "chat +messages-send-card",
"runtime_gate": "typed_yes"
},
@@ -2172,7 +2172,7 @@
"chat.shortcut_messages_send_card": {
"agent_summary": "创建流式卡片,可在同一次调用中写入内容并结束",
"use_when": [
"当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
"当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。"
],
"avoid_when": [
"已有 bizId、只需要追加或更新现有卡片内容时使用 +messages-update-card"
@@ -2182,7 +2182,7 @@
"dws chat +messages-send-card --receiver <userId>"
],
"reviewed": true,
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, two-action dry-run, bizId extraction, and partial-failure reporting.",
"review_reason": "Agent-authored from the verified create-only and create-then-update lifecycle, explicit userId/openDingTalkId routing, two-action dry-run, bizId extraction, and partial-failure reporting.",
"source_refs": [
"internal/cli/schema_command_registry.json#chat.shortcut_messages_send_card",
"cobra-help:dws chat +messages-send-card",
+24 -12
View File
@@ -660,6 +660,19 @@ func DownloadMessageResources(
"resources": resources,
}
}
if len(resources) == 0 {
return map[string]any{
"ok": true,
"partial": false,
"discoveredCount": discoveredCount,
"requestedCount": 0,
"deduplicatedCount": discoveredCount,
"downloadedCount": 0,
"failedCount": 0,
"downloads": []map[string]any{},
"failures": []map[string]any{},
}
}
cwd, err := resourceGetwd()
if err != nil {
@@ -1002,16 +1015,17 @@ var MessagesSendCard = shortcut.Shortcut{
Command: "+messages-send-card",
Product: "im",
Description: "创建流式卡片,可在同一次调用中写入内容并结束",
Intent: "当你要发送一张流式卡片消息时使用;群 openConversationId 或单聊接收者 userId/openDingTalkId 二选一。单聊目标会统一解析后以 receiverOpenDingTalkId 传给下层;userId 包括在 --dry-run 时也会先做只读通讯录解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。",
Intent: "当你要发送一张流式卡片消息时使用;群 openConversationId、单聊 userId、单聊 openDingTalkId 严格三选一,分别使用 --group、--receiver、--receiver-open-dingtalk-id。--receiver 始终按 userId 做只读通讯录解析,即使值以 D/d 开头也不会猜成 openDingTalkId;已有 openDingTalkId 时必须用显式参数直传。userId 包括在 --dry-run 时也会先解析。只传目标时创建卡片并返回 bizId,供后续 messages-update-card 流式更新;同时传 --content 时会自动串联创建和更新,默认以 flowStatus=3 完成,避免卡片停留在加载中。",
Risk: shortcut.RiskWrite,
Flags: []shortcut.Flag{
{Name: "group", Type: shortcut.FlagString, Desc: "群 openConversationId(与 --receiver 互斥)"},
{Name: "receiver", Type: shortcut.FlagString, Desc: "单聊接收者 userId 或 openDingTalkId(与 --group 互斥);userId 包括在 --dry-run 时也会先只读解析"},
{Name: "group", Type: shortcut.FlagString, Desc: "群 openConversationId(与两个单聊接收者参数互斥)"},
{Name: "receiver", Type: shortcut.FlagString, Desc: "单聊接收者 userId(与 --group/--receiver-open-dingtalk-id 互斥);始终只读解析为 openDingTalkId,包括 --dry-run 和 D/d 开头的 userId"},
{Name: "receiver-open-dingtalk-id", Type: shortcut.FlagString, Desc: "单聊接收者 openDingTalkId(与 --group/--receiver 互斥);显式直传且不做通讯录解析"},
{Name: "content", Type: shortcut.FlagString, Desc: "创建后立即写入的卡片内容;省略时仅创建并返回 bizId"},
{Name: "flow-status", Type: shortcut.FlagInt, Default: "3", Desc: "自动更新状态:1处理中/2输入中/3完成/4执行中/5错误;--flow-status 必须在 1-5 之间,且显式指定时必须同时提供 --content"},
},
Constraints: []shortcut.Constraint{
{Kind: shortcut.ConstraintExactlyOne, Flags: []string{"group", "receiver"}},
{Kind: shortcut.ConstraintExactlyOne, Flags: []string{"group", "receiver", "receiver-open-dingtalk-id"}},
{
Kind: shortcut.ConstraintCustom,
Flags: []string{"flow-status"},
@@ -1034,21 +1048,19 @@ var MessagesSendCard = shortcut.Shortcut{
Execute: func(rt *shortcut.RuntimeContext) error {
group := rt.Str("group")
receiver := rt.Str("receiver")
if group == "" && receiver == "" {
return fmt.Errorf("--group 或 --receiver 必填其一")
}
if group != "" && receiver != "" {
return fmt.Errorf("--group 与 --receiver 互斥")
}
receiverOpenID := rt.Str("receiver-open-dingtalk-id")
params := map[string]any{}
if group != "" {
switch {
case group != "":
params["openConversationId"] = group
} else {
case receiver != "":
openID, err := resolveUserOpenDingTalkID(rt, receiver)
if err != nil {
return err
}
params["receiverOpenDingTalkId"] = openID
default:
params["receiverOpenDingTalkId"] = receiverOpenID
}
content := rt.Str("content")
if content == "" {
@@ -89,6 +89,39 @@ func TestCrossPlatformCoverageMgetResourceDownloadOutcomes(t *testing.T) {
t.Fatalf("getwd ledger = %#v", ledger)
}
})
t.Run("zero resources skip getwd", func(t *testing.T) {
resetResourceDownloadHooks(t)
getwdCalled := false
resourceGetwd = func() (string, error) {
getwdCalled = true
return "", errors.New("getwd")
}
helpers.InitDeps(&larkAlignmentCaller{responses: map[string]string{
"im/list_messages_by_ids": `{"result":[{"openMessageId":"msg","openConversationId":"cid","content":"plain text"}]}`,
}})
root := newPlatformCoverageRoot()
var output bytes.Buffer
root.SetOut(&output)
root.SetArgs(baseArgs)
if err := root.Execute(); err != nil {
t.Fatalf("zero-resource download error = %v", err)
}
if getwdCalled {
t.Fatal("zero-resource download unnecessarily read the working directory")
}
var payload map[string]any
if err := json.Unmarshal(output.Bytes(), &payload); err != nil {
t.Fatal(err)
}
ledger, _ := payload["resourceDownloads"].(map[string]any)
failures, _ := ledger["failures"].([]any)
if ledger["ok"] != true ||
ledger["requestedCount"] != float64(0) ||
ledger["failedCount"] != float64(0) ||
len(failures) != 0 {
t.Fatalf("zero-resource ledger = %#v", ledger)
}
})
cases := []struct {
name string
+10 -5
View File
@@ -56,8 +56,8 @@ func TestMessagesSendPublishesCompleteIdentityConstraintInputs(t *testing.T) {
func TestCrossPlatformCoverageSafeResourceDownloadsStayReadOnly(t *testing.T) {
for _, command := range []shortcut.Shortcut{MessagesMget, MessagesResourceDownload} {
if command.Risk != shortcut.RiskRead || command.RiskWhen != nil {
t.Errorf("%s risk contract = %q, dynamic=%v", command.Command, command.Risk, command.RiskWhen != nil)
if command.Risk != shortcut.RiskRead {
t.Errorf("%s risk contract = %q", command.Command, command.Risk)
}
}
}
@@ -386,7 +386,7 @@ func TestCrossPlatformCoverageMessagesSendCardResolvesReceiverForLowerTool(t *te
root := newPlatformCoverageRoot()
root.SetArgs([]string{
"chat", "+messages-send-card",
"--receiver", "user-id",
"--receiver", "d-user-id",
"--yes",
})
if err := root.Execute(); err != nil {
@@ -402,18 +402,21 @@ func TestCrossPlatformCoverageMessagesSendCardResolvesReceiverForLowerTool(t *te
if got := fake.calls[1].args["receiverOpenDingTalkId"]; got != "D-resolved" {
t.Fatalf("receiverOpenDingTalkId = %#v, want D-resolved", got)
}
if got := fake.calls[0].args["user_id_list"]; !reflect.DeepEqual(got, []string{"d-user-id"}) {
t.Fatalf("D/d-prefixed userId resolution args = %#v", fake.calls[0].args)
}
if _, exists := fake.calls[1].args["receiverUid"]; exists {
t.Fatalf("obsolete receiverUid leaked to lower tool: %#v", fake.calls[1].args)
}
}
func TestCrossPlatformCoverageMessagesSendCardKeepsOpenReceiver(t *testing.T) {
func TestCrossPlatformCoverageMessagesSendCardUsesExplicitOpenReceiver(t *testing.T) {
fake := &larkAlignmentCaller{}
helpers.InitDeps(fake)
root := newPlatformCoverageRoot()
root.SetArgs([]string{
"chat", "+messages-send-card",
"--receiver", "D-direct",
"--receiver-open-dingtalk-id", "D-direct",
"--yes",
})
if err := root.Execute(); err != nil {
@@ -547,6 +550,8 @@ func TestCrossPlatformCoverageMessagesSendCardDryRunAndFailureBoundaries(t *test
for _, args := range [][]string{
{"--group", "cid", "--content", "x", "--flow-status", "6"},
{"--group", "cid", "--flow-status", "2"},
{"--group", "cid", "--receiver-open-dingtalk-id", "D-direct"},
{"--receiver", "user-id", "--receiver-open-dingtalk-id", "D-direct"},
} {
helpers.InitDeps(&larkAlignmentCaller{})
root := newPlatformCoverageRoot()
+1 -4
View File
@@ -368,9 +368,6 @@ func messagesSendUserTarget(rt *shortcut.RuntimeContext) (group, openID string,
func resolveUserOpenDingTalkID(rt *shortcut.RuntimeContext, userID string) (string, error) {
userID = strings.TrimSpace(userID)
if isOpenID(userID) {
return userID, nil
}
data, err := rt.CallMCPData("contact", "get_user_info_by_user_ids", map[string]any{
"user_id_list": []string{userID},
})
@@ -379,7 +376,7 @@ func resolveUserOpenDingTalkID(rt *shortcut.RuntimeContext, userID string) (stri
}
openID := findOpenDingTalkID(data)
if openID == "" {
return "", apperrors.NewValidation("无法把 userId 解析为 openDingTalkId;请改传 openDingTalkId")
return "", apperrors.NewValidation("无法把 userId 解析为 openDingTalkId;请改用当前命令提供的显式 openDingTalkId 参数")
}
return openID, nil
}
+1 -1
View File
@@ -461,7 +461,7 @@ func validateConstraints(rt *RuntimeContext, s Shortcut) error {
// --dry-run is set. Read-only shortcuts never prompt. Returns false when the
// user declines.
func confirmRisk(rt *RuntimeContext, s Shortcut) bool {
risk := s.effectiveRisk(rt)
risk := s.risk()
if risk == RiskRead || rt.DryRun() || rt.Yes() {
return true
}
+5 -103
View File
@@ -41,73 +41,15 @@ func TestCrossPlatformCoverageRiskDefaultsToRead(t *testing.T) {
}
}
func TestCrossPlatformCoverageEffectiveRiskUsesParsedFlagsAndFailsClosed(t *testing.T) {
func TestCrossPlatformCoverageConfirmRiskPromptsForStaticWrite(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool("download-resources", false, "")
rt := &RuntimeContext{cmd: cmd}
s := Shortcut{
Risk: RiskRead,
RiskWhen: func(rt *RuntimeContext) Risk {
if rt.Bool("download-resources") {
return RiskWrite
}
return RiskRead
},
}
if got := s.effectiveRisk(rt); got != RiskRead {
t.Fatalf("default effectiveRisk() = %q, want read", got)
}
if err := cmd.Flags().Set("download-resources", "true"); err != nil {
t.Fatal(err)
}
if got := s.effectiveRisk(rt); got != RiskWrite {
t.Fatalf("download effectiveRisk() = %q, want write", got)
}
s.Risk = RiskRead
s.RiskWhen = func(*RuntimeContext) Risk { return Risk("unknown") }
if got := s.effectiveRisk(rt); got != RiskHighWrite {
t.Fatalf("invalid callback effectiveRisk() = %q, want high-risk-write", got)
}
s.Risk = RiskWrite
s.RiskWhen = func(*RuntimeContext) Risk { return RiskHighWrite }
if got := s.effectiveRisk(rt); got != RiskHighWrite {
t.Fatalf("write-to-high upgrade effectiveRisk() = %q, want high-risk-write", got)
}
s.RiskWhen = func(*RuntimeContext) Risk { return RiskRead }
if got := s.effectiveRisk(rt); got != RiskWrite {
t.Fatalf("write-to-read downgrade effectiveRisk() = %q, want write", got)
}
s.Risk = Risk("unknown")
if got := s.effectiveRisk(rt); got != RiskHighWrite {
t.Fatalf("invalid base effectiveRisk() = %q, want high-risk-write", got)
}
s.Risk = RiskHighWrite
s.RiskWhen = func(*RuntimeContext) Risk { return RiskRead }
if got := s.effectiveRisk(rt); got != RiskHighWrite {
t.Fatalf("high-risk downgrade effectiveRisk() = %q, want high-risk-write", got)
}
}
func TestCrossPlatformCoverageConfirmRiskUsesEffectiveRisk(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool("download-resources", false, "")
if err := cmd.Flags().Set("download-resources", "true"); err != nil {
t.Fatal(err)
}
var stderr bytes.Buffer
cmd.SetErr(&stderr)
rt := &RuntimeContext{cmd: cmd}
s := Shortcut{
Service: "chat",
Command: "+messages-mget",
Risk: RiskRead,
RiskWhen: func(rt *RuntimeContext) Risk {
if rt.Bool("download-resources") {
return RiskWrite
}
return RiskRead
},
Command: "+messages-send",
Risk: RiskWrite,
}
reader, writer, err := os.Pipe()
@@ -128,53 +70,13 @@ func TestCrossPlatformCoverageConfirmRiskUsesEffectiveRisk(t *testing.T) {
t.Fatal(err)
}
if !confirmRisk(rt, s) {
t.Fatal("dynamic write risk was not confirmed")
t.Fatal("static write risk was not confirmed")
}
if got := stderr.String(); !strings.Contains(got, "chat +messages-mget(write)") {
if got := stderr.String(); !strings.Contains(got, "chat +messages-send(write)") {
t.Fatalf("confirmation prompt = %q", got)
}
}
func TestConfirmRiskDoesNotReadStdinForDynamicReadInvocation(t *testing.T) {
cmd := &cobra.Command{}
cmd.Flags().Bool("download-resources", false, "")
var stderr bytes.Buffer
cmd.SetErr(&stderr)
rt := &RuntimeContext{cmd: cmd}
s := Shortcut{
Service: "chat",
Command: "+at-me",
Risk: RiskRead,
RiskWhen: func(rt *RuntimeContext) Risk {
if rt.Bool("download-resources") {
return RiskWrite
}
return RiskRead
},
}
reader, writer, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
previousStdin := os.Stdin
os.Stdin = reader
t.Cleanup(func() {
os.Stdin = previousStdin
_ = reader.Close()
})
if !confirmRisk(rt, s) {
t.Fatal("dynamic read invocation was treated as a declined write")
}
if stderr.Len() != 0 {
t.Fatalf("dynamic read invocation prompted: %q", stderr.String())
}
}
func TestCrossPlatformCoverageMountRegistersFlagsAndUse(t *testing.T) {
s := Shortcut{
Service: "contact",
+2 -2
View File
@@ -15,8 +15,8 @@ import (
func TestCrossPlatformCoverageSafeResourceQueryDownloadsStayReadOnly(t *testing.T) {
for _, command := range []shortcut.Shortcut{AtMe, ChatMessages, SearchMsg, ThreadReplies} {
if command.Risk != shortcut.RiskRead || command.RiskWhen != nil {
t.Errorf("%s risk contract = %q, dynamic=%v", command.Command, command.Risk, command.RiskWhen != nil)
if command.Risk != shortcut.RiskRead {
t.Errorf("%s risk contract = %q", command.Command, command.Risk)
}
}
}
-37
View File
@@ -138,13 +138,6 @@ type Shortcut struct {
Intent string
// Risk classifies the side effect; defaults to RiskRead when empty.
Risk Risk
// RiskWhen optionally refines Risk after flags have been parsed and
// validated. It is intended for commands that are read-only by default but
// gain a local or remote write mode behind an explicit opt-in flag.
//
// Distribution Schema keeps publishing the stable default Risk contract;
// runtime confirmation uses the value returned here.
RiskWhen func(rt *RuntimeContext) Risk
// Flags are the command-specific flags. Global flags are injected separately.
Flags []Flag
// Constraints publish and enforce relationships that individual flags cannot
@@ -199,33 +192,3 @@ func (s Shortcut) risk() Risk {
}
return s.Risk
}
// effectiveRisk returns the invocation-specific risk after flags are resolved.
// Invalid callback/base values fail closed to high-risk-write, while attempted
// downgrades preserve the declarative default instead of weakening its
// confirmation policy.
func (s Shortcut) effectiveRisk(rt *RuntimeContext) Risk {
base := s.risk()
if s.RiskWhen == nil {
return base
}
dynamic := s.RiskWhen(rt)
switch dynamic {
case RiskRead, RiskWrite, RiskHighWrite:
default:
return RiskHighWrite
}
switch base {
case RiskHighWrite:
return RiskHighWrite
case RiskWrite:
if dynamic == RiskHighWrite {
return RiskHighWrite
}
return RiskWrite
case RiskRead:
return dynamic
default:
return RiskHighWrite
}
}
+6
View File
@@ -289,6 +289,12 @@ dws chat +messages-send --as user --open-dingtalk-id <openDingTalkId> --msg-type
`+messages-send` 的 @ 占位符按身份自动规范化并补齐:user 使用 `<@id>` / `<@all>`;bot/webhook 使用 `@id` / `@手机号` / `@all`。声明 `--at-*` / `--at-all` 即可,不要再手工拼 `@10`。
**用 `chat +messages-send-card` 的场景**:
- 群聊流式卡片使用 `--group <openConversationId>`。
- 单聊已有 userId 时使用 `--receiver <userId>`,CLI 始终先只读解析为 openDingTalkId;即使 userId 以 D/d 开头也不会猜测类型,`--dry-run` 也会执行该解析。
- 单聊已有 openDingTalkId 时必须显式使用 `--receiver-open-dingtalk-id <openDingTalkId>`,避免与 userId 混淆。
- `--group`、`--receiver`、`--receiver-open-dingtalk-id` 严格三选一;传 `--content` 可在同一次调用中创建并结束卡片。
**用 `chat +messages-send --as bot` 的场景**:
- "让机器人在群里发一条通知" — **机器人身份**发消息
- "给张三发一条机器人单聊消息" — 机器人单聊
+3 -1
View File
@@ -105,7 +105,7 @@ metadata:
| `dws chat +messages-remove-emoji` | write | 移除消息的 emoji 表情回应 |
| `dws chat +messages-remove-text-emotion` | write | 移除消息的文字表情回应 |
| `dws chat +messages-reply` | write | 以当前用户身份引用回复消息(自动补全原发送者) |
| `dws chat +messages-resource-download` | write | 安全下载消息资源(图片/视频/语音/文件)到本地 |
| `dws chat +messages-resource-download` | read | 安全下载消息资源(图片/视频/语音/文件)到工作目录内,无需交互确认 |
| `dws chat +messages-resource-url` | read | 获取消息资源(图片/视频/语音)下载链接 |
| `dws chat +messages-send` | write | 统一发送文本、Markdown、当前用户文件或已有 mediaId 图片 |
| `dws chat +messages-send-by-bot` | write | 机器人向群聊发送 Markdown 消息 |
@@ -130,6 +130,7 @@ metadata:
- 查询结果中的 `resourceRefs` 是可继续执行的资源上下文。引用、回复或合并转发中的子消息必须使用该子消息返回的 `messageId`;子消息缺会话 ID 时才继承父消息的 `openConversationId`。
- 上述五个查询 Shortcut 与 `+messages-resource-download` 都沿用安全本地下载的 `read/not_required` 契约,不应添加 `--yes` 或触发交互确认。下载只允许工作目录内相对路径、默认不覆盖并原子落盘;需要覆盖时必须由用户显式传 `--overwrite`。
- `+messages-send` 会按身份规范化 @ 占位符:user 使用 `<@id>` / `<@all>`,bot/webhook 使用 `@id` / `@手机号` / `@all`;只需声明 `--at-*` / `--at-all`,缺失占位符会自动补齐。
- 流式卡片优先用 `+messages-send-card`:群聊传 `--group`,单聊 userId 传 `--receiver` 并由 CLI 只读解析,已有 openDingTalkId 则必须显式传 `--receiver-open-dingtalk-id`;三者严格三选一,禁止根据首字母猜身份类型。
## 意图表
@@ -145,6 +146,7 @@ metadata:
| "查看我收藏的消息" | `dws chat +flag-list` |
| "用机器人发消息" | `dws chat +messages-send --as bot --robot-code <code> --chat-id <id> --text "<内容>"` |
| "Webhook 推一条" | `dws chat +messages-send --as webhook --webhook-token <token> --text "<内容>"` |
| "发送流式卡片" | `dws chat +messages-send-card --group <openConversationId> --content "<内容>"`;单聊 userId 改用 `--receiver`,openDingTalkId 改用 `--receiver-open-dingtalk-id` |
| "撤回消息" | `dws chat message recall --conversation-id <openConversationId> --msg-id <openMessageId>` |
| "标记未读 / 清除红点 / 全部已读" | `dws chat mark-unread` / `dws chat clear-red-point` / `dws chat clear-all-red-point` |
| "置顶某条消息 / 取消消息置顶" | `dws chat message set-top-msg` / `dws chat message unset-top-msg` |
@@ -301,6 +301,12 @@ dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file
> ❌ 反模式:调 `dt_media_upload` / `extract_media_id.py` / `drive upload` / `drive download` 等前置工具再 `--msg-type image --media-id`。这是**旧链路**,仅当上游已持有 mediaId 才用;新场景一律 `--file-path` 直发,避免长链路与“空白图”现象。
> 富媒体消息单聊优先使用 `--open-dingtalk-id`;传 `--user` 时 CLI 会尝试解析为 openDingTalkId 后发送。
**用 `chat +messages-send-card` 的场景**:
- 群聊流式卡片使用 `--group <openConversationId>`。
- 单聊已有 userId 时使用 `--receiver <userId>`,CLI 始终先只读解析为 openDingTalkId;即使 userId 以 D/d 开头也不会猜测类型,`--dry-run` 也会执行该解析。
- 单聊已有 openDingTalkId 时必须显式使用 `--receiver-open-dingtalk-id <openDingTalkId>`,避免与 userId 混淆。
- `--group`、`--receiver`、`--receiver-open-dingtalk-id` 严格三选一;传 `--content` 可在同一次调用中创建并结束卡片。
- "发送位置/坐标/地址到群里" / "发个位置给某某" — `dws chat message send ... --msg-type location --latitude <纬度> --longitude <经度> --location-name <地址名称> --map-thumbnail-url @mediaId`;地图缩略图需先通过 `dt_media_upload` 上传获取 mediaId
```bash