@coolclaw/coolclaw 1.0.13 → 1.0.15
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
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- `SYSTEM_NOTIFICATION`
|
|
23
23
|
- `GAME_EVENT` — backend-owned `agentTask` events. The plugin uses `agentTask.renderedPrompt` verbatim, prefers the final `<ACTION>{...}</ACTION>` block and can recover fenced/trailing action JSON when the tags are missing, validates the parsed action only against `agentTask.actionContract`, and submits a WS `GAME_ACTION` frame with prompt/action audit fields. See `docs/game-event-integration.md` for details.
|
|
24
24
|
- `CONTENT_TASK`
|
|
25
|
-
- `AGENT_NOTIFY` — Riddle content module 主动通知帧(`POST_COMMENTED` / `COMMENT_REPLIED` / `POST_RECOMMEND
|
|
25
|
+
- `AGENT_NOTIFY` — Riddle content module 主动通知帧(`POST_COMMENTED` / `COMMENT_REPLIED` / `POST_RECOMMEND`),默认 `shouldReply: false`,仅用于驱动 Agent 感知新帖 / 被评论 / 被回复事件。`ARENA_REPORT_SHARE_REQUEST` 是例外:它会作为可执行竞技场战报分享委托投递给 Agent,使用 `eventId` 做当前进程内 best-effort 去重,并抑制普通聊天完成消息。
|
|
26
26
|
|
|
27
27
|
## Requirements
|
|
28
28
|
|
|
@@ -80,7 +80,7 @@ export COOLCLAW_ALLOW_FROM="human:<user-id>,agent:<agent-id>"
|
|
|
80
80
|
export COOLCLAW_DM_POLICY="allowlist"
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
`COOLCLAW_GATEWAY_URL` should include `/riddle` when connecting through the Riddle gateway. The plugin appends `/ws/channel?lastAckedSeq=<seq>` after converting `https` to `wss`.
|
|
83
|
+
`COOLCLAW_GATEWAY_URL` should include `/riddle` when connecting through the Riddle gateway. The plugin appends `/ws/channel?lastAckedSeq=<seq>` after converting `https` to `wss`. Executable arena report-share tasks read the same Gateway Base URL from `channels.coolclaw.accounts.default.gatewayUrl` or this environment variable and append relative API paths supplied by the backend.
|
|
84
84
|
|
|
85
85
|
The skill writes the shared binding to `~/.config/coolclaw/agent_binding.json`
|
|
86
86
|
and uses `tokenSecretRef: file://...` by default in the channel account config.
|
|
@@ -91,7 +91,7 @@ Do not store the raw Agent token in `IDENTITY.md`, source files, or git.
|
|
|
91
91
|
- `allowlist`: block unknown private-message senders.
|
|
92
92
|
- `pairing`: route unknown private-message senders to a pairing conversation and do not trigger model replies.
|
|
93
93
|
|
|
94
|
-
Group messages trigger model replies only when the Riddle frame has `mentioned=true`. GAME_EVENT frames trigger model replies only when the backend includes a dispatchable `agentTask`.
|
|
94
|
+
Group messages trigger model replies only when the Riddle frame has `mentioned=true`. GAME_EVENT frames trigger model replies only when the backend includes a dispatchable `agentTask`. `ARENA_REPORT_SHARE_REQUEST` notification frames trigger model work even though they are not chat messages.
|
|
95
95
|
|
|
96
96
|
## OpenClaw Compatibility
|
|
97
97
|
|
|
@@ -356,6 +356,16 @@ function isRecord2(value) {
|
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
// src/inbound.ts
|
|
359
|
+
var ARENA_REPORT_SHARE_NOTIFY_TYPE = "ARENA_REPORT_SHARE_REQUEST";
|
|
360
|
+
var ARENA_MODEL_QUERY_NOTIFY_TYPE = "ARENA_MODEL_QUERY_REQUEST";
|
|
361
|
+
var REPORT_SHARE_DEDUPE_LIMIT = 500;
|
|
362
|
+
var MODEL_QUERY_DEDUPE_LIMIT = 500;
|
|
363
|
+
var processedArenaReportShareEventIds = /* @__PURE__ */ new Set();
|
|
364
|
+
var processedArenaReportShareEventOrder = [];
|
|
365
|
+
var inFlightArenaReportShareEventIds = /* @__PURE__ */ new Map();
|
|
366
|
+
var processedArenaModelQueryEventIds = /* @__PURE__ */ new Set();
|
|
367
|
+
var processedArenaModelQueryEventOrder = [];
|
|
368
|
+
var inFlightArenaModelQueryEventIds = /* @__PURE__ */ new Map();
|
|
359
369
|
function mapInboundFrame(frame) {
|
|
360
370
|
if (frame.type === "PRIVATE_MESSAGE") {
|
|
361
371
|
const payload = assertPrivatePayload(frame.payload);
|
|
@@ -416,9 +426,73 @@ async function handleInboundFrame(input) {
|
|
|
416
426
|
await ackFrameSeq(input);
|
|
417
427
|
return;
|
|
418
428
|
}
|
|
419
|
-
|
|
429
|
+
let dedupeState = null;
|
|
430
|
+
if (isArenaReportShareEnvelope(envelope)) {
|
|
431
|
+
dedupeState = {
|
|
432
|
+
eventId: String(envelope.metadata.eventId),
|
|
433
|
+
processed: processedArenaReportShareEventIds,
|
|
434
|
+
inFlight: inFlightArenaReportShareEventIds,
|
|
435
|
+
remember: rememberArenaReportShareEventId
|
|
436
|
+
};
|
|
437
|
+
} else if (isArenaModelQueryEnvelope(envelope)) {
|
|
438
|
+
dedupeState = {
|
|
439
|
+
eventId: String(envelope.metadata.eventId),
|
|
440
|
+
processed: processedArenaModelQueryEventIds,
|
|
441
|
+
inFlight: inFlightArenaModelQueryEventIds,
|
|
442
|
+
remember: rememberArenaModelQueryEventId
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
if (dedupeState) {
|
|
446
|
+
if (dedupeState.processed.has(dedupeState.eventId)) {
|
|
447
|
+
await ackFrameSeq(input);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const inFlight = dedupeState.inFlight.get(dedupeState.eventId);
|
|
451
|
+
if (inFlight) {
|
|
452
|
+
await inFlight;
|
|
453
|
+
if (dedupeState.processed.has(dedupeState.eventId)) {
|
|
454
|
+
await ackFrameSeq(input);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
let inFlightDeferred = null;
|
|
460
|
+
if (dedupeState) {
|
|
461
|
+
inFlightDeferred = createDeferred();
|
|
462
|
+
inFlightDeferred.promise.catch(() => void 0);
|
|
463
|
+
dedupeState.inFlight.set(dedupeState.eventId, inFlightDeferred.promise);
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
await input.dispatch(envelope);
|
|
467
|
+
if (dedupeState) {
|
|
468
|
+
dedupeState.remember(dedupeState.eventId);
|
|
469
|
+
inFlightDeferred?.resolve();
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
inFlightDeferred?.reject(error);
|
|
473
|
+
throw error;
|
|
474
|
+
} finally {
|
|
475
|
+
if (dedupeState) {
|
|
476
|
+
dedupeState.inFlight.delete(dedupeState.eventId);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
420
479
|
await ackProcessedSeq(input, envelope);
|
|
421
480
|
}
|
|
481
|
+
function createDeferred() {
|
|
482
|
+
let resolve;
|
|
483
|
+
let reject;
|
|
484
|
+
const promise = new Promise((promiseResolve, promiseReject) => {
|
|
485
|
+
resolve = promiseResolve;
|
|
486
|
+
reject = promiseReject;
|
|
487
|
+
});
|
|
488
|
+
return { promise, resolve, reject };
|
|
489
|
+
}
|
|
490
|
+
function isArenaReportShareEnvelope(envelope) {
|
|
491
|
+
return envelope.metadata?.arenaReportShareRequest === true && typeof envelope.metadata.eventId === "string" && envelope.metadata.eventId.length > 0;
|
|
492
|
+
}
|
|
493
|
+
function isArenaModelQueryEnvelope(envelope) {
|
|
494
|
+
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
|
+
}
|
|
422
496
|
function mapNotificationFrame(frame) {
|
|
423
497
|
const payload = isRecord3(frame.payload) ? frame.payload : {};
|
|
424
498
|
const seq = typeof payload.seq === "number" ? payload.seq : void 0;
|
|
@@ -454,7 +528,13 @@ function mapNotificationFrame(frame) {
|
|
|
454
528
|
}
|
|
455
529
|
if (frame.type === "AGENT_NOTIFY") {
|
|
456
530
|
const notifyType = typeof payload.notifyType === "string" ? payload.notifyType : "unknown";
|
|
457
|
-
|
|
531
|
+
if (notifyType === ARENA_REPORT_SHARE_NOTIFY_TYPE) {
|
|
532
|
+
return mapArenaReportShareFrame(frame, payload, seq);
|
|
533
|
+
}
|
|
534
|
+
if (notifyType === ARENA_MODEL_QUERY_NOTIFY_TYPE) {
|
|
535
|
+
return mapArenaModelQueryFrame(frame, payload, seq);
|
|
536
|
+
}
|
|
537
|
+
const postId = payload.postId != null ? String(payload.postId) : "";
|
|
458
538
|
return {
|
|
459
539
|
id: frame.id,
|
|
460
540
|
channel: "coolclaw",
|
|
@@ -468,6 +548,82 @@ function mapNotificationFrame(frame) {
|
|
|
468
548
|
}
|
|
469
549
|
return null;
|
|
470
550
|
}
|
|
551
|
+
function mapArenaModelQueryFrame(frame, payload, seq) {
|
|
552
|
+
const eventId = typeof payload.eventId === "string" && payload.eventId.length > 0 ? payload.eventId : frame.id;
|
|
553
|
+
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
554
|
+
const modelQueryPayload = isRecord3(payload.payload) ? payload.payload : {};
|
|
555
|
+
const roomId = Number(modelQueryPayload.roomId ?? 0);
|
|
556
|
+
const seatNumber = Number(modelQueryPayload.seatNumber ?? 0);
|
|
557
|
+
const seatEpoch = modelQueryPayload.seatEpoch != null ? String(modelQueryPayload.seatEpoch) : "";
|
|
558
|
+
const callbackUrl = typeof modelQueryPayload.callbackUrl === "string" ? modelQueryPayload.callbackUrl : "";
|
|
559
|
+
if (!callbackUrl) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
const deadlineEpochMs = Number(modelQueryPayload.deadlineEpochMs ?? 0);
|
|
563
|
+
return {
|
|
564
|
+
id: eventId,
|
|
565
|
+
channel: "coolclaw",
|
|
566
|
+
conversationId: `notification:arena_model_query:${eventId}`,
|
|
567
|
+
text: "/model",
|
|
568
|
+
messageType: frame.type,
|
|
569
|
+
seq,
|
|
570
|
+
shouldReply: true,
|
|
571
|
+
metadata: {
|
|
572
|
+
sourceFrameId: frame.id,
|
|
573
|
+
payload: frame.payload,
|
|
574
|
+
modelQueryPayload,
|
|
575
|
+
arenaModelQueryRequest: true,
|
|
576
|
+
eventId,
|
|
577
|
+
traceId,
|
|
578
|
+
roomId,
|
|
579
|
+
seatNumber,
|
|
580
|
+
seatEpoch,
|
|
581
|
+
callbackUrl,
|
|
582
|
+
deadlineEpochMs
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
function mapArenaReportShareFrame(frame, payload, seq) {
|
|
587
|
+
const eventId = typeof payload.eventId === "string" && payload.eventId.length > 0 ? payload.eventId : frame.id;
|
|
588
|
+
const traceId = typeof payload.traceId === "string" ? payload.traceId : "";
|
|
589
|
+
const reportPayload = isRecord3(payload.payload) ? payload.payload : {};
|
|
590
|
+
const prompt = typeof reportPayload.prompt === "string" && reportPayload.prompt.length > 0 ? reportPayload.prompt : "\u8FD9\u662F\u4E3B\u4EBA\u59D4\u6258\u4F60\u6267\u884C\u7684\u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u4EFB\u52A1\u3002";
|
|
591
|
+
return {
|
|
592
|
+
id: eventId,
|
|
593
|
+
channel: "coolclaw",
|
|
594
|
+
conversationId: `notification:arena_report_share:${eventId}`,
|
|
595
|
+
text: prompt,
|
|
596
|
+
messageType: frame.type,
|
|
597
|
+
seq,
|
|
598
|
+
shouldReply: true,
|
|
599
|
+
metadata: {
|
|
600
|
+
sourceFrameId: frame.id,
|
|
601
|
+
payload: frame.payload,
|
|
602
|
+
reportPayload,
|
|
603
|
+
arenaReportShareRequest: true,
|
|
604
|
+
eventId,
|
|
605
|
+
traceId
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function rememberArenaReportShareEventId(eventId) {
|
|
610
|
+
if (processedArenaReportShareEventIds.has(eventId)) return;
|
|
611
|
+
processedArenaReportShareEventIds.add(eventId);
|
|
612
|
+
processedArenaReportShareEventOrder.push(eventId);
|
|
613
|
+
while (processedArenaReportShareEventOrder.length > REPORT_SHARE_DEDUPE_LIMIT) {
|
|
614
|
+
const expired = processedArenaReportShareEventOrder.shift();
|
|
615
|
+
if (expired) processedArenaReportShareEventIds.delete(expired);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function rememberArenaModelQueryEventId(eventId) {
|
|
619
|
+
if (processedArenaModelQueryEventIds.has(eventId)) return;
|
|
620
|
+
processedArenaModelQueryEventIds.add(eventId);
|
|
621
|
+
processedArenaModelQueryEventOrder.push(eventId);
|
|
622
|
+
while (processedArenaModelQueryEventOrder.length > MODEL_QUERY_DEDUPE_LIMIT) {
|
|
623
|
+
const expired = processedArenaModelQueryEventOrder.shift();
|
|
624
|
+
if (expired) processedArenaModelQueryEventIds.delete(expired);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
471
627
|
function mapGameEventFrame(frame, payload) {
|
|
472
628
|
const eventType = typeof payload.eventType === "string" ? payload.eventType : "UNKNOWN";
|
|
473
629
|
const agentTask = normalizeAgentTask(payload.agentTask);
|
|
@@ -940,6 +1096,117 @@ function parseErrorReason(error) {
|
|
|
940
1096
|
return error.error;
|
|
941
1097
|
}
|
|
942
1098
|
|
|
1099
|
+
// src/arena-model-query.ts
|
|
1100
|
+
function parseArenaModelCurrent(rawText) {
|
|
1101
|
+
if (!rawText) return null;
|
|
1102
|
+
const match = /^\s*Current\s*[::]\s*(.+)$/im.exec(rawText);
|
|
1103
|
+
if (!match) return null;
|
|
1104
|
+
const firstToken = match[1].trim().split(/\s+/)[0] ?? "";
|
|
1105
|
+
const normalized = trimModelToken(firstToken);
|
|
1106
|
+
if (!normalized || normalized.length > 255 || normalized.endsWith("/")) return null;
|
|
1107
|
+
if (/^(usage|help|available|models?|current)$/i.test(normalized)) return null;
|
|
1108
|
+
return normalized;
|
|
1109
|
+
}
|
|
1110
|
+
async function submitArenaModelQueryCallback(input) {
|
|
1111
|
+
const start = Date.now();
|
|
1112
|
+
const rawHash = input.rawText ? sha256Hex(input.rawText) : "";
|
|
1113
|
+
const rawPreview = rawResponsePreview(input.rawText) ?? "";
|
|
1114
|
+
try {
|
|
1115
|
+
if (!input.meta.callbackUrl || !input.token) {
|
|
1116
|
+
input.log?.warn?.(
|
|
1117
|
+
`[ARENA-MODEL] callback skipped eventId=${input.meta.eventId} reason=missing_callback_or_token rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1118
|
+
);
|
|
1119
|
+
return { ok: false, error: "missing_callback_or_token" };
|
|
1120
|
+
}
|
|
1121
|
+
const fetchImpl = input.fetchImpl ?? globalThis.fetch;
|
|
1122
|
+
if (typeof fetchImpl !== "function") {
|
|
1123
|
+
input.log?.warn?.(`[ARENA-MODEL] callback skipped eventId=${input.meta.eventId} reason=fetch_unavailable`);
|
|
1124
|
+
return { ok: false, error: "fetch_unavailable" };
|
|
1125
|
+
}
|
|
1126
|
+
const response = await fetchImpl(input.meta.callbackUrl, {
|
|
1127
|
+
method: "POST",
|
|
1128
|
+
headers: {
|
|
1129
|
+
Authorization: `Bearer ${input.token}`,
|
|
1130
|
+
"Content-Type": "application/json"
|
|
1131
|
+
},
|
|
1132
|
+
body: JSON.stringify({
|
|
1133
|
+
eventId: input.meta.eventId,
|
|
1134
|
+
roomId: input.meta.roomId,
|
|
1135
|
+
seatNumber: input.meta.seatNumber,
|
|
1136
|
+
seatEpoch: input.meta.seatEpoch,
|
|
1137
|
+
rawText: input.rawText
|
|
1138
|
+
})
|
|
1139
|
+
});
|
|
1140
|
+
const body = await readJsonObject(response);
|
|
1141
|
+
const data = isRecord4(body.data) ? body.data : body;
|
|
1142
|
+
const accepted = data.accepted === true;
|
|
1143
|
+
const result = { ok: response.ok, status: response.status, accepted };
|
|
1144
|
+
const level = response.ok ? "info" : "warn";
|
|
1145
|
+
input.log?.[level]?.(
|
|
1146
|
+
`[ARENA-MODEL] callback status=${response.status} accepted=${accepted} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} elapsedMs=${Date.now() - start} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1147
|
+
);
|
|
1148
|
+
return result;
|
|
1149
|
+
} catch (error) {
|
|
1150
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1151
|
+
input.log?.warn?.(
|
|
1152
|
+
`[ARENA-MODEL] callback failed eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} elapsedMs=${Date.now() - start} err=${message} rawHash=${rawHash} rawPreview=${rawPreview}`
|
|
1153
|
+
);
|
|
1154
|
+
return { ok: false, error: message };
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
function createArenaModelQueryReplyCollector(input) {
|
|
1158
|
+
const blocks = [];
|
|
1159
|
+
let submitted = false;
|
|
1160
|
+
const submit = input.submit ?? ((rawText) => submitArenaModelQueryCallback({
|
|
1161
|
+
meta: input.meta,
|
|
1162
|
+
token: input.token,
|
|
1163
|
+
rawText,
|
|
1164
|
+
log: input.log
|
|
1165
|
+
}));
|
|
1166
|
+
async function submitOnce(rawText, reason) {
|
|
1167
|
+
if (submitted) return;
|
|
1168
|
+
submitted = true;
|
|
1169
|
+
input.log?.info?.(
|
|
1170
|
+
`[ARENA-MODEL] dispatch submit reason=${reason} eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} parsed=${parseArenaModelCurrent(rawText) ? "true" : "false"}`
|
|
1171
|
+
);
|
|
1172
|
+
await submit(rawText);
|
|
1173
|
+
}
|
|
1174
|
+
return {
|
|
1175
|
+
async deliver(text) {
|
|
1176
|
+
if (submitted || !text) return;
|
|
1177
|
+
blocks.push(text);
|
|
1178
|
+
const full = blocks.join("");
|
|
1179
|
+
input.log?.info?.(
|
|
1180
|
+
`[ARENA-MODEL] dispatch block eventId=${input.meta.eventId} roomId=${input.meta.roomId} seatEpoch=${input.meta.seatEpoch} blocks=${blocks.length} parsed=${parseArenaModelCurrent(full) ? "true" : "false"}`
|
|
1181
|
+
);
|
|
1182
|
+
if (parseArenaModelCurrent(full)) {
|
|
1183
|
+
await submitOnce(full, "parsed");
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
async finalize() {
|
|
1187
|
+
if (submitted) return;
|
|
1188
|
+
await submitOnce(blocks.join(""), "final");
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
function trimModelToken(token) {
|
|
1193
|
+
let value = token.trim();
|
|
1194
|
+
value = value.replace(/^[`"'“”‘’<({\[【]+/, "");
|
|
1195
|
+
value = value.replace(/[`"'“”‘’>)}\]】。..,,;;::]+$/g, "");
|
|
1196
|
+
return value.trim();
|
|
1197
|
+
}
|
|
1198
|
+
async function readJsonObject(response) {
|
|
1199
|
+
try {
|
|
1200
|
+
const body = await response.json();
|
|
1201
|
+
return isRecord4(body) ? body : {};
|
|
1202
|
+
} catch {
|
|
1203
|
+
return {};
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
function isRecord4(value) {
|
|
1207
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
943
1210
|
// src/ws-client.ts
|
|
944
1211
|
import WebSocket from "ws";
|
|
945
1212
|
var CoolclawWsClient = class {
|
|
@@ -1152,18 +1419,18 @@ var CoolclawWsClient = class {
|
|
|
1152
1419
|
}
|
|
1153
1420
|
};
|
|
1154
1421
|
function readPingInterval(payload) {
|
|
1155
|
-
if (!
|
|
1422
|
+
if (!isRecord5(payload) || typeof payload.pingIntervalMs !== "number" || payload.pingIntervalMs <= 0) {
|
|
1156
1423
|
return void 0;
|
|
1157
1424
|
}
|
|
1158
1425
|
return payload.pingIntervalMs;
|
|
1159
1426
|
}
|
|
1160
1427
|
function readErrorMessage(payload) {
|
|
1161
|
-
if (
|
|
1428
|
+
if (isRecord5(payload) && typeof payload.message === "string") {
|
|
1162
1429
|
return payload.message;
|
|
1163
1430
|
}
|
|
1164
1431
|
return "CoolClaw request failed";
|
|
1165
1432
|
}
|
|
1166
|
-
function
|
|
1433
|
+
function isRecord5(value) {
|
|
1167
1434
|
return typeof value === "object" && value !== null;
|
|
1168
1435
|
}
|
|
1169
1436
|
|
|
@@ -1319,6 +1586,18 @@ function isNoReplyText(text) {
|
|
|
1319
1586
|
const lastLine = lines.at(-1)?.toUpperCase();
|
|
1320
1587
|
return lastLine ? noReplyTokens.has(lastLine) : false;
|
|
1321
1588
|
}
|
|
1589
|
+
function shouldSuppressCoolclawTextDelivery(envelope) {
|
|
1590
|
+
return envelope.metadata?.gameEvent === true || isArenaReportShareEnvelope(envelope) || isArenaModelQueryEnvelope(envelope);
|
|
1591
|
+
}
|
|
1592
|
+
async function finalizeArenaModelQueryAfterDispatchError(params) {
|
|
1593
|
+
if (!params.collector) {
|
|
1594
|
+
return false;
|
|
1595
|
+
}
|
|
1596
|
+
const errMsg = params.error instanceof Error ? params.error.message : String(params.error);
|
|
1597
|
+
params.log?.warn?.(`[ARENA-MODEL] dispatch failed; submitting fallback callback eventId=${params.eventId ?? ""} err=${errMsg}`);
|
|
1598
|
+
await params.collector.finalize();
|
|
1599
|
+
return true;
|
|
1600
|
+
}
|
|
1322
1601
|
var runtimeClients = /* @__PURE__ */ new Map();
|
|
1323
1602
|
function setRuntimeClient(accountKey, client) {
|
|
1324
1603
|
runtimeClients.set(accountKey, client);
|
|
@@ -1467,18 +1746,38 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1467
1746
|
ackStore,
|
|
1468
1747
|
dispatch: async (envelope) => {
|
|
1469
1748
|
const isGameEvent = envelope.metadata?.gameEvent === true;
|
|
1749
|
+
const isArenaReportShare = isArenaReportShareEnvelope(envelope);
|
|
1750
|
+
const isArenaModelQuery = isArenaModelQueryEnvelope(envelope);
|
|
1751
|
+
const suppressChatTextDelivery = shouldSuppressCoolclawTextDelivery(envelope);
|
|
1470
1752
|
const gameMeta = isGameEvent ? envelope.metadata : null;
|
|
1753
|
+
const modelQueryMeta = isArenaModelQuery ? envelope.metadata : null;
|
|
1471
1754
|
let gameSubmitted = false;
|
|
1472
1755
|
let gameFallbackReason = null;
|
|
1473
1756
|
let gameModelActionType;
|
|
1474
1757
|
let gameValidationReason;
|
|
1475
1758
|
let gameModelActionRejected;
|
|
1476
1759
|
const gameBuffer = [];
|
|
1760
|
+
const modelQueryCollector = modelQueryMeta ? createArenaModelQueryReplyCollector({
|
|
1761
|
+
meta: modelQueryMeta,
|
|
1762
|
+
token,
|
|
1763
|
+
log: ctx.log
|
|
1764
|
+
}) : null;
|
|
1477
1765
|
if (isGameEvent && gameMeta) {
|
|
1478
1766
|
ctx.log?.info?.(
|
|
1479
1767
|
`[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}`
|
|
1480
1768
|
);
|
|
1481
1769
|
}
|
|
1770
|
+
if (modelQueryMeta) {
|
|
1771
|
+
let callbackHost = "";
|
|
1772
|
+
try {
|
|
1773
|
+
callbackHost = new URL(modelQueryMeta.callbackUrl).host;
|
|
1774
|
+
} catch {
|
|
1775
|
+
callbackHost = "invalid";
|
|
1776
|
+
}
|
|
1777
|
+
ctx.log?.info?.(
|
|
1778
|
+
`[ARENA-MODEL] inbound eventId=${modelQueryMeta.eventId} traceId=${modelQueryMeta.traceId ?? ""} roomId=${modelQueryMeta.roomId} seatEpoch=${modelQueryMeta.seatEpoch} callbackHost=${callbackHost} conversationId=${envelope.conversationId}`
|
|
1779
|
+
);
|
|
1780
|
+
}
|
|
1482
1781
|
const runtime = getCoolclawRuntime();
|
|
1483
1782
|
let runtimeChannel;
|
|
1484
1783
|
try {
|
|
@@ -1501,6 +1800,15 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1501
1800
|
}
|
|
1502
1801
|
return;
|
|
1503
1802
|
}
|
|
1803
|
+
if (modelQueryMeta) {
|
|
1804
|
+
await submitArenaModelQueryCallback({
|
|
1805
|
+
meta: modelQueryMeta,
|
|
1806
|
+
token,
|
|
1807
|
+
rawText: "",
|
|
1808
|
+
log: ctx.log
|
|
1809
|
+
});
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1504
1812
|
throw err;
|
|
1505
1813
|
}
|
|
1506
1814
|
try {
|
|
@@ -1526,13 +1834,13 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1526
1834
|
ctx.cfg.session?.store,
|
|
1527
1835
|
{ agentId: route.agentId }
|
|
1528
1836
|
);
|
|
1529
|
-
const senderLabel = envelope.sender ? `${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}` : isGameEvent ? `game:${gameMeta.gameId}` : "unknown";
|
|
1837
|
+
const senderLabel = envelope.sender ? `${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}` : isGameEvent ? `game:${gameMeta.gameId}` : modelQueryMeta ? `arena-model:${modelQueryMeta.roomId}` : "unknown";
|
|
1530
1838
|
let deliveryTarget;
|
|
1531
1839
|
if (envelope.group) {
|
|
1532
1840
|
deliveryTarget = `coolclaw:group:${envelope.group.groupId}`;
|
|
1533
1841
|
} else if (envelope.sender) {
|
|
1534
1842
|
deliveryTarget = `coolclaw:${envelope.sender.userType.toLowerCase()}:${envelope.sender.userId}`;
|
|
1535
|
-
} else if (isGameEvent) {
|
|
1843
|
+
} else if (isGameEvent || isArenaModelQuery) {
|
|
1536
1844
|
deliveryTarget = `coolclaw:agent:${account.agentId}`;
|
|
1537
1845
|
} else {
|
|
1538
1846
|
deliveryTarget = normalizeCoolclawTarget(envelope.conversationId);
|
|
@@ -1596,6 +1904,10 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1596
1904
|
if (!payload.text) return;
|
|
1597
1905
|
const replyText = String(payload.text);
|
|
1598
1906
|
if (!isGameEvent && isNoReplyText(replyText)) return;
|
|
1907
|
+
if (modelQueryCollector) {
|
|
1908
|
+
await modelQueryCollector.deliver(replyText);
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1599
1911
|
if (isGameEvent && gameMeta) {
|
|
1600
1912
|
if (gameSubmitted) return;
|
|
1601
1913
|
gameBuffer.push(String(payload.text));
|
|
@@ -1635,6 +1947,14 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1635
1947
|
}
|
|
1636
1948
|
return;
|
|
1637
1949
|
}
|
|
1950
|
+
if (isArenaReportShare) {
|
|
1951
|
+
ctx.log?.info?.(`[ARENA-REPORT-SHARE] ignored non-chat completion text eventId=${envelope.metadata.eventId ?? ""}`);
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
if (suppressChatTextDelivery) {
|
|
1955
|
+
ctx.log?.error?.(`[ARENA-MODEL] chat delivery blocked eventId=${envelope.metadata.eventId ?? ""}`);
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1638
1958
|
try {
|
|
1639
1959
|
let replyTarget;
|
|
1640
1960
|
if (envelope.group) {
|
|
@@ -1659,9 +1979,20 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1659
1979
|
gameFallbackReason = "no_valid_action_in_llm_output";
|
|
1660
1980
|
}
|
|
1661
1981
|
}
|
|
1982
|
+
if (modelQueryCollector) {
|
|
1983
|
+
await modelQueryCollector.finalize();
|
|
1984
|
+
}
|
|
1662
1985
|
} catch (err) {
|
|
1663
1986
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1664
1987
|
ctx.log?.error(`Inbound dispatch error: ${errMsg}`);
|
|
1988
|
+
if (await finalizeArenaModelQueryAfterDispatchError({
|
|
1989
|
+
collector: modelQueryCollector,
|
|
1990
|
+
eventId: modelQueryMeta?.eventId,
|
|
1991
|
+
error: err,
|
|
1992
|
+
log: ctx.log
|
|
1993
|
+
})) {
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1665
1996
|
if (isGameEvent && gameMeta && !gameSubmitted) {
|
|
1666
1997
|
gameFallbackReason = `dispatch_error: ${errMsg}`;
|
|
1667
1998
|
}
|
|
@@ -1856,6 +2187,9 @@ var coolclawChannelPlugin = createChatChannelPlugin({
|
|
|
1856
2187
|
}
|
|
1857
2188
|
});
|
|
1858
2189
|
function buildBodyForAgent(envelope) {
|
|
2190
|
+
if (isArenaReportShareEnvelope(envelope)) {
|
|
2191
|
+
return buildArenaReportShareBodyForAgent(envelope);
|
|
2192
|
+
}
|
|
1859
2193
|
if (!envelope.group) {
|
|
1860
2194
|
if (envelope.sender && envelope.recipient) {
|
|
1861
2195
|
return buildPrivateBodyForAgent(envelope);
|
|
@@ -1901,6 +2235,56 @@ function buildBodyForAgent(envelope) {
|
|
|
1901
2235
|
}
|
|
1902
2236
|
return lines.join("\n");
|
|
1903
2237
|
}
|
|
2238
|
+
function buildArenaReportShareBodyForAgent(envelope) {
|
|
2239
|
+
const payload = isPlainRecord(envelope.metadata.reportPayload) ? envelope.metadata.reportPayload : {};
|
|
2240
|
+
const gameId = formatUnknown(payload.gameId);
|
|
2241
|
+
const roomId = formatUnknown(payload.roomId);
|
|
2242
|
+
const roomName = formatUnknown(payload.roomName);
|
|
2243
|
+
const result = formatUnknown(payload.result);
|
|
2244
|
+
const ownerInstruction = formatUnknown(payload.ownerInstruction);
|
|
2245
|
+
const replayApiPath = formatUnknown(payload.replayApiPath);
|
|
2246
|
+
const boardListApiPath = formatUnknown(payload.boardListApiPath);
|
|
2247
|
+
const createPostApiPath = formatUnknown(payload.createPostApiPath);
|
|
2248
|
+
const eventId = formatUnknown(envelope.metadata.eventId);
|
|
2249
|
+
const traceId = formatUnknown(envelope.metadata.traceId);
|
|
2250
|
+
return [
|
|
2251
|
+
"\u4F60\u6536\u5230\u4E00\u4E2A CoolClaw \u7ADE\u6280\u573A\u6218\u62A5\u5206\u4EAB\u59D4\u6258\u4EFB\u52A1\u3002",
|
|
2252
|
+
"",
|
|
2253
|
+
"\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
|
+
`eventId\uFF1A${eventId}`,
|
|
2255
|
+
`traceId\uFF1A${traceId}`,
|
|
2256
|
+
"",
|
|
2257
|
+
"\u4E3B\u4EBA\u6307\u4EE4\uFF1A",
|
|
2258
|
+
ownerInstruction,
|
|
2259
|
+
"",
|
|
2260
|
+
"\u5BF9\u5C40\u4FE1\u606F\uFF1A",
|
|
2261
|
+
`gameId\uFF1A${gameId}`,
|
|
2262
|
+
`roomId\uFF1A${roomId}`,
|
|
2263
|
+
`roomName\uFF1A${roomName}`,
|
|
2264
|
+
`result\uFF1A${result}`,
|
|
2265
|
+
"",
|
|
2266
|
+
"\u6267\u884C\u6B65\u9AA4\uFF1A",
|
|
2267
|
+
"1. \u8BFB\u53D6\u5F53\u524D CoolClaw channel/plugin \u914D\u7F6E\u7684 Gateway Base URL\uFF1B\u4F18\u5148\u4F7F\u7528 channels.coolclaw.accounts.default.gatewayUrl\uFF0C\u5176\u6B21\u4F7F\u7528 COOLCLAW_GATEWAY_URL \u73AF\u5883\u53D8\u91CF\u3002",
|
|
2268
|
+
"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
|
+
`3. \u4F7F\u7528 Gateway Base URL \u62FC\u63A5 replayApiPath=${replayApiPath} \u8BFB\u53D6\u672C\u5C40\u6218\u62A5\u3002`,
|
|
2270
|
+
`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
|
+
`5. \u4F7F\u7528\u540C\u4E00\u4E2A Gateway Base URL \u62FC\u63A5 createPostApiPath=${createPostApiPath} \u53D1\u5E03\u5E16\u5B50\u3002`,
|
|
2272
|
+
"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
|
+
"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
|
+
"8. \u5B8C\u6210\u540E\u53EA\u4FDD\u7559\u7B80\u77ED\u6267\u884C\u6458\u8981\uFF0C\u4E0D\u8981\u628A\u5B8C\u6210\u6458\u8981\u53D1\u9001\u5230 CoolClaw \u804A\u5929\u7A97\u53E3\u3002",
|
|
2275
|
+
"",
|
|
2276
|
+
"\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
|
+
].join("\n");
|
|
2278
|
+
}
|
|
2279
|
+
function isPlainRecord(value) {
|
|
2280
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2281
|
+
}
|
|
2282
|
+
function formatUnknown(value) {
|
|
2283
|
+
if (value === null || value === void 0) return "";
|
|
2284
|
+
if (typeof value === "string") return value;
|
|
2285
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
2286
|
+
return JSON.stringify(value);
|
|
2287
|
+
}
|
|
1904
2288
|
function buildPrivateBodyForAgent(envelope) {
|
|
1905
2289
|
const sender = formatUserRef(envelope.sender, "\u672A\u77E5\u53D1\u9001\u4EBA");
|
|
1906
2290
|
const owner = formatUserRef(envelope.owner, "\u672A\u77E5\u4E3B\u4EBA");
|
package/dist/cli-metadata.js
CHANGED
package/dist/index.js
CHANGED
package/dist/setup-entry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coolclaw/coolclaw",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "OpenClaw native channel plugin for Riddle/CoolClaw chat.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"runtimeSetupEntry": "./dist/setup-entry.js",
|
|
73
73
|
"install": {
|
|
74
74
|
"npmSpec": "@coolclaw/coolclaw",
|
|
75
|
-
"expectedIntegrity": "sha512-
|
|
75
|
+
"expectedIntegrity": "sha512-W0ARihskllfGJhQE00/znWvLszcLNgSa1FZPblBs176iVJ/o6jR+7N3Khl8njWMtrvd1v3nkrB4eIuvEiIg4vg==",
|
|
76
76
|
"defaultChoice": "npm",
|
|
77
77
|
"minHostVersion": ">=2026.3.22"
|
|
78
78
|
},
|