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 +1 -1
- package/dist/src/approval-native.d.ts +11 -0
- package/dist/src/approval-native.js +22 -12
- package/dist/src/client/message.js +4 -0
- package/dist/src/services/index.js +3 -2
- package/dist/src/services/services.js +2 -1
- package/dist/src/stream-map.d.ts +6 -0
- package/dist/src/stream-map.js +32 -0
- package/package.json +1 -1
package/dist/package.json
CHANGED
|
@@ -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
|
|
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?.(
|
|
61
|
+
logger.info?.('approval forwarded: id=' + info.approvalId + ' agent=' + info.agentId);
|
|
69
62
|
}
|
|
70
63
|
catch (e) {
|
|
71
|
-
logger.warn?.(
|
|
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-
|
|
29
|
-
void this.dlClient
|
|
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
|
+
}
|