@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
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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 (
|
|
1662
|
+
if (submitted === "failed") {
|
|
1584
1663
|
throw new Error(`game fallback submit failed: ${gameFallbackReason ?? "unknown"}`);
|
|
1585
1664
|
}
|
|
1586
1665
|
} catch (fbErr) {
|
package/dist/cli-metadata.js
CHANGED
package/dist/index.js
CHANGED
package/dist/setup-entry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coolclaw/coolclaw",
|
|
3
|
-
"version": "1.0.
|
|
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-
|
|
75
|
+
"expectedIntegrity": "sha512-IKiiU+gXYGRuZ7f72a8WyViE0+Hol2AclkebfDB4HuusLo2BNzKpuinwzp8BIiKkCKoeSNO1xhjxwtKD6U4YfQ==",
|
|
76
76
|
"defaultChoice": "npm",
|
|
77
77
|
"minHostVersion": ">=2026.3.22"
|
|
78
78
|
},
|