@coolclaw/coolclaw 1.0.17 → 1.0.19
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.
|
@@ -219,23 +219,26 @@ function validateAgentAction(action, task) {
|
|
|
219
219
|
if (!option) {
|
|
220
220
|
return { ok: false, reason: "disallowed_action_type" };
|
|
221
221
|
}
|
|
222
|
-
const
|
|
222
|
+
const sanitizedActionData = sanitizeActionData(action.actionData);
|
|
223
|
+
const repaired = repairActionDataForSchema(sanitizedActionData, option.actionDataSchema);
|
|
223
224
|
if (!matchesActionDataSchema(repaired.actionData, option.actionDataSchema)) {
|
|
224
225
|
return { ok: false, reason: "invalid_action_shape" };
|
|
225
226
|
}
|
|
226
|
-
if (isStructuredJsonTask(task) && !matchesRootOutputSchema(action, task.outputSchema)) {
|
|
227
|
-
return { ok: false, reason: "invalid_action_shape" };
|
|
228
|
-
}
|
|
229
227
|
const normalizedAction = {
|
|
230
228
|
actionType: action.actionType,
|
|
231
229
|
actionData: repaired.actionData
|
|
232
230
|
};
|
|
233
231
|
if (typeof action.speech === "string") normalizedAction.speech = action.speech;
|
|
234
|
-
if (typeof action.
|
|
232
|
+
if (typeof action.voteReason === "string") normalizedAction.voteReason = action.voteReason;
|
|
233
|
+
const shouldValidateRoot = isStructuredJsonTask(task);
|
|
234
|
+
const repairedRoot = shouldValidateRoot ? repairRootOutputForSchema(normalizedAction, task.outputSchema) : { action: normalizedAction };
|
|
235
|
+
if (!repairedRoot || shouldValidateRoot && !matchesRootOutputSchema(repairedRoot.action, task.outputSchema)) {
|
|
236
|
+
return { ok: false, reason: "invalid_action_shape" };
|
|
237
|
+
}
|
|
235
238
|
return {
|
|
236
239
|
ok: true,
|
|
237
|
-
action:
|
|
238
|
-
repairReason: repaired.repairReason
|
|
240
|
+
action: repairedRoot.action,
|
|
241
|
+
repairReason: combineRepairReasons(repaired.repairReason, repairedRoot.repairReason)
|
|
239
242
|
};
|
|
240
243
|
}
|
|
241
244
|
function backendFallbackAction(task) {
|
|
@@ -296,9 +299,17 @@ function matchesRootOutputSchema(action, schema) {
|
|
|
296
299
|
actionData: action.actionData
|
|
297
300
|
};
|
|
298
301
|
if (typeof action.speech === "string") root.speech = action.speech;
|
|
299
|
-
if (typeof action.
|
|
302
|
+
if (typeof action.voteReason === "string") root.voteReason = action.voteReason;
|
|
300
303
|
return matchesSchemaValue(root, schema, true);
|
|
301
304
|
}
|
|
305
|
+
function sanitizeActionData(actionData) {
|
|
306
|
+
if (!Object.prototype.hasOwnProperty.call(actionData, "reason")) {
|
|
307
|
+
return actionData;
|
|
308
|
+
}
|
|
309
|
+
const sanitized = { ...actionData };
|
|
310
|
+
delete sanitized.reason;
|
|
311
|
+
return sanitized;
|
|
312
|
+
}
|
|
302
313
|
function repairActionDataForSchema(actionData, schema) {
|
|
303
314
|
if (!schema || Object.keys(schema).length === 0) {
|
|
304
315
|
return { actionData };
|
|
@@ -321,7 +332,7 @@ function repairActionDataForSchema(actionData, schema) {
|
|
|
321
332
|
if (!repaired) {
|
|
322
333
|
repaired = { ...actionData };
|
|
323
334
|
}
|
|
324
|
-
repaired[field] = value
|
|
335
|
+
repaired[field] = truncateWithEllipsis(value, maxLength);
|
|
325
336
|
truncatedFields.push(field);
|
|
326
337
|
}
|
|
327
338
|
if (truncatedFields.length === 0) {
|
|
@@ -332,6 +343,54 @@ function repairActionDataForSchema(actionData, schema) {
|
|
|
332
343
|
repairReason: truncatedFields.map((field) => `${field}_too_long`).join(",")
|
|
333
344
|
};
|
|
334
345
|
}
|
|
346
|
+
function repairRootOutputForSchema(action, schema) {
|
|
347
|
+
if (!schema || Object.keys(schema).length === 0) {
|
|
348
|
+
return { action };
|
|
349
|
+
}
|
|
350
|
+
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
351
|
+
let repaired = null;
|
|
352
|
+
const truncatedFields = [];
|
|
353
|
+
for (const field of ["speech", "voteReason"]) {
|
|
354
|
+
const propertySchema = properties[field];
|
|
355
|
+
const value = action[field];
|
|
356
|
+
if (typeof value !== "string" || !isRecord(propertySchema)) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (!schemaAllowsType(propertySchema.type, "string") || typeof propertySchema.maxLength !== "number") {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
const maxLength = Math.max(0, Math.floor(propertySchema.maxLength));
|
|
363
|
+
if (value.length <= maxLength) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (!repaired) {
|
|
367
|
+
repaired = { ...action };
|
|
368
|
+
}
|
|
369
|
+
repaired[field] = truncateWithEllipsis(value, maxLength);
|
|
370
|
+
truncatedFields.push(field);
|
|
371
|
+
}
|
|
372
|
+
if (truncatedFields.length === 0) {
|
|
373
|
+
return { action };
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
action: repaired ?? action,
|
|
377
|
+
repairReason: truncatedFields.map((field) => `${field}_too_long`).join(",")
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function combineRepairReasons(first, second) {
|
|
381
|
+
if (!first) return second;
|
|
382
|
+
if (!second) return first;
|
|
383
|
+
return `${first},${second}`;
|
|
384
|
+
}
|
|
385
|
+
function truncateWithEllipsis(value, maxLength) {
|
|
386
|
+
if (value.length <= maxLength) {
|
|
387
|
+
return value;
|
|
388
|
+
}
|
|
389
|
+
if (maxLength <= 3) {
|
|
390
|
+
return value.slice(0, Math.max(0, maxLength));
|
|
391
|
+
}
|
|
392
|
+
return `${value.slice(0, maxLength - 3)}...`;
|
|
393
|
+
}
|
|
335
394
|
function matchesSchemaValue(value, schema, required) {
|
|
336
395
|
if (!isRecord(schema)) return true;
|
|
337
396
|
if (value == null) {
|
|
@@ -1111,7 +1170,7 @@ async function sendGameAction(input) {
|
|
|
1111
1170
|
retryRawResponseHash: input.retryRawResponseHash,
|
|
1112
1171
|
retryValidationReason: input.retryValidationReason,
|
|
1113
1172
|
speech: input.speech,
|
|
1114
|
-
|
|
1173
|
+
voteReason: input.voteReason
|
|
1115
1174
|
});
|
|
1116
1175
|
const response = await input.client.request(frame);
|
|
1117
1176
|
if (response.ok === false) {
|
|
@@ -1121,15 +1180,6 @@ async function sendGameAction(input) {
|
|
|
1121
1180
|
}
|
|
1122
1181
|
|
|
1123
1182
|
// src/game-action-parser.ts
|
|
1124
|
-
function extractActionBlock(text) {
|
|
1125
|
-
const re = /<ACTION>\s*([\s\S]+?)\s*<\/ACTION>/gi;
|
|
1126
|
-
let match;
|
|
1127
|
-
let last = null;
|
|
1128
|
-
while ((match = re.exec(text)) !== null) {
|
|
1129
|
-
last = match[1];
|
|
1130
|
-
}
|
|
1131
|
-
return last;
|
|
1132
|
-
}
|
|
1133
1183
|
function extractFencedJsonBlocks(text) {
|
|
1134
1184
|
const re = /```(?:json)?\s*([\s\S]*?)\s*```/gi;
|
|
1135
1185
|
const blocks = [];
|
|
@@ -1149,15 +1199,12 @@ function extractTrailingJsonCandidates(text) {
|
|
|
1149
1199
|
if (candidate.includes('"actionType"') && candidate.includes('"actionData"')) {
|
|
1150
1200
|
candidates.push(candidate);
|
|
1151
1201
|
}
|
|
1202
|
+
if (idx === 0) break;
|
|
1152
1203
|
idx = trimmed.lastIndexOf("{", idx - 1);
|
|
1153
1204
|
}
|
|
1154
1205
|
return candidates;
|
|
1155
1206
|
}
|
|
1156
1207
|
function extractActionCandidates(text) {
|
|
1157
|
-
const actionBlock = extractActionBlock(text);
|
|
1158
|
-
if (actionBlock !== null) {
|
|
1159
|
-
return [actionBlock];
|
|
1160
|
-
}
|
|
1161
1208
|
const fenced = extractFencedJsonBlocks(text);
|
|
1162
1209
|
if (fenced.length > 0) {
|
|
1163
1210
|
return fenced.reverse();
|
|
@@ -1270,7 +1317,7 @@ function parseActionJson(body) {
|
|
|
1270
1317
|
actionType: rec.actionType,
|
|
1271
1318
|
actionData: rec.actionData,
|
|
1272
1319
|
...typeof rec.speech === "string" ? { speech: rec.speech } : {},
|
|
1273
|
-
...typeof rec.
|
|
1320
|
+
...typeof rec.voteReason === "string" ? { voteReason: rec.voteReason } : {}
|
|
1274
1321
|
};
|
|
1275
1322
|
}
|
|
1276
1323
|
function parseActionJsonStrict(body) {
|
|
@@ -1285,7 +1332,7 @@ function parseActionJsonStrict(body) {
|
|
|
1285
1332
|
}
|
|
1286
1333
|
const rec = obj;
|
|
1287
1334
|
for (const field of Object.keys(rec)) {
|
|
1288
|
-
if (!["speech", "reason", "actionType", "actionData"].includes(field)) {
|
|
1335
|
+
if (!["speech", "voteReason", "reason", "actionType", "actionData"].includes(field)) {
|
|
1289
1336
|
return { error: "invalid_json", detail: `unknown_top_level_field:${field}` };
|
|
1290
1337
|
}
|
|
1291
1338
|
}
|
|
@@ -1299,7 +1346,7 @@ function parseActionJsonStrict(body) {
|
|
|
1299
1346
|
actionType: rec.actionType,
|
|
1300
1347
|
actionData: rec.actionData,
|
|
1301
1348
|
...typeof rec.speech === "string" ? { speech: rec.speech } : {},
|
|
1302
|
-
...typeof rec.
|
|
1349
|
+
...typeof rec.voteReason === "string" ? { voteReason: rec.voteReason } : {}
|
|
1303
1350
|
};
|
|
1304
1351
|
}
|
|
1305
1352
|
function removeTrailingCommas(body) {
|
|
@@ -1993,7 +2040,7 @@ function buildStructuredActionRetryPrompt(renderedPrompt, reason) {
|
|
|
1993
2040
|
return `${renderedPrompt}
|
|
1994
2041
|
|
|
1995
2042
|
\u4E0A\u4E00\u6B21\u8F93\u51FA\u672A\u88AB\u72FC\u4EBA\u6740\u7ED3\u6784\u5316\u52A8\u4F5C\u534F\u8BAE\u63A5\u53D7\uFF0C\u5931\u8D25\u539F\u56E0\uFF1A${reasonText}\u3002
|
|
1996
|
-
\u8BF7\u91CD\u65B0\u4F5C\u7B54\uFF1A\u53EA\u8F93\u51FA\u4E00\u4E2A\u5B8C\u6574 JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u8F93\u51FA Markdown\u3001\u89E3\u91CA\u6587\u5B57\
|
|
2043
|
+
\u8BF7\u91CD\u65B0\u4F5C\u7B54\uFF1A\u53EA\u8F93\u51FA\u4E00\u4E2A\u5B8C\u6574 JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u8F93\u51FA Markdown\u3001\u89E3\u91CA\u6587\u5B57\u6216\u4EE3\u7801\u5757\u3002`;
|
|
1997
2044
|
}
|
|
1998
2045
|
function hasStructuredRetryBudget(deadlineEpochMs, nowEpochMs = Date.now(), safetyMarginMs = 1e3) {
|
|
1999
2046
|
if (!deadlineEpochMs || deadlineEpochMs <= 0) {
|
|
@@ -2075,7 +2122,7 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
|
|
|
2075
2122
|
retryRawResponseHash: auditMeta?.retryRawResponseHash,
|
|
2076
2123
|
retryValidationReason: normalizeAuditText(auditMeta?.retryValidationReason),
|
|
2077
2124
|
speech: action.speech,
|
|
2078
|
-
|
|
2125
|
+
voteReason: action.voteReason
|
|
2079
2126
|
});
|
|
2080
2127
|
if (response.uncertain === true) {
|
|
2081
2128
|
log?.warn?.(
|
package/dist/cli-metadata.js
CHANGED
package/dist/index.js
CHANGED
package/dist/setup-entry.js
CHANGED