@smart-coders-hq/opencode-model-fallback 1.0.1

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.
Files changed (50) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +237 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +4198 -0
  6. package/dist/src/config/agent-loader.d.ts +5 -0
  7. package/dist/src/config/agent-loader.d.ts.map +1 -0
  8. package/dist/src/config/defaults.d.ts +5 -0
  9. package/dist/src/config/defaults.d.ts.map +1 -0
  10. package/dist/src/config/loader.d.ts +9 -0
  11. package/dist/src/config/loader.d.ts.map +1 -0
  12. package/dist/src/config/migrate.d.ts +16 -0
  13. package/dist/src/config/migrate.d.ts.map +1 -0
  14. package/dist/src/config/schema.d.ts +30 -0
  15. package/dist/src/config/schema.d.ts.map +1 -0
  16. package/dist/src/detection/classifier.d.ts +3 -0
  17. package/dist/src/detection/classifier.d.ts.map +1 -0
  18. package/dist/src/detection/patterns.d.ts +5 -0
  19. package/dist/src/detection/patterns.d.ts.map +1 -0
  20. package/dist/src/display/notifier.d.ts +8 -0
  21. package/dist/src/display/notifier.d.ts.map +1 -0
  22. package/dist/src/display/usage.d.ts +24 -0
  23. package/dist/src/display/usage.d.ts.map +1 -0
  24. package/dist/src/logging/logger.d.ts +24 -0
  25. package/dist/src/logging/logger.d.ts.map +1 -0
  26. package/dist/src/plugin.d.ts +9 -0
  27. package/dist/src/plugin.d.ts.map +1 -0
  28. package/dist/src/preemptive.d.ts +16 -0
  29. package/dist/src/preemptive.d.ts.map +1 -0
  30. package/dist/src/replay/message-converter.d.ts +22 -0
  31. package/dist/src/replay/message-converter.d.ts.map +1 -0
  32. package/dist/src/replay/orchestrator.d.ts +13 -0
  33. package/dist/src/replay/orchestrator.d.ts.map +1 -0
  34. package/dist/src/resolution/agent-resolver.d.ts +11 -0
  35. package/dist/src/resolution/agent-resolver.d.ts.map +1 -0
  36. package/dist/src/resolution/fallback-resolver.d.ts +13 -0
  37. package/dist/src/resolution/fallback-resolver.d.ts.map +1 -0
  38. package/dist/src/state/model-health.d.ts +18 -0
  39. package/dist/src/state/model-health.d.ts.map +1 -0
  40. package/dist/src/state/session-state.d.ts +18 -0
  41. package/dist/src/state/session-state.d.ts.map +1 -0
  42. package/dist/src/state/store.d.ts +14 -0
  43. package/dist/src/state/store.d.ts.map +1 -0
  44. package/dist/src/tools/fallback-status.d.ts +8 -0
  45. package/dist/src/tools/fallback-status.d.ts.map +1 -0
  46. package/dist/src/types.d.ts +51 -0
  47. package/dist/src/types.d.ts.map +1 -0
  48. package/examples/model-fallback.json +34 -0
  49. package/package.json +73 -0
  50. package/plugin.json +14 -0
@@ -0,0 +1,5 @@
1
+ import type { AgentConfig } from "../types.js";
2
+ export declare function toRelativeAgentPath(absPath: string, projectDirectory: string, homeDir?: string): string;
3
+ export declare function resolveAgentFile(agentName: string, projectDirectory: string, customDirs?: string[], homeDir?: string): string | null;
4
+ export declare function loadAgentFallbackConfigs(projectDirectory: string, homeDir?: string): Record<string, AgentConfig>;
5
+ //# sourceMappingURL=agent-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-loader.d.ts","sourceRoot":"","sources":["../../../src/config/agent-loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAS/C,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,MAAkB,GAC1B,MAAM,CAgBR;AAuGD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,UAAU,CAAC,EAAE,MAAM,EAAE,EACrB,OAAO,GAAE,MAAkB,GAC1B,MAAM,GAAG,IAAI,CAwCf;AAED,wBAAgB,wBAAwB,CACtC,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,MAAkB,GAC1B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAqB7B"}
@@ -0,0 +1,5 @@
1
+ import type { PluginConfig } from "../types.js";
2
+ export declare const DEFAULT_PATTERNS: string[];
3
+ export declare const DEFAULT_LOG_PATH: string;
4
+ export declare const DEFAULT_CONFIG: PluginConfig;
5
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../src/config/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,eAAO,MAAM,gBAAgB,UAU5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAAmE,CAAC;AAEjG,eAAO,MAAM,cAAc,EAAE,YAiB5B,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { PluginConfig } from "../types.js";
2
+ export interface LoadResult {
3
+ config: PluginConfig;
4
+ path: string | null;
5
+ warnings: string[];
6
+ migrated: boolean;
7
+ }
8
+ export declare function loadConfig(directory: string): LoadResult;
9
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoBhD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAgDxD"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Auto-migration from old `rate-limit-fallback.json` format:
3
+ * { fallbackModel: "providerID/modelID", cooldownMs, patterns, logging }
4
+ * → new format:
5
+ * { agents: { "*": { fallbackModels: ["providerID/modelID"] } }, ... }
6
+ */
7
+ export interface OldConfig {
8
+ fallbackModel?: string;
9
+ enabled?: boolean;
10
+ cooldownMs?: number;
11
+ patterns?: string[];
12
+ logging?: boolean;
13
+ }
14
+ export declare function isOldFormat(raw: unknown): raw is OldConfig;
15
+ export declare function migrateOldConfig(old: OldConfig): Record<string, unknown>;
16
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/config/migrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,SAAS,CAG1D;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBxE"}
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ export declare const pluginConfigSchema: z.ZodObject<{
3
+ enabled: z.ZodOptional<z.ZodBoolean>;
4
+ defaults: z.ZodOptional<z.ZodObject<{
5
+ fallbackOn: z.ZodOptional<z.ZodArray<z.ZodEnum<{
6
+ rate_limit: "rate_limit";
7
+ quota_exceeded: "quota_exceeded";
8
+ "5xx": "5xx";
9
+ timeout: "timeout";
10
+ overloaded: "overloaded";
11
+ }>>>;
12
+ cooldownMs: z.ZodOptional<z.ZodNumber>;
13
+ retryOriginalAfterMs: z.ZodOptional<z.ZodNumber>;
14
+ maxFallbackDepth: z.ZodOptional<z.ZodNumber>;
15
+ }, z.core.$strip>>;
16
+ agents: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
17
+ fallbackModels: z.ZodArray<z.ZodString>;
18
+ }, z.core.$strip>>>;
19
+ patterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
20
+ logging: z.ZodOptional<z.ZodBoolean>;
21
+ logPath: z.ZodOptional<z.ZodString>;
22
+ agentDirs: z.ZodOptional<z.ZodArray<z.ZodString>>;
23
+ }, z.core.$strict>;
24
+ export type RawConfig = z.infer<typeof pluginConfigSchema>;
25
+ export declare function parseConfig(raw: unknown): {
26
+ config: RawConfig;
27
+ warnings: string[];
28
+ };
29
+ export declare function mergeWithDefaults(raw: RawConfig): import("../types.js").PluginConfig;
30
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2CxB,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;kBAUpB,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE3D,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG;IACzC,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CA6LA;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,aAAa,EAAE,YAAY,CAkBpF"}
@@ -0,0 +1,3 @@
1
+ import type { ErrorCategory } from "../types.js";
2
+ export declare function classifyError(message: string, statusCode?: number): ErrorCategory;
3
+ //# sourceMappingURL=classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../../../src/detection/classifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkCjD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CAsBjF"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Case-insensitive pattern matching against error messages.
3
+ */
4
+ export declare function matchesAnyPattern(text: string, patterns: string[]): boolean;
5
+ //# sourceMappingURL=patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/detection/patterns.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAG3E"}
@@ -0,0 +1,8 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { ModelKey, ErrorCategory } from "../types.js";
3
+ type Client = PluginInput["client"];
4
+ export declare function notifyFallback(client: Client, from: ModelKey | null, to: ModelKey, reason: ErrorCategory): Promise<void>;
5
+ export declare function notifyFallbackActive(client: Client, originalModel: ModelKey, currentModel: ModelKey): Promise<void>;
6
+ export declare function notifyRecovery(client: Client, originalModel: ModelKey): Promise<void>;
7
+ export {};
8
+ //# sourceMappingURL=notifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifier.d.ts","sourceRoot":"","sources":["../../../src/display/notifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE3D,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEpC,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,GAAG,IAAI,EACrB,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,QAAQ,EACvB,YAAY,EAAE,QAAQ,GACrB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3F"}
@@ -0,0 +1,24 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { SessionFallbackState } from "../types.js";
3
+ type Client = PluginInput["client"];
4
+ export interface FallbackUsageSummary {
5
+ sessionId: string;
6
+ totalInputTokens: number;
7
+ totalOutputTokens: number;
8
+ totalCost: number;
9
+ fallbackPeriods: Array<{
10
+ model: string;
11
+ from: number;
12
+ to: number | null;
13
+ inputTokens: number;
14
+ outputTokens: number;
15
+ cost: number;
16
+ }>;
17
+ }
18
+ /**
19
+ * Query OpenCode's native session message data to enrich with fallback context.
20
+ * Groups token/cost data by model, correlated with fallback history timestamps.
21
+ */
22
+ export declare function getFallbackUsage(client: Client, state: SessionFallbackState): Promise<FallbackUsageSummary>;
23
+ export {};
24
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../../src/display/usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEpC,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,KAAK,CAAC;QACrB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACJ;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC,oBAAoB,CAAC,CAwC/B"}
@@ -0,0 +1,24 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ type Client = PluginInput["client"];
3
+ export type LogLevel = "debug" | "info" | "warn" | "error";
4
+ export interface LogEntry {
5
+ ts: string;
6
+ level: LogLevel;
7
+ event: string;
8
+ [key: string]: unknown;
9
+ }
10
+ export declare class Logger {
11
+ private client;
12
+ private logPath;
13
+ private enabled;
14
+ private dirCreated;
15
+ constructor(client: Client, logPath: string, enabled: boolean);
16
+ log(level: LogLevel, event: string, fields?: Record<string, unknown>): void;
17
+ info(event: string, fields?: Record<string, unknown>): void;
18
+ warn(event: string, fields?: Record<string, unknown>): void;
19
+ error(event: string, fields?: Record<string, unknown>): void;
20
+ debug(event: string, fields?: Record<string, unknown>): void;
21
+ private writeToFile;
22
+ }
23
+ export {};
24
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/logging/logger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEpC,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,UAAU,CAAS;gBAEf,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAM7D,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAyB/E,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,OAAO,CAAC,WAAW;CAWpB"}
@@ -0,0 +1,9 @@
1
+ import type { Plugin } from "@opencode-ai/plugin";
2
+ import type { Event } from "@opencode-ai/sdk";
3
+ import { loadConfig } from "./config/loader.js";
4
+ import { Logger } from "./logging/logger.js";
5
+ import { FallbackStore } from "./state/store.js";
6
+ export declare const createPlugin: Plugin;
7
+ export declare function handleEvent(event: Event, client: Parameters<Plugin>[0]["client"], store: FallbackStore, config: ReturnType<typeof loadConfig>["config"], logger: Logger, directory: string): Promise<void>;
8
+ export declare function handleIdle(sessionId: string, client: Parameters<Plugin>[0]["client"], store: FallbackStore, _config: ReturnType<typeof loadConfig>["config"], logger: Logger): Promise<void>;
9
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAS,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAI9C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAUjD,eAAO,MAAM,YAAY,EAAE,MAwG1B,CAAC;AAEF,wBAAsB,WAAW,CAC/B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EACvC,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,QAAQ,CAAC,EAC/C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAuDf;AAoFD,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EACvC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,QAAQ,CAAC,EAChD,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAuBf"}
@@ -0,0 +1,16 @@
1
+ import type { ModelKey, PluginConfig } from "./types.js";
2
+ import type { FallbackStore } from "./state/store.js";
3
+ import type { Logger } from "./logging/logger.js";
4
+ export interface PreemptiveResult {
5
+ redirected: boolean;
6
+ fallbackModel?: ModelKey;
7
+ }
8
+ /**
9
+ * Check whether a message's target model is rate-limited and, if so,
10
+ * resolve a healthy fallback model. Also syncs session state and resets
11
+ * fallbackDepth when the TUI reverts to the original model.
12
+ *
13
+ * Pure synchronous logic — no API calls. Designed for the `chat.message` hook.
14
+ */
15
+ export declare function tryPreemptiveRedirect(sessionId: string, modelKey: ModelKey, agentName: string | null, store: FallbackStore, config: PluginConfig, logger: Logger): PreemptiveResult;
16
+ //# sourceMappingURL=preemptive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preemptive.d.ts","sourceRoot":"","sources":["../../src/preemptive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAIlD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,QAAQ,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,GACb,gBAAgB,CA6ClB"}
@@ -0,0 +1,22 @@
1
+ import type { Part } from "@opencode-ai/sdk";
2
+ type PromptPart = {
3
+ type: "text";
4
+ text: string;
5
+ synthetic?: boolean;
6
+ ignored?: boolean;
7
+ } | {
8
+ type: "file";
9
+ mime: string;
10
+ url: string;
11
+ filename?: string;
12
+ } | {
13
+ type: "agent";
14
+ name: string;
15
+ };
16
+ /**
17
+ * Convert stored message parts to the format expected by session.prompt().
18
+ * Filters out synthetic/ignored parts and server-generated part types.
19
+ */
20
+ export declare function convertPartsForPrompt(parts: Part[]): PromptPart[];
21
+ export {};
22
+ //# sourceMappingURL=message-converter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-converter.d.ts","sourceRoot":"","sources":["../../../src/replay/message-converter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,KAAK,UAAU,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,CA+BjE"}
@@ -0,0 +1,13 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { ModelKey, ErrorCategory, PluginConfig } from "../types.js";
3
+ import type { FallbackStore } from "../state/store.js";
4
+ import type { Logger } from "../logging/logger.js";
5
+ type Client = PluginInput["client"];
6
+ export interface ReplayResult {
7
+ success: boolean;
8
+ fallbackModel?: ModelKey;
9
+ error?: string;
10
+ }
11
+ export declare function attemptFallback(sessionId: string, reason: ErrorCategory, client: Client, store: FallbackStore, config: PluginConfig, logger: Logger, directory: string): Promise<ReplayResult>;
12
+ export {};
13
+ //# sourceMappingURL=orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../../src/replay/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAMnD,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEpC,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,QAAQ,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,YAAY,CAAC,CAuOvB"}
@@ -0,0 +1,11 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { PluginConfig, ModelKey } from "../types.js";
3
+ type Client = PluginInput["client"];
4
+ /**
5
+ * Map a session to its agent name, then to the fallback config for that agent.
6
+ * Falls back to wildcard "*" agent config.
7
+ */
8
+ export declare function resolveAgentName(client: Client, sessionId: string, cachedName: string | null): Promise<string | null>;
9
+ export declare function resolveFallbackModels(config: PluginConfig, agentName: string | null): ModelKey[];
10
+ export {};
11
+ //# sourceMappingURL=agent-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-resolver.d.ts","sourceRoot":"","sources":["../../../src/resolution/agent-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE1D,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEpC;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBxB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,EAAE,CAKhG"}
@@ -0,0 +1,13 @@
1
+ import type { ModelKey } from "../types.js";
2
+ import type { ModelHealthStore } from "../state/model-health.js";
3
+ /**
4
+ * Walk the fallback chain and return the best available model.
5
+ * Strategy:
6
+ * 1. First model that is "healthy"
7
+ * 2. First model that is "cooldown" (better than nothing)
8
+ * 3. null if all are "rate_limited"
9
+ *
10
+ * Always skips the current model.
11
+ */
12
+ export declare function resolveFallbackModel(chain: ModelKey[], currentModel: ModelKey | null, health: ModelHealthStore): ModelKey | null;
13
+ //# sourceMappingURL=fallback-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback-resolver.d.ts","sourceRoot":"","sources":["../../../src/resolution/fallback-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,QAAQ,EAAE,EACjB,YAAY,EAAE,QAAQ,GAAG,IAAI,EAC7B,MAAM,EAAE,gBAAgB,GACvB,QAAQ,GAAG,IAAI,CAWjB"}
@@ -0,0 +1,18 @@
1
+ import type { ModelHealth, ModelKey, HealthState } from "../types.js";
2
+ export declare class ModelHealthStore {
3
+ private store;
4
+ private timer;
5
+ private onTransition?;
6
+ constructor(opts?: {
7
+ onTransition?: (modelKey: ModelKey, from: HealthState, to: HealthState) => void;
8
+ });
9
+ get(modelKey: ModelKey): ModelHealth;
10
+ markRateLimited(modelKey: ModelKey, cooldownMs: number, retryOriginalAfterMs: number): void;
11
+ isUsable(modelKey: ModelKey): boolean;
12
+ preferScore(modelKey: ModelKey): number;
13
+ getAll(): ModelHealth[];
14
+ destroy(): void;
15
+ private tick;
16
+ private newHealth;
17
+ }
18
+ //# sourceMappingURL=model-health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-health.d.ts","sourceRoot":"","sources":["../../../src/state/model-health.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,YAAY,CAAC,CAAmE;gBAE5E,IAAI,CAAC,EAAE;QACjB,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,KAAK,IAAI,CAAC;KACjF;IAMD,GAAG,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW;IAIpC,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,IAAI;IAc3F,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO;IAKrC,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM;IAQvC,MAAM,IAAI,WAAW,EAAE;IAIvB,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,IAAI;IA4BZ,OAAO,CAAC,SAAS;CAUlB"}
@@ -0,0 +1,18 @@
1
+ import type { SessionFallbackState, ModelKey, ErrorCategory } from "../types.js";
2
+ export declare class SessionStateStore {
3
+ private store;
4
+ get(sessionId: string): SessionFallbackState;
5
+ acquireLock(sessionId: string): boolean;
6
+ releaseLock(sessionId: string): void;
7
+ isInDedupWindow(sessionId: string, windowMs?: number): boolean;
8
+ recordFallback(sessionId: string, fromModel: ModelKey, toModel: ModelKey, reason: ErrorCategory, agentName: string | null): void;
9
+ recordPreemptiveRedirect(sessionId: string, fromModel: ModelKey, toModel: ModelKey, agentName: string | null): void;
10
+ setOriginalModel(sessionId: string, model: ModelKey): void;
11
+ setAgentName(sessionId: string, agentName: string): void;
12
+ setAgentFile(sessionId: string, agentFile: string): void;
13
+ partialReset(sessionId: string): void;
14
+ delete(sessionId: string): void;
15
+ getAll(): SessionFallbackState[];
16
+ private newState;
17
+ }
18
+ //# sourceMappingURL=session-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../../src/state/session-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAiB,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhG,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAA2C;IAExD,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,oBAAoB;IAO5C,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IASvC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKpC,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,SAAQ,GAAG,OAAO;IAM7D,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,QAAQ,EACnB,OAAO,EAAE,QAAQ,EACjB,MAAM,EAAE,aAAa,EACrB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,IAAI;IAmBP,wBAAwB,CACtB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,QAAQ,EACnB,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,IAAI;IAkBP,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI;IAQ1D,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAKxD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAKxD,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IASrC,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI/B,MAAM,IAAI,oBAAoB,EAAE;IAIhC,OAAO,CAAC,QAAQ;CAcjB"}
@@ -0,0 +1,14 @@
1
+ import { ModelHealthStore } from "./model-health.js";
2
+ import { SessionStateStore } from "./session-state.js";
3
+ import type { Logger } from "../logging/logger.js";
4
+ import type { PluginConfig } from "../types.js";
5
+ /**
6
+ * Centralized in-memory store — single entry point for all state.
7
+ */
8
+ export declare class FallbackStore {
9
+ readonly health: ModelHealthStore;
10
+ readonly sessions: SessionStateStore;
11
+ constructor(_config: PluginConfig, logger: Logger);
12
+ destroy(): void;
13
+ }
14
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/state/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;GAEG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;gBAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM;IASjD,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,8 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin";
2
+ import type { FallbackStore } from "../state/store.js";
3
+ import type { PluginConfig } from "../types.js";
4
+ import type { PluginInput } from "@opencode-ai/plugin";
5
+ type Client = PluginInput["client"];
6
+ export declare function createFallbackStatusTool(store: FallbackStore, config: PluginConfig, client: Client, directory: string): ToolDefinition;
7
+ export {};
8
+ //# sourceMappingURL=fallback-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback-status.d.ts","sourceRoot":"","sources":["../../../src/tools/fallback-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,KAAK,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEpC,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,cAAc,CA6HhB"}
@@ -0,0 +1,51 @@
1
+ export type ModelKey = string;
2
+ export type ErrorCategory = "rate_limit" | "quota_exceeded" | "5xx" | "timeout" | "overloaded" | "unknown";
3
+ export type HealthState = "healthy" | "rate_limited" | "cooldown";
4
+ export interface ModelHealth {
5
+ modelKey: ModelKey;
6
+ state: HealthState;
7
+ lastFailure: number | null;
8
+ failureCount: number;
9
+ cooldownExpiresAt: number | null;
10
+ retryOriginalAt: number | null;
11
+ }
12
+ export interface FallbackEvent {
13
+ at: number;
14
+ fromModel: ModelKey;
15
+ toModel: ModelKey;
16
+ reason: ErrorCategory;
17
+ sessionId: string;
18
+ trigger: "reactive" | "preemptive";
19
+ agentName: string | null;
20
+ }
21
+ export interface SessionFallbackState {
22
+ sessionId: string;
23
+ agentName: string | null;
24
+ agentFile: string | null;
25
+ originalModel: ModelKey | null;
26
+ currentModel: ModelKey | null;
27
+ fallbackDepth: number;
28
+ isProcessing: boolean;
29
+ lastFallbackAt: number | null;
30
+ fallbackHistory: FallbackEvent[];
31
+ recoveryNotifiedForModel: ModelKey | null;
32
+ }
33
+ export interface AgentConfig {
34
+ fallbackModels: ModelKey[];
35
+ }
36
+ export interface FallbackDefaults {
37
+ fallbackOn: ErrorCategory[];
38
+ cooldownMs: number;
39
+ retryOriginalAfterMs: number;
40
+ maxFallbackDepth: number;
41
+ }
42
+ export interface PluginConfig {
43
+ enabled: boolean;
44
+ defaults: FallbackDefaults;
45
+ agents: Record<string, AgentConfig>;
46
+ patterns: string[];
47
+ logging: boolean;
48
+ logPath: string;
49
+ agentDirs: string[];
50
+ }
51
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,MAAM,aAAa,GACrB,YAAY,GACZ,gBAAgB,GAChB,KAAK,GACL,SAAS,GACT,YAAY,GACZ,SAAS,CAAC;AAEd,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,cAAc,GAAG,UAAU,CAAC;AAElE,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,QAAQ,CAAC;IACpB,OAAO,EAAE,QAAQ,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,UAAU,GAAG,YAAY,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,wBAAwB,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE,QAAQ,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB"}
@@ -0,0 +1,34 @@
1
+ {
2
+ "enabled": true,
3
+ "defaults": {
4
+ "fallbackOn": ["rate_limit", "quota_exceeded", "5xx", "timeout", "overloaded"],
5
+ "cooldownMs": 300000,
6
+ "retryOriginalAfterMs": 900000,
7
+ "maxFallbackDepth": 3
8
+ },
9
+ "agents": {
10
+ "build": {
11
+ "fallbackModels": [
12
+ "anthropic/claude-sonnet-4-20250514",
13
+ "google/gemini-3-pro",
14
+ "openai/gpt-4o"
15
+ ]
16
+ },
17
+ "*": {
18
+ "fallbackModels": ["anthropic/claude-sonnet-4-20250514", "google/gemini-flash-2-5"]
19
+ }
20
+ },
21
+ "patterns": [
22
+ "rate limit",
23
+ "usage limit",
24
+ "too many requests",
25
+ "quota exceeded",
26
+ "overloaded",
27
+ "capacity exceeded",
28
+ "credits exhausted",
29
+ "billing limit",
30
+ "429"
31
+ ],
32
+ "logging": true,
33
+ "logPath": "~/.local/share/opencode/logs/model-fallback.log"
34
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@smart-coders-hq/opencode-model-fallback",
3
+ "version": "1.0.1",
4
+ "description": "Ordered model fallback chains with health tracking for OpenCode",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "bun build index.ts --outdir dist --target node --format esm --external @opencode-ai/plugin --external zod && tsc --emitDeclarationOnly",
16
+ "test": "bun test",
17
+ "typecheck": "tsc --noEmit",
18
+ "dev": "tsc --watch --noEmit",
19
+ "lint": "biome lint .",
20
+ "lint:fix": "biome lint --write .",
21
+ "format": "biome format --write .",
22
+ "format:check": "biome format --check .",
23
+ "release": "semantic-release"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "plugin.json",
28
+ "examples"
29
+ ],
30
+ "peerDependencies": {
31
+ "@opencode-ai/plugin": ">=1.2.0",
32
+ "zod": ">=4.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@biomejs/biome": "2.4.7",
36
+ "@opencode-ai/plugin": "^1.2.24",
37
+ "@semantic-release/changelog": "^6.0.3",
38
+ "@semantic-release/commit-analyzer": "^13.0.1",
39
+ "@semantic-release/git": "^10.0.1",
40
+ "@semantic-release/github": "^12.0.6",
41
+ "@semantic-release/npm": "^13.1.5",
42
+ "@semantic-release/release-notes-generator": "^14.1.0",
43
+ "@types/bun": "^1.3.10",
44
+ "@types/js-yaml": "^4.0.9",
45
+ "js-yaml": "^4.1.0",
46
+ "semantic-release": "^25.0.3",
47
+ "typescript": "^5.9.3",
48
+ "zod": "^4.3.6"
49
+ },
50
+ "keywords": [
51
+ "opencode",
52
+ "plugin",
53
+ "model-fallback",
54
+ "rate-limit",
55
+ "failover"
56
+ ],
57
+ "license": "Apache-2.0",
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "https://github.com/Smart-Coders-HQ/opencode-model-fallback.git"
61
+ },
62
+ "author": "Błażej Pawlak <blazej@smartcoders.xyz>",
63
+ "homepage": "https://github.com/Smart-Coders-HQ/opencode-model-fallback#readme",
64
+ "bugs": {
65
+ "url": "https://github.com/Smart-Coders-HQ/opencode-model-fallback/issues"
66
+ },
67
+ "publishConfig": {
68
+ "access": "public"
69
+ },
70
+ "engines": {
71
+ "node": ">=18.0.0"
72
+ }
73
+ }
package/plugin.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "opencode-model-fallback",
4
+ "name": "Model Fallback",
5
+ "description": "Ordered model fallback chains with health tracking and preemptive redirects for OpenCode",
6
+ "version": "0.1.0",
7
+ "entry": "dist/index.js",
8
+ "links": [
9
+ {
10
+ "label": "GitHub",
11
+ "url": "https://github.com/Smart-Coders-HQ/opencode-model-fallback"
12
+ }
13
+ ]
14
+ }