diff --git a/src/mirror/status/status.ts b/src/mirror/status/status.ts index 7487c58af0..c5aabddfd1 100644 --- a/src/mirror/status/status.ts +++ b/src/mirror/status/status.ts @@ -1,6 +1,7 @@ import type { MirrorRuntimeHost } from "../../mirror-service/index.js"; import { getMirrordaemonHealthState, + getMirrordaemonProvidersState, getMirrordaemonRuntimeState, } from "../../mirrordaemon/index.js"; @@ -50,7 +51,6 @@ export type GetMirrorStatusOptions = { export async function getMirrorStatus(opts: GetMirrorStatusOptions): Promise { const daemon = opts.runtimeHost.daemon; - const boot = daemon.getBootSnapshot(); const peers = opts.runtimeHost.syncManager.listPeers(); const baseUrl = opts.runtimeHost.syncManager.getLocalBaseUrl(); const runtime = getMirrordaemonRuntimeState(daemon, { @@ -62,23 +62,26 @@ export async function getMirrorStatus(opts: GetMirrorStatusOptions): Promise ({ + active_provider_id: providers.active_provider_id, + total: providers.total, + available: providers.available, + fallback_available: providers.fallback_available, + providers: providers.providers.map((provider) => ({ provider_id: provider.provider_id, label: provider.label, url: provider.url, @@ -88,16 +91,16 @@ export async function getMirrorStatus(opts: GetMirrorStatusOptions): Promise { expect(status.runtime.node_id).toBe("status-node"); expect(status.runtime.sessions.total).toBe(0); expect(status.service.lore_dir).toBe(path.resolve(loreDir)); + expect(status.lore.dir).toBe(status.service.lore_dir); + expect(status.service.workspace_users_root).toBe(status.workspace.users_root); expect(status.provider.configured).toBe(false); expect(status.provider.active_provider_id).toBe("primary"); expect(status.provider.total).toBe(1); @@ -71,6 +73,7 @@ describe("mirror status", () => { expect(status.runtime.sessions.total).toBe(1); expect(status.runtime.sessions.open).toBe(1); + expect(status.sync.node_id).toBe(status.runtime.node_id); expect(status.provider.providers[0]?.provider_id).toBe("primary"); const json = JSON.stringify(status); diff --git a/src/mirrordaemon/daemon_types.ts b/src/mirrordaemon/daemon_types.ts index d0e35c3130..9bf7c781f0 100644 --- a/src/mirrordaemon/daemon_types.ts +++ b/src/mirrordaemon/daemon_types.ts @@ -194,9 +194,11 @@ export type MirrordaemonProvidersSummary = { provider_id: string; label: string; kind: string; + url: string; ready: boolean; configured: boolean; selected: boolean; + last_error?: string; }>; }; diff --git a/src/mirrordaemon/runtime_state.test.ts b/src/mirrordaemon/runtime_state.test.ts index 197783eb98..16ea7aab51 100644 --- a/src/mirrordaemon/runtime_state.test.ts +++ b/src/mirrordaemon/runtime_state.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "vitest"; import { + buildProvidersSummary, buildDebugSnapshot, buildHealthSummary, buildRuntimeSummary, @@ -227,4 +228,65 @@ describe("mirrordaemon runtime state", () => { expect(health.event_stream.recent_events).toBe(daemon.getRecentEvents().length); expect(health.sync.peers_known).toBe(2); }); + + it("includes provider url and last_error in the daemon provider summary", () => { + const daemon = createMirrordaemon({ + config: baseConfig, + lifecycle: { + discoveredLoreFiles: 2, + shutdown: async () => undefined, + }, + runtimeStartedAt: "2026-03-13T00:00:00.000Z", + }); + + const providers = buildProvidersSummary(daemon, { + providerPlane: { + listProviders: () => [ + { + provider_id: "primary", + label: "Primary", + kind: "openai-compatible", + url: "http://brain.local/v1/chat/completions", + ready: true, + configured: true, + selected: true, + last_error: undefined, + }, + { + provider_id: "fallback", + label: "Fallback", + kind: "openai-compatible", + url: "http://brain.local/v1/fallback", + ready: false, + configured: false, + selected: false, + last_error: "provider unavailable", + }, + ], + } as never, + }); + + expect(providers.providers).toEqual([ + { + provider_id: "primary", + label: "Primary", + kind: "openai-compatible", + url: "http://brain.local/v1/chat/completions", + ready: true, + configured: true, + selected: true, + last_error: undefined, + }, + { + provider_id: "fallback", + label: "Fallback", + kind: "openai-compatible", + url: "http://brain.local/v1/fallback", + ready: false, + configured: false, + selected: false, + last_error: "provider unavailable", + }, + ]); + }); }); diff --git a/src/mirrordaemon/runtime_state.ts b/src/mirrordaemon/runtime_state.ts index 260905be59..f4e0575dfb 100644 --- a/src/mirrordaemon/runtime_state.ts +++ b/src/mirrordaemon/runtime_state.ts @@ -95,9 +95,11 @@ export function buildProvidersSummary( provider_id: provider.provider_id, label: provider.label, kind: provider.kind, + url: provider.url, ready: provider.ready, configured: provider.configured, selected: provider.selected, + last_error: provider.last_error, })) ?? []; return {