diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e3df6c7..a98264da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -694,7 +694,10 @@ jobs: go-version-file: go.mod - name: Test macOS auth and Keychain paths with Race Detection - run: go test -v -race -count=1 -timeout=10m ./internal/keychain ./internal/auth ./internal/app + run: go test -v -race -count=1 -timeout=5m ./internal/keychain ./internal/auth + + - name: Test macOS app paths with Race Detection + run: go test -v -race -count=1 -timeout=12m ./internal/app test-windows: name: Test (Windows) diff --git a/internal/cli/runtime_schema.go b/internal/cli/runtime_schema.go index 3a8bdab7..964bae72 100644 --- a/internal/cli/runtime_schema.go +++ b/internal/cli/runtime_schema.go @@ -1600,6 +1600,12 @@ func inferredRuntimeFlagFormat(flag *pflag.Flag) string { } usage := strings.ToLower(strings.TrimSpace(flag.Usage)) if strings.Contains(usage, "iso-8601") || strings.Contains(usage, "rfc3339") { + // JSON Schema's date-time format means one RFC3339 value. Do not publish + // that narrower wire contract when the CLI also accepts local timestamps + // or date-only values alongside RFC3339. + if strings.Contains(usage, "yyyy-mm-dd") { + return "" + } return "date-time" } if strings.Contains(usage, "a1") { diff --git a/internal/cli/schema_agent_metadata/chat.json b/internal/cli/schema_agent_metadata/chat.json index 3f06643b..5403b5e5 100644 --- a/internal/cli/schema_agent_metadata/chat.json +++ b/internal/cli/schema_agent_metadata/chat.json @@ -3585,7 +3585,7 @@ }, "use_when": { "value": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ], "source": "internal/cli/schema_hints/selection/chat.json", "precedence": "reviewed_explicit", @@ -3594,7 +3594,7 @@ "candidates": [ { "value": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ], "source": "internal/cli/schema_hints/selection/chat.json", "precedence": "reviewed_explicit", @@ -3617,7 +3617,7 @@ "internal/cli/schema_hints/selection/chat.json" ], "use_when": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ] }, "chat +chat-create": { diff --git a/internal/cli/schema_agent_metadata/index.json b/internal/cli/schema_agent_metadata/index.json index bc94bdd9..7ccf469e 100644 --- a/internal/cli/schema_agent_metadata/index.json +++ b/internal/cli/schema_agent_metadata/index.json @@ -1,6 +1,6 @@ { "version": 1, - "source_hash": "sha256:6f4ff004d7060db709d732e4c5884586f71b1947c61352a8281d0664398c150b", + "source_hash": "sha256:495401fa3a29d8bbeb2a0b7c989f01bb50793764d0e0e7d467c07718db05808d", "surface_hash": "sha256:ae4288a29f237ab41d080695d94794133143ad1efc0899f71f05047443ab09c0", "coverage": { "surface_products": 26, diff --git a/internal/cli/schema_agent_metadata_audit.json b/internal/cli/schema_agent_metadata_audit.json index 4357946d..69450f5c 100644 --- a/internal/cli/schema_agent_metadata_audit.json +++ b/internal/cli/schema_agent_metadata_audit.json @@ -1,6 +1,6 @@ { "version": 1, - "source_hash": "sha256:6f4ff004d7060db709d732e4c5884586f71b1947c61352a8281d0664398c150b", + "source_hash": "sha256:495401fa3a29d8bbeb2a0b7c989f01bb50793764d0e0e7d467c07718db05808d", "surface_hash": "sha256:ae4288a29f237ab41d080695d94794133143ad1efc0899f71f05047443ab09c0", "source_files": 160, "hint_files": 54, diff --git a/internal/cli/schema_catalog/catalog.json b/internal/cli/schema_catalog/catalog.json index 820e6de7..68cccc3a 100644 --- a/internal/cli/schema_catalog/catalog.json +++ b/internal/cli/schema_catalog/catalog.json @@ -1,12 +1,12 @@ { "version": 1, "surface_hash": "sha256:ae4288a29f237ab41d080695d94794133143ad1efc0899f71f05047443ab09c0", - "source_hash": "sha256:061bccdda8236eab6dbfd9a8ee2a7d20b383ef493caa95f5c784c74937d79742", + "source_hash": "sha256:6b900a4082db317578dcfff43560e6e0ba075caf859e4858d9d591545fb6fa76", "catalog": { "agent_metadata": { "products_with_metadata": 26, "source": "embedded-skill-metadata", - "source_hash": "sha256:6f4ff004d7060db709d732e4c5884586f71b1947c61352a8281d0664398c150b", + "source_hash": "sha256:495401fa3a29d8bbeb2a0b7c989f01bb50793764d0e0e7d467c07718db05808d", "surface_hash": "sha256:ae4288a29f237ab41d080695d94794133143ad1efc0899f71f05047443ab09c0", "surface_products": 26, "surface_tools": 897, @@ -10187,7 +10187,7 @@ "risk": "low", "title": "查看群内所有机器人", "use_when": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ] }, { @@ -10448,7 +10448,7 @@ "primary_cli_path": "chat +chat-messages", "reviewed": true, "risk": "low", - "title": "按会话 ID、群名或姓名读取消息,支持有界全量分页与原子 JSON 导出", + "title": "按会话 ID、群名或用户 ID 读取消息,支持有界全量分页与原子 JSON 导出", "use_when": [ "当你想快速看一个群聊或单聊里的消息(谁在什么时间说了什么),而不想拿到大段原始消息字段时使用;群聊的 --group 可传群名或 openConversationId,单聊可传 --user 或 --open-dingtalk-id,所有目标参数互斥且必须选一个。自然群名只在唯一解析后读取,多候选会返回结构化 candidates。省略 --time 时默认从当前时间向前读取最近消息;也可指定时间边界并用 --direction newer/older 控制方向。全量读取用 --page-all,并由 --page-limit/--max-results 保持有界;结果公开 complete、hasMore、nextPage、stopReason、截断和逐页失败,不能把部分结果称为完整。--output 把同一 ledger 原子写为工作目录内 JSON。默认只读;--download-resources 使用工作目录内安全路径、默认不覆盖和原子落盘。" ] diff --git a/internal/cli/schema_catalog/tools/chat.json b/internal/cli/schema_catalog/tools/chat.json index b5b04535..594930b4 100644 --- a/internal/cli/schema_catalog/tools/chat.json +++ b/internal/cli/schema_catalog/tools/chat.json @@ -60331,7 +60331,7 @@ "selected": true, "source": "internal/cli/schema_hints/selection/chat.json", "value": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ] } ], @@ -60340,7 +60340,7 @@ "review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.", "source": "internal/cli/schema_hints/selection/chat.json", "value": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ] } }, @@ -60445,7 +60445,7 @@ "source": "reviewed_command_registry", "title": "查看群内所有机器人", "use_when": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ] }, "chat.shortcut_chat_create": { @@ -65929,13 +65929,13 @@ "precedence": "cobra_help", "selected": true, "source": "cobra_help", - "value": "按会话 ID、群名或姓名读取消息,支持有界全量分页与原子 JSON 导出" + "value": "按会话 ID、群名或用户 ID 读取消息,支持有界全量分页与原子 JSON 导出" } ], "precedence": "cobra_help", "resolution": "highest_precedence", "source": "cobra_help", - "value": "按会话 ID、群名或姓名读取消息,支持有界全量分页与原子 JSON 导出" + "value": "按会话 ID、群名或用户 ID 读取消息,支持有界全量分页与原子 JSON 导出" }, "use_when": { "candidates": [ @@ -67043,7 +67043,7 @@ "type": "integer" }, "time": { - "description": "时间边界,如 \"2025-03-01 00:00:00\";省略时从当前时间向前读取最近消息", + "description": "时间边界,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD;省略时从当前时间向前读取最近消息", "field_provenance": { "description": { "candidates": [ @@ -67051,7 +67051,7 @@ "precedence": "cobra_contract", "selected": true, "source": "cobra_usage", - "value": "时间边界,如 \"2025-03-01 00:00:00\";省略时从当前时间向前读取最近消息" + "value": "时间边界,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD;省略时从当前时间向前读取最近消息" }, { "precedence": "default", @@ -67063,7 +67063,7 @@ "precedence": "cobra_contract", "resolution": "highest_precedence", "source": "cobra_usage", - "value": "时间边界,如 \"2025-03-01 00:00:00\";省略时从当前时间向前读取最近消息" + "value": "时间边界,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD;省略时从当前时间向前读取最近消息" }, "property": { "candidates": [ @@ -67223,7 +67223,7 @@ "reviewed": true, "risk": "low", "source": "reviewed_command_registry", - "title": "按会话 ID、群名或姓名读取消息,支持有界全量分页与原子 JSON 导出", + "title": "按会话 ID、群名或用户 ID 读取消息,支持有界全量分页与原子 JSON 导出", "use_when": [ "当你想快速看一个群聊或单聊里的消息(谁在什么时间说了什么),而不想拿到大段原始消息字段时使用;群聊的 --group 可传群名或 openConversationId,单聊可传 --user 或 --open-dingtalk-id,所有目标参数互斥且必须选一个。自然群名只在唯一解析后读取,多候选会返回结构化 candidates。省略 --time 时默认从当前时间向前读取最近消息;也可指定时间边界并用 --direction newer/older 控制方向。全量读取用 --page-all,并由 --page-limit/--max-results 保持有界;结果公开 complete、hasMore、nextPage、stopReason、截断和逐页失败,不能把部分结果称为完整。--output 把同一 ledger 原子写为工作目录内 JSON。默认只读;--download-resources 使用工作目录内安全路径、默认不覆盖和原子落盘。" ] @@ -115762,7 +115762,7 @@ "type": "string" }, "time": { - "description": "起始时间,如 \"2025-03-01 00:00:00\"(可选)", + "description": "起始时间,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD(可选)", "field_provenance": { "description": { "candidates": [ @@ -115770,7 +115770,7 @@ "precedence": "cobra_contract", "selected": true, "source": "cobra_usage", - "value": "起始时间,如 \"2025-03-01 00:00:00\"(可选)" + "value": "起始时间,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD(可选)" }, { "precedence": "default", @@ -115782,7 +115782,7 @@ "precedence": "cobra_contract", "resolution": "highest_precedence", "source": "cobra_usage", - "value": "起始时间,如 \"2025-03-01 00:00:00\"(可选)" + "value": "起始时间,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD(可选)" }, "property": { "candidates": [ diff --git a/internal/cli/schema_hints/selection/chat.json b/internal/cli/schema_hints/selection/chat.json index bf78b7ad..03a9e889 100644 --- a/internal/cli/schema_hints/selection/chat.json +++ b/internal/cli/schema_hints/selection/chat.json @@ -1969,7 +1969,7 @@ "chat.shortcut_chat_bots": { "agent_summary": "查看群内所有机器人", "use_when": [ - "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,也可显式用 --chat-query 按群名唯一解析;多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" + "当你想查看某个群里已添加了哪些机器人时使用;--group 可传群 openConversationId 或群名,多命中会安全停止。只读返回机器人列表(含 openBotId,供后续移除)。" ], "avoid_when": [ "用户要搜索当前账号可用或创建的机器人而非群内机器人时,使用 bot find/search;不要先走 chat group search + group bots 原子链。" diff --git a/internal/cli/schema_parameter_precedence_test.go b/internal/cli/schema_parameter_precedence_test.go index 1f20d33d..70bc57cb 100644 --- a/internal/cli/schema_parameter_precedence_test.go +++ b/internal/cli/schema_parameter_precedence_test.go @@ -675,6 +675,10 @@ func TestRuntimeSchemaFormatPrecedenceMatrix(t *testing.T) { wantSource: "usage_format_inference", wantSources: []string{"usage_format_inference", "default"}, }, + { + name: "mixed accepted time syntaxes stay untyped", + usage: "RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD", + }, {name: "empty default stays omitted"}, } for _, test := range tests { diff --git a/internal/shortcut/smart/chat_messages.go b/internal/shortcut/smart/chat_messages.go index 756617c2..b317f1d0 100644 --- a/internal/shortcut/smart/chat_messages.go +++ b/internal/shortcut/smart/chat_messages.go @@ -47,7 +47,7 @@ var ChatMessages = shortcut.Shortcut{ Service: "chat", Command: "+chat-messages", Product: "chat", - Description: "按会话 ID、群名或姓名读取消息,支持有界全量分页与原子 JSON 导出", + Description: "按会话 ID、群名或用户 ID 读取消息,支持有界全量分页与原子 JSON 导出", Intent: "当你想快速看一个群聊或单聊里的消息(谁在什么时间说了什么),而不想拿到大段原始消息字段时使用;" + "群聊的 --group 可传群名或 openConversationId,单聊可传 --user 或 --open-dingtalk-id,所有目标参数互斥且必须选一个。自然群名只在唯一解析后读取,多候选会返回结构化 candidates。" + "省略 --time 时默认从当前时间向前读取最近消息;也可指定时间边界并用 --direction newer/older 控制方向。" + @@ -63,7 +63,7 @@ var ChatMessages = shortcut.Shortcut{ {Name: "user", Type: shortcut.FlagString, Desc: "单聊对方的 userId,与 --group 互斥"}, {Name: "user-query", Type: shortcut.FlagString, Desc: "按姓名解析唯一 openDingTalkId 的兼容入口", Hidden: true}, {Name: "open-dingtalk-id", Type: shortcut.FlagString, Desc: "单聊对方的 openDingTalkId,与 --group/--user 互斥"}, - {Name: "time", Type: shortcut.FlagString, Desc: "时间边界,如 \"2025-03-01 00:00:00\";省略时从当前时间向前读取最近消息"}, + {Name: "time", Type: shortcut.FlagString, Desc: "时间边界,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD;省略时从当前时间向前读取最近消息"}, {Name: "limit", Type: shortcut.FlagInt, Desc: "每页拉取的消息条数;显式页大小必须大于 0"}, {Name: "size", Type: shortcut.FlagInt, Desc: "--limit 的旧版别名", Hidden: true}, {Name: "page-size", Type: shortcut.FlagInt, Desc: "--limit 的兼容别名", Hidden: true}, diff --git a/internal/shortcut/smart/thread_replies.go b/internal/shortcut/smart/thread_replies.go index 304dceaf..3118035b 100644 --- a/internal/shortcut/smart/thread_replies.go +++ b/internal/shortcut/smart/thread_replies.go @@ -50,7 +50,7 @@ var ThreadReplies = shortcut.Shortcut{ {Name: "group", Type: shortcut.FlagString, Desc: "群会话 ID(openConversationId,必填)", Required: true}, {Name: "thread-id", Type: shortcut.FlagString, Desc: "话题/线程 ID(可直接使用消息列表返回的 threadId)"}, {Name: "topic-id", Type: shortcut.FlagString, Desc: "--thread-id 的兼容别名"}, - {Name: "time", Type: shortcut.FlagString, Desc: "起始时间,如 \"2025-03-01 00:00:00\"(可选)"}, + {Name: "time", Type: shortcut.FlagString, Desc: "起始时间,如 \"2025-03-01 00:00:00\";--time 必须是 RFC3339、YYYY-MM-DD HH:mm:ss 或 YYYY-MM-DD(可选)"}, {Name: "limit", Type: shortcut.FlagInt, Desc: "每页拉取的回复条数;--limit 必须大于 0"}, {Name: "no-reactions", Type: shortcut.FlagBool, Desc: "不输出回复 reaction(默认输出)"}, }, chatshortcut.MessageResourceDownloadFlags()...),