import {
resolveAgentConfig,
resolveAgentDir,
resolveDefaultAgentId,
resolveSessionAgentId,
resolveAgentModelFallbacksOverride,
} from "../agents/agent-scope.js" ;
import { resolveFastModeState } from "../agents/fast-mode.js" ;
import { selectAgentHarness } from "../agents/harness/selection.js" ;
import { resolveModelAuthLabel } from "../agents/model-auth-label.js" ;
import {
resolveInternalSessionKey,
resolveMainSessionAlias,
} from "../agents/tools/sessions-helpers.js" ;
import { normalizeGroupActivation } from "../auto-reply/group-activation.js" ;
import { resolveSelectedAndActiveModel } from "../auto-reply/model-runtime.js" ;
import type { ThinkLevel } from "../auto-reply/thinking.js" ;
import { toAgentModelListLike } from "../config/model-input.js" ;
import type { SessionEntry } from "../config/sessions.js" ;
import type { OpenClawConfig } from "../config/types.openclaw.js" ;
import {
formatUsageWindowSummary,
loadProviderUsageSummary,
resolveUsageProviderId,
} from "../infra/provider-usage.js" ;
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js" ;
import {
listTasksForAgentIdForStatus,
listTasksForSessionKeyForStatus,
} from "../tasks/task-status-access.js" ;
import {
buildTaskStatusSnapshot,
formatTaskStatusDetail,
formatTaskStatusTitle,
} from "../tasks/task-status.js" ;
import type { BuildStatusTextParams } from "./status-text.types.js" ;
export type { BuildStatusTextParams } from "./status-text.types.js" ;
const USAGE_OAUTH_ONLY_PROVIDERS = new Set([
"anthropic" ,
"github-copilot" ,
"google-gemini-cli" ,
"openai-codex" ,
]);
let statusMessageRuntimePromise: Promise<typeof import ("../auto-reply/status.runtime.js" )> | null =
null ;
let statusQueueRuntimePromise: Promise<typeof import ("./status-queue.runtime.js" )> | null = null ;
let statusSubagentsRuntimePromise: Promise<typeof import ("./status-subagents.runtime.js" )> | null =
null ;
function loadStatusMessageRuntime(): Promise<typeof import ("../auto-reply/status.runtime.js" )> {
const runtimePromise = (statusMessageRuntimePromise ??=
import ("./status-message.runtime.js" ).then((module) =>
module.loadStatusMessageRuntimeModule(),
));
return runtimePromise;
}
function loadStatusSubagentsRuntime(): Promise<typeof import ("./status-subagents.runtime.js" )> {
const runtimePromise = (statusSubagentsRuntimePromise ??=
import ("./status-subagents.runtime.js" ));
return runtimePromise;
}
function loadStatusQueueRuntime(): Promise<typeof import ("./status-queue.runtime.js" )> {
const runtimePromise = (statusQueueRuntimePromise ??= import ("./status-queue.runtime.js" ));
return runtimePromise;
}
function shouldLoadUsageSummary(params: {
provider?: string;
selectedModelAuth?: string;
}): boolean {
if (!params.provider) {
return false ;
}
if (!USAGE_OAUTH_ONLY_PROVIDERS.has(params.provider)) {
return true ;
}
const auth = normalizeOptionalLowercaseString(params.selectedModelAuth);
return Boolean (auth?.startsWith("oauth" ) || auth?.startsWith("token" ));
}
function formatSessionTaskLine(sessionKey: string): string | undefined {
const snapshot = buildTaskStatusSnapshot(listTasksForSessionKeyForStatus(sessionKey));
const task = snapshot.focus;
if (!task) {
return undefined;
}
const headline =
snapshot.activeCount > 0
? `${snapshot.activeCount} active · ${snapshot.totalCount} total`
: snapshot.recentFailureCount > 0
? `${snapshot.recentFailureCount} recent failure${snapshot.recentFailureCount === 1 ? "" : "s" }`
: "recently finished" ;
const title = formatTaskStatusTitle(task);
const detail = formatTaskStatusDetail(task);
const parts = [headline, task.runtime, title, detail].filter(Boolean );
return parts.length ? ` Tasks: ${parts.join(" · " )}` : undefined;
}
function resolveStatusHarnessId(params: {
cfg: OpenClawConfig;
provider: string;
model: string;
agentId: string;
sessionKey: string;
sessionEntry?: SessionEntry;
}): string | undefined {
try {
const selected = selectAgentHarness({
provider: params.provider,
modelId: params.model,
config: params.cfg,
agentId: params.agentId,
sessionKey: params.sessionKey,
agentHarnessId: params.sessionEntry?.agentHarnessId,
});
const id = normalizeOptionalLowercaseString(selected.id);
return id && id !== "pi" ? id : undefined;
} catch {
return undefined;
}
}
function formatAgentTaskCountsLine(agentId: string): string | undefined {
const snapshot = buildTaskStatusSnapshot(listTasksForAgentIdForStatus(agentId));
if (snapshot.totalCount === 0 ) {
return undefined;
}
return ` Tasks: ${snapshot.activeCount} active · ${snapshot.totalCount} total · agent-local`;
}
export async function buildStatusText(params: BuildStatusTextParams): Promise<string> {
const {
cfg,
sessionEntry,
sessionKey,
parentSessionKey,
sessionScope,
storePath,
statusChannel,
provider,
model,
contextTokens,
resolvedThinkLevel,
resolvedFastMode,
resolvedVerboseLevel,
resolvedReasoningLevel,
resolvedElevatedLevel,
resolveDefaultThinkingLevel,
isGroup,
defaultGroupActivation,
} = params;
const statusAgentId = sessionKey
? resolveSessionAgentId({ sessionKey, config: cfg })
: resolveDefaultAgentId(cfg);
const statusAgentDir = resolveAgentDir(cfg, statusAgentId);
const modelRefs = resolveSelectedAndActiveModel({
selectedProvider: provider,
selectedModel: model,
sessionEntry,
});
const selectedModelAuth = Object.hasOwn(params, "modelAuthOverride" )
? params.modelAuthOverride
: resolveModelAuthLabel({
provider,
cfg,
sessionEntry,
agentDir: statusAgentDir,
includeExternalProfiles: false ,
});
const activeModelAuth = Object.hasOwn(params, "activeModelAuthOverride" )
? params.activeModelAuthOverride
: modelRefs.activeDiffers
? resolveModelAuthLabel({
provider: modelRefs.active.provider,
cfg,
sessionEntry,
agentDir: statusAgentDir,
includeExternalProfiles: false ,
})
: selectedModelAuth;
const currentUsageProvider = (() => {
try {
return resolveUsageProviderId(provider);
} catch {
return undefined;
}
})();
let usageLine: string | null = null ;
if (
currentUsageProvider &&
shouldLoadUsageSummary({
provider: currentUsageProvider,
selectedModelAuth,
})
) {
try {
const usageSummaryTimeoutMs = 3500 ;
let usageTimeout: NodeJS.Timeout | undefined;
const usageSummary = await Promise.race([
loadProviderUsageSummary({
timeoutMs: usageSummaryTimeoutMs,
providers: [currentUsageProvider],
agentDir: statusAgentDir,
}),
new Promise<never>((_, reject) => {
usageTimeout = setTimeout(
() => reject(new Error("usage summary timeout" )),
usageSummaryTimeoutMs,
);
}),
]).finally (() => {
if (usageTimeout) {
clearTimeout(usageTimeout);
}
});
const usageEntry = usageSummary.providers[0 ];
if (usageEntry && !usageEntry.error && usageEntry.windows.length > 0 ) {
const summaryLine = formatUsageWindowSummary(usageEntry, {
now: Date.now(),
maxWindows: 2 ,
includeResets: true ,
});
if (summaryLine) {
usageLine = ` Usage: ${summaryLine}`;
}
}
} catch {
usageLine = null ;
}
}
const { getFollowupQueueDepth, resolveQueueSettings } = await loadStatusQueueRuntime();
const queueSettings = resolveQueueSettings({
cfg,
channel: statusChannel,
sessionEntry,
});
const queueKey = sessionKey ?? sessionEntry?.sessionId;
const queueDepth = queueKey ? getFollowupQueueDepth(queueKey) : 0 ;
const queueOverrides = Boolean (
sessionEntry?.queueDebounceMs ?? sessionEntry?.queueCap ?? sessionEntry?.queueDrop,
);
let subagentsLine: string | undefined;
let taskLine: string | undefined;
if (sessionKey) {
const { mainKey, alias } = resolveMainSessionAlias(cfg);
const requesterKey = resolveInternalSessionKey({ key: sessionKey, alias, mainKey });
taskLine = params.skipDefaultTaskLookup
? params.taskLineOverride
: (params.taskLineOverride ?? formatSessionTaskLine(requesterKey));
if (!taskLine && !params.skipDefaultTaskLookup) {
taskLine = formatAgentTaskCountsLine(statusAgentId);
}
const { buildSubagentsStatusLine, countPendingDescendantRuns, listControlledSubagentRuns } =
await loadStatusSubagentsRuntime();
const runs = listControlledSubagentRuns(requesterKey);
const verboseEnabled = resolvedVerboseLevel && resolvedVerboseLevel !== "off" ;
subagentsLine = buildSubagentsStatusLine({
runs,
verboseEnabled,
pendingDescendantsForRun: (entry) => countPendingDescendantRuns(entry.childSessionKey),
});
}
const groupActivation = isGroup
? (normalizeGroupActivation(sessionEntry?.groupActivation) ?? defaultGroupActivation())
: undefined;
const agentDefaults = cfg.agents?.defaults ?? {};
const agentConfig = resolveAgentConfig(cfg, statusAgentId);
const effectiveFastMode =
resolvedFastMode ??
resolveFastModeState({
cfg,
provider,
model,
agentId: statusAgentId,
sessionEntry,
}).enabled;
const effectiveHarness =
params.resolvedHarness ??
resolveStatusHarnessId({
cfg,
provider,
model,
agentId: statusAgentId,
sessionKey,
sessionEntry,
});
const agentFallbacksOverride = resolveAgentModelFallbacksOverride(cfg, statusAgentId);
const { buildStatusMessage } = await loadStatusMessageRuntime();
const explicitThinkingDefault =
(agentConfig?.thinkingDefault as ThinkLevel | undefined) ??
(agentDefaults.thinkingDefault as ThinkLevel | undefined);
return buildStatusMessage({
config: cfg,
agent: {
...agentDefaults,
model: {
...toAgentModelListLike(agentDefaults.model),
primary: params.primaryModelLabelOverride ?? `${provider}/${model}`,
...(agentFallbacksOverride === undefined ? {} : { fallbacks: agentFallbacksOverride }),
},
...(typeof contextTokens === "number" && contextTokens > 0 ? { contextTokens } : {}),
thinkingDefault: explicitThinkingDefault,
verboseDefault: agentDefaults.verboseDefault,
elevatedDefault: agentDefaults.elevatedDefault,
},
agentId: statusAgentId,
explicitConfiguredContextTokens:
typeof agentDefaults.contextTokens === "number" && agentDefaults.contextTokens > 0
? agentDefaults.contextTokens
: undefined,
sessionEntry,
sessionKey,
parentSessionKey,
sessionScope,
sessionStorePath: storePath,
groupActivation,
resolvedThink:
resolvedThinkLevel ?? explicitThinkingDefault ?? (await resolveDefaultThinkingLevel()),
resolvedFast: effectiveFastMode,
resolvedHarness: effectiveHarness,
resolvedVerbose: resolvedVerboseLevel,
resolvedReasoning: resolvedReasoningLevel,
resolvedElevated: resolvedElevatedLevel,
modelAuth: selectedModelAuth,
activeModelAuth,
usageLine: usageLine ?? undefined,
queue: {
mode: queueSettings.mode,
depth: queueDepth,
debounceMs: queueSettings.debounceMs,
cap: queueSettings.cap,
dropPolicy: queueSettings.dropPolicy,
showDetails: queueOverrides,
},
subagentsLine,
taskLine,
mediaDecisions: params.mediaDecisions,
includeTranscriptUsage: params.includeTranscriptUsage ?? true ,
});
}
Messung V0.5 in Prozent C=100 H=97 G=98
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland