mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 06:47:26 +00:00
fix: agent-only announce path, BB message IDs, sender identity, SSRF allowlist (#23970)
* fix(agents): defer announces until descendant cleanup settles * fix(bluebubbles): harden message metadata extraction * feat(contributors): rank by composite score (commits, PRs, LOC, tenure) * refactor(control-ui): move method guard after path checks to improve request handling * fix subagent completion announce when only current run is pending * fix(subagents): keep orchestrator runs active until descendants finish * fix: prepare PR feedback follow-ups (#23970) (thanks @tyler6204)
This commit is contained in:
@@ -23,31 +23,43 @@ export function extractBlueBubblesMessageId(payload: unknown): string {
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return "unknown";
|
||||
}
|
||||
const record = payload as Record<string, unknown>;
|
||||
const data =
|
||||
record.data && typeof record.data === "object"
|
||||
? (record.data as Record<string, unknown>)
|
||||
|
||||
const asRecord = (value: unknown): Record<string, unknown> | null =>
|
||||
value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null;
|
||||
const candidates = [
|
||||
record.messageId,
|
||||
record.messageGuid,
|
||||
record.message_guid,
|
||||
record.guid,
|
||||
record.id,
|
||||
data?.messageId,
|
||||
data?.messageGuid,
|
||||
data?.message_guid,
|
||||
data?.message_id,
|
||||
data?.guid,
|
||||
data?.id,
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (typeof candidate === "string" && candidate.trim()) {
|
||||
return candidate.trim();
|
||||
|
||||
const record = payload as Record<string, unknown>;
|
||||
const dataRecord = asRecord(record.data);
|
||||
const resultRecord = asRecord(record.result);
|
||||
const payloadRecord = asRecord(record.payload);
|
||||
const messageRecord = asRecord(record.message);
|
||||
const dataArrayFirst = Array.isArray(record.data) ? asRecord(record.data[0]) : null;
|
||||
|
||||
const roots = [record, dataRecord, resultRecord, payloadRecord, messageRecord, dataArrayFirst];
|
||||
|
||||
for (const root of roots) {
|
||||
if (!root) {
|
||||
continue;
|
||||
}
|
||||
if (typeof candidate === "number" && Number.isFinite(candidate)) {
|
||||
return String(candidate);
|
||||
const candidates = [
|
||||
root.message_id,
|
||||
root.messageId,
|
||||
root.messageGuid,
|
||||
root.message_guid,
|
||||
root.guid,
|
||||
root.id,
|
||||
root.uuid,
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (typeof candidate === "string" && candidate.trim()) {
|
||||
return candidate.trim();
|
||||
}
|
||||
if (typeof candidate === "number" && Number.isFinite(candidate)) {
|
||||
return String(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@@ -721,6 +721,30 @@ describe("send", () => {
|
||||
expect(result.messageId).toBe("msg-guid-789");
|
||||
});
|
||||
|
||||
it("extracts top-level message_id from response payload", async () => {
|
||||
mockResolvedHandleTarget();
|
||||
mockSendResponse({ message_id: "bb-msg-321" });
|
||||
|
||||
const result = await sendMessageBlueBubbles("+15551234567", "Hello", {
|
||||
serverUrl: "http://localhost:1234",
|
||||
password: "test",
|
||||
});
|
||||
|
||||
expect(result.messageId).toBe("bb-msg-321");
|
||||
});
|
||||
|
||||
it("extracts nested result.message_id from response payload", async () => {
|
||||
mockResolvedHandleTarget();
|
||||
mockSendResponse({ result: { message_id: "bb-msg-654" } });
|
||||
|
||||
const result = await sendMessageBlueBubbles("+15551234567", "Hello", {
|
||||
serverUrl: "http://localhost:1234",
|
||||
password: "test",
|
||||
});
|
||||
|
||||
expect(result.messageId).toBe("bb-msg-654");
|
||||
});
|
||||
|
||||
it("resolves credentials from config", async () => {
|
||||
mockResolvedHandleTarget();
|
||||
mockSendResponse({ data: { guid: "msg-123" } });
|
||||
|
||||
Reference in New Issue
Block a user