@coolclaw/coolclaw 1.0.15 → 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.
- package/README.md +19 -150
- package/dist/{chunk-R4H3YAWU.js → chunk-3YKMAM6K.js} +1069 -125
- package/dist/{chunk-RCPRLBZL.js → chunk-RPT3G66A.js} +13 -9
- package/dist/cli-metadata.js +2 -2
- package/dist/index.js +2 -2
- package/dist/setup-entry.js +1 -1
- package/openclaw.plugin.json +6 -6
- package/package.json +3 -19
|
@@ -1,10 +1,68 @@
|
|
|
1
|
+
// flavors/coolclaw.flavor.json
|
|
2
|
+
var coolclaw_flavor_default = {
|
|
3
|
+
productKey: "coolclaw",
|
|
4
|
+
displayName: "CoolClaw",
|
|
5
|
+
npmScope: "@coolclaw",
|
|
6
|
+
channelPackageName: "@coolclaw/coolclaw",
|
|
7
|
+
cliPackageName: "@coolclaw/coolclaw-cli",
|
|
8
|
+
skillsPackageName: "@coolclaw/coolclaw-skills",
|
|
9
|
+
channelId: "coolclaw",
|
|
10
|
+
pluginId: "coolclaw",
|
|
11
|
+
skillName: "coolclaw",
|
|
12
|
+
envPrefix: "COOLCLAW",
|
|
13
|
+
configDirName: "coolclaw",
|
|
14
|
+
defaultGatewayUrl: "https://agits-xa.baidu.com/riddle",
|
|
15
|
+
targetPrefix: "coolclaw"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// flavors/clawtopia.flavor.json
|
|
19
|
+
var clawtopia_flavor_default = {
|
|
20
|
+
productKey: "clawtopia",
|
|
21
|
+
displayName: "Clawtopia",
|
|
22
|
+
npmScope: "@clawtopia",
|
|
23
|
+
channelPackageName: "@clawtopia/clawtopia",
|
|
24
|
+
cliPackageName: "@clawtopia/clawtopia-cli",
|
|
25
|
+
skillsPackageName: "@clawtopia/clawtopia-skills",
|
|
26
|
+
channelId: "clawtopia",
|
|
27
|
+
pluginId: "clawtopia",
|
|
28
|
+
skillName: "clawtopia",
|
|
29
|
+
envPrefix: "CLAWTOPIA",
|
|
30
|
+
configDirName: "clawtopia",
|
|
31
|
+
defaultGatewayUrl: "https://clawtopia.baidu.com/riddle",
|
|
32
|
+
targetPrefix: "clawtopia"
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/flavor-build.ts
|
|
36
|
+
var BUILT_PRODUCT_FLAVOR = "coolclaw";
|
|
37
|
+
|
|
38
|
+
// src/flavor.ts
|
|
39
|
+
var FLAVORS = {
|
|
40
|
+
coolclaw: coolclaw_flavor_default,
|
|
41
|
+
clawtopia: clawtopia_flavor_default
|
|
42
|
+
};
|
|
43
|
+
function getFlavorByKey(key) {
|
|
44
|
+
const flavor = FLAVORS[key];
|
|
45
|
+
if (!flavor) {
|
|
46
|
+
throw new Error(`Unknown PRODUCT_FLAVOR: ${key}`);
|
|
47
|
+
}
|
|
48
|
+
return flavor;
|
|
49
|
+
}
|
|
50
|
+
function activeFlavor(env = process.env) {
|
|
51
|
+
return getFlavorByKey(env.PRODUCT_FLAVOR ?? BUILT_PRODUCT_FLAVOR);
|
|
52
|
+
}
|
|
53
|
+
function resolveFlavor(input) {
|
|
54
|
+
if (!input) return activeFlavor();
|
|
55
|
+
return typeof input === "string" ? getFlavorByKey(input) : input;
|
|
56
|
+
}
|
|
57
|
+
|
|
1
58
|
// src/binding.ts
|
|
2
59
|
import { mkdir, readFile, rename, writeFile, chmod } from "fs/promises";
|
|
3
60
|
import { homedir } from "os";
|
|
4
61
|
import path from "path";
|
|
5
62
|
import { fileURLToPath } from "url";
|
|
6
|
-
function defaultBindingFile(home = homedir()) {
|
|
7
|
-
|
|
63
|
+
function defaultBindingFile(home = homedir(), flavorInput) {
|
|
64
|
+
const flavor = resolveFlavor(flavorInput);
|
|
65
|
+
return path.join(home, ".config", flavor.configDirName, "agent_binding.json");
|
|
8
66
|
}
|
|
9
67
|
async function readTokenRef(tokenRef) {
|
|
10
68
|
if (!tokenRef) return void 0;
|
|
@@ -122,6 +180,7 @@ var FileAckStore = class {
|
|
|
122
180
|
|
|
123
181
|
// src/agent-task.ts
|
|
124
182
|
import { createHash } from "crypto";
|
|
183
|
+
var WEREWOLF_STRUCTURED_ACTION_PROTOCOL_VERSION = "WEREWOLF_STRUCTURED_JSON_MESSAGE_V1";
|
|
125
184
|
function normalizeAgentTask(value) {
|
|
126
185
|
if (!isRecord(value)) return null;
|
|
127
186
|
const actionContract = normalizeActionContract(value.actionContract);
|
|
@@ -133,6 +192,9 @@ function normalizeAgentTask(value) {
|
|
|
133
192
|
renderedPrompt: readString(value.renderedPrompt) ?? "",
|
|
134
193
|
renderedPromptHash: readString(value.renderedPromptHash),
|
|
135
194
|
actionFormat: readString(value.actionFormat),
|
|
195
|
+
actionProtocolVersion: readString(value.actionProtocolVersion),
|
|
196
|
+
outputSchema: isRecord(value.outputSchema) ? value.outputSchema : void 0,
|
|
197
|
+
retryPolicy: normalizeRetryPolicy(value.retryPolicy),
|
|
136
198
|
conversationKey: readString(value.conversationKey),
|
|
137
199
|
actionContract,
|
|
138
200
|
fallbackAction,
|
|
@@ -142,6 +204,9 @@ function normalizeAgentTask(value) {
|
|
|
142
204
|
function isDispatchableAgentTask(task) {
|
|
143
205
|
return task.requiresReply === true && task.renderedPrompt.trim().length > 0 && allowedActionTypes(task).length > 0;
|
|
144
206
|
}
|
|
207
|
+
function isStructuredJsonTask(task) {
|
|
208
|
+
return task.actionProtocolVersion === WEREWOLF_STRUCTURED_ACTION_PROTOCOL_VERSION || task.actionFormat === "STRICT_JSON_OBJECT";
|
|
209
|
+
}
|
|
145
210
|
function validateAgentAction(action, task) {
|
|
146
211
|
if (!isRecord(action.actionData)) {
|
|
147
212
|
return { ok: false, reason: "invalid_action_shape" };
|
|
@@ -158,12 +223,18 @@ function validateAgentAction(action, task) {
|
|
|
158
223
|
if (!matchesActionDataSchema(repaired.actionData, option.actionDataSchema)) {
|
|
159
224
|
return { ok: false, reason: "invalid_action_shape" };
|
|
160
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;
|
|
161
235
|
return {
|
|
162
236
|
ok: true,
|
|
163
|
-
action:
|
|
164
|
-
actionType: action.actionType,
|
|
165
|
-
actionData: repaired.actionData
|
|
166
|
-
},
|
|
237
|
+
action: normalizedAction,
|
|
167
238
|
repairReason: repaired.repairReason
|
|
168
239
|
};
|
|
169
240
|
}
|
|
@@ -193,6 +264,14 @@ function normalizeActionContract(value) {
|
|
|
193
264
|
finalOutputRules: Array.isArray(value.finalOutputRules) ? value.finalOutputRules.filter((rule) => typeof rule === "string") : void 0
|
|
194
265
|
};
|
|
195
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
|
+
}
|
|
196
275
|
function normalizeActionOption(value) {
|
|
197
276
|
if (!isRecord(value) || typeof value.actionType !== "string" || value.actionType.length === 0) {
|
|
198
277
|
return [];
|
|
@@ -208,20 +287,17 @@ function allowedActionTypes(task) {
|
|
|
208
287
|
}
|
|
209
288
|
function matchesActionDataSchema(actionData, schema) {
|
|
210
289
|
if (!schema || Object.keys(schema).length === 0) return true;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
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);
|
|
225
301
|
}
|
|
226
302
|
function repairActionDataForSchema(actionData, schema) {
|
|
227
303
|
if (!schema || Object.keys(schema).length === 0) {
|
|
@@ -257,12 +333,28 @@ function repairActionDataForSchema(actionData, schema) {
|
|
|
257
333
|
};
|
|
258
334
|
}
|
|
259
335
|
function matchesSchemaValue(value, schema, required) {
|
|
260
|
-
if (value == null) return !required;
|
|
261
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
|
+
}
|
|
262
344
|
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
263
345
|
return false;
|
|
264
346
|
}
|
|
265
|
-
|
|
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) {
|
|
266
358
|
case "integer":
|
|
267
359
|
return typeof value === "number" && Number.isInteger(value);
|
|
268
360
|
case "number":
|
|
@@ -272,13 +364,33 @@ function matchesSchemaValue(value, schema, required) {
|
|
|
272
364
|
case "boolean":
|
|
273
365
|
return typeof value === "boolean";
|
|
274
366
|
case "object":
|
|
275
|
-
return
|
|
367
|
+
return matchesObjectSchema(value, schema);
|
|
276
368
|
case "array":
|
|
277
369
|
return Array.isArray(value);
|
|
370
|
+
case "null":
|
|
371
|
+
return value == null;
|
|
278
372
|
default:
|
|
279
373
|
return true;
|
|
280
374
|
}
|
|
281
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
|
+
}
|
|
282
394
|
function readString(value) {
|
|
283
395
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
284
396
|
}
|
|
@@ -358,14 +470,19 @@ function isRecord2(value) {
|
|
|
358
470
|
// src/inbound.ts
|
|
359
471
|
var ARENA_REPORT_SHARE_NOTIFY_TYPE = "ARENA_REPORT_SHARE_REQUEST";
|
|
360
472
|
var ARENA_MODEL_QUERY_NOTIFY_TYPE = "ARENA_MODEL_QUERY_REQUEST";
|
|
473
|
+
var ARENA_VOICE_SELECT_NOTIFY_TYPE = "ARENA_VOICE_SELECT_REQUEST";
|
|
361
474
|
var REPORT_SHARE_DEDUPE_LIMIT = 500;
|
|
362
475
|
var MODEL_QUERY_DEDUPE_LIMIT = 500;
|
|
476
|
+
var VOICE_SELECT_DEDUPE_LIMIT = 500;
|
|
363
477
|
var processedArenaReportShareEventIds = /* @__PURE__ */ new Set();
|
|
364
478
|
var processedArenaReportShareEventOrder = [];
|
|
365
479
|
var inFlightArenaReportShareEventIds = /* @__PURE__ */ new Map();
|
|
366
480
|
var processedArenaModelQueryEventIds = /* @__PURE__ */ new Set();
|
|
367
481
|
var processedArenaModelQueryEventOrder = [];
|
|
368
482
|
var inFlightArenaModelQueryEventIds = /* @__PURE__ */ new Map();
|
|
483
|
+
var processedArenaVoiceSelectEventIds = /* @__PURE__ */ new Set();
|
|
484
|
+
var processedArenaVoiceSelectEventOrder = [];
|
|
485
|
+
var inFlightArenaVoiceSelectEventIds = /* @__PURE__ */ new Map();
|
|
369
486
|
function mapInboundFrame(frame) {
|
|
370
487
|
if (frame.type === "PRIVATE_MESSAGE") {
|
|
371
488
|
const payload = assertPrivatePayload(frame.payload);
|
|
@@ -441,6 +558,13 @@ async function handleInboundFrame(input) {
|
|
|
441
558
|
inFlight: inFlightArenaModelQueryEventIds,
|
|
442
559
|
remember: rememberArenaModelQueryEventId
|
|
443
560
|
};
|
|
561
|
+
} else if (isArenaVoiceSelectEnvelope(envelope)) {
|
|
562
|
+
dedupeState = {
|
|
563
|
+
eventId: String(envelope.metadata.eventId),
|
|
564
|
+
processed: processedArenaVoiceSelectEventIds,
|
|
565
|
+
inFlight: inFlightArenaVoiceSelectEventIds,
|
|
566
|
+
remember: rememberArenaVoiceSelectEventId
|
|
567
|
+
};
|
|
444
568
|
}
|
|
445
569
|
if (dedupeState) {
|
|
446
570
|
if (dedupeState.processed.has(dedupeState.eventId)) {
|
|
@@ -493,6 +617,9 @@ function isArenaReportShareEnvelope(envelope) {
|
|
|
493
617
|
function isArenaModelQueryEnvelope(envelope) {
|
|
494
618
|
return envelope.metadata?.arenaModelQueryRequest === true && typeof envelope.metadata.eventId === "string" && envelope.metadata.eventId.length > 0 && typeof envelope.metadata.callbackUrl === "string" && envelope.metadata.callbackUrl.length > 0;
|
|
495
619
|
}
|
|
620
|
+
function isArenaVoiceSelectEnvelope(envelope) {
|
|
621
|
+
return envelope.metadata?.arenaVoiceSelectRequest === true && typeof envelope.metadata.eventId === "string" && envelope.metadata.eventId.length > 0 && typeof envelope.metadata.callbackUrl === "string" && envelope.metadata.callbackUrl.length > 0;
|
|
622
|
+
}
|
|
496
623
|
function mapNotificationFrame(frame) {
|
|
497
624
|
const payload = isRecord3(frame.payload) ? frame.payload : {};
|
|
498
625
|
const seq = typeof payload.seq === "number" ? payload.seq : void 0;
|
|
@@ -534,6 +661,9 @@ function mapNotificationFrame(frame) {
|
|
|
534
661
|
if (notifyType === ARENA_MODEL_QUERY_NOTIFY_TYPE) {
|
|
535
662
|
return mapArenaModelQueryFrame(frame, payload, seq);
|
|
536
663
|
}
|
|
664
|
+
if (notifyType === ARENA_VOICE_SELECT_NOTIFY_TYPE) {
|
|
665
|
+
return mapArenaVoiceSelectFrame(frame, payload, seq);
|
|
666
|
+
}
|
|
537
667
|
const postId = payload.postId != null ? String(payload.postId) : "";
|
|
538
668
|
return {
|
|
539
669
|
id: frame.id,
|
|
@@ -548,6 +678,43 @@ function mapNotificationFrame(frame) {
|
|
|
548
678
|
}
|
|
549
679
|
return null;
|
|
550
680
|
}
|
|
681
|
+
function mapArenaVoiceSelectFrame(frame, payload, seq) {
|
|
682
|
+
const voicePayload = isRecord3(payload.payload) ? payload.payload : {};
|
|
683
|
+
const eventId = firstText(payload.eventId, voicePayload.eventId) ?? frame.id;
|
|
684
|
+
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
685
|
+
const roomId = Number(voicePayload.roomId ?? 0);
|
|
686
|
+
const seatNumber = Number(voicePayload.seatNumber ?? 0);
|
|
687
|
+
const seatEpoch = voicePayload.seatEpoch != null ? String(voicePayload.seatEpoch) : "";
|
|
688
|
+
const callbackUrl = typeof voicePayload.callbackUrl === "string" ? voicePayload.callbackUrl : "";
|
|
689
|
+
if (!callbackUrl || !roomId || !seatNumber || !seatEpoch) {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
const deadlineEpochMs = Number(voicePayload.deadlineEpochMs ?? 0);
|
|
693
|
+
const voiceOptions = Array.isArray(voicePayload.voiceOptions) ? voicePayload.voiceOptions.filter(isRecord3).map((option) => option) : [];
|
|
694
|
+
return {
|
|
695
|
+
id: eventId,
|
|
696
|
+
channel: "coolclaw",
|
|
697
|
+
conversationId: `notification:arena_voice_select:${eventId}`,
|
|
698
|
+
text: "/arena-voice-select",
|
|
699
|
+
messageType: frame.type,
|
|
700
|
+
seq,
|
|
701
|
+
shouldReply: true,
|
|
702
|
+
metadata: {
|
|
703
|
+
sourceFrameId: frame.id,
|
|
704
|
+
payload: frame.payload,
|
|
705
|
+
voiceSelectPayload: voicePayload,
|
|
706
|
+
arenaVoiceSelectRequest: true,
|
|
707
|
+
eventId,
|
|
708
|
+
traceId,
|
|
709
|
+
roomId,
|
|
710
|
+
seatNumber,
|
|
711
|
+
seatEpoch,
|
|
712
|
+
callbackUrl,
|
|
713
|
+
deadlineEpochMs,
|
|
714
|
+
voiceOptions
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
551
718
|
function mapArenaModelQueryFrame(frame, payload, seq) {
|
|
552
719
|
const eventId = typeof payload.eventId === "string" && payload.eventId.length > 0 ? payload.eventId : frame.id;
|
|
553
720
|
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
@@ -583,6 +750,14 @@ function mapArenaModelQueryFrame(frame, payload, seq) {
|
|
|
583
750
|
}
|
|
584
751
|
};
|
|
585
752
|
}
|
|
753
|
+
function firstText(...values) {
|
|
754
|
+
for (const value of values) {
|
|
755
|
+
if (typeof value === "string" && value.length > 0) {
|
|
756
|
+
return value;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return null;
|
|
760
|
+
}
|
|
586
761
|
function mapArenaReportShareFrame(frame, payload, seq) {
|
|
587
762
|
const eventId = typeof payload.eventId === "string" && payload.eventId.length > 0 ? payload.eventId : frame.id;
|
|
588
763
|
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
@@ -624,6 +799,15 @@ function rememberArenaModelQueryEventId(eventId) {
|
|
|
624
799
|
if (expired) processedArenaModelQueryEventIds.delete(expired);
|
|
625
800
|
}
|
|
626
801
|
}
|
|
802
|
+
function rememberArenaVoiceSelectEventId(eventId) {
|
|
803
|
+
if (processedArenaVoiceSelectEventIds.has(eventId)) return;
|
|
804
|
+
processedArenaVoiceSelectEventIds.add(eventId);
|
|
805
|
+
processedArenaVoiceSelectEventOrder.push(eventId);
|
|
806
|
+
while (processedArenaVoiceSelectEventOrder.length > VOICE_SELECT_DEDUPE_LIMIT) {
|
|
807
|
+
const expired = processedArenaVoiceSelectEventOrder.shift();
|
|
808
|
+
if (expired) processedArenaVoiceSelectEventIds.delete(expired);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
627
811
|
function mapGameEventFrame(frame, payload) {
|
|
628
812
|
const eventType = typeof payload.eventType === "string" ? payload.eventType : "UNKNOWN";
|
|
629
813
|
const agentTask = normalizeAgentTask(payload.agentTask);
|
|
@@ -782,15 +966,16 @@ var TargetParseError = class extends Error {
|
|
|
782
966
|
this.name = "TargetParseError";
|
|
783
967
|
}
|
|
784
968
|
};
|
|
785
|
-
function parseCoolclawTarget(raw) {
|
|
786
|
-
const
|
|
969
|
+
function parseCoolclawTarget(raw, flavorInput) {
|
|
970
|
+
const flavor = resolveFlavor(flavorInput);
|
|
971
|
+
const normalized = normalizeCoolclawTarget(raw, flavor);
|
|
787
972
|
const parts = normalized.split(":");
|
|
788
973
|
if (parts.length !== 3) {
|
|
789
|
-
throw new TargetParseError(`Invalid
|
|
974
|
+
throw new TargetParseError(`Invalid ${flavor.displayName} target: ${raw}`);
|
|
790
975
|
}
|
|
791
976
|
const [channel, type, id] = parts;
|
|
792
|
-
if (channel !==
|
|
793
|
-
throw new TargetParseError(`Invalid
|
|
977
|
+
if (channel !== flavor.targetPrefix || id.length === 0) {
|
|
978
|
+
throw new TargetParseError(`Invalid ${flavor.displayName} target: ${raw}`);
|
|
794
979
|
}
|
|
795
980
|
if (type === "human") {
|
|
796
981
|
return { kind: "private", userType: "HUMAN", userId: id };
|
|
@@ -801,9 +986,10 @@ function parseCoolclawTarget(raw) {
|
|
|
801
986
|
if (type === "group") {
|
|
802
987
|
return { kind: "group", groupId: id };
|
|
803
988
|
}
|
|
804
|
-
throw new TargetParseError(`Invalid
|
|
989
|
+
throw new TargetParseError(`Invalid ${flavor.displayName} target type: ${type}`);
|
|
805
990
|
}
|
|
806
|
-
function normalizeCoolclawTarget(raw) {
|
|
991
|
+
function normalizeCoolclawTarget(raw, flavorInput) {
|
|
992
|
+
resolveFlavor(flavorInput);
|
|
807
993
|
const trimmed = raw.trim();
|
|
808
994
|
const parts = trimmed.split(":");
|
|
809
995
|
if (parts.length !== 3) {
|
|
@@ -812,25 +998,27 @@ function normalizeCoolclawTarget(raw) {
|
|
|
812
998
|
const [channel, type, id] = parts;
|
|
813
999
|
return `${channel.toLowerCase()}:${type.toLowerCase()}:${id.trim()}`;
|
|
814
1000
|
}
|
|
815
|
-
function inferCoolclawTargetChatType(raw) {
|
|
1001
|
+
function inferCoolclawTargetChatType(raw, flavorInput) {
|
|
816
1002
|
try {
|
|
817
|
-
const target = parseCoolclawTarget(raw);
|
|
1003
|
+
const target = parseCoolclawTarget(raw, flavorInput);
|
|
818
1004
|
return target.kind === "private" ? "direct" : "group";
|
|
819
1005
|
} catch {
|
|
820
1006
|
return void 0;
|
|
821
1007
|
}
|
|
822
1008
|
}
|
|
823
|
-
function isCoolclawTargetId(raw, normalized = normalizeCoolclawTarget(raw)) {
|
|
1009
|
+
function isCoolclawTargetId(raw, normalized = normalizeCoolclawTarget(raw), flavorInput) {
|
|
1010
|
+
const flavor = resolveFlavor(flavorInput);
|
|
824
1011
|
try {
|
|
825
|
-
parseCoolclawTarget(normalized);
|
|
826
|
-
return normalized.startsWith(
|
|
1012
|
+
parseCoolclawTarget(normalized, flavor);
|
|
1013
|
+
return normalized.startsWith(`${flavor.targetPrefix}:`);
|
|
827
1014
|
} catch {
|
|
828
1015
|
return false;
|
|
829
1016
|
}
|
|
830
1017
|
}
|
|
831
|
-
async function resolveCoolclawMessagingTarget(raw, preferredKind) {
|
|
832
|
-
const
|
|
833
|
-
const
|
|
1018
|
+
async function resolveCoolclawMessagingTarget(raw, preferredKind, flavorInput) {
|
|
1019
|
+
const flavor = resolveFlavor(flavorInput);
|
|
1020
|
+
const normalized = normalizeCoolclawTarget(raw, flavor);
|
|
1021
|
+
const target = parseCoolclawTarget(normalized, flavor);
|
|
834
1022
|
const kind = target.kind === "private" ? "user" : "group";
|
|
835
1023
|
if (preferredKind && preferredKind !== kind) {
|
|
836
1024
|
return null;
|
|
@@ -913,7 +1101,17 @@ async function sendGameAction(input) {
|
|
|
913
1101
|
rawResponsePreview: input.rawResponsePreview,
|
|
914
1102
|
modelActionRejected: input.modelActionRejected,
|
|
915
1103
|
modelActionType: input.modelActionType,
|
|
916
|
-
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
|
|
917
1115
|
});
|
|
918
1116
|
const response = await input.client.request(frame);
|
|
919
1117
|
if (response.ok === false) {
|
|
@@ -989,6 +1187,59 @@ function parseAgentAction(text) {
|
|
|
989
1187
|
}
|
|
990
1188
|
return lastError ?? { error: "no_action_block" };
|
|
991
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
|
+
}
|
|
992
1243
|
function parseActionJson(body) {
|
|
993
1244
|
let obj;
|
|
994
1245
|
try {
|
|
@@ -1017,7 +1268,38 @@ function parseActionJson(body) {
|
|
|
1017
1268
|
}
|
|
1018
1269
|
return {
|
|
1019
1270
|
actionType: rec.actionType,
|
|
1020
|
-
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 } : {}
|
|
1021
1303
|
};
|
|
1022
1304
|
}
|
|
1023
1305
|
function removeTrailingCommas(body) {
|
|
@@ -1068,7 +1350,7 @@ function inferRejectedModelAction(rawResponse, task) {
|
|
|
1068
1350
|
validationReason: "no_model_output"
|
|
1069
1351
|
};
|
|
1070
1352
|
}
|
|
1071
|
-
const parsed = parseAgentAction(rawResponse);
|
|
1353
|
+
const parsed = isStructuredJsonTask(task) ? parseStrictJsonAgentAction(rawResponse) : parseAgentAction(rawResponse);
|
|
1072
1354
|
if ("error" in parsed) {
|
|
1073
1355
|
return {
|
|
1074
1356
|
modelActionRejected: true,
|
|
@@ -1207,6 +1489,223 @@ function isRecord4(value) {
|
|
|
1207
1489
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1208
1490
|
}
|
|
1209
1491
|
|
|
1492
|
+
// src/arena-voice-select.ts
|
|
1493
|
+
var FALLBACK_REASON = "\u672A\u89E3\u6790\u5230\u6A21\u578B\u97F3\u8272\u504F\u597D\uFF0C\u4EA4\u7531\u540E\u7AEF\u515C\u5E95\u9009\u62E9\u3002";
|
|
1494
|
+
var DEFAULT_REASON = "\u57FA\u4E8E\u6A21\u578B\u8F93\u51FA\u9009\u62E9\u5019\u9009\u73A9\u5BB6\u97F3\u8272\u3002";
|
|
1495
|
+
function parseArenaVoiceSelection(rawText, voiceOptions) {
|
|
1496
|
+
const structured = parseStructuredVoiceSelection(rawText, voiceOptions);
|
|
1497
|
+
if (structured) return structured;
|
|
1498
|
+
const topVoiceIds = extractVoiceIdsFromText(rawText, voiceOptions);
|
|
1499
|
+
return {
|
|
1500
|
+
topVoiceIds,
|
|
1501
|
+
reason: topVoiceIds.length > 0 ? DEFAULT_REASON : FALLBACK_REASON
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
async function submitArenaVoiceSelectCallback(input) {
|
|
1505
|
+
const start = Date.now();
|
|
1506
|
+
const rawHash = input.rawText ? sha256Hex(input.rawText) : "";
|
|
1507
|
+
const rawPreview = rawResponsePreview(input.rawText) ?? "";
|
|
1508
|
+
const selection = parseArenaVoiceSelection(input.rawText, input.meta.voiceOptions);
|
|
1509
|
+
try {
|
|
1510
|
+
if (!input.meta.callbackUrl || !input.token) {
|
|
1511
|
+
input.log?.warn?.(
|
|
1512
|
+
`[ARENA-VOICE] callback skipped eventId=${input.meta.eventId} reason=missing_callback_or_token rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1513
|
+
);
|
|
1514
|
+
return { ok: false, error: "missing_callback_or_token" };
|
|
1515
|
+
}
|
|
1516
|
+
const fetchImpl = input.fetchImpl ?? globalThis.fetch;
|
|
1517
|
+
if (typeof fetchImpl !== "function") {
|
|
1518
|
+
input.log?.warn?.(`[ARENA-VOICE] callback skipped eventId=${input.meta.eventId} reason=fetch_unavailable`);
|
|
1519
|
+
return { ok: false, error: "fetch_unavailable" };
|
|
1520
|
+
}
|
|
1521
|
+
const response = await fetchImpl(input.meta.callbackUrl, {
|
|
1522
|
+
method: "POST",
|
|
1523
|
+
headers: {
|
|
1524
|
+
Authorization: `Bearer ${input.token}`,
|
|
1525
|
+
"Content-Type": "application/json"
|
|
1526
|
+
},
|
|
1527
|
+
body: JSON.stringify({
|
|
1528
|
+
eventId: input.meta.eventId,
|
|
1529
|
+
roomId: input.meta.roomId,
|
|
1530
|
+
seatNumber: input.meta.seatNumber,
|
|
1531
|
+
seatEpoch: input.meta.seatEpoch,
|
|
1532
|
+
topVoiceIds: selection.topVoiceIds,
|
|
1533
|
+
reason: selection.reason
|
|
1534
|
+
})
|
|
1535
|
+
});
|
|
1536
|
+
const body = await readJsonObject2(response);
|
|
1537
|
+
const data = isRecord5(body.data) ? body.data : body;
|
|
1538
|
+
const accepted = data.accepted === true;
|
|
1539
|
+
const result = { ok: response.ok, status: response.status, accepted };
|
|
1540
|
+
const level = response.ok ? "info" : "warn";
|
|
1541
|
+
input.log?.[level]?.(
|
|
1542
|
+
`[ARENA-VOICE] callback status=${response.status} accepted=${accepted} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} selected=${selection.topVoiceIds.length} elapsedMs=${Date.now() - start} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1543
|
+
);
|
|
1544
|
+
return result;
|
|
1545
|
+
} catch (error) {
|
|
1546
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1547
|
+
input.log?.warn?.(
|
|
1548
|
+
`[ARENA-VOICE] callback failed eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} elapsedMs=${Date.now() - start} err=${message} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1549
|
+
);
|
|
1550
|
+
return { ok: false, error: message };
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
function createArenaVoiceSelectReplyCollector(input) {
|
|
1554
|
+
const blocks = [];
|
|
1555
|
+
let submitted = false;
|
|
1556
|
+
const submit = input.submit ?? ((rawText) => submitArenaVoiceSelectCallback({
|
|
1557
|
+
meta: input.meta,
|
|
1558
|
+
token: input.token,
|
|
1559
|
+
rawText,
|
|
1560
|
+
log: input.log
|
|
1561
|
+
}));
|
|
1562
|
+
async function submitOnce(rawText, reason) {
|
|
1563
|
+
if (submitted) return;
|
|
1564
|
+
submitted = true;
|
|
1565
|
+
const parsed = parseArenaVoiceSelection(rawText, input.meta.voiceOptions);
|
|
1566
|
+
input.log?.info?.(
|
|
1567
|
+
`[ARENA-VOICE] dispatch submit reason=${reason} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} selected=${parsed.topVoiceIds.length}`
|
|
1568
|
+
);
|
|
1569
|
+
await submit(rawText);
|
|
1570
|
+
}
|
|
1571
|
+
return {
|
|
1572
|
+
async deliver(text) {
|
|
1573
|
+
if (submitted || !text) return;
|
|
1574
|
+
blocks.push(text);
|
|
1575
|
+
const full = blocks.join("");
|
|
1576
|
+
const structured = parseStructuredVoiceSelection(full, input.meta.voiceOptions);
|
|
1577
|
+
input.log?.info?.(
|
|
1578
|
+
`[ARENA-VOICE] dispatch block eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} parsed=${structured?.topVoiceIds.length ? "true" : "false"}`
|
|
1579
|
+
);
|
|
1580
|
+
if (structured?.topVoiceIds.length) {
|
|
1581
|
+
await submitOnce(full, "parsed");
|
|
1582
|
+
}
|
|
1583
|
+
},
|
|
1584
|
+
async finalize() {
|
|
1585
|
+
if (submitted) return;
|
|
1586
|
+
await submitOnce(blocks.join(""), "final");
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function parseStructuredVoiceSelection(rawText, voiceOptions) {
|
|
1591
|
+
if (!rawText) return null;
|
|
1592
|
+
const body = parseFirstJsonObject(rawText);
|
|
1593
|
+
if (!body) return null;
|
|
1594
|
+
const rawIds = Array.isArray(body.topVoiceIds) ? body.topVoiceIds : [];
|
|
1595
|
+
const topVoiceIds = sanitizeVoiceIds(rawIds, voiceOptions);
|
|
1596
|
+
const reason = normalizeReason(body.reason);
|
|
1597
|
+
if (topVoiceIds.length === 0 && !reason) return null;
|
|
1598
|
+
return {
|
|
1599
|
+
topVoiceIds,
|
|
1600
|
+
reason: reason || (topVoiceIds.length > 0 ? DEFAULT_REASON : FALLBACK_REASON)
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
function extractVoiceIdsFromText(rawText, voiceOptions) {
|
|
1604
|
+
if (!rawText) return [];
|
|
1605
|
+
const allowed = allowedVoiceIds(voiceOptions);
|
|
1606
|
+
const result = [];
|
|
1607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1608
|
+
const regex = /\b\d{3,8}\b/g;
|
|
1609
|
+
for (const match of rawText.matchAll(regex)) {
|
|
1610
|
+
const voiceId = match[0];
|
|
1611
|
+
if (!allowed.has(voiceId) || seen.has(voiceId)) continue;
|
|
1612
|
+
seen.add(voiceId);
|
|
1613
|
+
result.push(voiceId);
|
|
1614
|
+
if (result.length >= 3) break;
|
|
1615
|
+
}
|
|
1616
|
+
return result;
|
|
1617
|
+
}
|
|
1618
|
+
function sanitizeVoiceIds(rawIds, voiceOptions) {
|
|
1619
|
+
const allowed = allowedVoiceIds(voiceOptions);
|
|
1620
|
+
const result = [];
|
|
1621
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1622
|
+
for (const rawId of rawIds) {
|
|
1623
|
+
const voiceId = typeof rawId === "string" ? rawId.trim() : "";
|
|
1624
|
+
if (!voiceId || !allowed.has(voiceId) || seen.has(voiceId)) continue;
|
|
1625
|
+
seen.add(voiceId);
|
|
1626
|
+
result.push(voiceId);
|
|
1627
|
+
if (result.length >= 3) break;
|
|
1628
|
+
}
|
|
1629
|
+
return result;
|
|
1630
|
+
}
|
|
1631
|
+
function allowedVoiceIds(voiceOptions) {
|
|
1632
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1633
|
+
if (!Array.isArray(voiceOptions)) return ids;
|
|
1634
|
+
for (const option of voiceOptions) {
|
|
1635
|
+
const voiceId = typeof option?.voiceId === "string" ? option.voiceId.trim() : "";
|
|
1636
|
+
if (voiceId) ids.add(voiceId);
|
|
1637
|
+
}
|
|
1638
|
+
return ids;
|
|
1639
|
+
}
|
|
1640
|
+
function normalizeReason(value) {
|
|
1641
|
+
if (typeof value !== "string") return "";
|
|
1642
|
+
return value.replace(/\s+/g, " ").trim().slice(0, 160);
|
|
1643
|
+
}
|
|
1644
|
+
function parseFirstJsonObject(rawText) {
|
|
1645
|
+
const direct = tryParseJsonObject(rawText.trim());
|
|
1646
|
+
if (direct) return direct;
|
|
1647
|
+
const candidates = extractJsonObjectCandidates(rawText);
|
|
1648
|
+
for (const candidate of candidates) {
|
|
1649
|
+
const parsed = tryParseJsonObject(candidate);
|
|
1650
|
+
if (parsed) return parsed;
|
|
1651
|
+
}
|
|
1652
|
+
return null;
|
|
1653
|
+
}
|
|
1654
|
+
function extractJsonObjectCandidates(rawText) {
|
|
1655
|
+
const candidates = [];
|
|
1656
|
+
let start = -1;
|
|
1657
|
+
let depth = 0;
|
|
1658
|
+
let inString = false;
|
|
1659
|
+
let escaped = false;
|
|
1660
|
+
for (let index = 0; index < rawText.length; index += 1) {
|
|
1661
|
+
const char = rawText[index];
|
|
1662
|
+
if (inString) {
|
|
1663
|
+
if (escaped) {
|
|
1664
|
+
escaped = false;
|
|
1665
|
+
} else if (char === "\\") {
|
|
1666
|
+
escaped = true;
|
|
1667
|
+
} else if (char === '"') {
|
|
1668
|
+
inString = false;
|
|
1669
|
+
}
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
if (char === '"') {
|
|
1673
|
+
inString = true;
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1676
|
+
if (char === "{") {
|
|
1677
|
+
if (depth === 0) start = index;
|
|
1678
|
+
depth += 1;
|
|
1679
|
+
} else if (char === "}" && depth > 0) {
|
|
1680
|
+
depth -= 1;
|
|
1681
|
+
if (depth === 0 && start >= 0) {
|
|
1682
|
+
candidates.push(rawText.slice(start, index + 1));
|
|
1683
|
+
start = -1;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return candidates;
|
|
1688
|
+
}
|
|
1689
|
+
async function readJsonObject2(response) {
|
|
1690
|
+
try {
|
|
1691
|
+
const body = await response.json();
|
|
1692
|
+
return isRecord5(body) ? body : {};
|
|
1693
|
+
} catch {
|
|
1694
|
+
return {};
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
function isRecord5(value) {
|
|
1698
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1699
|
+
}
|
|
1700
|
+
function tryParseJsonObject(value) {
|
|
1701
|
+
try {
|
|
1702
|
+
const parsed = JSON.parse(value);
|
|
1703
|
+
return isRecord5(parsed) ? parsed : null;
|
|
1704
|
+
} catch {
|
|
1705
|
+
return null;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1210
1709
|
// src/ws-client.ts
|
|
1211
1710
|
import WebSocket from "ws";
|
|
1212
1711
|
var CoolclawWsClient = class {
|
|
@@ -1269,11 +1768,13 @@ var CoolclawWsClient = class {
|
|
|
1269
1768
|
async connect() {
|
|
1270
1769
|
this.notifyState("connecting");
|
|
1271
1770
|
const lastAckedSeq = await this.options.ackStore.getLastAckedSeq(this.options.accountKey);
|
|
1771
|
+
const capabilities = this.options.capabilities ?? [WEREWOLF_STRUCTURED_ACTION_PROTOCOL_VERSION];
|
|
1272
1772
|
const socket = new WebSocket(buildWsUrl(this.options.gatewayUrl, lastAckedSeq), {
|
|
1273
1773
|
headers: {
|
|
1274
1774
|
Authorization: `Bearer ${this.options.token}`,
|
|
1275
1775
|
"X-CoolClaw-Agent-Id": this.options.agentId,
|
|
1276
|
-
"X-CoolClaw-Plugin-Version": this.options.pluginVersion
|
|
1776
|
+
"X-CoolClaw-Plugin-Version": this.options.pluginVersion,
|
|
1777
|
+
"X-CoolClaw-Capabilities": capabilities.join(",")
|
|
1277
1778
|
}
|
|
1278
1779
|
});
|
|
1279
1780
|
this.socket = socket;
|
|
@@ -1419,18 +1920,18 @@ var CoolclawWsClient = class {
|
|
|
1419
1920
|
}
|
|
1420
1921
|
};
|
|
1421
1922
|
function readPingInterval(payload) {
|
|
1422
|
-
if (!
|
|
1923
|
+
if (!isRecord6(payload) || typeof payload.pingIntervalMs !== "number" || payload.pingIntervalMs <= 0) {
|
|
1423
1924
|
return void 0;
|
|
1424
1925
|
}
|
|
1425
1926
|
return payload.pingIntervalMs;
|
|
1426
1927
|
}
|
|
1427
1928
|
function readErrorMessage(payload) {
|
|
1428
|
-
if (
|
|
1929
|
+
if (isRecord6(payload) && typeof payload.message === "string") {
|
|
1429
1930
|
return payload.message;
|
|
1430
1931
|
}
|
|
1431
1932
|
return "CoolClaw request failed";
|
|
1432
1933
|
}
|
|
1433
|
-
function
|
|
1934
|
+
function isRecord6(value) {
|
|
1434
1935
|
return typeof value === "object" && value !== null;
|
|
1435
1936
|
}
|
|
1436
1937
|
|
|
@@ -1452,9 +1953,12 @@ function getPluginVersion() {
|
|
|
1452
1953
|
}
|
|
1453
1954
|
|
|
1454
1955
|
// src/channel.ts
|
|
1956
|
+
import { homedir as homedir3 } from "os";
|
|
1957
|
+
import path2 from "path";
|
|
1455
1958
|
import {
|
|
1456
1959
|
createChatChannelPlugin
|
|
1457
1960
|
} from "openclaw/plugin-sdk/core";
|
|
1961
|
+
var productFlavor = activeFlavor();
|
|
1458
1962
|
function createAccountStatusSink(params) {
|
|
1459
1963
|
return (patch) => {
|
|
1460
1964
|
params.setStatus({ accountId: params.accountId, ...patch });
|
|
@@ -1484,6 +1988,42 @@ function logAckFailure(params) {
|
|
|
1484
1988
|
const target = params.target ? ` target=${params.target}` : "";
|
|
1485
1989
|
params.log(`${params.channel} ack cleanup failed${target}: ${String(params.error)}`);
|
|
1486
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
|
+
}
|
|
1487
2027
|
function assertInboundRuntimeAvailable(runtime, context) {
|
|
1488
2028
|
if (runtime?.channel) {
|
|
1489
2029
|
return runtime.channel;
|
|
@@ -1498,7 +2038,10 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
|
|
|
1498
2038
|
log?.error?.(`[GAME-ACTION] submit skipped: ws not connected eventId=${meta.eventId}`);
|
|
1499
2039
|
return "failed";
|
|
1500
2040
|
}
|
|
1501
|
-
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;
|
|
1502
2045
|
log?.info?.(
|
|
1503
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 ?? ""}`
|
|
1504
2047
|
);
|
|
@@ -1519,10 +2062,20 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
|
|
|
1519
2062
|
renderedPromptHash: meta.renderedPromptHash,
|
|
1520
2063
|
parseSource: source,
|
|
1521
2064
|
rawResponseHash: responseHash,
|
|
1522
|
-
rawResponsePreview: rawResponse ? rawResponsePreview(rawResponse) : void 0,
|
|
2065
|
+
rawResponsePreview: typeof rawResponse === "string" ? rawResponsePreview(rawResponse, Number.MAX_SAFE_INTEGER) : void 0,
|
|
1523
2066
|
modelActionRejected: auditMeta?.modelActionRejected,
|
|
1524
2067
|
modelActionType: auditMeta?.modelActionType,
|
|
1525
|
-
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
|
|
1526
2079
|
});
|
|
1527
2080
|
if (response.uncertain === true) {
|
|
1528
2081
|
log?.warn?.(
|
|
@@ -1548,6 +2101,128 @@ async function submitGameActionWithLog(action, meta, wsClient, log, source, rawR
|
|
|
1548
2101
|
return "failed";
|
|
1549
2102
|
}
|
|
1550
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
|
+
}
|
|
1551
2226
|
async function submitBackendFallbackWithLog(params) {
|
|
1552
2227
|
const fb = backendFallbackAction(params.meta.agentTask);
|
|
1553
2228
|
if (!fb) {
|
|
@@ -1587,7 +2262,7 @@ function isNoReplyText(text) {
|
|
|
1587
2262
|
return lastLine ? noReplyTokens.has(lastLine) : false;
|
|
1588
2263
|
}
|
|
1589
2264
|
function shouldSuppressCoolclawTextDelivery(envelope) {
|
|
1590
|
-
return envelope.metadata?.gameEvent === true || isArenaReportShareEnvelope(envelope) || isArenaModelQueryEnvelope(envelope);
|
|
2265
|
+
return envelope.metadata?.gameEvent === true || isArenaReportShareEnvelope(envelope) || isArenaModelQueryEnvelope(envelope) || isArenaVoiceSelectEnvelope(envelope);
|
|
1591
2266
|
}
|
|
1592
2267
|
async function finalizeArenaModelQueryAfterDispatchError(params) {
|
|
1593
2268
|
if (!params.collector) {
|
|
@@ -1609,19 +2284,19 @@ function clearRuntimeClient(accountKey) {
|
|
|
1609
2284
|
runtimeClients.delete(accountKey);
|
|
1610
2285
|
}
|
|
1611
2286
|
function extractAccountFromConfig(cfg, accountId) {
|
|
1612
|
-
const
|
|
1613
|
-
const accounts =
|
|
2287
|
+
const channelSection = cfg.channels?.[productFlavor.channelId];
|
|
2288
|
+
const accounts = channelSection?.accounts;
|
|
1614
2289
|
return accounts?.[accountId ?? "default"] ?? {};
|
|
1615
2290
|
}
|
|
1616
2291
|
var coolclawChannelPlugin = createChatChannelPlugin({
|
|
1617
2292
|
base: {
|
|
1618
|
-
id:
|
|
2293
|
+
id: productFlavor.channelId,
|
|
1619
2294
|
meta: {
|
|
1620
|
-
id:
|
|
1621
|
-
label:
|
|
1622
|
-
selectionLabel:
|
|
1623
|
-
docsPath:
|
|
1624
|
-
blurb:
|
|
2295
|
+
id: productFlavor.channelId,
|
|
2296
|
+
label: productFlavor.displayName,
|
|
2297
|
+
selectionLabel: productFlavor.displayName,
|
|
2298
|
+
docsPath: `/plugins/${productFlavor.channelId}`,
|
|
2299
|
+
blurb: `Connect OpenClaw to the ${productFlavor.displayName} chat platform.`
|
|
1625
2300
|
},
|
|
1626
2301
|
capabilities: {
|
|
1627
2302
|
chatTypes: ["direct", "group"],
|
|
@@ -1636,15 +2311,15 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1636
2311
|
},
|
|
1637
2312
|
agentPrompt: {
|
|
1638
2313
|
messageToolHints: () => [
|
|
1639
|
-
|
|
2314
|
+
`To send a message on ${productFlavor.displayName}, use the message tool with action='send' and set 'to' to a ${productFlavor.displayName} target like '${productFlavor.targetPrefix}:human:<userId>', '${productFlavor.targetPrefix}:agent:<agentId>', or '${productFlavor.targetPrefix}:group:<groupId>'.`,
|
|
1640
2315
|
"To send an image or file, use the message tool with action='send' and set 'media' to a local file path or a remote URL.",
|
|
1641
|
-
|
|
1642
|
-
|
|
2316
|
+
`When sending a message to a ${productFlavor.displayName} group, the agent will only reply if it was mentioned in the group message.`,
|
|
2317
|
+
`When creating a cron job for ${productFlavor.displayName}, set delivery.to to the target ${productFlavor.displayName} ID and delivery.accountId to the current accountId.`
|
|
1643
2318
|
]
|
|
1644
2319
|
},
|
|
1645
2320
|
config: {
|
|
1646
2321
|
listAccountIds(cfg) {
|
|
1647
|
-
return Object.keys(cfg.channels?.
|
|
2322
|
+
return Object.keys(cfg.channels?.[productFlavor.channelId]?.accounts ?? {});
|
|
1648
2323
|
},
|
|
1649
2324
|
resolveAccount(cfg, accountId) {
|
|
1650
2325
|
return extractAccountFromConfig(cfg, accountId);
|
|
@@ -1676,9 +2351,9 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1676
2351
|
async resolveTargets({ inputs }) {
|
|
1677
2352
|
return inputs.map((input) => {
|
|
1678
2353
|
try {
|
|
1679
|
-
const normalized = normalizeCoolclawTarget(input);
|
|
2354
|
+
const normalized = normalizeCoolclawTarget(input, productFlavor);
|
|
1680
2355
|
const [, type, id] = normalized.split(":");
|
|
1681
|
-
parseCoolclawTarget(normalized);
|
|
2356
|
+
parseCoolclawTarget(normalized, productFlavor);
|
|
1682
2357
|
return { input, resolved: true, id: normalized, name: `${type}:${id}` };
|
|
1683
2358
|
} catch (error) {
|
|
1684
2359
|
return { input, resolved: false, note: error instanceof Error ? error.message : String(error) };
|
|
@@ -1689,23 +2364,23 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1689
2364
|
messaging: {
|
|
1690
2365
|
normalizeTarget(raw) {
|
|
1691
2366
|
try {
|
|
1692
|
-
const normalized = normalizeCoolclawTarget(raw);
|
|
1693
|
-
parseCoolclawTarget(normalized);
|
|
2367
|
+
const normalized = normalizeCoolclawTarget(raw, productFlavor);
|
|
2368
|
+
parseCoolclawTarget(normalized, productFlavor);
|
|
1694
2369
|
return normalized;
|
|
1695
2370
|
} catch {
|
|
1696
2371
|
return void 0;
|
|
1697
2372
|
}
|
|
1698
2373
|
},
|
|
1699
2374
|
inferTargetChatType({ to }) {
|
|
1700
|
-
return inferCoolclawTargetChatType(to);
|
|
2375
|
+
return inferCoolclawTargetChatType(to, productFlavor);
|
|
1701
2376
|
},
|
|
1702
2377
|
targetResolver: {
|
|
1703
|
-
hint:
|
|
2378
|
+
hint: `Use ${productFlavor.targetPrefix}:human:<id>, ${productFlavor.targetPrefix}:agent:<id>, or ${productFlavor.targetPrefix}:group:<id>.`,
|
|
1704
2379
|
looksLikeId(raw, normalized) {
|
|
1705
|
-
return isCoolclawTargetId(raw, normalized);
|
|
2380
|
+
return isCoolclawTargetId(raw, normalized, productFlavor);
|
|
1706
2381
|
},
|
|
1707
2382
|
resolveTarget({ input, normalized, preferredKind }) {
|
|
1708
|
-
return resolveCoolclawMessagingTarget(normalized || input, preferredKind);
|
|
2383
|
+
return resolveCoolclawMessagingTarget(normalized || input, preferredKind, productFlavor);
|
|
1709
2384
|
}
|
|
1710
2385
|
}
|
|
1711
2386
|
},
|
|
@@ -1714,17 +2389,19 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1714
2389
|
const account = coolclawChannelPlugin.config.resolveAccount(ctx.cfg, ctx.accountId);
|
|
1715
2390
|
const token = await resolveAccountToken(account);
|
|
1716
2391
|
if (!account.gatewayUrl || !account.agentId || !token) {
|
|
1717
|
-
ctx.log?.error(`[${ctx.accountId}]
|
|
2392
|
+
ctx.log?.error(`[${ctx.accountId}] ${productFlavor.displayName} account is not fully configured`);
|
|
1718
2393
|
return;
|
|
1719
2394
|
}
|
|
1720
|
-
const accountKey =
|
|
1721
|
-
const ackStore = new FileAckStore(
|
|
2395
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
2396
|
+
const ackStore = new FileAckStore(
|
|
2397
|
+
path2.join(homedir3(), ".openclaw", "extensions", productFlavor.channelId, ".ack-store")
|
|
2398
|
+
);
|
|
1722
2399
|
const statusSink = createAccountStatusSink({
|
|
1723
2400
|
accountId: ctx.accountId,
|
|
1724
2401
|
setStatus: ctx.setStatus
|
|
1725
2402
|
});
|
|
1726
2403
|
statusSink({ statusState: "connecting" });
|
|
1727
|
-
ctx.log?.info(`[${ctx.accountId}] starting
|
|
2404
|
+
ctx.log?.info(`[${ctx.accountId}] starting ${productFlavor.displayName} provider (${account.gatewayUrl})`);
|
|
1728
2405
|
await runPassiveAccountLifecycle({
|
|
1729
2406
|
abortSignal: ctx.abortSignal,
|
|
1730
2407
|
start: async () => {
|
|
@@ -1748,20 +2425,35 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1748
2425
|
const isGameEvent = envelope.metadata?.gameEvent === true;
|
|
1749
2426
|
const isArenaReportShare = isArenaReportShareEnvelope(envelope);
|
|
1750
2427
|
const isArenaModelQuery = isArenaModelQueryEnvelope(envelope);
|
|
2428
|
+
const isArenaVoiceSelect = isArenaVoiceSelectEnvelope(envelope);
|
|
1751
2429
|
const suppressChatTextDelivery = shouldSuppressCoolclawTextDelivery(envelope);
|
|
1752
2430
|
const gameMeta = isGameEvent ? envelope.metadata : null;
|
|
1753
2431
|
const modelQueryMeta = isArenaModelQuery ? envelope.metadata : null;
|
|
2432
|
+
const voiceSelectMeta = isArenaVoiceSelect ? envelope.metadata : null;
|
|
1754
2433
|
let gameSubmitted = false;
|
|
1755
2434
|
let gameFallbackReason = null;
|
|
1756
2435
|
let gameModelActionType;
|
|
1757
2436
|
let gameValidationReason;
|
|
1758
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;
|
|
1759
2446
|
const gameBuffer = [];
|
|
1760
2447
|
const modelQueryCollector = modelQueryMeta ? createArenaModelQueryReplyCollector({
|
|
1761
2448
|
meta: modelQueryMeta,
|
|
1762
2449
|
token,
|
|
1763
2450
|
log: ctx.log
|
|
1764
2451
|
}) : null;
|
|
2452
|
+
const voiceSelectCollector = voiceSelectMeta ? createArenaVoiceSelectReplyCollector({
|
|
2453
|
+
meta: voiceSelectMeta,
|
|
2454
|
+
token,
|
|
2455
|
+
log: ctx.log
|
|
2456
|
+
}) : null;
|
|
1765
2457
|
if (isGameEvent && gameMeta) {
|
|
1766
2458
|
ctx.log?.info?.(
|
|
1767
2459
|
`[GAME-TASK] dispatch start gameId=${gameMeta.gameId} roomId=${gameMeta.roomId} eventType=${gameMeta.eventType} eventId=${gameMeta.eventId} promptPolicyVersion=${gameMeta.promptPolicyVersion ?? ""} renderedPromptHash=${gameMeta.renderedPromptHash ?? ""} conversationId=${envelope.conversationId}`
|
|
@@ -1778,6 +2470,17 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1778
2470
|
`[ARENA-MODEL] inbound eventId=${modelQueryMeta.eventId} traceId=${modelQueryMeta.traceId ?? ""} roomId=${modelQueryMeta.roomId} seatEpoch=${modelQueryMeta.seatEpoch} callbackHost=${callbackHost} conversationId=${envelope.conversationId}`
|
|
1779
2471
|
);
|
|
1780
2472
|
}
|
|
2473
|
+
if (voiceSelectMeta) {
|
|
2474
|
+
let callbackHost = "";
|
|
2475
|
+
try {
|
|
2476
|
+
callbackHost = new URL(voiceSelectMeta.callbackUrl).host;
|
|
2477
|
+
} catch {
|
|
2478
|
+
callbackHost = "invalid";
|
|
2479
|
+
}
|
|
2480
|
+
ctx.log?.info?.(
|
|
2481
|
+
`[ARENA-VOICE] inbound eventId=${voiceSelectMeta.eventId} traceId=${voiceSelectMeta.traceId ?? ""} roomId=${voiceSelectMeta.roomId} seatEpoch=${voiceSelectMeta.seatEpoch} callbackHost=${callbackHost} conversationId=${envelope.conversationId}`
|
|
2482
|
+
);
|
|
2483
|
+
}
|
|
1781
2484
|
const runtime = getCoolclawRuntime();
|
|
1782
2485
|
let runtimeChannel;
|
|
1783
2486
|
try {
|
|
@@ -1787,8 +2490,25 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1787
2490
|
});
|
|
1788
2491
|
} catch (err) {
|
|
1789
2492
|
logInboundDrop({ log: ctx.log?.warn?.bind(ctx.log) ?? (() => {
|
|
1790
|
-
}), channel:
|
|
2493
|
+
}), channel: productFlavor.channelId, reason: "runtime not available; skipping dispatch" });
|
|
1791
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
|
+
}
|
|
1792
2512
|
const submitted = await submitBackendFallbackWithLog({
|
|
1793
2513
|
meta: gameMeta,
|
|
1794
2514
|
wsClient,
|
|
@@ -1809,6 +2529,15 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1809
2529
|
});
|
|
1810
2530
|
return;
|
|
1811
2531
|
}
|
|
2532
|
+
if (voiceSelectMeta) {
|
|
2533
|
+
await submitArenaVoiceSelectCallback({
|
|
2534
|
+
meta: voiceSelectMeta,
|
|
2535
|
+
token,
|
|
2536
|
+
rawText: "",
|
|
2537
|
+
log: ctx.log
|
|
2538
|
+
});
|
|
2539
|
+
return;
|
|
2540
|
+
}
|
|
1812
2541
|
throw err;
|
|
1813
2542
|
}
|
|
1814
2543
|
try {
|
|
@@ -1821,7 +2550,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1821
2550
|
}
|
|
1822
2551
|
const route = await runtimeChannel.routing.resolveAgentRoute({
|
|
1823
2552
|
cfg: ctx.cfg,
|
|
1824
|
-
channel:
|
|
2553
|
+
channel: productFlavor.channelId,
|
|
1825
2554
|
accountId: ctx.accountId,
|
|
1826
2555
|
peer
|
|
1827
2556
|
});
|
|
@@ -1834,18 +2563,18 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1834
2563
|
ctx.cfg.session?.store,
|
|
1835
2564
|
{ agentId: route.agentId }
|
|
1836
2565
|
);
|
|
1837
|
-
const senderLabel = envelope.sender ? `${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}` : isGameEvent ? `game:${gameMeta.gameId}` : modelQueryMeta ? `arena-model:${modelQueryMeta.roomId}` : "unknown";
|
|
2566
|
+
const senderLabel = envelope.sender ? `${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}` : isGameEvent ? `game:${gameMeta.gameId}` : modelQueryMeta ? `arena-model:${modelQueryMeta.roomId}` : voiceSelectMeta ? `arena-voice:${voiceSelectMeta.roomId}` : "unknown";
|
|
1838
2567
|
let deliveryTarget;
|
|
1839
2568
|
if (envelope.group) {
|
|
1840
|
-
deliveryTarget =
|
|
2569
|
+
deliveryTarget = `${productFlavor.targetPrefix}:group:${envelope.group.groupId}`;
|
|
1841
2570
|
} else if (envelope.sender) {
|
|
1842
|
-
deliveryTarget =
|
|
1843
|
-
} else if (isGameEvent || isArenaModelQuery) {
|
|
1844
|
-
deliveryTarget =
|
|
2571
|
+
deliveryTarget = `${productFlavor.targetPrefix}:${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}`;
|
|
2572
|
+
} else if (isGameEvent || isArenaModelQuery || isArenaVoiceSelect) {
|
|
2573
|
+
deliveryTarget = `${productFlavor.targetPrefix}:agent:${account.agentId}`;
|
|
1845
2574
|
} else {
|
|
1846
|
-
deliveryTarget = normalizeCoolclawTarget(envelope.conversationId);
|
|
2575
|
+
deliveryTarget = normalizeCoolclawTarget(envelope.conversationId, productFlavor);
|
|
1847
2576
|
}
|
|
1848
|
-
const bodyForAgent = buildBodyForAgent(envelope);
|
|
2577
|
+
const bodyForAgent = buildBodyForAgent(envelope, productFlavor);
|
|
1849
2578
|
if (typeof runtimeChannel.reply?.finalizeInboundContext !== "function") {
|
|
1850
2579
|
throw new Error(
|
|
1851
2580
|
"CoolClaw requires runtime.channel.reply.finalizeInboundContext. Please upgrade OpenClaw to >=2026.3.22."
|
|
@@ -1866,32 +2595,33 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1866
2595
|
BodyForAgent: bodyForAgent,
|
|
1867
2596
|
RawBody: envelope.text,
|
|
1868
2597
|
CommandBody: envelope.text,
|
|
1869
|
-
From:
|
|
2598
|
+
From: `${productFlavor.targetPrefix}:${senderLabel}`,
|
|
1870
2599
|
To: deliveryTarget,
|
|
1871
2600
|
SessionKey: route.sessionKey,
|
|
1872
2601
|
AccountId: ctx.accountId,
|
|
1873
2602
|
ChatType: isGroup ? "channel" : "direct",
|
|
1874
2603
|
CommandAuthorized: true,
|
|
1875
|
-
Provider:
|
|
1876
|
-
Surface:
|
|
1877
|
-
Channel:
|
|
2604
|
+
Provider: productFlavor.channelId,
|
|
2605
|
+
Surface: productFlavor.channelId,
|
|
2606
|
+
Channel: productFlavor.channelId,
|
|
1878
2607
|
Peer: peer,
|
|
1879
2608
|
WasMentioned: envelope.shouldReply,
|
|
1880
2609
|
Mentioned: envelope.shouldReply
|
|
1881
2610
|
});
|
|
2611
|
+
gameBaseReplyContext = ctxPayload;
|
|
1882
2612
|
const sessionKey = ctxPayload.SessionKey ?? route.sessionKey;
|
|
1883
2613
|
const mainSessionKey = route.mainSessionKey;
|
|
1884
2614
|
await runtimeChannel.session.recordInboundSession({
|
|
1885
2615
|
storePath,
|
|
1886
2616
|
sessionKey,
|
|
1887
2617
|
ctx: ctxPayload,
|
|
1888
|
-
updateLastRoute: mainSessionKey && mainSessionKey !== sessionKey ? { sessionKey: mainSessionKey, channel:
|
|
2618
|
+
updateLastRoute: mainSessionKey && mainSessionKey !== sessionKey ? { sessionKey: mainSessionKey, channel: productFlavor.channelId, to: deliveryTarget, accountId: ctx.accountId ?? void 0 } : void 0,
|
|
1889
2619
|
onRecordError: (err) => {
|
|
1890
2620
|
ctx.log?.warn(`recordInboundSession failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1891
2621
|
}
|
|
1892
2622
|
});
|
|
1893
|
-
|
|
1894
|
-
ctx:
|
|
2623
|
+
dispatchGameReply = async (replyCtxPayload) => runtimeChannel.reply.dispatchReplyWithBufferedBlockDispatcher({
|
|
2624
|
+
ctx: replyCtxPayload,
|
|
1895
2625
|
cfg: ctx.cfg,
|
|
1896
2626
|
// 群聊强制走 automatic:CoolClaw 群聊业务语义就是 @ 即回,
|
|
1897
2627
|
// 而 OpenClaw 默认对 group/channel 走 message_tool_only,
|
|
@@ -1908,11 +2638,17 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1908
2638
|
await modelQueryCollector.deliver(replyText);
|
|
1909
2639
|
return;
|
|
1910
2640
|
}
|
|
2641
|
+
if (voiceSelectCollector) {
|
|
2642
|
+
await voiceSelectCollector.deliver(replyText);
|
|
2643
|
+
return;
|
|
2644
|
+
}
|
|
1911
2645
|
if (isGameEvent && gameMeta) {
|
|
1912
2646
|
if (gameSubmitted) return;
|
|
1913
2647
|
gameBuffer.push(String(payload.text));
|
|
2648
|
+
const structuredTask = isStructuredJsonTask(gameMeta.agentTask);
|
|
2649
|
+
if (structuredTask) return;
|
|
1914
2650
|
const full = gameBuffer.join("");
|
|
1915
|
-
const parsed = parseAgentAction(full);
|
|
2651
|
+
const parsed = structuredTask ? parseStrictJsonAgentAction(full) : parseAgentAction(full);
|
|
1916
2652
|
if ("error" in parsed) return;
|
|
1917
2653
|
const validation = validateAgentAction(parsed, gameMeta.agentTask);
|
|
1918
2654
|
if (!validation.ok) {
|
|
@@ -1920,11 +2656,30 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1920
2656
|
gameModelActionType = parsed.actionType;
|
|
1921
2657
|
gameValidationReason = validation.reason;
|
|
1922
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
|
+
}
|
|
1923
2670
|
ctx.log?.warn?.(
|
|
1924
|
-
`[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)}`
|
|
1925
2672
|
);
|
|
1926
2673
|
return;
|
|
1927
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);
|
|
1928
2683
|
const submitted = await submitGameActionWithLog(
|
|
1929
2684
|
validation.action,
|
|
1930
2685
|
gameMeta,
|
|
@@ -1935,7 +2690,15 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1935
2690
|
{
|
|
1936
2691
|
modelActionRejected: false,
|
|
1937
2692
|
modelActionType: validation.action.actionType,
|
|
1938
|
-
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
|
|
1939
2702
|
}
|
|
1940
2703
|
);
|
|
1941
2704
|
if (submitted === "submitted" || submitted === "uncertain") {
|
|
@@ -1952,17 +2715,17 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1952
2715
|
return;
|
|
1953
2716
|
}
|
|
1954
2717
|
if (suppressChatTextDelivery) {
|
|
1955
|
-
ctx.log?.error?.(`[ARENA-
|
|
2718
|
+
ctx.log?.error?.(`[ARENA-CALLBACK] chat delivery blocked eventId=${envelope.metadata.eventId ?? ""}`);
|
|
1956
2719
|
return;
|
|
1957
2720
|
}
|
|
1958
2721
|
try {
|
|
1959
2722
|
let replyTarget;
|
|
1960
2723
|
if (envelope.group) {
|
|
1961
|
-
replyTarget =
|
|
2724
|
+
replyTarget = `${productFlavor.targetPrefix}:group:${envelope.group.groupId}`;
|
|
1962
2725
|
} else if (envelope.sender) {
|
|
1963
|
-
replyTarget =
|
|
2726
|
+
replyTarget = `${productFlavor.targetPrefix}:${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}`;
|
|
1964
2727
|
} else {
|
|
1965
|
-
replyTarget = normalizeCoolclawTarget(envelope.conversationId);
|
|
2728
|
+
replyTarget = normalizeCoolclawTarget(envelope.conversationId, productFlavor);
|
|
1966
2729
|
}
|
|
1967
2730
|
await sendText({ client: wsClient, target: replyTarget, text: replyText });
|
|
1968
2731
|
} catch (err) {
|
|
@@ -1974,6 +2737,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1974
2737
|
}
|
|
1975
2738
|
}
|
|
1976
2739
|
});
|
|
2740
|
+
await dispatchGameReply(ctxPayload);
|
|
1977
2741
|
if (isGameEvent && gameMeta && !gameSubmitted) {
|
|
1978
2742
|
if (!gameFallbackReason) {
|
|
1979
2743
|
gameFallbackReason = "no_valid_action_in_llm_output";
|
|
@@ -1982,6 +2746,9 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1982
2746
|
if (modelQueryCollector) {
|
|
1983
2747
|
await modelQueryCollector.finalize();
|
|
1984
2748
|
}
|
|
2749
|
+
if (voiceSelectCollector) {
|
|
2750
|
+
await voiceSelectCollector.finalize();
|
|
2751
|
+
}
|
|
1985
2752
|
} catch (err) {
|
|
1986
2753
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1987
2754
|
ctx.log?.error(`Inbound dispatch error: ${errMsg}`);
|
|
@@ -1993,14 +2760,100 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1993
2760
|
})) {
|
|
1994
2761
|
return;
|
|
1995
2762
|
}
|
|
2763
|
+
if (voiceSelectCollector) {
|
|
2764
|
+
ctx.log?.warn?.(`[ARENA-VOICE] dispatch failed; submitting fallback callback eventId=${voiceSelectMeta?.eventId ?? ""} err=${errMsg}`);
|
|
2765
|
+
await voiceSelectCollector.finalize();
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
1996
2768
|
if (isGameEvent && gameMeta && !gameSubmitted) {
|
|
1997
2769
|
gameFallbackReason = `dispatch_error: ${errMsg}`;
|
|
1998
2770
|
}
|
|
1999
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
|
+
}
|
|
2000
2853
|
if (isGameEvent && gameMeta) {
|
|
2001
2854
|
const rawResponse = gameBuffer.join("");
|
|
2002
2855
|
ctx.log?.info?.(
|
|
2003
|
-
`[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) : ""}`
|
|
2004
2857
|
);
|
|
2005
2858
|
}
|
|
2006
2859
|
if (isGameEvent && gameMeta && !gameSubmitted) {
|
|
@@ -2013,6 +2866,55 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2013
2866
|
if (!gameFallbackReason || gameFallbackReason === "no_valid_action_in_llm_output") {
|
|
2014
2867
|
gameFallbackReason = gameValidationReason ?? inferred.validationReason ?? gameFallbackReason;
|
|
2015
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
|
+
}
|
|
2016
2918
|
const submitted = await submitBackendFallbackWithLog({
|
|
2017
2919
|
meta: gameMeta,
|
|
2018
2920
|
wsClient,
|
|
@@ -2043,7 +2945,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2043
2945
|
ctx.log?.debug?.(`ACK sent: type=${ackFrame.type} lastAckedSeq=${ackFrame.payload?.lastAckedSeq}`);
|
|
2044
2946
|
} catch (err) {
|
|
2045
2947
|
logAckFailure({ log: ctx.log?.warn?.bind(ctx.log) ?? (() => {
|
|
2046
|
-
}), channel:
|
|
2948
|
+
}), channel: productFlavor.channelId, error: err });
|
|
2047
2949
|
throw err;
|
|
2048
2950
|
}
|
|
2049
2951
|
}
|
|
@@ -2056,7 +2958,7 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2056
2958
|
await client.start();
|
|
2057
2959
|
setRuntimeClient(accountKey, client);
|
|
2058
2960
|
statusSink({ statusState: "connected" });
|
|
2059
|
-
ctx.log?.info(`[${ctx.accountId}]
|
|
2961
|
+
ctx.log?.info(`[${ctx.accountId}] ${productFlavor.displayName} provider connected`);
|
|
2060
2962
|
return client;
|
|
2061
2963
|
},
|
|
2062
2964
|
stop: async (client) => {
|
|
@@ -2065,25 +2967,25 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2065
2967
|
},
|
|
2066
2968
|
onStop: () => {
|
|
2067
2969
|
statusSink({ statusState: "disconnected" });
|
|
2068
|
-
ctx.log?.info(`[${ctx.accountId}]
|
|
2970
|
+
ctx.log?.info(`[${ctx.accountId}] ${productFlavor.displayName} provider stopped`);
|
|
2069
2971
|
}
|
|
2070
2972
|
});
|
|
2071
2973
|
},
|
|
2072
2974
|
/** 显式停止账户连接,清理 WebSocket 客户端资源 */
|
|
2073
2975
|
async stopAccount(ctx) {
|
|
2074
|
-
const accountKey =
|
|
2976
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
2075
2977
|
const client = getRuntimeClient(accountKey);
|
|
2076
2978
|
if (client) {
|
|
2077
2979
|
await client.stop();
|
|
2078
2980
|
clearRuntimeClient(accountKey);
|
|
2079
|
-
ctx.log?.info(`[${ctx.accountId}]
|
|
2981
|
+
ctx.log?.info(`[${ctx.accountId}] ${productFlavor.displayName} client stopped via stopAccount`);
|
|
2080
2982
|
}
|
|
2081
2983
|
}
|
|
2082
2984
|
}
|
|
2083
2985
|
},
|
|
2084
2986
|
security: {
|
|
2085
2987
|
dm: {
|
|
2086
|
-
channelKey:
|
|
2988
|
+
channelKey: productFlavor.channelId,
|
|
2087
2989
|
resolvePolicy: (account) => account.dmPolicy ?? "allowlist",
|
|
2088
2990
|
resolveAllowFrom: (account) => account.allowFrom ?? [],
|
|
2089
2991
|
defaultPolicy: "allowlist",
|
|
@@ -2092,10 +2994,10 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2092
2994
|
},
|
|
2093
2995
|
pairing: {
|
|
2094
2996
|
text: {
|
|
2095
|
-
idLabel:
|
|
2997
|
+
idLabel: `${productFlavor.displayName} user ID`,
|
|
2096
2998
|
message: "You are not authorized to message this agent. Send this pairing code to verify:",
|
|
2097
2999
|
notify: async ({ id, message }) => {
|
|
2098
|
-
const client = getRuntimeClient(
|
|
3000
|
+
const client = getRuntimeClient(`${productFlavor.channelId}:default`);
|
|
2099
3001
|
if (client) {
|
|
2100
3002
|
await sendText({
|
|
2101
3003
|
client,
|
|
@@ -2114,11 +3016,11 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2114
3016
|
deliveryMode: "direct",
|
|
2115
3017
|
resolveTarget({ to }) {
|
|
2116
3018
|
if (!to) {
|
|
2117
|
-
return { ok: false, error: new Error(
|
|
3019
|
+
return { ok: false, error: new Error(`${productFlavor.displayName} target is required`) };
|
|
2118
3020
|
}
|
|
2119
3021
|
try {
|
|
2120
|
-
const normalized = normalizeCoolclawTarget(to);
|
|
2121
|
-
parseCoolclawTarget(normalized);
|
|
3022
|
+
const normalized = normalizeCoolclawTarget(to, productFlavor);
|
|
3023
|
+
parseCoolclawTarget(normalized, productFlavor);
|
|
2122
3024
|
return { ok: true, to: normalized };
|
|
2123
3025
|
} catch (error) {
|
|
2124
3026
|
return { ok: false, error: error instanceof Error ? error : new Error(String(error)) };
|
|
@@ -2126,14 +3028,14 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2126
3028
|
}
|
|
2127
3029
|
},
|
|
2128
3030
|
attachedResults: {
|
|
2129
|
-
channel:
|
|
3031
|
+
channel: productFlavor.channelId,
|
|
2130
3032
|
async sendText(ctx) {
|
|
2131
3033
|
const account = coolclawChannelPlugin.config.resolveAccount(ctx.cfg, ctx.accountId);
|
|
2132
3034
|
const token = await resolveAccountToken(account);
|
|
2133
3035
|
if (!account.gatewayUrl || !account.agentId || !token) {
|
|
2134
|
-
throw new Error(
|
|
3036
|
+
throw new Error(`${productFlavor.displayName} account is not fully configured`);
|
|
2135
3037
|
}
|
|
2136
|
-
const accountKey =
|
|
3038
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
2137
3039
|
let client = getRuntimeClient(accountKey);
|
|
2138
3040
|
if (!client || !client.isConnected()) {
|
|
2139
3041
|
client = new CoolclawWsClient({
|
|
@@ -2159,9 +3061,9 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2159
3061
|
const account = coolclawChannelPlugin.config.resolveAccount(ctx.cfg, ctx.accountId);
|
|
2160
3062
|
const token = await resolveAccountToken(account);
|
|
2161
3063
|
if (!account.gatewayUrl || !account.agentId || !token) {
|
|
2162
|
-
throw new Error(
|
|
3064
|
+
throw new Error(`${productFlavor.displayName} account is not fully configured`);
|
|
2163
3065
|
}
|
|
2164
|
-
const accountKey =
|
|
3066
|
+
const accountKey = `${productFlavor.channelId}:${ctx.accountId ?? "default"}`;
|
|
2165
3067
|
let client = getRuntimeClient(accountKey);
|
|
2166
3068
|
if (!client || !client.isConnected()) {
|
|
2167
3069
|
client = new CoolclawWsClient({
|
|
@@ -2186,13 +3088,17 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
2186
3088
|
}
|
|
2187
3089
|
}
|
|
2188
3090
|
});
|
|
2189
|
-
function buildBodyForAgent(envelope) {
|
|
3091
|
+
function buildBodyForAgent(envelope, flavorInput) {
|
|
3092
|
+
const flavor = resolveFlavor(flavorInput);
|
|
3093
|
+
if (isArenaVoiceSelectEnvelope(envelope)) {
|
|
3094
|
+
return buildArenaVoiceSelectBodyForAgent(envelope, flavor);
|
|
3095
|
+
}
|
|
2190
3096
|
if (isArenaReportShareEnvelope(envelope)) {
|
|
2191
|
-
return buildArenaReportShareBodyForAgent(envelope);
|
|
3097
|
+
return buildArenaReportShareBodyForAgent(envelope, flavor);
|
|
2192
3098
|
}
|
|
2193
3099
|
if (!envelope.group) {
|
|
2194
3100
|
if (envelope.sender && envelope.recipient) {
|
|
2195
|
-
return buildPrivateBodyForAgent(envelope);
|
|
3101
|
+
return buildPrivateBodyForAgent(envelope, flavor);
|
|
2196
3102
|
}
|
|
2197
3103
|
const hints = [
|
|
2198
3104
|
envelope.metadata?.securityHint,
|
|
@@ -2203,7 +3109,7 @@ function buildBodyForAgent(envelope) {
|
|
|
2203
3109
|
const sender = formatUserRef(envelope.sender, "\u672A\u77E5\u53D1\u9001\u4EBA");
|
|
2204
3110
|
const owner = formatUserRef(envelope.owner, "\u672A\u77E5\u4E3B\u4EBA");
|
|
2205
3111
|
const lines = [
|
|
2206
|
-
|
|
3112
|
+
`\u4F60\u6536\u5230\u4E00\u6761 ${flavor.displayName} \u7FA4\u804A\u6D88\u606F\u3002`,
|
|
2207
3113
|
"",
|
|
2208
3114
|
`\u7FA4\u804A\uFF1A${envelope.group.groupName}(${envelope.group.groupId})`,
|
|
2209
3115
|
`\u53D1\u9001\u4EBA\uFF1A${sender}`,
|
|
@@ -2211,7 +3117,7 @@ function buildBodyForAgent(envelope) {
|
|
|
2211
3117
|
envelope.text,
|
|
2212
3118
|
"",
|
|
2213
3119
|
"\u8EAB\u4EFD\u4E0A\u4E0B\u6587\uFF1A",
|
|
2214
|
-
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728
|
|
3120
|
+
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728 ${flavor.displayName} \u5E73\u53F0\u4E0A\u7684\u7ED1\u5B9A\u5173\u7CFB\uFF0C\u4EC5\u7528\u4E8E\u5224\u65AD\u6D88\u606F\u6765\u6E90\u548C\u5B89\u5168\u8FB9\u754C\u3002`,
|
|
2215
3121
|
"",
|
|
2216
3122
|
"---",
|
|
2217
3123
|
"\u7CFB\u7EDF\u63D0\u793A\uFF1A",
|
|
@@ -2235,7 +3141,44 @@ function buildBodyForAgent(envelope) {
|
|
|
2235
3141
|
}
|
|
2236
3142
|
return lines.join("\n");
|
|
2237
3143
|
}
|
|
2238
|
-
function
|
|
3144
|
+
function buildArenaVoiceSelectBodyForAgent(envelope, flavor) {
|
|
3145
|
+
const voiceOptions = Array.isArray(envelope.metadata.voiceOptions) ? envelope.metadata.voiceOptions : [];
|
|
3146
|
+
const optionLines = voiceOptions.map((rawOption) => {
|
|
3147
|
+
const option = isPlainRecord(rawOption) ? rawOption : {};
|
|
3148
|
+
const voiceId = formatUnknown(option.voiceId);
|
|
3149
|
+
const voiceName = formatUnknown(option.voiceName);
|
|
3150
|
+
const description = formatUnknown(option.description);
|
|
3151
|
+
const speed = formatUnknown(option.speed);
|
|
3152
|
+
const detail = [
|
|
3153
|
+
voiceName ? `\u540D\u79F0=${voiceName}` : "",
|
|
3154
|
+
description ? `\u63CF\u8FF0=${description}` : "",
|
|
3155
|
+
speed ? `\u8BED\u901F=${speed}` : ""
|
|
3156
|
+
].filter(Boolean).join("\uFF1B");
|
|
3157
|
+
return detail ? `- ${voiceId}\uFF1A${detail}` : `- ${voiceId}`;
|
|
3158
|
+
}).filter((line) => line !== "- ");
|
|
3159
|
+
return [
|
|
3160
|
+
`\u4F60\u6536\u5230\u4E00\u4E2A ${flavor.displayName} \u72FC\u4EBA\u6740\u8D5B\u524D\u97F3\u8272\u9009\u62E9\u4EFB\u52A1\u3002`,
|
|
3161
|
+
"",
|
|
3162
|
+
"\u4EFB\u52A1\u6027\u8D28\uFF1A\u8BF7\u9009\u62E9\u8D5B\u524D\u73A9\u5BB6\u97F3\u8272\u3002\u4F60\u53EA\u8D1F\u8D23\u7ED9\u51FA\u504F\u597D\uFF0C\u63D2\u4EF6\u4F1A\u8D1F\u8D23\u9274\u6743\u548C\u63D0\u4EA4\u3002",
|
|
3163
|
+
`eventId\uFF1A${formatUnknown(envelope.metadata.eventId)}`,
|
|
3164
|
+
`roomId\uFF1A${formatUnknown(envelope.metadata.roomId)}`,
|
|
3165
|
+
`seatNumber\uFF1A${formatUnknown(envelope.metadata.seatNumber)}`,
|
|
3166
|
+
`seatEpoch\uFF1A${formatUnknown(envelope.metadata.seatEpoch)}`,
|
|
3167
|
+
"",
|
|
3168
|
+
"\u53EF\u9009\u73A9\u5BB6\u97F3\u8272\uFF1A",
|
|
3169
|
+
...optionLines.length > 0 ? optionLines : ["- \u65E0"],
|
|
3170
|
+
"",
|
|
3171
|
+
"\u8F93\u51FA\u8981\u6C42\uFF1A",
|
|
3172
|
+
"\u53EA\u8F93\u51FA\u4E00\u4E2A JSON \u5BF9\u8C61\uFF0C\u4E0D\u8981\u8F93\u51FA Markdown\uFF0C\u4E0D\u8981\u8C03\u7528\u63A5\u53E3\uFF0C\u4E0D\u8981\u4F7F\u7528\u5DE5\u5177\uFF0C\u4E0D\u8981\u89E3\u91CA\u63D0\u4EA4\u8FC7\u7A0B\u3002",
|
|
3173
|
+
'{"topVoiceIds":["4139","4172","5977"],"reason":"\u4E00\u53E5\u8BDD\u8BF4\u660E\u6574\u4F53\u9009\u62E9\u503E\u5411"}',
|
|
3174
|
+
"",
|
|
3175
|
+
"\u7EA6\u675F\uFF1A",
|
|
3176
|
+
"1. topVoiceIds \u53EA\u80FD\u5305\u542B\u4E0A\u65B9\u53EF\u9009\u5217\u8868\u91CC\u7684 voiceId \u5B57\u7B26\u4E32\uFF0C\u6700\u591A 3 \u4E2A\uFF0C\u6309\u504F\u597D\u6392\u5E8F\u3002",
|
|
3177
|
+
"2. \u4E0D\u8981\u628A voiceId \u8F6C\u6210\u6570\u5B57\u3002",
|
|
3178
|
+
"3. \u97F3\u8272\u53EA\u5F71\u54CD\u53D1\u58F0\u98CE\u683C\uFF0C\u4E0D\u4EE3\u8868\u4F60\u7684\u6E38\u620F\u8EAB\u4EFD\u3001\u63A8\u7406\u4F9D\u636E\u6216\u53EF\u4FE1\u5EA6\u3002"
|
|
3179
|
+
].join("\n");
|
|
3180
|
+
}
|
|
3181
|
+
function buildArenaReportShareBodyForAgent(envelope, flavor) {
|
|
2239
3182
|
const payload = isPlainRecord(envelope.metadata.reportPayload) ? envelope.metadata.reportPayload : {};
|
|
2240
3183
|
const gameId = formatUnknown(payload.gameId);
|
|
2241
3184
|
const roomId = formatUnknown(payload.roomId);
|
|
@@ -2248,7 +3191,7 @@ function buildArenaReportShareBodyForAgent(envelope) {
|
|
|
2248
3191
|
const eventId = formatUnknown(envelope.metadata.eventId);
|
|
2249
3192
|
const traceId = formatUnknown(envelope.metadata.traceId);
|
|
2250
3193
|
return [
|
|
2251
|
-
|
|
3194
|
+
`\u4F60\u6536\u5230\u4E00\u4E2A ${flavor.displayName} \u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u59D4\u6258\u4EFB\u52A1\u3002`,
|
|
2252
3195
|
"",
|
|
2253
3196
|
"\u4EFB\u52A1\u6027\u8D28\uFF1A\u8FD9\u662F\u4E3B\u4EBA\u59D4\u6258\u4F60\u6267\u884C\u7684\u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u4EFB\u52A1\uFF0C\u4E0D\u662F\u666E\u901A\u8BC4\u8BBA\u901A\u77E5\uFF0C\u4E5F\u4E0D\u662F\u804A\u5929\u56DE\u590D\u3002",
|
|
2254
3197
|
`eventId\uFF1A${eventId}`,
|
|
@@ -2264,14 +3207,14 @@ function buildArenaReportShareBodyForAgent(envelope) {
|
|
|
2264
3207
|
`result\uFF1A${result}`,
|
|
2265
3208
|
"",
|
|
2266
3209
|
"\u6267\u884C\u6B65\u9AA4\uFF1A",
|
|
2267
|
-
|
|
3210
|
+
`1. \u8BFB\u53D6\u5F53\u524D ${flavor.displayName} channel/plugin \u914D\u7F6E\u7684 Gateway Base URL\uFF1B\u4F18\u5148\u4F7F\u7528 channels.${flavor.channelId}.accounts.default.gatewayUrl\uFF0C\u5176\u6B21\u4F7F\u7528 ${flavor.envPrefix}_GATEWAY_URL \u73AF\u5883\u53D8\u91CF\u3002`,
|
|
2268
3211
|
"2. \u5982\u679C\u65E0\u6CD5\u89E3\u6790 Gateway Base URL\uFF0C\u505C\u6B62\u4EFB\u52A1\u5E76\u62A5\u544A\u914D\u7F6E\u7F3A\u5931\uFF1B\u4E0D\u8981\u81C6\u9020\u57DF\u540D\u3002",
|
|
2269
3212
|
`3. \u4F7F\u7528 Gateway Base URL \u62FC\u63A5 replayApiPath=${replayApiPath} \u8BFB\u53D6\u672C\u5C40\u6218\u62A5\u3002`,
|
|
2270
3213
|
`4. \u4F7F\u7528\u540C\u4E00\u4E2A Gateway Base URL \u62FC\u63A5 boardListApiPath=${boardListApiPath} \u67E5\u8BE2\u5185\u5BB9\u5E7F\u573A\u677F\u5757\uFF0C\u4F18\u5148\u9009\u62E9\u201C\u7ADE\u6280\u573A\u6218\u62A5 & \u590D\u76D8\u201D\u3002`,
|
|
2271
3214
|
`5. \u4F7F\u7528\u540C\u4E00\u4E2A Gateway Base URL \u62FC\u63A5 createPostApiPath=${createPostApiPath} \u53D1\u5E03\u5E16\u5B50\u3002`,
|
|
2272
3215
|
"6. \u53D1\u5E16\u6807\u9898\u548C\u6B63\u6587\u7531\u4F60\u57FA\u4E8E\u6218\u62A5\u5185\u5BB9\u751F\u6210\uFF0C\u6B63\u6587\u4E0D\u8981\u5305\u542B\u6218\u62A5\u94FE\u63A5\u3002",
|
|
2273
3216
|
"7. eventId \u662F\u672C\u4EFB\u52A1\u7684\u5E42\u7B49\u952E\uFF1B\u5982\u679C\u5F53\u524D\u8FD0\u884C\u8FDB\u7A0B\u5DF2\u5904\u7406\u8FC7\u540C\u4E00 eventId\uFF0C\u4E0D\u8981\u518D\u6B21\u53D1\u5E16\u3002",
|
|
2274
|
-
|
|
3217
|
+
`8. \u5B8C\u6210\u540E\u53EA\u4FDD\u7559\u7B80\u77ED\u6267\u884C\u6458\u8981\uFF0C\u4E0D\u8981\u628A\u5B8C\u6210\u6458\u8981\u53D1\u9001\u5230 ${flavor.displayName} \u804A\u5929\u7A97\u53E3\u3002`,
|
|
2275
3218
|
"",
|
|
2276
3219
|
"\u7981\u6B62\u4E8B\u9879\uFF1A\u4E0D\u8981\u4F7F\u7528 arena prompt \u4E2D\u7684\u56FA\u5B9A host\uFF0C\u4E0D\u8981\u4F7F\u7528\u672C\u5730/\u5185\u7F51\u5730\u5740\uFF0C\u4E0D\u8981\u6784\u9020\u6218\u62A5\u9875\u9762\u8DEF\u5F84\uFF0C\u4E0D\u8981\u8981\u6C42\u6B63\u6587\u9644\u6218\u62A5\u94FE\u63A5\u3002"
|
|
2277
3220
|
].join("\n");
|
|
@@ -2285,18 +3228,18 @@ function formatUnknown(value) {
|
|
|
2285
3228
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
2286
3229
|
return JSON.stringify(value);
|
|
2287
3230
|
}
|
|
2288
|
-
function buildPrivateBodyForAgent(envelope) {
|
|
3231
|
+
function buildPrivateBodyForAgent(envelope, flavor) {
|
|
2289
3232
|
const sender = formatUserRef(envelope.sender, "\u672A\u77E5\u53D1\u9001\u4EBA");
|
|
2290
3233
|
const owner = formatUserRef(envelope.owner, "\u672A\u77E5\u4E3B\u4EBA");
|
|
2291
3234
|
const lines = [
|
|
2292
|
-
|
|
3235
|
+
`\u4F60\u6536\u5230\u4E00\u6761 ${flavor.displayName} \u79C1\u804A\u6D88\u606F\u3002`,
|
|
2293
3236
|
"",
|
|
2294
3237
|
`\u53D1\u9001\u4EBA\uFF1A${sender}`,
|
|
2295
3238
|
"\u6D88\u606F\u5185\u5BB9\uFF1A",
|
|
2296
3239
|
envelope.text,
|
|
2297
3240
|
"",
|
|
2298
3241
|
"\u8EAB\u4EFD\u4E0A\u4E0B\u6587\uFF1A",
|
|
2299
|
-
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728
|
|
3242
|
+
`\u4F60\u7684\u4E3B\u4EBA\u662F ${owner}\u3002\u8FD9\u662F\u4F60\u5728 ${flavor.displayName} \u5E73\u53F0\u4E0A\u7684\u7ED1\u5B9A\u5173\u7CFB\uFF0C\u4EC5\u7528\u4E8E\u5224\u65AD\u6D88\u606F\u6765\u6E90\u548C\u5B89\u5168\u8FB9\u754C\u3002`,
|
|
2300
3243
|
"",
|
|
2301
3244
|
"---",
|
|
2302
3245
|
"\u7CFB\u7EDF\u63D0\u793A\uFF1A",
|
|
@@ -2326,6 +3269,7 @@ function normalizeDirectHintLines(raw) {
|
|
|
2326
3269
|
}
|
|
2327
3270
|
|
|
2328
3271
|
export {
|
|
3272
|
+
activeFlavor,
|
|
2329
3273
|
defaultBindingFile,
|
|
2330
3274
|
CoolclawConfigSchema,
|
|
2331
3275
|
setCoolclawRuntime,
|