pi-smart-router 0.19.4 → 0.20.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 (73) hide show
  1. package/.pi/extensions/smart-router/command-formatters.ts +19 -3
  2. package/.pi/extensions/smart-router/delegate-stream.ts +78 -1
  3. package/.pi/extensions/smart-router/delegation-runtime.ts +20 -4
  4. package/.pi/extensions/smart-router/extension-setup.ts +29 -0
  5. package/.pi/extensions/smart-router/planning-delegate.ts +23 -0
  6. package/.pi/extensions/smart-router/route-and-delegate.ts +25 -0
  7. package/.pi/extensions/smart-router/stream-delegation.ts +15 -0
  8. package/.pi/extensions/smart-router/types.ts +17 -0
  9. package/README.md +82 -1
  10. package/config/operator-config.json.example +10 -0
  11. package/dist/config/defaults.d.ts +2 -2
  12. package/dist/config/defaults.d.ts.map +1 -1
  13. package/dist/config/defaults.js +6 -3
  14. package/dist/config/defaults.js.map +1 -1
  15. package/dist/config/pi-model-mapper.d.ts.map +1 -1
  16. package/dist/config/pi-model-mapper.js +5 -0
  17. package/dist/config/pi-model-mapper.js.map +1 -1
  18. package/dist/domain/delegation/adaptive-reasoning.d.ts +114 -0
  19. package/dist/domain/delegation/adaptive-reasoning.d.ts.map +1 -0
  20. package/dist/domain/delegation/adaptive-reasoning.js +273 -0
  21. package/dist/domain/delegation/adaptive-reasoning.js.map +1 -0
  22. package/dist/domain/pricing/peak-pricing.d.ts +94 -0
  23. package/dist/domain/pricing/peak-pricing.d.ts.map +1 -0
  24. package/dist/domain/pricing/peak-pricing.js +144 -0
  25. package/dist/domain/pricing/peak-pricing.js.map +1 -0
  26. package/dist/domain/routing/expected-cost.d.ts +89 -0
  27. package/dist/domain/routing/expected-cost.d.ts.map +1 -1
  28. package/dist/domain/routing/expected-cost.js +138 -2
  29. package/dist/domain/routing/expected-cost.js.map +1 -1
  30. package/dist/domain/types/entities.d.ts +39 -0
  31. package/dist/domain/types/entities.d.ts.map +1 -1
  32. package/dist/domain/types/index.d.ts +2 -1
  33. package/dist/domain/types/index.d.ts.map +1 -1
  34. package/dist/domain/types/schemas.d.ts +64 -0
  35. package/dist/domain/types/schemas.d.ts.map +1 -1
  36. package/dist/domain/types/schemas.js +84 -0
  37. package/dist/domain/types/schemas.js.map +1 -1
  38. package/dist/domain/types/store-port.d.ts +16 -1
  39. package/dist/domain/types/store-port.d.ts.map +1 -1
  40. package/dist/infrastructure/persistence/sqlite-store.d.ts +16 -1
  41. package/dist/infrastructure/persistence/sqlite-store.d.ts.map +1 -1
  42. package/dist/infrastructure/persistence/sqlite-store.js +119 -4
  43. package/dist/infrastructure/persistence/sqlite-store.js.map +1 -1
  44. package/dist/infrastructure/pricing/price-broker.d.ts +7 -1
  45. package/dist/infrastructure/pricing/price-broker.d.ts.map +1 -1
  46. package/dist/infrastructure/pricing/price-broker.js +11 -5
  47. package/dist/infrastructure/pricing/price-broker.js.map +1 -1
  48. package/dist/infrastructure/telemetry/routing-telemetry.d.ts +69 -4
  49. package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -1
  50. package/dist/infrastructure/telemetry/routing-telemetry.js +132 -3
  51. package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -1
  52. package/dist/infrastructure/telemetry/session-stats.d.ts +49 -1
  53. package/dist/infrastructure/telemetry/session-stats.d.ts.map +1 -1
  54. package/dist/infrastructure/telemetry/session-stats.js +78 -6
  55. package/dist/infrastructure/telemetry/session-stats.js.map +1 -1
  56. package/package.json +2 -1
  57. package/skills/router-release-operator/SKILL.md +67 -12
  58. package/skills/router-release-operator/references/issue-intake-checklist.md +21 -8
  59. package/skills/router-release-operator/references/release-manifest-template.md +18 -7
  60. package/skills/router-release-operator/references/release-profiles.md +13 -0
  61. package/src/config/defaults.ts +8 -1
  62. package/src/config/pi-model-mapper.ts +5 -0
  63. package/src/domain/delegation/adaptive-reasoning.ts +407 -0
  64. package/src/domain/pricing/peak-pricing.ts +220 -0
  65. package/src/domain/routing/expected-cost.ts +240 -2
  66. package/src/domain/types/entities.ts +43 -0
  67. package/src/domain/types/index.ts +7 -0
  68. package/src/domain/types/schemas.ts +101 -0
  69. package/src/domain/types/store-port.ts +18 -1
  70. package/src/infrastructure/persistence/sqlite-store.ts +138 -5
  71. package/src/infrastructure/pricing/price-broker.ts +15 -4
  72. package/src/infrastructure/telemetry/routing-telemetry.ts +186 -6
  73. package/src/infrastructure/telemetry/session-stats.ts +126 -7
@@ -312,9 +312,16 @@ export function formatStatsMessage(
312
312
  return 'No routing stats yet (empty telemetry window).';
313
313
  }
314
314
 
315
+ const costBasisLabel =
316
+ snapshot.cost_basis === 'actual'
317
+ ? 'actual'
318
+ : snapshot.cost_basis === 'mixed'
319
+ ? `mixed (${snapshot.actual_usage_count}/${snapshot.entry_count} actual)`
320
+ : 'estimated';
321
+
315
322
  const lines = [
316
323
  `Entries: ${snapshot.entry_count}`,
317
- `Cost: total ${formatUsd(snapshot.total_cost_usd)} | mean ${formatMean(snapshot.mean_cost_usd, '')}`,
324
+ `Cost (${costBasisLabel}): total ${formatUsd(snapshot.total_cost_usd)} | mean ${formatMean(snapshot.mean_cost_usd, '')}`,
318
325
  `Latency: total ${snapshot.total_latency_ms.toFixed(0)}ms | mean ${formatMean(snapshot.mean_latency_ms, 'ms')}`,
319
326
  `Planning delegate share: ${formatShare(snapshot.planning_delegate_share)} (direct ${formatShare(snapshot.direct_share)})`,
320
327
  `Local vs cloud (when known): local ${formatShare(snapshot.local_share)} | cloud ${formatShare(snapshot.cloud_share)}`,
@@ -324,10 +331,19 @@ export function formatStatsMessage(
324
331
  ` other: ${snapshot.role_cost.other.count} | ${formatUsd(snapshot.role_cost.other.total_cost_usd)}`,
325
332
  ];
326
333
 
334
+ if (snapshot.actual_usage_count > 0) {
335
+ lines.push(
336
+ `Actual usage: ${snapshot.actual_usage_count}/${snapshot.entry_count} entries with host-reported tokens` +
337
+ (snapshot.actual_total_tokens !== null
338
+ ? ` | ${snapshot.actual_total_tokens} actual tokens`
339
+ : ''),
340
+ );
341
+ }
342
+
327
343
  if (snapshot.frontier_savings_usd !== undefined) {
328
344
  lines.push(
329
- `Vs always-frontier savings (est.): ${formatUsd(snapshot.frontier_savings_usd)}`,
330
- ' formula: sum max(0, tokens/1e6 * frontier_cost_per_1m - estimated_cost_usd); omitted when prices missing',
345
+ `Vs always-frontier savings: ${formatUsd(snapshot.frontier_savings_usd)} (${snapshot.cost_basis === 'estimated' ? 'est.' : 'actuals preferred, est. when missing'})`,
346
+ ' formula: sum max(0, tokens/1e6 * frontier_cost_per_1m - cost); tokens/cost prefer actuals; omitted when prices missing',
331
347
  );
332
348
  } else {
333
349
  lines.push('Vs always-frontier savings: (omitted — frontier prices unavailable)');
@@ -10,6 +10,12 @@ import {
10
10
  } from '@earendil-works/pi-ai/compat';
11
11
 
12
12
  import { parseAssistantMessageError } from '../../../src/infrastructure/delegation/provider-error.js';
13
+ import { extractUsageActuals } from '../../../src/infrastructure/telemetry/routing-telemetry.js';
14
+ import {
15
+ resolveAdaptiveReasoning,
16
+ type AdaptiveReasoningResult,
17
+ type AdaptiveReasoningSignal,
18
+ } from '../../../src/domain/delegation/adaptive-reasoning.js';
13
19
  import {
14
20
  buildDelegationContext,
15
21
  forwardDelegatedEvent,
@@ -89,6 +95,7 @@ export async function collectDelegatedStream(
89
95
  deps: StreamDelegationDeps,
90
96
  options: SimpleStreamOptions | undefined,
91
97
  headroomContext?: DelegationHeadroomContext,
98
+ reasoning?: AdaptiveReasoningResult,
92
99
  ): Promise<DelegatedStreamResult> {
93
100
  throwIfAborted(options);
94
101
 
@@ -97,6 +104,7 @@ export async function collectDelegatedStream(
97
104
  targetModel,
98
105
  options,
99
106
  headroomContext,
107
+ reasoning,
100
108
  );
101
109
  const delegateStream = resolveDelegateStream(targetModel, deps);
102
110
  const inner = delegateStream(targetModel, context, delegationOptions);
@@ -156,6 +164,7 @@ export async function pipeDelegatedStream(
156
164
  options: SimpleStreamOptions | undefined,
157
165
  headroomContext: DelegationHeadroomContext | undefined,
158
166
  pipe: PipeDelegatedStreamOptions,
167
+ reasoning?: AdaptiveReasoningResult,
159
168
  ): Promise<PipedDelegatedStreamResult> {
160
169
  throwIfAborted(options);
161
170
 
@@ -164,6 +173,7 @@ export async function pipeDelegatedStream(
164
173
  targetModel,
165
174
  options,
166
175
  headroomContext,
176
+ reasoning,
167
177
  );
168
178
  const delegateStream = resolveDelegateStream(targetModel, deps);
169
179
  const inner = delegateStream(targetModel, context, delegationOptions);
@@ -257,11 +267,29 @@ function recordDelegateOutcome(
257
267
  deps: StreamDelegationDeps,
258
268
  sessionId: string | undefined,
259
269
  result: DelegatedStreamResult,
270
+ requestId?: string,
260
271
  ): void {
261
272
  if (!result.finalMessage) {
262
273
  return;
263
274
  }
264
275
 
276
+ // SP-241 / #164: capture post-turn usage actuals (success and
277
+ // failed-with-usage terminals). Fail open — extraction/hook errors and
278
+ // missing usage must never fail the route.
279
+ if (requestId !== undefined) {
280
+ try {
281
+ const actuals = extractUsageActuals(result.finalMessage.usage);
282
+ if (actuals) {
283
+ deps.onDelegationUsage?.(requestId, actuals);
284
+ }
285
+ } catch (error) {
286
+ console.warn(
287
+ '[smart-router] usage actuals capture failed (fail open)',
288
+ error instanceof Error ? error.message : String(error),
289
+ );
290
+ }
291
+ }
292
+
265
293
  if (result.failed) {
266
294
  const parsed = parseAssistantMessageError(result.finalMessage);
267
295
  deps.router.dispatch.recordOutcome(targetModel.id, parsed);
@@ -282,6 +310,11 @@ function recordDelegateOutcome(
282
310
  /**
283
311
  * Delegate with outcome recording. When `pipe` is provided, live-forwards to outer
284
312
  * (holding the terminal event). Otherwise collects into a buffer (planning / probes).
313
+ *
314
+ * SP-245 (#166): when `reasoningSignal` is provided, the adaptive reasoning
315
+ * policy resolves the effective thinking level from turn signals and merges it
316
+ * into the delegated stream options (never lowering an explicit operator
317
+ * /thinking; fail open on non-reasoning models).
285
318
  */
286
319
  export async function delegateWithOutcome(
287
320
  targetModel: Model<Api>,
@@ -291,12 +324,54 @@ export async function delegateWithOutcome(
291
324
  sessionId: string | undefined,
292
325
  headroomContext?: DelegationHeadroomContext,
293
326
  pipe?: PipeDelegatedStreamOptions,
327
+ /** Routing request id for post-turn usage actuals capture (SP-241, #164). */
328
+ requestId?: string,
329
+ /** Turn/routing signals for adaptive reasoning (SP-245, #166). */
330
+ reasoningSignal?: AdaptiveReasoningSignal,
294
331
  ): Promise<PipedDelegatedStreamResult | DelegatedStreamResult> {
332
+ const reasoning = reasoningSignal
333
+ ? resolveAdaptiveReasoning(
334
+ targetModel,
335
+ reasoningSignal,
336
+ options?.reasoning,
337
+ // SP-246 (#166): operator enable/disable + floor/ceiling knobs.
338
+ deps.adaptiveReasoningConfig
339
+ ? {
340
+ enabled: deps.adaptiveReasoningConfig.enabled,
341
+ ...(deps.adaptiveReasoningConfig.min_level !== undefined
342
+ ? { floor: deps.adaptiveReasoningConfig.min_level }
343
+ : {}),
344
+ ...(deps.adaptiveReasoningConfig.max_level !== undefined
345
+ ? { ceiling: deps.adaptiveReasoningConfig.max_level }
346
+ : {}),
347
+ }
348
+ : undefined,
349
+ )
350
+ : undefined;
351
+
352
+ // SP-246 (#166): reasoning telemetry (requested / applied / reason code).
353
+ // Fail open — telemetry must never fail the route.
354
+ if (reasoningSignal && reasoning && requestId !== undefined) {
355
+ try {
356
+ deps.onDelegationReasoning?.(requestId, {
357
+ reasoning_level_requested: options?.reasoning ?? null,
358
+ reasoning_level_applied: reasoning.reasoning ?? null,
359
+ reasoning_reason_code: reasoning.reasonCode,
360
+ });
361
+ } catch (error) {
362
+ console.warn(
363
+ '[smart-router] reasoning telemetry callback failed (fail open)',
364
+ error instanceof Error ? error.message : String(error),
365
+ );
366
+ }
367
+ }
368
+
295
369
  const delegationContext = buildDelegationContext(
296
370
  context,
297
371
  targetModel,
298
372
  deps,
299
373
  sessionId,
374
+ reasoning,
300
375
  );
301
376
 
302
377
  const result = pipe
@@ -307,6 +382,7 @@ export async function delegateWithOutcome(
307
382
  options,
308
383
  headroomContext,
309
384
  pipe,
385
+ reasoning,
310
386
  )
311
387
  : await collectDelegatedStream(
312
388
  targetModel,
@@ -314,9 +390,10 @@ export async function delegateWithOutcome(
314
390
  deps,
315
391
  options,
316
392
  headroomContext,
393
+ reasoning,
317
394
  );
318
395
 
319
- recordDelegateOutcome(targetModel, deps, sessionId, result);
396
+ recordDelegateOutcome(targetModel, deps, sessionId, result, requestId);
320
397
 
321
398
  return result;
322
399
  }
@@ -13,6 +13,10 @@ import {
13
13
  } from '@earendil-works/pi-ai/compat';
14
14
  import type { ModelRegistry } from '@earendil-works/pi-coding-agent';
15
15
 
16
+ import {
17
+ applyConcisenessHint,
18
+ type AdaptiveReasoningResult,
19
+ } from '../../../src/domain/delegation/adaptive-reasoning.js';
16
20
  import {
17
21
  isGoogleDelegationTarget,
18
22
  normalizeDelegationContext,
@@ -87,6 +91,7 @@ export async function resolveDelegationOptions(
87
91
  targetModel: Model<Api>,
88
92
  callerOptions?: SimpleStreamOptions,
89
93
  headroomContext?: DelegationHeadroomContext,
94
+ reasoning?: AdaptiveReasoningResult,
90
95
  ): Promise<SimpleStreamOptions> {
91
96
  const auth = await modelRegistry.getApiKeyAndHeaders(targetModel);
92
97
  if (!auth.ok) {
@@ -124,6 +129,14 @@ export async function resolveDelegationOptions(
124
129
  ...(auth.headers !== undefined ? { headers: auth.headers } : {}),
125
130
  ...(mergedEnv !== undefined ? { env: mergedEnv } : {}),
126
131
  ...(maxTokens !== undefined ? { maxTokens } : {}),
132
+ // SP-245 (#166): adaptive reasoning policy merge — applied after the
133
+ // caller pick so the policy-effective level wins, while every other
134
+ // DELEGATION_CALLER_OPTION_KEYS entry (incl. thinkingBudgets) passes
135
+ // through untouched. Explicit operator /thinking floors are already
136
+ // resolved inside the policy result.
137
+ ...(reasoning?.reasoning !== undefined
138
+ ? { reasoning: reasoning.reasoning }
139
+ : {}),
127
140
  };
128
141
  }
129
142
 
@@ -146,6 +159,7 @@ export function buildDelegationContext(
146
159
  targetModel: Model<Api>,
147
160
  deps: StreamDelegationDeps,
148
161
  sessionId: string | undefined,
162
+ reasoning?: AdaptiveReasoningResult,
149
163
  ): Context {
150
164
  const sessionExecution = sessionId
151
165
  ? deps.executionLedger.getLastExecution(sessionId)
@@ -155,11 +169,13 @@ export function buildDelegationContext(
155
169
  sessionExecution,
156
170
  });
157
171
 
158
- if (isGoogleDelegationTarget(targetModel)) {
159
- return repairGeminiReplayContext(normalized, targetModel, sessionExecution);
160
- }
172
+ const repaired = isGoogleDelegationTarget(targetModel)
173
+ ? repairGeminiReplayContext(normalized, targetModel, sessionExecution)
174
+ : normalized;
161
175
 
162
- return normalized;
176
+ // SP-245 (#166): light conciseness nudge only when the policy resolved a
177
+ // low/minimal effective level on a high-verbosity (GLM-class) profile.
178
+ return applyConcisenessHint(repaired, reasoning);
163
179
  }
164
180
 
165
181
  export function createErrorMessage(
@@ -120,6 +120,9 @@ export async function createSmartRouterRuntime(cwd: string): Promise<{
120
120
  executionLedger,
121
121
  lifecycleHookState,
122
122
  planningDelegateConfig: operatorConfig.planning_delegate,
123
+ ...(operatorConfig.adaptive_reasoning !== undefined
124
+ ? { adaptiveReasoningConfig: operatorConfig.adaptive_reasoning }
125
+ : {}),
123
126
  datasetRecorder,
124
127
  outcomeRecorder,
125
128
  sessionPinner,
@@ -138,6 +141,32 @@ export async function createSmartRouterRuntime(cwd: string): Promise<{
138
141
  }
139
142
  runtime.syncRegisteredLimits?.(limits);
140
143
  },
144
+ onDelegationUsage(requestId, actuals) {
145
+ // SP-241 / #164: persist post-turn usage actuals onto the routing
146
+ // telemetry row. Fail open — a telemetry write must never fail the
147
+ // route, and stores without update support simply skip actuals.
148
+ try {
149
+ runtime.store.updateTelemetryUsageActuals?.(requestId, actuals);
150
+ } catch (error) {
151
+ console.warn(
152
+ '[smart-router] failed to persist usage actuals (fail open)',
153
+ error instanceof Error ? error.message : String(error),
154
+ );
155
+ }
156
+ },
157
+ onDelegationReasoning(requestId, fields) {
158
+ // SP-246 / #166: persist the adaptive reasoning decision (requested /
159
+ // applied level + reason code) onto the routing telemetry row. Fail
160
+ // open — telemetry must never fail the route.
161
+ try {
162
+ runtime.store.updateTelemetryReasoning?.(requestId, fields);
163
+ } catch (error) {
164
+ console.warn(
165
+ '[smart-router] failed to persist reasoning telemetry (fail open)',
166
+ error instanceof Error ? error.message : String(error),
167
+ );
168
+ }
169
+ },
141
170
  },
142
171
  };
143
172
 
@@ -27,6 +27,7 @@ import type {
27
27
  PlanningDelegateObservability,
28
28
  RoutingDecision,
29
29
  } from '../../../src/domain/types/index.js';
30
+ import { resolveAdaptiveReasoning } from '../../../src/domain/delegation/adaptive-reasoning.js';
30
31
  import { DEFAULT_PLANNING_DELEGATE_CONFIG } from '../../../src/domain/types/schemas.js';
31
32
  import {
32
33
  createPlanningDelegateObservability,
@@ -190,11 +191,33 @@ export async function defaultSpawnPlanningDelegate(
190
191
  deps: StreamDelegationDeps,
191
192
  ): Promise<PlanningDelegateSpawnResult> {
192
193
  try {
194
+ // SP-245 (#166): planning sub-calls keep higher reasoning — the adaptive
195
+ // policy resolves at least `medium` for planning turns (explicit operator
196
+ // /thinking floors still win inside the policy). SP-246: honor the
197
+ // operator enable/disable + floor/ceiling knobs.
198
+ const reasoning = resolveAdaptiveReasoning(
199
+ frontierModel,
200
+ { turnType: 'planning' },
201
+ options?.reasoning,
202
+ deps.adaptiveReasoningConfig
203
+ ? {
204
+ enabled: deps.adaptiveReasoningConfig.enabled,
205
+ ...(deps.adaptiveReasoningConfig.min_level !== undefined
206
+ ? { floor: deps.adaptiveReasoningConfig.min_level }
207
+ : {}),
208
+ ...(deps.adaptiveReasoningConfig.max_level !== undefined
209
+ ? { ceiling: deps.adaptiveReasoningConfig.max_level }
210
+ : {}),
211
+ }
212
+ : undefined,
213
+ );
193
214
  const result = await collectDelegatedStream(
194
215
  frontierModel,
195
216
  compressedContext,
196
217
  deps,
197
218
  options,
219
+ undefined,
220
+ reasoning,
198
221
  );
199
222
  if (result.failed || !result.finalMessage) {
200
223
  return {
@@ -9,6 +9,7 @@ import type {
9
9
 
10
10
  import { safeCloudDefault } from '../../../src/domain/pipeline/safe-default.js';
11
11
  import { computeOutputHeadroom } from '../../../src/domain/delegation/output-headroom.js';
12
+ import { resolvePeakPricingAdjustment } from '../../../src/domain/pricing/peak-pricing.js';
12
13
  import {
13
14
  CONTEXT_OVERFLOW_NO_FIT,
14
15
  resolveContextOverflowFallback,
@@ -84,6 +85,14 @@ function logRoutingDecision(
84
85
  return;
85
86
  }
86
87
 
88
+ // SP-244 / #165: surface peak vs off-peak pricing rationale for the selected
89
+ // model (Z.ai GLM Coding Plan / DeepSeek API windows; multiplier 1 + window
90
+ // 'none' for non-target providers — fail open).
91
+ const peakPricing = resolvePeakPricingAdjustment({
92
+ id: delegate?.modelId ?? decision.selected_model_id,
93
+ ...(delegate?.provider !== undefined ? { provider: delegate.provider } : {}),
94
+ });
95
+
87
96
  console.warn(
88
97
  '[smart-router] routing decision',
89
98
  JSON.stringify({
@@ -93,6 +102,12 @@ function logRoutingDecision(
93
102
  stage: decision.stage,
94
103
  reason_code: decision.reason_code,
95
104
  routing_latency_ms: decision.routing_latency_ms,
105
+ pricing_window: peakPricing.window,
106
+ peak_pricing: {
107
+ window: peakPricing.window,
108
+ cost_multiplier: peakPricing.cost_multiplier,
109
+ adapter_id: peakPricing.adapter_id,
110
+ },
96
111
  features: decision.features ?? null,
97
112
  delegate,
98
113
  }),
@@ -521,6 +536,15 @@ export async function routeAndDelegate(
521
536
  ...(failoverNotice !== undefined ? { failoverNotice } : {}),
522
537
  contextWindow: targetModel.contextWindow,
523
538
  },
539
+ decision.request_id,
540
+ // SP-245 (#166): adaptive reasoning policy — effective thinking level
541
+ // from this turn's envelope + routing decision (recomputed per
542
+ // failover iteration so escalation targets re-evaluate).
543
+ {
544
+ turnType: request.turn_type,
545
+ decision,
546
+ ...(targetProfile !== undefined ? { profile: targetProfile } : {}),
547
+ },
524
548
  );
525
549
 
526
550
  if (!isPipedResult(result)) {
@@ -761,6 +785,7 @@ export async function routeAndDelegate(
761
785
  ...(failoverNotice !== undefined ? { failoverNotice } : {}),
762
786
  contextWindow: fallbackModel.contextWindow,
763
787
  },
788
+ decision.request_id,
764
789
  );
765
790
  if (isPipedResult(fallbackResult)) {
766
791
  commitPipedTerminal(fallbackResult);
@@ -8,6 +8,7 @@ import {
8
8
  } from '@earendil-works/pi-ai/compat';
9
9
 
10
10
  import type { RoutingDecision, RoutingFeatureSidecar } from '../../../src/domain/types/index.js';
11
+ import { resolvePeakPricingAdjustment } from '../../../src/domain/pricing/peak-pricing.js';
11
12
  import { createErrorMessage } from './delegation-runtime.js';
12
13
  import { routeAndDelegate } from './route-and-delegate.js';
13
14
  import type { StreamDelegationDeps } from './types.js';
@@ -24,6 +25,14 @@ export function logRoutingDecision(
24
25
  return;
25
26
  }
26
27
 
28
+ // SP-244 / #165: surface peak vs off-peak pricing rationale for the selected
29
+ // model (Z.ai GLM Coding Plan / DeepSeek API windows; multiplier 1 + window
30
+ // 'none' for non-target providers — fail open).
31
+ const peakPricing = resolvePeakPricingAdjustment({
32
+ id: delegate?.modelId ?? decision.selected_model_id,
33
+ ...(delegate?.provider !== undefined ? { provider: delegate.provider } : {}),
34
+ });
35
+
27
36
  console.warn(
28
37
  '[smart-router] routing decision',
29
38
  JSON.stringify({
@@ -33,6 +42,12 @@ export function logRoutingDecision(
33
42
  stage: decision.stage,
34
43
  reason_code: decision.reason_code,
35
44
  routing_latency_ms: decision.routing_latency_ms,
45
+ pricing_window: peakPricing.window,
46
+ peak_pricing: {
47
+ window: peakPricing.window,
48
+ cost_multiplier: peakPricing.cost_multiplier,
49
+ adapter_id: peakPricing.adapter_id,
50
+ },
36
51
  features: decision.features ?? null,
37
52
  delegate,
38
53
  }),
@@ -11,10 +11,13 @@ import type { HydraMatcher } from '../../../src/domain/matching/hydra-matcher.js
11
11
  import { ExecutionLedger } from '../../../src/domain/delegation/execution-ledger.js';
12
12
  import { SessionPinner } from '../../../src/domain/pinning/session-pinner.js';
13
13
  import type {
14
+ AdaptiveReasoningConfig,
14
15
  ModelProfile,
15
16
  PlanningDelegateConfig,
16
17
  PriceCatalog,
17
18
  RoutingDecision,
19
+ RoutingReasoningTelemetry,
20
+ RoutingUsageActuals,
18
21
  } from '../../../src/domain/types/index.js';
19
22
  import type { PlanningDelegateSpawnFn } from './planning-delegate.js';
20
23
  import type { StorePort } from '../../../src/domain/types/store-port.js';
@@ -68,6 +71,8 @@ export interface StreamDelegationDeps {
68
71
  spawnPlanningDelegate?: PlanningDelegateSpawnFn;
69
72
  /** Planning delegate knobs incl. global + per-call timeout bounds (SP-213, #120). */
70
73
  readonly planningDelegateConfig?: PlanningDelegateConfig;
74
+ /** Adaptive reasoning knobs: enable/disable + floor/ceiling (SP-246, #166). */
75
+ readonly adaptiveReasoningConfig?: AdaptiveReasoningConfig;
71
76
  readonly lifecycleHookState?: LifecycleHookState;
72
77
  readonly datasetRecorder?: DatasetRecorder;
73
78
  readonly outcomeRecorder?: OutcomeRecorder;
@@ -82,6 +87,18 @@ export interface StreamDelegationDeps {
82
87
  readonly contextWindow?: number;
83
88
  readonly maxTokens?: number;
84
89
  }) => void;
90
+ /**
91
+ * Fired after a delegated stream ends with host-reported usage actuals
92
+ * (SP-241, #164). Also fires on failed-with-usage terminals. Implementations
93
+ * must not throw — actuals capture never fails the route.
94
+ */
95
+ onDelegationUsage?: (requestId: string, actuals: RoutingUsageActuals) => void;
96
+ /**
97
+ * Fired after the adaptive reasoning policy resolves the effective thinking
98
+ * level for a delegated stream (SP-246, #166). Implementations must not
99
+ * throw — reasoning telemetry must never fail the route.
100
+ */
101
+ onDelegationReasoning?: (requestId: string, fields: RoutingReasoningTelemetry) => void;
85
102
  }
86
103
 
87
104
  export interface SmartRouterRuntime {
package/README.md CHANGED
@@ -218,7 +218,7 @@ Cursor models bill against your **Cursor Pro subscription quota**, not per-token
218
218
  | `/smart-router` | Same as `status` (default when no subcommand is given) |
219
219
  | `/smart-router status` | Show fleet mode, fleet size, pricing freshness/staleness, and the last routing decision (stage, tier, selected model, latency) |
220
220
  | `/smart-router history` | Show recent routing telemetry from SQLite (default limit; optional numeric limit, e.g. `/smart-router history 20`). Displays the concrete delegated model id (never bare virtual `auto`) |
221
- | `/smart-router stats` | Privacy-safe session/window aggregates from routing telemetry: count, mean cost/latency, planning_delegate vs direct share, local vs cloud when distinguishable, and role cost breakdown (primary pin path / planning_delegate / other). Optional vs-always-frontier savings when frontier fleet prices exist (omitted otherwise). Optional numeric limit, e.g. `/smart-router stats 50` |
221
+ | `/smart-router stats` | Privacy-safe session/window aggregates from routing telemetry: count, mean cost/latency with `cost_basis` labeling (actual vs estimated, SP-241), planning_delegate vs direct share, local vs cloud when distinguishable, and role cost breakdown. Optional vs-always-frontier savings when frontier fleet prices exist (omitted otherwise). The JSON snapshot (`buildStatsSnapshot`, automation surface) additionally carries warm rolling `cost_calibration` actual/estimate buckets (SP-242). Optional numeric limit, e.g. `/smart-router stats 50` |
222
222
  | `/smart-router mode scoped` | Route only among pi's **enabled model patterns** (default) |
223
223
  | `/smart-router mode all` | Route among **all authenticated models** in the registry |
224
224
  | `/smart-router pricing refresh` | Manually fetch LiteLLM pricing from `LITELLM_PRICING_URL`, persist to SQLite, and rebuild the fleet with updated rates |
@@ -441,6 +441,9 @@ Cluster IDs are stable reason-code prefixes (`cluster_low_stakes_general`, `clus
441
441
  | `SMART_ROUTER_PLANNING_DELEGATE_EXCLUDE_EXECUTION_HISTORY` | `true` | Exclude tool execution history from delegate payload |
442
442
  | `SMART_ROUTER_PLANNING_DELEGATE_GLOBAL_TIMEOUT_MS` | `120000` | Global cap (ms) on the whole planning-delegate stage per planning turn — bounds fan-out wall-clock so a stalled worker cannot hang TTFT ([#120](https://github.com/beettlle/pi-smart-router/issues/120)) |
443
443
  | `SMART_ROUTER_PLANNING_DELEGATE_SUB_CALL_TIMEOUT_MS` | `30000` | Per-call cap (ms) on each delegate sub-call worker; on expiry the worker is cancelled/abandoned and routing falls back to direct frontier with `planning_delegate_timeout` ([#120](https://github.com/beettlle/pi-smart-router/issues/120)) |
444
+ | `SMART_ROUTER_ADAPTIVE_REASONING_ENABLED` | `true` | Master switch for the adaptive thinking-level policy ([#166](https://github.com/beettlle/pi-smart-router/issues/166)); `false` passes the session thinking level through unchanged |
445
+ | `SMART_ROUTER_ADAPTIVE_REASONING_MIN_LEVEL` | (unset) | Floor on policy-derived thinking levels (`minimal\|low\|medium\|high\|xhigh\|max`) — see [Adaptive reasoning](#adaptive-reasoning-thinking-level-166) |
446
+ | `SMART_ROUTER_ADAPTIVE_REASONING_MAX_LEVEL` | (unset) | Ceiling on policy-derived thinking levels (incl. turn-class upgrades) — see [Adaptive reasoning](#adaptive-reasoning-thinking-level-166) |
444
447
  | `SMART_ROUTER_PREFIX_CACHE_WEIGHT` | `0.20` | SAAR weight on warm prefix value in cache breakeven math (0–1; [#73](https://github.com/beettlle/pi-smart-router/issues/73)) |
445
448
  | `SMART_ROUTER_IDLE_TIMEOUT_SECONDS` | `300` | SAAR idle seconds before pin reopens for full re-route |
446
449
  | `SMART_ROUTER_SWITCH_THRESHOLD` | `0.5` | SAAR switch score gate (0–1) for tier upgrades during hard-lock |
@@ -459,6 +462,7 @@ When `SMART_ROUTER_LOG_ROUTING=1`, prefer the canonical payload from `buildRouti
459
462
  | `tier_hint` | Yes (top-level + `cluster_summary`) | Null when no tier hint |
460
463
  | `local_eligible_reason` | Yes (top-level + `features`) | Null when local_zero did not evaluate eligibility |
461
464
  | `cluster_id` | Yes (top-level + `cluster_summary`) | Null when no cluster match |
465
+ | `pricing_window` | Yes (top-level + `peak_pricing_summary`) | Peak vs off-peak rationale for the selected model (SP-244 / #165); the extension stderr logger carries `pricing_window` + `peak_pricing` |
462
466
 
463
467
  **Gap:** the pi extension’s live stderr path (`logRoutingDecision` in `.pi/extensions/smart-router`) still emits a slim JSON object (`selected_model_id`, `stage`, `reason_code`, `features`, `delegate`) and does **not** yet call `buildRoutingDecisionLogPayload`. SQLite `/smart-router history` and the payload builder carry the full checklist; wire the extension logger in a follow-up if dogfood needs identical stderr shape.
464
468
 
@@ -515,6 +519,41 @@ When a **planning** turn would route primary inference to frontier while a warm
515
519
 
516
520
  See [routing-roadmap.md](docs/routing-roadmap.md) §2 P0 and GitHub [#71](https://github.com/beettlle/pi-smart-router/issues/71) for acceptance criteria.
517
521
 
522
+ ### Adaptive reasoning (thinking level) (#166)
523
+
524
+ Adaptive reasoning tunes the **thinking intensity of the model already selected** — it never changes which model runs. Per turn class:
525
+
526
+ | Turn class | Policy level |
527
+ |-----------|--------------|
528
+ | `tool_result` | `minimal` |
529
+ | `main_loop` | `low` |
530
+ | planning / `planning_delegate` | `medium` |
531
+ | frontier escalation / `loop_escalation` | `high` |
532
+
533
+ pi passes the session thinking level on every call; the router treats pi's ambient default (`medium`) as adjustable by policy, and any **other** explicit level as an operator `/thinking` floor that is **never lowered** (a turn-class upgrade may still raise it). Chatty profiles (high `verbosity_factor`) additionally get a one-line conciseness nudge at `minimal`/`low`.
534
+
535
+ **Three knobs that sound alike, do different things:**
536
+
537
+ | Knob | Acts on | What it changes |
538
+ |------|---------|-----------------|
539
+ | **Adaptive reasoning** (`adaptive_reasoning.*`) | The delegated call's `reasoning` option | Thinking *intensity* of the already-selected model — cost of thinking, not model choice |
540
+ | `frugality.lambda_verbosity` | Multi-objective **selection** scoring | Which model gets picked — penalizes verbose models while ranking candidates; never touches the delegated call's reasoning option |
541
+ | `/thinking` (pi session command) | The caller-provided reasoning level | Explicit operator override — an explicit level is never lowered by policy or bounds |
542
+
543
+ **Operator knobs** (config `adaptive_reasoning` / env):
544
+
545
+ | Key | Env var | Default | Effect |
546
+ |-----|---------|---------|--------|
547
+ | `enabled` | `SMART_ROUTER_ADAPTIVE_REASONING_ENABLED` | `true` | When `false`, the policy is skipped — delegated calls pass the session thinking level through unchanged (`reasoning_reason_code: adaptive_reasoning_disabled`) |
548
+ | `min_level` | `SMART_ROUTER_ADAPTIVE_REASONING_MIN_LEVEL` | (none) | Floor: policy-derived levels are raised to at least this level. Discrete level (`minimal\|low\|medium\|high\|xhigh\|max`) — deliberately **not** a free-form verbosity percent |
549
+ | `max_level` | `SMART_ROUTER_ADAPTIVE_REASONING_MAX_LEVEL` | (none) | Ceiling: policy-derived levels (incl. turn-class upgrades) are capped at this level. When `min_level` exceeds `max_level` (e.g. via env), the ceiling wins (cost-safe) |
550
+
551
+ Floor/ceiling bind only what the policy itself derives. An explicit operator `/thinking` choice is never **lowered** by either bound (a floor can still *raise* one via the policy-upgrade path). Both bounds re-clamp down to the model's supported levels.
552
+
553
+ **Fail-open behavior:** models that do not support reasoning options (`reasoning: false`, or a `thinkingLevelMap` mapping every relevant level to `null`) pass caller options through unchanged — telemetry records `reasoning_reason_code: reasoning_unsupported` and the route never fails. Providers that ignore reasoning options degrade to a no-op.
554
+
555
+ **Telemetry:** each routed delegation records `reasoning_level_requested` (the session/caller level), `reasoning_level_applied` (the effective delegated level), and `reasoning_reason_code` (e.g. `turn_envelope_main_loop`, `operator_thinking_floor`, `operator_floor_applied`, `operator_ceiling_applied`, `reasoning_unsupported`) on the routing telemetry row — enriched post-delegation like usage actuals ([SP-241](https://github.com/beettlle/pi-smart-router/issues/164)). Inspect via `/smart-router history`.
556
+
518
557
  ### Degraded neural failover sandwich (#119)
519
558
 
520
559
  When the encoder/neural stage (HyDRA) **fails, is misconfigured, or exceeds its latency budget without a selection**, routing fails open through a cheap chain instead of crashing the host agent ([#119](https://github.com/beettlle/pi-smart-router/issues/119)):
@@ -587,6 +626,48 @@ When `quotaWindowPosition` is omitted, λ stays at 1 and quota premiums are zero
587
626
 
588
627
  See [routing-roadmap.md](docs/routing-roadmap.md) §2 P2 and GitHub [#78](https://github.com/beettlle/pi-smart-router/issues/78).
589
628
 
629
+ ### Usage actuals (post-turn capture)
630
+
631
+ When pi reports an assistant message `usage` object after a delegated turn, the smart-router persists it onto that turn's routing telemetry row ([#164](https://github.com/beettlle/pi-smart-router/issues/164)): `actual_cost_usd`, `actual_input_tokens`, `actual_output_tokens`, and cache read/write token counts — while retaining `estimated_cost_usd`. Capture fails open: library embeds and non-pi hosts that report no usage simply leave the actual fields null, and a telemetry write error never fails the route. Subscription/OAuth models report `cost.total === 0`: token actuals are still recorded, but no USD is invented — `/smart-router stats` labels its totals `cost_basis: 'actual' | 'estimated' | 'mixed'` accordingly.
632
+
633
+ ### Rolling cost calibration (v0.20.0 usage actuals)
634
+
635
+ **Rolling cost calibration** soft-biases future cost estimates with the ratio your models *actually* bill versus what the router estimated ([#164](https://github.com/beettlle/pi-smart-router/issues/164)). It is built from the privacy-safe post-turn [usage actuals](#usage-actuals-post-turn-capture) — no new state to configure, and prompt/message bodies are never touched.
636
+
637
+ **How it works**
638
+
639
+ 1. Every routed turn records an `estimated_cost_usd` (input tokens × catalog rate) and, when pi reports it, an `actual_cost_usd` on the same telemetry row.
640
+ 2. `buildCostCalibrationPrior` derives per-**model** and per-**tier** mean `actual / estimate` ratios over the rolling telemetry window. Per-pair outliers are clamped (±10×) before averaging; the aggregate is clamped to a soft band of **[0.5, 2.0]** so calibration can never hard-ban or hard-favor a model on cost alone.
641
+ 3. When warm (≥ 3 usable pairs per bucket), the ratio multiplies the tier's base per-1M cost **before** the virtual-cost v2 λ/premium/KV chain — so quota decay and cache credits compound on the calibrated base. Model buckets win over tier buckets.
642
+ 4. `estimateRoutingCost` accepts the same prior as an optional argument, soft-biasing per-request cost estimates identically.
643
+
644
+ **Cold start fails open.** No prior, an empty prior, or a bucket below the warmup threshold resolves ratio 1 — the catalog estimate is used unchanged. Subscription rows (host reports `cost.total === 0`) never contribute: stats and calibration never invent USD.
645
+
646
+ **Where it applies**
647
+
648
+ - **Expected-cost tier selection** — pass `costCalibration` into `selectTierByExpectedCost` / `computeExpectedCost` (library API, same pattern as `heatBias`). A warm ratio that doubles an economical tier's effective cost can flip the next selection to frontier; the price-delta and pin-economics hard gates still apply after the soft bias.
649
+ - **Pre-route estimates** — `estimateRoutingCost(model, request, catalog, calibration?)`; the router pipeline's uncalibrated call is unchanged.
650
+ - **`/smart-router stats`** — the aggregate's JSON snapshot (`aggregateSessionStats` / `buildStatsSnapshot`, the automation/MCP surface) carries a `cost_calibration` array (model buckets first, then tier buckets, each `{key, kind, ratio, samples}`); omitted entirely when cold so automation can treat absence as catalog-only. The human-readable stats text keeps rendering cost basis and role breakdown only.
651
+
652
+ **Observing the bias** — run with `SMART_ROUTER_LOG_ROUTING=1` and read the expected-cost gate line: calibrated winners carry a `[cost-calib ×N.NN from rolling actuals (SP-242)]` note on the rationale, and each tier's `calibrationRatio` appears in the expected-cost breakdown (`features.tier_selection.tier_costs[]`).
653
+
654
+ **Knobs** (`DEFAULT_COST_CALIBRATION_CONFIG`): `minSamples` 3 (warmup), `minRatio`/`maxRatio` 0.5/2.0 (soft band), `sampleMinRatio`/`sampleMaxRatio` 0.1/10 (per-pair outlier clamp). Not a vendor peak-clock schedule — see [#165](https://github.com/beettlle/pi-smart-router/issues/165) for time-of-day pricing.
655
+
656
+ ### Peak/off-peak pricing adapters (v0.20.0, #165)
657
+
658
+ Two vendors publish documented **time-of-day rate cards**. The peak-pricing adapters (`src/domain/pricing/peak-pricing.js`, SP-243) soft-bias the resolved cost-per-1M by the current pricing window — they never hard-ban a model, and non-target providers (OpenAI / Anthropic / Gemini / local / unknown) always resolve `window: 'none'` with multiplier 1 (fail open).
659
+
660
+ | Vendor | Adapter | Peak window | Off-peak rate | Docs |
661
+ |--------|---------|-------------|---------------|------|
662
+ | Z.ai GLM Coding Plan | `zai` (matches `zai`/`glm`/`zhipu` providers and `glm-*` ids) | Mon–Fri 14:00–18:00 Asia/Singapore (UTC+8) | 0.5× the standard credit rate | [Z.ai GLM Coding Plan docs](https://docs.z.ai/devpack/overview) |
663
+ | DeepSeek pay-as-you-go API | `deepseek` (matches `deepseek` provider / ids) | Mon–Fri 01:00–04:00 and 06:00–10:00 UTC | ½ the peak rate on cache-hit input, cache-miss input, and output | [DeepSeek Models & Pricing](https://api-docs.deepseek.com/quick_start/pricing) |
664
+
665
+ **Z.ai plan profiles.** The default plan profile is `credits` — off-peak usage at 0.5× the standard credit rate, peak at 1×. **Legacy plans** (e.g. GLM-5.3 legacy at 3× peak / 1× off-peak, Flash at 1.2× / 0.4×) share the same window but use different multipliers; they are available **only** via an explicit operator override (`PeakPricingConfig.zai`: `plan_profile: 'legacy'` plus documented `peak_multiplier` / `off_peak_multiplier`). The adapter never scrapes the live account plan — legacy multipliers are documented override inputs, not detected state.
666
+
667
+ **Configuration.** Adapters are on by default; `PeakPricingConfig.enabled: false` returns to flat rates. `PeakPricingConfig.deepseek.off_peak_multiplier` overrides the documented 0.5 off-peak discount if DeepSeek changes its schedule.
668
+
669
+ **Observing the window.** Every routed turn records `pricing_window: 'peak' | 'off_peak' | 'none'` on its telemetry row (SP-243). With `SMART_ROUTER_LOG_ROUTING=1`, the routing-decision payload surfaces the rationale as top-level `pricing_window` plus `peak_pricing_summary` (`window`, `cost_multiplier`, `adapter_id`) on the canonical `buildRoutingDecisionLogPayload`, and `pricing_window` + `peak_pricing` on the extension stderr logger (SP-244).
670
+
590
671
  ### P(success) training export (baseline classifier)
591
672
 
592
673
  When `SMART_ROUTER_DATASET=1`, the router records privacy-safe dataset rows and behavioral outcome labels. Export labeled training data from pi:
@@ -74,5 +74,15 @@
74
74
  "max_tool_use_requirement": "Ceiling (0–1) on cheap predicted tool_use for local_zero dispatch. Effective limit is min(local model tool_use capability, this value). Default 0.25 keeps format/lint local while skipping agentic git/bash/edit/explore/delete/repo cues (SP-177, #98)."
75
75
  }
76
76
  },
77
+ "adaptive_reasoning": {
78
+ "enabled": true,
79
+ "min_level": "low",
80
+ "max_level": "high",
81
+ "_documentation": {
82
+ "enabled": "When false, adaptive reasoning is skipped — delegated calls pass the caller (session) thinking level through unchanged. Default true (SP-246, #166).",
83
+ "min_level": "Optional floor on policy-derived thinking levels (minimal | low | medium | high | xhigh | max). Routine tool_result/main_loop turns policy-resolve to minimal/low; a floor raises that. Never lowers an explicit operator /thinking choice. Omit for no floor.",
84
+ "max_level": "Optional ceiling on policy-derived thinking levels, including turn-class upgrades. When min_level would exceed max_level (e.g. via env), the ceiling wins. Omit for no ceiling."
85
+ }
86
+ },
77
87
  "pin_only_fallback": false
78
88
  }
@@ -3,8 +3,8 @@
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, DEFAULT_SPECULATIVE_PREWARM_CONFIG, DEFAULT_WORKLOAD_HEAT_CONFIG, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
7
- /** Merge operator env overrides onto defaults (SAAR and planning delegate sections). */
6
+ export { DEFAULT_ADAPTIVE_REASONING_CONFIG, DEFAULT_LOCAL_ZERO_CONFIG, DEFAULT_PLANNING_DELEGATE_CONFIG, DEFAULT_SAAR_CONFIG, DEFAULT_SPECULATIVE_PREWARM_CONFIG, DEFAULT_WORKLOAD_HEAT_CONFIG, resolveAdaptiveReasoningConfigFromEnv, resolvePlanningDelegateConfigFromEnv, resolveSaarConfigFromEnv, } from '../domain/types/schemas.js';
7
+ /** Merge operator env overrides onto defaults (adaptive reasoning, SAAR and planning delegate sections). */
8
8
  export declare function resolveOperatorConfigFromEnv(base?: OperatorConfig): OperatorConfig;
9
9
  export declare const DEFAULT_OPERATOR_CONFIG: Readonly<OperatorConfig>;
10
10
  //# sourceMappingURL=defaults.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAWL,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,iCAAiC,EACjC,yBAAyB,EACzB,gCAAgC,EAChC,mBAAmB,EACnB,kCAAkC,EAClC,4BAA4B,EAC5B,qCAAqC,EACrC,oCAAoC,EACpC,wBAAwB,GACzB,MAAM,4BAA4B,CAAC;AAEpC,4GAA4G;AAC5G,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE,cAAwC,GAC7C,cAAc,CAOhB;AAED,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,cAAc,CAuCnD,CAAC"}