dlinkclaw-openclaw 2026.10.26 → 2026.10.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dlinkclaw-openclaw",
3
- "version": "2026.10.26",
3
+ "version": "2026.10.27",
4
4
  "description": "DLinkClaw OpenClaw channel plugin — 朵联 OpenClaw 接入通道",
5
5
  "keywords": [
6
6
  "DLinkClaw",
@@ -1 +1,12 @@
1
+ /**
2
+ * 2026-10-29 dlink 原生审批投递能力。
3
+ * core 产生审批请求时交给插件,插件转发中继;发起人提示由客户端弹窗负责(不改写会话气泡)。
4
+ */
5
+ type AnyObj = Record<string, any>;
1
6
  export declare function createDLApprovalCapability(): any;
7
+ /**
8
+ * 2026-10-29 轮询路径入口:把审批推给中继。
9
+ * 发起人的等待审批提示由中继通知 + 客户端弹窗完成,这里不改写会话状态。
10
+ */
11
+ export declare function announceWaiting(dlClient: any, payload: AnyObj): Promise<void>;
12
+ export {};
@@ -16,7 +16,7 @@ function pickRequest(ctx) {
16
16
  return {
17
17
  approvalId: firstString(payload.approvalId, req.id, req.approvalId),
18
18
  kind: firstString(ctx?.approvalKind, req.kind, 'exec'),
19
- agentId: firstString(req.agentId, view.agentId, req.sessionKey ? '' : ''),
19
+ agentId: firstString(req.agentId, view.agentId),
20
20
  sessionKey: firstString(req.sessionKey, view.sessionKey),
21
21
  command: firstString(payload.command, view.command, req.command, req.payload?.command),
22
22
  cwd: firstString(payload.cwd, view.cwd, req.cwd),
@@ -25,18 +25,11 @@ function pickRequest(ctx) {
25
25
  }
26
26
  export function createDLApprovalCapability() {
27
27
  return {
28
- availability: {
29
- // dlink 是唯一通道;账号已配置即可承接审批
30
- isConfigured: () => true,
31
- shouldHandle: () => true
32
- },
28
+ availability: { isConfigured: () => true, shouldHandle: () => true },
33
29
  presentation: {
34
30
  buildPendingPayload: (ctx) => {
35
31
  const info = pickRequest(ctx);
36
- return {
37
- ...info,
38
- text: firstString(ctx?.view?.summary, info.command)
39
- };
32
+ return { ...info, text: firstString(ctx?.view?.summary, info.command) };
40
33
  },
41
34
  buildResolvedResult: () => ({ kind: 'leave' }),
42
35
  buildExpiredResult: () => ({ kind: 'leave' })
@@ -65,10 +58,10 @@ export function createDLApprovalCapability() {
65
58
  cwd: info.cwd,
66
59
  expiresAtMs: info.expiresAtMs
67
60
  });
68
- logger.info?.(`approval forwarded to relay: id=${info.approvalId} agent=${info.agentId} kind=${info.kind}`);
61
+ logger.info?.('approval forwarded: id=' + info.approvalId + ' agent=' + info.agentId);
69
62
  }
70
63
  catch (e) {
71
- logger.warn?.(`approval forward failed: ${String(e)}`);
64
+ logger.warn?.('approval forward failed: ' + String(e));
72
65
  }
73
66
  return { approvalId: info.approvalId, deliveredAt: Date.now() };
74
67
  },
@@ -78,3 +71,20 @@ export function createDLApprovalCapability() {
78
71
  interactions: {}
79
72
  };
80
73
  }
74
+ /**
75
+ * 2026-10-29 轮询路径入口:把审批推给中继。
76
+ * 发起人的等待审批提示由中继通知 + 客户端弹窗完成,这里不改写会话状态。
77
+ */
78
+ export async function announceWaiting(dlClient, payload) {
79
+ const logger = getDLLogger();
80
+ try {
81
+ const pushed = await dlClient.pushApprovalEvent(payload);
82
+ if (!pushed) {
83
+ await dlClient.publishEventToServer(payload, 'onApproval', '1');
84
+ }
85
+ logger.info?.('approval pushed: id=' + String(payload?.approvalId) + ' names=' + JSON.stringify(pushed?.approverNames ?? []));
86
+ }
87
+ catch (e) {
88
+ logger.warn?.('announceWaiting failed: ' + String(e));
89
+ }
90
+ }
@@ -4,6 +4,7 @@ import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
4
4
  import { buildChannelInboundEventContext } from "openclaw/plugin-sdk/channel-inbound";
5
5
  import { beginPersonaScope, endPersonaScope } from "../persona.js";
6
6
  import { uuidStr } from "../utils.js";
7
+ import { rememberStream, forgetStream } from "../stream-map.js";
7
8
  /** 2026-10-18:只有包含复选框行(- [ ] / - [x] / - [/])的内容才算任务清单。 */
8
9
  function isTaskListContent(content) {
9
10
  return /(^|\n)\s*[-*]\s*\[[ xX/~%>]*\]\s*\S/.test(content);
@@ -993,6 +994,8 @@ export async function routeAndDispatchMessage(ctx) {
993
994
  // 2026-10-26 群聊人设作用域:分发期间绑定被 @ 的 agent,整轮(含工具循环)都能读到正确值
994
995
  // 2026-10-26 OpenClaw 的消息上下文字段名是 SessionKey(大写),兼容两种写法
995
996
  personaScopeKey = ((ctx.SessionKey ?? ctx.sessionKey) || '');
997
+ // 2026-10-29 记录 sessionKey → streamId,审批等待提示需要
998
+ rememberStream(personaScopeKey, streamId, accountId);
996
999
  logger.debug?.(`persona scope: session=${personaScopeKey} agent=${personaAgentId ?? '-'}`);
997
1000
  if (personaAgentId) {
998
1001
  beginPersonaScope(personaScopeKey, personaAgentId);
@@ -1039,6 +1042,7 @@ export async function routeAndDispatchMessage(ctx) {
1039
1042
  },
1040
1043
  onSettled: async () => {
1041
1044
  endScopeOnce();
1045
+ forgetStream(personaScopeKey);
1042
1046
  if ((accStatus.accumulatedPayload && accStatus.accumulatedPayload.length > 0) || mediaFiles.length > 0) {
1043
1047
  //发送结束状态
1044
1048
  await client.publishProcessingState({
@@ -2,6 +2,7 @@
2
2
  * CMD 消息处理器
3
3
  * 集成到 duolian 的消息处理流程中
4
4
  */
5
+ import { announceWaiting } from '../approval-native.js';
5
6
  import ClawServices from "./services.js";
6
7
  const AGENT_RELAY_MAP = new Map();
7
8
  // 2026-10-13 去重缓存(石头哥需求:更精准控制 + 减少不必要的 MQTT 推送):
@@ -25,8 +26,8 @@ export class CmdHandler {
25
26
  // 2026-10-22:用量上报走服务端事件,由中继落库
26
27
  void this.dlClient.publishEventToServer(payload, 'onUsage', '1');
27
28
  }, (payload) => {
28
- // 2026-10-27 审批待审推送:插件发现新条目即上报中继,中继再推审批人
29
- void this.dlClient.publishEventToServer(payload, 'onApproval', '1');
29
+ // 2026-10-29 审批推送:优先走 HTTP(可拿到审批人姓名),失败退回 MQTT 事件
30
+ void announceWaiting(this.dlClient, payload);
30
31
  });
31
32
  }
32
33
  log(msg, type) {
@@ -70,7 +70,7 @@ export class ClawServices extends EventEmitter {
70
70
  continue;
71
71
  }
72
72
  this.approvalSeen.add(String(id));
73
- publish({
73
+ const pushed = await publish({
74
74
  approvalId: String(id),
75
75
  kind,
76
76
  agentId: item?.agentId ?? item?.request?.agentId ?? '',
@@ -80,6 +80,7 @@ export class ClawServices extends EventEmitter {
80
80
  createdAtMs: item?.createdAtMs ?? 0,
81
81
  expiresAtMs: item?.expiresAtMs ?? 0
82
82
  });
83
+ void pushed;
83
84
  }
84
85
  }
85
86
  if (this.approvalSeen.size > 500) {
@@ -0,0 +1,6 @@
1
+ export declare function rememberStream(sessionKey: string, streamId: string, accountId: string): void;
2
+ export declare function streamOf(sessionKey: string): {
3
+ streamId: string;
4
+ accountId: string;
5
+ } | undefined;
6
+ export declare function forgetStream(sessionKey: string): void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 2026-10-29 sessionKey → 当前运行 streamId 映射。
3
+ * 审批发生时只有 sessionKey,而推送处理状态需要 streamId。
4
+ */
5
+ const map = new Map();
6
+ const TTL_MS = 30 * 60 * 1000;
7
+ export function rememberStream(sessionKey, streamId, accountId) {
8
+ if (!sessionKey || !streamId) {
9
+ return;
10
+ }
11
+ // 2026-10-29 sessionKey 大小写可能不一致,统一小写归一
12
+ map.set(sessionKey.toLowerCase(), { streamId, accountId, at: Date.now() });
13
+ }
14
+ export function streamOf(sessionKey) {
15
+ if (!sessionKey) {
16
+ return undefined;
17
+ }
18
+ const hit = map.get(sessionKey.toLowerCase());
19
+ if (!hit) {
20
+ return undefined;
21
+ }
22
+ if (Date.now() - hit.at > TTL_MS) {
23
+ map.delete(sessionKey.toLowerCase());
24
+ return undefined;
25
+ }
26
+ return { streamId: hit.streamId, accountId: hit.accountId };
27
+ }
28
+ export function forgetStream(sessionKey) {
29
+ if (sessionKey) {
30
+ map.delete(sessionKey.toLowerCase());
31
+ }
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dlinkclaw-openclaw",
3
- "version": "2026.10.26",
3
+ "version": "2026.10.27",
4
4
  "description": "DLinkClaw OpenClaw channel plugin — 朵联 OpenClaw 接入通道",
5
5
  "keywords": [
6
6
  "DLinkClaw",