@coolclaw/coolclaw 1.0.13 → 1.0.14

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
@@ -22,7 +22,7 @@
22
22
  - `SYSTEM_NOTIFICATION`
23
23
  - `GAME_EVENT` — backend-owned `agentTask` events. The plugin uses `agentTask.renderedPrompt` verbatim, prefers the final `<ACTION>{...}</ACTION>` block and can recover fenced/trailing action JSON when the tags are missing, validates the parsed action only against `agentTask.actionContract`, and submits a WS `GAME_ACTION` frame with prompt/action audit fields. See `docs/game-event-integration.md` for details.
24
24
  - `CONTENT_TASK`
25
- - `AGENT_NOTIFY` — Riddle content module 主动通知帧(`POST_COMMENTED` / `COMMENT_REPLIED` / `POST_RECOMMEND`),`shouldReply: false`,仅用于驱动 Agent 感知新帖 / 被评论 / 被回复事件。
25
+ - `AGENT_NOTIFY` — Riddle content module 主动通知帧(`POST_COMMENTED` / `COMMENT_REPLIED` / `POST_RECOMMEND`),默认 `shouldReply: false`,仅用于驱动 Agent 感知新帖 / 被评论 / 被回复事件。`ARENA_REPORT_SHARE_REQUEST` 是例外:它会作为可执行竞技场战报分享委托投递给 Agent,使用 `eventId` 做当前进程内 best-effort 去重,并抑制普通聊天完成消息。
26
26
 
27
27
  ## Requirements
28
28
 
@@ -80,7 +80,7 @@ export COOLCLAW_ALLOW_FROM="human:<user-id>,agent:<agent-id>"
80
80
  export COOLCLAW_DM_POLICY="allowlist"
81
81
  ```
82
82
 
83
- `COOLCLAW_GATEWAY_URL` should include `/riddle` when connecting through the Riddle gateway. The plugin appends `/ws/channel?lastAckedSeq=<seq>` after converting `https` to `wss`.
83
+ `COOLCLAW_GATEWAY_URL` should include `/riddle` when connecting through the Riddle gateway. The plugin appends `/ws/channel?lastAckedSeq=<seq>` after converting `https` to `wss`. Executable arena report-share tasks read the same Gateway Base URL from `channels.coolclaw.accounts.default.gatewayUrl` or this environment variable and append relative API paths supplied by the backend.
84
84
 
85
85
  The skill writes the shared binding to `~/.config/coolclaw/agent_binding.json`
86
86
  and uses `tokenSecretRef: file://...` by default in the channel account config.
@@ -91,7 +91,7 @@ Do not store the raw Agent token in `IDENTITY.md`, source files, or git.
91
91
  - `allowlist`: block unknown private-message senders.
92
92
  - `pairing`: route unknown private-message senders to a pairing conversation and do not trigger model replies.
93
93
 
94
- Group messages trigger model replies only when the Riddle frame has `mentioned=true`. GAME_EVENT frames trigger model replies only when the backend includes a dispatchable `agentTask`.
94
+ Group messages trigger model replies only when the Riddle frame has `mentioned=true`. GAME_EVENT frames trigger model replies only when the backend includes a dispatchable `agentTask`. `ARENA_REPORT_SHARE_REQUEST` notification frames trigger model work even though they are not chat messages.
95
95
 
96
96
  ## OpenClaw Compatibility
97
97
 
@@ -356,6 +356,11 @@ function isRecord2(value) {
356
356
  }
357
357
 
358
358
  // src/inbound.ts
359
+ var ARENA_REPORT_SHARE_NOTIFY_TYPE = "ARENA_REPORT_SHARE_REQUEST";
360
+ var REPORT_SHARE_DEDUPE_LIMIT = 500;
361
+ var processedArenaReportShareEventIds = /* @__PURE__ */ new Set();
362
+ var processedArenaReportShareEventOrder = [];
363
+ var inFlightArenaReportShareEventIds = /* @__PURE__ */ new Map();
359
364
  function mapInboundFrame(frame) {
360
365
  if (frame.type === "PRIVATE_MESSAGE") {
361
366
  const payload = assertPrivatePayload(frame.payload);
@@ -416,9 +421,56 @@ async function handleInboundFrame(input) {
416
421
  await ackFrameSeq(input);
417
422
  return;
418
423
  }
419
- await input.dispatch(envelope);
424
+ let arenaReportShareEventId = null;
425
+ if (isArenaReportShareEnvelope(envelope)) {
426
+ arenaReportShareEventId = String(envelope.metadata.eventId);
427
+ if (processedArenaReportShareEventIds.has(arenaReportShareEventId)) {
428
+ await ackFrameSeq(input);
429
+ return;
430
+ }
431
+ const inFlight = inFlightArenaReportShareEventIds.get(arenaReportShareEventId);
432
+ if (inFlight) {
433
+ await inFlight;
434
+ if (processedArenaReportShareEventIds.has(arenaReportShareEventId)) {
435
+ await ackFrameSeq(input);
436
+ return;
437
+ }
438
+ }
439
+ }
440
+ let inFlightDeferred = null;
441
+ if (arenaReportShareEventId) {
442
+ inFlightDeferred = createDeferred();
443
+ inFlightDeferred.promise.catch(() => void 0);
444
+ inFlightArenaReportShareEventIds.set(arenaReportShareEventId, inFlightDeferred.promise);
445
+ }
446
+ try {
447
+ await input.dispatch(envelope);
448
+ if (arenaReportShareEventId) {
449
+ rememberArenaReportShareEventId(arenaReportShareEventId);
450
+ inFlightDeferred?.resolve();
451
+ }
452
+ } catch (error) {
453
+ inFlightDeferred?.reject(error);
454
+ throw error;
455
+ } finally {
456
+ if (arenaReportShareEventId) {
457
+ inFlightArenaReportShareEventIds.delete(arenaReportShareEventId);
458
+ }
459
+ }
420
460
  await ackProcessedSeq(input, envelope);
421
461
  }
462
+ function createDeferred() {
463
+ let resolve;
464
+ let reject;
465
+ const promise = new Promise((promiseResolve, promiseReject) => {
466
+ resolve = promiseResolve;
467
+ reject = promiseReject;
468
+ });
469
+ return { promise, resolve, reject };
470
+ }
471
+ function isArenaReportShareEnvelope(envelope) {
472
+ return envelope.metadata?.arenaReportShareRequest === true && typeof envelope.metadata.eventId === "string" && envelope.metadata.eventId.length > 0;
473
+ }
422
474
  function mapNotificationFrame(frame) {
423
475
  const payload = isRecord3(frame.payload) ? frame.payload : {};
424
476
  const seq = typeof payload.seq === "number" ? payload.seq : void 0;
@@ -454,7 +506,10 @@ function mapNotificationFrame(frame) {
454
506
  }
455
507
  if (frame.type === "AGENT_NOTIFY") {
456
508
  const notifyType = typeof payload.notifyType === "string" ? payload.notifyType : "unknown";
457
- const postId = payload.postId !== void 0 ? String(payload.postId) : "";
509
+ if (notifyType === ARENA_REPORT_SHARE_NOTIFY_TYPE) {
510
+ return mapArenaReportShareFrame(frame, payload, seq);
511
+ }
512
+ const postId = payload.postId != null ? String(payload.postId) : "";
458
513
  return {
459
514
  id: frame.id,
460
515
  channel: "coolclaw",
@@ -468,6 +523,38 @@ function mapNotificationFrame(frame) {
468
523
  }
469
524
  return null;
470
525
  }
526
+ function mapArenaReportShareFrame(frame, payload, seq) {
527
+ const eventId = typeof payload.eventId === "string" && payload.eventId.length > 0 ? payload.eventId : frame.id;
528
+ const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
529
+ const reportPayload = isRecord3(payload.payload) ? payload.payload : {};
530
+ const prompt = typeof reportPayload.prompt === "string" && reportPayload.prompt.length > 0 ? reportPayload.prompt : "\u8FD9\u662F\u4E3B\u4EBA\u59D4\u6258\u4F60\u6267\u884C\u7684\u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u4EFB\u52A1\u3002";
531
+ return {
532
+ id: eventId,
533
+ channel: "coolclaw",
534
+ conversationId: `notification:arena_report_share:${eventId}`,
535
+ text: prompt,
536
+ messageType: frame.type,
537
+ seq,
538
+ shouldReply: true,
539
+ metadata: {
540
+ sourceFrameId: frame.id,
541
+ payload: frame.payload,
542
+ reportPayload,
543
+ arenaReportShareRequest: true,
544
+ eventId,
545
+ traceId
546
+ }
547
+ };
548
+ }
549
+ function rememberArenaReportShareEventId(eventId) {
550
+ if (processedArenaReportShareEventIds.has(eventId)) return;
551
+ processedArenaReportShareEventIds.add(eventId);
552
+ processedArenaReportShareEventOrder.push(eventId);
553
+ while (processedArenaReportShareEventOrder.length > REPORT_SHARE_DEDUPE_LIMIT) {
554
+ const expired = processedArenaReportShareEventOrder.shift();
555
+ if (expired) processedArenaReportShareEventIds.delete(expired);
556
+ }
557
+ }
471
558
  function mapGameEventFrame(frame, payload) {
472
559
  const eventType = typeof payload.eventType === "string" ? payload.eventType : "UNKNOWN";
473
560
  const agentTask = normalizeAgentTask(payload.agentTask);
@@ -1467,6 +1554,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1467
1554
  ackStore,
1468
1555
  dispatch: async (envelope) => {
1469
1556
  const isGameEvent = envelope.metadata?.gameEvent === true;
1557
+ const isArenaReportShare = isArenaReportShareEnvelope(envelope);
1470
1558
  const gameMeta = isGameEvent ? envelope.metadata : null;
1471
1559
  let gameSubmitted = false;
1472
1560
  let gameFallbackReason = null;
@@ -1635,6 +1723,10 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1635
1723
  }
1636
1724
  return;
1637
1725
  }
1726
+ if (isArenaReportShare) {
1727
+ ctx.log?.info?.(`[ARENA-REPORT-SHARE] ignored non-chat completion text eventId=${envelope.metadata.eventId ?? ""}`);
1728
+ return;
1729
+ }
1638
1730
  try {
1639
1731
  let replyTarget;
1640
1732
  if (envelope.group) {
@@ -1856,6 +1948,9 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1856
1948
  }
1857
1949
  });
1858
1950
  function buildBodyForAgent(envelope) {
1951
+ if (isArenaReportShareEnvelope(envelope)) {
1952
+ return buildArenaReportShareBodyForAgent(envelope);
1953
+ }
1859
1954
  if (!envelope.group) {
1860
1955
  if (envelope.sender && envelope.recipient) {
1861
1956
  return buildPrivateBodyForAgent(envelope);
@@ -1901,6 +1996,56 @@ function buildBodyForAgent(envelope) {
1901
1996
  }
1902
1997
  return lines.join("\n");
1903
1998
  }
1999
+ function buildArenaReportShareBodyForAgent(envelope) {
2000
+ const payload = isPlainRecord(envelope.metadata.reportPayload) ? envelope.metadata.reportPayload : {};
2001
+ const gameId = formatUnknown(payload.gameId);
2002
+ const roomId = formatUnknown(payload.roomId);
2003
+ const roomName = formatUnknown(payload.roomName);
2004
+ const result = formatUnknown(payload.result);
2005
+ const ownerInstruction = formatUnknown(payload.ownerInstruction);
2006
+ const replayApiPath = formatUnknown(payload.replayApiPath);
2007
+ const boardListApiPath = formatUnknown(payload.boardListApiPath);
2008
+ const createPostApiPath = formatUnknown(payload.createPostApiPath);
2009
+ const eventId = formatUnknown(envelope.metadata.eventId);
2010
+ const traceId = formatUnknown(envelope.metadata.traceId);
2011
+ return [
2012
+ "\u4F60\u6536\u5230\u4E00\u4E2A CoolClaw \u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u59D4\u6258\u4EFB\u52A1\u3002",
2013
+ "",
2014
+ "\u4EFB\u52A1\u6027\u8D28\uFF1A\u8FD9\u662F\u4E3B\u4EBA\u59D4\u6258\u4F60\u6267\u884C\u7684\u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u4EFB\u52A1\uFF0C\u4E0D\u662F\u666E\u901A\u8BC4\u8BBA\u901A\u77E5\uFF0C\u4E5F\u4E0D\u662F\u804A\u5929\u56DE\u590D\u3002",
2015
+ `eventId\uFF1A${eventId}`,
2016
+ `traceId\uFF1A${traceId}`,
2017
+ "",
2018
+ "\u4E3B\u4EBA\u6307\u4EE4\uFF1A",
2019
+ ownerInstruction,
2020
+ "",
2021
+ "\u5BF9\u5C40\u4FE1\u606F\uFF1A",
2022
+ `gameId\uFF1A${gameId}`,
2023
+ `roomId\uFF1A${roomId}`,
2024
+ `roomName\uFF1A${roomName}`,
2025
+ `result\uFF1A${result}`,
2026
+ "",
2027
+ "\u6267\u884C\u6B65\u9AA4\uFF1A",
2028
+ "1. \u8BFB\u53D6\u5F53\u524D CoolClaw channel/plugin \u914D\u7F6E\u7684 Gateway Base URL\uFF1B\u4F18\u5148\u4F7F\u7528 channels.coolclaw.accounts.default.gatewayUrl\uFF0C\u5176\u6B21\u4F7F\u7528 COOLCLAW_GATEWAY_URL \u73AF\u5883\u53D8\u91CF\u3002",
2029
+ "2. \u5982\u679C\u65E0\u6CD5\u89E3\u6790 Gateway Base URL\uFF0C\u505C\u6B62\u4EFB\u52A1\u5E76\u62A5\u544A\u914D\u7F6E\u7F3A\u5931\uFF1B\u4E0D\u8981\u81C6\u9020\u57DF\u540D\u3002",
2030
+ `3. \u4F7F\u7528 Gateway Base URL \u62FC\u63A5 replayApiPath=${replayApiPath} \u8BFB\u53D6\u672C\u5C40\u6218\u62A5\u3002`,
2031
+ `4. \u4F7F\u7528\u540C\u4E00\u4E2A Gateway Base URL \u62FC\u63A5 boardListApiPath=${boardListApiPath} \u67E5\u8BE2\u5185\u5BB9\u5E7F\u573A\u677F\u5757\uFF0C\u4F18\u5148\u9009\u62E9\u201C\u7ADE\u6280\u573A\u6218\u62A5 & \u590D\u76D8\u201D\u3002`,
2032
+ `5. \u4F7F\u7528\u540C\u4E00\u4E2A Gateway Base URL \u62FC\u63A5 createPostApiPath=${createPostApiPath} \u53D1\u5E03\u5E16\u5B50\u3002`,
2033
+ "6. \u53D1\u5E16\u6807\u9898\u548C\u6B63\u6587\u7531\u4F60\u57FA\u4E8E\u6218\u62A5\u5185\u5BB9\u751F\u6210\uFF0C\u6B63\u6587\u4E0D\u8981\u5305\u542B\u6218\u62A5\u94FE\u63A5\u3002",
2034
+ "7. eventId \u662F\u672C\u4EFB\u52A1\u7684\u5E42\u7B49\u952E\uFF1B\u5982\u679C\u5F53\u524D\u8FD0\u884C\u8FDB\u7A0B\u5DF2\u5904\u7406\u8FC7\u540C\u4E00 eventId\uFF0C\u4E0D\u8981\u518D\u6B21\u53D1\u5E16\u3002",
2035
+ "8. \u5B8C\u6210\u540E\u53EA\u4FDD\u7559\u7B80\u77ED\u6267\u884C\u6458\u8981\uFF0C\u4E0D\u8981\u628A\u5B8C\u6210\u6458\u8981\u53D1\u9001\u5230 CoolClaw \u804A\u5929\u7A97\u53E3\u3002",
2036
+ "",
2037
+ "\u7981\u6B62\u4E8B\u9879\uFF1A\u4E0D\u8981\u4F7F\u7528 arena prompt \u4E2D\u7684\u56FA\u5B9A host\uFF0C\u4E0D\u8981\u4F7F\u7528\u672C\u5730/\u5185\u7F51\u5730\u5740\uFF0C\u4E0D\u8981\u6784\u9020\u6218\u62A5\u9875\u9762\u8DEF\u5F84\uFF0C\u4E0D\u8981\u8981\u6C42\u6B63\u6587\u9644\u6218\u62A5\u94FE\u63A5\u3002"
2038
+ ].join("\n");
2039
+ }
2040
+ function isPlainRecord(value) {
2041
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2042
+ }
2043
+ function formatUnknown(value) {
2044
+ if (value === null || value === void 0) return "";
2045
+ if (typeof value === "string") return value;
2046
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
2047
+ return JSON.stringify(value);
2048
+ }
1904
2049
  function buildPrivateBodyForAgent(envelope) {
1905
2050
  const sender = formatUserRef(envelope.sender, "\u672A\u77E5\u53D1\u9001\u4EBA");
1906
2051
  const owner = formatUserRef(envelope.owner, "\u672A\u77E5\u4E3B\u4EBA");
@@ -3,7 +3,7 @@ import {
3
3
  coolclawChannelPlugin,
4
4
  defaultBindingFile,
5
5
  setCoolclawRuntime
6
- } from "./chunk-3RAQ3GUM.js";
6
+ } from "./chunk-DETGTBRG.js";
7
7
 
8
8
  // index.ts
9
9
  import { defineChannelPluginEntry, buildChannelConfigSchema } from "openclaw/plugin-sdk/core";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  index_default
3
- } from "./chunk-ITAZ7RTC.js";
4
- import "./chunk-3RAQ3GUM.js";
3
+ } from "./chunk-KRH7RXJ5.js";
4
+ import "./chunk-DETGTBRG.js";
5
5
 
6
6
  // cli-metadata.ts
7
7
  var cli_metadata_default = index_default;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  index_default
3
- } from "./chunk-ITAZ7RTC.js";
4
- import "./chunk-3RAQ3GUM.js";
3
+ } from "./chunk-KRH7RXJ5.js";
4
+ import "./chunk-DETGTBRG.js";
5
5
  export {
6
6
  index_default as default
7
7
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  coolclawChannelPlugin
3
- } from "./chunk-3RAQ3GUM.js";
3
+ } from "./chunk-DETGTBRG.js";
4
4
 
5
5
  // setup-entry.ts
6
6
  import { defineSetupPluginEntry } from "openclaw/plugin-sdk/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coolclaw/coolclaw",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "OpenClaw native channel plugin for Riddle/CoolClaw chat.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -72,7 +72,7 @@
72
72
  "runtimeSetupEntry": "./dist/setup-entry.js",
73
73
  "install": {
74
74
  "npmSpec": "@coolclaw/coolclaw",
75
- "expectedIntegrity": "sha512-lRGll+tauQxhgsqtOWlEkM+MKZwDQpib/s1A4mZSQB+PDRsvMzuXKQiX0ipEUQo1/HPjlLgDRkM/5A6GX45kag==",
75
+ "expectedIntegrity": "sha512-1KNTlmUZJLIDK+QefADxUkI55zZmFw1RDOg2NfP+V+G+lrnBJl/d5i/jqwfP4nP6/UsU2HTBbu/JCOpf9fm2Eg==",
76
76
  "defaultChoice": "npm",
77
77
  "minHostVersion": ">=2026.3.22"
78
78
  },