mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 18:04:59 +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:
@@ -728,6 +728,7 @@ async function sendSubagentAnnounceDirectly(params: {
|
||||
completionRouteMode?: "bound" | "fallback" | "hook";
|
||||
spawnMode?: SpawnSubagentMode;
|
||||
directIdempotencyKey: string;
|
||||
currentRunId?: string;
|
||||
completionDirectOrigin?: DeliveryContext;
|
||||
directOrigin?: DeliveryContext;
|
||||
requesterIsSubagent: boolean;
|
||||
@@ -770,19 +771,35 @@ async function sendSubagentAnnounceDirectly(params: {
|
||||
(params.completionRouteMode === "bound" || params.completionRouteMode === "hook");
|
||||
let shouldSendCompletionDirectly = true;
|
||||
if (!forceBoundSessionDirectDelivery) {
|
||||
let activeDescendantRuns = 0;
|
||||
let pendingDescendantRuns = 0;
|
||||
try {
|
||||
const { countActiveDescendantRuns } = await import("./subagent-registry.js");
|
||||
activeDescendantRuns = Math.max(
|
||||
0,
|
||||
countActiveDescendantRuns(canonicalRequesterSessionKey),
|
||||
);
|
||||
const {
|
||||
countPendingDescendantRuns,
|
||||
countPendingDescendantRunsExcludingRun,
|
||||
countActiveDescendantRuns,
|
||||
} = await import("./subagent-registry.js");
|
||||
if (params.currentRunId && typeof countPendingDescendantRunsExcludingRun === "function") {
|
||||
pendingDescendantRuns = Math.max(
|
||||
0,
|
||||
countPendingDescendantRunsExcludingRun(
|
||||
canonicalRequesterSessionKey,
|
||||
params.currentRunId,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
pendingDescendantRuns = Math.max(
|
||||
0,
|
||||
typeof countPendingDescendantRuns === "function"
|
||||
? countPendingDescendantRuns(canonicalRequesterSessionKey)
|
||||
: countActiveDescendantRuns(canonicalRequesterSessionKey),
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// Best-effort only; when unavailable keep historical direct-send behavior.
|
||||
}
|
||||
// Keep non-bound completion announcements coordinated via requester
|
||||
// session routing while sibling/descendant runs are still active.
|
||||
if (activeDescendantRuns > 0) {
|
||||
// session routing while sibling or descendant runs are still pending.
|
||||
if (pendingDescendantRuns > 0) {
|
||||
shouldSendCompletionDirectly = false;
|
||||
}
|
||||
}
|
||||
@@ -899,6 +916,7 @@ async function deliverSubagentAnnouncement(params: {
|
||||
completionRouteMode?: "bound" | "fallback" | "hook";
|
||||
spawnMode?: SpawnSubagentMode;
|
||||
directIdempotencyKey: string;
|
||||
currentRunId?: string;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<SubagentAnnounceDeliveryResult> {
|
||||
return await runSubagentAnnounceDispatch({
|
||||
@@ -922,6 +940,7 @@ async function deliverSubagentAnnouncement(params: {
|
||||
completionMessage: params.completionMessage,
|
||||
internalEvents: params.internalEvents,
|
||||
directIdempotencyKey: params.directIdempotencyKey,
|
||||
currentRunId: params.currentRunId,
|
||||
completionDirectOrigin: params.completionDirectOrigin,
|
||||
completionRouteMode: params.completionRouteMode,
|
||||
spawnMode: params.spawnMode,
|
||||
@@ -1203,16 +1222,23 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
|
||||
let requesterDepth = getSubagentDepthFromSessionStore(targetRequesterSessionKey);
|
||||
|
||||
let activeChildDescendantRuns = 0;
|
||||
let pendingChildDescendantRuns = 0;
|
||||
try {
|
||||
const { countActiveDescendantRuns } = await import("./subagent-registry.js");
|
||||
activeChildDescendantRuns = Math.max(0, countActiveDescendantRuns(params.childSessionKey));
|
||||
const { countPendingDescendantRuns, countActiveDescendantRuns } =
|
||||
await import("./subagent-registry.js");
|
||||
pendingChildDescendantRuns = Math.max(
|
||||
0,
|
||||
typeof countPendingDescendantRuns === "function"
|
||||
? countPendingDescendantRuns(params.childSessionKey)
|
||||
: countActiveDescendantRuns(params.childSessionKey),
|
||||
);
|
||||
} catch {
|
||||
// Best-effort only; fall back to direct announce behavior when unavailable.
|
||||
}
|
||||
if (activeChildDescendantRuns > 0) {
|
||||
// The finished run still has active descendant subagents. Defer announcing
|
||||
// this run until descendants settle so we avoid posting in-progress updates.
|
||||
if (pendingChildDescendantRuns > 0) {
|
||||
// The finished run still has pending descendant subagents (either active,
|
||||
// or ended but still finishing their own announce and cleanup flow). Defer
|
||||
// announcing this run until descendants fully settle.
|
||||
shouldDeleteChildSession = false;
|
||||
return false;
|
||||
}
|
||||
@@ -1383,6 +1409,7 @@ export async function runSubagentAnnounceFlow(params: {
|
||||
completionRouteMode: completionResolution.routeMode,
|
||||
spawnMode: params.spawnMode,
|
||||
directIdempotencyKey,
|
||||
currentRunId: params.childRunId,
|
||||
signal: params.signal,
|
||||
});
|
||||
// Cron delivery state should only be marked as delivered when we have a
|
||||
|
||||
Reference in New Issue
Block a user