openclaw-plugin-yuanbao 2.15.0 → 2.16.0

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/README.md CHANGED
@@ -18,7 +18,7 @@ through direct messages and group chats.
18
18
 
19
19
  ## Quick start
20
20
 
21
- > **Requires OpenClaw 2026.4.10 or above.** Run `openclaw --version` to check. Upgrade with `openclaw update`.
21
+ > **Requires OpenClaw 2026.5.7 or above.** Run `openclaw --version` to check. Upgrade with `openclaw update`.
22
22
 
23
23
  ### 1. Add the Yuanbao channel with your credentials
24
24
 
@@ -15,6 +15,7 @@ import { resolveYuanbaoAccount } from "../../accounts.js";
15
15
  import { createLog } from "../../logger.js";
16
16
  import { getYuanbaoRuntime } from "../../runtime.js";
17
17
  import { createMessageSender } from "../outbound/create-sender.js";
18
+ import { getActiveTraceContext } from "../trace/context.js";
18
19
  import { resolveActionTarget } from "./resolve-target.js";
19
20
  import { searchSticker } from "./sticker/send.js";
20
21
  /**
@@ -135,8 +136,14 @@ export async function handleAction(input) {
135
136
  }
136
137
  log.error(`${item.type} send failed: ${result.error}`);
137
138
  }
138
- else if (result.messageId) {
139
- lastMessageId = result.messageId;
139
+ else {
140
+ // Mark outbound delivered on the active agent-run trace context so
141
+ // dispatch-reply won't mistake an action-only reply (e.g. a sticker
142
+ // with no text) for an empty reply and send the fallback text.
143
+ getActiveTraceContext()?.markActionDelivered();
144
+ if (result.messageId) {
145
+ lastMessageId = result.messageId;
146
+ }
140
147
  }
141
148
  }
142
149
  return { channel: "yuanbao", ok: true, messageId: lastMessageId };
@@ -202,7 +202,12 @@ export const dispatchReply = {
202
202
  }
203
203
  // ⭐ Flush outbound queue
204
204
  const flushed = await queueSession.flush();
205
- if (!flushed && !hasSentContent && !ctx.abortSignal?.aborted) {
205
+ // The model may reply purely through a message action (e.g. sticker/react)
206
+ // which is delivered via handleAction and bypasses queueSession entirely.
207
+ // Such deliveries mark the agent-run trace context, so treat them as real
208
+ // outbound content and skip the fallback reply.
209
+ const deliveredViaAction = ctx.traceContext?.hasActionDelivered() ?? false;
210
+ if (!flushed && !hasSentContent && !deliveredViaAction && !ctx.abortSignal?.aborted) {
206
211
  const { fallbackReply } = account;
207
212
  if (fallbackReply) {
208
213
  ctx.log.warn("[dispatch-reply] AI returned no reply content, using fallback reply");
@@ -222,10 +222,10 @@ function resolveCronBase(params, intent) {
222
222
  // ============================================================================
223
223
  /** Builds a Gateway job config for a one-time job. */
224
224
  function buildOnceJob(params, time, to, accountId, intent) {
225
- const { name, atMs, message } = resolveOnceBase(params, time, intent);
225
+ const { name, atStr: at, message } = resolveOnceBase(params, time, intent);
226
226
  return {
227
227
  name,
228
- schedule: { kind: 'at', atMs },
228
+ schedule: { kind: 'at', at },
229
229
  sessionTarget: 'isolated',
230
230
  wakeMode: 'now',
231
231
  deleteAfterRun: true,
@@ -411,7 +411,7 @@ async function executeGateway(gatewayTool, p, resolvedTo, accountId) {
411
411
  if (isCronExpression(p.time)) {
412
412
  const job = buildCronJob({ ...p, content: p.content.trim() }, resolvedTo, accountId, intent);
413
413
  try {
414
- const cronResult = await gatewayTool('cron.add', { timeoutMs: DEFAULT_GATEWAY_TIMEOUT_MS }, { job });
414
+ const cronResult = await gatewayTool('cron.add', { timeoutMs: DEFAULT_GATEWAY_TIMEOUT_MS }, job);
415
415
  const typeLabel = intent === 'task' ? '循环任务' : '周期提醒';
416
416
  return json({
417
417
  status: 'ok',
@@ -430,7 +430,7 @@ async function executeGateway(gatewayTool, p, resolvedTo, accountId) {
430
430
  return json(timeResult);
431
431
  const job = buildOnceJob({ ...p, content: p.content.trim() }, timeResult.timeSpec, resolvedTo, accountId, intent);
432
432
  try {
433
- const cronResult = await gatewayTool('cron.add', { timeoutMs: DEFAULT_GATEWAY_TIMEOUT_MS }, { job });
433
+ const cronResult = await gatewayTool('cron.add', { timeoutMs: DEFAULT_GATEWAY_TIMEOUT_MS }, job);
434
434
  return json({
435
435
  status: 'ok',
436
436
  action: 'add',
@@ -4,6 +4,15 @@ export type YuanbaoTraceContext = {
4
4
  seqId?: string;
5
5
  /** Auto-incremented based on inbound seqId */
6
6
  nextMsgSeq: () => number | undefined;
7
+ /**
8
+ * Mark that an outbound message was successfully delivered via a message
9
+ * action (e.g. sticker/react/send) during this agent run. Used by
10
+ * dispatch-reply to avoid sending the fallback reply when the model already
11
+ * replied through an action rather than the deliver callback.
12
+ */
13
+ markActionDelivered: () => void;
14
+ /** Whether any action-driven outbound succeeded within this agent run. */
15
+ hasActionDelivered: () => boolean;
7
16
  };
8
17
  /**
9
18
  * Generate a random trace ID (32-char hex string).
@@ -59,6 +59,11 @@ export function resolveTraceContext(params) {
59
59
  seqCounter++;
60
60
  return baseSeq + seqCounter;
61
61
  };
62
+ let actionDelivered = false;
63
+ const markActionDelivered = () => {
64
+ actionDelivered = true;
65
+ };
66
+ const hasActionDelivered = () => actionDelivered;
62
67
  const log = createLog("trace");
63
68
  log.debug("[msg-trace] resolve context", {
64
69
  traceId,
@@ -69,6 +74,8 @@ export function resolveTraceContext(params) {
69
74
  traceId,
70
75
  traceparent: buildTraceparent(traceId),
71
76
  nextMsgSeq,
77
+ markActionDelivered,
78
+ hasActionDelivered,
72
79
  ...(seqId ? { seqId } : {}),
73
80
  };
74
81
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "openclaw-plugin-yuanbao",
3
- "version": "2.15.0",
3
+ "version": "2.16.0",
4
4
  "name": "YuanBao",
5
5
  "description": "YuanBao channel plugin",
6
6
  "channels": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-plugin-yuanbao",
3
- "version": "2.15.0",
3
+ "version": "2.16.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Tencent YuanBao intelligent bot channel plugin",
@@ -30,7 +30,7 @@
30
30
  "@types/ws": "^8.18.1",
31
31
  "c8": "^11.0.0",
32
32
  "eslint": "^9.0.0",
33
- "openclaw": ">=2026.4.5",
33
+ "openclaw": ">=2026.5.7",
34
34
  "tsx": "^4.19.0",
35
35
  "typescript": "^5.7.0",
36
36
  "typescript-eslint": "^8.0.0"
@@ -64,7 +64,7 @@
64
64
  "npmSpec": "openclaw-plugin-yuanbao",
65
65
  "localPath": "extensions/yuanbao",
66
66
  "defaultChoice": "npm",
67
- "minHostVersion": ">=2026.4.5"
67
+ "minHostVersion": ">=2026.5.7"
68
68
  }
69
69
  },
70
70
  "scripts": {