@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 repaired = repairActionDataForSchema(action.actionData, option.actionDataSchema);
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.reason === "string") normalizedAction.reason = action.reason;
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: normalizedAction,
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.reason === "string") root.reason = action.reason;
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.slice(0, maxLength);
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
- reason: input.reason
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.reason === "string" ? { reason: rec.reason } : {}
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.reason === "string" ? { reason: rec.reason } : {}
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\u3001\u4EE3\u7801\u5757\u6216\u65E7\u52A8\u4F5C\u6807\u7B7E\u3002`;
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
- reason: action.reason
2125
+ voteReason: action.voteReason
2079
2126
  });
2080
2127
  if (response.uncertain === true) {
2081
2128
  log?.warn?.(
@@ -4,7 +4,7 @@ import {
4
4
  coolclawChannelPlugin,
5
5
  defaultBindingFile,
6
6
  setCoolclawRuntime
7
- } from "./chunk-3YKMAM6K.js";
7
+ } from "./chunk-MKVNFXLL.js";
8
8
 
9
9
  // index.ts
10
10
  import { defineChannelPluginEntry, buildChannelConfigSchema } from "openclaw/plugin-sdk/core";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  index_default
3
- } from "./chunk-RPT3G66A.js";
4
- import "./chunk-3YKMAM6K.js";
3
+ } from "./chunk-R3WD2GBM.js";
4
+ import "./chunk-MKVNFXLL.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-RPT3G66A.js";
4
- import "./chunk-3YKMAM6K.js";
3
+ } from "./chunk-R3WD2GBM.js";
4
+ import "./chunk-MKVNFXLL.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-3YKMAM6K.js";
3
+ } from "./chunk-MKVNFXLL.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.17",
3
+ "version": "1.0.19",
4
4
  "description": "OpenClaw native channel plugin for CoolClaw chat.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",