@warmdrift/kgauto-compiler 2.0.0-alpha.37 → 2.0.0-alpha.39
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.
- package/dist/index.d.mts +191 -1
- package/dist/index.d.ts +191 -1
- package/dist/index.js +248 -14
- package/dist/index.mjs +244 -14
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -203,11 +203,28 @@ interface BrainQueryConfig {
|
|
|
203
203
|
pricing?: boolean;
|
|
204
204
|
/** Default true when endpoint set. Brain-driven model registry + aliases. */
|
|
205
205
|
models?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* alpha.38 — opt-in compile-time advisor for stale exclusions. Default
|
|
208
|
+
* `true` when endpoint is set. When enabled, the library reads
|
|
209
|
+
* `exclusion_findings` from the central kgauto-dashboard for the consumer's
|
|
210
|
+
* `app_id` (no consumer-side proxy work required) and surfaces a
|
|
211
|
+
* `stale-exclusion-candidate` advisory when compile()'s archetype has a
|
|
212
|
+
* recommend-probe finding open. Set `false` to silence (no fetch, no
|
|
213
|
+
* advisory).
|
|
214
|
+
*/
|
|
215
|
+
findingsExclusions?: boolean;
|
|
206
216
|
/** SWR window in ms. Default 300_000 (5 min). */
|
|
207
217
|
cacheTtlMs?: number;
|
|
208
218
|
/** Override the GET URL when the read endpoint differs from the write one.
|
|
209
219
|
* Defaults to `${endpoint}/v2/config` when omitted. */
|
|
210
220
|
configEndpoint?: string;
|
|
221
|
+
/**
|
|
222
|
+
* alpha.38 — override the findings endpoint when the consumer wants to
|
|
223
|
+
* proxy `exclusion_findings` reads through their own service rather than
|
|
224
|
+
* reading directly from the kgauto-dashboard. Library appends `?app_id=X`.
|
|
225
|
+
* Defaults to `https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions`.
|
|
226
|
+
*/
|
|
227
|
+
findingsEndpoint?: string;
|
|
211
228
|
}
|
|
212
229
|
interface BrainConfig {
|
|
213
230
|
/** Brain HTTP endpoint base URL (e.g., https://kgauto-brain.vercel.app/api). */
|
|
@@ -751,6 +768,80 @@ declare function markAdvisoryResolved(opts: MarkAdvisoryResolvedOptions): Promis
|
|
|
751
768
|
ok: false;
|
|
752
769
|
reason: string;
|
|
753
770
|
}>;
|
|
771
|
+
/**
|
|
772
|
+
* Resolution sources supported by the alpha.39 `markExclusionFindingHandled`
|
|
773
|
+
* API. Mirrors the subset of `exclusion_findings.resolution_source` CHECK
|
|
774
|
+
* constraint values that consumers can self-set:
|
|
775
|
+
*
|
|
776
|
+
* - `'consumer-marked'` — generic "we've handled this; stop reminding."
|
|
777
|
+
* Use this for "we evaluated and chose not to act" or "we'll act when
|
|
778
|
+
* ready but acknowledge the finding now."
|
|
779
|
+
* - `'declined'` — explicit "this exclusion is intentional; do not surface
|
|
780
|
+
* again." Stronger signal than consumer-marked; useful for compliance
|
|
781
|
+
* exclusions, brand-promise exclusions, etc.
|
|
782
|
+
* - `'probed-unblock'` — alpha.40+ probe-path verdict: consumer ran a live
|
|
783
|
+
* probe and decided to unblock the excluded model. Library accepts the
|
|
784
|
+
* value today so the probe path doesn't need a follow-up API release.
|
|
785
|
+
* - `'probed-stay-excluded'` — alpha.40+ probe-path verdict: consumer
|
|
786
|
+
* probed and confirmed the exclusion. Same forward-compat shape as
|
|
787
|
+
* `'probed-unblock'`.
|
|
788
|
+
*
|
|
789
|
+
* The `'auto'` resolution_source is NOT exposed here — that's reserved
|
|
790
|
+
* for the brain's own automatic resolution (e.g. when the detection
|
|
791
|
+
* conditions no longer hold). Consumer code sets only the four values
|
|
792
|
+
* above.
|
|
793
|
+
*/
|
|
794
|
+
type ExclusionResolutionSource = 'consumer-marked' | 'declined' | 'probed-unblock' | 'probed-stay-excluded';
|
|
795
|
+
interface MarkExclusionFindingHandledOptions {
|
|
796
|
+
/** App id the finding belongs to. Required (RLS scopes writes by this). */
|
|
797
|
+
appId: string;
|
|
798
|
+
/** Archetype the finding applies to (e.g. 'hunt', 'classify'). */
|
|
799
|
+
archetype: string;
|
|
800
|
+
/** Canonical model id that the consumer had excluded. */
|
|
801
|
+
excludedModel: string;
|
|
802
|
+
/** Resolution semantics — see `ExclusionResolutionSource` for the four
|
|
803
|
+
* values consumers can set. */
|
|
804
|
+
resolution: ExclusionResolutionSource;
|
|
805
|
+
/** Optional free-form note explaining the decision. Surfaces in operator
|
|
806
|
+
* digests + dashboards alongside the resolution_source. */
|
|
807
|
+
resolutionNote?: string;
|
|
808
|
+
/** Brain Supabase URL base (e.g. `https://<project>.supabase.co`). The
|
|
809
|
+
* function appends `/rest/v1/exclusion_findings?...`. */
|
|
810
|
+
brainEndpoint: string;
|
|
811
|
+
/** Consumer-scoped JWT carrying the `app_id` claim. The brain's RLS
|
|
812
|
+
* policy enforces that the JWT's app_id matches the finding's app_id;
|
|
813
|
+
* cross-tenant writes fail at the database layer regardless of input. */
|
|
814
|
+
brainJwt: string;
|
|
815
|
+
/** Supabase anon key for the `apikey` header (PostgREST requires it). */
|
|
816
|
+
brainAnonKey: string;
|
|
817
|
+
/** Injected fetch for tests. Defaults to global fetch. */
|
|
818
|
+
fetch?: typeof fetch;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Mark a stale-exclusion finding as handled. Returns an `ok/reason` envelope
|
|
822
|
+
* matching `markAdvisoryResolved` — consumer Admin UIs typically render the
|
|
823
|
+
* failure inline rather than crash.
|
|
824
|
+
*
|
|
825
|
+
* Idempotent: if no row matches the (app_id, archetype, excludedModel)
|
|
826
|
+
* tuple (already resolved, never existed, cron not yet UPSERTed), PostgREST
|
|
827
|
+
* returns 200 with zero affected rows and we return `{ ok: true }`. The
|
|
828
|
+
* caller's "I marked it as handled" intent is satisfied regardless of
|
|
829
|
+
* whether the row was already in that state.
|
|
830
|
+
*
|
|
831
|
+
* Reasons surfaced on failure:
|
|
832
|
+
* - `app_id_required` / `archetype_required` / `excluded_model_required`
|
|
833
|
+
* - `resolution_invalid` — `resolution` not one of the four documented values
|
|
834
|
+
* - `brain_auth_misconfig` — 401/403 (JWT / anon key wrong)
|
|
835
|
+
* - `brain_unavailable` — 5xx
|
|
836
|
+
* - `network_error:<message>` — fetch threw
|
|
837
|
+
* - `patch_failed:<status>` — anything else non-2xx
|
|
838
|
+
*/
|
|
839
|
+
declare function markExclusionFindingHandled(opts: MarkExclusionFindingHandledOptions): Promise<{
|
|
840
|
+
ok: true;
|
|
841
|
+
} | {
|
|
842
|
+
ok: false;
|
|
843
|
+
reason: string;
|
|
844
|
+
}>;
|
|
754
845
|
|
|
755
846
|
/**
|
|
756
847
|
* Archetype-cliff compatibility — alpha.28 (tt-intel-Cairn ratified).
|
|
@@ -1465,6 +1556,105 @@ declare const loadModelsFromBrain: () => Map<string, ModelProfile>;
|
|
|
1465
1556
|
*/
|
|
1466
1557
|
declare const loadAliasesFromBrain: () => Record<string, string>;
|
|
1467
1558
|
|
|
1559
|
+
/**
|
|
1560
|
+
* exclusion-findings-brain — alpha.38 KG-19 adapter.
|
|
1561
|
+
*
|
|
1562
|
+
* Per-tenant SWR cache for the `exclusion_findings` table populated by the
|
|
1563
|
+
* `kgauto-exclusion-re-evaluation-watch` cron (alpha.37 substrate). Distinct
|
|
1564
|
+
* from the cross-tenant `brain-query.ts` snapshot because exclusion findings
|
|
1565
|
+
* are scoped per `app_id` — one process = one consumer = one appId, but the
|
|
1566
|
+
* cache key supports multi-app processes by keying snapshots by appId.
|
|
1567
|
+
*
|
|
1568
|
+
* Architecture mirrors `brain-query.ts` but with a per-(appId) snapshot:
|
|
1569
|
+
*
|
|
1570
|
+
* - **Sync API surface.** `getStaleExclusionFindings({ appId, archetype })`
|
|
1571
|
+
* returns `ExclusionFindingRow[]` immediately. First call returns the
|
|
1572
|
+
* bundled fallback (empty array); async refresh fires in background;
|
|
1573
|
+
* subsequent calls within TTL return brain data.
|
|
1574
|
+
*
|
|
1575
|
+
* - **Per-appId snapshot.** Each appId gets its own cache entry. One fetch
|
|
1576
|
+
* per appId per TTL window. Tests can reset between cases.
|
|
1577
|
+
*
|
|
1578
|
+
* - **Tolerant.** Brain down / endpoint misconfigured / unexpected shape →
|
|
1579
|
+
* silent bundled fallback (empty array). Never throws. Warns once per
|
|
1580
|
+
* process per error to avoid log spam.
|
|
1581
|
+
*
|
|
1582
|
+
* - **Opt-in.** Activation gated on `configureExclusionFindingsBrain()`
|
|
1583
|
+
* having been called with a runtime. The public `configureBrain()` in
|
|
1584
|
+
* brain.ts wires this up automatically when `BrainConfig.brainQuery
|
|
1585
|
+
* .findingsExclusions !== false`.
|
|
1586
|
+
*
|
|
1587
|
+
* Default endpoint:
|
|
1588
|
+
* `https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions`
|
|
1589
|
+
* The route accepts `?app_id=X` and returns a JSON array of findings
|
|
1590
|
+
* for that app filtered to `resolved_at IS NULL`. Public read; data
|
|
1591
|
+
* carries no consumer PII (app id, archetype, model id, cost ratio,
|
|
1592
|
+
* savings estimate).
|
|
1593
|
+
*/
|
|
1594
|
+
/**
|
|
1595
|
+
* Shape of one row from the `exclusion_findings` cache table. Mirrors the
|
|
1596
|
+
* snake_case JSONB returned by the brain RPC + the dashboard endpoint.
|
|
1597
|
+
* Camel-case mapping happens at the boundary (rowToFinding) so the in-memory
|
|
1598
|
+
* shape stays consistent with the rest of the library.
|
|
1599
|
+
*/
|
|
1600
|
+
interface ExclusionFindingRow {
|
|
1601
|
+
/** Intent archetype the finding applies to (e.g. 'hunt', 'classify'). */
|
|
1602
|
+
archetype: string;
|
|
1603
|
+
/** Canonical model id that the consumer has excluded (zero traffic in window). */
|
|
1604
|
+
excludedModel: string;
|
|
1605
|
+
/** Provider of the excluded model (e.g. 'deepseek', 'openai'). */
|
|
1606
|
+
excludedProvider: string;
|
|
1607
|
+
/** Detection verdict — alpha.37 ships 'recommend-probe' only. */
|
|
1608
|
+
verdict: 'recommend-probe' | 'unblock' | 'stay-excluded' | 'inconclusive';
|
|
1609
|
+
/**
|
|
1610
|
+
* Estimated 30-day savings in USD if the excluded model becomes primary
|
|
1611
|
+
* on this archetype. Null when pricing is incomplete or excluded model is
|
|
1612
|
+
* more expensive than the leader.
|
|
1613
|
+
*/
|
|
1614
|
+
estimatedSavingsUsd30d: number | null;
|
|
1615
|
+
/** Renderable summary; produced by the detector / RPC. */
|
|
1616
|
+
message: string;
|
|
1617
|
+
/** Actionable recommendation; produced by the detector / RPC. */
|
|
1618
|
+
suggestion: string;
|
|
1619
|
+
/** Detector confidence tier. */
|
|
1620
|
+
confidence: 'high' | 'medium' | 'low';
|
|
1621
|
+
/**
|
|
1622
|
+
* Free-form evidence pack (cost ratio, leader info, promo flags, parallel-
|
|
1623
|
+
* tool hint, etc.). Surfaced verbatim to advisor rules for richer messages.
|
|
1624
|
+
* Optional — older detector runs may not populate every field.
|
|
1625
|
+
*/
|
|
1626
|
+
evidence?: Record<string, unknown>;
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Default endpoint hosted on the kgauto-dashboard. Public read — data carries
|
|
1630
|
+
* no PII (app id, archetype, model id, cost ratio, savings estimate). Mirrors
|
|
1631
|
+
* the cross-tenant `/api/kgauto-v2/config` pattern.
|
|
1632
|
+
*/
|
|
1633
|
+
declare const DEFAULT_FINDINGS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions";
|
|
1634
|
+
/**
|
|
1635
|
+
* Sync introspection — is the findings-brain active? Used by advisor rules
|
|
1636
|
+
* to decide whether to even attempt a read.
|
|
1637
|
+
*/
|
|
1638
|
+
declare function isExclusionFindingsBrainActive(): boolean;
|
|
1639
|
+
interface GetExclusionFindingsOpts {
|
|
1640
|
+
/** App id to read findings for. Required. */
|
|
1641
|
+
appId: string;
|
|
1642
|
+
/**
|
|
1643
|
+
* Optional archetype filter. When set, returns only findings where
|
|
1644
|
+
* `archetype` matches. When omitted, returns all findings for the app.
|
|
1645
|
+
*/
|
|
1646
|
+
archetype?: string;
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Sync reader. Returns the cached findings for `appId`, optionally filtered
|
|
1650
|
+
* to a single archetype. First call returns empty array and triggers async
|
|
1651
|
+
* refresh; subsequent calls within TTL return brain data.
|
|
1652
|
+
*
|
|
1653
|
+
* NEVER throws. Brain down / endpoint misconfigured / mapper exception →
|
|
1654
|
+
* empty array.
|
|
1655
|
+
*/
|
|
1656
|
+
declare function getStaleExclusionFindings(opts: GetExclusionFindingsOpts): ExclusionFindingRow[];
|
|
1657
|
+
|
|
1468
1658
|
/**
|
|
1469
1659
|
* @warmdrift/kgauto v2 — prompt compiler + central learning brain.
|
|
1470
1660
|
*
|
|
@@ -1511,4 +1701,4 @@ declare const loadAliasesFromBrain: () => Record<string, string>;
|
|
|
1511
1701
|
*/
|
|
1512
1702
|
declare function compile(ir: PromptIR, opts?: CompileOptions): CompileResult;
|
|
1513
1703
|
|
|
1514
|
-
export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompatibilityIntent, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
|
|
1704
|
+
export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompatibilityIntent, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, DEFAULT_FINDINGS_ENDPOINT, type ExclusionFindingRow, type ExclusionResolutionSource, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type MarkExclusionFindingHandledOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStaleExclusionFindings, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isExclusionFindingsBrainActive, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, markExclusionFindingHandled, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
|
package/dist/index.d.ts
CHANGED
|
@@ -203,11 +203,28 @@ interface BrainQueryConfig {
|
|
|
203
203
|
pricing?: boolean;
|
|
204
204
|
/** Default true when endpoint set. Brain-driven model registry + aliases. */
|
|
205
205
|
models?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* alpha.38 — opt-in compile-time advisor for stale exclusions. Default
|
|
208
|
+
* `true` when endpoint is set. When enabled, the library reads
|
|
209
|
+
* `exclusion_findings` from the central kgauto-dashboard for the consumer's
|
|
210
|
+
* `app_id` (no consumer-side proxy work required) and surfaces a
|
|
211
|
+
* `stale-exclusion-candidate` advisory when compile()'s archetype has a
|
|
212
|
+
* recommend-probe finding open. Set `false` to silence (no fetch, no
|
|
213
|
+
* advisory).
|
|
214
|
+
*/
|
|
215
|
+
findingsExclusions?: boolean;
|
|
206
216
|
/** SWR window in ms. Default 300_000 (5 min). */
|
|
207
217
|
cacheTtlMs?: number;
|
|
208
218
|
/** Override the GET URL when the read endpoint differs from the write one.
|
|
209
219
|
* Defaults to `${endpoint}/v2/config` when omitted. */
|
|
210
220
|
configEndpoint?: string;
|
|
221
|
+
/**
|
|
222
|
+
* alpha.38 — override the findings endpoint when the consumer wants to
|
|
223
|
+
* proxy `exclusion_findings` reads through their own service rather than
|
|
224
|
+
* reading directly from the kgauto-dashboard. Library appends `?app_id=X`.
|
|
225
|
+
* Defaults to `https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions`.
|
|
226
|
+
*/
|
|
227
|
+
findingsEndpoint?: string;
|
|
211
228
|
}
|
|
212
229
|
interface BrainConfig {
|
|
213
230
|
/** Brain HTTP endpoint base URL (e.g., https://kgauto-brain.vercel.app/api). */
|
|
@@ -751,6 +768,80 @@ declare function markAdvisoryResolved(opts: MarkAdvisoryResolvedOptions): Promis
|
|
|
751
768
|
ok: false;
|
|
752
769
|
reason: string;
|
|
753
770
|
}>;
|
|
771
|
+
/**
|
|
772
|
+
* Resolution sources supported by the alpha.39 `markExclusionFindingHandled`
|
|
773
|
+
* API. Mirrors the subset of `exclusion_findings.resolution_source` CHECK
|
|
774
|
+
* constraint values that consumers can self-set:
|
|
775
|
+
*
|
|
776
|
+
* - `'consumer-marked'` — generic "we've handled this; stop reminding."
|
|
777
|
+
* Use this for "we evaluated and chose not to act" or "we'll act when
|
|
778
|
+
* ready but acknowledge the finding now."
|
|
779
|
+
* - `'declined'` — explicit "this exclusion is intentional; do not surface
|
|
780
|
+
* again." Stronger signal than consumer-marked; useful for compliance
|
|
781
|
+
* exclusions, brand-promise exclusions, etc.
|
|
782
|
+
* - `'probed-unblock'` — alpha.40+ probe-path verdict: consumer ran a live
|
|
783
|
+
* probe and decided to unblock the excluded model. Library accepts the
|
|
784
|
+
* value today so the probe path doesn't need a follow-up API release.
|
|
785
|
+
* - `'probed-stay-excluded'` — alpha.40+ probe-path verdict: consumer
|
|
786
|
+
* probed and confirmed the exclusion. Same forward-compat shape as
|
|
787
|
+
* `'probed-unblock'`.
|
|
788
|
+
*
|
|
789
|
+
* The `'auto'` resolution_source is NOT exposed here — that's reserved
|
|
790
|
+
* for the brain's own automatic resolution (e.g. when the detection
|
|
791
|
+
* conditions no longer hold). Consumer code sets only the four values
|
|
792
|
+
* above.
|
|
793
|
+
*/
|
|
794
|
+
type ExclusionResolutionSource = 'consumer-marked' | 'declined' | 'probed-unblock' | 'probed-stay-excluded';
|
|
795
|
+
interface MarkExclusionFindingHandledOptions {
|
|
796
|
+
/** App id the finding belongs to. Required (RLS scopes writes by this). */
|
|
797
|
+
appId: string;
|
|
798
|
+
/** Archetype the finding applies to (e.g. 'hunt', 'classify'). */
|
|
799
|
+
archetype: string;
|
|
800
|
+
/** Canonical model id that the consumer had excluded. */
|
|
801
|
+
excludedModel: string;
|
|
802
|
+
/** Resolution semantics — see `ExclusionResolutionSource` for the four
|
|
803
|
+
* values consumers can set. */
|
|
804
|
+
resolution: ExclusionResolutionSource;
|
|
805
|
+
/** Optional free-form note explaining the decision. Surfaces in operator
|
|
806
|
+
* digests + dashboards alongside the resolution_source. */
|
|
807
|
+
resolutionNote?: string;
|
|
808
|
+
/** Brain Supabase URL base (e.g. `https://<project>.supabase.co`). The
|
|
809
|
+
* function appends `/rest/v1/exclusion_findings?...`. */
|
|
810
|
+
brainEndpoint: string;
|
|
811
|
+
/** Consumer-scoped JWT carrying the `app_id` claim. The brain's RLS
|
|
812
|
+
* policy enforces that the JWT's app_id matches the finding's app_id;
|
|
813
|
+
* cross-tenant writes fail at the database layer regardless of input. */
|
|
814
|
+
brainJwt: string;
|
|
815
|
+
/** Supabase anon key for the `apikey` header (PostgREST requires it). */
|
|
816
|
+
brainAnonKey: string;
|
|
817
|
+
/** Injected fetch for tests. Defaults to global fetch. */
|
|
818
|
+
fetch?: typeof fetch;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Mark a stale-exclusion finding as handled. Returns an `ok/reason` envelope
|
|
822
|
+
* matching `markAdvisoryResolved` — consumer Admin UIs typically render the
|
|
823
|
+
* failure inline rather than crash.
|
|
824
|
+
*
|
|
825
|
+
* Idempotent: if no row matches the (app_id, archetype, excludedModel)
|
|
826
|
+
* tuple (already resolved, never existed, cron not yet UPSERTed), PostgREST
|
|
827
|
+
* returns 200 with zero affected rows and we return `{ ok: true }`. The
|
|
828
|
+
* caller's "I marked it as handled" intent is satisfied regardless of
|
|
829
|
+
* whether the row was already in that state.
|
|
830
|
+
*
|
|
831
|
+
* Reasons surfaced on failure:
|
|
832
|
+
* - `app_id_required` / `archetype_required` / `excluded_model_required`
|
|
833
|
+
* - `resolution_invalid` — `resolution` not one of the four documented values
|
|
834
|
+
* - `brain_auth_misconfig` — 401/403 (JWT / anon key wrong)
|
|
835
|
+
* - `brain_unavailable` — 5xx
|
|
836
|
+
* - `network_error:<message>` — fetch threw
|
|
837
|
+
* - `patch_failed:<status>` — anything else non-2xx
|
|
838
|
+
*/
|
|
839
|
+
declare function markExclusionFindingHandled(opts: MarkExclusionFindingHandledOptions): Promise<{
|
|
840
|
+
ok: true;
|
|
841
|
+
} | {
|
|
842
|
+
ok: false;
|
|
843
|
+
reason: string;
|
|
844
|
+
}>;
|
|
754
845
|
|
|
755
846
|
/**
|
|
756
847
|
* Archetype-cliff compatibility — alpha.28 (tt-intel-Cairn ratified).
|
|
@@ -1465,6 +1556,105 @@ declare const loadModelsFromBrain: () => Map<string, ModelProfile>;
|
|
|
1465
1556
|
*/
|
|
1466
1557
|
declare const loadAliasesFromBrain: () => Record<string, string>;
|
|
1467
1558
|
|
|
1559
|
+
/**
|
|
1560
|
+
* exclusion-findings-brain — alpha.38 KG-19 adapter.
|
|
1561
|
+
*
|
|
1562
|
+
* Per-tenant SWR cache for the `exclusion_findings` table populated by the
|
|
1563
|
+
* `kgauto-exclusion-re-evaluation-watch` cron (alpha.37 substrate). Distinct
|
|
1564
|
+
* from the cross-tenant `brain-query.ts` snapshot because exclusion findings
|
|
1565
|
+
* are scoped per `app_id` — one process = one consumer = one appId, but the
|
|
1566
|
+
* cache key supports multi-app processes by keying snapshots by appId.
|
|
1567
|
+
*
|
|
1568
|
+
* Architecture mirrors `brain-query.ts` but with a per-(appId) snapshot:
|
|
1569
|
+
*
|
|
1570
|
+
* - **Sync API surface.** `getStaleExclusionFindings({ appId, archetype })`
|
|
1571
|
+
* returns `ExclusionFindingRow[]` immediately. First call returns the
|
|
1572
|
+
* bundled fallback (empty array); async refresh fires in background;
|
|
1573
|
+
* subsequent calls within TTL return brain data.
|
|
1574
|
+
*
|
|
1575
|
+
* - **Per-appId snapshot.** Each appId gets its own cache entry. One fetch
|
|
1576
|
+
* per appId per TTL window. Tests can reset between cases.
|
|
1577
|
+
*
|
|
1578
|
+
* - **Tolerant.** Brain down / endpoint misconfigured / unexpected shape →
|
|
1579
|
+
* silent bundled fallback (empty array). Never throws. Warns once per
|
|
1580
|
+
* process per error to avoid log spam.
|
|
1581
|
+
*
|
|
1582
|
+
* - **Opt-in.** Activation gated on `configureExclusionFindingsBrain()`
|
|
1583
|
+
* having been called with a runtime. The public `configureBrain()` in
|
|
1584
|
+
* brain.ts wires this up automatically when `BrainConfig.brainQuery
|
|
1585
|
+
* .findingsExclusions !== false`.
|
|
1586
|
+
*
|
|
1587
|
+
* Default endpoint:
|
|
1588
|
+
* `https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions`
|
|
1589
|
+
* The route accepts `?app_id=X` and returns a JSON array of findings
|
|
1590
|
+
* for that app filtered to `resolved_at IS NULL`. Public read; data
|
|
1591
|
+
* carries no consumer PII (app id, archetype, model id, cost ratio,
|
|
1592
|
+
* savings estimate).
|
|
1593
|
+
*/
|
|
1594
|
+
/**
|
|
1595
|
+
* Shape of one row from the `exclusion_findings` cache table. Mirrors the
|
|
1596
|
+
* snake_case JSONB returned by the brain RPC + the dashboard endpoint.
|
|
1597
|
+
* Camel-case mapping happens at the boundary (rowToFinding) so the in-memory
|
|
1598
|
+
* shape stays consistent with the rest of the library.
|
|
1599
|
+
*/
|
|
1600
|
+
interface ExclusionFindingRow {
|
|
1601
|
+
/** Intent archetype the finding applies to (e.g. 'hunt', 'classify'). */
|
|
1602
|
+
archetype: string;
|
|
1603
|
+
/** Canonical model id that the consumer has excluded (zero traffic in window). */
|
|
1604
|
+
excludedModel: string;
|
|
1605
|
+
/** Provider of the excluded model (e.g. 'deepseek', 'openai'). */
|
|
1606
|
+
excludedProvider: string;
|
|
1607
|
+
/** Detection verdict — alpha.37 ships 'recommend-probe' only. */
|
|
1608
|
+
verdict: 'recommend-probe' | 'unblock' | 'stay-excluded' | 'inconclusive';
|
|
1609
|
+
/**
|
|
1610
|
+
* Estimated 30-day savings in USD if the excluded model becomes primary
|
|
1611
|
+
* on this archetype. Null when pricing is incomplete or excluded model is
|
|
1612
|
+
* more expensive than the leader.
|
|
1613
|
+
*/
|
|
1614
|
+
estimatedSavingsUsd30d: number | null;
|
|
1615
|
+
/** Renderable summary; produced by the detector / RPC. */
|
|
1616
|
+
message: string;
|
|
1617
|
+
/** Actionable recommendation; produced by the detector / RPC. */
|
|
1618
|
+
suggestion: string;
|
|
1619
|
+
/** Detector confidence tier. */
|
|
1620
|
+
confidence: 'high' | 'medium' | 'low';
|
|
1621
|
+
/**
|
|
1622
|
+
* Free-form evidence pack (cost ratio, leader info, promo flags, parallel-
|
|
1623
|
+
* tool hint, etc.). Surfaced verbatim to advisor rules for richer messages.
|
|
1624
|
+
* Optional — older detector runs may not populate every field.
|
|
1625
|
+
*/
|
|
1626
|
+
evidence?: Record<string, unknown>;
|
|
1627
|
+
}
|
|
1628
|
+
/**
|
|
1629
|
+
* Default endpoint hosted on the kgauto-dashboard. Public read — data carries
|
|
1630
|
+
* no PII (app id, archetype, model id, cost ratio, savings estimate). Mirrors
|
|
1631
|
+
* the cross-tenant `/api/kgauto-v2/config` pattern.
|
|
1632
|
+
*/
|
|
1633
|
+
declare const DEFAULT_FINDINGS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions";
|
|
1634
|
+
/**
|
|
1635
|
+
* Sync introspection — is the findings-brain active? Used by advisor rules
|
|
1636
|
+
* to decide whether to even attempt a read.
|
|
1637
|
+
*/
|
|
1638
|
+
declare function isExclusionFindingsBrainActive(): boolean;
|
|
1639
|
+
interface GetExclusionFindingsOpts {
|
|
1640
|
+
/** App id to read findings for. Required. */
|
|
1641
|
+
appId: string;
|
|
1642
|
+
/**
|
|
1643
|
+
* Optional archetype filter. When set, returns only findings where
|
|
1644
|
+
* `archetype` matches. When omitted, returns all findings for the app.
|
|
1645
|
+
*/
|
|
1646
|
+
archetype?: string;
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* Sync reader. Returns the cached findings for `appId`, optionally filtered
|
|
1650
|
+
* to a single archetype. First call returns empty array and triggers async
|
|
1651
|
+
* refresh; subsequent calls within TTL return brain data.
|
|
1652
|
+
*
|
|
1653
|
+
* NEVER throws. Brain down / endpoint misconfigured / mapper exception →
|
|
1654
|
+
* empty array.
|
|
1655
|
+
*/
|
|
1656
|
+
declare function getStaleExclusionFindings(opts: GetExclusionFindingsOpts): ExclusionFindingRow[];
|
|
1657
|
+
|
|
1468
1658
|
/**
|
|
1469
1659
|
* @warmdrift/kgauto v2 — prompt compiler + central learning brain.
|
|
1470
1660
|
*
|
|
@@ -1511,4 +1701,4 @@ declare const loadAliasesFromBrain: () => Record<string, string>;
|
|
|
1511
1701
|
*/
|
|
1512
1702
|
declare function compile(ir: PromptIR, opts?: CompileOptions): CompileResult;
|
|
1513
1703
|
|
|
1514
|
-
export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompatibilityIntent, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
|
|
1704
|
+
export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, CallOptions, CallResult, ChainEntry, type CompatibilityIntent, type CompileOptions, CompilePolicy, CompileResult, CompiledRequest, DEFAULT_FINDINGS_ENDPOINT, type ExclusionFindingRow, type ExclusionResolutionSource, type ExecuteErr, type ExecuteOk, type ExecuteOptions, type ExecuteResult, type FallbackPosture, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type MarkExclusionFindingHandledOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProfileToRowOptions, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, call, clearBrain, compile, configureBrain, countTokens, execute, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStaleExclusionFindings, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isExclusionFindingsBrainActive, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, markExclusionFindingHandled, profileToRow, record, recordOutcome, resetTokenizer, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
ALL_ARCHETYPES: () => ALL_ARCHETYPES,
|
|
26
26
|
ARCHETYPE_FLOOR_DEFAULT: () => ARCHETYPE_FLOOR_DEFAULT,
|
|
27
27
|
CallError: () => CallError,
|
|
28
|
+
DEFAULT_FINDINGS_ENDPOINT: () => DEFAULT_FINDINGS_ENDPOINT,
|
|
28
29
|
DIALECT_VERSION: () => DIALECT_VERSION,
|
|
29
30
|
INTENT_ARCHETYPES: () => INTENT_ARCHETYPES,
|
|
30
31
|
MEASURED_GROUNDING_MIN_N: () => MEASURED_GROUNDING_MIN_N,
|
|
@@ -56,11 +57,13 @@ __export(index_exports, {
|
|
|
56
57
|
getReachabilityDiagnostic: () => getReachabilityDiagnostic,
|
|
57
58
|
getSequentialStarterChain: () => getSequentialStarterChain,
|
|
58
59
|
getSequentialStarterChainWithGrounding: () => getSequentialStarterChainWithGrounding,
|
|
60
|
+
getStaleExclusionFindings: () => getStaleExclusionFindings,
|
|
59
61
|
getStarterChain: () => getStarterChain,
|
|
60
62
|
getStarterChainWithGrounding: () => getStarterChainWithGrounding,
|
|
61
63
|
hashShape: () => hashShape,
|
|
62
64
|
isArchetype: () => isArchetype,
|
|
63
65
|
isBrainQueryActiveFor: () => isBrainQueryActiveFor,
|
|
66
|
+
isExclusionFindingsBrainActive: () => isExclusionFindingsBrainActive,
|
|
64
67
|
isModelReachable: () => isModelReachable,
|
|
65
68
|
isProviderReachable: () => isProviderReachable,
|
|
66
69
|
learningKey: () => learningKey,
|
|
@@ -71,6 +74,7 @@ __export(index_exports, {
|
|
|
71
74
|
loadModelsFromBrain: () => loadModelsFromBrain,
|
|
72
75
|
loadPricingFromBrain: () => loadPricingFromBrain,
|
|
73
76
|
markAdvisoryResolved: () => markAdvisoryResolved,
|
|
77
|
+
markExclusionFindingHandled: () => markExclusionFindingHandled,
|
|
74
78
|
profileToRow: () => profileToRow,
|
|
75
79
|
profilesByProvider: () => profilesByProvider,
|
|
76
80
|
record: () => record,
|
|
@@ -2400,6 +2404,125 @@ function getModelCompatibility(modelId, intent) {
|
|
|
2400
2404
|
};
|
|
2401
2405
|
}
|
|
2402
2406
|
|
|
2407
|
+
// src/exclusion-findings-brain.ts
|
|
2408
|
+
function isValidVerdict(v) {
|
|
2409
|
+
return v === "recommend-probe" || v === "unblock" || v === "stay-excluded" || v === "inconclusive";
|
|
2410
|
+
}
|
|
2411
|
+
function isValidConfidence(v) {
|
|
2412
|
+
return v === "high" || v === "medium" || v === "low";
|
|
2413
|
+
}
|
|
2414
|
+
function isRawFindingRow(x) {
|
|
2415
|
+
if (!x || typeof x !== "object") return false;
|
|
2416
|
+
const r = x;
|
|
2417
|
+
return typeof r.intent_archetype === "string" && typeof r.excluded_model === "string" && typeof r.excluded_provider === "string" && typeof r.verdict === "string" && typeof r.message === "string" && typeof r.suggestion === "string" && typeof r.confidence === "string";
|
|
2418
|
+
}
|
|
2419
|
+
function mapRowsToFindings(rows) {
|
|
2420
|
+
const out = [];
|
|
2421
|
+
for (const row of rows) {
|
|
2422
|
+
if (!isRawFindingRow(row)) continue;
|
|
2423
|
+
if (!isValidVerdict(row.verdict)) continue;
|
|
2424
|
+
if (!isValidConfidence(row.confidence)) continue;
|
|
2425
|
+
let savings = null;
|
|
2426
|
+
if (typeof row.estimated_savings_usd_30d === "number") {
|
|
2427
|
+
savings = Number.isFinite(row.estimated_savings_usd_30d) ? row.estimated_savings_usd_30d : null;
|
|
2428
|
+
} else if (typeof row.estimated_savings_usd_30d === "string") {
|
|
2429
|
+
const n = Number(row.estimated_savings_usd_30d);
|
|
2430
|
+
savings = Number.isFinite(n) ? n : null;
|
|
2431
|
+
}
|
|
2432
|
+
out.push({
|
|
2433
|
+
archetype: row.intent_archetype,
|
|
2434
|
+
excludedModel: row.excluded_model,
|
|
2435
|
+
excludedProvider: row.excluded_provider,
|
|
2436
|
+
verdict: row.verdict,
|
|
2437
|
+
estimatedSavingsUsd30d: savings,
|
|
2438
|
+
message: row.message,
|
|
2439
|
+
suggestion: row.suggestion,
|
|
2440
|
+
confidence: row.confidence,
|
|
2441
|
+
evidence: row.evidence && typeof row.evidence === "object" ? row.evidence : void 0
|
|
2442
|
+
});
|
|
2443
|
+
}
|
|
2444
|
+
return out;
|
|
2445
|
+
}
|
|
2446
|
+
var snapshots = /* @__PURE__ */ new Map();
|
|
2447
|
+
var runtime2;
|
|
2448
|
+
var warnedOnce = false;
|
|
2449
|
+
var DEFAULT_FINDINGS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions";
|
|
2450
|
+
function configureExclusionFindingsBrain(rt) {
|
|
2451
|
+
runtime2 = rt;
|
|
2452
|
+
snapshots.clear();
|
|
2453
|
+
warnedOnce = false;
|
|
2454
|
+
}
|
|
2455
|
+
function isExclusionFindingsBrainActive() {
|
|
2456
|
+
return runtime2 !== void 0;
|
|
2457
|
+
}
|
|
2458
|
+
function getStaleExclusionFindings(opts) {
|
|
2459
|
+
const rt = runtime2;
|
|
2460
|
+
if (!rt) return [];
|
|
2461
|
+
const appId = opts.appId;
|
|
2462
|
+
if (!appId) return [];
|
|
2463
|
+
let snap = snapshots.get(appId);
|
|
2464
|
+
if (!snap) {
|
|
2465
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
2466
|
+
snapshots.set(appId, snap);
|
|
2467
|
+
}
|
|
2468
|
+
const now = Date.now();
|
|
2469
|
+
const stale = snap.expiresAt <= now;
|
|
2470
|
+
if (stale && !snap.refreshing) {
|
|
2471
|
+
snap.refreshing = true;
|
|
2472
|
+
void asyncRefresh2(rt, appId);
|
|
2473
|
+
}
|
|
2474
|
+
if (opts.archetype) {
|
|
2475
|
+
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
2476
|
+
}
|
|
2477
|
+
return snap.data;
|
|
2478
|
+
}
|
|
2479
|
+
var pendingRefreshes = /* @__PURE__ */ new Map();
|
|
2480
|
+
async function asyncRefresh2(rt, appId) {
|
|
2481
|
+
const promise = doRefresh2(rt, appId);
|
|
2482
|
+
pendingRefreshes.set(appId, promise);
|
|
2483
|
+
try {
|
|
2484
|
+
await promise;
|
|
2485
|
+
} finally {
|
|
2486
|
+
if (pendingRefreshes.get(appId) === promise) {
|
|
2487
|
+
pendingRefreshes.delete(appId);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
async function doRefresh2(rt, appId) {
|
|
2492
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
2493
|
+
let snap = snapshots.get(appId);
|
|
2494
|
+
if (!snap) {
|
|
2495
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
2496
|
+
snapshots.set(appId, snap);
|
|
2497
|
+
}
|
|
2498
|
+
try {
|
|
2499
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
2500
|
+
if (!res.ok) {
|
|
2501
|
+
throw new Error(`findings ${res.status}: ${res.statusText}`);
|
|
2502
|
+
}
|
|
2503
|
+
const body = await res.json();
|
|
2504
|
+
if (runtime2 !== rt) return;
|
|
2505
|
+
const rows = Array.isArray(body) ? mapRowsToFindings(body) : [];
|
|
2506
|
+
snap.data = rows;
|
|
2507
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
2508
|
+
snap.refreshing = false;
|
|
2509
|
+
} catch (err) {
|
|
2510
|
+
if (runtime2 !== rt) return;
|
|
2511
|
+
snap.refreshing = false;
|
|
2512
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
2513
|
+
if (!warnedOnce) {
|
|
2514
|
+
warnedOnce = true;
|
|
2515
|
+
(rt.onError ?? defaultOnError2)(err);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
function defaultOnError2(err) {
|
|
2520
|
+
console.warn(
|
|
2521
|
+
"[kgauto] exclusion-findings fetch failed (using empty fallback):",
|
|
2522
|
+
err
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2403
2526
|
// src/advisor.ts
|
|
2404
2527
|
var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
|
|
2405
2528
|
var TIER_DOWN_COST_RATIO = 0.5;
|
|
@@ -2419,6 +2542,9 @@ function runAdvisor(ir, result, profile, policy, phase2) {
|
|
|
2419
2542
|
if (!translatorClearedToolCallCliff(phase2)) {
|
|
2420
2543
|
out.push(...detectArchetypePerfFloorBreach(ir, profile));
|
|
2421
2544
|
}
|
|
2545
|
+
if (policy?.posture !== "locked") {
|
|
2546
|
+
out.push(...detectStaleExclusionCandidate(ir));
|
|
2547
|
+
}
|
|
2422
2548
|
return out;
|
|
2423
2549
|
}
|
|
2424
2550
|
function translatorClearedToolCallCliff(phase2) {
|
|
@@ -2627,6 +2753,39 @@ function detectArchetypePerfFloorBreach(ir, profile) {
|
|
|
2627
2753
|
}
|
|
2628
2754
|
];
|
|
2629
2755
|
}
|
|
2756
|
+
function detectStaleExclusionCandidate(ir) {
|
|
2757
|
+
if (!isExclusionFindingsBrainActive()) return [];
|
|
2758
|
+
if (!ir.appId) return [];
|
|
2759
|
+
const findings = getStaleExclusionFindings({
|
|
2760
|
+
appId: ir.appId,
|
|
2761
|
+
archetype: ir.intent.archetype
|
|
2762
|
+
});
|
|
2763
|
+
if (findings.length === 0) return [];
|
|
2764
|
+
const ranked = [...findings].sort((a, b) => {
|
|
2765
|
+
const sa = a.estimatedSavingsUsd30d ?? -Infinity;
|
|
2766
|
+
const sb = b.estimatedSavingsUsd30d ?? -Infinity;
|
|
2767
|
+
if (sa !== sb) return sb - sa;
|
|
2768
|
+
return confidenceRank(b.confidence) - confidenceRank(a.confidence);
|
|
2769
|
+
});
|
|
2770
|
+
const top = ranked[0];
|
|
2771
|
+
const extraCount = findings.length - 1;
|
|
2772
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more excluded model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
2773
|
+
return [
|
|
2774
|
+
{
|
|
2775
|
+
level: "info",
|
|
2776
|
+
code: "stale-exclusion-candidate",
|
|
2777
|
+
message: `${top.message}${extraNote}`,
|
|
2778
|
+
suggestion: top.suggestion,
|
|
2779
|
+
recommendationType: "tier-down",
|
|
2780
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
2781
|
+
}
|
|
2782
|
+
];
|
|
2783
|
+
}
|
|
2784
|
+
function confidenceRank(c) {
|
|
2785
|
+
if (c === "high") return 3;
|
|
2786
|
+
if (c === "medium") return 2;
|
|
2787
|
+
return 1;
|
|
2788
|
+
}
|
|
2630
2789
|
|
|
2631
2790
|
// src/translator.ts
|
|
2632
2791
|
var TRANSLATOR_FLOOR = ARCHETYPE_FLOOR_DEFAULT;
|
|
@@ -2990,20 +3149,31 @@ function configureBrain(config) {
|
|
|
2990
3149
|
}
|
|
2991
3150
|
if (enabledTables.size === 0) {
|
|
2992
3151
|
configureBrainQuery(void 0);
|
|
2993
|
-
|
|
3152
|
+
} else {
|
|
3153
|
+
configureBrainQuery({
|
|
3154
|
+
endpoint,
|
|
3155
|
+
configEndpoint: bq.configEndpoint,
|
|
3156
|
+
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
3157
|
+
fetchImpl: config.fetchImpl ?? fetch,
|
|
3158
|
+
enabledTables,
|
|
3159
|
+
onError: config.onError
|
|
3160
|
+
});
|
|
3161
|
+
}
|
|
3162
|
+
if (bq.findingsExclusions !== false) {
|
|
3163
|
+
configureExclusionFindingsBrain({
|
|
3164
|
+
endpoint: bq.findingsEndpoint ?? DEFAULT_FINDINGS_ENDPOINT,
|
|
3165
|
+
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
3166
|
+
fetchImpl: config.fetchImpl ?? fetch,
|
|
3167
|
+
onError: config.onError
|
|
3168
|
+
});
|
|
3169
|
+
} else {
|
|
3170
|
+
configureExclusionFindingsBrain(void 0);
|
|
2994
3171
|
}
|
|
2995
|
-
configureBrainQuery({
|
|
2996
|
-
endpoint,
|
|
2997
|
-
configEndpoint: bq.configEndpoint,
|
|
2998
|
-
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
2999
|
-
fetchImpl: config.fetchImpl ?? fetch,
|
|
3000
|
-
enabledTables,
|
|
3001
|
-
onError: config.onError
|
|
3002
|
-
});
|
|
3003
3172
|
}
|
|
3004
3173
|
function clearBrain() {
|
|
3005
3174
|
activeConfig = void 0;
|
|
3006
3175
|
configureBrainQuery(void 0);
|
|
3176
|
+
configureExclusionFindingsBrain(void 0);
|
|
3007
3177
|
}
|
|
3008
3178
|
var compileRegistry = /* @__PURE__ */ new Map();
|
|
3009
3179
|
var REGISTRY_MAX_ENTRIES = 1e4;
|
|
@@ -3107,7 +3277,7 @@ async function record(input) {
|
|
|
3107
3277
|
}
|
|
3108
3278
|
outcomeId = await tryExtractOutcomeId(res);
|
|
3109
3279
|
} catch (err) {
|
|
3110
|
-
(config.onError ??
|
|
3280
|
+
(config.onError ?? defaultOnError3)(err);
|
|
3111
3281
|
return;
|
|
3112
3282
|
}
|
|
3113
3283
|
const advisories = input.advisories ?? reg?.advisoriesFromCompile;
|
|
@@ -3129,7 +3299,7 @@ async function record(input) {
|
|
|
3129
3299
|
throw new Error(`brain advisories ${res.status}: ${text}`);
|
|
3130
3300
|
}
|
|
3131
3301
|
} catch (err) {
|
|
3132
|
-
(config.onError ??
|
|
3302
|
+
(config.onError ?? defaultOnError3)(err);
|
|
3133
3303
|
}
|
|
3134
3304
|
};
|
|
3135
3305
|
if (config.sync) {
|
|
@@ -3138,7 +3308,7 @@ async function record(input) {
|
|
|
3138
3308
|
void send();
|
|
3139
3309
|
}
|
|
3140
3310
|
}
|
|
3141
|
-
function
|
|
3311
|
+
function defaultOnError3(err) {
|
|
3142
3312
|
console.warn("[kgauto] brain record failed:", err);
|
|
3143
3313
|
}
|
|
3144
3314
|
function buildPayload(input, reg) {
|
|
@@ -3270,12 +3440,12 @@ async function recordOutcome(input) {
|
|
|
3270
3440
|
if (!res.ok) {
|
|
3271
3441
|
const text = await res.text().catch(() => "<no body>");
|
|
3272
3442
|
const err = new Error(`brain ${res.status}: ${text}`);
|
|
3273
|
-
(config.onError ??
|
|
3443
|
+
(config.onError ?? defaultOnError3)(err);
|
|
3274
3444
|
return { ok: false, reason: "persistence_failed" };
|
|
3275
3445
|
}
|
|
3276
3446
|
return { ok: true };
|
|
3277
3447
|
} catch (err) {
|
|
3278
|
-
(config.onError ??
|
|
3448
|
+
(config.onError ?? defaultOnError3)(err);
|
|
3279
3449
|
return { ok: false, reason: "persistence_failed" };
|
|
3280
3450
|
}
|
|
3281
3451
|
};
|
|
@@ -5264,6 +5434,66 @@ async function markAdvisoryResolved(opts) {
|
|
|
5264
5434
|
}
|
|
5265
5435
|
return { ok: true };
|
|
5266
5436
|
}
|
|
5437
|
+
async function markExclusionFindingHandled(opts) {
|
|
5438
|
+
const {
|
|
5439
|
+
appId,
|
|
5440
|
+
archetype,
|
|
5441
|
+
excludedModel,
|
|
5442
|
+
resolution,
|
|
5443
|
+
resolutionNote,
|
|
5444
|
+
brainEndpoint,
|
|
5445
|
+
brainJwt,
|
|
5446
|
+
brainAnonKey,
|
|
5447
|
+
fetch: injectedFetch
|
|
5448
|
+
} = opts;
|
|
5449
|
+
if (!appId) return { ok: false, reason: "app_id_required" };
|
|
5450
|
+
if (!archetype) return { ok: false, reason: "archetype_required" };
|
|
5451
|
+
if (!excludedModel) {
|
|
5452
|
+
return { ok: false, reason: "excluded_model_required" };
|
|
5453
|
+
}
|
|
5454
|
+
if (resolution !== "consumer-marked" && resolution !== "declined" && resolution !== "probed-unblock" && resolution !== "probed-stay-excluded") {
|
|
5455
|
+
return { ok: false, reason: "resolution_invalid" };
|
|
5456
|
+
}
|
|
5457
|
+
const doFetch = resolveFetch(injectedFetch);
|
|
5458
|
+
const base = normalizeEndpoint(brainEndpoint);
|
|
5459
|
+
const url = `${base}/rest/v1/exclusion_findings?app_id=eq.${encodeURIComponent(appId)}&intent_archetype=eq.${encodeURIComponent(archetype)}&excluded_model=eq.${encodeURIComponent(excludedModel)}&resolved_at=is.null`;
|
|
5460
|
+
const patchBody = {
|
|
5461
|
+
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5462
|
+
resolution_source: resolution
|
|
5463
|
+
};
|
|
5464
|
+
if (resolutionNote !== void 0) {
|
|
5465
|
+
patchBody.resolution_note = resolutionNote;
|
|
5466
|
+
}
|
|
5467
|
+
let res;
|
|
5468
|
+
try {
|
|
5469
|
+
res = await doFetch(url, {
|
|
5470
|
+
method: "PATCH",
|
|
5471
|
+
headers: {
|
|
5472
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
5473
|
+
apikey: brainAnonKey,
|
|
5474
|
+
"Content-Type": "application/json",
|
|
5475
|
+
Accept: "application/json",
|
|
5476
|
+
// PATCH may match zero rows when already resolved — return=minimal
|
|
5477
|
+
// keeps the response body empty so the success path is uniform.
|
|
5478
|
+
Prefer: "return=minimal"
|
|
5479
|
+
},
|
|
5480
|
+
body: JSON.stringify(patchBody)
|
|
5481
|
+
});
|
|
5482
|
+
} catch (err) {
|
|
5483
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5484
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
5485
|
+
}
|
|
5486
|
+
if (res.status === 401 || res.status === 403) {
|
|
5487
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
5488
|
+
}
|
|
5489
|
+
if (res.status >= 500) {
|
|
5490
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
5491
|
+
}
|
|
5492
|
+
if (!res.ok) {
|
|
5493
|
+
return { ok: false, reason: `patch_failed:${res.status}` };
|
|
5494
|
+
}
|
|
5495
|
+
return { ok: true };
|
|
5496
|
+
}
|
|
5267
5497
|
|
|
5268
5498
|
// src/models-brain.ts
|
|
5269
5499
|
function isModelRow(x) {
|
|
@@ -5397,6 +5627,7 @@ function compile2(ir, opts) {
|
|
|
5397
5627
|
ALL_ARCHETYPES,
|
|
5398
5628
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
5399
5629
|
CallError,
|
|
5630
|
+
DEFAULT_FINDINGS_ENDPOINT,
|
|
5400
5631
|
DIALECT_VERSION,
|
|
5401
5632
|
INTENT_ARCHETYPES,
|
|
5402
5633
|
MEASURED_GROUNDING_MIN_N,
|
|
@@ -5428,11 +5659,13 @@ function compile2(ir, opts) {
|
|
|
5428
5659
|
getReachabilityDiagnostic,
|
|
5429
5660
|
getSequentialStarterChain,
|
|
5430
5661
|
getSequentialStarterChainWithGrounding,
|
|
5662
|
+
getStaleExclusionFindings,
|
|
5431
5663
|
getStarterChain,
|
|
5432
5664
|
getStarterChainWithGrounding,
|
|
5433
5665
|
hashShape,
|
|
5434
5666
|
isArchetype,
|
|
5435
5667
|
isBrainQueryActiveFor,
|
|
5668
|
+
isExclusionFindingsBrainActive,
|
|
5436
5669
|
isModelReachable,
|
|
5437
5670
|
isProviderReachable,
|
|
5438
5671
|
learningKey,
|
|
@@ -5443,6 +5676,7 @@ function compile2(ir, opts) {
|
|
|
5443
5676
|
loadModelsFromBrain,
|
|
5444
5677
|
loadPricingFromBrain,
|
|
5445
5678
|
markAdvisoryResolved,
|
|
5679
|
+
markExclusionFindingHandled,
|
|
5446
5680
|
profileToRow,
|
|
5447
5681
|
profilesByProvider,
|
|
5448
5682
|
record,
|
package/dist/index.mjs
CHANGED
|
@@ -825,6 +825,125 @@ function getArchetypePerfScore(modelId, archetype) {
|
|
|
825
825
|
return { score, n, grounding };
|
|
826
826
|
}
|
|
827
827
|
|
|
828
|
+
// src/exclusion-findings-brain.ts
|
|
829
|
+
function isValidVerdict(v) {
|
|
830
|
+
return v === "recommend-probe" || v === "unblock" || v === "stay-excluded" || v === "inconclusive";
|
|
831
|
+
}
|
|
832
|
+
function isValidConfidence(v) {
|
|
833
|
+
return v === "high" || v === "medium" || v === "low";
|
|
834
|
+
}
|
|
835
|
+
function isRawFindingRow(x) {
|
|
836
|
+
if (!x || typeof x !== "object") return false;
|
|
837
|
+
const r = x;
|
|
838
|
+
return typeof r.intent_archetype === "string" && typeof r.excluded_model === "string" && typeof r.excluded_provider === "string" && typeof r.verdict === "string" && typeof r.message === "string" && typeof r.suggestion === "string" && typeof r.confidence === "string";
|
|
839
|
+
}
|
|
840
|
+
function mapRowsToFindings(rows) {
|
|
841
|
+
const out = [];
|
|
842
|
+
for (const row of rows) {
|
|
843
|
+
if (!isRawFindingRow(row)) continue;
|
|
844
|
+
if (!isValidVerdict(row.verdict)) continue;
|
|
845
|
+
if (!isValidConfidence(row.confidence)) continue;
|
|
846
|
+
let savings = null;
|
|
847
|
+
if (typeof row.estimated_savings_usd_30d === "number") {
|
|
848
|
+
savings = Number.isFinite(row.estimated_savings_usd_30d) ? row.estimated_savings_usd_30d : null;
|
|
849
|
+
} else if (typeof row.estimated_savings_usd_30d === "string") {
|
|
850
|
+
const n = Number(row.estimated_savings_usd_30d);
|
|
851
|
+
savings = Number.isFinite(n) ? n : null;
|
|
852
|
+
}
|
|
853
|
+
out.push({
|
|
854
|
+
archetype: row.intent_archetype,
|
|
855
|
+
excludedModel: row.excluded_model,
|
|
856
|
+
excludedProvider: row.excluded_provider,
|
|
857
|
+
verdict: row.verdict,
|
|
858
|
+
estimatedSavingsUsd30d: savings,
|
|
859
|
+
message: row.message,
|
|
860
|
+
suggestion: row.suggestion,
|
|
861
|
+
confidence: row.confidence,
|
|
862
|
+
evidence: row.evidence && typeof row.evidence === "object" ? row.evidence : void 0
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
return out;
|
|
866
|
+
}
|
|
867
|
+
var snapshots = /* @__PURE__ */ new Map();
|
|
868
|
+
var runtime;
|
|
869
|
+
var warnedOnce = false;
|
|
870
|
+
var DEFAULT_FINDINGS_ENDPOINT = "https://kgauto-dashboard.vercel.app/api/kgauto-v2/findings/exclusions";
|
|
871
|
+
function configureExclusionFindingsBrain(rt) {
|
|
872
|
+
runtime = rt;
|
|
873
|
+
snapshots.clear();
|
|
874
|
+
warnedOnce = false;
|
|
875
|
+
}
|
|
876
|
+
function isExclusionFindingsBrainActive() {
|
|
877
|
+
return runtime !== void 0;
|
|
878
|
+
}
|
|
879
|
+
function getStaleExclusionFindings(opts) {
|
|
880
|
+
const rt = runtime;
|
|
881
|
+
if (!rt) return [];
|
|
882
|
+
const appId = opts.appId;
|
|
883
|
+
if (!appId) return [];
|
|
884
|
+
let snap = snapshots.get(appId);
|
|
885
|
+
if (!snap) {
|
|
886
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
887
|
+
snapshots.set(appId, snap);
|
|
888
|
+
}
|
|
889
|
+
const now = Date.now();
|
|
890
|
+
const stale = snap.expiresAt <= now;
|
|
891
|
+
if (stale && !snap.refreshing) {
|
|
892
|
+
snap.refreshing = true;
|
|
893
|
+
void asyncRefresh(rt, appId);
|
|
894
|
+
}
|
|
895
|
+
if (opts.archetype) {
|
|
896
|
+
return snap.data.filter((f) => f.archetype === opts.archetype);
|
|
897
|
+
}
|
|
898
|
+
return snap.data;
|
|
899
|
+
}
|
|
900
|
+
var pendingRefreshes = /* @__PURE__ */ new Map();
|
|
901
|
+
async function asyncRefresh(rt, appId) {
|
|
902
|
+
const promise = doRefresh(rt, appId);
|
|
903
|
+
pendingRefreshes.set(appId, promise);
|
|
904
|
+
try {
|
|
905
|
+
await promise;
|
|
906
|
+
} finally {
|
|
907
|
+
if (pendingRefreshes.get(appId) === promise) {
|
|
908
|
+
pendingRefreshes.delete(appId);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
async function doRefresh(rt, appId) {
|
|
913
|
+
const url = `${rt.endpoint}?app_id=${encodeURIComponent(appId)}`;
|
|
914
|
+
let snap = snapshots.get(appId);
|
|
915
|
+
if (!snap) {
|
|
916
|
+
snap = { data: [], expiresAt: 0, refreshing: false };
|
|
917
|
+
snapshots.set(appId, snap);
|
|
918
|
+
}
|
|
919
|
+
try {
|
|
920
|
+
const res = await rt.fetchImpl(url, { method: "GET" });
|
|
921
|
+
if (!res.ok) {
|
|
922
|
+
throw new Error(`findings ${res.status}: ${res.statusText}`);
|
|
923
|
+
}
|
|
924
|
+
const body = await res.json();
|
|
925
|
+
if (runtime !== rt) return;
|
|
926
|
+
const rows = Array.isArray(body) ? mapRowsToFindings(body) : [];
|
|
927
|
+
snap.data = rows;
|
|
928
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
929
|
+
snap.refreshing = false;
|
|
930
|
+
} catch (err) {
|
|
931
|
+
if (runtime !== rt) return;
|
|
932
|
+
snap.refreshing = false;
|
|
933
|
+
snap.expiresAt = Date.now() + rt.ttlMs;
|
|
934
|
+
if (!warnedOnce) {
|
|
935
|
+
warnedOnce = true;
|
|
936
|
+
(rt.onError ?? defaultOnError)(err);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
function defaultOnError(err) {
|
|
941
|
+
console.warn(
|
|
942
|
+
"[kgauto] exclusion-findings fetch failed (using empty fallback):",
|
|
943
|
+
err
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
|
|
828
947
|
// src/advisor.ts
|
|
829
948
|
var QUALITY_FLOOR_FOR_RECOMMENDATION = 6;
|
|
830
949
|
var TIER_DOWN_COST_RATIO = 0.5;
|
|
@@ -844,6 +963,9 @@ function runAdvisor(ir, result, profile, policy, phase2) {
|
|
|
844
963
|
if (!translatorClearedToolCallCliff(phase2)) {
|
|
845
964
|
out.push(...detectArchetypePerfFloorBreach(ir, profile));
|
|
846
965
|
}
|
|
966
|
+
if (policy?.posture !== "locked") {
|
|
967
|
+
out.push(...detectStaleExclusionCandidate(ir));
|
|
968
|
+
}
|
|
847
969
|
return out;
|
|
848
970
|
}
|
|
849
971
|
function translatorClearedToolCallCliff(phase2) {
|
|
@@ -1052,6 +1174,39 @@ function detectArchetypePerfFloorBreach(ir, profile) {
|
|
|
1052
1174
|
}
|
|
1053
1175
|
];
|
|
1054
1176
|
}
|
|
1177
|
+
function detectStaleExclusionCandidate(ir) {
|
|
1178
|
+
if (!isExclusionFindingsBrainActive()) return [];
|
|
1179
|
+
if (!ir.appId) return [];
|
|
1180
|
+
const findings = getStaleExclusionFindings({
|
|
1181
|
+
appId: ir.appId,
|
|
1182
|
+
archetype: ir.intent.archetype
|
|
1183
|
+
});
|
|
1184
|
+
if (findings.length === 0) return [];
|
|
1185
|
+
const ranked = [...findings].sort((a, b) => {
|
|
1186
|
+
const sa = a.estimatedSavingsUsd30d ?? -Infinity;
|
|
1187
|
+
const sb = b.estimatedSavingsUsd30d ?? -Infinity;
|
|
1188
|
+
if (sa !== sb) return sb - sa;
|
|
1189
|
+
return confidenceRank(b.confidence) - confidenceRank(a.confidence);
|
|
1190
|
+
});
|
|
1191
|
+
const top = ranked[0];
|
|
1192
|
+
const extraCount = findings.length - 1;
|
|
1193
|
+
const extraNote = extraCount > 0 ? ` (+ ${extraCount} more excluded model${extraCount === 1 ? "" : "s"} for this archetype)` : "";
|
|
1194
|
+
return [
|
|
1195
|
+
{
|
|
1196
|
+
level: "info",
|
|
1197
|
+
code: "stale-exclusion-candidate",
|
|
1198
|
+
message: `${top.message}${extraNote}`,
|
|
1199
|
+
suggestion: top.suggestion,
|
|
1200
|
+
recommendationType: "tier-down",
|
|
1201
|
+
docsUrl: "https://github.com/stue/command-center/blob/main/interfaces/kgauto.md#best-practice-advisories"
|
|
1202
|
+
}
|
|
1203
|
+
];
|
|
1204
|
+
}
|
|
1205
|
+
function confidenceRank(c) {
|
|
1206
|
+
if (c === "high") return 3;
|
|
1207
|
+
if (c === "medium") return 2;
|
|
1208
|
+
return 1;
|
|
1209
|
+
}
|
|
1055
1210
|
|
|
1056
1211
|
// src/translator.ts
|
|
1057
1212
|
var TRANSLATOR_FLOOR = ARCHETYPE_FLOOR_DEFAULT;
|
|
@@ -1415,20 +1570,31 @@ function configureBrain(config) {
|
|
|
1415
1570
|
}
|
|
1416
1571
|
if (enabledTables.size === 0) {
|
|
1417
1572
|
configureBrainQuery(void 0);
|
|
1418
|
-
|
|
1573
|
+
} else {
|
|
1574
|
+
configureBrainQuery({
|
|
1575
|
+
endpoint,
|
|
1576
|
+
configEndpoint: bq.configEndpoint,
|
|
1577
|
+
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
1578
|
+
fetchImpl: config.fetchImpl ?? fetch,
|
|
1579
|
+
enabledTables,
|
|
1580
|
+
onError: config.onError
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
if (bq.findingsExclusions !== false) {
|
|
1584
|
+
configureExclusionFindingsBrain({
|
|
1585
|
+
endpoint: bq.findingsEndpoint ?? DEFAULT_FINDINGS_ENDPOINT,
|
|
1586
|
+
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
1587
|
+
fetchImpl: config.fetchImpl ?? fetch,
|
|
1588
|
+
onError: config.onError
|
|
1589
|
+
});
|
|
1590
|
+
} else {
|
|
1591
|
+
configureExclusionFindingsBrain(void 0);
|
|
1419
1592
|
}
|
|
1420
|
-
configureBrainQuery({
|
|
1421
|
-
endpoint,
|
|
1422
|
-
configEndpoint: bq.configEndpoint,
|
|
1423
|
-
ttlMs: bq.cacheTtlMs ?? 3e5,
|
|
1424
|
-
fetchImpl: config.fetchImpl ?? fetch,
|
|
1425
|
-
enabledTables,
|
|
1426
|
-
onError: config.onError
|
|
1427
|
-
});
|
|
1428
1593
|
}
|
|
1429
1594
|
function clearBrain() {
|
|
1430
1595
|
activeConfig = void 0;
|
|
1431
1596
|
configureBrainQuery(void 0);
|
|
1597
|
+
configureExclusionFindingsBrain(void 0);
|
|
1432
1598
|
}
|
|
1433
1599
|
var compileRegistry = /* @__PURE__ */ new Map();
|
|
1434
1600
|
var REGISTRY_MAX_ENTRIES = 1e4;
|
|
@@ -1532,7 +1698,7 @@ async function record(input) {
|
|
|
1532
1698
|
}
|
|
1533
1699
|
outcomeId = await tryExtractOutcomeId(res);
|
|
1534
1700
|
} catch (err) {
|
|
1535
|
-
(config.onError ??
|
|
1701
|
+
(config.onError ?? defaultOnError2)(err);
|
|
1536
1702
|
return;
|
|
1537
1703
|
}
|
|
1538
1704
|
const advisories = input.advisories ?? reg?.advisoriesFromCompile;
|
|
@@ -1554,7 +1720,7 @@ async function record(input) {
|
|
|
1554
1720
|
throw new Error(`brain advisories ${res.status}: ${text}`);
|
|
1555
1721
|
}
|
|
1556
1722
|
} catch (err) {
|
|
1557
|
-
(config.onError ??
|
|
1723
|
+
(config.onError ?? defaultOnError2)(err);
|
|
1558
1724
|
}
|
|
1559
1725
|
};
|
|
1560
1726
|
if (config.sync) {
|
|
@@ -1563,7 +1729,7 @@ async function record(input) {
|
|
|
1563
1729
|
void send();
|
|
1564
1730
|
}
|
|
1565
1731
|
}
|
|
1566
|
-
function
|
|
1732
|
+
function defaultOnError2(err) {
|
|
1567
1733
|
console.warn("[kgauto] brain record failed:", err);
|
|
1568
1734
|
}
|
|
1569
1735
|
function buildPayload(input, reg) {
|
|
@@ -1695,12 +1861,12 @@ async function recordOutcome(input) {
|
|
|
1695
1861
|
if (!res.ok) {
|
|
1696
1862
|
const text = await res.text().catch(() => "<no body>");
|
|
1697
1863
|
const err = new Error(`brain ${res.status}: ${text}`);
|
|
1698
|
-
(config.onError ??
|
|
1864
|
+
(config.onError ?? defaultOnError2)(err);
|
|
1699
1865
|
return { ok: false, reason: "persistence_failed" };
|
|
1700
1866
|
}
|
|
1701
1867
|
return { ok: true };
|
|
1702
1868
|
} catch (err) {
|
|
1703
|
-
(config.onError ??
|
|
1869
|
+
(config.onError ?? defaultOnError2)(err);
|
|
1704
1870
|
return { ok: false, reason: "persistence_failed" };
|
|
1705
1871
|
}
|
|
1706
1872
|
};
|
|
@@ -2984,6 +3150,66 @@ async function markAdvisoryResolved(opts) {
|
|
|
2984
3150
|
}
|
|
2985
3151
|
return { ok: true };
|
|
2986
3152
|
}
|
|
3153
|
+
async function markExclusionFindingHandled(opts) {
|
|
3154
|
+
const {
|
|
3155
|
+
appId,
|
|
3156
|
+
archetype,
|
|
3157
|
+
excludedModel,
|
|
3158
|
+
resolution,
|
|
3159
|
+
resolutionNote,
|
|
3160
|
+
brainEndpoint,
|
|
3161
|
+
brainJwt,
|
|
3162
|
+
brainAnonKey,
|
|
3163
|
+
fetch: injectedFetch
|
|
3164
|
+
} = opts;
|
|
3165
|
+
if (!appId) return { ok: false, reason: "app_id_required" };
|
|
3166
|
+
if (!archetype) return { ok: false, reason: "archetype_required" };
|
|
3167
|
+
if (!excludedModel) {
|
|
3168
|
+
return { ok: false, reason: "excluded_model_required" };
|
|
3169
|
+
}
|
|
3170
|
+
if (resolution !== "consumer-marked" && resolution !== "declined" && resolution !== "probed-unblock" && resolution !== "probed-stay-excluded") {
|
|
3171
|
+
return { ok: false, reason: "resolution_invalid" };
|
|
3172
|
+
}
|
|
3173
|
+
const doFetch = resolveFetch(injectedFetch);
|
|
3174
|
+
const base = normalizeEndpoint(brainEndpoint);
|
|
3175
|
+
const url = `${base}/rest/v1/exclusion_findings?app_id=eq.${encodeURIComponent(appId)}&intent_archetype=eq.${encodeURIComponent(archetype)}&excluded_model=eq.${encodeURIComponent(excludedModel)}&resolved_at=is.null`;
|
|
3176
|
+
const patchBody = {
|
|
3177
|
+
resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3178
|
+
resolution_source: resolution
|
|
3179
|
+
};
|
|
3180
|
+
if (resolutionNote !== void 0) {
|
|
3181
|
+
patchBody.resolution_note = resolutionNote;
|
|
3182
|
+
}
|
|
3183
|
+
let res;
|
|
3184
|
+
try {
|
|
3185
|
+
res = await doFetch(url, {
|
|
3186
|
+
method: "PATCH",
|
|
3187
|
+
headers: {
|
|
3188
|
+
Authorization: `Bearer ${brainJwt}`,
|
|
3189
|
+
apikey: brainAnonKey,
|
|
3190
|
+
"Content-Type": "application/json",
|
|
3191
|
+
Accept: "application/json",
|
|
3192
|
+
// PATCH may match zero rows when already resolved — return=minimal
|
|
3193
|
+
// keeps the response body empty so the success path is uniform.
|
|
3194
|
+
Prefer: "return=minimal"
|
|
3195
|
+
},
|
|
3196
|
+
body: JSON.stringify(patchBody)
|
|
3197
|
+
});
|
|
3198
|
+
} catch (err) {
|
|
3199
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3200
|
+
return { ok: false, reason: `network_error:${msg}` };
|
|
3201
|
+
}
|
|
3202
|
+
if (res.status === 401 || res.status === 403) {
|
|
3203
|
+
return { ok: false, reason: "brain_auth_misconfig" };
|
|
3204
|
+
}
|
|
3205
|
+
if (res.status >= 500) {
|
|
3206
|
+
return { ok: false, reason: "brain_unavailable" };
|
|
3207
|
+
}
|
|
3208
|
+
if (!res.ok) {
|
|
3209
|
+
return { ok: false, reason: `patch_failed:${res.status}` };
|
|
3210
|
+
}
|
|
3211
|
+
return { ok: true };
|
|
3212
|
+
}
|
|
2987
3213
|
|
|
2988
3214
|
// src/models-brain.ts
|
|
2989
3215
|
function isModelRow(x) {
|
|
@@ -3116,6 +3342,7 @@ export {
|
|
|
3116
3342
|
ALL_ARCHETYPES,
|
|
3117
3343
|
ARCHETYPE_FLOOR_DEFAULT,
|
|
3118
3344
|
CallError,
|
|
3345
|
+
DEFAULT_FINDINGS_ENDPOINT,
|
|
3119
3346
|
DIALECT_VERSION,
|
|
3120
3347
|
INTENT_ARCHETYPES,
|
|
3121
3348
|
MEASURED_GROUNDING_MIN_N,
|
|
@@ -3147,11 +3374,13 @@ export {
|
|
|
3147
3374
|
getReachabilityDiagnostic,
|
|
3148
3375
|
getSequentialStarterChain,
|
|
3149
3376
|
getSequentialStarterChainWithGrounding,
|
|
3377
|
+
getStaleExclusionFindings,
|
|
3150
3378
|
getStarterChain,
|
|
3151
3379
|
getStarterChainWithGrounding,
|
|
3152
3380
|
hashShape,
|
|
3153
3381
|
isArchetype,
|
|
3154
3382
|
isBrainQueryActiveFor,
|
|
3383
|
+
isExclusionFindingsBrainActive,
|
|
3155
3384
|
isModelReachable,
|
|
3156
3385
|
isProviderReachable,
|
|
3157
3386
|
learningKey,
|
|
@@ -3162,6 +3391,7 @@ export {
|
|
|
3162
3391
|
loadModelsFromBrain,
|
|
3163
3392
|
loadPricingFromBrain,
|
|
3164
3393
|
markAdvisoryResolved,
|
|
3394
|
+
markExclusionFindingHandled,
|
|
3165
3395
|
profileToRow,
|
|
3166
3396
|
profilesByProvider,
|
|
3167
3397
|
record,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warmdrift/kgauto-compiler",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.39",
|
|
4
4
|
"description": "Prompt compiler + central learning brain for multi-model AI apps. Swap models without rewriting prompts.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|