mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 16:06:16 +00:00
* 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)
75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
import {
|
|
SUBAGENT_ENDED_REASON_COMPLETE,
|
|
type SubagentLifecycleEndedReason,
|
|
} from "./subagent-lifecycle-events.js";
|
|
import type { SubagentRunRecord } from "./subagent-registry.types.js";
|
|
|
|
export type DeferredCleanupDecision =
|
|
| {
|
|
kind: "defer-descendants";
|
|
delayMs: number;
|
|
}
|
|
| {
|
|
kind: "give-up";
|
|
reason: "retry-limit" | "expiry";
|
|
retryCount?: number;
|
|
}
|
|
| {
|
|
kind: "retry";
|
|
retryCount: number;
|
|
resumeDelayMs?: number;
|
|
};
|
|
|
|
export function resolveCleanupCompletionReason(
|
|
entry: SubagentRunRecord,
|
|
): SubagentLifecycleEndedReason {
|
|
return entry.endedReason ?? SUBAGENT_ENDED_REASON_COMPLETE;
|
|
}
|
|
|
|
function resolveEndedAgoMs(entry: SubagentRunRecord, now: number): number {
|
|
return typeof entry.endedAt === "number" ? now - entry.endedAt : 0;
|
|
}
|
|
|
|
export function resolveDeferredCleanupDecision(params: {
|
|
entry: SubagentRunRecord;
|
|
now: number;
|
|
activeDescendantRuns: number;
|
|
announceExpiryMs: number;
|
|
announceCompletionHardExpiryMs: number;
|
|
maxAnnounceRetryCount: number;
|
|
deferDescendantDelayMs: number;
|
|
resolveAnnounceRetryDelayMs: (retryCount: number) => number;
|
|
}): DeferredCleanupDecision {
|
|
const endedAgo = resolveEndedAgoMs(params.entry, params.now);
|
|
const isCompletionMessageFlow = params.entry.expectsCompletionMessage === true;
|
|
const completionHardExpiryExceeded =
|
|
isCompletionMessageFlow && endedAgo > params.announceCompletionHardExpiryMs;
|
|
if (isCompletionMessageFlow && params.activeDescendantRuns > 0) {
|
|
if (completionHardExpiryExceeded) {
|
|
return { kind: "give-up", reason: "expiry" };
|
|
}
|
|
return { kind: "defer-descendants", delayMs: params.deferDescendantDelayMs };
|
|
}
|
|
|
|
const retryCount = (params.entry.announceRetryCount ?? 0) + 1;
|
|
const expiryExceeded = isCompletionMessageFlow
|
|
? completionHardExpiryExceeded
|
|
: endedAgo > params.announceExpiryMs;
|
|
if (retryCount >= params.maxAnnounceRetryCount || expiryExceeded) {
|
|
return {
|
|
kind: "give-up",
|
|
reason: retryCount >= params.maxAnnounceRetryCount ? "retry-limit" : "expiry",
|
|
retryCount,
|
|
};
|
|
}
|
|
|
|
return {
|
|
kind: "retry",
|
|
retryCount,
|
|
resumeDelayMs:
|
|
params.entry.expectsCompletionMessage === true
|
|
? params.resolveAnnounceRetryDelayMs(retryCount)
|
|
: undefined,
|
|
};
|
|
}
|