@efengx/openclaw-channel-dragon 0.5.23 → 0.5.25

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.
@@ -76,156 +76,218 @@ export class ChannelComponent {
76
76
  msgId: replyMsgId,
77
77
  ...payload,
78
78
  });
79
- await replyDispatcher({
80
- ctx: {
81
- Body: content,
82
- From: sessionId === 'default' ? "workbench-user" : `workbench-user-${sessionId}`,
83
- To: sessionId === 'default' ? "dragon-workbench" : `dragon-workbench-${sessionId}`,
84
- ChatType: "direct",
85
- Provider: channelId,
86
- ChannelId: channelId,
87
- AccountId: accountId,
88
- SessionKey: sessionKey,
89
- Timestamp: Date.now(),
90
- Model: modelId,
91
- Attachments: attachments,
92
- },
93
- cfg,
94
- dispatcherOptions: {
95
- deliver: async (payload) => {
96
- const text = payload?.text || "";
97
- if (!text && !payload?.tool_calls?.length)
98
- return;
99
- await this.telemetry.reportReply({
100
- content: text,
101
- sessionId,
102
- tool_calls: payload?.tool_calls,
103
- reasoning_content: payload?.reasoning_content,
104
- source: "telemetry_deliver",
105
- msgId: replyMsgId,
106
- });
107
- }
108
- },
109
- replyOptions: {
110
- onReplyStart: async () => {
111
- await progress({ kind: 'lifecycle', phase: 'start', status: 'running', title: '开始处理请求' });
112
- },
113
- onAssistantMessageStart: async () => {
114
- await progress({ kind: 'assistant', phase: 'start', status: 'running', title: '开始生成回复' });
115
- },
116
- onPartialReply: async (payload) => {
117
- await progress({
118
- kind: 'assistant_delta',
119
- phase: 'update',
120
- status: 'running',
121
- title: '正在生成回复',
122
- detail: this.stringifyProgressDetail(payload?.delta || payload?.text),
123
- data: { replace: payload?.replace === true },
124
- });
125
- },
126
- onReasoningStream: async (payload) => {
127
- await progress({
128
- kind: 'reasoning',
129
- phase: 'update',
130
- status: 'running',
131
- title: '正在推理',
132
- detail: this.stringifyProgressDetail(payload?.text || payload),
133
- });
134
- },
135
- onReasoningEnd: async () => {
136
- await progress({ kind: 'reasoning', phase: 'end', status: 'completed', title: '推理完成' });
137
- },
138
- onToolStart: async (payload) => {
139
- await progress({
140
- kind: 'tool',
141
- phase: payload?.phase || 'start',
142
- status: 'running',
143
- title: payload?.name ? `调用工具:${payload.name}` : '调用工具',
144
- detail: this.stringifyProgressDetail(payload?.args),
145
- data: payload,
146
- });
147
- },
148
- onToolResult: async (payload) => {
149
- await progress({
150
- kind: 'tool',
151
- phase: 'end',
152
- status: 'completed',
153
- title: '工具执行完成',
154
- detail: this.stringifyProgressDetail(payload?.text || payload),
155
- });
156
- },
157
- onItemEvent: async (payload) => {
158
- await progress({
159
- kind: payload?.kind || 'item',
160
- phase: payload?.phase,
161
- status: payload?.status,
162
- title: payload?.title || payload?.name || '任务步骤更新',
163
- detail: this.stringifyProgressDetail(payload?.progressText || payload?.summary || payload?.meta),
164
- data: payload,
165
- });
166
- },
167
- onPlanUpdate: async (payload) => {
168
- await progress({
169
- kind: 'plan',
170
- phase: payload?.phase || 'update',
171
- status: 'running',
172
- title: payload?.title || '更新执行计划',
173
- detail: this.stringifyProgressDetail(payload?.explanation || payload?.steps),
174
- data: payload,
175
- });
79
+ try {
80
+ await replyDispatcher({
81
+ ctx: {
82
+ Body: content,
83
+ From: sessionId === 'default' ? "workbench-user" : `workbench-user-${sessionId}`,
84
+ To: sessionId === 'default' ? "dragon-workbench" : `dragon-workbench-${sessionId}`,
85
+ ChatType: "direct",
86
+ Provider: channelId,
87
+ ChannelId: channelId,
88
+ AccountId: accountId,
89
+ SessionKey: sessionKey,
90
+ Timestamp: Date.now(),
91
+ Model: modelId,
92
+ Attachments: attachments,
176
93
  },
177
- onApprovalEvent: async (payload) => {
178
- await progress({
179
- kind: 'approval',
180
- phase: payload?.phase || 'update',
181
- status: payload?.status || 'running',
182
- title: payload?.title || '等待审批',
183
- detail: this.stringifyProgressDetail(payload?.message || payload?.reason || payload?.command),
184
- data: payload,
185
- });
94
+ cfg,
95
+ dispatcherOptions: {
96
+ deliver: async (payload) => {
97
+ const text = payload?.text || "";
98
+ if (!text && !payload?.tool_calls?.length)
99
+ return;
100
+ await this.telemetry.reportReply({
101
+ content: text,
102
+ sessionId,
103
+ tool_calls: payload?.tool_calls,
104
+ reasoning_content: payload?.reasoning_content,
105
+ source: "telemetry_deliver",
106
+ msgId: replyMsgId,
107
+ });
108
+ }
186
109
  },
187
- onCommandOutput: async (payload) => {
188
- await progress({
189
- kind: 'command',
190
- phase: payload?.phase || 'update',
191
- status: payload?.status || 'running',
192
- title: payload?.title || payload?.name || '命令执行中',
193
- detail: this.stringifyProgressDetail(payload?.output || payload?.cwd),
194
- data: payload,
195
- });
110
+ replyOptions: {
111
+ onReplyStart: async () => {
112
+ await progress({ kind: 'lifecycle', phase: 'start', status: 'running', title: '开始处理请求' });
113
+ },
114
+ onAssistantMessageStart: async () => {
115
+ await progress({ kind: 'assistant', phase: 'start', status: 'running', title: '开始生成回复' });
116
+ },
117
+ onPartialReply: async (payload) => {
118
+ await progress({
119
+ kind: 'assistant_delta',
120
+ phase: 'update',
121
+ status: 'running',
122
+ title: '正在生成回复',
123
+ detail: this.stringifyProgressDetail(payload?.delta || payload?.text),
124
+ data: { replace: payload?.replace === true },
125
+ });
126
+ },
127
+ onReasoningStream: async (payload) => {
128
+ await progress({
129
+ kind: 'reasoning',
130
+ phase: 'update',
131
+ status: 'running',
132
+ title: '正在推理',
133
+ detail: this.stringifyProgressDetail(payload?.text || payload),
134
+ });
135
+ },
136
+ onReasoningEnd: async () => {
137
+ await progress({ kind: 'reasoning', phase: 'end', status: 'completed', title: '推理完成' });
138
+ },
139
+ onToolStart: async (payload) => {
140
+ await progress({
141
+ kind: 'tool',
142
+ phase: payload?.phase || 'start',
143
+ status: 'running',
144
+ title: payload?.name ? `调用工具:${payload.name}` : '调用工具',
145
+ detail: this.stringifyProgressDetail(payload?.args),
146
+ data: payload,
147
+ });
148
+ },
149
+ onToolResult: async (payload) => {
150
+ await progress({
151
+ kind: 'tool',
152
+ phase: 'end',
153
+ status: 'completed',
154
+ title: '工具执行完成',
155
+ detail: this.stringifyProgressDetail(payload?.text || payload),
156
+ });
157
+ },
158
+ onItemEvent: async (payload) => {
159
+ await progress({
160
+ kind: payload?.kind || 'item',
161
+ phase: payload?.phase,
162
+ status: payload?.status,
163
+ title: payload?.title || payload?.name || '任务步骤更新',
164
+ detail: this.stringifyProgressDetail(payload?.progressText || payload?.summary || payload?.meta),
165
+ data: payload,
166
+ });
167
+ },
168
+ onPlanUpdate: async (payload) => {
169
+ await progress({
170
+ kind: 'plan',
171
+ phase: payload?.phase || 'update',
172
+ status: 'running',
173
+ title: payload?.title || '更新执行计划',
174
+ detail: this.stringifyProgressDetail(payload?.explanation || payload?.steps),
175
+ data: payload,
176
+ });
177
+ },
178
+ onApprovalEvent: async (payload) => {
179
+ await progress({
180
+ kind: 'approval',
181
+ phase: payload?.phase || 'update',
182
+ status: payload?.status || 'running',
183
+ title: payload?.title || '等待审批',
184
+ detail: this.stringifyProgressDetail(payload?.message || payload?.reason || payload?.command),
185
+ data: payload,
186
+ });
187
+ },
188
+ onCommandOutput: async (payload) => {
189
+ await progress({
190
+ kind: 'command',
191
+ phase: payload?.phase || 'update',
192
+ status: payload?.status || 'running',
193
+ title: payload?.title || payload?.name || '命令执行中',
194
+ detail: this.stringifyProgressDetail(payload?.output || payload?.cwd),
195
+ data: payload,
196
+ });
197
+ },
198
+ onPatchSummary: async (payload) => {
199
+ await progress({
200
+ kind: 'patch',
201
+ phase: payload?.phase || 'end',
202
+ status: payload?.status || 'completed',
203
+ title: payload?.title || '代码变更完成',
204
+ detail: this.stringifyProgressDetail(payload?.summary || {
205
+ added: payload?.added,
206
+ modified: payload?.modified,
207
+ deleted: payload?.deleted,
208
+ }),
209
+ data: payload,
210
+ });
211
+ },
212
+ onCompactionStart: async () => {
213
+ await progress({ kind: 'compaction', phase: 'start', status: 'running', title: '正在压缩上下文' });
214
+ },
215
+ onCompactionEnd: async () => {
216
+ await progress({ kind: 'compaction', phase: 'end', status: 'completed', title: '上下文压缩完成' });
217
+ },
218
+ onModelSelected: (model) => {
219
+ void progress({
220
+ kind: 'model',
221
+ phase: 'selected',
222
+ status: 'completed',
223
+ title: '已选择模型',
224
+ detail: [model?.provider, model?.model].filter(Boolean).join('/'),
225
+ data: model,
226
+ });
227
+ },
196
228
  },
197
- onPatchSummary: async (payload) => {
198
- await progress({
199
- kind: 'patch',
200
- phase: payload?.phase || 'end',
201
- status: payload?.status || 'completed',
202
- title: payload?.title || '代码变更完成',
203
- detail: this.stringifyProgressDetail(payload?.summary || {
204
- added: payload?.added,
205
- modified: payload?.modified,
206
- deleted: payload?.deleted,
207
- }),
208
- data: payload,
209
- });
210
- },
211
- onCompactionStart: async () => {
212
- await progress({ kind: 'compaction', phase: 'start', status: 'running', title: '正在压缩上下文' });
213
- },
214
- onCompactionEnd: async () => {
215
- await progress({ kind: 'compaction', phase: 'end', status: 'completed', title: '上下文压缩完成' });
216
- },
217
- onModelSelected: (model) => {
218
- void progress({
219
- kind: 'model',
220
- phase: 'selected',
221
- status: 'completed',
222
- title: '已选择模型',
223
- detail: [model?.provider, model?.model].filter(Boolean).join('/'),
224
- data: model,
225
- });
226
- },
227
- },
228
- });
229
+ });
230
+ }
231
+ catch (err) {
232
+ logger?.error?.(`[Dragon Plugin] Error during replyDispatcher: ${err?.message || err}`);
233
+ let resolvedErrorMessage = err?.message || String(err);
234
+ const isTakeoverError = resolvedErrorMessage.includes("session file changed while embedded prompt lock was released");
235
+ if (isTakeoverError) {
236
+ try {
237
+ // Read actual error from the session file!
238
+ const { loadSessionStore, resolveStorePath } = (await import("openclaw/plugin-sdk/session-store-runtime"));
239
+ const { readFileSync } = await import("fs");
240
+ const storePath = resolveStorePath(cfg.session?.store, { agentId: this.resolveOpenClawAgentId() });
241
+ const store = loadSessionStore(storePath, { skipCache: true });
242
+ const entry = store[sessionKey];
243
+ if (entry && entry.sessionFile) {
244
+ const content = readFileSync(entry.sessionFile, "utf8");
245
+ const lines = content.split("\n").filter(line => line.trim());
246
+ for (let i = lines.length - 1; i >= 0; i--) {
247
+ try {
248
+ const item = JSON.parse(lines[i]);
249
+ if (item.type === "message" && item.stopReason === "error" && item.errorMessage) {
250
+ resolvedErrorMessage = item.errorMessage;
251
+ logger?.warn?.(`[Dragon Plugin] Extracted real underlying error from session file: ${resolvedErrorMessage}`);
252
+ break;
253
+ }
254
+ }
255
+ catch {
256
+ // Ignore parse errors for incomplete lines
257
+ }
258
+ }
259
+ }
260
+ }
261
+ catch (fsErr) {
262
+ logger?.error?.(`[Dragon Plugin] Failed to read real error from session file: ${fsErr}`);
263
+ }
264
+ }
265
+ // Check for insufficient spending cap/quota error and provide a friendly Chinese message
266
+ const lowerError = resolvedErrorMessage.toLowerCase();
267
+ const isSpendCapExceeded = lowerError.includes("spending cap") ||
268
+ lowerError.includes("spend cap") ||
269
+ lowerError.includes("resource_exhausted") ||
270
+ lowerError.includes("quota exceeded") ||
271
+ lowerError.includes("exceeded its monthly spending cap");
272
+ let friendlyMessage = `⚠️ OpenClaw Error: ${resolvedErrorMessage}`;
273
+ if (isSpendCapExceeded) {
274
+ friendlyMessage = "⚠️ 您的 Google Gemini API Key 已达到月度限额(Spending Cap)或余额不足,请前往 AI Studio (https://aistudio.google.com/) 调整限制或更新支付信息。";
275
+ }
276
+ // Report the actual failure via progress and reply
277
+ await progress({
278
+ kind: 'assistant',
279
+ phase: 'error',
280
+ status: 'failed',
281
+ title: '生成回复失败',
282
+ detail: isSpendCapExceeded ? "大模型账户额度或余额不足" : resolvedErrorMessage,
283
+ });
284
+ await this.telemetry.reportReply({
285
+ content: friendlyMessage,
286
+ sessionId,
287
+ source: "telemetry_deliver",
288
+ msgId: replyMsgId,
289
+ });
290
+ }
229
291
  })
230
292
  .finally(() => {
231
293
  if (this.sessionQueues.get(sessionKey) === current) {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const dragonChannelPluginVersion = "0.5.23";
1
+ export declare const dragonChannelPluginVersion = "0.5.25";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const dragonChannelPluginVersion = "0.5.23";
1
+ export const dragonChannelPluginVersion = "0.5.25";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@efengx/openclaw-channel-dragon",
3
- "version": "0.5.23",
3
+ "version": "0.5.25",
4
4
  "description": "Dragon workbench channel for OpenClaw",
5
5
  "author": "feng xiang <ofengx@gmail.com>",
6
6
  "type": "module",