@coolclaw/coolclaw 1.0.16 → 1.0.17
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.
|
@@ -180,6 +180,7 @@ var FileAckStore = class {
|
|
|
180
180
|
|
|
181
181
|
// src/agent-task.ts
|
|
182
182
|
import { createHash } from "crypto";
|
|
183
|
+
var WEREWOLF_STRUCTURED_ACTION_PROTOCOL_VERSION = "WEREWOLF_STRUCTURED_JSON_MESSAGE_V1";
|
|
183
184
|
function normalizeAgentTask(value) {
|
|
184
185
|
if (!isRecord(value)) return null;
|
|
185
186
|
const actionContract = normalizeActionContract(value.actionContract);
|
|
@@ -191,6 +192,9 @@ function normalizeAgentTask(value) {
|
|
|
191
192
|
renderedPrompt: readString(value.renderedPrompt) ?? "",
|
|
192
193
|
renderedPromptHash: readString(value.renderedPromptHash),
|
|
193
194
|
actionFormat: readString(value.actionFormat),
|
|
195
|
+
actionProtocolVersion: readString(value.actionProtocolVersion),
|
|
196
|
+
outputSchema: isRecord(value.outputSchema) ? value.outputSchema : void 0,
|
|
197
|
+
retryPolicy: normalizeRetryPolicy(value.retryPolicy),
|
|
194
198
|
conversationKey: readString(value.conversationKey),
|
|
195
199
|
actionContract,
|
|
196
200
|
fallbackAction,
|
|
@@ -200,6 +204,9 @@ function normalizeAgentTask(value) {
|
|
|
200
204
|
function isDispatchableAgentTask(task) {
|
|
201
205
|
return task.requiresReply === true && task.renderedPrompt.trim().length > 0 && allowedActionTypes(task).length > 0;
|
|
202
206
|
}
|
|
207
|
+
function isStructuredJsonTask(task) {
|
|
208
|
+
return task.actionProtocolVersion === WEREWOLF_STRUCTURED_ACTION_PROTOCOL_VERSION || task.actionFormat === "STRICT_JSON_OBJECT";
|
|
209
|
+
}
|
|
203
210
|
function validateAgentAction(action, task) {
|
|
204
211
|
if (!isRecord(action.actionData)) {
|
|
205
212
|
return { ok: false, reason: "invalid_action_shape" };
|
|
@@ -216,12 +223,18 @@ function validateAgentAction(action, task) {
|
|
|
216
223
|
if (!matchesActionDataSchema(repaired.actionData, option.actionDataSchema)) {
|
|
217
224
|
return { ok: false, reason: "invalid_action_shape" };
|
|
218
225
|
}
|
|
226
|
+
if (isStructuredJsonTask(task) && !matchesRootOutputSchema(action, task.outputSchema)) {
|
|
227
|
+
return { ok: false, reason: "invalid_action_shape" };
|
|
228
|
+
}
|
|
229
|
+
const normalizedAction = {
|
|
230
|
+
actionType: action.actionType,
|
|
231
|
+
actionData: repaired.actionData
|
|
232
|
+
};
|
|
233
|
+
if (typeof action.speech === "string") normalizedAction.speech = action.speech;
|
|
234
|
+
if (typeof action.reason === "string") normalizedAction.reason = action.reason;
|
|
219
235
|
return {
|
|
220
236
|
ok: true,
|
|
221
|
-
action:
|
|
222
|
-
actionType: action.actionType,
|
|
223
|
-
actionData: repaired.actionData
|
|
224
|
-
},
|
|
237
|
+
action: normalizedAction,
|
|
225
238
|
repairReason: repaired.repairReason
|
|
226
239
|
};
|
|
227
240
|
}
|
|
@@ -251,6 +264,14 @@ function normalizeActionContract(value) {
|
|
|
251
264
|
finalOutputRules: Array.isArray(value.finalOutputRules) ? value.finalOutputRules.filter((rule) => typeof rule === "string") : void 0
|
|
252
265
|
};
|
|
253
266
|
}
|
|
267
|
+
function normalizeRetryPolicy(value) {
|
|
268
|
+
if (!isRecord(value)) return void 0;
|
|
269
|
+
return {
|
|
270
|
+
maxRetries: readNumber(value.maxRetries),
|
|
271
|
+
shareDeadline: typeof value.shareDeadline === "boolean" ? value.shareDeadline : void 0,
|
|
272
|
+
rejectedOutputActionType: readString(value.rejectedOutputActionType)
|
|
273
|
+
};
|
|
274
|
+
}
|
|
254
275
|
function normalizeActionOption(value) {
|
|
255
276
|
if (!isRecord(value) || typeof value.actionType !== "string" || value.actionType.length === 0) {
|
|
256
277
|
return [];
|
|
@@ -266,20 +287,17 @@ function allowedActionTypes(task) {
|
|
|
266
287
|
}
|
|
267
288
|
function matchesActionDataSchema(actionData, schema) {
|
|
268
289
|
if (!schema || Object.keys(schema).length === 0) return true;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return true;
|
|
290
|
+
return matchesSchemaValue(actionData, schema, true);
|
|
291
|
+
}
|
|
292
|
+
function matchesRootOutputSchema(action, schema) {
|
|
293
|
+
if (!schema || Object.keys(schema).length === 0) return true;
|
|
294
|
+
const root = {
|
|
295
|
+
actionType: action.actionType,
|
|
296
|
+
actionData: action.actionData
|
|
297
|
+
};
|
|
298
|
+
if (typeof action.speech === "string") root.speech = action.speech;
|
|
299
|
+
if (typeof action.reason === "string") root.reason = action.reason;
|
|
300
|
+
return matchesSchemaValue(root, schema, true);
|
|
283
301
|
}
|
|
284
302
|
function repairActionDataForSchema(actionData, schema) {
|
|
285
303
|
if (!schema || Object.keys(schema).length === 0) {
|
|
@@ -315,12 +333,28 @@ function repairActionDataForSchema(actionData, schema) {
|
|
|
315
333
|
};
|
|
316
334
|
}
|
|
317
335
|
function matchesSchemaValue(value, schema, required) {
|
|
318
|
-
if (value == null) return !required;
|
|
319
336
|
if (!isRecord(schema)) return true;
|
|
337
|
+
if (value == null) {
|
|
338
|
+
return !required || schemaAllowsType(schema.type, "null");
|
|
339
|
+
}
|
|
340
|
+
const oneOf = Array.isArray(schema.oneOf) ? schema.oneOf : [];
|
|
341
|
+
if (oneOf.length > 0) {
|
|
342
|
+
return oneOf.some((variant) => matchesSchemaValue(value, variant, required));
|
|
343
|
+
}
|
|
320
344
|
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
321
345
|
return false;
|
|
322
346
|
}
|
|
323
|
-
|
|
347
|
+
const allowedTypes = Array.isArray(schema.type) ? schema.type.filter((type) => typeof type === "string") : typeof schema.type === "string" ? [schema.type] : [];
|
|
348
|
+
if (allowedTypes.length === 0) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
return allowedTypes.some((type) => matchesSchemaTypedValue(value, schema, type));
|
|
352
|
+
}
|
|
353
|
+
function schemaAllowsType(typeNode, type) {
|
|
354
|
+
return Array.isArray(typeNode) ? typeNode.includes(type) : typeNode === type;
|
|
355
|
+
}
|
|
356
|
+
function matchesSchemaTypedValue(value, schema, type) {
|
|
357
|
+
switch (type) {
|
|
324
358
|
case "integer":
|
|
325
359
|
return typeof value === "number" && Number.isInteger(value);
|
|
326
360
|
case "number":
|
|
@@ -330,13 +364,33 @@ function matchesSchemaValue(value, schema, required) {
|
|
|
330
364
|
case "boolean":
|
|
331
365
|
return typeof value === "boolean";
|
|
332
366
|
case "object":
|
|
333
|
-
return
|
|
367
|
+
return matchesObjectSchema(value, schema);
|
|
334
368
|
case "array":
|
|
335
369
|
return Array.isArray(value);
|
|
370
|
+
case "null":
|
|
371
|
+
return value == null;
|
|
336
372
|
default:
|
|
337
373
|
return true;
|
|
338
374
|
}
|
|
339
375
|
}
|
|
376
|
+
function matchesObjectSchema(value, schema) {
|
|
377
|
+
if (!isRecord(value)) return false;
|
|
378
|
+
const properties = isRecord(schema.properties) ? schema.properties : {};
|
|
379
|
+
const required = Array.isArray(schema.required) ? schema.required.filter((field) => typeof field === "string" && field.length > 0) : [];
|
|
380
|
+
for (const field of required) {
|
|
381
|
+
if (!Object.prototype.hasOwnProperty.call(value, field)) return false;
|
|
382
|
+
if (!matchesSchemaValue(value[field], properties[field], true)) return false;
|
|
383
|
+
}
|
|
384
|
+
for (const [field, fieldValue] of Object.entries(value)) {
|
|
385
|
+
const propertySchema = properties[field];
|
|
386
|
+
if (propertySchema === void 0) {
|
|
387
|
+
if (schema.additionalProperties === false) return false;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (!matchesSchemaValue(fieldValue, propertySchema, false)) return false;
|
|
391
|
+
}
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
340
394
|
function readString(value) {
|
|
341
395
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
342
396
|
}
|
|
@@ -1047,7 +1101,17 @@ async function sendGameAction(input) {
|
|
|
1047
1101
|
rawResponsePreview: input.rawResponsePreview,
|
|
1048
1102
|
modelActionRejected: input.modelActionRejected,
|
|
1049
1103
|
modelActionType: input.modelActionType,
|
|
1050
|
-
validationReason: input.validationReason
|
|
1104
|
+
validationReason: input.validationReason,
|
|
1105
|
+
submissionStatus: input.submissionStatus,
|
|
1106
|
+
structuredProtocolVersion: input.structuredProtocolVersion,
|
|
1107
|
+
retryCount: input.retryCount,
|
|
1108
|
+
firstRawResponseHash: input.firstRawResponseHash,
|
|
1109
|
+
firstModelActionType: input.firstModelActionType,
|
|
1110
|
+
firstValidationReason: input.firstValidationReason,
|
|
1111
|
+
retryRawResponseHash: input.retryRawResponseHash,
|
|
1112
|
+
retryValidationReason: input.retryValidationReason,
|
|
1113
|
+
speech: input.speech,
|
|
1114
|
+
reason: input.reason
|
|
1051
1115
|
});
|
|
1052
1116
|
const response = await input.client.request(frame);
|
|
1053
1117
|
if (response.ok === false) {
|
|
@@ -1123,6 +1187,59 @@ function parseAgentAction(text) {
|
|
|
1123
1187
|
}
|
|
1124
1188
|
return lastError ?? { error: "no_action_block" };
|
|
1125
1189
|
}
|
|
1190
|
+
function parseStrictJsonAgentAction(text) {
|
|
1191
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
1192
|
+
return { error: "no_action_block" };
|
|
1193
|
+
}
|
|
1194
|
+
const trimmed = text.trim();
|
|
1195
|
+
if (!trimmed.startsWith("{")) {
|
|
1196
|
+
return { error: "invalid_json", detail: "strict_json_object_required" };
|
|
1197
|
+
}
|
|
1198
|
+
const objectEnd = topLevelJsonObjectEnd(trimmed);
|
|
1199
|
+
if (objectEnd === null) {
|
|
1200
|
+
return { error: "invalid_json", detail: "strict_json_object_required" };
|
|
1201
|
+
}
|
|
1202
|
+
if (objectEnd !== trimmed.length - 1) {
|
|
1203
|
+
return { error: "invalid_json", detail: "trailing_text_after_json" };
|
|
1204
|
+
}
|
|
1205
|
+
return parseActionJsonStrict(trimmed);
|
|
1206
|
+
}
|
|
1207
|
+
function topLevelJsonObjectEnd(text) {
|
|
1208
|
+
let depth = 0;
|
|
1209
|
+
let inString = false;
|
|
1210
|
+
let escaped = false;
|
|
1211
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
1212
|
+
const ch = text[i];
|
|
1213
|
+
if (inString) {
|
|
1214
|
+
if (escaped) {
|
|
1215
|
+
escaped = false;
|
|
1216
|
+
} else if (ch === "\\") {
|
|
1217
|
+
escaped = true;
|
|
1218
|
+
} else if (ch === '"') {
|
|
1219
|
+
inString = false;
|
|
1220
|
+
}
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
if (ch === '"') {
|
|
1224
|
+
inString = true;
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
if (ch === "{") {
|
|
1228
|
+
depth += 1;
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
if (ch === "}") {
|
|
1232
|
+
depth -= 1;
|
|
1233
|
+
if (depth === 0) {
|
|
1234
|
+
return i;
|
|
1235
|
+
}
|
|
1236
|
+
if (depth < 0) {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return null;
|
|
1242
|
+
}
|
|
1126
1243
|
function parseActionJson(body) {
|
|
1127
1244
|
let obj;
|
|
1128
1245
|
try {
|
|
@@ -1151,7 +1268,38 @@ function parseActionJson(body) {
|
|
|
1151
1268
|
}
|
|
1152
1269
|
return {
|
|
1153
1270
|
actionType: rec.actionType,
|
|
1154
|
-
actionData: rec.actionData
|
|
1271
|
+
actionData: rec.actionData,
|
|
1272
|
+
...typeof rec.speech === "string" ? { speech: rec.speech } : {},
|
|
1273
|
+
...typeof rec.reason === "string" ? { reason: rec.reason } : {}
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
function parseActionJsonStrict(body) {
|
|
1277
|
+
let obj;
|
|
1278
|
+
try {
|
|
1279
|
+
obj = JSON.parse(body);
|
|
1280
|
+
} catch (e) {
|
|
1281
|
+
return { error: "invalid_json", detail: e instanceof Error ? e.message : String(e) };
|
|
1282
|
+
}
|
|
1283
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
|
|
1284
|
+
return { error: "invalid_json", detail: "not an object" };
|
|
1285
|
+
}
|
|
1286
|
+
const rec = obj;
|
|
1287
|
+
for (const field of Object.keys(rec)) {
|
|
1288
|
+
if (!["speech", "reason", "actionType", "actionData"].includes(field)) {
|
|
1289
|
+
return { error: "invalid_json", detail: `unknown_top_level_field:${field}` };
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
if (typeof rec.actionType !== "string" || rec.actionType.length === 0) {
|
|
1293
|
+
return { error: "missing_action_type" };
|
|
1294
|
+
}
|
|
1295
|
+
if (typeof rec.actionData !== "object" || rec.actionData === null || Array.isArray(rec.actionData)) {
|
|
1296
|
+
return { error: "missing_action_data" };
|
|
1297
|
+
}
|
|
1298
|
+
return {
|
|
1299
|
+
actionType: rec.actionType,
|
|
1300
|
+
actionData: rec.actionData,
|
|
1301
|
+
...typeof rec.speech === "string" ? { speech: rec.speech } : {},
|
|
1302
|
+
...typeof rec.reason === "string" ? { reason: rec.reason } : {}
|
|
1155
1303
|
};
|
|
1156
1304
|
}
|
|
1157
1305
|
function removeTrailingCommas(body) {
|
|
@@ -1202,7 +1350,7 @@ function inferRejectedModelAction(rawResponse, task) {
|
|
|
1202
1350
|
validationReason: "no_model_output"
|
|
1203
1351
|
};
|
|
1204
1352
|
}
|
|
1205
|
-
const parsed = parseAgentAction(rawResponse);
|
|
1353
|
+
const parsed = isStructuredJsonTask(task) ? parseStrictJsonAgentAction(rawResponse) : parseAgentAction(rawResponse);
|
|
1206
1354
|
if ("error" in parsed) {
|
|
1207
1355
|
return {
|
|
1208
1356
|
modelActionRejected: true,
|
|
@@ -1620,11 +1768,13 @@ var CoolclawWsClient = class {
|
|
|
1620
1768
|
async connect() {
|
|
1621
1769
|
this.notifyState("connecting");
|
|
1622
1770
|
const lastAckedSeq = await this.options.ackStore.getLastAckedSeq(this.options.accountKey);
|
|
1771
|
+
const capabilities = this.options.capabilities ?? [WEREWOLF_STRUCTURED_ACTION_PROTOCOL_VERSION];
|
|
1623
1772
|
const socket = new WebSocket(buildWsUrl(this.options.gatewayUrl, lastAckedSeq), {
|
|
1624
1773
|
headers: {
|
|
1625
1774
|
Authorization: `Bearer ${this.options.token}`,
|
|
1626
1775
|
"X-CoolClaw-Agent-Id": this.options.agentId,
|
|
1627
|
-
"X-CoolClaw-Plugin-Version": this.options.pluginVersion
|
|
1776
|
+
"X-CoolClaw-Plugin-Version": this.options.pluginVersion,
|
|
1777
|
+
"X-CoolClaw-Capabilities": capabilities.join(",")
|
|
1628
1778
|
}
|
|
1629
1779
|
});
|
|
1630
1780
|
this.socket = socket;
|
|
@@ -1838,6 +1988,42 @@ function logAckFailure(params) {
|
|
|
1838
1988
|
const target = params.target ? ` target=${params.target}` : "";
|
|
1839
1989
|
params.log(`${params.channel} ack cleanup failed${target}: ${String(params.error)}`);
|
|
1840
1990
|
}
|
|
1991
|
+
function buildStructuredActionRetryPrompt(renderedPrompt, reason) {
|
|
1992
|
+
const reasonText = reason && reason.trim().length > 0 ? reason.trim() : "invalid_output";
|
|
1993
|
+
return `${renderedPrompt}
|
|
1994
|
+
|
|
1995
|
+
\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`;
|
|
1997
|
+
}
|
|
1998
|
+
function hasStructuredRetryBudget(deadlineEpochMs, nowEpochMs = Date.now(), safetyMarginMs = 1e3) {
|
|
1999
|
+
if (!deadlineEpochMs || deadlineEpochMs <= 0) {
|
|
2000
|
+
return true;
|
|
2001
|
+
}
|
|
2002
|
+
return nowEpochMs < deadlineEpochMs - Math.max(0, safetyMarginMs);
|
|
2003
|
+
}
|
|
2004
|
+
function shouldSubmitStructuredRejectedOutput(params) {
|
|
2005
|
+
if (!params.structuredTask || params.gameSubmitted) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
if (params.modelActionRejected === false) {
|
|
2009
|
+
return false;
|
|
2010
|
+
}
|
|
2011
|
+
if (params.fallbackReason?.startsWith("llm_action_submit_failed:")) {
|
|
2012
|
+
return false;
|
|
2013
|
+
}
|
|
2014
|
+
return true;
|
|
2015
|
+
}
|
|
2016
|
+
function flattenForLog(text) {
|
|
2017
|
+
return text.replace(/\r/g, "").replace(/\n/g, " \\n ");
|
|
2018
|
+
}
|
|
2019
|
+
function logStructuredActionValidation(params) {
|
|
2020
|
+
const message = `[COOLCLAW-GAME-ACTION-VALIDATE] gameId=${params.meta.gameId} roomId=${params.meta.roomId} eventType=${params.meta.eventType} eventId=${params.meta.eventId} turnSeq=${params.meta.turnSeq} retryCount=${params.retryCount} submissionStatus=${params.submissionStatus} result=${params.result} reason=${params.reason ?? ""} actionType=${params.actionType ?? ""} promptPolicyVersion=${params.meta.promptPolicyVersion ?? ""} renderedPromptHash=${params.meta.renderedPromptHash ?? ""} rawResponseHash=${params.rawResponseHash ?? ""}`;
|
|
2021
|
+
if (params.result === "pass") {
|
|
2022
|
+
params.log?.info?.(message);
|
|
2023
|
+
} else {
|
|
2024
|
+
params.log?.warn?.(message);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
1841
2027
|
function assertInboundRuntimeAvailable(runtime, context) {
|
|
1842
2028
|
if (runtime?.channel) {
|
|
1843
2029
|
return runtime.channel;
|
|
@@ -1852,7 +2038,10 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
|
|
|
1852
2038
|
log?.error?.(`[GAME-ACTION] submit skipped: ws not connected eventId=${meta.eventId}`);
|
|
1853
2039
|
return "failed";
|
|
1854
2040
|
}
|
|
1855
|
-
const responseHash =
|
|
2041
|
+
const responseHash = typeof rawResponse === "string" ? sha256Hex(rawResponse) : void 0;
|
|
2042
|
+
const structured = isStructuredJsonTask(meta.agentTask);
|
|
2043
|
+
const retryCount = auditMeta?.retryCount ?? 0;
|
|
2044
|
+
const structuredProtocolVersion = auditMeta?.structuredProtocolVersion ?? meta.agentTask.actionProtocolVersion;
|
|
1856
2045
|
log?.info?.(
|
|
1857
2046
|
`[GAME-ACTION] submit start source=${source} eventType=${meta.eventType} actionType=${action.actionType} gameId=${meta.gameId} roomId=${meta.roomId} turnSeq=${meta.turnSeq} eventId=${meta.eventId} promptPolicyVersion=${meta.promptPolicyVersion ?? ""} renderedPromptHash=${meta.renderedPromptHash ?? ""} rawResponseHash=${responseHash ?? ""}`
|
|
1858
2047
|
);
|
|
@@ -1873,10 +2062,20 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
|
|
|
1873
2062
|
renderedPromptHash: meta.renderedPromptHash,
|
|
1874
2063
|
parseSource: source,
|
|
1875
2064
|
rawResponseHash: responseHash,
|
|
1876
|
-
rawResponsePreview: rawResponse ? rawResponsePreview(rawResponse) : void 0,
|
|
2065
|
+
rawResponsePreview: typeof rawResponse === "string" ? rawResponsePreview(rawResponse, Number.MAX_SAFE_INTEGER) : void 0,
|
|
1877
2066
|
modelActionRejected: auditMeta?.modelActionRejected,
|
|
1878
2067
|
modelActionType: auditMeta?.modelActionType,
|
|
1879
|
-
validationReason: normalizeAuditText(auditMeta?.validationReason)
|
|
2068
|
+
validationReason: normalizeAuditText(auditMeta?.validationReason),
|
|
2069
|
+
submissionStatus: auditMeta?.submissionStatus ?? (structured ? "VALID" : void 0),
|
|
2070
|
+
structuredProtocolVersion,
|
|
2071
|
+
retryCount: structured ? retryCount : void 0,
|
|
2072
|
+
firstRawResponseHash: auditMeta?.firstRawResponseHash ?? (structured ? responseHash : void 0),
|
|
2073
|
+
firstModelActionType: auditMeta?.firstModelActionType,
|
|
2074
|
+
firstValidationReason: normalizeAuditText(auditMeta?.firstValidationReason),
|
|
2075
|
+
retryRawResponseHash: auditMeta?.retryRawResponseHash,
|
|
2076
|
+
retryValidationReason: normalizeAuditText(auditMeta?.retryValidationReason),
|
|
2077
|
+
speech: action.speech,
|
|
2078
|
+
reason: action.reason
|
|
1880
2079
|
});
|
|
1881
2080
|
if (response.uncertain === true) {
|
|
1882
2081
|
log?.warn?.(
|
|
@@ -1902,6 +2101,128 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
|
|
|
1902
2101
|
return "failed";
|
|
1903
2102
|
}
|
|
1904
2103
|
}
|
|
2104
|
+
async function submitRejectedOutputWithLog(params) {
|
|
2105
|
+
const rawResponse = params.rawResponse ?? "";
|
|
2106
|
+
const rawHash = sha256Hex(rawResponse);
|
|
2107
|
+
const retryCount = params.auditMeta?.retryCount ?? 0;
|
|
2108
|
+
params.log?.warn?.(
|
|
2109
|
+
`[COOLCLAW-GAME-ACTION-VALIDATE] gameId=${params.meta.gameId} roomId=${params.meta.roomId} eventType=${params.meta.eventType} eventId=${params.meta.eventId} turnSeq=${params.meta.turnSeq} retryCount=${retryCount} submissionStatus=REJECTED_OUTPUT result=submit reason=${params.reason} actionType=INVALID_OUTPUT promptPolicyVersion=${params.meta.promptPolicyVersion ?? ""} renderedPromptHash=${params.meta.renderedPromptHash ?? ""} rawResponseHash=${rawHash ?? ""}`
|
|
2110
|
+
);
|
|
2111
|
+
return submitGameActionWithLog(
|
|
2112
|
+
{ actionType: "INVALID_OUTPUT", actionData: {} },
|
|
2113
|
+
params.meta,
|
|
2114
|
+
params.wsClient,
|
|
2115
|
+
params.log,
|
|
2116
|
+
"rejected_output",
|
|
2117
|
+
rawResponse,
|
|
2118
|
+
{
|
|
2119
|
+
modelActionRejected: true,
|
|
2120
|
+
validationReason: params.reason,
|
|
2121
|
+
submissionStatus: "REJECTED_OUTPUT",
|
|
2122
|
+
structuredProtocolVersion: params.meta.agentTask.actionProtocolVersion,
|
|
2123
|
+
retryCount,
|
|
2124
|
+
firstRawResponseHash: params.auditMeta?.firstRawResponseHash ?? rawHash,
|
|
2125
|
+
firstModelActionType: params.auditMeta?.firstModelActionType,
|
|
2126
|
+
firstValidationReason: params.auditMeta?.firstValidationReason,
|
|
2127
|
+
retryRawResponseHash: params.auditMeta?.retryRawResponseHash ?? (retryCount > 0 ? rawHash : void 0),
|
|
2128
|
+
retryValidationReason: params.auditMeta?.retryValidationReason ?? (retryCount > 0 ? params.reason : void 0)
|
|
2129
|
+
}
|
|
2130
|
+
);
|
|
2131
|
+
}
|
|
2132
|
+
async function submitStructuredFinalActionWithLog(params) {
|
|
2133
|
+
const rawResponse = params.rawResponse ?? "";
|
|
2134
|
+
const rawHash = sha256Hex(rawResponse);
|
|
2135
|
+
if (rawResponse.trim().length === 0) {
|
|
2136
|
+
logStructuredActionValidation({
|
|
2137
|
+
log: params.log,
|
|
2138
|
+
meta: params.meta,
|
|
2139
|
+
retryCount: params.retryCount,
|
|
2140
|
+
submissionStatus: "REJECTED_OUTPUT",
|
|
2141
|
+
result: "reject",
|
|
2142
|
+
reason: "no_model_output",
|
|
2143
|
+
rawResponseHash: rawHash
|
|
2144
|
+
});
|
|
2145
|
+
return { submitted: false, reason: "no_model_output", rawHash };
|
|
2146
|
+
}
|
|
2147
|
+
const parsed = parseStrictJsonAgentAction(rawResponse);
|
|
2148
|
+
if ("error" in parsed) {
|
|
2149
|
+
const reason = parsed.error === "invalid_json" ? `invalid_json:${parsed.detail}` : parsed.error;
|
|
2150
|
+
logStructuredActionValidation({
|
|
2151
|
+
log: params.log,
|
|
2152
|
+
meta: params.meta,
|
|
2153
|
+
retryCount: params.retryCount,
|
|
2154
|
+
submissionStatus: "REJECTED_OUTPUT",
|
|
2155
|
+
result: "reject",
|
|
2156
|
+
reason,
|
|
2157
|
+
rawResponseHash: rawHash
|
|
2158
|
+
});
|
|
2159
|
+
return {
|
|
2160
|
+
submitted: false,
|
|
2161
|
+
reason,
|
|
2162
|
+
rawHash
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2165
|
+
const validation = validateAgentAction(parsed, params.meta.agentTask);
|
|
2166
|
+
if (!validation.ok) {
|
|
2167
|
+
logStructuredActionValidation({
|
|
2168
|
+
log: params.log,
|
|
2169
|
+
meta: params.meta,
|
|
2170
|
+
retryCount: params.retryCount,
|
|
2171
|
+
submissionStatus: "REJECTED_OUTPUT",
|
|
2172
|
+
result: "reject",
|
|
2173
|
+
reason: validation.reason,
|
|
2174
|
+
actionType: parsed.actionType,
|
|
2175
|
+
rawResponseHash: rawHash
|
|
2176
|
+
});
|
|
2177
|
+
return {
|
|
2178
|
+
submitted: false,
|
|
2179
|
+
reason: validation.reason,
|
|
2180
|
+
rawHash,
|
|
2181
|
+
modelActionType: parsed.actionType
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
logStructuredActionValidation({
|
|
2185
|
+
log: params.log,
|
|
2186
|
+
meta: params.meta,
|
|
2187
|
+
retryCount: params.retryCount,
|
|
2188
|
+
submissionStatus: "VALID",
|
|
2189
|
+
result: "pass",
|
|
2190
|
+
reason: validation.repairReason,
|
|
2191
|
+
actionType: validation.action.actionType,
|
|
2192
|
+
rawResponseHash: rawHash
|
|
2193
|
+
});
|
|
2194
|
+
const status = await submitGameActionWithLog(
|
|
2195
|
+
validation.action,
|
|
2196
|
+
params.meta,
|
|
2197
|
+
params.wsClient,
|
|
2198
|
+
params.log,
|
|
2199
|
+
"llm",
|
|
2200
|
+
rawResponse,
|
|
2201
|
+
{
|
|
2202
|
+
...params.auditMeta,
|
|
2203
|
+
modelActionRejected: false,
|
|
2204
|
+
modelActionType: validation.action.actionType,
|
|
2205
|
+
validationReason: validation.repairReason,
|
|
2206
|
+
submissionStatus: "VALID",
|
|
2207
|
+
structuredProtocolVersion: params.meta.agentTask.actionProtocolVersion,
|
|
2208
|
+
retryCount: params.retryCount,
|
|
2209
|
+
firstRawResponseHash: params.auditMeta?.firstRawResponseHash ?? rawHash,
|
|
2210
|
+
firstModelActionType: params.auditMeta?.firstModelActionType,
|
|
2211
|
+
firstValidationReason: params.auditMeta?.firstValidationReason,
|
|
2212
|
+
retryRawResponseHash: params.retryCount > 0 ? params.auditMeta?.retryRawResponseHash ?? rawHash : params.auditMeta?.retryRawResponseHash,
|
|
2213
|
+
retryValidationReason: params.retryCount > 0 ? params.auditMeta?.retryValidationReason ?? validation.repairReason : params.auditMeta?.retryValidationReason
|
|
2214
|
+
}
|
|
2215
|
+
);
|
|
2216
|
+
return {
|
|
2217
|
+
submitted: status === "submitted" || status === "uncertain",
|
|
2218
|
+
status,
|
|
2219
|
+
reason: status === "failed" ? `llm_action_submit_failed:${validation.action.actionType}` : void 0,
|
|
2220
|
+
rawHash,
|
|
2221
|
+
modelActionType: validation.action.actionType,
|
|
2222
|
+
validationReason: validation.repairReason,
|
|
2223
|
+
validAction: true
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
1905
2226
|
async function submitBackendFallbackWithLog(params) {
|
|
1906
2227
|
const fb = backendFallbackAction(params.meta.agentTask);
|
|
1907
2228
|
if (!fb) {
|
|
@@ -2114,6 +2435,14 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2114
2435
|
let gameModelActionType;
|
|
2115
2436
|
let gameValidationReason;
|
|
2116
2437
|
let gameModelActionRejected;
|
|
2438
|
+
let gameReplyAttempt = 0;
|
|
2439
|
+
let gameFirstRawResponseHash;
|
|
2440
|
+
let gameFirstModelActionType;
|
|
2441
|
+
let gameFirstValidationReason;
|
|
2442
|
+
let gameRetryRawResponseHash;
|
|
2443
|
+
let gameRetryValidationReason;
|
|
2444
|
+
let gameBaseReplyContext = null;
|
|
2445
|
+
let dispatchGameReply = null;
|
|
2117
2446
|
const gameBuffer = [];
|
|
2118
2447
|
const modelQueryCollector = modelQueryMeta ? createArenaModelQueryReplyCollector({
|
|
2119
2448
|
meta: modelQueryMeta,
|
|
@@ -2163,6 +2492,23 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2163
2492
|
logInboundDrop({ log: ctx.log?.warn?.bind(ctx.log) ?? (() => {
|
|
2164
2493
|
}), channel: productFlavor.channelId, reason: "runtime not available; skipping dispatch" });
|
|
2165
2494
|
if (isGameEvent && gameMeta) {
|
|
2495
|
+
if (isStructuredJsonTask(gameMeta.agentTask)) {
|
|
2496
|
+
const submitted2 = await submitRejectedOutputWithLog({
|
|
2497
|
+
meta: gameMeta,
|
|
2498
|
+
wsClient,
|
|
2499
|
+
log: ctx.log,
|
|
2500
|
+
reason: "runtime_not_available",
|
|
2501
|
+
rawResponse: "",
|
|
2502
|
+
auditMeta: {
|
|
2503
|
+
retryCount: 0,
|
|
2504
|
+
firstValidationReason: "runtime_not_available"
|
|
2505
|
+
}
|
|
2506
|
+
});
|
|
2507
|
+
if (submitted2 === "failed") {
|
|
2508
|
+
throw new Error("game rejected-output submit failed: runtime_not_available");
|
|
2509
|
+
}
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2166
2512
|
const submitted = await submitBackendFallbackWithLog({
|
|
2167
2513
|
meta: gameMeta,
|
|
2168
2514
|
wsClient,
|
|
@@ -2262,6 +2608,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2262
2608
|
WasMentioned: envelope.shouldReply,
|
|
2263
2609
|
Mentioned: envelope.shouldReply
|
|
2264
2610
|
});
|
|
2611
|
+
gameBaseReplyContext = ctxPayload;
|
|
2265
2612
|
const sessionKey = ctxPayload.SessionKey ?? route.sessionKey;
|
|
2266
2613
|
const mainSessionKey = route.mainSessionKey;
|
|
2267
2614
|
await runtimeChannel.session.recordInboundSession({
|
|
@@ -2273,8 +2620,8 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2273
2620
|
ctx.log?.warn(`recordInboundSession failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2274
2621
|
}
|
|
2275
2622
|
});
|
|
2276
|
-
|
|
2277
|
-
ctx:
|
|
2623
|
+
dispatchGameReply = async (replyCtxPayload) => runtimeChannel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
2624
|
+
ctx: replyCtxPayload,
|
|
2278
2625
|
cfg: ctx.cfg,
|
|
2279
2626
|
// 群聊强制走 automatic:CoolClaw 群聊业务语义就是 @ 即回,
|
|
2280
2627
|
// 而 OpenClaw 默认对 group/channel 走 message_tool_only,
|
|
@@ -2298,8 +2645,10 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2298
2645
|
if (isGameEvent && gameMeta) {
|
|
2299
2646
|
if (gameSubmitted) return;
|
|
2300
2647
|
gameBuffer.push(String(payload.text));
|
|
2648
|
+
const structuredTask = isStructuredJsonTask(gameMeta.agentTask);
|
|
2649
|
+
if (structuredTask) return;
|
|
2301
2650
|
const full = gameBuffer.join("");
|
|
2302
|
-
const parsed = parseAgentAction(full);
|
|
2651
|
+
const parsed = structuredTask ? parseStrictJsonAgentAction(full) : parseAgentAction(full);
|
|
2303
2652
|
if ("error" in parsed) return;
|
|
2304
2653
|
const validation = validateAgentAction(parsed, gameMeta.agentTask);
|
|
2305
2654
|
if (!validation.ok) {
|
|
@@ -2307,11 +2656,30 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2307
2656
|
gameModelActionType = parsed.actionType;
|
|
2308
2657
|
gameValidationReason = validation.reason;
|
|
2309
2658
|
gameFallbackReason = validation.reason;
|
|
2659
|
+
if (structuredTask) {
|
|
2660
|
+
const rawHash2 = sha256Hex(full);
|
|
2661
|
+
if (gameReplyAttempt === 0) {
|
|
2662
|
+
gameFirstRawResponseHash = rawHash2;
|
|
2663
|
+
gameFirstModelActionType = parsed.actionType;
|
|
2664
|
+
gameFirstValidationReason = validation.reason;
|
|
2665
|
+
} else {
|
|
2666
|
+
gameRetryRawResponseHash = rawHash2;
|
|
2667
|
+
gameRetryValidationReason = validation.reason;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2310
2670
|
ctx.log?.warn?.(
|
|
2311
|
-
`[GAME-ACTION]
|
|
2671
|
+
`[COOLCLAW-GAME-ACTION-VALIDATE] gameId=${gameMeta.gameId} roomId=${gameMeta.roomId} eventType=${gameMeta.eventType} eventId=${gameMeta.eventId} turnSeq=${gameMeta.turnSeq} retryCount=${gameReplyAttempt} submissionStatus=REJECTED_OUTPUT result=reject reason=${validation.reason} actionType=${parsed.actionType} promptPolicyVersion=${gameMeta.promptPolicyVersion ?? ""} renderedPromptHash=${gameMeta.renderedPromptHash ?? ""} rawResponseHash=${sha256Hex(full)}`
|
|
2312
2672
|
);
|
|
2313
2673
|
return;
|
|
2314
2674
|
}
|
|
2675
|
+
if (structuredTask) {
|
|
2676
|
+
gameModelActionRejected = false;
|
|
2677
|
+
gameModelActionType = validation.action.actionType;
|
|
2678
|
+
gameValidationReason = validation.repairReason;
|
|
2679
|
+
gameFallbackReason = null;
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
const rawHash = sha256Hex(full);
|
|
2315
2683
|
const submitted = await submitGameActionWithLog(
|
|
2316
2684
|
validation.action,
|
|
2317
2685
|
gameMeta,
|
|
@@ -2322,7 +2690,15 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2322
2690
|
{
|
|
2323
2691
|
modelActionRejected: false,
|
|
2324
2692
|
modelActionType: validation.action.actionType,
|
|
2325
|
-
validationReason: validation.repairReason
|
|
2693
|
+
validationReason: validation.repairReason,
|
|
2694
|
+
submissionStatus: structuredTask ? "VALID" : void 0,
|
|
2695
|
+
structuredProtocolVersion: structuredTask ? gameMeta.agentTask.actionProtocolVersion : void 0,
|
|
2696
|
+
retryCount: structuredTask ? gameReplyAttempt : void 0,
|
|
2697
|
+
firstRawResponseHash: structuredTask ? gameFirstRawResponseHash ?? rawHash : void 0,
|
|
2698
|
+
firstModelActionType: structuredTask ? gameFirstModelActionType : void 0,
|
|
2699
|
+
firstValidationReason: structuredTask ? gameFirstValidationReason : void 0,
|
|
2700
|
+
retryRawResponseHash: structuredTask && gameReplyAttempt > 0 ? rawHash : void 0,
|
|
2701
|
+
retryValidationReason: structuredTask && gameReplyAttempt > 0 ? validation.repairReason : void 0
|
|
2326
2702
|
}
|
|
2327
2703
|
);
|
|
2328
2704
|
if (submitted === "submitted" || submitted === "uncertain") {
|
|
@@ -2361,6 +2737,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2361
2737
|
}
|
|
2362
2738
|
}
|
|
2363
2739
|
});
|
|
2740
|
+
await dispatchGameReply(ctxPayload);
|
|
2364
2741
|
if (isGameEvent && gameMeta && !gameSubmitted) {
|
|
2365
2742
|
if (!gameFallbackReason) {
|
|
2366
2743
|
gameFallbackReason = "no_valid_action_in_llm_output";
|
|
@@ -2392,10 +2769,91 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2392
2769
|
gameFallbackReason = `dispatch_error: ${errMsg}`;
|
|
2393
2770
|
}
|
|
2394
2771
|
} finally {
|
|
2772
|
+
const submitFinalStructuredGameAction = async () => {
|
|
2773
|
+
if (!isGameEvent || !gameMeta || gameSubmitted || !isStructuredJsonTask(gameMeta.agentTask)) {
|
|
2774
|
+
return false;
|
|
2775
|
+
}
|
|
2776
|
+
const rawResponse = gameBuffer.join("");
|
|
2777
|
+
const result = await submitStructuredFinalActionWithLog({
|
|
2778
|
+
rawResponse,
|
|
2779
|
+
meta: gameMeta,
|
|
2780
|
+
wsClient,
|
|
2781
|
+
log: ctx.log,
|
|
2782
|
+
retryCount: gameReplyAttempt,
|
|
2783
|
+
auditMeta: {
|
|
2784
|
+
retryCount: gameReplyAttempt,
|
|
2785
|
+
firstRawResponseHash: gameFirstRawResponseHash,
|
|
2786
|
+
firstModelActionType: gameFirstModelActionType,
|
|
2787
|
+
firstValidationReason: gameFirstValidationReason,
|
|
2788
|
+
retryRawResponseHash: gameRetryRawResponseHash,
|
|
2789
|
+
retryValidationReason: gameRetryValidationReason
|
|
2790
|
+
}
|
|
2791
|
+
});
|
|
2792
|
+
if (result.submitted) {
|
|
2793
|
+
gameSubmitted = true;
|
|
2794
|
+
gameModelActionRejected = false;
|
|
2795
|
+
gameModelActionType = result.modelActionType;
|
|
2796
|
+
gameValidationReason = result.validationReason;
|
|
2797
|
+
gameFallbackReason = null;
|
|
2798
|
+
return true;
|
|
2799
|
+
}
|
|
2800
|
+
gameModelActionRejected = result.validAction ? false : true;
|
|
2801
|
+
gameModelActionType = result.modelActionType;
|
|
2802
|
+
gameValidationReason = result.reason ?? result.validationReason;
|
|
2803
|
+
gameFallbackReason = result.reason ?? gameFallbackReason ?? "invalid_output";
|
|
2804
|
+
if (rawResponse.trim().length > 0) {
|
|
2805
|
+
const rawHash = result.rawHash ?? sha256Hex(rawResponse);
|
|
2806
|
+
if (gameReplyAttempt === 0) {
|
|
2807
|
+
gameFirstRawResponseHash = gameFirstRawResponseHash ?? rawHash;
|
|
2808
|
+
gameFirstModelActionType = gameFirstModelActionType ?? result.modelActionType;
|
|
2809
|
+
gameFirstValidationReason = gameFirstValidationReason ?? gameValidationReason;
|
|
2810
|
+
} else {
|
|
2811
|
+
gameRetryRawResponseHash = gameRetryRawResponseHash ?? rawHash;
|
|
2812
|
+
gameRetryValidationReason = gameRetryValidationReason ?? gameValidationReason;
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
return false;
|
|
2816
|
+
};
|
|
2817
|
+
await submitFinalStructuredGameAction();
|
|
2818
|
+
if (isGameEvent && gameMeta && !gameSubmitted && gameReplyAttempt === 0 && isStructuredJsonTask(gameMeta.agentTask) && dispatchGameReply && gameBaseReplyContext && hasStructuredRetryBudget(gameMeta.deadlineEpochMs) && Math.max(0, gameMeta.agentTask.retryPolicy?.maxRetries ?? 0) > 0) {
|
|
2819
|
+
const firstRawResponse = gameBuffer.join("");
|
|
2820
|
+
if (firstRawResponse.trim().length > 0) {
|
|
2821
|
+
const firstInferred = inferRejectedModelAction(firstRawResponse, gameMeta.agentTask);
|
|
2822
|
+
if (gameModelActionRejected === false || firstInferred.validationReason === "valid_action_not_submitted") {
|
|
2823
|
+
ctx.log?.warn?.(
|
|
2824
|
+
`[GAME-ACTION] structured retry skipped for valid unsubmitted action eventId=${gameMeta.eventId} turnSeq=${gameMeta.turnSeq} reason=${gameFallbackReason ?? firstInferred.validationReason ?? "submit_failed"}`
|
|
2825
|
+
);
|
|
2826
|
+
} else {
|
|
2827
|
+
const firstRawHash = sha256Hex(firstRawResponse);
|
|
2828
|
+
gameFirstRawResponseHash = gameFirstRawResponseHash ?? firstRawHash;
|
|
2829
|
+
gameFirstModelActionType = gameFirstModelActionType ?? firstInferred.modelActionType;
|
|
2830
|
+
gameFirstValidationReason = gameFirstValidationReason ?? gameValidationReason ?? firstInferred.validationReason;
|
|
2831
|
+
const retryReason = gameFirstValidationReason ?? firstInferred.validationReason ?? "invalid_output";
|
|
2832
|
+
ctx.log?.warn?.(
|
|
2833
|
+
`[GAME-ACTION] structured retry start eventId=${gameMeta.eventId} turnSeq=${gameMeta.turnSeq} reason=${retryReason} firstRawResponseHash=${firstRawHash}`
|
|
2834
|
+
);
|
|
2835
|
+
gameReplyAttempt = 1;
|
|
2836
|
+
gameBuffer.length = 0;
|
|
2837
|
+
gameFallbackReason = null;
|
|
2838
|
+
gameModelActionRejected = void 0;
|
|
2839
|
+
gameModelActionType = void 0;
|
|
2840
|
+
gameValidationReason = void 0;
|
|
2841
|
+
const retryPrompt = buildStructuredActionRetryPrompt(envelope.text, retryReason);
|
|
2842
|
+
await dispatchGameReply({
|
|
2843
|
+
...gameBaseReplyContext,
|
|
2844
|
+
Body: retryPrompt,
|
|
2845
|
+
BodyForAgent: retryPrompt,
|
|
2846
|
+
RawBody: retryPrompt,
|
|
2847
|
+
CommandBody: retryPrompt
|
|
2848
|
+
});
|
|
2849
|
+
await submitFinalStructuredGameAction();
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2395
2853
|
if (isGameEvent && gameMeta) {
|
|
2396
2854
|
const rawResponse = gameBuffer.join("");
|
|
2397
2855
|
ctx.log?.info?.(
|
|
2398
|
-
`[GAME-TASK] model-output eventId=${gameMeta.eventId} promptPolicyVersion=${gameMeta.promptPolicyVersion ?? ""} renderedPromptHash=${gameMeta.renderedPromptHash ?? ""} rawHash=${rawResponse ? sha256Hex(rawResponse) : ""}
|
|
2856
|
+
`[GAME-TASK] model-output eventId=${gameMeta.eventId} turnSeq=${gameMeta.turnSeq} retryAttempt=${gameReplyAttempt} promptPolicyVersion=${gameMeta.promptPolicyVersion ?? ""} renderedPromptHash=${gameMeta.renderedPromptHash ?? ""} rawHash=${rawResponse ? sha256Hex(rawResponse) : ""} rawResponseText=${rawResponse ? flattenForLog(rawResponse) : ""}`
|
|
2399
2857
|
);
|
|
2400
2858
|
}
|
|
2401
2859
|
if (isGameEvent && gameMeta && !gameSubmitted) {
|
|
@@ -2408,6 +2866,55 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2408
2866
|
if (!gameFallbackReason || gameFallbackReason === "no_valid_action_in_llm_output") {
|
|
2409
2867
|
gameFallbackReason = gameValidationReason ?? inferred.validationReason ?? gameFallbackReason;
|
|
2410
2868
|
}
|
|
2869
|
+
if (isStructuredJsonTask(gameMeta.agentTask)) {
|
|
2870
|
+
if (rawResponse.trim().length === 0) {
|
|
2871
|
+
gameFallbackReason = "no_model_output";
|
|
2872
|
+
gameValidationReason = gameValidationReason ?? "no_model_output";
|
|
2873
|
+
}
|
|
2874
|
+
if (gameReplyAttempt > 0 && rawResponse.trim().length > 0) {
|
|
2875
|
+
gameRetryRawResponseHash = gameRetryRawResponseHash ?? sha256Hex(rawResponse);
|
|
2876
|
+
gameRetryValidationReason = gameRetryValidationReason ?? gameValidationReason ?? inferred.validationReason;
|
|
2877
|
+
}
|
|
2878
|
+
const rawHashForAudit = rawResponse.trim().length > 0 ? sha256Hex(rawResponse) : void 0;
|
|
2879
|
+
const rejectedOutputReason = gameFallbackReason ?? gameValidationReason ?? inferred.validationReason ?? "invalid_json";
|
|
2880
|
+
if (!shouldSubmitStructuredRejectedOutput({
|
|
2881
|
+
structuredTask: true,
|
|
2882
|
+
gameSubmitted,
|
|
2883
|
+
modelActionRejected: gameModelActionRejected,
|
|
2884
|
+
fallbackReason: rejectedOutputReason
|
|
2885
|
+
})) {
|
|
2886
|
+
logStructuredActionValidation({
|
|
2887
|
+
log: ctx.log,
|
|
2888
|
+
meta: gameMeta,
|
|
2889
|
+
retryCount: gameReplyAttempt,
|
|
2890
|
+
submissionStatus: "VALID",
|
|
2891
|
+
result: "submit_failed_no_rejected_output",
|
|
2892
|
+
reason: rejectedOutputReason,
|
|
2893
|
+
actionType: gameModelActionType,
|
|
2894
|
+
rawResponseHash: rawHashForAudit
|
|
2895
|
+
});
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
const submitted2 = await submitRejectedOutputWithLog({
|
|
2899
|
+
meta: gameMeta,
|
|
2900
|
+
wsClient,
|
|
2901
|
+
log: ctx.log,
|
|
2902
|
+
reason: rejectedOutputReason,
|
|
2903
|
+
rawResponse,
|
|
2904
|
+
auditMeta: {
|
|
2905
|
+
retryCount: gameReplyAttempt,
|
|
2906
|
+
firstRawResponseHash: gameFirstRawResponseHash ?? rawHashForAudit,
|
|
2907
|
+
firstModelActionType: gameFirstModelActionType ?? gameModelActionType,
|
|
2908
|
+
firstValidationReason: gameFirstValidationReason ?? gameValidationReason ?? inferred.validationReason,
|
|
2909
|
+
retryRawResponseHash: gameRetryRawResponseHash,
|
|
2910
|
+
retryValidationReason: gameRetryValidationReason
|
|
2911
|
+
}
|
|
2912
|
+
});
|
|
2913
|
+
if (submitted2 === "failed") {
|
|
2914
|
+
throw new Error(`game rejected-output submit failed: ${gameFallbackReason ?? "unknown"}`);
|
|
2915
|
+
}
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2411
2918
|
const submitted = await submitBackendFallbackWithLog({
|
|
2412
2919
|
meta: gameMeta,
|
|
2413
2920
|
wsClient,
|
package/dist/cli-metadata.js
CHANGED
package/dist/index.js
CHANGED
package/dist/setup-entry.js
CHANGED