mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-20 13:13:06 +00:00
fix(status): align session_status with /status
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { normalizeGroupActivation } from "../../auto-reply/group-activation.js";
|
||||
import { getFollowupQueueDepth, resolveQueueSettings } from "../../auto-reply/reply/queue.js";
|
||||
import { buildStatusMessage } from "../../auto-reply/status.js";
|
||||
import { buildStatusText } from "../../auto-reply/reply/commands-status.js";
|
||||
import type {
|
||||
ElevatedLevel,
|
||||
ReasoningLevel,
|
||||
ThinkLevel,
|
||||
VerboseLevel,
|
||||
} from "../../auto-reply/thinking.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import {
|
||||
@@ -11,11 +15,6 @@ import {
|
||||
updateSessionStore,
|
||||
} from "../../config/sessions.js";
|
||||
import { resolveSessionModelIdentityRef } from "../../gateway/session-utils.js";
|
||||
import {
|
||||
formatUsageWindowSummary,
|
||||
loadProviderUsageSummary,
|
||||
resolveUsageProviderId,
|
||||
} from "../../infra/provider-usage.js";
|
||||
import {
|
||||
buildAgentMainSessionKey,
|
||||
DEFAULT_AGENT_ID,
|
||||
@@ -24,9 +23,6 @@ import {
|
||||
} from "../../routing/session-key.js";
|
||||
import { applyModelOverrideToSessionEntry } from "../../sessions/model-overrides.js";
|
||||
import { listTasksForRelatedSessionKeyForOwner } from "../../tasks/task-owner-access.js";
|
||||
import { resolveAgentConfig, resolveAgentDir } from "../agent-scope.js";
|
||||
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
|
||||
import { resolveModelAuthLabel } from "../model-auth-label.js";
|
||||
import { loadModelCatalog } from "../model-catalog.js";
|
||||
import {
|
||||
buildAllowedModelSet,
|
||||
@@ -449,7 +445,6 @@ export function createSessionStatusTool(opts?: {
|
||||
}
|
||||
}
|
||||
|
||||
const agentDir = resolveAgentDir(cfg, agentId);
|
||||
const runtimeModelIdentity = resolveSessionModelIdentityRef(
|
||||
cfg,
|
||||
resolved.entry,
|
||||
@@ -467,118 +462,54 @@ export function createSessionStatusTool(opts?: {
|
||||
const defaultModelForCard = hasExplicitModelOverride
|
||||
? configured.model
|
||||
: runtimeModelForCard || configured.model;
|
||||
// Preserve the "provider unknown" case for legacy runtime-only sessions so the
|
||||
// status card does not synthesize a configured provider/model pair that never ran.
|
||||
const statusSessionEntry =
|
||||
!hasExplicitModelOverride && !runtimeProviderForCard && runtimeModelForCard
|
||||
? { ...resolved.entry, providerOverride: "" }
|
||||
: resolved.entry;
|
||||
const providerOverrideForCard = statusSessionEntry.providerOverride?.trim();
|
||||
const providerForCard = providerOverrideForCard ?? defaultProviderForCard;
|
||||
const usageProvider = resolveUsageProviderId(providerForCard);
|
||||
let usageLine: string | undefined;
|
||||
if (usageProvider) {
|
||||
try {
|
||||
const usageSummary = await loadProviderUsageSummary({
|
||||
timeoutMs: 3500,
|
||||
providers: [usageProvider],
|
||||
agentDir,
|
||||
});
|
||||
const snapshot = usageSummary.providers.find((entry) => entry.provider === usageProvider);
|
||||
if (snapshot) {
|
||||
const formatted = formatUsageWindowSummary(snapshot, {
|
||||
now: Date.now(),
|
||||
maxWindows: 2,
|
||||
includeResets: true,
|
||||
});
|
||||
if (formatted && !formatted.startsWith("error:")) {
|
||||
usageLine = `📊 Usage: ${formatted}`;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
const primaryModelLabel =
|
||||
providerForCard && defaultModelForCard
|
||||
? `${providerForCard}/${defaultModelForCard}`
|
||||
: defaultModelForCard;
|
||||
const isGroup =
|
||||
resolved.entry.chatType === "group" ||
|
||||
resolved.entry.chatType === "channel" ||
|
||||
statusSessionEntry.chatType === "group" ||
|
||||
statusSessionEntry.chatType === "channel" ||
|
||||
resolved.key.includes(":group:") ||
|
||||
resolved.key.includes(":channel:");
|
||||
const groupActivation = isGroup
|
||||
? (normalizeGroupActivation(resolved.entry.groupActivation) ?? "mention")
|
||||
: undefined;
|
||||
|
||||
const queueSettings = resolveQueueSettings({
|
||||
cfg,
|
||||
channel:
|
||||
resolved.entry.channel ??
|
||||
resolved.entry.lastChannel ??
|
||||
resolved.entry.origin?.provider ??
|
||||
"unknown",
|
||||
sessionEntry: resolved.entry,
|
||||
});
|
||||
const queueKey = resolved.key ?? resolved.entry.sessionId;
|
||||
const queueDepth = queueKey ? getFollowupQueueDepth(queueKey) : 0;
|
||||
const queueOverrides = Boolean(
|
||||
resolved.entry.queueDebounceMs ?? resolved.entry.queueCap ?? resolved.entry.queueDrop,
|
||||
);
|
||||
|
||||
const userTimezone = resolveUserTimezone(cfg.agents?.defaults?.userTimezone);
|
||||
const userTimeFormat = resolveUserTimeFormat(cfg.agents?.defaults?.timeFormat);
|
||||
const userTime = formatUserTime(new Date(), userTimezone, userTimeFormat);
|
||||
const timeLine = userTime
|
||||
? `🕒 Time: ${userTime} (${userTimezone})`
|
||||
: `🕒 Time zone: ${userTimezone}`;
|
||||
|
||||
const agentDefaults = cfg.agents?.defaults ?? {};
|
||||
const agentConfig = resolveAgentConfig(cfg, agentId);
|
||||
const defaultLabel = defaultProviderForCard
|
||||
? `${defaultProviderForCard}/${defaultModelForCard}`
|
||||
: defaultModelForCard;
|
||||
const agentModel =
|
||||
typeof agentDefaults.model === "object" && agentDefaults.model
|
||||
? { ...agentDefaults.model, primary: defaultLabel }
|
||||
: { primary: defaultLabel };
|
||||
const statusText = buildStatusMessage({
|
||||
config: cfg,
|
||||
agent: {
|
||||
...agentDefaults,
|
||||
model: agentModel,
|
||||
thinkingDefault: agentConfig?.thinkingDefault ?? agentDefaults.thinkingDefault,
|
||||
},
|
||||
agentId,
|
||||
explicitConfiguredContextTokens:
|
||||
typeof agentDefaults.contextTokens === "number" && agentDefaults.contextTokens > 0
|
||||
? agentDefaults.contextTokens
|
||||
: undefined,
|
||||
sessionEntry: statusSessionEntry,
|
||||
sessionKey: resolved.key,
|
||||
sessionStorePath: storePath,
|
||||
groupActivation,
|
||||
modelAuth: resolveModelAuthLabel({
|
||||
provider: providerForCard,
|
||||
cfg,
|
||||
sessionEntry: statusSessionEntry,
|
||||
agentDir,
|
||||
}),
|
||||
usageLine,
|
||||
timeLine,
|
||||
queue: {
|
||||
mode: queueSettings.mode,
|
||||
depth: queueDepth,
|
||||
debounceMs: queueSettings.debounceMs,
|
||||
cap: queueSettings.cap,
|
||||
dropPolicy: queueSettings.dropPolicy,
|
||||
showDetails: queueOverrides,
|
||||
},
|
||||
includeTranscriptUsage: true,
|
||||
});
|
||||
const taskLine = formatSessionTaskLine({
|
||||
relatedSessionKey: resolved.key,
|
||||
callerOwnerKey: visibilityRequesterKey,
|
||||
});
|
||||
const fullStatusText = taskLine ? `${statusText}\n${taskLine}` : statusText;
|
||||
const statusText = await buildStatusText({
|
||||
cfg,
|
||||
sessionEntry: statusSessionEntry,
|
||||
sessionKey: resolved.key,
|
||||
parentSessionKey: statusSessionEntry.parentSessionKey,
|
||||
sessionScope: cfg.session?.scope,
|
||||
storePath,
|
||||
statusChannel:
|
||||
statusSessionEntry.channel ??
|
||||
statusSessionEntry.lastChannel ??
|
||||
statusSessionEntry.origin?.provider ??
|
||||
"unknown",
|
||||
provider: providerForCard,
|
||||
model: defaultModelForCard,
|
||||
resolvedThinkLevel: statusSessionEntry.thinkingLevel as ThinkLevel | undefined,
|
||||
resolvedFastMode: statusSessionEntry.fastMode,
|
||||
resolvedVerboseLevel: (statusSessionEntry.verboseLevel ?? "off") as VerboseLevel,
|
||||
resolvedReasoningLevel: (statusSessionEntry.reasoningLevel ?? "off") as ReasoningLevel,
|
||||
resolvedElevatedLevel: statusSessionEntry.elevatedLevel as ElevatedLevel | undefined,
|
||||
resolveDefaultThinkingLevel: async () => cfg.agents?.defaults?.thinkingDefault,
|
||||
isGroup,
|
||||
defaultGroupActivation: () => "mention",
|
||||
taskLineOverride: taskLine,
|
||||
skipDefaultTaskLookup: true,
|
||||
primaryModelLabelOverride: primaryModelLabel,
|
||||
...(providerForCard ? {} : { modelAuthOverride: undefined }),
|
||||
});
|
||||
const fullStatusText =
|
||||
taskLine && !statusText.includes(taskLine) ? `${statusText}\n${taskLine}` : statusText;
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: fullStatusText }],
|
||||
|
||||
@@ -106,14 +106,54 @@ export async function buildStatusReply(params: {
|
||||
defaultGroupActivation: () => "always" | "mention";
|
||||
mediaDecisions?: MediaUnderstandingDecision[];
|
||||
}): Promise<ReplyPayload | undefined> {
|
||||
const { command } = params;
|
||||
if (!command.isAuthorizedSender) {
|
||||
logVerbose(`Ignoring /status from unauthorized sender: ${command.senderId || "<unknown>"}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
text: await buildStatusText({
|
||||
...params,
|
||||
statusChannel: command.channel,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function buildStatusText(params: {
|
||||
cfg: OpenClawConfig;
|
||||
sessionEntry?: SessionEntry;
|
||||
sessionKey: string;
|
||||
parentSessionKey?: string;
|
||||
sessionScope?: SessionScope;
|
||||
storePath?: string;
|
||||
statusChannel: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
contextTokens?: number;
|
||||
resolvedThinkLevel?: ThinkLevel;
|
||||
resolvedFastMode?: boolean;
|
||||
resolvedVerboseLevel: VerboseLevel;
|
||||
resolvedReasoningLevel: ReasoningLevel;
|
||||
resolvedElevatedLevel?: ElevatedLevel;
|
||||
resolveDefaultThinkingLevel: () => Promise<ThinkLevel | undefined>;
|
||||
isGroup: boolean;
|
||||
defaultGroupActivation: () => "always" | "mention";
|
||||
mediaDecisions?: MediaUnderstandingDecision[];
|
||||
taskLineOverride?: string;
|
||||
skipDefaultTaskLookup?: boolean;
|
||||
primaryModelLabelOverride?: string;
|
||||
modelAuthOverride?: string;
|
||||
activeModelAuthOverride?: string;
|
||||
}): Promise<string> {
|
||||
const {
|
||||
cfg,
|
||||
command,
|
||||
sessionEntry,
|
||||
sessionKey,
|
||||
parentSessionKey,
|
||||
sessionScope,
|
||||
storePath,
|
||||
statusChannel,
|
||||
provider,
|
||||
model,
|
||||
contextTokens,
|
||||
@@ -126,10 +166,6 @@ export async function buildStatusReply(params: {
|
||||
isGroup,
|
||||
defaultGroupActivation,
|
||||
} = params;
|
||||
if (!command.isAuthorizedSender) {
|
||||
logVerbose(`Ignoring /status from unauthorized sender: ${command.senderId || "<unknown>"}`);
|
||||
return undefined;
|
||||
}
|
||||
const statusAgentId = sessionKey
|
||||
? resolveSessionAgentId({ sessionKey, config: cfg })
|
||||
: resolveDefaultAgentId(cfg);
|
||||
@@ -139,20 +175,24 @@ export async function buildStatusReply(params: {
|
||||
selectedModel: model,
|
||||
sessionEntry,
|
||||
});
|
||||
const selectedModelAuth = resolveModelAuthLabel({
|
||||
provider,
|
||||
cfg,
|
||||
sessionEntry,
|
||||
agentDir: statusAgentDir,
|
||||
});
|
||||
const activeModelAuth = modelRefs.activeDiffers
|
||||
? resolveModelAuthLabel({
|
||||
provider: modelRefs.active.provider,
|
||||
const selectedModelAuth = Object.hasOwn(params, "modelAuthOverride")
|
||||
? params.modelAuthOverride
|
||||
: resolveModelAuthLabel({
|
||||
provider,
|
||||
cfg,
|
||||
sessionEntry,
|
||||
agentDir: statusAgentDir,
|
||||
})
|
||||
: selectedModelAuth;
|
||||
});
|
||||
const activeModelAuth = Object.hasOwn(params, "activeModelAuthOverride")
|
||||
? params.activeModelAuthOverride
|
||||
: modelRefs.activeDiffers
|
||||
? resolveModelAuthLabel({
|
||||
provider: modelRefs.active.provider,
|
||||
cfg,
|
||||
sessionEntry,
|
||||
agentDir: statusAgentDir,
|
||||
})
|
||||
: selectedModelAuth;
|
||||
const currentUsageProvider = (() => {
|
||||
try {
|
||||
return resolveUsageProviderId(provider);
|
||||
@@ -205,7 +245,7 @@ export async function buildStatusReply(params: {
|
||||
}
|
||||
const queueSettings = resolveQueueSettings({
|
||||
cfg,
|
||||
channel: command.channel,
|
||||
channel: statusChannel,
|
||||
sessionEntry,
|
||||
});
|
||||
const queueKey = sessionKey ?? sessionEntry?.sessionId;
|
||||
@@ -219,8 +259,10 @@ export async function buildStatusReply(params: {
|
||||
if (sessionKey) {
|
||||
const { mainKey, alias } = resolveMainSessionAlias(cfg);
|
||||
const requesterKey = resolveInternalSessionKey({ key: sessionKey, alias, mainKey });
|
||||
taskLine = formatSessionTaskLine(requesterKey);
|
||||
if (!taskLine) {
|
||||
taskLine = params.skipDefaultTaskLookup
|
||||
? params.taskLineOverride
|
||||
: (params.taskLineOverride ?? formatSessionTaskLine(requesterKey));
|
||||
if (!taskLine && !params.skipDefaultTaskLookup) {
|
||||
taskLine = formatAgentTaskCountsLine(statusAgentId);
|
||||
}
|
||||
const runs = listControlledSubagentRuns(requesterKey);
|
||||
@@ -262,9 +304,9 @@ export async function buildStatusReply(params: {
|
||||
...agentDefaults,
|
||||
model: {
|
||||
...toAgentModelListLike(agentDefaults.model),
|
||||
primary: `${provider}/${model}`,
|
||||
primary: params.primaryModelLabelOverride ?? `${provider}/${model}`,
|
||||
},
|
||||
contextTokens,
|
||||
...(typeof contextTokens === "number" && contextTokens > 0 ? { contextTokens } : {}),
|
||||
thinkingDefault: agentConfig?.thinkingDefault ?? agentDefaults.thinkingDefault,
|
||||
verboseDefault: agentDefaults.verboseDefault,
|
||||
elevatedDefault: agentDefaults.elevatedDefault,
|
||||
@@ -302,5 +344,5 @@ export async function buildStatusReply(params: {
|
||||
includeTranscriptUsage: false,
|
||||
});
|
||||
|
||||
return { text: statusText };
|
||||
return statusText;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user