Dashboard: fix Channels schema errors, auto-load Usage, align Telegram health#31307
Dashboard: fix Channels schema errors, auto-load Usage, align Telegram health#31307stakeswky wants to merge 2 commits intoopenclaw:mainfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6aa7a5a1ec
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const [status, health, channels, models, heartbeat] = await Promise.all([ | ||
| state.client.request("status", {}), | ||
| state.client.request("health", {}), | ||
| state.client.request("channels.status", { probe: false, timeoutMs: 8000 }), | ||
| state.client.request("models.list", {}), |
There was a problem hiding this comment.
Keep debug refresh resilient when channels.status fails
loadDebug now bundles channels.status into the main Promise.all, so any failure from that extra call (for example an older gateway without the method, a channel plugin error, or a denied method) rejects the whole batch and prevents status, health, models, and last-heartbeat from updating. This makes the debug tab lose all core snapshots in cases where it previously still worked; fetch channels.status as a best-effort side request instead of making it a hard dependency.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR delivers four fixes: auto-loading the Usage tab on first open, precise Key observations:
Confidence Score: 3/5
Last reviewed commit: 6aa7a5a |
| const [status, health, channels, models, heartbeat] = await Promise.all([ | ||
| state.client.request("status", {}), | ||
| state.client.request("health", {}), | ||
| state.client.request("channels.status", { probe: false, timeoutMs: 8000 }), | ||
| state.client.request("models.list", {}), | ||
| state.client.request("last-heartbeat", {}), | ||
| ]); |
There was a problem hiding this comment.
channels.status failure will break the entire Debug panel
channels.status is added to the same Promise.all as the other critical debug requests (status, health, models.list, last-heartbeat). If this endpoint is unavailable (e.g., no channel configured, older gateway version, or a network timeout), the whole Promise.all rejects and the catch block sets debugCallError — meaning users lose all debug information, not just the Telegram health alignment.
Since the channel reconciliation is purely cosmetic (aligning running/lastStartAt), it should be fetched independently so it cannot poison the other requests:
const [status, health, models, heartbeat] = await Promise.all([
state.client.request("status", {}),
state.client.request("health", {}),
state.client.request("models.list", {}),
state.client.request("last-heartbeat", {}),
]);
// Non-critical: align Telegram fields from channels snapshot
const channelsSnapshot = await state.client
.request("channels.status", { probe: false, timeoutMs: 8000 })
.catch(() => null);Or alternatively, use Promise.allSettled and handle the settled result for channels separately.
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/controllers/debug.ts
Line: 27-33
Comment:
**`channels.status` failure will break the entire Debug panel**
`channels.status` is added to the same `Promise.all` as the other critical debug requests (`status`, `health`, `models.list`, `last-heartbeat`). If this endpoint is unavailable (e.g., no channel configured, older gateway version, or a network timeout), the whole `Promise.all` rejects and the catch block sets `debugCallError` — meaning users lose all debug information, not just the Telegram health alignment.
Since the channel reconciliation is purely cosmetic (aligning `running`/`lastStartAt`), it should be fetched independently so it cannot poison the other requests:
```ts
const [status, health, models, heartbeat] = await Promise.all([
state.client.request("status", {}),
state.client.request("health", {}),
state.client.request("models.list", {}),
state.client.request("last-heartbeat", {}),
]);
// Non-critical: align Telegram fields from channels snapshot
const channelsSnapshot = await state.client
.request("channels.status", { probe: false, timeoutMs: 8000 })
.catch(() => null);
```
Or alternatively, use `Promise.allSettled` and handle the settled result for `channels` separately.
How can I resolve this? If you propose a fix, please make it concise.| const telegramRuntime = channelsSnapshot?.channels?.telegram; | ||
| const telegramHealth = (debugHealth?.channels as Record<string, unknown> | undefined) | ||
| ?.telegram as Record<string, unknown> | undefined; | ||
| if (telegramHealth && telegramRuntime) { | ||
| if (typeof telegramRuntime.running === "boolean") { | ||
| telegramHealth.running = telegramRuntime.running; | ||
| } | ||
| if (telegramRuntime.lastStartAt != null) { | ||
| telegramHealth.lastStartAt = telegramRuntime.lastStartAt; | ||
| } | ||
| } |
There was a problem hiding this comment.
Telegram-only reconciliation is asymmetric
The reconciliation logic only applies to the telegram key inside channels. If the same running/lastStartAt mismatch exists for other channel types (WhatsApp, Discord, etc.), they will remain inconsistent. Consider iterating over all keys in channelsSnapshot.channels and applying the same alignment for any channel that has a corresponding entry in debugHealth.channels:
const channelsHealth = debugHealth?.channels as Record<string, Record<string, unknown>> | undefined;
if (channelsHealth && channelsSnapshot?.channels) {
for (const [name, runtime] of Object.entries(channelsSnapshot.channels)) {
const healthEntry = channelsHealth[name];
if (healthEntry && runtime) {
if (typeof runtime.running === "boolean") healthEntry.running = runtime.running;
if (runtime.lastStartAt != null) healthEntry.lastStartAt = runtime.lastStartAt;
}
}
}Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/controllers/debug.ts
Line: 41-51
Comment:
**Telegram-only reconciliation is asymmetric**
The reconciliation logic only applies to the `telegram` key inside `channels`. If the same `running`/`lastStartAt` mismatch exists for other channel types (WhatsApp, Discord, etc.), they will remain inconsistent. Consider iterating over all keys in `channelsSnapshot.channels` and applying the same alignment for any channel that has a corresponding entry in `debugHealth.channels`:
```ts
const channelsHealth = debugHealth?.channels as Record<string, Record<string, unknown>> | undefined;
if (channelsHealth && channelsSnapshot?.channels) {
for (const [name, runtime] of Object.entries(channelsSnapshot.channels)) {
const healthEntry = channelsHealth[name];
if (healthEntry && runtime) {
if (typeof runtime.running === "boolean") healthEntry.running = runtime.running;
if (runtime.lastStartAt != null) healthEntry.lastStartAt = runtime.lastStartAt;
}
}
}
```
How can I resolve this? If you propose a fix, please make it concise.
Fixes #31247\n\n### Summary\n- Dashboard Usage tab now loads data on first open (previously only loaded after manual refresh).\n- Config schema analysis now reports unsupported paths precisely for additionalProperties schemas (avoids marking whole parent objects as unsupported).\n- Config search respects tag filters when matching section names/labels (prevents false positives that hide the empty-results UI).\n- Debug health: align Telegram running/lastStartAt with Channels snapshot to avoid misleading mismatch in the dashboard.\n\n### Notes\n- The schema fix targets the form renderer's unsupported-path tracking so Telegram channel fields like accounts/capabilities no longer force Raw mode for the entire Channels section.\n\n