{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../../src/core/routing/fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAErF,MAAM,MAAM,eAAe,GACxB,sBAAsB,GACtB,mBAAmB,GACnB,cAAc,GACd,eAAe,GACf,2BAA2B,GAC3B,6BAA6B,GAC7B,sBAAsB,GACtB,8BAA8B,GAC9B,wBAAwB,CAAC;AAE5B,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,eAAe,CAAC;IACzB,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;IAC3C,aAAa,EAAE,UAAU,GAAG,kBAAkB,GAAG,wBAAwB,GAAG,UAAU,GAAG,SAAS,CAAC;IACnG,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;CACjB;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAC9B,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,0BAA0B,EACpC,OAAO,EAAE;IACR,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACpB,GACC,cAAc,CA4EhB;AAgBD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED,eAAO,MAAM,oBAAoB,IAAiB,CAAC","sourcesContent":["/**\n * Typed fallback and degradation.\n *\n * Fallback triggers are typed: no fuzzy substitution, no surprise paid\n * provider, local-only preserved, safety class preserved, fallback reason and\n * cost implications visible, loops bounded, and no silent quality downgrade.\n *\n * Fallback hierarchy:\n *   explicit operator fallback → validated policy fallback →\n *   deterministic baseline → safe degraded mode → typed blocked state.\n */\n\nimport { baselineSelect } from \"./baseline.js\";\nimport { appendEvent } from \"./store.js\";\nimport type { OrchestrationCandidate, OrchestrationFeatureVector } from \"./types.js\";\n\nexport type FallbackTrigger =\n\t| \"provider_unavailable\"\n\t| \"model_unavailable\"\n\t| \"rate_limited\"\n\t| \"context_limit\"\n\t| \"structured_output_invalid\"\n\t| \"tool_capability_unavailable\"\n\t| \"subagent_unavailable\"\n\t| \"evaluation_store_unavailable\"\n\t| \"routing_policy_corrupt\";\n\nexport interface FallbackResult {\n\ttrigger: FallbackTrigger;\n\tselectedCandidate?: OrchestrationCandidate;\n\tfallbackLayer: \"operator\" | \"validated_policy\" | \"deterministic_baseline\" | \"degraded\" | \"blocked\";\n\treason: string;\n\tcostImplication: string;\n\tblocked: boolean;\n}\n\nconst FALLBACK_LIMIT = 5;\n\n/**\n * Resolve a fallback for a trigger.\n * - explicitFallback: operator-provided candidate (highest authority).\n * - policyFallback: candidate from the validated policy.\n * - Otherwise, fall to the deterministic baseline then to a typed degraded mode.\n */\nexport function resolveFallback(\n\ttrigger: FallbackTrigger,\n\tfeatures: OrchestrationFeatureVector,\n\toptions: {\n\t\texplicitFallback?: OrchestrationCandidate;\n\t\tpolicyFallback?: OrchestrationCandidate;\n\t\tlocalOnly: boolean;\n\t\tsafetyClass: string;\n\t},\n): FallbackResult {\n\tif (options.explicitFallback) {\n\t\treturn {\n\t\t\ttrigger,\n\t\t\tselectedCandidate: options.explicitFallback,\n\t\t\tfallbackLayer: \"operator\",\n\t\t\treason: `operator fallback for ${trigger}`,\n\t\t\tcostImplication: \"as operator-specified\",\n\t\t\tblocked: false,\n\t\t};\n\t}\n\n\tif (options.policyFallback) {\n\t\treturn {\n\t\t\ttrigger,\n\t\t\tselectedCandidate: options.policyFallback,\n\t\t\tfallbackLayer: \"validated_policy\",\n\t\t\treason: `validated policy fallback for ${trigger}`,\n\t\t\tcostImplication: \"policy-specified candidate\",\n\t\t\tblocked: false,\n\t\t};\n\t}\n\n\t// Deterministic baseline.\n\tif (features) {\n\t\tconst baseline = baselineSelect(features);\n\t\tconst baselineCandidate = baseline.candidate;\n\t\t// Blocked triggers that cannot be safely served by baseline.\n\t\tif (trigger === \"routing_policy_corrupt\") {\n\t\t\t// Baseline remains available even when policy store is corrupt.\n\t\t\treturn {\n\t\t\t\ttrigger,\n\t\t\t\tselectedCandidate: baselineCandidate,\n\t\t\t\tfallbackLayer: \"deterministic_baseline\",\n\t\t\t\treason: `deterministic baseline available despite ${trigger}`,\n\t\t\t\tcostImplication: \"baseline local-only cost\",\n\t\t\t\tblocked: false,\n\t\t\t};\n\t\t}\n\t\tif (options.localOnly && isRemoteProvider(baselineCandidate.providerProfile)) {\n\t\t\treturn degradedOrBlocked(trigger, options);\n\t\t}\n\t\treturn {\n\t\t\ttrigger,\n\t\t\tselectedCandidate: baselineCandidate,\n\t\t\tfallbackLayer: \"deterministic_baseline\",\n\t\t\treason: `deterministic baseline fallback for ${trigger}`,\n\t\t\tcostImplication: \"baseline cost\",\n\t\t\tblocked: false,\n\t\t};\n\t}\n\n\t// Safe degraded mode.\n\tif (trigger !== \"provider_unavailable\" && trigger !== \"rate_limited\") {\n\t\tconst degraded: OrchestrationCandidate = {\n\t\t\tcandidateId: \"degraded-local-readonly\",\n\t\t\tproviderProfile: \"local\",\n\t\t\tconfiguredModel: \"local/readonly\",\n\t\t\texecutionTopology: \"single_agent\",\n\t\t\tskillIds: [],\n\t\t\tsubagentDefinitions: [],\n\t\t\tretrievalPolicy: \"lexical\",\n\t\t\tbudgetClass: \"small\",\n\t\t\tfallbackPolicy: \"degraded\",\n\t\t};\n\t\treturn {\n\t\t\ttrigger,\n\t\t\tselectedCandidate: degraded,\n\t\t\tfallbackLayer: \"degraded\",\n\t\t\treason: `safe degraded mode for ${trigger}`,\n\t\t\tcostImplication: \"local-only, no new paid provider\",\n\t\t\tblocked: false,\n\t\t};\n\t}\n\n\treturn degradedOrBlocked(trigger, options);\n}\n\nfunction degradedOrBlocked(trigger: FallbackTrigger, options: { safetyClass: string }): FallbackResult {\n\tappendEvent({\n\t\ttype: \"ORCHESTRATION_FALLBACK_APPLIED\",\n\t\tpayload: { trigger, layer: \"blocked\" },\n\t});\n\treturn {\n\t\ttrigger,\n\t\tfallbackLayer: \"blocked\",\n\t\treason: `no safe fallback for ${trigger}; must preserve safety class (${options.safetyClass})`,\n\t\tcostImplication: \"none (blocked)\",\n\t\tblocked: true,\n\t};\n}\n\nexport function isRemoteProvider(provider: string): boolean {\n\tconst p = provider.toLowerCase();\n\treturn !(p === \"local\" || p.startsWith(\"local-\") || p === \"fixture\" || p.includes(\"test\"));\n}\n\nexport const FALLBACK_CHAIN_LIMIT = FALLBACK_LIMIT;\n"]}