mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:51:37 +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:
@@ -18,6 +18,14 @@ function parseConversationInfoPayload(text: string): Record<string, unknown> {
|
||||
return JSON.parse(match[1]) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function parseSenderInfoPayload(text: string): Record<string, unknown> {
|
||||
const match = text.match(/Sender \(untrusted metadata\):\n```json\n([\s\S]*?)\n```/);
|
||||
if (!match?.[1]) {
|
||||
throw new Error("missing sender info json block");
|
||||
}
|
||||
return JSON.parse(match[1]) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
describe("buildInboundMetaSystemPrompt", () => {
|
||||
it("includes session-stable routing fields", () => {
|
||||
const prompt = buildInboundMetaSystemPrompt({
|
||||
@@ -147,6 +155,29 @@ describe("buildInboundUserContextPrefix", () => {
|
||||
expect(conversationInfo["sender"]).toBe("+15551234567");
|
||||
});
|
||||
|
||||
it("prefers SenderName in conversation info sender identity", () => {
|
||||
const text = buildInboundUserContextPrefix({
|
||||
ChatType: "group",
|
||||
SenderName: " Tyler ",
|
||||
SenderId: " +15551234567 ",
|
||||
} as TemplateContext);
|
||||
|
||||
const conversationInfo = parseConversationInfoPayload(text);
|
||||
expect(conversationInfo["sender"]).toBe("Tyler");
|
||||
});
|
||||
|
||||
it("includes sender metadata block for direct chats", () => {
|
||||
const text = buildInboundUserContextPrefix({
|
||||
ChatType: "direct",
|
||||
SenderName: "Tyler",
|
||||
SenderId: "+15551234567",
|
||||
} as TemplateContext);
|
||||
|
||||
const senderInfo = parseSenderInfoPayload(text);
|
||||
expect(senderInfo["label"]).toBe("Tyler (+15551234567)");
|
||||
expect(senderInfo["id"]).toBe("+15551234567");
|
||||
});
|
||||
|
||||
it("includes formatted timestamp in conversation info when provided", () => {
|
||||
const text = buildInboundUserContextPrefix({
|
||||
ChatType: "group",
|
||||
@@ -187,7 +218,7 @@ describe("buildInboundUserContextPrefix", () => {
|
||||
expect(conversationInfo["message_id"]).toBe("msg-123");
|
||||
});
|
||||
|
||||
it("includes message_id_full when it differs from message_id", () => {
|
||||
it("prefers MessageSid when both MessageSid and MessageSidFull are present", () => {
|
||||
const text = buildInboundUserContextPrefix({
|
||||
ChatType: "group",
|
||||
MessageSid: "short-id",
|
||||
@@ -196,18 +227,18 @@ describe("buildInboundUserContextPrefix", () => {
|
||||
|
||||
const conversationInfo = parseConversationInfoPayload(text);
|
||||
expect(conversationInfo["message_id"]).toBe("short-id");
|
||||
expect(conversationInfo["message_id_full"]).toBe("full-provider-message-id");
|
||||
expect(conversationInfo["message_id_full"]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("omits message_id_full when it matches message_id", () => {
|
||||
it("falls back to MessageSidFull when MessageSid is missing", () => {
|
||||
const text = buildInboundUserContextPrefix({
|
||||
ChatType: "group",
|
||||
MessageSid: "same-id",
|
||||
MessageSidFull: "same-id",
|
||||
MessageSid: " ",
|
||||
MessageSidFull: "full-provider-message-id",
|
||||
} as TemplateContext);
|
||||
|
||||
const conversationInfo = parseConversationInfoPayload(text);
|
||||
expect(conversationInfo["message_id"]).toBe("same-id");
|
||||
expect(conversationInfo["message_id"]).toBe("full-provider-message-id");
|
||||
expect(conversationInfo["message_id_full"]).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
@@ -88,21 +88,20 @@ export function buildInboundUserContextPrefix(ctx: TemplateContext): string {
|
||||
|
||||
const messageId = safeTrim(ctx.MessageSid);
|
||||
const messageIdFull = safeTrim(ctx.MessageSidFull);
|
||||
const resolvedMessageId = messageId ?? messageIdFull;
|
||||
const timestampStr = formatConversationTimestamp(ctx.Timestamp);
|
||||
|
||||
const conversationInfo = {
|
||||
message_id: isDirect ? undefined : messageId,
|
||||
message_id_full: isDirect
|
||||
? undefined
|
||||
: messageIdFull && messageIdFull !== messageId
|
||||
? messageIdFull
|
||||
: undefined,
|
||||
message_id: isDirect ? undefined : resolvedMessageId,
|
||||
reply_to_id: isDirect ? undefined : safeTrim(ctx.ReplyToId),
|
||||
sender_id: isDirect ? undefined : safeTrim(ctx.SenderId),
|
||||
conversation_label: isDirect ? undefined : safeTrim(ctx.ConversationLabel),
|
||||
sender: isDirect
|
||||
? undefined
|
||||
: (safeTrim(ctx.SenderE164) ?? safeTrim(ctx.SenderId) ?? safeTrim(ctx.SenderUsername)),
|
||||
: (safeTrim(ctx.SenderName) ??
|
||||
safeTrim(ctx.SenderE164) ??
|
||||
safeTrim(ctx.SenderId) ??
|
||||
safeTrim(ctx.SenderUsername)),
|
||||
timestamp: timestampStr,
|
||||
group_subject: safeTrim(ctx.GroupSubject),
|
||||
group_channel: safeTrim(ctx.GroupChannel),
|
||||
@@ -131,20 +130,20 @@ export function buildInboundUserContextPrefix(ctx: TemplateContext): string {
|
||||
);
|
||||
}
|
||||
|
||||
const senderInfo = isDirect
|
||||
? undefined
|
||||
: {
|
||||
label: resolveSenderLabel({
|
||||
name: safeTrim(ctx.SenderName),
|
||||
username: safeTrim(ctx.SenderUsername),
|
||||
tag: safeTrim(ctx.SenderTag),
|
||||
e164: safeTrim(ctx.SenderE164),
|
||||
}),
|
||||
name: safeTrim(ctx.SenderName),
|
||||
username: safeTrim(ctx.SenderUsername),
|
||||
tag: safeTrim(ctx.SenderTag),
|
||||
e164: safeTrim(ctx.SenderE164),
|
||||
};
|
||||
const senderInfo = {
|
||||
label: resolveSenderLabel({
|
||||
name: safeTrim(ctx.SenderName),
|
||||
username: safeTrim(ctx.SenderUsername),
|
||||
tag: safeTrim(ctx.SenderTag),
|
||||
e164: safeTrim(ctx.SenderE164),
|
||||
id: safeTrim(ctx.SenderId),
|
||||
}),
|
||||
id: safeTrim(ctx.SenderId),
|
||||
name: safeTrim(ctx.SenderName),
|
||||
username: safeTrim(ctx.SenderUsername),
|
||||
tag: safeTrim(ctx.SenderTag),
|
||||
e164: safeTrim(ctx.SenderE164),
|
||||
};
|
||||
if (senderInfo?.label) {
|
||||
blocks.push(
|
||||
["Sender (untrusted metadata):", "```json", JSON.stringify(senderInfo, null, 2), "```"].join(
|
||||
|
||||
Reference in New Issue
Block a user