refactor: share subagent helpers
This commit is contained in:
@@ -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(", ")})` : "";
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
53
src/auto-reply/reply/subagents-utils.ts
Normal file
53
src/auto-reply/reply/subagents-utils.ts
Normal 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;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user