kestrel.markets
Version:
A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.
65 lines (58 loc) • 3.5 kB
text/typescript
/**
* kestrel.markets/protocol — OfferTerms, the commercial-terms VOCABULARY carried on
* an Offer (PLAT-ADR-0028 slice A1, owner-approved protocol-first split 2026-07-15).
*
* SHAPES ONLY. This module states the closed set of ways an Offer may be priced —
* one-shot, recurring, minimum-commitment, included-credits, committed-use-discount —
* as pure nouns/enums. It carries NO semantics: no billing, proration, renewal-clock,
* or credit-burn logic lives here. Recurring-Envelope SEMANTICS are explicitly OUT of
* scope (platform M2). Like the rest of `src/protocol/**` this file imports NOTHING and
* pulls in no runtime (asserted by tests/protocol.boundary.test.ts).
*/
/* ─────────────────────────────── offer terms ───────────────────────────────── */
/**
* Billing/commitment cadence for recurring and minimum-commitment terms. A closed,
* calendar-grained vocabulary; opaque to the protocol (no scheduling semantics here).
*/
export type BillingPeriod = "day" | "week" | "month" | "quarter" | "year";
/** Whether a recurring term rolls over on its own or requires a fresh acceptance. */
export type RenewalMode = "auto" | "manual";
/**
* The commercial terms an Offer prices, discriminated on `kind` (same `kind` idiom as
* {@link import("./index.ts").SettlementMethod}). Additive union — a `PaymentRequired`
* may list several as plural alternatives (`offers[]`). No member carries execution
* semantics; every amount is a bare settlement number (asset lives on the Offer).
*/
export type OfferTerms =
/** A single up-front purchase; no recurring or committed terms. */
| { readonly kind: "one_shot" }
/** A subscription billed each `term`, rolling over per `renewal`. */
| { readonly kind: "recurring"; readonly term: BillingPeriod; readonly renewal: RenewalMode }
/** A floor the buyer commits to spend (`amount`) each `period`. */
| { readonly kind: "min_commit"; readonly amount: number; readonly period: BillingPeriod }
/** A bundle of prepaid `amount` credits, void after `expiry` (ISO-8601). */
| { readonly kind: "included_credits"; readonly amount: number; readonly expiry: string }
/** A discount (`discountBps`, basis points) for committing to spend `amount` each `period`. */
| {
readonly kind: "committed_use_discount";
readonly amount: number;
readonly period: BillingPeriod;
readonly discountBps: number;
};
/** The discriminant of every {@link OfferTerms} member. */
export type OfferTermsKind = OfferTerms["kind"];
/**
* Runtime tuple of every {@link OfferTermsKind}. Same both-ways closed-vocabulary guard
* as {@link import("./index.ts").SCOPES}: a `.kind` added to (or removed from) the union
* without editing this tuple fails the `satisfies`/exhaustiveness check below at compile
* time. The platform's OpenAPI conformance check reads these contents.
*/
export const OFFER_TERMS_KINDS = [
"one_shot",
"recurring",
"min_commit",
"included_credits",
"committed_use_discount",
] as const satisfies readonly OfferTermsKind[];
/** Reverse-exhaustiveness proof: any `OfferTermsKind` omitted from {@link OFFER_TERMS_KINDS} makes `_Missing`'s default non-`never`, which violates its `never` constraint and fails to compile. */
type _OfferTermsKindsExhaustive<_Missing extends never = Exclude<OfferTermsKind, (typeof OFFER_TERMS_KINDS)[number]>> = true;