import type { SubagentRunRecord } from "../../agents/subagent-registry.js" ;
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "../../shared/string-coerce.js" ;
import { sanitizeTaskStatusText } from "../../tasks/task-status.js" ;
import { truncateUtf16Safe } from "../../utils.js" ;
export function resolveSubagentLabel(entry: SubagentRunRecord, fallback = "subagent" ) {
const raw = normalizeOptionalString(entry.label) || normalizeOptionalString(entry.task) || "" ;
return raw || fallback;
}
export function formatRunLabel(entry: SubagentRunRecord, options?: { maxLength?: number }) {
const raw = sanitizeTaskStatusText(resolveSubagentLabel(entry)) || "subagent" ;
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].toSorted((a, b) => {
const aTime = a.startedAt ?? a.createdAt ?? 0 ;
const bTime = b.startedAt ?? b.createdAt ?? 0 ;
return bTime - aTime;
});
}
export type SubagentTargetResolution = {
entry?: SubagentRunRecord;
error?: string;
};
export function resolveSubagentTargetFromRuns(params: {
runs: SubagentRunRecord[];
token: string | undefined;
recentWindowMinutes: number;
label: (entry: SubagentRunRecord) => string;
isActive?: (entry: SubagentRunRecord) => boolean ;
errors: {
missingTarget: string;
invalidIndex: (value: string) => string;
unknownSession: (value: string) => string;
ambiguousLabel: (value: string) => string;
ambiguousLabelPrefix: (value: string) => string;
ambiguousRunIdPrefix: (value: string) => string;
unknownTarget: (value: string) => string;
};
}): SubagentTargetResolution {
const trimmed = normalizeOptionalString(params.token);
if (!trimmed) {
return { error: params.errors.missingTarget };
}
const sorted = sortSubagentRuns(params.runs);
const deduped: SubagentRunRecord[] = [];
const seenChildSessionKeys = new Set<string>();
for (const entry of sorted) {
if (seenChildSessionKeys.has(entry.childSessionKey)) {
continue ;
}
seenChildSessionKeys.add(entry.childSessionKey);
deduped.push(entry);
}
if (trimmed === "last" ) {
return { entry: deduped[0 ] };
}
const isActive = params.isActive ?? ((entry: SubagentRunRecord) => !entry.endedAt);
const recentCutoff = Date.now() - params.recentWindowMinutes * 60 _000 ;
const numericOrder = [
...deduped.filter((entry) => isActive(entry)),
...deduped.filter(
(entry) => !isActive(entry) && !!entry.endedAt && (entry.endedAt ?? 0 ) >= recentCutoff,
),
];
if (/^\d+$/.test(trimmed)) {
const idx = Number.parseInt(trimmed, 10 );
if (!Number.isFinite(idx) || idx <= 0 || idx > numericOrder.length) {
return { error: params.errors.invalidIndex(trimmed) };
}
return { entry: numericOrder[idx - 1 ] };
}
if (trimmed.includes(":" )) {
const bySessionKey = deduped.find((entry) => entry.childSessionKey === trimmed);
return bySessionKey
? { entry: bySessionKey }
: { error: params.errors.unknownSession(trimmed) };
}
const lowered = normalizeLowercaseStringOrEmpty(trimmed);
const byExactLabel = deduped.filter(
(entry) => normalizeLowercaseStringOrEmpty(params.label(entry)) === lowered,
);
if (byExactLabel.length === 1 ) {
return { entry: byExactLabel[0 ] };
}
if (byExactLabel.length > 1 ) {
return { error: params.errors.ambiguousLabel(trimmed) };
}
const byLabelPrefix = deduped.filter((entry) =>
normalizeLowercaseStringOrEmpty(params.label(entry)).startsWith(lowered),
);
if (byLabelPrefix.length === 1 ) {
return { entry: byLabelPrefix[0 ] };
}
if (byLabelPrefix.length > 1 ) {
return { error: params.errors.ambiguousLabelPrefix(trimmed) };
}
const byRunIdPrefix = deduped.filter((entry) => entry.runId.startsWith(trimmed));
if (byRunIdPrefix.length === 1 ) {
return { entry: byRunIdPrefix[0 ] };
}
if (byRunIdPrefix.length > 1 ) {
return { error: params.errors.ambiguousRunIdPrefix(trimmed) };
}
return { error: params.errors.unknownTarget(trimmed) };
}
Messung V0.5 in Prozent C=100 H=97 G=98
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland