@warmdrift/kgauto-compiler 2.0.0-alpha.46 → 2.0.0-alpha.48

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/README.md CHANGED
@@ -89,6 +89,37 @@ await record({
89
89
  });
90
90
  ```
91
91
 
92
+ ### `probeShadow()` — measure a swap candidate from an AI-SDK route (alpha.48)
93
+
94
+ `call()` runs a full-IR shadow probe internally (`shadowProbe` option). AI-SDK consumers who drive `streamText()` themselves never call `call()`, so they reach the same probe via the standalone `probeShadow()` export — fire it from `onFinish`, passing the served leg you already have, wrapped in `waitUntil()`/`after()` so it never blocks the response.
95
+
96
+ ```ts
97
+ import { probeShadow, configureBrain } from '@warmdrift/kgauto-compiler';
98
+ import { after } from 'next/server'; // or the platform's waitUntil()
99
+
100
+ configureBrain({ endpoint: 'https://your-app.com/api/kgauto/v2', apiKey: '...' });
101
+
102
+ const t0 = Date.now();
103
+ const result = streamText({
104
+ model, system, messages,
105
+ onFinish: ({ text, usage }) => {
106
+ after(() => probeShadow(ir, {
107
+ served: {
108
+ model: servedModelId,
109
+ responseText: text,
110
+ tokensIn: usage.promptTokens,
111
+ tokensOut: usage.completionTokens,
112
+ latencyMs: Date.now() - t0,
113
+ },
114
+ candidates: ['deepseek-v4-pro'], // what you'd consider swapping to
115
+ sampleRate: 0.05, // pin 1 for a dogfood window
116
+ }));
117
+ },
118
+ });
119
+ ```
120
+
121
+ kgauto runs each candidate on the **same IR** and persists a `probe_outcomes` row (`replay_source='inline-full-ir'`, `prompt_fidelity=1.0`, both latency legs). Phase 1 is `judge:'off'` — rows accumulate for an offline verdict rollup; inline Opus judging is Phase 2. Fire-and-forget: never throws into your `onFinish`.
122
+
92
123
  ## Structured advisories API (alpha.29+)
93
124
 
94
125
  kgauto's compile-time advisories (`caching-off-on-claude`, `tool-bloat`,
@@ -1,4 +1,15 @@
1
1
  // src/profiles.ts
2
+ var LATENCY_TIER_MS = {
3
+ fast: 4e3,
4
+ medium: 11e3,
5
+ slow: 24e3
6
+ };
7
+ function latencyTierOf(profile) {
8
+ if (profile.latencyTier) return profile.latencyTier;
9
+ if (profile.weaknesses.includes("latency")) return "slow";
10
+ if (profile.strengths.includes("speed")) return "fast";
11
+ return "medium";
12
+ }
2
13
  var ANTHROPIC_LOWERING_BASE = {
3
14
  system: { mode: "inline" },
4
15
  cache: {
@@ -545,6 +556,14 @@ var PROFILES_RAW = [
545
556
  ],
546
557
  strengths: ["cost", "1m_context", "json_output", "code", "reasoning"],
547
558
  weaknesses: ["parallel_tools", "large_tool_sets"],
559
+ // alpha.47 — explicit slow override. Tag derivation would say 'medium'
560
+ // (no 'latency' weakness, no 'speed' strength), but the alpha.46 shadow
561
+ // probe MEASURED deepseek-v4-flash at 20485ms served (2026-06-03) — ~2.3×
562
+ // gemini-2.5-flash on PB's synchronous /api/analyze path. The 'flash' name
563
+ // is DeepSeek's, not a speed promise. This is the row that, scoring 0.85
564
+ // baseQuality (it carries 'reasoning') with no latency counterweight,
565
+ // leapfrogged gemini-2.5-flash as PB's summarize leader once reachable.
566
+ latencyTier: "slow",
548
567
  notes: "Cheap workhorse. 1M context, 384k max output. Cache-hit input $0.0028/M (1/50\xD7 of miss). Aliased as `deepseek-chat` (non-thinking) and `deepseek-reasoner` (thinking) \u2014 see ALIASES.",
549
568
  // Master plan §6.2 anchor. Brain-validated tier 1 cross-provider for
550
569
  // classify (169 rows, 0% empty). Tier 0 for summarize-with-no-tools.
@@ -612,6 +631,11 @@ var PROFILES_RAW = [
612
631
  ],
613
632
  strengths: ["quality", "reasoning", "1m_context", "json_output", "code", "extended_thinking"],
614
633
  weaknesses: ["parallel_tools", "large_tool_sets"],
634
+ // alpha.47 — explicit slow override. Measured 47722ms on the alpha.46
635
+ // shadow probe (2026-06-03) — it's an extended-thinking reasoner, slowest
636
+ // of the served set. Tag derivation would say 'medium'; the measurement says
637
+ // otherwise.
638
+ latencyTier: "slow",
615
639
  notes: "Pro tier. 1M context, 384k max output. Regular pricing $1.74/$3.48; 75% promo through 2026-05-31 ($0.435/$0.87). Default mode = thinking.",
616
640
  // Master plan §3.3: tier 3 cross-provider for plan chain. Reasoning
617
641
  // bumped one notch over V4-Flash; same parallel-tool ceiling.
@@ -1264,6 +1288,8 @@ function profilesByProvider(provider) {
1264
1288
  }
1265
1289
 
1266
1290
  export {
1291
+ LATENCY_TIER_MS,
1292
+ latencyTierOf,
1267
1293
  ALIASES,
1268
1294
  _setProfileBrainHook,
1269
1295
  getProfile,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  tryGetProfile
3
- } from "./chunk-ZHUD3I52.mjs";
3
+ } from "./chunk-PKOFXEB3.mjs";
4
4
 
5
5
  // src/brain-query.ts
6
6
  var FRESH_SNAPSHOT = {
@@ -610,6 +610,14 @@ var PROFILES_RAW = [
610
610
  ],
611
611
  strengths: ["cost", "1m_context", "json_output", "code", "reasoning"],
612
612
  weaknesses: ["parallel_tools", "large_tool_sets"],
613
+ // alpha.47 — explicit slow override. Tag derivation would say 'medium'
614
+ // (no 'latency' weakness, no 'speed' strength), but the alpha.46 shadow
615
+ // probe MEASURED deepseek-v4-flash at 20485ms served (2026-06-03) — ~2.3×
616
+ // gemini-2.5-flash on PB's synchronous /api/analyze path. The 'flash' name
617
+ // is DeepSeek's, not a speed promise. This is the row that, scoring 0.85
618
+ // baseQuality (it carries 'reasoning') with no latency counterweight,
619
+ // leapfrogged gemini-2.5-flash as PB's summarize leader once reachable.
620
+ latencyTier: "slow",
613
621
  notes: "Cheap workhorse. 1M context, 384k max output. Cache-hit input $0.0028/M (1/50\xD7 of miss). Aliased as `deepseek-chat` (non-thinking) and `deepseek-reasoner` (thinking) \u2014 see ALIASES.",
614
622
  // Master plan §6.2 anchor. Brain-validated tier 1 cross-provider for
615
623
  // classify (169 rows, 0% empty). Tier 0 for summarize-with-no-tools.
@@ -677,6 +685,11 @@ var PROFILES_RAW = [
677
685
  ],
678
686
  strengths: ["quality", "reasoning", "1m_context", "json_output", "code", "extended_thinking"],
679
687
  weaknesses: ["parallel_tools", "large_tool_sets"],
688
+ // alpha.47 — explicit slow override. Measured 47722ms on the alpha.46
689
+ // shadow probe (2026-06-03) — it's an extended-thinking reasoner, slowest
690
+ // of the served set. Tag derivation would say 'medium'; the measurement says
691
+ // otherwise.
692
+ latencyTier: "slow",
680
693
  notes: "Pro tier. 1M context, 384k max output. Regular pricing $1.74/$3.48; 75% promo through 2026-05-31 ($0.435/$0.87). Default mode = thinking.",
681
694
  // Master plan §3.3: tier 3 cross-provider for plan chain. Reasoning
682
695
  // bumped one notch over V4-Flash; same parallel-tool ceiling.
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ARCHETYPE_FLOOR_DEFAULT,
3
3
  getDefaultFallbackChain
4
- } from "../chunk-6MSJQCAP.mjs";
4
+ } from "../chunk-SAWTKMD4.mjs";
5
5
  import {
6
6
  tryGetProfile
7
- } from "../chunk-ZHUD3I52.mjs";
7
+ } from "../chunk-PKOFXEB3.mjs";
8
8
  import {
9
9
  subscribe,
10
10
  subscribeApp
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { C as CompilePolicy, N as NormalizedResponse, A as ApiKeys, P as ProviderOverrides, a as CompiledRequest, b as PromptIR, c as CallOptions, d as CallResult, S as SystemModelMessage, e as CompileResult, f as SectionRewrite, R as RecordInput, g as RecordOutcomeInput, O as OutcomeResult, h as OracleScore, B as BestPracticeAdvisory, i as Adapter, j as PerAxisMetrics, k as Provider, l as ChainEntry, G as Grounding } from './ir-BBKgsUX7.mjs';
2
2
  export { m as CallAttempt, n as CallError, o as ChainModelEntry, p as ChainWithGrounding, q as Constraints, F as FallbackReason, H as HistoryCachePolicy, I as IntentDeclaration, M as Message, r as MutationApplied, s as NormalizedTokens, t as OutcomeKind, u as PerAxisMetricsByModel, v as PromptSection, w as SectionKind, x as ShadowProbeConfig, T as ToolCall, y as ToolDefinition } from './ir-BBKgsUX7.mjs';
3
3
  import { ModelProfile, ArchetypeConvention } from './profiles.mjs';
4
- export { ALIASES, CacheStrategy, CliffRule, LoweringSpec, RecoveryRule, StructuredOutputCapability, SystemPromptMode, allProfiles, getProfile, profilesByProvider, tryGetProfile } from './profiles.mjs';
4
+ export { ALIASES, CacheStrategy, CliffRule, LATENCY_TIER_MS, LatencyTier, LoweringSpec, RecoveryRule, StructuredOutputCapability, SystemPromptMode, allProfiles, getProfile, latencyTierOf, profilesByProvider, tryGetProfile } from './profiles.mjs';
5
5
  import { IntentArchetypeName } from './dialect.mjs';
6
6
  export { ALL_ARCHETYPES, ContextBucket, DIALECT_VERSION, HistoryDepth, INTENT_ARCHETYPES, OutputMode, ShapeSignature, ToolCountBucket, bucketContext, bucketHistory, bucketToolCount, hashShape, isArchetype, learningKey } from './dialect.mjs';
7
7
 
@@ -102,6 +102,70 @@ declare function execute(request: CompiledRequest, opts?: ExecuteOptions): Promi
102
102
  * exhausted without success.
103
103
  */
104
104
  declare function call(ir: PromptIR, opts?: CallOptions): Promise<CallResult>;
105
+ /** Served-side result the AI-SDK consumer already holds from streamText. */
106
+ interface ProbeShadowServed {
107
+ /** Model id that actually served the user (becomes `current_model` on the row). */
108
+ model: string;
109
+ /** The served response text (stored as a preview; never the prompt). */
110
+ responseText: string;
111
+ /** Served prompt tokens (AI SDK `usage.promptTokens`). */
112
+ tokensIn: number;
113
+ /** Served completion tokens (AI SDK `usage.completionTokens`). */
114
+ tokensOut: number;
115
+ /** The user's actual wait in ms (alpha.46 served-latency axis). */
116
+ latencyMs: number;
117
+ }
118
+ /** Options for the standalone {@link probeShadow} export. */
119
+ interface ProbeShadowOptions {
120
+ /** The served leg the consumer supplies; kgauto runs the candidate leg. */
121
+ served: ProbeShadowServed;
122
+ /** Model id(s)/family alias(es) to shadow-test against the served model. */
123
+ candidates: string | string[];
124
+ /** Probability [0,1] a given call fires a probe. Default 0.05; pin 1 for dogfood. */
125
+ sampleRate?: number;
126
+ /** Phase 1 = 'off' (offline rollup). 'opus' inline judge is Phase 2. Default 'off'. */
127
+ judge?: 'off' | 'opus';
128
+ /** Override API keys (defaults: process.env). Gates candidate reachability. */
129
+ apiKeys?: ApiKeys;
130
+ /** Override fetch (for tests / custom transport). */
131
+ fetchImpl?: typeof fetch;
132
+ /** Provider-specific request fields shallow-merged into the candidate request. */
133
+ providerOverrides?: ProviderOverrides;
134
+ /** Forwarded to compile() for the candidate run (match the served-side compile). */
135
+ policy?: CompilePolicy;
136
+ toolRelevanceThreshold?: number;
137
+ compressHistoryAfter?: number;
138
+ }
139
+ /**
140
+ * s54 — standalone full-IR shadow probe for AI-SDK consumers (tt-intel hunt, IC
141
+ * chat) that own their own streamText round-trip and never call call(). Fire it
142
+ * from the streamText `onFinish` callback, wrapped in the platform's
143
+ * `waitUntil()` / `after()`, so it runs after the response is flushed — no
144
+ * blocking, no `sync:true`, no teardown race (cleaner than call()'s in-process
145
+ * fire-and-forget for Edge runtimes; L-086).
146
+ *
147
+ * Runs the SAME candidate loop as the call()-inline probe ({@link
148
+ * runProbeCandidates}): same gates (sample, self-skip, G4 judge-self,
149
+ * reachability, G6), same fidelity=1.0 full-IR rows, same alpha.46 latency
150
+ * capture. The consumer supplies the served leg it already has; kgauto runs the
151
+ * candidate(s) and writes the `probe_outcomes` row.
152
+ *
153
+ * Requires `configureBrain()` to have been called (same as call()).
154
+ * Fire-and-forget: never throws into the caller.
155
+ *
156
+ * @example
157
+ * const result = streamText({ model, system, messages,
158
+ * onFinish: ({ text, usage }) => {
159
+ * after(() => probeShadow(ir, {
160
+ * served: { model: servedModelId, responseText: text,
161
+ * tokensIn: usage.promptTokens, tokensOut: usage.completionTokens,
162
+ * latencyMs: Date.now() - t0 },
163
+ * candidates: ['deepseek-v4-pro'], sampleRate: 0.05,
164
+ * }));
165
+ * },
166
+ * });
167
+ */
168
+ declare function probeShadow(ir: PromptIR, opts: ProbeShadowOptions): Promise<void>;
105
169
 
106
170
  /**
107
171
  * alpha.33 — AI-SDK streamText helpers.
@@ -2195,4 +2259,4 @@ declare function markPromoteReadyHandled(opts: MarkPromoteReadyHandledOptions):
2195
2259
  */
2196
2260
  declare function compile(ir: PromptIR, opts?: CompileOptions): CompileResult;
2197
2261
 
2198
- export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FAMILY_FITS, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, ArchetypeConvention, type ArchetypeFamilyFit, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, type BrainReadEnv, 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, FamilyResolutionError, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, type GetRecommendedPrimaryOptions, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type MarkExclusionFindingHandledOptions, type MarkPromoteReadyHandledOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PRODUCER_OWNED_RULE_CODES, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProfileToRowOptions, type PromoteReadyFindingRow, type PromoteReadyResolution, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type ShadowProbeRecordInput, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applyArchetypeConvention, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, buildShadowProbeRow, call, clearBrain, compile, configureBrain, countTokens, deriveFamilyFromModelId, deriveOwnership, execute, findBetterFit, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getRecommendedPrimary, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStaleExclusionFindings, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isBrainSync, isExclusionFindingsBrainActive, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, markExclusionFindingHandled, markPromoteReadyHandled, profileToRow, readBrainReadEnv, record, recordOutcome, recordShadowProbe, resetTokenizer, resolveConventionsForProfile, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
2262
+ export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FAMILY_FITS, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, ArchetypeConvention, type ArchetypeFamilyFit, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, type BrainReadEnv, 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, FamilyResolutionError, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, type GetRecommendedPrimaryOptions, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type MarkExclusionFindingHandledOptions, type MarkPromoteReadyHandledOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PRODUCER_OWNED_RULE_CODES, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProbeShadowOptions, type ProbeShadowServed, type ProfileToRowOptions, type PromoteReadyFindingRow, type PromoteReadyResolution, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type ShadowProbeRecordInput, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applyArchetypeConvention, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, buildShadowProbeRow, call, clearBrain, compile, configureBrain, countTokens, deriveFamilyFromModelId, deriveOwnership, execute, findBetterFit, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getRecommendedPrimary, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStaleExclusionFindings, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isBrainSync, isExclusionFindingsBrainActive, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, markExclusionFindingHandled, markPromoteReadyHandled, probeShadow, profileToRow, readBrainReadEnv, record, recordOutcome, recordShadowProbe, resetTokenizer, resolveConventionsForProfile, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { C as CompilePolicy, N as NormalizedResponse, A as ApiKeys, P as ProviderOverrides, a as CompiledRequest, b as PromptIR, c as CallOptions, d as CallResult, S as SystemModelMessage, e as CompileResult, f as SectionRewrite, R as RecordInput, g as RecordOutcomeInput, O as OutcomeResult, h as OracleScore, B as BestPracticeAdvisory, i as Adapter, j as PerAxisMetrics, k as Provider, l as ChainEntry, G as Grounding } from './ir-D3n-pBYI.js';
2
2
  export { m as CallAttempt, n as CallError, o as ChainModelEntry, p as ChainWithGrounding, q as Constraints, F as FallbackReason, H as HistoryCachePolicy, I as IntentDeclaration, M as Message, r as MutationApplied, s as NormalizedTokens, t as OutcomeKind, u as PerAxisMetricsByModel, v as PromptSection, w as SectionKind, x as ShadowProbeConfig, T as ToolCall, y as ToolDefinition } from './ir-D3n-pBYI.js';
3
3
  import { ModelProfile, ArchetypeConvention } from './profiles.js';
4
- export { ALIASES, CacheStrategy, CliffRule, LoweringSpec, RecoveryRule, StructuredOutputCapability, SystemPromptMode, allProfiles, getProfile, profilesByProvider, tryGetProfile } from './profiles.js';
4
+ export { ALIASES, CacheStrategy, CliffRule, LATENCY_TIER_MS, LatencyTier, LoweringSpec, RecoveryRule, StructuredOutputCapability, SystemPromptMode, allProfiles, getProfile, latencyTierOf, profilesByProvider, tryGetProfile } from './profiles.js';
5
5
  import { IntentArchetypeName } from './dialect.js';
6
6
  export { ALL_ARCHETYPES, ContextBucket, DIALECT_VERSION, HistoryDepth, INTENT_ARCHETYPES, OutputMode, ShapeSignature, ToolCountBucket, bucketContext, bucketHistory, bucketToolCount, hashShape, isArchetype, learningKey } from './dialect.js';
7
7
 
@@ -102,6 +102,70 @@ declare function execute(request: CompiledRequest, opts?: ExecuteOptions): Promi
102
102
  * exhausted without success.
103
103
  */
104
104
  declare function call(ir: PromptIR, opts?: CallOptions): Promise<CallResult>;
105
+ /** Served-side result the AI-SDK consumer already holds from streamText. */
106
+ interface ProbeShadowServed {
107
+ /** Model id that actually served the user (becomes `current_model` on the row). */
108
+ model: string;
109
+ /** The served response text (stored as a preview; never the prompt). */
110
+ responseText: string;
111
+ /** Served prompt tokens (AI SDK `usage.promptTokens`). */
112
+ tokensIn: number;
113
+ /** Served completion tokens (AI SDK `usage.completionTokens`). */
114
+ tokensOut: number;
115
+ /** The user's actual wait in ms (alpha.46 served-latency axis). */
116
+ latencyMs: number;
117
+ }
118
+ /** Options for the standalone {@link probeShadow} export. */
119
+ interface ProbeShadowOptions {
120
+ /** The served leg the consumer supplies; kgauto runs the candidate leg. */
121
+ served: ProbeShadowServed;
122
+ /** Model id(s)/family alias(es) to shadow-test against the served model. */
123
+ candidates: string | string[];
124
+ /** Probability [0,1] a given call fires a probe. Default 0.05; pin 1 for dogfood. */
125
+ sampleRate?: number;
126
+ /** Phase 1 = 'off' (offline rollup). 'opus' inline judge is Phase 2. Default 'off'. */
127
+ judge?: 'off' | 'opus';
128
+ /** Override API keys (defaults: process.env). Gates candidate reachability. */
129
+ apiKeys?: ApiKeys;
130
+ /** Override fetch (for tests / custom transport). */
131
+ fetchImpl?: typeof fetch;
132
+ /** Provider-specific request fields shallow-merged into the candidate request. */
133
+ providerOverrides?: ProviderOverrides;
134
+ /** Forwarded to compile() for the candidate run (match the served-side compile). */
135
+ policy?: CompilePolicy;
136
+ toolRelevanceThreshold?: number;
137
+ compressHistoryAfter?: number;
138
+ }
139
+ /**
140
+ * s54 — standalone full-IR shadow probe for AI-SDK consumers (tt-intel hunt, IC
141
+ * chat) that own their own streamText round-trip and never call call(). Fire it
142
+ * from the streamText `onFinish` callback, wrapped in the platform's
143
+ * `waitUntil()` / `after()`, so it runs after the response is flushed — no
144
+ * blocking, no `sync:true`, no teardown race (cleaner than call()'s in-process
145
+ * fire-and-forget for Edge runtimes; L-086).
146
+ *
147
+ * Runs the SAME candidate loop as the call()-inline probe ({@link
148
+ * runProbeCandidates}): same gates (sample, self-skip, G4 judge-self,
149
+ * reachability, G6), same fidelity=1.0 full-IR rows, same alpha.46 latency
150
+ * capture. The consumer supplies the served leg it already has; kgauto runs the
151
+ * candidate(s) and writes the `probe_outcomes` row.
152
+ *
153
+ * Requires `configureBrain()` to have been called (same as call()).
154
+ * Fire-and-forget: never throws into the caller.
155
+ *
156
+ * @example
157
+ * const result = streamText({ model, system, messages,
158
+ * onFinish: ({ text, usage }) => {
159
+ * after(() => probeShadow(ir, {
160
+ * served: { model: servedModelId, responseText: text,
161
+ * tokensIn: usage.promptTokens, tokensOut: usage.completionTokens,
162
+ * latencyMs: Date.now() - t0 },
163
+ * candidates: ['deepseek-v4-pro'], sampleRate: 0.05,
164
+ * }));
165
+ * },
166
+ * });
167
+ */
168
+ declare function probeShadow(ir: PromptIR, opts: ProbeShadowOptions): Promise<void>;
105
169
 
106
170
  /**
107
171
  * alpha.33 — AI-SDK streamText helpers.
@@ -2195,4 +2259,4 @@ declare function markPromoteReadyHandled(opts: MarkPromoteReadyHandledOptions):
2195
2259
  */
2196
2260
  declare function compile(ir: PromptIR, opts?: CompileOptions): CompileResult;
2197
2261
 
2198
- export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FAMILY_FITS, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, ArchetypeConvention, type ArchetypeFamilyFit, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, type BrainReadEnv, 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, FamilyResolutionError, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, type GetRecommendedPrimaryOptions, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type MarkExclusionFindingHandledOptions, type MarkPromoteReadyHandledOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PRODUCER_OWNED_RULE_CODES, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProfileToRowOptions, type PromoteReadyFindingRow, type PromoteReadyResolution, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type ShadowProbeRecordInput, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applyArchetypeConvention, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, buildShadowProbeRow, call, clearBrain, compile, configureBrain, countTokens, deriveFamilyFromModelId, deriveOwnership, execute, findBetterFit, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getRecommendedPrimary, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStaleExclusionFindings, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isBrainSync, isExclusionFindingsBrainActive, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, markExclusionFindingHandled, markPromoteReadyHandled, profileToRow, readBrainReadEnv, record, recordOutcome, recordShadowProbe, resetTokenizer, resolveConventionsForProfile, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
2262
+ export { ABSOLUTE_FLOOR, type AISDKConvertedMessage, ARCHETYPE_FAMILY_FITS, ARCHETYPE_FLOOR_DEFAULT, type ActionableAdvisory, Adapter, type AdvisoryResolutionSource, type AdvisorySeverity, type AdvisoryStatus, type AdvisorySuggestedFix, ApiKeys, type AppOracle, type ApplySectionRewritesArgs, type ApplySectionRewritesResult, ArchetypeConvention, type ArchetypeFamilyFit, type ArchetypePerfMap, type ArchetypePerfNMap, type ArchetypePerfScoreResult, type AttachCacheControlResult, BestPracticeAdvisory, type BrainConfig, type BrainQueryConfig, type BrainReadEnv, 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, FamilyResolutionError, type GetActionableAdvisoriesOptions, type GetDefaultFallbackChainOpts, type GetPerAxisMetricsOpts, type GetRecommendedPrimaryOptions, Grounding, IntentArchetypeName, type LLMJudgeOptions, MEASURED_GROUNDING_MIN_N, type MarkAdvisoryResolvedOptions, type MarkExclusionFindingHandledOptions, type MarkPromoteReadyHandledOptions, type ModelBrainRow, type ModelCompatibility, ModelProfile, NormalizedResponse, type OracleContext, OracleScore, type OutcomePayload, OutcomeResult, PRODUCER_OWNED_RULE_CODES, PROVIDER_ENV_KEYS, PerAxisMetrics, type PricingRow, type ProbeShadowOptions, type ProbeShadowServed, type ProfileToRowOptions, type PromoteReadyFindingRow, type PromoteReadyResolution, PromptIR, Provider, ProviderOverrides, type ProviderReachability, RULE_SEQUENTIAL_TOOL_CLIFF, type ReachabilityOpts, RecordInput, RecordOutcomeInput, type RunAdvisorPhase2Context, SectionRewrite, type ShadowProbeRecordInput, type SupportedProvider, SystemModelMessage, TRANSLATOR_FLOOR, applyArchetypeConvention, applySectionRewrites, attachCacheControlToStreamTextInput, buildLLMJudge, buildShadowProbeRow, call, clearBrain, compile, configureBrain, countTokens, deriveFamilyFromModelId, deriveOwnership, execute, findBetterFit, getActionableAdvisories, getAllStarterChains, getAllStarterChainsWithGrounding, getArchetypePerfScore, getDefaultFallbackChain, getDefaultFallbackChainWithGrounding, getModelCompatibility, getPerAxisMetrics, getReachabilityDiagnostic, getRecommendedPrimary, getSequentialStarterChain, getSequentialStarterChainWithGrounding, getStaleExclusionFindings, getStarterChain, getStarterChainWithGrounding, isBrainQueryActiveFor, isBrainSync, isExclusionFindingsBrainActive, isModelReachable, isProviderReachable, loadAliasesFromBrain, loadArchetypePerfFromBrain, loadArchetypePerfNFromBrain, loadChainsFromBrain, loadModelsFromBrain, loadPricingFromBrain, markAdvisoryResolved, markExclusionFindingHandled, markPromoteReadyHandled, probeShadow, profileToRow, readBrainReadEnv, record, recordOutcome, recordShadowProbe, resetTokenizer, resolveConventionsForProfile, resolvePricingAt, resolveProviderKey, runAdvisor, setTokenizer };
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  DIALECT_VERSION: () => DIALECT_VERSION,
31
31
  FamilyResolutionError: () => FamilyResolutionError,
32
32
  INTENT_ARCHETYPES: () => INTENT_ARCHETYPES,
33
+ LATENCY_TIER_MS: () => LATENCY_TIER_MS,
33
34
  MEASURED_GROUNDING_MIN_N: () => MEASURED_GROUNDING_MIN_N,
34
35
  PRODUCER_OWNED_RULE_CODES: () => PRODUCER_OWNED_RULE_CODES,
35
36
  PROVIDER_ENV_KEYS: () => PROVIDER_ENV_KEYS,
@@ -76,6 +77,7 @@ __export(index_exports, {
76
77
  isExclusionFindingsBrainActive: () => isExclusionFindingsBrainActive,
77
78
  isModelReachable: () => isModelReachable,
78
79
  isProviderReachable: () => isProviderReachable,
80
+ latencyTierOf: () => latencyTierOf,
79
81
  learningKey: () => learningKey,
80
82
  loadAliasesFromBrain: () => loadAliasesFromBrain,
81
83
  loadArchetypePerfFromBrain: () => loadArchetypePerfFromBrain,
@@ -86,6 +88,7 @@ __export(index_exports, {
86
88
  markAdvisoryResolved: () => markAdvisoryResolved,
87
89
  markExclusionFindingHandled: () => markExclusionFindingHandled,
88
90
  markPromoteReadyHandled: () => markPromoteReadyHandled,
91
+ probeShadow: () => probeShadow,
89
92
  profileToRow: () => profileToRow,
90
93
  profilesByProvider: () => profilesByProvider,
91
94
  readBrainReadEnv: () => readBrainReadEnv,
@@ -274,6 +277,17 @@ function mapPerAxisMetrics(raw, fallbackAppId, fallbackArchetype, fallbackModel,
274
277
  }
275
278
 
276
279
  // src/profiles.ts
280
+ var LATENCY_TIER_MS = {
281
+ fast: 4e3,
282
+ medium: 11e3,
283
+ slow: 24e3
284
+ };
285
+ function latencyTierOf(profile) {
286
+ if (profile.latencyTier) return profile.latencyTier;
287
+ if (profile.weaknesses.includes("latency")) return "slow";
288
+ if (profile.strengths.includes("speed")) return "fast";
289
+ return "medium";
290
+ }
277
291
  var ANTHROPIC_LOWERING_BASE = {
278
292
  system: { mode: "inline" },
279
293
  cache: {
@@ -820,6 +834,14 @@ var PROFILES_RAW = [
820
834
  ],
821
835
  strengths: ["cost", "1m_context", "json_output", "code", "reasoning"],
822
836
  weaknesses: ["parallel_tools", "large_tool_sets"],
837
+ // alpha.47 — explicit slow override. Tag derivation would say 'medium'
838
+ // (no 'latency' weakness, no 'speed' strength), but the alpha.46 shadow
839
+ // probe MEASURED deepseek-v4-flash at 20485ms served (2026-06-03) — ~2.3×
840
+ // gemini-2.5-flash on PB's synchronous /api/analyze path. The 'flash' name
841
+ // is DeepSeek's, not a speed promise. This is the row that, scoring 0.85
842
+ // baseQuality (it carries 'reasoning') with no latency counterweight,
843
+ // leapfrogged gemini-2.5-flash as PB's summarize leader once reachable.
844
+ latencyTier: "slow",
823
845
  notes: "Cheap workhorse. 1M context, 384k max output. Cache-hit input $0.0028/M (1/50\xD7 of miss). Aliased as `deepseek-chat` (non-thinking) and `deepseek-reasoner` (thinking) \u2014 see ALIASES.",
824
846
  // Master plan §6.2 anchor. Brain-validated tier 1 cross-provider for
825
847
  // classify (169 rows, 0% empty). Tier 0 for summarize-with-no-tools.
@@ -887,6 +909,11 @@ var PROFILES_RAW = [
887
909
  ],
888
910
  strengths: ["quality", "reasoning", "1m_context", "json_output", "code", "extended_thinking"],
889
911
  weaknesses: ["parallel_tools", "large_tool_sets"],
912
+ // alpha.47 — explicit slow override. Measured 47722ms on the alpha.46
913
+ // shadow probe (2026-06-03) — it's an extended-thinking reasoner, slowest
914
+ // of the served set. Tag derivation would say 'medium'; the measurement says
915
+ // otherwise.
916
+ latencyTier: "slow",
890
917
  notes: "Pro tier. 1M context, 384k max output. Regular pricing $1.74/$3.48; 75% promo through 2026-05-31 ($0.435/$0.87). Default mode = thinking.",
891
918
  // Master plan §3.3: tier 3 cross-provider for plan chain. Reasoning
892
919
  // bumped one notch over V4-Flash; same parallel-tool ceiling.
@@ -2360,6 +2387,8 @@ function passApplyCliffs(ir, profile, estimatedInputTokens) {
2360
2387
  }
2361
2388
  return { value: { ir: nextIR, loweringHints: hints }, mutations };
2362
2389
  }
2390
+ var LATENCY_OVERAGE_WEIGHT = 0.6;
2391
+ var LATENCY_PENALTY_CAP = 1;
2363
2392
  function passScoreTargets(ir, opts) {
2364
2393
  const constraints = ir.constraints ?? {};
2365
2394
  const policy = opts.policy ?? {};
@@ -2415,7 +2444,16 @@ function passScoreTargets(ir, opts) {
2415
2444
  const callerOrderBoost = (modelIds.length - modelIds.indexOf(modelId)) * 0.1;
2416
2445
  const costPenalty = estimatedCostUsd * 5;
2417
2446
  const preferredBoost = preferredSet.has(modelId) ? 0.5 : 0;
2418
- const rank = qualityScore + callerOrderBoost - costPenalty - reasons.length * 10 + preferredBoost;
2447
+ let latencyPenalty = 0;
2448
+ const maxLatencyMs = constraints.maxLatencyMs;
2449
+ if (typeof maxLatencyMs === "number" && maxLatencyMs > 0) {
2450
+ const tierMs = LATENCY_TIER_MS[latencyTierOf(profile)];
2451
+ if (tierMs > maxLatencyMs) {
2452
+ const overage = (tierMs - maxLatencyMs) / maxLatencyMs;
2453
+ latencyPenalty = Math.min(LATENCY_OVERAGE_WEIGHT * overage, LATENCY_PENALTY_CAP);
2454
+ }
2455
+ }
2456
+ const rank = qualityScore + callerOrderBoost - costPenalty - reasons.length * 10 + preferredBoost - latencyPenalty;
2419
2457
  scores.push({
2420
2458
  modelId,
2421
2459
  estimatedCostUsd,
@@ -2448,6 +2486,15 @@ function passScoreTargets(ir, opts) {
2448
2486
  description: `Model ${modelId} rank boosted by CompilePolicy.preferredModels`
2449
2487
  });
2450
2488
  }
2489
+ if (latencyPenalty > 0) {
2490
+ const tier = latencyTierOf(profile);
2491
+ policyMutations.push({
2492
+ id: `latency-downrank-${modelId}`,
2493
+ source: "latency_guard",
2494
+ passName: "score_targets",
2495
+ description: `Model ${modelId} rank down ${latencyPenalty.toFixed(2)} \u2014 latency tier '${tier}' (~${LATENCY_TIER_MS[tier]}ms) exceeds constraints.maxLatencyMs (${maxLatencyMs}ms)`
2496
+ });
2497
+ }
2451
2498
  }
2452
2499
  return { value: scores, mutations: policyMutations };
2453
2500
  }
@@ -6111,16 +6158,15 @@ function hashForProbe(s) {
6111
6158
  for (let i = 0; i < s.length; i++) h = (h << 5) - h + s.charCodeAt(i) | 0;
6112
6159
  return `h${(h >>> 0).toString(16)}`;
6113
6160
  }
6114
- async function runShadowProbe(args) {
6161
+ async function runProbeCandidates(args) {
6115
6162
  try {
6116
- const cfg = args.opts.shadowProbe;
6117
- if (!shouldSampleProbe(cfg.sampleRate ?? 0.05)) return;
6118
- const candidates = normalizeProbeCandidates(cfg.candidates);
6163
+ if (!shouldSampleProbe(args.cfg.sampleRate ?? 0.05)) return;
6164
+ const candidates = normalizeProbeCandidates(args.cfg.candidates);
6119
6165
  const promptHash = hashForProbe(args.ir.currentTurn?.content ?? "");
6120
6166
  for (const candidate of candidates) {
6121
6167
  if (candidate === args.servedModel) continue;
6122
6168
  if (deriveFamilyFromModelId(candidate) === "claude-opus") continue;
6123
- if (!isModelReachable(candidate, { apiKeys: args.opts.apiKeys })) continue;
6169
+ if (!isModelReachable(candidate, { apiKeys: args.exec.apiKeys })) continue;
6124
6170
  try {
6125
6171
  const candCompile = compileAndRegister(
6126
6172
  {
@@ -6128,13 +6174,13 @@ async function runShadowProbe(args) {
6128
6174
  models: args.ir.models.includes(candidate) ? args.ir.models : [candidate, ...args.ir.models],
6129
6175
  constraints: { ...args.ir.constraints ?? {}, forceModel: candidate }
6130
6176
  },
6131
- args.opts
6177
+ args.exec
6132
6178
  );
6133
6179
  const candStart = Date.now();
6134
6180
  const exec = await execute(candCompile.request, {
6135
- apiKeys: args.opts.apiKeys,
6136
- fetchImpl: args.opts.fetchImpl,
6137
- providerOverrides: args.opts.providerOverrides
6181
+ apiKeys: args.exec.apiKeys,
6182
+ fetchImpl: args.exec.fetchImpl,
6183
+ providerOverrides: args.exec.providerOverrides
6138
6184
  });
6139
6185
  const candidateLatencyMs = Date.now() - candStart;
6140
6186
  if (!exec.ok) continue;
@@ -6163,6 +6209,54 @@ async function runShadowProbe(args) {
6163
6209
  } catch {
6164
6210
  }
6165
6211
  }
6212
+ async function runShadowProbe(args) {
6213
+ const cfg = args.opts.shadowProbe;
6214
+ if (!cfg) return;
6215
+ await runProbeCandidates({
6216
+ ir: args.ir,
6217
+ cfg,
6218
+ servedModel: args.servedModel,
6219
+ servedResponse: args.servedResponse,
6220
+ servedLatencyMs: args.servedLatencyMs,
6221
+ // CallOptions is a structural superset of ProbeExecOpts.
6222
+ exec: args.opts
6223
+ });
6224
+ }
6225
+ async function probeShadow(ir, opts) {
6226
+ try {
6227
+ const servedResponse = {
6228
+ text: opts.served.responseText,
6229
+ structuredOutput: null,
6230
+ toolCalls: [],
6231
+ tokens: {
6232
+ input: opts.served.tokensIn,
6233
+ output: opts.served.tokensOut,
6234
+ total: opts.served.tokensIn + opts.served.tokensOut
6235
+ },
6236
+ raw: null
6237
+ };
6238
+ await runProbeCandidates({
6239
+ ir,
6240
+ cfg: {
6241
+ candidates: opts.candidates,
6242
+ sampleRate: opts.sampleRate,
6243
+ judge: opts.judge ?? "off"
6244
+ },
6245
+ servedModel: opts.served.model,
6246
+ servedResponse,
6247
+ servedLatencyMs: opts.served.latencyMs,
6248
+ exec: {
6249
+ policy: opts.policy,
6250
+ toolRelevanceThreshold: opts.toolRelevanceThreshold,
6251
+ compressHistoryAfter: opts.compressHistoryAfter,
6252
+ apiKeys: opts.apiKeys,
6253
+ fetchImpl: opts.fetchImpl,
6254
+ providerOverrides: opts.providerOverrides
6255
+ }
6256
+ });
6257
+ } catch {
6258
+ }
6259
+ }
6166
6260
  function validateStructuredContract(exec, ir) {
6167
6261
  if (!ir.constraints?.structuredOutput) {
6168
6262
  return { ok: true, response: exec.response };
@@ -6691,6 +6785,7 @@ function compile2(ir, opts) {
6691
6785
  DIALECT_VERSION,
6692
6786
  FamilyResolutionError,
6693
6787
  INTENT_ARCHETYPES,
6788
+ LATENCY_TIER_MS,
6694
6789
  MEASURED_GROUNDING_MIN_N,
6695
6790
  PRODUCER_OWNED_RULE_CODES,
6696
6791
  PROVIDER_ENV_KEYS,
@@ -6737,6 +6832,7 @@ function compile2(ir, opts) {
6737
6832
  isExclusionFindingsBrainActive,
6738
6833
  isModelReachable,
6739
6834
  isProviderReachable,
6835
+ latencyTierOf,
6740
6836
  learningKey,
6741
6837
  loadAliasesFromBrain,
6742
6838
  loadArchetypePerfFromBrain,
@@ -6747,6 +6843,7 @@ function compile2(ir, opts) {
6747
6843
  markAdvisoryResolved,
6748
6844
  markExclusionFindingHandled,
6749
6845
  markPromoteReadyHandled,
6846
+ probeShadow,
6750
6847
  profileToRow,
6751
6848
  profilesByProvider,
6752
6849
  readBrainReadEnv,
package/dist/index.mjs CHANGED
@@ -33,16 +33,18 @@ import {
33
33
  loadChainsFromBrain,
34
34
  readBrainReadEnv,
35
35
  resolveProviderKey
36
- } from "./chunk-6MSJQCAP.mjs";
36
+ } from "./chunk-SAWTKMD4.mjs";
37
37
  import {
38
38
  ALIASES,
39
+ LATENCY_TIER_MS,
39
40
  _setProfileBrainHook,
40
41
  allProfiles,
41
42
  allProfilesRaw,
42
43
  getProfile,
44
+ latencyTierOf,
43
45
  profilesByProvider,
44
46
  tryGetProfile
45
- } from "./chunk-ZHUD3I52.mjs";
47
+ } from "./chunk-PKOFXEB3.mjs";
46
48
  import {
47
49
  emitAdvisoryFired,
48
50
  emitCompileDone,
@@ -710,6 +712,8 @@ function passApplyCliffs(ir, profile, estimatedInputTokens) {
710
712
  }
711
713
  return { value: { ir: nextIR, loweringHints: hints }, mutations };
712
714
  }
715
+ var LATENCY_OVERAGE_WEIGHT = 0.6;
716
+ var LATENCY_PENALTY_CAP = 1;
713
717
  function passScoreTargets(ir, opts) {
714
718
  const constraints = ir.constraints ?? {};
715
719
  const policy = opts.policy ?? {};
@@ -765,7 +769,16 @@ function passScoreTargets(ir, opts) {
765
769
  const callerOrderBoost = (modelIds.length - modelIds.indexOf(modelId)) * 0.1;
766
770
  const costPenalty = estimatedCostUsd * 5;
767
771
  const preferredBoost = preferredSet.has(modelId) ? 0.5 : 0;
768
- const rank = qualityScore + callerOrderBoost - costPenalty - reasons.length * 10 + preferredBoost;
772
+ let latencyPenalty = 0;
773
+ const maxLatencyMs = constraints.maxLatencyMs;
774
+ if (typeof maxLatencyMs === "number" && maxLatencyMs > 0) {
775
+ const tierMs = LATENCY_TIER_MS[latencyTierOf(profile)];
776
+ if (tierMs > maxLatencyMs) {
777
+ const overage = (tierMs - maxLatencyMs) / maxLatencyMs;
778
+ latencyPenalty = Math.min(LATENCY_OVERAGE_WEIGHT * overage, LATENCY_PENALTY_CAP);
779
+ }
780
+ }
781
+ const rank = qualityScore + callerOrderBoost - costPenalty - reasons.length * 10 + preferredBoost - latencyPenalty;
769
782
  scores.push({
770
783
  modelId,
771
784
  estimatedCostUsd,
@@ -798,6 +811,15 @@ function passScoreTargets(ir, opts) {
798
811
  description: `Model ${modelId} rank boosted by CompilePolicy.preferredModels`
799
812
  });
800
813
  }
814
+ if (latencyPenalty > 0) {
815
+ const tier = latencyTierOf(profile);
816
+ policyMutations.push({
817
+ id: `latency-downrank-${modelId}`,
818
+ source: "latency_guard",
819
+ passName: "score_targets",
820
+ description: `Model ${modelId} rank down ${latencyPenalty.toFixed(2)} \u2014 latency tier '${tier}' (~${LATENCY_TIER_MS[tier]}ms) exceeds constraints.maxLatencyMs (${maxLatencyMs}ms)`
821
+ });
822
+ }
801
823
  }
802
824
  return { value: scores, mutations: policyMutations };
803
825
  }
@@ -3746,16 +3768,15 @@ function hashForProbe(s) {
3746
3768
  for (let i = 0; i < s.length; i++) h = (h << 5) - h + s.charCodeAt(i) | 0;
3747
3769
  return `h${(h >>> 0).toString(16)}`;
3748
3770
  }
3749
- async function runShadowProbe(args) {
3771
+ async function runProbeCandidates(args) {
3750
3772
  try {
3751
- const cfg = args.opts.shadowProbe;
3752
- if (!shouldSampleProbe(cfg.sampleRate ?? 0.05)) return;
3753
- const candidates = normalizeProbeCandidates(cfg.candidates);
3773
+ if (!shouldSampleProbe(args.cfg.sampleRate ?? 0.05)) return;
3774
+ const candidates = normalizeProbeCandidates(args.cfg.candidates);
3754
3775
  const promptHash = hashForProbe(args.ir.currentTurn?.content ?? "");
3755
3776
  for (const candidate of candidates) {
3756
3777
  if (candidate === args.servedModel) continue;
3757
3778
  if (deriveFamilyFromModelId(candidate) === "claude-opus") continue;
3758
- if (!isModelReachable(candidate, { apiKeys: args.opts.apiKeys })) continue;
3779
+ if (!isModelReachable(candidate, { apiKeys: args.exec.apiKeys })) continue;
3759
3780
  try {
3760
3781
  const candCompile = compileAndRegister(
3761
3782
  {
@@ -3763,13 +3784,13 @@ async function runShadowProbe(args) {
3763
3784
  models: args.ir.models.includes(candidate) ? args.ir.models : [candidate, ...args.ir.models],
3764
3785
  constraints: { ...args.ir.constraints ?? {}, forceModel: candidate }
3765
3786
  },
3766
- args.opts
3787
+ args.exec
3767
3788
  );
3768
3789
  const candStart = Date.now();
3769
3790
  const exec = await execute(candCompile.request, {
3770
- apiKeys: args.opts.apiKeys,
3771
- fetchImpl: args.opts.fetchImpl,
3772
- providerOverrides: args.opts.providerOverrides
3791
+ apiKeys: args.exec.apiKeys,
3792
+ fetchImpl: args.exec.fetchImpl,
3793
+ providerOverrides: args.exec.providerOverrides
3773
3794
  });
3774
3795
  const candidateLatencyMs = Date.now() - candStart;
3775
3796
  if (!exec.ok) continue;
@@ -3798,6 +3819,54 @@ async function runShadowProbe(args) {
3798
3819
  } catch {
3799
3820
  }
3800
3821
  }
3822
+ async function runShadowProbe(args) {
3823
+ const cfg = args.opts.shadowProbe;
3824
+ if (!cfg) return;
3825
+ await runProbeCandidates({
3826
+ ir: args.ir,
3827
+ cfg,
3828
+ servedModel: args.servedModel,
3829
+ servedResponse: args.servedResponse,
3830
+ servedLatencyMs: args.servedLatencyMs,
3831
+ // CallOptions is a structural superset of ProbeExecOpts.
3832
+ exec: args.opts
3833
+ });
3834
+ }
3835
+ async function probeShadow(ir, opts) {
3836
+ try {
3837
+ const servedResponse = {
3838
+ text: opts.served.responseText,
3839
+ structuredOutput: null,
3840
+ toolCalls: [],
3841
+ tokens: {
3842
+ input: opts.served.tokensIn,
3843
+ output: opts.served.tokensOut,
3844
+ total: opts.served.tokensIn + opts.served.tokensOut
3845
+ },
3846
+ raw: null
3847
+ };
3848
+ await runProbeCandidates({
3849
+ ir,
3850
+ cfg: {
3851
+ candidates: opts.candidates,
3852
+ sampleRate: opts.sampleRate,
3853
+ judge: opts.judge ?? "off"
3854
+ },
3855
+ servedModel: opts.served.model,
3856
+ servedResponse,
3857
+ servedLatencyMs: opts.served.latencyMs,
3858
+ exec: {
3859
+ policy: opts.policy,
3860
+ toolRelevanceThreshold: opts.toolRelevanceThreshold,
3861
+ compressHistoryAfter: opts.compressHistoryAfter,
3862
+ apiKeys: opts.apiKeys,
3863
+ fetchImpl: opts.fetchImpl,
3864
+ providerOverrides: opts.providerOverrides
3865
+ }
3866
+ });
3867
+ } catch {
3868
+ }
3869
+ }
3801
3870
  function validateStructuredContract(exec, ir) {
3802
3871
  if (!ir.constraints?.structuredOutput) {
3803
3872
  return { ok: true, response: exec.response };
@@ -4325,6 +4394,7 @@ export {
4325
4394
  DIALECT_VERSION,
4326
4395
  FamilyResolutionError,
4327
4396
  INTENT_ARCHETYPES,
4397
+ LATENCY_TIER_MS,
4328
4398
  MEASURED_GROUNDING_MIN_N,
4329
4399
  PRODUCER_OWNED_RULE_CODES,
4330
4400
  PROVIDER_ENV_KEYS,
@@ -4371,6 +4441,7 @@ export {
4371
4441
  isExclusionFindingsBrainActive,
4372
4442
  isModelReachable,
4373
4443
  isProviderReachable,
4444
+ latencyTierOf,
4374
4445
  learningKey,
4375
4446
  loadAliasesFromBrain,
4376
4447
  loadArchetypePerfFromBrain,
@@ -4381,6 +4452,7 @@ export {
4381
4452
  markAdvisoryResolved,
4382
4453
  markExclusionFindingHandled,
4383
4454
  markPromoteReadyHandled,
4455
+ probeShadow,
4384
4456
  profileToRow,
4385
4457
  profilesByProvider,
4386
4458
  readBrainReadEnv,
@@ -145,6 +145,14 @@ interface LoweringSpec {
145
145
  default?: number | 'auto' | 'off';
146
146
  };
147
147
  }
148
+ /**
149
+ * Coarse latency bucket for a model. alpha.47 — the third swap axis
150
+ * (cost / quality / SPEED). We bucket rather than store per-model ms because
151
+ * served latency varies with token count; honest precision is the tier, not a
152
+ * false-precise number. See {@link LATENCY_TIER_MS} for the representative ms
153
+ * each tier maps to when compared against `constraints.maxLatencyMs`.
154
+ */
155
+ type LatencyTier = 'fast' | 'medium' | 'slow';
148
156
  interface ModelProfile {
149
157
  id: string;
150
158
  provider: Provider;
@@ -163,6 +171,16 @@ interface ModelProfile {
163
171
  recovery: RecoveryRule[];
164
172
  strengths: string[];
165
173
  weaknesses: string[];
174
+ /**
175
+ * alpha.47 — explicit latency bucket. OPTIONAL: when unset, `latencyTierOf`
176
+ * derives it from the tags above (`weaknesses` includes `'latency'` → slow,
177
+ * `strengths` includes `'speed'` → fast, else medium). Set this explicitly
178
+ * ONLY when measured evidence contradicts the tag derivation — e.g.
179
+ * `deepseek-v4-flash` carries no `'latency'` weakness yet measures ~20s
180
+ * (alpha.46 shadow-probe). Carry provenance in an inline comment when you
181
+ * override, same discipline as capability data (step zero / L-081).
182
+ */
183
+ latencyTier?: LatencyTier;
166
184
  notes?: string;
167
185
  verifiedAgainstDocs?: string;
168
186
  /**
@@ -236,6 +254,26 @@ interface ModelProfile {
236
254
  */
237
255
  archetypeConventions?: ArchetypeConvention[];
238
256
  }
257
+ /**
258
+ * Representative p50 latency (ms) per tier. Coarse on purpose — used only to
259
+ * compare a model against `constraints.maxLatencyMs`, never reported as a
260
+ * per-model number. Grounded: alpha.46 shadow-probe measured
261
+ * `deepseek-v4-flash` at 20485ms and `deepseek-v4-pro` at 47722ms (2026-06-03),
262
+ * both bucket `slow`; gemini-2.5-flash / haiku-4-5 serve in single-digit
263
+ * seconds → `fast`; mid-tier (sonnet, gemini-pro) → `medium`. Phase 2 can swap
264
+ * these buckets for measured per-(archetype,model) p50 from
265
+ * `compile_outcomes.latency_ms` once enough served rows accumulate.
266
+ */
267
+ declare const LATENCY_TIER_MS: Record<LatencyTier, number>;
268
+ /**
269
+ * Resolve a model's latency tier. Explicit `profile.latencyTier` wins;
270
+ * otherwise derive from the tags already on the profile so we don't maintain a
271
+ * second source of truth that can silently disagree (L-073 family):
272
+ * - `weaknesses` includes `'latency'` → `'slow'`
273
+ * - `strengths` includes `'speed'` → `'fast'`
274
+ * - else → `'medium'`
275
+ */
276
+ declare function latencyTierOf(profile: ModelProfile): LatencyTier;
239
277
  declare const ALIASES: Record<string, string>;
240
278
  interface ProfileBrainHook {
241
279
  getProfile?: (canonicalId: string) => ModelProfile | undefined;
@@ -251,4 +289,4 @@ declare function allProfiles(): readonly ModelProfile[];
251
289
  declare function allProfilesRaw(): readonly ModelProfile[];
252
290
  declare function profilesByProvider(provider: Provider): readonly ModelProfile[];
253
291
 
254
- export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, profilesByProvider, tryGetProfile };
292
+ export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, LATENCY_TIER_MS, type LatencyTier, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, latencyTierOf, profilesByProvider, tryGetProfile };
@@ -145,6 +145,14 @@ interface LoweringSpec {
145
145
  default?: number | 'auto' | 'off';
146
146
  };
147
147
  }
148
+ /**
149
+ * Coarse latency bucket for a model. alpha.47 — the third swap axis
150
+ * (cost / quality / SPEED). We bucket rather than store per-model ms because
151
+ * served latency varies with token count; honest precision is the tier, not a
152
+ * false-precise number. See {@link LATENCY_TIER_MS} for the representative ms
153
+ * each tier maps to when compared against `constraints.maxLatencyMs`.
154
+ */
155
+ type LatencyTier = 'fast' | 'medium' | 'slow';
148
156
  interface ModelProfile {
149
157
  id: string;
150
158
  provider: Provider;
@@ -163,6 +171,16 @@ interface ModelProfile {
163
171
  recovery: RecoveryRule[];
164
172
  strengths: string[];
165
173
  weaknesses: string[];
174
+ /**
175
+ * alpha.47 — explicit latency bucket. OPTIONAL: when unset, `latencyTierOf`
176
+ * derives it from the tags above (`weaknesses` includes `'latency'` → slow,
177
+ * `strengths` includes `'speed'` → fast, else medium). Set this explicitly
178
+ * ONLY when measured evidence contradicts the tag derivation — e.g.
179
+ * `deepseek-v4-flash` carries no `'latency'` weakness yet measures ~20s
180
+ * (alpha.46 shadow-probe). Carry provenance in an inline comment when you
181
+ * override, same discipline as capability data (step zero / L-081).
182
+ */
183
+ latencyTier?: LatencyTier;
166
184
  notes?: string;
167
185
  verifiedAgainstDocs?: string;
168
186
  /**
@@ -236,6 +254,26 @@ interface ModelProfile {
236
254
  */
237
255
  archetypeConventions?: ArchetypeConvention[];
238
256
  }
257
+ /**
258
+ * Representative p50 latency (ms) per tier. Coarse on purpose — used only to
259
+ * compare a model against `constraints.maxLatencyMs`, never reported as a
260
+ * per-model number. Grounded: alpha.46 shadow-probe measured
261
+ * `deepseek-v4-flash` at 20485ms and `deepseek-v4-pro` at 47722ms (2026-06-03),
262
+ * both bucket `slow`; gemini-2.5-flash / haiku-4-5 serve in single-digit
263
+ * seconds → `fast`; mid-tier (sonnet, gemini-pro) → `medium`. Phase 2 can swap
264
+ * these buckets for measured per-(archetype,model) p50 from
265
+ * `compile_outcomes.latency_ms` once enough served rows accumulate.
266
+ */
267
+ declare const LATENCY_TIER_MS: Record<LatencyTier, number>;
268
+ /**
269
+ * Resolve a model's latency tier. Explicit `profile.latencyTier` wins;
270
+ * otherwise derive from the tags already on the profile so we don't maintain a
271
+ * second source of truth that can silently disagree (L-073 family):
272
+ * - `weaknesses` includes `'latency'` → `'slow'`
273
+ * - `strengths` includes `'speed'` → `'fast'`
274
+ * - else → `'medium'`
275
+ */
276
+ declare function latencyTierOf(profile: ModelProfile): LatencyTier;
239
277
  declare const ALIASES: Record<string, string>;
240
278
  interface ProfileBrainHook {
241
279
  getProfile?: (canonicalId: string) => ModelProfile | undefined;
@@ -251,4 +289,4 @@ declare function allProfiles(): readonly ModelProfile[];
251
289
  declare function allProfilesRaw(): readonly ModelProfile[];
252
290
  declare function profilesByProvider(provider: Provider): readonly ModelProfile[];
253
291
 
254
- export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, profilesByProvider, tryGetProfile };
292
+ export { ALIASES, type ArchetypeConvention, type CacheStrategy, type CliffRule, LATENCY_TIER_MS, type LatencyTier, type LoweringSpec, type ModelProfile, type RecoveryRule, type StructuredOutputCapability, type SystemPromptMode, _setProfileBrainHook, allProfiles, allProfilesRaw, getProfile, latencyTierOf, profilesByProvider, tryGetProfile };
package/dist/profiles.js CHANGED
@@ -21,14 +21,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var profiles_exports = {};
22
22
  __export(profiles_exports, {
23
23
  ALIASES: () => ALIASES,
24
+ LATENCY_TIER_MS: () => LATENCY_TIER_MS,
24
25
  _setProfileBrainHook: () => _setProfileBrainHook,
25
26
  allProfiles: () => allProfiles,
26
27
  allProfilesRaw: () => allProfilesRaw,
27
28
  getProfile: () => getProfile,
29
+ latencyTierOf: () => latencyTierOf,
28
30
  profilesByProvider: () => profilesByProvider,
29
31
  tryGetProfile: () => tryGetProfile
30
32
  });
31
33
  module.exports = __toCommonJS(profiles_exports);
34
+ var LATENCY_TIER_MS = {
35
+ fast: 4e3,
36
+ medium: 11e3,
37
+ slow: 24e3
38
+ };
39
+ function latencyTierOf(profile) {
40
+ if (profile.latencyTier) return profile.latencyTier;
41
+ if (profile.weaknesses.includes("latency")) return "slow";
42
+ if (profile.strengths.includes("speed")) return "fast";
43
+ return "medium";
44
+ }
32
45
  var ANTHROPIC_LOWERING_BASE = {
33
46
  system: { mode: "inline" },
34
47
  cache: {
@@ -575,6 +588,14 @@ var PROFILES_RAW = [
575
588
  ],
576
589
  strengths: ["cost", "1m_context", "json_output", "code", "reasoning"],
577
590
  weaknesses: ["parallel_tools", "large_tool_sets"],
591
+ // alpha.47 — explicit slow override. Tag derivation would say 'medium'
592
+ // (no 'latency' weakness, no 'speed' strength), but the alpha.46 shadow
593
+ // probe MEASURED deepseek-v4-flash at 20485ms served (2026-06-03) — ~2.3×
594
+ // gemini-2.5-flash on PB's synchronous /api/analyze path. The 'flash' name
595
+ // is DeepSeek's, not a speed promise. This is the row that, scoring 0.85
596
+ // baseQuality (it carries 'reasoning') with no latency counterweight,
597
+ // leapfrogged gemini-2.5-flash as PB's summarize leader once reachable.
598
+ latencyTier: "slow",
578
599
  notes: "Cheap workhorse. 1M context, 384k max output. Cache-hit input $0.0028/M (1/50\xD7 of miss). Aliased as `deepseek-chat` (non-thinking) and `deepseek-reasoner` (thinking) \u2014 see ALIASES.",
579
600
  // Master plan §6.2 anchor. Brain-validated tier 1 cross-provider for
580
601
  // classify (169 rows, 0% empty). Tier 0 for summarize-with-no-tools.
@@ -642,6 +663,11 @@ var PROFILES_RAW = [
642
663
  ],
643
664
  strengths: ["quality", "reasoning", "1m_context", "json_output", "code", "extended_thinking"],
644
665
  weaknesses: ["parallel_tools", "large_tool_sets"],
666
+ // alpha.47 — explicit slow override. Measured 47722ms on the alpha.46
667
+ // shadow probe (2026-06-03) — it's an extended-thinking reasoner, slowest
668
+ // of the served set. Tag derivation would say 'medium'; the measurement says
669
+ // otherwise.
670
+ latencyTier: "slow",
645
671
  notes: "Pro tier. 1M context, 384k max output. Regular pricing $1.74/$3.48; 75% promo through 2026-05-31 ($0.435/$0.87). Default mode = thinking.",
646
672
  // Master plan §3.3: tier 3 cross-provider for plan chain. Reasoning
647
673
  // bumped one notch over V4-Flash; same parallel-tool ceiling.
@@ -1295,10 +1321,12 @@ function profilesByProvider(provider) {
1295
1321
  // Annotate the CommonJS export names for ESM import in node:
1296
1322
  0 && (module.exports = {
1297
1323
  ALIASES,
1324
+ LATENCY_TIER_MS,
1298
1325
  _setProfileBrainHook,
1299
1326
  allProfiles,
1300
1327
  allProfilesRaw,
1301
1328
  getProfile,
1329
+ latencyTierOf,
1302
1330
  profilesByProvider,
1303
1331
  tryGetProfile
1304
1332
  });
package/dist/profiles.mjs CHANGED
@@ -1,18 +1,22 @@
1
1
  import {
2
2
  ALIASES,
3
+ LATENCY_TIER_MS,
3
4
  _setProfileBrainHook,
4
5
  allProfiles,
5
6
  allProfilesRaw,
6
7
  getProfile,
8
+ latencyTierOf,
7
9
  profilesByProvider,
8
10
  tryGetProfile
9
- } from "./chunk-ZHUD3I52.mjs";
11
+ } from "./chunk-PKOFXEB3.mjs";
10
12
  export {
11
13
  ALIASES,
14
+ LATENCY_TIER_MS,
12
15
  _setProfileBrainHook,
13
16
  allProfiles,
14
17
  allProfilesRaw,
15
18
  getProfile,
19
+ latencyTierOf,
16
20
  profilesByProvider,
17
21
  tryGetProfile
18
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmdrift/kgauto-compiler",
3
- "version": "2.0.0-alpha.46",
3
+ "version": "2.0.0-alpha.48",
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",