pi-smart-router 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.pi/extensions/smart-router/command-formatters.ts +77 -1
  2. package/.pi/extensions/smart-router/commands.ts +31 -1
  3. package/README.md +23 -1
  4. package/config/benchmark-profiles.json +2 -2
  5. package/dist/config/defaults.d.ts +1 -1
  6. package/dist/config/defaults.d.ts.map +1 -1
  7. package/dist/config/defaults.js +6 -2
  8. package/dist/config/defaults.js.map +1 -1
  9. package/dist/domain/pinning/heat-affinity.d.ts +70 -0
  10. package/dist/domain/pinning/heat-affinity.d.ts.map +1 -0
  11. package/dist/domain/pinning/heat-affinity.js +146 -0
  12. package/dist/domain/pinning/heat-affinity.js.map +1 -0
  13. package/dist/domain/pipeline/router-pipeline.d.ts +19 -0
  14. package/dist/domain/pipeline/router-pipeline.d.ts.map +1 -1
  15. package/dist/domain/pipeline/router-pipeline.js +72 -1
  16. package/dist/domain/pipeline/router-pipeline.js.map +1 -1
  17. package/dist/domain/routing/expected-cost.d.ts +25 -0
  18. package/dist/domain/routing/expected-cost.d.ts.map +1 -1
  19. package/dist/domain/routing/expected-cost.js +36 -3
  20. package/dist/domain/routing/expected-cost.js.map +1 -1
  21. package/dist/domain/routing/speculative-prewarm.d.ts +102 -0
  22. package/dist/domain/routing/speculative-prewarm.d.ts.map +1 -0
  23. package/dist/domain/routing/speculative-prewarm.js +177 -0
  24. package/dist/domain/routing/speculative-prewarm.js.map +1 -0
  25. package/dist/domain/routing/workload-heat.d.ts +158 -0
  26. package/dist/domain/routing/workload-heat.d.ts.map +1 -0
  27. package/dist/domain/routing/workload-heat.js +349 -0
  28. package/dist/domain/routing/workload-heat.js.map +1 -0
  29. package/dist/domain/types/entities.d.ts +12 -0
  30. package/dist/domain/types/entities.d.ts.map +1 -1
  31. package/dist/domain/types/schemas.d.ts +54 -0
  32. package/dist/domain/types/schemas.d.ts.map +1 -1
  33. package/dist/domain/types/schemas.js +74 -0
  34. package/dist/domain/types/schemas.js.map +1 -1
  35. package/dist/infrastructure/hardware/placement-plan.d.ts +141 -0
  36. package/dist/infrastructure/hardware/placement-plan.d.ts.map +1 -0
  37. package/dist/infrastructure/hardware/placement-plan.js +263 -0
  38. package/dist/infrastructure/hardware/placement-plan.js.map +1 -0
  39. package/dist/infrastructure/hardware/throughput-meter.d.ts +59 -4
  40. package/dist/infrastructure/hardware/throughput-meter.d.ts.map +1 -1
  41. package/dist/infrastructure/hardware/throughput-meter.js +77 -8
  42. package/dist/infrastructure/hardware/throughput-meter.js.map +1 -1
  43. package/dist/infrastructure/telemetry/routing-telemetry.d.ts +5 -0
  44. package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -1
  45. package/dist/infrastructure/telemetry/routing-telemetry.js +20 -0
  46. package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -1
  47. package/dist/infrastructure/telemetry/workload-heat-store.d.ts +32 -0
  48. package/dist/infrastructure/telemetry/workload-heat-store.d.ts.map +1 -0
  49. package/dist/infrastructure/telemetry/workload-heat-store.js +71 -0
  50. package/dist/infrastructure/telemetry/workload-heat-store.js.map +1 -0
  51. package/package.json +2 -1
  52. package/src/config/defaults.ts +8 -0
  53. package/src/domain/pinning/heat-affinity.ts +203 -0
  54. package/src/domain/pipeline/router-pipeline.ts +105 -5
  55. package/src/domain/routing/expected-cost.ts +61 -3
  56. package/src/domain/routing/speculative-prewarm.ts +252 -0
  57. package/src/domain/routing/workload-heat.ts +504 -0
  58. package/src/domain/types/entities.ts +12 -0
  59. package/src/domain/types/schemas.ts +82 -0
  60. package/src/infrastructure/hardware/placement-plan.ts +453 -0
  61. package/src/infrastructure/hardware/throughput-meter.ts +156 -13
  62. package/src/infrastructure/telemetry/routing-telemetry.ts +28 -0
  63. package/src/infrastructure/telemetry/workload-heat-store.ts +91 -0
@@ -17,6 +17,7 @@ import {
17
17
  DEFAULT_TELEMETRY_CONTRIB_EXPORT_LIMIT,
18
18
  parseExportTelemetryContribArgs,
19
19
  } from '../../../src/cli/smart-router-cli.js';
20
+ import type { PlacementPlanReport } from '../../../src/infrastructure/hardware/placement-plan.js';
20
21
  import {
21
22
  DEFAULT_DATASET_EXPORT_LIMIT,
22
23
  MAX_DATASET_EXPORT_LIMIT,
@@ -146,12 +147,20 @@ export function parseExportLimit(tokens: string[]): number {
146
147
  return limit;
147
148
  }
148
149
 
149
- export function parseSmartRouterArgs(args: string): SmartRouterCommand {
150
+ export function parseSmartRouterArgs(args: string): ParsedSmartRouterCommand {
150
151
  const tokens = args.trim().split(/\s+/).filter(Boolean);
151
152
  if (tokens.length === 0 || tokens[0] === 'status') {
152
153
  return { command: 'status' };
153
154
  }
154
155
 
156
+ if (tokens[0] === 'plan' && (tokens.length === 1 || (tokens.length === 2 && tokens[1] === '--json'))) {
157
+ return { command: 'plan', format: tokens[1] === '--json' ? 'json' : 'text' };
158
+ }
159
+
160
+ if (tokens[0] === 'doctor' && tokens.length === 1) {
161
+ return { command: 'doctor' };
162
+ }
163
+
155
164
  if (tokens[0] === 'history') {
156
165
  return { command: 'history', limit: parseHistoryLimit(tokens[1]) };
157
166
  }
@@ -339,3 +348,70 @@ export function buildStatsSnapshot(
339
348
  }
340
349
 
341
350
  export type { FleetMode };
351
+
352
+ /**
353
+ * Read-only placement commands (SP-216, #116). Declared here instead of
354
+ * types.ts to keep the SmartRouterCommand union untouched.
355
+ */
356
+ export type SmartRouterPlacementCommand =
357
+ | { command: 'plan'; format: 'text' | 'json' }
358
+ | { command: 'doctor' };
359
+
360
+ export type ParsedSmartRouterCommand = SmartRouterCommand | SmartRouterPlacementCommand;
361
+
362
+ function formatTps(value: number | null): string {
363
+ return value === null ? 'n/a' : `${value.toFixed(1)} tok/s`;
364
+ }
365
+
366
+ function formatGb(value: number | null): string {
367
+ return value === null ? 'unknown' : `${value.toFixed(1)} GiB`;
368
+ }
369
+
370
+ function formatPing(label: string, ping: PlacementPlanReport['localModel']['lmStudio']): string {
371
+ if (!ping.available) {
372
+ return ` ${label}: unreachable`;
373
+ }
374
+ return ` ${label}: up, model ${ping.hasLoadedModel ? 'loaded (warm)' : 'not loaded (cold)'}`;
375
+ }
376
+
377
+ /** Human-readable placement report for `/smart-router plan` (read-only). */
378
+ export function formatPlacementPlanMessage(report: PlacementPlanReport): string {
379
+ const lines = [
380
+ `Placement plan (read-only, schema v${report.schemaVersion}) — ${report.generatedAt}`,
381
+ `Recommendation: ${report.recommendation}`,
382
+ `Bottleneck guess: ${report.bottleneck.guess} — ${report.bottleneck.rationale}`,
383
+ `Encoder: ${report.encoder.resident ? 'resident' : 'not resident'} (${report.encoder.model}) at ${report.encoder.cachePath}`,
384
+ `Local model: ${report.localModel.warm ? 'warm' : report.localModel.coldStartExpected ? 'cold (load on first request)' : 'unavailable'}`,
385
+ formatPing('LM Studio', report.localModel.lmStudio),
386
+ formatPing('Ollama', report.localModel.ollama),
387
+ `Hardware: ${report.hardware.platform}/${report.hardware.arch}, total ${formatGb(report.hardware.totalMemoryGb)}, free ${formatGb(report.hardware.freeMemoryGb)}, probe=${report.hardware.probe}`,
388
+ `Disk (${report.disk.path}): free ${formatGb(report.disk.freeGb)}${report.disk.constrained ? ' [constrained]' : ''}`,
389
+ `Throughput: ${report.throughput.classification} | warm median ${formatTps(report.throughput.warmMedianTps)} (${report.throughput.warmSamples} samples) | cold median ${formatTps(report.throughput.coldMedianTps)} (${report.throughput.coldSamples} samples) | threshold ${report.throughput.thresholdTps} tok/s | viable=${report.throughput.viable}`,
390
+ `Policy: quality-preserving — on resource pressure: ${report.policy.onResourcePressure}`,
391
+ ];
392
+ return lines.join('\n');
393
+ }
394
+
395
+ /** Checklist-style readiness verdict for `/smart-router doctor` (read-only). */
396
+ export function formatDoctorMessage(report: PlacementPlanReport): string {
397
+ const check = (ok: boolean, label: string): string => `${ok ? '✓' : '✗'} ${label}`;
398
+ const lines = [
399
+ 'smart-router doctor (read-only)',
400
+ check(report.encoder.resident, `encoder resident (${report.encoder.model})`),
401
+ check(report.hardware.probe === 'full_local', `hardware probe: ${report.hardware.probe}`),
402
+ check(!report.disk.constrained, `disk: ${formatGb(report.disk.freeGb)} free${report.disk.constrained ? ' [constrained]' : ''}`),
403
+ check(
404
+ report.localModel.lmStudio.available || report.localModel.ollama.available,
405
+ 'local runtime reachable (LM Studio / Ollama)',
406
+ ),
407
+ check(report.localModel.warm, `local model ${report.localModel.warm ? 'warm' : 'cold/not loaded'}`),
408
+ check(
409
+ report.throughput.viable,
410
+ `throughput: ${report.throughput.classification}, viable=${report.throughput.viable}${report.throughput.classification === 'cold-only' ? ' (cold-only windows fail closed)' : ''}`,
411
+ ),
412
+ `Bottleneck guess: ${report.bottleneck.guess} — ${report.bottleneck.rationale}`,
413
+ `Recommendation: ${report.recommendation}`,
414
+ 'Policy: quality-preserving — under resource pressure local is reported unavailable and routing escalates safely; encoder fidelity is never weakened.',
415
+ ];
416
+ return lines.join('\n');
417
+ }
@@ -1,20 +1,24 @@
1
1
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
2
+ import { join } from 'node:path';
2
3
 
3
4
  import {
5
+ formatDoctorMessage,
4
6
  formatHistoryMessage,
7
+ formatPlacementPlanMessage,
5
8
  formatStatsMessage,
6
9
  formatStatusMessage,
7
10
  parseSmartRouterArgs,
8
11
  } from './command-formatters.js';
9
12
  import { exportDatasetToFile } from './dataset-export.js';
10
13
  import { exportTelemetryContrib } from '../../../src/cli/smart-router-cli.js';
14
+ import { collectPlacementPlan } from '../../../src/infrastructure/hardware/placement-plan.js';
11
15
  import { bindSharedModelRegistry, rebuildFleet } from './fleet-bootstrap.js';
12
16
  import { refreshPricingCatalog } from './pricing-lifecycle.js';
13
17
  import { FLEET_MODE_ENTRY_TYPE } from './session-lifecycle.js';
14
18
  import type { SmartRouterRuntime } from './types.js';
15
19
 
16
20
  export const SMART_ROUTER_USAGE =
17
- '/smart-router [status] | history [limit] | stats [limit] | mode scoped|all | pricing refresh | export dataset [--limit N] | export telemetry-contrib [--limit N] | feedback good|bad | unpin';
21
+ '/smart-router [status] | history [limit] | stats [limit] | mode scoped|all | pricing refresh | export dataset [--limit N] | export telemetry-contrib [--limit N] | feedback good|bad | unpin | plan [--json] | doctor';
18
22
 
19
23
  type CompletionItem = { value: string; label: string };
20
24
 
@@ -27,6 +31,8 @@ const TOP_LEVEL: CompletionItem[] = [
27
31
  { value: 'export', label: 'Export opt-in routing dataset' },
28
32
  { value: 'feedback', label: 'Label last routing outcome good or bad' },
29
33
  { value: 'unpin', label: 'Clear current session pin' },
34
+ { value: 'plan', label: 'Read-only local placement report (warm/cold, bottleneck)' },
35
+ { value: 'doctor', label: 'Read-only local readiness checklist' },
30
36
  ];
31
37
 
32
38
  const MODE_COMPLETIONS: CompletionItem[] = [
@@ -66,6 +72,9 @@ export const SMART_ROUTER_FULL_INVOCATIONS = [
66
72
  'feedback good',
67
73
  'feedback bad',
68
74
  'unpin',
75
+ 'plan',
76
+ 'plan --json',
77
+ 'doctor',
69
78
  ] as const;
70
79
 
71
80
  function filterByPrefix(items: CompletionItem[], prefix: string): CompletionItem[] {
@@ -267,6 +276,27 @@ export function registerSmartRouterCommand(
267
276
  return;
268
277
  }
269
278
 
279
+ if (parsed.command === 'plan' || parsed.command === 'doctor') {
280
+ throwIfCommandAborted(signal);
281
+ // Read-only: pings + fs stats only; never mutates route/pin/gates.
282
+ const report = await collectPlacementPlan({
283
+ encoderCachePath: join(ctx.cwd, '.pi-smart-router/models'),
284
+ diskPath: ctx.cwd,
285
+ });
286
+ throwIfCommandAborted(signal);
287
+ if (parsed.command === 'plan' && parsed.format === 'json') {
288
+ ctx.ui.notify(JSON.stringify(report, null, 2), 'info');
289
+ return;
290
+ }
291
+ ctx.ui.notify(
292
+ parsed.command === 'doctor'
293
+ ? formatDoctorMessage(report)
294
+ : formatPlacementPlanMessage(report),
295
+ 'info',
296
+ );
297
+ return;
298
+ }
299
+
270
300
  if (parsed.command === 'unpin') {
271
301
  const sessionId = ctx.sessionManager.getSessionId();
272
302
  const sessionPinner = runtime.streamDeps.sessionPinner;
package/README.md CHANGED
@@ -225,9 +225,31 @@ Cursor models bill against your **Cursor Pro subscription quota**, not per-token
225
225
  | `/smart-router export telemetry-contrib [--limit N]` | Export privacy-safe community telemetry JSON for calibration contributions |
226
226
  | `/smart-router feedback good\|bad` | Label the last auto-routed request outcome (requires `SMART_ROUTER_DATASET=1`) |
227
227
  | `/smart-router unpin` | Clear the current session pin (in-memory and SQLite) so the next request runs the full routing pipeline |
228
+ | `/smart-router plan [--json]` | **Read-only** local placement report: encoder resident status, local model warm/cold, RAM/disk constraints, cold vs warm TPS, and bottleneck guess. `--json` prints the schema-stable report for automation |
229
+ | `/smart-router doctor` | **Read-only** local readiness checklist (✓/✗) with bottleneck guess and recommendation — validate placement without starting a route |
228
230
 
229
231
  Fleet mode persists in the session. Use `scoped` to respect your `/model` enable-list; use `all` when you want the router to consider every provider you have logged into.
230
232
 
233
+ ### Local placement plan / doctor (read-only, #116)
234
+
235
+ `/smart-router plan` and `/smart-router doctor` report **local placement readiness without mutating anything** — no route, pin, or gate is touched. Inspired by Colibrì's `coli plan` / `coli doctor`.
236
+
237
+ The report covers:
238
+
239
+ - **Encoder** — whether HyDRA ONNX artifacts are resident in the cache (`.pi-smart-router/models/`) or will download on first route
240
+ - **Local model warm/cold** — LM Studio / Ollama reachability and whether a model is actually loaded (`warm`) vs reachable-but-unloaded (`cold-start expected`)
241
+ - **Hardware** — platform/arch, total/free RAM, hardware-probe verdict (`full_local` / `classification_only` / `disabled`), battery state
242
+ - **Disk** — free GiB; flagged constrained below 2 GiB
243
+ - **Throughput (cold vs warm TPS)** — see formula below
244
+ - **Bottleneck guess** — one of `none`, `unsupported-platform`, `battery`, `memory`, `disk`, `no-local-runtime`, `cold-start`, `cold-throughput`, `warm-throughput`, with a rationale string
245
+ - **Recommendation** — `local-ready`, `local-warmup-needed`, or `local-unavailable`
246
+
247
+ `/smart-router plan --json` prints the same report as schema-stable JSON (`schemaVersion: 1`, `kind: "smart-router-placement-plan"`; all keys always present, unknowns are `null`) for automation.
248
+
249
+ **Cold vs warm TPS formula.** Throughput samples are tagged by phase: `warm` samples measure steady-state generation after model load; `cold` samples include cold-start load cost and never count toward viability. `warmMedianTps = median(tps where phase='warm')`; viability is `warmSamples > 0 AND warmMedianTps >= threshold` (default 25 tok/s). **Cold-only windows fail closed**: when only cold samples exist, local viability is false by policy (`requireWarmSamples: true`) — cold-start cost must not masquerade as steady-state throughput.
250
+
251
+ **Quality-preserving resource policy.** Under RAM/disk/battery pressure the router prefers **"local unavailable / escalate safely"** to a cloud default over silently weakening encoder fidelity (no quantization flips, no cheaper cascades, no FrugalGPT-style downgrade chains). `plan`/`doctor` surface this policy verbatim in the report's `policy` block.
252
+
231
253
  After typing `/smart-router ` (with a trailing space), press **TAB** to see subcommands. Continue TAB-completing after `mode` or `pricing` for sub-options (`scoped`/`all`, `refresh`).
232
254
 
233
255
  ### 5. Verify
@@ -712,7 +734,7 @@ Dry-run behavior:
712
734
  | `--include-excluded-in-fit` | Weak rows may warm-start the fit pool; `ece_eligible` / holdout ECE stay verifier-grade |
713
735
  | Soft threshold | Advisory `0.25` calibrated ECE — **does not** change `config/release-gates.json` |
714
736
 
715
- **#96 / `modernbert_k4` advisory:** when deciding whether to enable ModernBERT K=4 heads, use **pack holdout ECE / Top-1 error on verifier-grade packs** (SWE-Gym + FC-RewardBench), not fixture-only QR and **not** weak-fit ECE. Weak TwinRouterBench rows are warm-start only. This task does **not** flip `modernbert_k4` defaults. Go/no-go evidence (SP-204 / #113): [`spine-tasks/_authoring/release-v0.11.0/encoder-gonogo-artifact.md`](spine-tasks/_authoring/release-v0.11.0/encoder-gonogo-artifact.md).
737
+ **#96 / `modernbert_k4` advisory:** when deciding whether to enable ModernBERT K=4 heads, use **pack holdout ECE / Top-1 error on verifier-grade packs** (SWE-Gym + FC-RewardBench), not fixture-only QR and **not** weak-fit ECE. Weak TwinRouterBench rows are warm-start only. This task does **not** flip `modernbert_k4` defaults. Go/no-go evidence (SP-204 / #113): [`spine-tasks/_authoring/release-v0.11.0/encoder-gonogo-artifact.md`](spine-tasks/_authoring/release-v0.11.0/encoder-gonogo-artifact.md). K=4 Top-1 / offline A/B decision (SP-219 / #114): [`spine-tasks/_authoring/release-v0.16.0/modernbert-k4-top1-artifact.md`](spine-tasks/_authoring/release-v0.16.0/modernbert-k4-top1-artifact.md) — recommendation: **keep default** (gate not measurable without trained heads; #96 open).
716
738
 
717
739
  ### Operator tuning (frugality slider)
718
740
 
@@ -7,8 +7,8 @@
7
7
  "livecodebench": "https://livecodebench.github.io/leaderboard.html",
8
8
  "bfcl": "https://gorilla.cs.berkeley.edu/leaderboard.html"
9
9
  },
10
- "scrape_date": "2026-07-20",
11
- "catalog_freeze_date": "2026-07-20"
10
+ "scrape_date": "2026-08-03",
11
+ "catalog_freeze_date": "2026-08-03"
12
12
  },
13
13
  "aliases": {
14
14
  "anthropic/claude-opus-4": "claude-opus-4-5",
@@ -3,7 +3,7 @@
3
3
  * Values sourced from specs/001-build-smart-router/data-model.md § Configuration (Operator).
4
4
  */
5
5
  import { type OperatorConfig } from '../domain/types/schemas.js';
6
- export { DEFAULT_LOCAL_ZERO_CONFIG, DEFAULT_PLANNING_DELEGATE_CONFIG, DEFAULT_SAAR_CONFIG, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
6
+ export { DEFAULT_LOCAL_ZERO_CONFIG, DEFAULT_PLANNING_DELEGATE_CONFIG, DEFAULT_SAAR_CONFIG, DEFAULT_SPECULATIVE_PREWARM_CONFIG, DEFAULT_WORKLOAD_HEAT_CONFIG, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
7
7
  /** Merge operator env overrides onto defaults (SAAR and planning delegate sections). */
8
8
  export declare function resolveOperatorConfigFromEnv(base?: OperatorConfig): OperatorConfig;
9
9
  export declare const DEFAULT_OPERATOR_CONFIG: Readonly<OperatorConfig>;
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,mBAAmB,EACnB,oCAAoC,EACpC,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AAEpC,wFAAwF;AACxF,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE,cAAwC,GAC7C,cAAc,CAMhB;AAED,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,cAAc,CAiCnD,CAAC"}
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EASL,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,mBAAmB,EACnB,kCAAkC,EAClC,4BAA4B,EAC5B,oCAAoC,EACpC,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AAEpC,wFAAwF;AACxF,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE,cAAwC,GAC7C,cAAc,CAMhB;AAED,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,cAAc,CAqCnD,CAAC"}
@@ -2,9 +2,9 @@
2
2
  * Operator configuration defaults (FR-021).
3
3
  * Values sourced from specs/001-build-smart-router/data-model.md § Configuration (Operator).
4
4
  */
5
- import { DEFAULT_DEGRADED_ROUTE_CONFIG, DEFAULT_LOCAL_ZERO_CONFIG, DEFAULT_PLANNING_DELEGATE_CONFIG, DEFAULT_SAAR_CONFIG, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
5
+ import { DEFAULT_DEGRADED_ROUTE_CONFIG, DEFAULT_LOCAL_ZERO_CONFIG, DEFAULT_PLANNING_DELEGATE_CONFIG, DEFAULT_SAAR_CONFIG, DEFAULT_SPECULATIVE_PREWARM_CONFIG, DEFAULT_WORKLOAD_HEAT_CONFIG, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
6
6
  import { DEFAULT_LOW_INTENSITY_WEIGHTS } from '../domain/routing/tier-features.js';
7
- export { DEFAULT_LOCAL_ZERO_CONFIG, DEFAULT_PLANNING_DELEGATE_CONFIG, DEFAULT_SAAR_CONFIG, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
7
+ export { DEFAULT_LOCAL_ZERO_CONFIG, DEFAULT_PLANNING_DELEGATE_CONFIG, DEFAULT_SAAR_CONFIG, DEFAULT_SPECULATIVE_PREWARM_CONFIG, DEFAULT_WORKLOAD_HEAT_CONFIG, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
8
8
  /** Merge operator env overrides onto defaults (SAAR and planning delegate sections). */
9
9
  export function resolveOperatorConfigFromEnv(base = DEFAULT_OPERATOR_CONFIG) {
10
10
  return {
@@ -45,6 +45,10 @@ export const DEFAULT_OPERATOR_CONFIG = {
45
45
  planning_delegate: DEFAULT_PLANNING_DELEGATE_CONFIG,
46
46
  local_zero: DEFAULT_LOCAL_ZERO_CONFIG,
47
47
  degraded_route: DEFAULT_DEGRADED_ROUTE_CONFIG,
48
+ /** Heat knobs only (SP-215) — no frugality default or absolute-gate flips. */
49
+ workload_heat: DEFAULT_WORKLOAD_HEAT_CONFIG,
50
+ /** Speculative prewarm (SP-217, #117): default OFF; opt-in via operator config. */
51
+ speculative_prewarm: DEFAULT_SPECULATIVE_PREWARM_CONFIG,
48
52
  pin_only_fallback: false,
49
53
  };
50
54
  //# sourceMappingURL=defaults.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,6BAA6B,EAC7B,yBAAyB,EACzB,gCAAgC,EAChC,mBAAmB,EACnB,oCAAoC,EACpC,wBAAwB,GAEzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAEnF,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,mBAAmB,EACnB,oCAAoC,EACpC,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AAEpC,wFAAwF;AACxF,MAAM,UAAU,4BAA4B,CAC1C,OAAuB,uBAAuB;IAE9C,OAAO;QACL,GAAG,IAAI;QACP,IAAI,EAAE,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC;QACzC,iBAAiB,EAAE,oCAAoC,CAAC,IAAI,CAAC,iBAAiB,CAAC;KAChF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAA6B;IAC/D,SAAS,EAAE;QACT,WAAW,EAAE,GAAG;QAChB,cAAc,EAAE,GAAG;QACnB,gBAAgB,EAAE,IAAI;KACvB;IACD,eAAe,EAAE;QACf,SAAS,EAAE,CAAC;KACb;IACD,OAAO,EAAE;QACP,cAAc,EAAE,EAAE;KACnB;IACD,KAAK,EAAE;QACL,kBAAkB,EAAE,EAAE;QACtB,4BAA4B,EAAE,CAAC;QAC/B,qBAAqB,EAAE,EAAE;KAC1B;IACD,KAAK,EAAE;QACL,mBAAmB,EAAE,0BAA0B;QAC/C,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,oBAAoB;KAClC;IACD,aAAa,EAAE;QACb,OAAO,EAAE,6BAA6B;QACtC,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,GAAG;KACrB;IACD,IAAI,EAAE,mBAAmB;IACzB,iBAAiB,EAAE,gCAAgC;IACnD,UAAU,EAAE,yBAAyB;IACrC,cAAc,EAAE,6BAA6B;IAC7C,iBAAiB,EAAE,KAAK;CAChB,CAAC"}
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,6BAA6B,EAC7B,yBAAyB,EACzB,gCAAgC,EAChC,mBAAmB,EACnB,kCAAkC,EAClC,4BAA4B,EAC5B,oCAAoC,EACpC,wBAAwB,GAEzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAEnF,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,mBAAmB,EACnB,kCAAkC,EAClC,4BAA4B,EAC5B,oCAAoC,EACpC,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AAEpC,wFAAwF;AACxF,MAAM,UAAU,4BAA4B,CAC1C,OAAuB,uBAAuB;IAE9C,OAAO;QACL,GAAG,IAAI;QACP,IAAI,EAAE,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC;QACzC,iBAAiB,EAAE,oCAAoC,CAAC,IAAI,CAAC,iBAAiB,CAAC;KAChF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAA6B;IAC/D,SAAS,EAAE;QACT,WAAW,EAAE,GAAG;QAChB,cAAc,EAAE,GAAG;QACnB,gBAAgB,EAAE,IAAI;KACvB;IACD,eAAe,EAAE;QACf,SAAS,EAAE,CAAC;KACb;IACD,OAAO,EAAE;QACP,cAAc,EAAE,EAAE;KACnB;IACD,KAAK,EAAE;QACL,kBAAkB,EAAE,EAAE;QACtB,4BAA4B,EAAE,CAAC;QAC/B,qBAAqB,EAAE,EAAE;KAC1B;IACD,KAAK,EAAE;QACL,mBAAmB,EAAE,0BAA0B;QAC/C,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,oBAAoB;KAClC;IACD,aAAa,EAAE;QACb,OAAO,EAAE,6BAA6B;QACtC,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,IAAI;QACnB,eAAe,EAAE,GAAG;KACrB;IACD,IAAI,EAAE,mBAAmB;IACzB,iBAAiB,EAAE,gCAAgC;IACnD,UAAU,EAAE,yBAAyB;IACrC,cAAc,EAAE,6BAA6B;IAC7C,8EAA8E;IAC9E,aAAa,EAAE,4BAA4B;IAC3C,mFAAmF;IACnF,mBAAmB,EAAE,kCAAkC;IACvD,iBAAiB,EAAE,KAAK;CAChB,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Live heat affinity at pin-safe boundaries (SP-215, #115).
3
+ *
4
+ * Colibri REPIN analog: OPTIONAL live affinity updates that let the hot tier
5
+ * track the live workload without thrashing. Updates are only evaluated at
6
+ * pin-safe boundaries — moments where the session pin is already broken or
7
+ * reopened, so affinity never smashes a warm pin:
8
+ *
9
+ * - FR-008 pin breaks (compaction, context overflow, cache economics, ...)
10
+ * - SAAR idle reopen (`saar_idle_reopen`)
11
+ *
12
+ * Hysteresis (Colibri-style): a swap toward the heat-preferred tier requires
13
+ * a success-rate advantage of at least `hysteresis_band` (~25%) over the
14
+ * active tier, and at most `swap_cap` live swaps per session. Heat counts are
15
+ * decayed at each swap so stale workload history cannot pin the affinity
16
+ * forever.
17
+ *
18
+ * Default OFF (`live_update_enabled: false`) — the persisted histogram and
19
+ * the first-turn soft bias (expected-cost.ts) work without live repinning.
20
+ * This module never flips frugality defaults or absolute release gates.
21
+ */
22
+ import type { Tier } from '../types/index.js';
23
+ import type { WorkloadHeatConfig } from '../types/schemas.js';
24
+ import type { WorkloadHeatKey, WorkloadHeatMap } from '../routing/workload-heat.js';
25
+ import type { PinLookupResult } from './session-pinner.js';
26
+ /**
27
+ * True when a pin lookup result represents a pin-safe boundary: the pin is
28
+ * already broken or reopened, so a live affinity update cannot smash a warm
29
+ * pin. Hard-locked / sub-routed / in-use pins are NOT safe boundaries.
30
+ */
31
+ export declare function isPinSafeBoundary(result: PinLookupResult): boolean;
32
+ export type AffinitySwapReason = 'live_update_disabled' | 'not_pin_safe_boundary' | 'no_heat_data' | 'affinity_already_active' | 'hysteresis_hold' | 'swap_cap_reached' | 'heat_affinity_swap';
33
+ export interface AffinitySwapDecision {
34
+ readonly shouldSwap: boolean;
35
+ readonly targetTier: Tier | null;
36
+ readonly reasonCode: AffinitySwapReason;
37
+ /** Success-rate advantage of the candidate over the active tier (0 when N/A). */
38
+ readonly advantage: number;
39
+ }
40
+ export interface SessionAffinityState {
41
+ readonly activeTier: Tier;
42
+ readonly swapsThisSession: number;
43
+ }
44
+ /** Heat decay factor applied at each live swap (Colibri decaying heat). */
45
+ export declare const AFFINITY_SWAP_HEAT_DECAY = 0.5;
46
+ /**
47
+ * Per-session live affinity controller. Evaluates heat-preferred tier swaps
48
+ * at pin-safe boundaries with hysteresis + swap cap. Stateless callers may
49
+ * construct one controller per process; sessions are bounded by pin lifecycle
50
+ * (`clearSession` on pin break/end).
51
+ */
52
+ export declare class HeatAffinityController {
53
+ private readonly config;
54
+ private readonly sessions;
55
+ constructor(config: WorkloadHeatConfig);
56
+ /** Read-only per-session affinity state (telemetry/tests). */
57
+ getState(sessionId: string): SessionAffinityState | null;
58
+ /** Drop session affinity state (pin break / session end). */
59
+ clearSession(sessionId: string): void;
60
+ /**
61
+ * Evaluate a live affinity swap at a pin-safe boundary.
62
+ *
63
+ * Gates, in order: live updates enabled → pin-safe boundary → heat data →
64
+ * not already active → hysteresis band → swap cap. On swap, the session's
65
+ * active tier moves and heat counts decay so the hot set tracks the live
66
+ * workload.
67
+ */
68
+ evaluateSwap(sessionId: string, boundary: PinLookupResult, key: WorkloadHeatKey, currentTier: Tier, heat: WorkloadHeatMap): AffinitySwapDecision;
69
+ }
70
+ //# sourceMappingURL=heat-affinity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heat-affinity.d.ts","sourceRoot":"","sources":["../../../src/domain/pinning/heat-affinity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAI3D;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAKlE;AAID,MAAM,MAAM,kBAAkB,GAC1B,sBAAsB,GACtB,uBAAuB,GACvB,cAAc,GACd,yBAAyB,GACzB,iBAAiB,GACjB,kBAAkB,GAClB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,iFAAiF;IACjF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAOD,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;;GAKG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkD;gBAE/D,MAAM,EAAE,kBAAkB;IAItC,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI;IAKxD,6DAA6D;IAC7D,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC;;;;;;;OAOG;IACH,YAAY,CACV,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,eAAe,EACzB,GAAG,EAAE,eAAe,EACpB,WAAW,EAAE,IAAI,EACjB,IAAI,EAAE,eAAe,GACpB,oBAAoB;CA0FxB"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Live heat affinity at pin-safe boundaries (SP-215, #115).
3
+ *
4
+ * Colibri REPIN analog: OPTIONAL live affinity updates that let the hot tier
5
+ * track the live workload without thrashing. Updates are only evaluated at
6
+ * pin-safe boundaries — moments where the session pin is already broken or
7
+ * reopened, so affinity never smashes a warm pin:
8
+ *
9
+ * - FR-008 pin breaks (compaction, context overflow, cache economics, ...)
10
+ * - SAAR idle reopen (`saar_idle_reopen`)
11
+ *
12
+ * Hysteresis (Colibri-style): a swap toward the heat-preferred tier requires
13
+ * a success-rate advantage of at least `hysteresis_band` (~25%) over the
14
+ * active tier, and at most `swap_cap` live swaps per session. Heat counts are
15
+ * decayed at each swap so stale workload history cannot pin the affinity
16
+ * forever.
17
+ *
18
+ * Default OFF (`live_update_enabled: false`) — the persisted histogram and
19
+ * the first-turn soft bias (expected-cost.ts) work without live repinning.
20
+ * This module never flips frugality defaults or absolute release gates.
21
+ */
22
+ // ─── Pin-safe boundary detection ─────────────────────────────────────────────
23
+ /**
24
+ * True when a pin lookup result represents a pin-safe boundary: the pin is
25
+ * already broken or reopened, so a live affinity update cannot smash a warm
26
+ * pin. Hard-locked / sub-routed / in-use pins are NOT safe boundaries.
27
+ */
28
+ export function isPinSafeBoundary(result) {
29
+ if (result.action === 'break') {
30
+ return true;
31
+ }
32
+ return result.action === 'no_pin' && result.saarReason === 'saar_idle_reopen';
33
+ }
34
+ /** Heat decay factor applied at each live swap (Colibri decaying heat). */
35
+ export const AFFINITY_SWAP_HEAT_DECAY = 0.5;
36
+ /**
37
+ * Per-session live affinity controller. Evaluates heat-preferred tier swaps
38
+ * at pin-safe boundaries with hysteresis + swap cap. Stateless callers may
39
+ * construct one controller per process; sessions are bounded by pin lifecycle
40
+ * (`clearSession` on pin break/end).
41
+ */
42
+ export class HeatAffinityController {
43
+ config;
44
+ sessions = new Map();
45
+ constructor(config) {
46
+ this.config = config;
47
+ }
48
+ /** Read-only per-session affinity state (telemetry/tests). */
49
+ getState(sessionId) {
50
+ const state = this.sessions.get(sessionId);
51
+ return state ? { ...state } : null;
52
+ }
53
+ /** Drop session affinity state (pin break / session end). */
54
+ clearSession(sessionId) {
55
+ this.sessions.delete(sessionId);
56
+ }
57
+ /**
58
+ * Evaluate a live affinity swap at a pin-safe boundary.
59
+ *
60
+ * Gates, in order: live updates enabled → pin-safe boundary → heat data →
61
+ * not already active → hysteresis band → swap cap. On swap, the session's
62
+ * active tier moves and heat counts decay so the hot set tracks the live
63
+ * workload.
64
+ */
65
+ evaluateSwap(sessionId, boundary, key, currentTier, heat) {
66
+ if (!this.config.live_update_enabled) {
67
+ return {
68
+ shouldSwap: false,
69
+ targetTier: null,
70
+ reasonCode: 'live_update_disabled',
71
+ advantage: 0,
72
+ };
73
+ }
74
+ if (!isPinSafeBoundary(boundary)) {
75
+ return {
76
+ shouldSwap: false,
77
+ targetTier: null,
78
+ reasonCode: 'not_pin_safe_boundary',
79
+ advantage: 0,
80
+ };
81
+ }
82
+ const summaries = heat.summarize(key);
83
+ if (!summaries || summaries.length === 0) {
84
+ return {
85
+ shouldSwap: false,
86
+ targetTier: null,
87
+ reasonCode: 'no_heat_data',
88
+ advantage: 0,
89
+ };
90
+ }
91
+ const ranked = [...summaries].sort((a, b) => b.successRate - a.successRate);
92
+ const candidate = ranked[0];
93
+ if (candidate.attempts < this.config.min_samples) {
94
+ return {
95
+ shouldSwap: false,
96
+ targetTier: null,
97
+ reasonCode: 'no_heat_data',
98
+ advantage: 0,
99
+ };
100
+ }
101
+ const state = this.sessions.get(sessionId) ?? {
102
+ activeTier: currentTier,
103
+ swapsThisSession: 0,
104
+ };
105
+ if (candidate.tier === state.activeTier) {
106
+ this.sessions.set(sessionId, state);
107
+ return {
108
+ shouldSwap: false,
109
+ targetTier: null,
110
+ reasonCode: 'affinity_already_active',
111
+ advantage: 0,
112
+ };
113
+ }
114
+ const activeSummary = summaries.find((s) => s.tier === state.activeTier);
115
+ const activeRate = activeSummary?.successRate ?? 0;
116
+ const advantage = candidate.successRate - activeRate;
117
+ if (advantage < this.config.hysteresis_band) {
118
+ this.sessions.set(sessionId, state);
119
+ return {
120
+ shouldSwap: false,
121
+ targetTier: null,
122
+ reasonCode: 'hysteresis_hold',
123
+ advantage,
124
+ };
125
+ }
126
+ if (state.swapsThisSession >= this.config.swap_cap) {
127
+ return {
128
+ shouldSwap: false,
129
+ targetTier: null,
130
+ reasonCode: 'swap_cap_reached',
131
+ advantage,
132
+ };
133
+ }
134
+ state.activeTier = candidate.tier;
135
+ state.swapsThisSession += 1;
136
+ this.sessions.set(sessionId, state);
137
+ heat.decay(AFFINITY_SWAP_HEAT_DECAY);
138
+ return {
139
+ shouldSwap: true,
140
+ targetTier: candidate.tier,
141
+ reasonCode: 'heat_affinity_swap',
142
+ advantage,
143
+ };
144
+ }
145
+ }
146
+ //# sourceMappingURL=heat-affinity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heat-affinity.js","sourceRoot":"","sources":["../../../src/domain/pinning/heat-affinity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAuB;IACvD,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,KAAK,kBAAkB,CAAC;AAChF,CAAC;AA+BD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,OAAO,sBAAsB;IAChB,MAAM,CAAqB;IAC3B,QAAQ,GAAG,IAAI,GAAG,EAAuC,CAAC;IAE3E,YAAY,MAA0B;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,8DAA8D;IAC9D,QAAQ,CAAC,SAAiB;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACrC,CAAC;IAED,6DAA6D;IAC7D,YAAY,CAAC,SAAiB;QAC5B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAED;;;;;;;OAOG;IACH,YAAY,CACV,SAAiB,EACjB,QAAyB,EACzB,GAAoB,EACpB,WAAiB,EACjB,IAAqB;QAErB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;YACrC,OAAO;gBACL,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,sBAAsB;gBAClC,SAAS,EAAE,CAAC;aACb,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,OAAO;gBACL,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,uBAAuB;gBACnC,SAAS,EAAE,CAAC;aACb,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzC,OAAO;gBACL,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,cAAc;gBAC1B,SAAS,EAAE,CAAC;aACb,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;QAC5E,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QAC7B,IAAI,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACjD,OAAO;gBACL,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,cAAc;gBAC1B,SAAS,EAAE,CAAC;aACb,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI;YAC5C,UAAU,EAAE,WAAW;YACvB,gBAAgB,EAAE,CAAC;SACpB,CAAC;QAEF,IAAI,SAAS,CAAC,IAAI,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YACpC,OAAO;gBACL,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,yBAAyB;gBACrC,SAAS,EAAE,CAAC;aACb,CAAC;QACJ,CAAC;QAED,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,UAAU,CAAC,CAAC;QACzE,MAAM,UAAU,GAAG,aAAa,EAAE,WAAW,IAAI,CAAC,CAAC;QACnD,MAAM,SAAS,GAAG,SAAS,CAAC,WAAW,GAAG,UAAU,CAAC;QAErD,IAAI,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC5C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YACpC,OAAO;gBACL,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,iBAAiB;gBAC7B,SAAS;aACV,CAAC;QACJ,CAAC;QAED,IAAI,KAAK,CAAC,gBAAgB,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACnD,OAAO;gBACL,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,kBAAkB;gBAC9B,SAAS;aACV,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC;QAClC,KAAK,CAAC,gBAAgB,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAErC,OAAO;YACL,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,SAAS,CAAC,IAAI;YAC1B,UAAU,EAAE,oBAAoB;YAChC,SAAS;SACV,CAAC;IACJ,CAAC;CACF"}
@@ -24,6 +24,7 @@ import type { ClusterMatcher, ClusterMatchResult } from '../matching/cluster-mat
24
24
  import { type CompiledPatternPack, type DegradedRouteConfig, type LearnedRouteStore } from '../routing/degraded-route-sandwich.js';
25
25
  import { type IsotonicCalibratorArtifact } from '../routing/isotonic-calibrator.js';
26
26
  import { type PSuccessWeights } from '../routing/p-success-classifier.js';
27
+ import { SpeculativePrewarmGuard, type SpeculativePrewarmConfig } from '../routing/speculative-prewarm.js';
27
28
  export interface StageResult {
28
29
  readonly decided: boolean;
29
30
  readonly decision?: RoutingDecision;
@@ -90,6 +91,10 @@ export interface PipelineOptions {
90
91
  readonly localZeroConfig?: LocalZeroConfig;
91
92
  /** Degraded neural failover sandwich knobs (SP-212, #119). */
92
93
  readonly degradedRouteConfig?: DegradedRouteConfig;
94
+ /** Speculative prewarm knobs (SP-217, #117). Default off; fail open. */
95
+ readonly prewarmConfig?: SpeculativePrewarmConfig;
96
+ /** Injected prewarm guard for tests; lazily created from prewarmConfig when omitted. */
97
+ readonly prewarmGuard?: SpeculativePrewarmGuard;
93
98
  /** Privacy-safe learned map for the degraded sandwich (SP-212, #119). */
94
99
  readonly learnedRouteStore?: LearnedRouteStore;
95
100
  /** Compiled operator pattern pack overlay for the degraded sandwich (SP-212, #119). */
@@ -134,6 +139,10 @@ export declare class RouterPipeline {
134
139
  /** Degraded sandwich route path for explain/telemetry (SP-212, #119). */
135
140
  private currentRoutePath;
136
141
  private currentRoutePathConfidence;
142
+ /** Speculative prewarm outcome for explain/telemetry (SP-217, #117). */
143
+ private currentPrewarmOutcome;
144
+ /** Lazily created session-scoped prewarm guard (acceptance state spans routes). */
145
+ private prewarmGuardInstance;
137
146
  constructor(fleet: readonly ModelProfile[], options?: PipelineOptions);
138
147
  route(request: RoutingRequest, fleetOverride?: readonly ModelProfile[]): Promise<RoutingDecision>;
139
148
  /**
@@ -187,6 +196,16 @@ export declare class RouterPipeline {
187
196
  * min(local tool_use capability, local_zero.max_tool_use_requirement).
188
197
  */
189
198
  private localZeroTierStage;
199
+ private resolvePrewarmGuard;
200
+ /**
201
+ * Bounded speculative prewarm of the local runtime (Colibri PILOT pattern).
202
+ * Runs only when local_zero eligibility already passed and the operator
203
+ * opted in. Returns the warm readiness result to reuse when accepted; null
204
+ * otherwise (caller falls back to the normal readiness probe — fail open).
205
+ * Pre-generation only: the warm probe is an I/O readiness ping; it never
206
+ * waits on generated tokens.
207
+ */
208
+ private attemptSpeculativePrewarm;
190
209
  private triage;
191
210
  /**
192
211
  * Economical-cloud fallback for trivial prompts after local zero-tier is skipped
@@ -1 +1 @@
1
- {"version":3,"file":"router-pipeline.d.ts","sourceRoot":"","sources":["../../../src/domain/pipeline/router-pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router-pipeline.d.ts","sourceRoot":"","sources":["../../../src/domain/pipeline/router-pipeline.ts"],"names":[],"mappings":"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"}