Skip to content
Closed
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
39b46a9
docs(spec): gap #01 DingTalk native approval design
zhumin-zizhu May 18, 2026
ad2d5b0
docs(spec): v2 修订 gap#01 — 对比 PR#489 + 真机回调证据
zhumin-zizhu May 18, 2026
898b4b2
docs(spec): v3 综合 gap#01 — PR#489 骨架 + 本设计权限/测试边界
zhumin-zizhu May 18, 2026
0a0e3ce
docs(spec): v3.1 修订 — 第二轮 review 反馈
zhumin-zizhu May 18, 2026
c071ea3
docs(spec): v3.2 修订 — 第三轮 review + 引入统一 resolver 抽象
zhumin-zizhu May 18, 2026
7b5ea23
docs(spec): v3.3 修订 — approval 挂在原 agent card 上 + markdown 路径独立
zhumin-zizhu May 18, 2026
ed463e8
docs(spec): v3.4 修订 — 第五轮 review,失败降级 + 6 处准确性 fix
zhumin-zizhu May 18, 2026
388f49e
docs(spec): v3.5 修订 — 对齐用户实际配好的 schema
zhumin-zizhu May 18, 2026
da15627
docs(spec): v3.5.1 流程审核 — 同步 §6 数据流到 v3.5 实施模型
zhumin-zizhu May 18, 2026
379c015
docs(spec): 新增 AI Card v3.0.0 schema 资产 + 在 spec 引用
zhumin-zizhu May 18, 2026
3cb2a5d
docs(spec): v3.6 修订 — approvalId 内置到按钮 + 4 处实施 blocker
zhumin-zizhu May 18, 2026
906afab
docs(spec): v3.7 修订 — approval_id → approveId 命名同步用户实配
zhumin-zizhu May 18, 2026
eccfac1
docs(spec): 锁定 v3 模板正式 templateId
zhumin-zizhu May 18, 2026
722c008
docs(assets): 更新 v3 模板 JSON 含 approveId 变量 + 三按钮 params 绑定
zhumin-zizhu May 18, 2026
f600fe5
docs(spec): v3.8 修订 — 5 处实施一致性 fix + 1 处 limitation 备注
zhumin-zizhu May 18, 2026
44afa9a
chore(deps): bump openclaw approval SDK baseline
zhumin-zizhu May 19, 2026
1ce36f0
feat(approval): add execApprovals config surface
zhumin-zizhu May 19, 2026
dac8af9
feat(approval): add approval config and command parser helpers
zhumin-zizhu May 19, 2026
1723940
feat(approval): add DingTalk origin target resolver
zhumin-zizhu May 19, 2026
8172a1a
feat(approval): resolve approvals through OpenClaw gateway
zhumin-zizhu May 19, 2026
de47886
feat(card): locate active card runs by session
zhumin-zizhu May 19, 2026
12e929e
feat(approval): locate active AI card for approvals
zhumin-zizhu May 19, 2026
b89d9c8
feat(approval): intercept DingTalk approve commands
zhumin-zizhu May 19, 2026
23c68fd
feat(approval): wire DingTalk approval capability
zhumin-zizhu May 19, 2026
e95c616
feat(approval): expose capability and bypass approve commands
zhumin-zizhu May 19, 2026
cb25eb9
feat(approval): implement DingTalk native approval runtime
zhumin-zizhu May 19, 2026
d01b7ec
docs(approval): document DingTalk native approval
zhumin-zizhu May 19, 2026
a119389
fix(approval): align native approval verification fixes
zhumin-zizhu May 19, 2026
004ea4f
fix(approval): enforce plugin approval authorization
zhumin-zizhu May 19, 2026
8c77f05
fix(approval): harden native approval handling
zhumin-zizhu May 19, 2026
71bfca4
chore(deps): raise openclaw peer baseline to 2026.5.7
zhumin-zizhu May 19, 2026
bb76790
ci: bump Node to 22 to match openclaw engines and undici 8.x
zhumin-zizhu May 19, 2026
3b1b023
test(approval): integration end-to-end coverage for 12 scenarios
zhumin-zizhu May 19, 2026
12fb8b2
fix(approval): preserve pending approval state across card finalize
zhumin-zizhu May 19, 2026
953e122
chore(release): defer 3.6.4 release notes and version bump
zhumin-zizhu May 19, 2026
dd49a9b
fix(approval): declare execApprovals in plugin manifest schema
zhumin-zizhu May 19, 2026
0306167
chore(approval): add INFO diagnostics to native runtime adapters
zhumin-zizhu May 19, 2026
0e74899
fix(approval): register native approval runtime context on startup
zhumin-zizhu May 19, 2026
66dbf7d
chore(approval): log terminalPatch branch decision
zhumin-zizhu May 19, 2026
13ba057
fix(approval): defer card finalize while approval is pending
zhumin-zizhu May 19, 2026
e803c22
fix(approval): defer streaming-lifecycle finalize while approval pending
zhumin-zizhu May 19, 2026
0d38b71
fix(approval): tighten deferred-finalize lifecycle and fence rendering
zhumin-zizhu May 19, 2026
0def952
fix(approval): classify "unknown or expired approval id" as not-found
zhumin-zizhu May 19, 2026
155d5ee
feat(approval): replace card body with friendly approval prompt
zhumin-zizhu May 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
cache: pnpm

- name: Install dependencies
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# DingTalk Channel for OpenClaw

<p class="repo-badges">
<a href="https://github.com/openclaw/openclaw"><img alt="OpenClaw" src="https://img.shields.io/badge/OpenClaw-%3E%3D2026.3.24-0A7CFF"></a>
<a href="https://github.com/openclaw/openclaw"><img alt="OpenClaw" src="https://img.shields.io/badge/OpenClaw-%3E%3D2026.4.7-0A7CFF"></a>
<a href="https://www.npmjs.com/package/@soimy/dingtalk"><img alt="npm version" src="https://img.shields.io/npm/v/%40soimy%2Fdingtalk"></a>
<a href="https://www.npmjs.com/package/@soimy/dingtalk"><img alt="npm downloads" src="https://img.shields.io/npm/dm/%40soimy%2Fdingtalk"></a>
<a href="https://github.com/soimy/openclaw-channel-dingtalk/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/github/license/soimy/openclaw-channel-dingtalk"></a>
Expand All @@ -22,6 +22,7 @@
- 支持引用消息恢复和常见文本附件正文抽取
- 支持 Markdown 回复与 AI 卡片流式回复(v2 结构化 block 渲染、taskInfo 元数据、图片内联)
- 支持多 Agent、多机器人绑定和实验性的 `@多助手路由`
- 支持 OpenClaw exec/plugin approval 在钉钉内审批(AI Card 按钮或 `/approve` 命令)
- 支持 `/btw` 旁路问答,绕过主会话锁立即获得独立快答
- 支持 DingTalk Device Flow 自动注册,扫码授权后自动获取凭证,无需手动复制
- 支持实时中止当前 AI generation。常用停止指令包括 `停止`、`stop`、`/stop`、`esc` 等
Expand All @@ -46,7 +47,7 @@
## 安装

> [!IMPORTANT]
> 最小兼容版本为 `OpenClaw 2026.3.24`。安装前请先升级到最新版 OpenClaw。
> 最小兼容版本为 `OpenClaw 2026.4.7`。安装前请先升级到最新版 OpenClaw。

```bash
openclaw plugins install @soimy/dingtalk
Expand Down Expand Up @@ -143,6 +144,7 @@ openclaw configure --section channels
- [消息类型支持](docs/user/features/message-types.md)
- [回复模式](docs/user/features/reply-modes.md)
- [AI 卡片](docs/user/features/ai-card.md)
- [DingTalk Native Approval](docs/user/features/exec-approval.md)
- [/btw 旁路问答](docs/user/features/btw.md)
- [钉钉文档 API](docs/user/features/dingtalk-docs-api.md)
- [反馈学习](docs/user/features/feedback-learning.md)
Expand Down
1 change: 1 addition & 0 deletions docs/assets/card-template-v3.json

Large diffs are not rendered by default.

2,350 changes: 2,350 additions & 0 deletions docs/features/2026-05-18-gap-01-approval-native-design.html

Large diffs are not rendered by default.

4,954 changes: 4,954 additions & 0 deletions docs/plans/2026-05-19-gap-01-approval-native.md

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions docs/user/features/ai-card.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ AI 卡片模式是钉钉插件最有辨识度的回复方式,基于结构化 b

插件使用统一的预置卡片模板,无需用户配置 `cardTemplateId` / `cardTemplateKey`。

如需覆盖预置模板 ID,可通过环境变量 `DINGTALK_CARD_TEMPLATE_ID` 设置,默认值为 `675cde2f-f526-40cb-b828-f5b2b57b8b77.schema`。
如需覆盖预置模板 ID,可通过环境变量 `DINGTALK_CARD_TEMPLATE_ID` 设置,默认值为 `58f73932-fc3b-46ae-8e90-93313e405061.schema`。

AI 卡片生命周期:

Expand Down Expand Up @@ -119,13 +119,15 @@ AI 卡片生命周期:

如需在预置模板基础上定制卡片样式或新增自定义变量/组件,可以参考以下资产文件,将修改后的模板上传到钉钉开放平台,再通过 `DINGTALK_CARD_TEMPLATE_ID` 环境变量指向新模板 ID:

- **[`card-template-v2.json`](../../assets/card-template-v2.json)** — 当前预置卡片模板的完整低代码 schema(钉钉卡片搭建器导出格式),包含组件映射、组件树、数据源和交互定义,可直接导入钉钉卡片搭建器进行编辑。
- **[`card-template-v3.json`](../../assets/card-template-v3.json)** — 当前预置卡片模板的完整低代码 schema(钉钉卡片搭建器导出格式),包含组件映射、组件树、数据源、AI Card 展示与 Native Approval 按钮组,可直接导入钉钉卡片搭建器进行编辑。
- **[`card-template-v2.json`](../../assets/card-template-v2.json)** — 旧版模板,仅用于历史参考。
- **[`card-data-mock-v2.json`](../../assets/card-data-mock-v2.json)** — 卡片渲染时的 mock 数据样例,展示 `blockList`、`content`、`quoteContent`、`statusLine` 等变量结构和取值,方便在搭建器中预览卡片效果。

定制时注意保持变量 key 与插件输出字段的对齐:`content`、`blockList`、`quoteContent`、`copy_content`、`statusLine`、`hasAction` 等。
定制时注意保持变量 key 与插件输出字段的对齐:`content`、`blockList`、`quoteContent`、`copy_content`、`statusLine`、`hasAction`、`show_approve_btns`、`approveId` 等。

## 相关文档

- [回复模式](reply-modes.md)
- [DingTalk Native Approval](exec-approval.md)
- [API 消耗说明](../reference/api-usage-and-cost.md)
- [配置项参考](../reference/configuration.md)
102 changes: 102 additions & 0 deletions docs/user/features/exec-approval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# DingTalk Native Approval

Native Approval allows OpenClaw approval requests to be handled directly from DingTalk.
It supports command execution approvals and plugin approvals.

## Enable

Configure approvers with DingTalk staff IDs. Prefixes such as `dingtalk:`, `dd:`, and `ding:` are optional.

```json5
{
"channels": {
"dingtalk": {
"messageType": "card",
"execApprovals": {
"enabled": "auto",
"approvers": ["staff-id-1", "staff-id-2"]
}
}
}
}
```

If `execApprovals.approvers` is empty, the plugin falls back to `commands.ownerAllowFrom`.

```json5
{
"commands": {
"ownerAllowFrom": ["staff-id-1"]
},
"channels": {
"dingtalk": {
"execApprovals": {
"enabled": true
}
}
}
}
```

Set `enabled` to `false` to disable DingTalk native delivery even when approvers are configured.

## Interaction

### AI Card Mode

When `messageType` is `card` and an active AI Card exists for the same DingTalk session, the plugin patches the existing card and shows three buttons:

- `允许一次`: approve once
- `总是允许`: approve always, when the request allows it
- `拒绝`: deny

While approval is pending, the normal stop button is hidden. After the approval is resolved or expired, approval buttons are removed. If the agent is still streaming, the stop button is restored.

### Markdown Mode

When there is no active AI Card, the plugin sends a Markdown approval message with copyable commands:

```text
/approve <approvalId> allow-once
/approve <approvalId> allow-always
/approve <approvalId> deny
```

Only decisions allowed by the OpenClaw request are shown.

### Command Fallback

Approvers can also send `/approve` manually in DingTalk:

```text
/approve abc123 allow-once
/approve allow-once abc123
```

The command path is intercepted before the normal agent session lock, so it can resolve a pending approval while the original agent turn is paused.
Decision aliases are also accepted: `allow`, `once`, and `allowonce` map to `allow-once`; `always` and `allowalways` map to `allow-always`; `reject` and `block` map to `deny`.

## Configuration

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `execApprovals.enabled` | `boolean \| "auto"` | `"auto"` | `false` disables native delivery. `true` and `"auto"` enable it when approvers exist. |
| `execApprovals.approvers` | `string[]` | `[]` | DingTalk staff IDs allowed to approve. Falls back to `commands.ownerAllowFrom` when empty. |

## Limits

- v1 is origin-only: approvals are delivered back to the DingTalk chat that initiated the agent turn. Dedicated approver DM fan-out is not implemented yet.
- The card path removes buttons after resolution but does not write a final approval status line into the card.
- The card callback normally carries `cardPrivateData.params.approveId`. A process-local registry is kept only as a fallback for old cards or abnormal callback payloads.
- Real-device validation is still required after changing the DingTalk low-code card template or overriding `DINGTALK_CARD_TEMPLATE_ID`.

## Troubleshooting

- If no approval prompt appears, confirm `execApprovals.approvers` or `commands.ownerAllowFrom` contains the DingTalk staff ID of the approver.
- If the card buttons do not appear, confirm the runtime uses the v3 card template and `messageType` is `card`.
- If `/approve` says the decision is unsupported, choose one of the decisions shown in the Markdown or private hint.

## Related

- [AI Card](ai-card.md)
- [Configuration](../reference/configuration.md)
1 change: 1 addition & 0 deletions docs/user/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
- [消息类型支持](features/message-types.md)
- [回复模式](features/reply-modes.md)
- [AI 卡片](features/ai-card.md)
- [DingTalk Native Approval](features/exec-approval.md)
- [钉钉文档 API](features/dingtalk-docs-api.md)
- [反馈学习](features/feedback-learning.md)
- [/btw 旁路问答](features/btw.md)
Expand Down
21 changes: 21 additions & 0 deletions docs/user/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
| `cardStreamingMode` | string | `off`(生效值) | 卡片流式模式:`off` / `answer` / `all` |
| `cardStreamInterval` | number | `1000` | 卡片实时更新节奏(毫秒,最小 `200`) |
| `cardAtSender` | string | - | 群聊中卡片完成后追加 @发送者 的消息文本;非空时生效 |
| `execApprovals.enabled` | boolean \| `"auto"` | `"auto"` | 是否启用 DingTalk Native Approval;`false` 强制关闭 |
| `execApprovals.approvers` | string[] | `[]` | 允许审批的 DingTalk staffId 列表;为空时回退到 `commands.ownerAllowFrom` |
| `cardRealTimeStream` | boolean | `false` | 已弃用;仅兼容旧配置,`true` 会回退到 `cardStreamingMode: all` |
| `aicardDegradeMs` | number | `1800000` | 卡片连续失败后的降级时间 |
| `debug` | boolean | `false` | 是否输出调试日志 |
Expand Down Expand Up @@ -144,6 +146,25 @@ SecretInput 对象字段:
- 同时设置时,以 `cardStreamingMode` 为准。
- `cardStreamInterval` 控制实时更新节奏(毫秒),在 `answer` / `all` 下生效;值越小,更新越频繁,API 调用通常越高。

## 关于 `execApprovals`

`execApprovals` 用于 DingTalk Native Approval。配置 approver 后,OpenClaw 的 exec/plugin approval 可以通过钉钉 AI Card 按钮或 `/approve` 命令处理。

```json5
{
"channels": {
"dingtalk": {
"execApprovals": {
"enabled": "auto",
"approvers": ["staff-id-1"]
}
}
}
}
```

更多交互方式与限制见 [DingTalk Native Approval](../features/exec-approval.md)。

## 关于连接参数

连接相关配置用于提升 Stream 连接鲁棒性:
Expand Down
42 changes: 42 additions & 0 deletions openclaw.plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,25 @@
"type": "boolean",
"description": "Enable the local feedback-learning loop for notes, reflections, and command-assisted learning."
},
"execApprovals": {
"type": "object",
"additionalProperties": false,
"description": "Native OpenClaw exec/plugin approval configuration. v1 intentionally rejects v2 future fields.",
"properties": {
"enabled": {
"anyOf": [
{ "type": "boolean" },
{ "type": "string", "const": "auto" }
],
"description": "Enable native approval delivery. true to force on, false to disable, or \"auto\" to follow upstream host capability."
},
"approvers": {
"type": "array",
"items": { "type": "string" },
"description": "Approver staffIds (supports dingtalk:, dd:, ding: prefixes). Falls back to commands.ownerAllowFrom when empty."
}
}
},
"learningAutoApply": {
"type": "boolean",
"description": "Automatically apply generated learning output into session notes or global rules when available."
Expand Down Expand Up @@ -563,6 +582,25 @@
"type": "boolean",
"description": "Enable the local feedback-learning loop for notes, reflections, and command-assisted learning."
},
"execApprovals": {
"type": "object",
"additionalProperties": false,
"description": "Native OpenClaw exec/plugin approval configuration. v1 intentionally rejects v2 future fields.",
"properties": {
"enabled": {
"anyOf": [
{ "type": "boolean" },
{ "type": "string", "const": "auto" }
],
"description": "Enable native approval delivery. true to force on, false to disable, or \"auto\" to follow upstream host capability."
},
"approvers": {
"type": "array",
"items": { "type": "string" },
"description": "Approver staffIds (supports dingtalk:, dd:, ding: prefixes). Falls back to commands.ownerAllowFrom when empty."
}
}
},
"learningAutoApply": {
"type": "boolean",
"description": "Automatically apply generated learning output into session notes or global rules when available."
Expand Down Expand Up @@ -782,6 +820,10 @@
"label": "Learning Enabled",
"help": "Enable the local feedback-learning loop for notes, reflections, and learning commands."
},
"execApprovals": {
"label": "Exec Approvals",
"help": "Approver staffIds and toggle for native exec/plugin approval delivery. Falls back to commands.ownerAllowFrom when approvers is empty."
},
"learningAutoApply": {
"label": "Learning Auto Apply",
"help": "Automatically apply generated learning output into session notes or global rules when available."
Expand Down
15 changes: 10 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
],
"type": "module",
"packageManager": "pnpm@10.33.0",
"pnpm": {
"overrides": {
"openclaw": "2026.5.7"
}
},
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
Expand Down Expand Up @@ -68,7 +73,7 @@
"form-data": "^4.0.0",
"mammoth": "^1.12.0",
"pdf-parse": "^2.4.5",
"zod": "^4.3.6"
"zod": "^4.4.3"
},
"devDependencies": {
"@types/node": "^25.2.0",
Expand All @@ -82,7 +87,7 @@
"vitest": "^3.2.4"
},
"peerDependencies": {
"openclaw": ">=2026.3.28"
"openclaw": ">=2026.5.7"
},
"peerDependenciesMeta": {
"openclaw": {
Expand All @@ -91,10 +96,10 @@
},
"openclaw": {
"compat": {
"pluginApi": ">=2026.3.28"
"pluginApi": ">=2026.5.7"
},
"build": {
"openclawVersion": "2026.3.28"
"openclawVersion": "2026.5.7"
},
"extensions": [
"./index.ts"
Expand All @@ -120,7 +125,7 @@
]
},
"install": {
"minHostVersion": ">=2026.3.28",
"minHostVersion": ">=2026.5.7",
"npmSpec": "@soimy/dingtalk",
"localPath": ".",
"defaultChoice": "npm"
Expand Down
Loading
Loading