openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
187 lines (186 loc) • 8.48 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-mnp54Vah.js";
import { c as parseAgentSessionKey } from "./session-key-utils-Bx3apsJ3.js";
import { u as resolveStorePath } from "./paths-NEwU8m3X.js";
import { t as loadSessionStore } from "./store-load-C4uq6Lrq.js";
import { A as getSubagentSessionRuntimeMs, D as isLiveUnendedSubagentRun, E as hasSubagentRunEnded, H as subagentRuns, _ as countPendingDescendantRunsFromRuns, j as getSubagentSessionStartedAt, k as shouldKeepSubagentRunChildLink, m as countActiveDescendantRunsFromRuns, t as getSubagentRunsSnapshotForRead } from "./subagent-registry-state-2yU6fwXx.js";
import "./subagent-registry-read-BmI9NJyA.js";
import { a as sortSubagentRuns, r as resolveSubagentLabel } from "./subagents-utils-BD_5s6t0.js";
import { t as formatDurationCompact } from "./format-duration-BrZ-AaEJ.js";
import { n as resolveTotalTokens, r as truncateLine, t as formatTokenUsageDisplay } from "./subagents-format-Bt4O4-nT.js";
import { n as resolveModelDisplayRef, t as resolveModelDisplayName } from "./model-selection-display-FeptE3LP.js";
//#region src/agents/subagent-list.ts
/**
* Subagent list builder.
*
* Combines live registry runs and persisted session metadata for sessions_list/subagents views.
*/
function resolveStorePathForKey(cfg, parsed) {
return resolveStorePath(cfg.session?.store, { agentId: parsed?.agentId });
}
/** Resolve persisted session metadata for a session key, caching per store path. */
function resolveSessionEntryForKey(params) {
const parsed = parseAgentSessionKey(params.key);
const storePath = resolveStorePathForKey(params.cfg, parsed);
let store = params.cache.get(storePath);
if (!store) {
store = loadSessionStore(storePath);
params.cache.set(storePath, store);
}
return {
storePath,
entry: store[params.key]
};
}
/** Build child-session indexes from the latest run associated with each child key. */
function buildLatestSubagentRunIndex(runs, options) {
const now = options?.now ?? Date.now();
const latestByChildSessionKey = /* @__PURE__ */ new Map();
for (const entry of runs.values()) {
const childSessionKey = entry.childSessionKey?.trim();
if (!childSessionKey) continue;
const existing = latestByChildSessionKey.get(childSessionKey);
if (!existing || entry.createdAt > existing.createdAt) latestByChildSessionKey.set(childSessionKey, entry);
}
const childSessionsByController = /* @__PURE__ */ new Map();
for (const [childSessionKey, entry] of latestByChildSessionKey.entries()) {
const controllerSessionKey = entry.controllerSessionKey?.trim() || entry.requesterSessionKey?.trim();
if (!controllerSessionKey) continue;
if (!shouldKeepSubagentRunChildLink(entry, {
activeDescendants: countActiveDescendantRunsFromRuns(runs, childSessionKey),
now
})) continue;
const existing = childSessionsByController.get(controllerSessionKey);
if (existing) {
existing.push(childSessionKey);
continue;
}
childSessionsByController.set(controllerSessionKey, [childSessionKey]);
}
for (const [controllerSessionKey, childSessions] of childSessionsByController) childSessionsByController.set(controllerSessionKey, childSessions.toSorted());
return {
latestByChildSessionKey,
childSessionsByController
};
}
/** Create a cached descendant counter for repeated list rendering checks. */
function createPendingDescendantCounter(runsSnapshot) {
const pendingDescendantCache = /* @__PURE__ */ new Map();
return (sessionKey) => {
if (pendingDescendantCache.has(sessionKey)) return pendingDescendantCache.get(sessionKey) ?? 0;
const snapshot = runsSnapshot ?? getSubagentRunsSnapshotForRead(subagentRuns);
const pending = Math.max(0, countPendingDescendantRunsFromRuns(snapshot, sessionKey));
pendingDescendantCache.set(sessionKey, pending);
return pending;
};
}
/** Return whether a run should be shown in the active subagent section. */
function isActiveSubagentRun(entry, pendingDescendantCount) {
return isLiveUnendedSubagentRun(entry) || pendingDescendantCount(entry.childSessionKey) > 0;
}
function resolveRunStatus(entry, options) {
const pendingDescendants = Math.max(0, options?.pendingDescendants ?? 0);
if (pendingDescendants > 0) return `active (waiting on ${pendingDescendants} ${pendingDescendants === 1 ? "child" : "children"})`;
if (!hasSubagentRunEnded(entry)) return "running";
const status = entry.outcome?.status ?? "done";
if (status === "ok") return "done";
if (status === "error") return "failed";
return status;
}
function resolveModelRef(entry, fallbackModel) {
return resolveModelDisplayRef({
runtimeProvider: entry?.modelProvider,
runtimeModel: entry?.model,
overrideProvider: entry?.providerOverride,
overrideModel: entry?.modelOverride,
fallbackModel
});
}
function resolveModelDisplay(entry, fallbackModel) {
return resolveModelDisplayName({
runtimeProvider: entry?.modelProvider,
runtimeModel: entry?.model,
overrideProvider: entry?.providerOverride,
overrideModel: entry?.modelOverride,
fallbackModel
});
}
function buildListText(params) {
const lines = [];
lines.push("active subagents:");
if (params.active.length === 0) lines.push("(none)");
else lines.push(...params.active.map((entry) => entry.line));
lines.push("");
lines.push(`recent (last ${params.recentMinutes}m):`);
if (params.recent.length === 0) lines.push("(none)");
else lines.push(...params.recent.map((entry) => entry.line));
return lines.join("\n");
}
/** Build structured and text views for active and recent subagent runs. */
function buildSubagentList(params) {
const now = Date.now();
const recentCutoff = now - params.recentMinutes * 6e4;
const dedupedRuns = [];
const seenChildSessionKeys = /* @__PURE__ */ new Set();
for (const entry of sortSubagentRuns(params.runs)) {
if (seenChildSessionKeys.has(entry.childSessionKey)) continue;
seenChildSessionKeys.add(entry.childSessionKey);
dedupedRuns.push(entry);
}
const cache = /* @__PURE__ */ new Map();
const snapshot = getSubagentRunsSnapshotForRead(subagentRuns);
const { childSessionsByController } = buildLatestSubagentRunIndex(snapshot);
const pendingDescendantCount = createPendingDescendantCounter(snapshot);
let index = 1;
const buildListEntry = (entry, runtimeMs) => {
const sessionEntry = resolveSessionEntryForKey({
cfg: params.cfg,
key: entry.childSessionKey,
cache
}).entry;
const totalTokens = resolveTotalTokens(sessionEntry);
const usageText = formatTokenUsageDisplay(sessionEntry);
const pendingDescendants = pendingDescendantCount(entry.childSessionKey);
const status = resolveRunStatus(entry, { pendingDescendants });
const childSessions = childSessionsByController.get(entry.childSessionKey) ?? [];
const runtime = formatDurationCompact(runtimeMs) ?? "n/a";
const label = truncateLine(resolveSubagentLabel(entry), 48);
const task = truncateLine(entry.task.trim(), params.taskMaxChars ?? 72);
const taskName = entry.taskName?.trim();
const taskNamePrefix = taskName ? `${taskName}: ` : "";
const line = `${index}. ${taskNamePrefix}${label} (${resolveModelDisplay(sessionEntry, entry.model)}, ${runtime}${usageText ? `, ${usageText}` : ""}) ${status}${normalizeLowercaseStringOrEmpty(task) !== normalizeLowercaseStringOrEmpty(label) ? ` - ${task}` : ""}`;
const view = {
index,
line,
runId: entry.runId,
sessionKey: entry.childSessionKey,
...taskName ? { taskName } : {},
label,
task,
status,
pendingDescendants,
runtime,
runtimeMs,
...childSessions.length > 0 ? { childSessions } : {},
model: resolveModelRef(sessionEntry, entry.model),
totalTokens,
startedAt: getSubagentSessionStartedAt(entry),
...entry.endedAt ? { endedAt: entry.endedAt } : {}
};
index += 1;
return view;
};
const active = dedupedRuns.filter((entry) => isActiveSubagentRun(entry, pendingDescendantCount)).map((entry) => buildListEntry(entry, getSubagentSessionRuntimeMs(entry, now) ?? 0));
const recent = dedupedRuns.filter((entry) => !isActiveSubagentRun(entry, pendingDescendantCount) && Boolean(entry.endedAt) && (entry.endedAt ?? 0) >= recentCutoff).map((entry) => buildListEntry(entry, getSubagentSessionRuntimeMs(entry, entry.endedAt ?? now) ?? 0));
return {
total: dedupedRuns.length,
active,
recent,
text: buildListText({
active,
recent,
recentMinutes: params.recentMinutes
})
};
}
//#endregion
export { buildSubagentList as n, resolveSessionEntryForKey as r, buildLatestSubagentRunIndex as t };