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:
Tyler Yust
2026-03-01 22:52:11 -08:00
committed by GitHub
parent cfba64c9db
commit f918b336d1
22 changed files with 814 additions and 155 deletions

View File

@@ -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";
}

View File

@@ -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" } });