pi-smart-router 0.1.0 → 0.2.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 (67) hide show
  1. package/.pi/extensions/smart-router/delegation-runtime.ts +12 -2
  2. package/README.md +55 -17
  3. package/dist/api/explain/router-explain.d.ts +4 -1
  4. package/dist/api/explain/router-explain.d.ts.map +1 -1
  5. package/dist/api/explain/router-explain.js +9 -9
  6. package/dist/api/explain/router-explain.js.map +1 -1
  7. package/dist/config/defaults.d.ts +4 -1
  8. package/dist/config/defaults.d.ts.map +1 -1
  9. package/dist/config/defaults.js +10 -0
  10. package/dist/config/defaults.js.map +1 -1
  11. package/dist/domain/delegation/delegation-context.d.ts +23 -1
  12. package/dist/domain/delegation/delegation-context.d.ts.map +1 -1
  13. package/dist/domain/delegation/delegation-context.js +93 -0
  14. package/dist/domain/delegation/delegation-context.js.map +1 -1
  15. package/dist/domain/pinning/cache-breakeven.d.ts +51 -0
  16. package/dist/domain/pinning/cache-breakeven.d.ts.map +1 -0
  17. package/dist/domain/pinning/cache-breakeven.js +124 -0
  18. package/dist/domain/pinning/cache-breakeven.js.map +1 -0
  19. package/dist/domain/pinning/saar-session-state.d.ts +36 -0
  20. package/dist/domain/pinning/saar-session-state.d.ts.map +1 -0
  21. package/dist/domain/pinning/saar-session-state.js +74 -0
  22. package/dist/domain/pinning/saar-session-state.js.map +1 -0
  23. package/dist/domain/pinning/session-pinner.d.ts +35 -3
  24. package/dist/domain/pinning/session-pinner.d.ts.map +1 -1
  25. package/dist/domain/pinning/session-pinner.js +149 -1
  26. package/dist/domain/pinning/session-pinner.js.map +1 -1
  27. package/dist/domain/pipeline/router-pipeline.d.ts +15 -1
  28. package/dist/domain/pipeline/router-pipeline.d.ts.map +1 -1
  29. package/dist/domain/pipeline/router-pipeline.js +118 -6
  30. package/dist/domain/pipeline/router-pipeline.js.map +1 -1
  31. package/dist/domain/routing/tool-history-guard.d.ts +21 -5
  32. package/dist/domain/routing/tool-history-guard.d.ts.map +1 -1
  33. package/dist/domain/routing/tool-history-guard.js +77 -10
  34. package/dist/domain/routing/tool-history-guard.js.map +1 -1
  35. package/dist/domain/types/entities.d.ts +48 -0
  36. package/dist/domain/types/entities.d.ts.map +1 -1
  37. package/dist/domain/types/index.d.ts +1 -1
  38. package/dist/domain/types/index.d.ts.map +1 -1
  39. package/dist/domain/types/schemas.d.ts +24 -0
  40. package/dist/domain/types/schemas.d.ts.map +1 -1
  41. package/dist/domain/types/schemas.js +54 -0
  42. package/dist/domain/types/schemas.js.map +1 -1
  43. package/dist/infra/gemini-provider.d.ts.map +1 -1
  44. package/dist/infra/gemini-provider.js +2 -1
  45. package/dist/infra/gemini-provider.js.map +1 -1
  46. package/dist/infrastructure/persistence/sqlite-store.d.ts.map +1 -1
  47. package/dist/infrastructure/persistence/sqlite-store.js +3 -0
  48. package/dist/infrastructure/persistence/sqlite-store.js.map +1 -1
  49. package/dist/infrastructure/telemetry/routing-telemetry.d.ts +30 -2
  50. package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -1
  51. package/dist/infrastructure/telemetry/routing-telemetry.js +221 -3
  52. package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -1
  53. package/package.json +5 -4
  54. package/src/api/explain/router-explain.ts +14 -10
  55. package/src/config/defaults.ts +18 -1
  56. package/src/domain/delegation/delegation-context.ts +124 -0
  57. package/src/domain/pinning/cache-breakeven.ts +219 -0
  58. package/src/domain/pinning/saar-session-state.ts +93 -0
  59. package/src/domain/pinning/session-pinner.ts +244 -3
  60. package/src/domain/pipeline/router-pipeline.ts +159 -7
  61. package/src/domain/routing/tool-history-guard.ts +108 -10
  62. package/src/domain/types/entities.ts +54 -0
  63. package/src/domain/types/index.ts +4 -0
  64. package/src/domain/types/schemas.ts +69 -0
  65. package/src/infra/gemini-provider.ts +2 -1
  66. package/src/infrastructure/persistence/sqlite-store.ts +6 -0
  67. package/src/infrastructure/telemetry/routing-telemetry.ts +327 -7
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Cache breakeven economics — SAAR gate (#73), routing-roadmap P0.
3
+ *
4
+ * Decides whether a proposed tier switch is justified when a warm prefix
5
+ * cache would be invalidated. Switch only when:
6
+ *
7
+ * marginal_savings + future_cache_value > cache_reprime_cost
8
+ *
9
+ * Distinct from legacy FR-008 warmup rule in `cache-economics.ts` (#32).
10
+ */
11
+ // ─── Defaults ─────────────────────────────────────────────────────────────────
12
+ const DEFAULT_PREFIX_CACHE_WEIGHT = 0.2;
13
+ const DEFAULT_PREFIX_CACHE_DISCOUNT = 0.9;
14
+ const TOKENS_PER_M = 1_000_000;
15
+ // ─── Component helpers ────────────────────────────────────────────────────────
16
+ function resolveSaarConfig(config) {
17
+ return {
18
+ prefixCacheWeight: config?.prefix_cache_weight ?? DEFAULT_PREFIX_CACHE_WEIGHT,
19
+ prefixCacheDiscount: config?.prefix_cache_discount ?? DEFAULT_PREFIX_CACHE_DISCOUNT,
20
+ };
21
+ }
22
+ function isFiniteNonNegative(value) {
23
+ return Number.isFinite(value) && value >= 0;
24
+ }
25
+ function isValidUsdComponent(value) {
26
+ return isFiniteNonNegative(value);
27
+ }
28
+ function isValidPrefixCacheWeight(weight) {
29
+ return Number.isFinite(weight) && weight >= 0 && weight <= 1;
30
+ }
31
+ function isValidPrefixCacheDiscount(discount) {
32
+ return Number.isFinite(discount) && discount >= 0 && discount <= 1;
33
+ }
34
+ function denySwitch(marginal_savings, future_cache_value, cache_reprime_cost, reason) {
35
+ return {
36
+ shouldSwitch: false,
37
+ marginal_savings,
38
+ future_cache_value,
39
+ cache_reprime_cost,
40
+ total_benefit: marginal_savings + future_cache_value,
41
+ reason,
42
+ };
43
+ }
44
+ /**
45
+ * Estimate the retained value of a warm prefix cache using SAAR
46
+ * `prefix_cache_weight`.
47
+ */
48
+ export function computeFutureCacheValue(warm_prefix_tokens, pinned_cost_per_1m, config) {
49
+ if (!isFiniteNonNegative(warm_prefix_tokens) || !isFiniteNonNegative(pinned_cost_per_1m)) {
50
+ return 0;
51
+ }
52
+ if (warm_prefix_tokens === 0) {
53
+ return 0;
54
+ }
55
+ const { prefixCacheWeight, prefixCacheDiscount } = resolveSaarConfig(config);
56
+ if (!isValidPrefixCacheWeight(prefixCacheWeight) || !isValidPrefixCacheDiscount(prefixCacheDiscount)) {
57
+ return 0;
58
+ }
59
+ const prefixCostUsd = (warm_prefix_tokens / TOKENS_PER_M) * pinned_cost_per_1m;
60
+ const discountedValueUsd = prefixCostUsd * prefixCacheDiscount;
61
+ return discountedValueUsd * prefixCacheWeight;
62
+ }
63
+ /**
64
+ * Cost to re-transmit a cold prefix on the candidate provider after a switch.
65
+ */
66
+ export function computeCacheReprimeCost(warm_prefix_tokens, candidate_cost_per_1m) {
67
+ if (!isFiniteNonNegative(warm_prefix_tokens) || !isFiniteNonNegative(candidate_cost_per_1m)) {
68
+ return 0;
69
+ }
70
+ if (warm_prefix_tokens === 0) {
71
+ return 0;
72
+ }
73
+ return (warm_prefix_tokens / TOKENS_PER_M) * candidate_cost_per_1m;
74
+ }
75
+ // ─── Evaluation ───────────────────────────────────────────────────────────────
76
+ /**
77
+ * Evaluate whether a proposed switch clears the cache breakeven gate.
78
+ *
79
+ * Returns `shouldSwitch: false` on invalid inputs (fail-safe deny).
80
+ */
81
+ export function evaluateCacheBreakeven(marginal_savings, future_cache_value, cache_reprime_cost) {
82
+ if (!isValidUsdComponent(marginal_savings) ||
83
+ !isValidUsdComponent(future_cache_value) ||
84
+ !isValidUsdComponent(cache_reprime_cost)) {
85
+ return denySwitch(marginal_savings, future_cache_value, cache_reprime_cost, 'invalid_input');
86
+ }
87
+ const total_benefit = marginal_savings + future_cache_value;
88
+ if (total_benefit <= cache_reprime_cost) {
89
+ return {
90
+ shouldSwitch: false,
91
+ marginal_savings,
92
+ future_cache_value,
93
+ cache_reprime_cost,
94
+ total_benefit,
95
+ reason: 'breakeven_not_met',
96
+ };
97
+ }
98
+ return {
99
+ shouldSwitch: true,
100
+ marginal_savings,
101
+ future_cache_value,
102
+ cache_reprime_cost,
103
+ total_benefit,
104
+ reason: 'breakeven_pass',
105
+ };
106
+ }
107
+ /**
108
+ * Derive breakeven components from prefix token economics, then evaluate.
109
+ */
110
+ export function evaluateCacheBreakevenForPrefix(marginal_savings, warm_prefix_tokens, pinned_cost_per_1m, candidate_cost_per_1m, config) {
111
+ const { prefixCacheWeight, prefixCacheDiscount } = resolveSaarConfig(config);
112
+ if (!isValidUsdComponent(marginal_savings) ||
113
+ !isFiniteNonNegative(warm_prefix_tokens) ||
114
+ !isFiniteNonNegative(pinned_cost_per_1m) ||
115
+ !isFiniteNonNegative(candidate_cost_per_1m) ||
116
+ !isValidPrefixCacheWeight(prefixCacheWeight) ||
117
+ !isValidPrefixCacheDiscount(prefixCacheDiscount)) {
118
+ return denySwitch(marginal_savings, 0, 0, 'invalid_input');
119
+ }
120
+ const future_cache_value = computeFutureCacheValue(warm_prefix_tokens, pinned_cost_per_1m, config);
121
+ const cache_reprime_cost = computeCacheReprimeCost(warm_prefix_tokens, candidate_cost_per_1m);
122
+ return evaluateCacheBreakeven(marginal_savings, future_cache_value, cache_reprime_cost);
123
+ }
124
+ //# sourceMappingURL=cache-breakeven.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-breakeven.js","sourceRoot":"","sources":["../../../src/domain/pinning/cache-breakeven.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * SAAR per-session state tracker (SP-122, #72).
3
+ *
4
+ * Tracks turn index, planning buffer window, hard-lock, and idle timeout
5
+ * per Session-Aware Agentic Routing semantics.
6
+ */
7
+ import type { SaarConfig, SaarSessionState } from '../types/index.js';
8
+ export interface SaarSessionStateTrackerOptions {
9
+ readonly config: SaarConfig;
10
+ readonly initial?: Partial<SaarSessionState>;
11
+ /** Injectable clock for tests. */
12
+ readonly now?: () => number;
13
+ }
14
+ export declare class SaarSessionStateTracker {
15
+ private state;
16
+ private readonly config;
17
+ private readonly now;
18
+ constructor(options: SaarSessionStateTrackerOptions);
19
+ getState(): Readonly<SaarSessionState>;
20
+ /**
21
+ * Advance turn index after a routed turn and refresh hard-lock.
22
+ * Hard-lock engages once turn_index reaches planning_turn_buffer.
23
+ */
24
+ recordTurn(): SaarSessionState;
25
+ /** Turns 0..(planning_turn_buffer - 1) are inside the planning buffer. */
26
+ isInBufferWindow(): boolean;
27
+ /** True after the planning buffer has been exhausted. */
28
+ shouldHardLock(): boolean;
29
+ /** True when idle time since last activity exceeds configured timeout. */
30
+ isIdleExpired(): boolean;
31
+ /** Reset SAAR weight and reopen routing after idle timeout. */
32
+ resetForIdleReopen(): SaarSessionState;
33
+ /** Refresh last-activity without advancing turn index. */
34
+ touchActivity(): void;
35
+ }
36
+ //# sourceMappingURL=saar-session-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saar-session-state.d.ts","sourceRoot":"","sources":["../../../src/domain/pinning/saar-session-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEtE,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC7C,kCAAkC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAMD,qBAAa,uBAAuB;IAClC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,OAAO,EAAE,8BAA8B;IAYnD,QAAQ,IAAI,QAAQ,CAAC,gBAAgB,CAAC;IAItC;;;OAGG;IACH,UAAU,IAAI,gBAAgB;IAU9B,0EAA0E;IAC1E,gBAAgB,IAAI,OAAO;IAI3B,yDAAyD;IACzD,cAAc,IAAI,OAAO;IAIzB,0EAA0E;IAC1E,aAAa,IAAI,OAAO;IASxB,+DAA+D;IAC/D,kBAAkB,IAAI,gBAAgB;IAStC,0DAA0D;IAC1D,aAAa,IAAI,IAAI;CAMtB"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * SAAR per-session state tracker (SP-122, #72).
3
+ *
4
+ * Tracks turn index, planning buffer window, hard-lock, and idle timeout
5
+ * per Session-Aware Agentic Routing semantics.
6
+ */
7
+ function toIso(ms) {
8
+ return new Date(ms).toISOString();
9
+ }
10
+ export class SaarSessionStateTracker {
11
+ state;
12
+ config;
13
+ now;
14
+ constructor(options) {
15
+ const clock = options.now ?? (() => Date.now());
16
+ this.now = clock;
17
+ this.config = options.config;
18
+ this.state = {
19
+ turn_index: options.initial?.turn_index ?? 0,
20
+ hard_lock: options.initial?.hard_lock ?? false,
21
+ last_activity_at: options.initial?.last_activity_at ?? toIso(clock()),
22
+ };
23
+ }
24
+ getState() {
25
+ return this.state;
26
+ }
27
+ /**
28
+ * Advance turn index after a routed turn and refresh hard-lock.
29
+ * Hard-lock engages once turn_index reaches planning_turn_buffer.
30
+ */
31
+ recordTurn() {
32
+ const nextIndex = this.state.turn_index + 1;
33
+ this.state = {
34
+ turn_index: nextIndex,
35
+ hard_lock: nextIndex >= this.config.planning_turn_buffer,
36
+ last_activity_at: toIso(this.now()),
37
+ };
38
+ return this.state;
39
+ }
40
+ /** Turns 0..(planning_turn_buffer - 1) are inside the planning buffer. */
41
+ isInBufferWindow() {
42
+ return this.state.turn_index < this.config.planning_turn_buffer;
43
+ }
44
+ /** True after the planning buffer has been exhausted. */
45
+ shouldHardLock() {
46
+ return this.state.hard_lock;
47
+ }
48
+ /** True when idle time since last activity exceeds configured timeout. */
49
+ isIdleExpired() {
50
+ const lastMs = Date.parse(this.state.last_activity_at);
51
+ if (Number.isNaN(lastMs)) {
52
+ return false;
53
+ }
54
+ const idleMs = this.config.idle_timeout_seconds * 1000;
55
+ return this.now() - lastMs >= idleMs;
56
+ }
57
+ /** Reset SAAR weight and reopen routing after idle timeout. */
58
+ resetForIdleReopen() {
59
+ this.state = {
60
+ turn_index: 0,
61
+ hard_lock: false,
62
+ last_activity_at: toIso(this.now()),
63
+ };
64
+ return this.state;
65
+ }
66
+ /** Refresh last-activity without advancing turn index. */
67
+ touchActivity() {
68
+ this.state = {
69
+ ...this.state,
70
+ last_activity_at: toIso(this.now()),
71
+ };
72
+ }
73
+ }
74
+ //# sourceMappingURL=saar-session-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saar-session-state.js","sourceRoot":"","sources":["../../../src/domain/pinning/saar-session-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,OAAO,uBAAuB;IAC1B,KAAK,CAAmB;IACf,MAAM,CAAa;IACnB,GAAG,CAAe;IAEnC,YAAY,OAAuC;QACjD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG;YACX,UAAU,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,IAAI,CAAC;YAC5C,SAAS,EAAE,OAAO,CAAC,OAAO,EAAE,SAAS,IAAI,KAAK;YAC9C,gBAAgB,EACd,OAAO,CAAC,OAAO,EAAE,gBAAgB,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;SACtD,CAAC;IACJ,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK,GAAG;YACX,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,oBAAoB;YACxD,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;SACpC,CAAC;QACF,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,0EAA0E;IAC1E,gBAAgB;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;IAClE,CAAC;IAED,yDAAyD;IACzD,cAAc;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;IAC9B,CAAC;IAED,0EAA0E;IAC1E,aAAa;QACX,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACvD,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACvD,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,CAAC;IACvC,CAAC;IAED,+DAA+D;IAC/D,kBAAkB;QAChB,IAAI,CAAC,KAAK,GAAG;YACX,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,KAAK;YAChB,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;SACpC,CAAC;QACF,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,0DAA0D;IAC1D,aAAa;QACX,IAAI,CAAC,KAAK,GAAG;YACX,GAAG,IAAI,CAAC,KAAK;YACb,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;SACpC,CAAC;IACJ,CAAC;CACF"}
@@ -14,14 +14,18 @@
14
14
  * Sub-routing (FR-024): tool-result turns below the payload threshold
15
15
  * may use an economical model on the same provider without breaking the pin.
16
16
  */
17
- import type { ModelProfile, PinReason, RoutingRequest, SessionPin } from '../types/index.js';
17
+ import type { ModelProfile, PinReason, RoutingRequest, SaarConfig, SaarSessionState, SessionPin } from '../types/index.js';
18
18
  import type { StorePort } from '../types/store-port.js';
19
19
  import { type CacheEconomicsConfig } from './cache-economics.js';
20
- export type PinAction = 'use_pin' | 'sub_route' | 'break' | 'no_pin';
20
+ import { type CacheBreakevenResult } from './cache-breakeven.js';
21
+ export type PinAction = 'use_pin' | 'sub_route' | 'saar_route' | 'break' | 'no_pin';
22
+ export type PinSaarReason = 'saar_buffer_active' | 'saar_hard_lock' | 'saar_idle_reopen' | 'saar_tier_upgrade';
21
23
  export interface PinLookupResult {
22
24
  readonly action: PinAction;
23
25
  readonly pinnedModel?: ModelProfile;
24
26
  readonly subRouteModel?: ModelProfile;
27
+ readonly saarRouteModel?: ModelProfile;
28
+ readonly saarReason?: PinSaarReason;
25
29
  readonly breakReason?: PinReason;
26
30
  }
27
31
  export interface SessionPinnerConfig {
@@ -29,19 +33,38 @@ export interface SessionPinnerConfig {
29
33
  readonly toolResultSizeThreshold?: number;
30
34
  /** Optional persistence — pins survive process restart when set. */
31
35
  readonly store?: StorePort;
32
- /** FR-008 rule #4: cache-warmup economics thresholds. */
36
+ /**
37
+ * FR-008 rule #4: cache-warmup economics thresholds.
38
+ */
33
39
  readonly cacheEconomicsConfig?: CacheEconomicsConfig;
40
+ /** SAAR pin policy (SP-122). When omitted, SAAR behavior is disabled. */
41
+ readonly saarConfig?: SaarConfig;
42
+ /** Injectable clock for SAAR idle-timeout tests. */
43
+ readonly saarClock?: () => number;
34
44
  /**
35
45
  * Break pin when estimated input tokens exceed the pinned model's
36
46
  * max_input_tokens multiplied by this margin. Default 0.90.
37
47
  */
38
48
  readonly contextOverflowSafetyMargin?: number;
39
49
  }
50
+ /**
51
+ * Per-turn marginal savings from switching pinned → candidate on this request.
52
+ * Uses full estimated input (same basis as cache-economics per-turn savings).
53
+ */
54
+ export declare function computeMarginalSwitchSavings(pinnedModel: ModelProfile, candidateModel: ModelProfile, estimatedInputTokens: number): number;
55
+ /**
56
+ * SAAR cache breakeven gate (#73) — shared by turn_envelope and session_pin.
57
+ * `warmPrefixTokens` is 0 on cold sessions (no pin); otherwise the warm prefix size.
58
+ */
59
+ export declare function evaluateModelSwitchBreakeven(pinnedModel: ModelProfile, candidateModel: ModelProfile, estimatedInputTokens: number, warmPrefixTokens: number, saarConfig?: SaarConfig): CacheBreakevenResult;
40
60
  export declare class SessionPinner {
41
61
  private readonly pins;
62
+ private readonly saarTrackers;
42
63
  private readonly toolResultSizeThreshold;
43
64
  private readonly store;
44
65
  private readonly cacheEconomicsConfig;
66
+ private readonly saarConfig;
67
+ private readonly saarClock;
45
68
  private readonly contextOverflowSafetyMargin;
46
69
  constructor(config?: SessionPinnerConfig);
47
70
  /**
@@ -62,6 +85,13 @@ export declare class SessionPinner {
62
85
  * Delete a session pin — used by loop escalation or external callers.
63
86
  */
64
87
  breakPin(sessionId: string): void;
88
+ /**
89
+ * Record a completed turn for SAAR turn-index and hard-lock tracking.
90
+ * Pipeline callers invoke after routing (SP-123).
91
+ */
92
+ recordSaarTurn(sessionId: string): SaarSessionState | null;
93
+ /** Read-only SAAR runtime state for telemetry (SP-126). */
94
+ getSaarState(sessionId: string): SaarSessionState | null;
65
95
  /**
66
96
  * Hydrate a pin from persistent storage (e.g. SQLite restore).
67
97
  */
@@ -70,6 +100,8 @@ export declare class SessionPinner {
70
100
  * Read-only access to the current pin (telemetry, inspection).
71
101
  */
72
102
  getPin(sessionId: string): SessionPin | null;
103
+ private getOrCreateSaarTracker;
104
+ private evaluateSaarPolicy;
73
105
  private evaluateBreakRules;
74
106
  private evaluateContextOverflowBreak;
75
107
  private evaluateCacheEconomicsBreak;
@@ -1 +1 @@
1
- {"version":3,"file":"session-pinner.d.ts","sourceRoot":"","sources":["../../../src/domain/pinning/session-pinner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,EACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAI9B,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,YAAY,CAAC;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,YAAY,CAAC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,wFAAwF;IACxF,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,oEAAoE;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,yDAAyD;IACzD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IACrD;;;OAGG;IACH,QAAQ,CAAC,2BAA2B,CAAC,EAAE,MAAM,CAAC;CAC/C;AASD,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IACtD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAmC;IACxE,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAS;gBAEzC,MAAM,CAAC,EAAE,mBAAmB;IASxC;;;OAGG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWzD;;;OAGG;IACH,SAAS,CACP,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,SAAS,YAAY,EAAE,GAC7B,eAAe;IAmClB;;OAEG;IACH,SAAS,CACP,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,GAChB,UAAU;IAuBb;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKjC;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAK9B;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAM5C,OAAO,CAAC,kBAAkB;IA4C1B,OAAO,CAAC,4BAA4B;IAyBpC,OAAO,CAAC,2BAA2B;IA+CnC,OAAO,CAAC,mBAAmB;IAqB3B,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,kBAAkB;CAuC3B"}
1
+ {"version":3,"file":"session-pinner.d.ts","sourceRoot":"","sources":["../../../src/domain/pinning/session-pinner.ts"],"names":[],"mappings":"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"}
@@ -14,22 +14,75 @@
14
14
  * Sub-routing (FR-024): tool-result turns below the payload threshold
15
15
  * may use an economical model on the same provider without breaking the pin.
16
16
  */
17
+ import { DEFAULT_SAAR_CONFIG } from '../types/schemas.js';
17
18
  import { evaluateCacheEconomics, } from './cache-economics.js';
19
+ import { evaluateCacheBreakevenForPrefix, } from './cache-breakeven.js';
20
+ import { SaarSessionStateTracker } from './saar-session-state.js';
18
21
  // ─── Defaults ─────────────────────────────────────────────────────────────────
19
22
  const DEFAULT_TOOL_RESULT_SIZE_THRESHOLD = 2048;
20
23
  const DEFAULT_CONTEXT_OVERFLOW_SAFETY_MARGIN = 0.9;
24
+ const TIER_RANK = {
25
+ 'zero-tier': 0,
26
+ 'economical-cloud': 1,
27
+ 'frontier-cloud': 2,
28
+ };
29
+ function isTierUpgrade(from, to) {
30
+ return TIER_RANK[to] > TIER_RANK[from];
31
+ }
32
+ function isToolLoopTurn(turnType) {
33
+ return turnType === 'main_loop' || turnType === 'tool_result';
34
+ }
35
+ const TOKENS_PER_M = 1_000_000;
36
+ /**
37
+ * Per-turn marginal savings from switching pinned → candidate on this request.
38
+ * Uses full estimated input (same basis as cache-economics per-turn savings).
39
+ */
40
+ export function computeMarginalSwitchSavings(pinnedModel, candidateModel, estimatedInputTokens) {
41
+ if (!Number.isFinite(estimatedInputTokens) || estimatedInputTokens < 0) {
42
+ return 0;
43
+ }
44
+ return (((pinnedModel.pricing.fallback_cost_per_1m -
45
+ candidateModel.pricing.fallback_cost_per_1m) *
46
+ estimatedInputTokens) /
47
+ TOKENS_PER_M);
48
+ }
49
+ /**
50
+ * SAAR cache breakeven gate (#73) — shared by turn_envelope and session_pin.
51
+ * `warmPrefixTokens` is 0 on cold sessions (no pin); otherwise the warm prefix size.
52
+ */
53
+ export function evaluateModelSwitchBreakeven(pinnedModel, candidateModel, estimatedInputTokens, warmPrefixTokens, saarConfig) {
54
+ if (pinnedModel.id === candidateModel.id) {
55
+ return {
56
+ shouldSwitch: true,
57
+ marginal_savings: 0,
58
+ future_cache_value: 0,
59
+ cache_reprime_cost: 0,
60
+ total_benefit: 0,
61
+ reason: 'breakeven_pass',
62
+ };
63
+ }
64
+ const marginal_savings = computeMarginalSwitchSavings(pinnedModel, candidateModel, estimatedInputTokens);
65
+ return evaluateCacheBreakevenForPrefix(marginal_savings, warmPrefixTokens, pinnedModel.pricing.fallback_cost_per_1m, candidateModel.pricing.fallback_cost_per_1m, saarConfig
66
+ ? { prefix_cache_weight: saarConfig.prefix_cache_weight }
67
+ : undefined);
68
+ }
21
69
  // ─── SessionPinner ────────────────────────────────────────────────────────────
22
70
  export class SessionPinner {
23
71
  pins = new Map();
72
+ saarTrackers = new Map();
24
73
  toolResultSizeThreshold;
25
74
  store;
26
75
  cacheEconomicsConfig;
76
+ saarConfig;
77
+ saarClock;
27
78
  contextOverflowSafetyMargin;
28
79
  constructor(config) {
29
80
  this.toolResultSizeThreshold =
30
81
  config?.toolResultSizeThreshold ?? DEFAULT_TOOL_RESULT_SIZE_THRESHOLD;
31
82
  this.store = config?.store;
32
83
  this.cacheEconomicsConfig = config?.cacheEconomicsConfig;
84
+ this.saarConfig = config?.saarConfig;
85
+ this.saarClock = config?.saarClock;
33
86
  this.contextOverflowSafetyMargin =
34
87
  config?.contextOverflowSafetyMargin ?? DEFAULT_CONTEXT_OVERFLOW_SAFETY_MARGIN;
35
88
  }
@@ -60,6 +113,11 @@ export class SessionPinner {
60
113
  if (breakResult) {
61
114
  return breakResult;
62
115
  }
116
+ // ── SAAR policy (SP-122) ────────────────────────────────────────────────
117
+ const saarResult = this.evaluateSaarPolicy(request, pin, fleet);
118
+ if (saarResult) {
119
+ return saarResult;
120
+ }
63
121
  // ── Sub-routing (FR-024) ────────────────────────────────────────────────
64
122
  const subRouteResult = this.evaluateSubRouting(request, pin, fleet);
65
123
  if (subRouteResult) {
@@ -101,8 +159,24 @@ export class SessionPinner {
101
159
  */
102
160
  breakPin(sessionId) {
103
161
  this.pins.delete(sessionId);
162
+ this.saarTrackers.delete(sessionId);
104
163
  this.deletePersistedPin(sessionId);
105
164
  }
165
+ /**
166
+ * Record a completed turn for SAAR turn-index and hard-lock tracking.
167
+ * Pipeline callers invoke after routing (SP-123).
168
+ */
169
+ recordSaarTurn(sessionId) {
170
+ if (!this.saarConfig) {
171
+ return null;
172
+ }
173
+ const tracker = this.getOrCreateSaarTracker(sessionId);
174
+ return tracker.recordTurn();
175
+ }
176
+ /** Read-only SAAR runtime state for telemetry (SP-126). */
177
+ getSaarState(sessionId) {
178
+ return this.saarTrackers.get(sessionId)?.getState() ?? null;
179
+ }
106
180
  /**
107
181
  * Hydrate a pin from persistent storage (e.g. SQLite restore).
108
182
  */
@@ -116,6 +190,71 @@ export class SessionPinner {
116
190
  getPin(sessionId) {
117
191
  return this.pins.get(sessionId) ?? null;
118
192
  }
193
+ // ─── SAAR policy (SP-122) ─────────────────────────────────────────────────
194
+ getOrCreateSaarTracker(sessionId) {
195
+ let tracker = this.saarTrackers.get(sessionId);
196
+ if (!tracker) {
197
+ const options = {
198
+ config: this.saarConfig ?? DEFAULT_SAAR_CONFIG,
199
+ };
200
+ if (this.saarClock) {
201
+ options.now = this.saarClock;
202
+ }
203
+ tracker = new SaarSessionStateTracker(options);
204
+ this.saarTrackers.set(sessionId, tracker);
205
+ }
206
+ return tracker;
207
+ }
208
+ evaluateSaarPolicy(request, pin, fleet) {
209
+ if (!this.saarConfig) {
210
+ return null;
211
+ }
212
+ const tracker = this.getOrCreateSaarTracker(request.session_id);
213
+ if (tracker.isIdleExpired()) {
214
+ tracker.resetForIdleReopen();
215
+ this.breakPin(request.session_id);
216
+ return { action: 'no_pin', saarReason: 'saar_idle_reopen' };
217
+ }
218
+ tracker.touchActivity();
219
+ const pinnedModel = fleet.find((m) => m.id === pin.pinned_model_id && m.healthy !== false);
220
+ if (!pinnedModel) {
221
+ return null;
222
+ }
223
+ const candidate = request.candidate_model_id
224
+ ? fleet.find((m) => m.id === request.candidate_model_id && m.healthy !== false)
225
+ : undefined;
226
+ if (tracker.isInBufferWindow() && candidate) {
227
+ if (isTierUpgrade(pinnedModel.tier, candidate.tier)) {
228
+ return {
229
+ action: 'saar_route',
230
+ pinnedModel,
231
+ saarRouteModel: candidate,
232
+ saarReason: 'saar_buffer_active',
233
+ };
234
+ }
235
+ }
236
+ if (tracker.shouldHardLock()) {
237
+ if (isToolLoopTurn(request.turn_type) &&
238
+ candidate &&
239
+ isTierUpgrade(pinnedModel.tier, candidate.tier)) {
240
+ this.recordPin(request.session_id, candidate.id, pin.pin_reason);
241
+ const upgraded = fleet.find((m) => m.id === candidate.id);
242
+ return {
243
+ action: 'use_pin',
244
+ pinnedModel: upgraded,
245
+ saarReason: 'saar_tier_upgrade',
246
+ };
247
+ }
248
+ if (candidate && candidate.id !== pin.pinned_model_id) {
249
+ return {
250
+ action: 'use_pin',
251
+ pinnedModel,
252
+ saarReason: 'saar_hard_lock',
253
+ };
254
+ }
255
+ }
256
+ return null;
257
+ }
119
258
  // ─── Break rules (FR-008) ───────────────────────────────────────────────────
120
259
  evaluateBreakRules(request, pin, fleet) {
121
260
  // 1. History compaction → break pin, allow full re-route
@@ -175,10 +314,14 @@ export class SessionPinner {
175
314
  const tokenEstimate = request.estimated_input_tokens ?? request.prompt_text.length;
176
315
  const econ = evaluateCacheEconomics(pin, pinnedModel, candidate, tokenEstimate, this.cacheEconomicsConfig);
177
316
  if (econ.shouldSwitch) {
317
+ const breakeven = evaluateModelSwitchBreakeven(pinnedModel, candidate, tokenEstimate, tokenEstimate, this.saarConfig);
318
+ if (!breakeven.shouldSwitch) {
319
+ return null;
320
+ }
178
321
  this.breakPin(request.session_id);
179
322
  return { action: 'break', breakReason: 'cache_economics' };
180
323
  }
181
- return { action: 'use_pin', pinnedModel };
324
+ return null;
182
325
  }
183
326
  handleForceOverride(request, _pin, fleet) {
184
327
  const forced = fleet.find((m) => m.id === request.force_model_id && m.healthy !== false);
@@ -227,6 +370,11 @@ export class SessionPinner {
227
370
  if (!econModel) {
228
371
  return null;
229
372
  }
373
+ const tokenEstimate = request.estimated_input_tokens ?? request.prompt_text.length;
374
+ const breakeven = evaluateModelSwitchBreakeven(pinnedModel, econModel, tokenEstimate, tokenEstimate, this.saarConfig);
375
+ if (!breakeven.shouldSwitch) {
376
+ return null;
377
+ }
230
378
  return {
231
379
  action: 'sub_route',
232
380
  subRouteModel: econModel,
@@ -1 +1 @@
1
- {"version":3,"file":"session-pinner.js","sourceRoot":"","sources":["../../../src/domain/pinning/session-pinner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-pinner.js","sourceRoot":"","sources":["../../../src/domain/pinning/session-pinner.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@
8
8
  * → safe_default → context_overflow_fallback
9
9
  * Any stage failure falls back to safeCloudDefault(); never throws to host.
10
10
  */
11
- import type { ModelProfile, PriceCatalog, RoutingDecision, RoutingRequest, Tier } from '../types/index.js';
11
+ import type { ModelProfile, PriceCatalog, RoutingDecision, RoutingRequest, SaarConfig, Tier } from '../types/index.js';
12
12
  import type { LowIntensityConfig } from '../types/schemas.js';
13
13
  import type { HardwareProbeConfig, SystemInfo } from '../../infrastructure/hardware/hardware-probe.js';
14
14
  import type { HttpFetchPort, LocalZeroTierConfig } from '../../infrastructure/local/local-zero-tier.js';
@@ -62,6 +62,8 @@ export interface PipelineOptions {
62
62
  /** Preloaded P(success) weights for tests; lazy-loads artifact when omitted (SP-105). */
63
63
  readonly pSuccessWeights?: PSuccessWeights;
64
64
  readonly pSuccessWeightsPath?: string;
65
+ /** SAAR pin policy (SP-123). Must match sessionPinner.saarConfig when enabled. */
66
+ readonly saarConfig?: SaarConfig;
65
67
  }
66
68
  export declare class RouterPipeline {
67
69
  private readonly stages;
@@ -89,6 +91,8 @@ export declare class RouterPipeline {
89
91
  private currentContextFitViableCount;
90
92
  private contextOverflowPreferredProvider;
91
93
  private contextOverflowTriggered;
94
+ /** Internal breakeven gate reason for SP-126 explain wiring. */
95
+ private currentBreakevenReason;
92
96
  constructor(fleet: readonly ModelProfile[], options?: PipelineOptions);
93
97
  route(request: RoutingRequest, fleetOverride?: readonly ModelProfile[]): Promise<RoutingDecision>;
94
98
  /**
@@ -144,6 +148,16 @@ export declare class RouterPipeline {
144
148
  * Sub-routes and already-pinned decisions skip persistence.
145
149
  */
146
150
  private persistPinIfNeeded;
151
+ /** Persist pin updates and advance SAAR turn index after each routed turn (SP-123). */
152
+ private finalizeRoute;
153
+ private recordSaarTurnIfNeeded;
154
+ private enrichRequestWithSaarCandidate;
155
+ private shouldDeferPlanningForSaar;
156
+ /**
157
+ * SP-123: SAAR planning buffer explicitly allows frontier planning turns
158
+ * without breakeven gating (#73 composes with buffer, not replaces it).
159
+ */
160
+ private isSaarPlanningBufferActive;
147
161
  private static readonly TURN_TIER_MAP;
148
162
  private turnEnvelope;
149
163
  /**