modelrig 0.0.1 → 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 (220) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +115 -8
  3. package/dist/adapters/anthropic.d.ts +46 -0
  4. package/dist/adapters/anthropic.d.ts.map +1 -0
  5. package/dist/adapters/anthropic.js +202 -0
  6. package/dist/adapters/deepseek.d.ts +41 -0
  7. package/dist/adapters/deepseek.d.ts.map +1 -0
  8. package/dist/adapters/deepseek.js +163 -0
  9. package/dist/adapters/gemini.d.ts +44 -0
  10. package/dist/adapters/gemini.d.ts.map +1 -0
  11. package/dist/adapters/gemini.js +222 -0
  12. package/dist/adapters/grok.d.ts +44 -0
  13. package/dist/adapters/grok.d.ts.map +1 -0
  14. package/dist/adapters/grok.js +168 -0
  15. package/dist/adapters/openai-compat.d.ts +74 -0
  16. package/dist/adapters/openai-compat.d.ts.map +1 -0
  17. package/dist/adapters/openai-compat.js +187 -0
  18. package/dist/adapters/openai.d.ts +49 -0
  19. package/dist/adapters/openai.d.ts.map +1 -0
  20. package/dist/adapters/openai.js +222 -0
  21. package/dist/bakeoff/engine.d.ts +52 -0
  22. package/dist/bakeoff/engine.d.ts.map +1 -0
  23. package/dist/bakeoff/engine.js +163 -0
  24. package/dist/bakeoff/replay.d.ts +52 -0
  25. package/dist/bakeoff/replay.d.ts.map +1 -0
  26. package/dist/bakeoff/replay.js +94 -0
  27. package/dist/bakeoff/samples.d.ts +62 -0
  28. package/dist/bakeoff/samples.d.ts.map +1 -0
  29. package/dist/bakeoff/samples.js +128 -0
  30. package/dist/bakeoff/stats.d.ts +13 -0
  31. package/dist/bakeoff/stats.d.ts.map +1 -0
  32. package/dist/bakeoff/stats.js +48 -0
  33. package/dist/bakeoff/types.d.ts +37 -0
  34. package/dist/bakeoff/types.d.ts.map +1 -0
  35. package/dist/bakeoff/types.js +10 -0
  36. package/dist/cli.d.ts +27 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +572 -0
  39. package/dist/config.d.ts +104 -0
  40. package/dist/config.d.ts.map +1 -0
  41. package/dist/config.js +148 -0
  42. package/dist/feedback/coverage.d.ts +32 -0
  43. package/dist/feedback/coverage.d.ts.map +1 -0
  44. package/dist/feedback/coverage.js +95 -0
  45. package/dist/feedback/feedback.d.ts +35 -0
  46. package/dist/feedback/feedback.d.ts.map +1 -0
  47. package/dist/feedback/feedback.js +39 -0
  48. package/dist/index.d.ts +155 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +448 -0
  51. package/dist/init/finder.d.ts +79 -0
  52. package/dist/init/finder.d.ts.map +1 -0
  53. package/dist/init/finder.js +297 -0
  54. package/dist/init/run.d.ts +99 -0
  55. package/dist/init/run.d.ts.map +1 -0
  56. package/dist/init/run.js +281 -0
  57. package/dist/init/signatures.d.ts +39 -0
  58. package/dist/init/signatures.d.ts.map +1 -0
  59. package/dist/init/signatures.js +116 -0
  60. package/dist/matrix/aggregate.d.ts +89 -0
  61. package/dist/matrix/aggregate.d.ts.map +1 -0
  62. package/dist/matrix/aggregate.js +186 -0
  63. package/dist/query/grammar.d.ts +35 -0
  64. package/dist/query/grammar.d.ts.map +1 -0
  65. package/dist/query/grammar.js +279 -0
  66. package/dist/query/run.d.ts +8 -0
  67. package/dist/query/run.d.ts.map +1 -0
  68. package/dist/query/run.js +21 -0
  69. package/dist/registry/build.d.ts +126 -0
  70. package/dist/registry/build.d.ts.map +1 -0
  71. package/dist/registry/build.js +192 -0
  72. package/dist/registry/call-notes.d.ts +11 -0
  73. package/dist/registry/call-notes.d.ts.map +1 -0
  74. package/dist/registry/call-notes.js +72 -0
  75. package/dist/registry/claims.d.ts +63 -0
  76. package/dist/registry/claims.d.ts.map +1 -0
  77. package/dist/registry/claims.js +107 -0
  78. package/dist/registry/facts.d.ts +32 -0
  79. package/dist/registry/facts.d.ts.map +1 -0
  80. package/dist/registry/facts.js +120 -0
  81. package/dist/registry/fingerprint.d.ts +20 -0
  82. package/dist/registry/fingerprint.d.ts.map +1 -0
  83. package/dist/registry/fingerprint.js +150 -0
  84. package/dist/registry/parity.d.ts +26 -0
  85. package/dist/registry/parity.d.ts.map +1 -0
  86. package/dist/registry/parity.js +41 -0
  87. package/dist/registry/pricing.d.ts +19 -0
  88. package/dist/registry/pricing.d.ts.map +1 -0
  89. package/dist/registry/pricing.js +69 -0
  90. package/dist/registry/rows.d.ts +16 -0
  91. package/dist/registry/rows.d.ts.map +1 -0
  92. package/dist/registry/rows.js +57 -0
  93. package/dist/registry/seed.d.ts +43 -0
  94. package/dist/registry/seed.d.ts.map +1 -0
  95. package/dist/registry/seed.js +97 -0
  96. package/dist/registry/select.d.ts +68 -0
  97. package/dist/registry/select.d.ts.map +1 -0
  98. package/dist/registry/select.js +141 -0
  99. package/dist/registry/staleness.d.ts +18 -0
  100. package/dist/registry/staleness.d.ts.map +1 -0
  101. package/dist/registry/staleness.js +39 -0
  102. package/dist/routes/draft.d.ts +83 -0
  103. package/dist/routes/draft.d.ts.map +1 -0
  104. package/dist/routes/draft.js +135 -0
  105. package/dist/routes/loader.d.ts +26 -0
  106. package/dist/routes/loader.d.ts.map +1 -0
  107. package/dist/routes/loader.js +436 -0
  108. package/dist/routes/manifest.d.ts +24 -0
  109. package/dist/routes/manifest.d.ts.map +1 -0
  110. package/dist/routes/manifest.js +118 -0
  111. package/dist/routes/serveability.d.ts +52 -0
  112. package/dist/routes/serveability.d.ts.map +1 -0
  113. package/dist/routes/serveability.js +73 -0
  114. package/dist/routes/template.d.ts +18 -0
  115. package/dist/routes/template.d.ts.map +1 -0
  116. package/dist/routes/template.js +94 -0
  117. package/dist/run/cost.d.ts +26 -0
  118. package/dist/run/cost.d.ts.map +1 -0
  119. package/dist/run/cost.js +41 -0
  120. package/dist/run/envelope.d.ts +21 -0
  121. package/dist/run/envelope.d.ts.map +1 -0
  122. package/dist/run/envelope.js +45 -0
  123. package/dist/run/grounding.d.ts +56 -0
  124. package/dist/run/grounding.d.ts.map +1 -0
  125. package/dist/run/grounding.js +89 -0
  126. package/dist/run/repair.d.ts +36 -0
  127. package/dist/run/repair.d.ts.map +1 -0
  128. package/dist/run/repair.js +61 -0
  129. package/dist/run/resolve.d.ts +26 -0
  130. package/dist/run/resolve.d.ts.map +1 -0
  131. package/dist/run/resolve.js +81 -0
  132. package/dist/run/retry.d.ts +19 -0
  133. package/dist/run/retry.d.ts.map +1 -0
  134. package/dist/run/retry.js +50 -0
  135. package/dist/run/run.d.ts +41 -0
  136. package/dist/run/run.d.ts.map +1 -0
  137. package/dist/run/run.js +0 -0
  138. package/dist/run/validate.d.ts +15 -0
  139. package/dist/run/validate.d.ts.map +1 -0
  140. package/dist/run/validate.js +56 -0
  141. package/dist/savings/emit.d.ts +25 -0
  142. package/dist/savings/emit.d.ts.map +1 -0
  143. package/dist/savings/emit.js +75 -0
  144. package/dist/savings/odometer.d.ts +32 -0
  145. package/dist/savings/odometer.d.ts.map +1 -0
  146. package/dist/savings/odometer.js +70 -0
  147. package/dist/savings/swap-execute.d.ts +44 -0
  148. package/dist/savings/swap-execute.d.ts.map +1 -0
  149. package/dist/savings/swap-execute.js +87 -0
  150. package/dist/savings/swaps-client.d.ts +41 -0
  151. package/dist/savings/swaps-client.d.ts.map +1 -0
  152. package/dist/savings/swaps-client.js +81 -0
  153. package/dist/telemetry/capture.d.ts +28 -0
  154. package/dist/telemetry/capture.d.ts.map +1 -0
  155. package/dist/telemetry/capture.js +74 -0
  156. package/dist/telemetry/exporter.d.ts +62 -0
  157. package/dist/telemetry/exporter.d.ts.map +1 -0
  158. package/dist/telemetry/exporter.js +233 -0
  159. package/dist/telemetry/ingest-protocol.d.ts +161 -0
  160. package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
  161. package/dist/telemetry/ingest-protocol.js +163 -0
  162. package/dist/telemetry/sinks.d.ts +54 -0
  163. package/dist/telemetry/sinks.d.ts.map +1 -0
  164. package/dist/telemetry/sinks.js +89 -0
  165. package/dist/telemetry/store.d.ts +38 -0
  166. package/dist/telemetry/store.d.ts.map +1 -0
  167. package/dist/telemetry/store.js +332 -0
  168. package/dist/telemetry/supabase-exporter.d.ts +64 -0
  169. package/dist/telemetry/supabase-exporter.d.ts.map +1 -0
  170. package/dist/telemetry/supabase-exporter.js +211 -0
  171. package/dist/tenancy/api-key-format.d.ts +67 -0
  172. package/dist/tenancy/api-key-format.d.ts.map +1 -0
  173. package/dist/tenancy/api-key-format.js +91 -0
  174. package/dist/tenancy/constants.d.ts +23 -0
  175. package/dist/tenancy/constants.d.ts.map +1 -0
  176. package/dist/tenancy/constants.js +25 -0
  177. package/dist/tenancy/scrub.d.ts +47 -0
  178. package/dist/tenancy/scrub.d.ts.map +1 -0
  179. package/dist/tenancy/scrub.js +85 -0
  180. package/dist/types/failures-teaching.d.ts +56 -0
  181. package/dist/types/failures-teaching.d.ts.map +1 -0
  182. package/dist/types/failures-teaching.js +177 -0
  183. package/dist/types/failures.d.ts +27 -0
  184. package/dist/types/failures.d.ts.map +1 -0
  185. package/dist/types/failures.js +41 -0
  186. package/dist/types/provider.d.ts +79 -0
  187. package/dist/types/provider.d.ts.map +1 -0
  188. package/dist/types/provider.js +20 -0
  189. package/dist/types/route.d.ts +127 -0
  190. package/dist/types/route.d.ts.map +1 -0
  191. package/dist/types/route.js +6 -0
  192. package/dist/types/telemetry.d.ts +89 -0
  193. package/dist/types/telemetry.d.ts.map +1 -0
  194. package/dist/types/telemetry.js +6 -0
  195. package/dist/watch/budget.d.ts +19 -0
  196. package/dist/watch/budget.d.ts.map +1 -0
  197. package/dist/watch/budget.js +35 -0
  198. package/dist/watch/evaluate.d.ts +66 -0
  199. package/dist/watch/evaluate.d.ts.map +1 -0
  200. package/dist/watch/evaluate.js +94 -0
  201. package/dist/watch/proposals.d.ts +73 -0
  202. package/dist/watch/proposals.d.ts.map +1 -0
  203. package/dist/watch/proposals.js +169 -0
  204. package/dist/watch/run-record.d.ts +64 -0
  205. package/dist/watch/run-record.d.ts.map +1 -0
  206. package/dist/watch/run-record.js +86 -0
  207. package/dist/watch/run-reported.d.ts +20 -0
  208. package/dist/watch/run-reported.d.ts.map +1 -0
  209. package/dist/watch/run-reported.js +53 -0
  210. package/dist/watch/run.d.ts +40 -0
  211. package/dist/watch/run.d.ts.map +1 -0
  212. package/dist/watch/run.js +44 -0
  213. package/dist/watch/state-store.d.ts +48 -0
  214. package/dist/watch/state-store.d.ts.map +1 -0
  215. package/dist/watch/state-store.js +86 -0
  216. package/dist/watch/state.d.ts +21 -0
  217. package/dist/watch/state.d.ts.map +1 -0
  218. package/dist/watch/state.js +93 -0
  219. package/package.json +50 -14
  220. package/index.js +0 -3
@@ -0,0 +1,79 @@
1
+ /**
2
+ * ProviderAdapter contract + the candidate-set invariant's type machinery.
3
+ * Spec §4. The invariant is structural: AdapterRequest carries a CandidateRef,
4
+ * and CandidateRef is a branded type constructible ONLY in run/resolve.ts.
5
+ */
6
+ import type { RigFailure } from "./failures";
7
+ export type ProviderId = "gemini" | "openai" | "deepseek" | "anthropic" | "grok" | "deepinfra" | "fireworks";
8
+ export type CapabilityFlag = "structured_native" | "json_mode" | "grounded_native" | "context_cache_explicit" | "prompt_cache_key" | "prefix_cache_implicit" | "tier_flex" | "trace_visible";
9
+ /** The canonical flag enumeration — the single list every static capability
10
+ * materialization iterates (index.ts, template validation, tests). */
11
+ export declare const ALL_CAPABILITY_FLAGS: readonly CapabilityFlag[];
12
+ declare const candidateBrand: unique symbol;
13
+ /**
14
+ * A (provider, model) pair validated against a route's candidate list.
15
+ * DO NOT construct outside run/resolve.ts — no `as CandidateRef` anywhere
16
+ * (CI greps for it; see candidate-set-invariant.test.ts).
17
+ */
18
+ export interface CandidateRef {
19
+ readonly provider: ProviderId;
20
+ readonly model: string;
21
+ readonly [candidateBrand]: true;
22
+ }
23
+ export type ServiceTier = "standard" | "flex" | "priority";
24
+ export interface AdapterRequest {
25
+ readonly candidate: CandidateRef;
26
+ readonly systemPrompt: string;
27
+ readonly userPrompt: string;
28
+ /** JSON Schema for native structured output; null => prompt/json_mode path. */
29
+ readonly outputSchema: object | null;
30
+ readonly requestedTier: ServiceTier;
31
+ readonly timeoutMs: number;
32
+ /** Provider-neutral cache directive; adapters map to native mechanisms. */
33
+ readonly cache?: {
34
+ key: string;
35
+ ttlSeconds?: number;
36
+ };
37
+ /**
38
+ * Provider-native web-grounding directive (set by the run loop when the
39
+ * route requires "grounded"). BUILDER ADDITION pending architect sign-off:
40
+ * the scaffold's gemini adapter notes grounding is "driven by a
41
+ * cache/grounding directive on AdapterRequest", but the scaffolded type
42
+ * carried only `cache` — this optional field fills that gap additively.
43
+ */
44
+ readonly grounding?: boolean;
45
+ readonly maxOutputTokens?: number;
46
+ }
47
+ export interface AdapterUsage {
48
+ readonly tokensIn: number;
49
+ readonly tokensOut: number;
50
+ readonly tokensCached: number;
51
+ readonly servedTier: ServiceTier;
52
+ /** Time to first body byte, ms (Phase 4 WS3 — headers lie on inference
53
+ * servers; TTFB is the honest slowness signal). Streaming adapters stamp
54
+ * first-chunk arrival; non-streaming calls equal total latency (the first
55
+ * byte genuinely arrives with the body). Optional: absent on failures
56
+ * where nothing arrived. */
57
+ readonly ttfbMs?: number;
58
+ }
59
+ export type AdapterResult = {
60
+ readonly ok: true;
61
+ readonly text: string;
62
+ readonly usage: AdapterUsage;
63
+ }
64
+ /** F1 (C1 finding, closed Phase 3 step 0): failures that still BILLED
65
+ * tokens (refusals, truncations, post-stream rejections) carry `usage` so
66
+ * envelopes, telemetry, and bake-off cost math see the spend. Absent when
67
+ * the provider billed nothing (transport errors, pre-dispatch rejections). */
68
+ | {
69
+ readonly ok: false;
70
+ readonly failure: RigFailure;
71
+ readonly usage?: AdapterUsage;
72
+ };
73
+ export interface ProviderAdapter {
74
+ readonly id: ProviderId;
75
+ supports(feature: CapabilityFlag, model: string): boolean;
76
+ call(req: AdapterRequest): Promise<AdapterResult>;
77
+ }
78
+ export {};
79
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/types/provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,WAAW,GACX,MAAM,GAGN,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,WAAW,GACX,iBAAiB,GACjB,wBAAwB,GACxB,kBAAkB,GAClB,uBAAuB,GACvB,WAAW,GACX,eAAe,CAAC;AAEpB;sEACsE;AACtE,eAAO,MAAM,oBAAoB,EAAE,SAAS,cAAc,EASzD,CAAC;AAEF,OAAO,CAAC,MAAM,cAAc,EAAE,OAAO,MAAM,CAAC;AAE5C;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;CACjC;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC;;;;gCAI4B;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAAE;AAC5E;;;8EAG8E;GAC5E;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAA;CAAE,CAAC;AAExF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1D,IAAI,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACnD"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * ProviderAdapter contract + the candidate-set invariant's type machinery.
4
+ * Spec §4. The invariant is structural: AdapterRequest carries a CandidateRef,
5
+ * and CandidateRef is a branded type constructible ONLY in run/resolve.ts.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.ALL_CAPABILITY_FLAGS = void 0;
9
+ /** The canonical flag enumeration — the single list every static capability
10
+ * materialization iterates (index.ts, template validation, tests). */
11
+ exports.ALL_CAPABILITY_FLAGS = [
12
+ "structured_native",
13
+ "json_mode",
14
+ "grounded_native",
15
+ "context_cache_explicit",
16
+ "prompt_cache_key",
17
+ "prefix_cache_implicit",
18
+ "tier_flex",
19
+ "trace_visible",
20
+ ];
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Route bundle contract — spec §3. Loaded from modelrig/routes/*.yaml by
3
+ * routes/loader.ts; frozen after load.
4
+ */
5
+ import type { FailureClass } from "./failures";
6
+ import type { ProviderId, ServiceTier } from "./provider";
7
+ export interface CandidateSpec {
8
+ readonly provider: ProviderId;
9
+ readonly model: string;
10
+ }
11
+ export type Requirement = "schema_conformant" | "grounded" | "zero_retention" | "trace_visible";
12
+ export type Preference = "cost" | "latency";
13
+ export interface RoutePolicy {
14
+ /** Per-class retry budgets — non-fungible. Missing class => 0 retries. */
15
+ readonly retries: Partial<Record<FailureClass, number>>;
16
+ readonly timeoutMs: number;
17
+ readonly tier: ServiceTier;
18
+ readonly json: "native" | "json_mode";
19
+ }
20
+ /** Repair rung config (Phase 3 WS2, ladder rung 4). */
21
+ export interface RepairPolicy {
22
+ /** Total repair attempts on a schema-invalid output: attempt 1 re-asks the
23
+ * SAME model with the ajv error summary; attempt 2 (when maxRepairs ≥ 2 and
24
+ * repairModel is set) hands {invalid output, errors, schema} to the
25
+ * designated repair model. */
26
+ readonly maxRepairs: number;
27
+ /** Designated cheap repair model — must be one of the route's declared
28
+ * candidates (candidate-set invariant holds through repair). */
29
+ readonly repairModel?: CandidateSpec;
30
+ }
31
+ /** A named route variant (Phase 3 WS1, bundle format v2 — additive).
32
+ * Every field optional; an absent field inherits the route's base config. */
33
+ export interface RouteVariant {
34
+ readonly name: string;
35
+ /** Candidate narrowing — MUST be a subset of the route's declared
36
+ * candidates (loader-enforced; the candidate-set invariant is unbreachable
37
+ * via variants). */
38
+ readonly candidates?: readonly CandidateSpec[];
39
+ /** Replaces the system template entirely (rendered with the same variables
40
+ * and capability conditionals). Mutually exclusive with promptAppend. */
41
+ readonly promptOverride?: string;
42
+ /** Appended to the rendered system prompt (after capability conditionals,
43
+ * before json coaching). */
44
+ readonly promptAppend?: string;
45
+ /** Reasoning scaffold (thinking directives; manual authoring this phase) —
46
+ * appended after promptAppend, before json coaching. */
47
+ readonly scaffold?: string;
48
+ /** JSON rung override for this variant. */
49
+ readonly json?: "native" | "json_mode";
50
+ readonly repair?: RepairPolicy;
51
+ }
52
+ export interface RouteBundle {
53
+ readonly route: string;
54
+ readonly version: number;
55
+ /** Resolved, parsed JSON Schema (null = unstructured route). */
56
+ readonly outputSchema: object | null;
57
+ readonly candidates: readonly CandidateSpec[];
58
+ readonly require: readonly Requirement[];
59
+ readonly prefer: readonly Preference[];
60
+ /** Raw template text with {{var}} and {{#if capability.X}} blocks. */
61
+ readonly systemTemplate: string;
62
+ readonly variables: readonly string[];
63
+ readonly policy: RoutePolicy;
64
+ /** Base-config repair rung (Phase 3 WS2) — variants may override. */
65
+ readonly repair?: RepairPolicy;
66
+ /** Named variants (Phase 3 WS1, bundle v2) — [] for v1 bundles. */
67
+ readonly variants: readonly RouteVariant[];
68
+ /** Local-only replay capture opt-in (Phase 3 WS4) — default false. */
69
+ readonly capture: boolean;
70
+ /** Grounding-inject search-query template (Phase 3 WS3) — rendered with
71
+ * the same variables as the system template; absent = the capped rendered
72
+ * prompt is used as the query (adequate only for short prompts). */
73
+ readonly groundingQuery?: string;
74
+ /** Schema-fingerprint cell key, computed at load from outputSchema
75
+ * (registry/fingerprint.ts); null for unstructured routes. */
76
+ readonly fingerprintCell: string | null;
77
+ }
78
+ export interface RunTags {
79
+ readonly run_id?: string;
80
+ readonly step?: string;
81
+ readonly [key: string]: string | undefined;
82
+ }
83
+ export interface RunOptions {
84
+ readonly input: Record<string, unknown>;
85
+ readonly tags: RunTags;
86
+ readonly budget?: {
87
+ readonly envelope: string;
88
+ };
89
+ /** Named variant to serve (Phase 3 WS1); omitted = the default variant
90
+ * (the route's base config). Unknown names are config errors. */
91
+ readonly variant?: string;
92
+ }
93
+ export interface RunMeta {
94
+ /** Telemetry row id of the serving attempt — the handle rig.feedback()
95
+ * takes (Phase 4 WS2). */
96
+ readonly inferenceId: string;
97
+ readonly route: string;
98
+ readonly routeVersion: number;
99
+ readonly provider: ProviderId;
100
+ readonly model: string;
101
+ readonly requestedTier: ServiceTier;
102
+ readonly servedTier: ServiceTier;
103
+ readonly tokensIn: number;
104
+ readonly tokensOut: number;
105
+ readonly tokensCached: number;
106
+ readonly costEstimateUsd: number;
107
+ readonly latencyMs: number;
108
+ readonly validated: boolean;
109
+ readonly attemptsByClass: Partial<Record<FailureClass, number>>;
110
+ /** Variant that served this run — null = default variant (Phase 3 WS1). */
111
+ readonly servedVariant: string | null;
112
+ /** "provider/model" that repaired the output — null when unrepaired
113
+ * (Phase 3 WS2). */
114
+ readonly repairedBy: string | null;
115
+ /** Normalized url_citation annotations (Phase 3 WS3) — present when the
116
+ * grounding-inject rung served this run. */
117
+ readonly citations?: ReadonlyArray<{
118
+ readonly type: "url_citation";
119
+ readonly url: string;
120
+ readonly title?: string;
121
+ }>;
122
+ }
123
+ export interface RunResult {
124
+ readonly output: unknown;
125
+ readonly meta: RunMeta;
126
+ }
127
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Route bundle contract — spec §3. Loaded from modelrig/routes/*.yaml by
4
+ * routes/loader.ts; frozen after load.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Telemetry contracts — spec §5. SQLite-backed in Phase 1 (telemetry/store.ts);
3
+ * the interface exists so Phase 6 can add a remote control-plane sink.
4
+ */
5
+ import type { FailureClass } from "./failures";
6
+ import type { RunTags } from "./route";
7
+ export interface TelemetryRow {
8
+ readonly id: string;
9
+ readonly ts: string;
10
+ readonly route: string;
11
+ readonly routeVersion: number;
12
+ readonly provider: string;
13
+ readonly model: string;
14
+ readonly requestedTier: string;
15
+ readonly servedTier: string;
16
+ readonly tokensIn: number;
17
+ readonly tokensOut: number;
18
+ readonly tokensCached: number;
19
+ readonly costEstimateUsd: number;
20
+ readonly latencyMs: number;
21
+ /** Time to first body byte, ms (Phase 4 WS3) — null when nothing arrived
22
+ * or for rows recorded before the v6 migration. */
23
+ readonly ttfbMs: number | null;
24
+ readonly failureClass: FailureClass | null;
25
+ readonly attemptsJson: string;
26
+ readonly tagsJson: string;
27
+ /** Schema-fingerprint cell key (registry/fingerprint.ts) — null for
28
+ * unstructured routes and rows recorded before the v2 migration. */
29
+ readonly fingerprintCell: string | null;
30
+ /** True when this attempt's model had no pricing entry (addendum A1):
31
+ * cost_estimate_usd stays 0 (no invented dollars) and any active envelope
32
+ * was charged the conservative unpriced rate instead. */
33
+ readonly pricingMissing: boolean;
34
+ /** Variant name this attempt served (Phase 3 WS1) — null = default variant
35
+ * and rows recorded before the v3 migration. */
36
+ readonly servedVariant: string | null;
37
+ /** "provider/model" of the repair model that produced this row's output
38
+ * (Phase 3 WS2) — null for unrepaired attempts. */
39
+ readonly repairedBy: string | null;
40
+ }
41
+ export interface EnvelopeRow {
42
+ readonly envelopeKey: string;
43
+ readonly budgetUsd: number;
44
+ readonly spentUsd: number;
45
+ readonly status: "open" | "exhausted";
46
+ }
47
+ /** One rig.feedback() judgment (Phase 4 WS2) — attached to a single
48
+ * inference OR an episode (= run-level key), or both. Local SQLite is the
49
+ * write path; the exporter mirrors (dual-store symmetry with inferences). */
50
+ export interface FeedbackRow {
51
+ readonly id: string;
52
+ readonly ts: string;
53
+ readonly inferenceId: string | null;
54
+ readonly episodeKey: string | null;
55
+ readonly verdict: "up" | "down";
56
+ readonly score: number | null;
57
+ readonly note: string | null;
58
+ readonly source: string;
59
+ /**
60
+ * The org this verdict belongs to — Phase 6 WS2, and the reason this field
61
+ * exists at all.
62
+ *
63
+ * A verdict recorded through OUR hosted server belongs to the org of the
64
+ * person who recorded it, and one SQLite store serves every org that reaches
65
+ * that server. Without a per-row org the exporter stamps its OWN, which
66
+ * files a customer's judgment into somebody else's corpus — unrecomputable
67
+ * evidence, silently wrong. WS1 shipped a 409 refusal rather than misfile;
68
+ * this column is what replaces the refusal with routing (QA-P6-010).
69
+ *
70
+ * NULL is the ordinary case and means "whatever org this exporter is": a
71
+ * self-hosted rig has exactly one, and `rig.feedback()` from application
72
+ * code has no notion of orgs at all.
73
+ */
74
+ readonly orgId: string | null;
75
+ /** Authenticated user who recorded it (00017's `feedback.created_by`), or
76
+ * null for an SDK-recorded verdict — which is a fact about the row, not
77
+ * missing data, and is never backfilled. */
78
+ readonly createdBy: string | null;
79
+ }
80
+ export interface TelemetryStore {
81
+ record(row: TelemetryRow): void;
82
+ recordFeedback(row: FeedbackRow): void;
83
+ queryByTag(tag: keyof RunTags & string, value: string): TelemetryRow[];
84
+ getEnvelope(key: string): EnvelopeRow | null;
85
+ accrue(key: string, usd: number): EnvelopeRow;
86
+ openEnvelope(key: string, budgetUsd: number): EnvelopeRow;
87
+ close(): void;
88
+ }
89
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../src/types/telemetry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Telemetry contracts — spec §5. SQLite-backed in Phase 1 (telemetry/store.ts);
4
+ * the interface exists so Phase 6 can add a remote control-plane sink.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Watch monthly spend cap — Phase 4 WS1 (spec: MODELRIG_WATCH_MONTHLY_USD,
3
+ * default $50). Every paid run the loop triggers (--from-proposal
4
+ * bake-offs, future re-probes) draws from a calendar-month envelope in the
5
+ * SAME envelope machinery that guards everything else — one budget system,
6
+ * not two.
7
+ */
8
+ import type { TelemetryStore } from "../types/telemetry";
9
+ export declare const DEFAULT_WATCH_MONTHLY_USD = 50;
10
+ export declare function watchEnvelopeKey(now: Date): string;
11
+ export interface WatchBudgetReservation {
12
+ readonly key: string;
13
+ readonly remainingUsd: number;
14
+ }
15
+ /** Open (idempotently) this month's watch envelope and report the headroom.
16
+ * Throws typed budget_exhausted when the month's cap is spent — the caller
17
+ * refuses the paid run; nothing silently overruns. */
18
+ export declare function reserveWatchBudget(store: TelemetryStore, monthlyUsd: number, now: Date): WatchBudgetReservation;
19
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/watch/budget.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAElD;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;sDAEsD;AACtD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,cAAc,EACrB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,IAAI,GACR,sBAAsB,CAgBxB"}
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * Watch monthly spend cap — Phase 4 WS1 (spec: MODELRIG_WATCH_MONTHLY_USD,
4
+ * default $50). Every paid run the loop triggers (--from-proposal
5
+ * bake-offs, future re-probes) draws from a calendar-month envelope in the
6
+ * SAME envelope machinery that guards everything else — one budget system,
7
+ * not two.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.DEFAULT_WATCH_MONTHLY_USD = void 0;
11
+ exports.watchEnvelopeKey = watchEnvelopeKey;
12
+ exports.reserveWatchBudget = reserveWatchBudget;
13
+ const failures_1 = require("../types/failures");
14
+ exports.DEFAULT_WATCH_MONTHLY_USD = 50;
15
+ function watchEnvelopeKey(now) {
16
+ return `watch-${now.toISOString().slice(0, 7)}`;
17
+ }
18
+ /** Open (idempotently) this month's watch envelope and report the headroom.
19
+ * Throws typed budget_exhausted when the month's cap is spent — the caller
20
+ * refuses the paid run; nothing silently overruns. */
21
+ function reserveWatchBudget(store, monthlyUsd, now) {
22
+ const key = watchEnvelopeKey(now);
23
+ const envelope = store.openEnvelope(key, monthlyUsd);
24
+ const remainingUsd = Math.max(0, envelope.budgetUsd - envelope.spentUsd);
25
+ if (envelope.status === "exhausted" || remainingUsd <= 0) {
26
+ throw new failures_1.RigFailureError({
27
+ class: "budget_exhausted",
28
+ message: `watch monthly budget ${key} exhausted ` +
29
+ `($${envelope.spentUsd.toFixed(2)} of $${envelope.budgetUsd.toFixed(2)} spent)`,
30
+ fixHint: "Raise MODELRIG_WATCH_MONTHLY_USD or wait for the next calendar month — " +
31
+ "the watch loop never overruns its cap.",
32
+ });
33
+ }
34
+ return { key, remainingUsd };
35
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Watch diff-evaluators — Phase 4 WS1 (plan §4). Pure: no store, no
3
+ * network, no clock beyond the expiry stamp. Two event classes:
4
+ *
5
+ * price_change — a candidate's pricing moved; when the blended cost shift
6
+ * clears the threshold (default ±10%), every route declaring that
7
+ * candidate gets a suggested bake-off proposal.
8
+ * new_model — a new pricing-map entry on a provider a route already
9
+ * uses → suggested bake-off (the re-probe/bake-off run IS the evidence
10
+ * step; the proposal never claims conformance it hasn't measured).
11
+ *
12
+ * Proposals NEVER execute anything. Idempotency: the trigger hash is a
13
+ * stable digest of (route, trigger content) — re-running a watch cycle over
14
+ * the same facts produces the same hashes, and the control-plane unique
15
+ * index (route, trigger_hash) makes duplicates a no-op.
16
+ */
17
+ export interface CandidateEconomics {
18
+ readonly provider: string;
19
+ readonly model: string;
20
+ }
21
+ export interface RouteEconomics {
22
+ readonly route: string;
23
+ readonly candidates: readonly CandidateEconomics[];
24
+ }
25
+ export interface TokenRates {
26
+ readonly inputUsdPerTok: number;
27
+ readonly outputUsdPerTok: number;
28
+ }
29
+ export type WatchEvent = {
30
+ readonly type: "price_change";
31
+ readonly provider: string;
32
+ readonly model: string;
33
+ readonly old: TokenRates;
34
+ readonly new: TokenRates;
35
+ } | {
36
+ readonly type: "new_model";
37
+ readonly provider: string;
38
+ readonly model: string;
39
+ readonly pricing: TokenRates | null;
40
+ };
41
+ export interface WatchConfig {
42
+ /** Material-shift threshold in percent (spec default ±10). */
43
+ readonly thresholdPct: number;
44
+ }
45
+ /** Trigger payload persisted on the proposal row (jsonb). */
46
+ export interface ProposalTrigger {
47
+ readonly type: "price_change" | "new_model";
48
+ readonly provider: string;
49
+ readonly model: string;
50
+ /** Blended-cost percent change (price_change only). */
51
+ readonly deltaPct?: number;
52
+ readonly oldRates?: TokenRates;
53
+ readonly newRates?: TokenRates;
54
+ readonly pricing?: TokenRates | null;
55
+ }
56
+ export interface ProposalDraft {
57
+ readonly route: string;
58
+ readonly kind: "bakeoff";
59
+ readonly trigger: ProposalTrigger;
60
+ readonly triggerHash: string;
61
+ readonly expiresAt: string;
62
+ }
63
+ export declare const PROPOSAL_TTL_DAYS = 30;
64
+ export declare function triggerHash(route: string, trigger: ProposalTrigger): string;
65
+ export declare function evaluateEvents(routes: readonly RouteEconomics[], events: readonly WatchEvent[], config: WatchConfig): ProposalDraft[];
66
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.d.ts","sourceRoot":"","sources":["../../src/watch/evaluate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,MAAM,UAAU,GAClB;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CAC1B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;CACrC,CAAC;AAEN,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,6DAA6D;AAC7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,WAAW,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAYpC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,MAAM,CAE3E;AAMD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,MAAM,EAAE,SAAS,UAAU,EAAE,EAC7B,MAAM,EAAE,WAAW,GAClB,aAAa,EAAE,CAoDjB"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * Watch diff-evaluators — Phase 4 WS1 (plan §4). Pure: no store, no
4
+ * network, no clock beyond the expiry stamp. Two event classes:
5
+ *
6
+ * price_change — a candidate's pricing moved; when the blended cost shift
7
+ * clears the threshold (default ±10%), every route declaring that
8
+ * candidate gets a suggested bake-off proposal.
9
+ * new_model — a new pricing-map entry on a provider a route already
10
+ * uses → suggested bake-off (the re-probe/bake-off run IS the evidence
11
+ * step; the proposal never claims conformance it hasn't measured).
12
+ *
13
+ * Proposals NEVER execute anything. Idempotency: the trigger hash is a
14
+ * stable digest of (route, trigger content) — re-running a watch cycle over
15
+ * the same facts produces the same hashes, and the control-plane unique
16
+ * index (route, trigger_hash) makes duplicates a no-op.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.PROPOSAL_TTL_DAYS = void 0;
20
+ exports.triggerHash = triggerHash;
21
+ exports.evaluateEvents = evaluateEvents;
22
+ const node_crypto_1 = require("node:crypto");
23
+ exports.PROPOSAL_TTL_DAYS = 30;
24
+ /** Reference token mix for blending input/output rates into one comparable
25
+ * number: 3 input tokens per output token — a conservative, route-agnostic
26
+ * default (schema-bound analysis routes run input-heavy). */
27
+ const BLEND_IN = 3;
28
+ const BLEND_OUT = 1;
29
+ function blended(rates) {
30
+ return rates.inputUsdPerTok * BLEND_IN + rates.outputUsdPerTok * BLEND_OUT;
31
+ }
32
+ function triggerHash(route, trigger) {
33
+ return (0, node_crypto_1.createHash)("sha256").update(`${route}\n${JSON.stringify(trigger)}`).digest("hex").slice(0, 32);
34
+ }
35
+ function expiryStamp() {
36
+ return new Date(Date.now() + exports.PROPOSAL_TTL_DAYS * 24 * 3600 * 1000).toISOString();
37
+ }
38
+ function evaluateEvents(routes, events, config) {
39
+ const drafts = [];
40
+ for (const event of events) {
41
+ if (event.type === "price_change") {
42
+ const before = blended(event.old);
43
+ if (before <= 0)
44
+ continue; // nothing sane to compare against
45
+ const deltaPct = ((blended(event.new) - before) / before) * 100;
46
+ if (Math.abs(deltaPct) < config.thresholdPct)
47
+ continue;
48
+ const trigger = {
49
+ type: "price_change",
50
+ provider: event.provider,
51
+ model: event.model,
52
+ deltaPct,
53
+ oldRates: event.old,
54
+ newRates: event.new,
55
+ };
56
+ for (const route of routes) {
57
+ if (!route.candidates.some((c) => c.provider === event.provider && c.model === event.model)) {
58
+ continue;
59
+ }
60
+ drafts.push({
61
+ route: route.route,
62
+ kind: "bakeoff",
63
+ trigger,
64
+ triggerHash: triggerHash(route.route, trigger),
65
+ expiresAt: expiryStamp(),
66
+ });
67
+ }
68
+ }
69
+ else {
70
+ const trigger = {
71
+ type: "new_model",
72
+ provider: event.provider,
73
+ model: event.model,
74
+ pricing: event.pricing,
75
+ };
76
+ for (const route of routes) {
77
+ if (!route.candidates.some((c) => c.provider === event.provider))
78
+ continue;
79
+ // A model the route already declares is not "new" for it.
80
+ if (route.candidates.some((c) => c.provider === event.provider && c.model === event.model)) {
81
+ continue;
82
+ }
83
+ drafts.push({
84
+ route: route.route,
85
+ kind: "bakeoff",
86
+ trigger,
87
+ triggerHash: triggerHash(route.route, trigger),
88
+ expiresAt: expiryStamp(),
89
+ });
90
+ }
91
+ }
92
+ }
93
+ return drafts;
94
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Proposal lifecycle + control-plane client — Phase 4 WS1 (plan §4).
3
+ *
4
+ * suggested → evidenced (bake-off attached)
5
+ * → approved (human, recorded) → executed (swap machinery)
6
+ * suggested|evidenced → rejected | expired
7
+ *
8
+ * Proposals NEVER auto-execute: "executed" is only reachable through
9
+ * "approved", and approval is a human CLI action (`swap execute --approve`).
10
+ * Transport is PostgREST with the service-role key — proposals are
11
+ * control-plane-only state (cross-run truth, not per-inference telemetry).
12
+ */
13
+ import type { ProposalDraft, ProposalTrigger } from "./evaluate";
14
+ export type ProposalStatus = "suggested" | "evidenced" | "approved" | "rejected" | "executed" | "expired";
15
+ export declare function assertTransition(from: ProposalStatus, to: ProposalStatus): void;
16
+ export interface ProposalRecord {
17
+ readonly id: string;
18
+ readonly route: string;
19
+ readonly kind: "bakeoff" | "swap";
20
+ readonly trigger: ProposalTrigger;
21
+ readonly triggerHash: string;
22
+ readonly evidence: Record<string, unknown> | null;
23
+ readonly status: ProposalStatus;
24
+ readonly createdAt: string;
25
+ readonly updatedAt: string;
26
+ readonly expiresAt: string;
27
+ /** Who approved or rejected (A1.2). Null while undecided. */
28
+ readonly approvedBy: string | null;
29
+ /** When the decision was recorded — NOT when a swap executed. */
30
+ readonly decidedAt: string | null;
31
+ }
32
+ export interface ProposalsClientConfig {
33
+ readonly supabaseUrl: string;
34
+ readonly serviceRoleKey: string;
35
+ readonly fetchFn?: typeof fetch;
36
+ }
37
+ export interface ProposalsClient {
38
+ /** Insert drafts; duplicates on (route, trigger_hash) are silent no-ops.
39
+ * Returns how many rows the control plane actually created. */
40
+ submitDrafts(drafts: readonly ProposalDraft[]): Promise<number>;
41
+ /**
42
+ * List proposals for ONE org, or across all of them.
43
+ *
44
+ * `orgId` is required and `null` is the explicit "every org" case, used by
45
+ * the CLI and by self-host deployments where there is exactly one. An
46
+ * OPTIONAL parameter would have been the same code with a worse failure
47
+ * mode: the unscoped read this replaces was unscoped because nobody passed
48
+ * anything, and an omitted argument reads as an oversight while an explicit
49
+ * null reads as a decision (C6 review ruling §7.2).
50
+ */
51
+ list(orgId: string | null, status?: ProposalStatus): Promise<ProposalRecord[]>;
52
+ get(id: string): Promise<ProposalRecord | null>;
53
+ /** Bake-off evidence + the suggested→evidenced flip, one PATCH. */
54
+ attachEvidence(id: string, evidence: Record<string, unknown>): Promise<void>;
55
+ /** Validated status change (assertTransition is the caller's guard for
56
+ * reads-before-writes; the PATCH is still status-scoped server-side). */
57
+ setStatus(id: string, from: ProposalStatus, to: ProposalStatus): Promise<void>;
58
+ /** A HUMAN decision on a proposal — addendum A1.2. Records who decided and
59
+ * when alongside the status flip, in one status-scoped PATCH.
60
+ *
61
+ * Approving is NOT executing: this writes a decision, never a swap_event.
62
+ * The swap still happens at a CLI (`modelrig swap execute --approve`) and
63
+ * the YAML it emits is still applied by git. Returns false when the row had
64
+ * already moved on (a concurrent decision), so the caller can say so
65
+ * instead of reporting a success that did not happen. */
66
+ /** The org that owns this proposal — the tenant a write must be authorized against. */
67
+ orgOf(id: string): Promise<string | null>;
68
+ decide(id: string, from: ProposalStatus, to: "approved" | "rejected", decidedBy: string, decidedByUserId?: string | null, orgId?: string): Promise<boolean>;
69
+ /** One sweep: overdue suggested/evidenced → expired. */
70
+ expireOverdue(now: Date): Promise<void>;
71
+ }
72
+ export declare function createProposalsClient(config: ProposalsClientConfig): ProposalsClient;
73
+ //# sourceMappingURL=proposals.d.ts.map