@coolclaw/coolclaw 1.0.6 → 1.0.8

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.
@@ -154,13 +154,18 @@ function validateAgentAction(action, task) {
154
154
  if (!option) {
155
155
  return { ok: false, reason: "disallowed_action_type" };
156
156
  }
157
- if (!matchesActionDataSchema(action.actionData, option.actionDataSchema)) {
157
+ const repaired = repairActionDataForSchema(action.actionData, option.actionDataSchema);
158
+ if (!matchesActionDataSchema(repaired.actionData, option.actionDataSchema)) {
158
159
  return { ok: false, reason: "invalid_action_shape" };
159
160
  }
160
- if (containsPublicPrivateInfoLeak(action.actionType, action.actionData)) {
161
- return { ok: false, reason: "public_private_info_leak" };
162
- }
163
- return { ok: true, action };
161
+ return {
162
+ ok: true,
163
+ action: {
164
+ actionType: action.actionType,
165
+ actionData: repaired.actionData
166
+ },
167
+ repairReason: repaired.repairReason
168
+ };
164
169
  }
165
170
  function backendFallbackAction(task) {
166
171
  const fallback = task.fallbackAction;
@@ -218,6 +223,39 @@ function matchesActionDataSchema(actionData, schema) {
218
223
  }
219
224
  return true;
220
225
  }
226
+ function repairActionDataForSchema(actionData, schema) {
227
+ if (!schema || Object.keys(schema).length === 0) {
228
+ return { actionData };
229
+ }
230
+ const properties = isRecord(schema.properties) ? schema.properties : {};
231
+ let repaired = null;
232
+ const truncatedFields = [];
233
+ for (const [field, propertySchema] of Object.entries(properties)) {
234
+ const value = actionData[field];
235
+ if (typeof value !== "string" || !isRecord(propertySchema)) {
236
+ continue;
237
+ }
238
+ if (propertySchema.type !== "string" || typeof propertySchema.maxLength !== "number") {
239
+ continue;
240
+ }
241
+ const maxLength = Math.max(0, Math.floor(propertySchema.maxLength));
242
+ if (value.length <= maxLength) {
243
+ continue;
244
+ }
245
+ if (!repaired) {
246
+ repaired = { ...actionData };
247
+ }
248
+ repaired[field] = value.slice(0, maxLength);
249
+ truncatedFields.push(field);
250
+ }
251
+ if (truncatedFields.length === 0) {
252
+ return { actionData };
253
+ }
254
+ return {
255
+ actionData: repaired ?? actionData,
256
+ repairReason: truncatedFields.map((field) => `${field}_too_long`).join(",")
257
+ };
258
+ }
221
259
  function matchesSchemaValue(value, schema, required) {
222
260
  if (value == null) return !required;
223
261
  if (!isRecord(schema)) return true;
@@ -241,28 +279,6 @@ function matchesSchemaValue(value, schema, required) {
241
279
  return true;
242
280
  }
243
281
  }
244
- var PUBLIC_ACTION_TYPES = /* @__PURE__ */ new Set(["DAY_SPEAK", "DAY_VOTE", "LAST_WORD", "HUNTER_SHOOT", "HUNTER_PASS"]);
245
- var PUBLIC_TEXT_FIELDS = ["content", "reason"];
246
- var PRIVATE_LEAK_PATTERNS = [
247
- /我是\s*狼/u,
248
- /我是\s*狼人/u,
249
- /作为\s*狼/u,
250
- /我们\s*狼队/u,
251
- /我方\s*狼队/u,
252
- /我(?:的)?狼队友/u,
253
- /我(?:的)?队友/u,
254
- /(?:我|我们|我方).{0,8}夜聊/u,
255
- /夜聊.{0,16}(?:我说|我建议|我们|我方|决定|刀|击杀)/u,
256
- /(?:我们|我方)\s*狼队.*(?:刀|击杀|目标)/u,
257
- /今晚\s*(?:先)?刀/u
258
- ];
259
- function containsPublicPrivateInfoLeak(actionType, actionData) {
260
- if (!PUBLIC_ACTION_TYPES.has(actionType)) return false;
261
- return PUBLIC_TEXT_FIELDS.some((field) => {
262
- const value = actionData[field];
263
- return typeof value === "string" && PRIVATE_LEAK_PATTERNS.some((pattern) => pattern.test(value));
264
- });
265
- }
266
282
  function readString(value) {
267
283
  return typeof value === "string" && value.length > 0 ? value : void 0;
268
284
  }
@@ -723,6 +739,7 @@ async function sendGameAction(input) {
723
739
  timestamp: String(Date.now()),
724
740
  turnSeq: input.turnSeq,
725
741
  eventId: input.eventId,
742
+ deadlineEpochMs: input.deadlineEpochMs,
726
743
  traceId: input.traceId,
727
744
  promptPolicyVersion: input.promptPolicyVersion,
728
745
  renderedPromptHash: input.renderedPromptHash,
@@ -737,6 +754,7 @@ async function sendGameAction(input) {
737
754
  if (response.ok === false) {
738
755
  throw new Error(response.error?.message ?? "CoolClaw game action failed");
739
756
  }
757
+ return response;
740
758
  }
741
759
 
742
760
  // src/game-action-parser.ts
@@ -811,7 +829,16 @@ function parseActionJson(body) {
811
829
  try {
812
830
  obj = JSON.parse(body);
813
831
  } catch (e) {
814
- return { error: "invalid_json", detail: e instanceof Error ? e.message : String(e) };
832
+ const repaired = removeTrailingCommas(body);
833
+ if (repaired !== body) {
834
+ try {
835
+ obj = JSON.parse(repaired);
836
+ } catch {
837
+ return { error: "invalid_json", detail: e instanceof Error ? e.message : String(e) };
838
+ }
839
+ } else {
840
+ return { error: "invalid_json", detail: e instanceof Error ? e.message : String(e) };
841
+ }
815
842
  }
816
843
  if (typeof obj !== "object" || obj === null) {
817
844
  return { error: "invalid_json", detail: "not an object" };
@@ -828,6 +855,41 @@ function parseActionJson(body) {
828
855
  actionData: rec.actionData
829
856
  };
830
857
  }
858
+ function removeTrailingCommas(body) {
859
+ let out = "";
860
+ let inString = false;
861
+ let escaped = false;
862
+ for (let i = 0; i < body.length; i += 1) {
863
+ const ch = body[i];
864
+ if (inString) {
865
+ out += ch;
866
+ if (escaped) {
867
+ escaped = false;
868
+ } else if (ch === "\\") {
869
+ escaped = true;
870
+ } else if (ch === '"') {
871
+ inString = false;
872
+ }
873
+ continue;
874
+ }
875
+ if (ch === '"') {
876
+ inString = true;
877
+ out += ch;
878
+ continue;
879
+ }
880
+ if (ch === ",") {
881
+ let j = i + 1;
882
+ while (j < body.length && /\s/.test(body[j])) {
883
+ j += 1;
884
+ }
885
+ if (body[j] === "}" || body[j] === "]") {
886
+ continue;
887
+ }
888
+ }
889
+ out += ch;
890
+ }
891
+ return out;
892
+ }
831
893
 
832
894
  // src/game-action-audit.ts
833
895
  function normalizeAuditText(value, maxChars = 256) {
@@ -1149,7 +1211,7 @@ function logAckFailure(params) {
1149
1211
  async function submitGameActionWithLog(action, meta, wsClient, log, source, rawResponse, auditMeta) {
1150
1212
  if (!wsClient.isConnected()) {
1151
1213
  log?.error?.(`[GAME-ACTION] submit skipped: ws not connected eventId=${meta.eventId}`);
1152
- return false;
1214
+ return "failed";
1153
1215
  }
1154
1216
  const responseHash = rawResponse && rawResponse.length > 0 ? sha256Hex(rawResponse) : void 0;
1155
1217
  log?.info?.(
@@ -1157,7 +1219,7 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
1157
1219
  );
1158
1220
  const start = Date.now();
1159
1221
  try {
1160
- await sendGameAction({
1222
+ const response = await sendGameAction({
1161
1223
  client: wsClient,
1162
1224
  gameId: meta.gameId,
1163
1225
  roomId: meta.roomId,
@@ -1166,6 +1228,7 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
1166
1228
  actionData: action.actionData,
1167
1229
  turnSeq: meta.turnSeq,
1168
1230
  eventId: meta.eventId,
1231
+ deadlineEpochMs: meta.deadlineEpochMs,
1169
1232
  traceId: meta.traceId,
1170
1233
  promptPolicyVersion: meta.promptPolicyVersion,
1171
1234
  renderedPromptHash: meta.renderedPromptHash,
@@ -1176,16 +1239,28 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
1176
1239
  modelActionType: auditMeta?.modelActionType,
1177
1240
  validationReason: normalizeAuditText(auditMeta?.validationReason)
1178
1241
  });
1242
+ if (response.uncertain === true) {
1243
+ log?.warn?.(
1244
+ `[GAME-ACTION] submit uncertain source=${source} eventId=${meta.eventId} elapsedMs=${Date.now() - start} msg=${response.message ?? ""}`
1245
+ );
1246
+ return "uncertain";
1247
+ }
1179
1248
  log?.info?.(
1180
1249
  `[GAME-ACTION] submit ok source=${source} gameId=${meta.gameId} eventId=${meta.eventId} promptPolicyVersion=${meta.promptPolicyVersion ?? ""} renderedPromptHash=${meta.renderedPromptHash ?? ""} rawResponseHash=${responseHash ?? ""} elapsedMs=${Date.now() - start}`
1181
1250
  );
1182
- return true;
1251
+ return "submitted";
1183
1252
  } catch (err) {
1184
1253
  const errMsg = err instanceof Error ? err.message : String(err);
1254
+ if (isGameActionSubmitUncertainError(errMsg)) {
1255
+ log?.warn?.(
1256
+ `[GAME-ACTION] submit uncertain source=${source} eventId=${meta.eventId} elapsedMs=${Date.now() - start} err=${errMsg}`
1257
+ );
1258
+ return "uncertain";
1259
+ }
1185
1260
  log?.error?.(
1186
1261
  `[GAME-ACTION] submit failed source=${source} eventId=${meta.eventId} elapsedMs=${Date.now() - start} err=${errMsg}`
1187
1262
  );
1188
- return false;
1263
+ return "failed";
1189
1264
  }
1190
1265
  }
1191
1266
  async function submitBackendFallbackWithLog(params) {
@@ -1194,7 +1269,7 @@ async function submitBackendFallbackWithLog(params) {
1194
1269
  params.log?.warn?.(
1195
1270
  `[GAME-ACTION] backend fallback unavailable eventType=${params.meta.eventType} eventId=${params.meta.eventId} reason=${params.reason} promptPolicyVersion=${params.meta.promptPolicyVersion ?? ""} renderedPromptHash=${params.meta.renderedPromptHash ?? ""}`
1196
1271
  );
1197
- return false;
1272
+ return "failed";
1198
1273
  }
1199
1274
  const inferred = inferRejectedModelAction(params.rawResponse ?? "", params.meta.agentTask);
1200
1275
  const auditMeta = {
@@ -1215,6 +1290,9 @@ async function submitBackendFallbackWithLog(params) {
1215
1290
  auditMeta
1216
1291
  );
1217
1292
  }
1293
+ function isGameActionSubmitUncertainError(message) {
1294
+ return /request timed out|wss client stopped|websocket.*clos|socket hang up|econnreset|write epipe/i.test(message);
1295
+ }
1218
1296
  var runtimeClients = /* @__PURE__ */ new Map();
1219
1297
  function setRuntimeClient(accountKey, client) {
1220
1298
  runtimeClients.set(accountKey, client);
@@ -1386,7 +1464,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1386
1464
  log: ctx.log,
1387
1465
  reason: "runtime_not_available"
1388
1466
  });
1389
- if (!submitted) {
1467
+ if (submitted === "failed") {
1390
1468
  throw new Error("game fallback submit failed: runtime_not_available");
1391
1469
  }
1392
1470
  }
@@ -1509,10 +1587,11 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1509
1587
  full,
1510
1588
  {
1511
1589
  modelActionRejected: false,
1512
- modelActionType: validation.action.actionType
1590
+ modelActionType: validation.action.actionType,
1591
+ validationReason: validation.repairReason
1513
1592
  }
1514
1593
  );
1515
- if (submitted) {
1594
+ if (submitted === "submitted" || submitted === "uncertain") {
1516
1595
  gameSubmitted = true;
1517
1596
  } else {
1518
1597
  gameFallbackReason = `llm_action_submit_failed:${validation.action.actionType}`;
@@ -1580,7 +1659,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
1580
1659
  validationReason: gameFallbackReason ?? gameValidationReason ?? inferred.validationReason
1581
1660
  }
1582
1661
  });
1583
- if (!submitted) {
1662
+ if (submitted === "failed") {
1584
1663
  throw new Error(`game fallback submit failed: ${gameFallbackReason ?? "unknown"}`);
1585
1664
  }
1586
1665
  } catch (fbErr) {
@@ -3,7 +3,7 @@ import {
3
3
  coolclawChannelPlugin,
4
4
  defaultBindingFile,
5
5
  setCoolclawRuntime
6
- } from "./chunk-W6LHGLKL.js";
6
+ } from "./chunk-DYLCY242.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-4RKDX3HY.js";
4
- import "./chunk-W6LHGLKL.js";
3
+ } from "./chunk-Y7YB5VOE.js";
4
+ import "./chunk-DYLCY242.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-4RKDX3HY.js";
4
- import "./chunk-W6LHGLKL.js";
3
+ } from "./chunk-Y7YB5VOE.js";
4
+ import "./chunk-DYLCY242.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-W6LHGLKL.js";
3
+ } from "./chunk-DYLCY242.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.6",
3
+ "version": "1.0.8",
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-7VcGcTTi2KmOCAJ76tj8uyEIUURe6LftxSpjmbrLXzcKC7YKjNrpNzwFMBnN4g4yfxtSsyf0bhT61gsI8RyUpw==",
75
+ "expectedIntegrity": "sha512-IKiiU+gXYGRuZ7f72a8WyViE0+Hol2AclkebfDB4HuusLo2BNzKpuinwzp8BIiKkCKoeSNO1xhjxwtKD6U4YfQ==",
76
76
  "defaultChoice": "npm",
77
77
  "minHostVersion": ">=2026.3.22"
78
78
  },