feat(approval): exec/plugin approval card with command session dispatch - #489
Draft
BrilliantWang wants to merge 30 commits into
Draft
feat(approval): exec/plugin approval card with command session dispatch#489BrilliantWang wants to merge 30 commits into
BrilliantWang wants to merge 30 commits into
Conversation
|
非常不错,我最近也在做这个,你是阿里的同学吗? 我目前是在服务器端做的。 |
Collaborator
Author
我不是哈哈, 服务端是什么思路啊? |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
BrilliantWang
force-pushed
the
feat/approval-v2
branch
from
April 8, 2026 02:26
dac385f to
e4eba39
Compare
…and gateway resolve
…nd TOPIC_CARD handler
…l rejection handlers
…dOnce/Always/Deny + status)
…, add cardPrivateData tests
…and handleApprovalCardCallback retry
- Add card-approve-command.ts using CommandTargetSessionKey pattern - Intercept /approve text command before session lock in inbound-handler - Refactor handleApprovalCardCallback to use command session dispatch - Remove gateway client (getGatewayClient, prewarmGatewayClient, resolveApprovalDecision) - Expand ApprovalCardEntry with agentId/sessionKey for command routing - Update tests for new dispatch pattern
Implement DingTalk's ChannelApprovalNativeRuntimeAdapter (5 sub-adapters: availability / presentation / transport / interactions / observe) plus a ChannelApprovalNativeAdapter declaring the origin-only delivery surface. - src/approval/approval-native-adapter.ts: handles createAndDeliver, card streaming, and resolved/expired updates. Returns a typed pending entry that core persists across the resolve lifecycle, removing the need for a local approvalCardStore. - src/approval/approval-capability.ts: assembles the capability with origin-surface delivery; resolveOriginTarget reads turnSourceTo from the request as the raw conversationId/userId. Aligns DingTalk with the SDK contract introduced in openclaw 2026.4.7 (PR #62135), matching the model used by discord/telegram/slack/matrix.
…time
Wire the new ChannelApprovalCapability into the channel and remove the
legacy approval delivery path. SDK now drives the entire pending → resolved
lifecycle through the native runtime adapter; channel.sendPayload no
longer special-cases approval payloads.
Card button callbacks now resolve approvals directly via
resolveApprovalOverGateway from openclaw/plugin-sdk/approval-gateway-runtime,
removing the local handleApprovalCardCallback indirection. The /approve
text command intercept in inbound-handler does the same — it stays in place
because DingTalk's own session-lock would deadlock plugin-approval
waitDecision if /approve went through the normal inbound pipeline.
Changes
- src/channel.ts: replace `approvals: { render: ... }` block (which was
already a type error against the SDK channel plugin shape) with
`approvalCapability: dingtalkApprovalCapability`. Drop the
`_dingtalkApproval` channelData hack from sendPayload.
- src/inbound-handler.ts: /approve early intercept calls
resolveApprovalOverGateway directly with parsed approvalId/decision/
senderId; no command session dispatch indirection.
- src/approval-card-service.ts: trim down to card param map builders +
callback parsers. Card delivery and updates moved to the adapter.
- src/command/card-approve-command.ts: deleted (functionality replaced
by direct resolveApprovalOverGateway calls in two sites).
- tests/unit/approval-card-callback.test.ts: deleted (covered by adapter
tests + inbound-handler /approve tests).
- tests/unit/approval-card-service.test.ts: drop store/sendApprovalCard
tests; the adapter tests cover the API surface.
- tests/unit/inbound-handler.test.ts: rewrite /approve tests to assert
on resolveApprovalOverGateway invocation instead of command session
dispatch.
- package.json: bump peerDep openclaw to >=2026.4.7 (the version that
ships ChannelApprovalNativeRuntimeAdapter from PR #62135).
- tests/unit/{plugin-manifest,sdk-import-structure}.test.ts: align
expected min versions with the bump.
BrilliantWang
force-pushed
the
feat/approval-v2
branch
from
April 8, 2026 12:52
e4eba39 to
aa3fad1
Compare
…vate types `defineChannelPluginEntry` from openclaw/plugin-sdk/core returns an internal `DefinedChannelPluginEntry<TPlugin>` type that is not exported publicly. With `declaration: true` in tsconfig, TypeScript tries to emit a portable name for the inferred `default` export type and fails with TS2742 once the plugin type transitively references any non-public SDK internal (which happens as soon as we add `approvalCapability` referencing the new approval types). This project sets `noEmit: true`, so `declaration` and `declarationMap` only run as extra static checks — no .d.ts files are produced. Removing them trades that strictness for compatibility with the SDK's private internal types and matches how other channels in this ecosystem build.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(approval): DingTalk native approval support
背景
OpenClaw 的 ExecApproval / Plugin Approval 功能此前在 DingTalk channel 上没有原生支持。当 agent 命中需要审批的命令或工具时,DingTalk 用户只能通过 Web UI / 终端 UI 完成审批,无法在 DingTalk 会话内处理。
目标
在 DingTalk 会话内完整承接审批流程:
/approve <id> <decision>文本命令作为备用入口解决方案
实现 DingTalk 的
ChannelApprovalCapability,对齐 OpenClaw v2026.4.7 引入的ChannelApprovalNativeRuntimeAdapter契约(openclaw#62135)。Core 驱动整个 pending → resolved 生命周期,channel 实现 5 个 sub-adapter:availabilityturnSourceChannel === "dingtalk"的请求presentationCardBtn[]三按钮)transportcreateAndDeliver+ card streaming;resolved/expired 时更新卡片 UIinteractionsobserve投递模型:仅支持 origin surface(审批卡片发到 agent 所在的 DingTalk 会话),不走 approver DM 分发。DingTalk 的企业群/私聊本身就有天然的可见性边界。
按钮回调:
TOPIC_CARD回调直接调 SDK 的resolveApprovalOverGateway提交决策,不经过任何本地 store 或文本命令中转。/approve文本命令:在 inbound-handler 入口早期 intercept,同样直接调resolveApprovalOverGateway。此 bypass 是必需的:Plugin Approval 的waitDecision阻塞在dispatchReply内并持有 DingTalk 自己的 session lock,若/approve走正常 inbound 路径会 session lock 死锁,120s 超时失败。重要决策
使用 nativeRuntime adapter,不使用
approver-restricted上层工厂Telegram / Discord / Slack / Matrix 都通过
createApproverRestrictedNativeApprovalCapability装配 capability,围绕 "approver 列表" 语义设计。DingTalk 不引入 approver 配置概念,直接装配最小 capability。未来如有需要切到工厂方案,5 个 sub-adapter 可以原封复用。没有本地 store
Core 在
activeEntries内存 Map 中管理 pending entry 生命周期,transport.deliverPending返回的 entry(含outTrackId等)会自动带回给transport.updateEntry。channel 侧不需要自建 approvalCardStore。进程重启会丢失 pending 审批——这个行为和 core 一致,不是 channel 的责任。卡片内完成全部状态反馈
transport.updateEntry在 resolved/expired 时将按钮替换为状态文本("✅ 已允许"/"❌ 已拒绝"/"⏱️ 已过期")并置hasAction=false。不发额外的群聊文本回复,界面整洁。/approve文本命令同理。Breaking Change
peerDependencies.openclaw从>=2026.3.28bump 到>=2026.4.7,需要 v2026.4.7 提供的ChannelApprovalNativeRuntimeAdapter契约和resolveApprovalOverGateway公开 API。依赖
依赖 PR #480(AI Card template v2 /
CardBtn[]withsendCardRequest)合并后才能在真机上验证按钮交互。当前实现已按 PR #480 的回调格式编码。测试
/approve文本命令在直接消息和群聊(带 @bot 前缀)两种场景下生效