@bivelio/savings-layer 0.4.20 → 0.4.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -205,7 +205,7 @@ declare function precioDeLista(model: string): CostModel | undefined;
205
205
  * on the customer's machine. `test/version-declarada.test.ts` ties this
206
206
  * constant to package.json so the two can never drift.
207
207
  */
208
- declare const BV_SALA_VERSION = "0.4.20";
208
+ declare const BV_SALA_VERSION = "0.4.22";
209
209
 
210
210
  /**
211
211
  * Environment-agnostic UUID generation.
@@ -990,6 +990,15 @@ interface LicenseGate {
990
990
  * que tener una rama por defecto de todas formas.
991
991
  */
992
992
  status?(): string;
993
+ /**
994
+ * Why the key does not open, when it does not; `undefined` when it does. It
995
+ * only refines the MESSAGE, never the verdict: an `"invalid"` whose reason is
996
+ * `"malformed"` (not even the shape of a token: the `bvk_` pasted where the
997
+ * license goes) gets different advice from an `"invalid"` that has the shape
998
+ * but no valid signature. Optional because a gate is implemented by the
999
+ * caller; without it, the generic message for that status.
1000
+ */
1001
+ reason?(): string | undefined;
993
1002
  }
994
1003
  interface TraceEvent {
995
1004
  stage: string;
@@ -2378,7 +2387,18 @@ declare function createMockProvider(options?: MockProviderOptions): Provider;
2378
2387
  interface OpenAICompatibleOptions {
2379
2388
  baseUrl: string;
2380
2389
  apiKey?: string;
2381
- /** Strip the "provider/" prefix from model ids before sending. */
2390
+ /**
2391
+ * Strip the `provider/` prefix from model ids before sending (`openai/gpt-4o-mini`
2392
+ * → `gpt-4o-mini`).
2393
+ *
2394
+ * Default (undefined): strip when `baseUrl` is OpenAI's own API or an Azure
2395
+ * OpenAI endpoint, keep otherwise. OpenAI rejects a prefixed id with a 404
2396
+ * ("model does not exist"), so the qualified ids the layer uses everywhere
2397
+ * else — pricing, cache keys, the report — would break the very first call
2398
+ * that reaches the provider. A gateway or a self-hosted server with a
2399
+ * namespaced model name usually WANTS the prefix, so it is kept there. Set `true`/`false` to decide explicitly; an explicit value
2400
+ * always wins.
2401
+ */
2382
2402
  stripProviderPrefix?: boolean;
2383
2403
  /** Extra headers (e.g. for a gateway). */
2384
2404
  headers?: Record<string, string>;
@@ -2408,6 +2428,13 @@ interface OpenAICompatibleOptions {
2408
2428
  * es una restricción de OpenAI que JSON Schema no tiene.
2409
2429
  */
2410
2430
  declare function isStrictCompatible(schema: unknown): boolean;
2431
+ /**
2432
+ * Decides the id that travels on the wire. Explicit option wins; otherwise the
2433
+ * endpoint decides: OpenAI/Azure get the bare id, everything else keeps what
2434
+ * the caller wrote. Exported for the pipeline-level test that pins the README
2435
+ * snippet; not part of the public API.
2436
+ */
2437
+ declare function quitarPrefijoDeProveedor(model: string, explicito: boolean | undefined, baseUrl: string): string;
2411
2438
  declare class OpenAICompatibleProvider implements Provider {
2412
2439
  readonly id: string;
2413
2440
  private readonly opts;
@@ -2439,6 +2466,14 @@ interface AnthropicOptions {
2439
2466
  apiKey?: string;
2440
2467
  /** Defaults to the public API. Override for a gateway or a proxy. */
2441
2468
  baseUrl?: string;
2469
+ /**
2470
+ * Strip the `provider/` prefix from model ids before sending
2471
+ * (`anthropic/claude-sonnet-4-5` → `claude-sonnet-4-5`). Default (undefined):
2472
+ * strip when talking to `api.anthropic.com`, which rejects a prefixed id;
2473
+ * keep for a gateway or proxy, which usually routes on it. An explicit value
2474
+ * always wins.
2475
+ */
2476
+ stripProviderPrefix?: boolean;
2442
2477
  /** Wire version pinned in the `anthropic-version` header. */
2443
2478
  apiVersion?: string;
2444
2479
  /** Extra headers (e.g. for a gateway). */
@@ -2503,6 +2538,8 @@ declare class AnthropicProvider implements Provider {
2503
2538
  private readonly opts;
2504
2539
  private readonly fetchImpl;
2505
2540
  constructor(opts?: AnthropicOptions);
2541
+ /** The id Anthropic's API sees: bare at `api.anthropic.com`, as written elsewhere. */
2542
+ private modeloEnElCable;
2506
2543
  generate(req: ProviderRequest): Promise<ProviderResponse>;
2507
2544
  private construirCuerpo;
2508
2545
  private marcarCache;
@@ -2575,8 +2612,14 @@ declare class LicenseError extends Error {
2575
2612
  *
2576
2613
  * El comportamiento no cambia —sigue siendo fail-closed, `granted()` no lo
2577
2614
  * concede— pero ahora el mensaje puede decir la verdad.
2615
+ *
2616
+ * `none` means "there is no key": what is left after `normalizeLicenseKey` is
2617
+ * EMPTY. It is the same value `LicenseError.status` already carries when nothing
2618
+ * was configured, because it is the same problem with the same fix: a
2619
+ * `licenseKey: " "` (an empty environment variable, a blank secret file) used
2620
+ * to tell the customer their key "does not verify" — there was no key.
2578
2621
  */
2579
- type LicenseStatus = "active" | "grace" | "expired" | "invalid" | "revoked" | "stale";
2622
+ type LicenseStatus = "active" | "grace" | "expired" | "invalid" | "revoked" | "stale" | "none";
2580
2623
  /** Decoded license token body (mirror of `@bivelio/protocol`'s LicensePayload,
2581
2624
  * declared locally so the SDK stays free of a control-plane dependency). */
2582
2625
  interface LicenseTokenPayload {
@@ -2621,6 +2664,48 @@ interface LicenseKeyset {
2621
2664
  }
2622
2665
  /** Narrow a verification key to a {@link LicenseKeyset} (vs a bare SPKI PEM string). */
2623
2666
  declare function isKeyset(k: string | LicenseKeyset): k is LicenseKeyset;
2667
+ /**
2668
+ * Normalize a license key the way it arrives from the real world — ONCE, at the
2669
+ * entry point, never inside the verifier.
2670
+ *
2671
+ * The README says `export BIVELIO_LICENSE_KEY=…` and dotenv trims, so that path
2672
+ * never showed it. But anyone reading the key from a file (`readFileSync`), from
2673
+ * a Docker/Kubernetes secret (mounted as a file, trailing newline included) or
2674
+ * copying it out of an `Authorization` header handed over a CORRECT key with a
2675
+ * `\n` after it, a space before it, quotes around it or a `Bearer ` in front —
2676
+ * and the SDK answered "does not verify, check you pasted the WHOLE key". A
2677
+ * wrong diagnosis on a good key is the worst possible hint: it sends the
2678
+ * customer to look where the problem is not.
2679
+ *
2680
+ * Worse, it was INCONSISTENT: `tok + "\r\n"` passed and `tok + "\n"` did not,
2681
+ * because `atob` ignores ASCII whitespace while the padding was computed from a
2682
+ * length that included it, and two extra characters give the same remainder
2683
+ * modulo 4 as zero. Behavior that depends on which editor saved the file is not
2684
+ * behavior.
2685
+ *
2686
+ * Only OUTER wrapping is removed — whitespace, one pair of single or double
2687
+ * quotes, a `Bearer` prefix in any capitalization — in that order, in one pass.
2688
+ * Anything INSIDE the key is left alone: an interior space is not wrapping, it
2689
+ * is a broken key, and {@link licenseKeyShape} reports it as `malformed`.
2690
+ */
2691
+ declare function normalizeLicenseKey(raw: string): string;
2692
+ /**
2693
+ * The SHAPE of an already-normalized key, so the error message can tell apart
2694
+ * three problems that used to get the same text:
2695
+ *
2696
+ * · `empty` — there is no key (only whitespace, or nothing at all).
2697
+ * · `malformed` — not even the shape of a token: not three dot-separated
2698
+ * base64url segments. This is where the `bvk_` service key
2699
+ * pasted where the license goes lands, as does arbitrary text
2700
+ * or a token with a line break in the middle.
2701
+ * · `token` — has the shape; whether it verifies is up to the signature.
2702
+ *
2703
+ * It is the SAME shape rule {@link verifyCompactSignature} enforces, so that "has
2704
+ * the shape" and "verification was attempted" mean the same thing.
2705
+ */
2706
+ type LicenseKeyShape = "empty" | "malformed" | "token";
2707
+ /** Classify a normalized key. See {@link LicenseKeyShape}. */
2708
+ declare function licenseKeyShape(normalizedKey: string): LicenseKeyShape;
2624
2709
  /**
2625
2710
  * Verify the Ed25519 signature of any BV-SALA compact token
2626
2711
  * (`base64url(header).base64url(body).base64url(sig)`, sig over `${head}.${body}`)
@@ -2629,6 +2714,13 @@ declare function isKeyset(k: string | LicenseKeyset): k is LicenseKeyset;
2629
2714
  * so the browser (`@noble/curves`) and node (`node:crypto`) sides agree byte-for-byte
2630
2715
  * and there is no bespoke per-message verification path to keep in sync. It does NOT
2631
2716
  * interpret the payload or the header `typ` — callers do that.
2717
+ *
2718
+ * Shape is checked SEGMENT by segment, not just by counting dots. Three parts
2719
+ * used to be enough and the rest was left to `atob`, which ignores whitespace:
2720
+ * that is how a token with `\r\n` after it verified while the same token with
2721
+ * `\n` did not (see {@link normalizeLicenseKey}). The shared primitive must give
2722
+ * the same answer to the same input, always; a caller that wants to tolerate
2723
+ * wrapping normalizes BEFORE calling.
2632
2724
  */
2633
2725
  declare function verifyCompactSignature(token: string, publicKey: string | LicenseKeyset): CompactVerification;
2634
2726
  /** Verify a token's signature and decode its payload. Does not check expiry. */
@@ -2656,6 +2748,9 @@ declare function deriveStatus(payload: {
2656
2748
  * Verify + apply the grace policy → a resolved status. Mirrors what the issuing
2657
2749
  * control plane resolves for the same token. Bad signature / malformed
2658
2750
  * → invalid; otherwise the status comes from {@link deriveStatus}.
2751
+ *
2752
+ * A public entry point, so it normalizes like the others (gate and refresher):
2753
+ * the same key must resolve the same whichever way it comes in.
2659
2754
  */
2660
2755
  declare function resolveLicense(token: string, publicKey: string | LicenseKeyset, opts?: {
2661
2756
  nowSeconds?: number;
@@ -2732,6 +2827,15 @@ declare class RevocationSnapshot {
2732
2827
  interface ResolvedGate extends LicenseGate {
2733
2828
  isActive(): boolean;
2734
2829
  status(): LicenseStatus;
2830
+ /**
2831
+ * Why the key does not open, when it does not; `undefined` when it verified.
2832
+ * Under `status() === "invalid"` it tells a key that does NOT EVEN HAVE THE
2833
+ * SHAPE of a token (`"malformed"`: the `bvk_` pasted where the license goes,
2834
+ * arbitrary text) from a well-formed one whose signature fails (the verifier's
2835
+ * reason, e.g. `"bad signature"`). Both are `invalid` — the stable handle for
2836
+ * branching does not change — but the advice given to the customer differs.
2837
+ */
2838
+ reason(): string | undefined;
2735
2839
  /** Unix seconds after which the online status should be re-checked (poller anchor). */
2736
2840
  refreshAfter?(): number | undefined;
2737
2841
  }
@@ -3063,6 +3167,11 @@ interface SavingsLayerConfig {
3063
3167
  * against the embedded public key and builds a {@link licenseGate} from it.
3064
3168
  * Without a valid, active license (and without {@link devUnlicensed}), the
3065
3169
  * pipeline refuses to run — get a key at https://savings.bivelio.com.
3170
+ *
3171
+ * Surrounding whitespace (the trailing newline of a file or mounted secret),
3172
+ * wrapping quotes and a `Bearer ` prefix are stripped before verification, so
3173
+ * a key read with `readFileSync` works as-is. Anything INSIDE the key is not
3174
+ * touched: a key with a space in the middle is a broken key.
3066
3175
  */
3067
3176
  licenseKey?: string;
3068
3177
  /**
@@ -3366,4 +3475,4 @@ interface SavingsLayer {
3366
3475
  */
3367
3476
  declare function createSavingsLayer(config: Omit<SavingsLayerConfig, "tokenizer"> & Partial<Pick<SavingsLayerConfig, "tokenizer">>): SavingsLayer;
3368
3477
 
3369
- export { type AnthropicOptions, AnthropicProvider, BV_SALA_VERSION, type BivelioHandoff, CATALOGO, COMPACT_ROWS_INSTRUCTION, type CacheOutcome, type CacheTier, type CachedAnswer, type CanonicalRequest, type ChatMessage, type CompactRowsDecodeOptions, type CompactRowsOptions, type CompactVerification, type CompressionEconomics, type CompressionPolicyInput, type CompressionRequest, type CompressionResult, type Compressor, type ConstraintBackend, type ConstraintBackendId, type ConstraintPlan, type ConversationState, type CostBasis, type CostModel, DEFAULT_COST_MODEL, DEFAULT_MAX_STALENESS_SECONDS, type DeferredExecutionReport, type DeterministicResolver, EMBEDDED_PUBLIC_KEY, type EmbeddingFn, type EvidenceDoc, type ExactCache, ExactTokenizer, type ExecutionClass, ExecutionRouter, type FormatChoice, FormatSelector, type FormatSelectorOptions, type HandoffFact, HeuristicEstimator, InMemoryExactCache, InMemorySemanticCache, InMemoryToolSetFreezer, type JsonSchema, LicenseError, type LicenseGate, type LicenseKeyset, type LicenseStatus, type LicenseTokenPayload, type LocalClassifier, MODELOS_POR_CODIFICACION, MODELS_BY_ENCODING, type MemoryDelta, type MessageRole, MockProvider, type MockProviderOptions, type ModelCascadeMode, type ModelCascadeReport, type ModelCascadeSettings, type OpenAICompatibleOptions, OpenAICompatibleProvider, type OutputContract, PRECIOS_AL_DIA, type Policy, type ProtectedSpan, type Provider, type ProviderRequest, type ProviderResponse, type ProviderUsage, QUALITY_STATUSES, type QualityStatus, type RequestContext, type Reranker, type ResolvedDeps, type ResolvedGate, type ResolvedLicense, ResolverRegistry, type ResolverResult, type RetrievalOptions, type RetrievalOutcome, type RetrievalPlan, type Retriever, type RevocationListBody, type RevocationListVerification, type RevocationPollEvent, RevocationPoller, type RevocationPollerOptions, RevocationSnapshot, type RouteDecision, type SavingsLayer, type SavingsLayerConfig, SavingsLedger, type SavingsReport, type SavingsRequest, type SavingsResult, type SavingsToggles, SchemaRegistry, type SemanticCacheOptions, type SemanticHit, type SemanticRecord, type SemanticVerifier, type SerializationFormat, type Serializer, Singleflight, type TokenEstimator, TokenizerRegistry, type ToolCallRequest, type ToolDescriptor, type ToolRisk, type ToolSelection, ToolSelector, type ToolSelectorOptions, type ToolSetFreezer, TraceCollector, type TraceEvent, UMBRAL_LOCAL, UsageEmitter, type UsageEmitterOptions, type ValidationError, type ValidationResult, adaptiveK, arithmeticResolver, buildExactKey, buildInflightKey, canonicalize, compactRowsSerializer, compresionFiel, computeNextDelay, cosine, createCompactRowsSerializer, createDefaultResolvers, createHandoff, createMockProvider, createSavingsLayer, csvSerializer, decodeCompactRows, dedupe, defaultTokenizerRegistry, deriveSeatId, deriveStatus, detectCi, embebedorLocal, emptyState, encodeCompactRows, evalArithmetic, evaluatePolicy, executeRetrievalPlan, gateFromKey, heuristicEstimator, isKeyset, isStrictCompatible, jsonCompactSerializer, keyTokensMatch, liveGateFromKey, mergeDelta, modelFamilyOf, multisetEqual, negationCount, noopCompressor, normalizeText, numericTokens, precioDeLista, projectContext, providerNativeBackend, randomId, registerExactTokenizer, registrarTokenizadorExacto, renderState, resolveConstraintBackend, resolveDependencies, resolveLicense, sha256, shouldCompress, stableStringify, uniformColumns, validateAgainstSchema, vectorLocal, verifyCompactSignature, verifyLicenseToken, verifyRevocationList, withProtectedSpans, words };
3478
+ export { type AnthropicOptions, AnthropicProvider, BV_SALA_VERSION, type BivelioHandoff, CATALOGO, COMPACT_ROWS_INSTRUCTION, type CacheOutcome, type CacheTier, type CachedAnswer, type CanonicalRequest, type ChatMessage, type CompactRowsDecodeOptions, type CompactRowsOptions, type CompactVerification, type CompressionEconomics, type CompressionPolicyInput, type CompressionRequest, type CompressionResult, type Compressor, type ConstraintBackend, type ConstraintBackendId, type ConstraintPlan, type ConversationState, type CostBasis, type CostModel, DEFAULT_COST_MODEL, DEFAULT_MAX_STALENESS_SECONDS, type DeferredExecutionReport, type DeterministicResolver, EMBEDDED_PUBLIC_KEY, type EmbeddingFn, type EvidenceDoc, type ExactCache, ExactTokenizer, type ExecutionClass, ExecutionRouter, type FormatChoice, FormatSelector, type FormatSelectorOptions, type HandoffFact, HeuristicEstimator, InMemoryExactCache, InMemorySemanticCache, InMemoryToolSetFreezer, type JsonSchema, LicenseError, type LicenseGate, type LicenseKeyShape, type LicenseKeyset, type LicenseStatus, type LicenseTokenPayload, type LocalClassifier, MODELOS_POR_CODIFICACION, MODELS_BY_ENCODING, type MemoryDelta, type MessageRole, MockProvider, type MockProviderOptions, type ModelCascadeMode, type ModelCascadeReport, type ModelCascadeSettings, type OpenAICompatibleOptions, OpenAICompatibleProvider, type OutputContract, PRECIOS_AL_DIA, type Policy, type ProtectedSpan, type Provider, type ProviderRequest, type ProviderResponse, type ProviderUsage, QUALITY_STATUSES, type QualityStatus, type RequestContext, type Reranker, type ResolvedDeps, type ResolvedGate, type ResolvedLicense, ResolverRegistry, type ResolverResult, type RetrievalOptions, type RetrievalOutcome, type RetrievalPlan, type Retriever, type RevocationListBody, type RevocationListVerification, type RevocationPollEvent, RevocationPoller, type RevocationPollerOptions, RevocationSnapshot, type RouteDecision, type SavingsLayer, type SavingsLayerConfig, SavingsLedger, type SavingsReport, type SavingsRequest, type SavingsResult, type SavingsToggles, SchemaRegistry, type SemanticCacheOptions, type SemanticHit, type SemanticRecord, type SemanticVerifier, type SerializationFormat, type Serializer, Singleflight, type TokenEstimator, TokenizerRegistry, type ToolCallRequest, type ToolDescriptor, type ToolRisk, type ToolSelection, ToolSelector, type ToolSelectorOptions, type ToolSetFreezer, TraceCollector, type TraceEvent, UMBRAL_LOCAL, UsageEmitter, type UsageEmitterOptions, type ValidationError, type ValidationResult, adaptiveK, arithmeticResolver, buildExactKey, buildInflightKey, canonicalize, compactRowsSerializer, compresionFiel, computeNextDelay, cosine, createCompactRowsSerializer, createDefaultResolvers, createHandoff, createMockProvider, createSavingsLayer, csvSerializer, decodeCompactRows, dedupe, defaultTokenizerRegistry, deriveSeatId, deriveStatus, detectCi, embebedorLocal, emptyState, encodeCompactRows, evalArithmetic, evaluatePolicy, executeRetrievalPlan, gateFromKey, heuristicEstimator, isKeyset, isStrictCompatible, jsonCompactSerializer, keyTokensMatch, licenseKeyShape, liveGateFromKey, mergeDelta, modelFamilyOf, multisetEqual, negationCount, noopCompressor, normalizeLicenseKey, normalizeText, numericTokens, precioDeLista, projectContext, providerNativeBackend, quitarPrefijoDeProveedor, randomId, registerExactTokenizer, registrarTokenizadorExacto, renderState, resolveConstraintBackend, resolveDependencies, resolveLicense, sha256, shouldCompress, stableStringify, uniformColumns, validateAgainstSchema, vectorLocal, verifyCompactSignature, verifyLicenseToken, verifyRevocationList, withProtectedSpans, words };