From a17d4371d101da101d8a263698d5499d681d066c Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 4 May 2026 22:52:00 -0700 Subject: [PATCH] feat(status): show uptime in chat status Show compact Gateway process and host system uptime in chat /status output. --- CHANGELOG.md | 1 + docs/cli/status.md | 1 + docs/tools/slash-commands.md | 2 +- src/auto-reply/reply/commands-status.test.ts | 31 ++++++++++++++++++++ src/status/status-message.ts | 2 ++ src/status/status-text.ts | 13 ++++++++ 6 files changed, 49 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72153a51f99..d1e8b8761f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai ### Changes - Gateway/Windows: bind the default loopback gateway listener only to `127.0.0.1` on Windows so libuv's dual-stack `::1` behavior cannot wedge localhost HTTP requests. (#69701, fixes #69674) Thanks @SARAMALI15792. +- Status: show compact Gateway process uptime and host system uptime in `/status`, making restart and host-lifetime checks visible from chat. Thanks @vincentkoc. - Contributor PRs: require external pull requests to include after-fix real behavior proof from a real OpenClaw setup, with terminal screenshots, console output, redacted runtime logs, linked artifacts, and copied live output treated as valid evidence while unit tests, mocks, lint, typechecks, snapshots, and CI remain supplemental only. - Plugins/migration: emit catalog-backed install hints when `plugins.entries` or `plugins.allow` references an official external plugin that is not installed, so upgraded configs point operators to `openclaw plugins install ` instead of telling them to remove valid plugin config. (#77483) Thanks @hclsys. - OpenAI/Codex media: advertise Codex audio transcription in runtime and manifest metadata and route active Codex chat models to the OpenAI transcription default instead of sending chat model ids to audio transcription. Thanks @vincentkoc. diff --git a/docs/cli/status.md b/docs/cli/status.md index b9281899303..8577e869ec5 100644 --- a/docs/cli/status.md +++ b/docs/cli/status.md @@ -26,6 +26,7 @@ Notes: - Session status output separates `Execution:` from `Runtime:`. `Execution` is the sandbox path (`direct`, `docker/*`), while `Runtime` tells you whether the session is using `OpenClaw Pi Default`, `OpenAI Codex`, a CLI backend, or an ACP backend such as `codex (acp/acpx)`. See [Agent runtimes](/concepts/agent-runtimes) for the provider/model/runtime distinction. - MiniMax's raw `usage_percent` / `usagePercent` fields are remaining quota, so OpenClaw inverts them before display; count-based fields win when present. `model_remains` responses prefer the chat-model entry, derive the window label from timestamps when needed, and include the model name in the plan label. - When the current session snapshot is sparse, `/status` can backfill token and cache counters from the most recent transcript usage log. Existing nonzero live values still win over transcript fallback values. +- `/status` includes compact Gateway process uptime and host system uptime. - Transcript fallback can also recover the active runtime model label when the live session entry is missing it. If that transcript model differs from the selected model, status resolves the context window against the recovered runtime model instead of the selected one. - For prompt-size accounting, transcript fallback prefers the larger prompt-oriented total when session metadata is missing or smaller, so custom-provider sessions do not collapse to `0` token displays. - Output includes per-agent session stores when multiple agents are configured. diff --git a/docs/tools/slash-commands.md b/docs/tools/slash-commands.md index 9f5475b6a0f..07735a3a15b 100644 --- a/docs/tools/slash-commands.md +++ b/docs/tools/slash-commands.md @@ -152,7 +152,7 @@ Current source-of-truth: - `/help` shows the short help summary. - `/commands` shows the generated command catalog. - `/tools [compact|verbose]` shows what the current agent can use right now. - - `/status` shows execution/runtime status, including `Execution`/`Runtime` labels and provider usage/quota when available. + - `/status` shows execution/runtime status, Gateway and system uptime, plus provider usage/quota when available. - `/diagnostics [note]` is the owner-only support-report flow for Gateway bugs and Codex harness runs. It asks for explicit exec approval every time before running `openclaw gateway diagnostics export --json`; do not approve diagnostics with an allow-all rule. After approval, it sends a pasteable report with the local bundle path, manifest summary, privacy notes, and relevant session ids. In group chats, the approval prompt and report go to the owner privately. When the active session uses the OpenAI Codex harness, the same approval also sends relevant Codex feedback to OpenAI servers and the completed reply lists the OpenClaw session ids, Codex thread ids, and `codex resume ` commands. See [Diagnostics Export](/gateway/diagnostics). - `/crestodian ` runs the Crestodian setup and repair helper from an owner DM. - `/tasks` lists active/recent background tasks for the current session. diff --git a/src/auto-reply/reply/commands-status.test.ts b/src/auto-reply/reply/commands-status.test.ts index eab1ed48d37..6c026b4210a 100644 --- a/src/auto-reply/reply/commands-status.test.ts +++ b/src/auto-reply/reply/commands-status.test.ts @@ -494,6 +494,37 @@ describe("buildStatusReply subagent summary", () => { }); }); + it("shows gateway and system uptime in /status output", async () => { + vi.spyOn(process, "uptime").mockReturnValue(2 * 60 * 60 + 5 * 60); + vi.spyOn(os, "uptime").mockReturnValue(4 * 24 * 60 * 60 + 3 * 60 * 60); + + const text = await buildStatusText({ + cfg: baseCfg, + sessionEntry: { + sessionId: "sess-status-uptime", + updatedAt: 0, + contextTokens: 32_000, + }, + sessionKey: "agent:main:main", + parentSessionKey: "agent:main:main", + sessionScope: "per-sender", + statusChannel: "mobilechat", + provider: "anthropic", + model: "claude-opus-4-5", + contextTokens: 32_000, + resolvedFastMode: false, + resolvedVerboseLevel: "off", + resolvedReasoningLevel: "off", + resolveDefaultThinkingLevel: async () => undefined, + isGroup: false, + defaultGroupActivation: () => "mention", + modelAuthOverride: "api-key", + activeModelAuthOverride: "api-key", + }); + + expect(normalizeTestText(text)).toContain("Uptime: gateway 2h 5m · system 4d 3h"); + }); + it("shows the effective non-PI embedded harness in /status", async () => { registerStatusCodexHarness(); diff --git a/src/status/status-message.ts b/src/status/status-message.ts index 7a39cb4c07d..4a0401adc41 100644 --- a/src/status/status-message.ts +++ b/src/status/status-message.ts @@ -97,6 +97,7 @@ export type StatusArgs = { activeModelAuth?: string; usageLine?: string; timeLine?: string; + uptimeLine?: string; queue?: QueueStatus; mediaDecisions?: ReadonlyArray; subagentsLine?: string; @@ -961,6 +962,7 @@ export function buildStatusMessage(args: StatusArgs): string { return [ versionLine, args.timeLine, + args.uptimeLine, modelLine, configuredFallbacksLine, fallbackLine, diff --git a/src/status/status-text.ts b/src/status/status-text.ts index c4573396386..0c3bd1c3633 100644 --- a/src/status/status-text.ts +++ b/src/status/status-text.ts @@ -1,3 +1,4 @@ +import os from "node:os"; import { resolveAgentConfig, resolveAgentDir, @@ -18,6 +19,7 @@ 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 { formatDurationCompact } from "../infra/format-time/format-duration.ts"; import { formatUsageWindowSummary, loadProviderUsageSummary, @@ -156,6 +158,16 @@ function formatAgentTaskCountsLine(agentId: string): string | undefined { return `📌 Tasks: ${snapshot.activeCount} active · ${snapshot.totalCount} total · agent-local`; } +function formatStatusUptimeDuration(ms: number): string { + return formatDurationCompact(ms, { spaced: true }) ?? "0s"; +} + +export function buildStatusUptimeLine(): string { + const gatewayUptimeMs = Math.max(0, Math.round(process.uptime() * 1000)); + const systemUptimeMs = Math.max(0, Math.round(os.uptime() * 1000)); + return `⏱️ Uptime: gateway ${formatStatusUptimeDuration(gatewayUptimeMs)} · system ${formatStatusUptimeDuration(systemUptimeMs)}`; +} + export async function buildStatusText(params: BuildStatusTextParams): Promise { const { cfg, @@ -365,6 +377,7 @@ export async function buildStatusText(params: BuildStatusTextParams): Promise