@efengx/openclaw-channel-dragon 0.5.27 → 0.5.29
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/components/channel/ChannelComponent.d.ts +4 -0
- package/dist/components/channel/ChannelComponent.js +80 -1
- package/dist/components/telemetry/TelemetryComponent.d.ts +1 -1
- package/dist/components/telemetry/TelemetryComponent.js +2 -2
- package/dist/index.js +69 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -18,7 +18,11 @@ export declare class ChannelComponent implements IComponent {
|
|
|
18
18
|
private normalizeSessionSegment;
|
|
19
19
|
private resolveOpenClawAgentId;
|
|
20
20
|
private buildOpenClawSessionKey;
|
|
21
|
+
private resolveWorkbenchSessionIdFromOpenClawSessionKey;
|
|
21
22
|
private stringifyProgressDetail;
|
|
23
|
+
private findTaskCompletionEvent;
|
|
24
|
+
private extractGeneratedMediaLines;
|
|
25
|
+
private formatTaskCompletionReply;
|
|
22
26
|
deliverToOpenClaw: (content: string, sessionId?: string, modelId?: string, attachments?: any[], messageId?: string | number) => Promise<void>;
|
|
23
27
|
handleOutboundText: (ctx: any) => Promise<{
|
|
24
28
|
ok: boolean;
|
|
@@ -34,6 +34,19 @@ export class ChannelComponent {
|
|
|
34
34
|
const workbenchSessionId = this.normalizeSessionSegment(sessionId, 'default');
|
|
35
35
|
return `agent:${openClawAgentId}:dragon:direct:${dragonAgentId}:${workbenchSessionId}`;
|
|
36
36
|
}
|
|
37
|
+
resolveWorkbenchSessionIdFromOpenClawSessionKey(sessionKey) {
|
|
38
|
+
const raw = String(sessionKey || '').trim();
|
|
39
|
+
if (!raw)
|
|
40
|
+
return 'default';
|
|
41
|
+
const parts = raw.split(':');
|
|
42
|
+
if (parts[0] === 'agent' && parts[2] === channelId) {
|
|
43
|
+
return parts.slice(5).join(':') || 'default';
|
|
44
|
+
}
|
|
45
|
+
if (parts[0] === channelId) {
|
|
46
|
+
return parts.slice(3).join(':') || 'default';
|
|
47
|
+
}
|
|
48
|
+
return 'default';
|
|
49
|
+
}
|
|
37
50
|
stringifyProgressDetail(value, maxLength = 240) {
|
|
38
51
|
if (typeof value === 'string') {
|
|
39
52
|
const text = value.trim();
|
|
@@ -49,6 +62,48 @@ export class ChannelComponent {
|
|
|
49
62
|
return String(value).slice(0, maxLength);
|
|
50
63
|
}
|
|
51
64
|
}
|
|
65
|
+
findTaskCompletionEvent(value) {
|
|
66
|
+
if (!value || typeof value !== 'object')
|
|
67
|
+
return undefined;
|
|
68
|
+
if (value.type === 'task_completion')
|
|
69
|
+
return value;
|
|
70
|
+
const candidates = [
|
|
71
|
+
value.event,
|
|
72
|
+
value.data,
|
|
73
|
+
...(Array.isArray(value.internalEvents) ? value.internalEvents : []),
|
|
74
|
+
...(Array.isArray(value.events) ? value.events : []),
|
|
75
|
+
];
|
|
76
|
+
for (const candidate of candidates) {
|
|
77
|
+
const found = this.findTaskCompletionEvent(candidate);
|
|
78
|
+
if (found)
|
|
79
|
+
return found;
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
extractGeneratedMediaLines(event) {
|
|
84
|
+
const lines = new Set();
|
|
85
|
+
for (const url of Array.isArray(event?.mediaUrls) ? event.mediaUrls : []) {
|
|
86
|
+
if (typeof url === 'string' && url.trim()) {
|
|
87
|
+
lines.add(`MEDIA:${url.trim()}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const attachment of Array.isArray(event?.attachments) ? event.attachments : []) {
|
|
91
|
+
const value = attachment?.url || attachment?.path || attachment?.filePath || attachment?.mediaUrl;
|
|
92
|
+
if (typeof value === 'string' && value.trim()) {
|
|
93
|
+
lines.add(`MEDIA:${value.trim()}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return Array.from(lines);
|
|
97
|
+
}
|
|
98
|
+
formatTaskCompletionReply(event) {
|
|
99
|
+
const result = typeof event?.result === 'string' ? event.result.trim() : '';
|
|
100
|
+
const mediaLines = this.extractGeneratedMediaLines(event);
|
|
101
|
+
const lines = [
|
|
102
|
+
result,
|
|
103
|
+
...mediaLines.filter((line) => !result.includes(line)),
|
|
104
|
+
].filter((line) => line.trim());
|
|
105
|
+
return lines.join('\n');
|
|
106
|
+
}
|
|
52
107
|
deliverToOpenClaw = async (content, sessionId = 'default', modelId, attachments, messageId) => {
|
|
53
108
|
const replyMsgId = messageId ? `dragon_msg_${messageId}` : `dragon_msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
54
109
|
if (messageId && this.processedMessageIds.has(messageId))
|
|
@@ -305,6 +360,30 @@ export class ChannelComponent {
|
|
|
305
360
|
return { ok: true, messageId: Date.now().toString() };
|
|
306
361
|
};
|
|
307
362
|
handleAgentEvent = async (evt) => {
|
|
308
|
-
|
|
363
|
+
const sessionId = evt?.sessionId ||
|
|
364
|
+
this.resolveWorkbenchSessionIdFromOpenClawSessionKey(evt?.sessionKey);
|
|
365
|
+
await this.telemetry.reportEvent(evt.stream, evt.data, evt.ts, sessionId);
|
|
366
|
+
const taskCompletion = this.findTaskCompletionEvent(evt?.data);
|
|
367
|
+
if (!taskCompletion)
|
|
368
|
+
return;
|
|
369
|
+
const content = this.formatTaskCompletionReply(taskCompletion);
|
|
370
|
+
if (!content)
|
|
371
|
+
return;
|
|
372
|
+
await this.telemetry.reportProgress({
|
|
373
|
+
sessionId,
|
|
374
|
+
msgId: taskCompletion.childSessionId ? `dragon_task_${taskCompletion.childSessionId}` : undefined,
|
|
375
|
+
kind: 'task_completion',
|
|
376
|
+
phase: 'end',
|
|
377
|
+
status: taskCompletion.status === 'ok' ? 'completed' : taskCompletion.status,
|
|
378
|
+
title: taskCompletion.status === 'ok' ? '后台任务完成' : '后台任务更新',
|
|
379
|
+
detail: this.stringifyProgressDetail(taskCompletion.taskLabel || taskCompletion.statusLabel),
|
|
380
|
+
data: taskCompletion,
|
|
381
|
+
});
|
|
382
|
+
await this.telemetry.reportReply({
|
|
383
|
+
content,
|
|
384
|
+
sessionId,
|
|
385
|
+
source: 'agent_event_task_completion',
|
|
386
|
+
msgId: taskCompletion.childSessionId ? `dragon_task_reply_${taskCompletion.childSessionId}` : undefined,
|
|
387
|
+
});
|
|
309
388
|
};
|
|
310
389
|
}
|
|
@@ -16,7 +16,7 @@ export declare class TelemetryComponent implements IComponent {
|
|
|
16
16
|
source?: string;
|
|
17
17
|
msgId?: string;
|
|
18
18
|
}): Promise<void>;
|
|
19
|
-
reportEvent(stream: string, data: any, ts: number): Promise<void>;
|
|
19
|
+
reportEvent(stream: string, data: any, ts: number, sessionId?: string): Promise<void>;
|
|
20
20
|
reportProgress(payload: {
|
|
21
21
|
sessionId: string;
|
|
22
22
|
msgId?: string;
|
|
@@ -36,7 +36,7 @@ export class TelemetryComponent {
|
|
|
36
36
|
// Error logged by HttpComponent
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
async reportEvent(stream, data, ts) {
|
|
39
|
+
async reportEvent(stream, data, ts, sessionId = "default") {
|
|
40
40
|
try {
|
|
41
41
|
let telemetryPayload = null;
|
|
42
42
|
if (stream === "thinking") {
|
|
@@ -63,7 +63,7 @@ export class TelemetryComponent {
|
|
|
63
63
|
this.http.fetch(`/api/agents/${this.agentId}/messages/reply`, {
|
|
64
64
|
method: "POST",
|
|
65
65
|
headers: { "Content-Type": "application/json" },
|
|
66
|
-
body: JSON.stringify({ ...telemetryPayload, msgId }),
|
|
66
|
+
body: JSON.stringify({ ...telemetryPayload, sessionId, msgId }),
|
|
67
67
|
}).catch(() => { });
|
|
68
68
|
}
|
|
69
69
|
}
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,18 @@ import { dragonChannelPluginVersion } from "./version.js";
|
|
|
10
10
|
const channelId = "dragon";
|
|
11
11
|
let cachedRuntime;
|
|
12
12
|
const containers = new Map();
|
|
13
|
+
function isDragonSessionKey(sessionKey) {
|
|
14
|
+
const raw = String(sessionKey || '').trim();
|
|
15
|
+
return raw.startsWith(`${channelId}:`) || /^agent:[^:]+:dragon(?::|$)/.test(raw);
|
|
16
|
+
}
|
|
17
|
+
function resolveAccountIdFromSessionKey(sessionKey) {
|
|
18
|
+
const raw = String(sessionKey || '').trim();
|
|
19
|
+
const parts = raw.split(':');
|
|
20
|
+
if (parts[0] === channelId) {
|
|
21
|
+
return parts[2] || "default";
|
|
22
|
+
}
|
|
23
|
+
return "default";
|
|
24
|
+
}
|
|
13
25
|
async function getOrCreateContainer(account, ctx) {
|
|
14
26
|
const key = `${account.agentId}:${account.orchestratorUrl}`;
|
|
15
27
|
if (containers.has(key))
|
|
@@ -147,37 +159,84 @@ const plugin = createChatChannelPlugin({
|
|
|
147
159
|
},
|
|
148
160
|
handleAction: async (ctx) => {
|
|
149
161
|
const { action, params, cfg, accountId } = ctx;
|
|
162
|
+
const logger = ctx?.logger ?? ctx?.log ?? cachedRuntime?.logger ?? cachedRuntime?.log;
|
|
163
|
+
logger?.info?.(`[Dragon Plugin] handleAction start: action=${action}, accountId=${accountId}, paramsKeys=${Object.keys(params || {})}`);
|
|
150
164
|
if (action === "send") {
|
|
151
165
|
const account = base.config.resolveAccount(cfg, accountId);
|
|
152
166
|
const container = await getOrCreateContainer(account, ctx);
|
|
153
167
|
const channel = container.get('channel');
|
|
154
168
|
let target = params.to || params.target || ctx.toolContext?.currentChannelId || "default";
|
|
169
|
+
logger?.info?.(`[Dragon Plugin] handleAction target resolution input: ${target}`);
|
|
155
170
|
for (const prefix of ["dragon-workbench-", "dragon-workbench:", "dragon-", "dragon:"]) {
|
|
156
171
|
if (target.startsWith(prefix)) {
|
|
157
172
|
target = target.substring(prefix.length);
|
|
158
173
|
}
|
|
159
174
|
}
|
|
160
175
|
const sessionId = target || "default";
|
|
176
|
+
logger?.info?.(`[Dragon Plugin] handleAction target resolved: sessionId=${sessionId}, rawTarget=${target}`);
|
|
177
|
+
// Collect ALL media URLs from the params (mirrors what OpenClaw tracks internally
|
|
178
|
+
// via collectMessagingMediaUrlsFromRecord when the tool call starts, so that
|
|
179
|
+
// hasGatewayAgentDeliveredExpectedMedia can match them against expectedMediaUrls).
|
|
180
|
+
const sentMediaUrls = [];
|
|
181
|
+
const pushMedia = (v, source) => {
|
|
182
|
+
if (typeof v === 'string' && v.trim()) {
|
|
183
|
+
const url = v.trim();
|
|
184
|
+
sentMediaUrls.push(url);
|
|
185
|
+
logger?.info?.(`[Dragon Plugin] handleAction extracted media URL from ${source}: ${url}`);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
pushMedia(params.media, "params.media");
|
|
189
|
+
pushMedia(params.mediaUrl, "params.mediaUrl");
|
|
190
|
+
pushMedia(params.path, "params.path");
|
|
191
|
+
pushMedia(params.filePath, "params.filePath");
|
|
192
|
+
pushMedia(params.fileUrl, "params.fileUrl");
|
|
193
|
+
if (Array.isArray(params.mediaUrls)) {
|
|
194
|
+
logger?.info?.(`[Dragon Plugin] handleAction found params.mediaUrls array, size=${params.mediaUrls.length}`);
|
|
195
|
+
for (const u of params.mediaUrls)
|
|
196
|
+
pushMedia(u, "params.mediaUrls");
|
|
197
|
+
}
|
|
198
|
+
if (Array.isArray(params.attachments)) {
|
|
199
|
+
logger?.info?.(`[Dragon Plugin] handleAction found params.attachments array, size=${params.attachments.length}`);
|
|
200
|
+
for (const a of params.attachments) {
|
|
201
|
+
if (a && typeof a === 'object') {
|
|
202
|
+
pushMedia(a.media, "attachment.media");
|
|
203
|
+
pushMedia(a.mediaUrl, "attachment.mediaUrl");
|
|
204
|
+
pushMedia(a.path, "attachment.path");
|
|
205
|
+
pushMedia(a.filePath, "attachment.filePath");
|
|
206
|
+
pushMedia(a.fileUrl, "attachment.fileUrl");
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
logger?.info?.(`[Dragon Plugin] handleAction total media URLs extracted: count=${sentMediaUrls.length}, urls=${JSON.stringify(sentMediaUrls)}`);
|
|
161
211
|
let text = params.message || params.text || "";
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
212
|
+
logger?.info?.(`[Dragon Plugin] handleAction base message text length: ${text.length}`);
|
|
213
|
+
// Append media path to text so the Workbench can render the image
|
|
214
|
+
for (const url of sentMediaUrls) {
|
|
215
|
+
const mediaLine = `MEDIA: ${url}`;
|
|
165
216
|
if (!text.includes(mediaLine)) {
|
|
166
217
|
text = text ? `${text}\n${mediaLine}` : mediaLine;
|
|
167
218
|
}
|
|
168
219
|
}
|
|
220
|
+
logger?.info?.(`[Dragon Plugin] handleAction sending text over channel: text_len=${text.length}, targetSession=${sessionId}`);
|
|
169
221
|
const result = await channel.handleOutboundText({
|
|
170
222
|
text,
|
|
171
223
|
peer: { id: sessionId }
|
|
172
224
|
});
|
|
173
|
-
|
|
225
|
+
logger?.info?.(`[Dragon Plugin] handleAction channel send result: ok=${result.ok}, messageId=${result.messageId || "none"}`);
|
|
226
|
+
const evidence = {
|
|
174
227
|
ok: result.ok,
|
|
175
228
|
...result.messageId ? { messageId: result.messageId } : {},
|
|
229
|
+
// Evidence fields that OpenClaw checks via hasMessagingToolDeliveryEvidence
|
|
230
|
+
// and hasGatewayAgentDeliveredExpectedMedia
|
|
176
231
|
didSendViaMessagingTool: true,
|
|
177
|
-
messagingToolSentTexts: [text],
|
|
178
|
-
|
|
232
|
+
messagingToolSentTexts: text ? [text] : [],
|
|
233
|
+
messagingToolSentMediaUrls: sentMediaUrls,
|
|
234
|
+
messagingToolSentTargets: [{ to: target, ...(sentMediaUrls.length > 0 ? { mediaUrls: sentMediaUrls } : {}) }],
|
|
179
235
|
};
|
|
236
|
+
logger?.info?.(`[Dragon Plugin] handleAction returning evidence to OpenClaw: didSendViaMessagingTool=${evidence.didSendViaMessagingTool}, sentMediaUrlsCount=${evidence.messagingToolSentMediaUrls.length}`);
|
|
237
|
+
return evidence;
|
|
180
238
|
}
|
|
239
|
+
logger?.warn?.(`[Dragon Plugin] handleAction action "${action}" is not supported. Ignoring.`);
|
|
181
240
|
throw new Error(`Action "${action}" is not supported by the dragon channel plugin.`);
|
|
182
241
|
}
|
|
183
242
|
}
|
|
@@ -205,14 +264,15 @@ const entry = defineChannelPluginEntry({
|
|
|
205
264
|
if (typeof agentEventHandler === 'function') {
|
|
206
265
|
agentEventHandler(async (evt) => {
|
|
207
266
|
const sessionKey = evt.sessionKey;
|
|
208
|
-
if (!sessionKey
|
|
267
|
+
if (!isDragonSessionKey(sessionKey))
|
|
209
268
|
return;
|
|
210
|
-
const accountId = sessionKey
|
|
269
|
+
const accountId = resolveAccountIdFromSessionKey(sessionKey);
|
|
211
270
|
const account = base.config.resolveAccount(api?.runtime?.cfg, accountId);
|
|
212
271
|
const container = await getOrCreateContainer(account, {
|
|
213
272
|
cfg: api?.runtime?.cfg,
|
|
214
273
|
abortSignal: new AbortController().signal,
|
|
215
|
-
channelRuntime: api?.runtime?.channelRuntime
|
|
274
|
+
channelRuntime: api?.runtime?.channelRuntime,
|
|
275
|
+
logger: api?.runtime?.logger ?? api?.runtime?.log,
|
|
216
276
|
});
|
|
217
277
|
const channel = container.get('channel');
|
|
218
278
|
await channel.handleAgentEvent(evt);
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const dragonChannelPluginVersion = "0.5.
|
|
1
|
+
export declare const dragonChannelPluginVersion = "0.5.29";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const dragonChannelPluginVersion = "0.5.
|
|
1
|
+
export const dragonChannelPluginVersion = "0.5.29";
|