@coolclaw/coolclaw 1.0.0 → 1.0.2

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.
@@ -209,13 +209,13 @@ function renderPlayerInfo(list, selfSeat) {
209
209
  if (list.length === 0) return "\uFF08\u65E0\u5EA7\u4F4D\u4FE1\u606F\uFF09";
210
210
  return list.map((p) => {
211
211
  const seat = asNumberOrNull(p.seat);
212
- const alive = p.alive === true ? "\u5B58\u6D3B" : "\u5DF2\u6B7B\u4EA1";
212
+ const playerStatus = p.alive === true ? "\u5B58\u6D3B" : p.alive === false ? "\u5DF2\u6B7B\u4EA1" : "\u53C2\u8D5B";
213
213
  if (seat != null && selfSeat != null && seat === selfSeat) {
214
214
  const name = asString(p.name, "\u672A\u77E5");
215
215
  const voice = asString(p.voiceDesc, "");
216
- return `\u5EA7\u4F4D${seat}\uFF08\u4F60\u81EA\u5DF1\uFF0C${name}${voice ? `\uFF0C${voice}` : ""}\uFF0C${alive}\uFF09`;
216
+ return `\u5EA7\u4F4D${seat}\uFF08\u4F60\u81EA\u5DF1\uFF0C${name}${voice ? `\uFF0C${voice}` : ""}\uFF0C${playerStatus}\uFF09`;
217
217
  }
218
- return `\u5EA7\u4F4D${seat ?? "?"}\uFF08${alive}\uFF09`;
218
+ return `\u5EA7\u4F4D${seat ?? "?"}\uFF08${playerStatus}\uFF09`;
219
219
  }).join("\uFF1B");
220
220
  }
221
221
  function stripAudioSuffix(line) {
@@ -228,20 +228,42 @@ function renderHistory(history) {
228
228
  function renderAliveSeats(seats) {
229
229
  return seats.length === 0 ? "\uFF08\u65E0\uFF09" : `[${seats.join(", ")}]`;
230
230
  }
231
+ function asMvpCandidates(v) {
232
+ if (!Array.isArray(v)) return [];
233
+ return v.flatMap((item) => {
234
+ if (typeof item === "number" && Number.isFinite(item)) {
235
+ return [{ agentId: item, seatNumber: null }];
236
+ }
237
+ const record = asRecord(item);
238
+ const agentId = asNumberOrNull(record.agentId) ?? asNumberOrNull(record.targetAgentId);
239
+ if (agentId == null) return [];
240
+ const seatNumber = asNumberOrNull(record.seatNumber) ?? asNumberOrNull(record.seat);
241
+ return [{ agentId, seatNumber }];
242
+ });
243
+ }
244
+ function renderMvpCandidateList(candidates, selfAgentId) {
245
+ if (candidates.length === 0) return "\uFF08\u5019\u9009\u4FE1\u606F\u7F3A\u5931\uFF1B\u7B49\u5F85\u5E73\u53F0\u8D85\u65F6\u515C\u5E95\uFF09";
246
+ return candidates.map((candidate) => {
247
+ const seat = candidate.seatNumber == null ? "?" : candidate.seatNumber;
248
+ const selfMark = selfAgentId != null && candidate.agentId === selfAgentId ? "\uFF08\u4F60\u81EA\u5DF1\uFF0C\u4E0D\u80FD\u6295\u7ED9\u81EA\u5DF1\uFF09" : "";
249
+ return `- \u5EA7\u4F4D${seat} / Agent ${candidate.agentId}${selfMark}`;
250
+ }).join("\n");
251
+ }
231
252
  function renderHeader(eventType, outer, payload) {
232
253
  const round = asNumberOrNull(outer.round) ?? 1;
233
254
  const selfSeat = asNumberOrNull(payload.selfSeat);
234
255
  const selfRole = asString(payload.selfRole, "\u672A\u77E5");
235
256
  const selfName = asString(payload.selfAgentName, "");
236
- const phase = eventType.startsWith("DAY_") || eventType === "LAST_WORD_TURN" || eventType === "HUNTER_SKILL_TURN" ? "\u767D\u5929" : "\u591C\u665A";
257
+ const phase = eventType === "MVP_VOTE_REQUEST" ? "\u8D5B\u540E" : eventType.startsWith("DAY_") || eventType === "LAST_WORD_TURN" || eventType === "HUNTER_SKILL_TURN" ? "\u767D\u5929" : "\u591C\u665A";
237
258
  const aliveSeats = asNumberArray(payload.aliveSeats);
259
+ const seatsLabel = eventType === "MVP_VOTE_REQUEST" ? "MVP \u5019\u9009\u5EA7\u4F4D" : "\u5F53\u524D\u5B58\u6D3B\u5EA7\u4F4D";
238
260
  const playerInfo = renderPlayerInfo(asRecordArray(payload.playerInfoList), selfSeat);
239
261
  const history = renderHistory(asStringArray(payload.scopedHistory));
240
262
  return [
241
263
  `[\u6E38\u620F] \u72FC\u4EBA\u6740 \xB7 \u7B2C ${round} \u8F6E \xB7 ${phase} \xB7 ${describeEventType(eventType)}`,
242
264
  ``,
243
265
  `\u4F60\u662F\u5EA7\u4F4D ${selfSeat ?? "?"} \u7684 ${selfRole}${selfName ? `\uFF08${selfName}\uFF09` : ""}\u3002`,
244
- `\u5F53\u524D\u5B58\u6D3B\u5EA7\u4F4D\uFF1A${renderAliveSeats(aliveSeats)}\u3002`,
266
+ `${seatsLabel}\uFF1A${renderAliveSeats(aliveSeats)}\u3002`,
245
267
  `\u5168\u4F53\u73A9\u5BB6\uFF1A${playerInfo}`,
246
268
  ``,
247
269
  `\u3010\u8FD1\u671F\u5386\u53F2\uFF08\u4EC5\u4F60\u53EF\u89C1\uFF09\u3011`,
@@ -265,6 +287,8 @@ function describeEventType(eventType) {
265
287
  return "\u767D\u5929\u6295\u7968";
266
288
  case "HUNTER_SKILL_TURN":
267
289
  return "\u730E\u4EBA\u6280\u80FD";
290
+ case "MVP_VOTE_REQUEST":
291
+ return "MVP \u6295\u7968";
268
292
  default:
269
293
  return eventType;
270
294
  }
@@ -285,6 +309,8 @@ function renderTask(eventType, payload) {
285
309
  return renderDayVoteTurn(payload);
286
310
  case "HUNTER_SKILL_TURN":
287
311
  return renderHunterTurn(payload);
312
+ case "MVP_VOTE_REQUEST":
313
+ return renderMvpVoteRequest(payload);
288
314
  default:
289
315
  return renderUnknownTurn(eventType);
290
316
  }
@@ -419,6 +445,23 @@ function renderHunterTurn(payload) {
419
445
  `<ACTION>{"actionType":"HUNTER_PASS","actionData":{}}</ACTION>`
420
446
  ].join("\n");
421
447
  }
448
+ function renderMvpVoteRequest(payload) {
449
+ const candidates = asMvpCandidates(payload.candidates);
450
+ const selfAgentId = asNumberOrNull(payload.selfAgentId);
451
+ const round = asNumberOrNull(payload.round) ?? 1;
452
+ const deadlineSeconds = asNumberOrNull(payload.deadlineSeconds);
453
+ return [
454
+ `\u3010\u4EFB\u52A1\u3011\u8D5B\u540E MVP \u6295\u7968\uFF08\u7B2C ${round} \u8F6E\uFF09`,
455
+ deadlineSeconds != null ? `\u8BF7\u5728 ${deadlineSeconds} \u79D2\u5185\u4ECE\u5019\u9009\u5BF9\u8C61\u91CC\u9009\u51FA\u4F60\u8BA4\u4E3A\u672C\u5C40\u8D21\u732E\u6700\u5927\u7684 Agent\u3002` : `\u8BF7\u4ECE\u5019\u9009\u5BF9\u8C61\u91CC\u9009\u51FA\u4F60\u8BA4\u4E3A\u672C\u5C40\u8D21\u732E\u6700\u5927\u7684 Agent\u3002`,
456
+ `\u5019\u9009\u5BF9\u8C61\uFF08targetAgentId \u5FC5\u987B\u4ECE\u8FD9\u91CC\u9009\uFF0C\u4E0D\u80FD\u6295\u7ED9\u81EA\u5DF1\uFF09\uFF1A`,
457
+ renderMvpCandidateList(candidates, selfAgentId),
458
+ ``,
459
+ `\u8BF7\u7EFC\u5408\u53D1\u8A00\u3001\u6295\u7968\u3001\u5173\u952E\u884C\u52A8\u548C\u80DC\u8D1F\u8D21\u732E\uFF0C\u7B80\u77ED\u8BF4\u660E\u7406\u7531\uFF0C\u7136\u540E\u5728\u56DE\u590D\u6700\u540E\u4E25\u683C\u6309\u4EE5\u4E0B\u683C\u5F0F\u58F0\u660E\u52A8\u4F5C\uFF1A`,
460
+ `<ACTION>`,
461
+ `{"actionType":"MVP_VOTE","actionData":{"targetAgentId":<number>,"round":${round},"reason":"<\u7B80\u8FF0>"}}`,
462
+ `</ACTION>`
463
+ ].join("\n");
464
+ }
422
465
  function renderUnknownTurn(eventType) {
423
466
  return [
424
467
  `\u3010\u63D0\u793A\u3011\u672A\u8BC6\u522B\u7684\u6E38\u620F\u4E8B\u4EF6\u7C7B\u578B\uFF1A${eventType}`,
@@ -427,7 +470,8 @@ function renderUnknownTurn(eventType) {
427
470
  }
428
471
  function buildGameEventPrompt(eventType, eventData) {
429
472
  const outer = asRecord(eventData);
430
- const payload = asRecord(outer.payload);
473
+ const nestedPayload = asRecord(outer.payload);
474
+ const payload = Object.keys(nestedPayload).length > 0 ? nestedPayload : outer;
431
475
  const header = renderHeader(eventType, outer, payload);
432
476
  const task = renderTask(eventType, payload);
433
477
  const footer = `
@@ -847,16 +891,38 @@ function parseAgentAction(text) {
847
891
  function asNumberArray2(v) {
848
892
  return Array.isArray(v) ? v.filter((x) => typeof x === "number") : [];
849
893
  }
894
+ function asNumberOrNull2(v) {
895
+ return typeof v === "number" && Number.isFinite(v) ? v : null;
896
+ }
850
897
  function asRecord2(v) {
851
898
  return typeof v === "object" && v !== null ? v : {};
852
899
  }
900
+ function hasKeys(record) {
901
+ return Object.keys(record).length > 0;
902
+ }
903
+ function candidateAgentIds(v) {
904
+ if (!Array.isArray(v)) return [];
905
+ return v.flatMap((item) => {
906
+ if (typeof item === "number" && Number.isFinite(item)) return [item];
907
+ const record = asRecord2(item);
908
+ const agentId = asNumberOrNull2(record.agentId) ?? asNumberOrNull2(record.targetAgentId);
909
+ return agentId == null ? [] : [agentId];
910
+ });
911
+ }
853
912
  function pickRandomAlive(aliveSeats, excludeSeat) {
854
913
  const candidates = excludeSeat != null ? aliveSeats.filter((s) => s !== excludeSeat) : aliveSeats;
855
914
  if (candidates.length === 0) return null;
856
915
  return candidates[Math.floor(Math.random() * candidates.length)];
857
916
  }
917
+ function pickRandomAgent(candidates, excludeAgentId) {
918
+ const eligible = excludeAgentId == null ? candidates : candidates.filter((candidate) => candidate !== excludeAgentId);
919
+ if (eligible.length === 0) return candidates[0] ?? null;
920
+ return eligible[Math.floor(Math.random() * eligible.length)];
921
+ }
858
922
  function fallbackActionFor(eventType, eventData) {
859
- const payload = asRecord2(asRecord2(eventData).payload);
923
+ const outer = asRecord2(eventData);
924
+ const nestedPayload = asRecord2(outer.payload);
925
+ const payload = hasKeys(nestedPayload) ? nestedPayload : outer;
860
926
  const aliveSeats = asNumberArray2(payload.aliveSeats);
861
927
  const selfSeat = typeof payload.selfSeat === "number" ? payload.selfSeat : void 0;
862
928
  switch (eventType) {
@@ -901,6 +967,22 @@ function fallbackActionFor(eventType, eventData) {
901
967
  };
902
968
  case "HUNTER_SKILL_TURN":
903
969
  return { actionType: "HUNTER_PASS", actionData: {} };
970
+ case "MVP_VOTE_REQUEST": {
971
+ const selfAgentId = asNumberOrNull2(payload.selfAgentId);
972
+ const candidates = candidateAgentIds(payload.candidates);
973
+ const targetAgentId = pickRandomAgent(candidates, selfAgentId);
974
+ if (targetAgentId == null) {
975
+ return { actionType: "UNKNOWN", actionData: {} };
976
+ }
977
+ return {
978
+ actionType: "MVP_VOTE",
979
+ actionData: {
980
+ targetAgentId,
981
+ round: asNumberOrNull2(payload.round) ?? asNumberOrNull2(outer.round) ?? 1,
982
+ reason: "\uFF08\u6258\u7BA1\uFF1ALLM \u672A\u7ED9\u51FA\u5408\u6CD5 MVP \u6295\u7968\uFF09"
983
+ }
984
+ };
985
+ }
904
986
  default:
905
987
  return { actionType: "UNKNOWN", actionData: {} };
906
988
  }
@@ -912,6 +994,7 @@ var CoolclawWsClient = class {
912
994
  constructor(options) {
913
995
  this.options = options;
914
996
  }
997
+ options;
915
998
  socket;
916
999
  heartbeatTimer;
917
1000
  reconnectTimer;
@@ -1451,6 +1534,12 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1451
1534
  await runtime.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
1452
1535
  ctx: ctxPayload,
1453
1536
  cfg: ctx.cfg,
1537
+ // 群聊强制走 automatic:CoolClaw 群聊业务语义就是 @ 即回,
1538
+ // 而 OpenClaw 默认对 group/channel 走 message_tool_only,
1539
+ // 模型若不主动调 message 工具就会被 runtime 静默吞掉,
1540
+ // 用户感知为"小甲群里不回"。这里按消息粒度覆盖,
1541
+ // 不污染用户全局 openclaw.json,私聊保持 SDK 默认。
1542
+ replyOptions: isGroup ? { sourceReplyDeliveryMode: "automatic" } : void 0,
1454
1543
  dispatcherOptions: {
1455
1544
  deliver: async (payload) => {
1456
1545
  if (!payload.text) return;
@@ -3,7 +3,7 @@ import {
3
3
  coolclawChannelPlugin,
4
4
  defaultBindingFile,
5
5
  setCoolclawRuntime
6
- } from "./chunk-RCCYELQW.js";
6
+ } from "./chunk-QNBJDZKJ.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-OYFUVZNM.js";
4
- import "./chunk-RCCYELQW.js";
3
+ } from "./chunk-TS5TXH6P.js";
4
+ import "./chunk-QNBJDZKJ.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-OYFUVZNM.js";
4
- import "./chunk-RCCYELQW.js";
3
+ } from "./chunk-TS5TXH6P.js";
4
+ import "./chunk-QNBJDZKJ.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-RCCYELQW.js";
3
+ } from "./chunk-QNBJDZKJ.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.0",
3
+ "version": "1.0.2",
4
4
  "description": "OpenClaw native channel plugin for Riddle/CoolClaw chat.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -41,16 +41,16 @@
41
41
  "lint": "tsc --noEmit"
42
42
  },
43
43
  "dependencies": {
44
- "ws": "latest",
45
- "zod": "latest"
44
+ "ws": "^8.20.1",
45
+ "zod": "^4.4.3"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/node": "latest",
49
- "@types/ws": "latest",
48
+ "@types/node": "^25.8.0",
49
+ "@types/ws": "^8.18.1",
50
50
  "openclaw": "^2026.4.27",
51
- "tsup": "latest",
52
- "typescript": "latest",
53
- "vitest": "latest"
51
+ "tsup": "^8.5.1",
52
+ "typescript": "^6.0.3",
53
+ "vitest": "^4.1.6"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "openclaw": ">=2026.3.22 <2027"
@@ -71,7 +71,7 @@
71
71
  "runtimeSetupEntry": "./dist/setup-entry.js",
72
72
  "install": {
73
73
  "npmSpec": "@coolclaw/coolclaw",
74
- "expectedIntegrity": "sha512-Wef0UFOLGxMxiPs4xoWVJu4X4ZHIZqzchDYi2n2bA6Bcg176PpnPMFnLnaTapZL77RMxOwT8io219bkY2mJroQ==",
74
+ "expectedIntegrity": "sha512-imrUEvouY9fCFzZT2GNJBKsuYaJm9boaC6SVKJu5pGXvrDWrYrWmxA/buWiw5ATlJvrIvZZxF3jAk7b9BMnMzA==",
75
75
  "defaultChoice": "npm",
76
76
  "minHostVersion": ">=2026.3.22"
77
77
  },