import { resolveSessionAgentId } from "../../agents/agent-scope.js" ;
import { logVerbose } from "../../globals.js" ;
import { formatDurationCompact } from "../../infra/format-time/format-duration.ts" ;
import { formatTimeAgo } from "../../infra/format-time/format-relative.ts" ;
import type { TaskRecord } from "../../tasks/task-registry.types.js" ;
import {
listTasksForAgentIdForStatus,
listTasksForSessionKeyForStatus,
} from "../../tasks/task-status-access.js" ;
import {
buildTaskStatusSnapshot,
formatTaskStatusDetail,
formatTaskStatusTitle,
} from "../../tasks/task-status.js" ;
import type { ReplyPayload } from "../types.js" ;
import type { CommandHandler, HandleCommandsParams } from "./commands-types.js" ;
const MAX_VISIBLE_TASKS = 5 ;
const TASK_STATUS_ICONS: Record<TaskRecord["status" ], string> = {
queued: "" ,
running: "" ,
succeeded: "✅" ,
failed: "" ,
timed_out: "⏱️" ,
cancelled: "⚪️" ,
lost: "⚠️" ,
};
const TASK_RUNTIME_LABELS: Record<TaskRecord["runtime" ], string> = {
subagent: "Subagent" ,
acp: "ACP" ,
cli: "CLI" ,
cron: "Cron" ,
};
function formatTaskHeadline(snapshot: ReturnType<typeof buildTaskStatusSnapshot>): string {
if (snapshot.totalCount === 0 ) {
return "All clear - nothing linked to this session right now." ;
}
return `Current session: ${snapshot.activeCount} active · ${snapshot.totalCount} total`;
}
function formatAgentFallbackLine(agentId: string): string | undefined {
const snapshot = buildTaskStatusSnapshot(listTasksForAgentIdForStatus(agentId));
if (snapshot.totalCount === 0 ) {
return undefined;
}
return `Agent-local: ${snapshot.activeCount} active · ${snapshot.totalCount} total`;
}
function formatTaskTiming(task: TaskRecord): string | undefined {
if (task.status === "running" ) {
const startedAt = task.startedAt ?? task.createdAt;
return `elapsed ${formatDurationCompact(Date.now() - startedAt, { spaced: true }) ?? "0s" }`;
}
if (task.status === "queued" ) {
return `queued ${formatTimeAgo(Date.now() - task.createdAt)}`;
}
const endedAt = task.endedAt ?? task.lastEventAt ?? task.createdAt;
return `finished ${formatTimeAgo(Date.now() - endedAt)}`;
}
function formatTaskDetail(task: TaskRecord): string | undefined {
return formatTaskStatusDetail(task);
}
function formatVisibleTask(task: TaskRecord, index: number): string {
const title = formatTaskStatusTitle(task);
const status = task.status.replaceAll("_" , " " );
const timing = formatTaskTiming(task);
const detail = formatTaskDetail(task);
const meta = [TASK_RUNTIME_LABELS[task.runtime], status, timing].filter(Boolean ).join(" · " );
const lines = [`${index + 1 }. ${TASK_STATUS_ICONS[task.status]} ${title}`, ` ${meta}`];
if (detail) {
lines.push(` ${detail}`);
}
return lines.join("\n" );
}
export function buildTasksText(params: { sessionKey: string; agentId: string }): string {
const sessionSnapshot = buildTaskStatusSnapshot(
listTasksForSessionKeyForStatus(params.sessionKey),
);
const lines = [" Tasks" , formatTaskHeadline(sessionSnapshot)];
if (sessionSnapshot.totalCount > 0 ) {
const visible = sessionSnapshot.visible.slice(0 , MAX_VISIBLE_TASKS);
lines.push("" );
for (const [index, task] of visible.entries()) {
lines.push(formatVisibleTask(task, index));
if (index < visible.length - 1 ) {
lines.push("" );
}
}
const hiddenCount = sessionSnapshot.visible.length - visible.length;
if (hiddenCount > 0 ) {
lines.push("" , `+${hiddenCount} more recent task${hiddenCount === 1 ? "" : "s" }`);
}
return lines.join("\n" );
}
const agentFallback = formatAgentFallbackLine(params.agentId);
if (agentFallback) {
lines.push(agentFallback);
}
return lines.join("\n" );
}
export async function buildTasksReply(params: HandleCommandsParams): Promise<ReplyPayload> {
const agentId = resolveSessionAgentId({
sessionKey: params.sessionKey,
config: params.cfg,
});
return {
text: buildTasksText({
sessionKey: params.sessionKey,
agentId,
}),
};
}
export const handleTasksCommand: CommandHandler = async (params, allowTextCommands) => {
if (!allowTextCommands) {
return null ;
}
const normalized = params.command.commandBodyNormalized;
if (normalized !== "/tasks" && !normalized.startsWith("/tasks " )) {
return null ;
}
if (!params.command.isAuthorizedSender) {
logVerbose(
`Ignoring /tasks from unauthorized sender: ${params.command.senderId || "<unknown>" }`,
);
return { shouldContinue: false };
}
if (normalized !== "/tasks" ) {
return {
shouldContinue: false ,
reply: { text: "Usage: /tasks" },
};
}
return {
shouldContinue: false ,
reply: await buildTasksReply(params),
};
};
Messung V0.5 in Prozent C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland