openclaw-quiubo 2.6.39 → 2.6.41

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
@@ -199,6 +199,20 @@ If an agent is registered, the gateway sends a heartbeat every 60s to report `co
199
199
 
200
200
  A typing indicator is sent immediately when processing begins, then repeated every 4s until the response is delivered.
201
201
 
202
+ ### Scheduled messages / Cron delivery
203
+
204
+ Cron jobs that deliver to Quiubo groups **must** include `--to <groupId>`:
205
+
206
+ ```bash
207
+ openclaw cron add --every 30m --channel quiubo --to 7868cc21-... "Check in with the team"
208
+ ```
209
+
210
+ Without `--to`, the extension cannot determine which group to deliver to. Unlike 1:1 sessions (which have a single bound chat), group sessions are multi-tenant — the target must be explicit.
211
+
212
+ > **Note:** The `--to` value is the Quiubo group UUID, visible in the group detail screen or conversation URL.
213
+
214
+ See [#84](https://github.com/bitlabs-com/quiubo/issues/84) for a planned improvement to auto-resolve the target from the session bound group.
215
+
202
216
  ## Managing Accounts
203
217
 
204
218
  ```bash
package/dist/index.js CHANGED
@@ -13531,6 +13531,43 @@ var quiuboPlugin = {
13531
13531
  const accts = { ...channel.accounts };
13532
13532
  delete accts[id];
13533
13533
  return setChannelConfig(cfg, { ...channel, accounts: accts });
13534
+ },
13535
+ /**
13536
+ * Resolve a default delivery target for cron/announce when no --to is provided.
13537
+ * Scans the agent's session store for quiubo group sessions and returns the
13538
+ * group ID if there's exactly one. With multiple groups, returns undefined
13539
+ * (user must specify --to).
13540
+ */
13541
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13542
+ resolveDefaultTo({ cfg, accountId }) {
13543
+ try {
13544
+ const fs = __require("node:fs");
13545
+ const path = __require("node:path");
13546
+ const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
13547
+ const agentId = accountId ?? "main";
13548
+ const possiblePaths = [
13549
+ path.join(homeDir, ".openclaw", "agents", agentId, "sessions", "sessions.json"),
13550
+ path.join(homeDir, ".openclaw", "sessions", "sessions.json")
13551
+ ];
13552
+ for (const storePath of possiblePaths) {
13553
+ if (!fs.existsSync(storePath)) continue;
13554
+ const raw = fs.readFileSync(storePath, "utf-8");
13555
+ const store = JSON.parse(raw);
13556
+ const groupIds = /* @__PURE__ */ new Set();
13557
+ for (const key of Object.keys(store)) {
13558
+ const match = key.match(/quiubo:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
13559
+ if (match && !key.includes(":group:")) {
13560
+ groupIds.add(match[1]);
13561
+ }
13562
+ }
13563
+ if (groupIds.size === 1) {
13564
+ return [...groupIds][0];
13565
+ }
13566
+ if (groupIds.size > 1) return void 0;
13567
+ }
13568
+ } catch {
13569
+ }
13570
+ return void 0;
13534
13571
  }
13535
13572
  },
13536
13573
  // ── setup adapter (for `channels add` CLI) ──────────────────────
@@ -14333,10 +14370,10 @@ function resolveOutboundGroupId(ctx) {
14333
14370
  if (targetGroupId) return targetGroupId;
14334
14371
  const toField = ctx.to;
14335
14372
  if (toField) {
14336
- const str = String(toField);
14337
- if (!str.match(/^quiubo:/i)) {
14338
- const cleaned = str.trim();
14339
- if (cleaned) return cleaned;
14373
+ const str = String(toField).trim();
14374
+ const stripped = str.replace(/^quiubo:/i, "").trim();
14375
+ if (stripped.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)) {
14376
+ return stripped;
14340
14377
  }
14341
14378
  }
14342
14379
  return void 0;
@@ -14350,16 +14387,24 @@ async function resolveAnnounceGroupId(accountId, log) {
14350
14387
  const raw = await readFile4(cronPath, "utf-8");
14351
14388
  const parsed = JSON.parse(raw);
14352
14389
  const jobs = parsed?.jobs ?? [];
14390
+ let fallback;
14353
14391
  for (const job of jobs) {
14354
14392
  if (job.enabled === false) continue;
14355
14393
  const delivery = job.delivery;
14356
14394
  if (!delivery) continue;
14357
14395
  if (delivery.channel !== "quiubo") continue;
14358
14396
  if (delivery.to) {
14359
- log?.info?.(`[${accountId}] [resolveAnnounceGroupId] found cron job ${job.id} \u2192 delivery.to=${delivery.to}`);
14360
- return delivery.to;
14397
+ if (job.agentId === accountId) {
14398
+ log?.info?.(`[${accountId}] [resolveAnnounceGroupId] found matching cron job ${job.id} \u2192 delivery.to=${delivery.to}`);
14399
+ return delivery.to;
14400
+ }
14401
+ if (!fallback) fallback = delivery.to;
14361
14402
  }
14362
14403
  }
14404
+ if (fallback) {
14405
+ log?.info?.(`[${accountId}] [resolveAnnounceGroupId] using fallback cron delivery.to=${fallback}`);
14406
+ return fallback;
14407
+ }
14363
14408
  } catch (err) {
14364
14409
  log?.warn?.(`[${accountId}] [resolveAnnounceGroupId] failed to read cron jobs: ${err}`);
14365
14410
  }