niahere 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -103,11 +103,11 @@ export interface SessionFinalizationConfig {
103
103
  export type BackendName = "claude" | "codex" | "gemini";
104
104
 
105
105
  export interface Config {
106
+ /** The primary model. Its provider is derived from the name — config never
107
+ * names a backend directly. */
106
108
  model: string;
107
- /** The primary backend for jobs and chat. */
108
- runner: BackendName;
109
- /** Ordered fallback backends, tried when the primary is provider-down. */
110
- fallback: BackendName[];
109
+ /** Ordered fallback models, tried when the one before them cannot serve. */
110
+ fallback_models: string[];
111
111
  timezone: string;
112
112
  activeHours: { start: string; end: string };
113
113
  database_url: string;
@@ -10,8 +10,7 @@ const TIME_RE = /^\d{2}:\d{2}$/;
10
10
 
11
11
  const DEFAULTS: Config = {
12
12
  model: "default",
13
- runner: "claude",
14
- fallback: [],
13
+ fallback_models: [],
15
14
  timezone: process.env.TZ || Intl.DateTimeFormat().resolvedOptions().timeZone,
16
15
  activeHours: { start: "00:00", end: "23:59" },
17
16
  database_url: DEFAULT_DATABASE_URL,
@@ -96,10 +95,10 @@ export function loadConfig(): Config {
96
95
  // Model
97
96
  const model = typeof raw.model === "string" ? raw.model : DEFAULTS.model;
98
97
 
99
- // Backends primary "runner" + ordered "fallback" chain for provider-down failover.
100
- const isBackend = (v: unknown): v is Config["runner"] => v === "claude" || v === "codex" || v === "gemini";
101
- const runner: Config["runner"] = isBackend(raw.runner) ? raw.runner : DEFAULTS.runner;
102
- const fallback: Config["fallback"] = Array.isArray(raw.fallback) ? raw.fallback.filter(isBackend) : DEFAULTS.fallback;
98
+ // Ordered fallback models; each one's provider is derived from its name.
99
+ const fallback_models = Array.isArray(raw.fallback_models)
100
+ ? raw.fallback_models.filter((m): m is string => typeof m === "string" && m.trim().length > 0)
101
+ : DEFAULTS.fallback_models;
103
102
 
104
103
  // Timezone
105
104
  let timezone = DEFAULTS.timezone;
@@ -252,8 +251,7 @@ export function loadConfig(): Config {
252
251
 
253
252
  return {
254
253
  model,
255
- runner,
256
- fallback,
254
+ fallback_models,
257
255
  timezone,
258
256
  activeHours: { start, end },
259
257
  database_url,
@@ -14,23 +14,6 @@ export async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T
14
14
  throw new Error("unreachable"); // satisfies TS return type
15
15
  }
16
16
 
17
- const RETRYABLE_PATTERNS = [/\b500\b/i, /internal server error/i, /overloaded/i, /529/, /rate limit/i];
18
-
19
- /** Check if an error string from the Claude API looks transient/retryable. */
20
- export function isRetryableApiError(error: string): boolean {
21
- return RETRYABLE_PATTERNS.some((p) => p.test(error));
22
- }
23
-
24
- /**
25
- * A blank or opaque ("unknown error") failure means the provider is down rather
26
- * than a specific, surfaceable error — the signal that should trigger failover.
27
- * Distinct from `isRetryableApiError` (a transient error worth an in-backend retry).
28
- */
29
- export function isProviderDownError(error: string | null | undefined): boolean {
30
- const trimmed = error?.trim();
31
- return !trimmed || trimmed.toLowerCase() === "unknown error";
32
- }
33
-
34
17
  /** Sleep for ms milliseconds. */
35
18
  export function sleep(ms: number): Promise<void> {
36
19
  return new Promise((r) => setTimeout(r, ms));