Files
openclaw/src/agents/subagent-registry-read.ts

183 lines
5.4 KiB
TypeScript

import { SUBAGENT_ENDED_REASON_KILLED } from "./subagent-lifecycle-events.js";
import { subagentRuns } from "./subagent-registry-memory.js";
import {
countActiveDescendantRunsFromRuns,
listDescendantRunsForRequesterFromRuns,
listRunsForControllerFromRuns,
} from "./subagent-registry-queries.js";
import { getSubagentRunsSnapshotForRead } from "./subagent-registry-state.js";
import type { SubagentRunRecord } from "./subagent-registry.types.js";
function resolveSubagentSessionStartedAt(
entry: Pick<SubagentRunRecord, "sessionStartedAt" | "startedAt" | "createdAt">,
): number | undefined {
if (typeof entry.sessionStartedAt === "number" && Number.isFinite(entry.sessionStartedAt)) {
return entry.sessionStartedAt;
}
if (typeof entry.startedAt === "number" && Number.isFinite(entry.startedAt)) {
return entry.startedAt;
}
return typeof entry.createdAt === "number" && Number.isFinite(entry.createdAt)
? entry.createdAt
: undefined;
}
export function getSubagentSessionStartedAt(
entry: Pick<SubagentRunRecord, "sessionStartedAt" | "startedAt" | "createdAt"> | null | undefined,
): number | undefined {
return entry ? resolveSubagentSessionStartedAt(entry) : undefined;
}
export function getSubagentSessionRuntimeMs(
entry:
| Pick<SubagentRunRecord, "startedAt" | "endedAt" | "accumulatedRuntimeMs">
| null
| undefined,
now = Date.now(),
): number | undefined {
if (!entry) {
return undefined;
}
const accumulatedRuntimeMs =
typeof entry.accumulatedRuntimeMs === "number" && Number.isFinite(entry.accumulatedRuntimeMs)
? Math.max(0, entry.accumulatedRuntimeMs)
: 0;
if (typeof entry.startedAt !== "number" || !Number.isFinite(entry.startedAt)) {
return entry.accumulatedRuntimeMs != null ? accumulatedRuntimeMs : undefined;
}
const currentRunEndedAt =
typeof entry.endedAt === "number" && Number.isFinite(entry.endedAt) ? entry.endedAt : now;
return Math.max(0, accumulatedRuntimeMs + Math.max(0, currentRunEndedAt - entry.startedAt));
}
export function resolveSubagentSessionStatus(
entry: Pick<SubagentRunRecord, "endedAt" | "endedReason" | "outcome"> | null | undefined,
): "running" | "killed" | "failed" | "timeout" | "done" | undefined {
if (!entry) {
return undefined;
}
if (!entry.endedAt) {
return "running";
}
if (entry.endedReason === SUBAGENT_ENDED_REASON_KILLED) {
return "killed";
}
const status = entry.outcome?.status;
if (status === "error") {
return "failed";
}
if (status === "timeout") {
return "timeout";
}
return "done";
}
export function listSubagentRunsForController(controllerSessionKey: string): SubagentRunRecord[] {
return listRunsForControllerFromRuns(
getSubagentRunsSnapshotForRead(subagentRuns),
controllerSessionKey,
);
}
export function countActiveDescendantRuns(rootSessionKey: string): number {
return countActiveDescendantRunsFromRuns(
getSubagentRunsSnapshotForRead(subagentRuns),
rootSessionKey,
);
}
export function listDescendantRunsForRequester(rootSessionKey: string): SubagentRunRecord[] {
return listDescendantRunsForRequesterFromRuns(
getSubagentRunsSnapshotForRead(subagentRuns),
rootSessionKey,
);
}
export function getSubagentRunByChildSessionKey(childSessionKey: string): SubagentRunRecord | null {
const key = childSessionKey.trim();
if (!key) {
return null;
}
let latestActive: SubagentRunRecord | null = null;
let latestEnded: SubagentRunRecord | null = null;
for (const entry of getSubagentRunsSnapshotForRead(subagentRuns).values()) {
if (entry.childSessionKey !== key) {
continue;
}
if (typeof entry.endedAt !== "number") {
if (!latestActive || entry.createdAt > latestActive.createdAt) {
latestActive = entry;
}
continue;
}
if (!latestEnded || entry.createdAt > latestEnded.createdAt) {
latestEnded = entry;
}
}
return latestActive ?? latestEnded;
}
export function getSessionDisplaySubagentRunByChildSessionKey(
childSessionKey: string,
): SubagentRunRecord | null {
const key = childSessionKey.trim();
if (!key) {
return null;
}
let latestInMemoryActive: SubagentRunRecord | null = null;
let latestInMemoryEnded: SubagentRunRecord | null = null;
for (const entry of subagentRuns.values()) {
if (entry.childSessionKey !== key) {
continue;
}
if (typeof entry.endedAt === "number") {
if (!latestInMemoryEnded || entry.createdAt > latestInMemoryEnded.createdAt) {
latestInMemoryEnded = entry;
}
continue;
}
if (!latestInMemoryActive || entry.createdAt > latestInMemoryActive.createdAt) {
latestInMemoryActive = entry;
}
}
if (latestInMemoryEnded || latestInMemoryActive) {
if (
latestInMemoryEnded &&
(!latestInMemoryActive || latestInMemoryEnded.createdAt > latestInMemoryActive.createdAt)
) {
return latestInMemoryEnded;
}
return latestInMemoryActive ?? latestInMemoryEnded;
}
return getSubagentRunByChildSessionKey(key);
}
export function getLatestSubagentRunByChildSessionKey(
childSessionKey: string,
): SubagentRunRecord | null {
const key = childSessionKey.trim();
if (!key) {
return null;
}
let latest: SubagentRunRecord | null = null;
for (const entry of getSubagentRunsSnapshotForRead(subagentRuns).values()) {
if (entry.childSessionKey !== key) {
continue;
}
if (!latest || entry.createdAt > latest.createdAt) {
latest = entry;
}
}
return latest;
}