diff --git a/README.md b/README.md index f02ba088..58775c3f 100644 --- a/README.md +++ b/README.md @@ -482,7 +482,7 @@ Env vars: `DWS_SKILL_MODE=mono|multi` (also honored by `install.sh` / `install.p
Personal Event Subscription — real-time DingTalk messages for event-driven agents -`dws event consume` subscribes as the currently logged-in user over a managed Stream WebSocket and emits each event as one NDJSON line on stdout. The public catalog covers scoped and all one-to-one/group messages, specified senders, read/recall/reaction events, group lifecycle events, and six OA approval task/instance events. +`dws event consume` subscribes as the currently logged-in user over a managed Stream WebSocket and emits each event as one NDJSON line on stdout. The public catalog covers scoped and all one-to-one/group messages, specified senders, read/recall/reaction events, group lifecycle events, and seven OA approval task/instance events. The default `ndjson`, `json`, and `pretty` output preserves the transport envelope (`type`, `event_type`, string `data`, and `headers`) for existing scripts; `compact` retains its existing processor. Add `--flatten` to emit the stable top-level business fields used by Agent workflows. `--format` controls JSON serialization; `--flatten` controls the data structure and cannot be combined with `-f raw` or `--debug-raw-events`. @@ -530,12 +530,13 @@ dws event consume user_im_group_disbanded --group --flatten dws event +listen-im --kind sender --user \ --events message,read,recall -f ndjson -# Listen for all six public OA approval events in one process +# Listen for all seven public OA approval events in one process dws event consume \ user_oa_approval_task_created \ user_oa_approval_task_finished \ user_oa_approval_task_redirected \ user_oa_approval_instance_started \ + user_oa_approval_instance_cc \ user_oa_approval_instance_terminated \ user_oa_approval_instance_finished \ --flatten -f ndjson diff --git a/README_zh.md b/README_zh.md index e47544a4..b2a5763f 100644 --- a/README_zh.md +++ b/README_zh.md @@ -476,7 +476,7 @@ multi setup 或 upgrade 后,DWS 会把官方 bundle 快照和统一所有权
个人事件订阅 — 实时接收钉钉消息,驱动事件触发的 Agent -`dws event consume` 使用当前 OAuth 登录用户建立托管的 Stream WebSocket 长连接,并把每条事件以 NDJSON 一行输出到 stdout。当前公开目录覆盖指定范围和全量单聊/群消息、指定发送人、已读/撤回/表情回应、群生命周期,以及六个 OA 审批任务/实例事件。 +`dws event consume` 使用当前 OAuth 登录用户建立托管的 Stream WebSocket 长连接,并把每条事件以 NDJSON 一行输出到 stdout。当前公开目录覆盖指定范围和全量单聊/群消息、指定发送人、已读/撤回/表情回应、群生命周期,以及七个 OA 审批任务/实例事件。 默认 `ndjson`、`json`、`pretty` 输出保留兼容 transport envelope(`type`、`event_type`、字符串 `data`、`headers`),`compact` 继续沿用原 processor。Agent 或新脚本显式加 `--flatten` 后,输出稳定的顶层业务字段。`--format` 控制 JSON 序列化,`--flatten` 控制数据结构,且不能与 `-f raw` 或 `--debug-raw-events` 同时使用。 @@ -524,12 +524,13 @@ dws event consume user_im_group_disbanded --group --flatten dws event +listen-im --kind sender --user \ --events message,read,recall -f ndjson -# 一个进程监听全部六个公开 OA 审批事件 +# 一个进程监听全部七个公开 OA 审批事件 dws event consume \ user_oa_approval_task_created \ user_oa_approval_task_finished \ user_oa_approval_task_redirected \ user_oa_approval_instance_started \ + user_oa_approval_instance_cc \ user_oa_approval_instance_terminated \ user_oa_approval_instance_finished \ --flatten -f ndjson diff --git a/internal/app/event_command.go b/internal/app/event_command.go index f0cc1e24..ef727259 100644 --- a/internal/app/event_command.go +++ b/internal/app/event_command.go @@ -403,7 +403,7 @@ SIGTERM、关 stdin,或先用 dws event stop --dry-run 预览 Selection: contract.SelectionSpec{ AgentSummary: "消费 OA、群生命周期或需要底层控制的个人事件流;Agent 通常使用 --flatten 输出 NDJSON", UseWhen: []string{ - "需要监听六个公开 OA 审批任务/实例 EventKey 中的一个或多个事件", + "需要监听七个公开 OA 审批任务/实例 EventKey 中的一个或多个事件", "需要监听指定群的标题变更、成员进退群或群解散事件", "用户显式给出原始 EventKey、Filter DSL、subscribe_id,要求原始 transport envelope,或需要普通 IM facade 不提供的高级多事件控制", }, diff --git a/internal/app/event_personal_attempts_test.go b/internal/app/event_personal_attempts_test.go index 1acdea27..913086bb 100644 --- a/internal/app/event_personal_attempts_test.go +++ b/internal/app/event_personal_attempts_test.go @@ -152,8 +152,8 @@ func TestCrossPlatformCoveragePersonalSubscriptionProtectionCoversAllPublicEvent } } - if publicCount != 22 { - t.Fatalf("public personal events = %d, want 22 (16 IM + 6 OA)", publicCount) + if publicCount != 23 { + t.Fatalf("public personal events = %d, want 23 (16 IM + 7 OA)", publicCount) } for _, ruleType := range []string{"at", "all", "singleChat", "sender", "group"} { if !ruleTypes[ruleType] { diff --git a/internal/app/event_personal_oa_test.go b/internal/app/event_personal_oa_test.go index 11f8dbe4..9defa152 100644 --- a/internal/app/event_personal_oa_test.go +++ b/internal/app/event_personal_oa_test.go @@ -61,6 +61,13 @@ func TestPersonalOAEventListAndSchemaCommands(t *testing.T) { "process_code", "title", "status", "create_time", "event_time", }, }, + { + eventKey: personal.EventOAApprovalInstanceCC, + properties: []string{ + "type", "event_id", "timestamp", "subscribe_id", "process_instance_id", + "process_code", "title", "status", "create_time", "event_time", + }, + }, { eventKey: personal.EventOAApprovalInstanceTerminated, properties: []string{ @@ -161,6 +168,7 @@ func TestPersonalOAEventConsumeDryRunAndValidation(t *testing.T) { personal.EventOAApprovalTaskFinished, personal.EventOAApprovalTaskRedirected, personal.EventOAApprovalInstanceStarted, + personal.EventOAApprovalInstanceCC, personal.EventOAApprovalInstanceTerminated, personal.EventOAApprovalInstanceFinished, } @@ -414,6 +422,7 @@ func TestPersonalOAMultiConsumeCreatesIndependentAllSubscriptionsOnSharedBus(t * personal.EventOAApprovalTaskFinished, personal.EventOAApprovalTaskRedirected, personal.EventOAApprovalInstanceStarted, + personal.EventOAApprovalInstanceCC, personal.EventOAApprovalInstanceTerminated, personal.EventOAApprovalInstanceFinished, } diff --git a/internal/event/personal/client_test.go b/internal/event/personal/client_test.go index e7ffe3af..9dfc12bd 100644 --- a/internal/event/personal/client_test.go +++ b/internal/event/personal/client_test.go @@ -132,6 +132,7 @@ func TestClientCreateRuleBasedSubscriptionsUsesDocumentedRuleParam(t *testing.T) {"oa_approval_task_finished", EventOAApprovalTaskFinished, RuleOptions{}, map[string]any{}}, {"oa_approval_task_redirected", EventOAApprovalTaskRedirected, RuleOptions{}, map[string]any{}}, {"oa_approval_instance_started", EventOAApprovalInstanceStarted, RuleOptions{}, map[string]any{}}, + {"oa_approval_instance_cc", EventOAApprovalInstanceCC, RuleOptions{}, map[string]any{}}, {"oa_approval_instance_terminated", EventOAApprovalInstanceTerminated, RuleOptions{}, map[string]any{}}, {"oa_approval_instance_finished", EventOAApprovalInstanceFinished, RuleOptions{}, map[string]any{}}, {"read_group", EventReadGroup, RuleOptions{GroupID: "cid-1"}, map[string]any{"openConversationId": "cid-1"}}, diff --git a/internal/event/personal/output.go b/internal/event/personal/output.go index 00d81a01..e42d1b45 100644 --- a/internal/event/personal/output.go +++ b/internal/event/personal/output.go @@ -180,6 +180,19 @@ type OAApprovalInstanceStartedOutput struct { EventTime int64 `json:"event_time" description:"审批实例事件业务时间" format:"timestamp_ms"` } +type OAApprovalInstanceCCOutput struct { + Type string `json:"type" description:"事件类型,固定为当前 event_key"` + EventID string `json:"event_id" description:"事件 ID,可用于去重"` + Timestamp int64 `json:"timestamp" description:"事件发生时间戳" format:"timestamp_ms"` + SubscribeID string `json:"subscribe_id" description:"订阅 ID"` + ProcessInstanceID string `json:"process_instance_id" description:"审批实例 ID"` + ProcessCode string `json:"process_code" description:"审批流程模板编码"` + Title string `json:"title" description:"审批标题"` + Status string `json:"status" description:"审批实例到达抄送节点时的状态"` + CreateTime int64 `json:"create_time" description:"审批实例创建时间" format:"timestamp_ms"` + EventTime int64 `json:"event_time" description:"审批抄送事件业务时间" format:"timestamp_ms"` +} + type OAApprovalInstanceTerminatedOutput struct { Type string `json:"type" description:"事件类型,固定为当前 event_key"` EventID string `json:"event_id" description:"事件 ID,可用于去重"` @@ -667,6 +680,19 @@ func projectOAApprovalEvent(ev transport.Event, base baseEventOutput, raw json.R CreateTime: payload.Body.CreateTime, EventTime: payload.EventTime, }, nil + case EventOAApprovalInstanceCC: + return OAApprovalInstanceCCOutput{ + Type: base.Type, + EventID: base.EventID, + Timestamp: base.Timestamp, + SubscribeID: base.SubscribeID, + ProcessInstanceID: payload.Body.ProcessInstanceID, + ProcessCode: payload.Body.ProcessCode, + Title: payload.Body.Title, + Status: payload.Body.Status, + CreateTime: payload.Body.CreateTime, + EventTime: payload.EventTime, + }, nil case EventOAApprovalInstanceTerminated: return OAApprovalInstanceTerminatedOutput{ Type: base.Type, @@ -871,6 +897,8 @@ func outputTypeForEvent(eventKey string) reflect.Type { return reflect.TypeOf(OAApprovalTaskRedirectedOutput{}) case eventKey == EventOAApprovalInstanceStarted: return reflect.TypeOf(OAApprovalInstanceStartedOutput{}) + case eventKey == EventOAApprovalInstanceCC: + return reflect.TypeOf(OAApprovalInstanceCCOutput{}) case eventKey == EventOAApprovalInstanceTerminated: return reflect.TypeOf(OAApprovalInstanceTerminatedOutput{}) case eventKey == EventOAApprovalInstanceFinished: @@ -906,6 +934,7 @@ func isOAEvent(eventKey string) bool { eventKey == EventOAApprovalTaskFinished || eventKey == EventOAApprovalTaskRedirected || eventKey == EventOAApprovalInstanceStarted || + eventKey == EventOAApprovalInstanceCC || eventKey == EventOAApprovalInstanceTerminated || eventKey == EventOAApprovalInstanceFinished } diff --git a/internal/event/personal/output_test.go b/internal/event/personal/output_test.go index 326caf67..e9de2e00 100644 --- a/internal/event/personal/output_test.go +++ b/internal/event/personal/output_test.go @@ -173,6 +173,8 @@ func personalOAData(eventKey string) string { body["finishTime"] = int64(1785229199000) case EventOAApprovalInstanceStarted: body["status"] = "RUNNING" + case EventOAApprovalInstanceCC: + body["status"] = "RUNNING" case EventOAApprovalInstanceTerminated: body["status"] = "TERMINATED" body["finishTime"] = int64(1785229199000) @@ -521,6 +523,21 @@ func TestCrossPlatformCoverageProjectOutputOAEvents(t *testing.T) { EventTime: 1785229199000, }, }, + { + eventKey: EventOAApprovalInstanceCC, + want: OAApprovalInstanceCCOutput{ + Type: EventOAApprovalInstanceCC, + EventID: "oa-event", + Timestamp: 1785229200123, + SubscribeID: "outer-sub", + ProcessInstanceID: "process-instance-1", + ProcessCode: "PROC-TEST-1", + Title: "测试审批", + Status: "RUNNING", + CreateTime: 1785229100000, + EventTime: 1785229199000, + }, + }, { eventKey: EventOAApprovalInstanceTerminated, want: OAApprovalInstanceTerminatedOutput{ @@ -785,6 +802,7 @@ func TestCrossPlatformCoverageProjectOutputRejectsInvalidOAPayloads(t *testing.T EventOAApprovalTaskFinished, EventOAApprovalTaskRedirected, EventOAApprovalInstanceStarted, + EventOAApprovalInstanceCC, EventOAApprovalInstanceTerminated, EventOAApprovalInstanceFinished, } { diff --git a/internal/event/personal/registry.go b/internal/event/personal/registry.go index bffb7918..50e3ce03 100644 --- a/internal/event/personal/registry.go +++ b/internal/event/personal/registry.go @@ -43,6 +43,7 @@ const ( EventOAApprovalTaskFinished = "user_oa_approval_task_finished" EventOAApprovalTaskRedirected = "user_oa_approval_task_redirected" EventOAApprovalInstanceStarted = "user_oa_approval_instance_started" + EventOAApprovalInstanceCC = "user_oa_approval_instance_cc" EventOAApprovalInstanceTerminated = "user_oa_approval_instance_terminated" EventOAApprovalInstanceFinished = "user_oa_approval_instance_finished" ) @@ -323,6 +324,17 @@ var definitions = []Definition{ Auth: map[string]any{"identity": "user"}, Public: true, }, + { + EventKey: EventOAApprovalInstanceCC, + DisplayName: "审批单抄送", + Description: "审批实例到达抄送节点,发送给被抄送人", + Category: "oa", + RuleType: "all", + Status: StatusEnabled, + RequiredParams: nil, + Auth: map[string]any{"identity": "user"}, + Public: true, + }, { EventKey: EventOAApprovalInstanceTerminated, DisplayName: "审批单终止", diff --git a/internal/event/personal/registry_test.go b/internal/event/personal/registry_test.go index fb01a3b3..1b80fa82 100644 --- a/internal/event/personal/registry_test.go +++ b/internal/event/personal/registry_test.go @@ -50,6 +50,7 @@ func TestCatalogEnabledEvents(t *testing.T) { EventOAApprovalTaskFinished, EventOAApprovalTaskRedirected, EventOAApprovalInstanceStarted, + EventOAApprovalInstanceCC, EventOAApprovalInstanceTerminated, EventOAApprovalInstanceFinished, } @@ -65,6 +66,7 @@ func TestOAEventCatalogDefinitions(t *testing.T) { EventOAApprovalTaskFinished, EventOAApprovalTaskRedirected, EventOAApprovalInstanceStarted, + EventOAApprovalInstanceCC, EventOAApprovalInstanceTerminated, EventOAApprovalInstanceFinished, } @@ -158,6 +160,7 @@ func TestSchemaDocumentsDefaultToTransportEnvelope(t *testing.T) { EventOAApprovalTaskFinished, EventOAApprovalTaskRedirected, EventOAApprovalInstanceStarted, + EventOAApprovalInstanceCC, EventOAApprovalInstanceTerminated, EventOAApprovalInstanceFinished, } { @@ -509,6 +512,13 @@ func TestOAEventSchemaDocumentsMatchOutputDTO(t *testing.T) { "process_code", "title", "status", "create_time", "event_time", }, }, + { + eventKey: EventOAApprovalInstanceCC, + properties: []string{ + "type", "event_id", "timestamp", "subscribe_id", "process_instance_id", + "process_code", "title", "status", "create_time", "event_time", + }, + }, { eventKey: EventOAApprovalInstanceTerminated, properties: []string{ @@ -633,6 +643,7 @@ func TestBuildRuleParamAllEvents(t *testing.T) { EventOAApprovalTaskFinished, EventOAApprovalTaskRedirected, EventOAApprovalInstanceStarted, + EventOAApprovalInstanceCC, EventOAApprovalInstanceTerminated, EventOAApprovalInstanceFinished, } { @@ -845,6 +856,7 @@ func TestSupportsMessageFilter(t *testing.T) { EventOAApprovalTaskFinished, EventOAApprovalTaskRedirected, EventOAApprovalInstanceStarted, + EventOAApprovalInstanceCC, EventOAApprovalInstanceTerminated, EventOAApprovalInstanceFinished, "unknown_event", diff --git a/skills/mono/SKILL.md b/skills/mono/SKILL.md index 189ffbad..2fb8cb3d 100644 --- a/skills/mono/SKILL.md +++ b/skills/mono/SKILL.md @@ -1,6 +1,6 @@ --- name: dws -description: 管理钉钉产品能力(AI表格/AI搜问/日历/通讯录/群聊与机器人/待办/审批/考勤/日志/DING消息/开放平台文档/钉钉文档/钉钉云盘/原生Markdown文件/AI听记/邮箱/在线电子表格/知识库等)。当用户需要操作表格数据、管理日程会议、模糊找人/查谁负责某事项、查询通讯录、管理群聊、机器人发消息、创建待办、提交审批、查看考勤、提交日报周报(钉钉日志模版)、读写钉钉文档、上传下载云盘文件、读取或修改原生.md文件、查询听记纪要、收发邮件、读写在线电子表格(axls)、管理钉钉知识库,或订阅个人 IM 事件或 OA 审批事件、实时监听群成员加入、群成员退出、群改名和群解散、审批实例发起/终止/完成,以及审批任务创建/完成/转交时使用。 +description: 管理钉钉产品能力(AI表格/AI搜问/日历/通讯录/群聊与机器人/待办/审批/考勤/日志/DING消息/开放平台文档/钉钉文档/钉钉云盘/原生Markdown文件/AI听记/邮箱/在线电子表格/知识库等)。当用户需要操作表格数据、管理日程会议、模糊找人/查谁负责某事项、查询通讯录、管理群聊、机器人发消息、创建待办、提交审批、查看考勤、提交日报周报(钉钉日志模版)、读写钉钉文档、上传下载云盘文件、读取或修改原生.md文件、查询听记纪要、收发邮件、读写在线电子表格(axls)、管理钉钉知识库,或订阅个人 IM 事件或 OA 审批事件、实时监听群成员加入、群成员退出、群改名和群解散、审批实例发起/抄送/终止/完成,以及审批任务创建/完成/转交时使用。 cli_version: ">=1.0.15" --- diff --git a/skills/mono/references/products/event.md b/skills/mono/references/products/event.md index f9b82c95..15178398 100644 --- a/skills/mono/references/products/event.md +++ b/skills/mono/references/products/event.md @@ -47,10 +47,11 @@ | `user_oa_approval_task_finished` | 审批任务已完成 | 无 | | `user_oa_approval_task_redirected` | 审批任务已转交 | 无 | | `user_oa_approval_instance_started` | 审批实例已发起 | 无 | +| `user_oa_approval_instance_cc` | 审批实例到达抄送节点,发送给被抄送人 | 无 | | `user_oa_approval_instance_terminated` | 审批实例已终止 | 无 | | `user_oa_approval_instance_finished` | 审批实例完成,发送给审批单发起人 | 无 | -只承认上表 22 个事件码。默认身份就是当前用户,使用当前用户 OAuth 登录态,不要额外加身份切换 flag。六个 OA 事件订阅当前用户相关的全部审批事件,规则均为 `all`、空 `filterRule`,不需要目标参数。 +只承认上表 23 个事件码。默认身份就是当前用户,使用当前用户 OAuth 登录态,不要额外加身份切换 flag。七个 OA 事件订阅当前用户相关的全部审批事件,规则均为 `all`、空 `filterRule`,不需要目标参数。 ## Intent mapping @@ -76,9 +77,10 @@ | "审批任务完成时通知我" | `event consume`,事件码 `user_oa_approval_task_finished`,参数 `--flatten -f ndjson` | | "审批任务被转交时通知我" | `event consume`,事件码 `user_oa_approval_task_redirected`,参数 `--flatten -f ndjson` | | "有审批单发起时通知我" | `event consume`,事件码 `user_oa_approval_instance_started`,参数 `--flatten -f ndjson` | +| "有审批抄送给我时通知我" | `event consume`,事件码 `user_oa_approval_instance_cc`,参数 `--flatten -f ndjson` | | "有审批单终止时通知我" | `event consume`,事件码 `user_oa_approval_instance_terminated`,参数 `--flatten -f ndjson` | | "监听我发起的审批何时完成" / "审批实例完成时通知我" | `event consume`,事件码 `user_oa_approval_instance_finished`,参数 `--flatten -f ndjson` | -| "同时监听全部已公开 OA 事件" | 一个 consume 放入六个 OA event key,不加目标或消息过滤参数 | +| "同时监听全部已公开 OA 事件" | 一个 consume 放入七个 OA event key,不加目标或消息过滤参数 | | "查看个人事件 schema" | `dws event schema --flatten` | | "看个人事件订阅状态" | `dws event status --event ` | | "停止这个个人事件订阅" | `dws event stop --dry-run`,确认后改用 `--yes` | @@ -130,6 +132,7 @@ dws event schema user_oa_approval_task_created --flatten dws event schema user_oa_approval_task_finished --flatten dws event schema user_oa_approval_task_redirected --flatten dws event schema user_oa_approval_instance_started --flatten +dws event schema user_oa_approval_instance_cc --flatten dws event schema user_oa_approval_instance_terminated --flatten dws event schema user_oa_approval_instance_finished --flatten ``` @@ -157,6 +160,7 @@ dws event consume user_oa_approval_task_created --flatten -f ndjson dws event consume user_oa_approval_task_finished --flatten -f ndjson dws event consume user_oa_approval_task_redirected --flatten -f ndjson dws event consume user_oa_approval_instance_started --flatten -f ndjson +dws event consume user_oa_approval_instance_cc --flatten -f ndjson dws event consume user_oa_approval_instance_terminated --flatten -f ndjson dws event consume user_oa_approval_instance_finished --flatten -f ndjson ``` @@ -185,13 +189,14 @@ dws event consume \ user_oa_approval_task_finished \ user_oa_approval_task_redirected \ user_oa_approval_instance_started \ + user_oa_approval_instance_cc \ user_oa_approval_instance_terminated \ user_oa_approval_instance_finished \ --flatten \ -f ndjson ``` -用户类事件共享 `--user` 或 `--open-dingtalk-id`,群类事件共享 `--group`,无目标 IM 事件可加入任一组合。用户类与群类、不同目标或不同过滤条件要拆成多个进程。六个 OA 事件可以同进程消费并共享 personal bus,但各自建立独立订阅。多事件共享 `--query` / `--filter-json` 时,所选事件必须全部是 IM 消息接收事件;OA 事件单独或组合消费都禁止使用这两个消息过滤参数。 +用户类事件共享 `--user` 或 `--open-dingtalk-id`,群类事件共享 `--group`,无目标 IM 事件可加入任一组合。用户类与群类、不同目标或不同过滤条件要拆成多个进程。七个 OA 事件可以同进程消费并共享 personal bus,但各自建立独立订阅。多事件共享 `--query` / `--filter-json` 时,所选事件必须全部是 IM 消息接收事件;OA 事件单独或组合消费都禁止使用这两个消息过滤参数。 上述所有 `*_o2o` 命令和 `user_im_message_receive_user` 都可将 `--user ` 替换为 `--open-dingtalk-id `,但两个参数不能同时使用。 @@ -216,7 +221,7 @@ dws event stop --all --yes ## 订阅创建失败与重试预算 -以下约束适用于上表全部 22 个公开个人事件(16 个 IM + 6 个 OA)以及多事件命令中的每一项,只治理 `[event] ready` 之前的订阅创建;ready 之后的 Stream 断线由长连接重连机制处理。 +以下约束适用于上表全部 23 个公开个人事件(16 个 IM + 7 个 OA)以及多事件命令中的每一项,只治理 `[event] ready` 之前的订阅创建;ready 之后的 Stream 断线由长连接重连机制处理。 - `0/2/1` 是 **Agent/host 编排约束**,不是 CLI 持久化硬总次数上限。每次 `dws event consume` 调用对每个逻辑订阅最多发送一次订阅创建 HTTP 请求,进程内不会自动重试。CLI 本地状态只持久化 `in_flight`、`cooldown`、`terminal_hold` 三种保护状态,不持久化或计算跨调用的 Agent/host 尝试次数。 - 解析人名或群名、执行 `event consume` 以及后续 `event status/stop` 必须使用同一个 `--profile`。不得把其它 profile 下解析出的 userId、openDingtalkId 或 openConversationId 直接带入当前 profile 的订阅。 diff --git a/skills/multi/dingtalk-event/SKILL.md b/skills/multi/dingtalk-event/SKILL.md index 4fdd75a9..719c3d64 100644 --- a/skills/multi/dingtalk-event/SKILL.md +++ b/skills/multi/dingtalk-event/SKILL.md @@ -1,6 +1,6 @@ --- name: dingtalk-event -description: 钉钉个人 IM 与 OA 审批事件长连接监听。Use when 用户说监听消息/@我/某人/某群/全部消息、已读/撤回/reaction、群成员加入/群成员退出/群状态变化,或监听审批任务创建/完成/转交、审批实例发起/终止/完成。命令前缀:dws event。 +description: 钉钉个人 IM 与 OA 审批事件长连接监听。Use when 用户说监听消息/@我/某人/某群/全部消息、已读/撤回/reaction、群成员加入/群成员退出/群状态变化,或监听审批任务创建/完成/转交、审批实例发起/抄送/终止/完成。命令前缀:dws event。 metadata: cli_version: ">=0.2.14" category: product @@ -45,7 +45,7 @@ metadata: - `group` 必须且只能传 `--chat-id` 或 `--chat-query` 之一。 - `--query` 只用于纯 `message` 监听;混入 reaction/read/recall 时不得使用。 -OA 事件不进入 `+listen-im`。六个公开 OA EventKey 都订阅当前 OAuth 用户相关的全部审批事件,使用 `ruleType=all`、`filterRule={}`;不接受 `--user`、`--open-dingtalk-id`、`--group`、`--query` 或 `--filter-json`。六项可放入同一个 consume,每项建立独立订阅并共享 bus。 +OA 事件不进入 `+listen-im`。七个公开 OA EventKey 都订阅当前 OAuth 用户相关的全部审批事件,使用 `ruleType=all`、`filterRule={}`;不接受 `--user`、`--open-dingtalk-id`、`--group`、`--query` 或 `--filter-json`。七项可放入同一个 consume,每项建立独立订阅并共享 bus。 自然姓名和群名由 CLI 内部唯一解析:零命中或多候选返回结构化失败,在创建任何订阅前停止。`--dry-run` 走同一解析链。解析、监听、状态和停止必须使用同一个 `--profile`,不得跨组织搬运 ID。 @@ -65,7 +65,7 @@ user_im_group_member_added user_im_group_member_exited user_im_group_disbanded ``` -六个 OA EventKey 及其输出字段见 [OA 事件参考](references/event-oa.md)。 +七个 OA EventKey 及其输出字段见 [OA 事件参考](references/event-oa.md)。 用户类事件传 `--user` 或 `--open-dingtalk-id`,群类事件传 `--group`。群生命周期输出可含 `operator_open_dingtalk_id` 和 `members`;成员项使用 `open_dingtalk_id`。精确组合、兼容性和 Filter 规则见 reference。 @@ -98,7 +98,7 @@ kind + events + target - `event stop` 会取消订阅并影响本地 consumer:先 `--dry-run`,用户确认后再加 `--yes`。 - 多事件属于一次原始操作;任一订阅启动失败时 Runtime 回滚本次已创建项,不拆成新命令绕过重试预算。 -- 这套 `0/2/1` 是 **Agent/host** 编排预算,适用于全部 22 个公开个人 EventKey(16 个 IM + 6 个 OA):`retryable=false` 对应 `max_additional_attempts=0`;`retryable=true` 对应 `max_additional_attempts=2`;`retryable=unknown` 对应 `max_additional_attempts=1`。它不是 CLI 持久化硬总次数上限;每次调用最多创建一次,进程内不会自动重试,CLI 也不持久化或计算跨调用的 Agent/host 尝试次数。 +- 这套 `0/2/1` 是 **Agent/host** 编排预算,适用于全部 23 个公开个人 EventKey(16 个 IM + 7 个 OA):`retryable=false` 对应 `max_additional_attempts=0`;`retryable=true` 对应 `max_additional_attempts=2`;`retryable=unknown` 对应 `max_additional_attempts=1`。它不是 CLI 持久化硬总次数上限;每次调用最多创建一次,进程内不会自动重试,CLI 也不持久化或计算跨调用的 Agent/host 尝试次数。 - 重试必须遵守 `retry_after_seconds` / `next_retry_at`。遇到 `in_flight`、`cooldown`、`terminal_hold` 不并发或递归重启同一逻辑订阅,也不换 `subscribe_id` / `trace_id` 绕过保护。 - 认证、profile、订阅保护状态和 bus 排障按失败类型读取 [订阅运维](references/event-im-operations.md),不要在正常路径预加载完整运维手册。 @@ -125,4 +125,4 @@ kind + events + target | ready、bounded consume 与退出清理 | [event-im-lifecycle.md](references/event-im-lifecycle.md) | 启动/托管/关闭 consumer | | 扁平字段与事件到 Chat 交接 | [event-im-output.md](references/event-im-output.md) | 解析事件或自动回复 | | Filter、status/stop、重试与排障 | [event-im-operations.md](references/event-im-operations.md) | 订阅控制或失败恢复 | -| OA 审批事件 | [event-oa.md](references/event-oa.md) | 选择六个 OA EventKey、组合消费或解析审批字段 | +| OA 审批事件 | [event-oa.md](references/event-oa.md) | 选择七个 OA EventKey、组合消费或解析审批字段 | diff --git a/skills/multi/dingtalk-event/references/event-oa.md b/skills/multi/dingtalk-event/references/event-oa.md index 6d7ec4e2..e8af0108 100644 --- a/skills/multi/dingtalk-event/references/event-oa.md +++ b/skills/multi/dingtalk-event/references/event-oa.md @@ -1,6 +1,6 @@ # OA 个人审批事件 -先读事件产品入口 [SKILL.md](../SKILL.md) 的命令规则、调用流和子进程契约。本参考覆盖当前公开的六个 OA 个人事件:审批实例发起、终止和完成,以及审批任务创建、完成和转交。 +先读事件产品入口 [SKILL.md](../SKILL.md) 的命令规则、调用流和子进程契约。本参考覆盖当前公开的七个 OA 个人事件:审批实例发起、抄送、终止和完成,以及审批任务创建、完成和转交。 实时监听审批事件必须使用 `dws event consume` 长连接,不要轮询 OA 待办或审批实例列表来模拟事件。 @@ -22,10 +22,11 @@ dws auth login | `user_oa_approval_task_finished` | `all` | 审批任务已完成 | 无 | | `user_oa_approval_task_redirected` | `all` | 审批任务已转交 | 无 | | `user_oa_approval_instance_started` | `all` | 审批实例已发起 | 无 | +| `user_oa_approval_instance_cc` | `all` | 审批实例到达抄送节点,发送给被抄送人 | 无 | | `user_oa_approval_instance_terminated` | `all` | 审批实例已终止 | 无 | | `user_oa_approval_instance_finished` | `all` | 审批实例完成,发送给审批单发起人 | 无 | -只承认上表 6 个 OA 事件码。CLI 为每个事件发送 `ruleType=all`、`filterRule={}` 的独立订阅请求;不要添加 `--user`、`--open-dingtalk-id`、`--group`、`--query` 或 `--filter-json`。 +只承认上表 7 个 OA 事件码。CLI 为每个事件发送 `ruleType=all`、`filterRule={}` 的独立订阅请求;不要添加 `--user`、`--open-dingtalk-id`、`--group`、`--query` 或 `--filter-json`。 ## Intent mapping @@ -35,13 +36,14 @@ dws auth login | “审批任务完成时通知我” | `dws event consume user_oa_approval_task_finished --flatten -f ndjson` | | “审批任务被转交时通知我” | `dws event consume user_oa_approval_task_redirected --flatten -f ndjson` | | “有审批单发起时通知我” | `dws event consume user_oa_approval_instance_started --flatten -f ndjson` | +| “有审批抄送给我时通知我” | `dws event consume user_oa_approval_instance_cc --flatten -f ndjson` | | “有审批单终止时通知我” | `dws event consume user_oa_approval_instance_terminated --flatten -f ndjson` | | “监听我发起的审批何时完成” / “审批实例完成时通知我” | `dws event consume user_oa_approval_instance_finished --flatten -f ndjson` | -| “同时监听全部已公开 OA 事件” | 一个 consume 放入六个 OA event key,不加目标或过滤参数 | +| “同时监听全部已公开 OA 事件” | 一个 consume 放入七个 OA event key,不加目标或过滤参数 | | “查看 OA 事件目录” | `dws event list --category oa` | | “查看 OA 事件输出字段” | 对对应事件运行 `dws event schema --flatten` | -三个审批任务事件分别表达任务已创建、已完成和已转交;三个审批实例事件分别表达实例已发起、已终止和已完成。扁平字段来自六类事件的预发联调样本;`status` 和 `result` 保留服务端原值,不把当前样本值推断为完整枚举。 +三个审批任务事件分别表达任务已创建、已完成和已转交;四个审批实例事件分别表达实例已发起、到达抄送节点、已终止和已完成。`status` 和 `result` 保留服务端原值,不把当前样本值推断为完整枚举。 ## Commands @@ -52,6 +54,7 @@ dws event schema user_oa_approval_task_created --flatten dws event schema user_oa_approval_task_finished --flatten dws event schema user_oa_approval_task_redirected --flatten dws event schema user_oa_approval_instance_started --flatten +dws event schema user_oa_approval_instance_cc --flatten dws event schema user_oa_approval_instance_terminated --flatten dws event schema user_oa_approval_instance_finished --flatten ``` @@ -63,11 +66,12 @@ dws event consume user_oa_approval_task_created --flatten -f ndjson dws event consume user_oa_approval_task_finished --flatten -f ndjson dws event consume user_oa_approval_task_redirected --flatten -f ndjson dws event consume user_oa_approval_instance_started --flatten -f ndjson +dws event consume user_oa_approval_instance_cc --flatten -f ndjson dws event consume user_oa_approval_instance_terminated --flatten -f ndjson dws event consume user_oa_approval_instance_finished --flatten -f ndjson ``` -同时监听六种事件: +同时监听七种事件: ```bash dws event consume \ @@ -75,13 +79,14 @@ dws event consume \ user_oa_approval_task_finished \ user_oa_approval_task_redirected \ user_oa_approval_instance_started \ + user_oa_approval_instance_cc \ user_oa_approval_instance_terminated \ user_oa_approval_instance_finished \ --flatten \ -f ndjson ``` -多事件 consume 会为六个 event key 分别创建订阅和逻辑 consumer,并共享当前组织的 personal bus、远程连接、stdout 和生命周期。不要给 OA 命令加 `--query` 或 `--filter-json`;这两个 flag 只用于兼容的 IM 消息接收事件。 +多事件 consume 会为七个 event key 分别创建订阅和逻辑 consumer,并共享当前组织的 personal bus、远程连接、stdout 和生命周期。不要给 OA 命令加 `--query` 或 `--filter-json`;这两个 flag 只用于兼容的 IM 消息接收事件。 ## Output contract @@ -105,7 +110,7 @@ dws event consume \ - `type` 是当前 event key;`event_id` 可用于去重;`timestamp` 是 transport 事件发生时间;`subscribe_id` 标识对应的独立订阅。 - `process_instance_id` 是审批实例 ID,可传给 OA 审批命令的 `--instance-id`;`process_code` 是审批流程模板编码。 - `create_time`、`finish_time` 和 `event_time` 都是毫秒时间戳。`event_time` 是审批业务事件时间,`timestamp` 是 transport 事件时间。 -- 六类事件的额外字段如下;具体事件始终以 `dws event schema --flatten` 为准。 +- 七类事件的额外字段如下;具体事件始终以 `dws event schema --flatten` 为准。 | 事件 | 额外顶层字段 | |---|---| @@ -113,6 +118,7 @@ dws event consume \ | `user_oa_approval_task_finished` | `task_id`、`result`、`finish_time` | | `user_oa_approval_task_redirected` | `task_id`、`result`、`finish_time` | | `user_oa_approval_instance_started` | 无 | +| `user_oa_approval_instance_cc` | 无 | | `user_oa_approval_instance_terminated` | `finish_time` | | `user_oa_approval_instance_finished` | `result`、`finish_time` | @@ -144,6 +150,6 @@ dws event consume \ ## Lifecycle - 单事件等待 `[event] ready event_key= bus_pid= subscribe_id=`。 -- 六事件先保存六条 `[event] subscription event_key= subscribe_id=`,再等待 `[event] ready event_count=6 bus_pid=`。 +- 七事件先保存七条 `[event] subscription event_key= subscribe_id=`,再等待 `[event] ready event_count=7 bus_pid=`。 - 临时验证使用 `--max-events 1` 或 `--duration 10m`;任务完成后优雅结束 consume,本次新建的订阅会自动取消。 - 外部停止已有订阅时先运行 `dws event stop --dry-run`,确认后再加 `--yes`。不要 `kill -9`,否则会跳过自动退订。 diff --git a/test/skill_tests.md b/test/skill_tests.md index 8c1b6c4e..2325e43d 100644 --- a/test/skill_tests.md +++ b/test/skill_tests.md @@ -1190,6 +1190,10 @@ Agent 安装 dws skill 后,仅依据 skill 提供的参考文档,将自然 - Prompt: 有和我相关的审批实例发起时实时通知我 - Expected: `dws event consume user_oa_approval_instance_started --flatten -f ndjson` +**event_event_consume_oa_instance_cc_001** +- Prompt: 有审批实例抄送给我时实时通知我 +- Expected: `dws event consume user_oa_approval_instance_cc --flatten -f ndjson` + **event_event_consume_oa_instance_terminated_001** - Prompt: 和我相关的审批实例终止时实时通知我 - Expected: `dws event consume user_oa_approval_instance_terminated --flatten -f ndjson` diff --git a/test/unit/skill_docs_policy_test.go b/test/unit/skill_docs_policy_test.go index 45877f26..d5c36cf1 100644 --- a/test/unit/skill_docs_policy_test.go +++ b/test/unit/skill_docs_policy_test.go @@ -280,7 +280,7 @@ func TestEventSkillFrontmatterAdvertisesGroupMemberLifecycle(t *testing.T) { "群成员加入", "群成员退出", "审批任务创建/完成/转交", - "审批实例发起/终止/完成", + "审批实例发起/抄送/终止/完成", } { if !strings.Contains(frontmatter, required) { t.Errorf("%s frontmatter missing event discovery trigger %q", path, required) @@ -306,7 +306,7 @@ func TestStandaloneEventSkillOwnsAllPersonalEventContracts(t *testing.T) { "", "", "16 个 EventKey", - "22 个公开个人 EventKey", + "23 个公开个人 EventKey", } { if !strings.Contains(string(skillContent), required) { t.Errorf("%s missing standalone event contract %q", skillPath, required) @@ -360,6 +360,7 @@ func TestStandaloneEventSkillOwnsAllPersonalEventContracts(t *testing.T) { "user_oa_approval_task_finished", "user_oa_approval_task_redirected", "user_oa_approval_instance_started", + "user_oa_approval_instance_cc", "user_oa_approval_instance_terminated", "user_oa_approval_instance_finished", }