@bivelio/savings-layer 0.4.20 → 0.4.21

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.21";
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;
@@ -2575,8 +2584,14 @@ declare class LicenseError extends Error {
2575
2584
  *
2576
2585
  * El comportamiento no cambia —sigue siendo fail-closed, `granted()` no lo
2577
2586
  * concede— pero ahora el mensaje puede decir la verdad.
2587
+ *
2588
+ * `none` means "there is no key": what is left after `normalizeLicenseKey` is
2589
+ * EMPTY. It is the same value `LicenseError.status` already carries when nothing
2590
+ * was configured, because it is the same problem with the same fix: a
2591
+ * `licenseKey: " "` (an empty environment variable, a blank secret file) used
2592
+ * to tell the customer their key "does not verify" — there was no key.
2578
2593
  */
2579
- type LicenseStatus = "active" | "grace" | "expired" | "invalid" | "revoked" | "stale";
2594
+ type LicenseStatus = "active" | "grace" | "expired" | "invalid" | "revoked" | "stale" | "none";
2580
2595
  /** Decoded license token body (mirror of `@bivelio/protocol`'s LicensePayload,
2581
2596
  * declared locally so the SDK stays free of a control-plane dependency). */
2582
2597
  interface LicenseTokenPayload {
@@ -2621,6 +2636,48 @@ interface LicenseKeyset {
2621
2636
  }
2622
2637
  /** Narrow a verification key to a {@link LicenseKeyset} (vs a bare SPKI PEM string). */
2623
2638
  declare function isKeyset(k: string | LicenseKeyset): k is LicenseKeyset;
2639
+ /**
2640
+ * Normalize a license key the way it arrives from the real world — ONCE, at the
2641
+ * entry point, never inside the verifier.
2642
+ *
2643
+ * The README says `export BIVELIO_LICENSE_KEY=…` and dotenv trims, so that path
2644
+ * never showed it. But anyone reading the key from a file (`readFileSync`), from
2645
+ * a Docker/Kubernetes secret (mounted as a file, trailing newline included) or
2646
+ * copying it out of an `Authorization` header handed over a CORRECT key with a
2647
+ * `\n` after it, a space before it, quotes around it or a `Bearer ` in front —
2648
+ * and the SDK answered "does not verify, check you pasted the WHOLE key". A
2649
+ * wrong diagnosis on a good key is the worst possible hint: it sends the
2650
+ * customer to look where the problem is not.
2651
+ *
2652
+ * Worse, it was INCONSISTENT: `tok + "\r\n"` passed and `tok + "\n"` did not,
2653
+ * because `atob` ignores ASCII whitespace while the padding was computed from a
2654
+ * length that included it, and two extra characters give the same remainder
2655
+ * modulo 4 as zero. Behavior that depends on which editor saved the file is not
2656
+ * behavior.
2657
+ *
2658
+ * Only OUTER wrapping is removed — whitespace, one pair of single or double
2659
+ * quotes, a `Bearer` prefix in any capitalization — in that order, in one pass.
2660
+ * Anything INSIDE the key is left alone: an interior space is not wrapping, it
2661
+ * is a broken key, and {@link licenseKeyShape} reports it as `malformed`.
2662
+ */
2663
+ declare function normalizeLicenseKey(raw: string): string;
2664
+ /**
2665
+ * The SHAPE of an already-normalized key, so the error message can tell apart
2666
+ * three problems that used to get the same text:
2667
+ *
2668
+ * · `empty` — there is no key (only whitespace, or nothing at all).
2669
+ * · `malformed` — not even the shape of a token: not three dot-separated
2670
+ * base64url segments. This is where the `bvk_` service key
2671
+ * pasted where the license goes lands, as does arbitrary text
2672
+ * or a token with a line break in the middle.
2673
+ * · `token` — has the shape; whether it verifies is up to the signature.
2674
+ *
2675
+ * It is the SAME shape rule {@link verifyCompactSignature} enforces, so that "has
2676
+ * the shape" and "verification was attempted" mean the same thing.
2677
+ */
2678
+ type LicenseKeyShape = "empty" | "malformed" | "token";
2679
+ /** Classify a normalized key. See {@link LicenseKeyShape}. */
2680
+ declare function licenseKeyShape(normalizedKey: string): LicenseKeyShape;
2624
2681
  /**
2625
2682
  * Verify the Ed25519 signature of any BV-SALA compact token
2626
2683
  * (`base64url(header).base64url(body).base64url(sig)`, sig over `${head}.${body}`)
@@ -2629,6 +2686,13 @@ declare function isKeyset(k: string | LicenseKeyset): k is LicenseKeyset;
2629
2686
  * so the browser (`@noble/curves`) and node (`node:crypto`) sides agree byte-for-byte
2630
2687
  * and there is no bespoke per-message verification path to keep in sync. It does NOT
2631
2688
  * interpret the payload or the header `typ` — callers do that.
2689
+ *
2690
+ * Shape is checked SEGMENT by segment, not just by counting dots. Three parts
2691
+ * used to be enough and the rest was left to `atob`, which ignores whitespace:
2692
+ * that is how a token with `\r\n` after it verified while the same token with
2693
+ * `\n` did not (see {@link normalizeLicenseKey}). The shared primitive must give
2694
+ * the same answer to the same input, always; a caller that wants to tolerate
2695
+ * wrapping normalizes BEFORE calling.
2632
2696
  */
2633
2697
  declare function verifyCompactSignature(token: string, publicKey: string | LicenseKeyset): CompactVerification;
2634
2698
  /** Verify a token's signature and decode its payload. Does not check expiry. */
@@ -2656,6 +2720,9 @@ declare function deriveStatus(payload: {
2656
2720
  * Verify + apply the grace policy → a resolved status. Mirrors what the issuing
2657
2721
  * control plane resolves for the same token. Bad signature / malformed
2658
2722
  * → invalid; otherwise the status comes from {@link deriveStatus}.
2723
+ *
2724
+ * A public entry point, so it normalizes like the others (gate and refresher):
2725
+ * the same key must resolve the same whichever way it comes in.
2659
2726
  */
2660
2727
  declare function resolveLicense(token: string, publicKey: string | LicenseKeyset, opts?: {
2661
2728
  nowSeconds?: number;
@@ -2732,6 +2799,15 @@ declare class RevocationSnapshot {
2732
2799
  interface ResolvedGate extends LicenseGate {
2733
2800
  isActive(): boolean;
2734
2801
  status(): LicenseStatus;
2802
+ /**
2803
+ * Why the key does not open, when it does not; `undefined` when it verified.
2804
+ * Under `status() === "invalid"` it tells a key that does NOT EVEN HAVE THE
2805
+ * SHAPE of a token (`"malformed"`: the `bvk_` pasted where the license goes,
2806
+ * arbitrary text) from a well-formed one whose signature fails (the verifier's
2807
+ * reason, e.g. `"bad signature"`). Both are `invalid` — the stable handle for
2808
+ * branching does not change — but the advice given to the customer differs.
2809
+ */
2810
+ reason(): string | undefined;
2735
2811
  /** Unix seconds after which the online status should be re-checked (poller anchor). */
2736
2812
  refreshAfter?(): number | undefined;
2737
2813
  }
@@ -3063,6 +3139,11 @@ interface SavingsLayerConfig {
3063
3139
  * against the embedded public key and builds a {@link licenseGate} from it.
3064
3140
  * Without a valid, active license (and without {@link devUnlicensed}), the
3065
3141
  * pipeline refuses to run — get a key at https://savings.bivelio.com.
3142
+ *
3143
+ * Surrounding whitespace (the trailing newline of a file or mounted secret),
3144
+ * wrapping quotes and a `Bearer ` prefix are stripped before verification, so
3145
+ * a key read with `readFileSync` works as-is. Anything INSIDE the key is not
3146
+ * touched: a key with a space in the middle is a broken key.
3066
3147
  */
3067
3148
  licenseKey?: string;
3068
3149
  /**
@@ -3366,4 +3447,4 @@ interface SavingsLayer {
3366
3447
  */
3367
3448
  declare function createSavingsLayer(config: Omit<SavingsLayerConfig, "tokenizer"> & Partial<Pick<SavingsLayerConfig, "tokenizer">>): SavingsLayer;
3368
3449
 
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 };
3450
+ 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, randomId, registerExactTokenizer, registrarTokenizadorExacto, renderState, resolveConstraintBackend, resolveDependencies, resolveLicense, sha256, shouldCompress, stableStringify, uniformColumns, validateAgainstSchema, vectorLocal, verifyCompactSignature, verifyLicenseToken, verifyRevocationList, withProtectedSpans, words };