@volter/twin-moonshot 0.1.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 (39) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +164 -0
  3. package/dist/src/cli.d.ts +2 -0
  4. package/dist/src/cli.js +25 -0
  5. package/dist/src/index.d.ts +14 -0
  6. package/dist/src/index.js +86 -0
  7. package/dist/src/moonshot-budget.d.ts +57 -0
  8. package/dist/src/moonshot-budget.js +142 -0
  9. package/dist/src/moonshot-capabilities.d.ts +4 -0
  10. package/dist/src/moonshot-capabilities.js +1200 -0
  11. package/dist/src/moonshot-conformance.d.ts +14 -0
  12. package/dist/src/moonshot-conformance.js +405 -0
  13. package/dist/src/moonshot-connector.d.ts +168 -0
  14. package/dist/src/moonshot-connector.js +416 -0
  15. package/dist/src/moonshot-models.d.ts +36 -0
  16. package/dist/src/moonshot-models.js +37 -0
  17. package/dist/src/moonshot-scenario.d.ts +54 -0
  18. package/dist/src/moonshot-scenario.js +175 -0
  19. package/dist/src/moonshot-server.d.ts +13 -0
  20. package/dist/src/moonshot-server.js +202 -0
  21. package/dist/src/moonshot-stub.d.ts +70 -0
  22. package/dist/src/moonshot-stub.js +222 -0
  23. package/dist/src/moonshot-twin.d.ts +144 -0
  24. package/dist/src/moonshot-twin.js +1647 -0
  25. package/dist/src/moonshot-types.d.ts +251 -0
  26. package/dist/src/moonshot-types.js +19 -0
  27. package/package.json +53 -0
  28. package/src/cli.ts +25 -0
  29. package/src/index.ts +129 -0
  30. package/src/moonshot-budget.ts +163 -0
  31. package/src/moonshot-capabilities.ts +1220 -0
  32. package/src/moonshot-conformance.ts +416 -0
  33. package/src/moonshot-connector.ts +465 -0
  34. package/src/moonshot-models.ts +89 -0
  35. package/src/moonshot-scenario.ts +194 -0
  36. package/src/moonshot-server.ts +220 -0
  37. package/src/moonshot-stub.ts +230 -0
  38. package/src/moonshot-twin.ts +1670 -0
  39. package/src/moonshot-types.ts +225 -0
@@ -0,0 +1,142 @@
1
+ // The client-side rate budget — the fail-closed backstop `liveMoonshotExecute` routes every live
2
+ // request through. The MECHANISM is the kernel's shared, vendor-agnostic `RateBudget`; what lives
3
+ // here is Moonshot's DECLARATION (window/ceiling/per-endpoint weights) plus the vendor-bound
4
+ // bindings, exactly as every other pack does.
5
+ //
6
+ // THE GROUNDING (platform.kimi.ai/docs/pricing/limits, read 2026-09-16): Moonshot publishes a
7
+ // per-tier table whose LOWEST published row (Tier 0, deposit ≥ $1) is concurrency 1, RPM 3,
8
+ // TPM 500,000, TPD 1,500,000. RPM 3 is an order of magnitude UNDER the kernel's undeclared
9
+ // fallback (30 calls/min), so the declaration buys resolution DOWNWARD from the fallback, never
10
+ // headroom: the ceiling is pinned at the kernel fallback in units, and the INFERENCE rules price
11
+ // the model-facing endpoints at a weight that keeps the real RPM-3 worst case inside the window.
12
+ // The tier table scales up to Tier 5 (RPM 300), but no caller's tier is knowable here — the
13
+ // budget protects the LOWEST tier by construction, and a higher tier is only ever under-spent.
14
+ // Web-search endpoints (POST /v1/tools/search, /v1/tools/search_pro, /v1/tools/fetch) are
15
+ // metered on their OWN QPS (QPS 1 at Tier 0), independent of the inference RPM — they get their
16
+ // own rule so a search sweep cannot be priced as a cheap read.
17
+ //
18
+ // THE 429 SHAPE (same limits page): a real 429 carries `X-RateLimit-Limit` /
19
+ // `X-RateLimit-Remaining` / `X-RateLimit-Reset` headers. `recordCall` reads the standard
20
+ // `retry-after` backstop plus these.
21
+ import { declareRateBudget, rateBudgetPath, rateBudgetWeight, RateBudget, } from '@volter/world-core';
22
+ const VENDOR = 'moonshot';
23
+ /** Rolling window, in ms. Spend older than this is pruned. */
24
+ export const MOONSHOT_BUDGET_WINDOW_MS = 60_000;
25
+ /**
26
+ * Weighted units allowed inside one window. 60/60s at `defaultWeight` 2 = 30 calls a minute —
27
+ * EXACTLY the kernel's undeclared fallback. Moonshot DOES publish an RPM scalar (3 at Tier 0),
28
+ * but it is a CALL ceiling, not a weight-unit ceiling; pricing every call 2 keeps the fallback
29
+ * shape and lets the inference rules spend the allowance where the vendor actually meters it.
30
+ */
31
+ export const MOONSHOT_BUDGET_CEILING = 60;
32
+ /** Seconds. A `retry-after` above this means the key is throttled hard — fail loudly, don't sleep. */
33
+ export const MOONSHOT_BUDGET_MAX_RETRY_AFTER_S = 300;
34
+ /** Per-call cost, keyed by `"<METHOD> <path>"`. See the header for what is documented vs. judged. */
35
+ export const MOONSHOT_CALL_WEIGHTS = {
36
+ /** Model-facing inference: /v1/chat/completions, /v1/responses, /anthropic/v1/messages.
37
+ * At 60/6 that is at most 10 inference calls a window — inside Tier 0's RPM 3×window shape
38
+ * for a single burst while leaving room for the interleaved reads a real client issues. */
39
+ inference: 6,
40
+ /** The web-search tool endpoints — Moonshot meters them on their OWN QPS (1 at Tier 0),
41
+ * independent of inference RPM. Priced like inference so a sweep cannot ride the read weight. */
42
+ webSearch: 6,
43
+ /** Everything else: models, files, batches, balance, token counting, signature verify. */
44
+ other: 2,
45
+ };
46
+ /** THE PACK'S DECLARATION — pure data, the only Moonshot-specific thing in the whole budget. */
47
+ export const MOONSHOT_RATE_BUDGET = {
48
+ windowMs: MOONSHOT_BUDGET_WINDOW_MS,
49
+ ceiling: MOONSHOT_BUDGET_CEILING,
50
+ defaultWeight: MOONSHOT_CALL_WEIGHTS.other,
51
+ maxRetryAfterSeconds: MOONSHOT_BUDGET_MAX_RETRY_AFTER_S,
52
+ rules: [
53
+ // Anchored on Moonshot's REAL paths. Inference is token-metered (TPM 500,000 at Tier 0
54
+ // dwarfs RPM 3 as the binding constraint on real traffic), so model-facing POSTs cost 6.
55
+ { match: '^POST /v1/chat/completions$', weight: MOONSHOT_CALL_WEIGHTS.inference },
56
+ { match: '^POST /v1/responses$', weight: MOONSHOT_CALL_WEIGHTS.inference },
57
+ { match: '^POST /anthropic/v1/messages$', weight: MOONSHOT_CALL_WEIGHTS.inference },
58
+ // Web-search tools: own QPS pool at the vendor, priced at inference weight here.
59
+ { match: '^POST /v1/tools/search$', weight: MOONSHOT_CALL_WEIGHTS.webSearch },
60
+ { match: '^POST /v1/tools/search_pro$', weight: MOONSHOT_CALL_WEIGHTS.webSearch },
61
+ { match: '^POST /v1/tools/fetch$', weight: MOONSHOT_CALL_WEIGHTS.webSearch },
62
+ ],
63
+ reason: 'Moonshot publishes a per-tier limit table (platform.kimi.ai/docs/pricing/limits, read ' +
64
+ '2026-09-16): Tier 0 (deposit ≥ $1) is concurrency 1, RPM 3, TPM 500,000, TPD 1,500,000, ' +
65
+ 'Web Search QPS 1; Tier 5 (≥ $3000) is 100/300/5M/Unlimited/50. A 429 carries ' +
66
+ 'X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset. Because the LOWEST published ' +
67
+ 'RPM (3) is far under the kernel fallback of 30 calls/min, the ceiling is pinned AT the ' +
68
+ 'fallback (60 units / 60s, defaultWeight 2) and the declaration spends it downward: the three ' +
69
+ 'model-facing inference endpoints (POST /v1/chat/completions, POST /v1/responses, ' +
70
+ 'POST /anthropic/v1/messages) cost 6 each — at most 10 land in a window — because those calls ' +
71
+ 'are token-metered (TPM) at the vendor and one call spends far more of a real account\'s ' +
72
+ 'allowance than a list poll. The web-search tool endpoints get their own 6-weight rule ' +
73
+ 'because Moonshot meters them on a SEPARATE QPS pool (1 at Tier 0), so a search sweep must ' +
74
+ 'not be priceable as a cheap read. The per-tier judgment call is the strict direction: the ' +
75
+ 'budget protects Tier 0 by construction and only ever under-spends a higher tier. The window ' +
76
+ 'bounds the 60s AVERAGE and does not pace; a `retry-after` read off a real 429 is the ' +
77
+ 'persisted cooldown backstop.',
78
+ };
79
+ // Declared at module load, so merely importing this module (which `moonshot-connector.ts` does)
80
+ // is enough to arm the real ceiling.
81
+ declareRateBudget(VENDOR, MOONSHOT_RATE_BUDGET);
82
+ /**
83
+ * Price one call. The key is `"<METHOD> <path>"` with the query string split off, so a rule can
84
+ * price by method (a write is not a read) without the kernel knowing anything about Moonshot. An
85
+ * unclassified endpoint still costs `defaultWeight` — nothing is ever free.
86
+ */
87
+ export function moonshotCallWeight(method, path) {
88
+ const { bare, query } = moonshotSplitQuery(path);
89
+ // UPPER-CASE the method: `fetch` normalizes a known lowercase method before sending, so
90
+ // `execute('post', …)` really does issue a POST and must be priced as one.
91
+ return rateBudgetWeight(VENDOR, `${String(method).toUpperCase()} ${bare}`, query);
92
+ }
93
+ /**
94
+ * `/v1/chat/completions?a=1` -> `{ bare: '/v1/chat/completions', query: { a: '1' } }`. Rules
95
+ * match the path; NORMALIZED, because the anchored rules are otherwise trivially evaded: `fetch`
96
+ * upper-cases a known method before sending, so `execute('post', …)` issues a real WRITE that a
97
+ * `^POST ` rule would price as a read; and a trailing slash makes a path miss a `$` anchor while
98
+ * most routers treat it as the same endpoint.
99
+ */
100
+ function moonshotSplitQuery(path) {
101
+ const at = path.indexOf('?');
102
+ const query = {};
103
+ if (at !== -1)
104
+ for (const [k, v] of new URLSearchParams(path.slice(at + 1)))
105
+ query[k] = v;
106
+ // Collapse REPEATED slashes as well as a trailing one: `//v1/chat/completions` reaches the
107
+ // same endpoint on most routers but misses a `^POST /v1/...$` rule, which would price an
108
+ // inference call as a 2-unit read.
109
+ const raw = (at === -1 ? path : path.slice(0, at)).replace(/\/{2,}/g, '/');
110
+ const bare = raw.length > 1 && raw.endsWith('/') ? raw.replace(/\/+$/, '') : raw;
111
+ return { bare, query };
112
+ }
113
+ /** Where Moonshot's ledger lives. Token-keyed and cwd-independent by default (Moonshot's limits
114
+ * are per ACCOUNT, so a cwd-scoped ledger would hand the same key a fresh allowance in every
115
+ * checkout, worktree and CI matrix leg); pass `root` for world-scoped accounting. */
116
+ export function moonshotBudgetPath(opts = {}) {
117
+ const o = typeof opts === 'string' ? { root: opts } : opts;
118
+ // VENDOR spread LAST: a loosely-typed `{ vendor: 'other', … }` slipping through (TypeScript's
119
+ // excess-property check only catches object literals) must not redirect this pack's ledger.
120
+ return rateBudgetPath({ ...o, vendor: VENDOR });
121
+ }
122
+ /**
123
+ * Moonshot's budget — the shared kernel guard bound to this vendor's declaration. A real
124
+ * subclass, not an alias, so `budget instanceof MoonshotBudget` in `liveMoonshotExecute` means
125
+ * "a budget that accounts against MOONSHOT's ledger under MOONSHOT's ceiling".
126
+ */
127
+ export class MoonshotBudget extends RateBudget {
128
+ constructor(opts = {}) {
129
+ super({
130
+ ...opts,
131
+ // Field-per-line so the mutation gate can neuter the vendor binding: a budget whose
132
+ // `vendor` is not 'moonshot' accounts against another vendor's ledger, and the ceiling
133
+ // verify reads `err.vendor` off the refusal — the neuter must redden THAT assertion.
134
+ vendor: VENDOR,
135
+ });
136
+ }
137
+ }
138
+ /** The typed refusal. One error class shared with every other vendor's budget; `err.vendor` says
139
+ * which one refused, and `err.kind` says why. */
140
+ export { RateBudgetError as MoonshotBudgetError } from '@volter/world-core';
141
+ /** Re-exported so a caller can age a window out against the DECLARED window, not a hard-coded 60_000. */
142
+ export { MOONSHOT_BUDGET_WINDOW_MS as MOONSHOT_RATE_BUDGET_WINDOW_MS };
@@ -0,0 +1,4 @@
1
+ import { type CapabilityReport, type CapabilitySpec } from '@volter/world-tooling';
2
+ export declare const MOONSHOT_CAPABILITIES: CapabilitySpec[];
3
+ export declare const MOONSHOT_AREAS: readonly ["auth", "balance", "batches", "chat", "conformance", "connector", "errors", "files", "messages", "models", "rate_limits", "responses", "signatures", "streaming", "tokens", "tools"];
4
+ export declare function moonshotCapabilities(): Promise<CapabilityReport>;