1/**
2 * Helpers for synthesizing "unavailable" `ServerProvider` snapshots.
3 *
4 * When `ServerSettings.providerInstances` (or persisted thread/session
5 * state) references a driver this build does not ship — typical after a
6 * downgrade from a fork or a feature-branch test session — the runtime
7 * needs to surface the entry to the UI without crashing. This module
8 * produces shadow snapshots that satisfy `ServerProvider`'s wire shape
9 * while signalling unavailability.
10 *
11 * @module unavailableProviderSnapshot
12 */
13import {
14 ProviderDriverKind,
15 type ProviderInstanceId,
16 type ServerProvider,
17} from "@t3tools/contracts";
18import * as DateTime from "effect/DateTime";
19import * as Effect from "effect/Effect";
20
21import { buildServerProvider } from "@t3tools/provider-core/server/snapshotProbe";
22
23export interface UnavailableProviderSnapshotInput {
24 readonly driverKind: ProviderDriverKind | string;
25 readonly instanceId: ProviderInstanceId;
26 readonly displayName?: string | undefined;
27 readonly accentColor?: string | undefined;
28 readonly reason: string;
29 /**
30 * Optional override for `checkedAt`. Defaulted to the current Effect
31 * `DateTime` so callers
32 * (notably tests) don't have to pass it.
33 */
34 readonly checkedAt?: string;
35}
36
37const nowIso = Effect.map(DateTime.now, DateTime.formatIso);
38
39/**
40 * Produce a `ServerProvider` snapshot representing a configured instance
41 * whose driver the running build does not implement. The result is safe
42 * to broadcast over the wire and is structured so the web UI can render
43 * a "missing driver" affordance without special-casing.
44 */
45export function buildUnavailableProviderSnapshot(
46 input: UnavailableProviderSnapshotInput,
47): Effect.Effect<ServerProvider> {
48 return Effect.gen(function* () {
49 const checkedAt = input.checkedAt ?? (yield* nowIso);
50 const displayName = input.displayName?.trim() || (input.driverKind as string);
51
52 const base = buildServerProvider({
53 presentation: { displayName },
54 enabled: false,
55 checkedAt,
56 models: [],
57 skills: [],
58 probe: {
59 installed: false,
60 version: null,
61 status: "error",
62 auth: { status: "unknown" },
63 message: input.reason,
64 },
65 });
66
67 return {
68 ...base,
69 instanceId: input.instanceId,
70 ...(input.accentColor ? { accentColor: input.accentColor } : {}),
71 driver:
72 typeof input.driverKind === "string"
73 ? ProviderDriverKind.make(input.driverKind)
74 : input.driverKind,
75 availability: "unavailable",
76 unavailableReason: input.reason,
77 };
78 });
79}