refactor: share subagent helpers

This commit is contained in:
Peter Steinberger
2026-01-18 05:14:23 +00:00
parent 016693a1f5
commit 97cef49046
3 changed files with 64 additions and 45 deletions

View File

@@ -27,6 +27,7 @@ import type { ReplyPayload } from "../types.js";
import type { CommandContext } from "./commands-types.js";
import { getFollowupQueueDepth, resolveQueueSettings } from "./queue.js";
import type { MediaUnderstandingDecision } from "../../media-understanding/types.js";
import { resolveSubagentLabel } from "./subagents-utils.js";
function formatApiKeySnippet(apiKey: string): string {
const compact = apiKey.replace(/\s+/g, "");
@@ -187,7 +188,7 @@ export async function buildStatusReply(params: {
const done = runs.length - active.length;
if (verboseEnabled) {
const labels = active
.map((entry) => entry.label?.trim() || entry.task?.trim() || "")
.map((entry) => resolveSubagentLabel(entry, ""))
.filter(Boolean)
.slice(0, 3);
const labelText = labels.length ? ` (${labels.join(", ")})` : "";

View File

@@ -15,7 +15,13 @@ import { callGateway } from "../../gateway/call.js";
import { logVerbose } from "../../globals.js";
import { parseAgentSessionKey } from "../../routing/session-key.js";
import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
import { truncateUtf16Safe } from "../../utils.js";
import {
formatAgeShort,
formatDurationShort,
formatRunLabel,
formatRunStatus,
sortSubagentRuns,
} from "./subagents-utils.js";
import { stopSubagentsForRequester } from "./abort.js";
import type { CommandHandler } from "./commands-types.js";
import { clearSessionQueues } from "./queue.js";
@@ -28,28 +34,6 @@ type SubagentTargetResolution = {
const COMMAND = "/subagents";
const ACTIONS = new Set(["list", "stop", "log", "send", "info", "help"]);
function formatDurationShort(valueMs?: number) {
if (!valueMs || !Number.isFinite(valueMs) || valueMs <= 0) return "n/a";
const totalSeconds = Math.round(valueMs / 1000);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
if (hours > 0) return `${hours}h${minutes}m`;
if (minutes > 0) return `${minutes}m${seconds}s`;
return `${seconds}s`;
}
function formatAgeShort(valueMs?: number) {
if (!valueMs || !Number.isFinite(valueMs) || valueMs <= 0) return "n/a";
const minutes = Math.round(valueMs / 60_000);
if (minutes < 1) return "just now";
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.round(minutes / 60);
if (hours < 48) return `${hours}h ago`;
const days = Math.round(hours / 24);
return `${days}d ago`;
}
function formatTimestamp(valueMs?: number) {
if (!valueMs || !Number.isFinite(valueMs) || valueMs <= 0) return "n/a";
return new Date(valueMs).toISOString();
@@ -60,25 +44,6 @@ function formatTimestampWithAge(valueMs?: number) {
return `${formatTimestamp(valueMs)} (${formatAgeShort(Date.now() - valueMs)})`;
}
function formatRunStatus(entry: SubagentRunRecord) {
if (!entry.endedAt) return "running";
const status = entry.outcome?.status ?? "done";
return status === "ok" ? "done" : status;
}
function formatRunLabel(entry: SubagentRunRecord) {
const raw = entry.label?.trim() || entry.task?.trim() || "subagent";
return raw.length > 72 ? `${truncateUtf16Safe(raw, 72).trimEnd()}` : raw;
}
function sortRuns(runs: SubagentRunRecord[]) {
return [...runs].sort((a, b) => {
const aTime = a.startedAt ?? a.createdAt ?? 0;
const bTime = b.startedAt ?? b.createdAt ?? 0;
return bTime - aTime;
});
}
function resolveRequesterSessionKey(params: Parameters<CommandHandler>[0]): string | undefined {
const raw = params.ctx.CommandTargetSessionKey?.trim() || params.sessionKey;
if (!raw) return undefined;
@@ -93,7 +58,7 @@ function resolveSubagentTarget(
const trimmed = token?.trim();
if (!trimmed) return { error: "Missing subagent id." };
if (trimmed === "last") {
const sorted = sortRuns(runs);
const sorted = sortSubagentRuns(runs);
return { entry: sorted[0] };
}
const sorted = sortRuns(runs);
@@ -212,7 +177,7 @@ export const handleSubagentsCommand: CommandHandler = async (params, allowTextCo
if (runs.length === 0) {
return { shouldContinue: false, reply: { text: "🧭 Subagents: none for this session." } };
}
const sorted = sortRuns(runs);
const sorted = sortSubagentRuns(runs);
const active = sorted.filter((entry) => !entry.endedAt);
const done = sorted.length - active.length;
const lines = [

View File

@@ -0,0 +1,53 @@
import type { SubagentRunRecord } from "../../agents/subagent-registry.js";
import { truncateUtf16Safe } from "../../utils.js";
export function formatDurationShort(valueMs?: number) {
if (!valueMs || !Number.isFinite(valueMs) || valueMs <= 0) return "n/a";
const totalSeconds = Math.round(valueMs / 1000);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
if (hours > 0) return `${hours}h${minutes}m`;
if (minutes > 0) return `${minutes}m${seconds}s`;
return `${seconds}s`;
}
export function formatAgeShort(valueMs?: number) {
if (!valueMs || !Number.isFinite(valueMs) || valueMs <= 0) return "n/a";
const minutes = Math.round(valueMs / 60_000);
if (minutes < 1) return "just now";
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.round(minutes / 60);
if (hours < 48) return `${hours}h ago`;
const days = Math.round(hours / 24);
return `${days}d ago`;
}
export function resolveSubagentLabel(entry: SubagentRunRecord, fallback = "subagent") {
const raw = entry.label?.trim() || entry.task?.trim() || "";
return raw || fallback;
}
export function formatRunLabel(
entry: SubagentRunRecord,
options?: { maxLength?: number },
) {
const raw = resolveSubagentLabel(entry);
const maxLength = options?.maxLength ?? 72;
if (!Number.isFinite(maxLength) || maxLength <= 0) return raw;
return raw.length > maxLength ? `${truncateUtf16Safe(raw, maxLength).trimEnd()}` : raw;
}
export function formatRunStatus(entry: SubagentRunRecord) {
if (!entry.endedAt) return "running";
const status = entry.outcome?.status ?? "done";
return status === "ok" ? "done" : status;
}
export function sortSubagentRuns(runs: SubagentRunRecord[]) {
return [...runs].sort((a, b) => {
const aTime = a.startedAt ?? a.createdAt ?? 0;
const bTime = b.startedAt ?? b.createdAt ?? 0;
return bTime - aTime;
});
}